@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 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: sanitizeOutput(out.html, sanitize) });
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", { "data-aigui-renderer": "" }, current.value.nodes.value.map((n) => renderNode(n, ctx)));
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: sanitizeOutput(out.html, sanitize) });
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", { "data-aigui-renderer": "" }, current.value.nodes.value.map((n) => renderNode(n, ctx)));
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.5.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.5.0"
54
+ "@ai-gui/core": "0.6.0"
55
55
  },
56
56
  "peerDependencies": {
57
57
  "vue": ">=3.3"