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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/CHANGELOG.md +130 -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/ExpandableWrapper.cjs +24 -6
  18. package/dist/components/ExpandableWrapper.cjs.map +1 -1
  19. package/dist/components/ExpandableWrapper.d.ts.map +1 -1
  20. package/dist/components/ExpandableWrapper.js +24 -6
  21. package/dist/components/ExpandableWrapper.js.map +1 -1
  22. package/dist/components/FeedbackInline.cjs +6 -2
  23. package/dist/components/FeedbackInline.cjs.map +1 -1
  24. package/dist/components/FeedbackInline.d.ts +2 -2
  25. package/dist/components/FeedbackInline.d.ts.map +1 -1
  26. package/dist/components/FeedbackInline.js +7 -3
  27. package/dist/components/FeedbackInline.js.map +1 -1
  28. package/dist/components/PresentationFeedback.cjs +207 -0
  29. package/dist/components/PresentationFeedback.cjs.map +1 -0
  30. package/dist/components/PresentationFeedback.d.ts +113 -0
  31. package/dist/components/PresentationFeedback.d.ts.map +1 -0
  32. package/dist/components/PresentationFeedback.js +207 -0
  33. package/dist/components/PresentationFeedback.js.map +1 -0
  34. package/dist/components/StreamingUIRenderer.cjs +82 -195
  35. package/dist/components/StreamingUIRenderer.cjs.map +1 -1
  36. package/dist/components/StreamingUIRenderer.d.ts +25 -5
  37. package/dist/components/StreamingUIRenderer.d.ts.map +1 -1
  38. package/dist/components/StreamingUIRenderer.js +84 -197
  39. package/dist/components/StreamingUIRenderer.js.map +1 -1
  40. package/dist/components/index.d.ts +2 -0
  41. package/dist/components/index.d.ts.map +1 -1
  42. package/dist/components.cjs +3 -0
  43. package/dist/components.cjs.map +1 -1
  44. package/dist/components.d.cts +2 -0
  45. package/dist/components.d.ts +2 -0
  46. package/dist/components.js +3 -0
  47. package/dist/components.js.map +1 -1
  48. package/dist/context/MCPUIStringsContext.cjs +38 -0
  49. package/dist/context/MCPUIStringsContext.cjs.map +1 -0
  50. package/dist/context/MCPUIStringsContext.d.ts +95 -0
  51. package/dist/context/MCPUIStringsContext.d.ts.map +1 -0
  52. package/dist/context/MCPUIStringsContext.js +38 -0
  53. package/dist/context/MCPUIStringsContext.js.map +1 -0
  54. package/dist/index.cjs +8 -0
  55. package/dist/index.cjs.map +1 -1
  56. package/dist/index.d.cts +5 -0
  57. package/dist/index.d.ts +5 -0
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +8 -0
  60. package/dist/index.js.map +1 -1
  61. package/dist/mcp-ui-spec/dist/schemas.cjs +103 -0
  62. package/dist/mcp-ui-spec/dist/schemas.cjs.map +1 -1
  63. package/dist/mcp-ui-spec/dist/schemas.js +103 -0
  64. package/dist/mcp-ui-spec/dist/schemas.js.map +1 -1
  65. package/docs/briefs/ROADMAP-opendata-macro-mcpui.md +912 -0
  66. package/package.json +17 -5
  67. package/src/adapters/connector.test.ts +165 -0
  68. package/src/adapters/connector.ts +234 -0
  69. package/src/adapters/index.ts +24 -0
  70. package/src/components/ExpandableWrapper.test.tsx +5 -2
  71. package/src/components/ExpandableWrapper.tsx +8 -6
  72. package/src/components/FeedbackInline.test.tsx +6 -3
  73. package/src/components/FeedbackInline.tsx +8 -6
  74. package/src/components/PresentationFeedback.test.tsx +163 -0
  75. package/src/components/PresentationFeedback.tsx +326 -0
  76. package/src/components/StreamingUIRenderer.parity.test.tsx +158 -0
  77. package/src/components/StreamingUIRenderer.tsx +42 -166
  78. package/src/components/index.ts +10 -0
  79. package/src/context/MCPUIStringsContext.test.tsx +116 -0
  80. package/src/context/MCPUIStringsContext.tsx +128 -0
  81. package/src/index.ts +27 -0
  82. package/tsconfig.tsbuildinfo +1 -1
  83. package/vite.config.ts +1 -0
@@ -0,0 +1,207 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const web = require("solid-js/web");
4
+ const solidJs = require("solid-js");
5
+ var _tmpl$ = /* @__PURE__ */ web.template(`<div class="flex items-center gap-2"><span class=text-deposium-slate-500></span><button type=button class="px-2 py-0.5 rounded border border-gray-200 dark:border-gray-600 text-deposium-slate-600 dark:text-gray-300 hover:border-green-500 hover:text-green-600 transition-colors"data-presentation-feedback-verdict=readable></button><button type=button class="px-2 py-0.5 rounded border border-gray-200 dark:border-gray-600 text-deposium-slate-600 dark:text-gray-300 hover:border-amber-500 hover:text-amber-600 transition-colors"data-presentation-feedback-verdict=not_readable>`), _tmpl$2 = /* @__PURE__ */ web.template(`<div class="flex flex-wrap items-center gap-1"><span class=text-deposium-slate-500></span><!$><!/>`), _tmpl$3 = /* @__PURE__ */ web.template(`<div class="flex flex-col gap-2 p-2 rounded-lg border border-gray-200 dark:border-gray-700"><span class="font-medium text-deposium-slate-600 dark:text-gray-300"></span><div class="flex flex-wrap gap-1"></div><!$><!/><textarea rows=2 class="w-full rounded border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-800 p-1.5 text-xs resize-y"data-presentation-feedback-comment></textarea><button type=button class="self-end px-3 py-1 rounded bg-amber-500 text-white font-medium hover:bg-amber-600 transition-colors"data-presentation-feedback-submit>`), _tmpl$4 = /* @__PURE__ */ web.template(`<span class=text-deposium-slate-500 data-presentation-feedback-ack>`), _tmpl$5 = /* @__PURE__ */ web.template(`<div><!$><!/><!$><!/><!$><!/>`), _tmpl$6 = /* @__PURE__ */ web.template(`<button type=button>`);
6
+ const PROBLEM_ORDER = ["too_raw", "wrong_columns", "wrong_chart", "missing_context", "wrong_unit", "bad_grouping", "missing_dataset_context"];
7
+ const DEFAULT_PRESENTATION_FEEDBACK_LABELS = {
8
+ prompt: "Is this shown clearly?",
9
+ readable: "Clear",
10
+ notReadable: "Not clear",
11
+ problemsPrompt: "What's off?",
12
+ problemTooRaw: "Too raw",
13
+ problemWrongColumns: "Wrong columns",
14
+ problemWrongChart: "Wrong chart",
15
+ problemMissingContext: "Missing context",
16
+ problemWrongUnit: "Wrong unit",
17
+ problemBadGrouping: "Bad grouping",
18
+ problemMissingDatasetContext: "Missing dataset context",
19
+ preferLayoutPrompt: "Better shown as",
20
+ commentPlaceholder: "Anything else? (optional)",
21
+ submit: "Send feedback",
22
+ ack: "Thanks — noted."
23
+ };
24
+ const PROBLEM_LABEL_KEY = {
25
+ too_raw: "problemTooRaw",
26
+ wrong_columns: "problemWrongColumns",
27
+ wrong_chart: "problemWrongChart",
28
+ missing_context: "problemMissingContext",
29
+ wrong_unit: "problemWrongUnit",
30
+ bad_grouping: "problemBadGrouping",
31
+ missing_dataset_context: "problemMissingDatasetContext"
32
+ };
33
+ const PresentationFeedback = (props) => {
34
+ const [step, setStep] = solidJs.createSignal("idle");
35
+ const [problems, setProblems] = solidJs.createSignal(/* @__PURE__ */ new Set());
36
+ const [preferred, setPreferred] = solidJs.createSignal(void 0);
37
+ const [comment, setComment] = solidJs.createSignal("");
38
+ const label = (key) => {
39
+ var _a;
40
+ return ((_a = props.labels) == null ? void 0 : _a[key]) ?? DEFAULT_PRESENTATION_FEEDBACK_LABELS[key];
41
+ };
42
+ const emit = (feedback) => {
43
+ try {
44
+ const result = props.onSubmit(feedback);
45
+ if (result && typeof result.catch === "function") {
46
+ ;
47
+ result.catch(() => {
48
+ });
49
+ }
50
+ } catch {
51
+ }
52
+ setStep("done");
53
+ };
54
+ const base = () => ({
55
+ connectorId: props.connectorId,
56
+ toolName: props.toolName,
57
+ ...props.queryHash ? {
58
+ queryHash: props.queryHash
59
+ } : {},
60
+ ...props.renderKind ? {
61
+ renderKind: props.renderKind
62
+ } : {},
63
+ ...props.layoutType ? {
64
+ layoutType: props.layoutType
65
+ } : {}
66
+ });
67
+ const submitReadable = () => emit({
68
+ ...base(),
69
+ verdict: "readable"
70
+ });
71
+ const submitNotReadable = () => {
72
+ const picked = [...problems()];
73
+ const text = comment().trim();
74
+ emit({
75
+ ...base(),
76
+ verdict: "not_readable",
77
+ ...picked.length > 0 ? {
78
+ problems: picked
79
+ } : {},
80
+ ...preferred() ? {
81
+ preferredLayout: preferred()
82
+ } : {},
83
+ ...text ? {
84
+ comment: text
85
+ } : {}
86
+ });
87
+ };
88
+ const toggleProblem = (p) => {
89
+ setProblems((prev) => {
90
+ const next = new Set(prev);
91
+ if (next.has(p)) next.delete(p);
92
+ else next.add(p);
93
+ return next;
94
+ });
95
+ };
96
+ return (() => {
97
+ var _el$ = web.getNextElement(_tmpl$5), _el$16 = _el$.firstChild, [_el$17, _co$3] = web.getNextMarker(_el$16.nextSibling), _el$18 = _el$17.nextSibling, [_el$19, _co$4] = web.getNextMarker(_el$18.nextSibling), _el$20 = _el$19.nextSibling, [_el$21, _co$5] = web.getNextMarker(_el$20.nextSibling);
98
+ web.insert(_el$, web.createComponent(solidJs.Show, {
99
+ get when() {
100
+ return step() === "idle";
101
+ },
102
+ get children() {
103
+ var _el$2 = web.getNextElement(_tmpl$), _el$3 = _el$2.firstChild, _el$4 = _el$3.nextSibling, _el$5 = _el$4.nextSibling;
104
+ web.insert(_el$3, () => label("prompt"));
105
+ _el$4.$$click = submitReadable;
106
+ web.insert(_el$4, () => label("readable"));
107
+ _el$5.$$click = () => setStep("detail");
108
+ web.insert(_el$5, () => label("notReadable"));
109
+ web.runHydrationEvents();
110
+ return _el$2;
111
+ }
112
+ }), _el$17, _co$3);
113
+ web.insert(_el$, web.createComponent(solidJs.Show, {
114
+ get when() {
115
+ return step() === "detail";
116
+ },
117
+ get children() {
118
+ var _el$6 = web.getNextElement(_tmpl$3), _el$7 = _el$6.firstChild, _el$8 = _el$7.nextSibling, _el$13 = _el$8.nextSibling, [_el$14, _co$2] = web.getNextMarker(_el$13.nextSibling), _el$11 = _el$14.nextSibling, _el$12 = _el$11.nextSibling;
119
+ web.insert(_el$7, () => label("problemsPrompt"));
120
+ web.insert(_el$8, web.createComponent(solidJs.For, {
121
+ each: PROBLEM_ORDER,
122
+ children: (p) => (() => {
123
+ var _el$22 = web.getNextElement(_tmpl$6);
124
+ _el$22.$$click = () => toggleProblem(p);
125
+ web.setAttribute(_el$22, "data-presentation-feedback-problem", p);
126
+ web.insert(_el$22, () => label(PROBLEM_LABEL_KEY[p]));
127
+ web.effect((_p$) => {
128
+ var _v$3 = problems().has(p), _v$4 = `px-2 py-0.5 rounded-full border transition-colors ${problems().has(p) ? "border-amber-500 bg-amber-50 dark:bg-amber-900/30 text-amber-700 dark:text-amber-200" : "border-gray-200 dark:border-gray-600 text-deposium-slate-500 hover:border-amber-400"}`;
129
+ _v$3 !== _p$.e && web.setAttribute(_el$22, "aria-pressed", _p$.e = _v$3);
130
+ _v$4 !== _p$.t && web.className(_el$22, _p$.t = _v$4);
131
+ return _p$;
132
+ }, {
133
+ e: void 0,
134
+ t: void 0
135
+ });
136
+ web.runHydrationEvents();
137
+ return _el$22;
138
+ })()
139
+ }));
140
+ web.insert(_el$6, web.createComponent(solidJs.Show, {
141
+ get when() {
142
+ var _a;
143
+ return (((_a = props.preferredLayoutOptions) == null ? void 0 : _a.length) ?? 0) > 0;
144
+ },
145
+ get children() {
146
+ var _el$9 = web.getNextElement(_tmpl$2), _el$0 = _el$9.firstChild, _el$1 = _el$0.nextSibling, [_el$10, _co$] = web.getNextMarker(_el$1.nextSibling);
147
+ web.insert(_el$0, () => label("preferLayoutPrompt"));
148
+ web.insert(_el$9, web.createComponent(solidJs.For, {
149
+ get each() {
150
+ return props.preferredLayoutOptions;
151
+ },
152
+ children: (opt) => (() => {
153
+ var _el$23 = web.getNextElement(_tmpl$6);
154
+ _el$23.$$click = () => setPreferred((cur) => cur === opt ? void 0 : opt);
155
+ web.setAttribute(_el$23, "data-presentation-feedback-layout", opt);
156
+ web.insert(_el$23, opt);
157
+ web.effect((_p$) => {
158
+ var _v$5 = preferred() === opt, _v$6 = `px-2 py-0.5 rounded border transition-colors ${preferred() === opt ? "border-blue-500 bg-blue-50 dark:bg-blue-900/30 text-blue-700 dark:text-blue-200" : "border-gray-200 dark:border-gray-600 text-deposium-slate-500 hover:border-blue-400"}`;
159
+ _v$5 !== _p$.e && web.setAttribute(_el$23, "aria-pressed", _p$.e = _v$5);
160
+ _v$6 !== _p$.t && web.className(_el$23, _p$.t = _v$6);
161
+ return _p$;
162
+ }, {
163
+ e: void 0,
164
+ t: void 0
165
+ });
166
+ web.runHydrationEvents();
167
+ return _el$23;
168
+ })()
169
+ }), _el$10, _co$);
170
+ return _el$9;
171
+ }
172
+ }), _el$14, _co$2);
173
+ _el$11.$$input = (e) => setComment(e.currentTarget.value);
174
+ _el$12.$$click = submitNotReadable;
175
+ web.insert(_el$12, () => label("submit"));
176
+ web.effect(() => web.setAttribute(_el$11, "placeholder", label("commentPlaceholder")));
177
+ web.effect(() => web.setProperty(_el$11, "value", comment()));
178
+ web.runHydrationEvents();
179
+ return _el$6;
180
+ }
181
+ }), _el$19, _co$4);
182
+ web.insert(_el$, web.createComponent(solidJs.Show, {
183
+ get when() {
184
+ return step() === "done";
185
+ },
186
+ get children() {
187
+ var _el$15 = web.getNextElement(_tmpl$4);
188
+ web.insert(_el$15, () => label("ack"));
189
+ return _el$15;
190
+ }
191
+ }), _el$21, _co$5);
192
+ web.effect((_p$) => {
193
+ var _v$ = `text-xs ${props.class ?? ""}`.trim(), _v$2 = step();
194
+ _v$ !== _p$.e && web.className(_el$, _p$.e = _v$);
195
+ _v$2 !== _p$.t && web.setAttribute(_el$, "data-presentation-feedback-step", _p$.t = _v$2);
196
+ return _p$;
197
+ }, {
198
+ e: void 0,
199
+ t: void 0
200
+ });
201
+ return _el$;
202
+ })();
203
+ };
204
+ web.delegateEvents(["click", "input"]);
205
+ exports.DEFAULT_PRESENTATION_FEEDBACK_LABELS = DEFAULT_PRESENTATION_FEEDBACK_LABELS;
206
+ exports.PresentationFeedback = PresentationFeedback;
207
+ //# sourceMappingURL=PresentationFeedback.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PresentationFeedback.cjs","sources":["../../src/components/PresentationFeedback.tsx"],"sourcesContent":["/**\n * PresentationFeedback — feedback on how a connector result was *presented*.\n *\n * @experimental\n * @since v6.6.0 (R3 / D9 of ROADMAP-opendata-macro-mcpui)\n *\n * ## A separate axis from `FeedbackInline`\n *\n * MCP-UI has two distinct feedback widgets, kept separate on purpose\n * (cf. R3) :\n *\n * - **`FeedbackInline`** — was the *answer* good? (response quality)\n * - **`PresentationFeedback`** (this) — was the answer *shown well*?\n * (layout / readability)\n *\n * They are separate components, separate exports, separate payloads — so\n * the two axes never collapse into one in the UX or in the logs.\n *\n * ## Stateless — host owns persistence and re-render\n *\n * On submit the component calls `onSubmit(feedback)` with a\n * `ConnectorRenderFeedback` payload and flips to its acknowledgement state.\n * It does NOT persist anything and does NOT re-render the result itself\n * (cf. D1 : adapter pure + host-owned state). The host persists the\n * feedback and, if it wants to \"close the loop\", re-runs its adapter with\n * the corrected `preferredLayout`.\n *\n * Submission is best-effort : a rejected `onSubmit` promise is swallowed,\n * the UI still flips.\n *\n * ## Localization\n *\n * All labels ship in English and are overridable via the `labels` prop\n * (partial). This component carries its own label bag rather than routing\n * through `MCPUIStringsProvider` — its label set is large and specific to\n * this one widget.\n *\n * @example\n * ```tsx\n * <PresentationFeedback\n * connectorId=\"datagouv\"\n * toolName=\"datagouv.search\"\n * queryHash={result.queryHash}\n * layoutType=\"table\"\n * preferredLayoutOptions={['table', 'bar', 'map']}\n * onSubmit={(fb) => fetch('/api/render-feedback', {\n * method: 'POST', body: JSON.stringify(fb),\n * })}\n * />\n * ```\n */\n\nimport { Component, Show, For, createSignal } from 'solid-js'\nimport type {\n ConnectorRenderFeedback,\n ConnectorRenderProblem,\n ConnectorPreferredLayout,\n} from '@seed-ship/mcp-ui-spec'\n\n/** The full set of problem tags, in display order. */\nconst PROBLEM_ORDER: ConnectorRenderProblem[] = [\n 'too_raw',\n 'wrong_columns',\n 'wrong_chart',\n 'missing_context',\n 'wrong_unit',\n 'bad_grouping',\n 'missing_dataset_context',\n]\n\nexport interface PresentationFeedbackLabels {\n /** Resting-state question. */\n prompt: string\n /** Positive verdict button. */\n readable: string\n /** Negative verdict button — opens the detail step. */\n notReadable: string\n /** Heading of the detail step. */\n problemsPrompt: string\n /** Per-problem chip labels. */\n problemTooRaw: string\n problemWrongColumns: string\n problemWrongChart: string\n problemMissingContext: string\n problemWrongUnit: string\n problemBadGrouping: string\n problemMissingDatasetContext: string\n /** Heading of the optional layout picker. */\n preferLayoutPrompt: string\n /** Free-text comment placeholder. */\n commentPlaceholder: string\n /** Submit button of the detail step. */\n submit: string\n /** Acknowledgement shown after submission. */\n ack: string\n}\n\n/** English defaults. Override via the `labels` prop for other locales. */\nexport const DEFAULT_PRESENTATION_FEEDBACK_LABELS: PresentationFeedbackLabels = {\n prompt: 'Is this shown clearly?',\n readable: 'Clear',\n notReadable: 'Not clear',\n problemsPrompt: \"What's off?\",\n problemTooRaw: 'Too raw',\n problemWrongColumns: 'Wrong columns',\n problemWrongChart: 'Wrong chart',\n problemMissingContext: 'Missing context',\n problemWrongUnit: 'Wrong unit',\n problemBadGrouping: 'Bad grouping',\n problemMissingDatasetContext: 'Missing dataset context',\n preferLayoutPrompt: 'Better shown as',\n commentPlaceholder: 'Anything else? (optional)',\n submit: 'Send feedback',\n ack: 'Thanks — noted.',\n}\n\nconst PROBLEM_LABEL_KEY: Record<ConnectorRenderProblem, keyof PresentationFeedbackLabels> = {\n too_raw: 'problemTooRaw',\n wrong_columns: 'problemWrongColumns',\n wrong_chart: 'problemWrongChart',\n missing_context: 'problemMissingContext',\n wrong_unit: 'problemWrongUnit',\n bad_grouping: 'problemBadGrouping',\n missing_dataset_context: 'problemMissingDatasetContext',\n}\n\nexport interface PresentationFeedbackProps {\n /** Connector whose result is being rated. */\n connectorId: string\n /** Tool that produced the result. */\n toolName: string\n /** Stable key tying feedback to a `ConnectorDynamicResultV1.queryHash`. */\n queryHash?: string\n /** What is being rated (e.g. `'primary'`). Passed through to the payload. */\n renderKind?: string\n /** The layout type currently shown (e.g. `'table'`). Passed through. */\n layoutType?: string\n /**\n * Called once on submit with the assembled `ConnectorRenderFeedback`.\n * Persistence + any re-render are the host's responsibility.\n */\n onSubmit: (feedback: ConnectorRenderFeedback) => void | Promise<void>\n /**\n * Layout alternatives offered in the detail step. Omit (or pass an empty\n * array) to hide the layout picker entirely.\n */\n preferredLayoutOptions?: ConnectorPreferredLayout[]\n /** Partial label override (English defaults otherwise). */\n labels?: Partial<PresentationFeedbackLabels>\n /** Extra Tailwind classes on the container. */\n class?: string\n}\n\n/**\n * @experimental\n * Presentation-quality feedback widget (readable / not-readable + detail).\n */\nexport const PresentationFeedback: Component<PresentationFeedbackProps> = (props) => {\n const [step, setStep] = createSignal<'idle' | 'detail' | 'done'>('idle')\n const [problems, setProblems] = createSignal<Set<ConnectorRenderProblem>>(new Set())\n const [preferred, setPreferred] = createSignal<ConnectorPreferredLayout | undefined>(undefined)\n const [comment, setComment] = createSignal('')\n\n const label = (key: keyof PresentationFeedbackLabels): string =>\n props.labels?.[key] ?? DEFAULT_PRESENTATION_FEEDBACK_LABELS[key]\n\n const emit = (feedback: ConnectorRenderFeedback) => {\n try {\n // Fire-and-forget — feedback is best-effort, a rejection must not\n // break the UI (mirrors FeedbackInline).\n const result = props.onSubmit(feedback)\n if (result && typeof (result as Promise<void>).catch === 'function') {\n ;(result as Promise<void>).catch(() => {\n /* non-blocking */\n })\n }\n } catch {\n /* non-blocking */\n }\n setStep('done')\n }\n\n const base = (): Pick<\n ConnectorRenderFeedback,\n 'connectorId' | 'toolName' | 'queryHash' | 'renderKind' | 'layoutType'\n > => ({\n connectorId: props.connectorId,\n toolName: props.toolName,\n ...(props.queryHash ? { queryHash: props.queryHash } : {}),\n ...(props.renderKind ? { renderKind: props.renderKind } : {}),\n ...(props.layoutType ? { layoutType: props.layoutType } : {}),\n })\n\n const submitReadable = () => emit({ ...base(), verdict: 'readable' })\n\n const submitNotReadable = () => {\n const picked = [...problems()]\n const text = comment().trim()\n emit({\n ...base(),\n verdict: 'not_readable',\n ...(picked.length > 0 ? { problems: picked } : {}),\n ...(preferred() ? { preferredLayout: preferred() } : {}),\n ...(text ? { comment: text } : {}),\n })\n }\n\n const toggleProblem = (p: ConnectorRenderProblem) => {\n setProblems((prev) => {\n const next = new Set(prev)\n if (next.has(p)) next.delete(p)\n else next.add(p)\n return next\n })\n }\n\n return (\n <div\n class={`text-xs ${props.class ?? ''}`.trim()}\n data-presentation-feedback-step={step()}\n >\n {/* ── Step 1 : verdict ─────────────────────────────── */}\n <Show when={step() === 'idle'}>\n <div class=\"flex items-center gap-2\">\n <span class=\"text-deposium-slate-500\">{label('prompt')}</span>\n <button\n type=\"button\"\n onClick={submitReadable}\n class=\"px-2 py-0.5 rounded border border-gray-200 dark:border-gray-600 text-deposium-slate-600 dark:text-gray-300 hover:border-green-500 hover:text-green-600 transition-colors\"\n data-presentation-feedback-verdict=\"readable\"\n >\n {label('readable')}\n </button>\n <button\n type=\"button\"\n onClick={() => setStep('detail')}\n class=\"px-2 py-0.5 rounded border border-gray-200 dark:border-gray-600 text-deposium-slate-600 dark:text-gray-300 hover:border-amber-500 hover:text-amber-600 transition-colors\"\n data-presentation-feedback-verdict=\"not_readable\"\n >\n {label('notReadable')}\n </button>\n </div>\n </Show>\n\n {/* ── Step 2 : detail ──────────────────────────────── */}\n <Show when={step() === 'detail'}>\n <div class=\"flex flex-col gap-2 p-2 rounded-lg border border-gray-200 dark:border-gray-700\">\n <span class=\"font-medium text-deposium-slate-600 dark:text-gray-300\">\n {label('problemsPrompt')}\n </span>\n\n {/* Problem chips */}\n <div class=\"flex flex-wrap gap-1\">\n <For each={PROBLEM_ORDER}>\n {(p) => (\n <button\n type=\"button\"\n onClick={() => toggleProblem(p)}\n aria-pressed={problems().has(p)}\n data-presentation-feedback-problem={p}\n class={`px-2 py-0.5 rounded-full border transition-colors ${\n problems().has(p)\n ? 'border-amber-500 bg-amber-50 dark:bg-amber-900/30 text-amber-700 dark:text-amber-200'\n : 'border-gray-200 dark:border-gray-600 text-deposium-slate-500 hover:border-amber-400'\n }`}\n >\n {label(PROBLEM_LABEL_KEY[p])}\n </button>\n )}\n </For>\n </div>\n\n {/* Optional layout picker */}\n <Show when={(props.preferredLayoutOptions?.length ?? 0) > 0}>\n <div class=\"flex flex-wrap items-center gap-1\">\n <span class=\"text-deposium-slate-500\">{label('preferLayoutPrompt')}</span>\n <For each={props.preferredLayoutOptions}>\n {(opt) => (\n <button\n type=\"button\"\n onClick={() => setPreferred((cur) => (cur === opt ? undefined : opt))}\n aria-pressed={preferred() === opt}\n data-presentation-feedback-layout={opt}\n class={`px-2 py-0.5 rounded border transition-colors ${\n preferred() === opt\n ? 'border-blue-500 bg-blue-50 dark:bg-blue-900/30 text-blue-700 dark:text-blue-200'\n : 'border-gray-200 dark:border-gray-600 text-deposium-slate-500 hover:border-blue-400'\n }`}\n >\n {opt}\n </button>\n )}\n </For>\n </div>\n </Show>\n\n {/* Free-text comment */}\n <textarea\n value={comment()}\n onInput={(e) => setComment(e.currentTarget.value)}\n placeholder={label('commentPlaceholder')}\n rows={2}\n class=\"w-full rounded border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-800 p-1.5 text-xs resize-y\"\n data-presentation-feedback-comment\n />\n\n <button\n type=\"button\"\n onClick={submitNotReadable}\n class=\"self-end px-3 py-1 rounded bg-amber-500 text-white font-medium hover:bg-amber-600 transition-colors\"\n data-presentation-feedback-submit\n >\n {label('submit')}\n </button>\n </div>\n </Show>\n\n {/* ── Step 3 : acknowledgement ─────────────────────── */}\n <Show when={step() === 'done'}>\n <span class=\"text-deposium-slate-500\" data-presentation-feedback-ack>\n {label('ack')}\n </span>\n </Show>\n </div>\n )\n}\n"],"names":["PROBLEM_ORDER","DEFAULT_PRESENTATION_FEEDBACK_LABELS","prompt","readable","notReadable","problemsPrompt","problemTooRaw","problemWrongColumns","problemWrongChart","problemMissingContext","problemWrongUnit","problemBadGrouping","problemMissingDatasetContext","preferLayoutPrompt","commentPlaceholder","submit","ack","PROBLEM_LABEL_KEY","too_raw","wrong_columns","wrong_chart","missing_context","wrong_unit","bad_grouping","missing_dataset_context","PresentationFeedback","props","step","setStep","createSignal","problems","setProblems","Set","preferred","setPreferred","undefined","comment","setComment","label","key","labels","emit","feedback","result","onSubmit","catch","base","connectorId","toolName","queryHash","renderKind","layoutType","submitReadable","verdict","submitNotReadable","picked","text","trim","length","preferredLayout","toggleProblem","p","prev","next","has","delete","add","_el$","_$getNextElement","_tmpl$5","_el$16","firstChild","_el$17","_co$3","_$getNextMarker","nextSibling","_el$18","_el$19","_co$4","_el$20","_el$21","_co$5","_$insert","_$createComponent","Show","when","children","_el$2","_tmpl$","_el$3","_el$4","_el$5","$$click","_$runHydrationEvents","_el$6","_tmpl$3","_el$7","_el$8","_el$13","_el$14","_co$2","_el$11","_el$12","For","each","_el$22","_tmpl$6","_$setAttribute","_$effect","_p$","_v$3","_v$4","e","t","_$className","preferredLayoutOptions","_el$9","_tmpl$2","_el$0","_el$1","_el$10","_co$","opt","_el$23","cur","_v$5","_v$6","$$input","currentTarget","value","_$setProperty","_el$15","_tmpl$4","_v$","class","_v$2","_$delegateEvents"],"mappings":"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"}
@@ -0,0 +1,113 @@
1
+ /**
2
+ * PresentationFeedback — feedback on how a connector result was *presented*.
3
+ *
4
+ * @experimental
5
+ * @since v6.6.0 (R3 / D9 of ROADMAP-opendata-macro-mcpui)
6
+ *
7
+ * ## A separate axis from `FeedbackInline`
8
+ *
9
+ * MCP-UI has two distinct feedback widgets, kept separate on purpose
10
+ * (cf. R3) :
11
+ *
12
+ * - **`FeedbackInline`** — was the *answer* good? (response quality)
13
+ * - **`PresentationFeedback`** (this) — was the answer *shown well*?
14
+ * (layout / readability)
15
+ *
16
+ * They are separate components, separate exports, separate payloads — so
17
+ * the two axes never collapse into one in the UX or in the logs.
18
+ *
19
+ * ## Stateless — host owns persistence and re-render
20
+ *
21
+ * On submit the component calls `onSubmit(feedback)` with a
22
+ * `ConnectorRenderFeedback` payload and flips to its acknowledgement state.
23
+ * It does NOT persist anything and does NOT re-render the result itself
24
+ * (cf. D1 : adapter pure + host-owned state). The host persists the
25
+ * feedback and, if it wants to "close the loop", re-runs its adapter with
26
+ * the corrected `preferredLayout`.
27
+ *
28
+ * Submission is best-effort : a rejected `onSubmit` promise is swallowed,
29
+ * the UI still flips.
30
+ *
31
+ * ## Localization
32
+ *
33
+ * All labels ship in English and are overridable via the `labels` prop
34
+ * (partial). This component carries its own label bag rather than routing
35
+ * through `MCPUIStringsProvider` — its label set is large and specific to
36
+ * this one widget.
37
+ *
38
+ * @example
39
+ * ```tsx
40
+ * <PresentationFeedback
41
+ * connectorId="datagouv"
42
+ * toolName="datagouv.search"
43
+ * queryHash={result.queryHash}
44
+ * layoutType="table"
45
+ * preferredLayoutOptions={['table', 'bar', 'map']}
46
+ * onSubmit={(fb) => fetch('/api/render-feedback', {
47
+ * method: 'POST', body: JSON.stringify(fb),
48
+ * })}
49
+ * />
50
+ * ```
51
+ */
52
+ import { Component } from 'solid-js';
53
+ import type { ConnectorRenderFeedback, ConnectorPreferredLayout } from '@seed-ship/mcp-ui-spec';
54
+ export interface PresentationFeedbackLabels {
55
+ /** Resting-state question. */
56
+ prompt: string;
57
+ /** Positive verdict button. */
58
+ readable: string;
59
+ /** Negative verdict button — opens the detail step. */
60
+ notReadable: string;
61
+ /** Heading of the detail step. */
62
+ problemsPrompt: string;
63
+ /** Per-problem chip labels. */
64
+ problemTooRaw: string;
65
+ problemWrongColumns: string;
66
+ problemWrongChart: string;
67
+ problemMissingContext: string;
68
+ problemWrongUnit: string;
69
+ problemBadGrouping: string;
70
+ problemMissingDatasetContext: string;
71
+ /** Heading of the optional layout picker. */
72
+ preferLayoutPrompt: string;
73
+ /** Free-text comment placeholder. */
74
+ commentPlaceholder: string;
75
+ /** Submit button of the detail step. */
76
+ submit: string;
77
+ /** Acknowledgement shown after submission. */
78
+ ack: string;
79
+ }
80
+ /** English defaults. Override via the `labels` prop for other locales. */
81
+ export declare const DEFAULT_PRESENTATION_FEEDBACK_LABELS: PresentationFeedbackLabels;
82
+ export interface PresentationFeedbackProps {
83
+ /** Connector whose result is being rated. */
84
+ connectorId: string;
85
+ /** Tool that produced the result. */
86
+ toolName: string;
87
+ /** Stable key tying feedback to a `ConnectorDynamicResultV1.queryHash`. */
88
+ queryHash?: string;
89
+ /** What is being rated (e.g. `'primary'`). Passed through to the payload. */
90
+ renderKind?: string;
91
+ /** The layout type currently shown (e.g. `'table'`). Passed through. */
92
+ layoutType?: string;
93
+ /**
94
+ * Called once on submit with the assembled `ConnectorRenderFeedback`.
95
+ * Persistence + any re-render are the host's responsibility.
96
+ */
97
+ onSubmit: (feedback: ConnectorRenderFeedback) => void | Promise<void>;
98
+ /**
99
+ * Layout alternatives offered in the detail step. Omit (or pass an empty
100
+ * array) to hide the layout picker entirely.
101
+ */
102
+ preferredLayoutOptions?: ConnectorPreferredLayout[];
103
+ /** Partial label override (English defaults otherwise). */
104
+ labels?: Partial<PresentationFeedbackLabels>;
105
+ /** Extra Tailwind classes on the container. */
106
+ class?: string;
107
+ }
108
+ /**
109
+ * @experimental
110
+ * Presentation-quality feedback widget (readable / not-readable + detail).
111
+ */
112
+ export declare const PresentationFeedback: Component<PresentationFeedbackProps>;
113
+ //# sourceMappingURL=PresentationFeedback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PresentationFeedback.d.ts","sourceRoot":"","sources":["../../src/components/PresentationFeedback.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AAEH,OAAO,EAAE,SAAS,EAA2B,MAAM,UAAU,CAAA;AAC7D,OAAO,KAAK,EACV,uBAAuB,EAEvB,wBAAwB,EACzB,MAAM,wBAAwB,CAAA;AAa/B,MAAM,WAAW,0BAA0B;IACzC,8BAA8B;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAA;IAChB,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAA;IACnB,kCAAkC;IAClC,cAAc,EAAE,MAAM,CAAA;IACtB,+BAA+B;IAC/B,aAAa,EAAE,MAAM,CAAA;IACrB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,iBAAiB,EAAE,MAAM,CAAA;IACzB,qBAAqB,EAAE,MAAM,CAAA;IAC7B,gBAAgB,EAAE,MAAM,CAAA;IACxB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,4BAA4B,EAAE,MAAM,CAAA;IACpC,6CAA6C;IAC7C,kBAAkB,EAAE,MAAM,CAAA;IAC1B,qCAAqC;IACrC,kBAAkB,EAAE,MAAM,CAAA;IAC1B,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAA;IACd,8CAA8C;IAC9C,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,0EAA0E;AAC1E,eAAO,MAAM,oCAAoC,EAAE,0BAgBlD,CAAA;AAYD,MAAM,WAAW,yBAAyB;IACxC,6CAA6C;IAC7C,WAAW,EAAE,MAAM,CAAA;IACnB,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAA;IAChB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;OAGG;IACH,QAAQ,EAAE,CAAC,QAAQ,EAAE,uBAAuB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACrE;;;OAGG;IACH,sBAAsB,CAAC,EAAE,wBAAwB,EAAE,CAAA;IACnD,2DAA2D;IAC3D,MAAM,CAAC,EAAE,OAAO,CAAC,0BAA0B,CAAC,CAAA;IAC5C,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,SAAS,CAAC,yBAAyB,CAwKrE,CAAA"}
@@ -0,0 +1,207 @@
1
+ import { delegateEvents, getNextElement, template, getNextMarker, insert, createComponent, runHydrationEvents, setAttribute, effect, className, setProperty } from "solid-js/web";
2
+ import { createSignal, Show, For } from "solid-js";
3
+ var _tmpl$ = /* @__PURE__ */ template(`<div class="flex items-center gap-2"><span class=text-deposium-slate-500></span><button type=button class="px-2 py-0.5 rounded border border-gray-200 dark:border-gray-600 text-deposium-slate-600 dark:text-gray-300 hover:border-green-500 hover:text-green-600 transition-colors"data-presentation-feedback-verdict=readable></button><button type=button class="px-2 py-0.5 rounded border border-gray-200 dark:border-gray-600 text-deposium-slate-600 dark:text-gray-300 hover:border-amber-500 hover:text-amber-600 transition-colors"data-presentation-feedback-verdict=not_readable>`), _tmpl$2 = /* @__PURE__ */ template(`<div class="flex flex-wrap items-center gap-1"><span class=text-deposium-slate-500></span><!$><!/>`), _tmpl$3 = /* @__PURE__ */ template(`<div class="flex flex-col gap-2 p-2 rounded-lg border border-gray-200 dark:border-gray-700"><span class="font-medium text-deposium-slate-600 dark:text-gray-300"></span><div class="flex flex-wrap gap-1"></div><!$><!/><textarea rows=2 class="w-full rounded border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-800 p-1.5 text-xs resize-y"data-presentation-feedback-comment></textarea><button type=button class="self-end px-3 py-1 rounded bg-amber-500 text-white font-medium hover:bg-amber-600 transition-colors"data-presentation-feedback-submit>`), _tmpl$4 = /* @__PURE__ */ template(`<span class=text-deposium-slate-500 data-presentation-feedback-ack>`), _tmpl$5 = /* @__PURE__ */ template(`<div><!$><!/><!$><!/><!$><!/>`), _tmpl$6 = /* @__PURE__ */ template(`<button type=button>`);
4
+ const PROBLEM_ORDER = ["too_raw", "wrong_columns", "wrong_chart", "missing_context", "wrong_unit", "bad_grouping", "missing_dataset_context"];
5
+ const DEFAULT_PRESENTATION_FEEDBACK_LABELS = {
6
+ prompt: "Is this shown clearly?",
7
+ readable: "Clear",
8
+ notReadable: "Not clear",
9
+ problemsPrompt: "What's off?",
10
+ problemTooRaw: "Too raw",
11
+ problemWrongColumns: "Wrong columns",
12
+ problemWrongChart: "Wrong chart",
13
+ problemMissingContext: "Missing context",
14
+ problemWrongUnit: "Wrong unit",
15
+ problemBadGrouping: "Bad grouping",
16
+ problemMissingDatasetContext: "Missing dataset context",
17
+ preferLayoutPrompt: "Better shown as",
18
+ commentPlaceholder: "Anything else? (optional)",
19
+ submit: "Send feedback",
20
+ ack: "Thanks — noted."
21
+ };
22
+ const PROBLEM_LABEL_KEY = {
23
+ too_raw: "problemTooRaw",
24
+ wrong_columns: "problemWrongColumns",
25
+ wrong_chart: "problemWrongChart",
26
+ missing_context: "problemMissingContext",
27
+ wrong_unit: "problemWrongUnit",
28
+ bad_grouping: "problemBadGrouping",
29
+ missing_dataset_context: "problemMissingDatasetContext"
30
+ };
31
+ const PresentationFeedback = (props) => {
32
+ const [step, setStep] = createSignal("idle");
33
+ const [problems, setProblems] = createSignal(/* @__PURE__ */ new Set());
34
+ const [preferred, setPreferred] = createSignal(void 0);
35
+ const [comment, setComment] = createSignal("");
36
+ const label = (key) => {
37
+ var _a;
38
+ return ((_a = props.labels) == null ? void 0 : _a[key]) ?? DEFAULT_PRESENTATION_FEEDBACK_LABELS[key];
39
+ };
40
+ const emit = (feedback) => {
41
+ try {
42
+ const result = props.onSubmit(feedback);
43
+ if (result && typeof result.catch === "function") {
44
+ ;
45
+ result.catch(() => {
46
+ });
47
+ }
48
+ } catch {
49
+ }
50
+ setStep("done");
51
+ };
52
+ const base = () => ({
53
+ connectorId: props.connectorId,
54
+ toolName: props.toolName,
55
+ ...props.queryHash ? {
56
+ queryHash: props.queryHash
57
+ } : {},
58
+ ...props.renderKind ? {
59
+ renderKind: props.renderKind
60
+ } : {},
61
+ ...props.layoutType ? {
62
+ layoutType: props.layoutType
63
+ } : {}
64
+ });
65
+ const submitReadable = () => emit({
66
+ ...base(),
67
+ verdict: "readable"
68
+ });
69
+ const submitNotReadable = () => {
70
+ const picked = [...problems()];
71
+ const text = comment().trim();
72
+ emit({
73
+ ...base(),
74
+ verdict: "not_readable",
75
+ ...picked.length > 0 ? {
76
+ problems: picked
77
+ } : {},
78
+ ...preferred() ? {
79
+ preferredLayout: preferred()
80
+ } : {},
81
+ ...text ? {
82
+ comment: text
83
+ } : {}
84
+ });
85
+ };
86
+ const toggleProblem = (p) => {
87
+ setProblems((prev) => {
88
+ const next = new Set(prev);
89
+ if (next.has(p)) next.delete(p);
90
+ else next.add(p);
91
+ return next;
92
+ });
93
+ };
94
+ return (() => {
95
+ var _el$ = getNextElement(_tmpl$5), _el$16 = _el$.firstChild, [_el$17, _co$3] = getNextMarker(_el$16.nextSibling), _el$18 = _el$17.nextSibling, [_el$19, _co$4] = getNextMarker(_el$18.nextSibling), _el$20 = _el$19.nextSibling, [_el$21, _co$5] = getNextMarker(_el$20.nextSibling);
96
+ insert(_el$, createComponent(Show, {
97
+ get when() {
98
+ return step() === "idle";
99
+ },
100
+ get children() {
101
+ var _el$2 = getNextElement(_tmpl$), _el$3 = _el$2.firstChild, _el$4 = _el$3.nextSibling, _el$5 = _el$4.nextSibling;
102
+ insert(_el$3, () => label("prompt"));
103
+ _el$4.$$click = submitReadable;
104
+ insert(_el$4, () => label("readable"));
105
+ _el$5.$$click = () => setStep("detail");
106
+ insert(_el$5, () => label("notReadable"));
107
+ runHydrationEvents();
108
+ return _el$2;
109
+ }
110
+ }), _el$17, _co$3);
111
+ insert(_el$, createComponent(Show, {
112
+ get when() {
113
+ return step() === "detail";
114
+ },
115
+ get children() {
116
+ var _el$6 = getNextElement(_tmpl$3), _el$7 = _el$6.firstChild, _el$8 = _el$7.nextSibling, _el$13 = _el$8.nextSibling, [_el$14, _co$2] = getNextMarker(_el$13.nextSibling), _el$11 = _el$14.nextSibling, _el$12 = _el$11.nextSibling;
117
+ insert(_el$7, () => label("problemsPrompt"));
118
+ insert(_el$8, createComponent(For, {
119
+ each: PROBLEM_ORDER,
120
+ children: (p) => (() => {
121
+ var _el$22 = getNextElement(_tmpl$6);
122
+ _el$22.$$click = () => toggleProblem(p);
123
+ setAttribute(_el$22, "data-presentation-feedback-problem", p);
124
+ insert(_el$22, () => label(PROBLEM_LABEL_KEY[p]));
125
+ effect((_p$) => {
126
+ var _v$3 = problems().has(p), _v$4 = `px-2 py-0.5 rounded-full border transition-colors ${problems().has(p) ? "border-amber-500 bg-amber-50 dark:bg-amber-900/30 text-amber-700 dark:text-amber-200" : "border-gray-200 dark:border-gray-600 text-deposium-slate-500 hover:border-amber-400"}`;
127
+ _v$3 !== _p$.e && setAttribute(_el$22, "aria-pressed", _p$.e = _v$3);
128
+ _v$4 !== _p$.t && className(_el$22, _p$.t = _v$4);
129
+ return _p$;
130
+ }, {
131
+ e: void 0,
132
+ t: void 0
133
+ });
134
+ runHydrationEvents();
135
+ return _el$22;
136
+ })()
137
+ }));
138
+ insert(_el$6, createComponent(Show, {
139
+ get when() {
140
+ var _a;
141
+ return (((_a = props.preferredLayoutOptions) == null ? void 0 : _a.length) ?? 0) > 0;
142
+ },
143
+ get children() {
144
+ var _el$9 = getNextElement(_tmpl$2), _el$0 = _el$9.firstChild, _el$1 = _el$0.nextSibling, [_el$10, _co$] = getNextMarker(_el$1.nextSibling);
145
+ insert(_el$0, () => label("preferLayoutPrompt"));
146
+ insert(_el$9, createComponent(For, {
147
+ get each() {
148
+ return props.preferredLayoutOptions;
149
+ },
150
+ children: (opt) => (() => {
151
+ var _el$23 = getNextElement(_tmpl$6);
152
+ _el$23.$$click = () => setPreferred((cur) => cur === opt ? void 0 : opt);
153
+ setAttribute(_el$23, "data-presentation-feedback-layout", opt);
154
+ insert(_el$23, opt);
155
+ effect((_p$) => {
156
+ var _v$5 = preferred() === opt, _v$6 = `px-2 py-0.5 rounded border transition-colors ${preferred() === opt ? "border-blue-500 bg-blue-50 dark:bg-blue-900/30 text-blue-700 dark:text-blue-200" : "border-gray-200 dark:border-gray-600 text-deposium-slate-500 hover:border-blue-400"}`;
157
+ _v$5 !== _p$.e && setAttribute(_el$23, "aria-pressed", _p$.e = _v$5);
158
+ _v$6 !== _p$.t && className(_el$23, _p$.t = _v$6);
159
+ return _p$;
160
+ }, {
161
+ e: void 0,
162
+ t: void 0
163
+ });
164
+ runHydrationEvents();
165
+ return _el$23;
166
+ })()
167
+ }), _el$10, _co$);
168
+ return _el$9;
169
+ }
170
+ }), _el$14, _co$2);
171
+ _el$11.$$input = (e) => setComment(e.currentTarget.value);
172
+ _el$12.$$click = submitNotReadable;
173
+ insert(_el$12, () => label("submit"));
174
+ effect(() => setAttribute(_el$11, "placeholder", label("commentPlaceholder")));
175
+ effect(() => setProperty(_el$11, "value", comment()));
176
+ runHydrationEvents();
177
+ return _el$6;
178
+ }
179
+ }), _el$19, _co$4);
180
+ insert(_el$, createComponent(Show, {
181
+ get when() {
182
+ return step() === "done";
183
+ },
184
+ get children() {
185
+ var _el$15 = getNextElement(_tmpl$4);
186
+ insert(_el$15, () => label("ack"));
187
+ return _el$15;
188
+ }
189
+ }), _el$21, _co$5);
190
+ effect((_p$) => {
191
+ var _v$ = `text-xs ${props.class ?? ""}`.trim(), _v$2 = step();
192
+ _v$ !== _p$.e && className(_el$, _p$.e = _v$);
193
+ _v$2 !== _p$.t && setAttribute(_el$, "data-presentation-feedback-step", _p$.t = _v$2);
194
+ return _p$;
195
+ }, {
196
+ e: void 0,
197
+ t: void 0
198
+ });
199
+ return _el$;
200
+ })();
201
+ };
202
+ delegateEvents(["click", "input"]);
203
+ export {
204
+ DEFAULT_PRESENTATION_FEEDBACK_LABELS,
205
+ PresentationFeedback
206
+ };
207
+ //# sourceMappingURL=PresentationFeedback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PresentationFeedback.js","sources":["../../src/components/PresentationFeedback.tsx"],"sourcesContent":["/**\n * PresentationFeedback — feedback on how a connector result was *presented*.\n *\n * @experimental\n * @since v6.6.0 (R3 / D9 of ROADMAP-opendata-macro-mcpui)\n *\n * ## A separate axis from `FeedbackInline`\n *\n * MCP-UI has two distinct feedback widgets, kept separate on purpose\n * (cf. R3) :\n *\n * - **`FeedbackInline`** — was the *answer* good? (response quality)\n * - **`PresentationFeedback`** (this) — was the answer *shown well*?\n * (layout / readability)\n *\n * They are separate components, separate exports, separate payloads — so\n * the two axes never collapse into one in the UX or in the logs.\n *\n * ## Stateless — host owns persistence and re-render\n *\n * On submit the component calls `onSubmit(feedback)` with a\n * `ConnectorRenderFeedback` payload and flips to its acknowledgement state.\n * It does NOT persist anything and does NOT re-render the result itself\n * (cf. D1 : adapter pure + host-owned state). The host persists the\n * feedback and, if it wants to \"close the loop\", re-runs its adapter with\n * the corrected `preferredLayout`.\n *\n * Submission is best-effort : a rejected `onSubmit` promise is swallowed,\n * the UI still flips.\n *\n * ## Localization\n *\n * All labels ship in English and are overridable via the `labels` prop\n * (partial). This component carries its own label bag rather than routing\n * through `MCPUIStringsProvider` — its label set is large and specific to\n * this one widget.\n *\n * @example\n * ```tsx\n * <PresentationFeedback\n * connectorId=\"datagouv\"\n * toolName=\"datagouv.search\"\n * queryHash={result.queryHash}\n * layoutType=\"table\"\n * preferredLayoutOptions={['table', 'bar', 'map']}\n * onSubmit={(fb) => fetch('/api/render-feedback', {\n * method: 'POST', body: JSON.stringify(fb),\n * })}\n * />\n * ```\n */\n\nimport { Component, Show, For, createSignal } from 'solid-js'\nimport type {\n ConnectorRenderFeedback,\n ConnectorRenderProblem,\n ConnectorPreferredLayout,\n} from '@seed-ship/mcp-ui-spec'\n\n/** The full set of problem tags, in display order. */\nconst PROBLEM_ORDER: ConnectorRenderProblem[] = [\n 'too_raw',\n 'wrong_columns',\n 'wrong_chart',\n 'missing_context',\n 'wrong_unit',\n 'bad_grouping',\n 'missing_dataset_context',\n]\n\nexport interface PresentationFeedbackLabels {\n /** Resting-state question. */\n prompt: string\n /** Positive verdict button. */\n readable: string\n /** Negative verdict button — opens the detail step. */\n notReadable: string\n /** Heading of the detail step. */\n problemsPrompt: string\n /** Per-problem chip labels. */\n problemTooRaw: string\n problemWrongColumns: string\n problemWrongChart: string\n problemMissingContext: string\n problemWrongUnit: string\n problemBadGrouping: string\n problemMissingDatasetContext: string\n /** Heading of the optional layout picker. */\n preferLayoutPrompt: string\n /** Free-text comment placeholder. */\n commentPlaceholder: string\n /** Submit button of the detail step. */\n submit: string\n /** Acknowledgement shown after submission. */\n ack: string\n}\n\n/** English defaults. Override via the `labels` prop for other locales. */\nexport const DEFAULT_PRESENTATION_FEEDBACK_LABELS: PresentationFeedbackLabels = {\n prompt: 'Is this shown clearly?',\n readable: 'Clear',\n notReadable: 'Not clear',\n problemsPrompt: \"What's off?\",\n problemTooRaw: 'Too raw',\n problemWrongColumns: 'Wrong columns',\n problemWrongChart: 'Wrong chart',\n problemMissingContext: 'Missing context',\n problemWrongUnit: 'Wrong unit',\n problemBadGrouping: 'Bad grouping',\n problemMissingDatasetContext: 'Missing dataset context',\n preferLayoutPrompt: 'Better shown as',\n commentPlaceholder: 'Anything else? (optional)',\n submit: 'Send feedback',\n ack: 'Thanks — noted.',\n}\n\nconst PROBLEM_LABEL_KEY: Record<ConnectorRenderProblem, keyof PresentationFeedbackLabels> = {\n too_raw: 'problemTooRaw',\n wrong_columns: 'problemWrongColumns',\n wrong_chart: 'problemWrongChart',\n missing_context: 'problemMissingContext',\n wrong_unit: 'problemWrongUnit',\n bad_grouping: 'problemBadGrouping',\n missing_dataset_context: 'problemMissingDatasetContext',\n}\n\nexport interface PresentationFeedbackProps {\n /** Connector whose result is being rated. */\n connectorId: string\n /** Tool that produced the result. */\n toolName: string\n /** Stable key tying feedback to a `ConnectorDynamicResultV1.queryHash`. */\n queryHash?: string\n /** What is being rated (e.g. `'primary'`). Passed through to the payload. */\n renderKind?: string\n /** The layout type currently shown (e.g. `'table'`). Passed through. */\n layoutType?: string\n /**\n * Called once on submit with the assembled `ConnectorRenderFeedback`.\n * Persistence + any re-render are the host's responsibility.\n */\n onSubmit: (feedback: ConnectorRenderFeedback) => void | Promise<void>\n /**\n * Layout alternatives offered in the detail step. Omit (or pass an empty\n * array) to hide the layout picker entirely.\n */\n preferredLayoutOptions?: ConnectorPreferredLayout[]\n /** Partial label override (English defaults otherwise). */\n labels?: Partial<PresentationFeedbackLabels>\n /** Extra Tailwind classes on the container. */\n class?: string\n}\n\n/**\n * @experimental\n * Presentation-quality feedback widget (readable / not-readable + detail).\n */\nexport const PresentationFeedback: Component<PresentationFeedbackProps> = (props) => {\n const [step, setStep] = createSignal<'idle' | 'detail' | 'done'>('idle')\n const [problems, setProblems] = createSignal<Set<ConnectorRenderProblem>>(new Set())\n const [preferred, setPreferred] = createSignal<ConnectorPreferredLayout | undefined>(undefined)\n const [comment, setComment] = createSignal('')\n\n const label = (key: keyof PresentationFeedbackLabels): string =>\n props.labels?.[key] ?? DEFAULT_PRESENTATION_FEEDBACK_LABELS[key]\n\n const emit = (feedback: ConnectorRenderFeedback) => {\n try {\n // Fire-and-forget — feedback is best-effort, a rejection must not\n // break the UI (mirrors FeedbackInline).\n const result = props.onSubmit(feedback)\n if (result && typeof (result as Promise<void>).catch === 'function') {\n ;(result as Promise<void>).catch(() => {\n /* non-blocking */\n })\n }\n } catch {\n /* non-blocking */\n }\n setStep('done')\n }\n\n const base = (): Pick<\n ConnectorRenderFeedback,\n 'connectorId' | 'toolName' | 'queryHash' | 'renderKind' | 'layoutType'\n > => ({\n connectorId: props.connectorId,\n toolName: props.toolName,\n ...(props.queryHash ? { queryHash: props.queryHash } : {}),\n ...(props.renderKind ? { renderKind: props.renderKind } : {}),\n ...(props.layoutType ? { layoutType: props.layoutType } : {}),\n })\n\n const submitReadable = () => emit({ ...base(), verdict: 'readable' })\n\n const submitNotReadable = () => {\n const picked = [...problems()]\n const text = comment().trim()\n emit({\n ...base(),\n verdict: 'not_readable',\n ...(picked.length > 0 ? { problems: picked } : {}),\n ...(preferred() ? { preferredLayout: preferred() } : {}),\n ...(text ? { comment: text } : {}),\n })\n }\n\n const toggleProblem = (p: ConnectorRenderProblem) => {\n setProblems((prev) => {\n const next = new Set(prev)\n if (next.has(p)) next.delete(p)\n else next.add(p)\n return next\n })\n }\n\n return (\n <div\n class={`text-xs ${props.class ?? ''}`.trim()}\n data-presentation-feedback-step={step()}\n >\n {/* ── Step 1 : verdict ─────────────────────────────── */}\n <Show when={step() === 'idle'}>\n <div class=\"flex items-center gap-2\">\n <span class=\"text-deposium-slate-500\">{label('prompt')}</span>\n <button\n type=\"button\"\n onClick={submitReadable}\n class=\"px-2 py-0.5 rounded border border-gray-200 dark:border-gray-600 text-deposium-slate-600 dark:text-gray-300 hover:border-green-500 hover:text-green-600 transition-colors\"\n data-presentation-feedback-verdict=\"readable\"\n >\n {label('readable')}\n </button>\n <button\n type=\"button\"\n onClick={() => setStep('detail')}\n class=\"px-2 py-0.5 rounded border border-gray-200 dark:border-gray-600 text-deposium-slate-600 dark:text-gray-300 hover:border-amber-500 hover:text-amber-600 transition-colors\"\n data-presentation-feedback-verdict=\"not_readable\"\n >\n {label('notReadable')}\n </button>\n </div>\n </Show>\n\n {/* ── Step 2 : detail ──────────────────────────────── */}\n <Show when={step() === 'detail'}>\n <div class=\"flex flex-col gap-2 p-2 rounded-lg border border-gray-200 dark:border-gray-700\">\n <span class=\"font-medium text-deposium-slate-600 dark:text-gray-300\">\n {label('problemsPrompt')}\n </span>\n\n {/* Problem chips */}\n <div class=\"flex flex-wrap gap-1\">\n <For each={PROBLEM_ORDER}>\n {(p) => (\n <button\n type=\"button\"\n onClick={() => toggleProblem(p)}\n aria-pressed={problems().has(p)}\n data-presentation-feedback-problem={p}\n class={`px-2 py-0.5 rounded-full border transition-colors ${\n problems().has(p)\n ? 'border-amber-500 bg-amber-50 dark:bg-amber-900/30 text-amber-700 dark:text-amber-200'\n : 'border-gray-200 dark:border-gray-600 text-deposium-slate-500 hover:border-amber-400'\n }`}\n >\n {label(PROBLEM_LABEL_KEY[p])}\n </button>\n )}\n </For>\n </div>\n\n {/* Optional layout picker */}\n <Show when={(props.preferredLayoutOptions?.length ?? 0) > 0}>\n <div class=\"flex flex-wrap items-center gap-1\">\n <span class=\"text-deposium-slate-500\">{label('preferLayoutPrompt')}</span>\n <For each={props.preferredLayoutOptions}>\n {(opt) => (\n <button\n type=\"button\"\n onClick={() => setPreferred((cur) => (cur === opt ? undefined : opt))}\n aria-pressed={preferred() === opt}\n data-presentation-feedback-layout={opt}\n class={`px-2 py-0.5 rounded border transition-colors ${\n preferred() === opt\n ? 'border-blue-500 bg-blue-50 dark:bg-blue-900/30 text-blue-700 dark:text-blue-200'\n : 'border-gray-200 dark:border-gray-600 text-deposium-slate-500 hover:border-blue-400'\n }`}\n >\n {opt}\n </button>\n )}\n </For>\n </div>\n </Show>\n\n {/* Free-text comment */}\n <textarea\n value={comment()}\n onInput={(e) => setComment(e.currentTarget.value)}\n placeholder={label('commentPlaceholder')}\n rows={2}\n class=\"w-full rounded border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-800 p-1.5 text-xs resize-y\"\n data-presentation-feedback-comment\n />\n\n <button\n type=\"button\"\n onClick={submitNotReadable}\n class=\"self-end px-3 py-1 rounded bg-amber-500 text-white font-medium hover:bg-amber-600 transition-colors\"\n data-presentation-feedback-submit\n >\n {label('submit')}\n </button>\n </div>\n </Show>\n\n {/* ── Step 3 : acknowledgement ─────────────────────── */}\n <Show when={step() === 'done'}>\n <span class=\"text-deposium-slate-500\" data-presentation-feedback-ack>\n {label('ack')}\n </span>\n </Show>\n </div>\n )\n}\n"],"names":["PROBLEM_ORDER","DEFAULT_PRESENTATION_FEEDBACK_LABELS","prompt","readable","notReadable","problemsPrompt","problemTooRaw","problemWrongColumns","problemWrongChart","problemMissingContext","problemWrongUnit","problemBadGrouping","problemMissingDatasetContext","preferLayoutPrompt","commentPlaceholder","submit","ack","PROBLEM_LABEL_KEY","too_raw","wrong_columns","wrong_chart","missing_context","wrong_unit","bad_grouping","missing_dataset_context","PresentationFeedback","props","step","setStep","createSignal","problems","setProblems","Set","preferred","setPreferred","undefined","comment","setComment","label","key","labels","emit","feedback","result","onSubmit","catch","base","connectorId","toolName","queryHash","renderKind","layoutType","submitReadable","verdict","submitNotReadable","picked","text","trim","length","preferredLayout","toggleProblem","p","prev","next","has","delete","add","_el$","_$getNextElement","_tmpl$5","_el$16","firstChild","_el$17","_co$3","_$getNextMarker","nextSibling","_el$18","_el$19","_co$4","_el$20","_el$21","_co$5","_$insert","_$createComponent","Show","when","children","_el$2","_tmpl$","_el$3","_el$4","_el$5","$$click","_$runHydrationEvents","_el$6","_tmpl$3","_el$7","_el$8","_el$13","_el$14","_co$2","_el$11","_el$12","For","each","_el$22","_tmpl$6","_$setAttribute","_$effect","_p$","_v$3","_v$4","e","t","_$className","preferredLayoutOptions","_el$9","_tmpl$2","_el$0","_el$1","_el$10","_co$","opt","_el$23","cur","_v$5","_v$6","$$input","currentTarget","value","_$setProperty","_el$15","_tmpl$4","_v$","class","_v$2","_$delegateEvents"],"mappings":"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"}