@buoy-gg/shared-ui 7.0.34 → 7.0.36

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 (207) hide show
  1. package/lib/commonjs/dataViewer/CyberpunkInput.js +3 -2
  2. package/lib/commonjs/dataViewer/DataTreeActionBar.js +3 -2
  3. package/lib/commonjs/dataViewer/DataTreeActionDock.js +2 -1
  4. package/lib/commonjs/dataViewer/DiffSummary.js +4 -3
  5. package/lib/commonjs/dataViewer/LiveExplorer.js +11 -10
  6. package/lib/commonjs/dataViewer/SplitDiffViewer.js +10 -9
  7. package/lib/commonjs/dataViewer/ThemedSplitView.js +12 -11
  8. package/lib/commonjs/dataViewer/ValueEditModal.js +6 -5
  9. package/lib/commonjs/dataViewer/VirtualizedDataExplorer.js +4 -3
  10. package/lib/commonjs/dataViewer/tree/TreeDiffViewer.js +15 -14
  11. package/lib/commonjs/hooks/safe-area-impl.js +1 -1
  12. package/lib/commonjs/icons/index.js +540 -1
  13. package/lib/commonjs/index.js +48 -0
  14. package/lib/commonjs/license/FreeAccountPrompt.js +2 -1
  15. package/lib/commonjs/license/LicenseEntryModal.js +2 -1
  16. package/lib/commonjs/sync/keyedArrays.js +209 -0
  17. package/lib/commonjs/sync/toolAttention.test.js +51 -0
  18. package/lib/commonjs/sync/toolUiBridge.js +58 -0
  19. package/lib/commonjs/sync/typedEdit.js +228 -0
  20. package/lib/commonjs/sync/valuePath.js +337 -0
  21. package/lib/commonjs/sync/valueShape.js +161 -0
  22. package/lib/commonjs/ui/components/ChangeRow.js +2 -1
  23. package/lib/commonjs/ui/components/ClipboardHintBanner.js +2 -1
  24. package/lib/commonjs/ui/components/CompactRow.js +3 -2
  25. package/lib/commonjs/ui/components/DataInspector.js +3 -2
  26. package/lib/commonjs/ui/components/DetailView.js +2 -1
  27. package/lib/commonjs/ui/components/DynamicFilterView.js +8 -7
  28. package/lib/commonjs/ui/components/EntityFilterView.js +9 -8
  29. package/lib/commonjs/ui/components/EventHistoryViewer/CompareBar.js +5 -4
  30. package/lib/commonjs/ui/components/EventHistoryViewer/DiffModeTabs.js +2 -1
  31. package/lib/commonjs/ui/components/EventHistoryViewer/EventPickerModal.js +5 -4
  32. package/lib/commonjs/ui/components/EventStepperFooter.js +4 -3
  33. package/lib/commonjs/ui/components/ExpandablePopover.js +48 -8
  34. package/lib/commonjs/ui/components/ExpandedInfoRow.js +3 -2
  35. package/lib/commonjs/ui/components/FilterComponents.js +2 -1
  36. package/lib/commonjs/ui/components/InfoRow.js +2 -1
  37. package/lib/commonjs/ui/components/Pill.js +2 -1
  38. package/lib/commonjs/ui/components/ToolFooter.js +3 -2
  39. package/lib/commonjs/ui/console/CyberpunkSectionButton.js +3 -2
  40. package/lib/commonjs/ui/fonts.js +12 -0
  41. package/lib/commonjs/ui/gameUI/components/GameUICollapsibleSection.js +4 -3
  42. package/lib/commonjs/ui/gameUI/components/GameUICompactStats.js +12 -11
  43. package/lib/commonjs/ui/gameUI/components/GameUIIssuesList.js +9 -8
  44. package/lib/commonjs/ui/gameUI/components/GameUIStatusHeader.js +4 -3
  45. package/lib/commonjs/ui/index.js +8 -0
  46. package/lib/commonjs/ui/toolTheme.js +4 -3
  47. package/lib/module/dataViewer/CyberpunkInput.js +3 -2
  48. package/lib/module/dataViewer/DataTreeActionBar.js +3 -2
  49. package/lib/module/dataViewer/DataTreeActionDock.js +2 -1
  50. package/lib/module/dataViewer/DiffSummary.js +4 -3
  51. package/lib/module/dataViewer/LiveExplorer.js +11 -11
  52. package/lib/module/dataViewer/SplitDiffViewer.js +10 -9
  53. package/lib/module/dataViewer/ThemedSplitView.js +12 -11
  54. package/lib/module/dataViewer/ValueEditModal.js +6 -5
  55. package/lib/module/dataViewer/VirtualizedDataExplorer.js +5 -4
  56. package/lib/module/dataViewer/tree/TreeDiffViewer.js +15 -15
  57. package/lib/module/hooks/safe-area-impl.js +1 -1
  58. package/lib/module/icons/index.js +29 -2
  59. package/lib/module/index.js +4 -0
  60. package/lib/module/license/FreeAccountPrompt.js +2 -2
  61. package/lib/module/license/LicenseEntryModal.js +3 -3
  62. package/lib/module/sync/keyedArrays.js +204 -0
  63. package/lib/module/sync/toolAttention.test.js +50 -0
  64. package/lib/module/sync/toolUiBridge.js +55 -0
  65. package/lib/module/sync/typedEdit.js +220 -0
  66. package/lib/module/sync/valuePath.js +330 -0
  67. package/lib/module/sync/valueShape.js +153 -0
  68. package/lib/module/ui/components/ChangeRow.js +2 -1
  69. package/lib/module/ui/components/ClipboardHintBanner.js +2 -1
  70. package/lib/module/ui/components/CompactRow.js +3 -2
  71. package/lib/module/ui/components/DataInspector.js +3 -2
  72. package/lib/module/ui/components/DetailView.js +2 -1
  73. package/lib/module/ui/components/DynamicFilterView.js +8 -7
  74. package/lib/module/ui/components/EntityFilterView.js +9 -8
  75. package/lib/module/ui/components/EventHistoryViewer/CompareBar.js +6 -5
  76. package/lib/module/ui/components/EventHistoryViewer/DiffModeTabs.js +3 -2
  77. package/lib/module/ui/components/EventHistoryViewer/EventPickerModal.js +6 -5
  78. package/lib/module/ui/components/EventStepperFooter.js +4 -3
  79. package/lib/module/ui/components/ExpandablePopover.js +48 -8
  80. package/lib/module/ui/components/ExpandedInfoRow.js +3 -2
  81. package/lib/module/ui/components/FilterComponents.js +2 -2
  82. package/lib/module/ui/components/InfoRow.js +2 -1
  83. package/lib/module/ui/components/Pill.js +2 -1
  84. package/lib/module/ui/components/ToolFooter.js +3 -2
  85. package/lib/module/ui/console/CyberpunkSectionButton.js +3 -2
  86. package/lib/module/ui/fonts.js +8 -0
  87. package/lib/module/ui/gameUI/components/GameUICollapsibleSection.js +4 -3
  88. package/lib/module/ui/gameUI/components/GameUICompactStats.js +12 -11
  89. package/lib/module/ui/gameUI/components/GameUIIssuesList.js +9 -8
  90. package/lib/module/ui/gameUI/components/GameUIStatusHeader.js +4 -3
  91. package/lib/module/ui/index.js +3 -0
  92. package/lib/module/ui/toolTheme.js +4 -4
  93. package/lib/typescript/commonjs/dataViewer/CyberpunkInput.d.ts.map +1 -1
  94. package/lib/typescript/commonjs/dataViewer/DataTreeActionBar.d.ts.map +1 -1
  95. package/lib/typescript/commonjs/dataViewer/DataTreeActionDock.d.ts.map +1 -1
  96. package/lib/typescript/commonjs/dataViewer/DiffSummary.d.ts.map +1 -1
  97. package/lib/typescript/commonjs/dataViewer/LiveExplorer.d.ts.map +1 -1
  98. package/lib/typescript/commonjs/dataViewer/SplitDiffViewer.d.ts.map +1 -1
  99. package/lib/typescript/commonjs/dataViewer/ThemedSplitView.d.ts.map +1 -1
  100. package/lib/typescript/commonjs/dataViewer/ValueEditModal.d.ts.map +1 -1
  101. package/lib/typescript/commonjs/dataViewer/VirtualizedDataExplorer.d.ts.map +1 -1
  102. package/lib/typescript/commonjs/dataViewer/tree/TreeDiffViewer.d.ts.map +1 -1
  103. package/lib/typescript/commonjs/hooks/safe-area-impl.d.ts +1 -1
  104. package/lib/typescript/commonjs/icons/index.d.ts +4 -1
  105. package/lib/typescript/commonjs/icons/index.d.ts.map +1 -1
  106. package/lib/typescript/commonjs/index.d.ts +4 -0
  107. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  108. package/lib/typescript/commonjs/license/FreeAccountPrompt.d.ts.map +1 -1
  109. package/lib/typescript/commonjs/license/LicenseEntryModal.d.ts.map +1 -1
  110. package/lib/typescript/commonjs/sync/keyedArrays.d.ts +35 -0
  111. package/lib/typescript/commonjs/sync/keyedArrays.d.ts.map +1 -0
  112. package/lib/typescript/commonjs/sync/toolAttention.test.d.ts +2 -0
  113. package/lib/typescript/commonjs/sync/toolAttention.test.d.ts.map +1 -0
  114. package/lib/typescript/commonjs/sync/toolUiBridge.d.ts +25 -0
  115. package/lib/typescript/commonjs/sync/toolUiBridge.d.ts.map +1 -1
  116. package/lib/typescript/commonjs/sync/typedEdit.d.ts +70 -0
  117. package/lib/typescript/commonjs/sync/typedEdit.d.ts.map +1 -0
  118. package/lib/typescript/commonjs/sync/valuePath.d.ts +52 -0
  119. package/lib/typescript/commonjs/sync/valuePath.d.ts.map +1 -0
  120. package/lib/typescript/commonjs/sync/valueShape.d.ts +77 -0
  121. package/lib/typescript/commonjs/sync/valueShape.d.ts.map +1 -0
  122. package/lib/typescript/commonjs/ui/components/ChangeRow.d.ts.map +1 -1
  123. package/lib/typescript/commonjs/ui/components/ClipboardHintBanner.d.ts.map +1 -1
  124. package/lib/typescript/commonjs/ui/components/CompactRow.d.ts.map +1 -1
  125. package/lib/typescript/commonjs/ui/components/DataInspector.d.ts.map +1 -1
  126. package/lib/typescript/commonjs/ui/components/DetailView.d.ts.map +1 -1
  127. package/lib/typescript/commonjs/ui/components/DynamicFilterView.d.ts.map +1 -1
  128. package/lib/typescript/commonjs/ui/components/EntityFilterView.d.ts.map +1 -1
  129. package/lib/typescript/commonjs/ui/components/EventHistoryViewer/CompareBar.d.ts.map +1 -1
  130. package/lib/typescript/commonjs/ui/components/EventHistoryViewer/DiffModeTabs.d.ts.map +1 -1
  131. package/lib/typescript/commonjs/ui/components/EventHistoryViewer/EventPickerModal.d.ts.map +1 -1
  132. package/lib/typescript/commonjs/ui/components/EventStepperFooter.d.ts.map +1 -1
  133. package/lib/typescript/commonjs/ui/components/ExpandablePopover.d.ts +18 -1
  134. package/lib/typescript/commonjs/ui/components/ExpandablePopover.d.ts.map +1 -1
  135. package/lib/typescript/commonjs/ui/components/ExpandedInfoRow.d.ts.map +1 -1
  136. package/lib/typescript/commonjs/ui/components/FilterComponents.d.ts.map +1 -1
  137. package/lib/typescript/commonjs/ui/components/InfoRow.d.ts.map +1 -1
  138. package/lib/typescript/commonjs/ui/components/Pill.d.ts.map +1 -1
  139. package/lib/typescript/commonjs/ui/components/ToolFooter.d.ts.map +1 -1
  140. package/lib/typescript/commonjs/ui/console/CyberpunkSectionButton.d.ts.map +1 -1
  141. package/lib/typescript/commonjs/ui/fonts.d.ts +7 -0
  142. package/lib/typescript/commonjs/ui/fonts.d.ts.map +1 -0
  143. package/lib/typescript/commonjs/ui/gameUI/components/GameUICollapsibleSection.d.ts.map +1 -1
  144. package/lib/typescript/commonjs/ui/gameUI/components/GameUICompactStats.d.ts.map +1 -1
  145. package/lib/typescript/commonjs/ui/gameUI/components/GameUIIssuesList.d.ts.map +1 -1
  146. package/lib/typescript/commonjs/ui/gameUI/components/GameUIStatusHeader.d.ts.map +1 -1
  147. package/lib/typescript/commonjs/ui/index.d.ts +1 -0
  148. package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
  149. package/lib/typescript/commonjs/ui/toolTheme.d.ts.map +1 -1
  150. package/lib/typescript/module/dataViewer/CyberpunkInput.d.ts.map +1 -1
  151. package/lib/typescript/module/dataViewer/DataTreeActionBar.d.ts.map +1 -1
  152. package/lib/typescript/module/dataViewer/DataTreeActionDock.d.ts.map +1 -1
  153. package/lib/typescript/module/dataViewer/DiffSummary.d.ts.map +1 -1
  154. package/lib/typescript/module/dataViewer/LiveExplorer.d.ts.map +1 -1
  155. package/lib/typescript/module/dataViewer/SplitDiffViewer.d.ts.map +1 -1
  156. package/lib/typescript/module/dataViewer/ThemedSplitView.d.ts.map +1 -1
  157. package/lib/typescript/module/dataViewer/ValueEditModal.d.ts.map +1 -1
  158. package/lib/typescript/module/dataViewer/VirtualizedDataExplorer.d.ts.map +1 -1
  159. package/lib/typescript/module/dataViewer/tree/TreeDiffViewer.d.ts.map +1 -1
  160. package/lib/typescript/module/hooks/safe-area-impl.d.ts +1 -1
  161. package/lib/typescript/module/icons/index.d.ts +4 -1
  162. package/lib/typescript/module/icons/index.d.ts.map +1 -1
  163. package/lib/typescript/module/index.d.ts +4 -0
  164. package/lib/typescript/module/index.d.ts.map +1 -1
  165. package/lib/typescript/module/license/FreeAccountPrompt.d.ts.map +1 -1
  166. package/lib/typescript/module/license/LicenseEntryModal.d.ts.map +1 -1
  167. package/lib/typescript/module/sync/keyedArrays.d.ts +35 -0
  168. package/lib/typescript/module/sync/keyedArrays.d.ts.map +1 -0
  169. package/lib/typescript/module/sync/toolAttention.test.d.ts +2 -0
  170. package/lib/typescript/module/sync/toolAttention.test.d.ts.map +1 -0
  171. package/lib/typescript/module/sync/toolUiBridge.d.ts +25 -0
  172. package/lib/typescript/module/sync/toolUiBridge.d.ts.map +1 -1
  173. package/lib/typescript/module/sync/typedEdit.d.ts +70 -0
  174. package/lib/typescript/module/sync/typedEdit.d.ts.map +1 -0
  175. package/lib/typescript/module/sync/valuePath.d.ts +52 -0
  176. package/lib/typescript/module/sync/valuePath.d.ts.map +1 -0
  177. package/lib/typescript/module/sync/valueShape.d.ts +77 -0
  178. package/lib/typescript/module/sync/valueShape.d.ts.map +1 -0
  179. package/lib/typescript/module/ui/components/ChangeRow.d.ts.map +1 -1
  180. package/lib/typescript/module/ui/components/ClipboardHintBanner.d.ts.map +1 -1
  181. package/lib/typescript/module/ui/components/CompactRow.d.ts.map +1 -1
  182. package/lib/typescript/module/ui/components/DataInspector.d.ts.map +1 -1
  183. package/lib/typescript/module/ui/components/DetailView.d.ts.map +1 -1
  184. package/lib/typescript/module/ui/components/DynamicFilterView.d.ts.map +1 -1
  185. package/lib/typescript/module/ui/components/EntityFilterView.d.ts.map +1 -1
  186. package/lib/typescript/module/ui/components/EventHistoryViewer/CompareBar.d.ts.map +1 -1
  187. package/lib/typescript/module/ui/components/EventHistoryViewer/DiffModeTabs.d.ts.map +1 -1
  188. package/lib/typescript/module/ui/components/EventHistoryViewer/EventPickerModal.d.ts.map +1 -1
  189. package/lib/typescript/module/ui/components/EventStepperFooter.d.ts.map +1 -1
  190. package/lib/typescript/module/ui/components/ExpandablePopover.d.ts +18 -1
  191. package/lib/typescript/module/ui/components/ExpandablePopover.d.ts.map +1 -1
  192. package/lib/typescript/module/ui/components/ExpandedInfoRow.d.ts.map +1 -1
  193. package/lib/typescript/module/ui/components/FilterComponents.d.ts.map +1 -1
  194. package/lib/typescript/module/ui/components/InfoRow.d.ts.map +1 -1
  195. package/lib/typescript/module/ui/components/Pill.d.ts.map +1 -1
  196. package/lib/typescript/module/ui/components/ToolFooter.d.ts.map +1 -1
  197. package/lib/typescript/module/ui/console/CyberpunkSectionButton.d.ts.map +1 -1
  198. package/lib/typescript/module/ui/fonts.d.ts +7 -0
  199. package/lib/typescript/module/ui/fonts.d.ts.map +1 -0
  200. package/lib/typescript/module/ui/gameUI/components/GameUICollapsibleSection.d.ts.map +1 -1
  201. package/lib/typescript/module/ui/gameUI/components/GameUICompactStats.d.ts.map +1 -1
  202. package/lib/typescript/module/ui/gameUI/components/GameUIIssuesList.d.ts.map +1 -1
  203. package/lib/typescript/module/ui/gameUI/components/GameUIStatusHeader.d.ts.map +1 -1
  204. package/lib/typescript/module/ui/index.d.ts +1 -0
  205. package/lib/typescript/module/ui/index.d.ts.map +1 -1
  206. package/lib/typescript/module/ui/toolTheme.d.ts.map +1 -1
  207. package/package.json +4 -4
@@ -0,0 +1,153 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * A compact type sketch of a runtime value — one implementation, shared by every
5
+ * adapter that hands data to a model.
6
+ *
7
+ * WHY. A model cannot write a well-formed item into a list it has never seen the
8
+ * shape of, and we were not showing it. Measured on `cart/bag-still-zustand`: the
9
+ * rendered prompt is 83,000 characters and the words `lineId`, `itemId`,
10
+ * `sizeId`, `addonIds` and `unitPrice` appear in it ZERO times. Every model
11
+ * "failing" that case invented a line shape because inventing was the only move
12
+ * available. The zustand typed-edit guard would have caught the invention — but
13
+ * only on a list that already holds an item, since it builds its template from
14
+ * `list[0]`. On an EMPTY list there is no template, the guard has nothing to
15
+ * compare against, and the invented shape is written silently.
16
+ *
17
+ * So the sketch has two jobs, and the second is the one that pays:
18
+ *
19
+ * 1. Say what the items in a list look like, so an addition can match.
20
+ * 2. Say when NOTHING knows — an empty list's item shape is not merely
21
+ * unread, it is unknowable from runtime state, and the honest answer is to
22
+ * admit that rather than let a guess through unremarked.
23
+ *
24
+ * It is deliberately tiny. A sketch of a 120-line bag is the same size as a
25
+ * sketch of a 2-line one, so unlike the data it survives the engine's 24,000-
26
+ * character result cap — which is the whole point, because the cases where the
27
+ * model most needs the shape are exactly the ones where the data got cut off.
28
+ */
29
+
30
+ const isPlainObject = v => typeof v === "object" && v !== null && !Array.isArray(v);
31
+
32
+ /** Beyond this the sketch stops being cheaper to read than the data. */
33
+ const MAX_DEPTH = 3;
34
+ const MAX_KEYS = 24;
35
+ /**
36
+ * Hard caps on `listShapes`. MAX_DEPTH bounds how DEEP it goes; nothing bounded
37
+ * how WIDE it went, and it walks every entry of every object at each permitted
38
+ * level. A store holding a few hundred keyed records therefore produced a
39
+ * "compact" sketch larger than the data it was standing in for — in a value
40
+ * that rides in every prompt. Depth is not a size budget.
41
+ */
42
+ const MAX_LIST_PATHS = 40;
43
+ const MAX_ENTRIES_VISITED = 600;
44
+
45
+ /**
46
+ * `{lineId: string, qty: number, addonIds: string[]}` — a one-line type for a
47
+ * value, built from the value itself.
48
+ *
49
+ * An array is described by its FIRST item, because a list whose items disagree
50
+ * is a list nothing can safely add to and the guard will say so anyway. An empty
51
+ * array is `unknown[]`, which is the true answer and the one that matters: it
52
+ * tells a reader that adding to this list is unchecked.
53
+ */
54
+ export function shapeOf(value, depth = 0) {
55
+ if (value === null) return "null";
56
+ if (Array.isArray(value)) {
57
+ if (!value.length) return "unknown[]";
58
+ if (depth >= MAX_DEPTH) return "…[]";
59
+ const inner = shapeOf(value[0], depth + 1);
60
+ return inner.includes(" ") || inner.endsWith("]") ? `(${inner})[]` : `${inner}[]`;
61
+ }
62
+ if (isPlainObject(value)) {
63
+ if (depth >= MAX_DEPTH) return "{…}";
64
+ const keys = Object.keys(value);
65
+ const shown = keys.slice(0, MAX_KEYS);
66
+ const body = shown.map(k => `${k}: ${shapeOf(value[k], depth + 1)}`).join(", ");
67
+ const more = keys.length > shown.length ? `, +${keys.length - shown.length} more` : "";
68
+ return `{${body}${more}}`;
69
+ }
70
+ const t = typeof value;
71
+ return t === "undefined" ? "undefined" : t === "function" ? "function" : t;
72
+ }
73
+ /**
74
+ * Every list inside a value, with the shape of what goes in it.
75
+ *
76
+ * This is what a caller needs before adding to a list, and it is the part that
77
+ * cannot be recovered from a truncated read of the data itself.
78
+ *
79
+ * A list with `item: undefined` is the warning case: it is empty, so its item
80
+ * shape exists nowhere in the running app — not in the store, not in the
81
+ * persisted copy, not in this sketch. Anything added to it goes in unchecked.
82
+ */
83
+ export function listShapes(value, path = "", out = [], depth = 0, budget = {
84
+ visited: 0
85
+ }) {
86
+ if (depth > MAX_DEPTH) return out;
87
+ // Two independent stops: enough lists described, or enough of the value
88
+ // walked. The second is what a wide-but-shallow store hits.
89
+ if (out.length >= MAX_LIST_PATHS || budget.visited >= MAX_ENTRIES_VISITED) return out;
90
+ if (Array.isArray(value)) {
91
+ const first = value[0];
92
+ out.push({
93
+ path: path || "(root)",
94
+ length: value.length,
95
+ ...(value.length ? {
96
+ item: shapeOf(first)
97
+ } : {})
98
+ });
99
+ // One level into the items, so `lines[].addonIds` is reachable too.
100
+ if (value.length && isPlainObject(first)) {
101
+ for (const [k, v] of Object.entries(first)) {
102
+ if (++budget.visited >= MAX_ENTRIES_VISITED) break;
103
+ listShapes(v, path ? `${path}[].${k}` : `[].${k}`, out, depth + 1, budget);
104
+ }
105
+ }
106
+ return out;
107
+ }
108
+ if (isPlainObject(value)) {
109
+ for (const [k, v] of Object.entries(value)) {
110
+ if (++budget.visited >= MAX_ENTRIES_VISITED) break;
111
+ listShapes(v, path ? `${path}.${k}` : k, out, depth + 1, budget);
112
+ }
113
+ }
114
+ return out;
115
+ }
116
+
117
+ /** True when a sketch stopped early, so a caller can say so rather than imply completeness. */
118
+ export function shapesWereTruncated(value) {
119
+ const budget = {
120
+ visited: 0
121
+ };
122
+ const out = listShapes(value, "", [], 0, budget);
123
+ return out.length >= MAX_LIST_PATHS || budget.visited >= MAX_ENTRIES_VISITED;
124
+ }
125
+
126
+ /**
127
+ * The lists in a value that a caller could add to WITHOUT any way to know the
128
+ * item shape. Empty by definition on a healthy value; non-empty means a write
129
+ * into one of these paths cannot be checked by anything.
130
+ */
131
+ export function unshapedLists(value) {
132
+ return listShapes(value).filter(l => l.item === undefined).map(l => l.path);
133
+ }
134
+
135
+ /**
136
+ * The sketch an adapter attaches to a read: the value's own type, plus the item
137
+ * type of every list in it.
138
+ *
139
+ * Returns undefined when there is nothing worth saying (a scalar, or an object
140
+ * holding no lists) so adapters can spread it and add no noise.
141
+ */
142
+ export function shapeSummary(value) {
143
+ if (value === undefined) return undefined;
144
+ const lists = listShapes(value);
145
+ const shape = shapeOf(value);
146
+ if (!lists.length && !isPlainObject(value) && !Array.isArray(value)) return undefined;
147
+ return lists.length ? {
148
+ shape,
149
+ lists
150
+ } : {
151
+ shape
152
+ };
153
+ }
@@ -23,6 +23,7 @@ import { buoyColors } from "../gameUI/constants/gameUIColors.js";
23
23
  import { CompactRow } from "./CompactRow.js";
24
24
  import { Clock, Zap } from "../../icons/index.js";
25
25
  import { RelativeTime } from "../../utils/time/RelativeTime.js";
26
+ import { monoFont } from "../fonts.js";
26
27
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
27
28
  export function ChangeRow({
28
29
  statusColor,
@@ -97,7 +98,7 @@ const styles = StyleSheet.create({
97
98
  badgeText: {
98
99
  fontSize: 11,
99
100
  fontWeight: "600",
100
- fontFamily: "monospace"
101
+ fontFamily: monoFont
101
102
  },
102
103
  iconBadge: {
103
104
  paddingHorizontal: 4
@@ -11,6 +11,7 @@
11
11
  import { memo, useCallback } from "react";
12
12
  import { View, Text, TouchableOpacity, StyleSheet, Modal, Pressable } from "react-native";
13
13
  import { gameUIColors } from "../gameUI/index.js";
14
+ import { monoFont } from "../fonts.js";
14
15
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
15
16
  /**
16
17
  * A modal hint that teaches users about iOS Simulator clipboard sync
@@ -125,7 +126,7 @@ const styles = StyleSheet.create({
125
126
  textAlign: "center"
126
127
  },
127
128
  menuPath: {
128
- fontFamily: "monospace",
129
+ fontFamily: monoFont,
129
130
  fontSize: 12,
130
131
  color: gameUIColors.primary,
131
132
  fontWeight: "600"
@@ -3,6 +3,7 @@
3
3
  import { View, Text, TouchableOpacity, StyleSheet } from "react-native";
4
4
  import { ChevronDown, ChevronRight } from "../../icons/index.js";
5
5
  import { night } from "../night/nightTheme.js";
6
+ import { monoFont } from "../fonts.js";
6
7
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
8
  export function CompactRow({
8
9
  statusDotColor,
@@ -221,7 +222,7 @@ const styles = StyleSheet.create({
221
222
  paddingHorizontal: 12
222
223
  },
223
224
  queryHash: {
224
- fontFamily: "monospace",
225
+ fontFamily: monoFont,
225
226
  fontSize: 12,
226
227
  color: night.color.text,
227
228
  lineHeight: 16
@@ -265,6 +266,6 @@ const styles = StyleSheet.create({
265
266
  bottomRightText: {
266
267
  fontSize: 9,
267
268
  color: night.color.textTertiary,
268
- fontFamily: "monospace"
269
+ fontFamily: monoFont
269
270
  }
270
271
  });
@@ -5,6 +5,7 @@ import { View, Text, StyleSheet, ScrollView, TouchableOpacity, TextInput } from
5
5
  import { Search, Edit3, Check, X } from "../../icons/index.js";
6
6
  import { gameUIColors } from "../gameUI/index.js";
7
7
  import { CopyButton } from "./CopyButton.js";
8
+ import { monoFont } from "../fonts.js";
8
9
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
9
10
  export function DataInspector({
10
11
  data,
@@ -264,14 +265,14 @@ const styles = StyleSheet.create({
264
265
  maxHeight: 300
265
266
  },
266
267
  codeText: {
267
- fontFamily: "monospace",
268
+ fontFamily: monoFont,
268
269
  fontSize: 11,
269
270
  color: gameUIColors.text,
270
271
  padding: 12,
271
272
  lineHeight: 16
272
273
  },
273
274
  editor: {
274
- fontFamily: "monospace",
275
+ fontFamily: monoFont,
275
276
  fontSize: 11,
276
277
  color: gameUIColors.text,
277
278
  padding: 12,
@@ -5,6 +5,7 @@ import { View, Text, StyleSheet, ScrollView, TouchableOpacity } from "react-nati
5
5
  import { gameUIColors } from "../gameUI/index.js";
6
6
  import { ModalHeader } from "./ModalHeader.js";
7
7
  import { CopyButton } from "./CopyButton.js";
8
+ import { monoFont } from "../fonts.js";
8
9
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
9
10
  export function DetailView({
10
11
  children,
@@ -293,7 +294,7 @@ const styles = StyleSheet.create({
293
294
  },
294
295
  dataText: {
295
296
  fontSize: 12,
296
- fontFamily: "monospace",
297
+ fontFamily: monoFont,
297
298
  color: gameUIColors.text
298
299
  },
299
300
  statusBar: {
@@ -8,6 +8,7 @@ import { FilterList, AddFilterInput, AddFilterButton } from "./FilterComponents.
8
8
  import { useFilterManager } from "../../hooks/useFilterManager.js";
9
9
  import { buoyColors } from "../gameUI/constants/gameUIColors.js";
10
10
  import { night } from "../night/nightTheme.js";
11
+ import { monoFont } from "../fonts.js";
11
12
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
12
13
  export function DynamicFilterView({
13
14
  sections = [],
@@ -489,7 +490,7 @@ const styles = StyleSheet.create({
489
490
  fontSize: 14,
490
491
  fontWeight: "700",
491
492
  color: night.color.text,
492
- fontFamily: "monospace",
493
+ fontFamily: monoFont,
493
494
  marginTop: 3,
494
495
  marginBottom: 1,
495
496
  textShadowColor: "rgba(0, 0, 0, 0.2)",
@@ -503,7 +504,7 @@ const styles = StyleSheet.create({
503
504
  fontSize: 11,
504
505
  fontWeight: "500",
505
506
  color: night.color.textTertiary,
506
- fontFamily: "monospace",
507
+ fontFamily: monoFont,
507
508
  marginTop: 3,
508
509
  marginBottom: 1
509
510
  },
@@ -547,7 +548,7 @@ const styles = StyleSheet.create({
547
548
  fontSize: 13,
548
549
  fontWeight: "700",
549
550
  letterSpacing: 0.3,
550
- fontFamily: "monospace"
551
+ fontFamily: monoFont
551
552
  },
552
553
  activeFiltersSection: {
553
554
  backgroundColor: night.color.surface,
@@ -686,7 +687,7 @@ const styles = StyleSheet.create({
686
687
  flex: 1,
687
688
  fontSize: 11,
688
689
  color: night.color.text,
689
- fontFamily: "monospace",
690
+ fontFamily: monoFont,
690
691
  marginRight: 8
691
692
  },
692
693
  howItWorksSection: {
@@ -705,7 +706,7 @@ const styles = StyleSheet.create({
705
706
  marginBottom: 12,
706
707
  marginTop: 8,
707
708
  paddingHorizontal: 16,
708
- fontFamily: "monospace"
709
+ fontFamily: monoFont
709
710
  },
710
711
  examplesContainer: {
711
712
  paddingTop: 8,
@@ -718,14 +719,14 @@ const styles = StyleSheet.create({
718
719
  fontSize: 10,
719
720
  fontWeight: "600",
720
721
  color: night.color.textTertiary,
721
- fontFamily: "monospace",
722
+ fontFamily: monoFont,
722
723
  letterSpacing: 0.5,
723
724
  marginBottom: 6
724
725
  },
725
726
  exampleItem: {
726
727
  fontSize: 10,
727
728
  color: night.color.textTertiary,
728
- fontFamily: "monospace",
729
+ fontFamily: monoFont,
729
730
  lineHeight: 16
730
731
  },
731
732
  previewSection: {
@@ -23,6 +23,7 @@ import { Filter, X } from "../../icons/index.js";
23
23
  import { night } from "../night/nightTheme.js";
24
24
  import { SectionHeader } from "./SectionHeader.js";
25
25
  import { AddFilterButton, AddFilterInput } from "./FilterComponents.js";
26
+ import { monoFont } from "../fonts.js";
26
27
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
27
28
  /** True if any active pattern hides this entity. */
28
29
  function isHidden(entity, ignoredPatterns) {
@@ -264,7 +265,7 @@ const styles = StyleSheet.create({
264
265
  flex: 1,
265
266
  fontSize: 12,
266
267
  fontWeight: "600",
267
- fontFamily: "monospace"
268
+ fontFamily: monoFont
268
269
  },
269
270
  entityTextHidden: {
270
271
  color: night.color.textTertiary,
@@ -274,12 +275,12 @@ const styles = StyleSheet.create({
274
275
  fontSize: 10,
275
276
  fontWeight: "500",
276
277
  color: night.color.textTertiary,
277
- fontFamily: "monospace"
278
+ fontFamily: monoFont
278
279
  },
279
280
  addLabel: {
280
281
  fontSize: 10,
281
282
  fontWeight: "600",
282
- fontFamily: "monospace"
283
+ fontFamily: monoFont
283
284
  },
284
285
  addInputWrapper: {
285
286
  paddingHorizontal: 16,
@@ -306,7 +307,7 @@ const styles = StyleSheet.create({
306
307
  patternText: {
307
308
  flex: 1,
308
309
  fontSize: 12,
309
- fontFamily: "monospace",
310
+ fontFamily: monoFont,
310
311
  color: night.color.text
311
312
  },
312
313
  emptyText: {
@@ -315,7 +316,7 @@ const styles = StyleSheet.create({
315
316
  paddingHorizontal: 16,
316
317
  paddingTop: 8,
317
318
  paddingBottom: 16,
318
- fontFamily: "monospace"
319
+ fontFamily: monoFont
319
320
  },
320
321
  howItWorksText: {
321
322
  fontSize: 11,
@@ -323,7 +324,7 @@ const styles = StyleSheet.create({
323
324
  lineHeight: 16,
324
325
  marginTop: 8,
325
326
  paddingHorizontal: 16,
326
- fontFamily: "monospace"
327
+ fontFamily: monoFont
327
328
  },
328
329
  examplesContainer: {
329
330
  paddingTop: 8,
@@ -337,14 +338,14 @@ const styles = StyleSheet.create({
337
338
  fontSize: 10,
338
339
  fontWeight: "600",
339
340
  color: night.color.textTertiary,
340
- fontFamily: "monospace",
341
+ fontFamily: monoFont,
341
342
  letterSpacing: 0.5,
342
343
  marginBottom: 6
343
344
  },
344
345
  exampleItem: {
345
346
  fontSize: 10,
346
347
  color: night.color.textTertiary,
347
- fontFamily: "monospace",
348
+ fontFamily: monoFont,
348
349
  lineHeight: 16
349
350
  }
350
351
  });
@@ -12,11 +12,12 @@ import React, { memo } from "react";
12
12
  import { View, Text, TouchableOpacity, StyleSheet } from "react-native";
13
13
  import { macOSColors } from "../../gameUI/constants/macOSDesignSystemColors.js";
14
14
  import { ChevronLeft, ChevronRight } from "../../../icons/lucide-icons.js";
15
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
15
+ import { monoFont } from "../../fonts.js";
16
16
  /**
17
17
  * CompareBar displays a side-by-side comparison of two events
18
18
  * with optional navigation controls for any-to-any compare mode.
19
19
  */
20
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
20
21
  export const CompareBar = /*#__PURE__*/memo(function CompareBar({
21
22
  leftEvent,
22
23
  rightEvent,
@@ -150,7 +151,7 @@ const styles = StyleSheet.create({
150
151
  },
151
152
  label: {
152
153
  fontSize: 10,
153
- fontFamily: "monospace",
154
+ fontFamily: monoFont,
154
155
  fontWeight: "700",
155
156
  letterSpacing: 0.5,
156
157
  textTransform: "uppercase"
@@ -185,17 +186,17 @@ const styles = StyleSheet.create({
185
186
  index: {
186
187
  fontSize: 10,
187
188
  color: macOSColors.text.secondary,
188
- fontFamily: "monospace"
189
+ fontFamily: monoFont
189
190
  },
190
191
  time: {
191
192
  fontSize: 11,
192
193
  color: macOSColors.text.primary,
193
- fontFamily: "monospace"
194
+ fontFamily: monoFont
194
195
  },
195
196
  relative: {
196
197
  fontSize: 10,
197
198
  color: macOSColors.text.secondary,
198
- fontFamily: "monospace"
199
+ fontFamily: monoFont
199
200
  },
200
201
  divider: {
201
202
  width: 1,
@@ -11,11 +11,12 @@
11
11
  import React, { memo } from "react";
12
12
  import { View, Text, TouchableOpacity, StyleSheet } from "react-native";
13
13
  import { macOSColors } from "../../gameUI/constants/macOSDesignSystemColors.js";
14
- import { jsx as _jsx } from "react/jsx-runtime";
14
+ import { monoFont } from "../../fonts.js";
15
15
  /**
16
16
  * DiffModeTabs displays a horizontal tab bar for switching
17
17
  * between different diff visualization modes.
18
18
  */
19
+ import { jsx as _jsx } from "react/jsx-runtime";
19
20
  export const DiffModeTabs = /*#__PURE__*/memo(function DiffModeTabs({
20
21
  tabs,
21
22
  activeTab,
@@ -79,7 +80,7 @@ const styles = StyleSheet.create({
79
80
  },
80
81
  tabText: {
81
82
  fontSize: 11,
82
- fontFamily: "monospace",
83
+ fontFamily: monoFont,
83
84
  fontWeight: "600",
84
85
  color: macOSColors.text.secondary,
85
86
  letterSpacing: 0.5
@@ -13,11 +13,12 @@ import { View, Text, TouchableOpacity, ScrollView, StyleSheet } from "react-nati
13
13
  import { absoluteFill } from "../../../utils/absoluteFill.js";
14
14
  import { macOSColors } from "../../gameUI/constants/macOSDesignSystemColors.js";
15
15
  import { X } from "../../../icons/lucide-icons.js";
16
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
16
+ import { monoFont } from "../../fonts.js";
17
17
  /**
18
18
  * EventPickerModal displays a modal for selecting events
19
19
  * with color-coded styling based on position (left/right).
20
20
  */
21
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
21
22
  export const EventPickerModal = /*#__PURE__*/memo(function EventPickerModal({
22
23
  isOpen,
23
24
  onClose,
@@ -118,7 +119,7 @@ const styles = StyleSheet.create({
118
119
  title: {
119
120
  fontSize: 13,
120
121
  fontWeight: "700",
121
- fontFamily: "monospace",
122
+ fontFamily: monoFont,
122
123
  textTransform: "uppercase",
123
124
  marginBottom: 8,
124
125
  letterSpacing: 0.6
@@ -185,18 +186,18 @@ const styles = StyleSheet.create({
185
186
  itemIndex: {
186
187
  fontSize: 10,
187
188
  color: macOSColors.text.secondary,
188
- fontFamily: "monospace",
189
+ fontFamily: monoFont,
189
190
  width: 40
190
191
  },
191
192
  itemTime: {
192
193
  fontSize: 11,
193
194
  color: macOSColors.text.primary,
194
- fontFamily: "monospace",
195
+ fontFamily: monoFont,
195
196
  flex: 1
196
197
  },
197
198
  itemRelative: {
198
199
  fontSize: 10,
199
200
  color: macOSColors.text.secondary,
200
- fontFamily: "monospace"
201
+ fontFamily: monoFont
201
202
  }
202
203
  });
@@ -14,6 +14,7 @@ import { ChevronLeft, ChevronRight } from "../../icons/lucide-icons.js";
14
14
  import { macOSColors } from "../gameUI/constants/macOSDesignSystemColors.js";
15
15
  import { night } from "../night/nightTheme.js";
16
16
  import { useSafeAreaInsets } from "../../hooks/useSafeAreaInsets.js";
17
+ import { monoFont } from "../fonts.js";
17
18
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
18
19
  /**
19
20
  * A footer component for navigating through a list of events/items.
@@ -132,7 +133,7 @@ const styles = StyleSheet.create({
132
133
  fontSize: 12,
133
134
  fontWeight: "600",
134
135
  color: macOSColors.text.primary,
135
- fontFamily: "monospace",
136
+ fontFamily: monoFont,
136
137
  textTransform: "uppercase"
137
138
  },
138
139
  navButtonTextDisabled: {
@@ -145,13 +146,13 @@ const styles = StyleSheet.create({
145
146
  fontSize: 14,
146
147
  fontWeight: "700",
147
148
  color: macOSColors.text.primary,
148
- fontFamily: "monospace",
149
+ fontFamily: monoFont,
149
150
  textTransform: "uppercase"
150
151
  },
151
152
  subtitleText: {
152
153
  fontSize: 11,
153
154
  color: macOSColors.text.secondary,
154
- fontFamily: "monospace",
155
+ fontFamily: monoFont,
155
156
  marginTop: 2
156
157
  },
157
158
  // ── Night variant (see ui/night/nightTheme.ts): near-black glass bar with
@@ -28,7 +28,9 @@ function CollapsedPeek({
28
28
  width,
29
29
  height,
30
30
  customContent,
31
- label
31
+ label,
32
+ testID,
33
+ attention
32
34
  }) {
33
35
  // When progress=0 (collapsed): peek is visible (opacity=1, scale=1)
34
36
  // When progress=1 (expanded): peek is hidden (opacity=0, scale=0.9)
@@ -48,16 +50,17 @@ function CollapsedPeek({
48
50
  scale
49
51
  }]
50
52
  }],
51
- children: /*#__PURE__*/_jsx(TouchableOpacity, {
53
+ children: /*#__PURE__*/_jsxs(TouchableOpacity, {
52
54
  onPress: onPress,
53
55
  activeOpacity: 0.8,
54
56
  style: [styles.peekButton, {
55
57
  width,
56
58
  height
57
59
  }],
58
- accessibilityLabel: label || `Expand options`,
60
+ accessibilityLabel: attention?.label ? `${label || "Expand options"} — ${attention.label}` : label || `Expand options`,
59
61
  accessibilityRole: "button",
60
- children: customContent || /*#__PURE__*/_jsxs(_Fragment, {
62
+ testID: testID,
63
+ children: [customContent || /*#__PURE__*/_jsxs(_Fragment, {
61
64
  children: [/*#__PURE__*/_jsx(ChevronUp, {
62
65
  size: 12,
63
66
  color: buoyColors.textMuted,
@@ -66,7 +69,17 @@ function CollapsedPeek({
66
69
  style: styles.peekCount,
67
70
  children: count
68
71
  })]
69
- })
72
+ }), attention ? /*#__PURE__*/_jsx(View, {
73
+ pointerEvents: "none",
74
+ testID: testID ? `${testID}-attention` : undefined,
75
+ style: [styles.peekAttention, {
76
+ backgroundColor: attention.color
77
+ }],
78
+ children: attention.glyph ? /*#__PURE__*/_jsx(Text, {
79
+ style: styles.peekAttentionGlyph,
80
+ children: attention.glyph
81
+ }) : null
82
+ }) : null]
70
83
  })
71
84
  });
72
85
  }
@@ -82,7 +95,8 @@ function ExpandedWrapper({
82
95
  width,
83
96
  expandedHeight,
84
97
  isExpanded,
85
- style
98
+ style,
99
+ collapseTestID
86
100
  }) {
87
101
  // Animate height from 0 to full height
88
102
  const animatedHeight = progress.interpolate({
@@ -105,6 +119,7 @@ function ExpandedWrapper({
105
119
  style: styles.collapseButton,
106
120
  accessibilityLabel: "Collapse",
107
121
  accessibilityRole: "button",
122
+ testID: collapseTestID,
108
123
  children: /*#__PURE__*/_jsx(ChevronUp, {
109
124
  size: 12,
110
125
  color: buoyColors.textMuted,
@@ -134,10 +149,13 @@ export function ExpandablePopover({
134
149
  defaultExpanded = false,
135
150
  collapsedContent,
136
151
  collapsedLabel,
152
+ attention = null,
137
153
  expandedStyle,
138
154
  persistState = false,
139
155
  storageKey,
140
- peekHeight = DEFAULT_PEEK_HEIGHT
156
+ peekHeight = DEFAULT_PEEK_HEIGHT,
157
+ testID,
158
+ collapseTestID
141
159
  }) {
142
160
  const [isExpanded, setIsExpanded] = useState(defaultExpanded);
143
161
  const [isStateRestored, setIsStateRestored] = useState(!persistState);
@@ -240,6 +258,7 @@ export function ExpandablePopover({
240
258
  style: styles.expandedWrapper,
241
259
  pointerEvents: isExpanded ? "box-none" : "none",
242
260
  children: /*#__PURE__*/_jsx(ExpandedWrapper, {
261
+ collapseTestID: collapseTestID,
243
262
  onCollapse: handleCollapse,
244
263
  progress: progress,
245
264
  width: width,
@@ -259,7 +278,9 @@ export function ExpandablePopover({
259
278
  width: width,
260
279
  height: peekHeight,
261
280
  customContent: collapsedContent,
262
- label: collapsedLabel
281
+ label: collapsedLabel,
282
+ testID: testID,
283
+ attention: attention
263
284
  })
264
285
  })]
265
286
  });
@@ -270,6 +291,25 @@ export function ExpandablePopover({
270
291
  // ============================================================================
271
292
 
272
293
  const styles = StyleSheet.create({
294
+ peekAttention: {
295
+ position: "absolute",
296
+ top: 2,
297
+ right: 2,
298
+ minWidth: 11,
299
+ height: 11,
300
+ borderRadius: 6,
301
+ paddingHorizontal: 2,
302
+ alignItems: "center",
303
+ justifyContent: "center",
304
+ borderWidth: 1.5,
305
+ borderColor: "#0C0C0C"
306
+ },
307
+ peekAttentionGlyph: {
308
+ color: "#000",
309
+ fontSize: 8,
310
+ fontWeight: "800",
311
+ lineHeight: 9
312
+ },
273
313
  container: {
274
314
  overflow: "visible",
275
315
  zIndex: 1000