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