@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.
Files changed (37) hide show
  1. package/README.md +7 -1
  2. package/dist/{chunk-JZC3VC2C.js → chunk-7KEYXJWD.js} +45 -13
  3. package/dist/chunk-7KEYXJWD.js.map +1 -0
  4. package/dist/{chunk-Q7OSHSGZ.cjs → chunk-Y4ZDNAYQ.cjs} +45 -13
  5. package/dist/chunk-Y4ZDNAYQ.cjs.map +1 -0
  6. package/dist/codegen/cli.cjs +1 -1
  7. package/dist/codegen/cli.js +1 -1
  8. package/dist/codegen/index.d.cts +1 -1
  9. package/dist/codegen/index.d.ts +1 -1
  10. package/dist/index.cjs +2 -2
  11. package/dist/index.d.cts +2 -2
  12. package/dist/index.d.ts +2 -2
  13. package/dist/index.js +1 -1
  14. package/dist/react/index.cjs +22 -11
  15. package/dist/react/index.cjs.map +1 -1
  16. package/dist/react/index.d.cts +40 -2
  17. package/dist/react/index.d.ts +40 -2
  18. package/dist/react/index.js +22 -12
  19. package/dist/react/index.js.map +1 -1
  20. package/dist/{server-SEI7XI3B.cjs → server-3BDZ5S72.cjs} +26 -26
  21. package/dist/server-3BDZ5S72.cjs.map +1 -0
  22. package/dist/{server-RUCX2TIB.js → server-NNW54YW5.js} +26 -26
  23. package/dist/server-NNW54YW5.js.map +1 -0
  24. package/dist/synapse-runtime.iife.global.js +1 -1
  25. package/dist/types-DElq_otH.d.cts +165 -0
  26. package/dist/types-DElq_otH.d.ts +165 -0
  27. package/dist/vite/index.cjs +23 -13
  28. package/dist/vite/index.cjs.map +1 -1
  29. package/dist/vite/index.js +23 -13
  30. package/dist/vite/index.js.map +1 -1
  31. package/package.json +2 -2
  32. package/dist/chunk-JZC3VC2C.js.map +0 -1
  33. package/dist/chunk-Q7OSHSGZ.cjs.map +0 -1
  34. package/dist/server-RUCX2TIB.js.map +0 -1
  35. package/dist/server-SEI7XI3B.cjs.map +0 -1
  36. package/dist/types-BP0SNrpo.d.cts +0 -96
  37. package/dist/types-BP0SNrpo.d.ts +0 -96
@@ -1,4 +1,4 @@
1
- import { S as SynapseOptions, T as ToolCallResult, D as DataChangedEvent, A as ActionReducer, c as Store, e as StoreDispatch, a as Synapse, f as SynapseTheme } from '../types-BP0SNrpo.cjs';
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 };
@@ -1,4 +1,4 @@
1
- import { S as SynapseOptions, T as ToolCallResult, D as DataChangedEvent, A as ActionReducer, c as Store, e as StoreDispatch, a as Synapse, f as SynapseTheme } from '../types-BP0SNrpo.js';
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 };
@@ -1,19 +1,13 @@
1
- import { createSynapse } from '../chunk-JZC3VC2C.js';
2
- import { createContext, useRef, useEffect, useState, useCallback, useSyncExternalStore, useContext } from 'react';
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
- return useCallback(
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
@@ -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":"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","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
- "--nb-background": "#0f172a", "--nb-foreground": "#e2e8f0",
41
- "--nb-card": "#1e293b", "--nb-card-foreground": "#e2e8f0",
42
- "--nb-primary": "#6366f1", "--nb-primary-foreground": "#ffffff",
43
- "--nb-muted-foreground": "#94a3b8", "--nb-border": "#334155",
44
- "--nb-ring": "#6366f1", "--nb-destructive": "#ef4444",
45
- "--nb-radius": "0.5rem",
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
- "--nb-background": "#ffffff", "--nb-foreground": "#1a1a1a",
48
- "--nb-card": "#f9fafb", "--nb-card-foreground": "#1a1a1a",
49
- "--nb-primary": "#6366f1", "--nb-primary-foreground": "#ffffff",
50
- "--nb-muted-foreground": "#6b7280", "--nb-border": "#e5e7eb",
51
- "--nb-ring": "#6366f1", "--nb-destructive": "#ef4444",
52
- "--nb-radius": "0.5rem",
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
- "--nb-background": "#0f172a", "--nb-foreground": "#e2e8f0",
130
- "--nb-card": "#1e293b", "--nb-card-foreground": "#e2e8f0",
131
- "--nb-primary": "#6366f1", "--nb-primary-foreground": "#ffffff",
132
- "--nb-muted-foreground": "#94a3b8", "--nb-border": "#334155",
133
- "--nb-ring": "#6366f1", "--nb-destructive": "#ef4444",
134
- "--nb-radius": "0.5rem",
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
- "--nb-background": "#ffffff", "--nb-foreground": "#1a1a1a",
137
- "--nb-card": "#f9fafb", "--nb-card-foreground": "#1a1a1a",
138
- "--nb-primary": "#6366f1", "--nb-primary-foreground": "#ffffff",
139
- "--nb-muted-foreground": "#6b7280", "--nb-border": "#e5e7eb",
140
- "--nb-ring": "#6366f1", "--nb-destructive": "#ef4444",
141
- "--nb-radius": "0.5rem",
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-SEI7XI3B.cjs.map
206
- //# sourceMappingURL=server-SEI7XI3B.cjs.map
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
- "--nb-background": "#0f172a", "--nb-foreground": "#e2e8f0",
39
- "--nb-card": "#1e293b", "--nb-card-foreground": "#e2e8f0",
40
- "--nb-primary": "#6366f1", "--nb-primary-foreground": "#ffffff",
41
- "--nb-muted-foreground": "#94a3b8", "--nb-border": "#334155",
42
- "--nb-ring": "#6366f1", "--nb-destructive": "#ef4444",
43
- "--nb-radius": "0.5rem",
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
- "--nb-background": "#ffffff", "--nb-foreground": "#1a1a1a",
46
- "--nb-card": "#f9fafb", "--nb-card-foreground": "#1a1a1a",
47
- "--nb-primary": "#6366f1", "--nb-primary-foreground": "#ffffff",
48
- "--nb-muted-foreground": "#6b7280", "--nb-border": "#e5e7eb",
49
- "--nb-ring": "#6366f1", "--nb-destructive": "#ef4444",
50
- "--nb-radius": "0.5rem",
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
- "--nb-background": "#0f172a", "--nb-foreground": "#e2e8f0",
128
- "--nb-card": "#1e293b", "--nb-card-foreground": "#e2e8f0",
129
- "--nb-primary": "#6366f1", "--nb-primary-foreground": "#ffffff",
130
- "--nb-muted-foreground": "#94a3b8", "--nb-border": "#334155",
131
- "--nb-ring": "#6366f1", "--nb-destructive": "#ef4444",
132
- "--nb-radius": "0.5rem",
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
- "--nb-background": "#ffffff", "--nb-foreground": "#1a1a1a",
135
- "--nb-card": "#f9fafb", "--nb-card-foreground": "#1a1a1a",
136
- "--nb-primary": "#6366f1", "--nb-primary-foreground": "#ffffff",
137
- "--nb-muted-foreground": "#6b7280", "--nb-border": "#e5e7eb",
138
- "--nb-ring": "#6366f1", "--nb-destructive": "#ef4444",
139
- "--nb-radius": "0.5rem",
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-RUCX2TIB.js.map
204
- //# sourceMappingURL=server-RUCX2TIB.js.map
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 b={mode:"light",primaryColor:"#6366f1",tokens:{}};function E(r){let e=r,t=C(e?.serverInfo),n=typeof t?.name=="string"?t.name:"unknown",i=typeof e?.protocolVersion=="string"?e.protocolVersion:"unknown",s=C(e?.hostContext),l=K(s?.theme);return {isNimbleBrain:n==="nimblebrain",serverName:n,protocolVersion:i,theme:l}}function K(r){let e=C(r);if(!e)return {...b};let t=e.mode==="light"||e.mode==="dark"?e.mode:b.mode,n=typeof e.primaryColor=="string"?e.primaryColor:b.primaryColor,i=e.tokens!==null&&typeof e.tokens=="object"&&!Array.isArray(e.tokens)?e.tokens:{};return {mode:t,primaryColor:n,tokens:i}}function C(r){if(r!==null&&typeof r=="object"&&!Array.isArray(r))return r}var k=class{listener;destroyed=false;constructor(e,t){let n=t??null;this.listener=i=>{this.destroyed||this.shouldForward(i,n)&&(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,t){return t&&t.length===0?false:t?t.some(n=>e.key.toLowerCase()===n.key.toLowerCase()&&(n.ctrl===void 0||e.ctrlKey===n.ctrl)&&(n.meta===void 0||e.metaKey===n.meta)&&(n.shift===void 0||e.shiftKey===n.shift)&&(n.alt===void 0||e.altKey===n.alt)):!!(e.key==="Escape"||e.ctrlKey||e.metaKey)}};function M(r){return r==null?{data:null,isError:false}:A(r)?I(r):{data:r,isError:false}}function A(r){return r===null||typeof r!="object"||Array.isArray(r)?false:Array.isArray(r.content)}function H(r){if(r===null||typeof r!="object"||Array.isArray(r))return false;let e=r;return e.type==="text"&&typeof e.text=="string"}function I(r){let e=r.isError===true,t=r.content;if(t.length===0)return {data:null,isError:e};let n=t.find(H);if(!n)return {data:t,isError:e};try{return {data:JSON.parse(n.text),isError:e}}catch{return {data:n.text,isError:e}}}var R=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,t){if(this.destroyed)return;let n={jsonrpc:"2.0",method:e,...t!==void 0&&{params:t}};window.parent.postMessage(n,"*");}request(e,t){if(this.destroyed)return Promise.reject(new Error("Transport destroyed"));let n=`syn-${++this.counter}`,i={jsonrpc:"2.0",method:e,id:n,...t!==void 0&&{params:t}};return new Promise((s,l)=>{this.pending.set(n,{resolve:s,reject:l}),window.parent.postMessage(i,"*");})}onMessage(e,t){return this.handlers.has(e)||this.handlers.set(e,new Set),this.handlers.get(e)?.add(t),()=>{let n=this.handlers.get(e);n&&(n.delete(t),n.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 t of this.pending.values())t.reject(e);this.pending.clear(),this.handlers.clear();}handleMessage(e){if(this.destroyed)return;let t=e.data;if(!(!t||t.jsonrpc!=="2.0")){if("id"in t&&t.id&&!("method"in t)){let n=t,i=this.pending.get(n.id);if(!i)return;if(this.pending.delete(n.id),n.error){let s=new Error(n.error.message);s.code=n.error.code,s.data=n.error.data,i.reject(s);}else i.resolve(n.result);return}if("method"in t&&!("id"in t&&t.id)){let n=t,i=this.handlers.get(n.method);if(i)for(let s of i)s(n.params);}}}};function j(r){let{name:e,version:t,internal:n=false,forwardKeys:i}=r,s=new R,l=null,u={mode:"light",primaryColor:"#6366f1",tokens:{}},w=false,y=null,T=null,v=s.request("ui/initialize",{protocolVersion:"2026-01-26",clientInfo:{name:e,version:t},capabilities:{}}).then(o=>{l=E(o),u=l.theme,s.send("ui/notifications/initialized",{}),T=new k(s,i);}),d=s.onMessage("ui/notifications/host-context-changed",o=>{if(!o)return;let a=o.theme==="dark"?"dark":"light",p=o.tokens&&typeof o.tokens=="object"?o.tokens:u.tokens;u={mode:a,primaryColor:u.primaryColor,tokens:p};for(let g of m)g(u);}),f=s.onMessage("ui/themeChanged",o=>{if(!o)return;let a=o.mode==="dark"||o.mode==="light"?o.mode:u.mode,p=o.tokens&&typeof o.tokens=="object"?o.tokens:u.tokens;u={mode:a,primaryColor:u.primaryColor,tokens:p};for(let g of m)g(u);}),m=new Set,h=new Set,S=s.onMessage("ui/datachanged",o=>{if(!o)return;let a={source:"agent",server:o.server??"",tool:o.tool??""};for(let p of h)p(a);}),c=()=>l?.isNimbleBrain===true;return {get ready(){return v},get isNimbleBrainHost(){return c()},async callTool(o,a){let p={name:o,arguments:a??{}};n&&(p.server=e);let g=await s.request("tools/call",p);return M(g)},onDataChanged(o){return h.add(o),()=>{h.delete(o);}},getTheme(){return {...u}},onThemeChanged(o){return m.add(o),()=>{m.delete(o);}},action(o,a){c()&&s.send("ui/action",{action:o,...a});},chat(o,a){c()&&s.send("ui/chat",{message:o,context:a});},setVisibleState(o,a){c()&&(y&&clearTimeout(y),y=setTimeout(()=>{s.send("ui/stateChanged",{state:o,...a!==void 0&&{summary:a}}),y=null;},250));},downloadFile(o,a,p){if(!c())return;let g=typeof a=="string"?a:"[Blob content not serializable]";s.send("ui/downloadFile",{data:g,filename:o,mimeType:p??"application/octet-stream"});},openLink(o){c()?s.send("ui/openLink",{url:o}):window.open(o,"_blank","noopener");},_onMessage(o,a){return s.onMessage(o,a)},_request(o,a){return s.request(o,a)},destroy(){w||(w=true,y&&clearTimeout(y),T?.destroy(),d(),f(),S(),m.clear(),h.clear(),s.destroy());}}}function x(r,e){let t=structuredClone(e.initialState),n=new Set,i=false,s=null,l={};for(let d of Object.keys(e.actions))l[d]=f=>{i||(t=e.actions[d](t,f),u());};function u(){for(let d of n)d(t);e.visibleToAgent&&w(),e.persist&&y();}function w(){let d=e.summarize?.(t);r.setVisibleState(t,d);}function y(){s&&clearTimeout(s),s=setTimeout(()=>{r._request("ui/persistState",{state:t,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,m=d.version??1,h=e.version??1;if(e.migrations&&m<h){let S=m-1;for(let c=S;c<e.migrations.length;c++)f=e.migrations[c](f);}v.hydrate(f);}));let v={getState(){return t},subscribe(d){return n.add(d),()=>{n.delete(d);}},dispatch:l,hydrate(d){t=d;for(let f of n)f(t);},destroy(){i||(i=true,s&&clearTimeout(s),n.clear(),T?.());}};return v}window.NbSynapse={createSynapse:j,createStore:x};})();
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};})();