@buoy-gg/shared-ui 7.0.32 → 7.0.34

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 (237) hide show
  1. package/lib/commonjs/JsModal.js +70 -28
  2. package/lib/commonjs/hooks/index.js +26 -0
  3. package/lib/commonjs/hooks/safe-area-impl.js +1 -1
  4. package/lib/commonjs/hooks/useChangeLog.js +55 -0
  5. package/lib/commonjs/hooks/useEventStoreEvents.js +65 -0
  6. package/lib/commonjs/hooks/usePersistedState.js +45 -10
  7. package/lib/commonjs/index.js +143 -0
  8. package/lib/commonjs/storage/definePersistedSetting.js +67 -0
  9. package/lib/commonjs/storage/devToolsStorageKeys.js +12 -0
  10. package/lib/commonjs/storage/index.js +8 -1
  11. package/lib/commonjs/stores/ChangeLog.js +197 -0
  12. package/lib/commonjs/stores/createToolStore.js +175 -0
  13. package/lib/commonjs/stores/index.js +15 -1
  14. package/lib/commonjs/sync/toolSyncAdapter.js +63 -0
  15. package/lib/commonjs/sync/wireForm.js +172 -0
  16. package/lib/commonjs/tools/defineTool.js +79 -0
  17. package/lib/commonjs/ui/components/ChangeRow.js +110 -0
  18. package/lib/commonjs/ui/components/ChipStrip.js +94 -0
  19. package/lib/commonjs/ui/components/DetailViewToggle.js +130 -0
  20. package/lib/commonjs/ui/components/DevToolsCard.js +3 -1
  21. package/lib/commonjs/ui/components/EmptyState.js +10 -2
  22. package/lib/commonjs/ui/components/EntityFilterView.js +354 -0
  23. package/lib/commonjs/ui/components/ExportPreview.js +174 -0
  24. package/lib/commonjs/ui/components/InfoRow.js +90 -0
  25. package/lib/commonjs/ui/components/ModalHeader.js +40 -7
  26. package/lib/commonjs/ui/components/Pill.js +114 -0
  27. package/lib/commonjs/ui/components/ToggleRow.js +116 -0
  28. package/lib/commonjs/ui/components/ToolEmptyState.js +154 -0
  29. package/lib/commonjs/ui/components/ToolFooter.js +204 -0
  30. package/lib/commonjs/ui/components/WindowControls.js +8 -5
  31. package/lib/commonjs/ui/components/index.js +113 -1
  32. package/lib/commonjs/ui/index.js +36 -2
  33. package/lib/commonjs/ui/listPresets.js +43 -0
  34. package/lib/commonjs/ui/night/primitives.js +3 -1
  35. package/lib/commonjs/ui/otto/OttoSprite.js +510 -0
  36. package/lib/commonjs/ui/otto/index.js +38 -0
  37. package/lib/commonjs/ui/otto/ottoEngine.js +2734 -0
  38. package/lib/commonjs/ui/otto/ottoSheet.js +191 -0
  39. package/lib/commonjs/utils/snapshotProviderKit.js +167 -0
  40. package/lib/commonjs/utils/stateTools.js +160 -0
  41. package/lib/module/JsModal.js +70 -28
  42. package/lib/module/hooks/index.js +3 -1
  43. package/lib/module/hooks/safe-area-impl.js +1 -1
  44. package/lib/module/hooks/useChangeLog.js +50 -0
  45. package/lib/module/hooks/useEventStoreEvents.js +61 -0
  46. package/lib/module/hooks/usePersistedState.js +44 -10
  47. package/lib/module/index.js +11 -1
  48. package/lib/module/storage/definePersistedSetting.js +63 -0
  49. package/lib/module/storage/devToolsStorageKeys.js +12 -0
  50. package/lib/module/storage/index.js +2 -1
  51. package/lib/module/stores/ChangeLog.js +192 -0
  52. package/lib/module/stores/createToolStore.js +171 -0
  53. package/lib/module/stores/index.js +3 -1
  54. package/lib/module/sync/toolSyncAdapter.js +58 -0
  55. package/lib/module/sync/wireForm.js +159 -0
  56. package/lib/module/tools/defineTool.js +74 -0
  57. package/lib/module/ui/components/ChangeRow.js +105 -0
  58. package/lib/module/ui/components/ChipStrip.js +91 -0
  59. package/lib/module/ui/components/DetailViewToggle.js +125 -0
  60. package/lib/module/ui/components/DevToolsCard.js +3 -1
  61. package/lib/module/ui/components/EmptyState.js +10 -2
  62. package/lib/module/ui/components/EntityFilterView.js +350 -0
  63. package/lib/module/ui/components/ExportPreview.js +169 -0
  64. package/lib/module/ui/components/InfoRow.js +86 -0
  65. package/lib/module/ui/components/ModalHeader.js +40 -7
  66. package/lib/module/ui/components/Pill.js +110 -0
  67. package/lib/module/ui/components/ToggleRow.js +112 -0
  68. package/lib/module/ui/components/ToolEmptyState.js +149 -0
  69. package/lib/module/ui/components/ToolFooter.js +196 -0
  70. package/lib/module/ui/components/WindowControls.js +8 -5
  71. package/lib/module/ui/components/index.js +13 -1
  72. package/lib/module/ui/index.js +7 -1
  73. package/lib/module/ui/listPresets.js +39 -0
  74. package/lib/module/ui/night/primitives.js +3 -1
  75. package/lib/module/ui/otto/OttoSprite.js +496 -0
  76. package/lib/module/ui/otto/index.js +20 -0
  77. package/lib/module/ui/otto/ottoEngine.js +2726 -0
  78. package/lib/module/ui/otto/ottoSheet.js +180 -0
  79. package/lib/module/utils/snapshotProviderKit.js +156 -0
  80. package/lib/module/utils/stateTools.js +154 -0
  81. package/lib/typescript/commonjs/JsModal.d.ts.map +1 -1
  82. package/lib/typescript/commonjs/hooks/index.d.ts +3 -1
  83. package/lib/typescript/commonjs/hooks/index.d.ts.map +1 -1
  84. package/lib/typescript/commonjs/hooks/safe-area-impl.d.ts +1 -1
  85. package/lib/typescript/commonjs/hooks/useChangeLog.d.ts +38 -0
  86. package/lib/typescript/commonjs/hooks/useChangeLog.d.ts.map +1 -0
  87. package/lib/typescript/commonjs/hooks/useEventStoreEvents.d.ts +25 -0
  88. package/lib/typescript/commonjs/hooks/useEventStoreEvents.d.ts.map +1 -0
  89. package/lib/typescript/commonjs/hooks/usePersistedState.d.ts +34 -5
  90. package/lib/typescript/commonjs/hooks/usePersistedState.d.ts.map +1 -1
  91. package/lib/typescript/commonjs/index.d.ts +6 -1
  92. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  93. package/lib/typescript/commonjs/storage/definePersistedSetting.d.ts +39 -0
  94. package/lib/typescript/commonjs/storage/definePersistedSetting.d.ts.map +1 -0
  95. package/lib/typescript/commonjs/storage/devToolsStorageKeys.d.ts +12 -0
  96. package/lib/typescript/commonjs/storage/devToolsStorageKeys.d.ts.map +1 -1
  97. package/lib/typescript/commonjs/storage/index.d.ts +1 -0
  98. package/lib/typescript/commonjs/storage/index.d.ts.map +1 -1
  99. package/lib/typescript/commonjs/stores/ChangeLog.d.ts +100 -0
  100. package/lib/typescript/commonjs/stores/ChangeLog.d.ts.map +1 -0
  101. package/lib/typescript/commonjs/stores/createToolStore.d.ts +92 -0
  102. package/lib/typescript/commonjs/stores/createToolStore.d.ts.map +1 -0
  103. package/lib/typescript/commonjs/stores/index.d.ts +2 -0
  104. package/lib/typescript/commonjs/stores/index.d.ts.map +1 -1
  105. package/lib/typescript/commonjs/sync/toolSyncAdapter.d.ts +66 -0
  106. package/lib/typescript/commonjs/sync/toolSyncAdapter.d.ts.map +1 -0
  107. package/lib/typescript/commonjs/sync/wireForm.d.ts +100 -0
  108. package/lib/typescript/commonjs/sync/wireForm.d.ts.map +1 -0
  109. package/lib/typescript/commonjs/tools/defineTool.d.ts +100 -0
  110. package/lib/typescript/commonjs/tools/defineTool.d.ts.map +1 -0
  111. package/lib/typescript/commonjs/ui/components/ChangeRow.d.ts +52 -0
  112. package/lib/typescript/commonjs/ui/components/ChangeRow.d.ts.map +1 -0
  113. package/lib/typescript/commonjs/ui/components/ChipStrip.d.ts +41 -0
  114. package/lib/typescript/commonjs/ui/components/ChipStrip.d.ts.map +1 -0
  115. package/lib/typescript/commonjs/ui/components/DetailViewToggle.d.ts +36 -0
  116. package/lib/typescript/commonjs/ui/components/DetailViewToggle.d.ts.map +1 -0
  117. package/lib/typescript/commonjs/ui/components/DevToolsCard.d.ts +8 -1
  118. package/lib/typescript/commonjs/ui/components/DevToolsCard.d.ts.map +1 -1
  119. package/lib/typescript/commonjs/ui/components/EmptyState.d.ts +14 -1
  120. package/lib/typescript/commonjs/ui/components/EmptyState.d.ts.map +1 -1
  121. package/lib/typescript/commonjs/ui/components/EntityFilterView.d.ts +51 -0
  122. package/lib/typescript/commonjs/ui/components/EntityFilterView.d.ts.map +1 -0
  123. package/lib/typescript/commonjs/ui/components/ExportPreview.d.ts +48 -0
  124. package/lib/typescript/commonjs/ui/components/ExportPreview.d.ts.map +1 -0
  125. package/lib/typescript/commonjs/ui/components/InfoRow.d.ts +33 -0
  126. package/lib/typescript/commonjs/ui/components/InfoRow.d.ts.map +1 -0
  127. package/lib/typescript/commonjs/ui/components/ModalHeader.d.ts +31 -1
  128. package/lib/typescript/commonjs/ui/components/ModalHeader.d.ts.map +1 -1
  129. package/lib/typescript/commonjs/ui/components/Pill.d.ts +38 -0
  130. package/lib/typescript/commonjs/ui/components/Pill.d.ts.map +1 -0
  131. package/lib/typescript/commonjs/ui/components/ToggleRow.d.ts +33 -0
  132. package/lib/typescript/commonjs/ui/components/ToggleRow.d.ts.map +1 -0
  133. package/lib/typescript/commonjs/ui/components/ToolEmptyState.d.ts +113 -0
  134. package/lib/typescript/commonjs/ui/components/ToolEmptyState.d.ts.map +1 -0
  135. package/lib/typescript/commonjs/ui/components/ToolFooter.d.ts +69 -0
  136. package/lib/typescript/commonjs/ui/components/ToolFooter.d.ts.map +1 -0
  137. package/lib/typescript/commonjs/ui/components/WindowControls.d.ts +7 -1
  138. package/lib/typescript/commonjs/ui/components/WindowControls.d.ts.map +1 -1
  139. package/lib/typescript/commonjs/ui/components/index.d.ts +11 -0
  140. package/lib/typescript/commonjs/ui/components/index.d.ts.map +1 -1
  141. package/lib/typescript/commonjs/ui/index.d.ts +2 -0
  142. package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
  143. package/lib/typescript/commonjs/ui/listPresets.d.ts +28 -0
  144. package/lib/typescript/commonjs/ui/listPresets.d.ts.map +1 -0
  145. package/lib/typescript/commonjs/ui/night/primitives.d.ts +3 -1
  146. package/lib/typescript/commonjs/ui/night/primitives.d.ts.map +1 -1
  147. package/lib/typescript/commonjs/ui/otto/OttoSprite.d.ts +103 -0
  148. package/lib/typescript/commonjs/ui/otto/OttoSprite.d.ts.map +1 -0
  149. package/lib/typescript/commonjs/ui/otto/index.d.ts +19 -0
  150. package/lib/typescript/commonjs/ui/otto/index.d.ts.map +1 -0
  151. package/lib/typescript/commonjs/ui/otto/ottoEngine.d.ts +2205 -0
  152. package/lib/typescript/commonjs/ui/otto/ottoEngine.d.ts.map +1 -0
  153. package/lib/typescript/commonjs/ui/otto/ottoSheet.d.ts +73 -0
  154. package/lib/typescript/commonjs/ui/otto/ottoSheet.d.ts.map +1 -0
  155. package/lib/typescript/commonjs/utils/snapshotProviderKit.d.ts +81 -0
  156. package/lib/typescript/commonjs/utils/snapshotProviderKit.d.ts.map +1 -0
  157. package/lib/typescript/commonjs/utils/stateTools.d.ts +44 -0
  158. package/lib/typescript/commonjs/utils/stateTools.d.ts.map +1 -0
  159. package/lib/typescript/module/JsModal.d.ts.map +1 -1
  160. package/lib/typescript/module/hooks/index.d.ts +3 -1
  161. package/lib/typescript/module/hooks/index.d.ts.map +1 -1
  162. package/lib/typescript/module/hooks/safe-area-impl.d.ts +1 -1
  163. package/lib/typescript/module/hooks/useChangeLog.d.ts +38 -0
  164. package/lib/typescript/module/hooks/useChangeLog.d.ts.map +1 -0
  165. package/lib/typescript/module/hooks/useEventStoreEvents.d.ts +25 -0
  166. package/lib/typescript/module/hooks/useEventStoreEvents.d.ts.map +1 -0
  167. package/lib/typescript/module/hooks/usePersistedState.d.ts +34 -5
  168. package/lib/typescript/module/hooks/usePersistedState.d.ts.map +1 -1
  169. package/lib/typescript/module/index.d.ts +6 -1
  170. package/lib/typescript/module/index.d.ts.map +1 -1
  171. package/lib/typescript/module/storage/definePersistedSetting.d.ts +39 -0
  172. package/lib/typescript/module/storage/definePersistedSetting.d.ts.map +1 -0
  173. package/lib/typescript/module/storage/devToolsStorageKeys.d.ts +12 -0
  174. package/lib/typescript/module/storage/devToolsStorageKeys.d.ts.map +1 -1
  175. package/lib/typescript/module/storage/index.d.ts +1 -0
  176. package/lib/typescript/module/storage/index.d.ts.map +1 -1
  177. package/lib/typescript/module/stores/ChangeLog.d.ts +100 -0
  178. package/lib/typescript/module/stores/ChangeLog.d.ts.map +1 -0
  179. package/lib/typescript/module/stores/createToolStore.d.ts +92 -0
  180. package/lib/typescript/module/stores/createToolStore.d.ts.map +1 -0
  181. package/lib/typescript/module/stores/index.d.ts +2 -0
  182. package/lib/typescript/module/stores/index.d.ts.map +1 -1
  183. package/lib/typescript/module/sync/toolSyncAdapter.d.ts +66 -0
  184. package/lib/typescript/module/sync/toolSyncAdapter.d.ts.map +1 -0
  185. package/lib/typescript/module/sync/wireForm.d.ts +100 -0
  186. package/lib/typescript/module/sync/wireForm.d.ts.map +1 -0
  187. package/lib/typescript/module/tools/defineTool.d.ts +100 -0
  188. package/lib/typescript/module/tools/defineTool.d.ts.map +1 -0
  189. package/lib/typescript/module/ui/components/ChangeRow.d.ts +52 -0
  190. package/lib/typescript/module/ui/components/ChangeRow.d.ts.map +1 -0
  191. package/lib/typescript/module/ui/components/ChipStrip.d.ts +41 -0
  192. package/lib/typescript/module/ui/components/ChipStrip.d.ts.map +1 -0
  193. package/lib/typescript/module/ui/components/DetailViewToggle.d.ts +36 -0
  194. package/lib/typescript/module/ui/components/DetailViewToggle.d.ts.map +1 -0
  195. package/lib/typescript/module/ui/components/DevToolsCard.d.ts +8 -1
  196. package/lib/typescript/module/ui/components/DevToolsCard.d.ts.map +1 -1
  197. package/lib/typescript/module/ui/components/EmptyState.d.ts +14 -1
  198. package/lib/typescript/module/ui/components/EmptyState.d.ts.map +1 -1
  199. package/lib/typescript/module/ui/components/EntityFilterView.d.ts +51 -0
  200. package/lib/typescript/module/ui/components/EntityFilterView.d.ts.map +1 -0
  201. package/lib/typescript/module/ui/components/ExportPreview.d.ts +48 -0
  202. package/lib/typescript/module/ui/components/ExportPreview.d.ts.map +1 -0
  203. package/lib/typescript/module/ui/components/InfoRow.d.ts +33 -0
  204. package/lib/typescript/module/ui/components/InfoRow.d.ts.map +1 -0
  205. package/lib/typescript/module/ui/components/ModalHeader.d.ts +31 -1
  206. package/lib/typescript/module/ui/components/ModalHeader.d.ts.map +1 -1
  207. package/lib/typescript/module/ui/components/Pill.d.ts +38 -0
  208. package/lib/typescript/module/ui/components/Pill.d.ts.map +1 -0
  209. package/lib/typescript/module/ui/components/ToggleRow.d.ts +33 -0
  210. package/lib/typescript/module/ui/components/ToggleRow.d.ts.map +1 -0
  211. package/lib/typescript/module/ui/components/ToolEmptyState.d.ts +113 -0
  212. package/lib/typescript/module/ui/components/ToolEmptyState.d.ts.map +1 -0
  213. package/lib/typescript/module/ui/components/ToolFooter.d.ts +69 -0
  214. package/lib/typescript/module/ui/components/ToolFooter.d.ts.map +1 -0
  215. package/lib/typescript/module/ui/components/WindowControls.d.ts +7 -1
  216. package/lib/typescript/module/ui/components/WindowControls.d.ts.map +1 -1
  217. package/lib/typescript/module/ui/components/index.d.ts +11 -0
  218. package/lib/typescript/module/ui/components/index.d.ts.map +1 -1
  219. package/lib/typescript/module/ui/index.d.ts +2 -0
  220. package/lib/typescript/module/ui/index.d.ts.map +1 -1
  221. package/lib/typescript/module/ui/listPresets.d.ts +28 -0
  222. package/lib/typescript/module/ui/listPresets.d.ts.map +1 -0
  223. package/lib/typescript/module/ui/night/primitives.d.ts +3 -1
  224. package/lib/typescript/module/ui/night/primitives.d.ts.map +1 -1
  225. package/lib/typescript/module/ui/otto/OttoSprite.d.ts +103 -0
  226. package/lib/typescript/module/ui/otto/OttoSprite.d.ts.map +1 -0
  227. package/lib/typescript/module/ui/otto/index.d.ts +19 -0
  228. package/lib/typescript/module/ui/otto/index.d.ts.map +1 -0
  229. package/lib/typescript/module/ui/otto/ottoEngine.d.ts +2205 -0
  230. package/lib/typescript/module/ui/otto/ottoEngine.d.ts.map +1 -0
  231. package/lib/typescript/module/ui/otto/ottoSheet.d.ts +73 -0
  232. package/lib/typescript/module/ui/otto/ottoSheet.d.ts.map +1 -0
  233. package/lib/typescript/module/utils/snapshotProviderKit.d.ts +81 -0
  234. package/lib/typescript/module/utils/snapshotProviderKit.d.ts.map +1 -0
  235. package/lib/typescript/module/utils/stateTools.d.ts +44 -0
  236. package/lib/typescript/module/utils/stateTools.d.ts.map +1 -0
  237. package/package.json +4 -4
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.definePersistedSetting = definePersistedSetting;
7
+ var _persistentStorage = require("../utils/persistentStorage.js");
8
+ /**
9
+ * definePersistedSetting — the `load / save / clear` triple every tool wrote
10
+ * for its module-level settings (copy settings, HUD prefs, JS TOP prefs, …).
11
+ *
12
+ * Contract, the same one all of them followed:
13
+ * - `load()` never throws and never returns a partial object: the stored
14
+ * value is merged OVER the defaults so fields added in a later release
15
+ * get their default instead of `undefined`, then `sanitize` gets the last
16
+ * word (drop values the UI no longer offers, coerce types).
17
+ * - `save()` and `clear()` are best-effort; persistence is optional.
18
+ * - `loadOrNull()` distinguishes "nothing saved yet" from the defaults, for
19
+ * callers that treat first launch differently.
20
+ */
21
+
22
+ function isPlainObject(value) {
23
+ return typeof value === "object" && value !== null && !Array.isArray(value);
24
+ }
25
+ function definePersistedSetting(key, defaults, options = {}) {
26
+ const storage = options.storage ?? _persistentStorage.persistentStorage;
27
+ const parse = options.parse ?? (raw => JSON.parse(raw));
28
+ const serialize = options.serialize ?? (value => JSON.stringify(value));
29
+ function resolve(stored) {
30
+ const merged = isPlainObject(defaults) && isPlainObject(stored) ? {
31
+ ...defaults,
32
+ ...stored
33
+ } : stored;
34
+ return options.sanitize ? options.sanitize(merged, stored) : merged;
35
+ }
36
+ async function loadOrNull() {
37
+ try {
38
+ const raw = await storage.getItem(key);
39
+ if (raw == null || raw === "") return null;
40
+ return resolve(parse(raw));
41
+ } catch {
42
+ return null;
43
+ }
44
+ }
45
+ return {
46
+ key,
47
+ defaults,
48
+ loadOrNull,
49
+ async load() {
50
+ return (await loadOrNull()) ?? defaults;
51
+ },
52
+ async save(value) {
53
+ try {
54
+ await storage.setItem(key, serialize(value));
55
+ } catch {
56
+ // Persistence is best-effort.
57
+ }
58
+ },
59
+ async clear() {
60
+ try {
61
+ await storage.removeItem(key);
62
+ } catch {
63
+ // Persistence is best-effort.
64
+ }
65
+ }
66
+ };
67
+ }
@@ -257,6 +257,18 @@ const devToolsStorageKeys = exports.devToolsStorageKeys = {
257
257
  pausedAt: () => `${devToolsStorageKeys.events.root()}_paused_at`,
258
258
  /** Copy/export settings */
259
259
  copySettings: () => `${devToolsStorageKeys.events.root()}_copy_settings`
260
+ },
261
+ /**
262
+ * Desktop/MCP sync identity
263
+ */
264
+ sync: {
265
+ root: () => `${devToolsStorageKeys.base}_sync`,
266
+ /**
267
+ * The per-install id minted the first time this app dials the broker.
268
+ * It is what makes two devices on the same build register as two
269
+ * devices — see core's deviceIdentity.ts.
270
+ */
271
+ deviceId: () => `${devToolsStorageKeys.sync.root()}_device_id`
260
272
  }
261
273
  };
262
274
 
@@ -3,6 +3,12 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ Object.defineProperty(exports, "definePersistedSetting", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _definePersistedSetting.definePersistedSetting;
10
+ }
11
+ });
6
12
  Object.defineProperty(exports, "devToolsStorageKeys", {
7
13
  enumerable: true,
8
14
  get: function () {
@@ -15,4 +21,5 @@ Object.defineProperty(exports, "isDevToolsStorageKey", {
15
21
  return _devToolsStorageKeys.isDevToolsStorageKey;
16
22
  }
17
23
  });
18
- var _devToolsStorageKeys = require("./devToolsStorageKeys.js");
24
+ var _devToolsStorageKeys = require("./devToolsStorageKeys.js");
25
+ var _definePersistedSetting = require("./definePersistedSetting.js");
@@ -0,0 +1,197 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.ChangeLog = void 0;
7
+ /**
8
+ * ChangeLog — the class-shaped event log the state tools share.
9
+ *
10
+ * redux (`ReduxActionStore`), zustand (`ZustandStateStore`), jotai
11
+ * (`JotaiStateStore`) and react-query (`ReactQueryEventStore`) each carried
12
+ * the same ~120 lines: a newest-first ring buffer, a `Set` of array listeners
13
+ * (`(items) => void`), a `Set` of clear listeners for the desktop's mirror
14
+ * mode, an `enabled` flag (the UI's pause button), a `captureSuppressed` flag
15
+ * (the dashboard must never record its own traffic), a monotonic id counter,
16
+ * `getById`, and hydrate-by-id for lazily fetched detail payloads.
17
+ *
18
+ * This is that core. Tool stores `extend` it and keep their public method
19
+ * names (`addStateChange`, `getActions`, `clearAtomChanges`, …) as thin
20
+ * wrappers, so no call site — including Buoy Desktop — has to change.
21
+ *
22
+ * Semantics preserved from the originals, deliberately:
23
+ * - `getAll()` returns a COPY (`[...items]`) — consumers seed React state
24
+ * from it and rely on a fresh identity per notification.
25
+ * - `clear()` always notifies, even when already empty, then fires the clear
26
+ * listeners (the dashboard forwards the clear to the device either way).
27
+ * - `replaceAll()` respects `enabled`: the pause button freezes a mirror.
28
+ * - `disableCapture()` is permanent for the store's lifetime.
29
+ */
30
+
31
+ class ChangeLog {
32
+ /** Newest first. Subclasses may read; mutate only through the helpers. */
33
+ items = [];
34
+ itemListeners = new Set();
35
+ clearListeners = new Set();
36
+ idCounter = 0;
37
+ enabled = true;
38
+ captureSuppressed = false;
39
+ constructor(options = {}) {
40
+ this.max = options.max ?? 200;
41
+ }
42
+
43
+ // ---- Recording ----
44
+
45
+ /** True when a new item should be recorded (not paused, not a mirror). */
46
+ canRecord() {
47
+ return this.enabled && !this.captureSuppressed;
48
+ }
49
+
50
+ /** `${Date.now()}-${n}` — unique per store instance, sorts by time. */
51
+ nextId() {
52
+ return `${Date.now()}-${++this.idCounter}`;
53
+ }
54
+
55
+ /**
56
+ * Prepend one item and trim to `max`. Does NOT notify — subclasses often
57
+ * touch companion state (per-entity counters, raw-state eviction) before
58
+ * they notify. Call `notifyListeners()` when done, or use `record()`.
59
+ */
60
+ push(item) {
61
+ this.items = [item, ...this.items].slice(0, this.max);
62
+ }
63
+
64
+ /** `push()` + notify. The common case. */
65
+ record(item) {
66
+ this.push(item);
67
+ this.notifyListeners();
68
+ }
69
+
70
+ /** Replace the buffer wholesale (already merged/hydrated by the caller) and notify. */
71
+ setItems(next) {
72
+ this.items = next.slice(0, this.max);
73
+ this.notifyListeners();
74
+ }
75
+
76
+ // ---- Reading ----
77
+
78
+ /** All items, newest first. Fresh array each call. */
79
+ getAll() {
80
+ return [...this.items];
81
+ }
82
+ getById(id) {
83
+ return this.items.find(item => item.id === id);
84
+ }
85
+ get size() {
86
+ return this.items.length;
87
+ }
88
+
89
+ // ---- Clearing ----
90
+
91
+ /**
92
+ * Empty the buffer, notify, then fire clear listeners. Always notifies —
93
+ * a clear is a user action and the list must repaint even if nothing was
94
+ * in it. Subclasses that keep companion counters reset them first.
95
+ */
96
+ clear() {
97
+ this.items = [];
98
+ this.notifyListeners();
99
+ this.clearListeners.forEach(listener => {
100
+ try {
101
+ listener();
102
+ } catch {
103
+ // Ignore listener errors
104
+ }
105
+ });
106
+ }
107
+
108
+ /**
109
+ * Listen for `clear()` calls. Used in remote mirror mode to forward a
110
+ * clear performed in the dashboard UI to the synced device.
111
+ */
112
+ onClear(listener) {
113
+ this.clearListeners.add(listener);
114
+ return () => {
115
+ this.clearListeners.delete(listener);
116
+ };
117
+ }
118
+
119
+ // ---- Remote mirror mode ----
120
+
121
+ /**
122
+ * Permanently suppress local capture. Use when this store acts as a mirror
123
+ * of a remote device (the desktop dashboard): data arrives via
124
+ * `replaceAll()` / a subclass snapshot method and local instrumentation
125
+ * must never record.
126
+ */
127
+ disableCapture() {
128
+ this.captureSuppressed = true;
129
+ }
130
+
131
+ /** Whether the store is in remote mirror mode (capture suppressed). */
132
+ isCaptureSuppressed() {
133
+ return this.captureSuppressed;
134
+ }
135
+
136
+ /**
137
+ * Replace the whole list from a remote snapshot and notify. Respects the
138
+ * enabled flag so the UI's pause button freezes the mirror. Returns false
139
+ * when skipped. Subclasses with hydration-preserving merges compute the
140
+ * merged list themselves and call `setItems()`.
141
+ */
142
+ replaceAll(next) {
143
+ if (!this.enabled) return false;
144
+ this.setItems(next);
145
+ return true;
146
+ }
147
+
148
+ /**
149
+ * Patch one item in place (by id) and notify. Used to hydrate a mirrored
150
+ * item with detail payloads fetched on demand from the device.
151
+ */
152
+ hydrateById(id, patch) {
153
+ const index = this.items.findIndex(item => item.id === id);
154
+ if (index === -1) return;
155
+ this.items = [...this.items.slice(0, index), {
156
+ ...this.items[index],
157
+ ...patch
158
+ }, ...this.items.slice(index + 1)];
159
+ this.notifyListeners();
160
+ }
161
+
162
+ // ---- Enable / disable ----
163
+
164
+ setEnabled(enabled) {
165
+ this.enabled = enabled;
166
+ }
167
+ getEnabled() {
168
+ return this.enabled;
169
+ }
170
+ setMax(max) {
171
+ this.max = max;
172
+ if (this.items.length > max) {
173
+ this.items = this.items.slice(0, max);
174
+ this.notifyListeners();
175
+ }
176
+ }
177
+ getMax() {
178
+ return this.max;
179
+ }
180
+
181
+ // ---- Subscriptions ----
182
+
183
+ subscribe(listener) {
184
+ this.itemListeners.add(listener);
185
+ return () => {
186
+ this.itemListeners.delete(listener);
187
+ };
188
+ }
189
+ notifyListeners() {
190
+ const items = this.getAll();
191
+ this.itemListeners.forEach(listener => listener(items));
192
+ }
193
+ get listenerCount() {
194
+ return this.itemListeners.size;
195
+ }
196
+ }
197
+ exports.ChangeLog = ChangeLog;
@@ -0,0 +1,175 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.createToolStore = createToolStore;
7
+ /**
8
+ * createToolStore — the one bounded, subscribable buffer every tool used to
9
+ * hand-roll.
10
+ *
11
+ * Before this existed, console, sentry, images, assets, focus-inspector,
12
+ * tv-remote, redux, zustand, jotai and react-query each carried their own copy
13
+ * of the same ~100 lines: a `Set` of listeners, a `Set` of clear listeners
14
+ * (so the desktop dashboard can forward a clear to the device in mirror
15
+ * mode), a ring buffer with a max, an optional frame-coalesced emit, and the
16
+ * `__buoyFlushToolSync` escape hatch that pushes a tool's snapshot to the
17
+ * dashboard NOW (a crash's last words must not wait on the sync throttle).
18
+ *
19
+ * This is a plain function, not a class, on purpose: every consumer exposes a
20
+ * module-level functional API (`subscribe`, `getSnapshot`, `clearX`) and
21
+ * feeds `useSyncExternalStore`, so the store is a value the module wraps
22
+ * rather than something it extends. `BaseEventStore` stays for the
23
+ * class-based, auto-start/stop capture stores (network, storage, routes).
24
+ *
25
+ * Contract:
26
+ * - `getSnapshot()` returns the SAME array reference until the next
27
+ * mutation, so `useSyncExternalStore` never re-renders for nothing.
28
+ * - `order: "append"` keeps the newest item LAST (console-style scrollback);
29
+ * `order: "prepend"` keeps it FIRST (event-log style). Trimming drops from
30
+ * the opposite end. Consumers never have to `.reverse()`.
31
+ * - `clear()` fires `onClear` listeners even when the buffer is already
32
+ * empty — the dashboard's forwarded clear must reach the device regardless.
33
+ * - `set()` (mirror mode) never fires clear listeners; it is a replacement.
34
+ */
35
+
36
+ /** Listener signature: change-only, read the snapshot yourself. */
37
+
38
+ function scheduleFrame(cb) {
39
+ if (typeof requestAnimationFrame === "function") {
40
+ requestAnimationFrame(() => cb());
41
+ } else {
42
+ setTimeout(cb, 0);
43
+ }
44
+ }
45
+ function createToolStore(options) {
46
+ const id = options.id;
47
+ const order = options.order ?? "append";
48
+ const coalesce = options.coalesce ?? "sync";
49
+ const flushMinIntervalMs = options.flushMinIntervalMs ?? 150;
50
+ let max = options.max;
51
+ let items = [];
52
+ let paused = false;
53
+ let nextId = options.firstId ?? 1;
54
+ let lastFlushAt = 0;
55
+ let emitScheduled = false;
56
+ const listeners = new Set();
57
+ const clearListeners = new Set();
58
+ function fire() {
59
+ for (const listener of listeners) {
60
+ try {
61
+ listener();
62
+ } catch {
63
+ // A broken subscriber must not take the store (or a crash path) down.
64
+ }
65
+ }
66
+ }
67
+ function emit() {
68
+ if (coalesce === "sync") {
69
+ fire();
70
+ return;
71
+ }
72
+ if (emitScheduled) return;
73
+ emitScheduled = true;
74
+ scheduleFrame(() => {
75
+ emitScheduled = false;
76
+ fire();
77
+ });
78
+ }
79
+ function trim(list) {
80
+ if (list.length <= max) return list;
81
+ return order === "append" ? list.slice(list.length - max) : list.slice(0, max);
82
+ }
83
+ const store = {
84
+ id,
85
+ subscribe(listener) {
86
+ listeners.add(listener);
87
+ return () => {
88
+ listeners.delete(listener);
89
+ };
90
+ },
91
+ getSnapshot() {
92
+ return items;
93
+ },
94
+ size() {
95
+ return items.length;
96
+ },
97
+ add(item) {
98
+ if (paused) return;
99
+ if (order === "append") {
100
+ items = items.length >= max ? [...items.slice(items.length - max + 1), item] : [...items, item];
101
+ } else {
102
+ items = items.length >= max ? [item, ...items.slice(0, max - 1)] : [item, ...items];
103
+ }
104
+ emit();
105
+ },
106
+ set(next) {
107
+ items = trim(next);
108
+ emit();
109
+ },
110
+ update(fn) {
111
+ items = trim(fn(items));
112
+ emit();
113
+ },
114
+ clear() {
115
+ for (const listener of clearListeners) {
116
+ try {
117
+ listener();
118
+ } catch {
119
+ // ignore
120
+ }
121
+ }
122
+ if (items.length === 0) return;
123
+ items = [];
124
+ emit();
125
+ },
126
+ onClear(listener) {
127
+ clearListeners.add(listener);
128
+ return () => {
129
+ clearListeners.delete(listener);
130
+ };
131
+ },
132
+ notify() {
133
+ emit();
134
+ },
135
+ flushSync(force = false) {
136
+ const now = Date.now();
137
+ if (!force && now - lastFlushAt < flushMinIntervalMs) return;
138
+ lastFlushAt = now;
139
+ try {
140
+ const set = globalThis.__buoyFlushToolSync;
141
+ if (!set) return;
142
+ for (const flush of set) flush(id);
143
+ } catch {
144
+ // Best-effort: a crash path must never crash.
145
+ }
146
+ },
147
+ setPaused(next) {
148
+ paused = next;
149
+ },
150
+ isPaused() {
151
+ return paused;
152
+ },
153
+ setMax(next) {
154
+ max = next;
155
+ const trimmed = trim(items);
156
+ if (trimmed !== items) {
157
+ items = trimmed;
158
+ emit();
159
+ }
160
+ },
161
+ getMax() {
162
+ return max;
163
+ },
164
+ allocId() {
165
+ return nextId++;
166
+ },
167
+ bumpIdAbove(idFloor) {
168
+ if (idFloor + 1 > nextId) nextId = idFloor + 1;
169
+ },
170
+ listenerCount() {
171
+ return listeners.size;
172
+ }
173
+ };
174
+ return store;
175
+ }
@@ -9,6 +9,18 @@ Object.defineProperty(exports, "BaseEventStore", {
9
9
  return _BaseEventStore.BaseEventStore;
10
10
  }
11
11
  });
12
+ Object.defineProperty(exports, "ChangeLog", {
13
+ enumerable: true,
14
+ get: function () {
15
+ return _ChangeLog.ChangeLog;
16
+ }
17
+ });
18
+ Object.defineProperty(exports, "createToolStore", {
19
+ enumerable: true,
20
+ get: function () {
21
+ return _createToolStore.createToolStore;
22
+ }
23
+ });
12
24
  Object.defineProperty(exports, "ignoredPatternsStore", {
13
25
  enumerable: true,
14
26
  get: function () {
@@ -34,4 +46,6 @@ Object.defineProperty(exports, "useIgnoredPatterns", {
34
46
  }
35
47
  });
36
48
  var _BaseEventStore = require("./BaseEventStore.js");
37
- var _ignoredPatternsStore = require("./ignoredPatternsStore");
49
+ var _ignoredPatternsStore = require("./ignoredPatternsStore");
50
+ var _createToolStore = require("./createToolStore.js");
51
+ var _ChangeLog = require("./ChangeLog.js");
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.createSyncAdapter = createSyncAdapter;
7
+ exports.mergeChangeSources = mergeChangeSources;
8
+ /**
9
+ * ToolSyncAdapter — the contract a tool implements to sync to Buoy Desktop /
10
+ * the MCP server, plus `createSyncAdapter`, the one way to build one.
11
+ *
12
+ * Every tool package used to declare its adapter as a bare object literal
13
+ * with the comment "structurally matches external-sync's ToolSyncAdapter so
14
+ * this package doesn't need a dependency on it". That kept the packages
15
+ * decoupled but left the contract unchecked: storage shipped `version: 3`
16
+ * while every sibling was on 2 and nothing noticed. The type now lives here,
17
+ * in shared (which every tool already depends on), and external-sync
18
+ * re-exports it — one definition, checked everywhere.
19
+ *
20
+ * `createSyncAdapter` is deliberately thin. It:
21
+ * - type-checks the literal against the contract (a wrong `subscribe`
22
+ * signature or a non-function action fails at compile time),
23
+ * - keeps the CONCRETE `actions` type, so `adapter.actions.getChangeDetail()`
24
+ * stays typed for tests and callers,
25
+ * - merges several change sources into one `subscribe` (a store plus its
26
+ * registry, envelopes plus drops) — the second most-copied block.
27
+ */
28
+
29
+ /** A remote-invocable action. Thrown errors are reported back to the dashboard. */
30
+
31
+ /** Anything that can tell the bridge "something changed". */
32
+
33
+ /**
34
+ * The contract each dev tool implements to become remotely syncable.
35
+ * Works with BaseEventStore, ChangeLog, createToolStore, or any pattern
36
+ * exposing subscribe + getSnapshot.
37
+ *
38
+ * Payloads (snapshots and action results) must be JSON-serializable —
39
+ * no functions, circular references, or class instances.
40
+ */
41
+
42
+ /** Subscribe to every source; the returned function unsubscribes from all. */
43
+ function mergeChangeSources(sources) {
44
+ if (sources.length === 1) return sources[0];
45
+ return onChange => {
46
+ const offs = sources.map(source => source(onChange));
47
+ return () => {
48
+ for (const off of offs) off();
49
+ };
50
+ };
51
+ }
52
+ function createSyncAdapter(definition) {
53
+ const subscribe = Array.isArray(definition.subscribe) ? mergeChangeSources(definition.subscribe) : definition.subscribe;
54
+ const adapter = {
55
+ version: definition.version,
56
+ getSnapshot: definition.getSnapshot,
57
+ subscribe,
58
+ ...(definition.actions ? {
59
+ actions: definition.actions
60
+ } : {})
61
+ };
62
+ return adapter;
63
+ }