@sigx/lynx-markdown 0.4.5 → 0.4.7
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 +114 -44
- package/dist/XMarkdown.d.ts +36 -0
- package/dist/{Markdown.js → XMarkdown.js} +9 -3
- package/dist/ast.d.ts +117 -0
- package/dist/ast.js +11 -0
- package/dist/index.d.ts +13 -2
- package/dist/index.js +13 -1
- package/dist/parser/blocks.d.ts +21 -0
- package/dist/parser/blocks.js +274 -0
- package/dist/parser/incremental.d.ts +26 -0
- package/dist/parser/incremental.js +65 -0
- package/dist/parser/inline.d.ts +16 -0
- package/dist/parser/inline.js +451 -0
- package/dist/parser/scanner.d.ts +73 -0
- package/dist/parser/scanner.js +219 -0
- package/dist/render/MarkdownView.d.ts +29 -0
- package/dist/render/MarkdownView.js +43 -0
- package/dist/render/components.d.ts +140 -0
- package/dist/render/components.js +99 -0
- package/dist/render/engine.d.ts +21 -0
- package/dist/render/engine.js +145 -0
- package/dist/stream.d.ts +42 -0
- package/dist/stream.js +69 -0
- package/package.json +9 -4
- package/dist/Markdown.d.ts +0 -30
package/README.md
CHANGED
|
@@ -1,26 +1,17 @@
|
|
|
1
1
|
# @sigx/lynx-markdown
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A **SignalX-native, streaming-aware markdown renderer** for Lynx.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
It parses markdown in JavaScript (zero dependencies) and renders to native Lynx
|
|
6
|
+
`<view>`/`<text>`/`<image>` primitives — so it renders **identically on every
|
|
7
|
+
platform** (iOS, Android, Harmony) and is fully controllable from JS. Built for
|
|
8
|
+
AI output: as the source string grows token-by-token, finalized blocks keep a
|
|
9
|
+
stable identity and are never re-parsed or remounted, so completed content
|
|
10
|
+
doesn't flicker or reflow while new tokens stream in.
|
|
6
11
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
| Platform | First version | Notes |
|
|
11
|
-
| -------- | -------------- | ---------------------------------------------- |
|
|
12
|
-
| Harmony | Lynx 3.7.0 | Stable. |
|
|
13
|
-
| Android | Lynx 3.8.0-rc.0 | Ships as `org.lynxsdk.lynx:lynx_xelement_markdown`. |
|
|
14
|
-
| iOS | (post-3.8.0) | Currently only on the upstream `main` branch. |
|
|
15
|
-
|
|
16
|
-
On platforms where `<x-markdown>` is not yet registered, the component
|
|
17
|
-
renders nothing at runtime. This package exists so that app code can
|
|
18
|
-
already adopt the typed API surface — once you bump SignalX's Lynx pins
|
|
19
|
-
to a release that includes the native element on your target platforms,
|
|
20
|
-
it starts rendering without code changes.
|
|
21
|
-
|
|
22
|
-
When the iOS/Android pods land in stable, this README will be updated
|
|
23
|
-
with the matching native dependency wiring (Podfile + gradle).
|
|
12
|
+
The package also ships [`XMarkdown`](#xmarkdown--native-element-wrapper), the
|
|
13
|
+
thin wrapper around Lynx's native `<x-markdown>` element, for cases where you
|
|
14
|
+
want the platform's built-in renderer.
|
|
24
15
|
|
|
25
16
|
## Install
|
|
26
17
|
|
|
@@ -31,41 +22,120 @@ pnpm add @sigx/lynx-markdown
|
|
|
31
22
|
## Use
|
|
32
23
|
|
|
33
24
|
```tsx
|
|
34
|
-
import {
|
|
25
|
+
import { MarkdownView } from '@sigx/lynx-markdown';
|
|
35
26
|
|
|
36
27
|
export default function ArticleScreen() {
|
|
37
28
|
return (
|
|
38
|
-
<
|
|
39
|
-
value={
|
|
40
|
-
|
|
41
|
-
onLink={(e) => console.log('tapped', e.detail.url)}
|
|
42
|
-
onParseEnd={() => console.log('parsed')}
|
|
29
|
+
<MarkdownView
|
|
30
|
+
value={'# Hello\n\nThis is **markdown** with a [link](https://signalx.dev).'}
|
|
31
|
+
onLink={(href) => openUrl(href)}
|
|
43
32
|
/>
|
|
44
33
|
);
|
|
45
34
|
}
|
|
46
35
|
```
|
|
47
36
|
|
|
48
|
-
|
|
37
|
+
### Streaming AI output
|
|
38
|
+
|
|
39
|
+
`createMarkdownStream()` bridges a token loop to `<MarkdownView>` in one line. It
|
|
40
|
+
owns a reactive `value` signal and coalesces bursts of tokens into a bounded
|
|
41
|
+
number of re-renders.
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
import { MarkdownView, createMarkdownStream } from '@sigx/lynx-markdown';
|
|
45
|
+
|
|
46
|
+
const md = createMarkdownStream({ flushIntervalMs: 16 }); // ~60fps cap
|
|
47
|
+
|
|
48
|
+
// producer — your AI completion loop
|
|
49
|
+
for await (const token of completion) md.append(token);
|
|
50
|
+
md.done();
|
|
51
|
+
|
|
52
|
+
// consumer
|
|
53
|
+
<MarkdownView value={md.value.value} />;
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`MarkdownStream` API:
|
|
57
|
+
|
|
58
|
+
| Member | Description |
|
|
59
|
+
| ------------------ | ------------------------------------------------------- |
|
|
60
|
+
| `value` | Reactive accumulated source (`PrimitiveSignal<string>`).|
|
|
61
|
+
| `finished` | Reactive flag, set by `done()`. |
|
|
62
|
+
| `append(chunk)` | Append a token/chunk; buffered + coalesced into `value`.|
|
|
63
|
+
| `done()` | Flush the buffer and mark complete. |
|
|
64
|
+
| `reset()` | Clear buffer/`value`/`finished` (e.g. for a regenerate).|
|
|
65
|
+
|
|
66
|
+
## Supported syntax
|
|
67
|
+
|
|
68
|
+
Core CommonMark + GFM:
|
|
69
|
+
|
|
70
|
+
- Headings (`#`…`######`)
|
|
71
|
+
- Paragraphs with soft-wrap joining
|
|
72
|
+
- **Bold**, _italic_, ~~strikethrough~~, `inline code`
|
|
73
|
+
- Links, images, angle (`<url>`) and bare (`https://…`, `www.…`) autolinks
|
|
74
|
+
- Ordered and unordered nested lists, GFM task lists (`- [ ]` / `- [x]`)
|
|
75
|
+
- Blockquotes (with nested blocks)
|
|
76
|
+
- Fenced code blocks (with language label); unterminated fences render while
|
|
77
|
+
streaming and close cleanly when the final fence arrives
|
|
78
|
+
- Thematic breaks (`---`, `***`, `___`)
|
|
79
|
+
- GFM tables with per-column alignment
|
|
80
|
+
|
|
81
|
+
Not supported (renders as literal text): raw HTML, reference-style links,
|
|
82
|
+
setext headings, syntax highlighting. Link hrefs are sanitized — only
|
|
83
|
+
`http(s):`, `mailto:`, `tel:` and scheme-less links are exposed to `onLink`;
|
|
84
|
+
`javascript:`/`data:` collapse to `#`.
|
|
85
|
+
|
|
86
|
+
### `<MarkdownView>` props
|
|
87
|
+
|
|
88
|
+
| Prop | Type | Description |
|
|
89
|
+
| ------------- | ----------------------------- | -------------------------------------------- |
|
|
90
|
+
| `value` | `string` | Markdown source (reactive). |
|
|
91
|
+
| `onLink` | `(href: string) => void` | Fired when a link/autolink is tapped. |
|
|
92
|
+
| `onImageTap` | `(src: string) => void` | Fired when an image is tapped. |
|
|
93
|
+
| `components` | `Partial<MarkdownComponents>` | Per-node-type render-function overrides. |
|
|
94
|
+
|
|
95
|
+
### Theming with `components`
|
|
96
|
+
|
|
97
|
+
`MarkdownView` is **design-system agnostic**: it ships neutral, theme-agnostic
|
|
98
|
+
defaults and walks the AST calling a render function per node type. Override any
|
|
99
|
+
subset to control the element, styling, and layout — the engine pre-renders each
|
|
100
|
+
node's `children` and keeps the stable streaming keys regardless of what you
|
|
101
|
+
return.
|
|
49
102
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
103
|
+
```tsx
|
|
104
|
+
<MarkdownView
|
|
105
|
+
value={src}
|
|
106
|
+
components={{
|
|
107
|
+
heading: ({ level, children }) => <Heading level={level}>{children}</Heading>,
|
|
108
|
+
strong: ({ children }) => <text class="font-bold">{children}</text>,
|
|
109
|
+
}}
|
|
110
|
+
/>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
For a ready-made daisyUI mapping, pass `markdownComponents` from
|
|
114
|
+
`@sigx/lynx-daisyui`:
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
import { MarkdownView } from '@sigx/lynx-markdown';
|
|
118
|
+
import { markdownComponents } from '@sigx/lynx-daisyui';
|
|
119
|
+
|
|
120
|
+
<MarkdownView value={src} components={markdownComponents} />;
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## `XMarkdown` — native element wrapper
|
|
124
|
+
|
|
125
|
+
`XMarkdown` wraps Lynx's native `<x-markdown>` XElement. It's fast where
|
|
126
|
+
available but platform-gated and opaque (the engine owns parsing and styling):
|
|
60
127
|
|
|
61
|
-
|
|
128
|
+
| Platform | First version | Notes |
|
|
129
|
+
| -------- | --------------- | --------------------------------------------------- |
|
|
130
|
+
| Harmony | Lynx 3.7.0 | Stable. |
|
|
131
|
+
| Android | Lynx 3.8.0-rc.0 | Ships as `org.lynxsdk.lynx:lynx_xelement_markdown`. |
|
|
132
|
+
| iOS | (post-3.8.0) | Currently only on the upstream `main` branch. |
|
|
62
133
|
|
|
63
|
-
|
|
64
|
-
`
|
|
134
|
+
On platforms where `<x-markdown>` is not registered, it renders nothing. Prefer
|
|
135
|
+
`MarkdownView` for cross-platform, streaming, and customizable rendering.
|
|
65
136
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
- `getTextBoundingRect`, `setTextSelection`, `getSelectedText`
|
|
137
|
+
```tsx
|
|
138
|
+
import { XMarkdown } from '@sigx/lynx-markdown';
|
|
69
139
|
|
|
70
|
-
|
|
71
|
-
|
|
140
|
+
<XMarkdown value={'# Hello'} effect="typewriter" onLink={(e) => open(e.detail.url)} />;
|
|
141
|
+
```
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type Define } from '@sigx/lynx';
|
|
2
|
+
import './jsx-augment.js';
|
|
3
|
+
import type { MarkdownLinkEvent, MarkdownImageTapEvent, MarkdownParseEndEvent } from './jsx-augment.js';
|
|
4
|
+
export type XMarkdownEffect = 'typewriter' | 'none' | (string & {});
|
|
5
|
+
export type XMarkdownProps = Define.Prop<'value', string, false> & Define.Prop<'effect', XMarkdownEffect, false> & Define.Prop<'attachments', ReadonlyArray<unknown>, false> & Define.Prop<'class', string, false> & Define.Prop<'style', string | Record<string, string | number>, false> & Define.Prop<'onLink', (e: MarkdownLinkEvent) => void, false> & Define.Prop<'onImageTap', (e: MarkdownImageTapEvent) => void, false> & Define.Prop<'onParseEnd', (e: MarkdownParseEndEvent) => void, false>;
|
|
6
|
+
/**
|
|
7
|
+
* Render a markdown document using Lynx's native `<x-markdown>` XElement.
|
|
8
|
+
*
|
|
9
|
+
* This is the thin wrapper over the platform's native markdown element. It is
|
|
10
|
+
* fast where available but platform-gated (Harmony 3.7.0+, Android 3.8.0-rc.0+,
|
|
11
|
+
* iOS not yet in a tagged release) and opaque — the engine owns parsing and
|
|
12
|
+
* styling. For a cross-platform, fully-controllable, streaming-aware renderer
|
|
13
|
+
* built on Lynx `<view>`/`<text>` primitives, use {@link Markdown} instead.
|
|
14
|
+
*
|
|
15
|
+
* The markdown source is passed via the `value` prop; it is delivered to the
|
|
16
|
+
* native element as a raw-text child (per the 3.7.0 "raw-text node
|
|
17
|
+
* optimization" path). Event props use signalx's automatic
|
|
18
|
+
* `onLink`→`bindlink` mapping in `nodeOps.parseEventProp`, so handlers wire
|
|
19
|
+
* up without any per-event glue.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```tsx
|
|
23
|
+
* <XMarkdown
|
|
24
|
+
* value={"# Hello\n\nThis is **markdown**."}
|
|
25
|
+
* effect="typewriter"
|
|
26
|
+
* onLink={(e) => console.log('tapped', e.detail.url)}
|
|
27
|
+
* />
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* @remarks
|
|
31
|
+
* Availability of the `<x-markdown>` element is platform-dependent — see
|
|
32
|
+
* `jsx-augment.ts` for the per-platform schedule. On platforms where the
|
|
33
|
+
* native element is not registered, the engine logs a warning and renders
|
|
34
|
+
* no view; there is no JS-side feature gate.
|
|
35
|
+
*/
|
|
36
|
+
export declare const XMarkdown: import("@sigx/runtime-core").ComponentFactory<XMarkdownProps, void, {}>;
|
|
@@ -2,7 +2,13 @@ import { jsx as _jsx } from "@sigx/lynx/jsx-runtime";
|
|
|
2
2
|
import { component } from '@sigx/lynx';
|
|
3
3
|
import './jsx-augment.js';
|
|
4
4
|
/**
|
|
5
|
-
* Render a markdown document using Lynx's `<x-markdown>` XElement.
|
|
5
|
+
* Render a markdown document using Lynx's native `<x-markdown>` XElement.
|
|
6
|
+
*
|
|
7
|
+
* This is the thin wrapper over the platform's native markdown element. It is
|
|
8
|
+
* fast where available but platform-gated (Harmony 3.7.0+, Android 3.8.0-rc.0+,
|
|
9
|
+
* iOS not yet in a tagged release) and opaque — the engine owns parsing and
|
|
10
|
+
* styling. For a cross-platform, fully-controllable, streaming-aware renderer
|
|
11
|
+
* built on Lynx `<view>`/`<text>` primitives, use {@link Markdown} instead.
|
|
6
12
|
*
|
|
7
13
|
* The markdown source is passed via the `value` prop; it is delivered to the
|
|
8
14
|
* native element as a raw-text child (per the 3.7.0 "raw-text node
|
|
@@ -12,7 +18,7 @@ import './jsx-augment.js';
|
|
|
12
18
|
*
|
|
13
19
|
* @example
|
|
14
20
|
* ```tsx
|
|
15
|
-
* <
|
|
21
|
+
* <XMarkdown
|
|
16
22
|
* value={"# Hello\n\nThis is **markdown**."}
|
|
17
23
|
* effect="typewriter"
|
|
18
24
|
* onLink={(e) => console.log('tapped', e.detail.url)}
|
|
@@ -25,6 +31,6 @@ import './jsx-augment.js';
|
|
|
25
31
|
* native element is not registered, the engine logs a warning and renders
|
|
26
32
|
* no view; there is no JS-side feature gate.
|
|
27
33
|
*/
|
|
28
|
-
export const
|
|
34
|
+
export const XMarkdown = component(({ props }) => {
|
|
29
35
|
return () => (_jsx("x-markdown", { "markdown-effect": props.effect, "text-mark-attachments": props.attachments, class: props.class, style: props.style, bindlink: props.onLink, bindimageTap: props.onImageTap, bindparseEnd: props.onParseEnd, children: props.value ?? '' }));
|
|
30
36
|
});
|
package/dist/ast.d.ts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AST node types for the SignalX-native markdown renderer.
|
|
3
|
+
*
|
|
4
|
+
* The tree is intentionally small and flat: the parser produces it, the render
|
|
5
|
+
* layer maps it to Lynx `<view>`/`<text>`/`<image>` intrinsics, and the
|
|
6
|
+
* incremental engine memoizes finalized {@link BlockNode}s by reference. Every
|
|
7
|
+
* block carries a `key` (assigned by the incremental layer for stable
|
|
8
|
+
* reconciliation) and `raw` (the exact source slice, used for memo equality and
|
|
9
|
+
* introspection).
|
|
10
|
+
*/
|
|
11
|
+
export type InlineNode = InlineText | InlineStrong | InlineEm | InlineDel | InlineCodeSpan | InlineLink | InlineImage | InlineAutolink | InlineBreak;
|
|
12
|
+
/** A run of literal text. */
|
|
13
|
+
export interface InlineText {
|
|
14
|
+
type: 'text';
|
|
15
|
+
value: string;
|
|
16
|
+
}
|
|
17
|
+
/** `**bold**` / `__bold__`. */
|
|
18
|
+
export interface InlineStrong {
|
|
19
|
+
type: 'strong';
|
|
20
|
+
children: InlineNode[];
|
|
21
|
+
}
|
|
22
|
+
/** `*italic*` / `_italic_`. */
|
|
23
|
+
export interface InlineEm {
|
|
24
|
+
type: 'em';
|
|
25
|
+
children: InlineNode[];
|
|
26
|
+
}
|
|
27
|
+
/** GFM `~~strikethrough~~`. */
|
|
28
|
+
export interface InlineDel {
|
|
29
|
+
type: 'del';
|
|
30
|
+
children: InlineNode[];
|
|
31
|
+
}
|
|
32
|
+
/** `` `code` `` — content is literal (no nested inline parsing). */
|
|
33
|
+
export interface InlineCodeSpan {
|
|
34
|
+
type: 'codeSpan';
|
|
35
|
+
value: string;
|
|
36
|
+
}
|
|
37
|
+
/** `[text](href "title")`. */
|
|
38
|
+
export interface InlineLink {
|
|
39
|
+
type: 'link';
|
|
40
|
+
href: string;
|
|
41
|
+
title?: string;
|
|
42
|
+
children: InlineNode[];
|
|
43
|
+
}
|
|
44
|
+
/** ``. */
|
|
45
|
+
export interface InlineImage {
|
|
46
|
+
type: 'image';
|
|
47
|
+
src: string;
|
|
48
|
+
alt: string;
|
|
49
|
+
title?: string;
|
|
50
|
+
}
|
|
51
|
+
/** `<https://…>` / `<email>` / GFM bare-URL autolink. */
|
|
52
|
+
export interface InlineAutolink {
|
|
53
|
+
type: 'autolink';
|
|
54
|
+
href: string;
|
|
55
|
+
value: string;
|
|
56
|
+
}
|
|
57
|
+
/** A hard line break (two trailing spaces or a trailing backslash). */
|
|
58
|
+
export interface InlineBreak {
|
|
59
|
+
type: 'br';
|
|
60
|
+
}
|
|
61
|
+
export interface BlockBase {
|
|
62
|
+
/** Stable reconciliation key, assigned by the incremental engine. */
|
|
63
|
+
key: string;
|
|
64
|
+
/** Exact source slice this block was parsed from. */
|
|
65
|
+
raw: string;
|
|
66
|
+
}
|
|
67
|
+
export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
|
|
68
|
+
export type TableAlign = 'left' | 'center' | 'right';
|
|
69
|
+
export type BlockNode = HeadingBlock | ParagraphBlock | BlockquoteBlock | ListBlock | CodeBlock | ThematicBreakBlock | TableBlock;
|
|
70
|
+
export interface HeadingBlock extends BlockBase {
|
|
71
|
+
type: 'heading';
|
|
72
|
+
level: HeadingLevel;
|
|
73
|
+
children: InlineNode[];
|
|
74
|
+
}
|
|
75
|
+
export interface ParagraphBlock extends BlockBase {
|
|
76
|
+
type: 'paragraph';
|
|
77
|
+
children: InlineNode[];
|
|
78
|
+
}
|
|
79
|
+
export interface BlockquoteBlock extends BlockBase {
|
|
80
|
+
type: 'blockquote';
|
|
81
|
+
children: BlockNode[];
|
|
82
|
+
}
|
|
83
|
+
export interface ListBlock extends BlockBase {
|
|
84
|
+
type: 'list';
|
|
85
|
+
ordered: boolean;
|
|
86
|
+
/** Starting number for ordered lists (1 for unordered). */
|
|
87
|
+
start: number;
|
|
88
|
+
/** Tight lists have no blank lines between items → tighter spacing. */
|
|
89
|
+
tight: boolean;
|
|
90
|
+
items: ListItem[];
|
|
91
|
+
}
|
|
92
|
+
export interface ListItem {
|
|
93
|
+
key: string;
|
|
94
|
+
/** GFM task list state: `true`/`false`, or `null` when not a task item. */
|
|
95
|
+
checked: boolean | null;
|
|
96
|
+
children: BlockNode[];
|
|
97
|
+
}
|
|
98
|
+
export interface CodeBlock extends BlockBase {
|
|
99
|
+
type: 'codeBlock';
|
|
100
|
+
lang?: string;
|
|
101
|
+
value: string;
|
|
102
|
+
/** `false` while the fence is still open (streaming, or unterminated). */
|
|
103
|
+
closed: boolean;
|
|
104
|
+
}
|
|
105
|
+
export interface ThematicBreakBlock extends BlockBase {
|
|
106
|
+
type: 'thematicBreak';
|
|
107
|
+
}
|
|
108
|
+
export interface TableBlock extends BlockBase {
|
|
109
|
+
type: 'table';
|
|
110
|
+
/** Per-column alignment; `null` = default (left). */
|
|
111
|
+
align: (TableAlign | null)[];
|
|
112
|
+
header: TableCell[];
|
|
113
|
+
rows: TableCell[][];
|
|
114
|
+
}
|
|
115
|
+
export interface TableCell {
|
|
116
|
+
children: InlineNode[];
|
|
117
|
+
}
|
package/dist/ast.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AST node types for the SignalX-native markdown renderer.
|
|
3
|
+
*
|
|
4
|
+
* The tree is intentionally small and flat: the parser produces it, the render
|
|
5
|
+
* layer maps it to Lynx `<view>`/`<text>`/`<image>` intrinsics, and the
|
|
6
|
+
* incremental engine memoizes finalized {@link BlockNode}s by reference. Every
|
|
7
|
+
* block carries a `key` (assigned by the incremental layer for stable
|
|
8
|
+
* reconciliation) and `raw` (the exact source slice, used for memo equality and
|
|
9
|
+
* introspection).
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import './jsx-augment.js';
|
|
2
|
-
export {
|
|
3
|
-
export type {
|
|
2
|
+
export { MarkdownView } from './render/MarkdownView.js';
|
|
3
|
+
export type { MarkdownViewProps } from './render/MarkdownView.js';
|
|
4
|
+
export { defaultComponents } from './render/components.js';
|
|
5
|
+
export type { MarkdownComponents, MarkdownChild, RootProps, HeadingProps, ParagraphProps, BlockquoteProps, ListProps, ListItemProps, CodeProps, ThematicBreakProps, TableProps, TableRowProps, TableCellProps, StrongProps, EmProps, DelProps, CodeSpanProps, LinkProps, AutolinkProps, ImageProps, } from './render/components.js';
|
|
6
|
+
export { createMarkdownStream } from './stream.js';
|
|
7
|
+
export type { MarkdownStream, CreateMarkdownStreamOptions } from './stream.js';
|
|
8
|
+
export { XMarkdown } from './XMarkdown.js';
|
|
9
|
+
export type { XMarkdownProps, XMarkdownEffect } from './XMarkdown.js';
|
|
10
|
+
export { createIncrementalEngine } from './parser/incremental.js';
|
|
11
|
+
export type { IncrementalEngine } from './parser/incremental.js';
|
|
12
|
+
export { parseBlocks } from './parser/blocks.js';
|
|
13
|
+
export { parseInline } from './parser/inline.js';
|
|
14
|
+
export type * from './ast.js';
|
|
4
15
|
export type { XMarkdownAttributes, MarkdownLinkEvent, MarkdownLinkEventDetail, MarkdownImageTapEvent, MarkdownImageTapEventDetail, MarkdownParseEndEvent, MarkdownParseEndEventDetail, } from './jsx-augment.js';
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,14 @@
|
|
|
1
1
|
import './jsx-augment.js';
|
|
2
|
-
|
|
2
|
+
// Primary: the SignalX-native streaming renderer. (An editable `MarkdownEditor`
|
|
3
|
+
// is planned as a sibling export.)
|
|
4
|
+
export { MarkdownView } from './render/MarkdownView.js';
|
|
5
|
+
// Generic render-function override API (design systems plug in here).
|
|
6
|
+
export { defaultComponents } from './render/components.js';
|
|
7
|
+
// Streaming controller for AI token loops.
|
|
8
|
+
export { createMarkdownStream } from './stream.js';
|
|
9
|
+
// Native `<x-markdown>` wrapper, preserved for platforms that ship the element.
|
|
10
|
+
export { XMarkdown } from './XMarkdown.js';
|
|
11
|
+
// Parser primitives (for advanced consumers / testing).
|
|
12
|
+
export { createIncrementalEngine } from './parser/incremental.js';
|
|
13
|
+
export { parseBlocks } from './parser/blocks.js';
|
|
14
|
+
export { parseInline } from './parser/inline.js';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Block-level parser: source → `BlockNode[]`.
|
|
3
|
+
*
|
|
4
|
+
* Line-oriented with a small open-container model. The key streaming-safety
|
|
5
|
+
* rule lives here: a blank line only separates blocks at the top level and
|
|
6
|
+
* *outside* an open fenced code block — a blank line inside ``` does not close
|
|
7
|
+
* it. An unterminated fence parses as a `codeBlock` with `closed: false` and is
|
|
8
|
+
* still rendered, so streaming code dumps don't flicker when the closing fence
|
|
9
|
+
* finally arrives.
|
|
10
|
+
*
|
|
11
|
+
* Keys are assigned by the caller through a `KeyGen` so nested blocks (list
|
|
12
|
+
* items, blockquote contents) get stable, unique keys for reconciliation.
|
|
13
|
+
*/
|
|
14
|
+
import type { BlockNode } from '../ast.js';
|
|
15
|
+
/** Monotonic key generator so every block (incl. nested) gets a unique key. */
|
|
16
|
+
export interface KeyGen {
|
|
17
|
+
next(): string;
|
|
18
|
+
}
|
|
19
|
+
export declare function makeKeyGen(prefix: string): KeyGen;
|
|
20
|
+
/** Parse a complete (or partial) source string into block nodes. */
|
|
21
|
+
export declare function parseBlocks(src: string, keys?: KeyGen): BlockNode[];
|