@particle-academy/react-fancy 5.52.0 → 5.53.0

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 (49) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +26 -0
  3. package/dist/{chunk-FWCREAIT.js → chunk-2BXMMVQS.js} +2 -2
  4. package/dist/{chunk-FWCREAIT.js.map → chunk-2BXMMVQS.js.map} +1 -1
  5. package/dist/{chunk-BYUJJXGY.cjs → chunk-2KFHSFFY.cjs} +3 -3
  6. package/dist/{chunk-BYUJJXGY.cjs.map → chunk-2KFHSFFY.cjs.map} +1 -1
  7. package/dist/{chunk-FVDQNQKA.js → chunk-3NIXNJ3I.js} +3 -3
  8. package/dist/{chunk-FVDQNQKA.js.map → chunk-3NIXNJ3I.js.map} +1 -1
  9. package/dist/{chunk-OWT5B3C2.cjs → chunk-5ZNOLPIJ.cjs} +3 -3
  10. package/dist/{chunk-OWT5B3C2.cjs.map → chunk-5ZNOLPIJ.cjs.map} +1 -1
  11. package/dist/{chunk-HPNIEYSJ.cjs → chunk-A2JUTNE6.cjs} +2 -2
  12. package/dist/{chunk-HPNIEYSJ.cjs.map → chunk-A2JUTNE6.cjs.map} +1 -1
  13. package/dist/{chunk-N2JESOCJ.js → chunk-EJ6P7K4G.js} +3 -3
  14. package/dist/{chunk-N2JESOCJ.js.map → chunk-EJ6P7K4G.js.map} +1 -1
  15. package/dist/{chunk-JBHQQGG5.cjs → chunk-SW2MDI2E.cjs} +4 -4
  16. package/dist/{chunk-JBHQQGG5.cjs.map → chunk-SW2MDI2E.cjs.map} +1 -1
  17. package/dist/{chunk-Z4EPQWVC.js → chunk-UIE5FP3A.js} +3 -3
  18. package/dist/{chunk-Z4EPQWVC.js.map → chunk-UIE5FP3A.js.map} +1 -1
  19. package/dist/{chunk-ELBCK3V7.js → chunk-VOHTL6WN.js} +4 -4
  20. package/dist/{chunk-ELBCK3V7.js.map → chunk-VOHTL6WN.js.map} +1 -1
  21. package/dist/{chunk-RGMHRPHQ.cjs → chunk-ZKJLDBXA.cjs} +3 -3
  22. package/dist/{chunk-RGMHRPHQ.cjs.map → chunk-ZKJLDBXA.cjs.map} +1 -1
  23. package/dist/dark-variant.css +3 -0
  24. package/dist/dark-variant.css.map +1 -0
  25. package/dist/editor.cjs +5 -5
  26. package/dist/editor.js +3 -3
  27. package/dist/index.cjs +125 -125
  28. package/dist/index.js +23 -23
  29. package/dist/inputs.cjs +9 -9
  30. package/dist/inputs.js +1 -1
  31. package/dist/json-editor.cjs +19 -19
  32. package/dist/json-editor.js +8 -8
  33. package/dist/magic-wand.cjs +5 -5
  34. package/dist/magic-wand.js +4 -4
  35. package/dist/media-viewer.cjs +1 -1
  36. package/dist/media-viewer.js +1 -1
  37. package/dist/mobile-menu.cjs +1 -1
  38. package/dist/mobile-menu.js +1 -1
  39. package/dist/prompt-input.cjs +4 -4
  40. package/dist/prompt-input.js +4 -4
  41. package/dist/reason-tag.cjs +4 -4
  42. package/dist/reason-tag.js +4 -4
  43. package/dist/sidebar.cjs +1 -1
  44. package/dist/sidebar.js +1 -1
  45. package/dist/styles.css +37 -37
  46. package/dist/styles.css.map +1 -1
  47. package/dist/toast.cjs +4 -4
  48. package/dist/toast.js +2 -2
  49. package/package.json +2 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/hooks/use-id.ts","../src/hooks/use-node-registry.ts","../src/theme.ts","../src/hooks/use-theme.ts"],"names":["useReactId","useRef","useState","useCallback","useEffect"],"mappings":";;;;;AAEO,SAAS,MAAM,MAAA,EAAyB;AAC7C,EAAA,MAAM,KAAKA,WAAA,EAAW;AACtB,EAAA,OAAO,MAAA,GAAS,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA,GAAK,EAAA;AACtC;ACWO,SAAS,eAAA,GAAyC;AACvD,EAAA,MAAM,QAAA,GAAWC,YAAA,iBAAO,IAAI,GAAA,EAAuB,CAAA;AACnD,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAIC,eAAS,CAAC,CAAA;AAExC,EAAA,MAAM,YAAA,GAAeC,iBAAA,CAAY,CAAC,EAAA,EAAY,IAAA,KAAmB;AAC/D,IAAA,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,EAAA,EAAI,IAAI,CAAA;AAC7B,IAAA,UAAA,CAAW,CAAC,CAAA,KAAM,CAAA,GAAI,CAAC,CAAA;AAAA,EACzB,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,cAAA,GAAiBA,iBAAA,CAAY,CAAC,EAAA,KAAe;AACjD,IAAA,QAAA,CAAS,OAAA,CAAQ,OAAO,EAAE,CAAA;AAC1B,IAAA,UAAA,CAAW,CAAC,CAAA,KAAM,CAAA,GAAI,CAAC,CAAA;AAAA,EACzB,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,OAAO,EAAE,YAAA,EAAc,cAAA,EAAgB,SAAA,EAAW,QAAA,CAAS,SAAS,OAAA,EAAQ;AAC9E;;;ACEO,IAAM,iBAAA,GAAoB;AAEjC,IAAM,UAAA,GAAa,8BAAA;AAInB,IAAM,SAAA,uBAAgB,GAAA,EAAmB;AACzC,IAAI,UAAA,GAAa,iBAAA;AACjB,IAAI,kBAAA,GAA0C,IAAA;AAE9C,SAAS,SAAA,GAAqB;AAC1B,EAAA,OAAO,OAAO,MAAA,KAAW,WAAA,IAAe,OAAO,QAAA,KAAa,WAAA;AAChE;AAGO,SAAS,kBAAA,GAAsC;AAClD,EAAA,IAAI,CAAC,SAAA,EAAU,EAAG,OAAO,QAAA;AACzB,EAAA,IAAI;AACA,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,YAAA,CAAa,OAAA,CAAQ,UAAU,CAAA;AACpD,IAAA,OAAO,KAAA,KAAU,OAAA,IAAW,KAAA,KAAU,MAAA,GAAS,KAAA,GAAQ,QAAA;AAAA,EAC3D,CAAA,CAAA,MAAQ;AAGJ,IAAA,OAAO,QAAA;AAAA,EACX;AACJ;AAEA,SAAS,WAAA,GAA6B;AAClC,EAAA,IAAI,CAAC,SAAA,EAAU,IAAK,OAAO,MAAA,CAAO,UAAA,KAAe,YAAY,OAAO,OAAA;AACpE,EAAA,OAAO,MAAA,CAAO,UAAA,CAAW,UAAU,CAAA,CAAE,UAAU,MAAA,GAAS,OAAA;AAC5D;AAGO,SAAS,YAAA,CAAa,UAAA,GAA8B,kBAAA,EAAmB,EAAkB;AAC5F,EAAA,OAAO,UAAA,KAAe,QAAA,GAAW,WAAA,EAAY,GAAI,UAAA;AACrD;AAEA,SAAS,gBAAgB,QAAA,EAA+B;AACpD,EAAA,IAAI,CAAC,WAAU,EAAG;AAClB,EAAA,MAAM,OAAO,QAAA,CAAS,eAAA;AAGtB,EAAA,IAAA,CAAK,SAAA,CAAU,MAAA,CAAO,MAAA,EAAQ,QAAA,KAAa,MAAM,CAAA;AACjD,EAAA,IAAA,CAAK,YAAA,CAAa,cAAc,QAAQ,CAAA;AAC5C;AAEA,SAAS,OAAA,GAAgB;AACrB,EAAA,MAAM,aAAa,kBAAA,EAAmB;AACtC,EAAA,MAAM,QAAA,GAAW,aAAa,UAAU,CAAA;AACxC,EAAA,eAAA,CAAgB,QAAQ,CAAA;AACxB,EAAA,SAAA,CAAU,QAAQ,CAAC,QAAA,KAAa,QAAA,CAAS,QAAA,EAAU,UAAU,CAAC,CAAA;AAClE;AAMO,SAAS,mBAAmB,UAAA,EAAmC;AAClE,EAAA,IAAI,WAAU,EAAG;AACb,IAAA,IAAI;AACA,MAAA,IAAI,eAAe,QAAA,EAAU;AACzB,QAAA,MAAA,CAAO,YAAA,CAAa,WAAW,UAAU,CAAA;AAAA,MAC7C,CAAA,MAAO;AACH,QAAA,MAAA,CAAO,YAAA,CAAa,OAAA,CAAQ,UAAA,EAAY,UAAU,CAAA;AAAA,MACtD;AAAA,IACJ,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACJ;AACA,EAAA,OAAA,EAAQ;AACZ;AAGO,SAAS,eAAe,QAAA,EAAqC;AAChE,EAAA,SAAA,CAAU,IAAI,QAAQ,CAAA;AACtB,EAAA,OAAO,MAAM;AACT,IAAA,SAAA,CAAU,OAAO,QAAQ,CAAA;AAAA,EAC7B,CAAA;AACJ;AAOO,SAAS,SAAA,CAAU,OAAA,GAAmC,EAAC,EAAe;AACzE,EAAA,IAAI,OAAA,CAAQ,UAAA,EAAY,UAAA,GAAa,OAAA,CAAQ,UAAA;AAC7C,EAAA,IAAI,CAAC,SAAA,EAAU,EAAG,OAAO,MAAM;AAAA,EAAC,CAAA;AAEhC,EAAA,eAAA,CAAgB,cAAc,CAAA;AAE9B,EAAA,IAAI,CAAC,kBAAA,IAAsB,OAAO,MAAA,CAAO,eAAe,UAAA,EAAY;AAChE,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,UAAA,CAAW,UAAU,CAAA;AAC1C,IAAA,MAAM,iBAAiB,MAAY;AAG/B,MAAA,IAAI,kBAAA,EAAmB,KAAM,QAAA,EAAU,OAAA,EAAQ;AAAA,IACnD,CAAA;AAEA,IAAA,IAAI,OAAO,KAAA,CAAM,gBAAA,KAAqB,UAAA,EAAY;AAC9C,MAAA,KAAA,CAAM,gBAAA,CAAiB,UAAU,cAAc,CAAA;AAC/C,MAAA,kBAAA,GAAqB,MAAM,KAAA,CAAM,mBAAA,CAAoB,QAAA,EAAU,cAAc,CAAA;AAAA,IACjF,CAAA,MAAO;AAEH,MAAA,KAAA,CAAM,YAAY,cAAc,CAAA;AAChC,MAAA,kBAAA,GAAqB,MAAM,KAAA,CAAM,cAAA,CAAe,cAAc,CAAA;AAAA,IAClE;AAAA,EACJ;AAEA,EAAA,OAAO,MAAM;AACT,IAAA,kBAAA,IAAqB;AACrB,IAAA,kBAAA,GAAqB,IAAA;AAAA,EACzB,CAAA;AACJ;ACvHO,SAAS,QAAA,GAA2B;AACvC,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAID,cAAAA,CAAmE;AAAA,IACzF,UAAA,EAAY,QAAA;AAAA,IACZ,QAAA,EAAU;AAAA,GACb,CAAA;AAED,EAAAE,eAAA,CAAU,MAAM;AACZ,IAAA,MAAM,OAAO,MAAY;AACrB,MAAA,QAAA,CAAS,EAAE,UAAA,EAAY,kBAAA,IAAsB,QAAA,EAAU,YAAA,IAAgB,CAAA;AAAA,IAC3E,CAAA;AACA,IAAA,IAAA,EAAK;AACL,IAAA,OAAO,eAAe,IAAI,CAAA;AAAA,EAC9B,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,aAAA,GAAgBD,iBAAAA,CAAY,CAAC,UAAA,KAAsC;AACrE,IAAA,kBAAA,CAAmB,UAAU,CAAA;AAAA,EACjC,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,OAAO,EAAE,GAAG,KAAA,EAAO,aAAA,EAAc;AACrC","file":"chunk-HPNIEYSJ.cjs","sourcesContent":["import { useId as useReactId } from \"react\";\n\nexport function useId(prefix?: string): string {\n const id = useReactId();\n return prefix ? `${prefix}-${id}` : id;\n}\n","import { useCallback, useRef, useState } from \"react\";\n\nexport interface NodeRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\ninterface UseNodeRegistryReturn {\n registerNode: (id: string, rect: NodeRect) => void;\n unregisterNode: (id: string) => void;\n nodeRects: Map<string, NodeRect>;\n version: number;\n}\n\nexport function useNodeRegistry(): UseNodeRegistryReturn {\n const rectsRef = useRef(new Map<string, NodeRect>());\n const [version, setVersion] = useState(0);\n\n const registerNode = useCallback((id: string, rect: NodeRect) => {\n rectsRef.current.set(id, rect);\n setVersion((v) => v + 1);\n }, []);\n\n const unregisterNode = useCallback((id: string) => {\n rectsRef.current.delete(id);\n setVersion((v) => v + 1);\n }, []);\n\n return { registerNode, unregisterNode, nodeRects: rectsRef.current, version };\n}\n","/**\n * Light / dark / **system** theming.\n *\n * `styles.css` in this package defines the dark contract — it redefines the\n * whole `--color-secondary-*` scale under `:where(.dark)` so that \"900\" always\n * means strongest foreground and \"50\" always means faintest surface. This module\n * is the other half of that contract: the thing that decides when `.dark` is on.\n *\n * It lives here rather than in each app because every consumer was hand-rolling\n * it, and every hand-rolled copy had the same two holes:\n *\n * - **No live listener.** The OS preference was read once at boot, so changing\n * the system theme with the page open did nothing. That is a default, not a\n * \"system\" mode.\n * - **No way back.** Choosing light or dark wrote to storage forever, so\n * \"follow my system\" was unreachable after a single click.\n *\n * Both are fixed by treating **\"system\" as the absence of a stored choice**\n * rather than as a third stored value.\n *\n * Every export is safe to call during SSR, where it is a no-op that resolves to\n * `\"light\"`. Pair it with `useTheme`, which deliberately renders `system/light`\n * on the server and syncs in an effect — this project has paid for hydration\n * mismatches caused by reading the real theme during render.\n */\n\nexport type ThemePreference = \"light\" | \"dark\" | \"system\";\nexport type ResolvedTheme = \"light\" | \"dark\";\n\n/**\n * Deliberately the key the showcase already used, so adopting this module keeps\n * a returning visitor's saved choice instead of silently resetting it.\n */\nexport const THEME_STORAGE_KEY = \"fancy-ui.theme\";\n\nconst DARK_QUERY = \"(prefers-color-scheme: dark)\";\n\ntype ThemeListener = (resolved: ResolvedTheme, preference: ThemePreference) => void;\n\nconst listeners = new Set<ThemeListener>();\nlet storageKey = THEME_STORAGE_KEY;\nlet stopWatchingSystem: (() => void) | null = null;\n\nfunction canUseDom(): boolean {\n return typeof window !== \"undefined\" && typeof document !== \"undefined\";\n}\n\n/** The stored choice, or `\"system\"` when there isn't one. */\nexport function getThemePreference(): ThemePreference {\n if (!canUseDom()) return \"system\";\n try {\n const saved = window.localStorage.getItem(storageKey);\n return saved === \"light\" || saved === \"dark\" ? saved : \"system\";\n } catch {\n // Private browsing and blocked storage both throw here. A theme is not\n // worth taking the page down over.\n return \"system\";\n }\n}\n\nfunction systemTheme(): ResolvedTheme {\n if (!canUseDom() || typeof window.matchMedia !== \"function\") return \"light\";\n return window.matchMedia(DARK_QUERY).matches ? \"dark\" : \"light\";\n}\n\n/** What is actually on screen, as opposed to what the user asked for. */\nexport function resolveTheme(preference: ThemePreference = getThemePreference()): ResolvedTheme {\n return preference === \"system\" ? systemTheme() : preference;\n}\n\nfunction applyToDocument(resolved: ResolvedTheme): void {\n if (!canUseDom()) return;\n const root = document.documentElement;\n // The class is what this package's CSS keys off; the attribute is for\n // consumers whose own styles key off `[data-theme]`.\n root.classList.toggle(\"dark\", resolved === \"dark\");\n root.setAttribute(\"data-theme\", resolved);\n}\n\nfunction publish(): void {\n const preference = getThemePreference();\n const resolved = resolveTheme(preference);\n applyToDocument(resolved);\n listeners.forEach((listener) => listener(resolved, preference));\n}\n\n/**\n * Choose a theme. `\"system\"` REMOVES the stored value rather than storing the\n * string — an absent choice is what lets the OS keep speaking for the user.\n */\nexport function setThemePreference(preference: ThemePreference): void {\n if (canUseDom()) {\n try {\n if (preference === \"system\") {\n window.localStorage.removeItem(storageKey);\n } else {\n window.localStorage.setItem(storageKey, preference);\n }\n } catch {\n // As above: still apply it for this page even if it cannot persist.\n }\n }\n publish();\n}\n\n/** Observe theme changes. Returns an unsubscribe. */\nexport function subscribeTheme(listener: ThemeListener): () => void {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n}\n\n/**\n * Apply the current theme and start following the OS. Call once, as early as\n * possible — before first paint if you want to avoid a flash of the wrong\n * theme. Returns a disposer; calling it again is harmless.\n */\nexport function initTheme(options: { storageKey?: string } = {}): () => void {\n if (options.storageKey) storageKey = options.storageKey;\n if (!canUseDom()) return () => {};\n\n applyToDocument(resolveTheme());\n\n if (!stopWatchingSystem && typeof window.matchMedia === \"function\") {\n const media = window.matchMedia(DARK_QUERY);\n const onSystemChange = (): void => {\n // Only \"system\" follows the OS. An explicit choice has to survive\n // the user switching their OS theme, or it isn't a choice.\n if (getThemePreference() === \"system\") publish();\n };\n\n if (typeof media.addEventListener === \"function\") {\n media.addEventListener(\"change\", onSystemChange);\n stopWatchingSystem = () => media.removeEventListener(\"change\", onSystemChange);\n } else {\n // Safari < 14 and friends.\n media.addListener(onSystemChange);\n stopWatchingSystem = () => media.removeListener(onSystemChange);\n }\n }\n\n return () => {\n stopWatchingSystem?.();\n stopWatchingSystem = null;\n };\n}\n","import { useCallback, useEffect, useState } from \"react\";\nimport {\n getThemePreference,\n resolveTheme,\n setThemePreference,\n subscribeTheme,\n type ResolvedTheme,\n type ThemePreference,\n} from \"../theme\";\n\nexport type UseThemeResult = {\n /** What the user asked for, including `\"system\"`. */\n preference: ThemePreference;\n /** What is actually on screen. */\n resolved: ResolvedTheme;\n setPreference: (preference: ThemePreference) => void;\n};\n\n/**\n * Read and set the theme from a component, re-rendering when it changes —\n * including when the OS theme changes underneath a `\"system\"` preference.\n *\n * The initial state is deliberately `system` / `light` rather than the real\n * theme: the server has neither storage nor an OS preference, so reading the\n * truth during render is exactly the hydration mismatch this project has\n * already chased down more than once. The effect syncs immediately on mount.\n */\nexport function useTheme(): UseThemeResult {\n const [state, setState] = useState<{ preference: ThemePreference; resolved: ResolvedTheme }>({\n preference: \"system\",\n resolved: \"light\",\n });\n\n useEffect(() => {\n const sync = (): void => {\n setState({ preference: getThemePreference(), resolved: resolveTheme() });\n };\n sync();\n return subscribeTheme(sync);\n }, []);\n\n const setPreference = useCallback((preference: ThemePreference): void => {\n setThemePreference(preference);\n }, []);\n\n return { ...state, setPreference };\n}\n"]}
1
+ {"version":3,"sources":["../src/hooks/use-id.ts","../src/hooks/use-node-registry.ts","../src/theme.ts","../src/hooks/use-theme.ts"],"names":["useReactId","useRef","useState","useCallback","useEffect"],"mappings":"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","file":"chunk-A2JUTNE6.cjs","sourcesContent":["import { useId as useReactId } from \"react\";\n\nexport function useId(prefix?: string): string {\n const id = useReactId();\n return prefix ? `${prefix}-${id}` : id;\n}\n","import { useCallback, useRef, useState } from \"react\";\n\nexport interface NodeRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\ninterface UseNodeRegistryReturn {\n registerNode: (id: string, rect: NodeRect) => void;\n unregisterNode: (id: string) => void;\n nodeRects: Map<string, NodeRect>;\n version: number;\n}\n\nexport function useNodeRegistry(): UseNodeRegistryReturn {\n const rectsRef = useRef(new Map<string, NodeRect>());\n const [version, setVersion] = useState(0);\n\n const registerNode = useCallback((id: string, rect: NodeRect) => {\n rectsRef.current.set(id, rect);\n setVersion((v) => v + 1);\n }, []);\n\n const unregisterNode = useCallback((id: string) => {\n rectsRef.current.delete(id);\n setVersion((v) => v + 1);\n }, []);\n\n return { registerNode, unregisterNode, nodeRects: rectsRef.current, version };\n}\n","/**\n * Light / dark / **system** theming.\n *\n * `styles.css` in this package defines the dark contract — it redefines the\n * whole `--color-secondary-*` scale under `:where(.dark)` so that \"900\" always\n * means strongest foreground and \"50\" always means faintest surface. This module\n * is the other half of that contract: the thing that decides when `.dark` is on.\n *\n * It lives here rather than in each app because every consumer was hand-rolling\n * it, and every hand-rolled copy had the same two holes:\n *\n * - **No live listener.** The OS preference was read once at boot, so changing\n * the system theme with the page open did nothing. That is a default, not a\n * \"system\" mode.\n * - **No way back.** Choosing light or dark wrote to storage forever, so\n * \"follow my system\" was unreachable after a single click.\n *\n * Both are fixed by treating **\"system\" as the absence of a stored choice**\n * rather than as a third stored value.\n *\n * Every export is safe to call during SSR, where it is a no-op that resolves to\n * `\"light\"`. Pair it with `useTheme`, which deliberately renders `system/light`\n * on the server and syncs in an effect — this project has paid for hydration\n * mismatches caused by reading the real theme during render.\n */\n\nexport type ThemePreference = \"light\" | \"dark\" | \"system\";\nexport type ResolvedTheme = \"light\" | \"dark\";\n\n/**\n * Deliberately the key the showcase already used, so adopting this module keeps\n * a returning visitor's saved choice instead of silently resetting it.\n */\nexport const THEME_STORAGE_KEY = \"fancy-ui.theme\";\n\nconst DARK_QUERY = \"(prefers-color-scheme: dark)\";\n\ntype ThemeListener = (resolved: ResolvedTheme, preference: ThemePreference) => void;\n\nconst listeners = new Set<ThemeListener>();\nlet storageKey = THEME_STORAGE_KEY;\nlet stopWatchingSystem: (() => void) | null = null;\n\nfunction canUseDom(): boolean {\n return typeof window !== \"undefined\" && typeof document !== \"undefined\";\n}\n\n/** The stored choice, or `\"system\"` when there isn't one. */\nexport function getThemePreference(): ThemePreference {\n if (!canUseDom()) return \"system\";\n try {\n const saved = window.localStorage.getItem(storageKey);\n return saved === \"light\" || saved === \"dark\" ? saved : \"system\";\n } catch {\n // Private browsing and blocked storage both throw here. A theme is not\n // worth taking the page down over.\n return \"system\";\n }\n}\n\nfunction systemTheme(): ResolvedTheme {\n if (!canUseDom() || typeof window.matchMedia !== \"function\") return \"light\";\n return window.matchMedia(DARK_QUERY).matches ? \"dark\" : \"light\";\n}\n\n/** What is actually on screen, as opposed to what the user asked for. */\nexport function resolveTheme(preference: ThemePreference = getThemePreference()): ResolvedTheme {\n return preference === \"system\" ? systemTheme() : preference;\n}\n\nfunction applyToDocument(resolved: ResolvedTheme): void {\n if (!canUseDom()) return;\n const root = document.documentElement;\n // Both, because hosts key off either: this package's styles.css follows\n // `.dark` and `[data-theme=\"dark\"]` alike (5.53.0), and a consumer's\n // `dark:` variant follows whichever their CSS declares.\n root.classList.toggle(\"dark\", resolved === \"dark\");\n root.setAttribute(\"data-theme\", resolved);\n}\n\nfunction publish(): void {\n const preference = getThemePreference();\n const resolved = resolveTheme(preference);\n applyToDocument(resolved);\n listeners.forEach((listener) => listener(resolved, preference));\n}\n\n/**\n * Choose a theme. `\"system\"` REMOVES the stored value rather than storing the\n * string — an absent choice is what lets the OS keep speaking for the user.\n */\nexport function setThemePreference(preference: ThemePreference): void {\n if (canUseDom()) {\n try {\n if (preference === \"system\") {\n window.localStorage.removeItem(storageKey);\n } else {\n window.localStorage.setItem(storageKey, preference);\n }\n } catch {\n // As above: still apply it for this page even if it cannot persist.\n }\n }\n publish();\n}\n\n/** Observe theme changes. Returns an unsubscribe. */\nexport function subscribeTheme(listener: ThemeListener): () => void {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n}\n\n/**\n * Apply the current theme and start following the OS. Call once, as early as\n * possible — before first paint if you want to avoid a flash of the wrong\n * theme. Returns a disposer; calling it again is harmless.\n */\nexport function initTheme(options: { storageKey?: string } = {}): () => void {\n if (options.storageKey) storageKey = options.storageKey;\n if (!canUseDom()) return () => {};\n\n applyToDocument(resolveTheme());\n\n if (!stopWatchingSystem && typeof window.matchMedia === \"function\") {\n const media = window.matchMedia(DARK_QUERY);\n const onSystemChange = (): void => {\n // Only \"system\" follows the OS. An explicit choice has to survive\n // the user switching their OS theme, or it isn't a choice.\n if (getThemePreference() === \"system\") publish();\n };\n\n if (typeof media.addEventListener === \"function\") {\n media.addEventListener(\"change\", onSystemChange);\n stopWatchingSystem = () => media.removeEventListener(\"change\", onSystemChange);\n } else {\n // Safari < 14 and friends.\n media.addListener(onSystemChange);\n stopWatchingSystem = () => media.removeListener(onSystemChange);\n }\n }\n\n return () => {\n stopWatchingSystem?.();\n stopWatchingSystem = null;\n };\n}\n","import { useCallback, useEffect, useState } from \"react\";\nimport {\n getThemePreference,\n resolveTheme,\n setThemePreference,\n subscribeTheme,\n type ResolvedTheme,\n type ThemePreference,\n} from \"../theme\";\n\nexport type UseThemeResult = {\n /** What the user asked for, including `\"system\"`. */\n preference: ThemePreference;\n /** What is actually on screen. */\n resolved: ResolvedTheme;\n setPreference: (preference: ThemePreference) => void;\n};\n\n/**\n * Read and set the theme from a component, re-rendering when it changes —\n * including when the OS theme changes underneath a `\"system\"` preference.\n *\n * The initial state is deliberately `system` / `light` rather than the real\n * theme: the server has neither storage nor an OS preference, so reading the\n * truth during render is exactly the hydration mismatch this project has\n * already chased down more than once. The effect syncs immediately on mount.\n */\nexport function useTheme(): UseThemeResult {\n const [state, setState] = useState<{ preference: ThemePreference; resolved: ResolvedTheme }>({\n preference: \"system\",\n resolved: \"light\",\n });\n\n useEffect(() => {\n const sync = (): void => {\n setState({ preference: getThemePreference(), resolved: resolveTheme() });\n };\n sync();\n return subscribeTheme(sync);\n }, []);\n\n const setPreference = useCallback((preference: ThemePreference): void => {\n setThemePreference(preference);\n }, []);\n\n return { ...state, setPreference };\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  import { Textarea } from './chunk-UVUBV4VZ.js';
2
+ import { Portal } from './chunk-WHEXTDPV.js';
2
3
  import { Badge } from './chunk-6FCQD2KQ.js';
3
4
  import { Button } from './chunk-26JZ47BJ.js';
4
- import { Portal } from './chunk-WHEXTDPV.js';
5
5
  import { useRef, useState, useCallback, useEffect } from 'react';
6
6
  import { jsx, jsxs } from 'react/jsx-runtime';
7
7
 
@@ -260,5 +260,5 @@ function selectionPoint(ta, start, end) {
260
260
  }
261
261
 
262
262
  export { MagicWand };
263
- //# sourceMappingURL=chunk-N2JESOCJ.js.map
264
- //# sourceMappingURL=chunk-N2JESOCJ.js.map
263
+ //# sourceMappingURL=chunk-EJ6P7K4G.js.map
264
+ //# sourceMappingURL=chunk-EJ6P7K4G.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/MagicWand/MagicWand.tsx"],"names":[],"mappings":"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","file":"chunk-N2JESOCJ.js","sourcesContent":["import { useCallback, useEffect, useRef, useState, type RefObject } from \"react\";\nimport { Button } from \"../Button\";\nimport { Badge } from \"../Badge\";\nimport { Portal } from \"../Portal\";\nimport { Textarea } from \"../inputs/Textarea/Textarea\";\n\n/**\n * MagicWand — selection-anchored floating toolbar for text inputs.\n *\n * When the user highlights text inside the wrapped <Textarea>, a small\n * pill of AI quick-actions floats above the selection. Clicking an\n * action invokes the host callback with the selected substring + range;\n * the host returns a replacement string and MagicWand swaps it in-place.\n *\n * <MagicWand\n * value={body}\n * onValueChange={setBody}\n * actions={[\n * { id: \"rephrase\", label: \"Rephrase\", hint: \"same meaning, different words\",\n * run: async (s) => await ai.rephrase(s) },\n * …\n * ]}\n * />\n *\n * Pass `target` to act on a textarea, input or editor the HOST renders instead:\n * MagicWand then renders no textarea of its own, only the floating wand.\n *\n * <textarea ref={ref} value={body} onChange={(e) => setBody(e.target.value)} />\n * <MagicWand target={ref} value={body} onValueChange={setBody} actions={…} />\n *\n * Two appearances: `\"floating\"` (default — full labels) and `\"pill\"`\n * (icon-only, compact). Auto-hides on click-away or scroll by default.\n */\nexport type MagicWandSelection = { start: number; end: number; text: string };\n\nexport type MagicWandAction = {\n /** Stable id, also shown as the busy-state key. */\n id: string;\n /** Display label (first character used in `pill` mode). */\n label: string;\n /** Tooltip body. */\n hint?: string;\n /** Optional kind chip rendered next to the label. */\n tag?: string;\n /** Returns the replacement text for the selection. */\n run: (selection: string, range: MagicWandSelection) => Promise<string> | string;\n};\n\nexport type MagicWandAppearance = \"pill\" | \"floating\";\n\n/** A viewport rectangle, the shape `getBoundingClientRect()` returns. */\nexport type MagicWandRect = { left: number; top: number; width: number; height: number };\n\n/**\n * How MagicWand reads a selection out of an editor it does not own — a code\n * editor, a contenteditable, anything that is not a plain textarea.\n *\n * Read-only on purpose. The write goes through `onValueChange`, like every other\n * edit, so the host's `value` stays the one source of truth and the editor is\n * updated the way it always is: from that value.\n */\nexport interface MagicWandTargetAdapter {\n /**\n * The editor's element. MagicWand listens on it for `mouseup` / `keyup` /\n * `select` to know the selection may have changed, and treats a click inside\n * it as \"not away\" for `autoHide`.\n */\n element: RefObject<HTMLElement | null> | HTMLElement | null;\n /** The selection as offsets into `value`. `null` or `start === end` hides the wand. */\n getSelection: () => { start: number; end: number } | null;\n /** Where the selection is on screen. Default: the top-centre of `element`. */\n getSelectionRect?: () => MagicWandRect | null;\n}\n\n/** A host-owned textarea / input by ref, or an adapter for anything else. */\nexport type MagicWandTarget =\n | RefObject<HTMLTextAreaElement | HTMLInputElement | null>\n | MagicWandTargetAdapter;\n\nexport interface MagicWandProps {\n /** Controlled value — of the built-in textarea, or of `target`. */\n value: string;\n /** Called on every edit, and with the result of an action. */\n onValueChange: (v: string) => void;\n /** Action list. */\n actions: MagicWandAction[];\n /**\n * Act on a textarea / input / editor the host renders, instead of rendering\n * one. A ref to a textarea or input works as is; anything else takes a\n * `MagicWandTargetAdapter`. `value` must still be the target's current value.\n */\n target?: MagicWandTarget;\n /** Visual treatment. Defaults to \"floating\". */\n appearance?: MagicWandAppearance;\n /** Auto-hide the wand on click-away or scroll. Defaults to true. */\n autoHide?: boolean;\n /** Textarea rows (built-in textarea only). */\n rows?: number;\n /** Placeholder (built-in textarea only). */\n placeholder?: string;\n /** Called after an action runs successfully. */\n onAction?: (action: MagicWandAction, selection: MagicWandSelection, replacement: string) => void;\n}\n\nfunction isAdapter(target: MagicWandTarget): target is MagicWandTargetAdapter {\n return typeof (target as MagicWandTargetAdapter).getSelection === \"function\";\n}\n\nfunction targetElement(target: MagicWandTarget | undefined): HTMLElement | null {\n if (!target) return null;\n if (!isAdapter(target)) return target.current;\n const el = target.element;\n if (!el) return null;\n return el instanceof HTMLElement ? el : el.current;\n}\n\ntype TextField = HTMLTextAreaElement | HTMLInputElement;\n\n/** The selection and the viewport point the wand should sit above, or null. */\nfunction readTarget(\n target: MagicWandTarget,\n): { start: number; end: number; point: { x: number; y: number } } | null {\n if (!isAdapter(target)) {\n const field = target.current as TextField | null;\n if (!field) return null;\n const start = field.selectionStart ?? 0;\n const end = field.selectionEnd ?? 0;\n if (start === end) return null;\n return { start, end, point: selectionPoint(field, start, end) };\n }\n\n const range = target.getSelection();\n if (!range || range.start === range.end) return null;\n const rect = target.getSelectionRect?.() ?? targetElement(target)?.getBoundingClientRect() ?? null;\n const point = rect ? { x: rect.left + rect.width / 2, y: rect.top } : { x: 0, y: 0 };\n return { start: Math.min(range.start, range.end), end: Math.max(range.start, range.end), point };\n}\n\nexport function MagicWand({\n value,\n onValueChange,\n actions,\n target,\n appearance = \"floating\",\n autoHide = true,\n rows = 6,\n placeholder,\n onAction,\n}: MagicWandProps) {\n const taRef = useRef<HTMLTextAreaElement>(null);\n const wandRef = useRef<HTMLDivElement>(null);\n const [sel, setSel] = useState<MagicWandSelection | null>(null);\n const [pos, setPos] = useState<{ x: number; y: number } | null>(null);\n const [busy, setBusy] = useState<string | null>(null);\n\n const external = target !== undefined;\n // Latest value + target for the listeners, which attach once per element.\n const latest = useRef({ value, target });\n latest.current = { value, target };\n\n const hide = useCallback(() => {\n setSel(null);\n setPos(null);\n }, []);\n\n const measureSelection = useCallback(() => {\n const ta = taRef.current;\n if (!ta) return;\n const start = ta.selectionStart ?? 0;\n const end = ta.selectionEnd ?? 0;\n if (start === end) {\n hide();\n return;\n }\n const text = ta.value.slice(start, end);\n setSel({ start, end, text });\n // The built-in wand is positioned inside this component's `relative` box.\n const point = selectionPoint(ta, start, end);\n const box = (ta.offsetParent as HTMLElement | null)?.getBoundingClientRect() ?? ta.getBoundingClientRect();\n setPos({ x: point.x - box.left, y: point.y - box.top });\n }, [hide]);\n\n const measureTarget = useCallback(() => {\n const { target: t, value: v } = latest.current;\n if (!t) return;\n const read = readTarget(t);\n if (!read) {\n hide();\n return;\n }\n setSel({ start: read.start, end: read.end, text: v.slice(read.start, read.end) });\n setPos(read.point);\n }, [hide]);\n\n // The target's element is only known after the host renders it, so it is\n // re-resolved after every render and the listeners move if it changes.\n const [targetEl, setTargetEl] = useState<HTMLElement | null>(null);\n useEffect(() => {\n const next = targetElement(target);\n if (next !== targetEl) setTargetEl(next);\n });\n\n useEffect(() => {\n if (!targetEl) return;\n const events = [\"mouseup\", \"keyup\", \"select\"] as const;\n for (const e of events) targetEl.addEventListener(e, measureTarget);\n return () => {\n for (const e of events) targetEl.removeEventListener(e, measureTarget);\n };\n }, [targetEl, measureTarget]);\n\n useEffect(() => {\n if (!autoHide) return;\n window.addEventListener(\"scroll\", hide, true);\n return () => window.removeEventListener(\"scroll\", hide, true);\n }, [autoHide, hide]);\n\n useEffect(() => {\n if (!autoHide) return;\n const onDocMouseDown = (e: MouseEvent) => {\n const node = e.target as Node;\n if (wandRef.current?.contains(node)) return;\n if (taRef.current?.contains(node)) return;\n if (targetElement(latest.current.target)?.contains(node)) return;\n hide();\n };\n document.addEventListener(\"mousedown\", onDocMouseDown);\n return () => document.removeEventListener(\"mousedown\", onDocMouseDown);\n }, [autoHide, hide]);\n\n const handleAction = async (action: MagicWandAction) => {\n if (!sel) return;\n setBusy(action.id);\n try {\n const replacement = await action.run(sel.text, sel);\n const next = value.slice(0, sel.start) + replacement + value.slice(sel.end);\n onValueChange(next);\n onAction?.(action, sel, replacement);\n } finally {\n setBusy(null);\n hide();\n }\n };\n\n const wand =\n sel && pos ? (\n <Wand\n ref={wandRef}\n pos={pos}\n fixed={external}\n actions={actions}\n appearance={appearance}\n busy={busy}\n onAction={handleAction}\n selectionLength={sel.text.length}\n />\n ) : null;\n\n if (external) {\n return wand ? <Portal>{wand}</Portal> : null;\n }\n\n return (\n <div className=\"relative\">\n <Textarea\n ref={taRef as unknown as React.Ref<HTMLTextAreaElement>}\n value={value}\n onValueChange={onValueChange}\n onSelect={measureSelection}\n onKeyUp={measureSelection}\n onMouseUp={measureSelection}\n rows={rows}\n placeholder={placeholder}\n />\n {wand}\n </div>\n );\n}\n\nfunction Wand({\n ref,\n pos,\n fixed,\n actions,\n appearance,\n busy,\n onAction,\n selectionLength,\n}: {\n ref: React.RefObject<HTMLDivElement | null>;\n pos: { x: number; y: number };\n /** Viewport-positioned (portalled, for an external target) rather than inside the wrapper. */\n fixed: boolean;\n actions: MagicWandAction[];\n appearance: MagicWandAppearance;\n busy: string | null;\n onAction: (a: MagicWandAction) => void;\n selectionLength: number;\n}) {\n return (\n <div\n ref={ref}\n data-react-fancy-magic-wand=\"\"\n className={`${fixed ? \"fixed z-50\" : \"absolute z-20\"} -translate-x-1/2 -translate-y-full`}\n style={{ left: pos.x, top: pos.y - 6 }}\n >\n <div className=\"flex items-center gap-1 rounded-full border border-violet-300 bg-[color:var(--fancy-surface,var(--color-white))] px-1.5 py-1 shadow-lg ring-1 ring-violet-200 dark:border-violet-700 dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))] dark:ring-violet-900\">\n <span className=\"ml-1 mr-0.5 inline-flex items-center gap-1 text-[10px] font-medium text-violet-700 dark:text-violet-300\">\n <span aria-hidden>✦</span>\n {appearance === \"floating\" && (\n <Badge color=\"violet\">{selectionLength} chars</Badge>\n )}\n </span>\n {actions.map((a) => (\n <Button\n key={a.id}\n size=\"sm\"\n variant=\"ghost\"\n data-react-fancy-magic-wand-action={a.id}\n onClick={() => onAction(a)}\n disabled={busy !== null}\n title={a.hint}\n >\n {busy === a.id ? \"…\" : appearance === \"pill\" ? a.label[0] : a.label}\n {appearance === \"floating\" && a.tag && (\n <span className=\"ml-1 rounded-full bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] px-1 text-[9px] font-medium text-zinc-500 dark:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]\">\n {a.tag}\n </span>\n )}\n </Button>\n ))}\n </div>\n <div className=\"absolute left-1/2 top-full -translate-x-1/2 -translate-y-px\">\n <div className=\"h-2 w-2 rotate-45 border-b border-r border-violet-300 bg-[color:var(--fancy-surface,var(--color-white))] dark:border-violet-700 dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\" />\n </div>\n </div>\n );\n}\n\n/**\n * Caret/selection geometry: mirror the field into a hidden div and measure the\n * selected substring's bounding rect. Returns the VIEWPORT point at the top\n * centre of the selection. Not perfect for resized textareas, but works for\n * typical chat composers.\n */\nfunction selectionPoint(ta: TextField, start: number, end: number): { x: number; y: number } {\n const div = document.createElement(\"div\");\n const style = getComputedStyle(ta);\n const props = [\n \"boxSizing\",\n \"width\",\n \"height\",\n \"overflowX\",\n \"overflowY\",\n \"borderTopWidth\",\n \"borderRightWidth\",\n \"borderBottomWidth\",\n \"borderLeftWidth\",\n \"paddingTop\",\n \"paddingRight\",\n \"paddingBottom\",\n \"paddingLeft\",\n \"fontStyle\",\n \"fontVariant\",\n \"fontWeight\",\n \"fontStretch\",\n \"fontSize\",\n \"fontSizeAdjust\",\n \"lineHeight\",\n \"fontFamily\",\n \"textAlign\",\n \"textTransform\",\n \"textIndent\",\n \"letterSpacing\",\n \"wordSpacing\",\n \"tabSize\",\n ] as const;\n for (const p of props) (div.style as any)[p] = (style as any)[p];\n div.style.position = \"absolute\";\n div.style.top = \"-9999px\";\n div.style.left = \"-9999px\";\n div.style.whiteSpace = \"pre-wrap\";\n div.style.wordWrap = \"break-word\";\n\n const value = ta.value;\n div.textContent = value.substring(0, start);\n const startSpan = document.createElement(\"span\");\n startSpan.textContent = value.substring(start, end) || \".\";\n div.appendChild(startSpan);\n\n document.body.appendChild(div);\n const taRect = ta.getBoundingClientRect();\n const spanRect = startSpan.getBoundingClientRect();\n const divRect = div.getBoundingClientRect();\n const offsetX = spanRect.left - divRect.left;\n const offsetY = spanRect.top - divRect.top;\n document.body.removeChild(div);\n\n return {\n x: taRect.left + offsetX + spanRect.width / 2,\n y: taRect.top + offsetY - ta.scrollTop,\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/components/MagicWand/MagicWand.tsx"],"names":[],"mappings":"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","file":"chunk-EJ6P7K4G.js","sourcesContent":["import { useCallback, useEffect, useRef, useState, type RefObject } from \"react\";\nimport { Button } from \"../Button\";\nimport { Badge } from \"../Badge\";\nimport { Portal } from \"../Portal\";\nimport { Textarea } from \"../inputs/Textarea/Textarea\";\n\n/**\n * MagicWand — selection-anchored floating toolbar for text inputs.\n *\n * When the user highlights text inside the wrapped <Textarea>, a small\n * pill of AI quick-actions floats above the selection. Clicking an\n * action invokes the host callback with the selected substring + range;\n * the host returns a replacement string and MagicWand swaps it in-place.\n *\n * <MagicWand\n * value={body}\n * onValueChange={setBody}\n * actions={[\n * { id: \"rephrase\", label: \"Rephrase\", hint: \"same meaning, different words\",\n * run: async (s) => await ai.rephrase(s) },\n * …\n * ]}\n * />\n *\n * Pass `target` to act on a textarea, input or editor the HOST renders instead:\n * MagicWand then renders no textarea of its own, only the floating wand.\n *\n * <textarea ref={ref} value={body} onChange={(e) => setBody(e.target.value)} />\n * <MagicWand target={ref} value={body} onValueChange={setBody} actions={…} />\n *\n * Two appearances: `\"floating\"` (default — full labels) and `\"pill\"`\n * (icon-only, compact). Auto-hides on click-away or scroll by default.\n */\nexport type MagicWandSelection = { start: number; end: number; text: string };\n\nexport type MagicWandAction = {\n /** Stable id, also shown as the busy-state key. */\n id: string;\n /** Display label (first character used in `pill` mode). */\n label: string;\n /** Tooltip body. */\n hint?: string;\n /** Optional kind chip rendered next to the label. */\n tag?: string;\n /** Returns the replacement text for the selection. */\n run: (selection: string, range: MagicWandSelection) => Promise<string> | string;\n};\n\nexport type MagicWandAppearance = \"pill\" | \"floating\";\n\n/** A viewport rectangle, the shape `getBoundingClientRect()` returns. */\nexport type MagicWandRect = { left: number; top: number; width: number; height: number };\n\n/**\n * How MagicWand reads a selection out of an editor it does not own — a code\n * editor, a contenteditable, anything that is not a plain textarea.\n *\n * Read-only on purpose. The write goes through `onValueChange`, like every other\n * edit, so the host's `value` stays the one source of truth and the editor is\n * updated the way it always is: from that value.\n */\nexport interface MagicWandTargetAdapter {\n /**\n * The editor's element. MagicWand listens on it for `mouseup` / `keyup` /\n * `select` to know the selection may have changed, and treats a click inside\n * it as \"not away\" for `autoHide`.\n */\n element: RefObject<HTMLElement | null> | HTMLElement | null;\n /** The selection as offsets into `value`. `null` or `start === end` hides the wand. */\n getSelection: () => { start: number; end: number } | null;\n /** Where the selection is on screen. Default: the top-centre of `element`. */\n getSelectionRect?: () => MagicWandRect | null;\n}\n\n/** A host-owned textarea / input by ref, or an adapter for anything else. */\nexport type MagicWandTarget =\n | RefObject<HTMLTextAreaElement | HTMLInputElement | null>\n | MagicWandTargetAdapter;\n\nexport interface MagicWandProps {\n /** Controlled value — of the built-in textarea, or of `target`. */\n value: string;\n /** Called on every edit, and with the result of an action. */\n onValueChange: (v: string) => void;\n /** Action list. */\n actions: MagicWandAction[];\n /**\n * Act on a textarea / input / editor the host renders, instead of rendering\n * one. A ref to a textarea or input works as is; anything else takes a\n * `MagicWandTargetAdapter`. `value` must still be the target's current value.\n */\n target?: MagicWandTarget;\n /** Visual treatment. Defaults to \"floating\". */\n appearance?: MagicWandAppearance;\n /** Auto-hide the wand on click-away or scroll. Defaults to true. */\n autoHide?: boolean;\n /** Textarea rows (built-in textarea only). */\n rows?: number;\n /** Placeholder (built-in textarea only). */\n placeholder?: string;\n /** Called after an action runs successfully. */\n onAction?: (action: MagicWandAction, selection: MagicWandSelection, replacement: string) => void;\n}\n\nfunction isAdapter(target: MagicWandTarget): target is MagicWandTargetAdapter {\n return typeof (target as MagicWandTargetAdapter).getSelection === \"function\";\n}\n\nfunction targetElement(target: MagicWandTarget | undefined): HTMLElement | null {\n if (!target) return null;\n if (!isAdapter(target)) return target.current;\n const el = target.element;\n if (!el) return null;\n return el instanceof HTMLElement ? el : el.current;\n}\n\ntype TextField = HTMLTextAreaElement | HTMLInputElement;\n\n/** The selection and the viewport point the wand should sit above, or null. */\nfunction readTarget(\n target: MagicWandTarget,\n): { start: number; end: number; point: { x: number; y: number } } | null {\n if (!isAdapter(target)) {\n const field = target.current as TextField | null;\n if (!field) return null;\n const start = field.selectionStart ?? 0;\n const end = field.selectionEnd ?? 0;\n if (start === end) return null;\n return { start, end, point: selectionPoint(field, start, end) };\n }\n\n const range = target.getSelection();\n if (!range || range.start === range.end) return null;\n const rect = target.getSelectionRect?.() ?? targetElement(target)?.getBoundingClientRect() ?? null;\n const point = rect ? { x: rect.left + rect.width / 2, y: rect.top } : { x: 0, y: 0 };\n return { start: Math.min(range.start, range.end), end: Math.max(range.start, range.end), point };\n}\n\nexport function MagicWand({\n value,\n onValueChange,\n actions,\n target,\n appearance = \"floating\",\n autoHide = true,\n rows = 6,\n placeholder,\n onAction,\n}: MagicWandProps) {\n const taRef = useRef<HTMLTextAreaElement>(null);\n const wandRef = useRef<HTMLDivElement>(null);\n const [sel, setSel] = useState<MagicWandSelection | null>(null);\n const [pos, setPos] = useState<{ x: number; y: number } | null>(null);\n const [busy, setBusy] = useState<string | null>(null);\n\n const external = target !== undefined;\n // Latest value + target for the listeners, which attach once per element.\n const latest = useRef({ value, target });\n latest.current = { value, target };\n\n const hide = useCallback(() => {\n setSel(null);\n setPos(null);\n }, []);\n\n const measureSelection = useCallback(() => {\n const ta = taRef.current;\n if (!ta) return;\n const start = ta.selectionStart ?? 0;\n const end = ta.selectionEnd ?? 0;\n if (start === end) {\n hide();\n return;\n }\n const text = ta.value.slice(start, end);\n setSel({ start, end, text });\n // The built-in wand is positioned inside this component's `relative` box.\n const point = selectionPoint(ta, start, end);\n const box = (ta.offsetParent as HTMLElement | null)?.getBoundingClientRect() ?? ta.getBoundingClientRect();\n setPos({ x: point.x - box.left, y: point.y - box.top });\n }, [hide]);\n\n const measureTarget = useCallback(() => {\n const { target: t, value: v } = latest.current;\n if (!t) return;\n const read = readTarget(t);\n if (!read) {\n hide();\n return;\n }\n setSel({ start: read.start, end: read.end, text: v.slice(read.start, read.end) });\n setPos(read.point);\n }, [hide]);\n\n // The target's element is only known after the host renders it, so it is\n // re-resolved after every render and the listeners move if it changes.\n const [targetEl, setTargetEl] = useState<HTMLElement | null>(null);\n useEffect(() => {\n const next = targetElement(target);\n if (next !== targetEl) setTargetEl(next);\n });\n\n useEffect(() => {\n if (!targetEl) return;\n const events = [\"mouseup\", \"keyup\", \"select\"] as const;\n for (const e of events) targetEl.addEventListener(e, measureTarget);\n return () => {\n for (const e of events) targetEl.removeEventListener(e, measureTarget);\n };\n }, [targetEl, measureTarget]);\n\n useEffect(() => {\n if (!autoHide) return;\n window.addEventListener(\"scroll\", hide, true);\n return () => window.removeEventListener(\"scroll\", hide, true);\n }, [autoHide, hide]);\n\n useEffect(() => {\n if (!autoHide) return;\n const onDocMouseDown = (e: MouseEvent) => {\n const node = e.target as Node;\n if (wandRef.current?.contains(node)) return;\n if (taRef.current?.contains(node)) return;\n if (targetElement(latest.current.target)?.contains(node)) return;\n hide();\n };\n document.addEventListener(\"mousedown\", onDocMouseDown);\n return () => document.removeEventListener(\"mousedown\", onDocMouseDown);\n }, [autoHide, hide]);\n\n const handleAction = async (action: MagicWandAction) => {\n if (!sel) return;\n setBusy(action.id);\n try {\n const replacement = await action.run(sel.text, sel);\n const next = value.slice(0, sel.start) + replacement + value.slice(sel.end);\n onValueChange(next);\n onAction?.(action, sel, replacement);\n } finally {\n setBusy(null);\n hide();\n }\n };\n\n const wand =\n sel && pos ? (\n <Wand\n ref={wandRef}\n pos={pos}\n fixed={external}\n actions={actions}\n appearance={appearance}\n busy={busy}\n onAction={handleAction}\n selectionLength={sel.text.length}\n />\n ) : null;\n\n if (external) {\n return wand ? <Portal>{wand}</Portal> : null;\n }\n\n return (\n <div className=\"relative\">\n <Textarea\n ref={taRef as unknown as React.Ref<HTMLTextAreaElement>}\n value={value}\n onValueChange={onValueChange}\n onSelect={measureSelection}\n onKeyUp={measureSelection}\n onMouseUp={measureSelection}\n rows={rows}\n placeholder={placeholder}\n />\n {wand}\n </div>\n );\n}\n\nfunction Wand({\n ref,\n pos,\n fixed,\n actions,\n appearance,\n busy,\n onAction,\n selectionLength,\n}: {\n ref: React.RefObject<HTMLDivElement | null>;\n pos: { x: number; y: number };\n /** Viewport-positioned (portalled, for an external target) rather than inside the wrapper. */\n fixed: boolean;\n actions: MagicWandAction[];\n appearance: MagicWandAppearance;\n busy: string | null;\n onAction: (a: MagicWandAction) => void;\n selectionLength: number;\n}) {\n return (\n <div\n ref={ref}\n data-react-fancy-magic-wand=\"\"\n className={`${fixed ? \"fixed z-50\" : \"absolute z-20\"} -translate-x-1/2 -translate-y-full`}\n style={{ left: pos.x, top: pos.y - 6 }}\n >\n <div className=\"flex items-center gap-1 rounded-full border border-violet-300 bg-[color:var(--fancy-surface,var(--color-white))] px-1.5 py-1 shadow-lg ring-1 ring-violet-200 dark:border-violet-700 dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))] dark:ring-violet-900\">\n <span className=\"ml-1 mr-0.5 inline-flex items-center gap-1 text-[10px] font-medium text-violet-700 dark:text-violet-300\">\n <span aria-hidden>✦</span>\n {appearance === \"floating\" && (\n <Badge color=\"violet\">{selectionLength} chars</Badge>\n )}\n </span>\n {actions.map((a) => (\n <Button\n key={a.id}\n size=\"sm\"\n variant=\"ghost\"\n data-react-fancy-magic-wand-action={a.id}\n onClick={() => onAction(a)}\n disabled={busy !== null}\n title={a.hint}\n >\n {busy === a.id ? \"…\" : appearance === \"pill\" ? a.label[0] : a.label}\n {appearance === \"floating\" && a.tag && (\n <span className=\"ml-1 rounded-full bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] px-1 text-[9px] font-medium text-zinc-500 dark:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]\">\n {a.tag}\n </span>\n )}\n </Button>\n ))}\n </div>\n <div className=\"absolute left-1/2 top-full -translate-x-1/2 -translate-y-px\">\n <div className=\"h-2 w-2 rotate-45 border-b border-r border-violet-300 bg-[color:var(--fancy-surface,var(--color-white))] dark:border-violet-700 dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\" />\n </div>\n </div>\n );\n}\n\n/**\n * Caret/selection geometry: mirror the field into a hidden div and measure the\n * selected substring's bounding rect. Returns the VIEWPORT point at the top\n * centre of the selection. Not perfect for resized textareas, but works for\n * typical chat composers.\n */\nfunction selectionPoint(ta: TextField, start: number, end: number): { x: number; y: number } {\n const div = document.createElement(\"div\");\n const style = getComputedStyle(ta);\n const props = [\n \"boxSizing\",\n \"width\",\n \"height\",\n \"overflowX\",\n \"overflowY\",\n \"borderTopWidth\",\n \"borderRightWidth\",\n \"borderBottomWidth\",\n \"borderLeftWidth\",\n \"paddingTop\",\n \"paddingRight\",\n \"paddingBottom\",\n \"paddingLeft\",\n \"fontStyle\",\n \"fontVariant\",\n \"fontWeight\",\n \"fontStretch\",\n \"fontSize\",\n \"fontSizeAdjust\",\n \"lineHeight\",\n \"fontFamily\",\n \"textAlign\",\n \"textTransform\",\n \"textIndent\",\n \"letterSpacing\",\n \"wordSpacing\",\n \"tabSize\",\n ] as const;\n for (const p of props) (div.style as any)[p] = (style as any)[p];\n div.style.position = \"absolute\";\n div.style.top = \"-9999px\";\n div.style.left = \"-9999px\";\n div.style.whiteSpace = \"pre-wrap\";\n div.style.wordWrap = \"break-word\";\n\n const value = ta.value;\n div.textContent = value.substring(0, start);\n const startSpan = document.createElement(\"span\");\n startSpan.textContent = value.substring(start, end) || \".\";\n div.appendChild(startSpan);\n\n document.body.appendChild(div);\n const taRect = ta.getBoundingClientRect();\n const spanRect = startSpan.getBoundingClientRect();\n const divRect = div.getBoundingClientRect();\n const offsetX = spanRect.left - divRect.left;\n const offsetY = spanRect.top - divRect.top;\n document.body.removeChild(div);\n\n return {\n x: taRect.left + offsetX + spanRect.width / 2,\n y: taRect.top + offsetY - ta.scrollTop,\n };\n}\n"]}
@@ -7,12 +7,12 @@ var chunkTLRFXSPA_cjs = require('./chunk-TLRFXSPA.cjs');
7
7
  var chunkMAZEX4EZ_cjs = require('./chunk-MAZEX4EZ.cjs');
8
8
  var chunkP53432AK_cjs = require('./chunk-P53432AK.cjs');
9
9
  var chunkO3F6DYWR_cjs = require('./chunk-O3F6DYWR.cjs');
10
+ var chunk4XMBXG4K_cjs = require('./chunk-4XMBXG4K.cjs');
10
11
  var chunkGKLETY7D_cjs = require('./chunk-GKLETY7D.cjs');
12
+ var chunkKJ22KE56_cjs = require('./chunk-KJ22KE56.cjs');
11
13
  var chunkMOU6O324_cjs = require('./chunk-MOU6O324.cjs');
12
14
  var chunkCAMQ5B3V_cjs = require('./chunk-CAMQ5B3V.cjs');
13
15
  var chunkLCNHEYIM_cjs = require('./chunk-LCNHEYIM.cjs');
14
- var chunk4XMBXG4K_cjs = require('./chunk-4XMBXG4K.cjs');
15
- var chunkKJ22KE56_cjs = require('./chunk-KJ22KE56.cjs');
16
16
  var chunkZU5Q77OT_cjs = require('./chunk-ZU5Q77OT.cjs');
17
17
  var react = require('react');
18
18
  var jsxRuntime = require('react/jsx-runtime');
@@ -1350,5 +1350,5 @@ exports.parsePath = parsePath;
1350
1350
  exports.pathToString = pathToString;
1351
1351
  exports.resolveKeyRule = resolveKeyRule;
1352
1352
  exports.typeMatches = typeMatches;
1353
- //# sourceMappingURL=chunk-JBHQQGG5.cjs.map
1354
- //# sourceMappingURL=chunk-JBHQQGG5.cjs.map
1353
+ //# sourceMappingURL=chunk-SW2MDI2E.cjs.map
1354
+ //# sourceMappingURL=chunk-SW2MDI2E.cjs.map