@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.
- package/lib/commonjs/JsModal.js +70 -28
- package/lib/commonjs/hooks/index.js +26 -0
- package/lib/commonjs/hooks/safe-area-impl.js +1 -1
- package/lib/commonjs/hooks/useChangeLog.js +55 -0
- package/lib/commonjs/hooks/useEventStoreEvents.js +65 -0
- package/lib/commonjs/hooks/usePersistedState.js +45 -10
- package/lib/commonjs/index.js +143 -0
- package/lib/commonjs/storage/definePersistedSetting.js +67 -0
- package/lib/commonjs/storage/devToolsStorageKeys.js +12 -0
- package/lib/commonjs/storage/index.js +8 -1
- package/lib/commonjs/stores/ChangeLog.js +197 -0
- package/lib/commonjs/stores/createToolStore.js +175 -0
- package/lib/commonjs/stores/index.js +15 -1
- package/lib/commonjs/sync/toolSyncAdapter.js +63 -0
- package/lib/commonjs/sync/wireForm.js +172 -0
- package/lib/commonjs/tools/defineTool.js +79 -0
- package/lib/commonjs/ui/components/ChangeRow.js +110 -0
- package/lib/commonjs/ui/components/ChipStrip.js +94 -0
- package/lib/commonjs/ui/components/DetailViewToggle.js +130 -0
- package/lib/commonjs/ui/components/DevToolsCard.js +3 -1
- package/lib/commonjs/ui/components/EmptyState.js +10 -2
- package/lib/commonjs/ui/components/EntityFilterView.js +354 -0
- package/lib/commonjs/ui/components/ExportPreview.js +174 -0
- package/lib/commonjs/ui/components/InfoRow.js +90 -0
- package/lib/commonjs/ui/components/ModalHeader.js +40 -7
- package/lib/commonjs/ui/components/Pill.js +114 -0
- package/lib/commonjs/ui/components/ToggleRow.js +116 -0
- package/lib/commonjs/ui/components/ToolEmptyState.js +154 -0
- package/lib/commonjs/ui/components/ToolFooter.js +204 -0
- package/lib/commonjs/ui/components/WindowControls.js +8 -5
- package/lib/commonjs/ui/components/index.js +113 -1
- package/lib/commonjs/ui/index.js +36 -2
- package/lib/commonjs/ui/listPresets.js +43 -0
- package/lib/commonjs/ui/night/primitives.js +3 -1
- package/lib/commonjs/ui/otto/OttoSprite.js +510 -0
- package/lib/commonjs/ui/otto/index.js +38 -0
- package/lib/commonjs/ui/otto/ottoEngine.js +2734 -0
- package/lib/commonjs/ui/otto/ottoSheet.js +191 -0
- package/lib/commonjs/utils/snapshotProviderKit.js +167 -0
- package/lib/commonjs/utils/stateTools.js +160 -0
- package/lib/module/JsModal.js +70 -28
- package/lib/module/hooks/index.js +3 -1
- package/lib/module/hooks/safe-area-impl.js +1 -1
- package/lib/module/hooks/useChangeLog.js +50 -0
- package/lib/module/hooks/useEventStoreEvents.js +61 -0
- package/lib/module/hooks/usePersistedState.js +44 -10
- package/lib/module/index.js +11 -1
- package/lib/module/storage/definePersistedSetting.js +63 -0
- package/lib/module/storage/devToolsStorageKeys.js +12 -0
- package/lib/module/storage/index.js +2 -1
- package/lib/module/stores/ChangeLog.js +192 -0
- package/lib/module/stores/createToolStore.js +171 -0
- package/lib/module/stores/index.js +3 -1
- package/lib/module/sync/toolSyncAdapter.js +58 -0
- package/lib/module/sync/wireForm.js +159 -0
- package/lib/module/tools/defineTool.js +74 -0
- package/lib/module/ui/components/ChangeRow.js +105 -0
- package/lib/module/ui/components/ChipStrip.js +91 -0
- package/lib/module/ui/components/DetailViewToggle.js +125 -0
- package/lib/module/ui/components/DevToolsCard.js +3 -1
- package/lib/module/ui/components/EmptyState.js +10 -2
- package/lib/module/ui/components/EntityFilterView.js +350 -0
- package/lib/module/ui/components/ExportPreview.js +169 -0
- package/lib/module/ui/components/InfoRow.js +86 -0
- package/lib/module/ui/components/ModalHeader.js +40 -7
- package/lib/module/ui/components/Pill.js +110 -0
- package/lib/module/ui/components/ToggleRow.js +112 -0
- package/lib/module/ui/components/ToolEmptyState.js +149 -0
- package/lib/module/ui/components/ToolFooter.js +196 -0
- package/lib/module/ui/components/WindowControls.js +8 -5
- package/lib/module/ui/components/index.js +13 -1
- package/lib/module/ui/index.js +7 -1
- package/lib/module/ui/listPresets.js +39 -0
- package/lib/module/ui/night/primitives.js +3 -1
- package/lib/module/ui/otto/OttoSprite.js +496 -0
- package/lib/module/ui/otto/index.js +20 -0
- package/lib/module/ui/otto/ottoEngine.js +2726 -0
- package/lib/module/ui/otto/ottoSheet.js +180 -0
- package/lib/module/utils/snapshotProviderKit.js +156 -0
- package/lib/module/utils/stateTools.js +154 -0
- package/lib/typescript/commonjs/JsModal.d.ts.map +1 -1
- package/lib/typescript/commonjs/hooks/index.d.ts +3 -1
- package/lib/typescript/commonjs/hooks/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/hooks/safe-area-impl.d.ts +1 -1
- package/lib/typescript/commonjs/hooks/useChangeLog.d.ts +38 -0
- package/lib/typescript/commonjs/hooks/useChangeLog.d.ts.map +1 -0
- package/lib/typescript/commonjs/hooks/useEventStoreEvents.d.ts +25 -0
- package/lib/typescript/commonjs/hooks/useEventStoreEvents.d.ts.map +1 -0
- package/lib/typescript/commonjs/hooks/usePersistedState.d.ts +34 -5
- package/lib/typescript/commonjs/hooks/usePersistedState.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +6 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/storage/definePersistedSetting.d.ts +39 -0
- package/lib/typescript/commonjs/storage/definePersistedSetting.d.ts.map +1 -0
- package/lib/typescript/commonjs/storage/devToolsStorageKeys.d.ts +12 -0
- package/lib/typescript/commonjs/storage/devToolsStorageKeys.d.ts.map +1 -1
- package/lib/typescript/commonjs/storage/index.d.ts +1 -0
- package/lib/typescript/commonjs/storage/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/stores/ChangeLog.d.ts +100 -0
- package/lib/typescript/commonjs/stores/ChangeLog.d.ts.map +1 -0
- package/lib/typescript/commonjs/stores/createToolStore.d.ts +92 -0
- package/lib/typescript/commonjs/stores/createToolStore.d.ts.map +1 -0
- package/lib/typescript/commonjs/stores/index.d.ts +2 -0
- package/lib/typescript/commonjs/stores/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/sync/toolSyncAdapter.d.ts +66 -0
- package/lib/typescript/commonjs/sync/toolSyncAdapter.d.ts.map +1 -0
- package/lib/typescript/commonjs/sync/wireForm.d.ts +100 -0
- package/lib/typescript/commonjs/sync/wireForm.d.ts.map +1 -0
- package/lib/typescript/commonjs/tools/defineTool.d.ts +100 -0
- package/lib/typescript/commonjs/tools/defineTool.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/ChangeRow.d.ts +52 -0
- package/lib/typescript/commonjs/ui/components/ChangeRow.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/ChipStrip.d.ts +41 -0
- package/lib/typescript/commonjs/ui/components/ChipStrip.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/DetailViewToggle.d.ts +36 -0
- package/lib/typescript/commonjs/ui/components/DetailViewToggle.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/DevToolsCard.d.ts +8 -1
- package/lib/typescript/commonjs/ui/components/DevToolsCard.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/EmptyState.d.ts +14 -1
- package/lib/typescript/commonjs/ui/components/EmptyState.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/EntityFilterView.d.ts +51 -0
- package/lib/typescript/commonjs/ui/components/EntityFilterView.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/ExportPreview.d.ts +48 -0
- package/lib/typescript/commonjs/ui/components/ExportPreview.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/InfoRow.d.ts +33 -0
- package/lib/typescript/commonjs/ui/components/InfoRow.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/ModalHeader.d.ts +31 -1
- package/lib/typescript/commonjs/ui/components/ModalHeader.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/Pill.d.ts +38 -0
- package/lib/typescript/commonjs/ui/components/Pill.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/ToggleRow.d.ts +33 -0
- package/lib/typescript/commonjs/ui/components/ToggleRow.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/ToolEmptyState.d.ts +113 -0
- package/lib/typescript/commonjs/ui/components/ToolEmptyState.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/ToolFooter.d.ts +69 -0
- package/lib/typescript/commonjs/ui/components/ToolFooter.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/WindowControls.d.ts +7 -1
- package/lib/typescript/commonjs/ui/components/WindowControls.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/index.d.ts +11 -0
- package/lib/typescript/commonjs/ui/components/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/index.d.ts +2 -0
- package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/listPresets.d.ts +28 -0
- package/lib/typescript/commonjs/ui/listPresets.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/night/primitives.d.ts +3 -1
- package/lib/typescript/commonjs/ui/night/primitives.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/otto/OttoSprite.d.ts +103 -0
- package/lib/typescript/commonjs/ui/otto/OttoSprite.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/otto/index.d.ts +19 -0
- package/lib/typescript/commonjs/ui/otto/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/otto/ottoEngine.d.ts +2205 -0
- package/lib/typescript/commonjs/ui/otto/ottoEngine.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/otto/ottoSheet.d.ts +73 -0
- package/lib/typescript/commonjs/ui/otto/ottoSheet.d.ts.map +1 -0
- package/lib/typescript/commonjs/utils/snapshotProviderKit.d.ts +81 -0
- package/lib/typescript/commonjs/utils/snapshotProviderKit.d.ts.map +1 -0
- package/lib/typescript/commonjs/utils/stateTools.d.ts +44 -0
- package/lib/typescript/commonjs/utils/stateTools.d.ts.map +1 -0
- package/lib/typescript/module/JsModal.d.ts.map +1 -1
- package/lib/typescript/module/hooks/index.d.ts +3 -1
- package/lib/typescript/module/hooks/index.d.ts.map +1 -1
- package/lib/typescript/module/hooks/safe-area-impl.d.ts +1 -1
- package/lib/typescript/module/hooks/useChangeLog.d.ts +38 -0
- package/lib/typescript/module/hooks/useChangeLog.d.ts.map +1 -0
- package/lib/typescript/module/hooks/useEventStoreEvents.d.ts +25 -0
- package/lib/typescript/module/hooks/useEventStoreEvents.d.ts.map +1 -0
- package/lib/typescript/module/hooks/usePersistedState.d.ts +34 -5
- package/lib/typescript/module/hooks/usePersistedState.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +6 -1
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/storage/definePersistedSetting.d.ts +39 -0
- package/lib/typescript/module/storage/definePersistedSetting.d.ts.map +1 -0
- package/lib/typescript/module/storage/devToolsStorageKeys.d.ts +12 -0
- package/lib/typescript/module/storage/devToolsStorageKeys.d.ts.map +1 -1
- package/lib/typescript/module/storage/index.d.ts +1 -0
- package/lib/typescript/module/storage/index.d.ts.map +1 -1
- package/lib/typescript/module/stores/ChangeLog.d.ts +100 -0
- package/lib/typescript/module/stores/ChangeLog.d.ts.map +1 -0
- package/lib/typescript/module/stores/createToolStore.d.ts +92 -0
- package/lib/typescript/module/stores/createToolStore.d.ts.map +1 -0
- package/lib/typescript/module/stores/index.d.ts +2 -0
- package/lib/typescript/module/stores/index.d.ts.map +1 -1
- package/lib/typescript/module/sync/toolSyncAdapter.d.ts +66 -0
- package/lib/typescript/module/sync/toolSyncAdapter.d.ts.map +1 -0
- package/lib/typescript/module/sync/wireForm.d.ts +100 -0
- package/lib/typescript/module/sync/wireForm.d.ts.map +1 -0
- package/lib/typescript/module/tools/defineTool.d.ts +100 -0
- package/lib/typescript/module/tools/defineTool.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/ChangeRow.d.ts +52 -0
- package/lib/typescript/module/ui/components/ChangeRow.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/ChipStrip.d.ts +41 -0
- package/lib/typescript/module/ui/components/ChipStrip.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/DetailViewToggle.d.ts +36 -0
- package/lib/typescript/module/ui/components/DetailViewToggle.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/DevToolsCard.d.ts +8 -1
- package/lib/typescript/module/ui/components/DevToolsCard.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/EmptyState.d.ts +14 -1
- package/lib/typescript/module/ui/components/EmptyState.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/EntityFilterView.d.ts +51 -0
- package/lib/typescript/module/ui/components/EntityFilterView.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/ExportPreview.d.ts +48 -0
- package/lib/typescript/module/ui/components/ExportPreview.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/InfoRow.d.ts +33 -0
- package/lib/typescript/module/ui/components/InfoRow.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/ModalHeader.d.ts +31 -1
- package/lib/typescript/module/ui/components/ModalHeader.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/Pill.d.ts +38 -0
- package/lib/typescript/module/ui/components/Pill.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/ToggleRow.d.ts +33 -0
- package/lib/typescript/module/ui/components/ToggleRow.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/ToolEmptyState.d.ts +113 -0
- package/lib/typescript/module/ui/components/ToolEmptyState.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/ToolFooter.d.ts +69 -0
- package/lib/typescript/module/ui/components/ToolFooter.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/WindowControls.d.ts +7 -1
- package/lib/typescript/module/ui/components/WindowControls.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/index.d.ts +11 -0
- package/lib/typescript/module/ui/components/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/index.d.ts +2 -0
- package/lib/typescript/module/ui/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/listPresets.d.ts +28 -0
- package/lib/typescript/module/ui/listPresets.d.ts.map +1 -0
- package/lib/typescript/module/ui/night/primitives.d.ts +3 -1
- package/lib/typescript/module/ui/night/primitives.d.ts.map +1 -1
- package/lib/typescript/module/ui/otto/OttoSprite.d.ts +103 -0
- package/lib/typescript/module/ui/otto/OttoSprite.d.ts.map +1 -0
- package/lib/typescript/module/ui/otto/index.d.ts +19 -0
- package/lib/typescript/module/ui/otto/index.d.ts.map +1 -0
- package/lib/typescript/module/ui/otto/ottoEngine.d.ts +2205 -0
- package/lib/typescript/module/ui/otto/ottoEngine.d.ts.map +1 -0
- package/lib/typescript/module/ui/otto/ottoSheet.d.ts +73 -0
- package/lib/typescript/module/ui/otto/ottoSheet.d.ts.map +1 -0
- package/lib/typescript/module/utils/snapshotProviderKit.d.ts +81 -0
- package/lib/typescript/module/utils/snapshotProviderKit.d.ts.map +1 -0
- package/lib/typescript/module/utils/stateTools.d.ts +44 -0
- package/lib/typescript/module/utils/stateTools.d.ts.map +1 -0
- 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
|
+
}
|
package/lib/module/JsModal.js
CHANGED
|
@@ -433,15 +433,19 @@ const JsModalComponent = ({
|
|
|
433
433
|
return () => clearTimeout(timeoutId);
|
|
434
434
|
}, [mode, panelHeight, dimensions, visible, persistenceKey, enablePersistence, isStateLoaded]);
|
|
435
435
|
|
|
436
|
-
//
|
|
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
|
-
|
|
439
|
-
|
|
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
|
|
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
|
-
|
|
727
|
-
|
|
728
|
-
|
|
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
|
-
|
|
908
|
-
|
|
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,
|
|
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";
|