{"version":3,"sources":["../lib/demo/types.ts","../lib/demo/sleep.ts","../lib/motion/index.ts","../lib/demo/use-scripted-demo.ts","../lib/utils.ts","../components/ui/composer.tsx"],"names":["DEMO_SPEED_PRESETS","DEMO_IN_VIEW_AMOUNT","sleep","ms","signal","resolve","reject","timer","onAbort","typeText","text","onTick","stagger","i","isAbortError","err","MOTION_ATTR","MOTION_OFF","REDUCE_QUERY","readReduced","osReduced","toggledOff","useReducedMotion","reduced","setReduced","z","update","mql","observer","usePageActive","active","setActive","framed","compute","DEFAULT_LOOP_DELAY_MS","INSTANT_PRESET","useScriptedDemo","opts","steps","interpret","speed","trigger","playProp","loop","loopDelay","maxLoops","containerRef","onComplete","onLoopReset","preset","effectiveLoop","pageActive","isPlaying","setIsPlaying","b","isComplete","setIsComplete","currentIndex","setCurrentIndex","noopRef","inView","useInView","liveInView","playbackActive","manualPlayTick","setManualPlayTick","shouldPlay","interpretRef","completeRef","loopResetRef","controllerRef","restartTimerRef","stop","play","n","restart","delayMs","controller","ctx","cycles","cn","inputs","twMerge","clsx","SubmitPlugin","onSubmit","enabled","editor","useLexicalComposerContext","KEY_ENTER_COMMAND","event","COMMAND_PRIORITY_HIGH","PastePlugin","onImageFiles","rootElement","handler","e","imageFiles","it","f","AutoFocusPlugin","FORMAT_TEXT_KEYS","applyFormat","format","textKey","FORMAT_TEXT_COMMAND","INSERT_UNORDERED_LIST_COMMAND","INSERT_ORDERED_LIST_COMMAND","selection","$getSelection","$isRangeSelection","tag","$setBlocksType","$createHeadingNode","node","$createQuoteNode","insertMentionNode","value","data","$createBeautifulMentionNode","selectSubstring","needle","found","root","$getRoot","idx","cursor","startNode","startOffset","endNode","endOffset","walk","tn","len","nodeStart","nodeEnd","endIdx","child","sn","en","clearEditor","para","$createParagraphNode","snapshotContent","json","mentions","m","FORMAT_BUTTONS","Bold","Italic","Underline","Strikethrough","CodeIcon","Heading1","Heading2","Heading3","Quote","ListIcon","ListOrdered","ComposerToolbar","formats","activeFormats","setActiveFormats","mergeRegister","editorState","next","fmt","visible","Icon","label","isActive","AttachmentChips","attachments","onRemove","AnimatePresence","motion","att","X","ComposerInner","placeholder","triggers","attachmentsCfg","formatList","showToolbar","isLoading","onStop","maxLength","autoFocus","submitOnEnter","leftActions","rightActions","hideSend","bare","className","onChange","readOnly","handleEditorReady","submitRef","restartRef","setAttachments","ingestImages","fileInputRef","hasContent","setHasContent","onChangeRef","handleSubmit","content","hasText","hasAttachments","a","demoRestart","step","sel","focus","partial","head","p","$createTextNode","stripLen","char","sel2","showDefaultSend","showDefaultAttach","showActionRow","id","prev","target","RichTextPlugin","ContentEditable","LexicalErrorBoundary","HistoryPlugin","ListPlugin","LinkPlugin","OnChangePlugin","BeautifulMentionsPlugin","acc","t","item","Paperclip","Square","Send","Composer","props","forwardedRef","initialText","initialJson","toolbar","attachmentsProp","rest","obj","attachmentsRef","files","candidates","tok","room","file","editorRef","_steps","lexicalTheme","initialConfig","error","HeadingNode","QuoteNode","ListNode","ListItemNode","LinkNode","AutoLinkNode","CodeNode","CodeHighlightNode","BeautifulMentionNode","richMode","innerRest","LexicalRoot","ComposerReply","ref"],"mappings":"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","file":"composer.mjs","sourcesContent":["/**\n * Shared types + presets for scripted component demos.\n *\n * Lives at the bottom of the demo primitive stack so the hook, the\n * cursor, and any component that opts in (`<Code>`, `<Composer>`,\n * `<DemoStage>`) all read from one definition of \"what slow / normal /\n * fast feels like\" and one definition of \"when do we start playing\".\n *\n * If you ever want to retune the cadence of every demo on the marketing\n * site at once, this is the file to edit.\n */\n\n/**\n * Animation feel. Maps onto a triple of timing values so authors can\n * pick a vibe (slow / normal / fast) instead of hand-tuning ms.\n * Components that need finer control can still override the resolved\n * values per-instance.\n */\nexport type DemoSpeed = \"slow\" | \"normal\" | \"fast\";\n\n/**\n * What kicks the demo off:\n *   - `mount`   plays immediately on first paint\n *   - `inView`  waits for the container to cross the viewport threshold\n *   - `manual`  driven by the `play` prop or imperative ref\n */\nexport type DemoTrigger = \"mount\" | \"inView\" | \"manual\";\n\n/**\n * Speed presets shared across every scripted-demo surface. Three\n * unambiguously distinct feels: `slow` is \"I am being shown\", `normal`\n * is \"I am being told\", `fast` is \"I am being briefed\".\n *\n *   - `tokenStagger`  per-character cadence for typing-style steps\n *   - `lineStagger`   per-line cadence for reveal-style demos\n *   - `preDelay`      pause after the trigger fires before the first tick\n *   - `fadeMs`        default enter-transition duration for revealed parts\n */\nexport const DEMO_SPEED_PRESETS: Record<\n  DemoSpeed,\n  {\n    tokenStagger: number;\n    lineStagger: number;\n    preDelay: number;\n    fadeMs: number;\n  }\n> = {\n  slow: { tokenStagger: 70, lineStagger: 200, preDelay: 500, fadeMs: 480 },\n  normal: { tokenStagger: 22, lineStagger: 55, preDelay: 200, fadeMs: 280 },\n  fast: { tokenStagger: 8, lineStagger: 18, preDelay: 60, fadeMs: 160 },\n};\n\n/**\n * `inView` threshold + `once` semantics applied by the hook. Marketing\n * surfaces want the reveal to fire once, after enough of the block is\n * visible that the user actually sees it animate.\n */\nexport const DEMO_IN_VIEW_AMOUNT = 0.55;\n","/**\n * Cancellable sleep + type helpers for scripted demos.\n *\n * The runner threads an `AbortSignal` through every step so a `stop()`\n * call (or an unmount) can short-circuit long waits and typing loops\n * cleanly. Consumers write `await sleep(ms, signal)` inside their\n * interpret callback and the cancellation is automatic.\n *\n * Note: this throws DOMException(\"Aborted\", \"AbortError\") on cancel.\n * The runner's outer try/catch swallows that specific error and exits;\n * any other rejection bubbles up so authoring bugs aren't silenced.\n */\n\n/**\n * Promisified setTimeout that resolves after `ms` milliseconds, or\n * rejects with an AbortError if the signal fires first. Resolves\n * immediately if `ms <= 0`.\n */\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n  if (ms <= 0) return Promise.resolve();\n  return new Promise((resolve, reject) => {\n    if (signal?.aborted) {\n      reject(new DOMException(\"Aborted\", \"AbortError\"));\n      return;\n    }\n    const timer = window.setTimeout(() => {\n      signal?.removeEventListener(\"abort\", onAbort);\n      resolve();\n    }, ms);\n    const onAbort = () => {\n      window.clearTimeout(timer);\n      reject(new DOMException(\"Aborted\", \"AbortError\"));\n    };\n    signal?.addEventListener(\"abort\", onAbort, { once: true });\n  });\n}\n\n/**\n * Type `text` one character at a time, calling `onTick(partial)` for\n * each prefix. Stagger between ticks defaults to 22ms (the `normal`\n * speed preset's `tokenStagger`).\n *\n * `onTick` receives the cumulative typed text (not the new char), so\n * consumers can do `setBuffer(prefix + partial)` without tracking state\n * themselves. The final call fires with the complete text — there's no\n * separate \"done\" callback.\n */\nexport async function typeText(\n  text: string,\n  onTick: (partial: string) => void,\n  stagger = 22,\n  signal?: AbortSignal,\n): Promise<void> {\n  // Non-positive stagger (the reduced-motion preset, or any caller that\n  // wants no typing animation): emit the whole string in a single tick so\n  // the surface lands on its final state without per-character motion, or\n  // the flicker a 0ms loop would produce.\n  if (stagger <= 0) {\n    onTick(text);\n    return;\n  }\n  for (let i = 1; i <= text.length; i++) {\n    onTick(text.slice(0, i));\n    if (i < text.length) await sleep(stagger, signal);\n  }\n}\n\n/**\n * Helper for treating an unknown error as the runner's abort sentinel.\n * Returns true if the value is the AbortError thrown by `sleep` / a\n * downstream `fetch` / etc. Used by the runner's catch block to\n * distinguish cancellation (silent exit) from real bugs (rethrow).\n */\nexport function isAbortError(err: unknown): boolean {\n  return (\n    err instanceof DOMException && err.name === \"AbortError\"\n  );\n}\n","/**\n * lib/motion — the global motion control for gradeui.\n *\n * One choke point for \"should this animate?\". Every animated component\n * (ThreeScene, RivePlayer, VideoPlayer, aura surfaces) asks\n * `useReducedMotion()`, so flipping motion off in one place stills them all.\n *\n * Two independent inputs, ORed together (reduce-only by design — the toggle\n * can ADD restriction but never override a user's OS preference to force\n * motion ON):\n *\n *   1. the OS `prefers-reduced-motion: reduce` media query, and\n *   2. a `data-motion=\"off\"` attribute on the document root (`<html>`),\n *      the manual toggle.\n *\n * The attribute is the same mechanism the renderer's `data-fidelity` flag\n * uses: stamp it on `<html>` and CSS + components react. Inside Studio's\n * Fast Frame / embed / share iframes it is driven over postMessage\n * (`grade:set-motion`), so the toggle reaches into the sandbox where the\n * ThreeScene surfaces actually run. A matching `[data-motion=\"off\"]` CSS\n * reset in `styles/globals.css` covers pure-CSS animation/transition.\n *\n * Sibling to lib/demo (the scripted-reveal spine).\n */\n\nimport * as React from \"react\";\n\n/** The attribute stamped on `<html>` to force motion off. */\nexport const MOTION_ATTR = \"data-motion\";\nconst MOTION_OFF = \"off\";\n\nconst REDUCE_QUERY = \"(prefers-reduced-motion: reduce)\";\n\n/** Read the current effective reduced-motion state (OS query OR the global\n *  toggle). SSR-safe: returns `false` when there's no `window`. */\nfunction readReduced(): boolean {\n  if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n    return false;\n  }\n  const osReduced = window.matchMedia(REDUCE_QUERY).matches;\n  const toggledOff =\n    document.documentElement.getAttribute(MOTION_ATTR) === MOTION_OFF;\n  return osReduced || toggledOff;\n}\n\n/**\n * Returns `true` when motion should be suppressed — either the OS reports\n * `prefers-reduced-motion: reduce`, or the global `data-motion=\"off\"` toggle\n * is set on `<html>`. Stays live: re-reads on media-query change and on the\n * attribute mutating.\n *\n * SSR-safe — defaults to `false` (motion allowed) on the server and\n * rehydrates in an effect, so it never causes a hydration mismatch.\n */\nexport function useReducedMotion(): boolean {\n  const [reduced, setReduced] = React.useState(false);\n\n  React.useEffect(() => {\n    const update = () => setReduced(readReduced());\n    update();\n\n    const mql = window.matchMedia(REDUCE_QUERY);\n    mql.addEventListener(\"change\", update);\n\n    // Watch the toggle attribute on <html>.\n    const observer = new MutationObserver(update);\n    observer.observe(document.documentElement, {\n      attributes: true,\n      attributeFilter: [MOTION_ATTR],\n    });\n\n    return () => {\n      mql.removeEventListener(\"change\", update);\n      observer.disconnect();\n    };\n  }, []);\n\n  return reduced;\n}\n\n/**\n * @deprecated Prefer {@link useReducedMotion}. Kept for back-compat with the\n * components that import it from `media-surface`; it now also folds in the\n * global `data-motion=\"off\"` toggle, not just the OS query.\n */\nexport const usePrefersReducedMotion = useReducedMotion;\n\n/**\n * Returns `true` when the page is actually being watched — the tab is visible\n * AND (for a top-level document) the window is focused. Inside an iframe the\n * focus check is skipped, because an iframe rarely \"has focus\" even when its\n * tab is frontmost; it falls back to visibility, which correctly tracks the\n * top tab. Use it to PAUSE autoplay loops when nobody's looking: a movie stops\n * when you tab away.\n *\n * SSR-safe — defaults to `true` and rehydrates in an effect.\n *\n * Scope, deliberately: this knows about tab visibility + window focus, NOT\n * whether the element is scrolled into view (pair it with an\n * IntersectionObserver — e.g. motion's `useInView` — for that), and NOT\n * whether a cross-document iframe has scrolled off its PARENT's viewport. An\n * iframe can't see the parent's scroll; the parent must observe the host and\n * pause it. See the grid poster/promote policy in STUDIO-CAPTURE.md.\n */\nexport function usePageActive(): boolean {\n  const [active, setActive] = React.useState(true);\n\n  React.useEffect(() => {\n    // window.top identity check is safe cross-origin (no property access).\n    const framed = window.self !== window.top;\n    const compute = () =>\n      document.visibilityState !== \"hidden\" && (framed || document.hasFocus());\n    const update = () => setActive(compute());\n    update();\n\n    document.addEventListener(\"visibilitychange\", update);\n    window.addEventListener(\"focus\", update);\n    window.addEventListener(\"blur\", update);\n    window.addEventListener(\"pageshow\", update);\n\n    return () => {\n      document.removeEventListener(\"visibilitychange\", update);\n      window.removeEventListener(\"focus\", update);\n      window.removeEventListener(\"blur\", update);\n      window.removeEventListener(\"pageshow\", update);\n    };\n  }, []);\n\n  return active;\n}\n\n/**\n * Imperatively set the global motion toggle on `<html>`.\n *\n *   setMotion(false) → stamps `data-motion=\"off\"` (animation suppressed)\n *   setMotion(true)  → removes the attribute (default: respect the OS only)\n *\n * Reduce-only: turning motion \"on\" never forces animation for a viewer whose\n * OS asks for reduced motion — `useReducedMotion()` still honours the query.\n * No-op on the server.\n */\nexport function setMotion(enabled: boolean): void {\n  if (typeof document === \"undefined\") return;\n  const root = document.documentElement;\n  if (enabled) root.removeAttribute(MOTION_ATTR);\n  else root.setAttribute(MOTION_ATTR, MOTION_OFF);\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useInView } from \"motion/react\";\nimport {\n  DEMO_IN_VIEW_AMOUNT,\n  DEMO_SPEED_PRESETS,\n  type DemoSpeed,\n  type DemoTrigger,\n} from \"./types\";\nimport { isAbortError, sleep } from \"./sleep\";\nimport { useReducedMotion, usePageActive } from \"../motion\";\n\n/**\n * useScriptedDemo — the shared step-machine hook behind every\n * scripted-demo surface in gradeui (`<Code>`, `<Composer>`,\n * `<DemoStage>`, anything else that wants the same play / stop / speed\n * semantics).\n *\n * Generic over the Step type. Each consumer defines its own verbs\n * (Code has `output`, Composer has `mention`, DemoStage has `reveal`)\n * and passes an `interpret(step, ctx)` callback that executes one\n * step. The hook owns: sequencing, cancellation, trigger detection\n * (mount / inView / manual), loop, pre-delay, completion signal, and\n * the imperative play() / stop() API.\n *\n * Reduced motion: this hook is the declarative-motion layer's accessibility\n * boundary. When the OS reports `prefers-reduced-motion: reduce` OR the\n * global `data-motion=\"off\"` toggle is set (both via `useReducedMotion`),\n * the runner settles on the FINAL frame instead of animating — zeroed\n * timings run every step instantly and the sequence never loops, so the\n * end state shows and holds. That keeps lib/demo honouring the same motion\n * control as ThreeScene and the CSS reset; without it, a screen built with\n * <Code> would keep typing under reduced motion and would not be accessible.\n *\n * Authoring guide for `interpret`:\n *   - `await sleep(ms, ctx.signal)` for any pause so stop() can short\n *     a long wait cleanly.\n *   - `await typeText(text, onTick, stagger, ctx.signal)` for typing\n *     loops, again so stop() interrupts mid-character. A non-positive\n *     stagger (what the reduced-motion preset supplies) emits the whole\n *     string in one tick, so the final state shows with no animation.\n *   - Read `ctx.speed` to grab the resolved DEMO_SPEED_PRESETS entry\n *     when a step doesn't pin its own cadence.\n *   - Throw nothing on cancel — the helpers do it for you. Anything\n *     else thrown is treated as a real bug and bubbles to the console.\n */\n\nexport interface ScriptedDemoContext {\n  /** Resolved speed preset for the current run. Zeroed under reduced motion. */\n  speed: (typeof DEMO_SPEED_PRESETS)[DemoSpeed];\n  /** AbortSignal that fires on stop() / unmount / steps change. */\n  signal: AbortSignal;\n  /** Live cancellation check for code paths that can't take a signal. */\n  cancelled: () => boolean;\n  /** True when the run is in reduced-motion mode (settling, not animating).\n   *  Most interpreters don't need this — zeroed timings handle it — but a\n   *  step that does something non-timing-based (confetti, sound) can skip it. */\n  reduced: boolean;\n}\n\nexport interface UseScriptedDemoOptions<TStep> {\n  /** The steps to run. Undefined or empty means \"no script\". */\n  steps?: TStep[];\n  /**\n   * Per-step interpreter. Receives one step + a context with timing\n   * helpers, runs whatever the step means, and returns when done.\n   * Synchronous or async — the runner awaits either.\n   */\n  interpret: (step: TStep, ctx: ScriptedDemoContext) => Promise<void> | void;\n  /** Animation feel. Defaults to `normal`. */\n  speed?: DemoSpeed;\n  /** What kicks the script off. Defaults to `mount`. */\n  trigger?: DemoTrigger;\n  /** For `trigger=\"manual\"` — flip true to play. */\n  play?: boolean;\n  /** Loop the sequence forever after completion. Pause length controlled by `loopDelay`. */\n  loop?: boolean;\n  /** Cap the number of loop cycles, then settle and stop. A demo is a movie —\n   *  it shouldn't spin forever. Default Infinity. Grid/embed surfaces set a\n   *  small number so the loop ends instead of running unattended. */\n  maxLoops?: number;\n  /**\n   * Milliseconds to pause between loop cycles. Only applies when\n   * `loop` is true. Defaults to 2000. Marketing surfaces that want\n   * the demo to breathe between repeats bump this to 4000-6000; tight\n   * inline demos drop it to 800.\n   */\n  loopDelay?: number;\n  /** Container ref for inView detection. Required when `trigger=\"inView\"`. */\n  containerRef?: React.RefObject<HTMLElement | null>;\n  /**\n   * Fires when one loop iteration completes (or the whole run, when\n   * `loop` is false). Consumers can use this to reset their buffer or\n   * fire a parent callback.\n   */\n  onComplete?: () => void;\n  /**\n   * Fires before each loop iteration starts (after the 2s pause).\n   * Use to reset per-iteration state in the consumer.\n   */\n  onLoopReset?: () => void;\n}\n\nexport interface ScriptedDemoState {\n  /** True while the runner is actively walking steps. */\n  isPlaying: boolean;\n  /** True after the last step has run (and stays true until reset). */\n  isComplete: boolean;\n  /** 0-indexed pointer to the currently executing step, or -1 idle. */\n  currentIndex: number;\n  /** Imperative trigger. Mostly for `trigger=\"manual\"` ref handles. */\n  play: () => void;\n  /** Cancel the in-flight script. Idempotent. */\n  stop: () => void;\n  /**\n   * One-shot replay. Cancels any in-flight run, then re-plays from\n   * step 0. Pass a delay in ms to schedule the replay (useful for\n   * \"play, finish, breathe, play once more\" cadences without the\n   * commitment of `loop`).\n   */\n  restart: (delayMs?: number) => void;\n}\n\nconst DEFAULT_LOOP_DELAY_MS = 2000;\n\n// Reduced-motion timings — everything instant. `typeText` treats a\n// non-positive stagger as \"emit the whole string in one tick\", so steps\n// complete without the typing animation (or the flicker a 0ms loop causes)\n// and the surface lands on its final frame.\nconst INSTANT_PRESET: (typeof DEMO_SPEED_PRESETS)[DemoSpeed] = {\n  tokenStagger: 0,\n  lineStagger: 0,\n  preDelay: 0,\n  fadeMs: 0,\n};\n\nexport function useScriptedDemo<TStep>(\n  opts: UseScriptedDemoOptions<TStep>,\n): ScriptedDemoState {\n  const {\n    steps,\n    interpret,\n    speed = \"normal\",\n    trigger = \"mount\",\n    play: playProp,\n    loop = false,\n    loopDelay = DEFAULT_LOOP_DELAY_MS,\n    maxLoops = Infinity,\n    containerRef,\n    onComplete,\n    onLoopReset,\n  } = opts;\n\n  // Reduced motion (OS preference OR the global data-motion toggle): settle\n  // on the final frame instead of animating. Zeroed timings + no loop.\n  const reduced = useReducedMotion();\n  const preset = reduced ? INSTANT_PRESET : DEMO_SPEED_PRESETS[speed];\n  const effectiveLoop = reduced ? false : loop;\n\n  // Playback gate part 1 of 2: is the page even being watched? Pauses every\n  // loop when the tab is hidden/unfocused — a movie stops when you tab away.\n  // (Part 2, element-in-view, is wired below once we have the container ref.)\n  const pageActive = usePageActive();\n\n  const [isPlaying, setIsPlaying] = React.useState(false);\n  const [isComplete, setIsComplete] = React.useState(false);\n  const [currentIndex, setCurrentIndex] = React.useState(-1);\n\n  // inView wiring — only active when trigger=\"inView\" AND a container\n  // ref was supplied. amount + once mirror the marketing-hero contract.\n  // `useInView` accepts a nullable ref so we always pass the ref, but\n  // it only fires when actually observable.\n  // motion's typing requires a non-null element ref. We assert via cast\n  // because passing { current: null } is legal at runtime and behaves\n  // as \"never in view\".\n  const noopRef = React.useRef<HTMLElement>(null);\n  const inView = useInView(\n    (containerRef ?? noopRef) as React.RefObject<Element>,\n    {\n      once: true,\n      amount: DEMO_IN_VIEW_AMOUNT,\n    },\n  );\n\n  // Playback gate part 2 of 2: a LIVE in-view (re-fires on leave, unlike the\n  // once-only trigger above). Only meaningful when a container ref is being\n  // observed; with no ref we can't see the element, so we don't gate on it\n  // (tab visibility still applies). Together: pause when nobody's watching.\n  const liveInView = useInView(\n    (containerRef ?? noopRef) as React.RefObject<Element>,\n    { amount: DEMO_IN_VIEW_AMOUNT },\n  );\n  const playbackActive = pageActive && (containerRef ? liveInView : true);\n\n  // Manual play trigger — a counter we bump from the imperative play()\n  // method. The runner effect depends on it so a second play() call\n  // (after stop() or after completion) cleanly re-runs the script.\n  const [manualPlayTick, setManualPlayTick] = React.useState(0);\n\n  const shouldPlay = React.useMemo(() => {\n    if (trigger === \"mount\") return true;\n    if (trigger === \"inView\") return inView;\n    // manual: explicit prop OR imperative play() call (tick > 0)\n    return Boolean(playProp) || manualPlayTick > 0;\n  }, [trigger, inView, playProp, manualPlayTick]);\n\n  // Stable interpret ref so the runner effect doesn't restart on every\n  // render just because the consumer passes an inline function.\n  const interpretRef = React.useRef(interpret);\n  interpretRef.current = interpret;\n\n  const completeRef = React.useRef(onComplete);\n  completeRef.current = onComplete;\n  const loopResetRef = React.useRef(onLoopReset);\n  loopResetRef.current = onLoopReset;\n\n  // Active AbortController so stop() can fire from outside the effect.\n  const controllerRef = React.useRef<AbortController | null>(null);\n  // Pending restart timeout id so a second restart() call supersedes\n  // the first (and unmount cleans up before it fires).\n  const restartTimerRef = React.useRef<number | null>(null);\n\n  const stop = React.useCallback(() => {\n    controllerRef.current?.abort();\n    controllerRef.current = null;\n    if (restartTimerRef.current !== null) {\n      window.clearTimeout(restartTimerRef.current);\n      restartTimerRef.current = null;\n    }\n    setIsPlaying(false);\n  }, []);\n\n  const play = React.useCallback(() => {\n    // For mount/inView triggers, calling play() manually still works\n    // (treated as a manual re-run): we bump the tick and rely on the\n    // effect dependency to restart.\n    setManualPlayTick((n) => n + 1);\n  }, []);\n\n  const restart = React.useCallback((delayMs = 0) => {\n    // Cancel any in-flight run and any pending restart so the new\n    // call is the source of truth.\n    controllerRef.current?.abort();\n    controllerRef.current = null;\n    if (restartTimerRef.current !== null) {\n      window.clearTimeout(restartTimerRef.current);\n      restartTimerRef.current = null;\n    }\n    if (delayMs > 0) {\n      restartTimerRef.current = window.setTimeout(() => {\n        restartTimerRef.current = null;\n        setManualPlayTick((n) => n + 1);\n      }, delayMs);\n      return;\n    }\n    setManualPlayTick((n) => n + 1);\n  }, []);\n\n  // Unmount cleanup for any orphaned restart timeout.\n  React.useEffect(() => {\n    return () => {\n      if (restartTimerRef.current !== null) {\n        window.clearTimeout(restartTimerRef.current);\n        restartTimerRef.current = null;\n      }\n    };\n  }, []);\n\n  React.useEffect(() => {\n    if (!steps || steps.length === 0) return;\n    // Under reduced motion, run once to settle on the final frame even if\n    // the normal trigger (inView / manual) hasn't fired — the content\n    // should be present, just not animated.\n    if (!reduced && (!shouldPlay || !playbackActive)) return;\n\n    const controller = new AbortController();\n    controllerRef.current = controller;\n    const { signal } = controller;\n    const cancelled = () => signal.aborted;\n\n    const ctx: ScriptedDemoContext = { speed: preset, signal, cancelled, reduced };\n\n    let active = true;\n    let cycles = 0;\n\n    const run = async () => {\n      do {\n        setIsPlaying(true);\n        setIsComplete(false);\n        setCurrentIndex(-1);\n\n        try {\n          // Initial pre-delay so the static state is visible for a\n          // beat before the first step fires. Mostly matters for\n          // `trigger=\"inView\"` (the eye needs an anchor) but it's\n          // cheap to apply universally. Zero under reduced motion.\n          await sleep(trigger === \"inView\" ? preset.preDelay : 0, signal);\n\n          for (let i = 0; i < steps.length; i++) {\n            if (signal.aborted) return;\n            setCurrentIndex(i);\n            await interpretRef.current(steps[i], ctx);\n          }\n\n          if (signal.aborted) return;\n          setCurrentIndex(-1);\n          setIsComplete(true);\n          completeRef.current?.();\n\n          cycles += 1;\n          // Stop when not looping, or once we've hit the loop cap — settle on\n          // the final frame and don't spin unattended.\n          if (!effectiveLoop || cycles >= maxLoops) {\n            setIsPlaying(false);\n            return;\n          }\n\n          // Loop: pause (`loopDelay`, default 2000ms) then reset and replay.\n          await sleep(loopDelay, signal);\n          if (signal.aborted) return;\n          loopResetRef.current?.();\n        } catch (err) {\n          if (isAbortError(err)) return;\n          // Re-throw real errors so authoring bugs surface in the\n          // console rather than being swallowed silently.\n          if (process.env.NODE_ENV !== \"production\") {\n            // eslint-disable-next-line no-console\n            console.error(\"[useScriptedDemo] step error:\", err);\n          }\n          setIsPlaying(false);\n          return;\n        }\n      } while (active && effectiveLoop && !signal.aborted);\n    };\n\n    void run();\n\n    return () => {\n      active = false;\n      controller.abort();\n      if (controllerRef.current === controller) controllerRef.current = null;\n    };\n    // `interpret` is intentionally NOT in the dep list — we read it\n    // through interpretRef so the effect doesn't restart on every\n    // render. Same for the completion callbacks. `reduced` IS a dep so\n    // toggling motion re-runs (settles instantly when turned off,\n    // re-animates when turned on).\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [\n    steps,\n    shouldPlay,\n    playbackActive,\n    effectiveLoop,\n    loopDelay,\n    preset,\n    trigger,\n    manualPlayTick,\n    reduced,\n  ]);\n\n  return { isPlaying, isComplete, currentIndex, play, stop, restart };\n}\n","import { type ClassValue, clsx } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs))\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport {\n  LexicalComposer as LexicalRoot,\n  type InitialConfigType,\n} from \"@lexical/react/LexicalComposer\";\nimport { RichTextPlugin } from \"@lexical/react/LexicalRichTextPlugin\";\nimport { PlainTextPlugin } from \"@lexical/react/LexicalPlainTextPlugin\";\nimport { ContentEditable } from \"@lexical/react/LexicalContentEditable\";\nimport { LexicalErrorBoundary } from \"@lexical/react/LexicalErrorBoundary\";\nimport { HistoryPlugin } from \"@lexical/react/LexicalHistoryPlugin\";\nimport { OnChangePlugin } from \"@lexical/react/LexicalOnChangePlugin\";\nimport { ListPlugin } from \"@lexical/react/LexicalListPlugin\";\nimport { LinkPlugin } from \"@lexical/react/LexicalLinkPlugin\";\nimport { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport {\n  HeadingNode,\n  QuoteNode,\n  $createHeadingNode,\n  $createQuoteNode,\n  type HeadingTagType,\n} from \"@lexical/rich-text\";\nimport {\n  ListNode,\n  ListItemNode,\n  INSERT_ORDERED_LIST_COMMAND,\n  INSERT_UNORDERED_LIST_COMMAND,\n} from \"@lexical/list\";\nimport { LinkNode, AutoLinkNode } from \"@lexical/link\";\nimport { CodeNode, CodeHighlightNode } from \"@lexical/code\";\nimport { $setBlocksType } from \"@lexical/selection\";\nimport { mergeRegister } from \"@lexical/utils\";\nimport {\n  $getRoot,\n  $getSelection,\n  $createParagraphNode,\n  $createTextNode,\n  $isRangeSelection,\n  FORMAT_TEXT_COMMAND,\n  KEY_ENTER_COMMAND,\n  CLEAR_EDITOR_COMMAND,\n  COMMAND_PRIORITY_HIGH,\n  type TextFormatType,\n  type LexicalEditor,\n  type LexicalNode,\n  type TextNode,\n} from \"lexical\";\nimport {\n  BeautifulMentionsPlugin,\n  BeautifulMentionNode,\n  $createBeautifulMentionNode,\n  type BeautifulMentionsItem,\n} from \"lexical-beautiful-mentions\";\nimport { AnimatePresence, motion } from \"motion/react\";\nimport {\n  Bold,\n  Italic,\n  Underline,\n  Strikethrough,\n  Code as CodeIcon,\n  Heading1,\n  Heading2,\n  Heading3,\n  Quote,\n  List as ListIcon,\n  ListOrdered,\n  Paperclip,\n  Send,\n  Square,\n  X,\n} from \"lucide-react\";\nimport {\n  useScriptedDemo,\n  BlinkingCursor,\n  sleep,\n  typeText,\n  type DemoSpeed,\n  type DemoTrigger,\n} from \"@/lib/demo\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * Composer — the generic text composition surface for the design system.\n *\n * The answer wherever a user is composing a message: AI chat input,\n * comment thread reply, post-body editor, future copilot panels.\n * Replaces the textarea-with-buttons pattern that hosts kept rolling\n * by hand.\n *\n * Built on Lexical (Meta's React-first editor framework) so it can do:\n *   - rich text formatting (B / I / U / S / code, headings, blockquote,\n *     pullquote, lists)\n *   - mentions and slash commands via lexical-beautiful-mentions, with\n *     a typeahead popover and theme-able tokens\n *   - image attachments (paperclip + clipboard paste) when opted in\n *   - scripted demo playback for marketing surfaces (types text, opens\n *     mention popovers, applies formatting, all via the same step\n *     vocabulary as <Code>)\n *\n * Slot-based composition for the action row: hosts that need custom\n * affordances (template picker, voice button, attach-document) pass\n * `leftActions` / `rightActions`. Default Send / Stop / paperclip\n * render only when the host hasn't replaced the slot.\n *\n * Hosts that want the canned \"chat composer with paperclip + send\"\n * preset should reach for `<AIChatComposer>` instead, which configures\n * this Composer with the right slots wired up.\n *\n * Scripted demos: same `speed` / `trigger` / `play` / `loop` vocabulary\n * as `<Code>`, sharing the underlying `useScriptedDemo` hook from\n * `lib/demo/`. The Composer adds its own verbs (`mention`, `format`,\n * `select`, `submit`) on top of the universal `type` / `wait` / `clear`.\n */\n\n// ─── Public types ────────────────────────────────────────────────────\n\nexport type ComposerFormat =\n  | \"bold\"\n  | \"italic\"\n  | \"underline\"\n  | \"strikethrough\"\n  | \"code\"\n  | \"h1\"\n  | \"h2\"\n  | \"h3\"\n  | \"blockquote\"\n  | \"pullquote\"\n  | \"ul\"\n  | \"ol\";\n\nexport interface ComposerMentionItem {\n  id: string;\n  /** Display value (without the trigger char). */\n  value: string;\n  /** Optional secondary label shown in the suggester. */\n  label?: string;\n  /** Avatar URL or initials for the suggester row. */\n  avatar?: string;\n  /** Arbitrary payload the host can attach to the mention. */\n  data?: Record<string, unknown>;\n}\n\nexport interface ComposerTriggerConfig {\n  /** The trigger character, eg. \"@\" or \"/\". */\n  char: string;\n  /**\n   * Items to populate the suggester. Either a static array or a\n   * resolver function (sync or async) that receives the typed query.\n   * The plugin filters automatically when items is an array.\n   */\n  items:\n    | ComposerMentionItem[]\n    | ((query: string) => ComposerMentionItem[] | Promise<ComposerMentionItem[]>);\n  /**\n   * Whether to strip the trigger char on insert. Defaults: keep for\n   * \"@\" (mentions read as \"@alice\"), strip for \"/\" (commands read as\n   * \"Insert image\" not \"/insert-image\").\n   */\n  stripTrigger?: boolean;\n}\n\nexport interface ComposerAttachmentConfig {\n  /** Master enable. Set true on the prop to use defaults, or pass a config object. */\n  enabled?: boolean;\n  /** HTML accept attribute on the file input. Default \"image/*\". */\n  accept?: string;\n  /** Max number of attachments. Default 10. */\n  maxItems?: number;\n  /** Allow multiple selection in the file picker. Default true. */\n  multiple?: boolean;\n}\n\nexport interface ComposerAttachment {\n  id: string;\n  file: File;\n  /** Object URL owned by the composer. Hosts must NOT revoke it. */\n  previewUrl: string;\n  name: string;\n}\n\nexport interface ComposerContent {\n  /** Plain text representation of the editor contents (whitespace preserved). */\n  text: string;\n  /** Lexical editor state serialised to JSON (for round-trip persistence). */\n  json: string;\n  /** Resolved mention tokens in document order. */\n  mentions: Array<{ trigger: string; value: string; data?: Record<string, unknown> }>;\n}\n\nexport interface ComposerHandle {\n  /** Run a demo script imperatively (vs. via `steps` + `trigger=\"manual\"`). */\n  play: (steps: ComposerStep[]) => void;\n  /** Cancel an in-flight demo. Idempotent. */\n  stop: () => void;\n  /**\n   * One-shot replay of the configured `steps`. Cancels any in-flight\n   * run, clears the editor, replays from step 0. Pass a delay (ms) to\n   * schedule the replay (useful for chaining demos). Requires `steps`\n   * to be configured.\n   */\n  restart: (delayMs?: number) => void;\n  /** Move focus into the editor. */\n  focus: () => void;\n  /** Wipe the editor. */\n  clear: () => void;\n  /** Insert plain text at the current selection. */\n  insert: (text: string) => void;\n  /** Snapshot the current content + mentions. */\n  getContent: () => ComposerContent;\n  /** Direct access to the underlying Lexical editor (escape hatch). */\n  getEditor: () => LexicalEditor | null;\n}\n\n/**\n * Demo step vocabulary for Composer scripts. Shares `type` / `wait` /\n * `clear` with the universal `lib/demo` verbs; adds composer-specific\n * `mention`, `format`, `select`, `newline`, `submit` on top.\n */\nexport type ComposerStep =\n  | { type: \"type\"; text: string; speed?: DemoSpeed }\n  | { type: \"wait\"; ms: number }\n  | { type: \"clear\" }\n  | { type: \"newline\" }\n  | { type: \"submit\" }\n  | {\n      type: \"mention\";\n      /** Trigger char (must match a registered ComposerTriggerConfig.char). */\n      trigger: string;\n      /** Value to insert (without the trigger). Looks up the matching item by `value`. */\n      value: string;\n      /** Optional pre-typed query — the demo types this after the trigger char before \"selecting\" the value, to show the typeahead in action. */\n      query?: string;\n    }\n  | { type: \"format\"; format: ComposerFormat }\n  | {\n      type: \"select\";\n      /** Substring to find and select. First match wins. */\n      text: string;\n    };\n\nexport interface ComposerProps {\n  /** Placeholder copy shown when empty. */\n  placeholder?: string;\n  /** Initial plain text content. For richer initial state, use `initialJson`. */\n  initialText?: string;\n  /** Initial Lexical state JSON (from a previous `onSubmit` round-trip). */\n  initialJson?: string;\n  /**\n   * Available formats. Pass false to disable rich text entirely\n   * (plain text mode, half the bundle weight, no toolbar). Default\n   * enables a sensible set for most chat / comment surfaces.\n   */\n  formats?: ComposerFormat[] | false;\n  /**\n   * Render the formatting toolbar. Default false because most uses\n   * are short-form. Set true (or \"top\") to show the toolbar above the\n   * editor. \"floating\" is planned; not yet implemented.\n   */\n  toolbar?: boolean | \"top\";\n  /**\n   * Mention / slash command configs. Each entry registers one trigger\n   * char and its items. Pass `[{ char: \"@\", items: people }, { char: \"/\", items: commands }]`\n   * for the common chat-app setup.\n   */\n  triggers?: ComposerTriggerConfig[];\n  /**\n   * Image attachments (paperclip + clipboard paste). Pass true for\n   * defaults, an object to customise, or omit/false to skip the\n   * attachment plumbing entirely.\n   */\n  attachments?: boolean | ComposerAttachmentConfig;\n  /** Fires when the user submits (Enter, click Send, or scripted `submit` step). */\n  onSubmit?: (content: ComposerContent, attachments?: ComposerAttachment[]) => void;\n  /**\n   * Fires on every editor change with the current plain text. Use for\n   * length validation, controlled-value bridges (eg. AIChatComposer\n   * forwarding to a host's `value`/`onChange` pair), or live preview\n   * surfaces. Cheap, called frequently; debounce if you need the\n   * Lexical state JSON (use `getContent()` via ref instead).\n   */\n  onChange?: (text: string) => void;\n  /**\n   * Loading state — disables the editor + paperclip and swaps the\n   * default Send button for Stop. Has no effect when `rightActions`\n   * overrides the default Send.\n   */\n  isLoading?: boolean;\n  /** Stop handler — required for Stop to be active when loading. */\n  onStop?: () => void;\n  /** Hard character cap. */\n  maxLength?: number;\n  /** Auto-focus the editor on mount. Default false. */\n  autoFocus?: boolean;\n  /** Whether Enter submits. Default true (Shift-Enter still inserts a newline). */\n  submitOnEnter?: boolean;\n  /**\n   * Custom content for the left action slot. Replaces the default\n   * paperclip button when `attachments` is enabled.\n   */\n  leftActions?: React.ReactNode;\n  /**\n   * Custom content for the right action slot. Replaces the default\n   * Send / Stop button. Use the `useComposer()` hook inside to access\n   * imperative methods.\n   */\n  rightActions?: React.ReactNode;\n  /** Hide the default Send button without replacing it. */\n  hideSend?: boolean;\n  /** Scripted demo steps. */\n  steps?: ComposerStep[];\n  /** What kicks the script off. Defaults to \"mount\". */\n  trigger?: DemoTrigger;\n  /** For trigger=\"manual\" — flip true to play. */\n  play?: boolean;\n  /** Animation feel. */\n  speed?: DemoSpeed;\n  /** Loop the script forever. */\n  loop?: boolean;\n  /**\n   * Pause between loop iterations (ms). Defaults to 2000. Marketing\n   * heroes that want the demo to breathe between repeats bump this\n   * higher; tight inline demos drop it.\n   */\n  loopDelay?: number;\n  /**\n   * Fires once per loop cycle, AFTER the loopDelay pause and BEFORE\n   * the script replays. Use to reset parent state that the script\n   * mutated via onSubmit (e.g., wipe a messages list back to its\n   * seed before the demo types into it again). The editor is cleared\n   * automatically — you only need this hook if state outside the\n   * Composer needs to reset too.\n   */\n  onLoopReset?: () => void;\n  /**\n   * Bare mode — strip the card chrome (border / bg / rounding). Use\n   * when embedding inside an existing card or column layout.\n   */\n  bare?: boolean;\n  /**\n   * Read-only mode — disables editing AND focusability. Programmatic\n   * updates (including scripted demo playback) still work. Use for\n   * marketing surfaces that render a Composer purely for show, so the\n   * scripted typing doesn't steal focus from other inputs on the page.\n   * Hides the default Send / paperclip action row.\n   */\n  readOnly?: boolean;\n  className?: string;\n}\n\n// ─── Plugins (internal) ──────────────────────────────────────────────\n\n/**\n * SubmitPlugin — wires Enter to submit (Shift-Enter still inserts a\n * newline). Captures the editor command before the rich-text plugin\n * inserts a paragraph break.\n */\nfunction SubmitPlugin({\n  onSubmit,\n  enabled,\n}: {\n  onSubmit: () => void;\n  enabled: boolean;\n}) {\n  const [editor] = useLexicalComposerContext();\n\n  React.useEffect(() => {\n    if (!enabled) return;\n    return editor.registerCommand(\n      KEY_ENTER_COMMAND,\n      (event) => {\n        if (event && (event as KeyboardEvent).shiftKey) return false;\n        event?.preventDefault();\n        onSubmit();\n        return true;\n      },\n      COMMAND_PRIORITY_HIGH,\n    );\n  }, [editor, enabled, onSubmit]);\n\n  return null;\n}\n\n/**\n * PastePlugin — intercepts clipboard image pastes when attachments\n * are enabled, routes them to the attachment intake instead of\n * letting Lexical try to insert them as nodes.\n */\nfunction PastePlugin({\n  onImageFiles,\n  enabled,\n}: {\n  onImageFiles: (files: File[]) => void;\n  enabled: boolean;\n}) {\n  const [editor] = useLexicalComposerContext();\n\n  React.useEffect(() => {\n    if (!enabled) return;\n    const rootElement = editor.getRootElement();\n    if (!rootElement) return;\n    const handler = (e: ClipboardEvent) => {\n      const items = Array.from(e.clipboardData?.items ?? []);\n      const imageFiles = items\n        .filter((it) => it.kind === \"file\" && it.type.startsWith(\"image/\"))\n        .map((it) => it.getAsFile())\n        .filter((f): f is File => f !== null);\n      if (imageFiles.length > 0) {\n        e.preventDefault();\n        onImageFiles(imageFiles);\n      }\n    };\n    rootElement.addEventListener(\"paste\", handler);\n    return () => rootElement.removeEventListener(\"paste\", handler);\n  }, [editor, enabled, onImageFiles]);\n\n  return null;\n}\n\n/**\n * AutoFocusPlugin — focus the editor on mount when `autoFocus` is on.\n */\nfunction AutoFocusPlugin({ enabled }: { enabled: boolean }) {\n  const [editor] = useLexicalComposerContext();\n  React.useEffect(() => {\n    if (!enabled) return;\n    editor.focus();\n  }, [editor, enabled]);\n  return null;\n}\n\n/**\n * RefBridgePlugin — exposes the LexicalEditor instance up to the\n * outer forwardRef so ComposerHandle methods can drive it.\n */\nfunction RefBridgePlugin({\n  onEditor,\n}: {\n  onEditor: (editor: LexicalEditor) => void;\n}) {\n  const [editor] = useLexicalComposerContext();\n  React.useEffect(() => {\n    onEditor(editor);\n  }, [editor, onEditor]);\n  return null;\n}\n\n// ─── Demo step interpreter ───────────────────────────────────────────\n\nconst FORMAT_TEXT_KEYS: Partial<Record<ComposerFormat, TextFormatType>> = {\n  bold: \"bold\",\n  italic: \"italic\",\n  underline: \"underline\",\n  strikethrough: \"strikethrough\",\n  code: \"code\",\n};\n\n/**\n * Apply a block-level format to the current selection. Splits text\n * vs. block formats internally.\n */\nfunction applyFormat(editor: LexicalEditor, format: ComposerFormat) {\n  const textKey = FORMAT_TEXT_KEYS[format];\n  if (textKey) {\n    editor.dispatchCommand(FORMAT_TEXT_COMMAND, textKey);\n    return;\n  }\n  if (format === \"ul\") {\n    editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, undefined);\n    return;\n  }\n  if (format === \"ol\") {\n    editor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND, undefined);\n    return;\n  }\n  // Block-level conversions go through $setBlocksType inside an update.\n  editor.update(() => {\n    const selection = $getSelection();\n    if (!$isRangeSelection(selection)) return;\n    if (format === \"h1\" || format === \"h2\" || format === \"h3\") {\n      const tag = format as HeadingTagType;\n      $setBlocksType(selection, () => $createHeadingNode(tag));\n      return;\n    }\n    if (format === \"blockquote\" || format === \"pullquote\") {\n      // Pullquote is a styled variant of blockquote: same node, plus a\n      // data attribute the CSS targets. Lexical doesn't ship a custom\n      // attribute API per quote, so we tag the DOM via theme below\n      // and use a separate $createPullquoteNode if/when this graduates\n      // into a custom node type.\n      $setBlocksType(selection, () => {\n        const node = $createQuoteNode();\n        if (format === \"pullquote\") {\n          // Best-effort marker so the theme can style it differently.\n          // Stored on the element via a getter override later if we\n          // need it round-trippable.\n          (node as unknown as { __pullquote?: boolean }).__pullquote = true;\n        }\n        return node;\n      });\n    }\n  });\n}\n\n/**\n * Insert a beautiful-mention node programmatically at the current\n * selection. Used by demo `mention` steps and the imperative\n * ComposerHandle.insert flow.\n */\nfunction insertMentionNode(\n  editor: LexicalEditor,\n  trigger: string,\n  value: string,\n  data?: Record<string, unknown>,\n) {\n  editor.update(() => {\n    const selection = $getSelection();\n    if (!$isRangeSelection(selection)) return;\n    // BeautifulMentionsItemData restricts to primitives (string | number\n    // | boolean | null). Our public `data` is wider (Record<string,\n    // unknown>) so callers can pass through richer payloads — but at the\n    // node-creation boundary we cast since the plugin will JSON-roundtrip\n    // it and anything non-primitive would silently drop anyway.\n    const node = $createBeautifulMentionNode(\n      trigger,\n      value,\n      data as Record<string, string | boolean | number | null> | undefined,\n    );\n    selection.insertNodes([node]);\n    // Insert a trailing space so the caret sits ready for the next\n    // word rather than glued to the mention pill.\n    selection.insertText(\" \");\n  });\n}\n\n/**\n * Find a substring in the editor's plain text and set the selection\n * to it. Returns true on success, false if not found.\n */\nfunction selectSubstring(editor: LexicalEditor, needle: string): boolean {\n  let found = false;\n  editor.update(() => {\n    const root = $getRoot();\n    const fullText = root.getTextContent();\n    const idx = fullText.indexOf(needle);\n    if (idx === -1) return;\n    // Walk the text nodes to translate idx -> (node, offset).\n    let cursor = 0;\n    let startNode: TextNode | null = null;\n    let startOffset = 0;\n    let endNode: TextNode | null = null;\n    let endOffset = 0;\n    const walk = (node: LexicalNode) => {\n      if (startNode && endNode) return;\n      if (node.getType() === \"text\") {\n        const tn = node as TextNode;\n        const len = tn.getTextContentSize();\n        const nodeStart = cursor;\n        const nodeEnd = cursor + len;\n        if (!startNode && idx >= nodeStart && idx < nodeEnd) {\n          startNode = tn;\n          startOffset = idx - nodeStart;\n        }\n        const endIdx = idx + needle.length;\n        if (!endNode && endIdx > nodeStart && endIdx <= nodeEnd) {\n          endNode = tn;\n          endOffset = endIdx - nodeStart;\n        }\n        cursor = nodeEnd;\n        return;\n      }\n      if (\"getChildren\" in node) {\n        for (const child of (node as unknown as { getChildren: () => LexicalNode[] }).getChildren()) {\n          walk(child);\n        }\n      } else {\n        cursor += node.getTextContentSize();\n      }\n    };\n    walk(root);\n    // Cast inside the truthy branch. TS's control-flow analysis of a\n    // let-with-`null`-initial mutated inside a closure narrows the\n    // truthy-branch type to `never` (closure mutations aren't tracked\n    // for flow purposes). The `as TextNode` tells TS we know better —\n    // walk() either populated both or returned early.\n    if (startNode && endNode) {\n      const sn = startNode as TextNode;\n      const en = endNode as TextNode;\n      const selection = sn.select(startOffset, 0);\n      selection.focus.set(en.getKey(), endOffset, \"text\");\n      found = true;\n    }\n  });\n  return found;\n}\n\n/**\n * Wipe the editor contents and leave a fresh empty paragraph with\n * a collapsed selection. Used wherever we need to \"reset to empty\" —\n * after submit, on imperative clear(), on loop reset, on restart().\n *\n * Why not `dispatchCommand(CLEAR_EDITOR_COMMAND)`: that command is\n * registered by RichTextPlugin / PlainTextPlugin and short-circuits\n * silently in some editor states (notably readOnly mode, but also\n * observed in some controlled flows). Explicit root mutation always\n * works because it bypasses the command layer entirely.\n */\nfunction clearEditor(editor: LexicalEditor) {\n  editor.update(() => {\n    const root = $getRoot();\n    root.clear();\n    const para = $createParagraphNode();\n    root.append(para);\n    para.select();\n  });\n}\n\n/**\n * Snapshot the current editor content into the ComposerContent shape\n * exposed to onSubmit and via ComposerHandle.getContent.\n */\nfunction snapshotContent(editor: LexicalEditor): ComposerContent {\n  let text = \"\";\n  let json = \"\";\n  const mentions: ComposerContent[\"mentions\"] = [];\n  editor.getEditorState().read(() => {\n    text = $getRoot().getTextContent();\n    json = JSON.stringify(editor.getEditorState().toJSON());\n    const walk = (node: LexicalNode) => {\n      if (node.getType() === \"beautifulMention\") {\n        const m = node as unknown as {\n          getTrigger: () => string;\n          getValue: () => string;\n          getData: () => Record<string, unknown> | undefined;\n        };\n        mentions.push({\n          trigger: m.getTrigger(),\n          value: m.getValue(),\n          data: m.getData(),\n        });\n      }\n      if (\"getChildren\" in node) {\n        for (const child of (node as unknown as { getChildren: () => LexicalNode[] }).getChildren()) {\n          walk(child);\n        }\n      }\n    };\n    walk($getRoot());\n  });\n  return { text, json, mentions };\n}\n\n// ─── Toolbar ─────────────────────────────────────────────────────────\n\nconst FORMAT_BUTTONS: Array<{\n  format: ComposerFormat;\n  icon: React.ComponentType<{ className?: string }>;\n  label: string;\n}> = [\n  { format: \"bold\", icon: Bold, label: \"Bold\" },\n  { format: \"italic\", icon: Italic, label: \"Italic\" },\n  { format: \"underline\", icon: Underline, label: \"Underline\" },\n  { format: \"strikethrough\", icon: Strikethrough, label: \"Strikethrough\" },\n  { format: \"code\", icon: CodeIcon, label: \"Inline code\" },\n  { format: \"h1\", icon: Heading1, label: \"Heading 1\" },\n  { format: \"h2\", icon: Heading2, label: \"Heading 2\" },\n  { format: \"h3\", icon: Heading3, label: \"Heading 3\" },\n  { format: \"blockquote\", icon: Quote, label: \"Blockquote\" },\n  { format: \"ul\", icon: ListIcon, label: \"Bulleted list\" },\n  { format: \"ol\", icon: ListOrdered, label: \"Numbered list\" },\n];\n\nfunction ComposerToolbar({ formats }: { formats: ComposerFormat[] }) {\n  const [editor] = useLexicalComposerContext();\n  const [activeFormats, setActiveFormats] = React.useState<Set<TextFormatType>>(\n    () => new Set(),\n  );\n\n  React.useEffect(() => {\n    return mergeRegister(\n      editor.registerUpdateListener(({ editorState }) => {\n        editorState.read(() => {\n          const selection = $getSelection();\n          if (!$isRangeSelection(selection)) return;\n          const next = new Set<TextFormatType>();\n          for (const fmt of [\"bold\", \"italic\", \"underline\", \"strikethrough\", \"code\"] as TextFormatType[]) {\n            if (selection.hasFormat(fmt)) next.add(fmt);\n          }\n          setActiveFormats(next);\n        });\n      }),\n    );\n  }, [editor]);\n\n  const visible = FORMAT_BUTTONS.filter((b) => formats.includes(b.format));\n\n  return (\n    <div\n      data-gds-part=\"composer-toolbar\"\n      className={cn(\n        \"flex flex-wrap items-center gap-0.5\",\n        // No bottom border — the toolbar sits inside the same card\n        // as the editor and the action row. Internal dividers were\n        // reading as too many seams; let the surface flow as one.\n        \"px-2 py-1\",\n      )}\n    >\n      {visible.map(({ format, icon: Icon, label }) => {\n        const textKey = FORMAT_TEXT_KEYS[format];\n        const isActive = textKey ? activeFormats.has(textKey) : false;\n        return (\n          <button\n            key={format}\n            type=\"button\"\n            aria-label={label}\n            title={label}\n            data-gds-part=\"composer-toolbar-button\"\n            data-gds-active={isActive ? \"true\" : \"false\"}\n            onClick={() => applyFormat(editor, format)}\n            className={cn(\n              \"h-7 w-7 inline-flex items-center justify-center rounded\",\n              \"text-[var(--gds-composer-toolbar-fg)]\",\n              \"hover:bg-[var(--gds-composer-toolbar-hover-bg)]\",\n              \"focus:outline-none focus:ring-2 focus:ring-primary\",\n              isActive && \"bg-[var(--gds-composer-toolbar-active-bg)] text-[var(--gds-composer-toolbar-active-fg)]\",\n            )}\n          >\n            <Icon className=\"h-3.5 w-3.5\" />\n          </button>\n        );\n      })}\n    </div>\n  );\n}\n\n// ─── Attachment chip row ─────────────────────────────────────────────\n\nfunction AttachmentChips({\n  attachments,\n  onRemove,\n}: {\n  attachments: ComposerAttachment[];\n  onRemove: (id: string) => void;\n}) {\n  return (\n    <AnimatePresence initial={false}>\n      {attachments.length > 0 && (\n        <motion.div\n          initial={{ opacity: 0, height: 0 }}\n          animate={{ opacity: 1, height: \"auto\" }}\n          exit={{ opacity: 0, height: 0 }}\n          transition={{ duration: 0.18 }}\n          className=\"overflow-hidden\"\n        >\n          <div\n            data-gds-part=\"composer-attachments\"\n            className=\"flex flex-wrap gap-2 px-2 py-2\"\n          >\n            {attachments.map((att) => (\n              <div key={att.id} className=\"relative group\">\n                {att.file.type.startsWith(\"image/\") ? (\n                  // eslint-disable-next-line @next/next/no-img-element\n                  <img\n                    src={att.previewUrl}\n                    alt={att.name}\n                    className=\"h-14 w-14 rounded-md object-cover border border-[var(--gds-composer-border)]\"\n                  />\n                ) : (\n                  <div className=\"h-14 w-14 rounded-md border border-[var(--gds-composer-border)] flex items-center justify-center text-xs px-1 text-center text-[var(--gds-composer-muted-fg)]\">\n                    {att.name.slice(0, 18)}\n                  </div>\n                )}\n                <button\n                  type=\"button\"\n                  onClick={() => onRemove(att.id)}\n                  aria-label={`Remove ${att.name}`}\n                  className={cn(\n                    \"absolute -top-1.5 -right-1.5 h-5 w-5 rounded-full\",\n                    \"bg-[var(--gds-composer-chip-remove-bg)]\",\n                    \"text-[var(--gds-composer-chip-remove-fg)]\",\n                    \"flex items-center justify-center\",\n                    \"opacity-0 group-hover:opacity-100 focus:opacity-100\",\n                    \"focus:outline-none focus:ring-2 focus:ring-primary\",\n                    \"transition-opacity\",\n                  )}\n                >\n                  <X className=\"w-3 h-3\" />\n                </button>\n              </div>\n            ))}\n          </div>\n        </motion.div>\n      )}\n    </AnimatePresence>\n  );\n}\n\n// ─── Inner component (inside LexicalRoot) ────────────────────────────\n\ninterface InnerProps extends Omit<\n  ComposerProps,\n  \"initialJson\" | \"initialText\" | \"readOnly\" | \"attachments\"\n> {\n  readOnly: boolean;\n  /**\n   * Bridge for the outer forwardRef to surface useScriptedDemo's\n   * `restart()` on ComposerHandle. The inner component populates\n   * this; the outer reads from it inside useImperativeHandle.\n   */\n  restartRef: React.MutableRefObject<(delayMs?: number) => void>;\n  /** Resolved + normalised. */\n  triggers: ComposerTriggerConfig[];\n  attachmentsCfg: Required<ComposerAttachmentConfig> | null;\n  formatList: ComposerFormat[];\n  showToolbar: boolean;\n  handleEditorReady: (editor: LexicalEditor) => void;\n  /** Used by the demo player to drive submission. */\n  submitRef: React.MutableRefObject<() => void>;\n  /** Internal attachment state lifted up so the rightActions Send\n   *  button + the demo's submit step both see it. */\n  attachments: ComposerAttachment[];\n  setAttachments: React.Dispatch<React.SetStateAction<ComposerAttachment[]>>;\n  /** Image intake callback (shared by paperclip + paste). */\n  ingestImages: (files: File[]) => void;\n}\n\nfunction ComposerInner({\n  placeholder,\n  triggers,\n  attachmentsCfg,\n  formatList,\n  showToolbar,\n  isLoading,\n  onStop,\n  maxLength,\n  autoFocus,\n  submitOnEnter = true,\n  leftActions,\n  rightActions,\n  hideSend,\n  steps,\n  trigger = \"mount\",\n  play,\n  speed = \"normal\",\n  loop = false,\n  loopDelay,\n  onLoopReset,\n  bare,\n  className,\n  onSubmit,\n  onChange,\n  readOnly,\n  handleEditorReady,\n  submitRef,\n  restartRef,\n  attachments,\n  setAttachments,\n  ingestImages,\n}: InnerProps) {\n  const [editor] = useLexicalComposerContext();\n  const containerRef = React.useRef<HTMLDivElement>(null);\n  const fileInputRef = React.useRef<HTMLInputElement>(null);\n\n  // Send-button enable state — driven by editor content.\n  const [hasContent, setHasContent] = React.useState(false);\n\n  React.useEffect(() => {\n    handleEditorReady(editor);\n  }, [editor, handleEditorReady]);\n\n  // Read-only toggle — flips Lexical's editable flag. The contenteditable\n  // element becomes non-focusable so a scripted demo running in this\n  // composer can't steal focus from other inputs on the page.\n  // editor.update() still applies, so demo playback continues to work.\n  React.useEffect(() => {\n    editor.setEditable(!readOnly);\n  }, [editor, readOnly]);\n\n  // Stable ref for onChange so the update listener doesn't re-register\n  // every render. Read via ref so inline arrow functions don't churn it.\n  const onChangeRef = React.useRef(onChange);\n  onChangeRef.current = onChange;\n\n  React.useEffect(() => {\n    return editor.registerUpdateListener(({ editorState }) => {\n      editorState.read(() => {\n        const text = $getRoot().getTextContent();\n        setHasContent(text.trim().length > 0);\n        onChangeRef.current?.(text);\n      });\n    });\n  }, [editor]);\n\n  // ── Submit handler — shared by Enter, Send button, demo \"submit\" step.\n  const handleSubmit = React.useCallback(() => {\n    if (isLoading) return;\n    const content = snapshotContent(editor);\n    const hasText = content.text.trim().length > 0;\n    const hasAttachments = attachments.length > 0;\n    if (!hasText && !hasAttachments) return;\n    onSubmit?.(content, hasAttachments ? attachments : undefined);\n    // Reset for the next message. Explicit root.clear() rather than\n    // CLEAR_EDITOR_COMMAND so it works reliably in every state\n    // (readOnly, controlled-value flows, etc) — the command path\n    // short-circuits silently in some configurations.\n    clearEditor(editor);\n    attachments.forEach((a) => URL.revokeObjectURL(a.previewUrl));\n    setAttachments([]);\n  }, [editor, isLoading, attachments, onSubmit, setAttachments]);\n\n  // Keep submitRef pointing at the latest closure for the demo player.\n  submitRef.current = handleSubmit;\n\n  // ── Demo player ────────────────────────────────────────────────────\n  //\n  // The interpret callback runs Lexical updates per step. Typing\n  // splits into per-character updates so the user sees the text\n  // appear; mention inserts the node directly (we could open the\n  // popover UI, but for v1 keep it simple); format dispatches the\n  // matching command; submit calls handleSubmit; select walks text\n  // nodes to set a range.\n  const { restart: demoRestart } = useScriptedDemo<ComposerStep>({\n    steps,\n    speed,\n    trigger,\n    play,\n    loop,\n    loopDelay,\n    containerRef,\n    onLoopReset: () => {\n      clearEditor(editor);\n      // Consumer-provided callback fires after the editor clear so\n      // any external state (parent messages list, etc.) resets in\n      // the same tick before the script replays.\n      onLoopReset?.();\n    },\n    interpret: async (step, ctx) => {\n      const signal = ctx.signal;\n      if (step.type === \"wait\") return sleep(step.ms, signal);\n      if (step.type === \"clear\") {\n        clearEditor(editor);\n        return sleep(120, signal);\n      }\n      if (step.type === \"newline\") {\n        editor.update(() => {\n          const selection = $getSelection();\n          if (!$isRangeSelection(selection)) return;\n          selection.insertParagraph();\n        });\n        return sleep(60, signal);\n      }\n      if (step.type === \"submit\") {\n        handleSubmit();\n        return sleep(120, signal);\n      }\n      if (step.type === \"format\") {\n        applyFormat(editor, step.format);\n        // Collapse the selection to its end so the next type step\n        // appends instead of replacing the (still-selected) text.\n        // Without this, scripted \"select word → format italic → type\n        // more\" sequences would have the new typing wipe out the\n        // formatted word. Lexical selection has no direct collapse\n        // method, but anchor.set() with the focus's key/offset/type\n        // produces a zero-width range at the end of the selection.\n        editor.update(() => {\n          const sel = $getSelection();\n          if (!$isRangeSelection(sel) || sel.isCollapsed()) return;\n          const focus = sel.focus;\n          sel.anchor.set(focus.key, focus.offset, focus.type);\n        });\n        return sleep(80, signal);\n      }\n      if (step.type === \"select\") {\n        selectSubstring(editor, step.text);\n        return sleep(120, signal);\n      }\n      if (step.type === \"mention\") {\n        // Optional pre-typed query — shows the typeahead in flight.\n        if (step.query) {\n          editor.update(() => {\n            const sel = $getSelection();\n            if ($isRangeSelection(sel)) sel.insertText(step.trigger);\n          });\n          await typeText(\n            step.query,\n            (partial) => {\n              editor.update(() => {\n                const root = $getRoot();\n                const text = root.getTextContent();\n                // Replace the trailing trigger + partial query each tick.\n                const head = text.slice(0, text.length - step.trigger.length - (partial.length - 1));\n                root.clear();\n                const p = $createParagraphNode();\n                if (head) p.append($createTextNode(head));\n                p.append($createTextNode(step.trigger + partial));\n                root.append(p);\n              });\n            },\n            ctx.speed.tokenStagger,\n            signal,\n          );\n          await sleep(180, signal);\n          // Now strip the trigger + query and insert the mention.\n          editor.update(() => {\n            const root = $getRoot();\n            const text = root.getTextContent();\n            const stripLen = step.trigger.length + step.query!.length;\n            const head = text.slice(0, text.length - stripLen);\n            root.clear();\n            const p = $createParagraphNode();\n            if (head) p.append($createTextNode(head));\n            root.append(p);\n            p.select();\n          });\n        }\n        insertMentionNode(editor, step.trigger, step.value);\n        return sleep(120, signal);\n      }\n      // type — append one char per tick. Lexical wants its own update\n      // transaction per insert, so we can't use typeText() directly\n      // (that's a setter-style API for components that own a string\n      // buffer). Run our own cancellable loop here instead.\n      const stagger = step.speed\n        ? { slow: 70, normal: 22, fast: 8 }[step.speed]\n        : ctx.speed.tokenStagger;\n      for (let i = 0; i < step.text.length; i++) {\n        if (signal.aborted) return;\n        const char = step.text[i];\n        editor.update(() => {\n          const selection = $getSelection();\n          if (!$isRangeSelection(selection)) {\n            // No active selection (eg. initial mount before the user\n            // focused). Anchor at the end of the doc so the demo can\n            // still type.\n            $getRoot().selectEnd();\n            const sel2 = $getSelection();\n            if ($isRangeSelection(sel2)) sel2.insertText(char);\n            return;\n          }\n          selection.insertText(char);\n        });\n        if (i < step.text.length - 1) await sleep(stagger, signal);\n      }\n    },\n  });\n\n  // Bridge useScriptedDemo's restart() up to the outer ComposerHandle.\n  // Wrap it so the inner component also wipes the editor before the\n  // new run begins (otherwise the replay's first type step would\n  // append to the leftover text).\n  React.useEffect(() => {\n    restartRef.current = (delayMs?: number) => {\n      clearEditor(editor);\n      demoRestart(delayMs);\n    };\n  }, [editor, demoRestart, restartRef]);\n\n  // ── Action row ─────────────────────────────────────────────────────\n\n  // Read-only composers (marketing demos) shouldn't render the action\n  // row — there's no user input to send, no attachments to attach.\n  const showDefaultSend = !readOnly && !rightActions && !hideSend;\n  const showDefaultAttach = !readOnly && !leftActions && attachmentsCfg !== null;\n  const showActionRow = !readOnly && (showDefaultAttach || leftActions || showDefaultSend || rightActions);\n\n  return (\n    <div\n      ref={containerRef}\n      data-gds-part=\"composer\"\n      data-gds-loading={isLoading ? \"true\" : \"false\"}\n      className={cn(\n        \"w-full\",\n        // Match Input's chrome — rounded-md + border-input + shadow-sm\n        // + softer focus ring. The Composer reads as a heavier sibling\n        // of Input (multi-line, toolbar, attachments) and should sit\n        // in the same form rhythm without looking like a different\n        // family of control.\n        !bare && [\n          \"rounded-md\",\n          \"bg-transparent\",\n          \"border border-input\",\n          \"shadow-sm\",\n          \"focus-within:outline-none focus-within:ring-1 focus-within:ring-ring\",\n          \"transition-colors\",\n        ],\n        className,\n      )}\n    >\n      {showToolbar && <ComposerToolbar formats={formatList} />}\n\n      {attachmentsCfg && (\n        <AttachmentChips\n          attachments={attachments}\n          onRemove={(id) => {\n            setAttachments((prev) => {\n              const target = prev.find((a) => a.id === id);\n              if (target) URL.revokeObjectURL(target.previewUrl);\n              return prev.filter((a) => a.id !== id);\n            });\n          }}\n        />\n      )}\n\n      <div className=\"relative\">\n        <RichTextPlugin\n          contentEditable={\n            <ContentEditable\n              data-gds-part=\"composer-editor\"\n              className={cn(\n                \"outline-none\",\n                \"px-3 sm:px-4 py-3\",\n                \"text-sm text-[var(--gds-composer-fg)]\",\n                \"min-h-[44px] max-h-[300px] overflow-y-auto\",\n                \"[&_p]:m-0 [&_p+p]:mt-2\",\n                \"[&_h1]:text-xl [&_h1]:font-semibold [&_h1]:mt-2\",\n                \"[&_h2]:text-lg [&_h2]:font-semibold [&_h2]:mt-2\",\n                \"[&_h3]:text-base [&_h3]:font-semibold [&_h3]:mt-2\",\n                \"[&_ul]:list-disc [&_ul]:pl-5 [&_ol]:list-decimal [&_ol]:pl-5\",\n                \"[&_blockquote]:border-l-2 [&_blockquote]:border-[var(--gds-composer-border)] [&_blockquote]:pl-3 [&_blockquote]:italic\",\n                \"[&_code]:bg-[var(--gds-composer-toolbar-active-bg)] [&_code]:px-1 [&_code]:py-0.5 [&_code]:rounded [&_code]:text-xs [&_code]:font-mono\",\n                isLoading && \"opacity-60 pointer-events-none\",\n              )}\n              aria-placeholder={placeholder ?? \"\"}\n              placeholder={\n                <div\n                  data-gds-part=\"composer-placeholder\"\n                  // Match the standard Input placeholder exactly.\n                  // (The old `text-[var(--gds-composer-muted-fg)]`\n                  // emitted a bare OKLCH triplet with no oklch()\n                  // wrapper — an invalid color — so the placeholder\n                  // fell back to the dark editor foreground and read\n                  // like already-typed text.)\n                  className=\"absolute top-3 left-3 sm:left-4 text-sm text-muted-foreground pointer-events-none select-none\"\n                >\n                  {placeholder}\n                </div>\n              }\n              spellCheck\n            />\n          }\n          ErrorBoundary={LexicalErrorBoundary}\n        />\n        <HistoryPlugin />\n        <ListPlugin />\n        <LinkPlugin />\n        <OnChangePlugin onChange={() => {}} />\n        <AutoFocusPlugin enabled={Boolean(autoFocus)} />\n        <SubmitPlugin onSubmit={handleSubmit} enabled={submitOnEnter} />\n        {attachmentsCfg && (\n          <PastePlugin onImageFiles={ingestImages} enabled={true} />\n        )}\n        {triggers.length > 0 && (\n          <BeautifulMentionsPlugin\n            items={triggers.reduce<Record<string, BeautifulMentionsItem[]>>(\n              (acc, t) => {\n                // Resolver functions are deferred to v2 — for now only\n                // static arrays are wired into the plugin. The shape\n                // here is intentionally minimal: just `value`. The\n                // plugin spreads each item's extra props onto the DOM\n                // (which is how it warns \"React does not recognize the\n                // `foo` prop\"), so anything richer than primitives\n                // needs the plugin's own custom-component slot to\n                // render properly. Keep the surface narrow until we\n                // need richer items.\n                if (typeof t.items === \"function\") return acc;\n                acc[t.char] = t.items.map((item) => ({\n                  value: item.value,\n                })) as BeautifulMentionsItem[];\n                return acc;\n              },\n              {},\n            )}\n          />\n        )}\n      </div>\n\n      {/* Action row — paperclip on the left, send/stop on the right.\n          Slots win when supplied; defaults render otherwise. Hidden\n          entirely in readOnly mode. */}\n      {showActionRow && (\n        <div\n          data-gds-part=\"composer-actions\"\n          className=\"flex items-center justify-between gap-2 px-2 pb-2 pt-1\"\n        >\n          <div className=\"flex items-center gap-1\">\n            {leftActions ?? (\n              showDefaultAttach && (\n                <button\n                  type=\"button\"\n                  onClick={() => fileInputRef.current?.click()}\n                  disabled={isLoading}\n                  aria-label=\"Attach image\"\n                  title=\"Attach image\"\n                  className={cn(\n                    \"h-8 w-8 rounded-lg flex items-center justify-center\",\n                    \"text-[var(--gds-composer-action-fg)]\",\n                    \"hover:text-[var(--gds-composer-fg)]\",\n                    \"hover:bg-[var(--gds-composer-toolbar-hover-bg)]\",\n                    \"focus:outline-none focus:ring-2 focus:ring-primary\",\n                    \"transition-colors\",\n                    \"disabled:opacity-50 disabled:cursor-not-allowed\",\n                  )}\n                >\n                  <Paperclip className=\"w-4 h-4\" />\n                </button>\n              )\n            )}\n          </div>\n\n          {rightActions ??\n            (showDefaultSend && (\n              <button\n                type=\"button\"\n                onClick={isLoading ? onStop : handleSubmit}\n                disabled={\n                  isLoading\n                    ? !onStop\n                    : !hasContent && attachments.length === 0\n                }\n                aria-label={isLoading ? \"Stop\" : \"Send\"}\n                className={cn(\n                  \"h-8 w-8 rounded-lg flex items-center justify-center transition-colors flex-shrink-0\",\n                  \"focus:outline-none focus:ring-2 focus:ring-primary\",\n                  isLoading\n                    ? \"bg-red-500 hover:bg-red-600 text-white disabled:opacity-50\"\n                    : hasContent || attachments.length > 0\n                      ? \"bg-primary hover:bg-primary/90 text-primary-foreground\"\n                      : \"bg-[var(--gds-composer-toolbar-active-bg)] text-[var(--gds-composer-muted-fg)] cursor-not-allowed\",\n                )}\n              >\n                {isLoading ? (\n                  <Square className=\"w-3.5 h-3.5\" />\n                ) : (\n                  <Send className=\"w-3.5 h-3.5\" />\n                )}\n              </button>\n            ))}\n\n          {/* Hidden file input wired to the paperclip. */}\n          {attachmentsCfg && (\n            <input\n              ref={fileInputRef}\n              type=\"file\"\n              accept={attachmentsCfg.accept}\n              multiple={attachmentsCfg.multiple}\n              onChange={(e) => {\n                if (e.target.files) ingestImages(Array.from(e.target.files));\n                e.target.value = \"\";\n              }}\n              className=\"sr-only\"\n              tabIndex={-1}\n              aria-hidden=\"true\"\n            />\n          )}\n        </div>\n      )}\n    </div>\n  );\n}\n\n// ─── Outer component ────────────────────────────────────────────────\n\nexport const Composer = React.forwardRef<ComposerHandle, ComposerProps>(\n  function Composer(props, forwardedRef) {\n    const {\n      initialText,\n      initialJson,\n      formats,\n      toolbar,\n      triggers,\n      attachments: attachmentsProp,\n      ...rest\n    } = props;\n\n    // Resolve formats — false means plain text, undefined means defaults.\n    const formatList: ComposerFormat[] = React.useMemo(() => {\n      if (formats === false) return [];\n      return (\n        formats ?? [\n          \"bold\",\n          \"italic\",\n          \"underline\",\n          \"strikethrough\",\n          \"code\",\n          \"h1\",\n          \"h2\",\n          \"blockquote\",\n          \"ul\",\n          \"ol\",\n        ]\n      );\n    }, [formats]);\n\n    const showToolbar = toolbar === true || toolbar === \"top\";\n\n    // Resolve attachments config — true means defaults, object merges.\n    const attachmentsCfg = React.useMemo<Required<ComposerAttachmentConfig> | null>(() => {\n      if (!attachmentsProp) return null;\n      const obj = attachmentsProp === true ? {} : attachmentsProp;\n      if (obj.enabled === false) return null;\n      return {\n        enabled: true,\n        accept: obj.accept ?? \"image/*\",\n        maxItems: obj.maxItems ?? 10,\n        multiple: obj.multiple ?? true,\n      };\n    }, [attachmentsProp]);\n\n    const [attachments, setAttachments] = React.useState<ComposerAttachment[]>(\n      [],\n    );\n    const attachmentsRef = React.useRef(attachments);\n    attachmentsRef.current = attachments;\n\n    // Revoke any outstanding object URLs on unmount.\n    React.useEffect(() => {\n      return () => {\n        attachmentsRef.current.forEach((a) => URL.revokeObjectURL(a.previewUrl));\n      };\n    }, []);\n\n    const ingestImages = React.useCallback(\n      (files: File[]) => {\n        if (!attachmentsCfg) return;\n        const candidates = files.filter((f) => {\n          if (!attachmentsCfg.accept) return true;\n          if (attachmentsCfg.accept === \"image/*\") return f.type.startsWith(\"image/\");\n          // Coarse check — full accept-attribute matching is more\n          // permissive than we need for v1.\n          return attachmentsCfg.accept.split(\",\").some((tok) => {\n            const t = tok.trim();\n            if (t.endsWith(\"/*\")) return f.type.startsWith(t.slice(0, -1));\n            return f.type === t || f.name.toLowerCase().endsWith(t);\n          });\n        });\n        if (candidates.length === 0) return;\n        setAttachments((prev) => {\n          const room = attachmentsCfg.maxItems - prev.length;\n          if (room <= 0) return prev;\n          const next = candidates.slice(0, room).map((file) => ({\n            id: `${file.name}-${file.size}-${Date.now()}-${Math.random()\n              .toString(36)\n              .slice(2, 8)}`,\n            file,\n            previewUrl: URL.createObjectURL(file),\n            name: file.name,\n          }));\n          return [...prev, ...next];\n        });\n      },\n      [attachmentsCfg],\n    );\n\n    // Editor ref bridged via the inner plugin.\n    const editorRef = React.useRef<LexicalEditor | null>(null);\n    const submitRef = React.useRef<() => void>(() => {});\n    // restart() is wired by the inner component (which owns the\n    // useScriptedDemo state). Outer keeps a ref pointer it forwards\n    // through ComposerHandle so callers can `ref.current.restart()`.\n    const restartRef = React.useRef<(delayMs?: number) => void>(() => {});\n\n    const handleEditorReady = React.useCallback((editor: LexicalEditor) => {\n      editorRef.current = editor;\n    }, []);\n\n    // Expose imperative handle.\n    React.useImperativeHandle(\n      forwardedRef,\n      (): ComposerHandle => ({\n        play: (_steps) => {\n          // For imperative play, we re-mount with a manual trigger.\n          // V1: log + recommend using the `steps` prop with manual\n          // trigger. The plumbing for hot-swapping a fresh script is\n          // tracked as a follow-up.\n          // eslint-disable-next-line no-console\n          console.warn(\n            \"[Composer] handle.play(steps) is not wired in v1 — pass `steps` + `trigger=\\\"manual\\\"` and toggle `play` instead.\",\n          );\n        },\n        stop: () => {},\n        restart: (delayMs) => restartRef.current(delayMs),\n        focus: () => editorRef.current?.focus(),\n        clear: () => {\n          const editor = editorRef.current;\n          if (editor) clearEditor(editor);\n        },\n        insert: (text) => {\n          editorRef.current?.update(() => {\n            const sel = $getSelection();\n            if ($isRangeSelection(sel)) sel.insertText(text);\n          });\n        },\n        getContent: () =>\n          editorRef.current\n            ? snapshotContent(editorRef.current)\n            : { text: \"\", json: \"\", mentions: [] },\n        getEditor: () => editorRef.current,\n      }),\n      [],\n    );\n\n    // Lexical theme classes — kept light so most of the styling\n    // happens via Tailwind on the parent. The theme bridge is here\n    // so consumers can override individual node classes via CSS\n    // variables without forking the component.\n    const lexicalTheme = React.useMemo(\n      () => ({\n        paragraph: \"gds-composer-paragraph\",\n        quote: \"gds-composer-quote\",\n        heading: {\n          h1: \"gds-composer-h1\",\n          h2: \"gds-composer-h2\",\n          h3: \"gds-composer-h3\",\n        },\n        list: {\n          ul: \"gds-composer-ul\",\n          ol: \"gds-composer-ol\",\n          listitem: \"gds-composer-li\",\n        },\n        text: {\n          bold: \"font-semibold\",\n          italic: \"italic\",\n          underline: \"underline\",\n          strikethrough: \"line-through\",\n          code: \"gds-composer-code\",\n        },\n        beautifulMentions: {\n          // Tailwind utilities so the pill styling works without\n          // requiring consumers to load @gradeui/ui/styles.css. The\n          // gds-composer-mention* tokens in globals.css are still\n          // available for hosts that want to retheme without forking\n          // — they layer over these defaults via class precedence.\n          \"@\": \"gds-composer-mention px-1.5 py-0.5 mx-0.5 rounded bg-primary/10 text-primary font-medium\",\n          \"/\": \"gds-composer-mention px-1.5 py-0.5 mx-0.5 rounded bg-violet-500/15 text-violet-600 dark:text-violet-400 font-medium\",\n        },\n      }),\n      [],\n    );\n\n    // Initial config for LexicalRoot. Note nodes registration —\n    // every custom node we use anywhere (BeautifulMentionNode for\n    // mentions, HeadingNode/QuoteNode/etc for formatting) MUST be\n    // declared here or Lexical throws at first paint.\n    const initialConfig: InitialConfigType = React.useMemo(\n      () => ({\n        namespace: \"gds-composer\",\n        theme: lexicalTheme,\n        onError: (error: Error) => {\n          // eslint-disable-next-line no-console\n          console.error(\"[Composer]\", error);\n        },\n        nodes: [\n          HeadingNode,\n          QuoteNode,\n          ListNode,\n          ListItemNode,\n          LinkNode,\n          AutoLinkNode,\n          CodeNode,\n          CodeHighlightNode,\n          BeautifulMentionNode,\n        ],\n        editorState: initialJson\n          ? initialJson\n          : initialText\n            ? (editor: LexicalEditor) => {\n                const root = $getRoot();\n                const p = $createParagraphNode();\n                p.append($createTextNode(initialText));\n                root.append(p);\n              }\n            : undefined,\n      }),\n      [lexicalTheme, initialJson, initialText],\n    );\n\n    const richMode = formatList.length > 0;\n    const { readOnly = false, ...innerRest } = rest;\n\n    return (\n      <LexicalRoot\n        initialConfig={{\n          ...initialConfig,\n          editable: !readOnly,\n        }}\n      >\n        <ComposerInner\n          {...innerRest}\n          readOnly={readOnly}\n          triggers={triggers ?? []}\n          attachmentsCfg={readOnly ? null : attachmentsCfg}\n          formatList={formatList}\n          showToolbar={showToolbar && richMode && !readOnly}\n          handleEditorReady={handleEditorReady}\n          submitRef={submitRef}\n          restartRef={restartRef}\n          attachments={attachments}\n          setAttachments={setAttachments}\n          ingestImages={ingestImages}\n        />\n      </LexicalRoot>\n    );\n  },\n);\n\n// ─── ComposerReply — preset for the reply use case ───────────────────\n//\n// Wraps Composer with sensible defaults for comment threads and\n// reply boxes: plain text only (no toolbar), no attachments, the\n// \"Write a reply…\" placeholder. Surfaced from playground use — three\n// scaffolds (comments, chat, hero preview) all reached for the same\n// shape and re-passed the same props.\n//\n// All defaults are overridable — pass `formats=[...]` to enable a\n// toolbar, `attachments` to allow uploads, etc. ComposerReply is\n// a starting point, not a lock-in.\n\nexport const ComposerReply = React.forwardRef<ComposerHandle, ComposerProps>(\n  function ComposerReply(props, ref) {\n    return (\n      <Composer\n        ref={ref}\n        placeholder=\"Write a reply…\"\n        formats={false}\n        submitOnEnter={false}\n        {...props}\n      />\n    );\n  },\n);\n"]}