erl-mathtextx-editor 0.3.2 → 0.3.6

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 (49) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/README.md +14 -1
  3. package/dist/{CellPropertiesDialogImpl-CgRJUUSC.js → CellPropertiesDialogImpl-NSTfG9SA.js} +1 -1
  4. package/dist/ContentViewer-LqSkiQjT.js +161 -0
  5. package/dist/ImageInsertDialog-DDxR_Cah.js +331 -0
  6. package/dist/{InsertTableDialogImpl-CWM11lNz.js → InsertTableDialogImpl-DrW9QTDd.js} +1 -1
  7. package/dist/{LinkDialogImpl-DrEasmBM.js → LinkDialogImpl-BXjhfEiE.js} +1 -1
  8. package/dist/MathTextXEditor.d.ts +1 -1
  9. package/dist/{TablePropertiesDialogImpl-BPcKuKCT.js → TablePropertiesDialogImpl-C2eqgZMa.js} +1 -1
  10. package/dist/{TableTemplatesDialogImpl-DYrlbe5p.js → TableTemplatesDialogImpl-B2DJBmzm.js} +1 -1
  11. package/dist/assets/erl-mathtextx-editor.css +1 -1
  12. package/dist/assets/viewer.css +1 -1
  13. package/dist/erl-mathtextx-editor.js +45 -27
  14. package/dist/erl-mathtextx-editor.umd.cjs +470 -200
  15. package/dist/export-deps-Tu2DJTcR.js +14815 -0
  16. package/dist/{index-DYrjnSa6.js → index-CvLezwrJ.js} +1 -1
  17. package/dist/{index-CHqs4QOj.js → index-D1pCAIcb.js} +6260 -4294
  18. package/dist/index-D7oo0rd0.js +14235 -0
  19. package/dist/index.d.ts +9 -1
  20. package/dist/index.es-UDhZ1Ncp.js +5632 -0
  21. package/dist/jszip.min-I9rjwvpP.js +2341 -0
  22. package/dist/math/MathInputModeContext.d.ts +7 -0
  23. package/dist/math/TemplatePanel.d.ts +4 -1
  24. package/dist/math/mathFieldFontStyles.d.ts +7 -0
  25. package/dist/math/mathFieldSpaceKey.d.ts +10 -0
  26. package/dist/{tiptap-K3rU-Wjn.js → tiptap-BiWGiMFj.js} +27 -27
  27. package/dist/toolbar/MainToolbar.d.ts +3 -1
  28. package/dist/toolbar/MathToolbar.d.ts +9 -2
  29. package/dist/types/index.d.ts +18 -5
  30. package/dist/utils/docxImporter.d.ts +1 -0
  31. package/dist/utils/docxOmmlPreprocess.d.ts +12 -0
  32. package/dist/utils/imageFileValidation.d.ts +14 -0
  33. package/dist/utils/isLatexContent.d.ts +5 -0
  34. package/dist/utils/mathMlToLatex.d.ts +4 -0
  35. package/dist/utils/microsoftMathPaste.d.ts +43 -0
  36. package/dist/utils/ommlToLatex.d.ts +11 -0
  37. package/dist/utils/reuploadImagesInHTML.d.ts +9 -0
  38. package/dist/utils/sanitizeConfig.d.ts +11 -0
  39. package/dist/utils/unicodeMathToLatex.d.ts +24 -0
  40. package/dist/{viewer-deps-BDYoL2Ts.js → viewer-deps-_HnWniSt.js} +5 -0
  41. package/dist/viewer.js +1 -1
  42. package/package.json +23 -10
  43. package/dist/ContentViewer-RijJ5nlJ.js +0 -45
  44. package/dist/ImageInsertDialog-DbUscLs1.js +0 -244
  45. package/dist/components/__tests__/WordCount.test.d.ts +0 -1
  46. package/dist/core/__tests__/serializer.test.d.ts +0 -1
  47. package/dist/index-CVlS1KL8.js +0 -16549
  48. package/dist/viewer-styles.d.ts +0 -1
  49. package/dist/viewer-styles.js +0 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,94 @@ All notable changes to `erl-mathtextx-editor` will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.3.6] - 2026-07-21
9
+
10
+ ### Added
11
+
12
+ - **Word-like Math Conversions** — Unicode / LaTeX / Text mode toggles + Convert on the math toolbar
13
+ - `mathInputMode` / `onMathInputModeChange` props (default `latex`, persisted in `localStorage`)
14
+ - Paste interpretation is gated by mode (avoids mixing Unicode and LaTeX auto-detect)
15
+ - **Convert → Ke rumus** / **Ke teks LaTeX** for selection or active math node
16
+ - `MathInputModeContext` / `useMathInputMode` for NodeViews (math-field paste)
17
+
18
+ ### Fixed
19
+
20
+ - **Word equation paste as image** — in **LaTeX** mode, when clipboard has OMML/MathML/LaTeX *and* an equation bitmap, prefer math node; strip Word equation preview images after math rewrite. In **Unicode** mode, Word equation snapshots paste as image
21
+
22
+ ### Changed
23
+
24
+ - **Pecahan & Akar tab** — expanded templates: fraction ops (+/−/×/÷), proportion, rationalization, nth roots, power–root forms, and more preset formulas (ABC, distance, Pythagoras)
25
+ - **Operator & Relasi tab** — added ⊆/⊇, ∄, ∝, ≪/≫, ∤/∣, ∖/∁, ⇒/⇔, gcd/lcm, inequality chains, and more logic/set symbols
26
+ - **Geometri & Trigonometri tab** — added cot/sec/csc, invers trig, sudut ganda & penjumlahan, luas/volume bangun, identitas trig, dan templat geometri (garis, busur, vektor)
27
+ - **Equation Editor modal UX** — equation-first layout (persamaan di atas, templat di bawah), collapsible “Templat & simbol” panel, compact format bar in equation header, floating tooltips
28
+ - **Arabic math text** — Adobe Naskh Medium font (`mt-font-arabic`), RTL direction, shadow-DOM injection for MathLive, KaTeX `\class` → `\htmlClass` in viewer
29
+ - **Equation editor space key** — Space still inserts `\,` in LaTeX; key-repeat and focus fixes prevent stuck typing
30
+ - **Image insert dialog UX** — choose file → preview + alt text → Sisipkan; primary action always visible; clearer empty-state guidance
31
+
32
+ ## [0.3.5] - 2026-07-21
33
+
34
+ ### Added
35
+
36
+ - **OMML Word paste** — `<m:oMath>` / `<m:oMathPara>` convert to LaTeX math nodes (`ommlToLatex`)
37
+ - **DOCX equation import** — OMML inside `.docx` is preprocessed to math spans before mammoth
38
+ - **Default paste image re-upload** — when `onImageUpload` is set (and `onBeforePasteHTML` is not), pasted images are re-uploaded automatically (`reuploadImagesInHTML`)
39
+ - **Wired config props** — `toolbar`, `mathToolbar`, `templates` (education level), and `outputFormat` (`html` | `json`) now apply at runtime
40
+ - **Public export helpers** — `exportToImage`, `exportToPDF`, `exportToJSON` exported from package root
41
+
42
+ ### Fixed
43
+
44
+ - **LaTeX control-word gluing** — Word Unicode paste no longer produces `\timesE` / `\partialB`; commands get a terminating space before letters (`\times E`, `\partial B`) so MathLive renders correctly (e.g. Faraday `∇×E=-∂B/∂t`)
45
+
46
+ ### Notes
47
+
48
+ - OMML coverage is best-effort for common education structures (frac, sqrt, scripts, n-ary, delimiters, matrix)
49
+ - `onBeforePasteHTML` still overrides the default image re-upload when provided
50
+
51
+ ## [0.3.4] - 2026-07-21
52
+
53
+ ### Added
54
+
55
+ - **Microsoft Word math paste** — Unicode math characters (𝑥, 𝑎, ², ∫, …) and LaTeX from Word convert to `mathInline` / `mathBlock`
56
+ - `unicodeMathToLatex` / `isUnicodeMathContent` for Mathematical Alphanumeric Symbols, super/subscripts, operators
57
+ - `resolveMicrosoftMathPaste` priority: `text/latex` → MathML → LaTeX plain → Unicode math
58
+ - MathML → LaTeX via MathLive (`mathMlToLatex`)
59
+ - Office HTML path rewrites `<math>` / TeX annotations to `mtx-math-*` spans before OMML tags are stripped
60
+ - Linear UnicodeMath build-up: `1/2√2` → `\frac{1}{2}\sqrt{2}`, `√(a+b)`, `(a+b)/(c+d)`, ∛/∜
61
+ - Strips Word invisible operators (U+2061–2064, ZWSP, etc.)
62
+ - Unicode math text runs inside Office HTML spans also convert to math nodes
63
+
64
+ ### Fixed
65
+
66
+ - Supplementary-plane math letters detection now uses the Unicode `u` flag (`\u{1D400}`)
67
+ - Whitespace preserved before fractions (`= 1/2√2` → `= \frac{1}{2}\sqrt{2}`)
68
+ - Parentheses no longer collapse to braces outside frac/sqrt (`sin(x)` stays `sin(x)`)
69
+
70
+ ### Notes
71
+
72
+ - Full OMML (`<m:oMath>`) conversion is not included yet; Word pastes that only expose OMML without Unicode/MathML/LaTeX may still lose structure
73
+ - Complex UnicodeMath linear build-up is best-effort (nested scripts / matrices still limited)
74
+
75
+ ## [0.3.3] - 2026-07-21
76
+
77
+ ### Fixed
78
+
79
+ - **Stale TipTap handlers** — `onChange`, `onSave`, `onImageUpload`, `onBeforePasteHTML`, drop/paste handlers now use refs so they stay current after re-renders
80
+ - **Reactive `editable` / `content`** — `editable` syncs via `setEditable`; external `content` updates apply without fighting local edits
81
+ - **Paste race** — async paste transforms use a generation counter so only the latest paste is inserted
82
+ - **Image object URL leaks** — revoke on upload failure, tab switch, and unmount
83
+ - **WebP validation** — magic-byte check requires RIFF + WEBP signature
84
+ - **Image URL insert** — only `http:` / `https:` schemes allowed
85
+ - **Drop/paste image validation** — same MIME/size/signature checks as the insert dialog
86
+ - **GraphComponent DOM accumulation** — clears previous plot before re-render
87
+ - **Image drag overlay** — removed on node unmount
88
+ - **Word count without spaces** — correct whitespace stripping
89
+ - **XSS allowlists** — ContentViewer, paste, and serializer share one sanitization config; `getHTML()` is sanitized on output
90
+
91
+ ### Added
92
+
93
+ - Shared helpers: `isLatexContent`, `validateImageFile`, `isSafeImageUrl`
94
+ - Unit tests for LaTeX detection, upload errors, safe URLs, and word/character counting
95
+
8
96
  ## [0.3.2] - 2026-07-07
9
97
 
10
98
  ### Added
package/README.md CHANGED
@@ -510,7 +510,20 @@ Klik kanan → Table Templates untuk memilih tema:
510
510
  | `erl-mathtextx-editor` | `dist/erl-mathtextx-editor.js` |
511
511
  | `erl-mathtextx-editor/styles` | `dist/assets/erl-mathtextx-editor.css` |
512
512
  | `erl-mathtextx-editor/viewer` | `dist/viewer.js` |
513
- | `erl-mathtextx-editor/viewer/styles` | `dist/viewer-styles.js` |
513
+ | `erl-mathtextx-editor/viewer/styles` | `dist/assets/viewer.css` |
514
+
515
+ ---
516
+
517
+ ## 📤 Publish (maintainers)
518
+
519
+ ```bash
520
+ cd packages/editor
521
+ npm run lint
522
+ npm run pack:check # dry-run tarball
523
+ npm publish # runs prepack → test + build
524
+ ```
525
+
526
+ Published version on npm is independent of the monorepo demo (`file:` link). After publish, bump consumers to the new semver.
514
527
 
515
528
  ---
516
529
 
@@ -1,6 +1,6 @@
1
1
  import { jsx as t, jsxs as l } from "react/jsx-runtime";
2
2
  import V, { useId as B, useRef as M, useCallback as b, useState as d, useEffect as A } from "react";
3
- import { u as $ } from "./index-CHqs4QOj.js";
3
+ import { u as $ } from "./index-D1pCAIcb.js";
4
4
  import { u as I } from "./useDraggable-DEoLIB5k.js";
5
5
  const P = V.memo(({
6
6
  isOpen: o,
@@ -0,0 +1,161 @@
1
+ import { jsx as o } from "react/jsx-runtime";
2
+ import { useRef as n, useMemo as s, useEffect as m } from "react";
3
+ import { p as i, k as l } from "./viewer-deps-_HnWniSt.js";
4
+ const d = [
5
+ "p",
6
+ "br",
7
+ "b",
8
+ "strong",
9
+ "i",
10
+ "em",
11
+ "u",
12
+ "s",
13
+ "sub",
14
+ "sup",
15
+ "h1",
16
+ "h2",
17
+ "h3",
18
+ "h4",
19
+ "h5",
20
+ "h6",
21
+ "ul",
22
+ "ol",
23
+ "li",
24
+ "table",
25
+ "thead",
26
+ "tbody",
27
+ "tfoot",
28
+ "tr",
29
+ "td",
30
+ "th",
31
+ "div",
32
+ "span",
33
+ "a",
34
+ "img",
35
+ "figure",
36
+ "figcaption",
37
+ "pre",
38
+ "code",
39
+ "blockquote",
40
+ "hr",
41
+ "wbr",
42
+ "col",
43
+ "colgroup",
44
+ "caption",
45
+ // MathLive / MathML (incoming content + viewer)
46
+ "math-field",
47
+ "math",
48
+ "mi",
49
+ "mo",
50
+ "mn",
51
+ "msup",
52
+ "msub",
53
+ "mrow",
54
+ "mfrac",
55
+ "msqrt",
56
+ "mroot",
57
+ "mover",
58
+ "munder",
59
+ "munderover",
60
+ "mspace",
61
+ "mstyle",
62
+ "mtext",
63
+ "menclose",
64
+ "mfenced",
65
+ "merror",
66
+ "mpadded",
67
+ "mphantom",
68
+ "mprescripts",
69
+ "none"
70
+ ], c = [
71
+ "href",
72
+ "target",
73
+ "rel",
74
+ "title",
75
+ "src",
76
+ "alt",
77
+ "width",
78
+ "height",
79
+ "class",
80
+ "id",
81
+ "style",
82
+ "align",
83
+ "data-align",
84
+ "data-theme",
85
+ "data-colwidth",
86
+ "data-rowheight",
87
+ "data-latex",
88
+ "data-type",
89
+ "data-display",
90
+ "data-offset-x",
91
+ "data-offset-y",
92
+ "data-table-width",
93
+ "data-indent",
94
+ "data-mathml",
95
+ "lang",
96
+ "dir",
97
+ "hidden",
98
+ "value",
99
+ "virtual-keyboard-mode",
100
+ "math-virtual-keyboard-policy",
101
+ "smart-mode",
102
+ "smart-fence",
103
+ "smart-superscript",
104
+ // Table attributes
105
+ "colspan",
106
+ "rowspan",
107
+ "border",
108
+ "valign",
109
+ "cellspacing",
110
+ "cellpadding",
111
+ "summary",
112
+ "scope",
113
+ "abbr",
114
+ "axis"
115
+ ], h = {
116
+ ALLOWED_TAGS: d,
117
+ ALLOWED_ATTR: c,
118
+ ALLOW_DATA_ATTR: !0
119
+ };
120
+ function u(t) {
121
+ if (t.querySelector(".katex")) return;
122
+ const e = t.getAttribute("data-latex") || t.getAttribute("latex");
123
+ if (!e) return;
124
+ const a = t.classList.contains("mtx-math-block");
125
+ try {
126
+ l.render(e, t, {
127
+ throwOnError: !1,
128
+ displayMode: a,
129
+ output: "htmlAndMathml",
130
+ trust: (r) => r.command === "\\htmlClass",
131
+ macros: {
132
+ "\\class": "\\htmlClass{#1}{#2}"
133
+ }
134
+ });
135
+ } catch {
136
+ t.textContent = a ? `$$${e}$$` : `$${e}$`;
137
+ }
138
+ }
139
+ function y({ content: t, className: e }) {
140
+ const a = n(null), r = s(
141
+ () => i.sanitize(t, { ...h }),
142
+ [t]
143
+ );
144
+ return m(() => {
145
+ if (!a.current) return;
146
+ a.current.querySelectorAll(
147
+ '.mtx-math-inline, .mtx-math-block, [data-type="math-inline"], [data-type="math-block"]'
148
+ ).forEach(u);
149
+ }, [r]), /* @__PURE__ */ o(
150
+ "div",
151
+ {
152
+ ref: a,
153
+ className: `mtx-content-viewer${e ? ` ${e}` : ""}`,
154
+ dangerouslySetInnerHTML: { __html: r }
155
+ }
156
+ );
157
+ }
158
+ export {
159
+ y as C,
160
+ h as S
161
+ };
@@ -0,0 +1,331 @@
1
+ import { jsxs as l, Fragment as le, jsx as a } from "react/jsx-runtime";
2
+ import { useId as _, useRef as $, useState as m, useCallback as t, useEffect as ie } from "react";
3
+ import { u as te, v as re, l as ne, i as V, U as oe } from "./index-D1pCAIcb.js";
4
+ function de(u) {
5
+ return u < 1024 ? `${u} B` : u < 1024 * 1024 ? `${(u / 1024).toFixed(1)} KB` : `${(u / (1024 * 1024)).toFixed(1)} MB`;
6
+ }
7
+ function ue({
8
+ isOpen: u,
9
+ onClose: B,
10
+ onInsert: v,
11
+ onImageUpload: k
12
+ }) {
13
+ const x = _(), j = _(), E = _(), G = _(), N = _(), S = $(null), [o, J] = m(k ? "upload" : "url"), [d, D] = m(""), [g, F] = m(""), [q, U] = m(!1), [h, b] = m(!1), [f, p] = m(null), [r, M] = m(null), [P, n] = m(""), [T, w] = m(null), L = $(null), s = $(null), W = t(() => {
14
+ s.current && (URL.revokeObjectURL(s.current), s.current = null), D(""), F(""), p(null), M(null), n(""), w(null), b(!1), U(!1);
15
+ }, []), c = t(() => {
16
+ W(), B();
17
+ }, [B, W]);
18
+ te({ isOpen: u, dialogRef: S, onClose: c }), ie(() => () => {
19
+ s.current && (URL.revokeObjectURL(s.current), s.current = null);
20
+ }, []);
21
+ const y = t(() => {
22
+ s.current && (URL.revokeObjectURL(s.current), s.current = null), p(null), M(null);
23
+ }, []), A = t((e) => {
24
+ J(e), n(""), w(null), y(), D("");
25
+ }, [y]), R = t(
26
+ async (e) => {
27
+ const i = await re(e);
28
+ if (!i.valid) {
29
+ n(i.error);
30
+ return;
31
+ }
32
+ n(""), w(null), y();
33
+ const C = URL.createObjectURL(e);
34
+ if (s.current = C, p(C), M(e), !g.trim()) {
35
+ const I = e.name.replace(/\.[^.]+$/, "");
36
+ F(I);
37
+ }
38
+ },
39
+ [g, y]
40
+ ), O = t(async () => {
41
+ if (!r) return;
42
+ if (k) {
43
+ b(!0);
44
+ try {
45
+ const i = await k(r);
46
+ if (!i)
47
+ throw new Error("Upload gagal: URL tidak valid");
48
+ v(i, g.trim() || r.name), c();
49
+ } catch (i) {
50
+ const C = i instanceof Error ? i.message : "Upload gagal", I = ne("Image insert upload failed", i);
51
+ n(`Upload gagal: ${C}`), w(I);
52
+ } finally {
53
+ b(!1);
54
+ }
55
+ return;
56
+ }
57
+ b(!0);
58
+ const e = new FileReader();
59
+ e.onload = () => {
60
+ const i = e.result;
61
+ v(i, g.trim() || r.name), c();
62
+ }, e.onerror = () => {
63
+ n("Gagal membaca file gambar"), b(!1);
64
+ }, e.readAsDataURL(r);
65
+ }, [r, k, v, g, c]), H = t(
66
+ (e) => {
67
+ const i = e.target.files?.[0];
68
+ i && R(i), e.target.value = "";
69
+ },
70
+ [R]
71
+ ), Q = t((e) => {
72
+ e.stopPropagation(), L.current?.click();
73
+ }, []), X = t((e) => {
74
+ e.preventDefault(), e.stopPropagation(), U(!0);
75
+ }, []), Y = t((e) => {
76
+ e.preventDefault(), e.stopPropagation(), U(!1);
77
+ }, []), Z = t(
78
+ (e) => {
79
+ e.preventDefault(), e.stopPropagation(), U(!1);
80
+ const i = e.dataTransfer.files?.[0];
81
+ i && R(i);
82
+ },
83
+ [R]
84
+ ), z = t(() => {
85
+ if (!d.trim()) {
86
+ n("Masukkan URL gambar");
87
+ return;
88
+ }
89
+ if (!V(d)) {
90
+ n("URL tidak valid. Gunakan http:// atau https://");
91
+ return;
92
+ }
93
+ v(d.trim(), g.trim() || void 0), c();
94
+ }, [d, g, v, c]), ee = t(() => {
95
+ if (!d.trim()) {
96
+ p(null);
97
+ return;
98
+ }
99
+ if (!V(d)) {
100
+ n("URL tidak valid. Gunakan http:// atau https://"), p(null);
101
+ return;
102
+ }
103
+ p(d.trim()), n("");
104
+ }, [d]), K = t(() => {
105
+ o === "upload" ? O() : z();
106
+ }, [o, O, z]), ae = o === "upload" ? !!r && !h : !!d.trim() && !h;
107
+ return u ? /* @__PURE__ */ l(le, { children: [
108
+ /* @__PURE__ */ a("div", { className: "mtx-dialog-overlay", onClick: c, "aria-hidden": "true" }),
109
+ /* @__PURE__ */ l(
110
+ "div",
111
+ {
112
+ className: "mtx-image-dialog",
113
+ ref: S,
114
+ role: "dialog",
115
+ "aria-modal": "true",
116
+ "aria-labelledby": x,
117
+ tabIndex: -1,
118
+ children: [
119
+ /* @__PURE__ */ l("div", { className: "mtx-image-dialog__header", children: [
120
+ /* @__PURE__ */ a("h3", { id: x, children: "Sisipkan Gambar" }),
121
+ /* @__PURE__ */ a(
122
+ "button",
123
+ {
124
+ type: "button",
125
+ className: "mtx-image-dialog__close",
126
+ onClick: c,
127
+ "aria-label": "Tutup",
128
+ children: /* @__PURE__ */ a("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ a("path", { d: "M3 3l10 10M13 3L3 13", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round" }) })
129
+ }
130
+ )
131
+ ] }),
132
+ /* @__PURE__ */ l("div", { className: "mtx-image-dialog__tabs", role: "tablist", "aria-label": "Metode sisip gambar", children: [
133
+ /* @__PURE__ */ l(
134
+ "button",
135
+ {
136
+ type: "button",
137
+ id: j,
138
+ role: "tab",
139
+ "aria-selected": o === "upload",
140
+ "aria-controls": G,
141
+ className: `mtx-image-dialog__tab ${o === "upload" ? "is-active" : ""}`,
142
+ onClick: () => A("upload"),
143
+ children: [
144
+ /* @__PURE__ */ l("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: [
145
+ /* @__PURE__ */ a("path", { d: "M8 10V2M8 2L5 5M8 2l3 3", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }),
146
+ /* @__PURE__ */ a("path", { d: "M2 12v1.5A1.5 1.5 0 003.5 15h9a1.5 1.5 0 001.5-1.5V12", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
147
+ ] }),
148
+ "Upload File"
149
+ ]
150
+ }
151
+ ),
152
+ /* @__PURE__ */ l(
153
+ "button",
154
+ {
155
+ type: "button",
156
+ id: E,
157
+ role: "tab",
158
+ "aria-selected": o === "url",
159
+ "aria-controls": N,
160
+ className: `mtx-image-dialog__tab ${o === "url" ? "is-active" : ""}`,
161
+ onClick: () => A("url"),
162
+ children: [
163
+ /* @__PURE__ */ l("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: [
164
+ /* @__PURE__ */ a("path", { d: "M6.5 9.5a3 3 0 004.24 0l2-2a3 3 0 00-4.24-4.24l-.88.88", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }),
165
+ /* @__PURE__ */ a("path", { d: "M9.5 6.5a3 3 0 00-4.24 0l-2 2a3 3 0 004.24 4.24l.88-.88", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
166
+ ] }),
167
+ "URL"
168
+ ]
169
+ }
170
+ )
171
+ ] }),
172
+ /* @__PURE__ */ l("div", { className: "mtx-image-dialog__body", children: [
173
+ o === "upload" && /* @__PURE__ */ a(
174
+ "div",
175
+ {
176
+ id: G,
177
+ role: "tabpanel",
178
+ "aria-labelledby": j,
179
+ className: "mtx-image-dialog__panel",
180
+ children: /* @__PURE__ */ l(
181
+ "div",
182
+ {
183
+ className: `mtx-image-dialog__dropzone ${q ? "is-dragging" : ""} ${f ? "has-preview" : ""}`,
184
+ onDragOver: X,
185
+ onDragLeave: Y,
186
+ onDrop: Z,
187
+ onClick: () => !h && L.current?.click(),
188
+ onKeyDown: (e) => {
189
+ (e.key === "Enter" || e.key === " ") && (e.preventDefault(), h || L.current?.click());
190
+ },
191
+ role: "button",
192
+ tabIndex: 0,
193
+ "aria-label": "Pilih file gambar atau seret dan lepas di sini",
194
+ children: [
195
+ /* @__PURE__ */ a(
196
+ "input",
197
+ {
198
+ ref: L,
199
+ type: "file",
200
+ accept: "image/jpeg,image/png,image/gif,image/webp",
201
+ onChange: H,
202
+ className: "mtx-image-dialog__file-input",
203
+ "aria-hidden": "true",
204
+ tabIndex: -1
205
+ }
206
+ ),
207
+ h ? /* @__PURE__ */ l("div", { className: "mtx-image-dialog__uploading", children: [
208
+ /* @__PURE__ */ a("div", { className: "mtx-image-dialog__spinner", "aria-hidden": "true" }),
209
+ /* @__PURE__ */ a("p", { children: "Mengupload gambar..." })
210
+ ] }) : f && r ? /* @__PURE__ */ l("div", { className: "mtx-image-dialog__preview-card", children: [
211
+ /* @__PURE__ */ a("img", { src: f, alt: "", className: "mtx-image-dialog__preview-img" }),
212
+ /* @__PURE__ */ l("div", { className: "mtx-image-dialog__preview-meta", children: [
213
+ /* @__PURE__ */ a("span", { className: "mtx-image-dialog__preview-name", title: r.name, children: r.name }),
214
+ /* @__PURE__ */ a("span", { className: "mtx-image-dialog__preview-size", children: de(r.size) })
215
+ ] }),
216
+ /* @__PURE__ */ a(
217
+ "button",
218
+ {
219
+ type: "button",
220
+ className: "mtx-image-dialog__change-file",
221
+ onClick: Q,
222
+ children: "Ganti file"
223
+ }
224
+ )
225
+ ] }) : /* @__PURE__ */ l("div", { className: "mtx-image-dialog__placeholder", children: [
226
+ /* @__PURE__ */ a("div", { className: "mtx-image-dialog__placeholder-icon", "aria-hidden": "true", children: /* @__PURE__ */ l("svg", { width: "40", height: "40", viewBox: "0 0 48 48", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
227
+ /* @__PURE__ */ a("rect", { x: "4", y: "8", width: "40", height: "32", rx: "4" }),
228
+ /* @__PURE__ */ a("circle", { cx: "16", cy: "20", r: "4" }),
229
+ /* @__PURE__ */ a("path", { d: "M4 36l10-10 6 6 8-10 16 14" })
230
+ ] }) }),
231
+ /* @__PURE__ */ a("p", { className: "mtx-image-dialog__placeholder-title", children: "Klik untuk pilih file atau drag & drop" }),
232
+ /* @__PURE__ */ a("p", { className: "mtx-image-dialog__placeholder-hint", children: "JPG, PNG, GIF, WebP — maks. 10 MB" })
233
+ ] })
234
+ ]
235
+ }
236
+ )
237
+ }
238
+ ),
239
+ o === "url" && /* @__PURE__ */ l(
240
+ "div",
241
+ {
242
+ id: N,
243
+ role: "tabpanel",
244
+ "aria-labelledby": E,
245
+ className: "mtx-image-dialog__panel",
246
+ children: [
247
+ /* @__PURE__ */ l("div", { className: "mtx-image-dialog__url-form", children: [
248
+ /* @__PURE__ */ a("label", { htmlFor: `${N}-url`, children: "URL Gambar" }),
249
+ /* @__PURE__ */ a(
250
+ "input",
251
+ {
252
+ id: `${N}-url`,
253
+ type: "url",
254
+ value: d,
255
+ onChange: (e) => {
256
+ D(e.target.value), P && n("");
257
+ },
258
+ placeholder: "https://example.com/gambar.jpg",
259
+ onKeyDown: (e) => {
260
+ e.key === "Enter" && K();
261
+ },
262
+ onBlur: ee,
263
+ autoFocus: !0
264
+ }
265
+ )
266
+ ] }),
267
+ f && o === "url" && /* @__PURE__ */ a("div", { className: "mtx-image-dialog__url-preview", children: /* @__PURE__ */ a(
268
+ "img",
269
+ {
270
+ src: f,
271
+ alt: "Pratinjau gambar dari URL",
272
+ onError: () => {
273
+ p(null), n("Gambar tidak dapat dimuat dari URL ini");
274
+ }
275
+ }
276
+ ) })
277
+ ]
278
+ }
279
+ ),
280
+ /* @__PURE__ */ l("div", { className: "mtx-image-dialog__alt", children: [
281
+ /* @__PURE__ */ a("label", { htmlFor: `${x}-alt`, children: "Teks alternatif" }),
282
+ /* @__PURE__ */ a(
283
+ "input",
284
+ {
285
+ id: `${x}-alt`,
286
+ type: "text",
287
+ value: g,
288
+ onChange: (e) => F(e.target.value),
289
+ placeholder: "Deskripsi gambar untuk aksesibilitas (opsional)"
290
+ }
291
+ ),
292
+ /* @__PURE__ */ a("p", { className: "mtx-image-dialog__alt-hint", children: "Membantu pembaca layar memahami isi gambar." })
293
+ ] }),
294
+ P && /* @__PURE__ */ l("div", { className: "mtx-image-dialog__error", role: "alert", children: [
295
+ /* @__PURE__ */ a("div", { children: P }),
296
+ T && /* @__PURE__ */ a(oe, { debug: T })
297
+ ] })
298
+ ] }),
299
+ /* @__PURE__ */ l("div", { className: "mtx-image-dialog__footer", children: [
300
+ /* @__PURE__ */ a("p", { className: "mtx-image-dialog__footer-hint", "aria-live": "polite", children: o === "upload" && !r && !h ? "Pilih file terlebih dahulu, lalu klik Sisipkan." : "" }),
301
+ /* @__PURE__ */ l("div", { className: "mtx-image-dialog__actions", children: [
302
+ /* @__PURE__ */ a(
303
+ "button",
304
+ {
305
+ type: "button",
306
+ className: "mtx-image-dialog__btn mtx-image-dialog__btn--cancel",
307
+ onClick: c,
308
+ disabled: h,
309
+ children: "Batal"
310
+ }
311
+ ),
312
+ /* @__PURE__ */ a(
313
+ "button",
314
+ {
315
+ type: "button",
316
+ className: "mtx-image-dialog__btn mtx-image-dialog__btn--insert",
317
+ onClick: K,
318
+ disabled: !ae,
319
+ children: h ? "Mengupload..." : "Sisipkan"
320
+ }
321
+ )
322
+ ] })
323
+ ] })
324
+ ]
325
+ }
326
+ )
327
+ ] }) : null;
328
+ }
329
+ export {
330
+ ue as ImageInsertDialog
331
+ };
@@ -1,6 +1,6 @@
1
1
  import { jsx as e, jsxs as t } from "react/jsx-runtime";
2
2
  import v, { useId as g, useRef as y, useState as c, useEffect as k, useCallback as w } from "react";
3
- import { u as C } from "./index-CHqs4QOj.js";
3
+ import { u as C } from "./index-D1pCAIcb.js";
4
4
  import { u as M } from "./useDraggable-DEoLIB5k.js";
5
5
  const I = v.memo(({ isOpen: i, onInsert: d, onClose: m }) => {
6
6
  const u = g(), b = y(null), [l, n] = c(3), [a, s] = c(3), [o, h] = c(!0), { position: p, handleMouseDown: N } = M(i);
@@ -1,6 +1,6 @@
1
1
  import { jsx as a, jsxs as e } from "react/jsx-runtime";
2
2
  import S, { useId as T, useRef as _, useState as r, useEffect as w, useCallback as c } from "react";
3
- import { u as F } from "./index-CHqs4QOj.js";
3
+ import { u as F } from "./index-D1pCAIcb.js";
4
4
  const I = S.memo(({
5
5
  isOpen: s,
6
6
  currentUrl: o = "",
@@ -5,4 +5,4 @@ import { MathTextXEditorProps } from './types';
5
5
  * Drop-in replacement for CKEditor in solutest.id.
6
6
  * Supports rich text formatting and visual math input without LaTeX.
7
7
  */
8
- export declare function MathTextXEditor({ content, onChange, onSave, editable, placeholder, toolbarMode, toolbar: _toolbar, mathToolbar: _mathToolbar, templates: _templates, outputFormat: _outputFormat, className, minHeight, maxHeight, autoFocus, onImageUpload, onBeforePasteHTML, tableStyle, }: MathTextXEditorProps): import("react/jsx-runtime").JSX.Element;
8
+ export declare function MathTextXEditor({ content, onChange, onSave, editable, placeholder, toolbarMode, toolbar, mathToolbar, templates, outputFormat, mathInputMode: mathInputModeProp, onMathInputModeChange, className, minHeight, maxHeight, autoFocus, onImageUpload, onBeforePasteHTML, tableStyle, }: MathTextXEditorProps): import("react/jsx-runtime").JSX.Element;
@@ -1,6 +1,6 @@
1
1
  import { jsx as t, jsxs as l } from "react/jsx-runtime";
2
2
  import g, { useId as x, useRef as p, useState as f, useEffect as y, useCallback as h } from "react";
3
- import { u as N } from "./index-CHqs4QOj.js";
3
+ import { u as N } from "./index-D1pCAIcb.js";
4
4
  import { u as k } from "./useDraggable-DEoLIB5k.js";
5
5
  const z = g.memo(({
6
6
  isOpen: a,
@@ -1,6 +1,6 @@
1
1
  import { jsx as e, jsxs as l } from "react/jsx-runtime";
2
2
  import c, { useId as p, useRef as h, useCallback as x } from "react";
3
- import { u as b } from "./index-CHqs4QOj.js";
3
+ import { u as b } from "./index-D1pCAIcb.js";
4
4
  import { u } from "./useDraggable-DEoLIB5k.js";
5
5
  const g = [
6
6
  { id: "plain", name: "Polos", description: "Tabel sederhana tanpa gaya tambahan", headerStyle: "none", borderStyle: "all", stripeRows: !1 },