@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.
- package/README.md +320 -140
- package/dist/core/config/EditorConfig.d.ts +5 -0
- package/dist/core/config/defaults.d.ts +33 -0
- package/dist/core/config/index.d.ts +2 -0
- package/dist/core/dom/editor-pane.d.ts +7 -0
- package/dist/core/dom/editor-root.d.ts +8 -0
- package/dist/core/dom/index.d.ts +11 -0
- package/dist/core/dom/preview-pane.d.ts +5 -0
- package/dist/core/dom/skip-link.d.ts +1 -0
- package/dist/core/dom/status-bar.d.ts +13 -0
- package/dist/core/dom/toolbar-dom.d.ts +5 -0
- package/dist/core/editor.d.ts +99 -6
- package/dist/core/mdx-themes.d.ts +3 -0
- package/dist/core/platform.d.ts +31 -0
- package/dist/core/plugin-manager.d.ts +1 -24
- package/dist/core/plugins/PluginContext.d.ts +24 -0
- package/dist/core/plugins/PluginManager.d.ts +33 -0
- package/dist/core/plugins/index.d.ts +3 -0
- package/dist/core/renderer/CodeRenderer.d.ts +4 -0
- package/dist/core/renderer/ListRenderer.d.ts +5 -0
- package/dist/core/renderer/MarkdownRenderer.d.ts +7 -0
- package/dist/core/renderer/MdxValidator.d.ts +3 -0
- package/dist/core/renderer/Renderer.d.ts +10 -0
- package/dist/core/renderer/TableRenderer.d.ts +4 -0
- package/dist/core/renderer/index.d.ts +7 -0
- package/dist/core/renderer/sanitize.d.ts +1 -0
- package/dist/core/renderer.d.ts +6 -28
- package/dist/core/toolbar.d.ts +1 -0
- package/dist/core/types.d.ts +72 -2
- package/dist/core/ui/AutocompleteController.d.ts +58 -0
- package/dist/core/ui/DropdownController.d.ts +8 -0
- package/dist/core/ui/FindReplaceController.d.ts +60 -0
- package/dist/core/ui/LineNumberController.d.ts +218 -0
- package/dist/core/ui/ModeController.d.ts +9 -0
- package/dist/core/ui/PreviewController.d.ts +13 -0
- package/dist/core/ui/ResponsiveController.d.ts +30 -0
- package/dist/core/ui/SplitterController.d.ts +31 -0
- package/dist/core/ui/StatusBarController.d.ts +16 -0
- package/dist/core/ui/ThemeController.d.ts +8 -0
- package/dist/core/ui/ToolbarController.d.ts +10 -0
- package/dist/core/ui/UIController.d.ts +43 -0
- package/dist/core/ui/index.d.ts +12 -0
- package/dist/index.d.ts +28 -1
- package/dist/plugins/basic-formatting/index.d.ts +1 -0
- package/dist/plugins/callouts/index.d.ts +1 -0
- package/dist/plugins/diagrams/index.d.ts +1 -0
- package/dist/plugins/index.d.ts +1 -11
- package/dist/plugins/layout/index.d.ts +3 -0
- package/dist/plugins/lists/index.d.ts +1 -0
- package/dist/plugins/media/index.d.ts +2 -0
- package/dist/plugins/utilities/index.d.ts +5 -0
- package/dist/style.css +1 -1
- package/dist/syncline-mdx-editor.js +8468 -1812
- package/dist/syncline-mdx-editor.umd.cjs +958 -87
- 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 @@
|
|
|
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;
|
package/dist/core/editor.d.ts
CHANGED
|
@@ -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,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
|
-
|
|
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,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,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';
|
package/dist/core/renderer.d.ts
CHANGED
|
@@ -1,28 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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';
|
package/dist/core/toolbar.d.ts
CHANGED
package/dist/core/types.d.ts
CHANGED
|
@@ -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,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
|
+
}
|