@buoy-gg/shared-ui 7.0.15 → 7.0.17
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.web.js +1588 -0
- package/lib/commonjs/env/EnvironmentIndicator.web.js +114 -0
- package/lib/commonjs/env/EnvironmentSelectorInline.web.js +178 -0
- package/lib/commonjs/hooks/index.js +7 -0
- package/lib/commonjs/hooks/safe-area-impl.js +1 -1
- package/lib/commonjs/hooks/usePersistedState.js +71 -0
- package/lib/commonjs/icons/index.js +28 -0
- package/lib/commonjs/index.js +45 -3
- package/lib/commonjs/storage/devToolsStorageKeys.js +22 -0
- package/lib/commonjs/stores/ignoredPatternsCore.js +68 -0
- package/lib/commonjs/stores/ignoredPatternsStore.js +16 -52
- package/lib/commonjs/stores/ignoredPatternsStore.web.js +236 -0
- package/lib/commonjs/stores/index.js +1 -1
- package/lib/commonjs/ui/components/ModalHeader.js +69 -0
- package/lib/commonjs/ui/components/ToolButton.js +90 -0
- package/lib/commonjs/ui/components/index.js +7 -0
- package/lib/commonjs/ui/gameUI/constants/gameUIColors.js +5 -3
- package/lib/commonjs/ui/index.js +8 -0
- package/lib/commonjs/ui/toolTheme.js +96 -0
- package/lib/commonjs/utils/index.js +38 -0
- package/lib/commonjs/utils/stateSnapshotRegistry.js +62 -0
- package/lib/module/JsModal.web.js +1586 -0
- package/lib/module/env/EnvironmentIndicator.web.js +108 -0
- package/lib/module/env/EnvironmentSelectorInline.web.js +173 -0
- package/lib/module/hooks/index.js +1 -0
- package/lib/module/hooks/safe-area-impl.js +1 -1
- package/lib/module/hooks/usePersistedState.js +68 -0
- package/lib/module/icons/index.js +2 -0
- package/lib/module/index.js +7 -3
- package/lib/module/storage/devToolsStorageKeys.js +22 -0
- package/lib/module/stores/ignoredPatternsCore.js +62 -0
- package/lib/module/stores/ignoredPatternsStore.js +5 -51
- package/lib/module/stores/ignoredPatternsStore.web.js +221 -0
- package/lib/module/stores/index.js +1 -1
- package/lib/module/ui/components/ModalHeader.js +70 -2
- package/lib/module/ui/components/ToolButton.js +86 -0
- package/lib/module/ui/components/index.js +1 -0
- package/lib/module/ui/gameUI/constants/gameUIColors.js +4 -2
- package/lib/module/ui/index.js +3 -0
- package/lib/module/ui/toolTheme.js +93 -0
- package/lib/module/utils/index.js +2 -0
- package/lib/module/utils/stateSnapshotRegistry.js +55 -0
- package/lib/typescript/commonjs/JsModal.web.d.ts +138 -0
- package/lib/typescript/commonjs/JsModal.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/env/EnvironmentIndicator.web.d.ts +16 -0
- package/lib/typescript/commonjs/env/EnvironmentIndicator.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/env/EnvironmentSelectorInline.web.d.ts +8 -0
- package/lib/typescript/commonjs/env/EnvironmentSelectorInline.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/hooks/index.d.ts +1 -0
- 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/usePersistedState.d.ts +16 -0
- package/lib/typescript/commonjs/hooks/usePersistedState.d.ts.map +1 -0
- package/lib/typescript/commonjs/icons/index.d.ts +2 -0
- package/lib/typescript/commonjs/icons/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +1 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/storage/devToolsStorageKeys.d.ts +19 -0
- package/lib/typescript/commonjs/storage/devToolsStorageKeys.d.ts.map +1 -1
- package/lib/typescript/commonjs/stores/ignoredPatternsCore.d.ts +37 -0
- package/lib/typescript/commonjs/stores/ignoredPatternsCore.d.ts.map +1 -0
- package/lib/typescript/commonjs/stores/ignoredPatternsStore.d.ts +3 -27
- package/lib/typescript/commonjs/stores/ignoredPatternsStore.d.ts.map +1 -1
- package/lib/typescript/commonjs/stores/ignoredPatternsStore.web.d.ts +72 -0
- package/lib/typescript/commonjs/stores/ignoredPatternsStore.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/ModalHeader.d.ts +13 -1
- package/lib/typescript/commonjs/ui/components/ModalHeader.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/ToolButton.d.ts +24 -0
- package/lib/typescript/commonjs/ui/components/ToolButton.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/index.d.ts +4 -0
- package/lib/typescript/commonjs/ui/components/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/gameUI/constants/gameUIColors.d.ts +56 -0
- package/lib/typescript/commonjs/ui/gameUI/constants/gameUIColors.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/index.d.ts +1 -0
- package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/toolTheme.d.ts +68 -0
- package/lib/typescript/commonjs/ui/toolTheme.d.ts.map +1 -0
- package/lib/typescript/commonjs/utils/index.d.ts +2 -0
- package/lib/typescript/commonjs/utils/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/utils/stateSnapshotRegistry.d.ts +67 -0
- package/lib/typescript/commonjs/utils/stateSnapshotRegistry.d.ts.map +1 -0
- package/lib/typescript/module/JsModal.web.d.ts +138 -0
- package/lib/typescript/module/JsModal.web.d.ts.map +1 -0
- package/lib/typescript/module/env/EnvironmentIndicator.web.d.ts +16 -0
- package/lib/typescript/module/env/EnvironmentIndicator.web.d.ts.map +1 -0
- package/lib/typescript/module/env/EnvironmentSelectorInline.web.d.ts +8 -0
- package/lib/typescript/module/env/EnvironmentSelectorInline.web.d.ts.map +1 -0
- package/lib/typescript/module/hooks/index.d.ts +1 -0
- 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/usePersistedState.d.ts +16 -0
- package/lib/typescript/module/hooks/usePersistedState.d.ts.map +1 -0
- package/lib/typescript/module/icons/index.d.ts +2 -0
- package/lib/typescript/module/icons/index.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +1 -1
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/storage/devToolsStorageKeys.d.ts +19 -0
- package/lib/typescript/module/storage/devToolsStorageKeys.d.ts.map +1 -1
- package/lib/typescript/module/stores/ignoredPatternsCore.d.ts +37 -0
- package/lib/typescript/module/stores/ignoredPatternsCore.d.ts.map +1 -0
- package/lib/typescript/module/stores/ignoredPatternsStore.d.ts +3 -27
- package/lib/typescript/module/stores/ignoredPatternsStore.d.ts.map +1 -1
- package/lib/typescript/module/stores/ignoredPatternsStore.web.d.ts +72 -0
- package/lib/typescript/module/stores/ignoredPatternsStore.web.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/ModalHeader.d.ts +13 -1
- package/lib/typescript/module/ui/components/ModalHeader.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/ToolButton.d.ts +24 -0
- package/lib/typescript/module/ui/components/ToolButton.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/index.d.ts +4 -0
- package/lib/typescript/module/ui/components/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/gameUI/constants/gameUIColors.d.ts +56 -0
- package/lib/typescript/module/ui/gameUI/constants/gameUIColors.d.ts.map +1 -1
- package/lib/typescript/module/ui/index.d.ts +1 -0
- package/lib/typescript/module/ui/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/toolTheme.d.ts +68 -0
- package/lib/typescript/module/ui/toolTheme.d.ts.map +1 -0
- package/lib/typescript/module/utils/index.d.ts +2 -0
- package/lib/typescript/module/utils/index.d.ts.map +1 -1
- package/lib/typescript/module/utils/stateSnapshotRegistry.d.ts +67 -0
- package/lib/typescript/module/utils/stateSnapshotRegistry.d.ts.map +1 -0
- package/package.json +4 -4
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ignoredPatternsStore.web — web twin of ignoredPatternsStore.ts (the shared
|
|
5
|
+
* persisted "ignored patterns" singleton for network-style URL filtering).
|
|
6
|
+
*
|
|
7
|
+
* The native store imports shared's RN `persistentStorage` (whose module graph
|
|
8
|
+
* carries guarded expo-file-system / async-storage requires), which is why the
|
|
9
|
+
* web shims never re-exported it. This twin keeps the ENTIRE store class, hook
|
|
10
|
+
* and persistence contract identical — same class shape, same
|
|
11
|
+
* `devToolsStorageKeys.network.ignoredDomains()` storage key, same persisted
|
|
12
|
+
* JSON shape (`IgnoredPattern[]`, with the legacy `string[]` migration) — and
|
|
13
|
+
* swaps only the storage backing: a file-local localStorage adapter that
|
|
14
|
+
* matches the `persistentStorage` API (async getItem/setItem, best-effort,
|
|
15
|
+
* provenance: example-tauri/src/shims/shared-ui.web.ts's adapter — web Buoy
|
|
16
|
+
* chrome persistence lives in webview localStorage).
|
|
17
|
+
*
|
|
18
|
+
* Everything else — pure matching (`ignoredPatternsCore`), the migration +
|
|
19
|
+
* ensure-defaults logic, the dirty-flag load race guard, the module-scope
|
|
20
|
+
* stable mutation identities — is the native code, not a fork of its
|
|
21
|
+
* semantics. Web consumers get the same shared, persisted pattern list the
|
|
22
|
+
* phone has, keyed identically, so a DOM port and a react-native-web port of
|
|
23
|
+
* the same app read/write the same entry.
|
|
24
|
+
*
|
|
25
|
+
* @platform React DOM Web
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { useEffect, useMemo, useState } from "react";
|
|
29
|
+
import { devToolsStorageKeys } from "../storage/devToolsStorageKeys.js";
|
|
30
|
+
|
|
31
|
+
// Pure matching types/helpers live in ignoredPatternsCore.ts (web-shim-safe);
|
|
32
|
+
// re-exported here so this module's surface twins the native store's.
|
|
33
|
+
import { DEFAULT_IGNORED_PATTERNS } from "./ignoredPatternsCore.js";
|
|
34
|
+
export { urlMatchesIgnoredPattern, isUrlIgnored } from "./ignoredPatternsCore.js";
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* localStorage-backed implementation of the shared `persistentStorage` API
|
|
38
|
+
* (getItem/setItem returning promises, silent best-effort failure) — the same
|
|
39
|
+
* adapter recipe the web shared-ui shims use for Buoy chrome persistence.
|
|
40
|
+
* Local to this file so the web store pulls zero RN/expo modules.
|
|
41
|
+
*/
|
|
42
|
+
const persistentStorage = {
|
|
43
|
+
async getItem(key) {
|
|
44
|
+
try {
|
|
45
|
+
return window.localStorage.getItem(key);
|
|
46
|
+
} catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
async setItem(key, value) {
|
|
51
|
+
try {
|
|
52
|
+
window.localStorage.setItem(key, value);
|
|
53
|
+
} catch {
|
|
54
|
+
// Ignore quota/availability errors — callers treat this as best-effort.
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const DEFAULT_PATTERNS = DEFAULT_IGNORED_PATTERNS;
|
|
59
|
+
|
|
60
|
+
/** Tolerate the legacy `string[]` persisted format and stray junk. */
|
|
61
|
+
function migratePatterns(raw) {
|
|
62
|
+
if (!Array.isArray(raw)) return [];
|
|
63
|
+
const migrated = [];
|
|
64
|
+
for (const entry of raw) {
|
|
65
|
+
if (typeof entry === "string" && entry.trim()) {
|
|
66
|
+
migrated.push({
|
|
67
|
+
value: entry,
|
|
68
|
+
mode: "contains"
|
|
69
|
+
});
|
|
70
|
+
} else if (entry && typeof entry === "object" && typeof entry.value === "string") {
|
|
71
|
+
const e = entry;
|
|
72
|
+
migrated.push({
|
|
73
|
+
value: e.value,
|
|
74
|
+
mode: e.mode === "exact" ? "exact" : "contains"
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return migrated;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Always keep the default patterns present (e.g. Buoy's license API). */
|
|
82
|
+
function ensureDefaults(patterns) {
|
|
83
|
+
const result = [...patterns];
|
|
84
|
+
for (const def of DEFAULT_PATTERNS) {
|
|
85
|
+
if (!result.some(p => p.value === def.value)) {
|
|
86
|
+
result.push({
|
|
87
|
+
...def
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Singleton store. One instance per JS runtime, so every consumer (Network
|
|
95
|
+
* tool, Events tool, any web panel) shares the same patterns.
|
|
96
|
+
*/
|
|
97
|
+
class IgnoredPatternsStore {
|
|
98
|
+
patterns = ensureDefaults(DEFAULT_PATTERNS.map(p => ({
|
|
99
|
+
...p
|
|
100
|
+
})));
|
|
101
|
+
listeners = new Set();
|
|
102
|
+
loaded = false;
|
|
103
|
+
loadPromise = null;
|
|
104
|
+
/** Set once a mutation happens so a slow initial load can't clobber it. */
|
|
105
|
+
dirty = false;
|
|
106
|
+
|
|
107
|
+
/** Current patterns (stable reference until a mutation occurs). */
|
|
108
|
+
getPatterns() {
|
|
109
|
+
return this.patterns;
|
|
110
|
+
}
|
|
111
|
+
subscribe(listener) {
|
|
112
|
+
this.listeners.add(listener);
|
|
113
|
+
// Lazily hydrate from storage the first time anyone cares.
|
|
114
|
+
void this.ensureLoaded();
|
|
115
|
+
return () => {
|
|
116
|
+
this.listeners.delete(listener);
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
emit() {
|
|
120
|
+
this.listeners.forEach(l => l());
|
|
121
|
+
}
|
|
122
|
+
ensureLoaded() {
|
|
123
|
+
if (this.loaded) return Promise.resolve();
|
|
124
|
+
if (this.loadPromise) return this.loadPromise;
|
|
125
|
+
this.loadPromise = (async () => {
|
|
126
|
+
try {
|
|
127
|
+
const stored = await persistentStorage.getItem(devToolsStorageKeys.network.ignoredDomains());
|
|
128
|
+
// A mutation may have raced this read — never overwrite user intent.
|
|
129
|
+
if (stored && !this.dirty) {
|
|
130
|
+
const migrated = ensureDefaults(migratePatterns(JSON.parse(stored)));
|
|
131
|
+
this.patterns = migrated;
|
|
132
|
+
}
|
|
133
|
+
} catch {
|
|
134
|
+
// Silently fall back to defaults.
|
|
135
|
+
} finally {
|
|
136
|
+
this.loaded = true;
|
|
137
|
+
this.emit();
|
|
138
|
+
}
|
|
139
|
+
})();
|
|
140
|
+
return this.loadPromise;
|
|
141
|
+
}
|
|
142
|
+
persist() {
|
|
143
|
+
persistentStorage.setItem(devToolsStorageKeys.network.ignoredDomains(), JSON.stringify(this.patterns)).catch(() => {
|
|
144
|
+
// Silently fail — patterns remain in memory.
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
commit(next) {
|
|
148
|
+
this.dirty = true;
|
|
149
|
+
this.patterns = next;
|
|
150
|
+
this.emit();
|
|
151
|
+
this.persist();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Add a pattern (no-op if its value already exists). */
|
|
155
|
+
add(pattern) {
|
|
156
|
+
const value = pattern.value.trim();
|
|
157
|
+
if (!value) return;
|
|
158
|
+
if (this.patterns.some(p => p.value === value)) return;
|
|
159
|
+
this.commit([...this.patterns, {
|
|
160
|
+
value,
|
|
161
|
+
mode: pattern.mode
|
|
162
|
+
}]);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** Remove a pattern by value. */
|
|
166
|
+
remove(value) {
|
|
167
|
+
if (!this.patterns.some(p => p.value === value)) return;
|
|
168
|
+
this.commit(this.patterns.filter(p => p.value !== value));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Add as `contains` if absent, otherwise remove (used by detail-view chips). */
|
|
172
|
+
toggle(value) {
|
|
173
|
+
const trimmed = value.trim();
|
|
174
|
+
if (!trimmed) return;
|
|
175
|
+
this.commit(this.patterns.some(p => p.value === trimmed) ? this.patterns.filter(p => p.value !== trimmed) : [...this.patterns, {
|
|
176
|
+
value: trimmed,
|
|
177
|
+
mode: "contains"
|
|
178
|
+
}]);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** Flip an existing pattern between `contains` and `exact`. */
|
|
182
|
+
toggleMode(value) {
|
|
183
|
+
if (!this.patterns.some(p => p.value === value)) return;
|
|
184
|
+
this.commit(this.patterns.map(p => p.value === value ? {
|
|
185
|
+
...p,
|
|
186
|
+
mode: p.mode === "contains" ? "exact" : "contains"
|
|
187
|
+
} : p));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
export const ignoredPatternsStore = new IgnoredPatternsStore();
|
|
191
|
+
/**
|
|
192
|
+
* Mutations live at module scope so they keep a STABLE identity across
|
|
193
|
+
* renders (same rationale as native: inline arrows handed every consumer a
|
|
194
|
+
* fresh `toggle` per render and defeated `memo` downstream).
|
|
195
|
+
*/
|
|
196
|
+
const addPattern = pattern => ignoredPatternsStore.add(pattern);
|
|
197
|
+
const removePattern = value => ignoredPatternsStore.remove(value);
|
|
198
|
+
const togglePattern = value => ignoredPatternsStore.toggle(value);
|
|
199
|
+
const togglePatternMode = value => ignoredPatternsStore.toggleMode(value);
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Subscribe to the shared ignored-patterns store. All consumers in a runtime
|
|
203
|
+
* see the same patterns and the same mutations.
|
|
204
|
+
*/
|
|
205
|
+
export function useIgnoredPatterns() {
|
|
206
|
+
const [patterns, setPatterns] = useState(() => ignoredPatternsStore.getPatterns());
|
|
207
|
+
useEffect(() => {
|
|
208
|
+
return ignoredPatternsStore.subscribe(() => {
|
|
209
|
+
setPatterns(ignoredPatternsStore.getPatterns());
|
|
210
|
+
});
|
|
211
|
+
}, []);
|
|
212
|
+
const values = useMemo(() => new Set(patterns.map(p => p.value)), [patterns]);
|
|
213
|
+
return {
|
|
214
|
+
patterns,
|
|
215
|
+
values,
|
|
216
|
+
add: addPattern,
|
|
217
|
+
remove: removePattern,
|
|
218
|
+
toggle: togglePattern,
|
|
219
|
+
toggleMode: togglePatternMode
|
|
220
|
+
};
|
|
221
|
+
}
|
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
export { BaseEventStore } from "./BaseEventStore.js";
|
|
8
|
-
export { ignoredPatternsStore, useIgnoredPatterns, urlMatchesIgnoredPattern, isUrlIgnored } from "./ignoredPatternsStore
|
|
8
|
+
export { ignoredPatternsStore, useIgnoredPatterns, urlMatchesIgnoredPattern, isUrlIgnored } from "./ignoredPatternsStore";
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import { View, Text, TouchableOpacity, StyleSheet } from "react-native";
|
|
4
|
-
import {
|
|
3
|
+
import { View, Text, TextInput, TouchableOpacity, StyleSheet } from "react-native";
|
|
4
|
+
import { useEffect, useRef } from "react";
|
|
5
|
+
import { ChevronLeft, Search as SearchIcon, X } from "../../icons/index.js";
|
|
5
6
|
import { buoyColors } from "../gameUI/constants/gameUIColors.js";
|
|
6
7
|
|
|
7
8
|
// Base ModalHeader container component
|
|
@@ -111,10 +112,51 @@ export function Actions({
|
|
|
111
112
|
}
|
|
112
113
|
Actions.displayName = "ModalHeaderActions";
|
|
113
114
|
|
|
115
|
+
// Expanding search input for the header. Render this IN PLACE OF
|
|
116
|
+
// `ModalHeader.Content` while search is active — it fills the same flex slot.
|
|
117
|
+
// Extracts the idiom env/network/storage each hand-rolled (~50 lines apiece).
|
|
118
|
+
|
|
119
|
+
export function HeaderSearch({
|
|
120
|
+
value,
|
|
121
|
+
onChangeText,
|
|
122
|
+
onCancel,
|
|
123
|
+
placeholder = "Search...",
|
|
124
|
+
autoFocus = true
|
|
125
|
+
}) {
|
|
126
|
+
const inputRef = useRef(null);
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
if (autoFocus) inputRef.current?.focus();
|
|
129
|
+
}, [autoFocus]);
|
|
130
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
131
|
+
style: styles.searchContainer,
|
|
132
|
+
children: [/*#__PURE__*/_jsx(SearchIcon, {
|
|
133
|
+
size: 12,
|
|
134
|
+
color: buoyColors.textSecondary
|
|
135
|
+
}), /*#__PURE__*/_jsx(TextInput, {
|
|
136
|
+
ref: inputRef,
|
|
137
|
+
style: styles.searchInput,
|
|
138
|
+
placeholder: placeholder,
|
|
139
|
+
placeholderTextColor: buoyColors.textMuted,
|
|
140
|
+
value: value,
|
|
141
|
+
onChangeText: onChangeText,
|
|
142
|
+
autoCorrect: false,
|
|
143
|
+
autoCapitalize: "none"
|
|
144
|
+
}), /*#__PURE__*/_jsx(TouchableOpacity, {
|
|
145
|
+
onPress: onCancel,
|
|
146
|
+
style: styles.searchCancelButton,
|
|
147
|
+
children: /*#__PURE__*/_jsx(X, {
|
|
148
|
+
size: 12,
|
|
149
|
+
color: buoyColors.textSecondary
|
|
150
|
+
})
|
|
151
|
+
})]
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
114
155
|
// Attach sub-components to the main component
|
|
115
156
|
ModalHeader.Navigation = Navigation;
|
|
116
157
|
ModalHeader.Content = Content;
|
|
117
158
|
ModalHeader.Actions = Actions;
|
|
159
|
+
ModalHeader.Search = HeaderSearch;
|
|
118
160
|
const styles = StyleSheet.create({
|
|
119
161
|
headerContainer: {
|
|
120
162
|
flexDirection: "row",
|
|
@@ -162,5 +204,31 @@ const styles = StyleSheet.create({
|
|
|
162
204
|
},
|
|
163
205
|
headerContentNoMargin: {
|
|
164
206
|
marginHorizontal: 0
|
|
207
|
+
},
|
|
208
|
+
searchContainer: {
|
|
209
|
+
flexDirection: "row",
|
|
210
|
+
alignItems: "center",
|
|
211
|
+
flex: 1,
|
|
212
|
+
backgroundColor: buoyColors.input,
|
|
213
|
+
borderRadius: 6,
|
|
214
|
+
paddingHorizontal: 10,
|
|
215
|
+
paddingVertical: 4,
|
|
216
|
+
marginHorizontal: 12,
|
|
217
|
+
marginVertical: 4,
|
|
218
|
+
height: 32,
|
|
219
|
+
borderWidth: 1,
|
|
220
|
+
borderColor: buoyColors.border
|
|
221
|
+
},
|
|
222
|
+
searchInput: {
|
|
223
|
+
flex: 1,
|
|
224
|
+
marginLeft: 8,
|
|
225
|
+
fontSize: 13,
|
|
226
|
+
color: buoyColors.text,
|
|
227
|
+
padding: 0,
|
|
228
|
+
height: "100%"
|
|
229
|
+
},
|
|
230
|
+
searchCancelButton: {
|
|
231
|
+
padding: 4,
|
|
232
|
+
marginLeft: 4
|
|
165
233
|
}
|
|
166
234
|
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { StyleSheet, Text, TouchableOpacity } from "react-native";
|
|
4
|
+
import { buoyColors, gameUIColors } from "../gameUI/constants/gameUIColors.js";
|
|
5
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
6
|
+
/**
|
|
7
|
+
* The shared action button for dev-tool surfaces. Fills the gap where every
|
|
8
|
+
* tool hand-styled its own TouchableOpacity for "Clear", "Apply", "Reset".
|
|
9
|
+
*/
|
|
10
|
+
export function ToolButton({
|
|
11
|
+
label,
|
|
12
|
+
onPress,
|
|
13
|
+
variant = "primary",
|
|
14
|
+
size = "medium",
|
|
15
|
+
disabled = false,
|
|
16
|
+
icon,
|
|
17
|
+
style
|
|
18
|
+
}) {
|
|
19
|
+
const palette = VARIANTS[variant];
|
|
20
|
+
return /*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
21
|
+
accessibilityRole: "button",
|
|
22
|
+
accessibilityLabel: label,
|
|
23
|
+
onPress: onPress,
|
|
24
|
+
disabled: disabled,
|
|
25
|
+
style: [styles.base, size === "small" ? styles.small : styles.medium, {
|
|
26
|
+
backgroundColor: palette.background,
|
|
27
|
+
borderColor: palette.border,
|
|
28
|
+
borderWidth: variant === "ghost" ? 0 : 1
|
|
29
|
+
}, disabled && styles.disabled, style],
|
|
30
|
+
children: [icon, /*#__PURE__*/_jsx(Text, {
|
|
31
|
+
style: [styles.label, size === "small" && styles.labelSmall, {
|
|
32
|
+
color: palette.text
|
|
33
|
+
}],
|
|
34
|
+
children: label
|
|
35
|
+
})]
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
const VARIANTS = {
|
|
39
|
+
primary: {
|
|
40
|
+
background: buoyColors.primary + "22",
|
|
41
|
+
border: buoyColors.primary + "66",
|
|
42
|
+
text: buoyColors.primary
|
|
43
|
+
},
|
|
44
|
+
danger: {
|
|
45
|
+
background: gameUIColors.error + "15",
|
|
46
|
+
border: gameUIColors.error + "60",
|
|
47
|
+
text: gameUIColors.error
|
|
48
|
+
},
|
|
49
|
+
warning: {
|
|
50
|
+
background: gameUIColors.warning + "15",
|
|
51
|
+
border: gameUIColors.warning + "60",
|
|
52
|
+
text: gameUIColors.warning
|
|
53
|
+
},
|
|
54
|
+
ghost: {
|
|
55
|
+
background: "transparent",
|
|
56
|
+
border: "transparent",
|
|
57
|
+
text: buoyColors.textSecondary
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const styles = StyleSheet.create({
|
|
61
|
+
base: {
|
|
62
|
+
flexDirection: "row",
|
|
63
|
+
alignItems: "center",
|
|
64
|
+
justifyContent: "center",
|
|
65
|
+
gap: 6,
|
|
66
|
+
borderRadius: 8
|
|
67
|
+
},
|
|
68
|
+
medium: {
|
|
69
|
+
paddingVertical: 8,
|
|
70
|
+
paddingHorizontal: 14
|
|
71
|
+
},
|
|
72
|
+
small: {
|
|
73
|
+
paddingVertical: 5,
|
|
74
|
+
paddingHorizontal: 10
|
|
75
|
+
},
|
|
76
|
+
disabled: {
|
|
77
|
+
opacity: 0.4
|
|
78
|
+
},
|
|
79
|
+
label: {
|
|
80
|
+
fontSize: 12,
|
|
81
|
+
fontWeight: "600"
|
|
82
|
+
},
|
|
83
|
+
labelSmall: {
|
|
84
|
+
fontSize: 11
|
|
85
|
+
}
|
|
86
|
+
});
|
|
@@ -13,6 +13,7 @@ export { FilterSection, FilterBadge, AddFilterInput, AddFilterButton, FilterList
|
|
|
13
13
|
export { CompactFilterChips } from "./CompactFilterChips.js";
|
|
14
14
|
export { Badge, StatusBadge, CountBadge, TypeBadge, MethodBadge } from "./Badge.js";
|
|
15
15
|
export { TabSelector } from "./TabSelector.js";
|
|
16
|
+
export { ToolButton } from "./ToolButton.js";
|
|
16
17
|
export { EventListItem } from "./EventListItem.js";
|
|
17
18
|
export { StatsCard } from "./StatsCard.js";
|
|
18
19
|
export { CompactRow } from "./CompactRow.js";
|
|
@@ -15,7 +15,9 @@ import { macOSGameUIColors, refreshMacOSGameUIColors } from "./macOSDesignSystem
|
|
|
15
15
|
// THEME DEFINITIONS
|
|
16
16
|
// ============================================
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
// Kept (and exported) as the reference palette for theme swapping — see the
|
|
19
|
+
// header comment. Unused by the active theme selection.
|
|
20
|
+
export const _defaultTheme = {
|
|
19
21
|
// Base colors
|
|
20
22
|
border: "#00B8E666",
|
|
21
23
|
blackTint1: "rgba(8, 12, 21, 0.95)",
|
|
@@ -119,7 +121,7 @@ const macOSTheme = macOSGameUIColors;
|
|
|
119
121
|
// THEME SELECTION - Just change this one line!
|
|
120
122
|
// ============================================
|
|
121
123
|
|
|
122
|
-
// const activeTheme =
|
|
124
|
+
// const activeTheme = _defaultTheme; // DEFAULT - Mixed colors (original)
|
|
123
125
|
const activeTheme = macOSTheme; // macOS - Apple HIG design system
|
|
124
126
|
|
|
125
127
|
// ============================================
|
package/lib/module/ui/index.js
CHANGED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { buoyColors, gameUIColors } from "./gameUI/constants/gameUIColors.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Semantic design tokens for dev-tool surfaces — the single documented entry
|
|
7
|
+
* point for custom-tool authors (and, over time, internal tools).
|
|
8
|
+
*
|
|
9
|
+
* Buoy historically grew three color systems (`buoyColors`, `gameUIColors`,
|
|
10
|
+
* `macOSColors`) with tribal knowledge about which to use where. `toolTheme`
|
|
11
|
+
* encodes that knowledge: surfaces/text come from the brand palette, semantic
|
|
12
|
+
* status colors from the gameUI palette. Consumers should not need to know
|
|
13
|
+
* that split exists.
|
|
14
|
+
*/
|
|
15
|
+
export const toolTheme = {
|
|
16
|
+
colors: {
|
|
17
|
+
/** Modal/page background. */
|
|
18
|
+
surface: buoyColors.base,
|
|
19
|
+
/** Card / row background, one step above surface. */
|
|
20
|
+
card: buoyColors.card,
|
|
21
|
+
/** Pressed/hover background. */
|
|
22
|
+
hover: buoyColors.hover,
|
|
23
|
+
/** Text-input background. */
|
|
24
|
+
input: buoyColors.input,
|
|
25
|
+
border: buoyColors.border,
|
|
26
|
+
borderStrong: buoyColors.borderStrong,
|
|
27
|
+
text: buoyColors.text,
|
|
28
|
+
textSecondary: buoyColors.textSecondary,
|
|
29
|
+
textMuted: buoyColors.textMuted,
|
|
30
|
+
/** Buoy teal — primary accent. */
|
|
31
|
+
accent: buoyColors.primary,
|
|
32
|
+
success: gameUIColors.success,
|
|
33
|
+
warning: gameUIColors.warning,
|
|
34
|
+
error: gameUIColors.error,
|
|
35
|
+
info: gameUIColors.info
|
|
36
|
+
},
|
|
37
|
+
/**
|
|
38
|
+
* Text presets matching the styles internal tools re-declare per file.
|
|
39
|
+
* Spread into a style: `style={[toolTheme.text.label, { color: ... }]}`.
|
|
40
|
+
*/
|
|
41
|
+
text: {
|
|
42
|
+
/** Modal/section titles. */
|
|
43
|
+
title: {
|
|
44
|
+
fontSize: 14,
|
|
45
|
+
fontWeight: "600",
|
|
46
|
+
color: buoyColors.text
|
|
47
|
+
},
|
|
48
|
+
/** Default body copy. */
|
|
49
|
+
body: {
|
|
50
|
+
fontSize: 13,
|
|
51
|
+
color: buoyColors.text
|
|
52
|
+
},
|
|
53
|
+
/** Secondary copy under a primary line. */
|
|
54
|
+
subtitle: {
|
|
55
|
+
fontSize: 12,
|
|
56
|
+
color: buoyColors.textSecondary
|
|
57
|
+
},
|
|
58
|
+
/** ALL-CAPS section labels ("LOCAL OVERRIDES"). */
|
|
59
|
+
label: {
|
|
60
|
+
fontSize: 10,
|
|
61
|
+
fontWeight: "700",
|
|
62
|
+
color: buoyColors.textMuted,
|
|
63
|
+
letterSpacing: 1.2,
|
|
64
|
+
fontFamily: "monospace"
|
|
65
|
+
},
|
|
66
|
+
/** Monospace values (keys, ids, payload snippets). */
|
|
67
|
+
mono: {
|
|
68
|
+
fontSize: 11,
|
|
69
|
+
color: buoyColors.textSecondary,
|
|
70
|
+
fontFamily: "monospace"
|
|
71
|
+
},
|
|
72
|
+
/** Timestamps and fine print. */
|
|
73
|
+
small: {
|
|
74
|
+
fontSize: 9,
|
|
75
|
+
color: buoyColors.textMuted,
|
|
76
|
+
fontFamily: "monospace"
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
/** 4-based spacing scale used across tool surfaces. */
|
|
80
|
+
spacing: {
|
|
81
|
+
xs: 4,
|
|
82
|
+
sm: 8,
|
|
83
|
+
md: 12,
|
|
84
|
+
lg: 16,
|
|
85
|
+
xl: 24
|
|
86
|
+
},
|
|
87
|
+
radius: {
|
|
88
|
+
sm: 6,
|
|
89
|
+
md: 8,
|
|
90
|
+
lg: 12,
|
|
91
|
+
pill: 9999
|
|
92
|
+
}
|
|
93
|
+
};
|
|
@@ -9,6 +9,8 @@ export { getValueType, isPrimitive, isJsonSerializable, isValidJson, getConstruc
|
|
|
9
9
|
export { parseValue, formatValue, getTypeColor, truncateText, flattenObject, formatPath } from "./valueFormatting.js";
|
|
10
10
|
export { loadOptionalModule, getCachedOptionalModule } from "./loadOptionalModule.js";
|
|
11
11
|
export { setRenderCaptureProvider, getRenderCaptureProvider } from "./renderCaptureRegistry.js";
|
|
12
|
+
export { registerStateSnapshotProvider, getStateSnapshotProviders, getStateSnapshotProvider, subscribeToStateSnapshotProviders } from "./stateSnapshotRegistry.js";
|
|
13
|
+
export { serialize as superJsonSerialize, deserialize as superJsonDeserialize } from "./minimalSuperJSON.js";
|
|
12
14
|
export { reloadApp, scheduleReloadApp, resolveReloadStrategy, REMOTE_RELOAD_DELAY_MS } from "./reloadApp.js";
|
|
13
15
|
export { Subscribable } from "./subscribable.js";
|
|
14
16
|
export { subscriberCountNotifier, subscribeToSubscriberCountChanges, notifySubscriberCountChange } from "./subscriberCountNotifier.js";
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Cross-package bridge for app-state snapshot & restore (Time Machine).
|
|
5
|
+
*
|
|
6
|
+
* Each state tool (`@buoy-gg/storage`, `@buoy-gg/redux`, `@buoy-gg/zustand`,
|
|
7
|
+
* `@buoy-gg/jotai`, `@buoy-gg/react-query`) registers a provider here at
|
|
8
|
+
* module init (or from its app-lifetime sync hook); `@buoy-gg/time-machine`
|
|
9
|
+
* consumes the registry. Neither side imports the other — same
|
|
10
|
+
* optional-capability pattern as renderCaptureRegistry.
|
|
11
|
+
*
|
|
12
|
+
* Apps can also register custom providers for state Buoy doesn't know about.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const providers = new Map();
|
|
16
|
+
const listeners = new Set();
|
|
17
|
+
function notify() {
|
|
18
|
+
listeners.forEach(l => {
|
|
19
|
+
try {
|
|
20
|
+
l();
|
|
21
|
+
} catch {
|
|
22
|
+
// listener errors must not break registration
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Register a provider. Last registration wins for a given id.
|
|
29
|
+
* Returns an unregister function (only removes the provider if it is still
|
|
30
|
+
* the registered one for that id).
|
|
31
|
+
*/
|
|
32
|
+
export function registerStateSnapshotProvider(provider) {
|
|
33
|
+
providers.set(provider.id, provider);
|
|
34
|
+
notify();
|
|
35
|
+
return () => {
|
|
36
|
+
if (providers.get(provider.id) === provider) {
|
|
37
|
+
providers.delete(provider.id);
|
|
38
|
+
notify();
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export function getStateSnapshotProviders() {
|
|
43
|
+
return Array.from(providers.values());
|
|
44
|
+
}
|
|
45
|
+
export function getStateSnapshotProvider(id) {
|
|
46
|
+
return providers.get(id) ?? null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Notifies when providers register/unregister (query registers late). */
|
|
50
|
+
export function subscribeToStateSnapshotProviders(listener) {
|
|
51
|
+
listeners.add(listener);
|
|
52
|
+
return () => {
|
|
53
|
+
listeners.delete(listener);
|
|
54
|
+
};
|
|
55
|
+
}
|