@buoy-gg/env 7.0.41 → 7.0.42
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/README.md +11 -87
- package/lib/commonjs/env/components/EnvStatsOverview.js +1 -159
- package/lib/commonjs/env/components/EnvVarRow.js +1 -189
- package/lib/commonjs/env/components/EnvVarSection.js +1 -84
- package/lib/commonjs/env/components/EnvVarsModal.js +1 -322
- package/lib/commonjs/env/hooks/useDynamicEnv.js +1 -92
- package/lib/commonjs/env/types/index.js +1 -27
- package/lib/commonjs/env/types/types.js +1 -1
- package/lib/commonjs/env/types/userTypes.js +1 -1
- package/lib/commonjs/env/utils/envTypeDetector.js +1 -59
- package/lib/commonjs/env/utils/helpers.js +1 -119
- package/lib/commonjs/env/utils/index.js +1 -38
- package/lib/commonjs/env/utils/remoteEnv.js +1 -24
- package/lib/commonjs/env/utils/utils.js +1 -121
- package/lib/commonjs/index.js +1 -77
- package/lib/commonjs/preset.js +1 -81
- package/lib/commonjs/sync/envSyncAdapter.js +1 -51
- package/lib/module/env/components/EnvStatsOverview.js +1 -155
- package/lib/module/env/components/EnvVarRow.js +1 -185
- package/lib/module/env/components/EnvVarSection.js +1 -80
- package/lib/module/env/components/EnvVarsModal.js +1 -317
- package/lib/module/env/hooks/useDynamicEnv.js +1 -88
- package/lib/module/env/types/index.js +1 -4
- package/lib/module/env/types/types.js +1 -1
- package/lib/module/env/types/userTypes.js +1 -1
- package/lib/module/env/utils/envTypeDetector.js +1 -55
- package/lib/module/env/utils/helpers.js +1 -114
- package/lib/module/env/utils/index.js +1 -5
- package/lib/module/env/utils/remoteEnv.js +1 -19
- package/lib/module/env/utils/utils.js +1 -116
- package/lib/module/index.js +1 -21
- package/lib/module/preset.js +1 -78
- package/lib/module/sync/envSyncAdapter.js +1 -46
- package/lib/web/index.mjs +1 -2
- package/package.json +3 -3
- package/lib/typescript/env/components/EnvStatsOverview.d.ts.map +0 -1
- package/lib/typescript/env/components/EnvVarRow.d.ts.map +0 -1
- package/lib/typescript/env/components/EnvVarSection.d.ts.map +0 -1
- package/lib/typescript/env/components/EnvVarsModal.d.ts.map +0 -1
- package/lib/typescript/env/hooks/useDynamicEnv.d.ts.map +0 -1
- package/lib/typescript/env/types/index.d.ts.map +0 -1
- package/lib/typescript/env/types/types.d.ts.map +0 -1
- package/lib/typescript/env/types/userTypes.d.ts.map +0 -1
- package/lib/typescript/env/utils/envTypeDetector.d.ts.map +0 -1
- package/lib/typescript/env/utils/helpers.d.ts.map +0 -1
- package/lib/typescript/env/utils/index.d.ts.map +0 -1
- package/lib/typescript/env/utils/remoteEnv.d.ts.map +0 -1
- package/lib/typescript/env/utils/utils.d.ts.map +0 -1
- package/lib/typescript/index.d.ts.map +0 -1
- package/lib/typescript/preset.d.ts.map +0 -1
- package/lib/typescript/sync/envSyncAdapter.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,103 +1,27 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# @buoy-gg/env
|
|
4
4
|
|
|
5
5
|
Inspect the environment values available to your app at runtime and check required variables against expected types and values.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
<a href="https://buoy.gg/buoy/latest/docs/tools/env"><b>Env guide</b></a> · <a href="https://buoy.gg">buoy.gg</a> · <a href="https://github.com/Buoy-gg/buoy">All tools</a>
|
|
8
8
|
|
|
9
|
-
](https://www.npmjs.com/package/@buoy-gg/env) [](https://www.npmjs.com/package/@buoy-gg/env)
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
<a href="https://buoy.gg/buoy/latest/docs/tools/env"><img src="https://raw.githubusercontent.com/Buoy-gg/buoy/main/.github/readme/tools/env.png" alt="Watch the Env film on buoy.gg" width="640" /></a>
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
npm install @buoy-gg/core @buoy-gg/env
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
## Before you start
|
|
13
|
+
</div>
|
|
18
14
|
|
|
19
|
-
|
|
15
|
+
## Install
|
|
20
16
|
|
|
21
17
|
```bash
|
|
22
|
-
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
For Expo, initialize Buoy before rendering the menu:
|
|
26
|
-
|
|
27
|
-
```tsx
|
|
28
|
-
import { Buoy } from "@buoy-gg/core";
|
|
29
|
-
|
|
30
|
-
Buoy.init({ licenseKey: process.env.EXPO_PUBLIC_BUOY_KEY });
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
The login command writes the Expo key to `.env.local`. For React Native CLI, pass the key from your app’s environment configuration; React Native does not load `.env.local` automatically. Mount `FloatingDevTools` inside the same providers as your screens and restart the development server after installation. The [Quick Start](https://buoy.gg/buoy/latest/docs/quick-start) shows the complete root component setup.
|
|
34
|
-
|
|
35
|
-
## Quick start
|
|
36
|
-
|
|
37
|
-
After initialization, render the menu in your app root:
|
|
38
|
-
|
|
39
|
-
```tsx
|
|
40
|
-
import { FloatingDevTools } from "@buoy-gg/core";
|
|
41
|
-
|
|
42
|
-
export default function App() {
|
|
43
|
-
return (
|
|
44
|
-
<>
|
|
45
|
-
{/* your app */}
|
|
46
|
-
<FloatingDevTools />
|
|
47
|
-
</>
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
To validate required variables, build the tool with `createEnvTool` and the fluent `envVar` builder, then pass it via `apps={[...]}`:
|
|
53
|
-
|
|
54
|
-
```tsx
|
|
55
|
-
import { createEnvTool, envVar } from "@buoy-gg/env";
|
|
56
|
-
|
|
57
|
-
const envTool = createEnvTool({
|
|
58
|
-
requiredEnvVars: [
|
|
59
|
-
envVar("EXPO_PUBLIC_API_URL").exists(),
|
|
60
|
-
envVar("EXPO_PUBLIC_DEBUG_MODE").withType("boolean").build(),
|
|
61
|
-
envVar("EXPO_PUBLIC_ENVIRONMENT").withValue("development").build(),
|
|
62
|
-
envVar("EXPO_PUBLIC_MAX_RETRIES")
|
|
63
|
-
.withType("number")
|
|
64
|
-
.withDescription("Maximum API retry attempts")
|
|
65
|
-
.build(),
|
|
66
|
-
],
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
// Render this menu inside your existing app root.
|
|
70
|
-
<FloatingDevTools apps={[envTool]} />;
|
|
18
|
+
npm install @buoy-gg/core @buoy-gg/env
|
|
71
19
|
```
|
|
72
20
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
Open Env and check one known variable and one deliberately missing required variable. Expo statically replaces direct `process.env.EXPO_PUBLIC_NAME` references; enumerating `process.env` does not guarantee that those values are available. An empty list does not prove your app has no environment configuration.
|
|
21
|
+
Env appears in the menu once installed. To check required variables, build the tool with `createEnvTool` and pass it through `apps`.
|
|
76
22
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
- **Runtime discovery** — collects enumerable `EXPO_PUBLIC_` values available at runtime. Expo’s static inlining limits what enumeration can see.
|
|
80
|
-
- **Required-variable validation** — declare which vars must exist, with expected values or types: `string`, `number`, `boolean`, `array`, `object`, `url`.
|
|
81
|
-
- **Type detection** — values are auto-classified (string, number, boolean, array, object, url, json).
|
|
82
|
-
- **Health score** — a 0-100% health percentage with HEALTHY / WARNING / ERROR / CRITICAL states, plus counts for total, required, missing, and wrong value/type.
|
|
83
|
-
- **Per-variable status** — green (present and correct), yellow (wrong value or type), red (required but missing).
|
|
84
|
-
- **Search and filters** — real-time search plus "All", "Missing", and "Issues" filters.
|
|
85
|
-
- **Copy to clipboard** — copy any value with one tap.
|
|
86
|
-
|
|
87
|
-
## Desktop & AI
|
|
88
|
-
|
|
89
|
-
To connect a React Native app to [Buoy Desktop](https://github.com/Buoy-gg/Buoy-Desktop) or MCP, install `@buoy-gg/external-sync` and follow the [Desktop connection guide](https://buoy.gg/buoy/latest/docs/desktop). Sign in to Desktop separately. [MCP setup](https://buoy.gg/buoy/latest/docs/mcp) also requires a process account and Pro access. Available remote actions depend on the tool and app integration.
|
|
90
|
-
|
|
91
|
-
## Account and plan requirements
|
|
92
|
-
|
|
93
|
-
Use a Free or Pro Buoy account. History limits and paid features vary by tool; see [pricing](https://buoy.gg/pricing). Production access requires Pro where supported. Development-only hooks and actions remain unavailable in release builds.
|
|
23
|
+
The [Env guide](https://buoy.gg/buoy/latest/docs/tools/env) covers setup, what it captures and its limits. New to Buoy? Start with the [Quick start](https://buoy.gg/buoy/latest/docs/quick-start).
|
|
94
24
|
|
|
95
25
|
---
|
|
96
26
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
Proprietary software. © Buoy LLC. [Terms](https://buoy.gg/terms)
|
|
100
|
-
|
|
101
|
-
## Web support (unreleased)
|
|
102
|
-
|
|
103
|
-
Supply public runtime values explicitly with setRemoteEnv. The inspector cannot enumerate variables that a bundler replaced at build time. The browser build is available in this checkout and has not been published yet. See the [web setup guide](https://buoy.gg/buoy/latest/docs/web-preview) for registration, dependencies, and browser boundaries.
|
|
27
|
+
<sub>Part of <a href="https://buoy.gg">Buoy</a>, developer tools that live inside your app. Proprietary software. © Buoy LLC. <a href="https://buoy.gg/terms">Terms</a></sub>
|
|
@@ -1,159 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.EnvStatsOverview = EnvStatsOverview;
|
|
7
|
-
var _reactNative = require("react-native");
|
|
8
|
-
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
9
|
-
var _jsxRuntime = require("react/jsx-runtime");
|
|
10
|
-
function EnvStatsOverview({
|
|
11
|
-
stats,
|
|
12
|
-
healthPercentage,
|
|
13
|
-
healthStatus,
|
|
14
|
-
healthColor,
|
|
15
|
-
activeFilter = "all",
|
|
16
|
-
onFilterChange
|
|
17
|
-
}) {
|
|
18
|
-
const issuesCount = stats.wrongValueCount + stats.wrongTypeCount;
|
|
19
|
-
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
20
|
-
style: styles.container,
|
|
21
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.CompactRow, {
|
|
22
|
-
statusDotColor: healthColor,
|
|
23
|
-
statusLabel: "System",
|
|
24
|
-
statusSublabel: healthStatus.toLowerCase(),
|
|
25
|
-
primaryText: "Environment Configuration",
|
|
26
|
-
secondaryText: `${healthPercentage}% healthy`,
|
|
27
|
-
testID: "buoy-env-health",
|
|
28
|
-
statusSublabelTestID: "buoy-env-health-status",
|
|
29
|
-
accessibilityLabel: `Environment configuration ${healthStatus.toLowerCase()}, ${healthPercentage}% healthy`,
|
|
30
|
-
customBadge: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
31
|
-
style: [styles.percentBadge, {
|
|
32
|
-
borderColor: healthColor + "40",
|
|
33
|
-
backgroundColor: healthColor + "10"
|
|
34
|
-
}],
|
|
35
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
|
|
36
|
-
style: [styles.percentText, {
|
|
37
|
-
color: healthColor
|
|
38
|
-
}],
|
|
39
|
-
testID: "buoy-env-health-percent",
|
|
40
|
-
children: [healthPercentage, "%"]
|
|
41
|
-
})
|
|
42
|
-
})
|
|
43
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
44
|
-
style: styles.statsGrid,
|
|
45
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
|
|
46
|
-
style: [styles.statCard, activeFilter === "all" && styles.statCardActive],
|
|
47
|
-
onPress: () => onFilterChange?.("all"),
|
|
48
|
-
activeOpacity: 0.8,
|
|
49
|
-
testID: "buoy-env-filter-all",
|
|
50
|
-
accessibilityRole: "button",
|
|
51
|
-
accessibilityLabel: "Show all variables",
|
|
52
|
-
accessibilityState: {
|
|
53
|
-
selected: activeFilter === "all"
|
|
54
|
-
},
|
|
55
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
56
|
-
style: [styles.statValue, activeFilter === "all" && styles.statValueActive],
|
|
57
|
-
testID: "buoy-env-filter-all-count",
|
|
58
|
-
children: stats.requiredCount + stats.optionalCount
|
|
59
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
60
|
-
style: [styles.statLabel, activeFilter === "all" && styles.statLabelActive],
|
|
61
|
-
children: "ALL"
|
|
62
|
-
})]
|
|
63
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
|
|
64
|
-
style: [styles.statCard, activeFilter === "missing" && styles.statCardActive],
|
|
65
|
-
onPress: () => onFilterChange?.("missing"),
|
|
66
|
-
activeOpacity: 0.8,
|
|
67
|
-
testID: "buoy-env-filter-missing",
|
|
68
|
-
accessibilityRole: "button",
|
|
69
|
-
accessibilityLabel: "Show missing variables",
|
|
70
|
-
accessibilityState: {
|
|
71
|
-
selected: activeFilter === "missing"
|
|
72
|
-
},
|
|
73
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
74
|
-
style: [styles.statValue, activeFilter === "missing" && styles.statValueActive],
|
|
75
|
-
testID: "buoy-env-filter-missing-count",
|
|
76
|
-
children: stats.missingCount
|
|
77
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
78
|
-
style: [styles.statLabel, activeFilter === "missing" && styles.statLabelActive],
|
|
79
|
-
children: "MISSING"
|
|
80
|
-
})]
|
|
81
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
|
|
82
|
-
style: [styles.statCard, activeFilter === "issues" && styles.statCardActive],
|
|
83
|
-
onPress: () => onFilterChange?.("issues"),
|
|
84
|
-
activeOpacity: 0.8,
|
|
85
|
-
testID: "buoy-env-filter-issues",
|
|
86
|
-
accessibilityRole: "button",
|
|
87
|
-
accessibilityLabel: "Show variables with issues",
|
|
88
|
-
accessibilityState: {
|
|
89
|
-
selected: activeFilter === "issues"
|
|
90
|
-
},
|
|
91
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
92
|
-
style: [styles.statValue, activeFilter === "issues" && styles.statValueActive],
|
|
93
|
-
testID: "buoy-env-filter-issues-count",
|
|
94
|
-
children: issuesCount
|
|
95
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
96
|
-
style: [styles.statLabel, activeFilter === "issues" && styles.statLabelActive],
|
|
97
|
-
children: "ISSUES"
|
|
98
|
-
})]
|
|
99
|
-
})]
|
|
100
|
-
})]
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
const styles = _reactNative.StyleSheet.create({
|
|
104
|
-
container: {
|
|
105
|
-
gap: 8
|
|
106
|
-
},
|
|
107
|
-
statsGrid: {
|
|
108
|
-
flexDirection: "row",
|
|
109
|
-
gap: 8,
|
|
110
|
-
paddingHorizontal: 4
|
|
111
|
-
},
|
|
112
|
-
statCard: {
|
|
113
|
-
flex: 1,
|
|
114
|
-
backgroundColor: _sharedUi.night.color.surface,
|
|
115
|
-
borderRadius: 10,
|
|
116
|
-
borderWidth: 1,
|
|
117
|
-
borderColor: _sharedUi.night.color.border,
|
|
118
|
-
padding: 12,
|
|
119
|
-
alignItems: "center",
|
|
120
|
-
justifyContent: "center",
|
|
121
|
-
minHeight: 64
|
|
122
|
-
},
|
|
123
|
-
statCardActive: {
|
|
124
|
-
borderColor: _sharedUi.night.color.accentBorderStrong,
|
|
125
|
-
backgroundColor: _sharedUi.night.color.accentSoft
|
|
126
|
-
},
|
|
127
|
-
statValue: {
|
|
128
|
-
fontSize: 22,
|
|
129
|
-
fontWeight: "700",
|
|
130
|
-
fontFamily: _sharedUi.monoFont,
|
|
131
|
-
lineHeight: 26,
|
|
132
|
-
color: _sharedUi.night.color.text
|
|
133
|
-
},
|
|
134
|
-
statValueActive: {
|
|
135
|
-
color: _sharedUi.night.color.accent
|
|
136
|
-
},
|
|
137
|
-
statLabel: {
|
|
138
|
-
fontSize: 9,
|
|
139
|
-
color: _sharedUi.night.color.textTertiary,
|
|
140
|
-
marginTop: 4,
|
|
141
|
-
textTransform: "uppercase",
|
|
142
|
-
letterSpacing: 0.5,
|
|
143
|
-
fontWeight: "600"
|
|
144
|
-
},
|
|
145
|
-
statLabelActive: {
|
|
146
|
-
color: _sharedUi.night.color.accent
|
|
147
|
-
},
|
|
148
|
-
percentBadge: {
|
|
149
|
-
paddingHorizontal: 8,
|
|
150
|
-
paddingVertical: 4,
|
|
151
|
-
borderRadius: 6,
|
|
152
|
-
borderWidth: 1
|
|
153
|
-
},
|
|
154
|
-
percentText: {
|
|
155
|
-
fontSize: 14,
|
|
156
|
-
fontWeight: "700",
|
|
157
|
-
fontFamily: _sharedUi.monoFont
|
|
158
|
-
}
|
|
159
|
-
});
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.EnvStatsOverview=EnvStatsOverview;var _reactNative=require("react-native");var _sharedUi=require("@buoy-gg/shared-ui");var _jsxRuntime=require("react/jsx-runtime");function EnvStatsOverview({stats:e,healthPercentage:i,healthStatus:o,healthColor:s,activeFilter:t="all",onFilterChange:a}){const r=e.wrongValueCount+e.wrongTypeCount;return(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.container,children:[(0,_jsxRuntime.jsx)(_sharedUi.CompactRow,{statusDotColor:s,statusLabel:"System",statusSublabel:o.toLowerCase(),primaryText:"Environment Configuration",secondaryText:`${i}% healthy`,testID:"buoy-env-health",statusSublabelTestID:"buoy-env-health-status",accessibilityLabel:`Environment configuration ${o.toLowerCase()}, ${i}% healthy`,customBadge:(0,_jsxRuntime.jsx)(_reactNative.View,{style:[styles.percentBadge,{borderColor:s+"40",backgroundColor:s+"10"}],children:(0,_jsxRuntime.jsxs)(_reactNative.Text,{style:[styles.percentText,{color:s}],testID:"buoy-env-health-percent",children:[i,"%"]})})}),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.statsGrid,children:[(0,_jsxRuntime.jsxs)(_reactNative.TouchableOpacity,{style:[styles.statCard,t==="all"&&styles.statCardActive],onPress:()=>a?.("all"),activeOpacity:.8,testID:"buoy-env-filter-all",accessibilityRole:"button",accessibilityLabel:"Show all variables",accessibilityState:{selected:t==="all"},children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.statValue,t==="all"&&styles.statValueActive],testID:"buoy-env-filter-all-count",children:e.requiredCount+e.optionalCount}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.statLabel,t==="all"&&styles.statLabelActive],children:"ALL"})]}),(0,_jsxRuntime.jsxs)(_reactNative.TouchableOpacity,{style:[styles.statCard,t==="missing"&&styles.statCardActive],onPress:()=>a?.("missing"),activeOpacity:.8,testID:"buoy-env-filter-missing",accessibilityRole:"button",accessibilityLabel:"Show missing variables",accessibilityState:{selected:t==="missing"},children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.statValue,t==="missing"&&styles.statValueActive],testID:"buoy-env-filter-missing-count",children:e.missingCount}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.statLabel,t==="missing"&&styles.statLabelActive],children:"MISSING"})]}),(0,_jsxRuntime.jsxs)(_reactNative.TouchableOpacity,{style:[styles.statCard,t==="issues"&&styles.statCardActive],onPress:()=>a?.("issues"),activeOpacity:.8,testID:"buoy-env-filter-issues",accessibilityRole:"button",accessibilityLabel:"Show variables with issues",accessibilityState:{selected:t==="issues"},children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.statValue,t==="issues"&&styles.statValueActive],testID:"buoy-env-filter-issues-count",children:r}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.statLabel,t==="issues"&&styles.statLabelActive],children:"ISSUES"})]})]})]})}const styles=_reactNative.StyleSheet.create({container:{gap:8},statsGrid:{flexDirection:"row",gap:8,paddingHorizontal:4},statCard:{flex:1,backgroundColor:_sharedUi.night.color.surface,borderRadius:10,borderWidth:1,borderColor:_sharedUi.night.color.border,padding:12,alignItems:"center",justifyContent:"center",minHeight:64},statCardActive:{borderColor:_sharedUi.night.color.accentBorderStrong,backgroundColor:_sharedUi.night.color.accentSoft},statValue:{fontSize:22,fontWeight:"700",fontFamily:_sharedUi.monoFont,lineHeight:26,color:_sharedUi.night.color.text},statValueActive:{color:_sharedUi.night.color.accent},statLabel:{fontSize:9,color:_sharedUi.night.color.textTertiary,marginTop:4,textTransform:"uppercase",letterSpacing:.5,fontWeight:"600"},statLabelActive:{color:_sharedUi.night.color.accent},percentBadge:{paddingHorizontal:8,paddingVertical:4,borderRadius:6,borderWidth:1},percentText:{fontSize:14,fontWeight:"700",fontFamily:_sharedUi.monoFont}});
|
|
@@ -1,189 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.EnvVarRow = EnvVarRow;
|
|
7
|
-
var _reactNative = require("react-native");
|
|
8
|
-
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
9
|
-
var _envTypeDetector = require("../utils/envTypeDetector");
|
|
10
|
-
var _jsxRuntime = require("react/jsx-runtime");
|
|
11
|
-
const getStatusConfig = (status, expectedType) => {
|
|
12
|
-
switch (status) {
|
|
13
|
-
case "required_present":
|
|
14
|
-
return {
|
|
15
|
-
label: "Valid",
|
|
16
|
-
color: _sharedUi.buoyColors.success,
|
|
17
|
-
sublabel: "Required"
|
|
18
|
-
};
|
|
19
|
-
case "required_missing":
|
|
20
|
-
return {
|
|
21
|
-
label: "Missing",
|
|
22
|
-
color: _sharedUi.buoyColors.error,
|
|
23
|
-
sublabel: "Required"
|
|
24
|
-
};
|
|
25
|
-
case "required_wrong_value":
|
|
26
|
-
return {
|
|
27
|
-
label: "Wrong",
|
|
28
|
-
color: _sharedUi.buoyColors.warning,
|
|
29
|
-
sublabel: "Invalid value"
|
|
30
|
-
};
|
|
31
|
-
case "required_wrong_type":
|
|
32
|
-
return {
|
|
33
|
-
label: "Type Error",
|
|
34
|
-
color: _sharedUi.buoyColors.info,
|
|
35
|
-
sublabel: "Wrong type"
|
|
36
|
-
};
|
|
37
|
-
case "optional_present":
|
|
38
|
-
return {
|
|
39
|
-
label: "Set",
|
|
40
|
-
color: _sharedUi.buoyColors.textSecondary,
|
|
41
|
-
sublabel: "Optional"
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
const formatEnvKey = key => {
|
|
46
|
-
// Format key similar to React Query: "section > subsection"
|
|
47
|
-
return key.split("_").join(" › ");
|
|
48
|
-
};
|
|
49
|
-
const formatValue = value => {
|
|
50
|
-
if (value === undefined || value === null) {
|
|
51
|
-
return "undefined";
|
|
52
|
-
}
|
|
53
|
-
const str = typeof value === "string" ? value : String(value);
|
|
54
|
-
return str;
|
|
55
|
-
};
|
|
56
|
-
function EnvVarRow({
|
|
57
|
-
envVar,
|
|
58
|
-
isExpanded,
|
|
59
|
-
onPress
|
|
60
|
-
}) {
|
|
61
|
-
const config = getStatusConfig(envVar.status, envVar.expectedType);
|
|
62
|
-
const hasValue = envVar.value !== undefined && envVar.value !== null;
|
|
63
|
-
|
|
64
|
-
// Format primary text like React Query does: "section › subsection"
|
|
65
|
-
// For env vars, we'll show the key formatted nicely
|
|
66
|
-
const keyParts = envVar.key.split("_");
|
|
67
|
-
const primaryText = keyParts.map(part => part.toLowerCase()).join(" › ");
|
|
68
|
-
|
|
69
|
-
// The key is the row's identity everywhere a handle is needed. The visible
|
|
70
|
-
// primary text is the prettified "expo › public › api › url" form, which is
|
|
71
|
-
// no use to a test (or to VoiceOver) trying to find one specific variable.
|
|
72
|
-
const rowID = `buoy-env-row-${envVar.key}`;
|
|
73
|
-
|
|
74
|
-
// Create expanded content for value and expected value
|
|
75
|
-
const expandedContent = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
76
|
-
style: styles.expandedContainer,
|
|
77
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
78
|
-
style: styles.expandedRow,
|
|
79
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
80
|
-
style: styles.expandedLabel,
|
|
81
|
-
children: "Value:"
|
|
82
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
83
|
-
style: styles.expandedValue,
|
|
84
|
-
numberOfLines: 3,
|
|
85
|
-
testID: `${rowID}-value`,
|
|
86
|
-
children: formatValue(envVar.value) || "undefined"
|
|
87
|
-
})]
|
|
88
|
-
}), envVar.status === "required_wrong_type" && envVar.expectedType && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
89
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
90
|
-
style: styles.expandedRow,
|
|
91
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
92
|
-
style: styles.expandedLabel,
|
|
93
|
-
children: "Type:"
|
|
94
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.TypeBadge, {
|
|
95
|
-
type: (0, _envTypeDetector.getEnvVarType)(envVar.value),
|
|
96
|
-
testID: `${rowID}-actual-type`
|
|
97
|
-
})]
|
|
98
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
99
|
-
style: styles.expandedRow,
|
|
100
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
101
|
-
style: styles.expandedLabel,
|
|
102
|
-
children: "Expected:"
|
|
103
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.TypeBadge, {
|
|
104
|
-
type: envVar.expectedType,
|
|
105
|
-
testID: `${rowID}-expected-type`
|
|
106
|
-
})]
|
|
107
|
-
})]
|
|
108
|
-
}), envVar.status === "required_wrong_value" && envVar.expectedValue && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
109
|
-
style: styles.expandedRow,
|
|
110
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
111
|
-
style: styles.expandedLabel,
|
|
112
|
-
children: "Expected:"
|
|
113
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
114
|
-
style: styles.expandedExpected,
|
|
115
|
-
testID: `${rowID}-expected-value`,
|
|
116
|
-
children: String(envVar.expectedValue)
|
|
117
|
-
})]
|
|
118
|
-
}), envVar.description && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
119
|
-
style: styles.expandedRow,
|
|
120
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
121
|
-
style: styles.expandedLabel,
|
|
122
|
-
children: "Info:"
|
|
123
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
124
|
-
style: styles.expandedDescription,
|
|
125
|
-
testID: `${rowID}-description`,
|
|
126
|
-
children: envVar.description
|
|
127
|
-
})]
|
|
128
|
-
})]
|
|
129
|
-
});
|
|
130
|
-
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.CompactRow, {
|
|
131
|
-
statusDotColor: config.color,
|
|
132
|
-
statusLabel: config.label,
|
|
133
|
-
statusSublabel: config.sublabel,
|
|
134
|
-
primaryText: primaryText,
|
|
135
|
-
secondaryText: undefined // Don't show value inline anymore
|
|
136
|
-
,
|
|
137
|
-
expandedContent: expandedContent,
|
|
138
|
-
isExpanded: isExpanded,
|
|
139
|
-
expandedGlowColor: config.color,
|
|
140
|
-
customBadge: envVar.expectedType ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.TypeBadge, {
|
|
141
|
-
type: envVar.expectedType,
|
|
142
|
-
testID: `${rowID}-type-badge`
|
|
143
|
-
}) : undefined,
|
|
144
|
-
showChevron: true,
|
|
145
|
-
onPress: onPress ? () => onPress(envVar) : undefined,
|
|
146
|
-
testID: rowID,
|
|
147
|
-
statusTestID: `${rowID}-status`,
|
|
148
|
-
statusSublabelTestID: `${rowID}-category`
|
|
149
|
-
// The status is carried by a coloured dot and a two-word label that the
|
|
150
|
-
// row's container merges away on Fabric — spelled out here so the verdict
|
|
151
|
-
// reaches assistive tech (and Maestro) with the key it belongs to.
|
|
152
|
-
,
|
|
153
|
-
accessibilityLabel: `${envVar.key}, ${config.label}, ${config.sublabel}`
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
const styles = _reactNative.StyleSheet.create({
|
|
157
|
-
expandedContainer: {
|
|
158
|
-
gap: 6
|
|
159
|
-
},
|
|
160
|
-
expandedRow: {
|
|
161
|
-
flexDirection: "row",
|
|
162
|
-
alignItems: "center",
|
|
163
|
-
gap: 8
|
|
164
|
-
},
|
|
165
|
-
expandedLabel: {
|
|
166
|
-
fontSize: 10,
|
|
167
|
-
color: _sharedUi.night.color.textTertiary,
|
|
168
|
-
fontWeight: "600",
|
|
169
|
-
minWidth: 60,
|
|
170
|
-
fontFamily: _sharedUi.monoFont
|
|
171
|
-
},
|
|
172
|
-
expandedValue: {
|
|
173
|
-
fontSize: 11,
|
|
174
|
-
color: _sharedUi.night.color.textSecondary,
|
|
175
|
-
fontFamily: _sharedUi.monoFont,
|
|
176
|
-
flex: 1
|
|
177
|
-
},
|
|
178
|
-
expandedExpected: {
|
|
179
|
-
fontSize: 11,
|
|
180
|
-
color: _sharedUi.buoyColors.warning,
|
|
181
|
-
fontFamily: _sharedUi.monoFont,
|
|
182
|
-
flex: 1
|
|
183
|
-
},
|
|
184
|
-
expandedDescription: {
|
|
185
|
-
fontSize: 11,
|
|
186
|
-
color: _sharedUi.night.color.textSecondary,
|
|
187
|
-
flex: 1
|
|
188
|
-
}
|
|
189
|
-
});
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.EnvVarRow=EnvVarRow;var _reactNative=require("react-native");var _sharedUi=require("@buoy-gg/shared-ui");var _envTypeDetector=require("../utils/envTypeDetector");var _jsxRuntime=require("react/jsx-runtime");const getStatusConfig=(e,s)=>{switch(e){case"required_present":return{label:"Valid",color:_sharedUi.buoyColors.success,sublabel:"Required"};case"required_missing":return{label:"Missing",color:_sharedUi.buoyColors.error,sublabel:"Required"};case"required_wrong_value":return{label:"Wrong",color:_sharedUi.buoyColors.warning,sublabel:"Invalid value"};case"required_wrong_type":return{label:"Type Error",color:_sharedUi.buoyColors.info,sublabel:"Wrong type"};case"optional_present":return{label:"Set",color:_sharedUi.buoyColors.textSecondary,sublabel:"Optional"}}};const formatEnvKey=e=>{return e.split("_").join(" \u203A ")};const formatValue=e=>{if(e===void 0||e===null){return"undefined"}const s=typeof e==="string"?e:String(e);return s};function EnvVarRow({envVar:e,isExpanded:s,onPress:n}){const o=getStatusConfig(e.status,e.expectedType);const i=e.value!==void 0&&e.value!==null;const r=e.key.split("_");const l=r.map(a=>a.toLowerCase()).join(" \u203A ");const t=`buoy-env-row-${e.key}`;const d=(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.expandedContainer,children:[(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.expandedRow,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.expandedLabel,children:"Value:"}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.expandedValue,numberOfLines:3,testID:`${t}-value`,children:formatValue(e.value)||"undefined"})]}),e.status==="required_wrong_type"&&e.expectedType&&(0,_jsxRuntime.jsxs)(_jsxRuntime.Fragment,{children:[(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.expandedRow,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.expandedLabel,children:"Type:"}),(0,_jsxRuntime.jsx)(_sharedUi.TypeBadge,{type:(0,_envTypeDetector.getEnvVarType)(e.value),testID:`${t}-actual-type`})]}),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.expandedRow,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.expandedLabel,children:"Expected:"}),(0,_jsxRuntime.jsx)(_sharedUi.TypeBadge,{type:e.expectedType,testID:`${t}-expected-type`})]})]}),e.status==="required_wrong_value"&&e.expectedValue&&(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.expandedRow,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.expandedLabel,children:"Expected:"}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.expandedExpected,testID:`${t}-expected-value`,children:String(e.expectedValue)})]}),e.description&&(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.expandedRow,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.expandedLabel,children:"Info:"}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.expandedDescription,testID:`${t}-description`,children:e.description})]})]});return(0,_jsxRuntime.jsx)(_sharedUi.CompactRow,{statusDotColor:o.color,statusLabel:o.label,statusSublabel:o.sublabel,primaryText:l,secondaryText:void 0,expandedContent:d,isExpanded:s,expandedGlowColor:o.color,customBadge:e.expectedType?(0,_jsxRuntime.jsx)(_sharedUi.TypeBadge,{type:e.expectedType,testID:`${t}-type-badge`}):void 0,showChevron:true,onPress:n?()=>n(e):void 0,testID:t,statusTestID:`${t}-status`,statusSublabelTestID:`${t}-category`,accessibilityLabel:`${e.key}, ${o.label}, ${o.sublabel}`})}const styles=_reactNative.StyleSheet.create({expandedContainer:{gap:6},expandedRow:{flexDirection:"row",alignItems:"center",gap:8},expandedLabel:{fontSize:10,color:_sharedUi.night.color.textTertiary,fontWeight:"600",minWidth:60,fontFamily:_sharedUi.monoFont},expandedValue:{fontSize:11,color:_sharedUi.night.color.textSecondary,fontFamily:_sharedUi.monoFont,flex:1},expandedExpected:{fontSize:11,color:_sharedUi.buoyColors.warning,fontFamily:_sharedUi.monoFont,flex:1},expandedDescription:{fontSize:11,color:_sharedUi.night.color.textSecondary,flex:1}});
|
|
@@ -1,84 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.EnvVarSection = EnvVarSection;
|
|
7
|
-
var _react = require("react");
|
|
8
|
-
var _reactNative = require("react-native");
|
|
9
|
-
var _EnvVarRow = require("./EnvVarRow");
|
|
10
|
-
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
11
|
-
var _jsxRuntime = require("react/jsx-runtime");
|
|
12
|
-
function EnvVarSection({
|
|
13
|
-
title,
|
|
14
|
-
count,
|
|
15
|
-
vars,
|
|
16
|
-
emptyMessage
|
|
17
|
-
}) {
|
|
18
|
-
const [expandedVar, setExpandedVar] = (0, _react.useState)(null);
|
|
19
|
-
const handleVarPress = (0, _react.useCallback)(envVar => {
|
|
20
|
-
setExpandedVar(prev => prev === envVar.key ? null : envVar.key);
|
|
21
|
-
}, []);
|
|
22
|
-
if (vars.length === 0 && title === "Required Variables") {
|
|
23
|
-
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
24
|
-
style: styles.sectionContainer,
|
|
25
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.SectionHeader, {
|
|
26
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Title, {
|
|
27
|
-
children: title
|
|
28
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Badge, {
|
|
29
|
-
count: 0,
|
|
30
|
-
color: _sharedUi.night.color.accent
|
|
31
|
-
})]
|
|
32
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
33
|
-
style: styles.emptySection,
|
|
34
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
35
|
-
style: styles.emptySectionText,
|
|
36
|
-
children: emptyMessage
|
|
37
|
-
})
|
|
38
|
-
})]
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
if (vars.length === 0) return null;
|
|
42
|
-
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
43
|
-
style: styles.sectionContainer,
|
|
44
|
-
children: [title !== "" && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.SectionHeader, {
|
|
45
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Title, {
|
|
46
|
-
children: title
|
|
47
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Badge, {
|
|
48
|
-
count: count,
|
|
49
|
-
color: _sharedUi.night.color.accent
|
|
50
|
-
})]
|
|
51
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
52
|
-
style: styles.sectionContent,
|
|
53
|
-
children: vars.map(envVar => /*#__PURE__*/(0, _jsxRuntime.jsx)(_EnvVarRow.EnvVarRow, {
|
|
54
|
-
envVar: envVar,
|
|
55
|
-
isExpanded: expandedVar === envVar.key,
|
|
56
|
-
onPress: handleVarPress
|
|
57
|
-
}, envVar.key))
|
|
58
|
-
})]
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
const styles = _reactNative.StyleSheet.create({
|
|
62
|
-
sectionContainer: {
|
|
63
|
-
gap: 8
|
|
64
|
-
},
|
|
65
|
-
sectionContent: {
|
|
66
|
-
// No gap needed, EnvVarRow has its own margins
|
|
67
|
-
},
|
|
68
|
-
emptySection: {
|
|
69
|
-
padding: 20,
|
|
70
|
-
backgroundColor: _sharedUi.night.color.accent + "08",
|
|
71
|
-
borderRadius: 8,
|
|
72
|
-
borderWidth: 1,
|
|
73
|
-
borderColor: _sharedUi.night.color.accent + "20",
|
|
74
|
-
alignItems: "center"
|
|
75
|
-
},
|
|
76
|
-
emptySectionText: {
|
|
77
|
-
color: _sharedUi.night.color.accent,
|
|
78
|
-
fontSize: 11,
|
|
79
|
-
textAlign: "center",
|
|
80
|
-
fontFamily: _sharedUi.monoFont,
|
|
81
|
-
opacity: 0.8,
|
|
82
|
-
letterSpacing: 0.5
|
|
83
|
-
}
|
|
84
|
-
});
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.EnvVarSection=EnvVarSection;var _react=require("react");var _reactNative=require("react-native");var _EnvVarRow=require("./EnvVarRow");var _sharedUi=require("@buoy-gg/shared-ui");var _jsxRuntime=require("react/jsx-runtime");function EnvVarSection({title:t,count:r,vars:n,emptyMessage:o}){const[i,c]=(0,_react.useState)(null);const a=(0,_react.useCallback)(e=>{c(s=>s===e.key?null:e.key)},[]);if(n.length===0&&t==="Required Variables"){return(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.sectionContainer,children:[(0,_jsxRuntime.jsxs)(_sharedUi.SectionHeader,{children:[(0,_jsxRuntime.jsx)(_sharedUi.SectionHeader.Title,{children:t}),(0,_jsxRuntime.jsx)(_sharedUi.SectionHeader.Badge,{count:0,color:_sharedUi.night.color.accent})]}),(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.emptySection,children:(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.emptySectionText,children:o})})]})}if(n.length===0)return null;return(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.sectionContainer,children:[t!==""&&(0,_jsxRuntime.jsxs)(_sharedUi.SectionHeader,{children:[(0,_jsxRuntime.jsx)(_sharedUi.SectionHeader.Title,{children:t}),(0,_jsxRuntime.jsx)(_sharedUi.SectionHeader.Badge,{count:r,color:_sharedUi.night.color.accent})]}),(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.sectionContent,children:n.map(e=>(0,_jsxRuntime.jsx)(_EnvVarRow.EnvVarRow,{envVar:e,isExpanded:i===e.key,onPress:a},e.key))})]})}const styles=_reactNative.StyleSheet.create({sectionContainer:{gap:8},sectionContent:{},emptySection:{padding:20,backgroundColor:_sharedUi.night.color.accent+"08",borderRadius:8,borderWidth:1,borderColor:_sharedUi.night.color.accent+"20",alignItems:"center"},emptySectionText:{color:_sharedUi.night.color.accent,fontSize:11,textAlign:"center",fontFamily:_sharedUi.monoFont,opacity:.8,letterSpacing:.5}});
|