@wrrdev/fenix-ui 1.1.14 → 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 (69) hide show
  1. package/README.md +5 -4
  2. package/dist/components/editor/commands.d.ts +27 -0
  3. package/dist/components/editor/commands.d.ts.map +1 -0
  4. package/dist/components/editor/commands.js +235 -0
  5. package/dist/components/editor/commands.js.map +1 -0
  6. package/dist/components/editor/editor.d.ts +83 -0
  7. package/dist/components/editor/editor.d.ts.map +1 -0
  8. package/dist/components/editor/editor.js +1951 -0
  9. package/dist/components/editor/editor.js.map +1 -0
  10. package/dist/components/editor/index.d.ts +12 -0
  11. package/dist/components/editor/index.d.ts.map +1 -0
  12. package/dist/components/editor/index.js +7 -0
  13. package/dist/components/editor/index.js.map +1 -0
  14. package/dist/components/editor/normalize-html.d.ts +33 -0
  15. package/dist/components/editor/normalize-html.d.ts.map +1 -0
  16. package/dist/components/editor/normalize-html.js +316 -0
  17. package/dist/components/editor/normalize-html.js.map +1 -0
  18. package/dist/components/editor/sanitize-html.d.ts +37 -0
  19. package/dist/components/editor/sanitize-html.d.ts.map +1 -0
  20. package/dist/components/editor/sanitize-html.js +247 -0
  21. package/dist/components/editor/sanitize-html.js.map +1 -0
  22. package/dist/components/editor/schema.d.ts +36 -0
  23. package/dist/components/editor/schema.d.ts.map +1 -0
  24. package/dist/components/editor/schema.js +232 -0
  25. package/dist/components/editor/schema.js.map +1 -0
  26. package/dist/components/editor/selection.d.ts +22 -0
  27. package/dist/components/editor/selection.d.ts.map +1 -0
  28. package/dist/components/editor/selection.js +97 -0
  29. package/dist/components/editor/selection.js.map +1 -0
  30. package/dist/components/editor/styles.d.ts +10 -0
  31. package/dist/components/editor/styles.d.ts.map +1 -0
  32. package/dist/components/editor/styles.js +446 -0
  33. package/dist/components/editor/styles.js.map +1 -0
  34. package/dist/components/editor/table-utils.d.ts +33 -0
  35. package/dist/components/editor/table-utils.d.ts.map +1 -0
  36. package/dist/components/editor/table-utils.js +150 -0
  37. package/dist/components/editor/table-utils.js.map +1 -0
  38. package/dist/components/input/input.d.ts +5 -1
  39. package/dist/components/input/input.d.ts.map +1 -1
  40. package/dist/components/input/input.js +15 -1
  41. package/dist/components/input/input.js.map +1 -1
  42. package/dist/components/toast/toast.d.ts +10 -10
  43. package/dist/components/toast/toast.d.ts.map +1 -1
  44. package/dist/components/toast/toast.js +46 -13
  45. package/dist/components/toast/toast.js.map +1 -1
  46. package/dist/core/jsx.d.ts +25 -1
  47. package/dist/core/jsx.d.ts.map +1 -1
  48. package/dist/core/jsx.js.map +1 -1
  49. package/dist/core/vue.d.ts +3 -1
  50. package/dist/core/vue.d.ts.map +1 -1
  51. package/dist/core/vue.js.map +1 -1
  52. package/dist/docs/componentes/editor.doc.d.ts +6 -0
  53. package/dist/docs/componentes/editor.doc.d.ts.map +1 -0
  54. package/dist/docs/componentes/index.d.ts.map +1 -1
  55. package/dist/docs/componentes/input.doc.d.ts.map +1 -1
  56. package/dist/fenix-ui.umd.min.js +748 -254
  57. package/dist/fenix-ui2.esm.js.map +1 -1
  58. package/dist/fenix-ui4.esm.js +3817 -183
  59. package/dist/fenix-ui4.esm.js.map +1 -1
  60. package/dist/fenix-ui5.esm.js +1 -0
  61. package/dist/fenix-ui5.esm.js.map +1 -1
  62. package/dist/index.d.ts +1 -0
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +14 -0
  65. package/dist/index.js.map +1 -1
  66. package/dist/plugins/auto-import.d.ts.map +1 -1
  67. package/dist/plugins/auto-import.js +1 -0
  68. package/dist/plugins/auto-import.js.map +1 -1
  69. package/package.json +5 -1
@@ -2,9 +2,19 @@ var __typeError = (msg) => {
2
2
  throw TypeError(msg);
3
3
  };
4
4
  var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
5
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
5
6
  var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
7
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
6
8
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
7
- var _FxToast_instances, wirePause_fn, startTimer_fn, pauseTimer_fn, resumeTimer_fn, syncProgress_fn, render_fn, syncMore_fn;
9
+ var __privateWrapper = (obj, member, setter, getter) => ({
10
+ set _(value) {
11
+ __privateSet(obj, member, value, setter);
12
+ },
13
+ get _() {
14
+ return __privateGet(obj, member, getter);
15
+ }
16
+ });
17
+ var _built, _eventsBound, _initialized, _defaultValue, _value, _dirty, _sourceMode, _lastRange, _rafId, _emitTimer, _lastLimitError, _snapshotTimer, _history, _historyIndex, _imagePicker, _mediaFigure, _linkAnchor, _resizeDispose, _dialogReturnFocus, _dialogResolve, _disposers, _selectionRaf, _bodyOverflow, _FxEditor_instances, on_fn, bindEvents_fn, syncChrome_fn, applyCssVars_fn, updateEmpty_fn, updateCount_fn, fits_fn, limitError_fn, validate_fn, cleanHtml_fn, serialize_fn, applyValue_fn, scheduleEmit_fn, emitNow_fn, emitChange_fn, pushHistory_fn, scheduleSnapshot_fn, historyUndo_fn, historyRedo_fn, bindGlobalSelection_fn, restoreSelection_fn, syncBodyOverflow_fn, updateSourceUi_fn, releaseDialog_fn, renderToolbar_fn, updateToolbar_fn, handleToolbarKeydown_fn, moveToolbarTabStop_fn, trapDialogFocus_fn, execCommand_fn, afterCommand_fn, action_fn, handleKeydown_fn, tabInTable_fn, handleBeforeInput_fn, handlePaste_fn, handleDrop_fn, insertPlainText_fn, toggleSource_fn, applySource_fn, cancelSource_fn, openDialog_fn, fieldHtml_fn, submitDialog_fn, openLinkDialog_fn, openImageDialog_fn, toEmbedUrl_fn, openVideoDialog_fn, decorateMediaFigures_fn, syncQuickActions_fn, selectAnchor_fn, handleLinkOverlayClick_fn, openLink_fn, removeLink_fn, showLinkOverlay_fn, hideLinkOverlay_fn, positionLinkOverlay_fn, handleEditorClick_fn, showMediaOverlay_fn, hideMediaOverlay_fn, deleteMediaFigure_fn, positionMediaOverlay_fn, startResize_fn, endResize_fn, handleDoubleClick_fn, insertImage_fn, insertVideo_fn, insertEmbed_fn, insertTable_fn, insertColumns_fn, showTablePanel_fn, showColumnsPanel_fn, hidePanel_fn, currentTable_fn, tableAction_fn, handleImageFile_fn, fileToDataUrl_fn, error_fn, _FxToast_instances, wirePause_fn, startTimer_fn, pauseTimer_fn, resumeTimer_fn, syncProgress_fn, render_fn, syncMore_fn;
8
18
  import { a as FENIX_ICON_BASE_CSS, f as fenixIconHtml, g as esc, l as loadFenixIconsFont, i as isFenixIconName } from "./assets/font-DPeWTz-n.js";
9
19
  import { h, j } from "./assets/font-DPeWTz-n.js";
10
20
  const lightTokens = {
@@ -1177,6 +1187,11 @@ function defineFxSelect() {
1177
1187
  return defineElement("fx-select", FxSelect);
1178
1188
  }
1179
1189
  defineFxSelect();
1190
+ const INPUT_TYPES = /* @__PURE__ */ new Set(["text", "number", "password"]);
1191
+ function normalizeInputType(value) {
1192
+ const type = (value || "text").toLowerCase();
1193
+ return INPUT_TYPES.has(type) ? type : "text";
1194
+ }
1180
1195
  const _FxInput = class _FxInput extends FxFormElement {
1181
1196
  // `value` fica FORA da observação: refleti-lo a cada tecla não pode
1182
1197
  // re-renderizar o template, senão o campo perde o foco ao digitar.
@@ -1200,6 +1215,13 @@ const _FxInput = class _FxInput extends FxFormElement {
1200
1215
  "valid"
1201
1216
  ];
1202
1217
  }
1218
+ /** Tipo do input interno. Padrão: `'text'`. */
1219
+ get type() {
1220
+ return normalizeInputType(this.getAttr("type", "text"));
1221
+ }
1222
+ set type(value) {
1223
+ this.setAttribute("type", normalizeInputType(value));
1224
+ }
1203
1225
  /** Tamanho do campo. Padrão: `'md'`. */
1204
1226
  get size() {
1205
1227
  const s = this.getAttr("size", "md");
@@ -1223,7 +1245,7 @@ const _FxInput = class _FxInput extends FxFormElement {
1223
1245
  this.toggleAttr("disabled", Boolean(value));
1224
1246
  }
1225
1247
  render() {
1226
- const type = this.getAttr("type", "text");
1248
+ const type = this.type;
1227
1249
  const placeholder = this.getAttr("placeholder");
1228
1250
  const readonly = this.hasAttr("readonly");
1229
1251
  const disabled = this.hasAttr("disabled");
@@ -1292,6 +1314,8 @@ const _FxInput = class _FxInput extends FxFormElement {
1292
1314
  syncInternalControl() {
1293
1315
  const field = this.root.querySelector(".field");
1294
1316
  const value = this.getAttr("value");
1317
+ const type = this.type;
1318
+ if (field && field.type !== type) field.type = type;
1295
1319
  if (field && field.value !== value) field.value = value;
1296
1320
  }
1297
1321
  };
@@ -3795,13 +3819,13 @@ const _FxTable = class _FxTable extends FxElement {
3795
3819
  return [...this.querySelectorAll("fx-column")].map((c) => {
3796
3820
  const tpl = this._resolveCellTemplate(c);
3797
3821
  const direct = tpl ? void 0 : c.innerHTML.trim();
3798
- const align = c.getAttribute("align") ?? "left";
3822
+ const align2 = c.getAttribute("align") ?? "left";
3799
3823
  return {
3800
3824
  field: c.getAttribute("field") ?? "",
3801
3825
  header: c.getAttribute("header") ?? c.getAttribute("field") ?? "",
3802
3826
  sortable: c.hasAttribute("sortable"),
3803
3827
  filterable: c.hasAttribute("filterable"),
3804
- align: ["left", "center", "right", "justify", "start", "end"].includes(align) ? align : "left",
3828
+ align: ["left", "center", "right", "justify", "start", "end"].includes(align2) ? align2 : "left",
3805
3829
  template: tpl ?? void 0,
3806
3830
  directTemplate: direct || void 0
3807
3831
  };
@@ -4885,191 +4909,3759 @@ function defineFxTextarea() {
4885
4909
  return defineElement("fx-textarea", FxTextarea);
4886
4910
  }
4887
4911
  defineFxTextarea();
4888
- const _FxDialog = class _FxDialog extends FxElement {
4889
- constructor() {
4890
- super(...arguments);
4891
- this._previouslyFocused = null;
4912
+ const ALLOWED_TAGS = /* @__PURE__ */ new Set([
4913
+ "p",
4914
+ "br",
4915
+ "strong",
4916
+ "b",
4917
+ "em",
4918
+ "i",
4919
+ "u",
4920
+ "s",
4921
+ "del",
4922
+ "ins",
4923
+ "sub",
4924
+ "sup",
4925
+ "code",
4926
+ "pre",
4927
+ "blockquote",
4928
+ "h1",
4929
+ "h2",
4930
+ "h3",
4931
+ "h4",
4932
+ "h5",
4933
+ "h6",
4934
+ "ul",
4935
+ "ol",
4936
+ "li",
4937
+ "a",
4938
+ "img",
4939
+ "figure",
4940
+ "figcaption",
4941
+ "video",
4942
+ "source",
4943
+ "iframe",
4944
+ "table",
4945
+ "thead",
4946
+ "tbody",
4947
+ "tfoot",
4948
+ "tr",
4949
+ "th",
4950
+ "td",
4951
+ "colgroup",
4952
+ "col",
4953
+ "hr",
4954
+ "span",
4955
+ "div",
4956
+ "small",
4957
+ "mark"
4958
+ ]);
4959
+ const DROP_CONTENT_TAGS = /* @__PURE__ */ new Set([
4960
+ "script",
4961
+ "style",
4962
+ "template",
4963
+ "noscript",
4964
+ "svg",
4965
+ "math",
4966
+ "object",
4967
+ "embed",
4968
+ "form",
4969
+ "input",
4970
+ "button",
4971
+ "textarea",
4972
+ "select",
4973
+ "option",
4974
+ "meta",
4975
+ "link",
4976
+ "base",
4977
+ "title",
4978
+ "head",
4979
+ "audio",
4980
+ "canvas",
4981
+ "map",
4982
+ "area"
4983
+ ]);
4984
+ const GLOBAL_ATTRS = /* @__PURE__ */ new Set([
4985
+ "title",
4986
+ "dir",
4987
+ "lang",
4988
+ "role"
4989
+ ]);
4990
+ const TAG_ATTRS = {
4991
+ a: /* @__PURE__ */ new Set(["href", "target", "rel", "download"]),
4992
+ img: /* @__PURE__ */ new Set(["src", "alt", "width", "height", "loading", "decoding"]),
4993
+ video: /* @__PURE__ */ new Set(["src", "poster", "controls", "preload", "width", "height", "muted", "loop", "playsinline"]),
4994
+ source: /* @__PURE__ */ new Set(["src", "type"]),
4995
+ iframe: /* @__PURE__ */ new Set([
4996
+ "src",
4997
+ "title",
4998
+ "width",
4999
+ "height",
5000
+ "allow",
5001
+ "allowfullscreen",
5002
+ "sandbox",
5003
+ "referrerpolicy",
5004
+ "loading"
5005
+ ]),
5006
+ td: /* @__PURE__ */ new Set(["colspan", "rowspan", "scope", "style", "class", "data-align"]),
5007
+ th: /* @__PURE__ */ new Set(["colspan", "rowspan", "scope", "style", "class", "data-align"]),
5008
+ table: /* @__PURE__ */ new Set(["style", "class", "role", "data-cols", "data-stack"]),
5009
+ col: /* @__PURE__ */ new Set(["span", "style", "width"]),
5010
+ colgroup: /* @__PURE__ */ new Set(["span", "style"]),
5011
+ figure: /* @__PURE__ */ new Set(["class", "style", "data-align", "data-cols"]),
5012
+ figcaption: /* @__PURE__ */ new Set(["class", "style"]),
5013
+ span: /* @__PURE__ */ new Set(["class", "style"]),
5014
+ div: /* @__PURE__ */ new Set(["class", "style"]),
5015
+ p: /* @__PURE__ */ new Set(["class", "style"]),
5016
+ h1: /* @__PURE__ */ new Set(["class", "style"]),
5017
+ h2: /* @__PURE__ */ new Set(["class", "style"]),
5018
+ h3: /* @__PURE__ */ new Set(["class", "style"]),
5019
+ h4: /* @__PURE__ */ new Set(["class", "style"]),
5020
+ h5: /* @__PURE__ */ new Set(["class", "style"]),
5021
+ h6: /* @__PURE__ */ new Set(["class", "style"]),
5022
+ blockquote: /* @__PURE__ */ new Set(["class", "style"]),
5023
+ pre: /* @__PURE__ */ new Set(["class", "style"]),
5024
+ code: /* @__PURE__ */ new Set(["class"]),
5025
+ li: /* @__PURE__ */ new Set(["class", "style", "value"]),
5026
+ ol: /* @__PURE__ */ new Set(["class", "style", "start", "type"]),
5027
+ ul: /* @__PURE__ */ new Set(["class", "style"]),
5028
+ hr: /* @__PURE__ */ new Set(["class", "style"]),
5029
+ small: /* @__PURE__ */ new Set(["class"]),
5030
+ mark: /* @__PURE__ */ new Set(["class", "style"])
5031
+ };
5032
+ const ALLOWED_STYLE_PROPS = /* @__PURE__ */ new Set([
5033
+ "color",
5034
+ "background-color",
5035
+ "text-align",
5036
+ "font-size",
5037
+ "font-family",
5038
+ "font-weight",
5039
+ "font-style",
5040
+ "text-decoration",
5041
+ "text-decoration-line",
5042
+ "vertical-align",
5043
+ "width",
5044
+ "height",
5045
+ "max-width",
5046
+ "min-width",
5047
+ "margin",
5048
+ "margin-top",
5049
+ "margin-right",
5050
+ "margin-bottom",
5051
+ "margin-left",
5052
+ "padding",
5053
+ "padding-top",
5054
+ "padding-right",
5055
+ "padding-bottom",
5056
+ "padding-left",
5057
+ "border",
5058
+ "border-top",
5059
+ "border-right",
5060
+ "border-bottom",
5061
+ "border-left",
5062
+ "border-collapse",
5063
+ "border-spacing",
5064
+ "border-radius",
5065
+ "float",
5066
+ "display",
5067
+ "line-height",
5068
+ "white-space",
5069
+ "word-break",
5070
+ "overflow-wrap",
5071
+ "list-style-type"
5072
+ ]);
5073
+ const ALLOWED_CLASS_RE = /^fx-[a-z0-9-]+$/i;
5074
+ const ALLOWED_EMBED_HOSTS = /* @__PURE__ */ new Set([
5075
+ "www.youtube.com",
5076
+ "youtube.com",
5077
+ "www.youtube-nocookie.com",
5078
+ "youtube-nocookie.com",
5079
+ "player.vimeo.com"
5080
+ ]);
5081
+ const SAFE_LINK_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:"]);
5082
+ const SAFE_MEDIA_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
5083
+ const SAFE_IMAGE_DATA_RE = /^data:image\/(?:png|jpe?g|gif|webp|avif);base64,[a-z0-9+/=\s]+$/i;
5084
+ const DEFAULT_MAX_DATA_IMAGE_BYTES = 2 * 1024 * 1024;
5085
+ const DEFAULT_MAX_DATA_VIDEO_BYTES = 4 * 1024 * 1024;
5086
+ const FORBIDDEN_ATTRS = /* @__PURE__ */ new Set([
5087
+ "contenteditable",
5088
+ "spellcheck",
5089
+ "tabindex",
5090
+ "autofocus",
5091
+ "id",
5092
+ "onclick",
5093
+ "ondblclick",
5094
+ "onmousedown",
5095
+ "onmouseup",
5096
+ "onmouseover",
5097
+ "onmouseout",
5098
+ "onmousemove",
5099
+ "onkeydown",
5100
+ "onkeyup",
5101
+ "onkeypress",
5102
+ "onload",
5103
+ "onerror",
5104
+ "onfocus",
5105
+ "onblur",
5106
+ "oninput",
5107
+ "onchange",
5108
+ "onsubmit",
5109
+ "onreset",
5110
+ "onselect",
5111
+ "ondrag",
5112
+ "ondrop",
5113
+ "onpaste",
5114
+ "oncopy",
5115
+ "oncut",
5116
+ "onscroll",
5117
+ "onwheel",
5118
+ "onanimationstart",
5119
+ "onanimationend",
5120
+ "ontransitionend",
5121
+ "formaction",
5122
+ "srcdoc",
5123
+ "xlink:href",
5124
+ "xmlns",
5125
+ "http-equiv",
5126
+ "ping"
5127
+ ]);
5128
+ const DEFAULTS = {
5129
+ allowBase64Images: true,
5130
+ allowBase64Videos: false,
5131
+ allowEmbeds: true,
5132
+ maxDataImageBytes: DEFAULT_MAX_DATA_IMAGE_BYTES,
5133
+ maxDataVideoBytes: DEFAULT_MAX_DATA_VIDEO_BYTES
5134
+ };
5135
+ const SAFE_DATA_VIDEO_RE = /^data:video\/(?:mp4|webm);base64,[a-z0-9+/=\s]+$/i;
5136
+ function cleanRawUrl(value) {
5137
+ return value.replace(/[\u0000-\u001F\u007F\s]+/g, "").trim();
5138
+ }
5139
+ function isRelativeUrl(value) {
5140
+ return !value.startsWith("//") && /^(?:#|\/|\.{1,2}\/)/.test(value);
5141
+ }
5142
+ function sanitizeLinkUrl(value) {
5143
+ const raw = cleanRawUrl(value);
5144
+ if (!raw) return "";
5145
+ if (isRelativeUrl(raw)) return raw;
5146
+ const lower = raw.toLowerCase();
5147
+ if (lower.startsWith("javascript:") || lower.startsWith("vbscript:") || lower.startsWith("data:") || lower.startsWith("file:") || lower.startsWith("blob:")) {
5148
+ return "";
4892
5149
  }
4893
- static get observedAttributes() {
4894
- return ["open", "size", "heading"];
5150
+ try {
5151
+ const url = new URL(raw);
5152
+ return SAFE_LINK_PROTOCOLS.has(url.protocol) ? raw : "";
5153
+ } catch {
5154
+ return /^[a-z0-9][a-z0-9._~!$&'()*+,;=:@%/?#-]*$/i.test(raw) ? raw : "";
4895
5155
  }
4896
- get open() {
4897
- return this.hasAttr("open");
5156
+ }
5157
+ function sanitizeMediaUrl(value, tag, options = {}) {
5158
+ const opts = { ...DEFAULTS, ...options };
5159
+ const raw = cleanRawUrl(value);
5160
+ if (!raw) return "";
5161
+ if (raw.toLowerCase().startsWith("data:")) {
5162
+ if (tag === "img" && opts.allowBase64Images) {
5163
+ return SAFE_IMAGE_DATA_RE.test(raw) && raw.length <= opts.maxDataImageBytes ? raw : "";
5164
+ }
5165
+ if ((tag === "video" || tag === "source") && opts.allowBase64Videos) {
5166
+ return SAFE_DATA_VIDEO_RE.test(raw) && raw.length <= opts.maxDataVideoBytes ? raw : "";
5167
+ }
5168
+ return "";
4898
5169
  }
4899
- set open(value) {
4900
- this.toggleAttr("open", value);
5170
+ if (isRelativeUrl(raw)) return raw;
5171
+ const lower = raw.toLowerCase();
5172
+ if (lower.startsWith("javascript:") || lower.startsWith("vbscript:") || lower.startsWith("file:") || lower.startsWith("blob:")) {
5173
+ return "";
4901
5174
  }
4902
- render() {
4903
- const isOpen = this.open;
4904
- const heading = this.getAttr("heading");
4905
- this.setTemplate(`
4906
- <div class="overlay" part="overlay" ${isOpen ? "" : "hidden"}>
4907
- <div class="dialog" role="dialog" aria-modal="true" aria-label="${esc(heading)}" tabindex="-1">
4908
- <header>
4909
- <h2>${esc(heading)}</h2>
4910
- <button type="button" class="close" part="close" aria-label="Fechar">×</button>
4911
- </header>
4912
- <div class="body"><slot></slot></div>
4913
- <footer part="footer"><slot name="footer"></slot></footer>
4914
- </div>
4915
- </div>
4916
- `);
4917
- if (!isOpen) return;
4918
- this._cleanup?.();
4919
- this._cleanup = void 0;
4920
- const overlay = this.root.querySelector(".overlay");
4921
- const closeBtn = this.root.querySelector(".close");
4922
- const close = () => {
4923
- this.open = false;
4924
- this._cleanup?.();
4925
- this._cleanup = void 0;
4926
- this._restoreFocus();
4927
- this.dispatchEvent(new CustomEvent("close", { bubbles: true, composed: true }));
4928
- };
4929
- overlay?.addEventListener("click", (e) => {
4930
- if (e.target === overlay) close();
4931
- });
4932
- closeBtn?.addEventListener("click", close);
4933
- const onKey = (e) => {
4934
- if (e.key === "Escape") {
4935
- e.stopPropagation();
4936
- close();
4937
- return;
4938
- }
4939
- if (e.key === "Tab") this._trapFocus(e);
4940
- };
4941
- document.addEventListener("keydown", onKey);
4942
- this._cleanup = () => document.removeEventListener("keydown", onKey);
4943
- requestAnimationFrame(() => {
4944
- this._previouslyFocused = document.activeElement;
4945
- this.root.querySelector(".dialog")?.focus();
4946
- });
4947
- this.dispatchEvent(new CustomEvent("open", { bubbles: true, composed: true }));
5175
+ try {
5176
+ const url = new URL(raw);
5177
+ return SAFE_MEDIA_PROTOCOLS.has(url.protocol) ? raw : "";
5178
+ } catch {
5179
+ return "";
4948
5180
  }
4949
- /** Mantém o foco dentro do modal (WCAG 2.4.3 / 2.1.2). */
4950
- _trapFocus(e) {
4951
- const dialog = this.root.querySelector(".dialog");
4952
- if (!dialog) return;
4953
- const focusables = dialog.querySelectorAll(
4954
- 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
4955
- );
4956
- if (!focusables.length) return;
4957
- const first = focusables[0];
4958
- const last = focusables[focusables.length - 1];
4959
- const active = this.root.activeElement;
4960
- if (e.shiftKey && (active === first || active === dialog)) {
4961
- e.preventDefault();
4962
- last.focus();
4963
- } else if (!e.shiftKey && active === last) {
4964
- e.preventDefault();
4965
- first.focus();
5181
+ }
5182
+ function sanitizeEmbedUrl(value) {
5183
+ let raw = cleanRawUrl(value);
5184
+ if (!raw) return "";
5185
+ if (raw.startsWith("//")) raw = `https:${raw}`;
5186
+ let url;
5187
+ try {
5188
+ url = new URL(raw);
5189
+ } catch {
5190
+ return "";
5191
+ }
5192
+ if (url.protocol !== "https:") return "";
5193
+ if (!ALLOWED_EMBED_HOSTS.has(url.hostname.toLowerCase())) return "";
5194
+ const path = url.pathname.toLowerCase();
5195
+ const isValidPath = url.hostname.includes("youtube") && path.startsWith("/embed/") || url.hostname.includes("vimeo") && path.startsWith("/video/");
5196
+ if (!isValidPath) return "";
5197
+ return url.toString();
5198
+ }
5199
+ function sanitizeStyle(value) {
5200
+ if (!value) return "";
5201
+ const probe = document.createElement("span");
5202
+ probe.setAttribute("style", value);
5203
+ const out = [];
5204
+ for (let i = 0; i < probe.style.length; i++) {
5205
+ const prop = probe.style.item(i);
5206
+ if (!ALLOWED_STYLE_PROPS.has(prop)) continue;
5207
+ const val = probe.style.getPropertyValue(prop).trim();
5208
+ if (!val) continue;
5209
+ if (/\\/.test(val)) continue;
5210
+ if (/url\s*\(|expression\s*\(|javascript\s*:|@import|behavior\s*:|-moz-binding/i.test(val)) continue;
5211
+ out.push(`${prop}: ${val}`);
5212
+ }
5213
+ return out.join("; ");
5214
+ }
5215
+ function sanitizeAttributes(el, tag, opts) {
5216
+ for (const attr of Array.from(el.attributes)) {
5217
+ const name = attr.name.toLowerCase();
5218
+ const value = attr.value;
5219
+ if (FORBIDDEN_ATTRS.has(name) || name.startsWith("on")) {
5220
+ el.removeAttribute(attr.name);
5221
+ continue;
5222
+ }
5223
+ const allowed = GLOBAL_ATTRS.has(name) || TAG_ATTRS[tag]?.has(name);
5224
+ if (!allowed) {
5225
+ el.removeAttribute(attr.name);
5226
+ continue;
5227
+ }
5228
+ if (name === "style") {
5229
+ const safe = sanitizeStyle(value);
5230
+ if (safe) el.setAttribute("style", safe);
5231
+ else el.removeAttribute("style");
5232
+ continue;
5233
+ }
5234
+ if (name === "class") {
5235
+ const classes = value.split(/\s+/).filter((c) => ALLOWED_CLASS_RE.test(c));
5236
+ if (classes.length) el.setAttribute("class", classes.join(" "));
5237
+ else el.removeAttribute("class");
5238
+ continue;
5239
+ }
5240
+ if (name === "href") {
5241
+ const safe = sanitizeLinkUrl(value);
5242
+ if (safe) el.setAttribute("href", safe);
5243
+ else el.removeAttribute("href");
5244
+ continue;
5245
+ }
5246
+ if (name === "src") {
5247
+ const safe = sanitizeMediaUrl(value, tag, opts);
5248
+ if (safe) el.setAttribute("src", safe);
5249
+ else el.removeAttribute("src");
5250
+ continue;
5251
+ }
5252
+ if (name === "poster") {
5253
+ const safe = sanitizeMediaUrl(value, "img", opts);
5254
+ if (safe) el.setAttribute("poster", safe);
5255
+ else el.removeAttribute("poster");
5256
+ continue;
5257
+ }
5258
+ if (name === "target") {
5259
+ const safe = value === "_self" || value === "_blank" ? value : "_blank";
5260
+ el.setAttribute("target", safe);
5261
+ continue;
5262
+ }
5263
+ if (name === "colspan" || name === "rowspan" || name === "span" || name === "start") {
5264
+ const n = Number(value);
5265
+ if (Number.isInteger(n) && n > 0 && n <= 1e3) el.setAttribute(name, String(n));
5266
+ else el.removeAttribute(name);
5267
+ continue;
5268
+ }
5269
+ if (name === "width" || name === "height") {
5270
+ if (/^\d{1,4}(?:px|%)?$/i.test(value.trim())) el.setAttribute(name, value.trim());
5271
+ else el.removeAttribute(name);
5272
+ continue;
4966
5273
  }
4967
5274
  }
4968
- _restoreFocus() {
4969
- if (this._previouslyFocused?.isConnected) this._previouslyFocused.focus();
4970
- this._previouslyFocused = null;
5275
+ if (tag === "a") {
5276
+ const href = el.getAttribute("href");
5277
+ if (!href) {
5278
+ el.removeAttribute("target");
5279
+ el.removeAttribute("rel");
5280
+ } else if (el.getAttribute("target") === "_blank") {
5281
+ el.setAttribute("rel", "noopener noreferrer");
5282
+ }
4971
5283
  }
4972
- disconnectedCallback() {
4973
- super.disconnectedCallback();
4974
- this._cleanup?.();
4975
- this._cleanup = void 0;
4976
- this._restoreFocus();
5284
+ if (tag === "iframe") {
5285
+ el.setAttribute("sandbox", "allow-scripts allow-same-origin allow-presentation");
5286
+ el.setAttribute("allowfullscreen", "");
5287
+ el.setAttribute("loading", "lazy");
5288
+ el.setAttribute("referrerpolicy", "strict-origin-when-cross-origin");
4977
5289
  }
4978
- };
4979
- _FxDialog.styles = css`
4980
- :host { display: contents; }
4981
- .overlay {
4982
- position: fixed;
4983
- inset: 0;
4984
- background: color-mix(in srgb, var(--fx-text-default, #000) 45%, transparent);
4985
- backdrop-filter: blur(3px);
4986
- -webkit-backdrop-filter: blur(3px);
4987
- display: flex;
4988
- align-items: center;
4989
- justify-content: center;
4990
- z-index: var(--fx-z-modal, 1100);
4991
- animation: fade var(--fx-motion-duration-fast) var(--fx-motion-easing);
5290
+ if (tag === "img") {
5291
+ if (!el.hasAttribute("alt")) el.setAttribute("alt", "");
5292
+ el.setAttribute("loading", "lazy");
5293
+ el.setAttribute("decoding", "async");
5294
+ }
5295
+ if (tag === "video") {
5296
+ el.setAttribute("controls", "");
5297
+ el.setAttribute("preload", "metadata");
5298
+ }
5299
+ }
5300
+ function sanitizeIframe(el) {
5301
+ const src = el.getAttribute("src") ?? "";
5302
+ const safe = sanitizeEmbedUrl(src);
5303
+ if (!safe) return false;
5304
+ el.setAttribute("src", safe);
5305
+ return true;
5306
+ }
5307
+ function unwrap$2(el, opts) {
5308
+ const parent = el.parentNode;
5309
+ if (!parent) {
5310
+ el.remove();
5311
+ return;
5312
+ }
5313
+ for (const child of Array.from(el.childNodes)) {
5314
+ sanitizeNode(child, opts);
5315
+ parent.insertBefore(child, el);
5316
+ }
5317
+ el.remove();
5318
+ }
5319
+ function sanitizeNode(node, opts) {
5320
+ if (node.nodeType === Node.TEXT_NODE) return;
5321
+ if (node.nodeType === Node.COMMENT_NODE) {
5322
+ node.parentNode?.removeChild(node);
5323
+ return;
5324
+ }
5325
+ if (node.nodeType !== Node.ELEMENT_NODE) {
5326
+ node.parentNode?.removeChild(node);
5327
+ return;
5328
+ }
5329
+ const el = node;
5330
+ const tag = el.tagName.toLowerCase();
5331
+ if (DROP_CONTENT_TAGS.has(tag)) {
5332
+ el.remove();
5333
+ return;
5334
+ }
5335
+ if (tag === "iframe") {
5336
+ if (!opts.allowEmbeds || !sanitizeIframe(el)) el.remove();
5337
+ else sanitizeAttributes(el, tag, opts);
5338
+ return;
5339
+ }
5340
+ if (!ALLOWED_TAGS.has(tag)) {
5341
+ unwrap$2(el, opts);
5342
+ return;
5343
+ }
5344
+ sanitizeAttributes(el, tag, opts);
5345
+ if ((tag === "img" || tag === "video" || tag === "source") && !el.hasAttribute("src")) {
5346
+ el.remove();
5347
+ return;
5348
+ }
5349
+ for (const child of Array.from(el.childNodes)) sanitizeNode(child, opts);
5350
+ }
5351
+ function sanitizeBody(html, options = {}) {
5352
+ const opts = { ...DEFAULTS, ...options };
5353
+ if (!html) return null;
5354
+ if (typeof DOMParser === "undefined" || typeof document === "undefined") return null;
5355
+ const doc = new DOMParser().parseFromString(
5356
+ `<!doctype html><html><body>${html}</body></html>`,
5357
+ "text/html"
5358
+ );
5359
+ for (const child of Array.from(doc.body.childNodes)) sanitizeNode(child, opts);
5360
+ return doc.body;
5361
+ }
5362
+ function sanitizeHtml(html, options = {}) {
5363
+ const body = sanitizeBody(html, options);
5364
+ return body ? body.innerHTML : "";
5365
+ }
5366
+ const BLOCK_TAGS = /* @__PURE__ */ new Set([
5367
+ "p",
5368
+ "h1",
5369
+ "h2",
5370
+ "h3",
5371
+ "h4",
5372
+ "h5",
5373
+ "h6",
5374
+ "blockquote",
5375
+ "pre",
5376
+ "ul",
5377
+ "ol",
5378
+ "li",
5379
+ "table",
5380
+ "figure",
5381
+ "hr",
5382
+ "div"
5383
+ ]);
5384
+ const INLINE_TAGS = /* @__PURE__ */ new Set([
5385
+ "strong",
5386
+ "b",
5387
+ "em",
5388
+ "i",
5389
+ "u",
5390
+ "s",
5391
+ "del",
5392
+ "ins",
5393
+ "sub",
5394
+ "sup",
5395
+ "code",
5396
+ "a",
5397
+ "span",
5398
+ "small",
5399
+ "mark",
5400
+ "br"
5401
+ ]);
5402
+ function isElement(node) {
5403
+ return node.nodeType === Node.ELEMENT_NODE;
5404
+ }
5405
+ function tagOf(node) {
5406
+ return isElement(node) ? node.tagName.toLowerCase() : "";
5407
+ }
5408
+ function hasBlockChild(el) {
5409
+ return Array.from(el.children).some((child) => BLOCK_TAGS.has(tagOf(child)) || tagOf(child) === "figure");
5410
+ }
5411
+ function unwrap$1(el) {
5412
+ const parent = el.parentNode;
5413
+ if (!parent) {
5414
+ el.remove();
5415
+ return;
5416
+ }
5417
+ while (el.firstChild) parent.insertBefore(el.firstChild, el);
5418
+ el.remove();
5419
+ }
5420
+ function rename(el, tag) {
5421
+ const doc = el.ownerDocument;
5422
+ const next = doc.createElement(tag);
5423
+ for (const attr of Array.from(el.attributes)) next.setAttribute(attr.name, attr.value);
5424
+ while (el.firstChild) next.appendChild(el.firstChild);
5425
+ el.replaceWith(next);
5426
+ return next;
5427
+ }
5428
+ function normalizeChildren(parent, options) {
5429
+ for (const node of Array.from(parent.childNodes)) {
5430
+ if (node.nodeType === Node.COMMENT_NODE) {
5431
+ node.remove();
5432
+ continue;
4992
5433
  }
4993
- .overlay[hidden] { display: none; }
4994
- @keyframes fade { from { opacity: 0; } to { opacity: 1; } }
4995
- .dialog {
4996
- background: var(--fx-surface-background);
4997
- border: 1px solid var(--fx-border-default);
4998
- border-radius: var(--fx-radius-lg);
4999
- box-shadow: var(--fx-shadow-xl);
5000
- /* Largura customizável por instância: defina --fx-dialog-width no elemento. */
5001
- width: min(var(--fx-dialog-width, 560px), calc(100vw - 32px));
5002
- max-height: calc(100vh - 64px);
5003
- display: flex;
5004
- flex-direction: column;
5005
- font-family: var(--fx-font-family);
5006
- animation: pop var(--fx-motion-duration-normal) var(--fx-motion-easing);
5434
+ if (node.nodeType === Node.TEXT_NODE) {
5435
+ node.nodeValue = (node.nodeValue ?? "").replace(/\u00a0/g, " ");
5436
+ continue;
5007
5437
  }
5008
- @keyframes pop { from { opacity: 0; transform: scale(0.96); } to { opacity: 1; transform: scale(1); } }
5009
- :host([size='sm']) .dialog { width: min(var(--fx-dialog-width, 400px), calc(100vw - 32px)); }
5010
- :host([size='lg']) .dialog { width: min(var(--fx-dialog-width, 800px), calc(100vw - 32px)); }
5011
- /* Mobile: dialog ocupa praticamente toda a largura da tela. */
5012
- @media (max-width: 640px) {
5013
- .dialog, :host([size='sm']) .dialog, :host([size='lg']) .dialog {
5014
- width: calc(100vw - 16px);
5015
- max-width: calc(100vw - 16px);
5438
+ if (!isElement(node)) continue;
5439
+ let el = node;
5440
+ let tag = tagOf(el);
5441
+ if (tag === "b") el = rename(el, "strong");
5442
+ else if (tag === "i") el = rename(el, "em");
5443
+ else if (tag === "strike" || tag === "del") el = rename(el, "s");
5444
+ tag = tagOf(el);
5445
+ if (tag === "font") {
5446
+ const span = rename(el, "span");
5447
+ const styles = [];
5448
+ const color = el.getAttribute("color");
5449
+ const face = el.getAttribute("face");
5450
+ if (color) styles.push(`color: ${color}`);
5451
+ if (face) styles.push(`font-family: ${face}`);
5452
+ if (styles.length) span.setAttribute("style", styles.join("; "));
5453
+ el = span;
5454
+ tag = "span";
5455
+ }
5456
+ if (tag === "div" || tag === "section" || tag === "article" || tag === "header" || tag === "footer" || tag === "main" || tag === "aside") {
5457
+ if (hasBlockChild(el)) {
5458
+ unwrap$1(el);
5459
+ continue;
5016
5460
  }
5461
+ el = rename(el, "p");
5462
+ tag = "p";
5017
5463
  }
5018
- header {
5019
- display: flex;
5020
- align-items: center;
5021
- justify-content: space-between;
5022
- padding: var(--fx-space-lg) var(--fx-space-xl);
5023
- border-bottom: 1px solid var(--fx-border-default);
5464
+ if (tag === "p" && hasBlockChild(el)) {
5465
+ unwrap$1(el);
5466
+ continue;
5024
5467
  }
5025
- h2 {
5026
- margin: 0;
5027
- font-size: calc(var(--fx-font-size) + 2px);
5028
- font-weight: 600;
5029
- color: var(--fx-text-default);
5468
+ if (tag === "span" && !el.hasAttribute("style") && !el.hasAttribute("class")) {
5469
+ unwrap$1(el);
5470
+ continue;
5030
5471
  }
5031
- .close {
5032
- border: none;
5033
- background: transparent;
5034
- color: var(--fx-text-muted);
5035
- font-size: calc(var(--fx-font-size) + 6px);
5036
- cursor: pointer;
5037
- line-height: 1;
5038
- padding: 0 var(--fx-space-xs);
5039
- border-radius: var(--fx-radius-full);
5472
+ if (!options.forEditor) {
5473
+ el.removeAttribute("contenteditable");
5474
+ el.removeAttribute("spellcheck");
5475
+ el.removeAttribute("tabindex");
5476
+ }
5477
+ if (tag === "a") {
5478
+ if (!el.getAttribute("href")) {
5479
+ el.removeAttribute("target");
5480
+ el.removeAttribute("rel");
5481
+ } else if (el.getAttribute("target") === "_blank") {
5482
+ el.setAttribute("rel", "noopener noreferrer");
5483
+ }
5040
5484
  }
5041
- .close:hover { color: var(--fx-color-danger); }
5042
- .body {
5043
- padding: var(--fx-space-lg) var(--fx-space-xl);
5044
- overflow-y: auto;
5045
- color: var(--fx-text-default);
5046
- font-size: var(--fx-font-size);
5485
+ normalizeChildren(el, options);
5486
+ }
5487
+ }
5488
+ function wrapInlineChildren(cell, doc) {
5489
+ if (Array.from(cell.children).some((child) => BLOCK_TAGS.has(tagOf(child)))) return;
5490
+ const p = doc.createElement("p");
5491
+ while (cell.firstChild) p.appendChild(cell.firstChild);
5492
+ if (!p.textContent?.trim() && !p.querySelector("img,video,iframe,br")) {
5493
+ p.innerHTML = "<br>";
5494
+ }
5495
+ cell.appendChild(p);
5496
+ }
5497
+ function normalizeFigures(body, options) {
5498
+ const doc = body.ownerDocument;
5499
+ for (const media of Array.from(body.querySelectorAll("img, video, iframe"))) {
5500
+ if (media.closest("figure")) continue;
5501
+ const anchor = media.closest("a");
5502
+ const wrapper = anchor && anchor.querySelectorAll("*").length === 1 ? anchor : media;
5503
+ const figure = doc.createElement("figure");
5504
+ const kind = tagOf(media);
5505
+ figure.className = kind === "img" ? "fx-image" : kind === "video" ? "fx-video" : "fx-embed";
5506
+ if (kind === "img") figure.setAttribute("data-align", "center");
5507
+ const p = wrapper.closest("p");
5508
+ if (p) p.after(figure);
5509
+ else wrapper.parentNode?.insertBefore(figure, wrapper);
5510
+ figure.appendChild(wrapper);
5511
+ if (options.forEditor) figure.setAttribute("contenteditable", "false");
5512
+ }
5513
+ for (const figure of Array.from(body.querySelectorAll("figure"))) {
5514
+ if (figure.classList.contains("fx-table") || figure.querySelector("table")) {
5515
+ figure.classList.add("fx-table");
5516
+ figure.removeAttribute("contenteditable");
5517
+ continue;
5047
5518
  }
5048
- footer {
5049
- padding: var(--fx-space-md) var(--fx-space-xl);
5050
- border-top: 1px solid var(--fx-border-default);
5051
- display: flex;
5052
- justify-content: flex-end;
5053
- gap: var(--fx-space-sm);
5519
+ const hasImg = Boolean(figure.querySelector("img"));
5520
+ const hasVideo = Boolean(figure.querySelector("video"));
5521
+ const hasIframe = Boolean(figure.querySelector("iframe"));
5522
+ if (!hasImg && !hasVideo && !hasIframe) {
5523
+ figure.remove();
5524
+ continue;
5054
5525
  }
5055
- footer[hidden] { display: none; }
5056
- `;
5057
- let FxDialog = _FxDialog;
5058
- function defineFxDialog() {
5059
- return defineElement("fx-dialog", FxDialog);
5526
+ const classes = new Set((figure.getAttribute("class") ?? "").split(/\s+/).filter(Boolean));
5527
+ if (hasImg) classes.add("fx-image");
5528
+ if (hasVideo) classes.add("fx-video");
5529
+ if (hasIframe) classes.add("fx-embed");
5530
+ figure.setAttribute("class", Array.from(classes).join(" "));
5531
+ if (!figure.hasAttribute("data-align")) figure.setAttribute("data-align", "center");
5532
+ if (options.forEditor) figure.setAttribute("contenteditable", "false");
5533
+ else figure.removeAttribute("contenteditable");
5534
+ }
5060
5535
  }
5061
- defineFxDialog();
5062
- const scriptRel = function detectScriptRel() {
5063
- const relList = typeof document !== "undefined" && document.createElement("link").relList;
5064
- return relList && relList.supports && relList.supports("modulepreload") ? "modulepreload" : "preload";
5065
- }();
5066
- const assetsURL = function(dep, importerUrl) {
5067
- return new URL(dep, importerUrl).href;
5068
- };
5069
- const seen = {};
5070
- const __fenixPreload = function preload(baseModule, deps, importerUrl) {
5071
- let promise = Promise.resolve();
5072
- if (deps && deps.length > 0) {
5536
+ function normalizeTables(body) {
5537
+ const doc = body.ownerDocument;
5538
+ for (const table of Array.from(body.querySelectorAll("table"))) {
5539
+ const isGrid = table.classList.contains("fx-grid") || table.getAttribute("role") === "presentation";
5540
+ if (isGrid) {
5541
+ table.classList.add("fx-grid");
5542
+ table.setAttribute("role", "presentation");
5543
+ const firstRow = table.querySelector("tr");
5544
+ const cols = firstRow ? firstRow.children.length : 0;
5545
+ if (cols) table.setAttribute("data-cols", String(cols));
5546
+ } else {
5547
+ table.removeAttribute("role");
5548
+ const parentFigure = table.closest("figure");
5549
+ if (!parentFigure || !parentFigure.classList.contains("fx-table")) {
5550
+ const figure = doc.createElement("figure");
5551
+ figure.className = "fx-table";
5552
+ table.parentNode?.insertBefore(figure, table);
5553
+ figure.appendChild(table);
5554
+ }
5555
+ }
5556
+ let tbody = Array.from(table.children).find((child) => tagOf(child) === "tbody");
5557
+ const looseRows = Array.from(table.children).filter((child) => tagOf(child) === "tr");
5558
+ if (looseRows.length) {
5559
+ if (!tbody) {
5560
+ tbody = doc.createElement("tbody");
5561
+ table.appendChild(tbody);
5562
+ }
5563
+ for (const row of looseRows) tbody.appendChild(row);
5564
+ }
5565
+ if (!Array.from(table.children).some((child) => tagOf(child) === "colgroup")) {
5566
+ const firstRow = table.querySelector("tr");
5567
+ const count = firstRow ? Array.from(firstRow.children).filter((c) => tagOf(c) === "td" || tagOf(c) === "th").length : 0;
5568
+ if (count > 0) {
5569
+ const colgroup = doc.createElement("colgroup");
5570
+ for (let i = 0; i < count; i++) {
5571
+ const col = doc.createElement("col");
5572
+ col.style.width = `${(100 / count).toFixed(4).replace(/\.?0+$/, "")}%`;
5573
+ colgroup.appendChild(col);
5574
+ }
5575
+ table.insertBefore(colgroup, table.firstChild);
5576
+ }
5577
+ }
5578
+ for (const th of Array.from(table.querySelectorAll("th"))) {
5579
+ if (!th.getAttribute("scope")) th.setAttribute("scope", "col");
5580
+ }
5581
+ for (const cell of Array.from(table.querySelectorAll("th, td"))) {
5582
+ wrapInlineChildren(cell, doc);
5583
+ }
5584
+ }
5585
+ }
5586
+ function normalizeRoot(body, options) {
5587
+ const doc = body.ownerDocument;
5588
+ let buffer = [];
5589
+ const flush = (before) => {
5590
+ if (!buffer.length) return;
5591
+ const p = doc.createElement("p");
5592
+ body.insertBefore(p, before);
5593
+ for (const node of buffer) p.appendChild(node);
5594
+ buffer = [];
5595
+ if (!p.textContent?.trim() && !p.querySelector("img,video,iframe,br")) p.innerHTML = "<br>";
5596
+ };
5597
+ for (const node of Array.from(body.childNodes)) {
5598
+ if (node.nodeType === Node.TEXT_NODE) {
5599
+ if ((node.nodeValue ?? "").trim()) buffer.push(node);
5600
+ else node.remove();
5601
+ continue;
5602
+ }
5603
+ if (!isElement(node)) {
5604
+ node.remove();
5605
+ continue;
5606
+ }
5607
+ const tag = tagOf(node);
5608
+ if (BLOCK_TAGS.has(tag) || tag === "figure") {
5609
+ flush(node);
5610
+ continue;
5611
+ }
5612
+ if (INLINE_TAGS.has(tag) || tag === "img" || tag === "video" || tag === "iframe") {
5613
+ buffer.push(node);
5614
+ continue;
5615
+ }
5616
+ while (node.firstChild) buffer.push(node.firstChild), body.insertBefore(node.firstChild, node);
5617
+ node.remove();
5618
+ }
5619
+ flush(null);
5620
+ if (options.forEditor && !body.querySelector("p,h1,h2,h3,h4,h5,h6,ul,ol,blockquote,pre,table,figure,hr")) {
5621
+ body.innerHTML = "<p><br></p>";
5622
+ }
5623
+ }
5624
+ function stripEmpty(body, forEditor) {
5625
+ for (const span of Array.from(body.querySelectorAll("span"))) {
5626
+ if (!span.textContent?.trim() && !span.querySelector("img,video,iframe,br") && !span.hasAttribute("style")) {
5627
+ span.remove();
5628
+ }
5629
+ }
5630
+ for (const p of Array.from(body.querySelectorAll("p"))) {
5631
+ if (p.querySelector("img,video,iframe,br,table,figure")) continue;
5632
+ if (p.textContent?.trim()) continue;
5633
+ if (forEditor && p === body.lastElementChild) continue;
5634
+ p.remove();
5635
+ }
5636
+ for (const heading of Array.from(body.querySelectorAll("h1,h2,h3,h4,h5,h6,blockquote"))) {
5637
+ if (!heading.textContent?.trim() && !heading.querySelector("img,video,iframe,br")) heading.remove();
5638
+ }
5639
+ for (const figure of Array.from(body.querySelectorAll("figure"))) {
5640
+ if (!figure.querySelector("img,video,iframe,table")) figure.remove();
5641
+ }
5642
+ for (const table of Array.from(body.querySelectorAll("table"))) {
5643
+ if (!table.querySelector("td,th")) table.remove();
5644
+ }
5645
+ if (forEditor && !body.querySelector("p,h1,h2,h3,h4,h5,h6,ul,ol,blockquote,pre,table,figure,hr")) {
5646
+ body.innerHTML = "<p><br></p>";
5647
+ }
5648
+ }
5649
+ function normalizeElement(body, options = {}) {
5650
+ normalizeChildren(body, options);
5651
+ normalizeFigures(body, options);
5652
+ normalizeTables(body);
5653
+ normalizeChildren(body, options);
5654
+ normalizeRoot(body, options);
5655
+ stripEmpty(body, Boolean(options.forEditor));
5656
+ const result = body.innerHTML.trim();
5657
+ if (!result) return options.forEditor ? "<p><br></p>" : "";
5658
+ return result;
5659
+ }
5660
+ function normalizeHtml(html, options = {}) {
5661
+ if (!html) return options.forEditor ? "<p><br></p>" : "";
5662
+ if (typeof DOMParser === "undefined") return html;
5663
+ const doc = new DOMParser().parseFromString(
5664
+ `<!doctype html><html><body>${html}</body></html>`,
5665
+ "text/html"
5666
+ );
5667
+ return normalizeElement(doc.body, options);
5668
+ }
5669
+ function normalizeForStorage(html) {
5670
+ return normalizeHtml(html, { forEditor: false });
5671
+ }
5672
+ function normalizeForEditor(html) {
5673
+ return normalizeHtml(html, { forEditor: true });
5674
+ }
5675
+ const FX_EDITOR_CONTENT_CSS = `
5676
+ .content { line-height: 1.55; color: var(--fx-text-default); }
5677
+ .content p { margin: 0 0 0.65em; }
5678
+ .content h1, .content h2, .content h3, .content h4, .content h5, .content h6 {
5679
+ margin: 0.8em 0 0.4em; line-height: 1.25; font-weight: 600; color: var(--fx-text-default);
5680
+ }
5681
+ .content h1 { font-size: 1.8em; }
5682
+ .content h2 { font-size: 1.5em; }
5683
+ .content h3 { font-size: 1.25em; }
5684
+ .content h4 { font-size: 1.1em; }
5685
+ .content h5, .content h6 { font-size: 1em; }
5686
+ .content ul, .content ol { margin: 0 0 0.65em; padding-left: 1.6em; }
5687
+ .content li { margin: 0.15em 0; }
5688
+ .content blockquote {
5689
+ margin: 0.5em 0; padding: 0.25em 0 0.25em 1em;
5690
+ border-left: 3px solid var(--fx-color-primary); color: var(--fx-text-muted);
5691
+ }
5692
+ .content pre {
5693
+ margin: 0.6em 0; padding: var(--fx-space-md); overflow: auto;
5694
+ background: var(--fx-surface-surface); border-radius: var(--fx-radius-sm);
5695
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.9em;
5696
+ }
5697
+ .content code {
5698
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
5699
+ font-size: 0.92em; background: var(--fx-surface-surface);
5700
+ padding: 0.1em 0.3em; border-radius: 3px;
5701
+ }
5702
+ .content pre code { background: none; padding: 0; }
5703
+ .content hr { border: none; border-top: 1px solid var(--fx-border-default); margin: 1em 0; }
5704
+ .content a { color: var(--fx-color-primary); text-decoration: underline; cursor: pointer; }
5705
+ .content img { max-width: 100%; height: auto; display: block; }
5706
+ .content figure { margin: 1em 0; }
5707
+ .content figure.fx-image img { margin: 0 auto; }
5708
+ .content figure.fx-image[data-align="left"] { float: left; max-width: 50%; margin: 0 1em 0.5em 0; }
5709
+ .content figure.fx-image[data-align="right"] { float: right; max-width: 50%; margin: 0 0 0.5em 1em; }
5710
+ .content figure.fx-image[data-align="center"] { margin-left: auto; margin-right: auto; }
5711
+ .content figure.fx-video[data-align="left"],
5712
+ .content figure.fx-embed[data-align="left"] { float: left; max-width: 50%; margin: 0 1em 0.5em 0; }
5713
+ .content figure.fx-video[data-align="right"],
5714
+ .content figure.fx-embed[data-align="right"] { float: right; max-width: 50%; margin: 0 0 0.5em 1em; }
5715
+ .content figure.fx-video[data-align="center"],
5716
+ .content figure.fx-embed[data-align="center"] { margin-left: auto; margin-right: auto; }
5717
+ .content figcaption {
5718
+ margin-top: 0.4em; color: var(--fx-text-muted);
5719
+ font-size: 0.85em; text-align: center;
5720
+ }
5721
+ .content video { display: block; width: 100%; background: #000; border-radius: var(--fx-radius-sm); }
5722
+ .content iframe { display: block; width: 100%; aspect-ratio: 16 / 9; border: 0; }
5723
+ .content table { width: 100%; border-collapse: collapse; margin: 1em 0; }
5724
+ .content th, .content td { border: 1px solid var(--fx-border-default); padding: var(--fx-space-xs) var(--fx-space-sm); vertical-align: top; }
5725
+ .content th { background: var(--fx-surface-surface); text-align: left; font-weight: 600; }
5726
+ .content table.fx-grid { border: none; margin: 0.6em 0; }
5727
+ .content table.fx-grid > tbody > tr > td {
5728
+ border: 1px dashed var(--fx-border-default); padding: var(--fx-space-sm);
5729
+ vertical-align: top;
5730
+ }
5731
+ @media (max-width: 640px) {
5732
+ .content table.fx-grid[data-stack] > tbody > tr > td {
5733
+ display: block; width: 100% !important; border: none; padding: var(--fx-space-xs) 0;
5734
+ }
5735
+ }
5736
+ `;
5737
+ const EDITOR_STYLES = `
5738
+ :host {
5739
+ display: block;
5740
+ width: 100%;
5741
+ font-family: var(--fx-font-family);
5742
+ font-size: var(--fx-font-size);
5743
+ color: var(--fx-text-default);
5744
+ --fx-editor-min-height: 240px;
5745
+ --fx-editor-max-height: none;
5746
+ --fx-editor-radius: var(--fx-radius-md);
5747
+ --fx-editor-toolbar-bg: var(--fx-surface-surface);
5748
+ --fx-editor-content-padding: var(--fx-space-md) var(--fx-space-lg);
5749
+ }
5750
+ :host([full]) { width: 100%; }
5751
+ .editor {
5752
+ display: flex;
5753
+ flex-direction: column;
5754
+ overflow: hidden;
5755
+ background: var(--fx-surface-background);
5756
+ border: 1px solid var(--fx-border-default);
5757
+ border-radius: var(--fx-editor-radius);
5758
+ transition: border-color var(--fx-motion-duration-normal) var(--fx-motion-easing),
5759
+ box-shadow var(--fx-motion-duration-normal) var(--fx-motion-easing);
5760
+ }
5761
+ .editor:focus-within {
5762
+ border-color: var(--fx-color-primary);
5763
+ box-shadow: var(--fx-effect-focus-ring, none);
5764
+ }
5765
+ :host([error]) .editor, :host([invalid]) .editor {
5766
+ border-color: var(--fx-color-danger, #dc2626);
5767
+ box-shadow: var(--fx-effect-error-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-danger, #dc2626) 18%, transparent));
5768
+ }
5769
+ :host([success]) .editor, :host([valid]) .editor {
5770
+ border-color: var(--fx-color-success, #16a34a);
5771
+ box-shadow: var(--fx-effect-success-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent));
5772
+ }
5773
+ :host([disabled]) { opacity: 0.55; pointer-events: none; }
5774
+ :host([readonly]) .toolbar { display: none; }
5775
+
5776
+ .toolbar {
5777
+ position: relative;
5778
+ display: flex;
5779
+ flex-wrap: wrap;
5780
+ align-items: center;
5781
+ gap: 1px;
5782
+ padding: var(--fx-space-xs);
5783
+ background: var(--fx-editor-toolbar-bg);
5784
+ border-bottom: 1px solid var(--fx-border-default);
5785
+ user-select: none;
5786
+ }
5787
+ .toolbar[hidden] { display: none; }
5788
+ .tb-group {
5789
+ display: flex;
5790
+ align-items: center;
5791
+ gap: 1px;
5792
+ padding: 0 var(--fx-space-xs);
5793
+ border-right: 1px solid var(--fx-border-default);
5794
+ }
5795
+ .tb-group:last-child { border-right: none; }
5796
+ .tb-btn {
5797
+ display: inline-flex;
5798
+ align-items: center;
5799
+ justify-content: center;
5800
+ width: 30px;
5801
+ height: 30px;
5802
+ padding: 0;
5803
+ border: none;
5804
+ border-radius: var(--fx-radius-sm);
5805
+ background: transparent;
5806
+ color: var(--fx-text-default);
5807
+ font-size: 19px;
5808
+ cursor: pointer;
5809
+ transition: background var(--fx-motion-duration-fast) var(--fx-motion-easing);
5810
+ }
5811
+ .tb-btn:hover { background: var(--fx-surface-surface-hover, rgba(0, 0, 0, 0.06)); }
5812
+ .tb-btn:focus-visible { outline: 2px solid var(--fx-color-primary); outline-offset: -2px; }
5813
+ .tb-btn.active {
5814
+ background: color-mix(in srgb, var(--fx-color-primary) 14%, transparent);
5815
+ color: var(--fx-color-primary);
5816
+ }
5817
+ .tb-btn:disabled { opacity: 0.4; cursor: not-allowed; }
5818
+ .tb-select {
5819
+ height: 30px;
5820
+ padding: 0 var(--fx-space-xs);
5821
+ border: 1px solid var(--fx-border-default);
5822
+ border-radius: var(--fx-radius-sm);
5823
+ background: var(--fx-surface-background);
5824
+ color: var(--fx-text-default);
5825
+ font-family: inherit;
5826
+ font-size: calc(var(--fx-font-size) - 2px);
5827
+ cursor: pointer;
5828
+ }
5829
+ .tb-color {
5830
+ width: 30px;
5831
+ height: 30px;
5832
+ padding: 2px;
5833
+ border: none;
5834
+ background: transparent;
5835
+ cursor: pointer;
5836
+ }
5837
+ .tb-color input { width: 26px; height: 26px; padding: 0; border: none; background: none; cursor: pointer; }
5838
+ .body { position: relative; display: flex; flex-direction: column; flex: 1 1 auto; }
5839
+ .content {
5840
+ position: relative;
5841
+ min-height: var(--fx-editor-min-height);
5842
+ max-height: var(--fx-editor-max-height);
5843
+ overflow: auto;
5844
+ padding: var(--fx-editor-content-padding);
5845
+ outline: none;
5846
+ box-sizing: border-box;
5847
+ }
5848
+ .content[hidden] { display: none; }
5849
+ .content.is-empty::before {
5850
+ content: attr(data-placeholder);
5851
+ color: var(--fx-text-muted);
5852
+ pointer-events: none;
5853
+ position: absolute;
5854
+ }
5855
+ .content[contenteditable="false"] { background: var(--fx-surface-surface-hover, rgba(0,0,0,.02)); }
5856
+ .content figure.fx-image { touch-action: none; }
5857
+
5858
+ .media-overlay {
5859
+ position: absolute;
5860
+ z-index: 5;
5861
+ pointer-events: none;
5862
+ border: 1px solid var(--fx-color-primary);
5863
+ box-shadow: 0 0 0 1px color-mix(in srgb, var(--fx-color-primary) 30%, transparent);
5864
+ }
5865
+ .media-overlay[hidden] { display: none; }
5866
+ .media-handle {
5867
+ position: absolute;
5868
+ width: 11px;
5869
+ height: 11px;
5870
+ border: 1px solid #fff;
5871
+ border-radius: 50%;
5872
+ background: var(--fx-color-primary);
5873
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
5874
+ pointer-events: auto;
5875
+ touch-action: none;
5876
+ }
5877
+ .media-handle.nw { left: -6px; top: -6px; cursor: nwse-resize; }
5878
+ .media-handle.ne { right: -6px; top: -6px; cursor: nesw-resize; }
5879
+ .media-handle.sw { left: -6px; bottom: -6px; cursor: nesw-resize; }
5880
+ .media-handle.se { right: -6px; bottom: -6px; cursor: nwse-resize; }
5881
+ .media-size {
5882
+ position: absolute;
5883
+ top: -24px;
5884
+ left: 0;
5885
+ padding: 2px 6px;
5886
+ border-radius: var(--fx-radius-sm);
5887
+ background: var(--fx-color-primary);
5888
+ color: #fff;
5889
+ font-size: 11px;
5890
+ line-height: 1.4;
5891
+ white-space: nowrap;
5892
+ }
5893
+ .media-delete {
5894
+ position: absolute;
5895
+ top: -32px;
5896
+ right: -1px;
5897
+ display: inline-flex;
5898
+ align-items: center;
5899
+ justify-content: center;
5900
+ width: 24px;
5901
+ height: 24px;
5902
+ padding: 0;
5903
+ border: none;
5904
+ border-radius: 50%;
5905
+ background: var(--fx-color-danger, #dc2626);
5906
+ color: #fff;
5907
+ font-size: 15px;
5908
+ line-height: 1;
5909
+ cursor: pointer;
5910
+ pointer-events: auto;
5911
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
5912
+ }
5913
+ .media-delete:hover { filter: brightness(0.92); }
5914
+ .media-delete:focus-visible { outline: 2px solid #fff; outline-offset: 1px; }
5915
+
5916
+ .content figure.fx-image,
5917
+ .content figure.fx-video,
5918
+ .content figure.fx-embed { position: relative; }
5919
+ .fx-media-quick {
5920
+ position: absolute;
5921
+ top: 6px;
5922
+ z-index: 4;
5923
+ display: inline-flex;
5924
+ align-items: center;
5925
+ justify-content: center;
5926
+ width: 24px;
5927
+ height: 24px;
5928
+ padding: 0;
5929
+ border: 1px solid color-mix(in srgb, var(--fx-color-primary) 35%, transparent);
5930
+ border-radius: var(--fx-radius-sm);
5931
+ background: var(--fx-surface-background);
5932
+ color: var(--fx-text-default);
5933
+ cursor: pointer;
5934
+ opacity: 0;
5935
+ transition: opacity var(--fx-motion-duration-fast) var(--fx-motion-easing), background var(--fx-motion-duration-fast) var(--fx-motion-easing);
5936
+ }
5937
+ .content figure:hover > .fx-media-quick,
5938
+ .content figure:focus-within > .fx-media-quick { opacity: 1; }
5939
+ .content figure.fx-video > .fx-media-quick,
5940
+ .content figure.fx-embed > .fx-media-quick { opacity: 0.92; }
5941
+ .fx-media-quick:hover { background: var(--fx-surface-surface-hover, rgba(0, 0, 0, 0.06)); }
5942
+ .fx-media-quick.active { background: var(--fx-color-primary); color: #fff; }
5943
+ .fx-media-quick.quick-left { right: 82px; }
5944
+ .fx-media-quick.quick-center { right: 56px; }
5945
+ .fx-media-quick.quick-right { right: 30px; }
5946
+ .fx-media-quick.quick-delete { right: 4px; background: var(--fx-color-danger, #dc2626); color: #fff; border-color: transparent; }
5947
+ .fx-media-quick:focus-visible { outline: 2px solid var(--fx-color-primary); outline-offset: 1px; }
5948
+
5949
+ .link-overlay {
5950
+ position: absolute;
5951
+ z-index: 6;
5952
+ display: flex;
5953
+ gap: 2px;
5954
+ padding: 3px;
5955
+ background: var(--fx-surface-background);
5956
+ border: 1px solid var(--fx-border-default);
5957
+ border-radius: var(--fx-radius-sm);
5958
+ box-shadow: var(--fx-shadow-md, 0 4px 12px rgba(0, 0, 0, 0.16));
5959
+ }
5960
+ .link-overlay[hidden] { display: none; }
5961
+ .link-overlay button {
5962
+ border: none;
5963
+ background: none;
5964
+ color: var(--fx-text-default);
5965
+ font: inherit;
5966
+ font-size: 12px;
5967
+ line-height: 1.4;
5968
+ padding: 4px 8px;
5969
+ border-radius: var(--fx-radius-sm);
5970
+ cursor: pointer;
5971
+ }
5972
+ .link-overlay button:hover { background: var(--fx-surface-surface-hover, rgba(0, 0, 0, 0.06)); }
5973
+ .link-overlay button[data-link-action="remove"] { color: var(--fx-color-danger, #dc2626); }
5974
+ .link-overlay button:focus-visible { outline: 2px solid var(--fx-color-primary); outline-offset: 1px; }
5975
+
5976
+ .source {
5977
+ display: block;
5978
+ width: 100%;
5979
+ min-height: var(--fx-editor-min-height);
5980
+ max-height: var(--fx-editor-max-height);
5981
+ padding: var(--fx-editor-content-padding);
5982
+ border: none;
5983
+ outline: none;
5984
+ resize: vertical;
5985
+ background: var(--fx-surface-background);
5986
+ color: var(--fx-text-default);
5987
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
5988
+ font-size: 13px;
5989
+ line-height: 1.55;
5990
+ box-sizing: border-box;
5991
+ }
5992
+ .source[hidden] { display: none; }
5993
+
5994
+ .statusbar {
5995
+ display: flex;
5996
+ align-items: center;
5997
+ justify-content: space-between;
5998
+ gap: var(--fx-space-sm);
5999
+ min-height: 28px;
6000
+ padding: 2px var(--fx-space-md);
6001
+ border-top: 1px solid var(--fx-border-default);
6002
+ color: var(--fx-text-muted);
6003
+ font-size: calc(var(--fx-font-size) - 3px);
6004
+ }
6005
+ .statusbar:empty { display: none; }
6006
+ .statusbar[hidden] { display: none; }
6007
+ .source-actions { display: inline-flex; gap: var(--fx-space-xs); }
6008
+ .source-actions[hidden] { display: none; }
6009
+
6010
+ .backdrop {
6011
+ position: fixed;
6012
+ inset: 0;
6013
+ z-index: var(--fx-z-modal, 1100);
6014
+ display: flex;
6015
+ align-items: center;
6016
+ justify-content: center;
6017
+ background: color-mix(in srgb, var(--fx-text-default, #000) 45%, transparent);
6018
+ }
6019
+ .backdrop[hidden] { display: none; }
6020
+ .dialog {
6021
+ width: min(460px, calc(100vw - 32px));
6022
+ max-height: calc(100vh - 48px);
6023
+ overflow: auto;
6024
+ background: var(--fx-surface-background);
6025
+ border: 1px solid var(--fx-border-default);
6026
+ border-radius: var(--fx-radius-lg);
6027
+ box-shadow: var(--fx-shadow-xl);
6028
+ font-family: var(--fx-font-family);
6029
+ font-size: var(--fx-font-size);
6030
+ }
6031
+ .dialog header {
6032
+ display: flex;
6033
+ align-items: center;
6034
+ justify-content: space-between;
6035
+ padding: var(--fx-space-md) var(--fx-space-lg);
6036
+ border-bottom: 1px solid var(--fx-border-default);
6037
+ }
6038
+ .dialog h3 { margin: 0; font-size: 1rem; font-weight: 600; }
6039
+ .dialog-close {
6040
+ border: none; background: transparent; color: var(--fx-text-muted);
6041
+ font-size: 1.4rem; line-height: 1; cursor: pointer;
6042
+ }
6043
+ .dialog-body { display: flex; flex-direction: column; gap: var(--fx-space-sm); padding: var(--fx-space-lg); }
6044
+ .field { display: flex; flex-direction: column; gap: 3px; font-size: calc(var(--fx-font-size) - 2px); color: var(--fx-text-muted); }
6045
+ .field input, .field select {
6046
+ height: var(--fx-size-md);
6047
+ padding: 0 var(--fx-space-sm);
6048
+ border: 1px solid var(--fx-border-default);
6049
+ border-radius: var(--fx-radius-sm);
6050
+ background: var(--fx-surface-background);
6051
+ color: var(--fx-text-default);
6052
+ font: inherit;
6053
+ font-size: var(--fx-font-size);
6054
+ }
6055
+ .field input:focus, .field select:focus { outline: none; border-color: var(--fx-color-primary); box-shadow: var(--fx-effect-focus-ring, none); }
6056
+ .dialog footer {
6057
+ display: flex; justify-content: flex-end; gap: var(--fx-space-sm);
6058
+ padding: var(--fx-space-md) var(--fx-space-lg);
6059
+ border-top: 1px solid var(--fx-border-default);
6060
+ }
6061
+ .btn {
6062
+ min-height: var(--fx-size-sm);
6063
+ padding: 0 var(--fx-space-md);
6064
+ border: 1px solid var(--fx-border-default);
6065
+ border-radius: var(--fx-radius-sm);
6066
+ background: var(--fx-surface-background);
6067
+ color: var(--fx-text-default);
6068
+ font: inherit; font-size: calc(var(--fx-font-size) - 2px);
6069
+ cursor: pointer;
6070
+ }
6071
+ .btn:hover { background: var(--fx-surface-surface-hover, rgba(0,0,0,.05)); }
6072
+ .btn.primary { background: var(--fx-color-primary); border-color: transparent; color: #fff; }
6073
+ .btn.primary:hover { filter: brightness(0.92); }
6074
+
6075
+ .panel {
6076
+ position: absolute;
6077
+ top: calc(100% + 4px);
6078
+ left: var(--fx-space-md);
6079
+ z-index: 6;
6080
+ padding: var(--fx-space-sm);
6081
+ background: var(--fx-surface-background);
6082
+ border: 1px solid var(--fx-border-default);
6083
+ border-radius: var(--fx-radius-md);
6084
+ box-shadow: var(--fx-shadow-lg);
6085
+ }
6086
+ .panel[hidden] { display: none; }
6087
+ .grid-picker { display: grid; grid-template-columns: repeat(8, 18px); gap: 3px; }
6088
+ .grid-picker button {
6089
+ width: 18px; height: 18px; padding: 0;
6090
+ border: 1px solid var(--fx-border-default);
6091
+ border-radius: 2px; background: var(--fx-surface-background); cursor: pointer;
6092
+ }
6093
+ .grid-picker button.on { background: var(--fx-color-primary); border-color: var(--fx-color-primary); }
6094
+ .panel-title { margin: 0 0 6px; font-size: 11px; color: var(--fx-text-muted); }
6095
+ .panel-actions { display: flex; flex-wrap: wrap; gap: 4px; max-width: 210px; }
6096
+ .panel-actions .btn { min-height: 26px; padding: 0 6px; font-size: 11px; }
6097
+
6098
+ :host([fullscreen]) {
6099
+ position: fixed;
6100
+ inset: 0;
6101
+ z-index: var(--fx-z-modal, 1100);
6102
+ background: var(--fx-surface-background);
6103
+ }
6104
+ :host([fullscreen]) .editor { height: 100%; border: none; border-radius: 0; }
6105
+ :host([fullscreen]) .content, :host([fullscreen]) .source { flex: 1 1 auto; max-height: none; }
6106
+
6107
+ @media (max-width: 640px) {
6108
+ .toolbar { gap: 0; }
6109
+ .tb-group { padding: 0 1px; }
6110
+ .tb-btn { width: 28px; height: 28px; }
6111
+ }
6112
+ ${FX_EDITOR_CONTENT_CSS}
6113
+
6114
+
6115
+ `;
6116
+ const BLOCK_SELECTOR = "p,h1,h2,h3,h4,h5,h6,li,blockquote,pre,td,th,figcaption,div";
6117
+ function rootOf(editor) {
6118
+ const root = editor.getRootNode();
6119
+ return typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot ? root : document;
6120
+ }
6121
+ function isRangeInside(editor, range) {
6122
+ if (!range) return false;
6123
+ const start = range.startContainer;
6124
+ const end = range.endContainer;
6125
+ return editor.contains(start) && editor.contains(end);
6126
+ }
6127
+ function readSelectionRange(root) {
6128
+ const selection = document.getSelection();
6129
+ if (!selection) return null;
6130
+ if (root instanceof ShadowRoot && selection.getComposedRanges) {
6131
+ try {
6132
+ const ranges = selection.getComposedRanges({ shadowRoots: [root] });
6133
+ const first = ranges[0];
6134
+ if (first) {
6135
+ const range = document.createRange();
6136
+ range.setStart(first.startContainer, first.startOffset);
6137
+ range.setEnd(first.endContainer, first.endOffset);
6138
+ return range;
6139
+ }
6140
+ } catch {
6141
+ }
6142
+ }
6143
+ return selection.rangeCount > 0 ? selection.getRangeAt(0) : null;
6144
+ }
6145
+ function getRange(editor, fallback) {
6146
+ const range = readSelectionRange(rootOf(editor));
6147
+ if (isRangeInside(editor, range)) return range;
6148
+ if (isRangeInside(editor, fallback ?? null)) return fallback ?? null;
6149
+ return null;
6150
+ }
6151
+ function saveRange(editor) {
6152
+ const range = getRange(editor);
6153
+ return range ? range.cloneRange() : null;
6154
+ }
6155
+ function restoreRange(editor, range) {
6156
+ if (!range || !isRangeInside(editor, range)) return;
6157
+ editor.focus({ preventScroll: true });
6158
+ const selection = document.getSelection();
6159
+ if (!selection) return;
6160
+ selection.removeAllRanges();
6161
+ selection.addRange(range);
6162
+ }
6163
+ function placeCaretAtEnd(editor) {
6164
+ const range = document.createRange();
6165
+ range.selectNodeContents(editor);
6166
+ range.collapse(false);
6167
+ const selection = document.getSelection();
6168
+ if (!selection) return;
6169
+ selection.removeAllRanges();
6170
+ selection.addRange(range);
6171
+ }
6172
+ function getBlock(editor, range) {
6173
+ const active = range ?? getRange(editor);
6174
+ if (!active) return null;
6175
+ const node = active.startContainer;
6176
+ const el = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
6177
+ const block = el?.closest(BLOCK_SELECTOR) ?? null;
6178
+ return block && editor.contains(block) ? block : null;
6179
+ }
6180
+ function getSelectedBlocks(editor, range) {
6181
+ const active = getRange(editor);
6182
+ if (!active) return [];
6183
+ if (active.collapsed) {
6184
+ const block = getBlock(editor, active);
6185
+ return block ? [block] : [];
6186
+ }
6187
+ return Array.from(editor.querySelectorAll(BLOCK_SELECTOR)).filter((block) => {
6188
+ try {
6189
+ return active.intersectsNode(block);
6190
+ } catch {
6191
+ return false;
6192
+ }
6193
+ });
6194
+ }
6195
+ function closestInEditor(editor, node, selector) {
6196
+ const el = node && (node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement);
6197
+ const match = el?.closest(selector) ?? null;
6198
+ return match && editor.contains(match) ? match : null;
6199
+ }
6200
+ function setSelection(range) {
6201
+ const selection = document.getSelection();
6202
+ if (!selection) return;
6203
+ selection.removeAllRanges();
6204
+ selection.addRange(range);
6205
+ }
6206
+ function nativeExec(command, value) {
6207
+ const exec = document.execCommand;
6208
+ if (typeof exec !== "function") return false;
6209
+ try {
6210
+ return exec.call(document, command, false, value);
6211
+ } catch {
6212
+ return false;
6213
+ }
6214
+ }
6215
+ function insertHtml(editor, html) {
6216
+ if (nativeExec("insertHTML", html)) return true;
6217
+ const range = getRange(editor);
6218
+ if (!range) {
6219
+ editor.insertAdjacentHTML("beforeend", html);
6220
+ placeCaretAtEnd(editor);
6221
+ return true;
6222
+ }
6223
+ range.deleteContents();
6224
+ const fragment = range.createContextualFragment(html);
6225
+ const last = fragment.lastChild;
6226
+ range.insertNode(fragment);
6227
+ if (last) {
6228
+ const next = document.createRange();
6229
+ next.setStartAfter(last);
6230
+ next.collapse(true);
6231
+ setSelection(next);
6232
+ }
6233
+ return true;
6234
+ }
6235
+ function unwrap(el) {
6236
+ const parent = el.parentNode;
6237
+ if (!parent) return;
6238
+ while (el.firstChild) parent.insertBefore(el.firstChild, el);
6239
+ el.remove();
6240
+ }
6241
+ function wrapSelection(editor, tag) {
6242
+ const range = getRange(editor);
6243
+ if (!range) return false;
6244
+ const el = document.createElement(tag);
6245
+ if (range.collapsed) {
6246
+ el.appendChild(document.createTextNode("​"));
6247
+ range.insertNode(el);
6248
+ const caret = document.createRange();
6249
+ caret.setStart(el.firstChild ?? el, 1);
6250
+ caret.collapse(true);
6251
+ setSelection(caret);
6252
+ return true;
6253
+ }
6254
+ try {
6255
+ range.surroundContents(el);
6256
+ } catch {
6257
+ el.appendChild(range.extractContents());
6258
+ range.insertNode(el);
6259
+ }
6260
+ return true;
6261
+ }
6262
+ function toggleInline(editor, tag) {
6263
+ const nativeMap = {
6264
+ strong: "bold",
6265
+ b: "bold",
6266
+ em: "italic",
6267
+ i: "italic",
6268
+ u: "underline",
6269
+ s: "strikeThrough",
6270
+ strike: "strikeThrough"
6271
+ };
6272
+ const nativeCommand = nativeMap[tag];
6273
+ if (nativeCommand && nativeExec(nativeCommand)) return true;
6274
+ const range = getRange(editor);
6275
+ if (!range) return false;
6276
+ const current = (range.startContainer.nodeType === Node.ELEMENT_NODE ? range.startContainer : range.startContainer.parentElement)?.closest(tag);
6277
+ if (current && editor.contains(current)) {
6278
+ unwrap(current);
6279
+ return true;
6280
+ }
6281
+ return wrapSelection(editor, tag);
6282
+ }
6283
+ function formatBlock(editor, tag) {
6284
+ const normalized = tag.toLowerCase().replace(/[<>]/g, "");
6285
+ if (nativeExec("formatBlock", normalized)) return true;
6286
+ const blocks = getSelectedBlocks(editor);
6287
+ if (!blocks.length) {
6288
+ const block = getBlock(editor);
6289
+ if (block) blocks.push(block);
6290
+ }
6291
+ for (const block of blocks) {
6292
+ if (!editor.contains(block)) continue;
6293
+ const next = document.createElement(normalized);
6294
+ for (const attr of Array.from(block.attributes)) next.setAttribute(attr.name, attr.value);
6295
+ while (block.firstChild) next.appendChild(block.firstChild);
6296
+ block.replaceWith(next);
6297
+ }
6298
+ return blocks.length > 0;
6299
+ }
6300
+ function align(editor, value) {
6301
+ const nativeMap = {
6302
+ left: "justifyLeft",
6303
+ center: "justifyCenter",
6304
+ right: "justifyRight",
6305
+ justify: "justifyFull"
6306
+ };
6307
+ if (nativeExec(nativeMap[value])) return true;
6308
+ const blocks = getSelectedBlocks(editor);
6309
+ for (const block of blocks) block.style.textAlign = value;
6310
+ return blocks.length > 0;
6311
+ }
6312
+ function toggleList(editor, ordered) {
6313
+ if (nativeExec(ordered ? "insertOrderedList" : "insertUnorderedList")) return true;
6314
+ const blocks = getSelectedBlocks(editor);
6315
+ if (!blocks.length) return false;
6316
+ const list = document.createElement(ordered ? "ol" : "ul");
6317
+ const first = blocks[0];
6318
+ first.parentNode?.insertBefore(list, first);
6319
+ for (const block of blocks) {
6320
+ const li = document.createElement("li");
6321
+ while (block.firstChild) li.appendChild(block.firstChild);
6322
+ list.appendChild(li);
6323
+ block.remove();
6324
+ }
6325
+ const range = document.createRange();
6326
+ range.selectNodeContents(list.lastElementChild ?? list);
6327
+ range.collapse(false);
6328
+ setSelection(range);
6329
+ return true;
6330
+ }
6331
+ function removeFormat(editor) {
6332
+ if (nativeExec("removeFormat")) return true;
6333
+ const range = getRange(editor);
6334
+ if (!range) return false;
6335
+ const root = range.commonAncestorContainer;
6336
+ const container = root.nodeType === Node.ELEMENT_NODE ? root : root.parentElement;
6337
+ if (!container) return false;
6338
+ const tags = ["strong", "b", "em", "i", "u", "s", "strike", "span", "mark", "code", "sub", "sup"];
6339
+ for (const el of Array.from(container.querySelectorAll(tags.join(",")))) {
6340
+ if (range.intersectsNode(el)) unwrap(el);
6341
+ }
6342
+ return true;
6343
+ }
6344
+ const linkHtml = (href, label, target) => `<a href="${escapeAttr(href)}" target="${target}" rel="noopener noreferrer">${escapeText(label)}</a>`;
6345
+ function applyLink(editor, href, text, target = "_blank") {
6346
+ const label = text?.trim() || href;
6347
+ const range = getRange(editor);
6348
+ if (!range) return insertHtml(editor, linkHtml(href, label, target));
6349
+ const anchor = (range.startContainer.nodeType === Node.ELEMENT_NODE ? range.startContainer : range.startContainer.parentElement)?.closest("a");
6350
+ if (anchor && editor.contains(anchor)) {
6351
+ anchor.setAttribute("href", href);
6352
+ anchor.setAttribute("target", target);
6353
+ anchor.setAttribute("rel", "noopener noreferrer");
6354
+ if (text && text !== anchor.textContent) anchor.textContent = text;
6355
+ return true;
6356
+ }
6357
+ if (!range.collapsed) {
6358
+ if (nativeExec("createLink", href)) {
6359
+ const created = (range.startContainer.nodeType === Node.ELEMENT_NODE ? range.startContainer : range.startContainer.parentElement)?.closest("a");
6360
+ created?.setAttribute("target", target);
6361
+ created?.setAttribute("rel", "noopener noreferrer");
6362
+ return true;
6363
+ }
6364
+ const el = document.createElement("a");
6365
+ el.setAttribute("href", href);
6366
+ el.setAttribute("target", target);
6367
+ el.setAttribute("rel", "noopener noreferrer");
6368
+ try {
6369
+ range.surroundContents(el);
6370
+ } catch {
6371
+ el.appendChild(range.extractContents());
6372
+ range.insertNode(el);
6373
+ }
6374
+ return true;
6375
+ }
6376
+ return insertHtml(editor, linkHtml(href, label, target));
6377
+ }
6378
+ function unlink(editor) {
6379
+ if (nativeExec("unlink")) return true;
6380
+ const range = getRange(editor);
6381
+ if (!range) return false;
6382
+ const anchor = (range.startContainer.nodeType === Node.ELEMENT_NODE ? range.startContainer : range.startContainer.parentElement)?.closest("a");
6383
+ if (anchor && editor.contains(anchor)) {
6384
+ unwrap(anchor);
6385
+ return true;
6386
+ }
6387
+ return false;
6388
+ }
6389
+ function indent(editor, delta) {
6390
+ if (delta > 0 && nativeExec("indent")) return true;
6391
+ if (delta < 0 && nativeExec("outdent")) return true;
6392
+ const blocks = getSelectedBlocks(editor);
6393
+ for (const block of blocks) {
6394
+ const current = Number.parseInt(block.style.marginLeft || "0", 10) || 0;
6395
+ const next = Math.max(0, current + delta);
6396
+ block.style.marginLeft = next ? `${next}px` : "";
6397
+ }
6398
+ return blocks.length > 0;
6399
+ }
6400
+ function escapeAttr(value) {
6401
+ return value.replace(/[&<>"']/g, (ch) => {
6402
+ switch (ch) {
6403
+ case "&":
6404
+ return "&amp;";
6405
+ case "<":
6406
+ return "&lt;";
6407
+ case ">":
6408
+ return "&gt;";
6409
+ case '"':
6410
+ return "&quot;";
6411
+ default:
6412
+ return "&#39;";
6413
+ }
6414
+ });
6415
+ }
6416
+ function escapeText(value) {
6417
+ return value.replace(/[&<>]/g, (ch) => ch === "&" ? "&amp;" : ch === "<" ? "&lt;" : "&gt;");
6418
+ }
6419
+ function tableMatrix(table) {
6420
+ const rows = Array.from(table.rows);
6421
+ const matrix = rows.map(() => []);
6422
+ for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
6423
+ const row = rows[rowIndex];
6424
+ for (const cell of Array.from(row.cells)) {
6425
+ let col = 0;
6426
+ while (matrix[rowIndex][col]) col++;
6427
+ const rowspan = Math.max(1, Math.min(cell.rowSpan || 1, rows.length - rowIndex));
6428
+ const colspan = Math.max(1, cell.colSpan || 1);
6429
+ for (let r = rowIndex; r < rowIndex + rowspan; r++) {
6430
+ if (!matrix[r]) matrix[r] = [];
6431
+ for (let c = col; c < col + colspan; c++) matrix[r][c] = cell;
6432
+ }
6433
+ }
6434
+ }
6435
+ const width = matrix.reduce((max, row) => Math.max(max, row.length), 0);
6436
+ for (const row of matrix) {
6437
+ while (row.length < width) row.push(null);
6438
+ }
6439
+ return { rows, matrix };
6440
+ }
6441
+ function cellPosition(table, cell) {
6442
+ const { matrix } = tableMatrix(table);
6443
+ for (let row = 0; row < matrix.length; row++) {
6444
+ for (let col = 0; col < matrix[row].length; col++) {
6445
+ if (matrix[row][col] === cell) return { row, col };
6446
+ }
6447
+ }
6448
+ return null;
6449
+ }
6450
+ function selectedCells(table, range) {
6451
+ return Array.from(table.querySelectorAll("td,th")).filter((cell) => {
6452
+ try {
6453
+ return range.intersectsNode(cell);
6454
+ } catch {
6455
+ return false;
6456
+ }
6457
+ });
6458
+ }
6459
+ function copyCellAttributes(from, to) {
6460
+ for (const attr of Array.from(from.attributes)) {
6461
+ if (attr.name === "colspan" || attr.name === "rowspan") continue;
6462
+ to.setAttribute(attr.name, attr.value);
6463
+ }
6464
+ }
6465
+ function mergeSelectedCells(table, range) {
6466
+ const cells = selectedCells(table, range);
6467
+ if (cells.length < 2) return null;
6468
+ const { matrix } = tableMatrix(table);
6469
+ const positions = cells.map((cell) => ({ cell, position: cellPosition(table, cell) })).filter((item) => Boolean(item.position));
6470
+ if (positions.length !== cells.length) return null;
6471
+ const minRow = Math.min(...positions.map((item) => item.position.row));
6472
+ const maxRow = Math.max(...positions.map((item) => item.position.row));
6473
+ const minCol = Math.min(...positions.map((item) => item.position.col));
6474
+ const maxCol = Math.max(...positions.map((item) => item.position.col));
6475
+ for (const { cell, position } of positions) {
6476
+ if (position.row < minRow || position.col < minCol) return null;
6477
+ if (position.row + Math.max(1, cell.rowSpan || 1) - 1 > maxRow) return null;
6478
+ if (position.col + Math.max(1, cell.colSpan || 1) - 1 > maxCol) return null;
6479
+ }
6480
+ const selected = new Set(cells);
6481
+ for (let row = minRow; row <= maxRow; row++) {
6482
+ for (let col = minCol; col <= maxCol; col++) {
6483
+ const cell = matrix[row]?.[col];
6484
+ if (!cell || !selected.has(cell)) return null;
6485
+ }
6486
+ }
6487
+ const anchor = matrix[minRow]?.[minCol];
6488
+ if (!anchor) return null;
6489
+ const tag = cells.every((cell) => cell.tagName.toLowerCase() === "th") ? "th" : "td";
6490
+ const merged = document.createElement(tag);
6491
+ copyCellAttributes(anchor, merged);
6492
+ if (tag === "td") merged.removeAttribute("scope");
6493
+ merged.colSpan = maxCol - minCol + 1;
6494
+ merged.rowSpan = maxRow - minRow + 1;
6495
+ if (merged.colSpan === 1) merged.removeAttribute("colspan");
6496
+ if (merged.rowSpan === 1) merged.removeAttribute("rowspan");
6497
+ for (const cell of cells) {
6498
+ while (cell.firstChild) merged.appendChild(cell.firstChild);
6499
+ }
6500
+ anchor.replaceWith(merged);
6501
+ for (const cell of cells) {
6502
+ if (cell !== anchor && cell.parentNode) cell.remove();
6503
+ }
6504
+ return merged;
6505
+ }
6506
+ function insertCellAtColumn(table, row, col, cell) {
6507
+ const rowIndex = Array.from(table.rows).indexOf(row);
6508
+ const { matrix } = tableMatrix(table);
6509
+ let before = null;
6510
+ for (const existing of Array.from(row.cells)) {
6511
+ let start = -1;
6512
+ for (let c = 0; c < (matrix[rowIndex]?.length ?? 0); c++) {
6513
+ if (matrix[rowIndex][c] === existing) {
6514
+ start = c;
6515
+ break;
6516
+ }
6517
+ }
6518
+ if (start >= col) {
6519
+ before = existing;
6520
+ break;
6521
+ }
6522
+ }
6523
+ row.insertBefore(cell, before);
6524
+ }
6525
+ function splitCell(table, cell) {
6526
+ const colspan = Math.max(1, cell.colSpan || 1);
6527
+ const rowspan = Math.max(1, cell.rowSpan || 1);
6528
+ if (colspan === 1 && rowspan === 1) return [];
6529
+ const position = cellPosition(table, cell);
6530
+ if (!position) return [];
6531
+ const rows = Array.from(table.rows);
6532
+ const makeCell = () => {
6533
+ const next = document.createElement(cell.tagName.toLowerCase());
6534
+ if (next.tagName.toLowerCase() === "th") {
6535
+ next.setAttribute("scope", cell.getAttribute("scope") ?? "col");
6536
+ }
6537
+ next.innerHTML = "<p><br></p>";
6538
+ return next;
6539
+ };
6540
+ const created = [];
6541
+ let last = cell;
6542
+ for (let index = 1; index < colspan; index++) {
6543
+ const next = makeCell();
6544
+ last.after(next);
6545
+ last = next;
6546
+ created.push(next);
6547
+ }
6548
+ cell.removeAttribute("colspan");
6549
+ cell.removeAttribute("rowspan");
6550
+ for (let index = 1; index < rowspan; index++) {
6551
+ const row = rows[position.row + index];
6552
+ if (!row) break;
6553
+ for (let offset = 0; offset < colspan; offset++) {
6554
+ const next = makeCell();
6555
+ insertCellAtColumn(table, row, position.col + offset, next);
6556
+ created.push(next);
6557
+ }
6558
+ }
6559
+ return created;
6560
+ }
6561
+ const MAX_PASTE_BYTES = 1024 * 1024;
6562
+ const MAX_IMAGE_BYTES = 8 * 1024 * 1024;
6563
+ const LARGE_DOC_CHARS = 2e4;
6564
+ const EMIT_DEBOUNCE_MS = 140;
6565
+ const HISTORY_LIMIT = 50;
6566
+ const QUICK_SVG = {
6567
+ left: '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 6h16M4 10h10M4 14h16M4 18h10"/></svg>',
6568
+ center: '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 6h16M7 10h10M4 14h16M7 18h10"/></svg>',
6569
+ right: '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 6h16M10 10h10M4 14h16M10 18h10"/></svg>',
6570
+ delete: '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 6h18M8 6V4h8v2M6 6l1 15h10l1-15M10 10v7M14 10v7"/></svg>'
6571
+ };
6572
+ const _FxEditor = class _FxEditor extends FxFormElement {
6573
+ constructor() {
6574
+ super(...arguments);
6575
+ __privateAdd(this, _FxEditor_instances);
6576
+ __privateAdd(this, _built, false);
6577
+ __privateAdd(this, _eventsBound, false);
6578
+ __privateAdd(this, _initialized, false);
6579
+ __privateAdd(this, _defaultValue, "");
6580
+ __privateAdd(this, _value, "");
6581
+ __privateAdd(this, _dirty, false);
6582
+ __privateAdd(this, _sourceMode, false);
6583
+ __privateAdd(this, _lastRange, null);
6584
+ __privateAdd(this, _rafId, 0);
6585
+ __privateAdd(this, _emitTimer, null);
6586
+ __privateAdd(this, _lastLimitError, 0);
6587
+ __privateAdd(this, _snapshotTimer, null);
6588
+ __privateAdd(this, _history, []);
6589
+ __privateAdd(this, _historyIndex, -1);
6590
+ __privateAdd(this, _imagePicker, null);
6591
+ __privateAdd(this, _mediaFigure, null);
6592
+ __privateAdd(this, _linkAnchor, null);
6593
+ __privateAdd(this, _resizeDispose, null);
6594
+ __privateAdd(this, _dialogReturnFocus, null);
6595
+ __privateAdd(this, _dialogResolve, null);
6596
+ __privateAdd(this, _disposers, []);
6597
+ __privateAdd(this, _selectionRaf, 0);
6598
+ __privateAdd(this, _bodyOverflow, "");
6599
+ }
6600
+ static get observedAttributes() {
6601
+ return [
6602
+ "toolbar",
6603
+ "features",
6604
+ "placeholder",
6605
+ "readonly",
6606
+ "disabled",
6607
+ "required",
6608
+ "full",
6609
+ "error",
6610
+ "invalid",
6611
+ "success",
6612
+ "valid",
6613
+ "show-count",
6614
+ "paste-mode",
6615
+ "min-height",
6616
+ "max-height",
6617
+ "fullscreen",
6618
+ "maxlength"
6619
+ ];
6620
+ }
6621
+ get editor() {
6622
+ return this.root.querySelector(".content");
6623
+ }
6624
+ get source() {
6625
+ return this.root.querySelector(".source");
6626
+ }
6627
+ get toolbarEl() {
6628
+ return this.root.querySelector(".toolbar");
6629
+ }
6630
+ get value() {
6631
+ if (__privateGet(this, _sourceMode) && this.source) return __privateMethod(this, _FxEditor_instances, serialize_fn).call(this);
6632
+ if (__privateGet(this, _dirty)) __privateSet(this, _value, __privateMethod(this, _FxEditor_instances, serialize_fn).call(this));
6633
+ return __privateGet(this, _value);
6634
+ }
6635
+ set value(next) {
6636
+ const clean = __privateMethod(this, _FxEditor_instances, cleanHtml_fn).call(this, next ?? "");
6637
+ __privateSet(this, _value, clean);
6638
+ __privateSet(this, _dirty, false);
6639
+ if (!__privateGet(this, _defaultValue)) __privateSet(this, _defaultValue, clean);
6640
+ __privateMethod(this, _FxEditor_instances, applyValue_fn).call(this, clean);
6641
+ this.updateFormValue();
6642
+ }
6643
+ get html() {
6644
+ return this.value;
6645
+ }
6646
+ set html(next) {
6647
+ this.value = next;
6648
+ }
6649
+ get text() {
6650
+ return (this.editor?.textContent ?? "").replace(/\u200b/g, "").trim();
6651
+ }
6652
+ get defaultValue() {
6653
+ return __privateGet(this, _defaultValue);
6654
+ }
6655
+ set defaultValue(next) {
6656
+ __privateSet(this, _defaultValue, __privateMethod(this, _FxEditor_instances, cleanHtml_fn).call(this, next ?? ""));
6657
+ if (!__privateGet(this, _dirty)) this.value = __privateGet(this, _defaultValue);
6658
+ }
6659
+ get disabled() {
6660
+ return this.hasAttr("disabled");
6661
+ }
6662
+ set disabled(value) {
6663
+ this.toggleAttr("disabled", Boolean(value));
6664
+ }
6665
+ get readonly() {
6666
+ return this.hasAttr("readonly");
6667
+ }
6668
+ set readonly(value) {
6669
+ this.toggleAttr("readonly", Boolean(value));
6670
+ }
6671
+ get required() {
6672
+ return this.hasAttr("required");
6673
+ }
6674
+ set required(value) {
6675
+ this.toggleAttr("required", Boolean(value));
6676
+ }
6677
+ get placeholder() {
6678
+ return this.getAttr("placeholder");
6679
+ }
6680
+ set placeholder(value) {
6681
+ this.setAttribute("placeholder", value);
6682
+ }
6683
+ get toolbar() {
6684
+ const value = this.getAttr("toolbar", "standard");
6685
+ return ["minimal", "basic", "standard", "full", "none"].includes(value) ? value : "standard";
6686
+ }
6687
+ set toolbar(value) {
6688
+ this.setAttribute("toolbar", value);
6689
+ }
6690
+ get pasteMode() {
6691
+ return this.getAttr("paste-mode", "clean") === "plain" ? "plain" : "clean";
6692
+ }
6693
+ set pasteMode(value) {
6694
+ this.setAttribute("paste-mode", value);
6695
+ }
6696
+ get fullscreen() {
6697
+ return this.hasAttr("fullscreen");
6698
+ }
6699
+ set fullscreen(value) {
6700
+ this.toggleAttr("fullscreen", Boolean(value));
6701
+ }
6702
+ /** Limite de caracteres de texto (0 desativa). Padrão: 30.000. */
6703
+ get maxlength() {
6704
+ const value = Number.parseInt(this.getAttr("maxlength", "30000"), 10);
6705
+ return Number.isFinite(value) && value > 0 ? value : 0;
6706
+ }
6707
+ set maxlength(value) {
6708
+ if (Number.isFinite(value) && value >= 0) this.setAttribute("maxlength", String(Math.floor(value)));
6709
+ else this.removeAttribute("maxlength");
6710
+ }
6711
+ connectedCallback() {
6712
+ loadFenixIconsFont();
6713
+ super.connectedCallback();
6714
+ if (this.fullscreen) __privateMethod(this, _FxEditor_instances, syncBodyOverflow_fn).call(this);
6715
+ if (!__privateGet(this, _initialized)) {
6716
+ __privateSet(this, _initialized, true);
6717
+ const initial = __privateGet(this, _value) || this.getAttr("value") || this.innerHTML;
6718
+ this.replaceChildren();
6719
+ __privateSet(this, _defaultValue, __privateMethod(this, _FxEditor_instances, cleanHtml_fn).call(this, initial));
6720
+ __privateSet(this, _value, __privateGet(this, _defaultValue));
6721
+ __privateMethod(this, _FxEditor_instances, applyValue_fn).call(this, __privateGet(this, _defaultValue));
6722
+ this.updateFormValue();
6723
+ __privateMethod(this, _FxEditor_instances, pushHistory_fn).call(this, __privateGet(this, _value));
6724
+ this.dispatchEvent(new CustomEvent("ready", { bubbles: true, composed: true }));
6725
+ } else if (__privateGet(this, _built) && !__privateGet(this, _eventsBound)) {
6726
+ __privateMethod(this, _FxEditor_instances, bindEvents_fn).call(this);
6727
+ __privateSet(this, _eventsBound, true);
6728
+ }
6729
+ __privateMethod(this, _FxEditor_instances, bindGlobalSelection_fn).call(this);
6730
+ }
6731
+ disconnectedCallback() {
6732
+ for (const dispose of __privateGet(this, _disposers)) dispose();
6733
+ __privateSet(this, _disposers, []);
6734
+ __privateSet(this, _eventsBound, false);
6735
+ cancelAnimationFrame(__privateGet(this, _rafId));
6736
+ cancelAnimationFrame(__privateGet(this, _selectionRaf));
6737
+ if (__privateGet(this, _emitTimer)) clearTimeout(__privateGet(this, _emitTimer));
6738
+ if (__privateGet(this, _snapshotTimer)) clearTimeout(__privateGet(this, _snapshotTimer));
6739
+ __privateMethod(this, _FxEditor_instances, endResize_fn).call(this);
6740
+ __privateMethod(this, _FxEditor_instances, hideMediaOverlay_fn).call(this);
6741
+ __privateMethod(this, _FxEditor_instances, hideLinkOverlay_fn).call(this);
6742
+ if (this.hasAttr("fullscreen")) document.body.style.overflow = __privateGet(this, _bodyOverflow);
6743
+ __privateMethod(this, _FxEditor_instances, releaseDialog_fn).call(this, null);
6744
+ super.disconnectedCallback();
6745
+ }
6746
+ attributeChangedCallback(name, oldValue, newValue) {
6747
+ if (oldValue === newValue || !__privateGet(this, _built)) return;
6748
+ if (name === "toolbar" || name === "features") __privateMethod(this, _FxEditor_instances, renderToolbar_fn).call(this);
6749
+ if (name === "fullscreen") __privateMethod(this, _FxEditor_instances, syncBodyOverflow_fn).call(this);
6750
+ __privateMethod(this, _FxEditor_instances, syncChrome_fn).call(this);
6751
+ }
6752
+ formResetCallback() {
6753
+ __privateSet(this, _dirty, false);
6754
+ this.value = __privateGet(this, _defaultValue);
6755
+ }
6756
+ getFormValue() {
6757
+ return this.hasAttr("disabled") ? null : this.value;
6758
+ }
6759
+ render() {
6760
+ if (__privateGet(this, _built)) return;
6761
+ this.setTemplate(`
6762
+ <div class="editor" part="editor">
6763
+ <div class="toolbar" part="toolbar" role="toolbar" aria-label="Formatação" aria-orientation="horizontal"></div>
6764
+ <div class="body">
6765
+ <div class="content" part="content" contenteditable="true" role="textbox"
6766
+ aria-multiline="true" spellcheck="true"></div>
6767
+ <textarea class="source" part="source" spellcheck="false" hidden aria-label="HTML do conteúdo"></textarea>
6768
+ <div class="media-overlay" part="media-overlay" hidden>
6769
+ <span class="media-handle nw" data-handle="nw"></span>
6770
+ <span class="media-handle ne" data-handle="ne"></span>
6771
+ <span class="media-handle sw" data-handle="sw"></span>
6772
+ <span class="media-handle se" data-handle="se"></span>
6773
+ <span class="media-size"></span>
6774
+ <button type="button" class="media-delete" data-media-action="delete" aria-label="Excluir mídia" title="Excluir mídia">${fenixIconHtml("delete")}</button>
6775
+ </div>
6776
+ <div class="link-overlay" part="link-overlay" hidden>
6777
+ <button type="button" data-link-action="open">Abrir</button>
6778
+ <button type="button" data-link-action="edit">Editar</button>
6779
+ <button type="button" data-link-action="remove">Remover</button>
6780
+ </div>
6781
+ </div>
6782
+ <div class="statusbar" part="statusbar">
6783
+ <span class="count"></span>
6784
+ <span class="source-actions" hidden>
6785
+ <button type="button" class="btn source-apply">Aplicar</button>
6786
+ <button type="button" class="btn source-cancel">Cancelar</button>
6787
+ </span>
6788
+ <slot name="footer"></slot>
6789
+ </div>
6790
+ <input type="file" class="image-picker" accept="image/*" hidden>
6791
+ <div class="backdrop" part="dialog" hidden>
6792
+ <div class="dialog" role="dialog" aria-modal="true" aria-labelledby="fx-editor-dialog-title">
6793
+ <header>
6794
+ <h3 id="fx-editor-dialog-title"></h3>
6795
+ <button type="button" class="dialog-close" aria-label="Fechar">×</button>
6796
+ </header>
6797
+ <div class="dialog-body"></div>
6798
+ <footer>
6799
+ <button type="button" class="btn dialog-cancel">Cancelar</button>
6800
+ <button type="button" class="btn dialog-ok">Aplicar</button>
6801
+ </footer>
6802
+ </div>
6803
+ </div>
6804
+ </div>
6805
+ `);
6806
+ __privateSet(this, _built, true);
6807
+ __privateSet(this, _imagePicker, this.root.querySelector(".image-picker"));
6808
+ __privateMethod(this, _FxEditor_instances, bindEvents_fn).call(this);
6809
+ __privateSet(this, _eventsBound, true);
6810
+ __privateMethod(this, _FxEditor_instances, renderToolbar_fn).call(this);
6811
+ __privateMethod(this, _FxEditor_instances, syncChrome_fn).call(this);
6812
+ }
6813
+ /* ---------------------------------------------------------------------
6814
+ * API pública
6815
+ * ------------------------------------------------------------------ */
6816
+ focus(options) {
6817
+ this.editor?.focus(options ?? { preventScroll: true });
6818
+ }
6819
+ blur() {
6820
+ this.editor?.blur();
6821
+ }
6822
+ execute(command, value) {
6823
+ if (value !== void 0) {
6824
+ __privateMethod(this, _FxEditor_instances, restoreSelection_fn).call(this);
6825
+ nativeExec("styleWithCSS", "true");
6826
+ nativeExec(command, value);
6827
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
6828
+ return;
6829
+ }
6830
+ __privateMethod(this, _FxEditor_instances, execCommand_fn).call(this, command);
6831
+ }
6832
+ insertHTML(html) {
6833
+ const clean = __privateMethod(this, _FxEditor_instances, cleanHtml_fn).call(this, html);
6834
+ const probe = document.createElement("div");
6835
+ probe.innerHTML = clean;
6836
+ const extra = probe.textContent?.length ?? 0;
6837
+ if (!__privateMethod(this, _FxEditor_instances, fits_fn).call(this, extra)) {
6838
+ __privateMethod(this, _FxEditor_instances, limitError_fn).call(this);
6839
+ return;
6840
+ }
6841
+ __privateMethod(this, _FxEditor_instances, restoreSelection_fn).call(this);
6842
+ insertHtml(this.editor, clean);
6843
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
6844
+ }
6845
+ /** Mescla as células tocadas pela seleção. */
6846
+ mergeCells() {
6847
+ const table = __privateMethod(this, _FxEditor_instances, currentTable_fn).call(this);
6848
+ const editor = this.editor;
6849
+ const range = editor ? getRange(editor, __privateGet(this, _lastRange)) : null;
6850
+ if (!table || !range) return false;
6851
+ const merged = mergeSelectedCells(table, range);
6852
+ if (!merged) return false;
6853
+ placeCaretAtEnd(merged);
6854
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
6855
+ return true;
6856
+ }
6857
+ /** Divide a célula mesclada atual. */
6858
+ splitCell() {
6859
+ const table = __privateMethod(this, _FxEditor_instances, currentTable_fn).call(this);
6860
+ const editor = this.editor;
6861
+ const range = editor ? getRange(editor, __privateGet(this, _lastRange)) : null;
6862
+ const cell = editor ? closestInEditor(editor, range?.startContainer ?? null, "td,th") : null;
6863
+ if (!table || !cell) return false;
6864
+ const created = splitCell(table, cell);
6865
+ if (!created.length) return false;
6866
+ placeCaretAtEnd(cell);
6867
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
6868
+ return true;
6869
+ }
6870
+ getHtml() {
6871
+ return this.value;
6872
+ }
6873
+ setHtml(html) {
6874
+ this.value = html;
6875
+ }
6876
+ getText() {
6877
+ return this.text;
6878
+ }
6879
+ clear() {
6880
+ this.value = "";
6881
+ __privateSet(this, _dirty, false);
6882
+ __privateMethod(this, _FxEditor_instances, applyValue_fn).call(this, "");
6883
+ this.updateFormValue();
6884
+ __privateMethod(this, _FxEditor_instances, emitChange_fn).call(this);
6885
+ this.focus();
6886
+ }
6887
+ undo() {
6888
+ this.execute("undo");
6889
+ }
6890
+ redo() {
6891
+ this.execute("redo");
6892
+ }
6893
+ saveSelection() {
6894
+ if (this.editor) __privateSet(this, _lastRange, saveRange(this.editor) ?? __privateGet(this, _lastRange));
6895
+ }
6896
+ restoreSelection() {
6897
+ __privateMethod(this, _FxEditor_instances, restoreSelection_fn).call(this);
6898
+ }
6899
+ openDialog(kind) {
6900
+ if (kind === "link") void __privateMethod(this, _FxEditor_instances, openLinkDialog_fn).call(this);
6901
+ else if (kind === "image") void __privateMethod(this, _FxEditor_instances, openImageDialog_fn).call(this);
6902
+ else void __privateMethod(this, _FxEditor_instances, openVideoDialog_fn).call(this);
6903
+ }
6904
+ toggleSource() {
6905
+ __privateMethod(this, _FxEditor_instances, toggleSource_fn).call(this);
6906
+ }
6907
+ toggleFullscreen() {
6908
+ this.fullscreen = !this.fullscreen;
6909
+ }
6910
+ };
6911
+ _built = new WeakMap();
6912
+ _eventsBound = new WeakMap();
6913
+ _initialized = new WeakMap();
6914
+ _defaultValue = new WeakMap();
6915
+ _value = new WeakMap();
6916
+ _dirty = new WeakMap();
6917
+ _sourceMode = new WeakMap();
6918
+ _lastRange = new WeakMap();
6919
+ _rafId = new WeakMap();
6920
+ _emitTimer = new WeakMap();
6921
+ _lastLimitError = new WeakMap();
6922
+ _snapshotTimer = new WeakMap();
6923
+ _history = new WeakMap();
6924
+ _historyIndex = new WeakMap();
6925
+ _imagePicker = new WeakMap();
6926
+ _mediaFigure = new WeakMap();
6927
+ _linkAnchor = new WeakMap();
6928
+ _resizeDispose = new WeakMap();
6929
+ _dialogReturnFocus = new WeakMap();
6930
+ _dialogResolve = new WeakMap();
6931
+ _disposers = new WeakMap();
6932
+ _selectionRaf = new WeakMap();
6933
+ _bodyOverflow = new WeakMap();
6934
+ _FxEditor_instances = new WeakSet();
6935
+ on_fn = function(target, type, handler, options) {
6936
+ target.addEventListener(type, handler, options);
6937
+ __privateGet(this, _disposers).push(() => target.removeEventListener(type, handler, options));
6938
+ };
6939
+ bindEvents_fn = function() {
6940
+ const editor = this.editor;
6941
+ const toolbar = this.toolbarEl;
6942
+ if (!editor || !toolbar) return;
6943
+ __privateMethod(this, _FxEditor_instances, on_fn).call(this, toolbar, "mousedown", (event) => {
6944
+ const target = event.target;
6945
+ __privateSet(this, _lastRange, saveRange(editor) ?? __privateGet(this, _lastRange));
6946
+ __privateMethod(this, _FxEditor_instances, hideLinkOverlay_fn).call(this);
6947
+ if (target.closest("button")) event.preventDefault();
6948
+ });
6949
+ __privateMethod(this, _FxEditor_instances, on_fn).call(this, toolbar, "click", (event) => {
6950
+ const target = event.target.closest("[data-cmd],[data-action]");
6951
+ if (!target || target.disabled) return;
6952
+ const cmd = target.dataset.cmd;
6953
+ const action = target.dataset.action;
6954
+ if (cmd) __privateMethod(this, _FxEditor_instances, execCommand_fn).call(this, cmd);
6955
+ else if (action) __privateMethod(this, _FxEditor_instances, action_fn).call(this, action);
6956
+ });
6957
+ __privateMethod(this, _FxEditor_instances, on_fn).call(this, toolbar, "change", (event) => {
6958
+ const select = event.target.closest("[data-block]");
6959
+ if (!select) return;
6960
+ __privateMethod(this, _FxEditor_instances, restoreSelection_fn).call(this);
6961
+ formatBlock(editor, select.value || "p");
6962
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
6963
+ });
6964
+ __privateMethod(this, _FxEditor_instances, on_fn).call(this, toolbar, "input", (event) => {
6965
+ const input = event.target.closest("[data-color]");
6966
+ if (!input) return;
6967
+ __privateMethod(this, _FxEditor_instances, restoreSelection_fn).call(this);
6968
+ nativeExec("styleWithCSS", "true");
6969
+ nativeExec(input.dataset.color ?? "foreColor", input.value);
6970
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
6971
+ });
6972
+ __privateMethod(this, _FxEditor_instances, on_fn).call(this, toolbar, "keydown", (event) => __privateMethod(this, _FxEditor_instances, handleToolbarKeydown_fn).call(this, event));
6973
+ __privateMethod(this, _FxEditor_instances, on_fn).call(this, toolbar, "focusin", (event) => {
6974
+ const button = event.target.closest(".tb-btn");
6975
+ if (button) __privateMethod(this, _FxEditor_instances, moveToolbarTabStop_fn).call(this, button);
6976
+ });
6977
+ __privateMethod(this, _FxEditor_instances, on_fn).call(this, editor, "input", (event) => {
6978
+ event.stopPropagation();
6979
+ __privateMethod(this, _FxEditor_instances, hideLinkOverlay_fn).call(this);
6980
+ __privateSet(this, _dirty, true);
6981
+ __privateMethod(this, _FxEditor_instances, updateEmpty_fn).call(this);
6982
+ __privateMethod(this, _FxEditor_instances, scheduleEmit_fn).call(this);
6983
+ __privateMethod(this, _FxEditor_instances, scheduleSnapshot_fn).call(this);
6984
+ });
6985
+ __privateMethod(this, _FxEditor_instances, on_fn).call(this, editor, "focus", () => {
6986
+ this.dispatchEvent(new CustomEvent("focus", { bubbles: true, composed: true }));
6987
+ });
6988
+ __privateMethod(this, _FxEditor_instances, on_fn).call(this, editor, "blur", () => {
6989
+ __privateSet(this, _lastRange, saveRange(editor) ?? __privateGet(this, _lastRange));
6990
+ __privateMethod(this, _FxEditor_instances, emitNow_fn).call(this);
6991
+ __privateMethod(this, _FxEditor_instances, emitChange_fn).call(this);
6992
+ this.dispatchEvent(new CustomEvent("blur", { bubbles: true, composed: true }));
6993
+ });
6994
+ __privateMethod(this, _FxEditor_instances, on_fn).call(this, editor, "keydown", (event) => __privateMethod(this, _FxEditor_instances, handleKeydown_fn).call(this, event));
6995
+ __privateMethod(this, _FxEditor_instances, on_fn).call(this, editor, "beforeinput", (event) => __privateMethod(this, _FxEditor_instances, handleBeforeInput_fn).call(this, event));
6996
+ __privateMethod(this, _FxEditor_instances, on_fn).call(this, editor, "paste", (event) => __privateMethod(this, _FxEditor_instances, handlePaste_fn).call(this, event));
6997
+ __privateMethod(this, _FxEditor_instances, on_fn).call(this, editor, "dragover", (event) => event.preventDefault());
6998
+ __privateMethod(this, _FxEditor_instances, on_fn).call(this, editor, "drop", (event) => __privateMethod(this, _FxEditor_instances, handleDrop_fn).call(this, event));
6999
+ __privateMethod(this, _FxEditor_instances, on_fn).call(this, editor, "dblclick", (event) => __privateMethod(this, _FxEditor_instances, handleDoubleClick_fn).call(this, event));
7000
+ __privateMethod(this, _FxEditor_instances, on_fn).call(this, editor, "click", (event) => __privateMethod(this, _FxEditor_instances, handleEditorClick_fn).call(this, event));
7001
+ __privateMethod(this, _FxEditor_instances, on_fn).call(this, editor, "scroll", () => {
7002
+ __privateMethod(this, _FxEditor_instances, positionMediaOverlay_fn).call(this);
7003
+ __privateMethod(this, _FxEditor_instances, positionLinkOverlay_fn).call(this);
7004
+ }, { passive: true });
7005
+ __privateMethod(this, _FxEditor_instances, on_fn).call(this, window, "resize", () => {
7006
+ __privateMethod(this, _FxEditor_instances, positionMediaOverlay_fn).call(this);
7007
+ __privateMethod(this, _FxEditor_instances, positionLinkOverlay_fn).call(this);
7008
+ });
7009
+ const overlay = this.root.querySelector(".media-overlay");
7010
+ if (overlay) {
7011
+ __privateMethod(this, _FxEditor_instances, on_fn).call(this, overlay, "pointerdown", (event) => __privateMethod(this, _FxEditor_instances, startResize_fn).call(this, event));
7012
+ __privateMethod(this, _FxEditor_instances, on_fn).call(this, overlay, "click", (event) => {
7013
+ const action = event.target.closest("[data-media-action]");
7014
+ if (action?.dataset.mediaAction === "delete") __privateMethod(this, _FxEditor_instances, deleteMediaFigure_fn).call(this);
7015
+ });
7016
+ }
7017
+ const linkOverlay = this.root.querySelector(".link-overlay");
7018
+ if (linkOverlay) {
7019
+ __privateMethod(this, _FxEditor_instances, on_fn).call(this, linkOverlay, "click", (event) => __privateMethod(this, _FxEditor_instances, handleLinkOverlayClick_fn).call(this, event));
7020
+ }
7021
+ if (this.source) {
7022
+ __privateMethod(this, _FxEditor_instances, on_fn).call(this, this.source, "input", () => {
7023
+ __privateSet(this, _dirty, true);
7024
+ __privateMethod(this, _FxEditor_instances, scheduleEmit_fn).call(this);
7025
+ });
7026
+ }
7027
+ if (__privateGet(this, _imagePicker)) {
7028
+ __privateMethod(this, _FxEditor_instances, on_fn).call(this, __privateGet(this, _imagePicker), "change", () => {
7029
+ const file = __privateGet(this, _imagePicker)?.files?.[0];
7030
+ if (file) void __privateMethod(this, _FxEditor_instances, handleImageFile_fn).call(this, file);
7031
+ if (__privateGet(this, _imagePicker)) __privateGet(this, _imagePicker).value = "";
7032
+ });
7033
+ }
7034
+ const backdrop = this.root.querySelector(".backdrop");
7035
+ if (backdrop) {
7036
+ __privateMethod(this, _FxEditor_instances, on_fn).call(this, backdrop, "click", (event) => {
7037
+ if (event.target === backdrop) __privateMethod(this, _FxEditor_instances, releaseDialog_fn).call(this, null);
7038
+ });
7039
+ __privateMethod(this, _FxEditor_instances, on_fn).call(this, backdrop, "keydown", (event) => __privateMethod(this, _FxEditor_instances, trapDialogFocus_fn).call(this, event));
7040
+ }
7041
+ const dialogBody = this.root.querySelector(".dialog-body");
7042
+ if (dialogBody) {
7043
+ __privateMethod(this, _FxEditor_instances, on_fn).call(this, dialogBody, "keydown", (event) => {
7044
+ const target = event.target;
7045
+ if (event.key === "Enter" && target.tagName !== "BUTTON") {
7046
+ event.preventDefault();
7047
+ __privateMethod(this, _FxEditor_instances, submitDialog_fn).call(this);
7048
+ }
7049
+ });
7050
+ }
7051
+ for (const selector of [".dialog-close", ".dialog-cancel"]) {
7052
+ const btn = this.root.querySelector(selector);
7053
+ if (btn) __privateMethod(this, _FxEditor_instances, on_fn).call(this, btn, "click", () => __privateMethod(this, _FxEditor_instances, releaseDialog_fn).call(this, null));
7054
+ }
7055
+ const ok = this.root.querySelector(".dialog-ok");
7056
+ if (ok) __privateMethod(this, _FxEditor_instances, on_fn).call(this, ok, "click", () => __privateMethod(this, _FxEditor_instances, submitDialog_fn).call(this));
7057
+ const applySource = this.root.querySelector(".source-apply");
7058
+ if (applySource) __privateMethod(this, _FxEditor_instances, on_fn).call(this, applySource, "click", () => __privateMethod(this, _FxEditor_instances, applySource_fn).call(this));
7059
+ const cancelSource = this.root.querySelector(".source-cancel");
7060
+ if (cancelSource) __privateMethod(this, _FxEditor_instances, on_fn).call(this, cancelSource, "click", () => __privateMethod(this, _FxEditor_instances, cancelSource_fn).call(this));
7061
+ };
7062
+ syncChrome_fn = function() {
7063
+ const editor = this.editor;
7064
+ if (!editor) return;
7065
+ const locked = this.disabled || this.readonly;
7066
+ editor.setAttribute("contenteditable", locked ? "false" : "true");
7067
+ editor.setAttribute("aria-readonly", String(this.readonly));
7068
+ editor.setAttribute("aria-disabled", String(this.disabled));
7069
+ if (this.placeholder) editor.setAttribute("data-placeholder", this.placeholder);
7070
+ else editor.removeAttribute("data-placeholder");
7071
+ const toolbar = this.toolbarEl;
7072
+ toolbar?.toggleAttribute("hidden", this.toolbar === "none" || this.readonly);
7073
+ for (const control of Array.from(this.root.querySelectorAll(
7074
+ ".tb-btn, .tb-select, .tb-color input"
7075
+ ))) {
7076
+ const isSourceToggle = control.closest('[data-action="source"]') !== null;
7077
+ const isFullscreenToggle = control.closest('[data-action="fullscreen"]') !== null;
7078
+ control.disabled = this.readonly || this.disabled || __privateGet(this, _sourceMode) && !isSourceToggle && !isFullscreenToggle;
7079
+ }
7080
+ __privateMethod(this, _FxEditor_instances, applyCssVars_fn).call(this);
7081
+ __privateMethod(this, _FxEditor_instances, updateEmpty_fn).call(this);
7082
+ __privateMethod(this, _FxEditor_instances, updateCount_fn).call(this);
7083
+ __privateMethod(this, _FxEditor_instances, validate_fn).call(this);
7084
+ __privateMethod(this, _FxEditor_instances, updateSourceUi_fn).call(this);
7085
+ const fullscreenButton = this.root.querySelector('[data-action="fullscreen"]');
7086
+ fullscreenButton?.classList.toggle("active", this.fullscreen);
7087
+ fullscreenButton?.setAttribute("aria-pressed", String(this.fullscreen));
7088
+ if (locked || __privateGet(this, _sourceMode)) __privateMethod(this, _FxEditor_instances, hideMediaOverlay_fn).call(this);
7089
+ };
7090
+ applyCssVars_fn = function() {
7091
+ const min = this.getAttr("min-height");
7092
+ const max = this.getAttr("max-height");
7093
+ if (min) this.style.setProperty("--fx-editor-min-height", min);
7094
+ else this.style.removeProperty("--fx-editor-min-height");
7095
+ if (max) this.style.setProperty("--fx-editor-max-height", max);
7096
+ else this.style.removeProperty("--fx-editor-max-height");
7097
+ };
7098
+ updateEmpty_fn = function() {
7099
+ const editor = this.editor;
7100
+ if (!editor) return;
7101
+ const empty = !editor.textContent?.trim() && !editor.querySelector("img,video,iframe,table,hr");
7102
+ editor.classList.toggle("is-empty", empty);
7103
+ };
7104
+ updateCount_fn = function() {
7105
+ const count = this.root.querySelector(".count");
7106
+ if (!count) return;
7107
+ const statusbar = this.root.querySelector(".statusbar");
7108
+ const hasFooter = this.querySelector('[slot="footer"]') !== null;
7109
+ if (statusbar) statusbar.hidden = !__privateGet(this, _sourceMode) && !this.hasAttr("show-count") && !hasFooter;
7110
+ if (!this.hasAttr("show-count")) {
7111
+ count.textContent = "";
7112
+ return;
7113
+ }
7114
+ const text = this.text;
7115
+ const words = text ? text.split(/\s+/).filter(Boolean).length : 0;
7116
+ count.textContent = `${words} palavra${words === 1 ? "" : "s"} · ${text.length} caractere${text.length === 1 ? "" : "s"}`;
7117
+ };
7118
+ fits_fn = function(extra) {
7119
+ const max = this.maxlength;
7120
+ if (!max) return true;
7121
+ const editor = this.editor;
7122
+ const range = editor ? getRange(editor, __privateGet(this, _lastRange)) : null;
7123
+ const selected = range && !range.collapsed ? range.toString().length : 0;
7124
+ return this.text.length - selected + extra <= max;
7125
+ };
7126
+ limitError_fn = function() {
7127
+ const now = Date.now();
7128
+ if (now - __privateGet(this, _lastLimitError) < 1200) return;
7129
+ __privateSet(this, _lastLimitError, now);
7130
+ __privateMethod(this, _FxEditor_instances, error_fn).call(this, `Limite de ${this.maxlength.toLocaleString("pt-BR")} caracteres atingido.`);
7131
+ };
7132
+ validate_fn = function() {
7133
+ if (!this.internals || typeof this.internals.setValidity !== "function") return;
7134
+ try {
7135
+ if (this.required && !this.text) {
7136
+ this.internals.setValidity(
7137
+ { valueMissing: true },
7138
+ "Preencha este campo.",
7139
+ this.editor ?? void 0
7140
+ );
7141
+ } else {
7142
+ this.internals.setValidity({});
7143
+ }
7144
+ } catch {
7145
+ }
7146
+ };
7147
+ /** Sanitiza + normaliza em uma única parseada do HTML. */
7148
+ cleanHtml_fn = function(html, forEditor = false) {
7149
+ const body = sanitizeBody(html);
7150
+ if (!body) return forEditor ? "<p><br></p>" : "";
7151
+ return normalizeElement(body, { forEditor });
7152
+ };
7153
+ serialize_fn = function() {
7154
+ if (__privateGet(this, _sourceMode) && this.source) {
7155
+ return __privateMethod(this, _FxEditor_instances, cleanHtml_fn).call(this, this.source.value);
7156
+ }
7157
+ const editor = this.editor;
7158
+ if (!editor) return "";
7159
+ if (!editor.textContent?.trim() && !editor.querySelector("img,video,iframe,table,hr")) return "";
7160
+ let clean = __privateMethod(this, _FxEditor_instances, cleanHtml_fn).call(this, editor.innerHTML);
7161
+ if (this.sanitizeHtml) {
7162
+ clean = __privateMethod(this, _FxEditor_instances, cleanHtml_fn).call(this, this.sanitizeHtml(clean));
7163
+ }
7164
+ return clean;
7165
+ };
7166
+ applyValue_fn = function(html) {
7167
+ const editor = this.editor;
7168
+ if (!editor) return;
7169
+ __privateMethod(this, _FxEditor_instances, hideMediaOverlay_fn).call(this);
7170
+ const normalized = __privateMethod(this, _FxEditor_instances, cleanHtml_fn).call(this, html, true);
7171
+ if (editor.innerHTML !== normalized) editor.innerHTML = normalized;
7172
+ __privateMethod(this, _FxEditor_instances, hideLinkOverlay_fn).call(this);
7173
+ __privateMethod(this, _FxEditor_instances, hideMediaOverlay_fn).call(this);
7174
+ __privateMethod(this, _FxEditor_instances, decorateMediaFigures_fn).call(this);
7175
+ __privateMethod(this, _FxEditor_instances, updateEmpty_fn).call(this);
7176
+ __privateMethod(this, _FxEditor_instances, updateCount_fn).call(this);
7177
+ };
7178
+ scheduleEmit_fn = function() {
7179
+ cancelAnimationFrame(__privateGet(this, _rafId));
7180
+ if (__privateGet(this, _emitTimer)) {
7181
+ clearTimeout(__privateGet(this, _emitTimer));
7182
+ __privateSet(this, _emitTimer, null);
7183
+ }
7184
+ const size = this.editor?.textContent?.length ?? 0;
7185
+ if (size < LARGE_DOC_CHARS) {
7186
+ __privateSet(this, _rafId, requestAnimationFrame(() => __privateMethod(this, _FxEditor_instances, emitNow_fn).call(this)));
7187
+ return;
7188
+ }
7189
+ __privateSet(this, _emitTimer, setTimeout(() => {
7190
+ __privateSet(this, _emitTimer, null);
7191
+ __privateMethod(this, _FxEditor_instances, emitNow_fn).call(this);
7192
+ }, EMIT_DEBOUNCE_MS));
7193
+ };
7194
+ emitNow_fn = function() {
7195
+ cancelAnimationFrame(__privateGet(this, _rafId));
7196
+ if (__privateGet(this, _emitTimer)) {
7197
+ clearTimeout(__privateGet(this, _emitTimer));
7198
+ __privateSet(this, _emitTimer, null);
7199
+ }
7200
+ const value = __privateMethod(this, _FxEditor_instances, serialize_fn).call(this);
7201
+ __privateSet(this, _value, value);
7202
+ __privateSet(this, _dirty, false);
7203
+ const detail = { value, html: value, text: this.text };
7204
+ for (const type of ["input", "valueChange"]) {
7205
+ this.dispatchEvent(new CustomEvent(type, { bubbles: true, composed: true, detail }));
7206
+ }
7207
+ __privateMethod(this, _FxEditor_instances, updateCount_fn).call(this);
7208
+ __privateMethod(this, _FxEditor_instances, validate_fn).call(this);
7209
+ };
7210
+ emitChange_fn = function() {
7211
+ const value = this.value;
7212
+ const detail = { value, html: value, text: this.text };
7213
+ this.dispatchEvent(new CustomEvent("change", { bubbles: true, composed: true, detail }));
7214
+ };
7215
+ pushHistory_fn = function(html) {
7216
+ if (__privateGet(this, _history)[__privateGet(this, _historyIndex)] === html) return;
7217
+ __privateSet(this, _history, __privateGet(this, _history).slice(0, __privateGet(this, _historyIndex) + 1));
7218
+ __privateGet(this, _history).push(html);
7219
+ if (__privateGet(this, _history).length > HISTORY_LIMIT) __privateGet(this, _history).shift();
7220
+ __privateSet(this, _historyIndex, __privateGet(this, _history).length - 1);
7221
+ };
7222
+ scheduleSnapshot_fn = function() {
7223
+ if (__privateGet(this, _snapshotTimer)) clearTimeout(__privateGet(this, _snapshotTimer));
7224
+ __privateSet(this, _snapshotTimer, setTimeout(() => {
7225
+ __privateMethod(this, _FxEditor_instances, pushHistory_fn).call(this, __privateGet(this, _dirty) ? __privateMethod(this, _FxEditor_instances, serialize_fn).call(this) : __privateGet(this, _value));
7226
+ }, 600));
7227
+ };
7228
+ historyUndo_fn = function() {
7229
+ if (__privateGet(this, _historyIndex) <= 0) return;
7230
+ __privateWrapper(this, _historyIndex)._--;
7231
+ __privateMethod(this, _FxEditor_instances, applyValue_fn).call(this, __privateGet(this, _history)[__privateGet(this, _historyIndex)]);
7232
+ if (this.editor) placeCaretAtEnd(this.editor);
7233
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
7234
+ };
7235
+ historyRedo_fn = function() {
7236
+ if (__privateGet(this, _historyIndex) >= __privateGet(this, _history).length - 1) return;
7237
+ __privateWrapper(this, _historyIndex)._++;
7238
+ __privateMethod(this, _FxEditor_instances, applyValue_fn).call(this, __privateGet(this, _history)[__privateGet(this, _historyIndex)]);
7239
+ if (this.editor) placeCaretAtEnd(this.editor);
7240
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
7241
+ };
7242
+ bindGlobalSelection_fn = function() {
7243
+ const handler = () => {
7244
+ const editor = this.editor;
7245
+ if (!editor) return;
7246
+ const range = getRange(editor, __privateGet(this, _lastRange));
7247
+ if (!range) return;
7248
+ __privateSet(this, _lastRange, range.cloneRange());
7249
+ cancelAnimationFrame(__privateGet(this, _selectionRaf));
7250
+ __privateSet(this, _selectionRaf, requestAnimationFrame(() => __privateMethod(this, _FxEditor_instances, updateToolbar_fn).call(this)));
7251
+ };
7252
+ document.addEventListener("selectionchange", handler);
7253
+ __privateGet(this, _disposers).push(() => document.removeEventListener("selectionchange", handler));
7254
+ };
7255
+ restoreSelection_fn = function() {
7256
+ const editor = this.editor;
7257
+ if (!editor) return;
7258
+ if (__privateGet(this, _lastRange)) restoreRange(editor, __privateGet(this, _lastRange));
7259
+ else editor.focus({ preventScroll: true });
7260
+ };
7261
+ syncBodyOverflow_fn = function() {
7262
+ if (this.fullscreen) {
7263
+ __privateSet(this, _bodyOverflow, document.body.style.overflow);
7264
+ document.body.style.overflow = "hidden";
7265
+ } else {
7266
+ document.body.style.overflow = __privateGet(this, _bodyOverflow);
7267
+ }
7268
+ };
7269
+ updateSourceUi_fn = function() {
7270
+ const source = this.source;
7271
+ const content = this.editor;
7272
+ const actions = this.root.querySelector(".source-actions");
7273
+ if (actions) actions.toggleAttribute("hidden", !__privateGet(this, _sourceMode));
7274
+ if (source) source.hidden = !__privateGet(this, _sourceMode);
7275
+ if (content) content.hidden = __privateGet(this, _sourceMode);
7276
+ const toggle = this.root.querySelector('[data-action="source"]');
7277
+ toggle?.classList.toggle("active", __privateGet(this, _sourceMode));
7278
+ toggle?.setAttribute("aria-pressed", String(__privateGet(this, _sourceMode)));
7279
+ };
7280
+ releaseDialog_fn = function(result) {
7281
+ const backdrop = this.root.querySelector(".backdrop");
7282
+ backdrop?.setAttribute("hidden", "");
7283
+ const resolve = __privateGet(this, _dialogResolve);
7284
+ __privateSet(this, _dialogResolve, null);
7285
+ const returnFocus = __privateGet(this, _dialogReturnFocus);
7286
+ __privateSet(this, _dialogReturnFocus, null);
7287
+ resolve?.(result);
7288
+ if (returnFocus && returnFocus.isConnected) returnFocus.focus({ preventScroll: true });
7289
+ else __privateMethod(this, _FxEditor_instances, restoreSelection_fn).call(this);
7290
+ };
7291
+ renderToolbar_fn = function() {
7292
+ const toolbar = this.toolbarEl;
7293
+ if (!toolbar) return;
7294
+ const preset = this.toolbar;
7295
+ if (preset === "none") {
7296
+ toolbar.innerHTML = "";
7297
+ toolbar.hidden = true;
7298
+ return;
7299
+ }
7300
+ toolbar.hidden = false;
7301
+ const features = new Set(
7302
+ this.getAttr("features").split(",").map((item) => item.trim()).filter(Boolean)
7303
+ );
7304
+ const has = (feature) => features.size === 0 || features.has(feature);
7305
+ const button = (cmd, icon, label, action = "") => `<button type="button" class="tb-btn" tabindex="-1" ${action ? `data-action="${action}"` : `data-cmd="${cmd}"`} title="${label}" aria-label="${label}" aria-pressed="false">${fenixIconHtml(icon)}</button>`;
7306
+ const select = `<select class="tb-select" data-block aria-label="Formato do bloco">
7307
+ <option value="p">Parágrafo</option>
7308
+ <option value="h1">Título 1</option>
7309
+ <option value="h2">Título 2</option>
7310
+ <option value="h3">Título 3</option>
7311
+ <option value="h4">Título 4</option>
7312
+ <option value="blockquote">Citação</option>
7313
+ <option value="pre">Código</option>
7314
+ </select>`;
7315
+ const history = button("undo", "undo", "Desfazer") + button("redo", "redo", "Refazer");
7316
+ const inline = [
7317
+ button("bold", "format_bold", "Negrito"),
7318
+ button("italic", "format_italic", "Itálico"),
7319
+ button("underline", "format_underlined", "Sublinhado"),
7320
+ button("strikeThrough", "format_strikethrough", "Riscado"),
7321
+ button("code", "code", "Código inline"),
7322
+ button("removeFormat", "format_clear", "Limpar formatação")
7323
+ ].join("");
7324
+ const color = [
7325
+ `<label class="tb-color" title="Cor do texto"><input type="color" data-color="foreColor" value="#111827" aria-label="Cor do texto"></label>`,
7326
+ `<label class="tb-color" title="Realçar texto"><input type="color" data-color="hiliteColor" value="#fef08a" aria-label="Realçar texto"></label>`
7327
+ ].join("");
7328
+ const alignGroup = [
7329
+ button("justifyLeft", "format_align_left", "Alinhar à esquerda"),
7330
+ button("justifyCenter", "format_align_center", "Centralizar"),
7331
+ button("justifyRight", "format_align_right", "Alinhar à direita"),
7332
+ button("justifyFull", "format_align_justify", "Justificar")
7333
+ ].join("");
7334
+ const indentation = [
7335
+ button("outdent", "format_indent_decrease", "Diminuir recuo", "outdent"),
7336
+ button("indent", "format_indent_increase", "Aumentar recuo", "indent")
7337
+ ].join("");
7338
+ const lists = [
7339
+ button("insertUnorderedList", "format_list_bulleted", "Lista com marcadores"),
7340
+ button("insertOrderedList", "format_list_numbered", "Lista numerada")
7341
+ ].join("");
7342
+ const blocks = button("", "format_quote", "Citação", "blockquote") + button("", "code_blocks", "Bloco de código", "codeblock");
7343
+ const insert = [];
7344
+ if (has("link")) {
7345
+ insert.push(button("", "link", "Link (Ctrl+K)", "link"));
7346
+ insert.push(button("", "link_off", "Remover link", "unlink"));
7347
+ }
7348
+ if (preset !== "minimal") {
7349
+ if (has("image")) insert.push(button("", "image", "Imagem", "image"));
7350
+ if (has("video")) insert.push(button("", "videocam", "Vídeo", "video"));
7351
+ if (has("table")) insert.push(button("", "table", "Tabela", "table"));
7352
+ if (has("columns")) insert.push(button("", "view_column", "Colunas", "columns"));
7353
+ insert.push(button("insertHorizontalRule", "horizontal_rule", "Linha horizontal"));
7354
+ }
7355
+ const tools = [];
7356
+ if (preset === "standard" || preset === "full") {
7357
+ if (has("source")) tools.push(button("", "code", "Ver HTML", "source"));
7358
+ if (has("fullscreen")) tools.push(button("", "fullscreen", "Tela cheia", "fullscreen"));
7359
+ }
7360
+ const groups = [history, select + inline];
7361
+ if (preset !== "minimal") groups.push(lists);
7362
+ if (preset !== "minimal" && preset !== "basic") {
7363
+ groups.push(color, alignGroup, indentation, blocks);
7364
+ }
7365
+ if (insert.length) groups.push(insert.join(""));
7366
+ if (tools.length) groups.push(tools.join(""));
7367
+ toolbar.innerHTML = groups.map((group) => `<div class="tb-group">${group}</div>`).join("") + `<div class="panel" data-panel hidden></div>`;
7368
+ toolbar.querySelector(".tb-btn")?.setAttribute("tabindex", "0");
7369
+ };
7370
+ updateToolbar_fn = function() {
7371
+ const editor = this.editor;
7372
+ if (!editor) return;
7373
+ const range = getRange(editor, __privateGet(this, _lastRange));
7374
+ if (!range) return;
7375
+ const setActive = (cmd, active) => {
7376
+ const button = this.root.querySelector(`[data-cmd="${cmd}"]`);
7377
+ if (!button) return;
7378
+ button.classList.toggle("active", active);
7379
+ button.setAttribute("aria-pressed", String(active));
7380
+ };
7381
+ const ancestor = (selector) => {
7382
+ const node = range.startContainer;
7383
+ const el = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
7384
+ const match = el?.closest(selector);
7385
+ return Boolean(match && editor.contains(match));
7386
+ };
7387
+ setActive("bold", ancestor("strong,b"));
7388
+ setActive("italic", ancestor("em,i"));
7389
+ setActive("underline", ancestor("u"));
7390
+ setActive("strikeThrough", ancestor("s,del,strike"));
7391
+ setActive("code", ancestor("code"));
7392
+ setActive("insertUnorderedList", ancestor("ul"));
7393
+ setActive("insertOrderedList", ancestor("ol"));
7394
+ const block = getBlock(editor, range);
7395
+ const blockTag = block?.tagName.toLowerCase() ?? "p";
7396
+ const blockSelect = this.root.querySelector("[data-block]");
7397
+ if (blockSelect) {
7398
+ const value = Array.from(blockSelect.options).some((option) => option.value === blockTag) ? blockTag : "p";
7399
+ blockSelect.value = value;
7400
+ }
7401
+ const alignment = block ? block.style.textAlign || "left" : "left";
7402
+ setActive("justifyLeft", alignment === "left" || alignment === "start" || alignment === "");
7403
+ setActive("justifyCenter", alignment === "center");
7404
+ setActive("justifyRight", alignment === "right" || alignment === "end");
7405
+ setActive("justifyFull", alignment === "justify");
7406
+ };
7407
+ handleToolbarKeydown_fn = function(event) {
7408
+ const active = this.root.activeElement;
7409
+ if (!active || !active.classList.contains("tb-btn")) return;
7410
+ if (!["ArrowRight", "ArrowLeft", "Home", "End"].includes(event.key)) return;
7411
+ const controls = Array.from(
7412
+ this.toolbarEl?.querySelectorAll(
7413
+ ".tb-btn:not(:disabled), .tb-select:not(:disabled), .tb-color input:not(:disabled)"
7414
+ ) ?? []
7415
+ );
7416
+ if (!controls.length) return;
7417
+ event.preventDefault();
7418
+ let index = controls.indexOf(active);
7419
+ if (event.key === "Home") index = 0;
7420
+ else if (event.key === "End") index = controls.length - 1;
7421
+ else index = (index + (event.key === "ArrowRight" ? 1 : -1) + controls.length) % controls.length;
7422
+ const next = controls[index];
7423
+ if (!next) return;
7424
+ __privateMethod(this, _FxEditor_instances, moveToolbarTabStop_fn).call(this, next);
7425
+ next.focus();
7426
+ };
7427
+ moveToolbarTabStop_fn = function(active) {
7428
+ this.toolbarEl?.querySelectorAll(".tb-btn").forEach((button) => {
7429
+ button.setAttribute("tabindex", button === active ? "0" : "-1");
7430
+ });
7431
+ };
7432
+ trapDialogFocus_fn = function(event) {
7433
+ if (!__privateGet(this, _dialogResolve) || event.key !== "Tab") return;
7434
+ const dialog = this.root.querySelector(".dialog");
7435
+ if (!dialog) return;
7436
+ const focusables = Array.from(
7437
+ dialog.querySelectorAll(
7438
+ 'button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), a[href], [tabindex]:not([tabindex="-1"])'
7439
+ )
7440
+ );
7441
+ if (!focusables.length) return;
7442
+ const first = focusables[0];
7443
+ const last = focusables[focusables.length - 1];
7444
+ const active = this.root.activeElement;
7445
+ if (event.shiftKey && (active === first || !active || !dialog.contains(active))) {
7446
+ event.preventDefault();
7447
+ last.focus();
7448
+ } else if (!event.shiftKey && (active === last || !active || !dialog.contains(active))) {
7449
+ event.preventDefault();
7450
+ first.focus();
7451
+ }
7452
+ };
7453
+ execCommand_fn = function(cmd) {
7454
+ const editor = this.editor;
7455
+ if (!editor) return;
7456
+ __privateMethod(this, _FxEditor_instances, restoreSelection_fn).call(this);
7457
+ switch (cmd) {
7458
+ case "undo":
7459
+ if (!nativeExec("undo")) __privateMethod(this, _FxEditor_instances, historyUndo_fn).call(this);
7460
+ break;
7461
+ case "redo":
7462
+ if (!nativeExec("redo")) __privateMethod(this, _FxEditor_instances, historyRedo_fn).call(this);
7463
+ break;
7464
+ case "bold":
7465
+ toggleInline(editor, "strong");
7466
+ break;
7467
+ case "italic":
7468
+ toggleInline(editor, "em");
7469
+ break;
7470
+ case "underline":
7471
+ toggleInline(editor, "u");
7472
+ break;
7473
+ case "strikeThrough":
7474
+ toggleInline(editor, "s");
7475
+ break;
7476
+ case "code":
7477
+ toggleInline(editor, "code");
7478
+ break;
7479
+ case "justifyLeft":
7480
+ align(editor, "left");
7481
+ break;
7482
+ case "justifyCenter":
7483
+ align(editor, "center");
7484
+ break;
7485
+ case "justifyRight":
7486
+ align(editor, "right");
7487
+ break;
7488
+ case "justifyFull":
7489
+ align(editor, "justify");
7490
+ break;
7491
+ case "insertUnorderedList":
7492
+ toggleList(editor, false);
7493
+ break;
7494
+ case "insertOrderedList":
7495
+ toggleList(editor, true);
7496
+ break;
7497
+ case "removeFormat":
7498
+ removeFormat(editor);
7499
+ break;
7500
+ case "insertHorizontalRule":
7501
+ insertHtml(editor, "<hr><p><br></p>");
7502
+ break;
7503
+ case "foreColor":
7504
+ case "hiliteColor":
7505
+ nativeExec("styleWithCSS", "true");
7506
+ nativeExec(cmd, this.root.querySelector(`[data-color="${cmd}"]`)?.value);
7507
+ break;
7508
+ default:
7509
+ nativeExec(cmd);
7510
+ }
7511
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
7512
+ };
7513
+ afterCommand_fn = function() {
7514
+ __privateSet(this, _dirty, true);
7515
+ __privateMethod(this, _FxEditor_instances, hideLinkOverlay_fn).call(this);
7516
+ __privateMethod(this, _FxEditor_instances, decorateMediaFigures_fn).call(this);
7517
+ __privateMethod(this, _FxEditor_instances, updateEmpty_fn).call(this);
7518
+ __privateMethod(this, _FxEditor_instances, scheduleEmit_fn).call(this);
7519
+ __privateMethod(this, _FxEditor_instances, scheduleSnapshot_fn).call(this);
7520
+ __privateMethod(this, _FxEditor_instances, updateToolbar_fn).call(this);
7521
+ };
7522
+ action_fn = function(action) {
7523
+ const editor = this.editor;
7524
+ if (!editor) return;
7525
+ switch (action) {
7526
+ case "link":
7527
+ void __privateMethod(this, _FxEditor_instances, openLinkDialog_fn).call(this);
7528
+ break;
7529
+ case "image":
7530
+ void __privateMethod(this, _FxEditor_instances, openImageDialog_fn).call(this);
7531
+ break;
7532
+ case "video":
7533
+ void __privateMethod(this, _FxEditor_instances, openVideoDialog_fn).call(this);
7534
+ break;
7535
+ case "table":
7536
+ __privateMethod(this, _FxEditor_instances, showTablePanel_fn).call(this);
7537
+ break;
7538
+ case "columns":
7539
+ __privateMethod(this, _FxEditor_instances, showColumnsPanel_fn).call(this);
7540
+ break;
7541
+ case "source":
7542
+ __privateMethod(this, _FxEditor_instances, toggleSource_fn).call(this);
7543
+ break;
7544
+ case "fullscreen":
7545
+ this.fullscreen = !this.fullscreen;
7546
+ break;
7547
+ case "blockquote":
7548
+ __privateMethod(this, _FxEditor_instances, restoreSelection_fn).call(this);
7549
+ formatBlock(editor, "blockquote");
7550
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
7551
+ break;
7552
+ case "codeblock":
7553
+ __privateMethod(this, _FxEditor_instances, restoreSelection_fn).call(this);
7554
+ formatBlock(editor, "pre");
7555
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
7556
+ break;
7557
+ case "indent":
7558
+ __privateMethod(this, _FxEditor_instances, restoreSelection_fn).call(this);
7559
+ indent(editor, 24);
7560
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
7561
+ break;
7562
+ case "outdent":
7563
+ __privateMethod(this, _FxEditor_instances, restoreSelection_fn).call(this);
7564
+ indent(editor, -24);
7565
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
7566
+ break;
7567
+ case "clear":
7568
+ __privateMethod(this, _FxEditor_instances, restoreSelection_fn).call(this);
7569
+ removeFormat(editor);
7570
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
7571
+ break;
7572
+ case "unlink":
7573
+ __privateMethod(this, _FxEditor_instances, restoreSelection_fn).call(this);
7574
+ unlink(editor);
7575
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
7576
+ break;
7577
+ }
7578
+ };
7579
+ handleKeydown_fn = function(event) {
7580
+ if (event.isComposing) return;
7581
+ const active = this.root.activeElement;
7582
+ const overlay = this.root.querySelector(".media-overlay");
7583
+ if (__privateGet(this, _mediaFigure) && (event.key === "Delete" || event.key === "Backspace") && (active === __privateGet(this, _mediaFigure) || __privateGet(this, _mediaFigure).contains(active) || Boolean(overlay && active && overlay.contains(active)))) {
7584
+ event.preventDefault();
7585
+ __privateMethod(this, _FxEditor_instances, deleteMediaFigure_fn).call(this);
7586
+ return;
7587
+ }
7588
+ const mod = event.ctrlKey || event.metaKey;
7589
+ const key2 = event.key.toLowerCase();
7590
+ if (mod && !event.altKey) {
7591
+ if (key2 === "b") {
7592
+ event.preventDefault();
7593
+ __privateMethod(this, _FxEditor_instances, execCommand_fn).call(this, "bold");
7594
+ return;
7595
+ }
7596
+ if (key2 === "i") {
7597
+ event.preventDefault();
7598
+ __privateMethod(this, _FxEditor_instances, execCommand_fn).call(this, "italic");
7599
+ return;
7600
+ }
7601
+ if (key2 === "u") {
7602
+ event.preventDefault();
7603
+ __privateMethod(this, _FxEditor_instances, execCommand_fn).call(this, "underline");
7604
+ return;
7605
+ }
7606
+ if (key2 === "k") {
7607
+ event.preventDefault();
7608
+ void __privateMethod(this, _FxEditor_instances, openLinkDialog_fn).call(this);
7609
+ return;
7610
+ }
7611
+ if (key2 === "z" && !event.shiftKey) {
7612
+ event.preventDefault();
7613
+ __privateMethod(this, _FxEditor_instances, execCommand_fn).call(this, "undo");
7614
+ return;
7615
+ }
7616
+ if (key2 === "y" || key2 === "z" && event.shiftKey) {
7617
+ event.preventDefault();
7618
+ __privateMethod(this, _FxEditor_instances, execCommand_fn).call(this, "redo");
7619
+ return;
7620
+ }
7621
+ }
7622
+ if (event.key === "Escape") {
7623
+ if (__privateGet(this, _dialogResolve)) __privateMethod(this, _FxEditor_instances, releaseDialog_fn).call(this, null);
7624
+ else if (__privateGet(this, _sourceMode)) __privateMethod(this, _FxEditor_instances, cancelSource_fn).call(this);
7625
+ else if (this.fullscreen) this.fullscreen = false;
7626
+ else if (__privateGet(this, _linkAnchor)) __privateMethod(this, _FxEditor_instances, hideLinkOverlay_fn).call(this);
7627
+ else if (__privateGet(this, _mediaFigure)) __privateMethod(this, _FxEditor_instances, hideMediaOverlay_fn).call(this);
7628
+ else __privateMethod(this, _FxEditor_instances, hidePanel_fn).call(this);
7629
+ return;
7630
+ }
7631
+ if (event.key === "Tab" && __privateMethod(this, _FxEditor_instances, tabInTable_fn).call(this, event.shiftKey)) {
7632
+ event.preventDefault();
7633
+ }
7634
+ };
7635
+ tabInTable_fn = function(backwards) {
7636
+ const editor = this.editor;
7637
+ if (!editor) return false;
7638
+ const range = getRange(editor, __privateGet(this, _lastRange));
7639
+ if (!range) return false;
7640
+ const cell = closestInEditor(editor, range.startContainer, "td,th");
7641
+ const table = cell?.closest("table");
7642
+ if (!cell || !table) return false;
7643
+ const cells = Array.from(table.querySelectorAll("td,th"));
7644
+ let index = cells.indexOf(cell);
7645
+ if (index < 0) return false;
7646
+ if (backwards) {
7647
+ index = Math.max(0, index - 1);
7648
+ } else {
7649
+ index++;
7650
+ if (index >= cells.length) {
7651
+ const row = cell.parentElement;
7652
+ if (!row) return false;
7653
+ const clone = row.cloneNode(true);
7654
+ for (const cloned of Array.from(clone.querySelectorAll("td,th"))) {
7655
+ cloned.innerHTML = "<p><br></p>";
7656
+ }
7657
+ row.after(clone);
7658
+ cells.push(...Array.from(clone.querySelectorAll("td,th")));
7659
+ index = cells.indexOf(clone.querySelector("td,th"));
7660
+ }
7661
+ }
7662
+ const target = cells[index];
7663
+ if (!target) return false;
7664
+ const next = document.createRange();
7665
+ next.selectNodeContents(target);
7666
+ next.collapse(true);
7667
+ const selection = document.getSelection();
7668
+ selection?.removeAllRanges();
7669
+ selection?.addRange(next);
7670
+ return true;
7671
+ };
7672
+ handleBeforeInput_fn = function(event) {
7673
+ if (this.disabled || this.readonly || !this.maxlength) return;
7674
+ const type = event.inputType;
7675
+ if (type === "insertFromPaste" || type === "insertFromDrop" || type.startsWith("history") || type.startsWith("format")) {
7676
+ return;
7677
+ }
7678
+ const extra = event.data?.length ?? (type === "insertParagraph" || type === "insertLineBreak" ? 1 : 0);
7679
+ if (!extra) return;
7680
+ if (!__privateMethod(this, _FxEditor_instances, fits_fn).call(this, extra)) {
7681
+ event.preventDefault();
7682
+ __privateMethod(this, _FxEditor_instances, limitError_fn).call(this);
7683
+ }
7684
+ };
7685
+ handlePaste_fn = function(event) {
7686
+ const clipboard = event.clipboardData;
7687
+ if (!clipboard) return;
7688
+ const file = Array.from(clipboard.files ?? []).find((item) => item.type.startsWith("image/"));
7689
+ if (file) {
7690
+ event.preventDefault();
7691
+ void __privateMethod(this, _FxEditor_instances, handleImageFile_fn).call(this, file);
7692
+ return;
7693
+ }
7694
+ const html = clipboard.getData("text/html");
7695
+ const text = clipboard.getData("text/plain");
7696
+ if (html.length > MAX_PASTE_BYTES || text.length > MAX_PASTE_BYTES) return;
7697
+ event.preventDefault();
7698
+ if (!__privateMethod(this, _FxEditor_instances, fits_fn).call(this, text.length)) {
7699
+ __privateMethod(this, _FxEditor_instances, limitError_fn).call(this);
7700
+ return;
7701
+ }
7702
+ if (this.pasteMode === "plain" || !html) {
7703
+ __privateMethod(this, _FxEditor_instances, insertPlainText_fn).call(this, text);
7704
+ return;
7705
+ }
7706
+ const clean = __privateMethod(this, _FxEditor_instances, cleanHtml_fn).call(this, html);
7707
+ if (!clean) {
7708
+ __privateMethod(this, _FxEditor_instances, insertPlainText_fn).call(this, text);
7709
+ return;
7710
+ }
7711
+ __privateMethod(this, _FxEditor_instances, restoreSelection_fn).call(this);
7712
+ insertHtml(this.editor, clean);
7713
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
7714
+ };
7715
+ handleDrop_fn = function(event) {
7716
+ const transfer = event.dataTransfer;
7717
+ if (!transfer) return;
7718
+ event.preventDefault();
7719
+ const file = Array.from(transfer.files ?? []).find((item) => item.type.startsWith("image/"));
7720
+ if (file) {
7721
+ void __privateMethod(this, _FxEditor_instances, handleImageFile_fn).call(this, file);
7722
+ return;
7723
+ }
7724
+ const html = transfer.getData("text/html");
7725
+ const text = transfer.getData("text/plain");
7726
+ if (!__privateMethod(this, _FxEditor_instances, fits_fn).call(this, text.length)) {
7727
+ __privateMethod(this, _FxEditor_instances, limitError_fn).call(this);
7728
+ return;
7729
+ }
7730
+ if (html) {
7731
+ insertHtml(this.editor, __privateMethod(this, _FxEditor_instances, cleanHtml_fn).call(this, html));
7732
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
7733
+ } else if (text) {
7734
+ __privateMethod(this, _FxEditor_instances, insertPlainText_fn).call(this, text);
7735
+ }
7736
+ };
7737
+ insertPlainText_fn = function(text) {
7738
+ const paragraphs = text.replace(/\r\n/g, "\n").split(/\n{2,}/).map((block) => `<p>${escapeText(block).replace(/\n/g, "<br>")}</p>`).join("");
7739
+ insertHtml(this.editor, paragraphs || "<p><br></p>");
7740
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
7741
+ };
7742
+ toggleSource_fn = function() {
7743
+ if (__privateGet(this, _sourceMode)) {
7744
+ __privateMethod(this, _FxEditor_instances, applySource_fn).call(this);
7745
+ return;
7746
+ }
7747
+ const source = this.source;
7748
+ if (!source) return;
7749
+ source.value = __privateMethod(this, _FxEditor_instances, serialize_fn).call(this);
7750
+ __privateSet(this, _sourceMode, true);
7751
+ __privateMethod(this, _FxEditor_instances, syncChrome_fn).call(this);
7752
+ source.focus();
7753
+ };
7754
+ applySource_fn = function() {
7755
+ const source = this.source;
7756
+ if (!source) return;
7757
+ const clean = __privateMethod(this, _FxEditor_instances, cleanHtml_fn).call(this, source.value);
7758
+ __privateSet(this, _sourceMode, false);
7759
+ __privateSet(this, _value, clean);
7760
+ __privateSet(this, _dirty, false);
7761
+ __privateMethod(this, _FxEditor_instances, applyValue_fn).call(this, clean);
7762
+ __privateMethod(this, _FxEditor_instances, syncChrome_fn).call(this);
7763
+ this.updateFormValue();
7764
+ __privateMethod(this, _FxEditor_instances, emitChange_fn).call(this);
7765
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
7766
+ };
7767
+ cancelSource_fn = function() {
7768
+ __privateSet(this, _sourceMode, false);
7769
+ __privateMethod(this, _FxEditor_instances, applyValue_fn).call(this, __privateGet(this, _value));
7770
+ __privateMethod(this, _FxEditor_instances, syncChrome_fn).call(this);
7771
+ };
7772
+ openDialog_fn = function(title, fields) {
7773
+ const backdrop = this.root.querySelector(".backdrop");
7774
+ const titleEl = this.root.querySelector(".dialog h3");
7775
+ const body = this.root.querySelector(".dialog-body");
7776
+ if (!backdrop || !titleEl || !body) return Promise.resolve(null);
7777
+ titleEl.textContent = title;
7778
+ body.innerHTML = fields.map((field) => __privateMethod(this, _FxEditor_instances, fieldHtml_fn).call(this, field)).join("");
7779
+ __privateSet(this, _dialogReturnFocus, this.root.activeElement ?? this.editor);
7780
+ backdrop.removeAttribute("hidden");
7781
+ const first = body.querySelector("input,select");
7782
+ first?.focus();
7783
+ return new Promise((resolve) => {
7784
+ __privateSet(this, _dialogResolve, resolve);
7785
+ });
7786
+ };
7787
+ fieldHtml_fn = function(field) {
7788
+ const label = esc(field.label);
7789
+ if (field.type === "select") {
7790
+ const options = (field.options ?? []).map((option) => `<option value="${esc(option.value)}" ${option.value === field.value ? "selected" : ""}>${esc(option.label)}</option>`).join("");
7791
+ return `<label class="field"><span>${label}</span><select data-field="${esc(field.name)}">${options}</select></label>`;
7792
+ }
7793
+ const type = field.type === "url" ? "url" : field.type === "number" ? "number" : "text";
7794
+ return `<label class="field"><span>${label}</span><input data-field="${esc(field.name)}" type="${type}" value="${esc(field.value ?? "")}" placeholder="${esc(field.placeholder ?? "")}"></label>`;
7795
+ };
7796
+ submitDialog_fn = function() {
7797
+ const body = this.root.querySelector(".dialog-body");
7798
+ if (!body) return;
7799
+ const result = {};
7800
+ for (const field of Array.from(body.querySelectorAll("[data-field]"))) {
7801
+ result[field.dataset.field ?? ""] = field.value.trim();
7802
+ }
7803
+ __privateMethod(this, _FxEditor_instances, releaseDialog_fn).call(this, result);
7804
+ };
7805
+ openLinkDialog_fn = async function(existing) {
7806
+ __privateMethod(this, _FxEditor_instances, hideLinkOverlay_fn).call(this);
7807
+ const editor = this.editor;
7808
+ if (!editor) return;
7809
+ if (existing) __privateMethod(this, _FxEditor_instances, selectAnchor_fn).call(this, existing);
7810
+ let range = getRange(editor, __privateGet(this, _lastRange));
7811
+ if (!range) {
7812
+ placeCaretAtEnd(editor);
7813
+ range = getRange(editor);
7814
+ __privateSet(this, _lastRange, range?.cloneRange() ?? null);
7815
+ }
7816
+ const anchor = existing ?? closestInEditor(editor, range?.startContainer ?? null, "a");
7817
+ const selected = range && !range.collapsed ? range.toString() : anchor?.textContent ?? "";
7818
+ const result = await __privateMethod(this, _FxEditor_instances, openDialog_fn).call(this, "Link", [
7819
+ { name: "url", label: "URL", type: "url", value: anchor?.getAttribute("href") ?? "", placeholder: "https://..." },
7820
+ { name: "text", label: "Texto (quando não há seleção)", value: selected, placeholder: "Texto do link" },
7821
+ {
7822
+ name: "target",
7823
+ label: "Abrir em",
7824
+ type: "select",
7825
+ value: anchor?.getAttribute("target") ?? "_blank",
7826
+ options: [
7827
+ { value: "_blank", label: "Nova aba" },
7828
+ { value: "_self", label: "Mesma aba" }
7829
+ ]
7830
+ }
7831
+ ]);
7832
+ if (!result) return;
7833
+ const href = sanitizeLinkUrl(result.url ?? "");
7834
+ if (!href) {
7835
+ __privateMethod(this, _FxEditor_instances, error_fn).call(this, "URL de link inválida.");
7836
+ return;
7837
+ }
7838
+ const target = result.target === "_self" ? "_self" : "_blank";
7839
+ const label = (result.text ?? "").trim() || (anchor ? void 0 : selected || href);
7840
+ if (anchor) {
7841
+ anchor.setAttribute("href", href);
7842
+ anchor.setAttribute("target", target);
7843
+ anchor.setAttribute("rel", "noopener noreferrer");
7844
+ if (label) anchor.textContent = label;
7845
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
7846
+ __privateMethod(this, _FxEditor_instances, restoreSelection_fn).call(this);
7847
+ return;
7848
+ }
7849
+ __privateMethod(this, _FxEditor_instances, restoreSelection_fn).call(this);
7850
+ applyLink(editor, href, label ?? (selected || href), target);
7851
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
7852
+ };
7853
+ openImageDialog_fn = async function(existing) {
7854
+ __privateMethod(this, _FxEditor_instances, hideMediaOverlay_fn).call(this);
7855
+ const figure = existing?.closest("figure");
7856
+ const caption = figure?.querySelector("figcaption")?.textContent ?? "";
7857
+ const alignValue = figure?.getAttribute("data-align") ?? "center";
7858
+ const widthValue = figure?.style.width?.replace("%", "") || "100";
7859
+ const dialog = __privateMethod(this, _FxEditor_instances, openDialog_fn).call(this, existing ? "Editar imagem" : "Inserir imagem", [
7860
+ { name: "src", label: "URL da imagem", type: "url", value: existing?.getAttribute("src") ?? "", placeholder: "https://..." },
7861
+ { name: "alt", label: "Texto alternativo", value: existing?.getAttribute("alt") ?? "" },
7862
+ { name: "caption", label: "Legenda", value: caption },
7863
+ {
7864
+ name: "width",
7865
+ label: "Largura",
7866
+ type: "select",
7867
+ value: widthValue,
7868
+ options: [
7869
+ { value: "25", label: "25%" },
7870
+ { value: "50", label: "50%" },
7871
+ { value: "75", label: "75%" },
7872
+ { value: "100", label: "100%" }
7873
+ ]
7874
+ },
7875
+ {
7876
+ name: "align",
7877
+ label: "Alinhamento",
7878
+ type: "select",
7879
+ value: alignValue,
7880
+ options: [
7881
+ { value: "left", label: "Esquerda" },
7882
+ { value: "center", label: "Centro" },
7883
+ { value: "right", label: "Direita" }
7884
+ ]
7885
+ }
7886
+ ]);
7887
+ const body = this.root.querySelector(".dialog-body");
7888
+ if (body) {
7889
+ const upload = document.createElement("button");
7890
+ upload.type = "button";
7891
+ upload.className = "btn";
7892
+ upload.textContent = "Escolher arquivo…";
7893
+ upload.addEventListener("click", () => {
7894
+ __privateMethod(this, _FxEditor_instances, releaseDialog_fn).call(this, null);
7895
+ __privateGet(this, _imagePicker)?.click();
7896
+ });
7897
+ body.appendChild(upload);
7898
+ }
7899
+ const result = await dialog;
7900
+ if (!result) return;
7901
+ const src = sanitizeMediaUrl(result.src ?? "", "img");
7902
+ if (!src) {
7903
+ __privateMethod(this, _FxEditor_instances, error_fn).call(this, "URL de imagem inválida.");
7904
+ return;
7905
+ }
7906
+ if (existing && figure) {
7907
+ existing.setAttribute("src", src);
7908
+ existing.setAttribute("alt", result.alt ?? "");
7909
+ figure.setAttribute("data-align", result.align || "center");
7910
+ figure.style.width = `${result.width || 100}%`;
7911
+ const cap = figure.querySelector("figcaption");
7912
+ if (result.caption) {
7913
+ if (cap) cap.textContent = result.caption;
7914
+ else figure.insertAdjacentHTML("beforeend", `<figcaption>${escapeText(result.caption)}</figcaption>`);
7915
+ } else {
7916
+ cap?.remove();
7917
+ }
7918
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
7919
+ return;
7920
+ }
7921
+ __privateMethod(this, _FxEditor_instances, restoreSelection_fn).call(this);
7922
+ __privateMethod(this, _FxEditor_instances, insertImage_fn).call(this, src, result.alt ?? "", result.caption ?? "", result.width || "100", result.align || "center");
7923
+ };
7924
+ toEmbedUrl_fn = function(value) {
7925
+ try {
7926
+ const url = new URL(value);
7927
+ const host = url.hostname.replace(/^www\./, "");
7928
+ if (host === "youtu.be") {
7929
+ return `https://www.youtube-nocookie.com/embed/${url.pathname.slice(1)}`;
7930
+ }
7931
+ if (host === "youtube.com" || host === "m.youtube.com") {
7932
+ const id = url.searchParams.get("v");
7933
+ if (id) return `https://www.youtube-nocookie.com/embed/${id}`;
7934
+ if (url.pathname.startsWith("/shorts/")) {
7935
+ return `https://www.youtube-nocookie.com/embed/${url.pathname.split("/")[2] ?? ""}`;
7936
+ }
7937
+ }
7938
+ if (host === "vimeo.com") {
7939
+ const id = url.pathname.split("/").filter(Boolean)[0];
7940
+ if (id) return `https://player.vimeo.com/video/${id}`;
7941
+ }
7942
+ return value;
7943
+ } catch {
7944
+ return value;
7945
+ }
7946
+ };
7947
+ openVideoDialog_fn = async function() {
7948
+ const result = await __privateMethod(this, _FxEditor_instances, openDialog_fn).call(this, "Inserir vídeo", [
7949
+ { name: "src", label: "URL do vídeo ou embed", type: "url", placeholder: "https://.../video.mp4 ou YouTube/Vimeo" },
7950
+ { name: "caption", label: "Legenda", value: "" },
7951
+ {
7952
+ name: "width",
7953
+ label: "Largura",
7954
+ type: "select",
7955
+ value: "100",
7956
+ options: [
7957
+ { value: "50", label: "50%" },
7958
+ { value: "75", label: "75%" },
7959
+ { value: "100", label: "100%" }
7960
+ ]
7961
+ },
7962
+ {
7963
+ name: "align",
7964
+ label: "Alinhamento",
7965
+ type: "select",
7966
+ value: "center",
7967
+ options: [
7968
+ { value: "left", label: "Esquerda" },
7969
+ { value: "center", label: "Centro" },
7970
+ { value: "right", label: "Direita" }
7971
+ ]
7972
+ }
7973
+ ]);
7974
+ if (!result) return;
7975
+ const src = result.src ?? "";
7976
+ const embed = sanitizeEmbedUrl(__privateMethod(this, _FxEditor_instances, toEmbedUrl_fn).call(this, src));
7977
+ if (embed) {
7978
+ __privateMethod(this, _FxEditor_instances, restoreSelection_fn).call(this);
7979
+ __privateMethod(this, _FxEditor_instances, insertEmbed_fn).call(this, embed, result.caption ?? "", result.width || "100", result.align || "center");
7980
+ return;
7981
+ }
7982
+ const media = sanitizeMediaUrl(src, "video");
7983
+ if (!media) {
7984
+ __privateMethod(this, _FxEditor_instances, error_fn).call(this, "URL de vídeo inválida.");
7985
+ return;
7986
+ }
7987
+ __privateMethod(this, _FxEditor_instances, restoreSelection_fn).call(this);
7988
+ __privateMethod(this, _FxEditor_instances, insertVideo_fn).call(this, media, result.caption ?? "", result.width || "100", result.align || "center");
7989
+ };
7990
+ decorateMediaFigures_fn = function() {
7991
+ const editor = this.editor;
7992
+ if (!editor || __privateGet(this, _sourceMode)) return;
7993
+ for (const figure of Array.from(
7994
+ editor.querySelectorAll("figure.fx-image, figure.fx-video, figure.fx-embed")
7995
+ )) {
7996
+ if (figure.querySelector(":scope > .fx-media-quick")) {
7997
+ __privateMethod(this, _FxEditor_instances, syncQuickActions_fn).call(this, figure);
7998
+ continue;
7999
+ }
8000
+ for (const kind of ["left", "center", "right", "delete"]) {
8001
+ const label = kind === "delete" ? "Excluir mídia" : kind === "left" ? "Alinhar à esquerda" : kind === "center" ? "Centralizar" : "Alinhar à direita";
8002
+ const button = document.createElement("button");
8003
+ button.type = "button";
8004
+ button.className = `fx-media-quick quick-${kind}`;
8005
+ button.title = label;
8006
+ button.setAttribute("aria-label", label);
8007
+ if (kind === "delete") button.dataset.mediaDelete = "";
8008
+ else button.dataset.mediaAlign = kind;
8009
+ button.innerHTML = QUICK_SVG[kind];
8010
+ button.addEventListener("click", (event) => {
8011
+ event.preventDefault();
8012
+ event.stopPropagation();
8013
+ if (kind === "delete") {
8014
+ figure.remove();
8015
+ __privateMethod(this, _FxEditor_instances, hideMediaOverlay_fn).call(this);
8016
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
8017
+ } else {
8018
+ figure.setAttribute("data-align", kind);
8019
+ __privateMethod(this, _FxEditor_instances, syncQuickActions_fn).call(this, figure);
8020
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
8021
+ }
8022
+ });
8023
+ figure.appendChild(button);
8024
+ }
8025
+ __privateMethod(this, _FxEditor_instances, syncQuickActions_fn).call(this, figure);
8026
+ }
8027
+ };
8028
+ syncQuickActions_fn = function(figure) {
8029
+ const align2 = figure.getAttribute("data-align") || "center";
8030
+ for (const button of Array.from(
8031
+ figure.querySelectorAll(":scope > .fx-media-quick[data-media-align]")
8032
+ )) {
8033
+ button.classList.toggle("active", button.dataset.mediaAlign === align2);
8034
+ }
8035
+ };
8036
+ selectAnchor_fn = function(anchor) {
8037
+ const editor = this.editor;
8038
+ if (!editor) return;
8039
+ editor.focus({ preventScroll: true });
8040
+ const range = document.createRange();
8041
+ range.selectNodeContents(anchor);
8042
+ const selection = document.getSelection();
8043
+ if (!selection) return;
8044
+ selection.removeAllRanges();
8045
+ selection.addRange(range);
8046
+ __privateSet(this, _lastRange, range.cloneRange());
8047
+ };
8048
+ handleLinkOverlayClick_fn = function(event) {
8049
+ const action = event.target.closest("[data-link-action]");
8050
+ const anchor = __privateGet(this, _linkAnchor);
8051
+ if (!action || !anchor) return;
8052
+ event.preventDefault();
8053
+ event.stopPropagation();
8054
+ const kind = action.dataset.linkAction;
8055
+ if (kind === "open") __privateMethod(this, _FxEditor_instances, openLink_fn).call(this, anchor);
8056
+ else if (kind === "edit") void __privateMethod(this, _FxEditor_instances, openLinkDialog_fn).call(this, anchor);
8057
+ else if (kind === "remove") __privateMethod(this, _FxEditor_instances, removeLink_fn).call(this, anchor);
8058
+ };
8059
+ openLink_fn = function(anchor) {
8060
+ const href = anchor.getAttribute("href") ?? "";
8061
+ if (!href) return;
8062
+ __privateMethod(this, _FxEditor_instances, hideLinkOverlay_fn).call(this);
8063
+ const target = anchor.getAttribute("target") === "_self" ? "_self" : "_blank";
8064
+ window.open(href, target, target === "_blank" ? "noopener,noreferrer" : void 0);
8065
+ };
8066
+ removeLink_fn = function(anchor) {
8067
+ const parent = anchor.parentNode;
8068
+ if (!parent) return;
8069
+ while (anchor.firstChild) parent.insertBefore(anchor.firstChild, anchor);
8070
+ anchor.remove();
8071
+ __privateMethod(this, _FxEditor_instances, hideLinkOverlay_fn).call(this);
8072
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
8073
+ this.editor?.focus({ preventScroll: true });
8074
+ };
8075
+ showLinkOverlay_fn = function(anchor) {
8076
+ __privateSet(this, _linkAnchor, anchor);
8077
+ const overlay = this.root.querySelector(".link-overlay");
8078
+ if (!overlay) return;
8079
+ const target = anchor.getAttribute("target") === "_self" ? "mesma janela" : "nova aba";
8080
+ const open = overlay.querySelector('[data-link-action="open"]');
8081
+ if (open) open.title = `Abrir na ${target}`;
8082
+ overlay.hidden = false;
8083
+ __privateMethod(this, _FxEditor_instances, positionLinkOverlay_fn).call(this);
8084
+ };
8085
+ hideLinkOverlay_fn = function() {
8086
+ __privateSet(this, _linkAnchor, null);
8087
+ const overlay = this.root.querySelector(".link-overlay");
8088
+ if (overlay) overlay.hidden = true;
8089
+ };
8090
+ positionLinkOverlay_fn = function() {
8091
+ const overlay = this.root.querySelector(".link-overlay");
8092
+ const anchor = __privateGet(this, _linkAnchor);
8093
+ const editor = this.editor;
8094
+ if (!overlay) return;
8095
+ if (!anchor || !editor || __privateGet(this, _sourceMode) || !anchor.isConnected || !editor.contains(anchor)) {
8096
+ overlay.hidden = true;
8097
+ return;
8098
+ }
8099
+ const body = overlay.parentElement;
8100
+ if (!body) return;
8101
+ const bodyRect = body.getBoundingClientRect();
8102
+ const rect = anchor.getBoundingClientRect();
8103
+ if (!rect.width || !rect.height || rect.bottom < bodyRect.top || rect.top > bodyRect.bottom) {
8104
+ overlay.hidden = true;
8105
+ return;
8106
+ }
8107
+ overlay.hidden = false;
8108
+ const width = overlay.offsetWidth || 180;
8109
+ const height = overlay.offsetHeight || 30;
8110
+ const left = Math.max(4, Math.min(rect.left - bodyRect.left, bodyRect.width - width - 4));
8111
+ let top = rect.bottom - bodyRect.top + 6;
8112
+ if (top + height > bodyRect.height) top = rect.top - bodyRect.top - height - 6;
8113
+ overlay.style.left = `${left}px`;
8114
+ overlay.style.top = `${Math.max(4, top)}px`;
8115
+ };
8116
+ handleEditorClick_fn = function(event) {
8117
+ const target = event.target;
8118
+ const anchor = target.closest("a[href]");
8119
+ if (anchor && this.editor?.contains(anchor)) {
8120
+ __privateMethod(this, _FxEditor_instances, hideMediaOverlay_fn).call(this);
8121
+ __privateMethod(this, _FxEditor_instances, showLinkOverlay_fn).call(this, anchor);
8122
+ if (event.ctrlKey || event.metaKey) {
8123
+ event.preventDefault();
8124
+ __privateMethod(this, _FxEditor_instances, openLink_fn).call(this, anchor);
8125
+ }
8126
+ return;
8127
+ }
8128
+ __privateMethod(this, _FxEditor_instances, hideLinkOverlay_fn).call(this);
8129
+ const figure = target.closest("figure.fx-image, figure.fx-video, figure.fx-embed");
8130
+ if (figure) __privateMethod(this, _FxEditor_instances, showMediaOverlay_fn).call(this, figure);
8131
+ else __privateMethod(this, _FxEditor_instances, hideMediaOverlay_fn).call(this);
8132
+ };
8133
+ showMediaOverlay_fn = function(figure) {
8134
+ __privateSet(this, _mediaFigure, figure);
8135
+ figure.setAttribute("tabindex", "0");
8136
+ if (figure.classList.contains("fx-image")) figure.focus({ preventScroll: true });
8137
+ const overlay = this.root.querySelector(".media-overlay");
8138
+ if (!overlay) return;
8139
+ overlay.hidden = false;
8140
+ __privateMethod(this, _FxEditor_instances, positionMediaOverlay_fn).call(this);
8141
+ };
8142
+ hideMediaOverlay_fn = function() {
8143
+ __privateGet(this, _mediaFigure)?.removeAttribute("tabindex");
8144
+ __privateSet(this, _mediaFigure, null);
8145
+ const overlay = this.root.querySelector(".media-overlay");
8146
+ if (!overlay) return;
8147
+ overlay.hidden = true;
8148
+ const size = overlay.querySelector(".media-size");
8149
+ if (size) size.textContent = "";
8150
+ };
8151
+ deleteMediaFigure_fn = function() {
8152
+ const figure = __privateGet(this, _mediaFigure);
8153
+ if (!figure) return;
8154
+ figure.remove();
8155
+ __privateMethod(this, _FxEditor_instances, hideMediaOverlay_fn).call(this);
8156
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
8157
+ this.editor?.focus({ preventScroll: true });
8158
+ };
8159
+ positionMediaOverlay_fn = function() {
8160
+ const overlay = this.root.querySelector(".media-overlay");
8161
+ const figure = __privateGet(this, _mediaFigure);
8162
+ const editor = this.editor;
8163
+ if (!overlay) return;
8164
+ if (!figure || !editor || __privateGet(this, _sourceMode) || !figure.isConnected || !editor.contains(figure)) {
8165
+ overlay.hidden = true;
8166
+ return;
8167
+ }
8168
+ const body = overlay.parentElement;
8169
+ if (!body) return;
8170
+ const bodyRect = body.getBoundingClientRect();
8171
+ const rect = figure.getBoundingClientRect();
8172
+ if (!rect.width || !rect.height || rect.bottom < bodyRect.top || rect.top > bodyRect.bottom) {
8173
+ overlay.hidden = true;
8174
+ return;
8175
+ }
8176
+ overlay.hidden = false;
8177
+ overlay.style.left = `${rect.left - bodyRect.left}px`;
8178
+ overlay.style.top = `${rect.top - bodyRect.top}px`;
8179
+ overlay.style.width = `${rect.width}px`;
8180
+ overlay.style.height = `${rect.height}px`;
8181
+ const size = overlay.querySelector(".media-size");
8182
+ if (size) size.textContent = `${Math.round(rect.width)} × ${Math.round(rect.height)}`;
8183
+ };
8184
+ startResize_fn = function(event) {
8185
+ const handle = event.target.closest("[data-handle]");
8186
+ const figure = __privateGet(this, _mediaFigure);
8187
+ const editor = this.editor;
8188
+ if (!handle || !figure || !editor) return;
8189
+ event.preventDefault();
8190
+ event.stopPropagation();
8191
+ __privateMethod(this, _FxEditor_instances, endResize_fn).call(this);
8192
+ const parent = figure.parentElement;
8193
+ if (!parent) return;
8194
+ const parentWidth = parent.getBoundingClientRect().width || editor.clientWidth;
8195
+ if (!parentWidth) return;
8196
+ const rect = figure.getBoundingClientRect();
8197
+ const startWidth = rect.width ? rect.width / parentWidth * 100 : Number.parseFloat(figure.style.width) || 100;
8198
+ const startX = event.clientX;
8199
+ const direction = handle.dataset.handle?.includes("w") ? -1 : 1;
8200
+ const move = (moveEvent) => {
8201
+ const deltaPct = (moveEvent.clientX - startX) * direction * 100 / parentWidth;
8202
+ const width = Math.min(100, Math.max(15, startWidth + deltaPct));
8203
+ figure.style.width = `${width.toFixed(1).replace(/\.0$/, "")}%`;
8204
+ __privateMethod(this, _FxEditor_instances, positionMediaOverlay_fn).call(this);
8205
+ };
8206
+ const up = () => {
8207
+ __privateMethod(this, _FxEditor_instances, endResize_fn).call(this);
8208
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
8209
+ __privateMethod(this, _FxEditor_instances, positionMediaOverlay_fn).call(this);
8210
+ };
8211
+ window.addEventListener("pointermove", move);
8212
+ window.addEventListener("pointerup", up, { once: true });
8213
+ window.addEventListener("pointercancel", up, { once: true });
8214
+ __privateSet(this, _resizeDispose, () => {
8215
+ window.removeEventListener("pointermove", move);
8216
+ window.removeEventListener("pointerup", up);
8217
+ window.removeEventListener("pointercancel", up);
8218
+ });
8219
+ };
8220
+ endResize_fn = function() {
8221
+ var _a;
8222
+ (_a = __privateGet(this, _resizeDispose)) == null ? void 0 : _a.call(this);
8223
+ __privateSet(this, _resizeDispose, null);
8224
+ };
8225
+ handleDoubleClick_fn = function(event) {
8226
+ const target = event.target;
8227
+ const image = target.closest("img");
8228
+ if (image) {
8229
+ event.preventDefault();
8230
+ void __privateMethod(this, _FxEditor_instances, openImageDialog_fn).call(this, image);
8231
+ }
8232
+ };
8233
+ insertImage_fn = function(src, alt, caption, width, align2) {
8234
+ const html = `<figure class="fx-image" contenteditable="false" data-align="${escapeAttr(align2)}" style="width:${escapeAttr(width)}%"><img src="${escapeAttr(src)}" alt="${escapeAttr(alt)}" loading="lazy" decoding="async">${caption ? `<figcaption>${escapeText(caption)}</figcaption>` : ""}</figure><p><br></p>`;
8235
+ insertHtml(this.editor, html);
8236
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
8237
+ };
8238
+ insertVideo_fn = function(src, caption, width, align2 = "center") {
8239
+ const html = `<figure class="fx-video" contenteditable="false" data-align="${escapeAttr(align2)}" style="width:${escapeAttr(width)}%"><video controls preload="metadata" src="${escapeAttr(src)}" style="width:100%"></video>${caption ? `<figcaption>${escapeText(caption)}</figcaption>` : ""}</figure><p><br></p>`;
8240
+ insertHtml(this.editor, html);
8241
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
8242
+ };
8243
+ insertEmbed_fn = function(src, caption, width, align2 = "center") {
8244
+ const html = `<figure class="fx-embed" contenteditable="false" data-align="${escapeAttr(align2)}" style="width:${escapeAttr(width)}%"><iframe src="${escapeAttr(src)}" title="${escapeAttr(caption || "Vídeo")}" loading="lazy" allowfullscreen sandbox="allow-scripts allow-same-origin allow-presentation" referrerpolicy="strict-origin-when-cross-origin" style="width:100%;aspect-ratio:16/9;border:0"></iframe>${caption ? `<figcaption>${escapeText(caption)}</figcaption>` : ""}</figure><p><br></p>`;
8245
+ insertHtml(this.editor, html);
8246
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
8247
+ };
8248
+ insertTable_fn = function(rows, cols) {
8249
+ const rowCount = Math.min(20, Math.max(1, rows));
8250
+ const colCount = Math.min(12, Math.max(1, cols));
8251
+ const width = (100 / colCount).toFixed(4).replace(/\.?0+$/, "");
8252
+ const colsHtml = `<colgroup>${Array.from({ length: colCount }, () => `<col style="width:${width}%">`).join("")}</colgroup>`;
8253
+ const head = `<thead><tr>${Array.from({ length: colCount }, () => '<th scope="col"><p><br></p></th>').join("")}</tr></thead>`;
8254
+ const bodyRows = Math.max(0, rowCount - 1);
8255
+ const body = bodyRows ? `<tbody>${Array.from(
8256
+ { length: bodyRows },
8257
+ () => `<tr>${Array.from({ length: colCount }, () => "<td><p><br></p></td>").join("")}</tr>`
8258
+ ).join("")}</tbody>` : "";
8259
+ const html = `<figure class="fx-table"><table>${colsHtml}${head}${body}</table></figure><p><br></p>`;
8260
+ insertHtml(this.editor, html);
8261
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
8262
+ };
8263
+ insertColumns_fn = function(cols) {
8264
+ const count = Math.min(4, Math.max(1, cols));
8265
+ const width = (100 / count).toFixed(4).replace(/\.?0+$/, "");
8266
+ const cells = Array.from(
8267
+ { length: count },
8268
+ () => `<td style="width:${width}%;vertical-align:top;padding:8px"><p><br></p></td>`
8269
+ ).join("");
8270
+ const html = `<table class="fx-grid" role="presentation" data-cols="${count}" data-stack="sm" style="width:100%;border-collapse:collapse"><tbody><tr>${cells}</tr></tbody></table><p><br></p>`;
8271
+ insertHtml(this.editor, html);
8272
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
8273
+ };
8274
+ showTablePanel_fn = function() {
8275
+ const panel = this.root.querySelector("[data-panel]");
8276
+ if (!panel) return;
8277
+ const table = __privateMethod(this, _FxEditor_instances, currentTable_fn).call(this);
8278
+ const grid = Array.from({ length: 64 }, (_, index) => {
8279
+ const rows = Math.floor(index / 8) + 1;
8280
+ const cols = index % 8 + 1;
8281
+ return `<button type="button" data-grid="${rows},${cols}" aria-label="${rows} por ${cols}"></button>`;
8282
+ }).join("");
8283
+ panel.innerHTML = `<p class="panel-title">Inserir tabela</p><div class="grid-picker">${grid}</div>` + (table ? `<p class="panel-title" style="margin-top:8px">Tabela atual</p><div class="panel-actions"><button type="button" class="btn" data-table-action="add-row">+ Linha</button><button type="button" class="btn" data-table-action="add-col">+ Coluna</button><button type="button" class="btn" data-table-action="del-row">− Linha</button><button type="button" class="btn" data-table-action="del-col">− Coluna</button><button type="button" class="btn" data-table-action="toggle-header">Cabeçalho</button><button type="button" class="btn" data-table-action="merge-cells">Mesclar</button><button type="button" class="btn" data-table-action="split-cell">Dividir</button><button type="button" class="btn" data-table-action="del-table">Excluir tabela</button></div>` : "");
8284
+ panel.removeAttribute("hidden");
8285
+ panel.querySelectorAll("[data-grid]").forEach((button) => {
8286
+ button.addEventListener("click", () => {
8287
+ const [rows, cols] = (button.dataset.grid ?? "").split(",").map(Number);
8288
+ __privateMethod(this, _FxEditor_instances, hidePanel_fn).call(this);
8289
+ __privateMethod(this, _FxEditor_instances, insertTable_fn).call(this, rows, cols);
8290
+ });
8291
+ });
8292
+ panel.querySelectorAll("[data-table-action]").forEach((button) => {
8293
+ button.addEventListener("click", () => {
8294
+ __privateMethod(this, _FxEditor_instances, tableAction_fn).call(this, button.dataset.tableAction ?? "");
8295
+ __privateMethod(this, _FxEditor_instances, hidePanel_fn).call(this);
8296
+ });
8297
+ });
8298
+ };
8299
+ showColumnsPanel_fn = function() {
8300
+ const panel = this.root.querySelector("[data-panel]");
8301
+ if (!panel) return;
8302
+ panel.innerHTML = `<p class="panel-title">Inserir colunas</p><div class="panel-actions">` + [1, 2, 3, 4].map((count) => `<button type="button" class="btn" data-columns="${count}">${count}</button>`).join("") + `</div>`;
8303
+ panel.removeAttribute("hidden");
8304
+ panel.querySelectorAll("[data-columns]").forEach((button) => {
8305
+ button.addEventListener("click", () => {
8306
+ __privateMethod(this, _FxEditor_instances, hidePanel_fn).call(this);
8307
+ __privateMethod(this, _FxEditor_instances, insertColumns_fn).call(this, Number(button.dataset.columns ?? 1));
8308
+ });
8309
+ });
8310
+ };
8311
+ hidePanel_fn = function() {
8312
+ this.root.querySelector("[data-panel]")?.setAttribute("hidden", "");
8313
+ };
8314
+ currentTable_fn = function() {
8315
+ const editor = this.editor;
8316
+ if (!editor) return null;
8317
+ const range = getRange(editor, __privateGet(this, _lastRange));
8318
+ return closestInEditor(editor, range?.startContainer ?? null, "table");
8319
+ };
8320
+ tableAction_fn = function(action) {
8321
+ const table = __privateMethod(this, _FxEditor_instances, currentTable_fn).call(this);
8322
+ if (!table) return;
8323
+ const editor = this.editor;
8324
+ const range = editor ? getRange(editor, __privateGet(this, _lastRange)) : null;
8325
+ const cell = editor ? closestInEditor(editor, range?.startContainer ?? null, "td,th") : null;
8326
+ const row = cell?.parentElement;
8327
+ if (action === "merge-cells") {
8328
+ if (!range) return;
8329
+ const merged = mergeSelectedCells(table, range);
8330
+ if (!merged) return;
8331
+ placeCaretAtEnd(merged);
8332
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
8333
+ return;
8334
+ }
8335
+ if (action === "split-cell") {
8336
+ if (!cell) return;
8337
+ const created = splitCell(table, cell);
8338
+ if (!created.length) return;
8339
+ placeCaretAtEnd(cell);
8340
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
8341
+ return;
8342
+ }
8343
+ if (action === "add-row" && row) {
8344
+ const clone = row.cloneNode(true);
8345
+ for (const cloneCell of Array.from(clone.querySelectorAll("td,th"))) {
8346
+ cloneCell.innerHTML = "<p><br></p>";
8347
+ }
8348
+ row.after(clone);
8349
+ } else if (action === "add-col" && cell) {
8350
+ const index = Array.from(row?.children ?? []).indexOf(cell);
8351
+ for (const currentRow of Array.from(table.rows)) {
8352
+ const target = currentRow.cells[Math.max(0, index)];
8353
+ const clone = (target ?? currentRow.cells[currentRow.cells.length - 1])?.cloneNode(false);
8354
+ if (!clone) continue;
8355
+ clone.innerHTML = "<p><br></p>";
8356
+ currentRow.insertBefore(clone, target?.nextSibling ?? null);
8357
+ }
8358
+ } else if (action === "del-row" && row && table.rows.length > 1) {
8359
+ row.remove();
8360
+ } else if (action === "del-col" && cell && table.rows[0]?.cells.length > 1) {
8361
+ const index = Array.from(row?.children ?? []).indexOf(cell);
8362
+ for (const currentRow of Array.from(table.rows)) {
8363
+ currentRow.cells[index]?.remove();
8364
+ }
8365
+ } else if (action === "toggle-header") {
8366
+ const thead = table.tHead;
8367
+ if (thead && thead.rows.length) {
8368
+ const headerRow = thead.rows[0];
8369
+ for (const headerCell of Array.from(headerRow.cells)) {
8370
+ const td = document.createElement("td");
8371
+ td.innerHTML = headerCell.innerHTML;
8372
+ headerCell.replaceWith(td);
8373
+ }
8374
+ const tbody = table.tBodies[0] ?? table.createTBody();
8375
+ tbody.insertBefore(headerRow, tbody.firstChild);
8376
+ thead.remove();
8377
+ } else {
8378
+ const firstRow = table.rows[0];
8379
+ if (firstRow) {
8380
+ for (const firstCell of Array.from(firstRow.cells)) {
8381
+ const th = document.createElement("th");
8382
+ th.setAttribute("scope", "col");
8383
+ th.innerHTML = firstCell.innerHTML;
8384
+ firstCell.replaceWith(th);
8385
+ }
8386
+ table.createTHead().appendChild(firstRow);
8387
+ }
8388
+ }
8389
+ } else if (action === "del-table") {
8390
+ const figure = table.closest("figure.fx-table");
8391
+ (figure ?? table).remove();
8392
+ }
8393
+ __privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
8394
+ };
8395
+ handleImageFile_fn = async function(file) {
8396
+ if (!file.type.startsWith("image/")) {
8397
+ __privateMethod(this, _FxEditor_instances, error_fn).call(this, "O arquivo selecionado não é uma imagem.");
8398
+ return;
8399
+ }
8400
+ if (file.type === "image/svg+xml") {
8401
+ __privateMethod(this, _FxEditor_instances, error_fn).call(this, "SVG não é permitido por segurança. Use PNG, JPEG, WebP ou GIF.");
8402
+ return;
8403
+ }
8404
+ if (file.size > MAX_IMAGE_BYTES) {
8405
+ __privateMethod(this, _FxEditor_instances, error_fn).call(this, "Imagem muito grande (máximo 8 MB).");
8406
+ return;
8407
+ }
8408
+ let src = "";
8409
+ let alt = file.name.replace(/\.[a-z0-9]+$/i, "");
8410
+ try {
8411
+ if (this.uploadImage) {
8412
+ const uploaded = await this.uploadImage(file);
8413
+ if (typeof uploaded === "string") src = uploaded;
8414
+ else {
8415
+ src = uploaded.url;
8416
+ if (uploaded.alt) alt = uploaded.alt;
8417
+ }
8418
+ } else {
8419
+ src = await __privateMethod(this, _FxEditor_instances, fileToDataUrl_fn).call(this, file);
8420
+ }
8421
+ } catch {
8422
+ __privateMethod(this, _FxEditor_instances, error_fn).call(this, "Não foi possível processar a imagem.");
8423
+ return;
8424
+ }
8425
+ if (!src || !sanitizeMediaUrl(src, "img")) {
8426
+ __privateMethod(this, _FxEditor_instances, error_fn).call(this, "A imagem resultante não tem uma URL segura.");
8427
+ return;
8428
+ }
8429
+ __privateMethod(this, _FxEditor_instances, restoreSelection_fn).call(this);
8430
+ __privateMethod(this, _FxEditor_instances, insertImage_fn).call(this, src, alt, "", "100", "center");
8431
+ };
8432
+ fileToDataUrl_fn = async function(file) {
8433
+ const dataUrl = await new Promise((resolve, reject) => {
8434
+ const reader = new FileReader();
8435
+ reader.onload = () => resolve(String(reader.result ?? ""));
8436
+ reader.onerror = () => reject(new Error("read-error"));
8437
+ reader.readAsDataURL(file);
8438
+ });
8439
+ if (file.type === "image/gif") return dataUrl;
8440
+ return new Promise((resolve) => {
8441
+ const image = new Image();
8442
+ image.onload = () => {
8443
+ const maxWidth = 1600;
8444
+ const scale = Math.min(1, maxWidth / (image.naturalWidth || maxWidth));
8445
+ if (scale >= 1) {
8446
+ resolve(dataUrl);
8447
+ return;
8448
+ }
8449
+ const canvas = document.createElement("canvas");
8450
+ canvas.width = Math.round((image.naturalWidth || maxWidth) * scale);
8451
+ canvas.height = Math.round((image.naturalHeight || maxWidth) * scale);
8452
+ const context = canvas.getContext("2d");
8453
+ if (!context) {
8454
+ resolve(dataUrl);
8455
+ return;
8456
+ }
8457
+ context.drawImage(image, 0, 0, canvas.width, canvas.height);
8458
+ resolve(canvas.toDataURL("image/webp", 0.85));
8459
+ };
8460
+ image.onerror = () => resolve(dataUrl);
8461
+ image.src = dataUrl;
8462
+ });
8463
+ };
8464
+ error_fn = function(message) {
8465
+ this.dispatchEvent(
8466
+ new CustomEvent("editor-error", {
8467
+ bubbles: true,
8468
+ composed: true,
8469
+ detail: { message }
8470
+ })
8471
+ );
8472
+ };
8473
+ _FxEditor.styles = `${FENIX_ICON_BASE_CSS}
8474
+ ${EDITOR_STYLES}`;
8475
+ let FxEditor = _FxEditor;
8476
+ function defineFxEditor() {
8477
+ return defineElement("fx-editor", FxEditor);
8478
+ }
8479
+ defineFxEditor();
8480
+ const _FxDialog = class _FxDialog extends FxElement {
8481
+ constructor() {
8482
+ super(...arguments);
8483
+ this._previouslyFocused = null;
8484
+ }
8485
+ static get observedAttributes() {
8486
+ return ["open", "size", "heading"];
8487
+ }
8488
+ get open() {
8489
+ return this.hasAttr("open");
8490
+ }
8491
+ set open(value) {
8492
+ this.toggleAttr("open", value);
8493
+ }
8494
+ render() {
8495
+ const isOpen = this.open;
8496
+ const heading = this.getAttr("heading");
8497
+ this.setTemplate(`
8498
+ <div class="overlay" part="overlay" ${isOpen ? "" : "hidden"}>
8499
+ <div class="dialog" role="dialog" aria-modal="true" aria-label="${esc(heading)}" tabindex="-1">
8500
+ <header>
8501
+ <h2>${esc(heading)}</h2>
8502
+ <button type="button" class="close" part="close" aria-label="Fechar">×</button>
8503
+ </header>
8504
+ <div class="body"><slot></slot></div>
8505
+ <footer part="footer"><slot name="footer"></slot></footer>
8506
+ </div>
8507
+ </div>
8508
+ `);
8509
+ if (!isOpen) return;
8510
+ this._cleanup?.();
8511
+ this._cleanup = void 0;
8512
+ const overlay = this.root.querySelector(".overlay");
8513
+ const closeBtn = this.root.querySelector(".close");
8514
+ const close = () => {
8515
+ this.open = false;
8516
+ this._cleanup?.();
8517
+ this._cleanup = void 0;
8518
+ this._restoreFocus();
8519
+ this.dispatchEvent(new CustomEvent("close", { bubbles: true, composed: true }));
8520
+ };
8521
+ overlay?.addEventListener("click", (e) => {
8522
+ if (e.target === overlay) close();
8523
+ });
8524
+ closeBtn?.addEventListener("click", close);
8525
+ const onKey = (e) => {
8526
+ if (e.key === "Escape") {
8527
+ e.stopPropagation();
8528
+ close();
8529
+ return;
8530
+ }
8531
+ if (e.key === "Tab") this._trapFocus(e);
8532
+ };
8533
+ document.addEventListener("keydown", onKey);
8534
+ this._cleanup = () => document.removeEventListener("keydown", onKey);
8535
+ requestAnimationFrame(() => {
8536
+ this._previouslyFocused = document.activeElement;
8537
+ this.root.querySelector(".dialog")?.focus();
8538
+ });
8539
+ this.dispatchEvent(new CustomEvent("open", { bubbles: true, composed: true }));
8540
+ }
8541
+ /** Mantém o foco dentro do modal (WCAG 2.4.3 / 2.1.2). */
8542
+ _trapFocus(e) {
8543
+ const dialog = this.root.querySelector(".dialog");
8544
+ if (!dialog) return;
8545
+ const focusables = dialog.querySelectorAll(
8546
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
8547
+ );
8548
+ if (!focusables.length) return;
8549
+ const first = focusables[0];
8550
+ const last = focusables[focusables.length - 1];
8551
+ const active = this.root.activeElement;
8552
+ if (e.shiftKey && (active === first || active === dialog)) {
8553
+ e.preventDefault();
8554
+ last.focus();
8555
+ } else if (!e.shiftKey && active === last) {
8556
+ e.preventDefault();
8557
+ first.focus();
8558
+ }
8559
+ }
8560
+ _restoreFocus() {
8561
+ if (this._previouslyFocused?.isConnected) this._previouslyFocused.focus();
8562
+ this._previouslyFocused = null;
8563
+ }
8564
+ disconnectedCallback() {
8565
+ super.disconnectedCallback();
8566
+ this._cleanup?.();
8567
+ this._cleanup = void 0;
8568
+ this._restoreFocus();
8569
+ }
8570
+ };
8571
+ _FxDialog.styles = css`
8572
+ :host { display: contents; }
8573
+ .overlay {
8574
+ position: fixed;
8575
+ inset: 0;
8576
+ background: color-mix(in srgb, var(--fx-text-default, #000) 45%, transparent);
8577
+ backdrop-filter: blur(3px);
8578
+ -webkit-backdrop-filter: blur(3px);
8579
+ display: flex;
8580
+ align-items: center;
8581
+ justify-content: center;
8582
+ z-index: var(--fx-z-modal, 1100);
8583
+ animation: fade var(--fx-motion-duration-fast) var(--fx-motion-easing);
8584
+ }
8585
+ .overlay[hidden] { display: none; }
8586
+ @keyframes fade { from { opacity: 0; } to { opacity: 1; } }
8587
+ .dialog {
8588
+ background: var(--fx-surface-background);
8589
+ border: 1px solid var(--fx-border-default);
8590
+ border-radius: var(--fx-radius-lg);
8591
+ box-shadow: var(--fx-shadow-xl);
8592
+ /* Largura customizável por instância: defina --fx-dialog-width no elemento. */
8593
+ width: min(var(--fx-dialog-width, 560px), calc(100vw - 32px));
8594
+ max-height: calc(100vh - 64px);
8595
+ display: flex;
8596
+ flex-direction: column;
8597
+ font-family: var(--fx-font-family);
8598
+ animation: pop var(--fx-motion-duration-normal) var(--fx-motion-easing);
8599
+ }
8600
+ @keyframes pop { from { opacity: 0; transform: scale(0.96); } to { opacity: 1; transform: scale(1); } }
8601
+ :host([size='sm']) .dialog { width: min(var(--fx-dialog-width, 400px), calc(100vw - 32px)); }
8602
+ :host([size='lg']) .dialog { width: min(var(--fx-dialog-width, 800px), calc(100vw - 32px)); }
8603
+ /* Mobile: dialog ocupa praticamente toda a largura da tela. */
8604
+ @media (max-width: 640px) {
8605
+ .dialog, :host([size='sm']) .dialog, :host([size='lg']) .dialog {
8606
+ width: calc(100vw - 16px);
8607
+ max-width: calc(100vw - 16px);
8608
+ }
8609
+ }
8610
+ header {
8611
+ display: flex;
8612
+ align-items: center;
8613
+ justify-content: space-between;
8614
+ padding: var(--fx-space-lg) var(--fx-space-xl);
8615
+ border-bottom: 1px solid var(--fx-border-default);
8616
+ }
8617
+ h2 {
8618
+ margin: 0;
8619
+ font-size: calc(var(--fx-font-size) + 2px);
8620
+ font-weight: 600;
8621
+ color: var(--fx-text-default);
8622
+ }
8623
+ .close {
8624
+ border: none;
8625
+ background: transparent;
8626
+ color: var(--fx-text-muted);
8627
+ font-size: calc(var(--fx-font-size) + 6px);
8628
+ cursor: pointer;
8629
+ line-height: 1;
8630
+ padding: 0 var(--fx-space-xs);
8631
+ border-radius: var(--fx-radius-full);
8632
+ }
8633
+ .close:hover { color: var(--fx-color-danger); }
8634
+ .body {
8635
+ padding: var(--fx-space-lg) var(--fx-space-xl);
8636
+ overflow-y: auto;
8637
+ color: var(--fx-text-default);
8638
+ font-size: var(--fx-font-size);
8639
+ }
8640
+ footer {
8641
+ padding: var(--fx-space-md) var(--fx-space-xl);
8642
+ border-top: 1px solid var(--fx-border-default);
8643
+ display: flex;
8644
+ justify-content: flex-end;
8645
+ gap: var(--fx-space-sm);
8646
+ }
8647
+ footer[hidden] { display: none; }
8648
+ `;
8649
+ let FxDialog = _FxDialog;
8650
+ function defineFxDialog() {
8651
+ return defineElement("fx-dialog", FxDialog);
8652
+ }
8653
+ defineFxDialog();
8654
+ const scriptRel = function detectScriptRel() {
8655
+ const relList = typeof document !== "undefined" && document.createElement("link").relList;
8656
+ return relList && relList.supports && relList.supports("modulepreload") ? "modulepreload" : "preload";
8657
+ }();
8658
+ const assetsURL = function(dep, importerUrl) {
8659
+ return new URL(dep, importerUrl).href;
8660
+ };
8661
+ const seen = {};
8662
+ const __fenixPreload = function preload(baseModule, deps, importerUrl) {
8663
+ let promise = Promise.resolve();
8664
+ if (deps && deps.length > 0) {
5073
8665
  const links = document.getElementsByTagName("link");
5074
8666
  const cspNonceMeta = document.querySelector(
5075
8667
  "meta[property=csp-nonce]"
@@ -7854,12 +11446,13 @@ const CARD_CSS = `
7854
11446
  border-left: 4px solid var(--kind);
7855
11447
  border-radius: var(--fx-radius-md, 8px);
7856
11448
  box-shadow: var(--fx-shadow-lg, 0 10px 30px rgba(0,0,0,.14));
7857
- padding: var(--fx-space-sm, 8px) var(--fx-space-md, 12px);
11449
+ padding: var(--fx-space-xs, 8px) var(--fx-space-md, 12px);
7858
11450
  font-family: inherit;
7859
11451
  animation: fx-toast-in .25s ease;
7860
11452
  }
7861
11453
  @keyframes fx-toast-in { from { opacity: 0; transform: translateY(-6px); } }
7862
11454
  .card.leaving { opacity: 0; transition: opacity .2s ease; }
11455
+ .card.no-title { min-height: 48px; }
7863
11456
  .icon {
7864
11457
  width: 22px; height: 22px; border-radius: 50%;
7865
11458
  background: var(--kind);
@@ -7966,7 +11559,16 @@ class FxToast extends HTMLElement {
7966
11559
  this.attachShadow({ mode: "open" });
7967
11560
  }
7968
11561
  static get observedAttributes() {
7969
- return ["kind", "title", "message", "duration", "icon", "mode", "progress", "progress-origin"];
11562
+ return [
11563
+ "kind",
11564
+ "title",
11565
+ "message",
11566
+ "duration",
11567
+ "icon",
11568
+ "mode",
11569
+ "progress",
11570
+ "progress-origin"
11571
+ ];
7970
11572
  }
7971
11573
  /**
7972
11574
  * Exibe a barra de contagem regressiva na base do card.
@@ -8043,7 +11645,9 @@ class FxToast extends HTMLElement {
8043
11645
  this.disposed = true;
8044
11646
  if (this.timer) clearTimeout(this.timer);
8045
11647
  this.timer = null;
8046
- this.dispatchEvent(new CustomEvent(REMOVED_EVENT, { bubbles: false, composed: false }));
11648
+ this.dispatchEvent(
11649
+ new CustomEvent(REMOVED_EVENT, { bubbles: false, composed: false })
11650
+ );
8047
11651
  }
8048
11652
  attributeChangedCallback(name) {
8049
11653
  if (name === "duration") {
@@ -8053,7 +11657,8 @@ class FxToast extends HTMLElement {
8053
11657
  }
8054
11658
  const safe = this.safeDuration;
8055
11659
  this.counting = safe > 0;
8056
- if (safe > 0 && this.isConnected && !this.disposed) __privateMethod(this, _FxToast_instances, startTimer_fn).call(this, safe);
11660
+ if (safe > 0 && this.isConnected && !this.disposed)
11661
+ __privateMethod(this, _FxToast_instances, startTimer_fn).call(this, safe);
8057
11662
  }
8058
11663
  if (name === "duration" || name === "progress" || name === "progress-origin" || name === "kind") {
8059
11664
  __privateMethod(this, _FxToast_instances, syncProgress_fn).call(this);
@@ -8105,7 +11710,10 @@ pauseTimer_fn = function() {
8105
11710
  if (!this.timer) return;
8106
11711
  clearTimeout(this.timer);
8107
11712
  this.timer = null;
8108
- this.remaining = Math.max(0, this.remaining - (Date.now() - this.startedAt));
11713
+ this.remaining = Math.max(
11714
+ 0,
11715
+ this.remaining - (Date.now() - this.startedAt)
11716
+ );
8109
11717
  };
8110
11718
  resumeTimer_fn = function() {
8111
11719
  if (!this.counting || this.timer || this.disposed) return;
@@ -8135,15 +11743,23 @@ syncProgress_fn = function() {
8135
11743
  return;
8136
11744
  }
8137
11745
  if (!bar) {
8138
- this.shadowRoot.querySelector(".card").insertAdjacentHTML("beforeend", '<span class="progress" part="progress" aria-hidden="true"></span>');
11746
+ this.shadowRoot.querySelector(".card").insertAdjacentHTML(
11747
+ "beforeend",
11748
+ '<span class="progress" part="progress" aria-hidden="true"></span>'
11749
+ );
8139
11750
  }
8140
11751
  const el = this.shadowRoot.querySelector(".progress");
8141
11752
  el.style.setProperty("--toast-duration", `${dur}ms`);
8142
- el.style.setProperty("--toast-progress-origin", `${this.progressOrigin} center`);
11753
+ el.style.setProperty(
11754
+ "--toast-progress-origin",
11755
+ `${this.progressOrigin} center`
11756
+ );
8143
11757
  el.classList.remove("run");
8144
11758
  if (mode !== "paused") el.classList.add("run");
8145
11759
  card.classList.add("entering");
8146
- requestAnimationFrame(() => requestAnimationFrame(() => card.classList.remove("entering")));
11760
+ requestAnimationFrame(
11761
+ () => requestAnimationFrame(() => card.classList.remove("entering"))
11762
+ );
8147
11763
  };
8148
11764
  render_fn = function() {
8149
11765
  const kind = this.getAttribute("kind") || "info";
@@ -8156,7 +11772,7 @@ render_fn = function() {
8156
11772
  const iconContent = isFxIcon ? iconName : KIND_ICON[kind] ?? KIND_ICON.info;
8157
11773
  this.shadowRoot.innerHTML = `
8158
11774
  <style>${CARD_CSS}</style>
8159
- <div class="card" style="--kind:${color}" role="status" part="card">
11775
+ <div class="card${title ? "" : " no-title"}" style="--kind:${color}" role="status" part="card">
8160
11776
  <span class="icon${isFxIcon ? " fx-icon" : ""}">${iconContent}</span>
8161
11777
  <div class="body">
8162
11778
  ${title ? '<div class="title" part="title"></div>' : ""}
@@ -8196,7 +11812,10 @@ render_fn = function() {
8196
11812
  btn.title = "Copiar mensagem";
8197
11813
  }, 1200);
8198
11814
  });
8199
- this.shadowRoot.querySelector(".close").addEventListener("click", () => this.dismiss());
11815
+ this.shadowRoot.querySelector(".close").addEventListener(
11816
+ "click",
11817
+ () => this.dismiss()
11818
+ );
8200
11819
  };
8201
11820
  /**
8202
11821
  * Mostra "Ver mais" somente quando a mensagem passou de 2 linhas.
@@ -8214,7 +11833,9 @@ syncMore_fn = function() {
8214
11833
  more.hidden = !(msg.scrollHeight - msg.clientHeight > 1);
8215
11834
  };
8216
11835
  function regionFor(position) {
8217
- let region = document.querySelector(`[data-fx-toast-region="${position}"]`);
11836
+ let region = document.querySelector(
11837
+ `[data-fx-toast-region="${position}"]`
11838
+ );
8218
11839
  if (!region) {
8219
11840
  region = document.createElement("div");
8220
11841
  region.setAttribute("data-fx-toast-region", position);
@@ -8249,7 +11870,10 @@ class ToastApi {
8249
11870
  push(kind, title, messageOrOpts, opts) {
8250
11871
  const id = ++this.seq;
8251
11872
  const message = typeof messageOrOpts === "string" ? messageOrOpts : messageOrOpts?.message;
8252
- const o = { ...typeof messageOrOpts === "object" && messageOrOpts ? messageOrOpts : void 0, ...opts };
11873
+ const o = {
11874
+ ...typeof messageOrOpts === "object" && messageOrOpts ? messageOrOpts : void 0,
11875
+ ...opts
11876
+ };
8253
11877
  const el = document.createElement("fx-toast");
8254
11878
  el.setAttribute("kind", kind);
8255
11879
  el.setAttribute("title", title);
@@ -8274,7 +11898,8 @@ class ToastApi {
8274
11898
  const FenixToast = new ToastApi();
8275
11899
  globalThis.FenixToast = FenixToast;
8276
11900
  function defineFxToast() {
8277
- if (!customElements.get("fx-toast")) customElements.define("fx-toast", FxToast);
11901
+ if (!customElements.get("fx-toast"))
11902
+ customElements.define("fx-toast", FxToast);
8278
11903
  }
8279
11904
  defineFxToast();
8280
11905
  const _FxTooltip = class _FxTooltip extends FxElement {
@@ -11792,6 +15417,7 @@ defineFxAccordion();
11792
15417
  defineFxAccordionPanel();
11793
15418
  export {
11794
15419
  DEFAULT_PRESET,
15420
+ FX_EDITOR_CONTENT_CSS,
11795
15421
  FX_JSX_TYPES,
11796
15422
  FenixToast,
11797
15423
  FenixUI,
@@ -11814,6 +15440,7 @@ export {
11814
15440
  FxDrawer,
11815
15441
  FxDropdown,
11816
15442
  FxDropdownItem,
15443
+ FxEditor,
11817
15444
  FxElement,
11818
15445
  FxEmptyState,
11819
15446
  FxFileUpload,
@@ -11863,8 +15490,15 @@ export {
11863
15490
  kebabToCamel,
11864
15491
  lightTokens,
11865
15492
  listPresets,
15493
+ normalizeForEditor,
15494
+ normalizeForStorage,
15495
+ normalizeHtml,
11866
15496
  resetTheme,
11867
15497
  h as sanitizeData,
15498
+ sanitizeEmbedUrl,
15499
+ sanitizeHtml,
15500
+ sanitizeLinkUrl,
15501
+ sanitizeMediaUrl,
11868
15502
  setTokens,
11869
15503
  j as stripTags,
11870
15504
  theme,