{"version":3,"sources":["../src/components/MagicWand/MagicWand.tsx"],"names":["useRef","useState","useCallback","useEffect","jsxs","jsx","Textarea","Badge","Button"],"mappings":"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","file":"chunk-GHAZOIEG.cjs","sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { Button } from \"../Button\";\nimport { Badge } from \"../Badge\";\nimport { Textarea } from \"../inputs/Textarea/Textarea\";\n\n/**\n * MagicWand — selection-anchored floating toolbar for text inputs.\n *\n * When the user highlights text inside the wrapped <Textarea>, a small\n * pill of AI quick-actions floats above the selection. Clicking an\n * action invokes the host callback with the selected substring + range;\n * the host returns a replacement string and MagicWand swaps it in-place.\n *\n *   <MagicWand\n *     value={body}\n *     onValueChange={setBody}\n *     actions={[\n *       { id: \"rephrase\", label: \"Rephrase\", hint: \"same meaning, different words\",\n *         run: async (s) => await ai.rephrase(s) },\n *       …\n *     ]}\n *   />\n *\n * Two appearances: `\"floating\"` (default — full labels) and `\"pill\"`\n * (icon-only, compact). Auto-hides on click-away or scroll by default.\n */\nexport type MagicWandSelection = { start: number; end: number; text: string };\n\nexport type MagicWandAction = {\n  /** Stable id, also shown as the busy-state key. */\n  id: string;\n  /** Display label (first character used in `pill` mode). */\n  label: string;\n  /** Tooltip body. */\n  hint?: string;\n  /** Optional kind chip rendered next to the label. */\n  tag?: string;\n  /** Returns the replacement text for the selection. */\n  run: (selection: string, range: MagicWandSelection) => Promise<string> | string;\n};\n\nexport type MagicWandAppearance = \"pill\" | \"floating\";\n\nexport interface MagicWandProps {\n  /** Controlled textarea value. */\n  value: string;\n  /** Called on every edit. */\n  onValueChange: (v: string) => void;\n  /** Action list. */\n  actions: MagicWandAction[];\n  /** Visual treatment. Defaults to \"floating\". */\n  appearance?: MagicWandAppearance;\n  /** Auto-hide the wand on click-away or scroll. Defaults to true. */\n  autoHide?: boolean;\n  /** Textarea rows. */\n  rows?: number;\n  /** Placeholder. */\n  placeholder?: string;\n  /** Called after an action runs successfully. */\n  onAction?: (action: MagicWandAction, selection: MagicWandSelection, replacement: string) => void;\n}\n\nexport function MagicWand({\n  value,\n  onValueChange,\n  actions,\n  appearance = \"floating\",\n  autoHide = true,\n  rows = 6,\n  placeholder,\n  onAction,\n}: MagicWandProps) {\n  const taRef = useRef<HTMLTextAreaElement>(null);\n  const wandRef = useRef<HTMLDivElement>(null);\n  const [sel, setSel] = useState<MagicWandSelection | null>(null);\n  const [pos, setPos] = useState<{ x: number; y: number } | null>(null);\n  const [busy, setBusy] = useState<string | null>(null);\n\n  const measureSelection = useCallback(() => {\n    const ta = taRef.current;\n    if (!ta) return;\n    const start = ta.selectionStart ?? 0;\n    const end = ta.selectionEnd ?? 0;\n    if (start === end) {\n      setSel(null);\n      setPos(null);\n      return;\n    }\n    const text = ta.value.slice(start, end);\n    setSel({ start, end, text });\n    const rect = caretRect(ta, start, end);\n    if (rect) setPos({ x: rect.x, y: rect.y });\n  }, []);\n\n  useEffect(() => {\n    if (!autoHide) return;\n    const onScroll = () => {\n      setSel(null);\n      setPos(null);\n    };\n    window.addEventListener(\"scroll\", onScroll, true);\n    return () => window.removeEventListener(\"scroll\", onScroll, true);\n  }, [autoHide]);\n\n  useEffect(() => {\n    if (!autoHide) return;\n    const onDocMouseDown = (e: MouseEvent) => {\n      if (wandRef.current?.contains(e.target as Node)) return;\n      if (taRef.current?.contains(e.target as Node)) return;\n      setSel(null);\n      setPos(null);\n    };\n    document.addEventListener(\"mousedown\", onDocMouseDown);\n    return () => document.removeEventListener(\"mousedown\", onDocMouseDown);\n  }, [autoHide]);\n\n  const handleAction = async (action: MagicWandAction) => {\n    if (!sel) return;\n    setBusy(action.id);\n    try {\n      const replacement = await action.run(sel.text, sel);\n      const next = value.slice(0, sel.start) + replacement + value.slice(sel.end);\n      onValueChange(next);\n      onAction?.(action, sel, replacement);\n    } finally {\n      setBusy(null);\n      setSel(null);\n      setPos(null);\n    }\n  };\n\n  return (\n    <div className=\"relative\">\n      <Textarea\n        ref={taRef as unknown as React.Ref<HTMLTextAreaElement>}\n        value={value}\n        onValueChange={onValueChange}\n        onSelect={measureSelection}\n        onKeyUp={measureSelection}\n        onMouseUp={measureSelection}\n        rows={rows}\n        placeholder={placeholder}\n      />\n      {sel && pos && (\n        <Wand\n          ref={wandRef}\n          pos={pos}\n          actions={actions}\n          appearance={appearance}\n          busy={busy}\n          onAction={handleAction}\n          selectionLength={sel.text.length}\n        />\n      )}\n    </div>\n  );\n}\n\nfunction Wand({\n  ref,\n  pos,\n  actions,\n  appearance,\n  busy,\n  onAction,\n  selectionLength,\n}: {\n  ref: React.RefObject<HTMLDivElement | null>;\n  pos: { x: number; y: number };\n  actions: MagicWandAction[];\n  appearance: MagicWandAppearance;\n  busy: string | null;\n  onAction: (a: MagicWandAction) => void;\n  selectionLength: number;\n}) {\n  return (\n    <div\n      ref={ref}\n      className=\"absolute z-20 -translate-x-1/2 -translate-y-full\"\n      style={{ left: pos.x, top: pos.y - 6 }}\n    >\n      <div className=\"flex items-center gap-1 rounded-full border border-violet-300 bg-white px-1.5 py-1 shadow-lg ring-1 ring-violet-200 dark:border-violet-700 dark:bg-zinc-900 dark:ring-violet-900\">\n        <span className=\"ml-1 mr-0.5 inline-flex items-center gap-1 text-[10px] font-medium text-violet-700 dark:text-violet-300\">\n          <span aria-hidden>✦</span>\n          {appearance === \"floating\" && (\n            <Badge color=\"violet\">{selectionLength} chars</Badge>\n          )}\n        </span>\n        {actions.map((a) => (\n          <Button\n            key={a.id}\n            size=\"sm\"\n            variant=\"ghost\"\n            onClick={() => onAction(a)}\n            disabled={busy !== null}\n            title={a.hint}\n          >\n            {busy === a.id ? \"…\" : appearance === \"pill\" ? a.label[0] : a.label}\n            {appearance === \"floating\" && a.tag && (\n              <span className=\"ml-1 rounded-full bg-zinc-100 px-1 text-[9px] font-medium text-zinc-500 dark:bg-zinc-800\">\n                {a.tag}\n              </span>\n            )}\n          </Button>\n        ))}\n      </div>\n      <div className=\"absolute left-1/2 top-full -translate-x-1/2 -translate-y-px\">\n        <div className=\"h-2 w-2 rotate-45 border-b border-r border-violet-300 bg-white dark:border-violet-700 dark:bg-zinc-900\" />\n      </div>\n    </div>\n  );\n}\n\n/**\n * Caret/selection geometry: mirror the textarea into a hidden div and\n * measure the selected substring's bounding rect. Not perfect for\n * resized textareas, but works for typical chat composers.\n */\nfunction caretRect(\n  ta: HTMLTextAreaElement,\n  start: number,\n  end: number,\n): { x: number; y: number } | null {\n  const div = document.createElement(\"div\");\n  const style = getComputedStyle(ta);\n  const props = [\n    \"boxSizing\",\n    \"width\",\n    \"height\",\n    \"overflowX\",\n    \"overflowY\",\n    \"borderTopWidth\",\n    \"borderRightWidth\",\n    \"borderBottomWidth\",\n    \"borderLeftWidth\",\n    \"paddingTop\",\n    \"paddingRight\",\n    \"paddingBottom\",\n    \"paddingLeft\",\n    \"fontStyle\",\n    \"fontVariant\",\n    \"fontWeight\",\n    \"fontStretch\",\n    \"fontSize\",\n    \"fontSizeAdjust\",\n    \"lineHeight\",\n    \"fontFamily\",\n    \"textAlign\",\n    \"textTransform\",\n    \"textIndent\",\n    \"letterSpacing\",\n    \"wordSpacing\",\n    \"tabSize\",\n  ] as const;\n  for (const p of props) (div.style as any)[p] = (style as any)[p];\n  div.style.position = \"absolute\";\n  div.style.top = \"-9999px\";\n  div.style.left = \"-9999px\";\n  div.style.whiteSpace = \"pre-wrap\";\n  div.style.wordWrap = \"break-word\";\n\n  const value = ta.value;\n  div.textContent = value.substring(0, start);\n  const startSpan = document.createElement(\"span\");\n  startSpan.textContent = value.substring(start, end) || \".\";\n  div.appendChild(startSpan);\n\n  document.body.appendChild(div);\n  const taRect = ta.getBoundingClientRect();\n  const parentRect =\n    (ta.offsetParent as HTMLElement | null)?.getBoundingClientRect() ?? taRect;\n  const spanRect = startSpan.getBoundingClientRect();\n  const divRect = div.getBoundingClientRect();\n  const offsetX = spanRect.left - divRect.left;\n  const offsetY = spanRect.top - divRect.top;\n  document.body.removeChild(div);\n\n  const x = taRect.left - parentRect.left + offsetX + spanRect.width / 2;\n  const y = taRect.top - parentRect.top + offsetY - ta.scrollTop;\n  return { x, y };\n}\n"]}