@nimblebrain/synapse 0.1.4 → 0.2.1
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/README.md +7 -1
- package/dist/{chunk-JZC3VC2C.js → chunk-7KEYXJWD.js} +45 -13
- package/dist/chunk-7KEYXJWD.js.map +1 -0
- package/dist/{chunk-Q7OSHSGZ.cjs → chunk-Y4ZDNAYQ.cjs} +45 -13
- package/dist/chunk-Y4ZDNAYQ.cjs.map +1 -0
- package/dist/codegen/cli.cjs +1 -1
- package/dist/codegen/cli.js +1 -1
- package/dist/codegen/index.d.cts +1 -1
- package/dist/codegen/index.d.ts +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/react/index.cjs +22 -11
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +40 -2
- package/dist/react/index.d.ts +40 -2
- package/dist/react/index.js +22 -12
- package/dist/react/index.js.map +1 -1
- package/dist/{server-SEI7XI3B.cjs → server-3BDZ5S72.cjs} +26 -26
- package/dist/server-3BDZ5S72.cjs.map +1 -0
- package/dist/{server-RUCX2TIB.js → server-NNW54YW5.js} +26 -26
- package/dist/server-NNW54YW5.js.map +1 -0
- package/dist/synapse-runtime.iife.global.js +1 -1
- package/dist/types-DElq_otH.d.cts +165 -0
- package/dist/types-DElq_otH.d.ts +165 -0
- package/dist/vite/index.cjs +23 -13
- package/dist/vite/index.cjs.map +1 -1
- package/dist/vite/index.js +23 -13
- package/dist/vite/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-JZC3VC2C.js.map +0 -1
- package/dist/chunk-Q7OSHSGZ.cjs.map +0 -1
- package/dist/server-RUCX2TIB.js.map +0 -1
- package/dist/server-SEI7XI3B.cjs.map +0 -1
- package/dist/types-BP0SNrpo.d.cts +0 -96
- package/dist/types-BP0SNrpo.d.ts +0 -96
package/dist/react/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { S as SynapseOptions, T as ToolCallResult, D as DataChangedEvent, A as ActionReducer, c as Store,
|
|
1
|
+
import { S as SynapseOptions, d as AgentAction, T as ToolCallResult, D as DataChangedEvent, A as ActionReducer, c as Store, g as StoreDispatch, a as Synapse, h as SynapseTheme } from '../types-DElq_otH.cjs';
|
|
2
2
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
3
|
import { ReactNode } from 'react';
|
|
4
4
|
|
|
@@ -15,16 +15,54 @@ declare function useCallTool<TOutput = unknown>(toolName: string): {
|
|
|
15
15
|
data: TOutput | null;
|
|
16
16
|
};
|
|
17
17
|
declare function useDataSync(callback: (event: DataChangedEvent) => void): void;
|
|
18
|
+
/**
|
|
19
|
+
* Subscribe to agent actions — typed, declarative commands from the server.
|
|
20
|
+
*
|
|
21
|
+
* Actions are emitted by tools as deterministic side effects (e.g., "navigate
|
|
22
|
+
* to the board I just created"). The UI decides how to handle each action type.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```tsx
|
|
26
|
+
* useAgentAction((action) => {
|
|
27
|
+
* if (action.type === "navigate") {
|
|
28
|
+
* const { entity, id } = action.payload as NavigatePayload;
|
|
29
|
+
* if (entity === "board") setSelectedBoardId(id);
|
|
30
|
+
* }
|
|
31
|
+
* });
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
declare function useAgentAction(callback: (action: AgentAction) => void): void;
|
|
18
35
|
declare function useTheme(): SynapseTheme;
|
|
19
36
|
declare function useAction(): (action: string, params?: Record<string, unknown>) => void;
|
|
20
37
|
declare function useChat(): (message: string, context?: {
|
|
21
38
|
action?: string;
|
|
22
39
|
entity?: string;
|
|
23
40
|
}) => void;
|
|
41
|
+
/**
|
|
42
|
+
* Push the app's visible state to the agent via ext-apps `ui/update-model-context`.
|
|
43
|
+
*
|
|
44
|
+
* **Imperative** (no args) — returns a push function you call manually:
|
|
45
|
+
* ```tsx
|
|
46
|
+
* const push = useVisibleState();
|
|
47
|
+
* push({ board: selectedBoard }, "Viewing board X");
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* **Declarative** (factory + deps) — auto-pushes when deps change:
|
|
51
|
+
* ```tsx
|
|
52
|
+
* useVisibleState(() => ({
|
|
53
|
+
* state: { board: selectedBoard },
|
|
54
|
+
* summary: `Viewing "${selectedBoard?.name}"`,
|
|
55
|
+
* }), [selectedBoard]);
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
24
58
|
declare function useVisibleState(): (state: Record<string, unknown>, summary?: string) => void;
|
|
59
|
+
declare function useVisibleState(factory: () => {
|
|
60
|
+
state: Record<string, unknown>;
|
|
61
|
+
summary?: string;
|
|
62
|
+
}, deps: unknown[]): void;
|
|
25
63
|
declare function useStore<TState, TActions extends Record<string, ActionReducer<TState, any>>>(store: Store<TState, TActions>): {
|
|
26
64
|
state: TState;
|
|
27
65
|
dispatch: StoreDispatch<TActions>;
|
|
28
66
|
};
|
|
29
67
|
|
|
30
|
-
export { SynapseProvider, type SynapseProviderProps, useAction, useCallTool, useChat, useDataSync, useStore, useSynapse, useTheme, useVisibleState };
|
|
68
|
+
export { SynapseProvider, type SynapseProviderProps, useAction, useAgentAction, useCallTool, useChat, useDataSync, useStore, useSynapse, useTheme, useVisibleState };
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { S as SynapseOptions, T as ToolCallResult, D as DataChangedEvent, A as ActionReducer, c as Store,
|
|
1
|
+
import { S as SynapseOptions, d as AgentAction, T as ToolCallResult, D as DataChangedEvent, A as ActionReducer, c as Store, g as StoreDispatch, a as Synapse, h as SynapseTheme } from '../types-DElq_otH.js';
|
|
2
2
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
3
|
import { ReactNode } from 'react';
|
|
4
4
|
|
|
@@ -15,16 +15,54 @@ declare function useCallTool<TOutput = unknown>(toolName: string): {
|
|
|
15
15
|
data: TOutput | null;
|
|
16
16
|
};
|
|
17
17
|
declare function useDataSync(callback: (event: DataChangedEvent) => void): void;
|
|
18
|
+
/**
|
|
19
|
+
* Subscribe to agent actions — typed, declarative commands from the server.
|
|
20
|
+
*
|
|
21
|
+
* Actions are emitted by tools as deterministic side effects (e.g., "navigate
|
|
22
|
+
* to the board I just created"). The UI decides how to handle each action type.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```tsx
|
|
26
|
+
* useAgentAction((action) => {
|
|
27
|
+
* if (action.type === "navigate") {
|
|
28
|
+
* const { entity, id } = action.payload as NavigatePayload;
|
|
29
|
+
* if (entity === "board") setSelectedBoardId(id);
|
|
30
|
+
* }
|
|
31
|
+
* });
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
declare function useAgentAction(callback: (action: AgentAction) => void): void;
|
|
18
35
|
declare function useTheme(): SynapseTheme;
|
|
19
36
|
declare function useAction(): (action: string, params?: Record<string, unknown>) => void;
|
|
20
37
|
declare function useChat(): (message: string, context?: {
|
|
21
38
|
action?: string;
|
|
22
39
|
entity?: string;
|
|
23
40
|
}) => void;
|
|
41
|
+
/**
|
|
42
|
+
* Push the app's visible state to the agent via ext-apps `ui/update-model-context`.
|
|
43
|
+
*
|
|
44
|
+
* **Imperative** (no args) — returns a push function you call manually:
|
|
45
|
+
* ```tsx
|
|
46
|
+
* const push = useVisibleState();
|
|
47
|
+
* push({ board: selectedBoard }, "Viewing board X");
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* **Declarative** (factory + deps) — auto-pushes when deps change:
|
|
51
|
+
* ```tsx
|
|
52
|
+
* useVisibleState(() => ({
|
|
53
|
+
* state: { board: selectedBoard },
|
|
54
|
+
* summary: `Viewing "${selectedBoard?.name}"`,
|
|
55
|
+
* }), [selectedBoard]);
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
24
58
|
declare function useVisibleState(): (state: Record<string, unknown>, summary?: string) => void;
|
|
59
|
+
declare function useVisibleState(factory: () => {
|
|
60
|
+
state: Record<string, unknown>;
|
|
61
|
+
summary?: string;
|
|
62
|
+
}, deps: unknown[]): void;
|
|
25
63
|
declare function useStore<TState, TActions extends Record<string, ActionReducer<TState, any>>>(store: Store<TState, TActions>): {
|
|
26
64
|
state: TState;
|
|
27
65
|
dispatch: StoreDispatch<TActions>;
|
|
28
66
|
};
|
|
29
67
|
|
|
30
|
-
export { SynapseProvider, type SynapseProviderProps, useAction, useCallTool, useChat, useDataSync, useStore, useSynapse, useTheme, useVisibleState };
|
|
68
|
+
export { SynapseProvider, type SynapseProviderProps, useAction, useAgentAction, useCallTool, useChat, useDataSync, useStore, useSynapse, useTheme, useVisibleState };
|
package/dist/react/index.js
CHANGED
|
@@ -1,19 +1,13 @@
|
|
|
1
|
-
import { createSynapse } from '../chunk-
|
|
2
|
-
import { createContext, useRef,
|
|
1
|
+
import { createSynapse } from '../chunk-7KEYXJWD.js';
|
|
2
|
+
import { createContext, useRef, useState, useCallback, useEffect, useSyncExternalStore, useContext } from 'react';
|
|
3
3
|
import { jsx } from 'react/jsx-runtime';
|
|
4
4
|
|
|
5
5
|
var SynapseContext = createContext(null);
|
|
6
6
|
function SynapseProvider({ children, ...options }) {
|
|
7
7
|
const ref = useRef(null);
|
|
8
|
-
if (ref.current === null) {
|
|
8
|
+
if (ref.current === null || ref.current.destroyed) {
|
|
9
9
|
ref.current = createSynapse(options);
|
|
10
10
|
}
|
|
11
|
-
useEffect(() => {
|
|
12
|
-
return () => {
|
|
13
|
-
ref.current?.destroy();
|
|
14
|
-
ref.current = null;
|
|
15
|
-
};
|
|
16
|
-
}, []);
|
|
17
11
|
return /* @__PURE__ */ jsx(SynapseContext.Provider, { value: ref.current, children });
|
|
18
12
|
}
|
|
19
13
|
function useSynapseContext() {
|
|
@@ -69,6 +63,14 @@ function useDataSync(callback) {
|
|
|
69
63
|
return synapse.onDataChanged((event) => callbackRef.current(event));
|
|
70
64
|
}, [synapse]);
|
|
71
65
|
}
|
|
66
|
+
function useAgentAction(callback) {
|
|
67
|
+
const synapse = useSynapseContext();
|
|
68
|
+
const callbackRef = useRef(callback);
|
|
69
|
+
callbackRef.current = callback;
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
return synapse.onAction((action) => callbackRef.current(action));
|
|
72
|
+
}, [synapse]);
|
|
73
|
+
}
|
|
72
74
|
function useTheme() {
|
|
73
75
|
const synapse = useSynapseContext();
|
|
74
76
|
const [theme, setTheme] = useState(() => synapse.getTheme());
|
|
@@ -92,12 +94,20 @@ function useChat() {
|
|
|
92
94
|
[synapse]
|
|
93
95
|
);
|
|
94
96
|
}
|
|
95
|
-
function useVisibleState() {
|
|
97
|
+
function useVisibleState(factory, deps) {
|
|
96
98
|
const synapse = useSynapseContext();
|
|
97
|
-
|
|
99
|
+
const push = useCallback(
|
|
98
100
|
(state, summary) => synapse.setVisibleState(state, summary),
|
|
99
101
|
[synapse]
|
|
100
102
|
);
|
|
103
|
+
const factoryRef = useRef(factory);
|
|
104
|
+
factoryRef.current = factory;
|
|
105
|
+
useEffect(() => {
|
|
106
|
+
if (!factoryRef.current) return;
|
|
107
|
+
const { state, summary } = factoryRef.current();
|
|
108
|
+
push(state, summary);
|
|
109
|
+
}, [...deps ?? [], push]);
|
|
110
|
+
if (!factory) return push;
|
|
101
111
|
}
|
|
102
112
|
function useStore(store) {
|
|
103
113
|
const state = useSyncExternalStore(
|
|
@@ -108,6 +118,6 @@ function useStore(store) {
|
|
|
108
118
|
return { state, dispatch: store.dispatch };
|
|
109
119
|
}
|
|
110
120
|
|
|
111
|
-
export { SynapseProvider, useAction, useCallTool, useChat, useDataSync, useStore, useSynapse, useTheme, useVisibleState };
|
|
121
|
+
export { SynapseProvider, useAction, useAgentAction, useCallTool, useChat, useDataSync, useStore, useSynapse, useTheme, useVisibleState };
|
|
112
122
|
//# sourceMappingURL=index.js.map
|
|
113
123
|
//# sourceMappingURL=index.js.map
|
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/react/provider.tsx","../../src/react/hooks.ts"],"names":["useRef","useEffect"],"mappings":"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","file":"index.js","sourcesContent":["import { createContext, type ReactNode, useContext, useEffect, useRef } from \"react\";\nimport { createSynapse } from \"../core.js\";\nimport type { Synapse, SynapseOptions } from \"../types.js\";\n\nconst SynapseContext = createContext<Synapse | null>(null);\n\nexport interface SynapseProviderProps extends SynapseOptions {\n children: ReactNode;\n}\n\nexport function SynapseProvider({ children, ...options }: SynapseProviderProps) {\n const ref = useRef<Synapse | null>(null);\n\n if (ref.current === null) {\n ref.current = createSynapse(options);\n }\n\n useEffect(() => {\n // StrictMode: on double-mount, the ref may already have an instance.\n // The ref was created synchronously above, so it's always valid here.\n return () => {\n ref.current?.destroy();\n ref.current = null;\n };\n }, []);\n\n return <SynapseContext.Provider value={ref.current}>{children}</SynapseContext.Provider>;\n}\n\nexport function useSynapseContext(): Synapse {\n const ctx = useContext(SynapseContext);\n if (!ctx) {\n throw new Error(\n \"useSynapse must be used within a <SynapseProvider>. \" +\n \"Wrap your app component tree with <SynapseProvider>.\",\n );\n }\n return ctx;\n}\n","import { useCallback, useEffect, useRef, useState, useSyncExternalStore } from \"react\";\nimport type {\n ActionReducer,\n DataChangedEvent,\n Store,\n StoreDispatch,\n Synapse,\n SynapseTheme,\n ToolCallResult,\n} from \"../types.js\";\nimport { SynapseProvider, useSynapseContext } from \"./provider.js\";\n\n// Re-export provider components\nexport { SynapseProvider };\n\nexport function useSynapse(): Synapse {\n return useSynapseContext();\n}\n\nexport function useCallTool<TOutput = unknown>(\n toolName: string,\n): {\n call: (args?: Record<string, unknown>) => Promise<ToolCallResult<TOutput>>;\n isPending: boolean;\n error: Error | null;\n data: TOutput | null;\n} {\n const synapse = useSynapseContext();\n const [isPending, setIsPending] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [data, setData] = useState<TOutput | null>(null);\n const callIdRef = useRef(0);\n\n const call = useCallback(\n async (args?: Record<string, unknown>): Promise<ToolCallResult<TOutput>> => {\n const id = ++callIdRef.current;\n setIsPending(true);\n setError(null);\n\n try {\n const result = await synapse.callTool<Record<string, unknown>, TOutput>(toolName, args);\n // Stale guard: only update if this is still the latest call\n if (id === callIdRef.current) {\n setData(result.data);\n setIsPending(false);\n }\n return result;\n } catch (err) {\n if (id === callIdRef.current) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n setIsPending(false);\n }\n throw err;\n }\n },\n [synapse, toolName],\n );\n\n return { call, isPending, error, data };\n}\n\nexport function useDataSync(callback: (event: DataChangedEvent) => void): void {\n const synapse = useSynapseContext();\n const callbackRef = useRef(callback);\n callbackRef.current = callback;\n\n useEffect(() => {\n return synapse.onDataChanged((event) => callbackRef.current(event));\n }, [synapse]);\n}\n\nexport function useTheme(): SynapseTheme {\n const synapse = useSynapseContext();\n const [theme, setTheme] = useState<SynapseTheme>(() => synapse.getTheme());\n\n useEffect(() => {\n // Sync in case theme changed between render and effect\n setTheme(synapse.getTheme());\n return synapse.onThemeChanged(setTheme);\n }, [synapse]);\n\n return theme;\n}\n\nexport function useAction(): (action: string, params?: Record<string, unknown>) => void {\n const synapse = useSynapseContext();\n return useCallback(\n (action: string, params?: Record<string, unknown>) => synapse.action(action, params),\n [synapse],\n );\n}\n\nexport function useChat(): (\n message: string,\n context?: { action?: string; entity?: string },\n) => void {\n const synapse = useSynapseContext();\n return useCallback(\n (message: string, context?: { action?: string; entity?: string }) =>\n synapse.chat(message, context),\n [synapse],\n );\n}\n\nexport function useVisibleState(): (state: Record<string, unknown>, summary?: string) => void {\n const synapse = useSynapseContext();\n return useCallback(\n (state: Record<string, unknown>, summary?: string) => synapse.setVisibleState(state, summary),\n [synapse],\n );\n}\n\nexport function useStore<TState, TActions extends Record<string, ActionReducer<TState, any>>>(\n store: Store<TState, TActions>,\n): {\n state: TState;\n dispatch: StoreDispatch<TActions>;\n} {\n const state = useSyncExternalStore(\n (onStoreChange) => store.subscribe(onStoreChange),\n () => store.getState(),\n () => store.getState(),\n );\n\n return { state, dispatch: store.dispatch };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/react/provider.tsx","../../src/react/hooks.ts"],"names":["useRef"],"mappings":";;;;AAIA,IAAM,cAAA,GAAiB,cAA8B,IAAI,CAAA;AAMlD,SAAS,eAAA,CAAgB,EAAE,QAAA,EAAU,GAAG,SAAQ,EAAyB;AAK9E,EAAA,MAAM,GAAA,GAAM,OAAuB,IAAI,CAAA;AAEvC,EAAA,IAAI,GAAA,CAAI,OAAA,KAAY,IAAA,IAAQ,GAAA,CAAI,QAAQ,SAAA,EAAW;AACjD,IAAA,GAAA,CAAI,OAAA,GAAU,cAAc,OAAO,CAAA;AAAA,EACrC;AAEA,EAAA,2BAAQ,cAAA,CAAe,QAAA,EAAf,EAAwB,KAAA,EAAO,GAAA,CAAI,SAAU,QAAA,EAAS,CAAA;AAChE;AAEO,SAAS,iBAAA,GAA6B;AAC3C,EAAA,MAAM,GAAA,GAAM,WAAW,cAAc,CAAA;AACrC,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KAEF;AAAA,EACF;AACA,EAAA,OAAO,GAAA;AACT;;;ACjBO,SAAS,UAAA,GAAsB;AACpC,EAAA,OAAO,iBAAA,EAAkB;AAC3B;AAEO,SAAS,YACd,QAAA,EAMA;AACA,EAAA,MAAM,UAAU,iBAAA,EAAkB;AAClC,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAAS,KAAK,CAAA;AAChD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAuB,IAAI,CAAA;AACrD,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAI,SAAyB,IAAI,CAAA;AACrD,EAAA,MAAM,SAAA,GAAYA,OAAO,CAAC,CAAA;AAE1B,EAAA,MAAM,IAAA,GAAO,WAAA;AAAA,IACX,OAAO,IAAA,KAAqE;AAC1E,MAAA,MAAM,EAAA,GAAK,EAAE,SAAA,CAAU,OAAA;AACvB,MAAA,YAAA,CAAa,IAAI,CAAA;AACjB,MAAA,QAAA,CAAS,IAAI,CAAA;AAEb,MAAA,IAAI;AACF,QAAA,MAAM,MAAA,GAAS,MAAM,OAAA,CAAQ,QAAA,CAA2C,UAAU,IAAI,CAAA;AAEtF,QAAA,IAAI,EAAA,KAAO,UAAU,OAAA,EAAS;AAC5B,UAAA,OAAA,CAAQ,OAAO,IAAI,CAAA;AACnB,UAAA,YAAA,CAAa,KAAK,CAAA;AAAA,QACpB;AACA,QAAA,OAAO,MAAA;AAAA,MACT,SAAS,GAAA,EAAK;AACZ,QAAA,IAAI,EAAA,KAAO,UAAU,OAAA,EAAS;AAC5B,UAAA,MAAM,CAAA,GAAI,eAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,KAAA,CAAM,MAAA,CAAO,GAAG,CAAC,CAAA;AAC5D,UAAA,QAAA,CAAS,CAAC,CAAA;AACV,UAAA,YAAA,CAAa,KAAK,CAAA;AAAA,QACpB;AACA,QAAA,MAAM,GAAA;AAAA,MACR;AAAA,IACF,CAAA;AAAA,IACA,CAAC,SAAS,QAAQ;AAAA,GACpB;AAEA,EAAA,OAAO,EAAE,IAAA,EAAM,SAAA,EAAW,KAAA,EAAO,IAAA,EAAK;AACxC;AAEO,SAAS,YAAY,QAAA,EAAmD;AAC7E,EAAA,MAAM,UAAU,iBAAA,EAAkB;AAClC,EAAA,MAAM,WAAA,GAAcA,OAAO,QAAQ,CAAA;AACnC,EAAA,WAAA,CAAY,OAAA,GAAU,QAAA;AAEtB,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,OAAO,QAAQ,aAAA,CAAc,CAAC,UAAU,WAAA,CAAY,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,EACpE,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AACd;AAkBO,SAAS,eAAe,QAAA,EAA+C;AAC5E,EAAA,MAAM,UAAU,iBAAA,EAAkB;AAClC,EAAA,MAAM,WAAA,GAAcA,OAAO,QAAQ,CAAA;AACnC,EAAA,WAAA,CAAY,OAAA,GAAU,QAAA;AAEtB,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,OAAO,QAAQ,QAAA,CAAS,CAAC,WAAW,WAAA,CAAY,OAAA,CAAQ,MAAM,CAAC,CAAA;AAAA,EACjE,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AACd;AAEO,SAAS,QAAA,GAAyB;AACvC,EAAA,MAAM,UAAU,iBAAA,EAAkB;AAClC,EAAA,MAAM,CAAC,OAAO,QAAQ,CAAA,GAAI,SAAuB,MAAM,OAAA,CAAQ,UAAU,CAAA;AAEzE,EAAA,SAAA,CAAU,MAAM;AAEd,IAAA,QAAA,CAAS,OAAA,CAAQ,UAAU,CAAA;AAC3B,IAAA,OAAO,OAAA,CAAQ,eAAe,QAAQ,CAAA;AAAA,EACxC,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,OAAO,KAAA;AACT;AAEO,SAAS,SAAA,GAAwE;AACtF,EAAA,MAAM,UAAU,iBAAA,EAAkB;AAClC,EAAA,OAAO,WAAA;AAAA,IACL,CAAC,MAAA,EAAgB,MAAA,KAAqC,OAAA,CAAQ,MAAA,CAAO,QAAQ,MAAM,CAAA;AAAA,IACnF,CAAC,OAAO;AAAA,GACV;AACF;AAEO,SAAS,OAAA,GAGN;AACR,EAAA,MAAM,UAAU,iBAAA,EAAkB;AAClC,EAAA,OAAO,WAAA;AAAA,IACL,CAAC,OAAA,EAAiB,OAAA,KAChB,OAAA,CAAQ,IAAA,CAAK,SAAS,OAAO,CAAA;AAAA,IAC/B,CAAC,OAAO;AAAA,GACV;AACF;AAwBO,SAAS,eAAA,CACd,SACA,IAAA,EAC0E;AAC1E,EAAA,MAAM,UAAU,iBAAA,EAAkB;AAClC,EAAA,MAAM,IAAA,GAAO,WAAA;AAAA,IACX,CAAC,KAAA,EAAgC,OAAA,KAAqB,OAAA,CAAQ,eAAA,CAAgB,OAAO,OAAO,CAAA;AAAA,IAC5F,CAAC,OAAO;AAAA,GACV;AAIA,EAAA,MAAM,UAAA,GAAaA,OAAO,OAAO,CAAA;AACjC,EAAA,UAAA,CAAW,OAAA,GAAU,OAAA;AACrB,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,WAAW,OAAA,EAAS;AACzB,IAAA,MAAM,EAAE,KAAA,EAAO,OAAA,EAAQ,GAAI,WAAW,OAAA,EAAQ;AAC9C,IAAA,IAAA,CAAK,OAAO,OAAO,CAAA;AAAA,EACrB,GAAG,CAAC,GAAI,QAAQ,EAAC,EAAI,IAAI,CAAC,CAAA;AAE1B,EAAA,IAAI,CAAC,SAAS,OAAO,IAAA;AACvB;AAEO,SAAS,SACd,KAAA,EAIA;AACA,EAAA,MAAM,KAAA,GAAQ,oBAAA;AAAA,IACZ,CAAC,aAAA,KAAkB,KAAA,CAAM,SAAA,CAAU,aAAa,CAAA;AAAA,IAChD,MAAM,MAAM,QAAA,EAAS;AAAA,IACrB,MAAM,MAAM,QAAA;AAAS,GACvB;AAEA,EAAA,OAAO,EAAE,KAAA,EAAO,QAAA,EAAU,KAAA,CAAM,QAAA,EAAS;AAC3C","file":"index.js","sourcesContent":["import { createContext, type ReactNode, useContext, useRef } from \"react\";\nimport { createSynapse } from \"../core.js\";\nimport type { Synapse, SynapseOptions } from \"../types.js\";\n\nconst SynapseContext = createContext<Synapse | null>(null);\n\nexport interface SynapseProviderProps extends SynapseOptions {\n children: ReactNode;\n}\n\nexport function SynapseProvider({ children, ...options }: SynapseProviderProps) {\n // Use a ref so the same Synapse instance survives StrictMode's\n // unmount/remount cycle. We intentionally do NOT destroy on unmount\n // because StrictMode re-mounts immediately and the transport must\n // stay alive. The instance is GC'd when the provider is truly removed.\n const ref = useRef<Synapse | null>(null);\n\n if (ref.current === null || ref.current.destroyed) {\n ref.current = createSynapse(options);\n }\n\n return <SynapseContext.Provider value={ref.current}>{children}</SynapseContext.Provider>;\n}\n\nexport function useSynapseContext(): Synapse {\n const ctx = useContext(SynapseContext);\n if (!ctx) {\n throw new Error(\n \"useSynapse must be used within a <SynapseProvider>. \" +\n \"Wrap your app component tree with <SynapseProvider>.\",\n );\n }\n return ctx;\n}\n","import { useCallback, useEffect, useRef, useState, useSyncExternalStore } from \"react\";\nimport type {\n ActionReducer,\n AgentAction,\n DataChangedEvent,\n Store,\n StoreDispatch,\n Synapse,\n SynapseTheme,\n ToolCallResult,\n} from \"../types.js\";\nimport { SynapseProvider, useSynapseContext } from \"./provider.js\";\n\n// Re-export provider components\nexport { SynapseProvider };\n\nexport function useSynapse(): Synapse {\n return useSynapseContext();\n}\n\nexport function useCallTool<TOutput = unknown>(\n toolName: string,\n): {\n call: (args?: Record<string, unknown>) => Promise<ToolCallResult<TOutput>>;\n isPending: boolean;\n error: Error | null;\n data: TOutput | null;\n} {\n const synapse = useSynapseContext();\n const [isPending, setIsPending] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [data, setData] = useState<TOutput | null>(null);\n const callIdRef = useRef(0);\n\n const call = useCallback(\n async (args?: Record<string, unknown>): Promise<ToolCallResult<TOutput>> => {\n const id = ++callIdRef.current;\n setIsPending(true);\n setError(null);\n\n try {\n const result = await synapse.callTool<Record<string, unknown>, TOutput>(toolName, args);\n // Stale guard: only update if this is still the latest call\n if (id === callIdRef.current) {\n setData(result.data);\n setIsPending(false);\n }\n return result;\n } catch (err) {\n if (id === callIdRef.current) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n setIsPending(false);\n }\n throw err;\n }\n },\n [synapse, toolName],\n );\n\n return { call, isPending, error, data };\n}\n\nexport function useDataSync(callback: (event: DataChangedEvent) => void): void {\n const synapse = useSynapseContext();\n const callbackRef = useRef(callback);\n callbackRef.current = callback;\n\n useEffect(() => {\n return synapse.onDataChanged((event) => callbackRef.current(event));\n }, [synapse]);\n}\n\n/**\n * Subscribe to agent actions — typed, declarative commands from the server.\n *\n * Actions are emitted by tools as deterministic side effects (e.g., \"navigate\n * to the board I just created\"). The UI decides how to handle each action type.\n *\n * @example\n * ```tsx\n * useAgentAction((action) => {\n * if (action.type === \"navigate\") {\n * const { entity, id } = action.payload as NavigatePayload;\n * if (entity === \"board\") setSelectedBoardId(id);\n * }\n * });\n * ```\n */\nexport function useAgentAction(callback: (action: AgentAction) => void): void {\n const synapse = useSynapseContext();\n const callbackRef = useRef(callback);\n callbackRef.current = callback;\n\n useEffect(() => {\n return synapse.onAction((action) => callbackRef.current(action));\n }, [synapse]);\n}\n\nexport function useTheme(): SynapseTheme {\n const synapse = useSynapseContext();\n const [theme, setTheme] = useState<SynapseTheme>(() => synapse.getTheme());\n\n useEffect(() => {\n // Sync in case theme changed between render and effect\n setTheme(synapse.getTheme());\n return synapse.onThemeChanged(setTheme);\n }, [synapse]);\n\n return theme;\n}\n\nexport function useAction(): (action: string, params?: Record<string, unknown>) => void {\n const synapse = useSynapseContext();\n return useCallback(\n (action: string, params?: Record<string, unknown>) => synapse.action(action, params),\n [synapse],\n );\n}\n\nexport function useChat(): (\n message: string,\n context?: { action?: string; entity?: string },\n) => void {\n const synapse = useSynapseContext();\n return useCallback(\n (message: string, context?: { action?: string; entity?: string }) =>\n synapse.chat(message, context),\n [synapse],\n );\n}\n\n/**\n * Push the app's visible state to the agent via ext-apps `ui/update-model-context`.\n *\n * **Imperative** (no args) — returns a push function you call manually:\n * ```tsx\n * const push = useVisibleState();\n * push({ board: selectedBoard }, \"Viewing board X\");\n * ```\n *\n * **Declarative** (factory + deps) — auto-pushes when deps change:\n * ```tsx\n * useVisibleState(() => ({\n * state: { board: selectedBoard },\n * summary: `Viewing \"${selectedBoard?.name}\"`,\n * }), [selectedBoard]);\n * ```\n */\nexport function useVisibleState(): (state: Record<string, unknown>, summary?: string) => void;\nexport function useVisibleState(\n factory: () => { state: Record<string, unknown>; summary?: string },\n deps: unknown[],\n): void;\nexport function useVisibleState(\n factory?: () => { state: Record<string, unknown>; summary?: string },\n deps?: unknown[],\n): ((state: Record<string, unknown>, summary?: string) => void) | undefined {\n const synapse = useSynapseContext();\n const push = useCallback(\n (state: Record<string, unknown>, summary?: string) => synapse.setVisibleState(state, summary),\n [synapse],\n );\n\n // Declarative mode: auto-push when deps change.\n // The deps array is caller-provided (mirrors useMemo/useEffect pattern).\n const factoryRef = useRef(factory);\n factoryRef.current = factory;\n useEffect(() => {\n if (!factoryRef.current) return;\n const { state, summary } = factoryRef.current();\n push(state, summary);\n }, [...(deps ?? []), push]);\n\n if (!factory) return push;\n}\n\nexport function useStore<TState, TActions extends Record<string, ActionReducer<TState, any>>>(\n store: Store<TState, TActions>,\n): {\n state: TState;\n dispatch: StoreDispatch<TActions>;\n} {\n const state = useSyncExternalStore(\n (onStoreChange) => store.subscribe(onStoreChange),\n () => store.getState(),\n () => store.getState(),\n );\n\n return { state, dispatch: store.dispatch };\n}\n"]}
|
|
@@ -37,19 +37,19 @@ var HOST_HTML = (uiPort, serverPort) => `<!DOCTYPE html>
|
|
|
37
37
|
|
|
38
38
|
// Minimal NimbleBrain bridge host \u2014 just enough to make Synapse work
|
|
39
39
|
var tokens = darkMode ? {
|
|
40
|
-
"--
|
|
41
|
-
"--
|
|
42
|
-
"--
|
|
43
|
-
"--
|
|
44
|
-
"--
|
|
45
|
-
"--
|
|
40
|
+
"--color-background-primary": "#0f172a", "--color-text-primary": "#e2e8f0",
|
|
41
|
+
"--color-background-secondary": "#1e293b", "--color-text-primary": "#e2e8f0",
|
|
42
|
+
"--color-text-accent": "#6366f1", "--nb-color-accent-foreground": "#ffffff",
|
|
43
|
+
"--color-text-secondary": "#94a3b8", "--color-border-primary": "#334155",
|
|
44
|
+
"--color-ring-primary": "#6366f1", "--nb-color-danger": "#ef4444",
|
|
45
|
+
"--border-radius-sm": "0.5rem",
|
|
46
46
|
} : {
|
|
47
|
-
"--
|
|
48
|
-
"--
|
|
49
|
-
"--
|
|
50
|
-
"--
|
|
51
|
-
"--
|
|
52
|
-
"--
|
|
47
|
+
"--color-background-primary": "#ffffff", "--color-text-primary": "#1a1a1a",
|
|
48
|
+
"--color-background-secondary": "#f9fafb", "--color-text-primary": "#1a1a1a",
|
|
49
|
+
"--color-text-accent": "#6366f1", "--nb-color-accent-foreground": "#ffffff",
|
|
50
|
+
"--color-text-secondary": "#6b7280", "--color-border-primary": "#e5e7eb",
|
|
51
|
+
"--color-ring-primary": "#6366f1", "--nb-color-danger": "#ef4444",
|
|
52
|
+
"--border-radius-sm": "0.5rem",
|
|
53
53
|
};
|
|
54
54
|
|
|
55
55
|
function post(msg) { iframe.contentWindow.postMessage(msg, "*"); }
|
|
@@ -126,19 +126,19 @@ var HOST_HTML = (uiPort, serverPort) => `<!DOCTYPE html>
|
|
|
126
126
|
darkMode = !darkMode;
|
|
127
127
|
document.body.style.background = darkMode ? "#0f172a" : "#f1f5f9";
|
|
128
128
|
tokens = darkMode ? {
|
|
129
|
-
"--
|
|
130
|
-
"--
|
|
131
|
-
"--
|
|
132
|
-
"--
|
|
133
|
-
"--
|
|
134
|
-
"--
|
|
129
|
+
"--color-background-primary": "#0f172a", "--color-text-primary": "#e2e8f0",
|
|
130
|
+
"--color-background-secondary": "#1e293b", "--color-text-primary": "#e2e8f0",
|
|
131
|
+
"--color-text-accent": "#6366f1", "--nb-color-accent-foreground": "#ffffff",
|
|
132
|
+
"--color-text-secondary": "#94a3b8", "--color-border-primary": "#334155",
|
|
133
|
+
"--color-ring-primary": "#6366f1", "--nb-color-danger": "#ef4444",
|
|
134
|
+
"--border-radius-sm": "0.5rem",
|
|
135
135
|
} : {
|
|
136
|
-
"--
|
|
137
|
-
"--
|
|
138
|
-
"--
|
|
139
|
-
"--
|
|
140
|
-
"--
|
|
141
|
-
"--
|
|
136
|
+
"--color-background-primary": "#ffffff", "--color-text-primary": "#1a1a1a",
|
|
137
|
+
"--color-background-secondary": "#f9fafb", "--color-text-primary": "#1a1a1a",
|
|
138
|
+
"--color-text-accent": "#6366f1", "--nb-color-accent-foreground": "#ffffff",
|
|
139
|
+
"--color-text-secondary": "#6b7280", "--color-border-primary": "#e5e7eb",
|
|
140
|
+
"--color-ring-primary": "#6366f1", "--nb-color-danger": "#ef4444",
|
|
141
|
+
"--border-radius-sm": "0.5rem",
|
|
142
142
|
};
|
|
143
143
|
post({ jsonrpc: "2.0", method: "ui/themeChanged", params: { mode: darkMode ? "dark" : "light", tokens: tokens } });
|
|
144
144
|
});
|
|
@@ -202,5 +202,5 @@ async function startPreview(options) {
|
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
exports.startPreview = startPreview;
|
|
205
|
-
//# sourceMappingURL=server-
|
|
206
|
-
//# sourceMappingURL=server-
|
|
205
|
+
//# sourceMappingURL=server-3BDZ5S72.cjs.map
|
|
206
|
+
//# sourceMappingURL=server-3BDZ5S72.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/preview/server.ts"],"names":["spawn","resolve","existsSync","join","createServer"],"mappings":"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","file":"server-3BDZ5S72.cjs","sourcesContent":["/**\n * Synapse Preview — standalone dev harness for MCP apps with UIs.\n *\n * Starts the MCP server (HTTP mode) and a minimal bridge host page\n * that iframes the app UI and proxies tool calls to the server.\n *\n * Usage:\n * npx synapse preview --server \"uv run uvicorn mcp_hello.server:app --port 8001\" --ui ./ui\n * npx synapse preview --server \"node dist/index.js\" --ui ./ui --server-port 8001\n */\n\nimport { type ChildProcess, spawn } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { createServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport { join, resolve } from \"node:path\";\n\nexport interface PreviewOptions {\n /** Shell command to start the MCP server in HTTP mode */\n serverCmd: string;\n /** Port the MCP server listens on (default: 8001) */\n serverPort: number;\n /** Path to the UI directory (must have package.json with dev script) */\n uiDir: string;\n /** Port for the UI Vite dev server (default: 5173) */\n uiPort: number;\n /** Port for the preview harness (default: 5180) */\n previewPort: number;\n}\n\nconst HOST_HTML = (uiPort: number, serverPort: number) => `<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\" />\n <title>Synapse Preview</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; background: #0f172a; color: #e2e8f0; }\n header { padding: 12px 20px; background: #1e293b; border-bottom: 1px solid #334155; display: flex; align-items: center; gap: 12px; }\n header h1 { font-size: 14px; font-weight: 500; }\n header .dot { width: 8px; height: 8px; border-radius: 50%; background: #22c55e; }\n header .info { font-size: 12px; color: #94a3b8; margin-left: auto; }\n .theme-toggle { background: #334155; border: none; color: #e2e8f0; padding: 4px 10px; border-radius: 4px; cursor: pointer; font-size: 12px; }\n iframe { width: 100%; height: calc(100vh - 45px); border: none; }\n </style>\n</head>\n<body>\n <header>\n <span class=\"dot\"></span>\n <h1>Synapse Preview</h1>\n <button class=\"theme-toggle\" id=\"toggle\">Toggle Dark/Light</button>\n <span class=\"info\">MCP: localhost:${serverPort} | UI: localhost:${uiPort}</span>\n </header>\n <iframe id=\"app\" src=\"http://localhost:${uiPort}\"></iframe>\n\n <script>\n var iframe = document.getElementById(\"app\");\n var darkMode = true;\n\n // Minimal NimbleBrain bridge host — just enough to make Synapse work\n var tokens = darkMode ? {\n \"--color-background-primary\": \"#0f172a\", \"--color-text-primary\": \"#e2e8f0\",\n \"--color-background-secondary\": \"#1e293b\", \"--color-text-primary\": \"#e2e8f0\",\n \"--color-text-accent\": \"#6366f1\", \"--nb-color-accent-foreground\": \"#ffffff\",\n \"--color-text-secondary\": \"#94a3b8\", \"--color-border-primary\": \"#334155\",\n \"--color-ring-primary\": \"#6366f1\", \"--nb-color-danger\": \"#ef4444\",\n \"--border-radius-sm\": \"0.5rem\",\n } : {\n \"--color-background-primary\": \"#ffffff\", \"--color-text-primary\": \"#1a1a1a\",\n \"--color-background-secondary\": \"#f9fafb\", \"--color-text-primary\": \"#1a1a1a\",\n \"--color-text-accent\": \"#6366f1\", \"--nb-color-accent-foreground\": \"#ffffff\",\n \"--color-text-secondary\": \"#6b7280\", \"--color-border-primary\": \"#e5e7eb\",\n \"--color-ring-primary\": \"#6366f1\", \"--nb-color-danger\": \"#ef4444\",\n \"--border-radius-sm\": \"0.5rem\",\n };\n\n function post(msg) { iframe.contentWindow.postMessage(msg, \"*\"); }\n\n window.addEventListener(\"message\", async function (event) {\n if (event.source !== iframe.contentWindow) return;\n var msg = event.data;\n if (!msg || typeof msg !== \"object\") return;\n\n // ext-apps handshake\n if (msg.method === \"ui/initialize\" && msg.id) {\n post({\n jsonrpc: \"2.0\", id: msg.id,\n result: {\n protocolVersion: \"2026-01-26\",\n serverInfo: { name: \"nimblebrain\", version: \"preview\" },\n capabilities: { openLinks: {}, serverTools: {} },\n hostContext: { theme: darkMode ? \"dark\" : \"light\", primaryColor: \"#6366f1\", tokens: tokens }\n }\n });\n return;\n }\n\n if (msg.method === \"ui/notifications/initialized\") return;\n\n // Tool call proxy — forward to the MCP server\n if (msg.method === \"tools/call\" && msg.id) {\n try {\n var resp = await fetch(\"http://localhost:${serverPort}/mcp\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n jsonrpc: \"2.0\", id: msg.id,\n method: \"tools/call\",\n params: { name: msg.params.name, arguments: msg.params.arguments || {} }\n })\n });\n var result = await resp.json();\n // Forward the JSON-RPC response back to the iframe\n post(result);\n } catch (err) {\n post({ jsonrpc: \"2.0\", id: msg.id, error: { code: -32000, message: err.message || \"Server error\" } });\n }\n return;\n }\n\n // ui/chat — log to console\n if (msg.method === \"ui/chat\") {\n console.log(\"[chat]\", msg.params?.message);\n return;\n }\n\n // ui/action — log to console\n if (msg.method === \"ui/action\") {\n console.log(\"[action]\", msg.params?.action, msg.params);\n return;\n }\n\n // ui/keydown — ignore in preview\n if (msg.method === \"ui/keydown\") return;\n\n // ui/stateChanged — log\n if (msg.method === \"ui/stateChanged\") {\n console.log(\"[state]\", msg.params?.state);\n post({ jsonrpc: \"2.0\", method: \"ui/stateAcknowledged\", params: { truncated: false } });\n return;\n }\n\n console.log(\"[bridge] unhandled:\", msg.method, msg);\n });\n\n // Theme toggle\n document.getElementById(\"toggle\").addEventListener(\"click\", function () {\n darkMode = !darkMode;\n document.body.style.background = darkMode ? \"#0f172a\" : \"#f1f5f9\";\n tokens = darkMode ? {\n \"--color-background-primary\": \"#0f172a\", \"--color-text-primary\": \"#e2e8f0\",\n \"--color-background-secondary\": \"#1e293b\", \"--color-text-primary\": \"#e2e8f0\",\n \"--color-text-accent\": \"#6366f1\", \"--nb-color-accent-foreground\": \"#ffffff\",\n \"--color-text-secondary\": \"#94a3b8\", \"--color-border-primary\": \"#334155\",\n \"--color-ring-primary\": \"#6366f1\", \"--nb-color-danger\": \"#ef4444\",\n \"--border-radius-sm\": \"0.5rem\",\n } : {\n \"--color-background-primary\": \"#ffffff\", \"--color-text-primary\": \"#1a1a1a\",\n \"--color-background-secondary\": \"#f9fafb\", \"--color-text-primary\": \"#1a1a1a\",\n \"--color-text-accent\": \"#6366f1\", \"--nb-color-accent-foreground\": \"#ffffff\",\n \"--color-text-secondary\": \"#6b7280\", \"--color-border-primary\": \"#e5e7eb\",\n \"--color-ring-primary\": \"#6366f1\", \"--nb-color-danger\": \"#ef4444\",\n \"--border-radius-sm\": \"0.5rem\",\n };\n post({ jsonrpc: \"2.0\", method: \"ui/themeChanged\", params: { mode: darkMode ? \"dark\" : \"light\", tokens: tokens } });\n });\n </script>\n</body>\n</html>`;\n\nexport async function startPreview(options: PreviewOptions): Promise<void> {\n const { serverCmd, serverPort, uiDir, uiPort, previewPort } = options;\n const children: ChildProcess[] = [];\n\n console.log(`\\n Synapse Preview\\n`);\n\n // 1. Start MCP server\n console.log(` [server] ${serverCmd}`);\n const serverProc = spawn(serverCmd, {\n shell: true,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n children.push(serverProc);\n serverProc.stdout?.on(\"data\", (d: Buffer) => process.stdout.write(` [server] ${d}`));\n serverProc.stderr?.on(\"data\", (d: Buffer) => process.stderr.write(` [server] ${d}`));\n\n // 2. Start UI Vite dev server\n const resolvedUi = resolve(uiDir);\n if (!existsSync(join(resolvedUi, \"package.json\"))) {\n console.error(` [ui] Error: no package.json found at ${resolvedUi}`);\n process.exit(1);\n }\n console.log(` [ui] cd ${resolvedUi} && npx vite --port ${uiPort}`);\n const uiProc = spawn(\"npx\", [\"vite\", \"--port\", String(uiPort)], {\n cwd: resolvedUi,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n children.push(uiProc);\n uiProc.stdout?.on(\"data\", (d: Buffer) => process.stdout.write(` [ui] ${d}`));\n uiProc.stderr?.on(\"data\", (d: Buffer) => process.stderr.write(` [ui] ${d}`));\n\n // 3. Start preview host\n const html = HOST_HTML(uiPort, serverPort);\n const host = createServer((_req: IncomingMessage, res: ServerResponse) => {\n res.writeHead(200, { \"Content-Type\": \"text/html\" });\n res.end(html);\n });\n host.listen(previewPort, () => {\n console.log(`\\n Preview: http://localhost:${previewPort}`);\n console.log(` UI: http://localhost:${uiPort}`);\n console.log(` Server: http://localhost:${serverPort}`);\n console.log(`\\n Press Ctrl+C to stop.\\n`);\n });\n\n // Shutdown\n const shutdown = () => {\n console.log(\"\\n Shutting down...\");\n host.close();\n for (const child of children) {\n try {\n child.kill(\"SIGTERM\");\n } catch {\n /* already dead */\n }\n }\n process.exit(0);\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n}\n"]}
|
|
@@ -35,19 +35,19 @@ var HOST_HTML = (uiPort, serverPort) => `<!DOCTYPE html>
|
|
|
35
35
|
|
|
36
36
|
// Minimal NimbleBrain bridge host \u2014 just enough to make Synapse work
|
|
37
37
|
var tokens = darkMode ? {
|
|
38
|
-
"--
|
|
39
|
-
"--
|
|
40
|
-
"--
|
|
41
|
-
"--
|
|
42
|
-
"--
|
|
43
|
-
"--
|
|
38
|
+
"--color-background-primary": "#0f172a", "--color-text-primary": "#e2e8f0",
|
|
39
|
+
"--color-background-secondary": "#1e293b", "--color-text-primary": "#e2e8f0",
|
|
40
|
+
"--color-text-accent": "#6366f1", "--nb-color-accent-foreground": "#ffffff",
|
|
41
|
+
"--color-text-secondary": "#94a3b8", "--color-border-primary": "#334155",
|
|
42
|
+
"--color-ring-primary": "#6366f1", "--nb-color-danger": "#ef4444",
|
|
43
|
+
"--border-radius-sm": "0.5rem",
|
|
44
44
|
} : {
|
|
45
|
-
"--
|
|
46
|
-
"--
|
|
47
|
-
"--
|
|
48
|
-
"--
|
|
49
|
-
"--
|
|
50
|
-
"--
|
|
45
|
+
"--color-background-primary": "#ffffff", "--color-text-primary": "#1a1a1a",
|
|
46
|
+
"--color-background-secondary": "#f9fafb", "--color-text-primary": "#1a1a1a",
|
|
47
|
+
"--color-text-accent": "#6366f1", "--nb-color-accent-foreground": "#ffffff",
|
|
48
|
+
"--color-text-secondary": "#6b7280", "--color-border-primary": "#e5e7eb",
|
|
49
|
+
"--color-ring-primary": "#6366f1", "--nb-color-danger": "#ef4444",
|
|
50
|
+
"--border-radius-sm": "0.5rem",
|
|
51
51
|
};
|
|
52
52
|
|
|
53
53
|
function post(msg) { iframe.contentWindow.postMessage(msg, "*"); }
|
|
@@ -124,19 +124,19 @@ var HOST_HTML = (uiPort, serverPort) => `<!DOCTYPE html>
|
|
|
124
124
|
darkMode = !darkMode;
|
|
125
125
|
document.body.style.background = darkMode ? "#0f172a" : "#f1f5f9";
|
|
126
126
|
tokens = darkMode ? {
|
|
127
|
-
"--
|
|
128
|
-
"--
|
|
129
|
-
"--
|
|
130
|
-
"--
|
|
131
|
-
"--
|
|
132
|
-
"--
|
|
127
|
+
"--color-background-primary": "#0f172a", "--color-text-primary": "#e2e8f0",
|
|
128
|
+
"--color-background-secondary": "#1e293b", "--color-text-primary": "#e2e8f0",
|
|
129
|
+
"--color-text-accent": "#6366f1", "--nb-color-accent-foreground": "#ffffff",
|
|
130
|
+
"--color-text-secondary": "#94a3b8", "--color-border-primary": "#334155",
|
|
131
|
+
"--color-ring-primary": "#6366f1", "--nb-color-danger": "#ef4444",
|
|
132
|
+
"--border-radius-sm": "0.5rem",
|
|
133
133
|
} : {
|
|
134
|
-
"--
|
|
135
|
-
"--
|
|
136
|
-
"--
|
|
137
|
-
"--
|
|
138
|
-
"--
|
|
139
|
-
"--
|
|
134
|
+
"--color-background-primary": "#ffffff", "--color-text-primary": "#1a1a1a",
|
|
135
|
+
"--color-background-secondary": "#f9fafb", "--color-text-primary": "#1a1a1a",
|
|
136
|
+
"--color-text-accent": "#6366f1", "--nb-color-accent-foreground": "#ffffff",
|
|
137
|
+
"--color-text-secondary": "#6b7280", "--color-border-primary": "#e5e7eb",
|
|
138
|
+
"--color-ring-primary": "#6366f1", "--nb-color-danger": "#ef4444",
|
|
139
|
+
"--border-radius-sm": "0.5rem",
|
|
140
140
|
};
|
|
141
141
|
post({ jsonrpc: "2.0", method: "ui/themeChanged", params: { mode: darkMode ? "dark" : "light", tokens: tokens } });
|
|
142
142
|
});
|
|
@@ -200,5 +200,5 @@ async function startPreview(options) {
|
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
export { startPreview };
|
|
203
|
-
//# sourceMappingURL=server-
|
|
204
|
-
//# sourceMappingURL=server-
|
|
203
|
+
//# sourceMappingURL=server-NNW54YW5.js.map
|
|
204
|
+
//# sourceMappingURL=server-NNW54YW5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/preview/server.ts"],"names":[],"mappings":"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","file":"server-NNW54YW5.js","sourcesContent":["/**\n * Synapse Preview — standalone dev harness for MCP apps with UIs.\n *\n * Starts the MCP server (HTTP mode) and a minimal bridge host page\n * that iframes the app UI and proxies tool calls to the server.\n *\n * Usage:\n * npx synapse preview --server \"uv run uvicorn mcp_hello.server:app --port 8001\" --ui ./ui\n * npx synapse preview --server \"node dist/index.js\" --ui ./ui --server-port 8001\n */\n\nimport { type ChildProcess, spawn } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { createServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport { join, resolve } from \"node:path\";\n\nexport interface PreviewOptions {\n /** Shell command to start the MCP server in HTTP mode */\n serverCmd: string;\n /** Port the MCP server listens on (default: 8001) */\n serverPort: number;\n /** Path to the UI directory (must have package.json with dev script) */\n uiDir: string;\n /** Port for the UI Vite dev server (default: 5173) */\n uiPort: number;\n /** Port for the preview harness (default: 5180) */\n previewPort: number;\n}\n\nconst HOST_HTML = (uiPort: number, serverPort: number) => `<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\" />\n <title>Synapse Preview</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; background: #0f172a; color: #e2e8f0; }\n header { padding: 12px 20px; background: #1e293b; border-bottom: 1px solid #334155; display: flex; align-items: center; gap: 12px; }\n header h1 { font-size: 14px; font-weight: 500; }\n header .dot { width: 8px; height: 8px; border-radius: 50%; background: #22c55e; }\n header .info { font-size: 12px; color: #94a3b8; margin-left: auto; }\n .theme-toggle { background: #334155; border: none; color: #e2e8f0; padding: 4px 10px; border-radius: 4px; cursor: pointer; font-size: 12px; }\n iframe { width: 100%; height: calc(100vh - 45px); border: none; }\n </style>\n</head>\n<body>\n <header>\n <span class=\"dot\"></span>\n <h1>Synapse Preview</h1>\n <button class=\"theme-toggle\" id=\"toggle\">Toggle Dark/Light</button>\n <span class=\"info\">MCP: localhost:${serverPort} | UI: localhost:${uiPort}</span>\n </header>\n <iframe id=\"app\" src=\"http://localhost:${uiPort}\"></iframe>\n\n <script>\n var iframe = document.getElementById(\"app\");\n var darkMode = true;\n\n // Minimal NimbleBrain bridge host — just enough to make Synapse work\n var tokens = darkMode ? {\n \"--color-background-primary\": \"#0f172a\", \"--color-text-primary\": \"#e2e8f0\",\n \"--color-background-secondary\": \"#1e293b\", \"--color-text-primary\": \"#e2e8f0\",\n \"--color-text-accent\": \"#6366f1\", \"--nb-color-accent-foreground\": \"#ffffff\",\n \"--color-text-secondary\": \"#94a3b8\", \"--color-border-primary\": \"#334155\",\n \"--color-ring-primary\": \"#6366f1\", \"--nb-color-danger\": \"#ef4444\",\n \"--border-radius-sm\": \"0.5rem\",\n } : {\n \"--color-background-primary\": \"#ffffff\", \"--color-text-primary\": \"#1a1a1a\",\n \"--color-background-secondary\": \"#f9fafb\", \"--color-text-primary\": \"#1a1a1a\",\n \"--color-text-accent\": \"#6366f1\", \"--nb-color-accent-foreground\": \"#ffffff\",\n \"--color-text-secondary\": \"#6b7280\", \"--color-border-primary\": \"#e5e7eb\",\n \"--color-ring-primary\": \"#6366f1\", \"--nb-color-danger\": \"#ef4444\",\n \"--border-radius-sm\": \"0.5rem\",\n };\n\n function post(msg) { iframe.contentWindow.postMessage(msg, \"*\"); }\n\n window.addEventListener(\"message\", async function (event) {\n if (event.source !== iframe.contentWindow) return;\n var msg = event.data;\n if (!msg || typeof msg !== \"object\") return;\n\n // ext-apps handshake\n if (msg.method === \"ui/initialize\" && msg.id) {\n post({\n jsonrpc: \"2.0\", id: msg.id,\n result: {\n protocolVersion: \"2026-01-26\",\n serverInfo: { name: \"nimblebrain\", version: \"preview\" },\n capabilities: { openLinks: {}, serverTools: {} },\n hostContext: { theme: darkMode ? \"dark\" : \"light\", primaryColor: \"#6366f1\", tokens: tokens }\n }\n });\n return;\n }\n\n if (msg.method === \"ui/notifications/initialized\") return;\n\n // Tool call proxy — forward to the MCP server\n if (msg.method === \"tools/call\" && msg.id) {\n try {\n var resp = await fetch(\"http://localhost:${serverPort}/mcp\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n jsonrpc: \"2.0\", id: msg.id,\n method: \"tools/call\",\n params: { name: msg.params.name, arguments: msg.params.arguments || {} }\n })\n });\n var result = await resp.json();\n // Forward the JSON-RPC response back to the iframe\n post(result);\n } catch (err) {\n post({ jsonrpc: \"2.0\", id: msg.id, error: { code: -32000, message: err.message || \"Server error\" } });\n }\n return;\n }\n\n // ui/chat — log to console\n if (msg.method === \"ui/chat\") {\n console.log(\"[chat]\", msg.params?.message);\n return;\n }\n\n // ui/action — log to console\n if (msg.method === \"ui/action\") {\n console.log(\"[action]\", msg.params?.action, msg.params);\n return;\n }\n\n // ui/keydown — ignore in preview\n if (msg.method === \"ui/keydown\") return;\n\n // ui/stateChanged — log\n if (msg.method === \"ui/stateChanged\") {\n console.log(\"[state]\", msg.params?.state);\n post({ jsonrpc: \"2.0\", method: \"ui/stateAcknowledged\", params: { truncated: false } });\n return;\n }\n\n console.log(\"[bridge] unhandled:\", msg.method, msg);\n });\n\n // Theme toggle\n document.getElementById(\"toggle\").addEventListener(\"click\", function () {\n darkMode = !darkMode;\n document.body.style.background = darkMode ? \"#0f172a\" : \"#f1f5f9\";\n tokens = darkMode ? {\n \"--color-background-primary\": \"#0f172a\", \"--color-text-primary\": \"#e2e8f0\",\n \"--color-background-secondary\": \"#1e293b\", \"--color-text-primary\": \"#e2e8f0\",\n \"--color-text-accent\": \"#6366f1\", \"--nb-color-accent-foreground\": \"#ffffff\",\n \"--color-text-secondary\": \"#94a3b8\", \"--color-border-primary\": \"#334155\",\n \"--color-ring-primary\": \"#6366f1\", \"--nb-color-danger\": \"#ef4444\",\n \"--border-radius-sm\": \"0.5rem\",\n } : {\n \"--color-background-primary\": \"#ffffff\", \"--color-text-primary\": \"#1a1a1a\",\n \"--color-background-secondary\": \"#f9fafb\", \"--color-text-primary\": \"#1a1a1a\",\n \"--color-text-accent\": \"#6366f1\", \"--nb-color-accent-foreground\": \"#ffffff\",\n \"--color-text-secondary\": \"#6b7280\", \"--color-border-primary\": \"#e5e7eb\",\n \"--color-ring-primary\": \"#6366f1\", \"--nb-color-danger\": \"#ef4444\",\n \"--border-radius-sm\": \"0.5rem\",\n };\n post({ jsonrpc: \"2.0\", method: \"ui/themeChanged\", params: { mode: darkMode ? \"dark\" : \"light\", tokens: tokens } });\n });\n </script>\n</body>\n</html>`;\n\nexport async function startPreview(options: PreviewOptions): Promise<void> {\n const { serverCmd, serverPort, uiDir, uiPort, previewPort } = options;\n const children: ChildProcess[] = [];\n\n console.log(`\\n Synapse Preview\\n`);\n\n // 1. Start MCP server\n console.log(` [server] ${serverCmd}`);\n const serverProc = spawn(serverCmd, {\n shell: true,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n children.push(serverProc);\n serverProc.stdout?.on(\"data\", (d: Buffer) => process.stdout.write(` [server] ${d}`));\n serverProc.stderr?.on(\"data\", (d: Buffer) => process.stderr.write(` [server] ${d}`));\n\n // 2. Start UI Vite dev server\n const resolvedUi = resolve(uiDir);\n if (!existsSync(join(resolvedUi, \"package.json\"))) {\n console.error(` [ui] Error: no package.json found at ${resolvedUi}`);\n process.exit(1);\n }\n console.log(` [ui] cd ${resolvedUi} && npx vite --port ${uiPort}`);\n const uiProc = spawn(\"npx\", [\"vite\", \"--port\", String(uiPort)], {\n cwd: resolvedUi,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n children.push(uiProc);\n uiProc.stdout?.on(\"data\", (d: Buffer) => process.stdout.write(` [ui] ${d}`));\n uiProc.stderr?.on(\"data\", (d: Buffer) => process.stderr.write(` [ui] ${d}`));\n\n // 3. Start preview host\n const html = HOST_HTML(uiPort, serverPort);\n const host = createServer((_req: IncomingMessage, res: ServerResponse) => {\n res.writeHead(200, { \"Content-Type\": \"text/html\" });\n res.end(html);\n });\n host.listen(previewPort, () => {\n console.log(`\\n Preview: http://localhost:${previewPort}`);\n console.log(` UI: http://localhost:${uiPort}`);\n console.log(` Server: http://localhost:${serverPort}`);\n console.log(`\\n Press Ctrl+C to stop.\\n`);\n });\n\n // Shutdown\n const shutdown = () => {\n console.log(\"\\n Shutting down...\");\n host.close();\n for (const child of children) {\n try {\n child.kill(\"SIGTERM\");\n } catch {\n /* already dead */\n }\n }\n process.exit(0);\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(){'use strict';var
|
|
1
|
+
(function(){'use strict';var C={mode:"light",primaryColor:"#6366f1",tokens:{}};function x(r){let e=r,o=A(e?.serverInfo),t=typeof o?.name=="string"?o.name:"unknown",i=typeof e?.protocolVersion=="string"?e.protocolVersion:"unknown",s=A(e?.hostContext),c=H(s?.theme);return {isNimbleBrain:t==="nimblebrain",serverName:t,protocolVersion:i,theme:c}}function H(r){let e=A(r);if(!e)return {...C};let o=e.mode==="light"||e.mode==="dark"?e.mode:C.mode,t=typeof e.primaryColor=="string"?e.primaryColor:C.primaryColor,i=e.tokens!==null&&typeof e.tokens=="object"&&!Array.isArray(e.tokens)?e.tokens:{};return {mode:o,primaryColor:t,tokens:i}}function A(r){if(r!==null&&typeof r=="object"&&!Array.isArray(r))return r}var S=class{listener;destroyed=false;constructor(e,o){let t=o??null;this.listener=i=>{this.destroyed||this.shouldForward(i,t)&&(i.preventDefault(),e.send("ui/keydown",{key:i.key,ctrlKey:i.ctrlKey,metaKey:i.metaKey,shiftKey:i.shiftKey,altKey:i.altKey}));},document.addEventListener("keydown",this.listener);}destroy(){this.destroyed||(this.destroyed=true,document.removeEventListener("keydown",this.listener));}shouldForward(e,o){if(o&&o.length===0)return false;if(o)return o.some(t=>e.key.toLowerCase()===t.key.toLowerCase()&&(t.ctrl===void 0||e.ctrlKey===t.ctrl)&&(t.meta===void 0||e.metaKey===t.meta)&&(t.shift===void 0||e.shiftKey===t.shift)&&(t.alt===void 0||e.altKey===t.alt));if(e.key==="Escape")return true;if(e.ctrlKey||e.metaKey){let t=e.key.toLowerCase();return !(t==="c"||t==="v"||t==="x"||t==="a")}return false}};function E(r){return r==null?{data:null,isError:false}:I(r)?N(r):{data:r,isError:false}}function I(r){return r===null||typeof r!="object"||Array.isArray(r)?false:Array.isArray(r.content)}function L(r){if(r===null||typeof r!="object"||Array.isArray(r))return false;let e=r;return e.type==="text"&&typeof e.text=="string"}function N(r){let e=r.isError===true,o=r.content;if(o.length===0)return {data:null,isError:e};let t=o.find(L);if(!t)return {data:o,isError:e};try{return {data:JSON.parse(t.text),isError:e}}catch{return {data:t.text,isError:e}}}var b=class{counter=0;destroyed=false;pending=new Map;handlers=new Map;listener;constructor(){this.listener=e=>this.handleMessage(e),window.addEventListener("message",this.listener);}send(e,o){if(this.destroyed)return;let t={jsonrpc:"2.0",method:e,...o!==void 0&&{params:o}};window.parent.postMessage(t,"*");}request(e,o){if(this.destroyed)return Promise.reject(new Error("Transport destroyed"));let t=`syn-${++this.counter}`,i={jsonrpc:"2.0",method:e,id:t,...o!==void 0&&{params:o}};return new Promise((s,c)=>{this.pending.set(t,{resolve:s,reject:c}),window.parent.postMessage(i,"*");})}onMessage(e,o){return this.handlers.has(e)||this.handlers.set(e,new Set),this.handlers.get(e)?.add(o),()=>{let t=this.handlers.get(e);t&&(t.delete(o),t.size===0&&this.handlers.delete(e));}}destroy(){if(this.destroyed)return;this.destroyed=true,window.removeEventListener("message",this.listener);let e=new Error("Transport destroyed");for(let o of this.pending.values())o.reject(e);this.pending.clear(),this.handlers.clear();}handleMessage(e){if(this.destroyed)return;let o=e.data;if(!(!o||o.jsonrpc!=="2.0")){if("id"in o&&o.id&&!("method"in o)){let t=o,i=this.pending.get(t.id);if(!i)return;if(this.pending.delete(t.id),t.error){let s=new Error(t.error.message);s.code=t.error.code,s.data=t.error.data,i.reject(s);}else i.resolve(t.result);return}if("method"in o&&!("id"in o&&o.id)){let t=o,i=this.handlers.get(t.method);if(i)for(let s of i)s(t.params);}}}};function M(r){let{name:e,version:o,internal:t=false,forwardKeys:i}=r,s=new b,c=null,l={mode:"light",primaryColor:"#6366f1",tokens:{}},w=false,y=null,T=null,R=s.request("ui/initialize",{protocolVersion:"2026-01-26",clientInfo:{name:e,version:o},capabilities:{}}).then(n=>{c=x(n),l=c.theme,s.send("ui/notifications/initialized",{}),T=new S(s,i);}),d=s.onMessage("ui/notifications/host-context-changed",n=>{if(!n)return;let a=n.theme==="dark"?"dark":"light",u=n.tokens&&typeof n.tokens=="object"?n.tokens:l.tokens;l={mode:a,primaryColor:l.primaryColor,tokens:u};for(let g of p)g(l);}),f=s.onMessage("ui/themeChanged",n=>{if(!n)return;let a=n.mode==="dark"||n.mode==="light"?n.mode:l.mode,u=n.tokens&&typeof n.tokens=="object"?n.tokens:l.tokens;l={mode:a,primaryColor:l.primaryColor,tokens:u};for(let g of p)g(l);}),p=new Set,m=new Set,h=new Set,k=s.onMessage("ui/datachanged",n=>{if(!n)return;let a={source:"agent",server:n.server??"",tool:n.tool??""};for(let u of m)u(a);}),K=s.onMessage("ui/action",n=>{if(!n||typeof n.type!="string")return;let a={type:n.type,payload:n.payload??{},requiresConfirmation:n.requiresConfirmation===true,label:typeof n.label=="string"?n.label:void 0};for(let u of h)u(a);}),v=()=>c?.isNimbleBrain===true;return {get ready(){return R},get isNimbleBrainHost(){return v()},get destroyed(){return w},async callTool(n,a){let u={name:n,arguments:a??{}};t&&(u.server=e);let g=await s.request("tools/call",u);return E(g)},onDataChanged(n){return m.add(n),()=>{m.delete(n);}},onAction(n){return h.add(n),()=>{h.delete(n);}},getTheme(){return {...l}},onThemeChanged(n){return p.add(n),()=>{p.delete(n);}},action(n,a){v()&&s.send("ui/action",{action:n,...a});},chat(n,a){let u={type:"text",text:n};v()&&a&&(u._meta={context:a}),s.send("ui/message",{role:"user",content:[u]});},setVisibleState(n,a){y&&clearTimeout(y),y=setTimeout(()=>{s.send("ui/update-model-context",{structuredContent:n,...a!==void 0&&{content:[{type:"text",text:a}]}}),y=null;},250);},downloadFile(n,a,u){if(!v())return;let g=typeof a=="string"?a:"[Blob content not serializable]";s.send("ui/download-file",{data:g,filename:n,mimeType:u??"application/octet-stream"});},openLink(n){s.send("ui/open-link",{url:n}),v()||window.open(n,"_blank","noopener");},_onMessage(n,a){return s.onMessage(n,a)},_request(n,a){return s.request(n,a)},destroy(){w||(w=true,y&&clearTimeout(y),T?.destroy(),d(),f(),k(),K(),p.clear(),m.clear(),h.clear(),s.destroy());}}}function j(r,e){let o=structuredClone(e.initialState),t=new Set,i=false,s=null,c={};for(let d of Object.keys(e.actions))c[d]=f=>{i||(o=e.actions[d](o,f),l());};function l(){for(let d of t)d(o);e.visibleToAgent&&w(),e.persist&&y();}function w(){let d=e.summarize?.(o);r.setVisibleState(o,d);}function y(){s&&clearTimeout(s),s=setTimeout(()=>{r._request("ui/persistState",{state:o,version:e.version}).catch(()=>{}),s=null;},500);}let T;e.persist&&(T=r._onMessage("ui/stateLoaded",d=>{if(!d?.state)return;let f=d.state,p=d.version??1,m=e.version??1;if(e.migrations&&p<m){let h=p-1;for(let k=h;k<e.migrations.length;k++)f=e.migrations[k](f);}R.hydrate(f);}));let R={getState(){return o},subscribe(d){return t.add(d),()=>{t.delete(d);}},dispatch:c,hydrate(d){o=d;for(let f of t)f(o);},destroy(){i||(i=true,s&&clearTimeout(s),t.clear(),T?.());}};return R}window.NbSynapse={createSynapse:M,createStore:j};})();
|