@wrrdev/fenix-ui 1.1.13 → 1.1.15

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 (161) hide show
  1. package/README.md +6 -5
  2. package/dist/components/autocomplete/autocomplete.d.ts +11 -5
  3. package/dist/components/autocomplete/autocomplete.d.ts.map +1 -1
  4. package/dist/components/autocomplete/autocomplete.js +18 -2
  5. package/dist/components/autocomplete/autocomplete.js.map +1 -1
  6. package/dist/components/button/button.d.ts +15 -0
  7. package/dist/components/button/button.d.ts.map +1 -1
  8. package/dist/components/button/button.js +20 -1
  9. package/dist/components/button/button.js.map +1 -1
  10. package/dist/components/checkbox/checkbox.d.ts +20 -4
  11. package/dist/components/checkbox/checkbox.d.ts.map +1 -1
  12. package/dist/components/checkbox/checkbox.js +39 -2
  13. package/dist/components/checkbox/checkbox.js.map +1 -1
  14. package/dist/components/datepicker/datepicker.d.ts +16 -2
  15. package/dist/components/datepicker/datepicker.d.ts.map +1 -1
  16. package/dist/components/datepicker/datepicker.js +46 -2
  17. package/dist/components/datepicker/datepicker.js.map +1 -1
  18. package/dist/components/editor/commands.d.ts +27 -0
  19. package/dist/components/editor/commands.d.ts.map +1 -0
  20. package/dist/components/editor/commands.js +235 -0
  21. package/dist/components/editor/commands.js.map +1 -0
  22. package/dist/components/editor/editor.d.ts +83 -0
  23. package/dist/components/editor/editor.d.ts.map +1 -0
  24. package/dist/components/editor/editor.js +1951 -0
  25. package/dist/components/editor/editor.js.map +1 -0
  26. package/dist/components/editor/index.d.ts +12 -0
  27. package/dist/components/editor/index.d.ts.map +1 -0
  28. package/dist/components/editor/index.js +7 -0
  29. package/dist/components/editor/index.js.map +1 -0
  30. package/dist/components/editor/normalize-html.d.ts +33 -0
  31. package/dist/components/editor/normalize-html.d.ts.map +1 -0
  32. package/dist/components/editor/normalize-html.js +316 -0
  33. package/dist/components/editor/normalize-html.js.map +1 -0
  34. package/dist/components/editor/sanitize-html.d.ts +37 -0
  35. package/dist/components/editor/sanitize-html.d.ts.map +1 -0
  36. package/dist/components/editor/sanitize-html.js +247 -0
  37. package/dist/components/editor/sanitize-html.js.map +1 -0
  38. package/dist/components/editor/schema.d.ts +36 -0
  39. package/dist/components/editor/schema.d.ts.map +1 -0
  40. package/dist/components/editor/schema.js +232 -0
  41. package/dist/components/editor/schema.js.map +1 -0
  42. package/dist/components/editor/selection.d.ts +22 -0
  43. package/dist/components/editor/selection.d.ts.map +1 -0
  44. package/dist/components/editor/selection.js +97 -0
  45. package/dist/components/editor/selection.js.map +1 -0
  46. package/dist/components/editor/styles.d.ts +10 -0
  47. package/dist/components/editor/styles.d.ts.map +1 -0
  48. package/dist/components/editor/styles.js +446 -0
  49. package/dist/components/editor/styles.js.map +1 -0
  50. package/dist/components/editor/table-utils.d.ts +33 -0
  51. package/dist/components/editor/table-utils.d.ts.map +1 -0
  52. package/dist/components/editor/table-utils.js +150 -0
  53. package/dist/components/editor/table-utils.js.map +1 -0
  54. package/dist/components/fileupload/fileupload.d.ts +10 -2
  55. package/dist/components/fileupload/fileupload.d.ts.map +1 -1
  56. package/dist/components/fileupload/fileupload.js +17 -2
  57. package/dist/components/fileupload/fileupload.js.map +1 -1
  58. package/dist/components/floatlabel/floatlabel.d.ts +9 -3
  59. package/dist/components/floatlabel/floatlabel.d.ts.map +1 -1
  60. package/dist/components/floatlabel/floatlabel.js +22 -7
  61. package/dist/components/floatlabel/floatlabel.js.map +1 -1
  62. package/dist/components/input/input.d.ts +18 -7
  63. package/dist/components/input/input.d.ts.map +1 -1
  64. package/dist/components/input/input.js +33 -3
  65. package/dist/components/input/input.js.map +1 -1
  66. package/dist/components/knob/knob.d.ts +7 -3
  67. package/dist/components/knob/knob.d.ts.map +1 -1
  68. package/dist/components/knob/knob.js +4 -2
  69. package/dist/components/knob/knob.js.map +1 -1
  70. package/dist/components/multiselect/multiselect.d.ts +13 -3
  71. package/dist/components/multiselect/multiselect.d.ts.map +1 -1
  72. package/dist/components/multiselect/multiselect.js +27 -2
  73. package/dist/components/multiselect/multiselect.js.map +1 -1
  74. package/dist/components/radio/radio.d.ts +27 -4
  75. package/dist/components/radio/radio.d.ts.map +1 -1
  76. package/dist/components/radio/radio.js +67 -8
  77. package/dist/components/radio/radio.js.map +1 -1
  78. package/dist/components/rating/rating.d.ts +8 -4
  79. package/dist/components/rating/rating.d.ts.map +1 -1
  80. package/dist/components/rating/rating.js +5 -2
  81. package/dist/components/rating/rating.js.map +1 -1
  82. package/dist/components/select/select.d.ts +9 -3
  83. package/dist/components/select/select.d.ts.map +1 -1
  84. package/dist/components/select/select.js +11 -2
  85. package/dist/components/select/select.js.map +1 -1
  86. package/dist/components/slider/slider.d.ts +12 -6
  87. package/dist/components/slider/slider.d.ts.map +1 -1
  88. package/dist/components/slider/slider.js +10 -2
  89. package/dist/components/slider/slider.js.map +1 -1
  90. package/dist/components/switch/switch.d.ts +20 -4
  91. package/dist/components/switch/switch.d.ts.map +1 -1
  92. package/dist/components/switch/switch.js +40 -2
  93. package/dist/components/switch/switch.js.map +1 -1
  94. package/dist/components/textarea/textarea.d.ts +12 -5
  95. package/dist/components/textarea/textarea.d.ts.map +1 -1
  96. package/dist/components/textarea/textarea.js +12 -2
  97. package/dist/components/textarea/textarea.js.map +1 -1
  98. package/dist/components/toast/toast.d.ts +27 -15
  99. package/dist/components/toast/toast.d.ts.map +1 -1
  100. package/dist/components/toast/toast.js +205 -32
  101. package/dist/components/toast/toast.js.map +1 -1
  102. package/dist/components/toggle-button-group/toggle-button-group.d.ts +13 -5
  103. package/dist/components/toggle-button-group/toggle-button-group.d.ts.map +1 -1
  104. package/dist/components/toggle-button-group/toggle-button-group.js +19 -2
  105. package/dist/components/toggle-button-group/toggle-button-group.js.map +1 -1
  106. package/dist/core/base.d.ts +7 -0
  107. package/dist/core/base.d.ts.map +1 -1
  108. package/dist/core/base.js +4 -1
  109. package/dist/core/base.js.map +1 -1
  110. package/dist/core/form-element.d.ts +81 -0
  111. package/dist/core/form-element.d.ts.map +1 -0
  112. package/dist/core/form-element.js +129 -0
  113. package/dist/core/form-element.js.map +1 -0
  114. package/dist/core/jsx.d.ts +25 -1
  115. package/dist/core/jsx.d.ts.map +1 -1
  116. package/dist/core/jsx.js.map +1 -1
  117. package/dist/core/presets.d.ts +8 -3
  118. package/dist/core/presets.d.ts.map +1 -1
  119. package/dist/core/presets.js +6 -17
  120. package/dist/core/presets.js.map +1 -1
  121. package/dist/core/types.d.ts +4 -0
  122. package/dist/core/types.d.ts.map +1 -1
  123. package/dist/core/vue.d.ts +3 -1
  124. package/dist/core/vue.d.ts.map +1 -1
  125. package/dist/core/vue.js.map +1 -1
  126. package/dist/docs/app.d.ts.map +1 -1
  127. package/dist/docs/componentes/autocomplete.doc.d.ts.map +1 -1
  128. package/dist/docs/componentes/checkbox.doc.d.ts.map +1 -1
  129. package/dist/docs/componentes/datepicker.doc.d.ts.map +1 -1
  130. package/dist/docs/componentes/editor.doc.d.ts +6 -0
  131. package/dist/docs/componentes/editor.doc.d.ts.map +1 -0
  132. package/dist/docs/componentes/fileupload.doc.d.ts.map +1 -1
  133. package/dist/docs/componentes/index.d.ts.map +1 -1
  134. package/dist/docs/componentes/input.doc.d.ts.map +1 -1
  135. package/dist/docs/componentes/knob.doc.d.ts.map +1 -1
  136. package/dist/docs/componentes/multiselect.doc.d.ts.map +1 -1
  137. package/dist/docs/componentes/rating.doc.d.ts.map +1 -1
  138. package/dist/docs/componentes/select.doc.d.ts.map +1 -1
  139. package/dist/docs/componentes/slider.doc.d.ts.map +1 -1
  140. package/dist/docs/componentes/switch.doc.d.ts.map +1 -1
  141. package/dist/docs/componentes/textarea.doc.d.ts.map +1 -1
  142. package/dist/docs/componentes/toast.doc.d.ts.map +1 -1
  143. package/dist/docs/componentes/toggle-button-group.doc.d.ts.map +1 -1
  144. package/dist/docs/frameworks.d.ts +75 -0
  145. package/dist/docs/frameworks.d.ts.map +1 -0
  146. package/dist/docs/ui/code.d.ts +4 -0
  147. package/dist/docs/ui/code.d.ts.map +1 -1
  148. package/dist/fenix-ui.umd.min.js +786 -254
  149. package/dist/fenix-ui2.esm.js.map +1 -1
  150. package/dist/fenix-ui4.esm.js +4396 -172
  151. package/dist/fenix-ui4.esm.js.map +1 -1
  152. package/dist/fenix-ui5.esm.js +1 -0
  153. package/dist/fenix-ui5.esm.js.map +1 -1
  154. package/dist/index.d.ts +2 -0
  155. package/dist/index.d.ts.map +1 -1
  156. package/dist/index.js +18 -1
  157. package/dist/index.js.map +1 -1
  158. package/dist/plugins/auto-import.d.ts.map +1 -1
  159. package/dist/plugins/auto-import.js +1 -0
  160. package/dist/plugins/auto-import.js.map +1 -1
  161. package/package.json +5 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitize-html.js","sources":["../../../src/components/editor/sanitize-html.ts"],"sourcesContent":["/**\n * Sanitizador de HTML do <fx-editor>.\n *\n * Estratégia: allowlist + DOMParser inerte + serialização única.\n * Nada de regex para decidir o que é seguro: a árvore é percorrida e cada\n * nó/atributo é validado individualmente.\n *\n * O resultado é DEFENSE IN DEPTH. O HTML salvo também deve ser sanitizado\n * no backend antes de ser renderizado em outro contexto.\n */\n\nimport {\n ALLOWED_CLASS_RE,\n ALLOWED_EMBED_HOSTS,\n ALLOWED_STYLE_PROPS,\n ALLOWED_TAGS,\n DEFAULT_MAX_DATA_IMAGE_BYTES,\n DEFAULT_MAX_DATA_VIDEO_BYTES,\n DROP_CONTENT_TAGS,\n FORBIDDEN_ATTRS,\n GLOBAL_ATTRS,\n SAFE_IMAGE_DATA_RE,\n SAFE_LINK_PROTOCOLS,\n SAFE_MEDIA_PROTOCOLS,\n TAG_ATTRS,\n} from './schema';\n\nexport interface SanitizeOptions {\n allowBase64Images?: boolean;\n allowBase64Videos?: boolean;\n allowEmbeds?: boolean;\n maxDataImageBytes?: number;\n maxDataVideoBytes?: number;\n}\n\ntype Options = Required<SanitizeOptions>;\n\nconst DEFAULTS: Options = {\n allowBase64Images: true,\n allowBase64Videos: false,\n allowEmbeds: true,\n maxDataImageBytes: DEFAULT_MAX_DATA_IMAGE_BYTES,\n maxDataVideoBytes: DEFAULT_MAX_DATA_VIDEO_BYTES,\n};\n\nconst SAFE_DATA_VIDEO_RE = /^data:video\\/(?:mp4|webm);base64,[a-z0-9+/=\\s]+$/i;\n\n/** Remove caracteres de controle e normaliza espaços de uma URL. */\nfunction cleanRawUrl(value: string): string {\n return value.replace(/[\\u0000-\\u001F\\u007F\\s]+/g, '').trim();\n}\n\nfunction isRelativeUrl(value: string): boolean {\n return !value.startsWith('//') && /^(?:#|\\/|\\.{1,2}\\/)/.test(value);\n}\n\n/** Valida um link (href). Retorna '' quando não é seguro. */\nexport function sanitizeLinkUrl(value: string): string {\n const raw = cleanRawUrl(value);\n if (!raw) return '';\n if (isRelativeUrl(raw)) return raw;\n\n const lower = raw.toLowerCase();\n if (\n lower.startsWith('javascript:') ||\n lower.startsWith('vbscript:') ||\n lower.startsWith('data:') ||\n lower.startsWith('file:') ||\n lower.startsWith('blob:')\n ) {\n return '';\n }\n\n try {\n const url = new URL(raw);\n return SAFE_LINK_PROTOCOLS.has(url.protocol) ? raw : '';\n } catch {\n // Links relativos sem prefixo (`pagina.html`) são aceitos.\n return /^[a-z0-9][a-z0-9._~!$&'()*+,;=:@%/?#-]*$/i.test(raw) ? raw : '';\n }\n}\n\n/** Valida src/poster de mídia. */\nexport function sanitizeMediaUrl(value: string, tag: string, options: SanitizeOptions = {}): string {\n const opts = { ...DEFAULTS, ...options };\n const raw = cleanRawUrl(value);\n if (!raw) return '';\n\n if (raw.toLowerCase().startsWith('data:')) {\n if (tag === 'img' && opts.allowBase64Images) {\n return SAFE_IMAGE_DATA_RE.test(raw) && raw.length <= opts.maxDataImageBytes ? raw : '';\n }\n if ((tag === 'video' || tag === 'source') && opts.allowBase64Videos) {\n return SAFE_DATA_VIDEO_RE.test(raw) && raw.length <= opts.maxDataVideoBytes ? raw : '';\n }\n return '';\n }\n\n if (isRelativeUrl(raw)) return raw;\n\n const lower = raw.toLowerCase();\n if (\n lower.startsWith('javascript:') ||\n lower.startsWith('vbscript:') ||\n lower.startsWith('file:') ||\n lower.startsWith('blob:')\n ) {\n return '';\n }\n\n try {\n const url = new URL(raw);\n return SAFE_MEDIA_PROTOCOLS.has(url.protocol) ? raw : '';\n } catch {\n return '';\n }\n}\n\n/** Valida e canonicaliza um iframe de embed. */\nexport function sanitizeEmbedUrl(value: string): string {\n let raw = cleanRawUrl(value);\n if (!raw) return '';\n if (raw.startsWith('//')) raw = `https:${raw}`;\n\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n return '';\n }\n\n if (url.protocol !== 'https:') return '';\n if (!ALLOWED_EMBED_HOSTS.has(url.hostname.toLowerCase())) return '';\n\n const path = url.pathname.toLowerCase();\n const isValidPath =\n (url.hostname.includes('youtube') && path.startsWith('/embed/')) ||\n (url.hostname.includes('vimeo') && path.startsWith('/video/'));\n if (!isValidPath) return '';\n\n return url.toString();\n}\n\n/** Filtra o atributo style por propriedade e valor. */\nfunction sanitizeStyle(value: string): string {\n if (!value) return '';\n\n const probe = document.createElement('span');\n probe.setAttribute('style', value);\n const out: string[] = [];\n\n for (let i = 0; i < probe.style.length; i++) {\n const prop = probe.style.item(i);\n if (!ALLOWED_STYLE_PROPS.has(prop)) continue;\n const val = probe.style.getPropertyValue(prop).trim();\n if (!val) continue;\n if (/\\\\/.test(val)) continue;\n if (/url\\s*\\(|expression\\s*\\(|javascript\\s*:|@import|behavior\\s*:|-moz-binding/i.test(val)) continue;\n out.push(`${prop}: ${val}`);\n }\n\n return out.join('; ');\n}\n\nfunction sanitizeAttributes(el: Element, tag: string, opts: Options): void {\n for (const attr of Array.from(el.attributes)) {\n const name = attr.name.toLowerCase();\n const value = attr.value;\n\n if (FORBIDDEN_ATTRS.has(name) || name.startsWith('on')) {\n el.removeAttribute(attr.name);\n continue;\n }\n\n const allowed = GLOBAL_ATTRS.has(name) || TAG_ATTRS[tag]?.has(name);\n if (!allowed) {\n el.removeAttribute(attr.name);\n continue;\n }\n\n if (name === 'style') {\n const safe = sanitizeStyle(value);\n if (safe) el.setAttribute('style', safe);\n else el.removeAttribute('style');\n continue;\n }\n\n if (name === 'class') {\n const classes = value.split(/\\s+/).filter((c) => ALLOWED_CLASS_RE.test(c));\n if (classes.length) el.setAttribute('class', classes.join(' '));\n else el.removeAttribute('class');\n continue;\n }\n\n if (name === 'href') {\n const safe = sanitizeLinkUrl(value);\n if (safe) el.setAttribute('href', safe);\n else el.removeAttribute('href');\n continue;\n }\n\n if (name === 'src') {\n const safe = sanitizeMediaUrl(value, tag, opts);\n if (safe) el.setAttribute('src', safe);\n else el.removeAttribute('src');\n continue;\n }\n\n if (name === 'poster') {\n const safe = sanitizeMediaUrl(value, 'img', opts);\n if (safe) el.setAttribute('poster', safe);\n else el.removeAttribute('poster');\n continue;\n }\n\n if (name === 'target') {\n const safe = value === '_self' || value === '_blank' ? value : '_blank';\n el.setAttribute('target', safe);\n continue;\n }\n\n if (name === 'colspan' || name === 'rowspan' || name === 'span' || name === 'start') {\n const n = Number(value);\n if (Number.isInteger(n) && n > 0 && n <= 1000) el.setAttribute(name, String(n));\n else el.removeAttribute(name);\n continue;\n }\n\n if (name === 'width' || name === 'height') {\n if (/^\\d{1,4}(?:px|%)?$/i.test(value.trim())) el.setAttribute(name, value.trim());\n else el.removeAttribute(name);\n continue;\n }\n }\n\n if (tag === 'a') {\n const href = el.getAttribute('href');\n if (!href) {\n el.removeAttribute('target');\n el.removeAttribute('rel');\n } else if (el.getAttribute('target') === '_blank') {\n el.setAttribute('rel', 'noopener noreferrer');\n }\n }\n\n if (tag === 'iframe') {\n el.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-presentation');\n el.setAttribute('allowfullscreen', '');\n el.setAttribute('loading', 'lazy');\n el.setAttribute('referrerpolicy', 'strict-origin-when-cross-origin');\n }\n\n if (tag === 'img') {\n if (!el.hasAttribute('alt')) el.setAttribute('alt', '');\n el.setAttribute('loading', 'lazy');\n el.setAttribute('decoding', 'async');\n }\n\n if (tag === 'video') {\n el.setAttribute('controls', '');\n el.setAttribute('preload', 'metadata');\n }\n}\n\nfunction sanitizeIframe(el: Element): boolean {\n const src = el.getAttribute('src') ?? '';\n const safe = sanitizeEmbedUrl(src);\n if (!safe) return false;\n el.setAttribute('src', safe);\n return true;\n}\n\nfunction unwrap(el: Element, opts: Options): void {\n const parent = el.parentNode;\n if (!parent) {\n el.remove();\n return;\n }\n\n for (const child of Array.from(el.childNodes)) {\n sanitizeNode(child, opts);\n parent.insertBefore(child, el);\n }\n el.remove();\n}\n\nfunction sanitizeNode(node: Node, opts: Options): void {\n if (node.nodeType === Node.TEXT_NODE) return;\n if (node.nodeType === Node.COMMENT_NODE) {\n node.parentNode?.removeChild(node);\n return;\n }\n if (node.nodeType !== Node.ELEMENT_NODE) {\n node.parentNode?.removeChild(node);\n return;\n }\n\n const el = node as Element;\n const tag = el.tagName.toLowerCase();\n\n if (DROP_CONTENT_TAGS.has(tag)) {\n el.remove();\n return;\n }\n\n if (tag === 'iframe') {\n if (!opts.allowEmbeds || !sanitizeIframe(el)) el.remove();\n else sanitizeAttributes(el, tag, opts);\n return;\n }\n\n if (!ALLOWED_TAGS.has(tag)) {\n unwrap(el, opts);\n return;\n }\n\n sanitizeAttributes(el, tag, opts);\n\n if ((tag === 'img' || tag === 'video' || tag === 'source') && !el.hasAttribute('src')) {\n el.remove();\n return;\n }\n\n for (const child of Array.from(el.childNodes)) sanitizeNode(child, opts);\n}\n\n/**\n * Sanitiza um fragmento HTML e devolve o HTML seguro.\n *\n * @example\n * sanitizeHtml('<p onclick=\"x()\">Oi<script>alert(1)</script></p>')\n * // => '<p>Oi</p>'\n */\n/**\n * Sanitiza o HTML e devolve o `<body>` já filtrado, sem serializar.\n * Permite encadear a normalização no MESMO DOM (uma única parseada).\n */\nexport function sanitizeBody(html: string, options: SanitizeOptions = {}): HTMLElement | null {\n const opts: Options = { ...DEFAULTS, ...options };\n if (!html) return null;\n if (typeof DOMParser === 'undefined' || typeof document === 'undefined') return null;\n\n const doc = new DOMParser().parseFromString(\n `<!doctype html><html><body>${html}</body></html>`,\n 'text/html',\n );\n\n for (const child of Array.from(doc.body.childNodes)) sanitizeNode(child, opts);\n return doc.body;\n}\n\nexport function sanitizeHtml(html: string, options: SanitizeOptions = {}): string {\n const body = sanitizeBody(html, options);\n return body ? body.innerHTML : '';\n}\n\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Contrato de HTML do <fx-editor>.
3
+ *
4
+ * Este arquivo é a fonte única de verdade do que pode existir no HTML salvo.
5
+ * `sanitize-html.ts` usa estas listas para remover conteúdo perigoso e
6
+ * `normalize-html.ts` usa as mesmas listas para canonicalizar a estrutura.
7
+ */
8
+ /** Tags que podem permanecer no HTML final. */
9
+ export declare const ALLOWED_TAGS: Set<string>;
10
+ /** Tags cujo conteúdo deve ser descartado por inteiro (não basta remover a tag). */
11
+ export declare const DROP_CONTENT_TAGS: Set<string>;
12
+ /** Tags vazias (void) — não recebem fechamento. */
13
+ export declare const VOID_TAGS: Set<string>;
14
+ /** Atributos aceitos em qualquer tag permitida. */
15
+ export declare const GLOBAL_ATTRS: Set<string>;
16
+ /** Atributos aceitos por tag. */
17
+ export declare const TAG_ATTRS: Record<string, Set<string>>;
18
+ /** Propriedades CSS permitidas em `style` inline. */
19
+ export declare const ALLOWED_STYLE_PROPS: Set<string>;
20
+ /** Classes `fx-*` que podem ser preservadas no HTML salvo. */
21
+ export declare const ALLOWED_CLASS_RE: RegExp;
22
+ /** Hosts autorizados para embed de vídeo. */
23
+ export declare const ALLOWED_EMBED_HOSTS: Set<string>;
24
+ /** Protocolos permitidos em links. */
25
+ export declare const SAFE_LINK_PROTOCOLS: Set<string>;
26
+ /** Protocolos permitidos em mídia. */
27
+ export declare const SAFE_MEDIA_PROTOCOLS: Set<string>;
28
+ /** Data URL de imagem segura (SVG é bloqueado de propósito). */
29
+ export declare const SAFE_IMAGE_DATA_RE: RegExp;
30
+ /** Limite padrão para imagens em base64 (bytes aproximados do data URL). */
31
+ export declare const DEFAULT_MAX_DATA_IMAGE_BYTES: number;
32
+ /** Limite padrão para vídeo em base64 (bytes aproximados do data URL). */
33
+ export declare const DEFAULT_MAX_DATA_VIDEO_BYTES: number;
34
+ /** Atributos que nunca podem existir no HTML salvo. */
35
+ export declare const FORBIDDEN_ATTRS: Set<string>;
36
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/components/editor/schema.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,+CAA+C;AAC/C,eAAO,MAAM,YAAY,aAMvB,CAAC;AAEH,oFAAoF;AACpF,eAAO,MAAM,iBAAiB,aAI5B,CAAC;AAEH,mDAAmD;AACnD,eAAO,MAAM,SAAS,aAEpB,CAAC;AAEH,mDAAmD;AACnD,eAAO,MAAM,YAAY,aAEvB,CAAC;AAEH,iCAAiC;AACjC,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAkCjD,CAAC;AAEF,qDAAqD;AACrD,eAAO,MAAM,mBAAmB,aAS9B,CAAC;AAEH,8DAA8D;AAC9D,eAAO,MAAM,gBAAgB,QAAqB,CAAC;AAEnD,6CAA6C;AAC7C,eAAO,MAAM,mBAAmB,aAM9B,CAAC;AAEH,sCAAsC;AACtC,eAAO,MAAM,mBAAmB,aAA0D,CAAC;AAE3F,sCAAsC;AACtC,eAAO,MAAM,oBAAoB,aAAuC,CAAC;AAEzE,gEAAgE;AAChE,eAAO,MAAM,kBAAkB,QAAqE,CAAC;AAErG,4EAA4E;AAC5E,eAAO,MAAM,4BAA4B,QAAkB,CAAC;AAE5D,0EAA0E;AAC1E,eAAO,MAAM,4BAA4B,QAAkB,CAAC;AAE5D,uDAAuD;AACvD,eAAO,MAAM,eAAe,aAS1B,CAAC"}
@@ -0,0 +1,232 @@
1
+ const ALLOWED_TAGS = /* @__PURE__ */ new Set([
2
+ "p",
3
+ "br",
4
+ "strong",
5
+ "b",
6
+ "em",
7
+ "i",
8
+ "u",
9
+ "s",
10
+ "del",
11
+ "ins",
12
+ "sub",
13
+ "sup",
14
+ "code",
15
+ "pre",
16
+ "blockquote",
17
+ "h1",
18
+ "h2",
19
+ "h3",
20
+ "h4",
21
+ "h5",
22
+ "h6",
23
+ "ul",
24
+ "ol",
25
+ "li",
26
+ "a",
27
+ "img",
28
+ "figure",
29
+ "figcaption",
30
+ "video",
31
+ "source",
32
+ "iframe",
33
+ "table",
34
+ "thead",
35
+ "tbody",
36
+ "tfoot",
37
+ "tr",
38
+ "th",
39
+ "td",
40
+ "colgroup",
41
+ "col",
42
+ "hr",
43
+ "span",
44
+ "div",
45
+ "small",
46
+ "mark"
47
+ ]);
48
+ const DROP_CONTENT_TAGS = /* @__PURE__ */ new Set([
49
+ "script",
50
+ "style",
51
+ "template",
52
+ "noscript",
53
+ "svg",
54
+ "math",
55
+ "object",
56
+ "embed",
57
+ "form",
58
+ "input",
59
+ "button",
60
+ "textarea",
61
+ "select",
62
+ "option",
63
+ "meta",
64
+ "link",
65
+ "base",
66
+ "title",
67
+ "head",
68
+ "audio",
69
+ "canvas",
70
+ "map",
71
+ "area"
72
+ ]);
73
+ const GLOBAL_ATTRS = /* @__PURE__ */ new Set([
74
+ "title",
75
+ "dir",
76
+ "lang",
77
+ "role"
78
+ ]);
79
+ const TAG_ATTRS = {
80
+ a: /* @__PURE__ */ new Set(["href", "target", "rel", "download"]),
81
+ img: /* @__PURE__ */ new Set(["src", "alt", "width", "height", "loading", "decoding"]),
82
+ video: /* @__PURE__ */ new Set(["src", "poster", "controls", "preload", "width", "height", "muted", "loop", "playsinline"]),
83
+ source: /* @__PURE__ */ new Set(["src", "type"]),
84
+ iframe: /* @__PURE__ */ new Set([
85
+ "src",
86
+ "title",
87
+ "width",
88
+ "height",
89
+ "allow",
90
+ "allowfullscreen",
91
+ "sandbox",
92
+ "referrerpolicy",
93
+ "loading"
94
+ ]),
95
+ td: /* @__PURE__ */ new Set(["colspan", "rowspan", "scope", "style", "class", "data-align"]),
96
+ th: /* @__PURE__ */ new Set(["colspan", "rowspan", "scope", "style", "class", "data-align"]),
97
+ table: /* @__PURE__ */ new Set(["style", "class", "role", "data-cols", "data-stack"]),
98
+ col: /* @__PURE__ */ new Set(["span", "style", "width"]),
99
+ colgroup: /* @__PURE__ */ new Set(["span", "style"]),
100
+ figure: /* @__PURE__ */ new Set(["class", "style", "data-align", "data-cols"]),
101
+ figcaption: /* @__PURE__ */ new Set(["class", "style"]),
102
+ span: /* @__PURE__ */ new Set(["class", "style"]),
103
+ div: /* @__PURE__ */ new Set(["class", "style"]),
104
+ p: /* @__PURE__ */ new Set(["class", "style"]),
105
+ h1: /* @__PURE__ */ new Set(["class", "style"]),
106
+ h2: /* @__PURE__ */ new Set(["class", "style"]),
107
+ h3: /* @__PURE__ */ new Set(["class", "style"]),
108
+ h4: /* @__PURE__ */ new Set(["class", "style"]),
109
+ h5: /* @__PURE__ */ new Set(["class", "style"]),
110
+ h6: /* @__PURE__ */ new Set(["class", "style"]),
111
+ blockquote: /* @__PURE__ */ new Set(["class", "style"]),
112
+ pre: /* @__PURE__ */ new Set(["class", "style"]),
113
+ code: /* @__PURE__ */ new Set(["class"]),
114
+ li: /* @__PURE__ */ new Set(["class", "style", "value"]),
115
+ ol: /* @__PURE__ */ new Set(["class", "style", "start", "type"]),
116
+ ul: /* @__PURE__ */ new Set(["class", "style"]),
117
+ hr: /* @__PURE__ */ new Set(["class", "style"]),
118
+ small: /* @__PURE__ */ new Set(["class"]),
119
+ mark: /* @__PURE__ */ new Set(["class", "style"])
120
+ };
121
+ const ALLOWED_STYLE_PROPS = /* @__PURE__ */ new Set([
122
+ "color",
123
+ "background-color",
124
+ "text-align",
125
+ "font-size",
126
+ "font-family",
127
+ "font-weight",
128
+ "font-style",
129
+ "text-decoration",
130
+ "text-decoration-line",
131
+ "vertical-align",
132
+ "width",
133
+ "height",
134
+ "max-width",
135
+ "min-width",
136
+ "margin",
137
+ "margin-top",
138
+ "margin-right",
139
+ "margin-bottom",
140
+ "margin-left",
141
+ "padding",
142
+ "padding-top",
143
+ "padding-right",
144
+ "padding-bottom",
145
+ "padding-left",
146
+ "border",
147
+ "border-top",
148
+ "border-right",
149
+ "border-bottom",
150
+ "border-left",
151
+ "border-collapse",
152
+ "border-spacing",
153
+ "border-radius",
154
+ "float",
155
+ "display",
156
+ "line-height",
157
+ "white-space",
158
+ "word-break",
159
+ "overflow-wrap",
160
+ "list-style-type"
161
+ ]);
162
+ const ALLOWED_CLASS_RE = /^fx-[a-z0-9-]+$/i;
163
+ const ALLOWED_EMBED_HOSTS = /* @__PURE__ */ new Set([
164
+ "www.youtube.com",
165
+ "youtube.com",
166
+ "www.youtube-nocookie.com",
167
+ "youtube-nocookie.com",
168
+ "player.vimeo.com"
169
+ ]);
170
+ const SAFE_LINK_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:"]);
171
+ const SAFE_MEDIA_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
172
+ const SAFE_IMAGE_DATA_RE = /^data:image\/(?:png|jpe?g|gif|webp|avif);base64,[a-z0-9+/=\s]+$/i;
173
+ const DEFAULT_MAX_DATA_IMAGE_BYTES = 2 * 1024 * 1024;
174
+ const DEFAULT_MAX_DATA_VIDEO_BYTES = 4 * 1024 * 1024;
175
+ const FORBIDDEN_ATTRS = /* @__PURE__ */ new Set([
176
+ "contenteditable",
177
+ "spellcheck",
178
+ "tabindex",
179
+ "autofocus",
180
+ "id",
181
+ "onclick",
182
+ "ondblclick",
183
+ "onmousedown",
184
+ "onmouseup",
185
+ "onmouseover",
186
+ "onmouseout",
187
+ "onmousemove",
188
+ "onkeydown",
189
+ "onkeyup",
190
+ "onkeypress",
191
+ "onload",
192
+ "onerror",
193
+ "onfocus",
194
+ "onblur",
195
+ "oninput",
196
+ "onchange",
197
+ "onsubmit",
198
+ "onreset",
199
+ "onselect",
200
+ "ondrag",
201
+ "ondrop",
202
+ "onpaste",
203
+ "oncopy",
204
+ "oncut",
205
+ "onscroll",
206
+ "onwheel",
207
+ "onanimationstart",
208
+ "onanimationend",
209
+ "ontransitionend",
210
+ "formaction",
211
+ "srcdoc",
212
+ "xlink:href",
213
+ "xmlns",
214
+ "http-equiv",
215
+ "ping"
216
+ ]);
217
+ export {
218
+ ALLOWED_CLASS_RE,
219
+ ALLOWED_EMBED_HOSTS,
220
+ ALLOWED_STYLE_PROPS,
221
+ ALLOWED_TAGS,
222
+ DEFAULT_MAX_DATA_IMAGE_BYTES,
223
+ DEFAULT_MAX_DATA_VIDEO_BYTES,
224
+ DROP_CONTENT_TAGS,
225
+ FORBIDDEN_ATTRS,
226
+ GLOBAL_ATTRS,
227
+ SAFE_IMAGE_DATA_RE,
228
+ SAFE_LINK_PROTOCOLS,
229
+ SAFE_MEDIA_PROTOCOLS,
230
+ TAG_ATTRS
231
+ };
232
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sources":["../../../src/components/editor/schema.ts"],"sourcesContent":["/**\n * Contrato de HTML do <fx-editor>.\n *\n * Este arquivo é a fonte única de verdade do que pode existir no HTML salvo.\n * `sanitize-html.ts` usa estas listas para remover conteúdo perigoso e\n * `normalize-html.ts` usa as mesmas listas para canonicalizar a estrutura.\n */\n\n/** Tags que podem permanecer no HTML final. */\nexport const ALLOWED_TAGS = new Set<string>([\n 'p', 'br', 'strong', 'b', 'em', 'i', 'u', 's', 'del', 'ins', 'sub', 'sup',\n 'code', 'pre', 'blockquote', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',\n 'ul', 'ol', 'li', 'a', 'img', 'figure', 'figcaption', 'video', 'source',\n 'iframe', 'table', 'thead', 'tbody', 'tfoot', 'tr', 'th', 'td',\n 'colgroup', 'col', 'hr', 'span', 'div', 'small', 'mark',\n]);\n\n/** Tags cujo conteúdo deve ser descartado por inteiro (não basta remover a tag). */\nexport const DROP_CONTENT_TAGS = new Set<string>([\n 'script', 'style', 'template', 'noscript', 'svg', 'math', 'object', 'embed',\n 'form', 'input', 'button', 'textarea', 'select', 'option', 'meta', 'link',\n 'base', 'title', 'head', 'audio', 'canvas', 'map', 'area',\n]);\n\n/** Tags vazias (void) — não recebem fechamento. */\nexport const VOID_TAGS = new Set<string>([\n 'br', 'img', 'source', 'hr', 'col', 'input', 'meta', 'link', 'base',\n]);\n\n/** Atributos aceitos em qualquer tag permitida. */\nexport const GLOBAL_ATTRS = new Set<string>([\n 'title', 'dir', 'lang', 'role',\n]);\n\n/** Atributos aceitos por tag. */\nexport const TAG_ATTRS: Record<string, Set<string>> = {\n a: new Set(['href', 'target', 'rel', 'download']),\n img: new Set(['src', 'alt', 'width', 'height', 'loading', 'decoding']),\n video: new Set(['src', 'poster', 'controls', 'preload', 'width', 'height', 'muted', 'loop', 'playsinline']),\n source: new Set(['src', 'type']),\n iframe: new Set([\n 'src', 'title', 'width', 'height', 'allow', 'allowfullscreen', 'sandbox',\n 'referrerpolicy', 'loading',\n ]),\n td: new Set(['colspan', 'rowspan', 'scope', 'style', 'class', 'data-align']),\n th: new Set(['colspan', 'rowspan', 'scope', 'style', 'class', 'data-align']),\n table: new Set(['style', 'class', 'role', 'data-cols', 'data-stack']),\n col: new Set(['span', 'style', 'width']),\n colgroup: new Set(['span', 'style']),\n figure: new Set(['class', 'style', 'data-align', 'data-cols']),\n figcaption: new Set(['class', 'style']),\n span: new Set(['class', 'style']),\n div: new Set(['class', 'style']),\n p: new Set(['class', 'style']),\n h1: new Set(['class', 'style']),\n h2: new Set(['class', 'style']),\n h3: new Set(['class', 'style']),\n h4: new Set(['class', 'style']),\n h5: new Set(['class', 'style']),\n h6: new Set(['class', 'style']),\n blockquote: new Set(['class', 'style']),\n pre: new Set(['class', 'style']),\n code: new Set(['class']),\n li: new Set(['class', 'style', 'value']),\n ol: new Set(['class', 'style', 'start', 'type']),\n ul: new Set(['class', 'style']),\n hr: new Set(['class', 'style']),\n small: new Set(['class']),\n mark: new Set(['class', 'style']),\n};\n\n/** Propriedades CSS permitidas em `style` inline. */\nexport const ALLOWED_STYLE_PROPS = new Set<string>([\n 'color', 'background-color', 'text-align', 'font-size', 'font-family',\n 'font-weight', 'font-style', 'text-decoration', 'text-decoration-line',\n 'vertical-align', 'width', 'height', 'max-width', 'min-width',\n 'margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left',\n 'padding', 'padding-top', 'padding-right', 'padding-bottom', 'padding-left',\n 'border', 'border-top', 'border-right', 'border-bottom', 'border-left',\n 'border-collapse', 'border-spacing', 'border-radius', 'float', 'display',\n 'line-height', 'white-space', 'word-break', 'overflow-wrap', 'list-style-type',\n]);\n\n/** Classes `fx-*` que podem ser preservadas no HTML salvo. */\nexport const ALLOWED_CLASS_RE = /^fx-[a-z0-9-]+$/i;\n\n/** Hosts autorizados para embed de vídeo. */\nexport const ALLOWED_EMBED_HOSTS = new Set<string>([\n 'www.youtube.com',\n 'youtube.com',\n 'www.youtube-nocookie.com',\n 'youtube-nocookie.com',\n 'player.vimeo.com',\n]);\n\n/** Protocolos permitidos em links. */\nexport const SAFE_LINK_PROTOCOLS = new Set<string>(['http:', 'https:', 'mailto:', 'tel:']);\n\n/** Protocolos permitidos em mídia. */\nexport const SAFE_MEDIA_PROTOCOLS = new Set<string>(['http:', 'https:']);\n\n/** Data URL de imagem segura (SVG é bloqueado de propósito). */\nexport const SAFE_IMAGE_DATA_RE = /^data:image\\/(?:png|jpe?g|gif|webp|avif);base64,[a-z0-9+/=\\s]+$/i;\n\n/** Limite padrão para imagens em base64 (bytes aproximados do data URL). */\nexport const DEFAULT_MAX_DATA_IMAGE_BYTES = 2 * 1024 * 1024;\n\n/** Limite padrão para vídeo em base64 (bytes aproximados do data URL). */\nexport const DEFAULT_MAX_DATA_VIDEO_BYTES = 4 * 1024 * 1024;\n\n/** Atributos que nunca podem existir no HTML salvo. */\nexport const FORBIDDEN_ATTRS = new Set<string>([\n 'contenteditable', 'spellcheck', 'tabindex', 'autofocus', 'id',\n 'onclick', 'ondblclick', 'onmousedown', 'onmouseup', 'onmouseover',\n 'onmouseout', 'onmousemove', 'onkeydown', 'onkeyup', 'onkeypress',\n 'onload', 'onerror', 'onfocus', 'onblur', 'oninput', 'onchange',\n 'onsubmit', 'onreset', 'onselect', 'ondrag', 'ondrop', 'onpaste',\n 'oncopy', 'oncut', 'onscroll', 'onwheel', 'onanimationstart',\n 'onanimationend', 'ontransitionend', 'formaction', 'srcdoc',\n 'xlink:href', 'xmlns', 'http-equiv', 'ping',\n]);\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Utilitários de seleção/Range para o <fx-editor>.
3
+ *
4
+ * O contenteditable vive dentro de um Shadow DOM aberto. Em navegadores
5
+ * modernos `Selection.getComposedRanges()` devolve os nós reais; sem ele,
6
+ * caímos para `document.getSelection()` (light DOM/jsdom).
7
+ */
8
+ export interface ComposedSelection extends Selection {
9
+ }
10
+ export declare const BLOCK_SELECTOR = "p,h1,h2,h3,h4,h5,h6,li,blockquote,pre,td,th,figcaption,div";
11
+ export declare function isRangeInside(editor: HTMLElement, range: Range | null): boolean;
12
+ export declare function readSelectionRange(root: ShadowRoot | Document): Range | null;
13
+ export declare function getRange(editor: HTMLElement, fallback?: Range | null): Range | null;
14
+ export declare function saveRange(editor: HTMLElement): Range | null;
15
+ export declare function restoreRange(editor: HTMLElement, range: Range | null): void;
16
+ export declare function placeCaretAtEnd(editor: HTMLElement): void;
17
+ export declare function getBlock(editor: HTMLElement, range?: Range | null): HTMLElement | null;
18
+ export declare function getSelectedBlocks(editor: HTMLElement, range?: Range | null): HTMLElement[];
19
+ export declare function closestInEditor<T extends Element>(editor: HTMLElement, node: Node | null, selector: string): T | null;
20
+ export declare function selectElement(el: Element): void;
21
+ export declare function isSelectionCollapsed(editor: HTMLElement): boolean;
22
+ //# sourceMappingURL=selection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selection.d.ts","sourceRoot":"","sources":["../../../src/components/editor/selection.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,iBAAkB,SAAQ,SAAS;CAGnD;AAED,eAAO,MAAM,cAAc,+DACmC,CAAC;AAO/D,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,GAAG,OAAO,CAK/E;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,UAAU,GAAG,QAAQ,GAAG,KAAK,GAAG,IAAI,CAoB5E;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,KAAK,GAAG,IAAI,CAKnF;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,WAAW,GAAG,KAAK,GAAG,IAAI,CAG3D;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,GAAG,IAAI,CAO3E;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAQzD;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,WAAW,GAAG,IAAI,CAOtF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,WAAW,EAAE,CAe1F;AAED,wBAAgB,eAAe,CAAC,CAAC,SAAS,OAAO,EAC/C,MAAM,EAAE,WAAW,EACnB,IAAI,EAAE,IAAI,GAAG,IAAI,EACjB,QAAQ,EAAE,MAAM,GACf,CAAC,GAAG,IAAI,CAIV;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAO/C;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAGjE"}
@@ -0,0 +1,97 @@
1
+ const BLOCK_SELECTOR = "p,h1,h2,h3,h4,h5,h6,li,blockquote,pre,td,th,figcaption,div";
2
+ function rootOf(editor) {
3
+ const root = editor.getRootNode();
4
+ return typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot ? root : document;
5
+ }
6
+ function isRangeInside(editor, range) {
7
+ if (!range) return false;
8
+ const start = range.startContainer;
9
+ const end = range.endContainer;
10
+ return editor.contains(start) && editor.contains(end);
11
+ }
12
+ function readSelectionRange(root) {
13
+ const selection = document.getSelection();
14
+ if (!selection) return null;
15
+ if (root instanceof ShadowRoot && selection.getComposedRanges) {
16
+ try {
17
+ const ranges = selection.getComposedRanges({ shadowRoots: [root] });
18
+ const first = ranges[0];
19
+ if (first) {
20
+ const range = document.createRange();
21
+ range.setStart(first.startContainer, first.startOffset);
22
+ range.setEnd(first.endContainer, first.endOffset);
23
+ return range;
24
+ }
25
+ } catch {
26
+ }
27
+ }
28
+ return selection.rangeCount > 0 ? selection.getRangeAt(0) : null;
29
+ }
30
+ function getRange(editor, fallback) {
31
+ const range = readSelectionRange(rootOf(editor));
32
+ if (isRangeInside(editor, range)) return range;
33
+ if (isRangeInside(editor, fallback ?? null)) return fallback ?? null;
34
+ return null;
35
+ }
36
+ function saveRange(editor) {
37
+ const range = getRange(editor);
38
+ return range ? range.cloneRange() : null;
39
+ }
40
+ function restoreRange(editor, range) {
41
+ if (!range || !isRangeInside(editor, range)) return;
42
+ editor.focus({ preventScroll: true });
43
+ const selection = document.getSelection();
44
+ if (!selection) return;
45
+ selection.removeAllRanges();
46
+ selection.addRange(range);
47
+ }
48
+ function placeCaretAtEnd(editor) {
49
+ const range = document.createRange();
50
+ range.selectNodeContents(editor);
51
+ range.collapse(false);
52
+ const selection = document.getSelection();
53
+ if (!selection) return;
54
+ selection.removeAllRanges();
55
+ selection.addRange(range);
56
+ }
57
+ function getBlock(editor, range) {
58
+ const active = range ?? getRange(editor);
59
+ if (!active) return null;
60
+ const node = active.startContainer;
61
+ const el = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
62
+ const block = el?.closest(BLOCK_SELECTOR) ?? null;
63
+ return block && editor.contains(block) ? block : null;
64
+ }
65
+ function getSelectedBlocks(editor, range) {
66
+ const active = getRange(editor);
67
+ if (!active) return [];
68
+ if (active.collapsed) {
69
+ const block = getBlock(editor, active);
70
+ return block ? [block] : [];
71
+ }
72
+ return Array.from(editor.querySelectorAll(BLOCK_SELECTOR)).filter((block) => {
73
+ try {
74
+ return active.intersectsNode(block);
75
+ } catch {
76
+ return false;
77
+ }
78
+ });
79
+ }
80
+ function closestInEditor(editor, node, selector) {
81
+ const el = node && (node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement);
82
+ const match = el?.closest(selector) ?? null;
83
+ return match && editor.contains(match) ? match : null;
84
+ }
85
+ export {
86
+ BLOCK_SELECTOR,
87
+ closestInEditor,
88
+ getBlock,
89
+ getRange,
90
+ getSelectedBlocks,
91
+ isRangeInside,
92
+ placeCaretAtEnd,
93
+ readSelectionRange,
94
+ restoreRange,
95
+ saveRange
96
+ };
97
+ //# sourceMappingURL=selection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selection.js","sources":["../../../src/components/editor/selection.ts"],"sourcesContent":["/**\n * Utilitários de seleção/Range para o <fx-editor>.\n *\n * O contenteditable vive dentro de um Shadow DOM aberto. Em navegadores\n * modernos `Selection.getComposedRanges()` devolve os nós reais; sem ele,\n * caímos para `document.getSelection()` (light DOM/jsdom).\n */\n\nexport interface ComposedSelection extends Selection {\n // Tipagem de compatibilidade para navegadores que ainda não declaram\n // getComposedRanges no lib.dom. Em TS 5.6+ o método já existe em Selection.\n}\n\nexport const BLOCK_SELECTOR =\n 'p,h1,h2,h3,h4,h5,h6,li,blockquote,pre,td,th,figcaption,div';\n\nfunction rootOf(editor: HTMLElement): ShadowRoot | Document {\n const root = editor.getRootNode();\n return typeof ShadowRoot !== 'undefined' && root instanceof ShadowRoot ? root : document;\n}\n\nexport function isRangeInside(editor: HTMLElement, range: Range | null): boolean {\n if (!range) return false;\n const start = range.startContainer;\n const end = range.endContainer;\n return editor.contains(start) && editor.contains(end);\n}\n\nexport function readSelectionRange(root: ShadowRoot | Document): Range | null {\n const selection = document.getSelection() as ComposedSelection | null;\n if (!selection) return null;\n\n if (root instanceof ShadowRoot && selection.getComposedRanges) {\n try {\n const ranges = selection.getComposedRanges({ shadowRoots: [root] });\n const first = ranges[0];\n if (first) {\n const range = document.createRange();\n range.setStart(first.startContainer, first.startOffset);\n range.setEnd(first.endContainer, first.endOffset);\n return range;\n }\n } catch {\n /* fallback abaixo */\n }\n }\n\n return selection.rangeCount > 0 ? selection.getRangeAt(0) : null;\n}\n\nexport function getRange(editor: HTMLElement, fallback?: Range | null): Range | null {\n const range = readSelectionRange(rootOf(editor));\n if (isRangeInside(editor, range)) return range;\n if (isRangeInside(editor, fallback ?? null)) return fallback ?? null;\n return null;\n}\n\nexport function saveRange(editor: HTMLElement): Range | null {\n const range = getRange(editor);\n return range ? range.cloneRange() : null;\n}\n\nexport function restoreRange(editor: HTMLElement, range: Range | null): void {\n if (!range || !isRangeInside(editor, range)) return;\n editor.focus({ preventScroll: true });\n const selection = document.getSelection();\n if (!selection) return;\n selection.removeAllRanges();\n selection.addRange(range);\n}\n\nexport function placeCaretAtEnd(editor: HTMLElement): void {\n const range = document.createRange();\n range.selectNodeContents(editor);\n range.collapse(false);\n const selection = document.getSelection();\n if (!selection) return;\n selection.removeAllRanges();\n selection.addRange(range);\n}\n\nexport function getBlock(editor: HTMLElement, range?: Range | null): HTMLElement | null {\n const active = range ?? getRange(editor);\n if (!active) return null;\n const node = active.startContainer;\n const el = node.nodeType === Node.ELEMENT_NODE ? (node as Element) : node.parentElement;\n const block = el?.closest<HTMLElement>(BLOCK_SELECTOR) ?? null;\n return block && editor.contains(block) ? block : null;\n}\n\nexport function getSelectedBlocks(editor: HTMLElement, range?: Range | null): HTMLElement[] {\n const active = range ?? getRange(editor);\n if (!active) return [];\n if (active.collapsed) {\n const block = getBlock(editor, active);\n return block ? [block] : [];\n }\n\n return Array.from(editor.querySelectorAll<HTMLElement>(BLOCK_SELECTOR)).filter((block) => {\n try {\n return active.intersectsNode(block);\n } catch {\n return false;\n }\n });\n}\n\nexport function closestInEditor<T extends Element>(\n editor: HTMLElement,\n node: Node | null,\n selector: string,\n): T | null {\n const el = node && (node.nodeType === Node.ELEMENT_NODE ? (node as Element) : node.parentElement);\n const match = el?.closest<T>(selector) ?? null;\n return match && editor.contains(match) ? match : null;\n}\n\nexport function selectElement(el: Element): void {\n const range = document.createRange();\n range.selectNode(el);\n const selection = document.getSelection();\n if (!selection) return;\n selection.removeAllRanges();\n selection.addRange(range);\n}\n\nexport function isSelectionCollapsed(editor: HTMLElement): boolean {\n const range = getRange(editor);\n return !range || range.collapsed;\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Estilos do <fx-editor>.
3
+ *
4
+ * Tudo isolado no Shadow DOM. A parte de conteúdo também é exportada em
5
+ * `FX_EDITOR_CONTENT_CSS` para quem salva o HTML e precisa renderizá-lo fora
6
+ * do componente.
7
+ */
8
+ export declare const FX_EDITOR_CONTENT_CSS = "\n.content { line-height: 1.55; color: var(--fx-text-default); }\n.content p { margin: 0 0 0.65em; }\n.content h1, .content h2, .content h3, .content h4, .content h5, .content h6 {\n margin: 0.8em 0 0.4em; line-height: 1.25; font-weight: 600; color: var(--fx-text-default);\n}\n.content h1 { font-size: 1.8em; }\n.content h2 { font-size: 1.5em; }\n.content h3 { font-size: 1.25em; }\n.content h4 { font-size: 1.1em; }\n.content h5, .content h6 { font-size: 1em; }\n.content ul, .content ol { margin: 0 0 0.65em; padding-left: 1.6em; }\n.content li { margin: 0.15em 0; }\n.content blockquote {\n margin: 0.5em 0; padding: 0.25em 0 0.25em 1em;\n border-left: 3px solid var(--fx-color-primary); color: var(--fx-text-muted);\n}\n.content pre {\n margin: 0.6em 0; padding: var(--fx-space-md); overflow: auto;\n background: var(--fx-surface-surface); border-radius: var(--fx-radius-sm);\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.9em;\n}\n.content code {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 0.92em; background: var(--fx-surface-surface);\n padding: 0.1em 0.3em; border-radius: 3px;\n}\n.content pre code { background: none; padding: 0; }\n.content hr { border: none; border-top: 1px solid var(--fx-border-default); margin: 1em 0; }\n.content a { color: var(--fx-color-primary); text-decoration: underline; cursor: pointer; }\n.content img { max-width: 100%; height: auto; display: block; }\n.content figure { margin: 1em 0; }\n.content figure.fx-image img { margin: 0 auto; }\n.content figure.fx-image[data-align=\"left\"] { float: left; max-width: 50%; margin: 0 1em 0.5em 0; }\n.content figure.fx-image[data-align=\"right\"] { float: right; max-width: 50%; margin: 0 0 0.5em 1em; }\n.content figure.fx-image[data-align=\"center\"] { margin-left: auto; margin-right: auto; }\n.content figure.fx-video[data-align=\"left\"],\n.content figure.fx-embed[data-align=\"left\"] { float: left; max-width: 50%; margin: 0 1em 0.5em 0; }\n.content figure.fx-video[data-align=\"right\"],\n.content figure.fx-embed[data-align=\"right\"] { float: right; max-width: 50%; margin: 0 0 0.5em 1em; }\n.content figure.fx-video[data-align=\"center\"],\n.content figure.fx-embed[data-align=\"center\"] { margin-left: auto; margin-right: auto; }\n.content figcaption {\n margin-top: 0.4em; color: var(--fx-text-muted);\n font-size: 0.85em; text-align: center;\n}\n.content video { display: block; width: 100%; background: #000; border-radius: var(--fx-radius-sm); }\n.content iframe { display: block; width: 100%; aspect-ratio: 16 / 9; border: 0; }\n.content table { width: 100%; border-collapse: collapse; margin: 1em 0; }\n.content th, .content td { border: 1px solid var(--fx-border-default); padding: var(--fx-space-xs) var(--fx-space-sm); vertical-align: top; }\n.content th { background: var(--fx-surface-surface); text-align: left; font-weight: 600; }\n.content table.fx-grid { border: none; margin: 0.6em 0; }\n.content table.fx-grid > tbody > tr > td {\n border: 1px dashed var(--fx-border-default); padding: var(--fx-space-sm);\n vertical-align: top;\n}\n@media (max-width: 640px) {\n .content table.fx-grid[data-stack] > tbody > tr > td {\n display: block; width: 100% !important; border: none; padding: var(--fx-space-xs) 0;\n }\n}\n";
9
+ export declare const EDITOR_STYLES = "\n:host {\n display: block;\n width: 100%;\n font-family: var(--fx-font-family);\n font-size: var(--fx-font-size);\n color: var(--fx-text-default);\n --fx-editor-min-height: 240px;\n --fx-editor-max-height: none;\n --fx-editor-radius: var(--fx-radius-md);\n --fx-editor-toolbar-bg: var(--fx-surface-surface);\n --fx-editor-content-padding: var(--fx-space-md) var(--fx-space-lg);\n}\n:host([full]) { width: 100%; }\n.editor {\n display: flex;\n flex-direction: column;\n overflow: hidden;\n background: var(--fx-surface-background);\n border: 1px solid var(--fx-border-default);\n border-radius: var(--fx-editor-radius);\n transition: border-color var(--fx-motion-duration-normal) var(--fx-motion-easing),\n box-shadow var(--fx-motion-duration-normal) var(--fx-motion-easing);\n}\n.editor:focus-within {\n border-color: var(--fx-color-primary);\n box-shadow: var(--fx-effect-focus-ring, none);\n}\n:host([error]) .editor, :host([invalid]) .editor {\n border-color: var(--fx-color-danger, #dc2626);\n box-shadow: var(--fx-effect-error-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-danger, #dc2626) 18%, transparent));\n}\n:host([success]) .editor, :host([valid]) .editor {\n border-color: var(--fx-color-success, #16a34a);\n box-shadow: var(--fx-effect-success-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent));\n}\n:host([disabled]) { opacity: 0.55; pointer-events: none; }\n:host([readonly]) .toolbar { display: none; }\n\n.toolbar {\n position: relative;\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 1px;\n padding: var(--fx-space-xs);\n background: var(--fx-editor-toolbar-bg);\n border-bottom: 1px solid var(--fx-border-default);\n user-select: none;\n}\n.toolbar[hidden] { display: none; }\n.tb-group {\n display: flex;\n align-items: center;\n gap: 1px;\n padding: 0 var(--fx-space-xs);\n border-right: 1px solid var(--fx-border-default);\n}\n.tb-group:last-child { border-right: none; }\n.tb-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 30px;\n height: 30px;\n padding: 0;\n border: none;\n border-radius: var(--fx-radius-sm);\n background: transparent;\n color: var(--fx-text-default);\n font-size: 19px;\n cursor: pointer;\n transition: background var(--fx-motion-duration-fast) var(--fx-motion-easing);\n}\n.tb-btn:hover { background: var(--fx-surface-surface-hover, rgba(0, 0, 0, 0.06)); }\n.tb-btn:focus-visible { outline: 2px solid var(--fx-color-primary); outline-offset: -2px; }\n.tb-btn.active {\n background: color-mix(in srgb, var(--fx-color-primary) 14%, transparent);\n color: var(--fx-color-primary);\n}\n.tb-btn:disabled { opacity: 0.4; cursor: not-allowed; }\n.tb-select {\n height: 30px;\n padding: 0 var(--fx-space-xs);\n border: 1px solid var(--fx-border-default);\n border-radius: var(--fx-radius-sm);\n background: var(--fx-surface-background);\n color: var(--fx-text-default);\n font-family: inherit;\n font-size: calc(var(--fx-font-size) - 2px);\n cursor: pointer;\n}\n.tb-color {\n width: 30px;\n height: 30px;\n padding: 2px;\n border: none;\n background: transparent;\n cursor: pointer;\n}\n.tb-color input { width: 26px; height: 26px; padding: 0; border: none; background: none; cursor: pointer; }\n.body { position: relative; display: flex; flex-direction: column; flex: 1 1 auto; }\n.content {\n position: relative;\n min-height: var(--fx-editor-min-height);\n max-height: var(--fx-editor-max-height);\n overflow: auto;\n padding: var(--fx-editor-content-padding);\n outline: none;\n box-sizing: border-box;\n}\n.content[hidden] { display: none; }\n.content.is-empty::before {\n content: attr(data-placeholder);\n color: var(--fx-text-muted);\n pointer-events: none;\n position: absolute;\n}\n.content[contenteditable=\"false\"] { background: var(--fx-surface-surface-hover, rgba(0,0,0,.02)); }\n.content figure.fx-image { touch-action: none; }\n\n.media-overlay {\n position: absolute;\n z-index: 5;\n pointer-events: none;\n border: 1px solid var(--fx-color-primary);\n box-shadow: 0 0 0 1px color-mix(in srgb, var(--fx-color-primary) 30%, transparent);\n}\n.media-overlay[hidden] { display: none; }\n.media-handle {\n position: absolute;\n width: 11px;\n height: 11px;\n border: 1px solid #fff;\n border-radius: 50%;\n background: var(--fx-color-primary);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);\n pointer-events: auto;\n touch-action: none;\n}\n.media-handle.nw { left: -6px; top: -6px; cursor: nwse-resize; }\n.media-handle.ne { right: -6px; top: -6px; cursor: nesw-resize; }\n.media-handle.sw { left: -6px; bottom: -6px; cursor: nesw-resize; }\n.media-handle.se { right: -6px; bottom: -6px; cursor: nwse-resize; }\n.media-size {\n position: absolute;\n top: -24px;\n left: 0;\n padding: 2px 6px;\n border-radius: var(--fx-radius-sm);\n background: var(--fx-color-primary);\n color: #fff;\n font-size: 11px;\n line-height: 1.4;\n white-space: nowrap;\n}\n.media-delete {\n position: absolute;\n top: -32px;\n right: -1px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n padding: 0;\n border: none;\n border-radius: 50%;\n background: var(--fx-color-danger, #dc2626);\n color: #fff;\n font-size: 15px;\n line-height: 1;\n cursor: pointer;\n pointer-events: auto;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);\n}\n.media-delete:hover { filter: brightness(0.92); }\n.media-delete:focus-visible { outline: 2px solid #fff; outline-offset: 1px; }\n\n.content figure.fx-image,\n.content figure.fx-video,\n.content figure.fx-embed { position: relative; }\n.fx-media-quick {\n position: absolute;\n top: 6px;\n z-index: 4;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n padding: 0;\n border: 1px solid color-mix(in srgb, var(--fx-color-primary) 35%, transparent);\n border-radius: var(--fx-radius-sm);\n background: var(--fx-surface-background);\n color: var(--fx-text-default);\n cursor: pointer;\n opacity: 0;\n transition: opacity var(--fx-motion-duration-fast) var(--fx-motion-easing), background var(--fx-motion-duration-fast) var(--fx-motion-easing);\n}\n.content figure:hover > .fx-media-quick,\n.content figure:focus-within > .fx-media-quick { opacity: 1; }\n.content figure.fx-video > .fx-media-quick,\n.content figure.fx-embed > .fx-media-quick { opacity: 0.92; }\n.fx-media-quick:hover { background: var(--fx-surface-surface-hover, rgba(0, 0, 0, 0.06)); }\n.fx-media-quick.active { background: var(--fx-color-primary); color: #fff; }\n.fx-media-quick.quick-left { right: 82px; }\n.fx-media-quick.quick-center { right: 56px; }\n.fx-media-quick.quick-right { right: 30px; }\n.fx-media-quick.quick-delete { right: 4px; background: var(--fx-color-danger, #dc2626); color: #fff; border-color: transparent; }\n.fx-media-quick:focus-visible { outline: 2px solid var(--fx-color-primary); outline-offset: 1px; }\n\n.link-overlay {\n position: absolute;\n z-index: 6;\n display: flex;\n gap: 2px;\n padding: 3px;\n background: var(--fx-surface-background);\n border: 1px solid var(--fx-border-default);\n border-radius: var(--fx-radius-sm);\n box-shadow: var(--fx-shadow-md, 0 4px 12px rgba(0, 0, 0, 0.16));\n}\n.link-overlay[hidden] { display: none; }\n.link-overlay button {\n border: none;\n background: none;\n color: var(--fx-text-default);\n font: inherit;\n font-size: 12px;\n line-height: 1.4;\n padding: 4px 8px;\n border-radius: var(--fx-radius-sm);\n cursor: pointer;\n}\n.link-overlay button:hover { background: var(--fx-surface-surface-hover, rgba(0, 0, 0, 0.06)); }\n.link-overlay button[data-link-action=\"remove\"] { color: var(--fx-color-danger, #dc2626); }\n.link-overlay button:focus-visible { outline: 2px solid var(--fx-color-primary); outline-offset: 1px; }\n\n.source {\n display: block;\n width: 100%;\n min-height: var(--fx-editor-min-height);\n max-height: var(--fx-editor-max-height);\n padding: var(--fx-editor-content-padding);\n border: none;\n outline: none;\n resize: vertical;\n background: var(--fx-surface-background);\n color: var(--fx-text-default);\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 13px;\n line-height: 1.55;\n box-sizing: border-box;\n}\n.source[hidden] { display: none; }\n\n.statusbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--fx-space-sm);\n min-height: 28px;\n padding: 2px var(--fx-space-md);\n border-top: 1px solid var(--fx-border-default);\n color: var(--fx-text-muted);\n font-size: calc(var(--fx-font-size) - 3px);\n}\n.statusbar:empty { display: none; }\n.statusbar[hidden] { display: none; }\n.source-actions { display: inline-flex; gap: var(--fx-space-xs); }\n.source-actions[hidden] { display: none; }\n\n.backdrop {\n position: fixed;\n inset: 0;\n z-index: var(--fx-z-modal, 1100);\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--fx-text-default, #000) 45%, transparent);\n}\n.backdrop[hidden] { display: none; }\n.dialog {\n width: min(460px, calc(100vw - 32px));\n max-height: calc(100vh - 48px);\n overflow: auto;\n background: var(--fx-surface-background);\n border: 1px solid var(--fx-border-default);\n border-radius: var(--fx-radius-lg);\n box-shadow: var(--fx-shadow-xl);\n font-family: var(--fx-font-family);\n font-size: var(--fx-font-size);\n}\n.dialog header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--fx-space-md) var(--fx-space-lg);\n border-bottom: 1px solid var(--fx-border-default);\n}\n.dialog h3 { margin: 0; font-size: 1rem; font-weight: 600; }\n.dialog-close {\n border: none; background: transparent; color: var(--fx-text-muted);\n font-size: 1.4rem; line-height: 1; cursor: pointer;\n}\n.dialog-body { display: flex; flex-direction: column; gap: var(--fx-space-sm); padding: var(--fx-space-lg); }\n.field { display: flex; flex-direction: column; gap: 3px; font-size: calc(var(--fx-font-size) - 2px); color: var(--fx-text-muted); }\n.field input, .field select {\n height: var(--fx-size-md);\n padding: 0 var(--fx-space-sm);\n border: 1px solid var(--fx-border-default);\n border-radius: var(--fx-radius-sm);\n background: var(--fx-surface-background);\n color: var(--fx-text-default);\n font: inherit;\n font-size: var(--fx-font-size);\n}\n.field input:focus, .field select:focus { outline: none; border-color: var(--fx-color-primary); box-shadow: var(--fx-effect-focus-ring, none); }\n.dialog footer {\n display: flex; justify-content: flex-end; gap: var(--fx-space-sm);\n padding: var(--fx-space-md) var(--fx-space-lg);\n border-top: 1px solid var(--fx-border-default);\n}\n.btn {\n min-height: var(--fx-size-sm);\n padding: 0 var(--fx-space-md);\n border: 1px solid var(--fx-border-default);\n border-radius: var(--fx-radius-sm);\n background: var(--fx-surface-background);\n color: var(--fx-text-default);\n font: inherit; font-size: calc(var(--fx-font-size) - 2px);\n cursor: pointer;\n}\n.btn:hover { background: var(--fx-surface-surface-hover, rgba(0,0,0,.05)); }\n.btn.primary { background: var(--fx-color-primary); border-color: transparent; color: #fff; }\n.btn.primary:hover { filter: brightness(0.92); }\n\n.panel {\n position: absolute;\n top: calc(100% + 4px);\n left: var(--fx-space-md);\n z-index: 6;\n padding: var(--fx-space-sm);\n background: var(--fx-surface-background);\n border: 1px solid var(--fx-border-default);\n border-radius: var(--fx-radius-md);\n box-shadow: var(--fx-shadow-lg);\n}\n.panel[hidden] { display: none; }\n.grid-picker { display: grid; grid-template-columns: repeat(8, 18px); gap: 3px; }\n.grid-picker button {\n width: 18px; height: 18px; padding: 0;\n border: 1px solid var(--fx-border-default);\n border-radius: 2px; background: var(--fx-surface-background); cursor: pointer;\n}\n.grid-picker button.on { background: var(--fx-color-primary); border-color: var(--fx-color-primary); }\n.panel-title { margin: 0 0 6px; font-size: 11px; color: var(--fx-text-muted); }\n.panel-actions { display: flex; flex-wrap: wrap; gap: 4px; max-width: 210px; }\n.panel-actions .btn { min-height: 26px; padding: 0 6px; font-size: 11px; }\n\n:host([fullscreen]) {\n position: fixed;\n inset: 0;\n z-index: var(--fx-z-modal, 1100);\n background: var(--fx-surface-background);\n}\n:host([fullscreen]) .editor { height: 100%; border: none; border-radius: 0; }\n:host([fullscreen]) .content, :host([fullscreen]) .source { flex: 1 1 auto; max-height: none; }\n\n@media (max-width: 640px) {\n .toolbar { gap: 0; }\n .tb-group { padding: 0 1px; }\n .tb-btn { width: 28px; height: 28px; }\n}\n\n.content { line-height: 1.55; color: var(--fx-text-default); }\n.content p { margin: 0 0 0.65em; }\n.content h1, .content h2, .content h3, .content h4, .content h5, .content h6 {\n margin: 0.8em 0 0.4em; line-height: 1.25; font-weight: 600; color: var(--fx-text-default);\n}\n.content h1 { font-size: 1.8em; }\n.content h2 { font-size: 1.5em; }\n.content h3 { font-size: 1.25em; }\n.content h4 { font-size: 1.1em; }\n.content h5, .content h6 { font-size: 1em; }\n.content ul, .content ol { margin: 0 0 0.65em; padding-left: 1.6em; }\n.content li { margin: 0.15em 0; }\n.content blockquote {\n margin: 0.5em 0; padding: 0.25em 0 0.25em 1em;\n border-left: 3px solid var(--fx-color-primary); color: var(--fx-text-muted);\n}\n.content pre {\n margin: 0.6em 0; padding: var(--fx-space-md); overflow: auto;\n background: var(--fx-surface-surface); border-radius: var(--fx-radius-sm);\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.9em;\n}\n.content code {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 0.92em; background: var(--fx-surface-surface);\n padding: 0.1em 0.3em; border-radius: 3px;\n}\n.content pre code { background: none; padding: 0; }\n.content hr { border: none; border-top: 1px solid var(--fx-border-default); margin: 1em 0; }\n.content a { color: var(--fx-color-primary); text-decoration: underline; cursor: pointer; }\n.content img { max-width: 100%; height: auto; display: block; }\n.content figure { margin: 1em 0; }\n.content figure.fx-image img { margin: 0 auto; }\n.content figure.fx-image[data-align=\"left\"] { float: left; max-width: 50%; margin: 0 1em 0.5em 0; }\n.content figure.fx-image[data-align=\"right\"] { float: right; max-width: 50%; margin: 0 0 0.5em 1em; }\n.content figure.fx-image[data-align=\"center\"] { margin-left: auto; margin-right: auto; }\n.content figure.fx-video[data-align=\"left\"],\n.content figure.fx-embed[data-align=\"left\"] { float: left; max-width: 50%; margin: 0 1em 0.5em 0; }\n.content figure.fx-video[data-align=\"right\"],\n.content figure.fx-embed[data-align=\"right\"] { float: right; max-width: 50%; margin: 0 0 0.5em 1em; }\n.content figure.fx-video[data-align=\"center\"],\n.content figure.fx-embed[data-align=\"center\"] { margin-left: auto; margin-right: auto; }\n.content figcaption {\n margin-top: 0.4em; color: var(--fx-text-muted);\n font-size: 0.85em; text-align: center;\n}\n.content video { display: block; width: 100%; background: #000; border-radius: var(--fx-radius-sm); }\n.content iframe { display: block; width: 100%; aspect-ratio: 16 / 9; border: 0; }\n.content table { width: 100%; border-collapse: collapse; margin: 1em 0; }\n.content th, .content td { border: 1px solid var(--fx-border-default); padding: var(--fx-space-xs) var(--fx-space-sm); vertical-align: top; }\n.content th { background: var(--fx-surface-surface); text-align: left; font-weight: 600; }\n.content table.fx-grid { border: none; margin: 0.6em 0; }\n.content table.fx-grid > tbody > tr > td {\n border: 1px dashed var(--fx-border-default); padding: var(--fx-space-sm);\n vertical-align: top;\n}\n@media (max-width: 640px) {\n .content table.fx-grid[data-stack] > tbody > tr > td {\n display: block; width: 100% !important; border: none; padding: var(--fx-space-xs) 0;\n }\n}\n\n\n\n";
10
+ //# sourceMappingURL=styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../src/components/editor/styles.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,eAAO,MAAM,qBAAqB,owGA6DjC,CAAC;AAEF,eAAO,MAAM,aAAa,2wfA0XzB,CAAC"}