@seed-ship/mcp-ui-solid 6.22.0 → 6.23.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 +94 -0
- package/README.md +102 -0
- package/dist/components/UIResourceRenderer.cjs +15 -5
- package/dist/components/UIResourceRenderer.cjs.map +1 -1
- package/dist/components/UIResourceRenderer.d.ts +2 -1
- package/dist/components/UIResourceRenderer.d.ts.map +1 -1
- package/dist/components/UIResourceRenderer.js +15 -5
- package/dist/components/UIResourceRenderer.js.map +1 -1
- package/dist/context/MCPUIConfigContext.cjs.map +1 -1
- package/dist/context/MCPUIConfigContext.d.ts +57 -28
- package/dist/context/MCPUIConfigContext.d.ts.map +1 -1
- package/dist/context/MCPUIConfigContext.js.map +1 -1
- package/dist/context/MCPUIMathContext.cjs +19 -0
- package/dist/context/MCPUIMathContext.cjs.map +1 -0
- package/dist/context/MCPUIMathContext.d.ts +18 -0
- package/dist/context/MCPUIMathContext.d.ts.map +1 -0
- package/dist/context/MCPUIMathContext.js +19 -0
- package/dist/context/MCPUIMathContext.js.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/plugins/katex.cjs +23 -0
- package/dist/plugins/katex.cjs.map +1 -0
- package/dist/plugins/katex.d.cts +7 -0
- package/dist/plugins/katex.d.ts +7 -0
- package/dist/plugins/katex.d.ts.map +1 -0
- package/dist/plugins/katex.js +23 -0
- package/dist/plugins/katex.js.map +1 -0
- package/dist/utils/iframe-coep.cjs.map +1 -1
- package/dist/utils/iframe-coep.d.ts +7 -6
- package/dist/utils/iframe-coep.d.ts.map +1 -1
- package/dist/utils/iframe-coep.js.map +1 -1
- package/dist/utils/math-markdown.cjs +240 -0
- package/dist/utils/math-markdown.cjs.map +1 -0
- package/dist/utils/math-markdown.d.ts +16 -0
- package/dist/utils/math-markdown.d.ts.map +1 -0
- package/dist/utils/math-markdown.js +240 -0
- package/dist/utils/math-markdown.js.map +1 -0
- package/package.json +22 -3
- package/src/components/UIResourceRenderer.tsx +24 -3
- package/src/context/MCPUIConfigContext.tsx +57 -28
- package/src/context/MCPUIMathContext.tsx +32 -0
- package/src/index.ts +4 -0
- package/src/plugins/katex.ts +25 -0
- package/src/utils/iframe-coep.ts +7 -6
- package/src/utils/math-markdown.ts +222 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,95 @@ 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
|
+
## [6.23.0] - 2026-09-18
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Opt-in `MCPUIMathProvider` and `MathRenderer` for markdown text and table
|
|
13
|
+
cells, including virtualized tables; separate optional `/plugins/katex`
|
|
14
|
+
adapter. No new required config fields and no KaTeX import from the root.
|
|
15
|
+
- Bounded dollar-delimited math parsing, currency guards, isolated Marked
|
|
16
|
+
instances, sanitized MathML-only rendering and literal fallback. Styles,
|
|
17
|
+
annotation subtrees, executable markup and trusted TeX commands stay blocked.
|
|
18
|
+
- SSR keeps escaped source until the existing client hydration upgrade;
|
|
19
|
+
original text/data is retained for copy and export. No CSS or font install.
|
|
20
|
+
- Optional KaTeX peer pinned to 0.18.4 to preserve Node 20 installation
|
|
21
|
+
compatibility; later 0.18.x releases currently require Node >=22.12 via
|
|
22
|
+
Commander. Cell links retain the existing new-tab, rel and styling policy.
|
|
23
|
+
|
|
24
|
+
## [6.22.1] - 2026-09-18
|
|
25
|
+
|
|
26
|
+
Fixes a typing regression 6.22.0 introduced. Runtime behaviour is identical.
|
|
27
|
+
|
|
28
|
+
### The mistake
|
|
29
|
+
|
|
30
|
+
6.22.0 made every `MCPUIConfig` field optional so that adding a policy key
|
|
31
|
+
would stop breaking consumers who *author* a complete config. That fixed one
|
|
32
|
+
direction and broke the other: `MCPUIConfig` is a published type, so anyone who
|
|
33
|
+
*reads* one — `const policy: IframePolicy = config.iframePolicy` — suddenly
|
|
34
|
+
faced `… | undefined` and stopped compiling under `strictNullChecks`. Trading
|
|
35
|
+
one compile break for the opposite one is not a fix.
|
|
36
|
+
|
|
37
|
+
The two demands genuinely conflict, and one name cannot serve both: an author
|
|
38
|
+
must be allowed to omit a key, a reader must never meet `undefined`.
|
|
39
|
+
|
|
40
|
+
### The fix — two names
|
|
41
|
+
|
|
42
|
+
- **`MCPUIConfigInput`** (new, exported): what a host writes. Every field
|
|
43
|
+
optional, so a new policy key is always additive. This is what
|
|
44
|
+
`MCPUIConfigProvider`'s `config` prop takes. Note it is NOT what
|
|
45
|
+
`MCPUIConfigContext.Provider` takes — see below.
|
|
46
|
+
- **`MCPUIConfig`**: what the library hands back — an interface extending
|
|
47
|
+
`Required<MCPUIConfigInput>`, every key present, which is the 6.21.0 meaning
|
|
48
|
+
restored. Keeping it an interface also preserves consumer extension and
|
|
49
|
+
declaration merging. `useMCPUIConfig()`, `DEFAULT_MCPUI_CONFIG` and
|
|
50
|
+
`MCPUIConfigContext` carry it.
|
|
51
|
+
|
|
52
|
+
`MCPUIConfigContext` keeps the resolved type on purpose: it is exported, so
|
|
53
|
+
`useContext(MCPUIConfigContext)` is a public read surface and typing it with the
|
|
54
|
+
input view would leave a direct reader on `IframePolicy | undefined` — the very
|
|
55
|
+
regression being undone. A host that bypasses `MCPUIConfigProvider` therefore
|
|
56
|
+
passes a complete value, `{ ...DEFAULT_MCPUI_CONFIG, ...mine }`; passing only
|
|
57
|
+
some keys is what the provider's `config` prop is for. `useMCPUIConfig()` still
|
|
58
|
+
merges at runtime, so an untyped host cannot break a renderer.
|
|
59
|
+
|
|
60
|
+
Derived rather than declared twice, so a key is written once and the two views
|
|
61
|
+
cannot drift. The guard test now pins **both** halves: every `MCPUIConfigInput`
|
|
62
|
+
field stays optional, and `MCPUIConfig` stays an interface extending
|
|
63
|
+
`Required<…>`. The contract typecheck also pins interface extension and
|
|
64
|
+
declaration merging. Both sides were verified to fail when their corresponding
|
|
65
|
+
regression is reintroduced.
|
|
66
|
+
|
|
67
|
+
### How this got through
|
|
68
|
+
|
|
69
|
+
Through 6.22.0, `pnpm typecheck` ran the main `tsconfig.json` alone, and that
|
|
70
|
+
config excludes `**/*.test.ts(x)`: a type-level claim written in a test file
|
|
71
|
+
failed nothing. 6.22.0 shipped both a broken `MCPUIConfig` and a fixture
|
|
72
|
+
annotated with the wrong type, and neither `typecheck` nor `test` said a word.
|
|
73
|
+
`typecheck` now runs a second pass, `tsconfig.contracts.json`, which
|
|
74
|
+
type-checks the handful of test files whose `satisfies` and annotations *are*
|
|
75
|
+
the test. Including every test file instead would surface 53 unrelated errors,
|
|
76
|
+
so that stays a separate piece of work.
|
|
77
|
+
|
|
78
|
+
### Upgrading
|
|
79
|
+
|
|
80
|
+
Reading is exactly as it was in 6.21.0, whether through `useMCPUIConfig()`,
|
|
81
|
+
`useContext(MCPUIConfigContext)` or a value typed `MCPUIConfig`.
|
|
82
|
+
`MCPUIConfigProvider`'s `config` prop still accepts everything it accepted
|
|
83
|
+
before, `Partial<MCPUIConfig>` included.
|
|
84
|
+
|
|
85
|
+
Two authoring patterns stop compiling, coming from 6.21.0 or from 6.22.0:
|
|
86
|
+
|
|
87
|
+
- **A config annotated `MCPUIConfig`.** From 6.22.0 a partial one fails; from
|
|
88
|
+
6.21.0 a complete one fails too, since it lacks `chartZoom`. Annotate it
|
|
89
|
+
`MCPUIConfigInput` instead.
|
|
90
|
+
- **A value handed straight to `<MCPUIConfigContext.Provider>`**, annotated or
|
|
91
|
+
not: an inline literal fails the same way. Spread the defaults,
|
|
92
|
+
`{ ...DEFAULT_MCPUI_CONFIG, ...mine }`, or pass the keys to
|
|
93
|
+
`<MCPUIConfigProvider config={…}>`, which does that merge for you.
|
|
94
|
+
|
|
95
|
+
Both replacements keep compiling when the library adds its next policy key.
|
|
96
|
+
|
|
8
97
|
## [6.22.0] - 2026-09-18
|
|
9
98
|
|
|
10
99
|
Wheel zoom and drag pan on the surfaces that could not be read when the content
|
|
@@ -50,6 +139,11 @@ expanded view only, and why inline zoom is opt-in.
|
|
|
50
139
|
|
|
51
140
|
### Changed
|
|
52
141
|
|
|
142
|
+
> **Superseded by 6.22.1.** Making the fields of the published `MCPUIConfig`
|
|
143
|
+
> optional broke every consumer that *reads* one. 6.22.1 moves the optional
|
|
144
|
+
> fields to a new `MCPUIConfigInput` and restores `MCPUIConfig` as the
|
|
145
|
+
> resolved type. The entry below describes 6.22.0 as published.
|
|
146
|
+
|
|
53
147
|
- **Every `MCPUIConfig` field is now optional**, as `MCPUIStrings` has always
|
|
54
148
|
been. `MCPUIConfig` grows a key in a minor release each time the library
|
|
55
149
|
gains a policy switch — `iframeFallbackLink` in 6.21.0, `chartZoom` here —
|
package/README.md
CHANGED
|
@@ -5,6 +5,65 @@ SolidJS components + chat toolkit for MCP-generated UI. Part of the [MCP UI ecos
|
|
|
5
5
|
[](https://www.npmjs.com/package/@seed-ship/mcp-ui-solid)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
|
|
8
|
+
## What's New in v6.23.0
|
|
9
|
+
|
|
10
|
+
Opt-in LaTeX in markdown text components and table cells, with the same
|
|
11
|
+
currency guards and sanitized MathML output. No change without a provider;
|
|
12
|
+
`MCPUIConfig` is unchanged. See [Math rendering](#math-rendering).
|
|
13
|
+
|
|
14
|
+
## Math rendering
|
|
15
|
+
|
|
16
|
+
Install the optional peer only if the host wants the supplied KaTeX adapter:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm add --save-exact katex@0.18.4
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
import { MCPUIMathProvider, UIResourceRenderer } from '@seed-ship/mcp-ui-solid'
|
|
24
|
+
import { renderKatexMath } from '@seed-ship/mcp-ui-solid/plugins/katex'
|
|
25
|
+
|
|
26
|
+
<MCPUIMathProvider renderMath={renderKatexMath}>
|
|
27
|
+
<UIResourceRenderer content={resource} />
|
|
28
|
+
</MCPUIMathProvider>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The adapter's optional peer is pinned to 0.18.4 to retain Node 20 installation
|
|
32
|
+
compatibility. KaTeX 0.18.5–0.18.7 pull in Commander 15 (Node >=22.12);
|
|
33
|
+
do not replace the pin with a caret range on Node 20.
|
|
34
|
+
|
|
35
|
+
The provider applies to `text` with `markdown: true` and string-valued table
|
|
36
|
+
cells, including virtualized rows. It recognizes `$...$` inline and `$$...$$`
|
|
37
|
+
display formulas in their own paragraph (not embedded mid-sentence).
|
|
38
|
+
Code spans/fences and link destinations are not formulas;
|
|
39
|
+
currency such as `$5` and `$5-$10` remains text. Malformed/incomplete formulas
|
|
40
|
+
degrade to their source. Copy/export continues to use the original data.
|
|
41
|
+
Dollar syntax can be ambiguous: `$5$` is a formula. Escape a literal dollar
|
|
42
|
+
as `\$` when composing currency and formulas; numeric math such as `$2 + 3$`
|
|
43
|
+
remains supported. Math-enabled cell links keep their existing new-tab behavior.
|
|
44
|
+
|
|
45
|
+
The adapter emits **MathML only**: no KaTeX stylesheet, fonts, CDN requests,
|
|
46
|
+
HTML layout or inline styles. Native MathML rendering varies by browser and
|
|
47
|
+
may differ from the chat's HTML+MathML layout. There is no automatic loading:
|
|
48
|
+
the explicit plugin import is the opt-in dependency boundary; hosts can load
|
|
49
|
+
that module before mounting the provider if they want a separate chunk.
|
|
50
|
+
|
|
51
|
+
Alternatively pass a synchronous `MathRenderer` function returning MathML
|
|
52
|
+
(`(tex, { displayMode }) => string | null`). The library still sanitizes it;
|
|
53
|
+
return `null` or throw to retain literal source. Passing `null` (or omitting
|
|
54
|
+
`renderMath`) on a nested provider disables inherited math. Arbitrary Marked
|
|
55
|
+
extensions and sanitizer overrides are deliberately not public API.
|
|
56
|
+
|
|
57
|
+
On the server, escaped source is emitted without calling the renderer. The
|
|
58
|
+
existing `SafeHtml` client upgrade renders sanitized formulas after hydration.
|
|
59
|
+
Processing is bounded to 100,000 source characters, 4,096 per expression and
|
|
60
|
+
128 expressions per parse; oversized input stays escaped text. The adapter
|
|
61
|
+
also limits macro expansion and dimensions, disables trusted commands, and
|
|
62
|
+
does not share macro definitions across formulas. TeX annotation subtrees are
|
|
63
|
+
removed rather than exposing duplicate source after sanitization.
|
|
64
|
+
|
|
65
|
+
See the [design and integration handoff](./docs/briefs/LATEX-MATHML-2026-09-18.md).
|
|
66
|
+
|
|
8
67
|
## What's New in v6.21.0
|
|
9
68
|
|
|
10
69
|
- **Iframes work under `Cross-Origin-Embedder-Policy: credentialless`.** Both
|
|
@@ -1029,6 +1088,48 @@ import { MCPUIConfigProvider } from '@seed-ship/mcp-ui-solid'
|
|
|
1029
1088
|
</MCPUIConfigProvider>
|
|
1030
1089
|
```
|
|
1031
1090
|
|
|
1091
|
+
When you keep a partial configuration in a variable, name the authored,
|
|
1092
|
+
all-optional view explicitly:
|
|
1093
|
+
|
|
1094
|
+
```tsx
|
|
1095
|
+
import {
|
|
1096
|
+
MCPUIConfigProvider,
|
|
1097
|
+
type MCPUIConfigInput,
|
|
1098
|
+
} from '@seed-ship/mcp-ui-solid'
|
|
1099
|
+
|
|
1100
|
+
const config: MCPUIConfigInput = {
|
|
1101
|
+
iframeCredentialless: 'always',
|
|
1102
|
+
chartZoom: 'never',
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
<MCPUIConfigProvider config={config}>
|
|
1106
|
+
<App />
|
|
1107
|
+
</MCPUIConfigProvider>
|
|
1108
|
+
```
|
|
1109
|
+
|
|
1110
|
+
Prefer `MCPUIConfigProvider` for partial overrides. The exported raw context is
|
|
1111
|
+
also a public read surface, so its `Provider` requires the complete, resolved
|
|
1112
|
+
`MCPUIConfig` value. If an integration must provide that context directly,
|
|
1113
|
+
spread the defaults first:
|
|
1114
|
+
|
|
1115
|
+
```tsx
|
|
1116
|
+
import {
|
|
1117
|
+
DEFAULT_MCPUI_CONFIG,
|
|
1118
|
+
MCPUIConfigContext,
|
|
1119
|
+
} from '@seed-ship/mcp-ui-solid'
|
|
1120
|
+
|
|
1121
|
+
<MCPUIConfigContext.Provider
|
|
1122
|
+
value={{ ...DEFAULT_MCPUI_CONFIG, iframeCredentialless: 'always' }}
|
|
1123
|
+
>
|
|
1124
|
+
<App />
|
|
1125
|
+
</MCPUIConfigContext.Provider>
|
|
1126
|
+
```
|
|
1127
|
+
|
|
1128
|
+
`MCPUIConfigInput` contains the library's built-in authoring fields.
|
|
1129
|
+
`MCPUIConfig` remains an interface for consumers that extend or augment the
|
|
1130
|
+
resolved contract; `MCPUIConfigProvider` also accepts those augmented fields
|
|
1131
|
+
as partial inline overrides.
|
|
1132
|
+
|
|
1032
1133
|
| Option | Values | Default | What it does |
|
|
1033
1134
|
|--------|--------|---------|--------------|
|
|
1034
1135
|
| `iframeCredentialless` | `'auto'` \| `'always'` \| `'never'` | `'auto'` | `'auto'`: the attribute on the `iframe` component when its host is **not** in `TRUSTED_IFRAME_DOMAINS` (+ `customTrustedIframeDomains`) — those frames already run without cookies. The video embed is excluded, since it has none of that sandboxing. `'always'`: every iframe, video and trusted hosts included — what a COEP host wants. `'never'`: none. |
|
|
@@ -1966,6 +2067,7 @@ import type {
|
|
|
1966
2067
|
IframePolicy, ValidationOptions,
|
|
1967
2068
|
CitationCtx, CitationEntry, DuplicateMountInfo, DuplicateMountReporter,
|
|
1968
2069
|
MCPUIStrings, MCPUIConfig, // v6.21.0
|
|
2070
|
+
MCPUIConfigInput, // v6.22.1 — the authored, all-optional view
|
|
1969
2071
|
TelemetryEvent, TelemetrySink, TelemetryOptions, TelemetryDispatcher,
|
|
1970
2072
|
DataValidation, HallucinatedNumber, DataValidationOptions,
|
|
1971
2073
|
VerifiedTextContent, DataPreviewContent, MapSectionContent,
|
|
@@ -60,6 +60,8 @@ const RenderContext = require("./RenderContext.cjs");
|
|
|
60
60
|
const useAction = require("../hooks/useAction.cjs");
|
|
61
61
|
const createResponsiveGrid = require("../hooks/createResponsiveGrid.cjs");
|
|
62
62
|
const marked = require("marked");
|
|
63
|
+
const MCPUIMathContext = require("../context/MCPUIMathContext.cjs");
|
|
64
|
+
const mathMarkdown = require("../utils/math-markdown.cjs");
|
|
63
65
|
const formatString = require("../utils/format-string.cjs");
|
|
64
66
|
var _tmpl$ = /* @__PURE__ */ web.template(`<svg class="w-3 h-3 text-gray-500 dark:text-gray-400"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z">`), _tmpl$2 = /* @__PURE__ */ web.template(`<button data-mcp-ui-action=copy type=button>`), _tmpl$3 = /* @__PURE__ */ web.template(`<svg class="w-3 h-3 text-green-500"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M5 13l4 4L19 7">`), _tmpl$4 = /* @__PURE__ */ web.template(`<div class="p-4 bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700"><p class="text-red-500 dark:text-red-400 text-sm">`), _tmpl$5 = /* @__PURE__ */ web.template(`<div class=p-3>`), _tmpl$6 = /* @__PURE__ */ web.template(`<div class="absolute inset-0 flex items-center justify-center"><div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600">`), _tmpl$7 = /* @__PURE__ */ web.template(`<div class="absolute inset-0 flex items-center justify-center p-4"><div class=text-center><p class="text-red-600 dark:text-red-400 text-sm font-medium"></p><p class="text-gray-600 dark:text-gray-400 text-xs mt-1">`), _tmpl$8 = /* @__PURE__ */ web.template(`<h3 class="text-sm font-semibold text-gray-900 dark:text-white mb-3">`), _tmpl$9 = /* @__PURE__ */ web.template(`<div class="w-full h-full p-4"><!$><!/><div class="w-full h-full"role=img><img class="w-full h-auto max-h-[300px] object-contain">`), _tmpl$0 = /* @__PURE__ */ web.template(`<div class="relative w-full h-full min-h-[300px] bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden"><!$><!/><!$><!/><!$><!/><!$><!/>`), _tmpl$1 = /* @__PURE__ */ web.template(`<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">`), _tmpl$10 = /* @__PURE__ */ web.template(`<tr>`), _tmpl$11 = /* @__PURE__ */ web.template(`<td class="px-6 py-4 text-sm text-gray-700 dark:text-gray-200 whitespace-normal break-words leading-relaxed first:pl-6 last:pr-6">`), _tmpl$12 = /* @__PURE__ */ web.template(`<tbody class="bg-white dark:bg-gray-800 relative">`), _tmpl$13 = /* @__PURE__ */ web.template(`<button class="w-full text-left px-3 py-1.5 hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300">`), _tmpl$14 = /* @__PURE__ */ web.template(`<div class="absolute right-10 top-2 z-10"><button class="opacity-60 hover:opacity-100 px-2 py-1 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-all shadow-sm"aria-haspopup=menu><svg class="w-3 h-3 text-gray-500 dark:text-gray-400"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg></button><!$><!/>`), _tmpl$15 = /* @__PURE__ */ web.template(`<span class="ml-2 text-xs font-normal text-gray-400">`), _tmpl$16 = /* @__PURE__ */ web.template(`<h3 class="text-sm font-semibold text-gray-900 dark:text-white mb-3 flex-shrink-0"><!$><!/><!$><!/>`), _tmpl$17 = /* @__PURE__ */ web.template(`<button type=button class="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 text-sm">×`), _tmpl$18 = /* @__PURE__ */ web.template(`<div class="relative mb-3"><span class="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 pointer-events-none text-sm">🔍</span><input type=text class="w-full max-w-xs min-w-[200px] pl-8 pr-8 py-1.5 text-sm border border-gray-200 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-white placeholder-gray-400 focus:border-blue-400 focus:ring-1 focus:ring-blue-400 outline-none"><!$><!/>`), _tmpl$19 = /* @__PURE__ */ web.template(`<p class="text-xs text-gray-500 dark:text-gray-400 mb-2">`), _tmpl$20 = /* @__PURE__ */ web.template(`<div class="mt-3 flex items-center justify-between text-xs text-gray-500 dark:text-gray-400"><span>`), _tmpl$21 = /* @__PURE__ */ web.template(`<select class="ml-2 px-1 py-0.5 text-xs border border-gray-200 dark:border-gray-600 rounded bg-white dark:bg-gray-700 text-gray-700 dark:text-gray-300"data-mcp-ui-action=page-size>`), _tmpl$22 = /* @__PURE__ */ web.template(`<span class=text-gray-400>`), _tmpl$23 = /* @__PURE__ */ web.template(`<div class="mt-3 flex items-center justify-between text-xs text-gray-500 dark:text-gray-400"><span><!$><!/>–<!$><!/> / <!$><!/></span><div class="flex items-center gap-2"><button type=button class="px-2 py-1 rounded hover:bg-gray-100 dark:hover:bg-gray-700 disabled:opacity-40 disabled:cursor-not-allowed transition-colors"data-mcp-ui-action=page-prev><span aria-hidden=true>◀</span></button><span aria-live=polite><!$><!/> / <!$><!/></span><button type=button class="px-2 py-1 rounded hover:bg-gray-100 dark:hover:bg-gray-700 disabled:opacity-40 disabled:cursor-not-allowed transition-colors"data-mcp-ui-action=page-next><span aria-hidden=true>▶</span></button><!$><!/>`), _tmpl$24 = /* @__PURE__ */ web.template(`<div><!$><!/><div><!$><!/><!$><!/><div role=region tabindex=0><table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700 border-separate border-spacing-0"><thead class="bg-gray-100 dark:bg-gray-900 sticky top-0 z-10"><tr></tr></thead><!$><!/></table></div><!$><!/><!$><!/>`), _tmpl$25 = /* @__PURE__ */ web.template(`<th scope=col class="px-6 py-3 text-left text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider border-b border-gray-200 dark:border-gray-700 first:pl-6 last:pr-6 bg-gray-100 dark:bg-gray-900 cursor-pointer select-none hover:bg-gray-200 dark:hover:bg-gray-800 transition-colors"><span class="inline-flex items-center gap-1"><!$><!/><span class="text-[10px] leading-none">`), _tmpl$26 = /* @__PURE__ */ web.template(`<option>`), _tmpl$27 = /* @__PURE__ */ web.template(`<span class="ml-2 text-sm font-medium text-gray-500 dark:text-gray-400">`), _tmpl$28 = /* @__PURE__ */ web.template(`<div class="mt-3 flex items-center"><span><!$><!/> <!$><!/>%`), _tmpl$29 = /* @__PURE__ */ web.template(`<p class="mt-2 text-xs text-gray-500 dark:text-gray-400">`), _tmpl$30 = /* @__PURE__ */ web.template(`<div class="relative w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4 group"><!$><!/><div class="flex flex-col h-full justify-between"><div><p class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide"></p><div class="mt-2 flex items-baseline"><p class="text-2xl font-semibold text-gray-900 dark:text-white"></p><!$><!/></div></div><!$><!/><!$><!/>`), _tmpl$31 = /* @__PURE__ */ web.template(`<div class="relative w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4 group"><!$><!/><!$><!/>`), _tmpl$32 = /* @__PURE__ */ web.template(`<img class="max-w-full max-h-[400px] object-contain rounded shadow-sm hover:opacity-90 transition-opacity"loading=lazy>`, true, false, false), _tmpl$33 = /* @__PURE__ */ web.template(`<div class="w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden flex flex-col"><div class="flex-1 flex items-center justify-center p-4 bg-gray-50 dark:bg-gray-900 min-h-[200px]"></div><div class="p-3 border-t border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800"><p class="text-sm text-gray-600 dark:text-gray-400 text-center italic">`), _tmpl$34 = /* @__PURE__ */ web.template(`<a target=_blank rel="noopener noreferrer"class=cursor-zoom-in>`), _tmpl$35 = /* @__PURE__ */ web.template(`<div class="px-4 py-2 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900"><h3 class="text-sm font-semibold text-gray-900 dark:text-white">`), _tmpl$36 = /* @__PURE__ */ web.template(`<div class="w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden flex flex-col"><!$><!/><iframe class="w-full border-0 flex-1"loading=lazy></iframe><!$><!/>`, true, false, false), _tmpl$37 = /* @__PURE__ */ web.template(`<figcaption class="p-3 border-t border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800"><p class="text-sm text-gray-600 dark:text-gray-400 text-center">`), _tmpl$38 = /* @__PURE__ */ web.template(`<figure><div class="flex-1 flex items-center justify-center p-4 bg-gray-50 dark:bg-gray-900 min-h-[200px]"><a target=_blank rel="noopener noreferrer"class="cursor-zoom-in focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 rounded"><img class="max-w-full max-h-[500px] object-contain rounded shadow-sm hover:opacity-95 transition-opacity"loading=lazy></a></div><!$><!/>`, true, false, false), _tmpl$39 = /* @__PURE__ */ web.template(`<p class="text-xs text-gray-500 dark:text-gray-400 truncate">`), _tmpl$40 = /* @__PURE__ */ web.template(`<a target=_blank rel="noopener noreferrer"><div class="p-2 bg-blue-50 dark:bg-blue-900/30 rounded-full text-blue-600 dark:text-blue-400 group-hover:bg-blue-100 dark:group-hover:bg-blue-900/50 shrink-0 transition-colors"aria-hidden=true><svg xmlns=http://www.w3.org/2000/svg class="w-5 h-5"viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg></div><div class="flex-1 min-w-0"><h4 class="text-sm font-medium text-gray-900 dark:text-white truncate group-hover:text-blue-600 dark:group-hover:text-blue-400 transition-colors"></h4><!$><!/></div><svg xmlns=http://www.w3.org/2000/svg class="w-4 h-4 text-gray-400 group-hover:text-gray-600 dark:group-hover:text-gray-300 shrink-0 transition-colors"viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round aria-hidden=true><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1=10 y1=14 x2=21 y2=3>`), _tmpl$41 = /* @__PURE__ */ web.template(`<div class="inline-flex items-center gap-1.5 px-2 py-1 rounded-md bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 text-xs text-yellow-800 dark:text-yellow-200"role=alert><svg xmlns=http://www.w3.org/2000/svg class="w-3.5 h-3.5"viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round aria-hidden=true><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1=12 y1=9 x2=12 y2=13></line><line x1=12 y1=17 x2=12.01 y2=17></line></svg><span>`), _tmpl$42 = /* @__PURE__ */ web.template(`<div class="w-full h-full bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4"><p class="text-sm font-medium text-red-900 dark:text-red-100"></p><p class="text-xs text-red-700 dark:text-red-300 mt-1">`), _tmpl$43 = /* @__PURE__ */ web.template(`<div role=alert class="w-full rounded-lg border border-amber-300 bg-amber-50 px-3 py-2 text-sm text-amber-800 dark:border-amber-700/50 dark:bg-amber-900/20 dark:text-amber-200"><!$><!/> <code class=font-mono>`), _tmpl$44 = /* @__PURE__ */ web.template(`<span aria-hidden=true>`), _tmpl$45 = /* @__PURE__ */ web.template(`<a rel="noopener noreferrer"><!$><!/><!$><!/>`), _tmpl$46 = /* @__PURE__ */ web.template(`<span class="animate-spin h-4 w-4 border-2 border-current border-t-transparent rounded-full"aria-hidden=true>`), _tmpl$47 = /* @__PURE__ */ web.template(`<button type=button><!$><!/><!$><!/><!$><!/>`), _tmpl$48 = /* @__PURE__ */ web.template(`<p class="text-xs text-red-600 dark:text-red-400 mt-1">`), _tmpl$49 = /* @__PURE__ */ web.template(`<div class=mt-3><p class="text-xs font-medium text-red-700 dark:text-red-300"></p><ul class="mt-1 text-xs text-red-600 dark:text-red-400 list-disc list-inside">`), _tmpl$50 = /* @__PURE__ */ web.template(`<p class="text-xs text-red-500 dark:text-red-500 mt-2">`), _tmpl$51 = /* @__PURE__ */ web.template(`<div class="relative w-full bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4 group"><!$><!/><div class="flex items-start gap-3"><div class="p-2 bg-red-100 dark:bg-red-900/40 rounded-full shrink-0"><svg class="w-5 h-5 text-red-600 dark:text-red-400"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path></svg></div><div class="flex-1 min-w-0"><h4 class="text-sm font-semibold text-red-800 dark:text-red-200"></h4><p class="text-sm text-red-700 dark:text-red-300 mt-1"></p><!$><!/><!$><!/><!$><!/>`), _tmpl$52 = /* @__PURE__ */ web.template(`<li>`), _tmpl$53 = /* @__PURE__ */ web.template(`<div class="px-4 py-2 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900"><h3 class="text-sm font-semibold text-gray-900 dark:text-white capitalize">`), _tmpl$54 = /* @__PURE__ */ web.template(`<div class="w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden"><!$><!/><!$><!/>`), _tmpl$55 = /* @__PURE__ */ web.template(`<div><div class="grid gap-4"data-mcp-ui-grid></div><!$><!/>`), _tmpl$56 = /* @__PURE__ */ web.template(`<div class=min-w-0>`);
|
|
65
67
|
function CopyButton(props) {
|
|
@@ -308,7 +310,7 @@ function ChartRenderer(props) {
|
|
|
308
310
|
}
|
|
309
311
|
const HIGHLIGHT_MARK_CLASS = "bg-yellow-200 dark:bg-[#222F49] text-inherit rounded px-0.5";
|
|
310
312
|
function highlightQuery(html, query) {
|
|
311
|
-
var _a;
|
|
313
|
+
var _a, _b;
|
|
312
314
|
const q = query.trim();
|
|
313
315
|
if (!q) return html;
|
|
314
316
|
if (typeof document === "undefined") return html;
|
|
@@ -319,6 +321,7 @@ function highlightQuery(html, query) {
|
|
|
319
321
|
const walker = document.createTreeWalker(template.content, NodeFilter.SHOW_TEXT);
|
|
320
322
|
const textNodes = [];
|
|
321
323
|
for (let node = walker.nextNode(); node; node = walker.nextNode()) {
|
|
324
|
+
if ((_a = node.parentElement) == null ? void 0 : _a.closest("math")) continue;
|
|
322
325
|
textNodes.push(node);
|
|
323
326
|
}
|
|
324
327
|
for (const node of textNodes) {
|
|
@@ -346,7 +349,7 @@ function highlightQuery(html, query) {
|
|
|
346
349
|
if (cursor < text.length) {
|
|
347
350
|
fragment.appendChild(document.createTextNode(text.slice(cursor)));
|
|
348
351
|
}
|
|
349
|
-
(
|
|
352
|
+
(_b = node.parentNode) == null ? void 0 : _b.replaceChild(fragment, node);
|
|
350
353
|
}
|
|
351
354
|
return template.innerHTML;
|
|
352
355
|
}
|
|
@@ -374,7 +377,7 @@ function transformCellCitations(text, ctx) {
|
|
|
374
377
|
});
|
|
375
378
|
});
|
|
376
379
|
}
|
|
377
|
-
function renderCellValue(value, citationCtx) {
|
|
380
|
+
function renderCellValue(value, citationCtx, mathRenderer) {
|
|
378
381
|
if (value === null || value === void 0) {
|
|
379
382
|
return "-";
|
|
380
383
|
}
|
|
@@ -404,6 +407,9 @@ function renderCellValue(value, citationCtx) {
|
|
|
404
407
|
if (strValue.trim() === "") {
|
|
405
408
|
return "-";
|
|
406
409
|
}
|
|
410
|
+
if (mathRenderer && strValue.includes("$")) {
|
|
411
|
+
return mathMarkdown.parseMathMarkdown(strValue, mathRenderer, "cellMarkdown", citationCtx ? (text) => transformCellCitations(text, citationCtx) : void 0);
|
|
412
|
+
}
|
|
407
413
|
const markdownLinkRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
|
|
408
414
|
if (markdownLinkRegex.test(strValue)) {
|
|
409
415
|
const htmlValue = strValue.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer" class="text-blue-600 dark:text-blue-400 hover:underline">$1</a>');
|
|
@@ -429,6 +435,7 @@ function TableRenderer(props) {
|
|
|
429
435
|
const tableParams = props.component.params;
|
|
430
436
|
let scrollContainerRef;
|
|
431
437
|
const strings = MCPUIStringsContext.useMCPUIStrings();
|
|
438
|
+
const mathRenderer = MCPUIMathContext.useMCPUIMath();
|
|
432
439
|
const citationCtx = tableParams.citationMap ? {
|
|
433
440
|
map: tableParams.citationMap,
|
|
434
441
|
render: tableParams.citationRender,
|
|
@@ -688,7 +695,7 @@ ${dataRows}`;
|
|
|
688
695
|
children: (column) => (() => {
|
|
689
696
|
var _el$27 = web.getNextElement(_tmpl$11);
|
|
690
697
|
web.insert(_el$27, web.createComponent(SafeHtml.SafeHtml, {
|
|
691
|
-
html: () => highlightQuery(renderCellValue(row[column.key], citationCtx), debouncedQuery())
|
|
698
|
+
html: () => highlightQuery(renderCellValue(row[column.key], citationCtx, mathRenderer()), debouncedQuery())
|
|
692
699
|
}));
|
|
693
700
|
return _el$27;
|
|
694
701
|
})()
|
|
@@ -723,7 +730,7 @@ ${dataRows}`;
|
|
|
723
730
|
children: (column) => (() => {
|
|
724
731
|
var _el$30 = web.getNextElement(_tmpl$11);
|
|
725
732
|
web.insert(_el$30, web.createComponent(SafeHtml.SafeHtml, {
|
|
726
|
-
html: () => highlightQuery(renderCellValue(row[column.key], citationCtx), debouncedQuery())
|
|
733
|
+
html: () => highlightQuery(renderCellValue(row[column.key], citationCtx, mathRenderer()), debouncedQuery())
|
|
727
734
|
}));
|
|
728
735
|
return _el$30;
|
|
729
736
|
})()
|
|
@@ -1144,6 +1151,7 @@ function extractImageFromMarkdown(content, fallbackAlt) {
|
|
|
1144
1151
|
}
|
|
1145
1152
|
function TextRenderer(props) {
|
|
1146
1153
|
const strings = MCPUIStringsContext.useMCPUIStrings();
|
|
1154
|
+
const mathRenderer = MCPUIMathContext.useMCPUIMath();
|
|
1147
1155
|
const textParams = props.component.params;
|
|
1148
1156
|
const imageData = solidJs.createMemo(() => {
|
|
1149
1157
|
if (textParams.markdown && textParams.content) {
|
|
@@ -1168,6 +1176,8 @@ function TextRenderer(props) {
|
|
|
1168
1176
|
return escapeHtml.escapeHtml(String(raw));
|
|
1169
1177
|
}
|
|
1170
1178
|
if (textParams.markdown && !imageData()) {
|
|
1179
|
+
const renderMath = mathRenderer();
|
|
1180
|
+
if (renderMath) return mathMarkdown.parseMathMarkdown(String(raw), renderMath, "prose");
|
|
1171
1181
|
return sanitizeHtml.sanitizeHtml(marked.marked.parse(raw, {
|
|
1172
1182
|
async: false
|
|
1173
1183
|
}), sanitizeHtml.SANITIZE_PROFILES.prose);
|