@artooi/ag-ui-web-component 0.25.2 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -1
- package/README.md +69 -0
- package/dist/ag-ui-web-component.bundle.js +73 -29
- package/dist/ag-ui-web-component.bundle.js.map +4 -4
- package/dist/constants.d.ts +10 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/core/ag_ui_chat.d.ts +19 -0
- package/dist/core/ag_ui_chat.d.ts.map +1 -1
- package/dist/core/agui_client.d.ts +12 -1
- package/dist/core/agui_client.d.ts.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +712 -165
- package/dist/index.js.map +4 -4
- package/dist/tools/client_tool_registry.d.ts +24 -1
- package/dist/tools/client_tool_registry.d.ts.map +1 -1
- package/dist/ui/chart_block.d.ts +39 -0
- package/dist/ui/chart_block.d.ts.map +1 -0
- package/dist/ui/chart_spec_from.d.ts +12 -0
- package/dist/ui/chart_spec_from.d.ts.map +1 -0
- package/dist/ui/chart_tool.d.ts +15 -0
- package/dist/ui/chart_tool.d.ts.map +1 -0
- package/dist/ui/styles.d.ts +1 -1
- package/dist/ui/styles.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/constants.ts +11 -0
- package/src/core/ag_ui_chat.ts +145 -3
- package/src/core/agui_client.ts +51 -3
- package/src/index.ts +8 -0
- package/src/tools/client_tool_registry.ts +24 -1
- package/src/ui/chart_block.ts +349 -0
- package/src/ui/chart_spec_from.ts +106 -0
- package/src/ui/chart_tool.ts +64 -0
- package/src/ui/styles.ts +44 -0
- package/src/version.ts +1 -1
package/dist/index.js
CHANGED
|
@@ -46,12 +46,13 @@ var ICON_FILE = `<svg class="glyph" viewBox="0 0 24 24" aria-hidden="true"><path
|
|
|
46
46
|
var ICON_FILE_IMAGE = `<svg class="glyph" viewBox="0 0 24 24" aria-hidden="true"><rect x="3.5" y="4.5" width="17" height="15" rx="2.5"/><circle cx="9" cy="10" r="1.5"/><path d="M4.5 17.5 9 13.5l3.5 3 3-2.5 4.5 4"/></svg>`;
|
|
47
47
|
var ICON_FILE_PDF = `<svg class="glyph" viewBox="0 0 24 24" aria-hidden="true"><path d="M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z"/><path d="M14 3v5h5"/><rect class="glyph--solid" x="7.5" y="13.5" width="9" height="4.5" rx="1"/></svg>`;
|
|
48
48
|
var ICON_FILE_TEXT = `<svg class="glyph" viewBox="0 0 24 24" aria-hidden="true"><path d="M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z"/><path d="M14 3v5h5"/><path d="M8.5 13.5h7M8.5 17h4.5"/></svg>`;
|
|
49
|
+
var CHART_ACTIVITY_TYPE = "chart";
|
|
49
50
|
|
|
50
51
|
// src/skills/fill_template.ts
|
|
51
52
|
var PLACEHOLDER_RE = /\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
|
|
52
53
|
function fillTemplate(template, values) {
|
|
53
54
|
const missing = [];
|
|
54
|
-
const
|
|
55
|
+
const text3 = template.replace(PLACEHOLDER_RE, (token, key) => {
|
|
55
56
|
const value = values[key];
|
|
56
57
|
if (value === void 0 || value === null || value === "") {
|
|
57
58
|
if (!missing.includes(key)) {
|
|
@@ -61,7 +62,7 @@ function fillTemplate(template, values) {
|
|
|
61
62
|
}
|
|
62
63
|
return String(value);
|
|
63
64
|
});
|
|
64
|
-
return { text:
|
|
65
|
+
return { text: text3, missing };
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
// src/skills/parse_skills.ts
|
|
@@ -131,17 +132,17 @@ var setInputValue = prototypeSetter(HTMLInputElement.prototype, "value");
|
|
|
131
132
|
var setTextareaValue = prototypeSetter(HTMLTextAreaElement.prototype, "value");
|
|
132
133
|
var setSelectValue = prototypeSetter(HTMLSelectElement.prototype, "value");
|
|
133
134
|
var setInputChecked = prototypeSetter(HTMLInputElement.prototype, "checked");
|
|
134
|
-
function setNativeValue(
|
|
135
|
-
if (
|
|
136
|
-
setTextareaValue.call(
|
|
137
|
-
} else if (
|
|
138
|
-
setSelectValue.call(
|
|
135
|
+
function setNativeValue(el2, value) {
|
|
136
|
+
if (el2 instanceof HTMLTextAreaElement) {
|
|
137
|
+
setTextareaValue.call(el2, value);
|
|
138
|
+
} else if (el2 instanceof HTMLSelectElement) {
|
|
139
|
+
setSelectValue.call(el2, value);
|
|
139
140
|
} else {
|
|
140
|
-
setInputValue.call(
|
|
141
|
+
setInputValue.call(el2, value);
|
|
141
142
|
}
|
|
142
143
|
}
|
|
143
|
-
function setNativeChecked(
|
|
144
|
-
setInputChecked.call(
|
|
144
|
+
function setNativeChecked(el2, checked) {
|
|
145
|
+
setInputChecked.call(el2, checked);
|
|
145
146
|
}
|
|
146
147
|
|
|
147
148
|
// src/dom/animations.ts
|
|
@@ -162,42 +163,42 @@ function motionDelay(ms) {
|
|
|
162
163
|
}
|
|
163
164
|
return delay(ms);
|
|
164
165
|
}
|
|
165
|
-
function accentColor(
|
|
166
|
-
const themed = window.getComputedStyle(
|
|
166
|
+
function accentColor(el2, fallback) {
|
|
167
|
+
const themed = window.getComputedStyle(el2).getPropertyValue(ACCENT_PROPERTY).trim();
|
|
167
168
|
return themed === "" ? fallback : themed;
|
|
168
169
|
}
|
|
169
|
-
function ringShadow(
|
|
170
|
-
return `0 0 0 3px ${accentColor(
|
|
170
|
+
function ringShadow(el2) {
|
|
171
|
+
return `0 0 0 3px ${accentColor(el2, ACCENT_HALO)}`;
|
|
171
172
|
}
|
|
172
|
-
async function typeInto(
|
|
173
|
+
async function typeInto(el2, value, options = {}) {
|
|
173
174
|
const charDelayMs = options.charDelayMs ?? 35;
|
|
174
|
-
setNativeValue(
|
|
175
|
-
|
|
175
|
+
setNativeValue(el2, "");
|
|
176
|
+
el2.dispatchEvent(new Event("input", { bubbles: true }));
|
|
176
177
|
for (const char of value) {
|
|
177
|
-
setNativeValue(
|
|
178
|
-
|
|
178
|
+
setNativeValue(el2, el2.value + char);
|
|
179
|
+
el2.dispatchEvent(new Event("input", { bubbles: true }));
|
|
179
180
|
if (charDelayMs > 0) {
|
|
180
181
|
await delay(charDelayMs);
|
|
181
182
|
}
|
|
182
183
|
}
|
|
183
|
-
|
|
184
|
+
el2.dispatchEvent(new Event("change", { bubbles: true }));
|
|
184
185
|
}
|
|
185
|
-
async function highlightThenClick(
|
|
186
|
+
async function highlightThenClick(el2, options = {}) {
|
|
186
187
|
const highlightMs = options.highlightMs ?? 280;
|
|
187
|
-
const previousOutline =
|
|
188
|
-
const previousOffset =
|
|
189
|
-
|
|
190
|
-
|
|
188
|
+
const previousOutline = el2.style.outline;
|
|
189
|
+
const previousOffset = el2.style.outlineOffset;
|
|
190
|
+
el2.style.outline = `2px solid ${accentColor(el2, ACCENT)}`;
|
|
191
|
+
el2.style.outlineOffset = "2px";
|
|
191
192
|
await delay(highlightMs);
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
193
|
+
el2.style.outline = previousOutline;
|
|
194
|
+
el2.style.outlineOffset = previousOffset;
|
|
195
|
+
el2.click();
|
|
195
196
|
}
|
|
196
197
|
var SCROLL_SETTLE_MS = 600;
|
|
197
198
|
var SCROLL_START_MS = 100;
|
|
198
|
-
function scrollIntoCenterView(
|
|
199
|
+
function scrollIntoCenterView(el2, options = {}) {
|
|
199
200
|
const reduced = prefersReducedMotion();
|
|
200
|
-
|
|
201
|
+
el2.scrollIntoView({
|
|
201
202
|
block: "center",
|
|
202
203
|
inline: "nearest",
|
|
203
204
|
behavior: reduced ? "auto" : "smooth"
|
|
@@ -225,85 +226,85 @@ function scrollIntoCenterView(el, options = {}) {
|
|
|
225
226
|
}
|
|
226
227
|
var FLASH_MS = 1200;
|
|
227
228
|
var FLASH_FADE_RATIO = 1 / 3;
|
|
228
|
-
async function flashRing(
|
|
229
|
+
async function flashRing(el2, options, focusByDefault) {
|
|
229
230
|
if (options.focus ?? focusByDefault) {
|
|
230
|
-
|
|
231
|
+
el2.focus({ preventScroll: true });
|
|
231
232
|
}
|
|
232
233
|
const flashMs = options.flashMs ?? FLASH_MS;
|
|
233
234
|
if (flashMs <= 0) {
|
|
234
235
|
return;
|
|
235
236
|
}
|
|
236
|
-
const previousOutline =
|
|
237
|
-
const previousOffset =
|
|
238
|
-
const previousTransition =
|
|
239
|
-
const color = options.color ?? accentColor(
|
|
240
|
-
|
|
241
|
-
|
|
237
|
+
const previousOutline = el2.style.outline;
|
|
238
|
+
const previousOffset = el2.style.outlineOffset;
|
|
239
|
+
const previousTransition = el2.style.transition;
|
|
240
|
+
const color = options.color ?? accentColor(el2, ACCENT);
|
|
241
|
+
el2.style.outline = `3px solid ${color}`;
|
|
242
|
+
el2.style.outlineOffset = "2px";
|
|
242
243
|
const fadeMs = prefersReducedMotion() ? 0 : Math.round(flashMs * FLASH_FADE_RATIO);
|
|
243
244
|
await delay(flashMs - fadeMs);
|
|
244
245
|
if (fadeMs > 0) {
|
|
245
|
-
|
|
246
|
-
|
|
246
|
+
el2.style.transition = `outline-color ${fadeMs}ms ease-out`;
|
|
247
|
+
el2.style.outline = "3px solid transparent";
|
|
247
248
|
await delay(fadeMs);
|
|
248
249
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
250
|
+
el2.style.outline = previousOutline;
|
|
251
|
+
el2.style.outlineOffset = previousOffset;
|
|
252
|
+
el2.style.transition = previousTransition;
|
|
252
253
|
}
|
|
253
|
-
function flash(
|
|
254
|
-
return flashRing(
|
|
254
|
+
function flash(el2, options = {}) {
|
|
255
|
+
return flashRing(el2, options, false);
|
|
255
256
|
}
|
|
256
|
-
function focusWithFlash(
|
|
257
|
-
return flashRing(
|
|
257
|
+
function focusWithFlash(el2, options = {}) {
|
|
258
|
+
return flashRing(el2, options, true);
|
|
258
259
|
}
|
|
259
|
-
async function pressThenClick(
|
|
260
|
+
async function pressThenClick(el2, options = {}) {
|
|
260
261
|
const pressMs = options.pressMs ?? 140;
|
|
261
|
-
const previousTransform =
|
|
262
|
-
const previousTransition =
|
|
263
|
-
const previousShadow =
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
262
|
+
const previousTransform = el2.style.transform;
|
|
263
|
+
const previousTransition = el2.style.transition;
|
|
264
|
+
const previousShadow = el2.style.boxShadow;
|
|
265
|
+
el2.style.transition = "transform 80ms ease";
|
|
266
|
+
el2.style.transform = "scale(0.96)";
|
|
267
|
+
el2.style.boxShadow = ringShadow(el2);
|
|
267
268
|
await motionDelay(pressMs);
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
269
|
+
el2.style.transform = previousTransform;
|
|
270
|
+
el2.style.transition = previousTransition;
|
|
271
|
+
el2.style.boxShadow = previousShadow;
|
|
272
|
+
el2.click();
|
|
272
273
|
}
|
|
273
|
-
function findOption(
|
|
274
|
-
for (const option of Array.from(
|
|
274
|
+
function findOption(el2, value) {
|
|
275
|
+
for (const option of Array.from(el2.options)) {
|
|
275
276
|
if (option.value === value || option.text === value) {
|
|
276
277
|
return option;
|
|
277
278
|
}
|
|
278
279
|
}
|
|
279
280
|
return null;
|
|
280
281
|
}
|
|
281
|
-
async function selectOption(
|
|
282
|
-
const option = findOption(
|
|
282
|
+
async function selectOption(el2, value, options = {}) {
|
|
283
|
+
const option = findOption(el2, value);
|
|
283
284
|
if (option === null) {
|
|
284
285
|
throw new Error(`no <option> matching "${value}"`);
|
|
285
286
|
}
|
|
286
287
|
const highlightMs = options.highlightMs ?? 220;
|
|
287
|
-
const previousOutline =
|
|
288
|
-
const previousOffset =
|
|
289
|
-
|
|
290
|
-
|
|
288
|
+
const previousOutline = el2.style.outline;
|
|
289
|
+
const previousOffset = el2.style.outlineOffset;
|
|
290
|
+
el2.style.outline = `2px solid ${accentColor(el2, ACCENT)}`;
|
|
291
|
+
el2.style.outlineOffset = "2px";
|
|
291
292
|
await motionDelay(highlightMs);
|
|
292
|
-
setNativeValue(
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
293
|
+
setNativeValue(el2, option.value);
|
|
294
|
+
el2.dispatchEvent(new Event("input", { bubbles: true }));
|
|
295
|
+
el2.dispatchEvent(new Event("change", { bubbles: true }));
|
|
296
|
+
el2.style.outline = previousOutline;
|
|
297
|
+
el2.style.outlineOffset = previousOffset;
|
|
297
298
|
}
|
|
298
|
-
async function toggleControl(
|
|
299
|
+
async function toggleControl(el2, checked, options = {}) {
|
|
299
300
|
const flashMs = options.flashMs ?? 200;
|
|
300
|
-
const previousShadow =
|
|
301
|
-
|
|
301
|
+
const previousShadow = el2.style.boxShadow;
|
|
302
|
+
el2.style.boxShadow = ringShadow(el2);
|
|
302
303
|
await motionDelay(flashMs);
|
|
303
|
-
setNativeChecked(
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
304
|
+
setNativeChecked(el2, checked);
|
|
305
|
+
el2.dispatchEvent(new Event("input", { bubbles: true }));
|
|
306
|
+
el2.dispatchEvent(new Event("change", { bubbles: true }));
|
|
307
|
+
el2.style.boxShadow = previousShadow;
|
|
307
308
|
}
|
|
308
309
|
|
|
309
310
|
// src/tools/page_action_tools.ts
|
|
@@ -677,7 +678,7 @@ function attachCopyButtons(root, strings) {
|
|
|
677
678
|
}
|
|
678
679
|
}
|
|
679
680
|
function button(code, strings) {
|
|
680
|
-
const
|
|
681
|
+
const text3 = code.textContent;
|
|
681
682
|
const copy = document.createElement("button");
|
|
682
683
|
copy.type = "button";
|
|
683
684
|
copy.className = "code-copy";
|
|
@@ -686,19 +687,19 @@ function button(code, strings) {
|
|
|
686
687
|
copy.title = strings.copyCode;
|
|
687
688
|
copy.setAttribute("aria-label", strings.copyCode);
|
|
688
689
|
copy.addEventListener("click", () => {
|
|
689
|
-
void writeText(
|
|
690
|
+
void writeText(text3).then((ok) => {
|
|
690
691
|
confirm(copy, ok ? strings.copied : strings.copyFailed, strings);
|
|
691
692
|
});
|
|
692
693
|
});
|
|
693
694
|
return copy;
|
|
694
695
|
}
|
|
695
|
-
async function writeText(
|
|
696
|
+
async function writeText(text3) {
|
|
696
697
|
const clipboard = navigator.clipboard;
|
|
697
698
|
if (clipboard === void 0) {
|
|
698
699
|
return false;
|
|
699
700
|
}
|
|
700
701
|
try {
|
|
701
|
-
await clipboard.writeText(
|
|
702
|
+
await clipboard.writeText(text3);
|
|
702
703
|
return true;
|
|
703
704
|
} catch {
|
|
704
705
|
return false;
|
|
@@ -984,6 +985,364 @@ function accepts(accept, file) {
|
|
|
984
985
|
});
|
|
985
986
|
}
|
|
986
987
|
|
|
988
|
+
// src/ui/chart_block.ts
|
|
989
|
+
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
990
|
+
var WIDTH = 480;
|
|
991
|
+
var HEIGHT = 220;
|
|
992
|
+
var PAD = { top: 20, right: 12, bottom: 30, left: 44 };
|
|
993
|
+
var PLOT_W = WIDTH - PAD.left - PAD.right;
|
|
994
|
+
var PLOT_H = HEIGHT - PAD.top - PAD.bottom;
|
|
995
|
+
var SERIES_COLORS = [
|
|
996
|
+
"var(--ag-ui-chart-1, #4f7cff)",
|
|
997
|
+
"var(--ag-ui-chart-2, #21b573)",
|
|
998
|
+
"var(--ag-ui-chart-3, #e0803c)",
|
|
999
|
+
"var(--ag-ui-chart-4, #b563d8)",
|
|
1000
|
+
"var(--ag-ui-chart-5, #d84f6e)",
|
|
1001
|
+
"var(--ag-ui-chart-6, #3ba7c4)"
|
|
1002
|
+
];
|
|
1003
|
+
function seriesColor(index) {
|
|
1004
|
+
return SERIES_COLORS[index % SERIES_COLORS.length];
|
|
1005
|
+
}
|
|
1006
|
+
function el(name, attrs) {
|
|
1007
|
+
const node = document.createElementNS(SVG_NS, name);
|
|
1008
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
1009
|
+
node.setAttribute(key, String(value));
|
|
1010
|
+
}
|
|
1011
|
+
return node;
|
|
1012
|
+
}
|
|
1013
|
+
function text(value, attrs) {
|
|
1014
|
+
const node = el("text", {
|
|
1015
|
+
"font-size": 10,
|
|
1016
|
+
fill: "currentColor",
|
|
1017
|
+
"fill-opacity": 0.65,
|
|
1018
|
+
...attrs
|
|
1019
|
+
});
|
|
1020
|
+
node.textContent = value;
|
|
1021
|
+
return node;
|
|
1022
|
+
}
|
|
1023
|
+
function stackRunning(spec) {
|
|
1024
|
+
const seen = [];
|
|
1025
|
+
spec.labels.forEach((_label, i) => {
|
|
1026
|
+
let running = 0;
|
|
1027
|
+
for (const series of spec.series) {
|
|
1028
|
+
running += series.points[i] ?? 0;
|
|
1029
|
+
seen.push(running);
|
|
1030
|
+
}
|
|
1031
|
+
});
|
|
1032
|
+
return seen;
|
|
1033
|
+
}
|
|
1034
|
+
function extent(spec) {
|
|
1035
|
+
const values = spec.kind === "stacked" ? stackRunning(spec) : spec.series.flatMap((series) => [...series.points]);
|
|
1036
|
+
const max = Math.max(0, ...values);
|
|
1037
|
+
const min = Math.min(0, ...values);
|
|
1038
|
+
return max === min ? { min, max: max + 1 } : { min, max };
|
|
1039
|
+
}
|
|
1040
|
+
function scaleY(value, min, max) {
|
|
1041
|
+
return PAD.top + PLOT_H - (value - min) / (max - min) * PLOT_H;
|
|
1042
|
+
}
|
|
1043
|
+
function bandCentre(index, count) {
|
|
1044
|
+
const step = PLOT_W / count;
|
|
1045
|
+
return PAD.left + step * index + step / 2;
|
|
1046
|
+
}
|
|
1047
|
+
function drawAxes(svg2, spec, min, max) {
|
|
1048
|
+
for (const value of [min, max]) {
|
|
1049
|
+
const y2 = scaleY(value, min, max);
|
|
1050
|
+
svg2.appendChild(
|
|
1051
|
+
el("line", {
|
|
1052
|
+
x1: PAD.left,
|
|
1053
|
+
y1: y2,
|
|
1054
|
+
x2: WIDTH - PAD.right,
|
|
1055
|
+
y2,
|
|
1056
|
+
stroke: "currentColor",
|
|
1057
|
+
"stroke-opacity": value === min ? 0.35 : 0.12
|
|
1058
|
+
})
|
|
1059
|
+
);
|
|
1060
|
+
svg2.appendChild(
|
|
1061
|
+
text(String(Math.round(value)), { x: PAD.left - 6, y: y2 + 4, "text-anchor": "end" })
|
|
1062
|
+
);
|
|
1063
|
+
}
|
|
1064
|
+
spec.labels.forEach((label, i) => {
|
|
1065
|
+
svg2.appendChild(
|
|
1066
|
+
text(label, {
|
|
1067
|
+
x: bandCentre(i, spec.labels.length),
|
|
1068
|
+
y: HEIGHT - PAD.bottom + 16,
|
|
1069
|
+
"text-anchor": "middle"
|
|
1070
|
+
})
|
|
1071
|
+
);
|
|
1072
|
+
});
|
|
1073
|
+
}
|
|
1074
|
+
function drawBars(svg2, spec, min, max) {
|
|
1075
|
+
const step = PLOT_W / spec.labels.length;
|
|
1076
|
+
const width = step * 0.7 / spec.series.length;
|
|
1077
|
+
const base = scaleY(min, min, max);
|
|
1078
|
+
spec.series.forEach((series, s) => {
|
|
1079
|
+
series.points.forEach((value, i) => {
|
|
1080
|
+
const y2 = scaleY(value, min, max);
|
|
1081
|
+
svg2.appendChild(
|
|
1082
|
+
el("rect", {
|
|
1083
|
+
x: PAD.left + step * i + step * 0.15 + width * s,
|
|
1084
|
+
y: y2,
|
|
1085
|
+
width,
|
|
1086
|
+
height: Math.max(1, base - y2),
|
|
1087
|
+
fill: seriesColor(s),
|
|
1088
|
+
rx: 2
|
|
1089
|
+
})
|
|
1090
|
+
);
|
|
1091
|
+
});
|
|
1092
|
+
});
|
|
1093
|
+
}
|
|
1094
|
+
function drawStacked(svg2, spec, min, max) {
|
|
1095
|
+
const step = PLOT_W / spec.labels.length;
|
|
1096
|
+
const width = step * 0.7;
|
|
1097
|
+
const running = spec.labels.map(() => 0);
|
|
1098
|
+
spec.series.forEach((series, s) => {
|
|
1099
|
+
series.points.forEach((value, i) => {
|
|
1100
|
+
const from = running[i];
|
|
1101
|
+
const to = from + value;
|
|
1102
|
+
running[i] = to;
|
|
1103
|
+
const y2 = scaleY(to, min, max);
|
|
1104
|
+
svg2.appendChild(
|
|
1105
|
+
el("rect", {
|
|
1106
|
+
x: PAD.left + step * i + step * 0.15,
|
|
1107
|
+
y: y2,
|
|
1108
|
+
width,
|
|
1109
|
+
height: Math.max(1, scaleY(from, min, max) - y2),
|
|
1110
|
+
fill: seriesColor(s)
|
|
1111
|
+
})
|
|
1112
|
+
);
|
|
1113
|
+
});
|
|
1114
|
+
});
|
|
1115
|
+
}
|
|
1116
|
+
function drawLines(svg2, spec, min, max) {
|
|
1117
|
+
spec.series.forEach((series, s) => {
|
|
1118
|
+
const points = series.points.map((value, i) => `${bandCentre(i, spec.labels.length)},${scaleY(value, min, max)}`).join(" ");
|
|
1119
|
+
svg2.appendChild(
|
|
1120
|
+
el("polyline", {
|
|
1121
|
+
points,
|
|
1122
|
+
fill: "none",
|
|
1123
|
+
stroke: seriesColor(s),
|
|
1124
|
+
"stroke-width": 2,
|
|
1125
|
+
"stroke-linejoin": "round"
|
|
1126
|
+
})
|
|
1127
|
+
);
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
function drawScatter(svg2, spec, min, max) {
|
|
1131
|
+
spec.series.forEach((series, s) => {
|
|
1132
|
+
series.points.forEach((value, i) => {
|
|
1133
|
+
svg2.appendChild(
|
|
1134
|
+
el("circle", {
|
|
1135
|
+
cx: bandCentre(i, spec.labels.length),
|
|
1136
|
+
cy: scaleY(value, min, max),
|
|
1137
|
+
r: 4,
|
|
1138
|
+
fill: seriesColor(s),
|
|
1139
|
+
"fill-opacity": 0.85
|
|
1140
|
+
})
|
|
1141
|
+
);
|
|
1142
|
+
});
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
function drawPie(svg2, points) {
|
|
1146
|
+
const total = points.reduce((sum, value) => sum + value, 0);
|
|
1147
|
+
const cx = WIDTH / 2;
|
|
1148
|
+
const cy = PAD.top + PLOT_H / 2;
|
|
1149
|
+
const r = Math.min(PLOT_W, PLOT_H) / 2;
|
|
1150
|
+
if (total === 0) {
|
|
1151
|
+
svg2.appendChild(
|
|
1152
|
+
el("circle", { cx, cy, r, fill: "none", stroke: "currentColor", "stroke-opacity": 0.3 })
|
|
1153
|
+
);
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
let angle = -Math.PI / 2;
|
|
1157
|
+
points.forEach((value, i) => {
|
|
1158
|
+
const sweep = value / total * Math.PI * 2;
|
|
1159
|
+
const end = angle + sweep;
|
|
1160
|
+
if (sweep >= Math.PI * 2) {
|
|
1161
|
+
svg2.appendChild(el("circle", { cx, cy, r, fill: seriesColor(i) }));
|
|
1162
|
+
} else {
|
|
1163
|
+
const x1 = cx + r * Math.cos(angle);
|
|
1164
|
+
const y1 = cy + r * Math.sin(angle);
|
|
1165
|
+
const x2 = cx + r * Math.cos(end);
|
|
1166
|
+
const y2 = cy + r * Math.sin(end);
|
|
1167
|
+
const large = sweep > Math.PI ? 1 : 0;
|
|
1168
|
+
svg2.appendChild(
|
|
1169
|
+
el("path", {
|
|
1170
|
+
d: `M ${cx} ${cy} L ${x1} ${y1} A ${r} ${r} 0 ${large} 1 ${x2} ${y2} Z`,
|
|
1171
|
+
fill: seriesColor(i)
|
|
1172
|
+
})
|
|
1173
|
+
);
|
|
1174
|
+
}
|
|
1175
|
+
angle = end;
|
|
1176
|
+
});
|
|
1177
|
+
}
|
|
1178
|
+
function buildLegend(entries2) {
|
|
1179
|
+
if (entries2.length < 2) {
|
|
1180
|
+
return null;
|
|
1181
|
+
}
|
|
1182
|
+
const row = document.createElement("div");
|
|
1183
|
+
row.className = "chart-legend";
|
|
1184
|
+
row.setAttribute("part", "chart-legend");
|
|
1185
|
+
entries2.forEach((label, i) => {
|
|
1186
|
+
const item = document.createElement("span");
|
|
1187
|
+
item.className = "chart-legend-item";
|
|
1188
|
+
const swatch = document.createElement("span");
|
|
1189
|
+
swatch.className = "chart-legend-swatch";
|
|
1190
|
+
swatch.style.background = seriesColor(i);
|
|
1191
|
+
item.append(swatch, document.createTextNode(label));
|
|
1192
|
+
row.appendChild(item);
|
|
1193
|
+
});
|
|
1194
|
+
return row;
|
|
1195
|
+
}
|
|
1196
|
+
function renderChart(spec) {
|
|
1197
|
+
if (spec.labels.length === 0 || spec.series.length === 0) {
|
|
1198
|
+
return null;
|
|
1199
|
+
}
|
|
1200
|
+
const block = document.createElement("div");
|
|
1201
|
+
block.className = "chart-block";
|
|
1202
|
+
block.setAttribute("part", "chart-block");
|
|
1203
|
+
if (spec.title !== void 0 && spec.title !== "") {
|
|
1204
|
+
const heading = document.createElement("div");
|
|
1205
|
+
heading.className = "chart-title";
|
|
1206
|
+
heading.setAttribute("part", "chart-title");
|
|
1207
|
+
heading.textContent = spec.title;
|
|
1208
|
+
block.appendChild(heading);
|
|
1209
|
+
}
|
|
1210
|
+
const svg2 = el("svg", { viewBox: `0 0 ${WIDTH} ${HEIGHT}`, width: "100%", role: "img" });
|
|
1211
|
+
svg2.setAttribute("aria-label", spec.title ?? `${spec.kind} chart`);
|
|
1212
|
+
if (spec.kind === "pie") {
|
|
1213
|
+
const first = spec.series[0];
|
|
1214
|
+
drawPie(
|
|
1215
|
+
svg2,
|
|
1216
|
+
first.points.map((value) => Math.max(0, value))
|
|
1217
|
+
);
|
|
1218
|
+
} else {
|
|
1219
|
+
const { min, max } = extent(spec);
|
|
1220
|
+
drawAxes(svg2, spec, min, max);
|
|
1221
|
+
if (spec.kind === "bar") {
|
|
1222
|
+
drawBars(svg2, spec, min, max);
|
|
1223
|
+
} else if (spec.kind === "stacked") {
|
|
1224
|
+
drawStacked(svg2, spec, min, max);
|
|
1225
|
+
} else if (spec.kind === "line") {
|
|
1226
|
+
drawLines(svg2, spec, min, max);
|
|
1227
|
+
} else {
|
|
1228
|
+
drawScatter(svg2, spec, min, max);
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
block.appendChild(svg2);
|
|
1232
|
+
const legend = buildLegend(
|
|
1233
|
+
spec.kind === "pie" ? spec.labels : spec.series.map((series) => series.label)
|
|
1234
|
+
);
|
|
1235
|
+
if (legend !== null) {
|
|
1236
|
+
block.appendChild(legend);
|
|
1237
|
+
}
|
|
1238
|
+
return block;
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
// src/ui/chart_spec_from.ts
|
|
1242
|
+
var KINDS = ["bar", "line", "pie", "scatter", "stacked"];
|
|
1243
|
+
var MAX_POINTS = 2e4;
|
|
1244
|
+
var MAX_MAGNITUDE = 1e15;
|
|
1245
|
+
function asKind(value) {
|
|
1246
|
+
return KINDS.includes(value) ? value : "bar";
|
|
1247
|
+
}
|
|
1248
|
+
function asNumbers(value) {
|
|
1249
|
+
if (!Array.isArray(value)) {
|
|
1250
|
+
return null;
|
|
1251
|
+
}
|
|
1252
|
+
const out = [];
|
|
1253
|
+
for (const item of value) {
|
|
1254
|
+
if (typeof item !== "number" || !Number.isFinite(item)) {
|
|
1255
|
+
return null;
|
|
1256
|
+
}
|
|
1257
|
+
if (Math.abs(item) > MAX_MAGNITUDE) {
|
|
1258
|
+
return null;
|
|
1259
|
+
}
|
|
1260
|
+
out.push(item);
|
|
1261
|
+
}
|
|
1262
|
+
return out;
|
|
1263
|
+
}
|
|
1264
|
+
function asStrings(value) {
|
|
1265
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
|
|
1266
|
+
return null;
|
|
1267
|
+
}
|
|
1268
|
+
return value;
|
|
1269
|
+
}
|
|
1270
|
+
function chartSpecFrom(value) {
|
|
1271
|
+
if (typeof value !== "object" || value === null) {
|
|
1272
|
+
return null;
|
|
1273
|
+
}
|
|
1274
|
+
const raw = value;
|
|
1275
|
+
const labels = asStrings(raw["labels"]);
|
|
1276
|
+
if (labels === null || !Array.isArray(raw["series"])) {
|
|
1277
|
+
return null;
|
|
1278
|
+
}
|
|
1279
|
+
const series = [];
|
|
1280
|
+
for (const entry of raw["series"]) {
|
|
1281
|
+
if (typeof entry !== "object" || entry === null) {
|
|
1282
|
+
return null;
|
|
1283
|
+
}
|
|
1284
|
+
const item = entry;
|
|
1285
|
+
const points = asNumbers(item["points"]);
|
|
1286
|
+
if (points === null || points.length !== labels.length) {
|
|
1287
|
+
return null;
|
|
1288
|
+
}
|
|
1289
|
+
series.push({ label: typeof item["label"] === "string" ? item["label"] : "", points });
|
|
1290
|
+
}
|
|
1291
|
+
if (series.length === 0) {
|
|
1292
|
+
return null;
|
|
1293
|
+
}
|
|
1294
|
+
if (series.length * labels.length > MAX_POINTS) {
|
|
1295
|
+
return null;
|
|
1296
|
+
}
|
|
1297
|
+
const kind = asKind(raw["kind"]);
|
|
1298
|
+
const title = raw["title"];
|
|
1299
|
+
return typeof title === "string" ? { kind, title, labels, series } : { kind, labels, series };
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
// src/ui/chart_tool.ts
|
|
1303
|
+
var CHART_TOOL_NAME = "render_chart";
|
|
1304
|
+
function draw(args) {
|
|
1305
|
+
const spec = chartSpecFrom(args);
|
|
1306
|
+
return spec === null ? null : renderChart(spec);
|
|
1307
|
+
}
|
|
1308
|
+
var REJECTED = "chart not rendered: expected labels (strings) and series, each with one finite number per label";
|
|
1309
|
+
function createChartTool() {
|
|
1310
|
+
return {
|
|
1311
|
+
name: CHART_TOOL_NAME,
|
|
1312
|
+
description: "Show a chart in the conversation. Supply the data and the page draws it. Every series must have exactly one point per label.",
|
|
1313
|
+
parameters: {
|
|
1314
|
+
type: "object",
|
|
1315
|
+
properties: {
|
|
1316
|
+
kind: { type: "string", enum: ["bar", "line", "pie", "scatter", "stacked"] },
|
|
1317
|
+
title: { type: "string" },
|
|
1318
|
+
labels: { type: "array", items: { type: "string" } },
|
|
1319
|
+
series: {
|
|
1320
|
+
type: "array",
|
|
1321
|
+
items: {
|
|
1322
|
+
type: "object",
|
|
1323
|
+
properties: {
|
|
1324
|
+
label: { type: "string" },
|
|
1325
|
+
points: { type: "array", items: { type: "number" } }
|
|
1326
|
+
},
|
|
1327
|
+
required: ["points"]
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
},
|
|
1331
|
+
required: ["labels", "series"],
|
|
1332
|
+
"x-summary": "Draw a chart"
|
|
1333
|
+
},
|
|
1334
|
+
// Says what happened and nothing else; the drawing is `render`'s job. Told
|
|
1335
|
+
// plainly when the arguments are unusable, because the model can fix that
|
|
1336
|
+
// and retry — a silent no-op would leave it believing the chart is on screen.
|
|
1337
|
+
// Answers on what was actually drawn, not on what validated. A spec can
|
|
1338
|
+
// pass validation and still have nothing to show -- zero labels matches zero
|
|
1339
|
+
// points -- and reporting success then would leave the model believing a
|
|
1340
|
+
// chart is on screen.
|
|
1341
|
+
handler: (args) => draw(args) === null ? REJECTED : "chart rendered",
|
|
1342
|
+
render: draw
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
|
|
987
1346
|
// src/ui/relative_time.ts
|
|
988
1347
|
function relativeTime(timestamp, now = Date.now(), strings = DEFAULT_UI_STRINGS) {
|
|
989
1348
|
if (!Number.isFinite(timestamp)) {
|
|
@@ -1084,7 +1443,7 @@ var CheckpointMenu = class {
|
|
|
1084
1443
|
/** The panel's tabbable controls, in document order. */
|
|
1085
1444
|
#focusables() {
|
|
1086
1445
|
return Array.from(this.element.querySelectorAll("button, [tabindex]")).filter(
|
|
1087
|
-
(
|
|
1446
|
+
(el2) => !el2.hidden
|
|
1088
1447
|
);
|
|
1089
1448
|
}
|
|
1090
1449
|
/** Escape closes; Tab is trapped so focus cannot wander behind the dialog. */
|
|
@@ -1308,10 +1667,10 @@ function requestQuestion(host, request, options = {}) {
|
|
|
1308
1667
|
radio.name = group;
|
|
1309
1668
|
radio.value = choice;
|
|
1310
1669
|
radio.setAttribute("part", "question-radio");
|
|
1311
|
-
const
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
label.append(radio,
|
|
1670
|
+
const text3 = document.createElement("span");
|
|
1671
|
+
text3.setAttribute("part", "question-choice-text");
|
|
1672
|
+
text3.textContent = choice;
|
|
1673
|
+
label.append(radio, text3);
|
|
1315
1674
|
form.appendChild(label);
|
|
1316
1675
|
radios.push(radio);
|
|
1317
1676
|
}
|
|
@@ -1328,10 +1687,10 @@ function requestQuestion(host, request, options = {}) {
|
|
|
1328
1687
|
otherRadio.name = group;
|
|
1329
1688
|
otherRadio.value = "";
|
|
1330
1689
|
otherRadio.setAttribute("part", "question-radio");
|
|
1331
|
-
const
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
label.append(otherRadio,
|
|
1690
|
+
const text3 = document.createElement("span");
|
|
1691
|
+
text3.setAttribute("part", "question-choice-text");
|
|
1692
|
+
text3.textContent = strings.otherOption;
|
|
1693
|
+
label.append(otherRadio, text3);
|
|
1335
1694
|
form.appendChild(label);
|
|
1336
1695
|
input.disabled = true;
|
|
1337
1696
|
}
|
|
@@ -1652,7 +2011,7 @@ var svgFilters = freeze(["feBlend", "feColorMatrix", "feComponentTransfer", "feC
|
|
|
1652
2011
|
var svgDisallowed = freeze(["animate", "color-profile", "cursor", "discard", "font-face", "font-face-format", "font-face-name", "font-face-src", "font-face-uri", "foreignobject", "hatch", "hatchpath", "mesh", "meshgradient", "meshpatch", "meshrow", "missing-glyph", "script", "set", "solidcolor", "unknown", "use"]);
|
|
1653
2012
|
var mathMl$1 = freeze(["math", "menclose", "merror", "mfenced", "mfrac", "mglyph", "mi", "mlabeledtr", "mmultiscripts", "mn", "mo", "mover", "mpadded", "mphantom", "mroot", "mrow", "ms", "mspace", "msqrt", "mstyle", "msub", "msup", "msubsup", "mtable", "mtd", "mtext", "mtr", "munder", "munderover", "mprescripts"]);
|
|
1654
2013
|
var mathMlDisallowed = freeze(["maction", "maligngroup", "malignmark", "mlongdiv", "mscarries", "mscarry", "msgroup", "mstack", "msline", "msrow", "semantics", "annotation", "annotation-xml", "mprescripts", "none"]);
|
|
1655
|
-
var
|
|
2014
|
+
var text2 = freeze(["#text"]);
|
|
1656
2015
|
var html = freeze(["accept", "action", "align", "alt", "autocapitalize", "autocomplete", "autopictureinpicture", "autoplay", "background", "bgcolor", "border", "capture", "cellpadding", "cellspacing", "checked", "cite", "class", "clear", "color", "cols", "colspan", "command", "commandfor", "controls", "controlslist", "coords", "crossorigin", "datetime", "decoding", "default", "dir", "disabled", "disablepictureinpicture", "disableremoteplayback", "download", "draggable", "enctype", "enterkeyhint", "exportparts", "face", "for", "headers", "height", "hidden", "high", "href", "hreflang", "id", "inert", "inputmode", "integrity", "ismap", "kind", "label", "lang", "list", "loading", "loop", "low", "max", "maxlength", "media", "method", "min", "minlength", "multiple", "muted", "name", "nonce", "noshade", "novalidate", "nowrap", "open", "optimum", "part", "pattern", "placeholder", "playsinline", "popover", "popovertarget", "popovertargetaction", "poster", "preload", "pubdate", "radiogroup", "readonly", "rel", "required", "rev", "reversed", "role", "rows", "rowspan", "spellcheck", "scope", "selected", "shape", "size", "sizes", "slot", "span", "srclang", "start", "src", "srcset", "step", "style", "summary", "tabindex", "title", "translate", "type", "usemap", "valign", "value", "width", "wrap", "xmlns"]);
|
|
1657
2016
|
var svg = freeze(["accent-height", "accumulate", "additive", "alignment-baseline", "amplitude", "ascent", "attributename", "attributetype", "azimuth", "basefrequency", "baseline-shift", "begin", "bias", "by", "class", "clip", "clippathunits", "clip-path", "clip-rule", "color", "color-interpolation", "color-interpolation-filters", "color-profile", "color-rendering", "cx", "cy", "d", "dx", "dy", "diffuseconstant", "direction", "display", "divisor", "dominant-baseline", "dur", "edgemode", "elevation", "end", "exponent", "fill", "fill-opacity", "fill-rule", "filter", "filterunits", "flood-color", "flood-opacity", "font-family", "font-size", "font-size-adjust", "font-stretch", "font-style", "font-variant", "font-weight", "fx", "fy", "g1", "g2", "glyph-name", "glyphref", "gradientunits", "gradienttransform", "height", "href", "id", "image-rendering", "in", "in2", "intercept", "k", "k1", "k2", "k3", "k4", "kerning", "keypoints", "keysplines", "keytimes", "lang", "lengthadjust", "letter-spacing", "kernelmatrix", "kernelunitlength", "lighting-color", "local", "marker-end", "marker-mid", "marker-start", "markerheight", "markerunits", "markerwidth", "maskcontentunits", "maskunits", "max", "mask", "mask-type", "media", "method", "mode", "min", "name", "numoctaves", "offset", "operator", "opacity", "order", "orient", "orientation", "origin", "overflow", "paint-order", "path", "pathlength", "patterncontentunits", "patterntransform", "patternunits", "points", "preservealpha", "preserveaspectratio", "primitiveunits", "r", "rx", "ry", "radius", "refx", "refy", "repeatcount", "repeatdur", "restart", "result", "rotate", "scale", "seed", "shape-rendering", "slope", "specularconstant", "specularexponent", "spreadmethod", "startoffset", "stddeviation", "stitchtiles", "stop-color", "stop-opacity", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke", "stroke-width", "style", "surfacescale", "systemlanguage", "tabindex", "tablevalues", "targetx", "targety", "transform", "transform-origin", "text-anchor", "text-decoration", "text-orientation", "text-rendering", "textlength", "type", "u1", "u2", "unicode", "values", "viewbox", "visibility", "version", "vert-adv-y", "vert-origin-x", "vert-origin-y", "width", "word-spacing", "wrap", "writing-mode", "xchannelselector", "ychannelselector", "x", "x1", "x2", "xmlns", "y", "y1", "y2", "z", "zoomandpan"]);
|
|
1658
2017
|
var mathMl = freeze(["accent", "accentunder", "align", "bevelled", "close", "columnalign", "columnlines", "columnspacing", "columnspan", "denomalign", "depth", "dir", "display", "displaystyle", "encoding", "fence", "frame", "height", "href", "id", "largeop", "length", "linethickness", "lquote", "lspace", "mathbackground", "mathcolor", "mathsize", "mathvariant", "maxsize", "minsize", "movablelimits", "notation", "numalign", "open", "rowalign", "rowlines", "rowspacing", "rowspan", "rspace", "rquote", "scriptlevel", "scriptminsize", "scriptsizemultiplier", "selection", "separator", "separators", "stretchy", "subscriptshift", "supscriptshift", "symmetric", "voffset", "width", "xmlns"]);
|
|
@@ -1813,7 +2172,7 @@ function createDOMPurify() {
|
|
|
1813
2172
|
const MUSTACHE_EXPR$1 = MUSTACHE_EXPR, ERB_EXPR$1 = ERB_EXPR, TMPLIT_EXPR$1 = TMPLIT_EXPR, DATA_ATTR$1 = DATA_ATTR, ARIA_ATTR$1 = ARIA_ATTR, IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA, ATTR_WHITESPACE$1 = ATTR_WHITESPACE, CUSTOM_ELEMENT$1 = CUSTOM_ELEMENT;
|
|
1814
2173
|
let IS_ALLOWED_URI$1 = IS_ALLOWED_URI;
|
|
1815
2174
|
let ALLOWED_TAGS2 = null;
|
|
1816
|
-
const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...
|
|
2175
|
+
const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text2]);
|
|
1817
2176
|
let ALLOWED_ATTR2 = null;
|
|
1818
2177
|
const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);
|
|
1819
2178
|
let CUSTOM_ELEMENT_HANDLING = Object.seal(create(null, {
|
|
@@ -2012,7 +2371,7 @@ function createDOMPurify() {
|
|
|
2012
2371
|
RETURN_DOM = true;
|
|
2013
2372
|
}
|
|
2014
2373
|
if (USE_PROFILES) {
|
|
2015
|
-
ALLOWED_TAGS2 = addToSet({},
|
|
2374
|
+
ALLOWED_TAGS2 = addToSet({}, text2);
|
|
2016
2375
|
ALLOWED_ATTR2 = create(null);
|
|
2017
2376
|
if (USE_PROFILES.html === true) {
|
|
2018
2377
|
addToSet(ALLOWED_TAGS2, html$1);
|
|
@@ -4228,9 +4587,9 @@ var ALLOWED_TAGS = [
|
|
|
4228
4587
|
var ALLOWED_ATTR = ["href", "title", "class"];
|
|
4229
4588
|
var ALLOWED_TAGS_WITH_IMAGES = [...ALLOWED_TAGS, "img"];
|
|
4230
4589
|
var ALLOWED_ATTR_WITH_IMAGES = [...ALLOWED_ATTR, "src", "alt", "width", "height"];
|
|
4231
|
-
function renderMarkdown(
|
|
4590
|
+
function renderMarkdown(text3, options) {
|
|
4232
4591
|
const allowImages = options?.allowImages === true;
|
|
4233
|
-
const rendered = parser.parse(
|
|
4592
|
+
const rendered = parser.parse(text3, { async: false });
|
|
4234
4593
|
const clean = purify.sanitize(rendered, {
|
|
4235
4594
|
ALLOWED_TAGS: allowImages ? ALLOWED_TAGS_WITH_IMAGES : ALLOWED_TAGS,
|
|
4236
4595
|
ALLOWED_ATTR: allowImages ? ALLOWED_ATTR_WITH_IMAGES : ALLOWED_ATTR
|
|
@@ -4350,7 +4709,7 @@ function wrapWords(root) {
|
|
|
4350
4709
|
}
|
|
4351
4710
|
|
|
4352
4711
|
// src/ui/run_notice.ts
|
|
4353
|
-
function renderRunNotice(icon,
|
|
4712
|
+
function renderRunNotice(icon, text3, kind) {
|
|
4354
4713
|
const notice = document.createElement("div");
|
|
4355
4714
|
notice.className = `run-notice run-notice--${kind}`;
|
|
4356
4715
|
notice.setAttribute("part", `run-notice run-notice-${kind}`);
|
|
@@ -4363,7 +4722,7 @@ function renderRunNotice(icon, text2, kind) {
|
|
|
4363
4722
|
const label = document.createElement("span");
|
|
4364
4723
|
label.className = "run-notice-text";
|
|
4365
4724
|
label.setAttribute("part", "run-notice-text");
|
|
4366
|
-
label.textContent =
|
|
4725
|
+
label.textContent = text3;
|
|
4367
4726
|
notice.append(glyph, label);
|
|
4368
4727
|
return notice;
|
|
4369
4728
|
}
|
|
@@ -6048,6 +6407,50 @@ var STYLES = `
|
|
|
6048
6407
|
margin-top: 6px;
|
|
6049
6408
|
}
|
|
6050
6409
|
|
|
6410
|
+
/* Charts.
|
|
6411
|
+
*
|
|
6412
|
+
* The SVG scales to the column and carries no colours of its own beyond the
|
|
6413
|
+
* series palette, so a host restyles it the same way it restyles everything
|
|
6414
|
+
* else. Series colours are custom properties with fallbacks rather than fixed
|
|
6415
|
+
* values, and the axis furniture inherits currentColor at low opacity so it
|
|
6416
|
+
* reads correctly in either theme without a second palette.
|
|
6417
|
+
*/
|
|
6418
|
+
.chart-block {
|
|
6419
|
+
align-self: stretch;
|
|
6420
|
+
max-width: 100%;
|
|
6421
|
+
margin: 6px 0;
|
|
6422
|
+
color: var(--_fg);
|
|
6423
|
+
}
|
|
6424
|
+
|
|
6425
|
+
.chart-title {
|
|
6426
|
+
margin-bottom: 2px;
|
|
6427
|
+
font-size: 0.85em;
|
|
6428
|
+
font-weight: 600;
|
|
6429
|
+
opacity: 0.85;
|
|
6430
|
+
}
|
|
6431
|
+
|
|
6432
|
+
.chart-legend {
|
|
6433
|
+
display: flex;
|
|
6434
|
+
flex-wrap: wrap;
|
|
6435
|
+
gap: 4px 12px;
|
|
6436
|
+
margin-top: 4px;
|
|
6437
|
+
font-size: 0.78em;
|
|
6438
|
+
opacity: 0.75;
|
|
6439
|
+
}
|
|
6440
|
+
|
|
6441
|
+
.chart-legend-item {
|
|
6442
|
+
display: inline-flex;
|
|
6443
|
+
align-items: center;
|
|
6444
|
+
gap: 5px;
|
|
6445
|
+
}
|
|
6446
|
+
|
|
6447
|
+
.chart-legend-swatch {
|
|
6448
|
+
width: 9px;
|
|
6449
|
+
height: 9px;
|
|
6450
|
+
border-radius: 2px;
|
|
6451
|
+
flex: 0 0 auto;
|
|
6452
|
+
}
|
|
6453
|
+
|
|
6051
6454
|
.run-notice {
|
|
6052
6455
|
display: inline-flex;
|
|
6053
6456
|
align-items: center;
|
|
@@ -6969,7 +7372,7 @@ var ThreadDrawer = class {
|
|
|
6969
7372
|
}
|
|
6970
7373
|
const focusables = Array.from(
|
|
6971
7374
|
this.#panel.querySelectorAll("button, input, [tabindex]")
|
|
6972
|
-
).filter((
|
|
7375
|
+
).filter((el2) => !el2.hidden);
|
|
6973
7376
|
const first = focusables[0];
|
|
6974
7377
|
const last = focusables[focusables.length - 1];
|
|
6975
7378
|
const active = this.#activeElement();
|
|
@@ -7137,11 +7540,11 @@ function resultLabels(strings) {
|
|
|
7137
7540
|
[TOOL_CALL_STATUS.DECLINED]: strings.declinedLabel
|
|
7138
7541
|
};
|
|
7139
7542
|
}
|
|
7140
|
-
function formatPayload(
|
|
7543
|
+
function formatPayload(text3) {
|
|
7141
7544
|
try {
|
|
7142
|
-
return JSON.stringify(JSON.parse(
|
|
7545
|
+
return JSON.stringify(JSON.parse(text3), null, 2);
|
|
7143
7546
|
} catch {
|
|
7144
|
-
return
|
|
7547
|
+
return text3;
|
|
7145
7548
|
}
|
|
7146
7549
|
}
|
|
7147
7550
|
var ToolCallCard = class {
|
|
@@ -7253,7 +7656,7 @@ var ToolCallCard = class {
|
|
|
7253
7656
|
* Flip the status pill to `status` and fill in the result region, whose
|
|
7254
7657
|
* heading names the outcome (result / error / declined).
|
|
7255
7658
|
*/
|
|
7256
|
-
settle(status,
|
|
7659
|
+
settle(status, text3) {
|
|
7257
7660
|
if (this.#settled) {
|
|
7258
7661
|
return;
|
|
7259
7662
|
}
|
|
@@ -7261,7 +7664,7 @@ var ToolCallCard = class {
|
|
|
7261
7664
|
this.element.setAttribute("data-status", status);
|
|
7262
7665
|
this.#status.textContent = statusLabels(this.#strings)[status];
|
|
7263
7666
|
this.#resultLabel.textContent = resultLabels(this.#strings)[status];
|
|
7264
|
-
this.#resultBody.textContent = formatPayload(
|
|
7667
|
+
this.#resultBody.textContent = formatPayload(text3);
|
|
7265
7668
|
this.#resultSection.hidden = false;
|
|
7266
7669
|
}
|
|
7267
7670
|
/** Build one labelled region of the body: a heading plus a payload block. */
|
|
@@ -7374,10 +7777,10 @@ var VoiceInput = class {
|
|
|
7374
7777
|
this.#setState("transcribing");
|
|
7375
7778
|
const audio = new Blob(this.#chunks, { type: mimeType || "audio/webm" });
|
|
7376
7779
|
try {
|
|
7377
|
-
const
|
|
7780
|
+
const text3 = await this.#transcribe(audio);
|
|
7378
7781
|
this.#setState("idle");
|
|
7379
|
-
if (
|
|
7380
|
-
this.#onText(
|
|
7782
|
+
if (text3 !== "") {
|
|
7783
|
+
this.#onText(text3);
|
|
7381
7784
|
}
|
|
7382
7785
|
} catch (error) {
|
|
7383
7786
|
this.#fail(error instanceof Error ? error.message : this.#strings.transcriptionFailed);
|
|
@@ -7614,6 +8017,7 @@ var AgUiClient = class {
|
|
|
7614
8017
|
#buildSubscriber(pending, runState) {
|
|
7615
8018
|
const h = this.#handlers;
|
|
7616
8019
|
const closed = this.#closedMessageIds;
|
|
8020
|
+
const pendingDeltas = /* @__PURE__ */ new Set();
|
|
7617
8021
|
return {
|
|
7618
8022
|
onRunInitialized() {
|
|
7619
8023
|
h.onRunStart();
|
|
@@ -7644,8 +8048,39 @@ var AgUiClient = class {
|
|
|
7644
8048
|
onToolCallResultEvent({ event }) {
|
|
7645
8049
|
h.onToolResult(event.toolCallId, event.content);
|
|
7646
8050
|
},
|
|
7647
|
-
onActivitySnapshotEvent({ event }) {
|
|
7648
|
-
|
|
8051
|
+
onActivitySnapshotEvent({ event, messages }) {
|
|
8052
|
+
const known = messages.some(
|
|
8053
|
+
(message) => message.id === event.messageId && message.role === "activity"
|
|
8054
|
+
);
|
|
8055
|
+
if (known) {
|
|
8056
|
+
h.onActivityChanged(event.messageId, event.activityType, event.content);
|
|
8057
|
+
return;
|
|
8058
|
+
}
|
|
8059
|
+
h.onActivity(event.activityType, event.content, event.messageId);
|
|
8060
|
+
},
|
|
8061
|
+
// Deliberately does *not* read the message here. `@ag-ui/client`
|
|
8062
|
+
// dispatches this subscriber **before** applying the patch, so the
|
|
8063
|
+
// message still holds its previous content: redrawing from it would leave
|
|
8064
|
+
// the chart one revision behind for the life of the run, and disagreeing
|
|
8065
|
+
// with what a reload shows. Note which chart moved and read the result on
|
|
8066
|
+
// the change that follows.
|
|
8067
|
+
onActivityDeltaEvent({ event }) {
|
|
8068
|
+
pendingDeltas.add(event.messageId);
|
|
8069
|
+
},
|
|
8070
|
+
// Emitted after the client has written the patched messages, which is the
|
|
8071
|
+
// first moment the result exists. Only the ids marked above are looked at,
|
|
8072
|
+
// so an ordinary text delta does not walk the transcript.
|
|
8073
|
+
onMessagesChanged({ messages }) {
|
|
8074
|
+
if (pendingDeltas.size === 0) {
|
|
8075
|
+
return;
|
|
8076
|
+
}
|
|
8077
|
+
for (const id of pendingDeltas) {
|
|
8078
|
+
const message = messages.find((entry) => entry.id === id);
|
|
8079
|
+
if (message !== void 0 && message.role === "activity") {
|
|
8080
|
+
h.onActivityChanged(id, message.activityType, message.content);
|
|
8081
|
+
}
|
|
8082
|
+
}
|
|
8083
|
+
pendingDeltas.clear();
|
|
7649
8084
|
},
|
|
7650
8085
|
// `@ag-ui/client` maps the deprecated THINKING_* events onto these
|
|
7651
8086
|
// REASONING_* callbacks, so the reasoning family alone covers both
|
|
@@ -7857,9 +8292,9 @@ function isOwnedSuffix(suffix) {
|
|
|
7857
8292
|
function deriveTitle(messages) {
|
|
7858
8293
|
for (const message of messages) {
|
|
7859
8294
|
if (message.role === "user") {
|
|
7860
|
-
const
|
|
7861
|
-
if (
|
|
7862
|
-
return truncate(
|
|
8295
|
+
const text3 = cleanText(message.content);
|
|
8296
|
+
if (text3 !== "") {
|
|
8297
|
+
return truncate(text3, TITLE_LIMIT);
|
|
7863
8298
|
}
|
|
7864
8299
|
}
|
|
7865
8300
|
}
|
|
@@ -7867,9 +8302,9 @@ function deriveTitle(messages) {
|
|
|
7867
8302
|
}
|
|
7868
8303
|
function derivePreview(messages) {
|
|
7869
8304
|
for (const message of [...messages].reverse()) {
|
|
7870
|
-
const
|
|
7871
|
-
if (
|
|
7872
|
-
return truncate(
|
|
8305
|
+
const text3 = cleanText(message.content);
|
|
8306
|
+
if (text3 !== "") {
|
|
8307
|
+
return truncate(text3, PREVIEW_LIMIT);
|
|
7873
8308
|
}
|
|
7874
8309
|
}
|
|
7875
8310
|
return "";
|
|
@@ -7877,8 +8312,8 @@ function derivePreview(messages) {
|
|
|
7877
8312
|
function cleanText(content) {
|
|
7878
8313
|
return typeof content === "string" ? content.replace(/\s+/g, " ").trim() : "";
|
|
7879
8314
|
}
|
|
7880
|
-
function truncate(
|
|
7881
|
-
return
|
|
8315
|
+
function truncate(text3, limit) {
|
|
8316
|
+
return text3.length <= limit ? text3 : `${text3.slice(0, limit - 1).trimEnd()}\u2026`;
|
|
7882
8317
|
}
|
|
7883
8318
|
|
|
7884
8319
|
// src/core/create_http_agent.ts
|
|
@@ -8397,6 +8832,12 @@ var AgUiChat = class extends HTMLElement {
|
|
|
8397
8832
|
* (`TOOL_CALL_RESULT`), so the post-run executeTool sweep doesn't overwrite
|
|
8398
8833
|
* the real output with the generic "executed on the server" fallback.
|
|
8399
8834
|
*/
|
|
8835
|
+
/** Whether a server-pushed chart activity is drawn. Off unless asked for. */
|
|
8836
|
+
#chartActivity = false;
|
|
8837
|
+
/** Card elements by call id, so a rendering handler can find its own card. */
|
|
8838
|
+
#cardElements = /* @__PURE__ */ new Map();
|
|
8839
|
+
/** Chart blocks by activity message id, so an update redraws in place. */
|
|
8840
|
+
#activityBlocks = /* @__PURE__ */ new Map();
|
|
8400
8841
|
#serverSettled = /* @__PURE__ */ new Set();
|
|
8401
8842
|
/**
|
|
8402
8843
|
* Tool calls made during the current interaction, in the order they started,
|
|
@@ -9042,7 +9483,7 @@ var AgUiChat = class extends HTMLElement {
|
|
|
9042
9483
|
}
|
|
9043
9484
|
this.#voice = new VoiceInput({
|
|
9044
9485
|
transcribe,
|
|
9045
|
-
onText: (
|
|
9486
|
+
onText: (text3) => this.#insertVoiceText(text3),
|
|
9046
9487
|
strings: this.#strings
|
|
9047
9488
|
});
|
|
9048
9489
|
this.#voiceSlot.appendChild(this.#voice.element);
|
|
@@ -9059,9 +9500,9 @@ var AgUiChat = class extends HTMLElement {
|
|
|
9059
9500
|
});
|
|
9060
9501
|
}
|
|
9061
9502
|
/** Drop a voice transcript into the composer (appended to any typed text). */
|
|
9062
|
-
#insertVoiceText(
|
|
9503
|
+
#insertVoiceText(text3) {
|
|
9063
9504
|
const current = this.#input.value.trim();
|
|
9064
|
-
this.#input.value = current === "" ?
|
|
9505
|
+
this.#input.value = current === "" ? text3 : `${current} ${text3}`;
|
|
9065
9506
|
this.#onInput();
|
|
9066
9507
|
this.#input.focus();
|
|
9067
9508
|
}
|
|
@@ -9205,18 +9646,18 @@ var AgUiChat = class extends HTMLElement {
|
|
|
9205
9646
|
void this.sendMessage(`/${skill.name}`);
|
|
9206
9647
|
return;
|
|
9207
9648
|
}
|
|
9208
|
-
const { text:
|
|
9649
|
+
const { text: text3, missing } = fillTemplate(skill.prompt, this.skillContext());
|
|
9209
9650
|
if (missing.length > 0) {
|
|
9210
9651
|
this.#skillHint.textContent = this.#strings.skillNeeds.replace("{title}", skill.title).replace("{fields}", missing.join(", "));
|
|
9211
9652
|
this.#skillHint.hidden = false;
|
|
9212
|
-
this.#input.value =
|
|
9653
|
+
this.#input.value = text3;
|
|
9213
9654
|
this.#autoGrow();
|
|
9214
9655
|
this.#input.focus();
|
|
9215
|
-
this.#selectFirstPlaceholder(
|
|
9656
|
+
this.#selectFirstPlaceholder(text3);
|
|
9216
9657
|
return;
|
|
9217
9658
|
}
|
|
9218
9659
|
this.#skillHint.hidden = true;
|
|
9219
|
-
this.#input.value =
|
|
9660
|
+
this.#input.value = text3;
|
|
9220
9661
|
this.#autoGrow();
|
|
9221
9662
|
if (skill.sendImmediately === false) {
|
|
9222
9663
|
this.#input.focus();
|
|
@@ -9230,9 +9671,9 @@ var AgUiChat = class extends HTMLElement {
|
|
|
9230
9671
|
* Typing then replaces it, which is the shortest path from "this skill needs
|
|
9231
9672
|
* a topic" to a sendable prompt.
|
|
9232
9673
|
*/
|
|
9233
|
-
#selectFirstPlaceholder(
|
|
9234
|
-
const start =
|
|
9235
|
-
this.#input.setSelectionRange(start,
|
|
9674
|
+
#selectFirstPlaceholder(text3) {
|
|
9675
|
+
const start = text3.indexOf("{");
|
|
9676
|
+
this.#input.setSelectionRange(start, text3.indexOf("}", start) + 1);
|
|
9236
9677
|
}
|
|
9237
9678
|
/** Whether the widget is collapsed (reflected as the `collapsed` attribute). */
|
|
9238
9679
|
get collapsed() {
|
|
@@ -9488,6 +9929,8 @@ var AgUiChat = class extends HTMLElement {
|
|
|
9488
9929
|
this.#hidePending();
|
|
9489
9930
|
this.#toolCards.clear();
|
|
9490
9931
|
this.#serverSettled.clear();
|
|
9932
|
+
this.#cardElements.clear();
|
|
9933
|
+
this.#activityBlocks.clear();
|
|
9491
9934
|
this.#initialMessages = [];
|
|
9492
9935
|
this.#attachTray?.clear();
|
|
9493
9936
|
this.#messages.replaceChildren(this.#emptyWrap);
|
|
@@ -9599,11 +10042,11 @@ var AgUiChat = class extends HTMLElement {
|
|
|
9599
10042
|
* the full transcript — tool calls and their results — not just the prose.
|
|
9600
10043
|
*/
|
|
9601
10044
|
#renderHistoricMessage(message) {
|
|
9602
|
-
const
|
|
10045
|
+
const text3 = typeof message.content === "string" ? message.content : "";
|
|
9603
10046
|
if (message.role === MESSAGE_ROLE.USER) {
|
|
9604
10047
|
const attachments = messageAttachments(message);
|
|
9605
|
-
if (
|
|
9606
|
-
const bubble = this.appendMessage(MESSAGE_ROLE.USER,
|
|
10048
|
+
if (text3 !== "" || attachments.length > 0) {
|
|
10049
|
+
const bubble = this.appendMessage(MESSAGE_ROLE.USER, text3);
|
|
9607
10050
|
if (attachments.length > 0) {
|
|
9608
10051
|
bubble.appendChild(renderAttachmentChips(attachments));
|
|
9609
10052
|
}
|
|
@@ -9611,8 +10054,8 @@ var AgUiChat = class extends HTMLElement {
|
|
|
9611
10054
|
return;
|
|
9612
10055
|
}
|
|
9613
10056
|
if (message.role === MESSAGE_ROLE.ASSISTANT) {
|
|
9614
|
-
if (
|
|
9615
|
-
this.appendMessage(MESSAGE_ROLE.ASSISTANT,
|
|
10057
|
+
if (text3 !== "") {
|
|
10058
|
+
this.appendMessage(MESSAGE_ROLE.ASSISTANT, text3).classList.add("message--restored");
|
|
9616
10059
|
}
|
|
9617
10060
|
for (const call of restoredToolCalls(message.toolCalls)) {
|
|
9618
10061
|
const restored = {
|
|
@@ -9623,7 +10066,18 @@ var AgUiChat = class extends HTMLElement {
|
|
|
9623
10066
|
if (this.#noticeIfSkillLoad(restored)) {
|
|
9624
10067
|
continue;
|
|
9625
10068
|
}
|
|
9626
|
-
this.#cardFor(restored);
|
|
10069
|
+
this.#cardElements.set(restored.id, this.#cardFor(restored).element);
|
|
10070
|
+
const tool = this.#resolveTool(restored.name);
|
|
10071
|
+
if (tool !== null) {
|
|
10072
|
+
this.#renderToolOutput(tool, restored);
|
|
10073
|
+
}
|
|
10074
|
+
}
|
|
10075
|
+
return;
|
|
10076
|
+
}
|
|
10077
|
+
if (message.role === "activity") {
|
|
10078
|
+
const activity = message;
|
|
10079
|
+
if (activity.activityType === CHART_ACTIVITY_TYPE && this.#chartActivity) {
|
|
10080
|
+
this.#drawActivityChart(message.id, activity.content);
|
|
9627
10081
|
}
|
|
9628
10082
|
return;
|
|
9629
10083
|
}
|
|
@@ -10196,6 +10650,7 @@ var AgUiChat = class extends HTMLElement {
|
|
|
10196
10650
|
}
|
|
10197
10651
|
const card = this.#cardFor(call);
|
|
10198
10652
|
this.#toolCards.delete(call.id);
|
|
10653
|
+
this.#cardElements.set(call.id, card.element);
|
|
10199
10654
|
const tool = this.#resolveTool(call.name);
|
|
10200
10655
|
if (tool === null) {
|
|
10201
10656
|
if (!this.#serverSettled.has(call.id)) {
|
|
@@ -10237,7 +10692,8 @@ var AgUiChat = class extends HTMLElement {
|
|
|
10237
10692
|
this.conversationStore.saveCheckpoint(this.#threadId, { toolCallId: call.id });
|
|
10238
10693
|
}
|
|
10239
10694
|
try {
|
|
10240
|
-
const result = await tool.handler(call.args);
|
|
10695
|
+
const result = await tool.handler(call.args, call.id);
|
|
10696
|
+
this.#renderToolOutput(tool, call);
|
|
10241
10697
|
if (navigates) {
|
|
10242
10698
|
card.settle(TOOL_CALL_STATUS.DONE, this.#strings.navigating);
|
|
10243
10699
|
return { content: "", halt: true };
|
|
@@ -10356,7 +10812,13 @@ var AgUiChat = class extends HTMLElement {
|
|
|
10356
10812
|
this.#runTools.push({ id: call.id, name: call.name });
|
|
10357
10813
|
this.#cardFor(call);
|
|
10358
10814
|
},
|
|
10359
|
-
onActivity: (activityType, content) => {
|
|
10815
|
+
onActivity: (activityType, content, messageId) => {
|
|
10816
|
+
if (activityType === CHART_ACTIVITY_TYPE) {
|
|
10817
|
+
if (this.#chartActivity) {
|
|
10818
|
+
this.#drawActivityChart(messageId, content);
|
|
10819
|
+
}
|
|
10820
|
+
return;
|
|
10821
|
+
}
|
|
10360
10822
|
if (activityType !== COMPACTION_ACTIVITY_TYPE) {
|
|
10361
10823
|
return;
|
|
10362
10824
|
}
|
|
@@ -10379,6 +10841,11 @@ var AgUiChat = class extends HTMLElement {
|
|
|
10379
10841
|
this.#serverSettled.add(toolCallId);
|
|
10380
10842
|
this.#showPending();
|
|
10381
10843
|
},
|
|
10844
|
+
onActivityChanged: (messageId, activityType, content) => {
|
|
10845
|
+
if (activityType === CHART_ACTIVITY_TYPE && this.#chartActivity) {
|
|
10846
|
+
this.#drawActivityChart(messageId, content);
|
|
10847
|
+
}
|
|
10848
|
+
},
|
|
10382
10849
|
onRunEnd: () => {
|
|
10383
10850
|
this.#hidePending();
|
|
10384
10851
|
this.#streamingBubble = null;
|
|
@@ -10526,8 +10993,84 @@ var AgUiChat = class extends HTMLElement {
|
|
|
10526
10993
|
this.#appendNotice("\u2728", this.#strings.usingSkill.replace("{name}", skill), "skill");
|
|
10527
10994
|
return true;
|
|
10528
10995
|
}
|
|
10529
|
-
#appendNotice(icon,
|
|
10530
|
-
this.#ensureGroup().appendChild(renderRunNotice(icon,
|
|
10996
|
+
#appendNotice(icon, text3, kind) {
|
|
10997
|
+
this.#ensureGroup().appendChild(renderRunNotice(icon, text3, kind));
|
|
10998
|
+
this.#updateEmptyState();
|
|
10999
|
+
this.#messages.scrollTop = this.#messages.scrollHeight;
|
|
11000
|
+
}
|
|
11001
|
+
/**
|
|
11002
|
+
* Turn on chart rendering, by whichever route this consumer wants.
|
|
11003
|
+
*
|
|
11004
|
+
* Both routes converge on one renderer deliberately. Built apart they become
|
|
11005
|
+
* two chart implementations with two sets of bugs, and the choice between them
|
|
11006
|
+
* is about *where the data lives* rather than how a bar should look:
|
|
11007
|
+
*
|
|
11008
|
+
* - `"tool"` registers the built-in `render_chart`. The agent decides a chart
|
|
11009
|
+
* helps and calls it, so the numbers are in its context and it can discuss
|
|
11010
|
+
* them afterwards. Costs one model round, and works over any transport.
|
|
11011
|
+
* - `"activity"` draws a server-pushed `ACTIVITY_SNAPSHOT` of type `chart`.
|
|
11012
|
+
* No round trip, and the data never enters the model's context at all —
|
|
11013
|
+
* which is what makes it the one for a large or sensitive dataset. Only this
|
|
11014
|
+
* route can update a chart in place as the server computes.
|
|
11015
|
+
*
|
|
11016
|
+
* Off unless asked for, both of them: a component that renders whatever
|
|
11017
|
+
* arrives is not something to switch on for everybody.
|
|
11018
|
+
*/
|
|
11019
|
+
enableCharts(routes = ["tool", "activity"]) {
|
|
11020
|
+
if (routes.includes("activity")) {
|
|
11021
|
+
this.#chartActivity = true;
|
|
11022
|
+
}
|
|
11023
|
+
if (routes.includes("tool")) {
|
|
11024
|
+
this.registerTool(createChartTool());
|
|
11025
|
+
}
|
|
11026
|
+
}
|
|
11027
|
+
/**
|
|
11028
|
+
* Place a tool's rendered node against its own card.
|
|
11029
|
+
*
|
|
11030
|
+
* Anchored rather than appended because a client tool's handler does not run
|
|
11031
|
+
* until the round is over: appending would put the node after everything the
|
|
11032
|
+
* model said next, visibly detached from the call that produced it, and in a
|
|
11033
|
+
* different order than the same transcript takes on reload. The card was
|
|
11034
|
+
* created inline, in the right place, so anchoring makes *when* the handler
|
|
11035
|
+
* runs stop mattering.
|
|
11036
|
+
*/
|
|
11037
|
+
#renderToolOutput(tool, call) {
|
|
11038
|
+
if (tool.render === void 0) {
|
|
11039
|
+
return;
|
|
11040
|
+
}
|
|
11041
|
+
let node;
|
|
11042
|
+
try {
|
|
11043
|
+
node = tool.render(call.args);
|
|
11044
|
+
} catch (error) {
|
|
11045
|
+
console.warn(`ag-ui-chat: render failed for tool ${call.name}`, error);
|
|
11046
|
+
return;
|
|
11047
|
+
}
|
|
11048
|
+
if (node === null) {
|
|
11049
|
+
return;
|
|
11050
|
+
}
|
|
11051
|
+
this.#cardElements.get(call.id)?.after(node);
|
|
11052
|
+
this.#afterTranscriptGrew();
|
|
11053
|
+
}
|
|
11054
|
+
/** Draw, or redraw in place, the chart for one activity message. */
|
|
11055
|
+
#drawActivityChart(messageId, content) {
|
|
11056
|
+
const spec = chartSpecFrom(content);
|
|
11057
|
+
if (spec === null) {
|
|
11058
|
+
return;
|
|
11059
|
+
}
|
|
11060
|
+
const block = renderChart(spec);
|
|
11061
|
+
if (block === null) {
|
|
11062
|
+
return;
|
|
11063
|
+
}
|
|
11064
|
+
const existing = this.#activityBlocks.get(messageId);
|
|
11065
|
+
if (existing === void 0) {
|
|
11066
|
+
this.#ensureGroup().appendChild(block);
|
|
11067
|
+
} else {
|
|
11068
|
+
existing.replaceWith(block);
|
|
11069
|
+
}
|
|
11070
|
+
this.#activityBlocks.set(messageId, block);
|
|
11071
|
+
this.#afterTranscriptGrew();
|
|
11072
|
+
}
|
|
11073
|
+
#afterTranscriptGrew() {
|
|
10531
11074
|
this.#updateEmptyState();
|
|
10532
11075
|
this.#messages.scrollTop = this.#messages.scrollHeight;
|
|
10533
11076
|
}
|
|
@@ -10580,43 +11123,45 @@ function defineAgUiChat() {
|
|
|
10580
11123
|
}
|
|
10581
11124
|
|
|
10582
11125
|
// src/dom/dom_driver.ts
|
|
10583
|
-
async function fillField(
|
|
10584
|
-
await scrollIntoCenterView(
|
|
10585
|
-
await focusWithFlash(
|
|
10586
|
-
await typeInto(
|
|
10587
|
-
}
|
|
10588
|
-
async function clickElement(
|
|
10589
|
-
await scrollIntoCenterView(
|
|
10590
|
-
await highlightThenClick(
|
|
10591
|
-
}
|
|
10592
|
-
async function pressButton(
|
|
10593
|
-
await scrollIntoCenterView(
|
|
10594
|
-
await pressThenClick(
|
|
10595
|
-
}
|
|
10596
|
-
async function selectControl(
|
|
10597
|
-
await scrollIntoCenterView(
|
|
10598
|
-
await selectOption(
|
|
10599
|
-
}
|
|
10600
|
-
async function toggleCheckbox(
|
|
10601
|
-
await scrollIntoCenterView(
|
|
10602
|
-
await toggleControl(
|
|
10603
|
-
}
|
|
10604
|
-
function setControlValue(
|
|
10605
|
-
if (
|
|
10606
|
-
setNativeChecked(
|
|
11126
|
+
async function fillField(el2, value, options = {}) {
|
|
11127
|
+
await scrollIntoCenterView(el2);
|
|
11128
|
+
await focusWithFlash(el2, { ...options, flashMs: options.flashMs ?? 0 });
|
|
11129
|
+
await typeInto(el2, value, options);
|
|
11130
|
+
}
|
|
11131
|
+
async function clickElement(el2, options = {}) {
|
|
11132
|
+
await scrollIntoCenterView(el2);
|
|
11133
|
+
await highlightThenClick(el2, options);
|
|
11134
|
+
}
|
|
11135
|
+
async function pressButton(el2, options = {}) {
|
|
11136
|
+
await scrollIntoCenterView(el2);
|
|
11137
|
+
await pressThenClick(el2, options);
|
|
11138
|
+
}
|
|
11139
|
+
async function selectControl(el2, value, options = {}) {
|
|
11140
|
+
await scrollIntoCenterView(el2);
|
|
11141
|
+
await selectOption(el2, value, options);
|
|
11142
|
+
}
|
|
11143
|
+
async function toggleCheckbox(el2, checked, options = {}) {
|
|
11144
|
+
await scrollIntoCenterView(el2);
|
|
11145
|
+
await toggleControl(el2, checked, options);
|
|
11146
|
+
}
|
|
11147
|
+
function setControlValue(el2, value) {
|
|
11148
|
+
if (el2 instanceof HTMLInputElement && el2.type === "checkbox") {
|
|
11149
|
+
setNativeChecked(el2, Boolean(value));
|
|
10607
11150
|
} else {
|
|
10608
|
-
setNativeValue(
|
|
11151
|
+
setNativeValue(el2, String(value));
|
|
10609
11152
|
}
|
|
10610
|
-
|
|
10611
|
-
|
|
11153
|
+
el2.dispatchEvent(new Event("input", { bubbles: true }));
|
|
11154
|
+
el2.dispatchEvent(new Event("change", { bubbles: true }));
|
|
10612
11155
|
}
|
|
10613
11156
|
|
|
10614
11157
|
// src/version.ts
|
|
10615
|
-
var VERSION = "0.
|
|
11158
|
+
var VERSION = "0.26.0";
|
|
10616
11159
|
export {
|
|
10617
11160
|
ATTACHMENT_EVENT,
|
|
10618
11161
|
AgUiChat,
|
|
10619
11162
|
AgUiClient,
|
|
11163
|
+
CHART_ACTIVITY_TYPE,
|
|
11164
|
+
CHART_TOOL_NAME,
|
|
10620
11165
|
COMPACTION_ACTIVITY_TYPE,
|
|
10621
11166
|
CheckpointMenu,
|
|
10622
11167
|
ClientToolRegistry,
|
|
@@ -10643,6 +11188,7 @@ export {
|
|
|
10643
11188
|
X_DESTRUCTIVE_KEY,
|
|
10644
11189
|
X_NAVIGATES_KEY,
|
|
10645
11190
|
X_SUMMARY_KEY,
|
|
11191
|
+
chartSpecFrom,
|
|
10646
11192
|
clickElement,
|
|
10647
11193
|
createHttpAgent,
|
|
10648
11194
|
createPageActionTools,
|
|
@@ -10664,6 +11210,7 @@ export {
|
|
|
10664
11210
|
pressButton,
|
|
10665
11211
|
pressThenClick,
|
|
10666
11212
|
prettifyToolName,
|
|
11213
|
+
renderChart,
|
|
10667
11214
|
renderMarkdown,
|
|
10668
11215
|
requestApproval,
|
|
10669
11216
|
requestConfirmation,
|