@seed-ship/mcp-ui-solid 4.3.9 → 5.1.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/CHANGELOG.md +46 -0
- package/README.md +124 -6
- package/dist/components/ChatPrompt.cjs +71 -53
- package/dist/components/ChatPrompt.cjs.map +1 -1
- package/dist/components/ChatPrompt.d.ts +37 -2
- package/dist/components/ChatPrompt.d.ts.map +1 -1
- package/dist/components/ChatPrompt.js +72 -54
- package/dist/components/ChatPrompt.js.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/services/chat-bus.cjs +3 -2
- package/dist/services/chat-bus.cjs.map +1 -1
- package/dist/services/chat-bus.d.ts +3 -2
- package/dist/services/chat-bus.d.ts.map +1 -1
- package/dist/services/chat-bus.js +3 -2
- package/dist/services/chat-bus.js.map +1 -1
- package/dist/types/chat-bus.d.ts +125 -24
- package/dist/types/chat-bus.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/ChatPrompt.test.tsx +122 -0
- package/src/components/ChatPrompt.tsx +70 -15
- package/src/index.ts +1 -0
- package/src/services/chat-bus.test.ts +10 -8
- package/src/services/chat-bus.ts +7 -4
- package/src/types/chat-bus.ts +126 -24
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,52 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [5.1.0] - 2026-04-14
|
|
9
|
+
|
|
10
|
+
### Added — D4 custom choice rendering
|
|
11
|
+
|
|
12
|
+
- **`ChoicePromptConfig.optionRenderer?: (option, index) => JSX.Element`** — render prop for custom option bodies (confidence badges, rich metadata layouts, etc.). mcp-ui still wraps the returned JSX in the `<button>` with the `onClick` handler, keyboard support, and focus styles — only the *content* is yours.
|
|
13
|
+
- **`ChoicePromptConfig.buttonClass?: string`** — escape hatch appended to each option button's Tailwind classes for colour/border tweaks without a full `optionRenderer`.
|
|
14
|
+
- **`ChoicePromptConfig.containerClass?: string`** — escape hatch appended to the options wrapper's layout class.
|
|
15
|
+
- **Generic `ChoicePromptConfig<TMeta = Record<string, unknown>>`** — type parameter flows to `ChoiceOption<TMeta>` so consumers get strongly-typed `option.metadata` in their `optionRenderer` closure without casting. Default backward-compatible.
|
|
16
|
+
- **`ChoiceOption<TMeta>` type exported from the root package** — reusable shape for consumers building their own renderers or helpers.
|
|
17
|
+
- **Option buttons now have `type="button"`** — prevents accidental form submission when a `ChatPrompt` is nested inside an HTML `<form>`.
|
|
18
|
+
|
|
19
|
+
### Documented — D3 AbortSignal + re-entrance contract
|
|
20
|
+
|
|
21
|
+
- **`ChatPrompt.tsx` header JSDoc rewritten** — the v4.x doc claimed "Supports AbortSignal for cleanup on navigation" but the component never listened to any signal. v5.1.0 doc explicitly states that `ChatPrompt` is a pure presentation component and lifecycle (including abort) is the consumer's responsibility.
|
|
22
|
+
- **`ChatCommands.showChatPrompt` JSDoc rewritten** — documents the full implementer contract: no default handler, Promise wiring on `onSubmit`/`onDismiss`, `DOMException('AbortError')` rejection on `signal.aborted`, re-entrance auto-reject policy. Points at v5.2.0 `createChatPromptController()` as the future primitive.
|
|
23
|
+
- **README section `ChatPromptResponse — dismissed / aborted / answered`** — rewritten with a full reference wiring example covering re-entrance, `AbortSignal`, and the `DOMException('AbortError')` Web Platform convention. Consumer-side error branching example (`err.name === 'AbortError'`).
|
|
24
|
+
|
|
25
|
+
### Tests
|
|
26
|
+
- **438 passing** (+5 vs v5.0.0). New coverage in `ChatPrompt.test.tsx` for: default rendering unchanged when `optionRenderer` absent, custom renderer receives `option + index` + metadata, custom renderer button still wires `onClick`, `buttonClass` appended without dropping defaults, `containerClass` appended to wrapper, option buttons have `type="button"`.
|
|
27
|
+
|
|
28
|
+
### Non-breaking
|
|
29
|
+
- All additions are optional — existing consumers using `ChoicePromptConfig` as a plain interface with no generic parameter or new fields keep working identically.
|
|
30
|
+
|
|
31
|
+
### Deferred to v5.2.0 (unchanged)
|
|
32
|
+
- `createScratchpadStore()` factory for multi-instance scratchpad panels (D1).
|
|
33
|
+
- `createChatPromptController()` primitive centralising resolver lifecycle + re-entrance + abort (D2 + D3 code).
|
|
34
|
+
- `correlationId` natively threaded through `ChatPromptConfig` → `ChatPromptResponse`.
|
|
35
|
+
- Optional `progress_update` SSE event type for long-running agent pipelines.
|
|
36
|
+
|
|
37
|
+
See `/home/nico/code_source/tss/deposium_fullstack/docs/2026/r&d/mcpui-v5.1.0-consensus.md` for the full design discussion and the v5.1.0/v5.2.0 sequencing arbitration.
|
|
38
|
+
|
|
39
|
+
## [5.0.0] - 2026-04-14
|
|
40
|
+
|
|
41
|
+
### Major release — synchronized with `@seed-ship/mcp-ui-spec` 5.0.0 and `@seed-ship/mcp-ui-cli` 5.0.0
|
|
42
|
+
|
|
43
|
+
### Breaking
|
|
44
|
+
- **`ClarificationEvent.options[].file_id` removed from the TypeScript type** (deprecated in v4.3.9, removed in v5.0.0 as announced). The `clarificationToPromptConfig()` helper still migrates runtime `file_id` into `metadata.file_id` transparently, so host apps receiving payloads from older servers continue to work without upgrade pressure. New code should emit `metadata: { file_id }` directly.
|
|
45
|
+
- **`ChatPromptConfig.type = 'select'` and `SelectPromptConfig`** — already removed in 4.3.9 (the variant was declared in 4.0 but `ChatPrompt.tsx` never rendered it). Listed here for the v5 breaking recap.
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
- Version bump 4.3.9 → 5.0.0 for the synchronized monorepo major release.
|
|
49
|
+
- Root `README.md` + monorepo `CHANGELOG.md` consolidated with the full 4.x → 5.0.0 history.
|
|
50
|
+
|
|
51
|
+
### Documented
|
|
52
|
+
- v5.1.0 scope: `createScratchpadStore()` factory (multi-instance scratchpad), re-entrant `showChatPrompt` (auto-reject / FIFO queue), `ChatPrompt` AbortSignal wiring, `optionRenderer?` slot on `ChoicePromptConfig`.
|
|
53
|
+
|
|
8
54
|
## [4.3.9] - 2026-04-14
|
|
9
55
|
|
|
10
56
|
### Added — Sprint 52 multi-agent primitives
|
package/README.md
CHANGED
|
@@ -5,6 +5,29 @@ SolidJS components + chat toolkit for MCP-generated UI. Part of the [MCP UI ecos
|
|
|
5
5
|
[](https://www.npmjs.com/package/@seed-ship/mcp-ui-solid)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
|
|
8
|
+
## What's New in v5.1.0 (`mcp-ui-solid` only)
|
|
9
|
+
|
|
10
|
+
- **`optionRenderer` render prop** on `ChoicePromptConfig` — take full control of option bodies (confidence badges, rich layouts). mcp-ui still wraps the returned JSX in its own `<button>` with `onClick` + focus handling. See `optionRenderer (v5.1.0)` tests in `ChatPrompt.test.tsx` for usage.
|
|
11
|
+
- **Generic `ChoicePromptConfig<TMeta>`** — `ChoiceOption<TMeta>` flows through so your renderer closures get strongly-typed `option.metadata` without casting. Default `TMeta = Record<string, unknown>` keeps the non-generic shape valid for existing callers.
|
|
12
|
+
- **`buttonClass?` + `containerClass?`** escape hatches on `ChoicePromptConfig` — Tailwind class extensions that append to mcp-ui's defaults for light cosmetic tweaks without writing a full renderer.
|
|
13
|
+
- **`type="button"` on option buttons** — prevents accidental form submission when a `ChatPrompt` is nested inside an HTML `<form>`.
|
|
14
|
+
- **`ChatPrompt` + `showChatPrompt` JSDoc rewritten** — explicitly states the consumer contract : no default handler, Promise wiring is host-side, `AbortSignal` rejects with `DOMException('AbortError')` per Web Platform convention, re-entrance policy is host-enforced. Full reference wiring example in the README below. A `createChatPromptController()` primitive landing in v5.2.0 will bundle all of this.
|
|
15
|
+
|
|
16
|
+
## What's New in v5.0.0
|
|
17
|
+
|
|
18
|
+
Synchronized major release — `@seed-ship/mcp-ui-solid`, `@seed-ship/mcp-ui-spec`, and `@seed-ship/mcp-ui-cli` all move to 5.0.0.
|
|
19
|
+
|
|
20
|
+
**Sprint 52 multi-agent primitives** (new)
|
|
21
|
+
- `ChoicePromptConfig.options[].metadata?` — opaque metadata preserved through the `showChatPrompt` roundtrip (confidence, source tags, etc.).
|
|
22
|
+
- `clarificationToPromptConfig()` — universal `ClarificationEvent → ChatPromptConfig` bridge. Legacy runtime `file_id` auto-migrated into `metadata.file_id`.
|
|
23
|
+
- `createMockChatBus()` — new `src/testing/` entry point with FIFO prompt responses and spy hooks. Test agent flows without rendering any UI.
|
|
24
|
+
|
|
25
|
+
**Breaking**
|
|
26
|
+
- `ClarificationEvent.options[].file_id` removed from the TypeScript type (was deprecated in v4.3.9). Runtime fallback still works via `clarificationToPromptConfig()`.
|
|
27
|
+
- `ChatPromptConfig.type = 'select'` / `SelectPromptConfig` removed (dead code — never had a rendering branch).
|
|
28
|
+
|
|
29
|
+
Everything rolled up from the 4.x series is documented in the previous section below.
|
|
30
|
+
|
|
8
31
|
## What's New in v4.3
|
|
9
32
|
|
|
10
33
|
- **Prefilled Forms** — Fields render with pre-populated values + source indicators (detected/inferred/default/user)
|
|
@@ -384,19 +407,114 @@ Legacy `option.file_id` is automatically migrated into `metadata.file_id`.
|
|
|
384
407
|
Arbitrary `metadata` (confidence scores, source tags, ...) flows through
|
|
385
408
|
unchanged and can be rendered by a custom `ChoiceBody` wrapper.
|
|
386
409
|
|
|
387
|
-
### ChatPromptResponse — dismissed / aborted / answered
|
|
410
|
+
### ChatPromptResponse — dismissed / aborted / answered
|
|
388
411
|
|
|
389
|
-
Every `ChatPrompt`
|
|
412
|
+
Every `ChatPrompt` exchange ends in one of three outcomes:
|
|
390
413
|
|
|
391
414
|
| Outcome | How | `response.dismissed` | Promise |
|
|
392
415
|
|---------|-----|----------------------|---------|
|
|
393
416
|
| Explicit answer | Click a choice / submit a form | `undefined` | resolves |
|
|
394
417
|
| Dismissed | Click the X icon, click Cancel (confirm type) | `true` | resolves |
|
|
395
|
-
| Aborted | Host app rejects the Promise via `AbortSignal` | *(never resolves)* | rejects |
|
|
418
|
+
| Aborted | Host app rejects the Promise via `AbortSignal` | *(n/a — never resolves)* | rejects with `DOMException('AbortError')` |
|
|
419
|
+
|
|
420
|
+
> **v5.1.0 note** — `showChatPrompt` has **no default handler** in mcp-ui.
|
|
421
|
+
> The command name is declared on the bus, but every consumer wires its own
|
|
422
|
+
> handler. The handler is responsible for threading the Promise resolver
|
|
423
|
+
> through the SolidJS lifecycle AND for honouring the optional `AbortSignal`.
|
|
424
|
+
> A `createChatPromptController()` primitive that bundles all of this will
|
|
425
|
+
> land in v5.2.0 — see the v5.1.0 design doc.
|
|
426
|
+
|
|
427
|
+
#### Wiring a handler yourself (v5.1.0)
|
|
428
|
+
|
|
429
|
+
Until the v5.2.0 controller ships, here is the reference pattern every
|
|
430
|
+
consumer should implement. It honours re-entrance (auto-reject the previous
|
|
431
|
+
prompt) and `AbortSignal` (rejects with a Web-Platform `DOMException`):
|
|
432
|
+
|
|
433
|
+
```tsx
|
|
434
|
+
import { createSignal } from 'solid-js'
|
|
435
|
+
import { useChatBus } from '@seed-ship/mcp-ui-solid'
|
|
436
|
+
import type { ChatPromptConfig, ChatPromptResponse } from '@seed-ship/mcp-ui-solid'
|
|
437
|
+
|
|
438
|
+
function HitlHost() {
|
|
439
|
+
const bus = useChatBus()
|
|
440
|
+
const [activePrompt, setActivePrompt] = createSignal<ChatPromptConfig | null>(null)
|
|
441
|
+
|
|
442
|
+
// Mutable resolver + optional abort cleanup — one prompt at a time
|
|
443
|
+
let active: {
|
|
444
|
+
resolve: (response: ChatPromptResponse) => void
|
|
445
|
+
reject: (err: unknown) => void
|
|
446
|
+
cleanupAbort?: () => void
|
|
447
|
+
} | null = null
|
|
448
|
+
|
|
449
|
+
bus.commands.handle('showChatPrompt', (config, signal) => {
|
|
450
|
+
// Re-entrance: auto-reject any previous in-flight prompt
|
|
451
|
+
if (active) {
|
|
452
|
+
const stale = active
|
|
453
|
+
active = null
|
|
454
|
+
stale.reject(new Error('PromptReplaced'))
|
|
455
|
+
stale.cleanupAbort?.()
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Already-aborted signal → reject synchronously, never render
|
|
459
|
+
if (signal?.aborted) {
|
|
460
|
+
return Promise.reject(new DOMException('Prompt aborted', 'AbortError'))
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
return new Promise<ChatPromptResponse>((resolve, reject) => {
|
|
464
|
+
const onAbort = () => {
|
|
465
|
+
setActivePrompt(null)
|
|
466
|
+
active = null
|
|
467
|
+
reject(new DOMException('Prompt aborted', 'AbortError'))
|
|
468
|
+
}
|
|
469
|
+
signal?.addEventListener('abort', onAbort, { once: true })
|
|
470
|
+
|
|
471
|
+
active = {
|
|
472
|
+
resolve,
|
|
473
|
+
reject,
|
|
474
|
+
cleanupAbort: () => signal?.removeEventListener('abort', onAbort),
|
|
475
|
+
}
|
|
476
|
+
setActivePrompt(config)
|
|
477
|
+
})
|
|
478
|
+
})
|
|
479
|
+
|
|
480
|
+
const handleSubmit = (response: ChatPromptResponse) => {
|
|
481
|
+
const a = active
|
|
482
|
+
active = null
|
|
483
|
+
setActivePrompt(null)
|
|
484
|
+
a?.cleanupAbort?.()
|
|
485
|
+
a?.resolve(response)
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const handleDismiss = () => {
|
|
489
|
+
const a = active
|
|
490
|
+
active = null
|
|
491
|
+
setActivePrompt(null)
|
|
492
|
+
a?.cleanupAbort?.()
|
|
493
|
+
a?.resolve({ type: 'choice', value: '', label: '', dismissed: true })
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
return (
|
|
497
|
+
<Show when={activePrompt()}>
|
|
498
|
+
<ChatPrompt config={activePrompt()!} onSubmit={handleSubmit} onDismiss={handleDismiss} />
|
|
499
|
+
</Show>
|
|
500
|
+
)
|
|
501
|
+
}
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
Consumer-side the error is standard and branch-able without any mcp-ui import:
|
|
505
|
+
|
|
506
|
+
```ts
|
|
507
|
+
try {
|
|
508
|
+
const response = await bus.commands.exec('showChatPrompt', config, ctrl.signal)
|
|
509
|
+
// ...
|
|
510
|
+
} catch (err) {
|
|
511
|
+
if (err instanceof Error && err.name === 'AbortError') return // navigation killed it
|
|
512
|
+
throw err
|
|
513
|
+
}
|
|
514
|
+
```
|
|
396
515
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
> to `Promise.reject`. A built-in helper lands in v4.4.0.
|
|
516
|
+
Once `createChatPromptController()` ships in v5.2.0 this boilerplate
|
|
517
|
+
collapses to `bus.commands.handle('showChatPrompt', ctrl.handle)`.
|
|
400
518
|
|
|
401
519
|
### correlationId — host-propagated (v4.3.9)
|
|
402
520
|
|
|
@@ -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(`<
|
|
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(`<button type=button>`), _tmpl$5 = /* @__PURE__ */ web.template(`<span class=mr-2>`), _tmpl$6 = /* @__PURE__ */ web.template(`<span class="block text-xs text-gray-500 dark:text-gray-400 mt-0.5 font-normal">`), _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 (() => {
|
|
@@ -115,14 +115,23 @@ const ChatPrompt = (props) => {
|
|
|
115
115
|
};
|
|
116
116
|
const ChoiceBody = (props) => {
|
|
117
117
|
const layoutClass = () => {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
118
|
+
const base = (() => {
|
|
119
|
+
switch (props.config.layout) {
|
|
120
|
+
case "vertical":
|
|
121
|
+
return "flex flex-col gap-2";
|
|
122
|
+
case "grid":
|
|
123
|
+
return "grid grid-cols-2 gap-2";
|
|
124
|
+
default:
|
|
125
|
+
return "flex flex-wrap gap-2";
|
|
126
|
+
}
|
|
127
|
+
})();
|
|
128
|
+
const extra = props.config.containerClass;
|
|
129
|
+
return extra ? `${base} ${extra}` : base;
|
|
130
|
+
};
|
|
131
|
+
const buttonClass = () => {
|
|
132
|
+
const base = "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";
|
|
133
|
+
const extra = props.config.buttonClass;
|
|
134
|
+
return extra ? `${base} ${extra}` : base;
|
|
126
135
|
};
|
|
127
136
|
return (() => {
|
|
128
137
|
var _el$7 = web.getNextElement(_tmpl$3);
|
|
@@ -130,30 +139,39 @@ const ChoiceBody = (props) => {
|
|
|
130
139
|
get each() {
|
|
131
140
|
return props.config.options;
|
|
132
141
|
},
|
|
133
|
-
children: (option) => (() => {
|
|
134
|
-
var _el$8 = web.getNextElement(_tmpl$
|
|
142
|
+
children: (option, i) => (() => {
|
|
143
|
+
var _el$8 = web.getNextElement(_tmpl$4);
|
|
135
144
|
_el$8.$$click = () => props.onSelect(option.value, option.label);
|
|
136
145
|
web.insert(_el$8, web.createComponent(solidJs.Show, {
|
|
137
146
|
get when() {
|
|
138
|
-
return
|
|
147
|
+
return props.config.optionRenderer;
|
|
139
148
|
},
|
|
140
|
-
get
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
149
|
+
get fallback() {
|
|
150
|
+
return [web.createComponent(solidJs.Show, {
|
|
151
|
+
get when() {
|
|
152
|
+
return option.icon;
|
|
153
|
+
},
|
|
154
|
+
get children() {
|
|
155
|
+
var _el$9 = web.getNextElement(_tmpl$5);
|
|
156
|
+
web.insert(_el$9, () => option.icon);
|
|
157
|
+
return _el$9;
|
|
158
|
+
}
|
|
159
|
+
}), web.memo(() => option.label), web.createComponent(solidJs.Show, {
|
|
160
|
+
get when() {
|
|
161
|
+
return option.description;
|
|
162
|
+
},
|
|
163
|
+
get children() {
|
|
164
|
+
var _el$0 = web.getNextElement(_tmpl$6);
|
|
165
|
+
web.insert(_el$0, () => option.description);
|
|
166
|
+
return _el$0;
|
|
167
|
+
}
|
|
168
|
+
})];
|
|
150
169
|
},
|
|
151
170
|
get children() {
|
|
152
|
-
|
|
153
|
-
web.insert(_el$0, () => option.description);
|
|
154
|
-
return _el$0;
|
|
171
|
+
return props.config.optionRenderer(option, i());
|
|
155
172
|
}
|
|
156
|
-
})
|
|
173
|
+
}));
|
|
174
|
+
web.effect(() => web.className(_el$8, buttonClass()));
|
|
157
175
|
web.runHydrationEvents();
|
|
158
176
|
return _el$8;
|
|
159
177
|
})()
|
|
@@ -165,24 +183,24 @@ const ChoiceBody = (props) => {
|
|
|
165
183
|
const ConfirmBody = (props) => {
|
|
166
184
|
const isDanger = () => props.config.variant === "danger";
|
|
167
185
|
return (() => {
|
|
168
|
-
var _el$
|
|
169
|
-
web.insert(_el$
|
|
186
|
+
var _el$1 = web.getNextElement(_tmpl$8), _el$14 = _el$1.firstChild, [_el$15, _co$] = web.getNextMarker(_el$14.nextSibling), _el$11 = _el$15.nextSibling, _el$12 = _el$11.firstChild, _el$13 = _el$12.nextSibling;
|
|
187
|
+
web.insert(_el$1, web.createComponent(solidJs.Show, {
|
|
170
188
|
get when() {
|
|
171
189
|
return props.config.message;
|
|
172
190
|
},
|
|
173
191
|
get children() {
|
|
174
|
-
var _el$
|
|
175
|
-
web.insert(_el$
|
|
176
|
-
return _el$
|
|
192
|
+
var _el$10 = web.getNextElement(_tmpl$7);
|
|
193
|
+
web.insert(_el$10, () => props.config.message);
|
|
194
|
+
return _el$10;
|
|
177
195
|
}
|
|
178
|
-
}), _el$
|
|
179
|
-
web.addEventListener(_el$
|
|
180
|
-
web.insert(_el$
|
|
181
|
-
web.addEventListener(_el$
|
|
182
|
-
web.insert(_el$
|
|
183
|
-
web.effect(() => web.className(_el$
|
|
196
|
+
}), _el$15, _co$);
|
|
197
|
+
web.addEventListener(_el$12, "click", props.onCancel, true);
|
|
198
|
+
web.insert(_el$12, () => props.config.cancelLabel || "Cancel");
|
|
199
|
+
web.addEventListener(_el$13, "click", props.onConfirm, true);
|
|
200
|
+
web.insert(_el$13, () => props.config.confirmLabel || "Confirm");
|
|
201
|
+
web.effect(() => web.className(_el$13, `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"}`));
|
|
184
202
|
web.runHydrationEvents();
|
|
185
|
-
return _el$
|
|
203
|
+
return _el$1;
|
|
186
204
|
})();
|
|
187
205
|
};
|
|
188
206
|
const FormBody = (props) => {
|
|
@@ -289,9 +307,9 @@ const FormBody = (props) => {
|
|
|
289
307
|
return field;
|
|
290
308
|
};
|
|
291
309
|
return (() => {
|
|
292
|
-
var _el$
|
|
293
|
-
_el$
|
|
294
|
-
web.insert(_el$
|
|
310
|
+
var _el$16 = web.getNextElement(_tmpl$1), _el$21 = _el$16.firstChild, [_el$22, _co$2] = web.getNextMarker(_el$21.nextSibling), _el$23 = _el$22.nextSibling, [_el$24, _co$3] = web.getNextMarker(_el$23.nextSibling), _el$19 = _el$24.nextSibling, _el$20 = _el$19.firstChild;
|
|
311
|
+
_el$16.addEventListener("submit", handleSubmit);
|
|
312
|
+
web.insert(_el$16, web.createComponent(solidJs.For, {
|
|
295
313
|
get each() {
|
|
296
314
|
return props.config.fields;
|
|
297
315
|
},
|
|
@@ -305,34 +323,34 @@ const FormBody = (props) => {
|
|
|
305
323
|
onChange: (val) => updateField(field.name, val),
|
|
306
324
|
formData
|
|
307
325
|
})
|
|
308
|
-
}), _el$
|
|
309
|
-
web.insert(_el$
|
|
326
|
+
}), _el$22, _co$2);
|
|
327
|
+
web.insert(_el$16, web.createComponent(solidJs.Show, {
|
|
310
328
|
get when() {
|
|
311
329
|
return previewText() || previewLoading();
|
|
312
330
|
},
|
|
313
331
|
get children() {
|
|
314
|
-
var _el$
|
|
315
|
-
web.insert(_el$
|
|
332
|
+
var _el$17 = web.getNextElement(_tmpl$0);
|
|
333
|
+
web.insert(_el$17, web.createComponent(solidJs.Show, {
|
|
316
334
|
get when() {
|
|
317
335
|
return previewLoading();
|
|
318
336
|
},
|
|
319
337
|
get fallback() {
|
|
320
338
|
return (() => {
|
|
321
|
-
var _el$
|
|
322
|
-
web.insert(_el$
|
|
323
|
-
return _el$
|
|
339
|
+
var _el$25 = web.getNextElement(_tmpl$10);
|
|
340
|
+
web.insert(_el$25, previewText);
|
|
341
|
+
return _el$25;
|
|
324
342
|
})();
|
|
325
343
|
},
|
|
326
344
|
get children() {
|
|
327
345
|
return web.getNextElement(_tmpl$9);
|
|
328
346
|
}
|
|
329
347
|
}));
|
|
330
|
-
return _el$
|
|
348
|
+
return _el$17;
|
|
331
349
|
}
|
|
332
|
-
}), _el$
|
|
333
|
-
web.insert(_el$
|
|
334
|
-
web.effect(() => web.setProperty(_el$
|
|
335
|
-
return _el$
|
|
350
|
+
}), _el$24, _co$3);
|
|
351
|
+
web.insert(_el$20, () => props.config.submitLabel || "Submit");
|
|
352
|
+
web.effect(() => web.setProperty(_el$20, "disabled", !isValid()));
|
|
353
|
+
return _el$16;
|
|
336
354
|
})();
|
|
337
355
|
};
|
|
338
356
|
web.delegateEvents(["click"]);
|
|
@@ -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, 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
|
+
{"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 → consumer calls `onSubmit` →\n * prompt disappears.\n *\n * ## AbortSignal — known limitation (v5.1.0)\n *\n * **`ChatPrompt` itself does NOT listen to any `AbortSignal`.** It is a pure\n * presentation component: render a config, call `onSubmit` on user answer,\n * call `onDismiss` on X/Cancel. Lifecycle (including abort handling) is the\n * consumer's responsibility.\n *\n * `ChatCommands.showChatPrompt(config, signal?)` declares a `signal?` argument\n * in its type, but in v5.0.0/v5.1.0 mcp-ui ships **no default handler** for\n * this command — every consumer wires its own `bus.commands.handle('showChatPrompt', ...)`.\n * Each consumer's handler is responsible for:\n *\n * 1. Storing a `{ resolve, reject }` pair when the command fires.\n * 2. Calling `resolve(response)` from `onSubmit` / `resolve(dismissed)` from `onDismiss`.\n * 3. If a `signal` is provided: `signal.addEventListener('abort', () =>\n * reject(new DOMException('Prompt aborted', 'AbortError')))` and cleaning\n * up the listener on resolve/dismiss.\n *\n * The `DOMException('AbortError')` shape is the Web Platform convention\n * (matches `fetch()`, `Response.body.cancel()`, `WritableStream.abort()`).\n * Consumers can branch on `err.name === 'AbortError'` without importing any\n * mcp-ui type.\n *\n * A `createChatPromptController()` primitive centralising this wiring\n * (resolver lifecycle + re-entrance + abort) is planned for v5.2.0 — see\n * `docs/2026/r&d/mcpui-v5.1.0-consensus.md` for the design discussion.\n *\n * ## Re-entrance — known limitation (v5.1.0)\n *\n * Also handled by the consumer. If a new `showChatPrompt` arrives while a\n * previous one is active, the consumer's handler must decide whether to\n * auto-reject the previous Promise, queue, or throw. mcp-ui does not\n * currently enforce any policy. See the same design doc for the v5.2.0\n * direction (auto-reject with `PromptReplacedError`).\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 const base = (() => {\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 const extra = props.config.containerClass\n return extra ? `${base} ${extra}` : base\n }\n\n const buttonClass = () => {\n const base = '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 const extra = props.config.buttonClass\n return extra ? `${base} ${extra}` : base\n }\n\n return (\n <div class={layoutClass()}>\n <For each={props.config.options}>\n {(option, i) => (\n <button\n type=\"button\"\n onClick={() => props.onSelect(option.value, option.label)}\n class={buttonClass()}\n >\n <Show\n when={props.config.optionRenderer}\n fallback={\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 </>\n }\n >\n {props.config.optionRenderer!(option, i())}\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","base","layout","extra","containerClass","buttonClass","_el$7","_tmpl$3","For","each","options","option","i","_el$8","_tmpl$4","optionRenderer","icon","_el$9","_tmpl$5","_$memo","description","_el$0","_tmpl$6","isDanger","variant","_el$1","_tmpl$8","_el$14","_el$15","_co$","_$getNextMarker","_el$11","_el$12","_el$13","message","_el$10","_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$16","_tmpl$1","_el$21","_el$22","_co$2","_el$23","_el$24","_co$3","_el$19","_el$20","addEventListener","FormFieldRenderer","onChange","_el$17","_tmpl$0","_el$25","_tmpl$10","_tmpl$9","submitLabel","_$setProperty","_$delegateEvents"],"mappings":"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"}
|
|
@@ -4,8 +4,43 @@
|
|
|
4
4
|
*
|
|
5
5
|
* @experimental — This component may change without major bump until v2.5.0.
|
|
6
6
|
*
|
|
7
|
-
* Renders above the chat input. User responds →
|
|
8
|
-
*
|
|
7
|
+
* Renders above the chat input. User responds → consumer calls `onSubmit` →
|
|
8
|
+
* prompt disappears.
|
|
9
|
+
*
|
|
10
|
+
* ## AbortSignal — known limitation (v5.1.0)
|
|
11
|
+
*
|
|
12
|
+
* **`ChatPrompt` itself does NOT listen to any `AbortSignal`.** It is a pure
|
|
13
|
+
* presentation component: render a config, call `onSubmit` on user answer,
|
|
14
|
+
* call `onDismiss` on X/Cancel. Lifecycle (including abort handling) is the
|
|
15
|
+
* consumer's responsibility.
|
|
16
|
+
*
|
|
17
|
+
* `ChatCommands.showChatPrompt(config, signal?)` declares a `signal?` argument
|
|
18
|
+
* in its type, but in v5.0.0/v5.1.0 mcp-ui ships **no default handler** for
|
|
19
|
+
* this command — every consumer wires its own `bus.commands.handle('showChatPrompt', ...)`.
|
|
20
|
+
* Each consumer's handler is responsible for:
|
|
21
|
+
*
|
|
22
|
+
* 1. Storing a `{ resolve, reject }` pair when the command fires.
|
|
23
|
+
* 2. Calling `resolve(response)` from `onSubmit` / `resolve(dismissed)` from `onDismiss`.
|
|
24
|
+
* 3. If a `signal` is provided: `signal.addEventListener('abort', () =>
|
|
25
|
+
* reject(new DOMException('Prompt aborted', 'AbortError')))` and cleaning
|
|
26
|
+
* up the listener on resolve/dismiss.
|
|
27
|
+
*
|
|
28
|
+
* The `DOMException('AbortError')` shape is the Web Platform convention
|
|
29
|
+
* (matches `fetch()`, `Response.body.cancel()`, `WritableStream.abort()`).
|
|
30
|
+
* Consumers can branch on `err.name === 'AbortError'` without importing any
|
|
31
|
+
* mcp-ui type.
|
|
32
|
+
*
|
|
33
|
+
* A `createChatPromptController()` primitive centralising this wiring
|
|
34
|
+
* (resolver lifecycle + re-entrance + abort) is planned for v5.2.0 — see
|
|
35
|
+
* `docs/2026/r&d/mcpui-v5.1.0-consensus.md` for the design discussion.
|
|
36
|
+
*
|
|
37
|
+
* ## Re-entrance — known limitation (v5.1.0)
|
|
38
|
+
*
|
|
39
|
+
* Also handled by the consumer. If a new `showChatPrompt` arrives while a
|
|
40
|
+
* previous one is active, the consumer's handler must decide whether to
|
|
41
|
+
* auto-reject the previous Promise, queue, or throw. mcp-ui does not
|
|
42
|
+
* currently enforce any policy. See the same design doc for the v5.2.0
|
|
43
|
+
* direction (auto-reject with `PromptReplacedError`).
|
|
9
44
|
*/
|
|
10
45
|
import { Component } from 'solid-js';
|
|
11
46
|
import type { ChatPromptConfig, ChatPromptResponse } from '../types/chat-bus';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatPrompt.d.ts","sourceRoot":"","sources":["../../src/components/ChatPrompt.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"ChatPrompt.d.ts","sourceRoot":"","sources":["../../src/components/ChatPrompt.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;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"}
|