erl-mathtextx-editor 0.1.8 → 0.1.10

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 (28) hide show
  1. package/CHANGELOG.md +157 -0
  2. package/README.md +262 -107
  3. package/dist/{CellPropertiesDialogImpl-DBgs-7H9.js → CellPropertiesDialogImpl-Cl0pxbeQ.js} +1 -1
  4. package/dist/{ContentViewer-CsFSAN_B.js → ContentViewer-RijJ5nlJ.js} +15 -14
  5. package/dist/{ImageInsertDialog-B24KHrgt.js → ImageInsertDialog-BVBl1y36.js} +27 -25
  6. package/dist/{InsertTableDialogImpl-B6_PRu5m.js → InsertTableDialogImpl-Cx3ShX7u.js} +1 -1
  7. package/dist/{LinkDialogImpl-BTA8u_qQ.js → LinkDialogImpl-gMjoZVma.js} +1 -1
  8. package/dist/MathTextXEditor.d.ts +1 -1
  9. package/dist/{TablePropertiesDialogImpl-CuRRWS4H.js → TablePropertiesDialogImpl-CrTTV3Zr.js} +1 -1
  10. package/dist/{TableTemplatesDialogImpl-CU8seEdV.js → TableTemplatesDialogImpl-DTcom8H5.js} +2 -2
  11. package/dist/assets/erl-mathtextx-editor.css +1 -1
  12. package/dist/assets/viewer.css +1 -1
  13. package/dist/components/ErrorBoundary.d.ts +18 -0
  14. package/dist/components/ImageEditDialog.d.ts +0 -1
  15. package/dist/components/TableMenu.d.ts +4 -1
  16. package/dist/erl-mathtextx-editor.js +2 -2
  17. package/dist/erl-mathtextx-editor.umd.cjs +345 -115
  18. package/dist/{index-UCSefQk0.js → index-CakccgVO.js} +3801 -3201
  19. package/dist/{index-CB1g0gXh.js → index-Djb9MY7m.js} +1 -1
  20. package/dist/index-QMz8TDH0.js +16549 -0
  21. package/dist/toolbar/MainToolbar.d.ts +1 -0
  22. package/dist/types/index.d.ts +2 -0
  23. package/dist/utils/docxImporter.d.ts +31 -0
  24. package/dist/utils/pasteHandler.d.ts +14 -3
  25. package/dist/{viewer-deps-CjbAqdti.js → viewer-deps-BDYoL2Ts.js} +5794 -3489
  26. package/dist/viewer.js +1 -1
  27. package/package.json +6 -2
  28. package/dist/extensions/TableAlignPlugin.d.ts +0 -7
package/CHANGELOG.md ADDED
@@ -0,0 +1,157 @@
1
+ # Changelog
2
+
3
+ All notable changes to `erl-mathtextx-editor` will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.9] - 2026-04-01
9
+
10
+ ### Fixed
11
+ - **README Not Showing on NPM** — Added `README.md`, `CHANGELOG.md`, and `LICENSE` to `files` array in package.json
12
+ - **TypeScript Errors** — Added proper type annotations to `CustomTableView` class parameters
13
+
14
+ ### Changed
15
+ - **Package Files** — Expanded `files` field from `["dist"]` to `["dist", "README.md", "CHANGELOG.md", "LICENSE"]`
16
+ - **Version Bump** — 0.1.8 → 0.1.9 (previous version had missing README on npm)
17
+
18
+ ---
19
+
20
+ ## [0.1.8] - 2026-04-01
21
+
22
+ ### Fixed
23
+ - **Documentation Consistency** — Updated all .md files to reflect current version
24
+ - **CONTRIBUTING.md** — Fixed duplicate "Development Workflow" section and numbering issues
25
+
26
+ ### Changed
27
+ - **Version Bump** — 0.1.7 → 0.1.8 (previous version already published)
28
+ - **Documentation Sync** — All markdown files now reference v0.1.8 consistently
29
+
30
+ ---
31
+
32
+ ## [0.1.7] - 2026-03-31
33
+
34
+ ### Added
35
+ - **Code Block Syntax Highlighting** — Added `@tiptap/extension-code-block-lowlight` with lowlight for syntax highlighting in code blocks
36
+ - Supports 100+ programming languages
37
+ - Dark theme with syntax colors (keywords, strings, numbers, comments, functions, etc.)
38
+ - Works in both editor and viewer
39
+ - **Table Column Resize** — TipTap built-in `resizable: true` for column width adjustment
40
+ - **Cell Selection Highlight** — Light blue background with dashed border when selecting multiple cells for merge
41
+ - **Collapsible Details/Summary** — CSS styles ready for TipTap v3 details extension
42
+
43
+ ### Fixed
44
+ - **Table Selection Visibility** — Fixed `.selectedCell` CSS selector to work with TipTap's default table structure
45
+ - **DOM Nesting Warning** — Removed invalid `<div>` inside `<tbody>` structure
46
+
47
+ ### Changed
48
+ - **Removed Duplicate Dependencies** — Removed 16 packages already included in `@tiptap/starter-kit`:
49
+ - `@tiptap/extension-bold`, `bullet-list`, `code-block`, `document`
50
+ - `@tiptap/extension-dropcursor`, `gapcursor`, `hard-break`, `heading`
51
+ - `@tiptap/extension-history`, `horizontal-rule`, `italic`, `list-item`
52
+ - `@tiptap/extension-ordered-list`, `paragraph`, `strike`, `text`
53
+ - **Code Organization** — Extracted inline extensions to separate files:
54
+ - `IndentBackspace` → `src/extensions/IndentBackspace.ts`
55
+ - `SlashGraph` → `src/extensions/SlashGraph.ts`
56
+
57
+ ### Internal
58
+ - Cleaner dependency tree with reduced bundle overhead
59
+ - Better code maintainability with separated extension files
60
+
61
+ ## [0.1.6] - 2026-03-31
62
+
63
+ ### Fixed
64
+ - **Backspace Join Behavior** — Backspace at start of paragraph now properly joins with previous paragraph when no indentation exists
65
+ - **Tab Key Dual Behavior** — Tab now inserts 4 spaces when cursor is in the middle of a paragraph, and indents paragraph when cursor is at the start
66
+ - **Tab Character Rendering** — Added `white-space: pre-wrap` to editor and viewer CSS for consistent whitespace display
67
+ - **Indent Extension Conflict** — Created CustomIndent extension to override base package Tab keyboard shortcuts that were conflicting with custom handlers
68
+
69
+ ### Added
70
+ - **CustomIndent Extension** — New extension that extends `@weiruo/tiptap-extension-indent` and removes its Tab keyboard shortcuts to prevent conflicts
71
+ - **Improved IndentBackspace** — Now checks actual indent level before attempting outdent, allowing default joinBackward behavior when no indentation exists
72
+
73
+ ### Changed
74
+ - Enhanced Tab key behavior for better UX (similar to Notion/modern editors)
75
+ - Improved paragraph joining behavior to match expected editor behavior
76
+
77
+ ## [0.1.5] - 2026-03-26
78
+
79
+ ### Added
80
+ - **Custom CSS Tooltips** - Replace native browser tooltips
81
+ - Instant appearance (no delay)
82
+ - Dark-themed with smooth fade-in animation
83
+ - Arrow indicator pointing to button
84
+ - Consistent styling across all browsers
85
+ - **Tactile Button Feedback** - Physical feedback on click
86
+ - Toolbar buttons: scale(0.96) on click
87
+ - Math buttons: scale(0.96) on click
88
+ - Dialog buttons: scale(0.98) on click
89
+ - Provides satisfying micro-interaction
90
+
91
+ ### Changed
92
+ - Use `data-tooltip` attribute instead of native `title`
93
+ - Enhanced button `:active` states across all components
94
+
95
+ ## [0.1.4] - 2026-03-26
96
+
97
+ ### Added
98
+ - **Export MathTypeDialog** - Now available for standalone import
99
+ - `import { MathTypeDialog } from 'erl-mathtextx-editor'`
100
+ - Export both component and `MathTypeDialogProps` type
101
+ - **Export TemplatePanel** - Now available for standalone import
102
+ - `import { TemplatePanel } from 'erl-mathtextx-editor'`
103
+ - Export both component and `TemplatePanelProps` type
104
+
105
+ ### Changed
106
+ - **Package naming consistency** - All documentation now uses `erl-mathtextx-editor` (not `@mathtextx/editor`)
107
+ - **Static imports** - MathTypeDialog and TemplatePanel now statically imported in MathTextXEditor.tsx
108
+ - **Removed Suspense wrappers** - No longer needed for static imports
109
+ - **Build warnings fixed** - Eliminated dynamic/static import conflict warnings
110
+
111
+ ### Fixed
112
+ - Build warnings about dual import patterns
113
+ - Documentation inconsistencies across README, docs, and integration guides
114
+
115
+ ### Internal
116
+ - ImageInsertDialog remains internal (not exported)
117
+ - Optimized bundle structure
118
+
119
+ ## [0.1.3] - 2026-03-19
120
+
121
+ ### Published
122
+ - Initial release to npm registry
123
+ - Package: `erl-mathtextx-editor@0.1.3`
124
+
125
+ ## [0.1.0] - 2026-03-03
126
+
127
+ ### Added
128
+
129
+ - **MathTextXEditor** — React component utama untuk rich text + math editing
130
+ - TipTap/ProseMirror editor engine
131
+ - MathLive visual math input (inline & block)
132
+ - Toolbar: bold, italic, underline, strike, heading, lists, table, image, link, code, blockquote
133
+ - Math toolbar: fraction, sqrt, power, subscript, sum, integral, limit, matrix, symbols
134
+ - Symbol palette dengan 100+ simbol matematika
135
+ - Graph extension (function plotting via Function Plot)
136
+ - Keyboard shortcut: Ctrl+S untuk save
137
+ - Slash command: `/graph` untuk insert grafik
138
+
139
+ - **ContentViewer** — Komponen read-only untuk menampilkan konten di frontend quiz/ujian
140
+ - XSS sanitization via DOMPurify
141
+ - Auto-render rumus matematika via KaTeX
142
+ - Standalone CSS (`content-style.css`) untuk styling konsisten
143
+
144
+ - **Template Library** — 60+ preset rumus matematika
145
+ - Kategori: Algebra, Geometri, Trigonometri, Kalkulus, Statistik, Fisika, Kimia
146
+ - Filter by level: SD, SMP, SMA
147
+ - Utility functions: `getTemplatesByLevel()`, `getTemplatesByCategory()`, `getTemplateCategories()`
148
+
149
+ - **Serializer Utilities**
150
+ - `getHTML()` — export konten sebagai HTML
151
+ - `toCompatibleHTML()` — konversi ke format CKEditor-compatible
152
+ - `sanitizeCKEditorHTML()` — bersihkan HTML dari CKEditor
153
+
154
+ - **Build Output**
155
+ - ES Module (`mathtextx-editor.js`)
156
+ - UMD (`mathtextx-editor.umd.cjs`)
157
+ - TypeScript declarations (`.d.ts`)
package/README.md CHANGED
@@ -13,16 +13,15 @@ Embeddable visual math editor widget untuk CMS dan platform edukasi. User tidak
13
13
 
14
14
  - 🎯 **Visual Math Keyboard** — Klik simbol, operator, template formula
15
15
  - 📝 **Rich Text Editor** — Bold, italic, tables, lists, links, images
16
- - 🧮 **100+ Formula Templates** — Algebra, calculus, trigonometry, chemistry
17
- - 👁️ **Content Viewer** — Read-only renderer dengan KaTeX
18
- - 🎨 **Table Editor** — 6 professional table templates, column resize, cell selection
19
- - 🖥️ **Code Blocks** — Syntax highlighting untuk berbagai bahasa pemrograman
20
- - 📋 **Collapsible Sections** — Details/summary untuk konten tersembunyi
21
- - ⌨️ **Keyboard Shortcuts** — Ctrl+K (link), Ctrl+Shift+T (table), Ctrl+S (save)
22
- - 📊 **Graph Plotting** — Function plotting via Function Plot
23
- - 🔒 **XSS Protection** — DOMPurify sanitization
24
- - 💻 **Code Blocks** — Syntax highlighting with 100+ languages
25
- - 📋 **Collapsible Sections** — Details/summary for hidden content
16
+ - 🧮 **Equation Editor** — Dialog MathType-style dengan tab, grid, KaTeX preview
17
+ - 📋 **100+ Formula Templates** — Algebra, calculus, trigonometry, chemistry, matrix
18
+ - 🖼️ **Free-form Image Drag** — Drag gambar ke posisi bebas (pixel-perfect)
19
+ - 📄 **DOCX Import** — Import file .docx via mammoth.js (toolbar + drag-drop)
20
+ - 🗂️ **Google Docs Paste** — Paste dari Google Docs (equations + document) auto-cleaned
21
+ - 👁️ **Content Viewer** — Read-only renderer dengan KaTeX + DOMPurify
22
+ - 🎨 **Table Editor** — 6 templates, column resize, cell merge/split
23
+ - 🔒 **XSS Protection** — DOMPurify sanitization di paste + serializer
24
+ - 🛡️ **Error Boundary** — Anti white-screen crash protection
26
25
 
27
26
  ---
28
27
 
@@ -36,7 +35,7 @@ npm install erl-mathtextx-editor
36
35
 
37
36
  ## 🚀 Quick Start
38
37
 
39
- ### Basic Usage
38
+ ### 1. Basic Editor
40
39
 
41
40
  ```tsx
42
41
  import { MathTextXEditor } from 'erl-mathtextx-editor'
@@ -52,14 +51,19 @@ function App() {
52
51
  }
53
52
  ```
54
53
 
55
- ### With Save Handler
54
+ ### 2. Editor dengan Save Handler
56
55
 
57
56
  ```tsx
58
- import { MathTextXEditor } from 'erl-mathtextx-editor'
57
+ import { useRef } from 'react'
58
+ import { MathTextXEditor, getHTML } from 'erl-mathtextx-editor'
59
59
  import 'erl-mathtextx-editor/styles'
60
60
 
61
61
  function QuestionForm() {
62
- const handleSave = (html) => {
62
+ const editorRef = useRef<HTMLDivElement>(null)
63
+
64
+ const handleSave = () => {
65
+ if (!editorRef.current) return
66
+ const html = getHTML(editorRef.current)
63
67
  fetch('/api/questions', {
64
68
  method: 'POST',
65
69
  headers: { 'Content-Type': 'application/json' },
@@ -67,32 +71,210 @@ function QuestionForm() {
67
71
  })
68
72
  }
69
73
 
74
+ return (
75
+ <div>
76
+ <MathTextXEditor ref={editorRef} placeholder="Tulis pertanyaan..." onSave={handleSave} />
77
+ <button onClick={handleSave}>Simpan</button>
78
+ </div>
79
+ )
80
+ }
81
+ ```
82
+
83
+ ### 3. Edit Existing Content
84
+
85
+ ```tsx
86
+ import { MathTextXEditor } from 'erl-mathtextx-editor'
87
+ import 'erl-mathtextx-editor/styles'
88
+
89
+ function EditQuestion({ existingHtml }: { existingHtml: string }) {
70
90
  return (
71
91
  <MathTextXEditor
72
- onChange={(html) => console.log(html)}
73
- onSave={handleSave}
74
- placeholder="Tulis pertanyaan..."
92
+ content={existingHtml}
93
+ onChange={(html) => console.log('Updated:', html)}
75
94
  />
76
95
  )
77
96
  }
78
97
  ```
79
98
 
80
- ### Content Viewer (Read-Only)
99
+ ### 4. Read-Only Mode (Viewer)
81
100
 
82
101
  ```tsx
83
102
  import { ContentViewer } from 'erl-mathtextx-editor/viewer'
84
103
  import 'erl-mathtextx-editor/viewer/styles'
85
104
 
86
- function QuestionCard({ questionHtml }) {
105
+ function ExamQuestion({ questionHtml }: { questionHtml: string }) {
106
+ return <ContentViewer content={questionHtml} />
107
+ }
108
+ ```
109
+
110
+ ### 5. Multi-Instance (Soal + Pilihan Jawaban)
111
+
112
+ ```tsx
113
+ import { useState } from 'react'
114
+ import { MathTextXEditor } from 'erl-mathtextx-editor'
115
+ import 'erl-mathtextx-editor/styles'
116
+
117
+ function MultipleChoiceForm() {
118
+ const [question, setQuestion] = useState('')
119
+ const [options, setOptions] = useState(
120
+ ['A', 'B', 'C', 'D'].map((id) => ({ id, content: '' }))
121
+ )
122
+
87
123
  return (
88
- <div className="question-card">
89
- <h3>Soal 1</h3>
90
- <ContentViewer content={questionHtml} />
124
+ <div>
125
+ <label>Pertanyaan:</label>
126
+ <MathTextXEditor
127
+ content={question}
128
+ onChange={setQuestion}
129
+ placeholder="Tulis pertanyaan..."
130
+ minHeight="150px"
131
+ />
132
+ {options.map((opt) => (
133
+ <div key={opt.id}>
134
+ <label>Opsi {opt.id}:</label>
135
+ <MathTextXEditor
136
+ content={opt.content}
137
+ onChange={(html) => setOptions((prev) => prev.map((o) => o.id === opt.id ? { ...o, content: html } : o))}
138
+ placeholder={`Jawaban ${opt.id}...`}
139
+ minHeight="60px"
140
+ />
141
+ </div>
142
+ ))}
91
143
  </div>
92
144
  )
93
145
  }
94
146
  ```
95
147
 
148
+ ### 6. Next.js (App Router)
149
+
150
+ ```tsx
151
+ 'use client'
152
+ import dynamic from 'next/dynamic'
153
+ import 'erl-mathtextx-editor/styles'
154
+
155
+ const MathTextXEditor = dynamic(
156
+ () => import('erl-mathtextx-editor').then((mod) => mod.MathTextXEditor),
157
+ { ssr: false }
158
+ )
159
+
160
+ export default function EditorPage() {
161
+ return (
162
+ <MathTextXEditor
163
+ placeholder="Tulis soal matematika..."
164
+ onChange={(html) => console.log(html)}
165
+ minHeight="300px"
166
+ />
167
+ )
168
+ }
169
+ ```
170
+
171
+ ### 7. Vite + React
172
+
173
+ ```tsx
174
+ import { MathTextXEditor } from 'erl-mathtextx-editor'
175
+ import 'erl-mathtextx-editor/styles'
176
+
177
+ function App() {
178
+ return (
179
+ <MathTextXEditor
180
+ placeholder="Tulis soal..."
181
+ onChange={(html) => console.log(html)}
182
+ />
183
+ )
184
+ }
185
+ export default App
186
+ ```
187
+
188
+ ---
189
+
190
+ ## 🧰 Image Upload
191
+
192
+ Editor mendukung upload gambar dari: **Insert dialog**, **drag-drop**, **paste dari clipboard**, dan **DOCX import**. Semuanya melalui satu callback `onImageUpload`.
193
+
194
+ ### Basic Upload
195
+
196
+ ```tsx
197
+ import { MathTextXEditor } from 'erl-mathtextx-editor'
198
+ import 'erl-mathtextx-editor/styles'
199
+
200
+ function EditorWithUpload() {
201
+ const handleImageUpload = async (file: File): Promise<string> => {
202
+ const formData = new FormData()
203
+ formData.append('image', file)
204
+ const res = await fetch('/api/upload', { method: 'POST', body: formData })
205
+ if (!res.ok) throw new Error('Upload failed: ' + res.statusText)
206
+ const data = await res.json()
207
+ return data.url // Expected: { "url": "https://cdn.example.com/img.jpg" }
208
+ }
209
+
210
+ return (
211
+ <MathTextXEditor
212
+ placeholder="Tulis soal..."
213
+ onImageUpload={handleImageUpload}
214
+ />
215
+ )
216
+ }
217
+ ```
218
+
219
+ ### Re-upload Gambar dari Paste (Google Docs / Website)
220
+
221
+ ```tsx
222
+ import { MathTextXEditor } from 'erl-mathtextx-editor'
223
+ import 'erl-mathtextx-editor/styles'
224
+
225
+ function EditorWithPasteReupload() {
226
+ const handleImageUpload = async (file: File): Promise<string> => {
227
+ const formData = new FormData()
228
+ formData.append('image', file)
229
+ const res = await fetch('/api/upload', { method: 'POST', body: formData })
230
+ return (await res.json()).url
231
+ }
232
+
233
+ const handleBeforePasteHTML = async (html: string): Promise<string> => {
234
+ // Download + re-upload external images from pasted HTML
235
+ const imgRegex = /<img\s+[^>]*src="([^"]+)"[^>]*>/gi
236
+ const replacements: Array<[string, string]> = []
237
+ let match
238
+
239
+ while ((match = imgRegex.exec(html)) !== null) {
240
+ const src = match[1]
241
+ if (src.startsWith('data:') || src.includes('cdn.example.com')) continue
242
+ try {
243
+ const blob = await (await fetch(src)).blob()
244
+ const file = new File([blob], 'image.' + (blob.type.split('/')[1] || 'jpg'))
245
+ replacements.push([src, await handleImageUpload(file)])
246
+ } catch { console.warn('Skip image:', src) }
247
+ }
248
+
249
+ let result = html
250
+ for (const [oldSrc, newSrc] of replacements) result = result.replaceAll(oldSrc, newSrc)
251
+ return result
252
+ }
253
+
254
+ return (
255
+ <MathTextXEditor
256
+ placeholder="Tulis soal..."
257
+ onImageUpload={handleImageUpload}
258
+ onBeforePasteHTML={handleBeforePasteHTML}
259
+ />
260
+ )
261
+ }
262
+ ```
263
+
264
+ ### Base64 Fallback (Tanpa Server)
265
+
266
+ ```tsx
267
+ const handleImageUpload = async (file: File): Promise<string> => {
268
+ return new Promise((resolve, reject) => {
269
+ const reader = new FileReader()
270
+ reader.onload = () => resolve(reader.result as string)
271
+ reader.onerror = reject
272
+ reader.readAsDataURL(file)
273
+ })
274
+ }
275
+ // ⚠️ Base64 hanya cocok untuk gambar < 100KB. Untuk produksi, gunakan upload ke server.
276
+ ```
277
+
96
278
  ---
97
279
 
98
280
  ## 📋 Props API
@@ -102,15 +284,17 @@ function QuestionCard({ questionHtml }) {
102
284
  | Prop | Type | Default | Description |
103
285
  |------|------|---------|-------------|
104
286
  | `content` | `string` | `''` | Initial HTML content |
105
- | `onChange` | `(html: string) => void` | — | Callback on content change |
287
+ | `onChange` | `(html: string) => void` | — | Callback on content change (debounced 150ms) |
106
288
  | `onSave` | `(html: string) => void` | — | Callback on Ctrl+S |
107
289
  | `onImageUpload` | `(file: File) => Promise<string>` | — | Custom image upload handler |
290
+ | `onBeforePasteHTML` | `(html: string) => Promise<string>` | — | Transform pasted HTML (re-upload images) |
108
291
  | `placeholder` | `string` | `'Tulis soal...'` | Placeholder text |
109
292
  | `minHeight` | `string` | `'200px'` | Minimum editor height |
110
293
  | `maxHeight` | `string` | — | Maximum editor height |
111
294
  | `autoFocus` | `boolean` | `false` | Auto-focus editor on mount |
112
295
  | `toolbarMode` | `'basic' \| 'advanced'` | `'basic'` | Toolbar preset |
113
- | `educationLevel` | `'sd' \| 'smp' \| 'sma' \| 'all'` | `'all'` | Filter math templates by level |
296
+ | `editable` | `boolean` | `true` | Set false for read-only mode |
297
+ | `className` | `string` | — | Additional CSS class |
114
298
 
115
299
  ### ContentViewer
116
300
 
@@ -127,17 +311,35 @@ function QuestionCard({ questionHtml }) {
127
311
 
128
312
  ```tsx
129
313
  import {
130
- MathTextXEditor,
131
- ContentViewer,
132
- getHTML,
133
- toCompatibleHTML,
134
- sanitizeCKEditorHTML,
135
- MathTypeDialog,
136
- TemplatePanel,
137
- mathTemplates,
138
- getTemplatesByLevel,
139
- getTemplatesByCategory,
140
- getTemplateCategories,
314
+ MathTextXEditor, // Main editor component
315
+ ContentViewer, // Read-only renderer
316
+ MathTypeDialog, // Standalone equation editor dialog
317
+ TemplatePanel, // Standalone formula template panel
318
+ MainToolbar, // Standalone text formatting toolbar
319
+ MathToolbar, // Standalone math symbols toolbar
320
+ SymbolPalette, // Standalone symbol picker
321
+ WordCount, // Status bar word/char counter
322
+ LinkDialog, // Standalone link insert/edit dialog
323
+ ImageEditDialog, // Standalone image edit dialog
324
+ InsertTableDialog, // Standalone table insert dialog
325
+ TableMenu, // Standalone table context menu
326
+ CellPropertiesDialog, // Standalone cell properties dialog
327
+ TablePropertiesDialog, // Standalone table properties dialog
328
+ TableTemplatesDialog, // Standalone table template picker
329
+ MathInlineNode, // TipTap inline math extension
330
+ MathBlockNode, // TipTap block math extension
331
+ getHTML, // Serialize editor → HTML string
332
+ getJSON, // Serialize editor → JSON
333
+ sanitizeCKEditorHTML, // Clean CKEditor HTML for compatibility
334
+ toCompatibleHTML, // Convert to CKEditor-compatible format
335
+ createExtensions, // Create TipTap extensions programmatically
336
+ mathTemplates, // Template definitions
337
+ getTemplatesByLevel, // Filter templates by education level
338
+ getTemplatesByCategory,// Filter templates by category
339
+ getTemplateCategories, // Get all template categories
340
+ countWords, // Word count utility
341
+ countCharacters, // Character count utility
342
+ getTemplateStyles, // Table template CSS generator
141
343
  } from 'erl-mathtextx-editor'
142
344
 
143
345
  import 'erl-mathtextx-editor/styles'
@@ -160,87 +362,36 @@ import 'erl-mathtextx-editor/viewer/styles'
160
362
  | `Ctrl+I` | Italic |
161
363
  | `Ctrl+U` | Underline |
162
364
  | `Ctrl+K` | Insert/edit link |
365
+ | `Ctrl+M` | Insert inline math |
163
366
  | `Ctrl+Shift+T` | Insert table |
164
367
  | `Ctrl+S` | Save document |
165
- | `Tab` | Indent paragraph (at start) / Insert 4 spaces (in middle) |
166
- | `Shift+Tab` | Outdent paragraph |
167
- | `Backspace` | Outdent (if indented) / Join with previous paragraph |
368
+ | `Shift+Ctrl+V` | Paste as plain text |
369
+ | `Enter` (equation editor) | Insert formula |
370
+ | `Esc` (equation editor) | Close dialog |
168
371
 
169
372
  ---
170
373
 
171
- ## 🎯 Example: Multiple Choice Question Form
374
+ ## ⚠️ Troubleshooting
172
375
 
173
- ```tsx
174
- import { useState } from 'react'
175
- import { MathTextXEditor } from 'erl-mathtextx-editor'
176
- import 'erl-mathtextx-editor/styles'
376
+ | Error | Solution |
377
+ |---|---|
378
+ | `Can't resolve 'erl-mathtextx-editor'` | `npm install erl-mathtextx-editor` |
379
+ | `Can't resolve 'erl-mathtextx-editor/styles'` | Version ≥ 0.1.3. Alternatif: `import 'erl-mathtextx-editor/dist/assets/erl-mathtextx-editor.css'` |
380
+ | `MathTextXEditor is not a function` | Pastikan React ≥ 18 (`npm ls react`) |
381
+ | `window is not defined` (Next.js) | Gunakan `dynamic()` dengan `{ ssr: false }` |
382
+ | `Unexpected token 'export'` (CRA) | Webpack config: `resolve.mainFields: ['main', 'module']` |
383
+ | MathLive fonts error | Set `(window as any).MATHLIVE_FONTS_PATH = '/fonts'` + copy font ke `public/fonts/` |
177
384
 
178
- export default function QuestionForm() {
179
- const [question, setQuestion] = useState('')
180
- const [options, setOptions] = useState([
181
- { id: 'A', content: '', isCorrect: false },
182
- { id: 'B', content: '', isCorrect: false },
183
- { id: 'C', content: '', isCorrect: false },
184
- { id: 'D', content: '', isCorrect: false },
185
- ])
186
-
187
- const handleSubmit = async () => {
188
- await fetch('/api/questions', {
189
- method: 'POST',
190
- headers: { 'Content-Type': 'application/json' },
191
- body: JSON.stringify({ question, options }),
192
- })
193
- }
194
-
195
- return (
196
- <div>
197
- <h2>Buat Soal Pilihan Ganda</h2>
198
-
199
- <MathTextXEditor
200
- content={question}
201
- onChange={setQuestion}
202
- placeholder="Tulis pertanyaan..."
203
- minHeight="150px"
204
- />
385
+ ---
205
386
 
206
- {options.map((option) => (
207
- <div key={option.id}>
208
- <label>
209
- <input
210
- type="radio"
211
- name="correct"
212
- checked={option.isCorrect}
213
- onChange={() => {
214
- setOptions(prev =>
215
- prev.map(o => ({
216
- ...o,
217
- isCorrect: o.id === option.id
218
- }))
219
- )
220
- }}
221
- />
222
- Opsi {option.id}
223
- </label>
224
- <MathTextXEditor
225
- content={option.content}
226
- onChange={(html) => {
227
- setOptions(prev =>
228
- prev.map(o =>
229
- o.id === option.id ? { ...o, content: html } : o
230
- )
231
- )
232
- }}
233
- placeholder={`Jawaban ${option.id}...`}
234
- minHeight="80px"
235
- />
236
- </div>
237
- ))}
387
+ ## Verified Import Paths
238
388
 
239
- <button onClick={handleSubmit}>Simpan Soal</button>
240
- </div>
241
- )
242
- }
243
- ```
389
+ | Import | Resolves to |
390
+ |---|---|
391
+ | `erl-mathtextx-editor` | `dist/erl-mathtextx-editor.js` |
392
+ | `erl-mathtextx-editor/styles` | `dist/assets/erl-mathtextx-editor.css` |
393
+ | `erl-mathtextx-editor/viewer` | `dist/viewer.js` |
394
+ | `erl-mathtextx-editor/viewer/styles` | `dist/viewer-styles.js` |
244
395
 
245
396
  ---
246
397
 
@@ -250,8 +401,11 @@ export default function QuestionForm() {
250
401
  - **Editor Engine:** TipTap / ProseMirror
251
402
  - **Math Input:** MathLive (WYSIWYG math)
252
403
  - **Math Rendering:** KaTeX
404
+ - **DOCX Import:** mammoth.js
253
405
  - **XSS Protection:** DOMPurify
254
406
  - **Graph Plotting:** Function Plot
407
+ - **Syntax Highlight:** lowlight (100+ languages)
408
+ - **Build:** Vite (Library Mode)
255
409
 
256
410
  ---
257
411
 
@@ -266,3 +420,4 @@ export default function QuestionForm() {
266
420
  - **NPM:** [erl-mathtextx-editor](https://www.npmjs.com/package/erl-mathtextx-editor)
267
421
  - **Source:** [GitHub Repository](https://github.com/erlangga/richtext-editor-research)
268
422
  - **Issues:** [Report Bug](https://github.com/erlangga/richtext-editor-research/issues)
423
+ - **📘 Embed Tutorial:** [docs/EMBED_TUTORIAL.md](https://github.com/erlangga/richtext-editor-research/blob/main/docs/EMBED_TUTORIAL.md)
@@ -1,6 +1,6 @@
1
1
  import { jsx as t, jsxs as l } from "react/jsx-runtime";
2
2
  import z, { useId as H, useRef as A, useCallback as b, useState as d, useEffect as V } from "react";
3
- import { u as B } from "./index-UCSefQk0.js";
3
+ import { u as B } from "./index-CakccgVO.js";
4
4
  const M = z.memo(({
5
5
  isOpen: s,
6
6
  initialData: i = {},