@tangle-network/agent-app 0.46.3 → 0.46.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/assistant/index.js +6 -3
  2. package/dist/assistant/index.js.map +1 -1
  3. package/dist/chat-react/index.d.ts +10 -24
  4. package/dist/chat-react/index.js +6 -127
  5. package/dist/chat-react/index.js.map +1 -1
  6. package/dist/chunk-3HUFJ5LO.js +24 -0
  7. package/dist/chunk-3HUFJ5LO.js.map +1 -0
  8. package/dist/chunk-5SSUCLBW.js +1896 -0
  9. package/dist/chunk-5SSUCLBW.js.map +1 -0
  10. package/dist/{chunk-RZ6MYWMJ.js → chunk-JAYKCWW4.js} +625 -1551
  11. package/dist/chunk-JAYKCWW4.js.map +1 -0
  12. package/dist/chunk-L7MB2LLM.js +15 -0
  13. package/dist/chunk-L7MB2LLM.js.map +1 -0
  14. package/dist/mention-editor-WW7UVZMR.js +523 -0
  15. package/dist/mention-editor-WW7UVZMR.js.map +1 -0
  16. package/dist/teams/drizzle/invitations-schema.d.ts +1 -1
  17. package/dist/teams/drizzle/schema.d.ts +1 -1
  18. package/dist/web-react/chat-composer.d.ts +24 -4
  19. package/dist/{chat-react → web-react}/composer-mode-controls.d.ts +11 -1
  20. package/dist/{chat-react → web-react}/entry-composer.d.ts +11 -8
  21. package/dist/web-react/index.d.ts +3 -0
  22. package/dist/web-react/index.js +17 -8
  23. package/dist/web-react/mention-boundaries.d.ts +33 -0
  24. package/dist/web-react/mention-editor.d.ts +84 -0
  25. package/dist/web-react/mention-list.d.ts +35 -0
  26. package/dist/web-react/mention-pill.d.ts +10 -0
  27. package/dist/web-react/mention-serialize.d.ts +44 -0
  28. package/dist/web-react/use-file-mentions.d.ts +28 -14
  29. package/package.json +32 -1
  30. package/dist/chat-react/types.d.ts +0 -11
  31. package/dist/chunk-DINGA2MO.js +0 -718
  32. package/dist/chunk-DINGA2MO.js.map +0 -1
  33. package/dist/chunk-RZ6MYWMJ.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/web-react/composer-file-accept.ts","../src/web-react/use-dictation.ts","../src/web-react/chat-composer.tsx","../src/web-react/use-composer-attachments.ts","../src/web-react/harness-glyphs.tsx","../src/web-react/agent-session-controls.tsx","../src/web-react/composer-mode-controls.tsx","../src/web-react/entry-composer.tsx"],"sourcesContent":["/**\n * The composer's file-ingress filter, and the clipboard rename that goes with\n * it.\n *\n * A file reaches a composer by three routes — the picker dialog, a drag-and-drop,\n * and a clipboard paste — and only the picker gets a native `accept` filter (one\n * the user can defeat with \"All Files\"). Every route therefore funnels through\n * {@link filterAcceptedFiles}, so a type the picker will not offer cannot arrive\n * by another route instead.\n *\n * One route can still reach a different verdict, and it does so deliberately.\n * Paste is the only route that RENAMES, and {@link renamePastedImages} names a\n * file after the type it declares. So a clipboard bitmap called `image.png` that\n * declares `image/heic` is judged as `.heic` on paste, while the picker and a\n * drop judge the name they were handed and admit it under `accept=\".png\"`. The\n * filter is the same on all three; what differs is the name it is given, and\n * paste is stricter precisely because a rename that kept the contradicting name\n * would let the composer manufacture its own way past the filter.\n *\n * ONE accept matcher serves the package: `ChatComposer` gates its ingress on it\n * and `useComposerAttachments` gates `addFiles` on it. A second implementation\n * of the `accept` grammar is how the two ends of the same staging path start\n * disagreeing about what a file is.\n *\n * Pure data in, pure data out — nothing here throws, logs, or touches the DOM,\n * so a caller decides how a rejection is surfaced. Import-free beyond the\n * browser's own `File`, which keeps it usable from `/web-react`'s client bundle.\n */\n\n/** A file the `accept` list refused, with the reason to show for it. */\nexport interface ComposerFileRejection {\n file: File\n reason: string\n}\n\n/**\n * Checks one file against a comma-separated `accept` list, using the grammar of\n * the native `<input accept>` attribute: extensions (`.png`), exact MIME types\n * (`image/png`), and MIME wildcards (`image/*`). An absent or empty list accepts\n * everything, which is what an unset `accept` prop means.\n */\nexport function isAcceptedFileType(file: File, accept?: string): boolean {\n if (!accept || accept.trim().length === 0) return true\n\n const patterns = accept\n .split(',')\n .map((pattern) => pattern.trim())\n .filter((pattern) => pattern.length > 0)\n if (patterns.length === 0) return true\n\n const name = file.name.toLowerCase()\n const type = (file.type || '').toLowerCase()\n\n return patterns.some((pattern) => {\n const lower = pattern.toLowerCase()\n if (lower.startsWith('.')) return name.endsWith(lower)\n if (lower.endsWith('/*')) {\n // Keep the trailing \"/\" so `image/*` cannot match `imagex/png`, and\n // require a real subtype after it: `image/` and `image//png` are\n // malformed types, and a `File.type` can carry either.\n const prefix = lower.slice(0, -1)\n if (!type.startsWith(prefix)) return false\n const subtype = type.slice(prefix.length)\n return subtype.length > 0 && !subtype.includes('/')\n }\n return type === lower\n })\n}\n\n/** The reason an `accept` list refused a file. One wording for every ingress\n * route, so the same file reads the same whether it was picked or dropped. */\nexport function acceptRejectionReason(file: File, accept: string): string {\n return `\"${file.name}\" is not an accepted file type (${accept}).`\n}\n\n/**\n * Splits a batch into what the `accept` list admits and what it refuses. Size\n * and count limits are NOT applied here: they belong to the staging queue, which\n * knows what is already staged (`useComposerAttachments`), while this runs at the\n * composer's edge where that is unknown.\n */\nexport function filterAcceptedFiles(\n files: File[] | FileList,\n accept?: string,\n): { accepted: File[]; rejected: ComposerFileRejection[] } {\n const list = Array.isArray(files) ? files : Array.from(files)\n const accepted: File[] = []\n const rejected: ComposerFileRejection[] = []\n for (const file of list) {\n if (isAcceptedFileType(file, accept)) accepted.push(file)\n else rejected.push({ file, reason: acceptRejectionReason(file, accept ?? '') })\n }\n return { accepted, rejected }\n}\n\n/**\n * Extensions that truthfully name each image type, most canonical first. A type\n * with more than one is the reason this is a LIST and not a single name: `.jpeg`\n * and `.jpg` are the same claim, so a rename that swapped one for the other\n * would make a paste fail an `accept=\".jpeg\"` list that the very same file\n * passes through the picker.\n *\n * A type absent from here derives its single extension from the MIME subtype.\n */\nconst IMAGE_EXTENSIONS_BY_MIME: Record<string, readonly string[]> = {\n 'image/png': ['png'],\n 'image/jpeg': ['jpg', 'jpeg', 'jpe'],\n 'image/jpg': ['jpg', 'jpeg'],\n 'image/gif': ['gif'],\n 'image/webp': ['webp'],\n 'image/bmp': ['bmp'],\n 'image/svg+xml': ['svg'],\n 'image/tiff': ['tiff', 'tif'],\n 'image/x-icon': ['ico'],\n 'image/vnd.microsoft.icon': ['ico'],\n}\n\n/**\n * Matches EXACTLY the generic names browsers give a clipboard bitmap: an empty\n * name, `image`, or `image.<ext>`. Those are the names that collide across\n * pastes. A name that is only an extension (`.png`) is a real, if unusual,\n * filename and is left alone.\n */\nfunction isGenericImageName(name: string): boolean {\n return /^(?:image(?:\\.[a-z0-9]+)?)?$/i.test(name.trim())\n}\n\n/**\n * The extension a renamed clipboard image should carry, or null when none can\n * be derived truthfully.\n *\n * It must never name a format the bytes are not. A rename that hands an\n * `accept=\".png\"` list a file satisfying it by its new name alone turns the\n * rename into a way around the very gate it is filtered by.\n *\n * So the DECLARED TYPE decides which extensions are truthful, and the filename\n * may only pick among those. A clipboard file can carry a name whose extension\n * contradicts its type — a bitmap named `image.png` that is really `image/heic`\n * — and letting the name win there is exactly the hole this order closes. But\n * when the name's extension is one the type itself allows, it is kept: an\n * `image.jpeg` of type `image/jpeg` stays `.jpeg`, because rewriting it to the\n * canonical `.jpg` would make the paste fail an `accept=\".jpeg\"` list that the\n * same file passes through the picker.\n *\n * A subtype only stands in for an extension when it is already shaped like one.\n * `image/heic` is; `image/vnd.microsoft.icon` is not, and squeezing the\n * punctuation out of it would yield `vndmicrosofticon` — a name no accept list\n * will ever match, so a perfectly good `.ico` would be admitted through the\n * picker and refused on paste. An unrecognised compound type therefore names\n * nothing, and the filename is consulted instead.\n *\n * The filename is consulted on its own ONLY when the type names nothing usable\n * — either no subtype at all (`image/`) or a compound one. That is not an\n * exception to the rule above: the only named files this function ever sees are\n * `image.<ext>` (see {@link isGenericImageName}), so the fallback can only ever\n * preserve an extension the name already carried — it cannot manufacture one,\n * and an accept list therefore decides the same way it would have without the\n * rename. When nothing yields an extension, the caller skips the rename rather\n * than guessing.\n */\nfunction imageExtension(file: File): string | null {\n const type = file.type.toLowerCase()\n const fromName = /\\.([a-z0-9]+)$/i.exec(file.name)?.[1]?.toLowerCase()\n\n const mapped = IMAGE_EXTENSIONS_BY_MIME[type]\n const subtype = type.startsWith('image/') ? type.slice('image/'.length) : ''\n const bare = subtype.split('+')[0] ?? ''\n const truthful = mapped ?? (/^[a-z0-9]+$/.test(bare) ? [bare] : [])\n\n if (truthful.length === 0) return fromName ?? null\n if (fromName !== undefined && truthful.includes(fromName)) return fromName\n return truthful[0] ?? null\n}\n\n/** The `pasted-image-<n>` numbers these names already occupy. */\nfunction takenPastedImageIndexes(names: Iterable<string>): Set<number> {\n const taken = new Set<number>()\n for (const name of names) {\n const digits = /^pasted-image-(\\d+)(?:\\.[a-z0-9]+)?$/i.exec(name.trim())?.[1]\n if (digits === undefined) continue\n const parsed = Number.parseInt(digits, 10)\n if (Number.isSafeInteger(parsed)) taken.add(parsed)\n }\n return taken\n}\n\n/**\n * The lowest number above `after` that no name has claimed.\n *\n * Searching upward from the caller's own count — rather than from the highest\n * number any staged name happens to carry — is what keeps the usual case cheap:\n * staged numbers are avoided, never followed, so the walk advances at most once\n * per number already claimed.\n *\n * Termination does not rest on that, though, because `after` can be pushed near\n * the safe-integer ceiling and `+ 1` stops moving there — a walk that only\n * tested membership would spin on one value forever. The upward walk therefore\n * ends the moment a candidate leaves the safe range, and the search restarts\n * from the bottom, where a free number is guaranteed: `taken` is finite, so one\n * of the first `taken.size + 1` positive integers is always free. Every number\n * this returns is a safe integer, which is also what keeps an unsafe staged\n * name — invisible to {@link takenPastedImageIndexes} — impossible to collide\n * with.\n */\nfunction nextFreeIndex(taken: Set<number>, after: number): number {\n const start = Number.isSafeInteger(after) && after >= 0 ? after + 1 : 1\n for (let candidate = start; Number.isSafeInteger(candidate); candidate += 1) {\n if (!taken.has(candidate)) return candidate\n }\n for (let candidate = 1; ; candidate += 1) {\n if (!taken.has(candidate)) return candidate\n }\n}\n\n/**\n * Gives every generically-named clipboard image a distinct\n * `pasted-image-<n>.<ext>` name. Two pastes of the same bitmap otherwise arrive\n * as `image.png` twice, and a staging queue that keys on the name treats the\n * second as a duplicate of the first.\n *\n * A number is never reused. The search avoids every `pasted-image-<n>` already\n * present in `stagedNames` (the queue the host still holds, which outlives this\n * composer's own count) and in the batch itself (one paste can carry a file\n * already named that way beside a raw bitmap), so a collision is not reachable\n * rather than merely unlikely. `startIndex` is the caller's running count, and\n * `nextIndex` is the count to hand the next paste.\n *\n * Files that already carry a real name pass through untouched, so a copied\n * `report.pdf` keeps being `report.pdf`. So does an image whose extension\n * cannot be derived from what it declares — a renamed file must never claim a\n * format it is not. Only the name changes: the bytes, the type and the\n * modification time travel with it, so downstream fingerprinting still sees\n * the file the user pasted.\n */\nexport function renamePastedImages(\n files: File[],\n startIndex: number,\n stagedNames: Iterable<string> = [],\n): { files: File[]; nextIndex: number } {\n const taken = takenPastedImageIndexes([...stagedNames, ...files.map((file) => file.name)])\n // Whatever the caller's count is — a negative, a fraction, NaN, or a value at\n // the ceiling — `nextFreeIndex` normalises it and always answers with a safe\n // integer, so nothing here has to pre-screen it.\n let nextIndex = startIndex\n const renamed = files.map((file) => {\n const typed = file.type.startsWith('image/')\n // A clipboard file can arrive with no MIME type at all, and one named\n // `image.png` still collides across pastes exactly as a typed one does.\n // Its extension then comes from its own name, which cannot manufacture a\n // claim it did not already carry.\n if (!isGenericImageName(file.name) || (!typed && file.type !== '')) return file\n const extension = imageExtension(file)\n if (extension === null) return file\n nextIndex = nextFreeIndex(taken, nextIndex)\n taken.add(nextIndex)\n return new File([file], `pasted-image-${nextIndex}.${extension}`, {\n type: file.type,\n lastModified: file.lastModified,\n })\n })\n return { files: renamed, nextIndex }\n}\n","/**\n * `useDictation` — the capture half of composer dictation.\n *\n * Dictation splits at a clean seam: the browser owns capture (`getUserMedia` +\n * `MediaRecorder`), the host owns what the audio MEANS (transcription —\n * `sequences-react`'s Whisper provider — or a straight upload). This hook is\n * the capture half and nothing else: it asks for the mic, records, ticks whole\n * seconds while it does, and hands the assembled `Blob` to the host's\n * `onDictate`. A hook rather than composer-private code, because a host whose\n * composer is fully composed (hotkey, push-to-talk) needs the same capture\n * without re-deriving it.\n *\n * The rules the implementation exists to hold:\n *\n * - **Unsupported is a render signal, not an exception.** A browser without\n * `MediaRecorder`/`getUserMedia` gets `supported: false`, and the composer\n * renders no dead button. `start()` stays a no-op rather than throwing, so\n * a host that wired it to a hotkey cannot crash on such a browser.\n * - **The mic is released the moment recording ends.** Tracks are stopped in\n * every exit — stop, error, cancel-during-prompt, unmount. A red dot the\n * browser keeps showing after the composer says \"idle\" is the failure this\n * is written against.\n * - **A denied prompt is a message, not a crash.** `NotAllowedError` and a\n * missing device are reported through `onError` as words the composer can\n * show; the hook returns to idle.\n * - **Unmount discards.** A composer that unmounts mid-recording delivers\n * nothing: the host it would have called has moved on, and an arriving\n * transcript would land in a conversation the user left.\n * - **Duration is measured, not counted.** `durationSeconds` comes off the\n * clock at stop; the one-second ticker drives only the visible elapsed\n * display, so a throttled timer never falsifies the delivered figure.\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react'\n\n/** The audio a finished recording hands to the host. */\nexport interface DictationAudio {\n /** The assembled recording, typed with the MIME the recorder actually used. */\n readonly blob: Blob\n /** `blob.type`, surfaced so a host can switch on it without touching the blob. */\n readonly mimeType: string\n /** Clock-measured whole seconds between start and stop. */\n readonly durationSeconds: number\n}\n\nexport interface UseDictationOptions {\n /** The host callback: receive the recording. Transcription is the host's. */\n onDictate: (audio: DictationAudio) => void\n /** Capture failures in words (\"Microphone access was denied…\"). Optional —\n * the composer shows its own notice either way; this is for hosts that log. */\n onError?: (message: string) => void\n}\n\nexport interface DictationControls {\n /** Whether this browser can record at all. When false, render no affordance. */\n readonly supported: boolean\n readonly recording: boolean\n /** Whole seconds since the current recording started; drives the indicator. */\n readonly elapsedSeconds: number\n /** Ask for the mic and start. A no-op while a recording or a prompt is open. */\n readonly start: () => void\n /** Stop and deliver. Cancels a still-pending permission prompt instead. */\n readonly stop: () => void\n}\n\n/** Preference order: opus-in-webm first, Safari's mp4 last, UA default if none. */\nconst PREFERRED_MIME_TYPES = ['audio/webm;codecs=opus', 'audio/webm', 'audio/mp4'] as const\n\n/** The mime to ask the recorder for, or `undefined` to take the UA default. */\nexport function pickDictationMimeType(): string | undefined {\n if (typeof MediaRecorder === 'undefined' || typeof MediaRecorder.isTypeSupported !== 'function') {\n return undefined\n }\n for (const type of PREFERRED_MIME_TYPES) {\n if (MediaRecorder.isTypeSupported(type)) return type\n }\n return undefined\n}\n\n/** Capture support is a property of the browser, so it is read once per mount. */\nfunction detectDictationSupport(): boolean {\n return (\n typeof navigator !== 'undefined' &&\n typeof navigator.mediaDevices?.getUserMedia === 'function' &&\n typeof MediaRecorder !== 'undefined'\n )\n}\n\n/** The failure as a sentence. The denied prompt is the common case and the one\n * whose generic name (\"NotAllowedError\") says nothing to a reader. */\nexport function dictationErrorMessage(error: unknown): string {\n if (error instanceof DOMException) {\n if (error.name === 'NotAllowedError') return 'Microphone access was denied — allow it in the browser to dictate.'\n if (error.name === 'NotFoundError') return 'No microphone found on this device.'\n }\n return 'Could not start recording.'\n}\n\n/** `0:00`, `0:09`, `1:05`, `60:00` — minutes unbounded, seconds always two digits. */\nexport function formatDictationElapsed(totalSeconds: number): string {\n const safe = Number.isFinite(totalSeconds) && totalSeconds > 0 ? Math.floor(totalSeconds) : 0\n const minutes = Math.floor(safe / 60)\n const seconds = safe % 60\n return `${minutes}:${String(seconds).padStart(2, '0')}`\n}\n\n/** One capture's mutable internals, kept in a ref: they move with recorder\n * events, not with renders. */\ninterface DictationSession {\n readonly stream: MediaStream\n readonly recorder: MediaRecorder\n readonly chunks: Blob[]\n readonly mimeType: string\n readonly startedAt: number\n /** Set when the capture must deliver nothing (unmount, recorder failure). */\n cancelled: boolean\n}\n\n/** Release the mic. Idempotent — every exit path ends here. */\nfunction releaseStream(stream: MediaStream): void {\n for (const track of stream.getTracks()) track.stop()\n}\n\nexport function useDictation({ onDictate, onError }: UseDictationOptions): DictationControls {\n const [supported] = useState(detectDictationSupport)\n const [recording, setRecording] = useState(false)\n const [elapsedSeconds, setElapsedSeconds] = useState(0)\n\n const sessionRef = useRef<DictationSession | null>(null)\n /** Cancels a start whose getUserMedia has not resolved yet. */\n const cancelPendingStartRef = useRef<(() => void) | null>(null)\n // The recorder's event handlers fire outside React's render, so they read the\n // LATEST callbacks — a re-rendered host must not have its audio delivered to\n // the props the recording started with.\n const callbacksRef = useRef({ onDictate, onError })\n callbacksRef.current = { onDictate, onError }\n\n // The visible elapsed ticker. Follows `recording`; reset on each start so a\n // reused composer never opens at the previous capture's stale count.\n useEffect(() => {\n if (!recording) return\n setElapsedSeconds(0)\n const id = setInterval(() => setElapsedSeconds((s) => s + 1), 1000)\n return () => clearInterval(id)\n }, [recording])\n\n /** End the session: release the mic, reset state. Delivery is onstop's job. */\n const teardown = useCallback((cancelled: boolean) => {\n const session = sessionRef.current\n if (session === null) return\n session.cancelled = session.cancelled || cancelled\n sessionRef.current = null\n releaseStream(session.stream)\n setRecording(false)\n }, [])\n\n const stop = useCallback(() => {\n // A stop while the permission prompt is still open cancels the start: when\n // the stream arrives it is released unused, and nothing ever records.\n cancelPendingStartRef.current?.()\n cancelPendingStartRef.current = null\n const session = sessionRef.current\n if (session === null || session.cancelled) return\n // stop() flushes the buffered chunk (dataavailable) and THEN fires stop —\n // the blob is assembled in onstop, so a stop mid-chunk loses nothing.\n if (session.recorder.state !== 'inactive') session.recorder.stop()\n }, [])\n\n const start = useCallback(() => {\n if (!supported) return\n if (sessionRef.current !== null || cancelPendingStartRef.current !== null) return\n\n let pendingCancelled = false\n cancelPendingStartRef.current = () => {\n pendingCancelled = true\n }\n\n navigator.mediaDevices.getUserMedia({ audio: true }).then(\n (stream) => {\n cancelPendingStartRef.current = null\n if (pendingCancelled) {\n releaseStream(stream)\n return\n }\n const mimeType = pickDictationMimeType()\n const recorder = new MediaRecorder(stream, mimeType === undefined ? undefined : { mimeType })\n const session: DictationSession = {\n stream,\n recorder,\n chunks: [],\n mimeType: recorder.mimeType || mimeType || '',\n startedAt: Date.now(),\n cancelled: false,\n }\n sessionRef.current = session\n\n recorder.ondataavailable = (event) => {\n if (event.data.size > 0) session.chunks.push(event.data)\n }\n\n recorder.onstop = () => {\n teardown(session.cancelled)\n if (session.cancelled) return\n const blob = new Blob(session.chunks, { type: session.mimeType })\n if (blob.size === 0) {\n // A tap on/off can produce no bytes at all. Handing the host a\n // 0-byte blob reads as a recording that happened; it did not.\n callbacksRef.current.onError?.('Nothing was recorded.')\n return\n }\n const durationSeconds = Math.max(0, Math.round((Date.now() - session.startedAt) / 1000))\n callbacksRef.current.onDictate({ blob, mimeType: session.mimeType, durationSeconds })\n }\n\n recorder.onerror = () => {\n // The capture is dead; what matters is that the mic is released and\n // the hook is not wedged — the next start builds a fresh session.\n teardown(true)\n callbacksRef.current.onError?.('Recording stopped unexpectedly.')\n }\n\n recorder.start()\n setRecording(true)\n },\n (error: unknown) => {\n cancelPendingStartRef.current = null\n if (pendingCancelled) return\n callbacksRef.current.onError?.(dictationErrorMessage(error))\n },\n )\n }, [supported, teardown])\n\n // Unmount mid-recording discards the capture: mark it cancelled so onstop\n // delivers nothing, then stop the recorder to flush its events, and release\n // the mic whether or not those events ever fire.\n useEffect(\n () => () => {\n cancelPendingStartRef.current?.()\n cancelPendingStartRef.current = null\n const session = sessionRef.current\n if (session === null) return\n session.cancelled = true\n sessionRef.current = null\n try {\n if (session.recorder.state !== 'inactive') session.recorder.stop()\n } finally {\n releaseStream(session.stream)\n }\n },\n [],\n )\n\n return { supported, recording, elapsedSeconds, start, stop }\n}\n","/**\n * ChatComposer — the shared message input every agent app used to hand-roll:\n * an auto-resizing textarea (Enter sends, Shift+Enter inserts a newline), an\n * opt-in attach + drag-and-drop + clipboard-paste surface with pending-file\n * chips, an opt-in `@`-mention mode (`mention`) that swaps the textarea for a\n * lazily loaded rich input with atomic mention pills, a streaming Stop/Send\n * toggle, a slot for inline controls (model picker, reasoning effort), and a\n * Cmd/Ctrl+L focus shortcut.\n *\n * Files arrive by three routes — the picker dialog, a drop, and a paste — and\n * all three funnel through `accept` (`./composer-file-accept`) before they\n * reach `onAttach`, so a type the picker will not offer cannot get in by\n * another route. What `accept` refuses goes to `onRejectFiles` with a reason;\n * without that prop a refusal is silent, which is what the native picker also\n * does. Size and count limits stay the host's job — `useComposerAttachments`\n * owns them, because they depend on what is already staged.\n *\n * A REJECTED send never destroys the draft. The input clears optimistically —\n * the composer stays editable while a turn streams precisely so the next\n * message can be typed against a live answer, and holding the sent text in the\n * box until the server confirms would put the clear on a collision course with\n * that typing. So the clear happens immediately and the draft is held until the\n * send is known to have landed: a handler that throws, rejects, or returns\n * `{ ok: false }` puts the exact bytes back with the caret where it was, names\n * the reason, and reports `onSendFailed` so the host can restore the\n * attachments it consumed. If the user has already typed a replacement, the\n * unsent text is shown in the notice with its own Retry instead of overwriting\n * what they typed — neither draft is ever destroyed.\n *\n * Styling contract matches the rest of `web-react`: Tailwind over the shared\n * design tokens (`bg-card`, `border-border`, `text-foreground`, `bg-primary`, …)\n * and inline-SVG glyphs. It defines NO `--chat-*` / `--brand-*` custom\n * properties, so it themes correctly in any shell that provides the standard\n * tokens — the input renders on-palette instead of collapsing to unstyled\n * fallbacks when a host hasn't defined a private chat-token set.\n */\n\nimport {\n Component,\n lazy,\n Suspense,\n useCallback,\n useEffect,\n useMemo,\n useId,\n useRef,\n useState,\n type ChangeEvent,\n type ClipboardEvent,\n type DragEvent,\n type KeyboardEvent,\n type ReactNode,\n} from 'react'\n\nimport {\n filterAcceptedFiles,\n renamePastedImages,\n type ComposerFileRejection,\n} from './composer-file-accept'\nimport { filterCommandPaletteItems, type CommandPaletteItem } from '../session-shell/index'\nimport { OVERLAY_SHADOW, POPOVER_OPTION_FOCUS, PopoverSurface } from './controls'\nimport { formatDictationElapsed, useDictation, type DictationAudio } from './use-dictation'\nimport type { ComposerMentionProp } from './use-file-mentions'\n\n/**\n * The TipTap editor is a lazy chunk: only consumers that pass `mention` pull\n * the editor stack into their bundle, and only when the mention path renders.\n * The `@tiptap/*` packages behind it are OPTIONAL peers reached only\n * through `loadMentionEditor`'s dynamic imports — a bundler replaces a missing\n * one with a runtime-throwing stub, so a consumer without them still builds\n * and fails loudly only if the editor actually loads (see mention-editor.tsx,\n * whose loader error names the complete install set).\n *\n * Built per retry rather than once at module scope: `lazy` caches a rejected\n * load forever, so recovering from a transient chunk-fetch failure needs a\n * fresh `lazy` identity (see `MentionEditorBoundary`).\n */\nfunction createLazyMentionEditor() {\n return lazy(() => import('./mention-editor').then((m) => m.loadMentionEditor()))\n}\n\n/**\n * Contains a mention-editor failure to the input area. A rejected lazy chunk\n * (most likely the named missing-`@tiptap/*` error from `loadTiptapModules`)\n * would otherwise unwind past the composer and unmount the host's whole\n * region. This is containment, not a silent fallback: the error renders as a\n * visible alert naming the cause where the input would be — a misconfigured\n * consumer cannot mistake it for a working composer. Retry re-imports through\n * a fresh `lazy` identity — it recovers a transient fetch failure (a deploy\n * that invalidated chunk hashes, a network blip), while a missing peer just\n * fails loudly again.\n */\nclass MentionEditorBoundary extends Component<\n {\n onRetry: () => void\n /** Reported once per failure so the composer can gate Send — a draft the\n * user can no longer see or edit must not stay dispatchable. */\n onFailed: () => void\n /** The current draft, shown read-only in the error state so its content\n * is never invisible while it exists. */\n draft: string\n children: ReactNode\n },\n { error: unknown | null }\n> {\n state: { error: unknown | null } = { error: null }\n\n static getDerivedStateFromError(error: unknown) {\n return { error }\n }\n\n componentDidCatch() {\n this.props.onFailed()\n }\n\n render() {\n if (this.state.error === null) return this.props.children\n const message =\n this.state.error instanceof Error ? this.state.error.message : String(this.state.error)\n return (\n <div\n role=\"alert\"\n data-testid=\"composer-mention-editor-error\"\n className=\"rounded-xl border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive\"\n >\n <div className=\"flex items-start gap-2\">\n <span className=\"min-w-0 flex-1\">The mention input failed to load: {message}</span>\n <button\n type=\"button\"\n aria-label=\"Retry loading the mention input\"\n onClick={this.props.onRetry}\n className=\"shrink-0 font-medium underline-offset-2 hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-destructive/50\"\n >\n Retry\n </button>\n </div>\n {this.props.draft.trim() !== '' && (\n <p\n data-testid=\"composer-error-held-draft\"\n className=\"mt-1.5 max-h-20 overflow-y-auto whitespace-pre-wrap rounded-lg border border-destructive/30 bg-card px-2 py-1 text-foreground\"\n >\n {this.props.draft}\n </p>\n )}\n </div>\n )\n }\n}\n\n// ── glyphs (no icon-library dependency) ───────────────────────────────────\n\n/** The focus-shortcut hint names the platform's modifier: Cmd on Apple,\n * Ctrl everywhere else (the handler itself listens for both). SSR-safe —\n * defaults to Ctrl when there's no navigator to ask. */\nconst IS_APPLE_PLATFORM =\n typeof navigator !== 'undefined' && /Mac|iPhone|iPad|iPod/i.test(navigator.platform)\n\nfunction SendGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M22 2 11 13M22 2l-7 20-4-9-9-4 20-7z\" />\n </svg>\n )\n}\n\nfunction StopGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden>\n <rect x=\"6\" y=\"6\" width=\"12\" height=\"12\" rx=\"2\" />\n </svg>\n )\n}\n\nfunction ArrowUpGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M12 19V5M5 12l7-7 7 7\" />\n </svg>\n )\n}\n\nfunction PaperclipGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48\" />\n </svg>\n )\n}\n\nfunction FolderGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M4 20h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.93a2 2 0 0 1-1.66-.9l-.82-1.2A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13c0 1.1.9 2 2 2Z\" />\n <path d=\"M12 10v6m-3-3h6\" />\n </svg>\n )\n}\n\nfunction CloseGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n )\n}\n\nfunction RetryGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\" />\n <path d=\"M3 3v5h5\" />\n </svg>\n )\n}\n\nfunction UploadGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M17 8l-5-5-5 5M12 3v12\" />\n </svg>\n )\n}\n\nfunction MicGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <rect x=\"9\" y=\"2\" width=\"6\" height=\"12\" rx=\"3\" />\n <path d=\"M5 10v1a7 7 0 0 0 14 0v-1M12 18v4\" />\n </svg>\n )\n}\n\n// ── component ──────────────────────────────────────────────────────────────\n\n/** Prompt-part descriptor an uploaded file carries (the upload route's\n * `UploadedChatFile.part`), echoed back in the turn body on send. Mirrors\n * `/chat-routes`' wire shape structurally — no server import here. */\nexport interface ComposerFilePart {\n type: 'image' | 'file'\n filename?: string\n mediaType?: string\n url?: string\n path?: string\n content?: string\n}\n\nexport interface ComposerFile {\n id: string\n name: string\n size?: number\n kind: 'file' | 'folder'\n /** Number of files inside, for a folder chip. */\n fileCount?: number\n status: 'pending' | 'uploading' | 'ready' | 'error'\n /** Uploaded part descriptor; set once the upload route returns. Only\n * `status: 'ready'` files with a part travel on a parts-aware send. */\n part?: ComposerFilePart\n /** Object URL for an image thumbnail on the chip. The host owns the URL's\n * whole life — `URL.createObjectURL` when the file is staged,\n * `URL.revokeObjectURL` when it leaves — and the composer only reads it.\n * `useComposerAttachments` already does both. */\n previewUrl?: string\n /** Why this file failed, shown on the chip while `status: 'error'`. Without\n * it an error chip is red and mute, which tells the user nothing. */\n errorMessage?: string\n}\n\n/** A piece of context the agent will see beside the next message — an open\n * file, a selected record, a pinned document. Rendered as its own chip row,\n * separate from staged attachments: context is what the turn already carries,\n * an attachment is what the user is adding to it. */\nexport interface ComposerContextItem {\n id: string\n label: string\n icon?: ReactNode\n /** Omit for a chip the user cannot dismiss. */\n onRemove?: () => void\n}\n\n/** A send the host refused. `error` is shown verbatim in the composer's notice;\n * omit it for the generic copy. */\nexport interface ComposerSendRejected {\n ok: false\n error?: string\n}\n\n/**\n * What a send handler reports back. `void` — what every handler returned before\n * this existed — reads as accepted, so wiring stays unchanged; a thrown error, a\n * rejected promise, or `{ ok: false }` is the rejection that restores the draft.\n * A handler that resolves only when the whole turn finishes still reports\n * correctly: the input already cleared on dispatch, so the answer only decides\n * whether the draft comes back.\n */\nexport type ComposerSendOutcome = void | { ok: true } | ComposerSendRejected\nexport type ComposerSendResult = ComposerSendOutcome | Promise<ComposerSendOutcome>\n\n/**\n * A send handler, typed as a UNION with the legacy `=> void` signature rather\n * than as `(…) => ComposerSendResult` alone.\n *\n * TypeScript's return-type-`void` rule accepts a function returning ANYTHING\n * where a `=> void` is expected, and that rule fires only when the target's\n * return type is exactly `void` — not when it is a union that contains `void`.\n * So narrowing this prop to `ComposerSendResult` would reject handler shapes\n * that compiled against the shipped `onSend?: (message: string) => void`:\n * `onSend={(m) => rows.push(m)}` (returns `number`) and\n * `onSend={(m) => append({ role: 'user', content: m })}` (an ai-sdk append\n * returns `Promise<string | null | undefined>`) both stop compiling, on a\n * package whose pinned consumers must never need a source edit to take a minor.\n *\n * The union keeps both: a legacy handler lands on the first member, and a\n * handler that reports an outcome lands on the second. A call through it\n * resolves to `void | ComposerSendResult`, which IS `ComposerSendResult`, so\n * the composer reads the outcome exactly as before.\n */\nexport type ComposerSendHandler =\n | ((message: string) => void)\n | ((message: string) => ComposerSendResult)\n\n/** @see ComposerSendHandler — the parts-aware arity, same union for the same reason. */\nexport type ComposerSendPartsHandler =\n | ((message: string, parts: ComposerFilePart[]) => void)\n | ((message: string, parts: ComposerFilePart[]) => ComposerSendResult)\n\n/** The rejected send, handed to `onSendFailed` so the host can undo whatever it\n * cleared optimistically — most importantly the staged attachments, which the\n * composer does not own (`pendingFiles` is a prop). */\nexport interface ComposerSendFailure {\n /** The reason as the composer renders it. */\n message: string\n /** The user's exact draft, untrimmed. */\n text: string\n /** The parts the rejected send carried. */\n parts: ComposerFilePart[]\n /** Whatever the handler threw / rejected with, or the `{ ok: false }` value. */\n error: unknown\n /** True when the draft was put back in the textarea (the box was empty).\n * False means the user had typed a replacement, so the unsent text is held in\n * the notice instead. */\n restored: boolean\n}\n\n/**\n * One `/` command the composer offers. Typing `/` at position 0 opens the\n * command menu; the rest of the token filters it (the same prefix > substring\n * > token-order ranking as the command palette). Picking a command CLEARS the\n * token from the draft and calls `run` — what the command does (a route, a\n * dialog, a draft transformation) is the product's business.\n */\nexport interface SlashCommand {\n /** Command name without the leading slash: `model`, `clear`. */\n name: string\n /** One line of what it does, rendered beside the name. */\n description: string\n run: () => void\n}\n\nexport interface ChatComposerProps {\n /** Send the trimmed, non-empty message. Attached files travel separately via\n * `onAttach` + `pendingFiles` (the host consumes and clears them on send).\n * Optional when `onSendParts` is wired.\n *\n * Report a refused send by throwing, rejecting, or returning `{ ok: false }`\n * — the composer restores the draft rather than losing it. */\n onSend?: ComposerSendHandler\n /** Parts-aware send: receives the trimmed message plus the `part`\n * descriptors of every `ready` pending file. Takes precedence over\n * `onSend`; enables file-only sends (empty text, ≥1 ready part).\n *\n * Same rejection contract as `onSend`. */\n onSendParts?: ComposerSendPartsHandler\n /** Notified when a send is rejected, after the composer has restored what it\n * owns. The host uses it to put back the `pendingFiles` it consumed. */\n onSendFailed?: (failure: ComposerSendFailure) => void\n /** Notice copy when the handler names no reason of its own. */\n sendFailureMessage?: string\n /** Stop the in-flight turn; shown in place of Send while `isStreaming`. */\n onCancel?: () => void\n isStreaming?: boolean\n /** Block input + send (e.g. while restoring). Distinct from `isStreaming`,\n * which keeps the textarea editable so the next turn can be composed. */\n disabled?: boolean\n placeholder?: string\n\n /** Controlled value. Omit for self-managed internal state (cleared on send). */\n value?: string\n onValueChange?: (value: string) => void\n /** Initial text in uncontrolled mode; ignored when `value` is provided. */\n initialValue?: string\n\n /** One-shot external prefill: when this becomes a non-null string the\n * composer adopts it as the draft (replacing any current draft), focuses the\n * input with the caret at the end, and reports consumption via\n * `onSeedApplied` so the host can clear its seed state. */\n seed?: string | null\n onSeedApplied?: () => void\n\n /** Inline controls (e.g. `<ModelPicker/>` + `<EffortPicker/>` or\n * `<AgentSessionControls/>`). */\n controls?: ReactNode\n /**\n * Where {@link controls} sit. `inline` (default) puts them on the card's own\n * action row, beside attach and Send — the model a turn will use reads as\n * part of the input rather than as a separate widget floating above it.\n * `above` keeps them outside the card, for a host that wants the input to be\n * nothing but the input.\n */\n controlsPlacement?: 'above' | 'inline'\n\n /** Attachments are opt-in: pass `onAttach` to show the attach button, accept\n * drag-and-drop and clipboard paste onto the input, and render\n * `pendingFiles` chips. */\n onAttach?: (files: FileList) => void\n onAttachFolder?: (files: FileList) => void\n pendingFiles?: ComposerFile[]\n onRemoveFile?: (id: string) => void\n /** Pass it and a chip with `status: 'error'` gains a retry button. */\n onRetryFile?: (id: string) => void\n /**\n * File types the composer takes, in the native `<input accept>` grammar.\n * Enforced on every ingress route — the picker dialog (which the user can\n * override with \"All Files\"), drag-and-drop, and clipboard paste — so a type\n * the picker will not offer cannot arrive by another route. A non-matching\n * file goes to `onRejectFiles` and never reaches `onAttach`. Folder attach is\n * exempt: directory selection has no native accept semantics.\n */\n accept?: string\n /** Called with the files `accept` removed from a pick, drop, or paste, each\n * with a reason. Without it a refusal is silent — the same feedback the\n * native picker gives for a type it will not offer. */\n onRejectFiles?: (rejections: ComposerFileRejection[]) => void\n dropTitle?: string\n dropDescription?: string\n\n /** Context the agent will see beside the next message, as its own chip row\n * above the input. */\n contextItems?: ReadonlyArray<ComposerContextItem>\n\n /**\n * Let a staged file stand in for message text, so the send control stays live\n * while an upload is in flight instead of going dead with nothing to explain\n * it. Default false, where an empty message needs a `ready` file.\n *\n * It does NOT make an unfinished file sendable. A turn whose only content is a\n * file that is still uploading or has failed never reaches the send handler —\n * it would arrive empty and the attachment would be lost. The composer\n * refuses it and names the reason in its notice\n * ({@link attachmentsNotReadyMessage}). So the flag decides whether the\n * control is live, and the composer keeps the integrity gate rather than\n * leaving each host to re-derive it.\n */\n canSubmitAttachmentsOnly?: boolean\n /** Notice copy when a send is refused because no staged file is ready yet.\n * Defaults to wording chosen from whether a file failed or is still\n * uploading. */\n attachmentsNotReadyMessage?: string\n /**\n * Let Enter and Send keep firing while `isStreaming`, for a surface that\n * queues the next turn rather than blocking on the current one. Default\n * false. The button still flips to Stop while a turn streams, so this opens\n * the keyboard path, not a second button.\n */\n canSubmitWhileBusy?: boolean\n\n /** Focus the input on mount — for a surface whose whole job is the input\n * (an entry/hero composer), never for one docked under a transcript. */\n autoFocus?: boolean\n /** Rows the input shows before it grows. Default 2. */\n minRows?: number\n /** Pixel height the input grows to before it scrolls. Default 168. */\n maxHeight?: number\n /** Content between the controls slot and Send — a token meter, a cost, a\n * status line. It sits outside the controls slot and never shrinks, so a\n * wrapping picker set cannot push it away. */\n trailing?: ReactNode\n /**\n * Opt-in `@`-mentions. Present ⇒ the textarea is swapped for a lazily\n * loaded TipTap rich input that renders mentions as atomic pills and\n * serializes them to `@<id>` in the value; absent ⇒ exactly the plain\n * textarea, with no TipTap in the bundle. Wire `useFileMentions().mention`\n * straight in. The six `@tiptap/*` packages (core, extension-mention, pm,\n * react, starter-kit, suggestion) are OPTIONAL peers — a consumer installs\n * them to use this prop.\n *\n * The rich input owns its own keyboard surface, so `slashCommands` is\n * disabled while `mention` is set rather than left half-armed with a menu\n * no key can reach; no shipped surface combines the two. Seed and\n * failed-send drafts still apply in mention mode, but caret placement (a\n * textarea affordance) degrades to content-only.\n */\n mention?: ComposerMentionProp\n /** `/` commands offered when the draft is exactly a leading slash token.\n * Omit (or pass []) and `/` types as ordinary text. Inert while `mention`\n * is set — see {@link mention}. */\n slashCommands?: SlashCommand[]\n /** Dictation is opt-in: pass `onDictate` and the action row gains a mic\n * button (browsers without `MediaRecorder`/`getUserMedia` render none).\n * Click starts the capture; the button flips to a stop control with the\n * running elapsed seconds; stop hands the recorded audio blob here. The\n * composer owns capture only — turning the audio into text (e.g. the\n * Whisper provider from `sequences-react`) is the host's. */\n onDictate?: (audio: DictationAudio) => void\n /** Capture failures (a denied mic prompt, no device), after the composer has\n * shown its own dismissible notice. For hosts that log or track. */\n onDictateError?: (message: string) => void\n\n /** Cmd/Ctrl+L focuses the input and shows the hint. Default true. */\n focusShortcut?: boolean\n /** Float the card on a soft two-layer foreground-tinted shadow (opt-in).\n * Elevation only — radius, ring, and control layout are unchanged. */\n floating?: boolean\n /** Send button label. Default \"Send\". */\n sendLabel?: string\n /** Send control shape. `pill` (default) is the labeled button; `icon` is the\n * 34px circular inverted arrow (streaming: circular outlined stop) — the\n * grammar sandbox-ui's legacy AgentComposer used and the current agent-app\n * canon for new surfaces. */\n sendVariant?: 'pill' | 'icon'\n className?: string\n}\n\nconst DEFAULT_MAX_HEIGHT = 168\n\n/** The input's own `leading-6` line box and its `py-1` padding, in pixels. The\n * `minRows` floor is computed from them so the CSS floor and the `rows`\n * attribute cannot drift apart at a row count other than the default. */\nconst LINE_HEIGHT = 24\nconst TEXTAREA_PADDING_Y = 8\n\nconst DEFAULT_SEND_FAILURE = \"Message not sent. Your draft is still here — try again.\"\n\n/** A rejection the handler reported by value rather than by throwing. */\nfunction isRejectedOutcome(outcome: ComposerSendOutcome): outcome is ComposerSendRejected {\n return typeof outcome === 'object' && outcome !== null && outcome.ok === false\n}\n\nfunction isPromise(value: ComposerSendResult): value is Promise<ComposerSendOutcome> {\n return typeof (value as Promise<ComposerSendOutcome> | undefined)?.then === 'function'\n}\n\n/** The reason to show. A rejection's own `error` string wins; then an Error's\n * message; else the caller's copy — never an empty notice. */\nfunction sendFailureText(error: unknown, fallback: string): string {\n if (typeof error === 'object' && error !== null && 'ok' in error) {\n const named = (error as ComposerSendRejected).error\n if (typeof named === 'string' && named.trim() !== '') return named\n return fallback\n }\n if (typeof error === 'string' && error.trim() !== '') return error\n if (error instanceof Error && error.message.trim() !== '') return error.message\n return fallback\n}\n\ninterface FailedSend {\n message: string\n /** The user's exact draft, untrimmed — what a restore puts back. */\n text: string\n /** The trimmed form the handler was called with, so Retry sends the same\n * bytes the rejected attempt did. */\n trimmed: string\n parts: ComposerFilePart[]\n restored: boolean\n}\n\nexport function ChatComposer({\n onSend,\n onSendParts,\n onSendFailed,\n sendFailureMessage = DEFAULT_SEND_FAILURE,\n onCancel,\n isStreaming = false,\n disabled = false,\n placeholder = 'Message the agent…',\n value,\n onValueChange,\n initialValue,\n seed,\n onSeedApplied,\n controls,\n controlsPlacement = 'inline',\n onAttach,\n onAttachFolder,\n pendingFiles = [],\n onRemoveFile,\n onRetryFile,\n accept,\n onRejectFiles,\n dropTitle = 'Drop files to add context',\n dropDescription = 'They attach to your next message.',\n contextItems = [],\n canSubmitAttachmentsOnly = false,\n attachmentsNotReadyMessage,\n canSubmitWhileBusy = false,\n autoFocus,\n minRows = 2,\n maxHeight = DEFAULT_MAX_HEIGHT,\n trailing,\n mention,\n slashCommands,\n onDictate,\n onDictateError,\n\n focusShortcut = true,\n floating = false,\n sendLabel = 'Send',\n sendVariant = 'pill',\n className,\n}: ChatComposerProps) {\n const isControlled = value !== undefined\n const [internal, setInternal] = useState(initialValue ?? '')\n const text = isControlled ? value : internal\n // A send outcome arrives after the render that dispatched it, so the restore\n // decision must read the LIVE draft, not the one captured in that closure.\n const textRef = useRef(text)\n textRef.current = text\n\n const textareaRef = useRef<HTMLTextAreaElement>(null)\n // Set by the mention editor so autofocus-independent focus paths (the\n // Cmd/Ctrl+L shortcut) reach it in the rich path, where `textareaRef` stays\n // null. The editor registers `null` on unmount, so the shortcut can never\n // call into a destroyed editor.\n const richFocusRef = useRef<(() => void) | null>(null)\n // Stable identity so the mention editor's registration effect only reruns\n // when the editor instance itself changes, not on every parent render.\n const registerRichFocus = useCallback((focus: (() => void) | null) => {\n richFocusRef.current = focus\n }, [])\n // Bumped by the boundary's Retry: a new epoch mints a fresh `lazy` identity\n // (a rejected lazy caches its failure forever) and re-keys the boundary so\n // its error state clears.\n const [editorEpoch, setEditorEpoch] = useState(0)\n const MentionEditor = useMemo(createLazyMentionEditor, [editorEpoch])\n // While the mention editor is failed, the draft is visible only in the\n // boundary's read-only block — Send must not dispatch what the user cannot\n // edit.\n const [editorFailed, setEditorFailed] = useState(false)\n const fileInputRef = useRef<HTMLInputElement>(null)\n const folderInputRef = useRef<HTMLInputElement>(null)\n const [dragOver, setDragOver] = useState(false)\n const dragDepth = useRef(0)\n // Counts every clipboard image this composer has renamed, so two pastes of\n // the same bitmap do not both arrive as `image.png`.\n const pastedImageCount = useRef(0)\n\n const setText = useCallback(\n (next: string) => {\n if (!isControlled) setInternal(next)\n onValueChange?.(next)\n },\n [isControlled, onValueChange],\n )\n\n // Dictation: capture only. The hook reports every failure in words; the\n // composer shows them in its own dismissible notice (the same shape as a\n // rejected send) AND forwards them for hosts that log.\n const [dictateError, setDictateError] = useState<string | null>(null)\n const handleDictated = useCallback(\n (audio: DictationAudio) => {\n setDictateError(null)\n onDictate?.(audio)\n },\n [onDictate],\n )\n const handleDictateError = useCallback(\n (message: string) => {\n setDictateError(message)\n onDictateError?.(message)\n },\n [onDictateError],\n )\n const dictation = useDictation({ onDictate: handleDictated, onError: handleDictateError })\n\n // Keep the textarea height in sync with the content for BOTH typed and\n // external (controlled) value changes — one effect covers both paths. It also\n // reruns when the bounds move: `rows` is what `scrollHeight` resolves the\n // measurement against, so a changed `minRows` that did not re-measure would\n // strand the previous inline height.\n useEffect(() => {\n const el = textareaRef.current\n if (!el) return\n el.style.height = 'auto'\n el.style.height = `${Math.min(el.scrollHeight, maxHeight)}px`\n }, [text, maxHeight, minRows])\n\n // Adopt a one-shot seed. Applies only when the `seed` PROP transitions to a\n // new string (host sets it → consumed here → host clears it via\n // onSeedApplied), so an unstable callback identity re-running this effect\n // can never re-apply a still-set seed over the user's typing. Like\n // `initialValue`, the seed is honored ONLY in uncontrolled mode — a\n // controlled host drives its own `value` (which would shadow `setText`), so\n // it seeds by updating that state itself.\n const prevSeedRef = useRef<string | null>(null)\n const pendingCaretRef = useRef<string | null>(null)\n useEffect(() => {\n const prev = prevSeedRef.current\n prevSeedRef.current = seed ?? null\n if (seed == null || seed === prev || isControlled) return\n setText(seed)\n onSeedApplied?.()\n const el = textareaRef.current\n if (el && el.value === seed) {\n // The DOM already shows the seed — setText was a no-op (the user had\n // typed the exact string), so no re-render is coming and the [text]\n // effect below won't fire. Position the caret now instead of leaving a\n // stranded pendingCaretRef.\n el.focus()\n el.setSelectionRange(seed.length, seed.length)\n } else {\n // Defer caret positioning until the seeded value renders (see below).\n pendingCaretRef.current = seed\n }\n }, [seed, setText, onSeedApplied, isControlled])\n\n // Focus + caret-to-end AFTER the seeded value has rendered into the DOM —\n // setSelectionRange in the applying effect would run against the pre-render\n // value and clamp the caret to the old text's length.\n useEffect(() => {\n if (pendingCaretRef.current == null || pendingCaretRef.current !== text)\n return\n pendingCaretRef.current = null\n const el = textareaRef.current\n if (!el) return\n el.focus()\n el.setSelectionRange(text.length, text.length)\n }, [text])\n\n // A restored draft gets the caret back where the user left it — same\n // post-render rule as the seed above, but to the recorded offsets rather than\n // to the end, so a failed send returns the user to the word they were on.\n const restoreCaretRef = useRef<{ text: string; start: number; end: number } | null>(null)\n useEffect(() => {\n const pending = restoreCaretRef.current\n if (!pending || pending.text !== text) return\n restoreCaretRef.current = null\n const el = textareaRef.current\n if (!el) return\n el.focus()\n const start = Math.min(pending.start, text.length)\n const end = Math.min(pending.end, text.length)\n el.setSelectionRange(start, end)\n }, [text])\n\n // Cmd/Ctrl+L focuses the composer from anywhere — the shortcut the hint\n // advertises. Scoped to when the shortcut is enabled and not disabled.\n // Depends on mention PRESENCE, not identity: hosts build the prop object\n // inline per render, and only the mode switch changes which input to focus.\n const mentionEnabled = mention != null\n useEffect(() => {\n if (!focusShortcut || disabled) return\n function onKeyDown(e: globalThis.KeyboardEvent) {\n if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'l') {\n if (mentionEnabled) {\n // No live editor yet (still loading, or failed): leave the\n // browser's own shortcut alone rather than swallowing it for\n // nothing.\n const focus = richFocusRef.current\n if (!focus) return\n e.preventDefault()\n focus()\n } else {\n e.preventDefault()\n textareaRef.current?.focus()\n }\n }\n }\n document.addEventListener('keydown', onKeyDown)\n return () => document.removeEventListener('keydown', onKeyDown)\n }, [focusShortcut, disabled, mentionEnabled])\n\n // A ready file counts as sendable content even without a `part`: store-backed\n // attachments (`useComposerAttachments`) carry no prompt part — their\n // references ride the turn body's `attachments` field — but a file-only\n // message must still be sendable. `canSubmitAttachmentsOnly` widens that to a\n // file of ANY status, so an in-flight upload leaves the control live and the\n // host's handler decides what to do about it.\n const sendableFiles = canSubmitAttachmentsOnly\n ? pendingFiles\n : pendingFiles.filter((f) => f.status === 'ready')\n const hasSendable = text.trim().length > 0 || sendableFiles.length > 0\n // Streaming blocks a send unless the host queues turns. The button still\n // shows Stop while streaming, so `canSubmitWhileBusy` opens Enter, not a\n // second visible control.\n const sendBlockedByStream = isStreaming && !canSubmitWhileBusy\n const editorInputLost = mention != null && editorFailed\n const canSend = hasSendable && !sendBlockedByStream && !disabled && !editorInputLost\n\n const [failedSend, setFailedSend] = useState<FailedSend | null>(null)\n\n // The draft comes back only when the box is still empty. If the user typed a\n // replacement while the send was in flight, overwriting it would trade one\n // lost message for another — the unsent text is held in the notice instead,\n // where Retry can send it without touching what they typed.\n const failSend = useCallback(\n (error: unknown, draft: string, trimmed: string, parts: ComposerFilePart[], caret: { start: number; end: number }) => {\n const message = sendFailureText(error, sendFailureMessage)\n const restored = textRef.current === ''\n if (restored) {\n const el = textareaRef.current\n setText(draft)\n if (el && el.value === draft) {\n // A handler that rejected SYNCHRONOUSLY did so inside the same event\n // as the clear, so React collapses clear+restore into no state change\n // at all — no re-render is coming and the effect below will never\n // fire. Place the caret now rather than stranding the pending ref.\n el.focus()\n el.setSelectionRange(Math.min(caret.start, draft.length), Math.min(caret.end, draft.length))\n } else {\n restoreCaretRef.current = { text: draft, start: caret.start, end: caret.end }\n }\n }\n setFailedSend({ message, text: draft, trimmed, parts, restored })\n onSendFailed?.({ message, text: draft, parts, error, restored })\n },\n [onSendFailed, sendFailureMessage, setText],\n )\n\n // Hand the message to the host and watch the outcome. The input has already\n // been cleared by the caller — this only decides whether it comes back.\n const dispatchSend = useCallback(\n (draft: string, trimmed: string, parts: ComposerFilePart[], caret: { start: number; end: number }) => {\n let outcome: ComposerSendResult\n try {\n outcome = onSendParts ? onSendParts(trimmed, parts) : onSend?.(trimmed)\n } catch (error) {\n failSend(error, draft, trimmed, parts, caret)\n return\n }\n if (isPromise(outcome)) {\n void outcome.then(\n (settled) => {\n if (isRejectedOutcome(settled)) failSend(settled, draft, trimmed, parts, caret)\n },\n (error: unknown) => failSend(error, draft, trimmed, parts, caret),\n )\n return\n }\n if (isRejectedOutcome(outcome)) failSend(outcome, draft, trimmed, parts, caret)\n },\n [onSend, onSendParts, failSend],\n )\n\n const send = useCallback(() => {\n const trimmed = text.trim()\n if (sendBlockedByStream || disabled || editorInputLost) return\n const readyFiles = pendingFiles.filter((f) => f.status === 'ready')\n const sendable = canSubmitAttachmentsOnly ? pendingFiles : readyFiles\n if (!trimmed && sendable.length === 0) return\n // `canSubmitAttachmentsOnly` keeps the control live while a file is staged,\n // but a turn carrying no text and no file the host can deliver must not go\n // out: it would arrive empty and the attachment would be lost. Refuse it\n // here and say why, rather than dispatching and trusting every host to\n // re-derive the same check.\n if (!trimmed && readyFiles.length === 0) {\n const message =\n attachmentsNotReadyMessage ??\n (pendingFiles.some((f) => f.status === 'error')\n ? 'Retry or remove the failed attachment before sending.'\n : 'Wait for the attachment to finish uploading.')\n setFailedSend({ message, text: '', trimmed: '', parts: [], restored: true })\n return\n }\n // Only a parts-aware send carries parts; `onSend`'s files travel through the\n // host's own `pendingFiles`, so its failure payload names none. Parts come\n // from READY files whatever `canSubmitAttachmentsOnly` says — an unfinished\n // upload has no part to send.\n const parts = onSendParts\n ? readyFiles.filter((f) => f.part).map((f) => f.part as ComposerFilePart)\n : []\n const el = textareaRef.current\n const caret = { start: el?.selectionStart ?? text.length, end: el?.selectionEnd ?? text.length }\n setFailedSend(null)\n setText('')\n textRef.current = ''\n dispatchSend(text, trimmed, parts, caret)\n }, [\n text,\n sendBlockedByStream,\n disabled,\n editorInputLost,\n canSubmitAttachmentsOnly,\n attachmentsNotReadyMessage,\n onSendParts,\n pendingFiles,\n setText,\n dispatchSend,\n ])\n\n // Re-send the message the notice is holding. Reached only when the draft was\n // NOT restored (the restored path leaves the text in the box, where Send is\n // the affordance), so it never competes with the primary control.\n const retryFailedSend = useCallback(() => {\n const failure = failedSend\n if (!failure || sendBlockedByStream || disabled) return\n setFailedSend(null)\n const caret = { start: failure.text.length, end: failure.text.length }\n dispatchSend(failure.text, failure.trimmed, failure.parts, caret)\n }, [failedSend, sendBlockedByStream, disabled, dispatchSend])\n\n // ── '/' commands ─────────────────────────────────────────────────────────\n // The menu exists only while the WHOLE draft is one leading slash token\n // (`/`, `/mod`). The first space ends it — arguments are ordinary text. Esc\n // or an outside click dismisses for the CURRENT token only, so continued\n // typing reopens the menu instead of leaving it permanently suppressed.\n const slashPanelRef = useRef<HTMLDivElement>(null)\n const cardRef = useRef<HTMLDivElement>(null)\n const slashListId = useId()\n const [slashActive, setSlashActive] = useState(0)\n const [slashDismissedFor, setSlashDismissedFor] = useState<string | null>(null)\n // `mention` disables the slash menu outright: its keydown/anchor wiring is\n // the textarea's, so a token match in the rich path would arm a menu no key\n // or click can reach. See the `mention` prop doc.\n const slashToken =\n !mention && slashCommands && slashCommands.length > 0 ? /^\\/(\\S*)$/.exec(text)?.[1] : undefined\n const slashOpen = slashToken !== undefined && text !== slashDismissedFor\n const slashItems = useMemo<CommandPaletteItem[]>(\n () =>\n (slashCommands ?? []).map((command) => ({\n id: command.name,\n group: 'Commands',\n label: `/${command.name}`,\n description: command.description,\n keywords: [command.name, command.description],\n })),\n [slashCommands],\n )\n const slashFiltered = useMemo(\n () => (slashToken === undefined ? [] : filterCommandPaletteItems(slashItems, slashToken)),\n [slashItems, slashToken],\n )\n const slashActiveIndex = slashFiltered.length === 0 ? 0 : Math.min(slashActive, slashFiltered.length - 1)\n\n useEffect(() => {\n setSlashActive(0)\n }, [slashToken])\n\n useEffect(() => {\n if (!slashOpen) return\n document\n .getElementById(`${slashListId}-${slashActiveIndex}`)\n ?.scrollIntoView?.({ block: 'nearest' })\n }, [slashOpen, slashActiveIndex, slashListId])\n\n useEffect(() => {\n if (!slashOpen) return\n function onMouseDown(e: MouseEvent) {\n const target = e.target as Node\n if (cardRef.current?.contains(target)) return\n if (slashPanelRef.current?.contains(target)) return\n setSlashDismissedFor(textRef.current)\n }\n document.addEventListener('mousedown', onMouseDown)\n return () => document.removeEventListener('mousedown', onMouseDown)\n }, [slashOpen])\n\n const pickSlash = useCallback(\n (name: string) => {\n const command = slashCommands?.find((c) => c.name === name)\n // The draft IS the slash token (the menu only opens while it is), so the\n // pick consumes it: clear the box, then run.\n setText('')\n setSlashDismissedFor(null)\n command?.run()\n },\n [slashCommands, setText],\n )\n\n const handleKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {\n // Respect IME composition — Enter commits the candidate, it doesn't send.\n if (e.nativeEvent.isComposing) return\n if (slashOpen) {\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n if (slashFiltered.length > 0) setSlashActive((slashActiveIndex + 1) % slashFiltered.length)\n return\n }\n if (e.key === 'ArrowUp') {\n e.preventDefault()\n if (slashFiltered.length > 0)\n setSlashActive((slashActiveIndex - 1 + slashFiltered.length) % slashFiltered.length)\n return\n }\n if ((e.key === 'Enter' && !e.shiftKey) || e.key === 'Tab') {\n const item = slashFiltered[slashActiveIndex]\n if (item) {\n e.preventDefault()\n pickSlash(item.id)\n return\n }\n // No command matched — fall through and let Enter send the raw text.\n }\n if (e.key === 'Escape') {\n e.preventDefault()\n setSlashDismissedFor(text)\n return\n }\n }\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n send()\n }\n }\n\n // Every route a file can arrive by ends here: apply `accept`, report what it\n // removed, and hand `onAttach` only what passed. A batch the filter left\n // untouched is forwarded as the browser's own `FileList`; one it changed is\n // rebuilt, since `onAttach` takes a `FileList` and only a `DataTransfer` can\n // produce one.\n const deliverFiles = useCallback(\n (files: File[], original: FileList) => {\n if (!onAttach || files.length === 0) return\n const { accepted, rejected } = filterAcceptedFiles(files, accept)\n if (rejected.length > 0) onRejectFiles?.(rejected)\n if (accepted.length === 0) return\n const unchanged =\n accepted.length === original.length && accepted.every((file, i) => file === original[i])\n if (unchanged) {\n onAttach(original)\n return\n }\n const transfer = new DataTransfer()\n for (const file of accepted) transfer.items.add(file)\n onAttach(transfer.files)\n },\n [onAttach, onRejectFiles, accept],\n )\n\n const handleFileChange = (e: ChangeEvent<HTMLInputElement>) => {\n // Re-filter: a picker dialog lets the user override `accept` with\n // \"All Files\", so the attribute alone does not hold the gate.\n if (e.target.files?.length) deliverFiles(Array.from(e.target.files), e.target.files)\n e.target.value = ''\n }\n\n // One paste core for both input modes. Returns true when files were the\n // payload — the caller then suppresses the default text paste even when\n // every file is refused, so a rejection never half-pastes stray text.\n // The staged names go in alongside the count: the queue is the host's and\n // can outlive this mount, so the count alone could hand the next paste a\n // name the queue already holds.\n const ingestPastedFiles = (clipboardFiles: FileList): boolean => {\n if (!onAttach || clipboardFiles.length === 0) return false\n const { files, nextIndex } = renamePastedImages(\n Array.from(clipboardFiles),\n pastedImageCount.current,\n pendingFiles.map((f) => f.name),\n )\n pastedImageCount.current = nextIndex\n deliverFiles(files, clipboardFiles)\n return true\n }\n\n const handlePaste = (e: ClipboardEvent<HTMLTextAreaElement>) => {\n const clipboardFiles = e.clipboardData?.files\n if (!clipboardFiles || clipboardFiles.length === 0) return\n if (ingestPastedFiles(clipboardFiles)) e.preventDefault()\n }\n\n const handleFolderChange = (e: ChangeEvent<HTMLInputElement>) => {\n if (e.target.files?.length) (onAttachFolder ?? onAttach)?.(e.target.files)\n e.target.value = ''\n }\n\n const handleDragEnter = useCallback((e: DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n dragDepth.current++\n if (e.dataTransfer?.types.includes('Files')) setDragOver(true)\n }, [])\n\n const handleDragLeave = useCallback((e: DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n dragDepth.current--\n if (dragDepth.current <= 0) {\n dragDepth.current = 0\n setDragOver(false)\n }\n }, [])\n\n const handleDragOver = useCallback((e: DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n if (e.dataTransfer) e.dataTransfer.dropEffect = 'copy'\n }, [])\n\n const handleDrop = useCallback(\n (e: DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n dragDepth.current = 0\n setDragOver(false)\n const files = e.dataTransfer?.files\n if (files?.length) deliverFiles(Array.from(files), files)\n },\n [deliverFiles],\n )\n\n const folderChips = pendingFiles.filter((f) => f.kind === 'folder')\n const fileChips = pendingFiles.filter((f) => f.kind !== 'folder')\n // `above` is the only placement that takes controls OUT of the card, so it is\n // the only one matched exactly; everything else falls to inline. That keeps a\n // retired value (this prop used to accept `footer` for the same placement) or a\n // typo rendering the controls somewhere rather than nowhere — dropping them\n // silently is the one outcome with no recovery for the reader.\n const showAbove = controls != null && controlsPlacement === 'above'\n const showInline = controls != null && !showAbove\n\n // One floor for both input modes, so the mention editor and the textarea\n // cannot disagree about the empty-composer height.\n const inputMinHeight = minRows * LINE_HEIGHT + TEXTAREA_PADDING_Y\n\n return (\n <div\n className={`relative ${className ?? ''}`}\n onDragEnter={onAttach ? handleDragEnter : undefined}\n onDragLeave={onAttach ? handleDragLeave : undefined}\n onDragOver={onAttach ? handleDragOver : undefined}\n onDrop={onAttach ? handleDrop : undefined}\n >\n {dragOver && (\n <div className=\"pointer-events-none absolute inset-0 z-10 flex items-center justify-center rounded-2xl border-2 border-dashed border-primary/50 bg-card\">\n <div className=\"text-center\">\n <span className=\"mx-auto mb-2 flex h-11 w-11 items-center justify-center rounded-xl bg-primary/10 text-primary\">\n <UploadGlyph className=\"h-5 w-5\" />\n </span>\n <p className=\"text-sm font-semibold text-foreground\">{dropTitle}</p>\n <p className=\"mt-0.5 text-xs text-muted-foreground\">{dropDescription}</p>\n </div>\n </div>\n )}\n\n {showAbove && <div className=\"mb-1.5 flex flex-wrap items-center gap-1.5 px-1\">{controls}</div>}\n\n {dictateError && (\n <div\n role=\"alert\"\n data-testid=\"composer-dictate-error\"\n className=\"mb-2 flex items-start gap-2 rounded-xl border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive\"\n >\n <span className=\"min-w-0 flex-1\">{dictateError}</span>\n <button\n type=\"button\"\n aria-label=\"Dismiss dictation error\"\n onClick={() => setDictateError(null)}\n className=\"shrink-0 font-medium underline-offset-2 hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-destructive/50\"\n >\n Dismiss\n </button>\n </div>\n )}\n\n {failedSend && (\n <div\n role=\"alert\"\n data-testid=\"composer-send-error\"\n className=\"mb-2 rounded-xl border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive\"\n >\n <div className=\"flex items-start gap-2\">\n <span className=\"min-w-0 flex-1\">{failedSend.message}</span>\n <button\n type=\"button\"\n aria-label=\"Dismiss send error\"\n onClick={() => setFailedSend(null)}\n className=\"shrink-0 font-medium underline-offset-2 hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-destructive/50\"\n >\n Dismiss\n </button>\n </div>\n {/* The draft is only held here when it could NOT go back in the box —\n the user typed a replacement. Showing the bytes is what makes the\n message recoverable by hand even if Retry keeps failing. */}\n {!failedSend.restored && (\n <div className=\"mt-1.5\">\n <p\n data-testid=\"composer-unsent-draft\"\n className=\"max-h-20 overflow-y-auto whitespace-pre-wrap rounded-lg border border-destructive/30 bg-card px-2 py-1 text-foreground\"\n >\n {failedSend.text}\n </p>\n <button\n type=\"button\"\n aria-label=\"Retry sending the unsent message\"\n onClick={retryFailedSend}\n disabled={sendBlockedByStream || disabled}\n className=\"mt-1.5 font-medium underline-offset-2 hover:underline disabled:cursor-not-allowed disabled:opacity-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-destructive/50\"\n >\n Retry\n </button>\n </div>\n )}\n </div>\n )}\n\n {contextItems.length > 0 && (\n <div aria-label=\"Message context\" className=\"mb-2 flex min-w-0 flex-wrap gap-1.5\">\n {contextItems.map((item) => (\n <span\n key={item.id}\n className=\"inline-flex min-w-0 max-w-full items-center gap-1.5 rounded-md border border-primary/30 bg-primary/10 px-2.5 py-1 text-xs text-primary\"\n >\n {item.icon && (\n <span className=\"shrink-0\" aria-hidden>\n {item.icon}\n </span>\n )}\n <span className=\"min-w-0 truncate\">{item.label}</span>\n {item.onRemove && (\n <button\n type=\"button\"\n aria-label={`Remove context ${item.label}`}\n onClick={item.onRemove}\n className=\"shrink-0 rounded p-0.5 text-primary/70 transition hover:text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <CloseGlyph className=\"h-3 w-3\" />\n </button>\n )}\n </span>\n ))}\n </div>\n )}\n\n {pendingFiles.length > 0 && (\n <div className=\"mb-2 flex flex-wrap gap-1.5\">\n {[...folderChips, ...fileChips].map((f) => {\n const isError = f.status === 'error'\n return (\n <span\n key={f.id}\n title={isError ? f.errorMessage : undefined}\n className={`inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs ${\n isError\n ? 'border-destructive/40 text-destructive'\n : 'border-border bg-secondary text-foreground'\n } ${f.status === 'pending' ? 'opacity-60' : ''}`}\n >\n {/* A thumbnail identifies a pasted screenshot that the\n auto-generated name cannot. Folders never have one. */}\n {f.kind !== 'folder' && f.previewUrl ? (\n <img src={f.previewUrl} alt=\"\" className=\"h-8 w-8 shrink-0 rounded object-cover\" />\n ) : f.kind === 'folder' ? (\n <FolderGlyph className=\"h-3 w-3 shrink-0\" />\n ) : (\n <PaperclipGlyph className=\"h-3 w-3 shrink-0\" />\n )}\n <span className=\"max-w-[150px] truncate\">{f.name}</span>\n {f.fileCount !== undefined && <span className=\"text-muted-foreground\">({f.fileCount})</span>}\n {f.status === 'uploading' && (\n <span className=\"h-3 w-3 animate-spin rounded-full border-2 border-primary border-t-transparent\" />\n )}\n {isError && f.errorMessage && (\n <span className=\"max-w-[150px] truncate text-destructive/80\">{f.errorMessage}</span>\n )}\n {isError && onRetryFile && (\n <button\n type=\"button\"\n aria-label={`Retry upload ${f.name}`}\n onClick={() => onRetryFile(f.id)}\n className=\"rounded p-0.5 text-muted-foreground transition hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <RetryGlyph className=\"h-3 w-3\" />\n </button>\n )}\n {onRemoveFile && (\n <button\n type=\"button\"\n aria-label={`Remove ${f.name}`}\n onClick={() => onRemoveFile(f.id)}\n className=\"rounded p-0.5 text-muted-foreground transition hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <CloseGlyph className=\"h-3 w-3\" />\n </button>\n )}\n </span>\n )\n })}\n </div>\n )}\n\n {/* Two rows inside one card: the message gets the full width, and every\n affordance that acts on it — attach, the controls slot, send — sits on\n its own row beneath. A single row would make the textarea share its\n line with the buttons, which is what squeezed the input and pushed the\n controls out of the card in the first place. */}\n <div\n ref={cardRef}\n data-testid=\"composer-card\"\n className={`flex flex-col gap-1.5 rounded-2xl border border-card-edge bg-card px-3 py-2.5 transition focus-within:border-primary/40 focus-within:ring-2 focus-within:ring-primary/15 ${\n floating ? 'shadow-raised' : ''\n }`}\n >\n {mention ? (\n // The editor arrives as a lazy chunk; until it lands, a read-only\n // textarea with the same metrics holds the layout so the card\n // doesn't jump. The boundary contains a failed load (e.g. the\n // missing-peer error) to the input area instead of unmounting the\n // host's region.\n <MentionEditorBoundary\n key={editorEpoch}\n onRetry={() => {\n setEditorFailed(false)\n setEditorEpoch((epoch) => epoch + 1)\n }}\n onFailed={() => setEditorFailed(true)}\n draft={text}\n >\n <Suspense\n fallback={\n <textarea\n rows={minRows}\n value={text}\n readOnly\n disabled\n placeholder={placeholder}\n aria-label=\"Message input\"\n style={{ minHeight: inputMinHeight, maxHeight }}\n className=\"w-full resize-none bg-transparent px-1.5 py-1 text-base leading-6 text-foreground outline-none placeholder:text-muted-foreground disabled:opacity-50\"\n />\n }\n >\n <MentionEditor\n value={text}\n onChange={setText}\n onSubmit={send}\n placeholder={placeholder}\n disabled={disabled}\n autoFocus={autoFocus}\n minHeight={inputMinHeight}\n maxHeight={maxHeight}\n mention={mention}\n registerFocus={registerRichFocus}\n onPasteFiles={onAttach ? ingestPastedFiles : undefined}\n />\n </Suspense>\n </MentionEditorBoundary>\n ) : (\n // Focus: `outline-none` is safe because the card above draws the\n // keyboard indicator through `focus-within:` — one ring for\n // whichever input mode is mounted.\n <textarea\n ref={textareaRef}\n value={text}\n onChange={(e) => setText(e.target.value)}\n onKeyDown={handleKeyDown}\n onPaste={onAttach ? handlePaste : undefined}\n placeholder={placeholder}\n disabled={disabled}\n autoFocus={autoFocus}\n // `minRows` lines before it grows. `rows` is what actually holds the\n // floor: the autosize measures `scrollHeight` against `height: auto`,\n // which a textarea resolves through `rows`, so the measurement cannot\n // come back shorter. The paired `minHeight` is those same lines in CSS\n // (`box-sizing: border-box` puts the padding inside it), computed from\n // the same row count so the two cannot disagree. It sits exactly AT\n // the natural height on purpose: a floor is meant to be inert until\n // something tries to go under it, which here means an inline height\n // arriving from anywhere but the autosize. Setting it higher would buy\n // no protection and cost permanent dead space under the caret.\n rows={minRows}\n style={{ minHeight: inputMinHeight, maxHeight }}\n aria-label=\"Message input\"\n className=\"w-full resize-none bg-transparent px-1.5 py-1 text-base leading-6 text-foreground outline-none placeholder:text-muted-foreground disabled:opacity-50\"\n />\n )}\n\n <div className=\"flex items-end gap-2\">\n {onAttach && (\n <>\n <button\n type=\"button\"\n onClick={() => fileInputRef.current?.click()}\n disabled={disabled}\n aria-label=\"Attach files\"\n title=\"Attach files\"\n className=\"shrink-0 rounded-lg p-2 text-muted-foreground transition hover:bg-accent hover:text-foreground disabled:opacity-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <PaperclipGlyph className=\"h-4 w-4\" />\n </button>\n <input ref={fileInputRef} type=\"file\" multiple className=\"hidden\" accept={accept} onChange={handleFileChange} />\n </>\n )}\n {onAttachFolder && (\n <>\n <button\n type=\"button\"\n onClick={() => folderInputRef.current?.click()}\n disabled={disabled}\n aria-label=\"Attach folder\"\n title=\"Attach folder\"\n className=\"shrink-0 rounded-lg p-2 text-muted-foreground transition hover:bg-accent hover:text-foreground disabled:opacity-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <FolderGlyph className=\"h-4 w-4\" />\n </button>\n {/* webkitdirectory is non-standard but widely supported for folder picks. */}\n <input\n ref={folderInputRef}\n type=\"file\"\n multiple\n className=\"hidden\"\n onChange={handleFolderChange}\n {...({ webkitdirectory: '' } as Record<string, string>)}\n />\n </>\n )}\n\n {/* The controls take the row's slack and wrap onto a second line when\n a long picker set outgrows it. This slot must never establish an\n overflow box: a control owns its popover (ModelPicker, EffortPicker)\n and anchors it absolutely to itself, so a scroll/clip box here traps\n a 400px-tall list inside a ~34px row — the list renders and is never\n visible — and the scroll offset that comes with it cuts the trigger's\n own left edge. Growing a second line is the cost of controls that\n stay operable. Rendered even when empty so Send stays right-aligned. */}\n <div\n data-testid=\"composer-controls\"\n className=\"flex min-w-0 flex-1 flex-wrap items-center gap-1.5\"\n >\n {showInline && controls}\n </div>\n\n {/* Trailing content is the controls slot's SIBLING, not its content:\n the slot is where a picker set is allowed to wrap and shrink, and\n a meter or a status line put inside it would be pushed onto the\n second line by the very pickers it reports on. */}\n {trailing && (\n <div data-testid=\"composer-trailing\" className=\"flex shrink-0 items-center gap-1.5\">\n {trailing}\n </div>\n )}\n {/* Dictation sits beside Send: it produces input, like typing. The\n button renders only when the host takes audio AND the browser can\n record — a dead mic is worse than no mic. While recording, the\n elapsed seconds (not the pulsing dot, which reduced motion\n collapses) are the signal, and the stop control is never\n disabled: a `disabled` flip mid-capture must not strand the mic. */}\n {onDictate && dictation.supported ? (\n dictation.recording ? (\n <div className=\"flex shrink-0 items-center gap-1.5\">\n <span aria-hidden=\"true\" className=\"h-2 w-2 animate-pulse rounded-full bg-destructive\" />\n <span\n aria-hidden=\"true\"\n data-testid=\"composer-dictate-elapsed\"\n className=\"text-xs tabular-nums text-muted-foreground\"\n >\n {formatDictationElapsed(dictation.elapsedSeconds)}\n </span>\n <span role=\"status\" className=\"sr-only\">\n Recording\n </span>\n <button\n type=\"button\"\n onClick={dictation.stop}\n aria-label=\"Stop dictation\"\n title=\"Stop dictation\"\n className=\"shrink-0 rounded-lg p-2 text-destructive transition hover:bg-accent focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <StopGlyph className=\"h-4 w-4\" />\n </button>\n </div>\n ) : (\n <button\n type=\"button\"\n onClick={dictation.start}\n disabled={disabled}\n aria-label=\"Dictate message\"\n title=\"Dictate message\"\n className=\"shrink-0 rounded-lg p-2 text-muted-foreground transition hover:bg-accent hover:text-foreground disabled:opacity-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <MicGlyph className=\"h-4 w-4\" />\n </button>\n )\n ) : null}\n\n {isStreaming ? (\n sendVariant === 'icon' ? (\n <button\n type=\"button\"\n onClick={onCancel}\n aria-label=\"Stop response\"\n title=\"Stop\"\n className=\"inline-flex h-[34px] w-[34px] shrink-0 items-center justify-center rounded-full border border-border bg-transparent text-foreground transition hover:bg-accent focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <StopGlyph className=\"h-3 w-3\" />\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={onCancel}\n aria-label=\"Stop response\"\n className=\"inline-flex shrink-0 items-center gap-1.5 rounded-full bg-destructive/15 px-3.5 py-2 text-sm font-medium text-destructive transition hover:bg-destructive/25 focus:outline-none focus-visible:ring-2 focus-visible:ring-destructive/50\"\n >\n <StopGlyph className=\"h-3.5 w-3.5\" />\n <span>Stop</span>\n </button>\n )\n ) : sendVariant === 'icon' ? (\n <button\n type=\"button\"\n onClick={send}\n disabled={!canSend}\n aria-label={sendLabel}\n title={sendLabel}\n className=\"inline-flex h-[34px] w-[34px] shrink-0 items-center justify-center rounded-full bg-foreground text-background transition hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-40 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-card\"\n >\n <ArrowUpGlyph className=\"h-4 w-4\" />\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={send}\n disabled={!canSend}\n aria-label={sendLabel}\n className=\"inline-flex shrink-0 items-center gap-1.5 rounded-full bg-primary px-3.5 py-2 text-sm font-medium text-primary-foreground shadow-sm transition hover:bg-primary/90 disabled:cursor-not-allowed disabled:opacity-40 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-card\"\n >\n <SendGlyph className=\"h-3.5 w-3.5\" />\n <span>{sendLabel}</span>\n </button>\n )}\n </div>\n </div>\n\n {/* The slash menu ports through PopoverSurface like every canonical\n popover: the composer docks inside horizontally scrolling rails, and\n an in-place panel there is a panel the host clips away. It anchors\n to the textarea and opens above. Focus never leaves the input —\n rows are mousedown-swallowed so a click can't blur it. */}\n <PopoverSurface\n open={slashOpen}\n id={slashListId}\n role=\"listbox\"\n triggerRef={textareaRef}\n panelRef={slashPanelRef}\n className={`w-80 overflow-y-auto rounded-xl border border-card-edge bg-popover p-1 ${OVERLAY_SHADOW}`}\n >\n {slashFiltered.length === 0 && (\n <div className=\"px-3 py-4 text-center text-sm text-muted-foreground\">No matching commands</div>\n )}\n {slashFiltered.map((item, index) => (\n <button\n key={item.id}\n type=\"button\"\n role=\"option\"\n aria-selected={index === slashActiveIndex}\n id={`${slashListId}-${index}`}\n onMouseDown={(e) => e.preventDefault()}\n onMouseMove={() => setSlashActive(index)}\n onClick={() => pickSlash(item.id)}\n className={`flex w-full items-center gap-2.5 rounded-md px-3 py-2.5 text-left text-sm transition ${POPOVER_OPTION_FOCUS} ${\n index === slashActiveIndex ? 'bg-accent' : 'hover:bg-accent'\n }`}\n >\n <span className=\"shrink-0 font-medium text-foreground\">{item.label}</span>\n <span className=\"truncate text-xs text-muted-foreground\">{item.description}</span>\n </button>\n ))}\n </PopoverSurface>\n\n {focusShortcut && (\n <div className=\"mt-1.5 flex justify-end px-1\">\n <span className=\"text-xs text-muted-foreground\">\n <kbd className=\"rounded border border-border bg-background px-1 py-0.5 text-xs\">{IS_APPLE_PLATFORM ? 'Cmd' : 'Ctrl'}</kbd>\n <kbd className=\"ml-0.5 rounded border border-border bg-background px-1 py-0.5 text-xs\">L</kbd>\n <span className=\"ml-1\">to focus</span>\n </span>\n </div>\n )}\n </div>\n )\n}\n","/**\n * `useComposerAttachments` — the composer's staged-upload lifecycle: validate\n * selected/dropped/pasted files against the shared limits (the SAME\n * `sniffBinary`/`checkAttachmentType`/size-cap vocabulary the store-backed\n * upload route enforces server-side, `../chat-routes/attachment-validation`\n * + `../chat-routes/binary-sniff`), upload each accepted file with one POST\n * request per file (so a single failure never poisons the batch), and track\n * every file's status so a host composer can render chips and gate sending.\n *\n * Ported from gtm-agent's `src/components/composer-attachments.tsx`\n * (gtm#584/#592/#593 hardened the sniff gate and batch semantics this leans\n * on), de-gtm-ified:\n * - the hardcoded `/api/vault/upload?workspaceId=` URL becomes\n * `uploadUrl`/`buildUploadRequest` (the latter wins — it hands back both\n * the URL and a `RequestInit` override, e.g. an auth header);\n * - `sonner` toasts become `onReject` (client pre-validation, never hits the\n * network) and `onError` (a request that reached the server and failed);\n * - the `accept`-string gate comes from `./composer-file-accept`, the one\n * matcher `ChatComposer` also funnels its picker/drop/paste ingress\n * through, so both ends of the staging path admit the same files;\n * - the response is expected to be `{ files: ChatAttachmentInput[] }` (full\n * server-authoritative descriptors — size/mediaType/kind — not gtm's\n * `{path, name}`), so `references` is a verbatim pass-through with no\n * client recompute;\n * - `workspaceId`'s truthiness gate becomes `enabled` (default `true`).\n *\n * Import-free beyond React + the browser-safe `/chat-routes` validation core:\n * this module ships through `/web-react` into client bundles\n * (`tests/browser-safe-subpaths.test.ts` walks the graph), so nothing here\n * may reach a Node builtin, `sandbox-ui`, or an engine package.\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport type { ChatAttachmentInput, ChatAttachmentKind } from './chat-stream'\nimport type { ComposerFile } from './chat-composer'\nimport { acceptRejectionReason, isAcceptedFileType } from './composer-file-accept'\nimport {\n ATTACHMENT_ACCEPT,\n ATTACHMENT_MAX_COUNT,\n MAX_ATTACHMENT_TOTAL_BYTES,\n MAX_BINARY_ATTACHMENT_BYTES,\n MAX_TEXT_ATTACHMENT_BYTES,\n attachmentSizeErrorMessage,\n attachmentTotalSizeErrorMessage,\n checkAttachmentType,\n sanitizeAttachmentFileName,\n} from '../chat-routes/attachment-validation'\nimport { sniffBinary } from '../chat-routes/binary-sniff'\n\nexport { ATTACHMENT_ACCEPT } from '../chat-routes/attachment-validation'\n\n/** One staged file and its upload lifecycle. `file` is retained so a failed\n * upload can be retried without re-selecting; `previewUrl` is an object URL\n * for image thumbnails and must be revoked when the entry leaves the queue.\n * `reference` is the server's authoritative descriptor once the upload\n * lands — stored verbatim, never recomputed client-side. */\ninterface StagedAttachment {\n id: string\n file: File\n name: string\n size: number\n status: 'pending' | 'uploading' | 'ready' | 'error'\n reference?: ChatAttachmentInput\n previewUrl?: string\n errorMessage?: string\n}\n\n/** Define options for configuring file upload behavior and handling in a composer component */\nexport interface UseComposerAttachmentsOptions {\n /** Simple upload target: every file POSTs here. Ignored when\n * `buildUploadRequest` is provided. */\n uploadUrl?: string\n /** Full request-building seam (auth headers, per-file routing, …) — wins\n * over `uploadUrl` when both are set. */\n buildUploadRequest?: (args: { file: File; name: string; form: FormData }) => {\n url: string\n init?: Omit<RequestInit, 'body' | 'signal'>\n }\n /** Client pre-validation rejections — a file that never reaches the\n * network (bad type, over a size cap, over count, disallowed kind). */\n onReject?: (reason: string, file?: File) => void\n /** A file that reached the upload endpoint and failed (HTTP error,\n * transport error, malformed response). */\n onError?: (reason: string) => void\n limits?: {\n maxCount?: number\n maxBinaryBytes?: number\n maxTextBytes?: number\n maxTotalBytes?: number\n }\n /** Attachment kinds accepted, checked against the sniffed content's\n * mime. Default: both (`['image', 'file']` — i.e. no restriction). */\n allowedKinds?: ChatAttachmentKind[]\n /** `<input accept>`-style gate for the file picker/drop/paste path.\n * Default {@link ATTACHMENT_ACCEPT}. */\n accept?: string\n /** When `false`, `addFiles` rejects every call via `onReject` (and\n * `blockReason` explains why) instead of staging anything — the\n * replacement for gtm's `workspaceId`-truthiness gate (e.g. no workspace\n * loaded yet). Default `true`. */\n enabled?: boolean\n}\n\n/** Provide staged file chips, ready attachments, and methods to add, retry, or drop composer files */\nexport interface UseComposerAttachmentsResult {\n /** Chip models for `ChatComposer`'s `pendingFiles` prop, one per staged\n * file — `kind` is always `'file'` (agent-app's `ComposerFile.kind`\n * discriminates file-vs-folder chips, not attachment media type). */\n composerFiles: ComposerFile[]\n /** Ready-to-send attachment descriptors — only files whose upload\n * succeeded, straight from the server's response (no recompute). Feed\n * this into `ChatTurnRequestPayload.attachments`. */\n references: ChatAttachmentInput[]\n /** Validate + stage + upload the given files, one request per file. */\n addFiles: (files: File[] | FileList) => Promise<void>\n /** Re-upload a failed entry using its retained `File`. */\n retry: (id: string) => void\n /** Drop one staged entry, aborting its upload and revoking its preview. */\n removeAttachment: (id: string) => void\n /** Forget every staged entry (call after a successful send). */\n clear: () => void\n /** True while any file is still pending or uploading. */\n hasPending: boolean\n /** True while any file failed to upload. */\n hasError: boolean\n /** Why a send is blocked, or `null` when the queue is clean. */\n blockReason: string | null\n}\n\nfunction newId(): string {\n const cryptoObject = globalThis.crypto\n if (typeof cryptoObject?.randomUUID === 'function') return cryptoObject.randomUUID()\n return `att-${Date.now()}-${Math.random().toString(36).slice(2)}`\n}\n\n/** Suffix a name (`report.pdf` → `report-2.pdf`) until it's unused. The\n * server writes to a name-derived store path, so identical names would\n * overwrite. The suffix stays inside the store-path charset (see\n * `sanitizeAttachmentFileName`). Ported byte-for-byte from gtm's\n * `dedupeName`. */\nfunction dedupeName(name: string, taken: Set<string>): string {\n if (!taken.has(name)) return name\n const dot = name.lastIndexOf('.')\n const base = dot > 0 ? name.slice(0, dot) : name\n const ext = dot > 0 ? name.slice(dot) : ''\n let n = 2\n let candidate = `${base}-${n}${ext}`\n while (taken.has(candidate)) {\n n += 1\n candidate = `${base}-${n}${ext}`\n }\n return candidate\n}\n\n/** `image/*` → `'image'`, everything else → `'file'`. Deliberately\n * reimplemented here (not imported from `../chat-store/parts`, which pulls\n * the drizzle-adjacent `/chat-store` barrel): this module must stay reachable\n * from a browser bundle with only the `/chat-routes` validation core as a\n * dependency. */\nfunction kindForMime(mime: string): ChatAttachmentKind {\n return mime.startsWith('image/') ? 'image' : 'file'\n}\n\n/** Pull a human-readable message out of the upload endpoint's error body.\n * Ported from gtm's `parseUploadError`: handles both `{ error: string }`\n * (size/count/access errors) and `{ error: { message } }` (the\n * `createAttachmentUploadRoute` envelope, `{error:{code,message,path?}}`). */\nasync function parseUploadError(res: Response): Promise<string> {\n const detail = await res.json().catch(() => null)\n if (detail && typeof detail === 'object' && 'error' in detail) {\n const error = (detail as { error: unknown }).error\n if (typeof error === 'string' && error) return error\n if (error && typeof error === 'object' && 'message' in error) {\n const message = (error as { message: unknown }).message\n if (typeof message === 'string' && message) return message\n }\n }\n return `Upload failed (${res.status})`\n}\n\n/** Shown when neither `uploadUrl` nor `buildUploadRequest` is configured\n * while `enabled` — a product wiring bug, not a user-facing rejection, so it\n * lands each affected entry in `error` (with `onError`) rather than blocking\n * `addFiles` outright via `onReject`: the files still stage and can be\n * retried once the product fixes its config, instead of silently vanishing. */\nconst NO_UPLOAD_TARGET_MESSAGE = 'No upload destination configured (pass uploadUrl or buildUploadRequest)'\n\n/**\n * Owns the composer's attachment lifecycle: validate selected/dropped/pasted\n * files against the shared limits, upload each accepted file to the\n * product's store (one request per file), and track every file's status so\n * the composer can render chips and gate sending.\n *\n * Failures surface loud — a rejected file calls `onReject` and is never\n * uploaded; a failed upload calls `onError` and leaves an error chip the user\n * can retry or remove. `references` only ever contains files whose upload the\n * server actually confirmed.\n */\nexport function useComposerAttachments(\n options: UseComposerAttachmentsOptions,\n): UseComposerAttachmentsResult {\n // Latest options, read from inside stable callbacks — avoids re-creating\n // `addFiles`/`upload` (and therefore breaking referential stability for\n // effects a host might hang off them) every time a caller passes a fresh\n // options object literal.\n const optionsRef = useRef(options)\n optionsRef.current = options\n\n const [staged, setStagedState] = useState<StagedAttachment[]>([])\n // Mirror of `staged` kept in lockstep so dedupe/aggregate-cap/abort read\n // current values synchronously (setState callbacks alone can't answer\n // \"what's staged right now\" mid-validation).\n const stagedRef = useRef<StagedAttachment[]>([])\n const controllersRef = useRef<Map<string, AbortController>>(new Map())\n\n // Post-unmount calls reduce to a React no-op setState; the refs they touch\n // die with the instance.\n const setStaged = useCallback(\n (updater: StagedAttachment[] | ((prev: StagedAttachment[]) => StagedAttachment[])) => {\n const next =\n typeof updater === 'function'\n ? (updater as (prev: StagedAttachment[]) => StagedAttachment[])(stagedRef.current)\n : updater\n stagedRef.current = next\n setStagedState(next)\n },\n [],\n )\n\n const upload = useCallback(\n async (id: string, file: File, name: string) => {\n const opts = optionsRef.current\n setStaged((prev) =>\n prev.map((s) => (s.id === id ? { ...s, status: 'uploading', errorMessage: undefined } : s)),\n )\n const controller = new AbortController()\n controllersRef.current.set(id, controller)\n const form = new FormData()\n form.append('file', file, name)\n\n const request = opts.buildUploadRequest\n ? opts.buildUploadRequest({ file, name, form })\n : opts.uploadUrl\n ? { url: opts.uploadUrl }\n : null\n\n if (!request) {\n setStaged((prev) =>\n prev.map((s) =>\n s.id === id ? { ...s, status: 'error', errorMessage: NO_UPLOAD_TARGET_MESSAGE } : s,\n ),\n )\n opts.onError?.(NO_UPLOAD_TARGET_MESSAGE)\n controllersRef.current.delete(id)\n return\n }\n\n try {\n const res = await fetch(request.url, {\n method: 'POST',\n credentials: 'same-origin',\n ...request.init,\n body: form,\n signal: controller.signal,\n })\n if (!res.ok) {\n const message = await parseUploadError(res)\n setStaged((prev) =>\n prev.map((s) => (s.id === id ? { ...s, status: 'error', errorMessage: message } : s)),\n )\n opts.onError?.(message)\n return\n }\n const data = (await res.json()) as { files?: ChatAttachmentInput[] }\n const uploaded = data.files?.[0]\n if (!uploaded) {\n const message = 'Upload returned no file'\n setStaged((prev) =>\n prev.map((s) => (s.id === id ? { ...s, status: 'error', errorMessage: message } : s)),\n )\n opts.onError?.(message)\n return\n }\n setStaged((prev) =>\n prev.map((s) => (s.id === id ? { ...s, status: 'ready', reference: uploaded } : s)),\n )\n } catch (err) {\n if ((err as Error).name === 'AbortError') return // silent removal — see removeAttachment/clear\n const message =\n err instanceof Error && err.message ? err.message : 'Upload failed — check your connection'\n setStaged((prev) =>\n prev.map((s) => (s.id === id ? { ...s, status: 'error', errorMessage: message } : s)),\n )\n opts.onError?.(message)\n } finally {\n controllersRef.current.delete(id)\n }\n },\n [setStaged],\n )\n\n const addFiles = useCallback(\n async (files: File[] | FileList) => {\n const opts = optionsRef.current\n const enabled = opts.enabled ?? true\n if (!enabled) {\n opts.onReject?.('Attachments are disabled')\n return\n }\n\n const accept = opts.accept ?? ATTACHMENT_ACCEPT\n const maxCount = opts.limits?.maxCount ?? ATTACHMENT_MAX_COUNT\n const maxBinaryBytes = opts.limits?.maxBinaryBytes ?? MAX_BINARY_ATTACHMENT_BYTES\n const maxTextBytes = opts.limits?.maxTextBytes ?? MAX_TEXT_ATTACHMENT_BYTES\n const maxTotalBytes = opts.limits?.maxTotalBytes ?? MAX_ATTACHMENT_TOTAL_BYTES\n const allowedKinds = opts.allowedKinds ?? (['image', 'file'] as ChatAttachmentKind[])\n\n const list = Array.isArray(files) ? files : Array.from(files)\n\n // Pass 1: accept-list, then count cap — accept first, and the count\n // checked against what is already staged plus what this batch has taken.\n const currentCount = stagedRef.current.length\n const countAccepted: File[] = []\n for (const file of list) {\n if (!isAcceptedFileType(file, accept)) {\n opts.onReject?.(acceptRejectionReason(file, accept), file)\n continue\n }\n if (currentCount + countAccepted.length >= maxCount) {\n opts.onReject?.(`\"${file.name}\" was not added — the ${maxCount}-file limit is already reached.`, file)\n continue\n }\n countAccepted.push(file)\n }\n\n // Pass 2: real content sniff + type gate + per-kind size cap +\n // allowed-kinds gate — the SAME checks the server enforces, so a\n // rejection never differs depending on which side classified the bytes\n // first. Nothing here ever reaches the network.\n const sizeAccepted: File[] = []\n for (const file of countAccepted) {\n const bytes = new Uint8Array(await file.arrayBuffer())\n const sniff = sniffBinary(bytes)\n const typeCheck = checkAttachmentType(file.name, sniff)\n if (!typeCheck.succeeded) {\n opts.onReject?.(typeCheck.message, file)\n continue\n }\n const limit = sniff.binary ? maxBinaryBytes : maxTextBytes\n if (file.size > limit) {\n opts.onReject?.(attachmentSizeErrorMessage(file.name, file.size, limit), file)\n continue\n }\n const mediaType = sniff.mime ?? file.type ?? ''\n const kind = kindForMime(mediaType)\n if (!allowedKinds.includes(kind)) {\n opts.onReject?.(`\"${file.name}\" is a ${kind} attachment, which isn't accepted here`, file)\n continue\n }\n sizeAccepted.push(file)\n }\n\n // Pass 3: running aggregate cap across this batch + everything already\n // staged (any status) — a partial batch can still land.\n const accepted: File[] = []\n let totalBytes = stagedRef.current.reduce((total, s) => total + s.size, 0)\n for (const file of sizeAccepted) {\n const nextTotalBytes = totalBytes + file.size\n if (nextTotalBytes > maxTotalBytes) {\n opts.onReject?.(attachmentTotalSizeErrorMessage(nextTotalBytes, maxTotalBytes), file)\n continue\n }\n accepted.push(file)\n totalBytes = nextTotalBytes\n }\n if (accepted.length === 0) return\n\n // Stage under the name the server will actually store, so the chip and\n // the message's attachment references never diverge.\n const taken = new Set(stagedRef.current.map((s) => s.name))\n const entries: StagedAttachment[] = accepted.map((file) => {\n const name = dedupeName(sanitizeAttachmentFileName(file.name), taken)\n taken.add(name)\n return {\n id: newId(),\n file,\n name,\n size: file.size,\n status: 'pending',\n previewUrl: file.type.startsWith('image/') ? URL.createObjectURL(file) : undefined,\n }\n })\n setStaged((prev) => [...prev, ...entries])\n for (const entry of entries) void upload(entry.id, entry.file, entry.name)\n },\n [setStaged, upload],\n )\n\n const retry = useCallback(\n (id: string) => {\n const entry = stagedRef.current.find((s) => s.id === id)\n if (!entry) return\n void upload(entry.id, entry.file, entry.name)\n },\n [upload],\n )\n\n const removeAttachment = useCallback(\n (id: string) => {\n controllersRef.current.get(id)?.abort()\n controllersRef.current.delete(id)\n const entry = stagedRef.current.find((s) => s.id === id)\n if (entry?.previewUrl) URL.revokeObjectURL(entry.previewUrl)\n setStaged((prev) => prev.filter((s) => s.id !== id))\n },\n [setStaged],\n )\n\n const clear = useCallback(() => {\n for (const controller of controllersRef.current.values()) controller.abort()\n controllersRef.current.clear()\n for (const entry of stagedRef.current) {\n if (entry.previewUrl) URL.revokeObjectURL(entry.previewUrl)\n }\n setStaged([])\n }, [setStaged])\n\n useEffect(\n () => () => {\n for (const controller of controllersRef.current.values()) controller.abort()\n controllersRef.current.clear()\n for (const entry of stagedRef.current) {\n if (entry.previewUrl) URL.revokeObjectURL(entry.previewUrl)\n }\n },\n [],\n )\n\n // `previewUrl` and `errorMessage` travel with the chip model. This hook is\n // the only place that knows either one — it mints the object URL and holds\n // the upload's failure text — so a projection that dropped them left the\n // composer rendering a red chip with no reason on it and no thumbnail for a\n // staged image.\n const composerFiles = useMemo<ComposerFile[]>(\n () =>\n staged.map((s) => ({\n id: s.id,\n name: s.name,\n size: s.size,\n kind: 'file' as const,\n status: s.status,\n previewUrl: s.previewUrl,\n errorMessage: s.errorMessage,\n })),\n [staged],\n )\n\n const references = useMemo<ChatAttachmentInput[]>(\n () =>\n staged\n .filter((s): s is StagedAttachment & { reference: ChatAttachmentInput } => s.status === 'ready' && !!s.reference)\n .map((s) => s.reference),\n [staged],\n )\n\n const hasPending = useMemo(\n () => staged.some((s) => s.status === 'pending' || s.status === 'uploading'),\n [staged],\n )\n const hasError = useMemo(() => staged.some((s) => s.status === 'error'), [staged])\n const enabled = options.enabled ?? true\n const blockReason = !enabled\n ? 'Attachments are disabled'\n : hasPending\n ? 'Attachments are still uploading'\n : hasError\n ? 'Remove failed attachments to send'\n : null\n\n return {\n composerFiles,\n references,\n addFiles,\n retry,\n removeAttachment,\n clear,\n hasPending,\n hasError,\n blockReason,\n }\n}\n","/**\n * Per-harness brand marks for the canonical pickers — the same marks the\n * legacy sandbox-ui harness picker (`dashboard/harness-logo.tsx`) shipped,\n * vendored as inline SVG so `/web-react` stays dependency-free beyond React:\n * sandbox-ui (and its `@lobehub/icons-static-svg` bundle) is an OPTIONAL peer\n * the canonical pickers must not force on a consumer. Geometry is the lobehub\n * single-color artwork, rendered in `currentColor` exactly as the legacy\n * component painted it (a foreground-filled CSS mask), so every mark tracks\n * the theme. Harnesses with no published brand mark get an honest inline\n * lucide glyph — bot / plug / terminal, the same fallbacks the legacy picker\n * used — and an unknown id falls back to the neutral bot. Data-record\n * structure mirrors `./provider-logo`.\n */\n\nimport type { ReactNode } from 'react'\nimport type { Harness } from '../harness'\n\nexport interface HarnessGlyphProps {\n /** Harness to mark. Typed as the canonical union; an out-of-union runtime\n * value still renders — it gets the neutral fallback glyph. */\n harness: Harness\n className?: string\n}\n\n// ── brand marks (single-color lobehub artwork, fill) ──────────────────────\n\nconst BRAND_PATHS: Partial<Record<Harness, readonly string[]>> = {\n opencode: [\n 'M16 6H8v12h8V6zm4 16H4V2h16v20z',\n ],\n 'claude-code': [\n 'M20.998 10.949H24v3.102h-3v3.028h-1.487V20H18v-2.921h-1.487V20H15v-2.921H9V20H7.488v-2.921H6V20H4.487v-2.921H3V14.05H0V10.95h3V5h17.998v5.949zM6 10.949h1.488V8.102H6v2.847zm10.51 0H18V8.102h-1.49v2.847z',\n ],\n codex: [\n 'M8.086.457a6.105 6.105 0 013.046-.415c1.333.153 2.521.72 3.564 1.7a.117.117 0 00.107.029c1.408-.346 2.762-.224 4.061.366l.063.03.154.076c1.357.703 2.33 1.77 2.918 3.198.278.679.418 1.388.421 2.126a5.655 5.655 0 01-.18 1.631.167.167 0 00.04.155 5.982 5.982 0 011.578 2.891c.385 1.901-.01 3.615-1.183 5.14l-.182.22a6.063 6.063 0 01-2.934 1.851.162.162 0 00-.108.102c-.255.736-.511 1.364-.987 1.992-1.199 1.582-2.962 2.462-4.948 2.451-1.583-.008-2.986-.587-4.21-1.736a.145.145 0 00-.14-.032c-.518.167-1.04.191-1.604.185a5.924 5.924 0 01-2.595-.622 6.058 6.058 0 01-2.146-1.781c-.203-.269-.404-.522-.551-.821a7.74 7.74 0 01-.495-1.283 6.11 6.11 0 01-.017-3.064.166.166 0 00.008-.074.115.115 0 00-.037-.064 5.958 5.958 0 01-1.38-2.202 5.196 5.196 0 01-.333-1.589 6.915 6.915 0 01.188-2.132c.45-1.484 1.309-2.648 2.577-3.493.282-.188.55-.334.802-.438.286-.12.573-.22.861-.304a.129.129 0 00.087-.087A6.016 6.016 0 015.635 2.31C6.315 1.464 7.132.846 8.086.457zm-.804 7.85a.848.848 0 00-1.473.842l1.694 2.965-1.688 2.848a.849.849 0 001.46.864l1.94-3.272a.849.849 0 00.007-.854l-1.94-3.393zm5.446 6.24a.849.849 0 000 1.695h4.848a.849.849 0 000-1.696h-4.848z',\n ],\n amp: [\n 'M15.087 23.18L12.03 24l-2.097-7.823-5.738 5.738-2.251-2.251 5.718-5.719-7.769-2.082.82-3.057 11.294 3.08 3.08 11.295z',\n 'M19.505 18.762l-3.057.82-2.564-9.573-9.572-2.564.819-3.057 11.295 3.079 3.08 11.295z',\n 'M23.893 14.374l-3.057.82-2.565-9.572L8.7 3.057 9.52 0l11.295 3.08 3.079 11.294z',\n ],\n 'kimi-code': [\n 'M1.052 16.916l9.539 2.552a21.007 21.007 0 00.06 2.033l5.956 1.593a11.997 11.997 0 01-5.586.865l-.18-.016-.044-.004-.084-.009-.094-.01a11.605 11.605 0 01-.157-.02l-.107-.014-.11-.016a11.962 11.962 0 01-.32-.051l-.042-.008-.075-.013-.107-.02-.07-.015-.093-.019-.075-.016-.095-.02-.097-.023-.094-.022-.068-.017-.088-.022-.09-.024-.095-.025-.082-.023-.109-.03-.062-.02-.084-.025-.093-.028-.105-.034-.058-.019-.08-.026-.09-.031-.066-.024a6.293 6.293 0 01-.044-.015l-.068-.025-.101-.037-.057-.022-.08-.03-.087-.035-.088-.035-.079-.032-.095-.04-.063-.028-.063-.027a5.655 5.655 0 01-.041-.018l-.066-.03-.103-.047-.052-.024-.096-.046-.062-.03-.084-.04-.086-.044-.093-.047-.052-.027-.103-.055-.057-.03-.058-.032a6.49 6.49 0 01-.046-.026l-.094-.053-.06-.034-.051-.03-.072-.041-.082-.05-.093-.056-.052-.032-.084-.053-.061-.039-.079-.05-.07-.047-.053-.035a7.785 7.785 0 01-.054-.036l-.044-.03-.044-.03a6.066 6.066 0 01-.04-.028l-.057-.04-.076-.054-.069-.05-.074-.054-.056-.042-.076-.057-.076-.059-.086-.067-.045-.035-.064-.052-.074-.06-.089-.073-.046-.039-.046-.039a7.516 7.516 0 01-.043-.037l-.045-.04-.061-.053-.07-.062-.068-.06-.062-.058-.067-.062-.053-.05-.088-.084a13.28 13.28 0 01-.099-.097l-.029-.028-.041-.042-.069-.07-.05-.051-.05-.053a6.457 6.457 0 01-.168-.179l-.08-.088-.062-.07-.071-.08-.042-.049-.053-.062-.058-.068-.046-.056a7.175 7.175 0 01-.027-.033l-.045-.055-.066-.082-.041-.052-.05-.064-.02-.025a11.99 11.99 0 01-1.44-2.402zm-1.02-5.794l11.353 3.037a20.468 20.468 0 00-.469 2.011l10.817 2.894a12.076 12.076 0 01-1.845 2.005L.657 15.923l-.016-.046-.035-.104a11.965 11.965 0 01-.05-.153l-.007-.023a11.896 11.896 0 01-.207-.741l-.03-.126-.018-.08-.021-.097-.018-.081-.018-.09-.017-.084-.018-.094c-.026-.141-.05-.283-.071-.426l-.017-.118-.011-.083-.013-.102a12.01 12.01 0 01-.019-.161l-.005-.047a12.12 12.12 0 01-.034-2.145zm1.593-5.15l11.948 3.196c-.368.605-.705 1.231-1.01 1.875l11.295 3.022c-.142.82-.368 1.612-.668 2.365l-11.55-3.09L.124 10.26l.015-.1.008-.049.01-.067.015-.087.018-.098c.026-.148.056-.295.088-.442l.028-.124.02-.085.024-.097c.022-.09.045-.18.07-.268l.028-.102.023-.083.03-.1.025-.082.03-.096.026-.082.031-.095a11.896 11.896 0 011.01-2.232zm4.442-4.4L17.352 4.59a20.77 20.77 0 00-1.688 1.721l7.823 2.093c.267.852.442 1.744.513 2.665L2.106 5.213l.045-.065.027-.04.04-.055.046-.065.055-.076.054-.072.064-.086.05-.065.057-.073.055-.07.06-.074.055-.069.065-.077.054-.066.066-.077.053-.06.072-.082.053-.06.067-.074.054-.058.073-.078.058-.06.063-.067.168-.17.1-.098.059-.056.076-.071a12.084 12.084 0 012.272-1.677zM12.017 0h.097l.082.001.069.001.054.002.068.002.046.001.076.003.047.002.06.003.054.002.087.005.105.007.144.011.088.007.044.004.077.008.082.008.047.005.102.012.05.006.108.014.081.01.042.006.065.01.207.032.07.012.065.011.14.026.092.018.11.022.046.01.075.016.041.01L14.7.3l.042.01.065.015.049.012.071.017.096.024.112.03.113.03.113.032.05.015.07.02.078.024.073.023.05.016.05.016.076.025.099.033.102.036.048.017.064.023.093.034.11.041.116.045.1.04.047.02.06.024.041.018.063.026.04.018.057.025.11.048.1.046.074.035.075.036.06.028.092.046.091.045.102.052.053.028.049.026.046.024.06.033.041.022.052.029.088.05.106.06.087.051.057.034.053.032.096.059.088.055.098.062.036.024.064.041.084.056.04.027.062.042.062.043.023.017c.054.037.108.075.161.114l.083.06.065.048.056.043.086.065.082.064.04.03.05.041.086.069.079.065.085.071c.712.6 1.353 1.283 1.909 2.031L7.222.994l.062-.027.065-.028.081-.034.086-.035c.113-.045.227-.09.341-.131l.096-.035.093-.033.084-.03.096-.031c.087-.03.176-.058.264-.085l.091-.027.086-.025.102-.03.085-.023.1-.026L9.04.37l.09-.023.091-.022.095-.022.09-.02.098-.021.091-.02.095-.018.092-.018.1-.018.091-.016.098-.017.092-.014.097-.015.092-.013.102-.013.091-.012.105-.012.09-.01.105-.01c.093-.01.186-.018.28-.024l.106-.008.09-.005.11-.006.093-.004.1-.004.097-.002.099-.002.197-.002z',\n ],\n openclaw: [\n 'M9.046 7.104a.527.527 0 110 1.055.527.527 0 010-1.055z',\n 'M15.376 7.104a.528.528 0 110 1.056.528.528 0 010-1.056z',\n 'M16.877 1.912c.58-.27 1.14-.323 1.616-.037a.317.317 0 01-.326.542c-.227-.136-.547-.153-1.022.068-.352.165-.765.45-1.234.866 2.683 1.17 4.4 3.5 5.148 5.921a6.421 6.421 0 00-.704.184c-.578.016-1.174.204-1.502.735-.338.55-.268 1.276.072 2.069l.005.012.007.014c.523 1.045 1.318 1.91 2.2 2.284-.912 3.274-3.44 6.144-5.972 6.988v2.109h-2.11v-2.11c-1.043.417-2.086.01-2.11 0v2.11h-2.11v-2.11c-2.531-.843-5.061-3.713-5.973-6.987.882-.373 1.678-1.238 2.2-2.284l.007-.014.006-.012c.34-.793.41-1.518.071-2.069-.327-.531-.923-.719-1.503-.735a6.409 6.409 0 00-.704-.183c.749-2.421 2.466-4.751 5.149-5.922-.47-.416-.88-.701-1.234-.866-.474-.221-.794-.204-1.021-.068a.318.318 0 01-.435-.109.317.317 0 01.109-.433c.476-.286 1.036-.233 1.615.037.49.229 1.031.628 1.621 1.182A9.924 9.924 0 0112 2.568c1.199 0 2.284.19 3.256.526.59-.554 1.13-.953 1.62-1.182zM8.835 6.577a1.266 1.266 0 100 2.532 1.266 1.266 0 000-2.532zm6.33 0a1.267 1.267 0 100 2.533 1.267 1.267 0 000-2.533z',\n 'M.395 13.118c-.966-1.932-.163-3.863 2.41-3.365v-.001l.05.01c.084.018.17.038.26.06.033.009.067.017.1.027.084.022.168.048.255.076l.09.027c.528 0 .95.158 1.16.501.212.343.212.87-.105 1.61-.085.17-.178.333-.276.489l-.01.017a4.967 4.967 0 01-.62.791l-.019.02c-1.092 1.117-2.496 1.336-3.295-.262z',\n 'M21.193 9.753c2.574-.5 3.378 1.433 2.411 3.365-.58 1.159-1.476 1.361-2.342.96l-.011-.005a2.419 2.419 0 01-.114-.056l-.019-.01a2.751 2.751 0 01-.115-.067l-.023-.014c-.035-.022-.071-.044-.106-.068l-.05-.035c-.55-.388-1.062-1.007-1.44-1.76-.276-.647-.311-1.132-.174-1.472.176-.439.636-.639 1.23-.639.032-.011.066-.02.099-.03.08-.026.16-.05.238-.072l.117-.03a5.502 5.502 0 01.3-.067z',\n ],\n hermes: [\n 'M5.938 12.835c.127-.039.285.02.373.143.028.038.036.092.046.14.003.014-.02.033-.04.05-.124-.098-.24-.194-.354-.291-.011-.01-.016-.027-.025-.042zM8.396 9.412c.195-.032.39-.06.588-.05a.54.54 0 01.148.026c.202.071.402.147.601.224.028.01.05.036.075.055l-.013.027a9.203 9.203 0 01-.26-.089c-.115-.038-.213-.077-.315-.098-.25-.05-.25-.046-.292-.014l.574.144c.275.139.55.276.823.417.042.022.09.057.107.098.026.06.063.076.117.072.066-.006.132-.017.213-.027l-.04.086c.051.08.142.02.216.064-.074.13-.247.09-.334.199l.061.074-.12.087c0 .106-.038.168-.306.243l.026.085-.196.042.07.124h-.25l-.007.137c-.081-.01-.161-.018-.244-.027l-.053.123c-.027-.008-.052-.011-.073-.023-.067-.038-.128-.056-.195.006-.019.017-.063.014-.093.008-.026-.006-.05-.029-.07-.042-.11.095-.11.095-.208.003-.057.046-.12.074-.186.011-.063.027-.123-.02-.178-.014-.07.007-.097-.035-.133-.07l-.13.033c-.013-.236-.194-.19-.34-.203.005-.072.05-.092.095-.094a.474.474 0 01.159.022c.164.05.32.12.496.138.203.021.405.029.601-.015.265-.059.52-.149.707-.365.049-.056.083-.127.117-.195.019-.038.02-.084-.02-.116a1.397 1.397 0 00-.382-.217c.024.12-.031.182-.115.221 0 .014-.004.025 0 .03.08.115.084.16-.007.267a1.39 1.39 0 01-.218.211.477.477 0 01-.641-.05 1.36 1.36 0 01-.133-.152c-.078-.107-.076-.108-.033-.236-.165-.08-.128-.226-.104-.364.008-.05.028-.096.049-.163-.04.014-.067.017-.087.032a.897.897 0 00-.316.357c-.007.016-.01.034-.02.047-.012.015-.034.038-.045.035-.02-.006-.037-.027-.05-.045-.008-.012-.007-.032-.012-.057h-.126l.053-.172a14.82 14.82 0 00-.039-.049l.11-.284c-.06.026-.091.044-.124.051-.03.007-.064 0-.095 0 0-.031-.01-.07.004-.092.149-.22.305-.428.593-.476z',\n 'M8.06 10.788c-.003-.038-.004-.075.037-.062.016.006.034.048.028.067-.01.04-.038.032-.064-.005z',\n 'M11.981.009c.226-.012.453-.011.679 0 .247.01.495.024.74.062.401.064.798.157 1.19.273.463.138.92.299 1.356.511a7.31 7.31 0 012.948 2.642c.292.469.536.963.739 1.479.219.556.446 1.11.623 1.683.204.654.329 1.326.458 1.997.097.504.182 1.01.29 1.511.156.722.329 1.44.494 2.16.186.812.4 1.615.63 2.415.102.355.193.713.282 1.072.11.436.202.876.254 1.323.031.278.066.557.073.837a7.56 7.56 0 01-.017.88c-.037.413-.1.818-.226 1.212a5.017 5.017 0 01-.915 1.649l-.13.156.018.023c.043-.023.088-.041.127-.068.2-.138.373-.307.531-.49.4-.46.721-.973.975-1.529a3.59 3.59 0 00.325-1.72c-.024-.424-.097-.834-.3-1.213-.013-.027-.015-.06-.03-.121.05.035.082.048.101.072.107.13.22.258.315.398.33.494.46 1.052.486 1.64a3.75 3.75 0 01-.47 1.97c-.36.655-.887 1.14-1.526 1.506-.193.111-.394.21-.595.308-.157.078-.248.211-.318.365a.522.522 0 00-.033.406.359.359 0 01.013.139c-.005.077-.077.155-.14.162-.054.006-.125-.043-.15-.116a1.206 1.206 0 01-.06-.233c-.04-.314-.155-.6-.308-.87a3.906 3.906 0 00-.73-.91 2.129 2.129 0 00-.897-.524 4.093 4.093 0 00-.692-.131c-.075-.008-.15-.04-.22.01.18.06.363.11.538.18.434.173.82.43 1.18.728.308.255.58.543.794.884.098.155.186.315.227.496.027.123.042.25.067.375.013.062-.002.109-.053.144-.047.033-.122.034-.163-.01a.455.455 0 01-.08-.14c-.03-.073-.038-.159-.078-.225a7.314 7.314 0 00-1.423-1.664c-.16-.137-.329-.26-.537-.323-.376-.114-.753-.203-1.15-.154-.213.025-.427.032-.64.053a1.6 1.6 0 00-.736.278 5.14 5.14 0 00-.834.72c-.329.342-.642.699-.955 1.055-.136.155-.264.319-.314.531a5.227 5.227 0 00-.012.051.096.096 0 01-.09.076h-.31c-.046 0-.082-.048-.072-.094.023-.108.045-.216.07-.324.075-.325.19-.635.368-.917.024-.039.04-.088.104-.08l.01.049.027.077c.28-.435.571-.834.996-1.135.283-.204.584-.378.89-.55a.196.196 0 00-.098-.002c-.162.043-.325.084-.485.134-.402.124-.764.33-1.11.566-.147.1-.298.193-.414.333a7.314 7.314 0 00-1.07 1.767.845.845 0 00-.04.12.075.075 0 01-.072.056h-.494c-.04 0-.062-.051-.036-.082.123-.14.246-.282.377-.415.275-.281.58-.532.777-.884.027-.048.063-.09.095-.135.238-.333.54-.607.818-.902.082-.086.175-.16.26-.24.029-.027.053-.057.079-.085l-.018-.025-.135.041c-.034.017-.07.031-.102.05-.248.144-.494.292-.743.433-.408.23-.825.439-1.209.711-.281.2-.591.358-.889.533-.02.012-.044.015-.08.028-.015-.135.143-.201.108-.336-.033.014-.064.02-.085.038-.111.096-.227.19-.328.296-.148.157-.284.325-.425.488-.125.143-.25.286-.373.431A.153.153 0 019.89 24H8.762a.316.316 0 00.016-.042c.028-.09.085-.172.083-.28-.091-.018-.162.001-.212.077a4.45 4.45 0 00-.136.215c-.01.016-.024.03-.042.03h-.093c-.019 0-.029-.022-.017-.037.071-.088.14-.178.209-.268.001-.002-.006-.012-.012-.024-.014.004-.03.006-.045.013-.176.09-.352.181-.527.274a.363.363 0 01-.168.042H5.202c-.026 0-.039-.036-.019-.053.21-.178.402-.374.558-.605.335-.496.538-1.047.667-1.629.004-.02-.003-.043-.006-.091-.037.048-.059.072-.076.1a1.943 1.943 0 01-.334.415c-.28.258-.59.448-.983.464-.297.012-.588 0-.865-.127-.46-.21-.722-.57-.794-1.072-.025-.17-.017-.171-.182-.219A3.513 3.513 0 011.97 20.6a2.286 2.286 0 01-.808-1.13 3.569 3.569 0 01-.16-1.245c.002-.034.016-.067.024-.1.032.023.046.043.05.066.033.153.059.308.096.46.086.355.257.664.516.92.258.256.571.419.91.532.358.118.717.138 1.07-.016a1.89 1.89 0 00.621-.452c.328-.348.533-.76.648-1.223.009-.034.005-.071.007-.11-.015.006-.026.006-.03.011-.031.05-.064.1-.093.152-.284.502-.679.887-1.196 1.135-.351.17-.718.255-1.11.159a1.607 1.607 0 01-.971-.64 2.006 2.006 0 01-.368-.924 2.903 2.903 0 01.02-.886c.05-.439.466-1.17.742-1.271-.02.063-.035.112-.053.16-.043.116-.097.227-.13.345a1.901 1.901 0 00-.05.82c.033.212.09.416.204.6.147.236.346.407.62.465.11.023.225.014.338.018a.576.576 0 00.386-.131c.164-.128.282-.292.366-.481.168-.375.24-.777.309-1.179.05-.296.093-.594.133-.893.039-.281.071-.563.104-.845.026-.232.048-.464.074-.696.024-.228.052-.455.076-.683.024-.227.047-.455.069-.683.013-.14.022-.28.034-.42l.037-.417c.022-.25.041-.5.065-.748.008-.082-.02-.132-.09-.177a2.46 2.46 0 01-.492-.418c-.1-.109-.188-.228-.282-.342-.035-.042-.056-.097-.116-.118a2.084 2.084 0 00.275.597c.06.092.131.176.196.265.063.086.182.115.234.226-.028.003-.046.01-.06.006a4.74 4.74 0 01-.22-.057 2.71 2.71 0 01-1.287-.819c-.435-.487-.656-1.076-.71-1.723a5.206 5.206 0 01.014-1.06c.072-.602.22-1.186.45-1.745.155-.376.338-.741.526-1.102.205-.393.466-.75.765-1.076.512-.559 1.104-1.024 1.726-1.448.717-.49 1.478-.898 2.277-1.233C8.244.828 8.767.632 9.31.494c.655-.166 1.31-.33 1.982-.415.229-.03.458-.058.688-.07zm-1.847 22.82c-.07.06-.147.111-.207.18-.238.27-.464.549-.668.869l-.044.108a.177.177 0 00.093-.057c.174-.19.351-.378.519-.574.104-.122.195-.255.288-.386.024-.034.03-.08.046-.12l-.027-.02zm1.65-3.695a5.51 5.51 0 00-.653.593l-.37.386a.963.963 0 01-.377.25 1.372 1.372 0 01-.467.09c-.044 0-.087.006-.151.012.028.058.043.097.064.131.15.242.301.482.45.724.136.22.276.438.399.666.068.125.105.267.156.404.077.027.14-.018.202-.048.29-.135.579-.274.867-.412.213-.101.437-.186.636-.31.347-.215.68-.455 1.018-.685.015-.01.026-.028.042-.046-.023-.019-.038-.037-.056-.044-.287-.111-.527-.3-.77-.482a5.319 5.319 0 01-.506-.42 1.757 1.757 0 01-.41-.653c-.019-.049-.045-.095-.075-.156zm-5.847.264c-.06.096-.097.194-.132.293a3.38 3.38 0 01-.555 1.01c-.2.25-.455.412-.762.493-.23.06-.464.076-.7.07-.048-.002-.097.002-.158.005.016.04.021.066.035.085.1.145.23.246.4.295.157.046.316.034.498.023.181-.037.343-.115.485-.234.238-.199.402-.454.536-.732.175-.363.264-.751.342-1.144.01-.053.008-.11.011-.164zm14.945-4.586c.008.029.016.057.027.107.024.155.051.31.072.464.03.219.067.437.078.657.017.344.027.689-.014 1.033-.037.315-.063.633-.116.946a6.153 6.153 0 01-.46 1.518c-.008.018-.01.039-.02.082.047-.03.077-.042.098-.064.085-.083.17-.167.248-.255.271-.305.458-.66.596-1.043.18-.498.228-1.011.145-1.531-.103-.65-.33-1.263-.597-1.881a9.055 9.055 0 00-.024-.055l-.033.022zM5.797 8.29a.26.26 0 00.018.153c.124.251.25.501.379.75.025.049.066.09.03.163-.284.06-.578.119-.88.255.059.038.097.06.132.087.042.032.112.058.09.12-.01.033-.075.048-.117.072.017.01.043.021.067.036.166.102.33.207.447.368.138.192.229.404.188.644-.079.469-.306.85-.69 1.132-.054.04-.106.083-.161.122a.243.243 0 00-.103.245.77.77 0 00.055.195c.083.196.22.35.375.492.083.076.159.164.222.257a.37.37 0 01.025.377c-.023.05-.05.099-.076.148-.03.06-.028.111.022.162.041.042.08.089.112.138.038.058.078.079.147.05a.486.486 0 01.333-.006c.16.046.302.126.444.21.13.077.264.149.4.219.067.035.14.05.219.026.071-.022.124.01.145.076.02.064-.003.108-.074.139-.07.03-.137.063-.209.088-.1.035-.201.073-.314.077-.013-.107.11-.088.127-.159-.206-.126-.643-.145-.801-.034.063.112.035.21-.096.313-.13-.1-.025-.202.002-.3a.209.209 0 00-.249.17c-.015.101.067.216.178.224.108.007.218-.005.326-.012.06-.005.12-.027.199 0-.103.123-.248.127-.357.19.002.05.07.086.019.131-.053.048-.095-.001-.132-.03-.08-.063-.16-.126-.231-.197a.474.474 0 01-.157-.311.52.52 0 00-.043-.172c-.032-.074-.032-.137.033-.19-.018-.03-.028-.053-.045-.072a1.222 1.222 0 01-.196-.369c-.053-.137-.046-.264.048-.381.024-.03.05-.06.064-.095a.664.664 0 00.047-.168c.017-.165-.064-.287-.182-.387-.186-.156-.36-.322-.46-.551-.005-.011-.024-.017-.037-.026-.011.017-.024.027-.025.038-.019.185-.045.37-.052.557-.014.377.058.743.162 1.104.118.41.289.798.488 1.173.267.502.537 1.002.812 1.5.055.098.13.189.208.27.198.202.452.272.724.273.202 0 .404-.006.605-.026.295-.03.59-.073.884-.113.183-.025.365-.057.548-.08.21-.026.38.073.522.21.16.156.305.327.447.5.22.265.397.56.554.867.05.098.07.1.147.03.13-.121.26-.242.394-.36.067-.059.088-.12.067-.213a3.535 3.535 0 01-.085-.796c.002-.157.006-.314.018-.471.015-.224.03-.45.06-.672a59.114 59.114 0 01.362-2.298c.087-.493.182-.984.268-1.477.06-.347.118-.694.162-1.043.034-.273.055-.55.063-.825.011-.332.003-.665.002-.998 0-.077.004-.155-.01-.23-.028-.142-.01-.155-.162-.19a5.826 5.826 0 00-.607-.107c-.146-.018-.207-.053-.221-.19-.006-.049-.025-.098-.041-.146-.009-.025-.024-.048-.046-.09l-.025.264c-.009.096-.029.116-.127.115-.055 0-.11-.008-.164-.008-.476 0-.952-.008-1.426.032-.095.008-.173-.015-.226-.103-.04-.066-.088-.126-.134-.186-.063-.084-.086-.093-.182-.06-.195.068-.388.138-.582.21a2.71 2.71 0 00-.675.394.986.986 0 01-.323.168c-.033.01-.07.008-.127.013.02-.066.024-.114.047-.15.064-.105.135-.205.205-.306.023-.033.049-.063.073-.095l-.015-.023-.201.037c-.146.04-.296.07-.437.122-.148.053-.266.023-.386-.072a3.623 3.623 0 01-.733-.786l-.093-.132zm8.592 8.963l-.147.09c-.22.134-.44.266-.659.402-.093.058-.184.12-.27.188-.085.07-.124.161-.072.272.047.1.093.2.147.294.047.08.124.138.213.147.11.01.228.012.336-.012.217-.05.372-.205.528-.357a.291.291 0 00.087-.308c-.046-.18-.079-.365-.118-.547-.011-.052-.027-.103-.045-.169zm-.257-2.409c-.12.291-.205.597-.325.91-.151.433-.294.87-.435 1.323.036-.01.054-.01.067-.018.261-.16.522-.324.785-.484.054-.033.071-.078.065-.138-.012-.13-.024-.262-.034-.393l-.068-.886c-.008-.103-.02-.206-.029-.31-.009 0-.017-.002-.026-.004zm3.081-8.13l.099.285c.08.231.159.463.24.714l.58 1.952c.187.63.372 1.262.558 1.893.114.382.235.762.343 1.146.072.257.126.519.186.799.044.206.087.413.127.64.034.106.023.226.077.325l.025-.006-.068-.362c-.038-.206-.077-.412-.113-.638-.015-.07-.029-.141-.046-.211-.095-.396-.177-.796-.29-1.187-.196-.685-.413-1.364-.618-2.046-.165-.549-.322-1.1-.488-1.648-.069-.227-.15-.45-.226-.695l-.117-.336c-.037-.107-.075-.216-.115-.322-.04-.106-.084-.21-.127-.314a7.558 7.558 0 01-.027.01zM6.225 14.304c-.063-.001-.115.014-.134.083a.35.35 0 00.41.012 4.533 4.533 0 00-.276-.095zM5.23 11.98c-.026-.027-.057-.048-.075.002-.012.032-.007.07-.01.113.082-.037.082-.037.085-.115zm.062-1.189a.135.135 0 00-.088.056.197.197 0 00-.025.11c.005.152.01.306.026.457a.751.751 0 00.066.218c.061.136.157.167.288.101.055-.027.06-.054.025-.11a4.52 4.52 0 01-.129-.211c-.015-.068-.066-.131-.033-.207.04-.09-.076-.116-.074-.19V10.874c-.003-.038-.006-.087-.056-.083zm-.017-.968a.867.867 0 00-.467.127c-.076.045-.084.07-.05.158.034.087.07.173.115.254.064.117.09.125.21.077a.657.657 0 01.336-.053c.202.022.357.136.504.264l.092.077c.007-.006.014-.013.022-.018-.019-.105-.035-.226-.149-.264-.157-.053-.324-.075-.508-.117l-.24-.005c.24-.169.452-.044.687.009-.063-.115-.153-.147-.23-.193-.082-.05-.17-.092-.25-.144-.06-.037-.12-.08-.072-.172zm10.233.325c-.23-.01-.427.08-.608.211-.034.026-.06.065-.105.117.087.026.15.046.232.065.044-.015.088-.03.13-.046.306-.114.61-.115.904.031.126.063.237.04.366-.005-.02-.031-.03-.054-.045-.071a.986.986 0 00-.448-.273c-.14-.044-.284-.024-.426-.03zM7.99 6.483a.308.308 0 00.002.133c.08.321.156.643.242.962.104.387.27.75.456 1.103.02.037.061.08.098.087a.404.404 0 00.253-.051l-.472-.84c-.23-.448-.405-.92-.579-1.394zM10.397.497c-.2-.008-.405.004-.603.034-.236.035-.47.087-.7.152-.287.08-.569.18-.852.273-.04.013-.074.038-.11.058.028.014.05.018.07.014.287-.068.58-.085.873-.09.134-.002.269.009.402.025.19.024.382.048.57.09.456.104.874.3 1.265.556.464.306.888.66 1.257 1.078.205.232.395.475.56.739.17.274.315.561.449.856.273.601.456 1.232.6 1.876.04.173.07.348.1.524.017.104.065.167.17.19.122.028.2.105.22.251-.003.102-.06.174-.129.24a1.065 1.065 0 00-.268.358.164.164 0 00.083-.039c.08-.086.162-.172.235-.265a.56.56 0 00.13-.333c.009-.05.022-.1.024-.15.007-.124-.017-.15-.143-.168-.025-.004-.049-.014-.073-.015-.082-.007-.125-.063-.137-.131-.033-.198-.004-.355.247-.408.086-.018.174-.03.26-.042.158-.023.315-.053.473-.067.14-.012.19.033.226.167.008.029.018.057.021.087.019.179-.008.225-.141.288-.027.013-.055.024-.078.042a.148.148 0 00-.051.067c-.039.144.073.382.206.445l.673.32c.023.011.05.015.075.023l.018-.026c-.015-.008-.032-.013-.044-.024a2.27 2.27 0 00-.544-.32 4.898 4.898 0 00-.173-.075.203.203 0 01-.126-.191c-.003-.085.045-.154.128-.187l.059-.025c.099-.044.118-.076.112-.187a.384.384 0 00-.008-.063c-.067-.294-.123-.59-.205-.88a9.478 9.478 0 00-.826-2.036 7.465 7.465 0 00-1.39-1.805 4.536 4.536 0 00-1.177-.824 3.656 3.656 0 00-1.016-.328 6.155 6.155 0 00-.712-.074zm6.719 5.955c.01.014.018.028.038.034l-.022-.044-.016.01zM4.103 3.917a.062.062 0 01-.03.012.455.455 0 01-.04.039c-.01.01-.02.02-.045.04l-.363.354c-.088.085-.17.178-.266.253-.284.22-.425.53-.544.855a.132.132 0 00-.007.071c.013.055.033.108.052.168l.074.026c-.017.056-.03.105-.047.152-.058.164-.118.327-.175.491-.005.015.008.036.019.077.08-.175.158-.33.225-.489.228-.544.484-1.074.819-1.561.09-.133.182-.266.283-.401.004-.006.007-.013.022-.03.001-.016.003-.032.015-.04l.008-.017zm12.976 2.408a.023.023 0 01.009.019.073.073 0 00-.006.01.188.188 0 00.007.02l.018.022c.002-.007.007-.016.005-.021-.003-.01-.012-.018-.02-.038a1.331 1.331 0 01-.013-.012zM4.199 4.48c-.003.004-.008.008-.027.014-.005.013-.011.025-.031.047a2.085 2.085 0 01-.124.167c-.048.07-.116.055-.181.041-.134-.028-.228.016-.287.143-.089.187-.187.37-.273.56-.049.108-.11.216-.118.36.081.003.154.007.228.008h.228a2.563 2.563 0 01-.079.264c-.01.052-.022.103-.033.155l.02.004c.018-.046.037-.092.067-.153.066-.142.13-.285.2-.426.02-.04.034-.1.116-.092 0 .043.004.084 0 .124-.005.045-.017.09-.028.143.141.043.086.174.115.269.102-.022.104-.195.248-.144v.205l.017.002.439-1.059c-.13 0-.246-.02-.358.033-.024.011-.058-.001-.108-.004.075-.15.139-.278.211-.417a.128.128 0 01.025-.036c0-.015-.001-.03.008-.038l.006-.02c-.005.006-.01.011-.028.017-.004.012-.009.024-.026.045a.085.085 0 01-.032.033c-.123.157-.09.164-.258.106-.079-.027-.078-.028-.047-.144.028-.046.056-.093.098-.15 0-.016-.001-.032.007-.042L4.2 4.48zm2.073-.67c-.003.006-.007.011-.027.016-.094.125-.194.246-.28.377-.155.238-.301.481-.451.723-.14.224-.345.368-.575.481-.017.008-.04.006-.079.011.012-.059.016-.109.033-.153a6.076 6.076 0 01.229-.518l-.007-.02a.138.138 0 01-.035.025c-.028.05-.055.1-.093.164-.26.424-.443.817-.442.95.024.004.048.011.073.013.177.013.188.007.26-.165.03-.07.077-.12.147-.15l.175-.07c.044-.018.085-.057.146-.032.003.05-.01.11.014.145.042.062.044.125.047.193.002.049.017.098.026.147.029-.034.039-.065.05-.097.142-.39.277-.782.428-1.17.1-.256.22-.504.33-.756.013-.03.013-.067.03-.092V3.81zm3.987-.34c0 .045.01.084.021.123.042.16.094.318.124.48.024.133.023.27.028.406 0 .033-.019.067-.032.11-.094-.058-.047-.158-.106-.215h-.125c-.015.072-.01.152-.046.2-.066.085-.155.154-.236.227-.043.038-.078.018-.103-.025l-.046-.087c-.065.035-.117.069-.172.093-.116.051-.235.095-.35.147-.085.038-.09.053-.07.147.014.075.034.148.047.223.013.072.05.109.123.124.233.05.462.115.657.265.058-.102.058-.102.168-.151.03-.014.06-.03.092-.042.08-.03.115-.017.15.06.023.048.041.098.066.158.06-.14-.042-.267.017-.416.157.18.24.39.375.567a.235.235 0 00.022-.098c.002-.124 0-.247.002-.371 0-.034.013-.067.02-.1l.032-.003c.11.155.13.354.226.52a3.036 3.036 0 00-.01-.392c-.004-.045 0-.074.05-.088.08.036.116.14.215.158-.03-.275-.423-1.137-.798-1.635-.114-.127-.2-.28-.34-.386zm-2.667.696c-.019.034-.03.05-.037.067-.061.185-.125.37-.18.556-.031.105-.087.169-.195.19-.09.019-.178.052-.268.073-.038.009-.089.015-.118-.003-.024-.016-.025-.069-.036-.106-.064.076-.082.087-.17.047-.133-.062-.262-.135-.393-.201-.048-.025-.093-.063-.17-.03-.043.12-.091.25-.137.382-.099.28-.087.242.095.453.046.048.102.03.154.023.054-.009.106-.03.16-.036.13-.013.26-.08.367-.015.204-.064.387-.122.571-.178.05-.015.089.005.114.054.022.042.034.093.082.121.038-.056-.013-.128.063-.178l.14.241-.042-1.46zm.278.358c-.096-.01-.107.01-.11.108-.002.038-.003.078.002.115.03.2.099.386.174.57.002.006.012.01.022.015l.078-.05c.052.036.081.088.153.088.205-.002.41.014.616.012.099-.001.158.042.205.12.018.03.024.077.088.066l-.08-.394c-.05-.195-.085-.395-.172-.589-.057.057-.114.068-.18.046a.72.72 0 00-.135-.028c-.22-.028-.44-.059-.66-.08zm10.254-1.727c.089.163.155.316.139.491-.016.168.026.342-.044.516-.047-.033-.088-.082-.112-.075-.117.035-.164-.057-.227-.115a4.772 4.772 0 01-.286-.29l-.104-.113a4.856 4.856 0 01-.023.019c.035.046.07.093.11.156.04.064.084.127.122.193.034.058.065.118.031.205-.082-.01-.164-.019-.246-.032-.06-.01-.101 0-.124.07-.031.098-.037.096-.15.09.02.042.036.08.057.116.041.074.03.138-.03.196-.06.06-.118.122-.178.181a.175.175 0 01-.185.046c-.222-.061-.447-.113-.67-.174-.032-.009-.063-.04-.086-.068-.03-.04-.052-.087-.08-.13-.044-.07-.09-.138-.136-.207a.18.18 0 00-.014.105c.012.127.03.253.035.38.005.1-.024.12-.121.104-.104-.017-.206-.04-.31-.058-.064-.012-.131-.028-.202.03l.081.208c.09 0 .166-.01.237.002a.819.819 0 01.458.251c.078.083.154.168.241.26l.018-.005c-.004-.006-.008-.013-.01-.04.014-.056-.062-.118.018-.178.031.03.064.057.088.09.058.078.111.159.169.257l.089.141.024-.013a2093.819 2093.819 0 01-.427-.934c.055.007.083.007.108.016.193.07.385.142.577.216.074.028.147.06.219.094.062.028.112.018.157-.033.05-.056.102-.112.154-.167.05-.051.095-.046.132.014.016.025.026.053.04.08.071.138.143.277.217.433l.159.308.025-.011c-.044-.106-.07-.218-.138-.334-.057-.182-.168-.346-.206-.545.136.034.362.326.567.732l.057.074.018-.011a1.563 1.563 0 01-.052-.127c-.046-.145-.097-.29-.136-.436-.022-.083-.036-.173.022-.26l.109.058-.026-.207.027-.016c.022.02.05.036.065.06.073.108.143.22.215.33.01.016.029.029.043.043-.036-.217-.2-.38-.229-.626l.155.112c.014-.166.012-.319.042-.465.032-.158-.023-.297-.063-.445.024.004.036.006.055.025.092.124.183.249.277.371.02.027.05.047.069.087l.04.063.019-.015a.293.293 0 01-.053-.082 27.922 27.922 0 01-.332-.49c-.221-.311-.363-.467-.485-.521zm-6.57.327c-.003.161.092.275.069.415l-.368.087c.09.139.032.237-.052.331-.05.057-.092.122-.143.178-.037.04-.046.078-.018.126l.16.275c.029.048.072.066.128.064.076-.003.152 0 .228-.001.116-.003.216.022.275.137.006.014.02.024.044.052.004-.059-.003-.098.01-.13.016-.04.04-.099.072-.108.084-.023.173-.024.26-.03.013-.001.027.018.04.029l.071.065c.019-.11-.082-.198-.024-.31l.126.04c-.026-.123-.07-.245-.071-.366 0-.123.051-.243.115-.36.107.062.16.156.234.253.183.265.36.533.494.834.165-.078.27.068.407.088-.003-.106-.133-.441-.197-.492a.142.142 0 00-.102-.028c-.06.011-.119.039-.191.063-.025-.039-.056-.078-.077-.122a3.936 3.936 0 00-.473-.783c-.076-.094-.16-.182-.228-.26l-.391.285c-.049.035-.094.03-.132-.017l-.169-.207c-.025-.03-.053-.059-.097-.108z',\n ],}\n\n// ── honest fallbacks for harnesses with no published brand mark ───────────\n// Inline lucide paths (v1.27, ISC) — `/web-react` ships no icon-library\n// dependency, so the stroke glyphs here match the set in `./controls`.\n\nfunction BotGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M12 8V4H8\" />\n <rect width=\"16\" height=\"12\" x=\"4\" y=\"8\" rx=\"2\" />\n <path d=\"M2 14h2\" />\n <path d=\"M20 14h2\" />\n <path d=\"M15 13v2\" />\n <path d=\"M9 13v2\" />\n </svg>\n )\n}\n\nfunction PlugGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M12 22v-5\" />\n <path d=\"M15 8V2\" />\n <path d=\"M17 8a1 1 0 0 1 1 1v4a4 4 0 0 1-4 4h-4a4 4 0 0 1-4-4V9a1 1 0 0 1 1-1z\" />\n <path d=\"M9 8V2\" />\n </svg>\n )\n}\n\nfunction TerminalGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M12 19h8\" />\n <path d=\"m4 17 6-6-6-6\" />\n </svg>\n )\n}\n\n/** Lucide fallback per harness without a brand mark — the same assignments\n * the legacy picker shipped (`factory-droids`→bot, `nanoclaw`→plug,\n * `cli-base`→terminal). */\nconst FALLBACK_GLYPHS: Partial<Record<Harness, (props: { className?: string }) => ReactNode>> = {\n 'factory-droids': BotGlyph,\n nanoclaw: PlugGlyph,\n 'cli-base': TerminalGlyph,\n}\n\n/**\n * Brand mark for a harness — size it from the call site (`className=\"h-4\n * w-4\"`). Unknown ids render the neutral bot, never an invented logo.\n * `data-glyph` names the resolved mark so tests and stories can assert\n * brand-vs-fallback without snapshotting path data.\n */\nexport function HarnessGlyph({ harness, className }: HarnessGlyphProps): ReactNode {\n const brand = BRAND_PATHS[harness]\n if (brand) {\n return (\n <svg\n className={className}\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n role=\"img\"\n aria-label={harness}\n data-glyph={harness}\n >\n {brand.map((d) => (\n <path key={d.slice(0, 32)} d={d} />\n ))}\n </svg>\n )\n }\n const Fallback = FALLBACK_GLYPHS[harness] ?? BotGlyph\n const kind = Fallback === BotGlyph ? 'bot' : Fallback === PlugGlyph ? 'plug' : 'terminal'\n return (\n <span role=\"img\" aria-label={harness} data-glyph={kind} className=\"inline-flex\">\n <Fallback className={className} />\n </span>\n )\n}\n","/**\n * `AgentSessionControls` — the CANONICAL model + harness + reasoning-effort\n * cluster a chat composer docks (see \"UI chrome ownership (picker canon)\" in\n * AGENTS.md). One component so every product's two composers (and every\n * product) share the same control surface and harness↔model coherence policy.\n *\n * PICKER CANON. The model menu below IS `/web-react`'s `ModelPicker` and the\n * thinking-budget pill IS `EffortPicker` — the canonical ecosystem pickers.\n * sandbox-ui's `dashboard/ModelPicker` and the model menu inside sandbox-ui's\n * `chat/AgentSessionControls` are legacy (deprecated, frozen, removed at\n * sandbox-ui's next major), and the `/chat-react` `ComposerAgentControls`\n * adapter that rendered sandbox-ui's strip is REMOVED — a surface that still\n * renders the sandbox-ui strip is showing the old design; migrate it\n * (props mapping in `docs/ui-picker-canon.md`).\n *\n * Dependency-free beyond React by design: `/web-react` must not force the\n * optional sandbox-ui peer, so this component — the canonical one — can never\n * require it.\n *\n * Two layouts, additive — the default preserves the prior hand-rolled behavior:\n * - `layout=\"inline\"` (default): model, harness, and effort sit side by side as\n * pills. This is the original arrangement; existing call sites that mounted\n * `ModelPicker` + a harness picker + `EffortPicker` in a row get the same UI.\n * - `layout=\"compact\"`: the model picker stays inline and visible; the agent\n * backend (\"harness\") and reasoning-effort controls — internal jargon a user\n * rarely needs — tuck behind a single gear popover with plain-English copy.\n *\n * Harness ↔ model coherence is identical in both layouts, via the substrate's\n * snap helpers (`@tangle-network/agent-app/harness`): changing the harness snaps\n * an incompatible model to that harness's best catalog option; changing the\n * model switches to the model's native harness. Catalog model ids are canonical\n * (\"provider/model\"), which is exactly what the snap helpers expect — no id\n * translation is needed here.\n *\n * Dependency-free beyond React: inline SVG glyphs, CSS-var / Tailwind tokens the\n * app shell defines. The harness picker is rendered inline so this needs no\n * sandbox-ui dependency.\n */\n\nimport { useId, useMemo, useRef, useState, type ReactNode } from 'react'\nimport {\n snapHarnessToModel,\n snapModelToHarness,\n type Harness,\n} from '../harness'\nimport type { CatalogModel } from '../runtime/model-catalog'\nimport { ModelPicker, EffortPicker, CheckGlyph, OVERLAY_SHADOW, pickerRootClass, PopoverSurface, usePopover } from './controls'\nimport type { EffortLevel } from './controls'\nimport { HarnessGlyph } from './harness-glyphs'\n\n/** Plain-English labels for the harnesses a product is likely to expose. Unknown\n * ids fall back to the raw value so a new backend still renders a usable label. */\nconst HARNESS_LABELS: Partial<Record<Harness, string>> = {\n opencode: 'OpenCode (any model)',\n 'claude-code': 'Claude Code (Anthropic)',\n codex: 'Codex (OpenAI)',\n 'kimi-code': 'Kimi (Moonshot)',\n amp: 'Amp',\n 'factory-droids': 'Factory Droids',\n cursor: 'Cursor',\n hermes: 'Hermes',\n forge: 'Forge',\n pi: 'Pi',\n openclaw: 'OpenClaw',\n acp: 'ACP',\n 'cli-base': 'CLI',\n}\n\nfunction harnessLabel(h: Harness): string {\n return HARNESS_LABELS[h] ?? h\n}\n\nfunction ChevronDown({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n )\n}\n\n/** lucide `lock` — the closed padlock on a pinned harness trigger. */\nfunction LockGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <rect width=\"18\" height=\"11\" x=\"3\" y=\"11\" rx=\"2\" ry=\"2\" />\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\" />\n </svg>\n )\n}\n\nfunction GearGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n <path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1Z\" />\n </svg>\n )\n}\n\n/** Tailwind utilities for keyboard-visible focus on popover options + triggers. */\nconst FOCUS_RING =\n ''\n\n/**\n * Pill-styled harness picker — inline, no sandbox-ui dependency. The brand\n * marks come from `./harness-glyphs` (the set the legacy sandbox-ui picker\n * shipped, vendored inline).\n *\n * `rounded-full` + `min-h-[36px]`, not `rounded-lg` at whatever height the\n * padding gives: this pill sits beside `ModelPicker` and `EffortPicker` in\n * both layouts, and both of those are 36px pills. A single odd-shaped control\n * is what made the compact popover read as a pile of unrelated widgets rather\n * than one selector stack.\n *\n * `fullWidth` is opt-in and means what it means on `EffortPicker` — see\n * {@link pickerRootClass}.\n *\n * `lockReason` PINS the control: it keeps its selector shape and keeps\n * reporting the harness the thread is on, opens nothing, and explains itself\n * on hover AND on keyboard focus. Three deliberate choices there:\n *\n * - `aria-disabled`, never the native `disabled` attribute. A disabled button\n * is removed from the tab order and fires no pointer events in most\n * browsers, so the one control that has something to explain would become\n * the one control that can never be asked.\n * - the reason rides a permanent visually-hidden node that `aria-describedby`\n * points at, so assistive tech has it whether or not the floating hint is\n * up; the floating copy is `aria-hidden` so nothing is announced twice.\n * - the hint is a {@link PopoverSurface}, not an absolutely-positioned div —\n * the compact panel is an `overflow-y-auto` box, which clips a positioned\n * descendant, and that surface is this package's answer to exactly that.\n */\nfunction HarnessPicker({\n value,\n onChange,\n available,\n fullWidth = false,\n lockReason,\n}: {\n value: Harness\n onChange: (h: Harness) => void\n available?: ReadonlyArray<Harness>\n fullWidth?: boolean\n lockReason?: string\n}) {\n const [open, setOpen] = useState(false)\n const [hintOpen, setHintOpen] = useState(false)\n const { containerRef, triggerRef, panelRef, triggerProps } = usePopover(open, setOpen)\n const hintPanelRef = useRef<HTMLDivElement>(null)\n const panelId = useId()\n const reasonId = useId()\n const locked = lockReason !== undefined\n const options = available ?? (Object.keys(HARNESS_LABELS) as Harness[])\n const showHint = () => setHintOpen(true)\n const hideHint = () => setHintOpen(false)\n return (\n <div ref={containerRef} className={pickerRootClass(fullWidth)}>\n <button\n type=\"button\"\n {...triggerProps}\n aria-haspopup={locked ? undefined : true}\n aria-expanded={locked ? undefined : open}\n aria-controls={!locked && open ? panelId : undefined}\n aria-disabled={locked || undefined}\n aria-describedby={locked ? reasonId : undefined}\n onClick={locked ? undefined : () => setOpen(!open)}\n onMouseEnter={locked ? showHint : undefined}\n onMouseLeave={locked ? hideHint : undefined}\n onFocus={locked ? showHint : undefined}\n onBlur={locked ? hideHint : undefined}\n title=\"Agent backend\"\n className={`inline-flex min-h-[36px] w-full items-center justify-between gap-1.5 rounded-full border border-border bg-card px-3 py-1.5 text-sm font-medium text-foreground transition ${\n locked ? 'cursor-default' : 'hover:bg-accent'\n } ${FOCUS_RING}`}\n >\n <span className=\"flex min-w-0 items-center gap-1.5\">\n <HarnessGlyph harness={value} className=\"h-4 w-4 shrink-0 text-foreground\" />\n <span className=\"truncate\">{harnessLabel(value)}</span>\n </span>\n {locked ? (\n <LockGlyph className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n ) : (\n <ChevronDown className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n )}\n </button>\n {locked && (\n <>\n <span id={reasonId} className=\"sr-only\">\n {lockReason}\n </span>\n <PopoverSurface\n open={hintOpen}\n role=\"tooltip\"\n triggerRef={triggerRef}\n panelRef={hintPanelRef}\n matchTriggerWidth={fullWidth}\n className={`max-w-[248px] rounded-lg border border-card-edge bg-popover px-2.5 py-1.5 text-xs leading-snug text-muted-foreground ${OVERLAY_SHADOW}`}\n >\n <span aria-hidden>{lockReason}</span>\n </PopoverSurface>\n </>\n )}\n <PopoverSurface\n open={!locked && open}\n id={panelId}\n role=\"menu\"\n triggerRef={triggerRef}\n panelRef={panelRef}\n matchTriggerWidth\n className={`max-h-64 min-w-[248px] overflow-y-auto rounded-xl border border-card-edge bg-popover p-1 ${OVERLAY_SHADOW}`}\n >\n {options.map((h) => (\n <button\n key={h}\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={h === value}\n onClick={() => {\n onChange(h)\n setOpen(false)\n }}\n className={`flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-left text-sm transition ${FOCUS_RING} ${\n h === value ? 'bg-primary/10 font-medium' : 'hover:bg-accent'\n }`}\n >\n <HarnessGlyph harness={h} className=\"h-4 w-4 shrink-0 text-foreground\" />\n <span className=\"truncate\">{harnessLabel(h)}</span>\n {h === value && <CheckGlyph className=\"ml-auto h-3.5 w-3.5 shrink-0 text-primary\" />}\n </button>\n ))}\n </PopoverSurface>\n </div>\n )\n}\n\nexport interface AgentSessionControlsProps {\n /** Catalog models — canonical provider-prefixed ids. */\n models: CatalogModel[]\n modelsLoading?: boolean\n /** Selected canonical model id. */\n model: string\n onModelChange(modelId: string): void\n /** Current harness; harness↔model coherence is enforced on every change. */\n harness: Harness\n onHarnessChange(harness: Harness): void\n /** Harnesses to offer; defaults to the labeled set. */\n availableHarnesses?: ReadonlyArray<Harness>\n /** Reasoning-effort value + setter. Shown only when the selected model\n * `supportsReasoning`, matching `EffortPicker`'s guidance. */\n effort: string\n onEffortChange(effort: string): void\n /**\n * Levels to offer, forwarded verbatim to {@link EffortPicker}. Omit for the\n * default vocabulary.\n *\n * A product whose backend applies only a SUBSET of the levels for the\n * selected harness/model passes that subset here. Without it the strip\n * offers every level and the backend silently ignores the ones it does not\n * apply — a control that reports a choice the system never made.\n *\n * This is the COMPLETE renderable set, not an allow-list layered over a\n * default one — the removed `ComposerAgentControls`' `available` list was the\n * latter, and its picker injected the `auto` sentinel itself. A list that\n * omits the current {@link effort} is still safe: `EffortPicker` reconciles\n * the selected value into the rendered list under its own name rather than\n * resolving it to a different entry (`reconcileEffortLevels`). Build the list\n * from engine ids with `effortLevelsFromIds`; the migration is in\n * `docs/ui-picker-canon.md`.\n */\n effortLevels?: readonly EffortLevel[]\n /**\n * `inline` (default): model, harness, effort side by side — the prior\n * behavior. `compact`: model inline, harness + effort behind a gear popover.\n */\n layout?: 'inline' | 'compact'\n /** Hide the harness control entirely (single-harness products). */\n showHarness?: boolean\n /**\n * PIN the harness and say why, in the user's words (\"This thread already has\n * messages — start a new chat to switch backend\"). Presence IS the lock:\n * there is no separate boolean, because a lock a user cannot read is the\n * thing this prop exists to replace.\n *\n * The control stays VISIBLE and reports the harness the thread is on — the\n * shape a locked selector has to keep, since a thread whose backend is fixed\n * is exactly when a user wants to know what it is. Hiding it (`showHarness:\n * false`) is what pushed products into rendering their own lock label\n * outside the panel.\n *\n * While locked, `onHarnessChange` is never called — not from the picker, and\n * not from the model↔harness coherence policy either. See\n * {@link useCoherentHandlers}.\n */\n harnessLockReason?: string\n renderProviderBadge?: (provider: string) => ReactNode\n className?: string\n}\n\n/**\n * Apply the harness↔model coherence policy and emit the resulting change(s).\n * Returned from a hook-free helper so both layouts share one implementation.\n *\n * A LOCKED harness ({@link AgentSessionControlsProps.harnessLockReason}) is\n * authoritative over the snap: picking a model whose native backend differs\n * still changes the model, and leaves the harness alone. The alternative —\n * snapping a harness the UI has just told the user cannot change — is the one\n * behaviour a lock must not have.\n */\nfunction useCoherentHandlers(props: AgentSessionControlsProps) {\n const { model, models, harness, onModelChange, onHarnessChange, harnessLockReason } = props\n const canonicalIds = useMemo(() => models.map((m) => m.id), [models])\n const harnessLocked = harnessLockReason !== undefined\n\n const onModel = (next: string) => {\n onModelChange(next)\n if (harnessLocked) return\n const nextHarness = snapHarnessToModel(harness, next)\n if (nextHarness !== harness) onHarnessChange(nextHarness)\n }\n\n const onHarness = (next: Harness) => {\n onHarnessChange(next)\n const snapped = snapModelToHarness(next, model, canonicalIds)\n if (snapped !== model) onModelChange(snapped)\n }\n\n return { onModel, onHarness }\n}\n\nexport function AgentSessionControls(props: AgentSessionControlsProps) {\n const {\n models,\n modelsLoading,\n model,\n harness,\n availableHarnesses,\n effort,\n onEffortChange,\n effortLevels,\n layout = 'inline',\n showHarness = true,\n harnessLockReason,\n renderProviderBadge,\n className,\n } = props\n const { onModel, onHarness } = useCoherentHandlers(props)\n const [open, setOpen] = useState(false)\n const { containerRef: popoverRef, triggerRef, panelRef, triggerProps } = usePopover(open, setOpen)\n const panelId = useId()\n\n const selectedModel = models.find((m) => m.id === model)\n const showEffort = selectedModel?.supportsReasoning ?? true\n\n const modelPicker = (\n <ModelPicker\n value={model}\n onChange={onModel}\n models={models}\n loading={modelsLoading}\n renderProviderBadge={renderProviderBadge}\n />\n )\n\n if (layout === 'inline') {\n return (\n <div className={`flex items-center gap-1.5 ${className ?? ''}`}>\n {modelPicker}\n {showHarness && (\n <HarnessPicker value={harness} onChange={onHarness} available={availableHarnesses} lockReason={harnessLockReason} />\n )}\n {showEffort && <EffortPicker value={effort} onChange={onEffortChange} levels={effortLevels} />}\n </div>\n )\n }\n\n // compact: model inline; harness + effort behind a gear popover.\n const hasAdvanced = showHarness || showEffort\n return (\n <div className={`flex items-center gap-1.5 ${className ?? ''}`}>\n {modelPicker}\n {hasAdvanced && (\n <div ref={popoverRef} className=\"relative inline-flex\">\n <button\n type=\"button\"\n {...triggerProps}\n aria-controls={open ? panelId : undefined}\n onClick={() => setOpen(!open)}\n title=\"Model settings — pick the agent backend and how hard it thinks\"\n className={`flex h-8 w-8 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted hover:text-foreground data-[state=open]:bg-muted ${FOCUS_RING}`}\n data-state={open ? 'open' : 'closed'}\n >\n <GearGlyph className=\"h-4 w-4\" />\n </button>\n <PopoverSurface\n open={open}\n id={panelId}\n triggerRef={triggerRef}\n panelRef={panelRef}\n className={`w-72 space-y-3 overflow-y-auto rounded-xl border border-card-edge bg-popover p-3 ${OVERLAY_SHADOW}`}\n >\n {showHarness && (\n <div className=\"space-y-1.5\">\n <p className=\"text-xs font-medium text-foreground\">Agent backend</p>\n <HarnessPicker\n value={harness}\n onChange={onHarness}\n available={availableHarnesses}\n fullWidth\n lockReason={harnessLockReason}\n />\n <p className=\"text-xs leading-snug text-muted-foreground\">\n The engine that runs the agent. Switching it keeps your model choice compatible.\n </p>\n </div>\n )}\n {showEffort && (\n <div className=\"space-y-1.5\">\n <p className=\"text-xs font-medium text-foreground\">Thinking</p>\n <EffortPicker value={effort} onChange={onEffortChange} levels={effortLevels} label=\"\" fullWidth />\n <p className=\"text-xs leading-snug text-muted-foreground\">\n How hard the agent thinks before answering. Higher is slower but more thorough.\n </p>\n </div>\n )}\n </PopoverSurface>\n </div>\n )}\n </div>\n )\n}\n","import { joinClasses } from './class-names'\n\n/**\n * The plan-mode toggle state an entry surface docks beside the composer. The\n * agent-identity pickers (model / harness / effort) speak the canonical\n * `AgentSessionControls` vocabulary — see \"UI chrome ownership (picker\n * canon)\" in AGENTS.md.\n */\nexport interface ComposerPlanModeSelection {\n enabled: boolean\n setEnabled: (next: boolean) => void\n saving?: boolean\n}\n\nexport interface ComposerModeControlsProps {\n /**\n * Plan-approval mode. Products pass it only when the selected backend can\n * propose a plan and wait for approval; omitted means nothing renders.\n */\n planMode?: ComposerPlanModeSelection\n}\n\n/** Checklist glyph, inline like every `/web-react` glyph — an icon-library\n * import here would turn that optional peer into a build-time requirement\n * for every consumer of the default surface. */\nfunction ListChecksGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"m3 17 2 2 4-4M3 7l2 2 4-4M13 6h8M13 12h8M13 18h8\" />\n </svg>\n )\n}\n\n/**\n * The shared plan-mode toggle for the left side of an agent composer.\n * Plan mode is a behavioral switch, not part of the profile/backend/model/\n * thinking identity controls on the right.\n */\nexport function ComposerModeControls({ planMode }: ComposerModeControlsProps) {\n if (!planMode) return null\n\n return (\n <button\n type=\"button\"\n aria-pressed={planMode.enabled}\n disabled={planMode.saving}\n onClick={() => planMode.setEnabled(!planMode.enabled)}\n title=\"Plan mode: the agent proposes a plan you approve before it executes\"\n className={joinClasses(\n // Inset ring: this chip sits in a composer row that clips its overflow,\n // so an outward ring loses three of its four sides. Only the offset is\n // overridden — width and colour stay with the tokens.\n 'inline-flex h-7 items-center gap-1 rounded-full border px-2.5 text-xs transition-colors focus-visible:[outline-offset:-2px]',\n planMode.enabled\n ? 'border-primary/50 bg-primary/10 text-primary'\n : 'border-border bg-transparent text-muted-foreground hover:text-foreground',\n planMode.saving && 'opacity-60',\n )}\n >\n <ListChecksGlyph className=\"h-3.5 w-3.5\" />\n Plan\n </button>\n )\n}\n","import { useState, type ReactNode } from 'react'\nimport { ChatComposer } from './chat-composer'\nimport { ATTACHMENT_ACCEPT, useComposerAttachments } from './use-composer-attachments'\nimport type { ComposerFileRejection } from './composer-file-accept'\nimport type { UseFileMentionsResult } from './use-file-mentions'\nimport type { ChatAttachmentInput, FileMention } from '../chat-routes/wire'\nimport {\n AgentSessionControls,\n type AgentSessionControlsProps,\n} from './agent-session-controls'\nimport { ComposerModeControls, type ComposerPlanModeSelection } from './composer-mode-controls'\n\nexport interface EntryComposerProps {\n /** The one line above the input. Domain copy — always a product parameter. */\n heading?: ReactNode\n /** Optional supporting line under the heading. */\n subheading?: ReactNode\n placeholder?: string\n initialValue?: string\n sendLabel?: string\n disabled?: boolean\n /**\n * Agent identity (backend/model/effort). Pass it and the control row renders;\n * omit it and the composer ships without one. Omitting is a real choice for a\n * surface with nothing to choose — it should never be an oversight, which is\n * why this is one prop rather than a free-form slot a caller can forget.\n *\n * This is the CANONICAL picker cluster: `AgentSessionControls` from\n * `/web-react` (its model menu IS the canonical `ModelPicker`). The legacy\n * sandbox-ui adapter that used to back this prop was removed; the props\n * mapping for migrating a stored selection lives in\n * `docs/ui-picker-canon.md`.\n */\n agent?: AgentSessionControlsProps\n /**\n * Product-specific behavioral controls docked on the LEFT. A mode is an\n * on/off switch, not a value picker, so it sits apart from agent identity.\n * For the standard plan-approval control, prefer `planMode`.\n */\n modes?: ReactNode\n /**\n * Standard plan-approval mode. Pass only when the selected backend supports\n * it; omitted means the control is not rendered.\n */\n planMode?: ComposerPlanModeSelection\n /**\n * Upload endpoint for staged attachments. Omit and the attach affordance is\n * hidden — a composer with no place to put a file must not offer one.\n */\n uploadUrl?: string\n accept?: string\n onAttachmentError?: (reason: string) => void\n /** `ComposerFileRejection` is structurally identical to the sandbox-ui type\n * this prop used to infer (`{ file: File; reason: string }`), so handlers\n * typed against either compile unchanged. */\n onRejectFiles?: (rejections: ComposerFileRejection[]) => void\n /**\n * `@`-file mentions. The hook itself is shared (`useFileMentions`), but the\n * cold-box policy around it — whether pressing `@` is worth starting a\n * sandbox — is a per-surface product call, so the result is injected rather\n * than created here.\n */\n mentions?: UseFileMentionsResult\n mentionPopoverClassName?: string\n /** Rendered under the composer: suggestion pills, an onboarding nudge, a\n * disclaimer. All domain copy. */\n footer?: ReactNode\n /** Block submit until a persisted selection has resolved against the catalog,\n * so the first turn cannot go out under the wrong model. Defaults to true. */\n ready?: boolean\n onSubmit: (\n prompt: string,\n attachments: ChatAttachmentInput[],\n mentions: FileMention[],\n ) => void\n className?: string\n composerClassName?: string\n /** Max width of the centered column. Defaults to the fleet's 820px. */\n maxWidth?: number\n}\n\n/**\n * `EntryComposer` — the centered \"what do you want to work on?\" surface a\n * product shows before a conversation exists (a new thread, an empty session,\n * a workspace overview).\n *\n * It exists because three products each grew their own, and they drifted into\n * three different capability sets rather than three different looks: one lost\n * the attach button, one lost the model and effort pickers entirely, one\n * hand-rolled a `<textarea>` and wired pickers from three separate packages\n * into one row. The composer, the pickers, the attachment queue and the mention\n * index were all ALREADY shared — what was not shared was the assembly, so\n * every product re-derived which controls an entry surface gets, and each\n * re-derivation dropped a different one.\n *\n * Mechanism lives here: layout, the attachment queue, the mention wiring, the\n * submit gate (nothing sends while an upload is pending or failed, or before\n * the model selection has resolved). Domain stays a parameter: `heading`,\n * `placeholder`, `footer` (suggestion pills, disclaimers) and the selections\n * themselves.\n *\n * The input is `ChatComposer` — the same component a docked in-thread\n * composer renders directly; this assembly adds only the hero layout around\n * it.\n */\nexport function EntryComposer({\n heading,\n subheading,\n placeholder,\n initialValue = '',\n sendLabel,\n disabled,\n agent,\n modes,\n planMode,\n uploadUrl,\n accept = ATTACHMENT_ACCEPT,\n onAttachmentError,\n onRejectFiles,\n mentions,\n mentionPopoverClassName,\n footer,\n ready = true,\n onSubmit,\n className,\n composerClassName,\n maxWidth = 820,\n}: EntryComposerProps) {\n const [value, setValue] = useState(initialValue)\n const attachments = useComposerAttachments({\n uploadUrl,\n enabled: !!uploadUrl,\n onReject: onAttachmentError,\n onError: onAttachmentError,\n })\n\n function submit(prompt: string) {\n if (!ready || disabled) return\n const next = prompt.trim()\n // A pending or failed upload must not send: the turn would reference an\n // attachment the store does not have. Surface why rather than no-op'ing.\n if (attachments.hasPending || attachments.hasError) {\n if (attachments.blockReason) onAttachmentError?.(attachments.blockReason)\n return\n }\n if (!next && attachments.references.length === 0) return\n onSubmit(next, attachments.references, mentions?.mentions ?? [])\n setValue('')\n attachments.clear()\n mentions?.clearMentions()\n }\n\n return (\n <div\n className={\n className ??\n 'relative flex flex-1 flex-col items-center justify-center overflow-hidden bg-background px-5'\n }\n >\n <div className=\"w-full\" style={{ maxWidth }}>\n {heading ? (\n <h2 className=\"mb-4 text-center text-[1.75rem] font-medium tracking-tight text-foreground\">\n {heading}\n </h2>\n ) : null}\n {subheading ? (\n <p className=\"mx-auto mb-9 max-w-md text-center text-sm leading-relaxed text-muted-foreground\">\n {subheading}\n </p>\n ) : (\n heading ? <div className=\"mb-7\" /> : null\n )}\n <ChatComposer\n className={composerClassName ?? 'vt-composer'}\n value={value}\n onValueChange={setValue}\n onSend={(message) => submit(message)}\n placeholder={placeholder}\n sendLabel={sendLabel}\n // The circular arrow AgentComposer shipped and the agent-app canon\n // for new surfaces.\n sendVariant=\"icon\"\n disabled={disabled}\n autoFocus\n // A hero composer autofocuses on mount, so the Cmd/Ctrl+L hint\n // would advertise a shortcut to the input the user is already in.\n focusShortcut={false}\n canSubmitAttachmentsOnly\n accept={accept}\n pendingFiles={attachments.composerFiles}\n onAttach={uploadUrl ? (files) => void attachments.addFiles(files) : undefined}\n onRejectFiles={onRejectFiles}\n onRemoveFile={attachments.removeAttachment}\n onRetryFile={attachments.retry}\n mention={\n mentions\n ? {\n ...mentions.mention,\n // Sidebar tone + hairline so the popover reads as app chrome\n // (the same treatment the picker menus get) instead of the\n // brighter overlay tone the component defaults to.\n popoverClassName:\n mentionPopoverClassName ??\n 'bg-surface-container-low border-[var(--md3-outline-variant)]',\n }\n : undefined\n }\n controls={modes ?? <ComposerModeControls planMode={planMode} />}\n trailing={\n agent ? <AgentSessionControls {...agent} /> : undefined\n }\n />\n {footer ? <div className=\"mt-7\">{footer}</div> : null}\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":["useCallback","useEffect","useRef","useState","useState","useRef","useCallback","useEffect","useCallback","useEffect","useMemo","useRef","useState","useRef","useState","useCallback","enabled","useEffect","useMemo","jsx","jsxs","useId","useMemo","useRef","useState","Fragment","jsx","jsxs","useState","useRef","useId","useMemo","jsx","jsxs","useState","jsx","jsxs","useState"]}