@seed-ship/mcp-ui-solid 3.0.1 → 3.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ScratchpadPanel.cjs +49 -17
- package/dist/components/ScratchpadPanel.cjs.map +1 -1
- package/dist/components/ScratchpadPanel.d.ts.map +1 -1
- package/dist/components/ScratchpadPanel.js +49 -17
- package/dist/components/ScratchpadPanel.js.map +1 -1
- package/dist/index.cjs +3 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/stores/scratchpad-store.cjs +90 -0
- package/dist/stores/scratchpad-store.cjs.map +1 -0
- package/dist/stores/scratchpad-store.d.ts +34 -0
- package/dist/stores/scratchpad-store.d.ts.map +1 -0
- package/dist/stores/scratchpad-store.js +90 -0
- package/dist/stores/scratchpad-store.js.map +1 -0
- package/package.json +1 -1
- package/src/components/ScratchpadPanel.tsx +46 -1
- package/src/index.ts +1 -0
- package/src/stores/scratchpad-store.ts +119 -0
- package/tsconfig.tsbuildinfo +1 -1
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAGH,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAA;AAGjG,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AACxD,OAAO,EAAE,0BAA0B,EAAE,MAAM,yCAAyC,CAAA;AACpF,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAA;AAGhE,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -22,6 +22,7 @@ import { ComponentToolbar } from "./components/ComponentToolbar.js";
|
|
|
22
22
|
import { ChatBusProvider, useChatBus } from "./hooks/useChatBus.js";
|
|
23
23
|
import { ChatPrompt } from "./components/ChatPrompt.js";
|
|
24
24
|
import { ScratchpadPanel } from "./components/ScratchpadPanel.js";
|
|
25
|
+
import { dispatchScratchpad, useScratchpadState } from "./stores/scratchpad-store.js";
|
|
25
26
|
import { GhostText, GhostTextInput } from "./components/GhostText.js";
|
|
26
27
|
import { AutocompleteDropdown } from "./components/AutocompleteDropdown.js";
|
|
27
28
|
import { AutocompleteFormField } from "./components/AutocompleteFormField.js";
|
|
@@ -70,6 +71,7 @@ export {
|
|
|
70
71
|
createGroqPlugin,
|
|
71
72
|
createRestPlugin,
|
|
72
73
|
createSupabasePlugin,
|
|
74
|
+
dispatchScratchpad,
|
|
73
75
|
evaluateCondition,
|
|
74
76
|
getIframeSandbox,
|
|
75
77
|
mergeScratchpadSections,
|
|
@@ -86,6 +88,7 @@ export {
|
|
|
86
88
|
useMCPActionSafe,
|
|
87
89
|
useModal,
|
|
88
90
|
useResize,
|
|
91
|
+
useScratchpadState,
|
|
89
92
|
useStreamingUI,
|
|
90
93
|
useToolAction,
|
|
91
94
|
validateComponent,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const store = require("solid-js/store");
|
|
4
|
+
const [scratchpadStore, setScratchpadStore] = store.createStore({ current: null, pinned: false });
|
|
5
|
+
function useScratchpadState() {
|
|
6
|
+
return {
|
|
7
|
+
state: () => scratchpadStore.current,
|
|
8
|
+
pinned: () => scratchpadStore.pinned,
|
|
9
|
+
close: () => setScratchpadStore({ current: null, pinned: false })
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function dispatchScratchpad(event) {
|
|
13
|
+
var _a, _b;
|
|
14
|
+
if (event.action === "create") {
|
|
15
|
+
console.info(
|
|
16
|
+
`%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" : ""}`,
|
|
17
|
+
"color: #10b981; font-weight: bold",
|
|
18
|
+
"color: inherit"
|
|
19
|
+
);
|
|
20
|
+
setScratchpadStore({
|
|
21
|
+
current: {
|
|
22
|
+
id: event.id,
|
|
23
|
+
title: event.title || "",
|
|
24
|
+
sections: event.sections || [],
|
|
25
|
+
filters: event.filters || {},
|
|
26
|
+
preview: event.preview,
|
|
27
|
+
agentMessages: event.agentMessages || [],
|
|
28
|
+
status: event.status || "loading",
|
|
29
|
+
previewEndpoint: event.previewEndpoint,
|
|
30
|
+
previewDebounce: event.previewDebounce,
|
|
31
|
+
previewMethod: event.previewMethod,
|
|
32
|
+
previewHeaders: event.previewHeaders,
|
|
33
|
+
turn: event.turn,
|
|
34
|
+
totalTurns: event.totalTurns,
|
|
35
|
+
turnHistory: event.turnHistory
|
|
36
|
+
},
|
|
37
|
+
pinned: event.pinned || false
|
|
38
|
+
});
|
|
39
|
+
} else if (event.action === "update") {
|
|
40
|
+
console.info(
|
|
41
|
+
`%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 || "-"}`,
|
|
42
|
+
"color: #3b82f6; font-weight: bold",
|
|
43
|
+
"color: inherit"
|
|
44
|
+
);
|
|
45
|
+
setScratchpadStore(store.produce((s) => {
|
|
46
|
+
var _a2;
|
|
47
|
+
if (!s.current || s.current.id !== event.id) {
|
|
48
|
+
console.warn(`[MCP-UI] dispatchScratchpad: update for id=${event.id} but current is ${((_a2 = s.current) == null ? void 0 : _a2.id) || "null"}. Ignoring.`);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (event.sections) {
|
|
52
|
+
const mode = event.sectionMode || "replace";
|
|
53
|
+
if (mode === "replace") {
|
|
54
|
+
s.current.sections = event.sections;
|
|
55
|
+
} else if (mode === "append") {
|
|
56
|
+
s.current.sections = [...s.current.sections, ...event.sections];
|
|
57
|
+
} else if (mode === "upsert") {
|
|
58
|
+
let matchCount = 0;
|
|
59
|
+
for (const incoming of event.sections) {
|
|
60
|
+
const idx = s.current.sections.findIndex((sec) => sec.id === incoming.id);
|
|
61
|
+
if (idx >= 0) {
|
|
62
|
+
s.current.sections[idx] = incoming;
|
|
63
|
+
matchCount++;
|
|
64
|
+
} else {
|
|
65
|
+
s.current.sections.push(incoming);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (matchCount === 0 && event.sections.length > 0) {
|
|
69
|
+
console.warn(
|
|
70
|
+
`[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.`
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (event.agentMessages) s.current.agentMessages = event.agentMessages;
|
|
76
|
+
if (event.status) s.current.status = event.status;
|
|
77
|
+
if (event.filters) s.current.filters = event.filters;
|
|
78
|
+
if (event.preview) s.current.preview = event.preview;
|
|
79
|
+
if (event.pinned != null) s.pinned = event.pinned;
|
|
80
|
+
if (event.turnHistory) s.current.turnHistory = event.turnHistory;
|
|
81
|
+
if (event.turn != null) s.current.turn = event.turn;
|
|
82
|
+
}));
|
|
83
|
+
} else if (event.action === "close") {
|
|
84
|
+
console.info(`%c[MCP-UI] dispatchScratchpad%c close id=${event.id}`, "color: #6b7280; font-weight: bold", "color: inherit");
|
|
85
|
+
setScratchpadStore({ current: null, pinned: false });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
exports.dispatchScratchpad = dispatchScratchpad;
|
|
89
|
+
exports.useScratchpadState = useScratchpadState;
|
|
90
|
+
//# sourceMappingURL=scratchpad-store.cjs.map
|
|
@@ -0,0 +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\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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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scratchpad Store — singleton reactive state for HITL scratchpad
|
|
3
|
+
* v3.0.3: Eliminates ChatBus relay chain race condition
|
|
4
|
+
*
|
|
5
|
+
* @experimental
|
|
6
|
+
*
|
|
7
|
+
* Parser calls dispatchScratchpad() → store updates → ScratchpadPanel reads reactively.
|
|
8
|
+
* Zero bus, zero relay, zero race condition.
|
|
9
|
+
*/
|
|
10
|
+
import type { ScratchpadState, ScratchpadEvent } from '../types/chat-bus';
|
|
11
|
+
/**
|
|
12
|
+
* Hook for the COMPONENT — reads the scratchpad state reactively.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* const { state, pinned, close } = useScratchpadState()
|
|
16
|
+
* <Show when={state()}>
|
|
17
|
+
* <ScratchpadPanel state={state()!} pinned={pinned()} onClose={close} />
|
|
18
|
+
* </Show>
|
|
19
|
+
*/
|
|
20
|
+
export declare function useScratchpadState(): {
|
|
21
|
+
state: () => ScratchpadState | null;
|
|
22
|
+
pinned: () => boolean;
|
|
23
|
+
close: () => void;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Function for the PARSER/STORE — mutates the scratchpad state.
|
|
27
|
+
* Called from the SSE callback, no bus needed.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* // In your SSE parser callback — ONE LINE
|
|
31
|
+
* onScratchpad: (data) => dispatchScratchpad(data as ScratchpadEvent)
|
|
32
|
+
*/
|
|
33
|
+
export declare function dispatchScratchpad(event: ScratchpadEvent): void;
|
|
34
|
+
//# sourceMappingURL=scratchpad-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scratchpad-store.d.ts","sourceRoot":"","sources":["../../src/stores/scratchpad-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAqB,MAAM,mBAAmB,CAAA;AAO5F;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB;;;;EAMjC;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CA2E/D"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { createStore, produce } from "solid-js/store";
|
|
2
|
+
const [scratchpadStore, setScratchpadStore] = createStore({ current: null, pinned: false });
|
|
3
|
+
function useScratchpadState() {
|
|
4
|
+
return {
|
|
5
|
+
state: () => scratchpadStore.current,
|
|
6
|
+
pinned: () => scratchpadStore.pinned,
|
|
7
|
+
close: () => setScratchpadStore({ current: null, pinned: false })
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function dispatchScratchpad(event) {
|
|
11
|
+
var _a, _b;
|
|
12
|
+
if (event.action === "create") {
|
|
13
|
+
console.info(
|
|
14
|
+
`%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" : ""}`,
|
|
15
|
+
"color: #10b981; font-weight: bold",
|
|
16
|
+
"color: inherit"
|
|
17
|
+
);
|
|
18
|
+
setScratchpadStore({
|
|
19
|
+
current: {
|
|
20
|
+
id: event.id,
|
|
21
|
+
title: event.title || "",
|
|
22
|
+
sections: event.sections || [],
|
|
23
|
+
filters: event.filters || {},
|
|
24
|
+
preview: event.preview,
|
|
25
|
+
agentMessages: event.agentMessages || [],
|
|
26
|
+
status: event.status || "loading",
|
|
27
|
+
previewEndpoint: event.previewEndpoint,
|
|
28
|
+
previewDebounce: event.previewDebounce,
|
|
29
|
+
previewMethod: event.previewMethod,
|
|
30
|
+
previewHeaders: event.previewHeaders,
|
|
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
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export {
|
|
87
|
+
dispatchScratchpad,
|
|
88
|
+
useScratchpadState
|
|
89
|
+
};
|
|
90
|
+
//# sourceMappingURL=scratchpad-store.js.map
|
|
@@ -0,0 +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\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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"}
|
package/package.json
CHANGED
|
@@ -63,10 +63,49 @@ export const ScratchpadPanel: Component<ScratchpadPanelProps> = (props) => {
|
|
|
63
63
|
console.log(`[ScratchpadPanel:${props.state.id}] ${event}`, data || '')
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
// ─── DX1: Proactive console messages (always, not just debug) ───
|
|
67
|
+
|
|
68
|
+
const VALID_TRANSITIONS: Record<string, string[]> = {
|
|
69
|
+
loading: ['processing', 'waiting_human', 'error'],
|
|
70
|
+
waiting_human: ['processing', 'ready', 'complete', 'error'],
|
|
71
|
+
processing: ['ready', 'complete', 'error', 'waiting_human'],
|
|
72
|
+
ready: ['processing', 'complete', 'error', 'waiting_human'],
|
|
73
|
+
complete: [],
|
|
74
|
+
error: ['processing', 'ready', 'waiting_human'],
|
|
75
|
+
}
|
|
76
|
+
let prevStatus = props.state.status
|
|
77
|
+
|
|
78
|
+
// Etape 1: create log
|
|
79
|
+
console.info(
|
|
80
|
+
`%c[MCP-UI] Scratchpad created%c id=${props.state.id} sections=${props.state.sections?.length || 0} status=${props.state.status}${props.pinned ? ' pinned=true' : ''}`,
|
|
81
|
+
'color: #10b981; font-weight: bold', 'color: inherit'
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
// Etape 3: status transitions + Etape 4: auto-close info
|
|
85
|
+
createEffect(() => {
|
|
86
|
+
const newStatus = props.state.status
|
|
87
|
+
if (newStatus !== prevStatus) {
|
|
88
|
+
console.info(`%c[MCP-UI] Scratchpad status%c ${props.state.id}: ${prevStatus} → ${newStatus}`, 'color: #3b82f6; font-weight: bold', 'color: inherit')
|
|
89
|
+
if (!VALID_TRANSITIONS[prevStatus]?.includes(newStatus)) {
|
|
90
|
+
console.warn(`[MCP-UI] Scratchpad ${props.state.id}: unusual transition ${prevStatus} → ${newStatus}. Expected: ${VALID_TRANSITIONS[prevStatus]?.join(', ') || 'none (terminal)'}`)
|
|
91
|
+
}
|
|
92
|
+
prevStatus = newStatus
|
|
93
|
+
}
|
|
94
|
+
// Etape 4
|
|
95
|
+
if (props.autoCloseDelay && newStatus !== 'complete') {
|
|
96
|
+
console.info(`[MCP-UI] Scratchpad ${props.state.id}: autoCloseDelay=${props.autoCloseDelay}ms but status='${newStatus}' — auto-close will NOT trigger.`)
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
|
|
66
100
|
// Action aliases that auto-close the scratchpad
|
|
67
101
|
const CLOSE_ALIASES = new Set(['done', 'close', 'dismiss', 'validate', 'cancel', 'sufficient'])
|
|
68
102
|
|
|
69
103
|
const handleAction = (action: string, data?: unknown) => {
|
|
104
|
+
// DX1 Etape 5: action dispatch
|
|
105
|
+
console.info(`%c[MCP-UI] Action dispatched%c value='${action}' asyncAction=${!!props.asyncAction}`, 'color: #f59e0b; font-weight: bold', 'color: inherit')
|
|
106
|
+
if (!props.asyncAction && /^(try_alt:|retry|fetch|load)/.test(action)) {
|
|
107
|
+
console.warn(`[MCP-UI] ScratchpadPanel: action '${action}' looks async but asyncAction prop is not set. The button will NOT show a loading state.`)
|
|
108
|
+
}
|
|
70
109
|
debugLog('onAction', { action, asyncAction: props.asyncAction, data })
|
|
71
110
|
if (props.asyncAction && !CLOSE_ALIASES.has(action)) {
|
|
72
111
|
setLoadingAction(action)
|
|
@@ -503,6 +542,9 @@ const EmbeddedFormSection: Component<{
|
|
|
503
542
|
.filter(([, v]) => v !== undefined && v !== '' && !(Array.isArray(v) && v.length === 0))
|
|
504
543
|
)
|
|
505
544
|
|
|
545
|
+
// DX1 Etape 7: form submit log
|
|
546
|
+
console.info(`%c[MCP-UI] Form submitted%c section=${props.sectionId} fields=${Object.keys(values).join(',')}`, 'color: #8b5cf6; font-weight: bold', 'color: inherit')
|
|
547
|
+
|
|
506
548
|
if (props.onSubmit) {
|
|
507
549
|
props.onSubmit(props.sectionId, values)
|
|
508
550
|
} else {
|
|
@@ -849,7 +891,10 @@ const ErrorSectionRenderer: Component<{
|
|
|
849
891
|
const [showDetails, setShowDetails] = createSignal(false)
|
|
850
892
|
const data = () => {
|
|
851
893
|
const c = props.content as any
|
|
852
|
-
|
|
894
|
+
const d = { message: c?.message || 'Error', severity: c?.severity || 'error', retryAction: c?.retryAction, retryLabel: c?.retryLabel || 'Retry', details: c?.details, timestamp: c?.timestamp }
|
|
895
|
+
// DX1 Etape 8
|
|
896
|
+
console.info(`%c[MCP-UI] Error section rendered%c severity=${d.severity} retry=${!!d.retryAction}`, 'color: #ef4444; font-weight: bold', 'color: inherit')
|
|
897
|
+
return d
|
|
853
898
|
}
|
|
854
899
|
const isWarning = () => data().severity === 'warning'
|
|
855
900
|
|
package/src/index.ts
CHANGED
|
@@ -42,6 +42,7 @@ export { ComponentToolbar } from './components/ComponentToolbar'
|
|
|
42
42
|
export { ChatBusProvider, useChatBus } from './hooks/useChatBus'
|
|
43
43
|
export { ChatPrompt } from './components/ChatPrompt'
|
|
44
44
|
export { ScratchpadPanel } from './components/ScratchpadPanel'
|
|
45
|
+
export { dispatchScratchpad, useScratchpadState } from './stores/scratchpad-store'
|
|
45
46
|
|
|
46
47
|
// Autocomplete Components
|
|
47
48
|
export { GhostText, GhostTextInput } from './components/GhostText'
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scratchpad Store — singleton reactive state for HITL scratchpad
|
|
3
|
+
* v3.0.3: Eliminates ChatBus relay chain race condition
|
|
4
|
+
*
|
|
5
|
+
* @experimental
|
|
6
|
+
*
|
|
7
|
+
* Parser calls dispatchScratchpad() → store updates → ScratchpadPanel reads reactively.
|
|
8
|
+
* Zero bus, zero relay, zero race condition.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { createStore, produce } from 'solid-js/store'
|
|
12
|
+
import type { ScratchpadState, ScratchpadEvent, ScratchpadSection } from '../types/chat-bus'
|
|
13
|
+
|
|
14
|
+
const [scratchpadStore, setScratchpadStore] = createStore<{
|
|
15
|
+
current: ScratchpadState | null
|
|
16
|
+
pinned: boolean
|
|
17
|
+
}>({ current: null, pinned: false })
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Hook for the COMPONENT — reads the scratchpad state reactively.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* const { state, pinned, close } = useScratchpadState()
|
|
24
|
+
* <Show when={state()}>
|
|
25
|
+
* <ScratchpadPanel state={state()!} pinned={pinned()} onClose={close} />
|
|
26
|
+
* </Show>
|
|
27
|
+
*/
|
|
28
|
+
export function useScratchpadState() {
|
|
29
|
+
return {
|
|
30
|
+
state: () => scratchpadStore.current,
|
|
31
|
+
pinned: () => scratchpadStore.pinned,
|
|
32
|
+
close: () => setScratchpadStore({ current: null, pinned: false }),
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Function for the PARSER/STORE — mutates the scratchpad state.
|
|
38
|
+
* Called from the SSE callback, no bus needed.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* // In your SSE parser callback — ONE LINE
|
|
42
|
+
* onScratchpad: (data) => dispatchScratchpad(data as ScratchpadEvent)
|
|
43
|
+
*/
|
|
44
|
+
export function dispatchScratchpad(event: ScratchpadEvent): void {
|
|
45
|
+
// DX1: lifecycle logging
|
|
46
|
+
if (event.action === 'create') {
|
|
47
|
+
console.info(
|
|
48
|
+
`%c[MCP-UI] dispatchScratchpad%c create id=${event.id} sections=${event.sections?.length || 0} status=${event.status || 'loading'}${event.pinned ? ' pinned' : ''}`,
|
|
49
|
+
'color: #10b981; font-weight: bold', 'color: inherit'
|
|
50
|
+
)
|
|
51
|
+
setScratchpadStore({
|
|
52
|
+
current: {
|
|
53
|
+
id: event.id,
|
|
54
|
+
title: event.title || '',
|
|
55
|
+
sections: event.sections || [],
|
|
56
|
+
filters: event.filters || {},
|
|
57
|
+
preview: event.preview,
|
|
58
|
+
agentMessages: event.agentMessages || [],
|
|
59
|
+
status: event.status || 'loading',
|
|
60
|
+
previewEndpoint: (event as any).previewEndpoint,
|
|
61
|
+
previewDebounce: (event as any).previewDebounce,
|
|
62
|
+
previewMethod: (event as any).previewMethod,
|
|
63
|
+
previewHeaders: (event as any).previewHeaders,
|
|
64
|
+
turn: (event as any).turn,
|
|
65
|
+
totalTurns: (event as any).totalTurns,
|
|
66
|
+
turnHistory: (event as any).turnHistory,
|
|
67
|
+
},
|
|
68
|
+
pinned: event.pinned || false,
|
|
69
|
+
})
|
|
70
|
+
} else if (event.action === 'update') {
|
|
71
|
+
console.info(
|
|
72
|
+
`%c[MCP-UI] dispatchScratchpad%c update id=${event.id} sectionMode=${event.sectionMode || 'replace'} sections=${event.sections?.length || 0} status=${event.status || '-'}`,
|
|
73
|
+
'color: #3b82f6; font-weight: bold', 'color: inherit'
|
|
74
|
+
)
|
|
75
|
+
setScratchpadStore(produce((s) => {
|
|
76
|
+
if (!s.current || s.current.id !== event.id) {
|
|
77
|
+
console.warn(`[MCP-UI] dispatchScratchpad: update for id=${event.id} but current is ${s.current?.id || 'null'}. Ignoring.`)
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (event.sections) {
|
|
82
|
+
const mode = event.sectionMode || 'replace'
|
|
83
|
+
if (mode === 'replace') {
|
|
84
|
+
s.current.sections = event.sections
|
|
85
|
+
} else if (mode === 'append') {
|
|
86
|
+
s.current.sections = [...s.current.sections, ...event.sections]
|
|
87
|
+
} else if (mode === 'upsert') {
|
|
88
|
+
let matchCount = 0
|
|
89
|
+
for (const incoming of event.sections) {
|
|
90
|
+
const idx = s.current.sections.findIndex((sec: ScratchpadSection) => sec.id === incoming.id)
|
|
91
|
+
if (idx >= 0) {
|
|
92
|
+
s.current.sections[idx] = incoming
|
|
93
|
+
matchCount++
|
|
94
|
+
} else {
|
|
95
|
+
s.current.sections.push(incoming)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (matchCount === 0 && event.sections.length > 0) {
|
|
99
|
+
console.warn(
|
|
100
|
+
`[MCP-UI] dispatchScratchpad: sectionMode='upsert' but no IDs matched. ` +
|
|
101
|
+
`Incoming: [${event.sections.map((s: ScratchpadSection) => s.id).join(', ')}] ` +
|
|
102
|
+
`Existing: [${s.current.sections.map((s: ScratchpadSection) => s.id).join(', ')}]. All appended.`
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (event.agentMessages) s.current.agentMessages = event.agentMessages
|
|
108
|
+
if (event.status) s.current.status = event.status
|
|
109
|
+
if (event.filters) s.current.filters = event.filters
|
|
110
|
+
if (event.preview) s.current.preview = event.preview
|
|
111
|
+
if (event.pinned != null) s.pinned = event.pinned
|
|
112
|
+
if ((event as any).turnHistory) s.current.turnHistory = (event as any).turnHistory
|
|
113
|
+
if ((event as any).turn != null) s.current.turn = (event as any).turn
|
|
114
|
+
}))
|
|
115
|
+
} else if (event.action === 'close') {
|
|
116
|
+
console.info(`%c[MCP-UI] dispatchScratchpad%c close id=${event.id}`, 'color: #6b7280; font-weight: bold', 'color: inherit')
|
|
117
|
+
setScratchpadStore({ current: null, pinned: false })
|
|
118
|
+
}
|
|
119
|
+
}
|