@synclineapi/mdx-editor 0.1.2 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +403 -139
  2. package/dist/core/config/EditorConfig.d.ts +5 -0
  3. package/dist/core/config/defaults.d.ts +33 -0
  4. package/dist/core/config/index.d.ts +2 -0
  5. package/dist/core/dom/editor-pane.d.ts +7 -0
  6. package/dist/core/dom/editor-root.d.ts +8 -0
  7. package/dist/core/dom/index.d.ts +11 -0
  8. package/dist/core/dom/preview-pane.d.ts +5 -0
  9. package/dist/core/dom/skip-link.d.ts +1 -0
  10. package/dist/core/dom/status-bar.d.ts +13 -0
  11. package/dist/core/dom/toolbar-dom.d.ts +5 -0
  12. package/dist/core/editor.d.ts +139 -7
  13. package/dist/core/mdx-themes.d.ts +3 -0
  14. package/dist/core/platform.d.ts +31 -0
  15. package/dist/core/plugin-manager.d.ts +1 -24
  16. package/dist/core/plugins/PluginContext.d.ts +26 -0
  17. package/dist/core/plugins/PluginManager.d.ts +41 -0
  18. package/dist/core/plugins/index.d.ts +3 -0
  19. package/dist/core/renderer/CodeRenderer.d.ts +4 -0
  20. package/dist/core/renderer/ListRenderer.d.ts +5 -0
  21. package/dist/core/renderer/MarkdownRenderer.d.ts +7 -0
  22. package/dist/core/renderer/MdxValidator.d.ts +3 -0
  23. package/dist/core/renderer/Renderer.d.ts +10 -0
  24. package/dist/core/renderer/TableRenderer.d.ts +4 -0
  25. package/dist/core/renderer/index.d.ts +7 -0
  26. package/dist/core/renderer/sanitize.d.ts +1 -0
  27. package/dist/core/renderer.d.ts +6 -31
  28. package/dist/core/toolbar.d.ts +1 -0
  29. package/dist/core/types.d.ts +150 -2
  30. package/dist/core/ui/AutocompleteController.d.ts +58 -0
  31. package/dist/core/ui/DropdownController.d.ts +8 -0
  32. package/dist/core/ui/FindReplaceController.d.ts +60 -0
  33. package/dist/core/ui/LineNumberController.d.ts +218 -0
  34. package/dist/core/ui/ModeController.d.ts +9 -0
  35. package/dist/core/ui/PreviewController.d.ts +13 -0
  36. package/dist/core/ui/ResponsiveController.d.ts +30 -0
  37. package/dist/core/ui/SplitterController.d.ts +31 -0
  38. package/dist/core/ui/StatusBarController.d.ts +16 -0
  39. package/dist/core/ui/ThemeController.d.ts +8 -0
  40. package/dist/core/ui/ToolbarController.d.ts +10 -0
  41. package/dist/core/ui/UIController.d.ts +43 -0
  42. package/dist/core/ui/index.d.ts +12 -0
  43. package/dist/index.d.ts +29 -1
  44. package/dist/plugins/basic-formatting/index.d.ts +1 -0
  45. package/dist/plugins/callouts/index.d.ts +1 -0
  46. package/dist/plugins/diagrams/index.d.ts +1 -0
  47. package/dist/plugins/index.d.ts +1 -11
  48. package/dist/plugins/layout/index.d.ts +3 -0
  49. package/dist/plugins/lists/index.d.ts +1 -0
  50. package/dist/plugins/media/index.d.ts +2 -0
  51. package/dist/plugins/token-utils.d.ts +49 -0
  52. package/dist/plugins/utilities/index.d.ts +5 -0
  53. package/dist/style.css +1 -1
  54. package/dist/syncline-mdx-editor.js +9628 -1835
  55. package/dist/syncline-mdx-editor.umd.cjs +988 -90
  56. package/package.json +5 -4
package/README.md CHANGED
@@ -1,25 +1,30 @@
1
1
  # @synclineapi/mdx-editor
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@synclineapi/mdx-editor.svg)](https://www.npmjs.com/package/@synclineapi/mdx-editor)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
4
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.4-blue.svg)](https://www.typescriptlang.org/)
6
5
  [![WCAG AA](https://img.shields.io/badge/Accessibility-WCAG%20AA-green.svg)](https://www.w3.org/TR/WCAG21/)
7
6
  [![Website](https://img.shields.io/badge/Website-markdown.synclineapi.com-blue)](https://markdown.synclineapi.com/)
8
7
 
9
- A **framework-agnostic**, plugin-based MDX/Markdown editor with toolbar customization, live preview, and 33+ built-in features. Works with React, Vue, Angular, Next.js, Svelte, or plain JavaScript.
8
+ A **framework-agnostic**, plugin-based MDX/Markdown editor with a high-performance virtual-rendering code pane, live HTML preview, a configurable toolbar, and 38 built-in content plugins. Works with React, Vue, Angular, Next.js, Svelte, or plain JavaScript.
10
9
 
11
10
  **[Live Demo →](https://markdown.synclineapi.com/)**
12
11
 
13
- ## Quick Start
12
+ ---
13
+
14
+ ## Installation
14
15
 
15
16
  ```bash
16
17
  npm install @synclineapi/mdx-editor
17
18
  ```
18
19
 
20
+ ---
21
+
22
+ ## Quick Start
23
+
19
24
  ### Vanilla JavaScript
20
25
 
21
26
  ```html
22
- <div id="editor"></div>
27
+ <div id="editor" style="height: 600px;"></div>
23
28
 
24
29
  <script type="module">
25
30
  import { createEditor } from '@synclineapi/mdx-editor';
@@ -27,15 +32,36 @@ npm install @synclineapi/mdx-editor
27
32
 
28
33
  const editor = createEditor({
29
34
  container: '#editor',
30
- value: '# Hello World',
31
- onChange: (markdown) => console.log('Content changed:', markdown),
35
+ value: '# Hello World\n\nStart writing *MDX* here.',
36
+ onChange: (markdown) => console.log(markdown),
32
37
  });
33
38
  </script>
34
39
  ```
35
40
 
41
+ ### React
42
+
43
+ ```tsx
44
+ import { useEffect, useRef } from 'react';
45
+ import { createEditor, type SynclineMDXEditor } from '@synclineapi/mdx-editor';
46
+ import '@synclineapi/mdx-editor/style.css';
47
+
48
+ export function Editor({ value, onChange }: { value: string; onChange: (v: string) => void }) {
49
+ const ref = useRef<HTMLDivElement>(null);
50
+ const editor = useRef<SynclineMDXEditor | null>(null);
51
+
52
+ useEffect(() => {
53
+ if (!ref.current) return;
54
+ editor.current = createEditor({ container: ref.current, value, onChange });
55
+ return () => editor.current?.destroy();
56
+ }, []);
57
+
58
+ return <div ref={ref} style={{ height: 600 }} />;
59
+ }
60
+ ```
61
+
36
62
  ### Custom Plugin Selection
37
63
 
38
- ```js
64
+ ```ts
39
65
  import { SynclineMDXEditor, boldPlugin, italicPlugin, headingPlugin } from '@synclineapi/mdx-editor';
40
66
  import '@synclineapi/mdx-editor/style.css';
41
67
 
@@ -46,83 +72,349 @@ const editor = new SynclineMDXEditor({
46
72
  });
47
73
  ```
48
74
 
49
- ## Features (33+ Built-in Plugins)
75
+ ---
76
+
77
+ ## Features
78
+
79
+ ### 38 Built-in Plugins
50
80
 
51
81
  | Category | Plugins |
52
82
  |----------|---------|
53
- | **Formatting** | Heading (16), Bold, Italic, Strikethrough, Quote |
83
+ | **Formatting** | Heading (H1H6), Bold, Italic, Strikethrough, Quote |
54
84
  | **Links & Media** | Link, Image, Image Background, Image Frame |
55
- | **Code** | Inline Code, Code Block (with syntax highlighting placeholder) |
85
+ | **Code** | Inline Code, Fenced Code Block |
56
86
  | **Lists** | Unordered List, Ordered List, Task List |
57
- | **Layout** | Table, Multi-Column (2–5), Tabs, Container |
87
+ | **Layout** | Table, Multi-Column (2–5 cols), Tabs, Container |
58
88
  | **Components** | Accordion, Accordion Group, Card, Card Group, Steps |
59
- | **Callouts** | Admonition (Tip/Warning/Caution/Danger/Check/Info/Note), Tip (Good/Bad/Info) |
60
- | **Rich Content** | Highlight (8 colors), Emoji, Formula (inline/block via KaTeX), Tooltip, Copy Text |
89
+ | **Callouts** | Admonition (Tip / Warning / Caution / Danger / Info / Note), Tip (Good / Bad / Info) |
90
+ | **Rich Content** | Highlight (8 colours), Emoji, Formula (KaTeX inline & block), Tooltip, Copy Text |
91
+ | **Inline labels** | Badge (success / warning / error / info), Status Tag (live / beta / soon / archived) |
61
92
  | **Embeds** | YouTube, Video file, GitHub Gist, Twitter/X, CodePen, CodeSandbox |
62
93
  | **Diagrams** | Mermaid (Flowchart, Sequence, Class, State, ER, Journey, Gantt) |
63
94
  | **Insert** | API Endpoint, Markdown Import, Code Snippet |
64
95
 
65
- ## Architecture
96
+ ### Editor Engine
97
+
98
+ - **Virtual rendering** — only visible rows are in the DOM; handles documents of 10 000+ lines at 60 fps
99
+ - **Word wrap** — proper pixel-width wrap with full active-line highlight across all visual rows
100
+ - **MDX autocomplete** — context-aware component-tag and attribute suggestions; Tab-trigger snippet expansion; plugins contribute items declaratively via `completions: [...]` or dynamically via `ctx.registerCompletion()`
101
+ - **Semantic syntax highlighting** — every built-in plugin declares a `provideTokens` function mapping its MDX syntax to semantic token classes (`kw`, `cls`, `fn`, `str`, `op`, `typ`, `num`, `cmt`); consumers can extend this with `registerSyntaxHighlighter()`
102
+ - **Live HTML preview** — side-by-side split / editor-only / preview-only modes with a draggable splitter
103
+ - **Table of Contents** — auto-generated from headings, collapsible panel
104
+ - **Theme system** — light / dark toggle; auto-syncs with `data-theme`, `.smdx-dark`, or `data-color-scheme`
105
+
106
+ ---
66
107
 
108
+ ## Configuration
109
+
110
+ ### `EditorConfig`
111
+
112
+ | Option | Type | Default | Description |
113
+ |--------|------|---------|-------------|
114
+ | `container` | `HTMLElement \| string` | — | **Required.** DOM element or CSS selector to mount into. |
115
+ | `value` | `string` | `''` | Initial markdown content. |
116
+ | `onChange` | `(value: string) => void` | — | Called on every content change (typing, paste, undo, `setValue()`). |
117
+ | `plugins` | `EditorPlugin[]` | all built-ins | Plugins to register. |
118
+ | `toolbar` | `ToolbarConfig` | default toolbar | Toolbar layout (rows of item IDs, groups, dividers). |
119
+ | `theme` | `Record<string, string>` | — | CSS custom property overrides for the MDX prose shell. |
120
+ | `mode` | `'split' \| 'editor' \| 'preview'` | `'split'` | Initial layout mode. |
121
+ | `placeholder` | `string` | — | Placeholder shown in an empty editor. |
122
+ | `readOnly` | `boolean` | `false` | Prevent all content mutations. |
123
+ | `renderers` | `Record<string, RendererFn>` | — | Custom preview renderer overrides. |
124
+ | `locale` | `Partial<EditorLocale>` | — | i18n label overrides. |
125
+ ---
126
+
127
+ ## Theming
128
+
129
+ ### CSS custom properties
130
+
131
+ ```css
132
+ .smdx-editor {
133
+ --smdx-primary: #6366f1; /* accent colour */
134
+ --smdx-bg: #ffffff; /* prose shell background */
135
+ --smdx-text: #1e293b; /* prose text */
136
+ --smdx-border: #e2e8f0; /* dividers */
137
+ --smdx-font-family: 'Inter', sans-serif;
138
+ --smdx-font-mono: 'JetBrains Mono', monospace;
139
+ --smdx-radius: 12px;
140
+ }
67
141
  ```
68
- @synclineapi/mdx-editor
69
- ├── Core Engine (SynclineMDXEditor)
70
- │ ├── EventEmitter – Pub/sub event system
71
- │ ├── PluginManager – Plugin lifecycle
72
- │ ├── Toolbar – Configurable toolbar renderer
73
- │ ├── Renderer – Markdown/MDX → HTML engine
74
- │ └── History – Undo/redo stack
75
- └── Plugins (33+)
76
- └── Each exports: toolbarItems, shortcuts, renderers, parsers, styles
142
+
143
+ ### Via config
144
+
145
+ ```ts
146
+ createEditor({
147
+ container: '#editor',
148
+ theme: {
149
+ '--smdx-primary': '#e11d48',
150
+ '--smdx-bg': '#0f172a',
151
+ },
152
+ });
77
153
  ```
78
154
 
155
+ ### Dark mode
156
+
157
+ Add `.smdx-dark` to the editor root, any ancestor element, `<body>`, or `<html>` — the editor observes all of them. Alternatively set `data-theme="dark"` on any ancestor (Next.js / Nuxt / Tailwind pattern):
158
+
159
+ ```ts
160
+ // CSS class toggle
161
+ editor.getRoot().classList.add('smdx-dark');
162
+
163
+ // data-theme attribute (works on any ancestor)
164
+ document.documentElement.dataset.theme = 'dark';
165
+ ```
166
+
167
+ ---
168
+
169
+ ## Syntax Highlighting
170
+
171
+ ### `registerSyntaxHighlighter()`
172
+
173
+ Add custom token providers that colour your own MDX component syntax on top of the built-in highlighting. Every built-in plugin already declares a `provideTokens` function; this API lets you extend it from outside.
174
+
175
+ ```ts
176
+ import type { PluginTokenProvider } from '@synclineapi/mdx-editor';
177
+ import { componentTagTokens } from '@synclineapi/mdx-editor';
178
+
179
+ // Quickest option — use the built-in JSX helper
180
+ // Highlights <MyCard …> tag names as `cls`, attribute names as `fn`, values as `str`.
181
+ // Handles boolean attributes like `defaultOpen` and `disabled` automatically.
182
+ editor.registerSyntaxHighlighter(componentTagTokens(['MyCard', 'MyCardGroup']));
183
+
184
+ // Custom provider for non-JSX syntax
185
+ const myProvider: PluginTokenProvider = (line) => {
186
+ const segs = [];
187
+ // Highlight :::type admonition-style fences
188
+ const m = line.match(/^(:::)(\w+)/);
189
+ if (m) {
190
+ segs.push({ cls: 'kw', start: 0, end: 3 });
191
+ segs.push({ cls: 'cls', start: 3, end: 3 + m[2].length });
192
+ }
193
+ return segs;
194
+ };
195
+ editor.registerSyntaxHighlighter(myProvider);
196
+
197
+ // Pass an array to register several at once
198
+ editor.registerSyntaxHighlighter([providerA, providerB]);
199
+ ```
200
+
201
+ ### Token classes
202
+
203
+ | `cls` | Colour role | Typical MDX use |
204
+ |-------|-------------|-----------------|
205
+ | `kw` | purple | Heading `#`, code fences ` ``` `, `:::`, `---`, blockquotes `>` |
206
+ | `str` | green | Link URLs, attribute values, inline `` `code` ``, `$math$` |
207
+ | `cmt` | muted gray | ~~Strikethrough~~ text |
208
+ | `fn` | blue | Link labels, *italic*, attribute names |
209
+ | `num` | orange | **Bold** text, `$$formula$$` markers |
210
+ | `cls` | yellow | `<Card>`, `<Tabs>`, `<Accordion>` — JSX component tag names |
211
+ | `op` | cyan | List markers `- 1.`, table pipes `\|` |
212
+ | `typ` | blue | Code-fence language identifiers (`mermaid`, `ts`, `python`) |
213
+ | `dec` | red | Decorators (reserved for custom use) |
214
+
215
+ ### `componentTagTokens()` helper
216
+
217
+ The built-in JSX highlighter factory. Handles:
218
+
219
+ - **Tag names**: `<Card`, `</Card>`, `<Card />` → `cls`
220
+ - **Value attributes**: `title="…"`, `src="…"` → name as `fn`, value as `str`
221
+ - **Boolean attributes**: `defaultOpen`, `disabled`, `open` (no `=`) → `fn`
222
+ - **Multi-line tags**: attributes that continue on the next line(s) are highlighted correctly — the provider tracks open-tag state across lines.
223
+ - Attribute scanning is scoped to the region between the tag name and `>` so tag body text is never falsely coloured.
224
+
225
+ ```ts
226
+ import { componentTagTokens } from '@synclineapi/mdx-editor';
227
+
228
+ provideTokens: componentTagTokens(['Accordion', 'AccordionGroup']),
229
+ // <Accordion title="…" defaultOpen> → Accordion=cls title=fn "…"=str defaultOpen=fn
230
+ ```
231
+
232
+ ---
233
+
234
+ ## Autocomplete
235
+
236
+ ### `registerAutoComplete()`
237
+
238
+ Add custom autocomplete items on top of the built-in MDX completions. Items appear in the popup immediately and persist for the lifetime of the editor.
239
+
240
+ ```ts
241
+ import type { CompletionItem } from '@synclineapi/mdx-editor';
242
+
243
+ editor.registerAutoComplete([
244
+ // Plain word completion
245
+ { label: 'MyCard', kind: 'cls', detail: 'component' },
246
+
247
+ // Snippet — Tab or click inserts the full body; $1 is the cursor position
248
+ {
249
+ label: 'mycard',
250
+ kind: 'snip',
251
+ detail: '<MyCard> component',
252
+ description: 'Inserts a custom card block.',
253
+ body: '<MyCard title="$1">\n $2\n</MyCard>',
254
+ },
255
+ ]);
256
+ ```
257
+
258
+ Call it again at any time to add more items — they accumulate:
259
+
260
+ ```ts
261
+ editor.registerAutoComplete({ label: 'MyBanner', kind: 'cls', detail: 'component' });
262
+ ```
263
+
264
+ ### Completion kinds
265
+
266
+ | `kind` | Badge | Use for |
267
+ |--------|-------|---------|
268
+ | `'cls'` | **C** | Component names (`<MyCard>`, `<Tabs>`) |
269
+ | `'fn'` | **f** | Functions / methods |
270
+ | `'kw'` | **K** | Keywords |
271
+ | `'var'` | **·** | Attributes, variables, props |
272
+ | `'typ'` | **T** | Types / interfaces |
273
+ | `'snip'` | **S** | Snippet template — set `body` to expand on accept |
274
+
275
+ ### Snippet bodies
276
+
277
+ Use `$1`, `$2`, … as cursor tab stops. The cursor lands on `$1` after expansion:
278
+
279
+ ```ts
280
+ {
281
+ label: 'endpoint',
282
+ kind: 'snip',
283
+ detail: 'API endpoint block',
284
+ body: '<Endpoint method="$1GET" path="$2/api/resource">\n $3Description\n</Endpoint>',
285
+ }
286
+ ```
287
+
288
+ ---
289
+
79
290
  ## Plugin API
80
291
 
81
- Create custom plugins:
292
+ Plugins are the recommended way to bundle a toolbar button, keyboard shortcut, preview renderer, and autocomplete items together as one reusable unit.
82
293
 
83
294
  ```ts
84
295
  import type { EditorPlugin } from '@synclineapi/mdx-editor';
85
296
 
86
297
  const myPlugin: EditorPlugin = {
87
298
  name: 'my-custom-block',
299
+
300
+ // Toolbar button
88
301
  toolbarItems: [{
89
302
  id: 'myBlock',
90
303
  label: 'My Block',
91
304
  icon: '<svg>...</svg>',
92
- tooltip: 'Insert my block',
305
+ tooltip: 'Insert my block (Ctrl+Shift+M)',
93
306
  action: ({ editor }) => {
94
307
  editor.insertBlock('<MyBlock>\n Content\n</MyBlock>');
95
308
  },
96
309
  }],
310
+
311
+ // Autocomplete — same content as the toolbar action, now also reachable
312
+ // by typing the trigger word in the editor
313
+ completions: [
314
+ {
315
+ label: 'myblock',
316
+ kind: 'snip',
317
+ detail: '<MyBlock> component',
318
+ body: '<MyBlock>\n $1Content\n</MyBlock>',
319
+ },
320
+ // Component name entry (shows in autocomplete as a class suggestion)
321
+ { label: 'MyBlock', kind: 'cls', detail: 'custom block component' },
322
+ ],
323
+
324
+ // Preview renderer
97
325
  renderers: [{
98
326
  name: 'myBlock',
99
327
  pattern: /<MyBlock>([\s\S]*?)<\/MyBlock>/g,
100
328
  render: (match) => {
101
329
  const m = match.match(/<MyBlock>([\s\S]*?)<\/MyBlock>/);
102
- return `<div class="my-block">${m?.[1] || ''}</div>`;
330
+ return `<div class="my-block">${m?.[1] ?? ''}</div>`;
103
331
  },
104
332
  }],
333
+
334
+ // Syntax highlighting — called per-line, highlight component tag
335
+ // names, attribute names, and attribute values automatically.
336
+ // Use the built-in helper for JSX components:
337
+ provideTokens: componentTagTokens(['MyBlock']),
338
+ // Or write a custom provider for any syntax pattern:
339
+ // provideTokens: (line) => {
340
+ // const segs = [];
341
+ // const m = line.match(/^(:::)(\w+)/);
342
+ // if (m) {
343
+ // segs.push({ cls: 'kw', start: 0, end: 3 });
344
+ // segs.push({ cls: 'cls', start: 3, end: 3 + m[2].length });
345
+ // }
346
+ // return segs;
347
+ // },
348
+
105
349
  shortcuts: [
106
- { key: 'Ctrl+Shift+m', action: ({ editor }) => editor.insertBlock('<MyBlock>\n Content\n</MyBlock>') },
350
+ {
351
+ key: 'Ctrl+Shift+m',
352
+ action: ({ editor }) => editor.insertBlock('<MyBlock>\n Content\n</MyBlock>'),
353
+ description: 'Insert custom block',
354
+ },
107
355
  ],
108
- styles: `.my-block { border: 2px solid blue; padding: 16px; border-radius: 8px; }`,
356
+
357
+ styles: `.my-block { border: 2px solid #6366f1; padding: 16px; border-radius: 8px; }`,
358
+ };
359
+ ```
360
+
361
+ You can also register completions dynamically inside `init()` using `ctx.registerCompletion()`:
362
+
363
+ ```ts
364
+ const myPlugin: EditorPlugin = {
365
+ name: 'dynamic-completions',
366
+ async init(ctx) {
367
+ const components = await fetchMyComponents(); // dynamic list
368
+ for (const comp of components) {
369
+ ctx.registerCompletion({
370
+ label: comp.tag,
371
+ kind: 'cls',
372
+ detail: comp.description,
373
+ });
374
+ }
375
+ },
109
376
  };
110
377
  ```
111
378
 
379
+ ### Completion merge order
380
+
381
+ Every time a plugin is registered or unregistered the autocomplete list is rebuilt:
382
+
383
+ ```
384
+ built-in MDX completions (tags, JSX attributes)
385
+ + built-in MDX snippets (toolbar-matched snippet bodies)
386
+ + plugin completions (all registered plugins, in order)
387
+ + user completions (registerAutoComplete() calls)
388
+ ```
389
+
390
+ ---
391
+
112
392
  ## Toolbar Customization
113
393
 
114
394
  ### Flat toolbar
115
395
 
116
- ```js
396
+ ```ts
117
397
  createEditor({
118
398
  container: '#editor',
119
399
  toolbar: [['bold', 'italic', '|', 'heading', '|', 'link', 'image']],
120
400
  });
121
401
  ```
122
402
 
403
+ ### Multi-row toolbar
404
+
405
+ ```ts
406
+ createEditor({
407
+ container: '#editor',
408
+ toolbar: [
409
+ ['heading', '|', 'bold', 'italic', 'strikethrough'],
410
+ ['link', 'image', '|', 'table', 'code'],
411
+ ],
412
+ });
413
+ ```
414
+
123
415
  ### Nested dropdowns
124
416
 
125
- ```js
417
+ ```ts
126
418
  createEditor({
127
419
  container: '#editor',
128
420
  toolbar: [[
@@ -137,91 +429,81 @@ createEditor({
137
429
  });
138
430
  ```
139
431
 
140
- ## Theming
141
-
142
- Override CSS custom properties:
143
-
144
- ```css
145
- .smdx-editor {
146
- --smdx-primary: #e11d48;
147
- --smdx-bg: #0f172a;
148
- --smdx-text: #e2e8f0;
149
- --smdx-border: #334155;
150
- --smdx-font-family: 'Inter', sans-serif;
151
- --smdx-font-mono: 'JetBrains Mono', monospace;
152
- --smdx-radius: 12px;
153
- }
154
- ```
155
-
156
- Or pass theme via config:
157
-
158
- ```js
159
- createEditor({
160
- container: '#editor',
161
- theme: {
162
- '--smdx-primary': '#e11d48',
163
- '--smdx-bg': '#0f172a',
164
- },
165
- });
166
- ```
167
-
168
- Add `.smdx-dark` class for dark mode:
169
-
170
- ```js
171
- editor.getRoot().classList.add('smdx-dark');
172
- ```
173
-
174
- ## Configuration
175
-
176
- | Option | Type | Default | Description |
177
- |--------|------|---------|-------------|
178
- | `container` | `HTMLElement \| string` | — | **Required.** The DOM element (or CSS selector) to mount the editor into. |
179
- | `value` | `string` | `''` | Initial markdown content. |
180
- | `onChange` | `(value: string) => void` | — | Callback invoked every time the content changes (user input, `setValue()`, undo/redo). |
181
- | `plugins` | `EditorPlugin[]` | all built-in plugins | Plugins to register. |
182
- | `toolbar` | `ToolbarConfig` | default toolbar | Toolbar layout. |
183
- | `theme` | `Record<string, string>` | — | CSS custom property overrides. |
184
- | `mode` | `'split' \| 'editor' \| 'preview'` | `'split'` | Initial editor mode. |
185
- | `placeholder` | `string` | — | Textarea placeholder text. |
186
- | `readOnly` | `boolean` | `false` | Make the editor read-only. |
187
- | `scrollSync` | `boolean` | `true` | Sync scroll position between editor and preview. |
188
- | `renderers` | `Record<string, RendererFn>` | — | Custom renderer overrides. |
189
- | `locale` | `Partial<EditorLocale>` | — | i18n overrides. |
432
+ ---
190
433
 
191
434
  ## API Reference
192
435
 
193
436
  ```ts
194
- interface EditorAPI {
437
+ interface SynclineMDXEditor {
438
+ // Content
195
439
  getValue(): string;
196
440
  setValue(value: string): void;
441
+
442
+ // Editing
197
443
  insertText(text: string): void;
444
+ insertBlock(template: string): void;
198
445
  wrapSelection(prefix: string, suffix: string): void;
199
446
  replaceSelection(text: string): void;
447
+ replaceCurrentLine(text: string): void;
448
+ insertAt(position: number, text: string): void;
449
+
450
+ // Selection & cursor
200
451
  getSelection(): SelectionState;
201
- insertBlock(template: string): void;
452
+ setSelection(start: number, end: number): void;
453
+ getCurrentLine(): string;
454
+ getCurrentLineNumber(): number;
455
+ jumpToLine(lineNumber: number): void;
456
+
457
+ // Undo / Redo
458
+ undo(): void;
459
+ redo(): void;
460
+
461
+ // View
202
462
  focus(): void;
203
463
  renderPreview(): void;
204
- getMode(): EditorMode;
464
+ getMode(): 'split' | 'editor' | 'preview';
205
465
  setMode(mode: 'split' | 'editor' | 'preview'): void;
206
- registerPlugin(plugin: EditorPlugin): void;
207
- unregisterPlugin(name: string): void;
208
- on(event: string, handler: Function): void;
209
- off(event: string, handler: Function): void;
210
- undo(): void;
211
- redo(): void;
466
+ setLineNumbers(enabled: boolean): void;
467
+
468
+ // Metrics
212
469
  getWordCount(): number;
213
470
  getLineCount(): number;
471
+
472
+ // DOM access
473
+ getRoot(): HTMLElement;
474
+ getTextarea(): HTMLTextAreaElement;
475
+ getPreview(): HTMLElement;
476
+
477
+ // Plugins
478
+ registerPlugin(plugin: EditorPlugin): void;
479
+ unregisterPlugin(name: string): void;
480
+
481
+ // Autocomplete
482
+ registerAutoComplete(items: CompletionItem | CompletionItem[]): void;
483
+
484
+ // Syntax highlighting
485
+ registerSyntaxHighlighter(fn: PluginTokenProvider | PluginTokenProvider[]): void;
486
+
487
+ // Theme sync
488
+ syncCodeEditorTheme(): void;
489
+
490
+ // Events
491
+ on(event: string, handler: (data?: unknown) => void): void;
492
+ off(event: string, handler: (data?: unknown) => void): void;
493
+ emit(event: string, data?: unknown): void;
494
+
495
+ // Lifecycle
214
496
  destroy(): void;
215
497
  }
216
498
  ```
217
499
 
218
- ## Events
500
+ ---
219
501
 
220
- ### `onChange` config callback (recommended)
502
+ ## Events
221
503
 
222
- Pass `onChange` directly in the config for the simplest way to listen for content changes:
504
+ ### `onChange` callback (recommended)
223
505
 
224
- ```js
506
+ ```ts
225
507
  const editor = createEditor({
226
508
  container: '#editor',
227
509
  onChange: (markdown) => {
@@ -232,81 +514,63 @@ const editor = createEditor({
232
514
 
233
515
  ### Event emitter
234
516
 
235
- For all events (including selection changes, mode changes, etc.) use the event emitter API:
236
-
237
- ```js
238
- editor.on('change', (markdown) => console.log('Content changed:', markdown));
239
- editor.on('selection-change', (sel) => console.log('Selection:', sel));
240
- editor.on('mode-change', (mode) => console.log('Mode:', mode));
241
- editor.on('render', (html) => console.log('Preview rendered'));
242
- editor.on('toolbar-action', (id) => console.log('Toolbar clicked:', id));
517
+ ```ts
518
+ editor.on('change', (markdown) => console.log('Changed:', markdown));
519
+ editor.on('selection-change', (sel) => console.log('Selection:', sel));
520
+ editor.on('mode-change', (mode) => console.log('Mode:', mode));
521
+ editor.on('render', (html) => console.log('Preview HTML ready'));
522
+ editor.on('focus', () => console.log('Editor focused'));
523
+ editor.on('blur', () => console.log('Editor blurred'));
524
+ editor.on('toolbar-action', (id) => console.log('Toolbar clicked:', id));
243
525
  ```
244
526
 
245
- ## Development
246
-
247
- > **Note:** The GitHub repository for this package is **private** — the source code is not publicly visible. The package is freely installable from npm, but external contributions, forks, and pull requests are not accepted.
248
- >
249
- > To report a bug or request a feature, open an issue at the [GitHub Issues page](https://github.com/RadhaHariharan/syncline-mdx-editor/issues).
250
- >
251
- > ```bash
252
- > npm install @synclineapi/mdx-editor
253
- > ```
254
-
255
- Internal development commands (for maintainers only):
256
-
257
- ```bash
258
- cd syncline-mdx-editor
259
- npm install
260
- npm run dev # Start Vite dev server
261
- npm run build # Build library for production
262
- ```
527
+ ---
263
528
 
264
529
  ## Bundle Size
265
530
 
266
- Check the estimated npm publish size before publishing:
531
+ The production build targets **< 150 kB** (ESM + UMD, gzipped) when `mermaid`, `katex`, and `highlight.js` are treated as external peer dependencies.
267
532
 
268
533
  ```bash
269
534
  npm run build
270
535
  npm run size
271
536
  ```
272
537
 
273
- The core bundle (with `mermaid`, `katex`, `highlight.js` externalised) targets **< 150 kB** for both ESM and UMD outputs.
538
+ ---
274
539
 
275
540
  ## Security
276
541
 
277
- All rendered Markdown/MDX HTML is sanitized via a built-in XSS sanitizer before DOM injection. Dangerous tags (`script`, `iframe`, `object`), event handler attributes (`onclick`, `onerror`, etc.), and `javascript:` protocol URLs are all stripped. See [SECURITY.md](./SECURITY.md) for the vulnerability disclosure policy.
542
+ All rendered Markdown/MDX HTML is sanitized by a built-in XSS sanitizer before DOM injection. Dangerous tags (`script`, `iframe`, `object`), event-handler attributes (`onclick`, `onerror`, etc.), and `javascript:` protocol URLs are stripped automatically. See [SECURITY.md](./SECURITY.md) for the vulnerability disclosure policy.
543
+
544
+ ---
278
545
 
279
546
  ## Accessibility
280
547
 
281
548
  `@synclineapi/mdx-editor` targets **WCAG 2.1 AA** compliance:
282
- - All toolbar buttons have `aria-label` and `title` attributes
549
+
550
+ - All toolbar buttons carry `aria-label` and `title` attributes
283
551
  - The editor textarea has `aria-label="Markdown editor"` and `aria-multiline="true"`
284
552
  - The preview pane has `role="region"`, `aria-label="Preview"`, and `aria-live="polite"`
285
553
  - Mode toggle buttons expose `aria-pressed` state
286
- - Toolbar dropdowns have `role="menu"` / `role="menuitem"`
287
- - A skip link is provided for keyboard users
288
- - All text meets WCAG AA color contrast ratios
554
+ - Toolbar dropdowns use `role="menu"` / `role="menuitem"`
555
+ - A skip link is provided for keyboard-only users
556
+ - All foreground/background colour pairs meet WCAG AA contrast ratios
289
557
 
290
- ## Testing
291
-
292
- ```bash
293
- npm run test # Run all tests
294
- npm run test:watch # Watch mode
295
- npm run test:coverage # Coverage report (target: 80%+)
296
- npm run test:ui # Visual test UI
297
- ```
558
+ ---
298
559
 
299
560
  ## Peer Dependencies
300
561
 
301
- `mermaid`, `katex`, and `highlight.js` are optional peer dependencies. Install them only if you use those features:
562
+ `mermaid`, `katex`, and `highlight.js` are optional. Install only the ones you need:
302
563
 
303
564
  ```bash
304
- npm install mermaid # For Mermaid diagrams
305
- npm install katex # For math formulas
306
- npm install highlight.js # Included by default for syntax highlighting
565
+ npm install mermaid # Mermaid diagrams
566
+ npm install katex # Math / formula rendering
567
+ npm install highlight.js # Syntax highlighting in code blocks
307
568
  ```
308
569
 
570
+ ---
571
+
309
572
  ## License
310
573
 
311
- MIT
574
+ Copyright © Syncline API. All rights reserved.
312
575
 
576
+ This software is proprietary and confidential. Unauthorized copying, distribution, modification, or use of this software, in whole or in part, is strictly prohibited without the express written permission of Syncline API.