Secret Scan / scan (push) Successful in 5s
Build App (Preview) / compute-version (pull_request) Successful in 4s
Secret Scan / scan (pull_request) Successful in 5s
Build App (Preview) / create-release (pull_request) Successful in 3s
Build App (Preview) / build-macos (pull_request) Successful in 2m45s
Build App (Preview) / build-windows (pull_request) Successful in 4m58s
Build App (Preview) / build-linux (pull_request) Successful in 5m13s
Build App (Preview) / prune-previews (pull_request) Successful in 1s
The panel splits master/detail unconditionally: a 192px title column beside the editor. That fits the Project Home tab and does not fit the dock. At the dock's 352px default the editor gets 157px, and its action row wants ~200px, so the Delete button lands outside the dock's `overflow-hidden` with no scrollbar to reach it, and the textarea collapses to a two-word column. The two surfaces differ in width while sharing a viewport, so this is a container query rather than a `md:` breakpoint — a viewport query reads the window and hands both surfaces the same answer, which is wrong for one of them. Tailwind v4 has these in core; verified as real `@container (min-width: 32rem)` rules in the built CSS, since a variant that silently compiles to nothing looks identical in review. The threshold is arithmetic: side by side needs the 192px list, an editor wide enough for its own buttons (~280px), and the divider. `@lg` (512px) is the first stop clearing ~473px. Below it the titles become a capped strip above the editor, so the note being written keeps the height. The action row now wraps, which is the part that holds at *any* width rather than on one side of a threshold: the buttons are a group that does not shrink, the title field shrinks to 96px, and past that the title takes one row and the buttons the next. Nothing can be pushed out of the panel. Not covered by the suite — jsdom has no layout engine, so 711 tests pass before and after. This needs eyes on the dock at its minimum, default and maximum widths. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011YPqHpjV4EL6RNEwrRKqQm
72 lines
2.7 KiB
TypeScript
72 lines
2.7 KiB
TypeScript
import SendToAgentButton from "./SendToAgentButton";
|
|
import Button from "../ui/Button";
|
|
|
|
interface Props {
|
|
projectId: string;
|
|
title: string;
|
|
body: string;
|
|
onTitleChange: (value: string) => void;
|
|
onBodyChange: (value: string) => void;
|
|
onCommit: () => void;
|
|
onDelete: () => void;
|
|
}
|
|
|
|
/**
|
|
* Title and body, saved when a field loses focus.
|
|
*
|
|
* Plain text on purpose. There is no markdown rendering and no view/edit split,
|
|
* so there is no moment where the text on screen is not the text that would be
|
|
* sent — which is what makes "the agent gets exactly what you see" true rather
|
|
* than nearly true.
|
|
*/
|
|
export default function NoteEditor({
|
|
projectId,
|
|
title,
|
|
body,
|
|
onTitleChange,
|
|
onBodyChange,
|
|
onCommit,
|
|
onDelete,
|
|
}: Props) {
|
|
return (
|
|
<div className="flex flex-col h-full min-h-0 gap-2 p-3">
|
|
{/* Wraps rather than overflows. The two buttons are a group with a fixed
|
|
appetite (~190px) and the title field can shrink only so far, so in a
|
|
narrow dock the title takes the first row and the buttons the second.
|
|
Without the wrap the group is simply clipped by the dock's
|
|
`overflow-hidden`, which puts Delete off-window with no scrollbar to
|
|
reach it. */}
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
<input
|
|
value={title}
|
|
onChange={(e) => onTitleChange(e.target.value)}
|
|
onBlur={onCommit}
|
|
placeholder="Note title"
|
|
aria-label="Note title"
|
|
className="flex-1 min-w-24 px-2 h-8 bg-[var(--bg-primary)] border border-[var(--border-color)] rounded-[var(--radius-control)] text-[13px] text-[var(--text-primary)] focus:border-[var(--accent)] transition-colors"
|
|
/>
|
|
<div className="flex items-center gap-2 flex-shrink-0">
|
|
{/* The live editor text, not `note.body` — what is on screen is what
|
|
gets sent. */}
|
|
<SendToAgentButton projectId={projectId} body={body} />
|
|
<Button variant="danger" onClick={onDelete} aria-label="Delete note">
|
|
Delete
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
<textarea
|
|
value={body}
|
|
onChange={(e) => onBodyChange(e.target.value)}
|
|
onBlur={onCommit}
|
|
placeholder="Reminders, gotchas, a prompt worth keeping…"
|
|
aria-label="Note body"
|
|
className="flex-1 min-h-0 w-full px-3 py-2 bg-[var(--bg-primary)] border border-[var(--border-color)] rounded-[var(--radius-control)] text-[13px] text-[var(--text-primary)] focus:border-[var(--accent)] resize-none font-mono transition-colors"
|
|
/>
|
|
<p className="text-xs text-[var(--text-secondary)]">
|
|
Notes save when a field loses focus. Sending puts the note in the agent’s
|
|
prompt — you press Enter.
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|