@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,191 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.WATER_ROWS = exports.WATER_ROW0 = exports.WATER_FRAMES = exports.WATER_CELLS = exports.TRANSPARENT = exports.MAX_PERIOD = exports.CELLS = void 0;
7
+ exports.cycleIndexes = cycleIndexes;
8
+ exports.flatColors = flatColors;
9
+ exports.gridKey = gridKey;
10
+ exports.ottoCycle = ottoCycle;
11
+ exports.ottoSheets = ottoSheets;
12
+ exports.rectsOf = rectsOf;
13
+ var _ottoEngine = require("./ottoEngine.js");
14
+ /**
15
+ * Otto's precomputed animation.
16
+ *
17
+ * Every state is a short loop (48 ticks at most). This module renders that
18
+ * loop once, splits it into a BODY layer and a 7-frame WATER layer (they
19
+ * composite back pixel-for-pixel: compose() only paints water into empty
20
+ * cells and blanks a submerged body below the waterline), dedupes the
21
+ * frames, and turns each distinct frame into merged rectangles. The shipping
22
+ * <Otto/> mounts those rectangles ONCE as a strip of static Views and slides
23
+ * a window over the strip on the UI thread.
24
+ *
25
+ * Why: the buoy-optimize rounds in OTTO_PERF_PLAN.md showed that any renderer
26
+ * re-creating Views per tick pegs the JS thread (5 FPS at 34 sprites, dropped
27
+ * UI frames even at 1 sprite), while "mount once, move a window natively"
28
+ * costs nothing per tick and keeps moving while JS is blocked.
29
+ *
30
+ * Everything here is pure `react-native`-free TypeScript, cached per state.
31
+ */
32
+
33
+ const CELLS = exports.CELLS = _ottoEngine.OTTO_GRID * _ottoEngine.OTTO_GRID;
34
+ const WATER_ROW0 = exports.WATER_ROW0 = 13;
35
+ const WATER_ROWS = exports.WATER_ROWS = _ottoEngine.OTTO_GRID - WATER_ROW0;
36
+ const WATER_CELLS = exports.WATER_CELLS = WATER_ROWS * _ottoEngine.OTTO_GRID;
37
+ /** The wave is a function of floor(t/2) mod 7. */
38
+ const WATER_FRAMES = exports.WATER_FRAMES = 7;
39
+ const TRANSPARENT = exports.TRANSPARENT = "transparent";
40
+ /** Longest state cycle in ticks (idle and sleep are 48). */
41
+ const MAX_PERIOD = exports.MAX_PERIOD = 48;
42
+ function gridKey(grid) {
43
+ return grid.map(r => r.join("")).join("|");
44
+ }
45
+
46
+ /** Flat 256 colours, row-major, `transparent` for empty cells. */
47
+ function flatColors(grid) {
48
+ const out = new Array(CELLS);
49
+ for (let y = 0; y < _ottoEngine.OTTO_GRID; y++) {
50
+ const row = grid[y];
51
+ for (let x = 0; x < _ottoEngine.OTTO_GRID; x++) out[y * _ottoEngine.OTTO_GRID + x] = _ottoEngine.OTTO_PALETTE[row[x]] ?? TRANSPARENT;
52
+ }
53
+ return out;
54
+ }
55
+
56
+ // ---------------------------------------------------------------- cycle ---
57
+
58
+ const cycleCache = new Map();
59
+ function ottoCycle(state, keyline) {
60
+ const cacheKey = `${state}|${keyline ? 1 : 0}`;
61
+ const hit = cycleCache.get(cacheKey);
62
+ if (hit) return hit;
63
+ const keys = [];
64
+ const grids = [];
65
+ for (let t = 0; t < MAX_PERIOD * 2; t++) {
66
+ const g = (0, _ottoEngine.renderOttoFrame)(state, t, {
67
+ water: false,
68
+ keyline
69
+ });
70
+ grids.push(g);
71
+ keys.push(gridKey(g));
72
+ }
73
+ let period = MAX_PERIOD;
74
+ for (let P = 1; P <= MAX_PERIOD; P++) {
75
+ let ok = true;
76
+ for (let t = 0; t < MAX_PERIOD; t++) {
77
+ if (keys[t] !== keys[t + P]) {
78
+ ok = false;
79
+ break;
80
+ }
81
+ }
82
+ if (ok) {
83
+ period = P;
84
+ break;
85
+ }
86
+ }
87
+ const body = grids.slice(0, period).map(flatColors);
88
+ const water = [];
89
+ for (let w = 0; w < WATER_FRAMES; w++) {
90
+ const flat = [];
91
+ for (const row of (0, _ottoEngine.ottoWaterRows)(w * 2)) for (const k of row) flat.push(_ottoEngine.OTTO_PALETTE[k] ?? TRANSPARENT);
92
+ water.push(flat);
93
+ }
94
+ const cycle = {
95
+ period,
96
+ body,
97
+ water
98
+ };
99
+ cycleCache.set(cacheKey, cycle);
100
+ return cycle;
101
+ }
102
+
103
+ /** Frame indexes for a cycle at local tick `t`. */
104
+ function cycleIndexes(t, period) {
105
+ const local = Math.max(0, t);
106
+ return {
107
+ bi: local % period,
108
+ wi: Math.floor(local / 2) % WATER_FRAMES
109
+ };
110
+ }
111
+
112
+ // ---------------------------------------------------------------- rects ---
113
+
114
+ /**
115
+ * Same-colour runs per row, then runs merged with the identical run directly
116
+ * below them — roughly a third fewer Views than row runs alone. Covers
117
+ * exactly the non-transparent cells, never overlaps (proved offline over
118
+ * every frame of every state).
119
+ */
120
+ function rectsOf(colors, cols, rows) {
121
+ const open = [];
122
+ const done = [];
123
+ for (let y = 0; y < rows; y++) {
124
+ const rowRuns = [];
125
+ let x = 0;
126
+ while (x < cols) {
127
+ const color = colors[y * cols + x];
128
+ let w = 1;
129
+ while (x + w < cols && colors[y * cols + x + w] === color) w++;
130
+ if (color !== TRANSPARENT) rowRuns.push({
131
+ x,
132
+ y,
133
+ w,
134
+ h: 1,
135
+ color
136
+ });
137
+ x += w;
138
+ }
139
+ const next = [];
140
+ for (const r of rowRuns) {
141
+ const above = open.find(o => o.x === r.x && o.w === r.w && o.color === r.color && o.y + o.h === r.y);
142
+ if (above) {
143
+ above.h += 1;
144
+ next.push(above);
145
+ } else {
146
+ next.push(r);
147
+ }
148
+ }
149
+ for (const o of open) if (!next.includes(o)) done.push(o);
150
+ open.length = 0;
151
+ open.push(...next);
152
+ }
153
+ done.push(...open);
154
+ return done;
155
+ }
156
+
157
+ // ---------------------------------------------------------------- sheets --
158
+
159
+ /** One layer as a strip of distinct frames, and which frame each tick shows. */
160
+
161
+ const sheetCache = new Map();
162
+ function stripOf(frames, cols, rows) {
163
+ const keys = frames.map(f => f.join(","));
164
+ const indexOf = new Map();
165
+ const distinct = [];
166
+ keys.forEach((k, i) => {
167
+ if (!indexOf.has(k)) {
168
+ indexOf.set(k, distinct.length);
169
+ distinct.push(rectsOf(frames[i], cols, rows));
170
+ }
171
+ });
172
+ return {
173
+ colOf: keys.map(k => indexOf.get(k) ?? 0),
174
+ frames: distinct
175
+ };
176
+ }
177
+
178
+ /** Both layers of a state as strips of rectangles. Pure; cached per state. */
179
+ function ottoSheets(state, keyline) {
180
+ const key = `${state}|${keyline ? 1 : 0}`;
181
+ const hit = sheetCache.get(key);
182
+ if (hit) return hit;
183
+ const cycle = ottoCycle(state, keyline);
184
+ const built = {
185
+ period: cycle.period,
186
+ body: stripOf(cycle.body, _ottoEngine.OTTO_GRID, _ottoEngine.OTTO_GRID),
187
+ water: stripOf(cycle.water, _ottoEngine.OTTO_GRID, WATER_ROWS)
188
+ };
189
+ sheetCache.set(key, built);
190
+ return built;
191
+ }
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.PREVIEW_ITEM_RENDER_BYTE_LIMIT = void 0;
7
+ exports.classifyPreviewItem = classifyPreviewItem;
8
+ exports.createRestoreScope = createRestoreScope;
9
+ exports.emptyRestoreResult = emptyRestoreResult;
10
+ exports.failedRestore = failedRestore;
11
+ exports.skippedRestore = skippedRestore;
12
+ exports.unionKeys = unionKeys;
13
+ exports.wontApply = wontApply;
14
+ var _objectDiff = require("../dataViewer/objectDiff.js");
15
+ /**
16
+ * snapshotProviderKit — the parts of a Time Machine `StateSnapshotProvider`
17
+ * that are the same for every source.
18
+ *
19
+ * storage, redux, zustand, jotai and react-query each register a provider
20
+ * (see stateSnapshotRegistry.ts). What differs is how they read and write
21
+ * their source. What was copied five times is:
22
+ *
23
+ * - the restore SCOPE gate: `excludeKeys` (touch all but these) and
24
+ * `includeKeys` (touch only these), the "N item(s) excluded by you"
25
+ * warning, and the empty `StateSnapshotRestoreResult`;
26
+ * - the preview VERDICT ladder: removed → added → unchanged → too-large →
27
+ * changed, with the 256 KB per-item render cap;
28
+ * - the "won't apply" override: a difference restore cannot apply must say
29
+ * so instead of rendering as a green change.
30
+ *
31
+ * The parts that stay per-tool are the ones that matter: redux verifies the
32
+ * jump by reference identity, zustand grafts action functions back, jotai
33
+ * leaves derived atoms to recompute, storage gates MMKV buffers and
34
+ * biometric keys, react-query diffs a comparable projection.
35
+ */
36
+
37
+ /** Beyond this a preview item is "too-large": restore still applies it, the diff isn't drawn. */
38
+ const PREVIEW_ITEM_RENDER_BYTE_LIMIT = exports.PREVIEW_ITEM_RENDER_BYTE_LIMIT = 256 * 1024;
39
+ /**
40
+ * Both option lists use preview item keys, and both mean "leave everything
41
+ * else untouched in BOTH directions". `includeKeys` wins when present;
42
+ * `excludeKeys` still applies on top.
43
+ */
44
+ function createRestoreScope(options) {
45
+ const excludedSet = new Set(options?.excludeKeys ?? []);
46
+ const includedSet = options?.includeKeys ? new Set(options.includeKeys) : null;
47
+ let excludedHits = 0;
48
+ const touch = key => !excludedSet.has(key) && (!includedSet || includedSet.has(key));
49
+ return {
50
+ touch,
51
+ allows(key) {
52
+ if (touch(key)) return true;
53
+ excludedHits += 1;
54
+ return false;
55
+ },
56
+ countExcluded(n) {
57
+ excludedHits += n;
58
+ },
59
+ get excludedHits() {
60
+ return excludedHits;
61
+ },
62
+ excludedWarning(noun) {
63
+ return excludedHits > 0 ? `${excludedHits} ${noun} excluded by you` : null;
64
+ }
65
+ };
66
+ }
67
+
68
+ /** A fresh, successful, empty result to accumulate into. */
69
+ function emptyRestoreResult() {
70
+ return {
71
+ ok: true,
72
+ applied: 0,
73
+ skipped: [],
74
+ warnings: []
75
+ };
76
+ }
77
+
78
+ /** A failed result with one warning (an empty or unusable snapshot). */
79
+ function failedRestore(warning) {
80
+ return {
81
+ ok: false,
82
+ applied: 0,
83
+ skipped: [],
84
+ warnings: [warning]
85
+ };
86
+ }
87
+
88
+ /** A failed result with one skipped item and its reason. */
89
+ function skippedRestore(id, reason) {
90
+ return {
91
+ ok: false,
92
+ applied: 0,
93
+ skipped: [{
94
+ id,
95
+ reason
96
+ }],
97
+ warnings: []
98
+ };
99
+ }
100
+ /**
101
+ * The verdict ladder: removed → added → unchanged → too-large → changed.
102
+ * Callers layer source-specific verdicts (`wont-apply`, `recomputes`) on top
103
+ * with `wontApply()`.
104
+ */
105
+ function classifyPreviewItem(input, options = {}) {
106
+ const {
107
+ key,
108
+ label,
109
+ oldValue,
110
+ newValue,
111
+ inOld,
112
+ inNew
113
+ } = input;
114
+ if (!inNew) return {
115
+ key,
116
+ label,
117
+ verdict: "removed",
118
+ oldValue
119
+ };
120
+ if (!inOld) return {
121
+ key,
122
+ label,
123
+ verdict: "added",
124
+ newValue
125
+ };
126
+ const equal = options.equal ?? _objectDiff.boundedDeepEqual;
127
+ if (equal(oldValue, newValue)) return {
128
+ key,
129
+ label,
130
+ verdict: "unchanged"
131
+ };
132
+ const tooLarge = options.tooLarge ?? (value => (0, _objectDiff.jsonLikelyLargerThan)(value, PREVIEW_ITEM_RENDER_BYTE_LIMIT));
133
+ if (tooLarge(oldValue) || tooLarge(newValue)) {
134
+ return {
135
+ key,
136
+ label,
137
+ verdict: "too-large",
138
+ reason: options.tooLargeReason ?? "value too large to render a diff — restore still applies it"
139
+ };
140
+ }
141
+ return {
142
+ key,
143
+ label,
144
+ verdict: "changed",
145
+ oldValue,
146
+ newValue
147
+ };
148
+ }
149
+
150
+ /**
151
+ * Mark an item as something restore cannot apply. An unchanged item is left
152
+ * alone — there is nothing to apply, so nothing is being refused.
153
+ */
154
+ function wontApply(item, reason) {
155
+ if (item.verdict === "unchanged") return item;
156
+ return {
157
+ ...item,
158
+ verdict: "wont-apply",
159
+ reason
160
+ };
161
+ }
162
+
163
+ /** Every key present in either record, once. */
164
+ function unionKeys(a, b) {
165
+ const keysOf = r => r === undefined ? [] : r instanceof Map ? Array.from(r.keys()) : Object.keys(r);
166
+ return new Set([...keysOf(a), ...keysOf(b)]);
167
+ }
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.entityColor = entityColor;
7
+ exports.formatValuePreview = formatValuePreview;
8
+ exports.shallowDiffSummary = shallowDiffSummary;
9
+ /**
10
+ * stateTools — helpers the state tools (redux / zustand / jotai) each carried
11
+ * a private copy of. Pure functions; no React, no RN.
12
+ */
13
+
14
+ // ============================================
15
+ // Entity color
16
+ // ============================================
17
+
18
+ /**
19
+ * Consistent color for a named entity (a zustand store, a jotai atom, a redux
20
+ * slice). Exact match on `known` wins, then a substring match, then a stable
21
+ * HSL hash of the name rendered as `#rrggbb` so the "color + alpha hex"
22
+ * pattern works everywhere.
23
+ */
24
+ function entityColor(name, known = {}) {
25
+ const lower = name.toLowerCase();
26
+ if (known[lower]) return known[lower];
27
+ for (const [key, color] of Object.entries(known)) {
28
+ if (lower.includes(key)) return color;
29
+ }
30
+ const hash = name.split("").reduce((acc, char) => acc + char.charCodeAt(0), 0);
31
+ const hue = hash * 137 % 360;
32
+ const s = 0.7;
33
+ const l = 0.6;
34
+ const c = (1 - Math.abs(2 * l - 1)) * s;
35
+ const x = c * (1 - Math.abs(hue / 60 % 2 - 1));
36
+ const m = l - c / 2;
37
+ let r = 0;
38
+ let g = 0;
39
+ let b = 0;
40
+ if (hue < 60) {
41
+ r = c;
42
+ g = x;
43
+ } else if (hue < 120) {
44
+ r = x;
45
+ g = c;
46
+ } else if (hue < 180) {
47
+ g = c;
48
+ b = x;
49
+ } else if (hue < 240) {
50
+ g = x;
51
+ b = c;
52
+ } else if (hue < 300) {
53
+ r = x;
54
+ b = c;
55
+ } else {
56
+ r = c;
57
+ b = x;
58
+ }
59
+ const toHex = v => Math.round((v + m) * 255).toString(16).padStart(2, "0");
60
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
61
+ }
62
+
63
+ // ============================================
64
+ // Value preview
65
+ // ============================================
66
+
67
+ /**
68
+ * One-line preview of an arbitrary value for a list row: strings quoted and
69
+ * truncated, arrays and objects stringified when short, otherwise collapsed
70
+ * to `[N items]` / `{ N keys }`. Never throws.
71
+ */
72
+ function formatValuePreview(value, options = {}) {
73
+ const maxLength = options.maxLength ?? 40;
74
+ if (value === undefined) return options.undefinedLabel ?? "undefined";
75
+ if (value === null) return "null";
76
+ try {
77
+ if (typeof value === "function") return options.functionLabel ?? "(fn)";
78
+ if (typeof value === "string") {
79
+ return value.length > maxLength ? `"${value.slice(0, maxLength - 3)}..."` : `"${value}"`;
80
+ }
81
+ if (typeof value === "number" || typeof value === "boolean") {
82
+ return String(value);
83
+ }
84
+ if (Array.isArray(value)) {
85
+ if (value.length === 0) return "[]";
86
+ const preview = JSON.stringify(value);
87
+ return preview.length > maxLength ? `[${value.length} items]` : preview;
88
+ }
89
+ if (typeof value === "object") {
90
+ const keys = Object.keys(value);
91
+ if (keys.length === 0) return "{}";
92
+ const preview = JSON.stringify(value);
93
+ if (preview.length <= maxLength) return preview;
94
+ return `{ ${keys.length} keys }`;
95
+ }
96
+ return String(value).slice(0, maxLength);
97
+ } catch {
98
+ return "[complex]";
99
+ }
100
+ }
101
+
102
+ // ============================================
103
+ // Shallow diff summary
104
+ // ============================================
105
+
106
+ /**
107
+ * Top-level, reference-equality diff between two states. Enough for a list
108
+ * row ("+1 ~2 keys"); the detail view runs the real tree diff.
109
+ *
110
+ * A state object that was REPLACED without any top-level value moving —
111
+ * exactly what zustand's persist middleware does when it rehydrates — is
112
+ * reported as `"nested change"` with no keys, never as one changed key with
113
+ * none named.
114
+ */
115
+ function shallowDiffSummary(prev, next) {
116
+ if (prev === next) {
117
+ return {
118
+ summary: "no change",
119
+ changedKeys: [],
120
+ changedCount: 0
121
+ };
122
+ }
123
+ if (typeof prev !== "object" || typeof next !== "object" || prev === null || next === null) {
124
+ return {
125
+ summary: "changed",
126
+ changedKeys: [],
127
+ changedCount: 0
128
+ };
129
+ }
130
+ const prevRecord = prev;
131
+ const nextRecord = next;
132
+ const prevKeys = Object.keys(prevRecord);
133
+ const nextKeys = Object.keys(nextRecord);
134
+ const added = nextKeys.filter(k => !prevKeys.includes(k));
135
+ const removed = prevKeys.filter(k => !nextKeys.includes(k));
136
+ const changed = [];
137
+ for (const key of prevKeys) {
138
+ if (nextKeys.includes(key) && prevRecord[key] !== nextRecord[key]) {
139
+ changed.push(key);
140
+ }
141
+ }
142
+ const changedKeys = [...added, ...removed, ...changed];
143
+ const parts = [];
144
+ if (added.length > 0) parts.push(`+${added.length}`);
145
+ if (removed.length > 0) parts.push(`-${removed.length}`);
146
+ if (changed.length > 0) parts.push(`~${changed.length}`);
147
+ if (parts.length === 0) {
148
+ return {
149
+ summary: "nested change",
150
+ changedKeys: [],
151
+ changedCount: 0
152
+ };
153
+ }
154
+ const total = changedKeys.length;
155
+ return {
156
+ summary: `${parts.join(" ")} ${total === 1 ? "key" : "keys"}`,
157
+ changedKeys,
158
+ changedCount: total
159
+ };
160
+ }
@@ -433,15 +433,19 @@ const JsModalComponent = ({
433
433
  return () => clearTimeout(timeoutId);
434
434
  }, [mode, panelHeight, dimensions, visible, persistenceKey, enablePersistence, isStateLoaded]);
435
435
 
436
- // Sync with external height if provided
436
+ // Seed the external height ONCE. This used to re-run when `isResizing`
437
+ // flipped, which snapped an externally-controlled sheet back to
438
+ // initialHeight the moment a drag-resize ended (the release handler had
439
+ // just written the real height). The host owns the value after the seed;
440
+ // persistence restore above may overwrite the seed, which is correct.
441
+ const externalHeightSeeded = useRef(false);
437
442
  useEffect(() => {
438
- // Height sync effect
439
- if (externalAnimatedHeight && !isResizing) {
443
+ if (externalAnimatedHeight && !externalHeightSeeded.current) {
444
+ externalHeightSeeded.current = true;
440
445
  currentHeightRef.current = initialHeight;
441
446
  externalAnimatedHeight.setValue(initialHeight);
442
- // Set external height
443
447
  }
444
- }, [externalAnimatedHeight, initialHeight, isResizing]);
448
+ }, [externalAnimatedHeight, initialHeight]);
445
449
 
446
450
  // Update refs when dimensions change
447
451
  useEffect(() => {
@@ -531,11 +535,14 @@ const JsModalComponent = ({
531
535
  onModeChangeRef.current?.(savedState.mode);
532
536
  }
533
537
 
534
- // Restore bottom sheet height
538
+ // Restore bottom sheet height. The external animated height (when a
539
+ // host passes one) IS the rendered height, so it must be restored
540
+ // too or a persisted sheet always reopens at initialHeight.
535
541
  if (savedState.panelHeight) {
536
542
  setPanelHeight(savedState.panelHeight);
537
543
  currentHeightRef.current = savedState.panelHeight;
538
544
  animatedBottomPosition.setValue(savedState.panelHeight);
545
+ externalAnimatedHeight?.setValue(savedState.panelHeight);
539
546
  }
540
547
 
541
548
  // Restore floating dimensions and position
@@ -595,7 +602,6 @@ const JsModalComponent = ({
595
602
  // ============================================================================
596
603
  // REFS for values we need to track
597
604
  // ============================================================================
598
- const isExternallyControlled = !!externalAnimatedHeight;
599
605
  const effectiveMaxHeight = maxHeight || SCREEN.height - insets.top;
600
606
 
601
607
  // Mode toggle handler
@@ -717,29 +723,57 @@ const JsModalComponent = ({
717
723
  // ============================================================================
718
724
  // EFFECT: Visibility Animations - All using native driver!
719
725
  // ============================================================================
726
+ const snapVisibility = shown => {
727
+ if (shown) {
728
+ if (mode === "bottomSheet") {
729
+ bottomSheetTranslateY.setValue(0);
730
+ } else {
731
+ floatingScale.setValue(1);
732
+ floatingPosition.setValue({
733
+ x: currentDimensionsRef.current.left,
734
+ y: currentDimensionsRef.current.top
735
+ });
736
+ }
737
+ visibilityProgress.setValue(1);
738
+ } else {
739
+ if (mode === "bottomSheet") {
740
+ bottomSheetTranslateY.setValue(SCREEN.height);
741
+ }
742
+ visibilityProgress.setValue(0);
743
+ }
744
+ };
745
+
746
+ /**
747
+ * Snap DURING RENDER when `visible` flips, not only in the effect below.
748
+ *
749
+ * These values drive the sheet's opacity/translate through the native
750
+ * driver. The React commit for a `visible` change carries whatever the
751
+ * JS-side values are at render time — the OLD, hidden ones when the snap
752
+ * only happens in the effect — and on Fabric that commit's mount can land
753
+ * on the main thread AFTER the effect's native `setValue`, overwriting it.
754
+ * The Animated node then says "shown" while the UIView sits at opacity 0,
755
+ * until some unrelated re-render commits the JS-side values again. That
756
+ * is how "restore tool A: nothing; restore tool B: A appears; hide B: now
757
+ * B appears" happened — every restore was one commit behind.
758
+ *
759
+ * Setting the values before the render's props are computed makes the
760
+ * commit and the native node agree, whichever lands last. `setValue` is
761
+ * idempotent, so a re-run render is harmless.
762
+ */
763
+ const snappedVisibleRef = useRef(null);
764
+ if (INSTANT_MODAL_ENTRANCE && snappedVisibleRef.current !== visible) {
765
+ snappedVisibleRef.current = visible;
766
+ snapVisibility(visible);
767
+ }
720
768
  useEffect(() => {
721
769
  // Visibility effect
722
770
  let openAnimation = null;
723
771
  let closeAnimation = null;
724
772
  if (INSTANT_MODAL_ENTRANCE) {
725
- // Snap to the final state — no animation in either direction.
726
- if (visible) {
727
- if (mode === "bottomSheet") {
728
- bottomSheetTranslateY.setValue(0);
729
- } else {
730
- floatingScale.setValue(1);
731
- floatingPosition.setValue({
732
- x: currentDimensionsRef.current.left,
733
- y: currentDimensionsRef.current.top
734
- });
735
- }
736
- visibilityProgress.setValue(1);
737
- } else {
738
- if (mode === "bottomSheet") {
739
- bottomSheetTranslateY.setValue(SCREEN.height);
740
- }
741
- visibilityProgress.setValue(0);
742
- }
773
+ // Snap to the final state — no animation in either direction. The
774
+ // render-phase snap above already did this for a `visible` flip; this
775
+ // re-applies it for a `mode` change while shown.
776
+ snapVisibility(visible);
743
777
  return;
744
778
  }
745
779
  if (visible) {
@@ -904,8 +938,15 @@ const JsModalComponent = ({
904
938
  // eslint-disable-next-line react-hooks/exhaustive-deps
905
939
  }, [benchMotion, visible]);
906
940
  const bottomSheetPanResponder = useMemo(() => PanResponder.create({
907
- onStartShouldSetPanResponder: () => !isExternallyControlled && mode === "bottomSheet",
908
- onMoveShouldSetPanResponder: (evt, gestureState) => !isExternallyControlled && mode === "bottomSheet" && Math.abs(gestureState.dy) > 3,
941
+ // An external `animatedHeight` used to disable this responder
942
+ // outright ("externally controlled"), which left Ask Buoy's sheet —
943
+ // the one host that passes one, for its peek animation — with a
944
+ // header that could not be dragged in bottom-sheet mode. The move
945
+ // and release handlers below already write every height into the
946
+ // external value, so the host and the finger share one number; the
947
+ // grab just has to stop whatever the host is animating first.
948
+ onStartShouldSetPanResponder: () => mode === "bottomSheet",
949
+ onMoveShouldSetPanResponder: (evt, gestureState) => mode === "bottomSheet" && Math.abs(gestureState.dy) > 3,
909
950
  onPanResponderTerminationRequest: () => false,
910
951
  onPanResponderGrant: evt => {
911
952
  setIsResizing(true);
@@ -917,6 +958,7 @@ const JsModalComponent = ({
917
958
  animatedBottomPosition.stopAnimation(val => {
918
959
  currentHeightRef.current = val;
919
960
  });
961
+ externalAnimatedHeight?.stopAnimation();
920
962
  bottomSheetTranslateY.stopAnimation();
921
963
  },
922
964
  onPanResponderMove: evt => {
@@ -963,7 +1005,7 @@ const JsModalComponent = ({
963
1005
  setIsResizing(false);
964
1006
  // snap back to the last stable height if you want; otherwise no-op
965
1007
  }
966
- }), [mode, isExternallyControlled, minHeight, effectiveMaxHeight, animatedBottomPosition, externalAnimatedHeight, bottomSheetTranslateY, visibilityProgress, onClose]);
1008
+ }), [mode, minHeight, effectiveMaxHeight, animatedBottomPosition, externalAnimatedHeight, bottomSheetTranslateY, visibilityProgress, onClose]);
967
1009
 
968
1010
  // ============================================================================
969
1011
  // CREATE RESIZE HANDLER: For 4-corner resize in floating mode (fixed geometry)
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
 
3
3
  export { useFilterManager } from "./useFilterManager.js";
4
- export { usePersistedState } from "./usePersistedState.js";
4
+ export { usePersistedState, resetPersistedStateCache } from "./usePersistedState.js";
5
+ export { useChangeLog, useSubscribedValue } from "./useChangeLog.js";
6
+ export { useEventStoreEvents } from "./useEventStoreEvents.js";
5
7
  export { useSafeAreaInsets } from "./useSafeAreaInsets.js";
6
8
  export { getSafeAreaInsets } from "./useSafeAreaInsets.js";
7
9
  export { useDeviceInfo, getDeviceInfo, logDeviceInfo } from "./useDeviceInfo.js";
@@ -3,7 +3,7 @@
3
3
  /**
4
4
  * Auto-generated safe area implementation
5
5
  * Detected: none
6
- * Generated at: 2026-09-01T17:57:34.045Z
6
+ * Generated at: 2026-09-04T01:21:28.486Z
7
7
  *
8
8
  * DO NOT EDIT - This file is generated by scripts/detect-safe-area.js
9
9
  *