@ai-gui/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,245 @@
1
+ import MarkdownIt from "markdown-it";
2
+
3
+ //#region src/partial-json.d.ts
4
+ interface PartialJSONResult {
5
+ data: unknown;
6
+ complete: boolean;
7
+ }
8
+ /**
9
+ * Parse a possibly-incomplete JSON string, as produced by a streaming source.
10
+ *
11
+ * If the input is valid JSON, it is returned with `complete: true`. Otherwise
12
+ * the parser attempts to repair the fragment by dropping any trailing
13
+ * incomplete token (a dangling key, colon, or comma) and auto-closing unclosed
14
+ * strings, objects, and arrays, returning whatever data has arrived so far with
15
+ * `complete: false`. Unparseable input yields `{ data: undefined, complete: false }`
16
+ * and never throws.
17
+ */
18
+ declare function parsePartialJSON(input: string): PartialJSONResult;
19
+
20
+ //#endregion
21
+ //#region src/repair-markdown.d.ts
22
+ /**
23
+ * Temporarily repair half-finished markdown so a partial streaming buffer
24
+ * renders smoothly. Operates on a copy of the buffer and returns a new string;
25
+ * it never mutates the input. Pure function, no dependencies.
26
+ *
27
+ * The repairs are intentionally conservative: only unambiguous unclosed
28
+ * inline/fence syntax is completed. Ambiguous constructs (e.g. a dangling
29
+ * link text `[docs`) are left untouched to avoid guessing wrong.
30
+ */
31
+ declare function repairMarkdown(buffer: string): string;
32
+
33
+ //#endregion
34
+ //#region src/sanitizer.d.ts
35
+ /**
36
+ * Sanitize an HTML string, stripping scripts and unsafe attributes.
37
+ *
38
+ * When a DOM is available (browser, or Node with jsdom) DOMPurify is used. In a
39
+ * bare Node environment without a global `window`, DOMPurify cannot run, so we
40
+ * fall back to escaping the HTML-significant characters. This never emits raw
41
+ * markup and never throws.
42
+ */
43
+ declare function sanitizeHtml(html: string): string;
44
+
45
+ //#endregion
46
+ //#region src/types.d.ts
47
+ /** Framework-agnostic render node. */
48
+ interface ASTNode {
49
+ key: string;
50
+ type: string;
51
+ tag?: string;
52
+ content?: string;
53
+ html?: string;
54
+ attrs?: Record<string, string>;
55
+ children?: ASTNode[];
56
+ /** card-specific payload */
57
+ card?: {
58
+ type: string;
59
+ data: unknown;
60
+ complete: boolean;
61
+ valid: boolean;
62
+ };
63
+ }
64
+ /** Patch event produced by diffing. */
65
+ type Patch = {
66
+ op: "insert";
67
+ index: number;
68
+ node: ASTNode;
69
+ } | {
70
+ op: "update";
71
+ key: string;
72
+ node: ASTNode;
73
+ } | {
74
+ op: "remove";
75
+ key: string;
76
+ };
77
+ /** Framework-neutral render descriptor returned by plugin node renderers. */
78
+ type RenderOutput = {
79
+ kind: "html";
80
+ html: string;
81
+ } | {
82
+ kind: "element";
83
+ tag: string;
84
+ props?: Record<string, unknown>;
85
+ children?: RenderOutput[];
86
+ } | {
87
+ kind: "card";
88
+ type: string;
89
+ data: unknown;
90
+ } | {
91
+ kind: "mount";
92
+ mount: (el: HTMLElement) => void | (() => void);
93
+ };
94
+ type NodeRenderer = (node: ASTNode) => RenderOutput | Promise<RenderOutput>;
95
+ interface JSONSchema {
96
+ type?: string;
97
+ properties?: Record<string, JSONSchema>;
98
+ items?: JSONSchema;
99
+ required?: string[];
100
+ [k: string]: unknown;
101
+ }
102
+ interface CardDef<TData = unknown, TComponent = unknown> {
103
+ type: string;
104
+ description: string;
105
+ schema?: JSONSchema;
106
+ example?: TData;
107
+ render?: TComponent;
108
+ validate?: (data: TData) => boolean;
109
+ }
110
+ interface AIGuiPlugin {
111
+ name: string;
112
+ extendParser?: (md: MarkdownIt) => void;
113
+ cards?: CardDef[];
114
+ nodeRenderers?: Record<string, NodeRenderer>;
115
+ isBlockComplete?: (nodeType: string, raw: string) => boolean;
116
+ css?: string;
117
+ /** LLM-facing guidance describing this plugin's fence syntax. */
118
+ promptSpec?: string;
119
+ }
120
+ interface RendererOptions {
121
+ registry?: CardRegistry;
122
+ plugins?: AIGuiPlugin[];
123
+ sanitize?: boolean;
124
+ onPatch?: (patches: Patch[], nodes: ASTNode[]) => void;
125
+ }
126
+
127
+ //#endregion
128
+ //#region src/card-registry.d.ts
129
+ interface CardParseResult {
130
+ data: unknown;
131
+ complete: boolean;
132
+ valid: boolean;
133
+ }
134
+ declare class CardRegistry {
135
+ private cards;
136
+ register(def: CardDef): void;
137
+ has(type: string): boolean;
138
+ getRender(type: string): unknown;
139
+ parse(type: string, rawJson: string): CardParseResult;
140
+ private validate;
141
+ toPromptSpec(): string;
142
+ toJSONSchema(): JSONSchema;
143
+ }
144
+
145
+ //#endregion
146
+ //#region src/parser.d.ts
147
+ interface ParserOptions {
148
+ registry?: CardRegistry;
149
+ plugins?: AIGuiPlugin[];
150
+ configureMd?: (md: MarkdownIt) => void;
151
+ }
152
+ /** Build a parser that turns markdown source into a flat list of ASTNodes. */
153
+ declare function createParser(options?: ParserOptions): (src: string) => ASTNode[];
154
+
155
+ //#endregion
156
+ //#region src/plugins.d.ts
157
+ /** Merge every plugin's `nodeRenderers` into a single map (later plugins win). */
158
+ declare function collectNodeRenderers(plugins?: AIGuiPlugin[]): Record<string, NodeRenderer>;
159
+ /** The set of node types claimed by the given plugins. */
160
+ declare function pluginNodeTypes(plugins?: AIGuiPlugin[]): Set<string>;
161
+
162
+ //#endregion
163
+ //#region src/diff.d.ts
164
+ /** Produce a minimal set of patches turning `prev` into `next`, keyed by node key. */
165
+ declare function diffAst(prev: ASTNode[], next: ASTNode[]): Patch[];
166
+
167
+ //#endregion
168
+ //#region src/renderer.d.ts
169
+ /**
170
+ * Streaming render orchestrator: accumulate incoming markdown chunks, repair the
171
+ * partial buffer, parse it into an AST, diff against the previous AST, and emit
172
+ * the resulting patches via `onPatch`.
173
+ */
174
+ declare class Renderer {
175
+ private buffer;
176
+ private prevAst;
177
+ private parse;
178
+ private options;
179
+ private sanitize;
180
+ constructor(options?: RendererOptions);
181
+ push(chunk: string): void;
182
+ feed(source: AsyncIterable<string> | ReadableStream<string>): Promise<void>;
183
+ reset(): void;
184
+ private render;
185
+ }
186
+
187
+ //#endregion
188
+ //#region src/stream-router.d.ts
189
+ /**
190
+ * A consumer of a channel's text stream. `Renderer` satisfies this shape.
191
+ */
192
+ interface ChannelSink {
193
+ push(chunk: string): void;
194
+ }
195
+ /** Handler for structured data values (and text deltas as raw strings). */
196
+ type ChannelHandler = (value: unknown) => void;
197
+ /**
198
+ * Demultiplexes one incoming stream into multiple named channels so different
199
+ * UI regions can update independently (e.g. `progress` + `content` from a single
200
+ * SSE). Framing is auto-detected per line and supports:
201
+ *
202
+ * 1. JSON envelope: `{"ch":"<channel>","delta":"text"}` (a text delta) or
203
+ * `{"ch":"<channel>","data":<any>}` (a structured value). May appear bare or
204
+ * after a `data: ` prefix.
205
+ * 2. SSE `event: <name>` sets the channel for the following `data:` line(s).
206
+ * 3. A plain `data: <text>` line with no `ch` and no preceding `event:` is a
207
+ * text delta on the default `content` channel.
208
+ */
209
+ declare class StreamRouter {
210
+ private readonly sinks;
211
+ private readonly handlers;
212
+ /** Bind a text-stream sink to a channel: text deltas call `sink.push(delta)`. */
213
+ channel(name: string, sink: ChannelSink): this;
214
+ /** Bind a handler to a channel: data values (and deltas as strings) call it. */
215
+ on(name: string, handler: ChannelHandler): this;
216
+ /**
217
+ * Consume a stream to completion, dispatching each parsed line to its channel.
218
+ * Accepts an async iterable of strings, or a `ReadableStream` of either strings
219
+ * or `Uint8Array` bytes (decoded as UTF-8).
220
+ */
221
+ feed(source: AsyncIterable<string> | ReadableStream<Uint8Array | string>): Promise<void>;
222
+ /** Process a single raw line; returns the (possibly updated) current event. */
223
+ private processLine;
224
+ /** Route a text delta: to a bound sink and/or an on() handler (either/both). */
225
+ private routeDelta;
226
+ /** Route a structured value: to the handler, or a string value to a lone sink. */
227
+ private routeData;
228
+ }
229
+
230
+ //#endregion
231
+ //#region src/build-system-prompt.d.ts
232
+ interface BuildSystemPromptOptions {
233
+ base?: string;
234
+ registry?: CardRegistry;
235
+ plugins?: AIGuiPlugin[];
236
+ }
237
+ /**
238
+ * Assembles the LLM system-prompt guidance: an optional base, the registered
239
+ * cards' spec, and every plugin's promptSpec. Consumers prepend this to their
240
+ * own system prompt so the model knows which fenced blocks it may emit.
241
+ */
242
+ declare function buildSystemPrompt(options?: BuildSystemPromptOptions): string;
243
+
244
+ //#endregion
245
+ export { AIGuiPlugin, ASTNode, BuildSystemPromptOptions, CardDef, CardParseResult, CardRegistry, ChannelSink, JSONSchema, NodeRenderer, ParserOptions, PartialJSONResult, Patch, RenderOutput, Renderer, RendererOptions, StreamRouter, buildSystemPrompt, collectNodeRenderers, createParser, diffAst, parsePartialJSON, pluginNodeTypes, repairMarkdown, sanitizeHtml };
@@ -0,0 +1,245 @@
1
+ import MarkdownIt from "markdown-it";
2
+
3
+ //#region src/partial-json.d.ts
4
+ interface PartialJSONResult {
5
+ data: unknown;
6
+ complete: boolean;
7
+ }
8
+ /**
9
+ * Parse a possibly-incomplete JSON string, as produced by a streaming source.
10
+ *
11
+ * If the input is valid JSON, it is returned with `complete: true`. Otherwise
12
+ * the parser attempts to repair the fragment by dropping any trailing
13
+ * incomplete token (a dangling key, colon, or comma) and auto-closing unclosed
14
+ * strings, objects, and arrays, returning whatever data has arrived so far with
15
+ * `complete: false`. Unparseable input yields `{ data: undefined, complete: false }`
16
+ * and never throws.
17
+ */
18
+ declare function parsePartialJSON(input: string): PartialJSONResult;
19
+
20
+ //#endregion
21
+ //#region src/repair-markdown.d.ts
22
+ /**
23
+ * Temporarily repair half-finished markdown so a partial streaming buffer
24
+ * renders smoothly. Operates on a copy of the buffer and returns a new string;
25
+ * it never mutates the input. Pure function, no dependencies.
26
+ *
27
+ * The repairs are intentionally conservative: only unambiguous unclosed
28
+ * inline/fence syntax is completed. Ambiguous constructs (e.g. a dangling
29
+ * link text `[docs`) are left untouched to avoid guessing wrong.
30
+ */
31
+ declare function repairMarkdown(buffer: string): string;
32
+
33
+ //#endregion
34
+ //#region src/sanitizer.d.ts
35
+ /**
36
+ * Sanitize an HTML string, stripping scripts and unsafe attributes.
37
+ *
38
+ * When a DOM is available (browser, or Node with jsdom) DOMPurify is used. In a
39
+ * bare Node environment without a global `window`, DOMPurify cannot run, so we
40
+ * fall back to escaping the HTML-significant characters. This never emits raw
41
+ * markup and never throws.
42
+ */
43
+ declare function sanitizeHtml(html: string): string;
44
+
45
+ //#endregion
46
+ //#region src/types.d.ts
47
+ /** Framework-agnostic render node. */
48
+ interface ASTNode {
49
+ key: string;
50
+ type: string;
51
+ tag?: string;
52
+ content?: string;
53
+ html?: string;
54
+ attrs?: Record<string, string>;
55
+ children?: ASTNode[];
56
+ /** card-specific payload */
57
+ card?: {
58
+ type: string;
59
+ data: unknown;
60
+ complete: boolean;
61
+ valid: boolean;
62
+ };
63
+ }
64
+ /** Patch event produced by diffing. */
65
+ type Patch = {
66
+ op: "insert";
67
+ index: number;
68
+ node: ASTNode;
69
+ } | {
70
+ op: "update";
71
+ key: string;
72
+ node: ASTNode;
73
+ } | {
74
+ op: "remove";
75
+ key: string;
76
+ };
77
+ /** Framework-neutral render descriptor returned by plugin node renderers. */
78
+ type RenderOutput = {
79
+ kind: "html";
80
+ html: string;
81
+ } | {
82
+ kind: "element";
83
+ tag: string;
84
+ props?: Record<string, unknown>;
85
+ children?: RenderOutput[];
86
+ } | {
87
+ kind: "card";
88
+ type: string;
89
+ data: unknown;
90
+ } | {
91
+ kind: "mount";
92
+ mount: (el: HTMLElement) => void | (() => void);
93
+ };
94
+ type NodeRenderer = (node: ASTNode) => RenderOutput | Promise<RenderOutput>;
95
+ interface JSONSchema {
96
+ type?: string;
97
+ properties?: Record<string, JSONSchema>;
98
+ items?: JSONSchema;
99
+ required?: string[];
100
+ [k: string]: unknown;
101
+ }
102
+ interface CardDef<TData = unknown, TComponent = unknown> {
103
+ type: string;
104
+ description: string;
105
+ schema?: JSONSchema;
106
+ example?: TData;
107
+ render?: TComponent;
108
+ validate?: (data: TData) => boolean;
109
+ }
110
+ interface AIGuiPlugin {
111
+ name: string;
112
+ extendParser?: (md: MarkdownIt) => void;
113
+ cards?: CardDef[];
114
+ nodeRenderers?: Record<string, NodeRenderer>;
115
+ isBlockComplete?: (nodeType: string, raw: string) => boolean;
116
+ css?: string;
117
+ /** LLM-facing guidance describing this plugin's fence syntax. */
118
+ promptSpec?: string;
119
+ }
120
+ interface RendererOptions {
121
+ registry?: CardRegistry;
122
+ plugins?: AIGuiPlugin[];
123
+ sanitize?: boolean;
124
+ onPatch?: (patches: Patch[], nodes: ASTNode[]) => void;
125
+ }
126
+
127
+ //#endregion
128
+ //#region src/card-registry.d.ts
129
+ interface CardParseResult {
130
+ data: unknown;
131
+ complete: boolean;
132
+ valid: boolean;
133
+ }
134
+ declare class CardRegistry {
135
+ private cards;
136
+ register(def: CardDef): void;
137
+ has(type: string): boolean;
138
+ getRender(type: string): unknown;
139
+ parse(type: string, rawJson: string): CardParseResult;
140
+ private validate;
141
+ toPromptSpec(): string;
142
+ toJSONSchema(): JSONSchema;
143
+ }
144
+
145
+ //#endregion
146
+ //#region src/parser.d.ts
147
+ interface ParserOptions {
148
+ registry?: CardRegistry;
149
+ plugins?: AIGuiPlugin[];
150
+ configureMd?: (md: MarkdownIt) => void;
151
+ }
152
+ /** Build a parser that turns markdown source into a flat list of ASTNodes. */
153
+ declare function createParser(options?: ParserOptions): (src: string) => ASTNode[];
154
+
155
+ //#endregion
156
+ //#region src/plugins.d.ts
157
+ /** Merge every plugin's `nodeRenderers` into a single map (later plugins win). */
158
+ declare function collectNodeRenderers(plugins?: AIGuiPlugin[]): Record<string, NodeRenderer>;
159
+ /** The set of node types claimed by the given plugins. */
160
+ declare function pluginNodeTypes(plugins?: AIGuiPlugin[]): Set<string>;
161
+
162
+ //#endregion
163
+ //#region src/diff.d.ts
164
+ /** Produce a minimal set of patches turning `prev` into `next`, keyed by node key. */
165
+ declare function diffAst(prev: ASTNode[], next: ASTNode[]): Patch[];
166
+
167
+ //#endregion
168
+ //#region src/renderer.d.ts
169
+ /**
170
+ * Streaming render orchestrator: accumulate incoming markdown chunks, repair the
171
+ * partial buffer, parse it into an AST, diff against the previous AST, and emit
172
+ * the resulting patches via `onPatch`.
173
+ */
174
+ declare class Renderer {
175
+ private buffer;
176
+ private prevAst;
177
+ private parse;
178
+ private options;
179
+ private sanitize;
180
+ constructor(options?: RendererOptions);
181
+ push(chunk: string): void;
182
+ feed(source: AsyncIterable<string> | ReadableStream<string>): Promise<void>;
183
+ reset(): void;
184
+ private render;
185
+ }
186
+
187
+ //#endregion
188
+ //#region src/stream-router.d.ts
189
+ /**
190
+ * A consumer of a channel's text stream. `Renderer` satisfies this shape.
191
+ */
192
+ interface ChannelSink {
193
+ push(chunk: string): void;
194
+ }
195
+ /** Handler for structured data values (and text deltas as raw strings). */
196
+ type ChannelHandler = (value: unknown) => void;
197
+ /**
198
+ * Demultiplexes one incoming stream into multiple named channels so different
199
+ * UI regions can update independently (e.g. `progress` + `content` from a single
200
+ * SSE). Framing is auto-detected per line and supports:
201
+ *
202
+ * 1. JSON envelope: `{"ch":"<channel>","delta":"text"}` (a text delta) or
203
+ * `{"ch":"<channel>","data":<any>}` (a structured value). May appear bare or
204
+ * after a `data: ` prefix.
205
+ * 2. SSE `event: <name>` sets the channel for the following `data:` line(s).
206
+ * 3. A plain `data: <text>` line with no `ch` and no preceding `event:` is a
207
+ * text delta on the default `content` channel.
208
+ */
209
+ declare class StreamRouter {
210
+ private readonly sinks;
211
+ private readonly handlers;
212
+ /** Bind a text-stream sink to a channel: text deltas call `sink.push(delta)`. */
213
+ channel(name: string, sink: ChannelSink): this;
214
+ /** Bind a handler to a channel: data values (and deltas as strings) call it. */
215
+ on(name: string, handler: ChannelHandler): this;
216
+ /**
217
+ * Consume a stream to completion, dispatching each parsed line to its channel.
218
+ * Accepts an async iterable of strings, or a `ReadableStream` of either strings
219
+ * or `Uint8Array` bytes (decoded as UTF-8).
220
+ */
221
+ feed(source: AsyncIterable<string> | ReadableStream<Uint8Array | string>): Promise<void>;
222
+ /** Process a single raw line; returns the (possibly updated) current event. */
223
+ private processLine;
224
+ /** Route a text delta: to a bound sink and/or an on() handler (either/both). */
225
+ private routeDelta;
226
+ /** Route a structured value: to the handler, or a string value to a lone sink. */
227
+ private routeData;
228
+ }
229
+
230
+ //#endregion
231
+ //#region src/build-system-prompt.d.ts
232
+ interface BuildSystemPromptOptions {
233
+ base?: string;
234
+ registry?: CardRegistry;
235
+ plugins?: AIGuiPlugin[];
236
+ }
237
+ /**
238
+ * Assembles the LLM system-prompt guidance: an optional base, the registered
239
+ * cards' spec, and every plugin's promptSpec. Consumers prepend this to their
240
+ * own system prompt so the model knows which fenced blocks it may emit.
241
+ */
242
+ declare function buildSystemPrompt(options?: BuildSystemPromptOptions): string;
243
+
244
+ //#endregion
245
+ export { AIGuiPlugin, ASTNode, BuildSystemPromptOptions, CardDef, CardParseResult, CardRegistry, ChannelSink, JSONSchema, NodeRenderer, ParserOptions, PartialJSONResult, Patch, RenderOutput, Renderer, RendererOptions, StreamRouter, buildSystemPrompt, collectNodeRenderers, createParser, diffAst, parsePartialJSON, pluginNodeTypes, repairMarkdown, sanitizeHtml };