@particle-academy/react-fancy 5.60.2 → 5.61.1

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 (168) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/dist/autocomplete.cjs +4 -3
  3. package/dist/autocomplete.js +3 -2
  4. package/dist/{chunk-EEMNTO7L.cjs → chunk-3DMVK74Q.cjs} +4 -4
  5. package/dist/{chunk-EEMNTO7L.cjs.map → chunk-3DMVK74Q.cjs.map} +1 -1
  6. package/dist/{chunk-M77BB2QI.cjs → chunk-3I6PE2US.cjs} +12 -12
  7. package/dist/{chunk-M77BB2QI.cjs.map → chunk-3I6PE2US.cjs.map} +1 -1
  8. package/dist/{chunk-TGBLDJ72.js → chunk-4FJWHVC7.js} +3 -3
  9. package/dist/{chunk-TGBLDJ72.js.map → chunk-4FJWHVC7.js.map} +1 -1
  10. package/dist/{chunk-EXQ33MJR.cjs → chunk-4HUD33OP.cjs} +4 -4
  11. package/dist/{chunk-EXQ33MJR.cjs.map → chunk-4HUD33OP.cjs.map} +1 -1
  12. package/dist/chunk-4JLFIPHZ.js +43 -0
  13. package/dist/chunk-4JLFIPHZ.js.map +1 -0
  14. package/dist/{chunk-YQTKTEWS.js → chunk-5JFA7NYE.js} +3 -3
  15. package/dist/{chunk-YQTKTEWS.js.map → chunk-5JFA7NYE.js.map} +1 -1
  16. package/dist/chunk-5VMILVWD.js +3 -0
  17. package/dist/chunk-5VMILVWD.js.map +1 -0
  18. package/dist/chunk-645CRPU6.cjs +53 -0
  19. package/dist/chunk-645CRPU6.cjs.map +1 -0
  20. package/dist/{chunk-DN244T6G.cjs → chunk-6GBO7R25.cjs} +4 -4
  21. package/dist/{chunk-DN244T6G.cjs.map → chunk-6GBO7R25.cjs.map} +1 -1
  22. package/dist/{chunk-4FZOH7BJ.js → chunk-74MEJWMH.js} +3 -3
  23. package/dist/{chunk-4FZOH7BJ.js.map → chunk-74MEJWMH.js.map} +1 -1
  24. package/dist/{chunk-4DGXL6JC.cjs → chunk-7KTBVKNO.cjs} +4 -4
  25. package/dist/{chunk-4DGXL6JC.cjs.map → chunk-7KTBVKNO.cjs.map} +1 -1
  26. package/dist/{chunk-XQDGUXMS.js → chunk-BHUSI56C.js} +3 -3
  27. package/dist/{chunk-XQDGUXMS.js.map → chunk-BHUSI56C.js.map} +1 -1
  28. package/dist/{chunk-VD3C7IA4.js → chunk-BLS2IGPY.js} +48 -13
  29. package/dist/chunk-BLS2IGPY.js.map +1 -0
  30. package/dist/{chunk-XAQQX7J2.cjs → chunk-BMVYOVSW.cjs} +48 -13
  31. package/dist/chunk-BMVYOVSW.cjs.map +1 -0
  32. package/dist/{chunk-UIE5FP3A.js → chunk-BQFPC5FL.js} +3 -3
  33. package/dist/{chunk-UIE5FP3A.js.map → chunk-BQFPC5FL.js.map} +1 -1
  34. package/dist/chunk-CHQH6M67.js +297 -0
  35. package/dist/chunk-CHQH6M67.js.map +1 -0
  36. package/dist/{chunk-UZG6LO4Z.cjs → chunk-FU6K7XXZ.cjs} +4 -4
  37. package/dist/{chunk-UZG6LO4Z.cjs.map → chunk-FU6K7XXZ.cjs.map} +1 -1
  38. package/dist/{chunk-5ZNOLPIJ.cjs → chunk-H2YRZYKK.cjs} +5 -5
  39. package/dist/{chunk-5ZNOLPIJ.cjs.map → chunk-H2YRZYKK.cjs.map} +1 -1
  40. package/dist/{chunk-7R7HENZG.cjs → chunk-HRYM76ZE.cjs} +9 -9
  41. package/dist/{chunk-7R7HENZG.cjs.map → chunk-HRYM76ZE.cjs.map} +1 -1
  42. package/dist/{chunk-ZGQEKDG6.js → chunk-IHQHTJNN.js} +5 -5
  43. package/dist/{chunk-ZGQEKDG6.js.map → chunk-IHQHTJNN.js.map} +1 -1
  44. package/dist/{chunk-6HJF55U7.js → chunk-IY6LVALS.js} +3 -3
  45. package/dist/{chunk-6HJF55U7.js.map → chunk-IY6LVALS.js.map} +1 -1
  46. package/dist/{chunk-HTNQ5XUR.cjs → chunk-J4FKJ4IR.cjs} +4 -4
  47. package/dist/{chunk-HTNQ5XUR.cjs.map → chunk-J4FKJ4IR.cjs.map} +1 -1
  48. package/dist/{chunk-43ZRUYJC.cjs → chunk-JOWAS5A5.cjs} +9 -9
  49. package/dist/{chunk-43ZRUYJC.cjs.map → chunk-JOWAS5A5.cjs.map} +1 -1
  50. package/dist/{chunk-JRRYYW3Y.cjs → chunk-K6NADZ7O.cjs} +6 -6
  51. package/dist/{chunk-JRRYYW3Y.cjs.map → chunk-K6NADZ7O.cjs.map} +1 -1
  52. package/dist/{chunk-5JA4ENMF.js → chunk-LMOPAY7Q.js} +3 -3
  53. package/dist/{chunk-5JA4ENMF.js.map → chunk-LMOPAY7Q.js.map} +1 -1
  54. package/dist/{chunk-DAJPPKPO.js → chunk-M52S3TFI.js} +3 -3
  55. package/dist/{chunk-DAJPPKPO.js.map → chunk-M52S3TFI.js.map} +1 -1
  56. package/dist/{chunk-GLGUQGAQ.cjs → chunk-M6MGQI6G.cjs} +4 -4
  57. package/dist/{chunk-GLGUQGAQ.cjs.map → chunk-M6MGQI6G.cjs.map} +1 -1
  58. package/dist/chunk-MKHYENKR.cjs +4 -0
  59. package/dist/chunk-MKHYENKR.cjs.map +1 -0
  60. package/dist/{chunk-NHGU2BWT.js → chunk-MSGKIWYX.js} +3 -3
  61. package/dist/{chunk-NHGU2BWT.js.map → chunk-MSGKIWYX.js.map} +1 -1
  62. package/dist/{chunk-KQYOIPHQ.js → chunk-N7KUGFRG.js} +4 -4
  63. package/dist/{chunk-KQYOIPHQ.js.map → chunk-N7KUGFRG.js.map} +1 -1
  64. package/dist/{chunk-XB3H2EEL.js → chunk-NDLESFUH.js} +3 -3
  65. package/dist/{chunk-XB3H2EEL.js.map → chunk-NDLESFUH.js.map} +1 -1
  66. package/dist/chunk-NFTTNFQR.js +51 -0
  67. package/dist/chunk-NFTTNFQR.js.map +1 -0
  68. package/dist/{chunk-XYOT75HH.js → chunk-NORVXHRU.js} +3 -3
  69. package/dist/{chunk-XYOT75HH.js.map → chunk-NORVXHRU.js.map} +1 -1
  70. package/dist/{chunk-5H6IUCOD.cjs → chunk-P6V4QWJK.cjs} +4 -4
  71. package/dist/{chunk-5H6IUCOD.cjs.map → chunk-P6V4QWJK.cjs.map} +1 -1
  72. package/dist/{chunk-LZHGXRFD.js → chunk-PKFEJWVI.js} +3 -3
  73. package/dist/{chunk-LZHGXRFD.js.map → chunk-PKFEJWVI.js.map} +1 -1
  74. package/dist/{chunk-BJKKW77L.cjs → chunk-QXMQVLRQ.cjs} +4 -4
  75. package/dist/{chunk-BJKKW77L.cjs.map → chunk-QXMQVLRQ.cjs.map} +1 -1
  76. package/dist/{chunk-WHEXTDPV.js → chunk-RBCMKPBX.js} +7 -5
  77. package/dist/chunk-RBCMKPBX.js.map +1 -0
  78. package/dist/{chunk-LVE6ERRS.cjs → chunk-RRMQGTVB.cjs} +6 -6
  79. package/dist/{chunk-LVE6ERRS.cjs.map → chunk-RRMQGTVB.cjs.map} +1 -1
  80. package/dist/{chunk-T67FLC55.js → chunk-SHF44YGG.js} +3 -3
  81. package/dist/{chunk-T67FLC55.js.map → chunk-SHF44YGG.js.map} +1 -1
  82. package/dist/{chunk-NBZC6LOB.cjs → chunk-TI7D4QDM.cjs} +6 -3
  83. package/dist/chunk-TI7D4QDM.cjs.map +1 -0
  84. package/dist/{chunk-5JK2VDWY.cjs → chunk-TYTENEJQ.cjs} +6 -6
  85. package/dist/{chunk-5JK2VDWY.cjs.map → chunk-TYTENEJQ.cjs.map} +1 -1
  86. package/dist/{chunk-WW56U3RV.js → chunk-UBEOB55O.js} +3 -3
  87. package/dist/{chunk-WW56U3RV.js.map → chunk-UBEOB55O.js.map} +1 -1
  88. package/dist/{chunk-M6XBF2ZS.js → chunk-UJZJBHMH.js} +3 -3
  89. package/dist/{chunk-M6XBF2ZS.js.map → chunk-UJZJBHMH.js.map} +1 -1
  90. package/dist/{chunk-T45LBTXH.js → chunk-ULG4OA2T.js} +3 -3
  91. package/dist/{chunk-T45LBTXH.js.map → chunk-ULG4OA2T.js.map} +1 -1
  92. package/dist/{chunk-SIEPD6NR.cjs → chunk-UM4JXBRZ.cjs} +4 -4
  93. package/dist/{chunk-SIEPD6NR.cjs.map → chunk-UM4JXBRZ.cjs.map} +1 -1
  94. package/dist/chunk-X6TGRSVH.cjs +299 -0
  95. package/dist/chunk-X6TGRSVH.cjs.map +1 -0
  96. package/dist/{chunk-FGI2EC32.cjs → chunk-Z5OTOYWI.cjs} +4 -4
  97. package/dist/{chunk-FGI2EC32.cjs.map → chunk-Z5OTOYWI.cjs.map} +1 -1
  98. package/dist/chunk-ZPBCZPMD.cjs +48 -0
  99. package/dist/chunk-ZPBCZPMD.cjs.map +1 -0
  100. package/dist/command.cjs +5 -4
  101. package/dist/command.js +3 -2
  102. package/dist/composer.cjs +7 -6
  103. package/dist/composer.js +5 -4
  104. package/dist/context-menu.cjs +5 -4
  105. package/dist/context-menu.js +3 -2
  106. package/dist/drawer.cjs +5 -4
  107. package/dist/drawer.js +3 -2
  108. package/dist/dropdown.cjs +5 -4
  109. package/dist/dropdown.js +3 -2
  110. package/dist/editor.cjs +8 -3
  111. package/dist/editor.d.cts +53 -4
  112. package/dist/editor.d.ts +53 -4
  113. package/dist/editor.js +6 -1
  114. package/dist/full-screen.cjs +39 -0
  115. package/dist/full-screen.cjs.map +1 -0
  116. package/dist/full-screen.d.cts +54 -0
  117. package/dist/full-screen.d.ts +54 -0
  118. package/dist/full-screen.js +10 -0
  119. package/dist/full-screen.js.map +1 -0
  120. package/dist/index.cjs +152 -120
  121. package/dist/index.cjs.map +1 -1
  122. package/dist/index.d.cts +4 -2
  123. package/dist/index.d.ts +4 -2
  124. package/dist/index.js +35 -31
  125. package/dist/index.js.map +1 -1
  126. package/dist/input.cjs +6 -5
  127. package/dist/input.js +5 -4
  128. package/dist/inputs.cjs +17 -16
  129. package/dist/inputs.js +9 -8
  130. package/dist/json-editor.cjs +19 -18
  131. package/dist/json-editor.js +8 -7
  132. package/dist/magic-wand.cjs +4 -3
  133. package/dist/magic-wand.js +3 -2
  134. package/dist/mobile-menu.cjs +6 -4
  135. package/dist/mobile-menu.js +4 -2
  136. package/dist/modal.cjs +5 -4
  137. package/dist/modal.js +3 -2
  138. package/dist/paste-pills.cjs +17 -16
  139. package/dist/paste-pills.js +4 -3
  140. package/dist/popover.cjs +5 -4
  141. package/dist/popover.js +3 -2
  142. package/dist/portal.cjs +7 -2
  143. package/dist/portal.d.cts +12 -1
  144. package/dist/portal.d.ts +12 -1
  145. package/dist/portal.js +2 -1
  146. package/dist/prompt-input.cjs +7 -6
  147. package/dist/prompt-input.js +6 -5
  148. package/dist/reason-tag.cjs +5 -4
  149. package/dist/reason-tag.js +4 -3
  150. package/dist/select.cjs +4 -3
  151. package/dist/select.js +3 -2
  152. package/dist/sidebar.cjs +1 -0
  153. package/dist/sidebar.js +1 -0
  154. package/dist/styles.css +9 -0
  155. package/dist/styles.css.map +1 -1
  156. package/dist/toast.cjs +5 -4
  157. package/dist/toast.js +3 -2
  158. package/dist/tooltip.cjs +4 -3
  159. package/dist/tooltip.js +3 -2
  160. package/dist/use-full-screen-BUJ1ujG4.d.cts +94 -0
  161. package/dist/use-full-screen-BUJ1ujG4.d.ts +94 -0
  162. package/docs/Editor.md +24 -0
  163. package/docs/FullScreen.md +106 -0
  164. package/package.json +11 -1
  165. package/dist/chunk-NBZC6LOB.cjs.map +0 -1
  166. package/dist/chunk-VD3C7IA4.js.map +0 -1
  167. package/dist/chunk-WHEXTDPV.js.map +0 -1
  168. package/dist/chunk-XAQQX7J2.cjs.map +0 -1
@@ -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-SIEPD6NR.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-UM4JXBRZ.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"]}
@@ -0,0 +1,299 @@
1
+ 'use strict';
2
+
3
+ var chunkSNHU2AI4_cjs = require('./chunk-SNHU2AI4.cjs');
4
+ var react = require('react');
5
+
6
+ // src/utils/scroll-lock.ts
7
+ var holders = 0;
8
+ var saved = "";
9
+ function lockBodyScroll() {
10
+ if (holders === 0) {
11
+ saved = document.body.style.overflow;
12
+ document.body.style.overflow = "hidden";
13
+ }
14
+ holders++;
15
+ let released = false;
16
+ return () => {
17
+ if (released) return;
18
+ released = true;
19
+ holders--;
20
+ if (holders === 0) document.body.style.overflow = saved;
21
+ };
22
+ }
23
+
24
+ // src/hooks/use-full-screen.ts
25
+ var FOCUSABLE = [
26
+ "a[href]",
27
+ "button:not([disabled])",
28
+ "input:not([disabled])",
29
+ "textarea:not([disabled])",
30
+ "select:not([disabled])",
31
+ "[contenteditable]:not([contenteditable='false'])",
32
+ "[tabindex]:not([tabindex='-1'])"
33
+ ].join(", ");
34
+ var activeStack = [];
35
+ var OVERLAY_STYLE = {
36
+ position: "fixed",
37
+ top: 0,
38
+ right: 0,
39
+ bottom: 0,
40
+ left: 0,
41
+ width: "100%",
42
+ height: "100%",
43
+ maxWidth: "none",
44
+ maxHeight: "none",
45
+ margin: 0,
46
+ borderRadius: 0,
47
+ boxSizing: "border-box"
48
+ };
49
+ var promoted = /* @__PURE__ */ new Map();
50
+ function promoteToTopLayer(el, onLost) {
51
+ const p = el;
52
+ if (typeof p.showPopover !== "function" || typeof p.hidePopover !== "function") return null;
53
+ if (el.hasAttribute("popover")) return null;
54
+ el.setAttribute("popover", "manual");
55
+ try {
56
+ p.showPopover();
57
+ } catch {
58
+ el.removeAttribute("popover");
59
+ return null;
60
+ }
61
+ let open = true;
62
+ let raising = false;
63
+ const onToggle = (e) => {
64
+ if (raising || e.newState !== "closed" || !open) return;
65
+ open = false;
66
+ promoted.delete(el);
67
+ el.removeEventListener("toggle", onToggle);
68
+ el.removeAttribute("popover");
69
+ onLost();
70
+ };
71
+ el.addEventListener("toggle", onToggle);
72
+ for (const [other, raise] of promoted) {
73
+ if (other !== el && el.contains(other)) raise();
74
+ }
75
+ promoted.set(el, () => {
76
+ raising = true;
77
+ try {
78
+ p.hidePopover();
79
+ p.showPopover();
80
+ } catch {
81
+ } finally {
82
+ raising = false;
83
+ }
84
+ });
85
+ return () => {
86
+ promoted.delete(el);
87
+ el.removeEventListener("toggle", onToggle);
88
+ if (open) {
89
+ open = false;
90
+ try {
91
+ p.hidePopover();
92
+ } catch {
93
+ }
94
+ }
95
+ el.removeAttribute("popover");
96
+ };
97
+ }
98
+ function useFullScreen(options = {}) {
99
+ const {
100
+ fullScreen: controlled,
101
+ defaultFullScreen = false,
102
+ onFullScreenChange,
103
+ mode = "overlay",
104
+ closeOnEscape = true,
105
+ lockScroll = true,
106
+ zIndex = 50,
107
+ labels
108
+ } = options;
109
+ const [fullScreen, setState] = chunkSNHU2AI4_cjs.useControllableState(controlled, defaultFullScreen, onFullScreenChange);
110
+ const ref = react.useRef(null);
111
+ const token = react.useRef({});
112
+ const [nativeActive, setNativeActive] = react.useState(false);
113
+ const [portalContainer, setPortalContainer] = react.useState(null);
114
+ const [applied, setApplied] = react.useState(fullScreen);
115
+ const [layer, setLayer] = react.useState("fixed");
116
+ const topRelease = react.useRef(null);
117
+ const promote = (el) => {
118
+ if (topRelease.current) return;
119
+ topRelease.current = promoteToTopLayer(el, () => {
120
+ topRelease.current = null;
121
+ setLayer("fixed");
122
+ });
123
+ setLayer(topRelease.current ? "top" : "fixed");
124
+ };
125
+ const demote = () => {
126
+ topRelease.current?.();
127
+ topRelease.current = null;
128
+ };
129
+ const current = react.useRef(fullScreen);
130
+ current.current = fullScreen;
131
+ const setFullScreen = react.useCallback(
132
+ (next) => {
133
+ if (current.current === next) return;
134
+ current.current = next;
135
+ setState(next);
136
+ },
137
+ [setState]
138
+ );
139
+ const toggle = react.useCallback(() => setFullScreen(!current.current), [setFullScreen]);
140
+ react.useLayoutEffect(() => {
141
+ if (!fullScreen) return;
142
+ const el = ref.current;
143
+ if (!el) return;
144
+ const scrolled = [];
145
+ for (let p = el.parentElement; p && p !== document.body && p !== document.documentElement; p = p.parentElement) {
146
+ if (p.scrollTop || p.scrollLeft) scrolled.push({ el: p, top: p.scrollTop, left: p.scrollLeft });
147
+ }
148
+ const winX = window.scrollX;
149
+ const winY = window.scrollY;
150
+ const release = lockScroll ? lockBodyScroll() : null;
151
+ activeStack.push(token.current);
152
+ setPortalContainer(el);
153
+ setApplied(true);
154
+ const native = mode === "native" && typeof el.requestFullscreen === "function" && document.fullscreenEnabled !== false;
155
+ if (!native) promote(el);
156
+ return () => {
157
+ const i = activeStack.indexOf(token.current);
158
+ if (i >= 0) activeStack.splice(i, 1);
159
+ demote();
160
+ setLayer("fixed");
161
+ release?.();
162
+ for (const m of scrolled) {
163
+ if (m.el.scrollTop !== m.top) m.el.scrollTop = m.top;
164
+ if (m.el.scrollLeft !== m.left) m.el.scrollLeft = m.left;
165
+ }
166
+ if (window.scrollX !== winX || window.scrollY !== winY) window.scrollTo(winX, winY);
167
+ setPortalContainer(null);
168
+ setApplied(false);
169
+ };
170
+ }, [fullScreen, lockScroll, mode]);
171
+ react.useEffect(() => {
172
+ if (!fullScreen) return;
173
+ const el = ref.current;
174
+ if (!el) return;
175
+ const opener = document.activeElement instanceof HTMLElement && document.activeElement !== document.body ? document.activeElement : null;
176
+ if (!el.contains(document.activeElement)) {
177
+ el.querySelector(FOCUSABLE)?.focus({ preventScroll: true });
178
+ }
179
+ return () => {
180
+ const active = document.activeElement;
181
+ if (opener?.isConnected && (!active || active === document.body || el.contains(active))) {
182
+ opener.focus({ preventScroll: true });
183
+ }
184
+ };
185
+ }, [fullScreen]);
186
+ react.useEffect(() => {
187
+ if (!fullScreen || mode !== "native") return;
188
+ const el = ref.current;
189
+ if (!el || typeof el.requestFullscreen !== "function" || document.fullscreenEnabled === false) return;
190
+ let cancelled = false;
191
+ let entered = false;
192
+ const onChange = () => {
193
+ if (document.fullscreenElement === el) {
194
+ entered = true;
195
+ setNativeActive(true);
196
+ setLayer("top");
197
+ } else if (entered) {
198
+ entered = false;
199
+ setNativeActive(false);
200
+ setLayer("fixed");
201
+ if (!cancelled) setFullScreen(false);
202
+ }
203
+ };
204
+ document.addEventListener("fullscreenchange", onChange);
205
+ const refused = () => {
206
+ if (!cancelled && current.current && ref.current === el) promote(el);
207
+ };
208
+ try {
209
+ Promise.resolve(el.requestFullscreen()).then(onChange, refused);
210
+ } catch {
211
+ refused();
212
+ }
213
+ return () => {
214
+ cancelled = true;
215
+ document.removeEventListener("fullscreenchange", onChange);
216
+ if (document.fullscreenElement === el) {
217
+ try {
218
+ Promise.resolve(document.exitFullscreen()).catch(() => {
219
+ });
220
+ } catch {
221
+ }
222
+ }
223
+ setNativeActive(false);
224
+ };
225
+ }, [fullScreen, mode, setFullScreen]);
226
+ react.useEffect(() => {
227
+ if (!fullScreen || !closeOnEscape) return;
228
+ const onKey = (e) => {
229
+ if (e.key !== "Escape" || e.defaultPrevented) return;
230
+ const t = e.target;
231
+ if (t !== document.body && t !== document.documentElement && t !== document) return;
232
+ if (activeStack[activeStack.length - 1] !== token.current) return;
233
+ setFullScreen(false);
234
+ };
235
+ document.addEventListener("keydown", onKey);
236
+ return () => document.removeEventListener("keydown", onKey);
237
+ }, [fullScreen, closeOnEscape, setFullScreen]);
238
+ const onKeyDown = react.useCallback(
239
+ (e) => {
240
+ if (!current.current || e.defaultPrevented) return;
241
+ const el = ref.current;
242
+ if (!el) return;
243
+ if (e.key === "Tab") {
244
+ const items = Array.from(el.querySelectorAll(FOCUSABLE));
245
+ if (items.length === 0) return;
246
+ const first = items[0];
247
+ const last = items[items.length - 1];
248
+ const active = document.activeElement;
249
+ if (e.shiftKey && (active === first || active === el)) {
250
+ e.preventDefault();
251
+ last.focus();
252
+ } else if (!e.shiftKey && active === last) {
253
+ e.preventDefault();
254
+ first.focus();
255
+ }
256
+ return;
257
+ }
258
+ if (e.key !== "Escape" || !closeOnEscape) return;
259
+ const layer2 = e.target.closest?.("[data-react-fancy-portal]");
260
+ if (layer2 && !layer2.contains(el)) return;
261
+ e.preventDefault();
262
+ e.stopPropagation();
263
+ setFullScreen(false);
264
+ },
265
+ [closeOnEscape, setFullScreen]
266
+ );
267
+ const activeMode = fullScreen ? nativeActive ? "native" : "overlay" : null;
268
+ const enterLabel = labels?.enter ?? "Enter full screen";
269
+ const exitLabel = labels?.exit ?? "Exit full screen";
270
+ return {
271
+ fullScreen,
272
+ setFullScreen,
273
+ toggle,
274
+ activeMode,
275
+ layer: activeMode ? layer : null,
276
+ ref,
277
+ portalContainer,
278
+ targetProps: {
279
+ ref,
280
+ "data-fullscreen": fullScreen ? "true" : "false",
281
+ ...activeMode ? { "data-fullscreen-mode": activeMode, "data-fullscreen-layer": layer } : {},
282
+ ...fullScreen && applied ? { style: { ...OVERLAY_STYLE, zIndex } } : {},
283
+ onKeyDown
284
+ },
285
+ toggleProps: {
286
+ type: "button",
287
+ onClick: toggle,
288
+ "aria-pressed": fullScreen,
289
+ "aria-label": fullScreen ? exitLabel : enterLabel,
290
+ title: fullScreen ? exitLabel : enterLabel,
291
+ "data-react-fancy-fullscreen-toggle": "",
292
+ "data-fullscreen": fullScreen ? "true" : "false"
293
+ }
294
+ };
295
+ }
296
+
297
+ exports.useFullScreen = useFullScreen;
298
+ //# sourceMappingURL=chunk-X6TGRSVH.cjs.map
299
+ //# sourceMappingURL=chunk-X6TGRSVH.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/scroll-lock.ts","../src/hooks/use-full-screen.ts"],"names":["useControllableState","useRef","useState","useCallback","useLayoutEffect","useEffect","layer"],"mappings":"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","file":"chunk-X6TGRSVH.cjs","sourcesContent":["/**\n * Reference-counted body scroll lock.\n *\n * Two surfaces locking independently with the save-and-restore pattern\n * (`const original = body.style.overflow`) only unwind correctly in strict LIFO\n * order. Exit them out of order and the first one out restores `\"\"` while the\n * second is still covering the viewport, so the page scrolls behind it. Counting\n * holders keeps the lock until the last one lets go, and restores the value that\n * was there before the FIRST took it.\n *\n * Call only from an effect: it touches `document`.\n */\nlet holders = 0;\nlet saved = \"\";\n\nexport function lockBodyScroll(): () => void {\n if (holders === 0) {\n saved = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n }\n holders++;\n\n let released = false;\n return () => {\n if (released) return;\n released = true;\n holders--;\n if (holders === 0) document.body.style.overflow = saved;\n };\n}\n","import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type KeyboardEvent as ReactKeyboardEvent,\n type RefObject,\n} from \"react\";\nimport { useControllableState } from \"./use-controllable-state\";\nimport { lockBodyScroll } from \"../utils/scroll-lock\";\n\n/**\n * How a surface goes full screen.\n *\n * - `\"overlay\"` (default): the element itself becomes `position: fixed` over the\n * viewport, in place. It always works: inside an iframe without\n * `allow=\"fullscreen\"`, inside a dialog, without a user gesture. Nothing is\n * reparented, so an editor keeps its caret, undo stack and DOM.\n * - `\"native\"`: the browser Fullscreen API (`element.requestFullscreen()`),\n * which hides the browser chrome too. The overlay is applied first and stays\n * if the request is rejected or the API is missing, so the user gets full\n * screen either way; `activeMode` reports which one took.\n */\nexport type FullScreenMode = \"overlay\" | \"native\";\n\nexport interface UseFullScreenOptions {\n /** Controlled flag. Pair with `onFullScreenChange`. */\n fullScreen?: boolean;\n /** Initial flag when uncontrolled. @default false */\n defaultFullScreen?: boolean;\n /**\n * Fired with the requested state — from the toggle, Escape, an agent calling\n * `setFullScreen`, or the user leaving the browser's own full screen.\n */\n onFullScreenChange?: (fullScreen: boolean) => void;\n /** @default \"overlay\" */\n mode?: FullScreenMode;\n /** Leave full screen on Escape. @default true */\n closeOnEscape?: boolean;\n /** Stop the page behind from scrolling while active. @default true */\n lockScroll?: boolean;\n /** Stacking order of the overlay. Matches `Modal`. @default 50 */\n zIndex?: number;\n /** Accessible names for the toggle. */\n labels?: { enter?: string; exit?: string };\n}\n\nexport interface FullScreenTargetProps<T extends HTMLElement> {\n ref: RefObject<T | null>;\n \"data-fullscreen\": \"true\" | \"false\";\n \"data-fullscreen-mode\"?: FullScreenMode;\n \"data-fullscreen-layer\"?: FullScreenLayer;\n style?: CSSProperties;\n onKeyDown: (event: ReactKeyboardEvent<T>) => void;\n}\n\nexport interface FullScreenToggleProps {\n type: \"button\";\n onClick: () => void;\n \"aria-pressed\": boolean;\n \"aria-label\": string;\n title: string;\n \"data-react-fancy-fullscreen-toggle\": \"\";\n \"data-fullscreen\": \"true\" | \"false\";\n}\n\nexport interface UseFullScreenResult<T extends HTMLElement = HTMLElement> {\n fullScreen: boolean;\n setFullScreen: (fullScreen: boolean) => void;\n toggle: () => void;\n /** The mechanism in effect while active; `null` when not full screen. */\n activeMode: FullScreenMode | null;\n /**\n * `\"top\"` while the surface is in the browser's top layer (a manual popover,\n * or the native full screen), `\"fixed\"` while it relies on `position: fixed`\n * (no popover API, or the browser refused). `null` when not full screen.\n */\n layer: FullScreenLayer | null;\n /** Attach to the element that goes full screen. Also inside `targetProps`. */\n ref: RefObject<T | null>;\n /**\n * Spread onto the surface's root. Carries the ref, the `data-fullscreen`\n * handle, the overlay style and the Escape/Tab handler. Merge your own\n * `style` / `onKeyDown` if the root has them.\n */\n targetProps: FullScreenTargetProps<T>;\n /** Spread onto a `<button>` to make it the toggle. */\n toggleProps: FullScreenToggleProps;\n /**\n * The element to portal into while active, else `null`. Provide it through\n * `PortalContainerContext` so menus opened inside stay visible.\n */\n portalContainer: HTMLElement | null;\n}\n\nconst FOCUSABLE = [\n \"a[href]\",\n \"button:not([disabled])\",\n \"input:not([disabled])\",\n \"textarea:not([disabled])\",\n \"select:not([disabled])\",\n \"[contenteditable]:not([contenteditable='false'])\",\n \"[tabindex]:not([tabindex='-1'])\",\n].join(\", \");\n\n/** Active surfaces, innermost last. Only the top one answers a stray Escape. */\nconst activeStack: object[] = [];\n\nconst OVERLAY_STYLE: CSSProperties = {\n position: \"fixed\",\n top: 0,\n right: 0,\n bottom: 0,\n left: 0,\n width: \"100%\",\n height: \"100%\",\n maxWidth: \"none\",\n maxHeight: \"none\",\n margin: 0,\n borderRadius: 0,\n boxSizing: \"border-box\",\n};\n\n/**\n * Lift `el` into the browser's top layer with `popover=\"manual\"`.\n *\n * Why: `position: fixed` is only fixed to the viewport while no ancestor has a\n * transform, filter, perspective or paint containment. With one, the overlay\n * is fixed to THAT box. In the showcase a surface in a Tabs panel came out\n * 1248x95, because `.fancy-fade-in` held `transform: matrix(1,0,0,1,0,0)` via\n * `animation-fill-mode: both`. Transform-centred dialogs do the same. A\n * top-layer element's containing block is the viewport whatever its ancestors\n * do, and the popover API gets it there without moving it in the DOM.\n *\n * `manual` because `auto` light-dismisses on an outside click and on Escape;\n * Escape stays with the surface's own handler.\n *\n * Returns a release function, or `null` where the API is missing or refuses\n * (the caller keeps the fixed overlay). `onLost` fires if something else\n * closes the popover: a `[popover]` that is not open is `display: none`, so the\n * attribute must go at once or the surface vanishes while still \"full screen\".\n */\n/** Surfaces currently in the top layer through a popover, with a \"re-show\" each. */\nconst promoted = new Map<HTMLElement, () => void>();\n\nfunction promoteToTopLayer(el: HTMLElement, onLost: () => void): (() => void) | null {\n const p = el as HTMLElement & { showPopover?: () => void; hidePopover?: () => void };\n if (typeof p.showPopover !== \"function\" || typeof p.hidePopover !== \"function\") return null;\n // The consumer made it a popover themselves; do not fight over it.\n if (el.hasAttribute(\"popover\")) return null;\n\n el.setAttribute(\"popover\", \"manual\");\n try {\n p.showPopover();\n } catch {\n el.removeAttribute(\"popover\");\n return null;\n }\n\n let open = true;\n let raising = false;\n const onToggle = (e: Event) => {\n if (raising || (e as Event & { newState?: string }).newState !== \"closed\" || !open) return;\n open = false;\n promoted.delete(el);\n el.removeEventListener(\"toggle\", onToggle);\n el.removeAttribute(\"popover\");\n onLost();\n };\n el.addEventListener(\"toggle\", onToggle);\n\n // The top layer stacks in SHOW order. React runs a child's layout effects\n // before its parent's, so a surface nested in another that goes full screen\n // in the same commit is shown first and then buried by its parent. Re-show\n // any promoted descendant so the innermost stays on top.\n for (const [other, raise] of promoted) {\n if (other !== el && el.contains(other)) raise();\n }\n promoted.set(el, () => {\n raising = true;\n try {\n p.hidePopover!();\n p.showPopover!();\n } catch {\n /* leave it where it is */\n } finally {\n raising = false;\n }\n });\n\n return () => {\n promoted.delete(el);\n el.removeEventListener(\"toggle\", onToggle);\n if (open) {\n open = false;\n try {\n p.hidePopover!();\n } catch {\n /* already hidden, e.g. removed from the document */\n }\n }\n el.removeAttribute(\"popover\");\n };\n}\n\n/** Which mechanism keeps the surface over the page. */\nexport type FullScreenLayer = \"top\" | \"fixed\";\n\ninterface ScrollMemo {\n el: Element;\n top: number;\n left: number;\n}\n\n/**\n * A controlled full-screen state for any surface: Editor today, DeckEditor and\n * SheetWorkbook next. Render-time output is pure (no `window`/`document`), so it\n * server-renders; everything that touches the DOM runs in effects.\n */\nexport function useFullScreen<T extends HTMLElement = HTMLElement>(\n options: UseFullScreenOptions = {},\n): UseFullScreenResult<T> {\n const {\n fullScreen: controlled,\n defaultFullScreen = false,\n onFullScreenChange,\n mode = \"overlay\",\n closeOnEscape = true,\n lockScroll = true,\n zIndex = 50,\n labels,\n } = options;\n\n const [fullScreen, setState] = useControllableState(controlled, defaultFullScreen, onFullScreenChange);\n const ref = useRef<T | null>(null);\n const token = useRef({});\n const [nativeActive, setNativeActive] = useState(false);\n const [portalContainer, setPortalContainer] = useState<HTMLElement | null>(null);\n // Whether the overlay STYLE is on. It trails `fullScreen` by one synchronous\n // commit on entry, so the layout effect below reads scroll offsets while the\n // surface is still in flow. Read after the style lands and the browser has\n // already clamped every scroll container around it: a panel at 569 read as\n // 342 in Chromium, and that is where the user was sent back to. The re-render\n // happens inside the layout phase, before paint, so nothing flashes.\n // Initialised from `fullScreen` so a surface that starts full screen renders\n // the overlay on the server and on hydration alike.\n const [applied, setApplied] = useState(fullScreen);\n // \"fixed\" until a layout effect has actually promoted the surface, so the\n // server and the hydrating client agree, and a refused promotion reads true.\n const [layer, setLayer] = useState<FullScreenLayer>(\"fixed\");\n const topRelease = useRef<(() => void) | null>(null);\n\n const promote = (el: HTMLElement) => {\n if (topRelease.current) return;\n topRelease.current = promoteToTopLayer(el, () => {\n topRelease.current = null;\n setLayer(\"fixed\");\n });\n setLayer(topRelease.current ? \"top\" : \"fixed\");\n };\n const demote = () => {\n topRelease.current?.();\n topRelease.current = null;\n };\n\n const current = useRef(fullScreen);\n current.current = fullScreen;\n\n // Only report real transitions: Escape on an inactive surface, or a second\n // click racing a re-render, must not fire `onFullScreenChange(false)` twice.\n const setFullScreen = useCallback(\n (next: boolean) => {\n if (current.current === next) return;\n current.current = next;\n setState(next);\n },\n [setState],\n );\n const toggle = useCallback(() => setFullScreen(!current.current), [setFullScreen]);\n\n // Enter / exit bookkeeping. A LAYOUT effect so the cleanup runs after the\n // overlay style has been removed from the DOM but before paint: scroll offsets\n // restored there never flash, and the surface is back in flow to measure.\n useLayoutEffect(() => {\n if (!fullScreen) return;\n const el = ref.current;\n if (!el) return;\n\n // Taking the surface out of flow shrinks every scroll container around it,\n // which clamps their scrollTop. Remember them so exit lands where it began.\n const scrolled: ScrollMemo[] = [];\n for (let p = el.parentElement; p && p !== document.body && p !== document.documentElement; p = p.parentElement) {\n if (p.scrollTop || p.scrollLeft) scrolled.push({ el: p, top: p.scrollTop, left: p.scrollLeft });\n }\n const winX = window.scrollX;\n const winY = window.scrollY;\n\n const release = lockScroll ? lockBodyScroll() : null;\n activeStack.push(token.current);\n setPortalContainer(el);\n setApplied(true);\n\n // Native mode asks the browser first: Chromium rejects requestFullscreen()\n // on an element that is already an open popover (\"already open as a\n // Popover, and therefore cannot be opened via the fullscreen API\"). The\n // native effect promotes on refusal instead.\n const native = mode === \"native\" && typeof el.requestFullscreen === \"function\" && document.fullscreenEnabled !== false;\n if (!native) promote(el);\n\n return () => {\n const i = activeStack.indexOf(token.current);\n if (i >= 0) activeStack.splice(i, 1);\n // Out of the top layer first, so the surface is back in flow before the\n // scroll offsets are put back.\n demote();\n setLayer(\"fixed\");\n release?.();\n for (const m of scrolled) {\n if (m.el.scrollTop !== m.top) m.el.scrollTop = m.top;\n if (m.el.scrollLeft !== m.left) m.el.scrollLeft = m.left;\n }\n if (window.scrollX !== winX || window.scrollY !== winY) window.scrollTo(winX, winY);\n setPortalContainer(null);\n setApplied(false);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [fullScreen, lockScroll, mode]);\n\n // Focus in and back out. A PASSIVE effect on purpose: React re-focuses\n // whatever had focus before a commit once the commit's mutations finish, so a\n // focus() issued from a layout-effect cleanup is silently undone.\n useEffect(() => {\n if (!fullScreen) return;\n const el = ref.current;\n if (!el) return;\n\n const opener =\n document.activeElement instanceof HTMLElement && document.activeElement !== document.body\n ? document.activeElement\n : null;\n\n // Focus must not stay behind the overlay, where the user cannot see it.\n if (!el.contains(document.activeElement)) {\n el.querySelector<HTMLElement>(FOCUSABLE)?.focus({ preventScroll: true });\n }\n\n return () => {\n // Give focus back only if it is still ours to give: inside the surface,\n // or lost to <body>. A user who clicked elsewhere keeps their choice.\n const active = document.activeElement;\n if (opener?.isConnected && (!active || active === document.body || el.contains(active))) {\n opener.focus({ preventScroll: true });\n }\n };\n }, [fullScreen]);\n\n // The browser Fullscreen API, on top of the overlay.\n useEffect(() => {\n if (!fullScreen || mode !== \"native\") return;\n const el = ref.current;\n if (!el || typeof el.requestFullscreen !== \"function\" || document.fullscreenEnabled === false) return;\n\n let cancelled = false;\n let entered = false;\n\n const onChange = () => {\n if (document.fullscreenElement === el) {\n entered = true;\n setNativeActive(true);\n // The browser's full screen is the top layer too.\n setLayer(\"top\");\n } else if (entered) {\n // The user left through the browser's own UI (Esc, F11, the close\n // pill). Keep the controlled state honest rather than leaving an\n // overlay up that they believe they dismissed.\n entered = false;\n setNativeActive(false);\n setLayer(\"fixed\");\n if (!cancelled) setFullScreen(false);\n }\n };\n document.addEventListener(\"fullscreenchange\", onChange);\n\n // Blocked requests (iframe without allow=\"fullscreen\", no user gesture, a\n // permissions policy) reject, and some engines throw instead. Either way\n // the user stays full screen in the overlay, lifted into the top layer.\n const refused = () => {\n if (!cancelled && current.current && ref.current === el) promote(el);\n };\n try {\n Promise.resolve(el.requestFullscreen()).then(onChange, refused);\n } catch {\n refused();\n }\n\n return () => {\n cancelled = true;\n document.removeEventListener(\"fullscreenchange\", onChange);\n if (document.fullscreenElement === el) {\n try {\n Promise.resolve(document.exitFullscreen()).catch(() => {});\n } catch {\n /* already left */\n }\n }\n setNativeActive(false);\n };\n }, [fullScreen, mode, setFullScreen]);\n\n // Escape with nothing focused never reaches the surface's own handler.\n useEffect(() => {\n if (!fullScreen || !closeOnEscape) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key !== \"Escape\" || e.defaultPrevented) return;\n const t = e.target;\n if (t !== document.body && t !== document.documentElement && t !== document) return;\n if (activeStack[activeStack.length - 1] !== token.current) return;\n setFullScreen(false);\n };\n document.addEventListener(\"keydown\", onKey);\n return () => document.removeEventListener(\"keydown\", onKey);\n }, [fullScreen, closeOnEscape, setFullScreen]);\n\n const onKeyDown = useCallback(\n (e: ReactKeyboardEvent<T>) => {\n if (!current.current || e.defaultPrevented) return;\n const el = ref.current;\n if (!el) return;\n\n if (e.key === \"Tab\") {\n // Keep Tab inside the surface; what is behind the overlay is invisible.\n const items = Array.from(el.querySelectorAll<HTMLElement>(FOCUSABLE));\n if (items.length === 0) return;\n const first = items[0];\n const last = items[items.length - 1];\n const active = document.activeElement;\n if (e.shiftKey && (active === first || active === el)) {\n e.preventDefault();\n last.focus();\n } else if (!e.shiftKey && active === last) {\n e.preventDefault();\n first.focus();\n }\n return;\n }\n\n if (e.key !== \"Escape\" || !closeOnEscape) return;\n // An Escape from inside a layer opened FROM this surface (a menu, a\n // popover, a dialog) belongs to that layer. React bubbles portal events\n // through the component tree, so check the DOM: a portal wrapper that\n // does not contain this surface is nested inside it.\n const layer = (e.target as Element).closest?.(\"[data-react-fancy-portal]\");\n if (layer && !layer.contains(el)) return;\n // Stop it here so an enclosing dialog's document listener does not also\n // close: one Escape, one step back.\n e.preventDefault();\n e.stopPropagation();\n setFullScreen(false);\n },\n [closeOnEscape, setFullScreen],\n );\n\n const activeMode: FullScreenMode | null = fullScreen ? (nativeActive ? \"native\" : \"overlay\") : null;\n const enterLabel = labels?.enter ?? \"Enter full screen\";\n const exitLabel = labels?.exit ?? \"Exit full screen\";\n\n return {\n fullScreen,\n setFullScreen,\n toggle,\n activeMode,\n layer: activeMode ? layer : null,\n ref,\n portalContainer,\n targetProps: {\n ref,\n \"data-fullscreen\": fullScreen ? \"true\" : \"false\",\n ...(activeMode ? { \"data-fullscreen-mode\": activeMode, \"data-fullscreen-layer\": layer } : {}),\n ...(fullScreen && applied ? { style: { ...OVERLAY_STYLE, zIndex } } : {}),\n onKeyDown,\n },\n toggleProps: {\n type: \"button\",\n onClick: toggle,\n \"aria-pressed\": fullScreen,\n \"aria-label\": fullScreen ? exitLabel : enterLabel,\n title: fullScreen ? exitLabel : enterLabel,\n \"data-react-fancy-fullscreen-toggle\": \"\",\n \"data-fullscreen\": fullScreen ? \"true\" : \"false\",\n },\n };\n}\n"]}
@@ -2,7 +2,7 @@
2
2
 
3
3
  var chunkTXTRTTG7_cjs = require('./chunk-TXTRTTG7.cjs');
4
4
  var chunkFQZE5N2N_cjs = require('./chunk-FQZE5N2N.cjs');
5
- var chunkNBZC6LOB_cjs = require('./chunk-NBZC6LOB.cjs');
5
+ var chunkTI7D4QDM_cjs = require('./chunk-TI7D4QDM.cjs');
6
6
  var chunkRXDRYWVD_cjs = require('./chunk-RXDRYWVD.cjs');
7
7
  var chunkZU5Q77OT_cjs = require('./chunk-ZU5Q77OT.cjs');
8
8
  var react = require('react');
@@ -105,7 +105,7 @@ function ModalRoot({
105
105
  });
106
106
  const ctx = react.useMemo(() => ({ open, close }), [open, close]);
107
107
  if (!mounted) return null;
108
- return /* @__PURE__ */ jsxRuntime.jsx(ModalContext.Provider, { value: ctx, children: /* @__PURE__ */ jsxRuntime.jsx(chunkNBZC6LOB_cjs.Portal, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center p-4", children: [
108
+ return /* @__PURE__ */ jsxRuntime.jsx(ModalContext.Provider, { value: ctx, children: /* @__PURE__ */ jsxRuntime.jsx(chunkTI7D4QDM_cjs.Portal, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center p-4", children: [
109
109
  /* @__PURE__ */ jsxRuntime.jsx(
110
110
  "div",
111
111
  {
@@ -144,5 +144,5 @@ var Modal = Object.assign(ModalRoot, {
144
144
 
145
145
  exports.Modal = Modal;
146
146
  exports.useModal = useModal;
147
- //# sourceMappingURL=chunk-FGI2EC32.cjs.map
148
- //# sourceMappingURL=chunk-FGI2EC32.cjs.map
147
+ //# sourceMappingURL=chunk-Z5OTOYWI.cjs.map
148
+ //# sourceMappingURL=chunk-Z5OTOYWI.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/Modal/Modal.context.ts","../src/components/Modal/ModalHeader.tsx","../src/components/Modal/ModalBody.tsx","../src/components/Modal/ModalFooter.tsx","../src/components/Modal/Modal.tsx"],"names":["createContext","useContext","jsxs","cn","jsx","X","useRef","useCallback","useEscapeKey","useFocusTrap","useEffect","useAnimation","useMemo","Portal"],"mappings":"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","file":"chunk-FGI2EC32.cjs","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { ModalContextValue } from \"./Modal.types\";\n\nexport const ModalContext = createContext<ModalContextValue | null>(null);\n\nexport function useModal(): ModalContextValue {\n const ctx = useContext(ModalContext);\n if (!ctx) {\n throw new Error(\"Modal compound components must be used within <Modal>\");\n }\n return ctx;\n}\n","import { X } from \"lucide-react\";\nimport { cn } from \"../../utils/cn\";\nimport { useModal } from \"./Modal.context\";\nimport type { ModalHeaderProps } from \"./Modal.types\";\n\nexport function ModalHeader({ children, className }: ModalHeaderProps) {\n const { close } = useModal();\n\n return (\n <div\n data-react-fancy-modal-header=\"\"\n className={cn(\n \"flex items-center justify-between border-b border-[color:var(--fancy-border,var(--color-zinc-200))] px-6 py-4 dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))]\",\n className,\n )}\n >\n <div className=\"text-lg font-semibold text-[color:var(--fancy-text,var(--color-zinc-900))] dark:text-[color:var(--fancy-text-dark,var(--color-zinc-100))]\">{children}</div>\n <button\n type=\"button\"\n onClick={close}\n className=\"rounded-lg p-1 text-zinc-400 transition-colors hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] hover:text-zinc-600 dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))] dark:hover:text-zinc-300\"\n aria-label=\"Close\"\n >\n <X size={20} />\n </button>\n </div>\n );\n}\n\nModalHeader.displayName = \"ModalHeader\";\n","import { cn } from \"../../utils/cn\";\nimport type { ModalBodyProps } from \"./Modal.types\";\n\nexport function ModalBody({ children, className }: ModalBodyProps) {\n return (\n // `min-h-0` is load-bearing, not decoration. A flex child defaults to\n // `min-height: auto`, which refuses to shrink below its content — so\n // `flex-1` alone would push the panel through its own max-height and the\n // scrollbar would never appear. This is what permits the overflow that\n // `overflow-y-auto` then handles.\n <div data-react-fancy-modal-body=\"\" className={cn(\"min-h-0 flex-1 overflow-y-auto px-6 py-4 text-[color:var(--fancy-text-soft,var(--color-zinc-700))] dark:text-[color:var(--fancy-text-soft-dark,var(--color-zinc-300))]\", className)}>\n {children}\n </div>\n );\n}\n\nModalBody.displayName = \"ModalBody\";\n","import { cn } from \"../../utils/cn\";\nimport type { ModalFooterProps } from \"./Modal.types\";\n\nexport function ModalFooter({ children, className }: ModalFooterProps) {\n return (\n <div\n data-react-fancy-modal-footer=\"\"\n className={cn(\n \"flex items-center justify-end gap-3 border-t border-[color:var(--fancy-border,var(--color-zinc-200))] px-6 py-4 dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))]\",\n className,\n )}\n >\n {children}\n </div>\n );\n}\n\nModalFooter.displayName = \"ModalFooter\";\n","import { useCallback, useEffect, useMemo, useRef } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Portal } from \"../Portal\";\nimport { ModalContext } from \"./Modal.context\";\nimport { ModalHeader } from \"./ModalHeader\";\nimport { ModalBody } from \"./ModalBody\";\nimport { ModalFooter } from \"./ModalFooter\";\nimport { useFocusTrap } from \"../../hooks/use-focus-trap\";\nimport { useEscapeKey } from \"../../hooks/use-escape-key\";\nimport { useAnimation } from \"../../hooks/use-animation\";\nimport type { ModalProps } from \"./Modal.types\";\n\n/**\n * Every size is capped against the VIEWPORT, not just the width.\n *\n * Only `full` used to carry a max-height. The other four were width-constrained\n * and vertically unbounded, so a modal with long content simply grew past the\n * bottom of the screen and the rest became unreachable — no scrollbar, because\n * there was no overflow to scroll: the box got taller instead.\n *\n * `ModalBody` already had `flex-1 overflow-y-auto` and the panel already had\n * `overflow-hidden`. The scrolling machinery was complete and inert, waiting on\n * the one constraint that makes it engage.\n *\n * `dvh` over `vh` deliberately: on mobile browsers `100vh` is the tallest the\n * viewport ever gets, ignoring the address bar, so a `vh`-capped modal still\n * runs under the chrome on exactly the devices with least room. `dvh` tracks\n * the visible height. The `vh` fallback is listed first for any engine that\n * does not know `dvh` yet — an unknown unit drops the declaration, so ordering\n * is what keeps the old behaviour rather than none.\n */\nconst VIEWPORT_CAP = \"max-h-[calc(100vh-2rem)] max-h-[calc(100dvh-2rem)]\";\n\nconst SIZE_MAP = {\n sm: `max-w-sm ${VIEWPORT_CAP}`,\n md: `max-w-lg ${VIEWPORT_CAP}`,\n lg: `max-w-2xl ${VIEWPORT_CAP}`,\n xl: `max-w-4xl ${VIEWPORT_CAP}`,\n full: `max-w-[calc(100vw-2rem)] ${VIEWPORT_CAP}`,\n} as const;\n\nfunction ModalRoot({\n children,\n open,\n onClose,\n size = \"md\",\n className,\n ...rest\n}: ModalProps) {\n const panelRef = useRef<HTMLDivElement>(null);\n\n const close = useCallback(() => onClose(), [onClose]);\n useEscapeKey(close, open);\n useFocusTrap(panelRef, open);\n\n // Body scroll lock\n useEffect(() => {\n if (open) {\n const original = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n return () => {\n document.body.style.overflow = original;\n };\n }\n }, [open]);\n\n const { mounted, className: animClass, ref: animRef } = useAnimation({\n open,\n enterClass: \"fancy-slide-up\",\n exitClass: \"fancy-fade-out\",\n });\n\n const ctx = useMemo(() => ({ open, close }), [open, close]);\n\n if (!mounted) return null;\n\n return (\n <ModalContext.Provider value={ctx}>\n <Portal>\n <div className=\"fixed inset-0 z-50 flex items-center justify-center p-4\">\n {/* Backdrop */}\n <div\n className=\"absolute inset-0 bg-black/50 backdrop-blur-sm\"\n onClick={close}\n aria-hidden=\"true\"\n />\n {/* Panel */}\n <div\n ref={(node) => {\n panelRef.current = node;\n (animRef as React.MutableRefObject<HTMLElement | null>).current =\n node;\n }}\n data-react-fancy-modal=\"\"\n role=\"dialog\"\n aria-modal=\"true\"\n {...rest}\n className={cn(\n \"relative flex w-full flex-col overflow-hidden rounded-2xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] shadow-xl dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\",\n SIZE_MAP[size],\n animClass,\n className,\n )}\n >\n {children}\n </div>\n </div>\n </Portal>\n </ModalContext.Provider>\n );\n}\n\nexport const Modal = Object.assign(ModalRoot, {\n Header: ModalHeader,\n Body: ModalBody,\n Footer: ModalFooter,\n});\n"]}
1
+ {"version":3,"sources":["../src/components/Modal/Modal.context.ts","../src/components/Modal/ModalHeader.tsx","../src/components/Modal/ModalBody.tsx","../src/components/Modal/ModalFooter.tsx","../src/components/Modal/Modal.tsx"],"names":["createContext","useContext","jsxs","cn","jsx","X","useRef","useCallback","useEscapeKey","useFocusTrap","useEffect","useAnimation","useMemo","Portal"],"mappings":"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","file":"chunk-Z5OTOYWI.cjs","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { ModalContextValue } from \"./Modal.types\";\n\nexport const ModalContext = createContext<ModalContextValue | null>(null);\n\nexport function useModal(): ModalContextValue {\n const ctx = useContext(ModalContext);\n if (!ctx) {\n throw new Error(\"Modal compound components must be used within <Modal>\");\n }\n return ctx;\n}\n","import { X } from \"lucide-react\";\nimport { cn } from \"../../utils/cn\";\nimport { useModal } from \"./Modal.context\";\nimport type { ModalHeaderProps } from \"./Modal.types\";\n\nexport function ModalHeader({ children, className }: ModalHeaderProps) {\n const { close } = useModal();\n\n return (\n <div\n data-react-fancy-modal-header=\"\"\n className={cn(\n \"flex items-center justify-between border-b border-[color:var(--fancy-border,var(--color-zinc-200))] px-6 py-4 dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))]\",\n className,\n )}\n >\n <div className=\"text-lg font-semibold text-[color:var(--fancy-text,var(--color-zinc-900))] dark:text-[color:var(--fancy-text-dark,var(--color-zinc-100))]\">{children}</div>\n <button\n type=\"button\"\n onClick={close}\n className=\"rounded-lg p-1 text-zinc-400 transition-colors hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] hover:text-zinc-600 dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))] dark:hover:text-zinc-300\"\n aria-label=\"Close\"\n >\n <X size={20} />\n </button>\n </div>\n );\n}\n\nModalHeader.displayName = \"ModalHeader\";\n","import { cn } from \"../../utils/cn\";\nimport type { ModalBodyProps } from \"./Modal.types\";\n\nexport function ModalBody({ children, className }: ModalBodyProps) {\n return (\n // `min-h-0` is load-bearing, not decoration. A flex child defaults to\n // `min-height: auto`, which refuses to shrink below its content — so\n // `flex-1` alone would push the panel through its own max-height and the\n // scrollbar would never appear. This is what permits the overflow that\n // `overflow-y-auto` then handles.\n <div data-react-fancy-modal-body=\"\" className={cn(\"min-h-0 flex-1 overflow-y-auto px-6 py-4 text-[color:var(--fancy-text-soft,var(--color-zinc-700))] dark:text-[color:var(--fancy-text-soft-dark,var(--color-zinc-300))]\", className)}>\n {children}\n </div>\n );\n}\n\nModalBody.displayName = \"ModalBody\";\n","import { cn } from \"../../utils/cn\";\nimport type { ModalFooterProps } from \"./Modal.types\";\n\nexport function ModalFooter({ children, className }: ModalFooterProps) {\n return (\n <div\n data-react-fancy-modal-footer=\"\"\n className={cn(\n \"flex items-center justify-end gap-3 border-t border-[color:var(--fancy-border,var(--color-zinc-200))] px-6 py-4 dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))]\",\n className,\n )}\n >\n {children}\n </div>\n );\n}\n\nModalFooter.displayName = \"ModalFooter\";\n","import { useCallback, useEffect, useMemo, useRef } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Portal } from \"../Portal\";\nimport { ModalContext } from \"./Modal.context\";\nimport { ModalHeader } from \"./ModalHeader\";\nimport { ModalBody } from \"./ModalBody\";\nimport { ModalFooter } from \"./ModalFooter\";\nimport { useFocusTrap } from \"../../hooks/use-focus-trap\";\nimport { useEscapeKey } from \"../../hooks/use-escape-key\";\nimport { useAnimation } from \"../../hooks/use-animation\";\nimport type { ModalProps } from \"./Modal.types\";\n\n/**\n * Every size is capped against the VIEWPORT, not just the width.\n *\n * Only `full` used to carry a max-height. The other four were width-constrained\n * and vertically unbounded, so a modal with long content simply grew past the\n * bottom of the screen and the rest became unreachable — no scrollbar, because\n * there was no overflow to scroll: the box got taller instead.\n *\n * `ModalBody` already had `flex-1 overflow-y-auto` and the panel already had\n * `overflow-hidden`. The scrolling machinery was complete and inert, waiting on\n * the one constraint that makes it engage.\n *\n * `dvh` over `vh` deliberately: on mobile browsers `100vh` is the tallest the\n * viewport ever gets, ignoring the address bar, so a `vh`-capped modal still\n * runs under the chrome on exactly the devices with least room. `dvh` tracks\n * the visible height. The `vh` fallback is listed first for any engine that\n * does not know `dvh` yet — an unknown unit drops the declaration, so ordering\n * is what keeps the old behaviour rather than none.\n */\nconst VIEWPORT_CAP = \"max-h-[calc(100vh-2rem)] max-h-[calc(100dvh-2rem)]\";\n\nconst SIZE_MAP = {\n sm: `max-w-sm ${VIEWPORT_CAP}`,\n md: `max-w-lg ${VIEWPORT_CAP}`,\n lg: `max-w-2xl ${VIEWPORT_CAP}`,\n xl: `max-w-4xl ${VIEWPORT_CAP}`,\n full: `max-w-[calc(100vw-2rem)] ${VIEWPORT_CAP}`,\n} as const;\n\nfunction ModalRoot({\n children,\n open,\n onClose,\n size = \"md\",\n className,\n ...rest\n}: ModalProps) {\n const panelRef = useRef<HTMLDivElement>(null);\n\n const close = useCallback(() => onClose(), [onClose]);\n useEscapeKey(close, open);\n useFocusTrap(panelRef, open);\n\n // Body scroll lock\n useEffect(() => {\n if (open) {\n const original = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n return () => {\n document.body.style.overflow = original;\n };\n }\n }, [open]);\n\n const { mounted, className: animClass, ref: animRef } = useAnimation({\n open,\n enterClass: \"fancy-slide-up\",\n exitClass: \"fancy-fade-out\",\n });\n\n const ctx = useMemo(() => ({ open, close }), [open, close]);\n\n if (!mounted) return null;\n\n return (\n <ModalContext.Provider value={ctx}>\n <Portal>\n <div className=\"fixed inset-0 z-50 flex items-center justify-center p-4\">\n {/* Backdrop */}\n <div\n className=\"absolute inset-0 bg-black/50 backdrop-blur-sm\"\n onClick={close}\n aria-hidden=\"true\"\n />\n {/* Panel */}\n <div\n ref={(node) => {\n panelRef.current = node;\n (animRef as React.MutableRefObject<HTMLElement | null>).current =\n node;\n }}\n data-react-fancy-modal=\"\"\n role=\"dialog\"\n aria-modal=\"true\"\n {...rest}\n className={cn(\n \"relative flex w-full flex-col overflow-hidden rounded-2xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] shadow-xl dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\",\n SIZE_MAP[size],\n animClass,\n className,\n )}\n >\n {children}\n </div>\n </div>\n </Portal>\n </ModalContext.Provider>\n );\n}\n\nexport const Modal = Object.assign(ModalRoot, {\n Header: ModalHeader,\n Body: ModalBody,\n Footer: ModalFooter,\n});\n"]}
@@ -0,0 +1,48 @@
1
+ 'use strict';
2
+
3
+ var chunkTI7D4QDM_cjs = require('./chunk-TI7D4QDM.cjs');
4
+ var chunkMXWR3T3N_cjs = require('./chunk-MXWR3T3N.cjs');
5
+ var chunkZU5Q77OT_cjs = require('./chunk-ZU5Q77OT.cjs');
6
+ var react = require('react');
7
+ var jsxRuntime = require('react/jsx-runtime');
8
+
9
+ var FullScreenContext = react.createContext(null);
10
+ function useFullScreenContext() {
11
+ return react.useContext(FullScreenContext);
12
+ }
13
+ function FullScreenProvider({ value, children }) {
14
+ return /* @__PURE__ */ jsxRuntime.jsx(FullScreenContext.Provider, { value, children: /* @__PURE__ */ jsxRuntime.jsx(chunkTI7D4QDM_cjs.PortalContainerContext.Provider, { value: value.portalContainer, children }) });
15
+ }
16
+ function FullScreenToggle({
17
+ enterIcon = "maximize-2",
18
+ exitIcon = "minimize-2",
19
+ children,
20
+ className,
21
+ ...rest
22
+ }) {
23
+ const fs = useFullScreenContext();
24
+ if (!fs) return null;
25
+ const content = typeof children === "function" ? children(fs.fullScreen) : children ?? /* @__PURE__ */ jsxRuntime.jsx(chunkMXWR3T3N_cjs.Icon, { name: fs.fullScreen ? exitIcon : enterIcon, size: "sm" });
26
+ return /* @__PURE__ */ jsxRuntime.jsx(
27
+ "button",
28
+ {
29
+ ...rest,
30
+ ...fs.toggleProps,
31
+ className: chunkZU5Q77OT_cjs.cn(
32
+ "inline-flex h-8 w-8 items-center justify-center rounded-md text-sm transition-colors",
33
+ "hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]",
34
+ "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--color-brand,var(--color-blue-500))]",
35
+ className
36
+ ),
37
+ children: content
38
+ }
39
+ );
40
+ }
41
+ FullScreenToggle.displayName = "FullScreenToggle";
42
+
43
+ exports.FullScreenContext = FullScreenContext;
44
+ exports.FullScreenProvider = FullScreenProvider;
45
+ exports.FullScreenToggle = FullScreenToggle;
46
+ exports.useFullScreenContext = useFullScreenContext;
47
+ //# sourceMappingURL=chunk-ZPBCZPMD.cjs.map
48
+ //# sourceMappingURL=chunk-ZPBCZPMD.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/FullScreen/FullScreen.context.tsx","../src/components/FullScreen/FullScreenToggle.tsx"],"names":["createContext","useContext","jsx","PortalContainerContext","Icon","cn"],"mappings":";;;;;;;;AAIO,IAAM,iBAAA,GAAoBA,oBAA0C,IAAI;AAGxE,SAAS,oBAAA,GAAmD;AACjE,EAAA,OAAOC,iBAAW,iBAAiB,CAAA;AACrC;AAaO,SAAS,kBAAA,CAAmB,EAAE,KAAA,EAAO,QAAA,EAAS,EAA4B;AAC/E,EAAA,uBACEC,cAAA,CAAC,iBAAA,CAAkB,QAAA,EAAlB,EAA2B,KAAA,EAC1B,QAAA,kBAAAA,cAAA,CAACC,wCAAA,CAAuB,QAAA,EAAvB,EAAgC,KAAA,EAAO,KAAA,CAAM,eAAA,EAAkB,UAAS,CAAA,EAC3E,CAAA;AAEJ;ACTO,SAAS,gBAAA,CAAiB;AAAA,EAC/B,SAAA,GAAY,YAAA;AAAA,EACZ,QAAA,GAAW,YAAA;AAAA,EACX,QAAA;AAAA,EACA,SAAA;AAAA,EACA,GAAG;AACL,CAAA,EAA0B;AACxB,EAAA,MAAM,KAAK,oBAAA,EAAqB;AAChC,EAAA,IAAI,CAAC,IAAI,OAAO,IAAA;AAEhB,EAAA,MAAM,UACJ,OAAO,QAAA,KAAa,aAChB,QAAA,CAAS,EAAA,CAAG,UAAU,CAAA,GACtB,QAAA,oBAAYD,cAAAA,CAACE,0BAAK,IAAA,EAAM,EAAA,CAAG,aAAa,QAAA,GAAW,SAAA,EAAW,MAAK,IAAA,EAAK,CAAA;AAE9E,EAAA,uBACEF,cAAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACE,GAAG,IAAA;AAAA,MACH,GAAG,EAAA,CAAG,WAAA;AAAA,MACP,SAAA,EAAWG,oBAAA;AAAA,QACT,sFAAA;AAAA,QACA,+IAAA;AAAA,QACA,+HAAA;AAAA,QACA;AAAA,OACF;AAAA,MAEC,QAAA,EAAA;AAAA;AAAA,GACH;AAEJ;AAEA,gBAAA,CAAiB,WAAA,GAAc,kBAAA","file":"chunk-ZPBCZPMD.cjs","sourcesContent":["import { createContext, useContext, type ReactNode } from \"react\";\nimport { PortalContainerContext } from \"../Portal/Portal\";\nimport type { UseFullScreenResult } from \"../../hooks/use-full-screen\";\n\nexport const FullScreenContext = createContext<UseFullScreenResult | null>(null);\n\n/** The nearest full-screen surface's state, or `null` outside one. */\nexport function useFullScreenContext(): UseFullScreenResult | null {\n return useContext(FullScreenContext);\n}\n\nexport interface FullScreenProviderProps {\n /** What `useFullScreen()` returned for the surface. */\n value: UseFullScreenResult<HTMLElement> | UseFullScreenResult<HTMLDivElement>;\n children?: ReactNode;\n}\n\n/**\n * For a surface that owns its root and calls `useFullScreen()` itself (Editor,\n * DeckEditor, SheetWorkbook). Makes `<FullScreen.Toggle />` work anywhere inside\n * and redirects descendant `<Portal>`s into the surface while it is active.\n */\nexport function FullScreenProvider({ value, children }: FullScreenProviderProps) {\n return (\n <FullScreenContext.Provider value={value as UseFullScreenResult}>\n <PortalContainerContext.Provider value={value.portalContainer}>{children}</PortalContainerContext.Provider>\n </FullScreenContext.Provider>\n );\n}\n","import type { ButtonHTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Icon } from \"../Icon\";\nimport { useFullScreenContext } from \"./FullScreen.context\";\n\nexport interface FullScreenToggleProps\n extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, \"type\" | \"onClick\" | \"children\"> {\n /** Lucide name shown while NOT full screen. @default \"maximize-2\" */\n enterIcon?: string;\n /** Lucide name shown while full screen. @default \"minimize-2\" */\n exitIcon?: string;\n /** Replace the icon entirely. Receives the current state. */\n children?: ReactNode | ((fullScreen: boolean) => ReactNode);\n}\n\n/**\n * The toggle for the nearest `<FullScreen>` / `<FullScreenProvider>`. Renders\n * nothing outside one, so a shared toolbar can include it unconditionally.\n */\nexport function FullScreenToggle({\n enterIcon = \"maximize-2\",\n exitIcon = \"minimize-2\",\n children,\n className,\n ...rest\n}: FullScreenToggleProps) {\n const fs = useFullScreenContext();\n if (!fs) return null;\n\n const content =\n typeof children === \"function\"\n ? children(fs.fullScreen)\n : children ?? <Icon name={fs.fullScreen ? exitIcon : enterIcon} size=\"sm\" />;\n\n return (\n <button\n {...rest}\n {...fs.toggleProps}\n className={cn(\n \"inline-flex h-8 w-8 items-center justify-center rounded-md text-sm transition-colors\",\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 \"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--color-brand,var(--color-blue-500))]\",\n className,\n )}\n >\n {content}\n </button>\n );\n}\n\nFullScreenToggle.displayName = \"FullScreenToggle\";\n"]}