@tylertech/forge-ai 0.15.3-dev.0 → 0.15.3-dev.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.
Files changed (54) hide show
  1. package/custom-elements.json +7733 -6803
  2. package/dist/ai-assistant-response/ai-assistant-response.d.ts +2 -2
  3. package/dist/ai-assistant-response/ai-assistant-response.mjs +4 -3
  4. package/dist/ai-canvas/ai-canvas.d.ts +43 -0
  5. package/dist/ai-canvas/ai-canvas.mjs +95 -0
  6. package/dist/ai-canvas/ai-canvas.scss.mjs +4 -0
  7. package/dist/ai-canvas/index.d.ts +1 -0
  8. package/dist/ai-canvas/index.mjs +5 -0
  9. package/dist/ai-chatbot/ag-ui-adapter.d.ts +2 -2
  10. package/dist/ai-chatbot/agent-adapter.d.ts +5 -5
  11. package/dist/ai-chatbot/agent-runner.d.ts +2 -2
  12. package/dist/ai-chatbot/ai-chatbot-base.d.ts +3 -2
  13. package/dist/ai-chatbot/ai-chatbot-base.mjs +11 -0
  14. package/dist/ai-chatbot/ai-chatbot-tool-call.d.ts +2 -2
  15. package/dist/ai-chatbot/ai-chatbot-tool-call.mjs +88 -10
  16. package/dist/ai-chatbot/ai-chatbot.d.ts +24 -1
  17. package/dist/ai-chatbot/ai-chatbot.mjs +102 -2
  18. package/dist/ai-chatbot/ai-chatbot.scss.mjs +1 -1
  19. package/dist/ai-chatbot/create-tool-renderer.d.ts +5 -5
  20. package/dist/ai-chatbot/index.d.ts +2 -1
  21. package/dist/ai-chatbot/index.mjs +3 -0
  22. package/dist/ai-chatbot/message-state-controller.d.ts +2 -2
  23. package/dist/ai-chatbot/message-state-controller.mjs +11 -11
  24. package/dist/ai-chatbot/tool-canvas.d.ts +60 -0
  25. package/dist/ai-chatbot/tool-canvas.mjs +128 -0
  26. package/dist/ai-chatbot/types.d.ts +128 -16
  27. package/dist/ai-chatbot/utils.d.ts +8 -3
  28. package/dist/ai-chatbot/utils.mjs +4 -0
  29. package/dist/ai-chatbot-launcher/ai-chatbot-launcher.d.ts +40 -2
  30. package/dist/ai-chatbot-launcher/ai-chatbot-launcher.mjs +224 -5
  31. package/dist/ai-chatbot-launcher/ai-chatbot-launcher.scss.mjs +1 -1
  32. package/dist/ai-embedded-chat/ai-embedded-chat.mjs +1 -0
  33. package/dist/ai-message-thread/ai-message-thread.d.ts +2 -2
  34. package/dist/ai-message-thread/ai-message-thread.mjs +3 -2
  35. package/dist/ai-tool-call-indicator/ai-tool-call-indicator.d.ts +2 -2
  36. package/dist/core/canvas-controller.d.ts +81 -0
  37. package/dist/core/canvas-controller.mjs +221 -0
  38. package/dist/core/chatbot-core-controller.d.ts +2 -2
  39. package/dist/core/chatbot-core-controller.mjs +44 -22
  40. package/dist/core/index.d.ts +2 -1
  41. package/dist/core/index.mjs +2 -0
  42. package/dist/core/resize-controller.d.ts +5 -0
  43. package/dist/core/resize-controller.mjs +7 -5
  44. package/dist/index.d.ts +1 -0
  45. package/dist/index.mjs +6 -0
  46. package/dist/tools/ai-data-table/ai-data-table-definition.mjs +1 -0
  47. package/dist/tools/ai-mcp-app/ai-mcp-app.d.ts +2 -1
  48. package/dist/tools/ai-mcp-app/ai-mcp-app.mjs +16 -1
  49. package/dist/tools/ai-mcp-app/ai-mcp-app.scss.mjs +1 -1
  50. package/dist/tools/ai-mcp-app/mcp-app-bridge-controller.d.ts +3 -4
  51. package/dist/tools/ai-mcp-app/mcp-app-bridge-controller.mjs +12 -21
  52. package/dist/tools/ai-mcp-app/mcp-app-bridge-types.d.ts +2 -1
  53. package/dist/tools/ai-steps/ai-steps-definition.mjs +1 -0
  54. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import { LitElement, TemplateResult, CSSResult } from 'lit';
2
- import { AssistantResponse, ToolDefinition } from '../ai-chatbot/types.js';
2
+ import { AssistantResponse, AnyToolDefinition } from '../ai-chatbot/types.js';
3
3
  declare global {
4
4
  interface HTMLElementTagNameMap {
5
5
  'forge-ai-assistant-response': AiAssistantResponseComponent;
@@ -34,7 +34,7 @@ export declare class AiAssistantResponseComponent extends LitElement {
34
34
  #private;
35
35
  static styles: CSSResult;
36
36
  response: AssistantResponse;
37
- tools?: Map<string, ToolDefinition>;
37
+ tools?: Map<string, AnyToolDefinition>;
38
38
  enableReactions: boolean;
39
39
  debugMode: boolean;
40
40
  showThinking: boolean;
@@ -4,6 +4,7 @@ import { unsafeHTML } from "lit/directives/unsafe-html.js";
4
4
  import { ifDefined } from "lit/directives/if-defined.js";
5
5
  import { keyed } from "lit/directives/keyed.js";
6
6
  import { MarkdownStreamController } from "../ai-chatbot/markdown-stream-controller.mjs";
7
+ import { rendersAsMcpAppWidget } from "../ai-chatbot/utils.mjs";
7
8
  import "../ai-response-message-toolbar/ai-response-message-toolbar.mjs";
8
9
  import "../ai-chatbot/ai-chatbot-tool-call.mjs";
9
10
  import "../ai-tool-call-indicator/ai-tool-call-indicator.mjs";
@@ -84,10 +85,10 @@ renderTextChunk_fn = function(child) {
84
85
  return html`<div class="text-chunk">${unsafeHTML(renderedHtml)}</div>`;
85
86
  };
86
87
  isMcpAppToolCall_fn = function(toolCall) {
87
- return !!toolCall.uiResource || this.tools?.get(toolCall.name)?.mcpApp !== void 0;
88
+ return rendersAsMcpAppWidget(toolCall) || this.tools?.get(toolCall.name)?.mcpApp !== void 0;
88
89
  };
89
90
  renderToolCall_fn = function(toolCall) {
90
- if (toolCall.uiResource) {
91
+ if (rendersAsMcpAppWidget(toolCall)) {
91
92
  return keyed(
92
93
  toolCall.id,
93
94
  html`<forge-ai-mcp-app
@@ -199,7 +200,7 @@ thinkingIndicator_get = function() {
199
200
  }
200
201
  const isUnsettled = child.data.status === "parsing" || child.data.status === "executing" || child.data.status === "pending";
201
202
  if (__privateMethod(this, _AiAssistantResponseComponent_instances, isMcpAppToolCall_fn).call(this, child.data)) {
202
- return !!child.data.uiResource && isUnsettled;
203
+ return rendersAsMcpAppWidget(child.data) && isUnsettled;
203
204
  }
204
205
  return isUnsettled;
205
206
  });
@@ -0,0 +1,43 @@
1
+ import { LitElement, TemplateResult, CSSResult } from 'lit';
2
+ import { HeadingLevel } from '../ai-chatbot/types.js';
3
+ declare global {
4
+ interface HTMLElementTagNameMap {
5
+ 'forge-ai-canvas': AiCanvasComponent;
6
+ }
7
+ interface HTMLElementEventMap {
8
+ 'forge-ai-canvas-close': CustomEvent<void>;
9
+ }
10
+ }
11
+ export declare const AiCanvasComponentTagName: keyof HTMLElementTagNameMap;
12
+ /**
13
+ * @tag forge-ai-canvas
14
+ *
15
+ * @summary A side panel that presents an artifact next to a conversation.
16
+ *
17
+ * @description
18
+ * Presentational panel with a titled header, an actions area, a close button, and a scrollable body.
19
+ * Form factors such as `forge-ai-chatbot-launcher` own the open/close state and layout; this component
20
+ * only renders the panel chrome and reports close requests.
21
+ *
22
+ * @slot title - Replaces the default title heading
23
+ * @slot actions - Action buttons rendered before the close button
24
+ * @slot - Panel body content
25
+ *
26
+ * @property {string} titleText - Title shown in the panel header. Also names the panel region for assistive technology, including when the `title` slot replaces the heading
27
+ * @property {HeadingLevel} headingLevel - Heading level applied to the default title (default: 2)
28
+ * @property {string} closeLabel - Accessible label and tooltip for the close button (default: 'Close canvas')
29
+ *
30
+ * @cssproperty --forge-ai-canvas-body-padding - Padding around the body content
31
+ * @cssproperty --forge-ai-canvas-border-inline-start - Border on the panel's leading edge (default: 1px solid outline)
32
+ *
33
+ * @event {CustomEvent<void>} forge-ai-canvas-close - Fired when the close button is clicked
34
+ */
35
+ export declare class AiCanvasComponent extends LitElement {
36
+ #private;
37
+ static styles: CSSResult;
38
+ titleText: string;
39
+ headingLevel: HeadingLevel;
40
+ closeLabel: string;
41
+ focus(): void;
42
+ render(): TemplateResult;
43
+ }
@@ -0,0 +1,95 @@
1
+ import { unsafeCSS, LitElement, html } from "lit";
2
+ import { property, customElement } from "lit/decorators.js";
3
+ import { unsafeStatic, html as html$1 } from "lit/static-html.js";
4
+ import { createRef, ref } from "lit/directives/ref.js";
5
+ import { ifDefined } from "lit/directives/if-defined.js";
6
+ import "../core/tooltip/tooltip.mjs";
7
+ import styles from "./ai-canvas.scss.mjs";
8
+ var __defProp = Object.defineProperty;
9
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
10
+ var __typeError = (msg) => {
11
+ throw TypeError(msg);
12
+ };
13
+ var __decorateClass = (decorators, target, key, kind) => {
14
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
15
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
16
+ if (decorator = decorators[i])
17
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
18
+ if (kind && result) __defProp(target, key, result);
19
+ return result;
20
+ };
21
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
22
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
23
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
24
+ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
25
+ var _closeButtonRef, _AiCanvasComponent_instances, handleCloseClick_fn, heading_get;
26
+ const AiCanvasComponentTagName = "forge-ai-canvas";
27
+ let AiCanvasComponent = class extends LitElement {
28
+ constructor() {
29
+ super(...arguments);
30
+ __privateAdd(this, _AiCanvasComponent_instances);
31
+ this.titleText = "";
32
+ this.headingLevel = 2;
33
+ this.closeLabel = "Close canvas";
34
+ __privateAdd(this, _closeButtonRef, createRef());
35
+ }
36
+ focus() {
37
+ __privateGet(this, _closeButtonRef).value?.focus();
38
+ }
39
+ render() {
40
+ return html`
41
+ <div class="canvas" role="region" aria-label=${ifDefined(this.titleText || void 0)}>
42
+ <div class="header">
43
+ <div class="header-start">
44
+ <slot name="title">${__privateGet(this, _AiCanvasComponent_instances, heading_get)}</slot>
45
+ </div>
46
+ <div class="header-end">
47
+ <slot name="actions"></slot>
48
+ <button
49
+ ${ref(__privateGet(this, _closeButtonRef))}
50
+ id="close-button"
51
+ type="button"
52
+ class="forge-icon-button forge-icon-button--medium close-button"
53
+ aria-label=${this.closeLabel}
54
+ @click=${__privateMethod(this, _AiCanvasComponent_instances, handleCloseClick_fn)}>
55
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true">
56
+ <path
57
+ d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" />
58
+ </svg>
59
+ </button>
60
+ <forge-ai-tooltip for="close-button" placement="bottom">${this.closeLabel}</forge-ai-tooltip>
61
+ </div>
62
+ </div>
63
+ <div class="body">
64
+ <slot></slot>
65
+ </div>
66
+ </div>
67
+ `;
68
+ }
69
+ };
70
+ _closeButtonRef = /* @__PURE__ */ new WeakMap();
71
+ _AiCanvasComponent_instances = /* @__PURE__ */ new WeakSet();
72
+ handleCloseClick_fn = function() {
73
+ this.dispatchEvent(new CustomEvent("forge-ai-canvas-close", { bubbles: true, composed: true }));
74
+ };
75
+ heading_get = function() {
76
+ const tagName = unsafeStatic(`h${this.headingLevel}`);
77
+ return html$1`<${tagName} class="title">${this.titleText}</${tagName}>`;
78
+ };
79
+ AiCanvasComponent.styles = unsafeCSS(styles);
80
+ __decorateClass([
81
+ property({ attribute: "title-text" })
82
+ ], AiCanvasComponent.prototype, "titleText", 2);
83
+ __decorateClass([
84
+ property({ type: Number, attribute: "heading-level" })
85
+ ], AiCanvasComponent.prototype, "headingLevel", 2);
86
+ __decorateClass([
87
+ property({ attribute: "close-label" })
88
+ ], AiCanvasComponent.prototype, "closeLabel", 2);
89
+ AiCanvasComponent = __decorateClass([
90
+ customElement(AiCanvasComponentTagName)
91
+ ], AiCanvasComponent);
92
+ export {
93
+ AiCanvasComponent,
94
+ AiCanvasComponentTagName
95
+ };
@@ -0,0 +1,4 @@
1
+ const styles = '/* prettier-ignore */\n/**\n * @license\n * Copyright Tyler Technologies, Inc.\n * License: Apache-2.0\n */\n.forge-icon-button {\n --_icon-button-display:var(--forge-icon-button-display, inline-flex);\n --_icon-button-size:var(--forge-icon-button-size, 48px);\n --_icon-button-gap:var(--forge-icon-button-gap, 0);\n --_icon-button-icon-color:var(--forge-icon-button-icon-color, currentColor);\n --_icon-button-background-color:var(--forge-icon-button-background-color, none);\n --_icon-button-icon-size:var(--forge-icon-button-icon-size, calc(var(--forge-typography-font-size, 1rem) * 1.5));\n --_icon-button-cursor:var(--forge-icon-button-cursor, pointer);\n --_icon-button-padding:var(--forge-icon-button-padding, var(--forge-spacing-xxsmall, 4px));\n --_icon-button-border:var(--forge-icon-button-border, none);\n --_icon-button-shadow:var(--forge-icon-button-shadow, none);\n --_icon-button-transition-duration:var(--forge-icon-button-transition-duration, var(--forge-animation-duration-short3, 150ms));\n --_icon-button-transition-timing:var(--forge-icon-button-transition-timing, var(--forge-animation-easing-standard, cubic-bezier(0.2, 0, 0, 1)));\n --_icon-button-shape:var(--forge-icon-button-shape, calc(var(--forge-shape-full, 9999px) * var(--forge-shape-factor, 1)));\n --_icon-button-shape-start-start:var(--forge-icon-button-shape-start-start, var(--_icon-button-shape));\n --_icon-button-shape-start-end:var(--forge-icon-button-shape-start-end, var(--_icon-button-shape));\n --_icon-button-shape-end-start:var(--forge-icon-button-shape-end-start, var(--_icon-button-shape));\n --_icon-button-shape-end-end:var(--forge-icon-button-shape-end-end, var(--_icon-button-shape));\n --_icon-button-shape-squared:var(--forge-icon-button-shape-squared, calc(var(--forge-shape-medium, 4px) * var(--forge-shape-factor, 1)));\n --_icon-button-outlined-border-width:var(--forge-icon-button-outlined-border-width, 1px);\n --_icon-button-outlined-border-style:var(--forge-icon-button-outlined-border-style, solid);\n --_icon-button-outlined-border-color:var(--forge-icon-button-outlined-border-color, var(--_icon-button-icon-color));\n --_icon-button-tonal-icon-color:var(--forge-icon-button-tonal-icon-color, var(--forge-theme-on-primary-container, #222c62));\n --_icon-button-tonal-background-color:var(--forge-icon-button-tonal-background-color, var(--forge-theme-primary-container, #d1d5ed));\n --_icon-button-filled-icon-color:var(--forge-icon-button-filled-icon-color, var(--forge-theme-on-primary, #ffffff));\n --_icon-button-filled-background-color:var(--forge-icon-button-filled-background-color, var(--forge-theme-primary, #3f51b5));\n --_icon-button-raised-shadow:var(--forge-icon-button-raised-shadow, 0px 3px 1px -2px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12));\n --_icon-button-raised-hover-shadow:var(--forge-icon-button-raised-hover-shadow, 0px 2px 4px -1px rgba(0, 0, 0, 0.2), 0px 4px 5px 0px rgba(0, 0, 0, 0.14), 0px 1px 10px 0px rgba(0, 0, 0, 0.12));\n --_icon-button-raised-active-shadow:var(--forge-icon-button-raised-active-shadow, 0px 5px 5px -3px rgba(0, 0, 0, 0.2), 0px 8px 10px 1px rgba(0, 0, 0, 0.14), 0px 3px 14px 2px rgba(0, 0, 0, 0.12));\n --_icon-button-raised-disabled-shadow:var(--forge-icon-button-raised-disabled-shadow, none);\n --_icon-button-density-small-size:var(--forge-icon-button-density-small-size, 24px);\n --_icon-button-density-small-padding:var(--forge-icon-button-density-small-padding, var(--forge-spacing-xxxsmall, 2px));\n --_icon-button-density-small-icon-size:var(--forge-icon-button-density-small-icon-size, calc(var(--forge-typography-font-size, 1rem) * 1.125));\n --_icon-button-density-medium-size:var(--forge-icon-button-density-medium-size, 36px);\n --_icon-button-density-medium-padding:var(--forge-icon-button-density-medium-padding, var(--forge-spacing-xxsmall, 4px));\n --_icon-button-density-large-size:var(--forge-icon-button-density-large-size, var(--_icon-button-size));\n --_icon-button-toggle-on-background-color:var(--forge-icon-button-toggle-on-background-color, var(--forge-theme-primary-container, #d1d5ed));\n --_icon-button-toggle-on-icon-color:var(--forge-icon-button-toggle-on-icon-color, var(--forge-theme-primary, #3f51b5));\n --_icon-button-outlined-toggle-on-background-color:var(--forge-icon-button-outlined-toggle-on-background-color, var(--forge-theme-primary-container, #d1d5ed));\n --_icon-button-outlined-toggle-on-icon-color:var(--forge-icon-button-outlined-toggle-on-icon-color, var(--forge-theme-primary, #3f51b5));\n --_icon-button-tonal-toggle-background-color:var(--forge-icon-button-tonal-toggle-background-color, var(--forge-theme-surface-container-low, #ebebeb));\n --_icon-button-tonal-toggle-on-background-color:var(--forge-icon-button-tonal-toggle-on-background-color, var(--forge-theme-primary-container, #d1d5ed));\n --_icon-button-tonal-toggle-on-icon-color:var(--forge-icon-button-tonal-toggle-on-icon-color, var(--forge-theme-primary, #3f51b5));\n --_icon-button-filled-toggle-background-color:var(--forge-icon-button-filled-toggle-background-color, var(--forge-theme-surface-container-low, #ebebeb));\n --_icon-button-filled-toggle-icon-color:var(--forge-icon-button-filled-toggle-icon-color, var(--forge-theme-primary, #3f51b5));\n --_icon-button-filled-toggle-on-background-color:var(--forge-icon-button-filled-toggle-on-background-color, var(--forge-theme-primary, #3f51b5));\n --_icon-button-filled-toggle-on-icon-color:var(--forge-icon-button-filled-toggle-on-icon-color, var(--forge-theme-on-primary, #ffffff));\n --_icon-button-disabled-cursor:var(--forge-icon-button-disabled-cursor, not-allowed);\n --_icon-button-disabled-opacity:var(--forge-icon-button-disabled-opacity, 0.38);\n --_icon-button-popover-icon-padding:var(--forge-icon-button-popover-icon-padding, var(--forge-spacing-xsmall, 8px));\n --_icon-button-focus-indicator-color:var(--forge-icon-button-focus-indicator-color, var(--forge-theme-primary, #3f51b5));\n}\n\n.forge-icon-button {\n display: var(--_icon-button-display);\n position: relative;\n outline: none;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n position: relative;\n z-index: 0;\n display: var(--_icon-button-display);\n align-items: center;\n justify-content: center;\n gap: var(--_icon-button-gap);\n box-sizing: border-box;\n height: var(--_icon-button-density-large-size);\n min-width: var(--_icon-button-density-large-size);\n border: var(--_icon-button-border);\n border-start-start-radius: var(--_icon-button-shape-start-start);\n border-start-end-radius: var(--_icon-button-shape-start-end);\n border-end-start-radius: var(--_icon-button-shape-end-start);\n border-end-end-radius: var(--_icon-button-shape-end-end);\n padding: var(--_icon-button-padding);\n box-shadow: var(--_icon-button-shadow);\n color: var(--_icon-button-icon-color);\n background: var(--_icon-button-background-color);\n font-size: var(--_icon-button-icon-size);\n cursor: var(--_icon-button-cursor);\n user-select: none;\n transition-property: box-shadow, background;\n transition-duration: var(--_icon-button-transition-duration);\n transition-timing-function: var(--_icon-button-transition-timing);\n}\n\n.forge-icon-button img, .forge-icon-button svg {\n height: var(--_icon-button-icon-size);\n width: var(--_icon-button-icon-size);\n}\n\n.forge-icon-button svg {\n fill: currentColor;\n}\n\n.forge-icon-button:not(:disabled) {\n --_state-layer-display:var(--forge-state-layer-display, flex);\n --_state-layer-color:var(--forge-state-layer-color, var(--forge-theme-on-surface, #000000));\n --_state-layer-hover-color:var(--forge-state-layer-hover-color, var(--_state-layer-color));\n --_state-layer-hover-opacity:var(--forge-state-layer-hover-opacity, 0.08);\n --_state-layer-pressed-color:var(--forge-state-layer-pressed-color, var(--_state-layer-color));\n --_state-layer-pressed-opacity:var(--forge-state-layer-pressed-opacity, 0.12);\n --_state-layer-hover-duration:var(--forge-state-layer-hover-duration, 15ms);\n --_state-layer-pressed-duration:var(--forge-state-layer-pressed-duration, 105ms);\n --_state-layer-animation-duration:var(--forge-state-layer-animation-duration, 375ms);\n}\n\n.forge-icon-button:not(:disabled)::before {\n opacity: 0;\n position: absolute;\n backface-visibility: hidden;\n transform: translateZ(0);\n background-color: var(--_state-layer-hover-color);\n inset: 0;\n transition: opacity var(--_state-layer-hover-duration) linear, background-color var(--_state-layer-hover-duration) linear;\n --_state-layer-hover-duration:var(--forge-state-layer-hover-duration, 100ms);\n content: "";\n opacity: 0;\n border-radius: inherit;\n}\n\n.forge-icon-button:not(:disabled):hover::before {\n background-color: var(--_state-layer-hover-color);\n opacity: var(--_state-layer-hover-opacity);\n}\n\n.forge-icon-button:not(:disabled):active::before {\n opacity: var(--_state-layer-pressed-opacity);\n transition-duration: var(--_state-layer-pressed-duration);\n --_state-layer-pressed-opacity:var(--forge-state-layer-pressed-opacity, 0.18);\n}\n\n.forge-icon-button:not(:disabled) {\n --forge-state-layer-color: var(--_icon-button-icon-color);\n}\n\n@keyframes forge-focus-indicator-outward-grow {\n from {\n outline-width: 0;\n }\n to {\n outline-width: var(--_focus-indicator-active-width);\n }\n}\n@keyframes forge-focus-indicator-outward-shrink {\n from {\n outline-width: var(--_focus-indicator-active-width);\n }\n}\n@keyframes forge-focus-indicator-inward-grow {\n from {\n border-width: 0;\n }\n to {\n border-width: var(--_focus-indicator-active-width);\n }\n}\n@keyframes forge-focus-indicator-inward-shrink {\n from {\n border-width: var(--_focus-indicator-active-width);\n }\n}\n.forge-icon-button:not(:disabled) {\n outline: none;\n}\n\n.forge-icon-button:not(:disabled):focus-visible::after {\n --_focus-indicator-display:var(--forge-focus-indicator-display, flex);\n --_focus-indicator-width:var(--forge-focus-indicator-width, var(--forge-border-medium, 2px));\n --_focus-indicator-active-width:var(--forge-focus-indicator-active-width, 6px);\n --_focus-indicator-color:var(--forge-focus-indicator-color, var(--forge-theme-primary, #3f51b5));\n --_focus-indicator-shape:var(--forge-focus-indicator-shape, calc(var(--forge-shape-extra-small, 1px) * var(--forge-shape-factor, 1)));\n --_focus-indicator-duration:var(--forge-focus-indicator-duration, var(--forge-animation-duration-long4, 600ms));\n --_focus-indicator-easing:var(--forge-focus-indicator-easing, var(--forge-animation-easing-emphasized, cubic-bezier(0.2, 0, 0, 1)));\n --_focus-indicator-shape-start-start:var(--forge-focus-indicator-shape-start-start, var(--_focus-indicator-shape));\n --_focus-indicator-shape-start-end:var(--forge-focus-indicator-shape-start-end, var(--_focus-indicator-shape));\n --_focus-indicator-shape-end-end:var(--forge-focus-indicator-shape-end-end, var(--_focus-indicator-shape));\n --_focus-indicator-shape-end-start:var(--forge-focus-indicator-shape-end-start, var(--_focus-indicator-shape));\n --_focus-indicator-outward-offset:var(--forge-focus-indicator-outward-offset, var(--forge-spacing-xxsmall, 4px));\n --_focus-indicator-inward-offset:var(--forge-focus-indicator-inward-offset, 0px);\n --_focus-indicator-offset-block:var(--forge-focus-indicator-offset-block, 0);\n --_focus-indicator-offset-inline:var(--forge-focus-indicator-offset-inline, 0);\n}\n\n.forge-icon-button:not(:disabled):focus-visible::after {\n animation-delay: 0s, calc(var(--_focus-indicator-duration) * 0.25);\n animation-duration: calc(var(--_focus-indicator-duration) * 0.25), calc(var(--_focus-indicator-duration) * 0.75);\n animation-timing-function: var(--_focus-indicator-easing);\n box-sizing: border-box;\n color: var(--_focus-indicator-color);\n display: none;\n pointer-events: none;\n position: absolute;\n margin-block: var(--_focus-indicator-offset-block);\n margin-inline: var(--_focus-indicator-offset-inline);\n animation-name: forge-focus-indicator-outward-grow, forge-focus-indicator-outward-shrink;\n border-end-end-radius: calc(var(--_focus-indicator-shape-end-end) + var(--_focus-indicator-outward-offset));\n border-end-start-radius: calc(var(--_focus-indicator-shape-end-start) + var(--_focus-indicator-outward-offset));\n border-start-end-radius: calc(var(--_focus-indicator-shape-start-end) + var(--_focus-indicator-outward-offset));\n border-start-start-radius: calc(var(--_focus-indicator-shape-start-start) + var(--_focus-indicator-outward-offset));\n inset: calc(-1 * var(--_focus-indicator-outward-offset));\n outline: var(--_focus-indicator-width) solid currentColor;\n content: "";\n display: block;\n}\n\n.forge-icon-button:not(:disabled) {\n --forge-focus-indicator-color: var(--_icon-button-focus-indicator-color);\n --forge-focus-indicator-shape: var(--_icon-button-shape);\n}\n\n.forge-icon-button:not(:disabled):where(.forge-icon-button--text, :not(:where(.forge-icon-button--outlined, .forge-icon-button--tonal, .forge-icon-button--filled, .forge-icon-button--raised))) {\n --forge-focus-indicator-outward-offset: 0px;\n}\n\n.forge-icon-button--outlined {\n border-width: var(--_icon-button-outlined-border-width);\n border-style: var(--_icon-button-outlined-border-style);\n border-color: var(--_icon-button-outlined-border-color);\n}\n\n.forge-icon-button--tonal {\n --_icon-button-icon-color:var(--_icon-button-tonal-icon-color);\n --_icon-button-background-color:var(--_icon-button-tonal-background-color);\n}\n\n.forge-icon-button--filled, .forge-icon-button--raised {\n --_icon-button-icon-color:var(--_icon-button-filled-icon-color);\n --_icon-button-background-color:var(--_icon-button-filled-background-color);\n}\n\n.forge-icon-button--raised {\n --_icon-button-shadow:var(--_icon-button-raised-shadow);\n}\n\n.forge-icon-button--raised:hover {\n --_icon-button-raised-shadow:var(--_icon-button-raised-hover-shadow);\n}\n\n.forge-icon-button--raised:active {\n --_icon-button-raised-shadow:var(--_icon-button-raised-active-shadow);\n}\n\n.forge-icon-button--squared {\n --_icon-button-shape:var(--_icon-button-shape-squared);\n}\n\n.forge-icon-button--small {\n --_icon-button-size:var(--_icon-button-density-small-size);\n --_icon-button-icon-size:var(--_icon-button-density-small-icon-size);\n --_icon-button-padding:var(--_icon-button-density-small-padding);\n}\n\n.forge-icon-button--small > * {\n font-size: var(--_icon-button-density-small-icon-size);\n}\n\n.forge-icon-button--medium {\n --_icon-button-size:var(--_icon-button-density-medium-size);\n --_icon-button-padding:var(--_icon-button-density-medium-padding);\n}\n\n.forge-icon-button:disabled {\n pointer-events: none;\n opacity: var(--_icon-button-disabled-opacity);\n pointer-events: auto;\n cursor: not-allowed;\n}\n\n.forge-icon-button forge-circular-progress {\n --forge-circular-progress-indicator-color: var(--_icon-button-icon-color);\n --forge-circular-progress-track-color: transparent;\n --forge-circular-progress-size: 1em;\n}\n\n:host {\n display: block;\n height: 100%;\n min-inline-size: 0;\n box-sizing: border-box;\n}\n\n.canvas {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-inline-size: 0;\n box-sizing: border-box;\n background-color: var(--forge-theme-surface, #ffffff);\n border-inline-start: var(--forge-ai-canvas-border-inline-start, 1px solid var(--forge-theme-outline, #e0e0e0));\n}\n\n.header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--forge-spacing-small, 12px);\n flex-shrink: 0;\n min-height: 56px;\n padding-inline: var(--forge-spacing-medium, 16px);\n box-sizing: border-box;\n border-block-end: 1px solid var(--forge-theme-outline, #e0e0e0);\n}\n\n.header-start {\n display: flex;\n align-items: center;\n flex: 1;\n min-inline-size: 0;\n}\n\n.title {\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-family: var(--forge-typography-heading1-font-family, var(--forge-typography-font-family, "Roboto", sans-serif));\n font-size: var(--forge-typography-heading1-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-heading1-font-size-scale, 0.875)));\n font-weight: var(--forge-typography-heading1-font-weight, 500);\n line-height: var(--forge-typography-heading1-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-heading1-line-height-scale, 1.125)));\n letter-spacing: var(--forge-typography-heading1-letter-spacing, 0.0178571429em);\n text-transform: var(--forge-typography-heading1-text-transform, inherit);\n text-decoration: var(--forge-typography-heading1-text-decoration, inherit);\n margin: 0;\n color: var(--forge-theme-text-high, rgba(0, 0, 0, 0.87));\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.header-end {\n display: flex;\n align-items: center;\n gap: var(--forge-spacing-xxsmall, 4px);\n flex-shrink: 0;\n}\n\n.close-button,\n.close-button > svg {\n fill: var(--forge-theme-text-medium, rgba(0, 0, 0, 0.6));\n}\n\n.body::-webkit-scrollbar {\n height: var(--forge-scrollbar-height, 16px);\n width: var(--forge-scrollbar-width, 16px);\n}\n.body::-webkit-scrollbar-track {\n background-color: var(--forge-scrollbar-track-container, var(--forge-theme-surface-container-low, #ebebeb));\n}\n.body::-webkit-scrollbar-track:hover {\n background-color: var(--forge-scrollbar-track-container-hover, var(--forge-theme-surface-container-low, #ebebeb));\n}\n.body::-webkit-scrollbar-corner {\n background-color: var(--forge-scrollbar-track-container, var(--forge-theme-surface-container-low, #ebebeb));\n}\n.body::-webkit-scrollbar-thumb {\n height: var(--forge-scrollbar-thumb-min-height, 32px);\n width: var(--forge-scrollbar-thumb-min-width, 32px);\n border-radius: var(--forge-scrollbar-border-radius, calc(var(--forge-shape-full, 9999px) * var(--forge-shape-factor, 1)));\n border-width: var(--forge-scrollbar-border-width, 3px);\n border-style: solid;\n border-color: transparent;\n background-color: var(--forge-scrollbar-thumb-container, var(--forge-theme-surface-container-medium, #c2c2c2));\n background-clip: content-box;\n}\n.body::-webkit-scrollbar-thumb:hover {\n background-color: var(--forge-scrollbar-thumb-container-hover, var(--forge-theme-surface-container-high, #9e9e9e));\n}\n.body {\n flex: 1;\n min-height: 0;\n overflow: auto;\n padding: var(--forge-ai-canvas-body-padding, var(--forge-spacing-medium, 16px));\n box-sizing: border-box;\n}';
2
+ export {
3
+ styles as default
4
+ };
@@ -0,0 +1 @@
1
+ export * from './ai-canvas';
@@ -0,0 +1,5 @@
1
+ import { AiCanvasComponent, AiCanvasComponentTagName } from "./ai-canvas.mjs";
2
+ export {
3
+ AiCanvasComponent,
4
+ AiCanvasComponentTagName
5
+ };
@@ -1,10 +1,10 @@
1
1
  import { AgentAdapter } from './agent-adapter.js';
2
- import { ChatMessage, ToolDefinition } from './types.js';
2
+ import { ChatMessage, AnyToolDefinition } from './types.js';
3
3
  export interface AgUiAdapterConfig {
4
4
  url: string;
5
5
  context?: Record<string, unknown>;
6
6
  headers?: Record<string, string>;
7
- tools?: ToolDefinition[];
7
+ tools?: AnyToolDefinition[];
8
8
  }
9
9
  export declare class AgUiAdapter extends AgentAdapter {
10
10
  #private;
@@ -1,4 +1,4 @@
1
- import { ChatMessage, FileUploadCallbacks, McpAppUiResource, McpToolResult, ToolDefinition } from './types.js';
1
+ import { ChatMessage, FileUploadCallbacks, McpAppUiResource, McpToolResult, AnyToolDefinition } from './types.js';
2
2
  import { EventEmitter, Subscription } from './event-emitter.js';
3
3
  export interface MessageStartEvent {
4
4
  messageId: string;
@@ -167,7 +167,7 @@ export interface ActivityDeltaAgentEvent {
167
167
  }
168
168
  export declare abstract class AgentAdapter {
169
169
  protected _state: AdapterState;
170
- protected _tools: ToolDefinition[];
170
+ protected _tools: AnyToolDefinition[];
171
171
  protected _events: {
172
172
  runStarted: EventEmitter<void>;
173
173
  runFinished: EventEmitter<void>;
@@ -235,14 +235,14 @@ export declare abstract class AgentAdapter {
235
235
  disposeMcpAppView?(params: {
236
236
  toolCallId: string;
237
237
  }): void;
238
- setTools(tools: ToolDefinition[]): void;
239
- getTools(): ToolDefinition[];
238
+ setTools(tools: AnyToolDefinition[]): void;
239
+ getTools(): AnyToolDefinition[];
240
240
  /**
241
241
  * Tools to forward to the backend agent, excluding MCP-app tools (`ToolDefinition.mcpApp`).
242
242
  * MCP-app tools are a host-internal mechanism for streaming a UI resource into an existing
243
243
  * tool call — the model never calls them directly, so they must never reach the agent.
244
244
  */
245
- getModelFacingTools(): ToolDefinition[];
245
+ getModelFacingTools(): AnyToolDefinition[];
246
246
  getState(): AdapterState;
247
247
  get isConnected(): boolean;
248
248
  get isConnecting(): boolean;
@@ -1,12 +1,12 @@
1
1
  import { AgentAdapter } from './agent-adapter.js';
2
- import { ChatMessage, ToolCall, ToolDefinition } from './types.js';
2
+ import { ChatMessage, ToolCall, AnyToolDefinition } from './types.js';
3
3
  export interface AgentRunnerConfig {
4
4
  /** The adapter instance that handles communication with the AI model */
5
5
  adapter: AgentAdapter;
6
6
  /** The user prompt to send to the agent */
7
7
  prompt: string;
8
8
  /** Optional tools the agent can call during execution */
9
- tools?: ToolDefinition[];
9
+ tools?: AnyToolDefinition[];
10
10
  /** Execution mode: 'one-shot' stops after first response, 'multi-turn' allows tool usage loops. @default 'one-shot' */
11
11
  mode?: 'one-shot' | 'multi-turn';
12
12
  /** Maximum conversation turns in multi-turn mode to prevent infinite loops. @default 20 */
@@ -10,7 +10,7 @@ import { ForgeAiSuggestionsEventData, Suggestion } from '../ai-suggestions';
10
10
  import { ForgeAiVoiceInputResultEvent } from '../ai-voice-input';
11
11
  import { AgentAdapter } from './agent-adapter.js';
12
12
  import { ChatbotCoreController } from '../core/chatbot-core-controller.js';
13
- import { Agent, ChatMessage, ClientMessageInput, ContextItem, HeadingLevel, MessageItem, SlashCommand, ThreadState, ToolDefinition } from './types.js';
13
+ import { Agent, ChatMessage, ClientMessageInput, ContextItem, HeadingLevel, MessageItem, SlashCommand, ThreadState, ToolCall, AnyToolDefinition } from './types.js';
14
14
  export type FeatureToggle = 'on' | 'off';
15
15
  export declare abstract class AiChatbotBase extends LitElement {
16
16
  #private;
@@ -56,8 +56,9 @@ export declare abstract class AiChatbotBase extends LitElement {
56
56
  protected _coreController: ChatbotCoreController;
57
57
  protected get _isStreaming(): boolean;
58
58
  protected get _isUploading(): boolean;
59
- protected get _tools(): Map<string, ToolDefinition>;
59
+ protected get _tools(): Map<string, AnyToolDefinition>;
60
60
  protected get _messageItems(): MessageItem[];
61
+ protected get _toolCalls(): ToolCall[];
61
62
  protected get _hasMessages(): boolean;
62
63
  protected get _allContextItems(): ContextItem[];
63
64
  connectedCallback(): void;
@@ -140,6 +140,17 @@ class AiChatbotBase extends LitElement {
140
140
  get _messageItems() {
141
141
  return this._coreController?.messageItems ?? [];
142
142
  }
143
+ get _toolCalls() {
144
+ return this._messageItems.flatMap((item) => {
145
+ if (item.type === "toolCall") {
146
+ return [item.data];
147
+ }
148
+ if (item.type === "assistant") {
149
+ return item.data.children.flatMap((child) => child.type === "toolCall" ? [child.data] : []);
150
+ }
151
+ return [];
152
+ });
153
+ }
143
154
  get _hasMessages() {
144
155
  return this._coreController?.hasMessages ?? false;
145
156
  }
@@ -1,5 +1,5 @@
1
1
  import { LitElement, TemplateResult, nothing, PropertyValues, CSSResult } from 'lit';
2
- import { ToolCall, ToolDefinition } from './types.js';
2
+ import { ToolCall, AnyToolDefinition } from './types.js';
3
3
  declare global {
4
4
  interface HTMLElementTagNameMap {
5
5
  'forge-ai-chatbot-tool-call': AiChatbotToolCallComponent;
@@ -13,7 +13,7 @@ export declare class AiChatbotToolCallComponent extends LitElement {
13
13
  #private;
14
14
  static styles: CSSResult;
15
15
  toolCall: ToolCall;
16
- toolDefinition?: ToolDefinition;
16
+ toolDefinition?: AnyToolDefinition;
17
17
  updated(changedProperties: PropertyValues<this>): void;
18
18
  render(): TemplateResult | typeof nothing;
19
19
  }
@@ -1,7 +1,10 @@
1
1
  import { unsafeCSS, LitElement, nothing, html } from "lit";
2
2
  import { property, customElement } from "lit/decorators.js";
3
3
  import { createRef, ref } from "lit/directives/ref.js";
4
+ import { ContextConsumer } from "@lit/context";
5
+ import { toolCanvasHostContext, createToolCanvas, requestCanvasOpen, requestCanvasClose, resolveToolCanvasInline, resolveToolCanvasIcon, resolveToolCanvasTitle, resolveToolCanvasSubtitle, resolveToolCanvasActionLabel } from "./tool-canvas.mjs";
4
6
  import { shouldShowToolRenderer } from "./utils.mjs";
7
+ import "../ai-artifact-card/ai-artifact-card.mjs";
5
8
  import styles from "./ai-chatbot-tool-call.scss.mjs";
6
9
  var __defProp = Object.defineProperty;
7
10
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -21,7 +24,7 @@ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read fr
21
24
  var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
22
25
  var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value);
23
26
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
24
- var _customRendererRef, _renderedElement, _AiChatbotToolCallComponent_instances, shouldRenderCustom_get, dispatchScrollRequest_fn, customRenderer_get, replaceRenderedElement_fn;
27
+ var _customRendererRef, _renderedElement, _renderedCanvas, _canvasHostConsumer, _AiChatbotToolCallComponent_instances, shouldRenderCustom_get, presentation_get, isOpenInCanvas_get, _canvasControls, canvas_get, showsArtifactCard_get, artifactCard_fn, dispatchScrollRequest_fn, customRenderer_get, replaceRenderedElement_fn;
25
28
  const AiChatbotToolCallComponentTagName = "forge-ai-chatbot-tool-call";
26
29
  let AiChatbotToolCallComponent = class extends LitElement {
27
30
  constructor() {
@@ -29,27 +32,40 @@ let AiChatbotToolCallComponent = class extends LitElement {
29
32
  __privateAdd(this, _AiChatbotToolCallComponent_instances);
30
33
  __privateAdd(this, _customRendererRef, createRef());
31
34
  __privateAdd(this, _renderedElement);
35
+ __privateAdd(this, _renderedCanvas);
36
+ __privateAdd(this, _canvasHostConsumer, new ContextConsumer(this, {
37
+ context: toolCanvasHostContext,
38
+ subscribe: true,
39
+ callback: () => this.requestUpdate()
40
+ }));
41
+ __privateAdd(this, _canvasControls);
32
42
  }
33
43
  updated(changedProperties) {
34
44
  super.updated(changedProperties);
35
- if (!changedProperties.has("toolCall") && !changedProperties.has("toolDefinition")) {
36
- return;
37
- }
38
45
  const container = __privateGet(this, _customRendererRef).value;
39
46
  if (!container || !__privateGet(this, _AiChatbotToolCallComponent_instances, shouldRenderCustom_get)) {
40
47
  return;
41
48
  }
42
49
  const renderer = this.toolDefinition?.renderer;
50
+ const canvas = __privateGet(this, _AiChatbotToolCallComponent_instances, canvas_get);
51
+ const mounted = container.childNodes.length > 0;
52
+ const changed = changedProperties.has("toolCall") || changedProperties.has("toolDefinition");
43
53
  if (renderer?.elementTag) {
44
- if (__privateGet(this, _renderedElement) instanceof HTMLElement && __privateGet(this, _renderedElement).tagName.toLowerCase() === renderer.elementTag) {
45
- __privateGet(this, _renderedElement).toolCall = this.toolCall;
54
+ if (mounted && __privateGet(this, _renderedElement) instanceof HTMLElement && __privateGet(this, _renderedElement).tagName.toLowerCase() === renderer.elementTag) {
55
+ const existing = __privateGet(this, _renderedElement);
56
+ existing.toolCall = this.toolCall;
57
+ existing.presentation = __privateGet(this, _AiChatbotToolCallComponent_instances, presentation_get);
58
+ existing.canvas = canvas;
46
59
  return;
47
60
  }
48
61
  const element = document.createElement(renderer.elementTag);
49
62
  element.toolCall = this.toolCall;
63
+ element.presentation = __privateGet(this, _AiChatbotToolCallComponent_instances, presentation_get);
64
+ element.canvas = canvas;
50
65
  __privateMethod(this, _AiChatbotToolCallComponent_instances, replaceRenderedElement_fn).call(this, container, element);
51
- } else if (renderer?.render) {
52
- __privateMethod(this, _AiChatbotToolCallComponent_instances, replaceRenderedElement_fn).call(this, container, renderer.render(this.toolCall));
66
+ } else if (renderer?.render && (changed || !mounted || canvas !== __privateGet(this, _renderedCanvas))) {
67
+ __privateSet(this, _renderedCanvas, canvas);
68
+ __privateMethod(this, _AiChatbotToolCallComponent_instances, replaceRenderedElement_fn).call(this, container, renderer.render(this.toolCall, { presentation: __privateGet(this, _AiChatbotToolCallComponent_instances, presentation_get), canvas }));
53
69
  }
54
70
  }
55
71
  render() {
@@ -58,10 +74,68 @@ let AiChatbotToolCallComponent = class extends LitElement {
58
74
  };
59
75
  _customRendererRef = /* @__PURE__ */ new WeakMap();
60
76
  _renderedElement = /* @__PURE__ */ new WeakMap();
77
+ _renderedCanvas = /* @__PURE__ */ new WeakMap();
78
+ _canvasHostConsumer = /* @__PURE__ */ new WeakMap();
61
79
  _AiChatbotToolCallComponent_instances = /* @__PURE__ */ new WeakSet();
62
80
  shouldRenderCustom_get = function() {
63
81
  return shouldShowToolRenderer(this.toolCall, this.toolDefinition);
64
82
  };
83
+ presentation_get = function() {
84
+ if (!this.toolDefinition?.canvas) {
85
+ return "inline";
86
+ }
87
+ return __privateGet(this, _canvasHostConsumer).value?.presentation ?? "inline";
88
+ };
89
+ isOpenInCanvas_get = function() {
90
+ return __privateGet(this, _canvasHostConsumer).value?.openToolCallId === this.toolCall.id;
91
+ };
92
+ _canvasControls = /* @__PURE__ */ new WeakMap();
93
+ canvas_get = function() {
94
+ const available = __privateGet(this, _AiChatbotToolCallComponent_instances, presentation_get) === "inline-expandable";
95
+ const isOpen = __privateGet(this, _AiChatbotToolCallComponent_instances, isOpenInCanvas_get);
96
+ if (__privateGet(this, _canvasControls)?.available !== available || __privateGet(this, _canvasControls).isOpen !== isOpen) {
97
+ __privateSet(this, _canvasControls, createToolCanvas({
98
+ available,
99
+ isOpen: () => __privateGet(this, _AiChatbotToolCallComponent_instances, isOpenInCanvas_get),
100
+ open: (options) => requestCanvasOpen(this, {
101
+ toolCallId: this.toolCall.id,
102
+ toolName: this.toolCall.name,
103
+ title: options?.title
104
+ }),
105
+ close: () => requestCanvasClose(this, { toolCallId: this.toolCall.id })
106
+ }));
107
+ }
108
+ return __privateGet(this, _canvasControls);
109
+ };
110
+ showsArtifactCard_get = function() {
111
+ if (!this.toolDefinition || __privateGet(this, _AiChatbotToolCallComponent_instances, presentation_get) !== "inline-expandable") {
112
+ return false;
113
+ }
114
+ switch (resolveToolCanvasInline(this.toolDefinition)) {
115
+ case "card":
116
+ return true;
117
+ case "card-while-open":
118
+ return __privateGet(this, _AiChatbotToolCallComponent_instances, isOpenInCanvas_get);
119
+ default:
120
+ return false;
121
+ }
122
+ };
123
+ artifactCard_fn = function(toolDefinition) {
124
+ const active = __privateGet(this, _AiChatbotToolCallComponent_instances, isOpenInCanvas_get);
125
+ const icon = resolveToolCanvasIcon(this.toolCall, toolDefinition);
126
+ icon?.setAttribute("slot", "icon");
127
+ return html`
128
+ <forge-ai-artifact-card
129
+ title-text=${resolveToolCanvasTitle(this.toolCall, toolDefinition)}
130
+ .subtitleText=${resolveToolCanvasSubtitle(this.toolCall, toolDefinition) ?? ""}
131
+ action-label=${resolveToolCanvasActionLabel(this.toolCall, toolDefinition, active)}
132
+ asset-id=${this.toolCall.id}
133
+ ?active=${active}
134
+ @forge-ai-artifact-card-open=${() => __privateGet(this, _AiChatbotToolCallComponent_instances, canvas_get).toggle()}>
135
+ ${icon ?? nothing}
136
+ </forge-ai-artifact-card>
137
+ `;
138
+ };
65
139
  dispatchScrollRequest_fn = function() {
66
140
  this.dispatchEvent(
67
141
  new CustomEvent("forge-ai-message-thread-scroll-request", {
@@ -71,10 +145,14 @@ dispatchScrollRequest_fn = function() {
71
145
  );
72
146
  };
73
147
  customRenderer_get = function() {
74
- const renderer = this.toolDefinition?.renderer;
75
- if (!renderer || !__privateGet(this, _AiChatbotToolCallComponent_instances, shouldRenderCustom_get)) {
148
+ const toolDefinition = this.toolDefinition;
149
+ const renderer = toolDefinition?.renderer;
150
+ if (!toolDefinition || !renderer || !__privateGet(this, _AiChatbotToolCallComponent_instances, shouldRenderCustom_get)) {
76
151
  return nothing;
77
152
  }
153
+ if (__privateGet(this, _AiChatbotToolCallComponent_instances, showsArtifactCard_get)) {
154
+ return __privateMethod(this, _AiChatbotToolCallComponent_instances, artifactCard_fn).call(this, toolDefinition);
155
+ }
78
156
  if (renderer.elementTag || renderer.render) {
79
157
  return html`<div ${ref(__privateGet(this, _customRendererRef))}></div>`;
80
158
  }
@@ -1,7 +1,8 @@
1
- import { TemplateResult, CSSResult } from 'lit';
1
+ import { PropertyValues, TemplateResult, CSSResult } from 'lit';
2
2
  import { AiMessageThreadComponent } from '../ai-message-thread';
3
3
  import { AiPromptComponent } from '../ai-prompt';
4
4
  import { AiChatbotBase } from './ai-chatbot-base.js';
5
+ import { CanvasOpenEventData, OpenCanvasOptions } from '../core/canvas-controller.js';
5
6
  import { Agent, ChatMessage, ForgeAiChatbotThreadSelectEventData, ForgeAiChatbotThreadSearchEventData, ForgeAiChatbotThreadLoadMoreEventData, ForgeAiChatbotThreadRenameEventData, ForgeAiChatbotThreadDeleteEventData, ForgeAiChatbotContextRemoveEventData, Thread } from './types.js';
6
7
  import { Ref } from 'lit/directives/ref.js';
7
8
  declare global {
@@ -35,6 +36,8 @@ declare global {
35
36
  'forge-ai-chatbot-thread-delete': CustomEvent<ForgeAiChatbotThreadDeleteEventData>;
36
37
  'forge-ai-chatbot-context-remove': CustomEvent<ForgeAiChatbotContextRemoveEventData>;
37
38
  'forge-ai-chatbot-thread-retry': CustomEvent<void>;
39
+ 'forge-ai-chatbot-canvas-open': CustomEvent<CanvasOpenEventData>;
40
+ 'forge-ai-chatbot-canvas-close': CustomEvent<void>;
38
41
  }
39
42
  }
40
43
  export interface ForgeAiChatbotMessageEventData {
@@ -102,12 +105,15 @@ export declare const AiChatbotComponentTagName: keyof HTMLElementTagNameMap;
102
105
  * @property {FeatureToggle} exportOption - Controls the export-conversation header action. `'on'` (default) shows it when messages exist; `'off'` hides it entirely.
103
106
  * @property {string | null} selectedThreadId - The id of the currently selected thread. Set this to highlight a thread in the conversations panel (e.g. when restoring a conversation loaded from the backend). Updated internally when a thread is selected or a new chat starts.
104
107
  * @property {boolean} threadsLoading - When true, shows a loading indicator in the conversations panel's recent chats list while threads are loading (default: false)
108
+ * @property {string} canvasTitle - Default title for the artifact canvas modal, used when the open request does not supply one
105
109
  * @property {string | undefined} threadsError - Message describing a failed thread load. When set, the conversations panel shows the message with a retry button instead of its empty state or loading indicator. The conversations button also shows an error badge so the failure is visible with the panel closed. If threads are already loaded the list stays visible and the message renders as a compact single line with a retry - at the bottom of the list when a page was in flight, otherwise above it. Clear it once a load succeeds.
106
110
  *
107
111
  * @cssproperty --forge-ai-chatbot-icon-color - The fill color for the AI icon. Defaults to `tertiary`.
108
112
  * @cssproperty --forge-ai-chatbot-suggestion-background - The background color for suggestion buttons. Defaults to `tertiary-container`.
109
113
  * @cssproperty --forge-ai-chatbot-suggestion-foreground - The text color for suggestion buttons. Defaults to `on-tertiary-container`.
110
114
  * @cssproperty --forge-ai-chatbot-header-title-color - The color of the header title text. Defaults to `currentColor`.
115
+ * @cssproperty --forge-ai-chatbot-canvas-width - Width of the artifact canvas modal (default: 80vw).
116
+ * @cssproperty --forge-ai-chatbot-canvas-height - Height of the artifact canvas modal (default: 80vh).
111
117
  *
112
118
  * @event {CustomEvent<void>} forge-ai-chatbot-connected - Fired when adapter connects
113
119
  * @event {CustomEvent<void>} forge-ai-chatbot-disconnected - Fired when adapter disconnects
@@ -132,6 +138,8 @@ export declare const AiChatbotComponentTagName: keyof HTMLElementTagNameMap;
132
138
  * @event {CustomEvent<ForgeAiChatbotThreadRenameEventData>} forge-ai-chatbot-thread-rename - Fired when user renames a thread. Cancelable - if prevented, call onSuccess() to commit or onError() to revert.
133
139
  * @event {CustomEvent<ForgeAiChatbotThreadDeleteEventData>} forge-ai-chatbot-thread-delete - Fired when user deletes a thread. Cancelable - if prevented, call onSuccess() to commit deletion or onError() to revert. Otherwise optimistically removed.
134
140
  * @event {CustomEvent<void>} forge-ai-chatbot-thread-retry - Fired when the retry button in the conversations panel's error state is clicked. Re-request the threads and clear threadsError once the load succeeds
141
+ * @event {CustomEvent<CanvasOpenEventData>} forge-ai-chatbot-canvas-open - Fired before the canvas modal opens or replaces its content. Cancelable - prevents the canvas from opening
142
+ * @event {CustomEvent<void>} forge-ai-chatbot-canvas-close - Fired when the canvas modal closes. Cancelable only for user or `closeCanvas()` initiated closes; closes caused by clearing the thread cannot be prevented
135
143
  */
136
144
  export declare class AiChatbotComponent extends AiChatbotBase {
137
145
  #private;
@@ -148,11 +156,26 @@ export declare class AiChatbotComponent extends AiChatbotBase {
148
156
  selectedThreadId: string | null;
149
157
  threadsLoading: boolean;
150
158
  threadsError?: string;
159
+ canvasTitle?: string;
151
160
  protected _messageThreadRef: Ref<AiMessageThreadComponent>;
152
161
  protected _promptRef: Ref<AiPromptComponent>;
153
162
  protected _handleInfo(): void;
154
163
  sendMessage(content: string, files?: File[]): Promise<void>;
155
164
  focus(): void;
165
+ willUpdate(changedProperties: PropertyValues<this>): void;
166
+ updated(changedProperties: PropertyValues<this>): void;
167
+ get canvasOpen(): boolean;
168
+ /**
169
+ * Opens the artifact canvas in a modal over the chatbot, or swaps its content if already open.
170
+ * Returns false when the open event is prevented.
171
+ */
172
+ openCanvas(options?: OpenCanvasOptions): boolean;
173
+ /**
174
+ * Closes the artifact canvas. Returns false if it was not open or the close event was prevented.
175
+ */
176
+ closeCanvas(): boolean;
177
+ clearMessages(): boolean;
178
+ setMessages(messages: ChatMessage[]): void;
156
179
  showConversations(): void;
157
180
  hideConversations(): void;
158
181
  /**