@seed-ship/mcp-ui-solid 5.1.0 → 5.3.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 +97 -0
- package/README.md +64 -13
- package/dist/components/ElicitationForm.cjs +51 -0
- package/dist/components/ElicitationForm.cjs.map +1 -0
- package/dist/components/ElicitationForm.d.ts +68 -0
- package/dist/components/ElicitationForm.d.ts.map +1 -0
- package/dist/components/ElicitationForm.js +51 -0
- package/dist/components/ElicitationForm.js.map +1 -0
- package/dist/components/FeedbackInline.cjs +57 -0
- package/dist/components/FeedbackInline.cjs.map +1 -0
- package/dist/components/FeedbackInline.d.ts +71 -0
- package/dist/components/FeedbackInline.d.ts.map +1 -0
- package/dist/components/FeedbackInline.js +57 -0
- package/dist/components/FeedbackInline.js.map +1 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components.cjs +2 -0
- package/dist/components.cjs.map +1 -1
- package/dist/components.d.cts +2 -0
- package/dist/components.d.ts +2 -0
- package/dist/components.js +2 -0
- package/dist/components.js.map +1 -1
- package/dist/index.cjs +17 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -2
- package/dist/index.d.ts +12 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -2
- package/dist/index.js.map +1 -1
- package/dist/services/chat-bus.cjs +71 -0
- package/dist/services/chat-bus.cjs.map +1 -1
- package/dist/services/chat-bus.d.ts +31 -1
- package/dist/services/chat-bus.d.ts.map +1 -1
- package/dist/services/chat-bus.js +71 -0
- package/dist/services/chat-bus.js.map +1 -1
- package/dist/services/chat-prompt-controller.cjs +83 -0
- package/dist/services/chat-prompt-controller.cjs.map +1 -0
- package/dist/services/chat-prompt-controller.d.ts +93 -0
- package/dist/services/chat-prompt-controller.d.ts.map +1 -0
- package/dist/services/chat-prompt-controller.js +83 -0
- package/dist/services/chat-prompt-controller.js.map +1 -0
- package/dist/stores/scratchpad-store.cjs +105 -77
- package/dist/stores/scratchpad-store.cjs.map +1 -1
- package/dist/stores/scratchpad-store.d.ts +88 -19
- package/dist/stores/scratchpad-store.d.ts.map +1 -1
- package/dist/stores/scratchpad-store.js +105 -77
- package/dist/stores/scratchpad-store.js.map +1 -1
- package/dist/stores/server-capabilities-store.cjs +61 -0
- package/dist/stores/server-capabilities-store.cjs.map +1 -0
- package/dist/stores/server-capabilities-store.d.ts +172 -0
- package/dist/stores/server-capabilities-store.d.ts.map +1 -0
- package/dist/stores/server-capabilities-store.js +61 -0
- package/dist/stores/server-capabilities-store.js.map +1 -0
- package/dist/types/chat-bus.d.ts +39 -0
- package/dist/types/chat-bus.d.ts.map +1 -1
- package/docs/recipes/elicitation-pseudo-spec-adapter.md +171 -0
- package/docs/recipes/feedback-inline-wiring.md +142 -0
- package/package.json +1 -1
- package/src/components/ElicitationForm.test.tsx +197 -0
- package/src/components/ElicitationForm.tsx +126 -0
- package/src/components/FeedbackInline.test.tsx +117 -0
- package/src/components/FeedbackInline.tsx +143 -0
- package/src/components/index.ts +4 -0
- package/src/index.ts +39 -1
- package/src/services/chat-bus.test.ts +154 -2
- package/src/services/chat-bus.ts +115 -0
- package/src/services/chat-prompt-controller.test.ts +144 -0
- package/src/services/chat-prompt-controller.ts +214 -0
- package/src/stores/scratchpad-store.test.tsx +140 -0
- package/src/stores/scratchpad-store.tsx +244 -0
- package/src/stores/server-capabilities-store.test.tsx +206 -0
- package/src/stores/server-capabilities-store.tsx +215 -0
- package/src/types/chat-bus.ts +40 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/src/stores/scratchpad-store.ts +0 -126
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scratchpad-store.cjs","sources":["../../src/stores/scratchpad-store.ts"],"sourcesContent":["/**\n * Scratchpad Store — singleton reactive state for HITL scratchpad\n * v3.0.3: Eliminates ChatBus relay chain race condition\n *\n * @experimental\n *\n * Parser calls dispatchScratchpad() → store updates → ScratchpadPanel reads reactively.\n * Zero bus, zero relay, zero race condition.\n *\n * **Known limitation (v4.3.9):** This store is a module-level singleton, not\n * a context-scoped factory. Two `ScratchpadPanel` instances in the same app\n * will share the same state. Multi-panel scenarios (e.g. chat + admin dashboard\n * both showing scratchpads simultaneously) are unsupported. Host apps that need\n * isolated scratchpads should not reuse this store — wait for v4.4.0 which\n * will expose `createScratchpadStore()` factory for per-panel instances.\n */\n\nimport { createStore, produce } from 'solid-js/store'\nimport type { ScratchpadState, ScratchpadEvent, ScratchpadSection } from '../types/chat-bus'\n\nconst [scratchpadStore, setScratchpadStore] = createStore<{\n current: ScratchpadState | null\n pinned: boolean\n}>({ current: null, pinned: false })\n\n/**\n * Hook for the COMPONENT — reads the scratchpad state reactively.\n *\n * @example\n * const { state, pinned, close } = useScratchpadState()\n * <Show when={state()}>\n * <ScratchpadPanel state={state()!} pinned={pinned()} onClose={close} />\n * </Show>\n */\nexport function useScratchpadState() {\n return {\n state: () => scratchpadStore.current,\n pinned: () => scratchpadStore.pinned,\n close: () => setScratchpadStore({ current: null, pinned: false }),\n }\n}\n\n/**\n * Function for the PARSER/STORE — mutates the scratchpad state.\n * Called from the SSE callback, no bus needed.\n *\n * @example\n * // In your SSE parser callback — ONE LINE\n * onScratchpad: (data) => dispatchScratchpad(data as ScratchpadEvent)\n */\nexport function dispatchScratchpad(event: ScratchpadEvent): void {\n // DX1: lifecycle logging\n if (event.action === 'create') {\n console.info(\n `%c[MCP-UI] dispatchScratchpad%c create id=${event.id} sections=${event.sections?.length || 0} status=${event.status || 'loading'}${event.pinned ? ' pinned' : ''}`,\n 'color: #10b981; font-weight: bold', 'color: inherit'\n )\n setScratchpadStore({\n current: {\n id: event.id,\n title: event.title || '',\n sections: event.sections || [],\n filters: event.filters || {},\n preview: event.preview,\n agentMessages: event.agentMessages || [],\n status: event.status || 'loading',\n previewEndpoint: (event as any).previewEndpoint,\n previewDebounce: (event as any).previewDebounce,\n previewMethod: (event as any).previewMethod,\n previewHeaders: (event as any).previewHeaders,\n turn: (event as any).turn,\n totalTurns: (event as any).totalTurns,\n turnHistory: (event as any).turnHistory,\n },\n pinned: event.pinned || false,\n })\n } else if (event.action === 'update') {\n console.info(\n `%c[MCP-UI] dispatchScratchpad%c update id=${event.id} sectionMode=${event.sectionMode || 'replace'} sections=${event.sections?.length || 0} status=${event.status || '-'}`,\n 'color: #3b82f6; font-weight: bold', 'color: inherit'\n )\n setScratchpadStore(produce((s) => {\n if (!s.current || s.current.id !== event.id) {\n console.warn(`[MCP-UI] dispatchScratchpad: update for id=${event.id} but current is ${s.current?.id || 'null'}. Ignoring.`)\n return\n }\n\n if (event.sections) {\n const mode = event.sectionMode || 'replace'\n if (mode === 'replace') {\n s.current.sections = event.sections\n } else if (mode === 'append') {\n s.current.sections = [...s.current.sections, ...event.sections]\n } else if (mode === 'upsert') {\n let matchCount = 0\n for (const incoming of event.sections) {\n const idx = s.current.sections.findIndex((sec: ScratchpadSection) => sec.id === incoming.id)\n if (idx >= 0) {\n s.current.sections[idx] = incoming\n matchCount++\n } else {\n s.current.sections.push(incoming)\n }\n }\n if (matchCount === 0 && event.sections.length > 0) {\n console.warn(\n `[MCP-UI] dispatchScratchpad: sectionMode='upsert' but no IDs matched. ` +\n `Incoming: [${event.sections.map((s: ScratchpadSection) => s.id).join(', ')}] ` +\n `Existing: [${s.current.sections.map((s: ScratchpadSection) => s.id).join(', ')}]. All appended.`\n )\n }\n }\n }\n if (event.agentMessages) s.current.agentMessages = event.agentMessages\n if (event.status) s.current.status = event.status\n if (event.filters) s.current.filters = event.filters\n if (event.preview) s.current.preview = event.preview\n if (event.pinned != null) s.pinned = event.pinned\n if ((event as any).turnHistory) s.current.turnHistory = (event as any).turnHistory\n if ((event as any).turn != null) s.current.turn = (event as any).turn\n }))\n } else if (event.action === 'close') {\n console.info(`%c[MCP-UI] dispatchScratchpad%c close id=${event.id}`, 'color: #6b7280; font-weight: bold', 'color: inherit')\n setScratchpadStore({ current: null, pinned: false })\n }\n}\n"],"names":["createStore","produce","_a","s"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"scratchpad-store.cjs","sources":["../../src/stores/scratchpad-store.tsx"],"sourcesContent":["/**\n * Scratchpad Store — reactive state for HITL scratchpad\n *\n * @experimental\n *\n * **v5.2.0 :** the store is now a factory (`createScratchpadStore()`) with a\n * module-level singleton kept as default. Two consumption modes :\n *\n * 1. **Singleton mode (default, zero-config)** — `dispatchScratchpad(event)` +\n * `useScratchpadState()` read/write the module singleton. This is the v4.x\n * path and keeps working unchanged.\n *\n * 2. **Multi-instance mode** — wrap a subtree in `<ScratchpadStoreProvider>`\n * (it creates a scoped `ScratchpadStoreHandle` internally, or use your own\n * via the `store` prop). `useScratchpadState()` auto-detects the context\n * and reads from it; `ScratchpadPanel` mounted inside the provider reads\n * the scoped store. Non-reactive callers (SSE parsers) should pass the\n * handle explicitly — do NOT try to reach context from a non-reactive\n * scope.\n */\n\nimport { createContext, useContext, type ParentComponent, type JSX } from 'solid-js'\nimport { createStore, produce } from 'solid-js/store'\nimport type { ScratchpadState, ScratchpadEvent, ScratchpadSection } from '../types/chat-bus'\n\n// ─── Handle shape ─────────────────────────────────────────────\n\nexport interface ScratchpadStoreHandle {\n /** Mutate the store from an SSE/parser callback. */\n dispatch: (event: ScratchpadEvent) => void\n /** Reactive accessor for the current scratchpad state (null when closed). */\n state: () => ScratchpadState | null\n /** Reactive accessor for the pinned flag. */\n pinned: () => boolean\n /** Close the scratchpad (equivalent to dispatching an action='close'). */\n close: () => void\n}\n\n// ─── Factory ──────────────────────────────────────────────────\n\n/**\n * Create an isolated scratchpad store instance.\n *\n * Use this when you need two or more scratchpads live at the same time\n * (e.g. chat scratchpad + admin dashboard scratchpad). Pair with\n * `<ScratchpadStoreProvider store={...}>` to scope a SolidJS subtree.\n *\n * @experimental\n * @since v5.2.0\n */\nexport function createScratchpadStore(): ScratchpadStoreHandle {\n const [scratchpadStore, setScratchpadStore] = createStore<{\n current: ScratchpadState | null\n pinned: boolean\n }>({ current: null, pinned: false })\n\n const dispatch = (event: ScratchpadEvent): void => {\n if (event.action === 'create') {\n console.info(\n `%c[MCP-UI] dispatchScratchpad%c create id=${event.id} sections=${event.sections?.length || 0} status=${event.status || 'loading'}${event.pinned ? ' pinned' : ''}`,\n 'color: #10b981; font-weight: bold',\n 'color: inherit'\n )\n setScratchpadStore({\n current: {\n id: event.id,\n title: event.title || '',\n sections: event.sections || [],\n filters: event.filters || {},\n preview: event.preview,\n agentMessages: event.agentMessages || [],\n status: event.status || 'loading',\n previewEndpoint: (event as any).previewEndpoint,\n previewDebounce: (event as any).previewDebounce,\n previewMethod: (event as any).previewMethod,\n previewHeaders: (event as any).previewHeaders,\n turn: (event as any).turn,\n totalTurns: (event as any).totalTurns,\n turnHistory: (event as any).turnHistory,\n },\n pinned: event.pinned || false,\n })\n } else if (event.action === 'update') {\n console.info(\n `%c[MCP-UI] dispatchScratchpad%c update id=${event.id} sectionMode=${event.sectionMode || 'replace'} sections=${event.sections?.length || 0} status=${event.status || '-'}`,\n 'color: #3b82f6; font-weight: bold',\n 'color: inherit'\n )\n setScratchpadStore(\n produce((s) => {\n if (!s.current || s.current.id !== event.id) {\n console.warn(\n `[MCP-UI] dispatchScratchpad: update for id=${event.id} but current is ${s.current?.id || 'null'}. Ignoring.`\n )\n return\n }\n\n if (event.sections) {\n const mode = event.sectionMode || 'replace'\n if (mode === 'replace') {\n s.current.sections = event.sections\n } else if (mode === 'append') {\n s.current.sections = [...s.current.sections, ...event.sections]\n } else if (mode === 'upsert') {\n let matchCount = 0\n for (const incoming of event.sections) {\n const idx = s.current.sections.findIndex(\n (sec: ScratchpadSection) => sec.id === incoming.id\n )\n if (idx >= 0) {\n s.current.sections[idx] = incoming\n matchCount++\n } else {\n s.current.sections.push(incoming)\n }\n }\n if (matchCount === 0 && event.sections.length > 0) {\n console.warn(\n `[MCP-UI] dispatchScratchpad: sectionMode='upsert' but no IDs matched. ` +\n `Incoming: [${event.sections.map((s: ScratchpadSection) => s.id).join(', ')}] ` +\n `Existing: [${s.current.sections.map((s: ScratchpadSection) => s.id).join(', ')}]. All appended.`\n )\n }\n }\n }\n if (event.agentMessages) s.current.agentMessages = event.agentMessages\n if (event.status) s.current.status = event.status\n if (event.filters) s.current.filters = event.filters\n if (event.preview) s.current.preview = event.preview\n if (event.pinned != null) s.pinned = event.pinned\n if ((event as any).turnHistory) s.current.turnHistory = (event as any).turnHistory\n if ((event as any).turn != null) s.current.turn = (event as any).turn\n })\n )\n } else if (event.action === 'close') {\n console.info(\n `%c[MCP-UI] dispatchScratchpad%c close id=${event.id}`,\n 'color: #6b7280; font-weight: bold',\n 'color: inherit'\n )\n setScratchpadStore({ current: null, pinned: false })\n }\n }\n\n return {\n dispatch,\n state: () => scratchpadStore.current,\n pinned: () => scratchpadStore.pinned,\n close: () => setScratchpadStore({ current: null, pinned: false }),\n }\n}\n\n// ─── Module-level singleton (v4.x-compatible default) ─────────\n\nconst defaultStore: ScratchpadStoreHandle = createScratchpadStore()\n\n/**\n * Function for the PARSER/STORE — mutates the **module-level singleton**\n * scratchpad state. Use this when you only need one scratchpad at a time\n * (single-instance consumer, the v4.x pattern).\n *\n * For multi-instance scenarios, prefer `createScratchpadStore()` and pass the\n * handle around explicitly.\n *\n * @example\n * // In your SSE parser callback — ONE LINE\n * onScratchpad: (data) => dispatchScratchpad(data as ScratchpadEvent)\n */\nexport function dispatchScratchpad(event: ScratchpadEvent): void {\n defaultStore.dispatch(event)\n}\n\n// ─── Context (v5.2.0) ─────────────────────────────────────────\n\n/**\n * Context for a scoped scratchpad store. Populated by\n * `<ScratchpadStoreProvider>`. Read by `useScratchpadState()` with automatic\n * fallback to the module-level singleton when the context is absent.\n *\n * @experimental\n * @since v5.2.0\n */\nexport const ScratchpadStoreContext = createContext<ScratchpadStoreHandle | undefined>(undefined)\n\n/**\n * Provide a scoped `ScratchpadStoreHandle` to a SolidJS subtree. Children\n * reading via `useScratchpadState()` or rendering a `<ScratchpadPanel>` will\n * bind to this store instead of the module singleton.\n *\n * If no `store` prop is passed, a fresh store is created for the provider's\n * lifetime. Pass `store` explicitly when you need the handle outside the\n * tree (e.g. in an SSE parser that lives at the app root).\n *\n * @experimental\n * @since v5.2.0\n *\n * @example\n * const chatStore = createScratchpadStore()\n * const adminStore = createScratchpadStore()\n *\n * <ScratchpadStoreProvider store={chatStore}>\n * <ChatInterface />\n * </ScratchpadStoreProvider>\n * <ScratchpadStoreProvider store={adminStore}>\n * <AdminDashboard />\n * </ScratchpadStoreProvider>\n */\nexport const ScratchpadStoreProvider: ParentComponent<{\n store?: ScratchpadStoreHandle\n}> = (props): JSX.Element => {\n const store = props.store ?? createScratchpadStore()\n return (\n <ScratchpadStoreContext.Provider value={store}>{props.children}</ScratchpadStoreContext.Provider>\n )\n}\n\n// ─── Reactive hook (context-aware) ────────────────────────────\n\n/**\n * Hook for the COMPONENT — reads the scratchpad state reactively.\n *\n * **v5.2.0 :** if called inside a `<ScratchpadStoreProvider>`, reads the\n * scoped handle; otherwise falls back to the module singleton. Old v4.x\n * consumers keep working unchanged.\n *\n * @example\n * const { state, pinned, close } = useScratchpadState()\n * <Show when={state()}>\n * <ScratchpadPanel state={state()!} pinned={pinned()} onClose={close} />\n * </Show>\n */\nexport function useScratchpadState(): {\n state: () => ScratchpadState | null\n pinned: () => boolean\n close: () => void\n} {\n const scoped = useContext(ScratchpadStoreContext)\n const handle = scoped ?? defaultStore\n return {\n state: handle.state,\n pinned: handle.pinned,\n close: handle.close,\n }\n}\n"],"names":["createScratchpadStore","scratchpadStore","setScratchpadStore","createStore","current","pinned","dispatch","event","action","console","info","id","sections","length","status","title","filters","preview","agentMessages","previewEndpoint","previewDebounce","previewMethod","previewHeaders","turn","totalTurns","turnHistory","sectionMode","produce","s","warn","mode","matchCount","incoming","idx","findIndex","sec","push","map","join","state","close","defaultStore","dispatchScratchpad","ScratchpadStoreContext","createContext","undefined","ScratchpadStoreProvider","props","store","_$createComponent","Provider","value","children","useScratchpadState","scoped","useContext","handle"],"mappings":"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"}
|
|
@@ -1,23 +1,101 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Scratchpad Store —
|
|
3
|
-
* v3.0.3: Eliminates ChatBus relay chain race condition
|
|
2
|
+
* Scratchpad Store — reactive state for HITL scratchpad
|
|
4
3
|
*
|
|
5
4
|
* @experimental
|
|
6
5
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* **v5.2.0 :** the store is now a factory (`createScratchpadStore()`) with a
|
|
7
|
+
* module-level singleton kept as default. Two consumption modes :
|
|
9
8
|
*
|
|
10
|
-
* **
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
9
|
+
* 1. **Singleton mode (default, zero-config)** — `dispatchScratchpad(event)` +
|
|
10
|
+
* `useScratchpadState()` read/write the module singleton. This is the v4.x
|
|
11
|
+
* path and keeps working unchanged.
|
|
12
|
+
*
|
|
13
|
+
* 2. **Multi-instance mode** — wrap a subtree in `<ScratchpadStoreProvider>`
|
|
14
|
+
* (it creates a scoped `ScratchpadStoreHandle` internally, or use your own
|
|
15
|
+
* via the `store` prop). `useScratchpadState()` auto-detects the context
|
|
16
|
+
* and reads from it; `ScratchpadPanel` mounted inside the provider reads
|
|
17
|
+
* the scoped store. Non-reactive callers (SSE parsers) should pass the
|
|
18
|
+
* handle explicitly — do NOT try to reach context from a non-reactive
|
|
19
|
+
* scope.
|
|
16
20
|
*/
|
|
21
|
+
import { type ParentComponent } from 'solid-js';
|
|
17
22
|
import type { ScratchpadState, ScratchpadEvent } from '../types/chat-bus';
|
|
23
|
+
export interface ScratchpadStoreHandle {
|
|
24
|
+
/** Mutate the store from an SSE/parser callback. */
|
|
25
|
+
dispatch: (event: ScratchpadEvent) => void;
|
|
26
|
+
/** Reactive accessor for the current scratchpad state (null when closed). */
|
|
27
|
+
state: () => ScratchpadState | null;
|
|
28
|
+
/** Reactive accessor for the pinned flag. */
|
|
29
|
+
pinned: () => boolean;
|
|
30
|
+
/** Close the scratchpad (equivalent to dispatching an action='close'). */
|
|
31
|
+
close: () => void;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Create an isolated scratchpad store instance.
|
|
35
|
+
*
|
|
36
|
+
* Use this when you need two or more scratchpads live at the same time
|
|
37
|
+
* (e.g. chat scratchpad + admin dashboard scratchpad). Pair with
|
|
38
|
+
* `<ScratchpadStoreProvider store={...}>` to scope a SolidJS subtree.
|
|
39
|
+
*
|
|
40
|
+
* @experimental
|
|
41
|
+
* @since v5.2.0
|
|
42
|
+
*/
|
|
43
|
+
export declare function createScratchpadStore(): ScratchpadStoreHandle;
|
|
44
|
+
/**
|
|
45
|
+
* Function for the PARSER/STORE — mutates the **module-level singleton**
|
|
46
|
+
* scratchpad state. Use this when you only need one scratchpad at a time
|
|
47
|
+
* (single-instance consumer, the v4.x pattern).
|
|
48
|
+
*
|
|
49
|
+
* For multi-instance scenarios, prefer `createScratchpadStore()` and pass the
|
|
50
|
+
* handle around explicitly.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* // In your SSE parser callback — ONE LINE
|
|
54
|
+
* onScratchpad: (data) => dispatchScratchpad(data as ScratchpadEvent)
|
|
55
|
+
*/
|
|
56
|
+
export declare function dispatchScratchpad(event: ScratchpadEvent): void;
|
|
57
|
+
/**
|
|
58
|
+
* Context for a scoped scratchpad store. Populated by
|
|
59
|
+
* `<ScratchpadStoreProvider>`. Read by `useScratchpadState()` with automatic
|
|
60
|
+
* fallback to the module-level singleton when the context is absent.
|
|
61
|
+
*
|
|
62
|
+
* @experimental
|
|
63
|
+
* @since v5.2.0
|
|
64
|
+
*/
|
|
65
|
+
export declare const ScratchpadStoreContext: import("solid-js").Context<ScratchpadStoreHandle | undefined>;
|
|
66
|
+
/**
|
|
67
|
+
* Provide a scoped `ScratchpadStoreHandle` to a SolidJS subtree. Children
|
|
68
|
+
* reading via `useScratchpadState()` or rendering a `<ScratchpadPanel>` will
|
|
69
|
+
* bind to this store instead of the module singleton.
|
|
70
|
+
*
|
|
71
|
+
* If no `store` prop is passed, a fresh store is created for the provider's
|
|
72
|
+
* lifetime. Pass `store` explicitly when you need the handle outside the
|
|
73
|
+
* tree (e.g. in an SSE parser that lives at the app root).
|
|
74
|
+
*
|
|
75
|
+
* @experimental
|
|
76
|
+
* @since v5.2.0
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* const chatStore = createScratchpadStore()
|
|
80
|
+
* const adminStore = createScratchpadStore()
|
|
81
|
+
*
|
|
82
|
+
* <ScratchpadStoreProvider store={chatStore}>
|
|
83
|
+
* <ChatInterface />
|
|
84
|
+
* </ScratchpadStoreProvider>
|
|
85
|
+
* <ScratchpadStoreProvider store={adminStore}>
|
|
86
|
+
* <AdminDashboard />
|
|
87
|
+
* </ScratchpadStoreProvider>
|
|
88
|
+
*/
|
|
89
|
+
export declare const ScratchpadStoreProvider: ParentComponent<{
|
|
90
|
+
store?: ScratchpadStoreHandle;
|
|
91
|
+
}>;
|
|
18
92
|
/**
|
|
19
93
|
* Hook for the COMPONENT — reads the scratchpad state reactively.
|
|
20
94
|
*
|
|
95
|
+
* **v5.2.0 :** if called inside a `<ScratchpadStoreProvider>`, reads the
|
|
96
|
+
* scoped handle; otherwise falls back to the module singleton. Old v4.x
|
|
97
|
+
* consumers keep working unchanged.
|
|
98
|
+
*
|
|
21
99
|
* @example
|
|
22
100
|
* const { state, pinned, close } = useScratchpadState()
|
|
23
101
|
* <Show when={state()}>
|
|
@@ -29,13 +107,4 @@ export declare function useScratchpadState(): {
|
|
|
29
107
|
pinned: () => boolean;
|
|
30
108
|
close: () => void;
|
|
31
109
|
};
|
|
32
|
-
/**
|
|
33
|
-
* Function for the PARSER/STORE — mutates the scratchpad state.
|
|
34
|
-
* Called from the SSE callback, no bus needed.
|
|
35
|
-
*
|
|
36
|
-
* @example
|
|
37
|
-
* // In your SSE parser callback — ONE LINE
|
|
38
|
-
* onScratchpad: (data) => dispatchScratchpad(data as ScratchpadEvent)
|
|
39
|
-
*/
|
|
40
|
-
export declare function dispatchScratchpad(event: ScratchpadEvent): void;
|
|
41
110
|
//# sourceMappingURL=scratchpad-store.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scratchpad-store.d.ts","sourceRoot":"","sources":["../../src/stores/scratchpad-store.
|
|
1
|
+
{"version":3,"file":"scratchpad-store.d.ts","sourceRoot":"","sources":["../../src/stores/scratchpad-store.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAA6B,KAAK,eAAe,EAAY,MAAM,UAAU,CAAA;AAEpF,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAqB,MAAM,mBAAmB,CAAA;AAI5F,MAAM,WAAW,qBAAqB;IACpC,oDAAoD;IACpD,QAAQ,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAA;IAC1C,6EAA6E;IAC7E,KAAK,EAAE,MAAM,eAAe,GAAG,IAAI,CAAA;IACnC,6CAA6C;IAC7C,MAAM,EAAE,MAAM,OAAO,CAAA;IACrB,0EAA0E;IAC1E,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB;AAID;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,IAAI,qBAAqB,CAoG7D;AAMD;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAE/D;AAID;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,+DAA8D,CAAA;AAEjG;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,uBAAuB,EAAE,eAAe,CAAC;IACpD,KAAK,CAAC,EAAE,qBAAqB,CAAA;CAC9B,CAKA,CAAA;AAID;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,IAAI;IACpC,KAAK,EAAE,MAAM,eAAe,GAAG,IAAI,CAAA;IACnC,MAAM,EAAE,MAAM,OAAO,CAAA;IACrB,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB,CAQA"}
|
|
@@ -1,89 +1,117 @@
|
|
|
1
|
+
import { createComponent } from "solid-js/web";
|
|
2
|
+
import { createContext, useContext } from "solid-js";
|
|
1
3
|
import { createStore, produce } from "solid-js/store";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
+
function createScratchpadStore() {
|
|
5
|
+
const [scratchpadStore, setScratchpadStore] = createStore({
|
|
6
|
+
current: null,
|
|
7
|
+
pinned: false
|
|
8
|
+
});
|
|
9
|
+
const dispatch = (event) => {
|
|
10
|
+
var _a, _b;
|
|
11
|
+
if (event.action === "create") {
|
|
12
|
+
console.info(`%c[MCP-UI] dispatchScratchpad%c create id=${event.id} sections=${((_a = event.sections) == null ? void 0 : _a.length) || 0} status=${event.status || "loading"}${event.pinned ? " pinned" : ""}`, "color: #10b981; font-weight: bold", "color: inherit");
|
|
13
|
+
setScratchpadStore({
|
|
14
|
+
current: {
|
|
15
|
+
id: event.id,
|
|
16
|
+
title: event.title || "",
|
|
17
|
+
sections: event.sections || [],
|
|
18
|
+
filters: event.filters || {},
|
|
19
|
+
preview: event.preview,
|
|
20
|
+
agentMessages: event.agentMessages || [],
|
|
21
|
+
status: event.status || "loading",
|
|
22
|
+
previewEndpoint: event.previewEndpoint,
|
|
23
|
+
previewDebounce: event.previewDebounce,
|
|
24
|
+
previewMethod: event.previewMethod,
|
|
25
|
+
previewHeaders: event.previewHeaders,
|
|
26
|
+
turn: event.turn,
|
|
27
|
+
totalTurns: event.totalTurns,
|
|
28
|
+
turnHistory: event.turnHistory
|
|
29
|
+
},
|
|
30
|
+
pinned: event.pinned || false
|
|
31
|
+
});
|
|
32
|
+
} else if (event.action === "update") {
|
|
33
|
+
console.info(`%c[MCP-UI] dispatchScratchpad%c update id=${event.id} sectionMode=${event.sectionMode || "replace"} sections=${((_b = event.sections) == null ? void 0 : _b.length) || 0} status=${event.status || "-"}`, "color: #3b82f6; font-weight: bold", "color: inherit");
|
|
34
|
+
setScratchpadStore(produce((s) => {
|
|
35
|
+
var _a2;
|
|
36
|
+
if (!s.current || s.current.id !== event.id) {
|
|
37
|
+
console.warn(`[MCP-UI] dispatchScratchpad: update for id=${event.id} but current is ${((_a2 = s.current) == null ? void 0 : _a2.id) || "null"}. Ignoring.`);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (event.sections) {
|
|
41
|
+
const mode = event.sectionMode || "replace";
|
|
42
|
+
if (mode === "replace") {
|
|
43
|
+
s.current.sections = event.sections;
|
|
44
|
+
} else if (mode === "append") {
|
|
45
|
+
s.current.sections = [...s.current.sections, ...event.sections];
|
|
46
|
+
} else if (mode === "upsert") {
|
|
47
|
+
let matchCount = 0;
|
|
48
|
+
for (const incoming of event.sections) {
|
|
49
|
+
const idx = s.current.sections.findIndex((sec) => sec.id === incoming.id);
|
|
50
|
+
if (idx >= 0) {
|
|
51
|
+
s.current.sections[idx] = incoming;
|
|
52
|
+
matchCount++;
|
|
53
|
+
} else {
|
|
54
|
+
s.current.sections.push(incoming);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (matchCount === 0 && event.sections.length > 0) {
|
|
58
|
+
console.warn(`[MCP-UI] dispatchScratchpad: sectionMode='upsert' but no IDs matched. Incoming: [${event.sections.map((s2) => s2.id).join(", ")}] Existing: [${s.current.sections.map((s2) => s2.id).join(", ")}]. All appended.`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (event.agentMessages) s.current.agentMessages = event.agentMessages;
|
|
63
|
+
if (event.status) s.current.status = event.status;
|
|
64
|
+
if (event.filters) s.current.filters = event.filters;
|
|
65
|
+
if (event.preview) s.current.preview = event.preview;
|
|
66
|
+
if (event.pinned != null) s.pinned = event.pinned;
|
|
67
|
+
if (event.turnHistory) s.current.turnHistory = event.turnHistory;
|
|
68
|
+
if (event.turn != null) s.current.turn = event.turn;
|
|
69
|
+
}));
|
|
70
|
+
} else if (event.action === "close") {
|
|
71
|
+
console.info(`%c[MCP-UI] dispatchScratchpad%c close id=${event.id}`, "color: #6b7280; font-weight: bold", "color: inherit");
|
|
72
|
+
setScratchpadStore({
|
|
73
|
+
current: null,
|
|
74
|
+
pinned: false
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
};
|
|
4
78
|
return {
|
|
79
|
+
dispatch,
|
|
5
80
|
state: () => scratchpadStore.current,
|
|
6
81
|
pinned: () => scratchpadStore.pinned,
|
|
7
|
-
close: () => setScratchpadStore({
|
|
82
|
+
close: () => setScratchpadStore({
|
|
83
|
+
current: null,
|
|
84
|
+
pinned: false
|
|
85
|
+
})
|
|
8
86
|
};
|
|
9
87
|
}
|
|
88
|
+
const defaultStore = createScratchpadStore();
|
|
10
89
|
function dispatchScratchpad(event) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
turn: event.turn,
|
|
32
|
-
totalTurns: event.totalTurns,
|
|
33
|
-
turnHistory: event.turnHistory
|
|
34
|
-
},
|
|
35
|
-
pinned: event.pinned || false
|
|
36
|
-
});
|
|
37
|
-
} else if (event.action === "update") {
|
|
38
|
-
console.info(
|
|
39
|
-
`%c[MCP-UI] dispatchScratchpad%c update id=${event.id} sectionMode=${event.sectionMode || "replace"} sections=${((_b = event.sections) == null ? void 0 : _b.length) || 0} status=${event.status || "-"}`,
|
|
40
|
-
"color: #3b82f6; font-weight: bold",
|
|
41
|
-
"color: inherit"
|
|
42
|
-
);
|
|
43
|
-
setScratchpadStore(produce((s) => {
|
|
44
|
-
var _a2;
|
|
45
|
-
if (!s.current || s.current.id !== event.id) {
|
|
46
|
-
console.warn(`[MCP-UI] dispatchScratchpad: update for id=${event.id} but current is ${((_a2 = s.current) == null ? void 0 : _a2.id) || "null"}. Ignoring.`);
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
if (event.sections) {
|
|
50
|
-
const mode = event.sectionMode || "replace";
|
|
51
|
-
if (mode === "replace") {
|
|
52
|
-
s.current.sections = event.sections;
|
|
53
|
-
} else if (mode === "append") {
|
|
54
|
-
s.current.sections = [...s.current.sections, ...event.sections];
|
|
55
|
-
} else if (mode === "upsert") {
|
|
56
|
-
let matchCount = 0;
|
|
57
|
-
for (const incoming of event.sections) {
|
|
58
|
-
const idx = s.current.sections.findIndex((sec) => sec.id === incoming.id);
|
|
59
|
-
if (idx >= 0) {
|
|
60
|
-
s.current.sections[idx] = incoming;
|
|
61
|
-
matchCount++;
|
|
62
|
-
} else {
|
|
63
|
-
s.current.sections.push(incoming);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
if (matchCount === 0 && event.sections.length > 0) {
|
|
67
|
-
console.warn(
|
|
68
|
-
`[MCP-UI] dispatchScratchpad: sectionMode='upsert' but no IDs matched. Incoming: [${event.sections.map((s2) => s2.id).join(", ")}] Existing: [${s.current.sections.map((s2) => s2.id).join(", ")}]. All appended.`
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
if (event.agentMessages) s.current.agentMessages = event.agentMessages;
|
|
74
|
-
if (event.status) s.current.status = event.status;
|
|
75
|
-
if (event.filters) s.current.filters = event.filters;
|
|
76
|
-
if (event.preview) s.current.preview = event.preview;
|
|
77
|
-
if (event.pinned != null) s.pinned = event.pinned;
|
|
78
|
-
if (event.turnHistory) s.current.turnHistory = event.turnHistory;
|
|
79
|
-
if (event.turn != null) s.current.turn = event.turn;
|
|
80
|
-
}));
|
|
81
|
-
} else if (event.action === "close") {
|
|
82
|
-
console.info(`%c[MCP-UI] dispatchScratchpad%c close id=${event.id}`, "color: #6b7280; font-weight: bold", "color: inherit");
|
|
83
|
-
setScratchpadStore({ current: null, pinned: false });
|
|
84
|
-
}
|
|
90
|
+
defaultStore.dispatch(event);
|
|
91
|
+
}
|
|
92
|
+
const ScratchpadStoreContext = createContext(void 0);
|
|
93
|
+
const ScratchpadStoreProvider = (props) => {
|
|
94
|
+
const store = props.store ?? createScratchpadStore();
|
|
95
|
+
return createComponent(ScratchpadStoreContext.Provider, {
|
|
96
|
+
value: store,
|
|
97
|
+
get children() {
|
|
98
|
+
return props.children;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
function useScratchpadState() {
|
|
103
|
+
const scoped = useContext(ScratchpadStoreContext);
|
|
104
|
+
const handle = scoped ?? defaultStore;
|
|
105
|
+
return {
|
|
106
|
+
state: handle.state,
|
|
107
|
+
pinned: handle.pinned,
|
|
108
|
+
close: handle.close
|
|
109
|
+
};
|
|
85
110
|
}
|
|
86
111
|
export {
|
|
112
|
+
ScratchpadStoreContext,
|
|
113
|
+
ScratchpadStoreProvider,
|
|
114
|
+
createScratchpadStore,
|
|
87
115
|
dispatchScratchpad,
|
|
88
116
|
useScratchpadState
|
|
89
117
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scratchpad-store.js","sources":["../../src/stores/scratchpad-store.ts"],"sourcesContent":["/**\n * Scratchpad Store — singleton reactive state for HITL scratchpad\n * v3.0.3: Eliminates ChatBus relay chain race condition\n *\n * @experimental\n *\n * Parser calls dispatchScratchpad() → store updates → ScratchpadPanel reads reactively.\n * Zero bus, zero relay, zero race condition.\n *\n * **Known limitation (v4.3.9):** This store is a module-level singleton, not\n * a context-scoped factory. Two `ScratchpadPanel` instances in the same app\n * will share the same state. Multi-panel scenarios (e.g. chat + admin dashboard\n * both showing scratchpads simultaneously) are unsupported. Host apps that need\n * isolated scratchpads should not reuse this store — wait for v4.4.0 which\n * will expose `createScratchpadStore()` factory for per-panel instances.\n */\n\nimport { createStore, produce } from 'solid-js/store'\nimport type { ScratchpadState, ScratchpadEvent, ScratchpadSection } from '../types/chat-bus'\n\nconst [scratchpadStore, setScratchpadStore] = createStore<{\n current: ScratchpadState | null\n pinned: boolean\n}>({ current: null, pinned: false })\n\n/**\n * Hook for the COMPONENT — reads the scratchpad state reactively.\n *\n * @example\n * const { state, pinned, close } = useScratchpadState()\n * <Show when={state()}>\n * <ScratchpadPanel state={state()!} pinned={pinned()} onClose={close} />\n * </Show>\n */\nexport function useScratchpadState() {\n return {\n state: () => scratchpadStore.current,\n pinned: () => scratchpadStore.pinned,\n close: () => setScratchpadStore({ current: null, pinned: false }),\n }\n}\n\n/**\n * Function for the PARSER/STORE — mutates the scratchpad state.\n * Called from the SSE callback, no bus needed.\n *\n * @example\n * // In your SSE parser callback — ONE LINE\n * onScratchpad: (data) => dispatchScratchpad(data as ScratchpadEvent)\n */\nexport function dispatchScratchpad(event: ScratchpadEvent): void {\n // DX1: lifecycle logging\n if (event.action === 'create') {\n console.info(\n `%c[MCP-UI] dispatchScratchpad%c create id=${event.id} sections=${event.sections?.length || 0} status=${event.status || 'loading'}${event.pinned ? ' pinned' : ''}`,\n 'color: #10b981; font-weight: bold', 'color: inherit'\n )\n setScratchpadStore({\n current: {\n id: event.id,\n title: event.title || '',\n sections: event.sections || [],\n filters: event.filters || {},\n preview: event.preview,\n agentMessages: event.agentMessages || [],\n status: event.status || 'loading',\n previewEndpoint: (event as any).previewEndpoint,\n previewDebounce: (event as any).previewDebounce,\n previewMethod: (event as any).previewMethod,\n previewHeaders: (event as any).previewHeaders,\n turn: (event as any).turn,\n totalTurns: (event as any).totalTurns,\n turnHistory: (event as any).turnHistory,\n },\n pinned: event.pinned || false,\n })\n } else if (event.action === 'update') {\n console.info(\n `%c[MCP-UI] dispatchScratchpad%c update id=${event.id} sectionMode=${event.sectionMode || 'replace'} sections=${event.sections?.length || 0} status=${event.status || '-'}`,\n 'color: #3b82f6; font-weight: bold', 'color: inherit'\n )\n setScratchpadStore(produce((s) => {\n if (!s.current || s.current.id !== event.id) {\n console.warn(`[MCP-UI] dispatchScratchpad: update for id=${event.id} but current is ${s.current?.id || 'null'}. Ignoring.`)\n return\n }\n\n if (event.sections) {\n const mode = event.sectionMode || 'replace'\n if (mode === 'replace') {\n s.current.sections = event.sections\n } else if (mode === 'append') {\n s.current.sections = [...s.current.sections, ...event.sections]\n } else if (mode === 'upsert') {\n let matchCount = 0\n for (const incoming of event.sections) {\n const idx = s.current.sections.findIndex((sec: ScratchpadSection) => sec.id === incoming.id)\n if (idx >= 0) {\n s.current.sections[idx] = incoming\n matchCount++\n } else {\n s.current.sections.push(incoming)\n }\n }\n if (matchCount === 0 && event.sections.length > 0) {\n console.warn(\n `[MCP-UI] dispatchScratchpad: sectionMode='upsert' but no IDs matched. ` +\n `Incoming: [${event.sections.map((s: ScratchpadSection) => s.id).join(', ')}] ` +\n `Existing: [${s.current.sections.map((s: ScratchpadSection) => s.id).join(', ')}]. All appended.`\n )\n }\n }\n }\n if (event.agentMessages) s.current.agentMessages = event.agentMessages\n if (event.status) s.current.status = event.status\n if (event.filters) s.current.filters = event.filters\n if (event.preview) s.current.preview = event.preview\n if (event.pinned != null) s.pinned = event.pinned\n if ((event as any).turnHistory) s.current.turnHistory = (event as any).turnHistory\n if ((event as any).turn != null) s.current.turn = (event as any).turn\n }))\n } else if (event.action === 'close') {\n console.info(`%c[MCP-UI] dispatchScratchpad%c close id=${event.id}`, 'color: #6b7280; font-weight: bold', 'color: inherit')\n setScratchpadStore({ current: null, pinned: false })\n }\n}\n"],"names":["_a","s"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"scratchpad-store.js","sources":["../../src/stores/scratchpad-store.tsx"],"sourcesContent":["/**\n * Scratchpad Store — reactive state for HITL scratchpad\n *\n * @experimental\n *\n * **v5.2.0 :** the store is now a factory (`createScratchpadStore()`) with a\n * module-level singleton kept as default. Two consumption modes :\n *\n * 1. **Singleton mode (default, zero-config)** — `dispatchScratchpad(event)` +\n * `useScratchpadState()` read/write the module singleton. This is the v4.x\n * path and keeps working unchanged.\n *\n * 2. **Multi-instance mode** — wrap a subtree in `<ScratchpadStoreProvider>`\n * (it creates a scoped `ScratchpadStoreHandle` internally, or use your own\n * via the `store` prop). `useScratchpadState()` auto-detects the context\n * and reads from it; `ScratchpadPanel` mounted inside the provider reads\n * the scoped store. Non-reactive callers (SSE parsers) should pass the\n * handle explicitly — do NOT try to reach context from a non-reactive\n * scope.\n */\n\nimport { createContext, useContext, type ParentComponent, type JSX } from 'solid-js'\nimport { createStore, produce } from 'solid-js/store'\nimport type { ScratchpadState, ScratchpadEvent, ScratchpadSection } from '../types/chat-bus'\n\n// ─── Handle shape ─────────────────────────────────────────────\n\nexport interface ScratchpadStoreHandle {\n /** Mutate the store from an SSE/parser callback. */\n dispatch: (event: ScratchpadEvent) => void\n /** Reactive accessor for the current scratchpad state (null when closed). */\n state: () => ScratchpadState | null\n /** Reactive accessor for the pinned flag. */\n pinned: () => boolean\n /** Close the scratchpad (equivalent to dispatching an action='close'). */\n close: () => void\n}\n\n// ─── Factory ──────────────────────────────────────────────────\n\n/**\n * Create an isolated scratchpad store instance.\n *\n * Use this when you need two or more scratchpads live at the same time\n * (e.g. chat scratchpad + admin dashboard scratchpad). Pair with\n * `<ScratchpadStoreProvider store={...}>` to scope a SolidJS subtree.\n *\n * @experimental\n * @since v5.2.0\n */\nexport function createScratchpadStore(): ScratchpadStoreHandle {\n const [scratchpadStore, setScratchpadStore] = createStore<{\n current: ScratchpadState | null\n pinned: boolean\n }>({ current: null, pinned: false })\n\n const dispatch = (event: ScratchpadEvent): void => {\n if (event.action === 'create') {\n console.info(\n `%c[MCP-UI] dispatchScratchpad%c create id=${event.id} sections=${event.sections?.length || 0} status=${event.status || 'loading'}${event.pinned ? ' pinned' : ''}`,\n 'color: #10b981; font-weight: bold',\n 'color: inherit'\n )\n setScratchpadStore({\n current: {\n id: event.id,\n title: event.title || '',\n sections: event.sections || [],\n filters: event.filters || {},\n preview: event.preview,\n agentMessages: event.agentMessages || [],\n status: event.status || 'loading',\n previewEndpoint: (event as any).previewEndpoint,\n previewDebounce: (event as any).previewDebounce,\n previewMethod: (event as any).previewMethod,\n previewHeaders: (event as any).previewHeaders,\n turn: (event as any).turn,\n totalTurns: (event as any).totalTurns,\n turnHistory: (event as any).turnHistory,\n },\n pinned: event.pinned || false,\n })\n } else if (event.action === 'update') {\n console.info(\n `%c[MCP-UI] dispatchScratchpad%c update id=${event.id} sectionMode=${event.sectionMode || 'replace'} sections=${event.sections?.length || 0} status=${event.status || '-'}`,\n 'color: #3b82f6; font-weight: bold',\n 'color: inherit'\n )\n setScratchpadStore(\n produce((s) => {\n if (!s.current || s.current.id !== event.id) {\n console.warn(\n `[MCP-UI] dispatchScratchpad: update for id=${event.id} but current is ${s.current?.id || 'null'}. Ignoring.`\n )\n return\n }\n\n if (event.sections) {\n const mode = event.sectionMode || 'replace'\n if (mode === 'replace') {\n s.current.sections = event.sections\n } else if (mode === 'append') {\n s.current.sections = [...s.current.sections, ...event.sections]\n } else if (mode === 'upsert') {\n let matchCount = 0\n for (const incoming of event.sections) {\n const idx = s.current.sections.findIndex(\n (sec: ScratchpadSection) => sec.id === incoming.id\n )\n if (idx >= 0) {\n s.current.sections[idx] = incoming\n matchCount++\n } else {\n s.current.sections.push(incoming)\n }\n }\n if (matchCount === 0 && event.sections.length > 0) {\n console.warn(\n `[MCP-UI] dispatchScratchpad: sectionMode='upsert' but no IDs matched. ` +\n `Incoming: [${event.sections.map((s: ScratchpadSection) => s.id).join(', ')}] ` +\n `Existing: [${s.current.sections.map((s: ScratchpadSection) => s.id).join(', ')}]. All appended.`\n )\n }\n }\n }\n if (event.agentMessages) s.current.agentMessages = event.agentMessages\n if (event.status) s.current.status = event.status\n if (event.filters) s.current.filters = event.filters\n if (event.preview) s.current.preview = event.preview\n if (event.pinned != null) s.pinned = event.pinned\n if ((event as any).turnHistory) s.current.turnHistory = (event as any).turnHistory\n if ((event as any).turn != null) s.current.turn = (event as any).turn\n })\n )\n } else if (event.action === 'close') {\n console.info(\n `%c[MCP-UI] dispatchScratchpad%c close id=${event.id}`,\n 'color: #6b7280; font-weight: bold',\n 'color: inherit'\n )\n setScratchpadStore({ current: null, pinned: false })\n }\n }\n\n return {\n dispatch,\n state: () => scratchpadStore.current,\n pinned: () => scratchpadStore.pinned,\n close: () => setScratchpadStore({ current: null, pinned: false }),\n }\n}\n\n// ─── Module-level singleton (v4.x-compatible default) ─────────\n\nconst defaultStore: ScratchpadStoreHandle = createScratchpadStore()\n\n/**\n * Function for the PARSER/STORE — mutates the **module-level singleton**\n * scratchpad state. Use this when you only need one scratchpad at a time\n * (single-instance consumer, the v4.x pattern).\n *\n * For multi-instance scenarios, prefer `createScratchpadStore()` and pass the\n * handle around explicitly.\n *\n * @example\n * // In your SSE parser callback — ONE LINE\n * onScratchpad: (data) => dispatchScratchpad(data as ScratchpadEvent)\n */\nexport function dispatchScratchpad(event: ScratchpadEvent): void {\n defaultStore.dispatch(event)\n}\n\n// ─── Context (v5.2.0) ─────────────────────────────────────────\n\n/**\n * Context for a scoped scratchpad store. Populated by\n * `<ScratchpadStoreProvider>`. Read by `useScratchpadState()` with automatic\n * fallback to the module-level singleton when the context is absent.\n *\n * @experimental\n * @since v5.2.0\n */\nexport const ScratchpadStoreContext = createContext<ScratchpadStoreHandle | undefined>(undefined)\n\n/**\n * Provide a scoped `ScratchpadStoreHandle` to a SolidJS subtree. Children\n * reading via `useScratchpadState()` or rendering a `<ScratchpadPanel>` will\n * bind to this store instead of the module singleton.\n *\n * If no `store` prop is passed, a fresh store is created for the provider's\n * lifetime. Pass `store` explicitly when you need the handle outside the\n * tree (e.g. in an SSE parser that lives at the app root).\n *\n * @experimental\n * @since v5.2.0\n *\n * @example\n * const chatStore = createScratchpadStore()\n * const adminStore = createScratchpadStore()\n *\n * <ScratchpadStoreProvider store={chatStore}>\n * <ChatInterface />\n * </ScratchpadStoreProvider>\n * <ScratchpadStoreProvider store={adminStore}>\n * <AdminDashboard />\n * </ScratchpadStoreProvider>\n */\nexport const ScratchpadStoreProvider: ParentComponent<{\n store?: ScratchpadStoreHandle\n}> = (props): JSX.Element => {\n const store = props.store ?? createScratchpadStore()\n return (\n <ScratchpadStoreContext.Provider value={store}>{props.children}</ScratchpadStoreContext.Provider>\n )\n}\n\n// ─── Reactive hook (context-aware) ────────────────────────────\n\n/**\n * Hook for the COMPONENT — reads the scratchpad state reactively.\n *\n * **v5.2.0 :** if called inside a `<ScratchpadStoreProvider>`, reads the\n * scoped handle; otherwise falls back to the module singleton. Old v4.x\n * consumers keep working unchanged.\n *\n * @example\n * const { state, pinned, close } = useScratchpadState()\n * <Show when={state()}>\n * <ScratchpadPanel state={state()!} pinned={pinned()} onClose={close} />\n * </Show>\n */\nexport function useScratchpadState(): {\n state: () => ScratchpadState | null\n pinned: () => boolean\n close: () => void\n} {\n const scoped = useContext(ScratchpadStoreContext)\n const handle = scoped ?? defaultStore\n return {\n state: handle.state,\n pinned: handle.pinned,\n close: handle.close,\n }\n}\n"],"names":["createScratchpadStore","scratchpadStore","setScratchpadStore","createStore","current","pinned","dispatch","event","action","console","info","id","sections","length","status","title","filters","preview","agentMessages","previewEndpoint","previewDebounce","previewMethod","previewHeaders","turn","totalTurns","turnHistory","sectionMode","produce","s","warn","mode","matchCount","incoming","idx","findIndex","sec","push","map","join","state","close","defaultStore","dispatchScratchpad","ScratchpadStoreContext","createContext","undefined","ScratchpadStoreProvider","props","store","_$createComponent","Provider","value","children","useScratchpadState","scoped","useContext","handle"],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const web = require("solid-js/web");
|
|
4
|
+
const solidJs = require("solid-js");
|
|
5
|
+
const store = require("solid-js/store");
|
|
6
|
+
function createServerCapabilitiesStore() {
|
|
7
|
+
const [state, setState] = store.createStore({
|
|
8
|
+
info: null
|
|
9
|
+
});
|
|
10
|
+
return {
|
|
11
|
+
set: (info) => setState("info", info),
|
|
12
|
+
info: () => state.info,
|
|
13
|
+
capabilities: () => {
|
|
14
|
+
var _a;
|
|
15
|
+
return ((_a = state.info) == null ? void 0 : _a.capabilities) ?? null;
|
|
16
|
+
},
|
|
17
|
+
serverInfo: () => {
|
|
18
|
+
var _a;
|
|
19
|
+
return ((_a = state.info) == null ? void 0 : _a.serverInfo) ?? null;
|
|
20
|
+
},
|
|
21
|
+
protocolVersion: () => {
|
|
22
|
+
var _a;
|
|
23
|
+
return ((_a = state.info) == null ? void 0 : _a.protocolVersion) ?? null;
|
|
24
|
+
},
|
|
25
|
+
hasCapability: (key) => {
|
|
26
|
+
var _a, _b;
|
|
27
|
+
return Boolean((_b = (_a = state.info) == null ? void 0 : _a.capabilities) == null ? void 0 : _b[key]);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const defaultStore = createServerCapabilitiesStore();
|
|
32
|
+
function setServerCapabilities(info) {
|
|
33
|
+
defaultStore.set(info);
|
|
34
|
+
}
|
|
35
|
+
const ServerCapabilitiesContext = solidJs.createContext(void 0);
|
|
36
|
+
const ServerCapabilitiesProvider = (props) => {
|
|
37
|
+
const store2 = props.store ?? createServerCapabilitiesStore();
|
|
38
|
+
return web.createComponent(ServerCapabilitiesContext.Provider, {
|
|
39
|
+
value: store2,
|
|
40
|
+
get children() {
|
|
41
|
+
return props.children;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
function useServerCapabilities() {
|
|
46
|
+
const scoped = solidJs.useContext(ServerCapabilitiesContext);
|
|
47
|
+
const handle = scoped ?? defaultStore;
|
|
48
|
+
return {
|
|
49
|
+
info: handle.info,
|
|
50
|
+
capabilities: handle.capabilities,
|
|
51
|
+
serverInfo: handle.serverInfo,
|
|
52
|
+
protocolVersion: handle.protocolVersion,
|
|
53
|
+
hasCapability: handle.hasCapability
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
exports.ServerCapabilitiesContext = ServerCapabilitiesContext;
|
|
57
|
+
exports.ServerCapabilitiesProvider = ServerCapabilitiesProvider;
|
|
58
|
+
exports.createServerCapabilitiesStore = createServerCapabilitiesStore;
|
|
59
|
+
exports.setServerCapabilities = setServerCapabilities;
|
|
60
|
+
exports.useServerCapabilities = useServerCapabilities;
|
|
61
|
+
//# sourceMappingURL=server-capabilities-store.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-capabilities-store.cjs","sources":["../../src/stores/server-capabilities-store.tsx"],"sourcesContent":["/**\n * Server Capabilities Store — reactive snapshot of the MCP `initialize`\n * response echoed by the server.\n *\n * @experimental\n * @since v5.3.0\n *\n * mcp-ui doesn't speak MCP protocol directly — the consumer's transport\n * layer (stdio child process, HTTP/SSE client, ...) parses the\n * `initialize` JSON-RPC response and pushes the relevant fields into this\n * store via `setServerCapabilities(info)`. Components then read\n * reactively via `useServerCapabilities()` to gate behavior :\n *\n * ```tsx\n * const { capabilities } = useServerCapabilities()\n * <Show when={capabilities()?.tools?.listChanged}>\n * <ToolListSubscriber />\n * </Show>\n * ```\n *\n * ## Two consumption modes (mirrors `scratchpad-store`)\n *\n * 1. **Singleton mode (default)** — `setServerCapabilities(info)` mutates\n * the module-level singleton. `useServerCapabilities()` reads from it.\n * Use for single-MCP-server consumers (the common case).\n *\n * 2. **Multi-instance mode** — wrap a subtree in\n * `<ServerCapabilitiesProvider>` to scope a separate handle. Pass\n * `store={createServerCapabilitiesStore()}` explicitly when you need to\n * drive it from a non-reactive scope (e.g. a transport adapter living\n * at the app root).\n *\n * ## Note on `elicitation`\n *\n * Per MCP spec 2025-06-18, `elicitation` is a **CLIENT** capability, not\n * a server one. Servers do not declare it. If you need to gate\n * `<ElicitationForm>` rendering on whether the connected client *itself*\n * supports elicitation — that's a separate concern (your own state, set\n * by your transport layer based on its own configuration).\n */\n\nimport { createContext, useContext, type ParentComponent, type JSX } from 'solid-js'\nimport { createStore } from 'solid-js/store'\n\n// ─── Types ────────────────────────────────────────────────────\n\n/**\n * Server capabilities object as advertised in the MCP `initialize` response.\n * Mirrors the spec 2025-06-18 `ServerCapabilities` shape with permissive\n * `experimental` for forward compatibility.\n */\nexport interface ServerCapabilities {\n experimental?: Record<string, unknown>\n logging?: Record<string, never>\n tools?: { listChanged?: boolean }\n prompts?: { listChanged?: boolean }\n resources?: { listChanged?: boolean; subscribe?: boolean }\n completions?: Record<string, never>\n}\n\n/**\n * Subset of the MCP `initialize` response relevant to the UI layer.\n * Consumers may extend this via the `experimental` field.\n */\nexport interface ServerInitializeInfo {\n protocolVersion: string\n serverInfo: { name: string; version: string; title?: string; [key: string]: unknown }\n capabilities: ServerCapabilities\n instructions?: string\n}\n\n// ─── Handle ───────────────────────────────────────────────────\n\nexport interface ServerCapabilitiesStoreHandle {\n /** Push a fresh `initialize` snapshot into the store, or clear with `null`. */\n set: (info: ServerInitializeInfo | null) => void\n /** Reactive accessor for the full info (null when no initialize received). */\n info: () => ServerInitializeInfo | null\n /** Reactive accessor for just the `capabilities` field. */\n capabilities: () => ServerCapabilities | null\n /** Reactive accessor for just the `serverInfo` field. */\n serverInfo: () => ServerInitializeInfo['serverInfo'] | null\n /** Reactive accessor for the protocol version string. */\n protocolVersion: () => string | null\n /**\n * Helper : returns true if the server advertised the named capability key\n * with a truthy value (i.e. the key is present, even as an empty object).\n */\n hasCapability: (key: keyof ServerCapabilities) => boolean\n}\n\n// ─── Factory ──────────────────────────────────────────────────\n\n/**\n * Create an isolated server-capabilities store instance.\n *\n * Use this when you need to track multiple MCP servers in parallel (rare),\n * or to drive the store from a non-reactive transport adapter. Pair with\n * `<ServerCapabilitiesProvider store={...}>` to scope a SolidJS subtree.\n *\n * @experimental\n * @since v5.3.0\n */\nexport function createServerCapabilitiesStore(): ServerCapabilitiesStoreHandle {\n const [state, setState] = createStore<{ info: ServerInitializeInfo | null }>({ info: null })\n\n return {\n set: (info) => setState('info', info),\n info: () => state.info,\n capabilities: () => state.info?.capabilities ?? null,\n serverInfo: () => state.info?.serverInfo ?? null,\n protocolVersion: () => state.info?.protocolVersion ?? null,\n hasCapability: (key) => Boolean(state.info?.capabilities?.[key]),\n }\n}\n\n// ─── Module-level singleton ───────────────────────────────────\n\nconst defaultStore: ServerCapabilitiesStoreHandle = createServerCapabilitiesStore()\n\n/**\n * Push the parsed MCP `initialize` response into the module-level singleton\n * store. Pass `null` to clear (e.g. on disconnect / server change).\n *\n * @experimental\n * @since v5.3.0\n *\n * @example\n * // In your transport adapter, after receiving the initialize response :\n * setServerCapabilities({\n * protocolVersion: response.result.protocolVersion,\n * serverInfo: response.result.serverInfo,\n * capabilities: response.result.capabilities,\n * instructions: response.result.instructions,\n * })\n */\nexport function setServerCapabilities(info: ServerInitializeInfo | null): void {\n defaultStore.set(info)\n}\n\n// ─── Context ──────────────────────────────────────────────────\n\n/**\n * Context for a scoped server-capabilities store. Populated by\n * `<ServerCapabilitiesProvider>`. Read by `useServerCapabilities()` with\n * automatic fallback to the module-level singleton when absent.\n *\n * @experimental\n * @since v5.3.0\n */\nexport const ServerCapabilitiesContext = createContext<ServerCapabilitiesStoreHandle | undefined>(\n undefined\n)\n\n/**\n * Provide a scoped `ServerCapabilitiesStoreHandle` to a SolidJS subtree.\n * Children calling `useServerCapabilities()` bind to this store instead of\n * the module singleton.\n *\n * If no `store` prop is passed, a fresh store is created for the provider's\n * lifetime. Pass `store` explicitly when you need the handle outside the\n * tree (e.g. in a transport adapter living at the app root).\n *\n * @experimental\n * @since v5.3.0\n */\nexport const ServerCapabilitiesProvider: ParentComponent<{\n store?: ServerCapabilitiesStoreHandle\n}> = (props): JSX.Element => {\n const store = props.store ?? createServerCapabilitiesStore()\n return (\n <ServerCapabilitiesContext.Provider value={store}>\n {props.children}\n </ServerCapabilitiesContext.Provider>\n )\n}\n\n// ─── Reactive hook ────────────────────────────────────────────\n\n/**\n * Hook for components — reads the server capabilities reactively.\n *\n * If called inside a `<ServerCapabilitiesProvider>`, reads the scoped\n * handle; otherwise falls back to the module singleton.\n *\n * @experimental\n * @since v5.3.0\n *\n * @example\n * const { capabilities, serverInfo, hasCapability } = useServerCapabilities()\n *\n * <Show when={capabilities()}>\n * <p>Connected to {serverInfo()?.name} v{serverInfo()?.version}</p>\n * <Show when={hasCapability('tools')}>\n * <ToolPalette />\n * </Show>\n * </Show>\n */\nexport function useServerCapabilities(): {\n info: () => ServerInitializeInfo | null\n capabilities: () => ServerCapabilities | null\n serverInfo: () => ServerInitializeInfo['serverInfo'] | null\n protocolVersion: () => string | null\n hasCapability: (key: keyof ServerCapabilities) => boolean\n} {\n const scoped = useContext(ServerCapabilitiesContext)\n const handle = scoped ?? defaultStore\n return {\n info: handle.info,\n capabilities: handle.capabilities,\n serverInfo: handle.serverInfo,\n protocolVersion: handle.protocolVersion,\n hasCapability: handle.hasCapability,\n }\n}\n"],"names":["createServerCapabilitiesStore","state","setState","createStore","info","set","capabilities","serverInfo","protocolVersion","hasCapability","key","Boolean","defaultStore","setServerCapabilities","ServerCapabilitiesContext","createContext","undefined","ServerCapabilitiesProvider","props","store","_$createComponent","Provider","value","children","useServerCapabilities","scoped","useContext","handle"],"mappings":";;;;;AAuGO,SAASA,gCAA+D;AAC7E,QAAM,CAACC,OAAOC,QAAQ,IAAIC,kBAAmD;AAAA,IAAEC,MAAM;AAAA,EAAA,CAAM;AAE3F,SAAO;AAAA,IACLC,KAAMD,CAAAA,SAASF,SAAS,QAAQE,IAAI;AAAA,IACpCA,MAAMA,MAAMH,MAAMG;AAAAA,IAClBE,cAAcA,MAAAA;;AAAML,0BAAMG,SAANH,mBAAYK,iBAAgB;AAAA;AAAA,IAChDC,YAAYA,MAAAA;;AAAMN,0BAAMG,SAANH,mBAAYM,eAAc;AAAA;AAAA,IAC5CC,iBAAiBA,MAAAA;;AAAMP,0BAAMG,SAANH,mBAAYO,oBAAmB;AAAA;AAAA,IACtDC,eAAgBC,CAAAA;;AAAQC,sBAAQV,iBAAMG,SAANH,mBAAYK,iBAAZL,mBAA2BS,IAAI;AAAA;AAAA,EAAA;AAEnE;AAIA,MAAME,eAA8CZ,8BAAAA;AAkB7C,SAASa,sBAAsBT,MAAyC;AAC7EQ,eAAaP,IAAID,IAAI;AACvB;AAYO,MAAMU,4BAA4BC,QAAAA,cACvCC,MACF;AAcO,MAAMC,6BAERA,CAACC,UAAuB;AAC3B,QAAMC,SAAQD,MAAMC,SAASnB,8BAAAA;AAC7B,SAAAoB,IAAAA,gBACGN,0BAA0BO,UAAQ;AAAA,IAACC,OAAOH;AAAAA,IAAK,IAAAI,WAAA;AAAA,aAC7CL,MAAMK;AAAAA,IAAQ;AAAA,EAAA,CAAA;AAGrB;AAuBO,SAASC,wBAMd;AACA,QAAMC,SAASC,QAAAA,WAAWZ,yBAAyB;AACnD,QAAMa,SAASF,UAAUb;AACzB,SAAO;AAAA,IACLR,MAAMuB,OAAOvB;AAAAA,IACbE,cAAcqB,OAAOrB;AAAAA,IACrBC,YAAYoB,OAAOpB;AAAAA,IACnBC,iBAAiBmB,OAAOnB;AAAAA,IACxBC,eAAekB,OAAOlB;AAAAA,EAAAA;AAE1B;;;;;;"}
|