@shiftbloom-studio/symphony-state 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +198 -0
  3. package/dist/adapters/atom.cjs +36 -0
  4. package/dist/adapters/atom.cjs.map +1 -0
  5. package/dist/adapters/atom.d.cts +8 -0
  6. package/dist/adapters/atom.d.ts +8 -0
  7. package/dist/adapters/atom.js +34 -0
  8. package/dist/adapters/atom.js.map +1 -0
  9. package/dist/adapters/external.cjs +16 -0
  10. package/dist/adapters/external.cjs.map +1 -0
  11. package/dist/adapters/external.d.cts +13 -0
  12. package/dist/adapters/external.d.ts +13 -0
  13. package/dist/adapters/external.js +14 -0
  14. package/dist/adapters/external.js.map +1 -0
  15. package/dist/adapters/query.cjs +17 -0
  16. package/dist/adapters/query.cjs.map +1 -0
  17. package/dist/adapters/query.d.cts +16 -0
  18. package/dist/adapters/query.d.ts +16 -0
  19. package/dist/adapters/query.js +15 -0
  20. package/dist/adapters/query.js.map +1 -0
  21. package/dist/adapters/redux.cjs +20 -0
  22. package/dist/adapters/redux.cjs.map +1 -0
  23. package/dist/adapters/redux.d.cts +15 -0
  24. package/dist/adapters/redux.d.ts +15 -0
  25. package/dist/adapters/redux.js +18 -0
  26. package/dist/adapters/redux.js.map +1 -0
  27. package/dist/adapters/url.cjs +39 -0
  28. package/dist/adapters/url.cjs.map +1 -0
  29. package/dist/adapters/url.d.cts +13 -0
  30. package/dist/adapters/url.d.ts +13 -0
  31. package/dist/adapters/url.js +37 -0
  32. package/dist/adapters/url.js.map +1 -0
  33. package/dist/adapters/zustand.cjs +16 -0
  34. package/dist/adapters/zustand.cjs.map +1 -0
  35. package/dist/adapters/zustand.d.cts +10 -0
  36. package/dist/adapters/zustand.d.ts +10 -0
  37. package/dist/adapters/zustand.js +14 -0
  38. package/dist/adapters/zustand.js.map +1 -0
  39. package/dist/devtools.cjs +159 -0
  40. package/dist/devtools.cjs.map +1 -0
  41. package/dist/devtools.d.cts +9 -0
  42. package/dist/devtools.d.ts +9 -0
  43. package/dist/devtools.js +157 -0
  44. package/dist/devtools.js.map +1 -0
  45. package/dist/index.cjs +560 -0
  46. package/dist/index.cjs.map +1 -0
  47. package/dist/index.d.cts +20 -0
  48. package/dist/index.d.ts +20 -0
  49. package/dist/index.js +552 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/react.cjs +576 -0
  52. package/dist/react.cjs.map +1 -0
  53. package/dist/react.d.cts +40 -0
  54. package/dist/react.d.ts +40 -0
  55. package/dist/react.js +569 -0
  56. package/dist/react.js.map +1 -0
  57. package/dist/types-B1uzdpYH.d.cts +67 -0
  58. package/dist/types-B1uzdpYH.d.ts +67 -0
  59. package/package.json +127 -0
@@ -0,0 +1,37 @@
1
+ // src/adapters/url.ts
2
+ var defaultGetParams = () => {
3
+ if (typeof window === "undefined") {
4
+ return new URLSearchParams();
5
+ }
6
+ return new URLSearchParams(window.location.search);
7
+ };
8
+ var defaultSetParams = (params) => {
9
+ if (typeof window === "undefined") {
10
+ return;
11
+ }
12
+ const url = `${window.location.pathname}?${params.toString()}${window.location.hash}`;
13
+ window.history.replaceState({}, "", url);
14
+ };
15
+ var defaultSubscribe = (cb) => {
16
+ if (typeof window === "undefined") {
17
+ return () => void 0;
18
+ }
19
+ const handler = () => cb();
20
+ window.addEventListener("popstate", handler);
21
+ return () => window.removeEventListener("popstate", handler);
22
+ };
23
+ var createUrlParamsAdapter = (options) => {
24
+ const getParams = options.getSearchParams ?? defaultGetParams;
25
+ const setParams = options.setSearchParams ?? defaultSetParams;
26
+ const subscribe = options.subscribe ?? defaultSubscribe;
27
+ return {
28
+ kind: options.kind ?? "url",
29
+ get: () => options.parse(getParams()),
30
+ set: (next) => setParams(options.serialize(next)),
31
+ subscribe
32
+ };
33
+ };
34
+
35
+ export { createUrlParamsAdapter };
36
+ //# sourceMappingURL=url.js.map
37
+ //# sourceMappingURL=url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/url.ts"],"names":[],"mappings":";AAWA,IAAM,mBAAmB,MAAM;AAC7B,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACjC,IAAA,OAAO,IAAI,eAAA,EAAgB;AAAA,EAC7B;AACA,EAAA,OAAO,IAAI,eAAA,CAAgB,MAAA,CAAO,QAAA,CAAS,MAAM,CAAA;AACnD,CAAA;AAEA,IAAM,gBAAA,GAAmB,CAAC,MAAA,KAA4B;AACpD,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACjC,IAAA;AAAA,EACF;AACA,EAAA,MAAM,GAAA,GAAM,CAAA,EAAG,MAAA,CAAO,QAAA,CAAS,QAAQ,CAAA,CAAA,EAAI,MAAA,CAAO,QAAA,EAAU,CAAA,EAAG,MAAA,CAAO,QAAA,CAAS,IAAI,CAAA,CAAA;AACnF,EAAA,MAAA,CAAO,OAAA,CAAQ,YAAA,CAAa,EAAC,EAAG,IAAI,GAAG,CAAA;AACzC,CAAA;AAEA,IAAM,gBAAA,GAAmB,CAAC,EAAA,KAAgC;AACxD,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACjC,IAAA,OAAO,MAAM,MAAA;AAAA,EACf;AACA,EAAA,MAAM,OAAA,GAAU,MAAM,EAAA,EAAG;AACzB,EAAA,MAAA,CAAO,gBAAA,CAAiB,YAAY,OAAO,CAAA;AAC3C,EAAA,OAAO,MAAM,MAAA,CAAO,mBAAA,CAAoB,UAAA,EAAY,OAAO,CAAA;AAC7D,CAAA;AAEO,IAAM,sBAAA,GAAyB,CACpC,OAAA,KACqB;AACrB,EAAA,MAAM,SAAA,GAAY,QAAQ,eAAA,IAAmB,gBAAA;AAC7C,EAAA,MAAM,SAAA,GAAY,QAAQ,eAAA,IAAmB,gBAAA;AAC7C,EAAA,MAAM,SAAA,GAAY,QAAQ,SAAA,IAAa,gBAAA;AAEvC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,QAAQ,IAAA,IAAQ,KAAA;AAAA,IACtB,GAAA,EAAK,MAAM,OAAA,CAAQ,KAAA,CAAM,WAAW,CAAA;AAAA,IACpC,KAAK,CAAC,IAAA,KAAS,UAAU,OAAA,CAAQ,SAAA,CAAU,IAAI,CAAC,CAAA;AAAA,IAChD;AAAA,GACF;AACF","file":"url.js","sourcesContent":["import type { SourceAdapter, Subscriber, Unsubscribe } from \"../core/types\";\n\nexport type UrlAdapterOptions<T> = {\n parse: (params: URLSearchParams) => T;\n serialize: (value: T) => URLSearchParams;\n getSearchParams?: () => URLSearchParams;\n setSearchParams?: (params: URLSearchParams) => void;\n subscribe?: (cb: Subscriber) => Unsubscribe;\n kind?: string;\n};\n\nconst defaultGetParams = () => {\n if (typeof window === \"undefined\") {\n return new URLSearchParams();\n }\n return new URLSearchParams(window.location.search);\n};\n\nconst defaultSetParams = (params: URLSearchParams) => {\n if (typeof window === \"undefined\") {\n return;\n }\n const url = `${window.location.pathname}?${params.toString()}${window.location.hash}`;\n window.history.replaceState({}, \"\", url);\n};\n\nconst defaultSubscribe = (cb: Subscriber): Unsubscribe => {\n if (typeof window === \"undefined\") {\n return () => undefined;\n }\n const handler = () => cb();\n window.addEventListener(\"popstate\", handler);\n return () => window.removeEventListener(\"popstate\", handler);\n};\n\nexport const createUrlParamsAdapter = <T>(\n options: UrlAdapterOptions<T>\n): SourceAdapter<T> => {\n const getParams = options.getSearchParams ?? defaultGetParams;\n const setParams = options.setSearchParams ?? defaultSetParams;\n const subscribe = options.subscribe ?? defaultSubscribe;\n\n return {\n kind: options.kind ?? \"url\",\n get: () => options.parse(getParams()),\n set: (next) => setParams(options.serialize(next)),\n subscribe\n };\n};\n"]}
@@ -0,0 +1,16 @@
1
+ 'use strict';
2
+
3
+ // src/adapters/zustand.ts
4
+ var createZustandAdapter = (store) => {
5
+ return {
6
+ kind: "zustand",
7
+ get: store.getState,
8
+ set: (next) => store.setState(next, true),
9
+ patch: (partial) => store.setState(partial, false),
10
+ subscribe: (cb) => store.subscribe(() => cb())
11
+ };
12
+ };
13
+
14
+ exports.createZustandAdapter = createZustandAdapter;
15
+ //# sourceMappingURL=zustand.cjs.map
16
+ //# sourceMappingURL=zustand.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/zustand.ts"],"names":[],"mappings":";;;AAQO,IAAM,oBAAA,GAAuB,CAAI,KAAA,KAA6C;AACnF,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,SAAA;AAAA,IACN,KAAK,KAAA,CAAM,QAAA;AAAA,IACX,KAAK,CAAC,IAAA,KAAS,KAAA,CAAM,QAAA,CAAS,MAAM,IAAI,CAAA;AAAA,IACxC,OAAO,CAAC,OAAA,KAAY,KAAA,CAAM,QAAA,CAAS,SAAS,KAAK,CAAA;AAAA,IACjD,WAAW,CAAC,EAAA,KAAmB,MAAM,SAAA,CAAU,MAAM,IAAI;AAAA,GAC3D;AACF","file":"zustand.cjs","sourcesContent":["import type { SourceAdapter, Subscriber, Unsubscribe } from \"../core/types\";\n\nexport type ZustandStore<T> = {\n getState: () => T;\n setState: (next: T | Partial<T>, replace?: boolean) => void;\n subscribe: (listener: (state: T, prevState: T) => void) => Unsubscribe;\n};\n\nexport const createZustandAdapter = <T>(store: ZustandStore<T>): SourceAdapter<T> => {\n return {\n kind: \"zustand\",\n get: store.getState,\n set: (next) => store.setState(next, true),\n patch: (partial) => store.setState(partial, false),\n subscribe: (cb: Subscriber) => store.subscribe(() => cb())\n };\n};\n"]}
@@ -0,0 +1,10 @@
1
+ import { U as Unsubscribe, d as SourceAdapter } from '../types-B1uzdpYH.cjs';
2
+
3
+ type ZustandStore<T> = {
4
+ getState: () => T;
5
+ setState: (next: T | Partial<T>, replace?: boolean) => void;
6
+ subscribe: (listener: (state: T, prevState: T) => void) => Unsubscribe;
7
+ };
8
+ declare const createZustandAdapter: <T>(store: ZustandStore<T>) => SourceAdapter<T>;
9
+
10
+ export { type ZustandStore, createZustandAdapter };
@@ -0,0 +1,10 @@
1
+ import { U as Unsubscribe, d as SourceAdapter } from '../types-B1uzdpYH.js';
2
+
3
+ type ZustandStore<T> = {
4
+ getState: () => T;
5
+ setState: (next: T | Partial<T>, replace?: boolean) => void;
6
+ subscribe: (listener: (state: T, prevState: T) => void) => Unsubscribe;
7
+ };
8
+ declare const createZustandAdapter: <T>(store: ZustandStore<T>) => SourceAdapter<T>;
9
+
10
+ export { type ZustandStore, createZustandAdapter };
@@ -0,0 +1,14 @@
1
+ // src/adapters/zustand.ts
2
+ var createZustandAdapter = (store) => {
3
+ return {
4
+ kind: "zustand",
5
+ get: store.getState,
6
+ set: (next) => store.setState(next, true),
7
+ patch: (partial) => store.setState(partial, false),
8
+ subscribe: (cb) => store.subscribe(() => cb())
9
+ };
10
+ };
11
+
12
+ export { createZustandAdapter };
13
+ //# sourceMappingURL=zustand.js.map
14
+ //# sourceMappingURL=zustand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/zustand.ts"],"names":[],"mappings":";AAQO,IAAM,oBAAA,GAAuB,CAAI,KAAA,KAA6C;AACnF,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,SAAA;AAAA,IACN,KAAK,KAAA,CAAM,QAAA;AAAA,IACX,KAAK,CAAC,IAAA,KAAS,KAAA,CAAM,QAAA,CAAS,MAAM,IAAI,CAAA;AAAA,IACxC,OAAO,CAAC,OAAA,KAAY,KAAA,CAAM,QAAA,CAAS,SAAS,KAAK,CAAA;AAAA,IACjD,WAAW,CAAC,EAAA,KAAmB,MAAM,SAAA,CAAU,MAAM,IAAI;AAAA,GAC3D;AACF","file":"zustand.js","sourcesContent":["import type { SourceAdapter, Subscriber, Unsubscribe } from \"../core/types\";\n\nexport type ZustandStore<T> = {\n getState: () => T;\n setState: (next: T | Partial<T>, replace?: boolean) => void;\n subscribe: (listener: (state: T, prevState: T) => void) => Unsubscribe;\n};\n\nexport const createZustandAdapter = <T>(store: ZustandStore<T>): SourceAdapter<T> => {\n return {\n kind: \"zustand\",\n get: store.getState,\n set: (next) => store.setState(next, true),\n patch: (partial) => store.setState(partial, false),\n subscribe: (cb: Subscriber) => store.subscribe(() => cb())\n };\n};\n"]}
@@ -0,0 +1,159 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+
6
+ // src/devtools/SymphonyDevTools.tsx
7
+ var SymphonyContext = react.createContext(null);
8
+ var useConductor = () => {
9
+ const conductor = react.useContext(SymphonyContext);
10
+ if (!conductor) {
11
+ throw new Error("SymphonyProvider is missing in the React tree.");
12
+ }
13
+ return conductor;
14
+ };
15
+ var truncate = (value, limit = 120) => {
16
+ const json = JSON.stringify(value);
17
+ if (!json) {
18
+ return String(value);
19
+ }
20
+ if (json.length <= limit) {
21
+ return json;
22
+ }
23
+ return `${json.slice(0, limit)}\u2026`;
24
+ };
25
+ var SymphonyDevTools = ({
26
+ maxTransactions = 10,
27
+ redact
28
+ }) => {
29
+ if (process.env.NODE_ENV === "production") {
30
+ return null;
31
+ }
32
+ const conductor = useConductor();
33
+ const [paused, setPaused] = react.useState(false);
34
+ const lastSnapshotRef = react.useRef(conductor.getSnapshot());
35
+ const subscribe = react.useCallback(
36
+ (cb) => {
37
+ const keys = Object.keys(conductor.getSnapshot().sections);
38
+ const unsubs = keys.map((key) => conductor.subscribe(key, cb));
39
+ return () => {
40
+ unsubs.forEach((unsub) => unsub());
41
+ };
42
+ },
43
+ [conductor]
44
+ );
45
+ const getSnapshot = react.useCallback(() => conductor.getSnapshot(), [conductor]);
46
+ const snapshot = react.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
47
+ const displaySnapshot = react.useMemo(() => {
48
+ if (!paused) {
49
+ lastSnapshotRef.current = snapshot;
50
+ }
51
+ return lastSnapshotRef.current;
52
+ }, [paused, snapshot]);
53
+ const sectionEntries = Object.entries(displaySnapshot.sections).map(
54
+ ([key, value]) => ({
55
+ key,
56
+ value: redact ? redact(value, key) : value
57
+ })
58
+ );
59
+ const exportSnapshot = () => {
60
+ if (typeof window === "undefined") {
61
+ return;
62
+ }
63
+ const blob = new Blob([JSON.stringify(displaySnapshot, null, 2)], {
64
+ type: "application/json"
65
+ });
66
+ const url = URL.createObjectURL(blob);
67
+ const anchor = document.createElement("a");
68
+ anchor.href = url;
69
+ anchor.download = "symphony-snapshot.json";
70
+ anchor.click();
71
+ URL.revokeObjectURL(url);
72
+ };
73
+ return /* @__PURE__ */ jsxRuntime.jsxs(
74
+ "div",
75
+ {
76
+ style: {
77
+ position: "fixed",
78
+ right: 16,
79
+ bottom: 16,
80
+ width: 320,
81
+ maxHeight: 420,
82
+ overflow: "auto",
83
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
84
+ background: "var(--symphony-devtools-bg, #101114)",
85
+ color: "var(--symphony-devtools-fg, #e6e8ef)",
86
+ border: "1px solid var(--symphony-devtools-border, #2a2d35)",
87
+ borderRadius: 8,
88
+ padding: 12,
89
+ zIndex: 9999
90
+ },
91
+ children: [
92
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
93
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "Symphony DevTools" }),
94
+ /* @__PURE__ */ jsxRuntime.jsx(
95
+ "button",
96
+ {
97
+ type: "button",
98
+ onClick: () => setPaused((prev) => !prev),
99
+ style: {
100
+ fontSize: 12,
101
+ background: "transparent",
102
+ color: "inherit",
103
+ border: "1px solid currentColor",
104
+ borderRadius: 4,
105
+ padding: "2px 6px",
106
+ cursor: "pointer"
107
+ },
108
+ children: paused ? "Resume" : "Pause"
109
+ }
110
+ )
111
+ ] }),
112
+ /* @__PURE__ */ jsxRuntime.jsxs("section", { style: { marginTop: 12 }, children: [
113
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
114
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "Sections" }),
115
+ /* @__PURE__ */ jsxRuntime.jsx(
116
+ "button",
117
+ {
118
+ type: "button",
119
+ onClick: exportSnapshot,
120
+ style: {
121
+ fontSize: 12,
122
+ background: "transparent",
123
+ color: "inherit",
124
+ border: "1px solid currentColor",
125
+ borderRadius: 4,
126
+ padding: "2px 6px",
127
+ cursor: "pointer"
128
+ },
129
+ children: "Export"
130
+ }
131
+ )
132
+ ] }),
133
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { style: { listStyle: "none", padding: 0, margin: "8px 0 0" }, children: sectionEntries.map(({ key, value }) => /* @__PURE__ */ jsxRuntime.jsxs("li", { style: { marginBottom: 6 }, children: [
134
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontWeight: 600 }, children: key }),
135
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 12, opacity: 0.8 }, children: truncate(value) })
136
+ ] }, key)) })
137
+ ] }),
138
+ /* @__PURE__ */ jsxRuntime.jsxs("section", { style: { marginTop: 12 }, children: [
139
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "Transactions" }),
140
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { style: { listStyle: "none", padding: 0, margin: "8px 0 0" }, children: displaySnapshot.transactions.slice(0, maxTransactions).map((tx) => /* @__PURE__ */ jsxRuntime.jsxs(
141
+ "li",
142
+ {
143
+ style: { marginBottom: 6 },
144
+ children: [
145
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontWeight: 600 }, children: tx.label ?? "transaction" }),
146
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 12, opacity: 0.8 }, children: tx.touched.join(", ") })
147
+ ]
148
+ },
149
+ `${tx.timestamp}-${tx.label ?? "tx"}`
150
+ )) })
151
+ ] })
152
+ ]
153
+ }
154
+ );
155
+ };
156
+
157
+ exports.SymphonyDevTools = SymphonyDevTools;
158
+ //# sourceMappingURL=devtools.cjs.map
159
+ //# sourceMappingURL=devtools.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react/SymphonyProvider.tsx","../src/devtools/SymphonyDevTools.tsx"],"names":["createContext","useContext","useState","useRef","useCallback","useSyncExternalStore","useMemo","jsxs","jsx"],"mappings":"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","file":"devtools.cjs","sourcesContent":["import React, { createContext, useContext } from \"react\";\nimport type { Conductor } from \"../core/types\";\n\nconst SymphonyContext = createContext<Conductor | null>(null);\n\nexport type SymphonyProviderProps = {\n conductor: Conductor;\n children: React.ReactNode;\n};\n\nexport const SymphonyProvider = ({ conductor, children }: SymphonyProviderProps) => {\n return (\n <SymphonyContext.Provider value={conductor}>\n {children}\n </SymphonyContext.Provider>\n );\n};\n\nexport const useConductor = () => {\n const conductor = useContext(SymphonyContext);\n if (!conductor) {\n throw new Error(\"SymphonyProvider is missing in the React tree.\");\n }\n return conductor;\n};\n","import React, { useCallback, useMemo, useRef, useState, useSyncExternalStore } from \"react\";\nimport { useConductor } from \"../react/SymphonyProvider\";\n\nconst truncate = (value: unknown, limit = 120) => {\n const json = JSON.stringify(value);\n if (!json) {\n return String(value);\n }\n if (json.length <= limit) {\n return json;\n }\n return `${json.slice(0, limit)}…`;\n};\n\nexport type SymphonyDevToolsProps = {\n maxTransactions?: number;\n redact?: (value: unknown, key: string) => unknown;\n};\n\nexport const SymphonyDevTools = ({\n maxTransactions = 10,\n redact\n}: SymphonyDevToolsProps) => {\n if (process.env.NODE_ENV === \"production\") {\n return null;\n }\n const conductor = useConductor();\n const [paused, setPaused] = useState(false);\n const lastSnapshotRef = useRef(conductor.getSnapshot());\n\n const subscribe = useCallback(\n (cb: () => void) => {\n const keys = Object.keys(conductor.getSnapshot().sections);\n const unsubs = keys.map((key) => conductor.subscribe(key, cb));\n return () => {\n unsubs.forEach((unsub) => unsub());\n };\n },\n [conductor]\n );\n\n const getSnapshot = useCallback(() => conductor.getSnapshot(), [conductor]);\n\n const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n const displaySnapshot = useMemo(() => {\n if (!paused) {\n lastSnapshotRef.current = snapshot;\n }\n return lastSnapshotRef.current;\n }, [paused, snapshot]);\n\n const sectionEntries = Object.entries(displaySnapshot.sections).map(\n ([key, value]) => ({\n key,\n value: redact ? redact(value, key) : value\n })\n );\n\n const exportSnapshot = () => {\n if (typeof window === \"undefined\") {\n return;\n }\n const blob = new Blob([JSON.stringify(displaySnapshot, null, 2)], {\n type: \"application/json\"\n });\n const url = URL.createObjectURL(blob);\n const anchor = document.createElement(\"a\");\n anchor.href = url;\n anchor.download = \"symphony-snapshot.json\";\n anchor.click();\n URL.revokeObjectURL(url);\n };\n\n return (\n <div\n style={{\n position: \"fixed\",\n right: 16,\n bottom: 16,\n width: 320,\n maxHeight: 420,\n overflow: \"auto\",\n fontFamily: \"ui-monospace, SFMono-Regular, Menlo, monospace\",\n background: \"var(--symphony-devtools-bg, #101114)\",\n color: \"var(--symphony-devtools-fg, #e6e8ef)\",\n border: \"1px solid var(--symphony-devtools-border, #2a2d35)\",\n borderRadius: 8,\n padding: 12,\n zIndex: 9999\n }}\n >\n <div style={{ display: \"flex\", justifyContent: \"space-between\" }}>\n <strong>Symphony DevTools</strong>\n <button\n type=\"button\"\n onClick={() => setPaused((prev) => !prev)}\n style={{\n fontSize: 12,\n background: \"transparent\",\n color: \"inherit\",\n border: \"1px solid currentColor\",\n borderRadius: 4,\n padding: \"2px 6px\",\n cursor: \"pointer\"\n }}\n >\n {paused ? \"Resume\" : \"Pause\"}\n </button>\n </div>\n\n <section style={{ marginTop: 12 }}>\n <div style={{ display: \"flex\", justifyContent: \"space-between\" }}>\n <strong>Sections</strong>\n <button\n type=\"button\"\n onClick={exportSnapshot}\n style={{\n fontSize: 12,\n background: \"transparent\",\n color: \"inherit\",\n border: \"1px solid currentColor\",\n borderRadius: 4,\n padding: \"2px 6px\",\n cursor: \"pointer\"\n }}\n >\n Export\n </button>\n </div>\n <ul style={{ listStyle: \"none\", padding: 0, margin: \"8px 0 0\" }}>\n {sectionEntries.map(({ key, value }) => (\n <li key={key} style={{ marginBottom: 6 }}>\n <div style={{ fontWeight: 600 }}>{key}</div>\n <div style={{ fontSize: 12, opacity: 0.8 }}>\n {truncate(value)}\n </div>\n </li>\n ))}\n </ul>\n </section>\n\n <section style={{ marginTop: 12 }}>\n <strong>Transactions</strong>\n <ul style={{ listStyle: \"none\", padding: 0, margin: \"8px 0 0\" }}>\n {displaySnapshot.transactions.slice(0, maxTransactions).map((tx) => (\n <li key={`${tx.timestamp}-${tx.label ?? \"tx\"}`}\n style={{ marginBottom: 6 }}\n >\n <div style={{ fontWeight: 600 }}>\n {tx.label ?? \"transaction\"}\n </div>\n <div style={{ fontSize: 12, opacity: 0.8 }}>\n {tx.touched.join(\", \")}\n </div>\n </li>\n ))}\n </ul>\n </section>\n </div>\n );\n};\n"]}
@@ -0,0 +1,9 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ type SymphonyDevToolsProps = {
4
+ maxTransactions?: number;
5
+ redact?: (value: unknown, key: string) => unknown;
6
+ };
7
+ declare const SymphonyDevTools: ({ maxTransactions, redact }: SymphonyDevToolsProps) => react_jsx_runtime.JSX.Element | null;
8
+
9
+ export { SymphonyDevTools, type SymphonyDevToolsProps };
@@ -0,0 +1,9 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ type SymphonyDevToolsProps = {
4
+ maxTransactions?: number;
5
+ redact?: (value: unknown, key: string) => unknown;
6
+ };
7
+ declare const SymphonyDevTools: ({ maxTransactions, redact }: SymphonyDevToolsProps) => react_jsx_runtime.JSX.Element | null;
8
+
9
+ export { SymphonyDevTools, type SymphonyDevToolsProps };
@@ -0,0 +1,157 @@
1
+ import { createContext, useState, useRef, useCallback, useSyncExternalStore, useMemo, useContext } from 'react';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+
4
+ // src/devtools/SymphonyDevTools.tsx
5
+ var SymphonyContext = createContext(null);
6
+ var useConductor = () => {
7
+ const conductor = useContext(SymphonyContext);
8
+ if (!conductor) {
9
+ throw new Error("SymphonyProvider is missing in the React tree.");
10
+ }
11
+ return conductor;
12
+ };
13
+ var truncate = (value, limit = 120) => {
14
+ const json = JSON.stringify(value);
15
+ if (!json) {
16
+ return String(value);
17
+ }
18
+ if (json.length <= limit) {
19
+ return json;
20
+ }
21
+ return `${json.slice(0, limit)}\u2026`;
22
+ };
23
+ var SymphonyDevTools = ({
24
+ maxTransactions = 10,
25
+ redact
26
+ }) => {
27
+ if (process.env.NODE_ENV === "production") {
28
+ return null;
29
+ }
30
+ const conductor = useConductor();
31
+ const [paused, setPaused] = useState(false);
32
+ const lastSnapshotRef = useRef(conductor.getSnapshot());
33
+ const subscribe = useCallback(
34
+ (cb) => {
35
+ const keys = Object.keys(conductor.getSnapshot().sections);
36
+ const unsubs = keys.map((key) => conductor.subscribe(key, cb));
37
+ return () => {
38
+ unsubs.forEach((unsub) => unsub());
39
+ };
40
+ },
41
+ [conductor]
42
+ );
43
+ const getSnapshot = useCallback(() => conductor.getSnapshot(), [conductor]);
44
+ const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
45
+ const displaySnapshot = useMemo(() => {
46
+ if (!paused) {
47
+ lastSnapshotRef.current = snapshot;
48
+ }
49
+ return lastSnapshotRef.current;
50
+ }, [paused, snapshot]);
51
+ const sectionEntries = Object.entries(displaySnapshot.sections).map(
52
+ ([key, value]) => ({
53
+ key,
54
+ value: redact ? redact(value, key) : value
55
+ })
56
+ );
57
+ const exportSnapshot = () => {
58
+ if (typeof window === "undefined") {
59
+ return;
60
+ }
61
+ const blob = new Blob([JSON.stringify(displaySnapshot, null, 2)], {
62
+ type: "application/json"
63
+ });
64
+ const url = URL.createObjectURL(blob);
65
+ const anchor = document.createElement("a");
66
+ anchor.href = url;
67
+ anchor.download = "symphony-snapshot.json";
68
+ anchor.click();
69
+ URL.revokeObjectURL(url);
70
+ };
71
+ return /* @__PURE__ */ jsxs(
72
+ "div",
73
+ {
74
+ style: {
75
+ position: "fixed",
76
+ right: 16,
77
+ bottom: 16,
78
+ width: 320,
79
+ maxHeight: 420,
80
+ overflow: "auto",
81
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
82
+ background: "var(--symphony-devtools-bg, #101114)",
83
+ color: "var(--symphony-devtools-fg, #e6e8ef)",
84
+ border: "1px solid var(--symphony-devtools-border, #2a2d35)",
85
+ borderRadius: 8,
86
+ padding: 12,
87
+ zIndex: 9999
88
+ },
89
+ children: [
90
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
91
+ /* @__PURE__ */ jsx("strong", { children: "Symphony DevTools" }),
92
+ /* @__PURE__ */ jsx(
93
+ "button",
94
+ {
95
+ type: "button",
96
+ onClick: () => setPaused((prev) => !prev),
97
+ style: {
98
+ fontSize: 12,
99
+ background: "transparent",
100
+ color: "inherit",
101
+ border: "1px solid currentColor",
102
+ borderRadius: 4,
103
+ padding: "2px 6px",
104
+ cursor: "pointer"
105
+ },
106
+ children: paused ? "Resume" : "Pause"
107
+ }
108
+ )
109
+ ] }),
110
+ /* @__PURE__ */ jsxs("section", { style: { marginTop: 12 }, children: [
111
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
112
+ /* @__PURE__ */ jsx("strong", { children: "Sections" }),
113
+ /* @__PURE__ */ jsx(
114
+ "button",
115
+ {
116
+ type: "button",
117
+ onClick: exportSnapshot,
118
+ style: {
119
+ fontSize: 12,
120
+ background: "transparent",
121
+ color: "inherit",
122
+ border: "1px solid currentColor",
123
+ borderRadius: 4,
124
+ padding: "2px 6px",
125
+ cursor: "pointer"
126
+ },
127
+ children: "Export"
128
+ }
129
+ )
130
+ ] }),
131
+ /* @__PURE__ */ jsx("ul", { style: { listStyle: "none", padding: 0, margin: "8px 0 0" }, children: sectionEntries.map(({ key, value }) => /* @__PURE__ */ jsxs("li", { style: { marginBottom: 6 }, children: [
132
+ /* @__PURE__ */ jsx("div", { style: { fontWeight: 600 }, children: key }),
133
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 12, opacity: 0.8 }, children: truncate(value) })
134
+ ] }, key)) })
135
+ ] }),
136
+ /* @__PURE__ */ jsxs("section", { style: { marginTop: 12 }, children: [
137
+ /* @__PURE__ */ jsx("strong", { children: "Transactions" }),
138
+ /* @__PURE__ */ jsx("ul", { style: { listStyle: "none", padding: 0, margin: "8px 0 0" }, children: displaySnapshot.transactions.slice(0, maxTransactions).map((tx) => /* @__PURE__ */ jsxs(
139
+ "li",
140
+ {
141
+ style: { marginBottom: 6 },
142
+ children: [
143
+ /* @__PURE__ */ jsx("div", { style: { fontWeight: 600 }, children: tx.label ?? "transaction" }),
144
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 12, opacity: 0.8 }, children: tx.touched.join(", ") })
145
+ ]
146
+ },
147
+ `${tx.timestamp}-${tx.label ?? "tx"}`
148
+ )) })
149
+ ] })
150
+ ]
151
+ }
152
+ );
153
+ };
154
+
155
+ export { SymphonyDevTools };
156
+ //# sourceMappingURL=devtools.js.map
157
+ //# sourceMappingURL=devtools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react/SymphonyProvider.tsx","../src/devtools/SymphonyDevTools.tsx"],"names":["jsx"],"mappings":"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","file":"devtools.js","sourcesContent":["import React, { createContext, useContext } from \"react\";\nimport type { Conductor } from \"../core/types\";\n\nconst SymphonyContext = createContext<Conductor | null>(null);\n\nexport type SymphonyProviderProps = {\n conductor: Conductor;\n children: React.ReactNode;\n};\n\nexport const SymphonyProvider = ({ conductor, children }: SymphonyProviderProps) => {\n return (\n <SymphonyContext.Provider value={conductor}>\n {children}\n </SymphonyContext.Provider>\n );\n};\n\nexport const useConductor = () => {\n const conductor = useContext(SymphonyContext);\n if (!conductor) {\n throw new Error(\"SymphonyProvider is missing in the React tree.\");\n }\n return conductor;\n};\n","import React, { useCallback, useMemo, useRef, useState, useSyncExternalStore } from \"react\";\nimport { useConductor } from \"../react/SymphonyProvider\";\n\nconst truncate = (value: unknown, limit = 120) => {\n const json = JSON.stringify(value);\n if (!json) {\n return String(value);\n }\n if (json.length <= limit) {\n return json;\n }\n return `${json.slice(0, limit)}…`;\n};\n\nexport type SymphonyDevToolsProps = {\n maxTransactions?: number;\n redact?: (value: unknown, key: string) => unknown;\n};\n\nexport const SymphonyDevTools = ({\n maxTransactions = 10,\n redact\n}: SymphonyDevToolsProps) => {\n if (process.env.NODE_ENV === \"production\") {\n return null;\n }\n const conductor = useConductor();\n const [paused, setPaused] = useState(false);\n const lastSnapshotRef = useRef(conductor.getSnapshot());\n\n const subscribe = useCallback(\n (cb: () => void) => {\n const keys = Object.keys(conductor.getSnapshot().sections);\n const unsubs = keys.map((key) => conductor.subscribe(key, cb));\n return () => {\n unsubs.forEach((unsub) => unsub());\n };\n },\n [conductor]\n );\n\n const getSnapshot = useCallback(() => conductor.getSnapshot(), [conductor]);\n\n const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n const displaySnapshot = useMemo(() => {\n if (!paused) {\n lastSnapshotRef.current = snapshot;\n }\n return lastSnapshotRef.current;\n }, [paused, snapshot]);\n\n const sectionEntries = Object.entries(displaySnapshot.sections).map(\n ([key, value]) => ({\n key,\n value: redact ? redact(value, key) : value\n })\n );\n\n const exportSnapshot = () => {\n if (typeof window === \"undefined\") {\n return;\n }\n const blob = new Blob([JSON.stringify(displaySnapshot, null, 2)], {\n type: \"application/json\"\n });\n const url = URL.createObjectURL(blob);\n const anchor = document.createElement(\"a\");\n anchor.href = url;\n anchor.download = \"symphony-snapshot.json\";\n anchor.click();\n URL.revokeObjectURL(url);\n };\n\n return (\n <div\n style={{\n position: \"fixed\",\n right: 16,\n bottom: 16,\n width: 320,\n maxHeight: 420,\n overflow: \"auto\",\n fontFamily: \"ui-monospace, SFMono-Regular, Menlo, monospace\",\n background: \"var(--symphony-devtools-bg, #101114)\",\n color: \"var(--symphony-devtools-fg, #e6e8ef)\",\n border: \"1px solid var(--symphony-devtools-border, #2a2d35)\",\n borderRadius: 8,\n padding: 12,\n zIndex: 9999\n }}\n >\n <div style={{ display: \"flex\", justifyContent: \"space-between\" }}>\n <strong>Symphony DevTools</strong>\n <button\n type=\"button\"\n onClick={() => setPaused((prev) => !prev)}\n style={{\n fontSize: 12,\n background: \"transparent\",\n color: \"inherit\",\n border: \"1px solid currentColor\",\n borderRadius: 4,\n padding: \"2px 6px\",\n cursor: \"pointer\"\n }}\n >\n {paused ? \"Resume\" : \"Pause\"}\n </button>\n </div>\n\n <section style={{ marginTop: 12 }}>\n <div style={{ display: \"flex\", justifyContent: \"space-between\" }}>\n <strong>Sections</strong>\n <button\n type=\"button\"\n onClick={exportSnapshot}\n style={{\n fontSize: 12,\n background: \"transparent\",\n color: \"inherit\",\n border: \"1px solid currentColor\",\n borderRadius: 4,\n padding: \"2px 6px\",\n cursor: \"pointer\"\n }}\n >\n Export\n </button>\n </div>\n <ul style={{ listStyle: \"none\", padding: 0, margin: \"8px 0 0\" }}>\n {sectionEntries.map(({ key, value }) => (\n <li key={key} style={{ marginBottom: 6 }}>\n <div style={{ fontWeight: 600 }}>{key}</div>\n <div style={{ fontSize: 12, opacity: 0.8 }}>\n {truncate(value)}\n </div>\n </li>\n ))}\n </ul>\n </section>\n\n <section style={{ marginTop: 12 }}>\n <strong>Transactions</strong>\n <ul style={{ listStyle: \"none\", padding: 0, margin: \"8px 0 0\" }}>\n {displaySnapshot.transactions.slice(0, maxTransactions).map((tx) => (\n <li key={`${tx.timestamp}-${tx.label ?? \"tx\"}`}\n style={{ marginBottom: 6 }}\n >\n <div style={{ fontWeight: 600 }}>\n {tx.label ?? \"transaction\"}\n </div>\n <div style={{ fontSize: 12, opacity: 0.8 }}>\n {tx.touched.join(\", \")}\n </div>\n </li>\n ))}\n </ul>\n </section>\n </div>\n );\n};\n"]}