@formulaxjs/editor 0.2.0 → 0.3.1

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.
package/dist/index.cjs CHANGED
@@ -20,101 +20,178 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- DEFAULT_FORMULA_ATTRIBUTE: () => DEFAULT_FORMULA_ATTRIBUTE,
24
- DEFAULT_FORMULA_CLASS: () => DEFAULT_FORMULA_CLASS,
25
- FORMULA_FLAG_ATTRIBUTE: () => FORMULA_FLAG_ATTRIBUTE,
26
- FormulaXEditor: () => import_kity_runtime2.FormulaXEditor,
27
- createFormulaElement: () => createFormulaElement,
28
- createFormulaMarkup: () => createFormulaMarkup,
29
- createKityEditor: () => import_kity_runtime2.createKityEditor,
23
+ clearFormulaXPerfMarks: () => clearFormulaXPerfMarks,
30
24
  ensureFormulaXModalStyles: () => ensureFormulaXModalStyles,
31
- ensureKityRuntime: () => import_kity_runtime2.ensureKityRuntime,
32
- escapeAttribute: () => escapeAttribute,
33
- escapeHtml: () => escapeHtml,
34
- findFormulaElement: () => findFormulaElement,
35
25
  formulaXModalStyles: () => formulaXModalStyles,
36
- getFormulaLatexFromElement: () => getFormulaLatexFromElement,
37
- isFormulaElement: () => isFormulaElement,
26
+ markFormulaXPerf: () => markFormulaXPerf,
27
+ measureFormulaXPerf: () => measureFormulaXPerf,
38
28
  mountFormulaXEditor: () => mountFormulaXEditor,
39
- mountKityEditor: () => import_kity_runtime2.mountKityEditor,
40
- replaceFormulaElement: () => replaceFormulaElement,
41
- serializeSvgForInsertion: () => serializeSvgForInsertion
29
+ preloadFormulaXEditor: () => preloadFormulaXEditor,
30
+ recordFormulaXPerfPoint: () => recordFormulaXPerfPoint,
31
+ renderFormulaXEditorLoadingState: () => renderFormulaXEditorLoadingState,
32
+ scheduleFormulaXEditorPreload: () => scheduleFormulaXEditorPreload,
33
+ waitForFormulaXAnimationFrame: () => waitForFormulaXAnimationFrame
42
34
  });
43
35
  module.exports = __toCommonJS(index_exports);
44
36
 
45
37
  // src/formula-modal.ts
46
38
  var import_core = require("@formulaxjs/core");
47
- var import_kity_runtime = require("@formulaxjs/kity-runtime");
39
+ var import_kity_runtime2 = require("@formulaxjs/kity-runtime");
40
+ var import_renderer = require("@formulaxjs/renderer");
41
+ var import_renderer_kity = require("@formulaxjs/renderer-kity");
48
42
 
49
- // src/formula-node.ts
50
- var DEFAULT_FORMULA_ATTRIBUTE = "data-formulax-latex";
51
- var FORMULA_FLAG_ATTRIBUTE = "data-formulax";
52
- var DEFAULT_FORMULA_CLASS = "formulax-math";
53
- function escapeAttribute(value) {
54
- return value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
55
- }
56
- function escapeHtml(value) {
57
- return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
58
- }
59
- function createFormulaMarkup(latex, options = {}) {
60
- const attributeName = options.attributeName ?? DEFAULT_FORMULA_ATTRIBUTE;
61
- const className = options.className ?? DEFAULT_FORMULA_CLASS;
62
- const displayClass = options.displayMode ? `${className} ${className}--block` : className;
63
- const safeLatex = escapeAttribute(latex);
64
- const cursorStyle = options.cursorStyle?.trim() || "pointer";
65
- const extraAttributes = {
66
- ...options.extraAttributes ?? {},
67
- style: mergeInlineStyles(
68
- typeof options.extraAttributes?.style === "string" ? options.extraAttributes.style : "",
69
- cursorStyle ? `cursor: ${cursorStyle}` : ""
70
- )
43
+ // src/perf.ts
44
+ var import_kity_runtime = require("@formulaxjs/kity-runtime");
45
+ function getPerfHost() {
46
+ return globalThis;
47
+ }
48
+ function getPerfState() {
49
+ const host = getPerfHost();
50
+ host.__FORMULAX_PERF_STATE__ ??= {
51
+ reportedMeasureCount: 0,
52
+ reportScheduled: false
71
53
  };
72
- const serializedAttributes = Object.entries(extraAttributes).filter(([, value]) => value !== null && value !== void 0 && value !== false).map(([key, value]) => value === true ? key : `${key}="${escapeAttribute(String(value))}"`);
73
- return [
74
- "<span",
75
- ` class="${escapeAttribute(displayClass)}"`,
76
- ` ${FORMULA_FLAG_ATTRIBUTE}="true"`,
77
- ` ${attributeName}="${safeLatex}"`,
78
- ` data-latex="${safeLatex}"`,
79
- ' contenteditable="false"',
80
- ' role="button"',
81
- ' tabindex="0"',
82
- serializedAttributes.length ? ` ${serializedAttributes.join(" ")}` : "",
83
- ">",
84
- options.renderHtml ?? `<span class="${escapeAttribute(className)}__render">${escapeHtml(latex || "\\square")}</span>`,
85
- "</span>"
86
- ].join("");
87
- }
88
- function mergeInlineStyles(existingStyle, nextStyle) {
89
- const existing = existingStyle.trim().replace(/;+\s*$/, "");
90
- const next = nextStyle.trim().replace(/;+\s*$/, "");
91
- if (!existing) return next;
92
- if (!next) return existing;
93
- return `${existing}; ${next}`;
94
- }
95
- function createFormulaElement(ownerDocument, latex, options = {}) {
96
- const wrapper = ownerDocument.createElement("span");
97
- wrapper.innerHTML = createFormulaMarkup(latex, options);
98
- return wrapper.firstElementChild;
99
- }
100
- function replaceFormulaElement(target, latex, options = {}) {
101
- const next = createFormulaElement(target.ownerDocument ?? document, latex, options);
102
- if (!next) return null;
103
- target.replaceWith(next);
104
- return next;
105
- }
106
- function getFormulaLatexFromElement(element, attributeName = DEFAULT_FORMULA_ATTRIBUTE) {
107
- return element.getAttribute(attributeName) ?? element.getAttribute("data-latex") ?? "";
108
- }
109
- function isFormulaElement(node) {
110
- if (!node || typeof node !== "object") return false;
111
- const element = node;
112
- return typeof element.getAttribute === "function" && element.getAttribute(FORMULA_FLAG_ATTRIBUTE) === "true";
113
- }
114
- function findFormulaElement(node) {
115
- if (!node) return null;
116
- const element = node.nodeType === 1 ? node : node.parentElement;
117
- return element?.closest?.(`[${FORMULA_FLAG_ATTRIBUTE}="true"]`);
54
+ return host.__FORMULAX_PERF_STATE__;
55
+ }
56
+ function hasPerfSupport() {
57
+ return typeof performance !== "undefined" && typeof performance.mark === "function" && typeof performance.measure === "function" && typeof performance.getEntriesByType === "function";
58
+ }
59
+ function isPerfDebugEnabled() {
60
+ return getPerfHost().__FORMULAX_PERF__ === true;
61
+ }
62
+ function schedulePerfReport() {
63
+ if (!hasPerfSupport() || !isPerfDebugEnabled()) {
64
+ return;
65
+ }
66
+ const state = getPerfState();
67
+ if (state.reportScheduled) {
68
+ return;
69
+ }
70
+ state.reportScheduled = true;
71
+ queueMicrotask(() => {
72
+ state.reportScheduled = false;
73
+ const entries = performance.getEntriesByType("measure").filter((entry) => entry.name.startsWith("fx:")).sort((left, right) => left.startTime - right.startTime);
74
+ const nextEntries = entries.slice(state.reportedMeasureCount);
75
+ state.reportedMeasureCount = entries.length;
76
+ if (!nextEntries.length) {
77
+ return;
78
+ }
79
+ console.table(nextEntries.map((entry) => ({
80
+ name: entry.name,
81
+ duration: Number(entry.duration.toFixed(2)),
82
+ startTime: Number(entry.startTime.toFixed(2))
83
+ })));
84
+ });
85
+ }
86
+ function markFormulaXPerf(name) {
87
+ if (!hasPerfSupport()) {
88
+ return null;
89
+ }
90
+ const markName = `${name}::${Date.now()}::${Math.random().toString(36).slice(2, 8)}`;
91
+ performance.mark(markName);
92
+ return markName;
93
+ }
94
+ function measureFormulaXPerf(name, startMark, endMark) {
95
+ if (!hasPerfSupport() || !startMark) {
96
+ return null;
97
+ }
98
+ const resolvedEndMark = endMark ?? markFormulaXPerf(`${name}:end`);
99
+ if (!resolvedEndMark) {
100
+ return null;
101
+ }
102
+ performance.measure(name, startMark, resolvedEndMark);
103
+ schedulePerfReport();
104
+ return resolvedEndMark;
105
+ }
106
+ function recordFormulaXPerfPoint(name) {
107
+ const markName = markFormulaXPerf(name);
108
+ if (!markName) {
109
+ return;
110
+ }
111
+ measureFormulaXPerf(name, markName, markName);
112
+ clearFormulaXPerfMarks(markName);
113
+ }
114
+ function clearFormulaXPerfMarks(...marks) {
115
+ if (!hasPerfSupport()) {
116
+ return;
117
+ }
118
+ for (const mark of marks) {
119
+ if (!mark) {
120
+ continue;
121
+ }
122
+ performance.clearMarks(mark);
123
+ }
124
+ }
125
+ async function preloadFormulaXEditor() {
126
+ await (0, import_kity_runtime.ensureKityRuntime)();
127
+ }
128
+ function scheduleFormulaXEditorPreload(mode, target) {
129
+ if (mode === false || typeof window === "undefined") {
130
+ return () => void 0;
131
+ }
132
+ let disposed = false;
133
+ let triggered = false;
134
+ const cleanupCallbacks = [];
135
+ const trigger = () => {
136
+ if (disposed || triggered) {
137
+ return;
138
+ }
139
+ triggered = true;
140
+ while (cleanupCallbacks.length) {
141
+ cleanupCallbacks.pop()?.();
142
+ }
143
+ void preloadFormulaXEditor();
144
+ };
145
+ if (mode === "idle") {
146
+ const host = getPerfHost();
147
+ if (typeof host.requestIdleCallback === "function") {
148
+ const handle = host.requestIdleCallback(() => {
149
+ trigger();
150
+ });
151
+ cleanupCallbacks.push(() => {
152
+ host.cancelIdleCallback?.(handle);
153
+ });
154
+ } else {
155
+ const handle = window.setTimeout(() => {
156
+ trigger();
157
+ }, 1);
158
+ cleanupCallbacks.push(() => {
159
+ window.clearTimeout(handle);
160
+ });
161
+ }
162
+ return () => {
163
+ disposed = true;
164
+ while (cleanupCallbacks.length) {
165
+ cleanupCallbacks.pop()?.();
166
+ }
167
+ };
168
+ }
169
+ if (target && "addEventListener" in target && "removeEventListener" in target) {
170
+ const eventTarget = target;
171
+ const onActivate = () => {
172
+ trigger();
173
+ };
174
+ eventTarget.addEventListener("pointerenter", onActivate, { once: true, passive: true });
175
+ eventTarget.addEventListener("focusin", onActivate, { once: true });
176
+ cleanupCallbacks.push(() => {
177
+ eventTarget.removeEventListener("pointerenter", onActivate);
178
+ eventTarget.removeEventListener("focusin", onActivate);
179
+ });
180
+ }
181
+ return () => {
182
+ disposed = true;
183
+ while (cleanupCallbacks.length) {
184
+ cleanupCallbacks.pop()?.();
185
+ }
186
+ };
187
+ }
188
+ function waitForFormulaXAnimationFrame() {
189
+ if (typeof window === "undefined" || typeof window.requestAnimationFrame !== "function") {
190
+ return Promise.resolve();
191
+ }
192
+ return new Promise((resolve) => {
193
+ window.requestAnimationFrame(() => resolve());
194
+ });
118
195
  }
119
196
 
120
197
  // src/formula-modal.ts
@@ -223,6 +300,8 @@ var formulaXModalStyles = `
223
300
  }
224
301
 
225
302
  .fx-formula-kity-host .kf-editor {
303
+ box-sizing: border-box;
304
+ width: 100%;
226
305
  height: var(--fx-formula-editor-body-height) !important;
227
306
  overflow: visible !important;
228
307
  }
@@ -255,8 +334,7 @@ var formulaXModalStyles = `
255
334
  .fx-formula-kity-host .kf-editor svg text,
256
335
  .fx-formula-kity-host .kf-editor-ui-area-item-text,
257
336
  .fx-formula-kity-host .kf-editor-ui-box-item-text,
258
- .fx-formula-kity-host .kf-editor-ui-box-item-val,
259
- .formulax-math__render {
337
+ .fx-formula-kity-host .kf-editor-ui-box-item-val {
260
338
  font-family: "KF AMS MAIN", "Cambria Math", "Latin Modern Math", "Times New Roman", serif !important;
261
339
  }
262
340
 
@@ -331,60 +409,35 @@ var formulaXModalStyles = `
331
409
  background: #2563eb;
332
410
  color: #fff;
333
411
  }
334
-
335
- .formulax-math {
336
- display: inline-flex;
337
- align-items: center;
338
- vertical-align: middle;
339
- line-height: 1;
340
- padding: 0 2px;
341
- margin: 0 1px;
342
- border-radius: 3px;
343
- background: transparent;
344
- cursor: pointer;
345
- user-select: none;
346
- }
347
-
348
- .formulax-math:hover {
349
- outline: 1px solid rgba(37, 99, 235, 0.35);
350
- background: rgba(37, 99, 235, 0.06);
351
- }
352
-
353
- .formulax-math__svg {
354
- display: inline-block;
355
- flex: 0 0 auto;
356
- max-width: 100%;
357
- vertical-align: -0.35em;
358
- pointer-events: none;
359
- }
360
-
361
- .formulax-math__image {
362
- display: inline-block;
363
- max-width: 100%;
364
- height: auto;
365
- vertical-align: middle;
366
- pointer-events: none;
367
- }
368
412
  `;
369
413
  function ensureFormulaXModalStyles(doc = document) {
414
+ (0, import_renderer.ensureFormulaXBaseStyles)(doc);
370
415
  if (doc.getElementById(STYLE_ID)) return;
371
416
  const style = doc.createElement("style");
372
417
  style.id = STYLE_ID;
373
418
  style.textContent = formulaXModalStyles;
374
419
  doc.head.appendChild(style);
375
420
  }
376
- function mountFormulaXEditor(root, options = {}) {
377
- let destroyed = false;
378
- let latestLatex = options.initialLatex ?? "";
379
- let handle = null;
380
- const initialLatex = latestLatex.trim() ? latestLatex : EMPTY_FORMULA_PLACEHOLDER;
421
+ function renderFormulaXEditorLoadingState(root) {
381
422
  root.classList.add("fx-formula-kity-host");
382
423
  root.innerHTML = `
383
424
  <div class="fx-formula-editor-loading" role="status" aria-live="polite">
384
425
  Loading FormulaX editor...
385
426
  </div>
386
427
  `;
387
- const readyPromise = (0, import_kity_runtime.mountKityEditor)(root, {
428
+ }
429
+ function mountFormulaXEditor(root, options = {}) {
430
+ recordFormulaXPerfPoint("fx:formula-editor:mount:start");
431
+ const mountStart = markFormulaXPerf("fx:formula-editor:mount:start:scope");
432
+ let destroyed = false;
433
+ let latestLatex = options.initialLatex ?? "";
434
+ let handle = null;
435
+ const initialLatex = latestLatex.trim() ? latestLatex : EMPTY_FORMULA_PLACEHOLDER;
436
+ renderFormulaXEditorLoadingState(root);
437
+ const loadingVisibleMark = markFormulaXPerf("fx:formula-editor:loading-visible");
438
+ measureFormulaXPerf("fx:formula-editor:loading-visible", mountStart, loadingVisibleMark);
439
+ clearFormulaXPerfMarks(loadingVisibleMark);
440
+ const readyPromise = (0, import_kity_runtime2.mountKityEditor)(root, {
388
441
  initialLatex,
389
442
  height: options.height ?? "100%",
390
443
  autofocus: options.autofocus ?? true,
@@ -397,6 +450,9 @@ function mountFormulaXEditor(root, options = {}) {
397
450
  nextHandle.destroy();
398
451
  throw new Error("FormulaX editor mount cancelled");
399
452
  }
453
+ const readyMark = markFormulaXPerf("fx:kity-editor:ready");
454
+ measureFormulaXPerf("fx:kity-editor:ready", mountStart, readyMark);
455
+ clearFormulaXPerfMarks(readyMark);
400
456
  handle = nextHandle;
401
457
  return nextHandle;
402
458
  }).catch((error) => {
@@ -405,11 +461,13 @@ function mountFormulaXEditor(root, options = {}) {
405
461
  root.innerHTML = `
406
462
  <div class="fx-formula-editor-error">
407
463
  Failed to load FormulaX editor.
408
- <pre>${escapeHtml(error instanceof Error ? error.message : String(error))}</pre>
464
+ <pre>${(0, import_renderer.escapeHtml)(error instanceof Error ? error.message : String(error))}</pre>
409
465
  </div>
410
466
  `;
411
467
  }
412
468
  throw error;
469
+ }).finally(() => {
470
+ clearFormulaXPerfMarks(mountStart);
413
471
  });
414
472
  const getCurrentLatex = async () => {
415
473
  const readyHandle = handle ?? await readyPromise;
@@ -435,8 +493,8 @@ function mountFormulaXEditor(root, options = {}) {
435
493
  },
436
494
  async getRenderHtml() {
437
495
  await readyPromise;
438
- await waitForFormulaSvgLayout(root);
439
- return renderCurrentFormulaAsSvgHtml(root);
496
+ await (0, import_renderer_kity.waitForKityFormulaSvgLayout)(root);
497
+ return (0, import_renderer_kity.serializeKityFormulaFromRoot)(root);
440
498
  },
441
499
  destroy() {
442
500
  if (destroyed) return;
@@ -486,325 +544,18 @@ async function tryReadLatexFromKityHandle(handle) {
486
544
  }
487
545
  return null;
488
546
  }
489
- function renderCurrentFormulaAsSvgHtml(root) {
490
- const svg = findFormulaSvg(root);
491
- if (!svg) {
492
- return "";
493
- }
494
- return serializeSvgForInsertion(svg);
495
- }
496
- async function waitForFormulaSvgLayout(root) {
497
- const doc = root.ownerDocument ?? document;
498
- const view = doc.defaultView ?? window;
499
- await waitForDocumentFonts(doc);
500
- let previous = readRenderedFormulaBox(root);
501
- for (let attempt = 0; attempt < 4; attempt += 1) {
502
- await waitForAnimationFrame(view);
503
- const current = readRenderedFormulaBox(root);
504
- if (previous && current && areSvgBoxesClose(previous, current)) {
505
- return;
506
- }
507
- previous = current;
508
- }
509
- }
510
- function findFormulaSvg(root) {
511
- return root.querySelector(
512
- ".kf-editor-edit-area svg, .kf-editor-canvas-container svg, svg"
513
- );
514
- }
515
- function readRenderedFormulaBox(root) {
516
- const svg = findFormulaSvg(root);
517
- if (!svg) {
518
- return null;
519
- }
520
- return getInlineSvgContent(svg)?.box ?? readSvgBox(svg);
521
- }
522
- function areSvgBoxesClose(left, right) {
523
- return Math.abs(left.x - right.x) < 0.01 && Math.abs(left.y - right.y) < 0.01 && Math.abs(left.width - right.width) < 0.01 && Math.abs(left.height - right.height) < 0.01;
524
- }
525
- async function waitForDocumentFonts(doc) {
526
- if (!doc.fonts?.ready) {
527
- return;
528
- }
529
- try {
530
- await doc.fonts.ready;
531
- } catch {
532
- }
533
- }
534
- function waitForAnimationFrame(view) {
535
- return new Promise((resolve) => {
536
- view.requestAnimationFrame(() => resolve());
537
- });
538
- }
539
- function serializeSvgForInsertion(svg) {
540
- const content = getInlineSvgContent(svg);
541
- const inlineViewport = content ? createInlineSvgViewport(content.box) : null;
542
- const clone = content && inlineViewport ? createInlineSvgClone(svg, content, inlineViewport) : svg.cloneNode(true);
543
- uniquifySvgIds(clone);
544
- sizeSvgForInlineDisplay(clone, svg, inlineViewport);
545
- clone.removeAttribute("id");
546
- clone.removeAttribute("xmlns");
547
- clone.removeAttribute("xmlns:xlink");
548
- clone.setAttribute("class", mergeClassNames(clone.getAttribute("class"), "formulax-math__svg"));
549
- clone.setAttribute("focusable", "false");
550
- clone.setAttribute("aria-hidden", "true");
551
- clone.setAttribute("preserveAspectRatio", clone.getAttribute("preserveAspectRatio") || "xMinYMin meet");
552
- return new XMLSerializer().serializeToString(clone);
553
- }
554
- function getInlineSvgContent(svg) {
555
- const candidates = [
556
- '[data-root="true"] > g[data-type="kf-editor-exp-content-box"]',
557
- 'g[data-type="kf-editor-exp-content-box"]',
558
- 'g[data-type="kf-container"]',
559
- "svg > g, g"
560
- ];
561
- for (const selector of candidates) {
562
- const content = svg.querySelector(selector);
563
- const rootSpace = content ? readSvgBoxInRootSpace(content) : null;
564
- if (content && rootSpace) {
565
- return {
566
- root: content,
567
- box: rootSpace.box,
568
- matrix: rootSpace.matrix
569
- };
570
- }
571
- }
572
- return null;
573
- }
574
- function readSvgBoxInRootSpace(element) {
575
- const box = readSvgBox(element);
576
- const matrix = getSvgRootSpaceMatrix(element);
577
- if (!box || !matrix) {
578
- return null;
579
- }
580
- const points = [
581
- { x: box.x, y: box.y },
582
- { x: box.x + box.width, y: box.y },
583
- { x: box.x, y: box.y + box.height },
584
- { x: box.x + box.width, y: box.y + box.height }
585
- ].map((point) => ({
586
- x: matrix.a * point.x + matrix.c * point.y + matrix.e,
587
- y: matrix.b * point.x + matrix.d * point.y + matrix.f
588
- }));
589
- const xs = points.map((point) => point.x);
590
- const ys = points.map((point) => point.y);
591
- const x = Math.min(...xs);
592
- const y = Math.min(...ys);
593
- const width = Math.max(...xs) - x;
594
- const height = Math.max(...ys) - y;
595
- if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
596
- return null;
597
- }
598
- return {
599
- box: { x, y, width, height },
600
- matrix
601
- };
602
- }
603
- function getSvgRootSpaceMatrix(element) {
604
- const elementMatrix = typeof element.getCTM === "function" ? element.getCTM() : null;
605
- const rootMatrix = typeof element.ownerSVGElement?.getCTM === "function" ? element.ownerSVGElement.getCTM() : null;
606
- if (!elementMatrix) {
607
- return null;
608
- }
609
- return rootMatrix ? multiplySvgMatrices(invertSvgMatrix(rootMatrix), elementMatrix) : toSvgMatrixLike(elementMatrix);
610
- }
611
- function invertSvgMatrix(matrix) {
612
- const determinant = matrix.a * matrix.d - matrix.b * matrix.c;
613
- if (!Number.isFinite(determinant) || determinant === 0) {
614
- return {
615
- a: 1,
616
- b: 0,
617
- c: 0,
618
- d: 1,
619
- e: 0,
620
- f: 0
621
- };
622
- }
623
- return {
624
- a: matrix.d / determinant,
625
- b: -matrix.b / determinant,
626
- c: -matrix.c / determinant,
627
- d: matrix.a / determinant,
628
- e: (matrix.c * matrix.f - matrix.d * matrix.e) / determinant,
629
- f: (matrix.b * matrix.e - matrix.a * matrix.f) / determinant
630
- };
631
- }
632
- function multiplySvgMatrices(left, right) {
633
- return {
634
- a: left.a * right.a + left.c * right.b,
635
- b: left.b * right.a + left.d * right.b,
636
- c: left.a * right.c + left.c * right.d,
637
- d: left.b * right.c + left.d * right.d,
638
- e: left.a * right.e + left.c * right.f + left.e,
639
- f: left.b * right.e + left.d * right.f + left.f
640
- };
641
- }
642
- function toSvgMatrixLike(matrix) {
643
- return {
644
- a: matrix.a,
645
- b: matrix.b,
646
- c: matrix.c,
647
- d: matrix.d,
648
- e: matrix.e,
649
- f: matrix.f
650
- };
651
- }
652
- function readSvgBox(element) {
653
- if (typeof element.getBBox !== "function") {
654
- return null;
655
- }
656
- try {
657
- const box = element.getBBox();
658
- if (!Number.isFinite(box.width) || !Number.isFinite(box.height) || box.width <= 0 || box.height <= 0) {
659
- return null;
660
- }
661
- return {
662
- x: box.x,
663
- y: box.y,
664
- width: box.width,
665
- height: box.height
666
- };
667
- } catch {
668
- return null;
669
- }
670
- }
671
- function createInlineSvgViewport(contentBox) {
672
- const edgePadding = Math.max(0.5, Math.min(contentBox.width, contentBox.height) * 6e-3);
673
- const inset = edgePadding / 2;
674
- return {
675
- x: contentBox.x - inset,
676
- y: contentBox.y - inset,
677
- width: contentBox.width + edgePadding,
678
- height: contentBox.height + edgePadding
679
- };
680
- }
681
- function createInlineSvgClone(source, content, viewport) {
682
- const clone = source.cloneNode(false);
683
- const ownerDocument = source.ownerDocument;
684
- copySvgRootAttributes(source, clone);
685
- clone.setAttribute(
686
- "viewBox",
687
- `0 0 ${roundLength(viewport.width)} ${roundLength(viewport.height)}`
688
- );
689
- Array.from(source.children).forEach((child) => {
690
- if (child.tagName.toLowerCase() === "defs") {
691
- clone.appendChild(child.cloneNode(true));
692
- }
693
- });
694
- const wrapper = ownerDocument.createElementNS("http://www.w3.org/2000/svg", "g");
695
- wrapper.setAttribute(
696
- "transform",
697
- `translate(${roundLength(-viewport.x)} ${roundLength(-viewport.y)})`
698
- );
699
- const flattened = ownerDocument.createElementNS("http://www.w3.org/2000/svg", "g");
700
- flattened.setAttribute(
701
- "transform",
702
- `matrix(${roundLength(content.matrix.a)} ${roundLength(content.matrix.b)} ${roundLength(content.matrix.c)} ${roundLength(content.matrix.d)} ${roundLength(content.matrix.e)} ${roundLength(content.matrix.f)})`
703
- );
704
- flattened.appendChild(content.root.cloneNode(true));
705
- wrapper.appendChild(flattened);
706
- clone.appendChild(wrapper);
707
- return clone;
708
- }
709
- function copySvgRootAttributes(source, target) {
710
- const excluded = /* @__PURE__ */ new Set([
711
- "id",
712
- "width",
713
- "height",
714
- "viewBox",
715
- "class",
716
- "focusable",
717
- "aria-hidden",
718
- "xmlns",
719
- "xmlns:xlink"
720
- ]);
721
- Array.from(source.attributes).forEach((attribute) => {
722
- if (excluded.has(attribute.name)) return;
723
- target.setAttribute(attribute.name, attribute.value);
724
- });
725
- }
726
- function sizeSvgForInlineDisplay(clone, source, viewport) {
727
- const viewBox = clone.viewBox?.baseVal;
728
- const rect = source.getBoundingClientRect();
729
- const width = viewport?.width || viewBox?.width || rect.width || Number(clone.getAttribute("width")) || 1;
730
- const height = viewport?.height || viewBox?.height || rect.height || Number(clone.getAttribute("height")) || 1;
731
- const ratio = Math.max(0.1, width / Math.max(1, height));
732
- const inlineHeightEm = 0.875;
733
- const inlineWidthEm = Math.min(40, Math.max(0.75, ratio * inlineHeightEm));
734
- clone.setAttribute("width", roundLength(width));
735
- clone.setAttribute("height", roundLength(height));
736
- clone.setAttribute(
737
- "style",
738
- mergeInlineStyles2(
739
- clone.getAttribute("style"),
740
- `width:${roundLength(inlineWidthEm)}em`,
741
- `height:${inlineHeightEm}em`
742
- )
743
- );
744
- }
745
- function roundLength(value) {
746
- return String(Math.round(value * 1e3) / 1e3);
747
- }
748
- function uniquifySvgIds(svg) {
749
- const idMap = /* @__PURE__ */ new Map();
750
- const prefix = `fx-${randomIdPrefix()}-`;
751
- const elementsWithId = svg.querySelectorAll("[id]");
752
- elementsWithId.forEach((element) => {
753
- const id = element.getAttribute("id");
754
- if (!id) return;
755
- const nextId = `${prefix}${id}`;
756
- idMap.set(id, nextId);
757
- element.setAttribute("id", nextId);
758
- });
759
- if (!idMap.size) return;
760
- svg.querySelectorAll("*").forEach((element) => {
761
- Array.from(element.attributes).forEach((attribute) => {
762
- const nextValue = rewriteSvgReferences(attribute.value, idMap);
763
- if (nextValue !== attribute.value) {
764
- element.setAttribute(attribute.name, nextValue);
765
- }
766
- });
767
- });
768
- }
769
- function randomIdPrefix() {
770
- return Math.random().toString(36).slice(2, 5).padEnd(3, "0");
771
- }
772
- function rewriteSvgReferences(value, idMap) {
773
- let nextValue = value;
774
- idMap.forEach((nextId, id) => {
775
- nextValue = nextValue.replaceAll(`#${id}`, `#${nextId}`).replaceAll(`url(${id})`, `url(${nextId})`).replaceAll(`url(#${id})`, `url(#${nextId})`);
776
- });
777
- return nextValue;
778
- }
779
- function mergeClassNames(...values) {
780
- return values.flatMap((value) => value?.split(/\s+/) ?? []).filter(Boolean).filter((value, index, list) => list.indexOf(value) === index).join(" ");
781
- }
782
- function mergeInlineStyles2(...values) {
783
- return values.flatMap((value) => value?.split(";") ?? []).map((value) => value.trim()).filter(Boolean).join("; ");
784
- }
785
-
786
- // src/index.ts
787
- var import_kity_runtime2 = require("@formulaxjs/kity-runtime");
788
547
  // Annotate the CommonJS export names for ESM import in node:
789
548
  0 && (module.exports = {
790
- DEFAULT_FORMULA_ATTRIBUTE,
791
- DEFAULT_FORMULA_CLASS,
792
- FORMULA_FLAG_ATTRIBUTE,
793
- FormulaXEditor,
794
- createFormulaElement,
795
- createFormulaMarkup,
796
- createKityEditor,
549
+ clearFormulaXPerfMarks,
797
550
  ensureFormulaXModalStyles,
798
- ensureKityRuntime,
799
- escapeAttribute,
800
- escapeHtml,
801
- findFormulaElement,
802
551
  formulaXModalStyles,
803
- getFormulaLatexFromElement,
804
- isFormulaElement,
552
+ markFormulaXPerf,
553
+ measureFormulaXPerf,
805
554
  mountFormulaXEditor,
806
- mountKityEditor,
807
- replaceFormulaElement,
808
- serializeSvgForInsertion
555
+ preloadFormulaXEditor,
556
+ recordFormulaXPerfPoint,
557
+ renderFormulaXEditorLoadingState,
558
+ scheduleFormulaXEditorPreload,
559
+ waitForFormulaXAnimationFrame
809
560
  });
810
561
  //# sourceMappingURL=index.cjs.map