@tylertech/forge-ai 0.15.1 → 0.15.3-dev.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.
Files changed (71) hide show
  1. package/custom-elements.json +8152 -6309
  2. package/dist/ai-artifact-card/ai-artifact-card.d.ts +75 -0
  3. package/dist/ai-artifact-card/ai-artifact-card.mjs +142 -0
  4. package/dist/ai-artifact-card/ai-artifact-card.scss.mjs +283 -0
  5. package/dist/ai-artifact-card/index.d.ts +1 -0
  6. package/dist/ai-artifact-card/index.mjs +5 -0
  7. package/dist/ai-artifact-card-group/ai-artifact-card-group.d.ts +57 -0
  8. package/dist/ai-artifact-card-group/ai-artifact-card-group.mjs +137 -0
  9. package/dist/ai-artifact-card-group/ai-artifact-card-group.scss.mjs +173 -0
  10. package/dist/ai-artifact-card-group/index.d.ts +1 -0
  11. package/dist/ai-artifact-card-group/index.mjs +5 -0
  12. package/dist/ai-assistant-response/ai-assistant-response.mjs +25 -2
  13. package/dist/ai-chatbot/ag-ui-adapter.mjs +1 -1
  14. package/dist/ai-chatbot/agent-adapter.d.ts +108 -1
  15. package/dist/ai-chatbot/agent-adapter.mjs +22 -0
  16. package/dist/ai-chatbot/ai-chatbot-base.d.ts +26 -0
  17. package/dist/ai-chatbot/ai-chatbot-base.mjs +136 -3
  18. package/dist/ai-chatbot/index.d.ts +2 -2
  19. package/dist/ai-chatbot/message-state-controller.d.ts +10 -1
  20. package/dist/ai-chatbot/message-state-controller.mjs +78 -20
  21. package/dist/ai-chatbot/types.d.ts +199 -1
  22. package/dist/ai-chatbot/utils.mjs +3 -1
  23. package/dist/ai-chatbot-launcher/ai-chatbot-launcher.mjs +14 -4
  24. package/dist/ai-message-thread/ai-message-thread.d.ts +1 -0
  25. package/dist/ai-message-thread/ai-message-thread.mjs +35 -3
  26. package/dist/ai-sidebar/ai-sidebar.d.ts +17 -6
  27. package/dist/ai-sidebar/ai-sidebar.mjs +78 -36
  28. package/dist/ai-sidebar/ai-sidebar.scss.mjs +1 -1
  29. package/dist/ai-sidebar/index.d.ts +1 -0
  30. package/dist/ai-sidebar-chat/ai-sidebar-chat.d.ts +14 -1
  31. package/dist/ai-sidebar-chat/ai-sidebar-chat.mjs +18 -14
  32. package/dist/ai-steps/ai-steps.d.ts +67 -0
  33. package/dist/ai-steps/ai-steps.mjs +163 -0
  34. package/dist/ai-steps/ai-steps.scss.mjs +4 -0
  35. package/dist/ai-steps/index.d.ts +1 -0
  36. package/dist/ai-steps/index.mjs +5 -0
  37. package/dist/ai-tool-call-indicator/ai-tool-call-indicator.mjs +1 -1
  38. package/dist/core/chatbot-core-controller.d.ts +2 -0
  39. package/dist/core/chatbot-core-controller.mjs +99 -4
  40. package/dist/core/resize-controller.d.ts +1 -0
  41. package/dist/core/resize-controller.mjs +11 -2
  42. package/dist/index.d.ts +3 -0
  43. package/dist/index.mjs +9 -0
  44. package/dist/tools/ai-mcp-app/ai-mcp-app.d.ts +69 -0
  45. package/dist/tools/ai-mcp-app/ai-mcp-app.mjs +574 -0
  46. package/dist/tools/ai-mcp-app/ai-mcp-app.scss.mjs +4 -0
  47. package/dist/tools/ai-mcp-app/index.d.ts +3 -0
  48. package/dist/tools/ai-mcp-app/index.mjs +6 -0
  49. package/dist/tools/ai-mcp-app/mcp-app-bridge-controller.d.ts +61 -0
  50. package/dist/tools/ai-mcp-app/mcp-app-bridge-controller.mjs +156 -0
  51. package/dist/tools/ai-mcp-app/mcp-app-bridge-types.d.ts +123 -0
  52. package/dist/tools/ai-mcp-app/mcp-app-bridge.d.ts +41 -0
  53. package/dist/tools/ai-mcp-app/mcp-app-bridge.mjs +293 -0
  54. package/dist/tools/ai-mcp-app/mcp-app-context.d.ts +46 -0
  55. package/dist/tools/ai-mcp-app/mcp-app-context.mjs +5 -0
  56. package/dist/tools/ai-steps/ai-steps-definition.d.ts +2 -0
  57. package/dist/tools/ai-steps/ai-steps-definition.mjs +50 -0
  58. package/dist/tools/ai-steps/ai-steps.d.ts +28 -0
  59. package/dist/tools/ai-steps/ai-steps.mjs +49 -0
  60. package/dist/tools/ai-steps/ai-steps.scss.mjs +4 -0
  61. package/dist/tools/ai-steps/index.d.ts +2 -0
  62. package/dist/tools/ai-steps/index.mjs +6 -0
  63. package/dist/tools/index.d.ts +1 -0
  64. package/dist/tools/index.mjs +5 -1
  65. package/dist/utils/index.d.ts +1 -0
  66. package/dist/utils/index.mjs +5 -1
  67. package/dist/utils/inert-outside.d.ts +7 -0
  68. package/dist/utils/inert-outside.mjs +28 -0
  69. package/dist/utils/storage-utils.d.ts +26 -0
  70. package/dist/utils/storage-utils.mjs +32 -0
  71. package/package.json +4 -1
@@ -0,0 +1,67 @@
1
+ import { LitElement, PropertyValues, nothing, TemplateResult, CSSResult } from 'lit';
2
+ declare global {
3
+ interface HTMLElementTagNameMap {
4
+ 'forge-ai-steps': AiStepsComponent;
5
+ }
6
+ }
7
+ export declare const AiStepsComponentTagName: keyof HTMLElementTagNameMap;
8
+ /** Lifecycle state of an individual step. */
9
+ export type AiStepStatus = 'pending' | 'running' | 'complete';
10
+ /**
11
+ * A single step in the timeline. Every field is supplied by the agent — the component derives no
12
+ * labels of its own and makes no assumption about how the agent names its work.
13
+ */
14
+ export interface AiStep {
15
+ /**
16
+ * Agent-provided label for the step, e.g. `'Filtered by \`crime_category\`'`. Rendered as inline
17
+ * markdown, so the agent decides which part of its own label to emphasize. Literal `*`, `_` and
18
+ * `` ` `` characters need escaping.
19
+ */
20
+ label: string;
21
+ /**
22
+ * Optional agent-provided elaboration, rendered as a detail card beneath the label. Also rendered
23
+ * as inline markdown.
24
+ */
25
+ detail?: string;
26
+ /** Lifecycle state of this step. Defaults to `'complete'` when omitted. */
27
+ status?: AiStepStatus;
28
+ }
29
+ /**
30
+ * @tag forge-ai-steps
31
+ *
32
+ * @summary Expandable timeline summarizing the steps an agent took while producing a response.
33
+ *
34
+ * @description
35
+ * Collapsed, it shows a step count and a summary of the first couple of step labels. When expanded,
36
+ * it lists each step as a row in a vertical timeline along with its status. Steps carrying a
37
+ * `detail` render as detail cards; steps without one render as a simple labeled row.
38
+ *
39
+ * Labels and details are rendered as sanitized inline markdown, so the agent controls its own
40
+ * emphasis — bold, italic, strikethrough, `code` and links — rather than the component or the
41
+ * consumer prescribing which part of a step matters.
42
+ *
43
+ * This is a presentational primitive: it renders whatever `steps` it is given and knows nothing
44
+ * about tool calls or tool definitions. `forge-ai-tool-steps` is the tool renderer that maps an
45
+ * agent's `displaySteps` tool call onto these properties.
46
+ *
47
+ * While `running` is `true` the timeline stays expanded and the summary row is hidden. When
48
+ * `running` flips back to `false` the timeline auto-collapses, leaving the summary row as the way
49
+ * back in. An element that mounts with `running` already `false` renders expanded, so static usage
50
+ * (a finished run, or a Storybook story) is readable without interaction.
51
+ *
52
+ * @property {AiStep[]} steps - The steps to render as timeline rows
53
+ * @property {boolean} running - Whether the run producing these steps is still in progress
54
+ */
55
+ export declare class AiStepsComponent extends LitElement {
56
+ #private;
57
+ static styles: CSSResult;
58
+ steps: AiStep[];
59
+ /**
60
+ * Whether the run producing these steps is still in progress. While `true` the timeline is held
61
+ * open and the summary row is hidden; the transition back to `false` collapses the timeline.
62
+ */
63
+ running: boolean;
64
+ private _expanded;
65
+ willUpdate(changedProperties: PropertyValues<this>): void;
66
+ render(): TemplateResult | typeof nothing;
67
+ }
@@ -0,0 +1,163 @@
1
+ import { unsafeCSS, LitElement, html, nothing } from "lit";
2
+ import { property, state, customElement } from "lit/decorators.js";
3
+ import { join } from "lit/directives/join.js";
4
+ import { unsafeHTML } from "lit/directives/unsafe-html.js";
5
+ import { when } from "lit/directives/when.js";
6
+ import { renderInlineMarkdown } from "../utils/markdown.mjs";
7
+ import styles from "./ai-steps.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 _chevronIcon, _rowMarker, _AiStepsComponent_instances, renderInline_fn, summaryLabel_get, toggle_fn, renderNoDetailStep_fn, renderStep_fn, isExpanded_get, steps_get, stepsSummary_get, stepsCountLabel_get;
26
+ const AiStepsComponentTagName = "forge-ai-steps";
27
+ const MAX_DETAIL_LENGTH = 2e3;
28
+ let AiStepsComponent = class extends LitElement {
29
+ constructor() {
30
+ super(...arguments);
31
+ __privateAdd(this, _AiStepsComponent_instances);
32
+ this.steps = [];
33
+ this.running = false;
34
+ this._expanded = true;
35
+ __privateAdd(this, _chevronIcon, html`
36
+ <svg
37
+ class="chevron"
38
+ xmlns="http://www.w3.org/2000/svg"
39
+ viewBox="0 0 24 24"
40
+ height="20"
41
+ width="20"
42
+ fill="currentColor"
43
+ aria-hidden="true">
44
+ <path d="M7.41 8.59 12 13.17l4.59-4.58L18 10l-6 6-6-6z" />
45
+ </svg>
46
+ `);
47
+ __privateAdd(this, _rowMarker, html`<span class="row-marker"></span>`);
48
+ }
49
+ willUpdate(changedProperties) {
50
+ if (changedProperties.has("running") && !this.running && changedProperties.get("running") === true) {
51
+ this._expanded = false;
52
+ }
53
+ }
54
+ render() {
55
+ if (this.steps.length === 0) {
56
+ return nothing;
57
+ }
58
+ return html`
59
+ <div class="steps">
60
+ <div class="steps-count">${__privateGet(this, _AiStepsComponent_instances, stepsCountLabel_get)}</div>
61
+ ${__privateGet(this, _AiStepsComponent_instances, stepsSummary_get)} ${__privateGet(this, _AiStepsComponent_instances, steps_get)}
62
+ </div>
63
+ `;
64
+ }
65
+ };
66
+ _chevronIcon = /* @__PURE__ */ new WeakMap();
67
+ _rowMarker = /* @__PURE__ */ new WeakMap();
68
+ _AiStepsComponent_instances = /* @__PURE__ */ new WeakSet();
69
+ renderInline_fn = function(value) {
70
+ return html`${unsafeHTML(renderInlineMarkdown(value))}`;
71
+ };
72
+ summaryLabel_get = function() {
73
+ const stepLabels = this.steps.map((step) => html`<span>${__privateMethod(this, _AiStepsComponent_instances, renderInline_fn).call(this, step.label)}</span>`);
74
+ const visibleLabels = stepLabels.slice(0, 2);
75
+ const remaining = stepLabels.length - visibleLabels.length;
76
+ return remaining > 0 ? html`${join(visibleLabels, html`, `)} +${remaining} more` : html`${join(visibleLabels, html`, `)}`;
77
+ };
78
+ toggle_fn = function() {
79
+ this._expanded = !this._expanded;
80
+ };
81
+ renderNoDetailStep_fn = function(step) {
82
+ return html`
83
+ <div class="timeline-row" data-status=${step.status ?? "complete"}>
84
+ <div class="row-header">
85
+ ${__privateGet(this, _rowMarker)}
86
+ <span class="row-label">
87
+ <span class="row-name">${__privateMethod(this, _AiStepsComponent_instances, renderInline_fn).call(this, step.label)}</span>
88
+ </span>
89
+ </div>
90
+ </div>
91
+ `;
92
+ };
93
+ renderStep_fn = function(step) {
94
+ const detail = step.detail;
95
+ if (!detail) {
96
+ return __privateMethod(this, _AiStepsComponent_instances, renderNoDetailStep_fn).call(this, step);
97
+ }
98
+ const truncated = detail.length > MAX_DETAIL_LENGTH ? `${detail.slice(0, MAX_DETAIL_LENGTH)}… (truncated)` : detail;
99
+ return html`
100
+ <div class="timeline-row" data-status=${step.status ?? "complete"}>
101
+ <div class="row-header">
102
+ ${__privateGet(this, _rowMarker)}
103
+ <div class="step-card">
104
+ <span class="step-card-title">${__privateMethod(this, _AiStepsComponent_instances, renderInline_fn).call(this, step.label)}</span>
105
+ <div class="step-card-body">
106
+ <div class="step-card-result">${__privateMethod(this, _AiStepsComponent_instances, renderInline_fn).call(this, truncated)}</div>
107
+ </div>
108
+ </div>
109
+ </div>
110
+ </div>
111
+ `;
112
+ };
113
+ isExpanded_get = function() {
114
+ return this.running || this._expanded;
115
+ };
116
+ steps_get = function() {
117
+ return html`
118
+ <div
119
+ id="timeline-content"
120
+ class="timeline ${__privateGet(this, _AiStepsComponent_instances, isExpanded_get) ? "expanded" : ""}"
121
+ aria-hidden=${__privateGet(this, _AiStepsComponent_instances, isExpanded_get) ? "false" : "true"}>
122
+ <div class="timeline-content">${this.steps.map((step) => __privateMethod(this, _AiStepsComponent_instances, renderStep_fn).call(this, step))}</div>
123
+ </div>
124
+ `;
125
+ };
126
+ stepsSummary_get = function() {
127
+ return when(
128
+ !this.running,
129
+ () => html`
130
+ <button
131
+ class="summary"
132
+ type="button"
133
+ aria-expanded=${this._expanded}
134
+ aria-controls="timeline-content"
135
+ @click=${__privateMethod(this, _AiStepsComponent_instances, toggle_fn)}>
136
+ ${__privateGet(this, _chevronIcon)}
137
+ <span class="status-text">${__privateGet(this, _AiStepsComponent_instances, summaryLabel_get)}</span>
138
+ <span class="focus-indicator"></span>
139
+ </button>
140
+ `
141
+ );
142
+ };
143
+ stepsCountLabel_get = function() {
144
+ const count = this.steps.length;
145
+ return `${count} step${count === 1 ? "" : "s"}`;
146
+ };
147
+ AiStepsComponent.styles = unsafeCSS(styles);
148
+ __decorateClass([
149
+ property({ attribute: false })
150
+ ], AiStepsComponent.prototype, "steps", 2);
151
+ __decorateClass([
152
+ property({ type: Boolean, reflect: true })
153
+ ], AiStepsComponent.prototype, "running", 2);
154
+ __decorateClass([
155
+ state()
156
+ ], AiStepsComponent.prototype, "_expanded", 2);
157
+ AiStepsComponent = __decorateClass([
158
+ customElement(AiStepsComponentTagName)
159
+ ], AiStepsComponent);
160
+ export {
161
+ AiStepsComponent,
162
+ AiStepsComponentTagName
163
+ };
@@ -0,0 +1,4 @@
1
+ const styles = '/* prettier-ignore */\n@keyframes fadein {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\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:host {\n display: flex;\n flex-direction: column;\n gap: var(--forge-spacing-small, 12px);\n}\n\n.summary {\n display: flex;\n align-items: center;\n gap: var(--forge-spacing-xsmall, 8px);\n padding-block: var(--forge-spacing-xxsmall, 4px);\n padding-inline: var(--forge-spacing-xsmall, 8px);\n border: none;\n background-color: var(--forge-theme-surface-container-minimum, #f5f5f5);\n border-radius: calc(var(--forge-shape-large, 8px) * var(--forge-shape-factor, 1));\n cursor: pointer;\n text-align: start;\n position: relative;\n outline: none;\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.summary::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.summary:hover::before {\n background-color: var(--_state-layer-hover-color);\n opacity: var(--_state-layer-hover-opacity);\n}\n.summary: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.status-text {\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-family: var(--forge-typography-body1-font-family, var(--forge-typography-font-family, "Roboto", sans-serif));\n font-size: var(--forge-typography-body1-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-font-size-scale, 0.875)));\n font-weight: var(--forge-typography-body1-font-weight, 400);\n line-height: var(--forge-typography-body1-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-line-height-scale, 1.125)));\n letter-spacing: var(--forge-typography-body1-letter-spacing, 0.0357142857em);\n text-transform: var(--forge-typography-body1-text-transform, inherit);\n text-decoration: var(--forge-typography-body1-text-decoration, inherit);\n}\n\n.chevron {\n flex-shrink: 0;\n transform: rotate(-90deg);\n transition: transform var(--forge-animation-duration-short4, 200ms) var(--forge-animation-easing-standard, cubic-bezier(0.2, 0, 0, 1));\n}\n\n.summary[aria-expanded=true] .chevron {\n transform: rotate(0deg);\n}\n\n.focus-indicator {\n position: absolute;\n display: none;\n}\n\n:host .summary:focus .focus-indicator {\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 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-inward-grow, forge-focus-indicator-inward-shrink;\n border-end-end-radius: calc(var(--_focus-indicator-shape-end-end) - var(--_focus-indicator-inward-offset));\n border-end-start-radius: calc(var(--_focus-indicator-shape-end-start) - var(--_focus-indicator-inward-offset));\n border-start-end-radius: calc(var(--_focus-indicator-shape-start-end) - var(--_focus-indicator-inward-offset));\n border-start-start-radius: calc(var(--_focus-indicator-shape-start-start) - var(--_focus-indicator-inward-offset));\n border: var(--_focus-indicator-width) solid currentColor;\n inset: var(--_focus-indicator-inward-offset);\n}\n:host .summary:focus .focus-indicator::before {\n content: "";\n}\n:host .summary:focus .focus-indicator {\n display: block;\n --forge-focus-indicator-shape: calc(var(--forge-shape-large, 8px) * var(--forge-shape-factor, 1));\n}\n\n.steps {\n display: flex;\n flex-direction: column;\n gap: var(--forge-spacing-xsmall, 8px);\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-family: var(--forge-typography-body1-font-family, var(--forge-typography-font-family, "Roboto", sans-serif));\n font-size: var(--forge-typography-body1-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-font-size-scale, 0.875)));\n font-weight: var(--forge-typography-body1-font-weight, 400);\n line-height: var(--forge-typography-body1-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-line-height-scale, 1.125)));\n letter-spacing: var(--forge-typography-body1-letter-spacing, 0.0357142857em);\n text-transform: var(--forge-typography-body1-text-transform, inherit);\n text-decoration: var(--forge-typography-body1-text-decoration, inherit);\n font-size: var(--forge-ai-markdown-font-size, 0.875rem);\n line-height: var(--forge-ai-markdown-line-height, 1.5rem);\n color: var(--forge-theme-on-surface, #000000);\n overflow-wrap: break-word;\n}\n.steps h1,\n.steps h2,\n.steps h3,\n.steps h4,\n.steps h5,\n.steps h6 {\n margin: 1.75rem 0 var(--forge-spacing-small, 12px);\n}\n.steps strong {\n font-weight: 500;\n}\n.steps a {\n color: var(--forge-theme-primary, #3f51b5);\n}\n.steps hr {\n border: none;\n border-top: 1px solid var(--forge-theme-outline, #e0e0e0);\n}\n.steps code {\n color: var(--forge-theme-on-surface-container-low, #000000);\n background-color: var(--forge-theme-surface-container-low, #ebebeb);\n border-radius: calc(var(--forge-shape-medium, 4px) * var(--forge-shape-factor, 1));\n padding: var(--forge-spacing-xxxsmall, 2px) var(--forge-spacing-xxsmall, 4px);\n font-size: 0.75em;\n}\n.steps pre {\n border: 1px solid var(--forge-theme-outline, #e0e0e0);\n border-radius: calc(var(--forge-shape-medium, 4px) * var(--forge-shape-factor, 1));\n margin: 0;\n max-width: 100%;\n overflow-x: auto;\n}\n.steps pre:focus-visible {\n outline: 2px solid var(--forge-theme-primary, #3f51b5);\n outline-offset: 2px;\n}\n.steps pre code {\n display: block;\n padding: var(--forge-spacing-xsmall, 8px) var(--forge-spacing-small, 12px);\n color: var(--forge-theme-on-surface-container-minimum, #000000);\n background: var(--forge-theme-surface-container-minimum, #f5f5f5);\n white-space: pre;\n}\n.steps [data-scroll-region=table] {\n max-width: 100%;\n overflow-x: auto;\n border-radius: calc(var(--forge-shape-large, 8px) * var(--forge-shape-factor, 1));\n margin-block: var(--forge-spacing-medium, 16px);\n}\n.steps [data-scroll-region=table]:focus-visible {\n outline: 2px solid var(--forge-theme-primary, #3f51b5);\n outline-offset: 2px;\n}\n.steps [data-scroll-region=table] table {\n margin-block: 0;\n}\n.steps table {\n width: 100%;\n border-spacing: 0;\n background-color: var(--forge-theme-surface, #ffffff);\n border-radius: calc(var(--forge-shape-large, 8px) * var(--forge-shape-factor, 1));\n overflow: hidden;\n margin-block: var(--forge-spacing-medium, 16px);\n}\n.steps table thead {\n color: var(--forge-theme-text-medium, rgba(0, 0, 0, 0.6));\n font-weight: bold;\n text-align: left;\n font-size: calc(var(--forge-typography-font-size, 1rem) * 0.75);\n box-sizing: border-box;\n}\n.steps table tr {\n height: 36px;\n height: 32px;\n}\n.steps table tr:nth-child(even) {\n background-color: var(--forge-theme-surface-dim, #fafafa);\n}\n.steps table tr:last-child td {\n border-bottom: none;\n}\n.steps table th,\n.steps table td {\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-family: var(--forge-typography-label1-font-family, var(--forge-typography-font-family, "Roboto", sans-serif));\n font-size: var(--forge-typography-label1-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-label-font-size-scale, 0.75)));\n font-weight: var(--forge-typography-label1-font-weight, 400);\n line-height: var(--forge-typography-label1-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-label-line-height-scale, 1.25)));\n letter-spacing: var(--forge-typography-label1-letter-spacing, 0.0357142857em);\n text-transform: var(--forge-typography-label1-text-transform, inherit);\n text-decoration: var(--forge-typography-label1-text-decoration, inherit);\n padding-block: var(--forge-spacing-xsmall, 8px);\n padding-inline-start: var(--forge-spacing-xsmall, 8px);\n padding-inline-end: var(--forge-spacing-medium, 16px);\n border-bottom-width: 1px;\n border-bottom-style: solid;\n border-bottom-color: var(--forge-theme-outline, #e0e0e0);\n}\n.steps table th {\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-family: var(--forge-typography-label2-font-family, var(--forge-typography-font-family, "Roboto", sans-serif));\n font-size: var(--forge-typography-label2-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-label-font-size-scale, 0.8125)));\n font-weight: var(--forge-typography-label2-font-weight, 400);\n line-height: var(--forge-typography-label2-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-label-line-height-scale, 1.25)));\n letter-spacing: var(--forge-typography-label2-letter-spacing, 0.0096153846em);\n text-transform: var(--forge-typography-label2-text-transform, inherit);\n text-decoration: var(--forge-typography-label2-text-decoration, inherit);\n font-weight: 600;\n}\n.steps > p:has(+ ol),\n.steps > p:has(+ ul) {\n margin-block-end: var(--forge-spacing-small, 12px);\n}\n.steps > ul {\n list-style-type: disc;\n padding-inline-start: 1.6875rem;\n}\n.steps > ol {\n padding-inline-start: 1.75rem;\n}\n.steps > ol,\n.steps > ul {\n margin: var(--forge-spacing-xsmall, 8px) 0;\n margin-block-end: var(--forge-spacing-medium, 16px);\n}\n.steps > :first-child {\n margin-block-start: 0;\n}\n.steps > p {\n margin-block-end: var(--forge-spacing-medium, 16px);\n overflow-wrap: anywhere;\n}\n.steps > p:first-child {\n margin-block-start: 0;\n}\n.steps > p:last-child {\n margin-block-end: 0;\n}\n.steps-count {\n color: var(--forge-theme-text-medium, rgba(0, 0, 0, 0.6));\n font-weight: 700;\n text-transform: uppercase;\n}\n\n.timeline {\n display: grid;\n grid-template-rows: 0fr;\n transition: grid-template-rows var(--forge-animation-duration-medium2, 300ms) var(--forge-animation-easing-decelerate, cubic-bezier(0, 0, 0, 1));\n}\n.timeline.expanded {\n grid-template-rows: 1fr;\n}\n\n.timeline-content {\n overflow: hidden;\n min-height: 0;\n padding-inline-start: var(--forge-spacing-small, 12px);\n}\n\n.timeline-row {\n position: relative;\n padding-inline-start: var(--forge-spacing-large, 24px);\n padding-block-end: var(--forge-spacing-medium, 16px);\n}\n.timeline-row::before {\n content: "";\n position: absolute;\n inset-block-start: calc((var(--forge-spacing-medium-large, 20px) - var(--forge-spacing-xsmall, 8px)) / 2);\n inset-block-end: calc(-1 * var(--forge-spacing-xsmall, 8px));\n inset-inline-start: calc((var(--forge-spacing-medium-large, 20px) - 1px) / 2);\n width: 1px;\n background-color: var(--forge-theme-outline, #e0e0e0);\n}\n.timeline-row:last-child::before {\n display: none;\n}\n\n.row-header {\n display: flex;\n align-items: flex-start;\n gap: var(--forge-spacing-small, 12px);\n width: 100%;\n min-height: var(--forge-spacing-medium-large, 20px);\n margin-inline-start: calc(-1 * var(--forge-spacing-large, 24px));\n}\n\n.row-marker {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n width: var(--forge-spacing-medium-large, 20px);\n height: var(--forge-spacing-medium-large, 20px);\n border-radius: calc(var(--forge-shape-round, 50%) * var(--forge-shape-factor, 1));\n}\n.row-marker::before {\n content: "";\n flex-shrink: 0;\n width: var(--forge-spacing-xsmall, 8px);\n height: var(--forge-spacing-xsmall, 8px);\n border-radius: calc(var(--forge-shape-round, 50%) * var(--forge-shape-factor, 1));\n background-color: currentColor;\n position: relative;\n z-index: 1;\n}\n\n.row-label {\n display: flex;\n flex-direction: column;\n flex: 1;\n min-width: 0;\n gap: var(--forge-spacing-xxxsmall, 2px);\n}\n\n.row-name {\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-family: var(--forge-typography-body1-font-family, var(--forge-typography-font-family, "Roboto", sans-serif));\n font-size: var(--forge-typography-body1-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-font-size-scale, 0.875)));\n font-weight: var(--forge-typography-body1-font-weight, 400);\n line-height: var(--forge-typography-body1-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-line-height-scale, 1.125)));\n letter-spacing: var(--forge-typography-body1-letter-spacing, 0.0357142857em);\n text-transform: var(--forge-typography-body1-text-transform, inherit);\n text-decoration: var(--forge-typography-body1-text-decoration, inherit);\n display: flex;\n align-items: center;\n gap: var(--forge-spacing-xsmall, 8px);\n line-height: normal;\n font-weight: 500;\n color: var(--forge-theme-text-high, rgba(0, 0, 0, 0.87));\n}\n\n.step-card {\n width: 100%;\n}\n.step-card-title {\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-family: var(--forge-typography-body1-font-family, var(--forge-typography-font-family, "Roboto", sans-serif));\n font-size: var(--forge-typography-body1-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-font-size-scale, 0.875)));\n font-weight: var(--forge-typography-body1-font-weight, 400);\n line-height: var(--forge-typography-body1-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-line-height-scale, 1.125)));\n letter-spacing: var(--forge-typography-body1-letter-spacing, 0.0357142857em);\n text-transform: var(--forge-typography-body1-text-transform, inherit);\n text-decoration: var(--forge-typography-body1-text-decoration, inherit);\n font-weight: 700;\n}\n.step-card-result {\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-family: var(--forge-typography-body1-font-family, var(--forge-typography-font-family, "Roboto", sans-serif));\n font-size: var(--forge-typography-body1-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-font-size-scale, 0.875)));\n font-weight: var(--forge-typography-body1-font-weight, 400);\n line-height: var(--forge-typography-body1-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-line-height-scale, 1.125)));\n letter-spacing: var(--forge-typography-body1-letter-spacing, 0.0357142857em);\n text-transform: var(--forge-typography-body1-text-transform, inherit);\n text-decoration: var(--forge-typography-body1-text-decoration, inherit);\n}\n\n.row-name strong,\n.step-card-title strong {\n font-weight: 700;\n}';
2
+ export {
3
+ styles as default
4
+ };
@@ -0,0 +1 @@
1
+ export * from './ai-steps';
@@ -0,0 +1,5 @@
1
+ import { AiStepsComponent, AiStepsComponentTagName } from "./ai-steps.mjs";
2
+ export {
3
+ AiStepsComponent,
4
+ AiStepsComponentTagName
5
+ };
@@ -218,7 +218,7 @@ highlightJson_fn = function(value) {
218
218
  return html`${unsafeHTML(highlighted)}`;
219
219
  };
220
220
  statusMarker_fn = function(toolCall) {
221
- if (toolCall.status === "error") {
221
+ if (toolCall.status === "error" || toolCall.status === "cancelled") {
222
222
  return html`<span class="row-marker row-marker--error">${__privateGet(this, _errorIcon)}</span>`;
223
223
  }
224
224
  if (isToolCallSettled(toolCall, this.tools?.get(toolCall.name))) {
@@ -38,6 +38,8 @@ export declare class ChatbotCoreController implements ReactiveController {
38
38
  getMessages(): ChatMessage[];
39
39
  setMessages(messages: ChatMessage[]): void;
40
40
  updateMessageContent(id: string, content: string): void;
41
+ /** Tool call ids rendered at or after `index` in {@link messageItems}. */
42
+ getToolCallIdsFrom(index: number): string[];
41
43
  removeMessageItemsFrom(index: number): void;
42
44
  setResponseFeedback(responseId: string, feedback: {
43
45
  type: 'positive' | 'negative';
@@ -2,6 +2,7 @@ import { SubscriptionManager } from "../ai-chatbot/event-emitter.mjs";
2
2
  import { FileUploadManager } from "../ai-chatbot/file-upload-manager.mjs";
3
3
  import { MessageStateController } from "../ai-chatbot/message-state-controller.mjs";
4
4
  import { generateId } from "../ai-chatbot/utils.mjs";
5
+ const UNSETTLED_TOOL_CALL_STATUSES = ["pending", "parsing", "executing"];
5
6
  class ChatbotCoreController {
6
7
  #host;
7
8
  #callbacks;
@@ -10,6 +11,14 @@ class ChatbotCoreController {
10
11
  #adapterSubscriptions;
11
12
  #adapter;
12
13
  #executingToolHandlers = 0;
14
+ /** Tool calls announced by an `mcp-ui-resource` event; they only settle via a tool result. */
15
+ #mcpAppToolCallIds = /* @__PURE__ */ new Set();
16
+ /** Agent tool calls marked complete on their `tool-call` chunk, before any result arrived. */
17
+ #presumedCompleteToolCallIds = /* @__PURE__ */ new Set();
18
+ /** Resolved UI resources whose tool call is not in state yet. */
19
+ #pendingUiResources = /* @__PURE__ */ new Map();
20
+ /** Bumped whenever the thread is cleared or replaced, so in-flight UI resource resolves from before are dropped. */
21
+ #threadGeneration = 0;
13
22
  constructor(host, config) {
14
23
  this.#host = host;
15
24
  this.#callbacks = config.callbacks;
@@ -83,6 +92,7 @@ class ChatbotCoreController {
83
92
  this.#adapter.onToolCallEnd(this.#handleToolCallEnd.bind(this)),
84
93
  this.#adapter.onToolCall(this.#handleToolCall.bind(this)),
85
94
  this.#adapter.onToolCallResult(this.#handleToolCallResult.bind(this)),
95
+ this.#adapter.onMcpUiResource(this.#handleMcpUiResource.bind(this)),
86
96
  this.#adapter.onStepStarted(this.#handleStepStarted.bind(this)),
87
97
  this.#adapter.onStepFinished(this.#handleStepFinished.bind(this)),
88
98
  this.#adapter.onRunFinished(this.#handleRunFinished.bind(this)),
@@ -115,10 +125,29 @@ class ChatbotCoreController {
115
125
  #handleRunFinished() {
116
126
  this.#tryCompleteResponse();
117
127
  }
128
+ /**
129
+ * A run ended (finished, aborted, or errored) without delivering a result for an
130
+ * MCP-app tool call: mark it cancelled so the widget receives `tool-cancelled`.
131
+ */
132
+ #cancelUnsettledMcpAppToolCalls() {
133
+ for (const id of this.#mcpAppToolCallIds) {
134
+ const toolCall = this.#messageStateController.getToolCall(id);
135
+ if (toolCall && UNSETTLED_TOOL_CALL_STATUSES.includes(toolCall.status)) {
136
+ this.#messageStateController.updateToolCallInResponse(id, { status: "cancelled" });
137
+ }
138
+ }
139
+ this.#mcpAppToolCallIds.clear();
140
+ this.#presumedCompleteToolCallIds.clear();
141
+ }
142
+ /**
143
+ * Finalizes once the run is truly over. An executing client-tool handler or a running
144
+ * adapter means a continuation follows, so widgets stay open for it to settle (§2).
145
+ */
118
146
  #tryCompleteResponse() {
119
147
  if (this.#executingToolHandlers > 0 || this.#adapter?.isRunning) {
120
148
  return;
121
149
  }
150
+ this.#cancelUnsettledMcpAppToolCalls();
122
151
  this.#messageStateController.tryFinalizeResponse();
123
152
  const messages = this.getMessages();
124
153
  const lastMessage = messages[messages.length - 1];
@@ -134,7 +163,8 @@ class ChatbotCoreController {
134
163
  args: {},
135
164
  argsBuffer: "",
136
165
  status: "parsing",
137
- type: this.tools.has(event.name) ? "client" : "agent"
166
+ type: this.tools.has(event.name) ? "client" : "agent",
167
+ ...this.#takePendingUiResource(event.id)
138
168
  };
139
169
  this.#messageStateController.addToolCallToResponse(toolCall);
140
170
  const toolDef = this.tools.get(event.name);
@@ -195,7 +225,8 @@ class ChatbotCoreController {
195
225
  name: event.name,
196
226
  args: event.args,
197
227
  status: "executing",
198
- type: this.tools.has(event.name) ? "client" : "agent"
228
+ type: this.tools.has(event.name) ? "client" : "agent",
229
+ ...this.#takePendingUiResource(event.id)
199
230
  };
200
231
  this.#messageStateController.addToolCallToResponse(toolCall);
201
232
  }
@@ -211,7 +242,8 @@ class ChatbotCoreController {
211
242
  } else {
212
243
  this.#sendToolResult(event.id, this.#createToolResponse(event.name));
213
244
  }
214
- } else {
245
+ } else if (!this.#mcpAppToolCallIds.has(event.id)) {
246
+ this.#presumedCompleteToolCallIds.add(event.id);
215
247
  this.#messageStateController.updateToolCallInResponse(event.id, { status: "complete" });
216
248
  }
217
249
  }
@@ -249,6 +281,7 @@ class ChatbotCoreController {
249
281
  }
250
282
  }
251
283
  #handleError(event) {
284
+ this.#cancelUnsettledMcpAppToolCalls();
252
285
  const errorMessage = {
253
286
  id: generateId(),
254
287
  role: "assistant",
@@ -261,6 +294,7 @@ class ChatbotCoreController {
261
294
  this.#callbacks.onDispatchEvent("forge-ai-chatbot-error", { error: event.message });
262
295
  }
263
296
  #handleRunAborted() {
297
+ this.#cancelUnsettledMcpAppToolCalls();
264
298
  this.#messageStateController.tryFinalizeResponse();
265
299
  const id = this.#messageStateController.addClientMessage({ content: "Run cancelled" });
266
300
  const abortMessage = this.#messageStateController.getMessage(id);
@@ -272,8 +306,53 @@ class ChatbotCoreController {
272
306
  this.#callbacks.onRequestUpdate();
273
307
  }
274
308
  #handleToolCallResult(event) {
309
+ this.#presumedCompleteToolCallIds.delete(event.toolCallId);
275
310
  this.#messageStateController.completeToolCallInResponse(event.toolCallId, event.result, event);
276
311
  }
312
+ /**
313
+ * Resolves and stamps the UI resource. Works whether the event arrives before or after
314
+ * the tool call's chunks, mid-run, after the run ended, or after `setMessages` for
315
+ * tool calls restored from history. A resolve that outlives the thread it started in
316
+ * (cleared/replaced, or the adapter swapped) is dropped.
317
+ */
318
+ async #handleMcpUiResource(event) {
319
+ const adapter = this.#adapter;
320
+ if (!adapter?.resolveMcpAppResource) {
321
+ return;
322
+ }
323
+ const { toolCallId, resourceUri } = event;
324
+ const generation = this.#threadGeneration;
325
+ this.#mcpAppToolCallIds.add(toolCallId);
326
+ if (this.#presumedCompleteToolCallIds.delete(toolCallId)) {
327
+ this.#messageStateController.updateToolCallInResponse(toolCallId, {
328
+ status: "executing",
329
+ endTimestamp: void 0
330
+ });
331
+ }
332
+ let uiResource;
333
+ try {
334
+ uiResource = await adapter.resolveMcpAppResource({ resourceUri, toolCallId });
335
+ } catch (error) {
336
+ console.warn(`[forge-ai-mcp-app] Failed to resolve UI resource for tool call ${toolCallId}:`, error);
337
+ return;
338
+ }
339
+ if (!uiResource || adapter !== this.#adapter || generation !== this.#threadGeneration) {
340
+ return;
341
+ }
342
+ if (this.#messageStateController.getToolCall(toolCallId)) {
343
+ this.#messageStateController.updateToolCallInResponse(toolCallId, { uiResource });
344
+ } else {
345
+ this.#pendingUiResources.set(toolCallId, uiResource);
346
+ }
347
+ }
348
+ #takePendingUiResource(toolCallId) {
349
+ const uiResource = this.#pendingUiResources.get(toolCallId);
350
+ if (!uiResource) {
351
+ return {};
352
+ }
353
+ this.#pendingUiResources.delete(toolCallId);
354
+ return { uiResource };
355
+ }
277
356
  async #sendToolResult(toolCallId, result) {
278
357
  const toolCall = this.#messageStateController.getToolCall(toolCallId);
279
358
  if (!toolCall || !this.#adapter) {
@@ -366,6 +445,10 @@ class ChatbotCoreController {
366
445
  return this.#messageStateController.getToolCall(id);
367
446
  }
368
447
  clearMessages() {
448
+ this.#threadGeneration++;
449
+ this.#mcpAppToolCallIds.clear();
450
+ this.#presumedCompleteToolCallIds.clear();
451
+ this.#pendingUiResources.clear();
369
452
  this.#messageStateController.clearMessages();
370
453
  this.#fileUploadManager.clear();
371
454
  }
@@ -373,13 +456,25 @@ class ChatbotCoreController {
373
456
  return this.#messageStateController.getMessages();
374
457
  }
375
458
  setMessages(messages) {
459
+ this.#threadGeneration++;
460
+ this.#mcpAppToolCallIds.clear();
461
+ this.#presumedCompleteToolCallIds.clear();
462
+ this.#pendingUiResources.clear();
376
463
  this.#messageStateController.setMessages(messages);
377
464
  }
378
465
  updateMessageContent(id, content) {
379
466
  this.#messageStateController.updateMessageContent(id, content);
380
467
  }
468
+ /** Tool call ids rendered at or after `index` in {@link messageItems}. */
469
+ getToolCallIdsFrom(index) {
470
+ return this.#messageStateController.getToolCallIdsFrom(index);
471
+ }
381
472
  removeMessageItemsFrom(index) {
382
- this.#messageStateController.removeMessageItemsFrom(index);
473
+ for (const id of this.#messageStateController.removeMessageItemsFrom(index)) {
474
+ this.#mcpAppToolCallIds.delete(id);
475
+ this.#presumedCompleteToolCallIds.delete(id);
476
+ this.#pendingUiResources.delete(id);
477
+ }
383
478
  }
384
479
  setResponseFeedback(responseId, feedback) {
385
480
  this.#messageStateController.setResponseFeedback(responseId, feedback);
@@ -10,6 +10,7 @@ export interface ResizeControllerOptions {
10
10
  onResize?: (width: number) => void;
11
11
  onResizeStart?: () => void;
12
12
  onResizeEnd?: () => void;
13
+ onCommit?: (width: number) => void;
13
14
  }
14
15
  export declare class ResizeController implements ReactiveController {
15
16
  #private;
@@ -36,9 +36,10 @@ class ResizeController {
36
36
  return;
37
37
  }
38
38
  event.preventDefault();
39
- const newWidth = this.#currentWidth + delta;
40
- this.#currentWidth = this.#constrainWidth(newWidth);
39
+ const previousWidth = this.#currentWidth;
40
+ this.#currentWidth = this.#constrainWidth(previousWidth + delta);
41
41
  this.#applyWidth();
42
+ this.#commitIfChanged(previousWidth);
42
43
  };
43
44
  this.#handlePointerMove = (event) => {
44
45
  if (!this.#isResizing) {
@@ -62,6 +63,7 @@ class ResizeController {
62
63
  this.#isResizing = false;
63
64
  this.#host.requestUpdate();
64
65
  this.#options.onResizeEnd();
66
+ this.#commitIfChanged(this.#dragStartWidth);
65
67
  this.#removeResizeListeners();
66
68
  };
67
69
  this.#host = host;
@@ -76,6 +78,8 @@ class ResizeController {
76
78
  },
77
79
  onResizeEnd: () => {
78
80
  },
81
+ onCommit: () => {
82
+ },
79
83
  ...options
80
84
  };
81
85
  this.#currentWidth = this.#options.defaultWidth;
@@ -108,6 +112,11 @@ class ResizeController {
108
112
  }
109
113
  #handlePointerMove;
110
114
  #handlePointerUp;
115
+ #commitIfChanged(previousWidth) {
116
+ if (this.#currentWidth !== previousWidth) {
117
+ this.#options.onCommit(this.#currentWidth);
118
+ }
119
+ }
111
120
  #constrainWidth(width) {
112
121
  const availableWidth = window.innerWidth - RESIZE_SCREEN_BUFFER;
113
122
  const effectiveMaxWidth = Math.min(this.#options.maxWidth, availableWidth);
package/dist/index.d.ts CHANGED
@@ -5,6 +5,8 @@ export * from './ai-agent-info';
5
5
  export * from './ai-agent-selector';
6
6
  export * from './ai-tool-call-indicator';
7
7
  export * from './ai-artifact';
8
+ export * from './ai-artifact-card';
9
+ export * from './ai-artifact-card-group';
8
10
  export * from './ai-attachment';
9
11
  export * from './ai-button';
10
12
  export * from './ai-chain-of-thought';
@@ -40,6 +42,7 @@ export * from './ai-response-message';
40
42
  export * from './ai-sidebar';
41
43
  export * from './ai-sidebar-chat';
42
44
  export * from './ai-slash-command-menu';
45
+ export * from './ai-steps';
43
46
  export * from './ai-suggestions';
44
47
  export * from './ai-thinking-indicator';
45
48
  export * from './ai-threads';
package/dist/index.mjs CHANGED
@@ -5,6 +5,8 @@ import { AiAgentInfoComponent, AiAgentInfoComponentTagName } from "./ai-agent-in
5
5
  import { AiAgentSelectorComponent, AiAgentSelectorComponentTagName } from "./ai-agent-selector/ai-agent-selector.mjs";
6
6
  import { AiToolCallIndicatorComponent, AiToolCallIndicatorComponentTagName } from "./ai-tool-call-indicator/ai-tool-call-indicator.mjs";
7
7
  import { AiArtifactComponent, AiArtifactComponentTagName } from "./ai-artifact/ai-artifact.mjs";
8
+ import { AiArtifactCardComponent, AiArtifactCardComponentTagName } from "./ai-artifact-card/ai-artifact-card.mjs";
9
+ import { AiArtifactCardGroupComponent, AiArtifactCardGroupComponentTagName } from "./ai-artifact-card-group/ai-artifact-card-group.mjs";
8
10
  import { AiAttachmentComponent, AiAttachmentComponentTagName } from "./ai-attachment/ai-attachment.mjs";
9
11
  import { AiButtonComponent, AiButtonComponentTagName } from "./ai-button/ai-button.mjs";
10
12
  import { AiChainOfThoughtComponent, AiChainOfThoughtComponentTagName } from "./ai-chain-of-thought/ai-chain-of-thought.mjs";
@@ -61,6 +63,7 @@ import { AiResponseMessageComponent, AiResponseMessageComponentTagName } from ".
61
63
  import { AiSidebarComponent, AiSidebarComponentTagName } from "./ai-sidebar/ai-sidebar.mjs";
62
64
  import { AiSidebarChatComponent, AiSidebarChatComponentTagName } from "./ai-sidebar-chat/ai-sidebar-chat.mjs";
63
65
  import { AiSlashCommandMenuComponent, AiSlashCommandMenuComponentTagName } from "./ai-slash-command-menu/ai-slash-command-menu.mjs";
66
+ import { AiStepsComponent, AiStepsComponentTagName } from "./ai-steps/ai-steps.mjs";
64
67
  import { AiSuggestionsComponent, AiSuggestionsComponentTagName } from "./ai-suggestions/ai-suggestions.mjs";
65
68
  import { AiThinkingIndicatorComponent, AiThinkingIndicatorComponentTagName } from "./ai-thinking-indicator/ai-thinking-indicator.mjs";
66
69
  import { AiThreadsComponent, AiThreadsComponentTagName } from "./ai-threads/ai-threads.mjs";
@@ -77,6 +80,10 @@ export {
77
80
  AiAgentInfoComponentTagName,
78
81
  AiAgentSelectorComponent,
79
82
  AiAgentSelectorComponentTagName,
83
+ AiArtifactCardComponent,
84
+ AiArtifactCardComponentTagName,
85
+ AiArtifactCardGroupComponent,
86
+ AiArtifactCardGroupComponentTagName,
80
87
  AiArtifactComponent,
81
88
  AiArtifactComponentTagName,
82
89
  AiAssistantResponseComponent,
@@ -152,6 +159,8 @@ export {
152
159
  AiSidebarComponentTagName,
153
160
  AiSlashCommandMenuComponent,
154
161
  AiSlashCommandMenuComponentTagName,
162
+ AiStepsComponent,
163
+ AiStepsComponentTagName,
155
164
  AiSuggestionsComponent,
156
165
  AiSuggestionsComponentTagName,
157
166
  AiThinkingIndicatorComponent,