@particle-academy/react-fancy 5.71.1 → 5.71.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/dist/button.cjs +3 -3
  3. package/dist/button.js +1 -1
  4. package/dist/chat-thread.cjs +5 -5
  5. package/dist/chat-thread.js +3 -3
  6. package/dist/{chunk-3U7PUZLI.cjs → chunk-2KQVBD7K.cjs} +8 -3
  7. package/dist/chunk-2KQVBD7K.cjs.map +1 -0
  8. package/dist/{chunk-FGOFG2UZ.cjs → chunk-7OKF6M32.cjs} +15 -15
  9. package/dist/{chunk-FGOFG2UZ.cjs.map → chunk-7OKF6M32.cjs.map} +1 -1
  10. package/dist/{chunk-5IE65DIH.cjs → chunk-7PUTP2N7.cjs} +10 -10
  11. package/dist/{chunk-5IE65DIH.cjs.map → chunk-7PUTP2N7.cjs.map} +1 -1
  12. package/dist/{chunk-FYUEDPST.js → chunk-7T7L5IK5.js} +3 -3
  13. package/dist/{chunk-FYUEDPST.js.map → chunk-7T7L5IK5.js.map} +1 -1
  14. package/dist/{chunk-Q72VHIBI.js → chunk-ARNFT23K.js} +8 -3
  15. package/dist/chunk-ARNFT23K.js.map +1 -0
  16. package/dist/{chunk-S3NB6LDV.cjs → chunk-AWIUXZTG.cjs} +4 -4
  17. package/dist/{chunk-S3NB6LDV.cjs.map → chunk-AWIUXZTG.cjs.map} +1 -1
  18. package/dist/{chunk-5YRSYS2F.cjs → chunk-BL6HCIP4.cjs} +12 -12
  19. package/dist/{chunk-5YRSYS2F.cjs.map → chunk-BL6HCIP4.cjs.map} +1 -1
  20. package/dist/{chunk-WFR7HKJL.js → chunk-CINWSRA2.js} +3 -3
  21. package/dist/{chunk-WFR7HKJL.js.map → chunk-CINWSRA2.js.map} +1 -1
  22. package/dist/{chunk-RALJMI7C.cjs → chunk-EEKIVROJ.cjs} +4 -4
  23. package/dist/{chunk-RALJMI7C.cjs.map → chunk-EEKIVROJ.cjs.map} +1 -1
  24. package/dist/{chunk-NAYXNVNU.js → chunk-FJJ3GMXI.js} +3 -3
  25. package/dist/{chunk-NAYXNVNU.js.map → chunk-FJJ3GMXI.js.map} +1 -1
  26. package/dist/{chunk-BL5IKSB3.js → chunk-FUMX6TYZ.js} +3 -3
  27. package/dist/{chunk-BL5IKSB3.js.map → chunk-FUMX6TYZ.js.map} +1 -1
  28. package/dist/{chunk-F4GMOMUT.js → chunk-HTPMFADD.js} +4 -4
  29. package/dist/{chunk-F4GMOMUT.js.map → chunk-HTPMFADD.js.map} +1 -1
  30. package/dist/{chunk-R3EJNGJV.cjs → chunk-JGEFIWDA.cjs} +9 -9
  31. package/dist/{chunk-R3EJNGJV.cjs.map → chunk-JGEFIWDA.cjs.map} +1 -1
  32. package/dist/{chunk-AQ3NPK2X.cjs → chunk-L5AI4MGV.cjs} +4 -4
  33. package/dist/{chunk-AQ3NPK2X.cjs.map → chunk-L5AI4MGV.cjs.map} +1 -1
  34. package/dist/{chunk-K7SPT7YF.js → chunk-LYG2NLSV.js} +3 -3
  35. package/dist/{chunk-K7SPT7YF.js.map → chunk-LYG2NLSV.js.map} +1 -1
  36. package/dist/{chunk-4TK3IYR6.cjs → chunk-MF2DCMOA.cjs} +8 -3
  37. package/dist/chunk-MF2DCMOA.cjs.map +1 -0
  38. package/dist/{chunk-PS7C6GKI.cjs → chunk-MPUR2ZLF.cjs} +6 -6
  39. package/dist/{chunk-PS7C6GKI.cjs.map → chunk-MPUR2ZLF.cjs.map} +1 -1
  40. package/dist/{chunk-OPDHSOUN.js → chunk-R2MFCR7H.js} +3 -3
  41. package/dist/{chunk-OPDHSOUN.js.map → chunk-R2MFCR7H.js.map} +1 -1
  42. package/dist/{chunk-OXERDFFF.js → chunk-VV5435PS.js} +8 -3
  43. package/dist/chunk-VV5435PS.js.map +1 -0
  44. package/dist/{chunk-UWYSJARZ.js → chunk-WFEX7DAR.js} +4 -4
  45. package/dist/{chunk-UWYSJARZ.js.map → chunk-WFEX7DAR.js.map} +1 -1
  46. package/dist/content-renderer.cjs +5 -5
  47. package/dist/content-renderer.js +2 -2
  48. package/dist/editor.cjs +5 -5
  49. package/dist/editor.js +3 -3
  50. package/dist/index.cjs +45 -45
  51. package/dist/index.js +10 -10
  52. package/dist/json-editor.cjs +13 -13
  53. package/dist/json-editor.js +2 -2
  54. package/dist/magic-wand.cjs +3 -3
  55. package/dist/magic-wand.js +2 -2
  56. package/dist/prompt-input.cjs +5 -5
  57. package/dist/prompt-input.js +2 -2
  58. package/dist/reason-tag.cjs +3 -3
  59. package/dist/reason-tag.js +2 -2
  60. package/dist/tree-nav.cjs +4 -4
  61. package/dist/tree-nav.js +2 -2
  62. package/dist/virtual-list.cjs +2 -2
  63. package/dist/virtual-list.js +1 -1
  64. package/package.json +1 -1
  65. package/dist/chunk-3U7PUZLI.cjs.map +0 -1
  66. package/dist/chunk-4TK3IYR6.cjs.map +0 -1
  67. package/dist/chunk-OXERDFFF.js.map +0 -1
  68. package/dist/chunk-Q72VHIBI.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/VirtualList/VirtualList.tsx"],"names":[],"mappings":"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","file":"chunk-ARNFT23K.js","sourcesContent":["import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport type { KeyboardEvent, TouchEvent, WheelEvent } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport type { VirtualListProps, VirtualListRow } from \"./VirtualList.types\";\n\n/**\n * A windowed list for ledgers that run to tens of thousands of rows — only the\n * visible slice is in the DOM, with **measured** variable row heights, deep\n * links by row id, and sticky-bottom tail-following.\n *\n * Built for an agent event stream, where every one of those three is load\n * bearing at once: message rows wrap and event rows do not, a link carries\n * `?at=<rowId>` to the exact row under discussion, and new rows arrive while\n * the human is reading older ones.\n *\n * ## Heights are measured, never predicted\n *\n * `estimateRowHeight` only seeds rows that have never been on screen. The\n * moment a row renders, its real height is measured and replaces the estimate,\n * and every offset after it moves. The alternative — a `rowHeight(item)`\n * callback — is a prediction about text wrapping, font loading and container\n * width, and when it is wrong the error accumulates down the list: rows draw in\n * the wrong place and `scrollToId` lands on a neighbour. Measuring is slower to\n * settle and correct when it does.\n *\n * ## Tail-follow breaks on INTENT, not on position\n *\n * Any upward gesture — wheel, drag, PageUp, Home — stops following\n * immediately, even when the list is still pinned to the bottom and\n * `scrollTop` has not changed yet. Position cannot tell \"the human scrolled\n * up\" from \"content grew downward\", so a position-only implementation keeps\n * following through a wheel-up and yanks the reader back on the next append.\n *\n * ## It is controlled\n *\n * `followTail` is the consumer's state. This component only ever *asks* via\n * `onFollowChange`, so the follow state is readable and writable by an agent\n * over a bridge, as the Human+ component contract requires. The `↓ n new` pill\n * is the consumer's to render, from `onUnseenChange`.\n *\n * @example\n * ```tsx\n * const [following, setFollowing] = useState(true);\n * const [unseen, setUnseen] = useState(0);\n *\n * <VirtualList\n * items={events}\n * renderRow={(e) => <StreamRow event={e} />}\n * height=\"100%\"\n * estimateRowHeight={28}\n * followTail={following}\n * onFollowChange={setFollowing}\n * onUnseenChange={setUnseen}\n * scrollToId={params.at}\n * />\n * {!following && unseen > 0 && (\n * <Button onClick={() => setFollowing(true)}>↓ {unseen} new</Button>\n * )}\n * ```\n */\nexport function VirtualList<T extends VirtualListRow>({\n items,\n renderRow,\n height = \"100%\",\n estimateRowHeight = 32,\n overscan = 6,\n followTail = false,\n onFollowChange,\n onUnseenChange,\n scrollToId = null,\n onScrollToIdResolved,\n className,\n ref,\n ...props\n}: VirtualListProps<T>) {\n const viewportRef = useRef<HTMLDivElement | null>(null);\n const windowRef = useRef<HTMLDivElement | null>(null);\n\n /** Measured heights by row id, so a height follows its row across a reindex. */\n const heights = useRef(new Map<string, number>());\n /** Bumped when a measurement actually changed, to recompute offsets. */\n const [measured, setMeasured] = useState(0);\n\n const [scrollTop, setScrollTop] = useState(0);\n const [clientHeight, setClientHeight] = useState(0);\n\n /**\n * Where the viewport is scrolled, as far as this list knows: the value from\n * the last `scroll` event, or the last position it set itself. Every change\n * to scrollTop raises a scroll event, so this is never wrong for longer than\n * one frame — and reading it, unlike reading `vp.scrollTop` inside a commit,\n * never forces a synchronous layout.\n */\n const knownTop = useRef(0);\n\n const attachViewport = useCallback(\n (el: HTMLDivElement | null) => {\n viewportRef.current = el;\n if (typeof ref === \"function\") ref(el);\n else if (ref) (ref as { current: HTMLDivElement | null }).current = el;\n },\n [ref],\n );\n\n // A number `height` IS the viewport height, and is usable before the element\n // has been measured. Without one, render a conservative first window so there\n // is something on screen for the measurement pass to work from.\n const viewport =\n clientHeight > 0 ? clientHeight : typeof height === \"number\" ? height : estimateRowHeight * 20;\n\n /**\n * Prefix sums of row heights: `offsets[i]` is where row `i` starts, and the\n * last entry is the total. Rebuilt whenever rows or measurements change —\n * O(n) once per change, against O(log n) per scroll from the binary search,\n * rather than O(n) on every scroll frame.\n */\n const { offsets, total } = useMemo(() => {\n const offs = new Float64Array(items.length + 1);\n for (let i = 0; i < items.length; i += 1) {\n offs[i + 1] = offs[i] + (heights.current.get(items[i].id) ?? estimateRowHeight);\n }\n\n return { offsets: offs, total: items.length === 0 ? 0 : offs[items.length] };\n }, [items, measured, estimateRowHeight]);\n\n /** First row whose span contains `y`. */\n const indexAt = useCallback(\n (y: number): number => {\n let lo = 0;\n let hi = items.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1;\n if (offsets[mid] <= y) lo = mid;\n else hi = mid - 1;\n }\n\n return Math.max(0, lo);\n },\n [items.length, offsets],\n );\n\n const start = Math.max(0, indexAt(scrollTop) - overscan);\n const end = Math.min(items.length, indexAt(scrollTop + viewport) + 1 + overscan);\n const visible = useMemo(() => items.slice(start, end), [items, start, end]);\n\n // ---------------------------------------------------------------- measuring\n\n //\n // Every getBoundingClientRect() / clientHeight read after a commit forces a\n // synchronous layout, and this list re-renders on every appended row. So a\n // row is measured ONCE, when its element first appears, and from then on the\n // ResizeObserver reports when it changes, with the size in the entry: no read\n // that forces layout. An append costs one measurement; a render where nothing\n // changed costs none. (Measured in genie#1340: re-measuring every visible row\n // on every render was 472 forced row measurements/s with 11 agents streaming.)\n\n const observer = useRef<ResizeObserver | null>(null);\n /** Row elements currently observed, so each is observed once and released. */\n const observed = useRef(new Set<Element>());\n\n // A layout effect, declared before the measuring one, so the observer exists\n // by the time the first window is measured and the first rows are observed.\n useLayoutEffect(() => {\n // Absent in jsdom and in older runtimes. Without it the list measures every\n // visible row on every render instead, which is the only way it can notice\n // a row reflowing in place.\n if (typeof ResizeObserver === \"undefined\") return;\n\n const ro = new ResizeObserver((entries) => {\n let changed = false;\n for (const entry of entries) {\n const el = entry.target as HTMLElement;\n\n if (el === viewportRef.current) {\n // The viewport's own resize changes how many rows fit. Reading\n // clientHeight here is free: the browser has just done layout.\n const ch = el.clientHeight;\n if (ch > 0) setClientHeight(ch);\n continue;\n }\n\n const id = el.getAttribute(ROW_ID_ATTR);\n if (id === null) continue;\n\n const h = entryHeight(entry);\n if (h > 0 && heights.current.get(id) !== h) {\n heights.current.set(id, h);\n changed = true;\n }\n }\n\n if (changed) setMeasured((v) => v + 1);\n });\n observer.current = ro;\n\n const vp = viewportRef.current;\n if (vp) ro.observe(vp);\n\n const tracked = observed.current;\n\n return () => {\n ro.disconnect();\n observer.current = null;\n tracked.clear();\n };\n }, []);\n\n // No dependency array on purpose: every render looks at what is on screen.\n // That LOOK is cheap — walking the window's children reads no layout. Only a\n // row seen for the first time is measured (or, with no ResizeObserver, every\n // row, as nothing else would notice a reflow). A render that learns nothing\n // new sets no state, which is what stops it looping.\n useLayoutEffect(() => {\n const win = windowRef.current;\n if (!win) return;\n\n const ro = observer.current;\n const tracked = observed.current;\n const present = ro ? new Set<Element>() : null;\n\n let changed = false;\n let measuredAny = false;\n for (const el of Array.from(win.children) as HTMLElement[]) {\n const id = el.getAttribute(ROW_ID_ATTR);\n if (id === null) continue;\n\n if (ro && present) {\n present.add(el);\n // Already observed: the observer reports its resizes.\n if (tracked.has(el)) continue;\n\n ro.observe(el);\n tracked.add(el);\n // A row scrolling back into view: its height is known, and the\n // observer's first report for the new element corrects it if it moved.\n if (heights.current.has(id)) continue;\n }\n\n const h = el.getBoundingClientRect().height;\n measuredAny = true;\n // A zero height is \"not laid out yet\", not a 0px row. Recording it would\n // collapse the list and make every offset after it wrong.\n if (h > 0 && heights.current.get(id) !== h) {\n heights.current.set(id, h);\n changed = true;\n }\n }\n\n if (ro && present) {\n for (const el of tracked) {\n if (!present.has(el)) {\n ro.unobserve(el);\n tracked.delete(el);\n }\n }\n }\n\n // With an observer, the viewport's resizes arrive through it; read it here\n // only when this pass has already paid for a layout, so it is free.\n const vp = viewportRef.current;\n if (vp && (!ro || measuredAny) && vp.clientHeight > 0 && vp.clientHeight !== clientHeight) {\n setClientHeight(vp.clientHeight);\n }\n\n if (changed) setMeasured((v) => v + 1);\n });\n\n // ------------------------------------------------------------- deep linking\n\n /** The target this component last moved to, to detect that it has settled. */\n const anchored = useRef<number | null>(null);\n const resolved = useRef<string | null>(null);\n\n useEffect(() => {\n // A new id is a new request, even if the old one had resolved.\n anchored.current = null;\n resolved.current = null;\n }, [scrollToId]);\n\n useLayoutEffect(() => {\n if (scrollToId === null || resolved.current === scrollToId) return;\n\n const index = items.findIndex((item) => item.id === scrollToId);\n if (index < 0) {\n // A link to a row that is no longer in the list. Leaving the viewport\n // where it is and SAYING SO is the only honest outcome — scrolling to the\n // top would be indistinguishable from a successful landing.\n resolved.current = scrollToId;\n onScrollToIdResolved?.(scrollToId, false);\n\n return;\n }\n\n const vp = viewportRef.current;\n if (!vp) return;\n\n // Clamped the way the browser clamps: a row inside the final viewport-height\n // of the list cannot be brought to the top, and pretending otherwise would\n // leave this waiting for a position that can never be reached.\n const target = Math.max(0, Math.min(offsets[index], Math.max(0, total - viewport)));\n\n if (anchored.current !== target) {\n anchored.current = target;\n vp.scrollTop = target;\n knownTop.current = target;\n setScrollTop(target);\n\n return;\n }\n\n // The target stopped moving as rows above it were measured, and we are on\n // it. Only now is the deep link actually honoured.\n if (Math.abs(vp.scrollTop - target) <= 1) {\n resolved.current = scrollToId;\n onScrollToIdResolved?.(scrollToId, true);\n }\n });\n\n // ------------------------------------------------------------- tail-follow\n\n const reportedFollow = useRef(followTail);\n const programmatic = useRef(false);\n\n useEffect(() => {\n // The prop is the truth, so adopt it rather than remembering what we asked\n // for. Otherwise a consumer that declines an `onFollowChange(false)` would\n // never be asked again.\n reportedFollow.current = followTail;\n }, [followTail]);\n\n const askFollow = useCallback(\n (next: boolean) => {\n if (reportedFollow.current === next) return;\n reportedFollow.current = next;\n onFollowChange?.(next);\n },\n [onFollowChange],\n );\n\n useLayoutEffect(() => {\n if (!followTail) return;\n // A pending deep link wins while it resolves; otherwise the two would fight\n // over the same scrollTop and the link would lose.\n if (scrollToId !== null && resolved.current !== scrollToId) return;\n\n const vp = viewportRef.current;\n if (!vp) return;\n\n // This is the first layout read after most commits, so reading\n // `vp.scrollTop` here paid the forced synchronous layout on every append\n // (genie#1340 follow-up: 1.5 to 1.8 s per 120 s with 11 agents streaming).\n // Compare against the position the list already tracks instead, which only\n // differs from `bottom` when the bottom moved (rows, measurements or the\n // viewport changed) or someone scrolled — so a commit that moved nothing\n // neither reads nor writes. Without a ResizeObserver the measuring pass has\n // already paid for a layout this commit, so reading is free and keeps the\n // fallback exactly as it was.\n const current = observer.current ? knownTop.current : vp.scrollTop;\n const bottom = Math.max(0, total - viewport);\n if (Math.abs(current - bottom) > 0.5) {\n programmatic.current = true;\n vp.scrollTop = bottom;\n knownTop.current = bottom;\n setScrollTop(bottom);\n }\n });\n\n const onScroll = useCallback(() => {\n const vp = viewportRef.current;\n if (!vp) return;\n\n const next = vp.scrollTop;\n knownTop.current = next;\n const previous = scrollTop;\n setScrollTop(next);\n\n const bottom = Math.max(0, total - viewport);\n const wasProgrammatic = programmatic.current;\n programmatic.current = false;\n\n if (next <= bottom - BOTTOM_SLACK) {\n // Moved up, and not because we moved it.\n if (!wasProgrammatic && next < previous) askFollow(false);\n } else {\n askFollow(true);\n }\n }, [scrollTop, total, viewport, askFollow]);\n\n /** Any upward gesture is a statement of intent, whatever scrollTop says. */\n const onWheel = useCallback(\n (e: WheelEvent<HTMLDivElement>) => {\n if (e.deltaY < 0) askFollow(false);\n },\n [askFollow],\n );\n\n const onKeyDown = useCallback(\n (e: KeyboardEvent<HTMLDivElement>) => {\n if (UPWARD_KEYS.has(e.key)) askFollow(false);\n },\n [askFollow],\n );\n\n const touchStart = useRef<number | null>(null);\n\n const onTouchStart = useCallback((e: TouchEvent<HTMLDivElement>) => {\n touchStart.current = e.touches[0]?.clientY ?? null;\n }, []);\n\n const onTouchMove = useCallback(\n (e: TouchEvent<HTMLDivElement>) => {\n const from = touchStart.current;\n const y = e.touches[0]?.clientY;\n // Finger travelling DOWN drags the content down, which scrolls up.\n if (from !== null && y !== undefined && y - from > TOUCH_SLOP) askFollow(false);\n },\n [askFollow],\n );\n\n // ------------------------------------------------------------ unseen count\n\n const seen = useRef(items.length);\n const reportedUnseen = useRef<number | null>(null);\n\n useEffect(() => {\n const unseen = followTail ? 0 : Math.max(0, items.length - seen.current);\n if (followTail) seen.current = items.length;\n\n if (reportedUnseen.current === unseen) return;\n reportedUnseen.current = unseen;\n onUnseenChange?.(unseen);\n }, [items.length, followTail, onUnseenChange]);\n\n // ------------------------------------------------------------------ render\n\n return (\n <div\n ref={attachViewport}\n data-react-fancy-virtual-list=\"\"\n data-react-fancy-virtual-list-following={followTail ? \"true\" : \"false\"}\n data-react-fancy-virtual-list-rows={items.length}\n role=\"log\"\n tabIndex={0}\n className={cn(\"relative overflow-y-auto overflow-x-hidden outline-none\", className)}\n style={{ height }}\n onScroll={onScroll}\n onWheel={onWheel}\n onKeyDown={onKeyDown}\n onTouchStart={onTouchStart}\n onTouchMove={onTouchMove}\n {...props}\n >\n {/* Holds the full scroll range so the scrollbar is honest from the first\n paint, instead of growing as rows are discovered. */}\n <div\n data-react-fancy-virtual-list-sizer=\"\"\n style={{ height: `${total}px`, position: \"relative\" }}\n >\n <div\n ref={windowRef}\n data-react-fancy-virtual-list-window=\"\"\n data-react-fancy-virtual-list-start={start}\n style={{ transform: `translateY(${offsets[start] ?? 0}px)` }}\n >\n {visible.map((item, i) => (\n <div key={item.id} data-react-fancy-virtual-row-id={item.id}>\n {renderRow(item, start + i)}\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n\nconst ROW_ID_ATTR = \"data-react-fancy-virtual-row-id\";\n\n/**\n * A row's border-box height from a ResizeObserver entry. `borderBoxSize` is\n * what getBoundingClientRect() would report; older engines lack it (and early\n * Firefox gave a single object, not an array), so fall back to `contentRect`,\n * which equals it for the row wrapper, as that carries no padding or border.\n */\nfunction entryHeight(entry: ResizeObserverEntry): number {\n const box = entry.borderBoxSize as ReadonlyArray<ResizeObserverSize> | ResizeObserverSize | undefined;\n const first = Array.isArray(box) ? box[0] : (box as ResizeObserverSize | undefined);\n if (first && typeof first.blockSize === \"number\") return first.blockSize;\n\n return entry.contentRect?.height ?? 0;\n}\n/** Within this many px of the bottom counts as \"at the bottom\". */\nconst BOTTOM_SLACK = 4;\n/** Ignore a touch jitter smaller than this before calling it an upward drag. */\nconst TOUCH_SLOP = 4;\nconst UPWARD_KEYS = new Set([\"ArrowUp\", \"PageUp\", \"Home\"]);\n"]}
@@ -3,7 +3,7 @@
3
3
  var chunkMAZEX4EZ_cjs = require('./chunk-MAZEX4EZ.cjs');
4
4
  var chunkTI7D4QDM_cjs = require('./chunk-TI7D4QDM.cjs');
5
5
  var chunkMOU6O324_cjs = require('./chunk-MOU6O324.cjs');
6
- var chunk3U7PUZLI_cjs = require('./chunk-3U7PUZLI.cjs');
6
+ var chunk2KQVBD7K_cjs = require('./chunk-2KQVBD7K.cjs');
7
7
  var react = require('react');
8
8
  var jsxRuntime = require('react/jsx-runtime');
9
9
 
@@ -184,7 +184,7 @@ function Wand({
184
184
  ] })
185
185
  ] }),
186
186
  actions.map((a) => /* @__PURE__ */ jsxRuntime.jsxs(
187
- chunk3U7PUZLI_cjs.Button,
187
+ chunk2KQVBD7K_cjs.Button,
188
188
  {
189
189
  size: "sm",
190
190
  variant: "ghost",
@@ -262,5 +262,5 @@ function selectionPoint(ta, start, end) {
262
262
  }
263
263
 
264
264
  exports.MagicWand = MagicWand;
265
- //# sourceMappingURL=chunk-S3NB6LDV.cjs.map
266
- //# sourceMappingURL=chunk-S3NB6LDV.cjs.map
265
+ //# sourceMappingURL=chunk-AWIUXZTG.cjs.map
266
+ //# sourceMappingURL=chunk-AWIUXZTG.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/MagicWand/MagicWand.tsx"],"names":["useRef","useState","useCallback","useEffect","jsx","Portal","jsxs","Textarea","Badge","Button"],"mappings":"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","file":"chunk-S3NB6LDV.cjs","sourcesContent":["import { useCallback, useEffect, useRef, useState, type RefObject } from \"react\";\nimport { Button } from \"../Button\";\nimport { Badge } from \"../Badge\";\nimport { Portal } from \"../Portal\";\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 * Pass `target` to act on a textarea, input or editor the HOST renders instead:\n * MagicWand then renders no textarea of its own, only the floating wand.\n *\n * <textarea ref={ref} value={body} onChange={(e) => setBody(e.target.value)} />\n * <MagicWand target={ref} value={body} onValueChange={setBody} actions={…} />\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\n/** A viewport rectangle, the shape `getBoundingClientRect()` returns. */\nexport type MagicWandRect = { left: number; top: number; width: number; height: number };\n\n/**\n * How MagicWand reads a selection out of an editor it does not own — a code\n * editor, a contenteditable, anything that is not a plain textarea.\n *\n * Read-only on purpose. The write goes through `onValueChange`, like every other\n * edit, so the host's `value` stays the one source of truth and the editor is\n * updated the way it always is: from that value.\n */\nexport interface MagicWandTargetAdapter {\n /**\n * The editor's element. MagicWand listens on it for `mouseup` / `keyup` /\n * `select` to know the selection may have changed, and treats a click inside\n * it as \"not away\" for `autoHide`.\n */\n element: RefObject<HTMLElement | null> | HTMLElement | null;\n /** The selection as offsets into `value`. `null` or `start === end` hides the wand. */\n getSelection: () => { start: number; end: number } | null;\n /** Where the selection is on screen. Default: the top-centre of `element`. */\n getSelectionRect?: () => MagicWandRect | null;\n}\n\n/** A host-owned textarea / input by ref, or an adapter for anything else. */\nexport type MagicWandTarget =\n | RefObject<HTMLTextAreaElement | HTMLInputElement | null>\n | MagicWandTargetAdapter;\n\nexport interface MagicWandProps {\n /** Controlled value — of the built-in textarea, or of `target`. */\n value: string;\n /** Called on every edit, and with the result of an action. */\n onValueChange: (v: string) => void;\n /** Action list. */\n actions: MagicWandAction[];\n /**\n * Act on a textarea / input / editor the host renders, instead of rendering\n * one. A ref to a textarea or input works as is; anything else takes a\n * `MagicWandTargetAdapter`. `value` must still be the target's current value.\n */\n target?: MagicWandTarget;\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 (built-in textarea only). */\n rows?: number;\n /** Placeholder (built-in textarea only). */\n placeholder?: string;\n /** Called after an action runs successfully. */\n onAction?: (action: MagicWandAction, selection: MagicWandSelection, replacement: string) => void;\n}\n\nfunction isAdapter(target: MagicWandTarget): target is MagicWandTargetAdapter {\n return typeof (target as MagicWandTargetAdapter).getSelection === \"function\";\n}\n\nfunction targetElement(target: MagicWandTarget | undefined): HTMLElement | null {\n if (!target) return null;\n if (!isAdapter(target)) return target.current;\n const el = target.element;\n if (!el) return null;\n return el instanceof HTMLElement ? el : el.current;\n}\n\ntype TextField = HTMLTextAreaElement | HTMLInputElement;\n\n/** The selection and the viewport point the wand should sit above, or null. */\nfunction readTarget(\n target: MagicWandTarget,\n): { start: number; end: number; point: { x: number; y: number } } | null {\n if (!isAdapter(target)) {\n const field = target.current as TextField | null;\n if (!field) return null;\n const start = field.selectionStart ?? 0;\n const end = field.selectionEnd ?? 0;\n if (start === end) return null;\n return { start, end, point: selectionPoint(field, start, end) };\n }\n\n const range = target.getSelection();\n if (!range || range.start === range.end) return null;\n const rect = target.getSelectionRect?.() ?? targetElement(target)?.getBoundingClientRect() ?? null;\n const point = rect ? { x: rect.left + rect.width / 2, y: rect.top } : { x: 0, y: 0 };\n return { start: Math.min(range.start, range.end), end: Math.max(range.start, range.end), point };\n}\n\nexport function MagicWand({\n value,\n onValueChange,\n actions,\n target,\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 external = target !== undefined;\n // Latest value + target for the listeners, which attach once per element.\n const latest = useRef({ value, target });\n latest.current = { value, target };\n\n const hide = useCallback(() => {\n setSel(null);\n setPos(null);\n }, []);\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 hide();\n return;\n }\n const text = ta.value.slice(start, end);\n setSel({ start, end, text });\n // The built-in wand is positioned inside this component's `relative` box.\n const point = selectionPoint(ta, start, end);\n const box = (ta.offsetParent as HTMLElement | null)?.getBoundingClientRect() ?? ta.getBoundingClientRect();\n setPos({ x: point.x - box.left, y: point.y - box.top });\n }, [hide]);\n\n const measureTarget = useCallback(() => {\n const { target: t, value: v } = latest.current;\n if (!t) return;\n const read = readTarget(t);\n if (!read) {\n hide();\n return;\n }\n setSel({ start: read.start, end: read.end, text: v.slice(read.start, read.end) });\n setPos(read.point);\n }, [hide]);\n\n // The target's element is only known after the host renders it, so it is\n // re-resolved after every render and the listeners move if it changes.\n const [targetEl, setTargetEl] = useState<HTMLElement | null>(null);\n useEffect(() => {\n const next = targetElement(target);\n if (next !== targetEl) setTargetEl(next);\n });\n\n useEffect(() => {\n if (!targetEl) return;\n const events = [\"mouseup\", \"keyup\", \"select\"] as const;\n for (const e of events) targetEl.addEventListener(e, measureTarget);\n return () => {\n for (const e of events) targetEl.removeEventListener(e, measureTarget);\n };\n }, [targetEl, measureTarget]);\n\n useEffect(() => {\n if (!autoHide) return;\n window.addEventListener(\"scroll\", hide, true);\n return () => window.removeEventListener(\"scroll\", hide, true);\n }, [autoHide, hide]);\n\n useEffect(() => {\n if (!autoHide) return;\n const onDocMouseDown = (e: MouseEvent) => {\n const node = e.target as Node;\n if (wandRef.current?.contains(node)) return;\n if (taRef.current?.contains(node)) return;\n if (targetElement(latest.current.target)?.contains(node)) return;\n hide();\n };\n document.addEventListener(\"mousedown\", onDocMouseDown);\n return () => document.removeEventListener(\"mousedown\", onDocMouseDown);\n }, [autoHide, hide]);\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 hide();\n }\n };\n\n const wand =\n sel && pos ? (\n <Wand\n ref={wandRef}\n pos={pos}\n fixed={external}\n actions={actions}\n appearance={appearance}\n busy={busy}\n onAction={handleAction}\n selectionLength={sel.text.length}\n />\n ) : null;\n\n if (external) {\n return wand ? <Portal>{wand}</Portal> : null;\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 {wand}\n </div>\n );\n}\n\nfunction Wand({\n ref,\n pos,\n fixed,\n actions,\n appearance,\n busy,\n onAction,\n selectionLength,\n}: {\n ref: React.RefObject<HTMLDivElement | null>;\n pos: { x: number; y: number };\n /** Viewport-positioned (portalled, for an external target) rather than inside the wrapper. */\n fixed: boolean;\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 data-react-fancy-magic-wand=\"\"\n className={`${fixed ? \"fixed z-50\" : \"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-[color:var(--fancy-surface,var(--color-white))] px-1.5 py-1 shadow-lg ring-1 ring-violet-200 dark:border-violet-700 dark:bg-[color:var(--fancy-surface-dark,var(--color-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 data-react-fancy-magic-wand-action={a.id}\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-[color:var(--fancy-surface-muted,var(--color-zinc-100))] px-1 text-[9px] font-medium text-zinc-500 dark:bg-[color:var(--fancy-surface-muted-dark,var(--color-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-[color:var(--fancy-surface,var(--color-white))] dark:border-violet-700 dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\" />\n </div>\n </div>\n );\n}\n\n/**\n * Caret/selection geometry: mirror the field into a hidden div and measure the\n * selected substring's bounding rect. Returns the VIEWPORT point at the top\n * centre of the selection. Not perfect for resized textareas, but works for\n * typical chat composers.\n */\nfunction selectionPoint(ta: TextField, start: number, end: number): { x: number; y: number } {\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 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 return {\n x: taRect.left + offsetX + spanRect.width / 2,\n y: taRect.top + offsetY - ta.scrollTop,\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/components/MagicWand/MagicWand.tsx"],"names":["useRef","useState","useCallback","useEffect","jsx","Portal","jsxs","Textarea","Badge","Button"],"mappings":";;;;;;;;;AAwGA,SAAS,UAAU,MAAA,EAA2D;AAC5E,EAAA,OAAO,OAAQ,OAAkC,YAAA,KAAiB,UAAA;AACpE;AAEA,SAAS,cAAc,MAAA,EAAyD;AAC9E,EAAA,IAAI,CAAC,QAAQ,OAAO,IAAA;AACpB,EAAA,IAAI,CAAC,SAAA,CAAU,MAAM,CAAA,SAAU,MAAA,CAAO,OAAA;AACtC,EAAA,MAAM,KAAK,MAAA,CAAO,OAAA;AAClB,EAAA,IAAI,CAAC,IAAI,OAAO,IAAA;AAChB,EAAA,OAAO,EAAA,YAAc,WAAA,GAAc,EAAA,GAAK,EAAA,CAAG,OAAA;AAC7C;AAKA,SAAS,WACP,MAAA,EACwE;AACxE,EAAA,IAAI,CAAC,SAAA,CAAU,MAAM,CAAA,EAAG;AACtB,IAAA,MAAM,QAAQ,MAAA,CAAO,OAAA;AACrB,IAAA,IAAI,CAAC,OAAO,OAAO,IAAA;AACnB,IAAA,MAAM,KAAA,GAAQ,MAAM,cAAA,IAAkB,CAAA;AACtC,IAAA,MAAM,GAAA,GAAM,MAAM,YAAA,IAAgB,CAAA;AAClC,IAAA,IAAI,KAAA,KAAU,KAAK,OAAO,IAAA;AAC1B,IAAA,OAAO,EAAE,OAAO,GAAA,EAAK,KAAA,EAAO,eAAe,KAAA,EAAO,KAAA,EAAO,GAAG,CAAA,EAAE;AAAA,EAChE;AAEA,EAAA,MAAM,KAAA,GAAQ,OAAO,YAAA,EAAa;AAClC,EAAA,IAAI,CAAC,KAAA,IAAS,KAAA,CAAM,KAAA,KAAU,KAAA,CAAM,KAAK,OAAO,IAAA;AAChD,EAAA,MAAM,IAAA,GAAO,OAAO,gBAAA,IAAmB,IAAK,cAAc,MAAM,CAAA,EAAG,uBAAsB,IAAK,IAAA;AAC9F,EAAA,MAAM,QAAQ,IAAA,GAAO,EAAE,CAAA,EAAG,IAAA,CAAK,OAAO,IAAA,CAAK,KAAA,GAAQ,CAAA,EAAG,CAAA,EAAG,KAAK,GAAA,EAAI,GAAI,EAAE,CAAA,EAAG,CAAA,EAAG,GAAG,CAAA,EAAE;AACnF,EAAA,OAAO,EAAE,KAAA,EAAO,IAAA,CAAK,GAAA,CAAI,KAAA,CAAM,OAAO,KAAA,CAAM,GAAG,CAAA,EAAG,GAAA,EAAK,KAAK,GAAA,CAAI,KAAA,CAAM,OAAO,KAAA,CAAM,GAAG,GAAG,KAAA,EAAM;AACjG;AAEO,SAAS,SAAA,CAAU;AAAA,EACxB,KAAA;AAAA,EACA,aAAA;AAAA,EACA,OAAA;AAAA,EACA,MAAA;AAAA,EACA,UAAA,GAAa,UAAA;AAAA,EACb,QAAA,GAAW,IAAA;AAAA,EACX,IAAA,GAAO,CAAA;AAAA,EACP,WAAA;AAAA,EACA;AACF,CAAA,EAAmB;AACjB,EAAA,MAAM,KAAA,GAAQA,aAA4B,IAAI,CAAA;AAC9C,EAAA,MAAM,OAAA,GAAUA,aAAuB,IAAI,CAAA;AAC3C,EAAA,MAAM,CAAC,GAAA,EAAK,MAAM,CAAA,GAAIC,eAAoC,IAAI,CAAA;AAC9D,EAAA,MAAM,CAAC,GAAA,EAAK,MAAM,CAAA,GAAIA,eAA0C,IAAI,CAAA;AACpE,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAIA,eAAwB,IAAI,CAAA;AAEpD,EAAA,MAAM,WAAW,MAAA,KAAW,MAAA;AAE5B,EAAA,MAAM,MAAA,GAASD,YAAA,CAAO,EAAE,KAAA,EAAO,QAAQ,CAAA;AACvC,EAAA,MAAA,CAAO,OAAA,GAAU,EAAE,KAAA,EAAO,MAAA,EAAO;AAEjC,EAAA,MAAM,IAAA,GAAOE,kBAAY,MAAM;AAC7B,IAAA,MAAA,CAAO,IAAI,CAAA;AACX,IAAA,MAAA,CAAO,IAAI,CAAA;AAAA,EACb,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,gBAAA,GAAmBA,kBAAY,MAAM;AACzC,IAAA,MAAM,KAAK,KAAA,CAAM,OAAA;AACjB,IAAA,IAAI,CAAC,EAAA,EAAI;AACT,IAAA,MAAM,KAAA,GAAQ,GAAG,cAAA,IAAkB,CAAA;AACnC,IAAA,MAAM,GAAA,GAAM,GAAG,YAAA,IAAgB,CAAA;AAC/B,IAAA,IAAI,UAAU,GAAA,EAAK;AACjB,MAAA,IAAA,EAAK;AACL,MAAA;AAAA,IACF;AACA,IAAA,MAAM,IAAA,GAAO,EAAA,CAAG,KAAA,CAAM,KAAA,CAAM,OAAO,GAAG,CAAA;AACtC,IAAA,MAAA,CAAO,EAAE,KAAA,EAAO,GAAA,EAAK,IAAA,EAAM,CAAA;AAE3B,IAAA,MAAM,KAAA,GAAQ,cAAA,CAAe,EAAA,EAAI,KAAA,EAAO,GAAG,CAAA;AAC3C,IAAA,MAAM,MAAO,EAAA,CAAG,YAAA,EAAqC,qBAAA,EAAsB,IAAK,GAAG,qBAAA,EAAsB;AACzG,IAAA,MAAA,CAAO,EAAE,CAAA,EAAG,KAAA,CAAM,CAAA,GAAI,GAAA,CAAI,IAAA,EAAM,CAAA,EAAG,KAAA,CAAM,CAAA,GAAI,GAAA,CAAI,GAAA,EAAK,CAAA;AAAA,EACxD,CAAA,EAAG,CAAC,IAAI,CAAC,CAAA;AAET,EAAA,MAAM,aAAA,GAAgBA,kBAAY,MAAM;AACtC,IAAA,MAAM,EAAE,MAAA,EAAQ,CAAA,EAAG,KAAA,EAAO,CAAA,KAAM,MAAA,CAAO,OAAA;AACvC,IAAA,IAAI,CAAC,CAAA,EAAG;AACR,IAAA,MAAM,IAAA,GAAO,WAAW,CAAC,CAAA;AACzB,IAAA,IAAI,CAAC,IAAA,EAAM;AACT,MAAA,IAAA,EAAK;AACL,MAAA;AAAA,IACF;AACA,IAAA,MAAA,CAAO,EAAE,KAAA,EAAO,IAAA,CAAK,KAAA,EAAO,KAAK,IAAA,CAAK,GAAA,EAAK,IAAA,EAAM,CAAA,CAAE,MAAM,IAAA,CAAK,KAAA,EAAO,IAAA,CAAK,GAAG,GAAG,CAAA;AAChF,IAAA,MAAA,CAAO,KAAK,KAAK,CAAA;AAAA,EACnB,CAAA,EAAG,CAAC,IAAI,CAAC,CAAA;AAIT,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAID,eAA6B,IAAI,CAAA;AACjE,EAAAE,eAAA,CAAU,MAAM;AACd,IAAA,MAAM,IAAA,GAAO,cAAc,MAAM,CAAA;AACjC,IAAA,IAAI,IAAA,KAAS,QAAA,EAAU,WAAA,CAAY,IAAI,CAAA;AAAA,EACzC,CAAC,CAAA;AAED,EAAAA,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,QAAA,EAAU;AACf,IAAA,MAAM,MAAA,GAAS,CAAC,SAAA,EAAW,OAAA,EAAS,QAAQ,CAAA;AAC5C,IAAA,KAAA,MAAW,CAAA,IAAK,MAAA,EAAQ,QAAA,CAAS,gBAAA,CAAiB,GAAG,aAAa,CAAA;AAClE,IAAA,OAAO,MAAM;AACX,MAAA,KAAA,MAAW,CAAA,IAAK,MAAA,EAAQ,QAAA,CAAS,mBAAA,CAAoB,GAAG,aAAa,CAAA;AAAA,IACvE,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,QAAA,EAAU,aAAa,CAAC,CAAA;AAE5B,EAAAA,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,QAAA,EAAU;AACf,IAAA,MAAA,CAAO,gBAAA,CAAiB,QAAA,EAAU,IAAA,EAAM,IAAI,CAAA;AAC5C,IAAA,OAAO,MAAM,MAAA,CAAO,mBAAA,CAAoB,QAAA,EAAU,MAAM,IAAI,CAAA;AAAA,EAC9D,CAAA,EAAG,CAAC,QAAA,EAAU,IAAI,CAAC,CAAA;AAEnB,EAAAA,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,QAAA,EAAU;AACf,IAAA,MAAM,cAAA,GAAiB,CAAC,CAAA,KAAkB;AACxC,MAAA,MAAM,OAAO,CAAA,CAAE,MAAA;AACf,MAAA,IAAI,OAAA,CAAQ,OAAA,EAAS,QAAA,CAAS,IAAI,CAAA,EAAG;AACrC,MAAA,IAAI,KAAA,CAAM,OAAA,EAAS,QAAA,CAAS,IAAI,CAAA,EAAG;AACnC,MAAA,IAAI,cAAc,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,EAAG,QAAA,CAAS,IAAI,CAAA,EAAG;AAC1D,MAAA,IAAA,EAAK;AAAA,IACP,CAAA;AACA,IAAA,QAAA,CAAS,gBAAA,CAAiB,aAAa,cAAc,CAAA;AACrD,IAAA,OAAO,MAAM,QAAA,CAAS,mBAAA,CAAoB,WAAA,EAAa,cAAc,CAAA;AAAA,EACvE,CAAA,EAAG,CAAC,QAAA,EAAU,IAAI,CAAC,CAAA;AAEnB,EAAA,MAAM,YAAA,GAAe,OAAO,MAAA,KAA4B;AACtD,IAAA,IAAI,CAAC,GAAA,EAAK;AACV,IAAA,OAAA,CAAQ,OAAO,EAAE,CAAA;AACjB,IAAA,IAAI;AACF,MAAA,MAAM,cAAc,MAAM,MAAA,CAAO,GAAA,CAAI,GAAA,CAAI,MAAM,GAAG,CAAA;AAClD,MAAA,MAAM,IAAA,GAAO,KAAA,CAAM,KAAA,CAAM,CAAA,EAAG,GAAA,CAAI,KAAK,CAAA,GAAI,WAAA,GAAc,KAAA,CAAM,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA;AAC1E,MAAA,aAAA,CAAc,IAAI,CAAA;AAClB,MAAA,QAAA,GAAW,MAAA,EAAQ,KAAK,WAAW,CAAA;AAAA,IACrC,CAAA,SAAE;AACA,MAAA,OAAA,CAAQ,IAAI,CAAA;AACZ,MAAA,IAAA,EAAK;AAAA,IACP;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,IAAA,GACJ,OAAO,GAAA,mBACLC,cAAA;AAAA,IAAC,IAAA;AAAA,IAAA;AAAA,MACC,GAAA,EAAK,OAAA;AAAA,MACL,GAAA;AAAA,MACA,KAAA,EAAO,QAAA;AAAA,MACP,OAAA;AAAA,MACA,UAAA;AAAA,MACA,IAAA;AAAA,MACA,QAAA,EAAU,YAAA;AAAA,MACV,eAAA,EAAiB,IAAI,IAAA,CAAK;AAAA;AAAA,GAC5B,GACE,IAAA;AAEN,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,OAAO,IAAA,mBAAOA,cAAA,CAACC,wBAAA,EAAA,EAAQ,QAAA,EAAA,IAAA,EAAK,CAAA,GAAY,IAAA;AAAA,EAC1C;AAEA,EAAA,uBACEC,eAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,UAAA,EACb,QAAA,EAAA;AAAA,oBAAAF,cAAA;AAAA,MAACG,0BAAA;AAAA,MAAA;AAAA,QACC,GAAA,EAAK,KAAA;AAAA,QACL,KAAA;AAAA,QACA,aAAA;AAAA,QACA,QAAA,EAAU,gBAAA;AAAA,QACV,OAAA,EAAS,gBAAA;AAAA,QACT,SAAA,EAAW,gBAAA;AAAA,QACX,IAAA;AAAA,QACA;AAAA;AAAA,KACF;AAAA,IACC;AAAA,GAAA,EACH,CAAA;AAEJ;AAEA,SAAS,IAAA,CAAK;AAAA,EACZ,GAAA;AAAA,EACA,GAAA;AAAA,EACA,KAAA;AAAA,EACA,OAAA;AAAA,EACA,UAAA;AAAA,EACA,IAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAA,EAUG;AACD,EAAA,uBACED,eAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAA;AAAA,MACA,6BAAA,EAA4B,EAAA;AAAA,MAC5B,SAAA,EAAW,CAAA,EAAG,KAAA,GAAQ,YAAA,GAAe,eAAe,CAAA,mCAAA,CAAA;AAAA,MACpD,KAAA,EAAO,EAAE,IAAA,EAAM,GAAA,CAAI,GAAG,GAAA,EAAK,GAAA,CAAI,IAAI,CAAA,EAAE;AAAA,MAErC,QAAA,EAAA;AAAA,wBAAAA,eAAA,CAAC,KAAA,EAAA,EAAI,WAAU,2QAAA,EACb,QAAA,EAAA;AAAA,0BAAAA,eAAA,CAAC,MAAA,EAAA,EAAK,WAAU,yGAAA,EACd,QAAA,EAAA;AAAA,4BAAAF,cAAA,CAAC,MAAA,EAAA,EAAK,aAAA,EAAW,IAAA,EAAC,QAAA,EAAA,QAAA,EAAC,CAAA;AAAA,YAClB,UAAA,KAAe,UAAA,oBACdE,eAAA,CAACE,uBAAA,EAAA,EAAM,OAAM,QAAA,EAAU,QAAA,EAAA;AAAA,cAAA,eAAA;AAAA,cAAgB;AAAA,aAAA,EAAM;AAAA,WAAA,EAEjD,CAAA;AAAA,UACC,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,qBACZF,eAAA;AAAA,YAACG,wBAAA;AAAA,YAAA;AAAA,cAEC,IAAA,EAAK,IAAA;AAAA,cACL,OAAA,EAAQ,OAAA;AAAA,cACR,sCAAoC,CAAA,CAAE,EAAA;AAAA,cACtC,OAAA,EAAS,MAAM,QAAA,CAAS,CAAC,CAAA;AAAA,cACzB,UAAU,IAAA,KAAS,IAAA;AAAA,cACnB,OAAO,CAAA,CAAE,IAAA;AAAA,cAER,QAAA,EAAA;AAAA,gBAAA,IAAA,KAAS,CAAA,CAAE,KAAK,QAAA,GAAM,UAAA,KAAe,SAAS,CAAA,CAAE,KAAA,CAAM,CAAC,CAAA,GAAI,CAAA,CAAE,KAAA;AAAA,gBAC7D,UAAA,KAAe,cAAc,CAAA,CAAE,GAAA,mCAC7B,MAAA,EAAA,EAAK,SAAA,EAAU,+LAAA,EACb,QAAA,EAAA,CAAA,CAAE,GAAA,EACL;AAAA;AAAA,aAAA;AAAA,YAZG,CAAA,CAAE;AAAA,WAeV;AAAA,SAAA,EACH,CAAA;AAAA,wBACAL,cAAA,CAAC,SAAI,SAAA,EAAU,6DAAA,EACb,yCAAC,KAAA,EAAA,EAAI,SAAA,EAAU,mMAAkM,CAAA,EACnN;AAAA;AAAA;AAAA,GACF;AAEJ;AAQA,SAAS,cAAA,CAAe,EAAA,EAAe,KAAA,EAAe,GAAA,EAAuC;AAC3F,EAAA,MAAM,GAAA,GAAM,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AACxC,EAAA,MAAM,KAAA,GAAQ,iBAAiB,EAAE,CAAA;AACjC,EAAA,MAAM,KAAA,GAAQ;AAAA,IACZ,WAAA;AAAA,IACA,OAAA;AAAA,IACA,QAAA;AAAA,IACA,WAAA;AAAA,IACA,WAAA;AAAA,IACA,gBAAA;AAAA,IACA,kBAAA;AAAA,IACA,mBAAA;AAAA,IACA,iBAAA;AAAA,IACA,YAAA;AAAA,IACA,cAAA;AAAA,IACA,eAAA;AAAA,IACA,aAAA;AAAA,IACA,WAAA;AAAA,IACA,aAAA;AAAA,IACA,YAAA;AAAA,IACA,aAAA;AAAA,IACA,UAAA;AAAA,IACA,gBAAA;AAAA,IACA,YAAA;AAAA,IACA,YAAA;AAAA,IACA,WAAA;AAAA,IACA,eAAA;AAAA,IACA,YAAA;AAAA,IACA,eAAA;AAAA,IACA,aAAA;AAAA,IACA;AAAA,GACF;AACA,EAAA,KAAA,MAAW,CAAA,IAAK,OAAQ,GAAA,CAAI,MAAc,CAAC,CAAA,GAAK,MAAc,CAAC,CAAA;AAC/D,EAAA,GAAA,CAAI,MAAM,QAAA,GAAW,UAAA;AACrB,EAAA,GAAA,CAAI,MAAM,GAAA,GAAM,SAAA;AAChB,EAAA,GAAA,CAAI,MAAM,IAAA,GAAO,SAAA;AACjB,EAAA,GAAA,CAAI,MAAM,UAAA,GAAa,UAAA;AACvB,EAAA,GAAA,CAAI,MAAM,QAAA,GAAW,YAAA;AAErB,EAAA,MAAM,QAAQ,EAAA,CAAG,KAAA;AACjB,EAAA,GAAA,CAAI,WAAA,GAAc,KAAA,CAAM,SAAA,CAAU,CAAA,EAAG,KAAK,CAAA;AAC1C,EAAA,MAAM,SAAA,GAAY,QAAA,CAAS,aAAA,CAAc,MAAM,CAAA;AAC/C,EAAA,SAAA,CAAU,WAAA,GAAc,KAAA,CAAM,SAAA,CAAU,KAAA,EAAO,GAAG,CAAA,IAAK,GAAA;AACvD,EAAA,GAAA,CAAI,YAAY,SAAS,CAAA;AAEzB,EAAA,QAAA,CAAS,IAAA,CAAK,YAAY,GAAG,CAAA;AAC7B,EAAA,MAAM,MAAA,GAAS,GAAG,qBAAA,EAAsB;AACxC,EAAA,MAAM,QAAA,GAAW,UAAU,qBAAA,EAAsB;AACjD,EAAA,MAAM,OAAA,GAAU,IAAI,qBAAA,EAAsB;AAC1C,EAAA,MAAM,OAAA,GAAU,QAAA,CAAS,IAAA,GAAO,OAAA,CAAQ,IAAA;AACxC,EAAA,MAAM,OAAA,GAAU,QAAA,CAAS,GAAA,GAAM,OAAA,CAAQ,GAAA;AACvC,EAAA,QAAA,CAAS,IAAA,CAAK,YAAY,GAAG,CAAA;AAE7B,EAAA,OAAO;AAAA,IACL,CAAA,EAAG,MAAA,CAAO,IAAA,GAAO,OAAA,GAAU,SAAS,KAAA,GAAQ,CAAA;AAAA,IAC5C,CAAA,EAAG,MAAA,CAAO,GAAA,GAAM,OAAA,GAAU,EAAA,CAAG;AAAA,GAC/B;AACF","file":"chunk-AWIUXZTG.cjs","sourcesContent":["import { useCallback, useEffect, useRef, useState, type RefObject } from \"react\";\nimport { Button } from \"../Button\";\nimport { Badge } from \"../Badge\";\nimport { Portal } from \"../Portal\";\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 * Pass `target` to act on a textarea, input or editor the HOST renders instead:\n * MagicWand then renders no textarea of its own, only the floating wand.\n *\n * <textarea ref={ref} value={body} onChange={(e) => setBody(e.target.value)} />\n * <MagicWand target={ref} value={body} onValueChange={setBody} actions={…} />\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\n/** A viewport rectangle, the shape `getBoundingClientRect()` returns. */\nexport type MagicWandRect = { left: number; top: number; width: number; height: number };\n\n/**\n * How MagicWand reads a selection out of an editor it does not own — a code\n * editor, a contenteditable, anything that is not a plain textarea.\n *\n * Read-only on purpose. The write goes through `onValueChange`, like every other\n * edit, so the host's `value` stays the one source of truth and the editor is\n * updated the way it always is: from that value.\n */\nexport interface MagicWandTargetAdapter {\n /**\n * The editor's element. MagicWand listens on it for `mouseup` / `keyup` /\n * `select` to know the selection may have changed, and treats a click inside\n * it as \"not away\" for `autoHide`.\n */\n element: RefObject<HTMLElement | null> | HTMLElement | null;\n /** The selection as offsets into `value`. `null` or `start === end` hides the wand. */\n getSelection: () => { start: number; end: number } | null;\n /** Where the selection is on screen. Default: the top-centre of `element`. */\n getSelectionRect?: () => MagicWandRect | null;\n}\n\n/** A host-owned textarea / input by ref, or an adapter for anything else. */\nexport type MagicWandTarget =\n | RefObject<HTMLTextAreaElement | HTMLInputElement | null>\n | MagicWandTargetAdapter;\n\nexport interface MagicWandProps {\n /** Controlled value — of the built-in textarea, or of `target`. */\n value: string;\n /** Called on every edit, and with the result of an action. */\n onValueChange: (v: string) => void;\n /** Action list. */\n actions: MagicWandAction[];\n /**\n * Act on a textarea / input / editor the host renders, instead of rendering\n * one. A ref to a textarea or input works as is; anything else takes a\n * `MagicWandTargetAdapter`. `value` must still be the target's current value.\n */\n target?: MagicWandTarget;\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 (built-in textarea only). */\n rows?: number;\n /** Placeholder (built-in textarea only). */\n placeholder?: string;\n /** Called after an action runs successfully. */\n onAction?: (action: MagicWandAction, selection: MagicWandSelection, replacement: string) => void;\n}\n\nfunction isAdapter(target: MagicWandTarget): target is MagicWandTargetAdapter {\n return typeof (target as MagicWandTargetAdapter).getSelection === \"function\";\n}\n\nfunction targetElement(target: MagicWandTarget | undefined): HTMLElement | null {\n if (!target) return null;\n if (!isAdapter(target)) return target.current;\n const el = target.element;\n if (!el) return null;\n return el instanceof HTMLElement ? el : el.current;\n}\n\ntype TextField = HTMLTextAreaElement | HTMLInputElement;\n\n/** The selection and the viewport point the wand should sit above, or null. */\nfunction readTarget(\n target: MagicWandTarget,\n): { start: number; end: number; point: { x: number; y: number } } | null {\n if (!isAdapter(target)) {\n const field = target.current as TextField | null;\n if (!field) return null;\n const start = field.selectionStart ?? 0;\n const end = field.selectionEnd ?? 0;\n if (start === end) return null;\n return { start, end, point: selectionPoint(field, start, end) };\n }\n\n const range = target.getSelection();\n if (!range || range.start === range.end) return null;\n const rect = target.getSelectionRect?.() ?? targetElement(target)?.getBoundingClientRect() ?? null;\n const point = rect ? { x: rect.left + rect.width / 2, y: rect.top } : { x: 0, y: 0 };\n return { start: Math.min(range.start, range.end), end: Math.max(range.start, range.end), point };\n}\n\nexport function MagicWand({\n value,\n onValueChange,\n actions,\n target,\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 external = target !== undefined;\n // Latest value + target for the listeners, which attach once per element.\n const latest = useRef({ value, target });\n latest.current = { value, target };\n\n const hide = useCallback(() => {\n setSel(null);\n setPos(null);\n }, []);\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 hide();\n return;\n }\n const text = ta.value.slice(start, end);\n setSel({ start, end, text });\n // The built-in wand is positioned inside this component's `relative` box.\n const point = selectionPoint(ta, start, end);\n const box = (ta.offsetParent as HTMLElement | null)?.getBoundingClientRect() ?? ta.getBoundingClientRect();\n setPos({ x: point.x - box.left, y: point.y - box.top });\n }, [hide]);\n\n const measureTarget = useCallback(() => {\n const { target: t, value: v } = latest.current;\n if (!t) return;\n const read = readTarget(t);\n if (!read) {\n hide();\n return;\n }\n setSel({ start: read.start, end: read.end, text: v.slice(read.start, read.end) });\n setPos(read.point);\n }, [hide]);\n\n // The target's element is only known after the host renders it, so it is\n // re-resolved after every render and the listeners move if it changes.\n const [targetEl, setTargetEl] = useState<HTMLElement | null>(null);\n useEffect(() => {\n const next = targetElement(target);\n if (next !== targetEl) setTargetEl(next);\n });\n\n useEffect(() => {\n if (!targetEl) return;\n const events = [\"mouseup\", \"keyup\", \"select\"] as const;\n for (const e of events) targetEl.addEventListener(e, measureTarget);\n return () => {\n for (const e of events) targetEl.removeEventListener(e, measureTarget);\n };\n }, [targetEl, measureTarget]);\n\n useEffect(() => {\n if (!autoHide) return;\n window.addEventListener(\"scroll\", hide, true);\n return () => window.removeEventListener(\"scroll\", hide, true);\n }, [autoHide, hide]);\n\n useEffect(() => {\n if (!autoHide) return;\n const onDocMouseDown = (e: MouseEvent) => {\n const node = e.target as Node;\n if (wandRef.current?.contains(node)) return;\n if (taRef.current?.contains(node)) return;\n if (targetElement(latest.current.target)?.contains(node)) return;\n hide();\n };\n document.addEventListener(\"mousedown\", onDocMouseDown);\n return () => document.removeEventListener(\"mousedown\", onDocMouseDown);\n }, [autoHide, hide]);\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 hide();\n }\n };\n\n const wand =\n sel && pos ? (\n <Wand\n ref={wandRef}\n pos={pos}\n fixed={external}\n actions={actions}\n appearance={appearance}\n busy={busy}\n onAction={handleAction}\n selectionLength={sel.text.length}\n />\n ) : null;\n\n if (external) {\n return wand ? <Portal>{wand}</Portal> : null;\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 {wand}\n </div>\n );\n}\n\nfunction Wand({\n ref,\n pos,\n fixed,\n actions,\n appearance,\n busy,\n onAction,\n selectionLength,\n}: {\n ref: React.RefObject<HTMLDivElement | null>;\n pos: { x: number; y: number };\n /** Viewport-positioned (portalled, for an external target) rather than inside the wrapper. */\n fixed: boolean;\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 data-react-fancy-magic-wand=\"\"\n className={`${fixed ? \"fixed z-50\" : \"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-[color:var(--fancy-surface,var(--color-white))] px-1.5 py-1 shadow-lg ring-1 ring-violet-200 dark:border-violet-700 dark:bg-[color:var(--fancy-surface-dark,var(--color-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 data-react-fancy-magic-wand-action={a.id}\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-[color:var(--fancy-surface-muted,var(--color-zinc-100))] px-1 text-[9px] font-medium text-zinc-500 dark:bg-[color:var(--fancy-surface-muted-dark,var(--color-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-[color:var(--fancy-surface,var(--color-white))] dark:border-violet-700 dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\" />\n </div>\n </div>\n );\n}\n\n/**\n * Caret/selection geometry: mirror the field into a hidden div and measure the\n * selected substring's bounding rect. Returns the VIEWPORT point at the top\n * centre of the selection. Not perfect for resized textareas, but works for\n * typical chat composers.\n */\nfunction selectionPoint(ta: TextField, start: number, end: number): { x: number; y: number } {\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 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 return {\n x: taRect.left + offsetX + spanRect.width / 2,\n y: taRect.top + offsetY - ta.scrollTop,\n };\n}\n"]}
@@ -2,10 +2,10 @@
2
2
 
3
3
  var chunkVQJ5LJJH_cjs = require('./chunk-VQJ5LJJH.cjs');
4
4
  var chunk4GOOSZEK_cjs = require('./chunk-4GOOSZEK.cjs');
5
- var chunkR3EJNGJV_cjs = require('./chunk-R3EJNGJV.cjs');
5
+ var chunkJGEFIWDA_cjs = require('./chunk-JGEFIWDA.cjs');
6
6
  var chunkG7227RTY_cjs = require('./chunk-G7227RTY.cjs');
7
7
  var chunkL3V3BYVS_cjs = require('./chunk-L3V3BYVS.cjs');
8
- var chunk3U7PUZLI_cjs = require('./chunk-3U7PUZLI.cjs');
8
+ var chunk2KQVBD7K_cjs = require('./chunk-2KQVBD7K.cjs');
9
9
  var chunkSNHU2AI4_cjs = require('./chunk-SNHU2AI4.cjs');
10
10
  var chunkZU5Q77OT_cjs = require('./chunk-ZU5Q77OT.cjs');
11
11
  var react = require('react');
@@ -158,7 +158,7 @@ function EditorContent({ className, maxHeight: maxHeightProp, sourceClassName })
158
158
  }
159
159
  }, [contentRef, _initialHtml, showSource]);
160
160
  const extClasses = react.useMemo(
161
- () => chunkR3EJNGJV_cjs.extensionEditorClasses(extensions),
161
+ () => chunkJGEFIWDA_cjs.extensionEditorClasses(extensions),
162
162
  [extensions]
163
163
  );
164
164
  if (showSource) {
@@ -190,7 +190,7 @@ function EditorContent({ className, maxHeight: maxHeightProp, sourceClassName })
190
190
  className: chunkZU5Q77OT_cjs.cn(
191
191
  "min-h-[120px] flex-auto overflow-y-auto px-4 py-3 text-sm outline-none",
192
192
  "focus:outline-none",
193
- chunkR3EJNGJV_cjs.proseClasses,
193
+ chunkJGEFIWDA_cjs.proseClasses,
194
194
  extClasses,
195
195
  "empty:before:content-[attr(data-placeholder)] empty:before:text-zinc-400 empty:before:pointer-events-none",
196
196
  className
@@ -201,10 +201,10 @@ function EditorContent({ className, maxHeight: maxHeightProp, sourceClassName })
201
201
  EditorContent.displayName = "EditorContent";
202
202
  function toHtml(value, outputFormat, unsafe, valueFormat = "auto") {
203
203
  if (!value) return { html: "", source: null };
204
- const sanitize = (html) => unsafe ? html : chunk3U7PUZLI_cjs.sanitizeHtml(html);
205
- const format = valueFormat !== "auto" ? valueFormat : outputFormat === "html" ? "html" : chunkR3EJNGJV_cjs.detectFormat(value);
204
+ const sanitize = (html) => unsafe ? html : chunk2KQVBD7K_cjs.sanitizeHtml(html);
205
+ const format = valueFormat !== "auto" ? valueFormat : outputFormat === "html" ? "html" : chunkJGEFIWDA_cjs.detectFormat(value);
206
206
  if (format === "html") return { html: sanitize(value), source: null };
207
- if (outputFormat === "markdown") return chunkR3EJNGJV_cjs.markdownToEditorHtml(value, sanitize);
207
+ if (outputFormat === "markdown") return chunkJGEFIWDA_cjs.markdownToEditorHtml(value, sanitize);
208
208
  return { html: sanitize(marked.marked.parse(value, { async: false }).trim()), source: null };
209
209
  }
210
210
  function EditorRoot({
@@ -264,12 +264,12 @@ function EditorRoot({
264
264
  const initialHtml = seed.html;
265
265
  const seedSource = seed.source;
266
266
  const extensions = react.useMemo(
267
- () => chunkR3EJNGJV_cjs.mergeExtensions(instanceExtensions),
267
+ () => chunkJGEFIWDA_cjs.mergeExtensions(instanceExtensions),
268
268
  [instanceExtensions]
269
269
  );
270
270
  const getOutputValue = react.useCallback(
271
271
  (html) => {
272
- return outputFormat === "markdown" ? chunkR3EJNGJV_cjs.htmlToMarkdown(html, seedSource) : html;
272
+ return outputFormat === "markdown" ? chunkJGEFIWDA_cjs.htmlToMarkdown(html, seedSource) : html;
273
273
  },
274
274
  [outputFormat, seedSource]
275
275
  );
@@ -353,7 +353,7 @@ function EditorRoot({
353
353
  fs.fullScreen && "overflow-auto"
354
354
  ),
355
355
  children: /* @__PURE__ */ jsxRuntime.jsx(
356
- chunkR3EJNGJV_cjs.ContentRenderer,
356
+ chunkJGEFIWDA_cjs.ContentRenderer,
357
357
  {
358
358
  value,
359
359
  format: valueFormat !== "auto" ? valueFormat : outputFormat,
@@ -395,5 +395,5 @@ var Editor = Object.assign(EditorRoot, {
395
395
 
396
396
  exports.Editor = Editor;
397
397
  exports.useEditor = useEditor;
398
- //# sourceMappingURL=chunk-5YRSYS2F.cjs.map
399
- //# sourceMappingURL=chunk-5YRSYS2F.cjs.map
398
+ //# sourceMappingURL=chunk-BL6HCIP4.cjs.map
399
+ //# sourceMappingURL=chunk-BL6HCIP4.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/Editor/Editor.context.ts","../src/components/Editor/EditorSourceToggle.tsx","../src/components/Editor/EditorFullScreenToggle.tsx","../src/components/Editor/EditorToolbar.tsx","../src/components/Editor/EditorToolbarSeparator.tsx","../src/components/Editor/EditorContent.tsx","../src/components/Editor/Editor.tsx"],"names":["createContext","useContext","jsx","cn","FullScreenToggle","jsxs","Fragment","useRef","useEffect","useMemo","extensionEditorClasses","CodeView","proseClasses","sanitizeHtml","detectFormat","markdownToEditorHtml","marked","useControllableState","useFullScreen","useFieldMode","useState","mergeExtensions","useCallback","htmlToMarkdown","FullScreenProvider","ContentRenderer"],"mappings":"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","file":"chunk-5YRSYS2F.cjs","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { RefObject } from \"react\";\nimport type { RenderExtension } from \"../ContentRenderer/extensions\";\n\nexport interface EditorContextValue {\n contentRef: RefObject<HTMLDivElement | null>;\n exec: (command: string, arg?: string) => void;\n insertText: (text: string) => void;\n wrapSelection: (before: string, after: string) => void;\n outputFormat: \"html\" | \"markdown\";\n lineSpacing: number;\n placeholder?: string;\n /** Merged render extensions (global + instance). */\n extensions: RenderExtension[];\n /** Whether the raw-source view (textarea) is showing instead of the rich-text surface. */\n showSource: boolean;\n /** Toggle/set the raw-source view. */\n setShowSource: (showSource: boolean) => void;\n /** Whether the editor currently covers the viewport. */\n fullScreen: boolean;\n /** Enter/leave full screen. Goes through `onFullScreenChange` when controlled. */\n setFullScreen: (fullScreen: boolean) => void;\n /** Initial HTML content to load into the editor on mount */\n _initialHtml: string;\n /** @internal Called by EditorContent on input events */\n _onInput: () => void;\n /** @internal Raw source value (`value` in `outputFormat`), for the source textarea. */\n _sourceValue: string;\n /** @internal Commit an edit made directly in the source textarea. */\n _onSourceInput: (value: string) => void;\n}\n\nexport const EditorContext = createContext<EditorContextValue | null>(null);\n\nexport function useEditor(): EditorContextValue {\n const ctx = useContext(EditorContext);\n if (!ctx) {\n throw new Error(\"useEditor must be used within an <Editor> component\");\n }\n return ctx;\n}\n","import { cn } from \"../../utils/cn\";\nimport { useEditor } from \"./Editor.context\";\nimport type { EditorSourceToggleProps } from \"./Editor.types\";\n\n/**\n * Toolbar button that reveals the raw HTML/Markdown behind the editor. Reads\n * `showSource` / `setShowSource` from context, so it works in the default\n * toolbar and in any custom toolbar composed with `useEditor()`.\n */\nexport function EditorSourceToggle({\n className,\n icon = \"</>\",\n title = \"Source\",\n activeTitle = \"Rich text\",\n}: EditorSourceToggleProps) {\n const { showSource, setShowSource } = useEditor();\n\n return (\n <button\n type=\"button\"\n onClick={() => setShowSource(!showSource)}\n title={showSource ? activeTitle : title}\n aria-pressed={showSource}\n data-react-fancy-editor-source-toggle=\"\"\n className={cn(\n \"inline-flex h-8 items-center justify-center rounded-md px-2 font-mono text-xs transition-colors\",\n showSource\n ? \"bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]\"\n : \"hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]\",\n className,\n )}\n >\n {icon}\n </button>\n );\n}\n\nEditorSourceToggle.displayName = \"EditorSourceToggle\";\n","import { cn } from \"../../utils/cn\";\nimport { FullScreenToggle } from \"../FullScreen/FullScreenToggle\";\nimport type { EditorFullScreenToggleProps } from \"./Editor.types\";\n\n/**\n * Toolbar button that takes the editor full screen and back. The shared\n * `FullScreen.Toggle`, styled for the editor toolbar and carrying the editor's\n * own handle (`data-react-fancy-editor-fullscreen-toggle`) next to the shared\n * one (`data-react-fancy-fullscreen-toggle`).\n */\nexport function EditorFullScreenToggle({ className, enterIcon, exitIcon }: EditorFullScreenToggleProps) {\n return (\n <FullScreenToggle\n data-react-fancy-editor-fullscreen-toggle=\"\"\n enterIcon={enterIcon}\n exitIcon={exitIcon}\n className={cn(className)}\n />\n );\n}\n\nEditorFullScreenToggle.displayName = \"EditorFullScreenToggle\";\n","import { cn } from \"../../utils/cn\";\nimport { useEditor } from \"./Editor.context\";\nimport { EditorSourceToggle } from \"./EditorSourceToggle\";\nimport { EditorFullScreenToggle } from \"./EditorFullScreenToggle\";\nimport type { EditorToolbarProps } from \"./Editor.types\";\n\nconst DEFAULT_ACTIONS = [\n { icon: \"B\", label: \"Bold\", command: \"bold\" },\n { icon: \"I\", label: \"Italic\", command: \"italic\" },\n { icon: \"U\", label: \"Underline\", command: \"underline\" },\n { icon: \"S\", label: \"Strikethrough\", command: \"strikeThrough\" },\n];\n\nexport function EditorToolbar({\n actions = DEFAULT_ACTIONS,\n onAction,\n children,\n className,\n showSourceToggle = true,\n showFullScreenToggle = false,\n toolbarExtra,\n}: EditorToolbarProps) {\n const { exec, showSource } = useEditor();\n // `null` / `false` / `undefined` render nothing, so they must not add a\n // wrapper either: an unused slot leaves the toolbar byte-identical.\n const hasExtra = toolbarExtra !== undefined && toolbarExtra !== null && toolbarExtra !== false;\n\n return (\n <div\n data-react-fancy-editor-toolbar=\"\"\n className={cn(\n \"flex shrink-0 items-center gap-0.5 border-b border-[color:var(--fancy-border,var(--color-zinc-200))] px-2 py-1.5 dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))]\",\n className,\n )}\n >\n {children ?? (\n <>\n {actions.map((action) => (\n <button\n key={`${action.command}-${action.commandArg ?? \"\"}`}\n type=\"button\"\n // Rich-text commands don't apply to the raw-source textarea.\n disabled={showSource}\n onClick={() => {\n if (onAction) {\n onAction(action.command);\n } else {\n exec(action.command, action.commandArg);\n }\n }}\n title={action.label}\n className={cn(\n \"inline-flex h-8 w-8 items-center justify-center rounded-md text-sm transition-colors\",\n action.active\n ? \"bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]\"\n : \"hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]\",\n showSource && \"cursor-not-allowed opacity-40 hover:bg-transparent\",\n )}\n >\n {action.icon}\n </button>\n ))}\n {showSourceToggle && <EditorSourceToggle className=\"ml-auto\" />}\n {hasExtra && (\n <div\n data-react-fancy-editor-toolbar-extra=\"\"\n className={cn(\"flex items-center gap-0.5\", !showSourceToggle && \"ml-auto\")}\n >\n {toolbarExtra}\n </div>\n )}\n {showFullScreenToggle && (\n <EditorFullScreenToggle className={showSourceToggle || hasExtra ? undefined : \"ml-auto\"} />\n )}\n </>\n )}\n </div>\n );\n}\n\nEditorToolbar.displayName = \"EditorToolbar\";\n","export function EditorToolbarSeparator() {\n return (\n <span\n data-react-fancy-editor-toolbar-separator=\"\"\n className=\"mx-1 h-5 w-px bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]\"\n />\n );\n}\n\nEditorToolbarSeparator.displayName = \"EditorToolbarSeparator\";\n","import { useEffect, useMemo, useRef } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { CodeView } from \"../CodeView\";\nimport { useEditor } from \"./Editor.context\";\nimport { proseClasses, extensionEditorClasses } from \"./editor.utils\";\nimport type { EditorContentProps } from \"./Editor.types\";\n\nexport function EditorContent({ className, maxHeight: maxHeightProp, sourceClassName }: EditorContentProps) {\n const {\n contentRef,\n lineSpacing,\n placeholder,\n extensions,\n outputFormat,\n showSource,\n _initialHtml,\n _onInput,\n _sourceValue,\n _onSourceInput,\n fullScreen,\n } = useEditor();\n // Full screen means \"fill the viewport\": a height cap would leave the bottom\n // of the screen empty under a scrolled box.\n const maxHeight = fullScreen ? undefined : maxHeightProp;\n // Tracks which seed HTML is currently in the contentEditable. Seeding on the\n // seed VALUE (not a one-shot boolean) re-seeds when the source view rewrites\n // `value`, while still never re-seeding on plain keystrokes — typing leaves\n // `_initialHtml` untouched, so `seededHtml.current === _initialHtml` holds.\n const seededHtml = useRef<string | null>(null);\n\n useEffect(() => {\n // Source view replaces the contentEditable in the tree, so it unmounts.\n // Forget what we seeded — the div that returns is a fresh, empty one and\n // must always be re-seeded, even if `value` was left untouched.\n if (showSource) {\n seededHtml.current = null;\n return;\n }\n const el = contentRef.current;\n if (el && seededHtml.current !== _initialHtml) {\n el.innerHTML = _initialHtml;\n seededHtml.current = _initialHtml;\n }\n }, [contentRef, _initialHtml, showSource]);\n\n const extClasses = useMemo(\n () => extensionEditorClasses(extensions),\n [extensions],\n );\n\n if (showSource) {\n // Raw-source view — a syntax-highlighted code surface (HTML highlighted;\n // markdown/plain un-highlighted), built on the shared fancy-file-commons\n // primitives. It's a DIRECT child of the flex-column editor root (no\n // wrapper — a nested flex box collapses the fill), so `flex-auto` makes it\n // fill the editor body when height-constrained and grow with content\n // (≥ minHeight) when not. `maxHeight` caps + scrolls.\n return (\n <CodeView\n value={_sourceValue}\n onChange={_onSourceInput}\n language={outputFormat === \"html\" ? \"html\" : \"plaintext\"}\n placeholder={placeholder}\n maxHeight={maxHeight}\n className={cn(\"min-h-0 flex-auto\", sourceClassName)}\n />\n );\n }\n\n return (\n <div\n ref={contentRef}\n contentEditable\n suppressContentEditableWarning\n data-react-fancy-editor-content=\"\"\n data-placeholder={placeholder}\n onInput={_onInput}\n style={{\n lineHeight: lineSpacing,\n maxHeight: maxHeight ? `${maxHeight}px` : undefined,\n }}\n className={cn(\n \"min-h-[120px] flex-auto overflow-y-auto px-4 py-3 text-sm outline-none\",\n \"focus:outline-none\",\n proseClasses,\n extClasses,\n \"empty:before:content-[attr(data-placeholder)] empty:before:text-zinc-400 empty:before:pointer-events-none\",\n className,\n )}\n />\n );\n}\n\nEditorContent.displayName = \"EditorContent\";\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { marked } from \"marked\";\nimport { cn } from \"../../utils/cn\";\nimport { sanitizeHtml } from \"../../utils/sanitize\";\nimport { useControllableState } from \"../../hooks\";\nimport { useFullScreen } from \"../../hooks/use-full-screen\";\nimport { FullScreenProvider } from \"../FullScreen/FullScreen.context\";\nimport { EditorContext } from \"./Editor.context\";\nimport { EditorToolbar } from \"./EditorToolbar\";\nimport { EditorToolbarSeparator } from \"./EditorToolbarSeparator\";\nimport { EditorSourceToggle } from \"./EditorSourceToggle\";\nimport { EditorContent } from \"./EditorContent\";\nimport { EditorFullScreenToggle } from \"./EditorFullScreenToggle\";\nimport { htmlToMarkdown, detectFormat, markdownToEditorHtml, type MarkdownSource } from \"./editor.utils\";\nimport { mergeExtensions } from \"../ContentRenderer/extensions\";\nimport { ContentRenderer } from \"../ContentRenderer/ContentRenderer\";\nimport { useFieldMode } from \"../inputs/mode/FieldMode.context\";\nimport type { EditorProps } from \"./Editor.types\";\n\ninterface Seed {\n html: string;\n /** Source map for source-preserving markdown output; null when there is none. */\n source: MarkdownSource | null;\n}\n\nfunction toHtml(\n value: string,\n outputFormat: \"html\" | \"markdown\",\n unsafe: boolean,\n valueFormat: \"markdown\" | \"html\" | \"auto\" = \"auto\",\n): Seed {\n if (!value) return { html: \"\", source: null };\n const sanitize = (html: string) => (unsafe ? html : sanitizeHtml(html));\n // An explicit valueFormat is authoritative — the sniff misclassifies\n // markdown that merely MENTIONS HTML-ish snippets (`<code>`, `<table`),\n // collapsing it into an innerHTML wall of text (#10).\n const format =\n valueFormat !== \"auto\"\n ? valueFormat\n : outputFormat === \"html\"\n ? \"html\"\n : detectFormat(value);\n if (format === \"html\") return { html: sanitize(value), source: null };\n // Markdown in, markdown out: tag each block with its source so untouched\n // blocks are emitted byte-identical.\n if (outputFormat === \"markdown\") return markdownToEditorHtml(value, sanitize);\n return { html: sanitize((marked.parse(value, { async: false }) as string).trim()), source: null };\n}\n\nfunction EditorRoot({\n children,\n className,\n value: controlledValue,\n defaultValue = \"\",\n onChange,\n outputFormat = \"html\",\n valueFormat = \"auto\",\n lineSpacing = 1.6,\n placeholder,\n mode: modeProp,\n extensions: instanceExtensions,\n unsafe = false,\n showSource: showSourceProp,\n defaultShowSource = false,\n onShowSourceChange,\n fullScreen: fullScreenProp,\n defaultFullScreen = false,\n onFullScreenChange,\n fullScreenMode = \"overlay\",\n copyButtons,\n clipboardWriter,\n onCopy,\n}: EditorProps) {\n const contentRef = useRef<HTMLDivElement | null>(null);\n const [value, setValue] = useControllableState(controlledValue, defaultValue, onChange);\n const [showSource, setShowSource] = useControllableState(\n showSourceProp,\n defaultShowSource,\n onShowSourceChange,\n );\n // Full screen is the element itself going `position: fixed`, not a portal:\n // reparenting would remount the contentEditable and lose the caret, the\n // browser's undo stack and the seeded HTML.\n const fs = useFullScreen<HTMLDivElement>({\n fullScreen: fullScreenProp,\n defaultFullScreen,\n onFullScreenChange,\n mode: fullScreenMode,\n });\n // View/edit mode — same resolution as the inputs: prop → <Form> context → \"edit\".\n const mode = useFieldMode(modeProp);\n const isView = mode === \"view\";\n\n // The value this editor last emitted through `onChange`. An incoming `value`\n // equal to it is our own keystroke echoing back; anything else came from\n // OUTSIDE — an async load, a host reset, a form repopulating.\n const lastEmitted = useRef<string | null>(null);\n\n // Bumped only on an external `value` change, so the seed memo below recomputes\n // for those and stays put while the user types. Without this, `value` was\n // excluded from the deps to avoid re-seeding on keystrokes, which also meant a\n // value arriving after mount was silently dropped and the editor stayed blank\n // forever (#15).\n const [externalSeed, setExternalSeed] = useState(0);\n useEffect(() => {\n if (value !== lastEmitted.current) setExternalSeed((n) => n + 1);\n }, [value]);\n\n const seed = useMemo(\n () => toHtml(value, outputFormat, unsafe, valueFormat),\n // Seed the contentEditable from the LIVE value when (re)entering edit mode —\n // and when returning from the raw-source view, which may have rewritten the\n // value out from under the rich-text surface. `value` itself stays out of\n // the deps so typing never re-seeds (which would fight the caret);\n // `externalSeed` covers the externally-driven case instead.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [isView, outputFormat, unsafe, valueFormat, showSource, externalSeed],\n );\n const initialHtml = seed.html;\n const seedSource = seed.source;\n\n const extensions = useMemo(\n () => mergeExtensions(instanceExtensions),\n [instanceExtensions],\n );\n\n const getOutputValue = useCallback(\n (html: string): string => {\n return outputFormat === \"markdown\" ? htmlToMarkdown(html, seedSource) : html;\n },\n [outputFormat, seedSource],\n );\n\n const handleInput = useCallback(() => {\n const el = contentRef.current;\n if (!el) return;\n const next = getOutputValue(el.innerHTML);\n // Record what we emitted BEFORE publishing it, so the value coming back in\n // is recognised as our own and does not trigger a re-seed mid-keystroke.\n lastEmitted.current = next;\n setValue(next);\n }, [setValue, getOutputValue]);\n\n const exec = useCallback(\n (command: string, arg?: string) => {\n const el = contentRef.current;\n if (!el) return;\n el.focus();\n document.execCommand(command, false, arg);\n handleInput();\n },\n [handleInput],\n );\n\n const insertText = useCallback(\n (text: string) => {\n const el = contentRef.current;\n if (!el) return;\n el.focus();\n document.execCommand(\"insertText\", false, text);\n handleInput();\n },\n [handleInput],\n );\n\n // The source textarea edits `value` directly — it is already the canonical\n // source in `outputFormat` (html or markdown), so no conversion round-trip.\n const handleSourceInput = useCallback(\n (next: string) => {\n // Also an internal emission — record it so typing in the source view is\n // not mistaken for an external change on every keystroke.\n lastEmitted.current = next;\n setValue(next);\n },\n [setValue],\n );\n\n const wrapSelection = useCallback(\n (before: string, after: string) => {\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return;\n const range = sel.getRangeAt(0);\n const selectedText = range.toString();\n const el = contentRef.current;\n if (!el) return;\n el.focus();\n document.execCommand(\"insertText\", false, `${before}${selectedText}${after}`);\n handleInput();\n },\n [handleInput],\n );\n\n const contextValue = {\n contentRef,\n exec,\n insertText,\n wrapSelection,\n outputFormat,\n lineSpacing,\n placeholder,\n extensions,\n showSource,\n setShowSource,\n fullScreen: fs.fullScreen,\n setFullScreen: fs.setFullScreen,\n _initialHtml: initialHtml,\n _onInput: handleInput,\n _sourceValue: value,\n _onSourceInput: handleSourceInput,\n };\n\n // View mode — render the value read-only through ContentRenderer, matching the\n // editor's own format (markdown/html), instead of the editable toolbar+content\n // tree. Driven by `mode` / a `<Form mode=\"view\">`, exactly like the inputs.\n if (isView) {\n return (\n <FullScreenProvider value={fs}>\n <div\n {...fs.targetProps}\n data-react-fancy-editor=\"\"\n data-mode=\"view\"\n className={cn(\n \"overflow-hidden rounded-xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\",\n className,\n // Read-only content taller than the viewport must scroll in place.\n fs.fullScreen && \"overflow-auto\",\n )}\n >\n <ContentRenderer\n value={value}\n format={valueFormat !== \"auto\" ? valueFormat : outputFormat}\n lineSpacing={lineSpacing}\n extensions={instanceExtensions}\n unsafe={unsafe}\n copyButtons={copyButtons}\n clipboardWriter={clipboardWriter}\n onCopy={onCopy}\n className=\"px-4 py-3\"\n />\n </div>\n </FullScreenProvider>\n );\n }\n\n return (\n <EditorContext.Provider value={contextValue}>\n <FullScreenProvider value={fs}>\n <div\n {...fs.targetProps}\n data-react-fancy-editor=\"\"\n data-mode=\"edit\"\n className={cn(\n \"flex flex-col overflow-hidden rounded-xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\",\n className,\n )}\n >\n {children}\n </div>\n </FullScreenProvider>\n </EditorContext.Provider>\n );\n}\n\nconst ToolbarWithSeparator = Object.assign(EditorToolbar, {\n Separator: EditorToolbarSeparator,\n});\n\nexport const Editor = Object.assign(EditorRoot, {\n Toolbar: ToolbarWithSeparator,\n Content: EditorContent,\n SourceToggle: EditorSourceToggle,\n FullScreenToggle: EditorFullScreenToggle,\n});\n"]}
1
+ {"version":3,"sources":["../src/components/Editor/Editor.context.ts","../src/components/Editor/EditorSourceToggle.tsx","../src/components/Editor/EditorFullScreenToggle.tsx","../src/components/Editor/EditorToolbar.tsx","../src/components/Editor/EditorToolbarSeparator.tsx","../src/components/Editor/EditorContent.tsx","../src/components/Editor/Editor.tsx"],"names":["createContext","useContext","jsx","cn","FullScreenToggle","jsxs","Fragment","useRef","useEffect","useMemo","extensionEditorClasses","CodeView","proseClasses","sanitizeHtml","detectFormat","markdownToEditorHtml","marked","useControllableState","useFullScreen","useFieldMode","useState","mergeExtensions","useCallback","htmlToMarkdown","FullScreenProvider","ContentRenderer"],"mappings":"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","file":"chunk-BL6HCIP4.cjs","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { RefObject } from \"react\";\nimport type { RenderExtension } from \"../ContentRenderer/extensions\";\n\nexport interface EditorContextValue {\n contentRef: RefObject<HTMLDivElement | null>;\n exec: (command: string, arg?: string) => void;\n insertText: (text: string) => void;\n wrapSelection: (before: string, after: string) => void;\n outputFormat: \"html\" | \"markdown\";\n lineSpacing: number;\n placeholder?: string;\n /** Merged render extensions (global + instance). */\n extensions: RenderExtension[];\n /** Whether the raw-source view (textarea) is showing instead of the rich-text surface. */\n showSource: boolean;\n /** Toggle/set the raw-source view. */\n setShowSource: (showSource: boolean) => void;\n /** Whether the editor currently covers the viewport. */\n fullScreen: boolean;\n /** Enter/leave full screen. Goes through `onFullScreenChange` when controlled. */\n setFullScreen: (fullScreen: boolean) => void;\n /** Initial HTML content to load into the editor on mount */\n _initialHtml: string;\n /** @internal Called by EditorContent on input events */\n _onInput: () => void;\n /** @internal Raw source value (`value` in `outputFormat`), for the source textarea. */\n _sourceValue: string;\n /** @internal Commit an edit made directly in the source textarea. */\n _onSourceInput: (value: string) => void;\n}\n\nexport const EditorContext = createContext<EditorContextValue | null>(null);\n\nexport function useEditor(): EditorContextValue {\n const ctx = useContext(EditorContext);\n if (!ctx) {\n throw new Error(\"useEditor must be used within an <Editor> component\");\n }\n return ctx;\n}\n","import { cn } from \"../../utils/cn\";\nimport { useEditor } from \"./Editor.context\";\nimport type { EditorSourceToggleProps } from \"./Editor.types\";\n\n/**\n * Toolbar button that reveals the raw HTML/Markdown behind the editor. Reads\n * `showSource` / `setShowSource` from context, so it works in the default\n * toolbar and in any custom toolbar composed with `useEditor()`.\n */\nexport function EditorSourceToggle({\n className,\n icon = \"</>\",\n title = \"Source\",\n activeTitle = \"Rich text\",\n}: EditorSourceToggleProps) {\n const { showSource, setShowSource } = useEditor();\n\n return (\n <button\n type=\"button\"\n onClick={() => setShowSource(!showSource)}\n title={showSource ? activeTitle : title}\n aria-pressed={showSource}\n data-react-fancy-editor-source-toggle=\"\"\n className={cn(\n \"inline-flex h-8 items-center justify-center rounded-md px-2 font-mono text-xs transition-colors\",\n showSource\n ? \"bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]\"\n : \"hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]\",\n className,\n )}\n >\n {icon}\n </button>\n );\n}\n\nEditorSourceToggle.displayName = \"EditorSourceToggle\";\n","import { cn } from \"../../utils/cn\";\nimport { FullScreenToggle } from \"../FullScreen/FullScreenToggle\";\nimport type { EditorFullScreenToggleProps } from \"./Editor.types\";\n\n/**\n * Toolbar button that takes the editor full screen and back. The shared\n * `FullScreen.Toggle`, styled for the editor toolbar and carrying the editor's\n * own handle (`data-react-fancy-editor-fullscreen-toggle`) next to the shared\n * one (`data-react-fancy-fullscreen-toggle`).\n */\nexport function EditorFullScreenToggle({ className, enterIcon, exitIcon }: EditorFullScreenToggleProps) {\n return (\n <FullScreenToggle\n data-react-fancy-editor-fullscreen-toggle=\"\"\n enterIcon={enterIcon}\n exitIcon={exitIcon}\n className={cn(className)}\n />\n );\n}\n\nEditorFullScreenToggle.displayName = \"EditorFullScreenToggle\";\n","import { cn } from \"../../utils/cn\";\nimport { useEditor } from \"./Editor.context\";\nimport { EditorSourceToggle } from \"./EditorSourceToggle\";\nimport { EditorFullScreenToggle } from \"./EditorFullScreenToggle\";\nimport type { EditorToolbarProps } from \"./Editor.types\";\n\nconst DEFAULT_ACTIONS = [\n { icon: \"B\", label: \"Bold\", command: \"bold\" },\n { icon: \"I\", label: \"Italic\", command: \"italic\" },\n { icon: \"U\", label: \"Underline\", command: \"underline\" },\n { icon: \"S\", label: \"Strikethrough\", command: \"strikeThrough\" },\n];\n\nexport function EditorToolbar({\n actions = DEFAULT_ACTIONS,\n onAction,\n children,\n className,\n showSourceToggle = true,\n showFullScreenToggle = false,\n toolbarExtra,\n}: EditorToolbarProps) {\n const { exec, showSource } = useEditor();\n // `null` / `false` / `undefined` render nothing, so they must not add a\n // wrapper either: an unused slot leaves the toolbar byte-identical.\n const hasExtra = toolbarExtra !== undefined && toolbarExtra !== null && toolbarExtra !== false;\n\n return (\n <div\n data-react-fancy-editor-toolbar=\"\"\n className={cn(\n \"flex shrink-0 items-center gap-0.5 border-b border-[color:var(--fancy-border,var(--color-zinc-200))] px-2 py-1.5 dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))]\",\n className,\n )}\n >\n {children ?? (\n <>\n {actions.map((action) => (\n <button\n key={`${action.command}-${action.commandArg ?? \"\"}`}\n type=\"button\"\n // Rich-text commands don't apply to the raw-source textarea.\n disabled={showSource}\n onClick={() => {\n if (onAction) {\n onAction(action.command);\n } else {\n exec(action.command, action.commandArg);\n }\n }}\n title={action.label}\n className={cn(\n \"inline-flex h-8 w-8 items-center justify-center rounded-md text-sm transition-colors\",\n action.active\n ? \"bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]\"\n : \"hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]\",\n showSource && \"cursor-not-allowed opacity-40 hover:bg-transparent\",\n )}\n >\n {action.icon}\n </button>\n ))}\n {showSourceToggle && <EditorSourceToggle className=\"ml-auto\" />}\n {hasExtra && (\n <div\n data-react-fancy-editor-toolbar-extra=\"\"\n className={cn(\"flex items-center gap-0.5\", !showSourceToggle && \"ml-auto\")}\n >\n {toolbarExtra}\n </div>\n )}\n {showFullScreenToggle && (\n <EditorFullScreenToggle className={showSourceToggle || hasExtra ? undefined : \"ml-auto\"} />\n )}\n </>\n )}\n </div>\n );\n}\n\nEditorToolbar.displayName = \"EditorToolbar\";\n","export function EditorToolbarSeparator() {\n return (\n <span\n data-react-fancy-editor-toolbar-separator=\"\"\n className=\"mx-1 h-5 w-px bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]\"\n />\n );\n}\n\nEditorToolbarSeparator.displayName = \"EditorToolbarSeparator\";\n","import { useEffect, useMemo, useRef } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { CodeView } from \"../CodeView\";\nimport { useEditor } from \"./Editor.context\";\nimport { proseClasses, extensionEditorClasses } from \"./editor.utils\";\nimport type { EditorContentProps } from \"./Editor.types\";\n\nexport function EditorContent({ className, maxHeight: maxHeightProp, sourceClassName }: EditorContentProps) {\n const {\n contentRef,\n lineSpacing,\n placeholder,\n extensions,\n outputFormat,\n showSource,\n _initialHtml,\n _onInput,\n _sourceValue,\n _onSourceInput,\n fullScreen,\n } = useEditor();\n // Full screen means \"fill the viewport\": a height cap would leave the bottom\n // of the screen empty under a scrolled box.\n const maxHeight = fullScreen ? undefined : maxHeightProp;\n // Tracks which seed HTML is currently in the contentEditable. Seeding on the\n // seed VALUE (not a one-shot boolean) re-seeds when the source view rewrites\n // `value`, while still never re-seeding on plain keystrokes — typing leaves\n // `_initialHtml` untouched, so `seededHtml.current === _initialHtml` holds.\n const seededHtml = useRef<string | null>(null);\n\n useEffect(() => {\n // Source view replaces the contentEditable in the tree, so it unmounts.\n // Forget what we seeded — the div that returns is a fresh, empty one and\n // must always be re-seeded, even if `value` was left untouched.\n if (showSource) {\n seededHtml.current = null;\n return;\n }\n const el = contentRef.current;\n if (el && seededHtml.current !== _initialHtml) {\n el.innerHTML = _initialHtml;\n seededHtml.current = _initialHtml;\n }\n }, [contentRef, _initialHtml, showSource]);\n\n const extClasses = useMemo(\n () => extensionEditorClasses(extensions),\n [extensions],\n );\n\n if (showSource) {\n // Raw-source view — a syntax-highlighted code surface (HTML highlighted;\n // markdown/plain un-highlighted), built on the shared fancy-file-commons\n // primitives. It's a DIRECT child of the flex-column editor root (no\n // wrapper — a nested flex box collapses the fill), so `flex-auto` makes it\n // fill the editor body when height-constrained and grow with content\n // (≥ minHeight) when not. `maxHeight` caps + scrolls.\n return (\n <CodeView\n value={_sourceValue}\n onChange={_onSourceInput}\n language={outputFormat === \"html\" ? \"html\" : \"plaintext\"}\n placeholder={placeholder}\n maxHeight={maxHeight}\n className={cn(\"min-h-0 flex-auto\", sourceClassName)}\n />\n );\n }\n\n return (\n <div\n ref={contentRef}\n contentEditable\n suppressContentEditableWarning\n data-react-fancy-editor-content=\"\"\n data-placeholder={placeholder}\n onInput={_onInput}\n style={{\n lineHeight: lineSpacing,\n maxHeight: maxHeight ? `${maxHeight}px` : undefined,\n }}\n className={cn(\n \"min-h-[120px] flex-auto overflow-y-auto px-4 py-3 text-sm outline-none\",\n \"focus:outline-none\",\n proseClasses,\n extClasses,\n \"empty:before:content-[attr(data-placeholder)] empty:before:text-zinc-400 empty:before:pointer-events-none\",\n className,\n )}\n />\n );\n}\n\nEditorContent.displayName = \"EditorContent\";\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { marked } from \"marked\";\nimport { cn } from \"../../utils/cn\";\nimport { sanitizeHtml } from \"../../utils/sanitize\";\nimport { useControllableState } from \"../../hooks\";\nimport { useFullScreen } from \"../../hooks/use-full-screen\";\nimport { FullScreenProvider } from \"../FullScreen/FullScreen.context\";\nimport { EditorContext } from \"./Editor.context\";\nimport { EditorToolbar } from \"./EditorToolbar\";\nimport { EditorToolbarSeparator } from \"./EditorToolbarSeparator\";\nimport { EditorSourceToggle } from \"./EditorSourceToggle\";\nimport { EditorContent } from \"./EditorContent\";\nimport { EditorFullScreenToggle } from \"./EditorFullScreenToggle\";\nimport { htmlToMarkdown, detectFormat, markdownToEditorHtml, type MarkdownSource } from \"./editor.utils\";\nimport { mergeExtensions } from \"../ContentRenderer/extensions\";\nimport { ContentRenderer } from \"../ContentRenderer/ContentRenderer\";\nimport { useFieldMode } from \"../inputs/mode/FieldMode.context\";\nimport type { EditorProps } from \"./Editor.types\";\n\ninterface Seed {\n html: string;\n /** Source map for source-preserving markdown output; null when there is none. */\n source: MarkdownSource | null;\n}\n\nfunction toHtml(\n value: string,\n outputFormat: \"html\" | \"markdown\",\n unsafe: boolean,\n valueFormat: \"markdown\" | \"html\" | \"auto\" = \"auto\",\n): Seed {\n if (!value) return { html: \"\", source: null };\n const sanitize = (html: string) => (unsafe ? html : sanitizeHtml(html));\n // An explicit valueFormat is authoritative — the sniff misclassifies\n // markdown that merely MENTIONS HTML-ish snippets (`<code>`, `<table`),\n // collapsing it into an innerHTML wall of text (#10).\n const format =\n valueFormat !== \"auto\"\n ? valueFormat\n : outputFormat === \"html\"\n ? \"html\"\n : detectFormat(value);\n if (format === \"html\") return { html: sanitize(value), source: null };\n // Markdown in, markdown out: tag each block with its source so untouched\n // blocks are emitted byte-identical.\n if (outputFormat === \"markdown\") return markdownToEditorHtml(value, sanitize);\n return { html: sanitize((marked.parse(value, { async: false }) as string).trim()), source: null };\n}\n\nfunction EditorRoot({\n children,\n className,\n value: controlledValue,\n defaultValue = \"\",\n onChange,\n outputFormat = \"html\",\n valueFormat = \"auto\",\n lineSpacing = 1.6,\n placeholder,\n mode: modeProp,\n extensions: instanceExtensions,\n unsafe = false,\n showSource: showSourceProp,\n defaultShowSource = false,\n onShowSourceChange,\n fullScreen: fullScreenProp,\n defaultFullScreen = false,\n onFullScreenChange,\n fullScreenMode = \"overlay\",\n copyButtons,\n clipboardWriter,\n onCopy,\n}: EditorProps) {\n const contentRef = useRef<HTMLDivElement | null>(null);\n const [value, setValue] = useControllableState(controlledValue, defaultValue, onChange);\n const [showSource, setShowSource] = useControllableState(\n showSourceProp,\n defaultShowSource,\n onShowSourceChange,\n );\n // Full screen is the element itself going `position: fixed`, not a portal:\n // reparenting would remount the contentEditable and lose the caret, the\n // browser's undo stack and the seeded HTML.\n const fs = useFullScreen<HTMLDivElement>({\n fullScreen: fullScreenProp,\n defaultFullScreen,\n onFullScreenChange,\n mode: fullScreenMode,\n });\n // View/edit mode — same resolution as the inputs: prop → <Form> context → \"edit\".\n const mode = useFieldMode(modeProp);\n const isView = mode === \"view\";\n\n // The value this editor last emitted through `onChange`. An incoming `value`\n // equal to it is our own keystroke echoing back; anything else came from\n // OUTSIDE — an async load, a host reset, a form repopulating.\n const lastEmitted = useRef<string | null>(null);\n\n // Bumped only on an external `value` change, so the seed memo below recomputes\n // for those and stays put while the user types. Without this, `value` was\n // excluded from the deps to avoid re-seeding on keystrokes, which also meant a\n // value arriving after mount was silently dropped and the editor stayed blank\n // forever (#15).\n const [externalSeed, setExternalSeed] = useState(0);\n useEffect(() => {\n if (value !== lastEmitted.current) setExternalSeed((n) => n + 1);\n }, [value]);\n\n const seed = useMemo(\n () => toHtml(value, outputFormat, unsafe, valueFormat),\n // Seed the contentEditable from the LIVE value when (re)entering edit mode —\n // and when returning from the raw-source view, which may have rewritten the\n // value out from under the rich-text surface. `value` itself stays out of\n // the deps so typing never re-seeds (which would fight the caret);\n // `externalSeed` covers the externally-driven case instead.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [isView, outputFormat, unsafe, valueFormat, showSource, externalSeed],\n );\n const initialHtml = seed.html;\n const seedSource = seed.source;\n\n const extensions = useMemo(\n () => mergeExtensions(instanceExtensions),\n [instanceExtensions],\n );\n\n const getOutputValue = useCallback(\n (html: string): string => {\n return outputFormat === \"markdown\" ? htmlToMarkdown(html, seedSource) : html;\n },\n [outputFormat, seedSource],\n );\n\n const handleInput = useCallback(() => {\n const el = contentRef.current;\n if (!el) return;\n const next = getOutputValue(el.innerHTML);\n // Record what we emitted BEFORE publishing it, so the value coming back in\n // is recognised as our own and does not trigger a re-seed mid-keystroke.\n lastEmitted.current = next;\n setValue(next);\n }, [setValue, getOutputValue]);\n\n const exec = useCallback(\n (command: string, arg?: string) => {\n const el = contentRef.current;\n if (!el) return;\n el.focus();\n document.execCommand(command, false, arg);\n handleInput();\n },\n [handleInput],\n );\n\n const insertText = useCallback(\n (text: string) => {\n const el = contentRef.current;\n if (!el) return;\n el.focus();\n document.execCommand(\"insertText\", false, text);\n handleInput();\n },\n [handleInput],\n );\n\n // The source textarea edits `value` directly — it is already the canonical\n // source in `outputFormat` (html or markdown), so no conversion round-trip.\n const handleSourceInput = useCallback(\n (next: string) => {\n // Also an internal emission — record it so typing in the source view is\n // not mistaken for an external change on every keystroke.\n lastEmitted.current = next;\n setValue(next);\n },\n [setValue],\n );\n\n const wrapSelection = useCallback(\n (before: string, after: string) => {\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return;\n const range = sel.getRangeAt(0);\n const selectedText = range.toString();\n const el = contentRef.current;\n if (!el) return;\n el.focus();\n document.execCommand(\"insertText\", false, `${before}${selectedText}${after}`);\n handleInput();\n },\n [handleInput],\n );\n\n const contextValue = {\n contentRef,\n exec,\n insertText,\n wrapSelection,\n outputFormat,\n lineSpacing,\n placeholder,\n extensions,\n showSource,\n setShowSource,\n fullScreen: fs.fullScreen,\n setFullScreen: fs.setFullScreen,\n _initialHtml: initialHtml,\n _onInput: handleInput,\n _sourceValue: value,\n _onSourceInput: handleSourceInput,\n };\n\n // View mode — render the value read-only through ContentRenderer, matching the\n // editor's own format (markdown/html), instead of the editable toolbar+content\n // tree. Driven by `mode` / a `<Form mode=\"view\">`, exactly like the inputs.\n if (isView) {\n return (\n <FullScreenProvider value={fs}>\n <div\n {...fs.targetProps}\n data-react-fancy-editor=\"\"\n data-mode=\"view\"\n className={cn(\n \"overflow-hidden rounded-xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\",\n className,\n // Read-only content taller than the viewport must scroll in place.\n fs.fullScreen && \"overflow-auto\",\n )}\n >\n <ContentRenderer\n value={value}\n format={valueFormat !== \"auto\" ? valueFormat : outputFormat}\n lineSpacing={lineSpacing}\n extensions={instanceExtensions}\n unsafe={unsafe}\n copyButtons={copyButtons}\n clipboardWriter={clipboardWriter}\n onCopy={onCopy}\n className=\"px-4 py-3\"\n />\n </div>\n </FullScreenProvider>\n );\n }\n\n return (\n <EditorContext.Provider value={contextValue}>\n <FullScreenProvider value={fs}>\n <div\n {...fs.targetProps}\n data-react-fancy-editor=\"\"\n data-mode=\"edit\"\n className={cn(\n \"flex flex-col overflow-hidden rounded-xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\",\n className,\n )}\n >\n {children}\n </div>\n </FullScreenProvider>\n </EditorContext.Provider>\n );\n}\n\nconst ToolbarWithSeparator = Object.assign(EditorToolbar, {\n Separator: EditorToolbarSeparator,\n});\n\nexport const Editor = Object.assign(EditorRoot, {\n Toolbar: ToolbarWithSeparator,\n Content: EditorContent,\n SourceToggle: EditorSourceToggle,\n FullScreenToggle: EditorFullScreenToggle,\n});\n"]}
@@ -1,7 +1,7 @@
1
1
  import { Textarea } from './chunk-UVUBV4VZ.js';
2
2
  import { Portal } from './chunk-RBCMKPBX.js';
3
3
  import { Badge } from './chunk-6FCQD2KQ.js';
4
- import { Button } from './chunk-OXERDFFF.js';
4
+ import { Button } from './chunk-VV5435PS.js';
5
5
  import { useRef, useState, useCallback, useEffect } from 'react';
6
6
  import { jsx, jsxs } from 'react/jsx-runtime';
7
7
 
@@ -260,5 +260,5 @@ function selectionPoint(ta, start, end) {
260
260
  }
261
261
 
262
262
  export { MagicWand };
263
- //# sourceMappingURL=chunk-WFR7HKJL.js.map
264
- //# sourceMappingURL=chunk-WFR7HKJL.js.map
263
+ //# sourceMappingURL=chunk-CINWSRA2.js.map
264
+ //# sourceMappingURL=chunk-CINWSRA2.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/MagicWand/MagicWand.tsx"],"names":[],"mappings":"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","file":"chunk-WFR7HKJL.js","sourcesContent":["import { useCallback, useEffect, useRef, useState, type RefObject } from \"react\";\nimport { Button } from \"../Button\";\nimport { Badge } from \"../Badge\";\nimport { Portal } from \"../Portal\";\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 * Pass `target` to act on a textarea, input or editor the HOST renders instead:\n * MagicWand then renders no textarea of its own, only the floating wand.\n *\n * <textarea ref={ref} value={body} onChange={(e) => setBody(e.target.value)} />\n * <MagicWand target={ref} value={body} onValueChange={setBody} actions={…} />\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\n/** A viewport rectangle, the shape `getBoundingClientRect()` returns. */\nexport type MagicWandRect = { left: number; top: number; width: number; height: number };\n\n/**\n * How MagicWand reads a selection out of an editor it does not own — a code\n * editor, a contenteditable, anything that is not a plain textarea.\n *\n * Read-only on purpose. The write goes through `onValueChange`, like every other\n * edit, so the host's `value` stays the one source of truth and the editor is\n * updated the way it always is: from that value.\n */\nexport interface MagicWandTargetAdapter {\n /**\n * The editor's element. MagicWand listens on it for `mouseup` / `keyup` /\n * `select` to know the selection may have changed, and treats a click inside\n * it as \"not away\" for `autoHide`.\n */\n element: RefObject<HTMLElement | null> | HTMLElement | null;\n /** The selection as offsets into `value`. `null` or `start === end` hides the wand. */\n getSelection: () => { start: number; end: number } | null;\n /** Where the selection is on screen. Default: the top-centre of `element`. */\n getSelectionRect?: () => MagicWandRect | null;\n}\n\n/** A host-owned textarea / input by ref, or an adapter for anything else. */\nexport type MagicWandTarget =\n | RefObject<HTMLTextAreaElement | HTMLInputElement | null>\n | MagicWandTargetAdapter;\n\nexport interface MagicWandProps {\n /** Controlled value — of the built-in textarea, or of `target`. */\n value: string;\n /** Called on every edit, and with the result of an action. */\n onValueChange: (v: string) => void;\n /** Action list. */\n actions: MagicWandAction[];\n /**\n * Act on a textarea / input / editor the host renders, instead of rendering\n * one. A ref to a textarea or input works as is; anything else takes a\n * `MagicWandTargetAdapter`. `value` must still be the target's current value.\n */\n target?: MagicWandTarget;\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 (built-in textarea only). */\n rows?: number;\n /** Placeholder (built-in textarea only). */\n placeholder?: string;\n /** Called after an action runs successfully. */\n onAction?: (action: MagicWandAction, selection: MagicWandSelection, replacement: string) => void;\n}\n\nfunction isAdapter(target: MagicWandTarget): target is MagicWandTargetAdapter {\n return typeof (target as MagicWandTargetAdapter).getSelection === \"function\";\n}\n\nfunction targetElement(target: MagicWandTarget | undefined): HTMLElement | null {\n if (!target) return null;\n if (!isAdapter(target)) return target.current;\n const el = target.element;\n if (!el) return null;\n return el instanceof HTMLElement ? el : el.current;\n}\n\ntype TextField = HTMLTextAreaElement | HTMLInputElement;\n\n/** The selection and the viewport point the wand should sit above, or null. */\nfunction readTarget(\n target: MagicWandTarget,\n): { start: number; end: number; point: { x: number; y: number } } | null {\n if (!isAdapter(target)) {\n const field = target.current as TextField | null;\n if (!field) return null;\n const start = field.selectionStart ?? 0;\n const end = field.selectionEnd ?? 0;\n if (start === end) return null;\n return { start, end, point: selectionPoint(field, start, end) };\n }\n\n const range = target.getSelection();\n if (!range || range.start === range.end) return null;\n const rect = target.getSelectionRect?.() ?? targetElement(target)?.getBoundingClientRect() ?? null;\n const point = rect ? { x: rect.left + rect.width / 2, y: rect.top } : { x: 0, y: 0 };\n return { start: Math.min(range.start, range.end), end: Math.max(range.start, range.end), point };\n}\n\nexport function MagicWand({\n value,\n onValueChange,\n actions,\n target,\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 external = target !== undefined;\n // Latest value + target for the listeners, which attach once per element.\n const latest = useRef({ value, target });\n latest.current = { value, target };\n\n const hide = useCallback(() => {\n setSel(null);\n setPos(null);\n }, []);\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 hide();\n return;\n }\n const text = ta.value.slice(start, end);\n setSel({ start, end, text });\n // The built-in wand is positioned inside this component's `relative` box.\n const point = selectionPoint(ta, start, end);\n const box = (ta.offsetParent as HTMLElement | null)?.getBoundingClientRect() ?? ta.getBoundingClientRect();\n setPos({ x: point.x - box.left, y: point.y - box.top });\n }, [hide]);\n\n const measureTarget = useCallback(() => {\n const { target: t, value: v } = latest.current;\n if (!t) return;\n const read = readTarget(t);\n if (!read) {\n hide();\n return;\n }\n setSel({ start: read.start, end: read.end, text: v.slice(read.start, read.end) });\n setPos(read.point);\n }, [hide]);\n\n // The target's element is only known after the host renders it, so it is\n // re-resolved after every render and the listeners move if it changes.\n const [targetEl, setTargetEl] = useState<HTMLElement | null>(null);\n useEffect(() => {\n const next = targetElement(target);\n if (next !== targetEl) setTargetEl(next);\n });\n\n useEffect(() => {\n if (!targetEl) return;\n const events = [\"mouseup\", \"keyup\", \"select\"] as const;\n for (const e of events) targetEl.addEventListener(e, measureTarget);\n return () => {\n for (const e of events) targetEl.removeEventListener(e, measureTarget);\n };\n }, [targetEl, measureTarget]);\n\n useEffect(() => {\n if (!autoHide) return;\n window.addEventListener(\"scroll\", hide, true);\n return () => window.removeEventListener(\"scroll\", hide, true);\n }, [autoHide, hide]);\n\n useEffect(() => {\n if (!autoHide) return;\n const onDocMouseDown = (e: MouseEvent) => {\n const node = e.target as Node;\n if (wandRef.current?.contains(node)) return;\n if (taRef.current?.contains(node)) return;\n if (targetElement(latest.current.target)?.contains(node)) return;\n hide();\n };\n document.addEventListener(\"mousedown\", onDocMouseDown);\n return () => document.removeEventListener(\"mousedown\", onDocMouseDown);\n }, [autoHide, hide]);\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 hide();\n }\n };\n\n const wand =\n sel && pos ? (\n <Wand\n ref={wandRef}\n pos={pos}\n fixed={external}\n actions={actions}\n appearance={appearance}\n busy={busy}\n onAction={handleAction}\n selectionLength={sel.text.length}\n />\n ) : null;\n\n if (external) {\n return wand ? <Portal>{wand}</Portal> : null;\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 {wand}\n </div>\n );\n}\n\nfunction Wand({\n ref,\n pos,\n fixed,\n actions,\n appearance,\n busy,\n onAction,\n selectionLength,\n}: {\n ref: React.RefObject<HTMLDivElement | null>;\n pos: { x: number; y: number };\n /** Viewport-positioned (portalled, for an external target) rather than inside the wrapper. */\n fixed: boolean;\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 data-react-fancy-magic-wand=\"\"\n className={`${fixed ? \"fixed z-50\" : \"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-[color:var(--fancy-surface,var(--color-white))] px-1.5 py-1 shadow-lg ring-1 ring-violet-200 dark:border-violet-700 dark:bg-[color:var(--fancy-surface-dark,var(--color-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 data-react-fancy-magic-wand-action={a.id}\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-[color:var(--fancy-surface-muted,var(--color-zinc-100))] px-1 text-[9px] font-medium text-zinc-500 dark:bg-[color:var(--fancy-surface-muted-dark,var(--color-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-[color:var(--fancy-surface,var(--color-white))] dark:border-violet-700 dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\" />\n </div>\n </div>\n );\n}\n\n/**\n * Caret/selection geometry: mirror the field into a hidden div and measure the\n * selected substring's bounding rect. Returns the VIEWPORT point at the top\n * centre of the selection. Not perfect for resized textareas, but works for\n * typical chat composers.\n */\nfunction selectionPoint(ta: TextField, start: number, end: number): { x: number; y: number } {\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 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 return {\n x: taRect.left + offsetX + spanRect.width / 2,\n y: taRect.top + offsetY - ta.scrollTop,\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/components/MagicWand/MagicWand.tsx"],"names":[],"mappings":"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","file":"chunk-CINWSRA2.js","sourcesContent":["import { useCallback, useEffect, useRef, useState, type RefObject } from \"react\";\nimport { Button } from \"../Button\";\nimport { Badge } from \"../Badge\";\nimport { Portal } from \"../Portal\";\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 * Pass `target` to act on a textarea, input or editor the HOST renders instead:\n * MagicWand then renders no textarea of its own, only the floating wand.\n *\n * <textarea ref={ref} value={body} onChange={(e) => setBody(e.target.value)} />\n * <MagicWand target={ref} value={body} onValueChange={setBody} actions={…} />\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\n/** A viewport rectangle, the shape `getBoundingClientRect()` returns. */\nexport type MagicWandRect = { left: number; top: number; width: number; height: number };\n\n/**\n * How MagicWand reads a selection out of an editor it does not own — a code\n * editor, a contenteditable, anything that is not a plain textarea.\n *\n * Read-only on purpose. The write goes through `onValueChange`, like every other\n * edit, so the host's `value` stays the one source of truth and the editor is\n * updated the way it always is: from that value.\n */\nexport interface MagicWandTargetAdapter {\n /**\n * The editor's element. MagicWand listens on it for `mouseup` / `keyup` /\n * `select` to know the selection may have changed, and treats a click inside\n * it as \"not away\" for `autoHide`.\n */\n element: RefObject<HTMLElement | null> | HTMLElement | null;\n /** The selection as offsets into `value`. `null` or `start === end` hides the wand. */\n getSelection: () => { start: number; end: number } | null;\n /** Where the selection is on screen. Default: the top-centre of `element`. */\n getSelectionRect?: () => MagicWandRect | null;\n}\n\n/** A host-owned textarea / input by ref, or an adapter for anything else. */\nexport type MagicWandTarget =\n | RefObject<HTMLTextAreaElement | HTMLInputElement | null>\n | MagicWandTargetAdapter;\n\nexport interface MagicWandProps {\n /** Controlled value — of the built-in textarea, or of `target`. */\n value: string;\n /** Called on every edit, and with the result of an action. */\n onValueChange: (v: string) => void;\n /** Action list. */\n actions: MagicWandAction[];\n /**\n * Act on a textarea / input / editor the host renders, instead of rendering\n * one. A ref to a textarea or input works as is; anything else takes a\n * `MagicWandTargetAdapter`. `value` must still be the target's current value.\n */\n target?: MagicWandTarget;\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 (built-in textarea only). */\n rows?: number;\n /** Placeholder (built-in textarea only). */\n placeholder?: string;\n /** Called after an action runs successfully. */\n onAction?: (action: MagicWandAction, selection: MagicWandSelection, replacement: string) => void;\n}\n\nfunction isAdapter(target: MagicWandTarget): target is MagicWandTargetAdapter {\n return typeof (target as MagicWandTargetAdapter).getSelection === \"function\";\n}\n\nfunction targetElement(target: MagicWandTarget | undefined): HTMLElement | null {\n if (!target) return null;\n if (!isAdapter(target)) return target.current;\n const el = target.element;\n if (!el) return null;\n return el instanceof HTMLElement ? el : el.current;\n}\n\ntype TextField = HTMLTextAreaElement | HTMLInputElement;\n\n/** The selection and the viewport point the wand should sit above, or null. */\nfunction readTarget(\n target: MagicWandTarget,\n): { start: number; end: number; point: { x: number; y: number } } | null {\n if (!isAdapter(target)) {\n const field = target.current as TextField | null;\n if (!field) return null;\n const start = field.selectionStart ?? 0;\n const end = field.selectionEnd ?? 0;\n if (start === end) return null;\n return { start, end, point: selectionPoint(field, start, end) };\n }\n\n const range = target.getSelection();\n if (!range || range.start === range.end) return null;\n const rect = target.getSelectionRect?.() ?? targetElement(target)?.getBoundingClientRect() ?? null;\n const point = rect ? { x: rect.left + rect.width / 2, y: rect.top } : { x: 0, y: 0 };\n return { start: Math.min(range.start, range.end), end: Math.max(range.start, range.end), point };\n}\n\nexport function MagicWand({\n value,\n onValueChange,\n actions,\n target,\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 external = target !== undefined;\n // Latest value + target for the listeners, which attach once per element.\n const latest = useRef({ value, target });\n latest.current = { value, target };\n\n const hide = useCallback(() => {\n setSel(null);\n setPos(null);\n }, []);\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 hide();\n return;\n }\n const text = ta.value.slice(start, end);\n setSel({ start, end, text });\n // The built-in wand is positioned inside this component's `relative` box.\n const point = selectionPoint(ta, start, end);\n const box = (ta.offsetParent as HTMLElement | null)?.getBoundingClientRect() ?? ta.getBoundingClientRect();\n setPos({ x: point.x - box.left, y: point.y - box.top });\n }, [hide]);\n\n const measureTarget = useCallback(() => {\n const { target: t, value: v } = latest.current;\n if (!t) return;\n const read = readTarget(t);\n if (!read) {\n hide();\n return;\n }\n setSel({ start: read.start, end: read.end, text: v.slice(read.start, read.end) });\n setPos(read.point);\n }, [hide]);\n\n // The target's element is only known after the host renders it, so it is\n // re-resolved after every render and the listeners move if it changes.\n const [targetEl, setTargetEl] = useState<HTMLElement | null>(null);\n useEffect(() => {\n const next = targetElement(target);\n if (next !== targetEl) setTargetEl(next);\n });\n\n useEffect(() => {\n if (!targetEl) return;\n const events = [\"mouseup\", \"keyup\", \"select\"] as const;\n for (const e of events) targetEl.addEventListener(e, measureTarget);\n return () => {\n for (const e of events) targetEl.removeEventListener(e, measureTarget);\n };\n }, [targetEl, measureTarget]);\n\n useEffect(() => {\n if (!autoHide) return;\n window.addEventListener(\"scroll\", hide, true);\n return () => window.removeEventListener(\"scroll\", hide, true);\n }, [autoHide, hide]);\n\n useEffect(() => {\n if (!autoHide) return;\n const onDocMouseDown = (e: MouseEvent) => {\n const node = e.target as Node;\n if (wandRef.current?.contains(node)) return;\n if (taRef.current?.contains(node)) return;\n if (targetElement(latest.current.target)?.contains(node)) return;\n hide();\n };\n document.addEventListener(\"mousedown\", onDocMouseDown);\n return () => document.removeEventListener(\"mousedown\", onDocMouseDown);\n }, [autoHide, hide]);\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 hide();\n }\n };\n\n const wand =\n sel && pos ? (\n <Wand\n ref={wandRef}\n pos={pos}\n fixed={external}\n actions={actions}\n appearance={appearance}\n busy={busy}\n onAction={handleAction}\n selectionLength={sel.text.length}\n />\n ) : null;\n\n if (external) {\n return wand ? <Portal>{wand}</Portal> : null;\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 {wand}\n </div>\n );\n}\n\nfunction Wand({\n ref,\n pos,\n fixed,\n actions,\n appearance,\n busy,\n onAction,\n selectionLength,\n}: {\n ref: React.RefObject<HTMLDivElement | null>;\n pos: { x: number; y: number };\n /** Viewport-positioned (portalled, for an external target) rather than inside the wrapper. */\n fixed: boolean;\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 data-react-fancy-magic-wand=\"\"\n className={`${fixed ? \"fixed z-50\" : \"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-[color:var(--fancy-surface,var(--color-white))] px-1.5 py-1 shadow-lg ring-1 ring-violet-200 dark:border-violet-700 dark:bg-[color:var(--fancy-surface-dark,var(--color-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 data-react-fancy-magic-wand-action={a.id}\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-[color:var(--fancy-surface-muted,var(--color-zinc-100))] px-1 text-[9px] font-medium text-zinc-500 dark:bg-[color:var(--fancy-surface-muted-dark,var(--color-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-[color:var(--fancy-surface,var(--color-white))] dark:border-violet-700 dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\" />\n </div>\n </div>\n );\n}\n\n/**\n * Caret/selection geometry: mirror the field into a hidden div and measure the\n * selected substring's bounding rect. Returns the VIEWPORT point at the top\n * centre of the selection. Not perfect for resized textareas, but works for\n * typical chat composers.\n */\nfunction selectionPoint(ta: TextField, start: number, end: number): { x: number; y: number } {\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 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 return {\n x: taRect.left + offsetX + spanRect.width / 2,\n y: taRect.top + offsetY - ta.scrollTop,\n };\n}\n"]}