@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
@@ -0,0 +1,11 @@
1
+ export { createEditorRoot } from './editor-root';
2
+ export type { EditorRootElements } from './editor-root';
3
+ export { createEditorPane } from './editor-pane';
4
+ export type { EditorPaneElements } from './editor-pane';
5
+ export { createPreviewPane } from './preview-pane';
6
+ export type { PreviewPaneElements } from './preview-pane';
7
+ export { createToolbarDOM } from './toolbar-dom';
8
+ export type { ToolbarDOMElements } from './toolbar-dom';
9
+ export { createStatusBar } from './status-bar';
10
+ export type { StatusBarConfig, StatusBarElements } from './status-bar';
11
+ export { createSkipLink } from './skip-link';
@@ -0,0 +1,5 @@
1
+ export interface PreviewPaneElements {
2
+ pane: HTMLDivElement;
3
+ content: HTMLDivElement;
4
+ }
5
+ export declare function createPreviewPane(id: string): PreviewPaneElements;
@@ -0,0 +1 @@
1
+ export declare function createSkipLink(targetId: string): HTMLAnchorElement;
@@ -0,0 +1,13 @@
1
+ export interface StatusBarConfig {
2
+ showWordCount?: boolean;
3
+ showLineCount?: boolean;
4
+ showModeSwitcher?: boolean;
5
+ }
6
+ export interface StatusBarElements {
7
+ bar: HTMLDivElement;
8
+ wordCount: HTMLSpanElement;
9
+ lineCount: HTMLSpanElement;
10
+ charCount: HTMLSpanElement;
11
+ modeSwitcher: HTMLDivElement;
12
+ }
13
+ export declare function createStatusBar(config?: StatusBarConfig): StatusBarElements;
@@ -0,0 +1,5 @@
1
+ export interface ToolbarDOMElements {
2
+ container: HTMLDivElement;
3
+ toolbar: HTMLDivElement;
4
+ }
5
+ export declare function createToolbarDOM(): ToolbarDOMElements;
@@ -1,33 +1,116 @@
1
1
  import { EditorConfig, EditorAPI, EditorPlugin, EditorMode, SelectionState } from './types';
2
+ import { CompletionItem } from '@synclineapi/editor';
2
3
  export declare class SynclineMDXEditor implements EditorAPI {
3
4
  private root;
4
5
  private toolbarEl;
6
+ private toolbarLeft;
7
+ private modeArea;
8
+ /** Container div where SynclineEditor mounts its DOM. */
9
+ private editorHost;
10
+ /** Hidden textarea kept for getTextarea() API compatibility. */
11
+ private textarea;
5
12
  private editorPane;
6
13
  private previewPane;
7
- private textarea;
8
14
  private previewContent;
9
15
  private statusBar;
16
+ private tocContainer;
17
+ private tocCollapsed;
18
+ private splitterController;
19
+ /** The SynclineEditor instance (from editor/ folder). */
20
+ private codeEditor;
21
+ /** Watches .smdx-dark class changes to swap the code-editor theme in real time. */
22
+ private themeObserver;
10
23
  private events;
11
24
  private pluginManager;
12
25
  private toolbar;
13
26
  private renderer;
14
- private history;
15
27
  private mode;
16
28
  private config;
17
29
  private renderTimer;
18
- private historyTimer;
19
30
  private _destroyed;
20
31
  constructor(config: EditorConfig);
21
32
  private init;
33
+ /**
34
+ * Returns true only when an explicit `smdx-dark` class is present on the
35
+ * editor root, a parent element, or the document/body.
36
+ * Deliberately ignores OS-level prefers-color-scheme so that the theme
37
+ * follows the toolbar toggle button, not the system setting.
38
+ */
39
+ private isDarkMode;
40
+ /**
41
+ * Apply the correct light/dark code-editor theme AND token colours.
42
+ * Called automatically whenever the `.smdx-dark` class changes on any
43
+ * ancestor element. Can also be called programmatically when the host app
44
+ * switches themes via means other than a CSS class toggle (e.g. data-theme
45
+ * attribute, CSS variable override, or a custom theme API).
46
+ */
47
+ syncCodeEditorTheme(): void;
48
+ /** Observe class / data-theme mutations on the root / document so we can
49
+ * swap code-editor themes live.
50
+ *
51
+ * Walks every ancestor from `this.root` up to `<html>` inclusive so that
52
+ * toggling `.smdx-dark` OR `data-theme` on ANY containing element is caught
53
+ * and forwarded to `syncCodeEditorTheme()`. This covers:
54
+ * - CSS class toggles (.smdx-dark on root, body, or html)
55
+ * - data-theme attribute patterns (Next.js, Nuxt, Tailwind dark-mode)
56
+ * - app-shell / modal wrapper patterns
57
+ */
58
+ private setupThemeObserver;
59
+ /** Completions registered directly by the consumer via `registerAutoComplete()`. */
60
+ private _userCompletions;
61
+ private mountCodeEditor;
62
+ /**
63
+ * Rebuilds the full completions list pushed into the SynclineEditor.
64
+ *
65
+ * Merge order:
66
+ * 1. Built-in MDX completions — tags, attributes
67
+ * 2. Built-in MDX snippets — toolbar-matched snippet bodies
68
+ * 3. Plugin completions — each registered plugin's items
69
+ * 4. User completions — items added via `registerAutoComplete()`
70
+ *
71
+ * Called automatically after every `registerPlugin` / `unregisterPlugin`
72
+ * and every `registerAutoComplete()` call.
73
+ */
74
+ private _refreshCompletions;
75
+ /**
76
+ * Register one or more custom autocomplete items on top of the built-in
77
+ * MDX completions. Items appear in the popup immediately and persist for
78
+ * the lifetime of the editor instance.
79
+ *
80
+ * Use `kind: 'snip'` with a `body` template for snippet expansion — Tab
81
+ * or clicking the item inserts the full body with the cursor placed at
82
+ * the first `$1` tab stop.
83
+ *
84
+ * @example
85
+ * ```ts
86
+ * editor.registerAutoComplete([
87
+ * {
88
+ * label: 'mycard',
89
+ * kind: 'snip',
90
+ * detail: '<MyCard> component',
91
+ * description: 'Inserts a custom card block.',
92
+ * body: '<MyCard title="$1">\n $2\n</MyCard>',
93
+ * },
94
+ * { label: 'MyCard', kind: 'cls', detail: 'component' },
95
+ * ]);
96
+ * ```
97
+ */
98
+ registerAutoComplete(items: CompletionItem | CompletionItem[]): void;
22
99
  private buildDOM;
100
+ /** Convert a flat character offset to { row, col } in the document. */
101
+ private offsetToPos;
102
+ /** Convert { row, col } to a flat character offset. */
103
+ private posToOffset;
23
104
  private registerPlugins;
24
105
  private getDefaultToolbar;
25
- private onInput;
26
- private onKeyDown;
27
106
  private matchShortcut;
28
- private onSelectionChange;
29
107
  private scheduleRender;
108
+ private getCursorPosition;
30
109
  private updateStatusBar;
110
+ private updateModeButtons;
111
+ private buildTocPanel;
112
+ refreshToc(): void;
113
+ toggleToc(): void;
31
114
  getValue(): string;
32
115
  setValue(value: string): void;
33
116
  insertText(text: string): void;
@@ -57,5 +140,15 @@ export declare class SynclineMDXEditor implements EditorAPI {
57
140
  insertAt(position: number, text: string): void;
58
141
  getWordCount(): number;
59
142
  getLineCount(): number;
143
+ setLineNumbers(enabled: boolean): void;
144
+ jumpToLine(lineNumber: number): void;
60
145
  private applyTheme;
146
+ /**
147
+ * Directly set a `ThemeDefinition` on the embedded code editor.
148
+ * Use this when you need to apply a theme that cannot be expressed via
149
+ * CSS class toggles — e.g. a fully custom colour palette loaded at runtime.
150
+ * Passing `null` reverts the code editor to the default MDX light/dark theme
151
+ * derived from the current `.smdx-dark` class state.
152
+ */
153
+ setCodeEditorTheme(theme: import('@synclineapi/editor').ThemeDefinition | null): void;
61
154
  }
@@ -0,0 +1,3 @@
1
+ import { ThemeDefinition } from '@synclineapi/editor';
2
+ export declare const THEME_MDX_DARK: ThemeDefinition;
3
+ export declare const THEME_MDX_LIGHT: ThemeDefinition;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Platform detection utilities for cross-platform shortcut label formatting.
3
+ *
4
+ * Shortcut labels are stored in Mac notation (⌘, ⇧, ⌥).
5
+ * On Windows/Linux these Unicode symbols are converted to readable text
6
+ * (Ctrl+, Shift+, Alt+) so users see the correct key names for their OS.
7
+ */
8
+ /**
9
+ * Returns true when running on macOS.
10
+ * Tries the modern `navigator.userAgentData.platform` first, then falls back
11
+ * to the legacy `navigator.platform` and `navigator.userAgent`.
12
+ */
13
+ export declare function isMac(): boolean;
14
+ /**
15
+ * Converts a shortcut label to the correct format for the current platform.
16
+ *
17
+ * On macOS, text modifiers are converted to Unicode symbols (e.g. "Ctrl+B" → "⌘B").
18
+ * On Windows/Linux, Unicode symbols are replaced with their keyboard text
19
+ * equivalents (e.g. "⌘B" → "Ctrl+B", "⌘⇧X" → "Ctrl+Shift+X").
20
+ *
21
+ * Substitutions applied on macOS (case-insensitive):
22
+ * Ctrl+ → ⌘
23
+ * Shift+ → ⇧
24
+ * Alt+ → ⌥
25
+ *
26
+ * Substitutions applied on Windows/Linux:
27
+ * ⌘ → Ctrl+
28
+ * ⇧ → Shift+
29
+ * ⌥ → Alt+
30
+ */
31
+ export declare function formatShortcutLabel(label: string): string;
@@ -1,24 +1 @@
1
- import { EditorPlugin, ToolbarItemConfig, ShortcutConfig, RendererConfig, ParserConfig, EditorAPI } from './types';
2
- import { EventEmitter } from './events';
3
- export declare class PluginManager {
4
- private editorApi;
5
- private plugins;
6
- private toolbarItems;
7
- private shortcuts;
8
- private renderers;
9
- private parsers;
10
- private styleElements;
11
- private events;
12
- constructor(editorApi: EditorAPI, events: EventEmitter);
13
- register(plugin: EditorPlugin): Promise<void>;
14
- unregister(name: string): void;
15
- getToolbarItem(id: string): ToolbarItemConfig | undefined;
16
- getAllToolbarItems(): Map<string, ToolbarItemConfig>;
17
- getShortcuts(): ShortcutConfig[];
18
- getRenderers(): RendererConfig[];
19
- getParsers(): ParserConfig[];
20
- hasPlugin(name: string): boolean;
21
- private createContext;
22
- private injectStyles;
23
- destroy(): void;
24
- }
1
+ export { PluginManager } from './plugins/PluginManager';
@@ -0,0 +1,24 @@
1
+ import { EditorAPI, ToolbarItemConfig, ShortcutConfig, RendererConfig, ParserConfig, EventHandler } from '../types';
2
+ export interface PluginContextOptions {
3
+ editor: EditorAPI;
4
+ registerToolbarItem: (item: ToolbarItemConfig) => void;
5
+ registerShortcut: (shortcut: ShortcutConfig) => void;
6
+ registerRenderer: (renderer: RendererConfig) => void;
7
+ registerParser: (parser: ParserConfig) => void;
8
+ injectStyles: (css: string) => void;
9
+ emit: (event: string, data?: unknown) => void;
10
+ on: (event: string, handler: EventHandler) => void;
11
+ off: (event: string, handler: EventHandler) => void;
12
+ }
13
+ export declare class PluginContext {
14
+ readonly editor: EditorAPI;
15
+ readonly registerToolbarItem: (item: ToolbarItemConfig) => void;
16
+ readonly registerShortcut: (shortcut: ShortcutConfig) => void;
17
+ readonly registerRenderer: (renderer: RendererConfig) => void;
18
+ readonly registerParser: (parser: ParserConfig) => void;
19
+ readonly injectStyles: (css: string) => void;
20
+ readonly emit: (event: string, data?: unknown) => void;
21
+ readonly on: (event: string, handler: EventHandler) => void;
22
+ readonly off: (event: string, handler: EventHandler) => void;
23
+ constructor(options: PluginContextOptions);
24
+ }
@@ -0,0 +1,33 @@
1
+ import { EditorPlugin, ToolbarItemConfig, ShortcutConfig, RendererConfig, ParserConfig, EditorAPI, CompletionItem } from '../types';
2
+ import { EventEmitter } from '../events';
3
+ export declare class PluginManager {
4
+ private editorApi;
5
+ private plugins;
6
+ private toolbarItems;
7
+ private shortcuts;
8
+ private renderers;
9
+ private parsers;
10
+ private styleElements;
11
+ private events;
12
+ /** All completion items contributed by plugins, keyed by plugin name. */
13
+ private completionsByPlugin;
14
+ constructor(editorApi: EditorAPI, events: EventEmitter);
15
+ register(plugin: EditorPlugin): Promise<void>;
16
+ unregister(name: string): void;
17
+ getToolbarItem(id: string): ToolbarItemConfig | undefined;
18
+ getAllToolbarItems(): Map<string, ToolbarItemConfig>;
19
+ getShortcuts(): ShortcutConfig[];
20
+ getRenderers(): RendererConfig[];
21
+ getParsers(): ParserConfig[];
22
+ /**
23
+ * Returns the flat list of all completion items contributed by every
24
+ * currently-registered plugin (both static `completions` declarations and
25
+ * items added dynamically via `ctx.registerCompletion()`).
26
+ */
27
+ getCompletions(): CompletionItem[];
28
+ hasPlugin(name: string): boolean;
29
+ getPlugin(name: string): EditorPlugin | undefined;
30
+ private createContext;
31
+ private injectStyles;
32
+ destroy(): void;
33
+ }
@@ -0,0 +1,3 @@
1
+ export { PluginManager } from './PluginManager';
2
+ export { PluginContext } from './PluginContext';
3
+ export type { PluginContextOptions } from './PluginContext';
@@ -0,0 +1,4 @@
1
+ export declare class CodeRenderer {
2
+ private escapeHtml;
3
+ renderBlock(lang: string, filename: string | undefined, code: string): string;
4
+ }
@@ -0,0 +1,5 @@
1
+ export declare class ListRenderer {
2
+ private isListItem;
3
+ private buildNestedList;
4
+ render(text: string): string;
5
+ }
@@ -0,0 +1,7 @@
1
+ export declare class MarkdownRenderer {
2
+ private codeRenderer;
3
+ private tableRenderer;
4
+ private listRenderer;
5
+ private escapeHtml;
6
+ render(source: string): string;
7
+ }
@@ -0,0 +1,3 @@
1
+ export declare class MdxValidator {
2
+ validate(source: string): string[];
3
+ }
@@ -0,0 +1,10 @@
1
+ import { RendererConfig, ParserConfig } from '../types';
2
+ export declare class Renderer {
3
+ private renderers;
4
+ private parsers;
5
+ private markdownRenderer;
6
+ private mdxValidator;
7
+ setRenderers(renderers: RendererConfig[]): void;
8
+ setParsers(parsers: ParserConfig[]): void;
9
+ render(source: string): Promise<string>;
10
+ }
@@ -0,0 +1,4 @@
1
+ export declare class TableRenderer {
2
+ private parseTableRow;
3
+ render(html: string): string;
4
+ }
@@ -0,0 +1,7 @@
1
+ export { Renderer } from './Renderer';
2
+ export { MarkdownRenderer } from './MarkdownRenderer';
3
+ export { CodeRenderer } from './CodeRenderer';
4
+ export { TableRenderer } from './TableRenderer';
5
+ export { ListRenderer } from './ListRenderer';
6
+ export { MdxValidator } from './MdxValidator';
7
+ export { sanitizeHtml } from './sanitize';
@@ -0,0 +1 @@
1
+ export { sanitizeHtml } from '../sanitize';
@@ -1,28 +1,6 @@
1
- import { RendererConfig, ParserConfig } from './types';
2
- /**
3
- * The Renderer transforms markdown+MDX source into preview HTML.
4
- * It runs custom parsers first, then applies marked for standard markdown,
5
- * then runs custom renderers for extended blocks.
6
- */
7
- export declare class Renderer {
8
- private renderers;
9
- private parsers;
10
- setRenderers(renderers: RendererConfig[]): void;
11
- setParsers(parsers: ParserConfig[]): void;
12
- render(source: string): Promise<string>;
13
- private applyParsers;
14
- private applyRenderers;
15
- /**
16
- * Lightweight built-in markdown renderer.
17
- * Handles standard markdown without external deps.
18
- */
19
- private renderMarkdown;
20
- private renderTables;
21
- private parseTableRow;
22
- private escapeHtml;
23
- /**
24
- * Validate MDX component tags — detect unclosed or mismatched tags.
25
- * Only checks PascalCase tags (MDX components), not standard HTML.
26
- */
27
- private validateMdxTags;
28
- }
1
+ export { Renderer } from './renderer/Renderer';
2
+ export { MarkdownRenderer } from './renderer/MarkdownRenderer';
3
+ export { CodeRenderer } from './renderer/CodeRenderer';
4
+ export { TableRenderer } from './renderer/TableRenderer';
5
+ export { ListRenderer } from './renderer/ListRenderer';
6
+ export { MdxValidator } from './renderer/MdxValidator';
@@ -17,6 +17,7 @@ export declare class Toolbar {
17
17
  private renderGroup;
18
18
  private renderDropdownGroup;
19
19
  private renderDropdownItem;
20
+ private positionMenu;
20
21
  private renderSubmenu;
21
22
  private createButton;
22
23
  private createMenuButton;
@@ -1,3 +1,5 @@
1
+ import { CompletionItem, CompletionKind } from '@synclineapi/editor';
2
+ export type { CompletionItem, CompletionKind };
1
3
  export interface EditorConfig {
2
4
  container: HTMLElement | string;
3
5
  /** Initial markdown content */
@@ -14,8 +16,6 @@ export interface EditorConfig {
14
16
  placeholder?: string;
15
17
  /** Read-only mode */
16
18
  readOnly?: boolean;
17
- /** Enable scroll sync between editor and preview */
18
- scrollSync?: boolean;
19
19
  /** Custom renderer overrides */
20
20
  renderers?: Record<string, RendererFn>;
21
21
  /** Locale / i18n overrides */
@@ -47,6 +47,29 @@ export interface EditorPlugin {
47
47
  styles?: string;
48
48
  /** Plugin dependencies (names) */
49
49
  dependencies?: string[];
50
+ /**
51
+ * Autocomplete items this plugin contributes to the code editor.
52
+ *
53
+ * These are merged with the built-in MDX completions so that anything
54
+ * a plugin can insert via the toolbar is also reachable via autocomplete.
55
+ *
56
+ * Use `kind: 'snip'` with a `body` template to get Tab-trigger snippet
57
+ * expansion that matches the toolbar insertion exactly.
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * completions: [
62
+ * {
63
+ * label: 'myblock',
64
+ * kind: 'snip',
65
+ * detail: '<MyBlock> component',
66
+ * description: 'Inserts a MyBlock component with optional title.',
67
+ * body: '<MyBlock title="$1Title">\n $2Content here\n</MyBlock>',
68
+ * },
69
+ * ]
70
+ * ```
71
+ */
72
+ completions?: CompletionItem[];
50
73
  }
51
74
  export interface PluginContext {
52
75
  /** The editor instance */
@@ -61,6 +84,22 @@ export interface PluginContext {
61
84
  registerParser(parser: ParserConfig): void;
62
85
  /** Inject CSS */
63
86
  injectStyles(css: string): void;
87
+ /**
88
+ * Register an autocomplete completion item for the code editor.
89
+ * Equivalent to declaring `completions` on the plugin definition, but
90
+ * useful when the items are built dynamically inside `init()`.
91
+ *
92
+ * @example
93
+ * ```ts
94
+ * ctx.registerCompletion({
95
+ * label: 'myblock',
96
+ * kind: 'snip',
97
+ * detail: '<MyBlock> component',
98
+ * body: '<MyBlock title="$1">\n $2\n</MyBlock>',
99
+ * });
100
+ * ```
101
+ */
102
+ registerCompletion(item: CompletionItem): void;
64
103
  /** Emit a plugin event */
65
104
  emit(event: string, data?: unknown): void;
66
105
  /** Listen to events */
@@ -166,6 +205,37 @@ export interface EditorAPI {
166
205
  getWordCount(): number;
167
206
  /** Get line count */
168
207
  getLineCount(): number;
208
+ /** Show or hide the line-number gutter (virtual rendering) */
209
+ setLineNumbers(enabled: boolean): void;
210
+ /** Scroll the editor to make the given 1-indexed line visible */
211
+ jumpToLine(lineNumber: number): void;
212
+ /**
213
+ * Register one or more custom autocomplete items on top of the built-in
214
+ * MDX completions. Items are merged immediately and appear in the popup
215
+ * the next time the user types.
216
+ *
217
+ * Use `kind: 'snip'` with a `body` template for snippet expansion — the
218
+ * Tab key or clicking the item inserts the full body with cursor placed
219
+ * at the first `$1` tab stop.
220
+ *
221
+ * Call multiple times to accumulate items. The full list (built-ins +
222
+ * plugin items + items registered here) is always deduplicated by label.
223
+ *
224
+ * @example
225
+ * ```ts
226
+ * editor.registerAutoComplete([
227
+ * {
228
+ * label: 'mycard',
229
+ * kind: 'snip',
230
+ * detail: '<MyCard> component',
231
+ * description: 'Inserts a custom card block.',
232
+ * body: '<MyCard title="$1">\n $2\n</MyCard>',
233
+ * },
234
+ * { label: 'MyCard', kind: 'cls', detail: 'component' },
235
+ * ]);
236
+ * ```
237
+ */
238
+ registerAutoComplete(items: CompletionItem | CompletionItem[]): void;
169
239
  }
170
240
  export interface SelectionState {
171
241
  start: number;
@@ -0,0 +1,58 @@
1
+ /**
2
+ * AutocompleteController — MDX/Markdown-aware autocomplete popup.
3
+ *
4
+ * Adapted from the vreditor.html autocomplete system. Shows suggestions for:
5
+ * - MDX component tags (Accordion, Card, Tabs, etc.)
6
+ * - Markdown syntax (headings, bold, lists, code blocks, etc.)
7
+ * - HTML tags
8
+ * - Document-based word completions (words already present in the editor)
9
+ *
10
+ * The popup is positioned relative to the cursor and supports keyboard
11
+ * navigation (Arrow Up/Down, Tab/Enter to accept, Escape to dismiss).
12
+ */
13
+ /** A single autocomplete suggestion. */
14
+ export interface AutocompleteItem {
15
+ /** Display label shown in the popup */
16
+ label: string;
17
+ /** Category kind — drives the badge icon */
18
+ kind: 'tag' | 'md' | 'html' | 'var' | 'snippet';
19
+ /** Short description shown next to label */
20
+ detail: string;
21
+ /** Text to insert (defaults to label if omitted) */
22
+ insertText?: string;
23
+ }
24
+ export declare class AutocompleteController {
25
+ private popup;
26
+ private items;
27
+ private selected;
28
+ private prefix;
29
+ private startPos;
30
+ private textarea;
31
+ private editorRoot;
32
+ private _onAccept;
33
+ /** Additional items registered at runtime by plugins. */
34
+ private extraItems;
35
+ constructor(textarea: HTMLTextAreaElement, editorRoot: HTMLElement);
36
+ /** Register additional completion items (e.g. from plugins). */
37
+ registerItems(items: AutocompleteItem[]): void;
38
+ /** Whether the popup is currently visible. */
39
+ isVisible(): boolean;
40
+ /** Trigger autocomplete based on current cursor context. */
41
+ trigger(): void;
42
+ /** Move selection up or down. */
43
+ move(dir: number): void;
44
+ /** Accept the current selection. */
45
+ accept(): void;
46
+ /** Set a callback to fire after an item is accepted (for triggering onInput). */
47
+ onAccept(fn: () => void): void;
48
+ /** Handle keyboard events. Returns true if the event was consumed. */
49
+ handleKeyDown(e: KeyboardEvent): boolean;
50
+ /** Hide the autocomplete popup. */
51
+ hide(): void;
52
+ /** Render the popup items. */
53
+ private render;
54
+ /** Position the popup near the cursor. */
55
+ private position;
56
+ /** Clean up DOM. */
57
+ destroy(): void;
58
+ }
@@ -0,0 +1,8 @@
1
+ export declare class DropdownController {
2
+ private activeDropdown;
3
+ private documentClickHandler;
4
+ constructor();
5
+ toggle(menu: HTMLElement, wrapper: HTMLElement): void;
6
+ closeAll(): void;
7
+ destroy(): void;
8
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * FindReplaceController — Find & Replace panel for the editor.
3
+ *
4
+ * Adapted from the vreditor.html find/replace system. Provides:
5
+ * - Ctrl+F to open find, Ctrl+H to open find+replace
6
+ * - Case-sensitive toggle
7
+ * - Regex search toggle
8
+ * - Whole-word match toggle
9
+ * - Navigate between matches (Enter / Shift+Enter or arrows)
10
+ * - Replace current / Replace all
11
+ * - Escape to close
12
+ */
13
+ export declare class FindReplaceController {
14
+ private panel;
15
+ private findInput;
16
+ private replaceInput;
17
+ private replaceRow;
18
+ private countLabel;
19
+ private caseSensitiveBtn;
20
+ private regexBtn;
21
+ private wholeWordBtn;
22
+ private textarea;
23
+ private editorRoot;
24
+ private matches;
25
+ private currentMatch;
26
+ private caseSensitive;
27
+ private useRegex;
28
+ private wholeWord;
29
+ private showReplace;
30
+ private _onInputCallback;
31
+ constructor(textarea: HTMLTextAreaElement, editorRoot: HTMLElement);
32
+ /** Set a callback to fire after a replace operation (for triggering onInput). */
33
+ onInput(fn: () => void): void;
34
+ /** Open the find panel. If `withReplace` is true, also show the replace row. */
35
+ open(withReplace?: boolean): void;
36
+ /** Close the find panel. */
37
+ close(): void;
38
+ /** Whether the panel is open. */
39
+ isOpen(): boolean;
40
+ /** Run the search. */
41
+ private search;
42
+ /** Go to next match. */
43
+ private next;
44
+ /** Go to previous match. */
45
+ private prev;
46
+ /** Select the current match in the textarea. */
47
+ private highlightCurrent;
48
+ /** Update the match count label. */
49
+ private updateCount;
50
+ /** Replace the current match. */
51
+ private replaceCurrent;
52
+ /** Replace all matches. */
53
+ private replaceAll;
54
+ /** Create a toggle button. */
55
+ private createToggle;
56
+ /** Create an action button. */
57
+ private createActionBtn;
58
+ /** Clean up. */
59
+ destroy(): void;
60
+ }