@seed-ship/mcp-ui-solid 2.6.4 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ChatPrompt.cjs +112 -6
- package/dist/components/ChatPrompt.cjs.map +1 -1
- package/dist/components/ChatPrompt.d.ts.map +1 -1
- package/dist/components/ChatPrompt.js +113 -7
- package/dist/components/ChatPrompt.js.map +1 -1
- package/dist/types/chat-bus.d.ts +25 -0
- package/dist/types/chat-bus.d.ts.map +1 -1
- package/dist/types/index.d.ts +13 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types.d.cts +13 -0
- package/dist/types.d.ts +13 -0
- package/package.json +1 -1
- package/src/components/ChatPrompt.tsx +98 -2
- package/src/types/chat-bus.ts +21 -0
- package/src/types/index.ts +15 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -8,7 +8,7 @@ var _tmpl$ = /* @__PURE__ */ web.template(`<div class="w-full max-w-2xl mx-auto
|
|
|
8
8
|
from { opacity: 0; transform: translateY(8px); }
|
|
9
9
|
to { opacity: 1; transform: translateY(0); }
|
|
10
10
|
}
|
|
11
|
-
`), _tmpl$2 = /* @__PURE__ */ web.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__ */ web.template(`<div>`), _tmpl$4 = /* @__PURE__ */ web.template(`<span class=mr-2>`), _tmpl$5 = /* @__PURE__ */ web.template(`<span class="block text-xs text-gray-500 dark:text-gray-400 mt-0.5 font-normal">`), _tmpl$6 = /* @__PURE__ */ web.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__ */ web.template(`<p class="text-sm text-gray-600 dark:text-gray-400 mb-3">`), _tmpl$8 = /* @__PURE__ */ web.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__ */ web.template(`<form class="flex flex-col gap-3"
|
|
11
|
+
`), _tmpl$2 = /* @__PURE__ */ web.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__ */ web.template(`<div>`), _tmpl$4 = /* @__PURE__ */ web.template(`<span class=mr-2>`), _tmpl$5 = /* @__PURE__ */ web.template(`<span class="block text-xs text-gray-500 dark:text-gray-400 mt-0.5 font-normal">`), _tmpl$6 = /* @__PURE__ */ web.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__ */ web.template(`<p class="text-sm text-gray-600 dark:text-gray-400 mb-3">`), _tmpl$8 = /* @__PURE__ */ web.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__ */ web.template(`<p class="text-blue-400 animate-pulse">Loading preview...`), _tmpl$0 = /* @__PURE__ */ web.template(`<div class="px-3 py-2 bg-blue-50 dark:bg-blue-900/20 rounded-lg border border-blue-200 dark:border-blue-800 text-sm">`), _tmpl$1 = /* @__PURE__ */ web.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$10 = /* @__PURE__ */ web.template(`<p class="text-blue-700 dark:text-blue-300">`);
|
|
12
12
|
const ChatPrompt = (props) => {
|
|
13
13
|
if (!props.config) return null;
|
|
14
14
|
return (() => {
|
|
@@ -187,12 +187,82 @@ const ConfirmBody = (props) => {
|
|
|
187
187
|
};
|
|
188
188
|
const FormBody = (props) => {
|
|
189
189
|
const [formData, setFormData] = solidJs.createSignal({});
|
|
190
|
+
const [dynamicOptions, setDynamicOptions] = solidJs.createSignal({});
|
|
191
|
+
const [previewText, setPreviewText] = solidJs.createSignal("");
|
|
192
|
+
const [previewLoading, setPreviewLoading] = solidJs.createSignal(false);
|
|
193
|
+
let previewTimer = null;
|
|
190
194
|
const updateField = (name, value) => {
|
|
191
195
|
setFormData((prev) => ({
|
|
192
196
|
...prev,
|
|
193
197
|
[name]: value
|
|
194
198
|
}));
|
|
195
199
|
};
|
|
200
|
+
solidJs.createEffect(() => {
|
|
201
|
+
const data = formData();
|
|
202
|
+
for (const field of props.config.fields || []) {
|
|
203
|
+
const dep = field.dependsOn || field.depends_on;
|
|
204
|
+
if (!dep) continue;
|
|
205
|
+
const parentValue = data[dep.field];
|
|
206
|
+
if (!parentValue) continue;
|
|
207
|
+
const apiUrl = (dep.apiUrl || dep.api_url || "").replace("{value}", encodeURIComponent(parentValue));
|
|
208
|
+
if (!apiUrl) continue;
|
|
209
|
+
const params = new URLSearchParams(dep.extraParams || dep.extra_params || {});
|
|
210
|
+
fetch(`${apiUrl}?${params}`).then((r) => r.json()).then((items) => {
|
|
211
|
+
const arr = Array.isArray(items) ? items : items.results || items.features || [];
|
|
212
|
+
const labelKey = dep.labelField || dep.label_field || "label";
|
|
213
|
+
const valueKey = dep.valueField || dep.value_field || "value";
|
|
214
|
+
setDynamicOptions((prev) => ({
|
|
215
|
+
...prev,
|
|
216
|
+
[field.name]: arr.map((item) => ({
|
|
217
|
+
label: item[labelKey] || String(item),
|
|
218
|
+
value: String(item[valueKey] || item[labelKey] || item)
|
|
219
|
+
}))
|
|
220
|
+
}));
|
|
221
|
+
}).catch(() => {
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
solidJs.createEffect(() => {
|
|
226
|
+
const preview = props.config.preview;
|
|
227
|
+
if (!preview) return;
|
|
228
|
+
const data = formData();
|
|
229
|
+
const hasValues = preview.fields.some((f) => {
|
|
230
|
+
const v = data[f];
|
|
231
|
+
return v !== void 0 && v !== "" && !(Array.isArray(v) && v.length === 0);
|
|
232
|
+
});
|
|
233
|
+
if (!hasValues) {
|
|
234
|
+
setPreviewText("");
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (previewTimer) clearTimeout(previewTimer);
|
|
238
|
+
previewTimer = setTimeout(async () => {
|
|
239
|
+
setPreviewLoading(true);
|
|
240
|
+
try {
|
|
241
|
+
const body = {};
|
|
242
|
+
for (const f of preview.fields) {
|
|
243
|
+
if (data[f] !== void 0) body[f] = data[f];
|
|
244
|
+
}
|
|
245
|
+
const res = await fetch(preview.endpoint, {
|
|
246
|
+
method: "POST",
|
|
247
|
+
headers: {
|
|
248
|
+
"Content-Type": "application/json"
|
|
249
|
+
},
|
|
250
|
+
credentials: "include",
|
|
251
|
+
body: JSON.stringify(body)
|
|
252
|
+
});
|
|
253
|
+
if (res.ok) {
|
|
254
|
+
const result = await res.json();
|
|
255
|
+
setPreviewText(result.summary_fr || result.summary || result.message || JSON.stringify(result));
|
|
256
|
+
}
|
|
257
|
+
} catch {
|
|
258
|
+
setPreviewText("");
|
|
259
|
+
}
|
|
260
|
+
setPreviewLoading(false);
|
|
261
|
+
}, preview.debounceMs || 500);
|
|
262
|
+
});
|
|
263
|
+
solidJs.onCleanup(() => {
|
|
264
|
+
if (previewTimer) clearTimeout(previewTimer);
|
|
265
|
+
});
|
|
196
266
|
const handleSubmit = (e) => {
|
|
197
267
|
e.preventDefault();
|
|
198
268
|
const data = formData();
|
|
@@ -208,24 +278,60 @@ const FormBody = (props) => {
|
|
|
208
278
|
return val !== void 0 && val !== "";
|
|
209
279
|
});
|
|
210
280
|
};
|
|
281
|
+
const getField = (field) => {
|
|
282
|
+
const dynOpts = dynamicOptions()[field.name];
|
|
283
|
+
if (dynOpts) {
|
|
284
|
+
return {
|
|
285
|
+
...field,
|
|
286
|
+
options: dynOpts
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
return field;
|
|
290
|
+
};
|
|
211
291
|
return (() => {
|
|
212
|
-
var _el$22 = web.getNextElement(_tmpl$
|
|
292
|
+
var _el$22 = web.getNextElement(_tmpl$1), _el$27 = _el$22.firstChild, [_el$28, _co$5] = web.getNextMarker(_el$27.nextSibling), _el$29 = _el$28.nextSibling, [_el$30, _co$6] = web.getNextMarker(_el$29.nextSibling), _el$25 = _el$30.nextSibling, _el$26 = _el$25.firstChild;
|
|
213
293
|
_el$22.addEventListener("submit", handleSubmit);
|
|
214
294
|
web.insert(_el$22, web.createComponent(solidJs.For, {
|
|
215
295
|
get each() {
|
|
216
296
|
return props.config.fields;
|
|
217
297
|
},
|
|
218
298
|
children: (field) => web.createComponent(FormFieldRenderer.FormFieldRenderer, {
|
|
219
|
-
field
|
|
299
|
+
get field() {
|
|
300
|
+
return getField(field);
|
|
301
|
+
},
|
|
220
302
|
get value() {
|
|
221
303
|
return formData()[field.name];
|
|
222
304
|
},
|
|
223
305
|
onChange: (val) => updateField(field.name, val),
|
|
224
306
|
formData
|
|
225
307
|
})
|
|
226
|
-
}), _el$
|
|
227
|
-
web.insert(_el$
|
|
228
|
-
|
|
308
|
+
}), _el$28, _co$5);
|
|
309
|
+
web.insert(_el$22, web.createComponent(solidJs.Show, {
|
|
310
|
+
get when() {
|
|
311
|
+
return previewText() || previewLoading();
|
|
312
|
+
},
|
|
313
|
+
get children() {
|
|
314
|
+
var _el$23 = web.getNextElement(_tmpl$0);
|
|
315
|
+
web.insert(_el$23, web.createComponent(solidJs.Show, {
|
|
316
|
+
get when() {
|
|
317
|
+
return previewLoading();
|
|
318
|
+
},
|
|
319
|
+
get fallback() {
|
|
320
|
+
return (() => {
|
|
321
|
+
var _el$31 = web.getNextElement(_tmpl$10);
|
|
322
|
+
web.insert(_el$31, previewText);
|
|
323
|
+
return _el$31;
|
|
324
|
+
})();
|
|
325
|
+
},
|
|
326
|
+
get children() {
|
|
327
|
+
return web.getNextElement(_tmpl$9);
|
|
328
|
+
}
|
|
329
|
+
}));
|
|
330
|
+
return _el$23;
|
|
331
|
+
}
|
|
332
|
+
}), _el$30, _co$6);
|
|
333
|
+
web.insert(_el$26, () => props.config.submitLabel || "Submit");
|
|
334
|
+
web.effect(() => web.setProperty(_el$26, "disabled", !isValid()));
|
|
229
335
|
return _el$22;
|
|
230
336
|
})();
|
|
231
337
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatPrompt.cjs","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'\nimport { FormFieldRenderer } from './FormFieldRenderer'\nimport type { FormFieldParams } from '../types'\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-visible\"\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 (delegates to FormFieldRenderer for all field types) ───\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, any>>({})\n\n const updateField = (name: string, value: any) => {\n setFormData((prev) => ({ ...prev, [name]: value }))\n }\n\n const handleSubmit = (e: Event) => {\n e.preventDefault()\n const data = formData()\n const label = Object.entries(data)\n .filter(([, v]) => v !== undefined && v !== '' && !(Array.isArray(v) && v.length === 0))\n .map(([k, v]) => `${k}: ${Array.isArray(v) ? v.join(', ') : 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) => {\n const val = data[f.name]\n if (Array.isArray(val)) return val.length > 0\n if (typeof val === 'boolean') return true\n return val !== undefined && val !== ''\n })\n }\n\n return (\n <form onSubmit={handleSubmit} class=\"flex flex-col gap-3\">\n <For each={props.config.fields}>\n {(field) => (\n <FormFieldRenderer\n field={field as FormFieldParams}\n value={formData()[field.name]}\n onChange={(val) => updateField(field.name, val)}\n formData={formData}\n />\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","Array","isArray","length","map","k","join","isValid","fields","f","required","every","val","_el$22","_tmpl$9","_el$25","_el$26","_co$5","_el$23","_el$24","addEventListener","field","FormFieldRenderer","onChange","submitLabel","_$setProperty","_$delegateEvents"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ChatPrompt.cjs","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, createEffect, onCleanup, Switch, Match } from 'solid-js'\nimport type {\n ChatPromptConfig,\n ChatPromptResponse,\n ChoicePromptConfig,\n ConfirmPromptConfig,\n FormPromptConfig,\n} from '../types/chat-bus'\nimport { FormFieldRenderer } from './FormFieldRenderer'\nimport type { FormFieldParams } from '../types'\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-visible\"\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 (delegates to FormFieldRenderer for all field types) ───\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, any>>({})\n const [dynamicOptions, setDynamicOptions] = createSignal<Record<string, Array<{ label: string; value: string }>>>({})\n const [previewText, setPreviewText] = createSignal<string>('')\n const [previewLoading, setPreviewLoading] = createSignal(false)\n let previewTimer: ReturnType<typeof setTimeout> | null = null\n\n const updateField = (name: string, value: any) => {\n setFormData((prev) => ({ ...prev, [name]: value }))\n }\n\n // --- depends_on: fetch child options when parent changes ---\n createEffect(() => {\n const data = formData()\n for (const field of props.config.fields || []) {\n const dep = field.dependsOn || (field as any).depends_on\n if (!dep) continue\n const parentValue = data[dep.field]\n if (!parentValue) continue\n\n const apiUrl = (dep.apiUrl || dep.api_url || '').replace('{value}', encodeURIComponent(parentValue))\n if (!apiUrl) continue\n\n const params = new URLSearchParams(dep.extraParams || dep.extra_params || {})\n fetch(`${apiUrl}?${params}`)\n .then((r) => r.json())\n .then((items) => {\n const arr = Array.isArray(items) ? items : items.results || items.features || []\n const labelKey = dep.labelField || dep.label_field || 'label'\n const valueKey = dep.valueField || dep.value_field || 'value'\n setDynamicOptions((prev) => ({\n ...prev,\n [field.name]: arr.map((item: any) => ({\n label: item[labelKey] || String(item),\n value: String(item[valueKey] || item[labelKey] || item),\n })),\n }))\n })\n .catch(() => {})\n }\n })\n\n // --- preview: debounced live preview ---\n createEffect(() => {\n const preview = props.config.preview\n if (!preview) return\n\n const data = formData()\n // Check if any preview field has a value\n const hasValues = preview.fields.some((f) => {\n const v = data[f]\n return v !== undefined && v !== '' && !(Array.isArray(v) && v.length === 0)\n })\n if (!hasValues) { setPreviewText(''); return }\n\n if (previewTimer) clearTimeout(previewTimer)\n previewTimer = setTimeout(async () => {\n setPreviewLoading(true)\n try {\n const body: Record<string, any> = {}\n for (const f of preview.fields) {\n if (data[f] !== undefined) body[f] = data[f]\n }\n const res = await fetch(preview.endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify(body),\n })\n if (res.ok) {\n const result = await res.json()\n setPreviewText(result.summary_fr || result.summary || result.message || JSON.stringify(result))\n }\n } catch {\n setPreviewText('')\n }\n setPreviewLoading(false)\n }, preview.debounceMs || 500)\n })\n\n onCleanup(() => { if (previewTimer) clearTimeout(previewTimer) })\n\n const handleSubmit = (e: Event) => {\n e.preventDefault()\n const data = formData()\n const label = Object.entries(data)\n .filter(([, v]) => v !== undefined && v !== '' && !(Array.isArray(v) && v.length === 0))\n .map(([k, v]) => `${k}: ${Array.isArray(v) ? v.join(', ') : 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) => {\n const val = data[f.name]\n if (Array.isArray(val)) return val.length > 0\n if (typeof val === 'boolean') return true\n return val !== undefined && val !== ''\n })\n }\n\n // Build field with dynamic options override\n const getField = (field: any): FormFieldParams => {\n const dynOpts = dynamicOptions()[field.name]\n if (dynOpts) {\n return { ...field, options: dynOpts } as FormFieldParams\n }\n return field as FormFieldParams\n }\n\n return (\n <form onSubmit={handleSubmit} class=\"flex flex-col gap-3\">\n <For each={props.config.fields}>\n {(field) => (\n <FormFieldRenderer\n field={getField(field)}\n value={formData()[field.name]}\n onChange={(val) => updateField(field.name, val)}\n formData={formData}\n />\n )}\n </For>\n\n {/* Live preview */}\n <Show when={previewText() || previewLoading()}>\n <div class=\"px-3 py-2 bg-blue-50 dark:bg-blue-900/20 rounded-lg border border-blue-200 dark:border-blue-800 text-sm\">\n <Show when={previewLoading()} fallback={\n <p class=\"text-blue-700 dark:text-blue-300\">{previewText()}</p>\n }>\n <p class=\"text-blue-400 animate-pulse\">Loading preview...</p>\n </Show>\n </div>\n </Show>\n\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","dynamicOptions","setDynamicOptions","previewText","setPreviewText","previewLoading","setPreviewLoading","previewTimer","updateField","name","prev","createEffect","field","fields","dep","dependsOn","depends_on","parentValue","apiUrl","api_url","replace","encodeURIComponent","params","URLSearchParams","extraParams","extra_params","fetch","then","r","json","items","arr","Array","isArray","results","features","labelKey","labelField","label_field","valueKey","valueField","value_field","map","item","String","catch","preview","hasValues","some","f","v","length","setTimeout","body","res","endpoint","method","headers","credentials","JSON","stringify","ok","result","summary_fr","summary","debounceMs","onCleanup","handleSubmit","preventDefault","Object","entries","filter","k","join","isValid","required","every","val","getField","dynOpts","_el$22","_tmpl$1","_el$27","_el$28","_co$5","_el$29","_el$30","_co$6","_el$25","_el$26","addEventListener","FormFieldRenderer","onChange","_el$23","_tmpl$0","_el$31","_tmpl$10","_tmpl$9","submitLabel","_$setProperty","_$delegateEvents"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatPrompt.d.ts","sourceRoot":"","sources":["../../src/components/ChatPrompt.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"ChatPrompt.d.ts","sourceRoot":"","sources":["../../src/components/ChatPrompt.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAAmE,MAAM,UAAU,CAAA;AACrG,OAAO,KAAK,EACV,gBAAgB,EAChB,kBAAkB,EAInB,MAAM,mBAAmB,CAAA;AAI1B,MAAM,WAAW,eAAe;IAC9B,2BAA2B;IAC3B,MAAM,EAAE,gBAAgB,CAAA;IACxB,gCAAgC;IAChC,QAAQ,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAChD,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,6EAA6E;IAC7E,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,UAAU,EAAE,SAAS,CAAC,eAAe,CAuEjD,CAAA"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
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";
|
|
2
|
+
import { Show, Switch, Match, For, createSignal, createEffect, onCleanup } from "solid-js";
|
|
3
3
|
import { FormFieldRenderer } from "./FormFieldRenderer.js";
|
|
4
4
|
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-visible"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>
|
|
5
5
|
@keyframes chat-prompt-slide-up {
|
|
6
6
|
from { opacity: 0; transform: translateY(8px); }
|
|
7
7
|
to { opacity: 1; transform: translateY(0); }
|
|
8
8
|
}
|
|
9
|
-
`), _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"
|
|
9
|
+
`), _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(`<p class="text-blue-400 animate-pulse">Loading preview...`), _tmpl$0 = /* @__PURE__ */ template(`<div class="px-3 py-2 bg-blue-50 dark:bg-blue-900/20 rounded-lg border border-blue-200 dark:border-blue-800 text-sm">`), _tmpl$1 = /* @__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$10 = /* @__PURE__ */ template(`<p class="text-blue-700 dark:text-blue-300">`);
|
|
10
10
|
const ChatPrompt = (props) => {
|
|
11
11
|
if (!props.config) return null;
|
|
12
12
|
return (() => {
|
|
@@ -185,12 +185,82 @@ const ConfirmBody = (props) => {
|
|
|
185
185
|
};
|
|
186
186
|
const FormBody = (props) => {
|
|
187
187
|
const [formData, setFormData] = createSignal({});
|
|
188
|
+
const [dynamicOptions, setDynamicOptions] = createSignal({});
|
|
189
|
+
const [previewText, setPreviewText] = createSignal("");
|
|
190
|
+
const [previewLoading, setPreviewLoading] = createSignal(false);
|
|
191
|
+
let previewTimer = null;
|
|
188
192
|
const updateField = (name, value) => {
|
|
189
193
|
setFormData((prev) => ({
|
|
190
194
|
...prev,
|
|
191
195
|
[name]: value
|
|
192
196
|
}));
|
|
193
197
|
};
|
|
198
|
+
createEffect(() => {
|
|
199
|
+
const data = formData();
|
|
200
|
+
for (const field of props.config.fields || []) {
|
|
201
|
+
const dep = field.dependsOn || field.depends_on;
|
|
202
|
+
if (!dep) continue;
|
|
203
|
+
const parentValue = data[dep.field];
|
|
204
|
+
if (!parentValue) continue;
|
|
205
|
+
const apiUrl = (dep.apiUrl || dep.api_url || "").replace("{value}", encodeURIComponent(parentValue));
|
|
206
|
+
if (!apiUrl) continue;
|
|
207
|
+
const params = new URLSearchParams(dep.extraParams || dep.extra_params || {});
|
|
208
|
+
fetch(`${apiUrl}?${params}`).then((r) => r.json()).then((items) => {
|
|
209
|
+
const arr = Array.isArray(items) ? items : items.results || items.features || [];
|
|
210
|
+
const labelKey = dep.labelField || dep.label_field || "label";
|
|
211
|
+
const valueKey = dep.valueField || dep.value_field || "value";
|
|
212
|
+
setDynamicOptions((prev) => ({
|
|
213
|
+
...prev,
|
|
214
|
+
[field.name]: arr.map((item) => ({
|
|
215
|
+
label: item[labelKey] || String(item),
|
|
216
|
+
value: String(item[valueKey] || item[labelKey] || item)
|
|
217
|
+
}))
|
|
218
|
+
}));
|
|
219
|
+
}).catch(() => {
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
createEffect(() => {
|
|
224
|
+
const preview = props.config.preview;
|
|
225
|
+
if (!preview) return;
|
|
226
|
+
const data = formData();
|
|
227
|
+
const hasValues = preview.fields.some((f) => {
|
|
228
|
+
const v = data[f];
|
|
229
|
+
return v !== void 0 && v !== "" && !(Array.isArray(v) && v.length === 0);
|
|
230
|
+
});
|
|
231
|
+
if (!hasValues) {
|
|
232
|
+
setPreviewText("");
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
if (previewTimer) clearTimeout(previewTimer);
|
|
236
|
+
previewTimer = setTimeout(async () => {
|
|
237
|
+
setPreviewLoading(true);
|
|
238
|
+
try {
|
|
239
|
+
const body = {};
|
|
240
|
+
for (const f of preview.fields) {
|
|
241
|
+
if (data[f] !== void 0) body[f] = data[f];
|
|
242
|
+
}
|
|
243
|
+
const res = await fetch(preview.endpoint, {
|
|
244
|
+
method: "POST",
|
|
245
|
+
headers: {
|
|
246
|
+
"Content-Type": "application/json"
|
|
247
|
+
},
|
|
248
|
+
credentials: "include",
|
|
249
|
+
body: JSON.stringify(body)
|
|
250
|
+
});
|
|
251
|
+
if (res.ok) {
|
|
252
|
+
const result = await res.json();
|
|
253
|
+
setPreviewText(result.summary_fr || result.summary || result.message || JSON.stringify(result));
|
|
254
|
+
}
|
|
255
|
+
} catch {
|
|
256
|
+
setPreviewText("");
|
|
257
|
+
}
|
|
258
|
+
setPreviewLoading(false);
|
|
259
|
+
}, preview.debounceMs || 500);
|
|
260
|
+
});
|
|
261
|
+
onCleanup(() => {
|
|
262
|
+
if (previewTimer) clearTimeout(previewTimer);
|
|
263
|
+
});
|
|
194
264
|
const handleSubmit = (e) => {
|
|
195
265
|
e.preventDefault();
|
|
196
266
|
const data = formData();
|
|
@@ -206,24 +276,60 @@ const FormBody = (props) => {
|
|
|
206
276
|
return val !== void 0 && val !== "";
|
|
207
277
|
});
|
|
208
278
|
};
|
|
279
|
+
const getField = (field) => {
|
|
280
|
+
const dynOpts = dynamicOptions()[field.name];
|
|
281
|
+
if (dynOpts) {
|
|
282
|
+
return {
|
|
283
|
+
...field,
|
|
284
|
+
options: dynOpts
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
return field;
|
|
288
|
+
};
|
|
209
289
|
return (() => {
|
|
210
|
-
var _el$22 = getNextElement(_tmpl$
|
|
290
|
+
var _el$22 = getNextElement(_tmpl$1), _el$27 = _el$22.firstChild, [_el$28, _co$5] = getNextMarker(_el$27.nextSibling), _el$29 = _el$28.nextSibling, [_el$30, _co$6] = getNextMarker(_el$29.nextSibling), _el$25 = _el$30.nextSibling, _el$26 = _el$25.firstChild;
|
|
211
291
|
_el$22.addEventListener("submit", handleSubmit);
|
|
212
292
|
insert(_el$22, createComponent(For, {
|
|
213
293
|
get each() {
|
|
214
294
|
return props.config.fields;
|
|
215
295
|
},
|
|
216
296
|
children: (field) => createComponent(FormFieldRenderer, {
|
|
217
|
-
field
|
|
297
|
+
get field() {
|
|
298
|
+
return getField(field);
|
|
299
|
+
},
|
|
218
300
|
get value() {
|
|
219
301
|
return formData()[field.name];
|
|
220
302
|
},
|
|
221
303
|
onChange: (val) => updateField(field.name, val),
|
|
222
304
|
formData
|
|
223
305
|
})
|
|
224
|
-
}), _el$
|
|
225
|
-
insert(_el$
|
|
226
|
-
|
|
306
|
+
}), _el$28, _co$5);
|
|
307
|
+
insert(_el$22, createComponent(Show, {
|
|
308
|
+
get when() {
|
|
309
|
+
return previewText() || previewLoading();
|
|
310
|
+
},
|
|
311
|
+
get children() {
|
|
312
|
+
var _el$23 = getNextElement(_tmpl$0);
|
|
313
|
+
insert(_el$23, createComponent(Show, {
|
|
314
|
+
get when() {
|
|
315
|
+
return previewLoading();
|
|
316
|
+
},
|
|
317
|
+
get fallback() {
|
|
318
|
+
return (() => {
|
|
319
|
+
var _el$31 = getNextElement(_tmpl$10);
|
|
320
|
+
insert(_el$31, previewText);
|
|
321
|
+
return _el$31;
|
|
322
|
+
})();
|
|
323
|
+
},
|
|
324
|
+
get children() {
|
|
325
|
+
return getNextElement(_tmpl$9);
|
|
326
|
+
}
|
|
327
|
+
}));
|
|
328
|
+
return _el$23;
|
|
329
|
+
}
|
|
330
|
+
}), _el$30, _co$6);
|
|
331
|
+
insert(_el$26, () => props.config.submitLabel || "Submit");
|
|
332
|
+
effect(() => setProperty(_el$26, "disabled", !isValid()));
|
|
227
333
|
return _el$22;
|
|
228
334
|
})();
|
|
229
335
|
};
|
|
@@ -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'\nimport { FormFieldRenderer } from './FormFieldRenderer'\nimport type { FormFieldParams } from '../types'\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-visible\"\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 (delegates to FormFieldRenderer for all field types) ───\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, any>>({})\n\n const updateField = (name: string, value: any) => {\n setFormData((prev) => ({ ...prev, [name]: value }))\n }\n\n const handleSubmit = (e: Event) => {\n e.preventDefault()\n const data = formData()\n const label = Object.entries(data)\n .filter(([, v]) => v !== undefined && v !== '' && !(Array.isArray(v) && v.length === 0))\n .map(([k, v]) => `${k}: ${Array.isArray(v) ? v.join(', ') : 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) => {\n const val = data[f.name]\n if (Array.isArray(val)) return val.length > 0\n if (typeof val === 'boolean') return true\n return val !== undefined && val !== ''\n })\n }\n\n return (\n <form onSubmit={handleSubmit} class=\"flex flex-col gap-3\">\n <For each={props.config.fields}>\n {(field) => (\n <FormFieldRenderer\n field={field as FormFieldParams}\n value={formData()[field.name]}\n onChange={(val) => updateField(field.name, val)}\n formData={formData}\n />\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","Array","isArray","length","map","k","join","isValid","fields","f","required","every","val","_el$22","_tmpl$9","_el$25","_el$26","_co$5","_el$23","_el$24","addEventListener","field","FormFieldRenderer","onChange","submitLabel","_$setProperty","_$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, createEffect, onCleanup, Switch, Match } from 'solid-js'\nimport type {\n ChatPromptConfig,\n ChatPromptResponse,\n ChoicePromptConfig,\n ConfirmPromptConfig,\n FormPromptConfig,\n} from '../types/chat-bus'\nimport { FormFieldRenderer } from './FormFieldRenderer'\nimport type { FormFieldParams } from '../types'\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-visible\"\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 (delegates to FormFieldRenderer for all field types) ───\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, any>>({})\n const [dynamicOptions, setDynamicOptions] = createSignal<Record<string, Array<{ label: string; value: string }>>>({})\n const [previewText, setPreviewText] = createSignal<string>('')\n const [previewLoading, setPreviewLoading] = createSignal(false)\n let previewTimer: ReturnType<typeof setTimeout> | null = null\n\n const updateField = (name: string, value: any) => {\n setFormData((prev) => ({ ...prev, [name]: value }))\n }\n\n // --- depends_on: fetch child options when parent changes ---\n createEffect(() => {\n const data = formData()\n for (const field of props.config.fields || []) {\n const dep = field.dependsOn || (field as any).depends_on\n if (!dep) continue\n const parentValue = data[dep.field]\n if (!parentValue) continue\n\n const apiUrl = (dep.apiUrl || dep.api_url || '').replace('{value}', encodeURIComponent(parentValue))\n if (!apiUrl) continue\n\n const params = new URLSearchParams(dep.extraParams || dep.extra_params || {})\n fetch(`${apiUrl}?${params}`)\n .then((r) => r.json())\n .then((items) => {\n const arr = Array.isArray(items) ? items : items.results || items.features || []\n const labelKey = dep.labelField || dep.label_field || 'label'\n const valueKey = dep.valueField || dep.value_field || 'value'\n setDynamicOptions((prev) => ({\n ...prev,\n [field.name]: arr.map((item: any) => ({\n label: item[labelKey] || String(item),\n value: String(item[valueKey] || item[labelKey] || item),\n })),\n }))\n })\n .catch(() => {})\n }\n })\n\n // --- preview: debounced live preview ---\n createEffect(() => {\n const preview = props.config.preview\n if (!preview) return\n\n const data = formData()\n // Check if any preview field has a value\n const hasValues = preview.fields.some((f) => {\n const v = data[f]\n return v !== undefined && v !== '' && !(Array.isArray(v) && v.length === 0)\n })\n if (!hasValues) { setPreviewText(''); return }\n\n if (previewTimer) clearTimeout(previewTimer)\n previewTimer = setTimeout(async () => {\n setPreviewLoading(true)\n try {\n const body: Record<string, any> = {}\n for (const f of preview.fields) {\n if (data[f] !== undefined) body[f] = data[f]\n }\n const res = await fetch(preview.endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify(body),\n })\n if (res.ok) {\n const result = await res.json()\n setPreviewText(result.summary_fr || result.summary || result.message || JSON.stringify(result))\n }\n } catch {\n setPreviewText('')\n }\n setPreviewLoading(false)\n }, preview.debounceMs || 500)\n })\n\n onCleanup(() => { if (previewTimer) clearTimeout(previewTimer) })\n\n const handleSubmit = (e: Event) => {\n e.preventDefault()\n const data = formData()\n const label = Object.entries(data)\n .filter(([, v]) => v !== undefined && v !== '' && !(Array.isArray(v) && v.length === 0))\n .map(([k, v]) => `${k}: ${Array.isArray(v) ? v.join(', ') : 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) => {\n const val = data[f.name]\n if (Array.isArray(val)) return val.length > 0\n if (typeof val === 'boolean') return true\n return val !== undefined && val !== ''\n })\n }\n\n // Build field with dynamic options override\n const getField = (field: any): FormFieldParams => {\n const dynOpts = dynamicOptions()[field.name]\n if (dynOpts) {\n return { ...field, options: dynOpts } as FormFieldParams\n }\n return field as FormFieldParams\n }\n\n return (\n <form onSubmit={handleSubmit} class=\"flex flex-col gap-3\">\n <For each={props.config.fields}>\n {(field) => (\n <FormFieldRenderer\n field={getField(field)}\n value={formData()[field.name]}\n onChange={(val) => updateField(field.name, val)}\n formData={formData}\n />\n )}\n </For>\n\n {/* Live preview */}\n <Show when={previewText() || previewLoading()}>\n <div class=\"px-3 py-2 bg-blue-50 dark:bg-blue-900/20 rounded-lg border border-blue-200 dark:border-blue-800 text-sm\">\n <Show when={previewLoading()} fallback={\n <p class=\"text-blue-700 dark:text-blue-300\">{previewText()}</p>\n }>\n <p class=\"text-blue-400 animate-pulse\">Loading preview...</p>\n </Show>\n </div>\n </Show>\n\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","dynamicOptions","setDynamicOptions","previewText","setPreviewText","previewLoading","setPreviewLoading","previewTimer","updateField","name","prev","createEffect","field","fields","dep","dependsOn","depends_on","parentValue","apiUrl","api_url","replace","encodeURIComponent","params","URLSearchParams","extraParams","extra_params","fetch","then","r","json","items","arr","Array","isArray","results","features","labelKey","labelField","label_field","valueKey","valueField","value_field","map","item","String","catch","preview","hasValues","some","f","v","length","setTimeout","body","res","endpoint","method","headers","credentials","JSON","stringify","ok","result","summary_fr","summary","debounceMs","onCleanup","handleSubmit","preventDefault","Object","entries","filter","k","join","isValid","required","every","val","getField","dynOpts","_el$22","_tmpl$1","_el$27","_el$28","_co$5","_el$29","_el$30","_co$6","_el$25","_el$26","addEventListener","FormFieldRenderer","onChange","_el$23","_tmpl$0","_el$31","_tmpl$10","_tmpl$9","submitLabel","_$setProperty","_$delegateEvents"],"mappings":"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"}
|
package/dist/types/chat-bus.d.ts
CHANGED
|
@@ -196,8 +196,33 @@ export interface FormPromptConfig {
|
|
|
196
196
|
checkboxLabel?: string;
|
|
197
197
|
/** Help text below field */
|
|
198
198
|
helpText?: string;
|
|
199
|
+
/** Dependent field — update options when parent field changes */
|
|
200
|
+
dependsOn?: {
|
|
201
|
+
field: string;
|
|
202
|
+
apiUrl: string;
|
|
203
|
+
labelField: string;
|
|
204
|
+
valueField: string;
|
|
205
|
+
extraParams?: Record<string, string>;
|
|
206
|
+
};
|
|
207
|
+
/** Conditional visibility (show/hide based on another field value) */
|
|
208
|
+
showWhen?: {
|
|
209
|
+
field: string;
|
|
210
|
+
operator: 'equals' | 'not_equals' | 'in';
|
|
211
|
+
value: any;
|
|
212
|
+
};
|
|
199
213
|
}>;
|
|
200
214
|
submitLabel?: string;
|
|
215
|
+
/** Live preview configuration — shows stats as user fills the form */
|
|
216
|
+
preview?: {
|
|
217
|
+
/** API endpoint to call for preview */
|
|
218
|
+
endpoint: string;
|
|
219
|
+
/** Debounce delay in ms (default: 500) */
|
|
220
|
+
debounceMs?: number;
|
|
221
|
+
/** Fields to include in the preview request */
|
|
222
|
+
fields: string[];
|
|
223
|
+
/** Display format */
|
|
224
|
+
format?: 'text' | 'stats';
|
|
225
|
+
};
|
|
201
226
|
}
|
|
202
227
|
export interface SelectPromptConfig {
|
|
203
228
|
options: Array<{
|