rn-backstage 1.4.7 → 1.5.1

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