@ai-gui/core 0.21.0 → 0.22.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 +5 -2
- package/dist/index.cjs +66 -3
- package/dist/index.d.cts +64 -1
- package/dist/index.d.ts +64 -1
- package/dist/index.js +65 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -110,12 +110,15 @@ const runtime = createActionRuntime({ registry: actions, cardStore })
|
|
|
110
110
|
|
|
111
111
|
## Exports
|
|
112
112
|
|
|
113
|
-
- `Renderer` — `push(chunk)`, `feed(AsyncIterable | ReadableStream)`, `reset()`; constructor `{ registry?, plugins?, sanitize?, onPatch?(patches, nodes) }`.
|
|
113
|
+
- `Renderer` — `push(chunk)`, `feed(AsyncIterable | ReadableStream)`, `reset()`, `setPlugins(plugins)`; constructor `{ registry?, plugins?, sanitize?, rawHtml?, onPatch?(patches, nodes) }`.
|
|
114
|
+
- `setPlugins` swaps the grammar and reparses the buffered source, so plugins deferred behind a dynamic import can arrive mid-answer without the host replaying what it pushed.
|
|
115
|
+
- `rawHtml: false` escapes raw HTML the model wrote instead of interpreting it — a stray `<code>` in prose otherwise swallows the rest of the line.
|
|
114
116
|
- `StreamRouter` — demultiplex one stream into named channels: `.channel(name, sink)`, `.on(name, cb)`, `.feed(source)`.
|
|
115
117
|
- `CardRegistry` — `register(def)`, `parse(type, rawJson)`, `getRender(type)`, `toPromptSpec()`, `toJSONSchema()`.
|
|
116
118
|
- `CardStore` — `register`, `get`, `list`, `subscribe`, `apply`, `applyAll`, `delete`, `clear`, `snapshot`, and `restore` for Cards with stable IDs.
|
|
117
119
|
- `ActionRegistry`, `ActionRuntime`, `createActionRuntime`, `getActionKey`, `getIdleActionState` — validated application-owned action execution and observable lifecycle state.
|
|
118
|
-
- `buildSystemPrompt({ base?, registry?, plugins? })`.
|
|
120
|
+
- `buildSystemPrompt({ base?, registry?, plugins?, locale? })` — collects the card specs and every plugin's `promptSpec`. Pass `plugins`: installing a plugin teaches the renderer to draw a block, not the model to ask for one.
|
|
121
|
+
- `loadPlugins(source)` / `samePlugins(a, b)` — normalize `AIGuiPlugin[] | (() => Promise<AIGuiPlugin[]>)` and compare two lists by members.
|
|
119
122
|
- Utilities: `parsePartialJSON`, `repairMarkdown`, `sanitizeHtml`, `createParser`, `diffAst`, `collectNodeRenderers`.
|
|
120
123
|
- Types: `ASTNode`, `Patch`, `RenderOutput` (`html | element | card | mount`), `CardDef`, `AIGuiPlugin`, `NodeRenderer`, `RendererOptions`, `JSONSchema`.
|
|
121
124
|
|
package/dist/index.cjs
CHANGED
|
@@ -1443,6 +1443,28 @@ function settleCardAction(settle) {
|
|
|
1443
1443
|
|
|
1444
1444
|
//#endregion
|
|
1445
1445
|
//#region src/plugins.ts
|
|
1446
|
+
/**
|
|
1447
|
+
* Resolve a plugin source to what the caller can act on now: the array itself, or a promise of it.
|
|
1448
|
+
*
|
|
1449
|
+
* The array form must stay synchronous. Deferring it by a microtask would render the first chunk
|
|
1450
|
+
* of every answer under the plain-markdown grammar and then redraw it, which is a visible flash
|
|
1451
|
+
* for a host that had its plugins all along.
|
|
1452
|
+
*/
|
|
1453
|
+
function loadPlugins(source) {
|
|
1454
|
+
if (!source) return [];
|
|
1455
|
+
return Array.isArray(source) ? source : source();
|
|
1456
|
+
}
|
|
1457
|
+
/**
|
|
1458
|
+
* Whether two lists hold the same plugins in the same order.
|
|
1459
|
+
*
|
|
1460
|
+
* `plugins={[chart, katex]}` is a new array on every render and the same two plugins every time.
|
|
1461
|
+
* What matters is the members, not the array.
|
|
1462
|
+
*/
|
|
1463
|
+
function samePlugins(a, b) {
|
|
1464
|
+
if (a === b) return true;
|
|
1465
|
+
if (!a || !b || a.length !== b.length) return false;
|
|
1466
|
+
return a.every((plugin, index) => plugin === b[index]);
|
|
1467
|
+
}
|
|
1446
1468
|
/** Merge every plugin's `nodeRenderers` into a single map (later plugins win). */
|
|
1447
1469
|
function collectNodeRenderers(plugins = [], debugOptions = {}) {
|
|
1448
1470
|
const map = {};
|
|
@@ -1529,7 +1551,7 @@ function createParser(options = {}) {
|
|
|
1529
1551
|
/** Build a parser that also reports the source range for each top-level block. */
|
|
1530
1552
|
function createParserWithMetadata(options = {}) {
|
|
1531
1553
|
const md = new markdown_it.default({
|
|
1532
|
-
html:
|
|
1554
|
+
html: options.rawHtml !== false,
|
|
1533
1555
|
linkify: true
|
|
1534
1556
|
});
|
|
1535
1557
|
options.configureMd?.(md);
|
|
@@ -2047,12 +2069,51 @@ var Renderer = class {
|
|
|
2047
2069
|
this.options = options;
|
|
2048
2070
|
this.debug = new DebugEmitter(this.debugSource, options);
|
|
2049
2071
|
this.sanitize = options.sanitize === false ? false : typeof options.sanitize === "object" ? options.sanitize : {};
|
|
2050
|
-
|
|
2072
|
+
this.registerPluginCards(options.plugins);
|
|
2051
2073
|
this.parse = createParserWithMetadata({
|
|
2052
2074
|
registry: options.registry,
|
|
2053
|
-
plugins: options.plugins
|
|
2075
|
+
plugins: options.plugins,
|
|
2076
|
+
rawHtml: options.rawHtml
|
|
2054
2077
|
});
|
|
2055
2078
|
}
|
|
2079
|
+
/** The plugins currently parsing and rendering this renderer's output. */
|
|
2080
|
+
get plugins() {
|
|
2081
|
+
return this.options.plugins ?? [];
|
|
2082
|
+
}
|
|
2083
|
+
/**
|
|
2084
|
+
* Swap the plugins in and redraw the answer already buffered.
|
|
2085
|
+
*
|
|
2086
|
+
* A plugin bundle is worth deferring — diagrams, maths and charts together outweigh everything
|
|
2087
|
+
* else a page loads — but the stream does not wait for the import, so whatever arrived meanwhile
|
|
2088
|
+
* was parsed under the plain-markdown grammar. Reparsing the buffer this renderer still holds is
|
|
2089
|
+
* what turns that text into diagrams, which is why a host does not have to remember what it
|
|
2090
|
+
* pushed and replay it once the chunk lands.
|
|
2091
|
+
*
|
|
2092
|
+
* Passing the same plugins again is a no-op, so a host may call this on every render.
|
|
2093
|
+
*/
|
|
2094
|
+
setPlugins(plugins) {
|
|
2095
|
+
if (samePlugins(this.options.plugins, plugins)) return;
|
|
2096
|
+
this.options = {
|
|
2097
|
+
...this.options,
|
|
2098
|
+
plugins
|
|
2099
|
+
};
|
|
2100
|
+
this.registerPluginCards(plugins);
|
|
2101
|
+
this.parse = createParserWithMetadata({
|
|
2102
|
+
registry: this.options.registry,
|
|
2103
|
+
plugins,
|
|
2104
|
+
rawHtml: this.options.rawHtml
|
|
2105
|
+
});
|
|
2106
|
+
this.parsed = void 0;
|
|
2107
|
+
this.renderScheduled = false;
|
|
2108
|
+
this.scheduleGeneration++;
|
|
2109
|
+
if (this.debug.active) this.debug.emit("plugins-changed", { plugins: (plugins ?? []).map((plugin) => plugin.name) });
|
|
2110
|
+
this.render();
|
|
2111
|
+
}
|
|
2112
|
+
registerPluginCards(plugins) {
|
|
2113
|
+
const registry = this.options.registry;
|
|
2114
|
+
if (!registry) return;
|
|
2115
|
+
for (const plugin of plugins ?? []) for (const card of plugin.cards ?? []) registry.register(card);
|
|
2116
|
+
}
|
|
2056
2117
|
get debugEnabled() {
|
|
2057
2118
|
return this.debug.available;
|
|
2058
2119
|
}
|
|
@@ -2844,6 +2905,7 @@ exports.getIdleActionState = getIdleActionState
|
|
|
2844
2905
|
exports.injectPluginStyles = injectPluginStyles
|
|
2845
2906
|
exports.isCardPatchResult = isCardPatchResult
|
|
2846
2907
|
exports.jsonLines = jsonLines
|
|
2908
|
+
exports.loadPlugins = loadPlugins
|
|
2847
2909
|
exports.mockModelStream = mockModelStream
|
|
2848
2910
|
exports.ndjson = ndjson
|
|
2849
2911
|
exports.parsePartialJSON = parsePartialJSON
|
|
@@ -2853,6 +2915,7 @@ exports.readableBytes = readableBytes
|
|
|
2853
2915
|
exports.repairMarkdown = repairMarkdown
|
|
2854
2916
|
exports.resolveMessages = resolveMessages
|
|
2855
2917
|
exports.safeDebugValue = safeDebugValue
|
|
2918
|
+
exports.samePlugins = samePlugins
|
|
2856
2919
|
exports.sanitizeHtml = sanitizeHtml
|
|
2857
2920
|
exports.sanitizeRenderedHtml = sanitizeRenderedHtml
|
|
2858
2921
|
exports.textLines = textLines
|
package/dist/index.d.cts
CHANGED
|
@@ -265,6 +265,17 @@ interface RendererOptions extends DebugOptions {
|
|
|
265
265
|
registry?: CardRegistry;
|
|
266
266
|
plugins?: AIGuiPlugin[];
|
|
267
267
|
sanitize?: boolean | SanitizeHtmlOptions;
|
|
268
|
+
/**
|
|
269
|
+
* Whether raw HTML in the model's output is interpreted as markup. On by default.
|
|
270
|
+
*
|
|
271
|
+
* A tag a model wrote inside prose is usually text it is describing, not markup it means: one
|
|
272
|
+
* stray `<code>` in a sentence about code swallows the rest of the line into an element. Turning
|
|
273
|
+
* this off escapes every tag the model writes and shows the characters instead, which is what a
|
|
274
|
+
* product wants when the model is meant to produce markdown and nothing else. It is not a
|
|
275
|
+
* substitute for `sanitize` — a plugin's own markup and the host's cards are unaffected either
|
|
276
|
+
* way.
|
|
277
|
+
*/
|
|
278
|
+
rawHtml?: boolean;
|
|
268
279
|
/** Coalesce multiple pushes by scheduling one render callback. */
|
|
269
280
|
scheduler?: (render: () => void) => void;
|
|
270
281
|
onPatch?: (patches: Patch[], nodes: ASTNode[]) => void;
|
|
@@ -614,6 +625,17 @@ interface ParserOptions {
|
|
|
614
625
|
registry?: CardRegistry;
|
|
615
626
|
plugins?: AIGuiPlugin[];
|
|
616
627
|
configureMd?: (md: MarkdownIt) => void;
|
|
628
|
+
/**
|
|
629
|
+
* Whether raw HTML in the model's output is interpreted as markup. On by default.
|
|
630
|
+
*
|
|
631
|
+
* A model writing *about* code emits tags in prose it never meant as markup — a line like
|
|
632
|
+
* `return "done\n<code>"` is text, but interpreting it swallows everything after the tag into an
|
|
633
|
+
* element and the rest of the sentence lands outside it. Sanitizing does not help: `<code>` is a
|
|
634
|
+
* tag any allowlist keeps. Turn this off and every tag the model writes is escaped and shown as
|
|
635
|
+
* the characters it wrote, so a model that gets HTML wrong produces an ugly line rather than a
|
|
636
|
+
* mangled answer.
|
|
637
|
+
*/
|
|
638
|
+
rawHtml?: boolean;
|
|
617
639
|
}
|
|
618
640
|
interface SourceBlock {
|
|
619
641
|
start: number;
|
|
@@ -636,6 +658,32 @@ declare function createParserWithMetadata(options?: ParserOptions): (src: string
|
|
|
636
658
|
interface CollectNodeRendererOptions extends DebugOptions {
|
|
637
659
|
debugTarget?: DebugInstrumentationTarget;
|
|
638
660
|
}
|
|
661
|
+
/** A function that produces the plugins, loading them first if they are not in the bundle yet. */
|
|
662
|
+
type PluginsLoader = () => AIGuiPlugin[] | Promise<AIGuiPlugin[]>;
|
|
663
|
+
/**
|
|
664
|
+
* Either the plugins themselves or a function that loads them.
|
|
665
|
+
*
|
|
666
|
+
* Diagrams, maths and charts are the heaviest thing a page carrying them loads, and an answer that
|
|
667
|
+
* never draws one should not pay for them. A loader lets the host defer the import: the renderer
|
|
668
|
+
* shows plain markdown until it resolves and reparses what has arrived by then, so the host does
|
|
669
|
+
* not have to hold the stream or replay it.
|
|
670
|
+
*/
|
|
671
|
+
type PluginSource = AIGuiPlugin[] | PluginsLoader;
|
|
672
|
+
/**
|
|
673
|
+
* Resolve a plugin source to what the caller can act on now: the array itself, or a promise of it.
|
|
674
|
+
*
|
|
675
|
+
* The array form must stay synchronous. Deferring it by a microtask would render the first chunk
|
|
676
|
+
* of every answer under the plain-markdown grammar and then redraw it, which is a visible flash
|
|
677
|
+
* for a host that had its plugins all along.
|
|
678
|
+
*/
|
|
679
|
+
declare function loadPlugins(source?: PluginSource): AIGuiPlugin[] | Promise<AIGuiPlugin[]>;
|
|
680
|
+
/**
|
|
681
|
+
* Whether two lists hold the same plugins in the same order.
|
|
682
|
+
*
|
|
683
|
+
* `plugins={[chart, katex]}` is a new array on every render and the same two plugins every time.
|
|
684
|
+
* What matters is the members, not the array.
|
|
685
|
+
*/
|
|
686
|
+
declare function samePlugins(a?: AIGuiPlugin[], b?: AIGuiPlugin[]): boolean;
|
|
639
687
|
/** Merge every plugin's `nodeRenderers` into a single map (later plugins win). */
|
|
640
688
|
declare function collectNodeRenderers(plugins?: AIGuiPlugin[], debugOptions?: CollectNodeRendererOptions): Record<string, NodeRenderer>;
|
|
641
689
|
/** The set of node types claimed by the given plugins. */
|
|
@@ -798,6 +846,21 @@ declare class Renderer {
|
|
|
798
846
|
private scheduleGeneration;
|
|
799
847
|
private readonly debug;
|
|
800
848
|
constructor(options?: RendererOptions);
|
|
849
|
+
/** The plugins currently parsing and rendering this renderer's output. */
|
|
850
|
+
get plugins(): readonly AIGuiPlugin[];
|
|
851
|
+
/**
|
|
852
|
+
* Swap the plugins in and redraw the answer already buffered.
|
|
853
|
+
*
|
|
854
|
+
* A plugin bundle is worth deferring — diagrams, maths and charts together outweigh everything
|
|
855
|
+
* else a page loads — but the stream does not wait for the import, so whatever arrived meanwhile
|
|
856
|
+
* was parsed under the plain-markdown grammar. Reparsing the buffer this renderer still holds is
|
|
857
|
+
* what turns that text into diagrams, which is why a host does not have to remember what it
|
|
858
|
+
* pushed and replay it once the chunk lands.
|
|
859
|
+
*
|
|
860
|
+
* Passing the same plugins again is a no-op, so a host may call this on every render.
|
|
861
|
+
*/
|
|
862
|
+
setPlugins(plugins: AIGuiPlugin[] | undefined): void;
|
|
863
|
+
private registerPluginCards;
|
|
801
864
|
get debugEnabled(): boolean;
|
|
802
865
|
emitDebug(type: string, data?: Record<string, unknown>): void;
|
|
803
866
|
push(chunk: string): void;
|
|
@@ -922,4 +985,4 @@ declare function mockModelStream(events: Iterable<ModelStreamEvent> | AsyncItera
|
|
|
922
985
|
declare function readableBytes(chunks: Iterable<string | Uint8Array> | AsyncIterable<string | Uint8Array>): ReadableStream<Uint8Array>;
|
|
923
986
|
|
|
924
987
|
//#endregion
|
|
925
|
-
export { AIGuiPlugin, ASTNode, ActionAbortedError, ActionAlreadyRegisteredError, ActionContext, ActionDefinition, ActionDestroyedError, ActionDispatchOptions, ActionErrorEvent, ActionEventBase, ActionExecutionError, ActionNotFoundError, ActionOutcome, ActionRegisterOptions, ActionRegistry, ActionRequest, ActionRuntime, ActionRuntimeError, ActionRuntimeOptions, ActionStartEvent, ActionState, ActionStateListener, ActionStatus, ActionSuccessEvent, ActionTimeoutError, ActionValidationError, BuildSystemPromptOptions, ByteStreamSource, CARD_ID_MAX_LENGTH, CARD_JSON_MAX_DEPTH, CARD_JSON_MAX_NODES, CARD_PATCH_BATCH_MAX_SIZE, CardAction, CardActionError, CardDef, CardJSONError, CardLimitError, CardListener, CardNotFoundError, CardParseResult, CardPatch, CardPatchBatch, CardPatchResult, CardRecord, CardRegistry, CardRevisionConflictError, CardSnapshot, CardSnapshotError, CardStore, CardStoreError, CardStoreListener, CardStoreOptions, CardTypeConflictError, CardValidationError, ChannelSink, Citation, CollectNodeRendererOptions, DEFAULT_LOCALE, DebugEmitter, DebugEvent, DebugEventListener, DebugEventTarget, DebugInstrumentationTarget, DebugOptions, DebugRedactContext, DebugSource, ExportImageOptions, ExportedImage, FeedChunk, FeedOptions, FeedSource, JSONSchema, JSONSchemaValidationResult, Locale, MessageBundle, Messages, ModelStreamEvent, MountCardSlotRequest, MountedCardSlot, NodeRenderContext, NodeRenderer, OutcomeTone, ParseResult, ParserOptions, PartialJSONResult, Patch, PluginCommitContext, PluginStyle, RenderMountContext, RenderOutput, Renderer, RendererOptions, SSEEvent, SSEOptions, SafeDebugValueOptions, SanitizeHtmlOptions, SanitizeSetting, SourceBlock, StreamParseOptions, StreamRouter, Usage, actionOutcome, applyPatches, availableLocales, baseCss, buildSystemPrompt, collectNodeRenderers, collectPluginStyles, contentDeltas, createActionRuntime, createParser, createParserWithMetadata, diffAst, downloadImage, exportRenderedImages, exportSVGToImage, getActionKey, getIdleActionState, injectPluginStyles, isCardPatchResult, jsonLines, mockModelStream, ndjson, parsePartialJSON, parseSSE, pluginNodeTypes, readableBytes, repairMarkdown, resolveMessages, safeDebugValue, sanitizeHtml, sanitizeRenderedHtml, textLines, translate, translator, validateJSONSchema };
|
|
988
|
+
export { AIGuiPlugin, ASTNode, ActionAbortedError, ActionAlreadyRegisteredError, ActionContext, ActionDefinition, ActionDestroyedError, ActionDispatchOptions, ActionErrorEvent, ActionEventBase, ActionExecutionError, ActionNotFoundError, ActionOutcome, ActionRegisterOptions, ActionRegistry, ActionRequest, ActionRuntime, ActionRuntimeError, ActionRuntimeOptions, ActionStartEvent, ActionState, ActionStateListener, ActionStatus, ActionSuccessEvent, ActionTimeoutError, ActionValidationError, BuildSystemPromptOptions, ByteStreamSource, CARD_ID_MAX_LENGTH, CARD_JSON_MAX_DEPTH, CARD_JSON_MAX_NODES, CARD_PATCH_BATCH_MAX_SIZE, CardAction, CardActionError, CardDef, CardJSONError, CardLimitError, CardListener, CardNotFoundError, CardParseResult, CardPatch, CardPatchBatch, CardPatchResult, CardRecord, CardRegistry, CardRevisionConflictError, CardSnapshot, CardSnapshotError, CardStore, CardStoreError, CardStoreListener, CardStoreOptions, CardTypeConflictError, CardValidationError, ChannelSink, Citation, CollectNodeRendererOptions, DEFAULT_LOCALE, DebugEmitter, DebugEvent, DebugEventListener, DebugEventTarget, DebugInstrumentationTarget, DebugOptions, DebugRedactContext, DebugSource, ExportImageOptions, ExportedImage, FeedChunk, FeedOptions, FeedSource, JSONSchema, JSONSchemaValidationResult, Locale, MessageBundle, Messages, ModelStreamEvent, MountCardSlotRequest, MountedCardSlot, NodeRenderContext, NodeRenderer, OutcomeTone, ParseResult, ParserOptions, PartialJSONResult, Patch, PluginCommitContext, PluginSource, PluginStyle, PluginsLoader, RenderMountContext, RenderOutput, Renderer, RendererOptions, SSEEvent, SSEOptions, SafeDebugValueOptions, SanitizeHtmlOptions, SanitizeSetting, SourceBlock, StreamParseOptions, StreamRouter, Usage, actionOutcome, applyPatches, availableLocales, baseCss, buildSystemPrompt, collectNodeRenderers, collectPluginStyles, contentDeltas, createActionRuntime, createParser, createParserWithMetadata, diffAst, downloadImage, exportRenderedImages, exportSVGToImage, getActionKey, getIdleActionState, injectPluginStyles, isCardPatchResult, jsonLines, loadPlugins, mockModelStream, ndjson, parsePartialJSON, parseSSE, pluginNodeTypes, readableBytes, repairMarkdown, resolveMessages, safeDebugValue, samePlugins, sanitizeHtml, sanitizeRenderedHtml, textLines, translate, translator, validateJSONSchema };
|
package/dist/index.d.ts
CHANGED
|
@@ -265,6 +265,17 @@ interface RendererOptions extends DebugOptions {
|
|
|
265
265
|
registry?: CardRegistry;
|
|
266
266
|
plugins?: AIGuiPlugin[];
|
|
267
267
|
sanitize?: boolean | SanitizeHtmlOptions;
|
|
268
|
+
/**
|
|
269
|
+
* Whether raw HTML in the model's output is interpreted as markup. On by default.
|
|
270
|
+
*
|
|
271
|
+
* A tag a model wrote inside prose is usually text it is describing, not markup it means: one
|
|
272
|
+
* stray `<code>` in a sentence about code swallows the rest of the line into an element. Turning
|
|
273
|
+
* this off escapes every tag the model writes and shows the characters instead, which is what a
|
|
274
|
+
* product wants when the model is meant to produce markdown and nothing else. It is not a
|
|
275
|
+
* substitute for `sanitize` — a plugin's own markup and the host's cards are unaffected either
|
|
276
|
+
* way.
|
|
277
|
+
*/
|
|
278
|
+
rawHtml?: boolean;
|
|
268
279
|
/** Coalesce multiple pushes by scheduling one render callback. */
|
|
269
280
|
scheduler?: (render: () => void) => void;
|
|
270
281
|
onPatch?: (patches: Patch[], nodes: ASTNode[]) => void;
|
|
@@ -614,6 +625,17 @@ interface ParserOptions {
|
|
|
614
625
|
registry?: CardRegistry;
|
|
615
626
|
plugins?: AIGuiPlugin[];
|
|
616
627
|
configureMd?: (md: MarkdownIt) => void;
|
|
628
|
+
/**
|
|
629
|
+
* Whether raw HTML in the model's output is interpreted as markup. On by default.
|
|
630
|
+
*
|
|
631
|
+
* A model writing *about* code emits tags in prose it never meant as markup — a line like
|
|
632
|
+
* `return "done\n<code>"` is text, but interpreting it swallows everything after the tag into an
|
|
633
|
+
* element and the rest of the sentence lands outside it. Sanitizing does not help: `<code>` is a
|
|
634
|
+
* tag any allowlist keeps. Turn this off and every tag the model writes is escaped and shown as
|
|
635
|
+
* the characters it wrote, so a model that gets HTML wrong produces an ugly line rather than a
|
|
636
|
+
* mangled answer.
|
|
637
|
+
*/
|
|
638
|
+
rawHtml?: boolean;
|
|
617
639
|
}
|
|
618
640
|
interface SourceBlock {
|
|
619
641
|
start: number;
|
|
@@ -636,6 +658,32 @@ declare function createParserWithMetadata(options?: ParserOptions): (src: string
|
|
|
636
658
|
interface CollectNodeRendererOptions extends DebugOptions {
|
|
637
659
|
debugTarget?: DebugInstrumentationTarget;
|
|
638
660
|
}
|
|
661
|
+
/** A function that produces the plugins, loading them first if they are not in the bundle yet. */
|
|
662
|
+
type PluginsLoader = () => AIGuiPlugin[] | Promise<AIGuiPlugin[]>;
|
|
663
|
+
/**
|
|
664
|
+
* Either the plugins themselves or a function that loads them.
|
|
665
|
+
*
|
|
666
|
+
* Diagrams, maths and charts are the heaviest thing a page carrying them loads, and an answer that
|
|
667
|
+
* never draws one should not pay for them. A loader lets the host defer the import: the renderer
|
|
668
|
+
* shows plain markdown until it resolves and reparses what has arrived by then, so the host does
|
|
669
|
+
* not have to hold the stream or replay it.
|
|
670
|
+
*/
|
|
671
|
+
type PluginSource = AIGuiPlugin[] | PluginsLoader;
|
|
672
|
+
/**
|
|
673
|
+
* Resolve a plugin source to what the caller can act on now: the array itself, or a promise of it.
|
|
674
|
+
*
|
|
675
|
+
* The array form must stay synchronous. Deferring it by a microtask would render the first chunk
|
|
676
|
+
* of every answer under the plain-markdown grammar and then redraw it, which is a visible flash
|
|
677
|
+
* for a host that had its plugins all along.
|
|
678
|
+
*/
|
|
679
|
+
declare function loadPlugins(source?: PluginSource): AIGuiPlugin[] | Promise<AIGuiPlugin[]>;
|
|
680
|
+
/**
|
|
681
|
+
* Whether two lists hold the same plugins in the same order.
|
|
682
|
+
*
|
|
683
|
+
* `plugins={[chart, katex]}` is a new array on every render and the same two plugins every time.
|
|
684
|
+
* What matters is the members, not the array.
|
|
685
|
+
*/
|
|
686
|
+
declare function samePlugins(a?: AIGuiPlugin[], b?: AIGuiPlugin[]): boolean;
|
|
639
687
|
/** Merge every plugin's `nodeRenderers` into a single map (later plugins win). */
|
|
640
688
|
declare function collectNodeRenderers(plugins?: AIGuiPlugin[], debugOptions?: CollectNodeRendererOptions): Record<string, NodeRenderer>;
|
|
641
689
|
/** The set of node types claimed by the given plugins. */
|
|
@@ -798,6 +846,21 @@ declare class Renderer {
|
|
|
798
846
|
private scheduleGeneration;
|
|
799
847
|
private readonly debug;
|
|
800
848
|
constructor(options?: RendererOptions);
|
|
849
|
+
/** The plugins currently parsing and rendering this renderer's output. */
|
|
850
|
+
get plugins(): readonly AIGuiPlugin[];
|
|
851
|
+
/**
|
|
852
|
+
* Swap the plugins in and redraw the answer already buffered.
|
|
853
|
+
*
|
|
854
|
+
* A plugin bundle is worth deferring — diagrams, maths and charts together outweigh everything
|
|
855
|
+
* else a page loads — but the stream does not wait for the import, so whatever arrived meanwhile
|
|
856
|
+
* was parsed under the plain-markdown grammar. Reparsing the buffer this renderer still holds is
|
|
857
|
+
* what turns that text into diagrams, which is why a host does not have to remember what it
|
|
858
|
+
* pushed and replay it once the chunk lands.
|
|
859
|
+
*
|
|
860
|
+
* Passing the same plugins again is a no-op, so a host may call this on every render.
|
|
861
|
+
*/
|
|
862
|
+
setPlugins(plugins: AIGuiPlugin[] | undefined): void;
|
|
863
|
+
private registerPluginCards;
|
|
801
864
|
get debugEnabled(): boolean;
|
|
802
865
|
emitDebug(type: string, data?: Record<string, unknown>): void;
|
|
803
866
|
push(chunk: string): void;
|
|
@@ -922,4 +985,4 @@ declare function mockModelStream(events: Iterable<ModelStreamEvent> | AsyncItera
|
|
|
922
985
|
declare function readableBytes(chunks: Iterable<string | Uint8Array> | AsyncIterable<string | Uint8Array>): ReadableStream<Uint8Array>;
|
|
923
986
|
|
|
924
987
|
//#endregion
|
|
925
|
-
export { AIGuiPlugin, ASTNode, ActionAbortedError, ActionAlreadyRegisteredError, ActionContext, ActionDefinition, ActionDestroyedError, ActionDispatchOptions, ActionErrorEvent, ActionEventBase, ActionExecutionError, ActionNotFoundError, ActionOutcome, ActionRegisterOptions, ActionRegistry, ActionRequest, ActionRuntime, ActionRuntimeError, ActionRuntimeOptions, ActionStartEvent, ActionState, ActionStateListener, ActionStatus, ActionSuccessEvent, ActionTimeoutError, ActionValidationError, BuildSystemPromptOptions, ByteStreamSource, CARD_ID_MAX_LENGTH, CARD_JSON_MAX_DEPTH, CARD_JSON_MAX_NODES, CARD_PATCH_BATCH_MAX_SIZE, CardAction, CardActionError, CardDef, CardJSONError, CardLimitError, CardListener, CardNotFoundError, CardParseResult, CardPatch, CardPatchBatch, CardPatchResult, CardRecord, CardRegistry, CardRevisionConflictError, CardSnapshot, CardSnapshotError, CardStore, CardStoreError, CardStoreListener, CardStoreOptions, CardTypeConflictError, CardValidationError, ChannelSink, Citation, CollectNodeRendererOptions, DEFAULT_LOCALE, DebugEmitter, DebugEvent, DebugEventListener, DebugEventTarget, DebugInstrumentationTarget, DebugOptions, DebugRedactContext, DebugSource, ExportImageOptions, ExportedImage, FeedChunk, FeedOptions, FeedSource, JSONSchema, JSONSchemaValidationResult, Locale, MessageBundle, Messages, ModelStreamEvent, MountCardSlotRequest, MountedCardSlot, NodeRenderContext, NodeRenderer, OutcomeTone, ParseResult, ParserOptions, PartialJSONResult, Patch, PluginCommitContext, PluginStyle, RenderMountContext, RenderOutput, Renderer, RendererOptions, SSEEvent, SSEOptions, SafeDebugValueOptions, SanitizeHtmlOptions, SanitizeSetting, SourceBlock, StreamParseOptions, StreamRouter, Usage, actionOutcome, applyPatches, availableLocales, baseCss, buildSystemPrompt, collectNodeRenderers, collectPluginStyles, contentDeltas, createActionRuntime, createParser, createParserWithMetadata, diffAst, downloadImage, exportRenderedImages, exportSVGToImage, getActionKey, getIdleActionState, injectPluginStyles, isCardPatchResult, jsonLines, mockModelStream, ndjson, parsePartialJSON, parseSSE, pluginNodeTypes, readableBytes, repairMarkdown, resolveMessages, safeDebugValue, sanitizeHtml, sanitizeRenderedHtml, textLines, translate, translator, validateJSONSchema };
|
|
988
|
+
export { AIGuiPlugin, ASTNode, ActionAbortedError, ActionAlreadyRegisteredError, ActionContext, ActionDefinition, ActionDestroyedError, ActionDispatchOptions, ActionErrorEvent, ActionEventBase, ActionExecutionError, ActionNotFoundError, ActionOutcome, ActionRegisterOptions, ActionRegistry, ActionRequest, ActionRuntime, ActionRuntimeError, ActionRuntimeOptions, ActionStartEvent, ActionState, ActionStateListener, ActionStatus, ActionSuccessEvent, ActionTimeoutError, ActionValidationError, BuildSystemPromptOptions, ByteStreamSource, CARD_ID_MAX_LENGTH, CARD_JSON_MAX_DEPTH, CARD_JSON_MAX_NODES, CARD_PATCH_BATCH_MAX_SIZE, CardAction, CardActionError, CardDef, CardJSONError, CardLimitError, CardListener, CardNotFoundError, CardParseResult, CardPatch, CardPatchBatch, CardPatchResult, CardRecord, CardRegistry, CardRevisionConflictError, CardSnapshot, CardSnapshotError, CardStore, CardStoreError, CardStoreListener, CardStoreOptions, CardTypeConflictError, CardValidationError, ChannelSink, Citation, CollectNodeRendererOptions, DEFAULT_LOCALE, DebugEmitter, DebugEvent, DebugEventListener, DebugEventTarget, DebugInstrumentationTarget, DebugOptions, DebugRedactContext, DebugSource, ExportImageOptions, ExportedImage, FeedChunk, FeedOptions, FeedSource, JSONSchema, JSONSchemaValidationResult, Locale, MessageBundle, Messages, ModelStreamEvent, MountCardSlotRequest, MountedCardSlot, NodeRenderContext, NodeRenderer, OutcomeTone, ParseResult, ParserOptions, PartialJSONResult, Patch, PluginCommitContext, PluginSource, PluginStyle, PluginsLoader, RenderMountContext, RenderOutput, Renderer, RendererOptions, SSEEvent, SSEOptions, SafeDebugValueOptions, SanitizeHtmlOptions, SanitizeSetting, SourceBlock, StreamParseOptions, StreamRouter, Usage, actionOutcome, applyPatches, availableLocales, baseCss, buildSystemPrompt, collectNodeRenderers, collectPluginStyles, contentDeltas, createActionRuntime, createParser, createParserWithMetadata, diffAst, downloadImage, exportRenderedImages, exportSVGToImage, getActionKey, getIdleActionState, injectPluginStyles, isCardPatchResult, jsonLines, loadPlugins, mockModelStream, ndjson, parsePartialJSON, parseSSE, pluginNodeTypes, readableBytes, repairMarkdown, resolveMessages, safeDebugValue, samePlugins, sanitizeHtml, sanitizeRenderedHtml, textLines, translate, translator, validateJSONSchema };
|
package/dist/index.js
CHANGED
|
@@ -1419,6 +1419,28 @@ function settleCardAction(settle) {
|
|
|
1419
1419
|
|
|
1420
1420
|
//#endregion
|
|
1421
1421
|
//#region src/plugins.ts
|
|
1422
|
+
/**
|
|
1423
|
+
* Resolve a plugin source to what the caller can act on now: the array itself, or a promise of it.
|
|
1424
|
+
*
|
|
1425
|
+
* The array form must stay synchronous. Deferring it by a microtask would render the first chunk
|
|
1426
|
+
* of every answer under the plain-markdown grammar and then redraw it, which is a visible flash
|
|
1427
|
+
* for a host that had its plugins all along.
|
|
1428
|
+
*/
|
|
1429
|
+
function loadPlugins(source) {
|
|
1430
|
+
if (!source) return [];
|
|
1431
|
+
return Array.isArray(source) ? source : source();
|
|
1432
|
+
}
|
|
1433
|
+
/**
|
|
1434
|
+
* Whether two lists hold the same plugins in the same order.
|
|
1435
|
+
*
|
|
1436
|
+
* `plugins={[chart, katex]}` is a new array on every render and the same two plugins every time.
|
|
1437
|
+
* What matters is the members, not the array.
|
|
1438
|
+
*/
|
|
1439
|
+
function samePlugins(a, b) {
|
|
1440
|
+
if (a === b) return true;
|
|
1441
|
+
if (!a || !b || a.length !== b.length) return false;
|
|
1442
|
+
return a.every((plugin, index) => plugin === b[index]);
|
|
1443
|
+
}
|
|
1422
1444
|
/** Merge every plugin's `nodeRenderers` into a single map (later plugins win). */
|
|
1423
1445
|
function collectNodeRenderers(plugins = [], debugOptions = {}) {
|
|
1424
1446
|
const map = {};
|
|
@@ -1505,7 +1527,7 @@ function createParser(options = {}) {
|
|
|
1505
1527
|
/** Build a parser that also reports the source range for each top-level block. */
|
|
1506
1528
|
function createParserWithMetadata(options = {}) {
|
|
1507
1529
|
const md = new MarkdownIt({
|
|
1508
|
-
html:
|
|
1530
|
+
html: options.rawHtml !== false,
|
|
1509
1531
|
linkify: true
|
|
1510
1532
|
});
|
|
1511
1533
|
options.configureMd?.(md);
|
|
@@ -2023,12 +2045,51 @@ var Renderer = class {
|
|
|
2023
2045
|
this.options = options;
|
|
2024
2046
|
this.debug = new DebugEmitter(this.debugSource, options);
|
|
2025
2047
|
this.sanitize = options.sanitize === false ? false : typeof options.sanitize === "object" ? options.sanitize : {};
|
|
2026
|
-
|
|
2048
|
+
this.registerPluginCards(options.plugins);
|
|
2027
2049
|
this.parse = createParserWithMetadata({
|
|
2028
2050
|
registry: options.registry,
|
|
2029
|
-
plugins: options.plugins
|
|
2051
|
+
plugins: options.plugins,
|
|
2052
|
+
rawHtml: options.rawHtml
|
|
2030
2053
|
});
|
|
2031
2054
|
}
|
|
2055
|
+
/** The plugins currently parsing and rendering this renderer's output. */
|
|
2056
|
+
get plugins() {
|
|
2057
|
+
return this.options.plugins ?? [];
|
|
2058
|
+
}
|
|
2059
|
+
/**
|
|
2060
|
+
* Swap the plugins in and redraw the answer already buffered.
|
|
2061
|
+
*
|
|
2062
|
+
* A plugin bundle is worth deferring — diagrams, maths and charts together outweigh everything
|
|
2063
|
+
* else a page loads — but the stream does not wait for the import, so whatever arrived meanwhile
|
|
2064
|
+
* was parsed under the plain-markdown grammar. Reparsing the buffer this renderer still holds is
|
|
2065
|
+
* what turns that text into diagrams, which is why a host does not have to remember what it
|
|
2066
|
+
* pushed and replay it once the chunk lands.
|
|
2067
|
+
*
|
|
2068
|
+
* Passing the same plugins again is a no-op, so a host may call this on every render.
|
|
2069
|
+
*/
|
|
2070
|
+
setPlugins(plugins) {
|
|
2071
|
+
if (samePlugins(this.options.plugins, plugins)) return;
|
|
2072
|
+
this.options = {
|
|
2073
|
+
...this.options,
|
|
2074
|
+
plugins
|
|
2075
|
+
};
|
|
2076
|
+
this.registerPluginCards(plugins);
|
|
2077
|
+
this.parse = createParserWithMetadata({
|
|
2078
|
+
registry: this.options.registry,
|
|
2079
|
+
plugins,
|
|
2080
|
+
rawHtml: this.options.rawHtml
|
|
2081
|
+
});
|
|
2082
|
+
this.parsed = void 0;
|
|
2083
|
+
this.renderScheduled = false;
|
|
2084
|
+
this.scheduleGeneration++;
|
|
2085
|
+
if (this.debug.active) this.debug.emit("plugins-changed", { plugins: (plugins ?? []).map((plugin) => plugin.name) });
|
|
2086
|
+
this.render();
|
|
2087
|
+
}
|
|
2088
|
+
registerPluginCards(plugins) {
|
|
2089
|
+
const registry = this.options.registry;
|
|
2090
|
+
if (!registry) return;
|
|
2091
|
+
for (const plugin of plugins ?? []) for (const card of plugin.cards ?? []) registry.register(card);
|
|
2092
|
+
}
|
|
2032
2093
|
get debugEnabled() {
|
|
2033
2094
|
return this.debug.available;
|
|
2034
2095
|
}
|
|
@@ -2772,4 +2833,4 @@ function delay(ms, signal) {
|
|
|
2772
2833
|
}
|
|
2773
2834
|
|
|
2774
2835
|
//#endregion
|
|
2775
|
-
export { ActionAbortedError, ActionAlreadyRegisteredError, ActionDestroyedError, ActionExecutionError, ActionNotFoundError, ActionRegistry, ActionRuntime, ActionRuntimeError, ActionTimeoutError, ActionValidationError, CARD_ID_MAX_LENGTH, CARD_JSON_MAX_DEPTH, CARD_JSON_MAX_NODES, CARD_PATCH_BATCH_MAX_SIZE, CardJSONError, CardLimitError, CardNotFoundError, CardRegistry, CardRevisionConflictError, CardSnapshotError, CardStore, CardStoreError, CardTypeConflictError, CardValidationError, DEFAULT_LOCALE, DebugEmitter, Renderer, StreamRouter, actionOutcome, applyPatches, availableLocales, baseCss, buildSystemPrompt, collectNodeRenderers, collectPluginStyles, contentDeltas, createActionRuntime, createParser, createParserWithMetadata, diffAst, downloadImage, exportRenderedImages, exportSVGToImage, getActionKey, getIdleActionState, injectPluginStyles, isCardPatchResult, jsonLines, mockModelStream, ndjson, parsePartialJSON, parseSSE, pluginNodeTypes, readableBytes, repairMarkdown, resolveMessages, safeDebugValue, sanitizeHtml, sanitizeRenderedHtml, textLines, translate, translator, validateJSONSchema };
|
|
2836
|
+
export { ActionAbortedError, ActionAlreadyRegisteredError, ActionDestroyedError, ActionExecutionError, ActionNotFoundError, ActionRegistry, ActionRuntime, ActionRuntimeError, ActionTimeoutError, ActionValidationError, CARD_ID_MAX_LENGTH, CARD_JSON_MAX_DEPTH, CARD_JSON_MAX_NODES, CARD_PATCH_BATCH_MAX_SIZE, CardJSONError, CardLimitError, CardNotFoundError, CardRegistry, CardRevisionConflictError, CardSnapshotError, CardStore, CardStoreError, CardTypeConflictError, CardValidationError, DEFAULT_LOCALE, DebugEmitter, Renderer, StreamRouter, actionOutcome, applyPatches, availableLocales, baseCss, buildSystemPrompt, collectNodeRenderers, collectPluginStyles, contentDeltas, createActionRuntime, createParser, createParserWithMetadata, diffAst, downloadImage, exportRenderedImages, exportSVGToImage, getActionKey, getIdleActionState, injectPluginStyles, isCardPatchResult, jsonLines, loadPlugins, mockModelStream, ndjson, parsePartialJSON, parseSSE, pluginNodeTypes, readableBytes, repairMarkdown, resolveMessages, safeDebugValue, samePlugins, sanitizeHtml, sanitizeRenderedHtml, textLines, translate, translator, validateJSONSchema };
|
package/package.json
CHANGED