@seed-ship/mcp-ui-solid 5.5.1 → 5.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/dist/components/FormRenderer.cjs +13 -2
  3. package/dist/components/FormRenderer.cjs.map +1 -1
  4. package/dist/components/FormRenderer.d.ts.map +1 -1
  5. package/dist/components/FormRenderer.js +13 -2
  6. package/dist/components/FormRenderer.js.map +1 -1
  7. package/dist/components/GenerativeUIErrorBoundary.cjs +11 -0
  8. package/dist/components/GenerativeUIErrorBoundary.cjs.map +1 -1
  9. package/dist/components/GenerativeUIErrorBoundary.d.ts.map +1 -1
  10. package/dist/components/GenerativeUIErrorBoundary.js +11 -0
  11. package/dist/components/GenerativeUIErrorBoundary.js.map +1 -1
  12. package/dist/components/StreamingUIRenderer.cjs +49 -3
  13. package/dist/components/StreamingUIRenderer.cjs.map +1 -1
  14. package/dist/components/StreamingUIRenderer.d.ts.map +1 -1
  15. package/dist/components/StreamingUIRenderer.js +51 -5
  16. package/dist/components/StreamingUIRenderer.js.map +1 -1
  17. package/dist/components/UIResourceRenderer.cjs +102 -14
  18. package/dist/components/UIResourceRenderer.cjs.map +1 -1
  19. package/dist/components/UIResourceRenderer.d.ts +36 -1
  20. package/dist/components/UIResourceRenderer.d.ts.map +1 -1
  21. package/dist/components/UIResourceRenderer.js +104 -16
  22. package/dist/components/UIResourceRenderer.js.map +1 -1
  23. package/dist/context/MCPUITelemetryContext.cjs +25 -0
  24. package/dist/context/MCPUITelemetryContext.cjs.map +1 -0
  25. package/dist/context/MCPUITelemetryContext.d.ts +36 -0
  26. package/dist/context/MCPUITelemetryContext.d.ts.map +1 -0
  27. package/dist/context/MCPUITelemetryContext.js +25 -0
  28. package/dist/context/MCPUITelemetryContext.js.map +1 -0
  29. package/dist/index.cjs +7 -0
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.d.cts +7 -0
  32. package/dist/index.d.ts +7 -0
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +8 -1
  35. package/dist/index.js.map +1 -1
  36. package/dist/mcp-ui-spec/dist/schemas.cjs +25 -6
  37. package/dist/mcp-ui-spec/dist/schemas.cjs.map +1 -1
  38. package/dist/mcp-ui-spec/dist/schemas.js +25 -6
  39. package/dist/mcp-ui-spec/dist/schemas.js.map +1 -1
  40. package/dist/services/telemetry.cjs +56 -0
  41. package/dist/services/telemetry.cjs.map +1 -0
  42. package/dist/services/telemetry.d.ts +87 -0
  43. package/dist/services/telemetry.d.ts.map +1 -0
  44. package/dist/services/telemetry.js +56 -0
  45. package/dist/services/telemetry.js.map +1 -0
  46. package/dist/services/validation.cjs +25 -24
  47. package/dist/services/validation.cjs.map +1 -1
  48. package/dist/services/validation.d.ts.map +1 -1
  49. package/dist/services/validation.js +26 -25
  50. package/dist/services/validation.js.map +1 -1
  51. package/dist/types/index.d.ts +26 -0
  52. package/dist/types/index.d.ts.map +1 -1
  53. package/dist/types.d.cts +26 -0
  54. package/dist/types.d.ts +26 -0
  55. package/docs/briefs/BRIEF-citations-in-table-cells.md +365 -0
  56. package/package.json +3 -3
  57. package/src/components/FormRenderer.tsx +14 -0
  58. package/src/components/GenerativeUIErrorBoundary.tsx +17 -1
  59. package/src/components/StreamingUIRenderer.tsx +55 -3
  60. package/src/components/TableRenderer.citation.test.tsx +157 -0
  61. package/src/components/UIResourceRenderer.tsx +212 -15
  62. package/src/context/MCPUITelemetryContext.test.tsx +119 -0
  63. package/src/context/MCPUITelemetryContext.tsx +71 -0
  64. package/src/index.ts +20 -0
  65. package/src/services/telemetry.test.ts +134 -0
  66. package/src/services/telemetry.ts +149 -0
  67. package/src/services/validation.ts +43 -41
  68. package/src/types/index.ts +30 -0
  69. package/tsconfig.tsbuildinfo +1 -1
@@ -6,6 +6,7 @@ const solidJs = require("solid-js");
6
6
  const validation = require("../services/validation.cjs");
7
7
  const GenerativeUIErrorBoundary = require("./GenerativeUIErrorBoundary.cjs");
8
8
  const perf = require("../utils/perf.cjs");
9
+ const MCPUITelemetryContext = require("../context/MCPUITelemetryContext.cjs");
9
10
  const GridRenderer = require("./GridRenderer.cjs");
10
11
  const FooterRenderer = require("./FooterRenderer.cjs");
11
12
  const CarouselRenderer = require("./CarouselRenderer.cjs");
@@ -220,7 +221,27 @@ function highlightQuery(html, query) {
220
221
  return text.replace(regex, '<mark class="bg-yellow-200 dark:bg-[#222F49] text-inherit rounded px-0.5">$1</mark>');
221
222
  });
222
223
  }
223
- function renderCellValue(value) {
224
+ function defaultCitationChip(pageNum, fileName, verified = true) {
225
+ const safeDocName = encodeURIComponent(fileName || "");
226
+ const label = fileName ? `${fileName} - ${pageNum}` : `${pageNum}`;
227
+ if (!verified) {
228
+ return `<span class="citation-ref inline-flex items-center gap-0.5 align-middle opacity-60"><span class="text-gray-500 line-through">[${label}]</span></span>`;
229
+ }
230
+ return ['<span class="citation-ref inline-flex items-center gap-0.5 align-middle">', `<span class="text-gray-500">[${label}]</span>`, '<button class="inline-flex items-center ml-0.5 px-1 py-0.5 text-xs bg-gray-800 hover:bg-gray-700 border border-gray-600 hover:border-teal-500 rounded cursor-pointer transition-colors align-middle"', ` data-citation-page="${pageNum}"`, ` data-citation-doc="${safeDocName}"`, ' data-citation-verified="true"', ` title="View source - ${label}">`, '<svg class="w-3 h-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>', "</button>", "</span>"].join("");
231
+ }
232
+ function transformCellCitations(text, ctx) {
233
+ let out = text.replace(new RegExp("(?<![p.])\\[(\\d{1,2})\\](?!\\()", "g"), "[📄 CITATION $1]");
234
+ out = out.replace(/\bCitations?\s*\[(\d+)\]/gi, "[📄 CITATION $1]");
235
+ out = out.replace(/\[CITATION\s+(\d+)\]/gi, "[📄 CITATION $1]");
236
+ return out.replace(/[【[]\s*📄\s*CITATION\s*(\d+)\s*[】\]]/gi, (_m, idStr) => {
237
+ const id = parseInt(idStr, 10);
238
+ const mapping = ctx.map[id] ?? ctx.map[String(id)];
239
+ if (ctx.render) return ctx.render(id, mapping);
240
+ if (mapping) return defaultCitationChip(mapping.page, mapping.file ?? "", true);
241
+ return Object.keys(ctx.map).length > 0 ? "" : `[réf. ${id}]`;
242
+ });
243
+ }
244
+ function renderCellValue(value, citationCtx) {
224
245
  if (value === null || value === void 0) {
225
246
  return "-";
226
247
  }
@@ -256,20 +277,25 @@ function renderCellValue(value) {
256
277
  ADD_ATTR: ["target", "rel"]
257
278
  });
258
279
  }
259
- const hasHtml = /<[a-z][\s\S]*>/i.test(strValue);
260
- if (hasHtml) {
261
- return purify_es.sanitize(strValue, {
262
- ALLOWED_TAGS: ["a", "strong", "em", "b", "i", "code", "span", "br", "button", "svg", "path"],
263
- ALLOWED_ATTR: ["href", "target", "rel", "class", "data-citation-page", "data-citation-source", "data-citation-doc", "data-citation-verified", "title", "fill", "stroke", "viewBox", "stroke-linecap", "stroke-linejoin", "stroke-width", "d"],
264
- ADD_ATTR: ["target", "rel"]
265
- });
280
+ if (citationCtx) {
281
+ strValue = transformCellCitations(strValue, citationCtx);
266
282
  }
267
- const hasMarkdown = /[*_`[\]#]/.test(strValue);
283
+ const hasMarkdown = /[*_`#]/.test(strValue);
268
284
  if (hasMarkdown) {
269
285
  const parsed = marked_esm.marked.parse(strValue, {
270
286
  async: false
271
287
  });
272
288
  return purify_es.sanitize(parsed, {
289
+ ALLOWED_TAGS: ["a", "strong", "em", "b", "i", "code", "span", "br", "button", "svg", "path", "p", "ul", "ol", "li", "pre", "blockquote", "h1", "h2", "h3", "h4", "h5", "h6"],
290
+ ALLOWED_ATTR: ["href", "target", "rel", "class", "data-citation-page", "data-citation-source", "data-citation-doc", "data-citation-verified", "title", "fill", "stroke", "viewBox", "stroke-linecap", "stroke-linejoin", "stroke-width", "d"],
291
+ ADD_ATTR: ["target", "rel"]
292
+ });
293
+ }
294
+ const hasHtml = /<[a-z][\s\S]*>/i.test(strValue);
295
+ if (hasHtml) {
296
+ return purify_es.sanitize(strValue, {
297
+ ALLOWED_TAGS: ["a", "strong", "em", "b", "i", "code", "span", "br", "button", "svg", "path"],
298
+ ALLOWED_ATTR: ["href", "target", "rel", "class", "data-citation-page", "data-citation-source", "data-citation-doc", "data-citation-verified", "title", "fill", "stroke", "viewBox", "stroke-linecap", "stroke-linejoin", "stroke-width", "d"],
273
299
  ADD_ATTR: ["target", "rel"]
274
300
  });
275
301
  }
@@ -278,6 +304,10 @@ function renderCellValue(value) {
278
304
  function TableRenderer(props) {
279
305
  const tableParams = props.component.params;
280
306
  let scrollContainerRef;
307
+ const citationCtx = tableParams.citationMap ? {
308
+ map: tableParams.citationMap,
309
+ render: tableParams.citationRender
310
+ } : void 0;
281
311
  const allRows = () => tableParams.rows || [];
282
312
  const columns = () => tableParams.columns || [];
283
313
  const [sortKey, setSortKey] = solidJs.createSignal(null);
@@ -518,7 +548,7 @@ ${dataRows}`;
518
548
  },
519
549
  children: (column) => (() => {
520
550
  var _el$23 = web.getNextElement(_tmpl$10), _el$24 = _el$23.firstChild;
521
- web.effect(() => web.setProperty(_el$24, "innerHTML", highlightQuery(renderCellValue(row[column.key]), debouncedQuery())));
551
+ web.effect(() => web.setProperty(_el$24, "innerHTML", highlightQuery(renderCellValue(row[column.key], citationCtx), debouncedQuery())));
522
552
  return _el$23;
523
553
  })()
524
554
  }));
@@ -551,7 +581,7 @@ ${dataRows}`;
551
581
  },
552
582
  children: (column) => (() => {
553
583
  var _el$27 = web.getNextElement(_tmpl$10), _el$28 = _el$27.firstChild;
554
- web.effect(() => web.setProperty(_el$28, "innerHTML", highlightQuery(renderCellValue(row[column.key]), debouncedQuery())));
584
+ web.effect(() => web.setProperty(_el$28, "innerHTML", highlightQuery(renderCellValue(row[column.key], citationCtx), debouncedQuery())));
555
585
  return _el$27;
556
586
  })()
557
587
  }));
@@ -1055,9 +1085,44 @@ function LinkRenderer(props) {
1055
1085
  })();
1056
1086
  }
1057
1087
  function ComponentRenderer(props) {
1058
- var _a, _b, _c;
1088
+ var _a, _b, _c, _d, _e, _f;
1059
1089
  perf.markRenderStart(props.component.id);
1060
- solidJs.onMount(() => perf.markRenderEnd(props.component.id));
1090
+ const telemetry = MCPUITelemetryContext.useTelemetry();
1091
+ solidJs.onMount(() => {
1092
+ perf.markRenderEnd(props.component.id);
1093
+ if (telemetry) {
1094
+ const ts = Date.now();
1095
+ telemetry.dispatch({
1096
+ type: "component:mounted",
1097
+ id: props.component.id,
1098
+ componentType: props.component.type,
1099
+ ts
1100
+ });
1101
+ if (typeof performance !== "undefined" && typeof performance.getEntriesByName === "function") {
1102
+ const entries = performance.getEntriesByName(`${perf.PERF_PREFIX}${props.component.id}:render`, "measure");
1103
+ const last = entries[entries.length - 1];
1104
+ if (last) {
1105
+ telemetry.dispatch({
1106
+ type: "component:rendered",
1107
+ id: props.component.id,
1108
+ componentType: props.component.type,
1109
+ durationMs: last.duration,
1110
+ ts
1111
+ });
1112
+ }
1113
+ }
1114
+ }
1115
+ });
1116
+ solidJs.onCleanup(() => {
1117
+ if (telemetry) {
1118
+ telemetry.dispatch({
1119
+ type: "component:unmounted",
1120
+ id: props.component.id,
1121
+ componentType: props.component.type,
1122
+ ts: Date.now()
1123
+ });
1124
+ }
1125
+ });
1061
1126
  const validation$1 = validation.validateComponent(props.component);
1062
1127
  if (!validation$1.valid) {
1063
1128
  (_a = props.onError) == null ? void 0 : _a.call(props, {
@@ -1066,8 +1131,18 @@ function ComponentRenderer(props) {
1066
1131
  componentId: props.component.id,
1067
1132
  details: validation$1.errors
1068
1133
  });
1134
+ if (telemetry) {
1135
+ telemetry.dispatch({
1136
+ type: "validation:failed",
1137
+ id: props.component.id,
1138
+ componentType: props.component.type,
1139
+ errorCount: ((_b = validation$1.errors) == null ? void 0 : _b.length) ?? 0,
1140
+ firstErrorCode: ((_d = (_c = validation$1.errors) == null ? void 0 : _c[0]) == null ? void 0 : _d.code) ?? null,
1141
+ ts: Date.now()
1142
+ });
1143
+ }
1069
1144
  const mode = props.errorMode ?? "block";
1070
- const firstError = ((_c = (_b = validation$1.errors) == null ? void 0 : _b[0]) == null ? void 0 : _c.message) || "Unknown validation error";
1145
+ const firstError = ((_f = (_e = validation$1.errors) == null ? void 0 : _e[0]) == null ? void 0 : _f.message) || "Unknown validation error";
1071
1146
  if (mode === "silent") {
1072
1147
  return null;
1073
1148
  }
@@ -1319,7 +1394,20 @@ function ActionRenderer(props) {
1319
1394
  execute,
1320
1395
  isExecuting
1321
1396
  } = useAction.useAction();
1397
+ const telemetry = MCPUITelemetryContext.useTelemetry();
1398
+ function dispatchTelemetry() {
1399
+ if (!telemetry) return;
1400
+ const actionName = params.toolName ?? params.action ?? "unknown";
1401
+ telemetry.dispatch({
1402
+ type: "action:dispatched",
1403
+ id: props.component.id,
1404
+ componentType: "action",
1405
+ actionName,
1406
+ ts: Date.now()
1407
+ });
1408
+ }
1322
1409
  const handleClick = async (e) => {
1410
+ dispatchTelemetry();
1323
1411
  if (params.action === "tool-call" && params.toolName) {
1324
1412
  e.preventDefault();
1325
1413
  await execute(params.toolName, params.params || {});