@particle-academy/react-fancy 5.66.0 → 5.67.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 (65) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/dist/button.cjs +3 -4
  3. package/dist/button.js +1 -2
  4. package/dist/{chunk-J44EV6SP.js → chunk-23AVEYUP.js} +3 -3
  5. package/dist/{chunk-J44EV6SP.js.map → chunk-23AVEYUP.js.map} +1 -1
  6. package/dist/{chunk-C5YX5XVL.cjs → chunk-3U7PUZLI.cjs} +214 -4
  7. package/dist/chunk-3U7PUZLI.cjs.map +1 -0
  8. package/dist/{chunk-ATIDUWY6.cjs → chunk-4S5DFN5N.cjs} +12 -12
  9. package/dist/{chunk-ATIDUWY6.cjs.map → chunk-4S5DFN5N.cjs.map} +1 -1
  10. package/dist/{chunk-QP35Z2WB.js → chunk-BL5IKSB3.js} +3 -3
  11. package/dist/{chunk-QP35Z2WB.js.map → chunk-BL5IKSB3.js.map} +1 -1
  12. package/dist/{chunk-4EUDW2WU.js → chunk-DCAD2JPP.js} +3 -3
  13. package/dist/{chunk-4EUDW2WU.js.map → chunk-DCAD2JPP.js.map} +1 -1
  14. package/dist/{chunk-MCQJFMDP.cjs → chunk-E5UU666U.cjs} +478 -36
  15. package/dist/chunk-E5UU666U.cjs.map +1 -0
  16. package/dist/{chunk-L24LUSEE.js → chunk-FXOT4P4P.js} +478 -36
  17. package/dist/chunk-FXOT4P4P.js.map +1 -0
  18. package/dist/{chunk-CAUEKGMR.js → chunk-HEY4SMS2.js} +4 -4
  19. package/dist/{chunk-CAUEKGMR.js.map → chunk-HEY4SMS2.js.map} +1 -1
  20. package/dist/{chunk-TLRRMYNV.js → chunk-OXERDFFF.js} +212 -4
  21. package/dist/chunk-OXERDFFF.js.map +1 -0
  22. package/dist/{chunk-SYEHWUM5.cjs → chunk-RALJMI7C.cjs} +4 -4
  23. package/dist/{chunk-SYEHWUM5.cjs.map → chunk-RALJMI7C.cjs.map} +1 -1
  24. package/dist/{chunk-UM4JXBRZ.cjs → chunk-S3NB6LDV.cjs} +4 -4
  25. package/dist/{chunk-UM4JXBRZ.cjs.map → chunk-S3NB6LDV.cjs.map} +1 -1
  26. package/dist/{chunk-MCQWTOTV.cjs → chunk-SAG6DHQK.cjs} +6 -6
  27. package/dist/{chunk-MCQWTOTV.cjs.map → chunk-SAG6DHQK.cjs.map} +1 -1
  28. package/dist/{chunk-7HXFEYOV.cjs → chunk-U5LKNGHU.cjs} +15 -15
  29. package/dist/{chunk-7HXFEYOV.cjs.map → chunk-U5LKNGHU.cjs.map} +1 -1
  30. package/dist/{chunk-M52S3TFI.js → chunk-WFR7HKJL.js} +3 -3
  31. package/dist/{chunk-M52S3TFI.js.map → chunk-WFR7HKJL.js.map} +1 -1
  32. package/dist/content-renderer.cjs +7 -5
  33. package/dist/content-renderer.d.cts +56 -4
  34. package/dist/content-renderer.d.ts +56 -4
  35. package/dist/content-renderer.js +4 -2
  36. package/dist/editor.cjs +6 -5
  37. package/dist/editor.d.cts +1 -1
  38. package/dist/editor.d.ts +1 -1
  39. package/dist/editor.js +4 -3
  40. package/dist/{extensions-6PVd3-vB.d.cts → extensions-DiByZIpD.d.cts} +26 -3
  41. package/dist/{extensions-6PVd3-vB.d.ts → extensions-DiByZIpD.d.ts} +26 -3
  42. package/dist/index.cjs +37 -38
  43. package/dist/index.cjs.map +1 -1
  44. package/dist/index.d.cts +2 -2
  45. package/dist/index.d.ts +2 -2
  46. package/dist/index.js +7 -8
  47. package/dist/index.js.map +1 -1
  48. package/dist/json-editor.cjs +13 -14
  49. package/dist/json-editor.js +2 -3
  50. package/dist/magic-wand.cjs +3 -4
  51. package/dist/magic-wand.js +2 -3
  52. package/dist/prompt-input.cjs +5 -6
  53. package/dist/prompt-input.js +2 -3
  54. package/dist/reason-tag.cjs +3 -4
  55. package/dist/reason-tag.js +2 -3
  56. package/docs/ContentRenderer.md +138 -3
  57. package/package.json +1 -1
  58. package/dist/chunk-6TBRBN7H.js +0 -212
  59. package/dist/chunk-6TBRBN7H.js.map +0 -1
  60. package/dist/chunk-C5YX5XVL.cjs.map +0 -1
  61. package/dist/chunk-L24LUSEE.js.map +0 -1
  62. package/dist/chunk-MCQJFMDP.cjs.map +0 -1
  63. package/dist/chunk-TLRRMYNV.js.map +0 -1
  64. package/dist/chunk-X2FC7ODX.cjs +0 -215
  65. package/dist/chunk-X2FC7ODX.cjs.map +0 -1
@@ -1,12 +1,220 @@
1
1
  'use strict';
2
2
 
3
- var chunkX2FC7ODX_cjs = require('./chunk-X2FC7ODX.cjs');
4
3
  var chunkXCZZVRBY_cjs = require('./chunk-XCZZVRBY.cjs');
5
4
  var chunkMXWR3T3N_cjs = require('./chunk-MXWR3T3N.cjs');
6
5
  var chunkZU5Q77OT_cjs = require('./chunk-ZU5Q77OT.cjs');
7
6
  var react = require('react');
8
7
  var jsxRuntime = require('react/jsx-runtime');
9
8
 
9
+ // src/utils/sanitize.ts
10
+ var DANGEROUS_TAGS = /* @__PURE__ */ new Set([
11
+ "script",
12
+ "style",
13
+ "iframe",
14
+ "object",
15
+ "embed",
16
+ "link",
17
+ "meta",
18
+ "base",
19
+ "form",
20
+ // A <title> anywhere in the document retitles the host's tab.
21
+ "title"
22
+ ]);
23
+ var URL_ATTRS = /* @__PURE__ */ new Set(["href", "src", "action", "formaction", "xlink:href"]);
24
+ var SAFE_PROTOCOL = /^(?:https?:|mailto:|tel:|sms:|ftp:|#|\/|\.\/|\.\.\/|[^:]*$)/i;
25
+ function sanitizeHref(href) {
26
+ if (href == null) return void 0;
27
+ const trimmed = href.trim();
28
+ if (!trimmed) return void 0;
29
+ return SAFE_PROTOCOL.test(trimmed) ? trimmed : void 0;
30
+ }
31
+ function stripDangerousAttrs(el) {
32
+ const names = [];
33
+ for (let i = 0; i < el.attributes.length; i++) {
34
+ names.push(el.attributes[i].name);
35
+ }
36
+ for (const name of names) {
37
+ const lower = name.toLowerCase();
38
+ if (lower.startsWith("on")) {
39
+ el.removeAttribute(name);
40
+ continue;
41
+ }
42
+ if (URL_ATTRS.has(lower)) {
43
+ const sanitized = sanitizeHref(el.getAttribute(name));
44
+ if (sanitized === void 0) {
45
+ el.removeAttribute(name);
46
+ } else {
47
+ el.setAttribute(name, sanitized);
48
+ }
49
+ continue;
50
+ }
51
+ if (lower === "srcdoc") {
52
+ el.removeAttribute(name);
53
+ }
54
+ }
55
+ }
56
+ function walk(el, removeQueue) {
57
+ const tag = el.tagName.toLowerCase();
58
+ if (DANGEROUS_TAGS.has(tag)) {
59
+ removeQueue.push(el);
60
+ return;
61
+ }
62
+ stripDangerousAttrs(el);
63
+ const children = Array.from(el.children);
64
+ for (const child of children) {
65
+ walk(child, removeQueue);
66
+ }
67
+ }
68
+ var ALWAYS_REMOVED_TAGS = /* @__PURE__ */ new Set([
69
+ ...DANGEROUS_TAGS,
70
+ "frame",
71
+ "frameset",
72
+ "applet",
73
+ "template",
74
+ "noscript",
75
+ "noembed",
76
+ "noframes",
77
+ "xmp",
78
+ "plaintext",
79
+ // Foreign content: namespace confusion is how mutation XSS gets past
80
+ // sanitisers that re-serialise, as this one does.
81
+ "svg",
82
+ "math"
83
+ ]);
84
+ var ALLOWLIST_URL_ATTRS = /* @__PURE__ */ new Set([
85
+ "href",
86
+ "src",
87
+ "action",
88
+ "formaction",
89
+ "cite",
90
+ "poster",
91
+ "background",
92
+ "ping",
93
+ "xlink:href"
94
+ ]);
95
+ var NEVER_ALLOWED_ATTRS = /* @__PURE__ */ new Set(["srcdoc", "srcset"]);
96
+ var NEVER_ALLOWED_SCHEMES = /* @__PURE__ */ new Set(["javascript:", "vbscript:"]);
97
+ var DEFAULT_SCHEMES = ["http:", "https:", "mailto:"];
98
+ var SCHEME = /^([a-z][a-z0-9+.-]*:)/i;
99
+ var SLASH = 47;
100
+ var BACKSLASH = 92;
101
+ function lowerAll(list) {
102
+ return (list ?? []).map((s) => s.toLowerCase());
103
+ }
104
+ function toPolicy(options) {
105
+ const tagAttrs = /* @__PURE__ */ new Map();
106
+ let globalAttrs = [];
107
+ const attrs = options.allowedAttributes;
108
+ if (Array.isArray(attrs)) {
109
+ globalAttrs = lowerAll(attrs);
110
+ } else if (attrs) {
111
+ for (const [tag, list] of Object.entries(attrs)) {
112
+ if (tag === "*") globalAttrs = lowerAll(list);
113
+ else tagAttrs.set(tag.toLowerCase(), lowerAll(list));
114
+ }
115
+ }
116
+ return {
117
+ tags: new Set(lowerAll(options.allowedTags)),
118
+ globalAttrs,
119
+ tagAttrs,
120
+ schemes: new Set(lowerAll(options.allowedSchemes ?? DEFAULT_SCHEMES)),
121
+ allowRelative: options.allowRelative === true
122
+ };
123
+ }
124
+ function attrListed(name, list) {
125
+ if (!list) return false;
126
+ for (const entry of list) {
127
+ if (entry === name) return true;
128
+ if (entry.endsWith("*") && name.startsWith(entry.slice(0, -1))) return true;
129
+ }
130
+ return false;
131
+ }
132
+ function stripControlAndSpace(value) {
133
+ let out = "";
134
+ for (let i = 0; i < value.length; i++) {
135
+ if (value.charCodeAt(i) > 32) out += value[i];
136
+ }
137
+ return out;
138
+ }
139
+ function urlAllowed(value, policy) {
140
+ const url = stripControlAndSpace(value);
141
+ const match = SCHEME.exec(url);
142
+ if (match) {
143
+ const scheme = match[1].toLowerCase();
144
+ return !NEVER_ALLOWED_SCHEMES.has(scheme) && policy.schemes.has(scheme);
145
+ }
146
+ if (!policy.allowRelative) return false;
147
+ const a = url.charCodeAt(0);
148
+ const b = url.charCodeAt(1);
149
+ const isSlash = (c) => c === SLASH || c === BACKSLASH;
150
+ return !(isSlash(a) && isSlash(b));
151
+ }
152
+ function cleanAttributes(el, tag, policy) {
153
+ const names = [];
154
+ for (let i = 0; i < el.attributes.length; i++) {
155
+ names.push(el.attributes[i].name);
156
+ }
157
+ for (const name of names) {
158
+ const lower = name.toLowerCase();
159
+ const keep = !lower.startsWith("on") && !NEVER_ALLOWED_ATTRS.has(lower) && (attrListed(lower, policy.globalAttrs) || attrListed(lower, policy.tagAttrs.get(tag))) && (!ALLOWLIST_URL_ATTRS.has(lower) || urlAllowed(el.getAttribute(name) ?? "", policy));
160
+ if (!keep) el.removeAttribute(name);
161
+ }
162
+ }
163
+ function cleanChildren(parent, policy) {
164
+ for (const node of Array.from(parent.childNodes)) {
165
+ if (node.nodeType === 3) continue;
166
+ if (node.nodeType !== 1) {
167
+ parent.removeChild(node);
168
+ continue;
169
+ }
170
+ const el = node;
171
+ const tag = el.tagName.toLowerCase();
172
+ if (ALWAYS_REMOVED_TAGS.has(tag)) {
173
+ parent.removeChild(el);
174
+ continue;
175
+ }
176
+ cleanChildren(el, policy);
177
+ if (!policy.tags.has(tag)) {
178
+ while (el.firstChild) parent.insertBefore(el.firstChild, el);
179
+ parent.removeChild(el);
180
+ continue;
181
+ }
182
+ cleanAttributes(el, tag, policy);
183
+ }
184
+ }
185
+ function escapeText(text) {
186
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
187
+ }
188
+ function sanitizeAllowlist(html, options) {
189
+ if (typeof window === "undefined" || typeof DOMParser === "undefined") {
190
+ return escapeText(html);
191
+ }
192
+ const doc = new DOMParser().parseFromString(`<body>${html}</body>`, "text/html");
193
+ const body = doc.body;
194
+ if (!body) return "";
195
+ cleanChildren(body, toPolicy(options));
196
+ return body.innerHTML;
197
+ }
198
+ function sanitizeHtml(html, options) {
199
+ if (options !== void 0 && options !== null) {
200
+ return sanitizeAllowlist(html, options);
201
+ }
202
+ if (typeof window === "undefined" || typeof DOMParser === "undefined") {
203
+ return html;
204
+ }
205
+ const doc = new DOMParser().parseFromString(`<body>${html}</body>`, "text/html");
206
+ const body = doc.body;
207
+ if (!body) return html;
208
+ const removeQueue = [];
209
+ for (const child of Array.from(body.children)) {
210
+ walk(child, removeQueue);
211
+ }
212
+ for (const el of removeQueue) {
213
+ el.parentNode?.removeChild(el);
214
+ }
215
+ return body.innerHTML;
216
+ }
217
+
10
218
  // src/components/Button/Button.colors.ts
11
219
  var buttonColorClasses = {
12
220
  // The host's colour, from CSS variables -- no dark: variant, because a
@@ -370,7 +578,7 @@ var Button = react.forwardRef(
370
578
  ),
371
579
  trailingElements
372
580
  ] });
373
- const safeHref = chunkX2FC7ODX_cjs.sanitizeHref(href);
581
+ const safeHref = sanitizeHref(href);
374
582
  const Anchor = As ?? "a";
375
583
  const buttonEl = safeHref && !disabled ? (
376
584
  // Anchor mode forwards the same rest props as the <button> branch so
@@ -462,5 +670,7 @@ var Action = Button;
462
670
 
463
671
  exports.Action = Action;
464
672
  exports.Button = Button;
465
- //# sourceMappingURL=chunk-C5YX5XVL.cjs.map
466
- //# sourceMappingURL=chunk-C5YX5XVL.cjs.map
673
+ exports.sanitizeHref = sanitizeHref;
674
+ exports.sanitizeHtml = sanitizeHtml;
675
+ //# sourceMappingURL=chunk-3U7PUZLI.cjs.map
676
+ //# sourceMappingURL=chunk-3U7PUZLI.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/sanitize.ts","../src/components/Button/Button.colors.ts","../src/components/Button/Button.tsx"],"names":["forwardRef","resolve","jsx","cn","Icon","jsxs","Fragment"],"mappings":"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","file":"chunk-3U7PUZLI.cjs","sourcesContent":["/**\n * HTML and URL sanitization utilities.\n *\n * Hand-rolled (no third-party deps) using browser-native `DOMParser`. Designed\n * for the trust model \"consumer passes user-generated markdown/HTML to a\n * react-fancy component\" — strips script tags, event handlers, and dangerous\n * URI schemes from `href`/`src` attributes.\n *\n * For server-side rendering (no `window`), `sanitizeHtml` returns the input\n * unchanged. Consumers SSR-rendering untrusted content should sanitize on the\n * server with their own pipeline.\n */\n\nconst DANGEROUS_TAGS = new Set([\n \"script\",\n \"style\",\n \"iframe\",\n \"object\",\n \"embed\",\n \"link\",\n \"meta\",\n \"base\",\n \"form\",\n // A <title> anywhere in the document retitles the host's tab.\n \"title\",\n]);\n\nconst URL_ATTRS = new Set([\"href\", \"src\", \"action\", \"formaction\", \"xlink:href\"]);\n\nconst SAFE_PROTOCOL = /^(?:https?:|mailto:|tel:|sms:|ftp:|#|\\/|\\.\\/|\\.\\.\\/|[^:]*$)/i;\n\n/**\n * Validate a URL/href against an allow-list of safe protocols. Returns the\n * input if safe, or `undefined` if it begins with a dangerous scheme like\n * `javascript:`, `data:`, or `vbscript:`. Relative URLs and fragment links\n * are allowed.\n */\nexport function sanitizeHref(href: string | undefined | null): string | undefined {\n if (href == null) return undefined;\n const trimmed = href.trim();\n if (!trimmed) return undefined;\n return SAFE_PROTOCOL.test(trimmed) ? trimmed : undefined;\n}\n\nfunction stripDangerousAttrs(el: Element): void {\n // Collect names first; mutating during iteration is awkward with NamedNodeMap.\n const names: string[] = [];\n for (let i = 0; i < el.attributes.length; i++) {\n names.push(el.attributes[i].name);\n }\n\n for (const name of names) {\n const lower = name.toLowerCase();\n\n // All on* event handlers\n if (lower.startsWith(\"on\")) {\n el.removeAttribute(name);\n continue;\n }\n\n // URL attributes — protocol-filter\n if (URL_ATTRS.has(lower)) {\n const sanitized = sanitizeHref(el.getAttribute(name));\n if (sanitized === undefined) {\n el.removeAttribute(name);\n } else {\n el.setAttribute(name, sanitized);\n }\n continue;\n }\n\n // srcdoc executes JS in the iframe — drop wholesale\n if (lower === \"srcdoc\") {\n el.removeAttribute(name);\n }\n }\n}\n\nfunction walk(el: Element, removeQueue: Element[]): void {\n const tag = el.tagName.toLowerCase();\n\n if (DANGEROUS_TAGS.has(tag)) {\n removeQueue.push(el);\n return;\n }\n\n stripDangerousAttrs(el);\n\n const children = Array.from(el.children);\n for (const child of children) {\n walk(child, removeQueue);\n }\n}\n\n/**\n * Allowlist options for {@link sanitizeHtml}. Passing ANY options object\n * (even `{}`) switches from the default blocklist to allowlist mode, where\n * everything not listed is removed.\n */\nexport interface SanitizeHtmlOptions {\n /**\n * Tags to keep, e.g. `[\"p\", \"a\", \"strong\"]`. A tag that is not listed is\n * removed but its text and (cleaned) children are kept. Default: none, so\n * only text survives.\n *\n * Some tags are removed WITH their content even when listed, because no\n * attribute filtering makes them safe: `script`, `style`, `iframe`, `frame`,\n * `frameset`, `object`, `embed`, `applet`, `link`, `meta`, `base`, `form`,\n * `title`, `template`, `noscript`, `noembed`, `noframes`, `xmp`,\n * `plaintext`, `svg` and `math`.\n */\n allowedTags?: readonly string[];\n /**\n * Attributes to keep. Either one list for every allowed tag, or a map from\n * tag name to its list, where the key `\"*\"` applies to every tag. A trailing\n * `*` matches a prefix (`\"data-*\"`, `\"aria-*\"`). Default: none.\n *\n * Never kept, even when listed: event handlers (`on*`), `srcdoc` and\n * `srcset`.\n */\n allowedAttributes?: readonly string[] | Readonly<Record<string, readonly string[]>>;\n /**\n * URL schemes allowed in URL attributes (`href`, `src`, `action`,\n * `formaction`, `cite`, `poster`, `background`, `ping`, `xlink:href`), with\n * the colon: `[\"https:\", \"mailto:\"]`. A URL attribute whose scheme is not\n * listed is removed. Default: `[\"http:\", \"https:\", \"mailto:\"]`.\n *\n * `javascript:` and `vbscript:` are never kept, even when listed.\n */\n allowedSchemes?: readonly string[];\n /**\n * Keep URLs with no scheme that stay on this site: `/x`, `x/y`, `../x`,\n * `?q`, `#frag`. Protocol-relative URLs (`//host/x`, and the backslash\n * spellings a browser treats the same way) leave the site and are always\n * removed. Default: `false`.\n */\n allowRelative?: boolean;\n}\n\n/** Removed with their content in allowlist mode, whatever the options say. */\nconst ALWAYS_REMOVED_TAGS = new Set([\n ...DANGEROUS_TAGS,\n \"frame\",\n \"frameset\",\n \"applet\",\n \"template\",\n \"noscript\",\n \"noembed\",\n \"noframes\",\n \"xmp\",\n \"plaintext\",\n // Foreign content: namespace confusion is how mutation XSS gets past\n // sanitisers that re-serialise, as this one does.\n \"svg\",\n \"math\",\n]);\n\nconst ALLOWLIST_URL_ATTRS = new Set([\n \"href\",\n \"src\",\n \"action\",\n \"formaction\",\n \"cite\",\n \"poster\",\n \"background\",\n \"ping\",\n \"xlink:href\",\n]);\n\nconst NEVER_ALLOWED_ATTRS = new Set([\"srcdoc\", \"srcset\"]);\n\nconst NEVER_ALLOWED_SCHEMES = new Set([\"javascript:\", \"vbscript:\"]);\n\nconst DEFAULT_SCHEMES = [\"http:\", \"https:\", \"mailto:\"];\n\nconst SCHEME = /^([a-z][a-z0-9+.-]*:)/i;\n\nconst SLASH = 47;\nconst BACKSLASH = 92;\n\ninterface Policy {\n tags: Set<string>;\n globalAttrs: string[];\n tagAttrs: Map<string, string[]>;\n schemes: Set<string>;\n allowRelative: boolean;\n}\n\nfunction lowerAll(list: readonly string[] | undefined): string[] {\n return (list ?? []).map((s) => s.toLowerCase());\n}\n\nfunction toPolicy(options: SanitizeHtmlOptions): Policy {\n const tagAttrs = new Map<string, string[]>();\n let globalAttrs: string[] = [];\n const attrs = options.allowedAttributes;\n if (Array.isArray(attrs)) {\n globalAttrs = lowerAll(attrs);\n } else if (attrs) {\n for (const [tag, list] of Object.entries(attrs as Record<string, readonly string[]>)) {\n if (tag === \"*\") globalAttrs = lowerAll(list);\n else tagAttrs.set(tag.toLowerCase(), lowerAll(list));\n }\n }\n return {\n tags: new Set(lowerAll(options.allowedTags)),\n globalAttrs,\n tagAttrs,\n schemes: new Set(lowerAll(options.allowedSchemes ?? DEFAULT_SCHEMES)),\n allowRelative: options.allowRelative === true,\n };\n}\n\nfunction attrListed(name: string, list: string[] | undefined): boolean {\n if (!list) return false;\n for (const entry of list) {\n if (entry === name) return true;\n if (entry.endsWith(\"*\") && name.startsWith(entry.slice(0, -1))) return true;\n }\n return false;\n}\n\n/**\n * Browsers ignore ASCII whitespace and control characters inside a URL's\n * scheme (`java<TAB>script:` runs), so judge the URL with them removed.\n */\nfunction stripControlAndSpace(value: string): string {\n let out = \"\";\n for (let i = 0; i < value.length; i++) {\n if (value.charCodeAt(i) > 32) out += value[i];\n }\n return out;\n}\n\nfunction urlAllowed(value: string, policy: Policy): boolean {\n const url = stripControlAndSpace(value);\n const match = SCHEME.exec(url);\n if (match) {\n const scheme = match[1].toLowerCase();\n return !NEVER_ALLOWED_SCHEMES.has(scheme) && policy.schemes.has(scheme);\n }\n if (!policy.allowRelative) return false;\n const a = url.charCodeAt(0);\n const b = url.charCodeAt(1);\n const isSlash = (c: number) => c === SLASH || c === BACKSLASH;\n // `//host`, and its backslash spellings, is another site.\n return !(isSlash(a) && isSlash(b));\n}\n\nfunction cleanAttributes(el: Element, tag: string, policy: Policy): void {\n const names: string[] = [];\n for (let i = 0; i < el.attributes.length; i++) {\n names.push(el.attributes[i].name);\n }\n for (const name of names) {\n const lower = name.toLowerCase();\n const keep =\n !lower.startsWith(\"on\") &&\n !NEVER_ALLOWED_ATTRS.has(lower) &&\n (attrListed(lower, policy.globalAttrs) || attrListed(lower, policy.tagAttrs.get(tag))) &&\n (!ALLOWLIST_URL_ATTRS.has(lower) || urlAllowed(el.getAttribute(name) ?? \"\", policy));\n if (!keep) el.removeAttribute(name);\n }\n}\n\nfunction cleanChildren(parent: Node, policy: Policy): void {\n for (const node of Array.from(parent.childNodes)) {\n if (node.nodeType === 3) continue; // text\n if (node.nodeType !== 1) {\n // comments, processing instructions, CDATA\n parent.removeChild(node);\n continue;\n }\n const el = node as Element;\n const tag = el.tagName.toLowerCase();\n if (ALWAYS_REMOVED_TAGS.has(tag)) {\n parent.removeChild(el);\n continue;\n }\n cleanChildren(el, policy);\n if (!policy.tags.has(tag)) {\n while (el.firstChild) parent.insertBefore(el.firstChild, el);\n parent.removeChild(el);\n continue;\n }\n cleanAttributes(el, tag, policy);\n }\n}\n\nfunction escapeText(text: string): string {\n return text\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#39;\");\n}\n\nfunction sanitizeAllowlist(html: string, options: SanitizeHtmlOptions): string {\n if (typeof window === \"undefined\" || typeof DOMParser === \"undefined\") {\n // Fail closed: with no parser we cannot tell markup from text, so the\n // whole input is returned as escaped text rather than passed through.\n return escapeText(html);\n }\n const doc = new DOMParser().parseFromString(`<body>${html}</body>`, \"text/html\");\n const body = doc.body;\n if (!body) return \"\";\n cleanChildren(body, toPolicy(options));\n return body.innerHTML;\n}\n\n/**\n * Sanitize an HTML string.\n *\n * **With no options** (the default blocklist): removes dangerous tags (script,\n * iframe, etc.), event-handler attributes (`onclick`, `onerror`, ...), and\n * dangerous URI schemes in `href`/`src` attributes. Everything else is kept.\n * In non-browser environments returns the input unchanged — sanitize on the\n * server in those cases.\n *\n * **With options** (allowlist mode): keeps ONLY the listed tags, attributes and\n * URL schemes; see {@link SanitizeHtmlOptions}. The allowlist can only narrow:\n * event handlers, `javascript:` URLs and script-capable tags never survive,\n * even when listed. In non-browser environments the input is returned as\n * escaped text (fail closed).\n *\n * Uses the browser's `DOMParser`.\n */\nexport function sanitizeHtml(html: string, options?: SanitizeHtmlOptions): string {\n if (options !== undefined && options !== null) {\n return sanitizeAllowlist(html, options);\n }\n if (typeof window === \"undefined\" || typeof DOMParser === \"undefined\") {\n return html;\n }\n\n const doc = new DOMParser().parseFromString(`<body>${html}</body>`, \"text/html\");\n const body = doc.body;\n if (!body) return html;\n\n const removeQueue: Element[] = [];\n for (const child of Array.from(body.children)) {\n walk(child, removeQueue);\n }\n\n for (const el of removeQueue) {\n el.parentNode?.removeChild(el);\n }\n\n return body.innerHTML;\n}\n","// AUTO-GENERATED by scripts/gen-button-colors — do not edit by hand.\n// Literal Tailwind class strings for every color in the v4 palette so the\n// scanner can detect them in the published dist (template-built names are\n// invisible to Tailwind's JIT). Regenerate after changing the palette.\nimport type { Color } from \"../../utils/types\";\n\nexport const buttonColorClasses: Record<Color, string> = {\n // The host's colour, from CSS variables -- no dark: variant, because a\n // host that themes per mode scopes the variables themselves.\n brand: \"bg-brand text-brand-contrast border border-brand hover:bg-brand-hover\",\n slate: \"bg-slate-500 text-white border border-slate-600 hover:bg-slate-600 dark:bg-slate-600 dark:border-slate-500 dark:hover:bg-slate-500\",\n gray: \"bg-gray-500 text-white border border-gray-600 hover:bg-gray-600 dark:bg-gray-600 dark:border-gray-500 dark:hover:bg-gray-500\",\n zinc: \"bg-zinc-500 text-white border border-zinc-600 hover:bg-zinc-600 dark:bg-zinc-600 dark:border-zinc-500 dark:hover:bg-zinc-500\",\n neutral: \"bg-neutral-500 text-white border border-neutral-600 hover:bg-neutral-600 dark:bg-neutral-600 dark:border-neutral-500 dark:hover:bg-neutral-500\",\n stone: \"bg-stone-500 text-white border border-stone-600 hover:bg-stone-600 dark:bg-stone-600 dark:border-stone-500 dark:hover:bg-stone-500\",\n red: \"bg-red-500 text-white border border-red-600 hover:bg-red-600 dark:bg-red-600 dark:border-red-500 dark:hover:bg-red-500\",\n orange: \"bg-orange-500 text-white border border-orange-600 hover:bg-orange-600 dark:bg-orange-600 dark:border-orange-500 dark:hover:bg-orange-500\",\n amber: \"bg-amber-400 text-amber-950 border border-amber-500 hover:bg-amber-500 dark:bg-amber-300 dark:text-amber-950 dark:border-amber-400 dark:hover:bg-amber-400\",\n yellow: \"bg-yellow-400 text-yellow-950 border border-yellow-500 hover:bg-yellow-500 dark:bg-yellow-300 dark:text-yellow-950 dark:border-yellow-400 dark:hover:bg-yellow-400\",\n lime: \"bg-lime-400 text-lime-950 border border-lime-500 hover:bg-lime-500 dark:bg-lime-300 dark:text-lime-950 dark:border-lime-400 dark:hover:bg-lime-400\",\n green: \"bg-green-500 text-white border border-green-600 hover:bg-green-600 dark:bg-green-600 dark:border-green-500 dark:hover:bg-green-500\",\n emerald: \"bg-emerald-500 text-white border border-emerald-600 hover:bg-emerald-600 dark:bg-emerald-600 dark:border-emerald-500 dark:hover:bg-emerald-500\",\n teal: \"bg-teal-500 text-white border border-teal-600 hover:bg-teal-600 dark:bg-teal-600 dark:border-teal-500 dark:hover:bg-teal-500\",\n cyan: \"bg-cyan-500 text-white border border-cyan-600 hover:bg-cyan-600 dark:bg-cyan-600 dark:border-cyan-500 dark:hover:bg-cyan-500\",\n sky: \"bg-sky-500 text-white border border-sky-600 hover:bg-sky-600 dark:bg-sky-600 dark:border-sky-500 dark:hover:bg-sky-500\",\n blue: \"bg-blue-500 text-white border border-blue-600 hover:bg-blue-600 dark:bg-blue-600 dark:border-blue-500 dark:hover:bg-blue-500\",\n indigo: \"bg-indigo-500 text-white border border-indigo-600 hover:bg-indigo-600 dark:bg-indigo-600 dark:border-indigo-500 dark:hover:bg-indigo-500\",\n violet: \"bg-violet-500 text-white border border-violet-600 hover:bg-violet-600 dark:bg-violet-600 dark:border-violet-500 dark:hover:bg-violet-500\",\n purple: \"bg-purple-500 text-white border border-purple-600 hover:bg-purple-600 dark:bg-purple-600 dark:border-purple-500 dark:hover:bg-purple-500\",\n fuchsia: \"bg-fuchsia-500 text-white border border-fuchsia-600 hover:bg-fuchsia-600 dark:bg-fuchsia-600 dark:border-fuchsia-500 dark:hover:bg-fuchsia-500\",\n pink: \"bg-pink-500 text-white border border-pink-600 hover:bg-pink-600 dark:bg-pink-600 dark:border-pink-500 dark:hover:bg-pink-500\",\n rose: \"bg-rose-500 text-white border border-rose-600 hover:bg-rose-600 dark:bg-rose-600 dark:border-rose-500 dark:hover:bg-rose-500\",\n};\n\nexport const buttonGhostClasses: Record<Color, string> = {\n // The host's colour, from CSS variables -- no dark: variant, because a\n // host that themes per mode scopes the variables themselves.\n brand: \"bg-transparent text-brand hover:bg-brand/10\",\n slate: \"bg-transparent text-slate-600 hover:bg-slate-50 dark:text-slate-400 dark:hover:bg-slate-950/50\",\n gray: \"bg-transparent text-gray-600 hover:bg-gray-50 dark:text-gray-400 dark:hover:bg-gray-950/50\",\n zinc: \"bg-transparent text-zinc-600 hover:bg-zinc-50 dark:text-zinc-400 dark:hover:bg-zinc-950/50\",\n neutral: \"bg-transparent text-neutral-600 hover:bg-neutral-50 dark:text-neutral-400 dark:hover:bg-neutral-950/50\",\n stone: \"bg-transparent text-stone-600 hover:bg-stone-50 dark:text-stone-400 dark:hover:bg-stone-950/50\",\n red: \"bg-transparent text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-950/50\",\n orange: \"bg-transparent text-orange-600 hover:bg-orange-50 dark:text-orange-400 dark:hover:bg-orange-950/50\",\n amber: \"bg-transparent text-amber-600 hover:bg-amber-50 dark:text-amber-400 dark:hover:bg-amber-950/50\",\n yellow: \"bg-transparent text-yellow-600 hover:bg-yellow-50 dark:text-yellow-400 dark:hover:bg-yellow-950/50\",\n lime: \"bg-transparent text-lime-600 hover:bg-lime-50 dark:text-lime-400 dark:hover:bg-lime-950/50\",\n green: \"bg-transparent text-green-600 hover:bg-green-50 dark:text-green-400 dark:hover:bg-green-950/50\",\n emerald: \"bg-transparent text-emerald-600 hover:bg-emerald-50 dark:text-emerald-400 dark:hover:bg-emerald-950/50\",\n teal: \"bg-transparent text-teal-600 hover:bg-teal-50 dark:text-teal-400 dark:hover:bg-teal-950/50\",\n cyan: \"bg-transparent text-cyan-600 hover:bg-cyan-50 dark:text-cyan-400 dark:hover:bg-cyan-950/50\",\n sky: \"bg-transparent text-sky-600 hover:bg-sky-50 dark:text-sky-400 dark:hover:bg-sky-950/50\",\n blue: \"bg-transparent text-blue-600 hover:bg-blue-50 dark:text-blue-400 dark:hover:bg-blue-950/50\",\n indigo: \"bg-transparent text-indigo-600 hover:bg-indigo-50 dark:text-indigo-400 dark:hover:bg-indigo-950/50\",\n violet: \"bg-transparent text-violet-600 hover:bg-violet-50 dark:text-violet-400 dark:hover:bg-violet-950/50\",\n purple: \"bg-transparent text-purple-600 hover:bg-purple-50 dark:text-purple-400 dark:hover:bg-purple-950/50\",\n fuchsia: \"bg-transparent text-fuchsia-600 hover:bg-fuchsia-50 dark:text-fuchsia-400 dark:hover:bg-fuchsia-950/50\",\n pink: \"bg-transparent text-pink-600 hover:bg-pink-50 dark:text-pink-400 dark:hover:bg-pink-950/50\",\n rose: \"bg-transparent text-rose-600 hover:bg-rose-50 dark:text-rose-400 dark:hover:bg-rose-950/50\",\n};\n","import { forwardRef, type AnchorHTMLAttributes, type ElementType, type ForwardedRef, type ReactNode } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { sanitizeHref } from \"../../utils/sanitize\";\nimport { resolve } from \"../../data/emoji-utils\";\nimport { Icon } from \"../Icon/Icon\";\nimport type { ButtonProps } from \"./Button.types\";\nimport type { ButtonColor, Size } from \"../../utils/types\";\nimport {\n buttonColorClasses as colorClasses,\n buttonGhostClasses as ghostColorClasses,\n} from \"./Button.colors\";\n\nconst iconSizeMap: Record<Size, \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\"> = {\n xs: \"xs\",\n sm: \"xs\",\n md: \"sm\",\n lg: \"md\",\n xl: \"lg\",\n};\n\nconst alertIconSizeMap: Record<Size, \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\"> = {\n xs: \"xs\",\n sm: \"xs\",\n md: \"xs\",\n lg: \"sm\",\n xl: \"sm\",\n};\n\n// ---------------------------------------------------------------------------\n// Color classes\n//\n// Solid + ghost color maps live in ./Button.colors (AUTO-GENERATED, all 22\n// Tailwind v4 hues as literal strings). The state-color classes below\n// (default/active/checked/warn) branch on flags, not specific colors.\n// ---------------------------------------------------------------------------\n\n// The default button's fill reads the theme tokens, like its text and border\n// already did, so a host palette reaches it. Each fallback is the colour it\n// used before: light is the surface (white), dark is the muted surface\n// (zinc-800), and the light hover is surface-hover / border-strong. The dark\n// hover stays zinc-700: no role defaults to it (surface-hover-dark is a\n// translucent zinc-800, which would all but erase the hover on a zinc-800\n// button), so tokenising it would change every host that sets nothing.\nconst defaultClasses =\n \"bg-[color:var(--fancy-surface,var(--color-white))] text-[color:var(--fancy-text-soft,var(--color-zinc-700))] border border-[color:var(--fancy-border,var(--color-zinc-200))] hover:bg-[color:var(--fancy-surface-hover,var(--color-zinc-50))] hover:border-[color:var(--fancy-border-strong,var(--color-zinc-300))] dark:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))] dark:text-[color:var(--fancy-text-soft-dark,var(--color-zinc-300))] dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:hover:bg-zinc-700\";\n\nconst activeClasses =\n \"bg-blue-500 text-white border border-blue-600 hover:bg-blue-600 dark:bg-blue-600 dark:border-blue-500 dark:hover:bg-blue-500\";\n\nconst checkedClasses =\n \"bg-emerald-500 text-white border border-emerald-600 hover:bg-emerald-600 dark:bg-emerald-600 dark:border-emerald-500 dark:hover:bg-emerald-500\";\n\nconst warnClasses =\n \"bg-amber-50 text-amber-700 border border-amber-200 hover:bg-amber-100 dark:bg-amber-900/30 dark:text-amber-300 dark:border-amber-700 dark:hover:bg-amber-900/50\";\n\nconst ghostDefaultClasses =\n \"bg-transparent text-[color:var(--fancy-text-soft,var(--color-zinc-700))] hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:text-[color:var(--fancy-text-soft-dark,var(--color-zinc-300))] dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]\";\n\nconst ghostActiveClasses =\n \"bg-blue-50 text-blue-700 hover:bg-blue-100 dark:bg-blue-950/50 dark:text-blue-300 dark:hover:bg-blue-950\";\n\nconst ghostCheckedClasses =\n \"bg-emerald-50 text-emerald-700 hover:bg-emerald-100 dark:bg-emerald-950/50 dark:text-emerald-300 dark:hover:bg-emerald-950\";\n\nconst ghostWarnClasses =\n \"bg-transparent text-amber-700 hover:bg-amber-50 dark:text-amber-300 dark:hover:bg-amber-950/50\";\n\n// ---------------------------------------------------------------------------\n// Size helpers\n// ---------------------------------------------------------------------------\n\nconst horizontalSize: Record<Size, string> = {\n xs: \"px-2 py-1 text-xs\",\n sm: \"px-2 py-1 text-xs\",\n md: \"px-3 py-1.5 text-sm\",\n lg: \"px-4 py-2.5 text-base\",\n xl: \"px-5 py-3 text-lg\",\n};\n\nconst circleSize: Record<Size, string> = {\n xs: \"w-7 h-7 text-xs\",\n sm: \"w-8 h-8 text-xs\",\n md: \"w-10 h-10 text-sm\",\n lg: \"w-12 h-12 text-base\",\n xl: \"w-14 h-14 text-lg\",\n};\n\nconst iconSize: Record<Size, string> = {\n xs: \"w-3 h-3\",\n sm: \"w-3 h-3\",\n md: \"w-4 h-4\",\n lg: \"w-5 h-5\",\n xl: \"w-6 h-6\",\n};\n\nconst avatarSize: Record<Size, string> = {\n xs: \"w-4 h-4\",\n sm: \"w-4 h-4\",\n md: \"w-5 h-5\",\n lg: \"w-6 h-6\",\n xl: \"w-7 h-7\",\n};\n\nconst alertIconSize: Record<Size, string> = {\n xs: \"w-2 h-2\",\n sm: \"w-2.5 h-2.5\",\n md: \"w-3 h-3\",\n lg: \"w-4 h-4\",\n xl: \"w-4 h-4\",\n};\n\nconst badgeSize: Record<Size, string> = {\n xs: \"text-[10px] px-1 min-w-[14px] h-3.5\",\n sm: \"text-[10px] px-1.5 min-w-[16px] h-4\",\n md: \"text-[11px] px-1.5 min-w-[18px] h-[18px]\",\n lg: \"text-xs px-2 min-w-[22px] h-5\",\n xl: \"text-xs px-2 min-w-[24px] h-5\",\n};\n\nconst externalIconOffset: Record<Size, string> = {\n xs: \"0.75rem\",\n sm: \"0.75rem\",\n md: \"1rem\",\n lg: \"1.25rem\",\n xl: \"1.5rem\",\n};\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction isColored(color?: ButtonColor, active?: boolean, checked?: boolean): boolean {\n return !!(color || active || checked);\n}\n\nfunction getColorClasses(\n color?: ButtonColor,\n active?: boolean,\n checked?: boolean,\n warn?: boolean,\n ghost?: boolean,\n): string {\n if (ghost) {\n if (color) return ghostColorClasses[color];\n if (checked) return ghostCheckedClasses;\n if (active) return ghostActiveClasses;\n if (warn) return ghostWarnClasses;\n return ghostDefaultClasses;\n }\n if (color) return colorClasses[color];\n if (checked) return checkedClasses;\n if (active) return activeClasses;\n if (warn) return warnClasses;\n return defaultClasses;\n}\n\nfunction getIconColorClasses(\n color?: ButtonColor,\n active?: boolean,\n checked?: boolean,\n warn?: boolean,\n ghost?: boolean,\n): string {\n if (ghost) {\n // Ghost variant: icon inherits currentColor from the button text\n return \"text-current\";\n }\n // A filled button's icon takes the label's colour — whatever that colour class\n // says, including a host's per-mode `text-brand-contrast` and the dark labels on\n // amber / yellow / lime. Hard-coded white was invisible on any light fill.\n if (color || active || checked) return \"text-current\";\n if (warn) return \"text-amber-600 dark:text-amber-400\";\n return \"text-[color:var(--fancy-text-muted,var(--color-zinc-500))] dark:text-[color:var(--fancy-text-muted-dark,var(--color-zinc-400))]\";\n}\n\nfunction getBadgeClasses(\n color?: ButtonColor,\n active?: boolean,\n checked?: boolean,\n warn?: boolean,\n ghost?: boolean,\n): string {\n if (ghost) {\n return \"bg-current/10 text-current\";\n }\n // Tinted with the label's own colour, for the same reason as the icon.\n if (isColored(color, active, checked)) return \"bg-current/20 text-current\";\n if (warn) return \"bg-amber-200 text-amber-800 dark:bg-amber-800 dark:text-amber-200\";\n return \"bg-zinc-200 text-zinc-700 dark:bg-zinc-600 dark:text-zinc-200\";\n}\n\n// ---------------------------------------------------------------------------\n// Placement parsing\n// ---------------------------------------------------------------------------\n\ntype PlacementAxis = \"left\" | \"right\" | \"top\" | \"bottom\";\n\nfunction parsePlacement(iconPlace: string): { primary: PlacementAxis; secondary?: PlacementAxis } {\n const parts = iconPlace.split(\" \") as PlacementAxis[];\n return { primary: parts[0], secondary: parts[1] };\n}\n\nfunction isVerticalPrimary(primary: PlacementAxis): boolean {\n return primary === \"top\" || primary === \"bottom\";\n}\n\n// ---------------------------------------------------------------------------\n// Sort parsing\n// ---------------------------------------------------------------------------\n\ntype ElementKey = \"e\" | \"i\" | \"a\" | \"b\";\nconst DEFAULT_SORT = \"eiab\";\n\nfunction parseSortOrder(sort?: string): ElementKey[] {\n const chars = (sort ?? DEFAULT_SORT).split(\"\") as ElementKey[];\n const valid: ElementKey[] = [];\n const seen = new Set<string>();\n for (const c of chars) {\n if (\"eiab\".includes(c) && !seen.has(c)) {\n valid.push(c);\n seen.add(c);\n }\n }\n // Add missing keys in default order\n for (const c of DEFAULT_SORT.split(\"\") as ElementKey[]) {\n if (!seen.has(c)) valid.push(c);\n }\n return valid;\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n children,\n className,\n variant = \"default\",\n color,\n size = \"md\",\n active,\n checked,\n warn,\n alert: alertProp,\n icon,\n iconTrailing,\n iconPlace = \"left\",\n alertIcon,\n alertIconTrailing,\n emoji,\n emojiTrailing,\n avatar,\n avatarTrailing,\n badge,\n badgeTrailing,\n sort,\n loading = false,\n disabled,\n href,\n as: As,\n responsive,\n labelClassName,\n ...props\n },\n ref,\n ) => {\n const isCircle = variant === \"circle\";\n const isGhost = variant === \"ghost\";\n const { primary, secondary } = parsePlacement(iconPlace);\n const isVertical = isVerticalPrimary(primary);\n const isCompound = !!secondary;\n const needsWrapper = isVertical || isCompound;\n const isReversed = iconPlace === \"right\";\n\n // -----------------------------------------------------------------------\n // Sizing — always use horizontalSize for the button itself\n // -----------------------------------------------------------------------\n const sizeClass = isCircle ? circleSize[size] : horizontalSize[size];\n\n // -----------------------------------------------------------------------\n // Build decorative elements\n // -----------------------------------------------------------------------\n const iconColorCls = getIconColorClasses(color, active, checked, warn, isGhost);\n\n const buildElement = (key: ElementKey, trailing: boolean): ReactNode => {\n switch (key) {\n case \"e\": {\n const slug = trailing ? emojiTrailing : emoji;\n if (!slug) return null;\n const char = resolve(slug);\n if (!char) return null;\n return (\n <span key={`emoji-${trailing ? \"t\" : \"l\"}`} data-action-emoji>\n {char}\n </span>\n );\n }\n case \"i\": {\n if (loading && !trailing) {\n return (\n <span\n key=\"spinner\"\n className={cn(\"animate-spin border-2 border-current border-t-transparent rounded-full\", iconSize[size])}\n />\n );\n }\n const iconSlug = trailing ? iconTrailing : icon;\n if (!iconSlug) return null;\n // For vertical/compound placements, icons render outside the button — not inside\n if (needsWrapper) return null;\n return (\n <span\n key={`icon-${trailing ? \"t\" : \"l\"}`}\n data-action-icon={trailing ? \"trailing\" : \"leading\"}\n className={cn(\"inline-flex items-center flex-shrink-0\", iconColorCls)}\n >\n <Icon name={iconSlug} size={iconSizeMap[size]} />\n </span>\n );\n }\n case \"a\": {\n if (!avatar) return null;\n const isTrailing = avatarTrailing ?? false;\n if (isTrailing !== trailing) return null;\n return (\n <img\n key=\"avatar\"\n src={avatar}\n alt=\"\"\n className={cn(avatarSize[size], \"rounded-full object-cover flex-shrink-0\")}\n data-action-avatar\n />\n );\n }\n case \"b\": {\n if (!badge) return null;\n const isTrailing = badgeTrailing ?? false;\n if (isTrailing !== trailing) return null;\n return (\n <span\n key=\"badge\"\n className={cn(\n \"inline-flex items-center justify-center font-medium rounded-full\",\n badgeSize[size],\n getBadgeClasses(color, active, checked, warn, isGhost),\n )}\n data-action-badge\n >\n {badge}\n </span>\n );\n }\n default:\n return null;\n }\n };\n\n // Alert icon element\n const renderAlertIcon = (trailing: boolean): ReactNode => {\n if (!alertIcon) return null;\n const isTrailing = alertIconTrailing ?? false;\n if (isTrailing !== trailing) return null;\n const alertIconEl = <Icon name={alertIcon} size={alertIconSizeMap[size]} />;\n return (\n <span\n key=\"alert-icon\"\n className=\"relative inline-flex flex-shrink-0\"\n data-action-alert\n >\n <span className=\"text-red-500 dark:text-red-400 animate-pulse\">\n {alertIconEl}\n </span>\n <span className=\"absolute inset-0 flex items-center justify-center text-red-400 dark:text-red-300 animate-ping opacity-75\">\n {alertIconEl}\n </span>\n </span>\n );\n };\n\n const sortOrder = parseSortOrder(sort);\n\n const leadingElements: ReactNode[] = [];\n const trailingElements: ReactNode[] = [];\n\n for (const key of sortOrder) {\n const leading = buildElement(key, false);\n if (leading) leadingElements.push(leading);\n const trailing = buildElement(key, true);\n if (trailing) trailingElements.push(trailing);\n }\n\n // Insert alert icon\n const leadingAlert = renderAlertIcon(false);\n if (leadingAlert) leadingElements.push(leadingAlert);\n const trailingAlert = renderAlertIcon(true);\n if (trailingAlert) trailingElements.push(trailingAlert);\n\n // -----------------------------------------------------------------------\n // Classes\n // -----------------------------------------------------------------------\n const classes = cn(\n // `text-left` overrides the UA <button> center default so a wrapped,\n // width-constrained multi-word label stacks left-aligned. Overridable —\n // twMerge lets a `text-center` / `text-right` in `className` win.\n \"inline-flex items-center justify-center text-left font-medium transition-all duration-200\",\n \"focus:outline-none focus:ring-2 focus:ring-offset-1\",\n \"disabled:opacity-50 disabled:cursor-not-allowed\",\n isCircle ? \"rounded-full\" : \"rounded-lg\",\n sizeClass,\n getColorClasses(color, active, checked, warn, isGhost),\n !isCircle && !needsWrapper && \"gap-2\",\n !isCircle && needsWrapper && \"gap-2\",\n isReversed && !needsWrapper && \"flex-row-reverse\",\n alertProp && \"animate-pulse\",\n className,\n );\n\n // -----------------------------------------------------------------------\n // Content\n // -----------------------------------------------------------------------\n const content = isCircle ? (\n <>\n {loading ? (\n <span\n className={cn(\n \"animate-spin border-2 border-current border-t-transparent rounded-full\",\n iconSize[size],\n )}\n />\n ) : (\n icon ? <Icon name={icon} size={iconSizeMap[size]} /> : children\n )}\n </>\n ) : (\n <>\n {leadingElements}\n {children != null && (\n <span\n data-react-fancy-button-label=\"\"\n className={cn(\n // A button narrower than its label WRAPS the label inside itself\n // (left-aligned, via the button's `text-left`); the icon is its own\n // shrink-0 flex item, so it stays put beside the text. `min-w-0`\n // lets the span shrink below its text. Words are never split\n // (measured: in the showcase's 104px column `break-words` turned\n // \"customer\" into \"custome/r\"). Unconstrained, nothing wraps and\n // the button looks as it always did.\n //\n // No white-space class here, on purpose: `labelClassName=\"truncate\"`\n // must win uncontested to give one line with an ellipsis, and a\n // `whitespace-normal` would fight its nowrap in the cascade.\n // (5.54.0 and earlier forced `whitespace-nowrap`, so a constrained\n // label spilled out of the button and over its neighbours.)\n \"min-w-0\",\n // `responsive` collapses to icon-only when squeezed, while keeping\n // the label readable to screen readers.\n responsive && \"sr-only sm:not-sr-only\",\n labelClassName,\n )}\n >\n {children}\n </span>\n )}\n {trailingElements}\n </>\n );\n\n // -----------------------------------------------------------------------\n // Build the button element\n // -----------------------------------------------------------------------\n const safeHref = sanitizeHref(href);\n // Anchor mode renders `as` when provided (e.g. a router's <Link> for\n // client-side navigation — Inertia, React Router, Next). Previously `as`\n // fell through ...props onto the <a> where React silently DROPS a\n // function-valued unknown attribute — so `<Button as={Link}>` looked fine\n // but did a full page load. `as` only applies in anchor mode; without an\n // href (or when disabled) the plain <button> renders as always.\n const Anchor = (As ?? \"a\") as ElementType;\n const buttonEl = safeHref && !disabled ? (\n // Anchor mode forwards the same rest props as the <button> branch so\n // onClick / target / rel / ref / ARIA / data-* reach the <a> (issue #7).\n <Anchor\n ref={ref as ForwardedRef<HTMLAnchorElement>}\n href={safeHref}\n className={classes}\n data-react-fancy-button=\"\"\n data-react-fancy-action=\"\"\n {...(props as AnchorHTMLAttributes<HTMLAnchorElement>)}\n >\n {content}\n </Anchor>\n ) : (\n <button\n ref={ref}\n className={classes}\n disabled={disabled || loading}\n data-react-fancy-button=\"\"\n data-react-fancy-action=\"\"\n {...props}\n >\n {content}\n </button>\n );\n\n // -----------------------------------------------------------------------\n // Wrap for vertical/compound icon placement\n // The icon is absolutely positioned so it never shifts the button.\n // -----------------------------------------------------------------------\n if (needsWrapper && (icon || iconTrailing)) {\n const iconSlug = icon ?? iconTrailing;\n const offset = externalIconOffset[size];\n\n // Determine position styles for the icon\n const posStyle: React.CSSProperties = { position: \"absolute\" };\n\n // Primary axis: which side of the button\n if (primary === \"top\") {\n posStyle.bottom = \"100%\";\n posStyle.marginBottom = \"0.125rem\";\n } else if (primary === \"bottom\") {\n posStyle.top = \"100%\";\n posStyle.marginTop = \"0.125rem\";\n } else if (primary === \"left\") {\n posStyle.right = \"100%\";\n posStyle.marginRight = \"0.125rem\";\n } else if (primary === \"right\") {\n posStyle.left = \"100%\";\n posStyle.marginLeft = \"0.125rem\";\n }\n\n // Secondary axis: cross-axis alignment\n if (isVerticalPrimary(primary)) {\n // Vertical primary → secondary controls horizontal position\n if (secondary === \"left\") {\n posStyle.left = \"0\";\n } else if (secondary === \"right\") {\n posStyle.right = \"0\";\n } else {\n // center (default)\n posStyle.left = \"50%\";\n posStyle.transform = \"translateX(-50%)\";\n }\n } else {\n // Horizontal primary → secondary controls vertical position\n if (secondary === \"top\") {\n posStyle.top = \"0\";\n } else if (secondary === \"bottom\") {\n posStyle.bottom = \"0\";\n } else {\n // center (default)\n posStyle.top = \"50%\";\n posStyle.transform = \"translateY(-50%)\";\n }\n }\n\n const externalIcon = (\n <span\n className={cn(\"absolute flex-shrink-0\", iconColorCls)}\n style={posStyle}\n >\n <Icon name={iconSlug!} size={iconSizeMap[size]} />\n </span>\n );\n\n return (\n <span\n data-react-fancy-action-group=\"\"\n className=\"relative inline-flex\"\n >\n {buttonEl}\n {externalIcon}\n </span>\n );\n }\n\n return buttonEl;\n },\n);\n\nButton.displayName = \"Button\";\n\n/**\n * @deprecated Renamed to {@link Button}. `Action` remains as an alias for\n * backward compatibility and will be removed in a future major version.\n * Prefer importing `Button` from \"@particle-academy/react-fancy\".\n */\nexport const Action = Button;\n"]}
@@ -2,10 +2,10 @@
2
2
 
3
3
  var chunkVQJ5LJJH_cjs = require('./chunk-VQJ5LJJH.cjs');
4
4
  var chunk4GOOSZEK_cjs = require('./chunk-4GOOSZEK.cjs');
5
- var chunkMCQJFMDP_cjs = require('./chunk-MCQJFMDP.cjs');
5
+ var chunkE5UU666U_cjs = require('./chunk-E5UU666U.cjs');
6
6
  var chunkG7227RTY_cjs = require('./chunk-G7227RTY.cjs');
7
7
  var chunkL3V3BYVS_cjs = require('./chunk-L3V3BYVS.cjs');
8
- var chunkX2FC7ODX_cjs = require('./chunk-X2FC7ODX.cjs');
8
+ var chunk3U7PUZLI_cjs = require('./chunk-3U7PUZLI.cjs');
9
9
  var chunkSNHU2AI4_cjs = require('./chunk-SNHU2AI4.cjs');
10
10
  var chunkZU5Q77OT_cjs = require('./chunk-ZU5Q77OT.cjs');
11
11
  var react = require('react');
@@ -158,7 +158,7 @@ function EditorContent({ className, maxHeight: maxHeightProp, sourceClassName })
158
158
  }
159
159
  }, [contentRef, _initialHtml, showSource]);
160
160
  const extClasses = react.useMemo(
161
- () => chunkMCQJFMDP_cjs.extensionEditorClasses(extensions),
161
+ () => chunkE5UU666U_cjs.extensionEditorClasses(extensions),
162
162
  [extensions]
163
163
  );
164
164
  if (showSource) {
@@ -190,7 +190,7 @@ function EditorContent({ className, maxHeight: maxHeightProp, sourceClassName })
190
190
  className: chunkZU5Q77OT_cjs.cn(
191
191
  "min-h-[120px] flex-auto overflow-y-auto px-4 py-3 text-sm outline-none",
192
192
  "focus:outline-none",
193
- chunkMCQJFMDP_cjs.proseClasses,
193
+ chunkE5UU666U_cjs.proseClasses,
194
194
  extClasses,
195
195
  "empty:before:content-[attr(data-placeholder)] empty:before:text-zinc-400 empty:before:pointer-events-none",
196
196
  className
@@ -201,10 +201,10 @@ function EditorContent({ className, maxHeight: maxHeightProp, sourceClassName })
201
201
  EditorContent.displayName = "EditorContent";
202
202
  function toHtml(value, outputFormat, unsafe, valueFormat = "auto") {
203
203
  if (!value) return { html: "", source: null };
204
- const sanitize = (html) => unsafe ? html : chunkX2FC7ODX_cjs.sanitizeHtml(html);
205
- const format = valueFormat !== "auto" ? valueFormat : outputFormat === "html" ? "html" : chunkMCQJFMDP_cjs.detectFormat(value);
204
+ const sanitize = (html) => unsafe ? html : chunk3U7PUZLI_cjs.sanitizeHtml(html);
205
+ const format = valueFormat !== "auto" ? valueFormat : outputFormat === "html" ? "html" : chunkE5UU666U_cjs.detectFormat(value);
206
206
  if (format === "html") return { html: sanitize(value), source: null };
207
- if (outputFormat === "markdown") return chunkMCQJFMDP_cjs.markdownToEditorHtml(value, sanitize);
207
+ if (outputFormat === "markdown") return chunkE5UU666U_cjs.markdownToEditorHtml(value, sanitize);
208
208
  return { html: sanitize(marked.marked.parse(value, { async: false }).trim()), source: null };
209
209
  }
210
210
  function EditorRoot({
@@ -261,12 +261,12 @@ function EditorRoot({
261
261
  const initialHtml = seed.html;
262
262
  const seedSource = seed.source;
263
263
  const extensions = react.useMemo(
264
- () => chunkMCQJFMDP_cjs.mergeExtensions(instanceExtensions),
264
+ () => chunkE5UU666U_cjs.mergeExtensions(instanceExtensions),
265
265
  [instanceExtensions]
266
266
  );
267
267
  const getOutputValue = react.useCallback(
268
268
  (html) => {
269
- return outputFormat === "markdown" ? chunkMCQJFMDP_cjs.htmlToMarkdown(html, seedSource) : html;
269
+ return outputFormat === "markdown" ? chunkE5UU666U_cjs.htmlToMarkdown(html, seedSource) : html;
270
270
  },
271
271
  [outputFormat, seedSource]
272
272
  );
@@ -350,7 +350,7 @@ function EditorRoot({
350
350
  fs.fullScreen && "overflow-auto"
351
351
  ),
352
352
  children: /* @__PURE__ */ jsxRuntime.jsx(
353
- chunkMCQJFMDP_cjs.ContentRenderer,
353
+ chunkE5UU666U_cjs.ContentRenderer,
354
354
  {
355
355
  value,
356
356
  format: valueFormat !== "auto" ? valueFormat : outputFormat,
@@ -389,5 +389,5 @@ var Editor = Object.assign(EditorRoot, {
389
389
 
390
390
  exports.Editor = Editor;
391
391
  exports.useEditor = useEditor;
392
- //# sourceMappingURL=chunk-ATIDUWY6.cjs.map
393
- //# sourceMappingURL=chunk-ATIDUWY6.cjs.map
392
+ //# sourceMappingURL=chunk-4S5DFN5N.cjs.map
393
+ //# sourceMappingURL=chunk-4S5DFN5N.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/Editor/Editor.context.ts","../src/components/Editor/EditorSourceToggle.tsx","../src/components/Editor/EditorFullScreenToggle.tsx","../src/components/Editor/EditorToolbar.tsx","../src/components/Editor/EditorToolbarSeparator.tsx","../src/components/Editor/EditorContent.tsx","../src/components/Editor/Editor.tsx"],"names":["createContext","useContext","jsx","cn","FullScreenToggle","jsxs","Fragment","useRef","useEffect","useMemo","extensionEditorClasses","CodeView","proseClasses","sanitizeHtml","detectFormat","markdownToEditorHtml","marked","useControllableState","useFullScreen","useFieldMode","useState","mergeExtensions","useCallback","htmlToMarkdown","FullScreenProvider","ContentRenderer"],"mappings":"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","file":"chunk-ATIDUWY6.cjs","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { RefObject } from \"react\";\nimport type { RenderExtension } from \"../ContentRenderer/extensions\";\n\nexport interface EditorContextValue {\n contentRef: RefObject<HTMLDivElement | null>;\n exec: (command: string, arg?: string) => void;\n insertText: (text: string) => void;\n wrapSelection: (before: string, after: string) => void;\n outputFormat: \"html\" | \"markdown\";\n lineSpacing: number;\n placeholder?: string;\n /** Merged render extensions (global + instance). */\n extensions: RenderExtension[];\n /** Whether the raw-source view (textarea) is showing instead of the rich-text surface. */\n showSource: boolean;\n /** Toggle/set the raw-source view. */\n setShowSource: (showSource: boolean) => void;\n /** Whether the editor currently covers the viewport. */\n fullScreen: boolean;\n /** Enter/leave full screen. Goes through `onFullScreenChange` when controlled. */\n setFullScreen: (fullScreen: boolean) => void;\n /** Initial HTML content to load into the editor on mount */\n _initialHtml: string;\n /** @internal Called by EditorContent on input events */\n _onInput: () => void;\n /** @internal Raw source value (`value` in `outputFormat`), for the source textarea. */\n _sourceValue: string;\n /** @internal Commit an edit made directly in the source textarea. */\n _onSourceInput: (value: string) => void;\n}\n\nexport const EditorContext = createContext<EditorContextValue | null>(null);\n\nexport function useEditor(): EditorContextValue {\n const ctx = useContext(EditorContext);\n if (!ctx) {\n throw new Error(\"useEditor must be used within an <Editor> component\");\n }\n return ctx;\n}\n","import { cn } from \"../../utils/cn\";\nimport { useEditor } from \"./Editor.context\";\nimport type { EditorSourceToggleProps } from \"./Editor.types\";\n\n/**\n * Toolbar button that reveals the raw HTML/Markdown behind the editor. Reads\n * `showSource` / `setShowSource` from context, so it works in the default\n * toolbar and in any custom toolbar composed with `useEditor()`.\n */\nexport function EditorSourceToggle({\n className,\n icon = \"</>\",\n title = \"Source\",\n activeTitle = \"Rich text\",\n}: EditorSourceToggleProps) {\n const { showSource, setShowSource } = useEditor();\n\n return (\n <button\n type=\"button\"\n onClick={() => setShowSource(!showSource)}\n title={showSource ? activeTitle : title}\n aria-pressed={showSource}\n data-react-fancy-editor-source-toggle=\"\"\n className={cn(\n \"inline-flex h-8 items-center justify-center rounded-md px-2 font-mono text-xs transition-colors\",\n showSource\n ? \"bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]\"\n : \"hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]\",\n className,\n )}\n >\n {icon}\n </button>\n );\n}\n\nEditorSourceToggle.displayName = \"EditorSourceToggle\";\n","import { cn } from \"../../utils/cn\";\nimport { FullScreenToggle } from \"../FullScreen/FullScreenToggle\";\nimport type { EditorFullScreenToggleProps } from \"./Editor.types\";\n\n/**\n * Toolbar button that takes the editor full screen and back. The shared\n * `FullScreen.Toggle`, styled for the editor toolbar and carrying the editor's\n * own handle (`data-react-fancy-editor-fullscreen-toggle`) next to the shared\n * one (`data-react-fancy-fullscreen-toggle`).\n */\nexport function EditorFullScreenToggle({ className, enterIcon, exitIcon }: EditorFullScreenToggleProps) {\n return (\n <FullScreenToggle\n data-react-fancy-editor-fullscreen-toggle=\"\"\n enterIcon={enterIcon}\n exitIcon={exitIcon}\n className={cn(className)}\n />\n );\n}\n\nEditorFullScreenToggle.displayName = \"EditorFullScreenToggle\";\n","import { cn } from \"../../utils/cn\";\nimport { useEditor } from \"./Editor.context\";\nimport { EditorSourceToggle } from \"./EditorSourceToggle\";\nimport { EditorFullScreenToggle } from \"./EditorFullScreenToggle\";\nimport type { EditorToolbarProps } from \"./Editor.types\";\n\nconst DEFAULT_ACTIONS = [\n { icon: \"B\", label: \"Bold\", command: \"bold\" },\n { icon: \"I\", label: \"Italic\", command: \"italic\" },\n { icon: \"U\", label: \"Underline\", command: \"underline\" },\n { icon: \"S\", label: \"Strikethrough\", command: \"strikeThrough\" },\n];\n\nexport function EditorToolbar({\n actions = DEFAULT_ACTIONS,\n onAction,\n children,\n className,\n showSourceToggle = true,\n showFullScreenToggle = false,\n toolbarExtra,\n}: EditorToolbarProps) {\n const { exec, showSource } = useEditor();\n // `null` / `false` / `undefined` render nothing, so they must not add a\n // wrapper either: an unused slot leaves the toolbar byte-identical.\n const hasExtra = toolbarExtra !== undefined && toolbarExtra !== null && toolbarExtra !== false;\n\n return (\n <div\n data-react-fancy-editor-toolbar=\"\"\n className={cn(\n \"flex shrink-0 items-center gap-0.5 border-b border-[color:var(--fancy-border,var(--color-zinc-200))] px-2 py-1.5 dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))]\",\n className,\n )}\n >\n {children ?? (\n <>\n {actions.map((action) => (\n <button\n key={`${action.command}-${action.commandArg ?? \"\"}`}\n type=\"button\"\n // Rich-text commands don't apply to the raw-source textarea.\n disabled={showSource}\n onClick={() => {\n if (onAction) {\n onAction(action.command);\n } else {\n exec(action.command, action.commandArg);\n }\n }}\n title={action.label}\n className={cn(\n \"inline-flex h-8 w-8 items-center justify-center rounded-md text-sm transition-colors\",\n action.active\n ? \"bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]\"\n : \"hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]\",\n showSource && \"cursor-not-allowed opacity-40 hover:bg-transparent\",\n )}\n >\n {action.icon}\n </button>\n ))}\n {showSourceToggle && <EditorSourceToggle className=\"ml-auto\" />}\n {hasExtra && (\n <div\n data-react-fancy-editor-toolbar-extra=\"\"\n className={cn(\"flex items-center gap-0.5\", !showSourceToggle && \"ml-auto\")}\n >\n {toolbarExtra}\n </div>\n )}\n {showFullScreenToggle && (\n <EditorFullScreenToggle className={showSourceToggle || hasExtra ? undefined : \"ml-auto\"} />\n )}\n </>\n )}\n </div>\n );\n}\n\nEditorToolbar.displayName = \"EditorToolbar\";\n","export function EditorToolbarSeparator() {\n return (\n <span\n data-react-fancy-editor-toolbar-separator=\"\"\n className=\"mx-1 h-5 w-px bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]\"\n />\n );\n}\n\nEditorToolbarSeparator.displayName = \"EditorToolbarSeparator\";\n","import { useEffect, useMemo, useRef } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { CodeView } from \"../CodeView\";\nimport { useEditor } from \"./Editor.context\";\nimport { proseClasses, extensionEditorClasses } from \"./editor.utils\";\nimport type { EditorContentProps } from \"./Editor.types\";\n\nexport function EditorContent({ className, maxHeight: maxHeightProp, sourceClassName }: EditorContentProps) {\n const {\n contentRef,\n lineSpacing,\n placeholder,\n extensions,\n outputFormat,\n showSource,\n _initialHtml,\n _onInput,\n _sourceValue,\n _onSourceInput,\n fullScreen,\n } = useEditor();\n // Full screen means \"fill the viewport\": a height cap would leave the bottom\n // of the screen empty under a scrolled box.\n const maxHeight = fullScreen ? undefined : maxHeightProp;\n // Tracks which seed HTML is currently in the contentEditable. Seeding on the\n // seed VALUE (not a one-shot boolean) re-seeds when the source view rewrites\n // `value`, while still never re-seeding on plain keystrokes — typing leaves\n // `_initialHtml` untouched, so `seededHtml.current === _initialHtml` holds.\n const seededHtml = useRef<string | null>(null);\n\n useEffect(() => {\n // Source view replaces the contentEditable in the tree, so it unmounts.\n // Forget what we seeded — the div that returns is a fresh, empty one and\n // must always be re-seeded, even if `value` was left untouched.\n if (showSource) {\n seededHtml.current = null;\n return;\n }\n const el = contentRef.current;\n if (el && seededHtml.current !== _initialHtml) {\n el.innerHTML = _initialHtml;\n seededHtml.current = _initialHtml;\n }\n }, [contentRef, _initialHtml, showSource]);\n\n const extClasses = useMemo(\n () => extensionEditorClasses(extensions),\n [extensions],\n );\n\n if (showSource) {\n // Raw-source view — a syntax-highlighted code surface (HTML highlighted;\n // markdown/plain un-highlighted), built on the shared fancy-file-commons\n // primitives. It's a DIRECT child of the flex-column editor root (no\n // wrapper — a nested flex box collapses the fill), so `flex-auto` makes it\n // fill the editor body when height-constrained and grow with content\n // (≥ minHeight) when not. `maxHeight` caps + scrolls.\n return (\n <CodeView\n value={_sourceValue}\n onChange={_onSourceInput}\n language={outputFormat === \"html\" ? \"html\" : \"plaintext\"}\n placeholder={placeholder}\n maxHeight={maxHeight}\n className={cn(\"min-h-0 flex-auto\", sourceClassName)}\n />\n );\n }\n\n return (\n <div\n ref={contentRef}\n contentEditable\n suppressContentEditableWarning\n data-react-fancy-editor-content=\"\"\n data-placeholder={placeholder}\n onInput={_onInput}\n style={{\n lineHeight: lineSpacing,\n maxHeight: maxHeight ? `${maxHeight}px` : undefined,\n }}\n className={cn(\n \"min-h-[120px] flex-auto overflow-y-auto px-4 py-3 text-sm outline-none\",\n \"focus:outline-none\",\n proseClasses,\n extClasses,\n \"empty:before:content-[attr(data-placeholder)] empty:before:text-zinc-400 empty:before:pointer-events-none\",\n className,\n )}\n />\n );\n}\n\nEditorContent.displayName = \"EditorContent\";\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { marked } from \"marked\";\nimport { cn } from \"../../utils/cn\";\nimport { sanitizeHtml } from \"../../utils/sanitize\";\nimport { useControllableState } from \"../../hooks\";\nimport { useFullScreen } from \"../../hooks/use-full-screen\";\nimport { FullScreenProvider } from \"../FullScreen/FullScreen.context\";\nimport { EditorContext } from \"./Editor.context\";\nimport { EditorToolbar } from \"./EditorToolbar\";\nimport { EditorToolbarSeparator } from \"./EditorToolbarSeparator\";\nimport { EditorSourceToggle } from \"./EditorSourceToggle\";\nimport { EditorContent } from \"./EditorContent\";\nimport { EditorFullScreenToggle } from \"./EditorFullScreenToggle\";\nimport { htmlToMarkdown, detectFormat, markdownToEditorHtml, type MarkdownSource } from \"./editor.utils\";\nimport { mergeExtensions } from \"../ContentRenderer/extensions\";\nimport { ContentRenderer } from \"../ContentRenderer/ContentRenderer\";\nimport { useFieldMode } from \"../inputs/mode/FieldMode.context\";\nimport type { EditorProps } from \"./Editor.types\";\n\ninterface Seed {\n html: string;\n /** Source map for source-preserving markdown output; null when there is none. */\n source: MarkdownSource | null;\n}\n\nfunction toHtml(\n value: string,\n outputFormat: \"html\" | \"markdown\",\n unsafe: boolean,\n valueFormat: \"markdown\" | \"html\" | \"auto\" = \"auto\",\n): Seed {\n if (!value) return { html: \"\", source: null };\n const sanitize = (html: string) => (unsafe ? html : sanitizeHtml(html));\n // An explicit valueFormat is authoritative — the sniff misclassifies\n // markdown that merely MENTIONS HTML-ish snippets (`<code>`, `<table`),\n // collapsing it into an innerHTML wall of text (#10).\n const format =\n valueFormat !== \"auto\"\n ? valueFormat\n : outputFormat === \"html\"\n ? \"html\"\n : detectFormat(value);\n if (format === \"html\") return { html: sanitize(value), source: null };\n // Markdown in, markdown out: tag each block with its source so untouched\n // blocks are emitted byte-identical.\n if (outputFormat === \"markdown\") return markdownToEditorHtml(value, sanitize);\n return { html: sanitize((marked.parse(value, { async: false }) as string).trim()), source: null };\n}\n\nfunction EditorRoot({\n children,\n className,\n value: controlledValue,\n defaultValue = \"\",\n onChange,\n outputFormat = \"html\",\n valueFormat = \"auto\",\n lineSpacing = 1.6,\n placeholder,\n mode: modeProp,\n extensions: instanceExtensions,\n unsafe = false,\n showSource: showSourceProp,\n defaultShowSource = false,\n onShowSourceChange,\n fullScreen: fullScreenProp,\n defaultFullScreen = false,\n onFullScreenChange,\n fullScreenMode = \"overlay\",\n}: EditorProps) {\n const contentRef = useRef<HTMLDivElement | null>(null);\n const [value, setValue] = useControllableState(controlledValue, defaultValue, onChange);\n const [showSource, setShowSource] = useControllableState(\n showSourceProp,\n defaultShowSource,\n onShowSourceChange,\n );\n // Full screen is the element itself going `position: fixed`, not a portal:\n // reparenting would remount the contentEditable and lose the caret, the\n // browser's undo stack and the seeded HTML.\n const fs = useFullScreen<HTMLDivElement>({\n fullScreen: fullScreenProp,\n defaultFullScreen,\n onFullScreenChange,\n mode: fullScreenMode,\n });\n // View/edit mode — same resolution as the inputs: prop → <Form> context → \"edit\".\n const mode = useFieldMode(modeProp);\n const isView = mode === \"view\";\n\n // The value this editor last emitted through `onChange`. An incoming `value`\n // equal to it is our own keystroke echoing back; anything else came from\n // OUTSIDE — an async load, a host reset, a form repopulating.\n const lastEmitted = useRef<string | null>(null);\n\n // Bumped only on an external `value` change, so the seed memo below recomputes\n // for those and stays put while the user types. Without this, `value` was\n // excluded from the deps to avoid re-seeding on keystrokes, which also meant a\n // value arriving after mount was silently dropped and the editor stayed blank\n // forever (#15).\n const [externalSeed, setExternalSeed] = useState(0);\n useEffect(() => {\n if (value !== lastEmitted.current) setExternalSeed((n) => n + 1);\n }, [value]);\n\n const seed = useMemo(\n () => toHtml(value, outputFormat, unsafe, valueFormat),\n // Seed the contentEditable from the LIVE value when (re)entering edit mode —\n // and when returning from the raw-source view, which may have rewritten the\n // value out from under the rich-text surface. `value` itself stays out of\n // the deps so typing never re-seeds (which would fight the caret);\n // `externalSeed` covers the externally-driven case instead.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [isView, outputFormat, unsafe, valueFormat, showSource, externalSeed],\n );\n const initialHtml = seed.html;\n const seedSource = seed.source;\n\n const extensions = useMemo(\n () => mergeExtensions(instanceExtensions),\n [instanceExtensions],\n );\n\n const getOutputValue = useCallback(\n (html: string): string => {\n return outputFormat === \"markdown\" ? htmlToMarkdown(html, seedSource) : html;\n },\n [outputFormat, seedSource],\n );\n\n const handleInput = useCallback(() => {\n const el = contentRef.current;\n if (!el) return;\n const next = getOutputValue(el.innerHTML);\n // Record what we emitted BEFORE publishing it, so the value coming back in\n // is recognised as our own and does not trigger a re-seed mid-keystroke.\n lastEmitted.current = next;\n setValue(next);\n }, [setValue, getOutputValue]);\n\n const exec = useCallback(\n (command: string, arg?: string) => {\n const el = contentRef.current;\n if (!el) return;\n el.focus();\n document.execCommand(command, false, arg);\n handleInput();\n },\n [handleInput],\n );\n\n const insertText = useCallback(\n (text: string) => {\n const el = contentRef.current;\n if (!el) return;\n el.focus();\n document.execCommand(\"insertText\", false, text);\n handleInput();\n },\n [handleInput],\n );\n\n // The source textarea edits `value` directly — it is already the canonical\n // source in `outputFormat` (html or markdown), so no conversion round-trip.\n const handleSourceInput = useCallback(\n (next: string) => {\n // Also an internal emission — record it so typing in the source view is\n // not mistaken for an external change on every keystroke.\n lastEmitted.current = next;\n setValue(next);\n },\n [setValue],\n );\n\n const wrapSelection = useCallback(\n (before: string, after: string) => {\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return;\n const range = sel.getRangeAt(0);\n const selectedText = range.toString();\n const el = contentRef.current;\n if (!el) return;\n el.focus();\n document.execCommand(\"insertText\", false, `${before}${selectedText}${after}`);\n handleInput();\n },\n [handleInput],\n );\n\n const contextValue = {\n contentRef,\n exec,\n insertText,\n wrapSelection,\n outputFormat,\n lineSpacing,\n placeholder,\n extensions,\n showSource,\n setShowSource,\n fullScreen: fs.fullScreen,\n setFullScreen: fs.setFullScreen,\n _initialHtml: initialHtml,\n _onInput: handleInput,\n _sourceValue: value,\n _onSourceInput: handleSourceInput,\n };\n\n // View mode — render the value read-only through ContentRenderer, matching the\n // editor's own format (markdown/html), instead of the editable toolbar+content\n // tree. Driven by `mode` / a `<Form mode=\"view\">`, exactly like the inputs.\n if (isView) {\n return (\n <FullScreenProvider value={fs}>\n <div\n {...fs.targetProps}\n data-react-fancy-editor=\"\"\n data-mode=\"view\"\n className={cn(\n \"overflow-hidden rounded-xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\",\n className,\n // Read-only content taller than the viewport must scroll in place.\n fs.fullScreen && \"overflow-auto\",\n )}\n >\n <ContentRenderer\n value={value}\n format={valueFormat !== \"auto\" ? valueFormat : outputFormat}\n lineSpacing={lineSpacing}\n extensions={instanceExtensions}\n unsafe={unsafe}\n className=\"px-4 py-3\"\n />\n </div>\n </FullScreenProvider>\n );\n }\n\n return (\n <EditorContext.Provider value={contextValue}>\n <FullScreenProvider value={fs}>\n <div\n {...fs.targetProps}\n data-react-fancy-editor=\"\"\n data-mode=\"edit\"\n className={cn(\n \"flex flex-col overflow-hidden rounded-xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\",\n className,\n )}\n >\n {children}\n </div>\n </FullScreenProvider>\n </EditorContext.Provider>\n );\n}\n\nconst ToolbarWithSeparator = Object.assign(EditorToolbar, {\n Separator: EditorToolbarSeparator,\n});\n\nexport const Editor = Object.assign(EditorRoot, {\n Toolbar: ToolbarWithSeparator,\n Content: EditorContent,\n SourceToggle: EditorSourceToggle,\n FullScreenToggle: EditorFullScreenToggle,\n});\n"]}
1
+ {"version":3,"sources":["../src/components/Editor/Editor.context.ts","../src/components/Editor/EditorSourceToggle.tsx","../src/components/Editor/EditorFullScreenToggle.tsx","../src/components/Editor/EditorToolbar.tsx","../src/components/Editor/EditorToolbarSeparator.tsx","../src/components/Editor/EditorContent.tsx","../src/components/Editor/Editor.tsx"],"names":["createContext","useContext","jsx","cn","FullScreenToggle","jsxs","Fragment","useRef","useEffect","useMemo","extensionEditorClasses","CodeView","proseClasses","sanitizeHtml","detectFormat","markdownToEditorHtml","marked","useControllableState","useFullScreen","useFieldMode","useState","mergeExtensions","useCallback","htmlToMarkdown","FullScreenProvider","ContentRenderer"],"mappings":"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","file":"chunk-4S5DFN5N.cjs","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { RefObject } from \"react\";\nimport type { RenderExtension } from \"../ContentRenderer/extensions\";\n\nexport interface EditorContextValue {\n contentRef: RefObject<HTMLDivElement | null>;\n exec: (command: string, arg?: string) => void;\n insertText: (text: string) => void;\n wrapSelection: (before: string, after: string) => void;\n outputFormat: \"html\" | \"markdown\";\n lineSpacing: number;\n placeholder?: string;\n /** Merged render extensions (global + instance). */\n extensions: RenderExtension[];\n /** Whether the raw-source view (textarea) is showing instead of the rich-text surface. */\n showSource: boolean;\n /** Toggle/set the raw-source view. */\n setShowSource: (showSource: boolean) => void;\n /** Whether the editor currently covers the viewport. */\n fullScreen: boolean;\n /** Enter/leave full screen. Goes through `onFullScreenChange` when controlled. */\n setFullScreen: (fullScreen: boolean) => void;\n /** Initial HTML content to load into the editor on mount */\n _initialHtml: string;\n /** @internal Called by EditorContent on input events */\n _onInput: () => void;\n /** @internal Raw source value (`value` in `outputFormat`), for the source textarea. */\n _sourceValue: string;\n /** @internal Commit an edit made directly in the source textarea. */\n _onSourceInput: (value: string) => void;\n}\n\nexport const EditorContext = createContext<EditorContextValue | null>(null);\n\nexport function useEditor(): EditorContextValue {\n const ctx = useContext(EditorContext);\n if (!ctx) {\n throw new Error(\"useEditor must be used within an <Editor> component\");\n }\n return ctx;\n}\n","import { cn } from \"../../utils/cn\";\nimport { useEditor } from \"./Editor.context\";\nimport type { EditorSourceToggleProps } from \"./Editor.types\";\n\n/**\n * Toolbar button that reveals the raw HTML/Markdown behind the editor. Reads\n * `showSource` / `setShowSource` from context, so it works in the default\n * toolbar and in any custom toolbar composed with `useEditor()`.\n */\nexport function EditorSourceToggle({\n className,\n icon = \"</>\",\n title = \"Source\",\n activeTitle = \"Rich text\",\n}: EditorSourceToggleProps) {\n const { showSource, setShowSource } = useEditor();\n\n return (\n <button\n type=\"button\"\n onClick={() => setShowSource(!showSource)}\n title={showSource ? activeTitle : title}\n aria-pressed={showSource}\n data-react-fancy-editor-source-toggle=\"\"\n className={cn(\n \"inline-flex h-8 items-center justify-center rounded-md px-2 font-mono text-xs transition-colors\",\n showSource\n ? \"bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]\"\n : \"hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]\",\n className,\n )}\n >\n {icon}\n </button>\n );\n}\n\nEditorSourceToggle.displayName = \"EditorSourceToggle\";\n","import { cn } from \"../../utils/cn\";\nimport { FullScreenToggle } from \"../FullScreen/FullScreenToggle\";\nimport type { EditorFullScreenToggleProps } from \"./Editor.types\";\n\n/**\n * Toolbar button that takes the editor full screen and back. The shared\n * `FullScreen.Toggle`, styled for the editor toolbar and carrying the editor's\n * own handle (`data-react-fancy-editor-fullscreen-toggle`) next to the shared\n * one (`data-react-fancy-fullscreen-toggle`).\n */\nexport function EditorFullScreenToggle({ className, enterIcon, exitIcon }: EditorFullScreenToggleProps) {\n return (\n <FullScreenToggle\n data-react-fancy-editor-fullscreen-toggle=\"\"\n enterIcon={enterIcon}\n exitIcon={exitIcon}\n className={cn(className)}\n />\n );\n}\n\nEditorFullScreenToggle.displayName = \"EditorFullScreenToggle\";\n","import { cn } from \"../../utils/cn\";\nimport { useEditor } from \"./Editor.context\";\nimport { EditorSourceToggle } from \"./EditorSourceToggle\";\nimport { EditorFullScreenToggle } from \"./EditorFullScreenToggle\";\nimport type { EditorToolbarProps } from \"./Editor.types\";\n\nconst DEFAULT_ACTIONS = [\n { icon: \"B\", label: \"Bold\", command: \"bold\" },\n { icon: \"I\", label: \"Italic\", command: \"italic\" },\n { icon: \"U\", label: \"Underline\", command: \"underline\" },\n { icon: \"S\", label: \"Strikethrough\", command: \"strikeThrough\" },\n];\n\nexport function EditorToolbar({\n actions = DEFAULT_ACTIONS,\n onAction,\n children,\n className,\n showSourceToggle = true,\n showFullScreenToggle = false,\n toolbarExtra,\n}: EditorToolbarProps) {\n const { exec, showSource } = useEditor();\n // `null` / `false` / `undefined` render nothing, so they must not add a\n // wrapper either: an unused slot leaves the toolbar byte-identical.\n const hasExtra = toolbarExtra !== undefined && toolbarExtra !== null && toolbarExtra !== false;\n\n return (\n <div\n data-react-fancy-editor-toolbar=\"\"\n className={cn(\n \"flex shrink-0 items-center gap-0.5 border-b border-[color:var(--fancy-border,var(--color-zinc-200))] px-2 py-1.5 dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))]\",\n className,\n )}\n >\n {children ?? (\n <>\n {actions.map((action) => (\n <button\n key={`${action.command}-${action.commandArg ?? \"\"}`}\n type=\"button\"\n // Rich-text commands don't apply to the raw-source textarea.\n disabled={showSource}\n onClick={() => {\n if (onAction) {\n onAction(action.command);\n } else {\n exec(action.command, action.commandArg);\n }\n }}\n title={action.label}\n className={cn(\n \"inline-flex h-8 w-8 items-center justify-center rounded-md text-sm transition-colors\",\n action.active\n ? \"bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]\"\n : \"hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]\",\n showSource && \"cursor-not-allowed opacity-40 hover:bg-transparent\",\n )}\n >\n {action.icon}\n </button>\n ))}\n {showSourceToggle && <EditorSourceToggle className=\"ml-auto\" />}\n {hasExtra && (\n <div\n data-react-fancy-editor-toolbar-extra=\"\"\n className={cn(\"flex items-center gap-0.5\", !showSourceToggle && \"ml-auto\")}\n >\n {toolbarExtra}\n </div>\n )}\n {showFullScreenToggle && (\n <EditorFullScreenToggle className={showSourceToggle || hasExtra ? undefined : \"ml-auto\"} />\n )}\n </>\n )}\n </div>\n );\n}\n\nEditorToolbar.displayName = \"EditorToolbar\";\n","export function EditorToolbarSeparator() {\n return (\n <span\n data-react-fancy-editor-toolbar-separator=\"\"\n className=\"mx-1 h-5 w-px bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]\"\n />\n );\n}\n\nEditorToolbarSeparator.displayName = \"EditorToolbarSeparator\";\n","import { useEffect, useMemo, useRef } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { CodeView } from \"../CodeView\";\nimport { useEditor } from \"./Editor.context\";\nimport { proseClasses, extensionEditorClasses } from \"./editor.utils\";\nimport type { EditorContentProps } from \"./Editor.types\";\n\nexport function EditorContent({ className, maxHeight: maxHeightProp, sourceClassName }: EditorContentProps) {\n const {\n contentRef,\n lineSpacing,\n placeholder,\n extensions,\n outputFormat,\n showSource,\n _initialHtml,\n _onInput,\n _sourceValue,\n _onSourceInput,\n fullScreen,\n } = useEditor();\n // Full screen means \"fill the viewport\": a height cap would leave the bottom\n // of the screen empty under a scrolled box.\n const maxHeight = fullScreen ? undefined : maxHeightProp;\n // Tracks which seed HTML is currently in the contentEditable. Seeding on the\n // seed VALUE (not a one-shot boolean) re-seeds when the source view rewrites\n // `value`, while still never re-seeding on plain keystrokes — typing leaves\n // `_initialHtml` untouched, so `seededHtml.current === _initialHtml` holds.\n const seededHtml = useRef<string | null>(null);\n\n useEffect(() => {\n // Source view replaces the contentEditable in the tree, so it unmounts.\n // Forget what we seeded — the div that returns is a fresh, empty one and\n // must always be re-seeded, even if `value` was left untouched.\n if (showSource) {\n seededHtml.current = null;\n return;\n }\n const el = contentRef.current;\n if (el && seededHtml.current !== _initialHtml) {\n el.innerHTML = _initialHtml;\n seededHtml.current = _initialHtml;\n }\n }, [contentRef, _initialHtml, showSource]);\n\n const extClasses = useMemo(\n () => extensionEditorClasses(extensions),\n [extensions],\n );\n\n if (showSource) {\n // Raw-source view — a syntax-highlighted code surface (HTML highlighted;\n // markdown/plain un-highlighted), built on the shared fancy-file-commons\n // primitives. It's a DIRECT child of the flex-column editor root (no\n // wrapper — a nested flex box collapses the fill), so `flex-auto` makes it\n // fill the editor body when height-constrained and grow with content\n // (≥ minHeight) when not. `maxHeight` caps + scrolls.\n return (\n <CodeView\n value={_sourceValue}\n onChange={_onSourceInput}\n language={outputFormat === \"html\" ? \"html\" : \"plaintext\"}\n placeholder={placeholder}\n maxHeight={maxHeight}\n className={cn(\"min-h-0 flex-auto\", sourceClassName)}\n />\n );\n }\n\n return (\n <div\n ref={contentRef}\n contentEditable\n suppressContentEditableWarning\n data-react-fancy-editor-content=\"\"\n data-placeholder={placeholder}\n onInput={_onInput}\n style={{\n lineHeight: lineSpacing,\n maxHeight: maxHeight ? `${maxHeight}px` : undefined,\n }}\n className={cn(\n \"min-h-[120px] flex-auto overflow-y-auto px-4 py-3 text-sm outline-none\",\n \"focus:outline-none\",\n proseClasses,\n extClasses,\n \"empty:before:content-[attr(data-placeholder)] empty:before:text-zinc-400 empty:before:pointer-events-none\",\n className,\n )}\n />\n );\n}\n\nEditorContent.displayName = \"EditorContent\";\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { marked } from \"marked\";\nimport { cn } from \"../../utils/cn\";\nimport { sanitizeHtml } from \"../../utils/sanitize\";\nimport { useControllableState } from \"../../hooks\";\nimport { useFullScreen } from \"../../hooks/use-full-screen\";\nimport { FullScreenProvider } from \"../FullScreen/FullScreen.context\";\nimport { EditorContext } from \"./Editor.context\";\nimport { EditorToolbar } from \"./EditorToolbar\";\nimport { EditorToolbarSeparator } from \"./EditorToolbarSeparator\";\nimport { EditorSourceToggle } from \"./EditorSourceToggle\";\nimport { EditorContent } from \"./EditorContent\";\nimport { EditorFullScreenToggle } from \"./EditorFullScreenToggle\";\nimport { htmlToMarkdown, detectFormat, markdownToEditorHtml, type MarkdownSource } from \"./editor.utils\";\nimport { mergeExtensions } from \"../ContentRenderer/extensions\";\nimport { ContentRenderer } from \"../ContentRenderer/ContentRenderer\";\nimport { useFieldMode } from \"../inputs/mode/FieldMode.context\";\nimport type { EditorProps } from \"./Editor.types\";\n\ninterface Seed {\n html: string;\n /** Source map for source-preserving markdown output; null when there is none. */\n source: MarkdownSource | null;\n}\n\nfunction toHtml(\n value: string,\n outputFormat: \"html\" | \"markdown\",\n unsafe: boolean,\n valueFormat: \"markdown\" | \"html\" | \"auto\" = \"auto\",\n): Seed {\n if (!value) return { html: \"\", source: null };\n const sanitize = (html: string) => (unsafe ? html : sanitizeHtml(html));\n // An explicit valueFormat is authoritative — the sniff misclassifies\n // markdown that merely MENTIONS HTML-ish snippets (`<code>`, `<table`),\n // collapsing it into an innerHTML wall of text (#10).\n const format =\n valueFormat !== \"auto\"\n ? valueFormat\n : outputFormat === \"html\"\n ? \"html\"\n : detectFormat(value);\n if (format === \"html\") return { html: sanitize(value), source: null };\n // Markdown in, markdown out: tag each block with its source so untouched\n // blocks are emitted byte-identical.\n if (outputFormat === \"markdown\") return markdownToEditorHtml(value, sanitize);\n return { html: sanitize((marked.parse(value, { async: false }) as string).trim()), source: null };\n}\n\nfunction EditorRoot({\n children,\n className,\n value: controlledValue,\n defaultValue = \"\",\n onChange,\n outputFormat = \"html\",\n valueFormat = \"auto\",\n lineSpacing = 1.6,\n placeholder,\n mode: modeProp,\n extensions: instanceExtensions,\n unsafe = false,\n showSource: showSourceProp,\n defaultShowSource = false,\n onShowSourceChange,\n fullScreen: fullScreenProp,\n defaultFullScreen = false,\n onFullScreenChange,\n fullScreenMode = \"overlay\",\n}: EditorProps) {\n const contentRef = useRef<HTMLDivElement | null>(null);\n const [value, setValue] = useControllableState(controlledValue, defaultValue, onChange);\n const [showSource, setShowSource] = useControllableState(\n showSourceProp,\n defaultShowSource,\n onShowSourceChange,\n );\n // Full screen is the element itself going `position: fixed`, not a portal:\n // reparenting would remount the contentEditable and lose the caret, the\n // browser's undo stack and the seeded HTML.\n const fs = useFullScreen<HTMLDivElement>({\n fullScreen: fullScreenProp,\n defaultFullScreen,\n onFullScreenChange,\n mode: fullScreenMode,\n });\n // View/edit mode — same resolution as the inputs: prop → <Form> context → \"edit\".\n const mode = useFieldMode(modeProp);\n const isView = mode === \"view\";\n\n // The value this editor last emitted through `onChange`. An incoming `value`\n // equal to it is our own keystroke echoing back; anything else came from\n // OUTSIDE — an async load, a host reset, a form repopulating.\n const lastEmitted = useRef<string | null>(null);\n\n // Bumped only on an external `value` change, so the seed memo below recomputes\n // for those and stays put while the user types. Without this, `value` was\n // excluded from the deps to avoid re-seeding on keystrokes, which also meant a\n // value arriving after mount was silently dropped and the editor stayed blank\n // forever (#15).\n const [externalSeed, setExternalSeed] = useState(0);\n useEffect(() => {\n if (value !== lastEmitted.current) setExternalSeed((n) => n + 1);\n }, [value]);\n\n const seed = useMemo(\n () => toHtml(value, outputFormat, unsafe, valueFormat),\n // Seed the contentEditable from the LIVE value when (re)entering edit mode —\n // and when returning from the raw-source view, which may have rewritten the\n // value out from under the rich-text surface. `value` itself stays out of\n // the deps so typing never re-seeds (which would fight the caret);\n // `externalSeed` covers the externally-driven case instead.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [isView, outputFormat, unsafe, valueFormat, showSource, externalSeed],\n );\n const initialHtml = seed.html;\n const seedSource = seed.source;\n\n const extensions = useMemo(\n () => mergeExtensions(instanceExtensions),\n [instanceExtensions],\n );\n\n const getOutputValue = useCallback(\n (html: string): string => {\n return outputFormat === \"markdown\" ? htmlToMarkdown(html, seedSource) : html;\n },\n [outputFormat, seedSource],\n );\n\n const handleInput = useCallback(() => {\n const el = contentRef.current;\n if (!el) return;\n const next = getOutputValue(el.innerHTML);\n // Record what we emitted BEFORE publishing it, so the value coming back in\n // is recognised as our own and does not trigger a re-seed mid-keystroke.\n lastEmitted.current = next;\n setValue(next);\n }, [setValue, getOutputValue]);\n\n const exec = useCallback(\n (command: string, arg?: string) => {\n const el = contentRef.current;\n if (!el) return;\n el.focus();\n document.execCommand(command, false, arg);\n handleInput();\n },\n [handleInput],\n );\n\n const insertText = useCallback(\n (text: string) => {\n const el = contentRef.current;\n if (!el) return;\n el.focus();\n document.execCommand(\"insertText\", false, text);\n handleInput();\n },\n [handleInput],\n );\n\n // The source textarea edits `value` directly — it is already the canonical\n // source in `outputFormat` (html or markdown), so no conversion round-trip.\n const handleSourceInput = useCallback(\n (next: string) => {\n // Also an internal emission — record it so typing in the source view is\n // not mistaken for an external change on every keystroke.\n lastEmitted.current = next;\n setValue(next);\n },\n [setValue],\n );\n\n const wrapSelection = useCallback(\n (before: string, after: string) => {\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return;\n const range = sel.getRangeAt(0);\n const selectedText = range.toString();\n const el = contentRef.current;\n if (!el) return;\n el.focus();\n document.execCommand(\"insertText\", false, `${before}${selectedText}${after}`);\n handleInput();\n },\n [handleInput],\n );\n\n const contextValue = {\n contentRef,\n exec,\n insertText,\n wrapSelection,\n outputFormat,\n lineSpacing,\n placeholder,\n extensions,\n showSource,\n setShowSource,\n fullScreen: fs.fullScreen,\n setFullScreen: fs.setFullScreen,\n _initialHtml: initialHtml,\n _onInput: handleInput,\n _sourceValue: value,\n _onSourceInput: handleSourceInput,\n };\n\n // View mode — render the value read-only through ContentRenderer, matching the\n // editor's own format (markdown/html), instead of the editable toolbar+content\n // tree. Driven by `mode` / a `<Form mode=\"view\">`, exactly like the inputs.\n if (isView) {\n return (\n <FullScreenProvider value={fs}>\n <div\n {...fs.targetProps}\n data-react-fancy-editor=\"\"\n data-mode=\"view\"\n className={cn(\n \"overflow-hidden rounded-xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\",\n className,\n // Read-only content taller than the viewport must scroll in place.\n fs.fullScreen && \"overflow-auto\",\n )}\n >\n <ContentRenderer\n value={value}\n format={valueFormat !== \"auto\" ? valueFormat : outputFormat}\n lineSpacing={lineSpacing}\n extensions={instanceExtensions}\n unsafe={unsafe}\n className=\"px-4 py-3\"\n />\n </div>\n </FullScreenProvider>\n );\n }\n\n return (\n <EditorContext.Provider value={contextValue}>\n <FullScreenProvider value={fs}>\n <div\n {...fs.targetProps}\n data-react-fancy-editor=\"\"\n data-mode=\"edit\"\n className={cn(\n \"flex flex-col overflow-hidden rounded-xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\",\n className,\n )}\n >\n {children}\n </div>\n </FullScreenProvider>\n </EditorContext.Provider>\n );\n}\n\nconst ToolbarWithSeparator = Object.assign(EditorToolbar, {\n Separator: EditorToolbarSeparator,\n});\n\nexport const Editor = Object.assign(EditorRoot, {\n Toolbar: ToolbarWithSeparator,\n Content: EditorContent,\n SourceToggle: EditorSourceToggle,\n FullScreenToggle: EditorFullScreenToggle,\n});\n"]}