@seed-ship/mcp-ui-solid 6.5.0 → 6.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/CHANGELOG.md +161 -0
  2. package/README.md +37 -0
  3. package/dist/adapters/connector.cjs +112 -0
  4. package/dist/adapters/connector.cjs.map +1 -0
  5. package/dist/adapters/connector.d.ts +71 -0
  6. package/dist/adapters/connector.d.ts.map +1 -0
  7. package/dist/adapters/connector.js +112 -0
  8. package/dist/adapters/connector.js.map +1 -0
  9. package/dist/adapters/index.d.ts +18 -0
  10. package/dist/adapters/index.d.ts.map +1 -0
  11. package/dist/adapters.cjs +6 -0
  12. package/dist/adapters.cjs.map +1 -0
  13. package/dist/adapters.d.cts +18 -0
  14. package/dist/adapters.d.ts +18 -0
  15. package/dist/adapters.js +6 -0
  16. package/dist/adapters.js.map +1 -0
  17. package/dist/components/ActionGroupRenderer.cjs +12 -3
  18. package/dist/components/ActionGroupRenderer.cjs.map +1 -1
  19. package/dist/components/ActionGroupRenderer.d.ts.map +1 -1
  20. package/dist/components/ActionGroupRenderer.js +12 -3
  21. package/dist/components/ActionGroupRenderer.js.map +1 -1
  22. package/dist/components/ExpandableWrapper.cjs +24 -6
  23. package/dist/components/ExpandableWrapper.cjs.map +1 -1
  24. package/dist/components/ExpandableWrapper.d.ts.map +1 -1
  25. package/dist/components/ExpandableWrapper.js +24 -6
  26. package/dist/components/ExpandableWrapper.js.map +1 -1
  27. package/dist/components/FeedbackInline.cjs +6 -2
  28. package/dist/components/FeedbackInline.cjs.map +1 -1
  29. package/dist/components/FeedbackInline.d.ts +2 -2
  30. package/dist/components/FeedbackInline.d.ts.map +1 -1
  31. package/dist/components/FeedbackInline.js +7 -3
  32. package/dist/components/FeedbackInline.js.map +1 -1
  33. package/dist/components/PresentationFeedback.cjs +207 -0
  34. package/dist/components/PresentationFeedback.cjs.map +1 -0
  35. package/dist/components/PresentationFeedback.d.ts +113 -0
  36. package/dist/components/PresentationFeedback.d.ts.map +1 -0
  37. package/dist/components/PresentationFeedback.js +207 -0
  38. package/dist/components/PresentationFeedback.js.map +1 -0
  39. package/dist/components/StreamingUIRenderer.cjs +82 -195
  40. package/dist/components/StreamingUIRenderer.cjs.map +1 -1
  41. package/dist/components/StreamingUIRenderer.d.ts +25 -5
  42. package/dist/components/StreamingUIRenderer.d.ts.map +1 -1
  43. package/dist/components/StreamingUIRenderer.js +84 -197
  44. package/dist/components/StreamingUIRenderer.js.map +1 -1
  45. package/dist/components/UIResourceRenderer.cjs +22 -15
  46. package/dist/components/UIResourceRenderer.cjs.map +1 -1
  47. package/dist/components/UIResourceRenderer.d.ts.map +1 -1
  48. package/dist/components/UIResourceRenderer.js +22 -15
  49. package/dist/components/UIResourceRenderer.js.map +1 -1
  50. package/dist/components/index.d.ts +2 -0
  51. package/dist/components/index.d.ts.map +1 -1
  52. package/dist/components.cjs +3 -0
  53. package/dist/components.cjs.map +1 -1
  54. package/dist/components.d.cts +2 -0
  55. package/dist/components.d.ts +2 -0
  56. package/dist/components.js +3 -0
  57. package/dist/components.js.map +1 -1
  58. package/dist/context/MCPActionContext.cjs +4 -1
  59. package/dist/context/MCPActionContext.cjs.map +1 -1
  60. package/dist/context/MCPActionContext.d.ts +13 -1
  61. package/dist/context/MCPActionContext.d.ts.map +1 -1
  62. package/dist/context/MCPActionContext.js +4 -1
  63. package/dist/context/MCPActionContext.js.map +1 -1
  64. package/dist/context/MCPUIStringsContext.cjs +38 -0
  65. package/dist/context/MCPUIStringsContext.cjs.map +1 -0
  66. package/dist/context/MCPUIStringsContext.d.ts +95 -0
  67. package/dist/context/MCPUIStringsContext.d.ts.map +1 -0
  68. package/dist/context/MCPUIStringsContext.js +38 -0
  69. package/dist/context/MCPUIStringsContext.js.map +1 -0
  70. package/dist/index.cjs +8 -0
  71. package/dist/index.cjs.map +1 -1
  72. package/dist/index.d.cts +5 -0
  73. package/dist/index.d.ts +5 -0
  74. package/dist/index.d.ts.map +1 -1
  75. package/dist/index.js +8 -0
  76. package/dist/index.js.map +1 -1
  77. package/dist/mcp-ui-spec/dist/schemas.cjs +103 -0
  78. package/dist/mcp-ui-spec/dist/schemas.cjs.map +1 -1
  79. package/dist/mcp-ui-spec/dist/schemas.js +103 -0
  80. package/dist/mcp-ui-spec/dist/schemas.js.map +1 -1
  81. package/docs/briefs/ROADMAP-opendata-macro-mcpui.md +912 -0
  82. package/package.json +17 -5
  83. package/src/adapters/connector.test.ts +165 -0
  84. package/src/adapters/connector.ts +234 -0
  85. package/src/adapters/index.ts +24 -0
  86. package/src/components/ActionGroupRenderer.test.tsx +1 -0
  87. package/src/components/ActionGroupRenderer.tsx +19 -4
  88. package/src/components/ActionSubmit.test.tsx +188 -0
  89. package/src/components/ExpandableWrapper.test.tsx +5 -2
  90. package/src/components/ExpandableWrapper.tsx +8 -6
  91. package/src/components/FeedbackInline.test.tsx +6 -3
  92. package/src/components/FeedbackInline.tsx +8 -6
  93. package/src/components/PresentationFeedback.test.tsx +163 -0
  94. package/src/components/PresentationFeedback.tsx +326 -0
  95. package/src/components/StreamingUIRenderer.parity.test.tsx +158 -0
  96. package/src/components/StreamingUIRenderer.tsx +42 -166
  97. package/src/components/UIResourceRenderer.tsx +19 -6
  98. package/src/components/index.ts +10 -0
  99. package/src/context/MCPActionContext.tsx +17 -1
  100. package/src/context/MCPUIStringsContext.test.tsx +116 -0
  101. package/src/context/MCPUIStringsContext.tsx +128 -0
  102. package/src/index.ts +27 -0
  103. package/tsconfig.tsbuildinfo +1 -1
  104. package/vite.config.ts +1 -0
package/CHANGELOG.md CHANGED
@@ -5,6 +5,167 @@ 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.6.1] - 2026-05-22
9
+
10
+ ### Fixed — `action: 'submit'` is no longer inert outside a `<form>`
11
+
12
+ `ActionParamsSchema.action` allows `'tool-call' | 'link' | 'submit'`, but
13
+ at runtime `submit` did nothing : `ActionGroupRenderer` only branched on
14
+ `tool-call` / `link`, and the standalone `action` renderer emitted a
15
+ native `type="submit"` button — which only fires inside a real `<form>`.
16
+ Standalone resources rendered by `<UIResourceRenderer>` have no
17
+ surrounding form, so the click was inert.
18
+
19
+ `submit` actions now route through the host executor — like `tool-call`,
20
+ but **not treated as a tool call** :
21
+
22
+ - `<ActionGroupRenderer>` and the standalone `action` renderer both call
23
+ `executeAction({ action: 'submit', toolName, params })` on click.
24
+ - The action **kind** is preserved : `ActionRequest` gains an optional
25
+ `action?: 'tool-call' | 'submit' | 'link'` field so a host `executor`
26
+ can tell a submit apart from a tool call (e.g. POST to
27
+ `params.submit_url`). The full `params` payload (`submit_url`,
28
+ `connector_id`, `feedback_value`, `preferred_layout`, …) is passed
29
+ through intact.
30
+ - The submit button is now `type="button"` (JS-handled) — it works with
31
+ no surrounding `<form>`, and shows the same loading / disabled state as
32
+ a `tool-call` button.
33
+ - The `defaultExecutor` `mcp-action` `CustomEvent` detail now carries
34
+ `action`, so a window-level listener can route submits too.
35
+
36
+ Backward compatible : `action` is optional everywhere and absent on every
37
+ pre-6.6.1 request — a request without it is still treated as `tool-call`.
38
+
39
+ ## [6.6.0] - 2026-05-21
40
+
41
+ Sprint OpenData / macros — cf. `docs/briefs/ROADMAP-opendata-macro-mcpui.md`
42
+ (decisions D1-D10 + R1-R4). Ships steps 1-5 of the execution order :
43
+ `StreamingUIRenderer` parity, `MCPUIStringsProvider`, the
44
+ `ConnectorDynamicResultV1` contract consumption, `PresentationFeedback`,
45
+ and the opt-in connector adapters. The macro adapters are deferred to a
46
+ later phase (they need a macro-run contract). Pairs with
47
+ `@seed-ship/mcp-ui-spec@5.1.0`.
48
+
49
+ ### Added — opt-in connector adapters (`@seed-ship/mcp-ui-solid/adapters`) (D5 / D6)
50
+
51
+ New dedicated subpath export — **never** imported by the core renderer
52
+ path, so consumers that don't emit connector results pay nothing for it:
53
+
54
+ ```ts
55
+ import { connectorResultToUILayout } from '@seed-ship/mcp-ui-solid/adapters'
56
+
57
+ const layout = connectorResultToUILayout(connectorResult)
58
+ ```
59
+
60
+ - `connectorResultToUILayout(result)` — assembles a `ConnectorDynamicResultV1`
61
+ (`primary` + `supplemental[]` + `actions`) into one `UILayout`. **Pure**
62
+ (D5) — deterministic, no side effects, safe to re-run after feedback.
63
+ - `connectorActionsToActionGroup(actions)` — wraps connector actions into
64
+ an `action-group` `UIComponent`.
65
+ - **Unknown `schemaVersion` never throws** (R2) : a usable-but-unversioned
66
+ envelope still renders, prefixed with a visible warning notice ; a truly
67
+ unreadable payload becomes an explicit degraded `UILayout` (a `text`
68
+ notice with id `connector-degraded`). Never a silent disappearance.
69
+
70
+ **Scope note** : this ships the *connector* adapters. The *macro* adapters
71
+ (`macroRunToScratchpadState`, `macroInterrogationToChatPromptConfig`) are
72
+ deferred — they need a macro-run contract that the roadmap leaves to
73
+ Phase 2.
74
+
75
+ ### Added — `PresentationFeedback` component (R3 / D9 / Phase 4)
76
+
77
+ A new opt-in feedback widget — **distinct** from `FeedbackInline` :
78
+
79
+ - `FeedbackInline` — was the *answer* good? (response quality)
80
+ - `PresentationFeedback` — was the answer *shown well*? (layout / readability)
81
+
82
+ They are separate components, separate exports, separate payloads — the
83
+ two axes never collapse (cf. R3). `PresentationFeedback` collects a
84
+ `verdict` (`readable` / `not_readable`) and, when not readable, problem
85
+ tags + an optional preferred layout + a free-text comment, then emits a
86
+ `ConnectorRenderFeedback` payload via `onSubmit`.
87
+
88
+ - Stateless : the host persists the feedback and owns any re-render
89
+ (cf. D1 — adapter pure + host state). The component never mutates the
90
+ rendered result.
91
+ - Best-effort : a rejected `onSubmit` promise is swallowed, the UI flips.
92
+ - Localizable : all labels ship in English, overridable via the `labels`
93
+ prop (`DEFAULT_PRESENTATION_FEEDBACK_LABELS` exported).
94
+ - `ConnectorRenderFeedback` / `ConnectorRenderProblem` /
95
+ `ConnectorPreferredLayout` types are re-exported from the package root
96
+ for convenience (defined in `@seed-ship/mcp-ui-spec`).
97
+
98
+ ### Added — `MCPUIStringsProvider` : i18n for the library's chrome (D2 / R4)
99
+
100
+ MCP-UI hardcoded a handful of its own UI strings (the expand-button
101
+ tooltip, the feedback acknowledgements…) — and they were an inconsistent
102
+ FR/EN mix. New opt-in context to localize them :
103
+
104
+ ```tsx
105
+ import { MCPUIStringsProvider } from '@seed-ship/mcp-ui-solid'
106
+
107
+ <MCPUIStringsProvider strings={{ expand: 'Agrandir', feedbackUseful: 'Utile' }}>
108
+ <App />
109
+ </MCPUIStringsProvider>
110
+ ```
111
+
112
+ - `MCPUIStrings` — flat string map of the library's own "chrome" strings.
113
+ **Content** (table headers, chart titles, action labels) is NOT covered:
114
+ it comes from the payload, already localized by the producer.
115
+ - `DEFAULT_MCPUI_STRINGS` — English defaults. A published library ships no
116
+ hardcoded non-English chrome.
117
+ - `<MCPUIStringsProvider strings={...}>` — partial override; unset keys fall
118
+ back to the EN defaults.
119
+ - `useMCPUIStrings()` — reads the active strings; returns the EN defaults
120
+ when no provider is mounted (every renderer works standalone).
121
+
122
+ Wired consumers : `FeedbackInline` (button tooltips + acks),
123
+ `ExpandableWrapper` (expand / copy / close chrome), `StreamingUIRenderer`
124
+ (retry button). Component props that already carry a label
125
+ (`FeedbackInline.positiveAck`, `ExpandableWrapper.copyLabel`) keep priority
126
+ over the provider.
127
+
128
+ **Behavior change** : `FeedbackInline`'s ack defaults were French
129
+ (`'Merci !'`, `"Noté, on s'améliore"`) — they are now English
130
+ (`'Thanks!'`, `"Noted — we'll improve"`). Consumers relying on the FR
131
+ defaults pass a `<MCPUIStringsProvider>` with FR strings, or the
132
+ `positiveAck` / `negativeAck` props. `ExpandableWrapper`'s default heading
133
+ casing was also unified to `'Expanded view'` (it was inconsistently
134
+ `'Expanded View'` for the heading vs `'Expanded view'` for the aria-label).
135
+
136
+ ### Changed — `StreamingUIRenderer` renders with full fidelity (Gap 1 / D3)
137
+
138
+ `StreamingUIRenderer` previously rendered each streamed component through an
139
+ inline simplified renderer (`StreamingComponentRenderer`) that only showed a
140
+ type label, the title, and — for metrics — the value. A streamed `table` /
141
+ `chart` / `map` did NOT render the real component.
142
+
143
+ Each streamed `UIComponent` is now delegated to the real
144
+ `<UIResourceRenderer>`. Streaming and static paths use the literal same
145
+ renderer, so they cannot drift. Validation, telemetry, the error boundary
146
+ and `errorMode` all come from `<UIResourceRenderer>` — the duplicated copies
147
+ in the streaming path are deleted.
148
+
149
+ - New `toolbarVariant` prop on `<StreamingUIRenderer>`, forwarded to streamed
150
+ components (parity with the static `<UIResourceRenderer toolbarVariant>`).
151
+ - Delegation is a one-way value import — `UIResourceRenderer` never imports
152
+ `StreamingUIRenderer`, no cycle.
153
+ - Progress bar, skeletons, arrival animation and metadata display unchanged.
154
+ - The streamed component's grid `position` is normalized to full-width
155
+ before delegation: `StreamingUIRenderer` owns the 12-column layout,
156
+ `<UIResourceRenderer>` only renders the component.
157
+
158
+ ### Changed — `size-limit` budgets
159
+
160
+ The "Streaming renderer" `size-limit` budget was raised from 30 KB to 1 MB.
161
+ Post-D3 that entry's reachable graph equals the full renderer set, and
162
+ `size-limit` measures the pre-built `dist/` including lazy `import()` chunks
163
+ (leaflet, `@antv/g6`, chart.js) that are fetched on demand, not at import.
164
+ The figure is a worst-case total, not eager load cost — the budget is
165
+ generous headroom, since `size-limit` here is a regression guardrail and
166
+ does not gate CI. The "Hooks only" and "Full bundle" entries are unchanged —
167
+ their pre-existing overages predate this sprint.
168
+
8
169
  ## [6.5.0] - 2026-05-05
9
170
 
10
171
  Closes Demande 1 + Demande 2 of `deposium_solid`'s
package/README.md CHANGED
@@ -5,6 +5,43 @@ SolidJS components + chat toolkit for MCP-generated UI. Part of the [MCP UI ecos
5
5
  [![npm version](https://img.shields.io/npm/v/@seed-ship/mcp-ui-solid.svg)](https://www.npmjs.com/package/@seed-ship/mcp-ui-solid)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
7
 
8
+ ## What's New in v6.6.0
9
+
10
+ Sprint OpenData / macros — `docs/briefs/ROADMAP-opendata-macro-mcpui.md`.
11
+
12
+ - **`StreamingUIRenderer` renders with full fidelity** — each streamed
13
+ component is now delegated to the real `<UIResourceRenderer>`. A streamed
14
+ `table` / `chart` / `map` renders exactly like a static one (no more
15
+ simplified type-label placeholder). New `toolbarVariant` prop forwarded
16
+ to streamed components.
17
+ - **`<MCPUIStringsProvider>`** — opt-in i18n for the library's own *chrome*
18
+ strings (expand tooltip, feedback acks…). English defaults; override
19
+ partially. Payload *content* is untouched — it stays the producer's job.
20
+
21
+ ```tsx
22
+ import { MCPUIStringsProvider } from '@seed-ship/mcp-ui-solid'
23
+ <MCPUIStringsProvider strings={{ expand: 'Agrandir' }}><App /></MCPUIStringsProvider>
24
+ ```
25
+ - **`<PresentationFeedback>`** — a feedback widget for how a result was
26
+ *presented* (layout / readability), distinct from `FeedbackInline`
27
+ (response quality). Emits a `ConnectorRenderFeedback` payload; stateless
28
+ (the host persists + re-renders).
29
+ - **`@seed-ship/mcp-ui-solid/adapters`** — new opt-in subpath.
30
+ `connectorResultToUILayout()` assembles a `ConnectorDynamicResultV1` into
31
+ a `UILayout`; `connectorActionsToActionGroup()` wraps connector actions.
32
+ Pure functions; an unknown `schemaVersion` degrades gracefully, never
33
+ throws.
34
+
35
+ ```ts
36
+ import { connectorResultToUILayout } from '@seed-ship/mcp-ui-solid/adapters'
37
+ const layout = connectorResultToUILayout(connectorResult)
38
+ ```
39
+
40
+ > **Note** — `FeedbackInline`'s acknowledgement defaults changed from
41
+ > French to English (`'Thanks!'`, `"Noted — we'll improve"`). Wrap your app
42
+ > in `<MCPUIStringsProvider>` with French strings, or pass `positiveAck` /
43
+ > `negativeAck`, to restore French.
44
+
8
45
  ## What's New in v5.2.0 (`mcp-ui-solid` only)
9
46
 
10
47
  - **`createChatPromptController()`** primitive — closes the v5.1.0 boilerplate. Owns resolver closure + `AbortSignal` wiring + re-entrance. Consumers write `bus.commands.handle('showChatPrompt', ctrl.handle)` + `<Show when={ctrl.activePrompt()}>{cfg => <ChatPrompt ... />}</Show>`. `PromptReplacedError` exported for `instanceof` checks.
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const schemas = require("../mcp-ui-spec/dist/schemas.cjs");
4
+ const types = require("../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.cjs");
5
+ function connectorActionsToActionGroup(actions, options = {}) {
6
+ return {
7
+ id: options.id ?? "connector-actions",
8
+ type: "action-group",
9
+ position: options.position ?? { colStart: 1, colSpan: 12 },
10
+ params: {
11
+ actions,
12
+ ...options.title ? { title: options.title } : {},
13
+ layout: options.layout ?? "horizontal",
14
+ gap: options.gap ?? "md"
15
+ }
16
+ };
17
+ }
18
+ const LenientResultSchema = types.object({
19
+ schemaVersion: types.string(),
20
+ connectorId: types.string().min(1),
21
+ toolName: types.string().min(1),
22
+ query: types.string(),
23
+ queryHash: types.string().optional(),
24
+ intent: types.string().optional(),
25
+ primary: types.record(types.unknown()),
26
+ supplemental: types.array(types.record(types.unknown())).optional(),
27
+ actions: types.array(types.record(types.unknown())).optional()
28
+ });
29
+ const DEFAULT_GRID = { columns: 12, gap: "1rem" };
30
+ function withPosition(component) {
31
+ if (component && component.position) return component;
32
+ return { ...component, position: { colStart: 1, colSpan: 12 } };
33
+ }
34
+ function isLayoutShape(value) {
35
+ return Array.isArray(value.components);
36
+ }
37
+ function degradedTextComponent(id, message) {
38
+ return {
39
+ id,
40
+ type: "text",
41
+ position: { colStart: 1, colSpan: 12 },
42
+ params: { markdown: true, content: message }
43
+ };
44
+ }
45
+ function connectorResultToUILayout(result, options = {}) {
46
+ const strict = schemas.ConnectorDynamicResultV1Schema.safeParse(result);
47
+ if (!strict.success) {
48
+ const lenient = LenientResultSchema.safeParse(result);
49
+ if (!lenient.success) {
50
+ const version = result && typeof result === "object" ? result.schemaVersion : void 0;
51
+ return {
52
+ id: "connector-degraded",
53
+ components: [
54
+ degradedTextComponent(
55
+ "connector-degraded-notice",
56
+ `### Résultat non rendu
57
+
58
+ Le résultat du connecteur n'a pas pu être interprété${typeof version === "string" ? ` (schéma : \`${version}\`)` : ""}. Cet état explicite remplace une disparition silencieuse du rendu.`
59
+ )
60
+ ],
61
+ grid: { ...DEFAULT_GRID }
62
+ };
63
+ }
64
+ const r2 = lenient.data;
65
+ const components2 = [
66
+ degradedTextComponent(
67
+ "connector-version-warning",
68
+ `> ⚠ Schéma connecteur non reconnu (\`${r2.schemaVersion}\`, attendu \`${schemas.CONNECTOR_DYNAMIC_RESULT_V1}\`). Le rendu ci-dessous est en mode dégradé.`
69
+ ),
70
+ ...collectComponents(r2)
71
+ ];
72
+ return {
73
+ id: options.id ?? `connector-degraded-${r2.connectorId}`,
74
+ components: components2.map(withPosition),
75
+ grid: { ...DEFAULT_GRID }
76
+ };
77
+ }
78
+ const r = strict.data;
79
+ const components = collectComponents(r, options.actionsTitle).map(withPosition);
80
+ return {
81
+ id: options.id ?? layoutId(r.connectorId, r.queryHash ?? r.toolName),
82
+ components,
83
+ grid: { ...DEFAULT_GRID }
84
+ };
85
+ }
86
+ function collectComponents(r, actionsTitle) {
87
+ const components = [];
88
+ if (isLayoutShape(r.primary)) {
89
+ const inner = r.primary.components ?? [];
90
+ components.push(...inner);
91
+ } else {
92
+ components.push(r.primary);
93
+ }
94
+ if (r.supplemental) {
95
+ components.push(...r.supplemental);
96
+ }
97
+ if (Array.isArray(r.actions) && r.actions.length > 0) {
98
+ components.push(
99
+ connectorActionsToActionGroup(r.actions, {
100
+ id: "connector-actions",
101
+ title: actionsTitle
102
+ })
103
+ );
104
+ }
105
+ return components;
106
+ }
107
+ function layoutId(connectorId, suffix) {
108
+ return `connector-${connectorId}-${suffix}`;
109
+ }
110
+ exports.connectorActionsToActionGroup = connectorActionsToActionGroup;
111
+ exports.connectorResultToUILayout = connectorResultToUILayout;
112
+ //# sourceMappingURL=connector.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connector.cjs","sources":["../../src/adapters/connector.ts"],"sourcesContent":["/**\n * Connector adapters — `ConnectorDynamicResultV1` → MCP-UI render structures.\n *\n * @since v6.6.0 (D5 / D6 of ROADMAP-opendata-macro-mcpui)\n *\n * ## Opt-in, pure\n *\n * These adapters are published under the dedicated subpath\n * `@seed-ship/mcp-ui-solid/adapters` — they are NEVER imported by the core\n * renderer path, so a consumer that does not emit connector results pays\n * nothing for them.\n *\n * Every adapter here is a **pure function** (D5) : same input → same\n * output, no `fetch`, no `localStorage`, no global state, no clock, no\n * randomness. This is what lets a host re-run an adapter deterministically\n * after presentation feedback (D1) and replay fixtures in tests.\n *\n * ## Unknown schema version — never throw (R2)\n *\n * `connectorResultToUILayout()` never throws on the runtime render path.\n * A payload it cannot read becomes an explicit degraded `UILayout` (a\n * visible notice), never a silent disappearance and never an exception.\n */\n\nimport type { UIComponent, UILayout, GridPosition } from '../types'\nimport {\n ConnectorDynamicResultV1Schema,\n CONNECTOR_DYNAMIC_RESULT_V1,\n type ConnectorAction,\n} from '@seed-ship/mcp-ui-spec'\nimport { z } from 'zod'\n\n// ─────────────────────────────────────────────────────────────\n// connectorActionsToActionGroup\n// ─────────────────────────────────────────────────────────────\n\nexport interface ConnectorActionsToActionGroupOptions {\n /** Component id. Default `'connector-actions'`. */\n id?: string\n /** Optional heading shown above the buttons. */\n title?: string\n /** Button row layout. Default `'horizontal'`. */\n layout?: 'horizontal' | 'vertical' | 'space-between' | 'end' | 'center'\n /** Gap between buttons. Default `'md'`. */\n gap?: 'none' | 'sm' | 'md' | 'lg'\n /** Grid position. Default full-width. */\n position?: GridPosition\n}\n\n/**\n * Wraps a connector's `actions` into an `action-group` `UIComponent`.\n *\n * `ConnectorAction` is the exact `action-group` action shape, so this is a\n * thin, pure envelope — no transformation of the actions themselves.\n */\nexport function connectorActionsToActionGroup(\n actions: ConnectorAction[],\n options: ConnectorActionsToActionGroupOptions = {}\n): UIComponent {\n return {\n id: options.id ?? 'connector-actions',\n type: 'action-group',\n position: options.position ?? { colStart: 1, colSpan: 12 },\n params: {\n actions,\n ...(options.title ? { title: options.title } : {}),\n layout: options.layout ?? 'horizontal',\n gap: options.gap ?? 'md',\n },\n } as UIComponent\n}\n\n// ─────────────────────────────────────────────────────────────\n// connectorResultToUILayout\n// ─────────────────────────────────────────────────────────────\n\nexport interface ConnectorResultToUILayoutOptions {\n /** Layout id. Default derived from `connectorId` + `queryHash` / `toolName`. */\n id?: string\n /** Heading for the actions `action-group`. */\n actionsTitle?: string\n}\n\n/**\n * Lenient mirror of `ConnectorDynamicResultV1Schema` — `schemaVersion`\n * relaxed to any string. Used to tell apart \"unknown version but otherwise\n * a usable envelope\" (→ render with a warning, R2) from \"truly unreadable\"\n * (→ explicit error state).\n */\nconst LenientResultSchema = z.object({\n schemaVersion: z.string(),\n connectorId: z.string().min(1),\n toolName: z.string().min(1),\n query: z.string(),\n queryHash: z.string().optional(),\n intent: z.string().optional(),\n primary: z.record(z.unknown()),\n supplemental: z.array(z.record(z.unknown())).optional(),\n actions: z.array(z.record(z.unknown())).optional(),\n})\n\nconst DEFAULT_GRID = { columns: 12, gap: '1rem' } as const\n\nfunction withPosition(component: UIComponent): UIComponent {\n if (component && component.position) return component\n return { ...component, position: { colStart: 1, colSpan: 12 } }\n}\n\n/** A component is a UILayout when it carries a `components` array. */\nfunction isLayoutShape(value: Record<string, unknown>): boolean {\n return Array.isArray((value as { components?: unknown }).components)\n}\n\nfunction degradedTextComponent(id: string, message: string): UIComponent {\n return {\n id,\n type: 'text',\n position: { colStart: 1, colSpan: 12 },\n params: { markdown: true, content: message },\n } as UIComponent\n}\n\n/**\n * Assembles a `ConnectorDynamicResultV1` into a single `UILayout` :\n * `primary` + `supplemental[]` + (`actions` → an `action-group`).\n *\n * - When `primary` is itself a layout, its components are spread in.\n * - Raw data is never sacrificed — every supplied component is kept.\n * - Pure : no side effects, deterministic.\n *\n * ### Degraded behavior (R2)\n *\n * - Valid v1 payload → normal assembled layout.\n * - Unknown `schemaVersion` but an otherwise-usable envelope → the\n * components are still rendered, prefixed with a visible warning notice.\n * - Unreadable payload → an explicit error `UILayout` (a single `text`\n * notice). Never throws, never returns an empty layout silently.\n *\n * A degraded layout always has an `id` starting with `connector-degraded`,\n * so a host can detect it (e.g. for telemetry) without inspecting content.\n */\nexport function connectorResultToUILayout(\n result: unknown,\n options: ConnectorResultToUILayoutOptions = {}\n): UILayout {\n const strict = ConnectorDynamicResultV1Schema.safeParse(result)\n\n // ── Tier 3 : unreadable ───────────────────────────────────\n if (!strict.success) {\n const lenient = LenientResultSchema.safeParse(result)\n if (!lenient.success) {\n const version =\n result && typeof result === 'object'\n ? (result as { schemaVersion?: unknown }).schemaVersion\n : undefined\n return {\n id: 'connector-degraded',\n components: [\n degradedTextComponent(\n 'connector-degraded-notice',\n `### Résultat non rendu\\n\\nLe résultat du connecteur n'a pas pu être interprété${\n typeof version === 'string' ? ` (schéma : \\`${version}\\`)` : ''\n }. Cet état explicite remplace une disparition silencieuse du rendu.`\n ),\n ],\n grid: { ...DEFAULT_GRID },\n }\n }\n // ── Tier 2 : usable envelope, unknown version ───────────\n const r = lenient.data\n const components: UIComponent[] = [\n degradedTextComponent(\n 'connector-version-warning',\n `> ⚠ Schéma connecteur non reconnu (\\`${r.schemaVersion}\\`, attendu \\`${CONNECTOR_DYNAMIC_RESULT_V1}\\`). Le rendu ci-dessous est en mode dégradé.`\n ),\n ...collectComponents(r),\n ]\n return {\n id: options.id ?? `connector-degraded-${r.connectorId}`,\n components: components.map(withPosition),\n grid: { ...DEFAULT_GRID },\n }\n }\n\n // ── Tier 1 : valid v1 ─────────────────────────────────────\n const r = strict.data\n const components = collectComponents(r, options.actionsTitle).map(withPosition)\n return {\n id: options.id ?? layoutId(r.connectorId, r.queryHash ?? r.toolName),\n components,\n grid: { ...DEFAULT_GRID },\n }\n}\n\n/**\n * Flattens `primary` + `supplemental` + `actions` into a component list.\n * Shared by the valid and degraded-but-usable paths.\n */\nfunction collectComponents(\n r: {\n primary: Record<string, unknown>\n supplemental?: Record<string, unknown>[]\n actions?: unknown[]\n },\n actionsTitle?: string\n): UIComponent[] {\n const components: UIComponent[] = []\n\n if (isLayoutShape(r.primary)) {\n const inner = (r.primary as { components?: UIComponent[] }).components ?? []\n components.push(...inner)\n } else {\n components.push(r.primary as unknown as UIComponent)\n }\n\n if (r.supplemental) {\n components.push(...(r.supplemental as unknown as UIComponent[]))\n }\n\n if (Array.isArray(r.actions) && r.actions.length > 0) {\n components.push(\n connectorActionsToActionGroup(r.actions as ConnectorAction[], {\n id: 'connector-actions',\n title: actionsTitle,\n })\n )\n }\n\n return components\n}\n\nfunction layoutId(connectorId: string, suffix: string): string {\n return `connector-${connectorId}-${suffix}`\n}\n"],"names":["z.object","z.string","z.record","z.unknown","z.array","ConnectorDynamicResultV1Schema","r","components","CONNECTOR_DYNAMIC_RESULT_V1"],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Connector adapters — `ConnectorDynamicResultV1` → MCP-UI render structures.
3
+ *
4
+ * @since v6.6.0 (D5 / D6 of ROADMAP-opendata-macro-mcpui)
5
+ *
6
+ * ## Opt-in, pure
7
+ *
8
+ * These adapters are published under the dedicated subpath
9
+ * `@seed-ship/mcp-ui-solid/adapters` — they are NEVER imported by the core
10
+ * renderer path, so a consumer that does not emit connector results pays
11
+ * nothing for them.
12
+ *
13
+ * Every adapter here is a **pure function** (D5) : same input → same
14
+ * output, no `fetch`, no `localStorage`, no global state, no clock, no
15
+ * randomness. This is what lets a host re-run an adapter deterministically
16
+ * after presentation feedback (D1) and replay fixtures in tests.
17
+ *
18
+ * ## Unknown schema version — never throw (R2)
19
+ *
20
+ * `connectorResultToUILayout()` never throws on the runtime render path.
21
+ * A payload it cannot read becomes an explicit degraded `UILayout` (a
22
+ * visible notice), never a silent disappearance and never an exception.
23
+ */
24
+ import type { UIComponent, UILayout, GridPosition } from '../types';
25
+ import { type ConnectorAction } from '@seed-ship/mcp-ui-spec';
26
+ export interface ConnectorActionsToActionGroupOptions {
27
+ /** Component id. Default `'connector-actions'`. */
28
+ id?: string;
29
+ /** Optional heading shown above the buttons. */
30
+ title?: string;
31
+ /** Button row layout. Default `'horizontal'`. */
32
+ layout?: 'horizontal' | 'vertical' | 'space-between' | 'end' | 'center';
33
+ /** Gap between buttons. Default `'md'`. */
34
+ gap?: 'none' | 'sm' | 'md' | 'lg';
35
+ /** Grid position. Default full-width. */
36
+ position?: GridPosition;
37
+ }
38
+ /**
39
+ * Wraps a connector's `actions` into an `action-group` `UIComponent`.
40
+ *
41
+ * `ConnectorAction` is the exact `action-group` action shape, so this is a
42
+ * thin, pure envelope — no transformation of the actions themselves.
43
+ */
44
+ export declare function connectorActionsToActionGroup(actions: ConnectorAction[], options?: ConnectorActionsToActionGroupOptions): UIComponent;
45
+ export interface ConnectorResultToUILayoutOptions {
46
+ /** Layout id. Default derived from `connectorId` + `queryHash` / `toolName`. */
47
+ id?: string;
48
+ /** Heading for the actions `action-group`. */
49
+ actionsTitle?: string;
50
+ }
51
+ /**
52
+ * Assembles a `ConnectorDynamicResultV1` into a single `UILayout` :
53
+ * `primary` + `supplemental[]` + (`actions` → an `action-group`).
54
+ *
55
+ * - When `primary` is itself a layout, its components are spread in.
56
+ * - Raw data is never sacrificed — every supplied component is kept.
57
+ * - Pure : no side effects, deterministic.
58
+ *
59
+ * ### Degraded behavior (R2)
60
+ *
61
+ * - Valid v1 payload → normal assembled layout.
62
+ * - Unknown `schemaVersion` but an otherwise-usable envelope → the
63
+ * components are still rendered, prefixed with a visible warning notice.
64
+ * - Unreadable payload → an explicit error `UILayout` (a single `text`
65
+ * notice). Never throws, never returns an empty layout silently.
66
+ *
67
+ * A degraded layout always has an `id` starting with `connector-degraded`,
68
+ * so a host can detect it (e.g. for telemetry) without inspecting content.
69
+ */
70
+ export declare function connectorResultToUILayout(result: unknown, options?: ConnectorResultToUILayoutOptions): UILayout;
71
+ //# sourceMappingURL=connector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connector.d.ts","sourceRoot":"","sources":["../../src/adapters/connector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACnE,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,wBAAwB,CAAA;AAO/B,MAAM,WAAW,oCAAoC;IACnD,mDAAmD;IACnD,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,gDAAgD;IAChD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iDAAiD;IACjD,MAAM,CAAC,EAAE,YAAY,GAAG,UAAU,GAAG,eAAe,GAAG,KAAK,GAAG,QAAQ,CAAA;IACvE,2CAA2C;IAC3C,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IACjC,yCAAyC;IACzC,QAAQ,CAAC,EAAE,YAAY,CAAA;CACxB;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,eAAe,EAAE,EAC1B,OAAO,GAAE,oCAAyC,GACjD,WAAW,CAYb;AAMD,MAAM,WAAW,gCAAgC;IAC/C,gFAAgF;IAChF,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,8CAA8C;IAC9C,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAyCD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,OAAO,EACf,OAAO,GAAE,gCAAqC,GAC7C,QAAQ,CAgDV"}
@@ -0,0 +1,112 @@
1
+ import { ConnectorDynamicResultV1Schema, CONNECTOR_DYNAMIC_RESULT_V1 } from "../mcp-ui-spec/dist/schemas.js";
2
+ import { object as objectType, array as arrayType, record as recordType, string as stringType, unknown as unknownType } from "../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js";
3
+ function connectorActionsToActionGroup(actions, options = {}) {
4
+ return {
5
+ id: options.id ?? "connector-actions",
6
+ type: "action-group",
7
+ position: options.position ?? { colStart: 1, colSpan: 12 },
8
+ params: {
9
+ actions,
10
+ ...options.title ? { title: options.title } : {},
11
+ layout: options.layout ?? "horizontal",
12
+ gap: options.gap ?? "md"
13
+ }
14
+ };
15
+ }
16
+ const LenientResultSchema = objectType({
17
+ schemaVersion: stringType(),
18
+ connectorId: stringType().min(1),
19
+ toolName: stringType().min(1),
20
+ query: stringType(),
21
+ queryHash: stringType().optional(),
22
+ intent: stringType().optional(),
23
+ primary: recordType(unknownType()),
24
+ supplemental: arrayType(recordType(unknownType())).optional(),
25
+ actions: arrayType(recordType(unknownType())).optional()
26
+ });
27
+ const DEFAULT_GRID = { columns: 12, gap: "1rem" };
28
+ function withPosition(component) {
29
+ if (component && component.position) return component;
30
+ return { ...component, position: { colStart: 1, colSpan: 12 } };
31
+ }
32
+ function isLayoutShape(value) {
33
+ return Array.isArray(value.components);
34
+ }
35
+ function degradedTextComponent(id, message) {
36
+ return {
37
+ id,
38
+ type: "text",
39
+ position: { colStart: 1, colSpan: 12 },
40
+ params: { markdown: true, content: message }
41
+ };
42
+ }
43
+ function connectorResultToUILayout(result, options = {}) {
44
+ const strict = ConnectorDynamicResultV1Schema.safeParse(result);
45
+ if (!strict.success) {
46
+ const lenient = LenientResultSchema.safeParse(result);
47
+ if (!lenient.success) {
48
+ const version = result && typeof result === "object" ? result.schemaVersion : void 0;
49
+ return {
50
+ id: "connector-degraded",
51
+ components: [
52
+ degradedTextComponent(
53
+ "connector-degraded-notice",
54
+ `### Résultat non rendu
55
+
56
+ Le résultat du connecteur n'a pas pu être interprété${typeof version === "string" ? ` (schéma : \`${version}\`)` : ""}. Cet état explicite remplace une disparition silencieuse du rendu.`
57
+ )
58
+ ],
59
+ grid: { ...DEFAULT_GRID }
60
+ };
61
+ }
62
+ const r2 = lenient.data;
63
+ const components2 = [
64
+ degradedTextComponent(
65
+ "connector-version-warning",
66
+ `> ⚠ Schéma connecteur non reconnu (\`${r2.schemaVersion}\`, attendu \`${CONNECTOR_DYNAMIC_RESULT_V1}\`). Le rendu ci-dessous est en mode dégradé.`
67
+ ),
68
+ ...collectComponents(r2)
69
+ ];
70
+ return {
71
+ id: options.id ?? `connector-degraded-${r2.connectorId}`,
72
+ components: components2.map(withPosition),
73
+ grid: { ...DEFAULT_GRID }
74
+ };
75
+ }
76
+ const r = strict.data;
77
+ const components = collectComponents(r, options.actionsTitle).map(withPosition);
78
+ return {
79
+ id: options.id ?? layoutId(r.connectorId, r.queryHash ?? r.toolName),
80
+ components,
81
+ grid: { ...DEFAULT_GRID }
82
+ };
83
+ }
84
+ function collectComponents(r, actionsTitle) {
85
+ const components = [];
86
+ if (isLayoutShape(r.primary)) {
87
+ const inner = r.primary.components ?? [];
88
+ components.push(...inner);
89
+ } else {
90
+ components.push(r.primary);
91
+ }
92
+ if (r.supplemental) {
93
+ components.push(...r.supplemental);
94
+ }
95
+ if (Array.isArray(r.actions) && r.actions.length > 0) {
96
+ components.push(
97
+ connectorActionsToActionGroup(r.actions, {
98
+ id: "connector-actions",
99
+ title: actionsTitle
100
+ })
101
+ );
102
+ }
103
+ return components;
104
+ }
105
+ function layoutId(connectorId, suffix) {
106
+ return `connector-${connectorId}-${suffix}`;
107
+ }
108
+ export {
109
+ connectorActionsToActionGroup,
110
+ connectorResultToUILayout
111
+ };
112
+ //# sourceMappingURL=connector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connector.js","sources":["../../src/adapters/connector.ts"],"sourcesContent":["/**\n * Connector adapters — `ConnectorDynamicResultV1` → MCP-UI render structures.\n *\n * @since v6.6.0 (D5 / D6 of ROADMAP-opendata-macro-mcpui)\n *\n * ## Opt-in, pure\n *\n * These adapters are published under the dedicated subpath\n * `@seed-ship/mcp-ui-solid/adapters` — they are NEVER imported by the core\n * renderer path, so a consumer that does not emit connector results pays\n * nothing for them.\n *\n * Every adapter here is a **pure function** (D5) : same input → same\n * output, no `fetch`, no `localStorage`, no global state, no clock, no\n * randomness. This is what lets a host re-run an adapter deterministically\n * after presentation feedback (D1) and replay fixtures in tests.\n *\n * ## Unknown schema version — never throw (R2)\n *\n * `connectorResultToUILayout()` never throws on the runtime render path.\n * A payload it cannot read becomes an explicit degraded `UILayout` (a\n * visible notice), never a silent disappearance and never an exception.\n */\n\nimport type { UIComponent, UILayout, GridPosition } from '../types'\nimport {\n ConnectorDynamicResultV1Schema,\n CONNECTOR_DYNAMIC_RESULT_V1,\n type ConnectorAction,\n} from '@seed-ship/mcp-ui-spec'\nimport { z } from 'zod'\n\n// ─────────────────────────────────────────────────────────────\n// connectorActionsToActionGroup\n// ─────────────────────────────────────────────────────────────\n\nexport interface ConnectorActionsToActionGroupOptions {\n /** Component id. Default `'connector-actions'`. */\n id?: string\n /** Optional heading shown above the buttons. */\n title?: string\n /** Button row layout. Default `'horizontal'`. */\n layout?: 'horizontal' | 'vertical' | 'space-between' | 'end' | 'center'\n /** Gap between buttons. Default `'md'`. */\n gap?: 'none' | 'sm' | 'md' | 'lg'\n /** Grid position. Default full-width. */\n position?: GridPosition\n}\n\n/**\n * Wraps a connector's `actions` into an `action-group` `UIComponent`.\n *\n * `ConnectorAction` is the exact `action-group` action shape, so this is a\n * thin, pure envelope — no transformation of the actions themselves.\n */\nexport function connectorActionsToActionGroup(\n actions: ConnectorAction[],\n options: ConnectorActionsToActionGroupOptions = {}\n): UIComponent {\n return {\n id: options.id ?? 'connector-actions',\n type: 'action-group',\n position: options.position ?? { colStart: 1, colSpan: 12 },\n params: {\n actions,\n ...(options.title ? { title: options.title } : {}),\n layout: options.layout ?? 'horizontal',\n gap: options.gap ?? 'md',\n },\n } as UIComponent\n}\n\n// ─────────────────────────────────────────────────────────────\n// connectorResultToUILayout\n// ─────────────────────────────────────────────────────────────\n\nexport interface ConnectorResultToUILayoutOptions {\n /** Layout id. Default derived from `connectorId` + `queryHash` / `toolName`. */\n id?: string\n /** Heading for the actions `action-group`. */\n actionsTitle?: string\n}\n\n/**\n * Lenient mirror of `ConnectorDynamicResultV1Schema` — `schemaVersion`\n * relaxed to any string. Used to tell apart \"unknown version but otherwise\n * a usable envelope\" (→ render with a warning, R2) from \"truly unreadable\"\n * (→ explicit error state).\n */\nconst LenientResultSchema = z.object({\n schemaVersion: z.string(),\n connectorId: z.string().min(1),\n toolName: z.string().min(1),\n query: z.string(),\n queryHash: z.string().optional(),\n intent: z.string().optional(),\n primary: z.record(z.unknown()),\n supplemental: z.array(z.record(z.unknown())).optional(),\n actions: z.array(z.record(z.unknown())).optional(),\n})\n\nconst DEFAULT_GRID = { columns: 12, gap: '1rem' } as const\n\nfunction withPosition(component: UIComponent): UIComponent {\n if (component && component.position) return component\n return { ...component, position: { colStart: 1, colSpan: 12 } }\n}\n\n/** A component is a UILayout when it carries a `components` array. */\nfunction isLayoutShape(value: Record<string, unknown>): boolean {\n return Array.isArray((value as { components?: unknown }).components)\n}\n\nfunction degradedTextComponent(id: string, message: string): UIComponent {\n return {\n id,\n type: 'text',\n position: { colStart: 1, colSpan: 12 },\n params: { markdown: true, content: message },\n } as UIComponent\n}\n\n/**\n * Assembles a `ConnectorDynamicResultV1` into a single `UILayout` :\n * `primary` + `supplemental[]` + (`actions` → an `action-group`).\n *\n * - When `primary` is itself a layout, its components are spread in.\n * - Raw data is never sacrificed — every supplied component is kept.\n * - Pure : no side effects, deterministic.\n *\n * ### Degraded behavior (R2)\n *\n * - Valid v1 payload → normal assembled layout.\n * - Unknown `schemaVersion` but an otherwise-usable envelope → the\n * components are still rendered, prefixed with a visible warning notice.\n * - Unreadable payload → an explicit error `UILayout` (a single `text`\n * notice). Never throws, never returns an empty layout silently.\n *\n * A degraded layout always has an `id` starting with `connector-degraded`,\n * so a host can detect it (e.g. for telemetry) without inspecting content.\n */\nexport function connectorResultToUILayout(\n result: unknown,\n options: ConnectorResultToUILayoutOptions = {}\n): UILayout {\n const strict = ConnectorDynamicResultV1Schema.safeParse(result)\n\n // ── Tier 3 : unreadable ───────────────────────────────────\n if (!strict.success) {\n const lenient = LenientResultSchema.safeParse(result)\n if (!lenient.success) {\n const version =\n result && typeof result === 'object'\n ? (result as { schemaVersion?: unknown }).schemaVersion\n : undefined\n return {\n id: 'connector-degraded',\n components: [\n degradedTextComponent(\n 'connector-degraded-notice',\n `### Résultat non rendu\\n\\nLe résultat du connecteur n'a pas pu être interprété${\n typeof version === 'string' ? ` (schéma : \\`${version}\\`)` : ''\n }. Cet état explicite remplace une disparition silencieuse du rendu.`\n ),\n ],\n grid: { ...DEFAULT_GRID },\n }\n }\n // ── Tier 2 : usable envelope, unknown version ───────────\n const r = lenient.data\n const components: UIComponent[] = [\n degradedTextComponent(\n 'connector-version-warning',\n `> ⚠ Schéma connecteur non reconnu (\\`${r.schemaVersion}\\`, attendu \\`${CONNECTOR_DYNAMIC_RESULT_V1}\\`). Le rendu ci-dessous est en mode dégradé.`\n ),\n ...collectComponents(r),\n ]\n return {\n id: options.id ?? `connector-degraded-${r.connectorId}`,\n components: components.map(withPosition),\n grid: { ...DEFAULT_GRID },\n }\n }\n\n // ── Tier 1 : valid v1 ─────────────────────────────────────\n const r = strict.data\n const components = collectComponents(r, options.actionsTitle).map(withPosition)\n return {\n id: options.id ?? layoutId(r.connectorId, r.queryHash ?? r.toolName),\n components,\n grid: { ...DEFAULT_GRID },\n }\n}\n\n/**\n * Flattens `primary` + `supplemental` + `actions` into a component list.\n * Shared by the valid and degraded-but-usable paths.\n */\nfunction collectComponents(\n r: {\n primary: Record<string, unknown>\n supplemental?: Record<string, unknown>[]\n actions?: unknown[]\n },\n actionsTitle?: string\n): UIComponent[] {\n const components: UIComponent[] = []\n\n if (isLayoutShape(r.primary)) {\n const inner = (r.primary as { components?: UIComponent[] }).components ?? []\n components.push(...inner)\n } else {\n components.push(r.primary as unknown as UIComponent)\n }\n\n if (r.supplemental) {\n components.push(...(r.supplemental as unknown as UIComponent[]))\n }\n\n if (Array.isArray(r.actions) && r.actions.length > 0) {\n components.push(\n connectorActionsToActionGroup(r.actions as ConnectorAction[], {\n id: 'connector-actions',\n title: actionsTitle,\n })\n )\n }\n\n return components\n}\n\nfunction layoutId(connectorId: string, suffix: string): string {\n return `connector-${connectorId}-${suffix}`\n}\n"],"names":["z.object","z.string","z.record","z.unknown","z.array","r","components"],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @seed-ship/mcp-ui-solid/adapters
3
+ *
4
+ * Opt-in, pure adapters that turn connector / macro contracts into MCP-UI
5
+ * render structures. Published as a dedicated subpath so the core renderer
6
+ * path never depends on them.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { connectorResultToUILayout } from '@seed-ship/mcp-ui-solid/adapters'
11
+ *
12
+ * const layout = connectorResultToUILayout(connectorResult)
13
+ * // → <UIResourceRenderer content={layout} />
14
+ * ```
15
+ */
16
+ export { connectorResultToUILayout, connectorActionsToActionGroup, } from './connector';
17
+ export type { ConnectorResultToUILayoutOptions, ConnectorActionsToActionGroupOptions, } from './connector';
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EACL,yBAAyB,EACzB,6BAA6B,GAC9B,MAAM,aAAa,CAAA;AACpB,YAAY,EACV,gCAAgC,EAChC,oCAAoC,GACrC,MAAM,aAAa,CAAA"}
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const connector = require("./adapters/connector.cjs");
4
+ exports.connectorActionsToActionGroup = connector.connectorActionsToActionGroup;
5
+ exports.connectorResultToUILayout = connector.connectorResultToUILayout;
6
+ //# sourceMappingURL=adapters.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapters.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @seed-ship/mcp-ui-solid/adapters
3
+ *
4
+ * Opt-in, pure adapters that turn connector / macro contracts into MCP-UI
5
+ * render structures. Published as a dedicated subpath so the core renderer
6
+ * path never depends on them.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { connectorResultToUILayout } from '@seed-ship/mcp-ui-solid/adapters'
11
+ *
12
+ * const layout = connectorResultToUILayout(connectorResult)
13
+ * // → <UIResourceRenderer content={layout} />
14
+ * ```
15
+ */
16
+ export { connectorResultToUILayout, connectorActionsToActionGroup, } from './connector';
17
+ export type { ConnectorResultToUILayoutOptions, ConnectorActionsToActionGroupOptions, } from './connector';
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @seed-ship/mcp-ui-solid/adapters
3
+ *
4
+ * Opt-in, pure adapters that turn connector / macro contracts into MCP-UI
5
+ * render structures. Published as a dedicated subpath so the core renderer
6
+ * path never depends on them.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { connectorResultToUILayout } from '@seed-ship/mcp-ui-solid/adapters'
11
+ *
12
+ * const layout = connectorResultToUILayout(connectorResult)
13
+ * // → <UIResourceRenderer content={layout} />
14
+ * ```
15
+ */
16
+ export { connectorResultToUILayout, connectorActionsToActionGroup, } from './connector';
17
+ export type { ConnectorResultToUILayoutOptions, ConnectorActionsToActionGroupOptions, } from './connector';
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,6 @@
1
+ import { connectorActionsToActionGroup, connectorResultToUILayout } from "./adapters/connector.js";
2
+ export {
3
+ connectorActionsToActionGroup,
4
+ connectorResultToUILayout
5
+ };
6
+ //# sourceMappingURL=adapters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapters.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}