@seed-ship/mcp-ui-solid 5.6.0 → 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.
- package/CHANGELOG.md +39 -0
- package/dist/components/UIResourceRenderer.cjs +40 -11
- package/dist/components/UIResourceRenderer.cjs.map +1 -1
- package/dist/components/UIResourceRenderer.d.ts +36 -1
- package/dist/components/UIResourceRenderer.d.ts.map +1 -1
- package/dist/components/UIResourceRenderer.js +40 -11
- package/dist/components/UIResourceRenderer.js.map +1 -1
- package/dist/index.cjs +1 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/mcp-ui-spec/dist/schemas.cjs +9 -1
- package/dist/mcp-ui-spec/dist/schemas.cjs.map +1 -1
- package/dist/mcp-ui-spec/dist/schemas.js +9 -1
- package/dist/mcp-ui-spec/dist/schemas.js.map +1 -1
- package/dist/types/index.d.ts +26 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types.d.cts +26 -0
- package/dist/types.d.ts +26 -0
- package/docs/briefs/BRIEF-citations-in-table-cells.md +365 -0
- package/package.json +3 -3
- package/src/components/TableRenderer.citation.test.tsx +157 -0
- package/src/components/UIResourceRenderer.tsx +133 -12
- package/src/index.ts +5 -0
- package/src/types/index.ts +30 -0
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,45 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [5.7.0] - 2026-05-02
|
|
9
|
+
|
|
10
|
+
### Added — citation chips inside table cells (opt-in)
|
|
11
|
+
|
|
12
|
+
Implements `mcp-ui-solid/docs/briefs/BRIEF-citations-in-table-cells.md`. Lifts the chip-rendering responsibility out of consumer apps (deposium had a server-side bridge in `deposium_MCPs` commit `7df433ae` that this obsoletes) so any host stops mirroring chip HTML byte-for-byte.
|
|
13
|
+
|
|
14
|
+
- **`TableComponentParams.citationMap`** (optional, JSON-serializable) — `Record<string|number, { page, file?, file_id? }>`. When present, `<TableRenderer>` walks each cell string and replaces LLM `[N]` / `Citation [N]` / `[CITATION N]` / `[📄 CITATION N]` markers with chip HTML carrying `data-citation-page` + `data-citation-doc` + `data-citation-verified` attrs. Hosts can route clicks via `target.closest('[data-citation-page]')` delegation — same path as inline-markdown chips.
|
|
15
|
+
- **`TableComponentParams.citationRender`** (optional, function — NOT JSON-serializable, consumer-wired) — override returning sanitized chip HTML for one marker. Wins over the default chip shape.
|
|
16
|
+
- **`renderCellValue(value, citationCtx?)`** — new optional 2nd arg. Standalone use (outside `<TableRenderer>`) supported : same opt-in behavior, same DOMPurify whitelist guarantees.
|
|
17
|
+
- **`CitationCtx`** + **`CitationEntry`** types exported from the package root.
|
|
18
|
+
- **`defaultCitationChip()`** uses neutral Tailwind classes (`bg-gray-800 text-gray-500 border-gray-600 hover:border-teal-500`) layered with the `.citation-ref` CSS class — hosts already styling `.citation-ref` for their inline chips get visual consistency for free, no per-table override needed.
|
|
19
|
+
|
|
20
|
+
Markdown composition : cells like `**MSP** [1]` produce `<strong>MSP</strong>` AND a chip in the same cell. The hasMarkdown / hasHtml branches in `renderCellValue` were re-ordered + DOMPurify whitelists extended so chip HTML survives both paths.
|
|
21
|
+
|
|
22
|
+
Resolution rules :
|
|
23
|
+
- Resolved id (in map) → default chip shape `[file - page]` + button with citation attrs.
|
|
24
|
+
- Unresolved id with **non-empty** map → marker dropped silently (likely LLM hallucination, mirrors typical host behavior).
|
|
25
|
+
- Unresolved id with **empty** map → human-visible `[réf. N]` placeholder.
|
|
26
|
+
- `[p.5]` page form → preserved (negative lookbehind).
|
|
27
|
+
- `[text](url)` markdown link → preserved (existing branch runs first).
|
|
28
|
+
|
|
29
|
+
CSV export of cells with citations : raw markers (`[1]`) flow through unchanged. The chip HTML is only injected at render time; the CSV path uses the original `row[key]` value, which is the right choice for re-importable exports.
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
|
|
33
|
+
- Dep bump : `@seed-ship/mcp-ui-spec` `^5.0.2` → `^5.0.3` (adds `CitationEntrySchema` + `TableComponentParamsSchema.citationMap` for cross-stack type safety).
|
|
34
|
+
|
|
35
|
+
### Tests
|
|
36
|
+
|
|
37
|
+
- `src/components/TableRenderer.citation.test.tsx` — **+15 tests** covering : no-ctx regression, single + multi chip emission, unresolved id (non-empty + empty map paths), `citationRender` override, `[p.5]` skip, markdown-link skip, mixed `**bold** [1]` compose, canonical marker shortcut, DOMPurify attr survival, button element check, plus 3 integration tests on a real `<UIResourceRenderer>` mount (no map → plain text, with map → chips in DOM, render override → custom chips).
|
|
38
|
+
- Existing 545/545 tests untouched, all still pass.
|
|
39
|
+
- Total solid suite : **560/560 tests pass** (vs 545 on v5.6.0, +15 net).
|
|
40
|
+
|
|
41
|
+
### Migration
|
|
42
|
+
|
|
43
|
+
- 100% backward compatible. `citationMap` not set → cells render exactly as before.
|
|
44
|
+
- Hosts opt in by adding `citationMap: gaResult.citation_map` to their table params.
|
|
45
|
+
- deposium_MCPs can now revert commit `7df433ae` (server-side `renderCitationChipHTML` + `replaceCitationsInCellHTML` helpers) and emit raw `[📄 CITATION N]` markers in cells with `params.citationMap` set — chip rendering happens client-side.
|
|
46
|
+
|
|
8
47
|
## [5.6.0] - 2026-04-27
|
|
9
48
|
|
|
10
49
|
Closes B.1 migration (14/17 ComponentTypes spec-driven) AND ships B.5 — UI
|
|
@@ -221,7 +221,27 @@ function highlightQuery(html, query) {
|
|
|
221
221
|
return text.replace(regex, '<mark class="bg-yellow-200 dark:bg-[#222F49] text-inherit rounded px-0.5">$1</mark>');
|
|
222
222
|
});
|
|
223
223
|
}
|
|
224
|
-
function
|
|
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) {
|
|
225
245
|
if (value === null || value === void 0) {
|
|
226
246
|
return "-";
|
|
227
247
|
}
|
|
@@ -257,20 +277,25 @@ function renderCellValue(value) {
|
|
|
257
277
|
ADD_ATTR: ["target", "rel"]
|
|
258
278
|
});
|
|
259
279
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
return purify_es.sanitize(strValue, {
|
|
263
|
-
ALLOWED_TAGS: ["a", "strong", "em", "b", "i", "code", "span", "br", "button", "svg", "path"],
|
|
264
|
-
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"],
|
|
265
|
-
ADD_ATTR: ["target", "rel"]
|
|
266
|
-
});
|
|
280
|
+
if (citationCtx) {
|
|
281
|
+
strValue = transformCellCitations(strValue, citationCtx);
|
|
267
282
|
}
|
|
268
|
-
const hasMarkdown = /[*_
|
|
283
|
+
const hasMarkdown = /[*_`#]/.test(strValue);
|
|
269
284
|
if (hasMarkdown) {
|
|
270
285
|
const parsed = marked_esm.marked.parse(strValue, {
|
|
271
286
|
async: false
|
|
272
287
|
});
|
|
273
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"],
|
|
274
299
|
ADD_ATTR: ["target", "rel"]
|
|
275
300
|
});
|
|
276
301
|
}
|
|
@@ -279,6 +304,10 @@ function renderCellValue(value) {
|
|
|
279
304
|
function TableRenderer(props) {
|
|
280
305
|
const tableParams = props.component.params;
|
|
281
306
|
let scrollContainerRef;
|
|
307
|
+
const citationCtx = tableParams.citationMap ? {
|
|
308
|
+
map: tableParams.citationMap,
|
|
309
|
+
render: tableParams.citationRender
|
|
310
|
+
} : void 0;
|
|
282
311
|
const allRows = () => tableParams.rows || [];
|
|
283
312
|
const columns = () => tableParams.columns || [];
|
|
284
313
|
const [sortKey, setSortKey] = solidJs.createSignal(null);
|
|
@@ -519,7 +548,7 @@ ${dataRows}`;
|
|
|
519
548
|
},
|
|
520
549
|
children: (column) => (() => {
|
|
521
550
|
var _el$23 = web.getNextElement(_tmpl$10), _el$24 = _el$23.firstChild;
|
|
522
|
-
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())));
|
|
523
552
|
return _el$23;
|
|
524
553
|
})()
|
|
525
554
|
}));
|
|
@@ -552,7 +581,7 @@ ${dataRows}`;
|
|
|
552
581
|
},
|
|
553
582
|
children: (column) => (() => {
|
|
554
583
|
var _el$27 = web.getNextElement(_tmpl$10), _el$28 = _el$27.firstChild;
|
|
555
|
-
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())));
|
|
556
585
|
return _el$27;
|
|
557
586
|
})()
|
|
558
587
|
}));
|