@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,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* EnvironmentIndicator — Web (React DOM) twin of EnvironmentIndicator.tsx.
|
|
5
|
+
* Same env→color/label map and layout; lucide glyph icons are omitted from
|
|
6
|
+
* the badge on native too (dot + label only), so parity holds.
|
|
7
|
+
*/
|
|
8
|
+
import { gameUIColors } from "../ui/gameUI/constants/gameUIColors.js";
|
|
9
|
+
import { normalizeEnvironment } from "../types/types.js";
|
|
10
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
11
|
+
export function getEnvironmentConfig(environment) {
|
|
12
|
+
switch (normalizeEnvironment(String(environment))) {
|
|
13
|
+
case 'dev':
|
|
14
|
+
return {
|
|
15
|
+
label: 'DEV',
|
|
16
|
+
backgroundColor: gameUIColors.warning
|
|
17
|
+
};
|
|
18
|
+
case 'qa':
|
|
19
|
+
return {
|
|
20
|
+
label: 'QA',
|
|
21
|
+
backgroundColor: gameUIColors.optional
|
|
22
|
+
};
|
|
23
|
+
case 'staging':
|
|
24
|
+
return {
|
|
25
|
+
label: 'STAGING',
|
|
26
|
+
backgroundColor: gameUIColors.success
|
|
27
|
+
};
|
|
28
|
+
case 'prod':
|
|
29
|
+
return {
|
|
30
|
+
label: 'PROD',
|
|
31
|
+
backgroundColor: gameUIColors.error
|
|
32
|
+
};
|
|
33
|
+
case 'local':
|
|
34
|
+
default:
|
|
35
|
+
return {
|
|
36
|
+
label: 'LOCAL',
|
|
37
|
+
backgroundColor: gameUIColors.info
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function ChevronDownGlyph({
|
|
42
|
+
color
|
|
43
|
+
}) {
|
|
44
|
+
// native: ChevronDown size 12 from shared lucide icons
|
|
45
|
+
return /*#__PURE__*/_jsx("svg", {
|
|
46
|
+
width: 12,
|
|
47
|
+
height: 12,
|
|
48
|
+
viewBox: "0 0 24 24",
|
|
49
|
+
"aria-hidden": "true",
|
|
50
|
+
children: /*#__PURE__*/_jsx("path", {
|
|
51
|
+
d: "M6 9l6 6 6-6",
|
|
52
|
+
fill: "none",
|
|
53
|
+
stroke: color,
|
|
54
|
+
strokeWidth: "2",
|
|
55
|
+
strokeLinecap: "round",
|
|
56
|
+
strokeLinejoin: "round"
|
|
57
|
+
})
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
export function EnvironmentIndicator({
|
|
61
|
+
environment,
|
|
62
|
+
interactive = false,
|
|
63
|
+
onPress
|
|
64
|
+
}) {
|
|
65
|
+
const envConfig = getEnvironmentConfig(environment);
|
|
66
|
+
return /*#__PURE__*/_jsxs("button", {
|
|
67
|
+
onClick: interactive ? onPress : undefined,
|
|
68
|
+
"aria-label": `Environment: ${envConfig.label}`,
|
|
69
|
+
style: {
|
|
70
|
+
display: 'flex',
|
|
71
|
+
flexDirection: 'row',
|
|
72
|
+
alignItems: 'center',
|
|
73
|
+
// native: paddingVertical 6, paddingLeft 8, paddingRight interactive?4:8
|
|
74
|
+
padding: `6px ${interactive ? 4 : 8}px 6px 8px`,
|
|
75
|
+
flexShrink: 0,
|
|
76
|
+
background: 'none',
|
|
77
|
+
border: 'none',
|
|
78
|
+
cursor: interactive ? 'pointer' : 'default'
|
|
79
|
+
},
|
|
80
|
+
children: [/*#__PURE__*/_jsx("div", {
|
|
81
|
+
style: {
|
|
82
|
+
width: 8,
|
|
83
|
+
height: 8,
|
|
84
|
+
borderRadius: 4,
|
|
85
|
+
backgroundColor: envConfig.backgroundColor,
|
|
86
|
+
marginRight: 6,
|
|
87
|
+
// native: shadowOpacity 0.6, shadowRadius 4 glow
|
|
88
|
+
boxShadow: `0 0 4px ${envConfig.backgroundColor}99`
|
|
89
|
+
}
|
|
90
|
+
}), /*#__PURE__*/_jsx("span", {
|
|
91
|
+
style: {
|
|
92
|
+
fontSize: 11,
|
|
93
|
+
fontWeight: 600,
|
|
94
|
+
color: gameUIColors.primaryLight,
|
|
95
|
+
letterSpacing: 0.5
|
|
96
|
+
},
|
|
97
|
+
children: envConfig.label
|
|
98
|
+
}), interactive && /*#__PURE__*/_jsx("span", {
|
|
99
|
+
style: {
|
|
100
|
+
marginLeft: 2,
|
|
101
|
+
display: 'flex'
|
|
102
|
+
},
|
|
103
|
+
children: /*#__PURE__*/_jsx(ChevronDownGlyph, {
|
|
104
|
+
color: "#7A8599"
|
|
105
|
+
})
|
|
106
|
+
})]
|
|
107
|
+
});
|
|
108
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* EnvironmentSelectorInline — Web (React DOM) twin of
|
|
5
|
+
* EnvironmentSelectorInline.tsx: the interactive environment badge that
|
|
6
|
+
* expands a drop-UP panel (absolute bottom:100%, opens upward so the bubble
|
|
7
|
+
* layout doesn't shift). Same geometry: OPTION_HEIGHT 36, 200ms animation,
|
|
8
|
+
* selected row gets a green check.
|
|
9
|
+
*/
|
|
10
|
+
import { useState } from 'react';
|
|
11
|
+
import { gameUIColors } from "../ui/gameUI/constants/gameUIColors.js";
|
|
12
|
+
import { getEnvironmentConfig } from "./EnvironmentIndicator.web.js";
|
|
13
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
14
|
+
const OPTION_HEIGHT = 36; // native EnvironmentSelectorInline OPTION_HEIGHT
|
|
15
|
+
|
|
16
|
+
function ChevronUpGlyph({
|
|
17
|
+
color,
|
|
18
|
+
flipped
|
|
19
|
+
}) {
|
|
20
|
+
return /*#__PURE__*/_jsx("svg", {
|
|
21
|
+
width: 10,
|
|
22
|
+
height: 10,
|
|
23
|
+
viewBox: "0 0 24 24",
|
|
24
|
+
"aria-hidden": "true",
|
|
25
|
+
style: {
|
|
26
|
+
transform: flipped ? 'rotate(180deg)' : 'rotate(0deg)',
|
|
27
|
+
transition: 'transform 200ms ease' // native: 200ms rotation
|
|
28
|
+
},
|
|
29
|
+
children: /*#__PURE__*/_jsx("path", {
|
|
30
|
+
d: "M18 15l-6-6-6 6",
|
|
31
|
+
fill: "none",
|
|
32
|
+
stroke: color,
|
|
33
|
+
strokeWidth: "2",
|
|
34
|
+
strokeLinecap: "round",
|
|
35
|
+
strokeLinejoin: "round"
|
|
36
|
+
})
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function CheckGlyph() {
|
|
40
|
+
// native: Check size 12 color #4AFF9F strokeWidth 3
|
|
41
|
+
return /*#__PURE__*/_jsx("svg", {
|
|
42
|
+
width: 12,
|
|
43
|
+
height: 12,
|
|
44
|
+
viewBox: "0 0 24 24",
|
|
45
|
+
"aria-hidden": "true",
|
|
46
|
+
children: /*#__PURE__*/_jsx("path", {
|
|
47
|
+
d: "M20 6L9 17l-5-5",
|
|
48
|
+
fill: "none",
|
|
49
|
+
stroke: "#4AFF9F",
|
|
50
|
+
strokeWidth: "3",
|
|
51
|
+
strokeLinecap: "round",
|
|
52
|
+
strokeLinejoin: "round"
|
|
53
|
+
})
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
export function EnvironmentSelectorInline({
|
|
57
|
+
currentEnvironment,
|
|
58
|
+
availableEnvironments,
|
|
59
|
+
onSelect
|
|
60
|
+
}) {
|
|
61
|
+
const [expanded, setExpanded] = useState(false);
|
|
62
|
+
const [hoveredEnv, setHoveredEnv] = useState(null);
|
|
63
|
+
const current = getEnvironmentConfig(currentEnvironment);
|
|
64
|
+
const panelHeight = availableEnvironments.length * OPTION_HEIGHT;
|
|
65
|
+
return /*#__PURE__*/_jsxs("div", {
|
|
66
|
+
style: {
|
|
67
|
+
position: 'relative',
|
|
68
|
+
zIndex: 1000,
|
|
69
|
+
flexShrink: 0
|
|
70
|
+
},
|
|
71
|
+
children: [/*#__PURE__*/_jsx("div", {
|
|
72
|
+
style: {
|
|
73
|
+
position: 'absolute',
|
|
74
|
+
bottom: '100%',
|
|
75
|
+
left: 0,
|
|
76
|
+
right: 0,
|
|
77
|
+
height: expanded ? panelHeight : 0,
|
|
78
|
+
opacity: expanded ? 1 : 0,
|
|
79
|
+
overflow: 'hidden',
|
|
80
|
+
transition: 'height 200ms ease, opacity 200ms ease',
|
|
81
|
+
backgroundColor: 'rgba(8,12,21,0.95)',
|
|
82
|
+
border: '1px solid #00B8E666',
|
|
83
|
+
borderBottomWidth: 0,
|
|
84
|
+
borderTopLeftRadius: 6,
|
|
85
|
+
borderTopRightRadius: 6,
|
|
86
|
+
boxShadow: '0 -4px 8px rgba(0,0,0,0.3)'
|
|
87
|
+
},
|
|
88
|
+
children: availableEnvironments.map(env => {
|
|
89
|
+
const config = getEnvironmentConfig(env);
|
|
90
|
+
const isSelected = String(env).toLowerCase() === String(currentEnvironment).toLowerCase();
|
|
91
|
+
return /*#__PURE__*/_jsxs("button", {
|
|
92
|
+
onClick: () => {
|
|
93
|
+
setExpanded(false);
|
|
94
|
+
onSelect(env);
|
|
95
|
+
},
|
|
96
|
+
onMouseEnter: () => setHoveredEnv(String(env)),
|
|
97
|
+
onMouseLeave: () => setHoveredEnv(null),
|
|
98
|
+
style: {
|
|
99
|
+
display: 'flex',
|
|
100
|
+
alignItems: 'center',
|
|
101
|
+
justifyContent: 'space-between',
|
|
102
|
+
width: '100%',
|
|
103
|
+
height: OPTION_HEIGHT,
|
|
104
|
+
padding: '0 10px',
|
|
105
|
+
background: isSelected ? 'rgba(128,128,128,0.1)' : hoveredEnv === String(env) ? 'rgba(128,128,128,0.2)' : 'none',
|
|
106
|
+
border: 'none',
|
|
107
|
+
cursor: 'pointer'
|
|
108
|
+
},
|
|
109
|
+
children: [/*#__PURE__*/_jsxs("span", {
|
|
110
|
+
style: {
|
|
111
|
+
display: 'flex',
|
|
112
|
+
alignItems: 'center'
|
|
113
|
+
},
|
|
114
|
+
children: [/*#__PURE__*/_jsx("span", {
|
|
115
|
+
style: {
|
|
116
|
+
width: 6,
|
|
117
|
+
height: 6,
|
|
118
|
+
borderRadius: 3,
|
|
119
|
+
backgroundColor: config.backgroundColor,
|
|
120
|
+
marginRight: 8
|
|
121
|
+
}
|
|
122
|
+
}), /*#__PURE__*/_jsx("span", {
|
|
123
|
+
style: {
|
|
124
|
+
fontSize: 10,
|
|
125
|
+
fontWeight: 600,
|
|
126
|
+
color: gameUIColors.primaryLight,
|
|
127
|
+
letterSpacing: 0.5
|
|
128
|
+
},
|
|
129
|
+
children: config.label
|
|
130
|
+
})]
|
|
131
|
+
}), isSelected && /*#__PURE__*/_jsx(CheckGlyph, {})]
|
|
132
|
+
}, String(env));
|
|
133
|
+
})
|
|
134
|
+
}), /*#__PURE__*/_jsxs("button", {
|
|
135
|
+
onClick: () => setExpanded(e => !e),
|
|
136
|
+
"aria-expanded": expanded,
|
|
137
|
+
"aria-label": `Environment: ${current.label}. Click to switch.`,
|
|
138
|
+
style: {
|
|
139
|
+
display: 'flex',
|
|
140
|
+
alignItems: 'center',
|
|
141
|
+
gap: 6,
|
|
142
|
+
padding: '6px 6px 6px 8px',
|
|
143
|
+
borderRadius: 6,
|
|
144
|
+
// native: expanded trigger fuses with panel (squared top corners)
|
|
145
|
+
borderTopLeftRadius: expanded ? 0 : 6,
|
|
146
|
+
borderTopRightRadius: expanded ? 0 : 6,
|
|
147
|
+
backgroundColor: expanded ? 'rgba(8,12,21,0.95)' : 'transparent',
|
|
148
|
+
border: 'none',
|
|
149
|
+
cursor: 'pointer'
|
|
150
|
+
},
|
|
151
|
+
children: [/*#__PURE__*/_jsx("span", {
|
|
152
|
+
style: {
|
|
153
|
+
width: 8,
|
|
154
|
+
height: 8,
|
|
155
|
+
borderRadius: 4,
|
|
156
|
+
backgroundColor: current.backgroundColor,
|
|
157
|
+
boxShadow: `0 0 4px ${current.backgroundColor}99`
|
|
158
|
+
}
|
|
159
|
+
}), /*#__PURE__*/_jsx("span", {
|
|
160
|
+
style: {
|
|
161
|
+
fontSize: 11,
|
|
162
|
+
fontWeight: 600,
|
|
163
|
+
color: gameUIColors.primaryLight,
|
|
164
|
+
letterSpacing: 0.5
|
|
165
|
+
},
|
|
166
|
+
children: current.label
|
|
167
|
+
}), /*#__PURE__*/_jsx(ChevronUpGlyph, {
|
|
168
|
+
color: "#7A8599",
|
|
169
|
+
flipped: expanded
|
|
170
|
+
})]
|
|
171
|
+
})]
|
|
172
|
+
});
|
|
173
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
export { useFilterManager } from "./useFilterManager.js";
|
|
4
|
+
export { usePersistedState } from "./usePersistedState.js";
|
|
4
5
|
export { useSafeAreaInsets } from "./useSafeAreaInsets.js";
|
|
5
6
|
export { getSafeAreaInsets } from "./useSafeAreaInsets.js";
|
|
6
7
|
export { useDeviceInfo, getDeviceInfo, logDeviceInfo } from "./useDeviceInfo.js";
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
4
|
+
import { persistentStorage } from "../utils/persistentStorage.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Per-key cache so remounts (and multiple consumers of the same key) render
|
|
8
|
+
* the last-known value immediately instead of flashing the default while the
|
|
9
|
+
* async backend loads again.
|
|
10
|
+
*/
|
|
11
|
+
const hydratedCache = new Map();
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* `useState` persisted through `persistentStorage` (FileSystem → AsyncStorage
|
|
15
|
+
* → memory). Replaces the load-then-`hasLoaded`-guard `useEffect` pattern
|
|
16
|
+
* every tool used to hand-roll.
|
|
17
|
+
*
|
|
18
|
+
* Values are JSON-serialized; the default is returned until hydration
|
|
19
|
+
* completes (subsequent mounts of the same key hydrate synchronously from an
|
|
20
|
+
* in-memory cache). The third tuple element reports hydration so callers can
|
|
21
|
+
* defer writes that must not clobber a not-yet-loaded value.
|
|
22
|
+
*
|
|
23
|
+
* ```tsx
|
|
24
|
+
* const [tab, setTab, tabHydrated] = usePersistedState("@react_buoy_custom_flags_tab", "flags");
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export function usePersistedState(key, defaultValue) {
|
|
28
|
+
const [value, setValue] = useState(() => hydratedCache.has(key) ? hydratedCache.get(key) : defaultValue);
|
|
29
|
+
const [hydrated, setHydrated] = useState(() => hydratedCache.has(key));
|
|
30
|
+
const keyRef = useRef(key);
|
|
31
|
+
keyRef.current = key;
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (hydratedCache.has(key)) {
|
|
34
|
+
setValue(hydratedCache.get(key));
|
|
35
|
+
setHydrated(true);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
let cancelled = false;
|
|
39
|
+
persistentStorage.getItem(key).then(raw => {
|
|
40
|
+
if (cancelled) return;
|
|
41
|
+
if (raw != null) {
|
|
42
|
+
try {
|
|
43
|
+
const parsed = JSON.parse(raw);
|
|
44
|
+
hydratedCache.set(key, parsed);
|
|
45
|
+
setValue(parsed);
|
|
46
|
+
} catch {
|
|
47
|
+
// Corrupt payload — fall back to the default and let the next
|
|
48
|
+
// write repair it.
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
setHydrated(true);
|
|
52
|
+
}).catch(() => {
|
|
53
|
+
if (!cancelled) setHydrated(true);
|
|
54
|
+
});
|
|
55
|
+
return () => {
|
|
56
|
+
cancelled = true;
|
|
57
|
+
};
|
|
58
|
+
}, [key]);
|
|
59
|
+
const set = useCallback(next => {
|
|
60
|
+
setValue(prev => {
|
|
61
|
+
const resolved = typeof next === "function" ? next(prev) : next;
|
|
62
|
+
hydratedCache.set(keyRef.current, resolved);
|
|
63
|
+
persistentStorage.setItem(keyRef.current, JSON.stringify(resolved)).catch(() => {});
|
|
64
|
+
return resolved;
|
|
65
|
+
});
|
|
66
|
+
}, []);
|
|
67
|
+
return [value, set, hydrated];
|
|
68
|
+
}
|
|
@@ -11,7 +11,9 @@ export { EnvIcon, ENV_ICON_COLOR } from "@buoy-gg/floating-tools-core";
|
|
|
11
11
|
// into core, so these are the same artwork the Flutter build renders. Tool
|
|
12
12
|
// packages re-export from here rather than drawing their own copies.
|
|
13
13
|
export { ConsoleIcon, CONSOLE_ICON_COLOR } from "@buoy-gg/floating-tools-core";
|
|
14
|
+
export { TimeMachineIcon, TIME_MACHINE_ICON_COLOR } from "@buoy-gg/floating-tools-core";
|
|
14
15
|
export { ImagesIcon, IMAGES_ICON_COLOR } from "@buoy-gg/floating-tools-core";
|
|
16
|
+
export { AssetsIcon, ASSETS_ICON_COLOR } from "@buoy-gg/floating-tools-core";
|
|
15
17
|
export { ImpersonateIcon, IMPERSONATE_ICON_COLOR } from "@buoy-gg/floating-tools-core";
|
|
16
18
|
export { ImageOverlayIcon, IMAGE_OVERLAY_ICON_COLOR } from "@buoy-gg/floating-tools-core";
|
|
17
19
|
export { ZustandIcon, ZUSTAND_ICON_COLOR } from "@buoy-gg/floating-tools-core";
|
package/lib/module/index.js
CHANGED
|
@@ -26,6 +26,10 @@ parseValue, formatValue, getTypeColor, truncateText, flattenObject, formatPath,
|
|
|
26
26
|
loadOptionalModule, getCachedOptionalModule,
|
|
27
27
|
// Render-commit capture bridge (provider: highlight-updates, consumer: perf-monitor)
|
|
28
28
|
setRenderCaptureProvider, getRenderCaptureProvider,
|
|
29
|
+
// State snapshot bridge (providers: storage/redux/zustand/jotai/react-query, consumer: time-machine)
|
|
30
|
+
registerStateSnapshotProvider, getStateSnapshotProviders, getStateSnapshotProvider, subscribeToStateSnapshotProviders,
|
|
31
|
+
// Minimal superjson (Date/Map/Set/BigInt-safe round-trip) for snapshot payloads
|
|
32
|
+
superJsonSerialize, superJsonDeserialize,
|
|
29
33
|
// JS-bundle reload (core's `app` adapter + perf-monitor's batch runner)
|
|
30
34
|
reloadApp, scheduleReloadApp, resolveReloadStrategy, REMOTE_RELOAD_DELAY_MS,
|
|
31
35
|
// Subscribable base class for self-managing listeners
|
|
@@ -53,7 +57,7 @@ export * from "./storage/index.js";
|
|
|
53
57
|
export { useSafeAreaInsets, useFilterManager, useDeviceInfo, getDeviceInfo, logDeviceInfo } from "./hooks/index.js";
|
|
54
58
|
|
|
55
59
|
// JsModal
|
|
56
|
-
export { JsModal } from "./JsModal
|
|
60
|
+
export { JsModal } from "./JsModal";
|
|
57
61
|
// game ui colors
|
|
58
62
|
export { gameUIColors, buoyColors } from "./ui/gameUI/constants/gameUIColors.js";
|
|
59
63
|
// Runtime re-tint hook — only the desktop dashboard's theme switcher uses it.
|
|
@@ -61,11 +65,11 @@ export { refreshGameUIColors } from "./ui/gameUI/constants/gameUIColors.js";
|
|
|
61
65
|
export { macOSColors } from "./ui/gameUI/constants/macOSDesignSystemColors.js";
|
|
62
66
|
export { dialColors } from "./ui/gameUI/constants/gameUIColors.js";
|
|
63
67
|
//EnvironmentIndicator
|
|
64
|
-
export { EnvironmentIndicator } from "./env/EnvironmentIndicator
|
|
68
|
+
export { EnvironmentIndicator } from "./env/EnvironmentIndicator";
|
|
65
69
|
// EnvironmentSelector
|
|
66
70
|
export { EnvironmentSelector } from "./env/EnvironmentSelector.js";
|
|
67
71
|
// EnvironmentSelectorInline
|
|
68
|
-
export { EnvironmentSelectorInline } from "./env/EnvironmentSelectorInline
|
|
72
|
+
export { EnvironmentSelectorInline } from "./env/EnvironmentSelectorInline";
|
|
69
73
|
export { normalizeEnvironment } from "./types/types.js";
|
|
70
74
|
|
|
71
75
|
// Context exports
|
|
@@ -31,6 +31,28 @@ export const devToolsStorageKeys = {
|
|
|
31
31
|
dimensions: () => `${devToolsStorageKeys.modal.root()}_dimensions`,
|
|
32
32
|
hintAcknowledged: () => `${devToolsStorageKeys.modal.root()}_hint_acknowledged`
|
|
33
33
|
},
|
|
34
|
+
/**
|
|
35
|
+
* Namespace factory for third-party custom tools. Keys share the
|
|
36
|
+
* `@react_buoy` prefix, so `isDevToolsStorageKey` filtering (and the Storage
|
|
37
|
+
* tool's internal-key handling) covers custom-tool persistence for free.
|
|
38
|
+
*
|
|
39
|
+
* ```ts
|
|
40
|
+
* const keys = devToolsStorageKeys.custom("feature-flags");
|
|
41
|
+
* keys.modal(); // "@react_buoy_custom_feature-flags_modal"
|
|
42
|
+
* keys.key("overrides"); // "@react_buoy_custom_feature-flags_overrides"
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
custom: toolId => {
|
|
46
|
+
const root = `${devToolsStorageKeys.base}_custom_${toolId}`;
|
|
47
|
+
return {
|
|
48
|
+
root: () => root,
|
|
49
|
+
/** JsModal `persistenceKey` for this tool's window dimensions. */
|
|
50
|
+
modal: () => `${root}_modal`,
|
|
51
|
+
settings: () => `${root}_settings`,
|
|
52
|
+
/** Arbitrary tool-scoped key. */
|
|
53
|
+
key: suffix => `${root}_${suffix}`
|
|
54
|
+
};
|
|
55
|
+
},
|
|
34
56
|
/**
|
|
35
57
|
* Settings-related storage keys
|
|
36
58
|
*/
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Pure, dependency-free part of the ignored-patterns feature — split from
|
|
5
|
+
* ignoredPatternsStore.ts so web consumers (which shim `@buoy-gg/shared-ui`
|
|
6
|
+
* and must not pull the store's relative `../utils/persistentStorage` import
|
|
7
|
+
* with its expo/async-storage requires into the bundle graph) can share the
|
|
8
|
+
* exact matching semantics. The store imports and re-exports everything here;
|
|
9
|
+
* the native barrel is unchanged.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* How a pattern matches a URL:
|
|
14
|
+
*
|
|
15
|
+
* - `contains`: case-insensitive substring match on the full URL (legacy behavior).
|
|
16
|
+
* - `exact`: smart equality — if pattern starts with `/`, matches URL.pathname;
|
|
17
|
+
* if it starts with `http://`/`https://`, matches origin+pathname; otherwise
|
|
18
|
+
* matches URL.host.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/** A single exclude pattern with its match mode. */
|
|
22
|
+
|
|
23
|
+
/** Patterns hidden by default — Buoy's own license API traffic. */
|
|
24
|
+
export const DEFAULT_IGNORED_PATTERNS = [{
|
|
25
|
+
value: "api.keygen.sh",
|
|
26
|
+
mode: "contains"
|
|
27
|
+
}];
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Returns true when `url` matches the ignored `pattern` according to its mode.
|
|
31
|
+
*
|
|
32
|
+
* `contains` → case-insensitive substring on the full URL (legacy behavior).
|
|
33
|
+
* `exact` → smart equality: pattern starting with `/` compares URL.pathname,
|
|
34
|
+
* full URLs compare origin+pathname (ignoring query/hash), bare
|
|
35
|
+
* values compare URL.host. Falls back to literal equality if URL
|
|
36
|
+
* parsing fails (e.g. relative URLs).
|
|
37
|
+
*/
|
|
38
|
+
export function urlMatchesIgnoredPattern(url, pattern) {
|
|
39
|
+
const lowerUrl = url.toLowerCase();
|
|
40
|
+
const lowerValue = pattern.value.toLowerCase();
|
|
41
|
+
if (pattern.mode === "contains") {
|
|
42
|
+
return lowerUrl.includes(lowerValue);
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const parsed = new URL(url);
|
|
46
|
+
if (lowerValue.startsWith("/")) {
|
|
47
|
+
return parsed.pathname.toLowerCase() === lowerValue;
|
|
48
|
+
}
|
|
49
|
+
if (lowerValue.startsWith("http://") || lowerValue.startsWith("https://")) {
|
|
50
|
+
return `${parsed.origin}${parsed.pathname}`.toLowerCase() === lowerValue;
|
|
51
|
+
}
|
|
52
|
+
return parsed.host.toLowerCase() === lowerValue;
|
|
53
|
+
} catch {
|
|
54
|
+
return lowerUrl === lowerValue;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Convenience: does `url` match ANY of the ignored `patterns`? */
|
|
59
|
+
export function isUrlIgnored(url, patterns) {
|
|
60
|
+
if (!url || patterns.length === 0) return false;
|
|
61
|
+
return patterns.some(pattern => urlMatchesIgnoredPattern(url, pattern));
|
|
62
|
+
}
|
|
@@ -17,57 +17,11 @@ import { useEffect, useMemo, useState } from "react";
|
|
|
17
17
|
import { persistentStorage } from "../utils/persistentStorage.js";
|
|
18
18
|
import { devToolsStorageKeys } from "../storage/devToolsStorageKeys.js";
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
* if it starts with `http://`/`https://`, matches origin+pathname; otherwise
|
|
26
|
-
* matches URL.host.
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
/** A single exclude pattern with its match mode. */
|
|
30
|
-
|
|
31
|
-
/** Patterns hidden by default — Buoy's own license API traffic. */
|
|
32
|
-
const DEFAULT_PATTERNS = [{
|
|
33
|
-
value: "api.keygen.sh",
|
|
34
|
-
mode: "contains"
|
|
35
|
-
}];
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Returns true when `url` matches the ignored `pattern` according to its mode.
|
|
39
|
-
*
|
|
40
|
-
* `contains` → case-insensitive substring on the full URL (legacy behavior).
|
|
41
|
-
* `exact` → smart equality: pattern starting with `/` compares URL.pathname,
|
|
42
|
-
* full URLs compare origin+pathname (ignoring query/hash), bare
|
|
43
|
-
* values compare URL.host. Falls back to literal equality if URL
|
|
44
|
-
* parsing fails (e.g. relative URLs).
|
|
45
|
-
*/
|
|
46
|
-
export function urlMatchesIgnoredPattern(url, pattern) {
|
|
47
|
-
const lowerUrl = url.toLowerCase();
|
|
48
|
-
const lowerValue = pattern.value.toLowerCase();
|
|
49
|
-
if (pattern.mode === "contains") {
|
|
50
|
-
return lowerUrl.includes(lowerValue);
|
|
51
|
-
}
|
|
52
|
-
try {
|
|
53
|
-
const parsed = new URL(url);
|
|
54
|
-
if (lowerValue.startsWith("/")) {
|
|
55
|
-
return parsed.pathname.toLowerCase() === lowerValue;
|
|
56
|
-
}
|
|
57
|
-
if (lowerValue.startsWith("http://") || lowerValue.startsWith("https://")) {
|
|
58
|
-
return `${parsed.origin}${parsed.pathname}`.toLowerCase() === lowerValue;
|
|
59
|
-
}
|
|
60
|
-
return parsed.host.toLowerCase() === lowerValue;
|
|
61
|
-
} catch {
|
|
62
|
-
return lowerUrl === lowerValue;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** Convenience: does `url` match ANY of the ignored `patterns`? */
|
|
67
|
-
export function isUrlIgnored(url, patterns) {
|
|
68
|
-
if (!url || patterns.length === 0) return false;
|
|
69
|
-
return patterns.some(pattern => urlMatchesIgnoredPattern(url, pattern));
|
|
70
|
-
}
|
|
20
|
+
// Pure matching types/helpers live in ignoredPatternsCore.ts (web-shim-safe);
|
|
21
|
+
// re-exported here so the shared-ui barrel surface is unchanged.
|
|
22
|
+
import { DEFAULT_IGNORED_PATTERNS } from "./ignoredPatternsCore.js";
|
|
23
|
+
export { urlMatchesIgnoredPattern, isUrlIgnored } from "./ignoredPatternsCore.js";
|
|
24
|
+
const DEFAULT_PATTERNS = DEFAULT_IGNORED_PATTERNS;
|
|
71
25
|
|
|
72
26
|
/** Tolerate the legacy `string[]` persisted format and stray junk. */
|
|
73
27
|
function migratePatterns(raw) {
|