@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/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 text2 = template.replace(PLACEHOLDER_RE, (token, key) => {
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: text2, missing };
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(el, value) {
135
- if (el instanceof HTMLTextAreaElement) {
136
- setTextareaValue.call(el, value);
137
- } else if (el instanceof HTMLSelectElement) {
138
- setSelectValue.call(el, value);
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(el, value);
141
+ setInputValue.call(el2, value);
141
142
  }
142
143
  }
143
- function setNativeChecked(el, checked) {
144
- setInputChecked.call(el, checked);
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(el, fallback) {
166
- const themed = window.getComputedStyle(el).getPropertyValue(ACCENT_PROPERTY).trim();
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(el) {
170
- return `0 0 0 3px ${accentColor(el, ACCENT_HALO)}`;
170
+ function ringShadow(el2) {
171
+ return `0 0 0 3px ${accentColor(el2, ACCENT_HALO)}`;
171
172
  }
172
- async function typeInto(el, value, options = {}) {
173
+ async function typeInto(el2, value, options = {}) {
173
174
  const charDelayMs = options.charDelayMs ?? 35;
174
- setNativeValue(el, "");
175
- el.dispatchEvent(new Event("input", { bubbles: true }));
175
+ setNativeValue(el2, "");
176
+ el2.dispatchEvent(new Event("input", { bubbles: true }));
176
177
  for (const char of value) {
177
- setNativeValue(el, el.value + char);
178
- el.dispatchEvent(new Event("input", { bubbles: true }));
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
- el.dispatchEvent(new Event("change", { bubbles: true }));
184
+ el2.dispatchEvent(new Event("change", { bubbles: true }));
184
185
  }
185
- async function highlightThenClick(el, options = {}) {
186
+ async function highlightThenClick(el2, options = {}) {
186
187
  const highlightMs = options.highlightMs ?? 280;
187
- const previousOutline = el.style.outline;
188
- const previousOffset = el.style.outlineOffset;
189
- el.style.outline = `2px solid ${accentColor(el, ACCENT)}`;
190
- el.style.outlineOffset = "2px";
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
- el.style.outline = previousOutline;
193
- el.style.outlineOffset = previousOffset;
194
- el.click();
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(el, options = {}) {
199
+ function scrollIntoCenterView(el2, options = {}) {
199
200
  const reduced = prefersReducedMotion();
200
- el.scrollIntoView({
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(el, options, focusByDefault) {
229
+ async function flashRing(el2, options, focusByDefault) {
229
230
  if (options.focus ?? focusByDefault) {
230
- el.focus({ preventScroll: true });
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 = el.style.outline;
237
- const previousOffset = el.style.outlineOffset;
238
- const previousTransition = el.style.transition;
239
- const color = options.color ?? accentColor(el, ACCENT);
240
- el.style.outline = `3px solid ${color}`;
241
- el.style.outlineOffset = "2px";
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
- el.style.transition = `outline-color ${fadeMs}ms ease-out`;
246
- el.style.outline = "3px solid transparent";
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
- el.style.outline = previousOutline;
250
- el.style.outlineOffset = previousOffset;
251
- el.style.transition = previousTransition;
250
+ el2.style.outline = previousOutline;
251
+ el2.style.outlineOffset = previousOffset;
252
+ el2.style.transition = previousTransition;
252
253
  }
253
- function flash(el, options = {}) {
254
- return flashRing(el, options, false);
254
+ function flash(el2, options = {}) {
255
+ return flashRing(el2, options, false);
255
256
  }
256
- function focusWithFlash(el, options = {}) {
257
- return flashRing(el, options, true);
257
+ function focusWithFlash(el2, options = {}) {
258
+ return flashRing(el2, options, true);
258
259
  }
259
- async function pressThenClick(el, options = {}) {
260
+ async function pressThenClick(el2, options = {}) {
260
261
  const pressMs = options.pressMs ?? 140;
261
- const previousTransform = el.style.transform;
262
- const previousTransition = el.style.transition;
263
- const previousShadow = el.style.boxShadow;
264
- el.style.transition = "transform 80ms ease";
265
- el.style.transform = "scale(0.96)";
266
- el.style.boxShadow = ringShadow(el);
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
- el.style.transform = previousTransform;
269
- el.style.transition = previousTransition;
270
- el.style.boxShadow = previousShadow;
271
- el.click();
269
+ el2.style.transform = previousTransform;
270
+ el2.style.transition = previousTransition;
271
+ el2.style.boxShadow = previousShadow;
272
+ el2.click();
272
273
  }
273
- function findOption(el, value) {
274
- for (const option of Array.from(el.options)) {
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(el, value, options = {}) {
282
- const option = findOption(el, value);
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 = el.style.outline;
288
- const previousOffset = el.style.outlineOffset;
289
- el.style.outline = `2px solid ${accentColor(el, ACCENT)}`;
290
- el.style.outlineOffset = "2px";
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(el, option.value);
293
- el.dispatchEvent(new Event("input", { bubbles: true }));
294
- el.dispatchEvent(new Event("change", { bubbles: true }));
295
- el.style.outline = previousOutline;
296
- el.style.outlineOffset = previousOffset;
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(el, checked, options = {}) {
299
+ async function toggleControl(el2, checked, options = {}) {
299
300
  const flashMs = options.flashMs ?? 200;
300
- const previousShadow = el.style.boxShadow;
301
- el.style.boxShadow = ringShadow(el);
301
+ const previousShadow = el2.style.boxShadow;
302
+ el2.style.boxShadow = ringShadow(el2);
302
303
  await motionDelay(flashMs);
303
- setNativeChecked(el, checked);
304
- el.dispatchEvent(new Event("input", { bubbles: true }));
305
- el.dispatchEvent(new Event("change", { bubbles: true }));
306
- el.style.boxShadow = previousShadow;
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 text2 = code.textContent;
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(text2).then((ok) => {
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(text2) {
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(text2);
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
- (el) => !el.hidden
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 text2 = document.createElement("span");
1312
- text2.setAttribute("part", "question-choice-text");
1313
- text2.textContent = choice;
1314
- label.append(radio, text2);
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 text2 = document.createElement("span");
1332
- text2.setAttribute("part", "question-choice-text");
1333
- text2.textContent = strings.otherOption;
1334
- label.append(otherRadio, text2);
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 text = freeze(["#text"]);
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, ...text]);
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({}, text);
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(text2, options) {
4590
+ function renderMarkdown(text3, options) {
4232
4591
  const allowImages = options?.allowImages === true;
4233
- const rendered = parser.parse(text2, { async: false });
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, text2, kind) {
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 = text2;
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((el) => !el.hidden);
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(text2) {
7543
+ function formatPayload(text3) {
7141
7544
  try {
7142
- return JSON.stringify(JSON.parse(text2), null, 2);
7545
+ return JSON.stringify(JSON.parse(text3), null, 2);
7143
7546
  } catch {
7144
- return text2;
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, text2) {
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(text2);
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 text2 = await this.#transcribe(audio);
7780
+ const text3 = await this.#transcribe(audio);
7378
7781
  this.#setState("idle");
7379
- if (text2 !== "") {
7380
- this.#onText(text2);
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
- h.onActivity(event.activityType, event.content);
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 text2 = cleanText(message.content);
7861
- if (text2 !== "") {
7862
- return truncate(text2, TITLE_LIMIT);
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 text2 = cleanText(message.content);
7871
- if (text2 !== "") {
7872
- return truncate(text2, PREVIEW_LIMIT);
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(text2, limit) {
7881
- return text2.length <= limit ? text2 : `${text2.slice(0, limit - 1).trimEnd()}\u2026`;
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: (text2) => this.#insertVoiceText(text2),
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(text2) {
9503
+ #insertVoiceText(text3) {
9063
9504
  const current = this.#input.value.trim();
9064
- this.#input.value = current === "" ? text2 : `${current} ${text2}`;
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: text2, missing } = fillTemplate(skill.prompt, this.skillContext());
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 = text2;
9653
+ this.#input.value = text3;
9213
9654
  this.#autoGrow();
9214
9655
  this.#input.focus();
9215
- this.#selectFirstPlaceholder(text2);
9656
+ this.#selectFirstPlaceholder(text3);
9216
9657
  return;
9217
9658
  }
9218
9659
  this.#skillHint.hidden = true;
9219
- this.#input.value = text2;
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(text2) {
9234
- const start = text2.indexOf("{");
9235
- this.#input.setSelectionRange(start, text2.indexOf("}", start) + 1);
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 text2 = typeof message.content === "string" ? message.content : "";
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 (text2 !== "" || attachments.length > 0) {
9606
- const bubble = this.appendMessage(MESSAGE_ROLE.USER, text2);
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 (text2 !== "") {
9615
- this.appendMessage(MESSAGE_ROLE.ASSISTANT, text2).classList.add("message--restored");
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, text2, kind) {
10530
- this.#ensureGroup().appendChild(renderRunNotice(icon, text2, kind));
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(el, value, options = {}) {
10584
- await scrollIntoCenterView(el);
10585
- await focusWithFlash(el, { ...options, flashMs: options.flashMs ?? 0 });
10586
- await typeInto(el, value, options);
10587
- }
10588
- async function clickElement(el, options = {}) {
10589
- await scrollIntoCenterView(el);
10590
- await highlightThenClick(el, options);
10591
- }
10592
- async function pressButton(el, options = {}) {
10593
- await scrollIntoCenterView(el);
10594
- await pressThenClick(el, options);
10595
- }
10596
- async function selectControl(el, value, options = {}) {
10597
- await scrollIntoCenterView(el);
10598
- await selectOption(el, value, options);
10599
- }
10600
- async function toggleCheckbox(el, checked, options = {}) {
10601
- await scrollIntoCenterView(el);
10602
- await toggleControl(el, checked, options);
10603
- }
10604
- function setControlValue(el, value) {
10605
- if (el instanceof HTMLInputElement && el.type === "checkbox") {
10606
- setNativeChecked(el, Boolean(value));
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(el, String(value));
11151
+ setNativeValue(el2, String(value));
10609
11152
  }
10610
- el.dispatchEvent(new Event("input", { bubbles: true }));
10611
- el.dispatchEvent(new Event("change", { bubbles: true }));
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.25.2";
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,