A prompt box that grows as you type, sends, streams the reply, and can be stopped.
The prompt26,623 characters
Add the "Composer" component from notboring to my project.
It is licensed for use in my projects — notboring.dev/license. Please:
1. Create the component at a sensible path for this
project, named Composer, from the source below.
2. Install what it needs: npm i lucide-react
3. Add the CSS to the project's stylesheet.
4. THE PART THAT NEEDS YOUR JUDGMENT. The CSS reads
these custom properties and does not define them:
--card
--font-ui
--ink
--ink-2
--ink-3
--ink-4
--ink-5
--ink-rgb
They are notboring's design tokens. Map each one to
whatever this project already uses for the same
job — its own ink, ground, surface and UI font —
rather than defining them as new globals. If the
project has no equivalent, define it locally on
the component's own root so nothing leaks out.
Anything ending -rgb wants three bare numbers,
because the CSS builds rgba() from it.
Keep the comments. They say why the numbers are what
they are, and they are most of what makes this worth
copying rather than rewriting.
--- Composer.tsx ---
import { useEffect, useId, useLayoutEffect, useRef, useState } from "react";
import { ArrowUp, Paperclip, X } from "lucide-react";
/* ══ Composer ═════════════════════════════════════════════
A prompt box, and the exchange it starts.
IT GROWS, SENDS, STREAMS, AND CAN BE STOPPED — and the four
are one object. The obvious build is a text field with a
button beside it and a chat log somewhere else; this puts
the box at the foot of the block, the thread above it, and
has the box GIVE ROOM to the thread as it shrinks back after
a send. The two are anchored to the same bottom edge, so
nothing above ever jumps to make space for what is below.
THE HEIGHT IS A NUMBER, NOT AUTO. The textarea is measured
through a mirror — the same text in the same face at the
same width — and its height is written as lines times the
line height. A number can be transitioned; auto cannot. So
the box eases to its new size and the thread's foot eases
with it, and the caret never waits for either.
THE SEND BECOMES THE STOP. It is the same circle; the arrow
in it becomes a square and a ring turns around it while the
reply streams. A second button for stopping would be a
The rest — the source and its comments — is what you are buying.