@artooi/ag-ui-web-component 0.25.2 → 0.26.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +92 -1
- package/README.md +89 -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 +726 -165
- package/dist/index.js.map +4 -4
- package/dist/tools/client_tool_registry.d.ts +33 -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 +165 -3
- package/src/core/agui_client.ts +51 -3
- package/src/index.ts +8 -0
- package/src/tools/client_tool_registry.ts +34 -1
- package/src/ui/chart_block.ts +354 -0
- package/src/ui/chart_spec_from.ts +125 -0
- package/src/ui/chart_tool.ts +72 -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,376 @@ 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] ?? 0;
|
|
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_LABELS = 2e3;
|
|
1245
|
+
var MAX_MAGNITUDE = 1e15;
|
|
1246
|
+
function asKind(value) {
|
|
1247
|
+
return KINDS.includes(value) ? value : "bar";
|
|
1248
|
+
}
|
|
1249
|
+
function asNumbers(value) {
|
|
1250
|
+
if (!Array.isArray(value)) {
|
|
1251
|
+
return null;
|
|
1252
|
+
}
|
|
1253
|
+
const out = [];
|
|
1254
|
+
for (const item of value) {
|
|
1255
|
+
if (typeof item !== "number" || !Number.isFinite(item)) {
|
|
1256
|
+
return null;
|
|
1257
|
+
}
|
|
1258
|
+
if (Math.abs(item) > MAX_MAGNITUDE) {
|
|
1259
|
+
return null;
|
|
1260
|
+
}
|
|
1261
|
+
out.push(item);
|
|
1262
|
+
}
|
|
1263
|
+
return out;
|
|
1264
|
+
}
|
|
1265
|
+
function asStrings(value) {
|
|
1266
|
+
if (!Array.isArray(value)) {
|
|
1267
|
+
return null;
|
|
1268
|
+
}
|
|
1269
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
1270
|
+
if (typeof value[i] !== "string") {
|
|
1271
|
+
return null;
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
return value;
|
|
1275
|
+
}
|
|
1276
|
+
function chartSpecFrom(value) {
|
|
1277
|
+
if (typeof value !== "object" || value === null) {
|
|
1278
|
+
return null;
|
|
1279
|
+
}
|
|
1280
|
+
const raw = value;
|
|
1281
|
+
const labels = asStrings(raw["labels"]);
|
|
1282
|
+
if (labels === null || !Array.isArray(raw["series"])) {
|
|
1283
|
+
return null;
|
|
1284
|
+
}
|
|
1285
|
+
const series = [];
|
|
1286
|
+
for (const entry of raw["series"]) {
|
|
1287
|
+
if (typeof entry !== "object" || entry === null) {
|
|
1288
|
+
return null;
|
|
1289
|
+
}
|
|
1290
|
+
const item = entry;
|
|
1291
|
+
const points = asNumbers(item["points"]);
|
|
1292
|
+
if (points === null || points.length !== labels.length) {
|
|
1293
|
+
return null;
|
|
1294
|
+
}
|
|
1295
|
+
series.push({ label: typeof item["label"] === "string" ? item["label"] : "", points });
|
|
1296
|
+
}
|
|
1297
|
+
if (series.length === 0) {
|
|
1298
|
+
return null;
|
|
1299
|
+
}
|
|
1300
|
+
if (series.length * labels.length > MAX_POINTS || labels.length > MAX_LABELS) {
|
|
1301
|
+
return null;
|
|
1302
|
+
}
|
|
1303
|
+
const kind = asKind(raw["kind"]);
|
|
1304
|
+
const title = raw["title"];
|
|
1305
|
+
return typeof title === "string" ? { kind, title, labels, series } : { kind, labels, series };
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
// src/ui/chart_tool.ts
|
|
1309
|
+
var CHART_TOOL_NAME = "render_chart";
|
|
1310
|
+
function draw(args) {
|
|
1311
|
+
const spec = chartSpecFrom(args);
|
|
1312
|
+
return spec === null ? null : renderChart(spec);
|
|
1313
|
+
}
|
|
1314
|
+
function drawable(args) {
|
|
1315
|
+
const spec = chartSpecFrom(args);
|
|
1316
|
+
return spec !== null && spec.labels.length > 0 && spec.series.length > 0;
|
|
1317
|
+
}
|
|
1318
|
+
var REJECTED = "chart not rendered: expected labels (strings) and series, each with one finite number per label";
|
|
1319
|
+
function createChartTool() {
|
|
1320
|
+
return {
|
|
1321
|
+
name: CHART_TOOL_NAME,
|
|
1322
|
+
description: "Show a chart in the conversation. Supply the data and the page draws it. Every series must have exactly one point per label.",
|
|
1323
|
+
parameters: {
|
|
1324
|
+
type: "object",
|
|
1325
|
+
properties: {
|
|
1326
|
+
kind: { type: "string", enum: ["bar", "line", "pie", "scatter", "stacked"] },
|
|
1327
|
+
title: { type: "string" },
|
|
1328
|
+
labels: { type: "array", items: { type: "string" } },
|
|
1329
|
+
series: {
|
|
1330
|
+
type: "array",
|
|
1331
|
+
items: {
|
|
1332
|
+
type: "object",
|
|
1333
|
+
properties: {
|
|
1334
|
+
label: { type: "string" },
|
|
1335
|
+
points: { type: "array", items: { type: "number" } }
|
|
1336
|
+
},
|
|
1337
|
+
required: ["points"]
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
},
|
|
1341
|
+
required: ["labels", "series"],
|
|
1342
|
+
"x-summary": "Draw a chart"
|
|
1343
|
+
},
|
|
1344
|
+
// Says what happened and nothing else; the drawing is `render`'s job. Told
|
|
1345
|
+
// plainly when the arguments are unusable, because the model can fix that
|
|
1346
|
+
// and retry — a silent no-op would leave it believing the chart is on screen.
|
|
1347
|
+
// Answers on what will actually be drawn, not on what validated: a spec can
|
|
1348
|
+
// pass validation and still have nothing to show, and reporting success
|
|
1349
|
+
// then would leave the model believing a chart is on screen. Asks the
|
|
1350
|
+
// question without building the chart, because `render` is about to build
|
|
1351
|
+
// the same one a moment later and drawing it twice is pure waste on a spec
|
|
1352
|
+
// large enough to matter.
|
|
1353
|
+
handler: (args) => drawable(args) ? "chart rendered" : REJECTED,
|
|
1354
|
+
render: draw
|
|
1355
|
+
};
|
|
1356
|
+
}
|
|
1357
|
+
|
|
987
1358
|
// src/ui/relative_time.ts
|
|
988
1359
|
function relativeTime(timestamp, now = Date.now(), strings = DEFAULT_UI_STRINGS) {
|
|
989
1360
|
if (!Number.isFinite(timestamp)) {
|
|
@@ -1084,7 +1455,7 @@ var CheckpointMenu = class {
|
|
|
1084
1455
|
/** The panel's tabbable controls, in document order. */
|
|
1085
1456
|
#focusables() {
|
|
1086
1457
|
return Array.from(this.element.querySelectorAll("button, [tabindex]")).filter(
|
|
1087
|
-
(
|
|
1458
|
+
(el2) => !el2.hidden
|
|
1088
1459
|
);
|
|
1089
1460
|
}
|
|
1090
1461
|
/** Escape closes; Tab is trapped so focus cannot wander behind the dialog. */
|
|
@@ -1308,10 +1679,10 @@ function requestQuestion(host, request, options = {}) {
|
|
|
1308
1679
|
radio.name = group;
|
|
1309
1680
|
radio.value = choice;
|
|
1310
1681
|
radio.setAttribute("part", "question-radio");
|
|
1311
|
-
const
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
label.append(radio,
|
|
1682
|
+
const text3 = document.createElement("span");
|
|
1683
|
+
text3.setAttribute("part", "question-choice-text");
|
|
1684
|
+
text3.textContent = choice;
|
|
1685
|
+
label.append(radio, text3);
|
|
1315
1686
|
form.appendChild(label);
|
|
1316
1687
|
radios.push(radio);
|
|
1317
1688
|
}
|
|
@@ -1328,10 +1699,10 @@ function requestQuestion(host, request, options = {}) {
|
|
|
1328
1699
|
otherRadio.name = group;
|
|
1329
1700
|
otherRadio.value = "";
|
|
1330
1701
|
otherRadio.setAttribute("part", "question-radio");
|
|
1331
|
-
const
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
label.append(otherRadio,
|
|
1702
|
+
const text3 = document.createElement("span");
|
|
1703
|
+
text3.setAttribute("part", "question-choice-text");
|
|
1704
|
+
text3.textContent = strings.otherOption;
|
|
1705
|
+
label.append(otherRadio, text3);
|
|
1335
1706
|
form.appendChild(label);
|
|
1336
1707
|
input.disabled = true;
|
|
1337
1708
|
}
|
|
@@ -1652,7 +2023,7 @@ var svgFilters = freeze(["feBlend", "feColorMatrix", "feComponentTransfer", "feC
|
|
|
1652
2023
|
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
2024
|
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
2025
|
var mathMlDisallowed = freeze(["maction", "maligngroup", "malignmark", "mlongdiv", "mscarries", "mscarry", "msgroup", "mstack", "msline", "msrow", "semantics", "annotation", "annotation-xml", "mprescripts", "none"]);
|
|
1655
|
-
var
|
|
2026
|
+
var text2 = freeze(["#text"]);
|
|
1656
2027
|
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
2028
|
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
2029
|
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 +2184,7 @@ function createDOMPurify() {
|
|
|
1813
2184
|
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
2185
|
let IS_ALLOWED_URI$1 = IS_ALLOWED_URI;
|
|
1815
2186
|
let ALLOWED_TAGS2 = null;
|
|
1816
|
-
const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...
|
|
2187
|
+
const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text2]);
|
|
1817
2188
|
let ALLOWED_ATTR2 = null;
|
|
1818
2189
|
const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);
|
|
1819
2190
|
let CUSTOM_ELEMENT_HANDLING = Object.seal(create(null, {
|
|
@@ -2012,7 +2383,7 @@ function createDOMPurify() {
|
|
|
2012
2383
|
RETURN_DOM = true;
|
|
2013
2384
|
}
|
|
2014
2385
|
if (USE_PROFILES) {
|
|
2015
|
-
ALLOWED_TAGS2 = addToSet({},
|
|
2386
|
+
ALLOWED_TAGS2 = addToSet({}, text2);
|
|
2016
2387
|
ALLOWED_ATTR2 = create(null);
|
|
2017
2388
|
if (USE_PROFILES.html === true) {
|
|
2018
2389
|
addToSet(ALLOWED_TAGS2, html$1);
|
|
@@ -4228,9 +4599,9 @@ var ALLOWED_TAGS = [
|
|
|
4228
4599
|
var ALLOWED_ATTR = ["href", "title", "class"];
|
|
4229
4600
|
var ALLOWED_TAGS_WITH_IMAGES = [...ALLOWED_TAGS, "img"];
|
|
4230
4601
|
var ALLOWED_ATTR_WITH_IMAGES = [...ALLOWED_ATTR, "src", "alt", "width", "height"];
|
|
4231
|
-
function renderMarkdown(
|
|
4602
|
+
function renderMarkdown(text3, options) {
|
|
4232
4603
|
const allowImages = options?.allowImages === true;
|
|
4233
|
-
const rendered = parser.parse(
|
|
4604
|
+
const rendered = parser.parse(text3, { async: false });
|
|
4234
4605
|
const clean = purify.sanitize(rendered, {
|
|
4235
4606
|
ALLOWED_TAGS: allowImages ? ALLOWED_TAGS_WITH_IMAGES : ALLOWED_TAGS,
|
|
4236
4607
|
ALLOWED_ATTR: allowImages ? ALLOWED_ATTR_WITH_IMAGES : ALLOWED_ATTR
|
|
@@ -4350,7 +4721,7 @@ function wrapWords(root) {
|
|
|
4350
4721
|
}
|
|
4351
4722
|
|
|
4352
4723
|
// src/ui/run_notice.ts
|
|
4353
|
-
function renderRunNotice(icon,
|
|
4724
|
+
function renderRunNotice(icon, text3, kind) {
|
|
4354
4725
|
const notice = document.createElement("div");
|
|
4355
4726
|
notice.className = `run-notice run-notice--${kind}`;
|
|
4356
4727
|
notice.setAttribute("part", `run-notice run-notice-${kind}`);
|
|
@@ -4363,7 +4734,7 @@ function renderRunNotice(icon, text2, kind) {
|
|
|
4363
4734
|
const label = document.createElement("span");
|
|
4364
4735
|
label.className = "run-notice-text";
|
|
4365
4736
|
label.setAttribute("part", "run-notice-text");
|
|
4366
|
-
label.textContent =
|
|
4737
|
+
label.textContent = text3;
|
|
4367
4738
|
notice.append(glyph, label);
|
|
4368
4739
|
return notice;
|
|
4369
4740
|
}
|
|
@@ -6048,6 +6419,50 @@ var STYLES = `
|
|
|
6048
6419
|
margin-top: 6px;
|
|
6049
6420
|
}
|
|
6050
6421
|
|
|
6422
|
+
/* Charts.
|
|
6423
|
+
*
|
|
6424
|
+
* The SVG scales to the column and carries no colours of its own beyond the
|
|
6425
|
+
* series palette, so a host restyles it the same way it restyles everything
|
|
6426
|
+
* else. Series colours are custom properties with fallbacks rather than fixed
|
|
6427
|
+
* values, and the axis furniture inherits currentColor at low opacity so it
|
|
6428
|
+
* reads correctly in either theme without a second palette.
|
|
6429
|
+
*/
|
|
6430
|
+
.chart-block {
|
|
6431
|
+
align-self: stretch;
|
|
6432
|
+
max-width: 100%;
|
|
6433
|
+
margin: 6px 0;
|
|
6434
|
+
color: var(--_fg);
|
|
6435
|
+
}
|
|
6436
|
+
|
|
6437
|
+
.chart-title {
|
|
6438
|
+
margin-bottom: 2px;
|
|
6439
|
+
font-size: 0.85em;
|
|
6440
|
+
font-weight: 600;
|
|
6441
|
+
opacity: 0.85;
|
|
6442
|
+
}
|
|
6443
|
+
|
|
6444
|
+
.chart-legend {
|
|
6445
|
+
display: flex;
|
|
6446
|
+
flex-wrap: wrap;
|
|
6447
|
+
gap: 4px 12px;
|
|
6448
|
+
margin-top: 4px;
|
|
6449
|
+
font-size: 0.78em;
|
|
6450
|
+
opacity: 0.75;
|
|
6451
|
+
}
|
|
6452
|
+
|
|
6453
|
+
.chart-legend-item {
|
|
6454
|
+
display: inline-flex;
|
|
6455
|
+
align-items: center;
|
|
6456
|
+
gap: 5px;
|
|
6457
|
+
}
|
|
6458
|
+
|
|
6459
|
+
.chart-legend-swatch {
|
|
6460
|
+
width: 9px;
|
|
6461
|
+
height: 9px;
|
|
6462
|
+
border-radius: 2px;
|
|
6463
|
+
flex: 0 0 auto;
|
|
6464
|
+
}
|
|
6465
|
+
|
|
6051
6466
|
.run-notice {
|
|
6052
6467
|
display: inline-flex;
|
|
6053
6468
|
align-items: center;
|
|
@@ -6969,7 +7384,7 @@ var ThreadDrawer = class {
|
|
|
6969
7384
|
}
|
|
6970
7385
|
const focusables = Array.from(
|
|
6971
7386
|
this.#panel.querySelectorAll("button, input, [tabindex]")
|
|
6972
|
-
).filter((
|
|
7387
|
+
).filter((el2) => !el2.hidden);
|
|
6973
7388
|
const first = focusables[0];
|
|
6974
7389
|
const last = focusables[focusables.length - 1];
|
|
6975
7390
|
const active = this.#activeElement();
|
|
@@ -7137,11 +7552,11 @@ function resultLabels(strings) {
|
|
|
7137
7552
|
[TOOL_CALL_STATUS.DECLINED]: strings.declinedLabel
|
|
7138
7553
|
};
|
|
7139
7554
|
}
|
|
7140
|
-
function formatPayload(
|
|
7555
|
+
function formatPayload(text3) {
|
|
7141
7556
|
try {
|
|
7142
|
-
return JSON.stringify(JSON.parse(
|
|
7557
|
+
return JSON.stringify(JSON.parse(text3), null, 2);
|
|
7143
7558
|
} catch {
|
|
7144
|
-
return
|
|
7559
|
+
return text3;
|
|
7145
7560
|
}
|
|
7146
7561
|
}
|
|
7147
7562
|
var ToolCallCard = class {
|
|
@@ -7253,7 +7668,7 @@ var ToolCallCard = class {
|
|
|
7253
7668
|
* Flip the status pill to `status` and fill in the result region, whose
|
|
7254
7669
|
* heading names the outcome (result / error / declined).
|
|
7255
7670
|
*/
|
|
7256
|
-
settle(status,
|
|
7671
|
+
settle(status, text3) {
|
|
7257
7672
|
if (this.#settled) {
|
|
7258
7673
|
return;
|
|
7259
7674
|
}
|
|
@@ -7261,7 +7676,7 @@ var ToolCallCard = class {
|
|
|
7261
7676
|
this.element.setAttribute("data-status", status);
|
|
7262
7677
|
this.#status.textContent = statusLabels(this.#strings)[status];
|
|
7263
7678
|
this.#resultLabel.textContent = resultLabels(this.#strings)[status];
|
|
7264
|
-
this.#resultBody.textContent = formatPayload(
|
|
7679
|
+
this.#resultBody.textContent = formatPayload(text3);
|
|
7265
7680
|
this.#resultSection.hidden = false;
|
|
7266
7681
|
}
|
|
7267
7682
|
/** Build one labelled region of the body: a heading plus a payload block. */
|
|
@@ -7374,10 +7789,10 @@ var VoiceInput = class {
|
|
|
7374
7789
|
this.#setState("transcribing");
|
|
7375
7790
|
const audio = new Blob(this.#chunks, { type: mimeType || "audio/webm" });
|
|
7376
7791
|
try {
|
|
7377
|
-
const
|
|
7792
|
+
const text3 = await this.#transcribe(audio);
|
|
7378
7793
|
this.#setState("idle");
|
|
7379
|
-
if (
|
|
7380
|
-
this.#onText(
|
|
7794
|
+
if (text3 !== "") {
|
|
7795
|
+
this.#onText(text3);
|
|
7381
7796
|
}
|
|
7382
7797
|
} catch (error) {
|
|
7383
7798
|
this.#fail(error instanceof Error ? error.message : this.#strings.transcriptionFailed);
|
|
@@ -7614,6 +8029,7 @@ var AgUiClient = class {
|
|
|
7614
8029
|
#buildSubscriber(pending, runState) {
|
|
7615
8030
|
const h = this.#handlers;
|
|
7616
8031
|
const closed = this.#closedMessageIds;
|
|
8032
|
+
const pendingDeltas = /* @__PURE__ */ new Set();
|
|
7617
8033
|
return {
|
|
7618
8034
|
onRunInitialized() {
|
|
7619
8035
|
h.onRunStart();
|
|
@@ -7644,8 +8060,39 @@ var AgUiClient = class {
|
|
|
7644
8060
|
onToolCallResultEvent({ event }) {
|
|
7645
8061
|
h.onToolResult(event.toolCallId, event.content);
|
|
7646
8062
|
},
|
|
7647
|
-
onActivitySnapshotEvent({ event }) {
|
|
7648
|
-
|
|
8063
|
+
onActivitySnapshotEvent({ event, messages }) {
|
|
8064
|
+
const known = messages.some(
|
|
8065
|
+
(message) => message.id === event.messageId && message.role === "activity"
|
|
8066
|
+
);
|
|
8067
|
+
if (known) {
|
|
8068
|
+
h.onActivityChanged(event.messageId, event.activityType, event.content);
|
|
8069
|
+
return;
|
|
8070
|
+
}
|
|
8071
|
+
h.onActivity(event.activityType, event.content, event.messageId);
|
|
8072
|
+
},
|
|
8073
|
+
// Deliberately does *not* read the message here. `@ag-ui/client`
|
|
8074
|
+
// dispatches this subscriber **before** applying the patch, so the
|
|
8075
|
+
// message still holds its previous content: redrawing from it would leave
|
|
8076
|
+
// the chart one revision behind for the life of the run, and disagreeing
|
|
8077
|
+
// with what a reload shows. Note which chart moved and read the result on
|
|
8078
|
+
// the change that follows.
|
|
8079
|
+
onActivityDeltaEvent({ event }) {
|
|
8080
|
+
pendingDeltas.add(event.messageId);
|
|
8081
|
+
},
|
|
8082
|
+
// Emitted after the client has written the patched messages, which is the
|
|
8083
|
+
// first moment the result exists. Only the ids marked above are looked at,
|
|
8084
|
+
// so an ordinary text delta does not walk the transcript.
|
|
8085
|
+
onMessagesChanged({ messages }) {
|
|
8086
|
+
if (pendingDeltas.size === 0) {
|
|
8087
|
+
return;
|
|
8088
|
+
}
|
|
8089
|
+
for (const id of pendingDeltas) {
|
|
8090
|
+
const message = messages.find((entry) => entry.id === id);
|
|
8091
|
+
if (message !== void 0 && message.role === "activity") {
|
|
8092
|
+
h.onActivityChanged(id, message.activityType, message.content);
|
|
8093
|
+
}
|
|
8094
|
+
}
|
|
8095
|
+
pendingDeltas.clear();
|
|
7649
8096
|
},
|
|
7650
8097
|
// `@ag-ui/client` maps the deprecated THINKING_* events onto these
|
|
7651
8098
|
// REASONING_* callbacks, so the reasoning family alone covers both
|
|
@@ -7857,9 +8304,9 @@ function isOwnedSuffix(suffix) {
|
|
|
7857
8304
|
function deriveTitle(messages) {
|
|
7858
8305
|
for (const message of messages) {
|
|
7859
8306
|
if (message.role === "user") {
|
|
7860
|
-
const
|
|
7861
|
-
if (
|
|
7862
|
-
return truncate(
|
|
8307
|
+
const text3 = cleanText(message.content);
|
|
8308
|
+
if (text3 !== "") {
|
|
8309
|
+
return truncate(text3, TITLE_LIMIT);
|
|
7863
8310
|
}
|
|
7864
8311
|
}
|
|
7865
8312
|
}
|
|
@@ -7867,9 +8314,9 @@ function deriveTitle(messages) {
|
|
|
7867
8314
|
}
|
|
7868
8315
|
function derivePreview(messages) {
|
|
7869
8316
|
for (const message of [...messages].reverse()) {
|
|
7870
|
-
const
|
|
7871
|
-
if (
|
|
7872
|
-
return truncate(
|
|
8317
|
+
const text3 = cleanText(message.content);
|
|
8318
|
+
if (text3 !== "") {
|
|
8319
|
+
return truncate(text3, PREVIEW_LIMIT);
|
|
7873
8320
|
}
|
|
7874
8321
|
}
|
|
7875
8322
|
return "";
|
|
@@ -7877,8 +8324,8 @@ function derivePreview(messages) {
|
|
|
7877
8324
|
function cleanText(content) {
|
|
7878
8325
|
return typeof content === "string" ? content.replace(/\s+/g, " ").trim() : "";
|
|
7879
8326
|
}
|
|
7880
|
-
function truncate(
|
|
7881
|
-
return
|
|
8327
|
+
function truncate(text3, limit) {
|
|
8328
|
+
return text3.length <= limit ? text3 : `${text3.slice(0, limit - 1).trimEnd()}\u2026`;
|
|
7882
8329
|
}
|
|
7883
8330
|
|
|
7884
8331
|
// src/core/create_http_agent.ts
|
|
@@ -8397,6 +8844,12 @@ var AgUiChat = class extends HTMLElement {
|
|
|
8397
8844
|
* (`TOOL_CALL_RESULT`), so the post-run executeTool sweep doesn't overwrite
|
|
8398
8845
|
* the real output with the generic "executed on the server" fallback.
|
|
8399
8846
|
*/
|
|
8847
|
+
/** Whether a server-pushed chart activity is drawn. Off unless asked for. */
|
|
8848
|
+
#chartActivity = false;
|
|
8849
|
+
/** Card elements by call id, so a rendering handler can find its own card. */
|
|
8850
|
+
#cardElements = /* @__PURE__ */ new Map();
|
|
8851
|
+
/** Chart blocks by activity message id, so an update redraws in place. */
|
|
8852
|
+
#activityBlocks = /* @__PURE__ */ new Map();
|
|
8400
8853
|
#serverSettled = /* @__PURE__ */ new Set();
|
|
8401
8854
|
/**
|
|
8402
8855
|
* Tool calls made during the current interaction, in the order they started,
|
|
@@ -9042,7 +9495,7 @@ var AgUiChat = class extends HTMLElement {
|
|
|
9042
9495
|
}
|
|
9043
9496
|
this.#voice = new VoiceInput({
|
|
9044
9497
|
transcribe,
|
|
9045
|
-
onText: (
|
|
9498
|
+
onText: (text3) => this.#insertVoiceText(text3),
|
|
9046
9499
|
strings: this.#strings
|
|
9047
9500
|
});
|
|
9048
9501
|
this.#voiceSlot.appendChild(this.#voice.element);
|
|
@@ -9059,9 +9512,9 @@ var AgUiChat = class extends HTMLElement {
|
|
|
9059
9512
|
});
|
|
9060
9513
|
}
|
|
9061
9514
|
/** Drop a voice transcript into the composer (appended to any typed text). */
|
|
9062
|
-
#insertVoiceText(
|
|
9515
|
+
#insertVoiceText(text3) {
|
|
9063
9516
|
const current = this.#input.value.trim();
|
|
9064
|
-
this.#input.value = current === "" ?
|
|
9517
|
+
this.#input.value = current === "" ? text3 : `${current} ${text3}`;
|
|
9065
9518
|
this.#onInput();
|
|
9066
9519
|
this.#input.focus();
|
|
9067
9520
|
}
|
|
@@ -9205,18 +9658,18 @@ var AgUiChat = class extends HTMLElement {
|
|
|
9205
9658
|
void this.sendMessage(`/${skill.name}`);
|
|
9206
9659
|
return;
|
|
9207
9660
|
}
|
|
9208
|
-
const { text:
|
|
9661
|
+
const { text: text3, missing } = fillTemplate(skill.prompt, this.skillContext());
|
|
9209
9662
|
if (missing.length > 0) {
|
|
9210
9663
|
this.#skillHint.textContent = this.#strings.skillNeeds.replace("{title}", skill.title).replace("{fields}", missing.join(", "));
|
|
9211
9664
|
this.#skillHint.hidden = false;
|
|
9212
|
-
this.#input.value =
|
|
9665
|
+
this.#input.value = text3;
|
|
9213
9666
|
this.#autoGrow();
|
|
9214
9667
|
this.#input.focus();
|
|
9215
|
-
this.#selectFirstPlaceholder(
|
|
9668
|
+
this.#selectFirstPlaceholder(text3);
|
|
9216
9669
|
return;
|
|
9217
9670
|
}
|
|
9218
9671
|
this.#skillHint.hidden = true;
|
|
9219
|
-
this.#input.value =
|
|
9672
|
+
this.#input.value = text3;
|
|
9220
9673
|
this.#autoGrow();
|
|
9221
9674
|
if (skill.sendImmediately === false) {
|
|
9222
9675
|
this.#input.focus();
|
|
@@ -9230,9 +9683,9 @@ var AgUiChat = class extends HTMLElement {
|
|
|
9230
9683
|
* Typing then replaces it, which is the shortest path from "this skill needs
|
|
9231
9684
|
* a topic" to a sendable prompt.
|
|
9232
9685
|
*/
|
|
9233
|
-
#selectFirstPlaceholder(
|
|
9234
|
-
const start =
|
|
9235
|
-
this.#input.setSelectionRange(start,
|
|
9686
|
+
#selectFirstPlaceholder(text3) {
|
|
9687
|
+
const start = text3.indexOf("{");
|
|
9688
|
+
this.#input.setSelectionRange(start, text3.indexOf("}", start) + 1);
|
|
9236
9689
|
}
|
|
9237
9690
|
/** Whether the widget is collapsed (reflected as the `collapsed` attribute). */
|
|
9238
9691
|
get collapsed() {
|
|
@@ -9488,6 +9941,8 @@ var AgUiChat = class extends HTMLElement {
|
|
|
9488
9941
|
this.#hidePending();
|
|
9489
9942
|
this.#toolCards.clear();
|
|
9490
9943
|
this.#serverSettled.clear();
|
|
9944
|
+
this.#cardElements.clear();
|
|
9945
|
+
this.#activityBlocks.clear();
|
|
9491
9946
|
this.#initialMessages = [];
|
|
9492
9947
|
this.#attachTray?.clear();
|
|
9493
9948
|
this.#messages.replaceChildren(this.#emptyWrap);
|
|
@@ -9599,11 +10054,11 @@ var AgUiChat = class extends HTMLElement {
|
|
|
9599
10054
|
* the full transcript — tool calls and their results — not just the prose.
|
|
9600
10055
|
*/
|
|
9601
10056
|
#renderHistoricMessage(message) {
|
|
9602
|
-
const
|
|
10057
|
+
const text3 = typeof message.content === "string" ? message.content : "";
|
|
9603
10058
|
if (message.role === MESSAGE_ROLE.USER) {
|
|
9604
10059
|
const attachments = messageAttachments(message);
|
|
9605
|
-
if (
|
|
9606
|
-
const bubble = this.appendMessage(MESSAGE_ROLE.USER,
|
|
10060
|
+
if (text3 !== "" || attachments.length > 0) {
|
|
10061
|
+
const bubble = this.appendMessage(MESSAGE_ROLE.USER, text3);
|
|
9607
10062
|
if (attachments.length > 0) {
|
|
9608
10063
|
bubble.appendChild(renderAttachmentChips(attachments));
|
|
9609
10064
|
}
|
|
@@ -9611,8 +10066,8 @@ var AgUiChat = class extends HTMLElement {
|
|
|
9611
10066
|
return;
|
|
9612
10067
|
}
|
|
9613
10068
|
if (message.role === MESSAGE_ROLE.ASSISTANT) {
|
|
9614
|
-
if (
|
|
9615
|
-
this.appendMessage(MESSAGE_ROLE.ASSISTANT,
|
|
10069
|
+
if (text3 !== "") {
|
|
10070
|
+
this.appendMessage(MESSAGE_ROLE.ASSISTANT, text3).classList.add("message--restored");
|
|
9616
10071
|
}
|
|
9617
10072
|
for (const call of restoredToolCalls(message.toolCalls)) {
|
|
9618
10073
|
const restored = {
|
|
@@ -9623,7 +10078,18 @@ var AgUiChat = class extends HTMLElement {
|
|
|
9623
10078
|
if (this.#noticeIfSkillLoad(restored)) {
|
|
9624
10079
|
continue;
|
|
9625
10080
|
}
|
|
9626
|
-
this.#cardFor(restored);
|
|
10081
|
+
this.#cardElements.set(restored.id, this.#cardFor(restored).element);
|
|
10082
|
+
const render = this.#resolveTool(restored.name)?.render;
|
|
10083
|
+
if (render !== void 0) {
|
|
10084
|
+
this.#renderToolOutput(render, restored);
|
|
10085
|
+
}
|
|
10086
|
+
}
|
|
10087
|
+
return;
|
|
10088
|
+
}
|
|
10089
|
+
if (message.role === "activity") {
|
|
10090
|
+
const activity = message;
|
|
10091
|
+
if (activity.activityType === CHART_ACTIVITY_TYPE && this.#chartActivity) {
|
|
10092
|
+
this.#drawActivityChart(message.id, activity.content);
|
|
9627
10093
|
}
|
|
9628
10094
|
return;
|
|
9629
10095
|
}
|
|
@@ -10196,6 +10662,7 @@ var AgUiChat = class extends HTMLElement {
|
|
|
10196
10662
|
}
|
|
10197
10663
|
const card = this.#cardFor(call);
|
|
10198
10664
|
this.#toolCards.delete(call.id);
|
|
10665
|
+
this.#cardElements.set(call.id, card.element);
|
|
10199
10666
|
const tool = this.#resolveTool(call.name);
|
|
10200
10667
|
if (tool === null) {
|
|
10201
10668
|
if (!this.#serverSettled.has(call.id)) {
|
|
@@ -10237,7 +10704,10 @@ var AgUiChat = class extends HTMLElement {
|
|
|
10237
10704
|
this.conversationStore.saveCheckpoint(this.#threadId, { toolCallId: call.id });
|
|
10238
10705
|
}
|
|
10239
10706
|
try {
|
|
10240
|
-
const result = await tool.handler(call.args);
|
|
10707
|
+
const result = await tool.handler(call.args, call.id);
|
|
10708
|
+
if (tool.render !== void 0) {
|
|
10709
|
+
this.#renderToolOutput(tool.render, call);
|
|
10710
|
+
}
|
|
10241
10711
|
if (navigates) {
|
|
10242
10712
|
card.settle(TOOL_CALL_STATUS.DONE, this.#strings.navigating);
|
|
10243
10713
|
return { content: "", halt: true };
|
|
@@ -10356,7 +10826,13 @@ var AgUiChat = class extends HTMLElement {
|
|
|
10356
10826
|
this.#runTools.push({ id: call.id, name: call.name });
|
|
10357
10827
|
this.#cardFor(call);
|
|
10358
10828
|
},
|
|
10359
|
-
onActivity: (activityType, content) => {
|
|
10829
|
+
onActivity: (activityType, content, messageId) => {
|
|
10830
|
+
if (activityType === CHART_ACTIVITY_TYPE) {
|
|
10831
|
+
if (this.#chartActivity) {
|
|
10832
|
+
this.#drawActivityChart(messageId, content);
|
|
10833
|
+
}
|
|
10834
|
+
return;
|
|
10835
|
+
}
|
|
10360
10836
|
if (activityType !== COMPACTION_ACTIVITY_TYPE) {
|
|
10361
10837
|
return;
|
|
10362
10838
|
}
|
|
@@ -10379,6 +10855,11 @@ var AgUiChat = class extends HTMLElement {
|
|
|
10379
10855
|
this.#serverSettled.add(toolCallId);
|
|
10380
10856
|
this.#showPending();
|
|
10381
10857
|
},
|
|
10858
|
+
onActivityChanged: (messageId, activityType, content) => {
|
|
10859
|
+
if (activityType === CHART_ACTIVITY_TYPE && this.#chartActivity) {
|
|
10860
|
+
this.#drawActivityChart(messageId, content);
|
|
10861
|
+
}
|
|
10862
|
+
},
|
|
10382
10863
|
onRunEnd: () => {
|
|
10383
10864
|
this.#hidePending();
|
|
10384
10865
|
this.#streamingBubble = null;
|
|
@@ -10526,8 +11007,84 @@ var AgUiChat = class extends HTMLElement {
|
|
|
10526
11007
|
this.#appendNotice("\u2728", this.#strings.usingSkill.replace("{name}", skill), "skill");
|
|
10527
11008
|
return true;
|
|
10528
11009
|
}
|
|
10529
|
-
#appendNotice(icon,
|
|
10530
|
-
this.#ensureGroup().appendChild(renderRunNotice(icon,
|
|
11010
|
+
#appendNotice(icon, text3, kind) {
|
|
11011
|
+
this.#ensureGroup().appendChild(renderRunNotice(icon, text3, kind));
|
|
11012
|
+
this.#updateEmptyState();
|
|
11013
|
+
this.#messages.scrollTop = this.#messages.scrollHeight;
|
|
11014
|
+
}
|
|
11015
|
+
/**
|
|
11016
|
+
* Turn on chart rendering, by whichever route this consumer wants.
|
|
11017
|
+
*
|
|
11018
|
+
* Both routes converge on one renderer deliberately. Built apart they become
|
|
11019
|
+
* two chart implementations with two sets of bugs, and the choice between them
|
|
11020
|
+
* is about *where the data lives* rather than how a bar should look:
|
|
11021
|
+
*
|
|
11022
|
+
* - `"tool"` registers the built-in `render_chart`. The agent decides a chart
|
|
11023
|
+
* helps and calls it, so the numbers are in its context and it can discuss
|
|
11024
|
+
* them afterwards. Costs one model round, and works over any transport.
|
|
11025
|
+
* - `"activity"` draws a server-pushed `ACTIVITY_SNAPSHOT` of type `chart`.
|
|
11026
|
+
* No round trip, and the data never enters the model's context at all —
|
|
11027
|
+
* which is what makes it the one for a large or sensitive dataset. Only this
|
|
11028
|
+
* route can update a chart in place as the server computes.
|
|
11029
|
+
*
|
|
11030
|
+
* Off unless asked for, both of them: a component that renders whatever
|
|
11031
|
+
* arrives is not something to switch on for everybody.
|
|
11032
|
+
*/
|
|
11033
|
+
enableCharts(routes = ["tool", "activity"]) {
|
|
11034
|
+
const first = !this.#chartActivity && !this.#toolRegistry.has(CHART_TOOL_NAME);
|
|
11035
|
+
if (routes.includes("activity")) {
|
|
11036
|
+
this.#chartActivity = true;
|
|
11037
|
+
}
|
|
11038
|
+
if (routes.includes("tool")) {
|
|
11039
|
+
this.registerTool(createChartTool());
|
|
11040
|
+
}
|
|
11041
|
+
if (first && this.isConnected) {
|
|
11042
|
+
this.reload();
|
|
11043
|
+
}
|
|
11044
|
+
}
|
|
11045
|
+
/**
|
|
11046
|
+
* Place a tool's rendered node against its own card.
|
|
11047
|
+
*
|
|
11048
|
+
* Anchored rather than appended because a client tool's handler does not run
|
|
11049
|
+
* until the round is over: appending would put the node after everything the
|
|
11050
|
+
* model said next, visibly detached from the call that produced it, and in a
|
|
11051
|
+
* different order than the same transcript takes on reload. The card was
|
|
11052
|
+
* created inline, in the right place, so anchoring makes *when* the handler
|
|
11053
|
+
* runs stop mattering.
|
|
11054
|
+
*/
|
|
11055
|
+
#renderToolOutput(render, call) {
|
|
11056
|
+
let node;
|
|
11057
|
+
try {
|
|
11058
|
+
node = render(call.args);
|
|
11059
|
+
} catch (error) {
|
|
11060
|
+
console.warn(`ag-ui-chat: render failed for tool ${call.name}`, error);
|
|
11061
|
+
return;
|
|
11062
|
+
}
|
|
11063
|
+
if (node === null) {
|
|
11064
|
+
return;
|
|
11065
|
+
}
|
|
11066
|
+
this.#cardElements.get(call.id)?.after(node);
|
|
11067
|
+
this.#afterTranscriptGrew();
|
|
11068
|
+
}
|
|
11069
|
+
/** Draw, or redraw in place, the chart for one activity message. */
|
|
11070
|
+
#drawActivityChart(messageId, content) {
|
|
11071
|
+
const spec = chartSpecFrom(content);
|
|
11072
|
+
const block = spec === null ? null : renderChart(spec);
|
|
11073
|
+
if (block === null) {
|
|
11074
|
+
this.#activityBlocks.get(messageId)?.remove();
|
|
11075
|
+
this.#activityBlocks.delete(messageId);
|
|
11076
|
+
return;
|
|
11077
|
+
}
|
|
11078
|
+
const existing = this.#activityBlocks.get(messageId);
|
|
11079
|
+
if (existing === void 0) {
|
|
11080
|
+
this.#ensureGroup().appendChild(block);
|
|
11081
|
+
} else {
|
|
11082
|
+
existing.replaceWith(block);
|
|
11083
|
+
}
|
|
11084
|
+
this.#activityBlocks.set(messageId, block);
|
|
11085
|
+
this.#afterTranscriptGrew();
|
|
11086
|
+
}
|
|
11087
|
+
#afterTranscriptGrew() {
|
|
10531
11088
|
this.#updateEmptyState();
|
|
10532
11089
|
this.#messages.scrollTop = this.#messages.scrollHeight;
|
|
10533
11090
|
}
|
|
@@ -10580,43 +11137,45 @@ function defineAgUiChat() {
|
|
|
10580
11137
|
}
|
|
10581
11138
|
|
|
10582
11139
|
// 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(
|
|
11140
|
+
async function fillField(el2, value, options = {}) {
|
|
11141
|
+
await scrollIntoCenterView(el2);
|
|
11142
|
+
await focusWithFlash(el2, { ...options, flashMs: options.flashMs ?? 0 });
|
|
11143
|
+
await typeInto(el2, value, options);
|
|
11144
|
+
}
|
|
11145
|
+
async function clickElement(el2, options = {}) {
|
|
11146
|
+
await scrollIntoCenterView(el2);
|
|
11147
|
+
await highlightThenClick(el2, options);
|
|
11148
|
+
}
|
|
11149
|
+
async function pressButton(el2, options = {}) {
|
|
11150
|
+
await scrollIntoCenterView(el2);
|
|
11151
|
+
await pressThenClick(el2, options);
|
|
11152
|
+
}
|
|
11153
|
+
async function selectControl(el2, value, options = {}) {
|
|
11154
|
+
await scrollIntoCenterView(el2);
|
|
11155
|
+
await selectOption(el2, value, options);
|
|
11156
|
+
}
|
|
11157
|
+
async function toggleCheckbox(el2, checked, options = {}) {
|
|
11158
|
+
await scrollIntoCenterView(el2);
|
|
11159
|
+
await toggleControl(el2, checked, options);
|
|
11160
|
+
}
|
|
11161
|
+
function setControlValue(el2, value) {
|
|
11162
|
+
if (el2 instanceof HTMLInputElement && el2.type === "checkbox") {
|
|
11163
|
+
setNativeChecked(el2, Boolean(value));
|
|
10607
11164
|
} else {
|
|
10608
|
-
setNativeValue(
|
|
11165
|
+
setNativeValue(el2, String(value));
|
|
10609
11166
|
}
|
|
10610
|
-
|
|
10611
|
-
|
|
11167
|
+
el2.dispatchEvent(new Event("input", { bubbles: true }));
|
|
11168
|
+
el2.dispatchEvent(new Event("change", { bubbles: true }));
|
|
10612
11169
|
}
|
|
10613
11170
|
|
|
10614
11171
|
// src/version.ts
|
|
10615
|
-
var VERSION = "0.
|
|
11172
|
+
var VERSION = "0.26.1";
|
|
10616
11173
|
export {
|
|
10617
11174
|
ATTACHMENT_EVENT,
|
|
10618
11175
|
AgUiChat,
|
|
10619
11176
|
AgUiClient,
|
|
11177
|
+
CHART_ACTIVITY_TYPE,
|
|
11178
|
+
CHART_TOOL_NAME,
|
|
10620
11179
|
COMPACTION_ACTIVITY_TYPE,
|
|
10621
11180
|
CheckpointMenu,
|
|
10622
11181
|
ClientToolRegistry,
|
|
@@ -10643,6 +11202,7 @@ export {
|
|
|
10643
11202
|
X_DESTRUCTIVE_KEY,
|
|
10644
11203
|
X_NAVIGATES_KEY,
|
|
10645
11204
|
X_SUMMARY_KEY,
|
|
11205
|
+
chartSpecFrom,
|
|
10646
11206
|
clickElement,
|
|
10647
11207
|
createHttpAgent,
|
|
10648
11208
|
createPageActionTools,
|
|
@@ -10664,6 +11224,7 @@ export {
|
|
|
10664
11224
|
pressButton,
|
|
10665
11225
|
pressThenClick,
|
|
10666
11226
|
prettifyToolName,
|
|
11227
|
+
renderChart,
|
|
10667
11228
|
renderMarkdown,
|
|
10668
11229
|
requestApproval,
|
|
10669
11230
|
requestConfirmation,
|