streamdown-angular 0.4.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/LICENSE +201 -0
- package/NOTICE +23 -0
- package/README.md +246 -0
- package/fesm2022/streamdown-angular.mjs +1936 -0
- package/fesm2022/streamdown-angular.mjs.map +1 -0
- package/package.json +72 -0
- package/types/streamdown-angular.d.ts +625 -0
|
@@ -0,0 +1,625 @@
|
|
|
1
|
+
import * as _angular_core from '@angular/core';
|
|
2
|
+
import { InjectionToken, OnDestroy, ComponentRef, ViewContainerRef, OnInit, Type, EnvironmentProviders } from '@angular/core';
|
|
3
|
+
import * as hast from 'hast';
|
|
4
|
+
import { Root, RootContent, ElementContent, Element } from 'hast';
|
|
5
|
+
import { PluggableList } from 'unified';
|
|
6
|
+
import { ClassValue } from 'clsx';
|
|
7
|
+
import * as streamdown_angular from 'streamdown-angular';
|
|
8
|
+
import { SafeHtml } from '@angular/platform-browser';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Main public component (Streamdown `index.tsx`).
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* <ngx-streamdown [content]="markdown()" />
|
|
15
|
+
*
|
|
16
|
+
* Data flow (React useMemo chain → Angular computed):
|
|
17
|
+
* content → parseMarkdownIntoBlocks → @for(block) → ngx-block → hast-renderer
|
|
18
|
+
*/
|
|
19
|
+
declare class StreamdownComponent {
|
|
20
|
+
/** Markdown text to render (grows during streaming) */
|
|
21
|
+
readonly content: _angular_core.InputSignal<string>;
|
|
22
|
+
/** Fix incomplete markdown (such as `**bold` during streaming). Default: true */
|
|
23
|
+
readonly parseIncompleteMarkdown: _angular_core.InputSignal<boolean>;
|
|
24
|
+
/** Show a streaming caret (typing cursor) at the end of the last block. Default: false */
|
|
25
|
+
readonly caret: _angular_core.InputSignal<boolean>;
|
|
26
|
+
readonly blocks: _angular_core.Signal<string[]>;
|
|
27
|
+
readonly dir: _angular_core.Signal<"rtl" | "ltr">;
|
|
28
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<StreamdownComponent, never>;
|
|
29
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<StreamdownComponent, "ngx-streamdown", never, { "content": { "alias": "content"; "required": false; "isSignal": true; }; "parseIncompleteMarkdown": { "alias": "parseIncompleteMarkdown"; "required": false; "isSignal": true; }; "caret": { "alias": "caret"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Renders a single markdown block (Streamdown `Block`, memoized).
|
|
34
|
+
*
|
|
35
|
+
* Thanks to `OnPush` + the parent's `@for track $index`, only the changed block
|
|
36
|
+
* re-renders — the streaming equivalent of React's `memo()`.
|
|
37
|
+
*/
|
|
38
|
+
declare class BlockComponent {
|
|
39
|
+
/** Markdown text of a single block */
|
|
40
|
+
readonly content: _angular_core.InputSignal<string>;
|
|
41
|
+
/** Fix incomplete markdown (remend) */
|
|
42
|
+
readonly parseIncompleteMarkdown: _angular_core.InputSignal<boolean>;
|
|
43
|
+
/** Whether to append a streaming caret at the end of this block (last block only) */
|
|
44
|
+
readonly caret: _angular_core.InputSignal<boolean>;
|
|
45
|
+
private readonly md;
|
|
46
|
+
readonly hast: _angular_core.Signal<hast.Root>;
|
|
47
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<BlockComponent, never>;
|
|
48
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<BlockComponent, "ngx-block", never, { "content": { "alias": "content"; "required": true; "isSignal": true; }; "parseIncompleteMarkdown": { "alias": "parseIncompleteMarkdown"; "required": false; "isSignal": true; }; "caret": { "alias": "caret"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Configuration that plugins add to the pipeline (math, cjk, ...).
|
|
53
|
+
* `provideStreamdown*()` providers populate this token with `multi: true`.
|
|
54
|
+
*/
|
|
55
|
+
interface StreamdownPipelineConfig {
|
|
56
|
+
/** remark plugins added BEFORE remark-rehype (e.g. remark-math) */
|
|
57
|
+
remarkPlugins?: PluggableList;
|
|
58
|
+
/** rehype plugins added BEFORE rehype-sanitize (e.g. rehype-katex) */
|
|
59
|
+
rehypePlugins?: PluggableList;
|
|
60
|
+
/** Override the rehype-sanitize schema (e.g. to allow katex output) */
|
|
61
|
+
sanitizeSchema?: unknown;
|
|
62
|
+
}
|
|
63
|
+
declare const STREAMDOWN_PIPELINE: InjectionToken<StreamdownPipelineConfig[]>;
|
|
64
|
+
/**
|
|
65
|
+
* Converts a markdown string into a HAST (HTML AST) tree.
|
|
66
|
+
* Matches the Streamdown `lib/markdown.ts` pipeline, but extensible via plugins:
|
|
67
|
+
* remark-parse → remark-gfm → [remarkPlugins] → remark-rehype → rehype-raw
|
|
68
|
+
* → [rehypePlugins] → rehype-sanitize
|
|
69
|
+
*/
|
|
70
|
+
declare class MarkdownService {
|
|
71
|
+
private readonly configs;
|
|
72
|
+
private readonly processor;
|
|
73
|
+
/**
|
|
74
|
+
* Markdown → HAST tree (sync). Uses `runSync` so each render is fast during streaming.
|
|
75
|
+
* @param options.remend "close" incomplete markdown (default: true)
|
|
76
|
+
*/
|
|
77
|
+
toHast(markdown: string, options?: {
|
|
78
|
+
remend?: boolean;
|
|
79
|
+
}): Root;
|
|
80
|
+
/** Builds the pipeline with plugins (once). */
|
|
81
|
+
private build;
|
|
82
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MarkdownService, never>;
|
|
83
|
+
static ɵprov: _angular_core.ɵɵInjectableDeclaration<MarkdownService>;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The entry point of the HAST renderer.
|
|
88
|
+
*
|
|
89
|
+
* When the `hast` input (Root) changes, it does not rebuild the whole tree — it
|
|
90
|
+
* **diffs (reconciles)** against the previous render: only changed text/attributes/elements
|
|
91
|
+
* are updated. During streaming this eliminates flicker, preserves scroll/selection, and updates
|
|
92
|
+
* the `node` input of special components (code-block, table) without recreating them.
|
|
93
|
+
*/
|
|
94
|
+
declare class HastRendererComponent implements OnDestroy {
|
|
95
|
+
/** The HAST Root tree to render */
|
|
96
|
+
readonly hast: _angular_core.InputSignal<Root>;
|
|
97
|
+
/** Append a streaming caret (text cursor) at the end */
|
|
98
|
+
readonly caret: _angular_core.InputSignal<boolean>;
|
|
99
|
+
private readonly hostRef;
|
|
100
|
+
private readonly vcr;
|
|
101
|
+
private readonly renderer;
|
|
102
|
+
private rendered;
|
|
103
|
+
private caretEl;
|
|
104
|
+
constructor();
|
|
105
|
+
ngOnDestroy(): void;
|
|
106
|
+
/** Appends the caret inside the last block element (inline) or to the host. */
|
|
107
|
+
private addCaret;
|
|
108
|
+
/** Removes the existing caret from the DOM. */
|
|
109
|
+
private removeCaret;
|
|
110
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<HastRendererComponent, never>;
|
|
111
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<HastRendererComponent, "ngx-hast-renderer", never, { "hast": { "alias": "hast"; "required": true; "isSignal": true; }; "caret": { "alias": "caret"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Element node + extra data (similar to React's `ExtraProps`).
|
|
116
|
+
* Passed to dynamic components via `setInput('node', ...)`.
|
|
117
|
+
*/
|
|
118
|
+
interface HastElementInput {
|
|
119
|
+
node: Element;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Child nodes the renderer iterates over.
|
|
123
|
+
* Both `Root.children` (RootContent[]) and `Element.children` (ElementContent[])
|
|
124
|
+
* fit into this union.
|
|
125
|
+
*/
|
|
126
|
+
type HastChild = RootContent | ElementContent;
|
|
127
|
+
|
|
128
|
+
/** Add a fade-in animation to new elements (optional). */
|
|
129
|
+
declare const STREAMDOWN_ANIMATE: InjectionToken<boolean>;
|
|
130
|
+
/**
|
|
131
|
+
* Internal bookkeeping record for a single rendered HAST node.
|
|
132
|
+
* Diffing (reconciliation) keeps this tree of records and only updates what changed.
|
|
133
|
+
*/
|
|
134
|
+
interface RenderedNode {
|
|
135
|
+
/** Corresponding HAST node (previous state) */
|
|
136
|
+
hast: HastChild;
|
|
137
|
+
/** Created DOM node (text / element / component host) */
|
|
138
|
+
dom: Node;
|
|
139
|
+
/** For a special tag — the component ref (for cleanup and input updates) */
|
|
140
|
+
componentRef?: ComponentRef<unknown>;
|
|
141
|
+
/** For a generic element — its children's records */
|
|
142
|
+
children?: RenderedNode[];
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* The core of the HAST renderer. Turns a HAST tree into DOM and — most importantly —
|
|
146
|
+
* on subsequent renders **diffs (reconciles)** against the existing DOM instead of
|
|
147
|
+
* rebuilding the whole tree: only changed text/attributes/elements are updated. During streaming this:
|
|
148
|
+
* - eliminates "flicker" (unchanged DOM is preserved),
|
|
149
|
+
* - does not break scroll/selection/focus,
|
|
150
|
+
* - updates the `node` input of special components (code-block, table) without recreating them,
|
|
151
|
+
* - applies a fade-in animation naturally to new elements (only for new DOM).
|
|
152
|
+
*/
|
|
153
|
+
declare class HastRendererService {
|
|
154
|
+
private readonly renderer;
|
|
155
|
+
private readonly linkModal;
|
|
156
|
+
private readonly linkSafety;
|
|
157
|
+
private readonly animate;
|
|
158
|
+
/**
|
|
159
|
+
* Renders the `next` HAST nodes into `parent`, diffing against the `previous` records.
|
|
160
|
+
* On the first render `previous` is an empty array. Returns: the new tree of records
|
|
161
|
+
* (passed to the next reconcile).
|
|
162
|
+
*/
|
|
163
|
+
reconcile(parent: HTMLElement, previous: RenderedNode[], next: readonly HastChild[], vcr: ViewContainerRef): RenderedNode[];
|
|
164
|
+
/** Fully cleans up records rendered via reconcile (destroys component refs). */
|
|
165
|
+
destroyAll(rendered: RenderedNode[]): void;
|
|
166
|
+
/**
|
|
167
|
+
* Creates the `nodes` list into `parent` (without diffing). Returns: the ComponentRefs.
|
|
168
|
+
* Kept for special cases (recursive render inside a table).
|
|
169
|
+
*/
|
|
170
|
+
renderNodes(nodes: readonly HastChild[], parent: HTMLElement, vcr: ViewContainerRef): ComponentRef<unknown>[];
|
|
171
|
+
/** Removes the DOM children under `parent` and destroys the ComponentRefs. */
|
|
172
|
+
clear(parent: HTMLElement, refs: ComponentRef<unknown>[]): void;
|
|
173
|
+
/** Creates a new DOM/component for a single HAST node (returns detached dom). */
|
|
174
|
+
private createRendered;
|
|
175
|
+
/** Updates existing DOM in place with the new HAST node (without recreating). */
|
|
176
|
+
private patch;
|
|
177
|
+
/** Cleans up the record (and its children): destroy the component + remove from DOM. */
|
|
178
|
+
private destroyRendered;
|
|
179
|
+
/**
|
|
180
|
+
* For `a` tags: external links get `target=_blank` + `rel=noopener noreferrer`;
|
|
181
|
+
* when link safety is enabled, intercept the click and open through a modal.
|
|
182
|
+
*/
|
|
183
|
+
private enhanceAnchor;
|
|
184
|
+
/** Merges the ELEMENT_CLASSES base with the element's own className. */
|
|
185
|
+
private applyClasses;
|
|
186
|
+
/** HAST properties → DOM attributes (initial, full application). */
|
|
187
|
+
private applyProperties;
|
|
188
|
+
/** Applies the attribute diff: removes the deleted ones, sets the new ones. */
|
|
189
|
+
private updateAttributes;
|
|
190
|
+
/** Writes a single HAST property to a DOM attribute (with safe allowlist logic). */
|
|
191
|
+
private setProp;
|
|
192
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<HastRendererService, never>;
|
|
193
|
+
static ɵprov: _angular_core.ɵɵInjectableDeclaration<HastRendererService>;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* A standalone component that renders a single generic HAST element node.
|
|
198
|
+
*
|
|
199
|
+
* The main render path goes through `HastRendererService` directly with Renderer2
|
|
200
|
+
* (wrapper-free, for correct HTML semantics). This component serves as a reusable
|
|
201
|
+
* unit: for when special components want to render their HAST children, or to use it
|
|
202
|
+
* from a template as `<ngx-hast-element [node]="...">`.
|
|
203
|
+
*
|
|
204
|
+
* `:host { display: contents }` — the `<ngx-hast-element>` wrapper does not affect layout.
|
|
205
|
+
*/
|
|
206
|
+
declare class HastElementComponent implements OnInit, OnDestroy {
|
|
207
|
+
/** The HAST element node to render */
|
|
208
|
+
readonly node: _angular_core.InputSignal<Element>;
|
|
209
|
+
private readonly hostRef;
|
|
210
|
+
private readonly vcr;
|
|
211
|
+
private readonly renderer;
|
|
212
|
+
private refs;
|
|
213
|
+
ngOnInit(): void;
|
|
214
|
+
ngOnDestroy(): void;
|
|
215
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<HastElementComponent, never>;
|
|
216
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<HastElementComponent, "ngx-hast-element", never, { "node": { "alias": "node"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Merges class values into a single string (Streamdown `cn`).
|
|
221
|
+
* Accepts array/object/conditional values: `cn('a', cond && 'b', ['c'])`.
|
|
222
|
+
* (No Tailwind needed — styles now live as plain CSS in `streamdown.css`.)
|
|
223
|
+
*/
|
|
224
|
+
declare function cn(...inputs: ClassValue[]): string;
|
|
225
|
+
/**
|
|
226
|
+
* HTML tagName → extra class. Empty (default) — because all typography is
|
|
227
|
+
* provided via element selectors in `streamdown.css` (`.ngx-streamdown h1` ...) and
|
|
228
|
+
* inlined automatically into `HastRendererComponent` (no Tailwind needed).
|
|
229
|
+
*
|
|
230
|
+
* Users can add their own extra classes for tags here, for example:
|
|
231
|
+
* ELEMENT_CLASSES['h1'] = 'my-heading';
|
|
232
|
+
* This value is merged with the element's own `className` via `cn()`.
|
|
233
|
+
*/
|
|
234
|
+
declare const ELEMENT_CLASSES: Record<string, string>;
|
|
235
|
+
/**
|
|
236
|
+
* Tags that require a dedicated Angular component (tagName → component class).
|
|
237
|
+
* Currently empty; in later stages `pre` → CodeBlock, `table` → Table will be added.
|
|
238
|
+
* Tags not in this map are rendered as generic DOM elements.
|
|
239
|
+
*/
|
|
240
|
+
declare const COMPONENT_MAP: Map<string, Type<unknown>>;
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Enables the fade-in animation for elements that newly appear during streaming.
|
|
244
|
+
* Thanks to diffing (reconcile), only NEW DOM is animated — existing elements don't replay.
|
|
245
|
+
* `prefers-reduced-motion` is respected.
|
|
246
|
+
*/
|
|
247
|
+
declare function provideStreamdownAnimation(): EnvironmentProviders;
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Splits markdown into top-level blocks (Streamdown `lib/parse-blocks.tsx`).
|
|
251
|
+
*
|
|
252
|
+
* The `marked` lexer breaks markdown into tokens; each token's `raw` text is one block.
|
|
253
|
+
* This matters for streaming: only the changed (last) block re-renders, while the
|
|
254
|
+
* rest stay unchanged thanks to `@for track $index` + OnPush.
|
|
255
|
+
*/
|
|
256
|
+
declare function parseMarkdownIntoBlocks(markdown: string): string[];
|
|
257
|
+
|
|
258
|
+
declare function detectDirection(text: string): 'rtl' | 'ltr';
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* MAIN component that renders a Markdown code block (`<pre>`).
|
|
262
|
+
*
|
|
263
|
+
* In COMPONENT_MAP this component is bound to the `pre` tag; the renderer
|
|
264
|
+
* calls `setInput('node', preElement)`.
|
|
265
|
+
*
|
|
266
|
+
* Logic:
|
|
267
|
+
* 1) First look up a component by language in `CODE_LANGUAGE_COMPONENTS`
|
|
268
|
+
* (e.g. `mermaid`). If found, create it, pass the `code` input,
|
|
269
|
+
* place it into the body and do NOT run Shiki.
|
|
270
|
+
* 2) Otherwise, show the code card: header (language + copy/download) and
|
|
271
|
+
* body (Shiki output). Until highlighting is ready, plain escaped
|
|
272
|
+
* code is shown (so it appears immediately during streaming).
|
|
273
|
+
*/
|
|
274
|
+
declare class CodeBlockComponent implements OnDestroy {
|
|
275
|
+
/** The `<pre>` HAST element to render. */
|
|
276
|
+
readonly node: _angular_core.InputSignal<Element>;
|
|
277
|
+
private readonly vcr;
|
|
278
|
+
private readonly highlighter;
|
|
279
|
+
private readonly sanitizer;
|
|
280
|
+
/** Host where the plugin component is placed (for registered languages). */
|
|
281
|
+
private readonly pluginHost;
|
|
282
|
+
/** Created plugin ComponentRefs (for cleanup). */
|
|
283
|
+
private pluginRefs;
|
|
284
|
+
/** Code block info: {code, language}. */
|
|
285
|
+
protected readonly info: _angular_core.Signal<streamdown_angular.CodeInfo>;
|
|
286
|
+
protected readonly code: _angular_core.Signal<string>;
|
|
287
|
+
protected readonly language: _angular_core.Signal<string>;
|
|
288
|
+
/** Name of the downloadable file (e.g. `code.ts`). */
|
|
289
|
+
protected readonly downloadName: _angular_core.Signal<string>;
|
|
290
|
+
/** Is a dedicated component registered for this language? */
|
|
291
|
+
protected readonly hasRegisteredComponent: _angular_core.Signal<boolean>;
|
|
292
|
+
/** Shiki-highlighted, sanitized HTML (null until loaded). */
|
|
293
|
+
protected readonly highlighted: _angular_core.WritableSignal<SafeHtml | null>;
|
|
294
|
+
constructor();
|
|
295
|
+
ngOnDestroy(): void;
|
|
296
|
+
private disposePlugins;
|
|
297
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<CodeBlockComponent, never>;
|
|
298
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<CodeBlockComponent, "ngx-code-block", never, { "node": { "alias": "node"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Service that syntax-highlights code via Shiki.
|
|
303
|
+
*
|
|
304
|
+
* `shiki` is an OPTIONAL peer dependency — so the dynamic `import()` is run
|
|
305
|
+
* once (cached) inside a try/catch. If shiki is not installed, the import
|
|
306
|
+
* fails, or the language is unknown, we fall back to a plain escaped
|
|
307
|
+
* `<pre><code>`. This service NEVER throws.
|
|
308
|
+
*/
|
|
309
|
+
declare class ShikiHighlighterService {
|
|
310
|
+
/** Cached promise for loading the Shiki module once. */
|
|
311
|
+
private shikiPromise;
|
|
312
|
+
/**
|
|
313
|
+
* Highlights `code` for the `lang` language and returns HTML.
|
|
314
|
+
* On error, falls back to an escaped `<pre><code>`.
|
|
315
|
+
*/
|
|
316
|
+
highlight(code: string, lang: string): Promise<string>;
|
|
317
|
+
/** Lazily loads the Shiki module (once); returns `null` on failure. */
|
|
318
|
+
private loadShiki;
|
|
319
|
+
/** Plain escaped `<pre><code>` (XSS-safe fallback). */
|
|
320
|
+
private fallback;
|
|
321
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<ShikiHighlighterService, never>;
|
|
322
|
+
static ɵprov: _angular_core.ɵɵInjectableDeclaration<ShikiHighlighterService>;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Language (lowercase) → component registry.
|
|
327
|
+
*
|
|
328
|
+
* Optional plugins (e.g. `mermaid`) can "claim" a specific code language:
|
|
329
|
+
* if a component is registered for that language, `CodeBlockComponent` creates
|
|
330
|
+
* that component instead of highlighting with Shiki.
|
|
331
|
+
*
|
|
332
|
+
* A registered component MUST accept a `code: string` (raw code) input.
|
|
333
|
+
*/
|
|
334
|
+
declare const CODE_LANGUAGE_COMPONENTS: Map<string, Type<unknown>>;
|
|
335
|
+
|
|
336
|
+
/** Information extracted about a code block. */
|
|
337
|
+
interface CodeInfo {
|
|
338
|
+
/** Raw code text (all text nodes concatenated). */
|
|
339
|
+
code: string;
|
|
340
|
+
/** Language identifier (lowercase), e.g. `'ts'`. Defaults to `'text'` if not found. */
|
|
341
|
+
language: string;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Finds the inner `<code>` in a `<pre>` HAST element and extracts the language and raw code.
|
|
345
|
+
*
|
|
346
|
+
* The language is taken from the value starting with `language-...` in the `<code>`'s
|
|
347
|
+
* `className` (e.g. `language-ts` → `ts`). Returns `'text'` if not found.
|
|
348
|
+
* The code is built by concatenating all descendant text nodes of the `<code>`.
|
|
349
|
+
*/
|
|
350
|
+
declare function extractCodeInfo(pre: Element): CodeInfo;
|
|
351
|
+
/** Converts a language name to a file extension (for the download button). Defaults to `'txt'`. */
|
|
352
|
+
declare function languageToExtension(lang: string): string;
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Custom component bound to `table` HAST tags (`table` → this in COMPONENT_MAP).
|
|
356
|
+
*
|
|
357
|
+
* Operating principle (AVOIDING RECURSION):
|
|
358
|
+
* If we re-render the `table` node ITSELF, we get infinite recursion
|
|
359
|
+
* (because `table` is in COMPONENT_MAP). So we create the `<table>` DOM
|
|
360
|
+
* element ourselves with Renderer2 and render only the node's CHILDREN
|
|
361
|
+
* (thead/tbody/tr ...) into that element.
|
|
362
|
+
*
|
|
363
|
+
* Additionally, a small toolbar is added for copying the table as Markdown/CSV
|
|
364
|
+
* and downloading it as CSV.
|
|
365
|
+
*/
|
|
366
|
+
declare class TableComponent implements OnDestroy {
|
|
367
|
+
/** HAST `table` element node to render */
|
|
368
|
+
readonly node: _angular_core.InputSignal<Element>;
|
|
369
|
+
protected readonly t: streamdown_angular.StreamdownTranslations;
|
|
370
|
+
/** Temporary "copied" message for a toolbar action (`null` when none) */
|
|
371
|
+
readonly copied: _angular_core.WritableSignal<string | null>;
|
|
372
|
+
private readonly hostElRef;
|
|
373
|
+
private readonly vcr;
|
|
374
|
+
private readonly renderer;
|
|
375
|
+
private readonly renderer2;
|
|
376
|
+
private refs;
|
|
377
|
+
private builtTable;
|
|
378
|
+
private feedbackTimer;
|
|
379
|
+
constructor();
|
|
380
|
+
ngOnDestroy(): void;
|
|
381
|
+
/** Copies the Markdown representation of the table to the clipboard. */
|
|
382
|
+
copyMarkdown(): Promise<void>;
|
|
383
|
+
/** Copies the CSV representation of the table to the clipboard. */
|
|
384
|
+
copyCsv(): Promise<void>;
|
|
385
|
+
/** Downloads the table as a `table.csv` file (via Blob). */
|
|
386
|
+
downloadCsv(): void;
|
|
387
|
+
/** Writes text to the clipboard and shows a temporary message (when the Clipboard API is available). */
|
|
388
|
+
private copyToClipboard;
|
|
389
|
+
/** Sets a temporary feedback message visible for ~2s. */
|
|
390
|
+
private flashFeedback;
|
|
391
|
+
/** Clears the built table and its ComponentRefs. */
|
|
392
|
+
private cleanup;
|
|
393
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<TableComponent, never>;
|
|
394
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<TableComponent, "ngx-table", never, { "node": { "alias": "node"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Walks a HAST `table` element and collects the text of each cell.
|
|
399
|
+
* Structure: table → (thead | tbody) → tr → (th | td). A `tr` placed directly
|
|
400
|
+
* under the table is also supported.
|
|
401
|
+
* Each cell's text is `trim()`-ed.
|
|
402
|
+
*/
|
|
403
|
+
declare function extractTableData(table: Element): string[][];
|
|
404
|
+
/** Converts rows to RFC 4180 CSV text (CRLF row separator). */
|
|
405
|
+
declare function toCsv(rows: string[][]): string;
|
|
406
|
+
/**
|
|
407
|
+
* Converts rows to TSV (tab-separated) text.
|
|
408
|
+
* Tabs and newlines inside a cell are replaced with spaces (TSV has no escaping).
|
|
409
|
+
*/
|
|
410
|
+
declare function toTsv(rows: string[][]): string;
|
|
411
|
+
/**
|
|
412
|
+
* Converts rows to a GFM Markdown table.
|
|
413
|
+
* The first row is taken as the header and a `---` separator row is added after it.
|
|
414
|
+
* Returns an empty string for empty input.
|
|
415
|
+
*/
|
|
416
|
+
declare function toMarkdown(rows: string[][]): string;
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Component that converts a `mermaid` language block into an SVG diagram.
|
|
420
|
+
*
|
|
421
|
+
* It is registered in the code-language registry via `provideStreamdownMermaid()`;
|
|
422
|
+
* after that, ```mermaid code blocks are rendered with this component instead of Shiki highlighting.
|
|
423
|
+
*
|
|
424
|
+
* Data flow:
|
|
425
|
+
* code() changes → effect → renderDiagram() → dynamic import('mermaid') → m.render() → innerHTML
|
|
426
|
+
*/
|
|
427
|
+
declare class MermaidComponent {
|
|
428
|
+
/** Raw mermaid source to render (provided by the registry via the `code` input) */
|
|
429
|
+
readonly code: _angular_core.InputSignal<string>;
|
|
430
|
+
/** Container where the SVG is placed */
|
|
431
|
+
private readonly host;
|
|
432
|
+
private readonly renderer;
|
|
433
|
+
protected readonly t: streamdown_angular.StreamdownTranslations;
|
|
434
|
+
/** Indicates a render is in progress (loading state) */
|
|
435
|
+
protected readonly rendering: _angular_core.WritableSignal<boolean>;
|
|
436
|
+
/** Error message (when non-empty, the error box is shown) */
|
|
437
|
+
protected readonly errorMessage: _angular_core.WritableSignal<string | null>;
|
|
438
|
+
/** Whether at least one successful render has occurred (for streaming UX) */
|
|
439
|
+
protected readonly hasRendered: _angular_core.WritableSignal<boolean>;
|
|
440
|
+
/**
|
|
441
|
+
* Sequence number of the most recent render request. Async renders may finish out of order —
|
|
442
|
+
* we only write the result of the latest request to the DOM (to avoid a race condition).
|
|
443
|
+
*/
|
|
444
|
+
private renderSeq;
|
|
445
|
+
constructor();
|
|
446
|
+
/**
|
|
447
|
+
* Dynamically loads mermaid and renders the current `code()` to SVG.
|
|
448
|
+
*
|
|
449
|
+
* IMPORTANT: first we validate the syntax with `mermaid.parse(..., { suppressErrors: true })`.
|
|
450
|
+
* During streaming, when the code is not yet complete, parse returns `false` — in that case
|
|
451
|
+
* we do NOT call `m.render()` AT ALL (so mermaid's "Syntax error" bomb graphic does not
|
|
452
|
+
* appear) and we keep the previous diagram.
|
|
453
|
+
*/
|
|
454
|
+
private renderDiagram;
|
|
455
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MermaidComponent, never>;
|
|
456
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<MermaidComponent, "ngx-mermaid", never, { "code": { "alias": "code"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Enables mermaid diagrams. Add `provideStreamdownMermaid()` in `app.config.ts`:
|
|
461
|
+
*
|
|
462
|
+
* export const appConfig: ApplicationConfig = {
|
|
463
|
+
* providers: [provideStreamdownMermaid()],
|
|
464
|
+
* };
|
|
465
|
+
*
|
|
466
|
+
* This binds the `mermaid` language to the code-language registry, so ```mermaid code blocks
|
|
467
|
+
* are rendered as SVG diagrams via `MermaidComponent`.
|
|
468
|
+
*/
|
|
469
|
+
declare function provideStreamdownMermaid(): EnvironmentProviders;
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Enables KaTeX math formulas ($inline$ and $$block$$).
|
|
473
|
+
* IMPORTANT: the consuming app MUST import `katex/dist/katex.min.css`.
|
|
474
|
+
*/
|
|
475
|
+
declare function provideStreamdownMath(): EnvironmentProviders;
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Sanitize schema that preserves KaTeX output.
|
|
479
|
+
* A deeply extended copy of `defaultSchema`:
|
|
480
|
+
* - allows `className`/`style`/`aria-hidden` on all elements,
|
|
481
|
+
* - adds MathML + SVG (katex) tags to `tagNames`.
|
|
482
|
+
* Loosely typed because the token expects `unknown`.
|
|
483
|
+
*/
|
|
484
|
+
declare const katexSanitizeSchema: unknown;
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* UI strings (i18n). Angular equivalent of Streamdown `translations-context.tsx`.
|
|
488
|
+
* Can be partially or fully overridden via `provideStreamdownTranslations({...})`.
|
|
489
|
+
*/
|
|
490
|
+
interface StreamdownTranslations {
|
|
491
|
+
copy: string;
|
|
492
|
+
copied: string;
|
|
493
|
+
download: string;
|
|
494
|
+
copyMarkdown: string;
|
|
495
|
+
copyCsv: string;
|
|
496
|
+
downloadCsv: string;
|
|
497
|
+
openLink: string;
|
|
498
|
+
cancel: string;
|
|
499
|
+
open: string;
|
|
500
|
+
linkWarningTitle: string;
|
|
501
|
+
linkWarningBody: string;
|
|
502
|
+
mermaidError: string;
|
|
503
|
+
renderingDiagram: string;
|
|
504
|
+
imageError: string;
|
|
505
|
+
}
|
|
506
|
+
/** Default (English) strings. */
|
|
507
|
+
declare const DEFAULT_TRANSLATIONS: StreamdownTranslations;
|
|
508
|
+
declare const STREAMDOWN_TRANSLATIONS: InjectionToken<Partial<StreamdownTranslations>>;
|
|
509
|
+
/** Components get UI strings through this service. */
|
|
510
|
+
declare class TranslationsService {
|
|
511
|
+
private readonly overrides;
|
|
512
|
+
/** Merged strings (default + override). */
|
|
513
|
+
readonly t: StreamdownTranslations;
|
|
514
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<TranslationsService, never>;
|
|
515
|
+
static ɵprov: _angular_core.ɵɵInjectableDeclaration<TranslationsService>;
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Overrides the UI strings (i18n). For example, in Uzbek:
|
|
519
|
+
* provideStreamdownTranslations({ copy: 'Nusxa', copied: 'Nusxalandi', download: 'Yuklab olish' })
|
|
520
|
+
*/
|
|
521
|
+
declare function provideStreamdownTranslations(translations: Partial<StreamdownTranslations>): EnvironmentProviders;
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Icons (inline SVG strings). Equivalent of Streamdown `icon-context.tsx`.
|
|
525
|
+
* Each value is a full `<svg>...</svg>` string. Override with your own icons
|
|
526
|
+
* via `provideStreamdownIcons({...})` (e.g. lucide/heroicons).
|
|
527
|
+
*/
|
|
528
|
+
interface StreamdownIcons {
|
|
529
|
+
copy: string;
|
|
530
|
+
check: string;
|
|
531
|
+
download: string;
|
|
532
|
+
externalLink: string;
|
|
533
|
+
close: string;
|
|
534
|
+
chevronDown: string;
|
|
535
|
+
}
|
|
536
|
+
/** Default icons (minimal SVG in lucide style). */
|
|
537
|
+
declare const DEFAULT_ICONS: StreamdownIcons;
|
|
538
|
+
declare const STREAMDOWN_ICONS: InjectionToken<Partial<StreamdownIcons>>;
|
|
539
|
+
/** Components get icons through this service. */
|
|
540
|
+
declare class IconService {
|
|
541
|
+
private readonly overrides;
|
|
542
|
+
/** Merged icons (default + override). */
|
|
543
|
+
readonly icons: StreamdownIcons;
|
|
544
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<IconService, never>;
|
|
545
|
+
static ɵprov: _angular_core.ɵɵInjectableDeclaration<IconService>;
|
|
546
|
+
}
|
|
547
|
+
/** Overrides the icons. */
|
|
548
|
+
declare function provideStreamdownIcons(icons: Partial<StreamdownIcons>): EnvironmentProviders;
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* CSS class/data-attribute prefix. Equivalent of Streamdown `prefix-context.tsx`.
|
|
552
|
+
* Provides a stable "hook" for styling (e.g. `data-streamdown="code-block"`).
|
|
553
|
+
*/
|
|
554
|
+
declare const STREAMDOWN_PREFIX: InjectionToken<string>;
|
|
555
|
+
declare class PrefixService {
|
|
556
|
+
/** Default prefix 'streamdown'. */
|
|
557
|
+
readonly prefix: string;
|
|
558
|
+
/** `prefixed('code-block')` → `'streamdown-code-block'`. */
|
|
559
|
+
prefixed(name: string): string;
|
|
560
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<PrefixService, never>;
|
|
561
|
+
static ɵprov: _angular_core.ɵɵInjectableDeclaration<PrefixService>;
|
|
562
|
+
}
|
|
563
|
+
/** Overrides the class/data-attribute prefix. */
|
|
564
|
+
declare function provideStreamdownPrefix(prefix: string): EnvironmentProviders;
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Component for markdown images (`img`). Equivalent of Streamdown `lib/image.tsx`.
|
|
568
|
+
*
|
|
569
|
+
* Bound to the `img` tag in COMPONENT_MAP. Comes with a loading skeleton, error state and
|
|
570
|
+
* lazy-loading. The `node` input is the hast `img` element node.
|
|
571
|
+
*/
|
|
572
|
+
declare class ImageComponent {
|
|
573
|
+
/** hast `img` element node */
|
|
574
|
+
readonly node: _angular_core.InputSignal<Element>;
|
|
575
|
+
protected readonly t: streamdown_angular.StreamdownTranslations;
|
|
576
|
+
protected readonly loaded: _angular_core.WritableSignal<boolean>;
|
|
577
|
+
protected readonly errored: _angular_core.WritableSignal<boolean>;
|
|
578
|
+
protected readonly src: _angular_core.Signal<string>;
|
|
579
|
+
protected readonly alt: _angular_core.Signal<string>;
|
|
580
|
+
protected readonly title: _angular_core.Signal<string | null>;
|
|
581
|
+
private prop;
|
|
582
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<ImageComponent, never>;
|
|
583
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<ImageComponent, "ngx-image", never, { "node": { "alias": "node"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* External link confirmation modal. Added to the `StreamdownComponent` template and
|
|
588
|
+
* shown when `LinkModalService.pendingUrl` is set. Otherwise renders nothing.
|
|
589
|
+
*/
|
|
590
|
+
declare class LinkModalComponent {
|
|
591
|
+
private readonly svc;
|
|
592
|
+
protected readonly t: streamdown_angular.StreamdownTranslations;
|
|
593
|
+
protected readonly url: _angular_core.WritableSignal<string | null>;
|
|
594
|
+
confirm(): void;
|
|
595
|
+
cancel(): void;
|
|
596
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<LinkModalComponent, never>;
|
|
597
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<LinkModalComponent, "ngx-link-modal", never, {}, {}, never, never, true, never>;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/** Whether link safety is enabled (default: no). */
|
|
601
|
+
declare const STREAMDOWN_LINK_SAFETY: InjectionToken<boolean>;
|
|
602
|
+
/**
|
|
603
|
+
* Manages the confirmation modal shown when an external link is clicked.
|
|
604
|
+
* Angular equivalent of the Streamdown `lib/link-modal.tsx` logic.
|
|
605
|
+
*/
|
|
606
|
+
declare class LinkModalService {
|
|
607
|
+
/** URL awaiting confirmation (modal is closed when null). */
|
|
608
|
+
readonly pendingUrl: _angular_core.WritableSignal<string | null>;
|
|
609
|
+
/** Opens the modal with the given URL. */
|
|
610
|
+
request(url: string): void;
|
|
611
|
+
/** Confirms: opens the link in a new window and closes the modal. */
|
|
612
|
+
confirm(): void;
|
|
613
|
+
/** Cancels: closes the modal, the link is not opened. */
|
|
614
|
+
cancel(): void;
|
|
615
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<LinkModalService, never>;
|
|
616
|
+
static ɵprov: _angular_core.ɵɵInjectableDeclaration<LinkModalService>;
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Enables the confirmation modal for external links (safety UX).
|
|
620
|
+
* When enabled, clicking http(s) links first shows the modal.
|
|
621
|
+
*/
|
|
622
|
+
declare function provideStreamdownLinkSafety(): EnvironmentProviders;
|
|
623
|
+
|
|
624
|
+
export { BlockComponent, CODE_LANGUAGE_COMPONENTS, COMPONENT_MAP, CodeBlockComponent, DEFAULT_ICONS, DEFAULT_TRANSLATIONS, ELEMENT_CLASSES, HastElementComponent, HastRendererComponent, HastRendererService, IconService, ImageComponent, LinkModalComponent, LinkModalService, MarkdownService, MermaidComponent, PrefixService, STREAMDOWN_ANIMATE, STREAMDOWN_ICONS, STREAMDOWN_LINK_SAFETY, STREAMDOWN_PIPELINE, STREAMDOWN_PREFIX, STREAMDOWN_TRANSLATIONS, ShikiHighlighterService, StreamdownComponent, TableComponent, TranslationsService, cn, detectDirection, extractCodeInfo, extractTableData, katexSanitizeSchema, languageToExtension, parseMarkdownIntoBlocks, provideStreamdownAnimation, provideStreamdownIcons, provideStreamdownLinkSafety, provideStreamdownMath, provideStreamdownMermaid, provideStreamdownPrefix, provideStreamdownTranslations, toCsv, toMarkdown, toTsv };
|
|
625
|
+
export type { CodeInfo, HastChild, HastElementInput, RenderedNode, StreamdownIcons, StreamdownPipelineConfig, StreamdownTranslations };
|