@ai-gui/vue 0.5.0 → 0.6.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/dist/index.cjs +40 -10
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +41 -11
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -105,7 +105,7 @@ const MountHost = (0, vue.defineComponent)({
|
|
|
105
105
|
/** Translate a framework-neutral RenderOutput into a Vue VNode. */
|
|
106
106
|
function renderOutput(out, sanitize, context = {}) {
|
|
107
107
|
switch (out.kind) {
|
|
108
|
-
case "html": return (0, vue.h)("div", { innerHTML:
|
|
108
|
+
case "html": return (0, vue.h)("div", { innerHTML: (0, __ai_gui_core.sanitizeRenderedHtml)(out.html, sanitize, out.trusted) });
|
|
109
109
|
case "element": return (0, vue.h)(out.tag, out.props, (out.children ?? []).map((child) => renderOutput(child, sanitize, context)));
|
|
110
110
|
case "mount": return (0, vue.h)(MountHost, {
|
|
111
111
|
mount: out.mount,
|
|
@@ -191,10 +191,6 @@ function createRenderMountContext(registry, onCardAction) {
|
|
|
191
191
|
};
|
|
192
192
|
} };
|
|
193
193
|
}
|
|
194
|
-
function sanitizeOutput(html, sanitize) {
|
|
195
|
-
if (sanitize === false) return html;
|
|
196
|
-
return (0, __ai_gui_core.sanitizeHtml)(html, typeof sanitize === "object" ? sanitize : void 0);
|
|
197
|
-
}
|
|
198
194
|
|
|
199
195
|
//#endregion
|
|
200
196
|
//#region src/render-node.ts
|
|
@@ -207,7 +203,7 @@ function renderNode(node, ctx) {
|
|
|
207
203
|
"data-block-type": node.type
|
|
208
204
|
});
|
|
209
205
|
try {
|
|
210
|
-
const out = r(node);
|
|
206
|
+
const out = r(node, { theme: ctx.theme });
|
|
211
207
|
const mountContext = createRenderMountContext(ctx.registry, ctx.onCardAction);
|
|
212
208
|
if (out && typeof out.then === "function") return (0, vue.h)(AsyncOutput, {
|
|
213
209
|
key: node.key,
|
|
@@ -377,8 +373,16 @@ function renderHtml(html, ctx) {
|
|
|
377
373
|
//#region src/ai-renderer.ts
|
|
378
374
|
const AIRenderer = (0, vue.defineComponent)({
|
|
379
375
|
name: "AIRenderer",
|
|
380
|
-
emits: ["card-action"],
|
|
376
|
+
emits: ["card-action", "render"],
|
|
381
377
|
props: {
|
|
378
|
+
text: {
|
|
379
|
+
type: String,
|
|
380
|
+
default: void 0
|
|
381
|
+
},
|
|
382
|
+
theme: {
|
|
383
|
+
type: String,
|
|
384
|
+
default: void 0
|
|
385
|
+
},
|
|
382
386
|
registry: {
|
|
383
387
|
type: Object,
|
|
384
388
|
default: void 0
|
|
@@ -451,18 +455,40 @@ const AIRenderer = (0, vue.defineComponent)({
|
|
|
451
455
|
resetActionScope();
|
|
452
456
|
current.value.destroy();
|
|
453
457
|
});
|
|
458
|
+
const root = (0, vue.shallowRef)();
|
|
459
|
+
(0, vue.watch)(() => current.value.nodes.value, (nodes) => emit("render", nodes), { immediate: true });
|
|
460
|
+
let rendered = "";
|
|
461
|
+
const syncText = () => {
|
|
462
|
+
const text = props.text;
|
|
463
|
+
if (text === void 0 || text === rendered) return;
|
|
464
|
+
if (text.startsWith(rendered)) current.value.push(text.slice(rendered.length));
|
|
465
|
+
else {
|
|
466
|
+
resetActionScope();
|
|
467
|
+
current.value.reset();
|
|
468
|
+
current.value.push(text);
|
|
469
|
+
}
|
|
470
|
+
rendered = text;
|
|
471
|
+
};
|
|
472
|
+
(0, vue.watch)(() => props.text, syncText, { immediate: true });
|
|
473
|
+
(0, vue.watch)(current, () => {
|
|
474
|
+
rendered = "";
|
|
475
|
+
syncText();
|
|
476
|
+
});
|
|
454
477
|
const push = (chunk) => current.value.push(chunk);
|
|
455
478
|
const feed = (source, options) => current.value.feed(source, options);
|
|
456
479
|
const reset = () => {
|
|
457
480
|
resetActionScope();
|
|
481
|
+
rendered = "";
|
|
458
482
|
current.value.reset();
|
|
459
483
|
};
|
|
484
|
+
const exportImages = (options) => root.value ? (0, __ai_gui_core.exportRenderedImages)(root.value, options) : Promise.resolve([]);
|
|
460
485
|
expose({
|
|
461
486
|
debugSource: "renderer",
|
|
462
487
|
subscribeDebug: (listener) => current.value.renderer.subscribeDebug(listener),
|
|
463
488
|
push,
|
|
464
489
|
feed,
|
|
465
|
-
reset
|
|
490
|
+
reset,
|
|
491
|
+
exportImages
|
|
466
492
|
});
|
|
467
493
|
return () => {
|
|
468
494
|
const onCardAction = (action) => {
|
|
@@ -487,9 +513,13 @@ const AIRenderer = (0, vue.defineComponent)({
|
|
|
487
513
|
nodeRenderers: nodeRenderers.value,
|
|
488
514
|
onCardAction,
|
|
489
515
|
sanitize: props.sanitize,
|
|
490
|
-
sanitized: true
|
|
516
|
+
sanitized: true,
|
|
517
|
+
theme: props.theme
|
|
491
518
|
};
|
|
492
|
-
return (0, vue.h)("div", {
|
|
519
|
+
return (0, vue.h)("div", {
|
|
520
|
+
"data-aigui-renderer": "",
|
|
521
|
+
ref: root
|
|
522
|
+
}, current.value.nodes.value.map((n) => renderNode(n, ctx)));
|
|
493
523
|
};
|
|
494
524
|
}
|
|
495
525
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -35,6 +35,8 @@ interface RenderContext {
|
|
|
35
35
|
}) => void;
|
|
36
36
|
sanitize?: RendererOptions["sanitize"];
|
|
37
37
|
sanitized?: boolean;
|
|
38
|
+
/** The host's colour scheme, handed to every plugin that renders a node. */
|
|
39
|
+
theme?: string;
|
|
38
40
|
}
|
|
39
41
|
declare function renderNode(node: ASTNode, ctx: RenderContext): VNode;
|
|
40
42
|
|
package/dist/index.d.ts
CHANGED
|
@@ -35,6 +35,8 @@ interface RenderContext {
|
|
|
35
35
|
}) => void;
|
|
36
36
|
sanitize?: RendererOptions["sanitize"];
|
|
37
37
|
sanitized?: boolean;
|
|
38
|
+
/** The host's colour scheme, handed to every plugin that renders a node. */
|
|
39
|
+
theme?: string;
|
|
38
40
|
}
|
|
39
41
|
declare function renderNode(node: ASTNode, ctx: RenderContext): VNode;
|
|
40
42
|
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineComponent, h, markRaw, onBeforeUnmount, onMounted, onScopeDispose, ref, render, shallowRef, toRaw, watch } from "vue";
|
|
2
|
-
import { Renderer, collectNodeRenderers, sanitizeHtml } from "@ai-gui/core";
|
|
2
|
+
import { Renderer, collectNodeRenderers, exportRenderedImages, sanitizeHtml, sanitizeRenderedHtml } from "@ai-gui/core";
|
|
3
3
|
|
|
4
4
|
//#region src/use-ai-renderer.ts
|
|
5
5
|
function useAIRenderer(options = {}) {
|
|
@@ -81,7 +81,7 @@ const MountHost = defineComponent({
|
|
|
81
81
|
/** Translate a framework-neutral RenderOutput into a Vue VNode. */
|
|
82
82
|
function renderOutput(out, sanitize, context = {}) {
|
|
83
83
|
switch (out.kind) {
|
|
84
|
-
case "html": return h("div", { innerHTML:
|
|
84
|
+
case "html": return h("div", { innerHTML: sanitizeRenderedHtml(out.html, sanitize, out.trusted) });
|
|
85
85
|
case "element": return h(out.tag, out.props, (out.children ?? []).map((child) => renderOutput(child, sanitize, context)));
|
|
86
86
|
case "mount": return h(MountHost, {
|
|
87
87
|
mount: out.mount,
|
|
@@ -167,10 +167,6 @@ function createRenderMountContext(registry, onCardAction) {
|
|
|
167
167
|
};
|
|
168
168
|
} };
|
|
169
169
|
}
|
|
170
|
-
function sanitizeOutput(html, sanitize) {
|
|
171
|
-
if (sanitize === false) return html;
|
|
172
|
-
return sanitizeHtml(html, typeof sanitize === "object" ? sanitize : void 0);
|
|
173
|
-
}
|
|
174
170
|
|
|
175
171
|
//#endregion
|
|
176
172
|
//#region src/render-node.ts
|
|
@@ -183,7 +179,7 @@ function renderNode(node, ctx) {
|
|
|
183
179
|
"data-block-type": node.type
|
|
184
180
|
});
|
|
185
181
|
try {
|
|
186
|
-
const out = r(node);
|
|
182
|
+
const out = r(node, { theme: ctx.theme });
|
|
187
183
|
const mountContext = createRenderMountContext(ctx.registry, ctx.onCardAction);
|
|
188
184
|
if (out && typeof out.then === "function") return h(AsyncOutput, {
|
|
189
185
|
key: node.key,
|
|
@@ -353,8 +349,16 @@ function renderHtml(html, ctx) {
|
|
|
353
349
|
//#region src/ai-renderer.ts
|
|
354
350
|
const AIRenderer = defineComponent({
|
|
355
351
|
name: "AIRenderer",
|
|
356
|
-
emits: ["card-action"],
|
|
352
|
+
emits: ["card-action", "render"],
|
|
357
353
|
props: {
|
|
354
|
+
text: {
|
|
355
|
+
type: String,
|
|
356
|
+
default: void 0
|
|
357
|
+
},
|
|
358
|
+
theme: {
|
|
359
|
+
type: String,
|
|
360
|
+
default: void 0
|
|
361
|
+
},
|
|
358
362
|
registry: {
|
|
359
363
|
type: Object,
|
|
360
364
|
default: void 0
|
|
@@ -427,18 +431,40 @@ const AIRenderer = defineComponent({
|
|
|
427
431
|
resetActionScope();
|
|
428
432
|
current.value.destroy();
|
|
429
433
|
});
|
|
434
|
+
const root = shallowRef();
|
|
435
|
+
watch(() => current.value.nodes.value, (nodes) => emit("render", nodes), { immediate: true });
|
|
436
|
+
let rendered = "";
|
|
437
|
+
const syncText = () => {
|
|
438
|
+
const text = props.text;
|
|
439
|
+
if (text === void 0 || text === rendered) return;
|
|
440
|
+
if (text.startsWith(rendered)) current.value.push(text.slice(rendered.length));
|
|
441
|
+
else {
|
|
442
|
+
resetActionScope();
|
|
443
|
+
current.value.reset();
|
|
444
|
+
current.value.push(text);
|
|
445
|
+
}
|
|
446
|
+
rendered = text;
|
|
447
|
+
};
|
|
448
|
+
watch(() => props.text, syncText, { immediate: true });
|
|
449
|
+
watch(current, () => {
|
|
450
|
+
rendered = "";
|
|
451
|
+
syncText();
|
|
452
|
+
});
|
|
430
453
|
const push = (chunk) => current.value.push(chunk);
|
|
431
454
|
const feed = (source, options) => current.value.feed(source, options);
|
|
432
455
|
const reset = () => {
|
|
433
456
|
resetActionScope();
|
|
457
|
+
rendered = "";
|
|
434
458
|
current.value.reset();
|
|
435
459
|
};
|
|
460
|
+
const exportImages = (options) => root.value ? exportRenderedImages(root.value, options) : Promise.resolve([]);
|
|
436
461
|
expose({
|
|
437
462
|
debugSource: "renderer",
|
|
438
463
|
subscribeDebug: (listener) => current.value.renderer.subscribeDebug(listener),
|
|
439
464
|
push,
|
|
440
465
|
feed,
|
|
441
|
-
reset
|
|
466
|
+
reset,
|
|
467
|
+
exportImages
|
|
442
468
|
});
|
|
443
469
|
return () => {
|
|
444
470
|
const onCardAction = (action) => {
|
|
@@ -463,9 +489,13 @@ const AIRenderer = defineComponent({
|
|
|
463
489
|
nodeRenderers: nodeRenderers.value,
|
|
464
490
|
onCardAction,
|
|
465
491
|
sanitize: props.sanitize,
|
|
466
|
-
sanitized: true
|
|
492
|
+
sanitized: true,
|
|
493
|
+
theme: props.theme
|
|
467
494
|
};
|
|
468
|
-
return h("div", {
|
|
495
|
+
return h("div", {
|
|
496
|
+
"data-aigui-renderer": "",
|
|
497
|
+
ref: root
|
|
498
|
+
}, current.value.nodes.value.map((n) => renderNode(n, ctx)));
|
|
469
499
|
};
|
|
470
500
|
}
|
|
471
501
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-gui/vue",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Vue 3 adapter for AIGUI — stream LLM output into live Vue components with cards and plugins.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"llm",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"access": "public"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@ai-gui/core": "0.
|
|
54
|
+
"@ai-gui/core": "0.6.0"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"vue": ">=3.3"
|