@ai-gui/vanilla 0.3.0 → 0.4.1

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
@@ -26,7 +26,7 @@ const __ai_gui_core = __toESM(require("@ai-gui/core"));
26
26
 
27
27
  //#region src/render-output.ts
28
28
  /** Translate a framework-neutral RenderOutput (from a plugin node renderer) into a DOM element. */
29
- function renderOutputToElement(out, sanitize) {
29
+ function renderOutputToElement(out, sanitize, mountContext = {}) {
30
30
  switch (out.kind) {
31
31
  case "html": {
32
32
  const el = document.createElement("div");
@@ -37,7 +37,7 @@ function renderOutputToElement(out, sanitize) {
37
37
  const el = document.createElement(out.tag);
38
38
  for (const [key, value] of Object.entries(out.props ?? {})) if (key === "class" || key === "className") el.className = String(value);
39
39
  else el.setAttribute(key, String(value));
40
- for (const child of out.children ?? []) el.appendChild(renderOutputToElement(child, sanitize));
40
+ for (const child of out.children ?? []) el.appendChild(renderOutputToElement(child, sanitize, mountContext));
41
41
  return el;
42
42
  }
43
43
  case "card": {
@@ -53,7 +53,7 @@ function renderOutputToElement(out, sanitize) {
53
53
  el.setAttribute("data-aigui-mount", "");
54
54
  queueMicrotask(() => {
55
55
  if (el.__aiguiDisposed) return;
56
- const c = out.mount(el);
56
+ const c = out.mount(el, mountContext);
57
57
  if (typeof c === "function") if (el.__aiguiDisposed) c();
58
58
  else el.__aiguiCleanup = c;
59
59
  });
@@ -85,7 +85,7 @@ function renderNodeToElement(node, ctx) {
85
85
  out.then((res) => {
86
86
  if (host.__aiguiDisposed) return;
87
87
  host.removeAttribute("data-aigui-async-pending");
88
- host.replaceChildren(renderOutputToElement(res, ctx.sanitize));
88
+ host.replaceChildren(renderOutputToElement(res, ctx.sanitize, createRenderMountContext(ctx)));
89
89
  }, () => {
90
90
  if (host.__aiguiDisposed) return;
91
91
  host.removeAttribute("data-aigui-async-pending");
@@ -93,7 +93,7 @@ function renderNodeToElement(node, ctx) {
93
93
  });
94
94
  return host;
95
95
  }
96
- return renderOutputToElement(out, ctx.sanitize);
96
+ return renderOutputToElement(out, ctx.sanitize, createRenderMountContext(ctx));
97
97
  } catch {
98
98
  return renderFallback(node, ctx);
99
99
  }
@@ -162,6 +162,53 @@ function renderCardElement(node, ctx) {
162
162
  host.__aiguiCleanup = () => controller.destroy();
163
163
  return host;
164
164
  }
165
+ function createRenderMountContext(ctx) {
166
+ return { mountCard(host, request) {
167
+ return mountCardSlot(host, request, ctx);
168
+ } };
169
+ }
170
+ function mountCardSlot(host, request, ctx) {
171
+ const factory = ctx.registry?.getRender(request.type);
172
+ if (!factory) return void 0;
173
+ return createVanillaCardSlot(host, request.data, factory, () => ({
174
+ state: { status: "idle" },
175
+ onAction: (action) => ctx.onCardAction?.({
176
+ ...action,
177
+ cardType: request.type
178
+ })
179
+ }));
180
+ }
181
+ function createVanillaCardSlot(host, initialData, factory, context) {
182
+ let destroyed = false;
183
+ let instance;
184
+ let element;
185
+ const mount = (data) => {
186
+ const rendered = factory(data, context());
187
+ if (isVanillaCardInstance(rendered)) {
188
+ instance = rendered;
189
+ element = rendered.element;
190
+ } else {
191
+ instance = void 0;
192
+ element = rendered;
193
+ }
194
+ host.replaceChildren(element);
195
+ };
196
+ mount(initialData);
197
+ return {
198
+ element: () => element,
199
+ update(data) {
200
+ if (destroyed) return;
201
+ if (instance) instance.update(data, context());
202
+ else mount(data);
203
+ },
204
+ destroy() {
205
+ if (destroyed) return;
206
+ destroyed = true;
207
+ instance?.destroy?.();
208
+ instance = void 0;
209
+ }
210
+ };
211
+ }
165
212
  function createCardFallback(card, ctx) {
166
213
  const pre = document.createElement("pre");
167
214
  pre.setAttribute("data-aigui-card-fallback", "");
@@ -206,8 +253,7 @@ function createCardController(host, initialCard, factory, ctx) {
206
253
  let card = initialCard;
207
254
  let destroyed = false;
208
255
  let unsubscribe = () => {};
209
- let instance;
210
- let element;
256
+ let slot;
211
257
  let missing = false;
212
258
  const id = card.id;
213
259
  const onAction = (action) => ctx.onCardAction?.({
@@ -229,20 +275,11 @@ function createCardController(host, initialCard, factory, ctx) {
229
275
  } catch (error) {
230
276
  registrationError = error;
231
277
  }
232
- const context = () => ({
233
- state,
234
- onAction
235
- });
236
278
  const mount = (nextData) => {
237
- const rendered = factory(nextData, context());
238
- if (isVanillaCardInstance(rendered)) {
239
- instance = rendered;
240
- element = rendered.element;
241
- } else {
242
- instance = void 0;
243
- element = rendered;
244
- }
245
- host.replaceChildren(element);
279
+ slot = createVanillaCardSlot(host, nextData, factory, () => ({
280
+ state,
281
+ onAction
282
+ }));
246
283
  };
247
284
  const update = (nextData, nextState) => {
248
285
  if (destroyed) return;
@@ -252,9 +289,10 @@ function createCardController(host, initialCard, factory, ctx) {
252
289
  if (missing) {
253
290
  missing = false;
254
291
  host.removeAttribute("data-aigui-card-missing");
292
+ const element = slot?.element();
255
293
  if (element) host.replaceChildren(element);
256
294
  }
257
- if (instance) instance.update(data, context());
295
+ if (slot) slot.update(data);
258
296
  else mount(data);
259
297
  };
260
298
  const showMissing = () => {
@@ -293,8 +331,8 @@ function createCardController(host, initialCard, factory, ctx) {
293
331
  destroyed = true;
294
332
  unsubscribe();
295
333
  unsubscribe = () => {};
296
- instance?.destroy?.();
297
- instance = void 0;
334
+ slot?.destroy();
335
+ slot = void 0;
298
336
  }
299
337
  };
300
338
  }
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { AIGuiPlugin, ASTNode, ActionRuntime, CardAction, CardRegistry, CardStore, DebugEventListener, FeedOptions, FeedSource, NodeRenderer, RenderOutput, RendererOptions } from "@ai-gui/core";
1
+ import { AIGuiPlugin, ASTNode, ActionRuntime, CardAction, CardRegistry, CardStore, DebugEventListener, FeedOptions, FeedSource, NodeRenderer, RenderMountContext, RenderOutput, RendererOptions } from "@ai-gui/core";
2
2
 
3
3
  //#region src/render-node-dom.d.ts
4
4
  interface VanillaCardAction {
@@ -51,7 +51,7 @@ declare function createRenderer(el: HTMLElement, options?: CreateRendererOptions
51
51
  //#endregion
52
52
  //#region src/render-output.d.ts
53
53
  /** Translate a framework-neutral RenderOutput (from a plugin node renderer) into a DOM element. */
54
- declare function renderOutputToElement(out: RenderOutput, sanitize?: RendererOptions["sanitize"]): HTMLElement;
54
+ declare function renderOutputToElement(out: RenderOutput, sanitize?: RendererOptions["sanitize"], mountContext?: RenderMountContext): HTMLElement;
55
55
 
56
56
  //#endregion
57
57
  export { CreateRendererOptions, DomRenderContext, VanillaCardAction, VanillaCardFactory, VanillaCardInstance, VanillaCardUpdateContext, VanillaRenderer, createRenderer, renderNodeToElement, renderOutputToElement };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AIGuiPlugin, ASTNode, ActionRuntime, CardAction, CardRegistry, CardStore, DebugEventListener, FeedOptions, FeedSource, NodeRenderer, RenderOutput, RendererOptions } from "@ai-gui/core";
1
+ import { AIGuiPlugin, ASTNode, ActionRuntime, CardAction, CardRegistry, CardStore, DebugEventListener, FeedOptions, FeedSource, NodeRenderer, RenderMountContext, RenderOutput, RendererOptions } from "@ai-gui/core";
2
2
 
3
3
  //#region src/render-node-dom.d.ts
4
4
  interface VanillaCardAction {
@@ -51,7 +51,7 @@ declare function createRenderer(el: HTMLElement, options?: CreateRendererOptions
51
51
  //#endregion
52
52
  //#region src/render-output.d.ts
53
53
  /** Translate a framework-neutral RenderOutput (from a plugin node renderer) into a DOM element. */
54
- declare function renderOutputToElement(out: RenderOutput, sanitize?: RendererOptions["sanitize"]): HTMLElement;
54
+ declare function renderOutputToElement(out: RenderOutput, sanitize?: RendererOptions["sanitize"], mountContext?: RenderMountContext): HTMLElement;
55
55
 
56
56
  //#endregion
57
57
  export { CreateRendererOptions, DomRenderContext, VanillaCardAction, VanillaCardFactory, VanillaCardInstance, VanillaCardUpdateContext, VanillaRenderer, createRenderer, renderNodeToElement, renderOutputToElement };
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { Renderer, collectNodeRenderers, sanitizeHtml } from "@ai-gui/core";
2
2
 
3
3
  //#region src/render-output.ts
4
4
  /** Translate a framework-neutral RenderOutput (from a plugin node renderer) into a DOM element. */
5
- function renderOutputToElement(out, sanitize) {
5
+ function renderOutputToElement(out, sanitize, mountContext = {}) {
6
6
  switch (out.kind) {
7
7
  case "html": {
8
8
  const el = document.createElement("div");
@@ -13,7 +13,7 @@ function renderOutputToElement(out, sanitize) {
13
13
  const el = document.createElement(out.tag);
14
14
  for (const [key, value] of Object.entries(out.props ?? {})) if (key === "class" || key === "className") el.className = String(value);
15
15
  else el.setAttribute(key, String(value));
16
- for (const child of out.children ?? []) el.appendChild(renderOutputToElement(child, sanitize));
16
+ for (const child of out.children ?? []) el.appendChild(renderOutputToElement(child, sanitize, mountContext));
17
17
  return el;
18
18
  }
19
19
  case "card": {
@@ -29,7 +29,7 @@ function renderOutputToElement(out, sanitize) {
29
29
  el.setAttribute("data-aigui-mount", "");
30
30
  queueMicrotask(() => {
31
31
  if (el.__aiguiDisposed) return;
32
- const c = out.mount(el);
32
+ const c = out.mount(el, mountContext);
33
33
  if (typeof c === "function") if (el.__aiguiDisposed) c();
34
34
  else el.__aiguiCleanup = c;
35
35
  });
@@ -61,7 +61,7 @@ function renderNodeToElement(node, ctx) {
61
61
  out.then((res) => {
62
62
  if (host.__aiguiDisposed) return;
63
63
  host.removeAttribute("data-aigui-async-pending");
64
- host.replaceChildren(renderOutputToElement(res, ctx.sanitize));
64
+ host.replaceChildren(renderOutputToElement(res, ctx.sanitize, createRenderMountContext(ctx)));
65
65
  }, () => {
66
66
  if (host.__aiguiDisposed) return;
67
67
  host.removeAttribute("data-aigui-async-pending");
@@ -69,7 +69,7 @@ function renderNodeToElement(node, ctx) {
69
69
  });
70
70
  return host;
71
71
  }
72
- return renderOutputToElement(out, ctx.sanitize);
72
+ return renderOutputToElement(out, ctx.sanitize, createRenderMountContext(ctx));
73
73
  } catch {
74
74
  return renderFallback(node, ctx);
75
75
  }
@@ -138,6 +138,53 @@ function renderCardElement(node, ctx) {
138
138
  host.__aiguiCleanup = () => controller.destroy();
139
139
  return host;
140
140
  }
141
+ function createRenderMountContext(ctx) {
142
+ return { mountCard(host, request) {
143
+ return mountCardSlot(host, request, ctx);
144
+ } };
145
+ }
146
+ function mountCardSlot(host, request, ctx) {
147
+ const factory = ctx.registry?.getRender(request.type);
148
+ if (!factory) return void 0;
149
+ return createVanillaCardSlot(host, request.data, factory, () => ({
150
+ state: { status: "idle" },
151
+ onAction: (action) => ctx.onCardAction?.({
152
+ ...action,
153
+ cardType: request.type
154
+ })
155
+ }));
156
+ }
157
+ function createVanillaCardSlot(host, initialData, factory, context) {
158
+ let destroyed = false;
159
+ let instance;
160
+ let element;
161
+ const mount = (data) => {
162
+ const rendered = factory(data, context());
163
+ if (isVanillaCardInstance(rendered)) {
164
+ instance = rendered;
165
+ element = rendered.element;
166
+ } else {
167
+ instance = void 0;
168
+ element = rendered;
169
+ }
170
+ host.replaceChildren(element);
171
+ };
172
+ mount(initialData);
173
+ return {
174
+ element: () => element,
175
+ update(data) {
176
+ if (destroyed) return;
177
+ if (instance) instance.update(data, context());
178
+ else mount(data);
179
+ },
180
+ destroy() {
181
+ if (destroyed) return;
182
+ destroyed = true;
183
+ instance?.destroy?.();
184
+ instance = void 0;
185
+ }
186
+ };
187
+ }
141
188
  function createCardFallback(card, ctx) {
142
189
  const pre = document.createElement("pre");
143
190
  pre.setAttribute("data-aigui-card-fallback", "");
@@ -182,8 +229,7 @@ function createCardController(host, initialCard, factory, ctx) {
182
229
  let card = initialCard;
183
230
  let destroyed = false;
184
231
  let unsubscribe = () => {};
185
- let instance;
186
- let element;
232
+ let slot;
187
233
  let missing = false;
188
234
  const id = card.id;
189
235
  const onAction = (action) => ctx.onCardAction?.({
@@ -205,20 +251,11 @@ function createCardController(host, initialCard, factory, ctx) {
205
251
  } catch (error) {
206
252
  registrationError = error;
207
253
  }
208
- const context = () => ({
209
- state,
210
- onAction
211
- });
212
254
  const mount = (nextData) => {
213
- const rendered = factory(nextData, context());
214
- if (isVanillaCardInstance(rendered)) {
215
- instance = rendered;
216
- element = rendered.element;
217
- } else {
218
- instance = void 0;
219
- element = rendered;
220
- }
221
- host.replaceChildren(element);
255
+ slot = createVanillaCardSlot(host, nextData, factory, () => ({
256
+ state,
257
+ onAction
258
+ }));
222
259
  };
223
260
  const update = (nextData, nextState) => {
224
261
  if (destroyed) return;
@@ -228,9 +265,10 @@ function createCardController(host, initialCard, factory, ctx) {
228
265
  if (missing) {
229
266
  missing = false;
230
267
  host.removeAttribute("data-aigui-card-missing");
268
+ const element = slot?.element();
231
269
  if (element) host.replaceChildren(element);
232
270
  }
233
- if (instance) instance.update(data, context());
271
+ if (slot) slot.update(data);
234
272
  else mount(data);
235
273
  };
236
274
  const showMissing = () => {
@@ -269,8 +307,8 @@ function createCardController(host, initialCard, factory, ctx) {
269
307
  destroyed = true;
270
308
  unsubscribe();
271
309
  unsubscribe = () => {};
272
- instance?.destroy?.();
273
- instance = void 0;
310
+ slot?.destroy();
311
+ slot = void 0;
274
312
  }
275
313
  };
276
314
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-gui/vanilla",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "Framework-free DOM adapter for AIGUI — stream LLM output straight into the DOM.",
5
5
  "keywords": [
6
6
  "llm",
@@ -50,7 +50,7 @@
50
50
  "access": "public"
51
51
  },
52
52
  "dependencies": {
53
- "@ai-gui/core": "0.3.0"
53
+ "@ai-gui/core": "0.4.1"
54
54
  },
55
55
  "scripts": {
56
56
  "build": "tsdown",