rn-backstage 1.4.7 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +43 -0
  2. package/lib/commonjs/Backstage.js +3 -1
  3. package/lib/commonjs/Backstage.js.map +1 -1
  4. package/lib/commonjs/bug-report.js.map +1 -1
  5. package/lib/commonjs/components/BackstagePanel.js +14 -2
  6. package/lib/commonjs/components/BackstagePanel.js.map +1 -1
  7. package/lib/commonjs/components/BugReportComposer.js.map +1 -1
  8. package/lib/commonjs/components/EnvironmentTab.js +746 -0
  9. package/lib/commonjs/components/EnvironmentTab.js.map +1 -0
  10. package/lib/commonjs/components/FlagsTab.js.map +1 -1
  11. package/lib/commonjs/components/FloatingPill.js.map +1 -1
  12. package/lib/commonjs/components/StorageTab.js.map +1 -1
  13. package/lib/commonjs/constants.js +17 -0
  14. package/lib/commonjs/constants.js.map +1 -1
  15. package/lib/commonjs/index.js +7 -0
  16. package/lib/commonjs/index.js.map +1 -1
  17. package/lib/commonjs/types.js +1 -0
  18. package/lib/commonjs/types.js.map +1 -1
  19. package/lib/module/Backstage.js +3 -1
  20. package/lib/module/Backstage.js.map +1 -1
  21. package/lib/module/bug-report.js.map +1 -1
  22. package/lib/module/components/BackstagePanel.js +14 -2
  23. package/lib/module/components/BackstagePanel.js.map +1 -1
  24. package/lib/module/components/BugReportComposer.js.map +1 -1
  25. package/lib/module/components/EnvironmentTab.js +741 -0
  26. package/lib/module/components/EnvironmentTab.js.map +1 -0
  27. package/lib/module/components/FlagsTab.js.map +1 -1
  28. package/lib/module/components/FloatingPill.js.map +1 -1
  29. package/lib/module/components/StorageTab.js.map +1 -1
  30. package/lib/module/constants.js +17 -0
  31. package/lib/module/constants.js.map +1 -1
  32. package/lib/module/index.js +1 -0
  33. package/lib/module/index.js.map +1 -1
  34. package/lib/module/types.js +2 -0
  35. package/lib/module/types.js.map +1 -1
  36. package/lib/typescript/Backstage.d.ts.map +1 -1
  37. package/lib/typescript/bug-report.d.ts.map +1 -1
  38. package/lib/typescript/components/BackstagePanel.d.ts +2 -1
  39. package/lib/typescript/components/BackstagePanel.d.ts.map +1 -1
  40. package/lib/typescript/components/BugReportComposer.d.ts.map +1 -1
  41. package/lib/typescript/components/EnvironmentTab.d.ts +8 -0
  42. package/lib/typescript/components/EnvironmentTab.d.ts.map +1 -0
  43. package/lib/typescript/components/FlagsTab.d.ts.map +1 -1
  44. package/lib/typescript/components/FloatingPill.d.ts.map +1 -1
  45. package/lib/typescript/components/StorageTab.d.ts.map +1 -1
  46. package/lib/typescript/constants.d.ts +16 -0
  47. package/lib/typescript/constants.d.ts.map +1 -1
  48. package/lib/typescript/index.d.ts +2 -1
  49. package/lib/typescript/index.d.ts.map +1 -1
  50. package/lib/typescript/types.d.ts +48 -0
  51. package/lib/typescript/types.d.ts.map +1 -1
  52. package/package.json +2 -2
@@ -0,0 +1,746 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.EnvironmentTab = void 0;
7
+ var _react = _interopRequireWildcard(require("react"));
8
+ var _reactNative = require("react-native");
9
+ var _constants = require("../constants");
10
+ var _ThemeContext = require("../ThemeContext");
11
+ var _jsxRuntime = require("react/jsx-runtime");
12
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
13
+ // ─── Constants ───────────────────────────────────────────────────────────────
14
+
15
+ const STORAGE_KEY_PREFIX = 'backstage.env.credentials.';
16
+
17
+ // ─── Credential Editor Modal ─────────────────────────────────────────────────
18
+
19
+ const EditorModal = ({
20
+ visible,
21
+ title,
22
+ credentialName,
23
+ fieldValues,
24
+ credentialFields = [],
25
+ onChangeName,
26
+ onChangeField,
27
+ onSave,
28
+ onCancel,
29
+ theme
30
+ }) => {
31
+ const s = (0, _react.useMemo)(() => createStyles(theme), [theme]);
32
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Modal, {
33
+ testID: _constants.TestIDs.environmentTab.editorModal,
34
+ visible: visible,
35
+ animationType: "slide",
36
+ transparent: false,
37
+ onRequestClose: onCancel,
38
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.SafeAreaView, {
39
+ style: s.modalContainer,
40
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
41
+ style: s.modalHeader,
42
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
43
+ testID: _constants.TestIDs.environmentTab.editorCancelButton,
44
+ onPress: onCancel,
45
+ hitSlop: {
46
+ top: 8,
47
+ bottom: 8,
48
+ left: 8,
49
+ right: 8
50
+ },
51
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
52
+ style: s.modalCancelText,
53
+ children: "Cancel"
54
+ })
55
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
56
+ style: s.modalTitle,
57
+ children: title
58
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
59
+ testID: _constants.TestIDs.environmentTab.editorSaveButton,
60
+ onPress: onSave,
61
+ hitSlop: {
62
+ top: 8,
63
+ bottom: 8,
64
+ left: 8,
65
+ right: 8
66
+ },
67
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
68
+ style: s.modalSaveText,
69
+ children: "Save"
70
+ })
71
+ })]
72
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.KeyboardAvoidingView, {
73
+ style: s.modalBody,
74
+ behavior: _reactNative.Platform.OS === 'ios' ? 'padding' : undefined,
75
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
76
+ contentContainerStyle: s.modalBodyContent,
77
+ keyboardShouldPersistTaps: "handled",
78
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
79
+ style: s.modalLabel,
80
+ children: "Name"
81
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
82
+ testID: _constants.TestIDs.environmentTab.editorNameInput,
83
+ style: s.modalInput,
84
+ value: credentialName,
85
+ onChangeText: onChangeName,
86
+ placeholder: "e.g., QA Account, Admin",
87
+ placeholderTextColor: theme.textMuted,
88
+ autoCapitalize: "words",
89
+ autoFocus: true
90
+ }), credentialFields.map(field => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
91
+ style: {
92
+ marginTop: 16
93
+ },
94
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
95
+ style: s.modalLabel,
96
+ children: field.label
97
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
98
+ testID: _constants.TestIDs.environmentTab.editorFieldInput(field.key),
99
+ style: [s.modalInput, field.secureTextEntry ? undefined : undefined],
100
+ value: fieldValues[field.key] || '',
101
+ onChangeText: val => onChangeField(field.key, val),
102
+ placeholder: field.placeholder || field.label,
103
+ placeholderTextColor: theme.textMuted,
104
+ secureTextEntry: field.secureTextEntry,
105
+ autoCapitalize: field.autoCapitalize ?? 'none',
106
+ autoCorrect: false,
107
+ keyboardType: field.keyboardType
108
+ })]
109
+ }, field.key))]
110
+ })
111
+ })]
112
+ })
113
+ });
114
+ };
115
+
116
+ // ─── Credential Row ──────────────────────────────────────────────────────────
117
+
118
+ const CredentialRow = ({
119
+ envKey,
120
+ index,
121
+ credential,
122
+ envColor,
123
+ onLogin,
124
+ onEdit,
125
+ onDelete,
126
+ loggingIn,
127
+ theme
128
+ }) => {
129
+ const s = (0, _react.useMemo)(() => createStyles(theme), [theme]);
130
+ const fieldCount = Object.keys(credential.values).length;
131
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
132
+ testID: _constants.TestIDs.environmentTab.credentialRow(envKey, index),
133
+ style: s.credRow,
134
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
135
+ style: s.credRowLeft,
136
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
137
+ style: s.credName,
138
+ numberOfLines: 1,
139
+ children: credential.name
140
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
141
+ style: s.credMeta,
142
+ children: [fieldCount, " field", fieldCount !== 1 ? 's' : '']
143
+ })]
144
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
145
+ style: s.credRowActions,
146
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
147
+ testID: _constants.TestIDs.environmentTab.credentialLogin(envKey, index),
148
+ style: [s.credActionButton, {
149
+ backgroundColor: envColor
150
+ }],
151
+ onPress: onLogin,
152
+ disabled: loggingIn,
153
+ activeOpacity: 0.7,
154
+ hitSlop: {
155
+ top: 4,
156
+ bottom: 4,
157
+ left: 4,
158
+ right: 4
159
+ },
160
+ children: loggingIn ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
161
+ size: "small",
162
+ color: "#FFF"
163
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
164
+ style: s.credActionLoginText,
165
+ children: "Login"
166
+ })
167
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
168
+ testID: _constants.TestIDs.environmentTab.credentialEdit(envKey, index),
169
+ onPress: onEdit,
170
+ hitSlop: {
171
+ top: 8,
172
+ bottom: 8,
173
+ left: 8,
174
+ right: 8
175
+ },
176
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
177
+ style: s.credActionIcon,
178
+ children: "\u270E"
179
+ })
180
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
181
+ testID: _constants.TestIDs.environmentTab.credentialDelete(envKey, index),
182
+ onPress: onDelete,
183
+ hitSlop: {
184
+ top: 8,
185
+ bottom: 8,
186
+ left: 8,
187
+ right: 8
188
+ },
189
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
190
+ style: [s.credActionIcon, s.credDeleteIcon],
191
+ children: "\u2715"
192
+ })
193
+ })]
194
+ })]
195
+ });
196
+ };
197
+
198
+ // ─── Main Component ──────────────────────────────────────────────────────────
199
+
200
+ const EnvironmentTab = ({
201
+ config
202
+ }) => {
203
+ const {
204
+ environments,
205
+ activeEnvironment,
206
+ onEnvironmentChange,
207
+ credentialFields = [],
208
+ onLogin,
209
+ initialCredentials,
210
+ storageAdapter
211
+ } = config;
212
+ const theme = (0, _ThemeContext.useBackstageTheme)();
213
+ const s = (0, _react.useMemo)(() => createStyles(theme), [theme]);
214
+
215
+ // ── Credentials state: Record<envKey, SavedCredential[]> ──
216
+ const [credentials, setCredentials] = (0, _react.useState)(() => {
217
+ const init = {};
218
+ for (const env of environments) {
219
+ init[env.key] = initialCredentials?.[env.key] ? [...initialCredentials[env.key]] : [];
220
+ }
221
+ return init;
222
+ });
223
+ const [loadingCredentials, setLoadingCredentials] = (0, _react.useState)(!!storageAdapter);
224
+ const [loggingInKey, setLoggingInKey] = (0, _react.useState)(null);
225
+
226
+ // ── Editor modal state ─────────────────────────────────────
227
+ const [editorVisible, setEditorVisible] = (0, _react.useState)(false);
228
+ const [editorEnvKey, setEditorEnvKey] = (0, _react.useState)('');
229
+ const [editorIndex, setEditorIndex] = (0, _react.useState)(null); // null = add new
230
+ const [editorName, setEditorName] = (0, _react.useState)('');
231
+ const [editorFields, setEditorFields] = (0, _react.useState)({});
232
+
233
+ // ── Load persisted credentials on mount ────────────────────
234
+ (0, _react.useEffect)(() => {
235
+ if (!storageAdapter) return;
236
+ let cancelled = false;
237
+ const load = async () => {
238
+ try {
239
+ for (const env of environments) {
240
+ const stored = await storageAdapter.getItem(STORAGE_KEY_PREFIX + env.key);
241
+ if (stored && !cancelled) {
242
+ try {
243
+ const parsed = JSON.parse(stored);
244
+ if (Array.isArray(parsed)) {
245
+ setCredentials(prev => ({
246
+ ...prev,
247
+ [env.key]: parsed
248
+ }));
249
+ }
250
+ } catch {
251
+ // Invalid JSON — ignore
252
+ }
253
+ }
254
+ }
255
+ } finally {
256
+ if (!cancelled) setLoadingCredentials(false);
257
+ }
258
+ };
259
+ load();
260
+ return () => {
261
+ cancelled = true;
262
+ };
263
+ }, [storageAdapter, environments]);
264
+
265
+ // ── Persist credentials to storage ─────────────────────────
266
+ const persistCredentials = (0, _react.useCallback)((envKey, creds) => {
267
+ if (!storageAdapter) return;
268
+ storageAdapter.setItem(STORAGE_KEY_PREFIX + envKey, JSON.stringify(creds)).catch(() => {});
269
+ }, [storageAdapter]);
270
+
271
+ // ── Editor open/close ──────────────────────────────────────
272
+ const openAddEditor = (0, _react.useCallback)(envKey => {
273
+ setEditorEnvKey(envKey);
274
+ setEditorIndex(null);
275
+ setEditorName('');
276
+ setEditorFields({});
277
+ setEditorVisible(true);
278
+ }, []);
279
+ const openEditEditor = (0, _react.useCallback)((envKey, index) => {
280
+ const cred = credentials[envKey]?.[index];
281
+ if (!cred) return;
282
+ setEditorEnvKey(envKey);
283
+ setEditorIndex(index);
284
+ setEditorName(cred.name);
285
+ setEditorFields({
286
+ ...cred.values
287
+ });
288
+ setEditorVisible(true);
289
+ }, [credentials]);
290
+ const closeEditor = (0, _react.useCallback)(() => {
291
+ setEditorVisible(false);
292
+ setEditorEnvKey('');
293
+ setEditorIndex(null);
294
+ setEditorName('');
295
+ setEditorFields({});
296
+ }, []);
297
+ const saveEditor = (0, _react.useCallback)(() => {
298
+ const trimmedName = editorName.trim();
299
+ if (!trimmedName) {
300
+ _reactNative.Alert.alert('Error', 'Credential name cannot be empty');
301
+ return;
302
+ }
303
+ const newCred = {
304
+ name: trimmedName,
305
+ values: {
306
+ ...editorFields
307
+ }
308
+ };
309
+ setCredentials(prev => {
310
+ const envCreds = [...(prev[editorEnvKey] || [])];
311
+ if (editorIndex !== null) {
312
+ // Edit existing
313
+ envCreds[editorIndex] = newCred;
314
+ } else {
315
+ // Add new
316
+ envCreds.push(newCred);
317
+ }
318
+ persistCredentials(editorEnvKey, envCreds);
319
+ return {
320
+ ...prev,
321
+ [editorEnvKey]: envCreds
322
+ };
323
+ });
324
+ closeEditor();
325
+ }, [editorName, editorFields, editorEnvKey, editorIndex, persistCredentials, closeEditor]);
326
+ const updateEditorField = (0, _react.useCallback)((fieldKey, value) => {
327
+ setEditorFields(prev => ({
328
+ ...prev,
329
+ [fieldKey]: value
330
+ }));
331
+ }, []);
332
+
333
+ // ── Delete credential ──────────────────────────────────────
334
+ const confirmDelete = (0, _react.useCallback)((envKey, index) => {
335
+ const cred = credentials[envKey]?.[index];
336
+ if (!cred) return;
337
+ _reactNative.Alert.alert('Delete Credential', `Remove "${cred.name}"?`, [{
338
+ text: 'Cancel',
339
+ style: 'cancel'
340
+ }, {
341
+ text: 'Delete',
342
+ style: 'destructive',
343
+ onPress: () => {
344
+ setCredentials(prev => {
345
+ const envCreds = [...(prev[envKey] || [])];
346
+ envCreds.splice(index, 1);
347
+ persistCredentials(envKey, envCreds);
348
+ return {
349
+ ...prev,
350
+ [envKey]: envCreds
351
+ };
352
+ });
353
+ }
354
+ }]);
355
+ }, [credentials, persistCredentials]);
356
+
357
+ // ── Handle login ───────────────────────────────────────────
358
+ const handleLogin = (0, _react.useCallback)(async (envKey, index) => {
359
+ if (!onLogin) return;
360
+ const cred = credentials[envKey]?.[index];
361
+ if (!cred) return;
362
+ const loginKey = `${envKey}:${index}`;
363
+ setLoggingInKey(loginKey);
364
+ try {
365
+ await onLogin(envKey, cred.values);
366
+ } finally {
367
+ setLoggingInKey(null);
368
+ }
369
+ }, [onLogin, credentials]);
370
+ const activeEnv = environments.find(e => e.key === activeEnvironment);
371
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
372
+ style: s.container,
373
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.FlatList, {
374
+ testID: _constants.TestIDs.environmentTab.container,
375
+ data: environments,
376
+ keyExtractor: env => env.key,
377
+ contentContainerStyle: s.contentContainer,
378
+ showsVerticalScrollIndicator: false,
379
+ keyboardShouldPersistTaps: "handled",
380
+ renderItem: ({
381
+ item: env
382
+ }) => {
383
+ const isActive = env.key === activeEnvironment;
384
+ const envColor = env.color || theme.accent;
385
+ const envCreds = credentials[env.key] || [];
386
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
387
+ testID: _constants.TestIDs.environmentTab.environmentCard(env.key),
388
+ style: [s.card, isActive && s.cardActive, isActive && {
389
+ borderColor: envColor
390
+ }],
391
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
392
+ testID: _constants.TestIDs.environmentTab.radioButton(env.key),
393
+ style: s.cardHeader,
394
+ onPress: () => onEnvironmentChange(env.key),
395
+ activeOpacity: 0.7,
396
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
397
+ style: s.radioRow,
398
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
399
+ style: [s.radio, isActive && s.radioActive, isActive && {
400
+ borderColor: envColor
401
+ }],
402
+ children: isActive && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
403
+ style: [s.radioDot, {
404
+ backgroundColor: envColor
405
+ }]
406
+ })
407
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
408
+ style: s.envInfo,
409
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
410
+ style: [s.envLabel, isActive && {
411
+ color: envColor
412
+ }],
413
+ children: env.label
414
+ }), env.baseUrl && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
415
+ style: s.envUrl,
416
+ numberOfLines: 1,
417
+ children: env.baseUrl
418
+ })]
419
+ })]
420
+ }), isActive && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
421
+ style: [s.activeBadge, {
422
+ backgroundColor: envColor + '20'
423
+ }],
424
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
425
+ style: [s.activeBadgeText, {
426
+ color: envColor
427
+ }],
428
+ children: "Active"
429
+ })
430
+ })]
431
+ }), isActive && credentialFields.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
432
+ style: s.credentialsContainer,
433
+ children: loadingCredentials ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
434
+ style: s.loadingContainer,
435
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
436
+ size: "small",
437
+ color: theme.textMuted
438
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
439
+ style: s.loadingText,
440
+ children: "Loading credentials\u2026"
441
+ })]
442
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
443
+ children: [envCreds.length === 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
444
+ style: s.emptyState,
445
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
446
+ style: s.emptyText,
447
+ children: "No saved credentials"
448
+ })
449
+ }) : envCreds.map((cred, index) => /*#__PURE__*/(0, _jsxRuntime.jsx)(CredentialRow, {
450
+ envKey: env.key,
451
+ index: index,
452
+ credential: cred,
453
+ envColor: envColor,
454
+ onLogin: () => handleLogin(env.key, index),
455
+ onEdit: () => openEditEditor(env.key, index),
456
+ onDelete: () => confirmDelete(env.key, index),
457
+ loggingIn: loggingInKey === `${env.key}:${index}`,
458
+ theme: theme
459
+ }, `${cred.name}-${index}`)), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
460
+ testID: _constants.TestIDs.environmentTab.addButton(env.key),
461
+ style: [s.addButton, {
462
+ borderColor: envColor
463
+ }],
464
+ onPress: () => openAddEditor(env.key),
465
+ activeOpacity: 0.7,
466
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
467
+ style: [s.addButtonText, {
468
+ color: envColor
469
+ }],
470
+ children: "\uFF0B Add Credential"
471
+ })
472
+ })]
473
+ })
474
+ })]
475
+ });
476
+ },
477
+ ListFooterComponent: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
478
+ testID: _constants.TestIDs.environmentTab.activeLabel,
479
+ style: s.footer,
480
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
481
+ style: s.footerText,
482
+ children: ["Active: ", activeEnv?.label ?? activeEnvironment, storageAdapter ? ' · Credentials saved' : '']
483
+ })
484
+ })
485
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(EditorModal, {
486
+ visible: editorVisible,
487
+ title: editorIndex !== null ? 'Edit Credential' : 'Add Credential',
488
+ credentialName: editorName,
489
+ fieldValues: editorFields,
490
+ credentialFields: credentialFields,
491
+ onChangeName: setEditorName,
492
+ onChangeField: updateEditorField,
493
+ onSave: saveEditor,
494
+ onCancel: closeEditor,
495
+ theme: theme
496
+ })]
497
+ });
498
+ };
499
+
500
+ // ─── Styles ──────────────────────────────────────────────────────────────────
501
+ exports.EnvironmentTab = EnvironmentTab;
502
+ const createStyles = t => _reactNative.StyleSheet.create({
503
+ container: {
504
+ flex: 1
505
+ },
506
+ contentContainer: {
507
+ padding: 14,
508
+ paddingBottom: 40
509
+ },
510
+ // ── Card ────────────────────────
511
+ card: {
512
+ backgroundColor: t.surface,
513
+ borderRadius: 12,
514
+ borderWidth: 1.5,
515
+ borderColor: t.border,
516
+ marginBottom: 12,
517
+ overflow: 'hidden'
518
+ },
519
+ cardActive: {
520
+ backgroundColor: t.surfaceElevated
521
+ },
522
+ cardHeader: {
523
+ flexDirection: 'row',
524
+ alignItems: 'center',
525
+ justifyContent: 'space-between',
526
+ padding: 14
527
+ },
528
+ radioRow: {
529
+ flexDirection: 'row',
530
+ alignItems: 'center',
531
+ flex: 1
532
+ },
533
+ radio: {
534
+ width: 20,
535
+ height: 20,
536
+ borderRadius: 10,
537
+ borderWidth: 2,
538
+ borderColor: t.border,
539
+ alignItems: 'center',
540
+ justifyContent: 'center',
541
+ marginRight: 12
542
+ },
543
+ radioActive: {
544
+ borderWidth: 2
545
+ },
546
+ radioDot: {
547
+ width: 10,
548
+ height: 10,
549
+ borderRadius: 5
550
+ },
551
+ envInfo: {
552
+ flex: 1
553
+ },
554
+ envLabel: {
555
+ fontFamily: _constants.MonospaceFont,
556
+ fontSize: 15,
557
+ fontWeight: '700',
558
+ color: t.text
559
+ },
560
+ envUrl: {
561
+ fontFamily: _constants.MonospaceFont,
562
+ fontSize: 11,
563
+ color: t.textMuted,
564
+ marginTop: 3
565
+ },
566
+ activeBadge: {
567
+ borderRadius: 6,
568
+ paddingHorizontal: 8,
569
+ paddingVertical: 3,
570
+ marginLeft: 8
571
+ },
572
+ activeBadgeText: {
573
+ fontFamily: _constants.MonospaceFont,
574
+ fontSize: 10,
575
+ fontWeight: '700',
576
+ textTransform: 'uppercase',
577
+ letterSpacing: 0.5
578
+ },
579
+ // ── Credentials Section ─────────
580
+ credentialsContainer: {
581
+ paddingHorizontal: 14,
582
+ paddingBottom: 14,
583
+ borderTopWidth: _reactNative.StyleSheet.hairlineWidth,
584
+ borderTopColor: t.border,
585
+ paddingTop: 10
586
+ },
587
+ emptyState: {
588
+ alignItems: 'center',
589
+ paddingVertical: 14
590
+ },
591
+ emptyText: {
592
+ fontFamily: _constants.MonospaceFont,
593
+ fontSize: 12,
594
+ color: t.textMuted,
595
+ fontStyle: 'italic'
596
+ },
597
+ // ── Credential Row ──────────────
598
+ credRow: {
599
+ flexDirection: 'row',
600
+ alignItems: 'center',
601
+ justifyContent: 'space-between',
602
+ paddingVertical: 10,
603
+ borderBottomWidth: _reactNative.StyleSheet.hairlineWidth,
604
+ borderBottomColor: t.border
605
+ },
606
+ credRowLeft: {
607
+ flex: 1,
608
+ marginRight: 8
609
+ },
610
+ credName: {
611
+ fontFamily: _constants.MonospaceFont,
612
+ fontSize: 13,
613
+ fontWeight: '700',
614
+ color: t.text
615
+ },
616
+ credMeta: {
617
+ fontFamily: _constants.MonospaceFont,
618
+ fontSize: 10,
619
+ color: t.textMuted,
620
+ marginTop: 2
621
+ },
622
+ credRowActions: {
623
+ flexDirection: 'row',
624
+ alignItems: 'center',
625
+ gap: 10
626
+ },
627
+ credActionButton: {
628
+ borderRadius: 6,
629
+ paddingHorizontal: 12,
630
+ paddingVertical: 5,
631
+ minWidth: 55,
632
+ alignItems: 'center'
633
+ },
634
+ credActionLoginText: {
635
+ fontFamily: _constants.MonospaceFont,
636
+ fontSize: 11,
637
+ fontWeight: '700',
638
+ color: '#FFFFFF'
639
+ },
640
+ credActionIcon: {
641
+ fontSize: 16,
642
+ color: t.textMuted
643
+ },
644
+ credDeleteIcon: {
645
+ color: t.error
646
+ },
647
+ // ── Add Button ──────────────────
648
+ addButton: {
649
+ borderRadius: 8,
650
+ borderWidth: 1,
651
+ borderStyle: 'dashed',
652
+ paddingVertical: 10,
653
+ alignItems: 'center',
654
+ marginTop: 10
655
+ },
656
+ addButtonText: {
657
+ fontFamily: _constants.MonospaceFont,
658
+ fontSize: 12,
659
+ fontWeight: '700'
660
+ },
661
+ // ── Footer ──────────────────────
662
+ footer: {
663
+ alignItems: 'center',
664
+ paddingVertical: 12
665
+ },
666
+ footerText: {
667
+ fontFamily: _constants.MonospaceFont,
668
+ fontSize: 11,
669
+ color: t.textMuted,
670
+ fontStyle: 'italic'
671
+ },
672
+ // ── Loading ─────────────────────
673
+ loadingContainer: {
674
+ flexDirection: 'row',
675
+ alignItems: 'center',
676
+ justifyContent: 'center',
677
+ gap: 8,
678
+ paddingVertical: 12
679
+ },
680
+ loadingText: {
681
+ fontFamily: _constants.MonospaceFont,
682
+ fontSize: 12,
683
+ color: t.textMuted
684
+ },
685
+ // ── Editor Modal ────────────────
686
+ modalContainer: {
687
+ flex: 1,
688
+ backgroundColor: t.background
689
+ },
690
+ modalHeader: {
691
+ flexDirection: 'row',
692
+ alignItems: 'center',
693
+ justifyContent: 'space-between',
694
+ paddingHorizontal: 16,
695
+ paddingVertical: 12,
696
+ borderBottomWidth: 1,
697
+ borderBottomColor: t.border
698
+ },
699
+ modalTitle: {
700
+ fontFamily: _constants.MonospaceFont,
701
+ fontSize: 16,
702
+ fontWeight: '700',
703
+ color: t.text,
704
+ flex: 1,
705
+ textAlign: 'center'
706
+ },
707
+ modalCancelText: {
708
+ fontFamily: _constants.MonospaceFont,
709
+ fontSize: 14,
710
+ color: t.textSecondary,
711
+ fontWeight: '600'
712
+ },
713
+ modalSaveText: {
714
+ fontFamily: _constants.MonospaceFont,
715
+ fontSize: 14,
716
+ color: t.accent,
717
+ fontWeight: '700'
718
+ },
719
+ modalBody: {
720
+ flex: 1
721
+ },
722
+ modalBodyContent: {
723
+ padding: 16
724
+ },
725
+ modalLabel: {
726
+ fontFamily: _constants.MonospaceFont,
727
+ fontSize: 12,
728
+ fontWeight: '700',
729
+ color: t.textMuted,
730
+ textTransform: 'uppercase',
731
+ letterSpacing: 0.5,
732
+ marginBottom: 6
733
+ },
734
+ modalInput: {
735
+ fontFamily: _constants.MonospaceFont,
736
+ fontSize: 13,
737
+ color: t.text,
738
+ backgroundColor: t.surfaceElevated,
739
+ borderRadius: 8,
740
+ borderWidth: 1,
741
+ borderColor: t.border,
742
+ paddingHorizontal: 12,
743
+ paddingVertical: 10
744
+ }
745
+ });
746
+ //# sourceMappingURL=EnvironmentTab.js.map