@formulaxjs/editor 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
53
+ };
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
+ }
71
186
  };
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"]`);
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
@@ -255,8 +332,7 @@ var formulaXModalStyles = `
255
332
  .fx-formula-kity-host .kf-editor svg text,
256
333
  .fx-formula-kity-host .kf-editor-ui-area-item-text,
257
334
  .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 {
335
+ .fx-formula-kity-host .kf-editor-ui-box-item-val {
260
336
  font-family: "KF AMS MAIN", "Cambria Math", "Latin Modern Math", "Times New Roman", serif !important;
261
337
  }
262
338
 
@@ -331,60 +407,35 @@ var formulaXModalStyles = `
331
407
  background: #2563eb;
332
408
  color: #fff;
333
409
  }
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
410
  `;
369
411
  function ensureFormulaXModalStyles(doc = document) {
412
+ (0, import_renderer.ensureFormulaXBaseStyles)(doc);
370
413
  if (doc.getElementById(STYLE_ID)) return;
371
414
  const style = doc.createElement("style");
372
415
  style.id = STYLE_ID;
373
416
  style.textContent = formulaXModalStyles;
374
417
  doc.head.appendChild(style);
375
418
  }
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;
419
+ function renderFormulaXEditorLoadingState(root) {
381
420
  root.classList.add("fx-formula-kity-host");
382
421
  root.innerHTML = `
383
422
  <div class="fx-formula-editor-loading" role="status" aria-live="polite">
384
423
  Loading FormulaX editor...
385
424
  </div>
386
425
  `;
387
- const readyPromise = (0, import_kity_runtime.mountKityEditor)(root, {
426
+ }
427
+ function mountFormulaXEditor(root, options = {}) {
428
+ recordFormulaXPerfPoint("fx:formula-editor:mount:start");
429
+ const mountStart = markFormulaXPerf("fx:formula-editor:mount:start:scope");
430
+ let destroyed = false;
431
+ let latestLatex = options.initialLatex ?? "";
432
+ let handle = null;
433
+ const initialLatex = latestLatex.trim() ? latestLatex : EMPTY_FORMULA_PLACEHOLDER;
434
+ renderFormulaXEditorLoadingState(root);
435
+ const loadingVisibleMark = markFormulaXPerf("fx:formula-editor:loading-visible");
436
+ measureFormulaXPerf("fx:formula-editor:loading-visible", mountStart, loadingVisibleMark);
437
+ clearFormulaXPerfMarks(loadingVisibleMark);
438
+ const readyPromise = (0, import_kity_runtime2.mountKityEditor)(root, {
388
439
  initialLatex,
389
440
  height: options.height ?? "100%",
390
441
  autofocus: options.autofocus ?? true,
@@ -397,6 +448,9 @@ function mountFormulaXEditor(root, options = {}) {
397
448
  nextHandle.destroy();
398
449
  throw new Error("FormulaX editor mount cancelled");
399
450
  }
451
+ const readyMark = markFormulaXPerf("fx:kity-editor:ready");
452
+ measureFormulaXPerf("fx:kity-editor:ready", mountStart, readyMark);
453
+ clearFormulaXPerfMarks(readyMark);
400
454
  handle = nextHandle;
401
455
  return nextHandle;
402
456
  }).catch((error) => {
@@ -405,11 +459,13 @@ function mountFormulaXEditor(root, options = {}) {
405
459
  root.innerHTML = `
406
460
  <div class="fx-formula-editor-error">
407
461
  Failed to load FormulaX editor.
408
- <pre>${escapeHtml(error instanceof Error ? error.message : String(error))}</pre>
462
+ <pre>${(0, import_renderer.escapeHtml)(error instanceof Error ? error.message : String(error))}</pre>
409
463
  </div>
410
464
  `;
411
465
  }
412
466
  throw error;
467
+ }).finally(() => {
468
+ clearFormulaXPerfMarks(mountStart);
413
469
  });
414
470
  const getCurrentLatex = async () => {
415
471
  const readyHandle = handle ?? await readyPromise;
@@ -435,8 +491,8 @@ function mountFormulaXEditor(root, options = {}) {
435
491
  },
436
492
  async getRenderHtml() {
437
493
  await readyPromise;
438
- await waitForFormulaSvgLayout(root);
439
- return renderCurrentFormulaAsSvgHtml(root);
494
+ await (0, import_renderer_kity.waitForKityFormulaSvgLayout)(root);
495
+ return (0, import_renderer_kity.serializeKityFormulaFromRoot)(root);
440
496
  },
441
497
  destroy() {
442
498
  if (destroyed) return;
@@ -486,325 +542,18 @@ async function tryReadLatexFromKityHandle(handle) {
486
542
  }
487
543
  return null;
488
544
  }
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
545
  // Annotate the CommonJS export names for ESM import in node:
789
546
  0 && (module.exports = {
790
- DEFAULT_FORMULA_ATTRIBUTE,
791
- DEFAULT_FORMULA_CLASS,
792
- FORMULA_FLAG_ATTRIBUTE,
793
- FormulaXEditor,
794
- createFormulaElement,
795
- createFormulaMarkup,
796
- createKityEditor,
547
+ clearFormulaXPerfMarks,
797
548
  ensureFormulaXModalStyles,
798
- ensureKityRuntime,
799
- escapeAttribute,
800
- escapeHtml,
801
- findFormulaElement,
802
549
  formulaXModalStyles,
803
- getFormulaLatexFromElement,
804
- isFormulaElement,
550
+ markFormulaXPerf,
551
+ measureFormulaXPerf,
805
552
  mountFormulaXEditor,
806
- mountKityEditor,
807
- replaceFormulaElement,
808
- serializeSvgForInsertion
553
+ preloadFormulaXEditor,
554
+ recordFormulaXPerfPoint,
555
+ renderFormulaXEditorLoadingState,
556
+ scheduleFormulaXEditorPreload,
557
+ waitForFormulaXAnimationFrame
809
558
  });
810
559
  //# sourceMappingURL=index.cjs.map