@synclineapi/mdx-editor 0.1.1 → 1.0.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.
Files changed (55) hide show
  1. package/README.md +320 -140
  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 +99 -6
  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 +24 -0
  17. package/dist/core/plugins/PluginManager.d.ts +33 -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 -28
  28. package/dist/core/toolbar.d.ts +1 -0
  29. package/dist/core/types.d.ts +72 -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 +28 -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/utilities/index.d.ts +5 -0
  52. package/dist/style.css +1 -1
  53. package/dist/syncline-mdx-editor.js +8468 -1812
  54. package/dist/syncline-mdx-editor.umd.cjs +958 -87
  55. package/package.json +6 -12
package/README.md CHANGED
@@ -1,23 +1,30 @@
1
1
  # @synclineapi/mdx-editor
2
2
 
3
- [![CI](https://github.com/RadhaHariharan/syncline-mdx-editor/actions/workflows/ci.yml/badge.svg)](https://github.com/RadhaHariharan/syncline-mdx-editor/actions/workflows/ci.yml)
4
3
  [![npm version](https://img.shields.io/npm/v/@synclineapi/mdx-editor.svg)](https://www.npmjs.com/package/@synclineapi/mdx-editor)
5
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
4
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.4-blue.svg)](https://www.typescriptlang.org/)
7
5
  [![WCAG AA](https://img.shields.io/badge/Accessibility-WCAG%20AA-green.svg)](https://www.w3.org/TR/WCAG21/)
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 33+ built-in content plugins. Works with React, Vue, Angular, Next.js, Svelte, or plain JavaScript.
10
9
 
11
- ## Quick Start
10
+ **[Live Demo →](https://markdown.synclineapi.com/)**
11
+
12
+ ---
13
+
14
+ ## Installation
12
15
 
13
16
  ```bash
14
17
  npm install @synclineapi/mdx-editor
15
18
  ```
16
19
 
20
+ ---
21
+
22
+ ## Quick Start
23
+
17
24
  ### Vanilla JavaScript
18
25
 
19
26
  ```html
20
- <div id="editor"></div>
27
+ <div id="editor" style="height: 600px;"></div>
21
28
 
22
29
  <script type="module">
23
30
  import { createEditor } from '@synclineapi/mdx-editor';
@@ -25,15 +32,36 @@ npm install @synclineapi/mdx-editor
25
32
 
26
33
  const editor = createEditor({
27
34
  container: '#editor',
28
- value: '# Hello World',
29
- onChange: (markdown) => console.log('Content changed:', markdown),
35
+ value: '# Hello World\n\nStart writing *MDX* here.',
36
+ onChange: (markdown) => console.log(markdown),
30
37
  });
31
38
  </script>
32
39
  ```
33
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
+
34
62
  ### Custom Plugin Selection
35
63
 
36
- ```js
64
+ ```ts
37
65
  import { SynclineMDXEditor, boldPlugin, italicPlugin, headingPlugin } from '@synclineapi/mdx-editor';
38
66
  import '@synclineapi/mdx-editor/style.css';
39
67
 
@@ -44,83 +72,266 @@ const editor = new SynclineMDXEditor({
44
72
  });
45
73
  ```
46
74
 
47
- ## Features (33+ Built-in Plugins)
75
+ ---
76
+
77
+ ## Features
78
+
79
+ ### 33+ Built-in Plugins
48
80
 
49
81
  | Category | Plugins |
50
82
  |----------|---------|
51
- | **Formatting** | Heading (16), Bold, Italic, Strikethrough, Quote |
83
+ | **Formatting** | Heading (H1H6), Bold, Italic, Strikethrough, Quote |
52
84
  | **Links & Media** | Link, Image, Image Background, Image Frame |
53
- | **Code** | Inline Code, Code Block (with syntax highlighting placeholder) |
85
+ | **Code** | Inline Code, Fenced Code Block |
54
86
  | **Lists** | Unordered List, Ordered List, Task List |
55
- | **Layout** | Table, Multi-Column (2–5), Tabs, Container |
87
+ | **Layout** | Table, Multi-Column (2–5 cols), Tabs, Container |
56
88
  | **Components** | Accordion, Accordion Group, Card, Card Group, Steps |
57
- | **Callouts** | Admonition (Tip/Warning/Caution/Danger/Check/Info/Note), Tip (Good/Bad/Info) |
58
- | **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 |
59
91
  | **Embeds** | YouTube, Video file, GitHub Gist, Twitter/X, CodePen, CodeSandbox |
60
92
  | **Diagrams** | Mermaid (Flowchart, Sequence, Class, State, ER, Journey, Gantt) |
61
93
  | **Insert** | API Endpoint, Markdown Import, Code Snippet |
62
94
 
63
- ## Architecture
95
+ ### Editor Engine
96
+
97
+ - **Virtual rendering** — only visible rows are in the DOM; handles documents of 10 000+ lines at 60 fps
98
+ - **Word wrap** — proper pixel-width wrap with full active-line highlight across all visual rows
99
+ - **MDX autocomplete** — context-aware component-tag and attribute suggestions; Tab-trigger snippet expansion
100
+ - **Single-colour prose mode** — all syntax tokens collapse to the text colour for distraction-free writing
101
+ - **Live HTML preview** — side-by-side split / editor-only / preview-only modes with a draggable splitter
102
+ - **Table of Contents** — auto-generated from headings, collapsible panel
103
+ - **Theme system** — light / dark toggle; auto-syncs with `data-theme`, `.smdx-dark`, or `data-color-scheme`
104
+
105
+ ---
106
+
107
+ ## Configuration
108
+
109
+ ### `EditorConfig`
110
+
111
+ | Option | Type | Default | Description |
112
+ |--------|------|---------|-------------|
113
+ | `container` | `HTMLElement \| string` | — | **Required.** DOM element or CSS selector to mount into. |
114
+ | `value` | `string` | `''` | Initial markdown content. |
115
+ | `onChange` | `(value: string) => void` | — | Called on every content change (typing, paste, undo, `setValue()`). |
116
+ | `plugins` | `EditorPlugin[]` | all built-ins | Plugins to register. |
117
+ | `toolbar` | `ToolbarConfig` | default toolbar | Toolbar layout (rows of item IDs, groups, dividers). |
118
+ | `theme` | `Record<string, string>` | — | CSS custom property overrides for the MDX prose shell. |
119
+ | `mode` | `'split' \| 'editor' \| 'preview'` | `'split'` | Initial layout mode. |
120
+ | `placeholder` | `string` | — | Placeholder shown in an empty editor. |
121
+ | `readOnly` | `boolean` | `false` | Prevent all content mutations. |
122
+ | `renderers` | `Record<string, RendererFn>` | — | Custom preview renderer overrides. |
123
+ | `locale` | `Partial<EditorLocale>` | — | i18n label overrides. |
124
+ ---
125
+
126
+ ## Theming
127
+
128
+ ### CSS custom properties
129
+
130
+ ```css
131
+ .smdx-editor {
132
+ --smdx-primary: #6366f1; /* accent colour */
133
+ --smdx-bg: #ffffff; /* prose shell background */
134
+ --smdx-text: #1e293b; /* prose text */
135
+ --smdx-border: #e2e8f0; /* dividers */
136
+ --smdx-font-family: 'Inter', sans-serif;
137
+ --smdx-font-mono: 'JetBrains Mono', monospace;
138
+ --smdx-radius: 12px;
139
+ }
140
+ ```
141
+
142
+ ### Via config
143
+
144
+ ```ts
145
+ createEditor({
146
+ container: '#editor',
147
+ theme: {
148
+ '--smdx-primary': '#e11d48',
149
+ '--smdx-bg': '#0f172a',
150
+ },
151
+ });
152
+ ```
153
+
154
+ ### Dark mode
155
+
156
+ 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):
64
157
 
158
+ ```ts
159
+ // CSS class toggle
160
+ editor.getRoot().classList.add('smdx-dark');
161
+
162
+ // data-theme attribute (works on any ancestor)
163
+ document.documentElement.dataset.theme = 'dark';
65
164
  ```
66
- @synclineapi/mdx-editor
67
- ├── Core Engine (SynclineMDXEditor)
68
- │ ├── EventEmitter – Pub/sub event system
69
- │ ├── PluginManager – Plugin lifecycle
70
- │ ├── Toolbar – Configurable toolbar renderer
71
- │ ├── Renderer – Markdown/MDX → HTML engine
72
- │ └── History – Undo/redo stack
73
- └── Plugins (33+)
74
- └── Each exports: toolbarItems, shortcuts, renderers, parsers, styles
165
+
166
+ ---
167
+
168
+ ## Autocomplete
169
+
170
+ ### `registerAutoComplete()`
171
+
172
+ 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.
173
+
174
+ ```ts
175
+ import type { CompletionItem } from '@synclineapi/mdx-editor';
176
+
177
+ editor.registerAutoComplete([
178
+ // Plain word completion
179
+ { label: 'MyCard', kind: 'cls', detail: 'component' },
180
+
181
+ // Snippet — Tab or click inserts the full body; $1 is the cursor position
182
+ {
183
+ label: 'mycard',
184
+ kind: 'snip',
185
+ detail: '<MyCard> component',
186
+ description: 'Inserts a custom card block.',
187
+ body: '<MyCard title="$1">\n $2\n</MyCard>',
188
+ },
189
+ ]);
190
+ ```
191
+
192
+ Call it again at any time to add more items — they accumulate:
193
+
194
+ ```ts
195
+ editor.registerAutoComplete({ label: 'MyBanner', kind: 'cls', detail: 'component' });
196
+ ```
197
+
198
+ ### Completion kinds
199
+
200
+ | `kind` | Badge | Use for |
201
+ |--------|-------|---------|
202
+ | `'cls'` | **C** | Component names (`<MyCard>`, `<Tabs>`) |
203
+ | `'fn'` | **f** | Functions / methods |
204
+ | `'kw'` | **K** | Keywords |
205
+ | `'var'` | **·** | Attributes, variables, props |
206
+ | `'typ'` | **T** | Types / interfaces |
207
+ | `'snip'` | **S** | Snippet template — set `body` to expand on accept |
208
+
209
+ ### Snippet bodies
210
+
211
+ Use `$1`, `$2`, … as cursor tab stops. The cursor lands on `$1` after expansion:
212
+
213
+ ```ts
214
+ {
215
+ label: 'endpoint',
216
+ kind: 'snip',
217
+ detail: 'API endpoint block',
218
+ body: '<Endpoint method="$1GET" path="$2/api/resource">\n $3Description\n</Endpoint>',
219
+ }
75
220
  ```
76
221
 
222
+ ---
223
+
77
224
  ## Plugin API
78
225
 
79
- Create custom plugins:
226
+ Plugins are the recommended way to bundle a toolbar button, keyboard shortcut, preview renderer, and autocomplete items together as one reusable unit.
80
227
 
81
228
  ```ts
82
229
  import type { EditorPlugin } from '@synclineapi/mdx-editor';
83
230
 
84
231
  const myPlugin: EditorPlugin = {
85
232
  name: 'my-custom-block',
233
+
234
+ // Toolbar button
86
235
  toolbarItems: [{
87
236
  id: 'myBlock',
88
237
  label: 'My Block',
89
238
  icon: '<svg>...</svg>',
90
- tooltip: 'Insert my block',
239
+ tooltip: 'Insert my block (Ctrl+Shift+M)',
91
240
  action: ({ editor }) => {
92
241
  editor.insertBlock('<MyBlock>\n Content\n</MyBlock>');
93
242
  },
94
243
  }],
244
+
245
+ // Autocomplete — same content as the toolbar action, now also reachable
246
+ // by typing the trigger word in the editor
247
+ completions: [
248
+ {
249
+ label: 'myblock',
250
+ kind: 'snip',
251
+ detail: '<MyBlock> component',
252
+ body: '<MyBlock>\n $1Content\n</MyBlock>',
253
+ },
254
+ ],
255
+
256
+ // Preview renderer
95
257
  renderers: [{
96
258
  name: 'myBlock',
97
259
  pattern: /<MyBlock>([\s\S]*?)<\/MyBlock>/g,
98
260
  render: (match) => {
99
261
  const m = match.match(/<MyBlock>([\s\S]*?)<\/MyBlock>/);
100
- return `<div class="my-block">${m?.[1] || ''}</div>`;
262
+ return `<div class="my-block">${m?.[1] ?? ''}</div>`;
101
263
  },
102
264
  }],
265
+
103
266
  shortcuts: [
104
- { key: 'Ctrl+Shift+m', action: ({ editor }) => editor.insertBlock('<MyBlock>\n Content\n</MyBlock>') },
267
+ {
268
+ key: 'Ctrl+Shift+m',
269
+ action: ({ editor }) => editor.insertBlock('<MyBlock>\n Content\n</MyBlock>'),
270
+ description: 'Insert custom block',
271
+ },
105
272
  ],
106
- styles: `.my-block { border: 2px solid blue; padding: 16px; border-radius: 8px; }`,
273
+
274
+ styles: `.my-block { border: 2px solid #6366f1; padding: 16px; border-radius: 8px; }`,
107
275
  };
108
276
  ```
109
277
 
278
+ You can also register completions dynamically inside `init()` using `ctx.registerCompletion()`:
279
+
280
+ ```ts
281
+ const myPlugin: EditorPlugin = {
282
+ name: 'dynamic-completions',
283
+ async init(ctx) {
284
+ const components = await fetchMyComponents(); // dynamic list
285
+ for (const comp of components) {
286
+ ctx.registerCompletion({
287
+ label: comp.tag,
288
+ kind: 'cls',
289
+ detail: comp.description,
290
+ });
291
+ }
292
+ },
293
+ };
294
+ ```
295
+
296
+ ### Completion merge order
297
+
298
+ Every time a plugin is registered or unregistered the autocomplete list is rebuilt:
299
+
300
+ ```
301
+ built-in MDX completions (tags, JSX attributes)
302
+ + built-in MDX snippets (toolbar-matched snippet bodies)
303
+ + plugin completions (all registered plugins, in order)
304
+ + user completions (registerAutoComplete() calls)
305
+ ```
306
+
307
+ ---
308
+
110
309
  ## Toolbar Customization
111
310
 
112
311
  ### Flat toolbar
113
312
 
114
- ```js
313
+ ```ts
115
314
  createEditor({
116
315
  container: '#editor',
117
316
  toolbar: [['bold', 'italic', '|', 'heading', '|', 'link', 'image']],
118
317
  });
119
318
  ```
120
319
 
320
+ ### Multi-row toolbar
321
+
322
+ ```ts
323
+ createEditor({
324
+ container: '#editor',
325
+ toolbar: [
326
+ ['heading', '|', 'bold', 'italic', 'strikethrough'],
327
+ ['link', 'image', '|', 'table', 'code'],
328
+ ],
329
+ });
330
+ ```
331
+
121
332
  ### Nested dropdowns
122
333
 
123
- ```js
334
+ ```ts
124
335
  createEditor({
125
336
  container: '#editor',
126
337
  toolbar: [[
@@ -135,91 +346,78 @@ createEditor({
135
346
  });
136
347
  ```
137
348
 
138
- ## Theming
139
-
140
- Override CSS custom properties:
141
-
142
- ```css
143
- .smdx-editor {
144
- --smdx-primary: #e11d48;
145
- --smdx-bg: #0f172a;
146
- --smdx-text: #e2e8f0;
147
- --smdx-border: #334155;
148
- --smdx-font-family: 'Inter', sans-serif;
149
- --smdx-font-mono: 'JetBrains Mono', monospace;
150
- --smdx-radius: 12px;
151
- }
152
- ```
153
-
154
- Or pass theme via config:
155
-
156
- ```js
157
- createEditor({
158
- container: '#editor',
159
- theme: {
160
- '--smdx-primary': '#e11d48',
161
- '--smdx-bg': '#0f172a',
162
- },
163
- });
164
- ```
165
-
166
- Add `.smdx-dark` class for dark mode:
167
-
168
- ```js
169
- editor.getRoot().classList.add('smdx-dark');
170
- ```
171
-
172
- ## Configuration
173
-
174
- | Option | Type | Default | Description |
175
- |--------|------|---------|-------------|
176
- | `container` | `HTMLElement \| string` | — | **Required.** The DOM element (or CSS selector) to mount the editor into. |
177
- | `value` | `string` | `''` | Initial markdown content. |
178
- | `onChange` | `(value: string) => void` | — | Callback invoked every time the content changes (user input, `setValue()`, undo/redo). |
179
- | `plugins` | `EditorPlugin[]` | all built-in plugins | Plugins to register. |
180
- | `toolbar` | `ToolbarConfig` | default toolbar | Toolbar layout. |
181
- | `theme` | `Record<string, string>` | — | CSS custom property overrides. |
182
- | `mode` | `'split' \| 'editor' \| 'preview'` | `'split'` | Initial editor mode. |
183
- | `placeholder` | `string` | — | Textarea placeholder text. |
184
- | `readOnly` | `boolean` | `false` | Make the editor read-only. |
185
- | `scrollSync` | `boolean` | `true` | Sync scroll position between editor and preview. |
186
- | `renderers` | `Record<string, RendererFn>` | — | Custom renderer overrides. |
187
- | `locale` | `Partial<EditorLocale>` | — | i18n overrides. |
349
+ ---
188
350
 
189
351
  ## API Reference
190
352
 
191
353
  ```ts
192
- interface EditorAPI {
354
+ interface SynclineMDXEditor {
355
+ // Content
193
356
  getValue(): string;
194
357
  setValue(value: string): void;
358
+
359
+ // Editing
195
360
  insertText(text: string): void;
361
+ insertBlock(template: string): void;
196
362
  wrapSelection(prefix: string, suffix: string): void;
197
363
  replaceSelection(text: string): void;
364
+ replaceCurrentLine(text: string): void;
365
+ insertAt(position: number, text: string): void;
366
+
367
+ // Selection & cursor
198
368
  getSelection(): SelectionState;
199
- insertBlock(template: string): void;
369
+ setSelection(start: number, end: number): void;
370
+ getCurrentLine(): string;
371
+ getCurrentLineNumber(): number;
372
+ jumpToLine(lineNumber: number): void;
373
+
374
+ // Undo / Redo
375
+ undo(): void;
376
+ redo(): void;
377
+
378
+ // View
200
379
  focus(): void;
201
380
  renderPreview(): void;
202
- getMode(): EditorMode;
381
+ getMode(): 'split' | 'editor' | 'preview';
203
382
  setMode(mode: 'split' | 'editor' | 'preview'): void;
204
- registerPlugin(plugin: EditorPlugin): void;
205
- unregisterPlugin(name: string): void;
206
- on(event: string, handler: Function): void;
207
- off(event: string, handler: Function): void;
208
- undo(): void;
209
- redo(): void;
383
+ setLineNumbers(enabled: boolean): void;
384
+
385
+ // Metrics
210
386
  getWordCount(): number;
211
387
  getLineCount(): number;
388
+
389
+ // DOM access
390
+ getRoot(): HTMLElement;
391
+ getTextarea(): HTMLTextAreaElement;
392
+ getPreview(): HTMLElement;
393
+
394
+ // Plugins
395
+ registerPlugin(plugin: EditorPlugin): void;
396
+ unregisterPlugin(name: string): void;
397
+
398
+ // Autocomplete
399
+ registerAutoComplete(items: CompletionItem | CompletionItem[]): void;
400
+
401
+ // Theme sync
402
+ syncCodeEditorTheme(): void;
403
+
404
+ // Events
405
+ on(event: string, handler: (data?: unknown) => void): void;
406
+ off(event: string, handler: (data?: unknown) => void): void;
407
+ emit(event: string, data?: unknown): void;
408
+
409
+ // Lifecycle
212
410
  destroy(): void;
213
411
  }
214
412
  ```
215
413
 
216
- ## Events
414
+ ---
217
415
 
218
- ### `onChange` config callback (recommended)
416
+ ## Events
219
417
 
220
- Pass `onChange` directly in the config for the simplest way to listen for content changes:
418
+ ### `onChange` callback (recommended)
221
419
 
222
- ```js
420
+ ```ts
223
421
  const editor = createEditor({
224
422
  container: '#editor',
225
423
  onChange: (markdown) => {
@@ -230,81 +428,63 @@ const editor = createEditor({
230
428
 
231
429
  ### Event emitter
232
430
 
233
- For all events (including selection changes, mode changes, etc.) use the event emitter API:
234
-
235
- ```js
236
- editor.on('change', (markdown) => console.log('Content changed:', markdown));
237
- editor.on('selection-change', (sel) => console.log('Selection:', sel));
238
- editor.on('mode-change', (mode) => console.log('Mode:', mode));
239
- editor.on('render', (html) => console.log('Preview rendered'));
240
- editor.on('toolbar-action', (id) => console.log('Toolbar clicked:', id));
431
+ ```ts
432
+ editor.on('change', (markdown) => console.log('Changed:', markdown));
433
+ editor.on('selection-change', (sel) => console.log('Selection:', sel));
434
+ editor.on('mode-change', (mode) => console.log('Mode:', mode));
435
+ editor.on('render', (html) => console.log('Preview HTML ready'));
436
+ editor.on('focus', () => console.log('Editor focused'));
437
+ editor.on('blur', () => console.log('Editor blurred'));
438
+ editor.on('toolbar-action', (id) => console.log('Toolbar clicked:', id));
241
439
  ```
242
440
 
243
- ## Development
244
-
245
- > **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.
246
- >
247
- > To report a bug or request a feature, open an issue at the [GitHub Issues page](https://github.com/RadhaHariharan/syncline-mdx-editor/issues).
248
- >
249
- > ```bash
250
- > npm install @synclineapi/mdx-editor
251
- > ```
252
-
253
- Internal development commands (for maintainers only):
254
-
255
- ```bash
256
- cd syncline-mdx-editor
257
- npm install
258
- npm run dev # Start Vite dev server
259
- npm run build # Build library for production
260
- ```
441
+ ---
261
442
 
262
443
  ## Bundle Size
263
444
 
264
- Check the estimated npm publish size before publishing:
445
+ The production build targets **< 150 kB** (ESM + UMD, gzipped) when `mermaid`, `katex`, and `highlight.js` are treated as external peer dependencies.
265
446
 
266
447
  ```bash
267
448
  npm run build
268
449
  npm run size
269
450
  ```
270
451
 
271
- The core bundle (with `mermaid`, `katex`, `highlight.js` externalised) targets **< 150 kB** for both ESM and UMD outputs.
452
+ ---
272
453
 
273
454
  ## Security
274
455
 
275
- 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.
456
+ 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.
457
+
458
+ ---
276
459
 
277
460
  ## Accessibility
278
461
 
279
462
  `@synclineapi/mdx-editor` targets **WCAG 2.1 AA** compliance:
280
- - All toolbar buttons have `aria-label` and `title` attributes
463
+
464
+ - All toolbar buttons carry `aria-label` and `title` attributes
281
465
  - The editor textarea has `aria-label="Markdown editor"` and `aria-multiline="true"`
282
466
  - The preview pane has `role="region"`, `aria-label="Preview"`, and `aria-live="polite"`
283
467
  - Mode toggle buttons expose `aria-pressed` state
284
- - Toolbar dropdowns have `role="menu"` / `role="menuitem"`
285
- - A skip link is provided for keyboard users
286
- - All text meets WCAG AA color contrast ratios
468
+ - Toolbar dropdowns use `role="menu"` / `role="menuitem"`
469
+ - A skip link is provided for keyboard-only users
470
+ - All foreground/background colour pairs meet WCAG AA contrast ratios
287
471
 
288
- ## Testing
289
-
290
- ```bash
291
- npm run test # Run all tests
292
- npm run test:watch # Watch mode
293
- npm run test:coverage # Coverage report (target: 80%+)
294
- npm run test:ui # Visual test UI
295
- ```
472
+ ---
296
473
 
297
474
  ## Peer Dependencies
298
475
 
299
- `mermaid`, `katex`, and `highlight.js` are optional peer dependencies. Install them only if you use those features:
476
+ `mermaid`, `katex`, and `highlight.js` are optional. Install only the ones you need:
300
477
 
301
478
  ```bash
302
- npm install mermaid # For Mermaid diagrams
303
- npm install katex # For math formulas
304
- npm install highlight.js # Included by default for syntax highlighting
479
+ npm install mermaid # Mermaid diagrams
480
+ npm install katex # Math / formula rendering
481
+ npm install highlight.js # Syntax highlighting in code blocks
305
482
  ```
306
483
 
484
+ ---
485
+
307
486
  ## License
308
487
 
309
- MIT
488
+ Copyright © Syncline API. All rights reserved.
310
489
 
490
+ 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.
@@ -0,0 +1,5 @@
1
+ import { EditorConfig } from '../types';
2
+ export declare function resolveConfig(config: Partial<EditorConfig> & {
3
+ container: EditorConfig['container'];
4
+ }): EditorConfig;
5
+ export declare function validateConfig(config: EditorConfig): void;
@@ -0,0 +1,33 @@
1
+ import { EditorConfig, EditorMode } from '../types';
2
+ export declare const DEFAULT_MODE: EditorMode;
3
+ export declare const DEFAULT_PLACEHOLDER = "Start writing markdown...";
4
+ export declare const DEFAULT_PREVIEW_DELAY = 150;
5
+ export declare const DEFAULT_FEATURES: {
6
+ readonly lineNumbers: false;
7
+ readonly wordCount: true;
8
+ readonly lineCount: true;
9
+ readonly modeSwitcher: true;
10
+ readonly statusBar: true;
11
+ readonly autoPreview: true;
12
+ readonly previewDelay: 150;
13
+ readonly spellcheck: false;
14
+ readonly autocomplete: false;
15
+ readonly dragAndDrop: true;
16
+ readonly pasteImages: false;
17
+ readonly keyboardShortcuts: true;
18
+ readonly autoSave: false;
19
+ readonly autoSaveKey: "smdx-autosave";
20
+ readonly autofocus: false;
21
+ readonly vimMode: false;
22
+ readonly history: {
23
+ readonly maxSize: 100;
24
+ readonly debounce: 300;
25
+ };
26
+ };
27
+ export declare const DEFAULT_LAYOUT: {
28
+ readonly splitterPosition: 50;
29
+ readonly minEditorWidth: 200;
30
+ readonly minPreviewWidth: 200;
31
+ readonly resizableSplitter: true;
32
+ };
33
+ export declare function resolveDefaults(config: Partial<EditorConfig>): EditorConfig;
@@ -0,0 +1,2 @@
1
+ export { resolveConfig, validateConfig } from './EditorConfig';
2
+ export { resolveDefaults, DEFAULT_MODE, DEFAULT_PLACEHOLDER, DEFAULT_FEATURES, DEFAULT_LAYOUT } from './defaults';
@@ -0,0 +1,7 @@
1
+ export interface EditorPaneElements {
2
+ pane: HTMLDivElement;
3
+ scrollWrapper: HTMLDivElement;
4
+ gutter: HTMLDivElement;
5
+ textarea: HTMLTextAreaElement;
6
+ }
7
+ export declare function createEditorPane(id: string): EditorPaneElements;
@@ -0,0 +1,8 @@
1
+ export interface EditorRootElements {
2
+ root: HTMLDivElement;
3
+ skipLink: HTMLAnchorElement;
4
+ toolbarSlot: HTMLDivElement;
5
+ main: HTMLDivElement;
6
+ statusBarSlot: HTMLDivElement;
7
+ }
8
+ export declare function createEditorRoot(id: string): EditorRootElements;