@owomark/react 0.1.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 ADDED
@@ -0,0 +1,363 @@
1
+ # OwoMark
2
+
3
+ A self-contained Markdown editor built on a single-layer `contenteditable` surface.
4
+
5
+ Official package: `@owomark/react`. The flat name `owomark-react` is reserved only as a compatibility redirect and should not be used for new installs.
6
+
7
+ Four packages:
8
+
9
+ | Package | Role |
10
+ |---|---|
11
+ | `@owomark/core` | Framework-agnostic editor engine (document model, input handling, commands, shared state) |
12
+ | `@owomark/view` | View engine + preview rendering (DOM events, active block proxy, DOM patching, render cache) |
13
+ | `@owomark/react` | React components (`OwoMarkEditor`, `OwoMarkPreview`) and shared state hooks |
14
+ | (theme built into `@owomark/view`) | Light / dark CSS theme presets and token definitions |
15
+
16
+ ## Quick Start (React)
17
+
18
+ ```tsx
19
+ import { OwoMarkEditor } from '@owomark/react';
20
+ import '@owomark/view/style.css';
21
+
22
+ function App() {
23
+ const [md, setMd] = useState('# Hello\n\nStart typing...');
24
+
25
+ return (
26
+ <OwoMarkEditor
27
+ value={md}
28
+ onChange={setMd}
29
+ config={{ theme: 'light', enableSideAnnotation: true }}
30
+ placeholder="Write something..."
31
+ />
32
+ );
33
+ }
34
+ ```
35
+
36
+ ### Props
37
+
38
+ | Prop | Type | Default | Description |
39
+ |---|---|---|---|
40
+ | `value` | `string` | — | Controlled markdown value |
41
+ | `defaultValue` | `string` | — | Uncontrolled initial value |
42
+ | `onChange` | `(markdown: string) => void` | — | Called on content change |
43
+ | `onSelectionChange` | `(selection: OwoMarkSelection) => void` | — | Cursor/selection updates |
44
+ | `onCompositionStateChange` | `(active: boolean) => void` | — | IME composition state |
45
+ | `onScroll` | `React.UIEventHandler<HTMLDivElement>` | — | Forwards scroll events from the root editor container |
46
+ | `readOnly` | `boolean` | `false` | Disable editing |
47
+ | `placeholder` | `string` | — | Empty-state placeholder text |
48
+ | `theme` | `'light' \| 'dark' \| string` | `'light'` | Theme preset or custom class |
49
+ | `themeClassName` | `string` | — | Additional theme class |
50
+ | `className` | `string` | — | Extra CSS class on the root element |
51
+ | `commandsRef` | `Ref<OwoMarkCommands>` | — | Imperative command handle |
52
+ | `coreRef` | `Ref<OwoMarkCore \| null>` | — | Direct access to the underlying OwoMarkCore instance |
53
+ | `controller` | `OwoMarkSharedStateController` | — | Auto-connects editor to shared state (recommended for split editor) |
54
+ | `indentMode` | `'auto' \| '2' \| '4'` | `'auto'` | Tab indent width |
55
+ | `config` | `OwoMarkEditorConfig` | — | Unified editor config (`indentMode`, `enableSideAnnotation`, `enableMath`, `theme`) |
56
+ | `ariaLabel` | `string` | — | Accessibility label |
57
+
58
+ When `config` is provided, it becomes the unified configuration surface for editor behavior:
59
+
60
+ ```tsx
61
+ <OwoMarkEditor
62
+ value={md}
63
+ onChange={setMd}
64
+ config={{
65
+ theme: 'light',
66
+ indentMode: '2',
67
+ enableSideAnnotation: true,
68
+ enableMath: true,
69
+ }}
70
+ />
71
+ ```
72
+
73
+ ### Imperative Commands
74
+
75
+ ```tsx
76
+ const cmds = useRef<OwoMarkCommands>(null);
77
+
78
+ <OwoMarkEditor commandsRef={cmds} ... />
79
+
80
+ // Later:
81
+ cmds.current.toggleBold();
82
+ cmds.current.toggleItalic();
83
+ cmds.current.insertLink('https://example.com');
84
+ cmds.current.insertCodeFence('ts');
85
+ cmds.current.undo();
86
+ cmds.current.redo();
87
+ cmds.current.getMarkdown();
88
+ cmds.current.setMarkdown('# New content');
89
+ cmds.current.replaceMarkdown('# New content', { anchor: 13, focus: 13 });
90
+ cmds.current.focus();
91
+ ```
92
+
93
+ Use `replaceMarkdown()` when the host needs to replace the document and also control the resulting selection in one atomic step, such as toolbar-driven inserts.
94
+
95
+ ## Shared State + Preview
96
+
97
+ For a split editor with synchronized incremental preview:
98
+
99
+ ```tsx
100
+ import {
101
+ OwoMarkEditor,
102
+ OwoMarkPreview,
103
+ useOwoMarkSharedState,
104
+ } from '@owomark/react';
105
+ import type { PreviewBlock, PreviewRenderContext } from '@owomark/react';
106
+
107
+ function SplitEditor() {
108
+ const controller = useOwoMarkSharedState({ initialMarkdown: '# Hello' });
109
+
110
+ return (
111
+ <div style={{ display: 'flex' }}>
112
+ <OwoMarkEditor
113
+ controller={controller}
114
+ theme="light"
115
+ placeholder="Write something..."
116
+ />
117
+ <OwoMarkPreview
118
+ state={controller}
119
+ themeKey="vitesse-light"
120
+ className="preview-panel"
121
+ />
122
+ </div>
123
+ );
124
+ }
125
+ ```
126
+
127
+ The `controller` prop on `OwoMarkEditor` automatically connects the editor to the shared state controller. The editor drives all state updates (content, selection, composition) directly — no manual `onChange` → `setMarkdown` bridging needed.
128
+
129
+ For hosts that still need `onChange` callbacks (e.g. for local draft persistence), they work alongside `controller`:
130
+
131
+ ```tsx
132
+ <OwoMarkEditor
133
+ controller={controller}
134
+ onChange={(md) => saveDraft(md)}
135
+ />
136
+ ```
137
+
138
+ ### Custom Block Renderer
139
+
140
+ Provide a host-side rendering function for full Markdown fidelity (GFM, math, syntax highlighting):
141
+
142
+ ```tsx
143
+ async function renderBlock(
144
+ block: PreviewBlock,
145
+ ctx: PreviewRenderContext,
146
+ ): Promise<string> {
147
+ const result = await myUnifiedPipeline.process(block.raw);
148
+ return result.toString();
149
+ }
150
+
151
+ <OwoMarkPreview state={controller} renderBlock={renderBlock} themeKey="vitesse-light" />
152
+ ```
153
+
154
+ ## Side Annotation
155
+
156
+ OwoMark supports right-side annotations for preview rendering and editor workflows. The feature is enabled by default and can be turned off with `config.enableSideAnnotation = false`.
157
+
158
+ ```tsx
159
+ <OwoMarkEditor
160
+ value={md}
161
+ onChange={setMd}
162
+ config={{ enableSideAnnotation: false }}
163
+ />
164
+ ```
165
+
166
+ With the flag disabled:
167
+
168
+ - side-annotation syntax is kept as plain Markdown text
169
+ - preview does not render side annotation layout
170
+ - the `/side` slash command is hidden
171
+ - pressing Enter after an annotated block does not auto-insert `(>+)`
172
+
173
+ ### Syntax Quick Reference
174
+
175
+ | Use case | Syntax |
176
+ |---|---|
177
+ | Single block annotation | `Text (>} note)` |
178
+ | Multi-block continuation (2-3 blocks) | `A (>} group)` + `B (>+)` |
179
+ | Container annotation (4+ blocks) | `:::side` + `(>} note)` + blocks + `:::` |
180
+ | Long-form note reference | `:::side [>id] ... :::` + `[>id]: {type=}} ...` |
181
+
182
+ ### Type Table
183
+
184
+ | Symbol | Meaning | Example |
185
+ |---|---|---|
186
+ | `:` | Plain note | `(>: note)` |
187
+ | `}` | Right brace | `(>} grouped note)` |
188
+ | `{` | Left brace | `(>{ expanded note)` |
189
+ | `]` | Right bracket | `(>] range)` |
190
+ | `[` | Left bracket | `(>[ range)` |
191
+ | `|` | Vertical line | `(>| comment)` |
192
+ | `-` | Dash | `(>- remark)` |
193
+ | `->` | Thin arrow | `(>-> conclusion)` |
194
+ | `=>` | Fat arrow | `(>=> therefore)` |
195
+ | `~>` | Wave arrow | `(>~> related)` |
196
+ | `!` | Warning | `(>! caution)` |
197
+ | `?` | Question | `(>? todo)` |
198
+
199
+ ### Examples
200
+
201
+ Single block:
202
+
203
+ ```md
204
+ 白菜 (>} 都是蔬菜)
205
+ ```
206
+
207
+ Continuation chain:
208
+
209
+ ```md
210
+ 香蕉 (>} 都是水果)
211
+ 菠萝 (>+)
212
+ 苹果 (>+)
213
+ ```
214
+
215
+ Container form:
216
+
217
+ ```md
218
+ :::side
219
+ (>! 高危操作)
220
+
221
+ 删除数据库前必须先备份。
222
+ 确认备份可恢复后再执行破坏性操作。
223
+ :::
224
+ ```
225
+
226
+ Reference form:
227
+
228
+ ```md
229
+ :::side [>cook-tip]
230
+ 炒菜时油温很重要。
231
+ 热锅凉油是基本功。
232
+ :::
233
+
234
+ [>cook-tip]: {type=}}
235
+ 烹饪小贴士:油温过高会产生油烟,
236
+ 日常烹饪建议控制在合适范围内。
237
+ ```
238
+
239
+ ### Notes
240
+
241
+ - Side annotations are right-side only; there is no left-column layout mode.
242
+ - `(>...)` must appear at the end of the block to be recognized.
243
+ - `(>+)` is a continuation marker only. It must follow a previous annotated sibling block.
244
+ - On narrow viewports, side annotations fall back to inline callout blocks under the anchor content.
245
+
246
+ ### Shared State Hooks
247
+
248
+ | Hook | Description |
249
+ |------|-------------|
250
+ | `useOwoMarkSharedState(options?)` | Creates a persistent `OwoMarkSharedStateController` (survives re-renders) |
251
+ | `useSharedStateSnapshot(controller)` | Subscribes via `useSyncExternalStore`, returns current `OwoMarkSharedState` |
252
+
253
+ ### `<OwoMarkPreview>` Props
254
+
255
+ | Prop | Type | Description |
256
+ |------|------|-------------|
257
+ | `state` | `OwoMarkSharedStateStore` | Shared state store (e.g. `controller` from `useOwoMarkSharedState`) |
258
+ | `themeKey` | `string` | Theme identifier for rendering and cache keying |
259
+ | `className` | `string` | CSS class for the preview root |
260
+ | `renderBlock` | `(block, context) => Promise<string>` | Custom per-block Markdown renderer |
261
+ | `registry` | `PreviewRendererRegistry` | Custom renderer registry (e.g. Mermaid) |
262
+ | `viewportFirst` | `boolean` | Prioritize rendering visible blocks first |
263
+ | `onContentUpdate` | `() => void` | Called after every DOM mutation — including idle backfill and deferred renders (for scroll sync) |
264
+ | `ariaLabel` | `string` | Accessibility label |
265
+
266
+ ## CSS Setup
267
+
268
+ ### Minimal (use package presets)
269
+
270
+ ```ts
271
+ import '@owomark/view/style.css'; // includes mdx-components.css + owomark.css + side-annotation.css + slash-menu.css + light.css + dark.css
272
+ ```
273
+
274
+ ### Selective imports
275
+
276
+ ```ts
277
+ import '@owomark/view/owomark.css'; // base editor styles (required)
278
+ import '@owomark/view/light.css'; // light preset tokens
279
+ // import '@owomark/view/dark.css'; // dark preset tokens
280
+ ```
281
+
282
+ ### Third-party theme layering
283
+
284
+ ```ts
285
+ import '@owomark/view/style.css';
286
+ import 'owomark-theme-acme/style.css';
287
+ ```
288
+
289
+ ```tsx
290
+ <OwoMarkEditor
291
+ value={md}
292
+ onChange={setMd}
293
+ theme="owo-theme-acme"
294
+ />
295
+ ```
296
+
297
+ The theme package should only override `--owo-*` tokens and optional visual details. It should not duplicate `@owomark/view` structural CSS. See the `@owomark/view` README for the authoring contract of `owomark-theme-xxx`.
298
+
299
+ ## Host Theme Mapping
300
+
301
+ OwoMark uses `--owo-*` CSS custom properties for all visual styles. To integrate with your design system, map your variables to OwoMark tokens on a wrapper element:
302
+
303
+ ```css
304
+ .my-editor-wrapper {
305
+ /* Surface */
306
+ --owo-editor-bg: var(--app-surface);
307
+ --owo-editor-text: var(--app-text-primary);
308
+ --owo-editor-heading: var(--app-text-strong);
309
+
310
+ /* Interaction */
311
+ --owo-editor-caret: var(--app-text-strong);
312
+ --owo-editor-link: var(--app-brand);
313
+
314
+ /* Border */
315
+ --owo-editor-border: var(--app-border);
316
+
317
+ /* Override default chrome if embedding */
318
+ & .owo-editor-root {
319
+ border: none;
320
+ padding: 0;
321
+ background: transparent;
322
+ }
323
+ }
324
+ ```
325
+
326
+ See `docs/specs/owomark-theme-tokens.md` for the full token list.
327
+
328
+ ## Standalone Usage (no React)
329
+
330
+ Use `@owomark/core` + `@owomark/view` for a framework-free DOM editor:
331
+
332
+ ```ts
333
+ import { createOwoMarkCore } from '@owomark/core';
334
+ import { createOwoMarkView } from '@owomark/view';
335
+
336
+ const core = createOwoMarkCore({ initialMarkdown: '# Hello' });
337
+ const view = createOwoMarkView(core, document.getElementById('editor')!);
338
+
339
+ core.onChange((markdown) => console.log(markdown));
340
+
341
+ // Cleanup
342
+ view.destroy();
343
+ core.destroy();
344
+ ```
345
+
346
+ Or use `createOwoMarkCore()` directly for pure logic without DOM:
347
+
348
+ ```ts
349
+ import { createOwoMarkCore } from '@owomark/core';
350
+
351
+ const core = createOwoMarkCore({ initialMarkdown: '# Hello' });
352
+ console.log(core.getMarkdown());
353
+ ```
354
+
355
+ ## Building
356
+
357
+ From the monorepo root:
358
+
359
+ ```bash
360
+ npm run build:packages
361
+ ```
362
+
363
+ This builds `@owomark/core` -> `@owomark/view` -> `@owomark/react` in dependency order using tsup (ESM + DTS).
package/dist/index.css ADDED
@@ -0,0 +1,32 @@
1
+ /* src/MdxSkeleton.css */
2
+ .owo-mdx-skeleton {
3
+ background:
4
+ linear-gradient(
5
+ 90deg,
6
+ var(--owo-skeleton-base, #e5e7eb) 25%,
7
+ var(--owo-skeleton-shine, #f3f4f6) 50%,
8
+ var(--owo-skeleton-base, #e5e7eb) 75%);
9
+ background-size: 200% 100%;
10
+ animation: owo-mdx-skeleton-shimmer 1.5s ease-in-out infinite;
11
+ border-radius: 4px;
12
+ }
13
+ @keyframes owo-mdx-skeleton-shimmer {
14
+ 0% {
15
+ background-position: 200% 0;
16
+ }
17
+ 100% {
18
+ background-position: -200% 0;
19
+ }
20
+ }
21
+ .owo-mdx-skeleton-block {
22
+ display: flex;
23
+ flex-direction: column;
24
+ gap: 8px;
25
+ padding: 12px 0;
26
+ }
27
+ .owo-mdx-skeleton-line {
28
+ height: 14px;
29
+ }
30
+ .owo-mdx-skeleton-line:last-child {
31
+ width: 60%;
32
+ }
@@ -0,0 +1,249 @@
1
+ import * as react from 'react';
2
+ import { Ref, MutableRefObject, CSSProperties, ReactNode, ComponentType } from 'react';
3
+ import { IndentMode, OwoMarkSelection, OwoMarkCommands, OwoMarkCore, OwoMarkSharedStateController, OwoMarkSharedStateStore, PreviewBlock, CreateSharedStateOptions, OwoMarkSharedState, OwoMarkDocument, SlashState, BlockContext, BlockNode, VisibleRange, VirtualRow, Decorator } from '@owomark/core';
4
+ export { BlockContext, BlockContextType, BlockInsertType, CommandDefinition, OwoMarkCommands, OwoMarkCore, OwoMarkEditorInstance, OwoMarkSelection, OwoMarkSharedState, OwoMarkSharedStateController, OwoMarkSharedStateStore, PreviewBlock, PreviewBlockKind, SlashState } from '@owomark/core';
5
+ import { OwoMarkProcessorOptions, OwoMarkThemeName, PreviewStrategy, PreviewRendererRegistry, PreviewRenderContext } from '@owomark/view';
6
+ export { OwoMarkThemeName, PreviewRenderContext, PreviewRenderResult, PreviewRendererDefinition, PreviewRendererRegistry, THEME_DARK_CLASS, THEME_LIGHT_CLASS, getThemeClassName } from '@owomark/view';
7
+ import * as react_jsx_runtime from 'react/jsx-runtime';
8
+
9
+ type OwoMarkEditorConfig = {
10
+ /** Indent mode: 'auto' | '2' | '4'. Default: 'auto'. */
11
+ indentMode?: IndentMode;
12
+ /** Enable side annotation syntax. Default: true. */
13
+ enableSideAnnotation?: boolean;
14
+ /** Enable math (KaTeX) rendering. Default: true. */
15
+ enableMath?: boolean;
16
+ /** Enable fenced markdown sandbox rendering. Default: true. */
17
+ enableMarkdownSandbox?: boolean;
18
+ /** Minimum backtick count required for markdown sandbox fences. Default: 4. */
19
+ markdownSandbox?: {
20
+ outerFenceTicks?: number;
21
+ };
22
+ /** Color theme. Default: 'light'. */
23
+ theme?: 'light' | 'dark';
24
+ };
25
+ declare const DEFAULT_EDITOR_CONFIG: Required<OwoMarkEditorConfig>;
26
+ declare function resolveEditorConfig(config?: OwoMarkEditorConfig): Required<OwoMarkEditorConfig>;
27
+ declare function deriveProcessorOptions(config: Required<OwoMarkEditorConfig>): Partial<OwoMarkProcessorOptions>;
28
+
29
+ type OwoMarkEditorProps = {
30
+ value?: string;
31
+ defaultValue?: string;
32
+ onChange?: (markdown: string) => void;
33
+ onSelectionChange?: (selection: OwoMarkSelection) => void;
34
+ onCompositionStateChange?: (active: boolean) => void;
35
+ onScroll?: React.UIEventHandler<HTMLDivElement>;
36
+ readOnly?: boolean;
37
+ placeholder?: string;
38
+ className?: string;
39
+ theme?: OwoMarkThemeName;
40
+ themeClassName?: string;
41
+ commandsRef?: Ref<OwoMarkCommands>;
42
+ /**
43
+ * Ref to the underlying OwoMarkCore instance.
44
+ */
45
+ coreRef?: Ref<OwoMarkCore | null>;
46
+ /**
47
+ * When provided, the editor automatically connects to this
48
+ * shared state controller.
49
+ */
50
+ controller?: OwoMarkSharedStateController;
51
+ indentMode?: IndentMode;
52
+ ariaLabel?: string;
53
+ /**
54
+ * Unified editor configuration. When provided, `indentMode` and `theme`
55
+ * props are ignored in favour of the config values.
56
+ */
57
+ config?: OwoMarkEditorConfig;
58
+ };
59
+ declare const OwoMarkEditor: react.ForwardRefExoticComponent<OwoMarkEditorProps & react.RefAttributes<HTMLDivElement>>;
60
+
61
+ type OwoMarkPreviewProps = {
62
+ state: OwoMarkSharedStateStore;
63
+ className?: string;
64
+ strategy?: PreviewStrategy;
65
+ themeKey?: string;
66
+ registry?: PreviewRendererRegistry;
67
+ viewportFirst?: boolean;
68
+ ariaLabel?: string;
69
+ /**
70
+ * External block renderer. When provided, the preview engine uses this
71
+ * to render each block instead of the built-in lightweight renderer.
72
+ * Typically backed by the host's unified/remark/rehype/Shiki pipeline.
73
+ *
74
+ * Engine is recreated when this prop transitions between defined/undefined.
75
+ * Identity changes of the function itself do NOT cause engine recreation —
76
+ * the latest reference is always used via a stable ref.
77
+ */
78
+ renderBlock?: (block: PreviewBlock, context: PreviewRenderContext) => Promise<string>;
79
+ /** Called after each successful DOM update. Use to trigger scroll sync. */
80
+ onContentUpdate?: () => void;
81
+ };
82
+ declare const OwoMarkPreview: (props: OwoMarkPreviewProps) => react_jsx_runtime.JSX.Element;
83
+
84
+ type UseOwoMarkSharedStateOptions = CreateSharedStateOptions;
85
+ /**
86
+ * React hook that creates and manages a OwoMarkSharedStateController.
87
+ *
88
+ * The controller is created once and persists across re-renders.
89
+ * It can be connected to a OwoMarkEditor and consumed by OwoMarkPreview.
90
+ */
91
+ declare function useOwoMarkSharedState(options?: UseOwoMarkSharedStateOptions): OwoMarkSharedStateController;
92
+ /**
93
+ * Subscribe to shared state changes for React rendering.
94
+ * Returns the current OwoMarkSharedState, triggering re-renders on changes.
95
+ */
96
+ declare function useSharedStateSnapshot(controller: OwoMarkSharedStateController): OwoMarkSharedState;
97
+
98
+ /**
99
+ * React hook that creates and manages an OwoMarkCore instance
100
+ * with DOM event binding.
101
+ *
102
+ * No syncFromDOM for normal input — Core handles all inputTypes.
103
+ * Composition sync is the only controlled exception.
104
+ */
105
+
106
+ type UseOwoMarkCoreOptions = {
107
+ initialMarkdown: string;
108
+ readOnly?: boolean;
109
+ onCompositionStateChange?: (active: boolean) => void;
110
+ };
111
+ type UseOwoMarkCoreReturn = {
112
+ core: OwoMarkCore;
113
+ doc: OwoMarkDocument;
114
+ slashState: SlashState;
115
+ containerRef: MutableRefObject<HTMLDivElement | null>;
116
+ };
117
+ declare function useOwoMarkCore(options: UseOwoMarkCoreOptions): UseOwoMarkCoreReturn;
118
+
119
+ /**
120
+ * React hook that subscribes to block context changes from OwoMarkCore.
121
+ * Returns the current BlockContext (block type, depth, heading level, etc.)
122
+ * at the cursor position.
123
+ *
124
+ * This replaces the host-maintained identifyBlockAtCursor() pattern.
125
+ */
126
+
127
+ declare function useBlockContext(core: OwoMarkCore): BlockContext;
128
+
129
+ type UseVirtualListOptions = {
130
+ blocks: readonly BlockNode[];
131
+ /** The scrollable container element. */
132
+ containerRef: React.RefObject<HTMLElement | null>;
133
+ /** Number of extra blocks to render above/below viewport. Default: 5 */
134
+ overscan?: number;
135
+ /** Estimated characters per line for height estimation. Default: 80 */
136
+ charsPerLine?: number;
137
+ };
138
+ type UseVirtualListResult = {
139
+ /** Current visible range (inclusive indices). */
140
+ visibleRange: VisibleRange;
141
+ /** All virtual rows with heights. */
142
+ rows: VirtualRow[];
143
+ /** Total scrollable height. */
144
+ totalHeight: number;
145
+ /** Update a block's measured height. */
146
+ updateBlockHeight: (blockId: string, height: number) => void;
147
+ /** Check if a block index is in the visible range. */
148
+ isBlockVisible: (blockIndex: number) => boolean;
149
+ /** Force re-measure all heights. */
150
+ invalidateAllHeights: () => void;
151
+ };
152
+ declare function useVirtualList(options: UseVirtualListOptions): UseVirtualListResult;
153
+
154
+ type EditorBlockProps = {
155
+ block: BlockNode;
156
+ blockIndex: number;
157
+ };
158
+ /**
159
+ * Block layer: logical line unit.
160
+ * Re-renders only when block identity (id) or depth changes.
161
+ * Decorators handle finer-grained updates.
162
+ */
163
+ declare const EditorBlock: react.NamedExoticComponent<EditorBlockProps>;
164
+
165
+ type EditorDecoratorProps = {
166
+ decorator: Decorator;
167
+ };
168
+ /**
169
+ * Decorator layer: applies visual styling.
170
+ * Re-renders only when decorator type or leaf content changes.
171
+ */
172
+ declare const EditorDecorator: react.NamedExoticComponent<EditorDecoratorProps>;
173
+
174
+ type EditorLeafProps = {
175
+ text: string;
176
+ };
177
+ /**
178
+ * Leaf layer: carries the actual text content.
179
+ * Re-renders only when text changes.
180
+ */
181
+ declare const EditorLeaf: react.NamedExoticComponent<EditorLeafProps>;
182
+
183
+ type SlashMenuProps = {
184
+ state: SlashState;
185
+ onSelect: (index: number) => void;
186
+ onDismiss: () => void;
187
+ };
188
+ declare const SlashMenu: react.NamedExoticComponent<SlashMenuProps>;
189
+
190
+ /**
191
+ * Lightweight caret positioning utility for slash menu.
192
+ * Uses Range.getBoundingClientRect() + viewport flip.
193
+ * ~30 LOC — no external dependency.
194
+ */
195
+ type CaretRect = {
196
+ top: number;
197
+ left: number;
198
+ bottom: number;
199
+ };
200
+ /**
201
+ * Get the caret's screen coordinates from the current selection.
202
+ * Returns null if no selection or range is available.
203
+ */
204
+ declare function getCaretRect(): CaretRect | null;
205
+ /**
206
+ * Compute menu position given caret rect and menu dimensions.
207
+ * Places menu below caret by default; flips above if it would overflow viewport.
208
+ */
209
+ declare function computeMenuPosition(caret: CaretRect, menuHeight: number, menuWidth: number): {
210
+ top: number;
211
+ left: number;
212
+ };
213
+
214
+ type MdxSkeletonProps = {
215
+ /** Height in pixels for the skeleton placeholder. */
216
+ height?: number;
217
+ /** Number of shimmer lines to show. Default: 3. */
218
+ lines?: number;
219
+ className?: string;
220
+ style?: CSSProperties;
221
+ };
222
+ /**
223
+ * Lightweight skeleton loading indicator for MDX component placeholders.
224
+ * Pure CSS animation — no external UI library dependency.
225
+ */
226
+ declare function MdxSkeleton({ height, lines, className, style }: MdxSkeletonProps): react_jsx_runtime.JSX.Element;
227
+
228
+ type MdxComponentShellProps = {
229
+ /** Component display name, used as cache key for height estimation. */
230
+ name: string;
231
+ children: ReactNode;
232
+ };
233
+ /**
234
+ * Wraps an MDX component to provide:
235
+ * 1. Skeleton placeholder with estimated height before first paint
236
+ * 2. Real-height measurement after mount via ResizeObserver
237
+ * 3. Height caching so subsequent renders of the same component type
238
+ * use a better estimate, reducing layout shift.
239
+ */
240
+ declare function MdxComponentShell({ name, children }: MdxComponentShellProps): react_jsx_runtime.JSX.Element;
241
+ /**
242
+ * Create a wrapped version of an MDX component that renders inside
243
+ * MdxComponentShell for skeleton loading + height management.
244
+ */
245
+ declare function wrapWithShell<P extends Record<string, any>>(name: string, Component: ComponentType<P>): ComponentType<P>;
246
+ /** Clear the height cache (useful for testing or theme changes). */
247
+ declare function clearComponentHeightCache(): void;
248
+
249
+ export { type CaretRect, DEFAULT_EDITOR_CONFIG, EditorBlock, type EditorBlockProps, EditorDecorator, type EditorDecoratorProps, EditorLeaf, type EditorLeafProps, MdxComponentShell, type MdxComponentShellProps, MdxSkeleton, type MdxSkeletonProps, OwoMarkEditor, type OwoMarkEditorConfig, type OwoMarkEditorProps, OwoMarkPreview, type OwoMarkPreviewProps, SlashMenu, type SlashMenuProps, type UseOwoMarkCoreOptions, type UseOwoMarkCoreReturn, type UseOwoMarkSharedStateOptions, type UseVirtualListOptions, type UseVirtualListResult, clearComponentHeightCache, computeMenuPosition, deriveProcessorOptions, getCaretRect, resolveEditorConfig, useBlockContext, useOwoMarkCore, useOwoMarkSharedState, useSharedStateSnapshot, useVirtualList, wrapWithShell };