@particle-academy/react-fancy 5.49.0 → 5.50.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 (85) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/button.cjs +4 -4
  3. package/dist/button.js +2 -2
  4. package/dist/{chunk-WBJRFWZE.cjs → chunk-2KZINS6D.cjs} +4 -4
  5. package/dist/{chunk-WBJRFWZE.cjs.map → chunk-2KZINS6D.cjs.map} +1 -1
  6. package/dist/chunk-6TBRBN7H.js +212 -0
  7. package/dist/chunk-6TBRBN7H.js.map +1 -0
  8. package/dist/{chunk-VM5BNL4L.js → chunk-7MJ7VEKE.js} +3 -3
  9. package/dist/{chunk-VM5BNL4L.js.map → chunk-7MJ7VEKE.js.map} +1 -1
  10. package/dist/{chunk-XOWWNG7D.js → chunk-ASS6EWNI.js} +3 -3
  11. package/dist/{chunk-XOWWNG7D.js.map → chunk-ASS6EWNI.js.map} +1 -1
  12. package/dist/{chunk-JHDO6CRT.js → chunk-FU6R6BZ4.js} +3 -3
  13. package/dist/{chunk-JHDO6CRT.js.map → chunk-FU6R6BZ4.js.map} +1 -1
  14. package/dist/{chunk-3XJKJQSY.cjs → chunk-FWA4PCW7.cjs} +4 -4
  15. package/dist/{chunk-3XJKJQSY.cjs.map → chunk-FWA4PCW7.cjs.map} +1 -1
  16. package/dist/{chunk-EEKXWXYO.cjs → chunk-GCWIZNN2.cjs} +4 -4
  17. package/dist/{chunk-EEKXWXYO.cjs.map → chunk-GCWIZNN2.cjs.map} +1 -1
  18. package/dist/{chunk-TKDUT7ZA.cjs → chunk-IPBEVSSC.cjs} +6 -6
  19. package/dist/{chunk-TKDUT7ZA.cjs.map → chunk-IPBEVSSC.cjs.map} +1 -1
  20. package/dist/{chunk-LP3HMRQT.cjs → chunk-IYVV4RD3.cjs} +4 -4
  21. package/dist/{chunk-LP3HMRQT.cjs.map → chunk-IYVV4RD3.cjs.map} +1 -1
  22. package/dist/{chunk-GMNMJLZO.js → chunk-K6EJLBBQ.js} +12 -6
  23. package/dist/chunk-K6EJLBBQ.js.map +1 -0
  24. package/dist/{chunk-ZLYOMQ2J.js → chunk-KODD54GT.js} +3 -3
  25. package/dist/{chunk-ZLYOMQ2J.js.map → chunk-KODD54GT.js.map} +1 -1
  26. package/dist/{chunk-ODAPI4QJ.cjs → chunk-M52IS5SO.cjs} +11 -11
  27. package/dist/{chunk-ODAPI4QJ.cjs.map → chunk-M52IS5SO.cjs.map} +1 -1
  28. package/dist/{chunk-FNYUUVOK.cjs → chunk-MFQ5NBSV.cjs} +14 -7
  29. package/dist/chunk-MFQ5NBSV.cjs.map +1 -0
  30. package/dist/{chunk-I6XHZ3S2.cjs → chunk-NAQJF3G6.cjs} +4 -4
  31. package/dist/{chunk-I6XHZ3S2.cjs.map → chunk-NAQJF3G6.cjs.map} +1 -1
  32. package/dist/{chunk-KST6GW2B.cjs → chunk-NCXH5O4I.cjs} +6 -6
  33. package/dist/{chunk-KST6GW2B.cjs.map → chunk-NCXH5O4I.cjs.map} +1 -1
  34. package/dist/{chunk-K74R6IEB.js → chunk-PCTZNK2Z.js} +4 -4
  35. package/dist/{chunk-K74R6IEB.js.map → chunk-PCTZNK2Z.js.map} +1 -1
  36. package/dist/{chunk-DUOZSOSB.cjs → chunk-RU6ZTA4M.cjs} +3 -3
  37. package/dist/chunk-RU6ZTA4M.cjs.map +1 -0
  38. package/dist/{chunk-62WPAKFS.js → chunk-UDEEPMQ4.js} +3 -3
  39. package/dist/{chunk-62WPAKFS.js.map → chunk-UDEEPMQ4.js.map} +1 -1
  40. package/dist/{chunk-VKRQHLM5.js → chunk-VB3QJ6QE.js} +3 -3
  41. package/dist/{chunk-VKRQHLM5.js.map → chunk-VB3QJ6QE.js.map} +1 -1
  42. package/dist/{chunk-RLC2BE3D.cjs → chunk-VWBKFQQN.cjs} +15 -15
  43. package/dist/{chunk-RLC2BE3D.cjs.map → chunk-VWBKFQQN.cjs.map} +1 -1
  44. package/dist/chunk-X2FC7ODX.cjs +215 -0
  45. package/dist/chunk-X2FC7ODX.cjs.map +1 -0
  46. package/dist/{chunk-SK4AEUO4.js → chunk-XBC3M4BL.js} +3 -3
  47. package/dist/{chunk-SK4AEUO4.js.map → chunk-XBC3M4BL.js.map} +1 -1
  48. package/dist/{chunk-XWXV6HSK.js → chunk-XZUGREDI.js} +3 -3
  49. package/dist/chunk-XZUGREDI.js.map +1 -0
  50. package/dist/{chunk-MQEV7XEG.js → chunk-YOTCZUNW.js} +4 -4
  51. package/dist/{chunk-MQEV7XEG.js.map → chunk-YOTCZUNW.js.map} +1 -1
  52. package/dist/content-renderer.cjs +5 -5
  53. package/dist/content-renderer.js +2 -2
  54. package/dist/editor.cjs +5 -5
  55. package/dist/editor.js +3 -3
  56. package/dist/faux-client.cjs +2 -2
  57. package/dist/faux-client.js +1 -1
  58. package/dist/image-viewer.cjs +3 -3
  59. package/dist/image-viewer.js +2 -2
  60. package/dist/index.cjs +48 -44
  61. package/dist/index.d.cts +74 -7
  62. package/dist/index.d.ts +74 -7
  63. package/dist/index.js +12 -12
  64. package/dist/json-editor.cjs +14 -14
  65. package/dist/json-editor.js +3 -3
  66. package/dist/magic-wand.cjs +4 -4
  67. package/dist/magic-wand.js +3 -3
  68. package/dist/media-viewer.cjs +5 -5
  69. package/dist/media-viewer.js +4 -4
  70. package/dist/pdf-viewer.cjs +3 -3
  71. package/dist/pdf-viewer.js +2 -2
  72. package/dist/prompt-input.cjs +4 -4
  73. package/dist/prompt-input.js +3 -3
  74. package/dist/reason-tag.cjs +4 -4
  75. package/dist/reason-tag.js +3 -3
  76. package/docs/ContentRenderer.md +36 -0
  77. package/package.json +1 -1
  78. package/dist/chunk-DUOZSOSB.cjs.map +0 -1
  79. package/dist/chunk-FNYUUVOK.cjs.map +0 -1
  80. package/dist/chunk-GMNMJLZO.js.map +0 -1
  81. package/dist/chunk-LRNWD26Z.cjs +0 -82
  82. package/dist/chunk-LRNWD26Z.cjs.map +0 -1
  83. package/dist/chunk-QXYO76NZ.js +0 -79
  84. package/dist/chunk-QXYO76NZ.js.map +0 -1
  85. package/dist/chunk-XWXV6HSK.js.map +0 -1
package/dist/index.d.ts CHANGED
@@ -131,14 +131,81 @@ declare function cn(...inputs: ClassValue[]): string;
131
131
  */
132
132
  declare function sanitizeHref(href: string | undefined | null): string | undefined;
133
133
  /**
134
- * Sanitize an HTML string by removing dangerous tags (script, iframe, etc.),
135
- * event-handler attributes (`onclick`, `onerror`, ...), and dangerous URI
136
- * schemes in `href`/`src` attributes.
134
+ * Allowlist options for {@link sanitizeHtml}. Passing ANY options object
135
+ * (even `{}`) switches from the default blocklist to allowlist mode, where
136
+ * everything not listed is removed.
137
+ */
138
+ interface SanitizeHtmlOptions {
139
+ /**
140
+ * Tags to keep, e.g. `["p", "a", "strong"]`. A tag that is not listed is
141
+ * removed but its text and (cleaned) children are kept. Default: none, so
142
+ * only text survives.
143
+ *
144
+ * Some tags are removed WITH their content even when listed, because no
145
+ * attribute filtering makes them safe: `script`, `style`, `iframe`, `frame`,
146
+ * `frameset`, `object`, `embed`, `applet`, `link`, `meta`, `base`, `form`,
147
+ * `title`, `template`, `noscript`, `noembed`, `noframes`, `xmp`,
148
+ * `plaintext`, `svg` and `math`.
149
+ */
150
+ allowedTags?: readonly string[];
151
+ /**
152
+ * Attributes to keep. Either one list for every allowed tag, or a map from
153
+ * tag name to its list, where the key `"*"` applies to every tag. A trailing
154
+ * `*` matches a prefix (`"data-*"`, `"aria-*"`). Default: none.
155
+ *
156
+ * Never kept, even when listed: event handlers (`on*`), `srcdoc` and
157
+ * `srcset`.
158
+ */
159
+ allowedAttributes?: readonly string[] | Readonly<Record<string, readonly string[]>>;
160
+ /**
161
+ * URL schemes allowed in URL attributes (`href`, `src`, `action`,
162
+ * `formaction`, `cite`, `poster`, `background`, `ping`, `xlink:href`), with
163
+ * the colon: `["https:", "mailto:"]`. A URL attribute whose scheme is not
164
+ * listed is removed. Default: `["http:", "https:", "mailto:"]`.
165
+ *
166
+ * `javascript:` and `vbscript:` are never kept, even when listed.
167
+ */
168
+ allowedSchemes?: readonly string[];
169
+ /**
170
+ * Keep URLs with no scheme that stay on this site: `/x`, `x/y`, `../x`,
171
+ * `?q`, `#frag`. Protocol-relative URLs (`//host/x`, and the backslash
172
+ * spellings a browser treats the same way) leave the site and are always
173
+ * removed. Default: `false`.
174
+ */
175
+ allowRelative?: boolean;
176
+ }
177
+ /**
178
+ * Sanitize an HTML string.
179
+ *
180
+ * **With no options** (the default blocklist): removes dangerous tags (script,
181
+ * iframe, etc.), event-handler attributes (`onclick`, `onerror`, ...), and
182
+ * dangerous URI schemes in `href`/`src` attributes. Everything else is kept.
183
+ * In non-browser environments returns the input unchanged — sanitize on the
184
+ * server in those cases.
185
+ *
186
+ * **With options** (allowlist mode): keeps ONLY the listed tags, attributes and
187
+ * URL schemes; see {@link SanitizeHtmlOptions}. The allowlist can only narrow:
188
+ * event handlers, `javascript:` URLs and script-capable tags never survive,
189
+ * even when listed. In non-browser environments the input is returned as
190
+ * escaped text (fail closed).
191
+ *
192
+ * Uses the browser's `DOMParser`.
193
+ */
194
+ declare function sanitizeHtml(html: string, options?: SanitizeHtmlOptions): string;
195
+
196
+ /**
197
+ * Render markdown (GitHub-flavoured) to sanitised HTML — the same parser and
198
+ * output `ContentRenderer` uses, so a host never has to import the parser
199
+ * itself.
200
+ *
201
+ * With no `sanitize` options the default blocklist of {@link sanitizeHtml} is
202
+ * applied; pass allowlist options to keep only what you list:
137
203
  *
138
- * Uses the browser's `DOMParser`. In non-browser environments returns the
139
- * input unchanged — sanitize on the server in those cases.
204
+ * ```ts
205
+ * renderMarkdown(md, { allowedTags: ["p", "strong", "a"], allowedAttributes: ["href"] })
206
+ * ```
140
207
  */
141
- declare function sanitizeHtml(html: string): string;
208
+ declare function renderMarkdown(markdown: string, sanitize?: SanitizeHtmlOptions): string;
142
209
 
143
210
  /**
144
211
  * One state slot that works controlled or uncontrolled.
@@ -316,4 +383,4 @@ type UseThemeResult = {
316
383
  */
317
384
  declare function useTheme(): UseThemeResult;
318
385
 
319
- export { type NodeRect, Placement, type ResolvedTheme, THEME_STORAGE_KEY, type ThemePreference, type UseThemeResult, cn, getThemePreference, initTheme, resolveTheme, sanitizeHref, sanitizeHtml, setThemePreference, subscribeTheme, useAnimation, useControllableState, useEscapeKey, useFloatingPosition, useFocusTrap, useId, useNodeRegistry, useOutsideClick, usePanZoom, useTheme };
386
+ export { type NodeRect, Placement, type ResolvedTheme, type SanitizeHtmlOptions, THEME_STORAGE_KEY, type ThemePreference, type UseThemeResult, cn, getThemePreference, initTheme, renderMarkdown, resolveTheme, sanitizeHref, sanitizeHtml, setThemePreference, subscribeTheme, useAnimation, useControllableState, useEscapeKey, useFloatingPosition, useFocusTrap, useId, useNodeRegistry, useOutsideClick, usePanZoom, useTheme };
package/dist/index.js CHANGED
@@ -19,7 +19,7 @@ export { TimeGrid } from './chunk-7CN7IHES.js';
19
19
  export { TimePicker } from './chunk-OTCLBINT.js';
20
20
  export { Timeline } from './chunk-7JDVCQXD.js';
21
21
  export { Toast, useToast } from './chunk-3AQLEMH7.js';
22
- export { ReasonTag } from './chunk-62WPAKFS.js';
22
+ export { ReasonTag } from './chunk-UDEEPMQ4.js';
23
23
  export { Section } from './chunk-SLJD25VV.js';
24
24
  export { Separator } from './chunk-ZLWCZV7R.js';
25
25
  export { Sidebar, useSidebar } from './chunk-YD4SXA6K.js';
@@ -31,7 +31,7 @@ export { Pillbox } from './chunk-FSRPZYZZ.js';
31
31
  export { Popover, usePopover } from './chunk-6KXAFP2T.js';
32
32
  export { Profile } from './chunk-KQQA7JBC.js';
33
33
  export { Progress } from './chunk-P7PKK5BO.js';
34
- export { PromptInput, browserPlatformHints, detectPlatform, modifierKeyLabel } from './chunk-SK4AEUO4.js';
34
+ export { PromptInput, browserPlatformHints, detectPlatform, modifierKeyLabel } from './chunk-XBC3M4BL.js';
35
35
  export { PullQuote } from './chunk-NDUEPDNS.js';
36
36
  export { QrCode, encodeQr, selectMode } from './chunk-RLE5WYY6.js';
37
37
  export { MobileMenu, useMobileMenu } from './chunk-P7F32HIH.js';
@@ -41,39 +41,39 @@ export { Navbar, useNavbar } from './chunk-IWXP2YUP.js';
41
41
  export { OtpInput } from './chunk-XDJSFGXW.js';
42
42
  export { Pagination } from './chunk-YF2EHBTV.js';
43
43
  export { InputTag, contentEditableAdapter, controlledAdapter, inputAdapter, textareaAdapter } from './chunk-Q7O5TLVU.js';
44
- export { JSON_FIELD_TYPES, JsonEditor, applyJsonEdit, describeEdit, findJsonConflicts, getAtPath, inferType, parseKeyMap, parsePath, pathToString, resolveKeyRule, typeMatches } from './chunk-VM5BNL4L.js';
44
+ export { JSON_FIELD_TYPES, JsonEditor, applyJsonEdit, describeEdit, findJsonConflicts, getAtPath, inferType, parseKeyMap, parsePath, pathToString, resolveKeyRule, typeMatches } from './chunk-7MJ7VEKE.js';
45
45
  export { Input } from './chunk-EEWRLFTI.js';
46
46
  export { Select } from './chunk-IECPLOSZ.js';
47
47
  export { Switch } from './chunk-NBAHXZHJ.js';
48
48
  export { DatePicker } from './chunk-OC75GWX5.js';
49
49
  export { Kanban, useKanban } from './chunk-O6E2WC3J.js';
50
50
  export { Kbd } from './chunk-TJJ63UJV.js';
51
- export { MagicWand } from './chunk-JHDO6CRT.js';
51
+ export { MagicWand } from './chunk-FU6R6BZ4.js';
52
52
  export { Textarea } from './chunk-DEFGT75G.js';
53
53
  import './chunk-PWEZCMIY.js';
54
54
  export { DisplayValue } from './chunk-KKWNKBKR.js';
55
55
  export { Field } from './chunk-5MU6X3KO.js';
56
56
  import './chunk-GFYSELJF.js';
57
57
  export { Marquee } from './chunk-RSLZL6VP.js';
58
- export { MediaViewer, resolveMediaType } from './chunk-MQEV7XEG.js';
58
+ export { MediaViewer, resolveMediaType } from './chunk-YOTCZUNW.js';
59
59
  export { VideoViewer } from './chunk-VOGI7GX3.js';
60
- export { PdfViewer } from './chunk-VKRQHLM5.js';
60
+ export { PdfViewer } from './chunk-VB3QJ6QE.js';
61
61
  export { Menu, useMenu } from './chunk-RA2VOQ46.js';
62
62
  export { FileBrowser, FileBrowserNewFolder, useFileBrowser, validateFolderName } from './chunk-2KAJ4L7C.js';
63
63
  export { FileUpload, useFileUpload } from './chunk-W6RDAZJC.js';
64
64
  export { Grid } from './chunk-TM3RTWKI.js';
65
65
  import './chunk-CFH6CJQG.js';
66
- export { ImageViewer } from './chunk-XOWWNG7D.js';
67
- export { FauxClient } from './chunk-XWXV6HSK.js';
66
+ export { ImageViewer } from './chunk-ASS6EWNI.js';
67
+ export { FauxClient } from './chunk-XZUGREDI.js';
68
68
  export { IndexList } from './chunk-UKUNWGL5.js';
69
69
  import './chunk-AUTW5O4W.js';
70
70
  export { ContextMenu, useContextMenu } from './chunk-SWBF2BXA.js';
71
71
  export { Drawer, useDrawer } from './chunk-VFIVF5YO.js';
72
72
  export { Dropdown, useDropdown } from './chunk-YQJWD53X.js';
73
- export { Editor, useEditor } from './chunk-K74R6IEB.js';
73
+ export { Editor, useEditor } from './chunk-PCTZNK2Z.js';
74
74
  export { THEME_STORAGE_KEY, getThemePreference, initTheme, resolveTheme, setThemePreference, subscribeTheme, useId, useNodeRegistry, useTheme } from './chunk-FWCREAIT.js';
75
75
  export { usePanZoom } from './chunk-CPY7NHXM.js';
76
- export { ContentRenderer, registerExtension, registerExtensions } from './chunk-GMNMJLZO.js';
76
+ export { ContentRenderer, registerExtension, registerExtensions, renderMarkdown } from './chunk-K6EJLBBQ.js';
77
77
  export { Emoji } from './chunk-YTMQ2K64.js';
78
78
  export { EmojiSelect } from './chunk-VYHZEQIG.js';
79
79
  export { Eyebrow } from './chunk-Y4UPKJN5.js';
@@ -97,8 +97,8 @@ export { Badge } from './chunk-6FCQD2KQ.js';
97
97
  export { Brand, BrandMark } from './chunk-FCSR2WVS.js';
98
98
  export { Breadcrumbs } from './chunk-TDDWWAM5.js';
99
99
  import './chunk-LQ4ICCN2.js';
100
- export { Action, Button } from './chunk-ZLYOMQ2J.js';
101
- export { sanitizeHref, sanitizeHtml } from './chunk-QXYO76NZ.js';
100
+ export { Action, Button } from './chunk-KODD54GT.js';
101
+ export { sanitizeHref, sanitizeHtml } from './chunk-6TBRBN7H.js';
102
102
  export { EMOJI_CATEGORY_ORDER, EMOJI_DATA, EMOJI_ENTRIES, SKIN_TONES, applyTone, find, hasSkinTones, resolve, search, skinTones } from './chunk-45BBNJQD.js';
103
103
  export { Icon, configureIcons, registerIconAddendum, registerIconSet, registerIcons } from './chunk-IVRYSSM7.js';
104
104
  export { Calendar } from './chunk-NTMHP7FS.js';
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkRLC2BE3D_cjs = require('./chunk-RLC2BE3D.cjs');
3
+ var chunkVWBKFQQN_cjs = require('./chunk-VWBKFQQN.cjs');
4
4
  require('./chunk-2QIGXSSZ.cjs');
5
5
  require('./chunk-AR3JOPGT.cjs');
6
6
  require('./chunk-Y52NXM73.cjs');
@@ -14,8 +14,8 @@ require('./chunk-BVRPDMB7.cjs');
14
14
  require('./chunk-MSRYJETY.cjs');
15
15
  require('./chunk-X7H75WVP.cjs');
16
16
  require('./chunk-MOU6O324.cjs');
17
- require('./chunk-3XJKJQSY.cjs');
18
- require('./chunk-LRNWD26Z.cjs');
17
+ require('./chunk-FWA4PCW7.cjs');
18
+ require('./chunk-X2FC7ODX.cjs');
19
19
  require('./chunk-XCZZVRBY.cjs');
20
20
  require('./chunk-QKFOJKVE.cjs');
21
21
  require('./chunk-LCNHEYIM.cjs');
@@ -33,47 +33,47 @@ require('./chunk-ZU5Q77OT.cjs');
33
33
 
34
34
  Object.defineProperty(exports, "JsonEditor", {
35
35
  enumerable: true,
36
- get: function () { return chunkRLC2BE3D_cjs.JsonEditor; }
36
+ get: function () { return chunkVWBKFQQN_cjs.JsonEditor; }
37
37
  });
38
38
  Object.defineProperty(exports, "applyJsonEdit", {
39
39
  enumerable: true,
40
- get: function () { return chunkRLC2BE3D_cjs.applyJsonEdit; }
40
+ get: function () { return chunkVWBKFQQN_cjs.applyJsonEdit; }
41
41
  });
42
42
  Object.defineProperty(exports, "describeEdit", {
43
43
  enumerable: true,
44
- get: function () { return chunkRLC2BE3D_cjs.describeEdit; }
44
+ get: function () { return chunkVWBKFQQN_cjs.describeEdit; }
45
45
  });
46
46
  Object.defineProperty(exports, "findJsonConflicts", {
47
47
  enumerable: true,
48
- get: function () { return chunkRLC2BE3D_cjs.findJsonConflicts; }
48
+ get: function () { return chunkVWBKFQQN_cjs.findJsonConflicts; }
49
49
  });
50
50
  Object.defineProperty(exports, "getAtPath", {
51
51
  enumerable: true,
52
- get: function () { return chunkRLC2BE3D_cjs.getAtPath; }
52
+ get: function () { return chunkVWBKFQQN_cjs.getAtPath; }
53
53
  });
54
54
  Object.defineProperty(exports, "inferType", {
55
55
  enumerable: true,
56
- get: function () { return chunkRLC2BE3D_cjs.inferType; }
56
+ get: function () { return chunkVWBKFQQN_cjs.inferType; }
57
57
  });
58
58
  Object.defineProperty(exports, "parseKeyMap", {
59
59
  enumerable: true,
60
- get: function () { return chunkRLC2BE3D_cjs.parseKeyMap; }
60
+ get: function () { return chunkVWBKFQQN_cjs.parseKeyMap; }
61
61
  });
62
62
  Object.defineProperty(exports, "parsePath", {
63
63
  enumerable: true,
64
- get: function () { return chunkRLC2BE3D_cjs.parsePath; }
64
+ get: function () { return chunkVWBKFQQN_cjs.parsePath; }
65
65
  });
66
66
  Object.defineProperty(exports, "pathToString", {
67
67
  enumerable: true,
68
- get: function () { return chunkRLC2BE3D_cjs.pathToString; }
68
+ get: function () { return chunkVWBKFQQN_cjs.pathToString; }
69
69
  });
70
70
  Object.defineProperty(exports, "resolveKeyRule", {
71
71
  enumerable: true,
72
- get: function () { return chunkRLC2BE3D_cjs.resolveKeyRule; }
72
+ get: function () { return chunkVWBKFQQN_cjs.resolveKeyRule; }
73
73
  });
74
74
  Object.defineProperty(exports, "typeMatches", {
75
75
  enumerable: true,
76
- get: function () { return chunkRLC2BE3D_cjs.typeMatches; }
76
+ get: function () { return chunkVWBKFQQN_cjs.typeMatches; }
77
77
  });
78
78
  //# sourceMappingURL=json-editor.cjs.map
79
79
  //# sourceMappingURL=json-editor.cjs.map
@@ -1,4 +1,4 @@
1
- export { JsonEditor, applyJsonEdit, describeEdit, findJsonConflicts, getAtPath, inferType, parseKeyMap, parsePath, pathToString, resolveKeyRule, typeMatches } from './chunk-VM5BNL4L.js';
1
+ export { JsonEditor, applyJsonEdit, describeEdit, findJsonConflicts, getAtPath, inferType, parseKeyMap, parsePath, pathToString, resolveKeyRule, typeMatches } from './chunk-7MJ7VEKE.js';
2
2
  import './chunk-EEWRLFTI.js';
3
3
  import './chunk-IECPLOSZ.js';
4
4
  import './chunk-NBAHXZHJ.js';
@@ -12,8 +12,8 @@ import './chunk-F2GAZZVS.js';
12
12
  import './chunk-KYZNCVRD.js';
13
13
  import './chunk-CF5KRDN4.js';
14
14
  import './chunk-6FCQD2KQ.js';
15
- import './chunk-ZLYOMQ2J.js';
16
- import './chunk-QXYO76NZ.js';
15
+ import './chunk-KODD54GT.js';
16
+ import './chunk-6TBRBN7H.js';
17
17
  import './chunk-45BBNJQD.js';
18
18
  import './chunk-IVRYSSM7.js';
19
19
  import './chunk-ZXZJYVWP.js';
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkI6XHZ3S2_cjs = require('./chunk-I6XHZ3S2.cjs');
3
+ var chunkNAQJF3G6_cjs = require('./chunk-NAQJF3G6.cjs');
4
4
  require('./chunk-V6VFSX6V.cjs');
5
5
  require('./chunk-7EZBMGEE.cjs');
6
6
  require('./chunk-XKDW6B7X.cjs');
@@ -9,8 +9,8 @@ require('./chunk-YLGTDBB2.cjs');
9
9
  require('./chunk-VBWRH4UW.cjs');
10
10
  require('./chunk-MOU6O324.cjs');
11
11
  require('./chunk-M5MUGSON.cjs');
12
- require('./chunk-3XJKJQSY.cjs');
13
- require('./chunk-LRNWD26Z.cjs');
12
+ require('./chunk-FWA4PCW7.cjs');
13
+ require('./chunk-X2FC7ODX.cjs');
14
14
  require('./chunk-XCZZVRBY.cjs');
15
15
  require('./chunk-QKFOJKVE.cjs');
16
16
  require('./chunk-KJ22KE56.cjs');
@@ -21,7 +21,7 @@ require('./chunk-ZU5Q77OT.cjs');
21
21
 
22
22
  Object.defineProperty(exports, "MagicWand", {
23
23
  enumerable: true,
24
- get: function () { return chunkI6XHZ3S2_cjs.MagicWand; }
24
+ get: function () { return chunkNAQJF3G6_cjs.MagicWand; }
25
25
  });
26
26
  //# sourceMappingURL=magic-wand.cjs.map
27
27
  //# sourceMappingURL=magic-wand.cjs.map
@@ -1,4 +1,4 @@
1
- export { MagicWand } from './chunk-JHDO6CRT.js';
1
+ export { MagicWand } from './chunk-FU6R6BZ4.js';
2
2
  import './chunk-DEFGT75G.js';
3
3
  import './chunk-PWEZCMIY.js';
4
4
  import './chunk-KKWNKBKR.js';
@@ -7,8 +7,8 @@ import './chunk-GFYSELJF.js';
7
7
  import './chunk-IQ4KQJAO.js';
8
8
  import './chunk-6FCQD2KQ.js';
9
9
  import './chunk-LQ4ICCN2.js';
10
- import './chunk-ZLYOMQ2J.js';
11
- import './chunk-QXYO76NZ.js';
10
+ import './chunk-KODD54GT.js';
11
+ import './chunk-6TBRBN7H.js';
12
12
  import './chunk-45BBNJQD.js';
13
13
  import './chunk-IVRYSSM7.js';
14
14
  import './chunk-OL53XYHF.js';
@@ -1,10 +1,10 @@
1
1
  'use strict';
2
2
 
3
- var chunkKST6GW2B_cjs = require('./chunk-KST6GW2B.cjs');
3
+ var chunkNCXH5O4I_cjs = require('./chunk-NCXH5O4I.cjs');
4
4
  require('./chunk-TH7VP4CL.cjs');
5
- require('./chunk-LP3HMRQT.cjs');
6
- require('./chunk-WBJRFWZE.cjs');
7
- require('./chunk-DUOZSOSB.cjs');
5
+ require('./chunk-IYVV4RD3.cjs');
6
+ require('./chunk-2KZINS6D.cjs');
7
+ require('./chunk-RU6ZTA4M.cjs');
8
8
  require('./chunk-T6YELZPJ.cjs');
9
9
  require('./chunk-QW66UHWO.cjs');
10
10
  require('./chunk-SNHU2AI4.cjs');
@@ -14,7 +14,7 @@ require('./chunk-ZU5Q77OT.cjs');
14
14
 
15
15
  Object.defineProperty(exports, "MediaViewer", {
16
16
  enumerable: true,
17
- get: function () { return chunkKST6GW2B_cjs.MediaViewer; }
17
+ get: function () { return chunkNCXH5O4I_cjs.MediaViewer; }
18
18
  });
19
19
  //# sourceMappingURL=media-viewer.cjs.map
20
20
  //# sourceMappingURL=media-viewer.cjs.map
@@ -1,8 +1,8 @@
1
- export { MediaViewer } from './chunk-MQEV7XEG.js';
1
+ export { MediaViewer } from './chunk-YOTCZUNW.js';
2
2
  import './chunk-VOGI7GX3.js';
3
- import './chunk-VKRQHLM5.js';
4
- import './chunk-XOWWNG7D.js';
5
- import './chunk-XWXV6HSK.js';
3
+ import './chunk-VB3QJ6QE.js';
4
+ import './chunk-ASS6EWNI.js';
5
+ import './chunk-XZUGREDI.js';
6
6
  import './chunk-CPY7NHXM.js';
7
7
  import './chunk-UIJR26YW.js';
8
8
  import './chunk-EDYAXGUM.js';
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var chunkLP3HMRQT_cjs = require('./chunk-LP3HMRQT.cjs');
4
- require('./chunk-DUOZSOSB.cjs');
3
+ var chunkIYVV4RD3_cjs = require('./chunk-IYVV4RD3.cjs');
4
+ require('./chunk-RU6ZTA4M.cjs');
5
5
  require('./chunk-SNHU2AI4.cjs');
6
6
  require('./chunk-ZU5Q77OT.cjs');
7
7
 
@@ -9,7 +9,7 @@ require('./chunk-ZU5Q77OT.cjs');
9
9
 
10
10
  Object.defineProperty(exports, "PdfViewer", {
11
11
  enumerable: true,
12
- get: function () { return chunkLP3HMRQT_cjs.PdfViewer; }
12
+ get: function () { return chunkIYVV4RD3_cjs.PdfViewer; }
13
13
  });
14
14
  //# sourceMappingURL=pdf-viewer.cjs.map
15
15
  //# sourceMappingURL=pdf-viewer.cjs.map
@@ -1,5 +1,5 @@
1
- export { PdfViewer } from './chunk-VKRQHLM5.js';
2
- import './chunk-XWXV6HSK.js';
1
+ export { PdfViewer } from './chunk-VB3QJ6QE.js';
2
+ import './chunk-XZUGREDI.js';
3
3
  import './chunk-EDYAXGUM.js';
4
4
  import './chunk-MMW2JGKZ.js';
5
5
  //# sourceMappingURL=pdf-viewer.js.map
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
 
3
- var chunkTKDUT7ZA_cjs = require('./chunk-TKDUT7ZA.cjs');
3
+ var chunkIPBEVSSC_cjs = require('./chunk-IPBEVSSC.cjs');
4
4
  require('./chunk-AIA5UNQ5.cjs');
5
5
  require('./chunk-MSRYJETY.cjs');
6
6
  require('./chunk-X7H75WVP.cjs');
7
7
  require('./chunk-M5MUGSON.cjs');
8
- require('./chunk-3XJKJQSY.cjs');
9
- require('./chunk-LRNWD26Z.cjs');
8
+ require('./chunk-FWA4PCW7.cjs');
9
+ require('./chunk-X2FC7ODX.cjs');
10
10
  require('./chunk-XCZZVRBY.cjs');
11
11
  require('./chunk-QKFOJKVE.cjs');
12
12
  require('./chunk-J2DI6RMR.cjs');
@@ -18,7 +18,7 @@ require('./chunk-ZU5Q77OT.cjs');
18
18
 
19
19
  Object.defineProperty(exports, "PromptInput", {
20
20
  enumerable: true,
21
- get: function () { return chunkTKDUT7ZA_cjs.PromptInput; }
21
+ get: function () { return chunkIPBEVSSC_cjs.PromptInput; }
22
22
  });
23
23
  //# sourceMappingURL=prompt-input.cjs.map
24
24
  //# sourceMappingURL=prompt-input.cjs.map
@@ -1,10 +1,10 @@
1
- export { PromptInput } from './chunk-SK4AEUO4.js';
1
+ export { PromptInput } from './chunk-XBC3M4BL.js';
2
2
  import './chunk-TJ66S5K6.js';
3
3
  import './chunk-KYZNCVRD.js';
4
4
  import './chunk-CF5KRDN4.js';
5
5
  import './chunk-LQ4ICCN2.js';
6
- import './chunk-ZLYOMQ2J.js';
7
- import './chunk-QXYO76NZ.js';
6
+ import './chunk-KODD54GT.js';
7
+ import './chunk-6TBRBN7H.js';
8
8
  import './chunk-45BBNJQD.js';
9
9
  import './chunk-IVRYSSM7.js';
10
10
  import './chunk-MQ6VGG62.js';
@@ -1,10 +1,10 @@
1
1
  'use strict';
2
2
 
3
- var chunkEEKXWXYO_cjs = require('./chunk-EEKXWXYO.cjs');
3
+ var chunkGCWIZNN2_cjs = require('./chunk-GCWIZNN2.cjs');
4
4
  require('./chunk-WKNVZUWZ.cjs');
5
5
  require('./chunk-M5MUGSON.cjs');
6
- require('./chunk-3XJKJQSY.cjs');
7
- require('./chunk-LRNWD26Z.cjs');
6
+ require('./chunk-FWA4PCW7.cjs');
7
+ require('./chunk-X2FC7ODX.cjs');
8
8
  require('./chunk-XCZZVRBY.cjs');
9
9
  require('./chunk-QKFOJKVE.cjs');
10
10
  require('./chunk-J2DI6RMR.cjs');
@@ -17,7 +17,7 @@ require('./chunk-ZU5Q77OT.cjs');
17
17
 
18
18
  Object.defineProperty(exports, "ReasonTag", {
19
19
  enumerable: true,
20
- get: function () { return chunkEEKXWXYO_cjs.ReasonTag; }
20
+ get: function () { return chunkGCWIZNN2_cjs.ReasonTag; }
21
21
  });
22
22
  //# sourceMappingURL=reason-tag.cjs.map
23
23
  //# sourceMappingURL=reason-tag.cjs.map
@@ -1,8 +1,8 @@
1
- export { ReasonTag } from './chunk-62WPAKFS.js';
1
+ export { ReasonTag } from './chunk-UDEEPMQ4.js';
2
2
  import './chunk-6KXAFP2T.js';
3
3
  import './chunk-LQ4ICCN2.js';
4
- import './chunk-ZLYOMQ2J.js';
5
- import './chunk-QXYO76NZ.js';
4
+ import './chunk-KODD54GT.js';
5
+ import './chunk-6TBRBN7H.js';
6
6
  import './chunk-45BBNJQD.js';
7
7
  import './chunk-IVRYSSM7.js';
8
8
  import './chunk-MQ6VGG62.js';
@@ -91,3 +91,39 @@ registerExtension({
91
91
  ```tsx
92
92
  <ContentRenderer value="# Title\n\nParagraph with **bold** text." format="markdown" />
93
93
  ```
94
+
95
+ ## Markdown and sanitising without the component
96
+
97
+ `renderMarkdown(markdown, sanitize?)` returns the same HTML `ContentRenderer`
98
+ renders, already sanitised, so a host never imports the markdown parser itself.
99
+
100
+ `sanitizeHtml(html)` is a blocklist: it strips what can run (script, iframe,
101
+ event handlers, `javascript:` URLs, ...) and keeps the rest. Pass options and it
102
+ becomes an **allowlist** that keeps only what you list:
103
+
104
+ ```ts
105
+ import { renderMarkdown, sanitizeHtml, type SanitizeHtmlOptions } from "@particle-academy/react-fancy";
106
+
107
+ const CHAT_PROSE: SanitizeHtmlOptions = {
108
+ allowedTags: ["p", "br", "strong", "em", "a", "ul", "ol", "li", "code", "pre"],
109
+ allowedAttributes: ["href", "title"], // or { "*": ["title"], a: ["href"], span: ["data-*"] }
110
+ allowedSchemes: ["https:", "http:", "mailto:"], // the default
111
+ allowRelative: true, // /x, ../x, ?q, #frag; never //host. Default false
112
+ };
113
+
114
+ renderMarkdown(md, CHAT_PROSE); // same as sanitizeHtml(renderMarkdown(md), CHAT_PROSE)
115
+ sanitizeHtml(html, CHAT_PROSE);
116
+ ```
117
+
118
+ | Rule | Allowlist mode |
119
+ |------|----------------|
120
+ | Tag not listed | removed, its text and cleaned children kept |
121
+ | `script`, `style`, `iframe`, `frame(set)`, `object`, `embed`, `applet`, `link`, `meta`, `base`, `form`, `title`, `template`, `noscript`, `noembed`, `noframes`, `xmp`, `plaintext`, `svg`, `math` | removed WITH content, even when listed |
122
+ | Attribute not listed | removed |
123
+ | `on*`, `srcdoc`, `srcset` | removed, even when listed |
124
+ | URL attribute with an unlisted scheme | removed |
125
+ | `javascript:`, `vbscript:` | removed, even when listed |
126
+ | Comments | removed |
127
+ | No DOM (SSR) | input returned as escaped text (fails closed) |
128
+
129
+ The allowlist can only narrow: nothing you list widens past the blocklist.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@particle-academy/react-fancy",
3
- "version": "5.49.0",
3
+ "version": "5.50.0",
4
4
  "description": "React UI component library for Human+ UX — controlled, agent-bridgeable primitives",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/FauxClient/FauxClient.tsx"],"names":["forwardRef","useRef","useState","useControllableState","useLayoutEffect","useEffect","useCallback","jsxs","cn","Fragment","jsx"],"mappings":"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","file":"chunk-DUOZSOSB.cjs","sourcesContent":["import { forwardRef, useCallback, useEffect, useLayoutEffect, useRef, useState } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { useControllableState } from \"../../hooks/use-controllable-state\";\nimport type { FauxClientProps } from \"./FauxClient.types\";\n\nconst DOT_COLORS = [\"bg-rose-300\", \"bg-amber-300\", \"bg-green-300\"];\n\n/** One click of the zoom bar. Matches `ImageViewer`, so the kit zooms one way. */\nconst ZOOM_STEP = 1.25;\n\nconst clamp = (n: number, min: number, max: number) => Math.min(max, Math.max(min, n));\n\n/**\n * A frame that mimics a browser window or device, rendering **real UI** inside.\n * With a logical `width` + `scale=\"fit\"` it renders a full-size page/app and\n * scales it down to fit any container (live thumbnails, device mockups) — the\n * content is real and interactive, just zoomed.\n *\n * Omit `width` for chrome around natural-size content (e.g. a code/preview card).\n *\n * `controls` adds a zoom bar and a full-screen button, for content that has to be\n * READ rather than glanced at: a slide or a sheet shrunk to fit a panel.\n */\nexport const FauxClient = forwardRef<HTMLDivElement, FauxClientProps>(\n (\n {\n variant = \"browser\",\n url,\n meta,\n dots = true,\n width,\n scale = \"fit\",\n controls = false,\n zoom: controlledZoom,\n defaultZoom = 1,\n onZoomChange,\n minZoom = 0.5,\n maxZoom = 4,\n onFullscreenChange,\n fill = false,\n applyZoom = true,\n className,\n barClassName,\n bodyClassName,\n children,\n ...rest\n },\n ref,\n ) => {\n const scaled = typeof width === \"number\";\n const rootRef = useRef<HTMLDivElement | null>(null);\n const viewportRef = useRef<HTMLDivElement>(null);\n const stageRef = useRef<HTMLDivElement>(null);\n const [fit, setFit] = useState(typeof scale === \"number\" ? scale : 1);\n const [naturalHeight, setNaturalHeight] = useState<number | undefined>(undefined);\n\n const showZoom = controls === true || (typeof controls === \"object\" && controls.zoom !== false);\n const showFullscreen = controls === true || (typeof controls === \"object\" && controls.fullscreen !== false);\n const [zoom, setZoom] = useControllableState(controlledZoom, defaultZoom, onZoomChange);\n const [fullscreen, setFullscreen] = useState(false);\n\n useLayoutEffect(() => {\n if (!scaled) return;\n const viewport = viewportRef.current;\n const stage = stageRef.current;\n if (!viewport || !stage) return;\n if (typeof ResizeObserver === \"undefined\") return;\n\n const measure = () => {\n const avail = viewport.clientWidth;\n setFit(scale === \"fit\" ? (width && width > 0 ? avail / width : 1) : scale);\n // offsetHeight is the pre-transform layout height (CSS transforms don't affect it).\n setNaturalHeight(stage.offsetHeight);\n };\n\n measure();\n const ro = new ResizeObserver(measure);\n ro.observe(viewport);\n ro.observe(stage);\n return () => ro.disconnect();\n }, [scaled, scale, width]);\n\n // Full screen is the browser's state, not ours: follow it, so Esc and the\n // browser's own exit leave the button telling the truth.\n useEffect(() => {\n if (!showFullscreen || typeof document === \"undefined\") return;\n const sync = () => {\n const on = document.fullscreenElement != null && document.fullscreenElement === rootRef.current;\n setFullscreen(on);\n onFullscreenChange?.(on);\n };\n document.addEventListener(\"fullscreenchange\", sync);\n return () => document.removeEventListener(\"fullscreenchange\", sync);\n }, [showFullscreen, onFullscreenChange]);\n\n const toggleFullscreen = useCallback(() => {\n const el = rootRef.current;\n if (!el || typeof document === \"undefined\") return;\n if (document.fullscreenElement === el) void document.exitFullscreen?.();\n else void el.requestFullscreen?.();\n }, []);\n\n const zoomBy = (factor: number) => {\n // Rounded so ten clicks in and ten out land back on 100%, not 99.99%.\n setZoom(clamp(Math.round(zoom * factor * 100) / 100, minZoom, maxZoom));\n };\n\n const setRefs = (el: HTMLDivElement | null) => {\n rootRef.current = el;\n if (typeof ref === \"function\") ref(el);\n else if (ref) ref.current = el;\n };\n\n const isBrowser = variant === \"browser\";\n const isDevice = variant === \"device\";\n // The zoom the FRAME applies: none when the content zooms itself.\n const appliedZoom = applyZoom ? zoom : 1;\n const effective = fit * appliedZoom;\n const zoomed = appliedZoom !== 1;\n const hasControls = showZoom || showFullscreen;\n\n /*\n * Out of the way until wanted: hidden, and revealed when the pointer is over\n * the frame or focus is inside it (keyboard). Always shown where there is no\n * hover to reveal it (touch), or it could never be reached.\n *\n * In the browser chrome it sits in the address bar, inline. On a device or a\n * bare frame there is no chrome, so it floats top-right as a slim strip: the\n * bottom corners belong to the content (a slide viewer's page count lives\n * there).\n */\n const controlsBar = hasControls ? (\n <div\n data-react-fancy-faux-client-controls=\"\"\n className={cn(\n \"pointer-events-auto flex shrink-0 items-center gap-px opacity-0 transition-opacity duration-150\",\n \"group-hover/faux-client:opacity-100 focus-within:opacity-100 [@media(hover:none)]:opacity-100\",\n isBrowser\n ? \"-my-1 text-zinc-400 dark:text-zinc-500\"\n : \"rounded-full bg-zinc-900/60 px-1 py-0.5 text-white/80 shadow-sm backdrop-blur-sm\",\n )}\n >\n {showZoom && (\n <>\n <button\n type=\"button\"\n data-react-fancy-faux-client-zoom-out=\"\"\n aria-label=\"Zoom out\"\n title=\"Zoom out\"\n disabled={zoom <= minZoom}\n onClick={() => zoomBy(1 / ZOOM_STEP)}\n className={controlButton(isBrowser)}\n >\n <ZoomOutIcon />\n </button>\n <button\n type=\"button\"\n data-react-fancy-faux-client-zoom-reset=\"\"\n aria-label=\"Reset zoom\"\n title=\"Reset zoom\"\n onClick={() => setZoom(1)}\n className={cn(controlButton(isBrowser), \"w-auto min-w-[2.25rem] px-1 font-sans text-[10.5px] tabular-nums\")}\n >\n {Math.round(zoom * 100)}%\n </button>\n <button\n type=\"button\"\n data-react-fancy-faux-client-zoom-in=\"\"\n aria-label=\"Zoom in\"\n title=\"Zoom in\"\n disabled={zoom >= maxZoom}\n onClick={() => zoomBy(ZOOM_STEP)}\n className={controlButton(isBrowser)}\n >\n <ZoomInIcon />\n </button>\n </>\n )}\n {showFullscreen && (\n <button\n type=\"button\"\n data-react-fancy-faux-client-fullscreen=\"\"\n aria-label={fullscreen ? \"Exit full screen\" : \"Full screen\"}\n aria-pressed={fullscreen}\n title={fullscreen ? \"Exit full screen\" : \"Full screen\"}\n onClick={toggleFullscreen}\n className={controlButton(isBrowser)}\n >\n {fullscreen ? <ExitFullscreenIcon /> : <FullscreenIcon />}\n </button>\n )}\n </div>\n ) : null;\n\n return (\n <div\n ref={setRefs}\n data-react-fancy-faux-client=\"\"\n data-fullscreen={showFullscreen ? String(fullscreen) : undefined}\n className={cn(\n \"group/faux-client relative overflow-hidden bg-white dark:bg-zinc-900\",\n isBrowser && \"rounded-2xl border border-zinc-200 shadow-xl dark:border-zinc-800\",\n isDevice && \"rounded-[2rem] border-[10px] border-zinc-800 shadow-2xl dark:border-zinc-700\",\n variant === \"bare\" && \"rounded-lg border border-zinc-200 dark:border-zinc-800\",\n // Fill (or full screen): the frame takes its parent's height and its\n // body takes whatever the chrome leaves.\n (fill || fullscreen) && \"flex flex-col\",\n fill && \"h-full\",\n fullscreen && \"rounded-none\",\n className,\n )}\n {...rest}\n >\n {isBrowser && (\n <div\n className={cn(\n \"flex items-center gap-2 border-b border-zinc-200 bg-zinc-50 px-3.5 py-2.5 font-mono text-[11.5px] text-zinc-500 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-400\",\n barClassName,\n )}\n >\n {dots && (\n <div className=\"mr-1.5 flex gap-[5px]\">\n {DOT_COLORS.map((color, i) => (\n <span key={i} className={cn(\"h-[9px] w-[9px] rounded-full\", color)} />\n ))}\n </div>\n )}\n {url != null && <span className=\"min-w-0 flex-1 truncate\">{url}</span>}\n {meta != null && <span className=\"shrink-0 text-zinc-400 dark:text-zinc-500\">{meta}</span>}\n {hasControls && <div className={cn(url == null && meta == null && \"ml-auto\")}>{controlsBar}</div>}\n </div>\n )}\n\n <div\n ref={viewportRef}\n className={cn(\n \"relative\",\n // Zoomed in, the content is bigger than the frame: scroll to it.\n zoomed ? \"overflow-auto\" : \"overflow-hidden\",\n (fill || fullscreen) && \"min-h-0 flex-1\",\n bodyClassName,\n )}\n // The frame keeps its FIT height whatever the zoom; zooming scrolls.\n style={scaled && !fullscreen && naturalHeight !== undefined ? { height: naturalHeight * fit } : undefined}\n >\n {scaled ? (\n // A sizer the size of the zoomed content, so the scrollbars are right.\n <div\n style={\n naturalHeight !== undefined && width !== undefined\n ? { width: width * effective, height: naturalHeight * effective, position: \"relative\" }\n : undefined\n }\n >\n <div\n ref={stageRef}\n data-react-fancy-faux-client-content=\"\"\n className=\"absolute left-0 top-0 origin-top-left\"\n style={{ width, transform: `scale(${effective})` }}\n >\n {children}\n </div>\n </div>\n ) : (\n <div\n data-react-fancy-faux-client-content=\"\"\n className={cn((fill || fullscreen) && \"h-full\")}\n // CSS `zoom` rather than a transform: it changes layout size, so the\n // content reflows and the viewport scrolls to all of it.\n style={zoomed ? { zoom: String(appliedZoom) } : undefined}\n >\n {children}\n </div>\n )}\n </div>\n\n {hasControls && !isBrowser && (\n <div className=\"pointer-events-none absolute right-2 top-2 z-10\">{controlsBar}</div>\n )}\n </div>\n );\n },\n);\n\nFauxClient.displayName = \"FauxClient\";\n\n/** Ghost icon buttons: no box of their own, so the bar reads as part of the frame. */\nfunction controlButton(inBar: boolean): string {\n return cn(\n \"flex h-6 w-6 items-center justify-center rounded-full transition-colors disabled:pointer-events-none disabled:opacity-30\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/50\",\n inBar\n ? \"hover:bg-zinc-900/5 hover:text-zinc-700 dark:hover:bg-white/10 dark:hover:text-zinc-200\"\n : \"hover:bg-white/15 hover:text-white\",\n );\n}\n\nfunction ZoomOutIcon() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"11\" cy=\"11\" r=\"7\" />\n <path d=\"m20 20-3.5-3.5\" />\n <path d=\"M8 11h6\" />\n </svg>\n );\n}\n\nfunction ZoomInIcon() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"11\" cy=\"11\" r=\"7\" />\n <path d=\"m20 20-3.5-3.5\" />\n <path d=\"M8 11h6\" />\n <path d=\"M11 8v6\" />\n </svg>\n );\n}\n\n/** Inline, like `Input`'s reveal icons: a frame should not need an icon set configured. */\nfunction FullscreenIcon() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M8 3H5a2 2 0 0 0-2 2v3\" />\n <path d=\"M21 8V5a2 2 0 0 0-2-2h-3\" />\n <path d=\"M3 16v3a2 2 0 0 0 2 2h3\" />\n <path d=\"M16 21h3a2 2 0 0 0 2-2v-3\" />\n </svg>\n );\n}\n\nfunction ExitFullscreenIcon() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M8 3v3a2 2 0 0 1-2 2H3\" />\n <path d=\"M21 8h-3a2 2 0 0 1-2-2V3\" />\n <path d=\"M3 16h3a2 2 0 0 1 2 2v3\" />\n <path d=\"M16 21v-3a2 2 0 0 1 2-2h3\" />\n </svg>\n );\n}\n"]}