@seed-ship/mcp-ui-solid 2.5.2 → 2.5.3

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.
@@ -1,11 +1,11 @@
1
- import { delegateEvents, getNextElement, template, insert, createComponent, effect, setAttribute, runHydrationEvents, getNextMarker, className, addEventListener, setProperty } from "solid-js/web";
2
- import { Switch, Match, For, Show, createSignal } from "solid-js";
3
- var _tmpl$ = /* @__PURE__ */ template(`<div class="w-full max-w-2xl mx-auto mb-2 bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 shadow-lg overflow-hidden"role=dialog style="animation:chat-prompt-slide-up 0.2s ease-out"><div class="flex items-center justify-between px-4 py-2.5 border-b border-gray-100 dark:border-gray-700"><p class="text-sm font-medium text-gray-900 dark:text-white"></p><button class="p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"aria-label=Dismiss><svg class="w-4 h-4"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M6 18L18 6M6 6l12 12"></path></svg></button></div><div class="px-4 py-3"></div><style>
1
+ import { delegateEvents, getNextElement, template, insert, createComponent, effect, setAttribute, className, runHydrationEvents, getNextMarker, addEventListener, setProperty } from "solid-js/web";
2
+ import { Show, Switch, Match, For, createSignal } from "solid-js";
3
+ var _tmpl$ = /* @__PURE__ */ template(`<div class="w-full max-w-2xl mx-auto mb-2 bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 shadow-lg overflow-hidden"role=dialog style="animation:chat-prompt-slide-up 0.2s ease-out"><div class="flex items-center justify-between px-4 py-2.5 border-b border-gray-100 dark:border-gray-700"><p class="text-sm font-medium text-gray-900 dark:text-white"></p><button></button></div><div class="px-4 py-3"></div><style>
4
4
  @keyframes chat-prompt-slide-up {
5
5
  from { opacity: 0; transform: translateY(8px); }
6
6
  to { opacity: 1; transform: translateY(0); }
7
7
  }
8
- `), _tmpl$2 = /* @__PURE__ */ template(`<div>`), _tmpl$3 = /* @__PURE__ */ template(`<span class=mr-2>`), _tmpl$4 = /* @__PURE__ */ template(`<span class="block text-xs text-gray-500 dark:text-gray-400 mt-0.5 font-normal">`), _tmpl$5 = /* @__PURE__ */ template(`<button class="px-4 py-2 text-sm font-medium rounded-lg border border-gray-200 dark:border-gray-600 bg-gray-50 dark:bg-gray-700 text-gray-900 dark:text-white hover:bg-blue-50 hover:border-blue-300 dark:hover:bg-blue-900/30 dark:hover:border-blue-600 transition-colors text-left"><!$><!/><!$><!/><!$><!/>`), _tmpl$6 = /* @__PURE__ */ template(`<p class="text-sm text-gray-600 dark:text-gray-400 mb-3">`), _tmpl$7 = /* @__PURE__ */ template(`<div><!$><!/><div class="flex gap-2 justify-end"><button class="px-4 py-2 text-sm font-medium rounded-lg border border-gray-200 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"></button><button>`), _tmpl$8 = /* @__PURE__ */ template(`<form class="flex flex-col gap-3"><!$><!/><div class="flex justify-end"><button type=submit class="px-4 py-2 text-sm font-medium rounded-lg text-white bg-blue-600 hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors">`), _tmpl$9 = /* @__PURE__ */ template(`<span class="text-red-500 ml-0.5">*`), _tmpl$0 = /* @__PURE__ */ template(`<textarea rows=3 class="w-full px-3 py-2 text-sm rounded-lg border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:border-blue-400 focus:ring-1 focus:ring-blue-400 outline-none transition-colors">`), _tmpl$1 = /* @__PURE__ */ template(`<select class="w-full px-3 py-2 text-sm rounded-lg border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:border-blue-400 focus:ring-1 focus:ring-blue-400 outline-none transition-colors"><option value></option><!$><!/>`), _tmpl$10 = /* @__PURE__ */ template(`<input class="w-full px-3 py-2 text-sm rounded-lg border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:border-blue-400 focus:ring-1 focus:ring-blue-400 outline-none transition-colors">`), _tmpl$11 = /* @__PURE__ */ template(`<div><label class="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1"><!$><!/><!$><!/></label><!$><!/>`), _tmpl$12 = /* @__PURE__ */ template(`<option>`);
8
+ `), _tmpl$2 = /* @__PURE__ */ template(`<svg class="w-4 h-4"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M6 18L18 6M6 6l12 12">`), _tmpl$3 = /* @__PURE__ */ template(`<div>`), _tmpl$4 = /* @__PURE__ */ template(`<span class=mr-2>`), _tmpl$5 = /* @__PURE__ */ template(`<span class="block text-xs text-gray-500 dark:text-gray-400 mt-0.5 font-normal">`), _tmpl$6 = /* @__PURE__ */ template(`<button class="px-4 py-2 text-sm font-medium rounded-lg border border-gray-200 dark:border-gray-600 bg-gray-50 dark:bg-gray-700 text-gray-900 dark:text-white hover:bg-blue-50 hover:border-blue-300 dark:hover:bg-blue-900/30 dark:hover:border-blue-600 transition-colors text-left"><!$><!/><!$><!/><!$><!/>`), _tmpl$7 = /* @__PURE__ */ template(`<p class="text-sm text-gray-600 dark:text-gray-400 mb-3">`), _tmpl$8 = /* @__PURE__ */ template(`<div><!$><!/><div class="flex gap-2 justify-end"><button class="px-4 py-2 text-sm font-medium rounded-lg border border-gray-200 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"></button><button>`), _tmpl$9 = /* @__PURE__ */ template(`<form class="flex flex-col gap-3"><!$><!/><div class="flex justify-end"><button type=submit class="px-4 py-2 text-sm font-medium rounded-lg text-white bg-blue-600 hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors">`), _tmpl$0 = /* @__PURE__ */ template(`<span class="text-red-500 ml-0.5">*`), _tmpl$1 = /* @__PURE__ */ template(`<textarea rows=3 class="w-full px-3 py-2 text-sm rounded-lg border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:border-blue-400 focus:ring-1 focus:ring-blue-400 outline-none transition-colors">`), _tmpl$10 = /* @__PURE__ */ template(`<select class="w-full px-3 py-2 text-sm rounded-lg border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:border-blue-400 focus:ring-1 focus:ring-blue-400 outline-none transition-colors"><option value></option><!$><!/>`), _tmpl$11 = /* @__PURE__ */ template(`<input class="w-full px-3 py-2 text-sm rounded-lg border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:border-blue-400 focus:ring-1 focus:ring-blue-400 outline-none transition-colors">`), _tmpl$12 = /* @__PURE__ */ template(`<div><label class="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1"><!$><!/><!$><!/></label><!$><!/>`), _tmpl$13 = /* @__PURE__ */ template(`<option>`);
9
9
  const ChatPrompt = (props) => {
10
10
  if (!props.config) return null;
11
11
  return (() => {
@@ -21,6 +21,17 @@ const ChatPrompt = (props) => {
21
21
  dismissed: true
22
22
  });
23
23
  };
24
+ insert(_el$4, createComponent(Show, {
25
+ get when() {
26
+ return props.dismissLabel;
27
+ },
28
+ get fallback() {
29
+ return getNextElement(_tmpl$2);
30
+ },
31
+ get children() {
32
+ return props.dismissLabel;
33
+ }
34
+ }));
24
35
  insert(_el$5, createComponent(Switch, {
25
36
  get children() {
26
37
  return [createComponent(Match, {
@@ -84,7 +95,17 @@ const ChatPrompt = (props) => {
84
95
  })];
85
96
  }
86
97
  }));
87
- effect(() => setAttribute(_el$, "aria-label", props.config.title));
98
+ effect((_p$) => {
99
+ var _v$ = props.config.title, _v$2 = props.dismissLabel ? "px-3 py-1 text-xs font-medium text-blue-600 dark:text-blue-400 hover:bg-blue-50 dark:hover:bg-blue-900/20 rounded-lg transition-colors" : "p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors", _v$3 = props.dismissLabel || "Dismiss";
100
+ _v$ !== _p$.e && setAttribute(_el$, "aria-label", _p$.e = _v$);
101
+ _v$2 !== _p$.t && className(_el$4, _p$.t = _v$2);
102
+ _v$3 !== _p$.a && setAttribute(_el$4, "aria-label", _p$.a = _v$3);
103
+ return _p$;
104
+ }, {
105
+ e: void 0,
106
+ t: void 0,
107
+ a: void 0
108
+ });
88
109
  runHydrationEvents();
89
110
  return _el$;
90
111
  })();
@@ -101,64 +122,64 @@ const ChoiceBody = (props) => {
101
122
  }
102
123
  };
103
124
  return (() => {
104
- var _el$6 = getNextElement(_tmpl$2);
105
- insert(_el$6, createComponent(For, {
125
+ var _el$7 = getNextElement(_tmpl$3);
126
+ insert(_el$7, createComponent(For, {
106
127
  get each() {
107
128
  return props.config.options;
108
129
  },
109
130
  children: (option) => (() => {
110
- var _el$7 = getNextElement(_tmpl$5), _el$0 = _el$7.firstChild, [_el$1, _co$] = getNextMarker(_el$0.nextSibling), _el$10 = _el$1.nextSibling, [_el$11, _co$2] = getNextMarker(_el$10.nextSibling), _el$12 = _el$11.nextSibling, [_el$13, _co$3] = getNextMarker(_el$12.nextSibling);
111
- _el$7.$$click = () => props.onSelect(option.value, option.label);
112
- insert(_el$7, createComponent(Show, {
131
+ var _el$8 = getNextElement(_tmpl$6), _el$1 = _el$8.firstChild, [_el$10, _co$] = getNextMarker(_el$1.nextSibling), _el$11 = _el$10.nextSibling, [_el$12, _co$2] = getNextMarker(_el$11.nextSibling), _el$13 = _el$12.nextSibling, [_el$14, _co$3] = getNextMarker(_el$13.nextSibling);
132
+ _el$8.$$click = () => props.onSelect(option.value, option.label);
133
+ insert(_el$8, createComponent(Show, {
113
134
  get when() {
114
135
  return option.icon;
115
136
  },
116
137
  get children() {
117
- var _el$8 = getNextElement(_tmpl$3);
118
- insert(_el$8, () => option.icon);
119
- return _el$8;
138
+ var _el$9 = getNextElement(_tmpl$4);
139
+ insert(_el$9, () => option.icon);
140
+ return _el$9;
120
141
  }
121
- }), _el$1, _co$);
122
- insert(_el$7, () => option.label, _el$11, _co$2);
123
- insert(_el$7, createComponent(Show, {
142
+ }), _el$10, _co$);
143
+ insert(_el$8, () => option.label, _el$12, _co$2);
144
+ insert(_el$8, createComponent(Show, {
124
145
  get when() {
125
146
  return option.description;
126
147
  },
127
148
  get children() {
128
- var _el$9 = getNextElement(_tmpl$4);
129
- insert(_el$9, () => option.description);
130
- return _el$9;
149
+ var _el$0 = getNextElement(_tmpl$5);
150
+ insert(_el$0, () => option.description);
151
+ return _el$0;
131
152
  }
132
- }), _el$13, _co$3);
153
+ }), _el$14, _co$3);
133
154
  runHydrationEvents();
134
- return _el$7;
155
+ return _el$8;
135
156
  })()
136
157
  }));
137
- effect(() => className(_el$6, layoutClass()));
138
- return _el$6;
158
+ effect(() => className(_el$7, layoutClass()));
159
+ return _el$7;
139
160
  })();
140
161
  };
141
162
  const ConfirmBody = (props) => {
142
163
  const isDanger = () => props.config.variant === "danger";
143
164
  return (() => {
144
- var _el$14 = getNextElement(_tmpl$7), _el$19 = _el$14.firstChild, [_el$20, _co$4] = getNextMarker(_el$19.nextSibling), _el$16 = _el$20.nextSibling, _el$17 = _el$16.firstChild, _el$18 = _el$17.nextSibling;
145
- insert(_el$14, createComponent(Show, {
165
+ var _el$15 = getNextElement(_tmpl$8), _el$20 = _el$15.firstChild, [_el$21, _co$4] = getNextMarker(_el$20.nextSibling), _el$17 = _el$21.nextSibling, _el$18 = _el$17.firstChild, _el$19 = _el$18.nextSibling;
166
+ insert(_el$15, createComponent(Show, {
146
167
  get when() {
147
168
  return props.config.message;
148
169
  },
149
170
  get children() {
150
- var _el$15 = getNextElement(_tmpl$6);
151
- insert(_el$15, () => props.config.message);
152
- return _el$15;
171
+ var _el$16 = getNextElement(_tmpl$7);
172
+ insert(_el$16, () => props.config.message);
173
+ return _el$16;
153
174
  }
154
- }), _el$20, _co$4);
155
- addEventListener(_el$17, "click", props.onCancel, true);
156
- insert(_el$17, () => props.config.cancelLabel || "Cancel");
157
- addEventListener(_el$18, "click", props.onConfirm, true);
158
- insert(_el$18, () => props.config.confirmLabel || "Confirm");
159
- effect(() => className(_el$18, `px-4 py-2 text-sm font-medium rounded-lg text-white transition-colors ${isDanger() ? "bg-red-600 hover:bg-red-700" : "bg-blue-600 hover:bg-blue-700"}`));
175
+ }), _el$21, _co$4);
176
+ addEventListener(_el$18, "click", props.onCancel, true);
177
+ insert(_el$18, () => props.config.cancelLabel || "Cancel");
178
+ addEventListener(_el$19, "click", props.onConfirm, true);
179
+ insert(_el$19, () => props.config.confirmLabel || "Confirm");
180
+ effect(() => className(_el$19, `px-4 py-2 text-sm font-medium rounded-lg text-white transition-colors ${isDanger() ? "bg-red-600 hover:bg-red-700" : "bg-blue-600 hover:bg-blue-700"}`));
160
181
  runHydrationEvents();
161
- return _el$14;
182
+ return _el$15;
162
183
  })();
163
184
  };
164
185
  const FormBody = (props) => {
@@ -183,86 +204,86 @@ const FormBody = (props) => {
183
204
  });
184
205
  };
185
206
  return (() => {
186
- var _el$21 = getNextElement(_tmpl$8), _el$24 = _el$21.firstChild, [_el$25, _co$5] = getNextMarker(_el$24.nextSibling), _el$22 = _el$25.nextSibling, _el$23 = _el$22.firstChild;
187
- _el$21.addEventListener("submit", handleSubmit);
188
- insert(_el$21, createComponent(For, {
207
+ var _el$22 = getNextElement(_tmpl$9), _el$25 = _el$22.firstChild, [_el$26, _co$5] = getNextMarker(_el$25.nextSibling), _el$23 = _el$26.nextSibling, _el$24 = _el$23.firstChild;
208
+ _el$22.addEventListener("submit", handleSubmit);
209
+ insert(_el$22, createComponent(For, {
189
210
  get each() {
190
211
  return props.config.fields;
191
212
  },
192
213
  children: (field) => (() => {
193
- var _el$26 = getNextElement(_tmpl$11), _el$27 = _el$26.firstChild, _el$29 = _el$27.firstChild, [_el$30, _co$6] = getNextMarker(_el$29.nextSibling), _el$31 = _el$30.nextSibling, [_el$32, _co$7] = getNextMarker(_el$31.nextSibling), _el$39 = _el$27.nextSibling, [_el$40, _co$9] = getNextMarker(_el$39.nextSibling);
194
- insert(_el$27, () => field.label, _el$30, _co$6);
195
- insert(_el$27, createComponent(Show, {
214
+ var _el$27 = getNextElement(_tmpl$12), _el$28 = _el$27.firstChild, _el$30 = _el$28.firstChild, [_el$31, _co$6] = getNextMarker(_el$30.nextSibling), _el$32 = _el$31.nextSibling, [_el$33, _co$7] = getNextMarker(_el$32.nextSibling), _el$40 = _el$28.nextSibling, [_el$41, _co$9] = getNextMarker(_el$40.nextSibling);
215
+ insert(_el$28, () => field.label, _el$31, _co$6);
216
+ insert(_el$28, createComponent(Show, {
196
217
  get when() {
197
218
  return field.required;
198
219
  },
199
220
  get children() {
200
- return getNextElement(_tmpl$9);
221
+ return getNextElement(_tmpl$0);
201
222
  }
202
- }), _el$32, _co$7);
203
- insert(_el$26, createComponent(Switch, {
223
+ }), _el$33, _co$7);
224
+ insert(_el$27, createComponent(Switch, {
204
225
  get children() {
205
226
  return [createComponent(Match, {
206
227
  get when() {
207
228
  return field.type === "textarea";
208
229
  },
209
230
  get children() {
210
- var _el$33 = getNextElement(_tmpl$0);
211
- _el$33.$$input = (e) => updateField(field.name, e.currentTarget.value);
212
- effect(() => setAttribute(_el$33, "placeholder", field.placeholder));
213
- effect(() => setProperty(_el$33, "value", formData()[field.name] || ""));
231
+ var _el$34 = getNextElement(_tmpl$1);
232
+ _el$34.$$input = (e) => updateField(field.name, e.currentTarget.value);
233
+ effect(() => setAttribute(_el$34, "placeholder", field.placeholder));
234
+ effect(() => setProperty(_el$34, "value", formData()[field.name] || ""));
214
235
  runHydrationEvents();
215
- return _el$33;
236
+ return _el$34;
216
237
  }
217
238
  }), createComponent(Match, {
218
239
  get when() {
219
240
  return field.type === "select";
220
241
  },
221
242
  get children() {
222
- var _el$34 = getNextElement(_tmpl$1), _el$35 = _el$34.firstChild, _el$36 = _el$35.nextSibling, [_el$37, _co$8] = getNextMarker(_el$36.nextSibling);
223
- _el$34.addEventListener("change", (e) => updateField(field.name, e.currentTarget.value));
224
- insert(_el$35, () => field.placeholder || "Select...");
225
- insert(_el$34, createComponent(For, {
243
+ var _el$35 = getNextElement(_tmpl$10), _el$36 = _el$35.firstChild, _el$37 = _el$36.nextSibling, [_el$38, _co$8] = getNextMarker(_el$37.nextSibling);
244
+ _el$35.addEventListener("change", (e) => updateField(field.name, e.currentTarget.value));
245
+ insert(_el$36, () => field.placeholder || "Select...");
246
+ insert(_el$35, createComponent(For, {
226
247
  get each() {
227
248
  return field.options;
228
249
  },
229
250
  children: (opt) => (() => {
230
- var _el$41 = getNextElement(_tmpl$12);
231
- insert(_el$41, () => opt.label);
232
- effect(() => setProperty(_el$41, "value", opt.value));
233
- return _el$41;
251
+ var _el$42 = getNextElement(_tmpl$13);
252
+ insert(_el$42, () => opt.label);
253
+ effect(() => setProperty(_el$42, "value", opt.value));
254
+ return _el$42;
234
255
  })()
235
- }), _el$37, _co$8);
236
- effect(() => setProperty(_el$34, "value", formData()[field.name] || ""));
237
- return _el$34;
256
+ }), _el$38, _co$8);
257
+ effect(() => setProperty(_el$35, "value", formData()[field.name] || ""));
258
+ return _el$35;
238
259
  }
239
260
  }), createComponent(Match, {
240
261
  when: true,
241
262
  get children() {
242
- var _el$38 = getNextElement(_tmpl$10);
243
- _el$38.$$input = (e) => updateField(field.name, e.currentTarget.value);
263
+ var _el$39 = getNextElement(_tmpl$11);
264
+ _el$39.$$input = (e) => updateField(field.name, e.currentTarget.value);
244
265
  effect((_p$) => {
245
- var _v$ = field.type === "number" ? "number" : "text", _v$2 = field.placeholder;
246
- _v$ !== _p$.e && setAttribute(_el$38, "type", _p$.e = _v$);
247
- _v$2 !== _p$.t && setAttribute(_el$38, "placeholder", _p$.t = _v$2);
266
+ var _v$4 = field.type === "number" ? "number" : "text", _v$5 = field.placeholder;
267
+ _v$4 !== _p$.e && setAttribute(_el$39, "type", _p$.e = _v$4);
268
+ _v$5 !== _p$.t && setAttribute(_el$39, "placeholder", _p$.t = _v$5);
248
269
  return _p$;
249
270
  }, {
250
271
  e: void 0,
251
272
  t: void 0
252
273
  });
253
- effect(() => setProperty(_el$38, "value", formData()[field.name] || ""));
274
+ effect(() => setProperty(_el$39, "value", formData()[field.name] || ""));
254
275
  runHydrationEvents();
255
- return _el$38;
276
+ return _el$39;
256
277
  }
257
278
  })];
258
279
  }
259
- }), _el$40, _co$9);
260
- return _el$26;
280
+ }), _el$41, _co$9);
281
+ return _el$27;
261
282
  })()
262
- }), _el$25, _co$5);
263
- insert(_el$23, () => props.config.submitLabel || "Submit");
264
- effect(() => setProperty(_el$23, "disabled", !isValid()));
265
- return _el$21;
283
+ }), _el$26, _co$5);
284
+ insert(_el$24, () => props.config.submitLabel || "Submit");
285
+ effect(() => setProperty(_el$24, "disabled", !isValid()));
286
+ return _el$22;
266
287
  })();
267
288
  };
268
289
  delegateEvents(["click", "input"]);
@@ -1 +1 @@
1
- {"version":3,"file":"ChatPrompt.js","sources":["../../src/components/ChatPrompt.tsx"],"sourcesContent":["/**\n * ChatPrompt — Ephemeral structured interaction above chat input\n * v2.4.0: choice, confirm, form subtypes\n *\n * @experimental — This component may change without major bump until v2.5.0.\n *\n * Renders above the chat input. User responds → Promise resolves → prompt disappears.\n * Supports AbortSignal for cleanup on navigation (C4).\n */\n\nimport { Component, Show, For, createSignal, onCleanup, Switch, Match } from 'solid-js'\nimport type {\n ChatPromptConfig,\n ChatPromptResponse,\n ChoicePromptConfig,\n ConfirmPromptConfig,\n FormPromptConfig,\n} from '../types/chat-bus'\n\nexport interface ChatPromptProps {\n /** Prompt configuration */\n config: ChatPromptConfig\n /** Called when user responds */\n onSubmit: (response: ChatPromptResponse) => void\n /** Called when user dismisses */\n onDismiss?: () => void\n}\n\n/**\n * @experimental\n * Ephemeral interaction component — choice buttons, confirmation dialog, or quick form.\n * Designed to sit between the chat messages and the input area.\n *\n * @example\n * <ChatPrompt\n * config={{ type: 'choice', title: 'Format?', config: { options: [...] } }}\n * onSubmit={(r) => bus.events.emit('onChatPromptResponse', { streamKey, response: r })}\n * onDismiss={() => setActivePrompt(null)}\n * />\n */\nexport const ChatPrompt: Component<ChatPromptProps> = (props) => {\n // F1: Guard against null/undefined config (e.g. after dismiss clears state)\n if (!props.config) return null\n\n return (\n <div\n class=\"w-full max-w-2xl mx-auto mb-2 bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 shadow-lg overflow-hidden\"\n style={{ animation: 'chat-prompt-slide-up 0.2s ease-out' }}\n role=\"dialog\"\n aria-label={props.config.title}\n >\n {/* Header */}\n <div class=\"flex items-center justify-between px-4 py-2.5 border-b border-gray-100 dark:border-gray-700\">\n <p class=\"text-sm font-medium text-gray-900 dark:text-white\">{props.config.title}</p>\n <button\n onClick={() => {\n props.onDismiss?.()\n props.onSubmit({ type: props.config.type, value: '', label: '', dismissed: true })\n }}\n class=\"p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors\"\n aria-label=\"Dismiss\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n {/* Body — type-specific */}\n <div class=\"px-4 py-3\">\n <Switch>\n <Match when={props.config.type === 'choice'}>\n <ChoiceBody\n config={props.config.config as ChoicePromptConfig}\n onSelect={(value, label) => props.onSubmit({ type: 'choice', value, label })}\n />\n </Match>\n <Match when={props.config.type === 'confirm'}>\n <ConfirmBody\n config={props.config.config as ConfirmPromptConfig}\n onConfirm={() => props.onSubmit({ type: 'confirm', value: 'confirmed', label: (props.config.config as ConfirmPromptConfig).confirmLabel || 'Confirmed' })}\n onCancel={() => {\n props.onDismiss?.()\n props.onSubmit({ type: 'confirm', value: 'cancelled', label: (props.config.config as ConfirmPromptConfig).cancelLabel || 'Cancelled', dismissed: true })\n }}\n />\n </Match>\n <Match when={props.config.type === 'form'}>\n <FormBody\n config={props.config.config as FormPromptConfig}\n onSubmit={(data, label) => props.onSubmit({ type: 'form', value: data, label })}\n />\n </Match>\n </Switch>\n </div>\n\n <style>{`\n @keyframes chat-prompt-slide-up {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n }\n `}</style>\n </div>\n )\n}\n\n// ─── Choice ──────────────────────────────────────────────────\n\nconst ChoiceBody: Component<{\n config: ChoicePromptConfig\n onSelect: (value: string, label: string) => void\n}> = (props) => {\n const layoutClass = () => {\n switch (props.config.layout) {\n case 'vertical': return 'flex flex-col gap-2'\n case 'grid': return 'grid grid-cols-2 gap-2'\n default: return 'flex flex-wrap gap-2'\n }\n }\n\n return (\n <div class={layoutClass()}>\n <For each={props.config.options}>\n {(option) => (\n <button\n onClick={() => props.onSelect(option.value, option.label)}\n class=\"px-4 py-2 text-sm font-medium rounded-lg border border-gray-200 dark:border-gray-600 bg-gray-50 dark:bg-gray-700 text-gray-900 dark:text-white hover:bg-blue-50 hover:border-blue-300 dark:hover:bg-blue-900/30 dark:hover:border-blue-600 transition-colors text-left\"\n >\n <Show when={option.icon}>\n <span class=\"mr-2\">{option.icon}</span>\n </Show>\n {option.label}\n <Show when={option.description}>\n <span class=\"block text-xs text-gray-500 dark:text-gray-400 mt-0.5 font-normal\">{option.description}</span>\n </Show>\n </button>\n )}\n </For>\n </div>\n )\n}\n\n// ─── Confirm ─────────────────────────────────────────────────\n\nconst ConfirmBody: Component<{\n config: ConfirmPromptConfig\n onConfirm: () => void\n onCancel: () => void\n}> = (props) => {\n const isDanger = () => props.config.variant === 'danger'\n\n return (\n <div>\n <Show when={props.config.message}>\n <p class=\"text-sm text-gray-600 dark:text-gray-400 mb-3\">{props.config.message}</p>\n </Show>\n <div class=\"flex gap-2 justify-end\">\n <button\n onClick={props.onCancel}\n class=\"px-4 py-2 text-sm font-medium rounded-lg border border-gray-200 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors\"\n >\n {props.config.cancelLabel || 'Cancel'}\n </button>\n <button\n onClick={props.onConfirm}\n class={`px-4 py-2 text-sm font-medium rounded-lg text-white transition-colors ${\n isDanger()\n ? 'bg-red-600 hover:bg-red-700'\n : 'bg-blue-600 hover:bg-blue-700'\n }`}\n >\n {props.config.confirmLabel || 'Confirm'}\n </button>\n </div>\n </div>\n )\n}\n\n// ─── Form ────────────────────────────────────────────────────\n\nconst FormBody: Component<{\n config: FormPromptConfig\n onSubmit: (data: Record<string, unknown>, label: string) => void\n}> = (props) => {\n const [formData, setFormData] = createSignal<Record<string, string>>({})\n\n const updateField = (name: string, value: string) => {\n setFormData((prev) => ({ ...prev, [name]: value }))\n }\n\n const handleSubmit = (e: Event) => {\n e.preventDefault()\n const data = formData()\n // Build a human-readable label from the form values\n const label = Object.entries(data)\n .filter(([, v]) => v)\n .map(([k, v]) => `${k}: ${v}`)\n .join(', ')\n props.onSubmit(data, label || 'Form submitted')\n }\n\n const isValid = () => {\n const data = formData()\n return (props.config.fields || [])\n .filter((f) => f.required)\n .every((f) => data[f.name]?.trim())\n }\n\n return (\n <form onSubmit={handleSubmit} class=\"flex flex-col gap-3\">\n <For each={props.config.fields}>\n {(field) => (\n <div>\n <label class=\"block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1\">\n {field.label}\n <Show when={field.required}>\n <span class=\"text-red-500 ml-0.5\">*</span>\n </Show>\n </label>\n <Switch>\n <Match when={field.type === 'textarea'}>\n <textarea\n value={formData()[field.name] || ''}\n onInput={(e) => updateField(field.name, e.currentTarget.value)}\n placeholder={field.placeholder}\n rows={3}\n class=\"w-full px-3 py-2 text-sm rounded-lg border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:border-blue-400 focus:ring-1 focus:ring-blue-400 outline-none transition-colors\"\n />\n </Match>\n <Match when={field.type === 'select'}>\n <select\n value={formData()[field.name] || ''}\n onChange={(e) => updateField(field.name, e.currentTarget.value)}\n class=\"w-full px-3 py-2 text-sm rounded-lg border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:border-blue-400 focus:ring-1 focus:ring-blue-400 outline-none transition-colors\"\n >\n <option value=\"\">{field.placeholder || 'Select...'}</option>\n <For each={field.options}>\n {(opt) => <option value={opt.value}>{opt.label}</option>}\n </For>\n </select>\n </Match>\n <Match when={true}>\n <input\n type={field.type === 'number' ? 'number' : 'text'}\n value={formData()[field.name] || ''}\n onInput={(e) => updateField(field.name, e.currentTarget.value)}\n placeholder={field.placeholder}\n class=\"w-full px-3 py-2 text-sm rounded-lg border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:border-blue-400 focus:ring-1 focus:ring-blue-400 outline-none transition-colors\"\n />\n </Match>\n </Switch>\n </div>\n )}\n </For>\n <div class=\"flex justify-end\">\n <button\n type=\"submit\"\n disabled={!isValid()}\n class=\"px-4 py-2 text-sm font-medium rounded-lg text-white bg-blue-600 hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors\"\n >\n {props.config.submitLabel || 'Submit'}\n </button>\n </div>\n </form>\n )\n}\n"],"names":["ChatPrompt","props","config","_el$","_$getNextElement","_tmpl$","_el$2","firstChild","_el$3","_el$4","nextSibling","_el$5","_$insert","title","$$click","onDismiss","onSubmit","type","value","label","dismissed","_$createComponent","Switch","children","Match","when","ChoiceBody","onSelect","ConfirmBody","onConfirm","confirmLabel","onCancel","cancelLabel","FormBody","data","_$effect","_$setAttribute","_$runHydrationEvents","layoutClass","layout","_el$6","_tmpl$2","For","each","options","option","_el$7","_tmpl$5","_el$0","_el$1","_co$","_$getNextMarker","_el$10","_el$11","_co$2","_el$12","_el$13","_co$3","Show","icon","_el$8","_tmpl$3","description","_el$9","_tmpl$4","_$className","isDanger","variant","_el$14","_tmpl$7","_el$19","_el$20","_co$4","_el$16","_el$17","_el$18","message","_el$15","_tmpl$6","_$addEventListener","formData","setFormData","createSignal","updateField","name","prev","handleSubmit","e","preventDefault","Object","entries","filter","v","map","k","join","isValid","fields","f","required","every","trim","_el$21","_tmpl$8","_el$24","_el$25","_co$5","_el$22","_el$23","addEventListener","field","_el$26","_tmpl$11","_el$27","_el$29","_el$30","_co$6","_el$31","_el$32","_co$7","_el$39","_el$40","_co$9","_tmpl$9","_el$33","_tmpl$0","$$input","currentTarget","placeholder","_$setProperty","_el$34","_tmpl$1","_el$35","_el$36","_el$37","_co$8","opt","_el$41","_tmpl$12","_el$38","_tmpl$10","_p$","_v$","_v$2","t","undefined","submitLabel","_$delegateEvents"],"mappings":"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"}
1
+ {"version":3,"file":"ChatPrompt.js","sources":["../../src/components/ChatPrompt.tsx"],"sourcesContent":["/**\n * ChatPrompt — Ephemeral structured interaction above chat input\n * v2.4.0: choice, confirm, form subtypes\n *\n * @experimental — This component may change without major bump until v2.5.0.\n *\n * Renders above the chat input. User responds → Promise resolves → prompt disappears.\n * Supports AbortSignal for cleanup on navigation (C4).\n */\n\nimport { Component, Show, For, createSignal, onCleanup, Switch, Match } from 'solid-js'\nimport type {\n ChatPromptConfig,\n ChatPromptResponse,\n ChoicePromptConfig,\n ConfirmPromptConfig,\n FormPromptConfig,\n} from '../types/chat-bus'\n\nexport interface ChatPromptProps {\n /** Prompt configuration */\n config: ChatPromptConfig\n /** Called when user responds */\n onSubmit: (response: ChatPromptResponse) => void\n /** Called when user dismisses (e.g. \"send as-is\") */\n onDismiss?: () => void\n /** Label for the dismiss button (replaces X icon). Default: shows X icon. */\n dismissLabel?: string\n}\n\n/**\n * @experimental\n * Ephemeral interaction component — choice buttons, confirmation dialog, or quick form.\n * Designed to sit between the chat messages and the input area.\n *\n * @example\n * <ChatPrompt\n * config={{ type: 'choice', title: 'Format?', config: { options: [...] } }}\n * onSubmit={(r) => bus.events.emit('onChatPromptResponse', { streamKey, response: r })}\n * onDismiss={() => setActivePrompt(null)}\n * />\n */\nexport const ChatPrompt: Component<ChatPromptProps> = (props) => {\n // F1: Guard against null/undefined config (e.g. after dismiss clears state)\n if (!props.config) return null\n\n return (\n <div\n class=\"w-full max-w-2xl mx-auto mb-2 bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 shadow-lg overflow-hidden\"\n style={{ animation: 'chat-prompt-slide-up 0.2s ease-out' }}\n role=\"dialog\"\n aria-label={props.config.title}\n >\n {/* Header */}\n <div class=\"flex items-center justify-between px-4 py-2.5 border-b border-gray-100 dark:border-gray-700\">\n <p class=\"text-sm font-medium text-gray-900 dark:text-white\">{props.config.title}</p>\n <button\n onClick={() => {\n props.onDismiss?.()\n props.onSubmit({ type: props.config.type, value: '', label: '', dismissed: true })\n }}\n class={props.dismissLabel\n ? 'px-3 py-1 text-xs font-medium text-blue-600 dark:text-blue-400 hover:bg-blue-50 dark:hover:bg-blue-900/20 rounded-lg transition-colors'\n : 'p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors'\n }\n aria-label={props.dismissLabel || 'Dismiss'}\n >\n <Show when={props.dismissLabel} fallback={\n <svg class=\"w-4 h-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n }>\n {props.dismissLabel}\n </Show>\n </button>\n </div>\n\n {/* Body — type-specific */}\n <div class=\"px-4 py-3\">\n <Switch>\n <Match when={props.config.type === 'choice'}>\n <ChoiceBody\n config={props.config.config as ChoicePromptConfig}\n onSelect={(value, label) => props.onSubmit({ type: 'choice', value, label })}\n />\n </Match>\n <Match when={props.config.type === 'confirm'}>\n <ConfirmBody\n config={props.config.config as ConfirmPromptConfig}\n onConfirm={() => props.onSubmit({ type: 'confirm', value: 'confirmed', label: (props.config.config as ConfirmPromptConfig).confirmLabel || 'Confirmed' })}\n onCancel={() => {\n props.onDismiss?.()\n props.onSubmit({ type: 'confirm', value: 'cancelled', label: (props.config.config as ConfirmPromptConfig).cancelLabel || 'Cancelled', dismissed: true })\n }}\n />\n </Match>\n <Match when={props.config.type === 'form'}>\n <FormBody\n config={props.config.config as FormPromptConfig}\n onSubmit={(data, label) => props.onSubmit({ type: 'form', value: data, label })}\n />\n </Match>\n </Switch>\n </div>\n\n <style>{`\n @keyframes chat-prompt-slide-up {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n }\n `}</style>\n </div>\n )\n}\n\n// ─── Choice ──────────────────────────────────────────────────\n\nconst ChoiceBody: Component<{\n config: ChoicePromptConfig\n onSelect: (value: string, label: string) => void\n}> = (props) => {\n const layoutClass = () => {\n switch (props.config.layout) {\n case 'vertical': return 'flex flex-col gap-2'\n case 'grid': return 'grid grid-cols-2 gap-2'\n default: return 'flex flex-wrap gap-2'\n }\n }\n\n return (\n <div class={layoutClass()}>\n <For each={props.config.options}>\n {(option) => (\n <button\n onClick={() => props.onSelect(option.value, option.label)}\n class=\"px-4 py-2 text-sm font-medium rounded-lg border border-gray-200 dark:border-gray-600 bg-gray-50 dark:bg-gray-700 text-gray-900 dark:text-white hover:bg-blue-50 hover:border-blue-300 dark:hover:bg-blue-900/30 dark:hover:border-blue-600 transition-colors text-left\"\n >\n <Show when={option.icon}>\n <span class=\"mr-2\">{option.icon}</span>\n </Show>\n {option.label}\n <Show when={option.description}>\n <span class=\"block text-xs text-gray-500 dark:text-gray-400 mt-0.5 font-normal\">{option.description}</span>\n </Show>\n </button>\n )}\n </For>\n </div>\n )\n}\n\n// ─── Confirm ─────────────────────────────────────────────────\n\nconst ConfirmBody: Component<{\n config: ConfirmPromptConfig\n onConfirm: () => void\n onCancel: () => void\n}> = (props) => {\n const isDanger = () => props.config.variant === 'danger'\n\n return (\n <div>\n <Show when={props.config.message}>\n <p class=\"text-sm text-gray-600 dark:text-gray-400 mb-3\">{props.config.message}</p>\n </Show>\n <div class=\"flex gap-2 justify-end\">\n <button\n onClick={props.onCancel}\n class=\"px-4 py-2 text-sm font-medium rounded-lg border border-gray-200 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors\"\n >\n {props.config.cancelLabel || 'Cancel'}\n </button>\n <button\n onClick={props.onConfirm}\n class={`px-4 py-2 text-sm font-medium rounded-lg text-white transition-colors ${\n isDanger()\n ? 'bg-red-600 hover:bg-red-700'\n : 'bg-blue-600 hover:bg-blue-700'\n }`}\n >\n {props.config.confirmLabel || 'Confirm'}\n </button>\n </div>\n </div>\n )\n}\n\n// ─── Form ────────────────────────────────────────────────────\n\nconst FormBody: Component<{\n config: FormPromptConfig\n onSubmit: (data: Record<string, unknown>, label: string) => void\n}> = (props) => {\n const [formData, setFormData] = createSignal<Record<string, string>>({})\n\n const updateField = (name: string, value: string) => {\n setFormData((prev) => ({ ...prev, [name]: value }))\n }\n\n const handleSubmit = (e: Event) => {\n e.preventDefault()\n const data = formData()\n // Build a human-readable label from the form values\n const label = Object.entries(data)\n .filter(([, v]) => v)\n .map(([k, v]) => `${k}: ${v}`)\n .join(', ')\n props.onSubmit(data, label || 'Form submitted')\n }\n\n const isValid = () => {\n const data = formData()\n return (props.config.fields || [])\n .filter((f) => f.required)\n .every((f) => data[f.name]?.trim())\n }\n\n return (\n <form onSubmit={handleSubmit} class=\"flex flex-col gap-3\">\n <For each={props.config.fields}>\n {(field) => (\n <div>\n <label class=\"block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1\">\n {field.label}\n <Show when={field.required}>\n <span class=\"text-red-500 ml-0.5\">*</span>\n </Show>\n </label>\n <Switch>\n <Match when={field.type === 'textarea'}>\n <textarea\n value={formData()[field.name] || ''}\n onInput={(e) => updateField(field.name, e.currentTarget.value)}\n placeholder={field.placeholder}\n rows={3}\n class=\"w-full px-3 py-2 text-sm rounded-lg border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:border-blue-400 focus:ring-1 focus:ring-blue-400 outline-none transition-colors\"\n />\n </Match>\n <Match when={field.type === 'select'}>\n <select\n value={formData()[field.name] || ''}\n onChange={(e) => updateField(field.name, e.currentTarget.value)}\n class=\"w-full px-3 py-2 text-sm rounded-lg border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:border-blue-400 focus:ring-1 focus:ring-blue-400 outline-none transition-colors\"\n >\n <option value=\"\">{field.placeholder || 'Select...'}</option>\n <For each={field.options}>\n {(opt) => <option value={opt.value}>{opt.label}</option>}\n </For>\n </select>\n </Match>\n <Match when={true}>\n <input\n type={field.type === 'number' ? 'number' : 'text'}\n value={formData()[field.name] || ''}\n onInput={(e) => updateField(field.name, e.currentTarget.value)}\n placeholder={field.placeholder}\n class=\"w-full px-3 py-2 text-sm rounded-lg border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:border-blue-400 focus:ring-1 focus:ring-blue-400 outline-none transition-colors\"\n />\n </Match>\n </Switch>\n </div>\n )}\n </For>\n <div class=\"flex justify-end\">\n <button\n type=\"submit\"\n disabled={!isValid()}\n class=\"px-4 py-2 text-sm font-medium rounded-lg text-white bg-blue-600 hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors\"\n >\n {props.config.submitLabel || 'Submit'}\n </button>\n </div>\n </form>\n )\n}\n"],"names":["ChatPrompt","props","config","_el$","_$getNextElement","_tmpl$","_el$2","firstChild","_el$3","_el$4","nextSibling","_el$5","_$insert","title","$$click","onDismiss","onSubmit","type","value","label","dismissed","_$createComponent","Show","when","dismissLabel","fallback","_tmpl$2","children","Switch","Match","ChoiceBody","onSelect","ConfirmBody","onConfirm","confirmLabel","onCancel","cancelLabel","FormBody","data","_$effect","_p$","_v$","_v$2","_v$3","e","_$setAttribute","t","_$className","a","undefined","_$runHydrationEvents","layoutClass","layout","_el$7","_tmpl$3","For","each","options","option","_el$8","_tmpl$6","_el$1","_el$10","_co$","_$getNextMarker","_el$11","_el$12","_co$2","_el$13","_el$14","_co$3","icon","_el$9","_tmpl$4","description","_el$0","_tmpl$5","isDanger","variant","_el$15","_tmpl$8","_el$20","_el$21","_co$4","_el$17","_el$18","_el$19","message","_el$16","_tmpl$7","_$addEventListener","formData","setFormData","createSignal","updateField","name","prev","handleSubmit","preventDefault","Object","entries","filter","v","map","k","join","isValid","fields","f","required","every","trim","_el$22","_tmpl$9","_el$25","_el$26","_co$5","_el$23","_el$24","addEventListener","field","_el$27","_tmpl$12","_el$28","_el$30","_el$31","_co$6","_el$32","_el$33","_co$7","_el$40","_el$41","_co$9","_tmpl$0","_el$34","_tmpl$1","$$input","currentTarget","placeholder","_$setProperty","_el$35","_tmpl$10","_el$36","_el$37","_el$38","_co$8","opt","_el$42","_tmpl$13","_el$39","_tmpl$11","_v$4","_v$5","submitLabel","_$delegateEvents"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seed-ship/mcp-ui-solid",
3
- "version": "2.5.2",
3
+ "version": "2.5.3",
4
4
  "description": "SolidJS components for rendering MCP-generated UI resources",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -259,6 +259,61 @@ describe('ChatPrompt', () => {
259
259
  })
260
260
  })
261
261
 
262
+ // ─── dismissLabel ────────────────────────────────────
263
+
264
+ describe('dismissLabel', () => {
265
+ it('shows X icon by default (no dismissLabel)', () => {
266
+ const config: ChatPromptConfig = {
267
+ type: 'choice',
268
+ title: 'Test',
269
+ config: { options: [{ value: 'a', label: 'A' }] },
270
+ }
271
+ const { getByLabelText } = render(() => (
272
+ <ChatPrompt config={config} onSubmit={() => {}} />
273
+ ))
274
+
275
+ expect(getByLabelText('Dismiss')).toBeDefined()
276
+ expect(getByLabelText('Dismiss').querySelector('svg')).not.toBeNull()
277
+ })
278
+
279
+ it('shows text button when dismissLabel is provided', () => {
280
+ const config: ChatPromptConfig = {
281
+ type: 'choice',
282
+ title: 'Test',
283
+ config: { options: [{ value: 'a', label: 'A' }] },
284
+ }
285
+ const { getByText, getByLabelText } = render(() => (
286
+ <ChatPrompt config={config} onSubmit={() => {}} dismissLabel="Send as-is" />
287
+ ))
288
+
289
+ expect(getByText('Send as-is')).toBeDefined()
290
+ expect(getByLabelText('Send as-is')).toBeDefined()
291
+ })
292
+
293
+ it('calls onDismiss + onSubmit when dismissLabel button clicked', () => {
294
+ const onSubmit = vi.fn()
295
+ const onDismiss = vi.fn()
296
+ const config: ChatPromptConfig = {
297
+ type: 'choice',
298
+ title: 'Test',
299
+ config: { options: [{ value: 'a', label: 'A' }] },
300
+ }
301
+ const { getByText } = render(() => (
302
+ <ChatPrompt config={config} onSubmit={onSubmit} onDismiss={onDismiss} dismissLabel="Envoyer directement" />
303
+ ))
304
+
305
+ fireEvent.click(getByText('Envoyer directement'))
306
+
307
+ expect(onDismiss).toHaveBeenCalled()
308
+ expect(onSubmit).toHaveBeenCalledWith({
309
+ type: 'choice',
310
+ value: '',
311
+ label: '',
312
+ dismissed: true,
313
+ })
314
+ })
315
+ })
316
+
262
317
  // ─── Null guard (F1) ──────────────────────────────────
263
318
 
264
319
  describe('null guard', () => {
@@ -22,8 +22,10 @@ export interface ChatPromptProps {
22
22
  config: ChatPromptConfig
23
23
  /** Called when user responds */
24
24
  onSubmit: (response: ChatPromptResponse) => void
25
- /** Called when user dismisses */
25
+ /** Called when user dismisses (e.g. "send as-is") */
26
26
  onDismiss?: () => void
27
+ /** Label for the dismiss button (replaces X icon). Default: shows X icon. */
28
+ dismissLabel?: string
27
29
  }
28
30
 
29
31
  /**
@@ -57,12 +59,19 @@ export const ChatPrompt: Component<ChatPromptProps> = (props) => {
57
59
  props.onDismiss?.()
58
60
  props.onSubmit({ type: props.config.type, value: '', label: '', dismissed: true })
59
61
  }}
60
- class="p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"
61
- aria-label="Dismiss"
62
+ class={props.dismissLabel
63
+ ? 'px-3 py-1 text-xs font-medium text-blue-600 dark:text-blue-400 hover:bg-blue-50 dark:hover:bg-blue-900/20 rounded-lg transition-colors'
64
+ : 'p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors'
65
+ }
66
+ aria-label={props.dismissLabel || 'Dismiss'}
62
67
  >
63
- <svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
64
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
65
- </svg>
68
+ <Show when={props.dismissLabel} fallback={
69
+ <svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
70
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
71
+ </svg>
72
+ }>
73
+ {props.dismissLabel}
74
+ </Show>
66
75
  </button>
67
76
  </div>
68
77