@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.
- package/CHANGELOG.md +161 -0
- package/README.md +37 -0
- package/dist/adapters/connector.cjs +112 -0
- package/dist/adapters/connector.cjs.map +1 -0
- package/dist/adapters/connector.d.ts +71 -0
- package/dist/adapters/connector.d.ts.map +1 -0
- package/dist/adapters/connector.js +112 -0
- package/dist/adapters/connector.js.map +1 -0
- package/dist/adapters/index.d.ts +18 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters.cjs +6 -0
- package/dist/adapters.cjs.map +1 -0
- package/dist/adapters.d.cts +18 -0
- package/dist/adapters.d.ts +18 -0
- package/dist/adapters.js +6 -0
- package/dist/adapters.js.map +1 -0
- package/dist/components/ActionGroupRenderer.cjs +12 -3
- package/dist/components/ActionGroupRenderer.cjs.map +1 -1
- package/dist/components/ActionGroupRenderer.d.ts.map +1 -1
- package/dist/components/ActionGroupRenderer.js +12 -3
- package/dist/components/ActionGroupRenderer.js.map +1 -1
- package/dist/components/ExpandableWrapper.cjs +24 -6
- package/dist/components/ExpandableWrapper.cjs.map +1 -1
- package/dist/components/ExpandableWrapper.d.ts.map +1 -1
- package/dist/components/ExpandableWrapper.js +24 -6
- package/dist/components/ExpandableWrapper.js.map +1 -1
- package/dist/components/FeedbackInline.cjs +6 -2
- package/dist/components/FeedbackInline.cjs.map +1 -1
- package/dist/components/FeedbackInline.d.ts +2 -2
- package/dist/components/FeedbackInline.d.ts.map +1 -1
- package/dist/components/FeedbackInline.js +7 -3
- package/dist/components/FeedbackInline.js.map +1 -1
- package/dist/components/PresentationFeedback.cjs +207 -0
- package/dist/components/PresentationFeedback.cjs.map +1 -0
- package/dist/components/PresentationFeedback.d.ts +113 -0
- package/dist/components/PresentationFeedback.d.ts.map +1 -0
- package/dist/components/PresentationFeedback.js +207 -0
- package/dist/components/PresentationFeedback.js.map +1 -0
- package/dist/components/StreamingUIRenderer.cjs +82 -195
- package/dist/components/StreamingUIRenderer.cjs.map +1 -1
- package/dist/components/StreamingUIRenderer.d.ts +25 -5
- package/dist/components/StreamingUIRenderer.d.ts.map +1 -1
- package/dist/components/StreamingUIRenderer.js +84 -197
- package/dist/components/StreamingUIRenderer.js.map +1 -1
- package/dist/components/UIResourceRenderer.cjs +22 -15
- package/dist/components/UIResourceRenderer.cjs.map +1 -1
- package/dist/components/UIResourceRenderer.d.ts.map +1 -1
- package/dist/components/UIResourceRenderer.js +22 -15
- package/dist/components/UIResourceRenderer.js.map +1 -1
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components.cjs +3 -0
- package/dist/components.cjs.map +1 -1
- package/dist/components.d.cts +2 -0
- package/dist/components.d.ts +2 -0
- package/dist/components.js +3 -0
- package/dist/components.js.map +1 -1
- package/dist/context/MCPActionContext.cjs +4 -1
- package/dist/context/MCPActionContext.cjs.map +1 -1
- package/dist/context/MCPActionContext.d.ts +13 -1
- package/dist/context/MCPActionContext.d.ts.map +1 -1
- package/dist/context/MCPActionContext.js +4 -1
- package/dist/context/MCPActionContext.js.map +1 -1
- package/dist/context/MCPUIStringsContext.cjs +38 -0
- package/dist/context/MCPUIStringsContext.cjs.map +1 -0
- package/dist/context/MCPUIStringsContext.d.ts +95 -0
- package/dist/context/MCPUIStringsContext.d.ts.map +1 -0
- package/dist/context/MCPUIStringsContext.js +38 -0
- package/dist/context/MCPUIStringsContext.js.map +1 -0
- package/dist/index.cjs +8 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-ui-spec/dist/schemas.cjs +103 -0
- package/dist/mcp-ui-spec/dist/schemas.cjs.map +1 -1
- package/dist/mcp-ui-spec/dist/schemas.js +103 -0
- package/dist/mcp-ui-spec/dist/schemas.js.map +1 -1
- package/docs/briefs/ROADMAP-opendata-macro-mcpui.md +912 -0
- package/package.json +17 -5
- package/src/adapters/connector.test.ts +165 -0
- package/src/adapters/connector.ts +234 -0
- package/src/adapters/index.ts +24 -0
- package/src/components/ActionGroupRenderer.test.tsx +1 -0
- package/src/components/ActionGroupRenderer.tsx +19 -4
- package/src/components/ActionSubmit.test.tsx +188 -0
- package/src/components/ExpandableWrapper.test.tsx +5 -2
- package/src/components/ExpandableWrapper.tsx +8 -6
- package/src/components/FeedbackInline.test.tsx +6 -3
- package/src/components/FeedbackInline.tsx +8 -6
- package/src/components/PresentationFeedback.test.tsx +163 -0
- package/src/components/PresentationFeedback.tsx +326 -0
- package/src/components/StreamingUIRenderer.parity.test.tsx +158 -0
- package/src/components/StreamingUIRenderer.tsx +42 -166
- package/src/components/UIResourceRenderer.tsx +19 -6
- package/src/components/index.ts +10 -0
- package/src/context/MCPActionContext.tsx +17 -1
- package/src/context/MCPUIStringsContext.test.tsx +116 -0
- package/src/context/MCPUIStringsContext.tsx +128 -0
- package/src/index.ts +27 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/vite.config.ts +1 -0
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const web = require("solid-js/web");
|
|
4
4
|
const solidJs = require("solid-js");
|
|
5
|
-
|
|
5
|
+
const MCPUIStringsContext = require("../context/MCPUIStringsContext.cjs");
|
|
6
|
+
var _tmpl$ = /* @__PURE__ */ web.template(`<button type=button class="p-1 rounded hover:bg-green-500/10 text-deposium-slate-500 hover:text-green-500 transition-colors"aria-label="Mark response as useful"data-feedback-inline-rating=positive><svg class="w-3.5 h-3.5"fill=none stroke=currentColor viewBox="0 0 24 24"><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M14 9V5a3 3 0 00-3-3l-4 9v11h11.28a2 2 0 002-1.7l1.38-9a2 2 0 00-2-2.3H14z M3 15v7">`), _tmpl$2 = /* @__PURE__ */ web.template(`<button type=button class="p-1 rounded hover:bg-red-500/10 text-deposium-slate-500 hover:text-red-500 transition-colors"aria-label="Mark response as not useful"data-feedback-inline-rating=negative><svg class="w-3.5 h-3.5"fill=none stroke=currentColor viewBox="0 0 24 24"><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M10 15v4a3 3 0 003 3l4-9V2H5.72a2 2 0 00-2 1.7l-1.38 9a2 2 0 002 2.3H10z M21 4v7">`), _tmpl$3 = /* @__PURE__ */ web.template(`<div>`), _tmpl$4 = /* @__PURE__ */ web.template(`<span class="text-[11px] text-deposium-slate-500">`);
|
|
6
7
|
const FeedbackInline = (props) => {
|
|
7
8
|
const [rating, setRating] = solidJs.createSignal(null);
|
|
9
|
+
const strings = MCPUIStringsContext.useMCPUIStrings();
|
|
8
10
|
const handle = (value) => {
|
|
9
11
|
if (rating() !== null) return;
|
|
10
12
|
setRating(value);
|
|
@@ -29,7 +31,7 @@ const FeedbackInline = (props) => {
|
|
|
29
31
|
var _el$4 = web.getNextElement(_tmpl$4);
|
|
30
32
|
web.insert(_el$4, (() => {
|
|
31
33
|
var _c$ = web.memo(() => rating() === "positive");
|
|
32
|
-
return () => _c$() ? props.positiveAck ??
|
|
34
|
+
return () => _c$() ? props.positiveAck ?? strings.feedbackPositiveAck : props.negativeAck ?? strings.feedbackNegativeAck;
|
|
33
35
|
})());
|
|
34
36
|
return _el$4;
|
|
35
37
|
})();
|
|
@@ -38,11 +40,13 @@ const FeedbackInline = (props) => {
|
|
|
38
40
|
return [(() => {
|
|
39
41
|
var _el$2 = web.getNextElement(_tmpl$);
|
|
40
42
|
_el$2.$$click = () => handle("positive");
|
|
43
|
+
web.effect(() => web.setAttribute(_el$2, "title", strings.feedbackUseful));
|
|
41
44
|
web.runHydrationEvents();
|
|
42
45
|
return _el$2;
|
|
43
46
|
})(), (() => {
|
|
44
47
|
var _el$3 = web.getNextElement(_tmpl$2);
|
|
45
48
|
_el$3.$$click = () => handle("negative");
|
|
49
|
+
web.effect(() => web.setAttribute(_el$3, "title", strings.feedbackNotUseful));
|
|
46
50
|
web.runHydrationEvents();
|
|
47
51
|
return _el$3;
|
|
48
52
|
})()];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FeedbackInline.cjs","sources":["../../src/components/FeedbackInline.tsx"],"sourcesContent":["/**\n * FeedbackInline — per-message inline feedback (thumbs up/down)\n *\n * @experimental\n * @since v5.2.0\n *\n * A small, non-blocking per-message feedback primitive. Sits next to an\n * assistant message, captures a rating, calls back to the consumer for\n * persistence. Best-effort by design — no retry UX, no revision UX.\n *\n * ## When to use vs other feedback primitives\n *\n * - **`FeedbackInline`** (this) → per-message thumb-up/down, non-blocking,\n * many can coexist.\n * - **`ChatPrompt` (type=choice)** → modal, one-at-a-time above the input,\n * used when the agent needs a blocking answer.\n * - **`ScratchpadPanel` feedback section** → structured feedback bound to a\n * scratchpad turn, panel-side.\n *\n * ## Persistence is the consumer's job\n *\n * The component flips to \"submitted\" state *optimistically* on click and\n * calls `onSubmit(rating, context)`. Network failures do not revert the UI —\n * feedback is best-effort. If you need stricter semantics (offline retry,\n * revision, ...) wrap this in your own component.\n *\n * @example\n * ```tsx\n * <FeedbackInline\n * messageHash={msg.hash}\n * context={{ intent: msg.intent, confidenceBand: msg.band }}\n * onSubmit={(rating, ctx) =>\n * fetch('/api/feedback', {\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify({ message_hash: msg.hash, rating, ...ctx }),\n * })\n * }\n * />\n * ```\n */\n\nimport { Component, Show, createSignal } from 'solid-js'\n\nexport interface FeedbackInlineContext {\n intent?: string\n confidenceBand?: string\n tags?: string[]\n [key: string]: unknown\n}\n\nexport interface FeedbackInlineProps {\n /** Stable identifier for the message being rated. */\n messageHash?: string\n /**\n * Called on click. Consumer is responsible for persistence (HTTP, store,\n * localStorage). Return value ignored.\n */\n onSubmit: (rating: 'positive' | 'negative', context?: FeedbackInlineContext) => void | Promise<void>\n /** Extra context forwarded to `onSubmit`. */\n context?: FeedbackInlineContext\n /** Ack text shown after positive rating.
|
|
1
|
+
{"version":3,"file":"FeedbackInline.cjs","sources":["../../src/components/FeedbackInline.tsx"],"sourcesContent":["/**\n * FeedbackInline — per-message inline feedback (thumbs up/down)\n *\n * @experimental\n * @since v5.2.0\n *\n * A small, non-blocking per-message feedback primitive. Sits next to an\n * assistant message, captures a rating, calls back to the consumer for\n * persistence. Best-effort by design — no retry UX, no revision UX.\n *\n * ## When to use vs other feedback primitives\n *\n * - **`FeedbackInline`** (this) → per-message thumb-up/down, non-blocking,\n * many can coexist.\n * - **`ChatPrompt` (type=choice)** → modal, one-at-a-time above the input,\n * used when the agent needs a blocking answer.\n * - **`ScratchpadPanel` feedback section** → structured feedback bound to a\n * scratchpad turn, panel-side.\n *\n * ## Persistence is the consumer's job\n *\n * The component flips to \"submitted\" state *optimistically* on click and\n * calls `onSubmit(rating, context)`. Network failures do not revert the UI —\n * feedback is best-effort. If you need stricter semantics (offline retry,\n * revision, ...) wrap this in your own component.\n *\n * @example\n * ```tsx\n * <FeedbackInline\n * messageHash={msg.hash}\n * context={{ intent: msg.intent, confidenceBand: msg.band }}\n * onSubmit={(rating, ctx) =>\n * fetch('/api/feedback', {\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify({ message_hash: msg.hash, rating, ...ctx }),\n * })\n * }\n * />\n * ```\n */\n\nimport { Component, Show, createSignal } from 'solid-js'\nimport { useMCPUIStrings } from '../context/MCPUIStringsContext'\n\nexport interface FeedbackInlineContext {\n intent?: string\n confidenceBand?: string\n tags?: string[]\n [key: string]: unknown\n}\n\nexport interface FeedbackInlineProps {\n /** Stable identifier for the message being rated. */\n messageHash?: string\n /**\n * Called on click. Consumer is responsible for persistence (HTTP, store,\n * localStorage). Return value ignored.\n */\n onSubmit: (rating: 'positive' | 'negative', context?: FeedbackInlineContext) => void | Promise<void>\n /** Extra context forwarded to `onSubmit`. */\n context?: FeedbackInlineContext\n /** Ack text shown after positive rating. Defaults to `MCPUIStrings.feedbackPositiveAck` ('Thanks!' in EN). */\n positiveAck?: string\n /** Ack text shown after negative rating. Defaults to `MCPUIStrings.feedbackNegativeAck`. */\n negativeAck?: string\n /** Extra Tailwind classes on the container. */\n class?: string\n}\n\n/**\n * @experimental\n * Per-message inline feedback (thumbs up/down). Non-blocking.\n */\nexport const FeedbackInline: Component<FeedbackInlineProps> = (props) => {\n const [rating, setRating] = createSignal<'positive' | 'negative' | null>(null)\n const strings = useMCPUIStrings()\n\n const handle = (value: 'positive' | 'negative') => {\n if (rating() !== null) return // already submitted, final state\n setRating(value)\n try {\n // Fire-and-forget. If the consumer returns a Promise that rejects,\n // swallow it — feedback is best-effort by design.\n const result = props.onSubmit(value, props.context)\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 }\n\n return (\n <div class={`flex items-center gap-1 ${props.class ?? ''}`.trim()}>\n <Show\n when={rating() === null}\n fallback={\n <span class=\"text-[11px] text-deposium-slate-500\">\n {rating() === 'positive'\n ? (props.positiveAck ?? strings.feedbackPositiveAck)\n : (props.negativeAck ?? strings.feedbackNegativeAck)}\n </span>\n }\n >\n <button\n type=\"button\"\n onClick={() => handle('positive')}\n class=\"p-1 rounded hover:bg-green-500/10 text-deposium-slate-500 hover:text-green-500 transition-colors\"\n title={strings.feedbackUseful}\n aria-label=\"Mark response as useful\"\n data-feedback-inline-rating=\"positive\"\n >\n <svg class=\"w-3.5 h-3.5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M14 9V5a3 3 0 00-3-3l-4 9v11h11.28a2 2 0 002-1.7l1.38-9a2 2 0 00-2-2.3H14z M3 15v7\"\n />\n </svg>\n </button>\n <button\n type=\"button\"\n onClick={() => handle('negative')}\n class=\"p-1 rounded hover:bg-red-500/10 text-deposium-slate-500 hover:text-red-500 transition-colors\"\n title={strings.feedbackNotUseful}\n aria-label=\"Mark response as not useful\"\n data-feedback-inline-rating=\"negative\"\n >\n <svg class=\"w-3.5 h-3.5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M10 15v4a3 3 0 003 3l4-9V2H5.72a2 2 0 00-2 1.7l-1.38 9a2 2 0 002 2.3H10z M21 4v7\"\n />\n </svg>\n </button>\n </Show>\n </div>\n )\n}\n"],"names":["FeedbackInline","props","rating","setRating","createSignal","strings","useMCPUIStrings","handle","value","result","onSubmit","context","catch","_el$","_$getNextElement","_tmpl$3","_$insert","_$createComponent","Show","when","fallback","_el$4","_tmpl$4","_c$","_$memo","positiveAck","feedbackPositiveAck","negativeAck","feedbackNegativeAck","children","_el$2","_tmpl$","$$click","_$effect","_$setAttribute","feedbackUseful","_$runHydrationEvents","_el$3","_tmpl$2","feedbackNotUseful","_$className","class","trim","_$delegateEvents"],"mappings":";;;;;;AA0EO,MAAMA,iBAAkDC,CAAAA,UAAU;AACvE,QAAM,CAACC,QAAQC,SAAS,IAAIC,QAAAA,aAA6C,IAAI;AAC7E,QAAMC,UAAUC,oBAAAA,gBAAAA;AAEhB,QAAMC,SAASA,CAACC,UAAmC;AACjD,QAAIN,OAAAA,MAAa,KAAM;AACvBC,cAAUK,KAAK;AACf,QAAI;AAGF,YAAMC,SAASR,MAAMS,SAASF,OAAOP,MAAMU,OAAO;AAClD,UAAIF,UAAU,OAAQA,OAAyBG,UAAU,YAAY;AACnE;AAAEH,eAAyBG,MAAM,MAAM;AAAA,QACrC,CACD;AAAA,MACH;AAAA,IACF,QAAQ;AAAA,IACN;AAAA,EAEJ;AAEA,UAAA,MAAA;AAAA,QAAAC,OAAAC,IAAAA,eAAAC,OAAA;AAAAC,eAAAH,MAAAI,IAAAA,gBAEKC,cAAI;AAAA,MAAA,IACHC,OAAI;AAAA,eAAEjB,aAAa;AAAA,MAAI;AAAA,MAAA,IACvBkB,WAAQ;AAAA,gBAAA,MAAA;AAAA,cAAAC,QAAAP,IAAAA,eAAAQ,OAAA;AAAAN,cAAAA,OAAAK,QAAA,MAAA;AAAA,gBAAAE,MAAAC,IAAAA,KAAA,MAEHtB,OAAAA,MAAa,UAAU;AAAA,mBAAA,MAAvBqB,QACItB,MAAMwB,eAAepB,QAAQqB,sBAC7BzB,MAAM0B,eAAetB,QAAQuB;AAAAA,UAAoB,IAAA;AAAA,iBAAAP;AAAAA,QAAA,GAAA;AAAA,MAAA;AAAA,MAAA,IAAAQ,WAAA;AAAA,eAAA,EAAA,MAAA;AAAA,cAAAC,QAAAhB,IAAAA,eAAAiB,MAAA;AAAAD,gBAAAE,UAM/C,MAAMzB,OAAO,UAAU;AAAC0B,cAAAA,aAAAC,IAAAA,aAAAJ,OAAA,SAE1BzB,QAAQ8B,cAAc,CAAA;AAAAC,iCAAAA;AAAA,iBAAAN;AAAAA,QAAA,GAAA,IAAA,MAAA;AAAA,cAAAO,QAAAvB,IAAAA,eAAAwB,OAAA;AAAAD,gBAAAL,UAepB,MAAMzB,OAAO,UAAU;AAAC0B,cAAAA,aAAAC,IAAAA,aAAAG,OAAA,SAE1BhC,QAAQkC,iBAAiB,CAAA;AAAAH,iCAAAA;AAAA,iBAAAC;AAAAA,QAAA,IAAA;AAAA,MAAA;AAAA,IAAA,CAAA,CAAA;AAAAJ,QAAAA,OAAA,MAAAO,IAAAA,UAAA3B,MAhC1B,2BAA2BZ,MAAMwC,SAAS,EAAE,GAAGC,KAAAA,CAAM,CAAA;AAAA,WAAA7B;AAAAA,EAAA,GAAA;AAgDrE;AAAC8B,IAAAA,eAAA,CAAA,OAAA,CAAA;;"}
|
|
@@ -56,9 +56,9 @@ export interface FeedbackInlineProps {
|
|
|
56
56
|
onSubmit: (rating: 'positive' | 'negative', context?: FeedbackInlineContext) => void | Promise<void>;
|
|
57
57
|
/** Extra context forwarded to `onSubmit`. */
|
|
58
58
|
context?: FeedbackInlineContext;
|
|
59
|
-
/** Ack text shown after positive rating.
|
|
59
|
+
/** Ack text shown after positive rating. Defaults to `MCPUIStrings.feedbackPositiveAck` ('Thanks!' in EN). */
|
|
60
60
|
positiveAck?: string;
|
|
61
|
-
/** Ack text shown after negative rating.
|
|
61
|
+
/** Ack text shown after negative rating. Defaults to `MCPUIStrings.feedbackNegativeAck`. */
|
|
62
62
|
negativeAck?: string;
|
|
63
63
|
/** Extra Tailwind classes on the container. */
|
|
64
64
|
class?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FeedbackInline.d.ts","sourceRoot":"","sources":["../../src/components/FeedbackInline.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,EAAE,SAAS,EAAsB,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"FeedbackInline.d.ts","sourceRoot":"","sources":["../../src/components/FeedbackInline.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,EAAE,SAAS,EAAsB,MAAM,UAAU,CAAA;AAGxD,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;OAGG;IACH,QAAQ,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,EAAE,OAAO,CAAC,EAAE,qBAAqB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACpG,6CAA6C;IAC7C,OAAO,CAAC,EAAE,qBAAqB,CAAA;IAC/B,8GAA8G;IAC9G,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,4FAA4F;IAC5F,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,SAAS,CAAC,mBAAmB,CAsEzD,CAAA"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { delegateEvents, getNextElement, template, insert, createComponent, runHydrationEvents, memo,
|
|
1
|
+
import { delegateEvents, getNextElement, template, insert, createComponent, effect, setAttribute, runHydrationEvents, memo, className } from "solid-js/web";
|
|
2
2
|
import { createSignal, Show } from "solid-js";
|
|
3
|
-
|
|
3
|
+
import { useMCPUIStrings } from "../context/MCPUIStringsContext.js";
|
|
4
|
+
var _tmpl$ = /* @__PURE__ */ template(`<button type=button class="p-1 rounded hover:bg-green-500/10 text-deposium-slate-500 hover:text-green-500 transition-colors"aria-label="Mark response as useful"data-feedback-inline-rating=positive><svg class="w-3.5 h-3.5"fill=none stroke=currentColor viewBox="0 0 24 24"><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M14 9V5a3 3 0 00-3-3l-4 9v11h11.28a2 2 0 002-1.7l1.38-9a2 2 0 00-2-2.3H14z M3 15v7">`), _tmpl$2 = /* @__PURE__ */ template(`<button type=button class="p-1 rounded hover:bg-red-500/10 text-deposium-slate-500 hover:text-red-500 transition-colors"aria-label="Mark response as not useful"data-feedback-inline-rating=negative><svg class="w-3.5 h-3.5"fill=none stroke=currentColor viewBox="0 0 24 24"><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M10 15v4a3 3 0 003 3l4-9V2H5.72a2 2 0 00-2 1.7l-1.38 9a2 2 0 002 2.3H10z M21 4v7">`), _tmpl$3 = /* @__PURE__ */ template(`<div>`), _tmpl$4 = /* @__PURE__ */ template(`<span class="text-[11px] text-deposium-slate-500">`);
|
|
4
5
|
const FeedbackInline = (props) => {
|
|
5
6
|
const [rating, setRating] = createSignal(null);
|
|
7
|
+
const strings = useMCPUIStrings();
|
|
6
8
|
const handle = (value) => {
|
|
7
9
|
if (rating() !== null) return;
|
|
8
10
|
setRating(value);
|
|
@@ -27,7 +29,7 @@ const FeedbackInline = (props) => {
|
|
|
27
29
|
var _el$4 = getNextElement(_tmpl$4);
|
|
28
30
|
insert(_el$4, (() => {
|
|
29
31
|
var _c$ = memo(() => rating() === "positive");
|
|
30
|
-
return () => _c$() ? props.positiveAck ??
|
|
32
|
+
return () => _c$() ? props.positiveAck ?? strings.feedbackPositiveAck : props.negativeAck ?? strings.feedbackNegativeAck;
|
|
31
33
|
})());
|
|
32
34
|
return _el$4;
|
|
33
35
|
})();
|
|
@@ -36,11 +38,13 @@ const FeedbackInline = (props) => {
|
|
|
36
38
|
return [(() => {
|
|
37
39
|
var _el$2 = getNextElement(_tmpl$);
|
|
38
40
|
_el$2.$$click = () => handle("positive");
|
|
41
|
+
effect(() => setAttribute(_el$2, "title", strings.feedbackUseful));
|
|
39
42
|
runHydrationEvents();
|
|
40
43
|
return _el$2;
|
|
41
44
|
})(), (() => {
|
|
42
45
|
var _el$3 = getNextElement(_tmpl$2);
|
|
43
46
|
_el$3.$$click = () => handle("negative");
|
|
47
|
+
effect(() => setAttribute(_el$3, "title", strings.feedbackNotUseful));
|
|
44
48
|
runHydrationEvents();
|
|
45
49
|
return _el$3;
|
|
46
50
|
})()];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FeedbackInline.js","sources":["../../src/components/FeedbackInline.tsx"],"sourcesContent":["/**\n * FeedbackInline — per-message inline feedback (thumbs up/down)\n *\n * @experimental\n * @since v5.2.0\n *\n * A small, non-blocking per-message feedback primitive. Sits next to an\n * assistant message, captures a rating, calls back to the consumer for\n * persistence. Best-effort by design — no retry UX, no revision UX.\n *\n * ## When to use vs other feedback primitives\n *\n * - **`FeedbackInline`** (this) → per-message thumb-up/down, non-blocking,\n * many can coexist.\n * - **`ChatPrompt` (type=choice)** → modal, one-at-a-time above the input,\n * used when the agent needs a blocking answer.\n * - **`ScratchpadPanel` feedback section** → structured feedback bound to a\n * scratchpad turn, panel-side.\n *\n * ## Persistence is the consumer's job\n *\n * The component flips to \"submitted\" state *optimistically* on click and\n * calls `onSubmit(rating, context)`. Network failures do not revert the UI —\n * feedback is best-effort. If you need stricter semantics (offline retry,\n * revision, ...) wrap this in your own component.\n *\n * @example\n * ```tsx\n * <FeedbackInline\n * messageHash={msg.hash}\n * context={{ intent: msg.intent, confidenceBand: msg.band }}\n * onSubmit={(rating, ctx) =>\n * fetch('/api/feedback', {\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify({ message_hash: msg.hash, rating, ...ctx }),\n * })\n * }\n * />\n * ```\n */\n\nimport { Component, Show, createSignal } from 'solid-js'\n\nexport interface FeedbackInlineContext {\n intent?: string\n confidenceBand?: string\n tags?: string[]\n [key: string]: unknown\n}\n\nexport interface FeedbackInlineProps {\n /** Stable identifier for the message being rated. */\n messageHash?: string\n /**\n * Called on click. Consumer is responsible for persistence (HTTP, store,\n * localStorage). Return value ignored.\n */\n onSubmit: (rating: 'positive' | 'negative', context?: FeedbackInlineContext) => void | Promise<void>\n /** Extra context forwarded to `onSubmit`. */\n context?: FeedbackInlineContext\n /** Ack text shown after positive rating.
|
|
1
|
+
{"version":3,"file":"FeedbackInline.js","sources":["../../src/components/FeedbackInline.tsx"],"sourcesContent":["/**\n * FeedbackInline — per-message inline feedback (thumbs up/down)\n *\n * @experimental\n * @since v5.2.0\n *\n * A small, non-blocking per-message feedback primitive. Sits next to an\n * assistant message, captures a rating, calls back to the consumer for\n * persistence. Best-effort by design — no retry UX, no revision UX.\n *\n * ## When to use vs other feedback primitives\n *\n * - **`FeedbackInline`** (this) → per-message thumb-up/down, non-blocking,\n * many can coexist.\n * - **`ChatPrompt` (type=choice)** → modal, one-at-a-time above the input,\n * used when the agent needs a blocking answer.\n * - **`ScratchpadPanel` feedback section** → structured feedback bound to a\n * scratchpad turn, panel-side.\n *\n * ## Persistence is the consumer's job\n *\n * The component flips to \"submitted\" state *optimistically* on click and\n * calls `onSubmit(rating, context)`. Network failures do not revert the UI —\n * feedback is best-effort. If you need stricter semantics (offline retry,\n * revision, ...) wrap this in your own component.\n *\n * @example\n * ```tsx\n * <FeedbackInline\n * messageHash={msg.hash}\n * context={{ intent: msg.intent, confidenceBand: msg.band }}\n * onSubmit={(rating, ctx) =>\n * fetch('/api/feedback', {\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify({ message_hash: msg.hash, rating, ...ctx }),\n * })\n * }\n * />\n * ```\n */\n\nimport { Component, Show, createSignal } from 'solid-js'\nimport { useMCPUIStrings } from '../context/MCPUIStringsContext'\n\nexport interface FeedbackInlineContext {\n intent?: string\n confidenceBand?: string\n tags?: string[]\n [key: string]: unknown\n}\n\nexport interface FeedbackInlineProps {\n /** Stable identifier for the message being rated. */\n messageHash?: string\n /**\n * Called on click. Consumer is responsible for persistence (HTTP, store,\n * localStorage). Return value ignored.\n */\n onSubmit: (rating: 'positive' | 'negative', context?: FeedbackInlineContext) => void | Promise<void>\n /** Extra context forwarded to `onSubmit`. */\n context?: FeedbackInlineContext\n /** Ack text shown after positive rating. Defaults to `MCPUIStrings.feedbackPositiveAck` ('Thanks!' in EN). */\n positiveAck?: string\n /** Ack text shown after negative rating. Defaults to `MCPUIStrings.feedbackNegativeAck`. */\n negativeAck?: string\n /** Extra Tailwind classes on the container. */\n class?: string\n}\n\n/**\n * @experimental\n * Per-message inline feedback (thumbs up/down). Non-blocking.\n */\nexport const FeedbackInline: Component<FeedbackInlineProps> = (props) => {\n const [rating, setRating] = createSignal<'positive' | 'negative' | null>(null)\n const strings = useMCPUIStrings()\n\n const handle = (value: 'positive' | 'negative') => {\n if (rating() !== null) return // already submitted, final state\n setRating(value)\n try {\n // Fire-and-forget. If the consumer returns a Promise that rejects,\n // swallow it — feedback is best-effort by design.\n const result = props.onSubmit(value, props.context)\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 }\n\n return (\n <div class={`flex items-center gap-1 ${props.class ?? ''}`.trim()}>\n <Show\n when={rating() === null}\n fallback={\n <span class=\"text-[11px] text-deposium-slate-500\">\n {rating() === 'positive'\n ? (props.positiveAck ?? strings.feedbackPositiveAck)\n : (props.negativeAck ?? strings.feedbackNegativeAck)}\n </span>\n }\n >\n <button\n type=\"button\"\n onClick={() => handle('positive')}\n class=\"p-1 rounded hover:bg-green-500/10 text-deposium-slate-500 hover:text-green-500 transition-colors\"\n title={strings.feedbackUseful}\n aria-label=\"Mark response as useful\"\n data-feedback-inline-rating=\"positive\"\n >\n <svg class=\"w-3.5 h-3.5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M14 9V5a3 3 0 00-3-3l-4 9v11h11.28a2 2 0 002-1.7l1.38-9a2 2 0 00-2-2.3H14z M3 15v7\"\n />\n </svg>\n </button>\n <button\n type=\"button\"\n onClick={() => handle('negative')}\n class=\"p-1 rounded hover:bg-red-500/10 text-deposium-slate-500 hover:text-red-500 transition-colors\"\n title={strings.feedbackNotUseful}\n aria-label=\"Mark response as not useful\"\n data-feedback-inline-rating=\"negative\"\n >\n <svg class=\"w-3.5 h-3.5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M10 15v4a3 3 0 003 3l4-9V2H5.72a2 2 0 00-2 1.7l-1.38 9a2 2 0 002 2.3H10z M21 4v7\"\n />\n </svg>\n </button>\n </Show>\n </div>\n )\n}\n"],"names":["FeedbackInline","props","rating","setRating","createSignal","strings","useMCPUIStrings","handle","value","result","onSubmit","context","catch","_el$","_$getNextElement","_tmpl$3","_$insert","_$createComponent","Show","when","fallback","_el$4","_tmpl$4","_c$","_$memo","positiveAck","feedbackPositiveAck","negativeAck","feedbackNegativeAck","children","_el$2","_tmpl$","$$click","_$effect","_$setAttribute","feedbackUseful","_$runHydrationEvents","_el$3","_tmpl$2","feedbackNotUseful","_$className","class","trim","_$delegateEvents"],"mappings":";;;;AA0EO,MAAMA,iBAAkDC,CAAAA,UAAU;AACvE,QAAM,CAACC,QAAQC,SAAS,IAAIC,aAA6C,IAAI;AAC7E,QAAMC,UAAUC,gBAAAA;AAEhB,QAAMC,SAASA,CAACC,UAAmC;AACjD,QAAIN,OAAAA,MAAa,KAAM;AACvBC,cAAUK,KAAK;AACf,QAAI;AAGF,YAAMC,SAASR,MAAMS,SAASF,OAAOP,MAAMU,OAAO;AAClD,UAAIF,UAAU,OAAQA,OAAyBG,UAAU,YAAY;AACnE;AAAEH,eAAyBG,MAAM,MAAM;AAAA,QACrC,CACD;AAAA,MACH;AAAA,IACF,QAAQ;AAAA,IACN;AAAA,EAEJ;AAEA,UAAA,MAAA;AAAA,QAAAC,OAAAC,eAAAC,OAAA;AAAAC,WAAAH,MAAAI,gBAEKC,MAAI;AAAA,MAAA,IACHC,OAAI;AAAA,eAAEjB,aAAa;AAAA,MAAI;AAAA,MAAA,IACvBkB,WAAQ;AAAA,gBAAA,MAAA;AAAA,cAAAC,QAAAP,eAAAQ,OAAA;AAAAN,iBAAAK,QAAA,MAAA;AAAA,gBAAAE,MAAAC,KAAA,MAEHtB,OAAAA,MAAa,UAAU;AAAA,mBAAA,MAAvBqB,QACItB,MAAMwB,eAAepB,QAAQqB,sBAC7BzB,MAAM0B,eAAetB,QAAQuB;AAAAA,UAAoB,IAAA;AAAA,iBAAAP;AAAAA,QAAA,GAAA;AAAA,MAAA;AAAA,MAAA,IAAAQ,WAAA;AAAA,eAAA,EAAA,MAAA;AAAA,cAAAC,QAAAhB,eAAAiB,MAAA;AAAAD,gBAAAE,UAM/C,MAAMzB,OAAO,UAAU;AAAC0B,uBAAAC,aAAAJ,OAAA,SAE1BzB,QAAQ8B,cAAc,CAAA;AAAAC,6BAAAA;AAAA,iBAAAN;AAAAA,QAAA,GAAA,IAAA,MAAA;AAAA,cAAAO,QAAAvB,eAAAwB,OAAA;AAAAD,gBAAAL,UAepB,MAAMzB,OAAO,UAAU;AAAC0B,uBAAAC,aAAAG,OAAA,SAE1BhC,QAAQkC,iBAAiB,CAAA;AAAAH,6BAAAA;AAAA,iBAAAC;AAAAA,QAAA,IAAA;AAAA,MAAA;AAAA,IAAA,CAAA,CAAA;AAAAJ,WAAA,MAAAO,UAAA3B,MAhC1B,2BAA2BZ,MAAMwC,SAAS,EAAE,GAAGC,KAAAA,CAAM,CAAA;AAAA,WAAA7B;AAAAA,EAAA,GAAA;AAgDrE;AAAC8B,eAAA,CAAA,OAAA,CAAA;"}
|
|
@@ -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"}
|