react-native-storage-inspector 1.0.2 → 1.0.4

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/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import React6, { useState, useMemo, useEffect, useCallback } from 'react';
1
+ import React7, { useState, useMemo, useEffect, useCallback } from 'react';
2
2
  import { StyleSheet, Dimensions, View, ScrollView, RefreshControl, Text, TouchableOpacity, Share, Modal, TextInput } from 'react-native';
3
3
 
4
4
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
@@ -25,7 +25,7 @@ function createMMKVAdapter(instance, name) {
25
25
  return Promise.resolve();
26
26
  },
27
27
  removeItem(key) {
28
- instance.delete(key);
28
+ instance.remove(key);
29
29
  return Promise.resolve();
30
30
  },
31
31
  isAvailable() {
@@ -35,7 +35,6 @@ function createMMKVAdapter(instance, name) {
35
35
  }
36
36
 
37
37
  // src/adapters/async-storage.ts
38
- var asyncStorage = null;
39
38
  function getAsyncStorageFromRequire() {
40
39
  try {
41
40
  const mod = require("@react-native-async-storage/async-storage");
@@ -44,8 +43,8 @@ function getAsyncStorageFromRequire() {
44
43
  return null;
45
44
  }
46
45
  }
47
- function createAsyncStorageAdapter(instance) {
48
- const getStorage = () => instance ?? (asyncStorage ?? (asyncStorage = getAsyncStorageFromRequire()));
46
+ function createAsyncStorageAdapter() {
47
+ const getStorage = () => getAsyncStorageFromRequire();
49
48
  return {
50
49
  type: "async-storage",
51
50
  name: "Async Storage",
@@ -76,7 +75,6 @@ function createAsyncStorageAdapter(instance) {
76
75
  }
77
76
 
78
77
  // src/adapters/keychain.ts
79
- var keychain = null;
80
78
  function getKeychainFromRequire() {
81
79
  try {
82
80
  return require("react-native-keychain");
@@ -84,14 +82,14 @@ function getKeychainFromRequire() {
84
82
  return null;
85
83
  }
86
84
  }
87
- function createKeychainAdapter(knownKeys = [], instance) {
88
- const getKc = () => instance ?? (keychain ?? (keychain = getKeychainFromRequire()));
85
+ function createKeychainAdapter() {
86
+ const getKc = () => getKeychainFromRequire();
89
87
  return {
90
88
  type: "keychain",
91
89
  name: "Keychain",
92
90
  async getAllKeys() {
93
91
  const kc = getKc();
94
- if (!kc) return [...knownKeys];
92
+ if (!kc) return [];
95
93
  const genericServices = [];
96
94
  if (typeof kc.getAllGenericPasswordServices === "function") {
97
95
  try {
@@ -100,42 +98,32 @@ function createKeychainAdapter(knownKeys = [], instance) {
100
98
  } catch {
101
99
  }
102
100
  }
103
- const merged = /* @__PURE__ */ new Set([...genericServices, ...knownKeys]);
104
- return Array.from(merged);
101
+ return [...new Set(genericServices)];
105
102
  },
106
103
  async getItem(key) {
107
104
  const kc = getKc();
108
- if (!kc) return null;
109
- if (typeof kc.getGenericPassword === "function") {
110
- try {
111
- const creds2 = await kc.getGenericPassword({ service: key });
112
- if (creds2 && typeof creds2 === "object" && "password" in creds2) {
113
- return creds2.password;
114
- }
115
- } catch {
105
+ if (!kc?.getGenericPassword) return null;
106
+ try {
107
+ const creds = await kc.getGenericPassword({ service: key });
108
+ if (creds && typeof creds === "object" && "password" in creds) {
109
+ return creds.password;
116
110
  }
111
+ } catch {
117
112
  }
118
- const creds = await kc.getInternetCredentials(key);
119
- return creds?.password ?? null;
113
+ return null;
120
114
  },
121
115
  async setItem(key, value) {
122
116
  const kc = getKc();
123
- if (!kc) throw new Error("react-native-keychain is not available");
124
- if (typeof kc.setGenericPassword === "function") {
125
- const result2 = await kc.setGenericPassword(key, value, { service: key });
126
- if (result2 === false) throw new Error("Keychain set failed");
127
- return;
128
- }
129
- const result = await kc.setInternetCredentials(key, key, value);
117
+ if (!kc?.setGenericPassword)
118
+ throw new Error("react-native-keychain is not available");
119
+ const result = await kc.setGenericPassword(key, value, { service: key });
130
120
  if (result === false) throw new Error("Keychain set failed");
131
121
  },
132
122
  async removeItem(key) {
133
123
  const kc = getKc();
134
- if (!kc) throw new Error("react-native-keychain is not available");
135
- if (typeof kc.resetGenericPassword === "function") {
136
- await kc.resetGenericPassword({ service: key });
137
- }
138
- await kc.resetInternetCredentials(key);
124
+ if (!kc?.resetGenericPassword)
125
+ throw new Error("react-native-keychain is not available");
126
+ await kc.resetGenericPassword({ service: key });
139
127
  },
140
128
  isAvailable() {
141
129
  return getKc() !== null;
@@ -143,8 +131,20 @@ function createKeychainAdapter(knownKeys = [], instance) {
143
131
  };
144
132
  }
145
133
 
134
+ // src/utils.ts
135
+ function parsePersistedKeys(raw) {
136
+ if (raw == null || raw === "") return [];
137
+ try {
138
+ const parsed = JSON.parse(raw);
139
+ if (!Array.isArray(parsed)) return [];
140
+ return parsed.filter((item) => typeof item === "string");
141
+ } catch {
142
+ return [];
143
+ }
144
+ }
145
+
146
146
  // src/adapters/secure-store.ts
147
- var secureStore = null;
147
+ var PERSISTED_KEYS_STORAGE_KEY = "__storage_inspector_secure_store_keys__";
148
148
  function getSecureStoreFromRequire() {
149
149
  try {
150
150
  return require("expo-secure-store");
@@ -152,13 +152,17 @@ function getSecureStoreFromRequire() {
152
152
  return null;
153
153
  }
154
154
  }
155
- function createSecureStoreAdapter(knownKeys = [], instance) {
156
- const getStore = () => instance ?? (secureStore ?? (secureStore = getSecureStoreFromRequire()));
155
+ function createSecureStoreAdapter(knownKeys = []) {
156
+ const getStore = () => getSecureStoreFromRequire();
157
157
  return {
158
158
  type: "expo-secure-store",
159
159
  name: "Secure Store",
160
160
  async getAllKeys() {
161
- return [...knownKeys];
161
+ const store = getStore();
162
+ if (!store) return [];
163
+ const persistedKeysString = await store.getItemAsync(PERSISTED_KEYS_STORAGE_KEY);
164
+ const persistedKeys = parsePersistedKeys(persistedKeysString);
165
+ return [.../* @__PURE__ */ new Set([...knownKeys, ...persistedKeys])];
162
166
  },
163
167
  async getItem(key) {
164
168
  const store = getStore();
@@ -169,11 +173,26 @@ function createSecureStoreAdapter(knownKeys = [], instance) {
169
173
  const store = getStore();
170
174
  if (!store) throw new Error("expo-secure-store is not available");
171
175
  await store.setItemAsync(key, value);
176
+ if (key === PERSISTED_KEYS_STORAGE_KEY) return;
177
+ const persistedKeysString = await store.getItemAsync(PERSISTED_KEYS_STORAGE_KEY);
178
+ const persistedKeys = parsePersistedKeys(persistedKeysString);
179
+ const newPersistedKeys = [.../* @__PURE__ */ new Set([...persistedKeys, key])];
180
+ await store.setItemAsync(
181
+ PERSISTED_KEYS_STORAGE_KEY,
182
+ JSON.stringify(newPersistedKeys)
183
+ );
172
184
  },
173
185
  async removeItem(key) {
174
186
  const store = getStore();
175
187
  if (!store) throw new Error("expo-secure-store is not available");
176
188
  await store.deleteItemAsync(key);
189
+ const persistedKeysString = await store.getItemAsync(PERSISTED_KEYS_STORAGE_KEY);
190
+ const persistedKeys = parsePersistedKeys(persistedKeysString);
191
+ const newPersistedKeys = persistedKeys.filter((k) => k !== key);
192
+ await store.setItemAsync(
193
+ PERSISTED_KEYS_STORAGE_KEY,
194
+ JSON.stringify(newPersistedKeys)
195
+ );
177
196
  },
178
197
  isAvailable() {
179
198
  return getStore() !== null;
@@ -197,7 +216,8 @@ function useStorageItems(adapter) {
197
216
  const pairs = [];
198
217
  for (const key of keys) {
199
218
  const value = await adapter.getItem(key);
200
- pairs.push({ key, value: value ?? "" });
219
+ if (value === null) continue;
220
+ pairs.push({ key, value });
201
221
  }
202
222
  setItems(pairs);
203
223
  } catch (e) {
@@ -224,7 +244,8 @@ var theme = {
224
244
  text: "#000000",
225
245
  textSecondary: "#666666",
226
246
  textMuted: "#999999",
227
- inverted: "#ffffff"
247
+ inverted: "#ffffff",
248
+ overlayBackdrop: "rgba(0,0,0,0.5)"
228
249
  }
229
250
  };
230
251
 
@@ -245,15 +266,11 @@ var ROTATION = {
245
266
  chevronDown: "90deg",
246
267
  chevronUp: "-90deg"
247
268
  };
248
- function Icon({
249
- name,
250
- size = 20,
251
- tintColor = theme.colors.text,
252
- iconStyle
253
- }) {
269
+ function Icon(props) {
270
+ const { name, size = 20, tintColor = theme.colors.text, iconStyle } = props;
254
271
  const glyph = GLYPHS[name] ?? "?";
255
272
  const rotation = ROTATION[name];
256
- return /* @__PURE__ */ React6.createElement(
273
+ return /* @__PURE__ */ React7.createElement(
257
274
  Text,
258
275
  {
259
276
  style: [
@@ -276,7 +293,6 @@ var { width: SCREEN_WIDTH } = Dimensions.get("window");
276
293
  var LAYOUT = {
277
294
  padding: 16,
278
295
  fontSize: 14,
279
- headerHeight: 52,
280
296
  rowMinHeight: 48,
281
297
  sectionHeaderHeight: 48,
282
298
  iconSize: 20,
@@ -290,331 +306,242 @@ var LAYOUT = {
290
306
  sectionRadius: 4,
291
307
  screenWidth: SCREEN_WIDTH
292
308
  };
293
- var { colors } = theme;
309
+
310
+ // src/components/IconButton.tsx
311
+ function IconButton(props) {
312
+ const {
313
+ name,
314
+ onPress,
315
+ size = LAYOUT.iconSize,
316
+ tintColor = theme.colors.text,
317
+ disabled = false,
318
+ hitSlop = LAYOUT.hitSlop,
319
+ style,
320
+ activeOpacity = 0.6
321
+ } = props;
322
+ return /* @__PURE__ */ React7.createElement(
323
+ TouchableOpacity,
324
+ {
325
+ style: [styles2.iconButton, style],
326
+ onPress,
327
+ disabled,
328
+ hitSlop,
329
+ activeOpacity
330
+ },
331
+ /* @__PURE__ */ React7.createElement(
332
+ Icon,
333
+ {
334
+ name,
335
+ size,
336
+ tintColor: disabled ? theme.colors.textMuted : tintColor
337
+ }
338
+ )
339
+ );
340
+ }
294
341
  var styles2 = StyleSheet.create({
295
- container: {
342
+ iconButton: {
343
+ width: LAYOUT.iconButtonSize,
344
+ height: LAYOUT.iconButtonSize,
345
+ alignItems: "center",
346
+ justifyContent: "center"
347
+ }
348
+ });
349
+
350
+ // src/strings.ts
351
+ var strings = {
352
+ // StorageInspector
353
+ noAdapterAvailable: "No storage adapter available. Install at least one of: react-native-mmkv, @react-native-async-storage/async-storage, react-native-keychain, expo-secure-store",
354
+ // StorageSection
355
+ keychainHint: "No generic password items yet. Add a key using + above.",
356
+ secureStoreHint: "Secure Store has no list API. Pass secureStoreKeys prop with known keys, or add a key using + above.",
357
+ loading: "Loading\u2026",
358
+ noItems: "No items",
359
+ valueLabel: "Value",
360
+ charCount: (n) => n === 1 ? "1 char" : `${n} chars`,
361
+ deleteItemTitle: (key) => `Delete ${key}?`,
362
+ deleteItemMessage: (key) => `This will permanently delete the ${key} storage item. Do you wish to continue?`,
363
+ clearAllTitle: (name) => `Clear All ${name}?`,
364
+ clearAllMessage: (count, name) => `This will permanently delete all ${count} ${name} items. Do you wish to continue?`,
365
+ // StorageList
366
+ storageNotAvailable: "This storage is not available.",
367
+ keychainHintShort: "No items yet. Add a key below.",
368
+ edit: "Edit",
369
+ delete: "Delete",
370
+ addItem: "Add item",
371
+ // ItemForm
372
+ keyRequired: "Key is required",
373
+ saveFailed: "Save failed",
374
+ editItemTitle: (key) => `Edit ${key}`,
375
+ addItemTitle: (storageName) => `Add ${storageName} Item`,
376
+ storageTypeLabel: (name) => `Storage Type: ${name}`,
377
+ keyLabel: "Key",
378
+ enterKeyPlaceholder: "Enter key",
379
+ enterValuePlaceholder: "Enter value",
380
+ cancel: "Cancel",
381
+ saving: "Saving\u2026",
382
+ save: "Save",
383
+ add: "Add",
384
+ // ConfirmModal defaults
385
+ confirmDelete: "Yes, delete",
386
+ cancelKeep: "No, keep it"
387
+ };
388
+
389
+ // src/components/ItemForm.tsx
390
+ function ItemForm(props) {
391
+ const { visible, storageName, editingItem, onSave, onCancel } = props;
392
+ const [key, setKey] = useState("");
393
+ const [value, setValue] = useState("");
394
+ const [saving, setSaving] = useState(false);
395
+ const [error, setError] = useState(null);
396
+ const isEdit = editingItem !== null;
397
+ useEffect(() => {
398
+ if (visible) {
399
+ setKey(editingItem?.key ?? "");
400
+ setValue(editingItem?.value ?? "");
401
+ setError(null);
402
+ }
403
+ }, [visible, editingItem]);
404
+ const handleSubmit = async () => {
405
+ const k = key.trim();
406
+ if (!k) {
407
+ setError(strings.keyRequired);
408
+ return;
409
+ }
410
+ setSaving(true);
411
+ setError(null);
412
+ try {
413
+ await onSave(k, value);
414
+ onCancel();
415
+ } catch (e) {
416
+ setError(e instanceof Error ? e.message : strings.saveFailed);
417
+ } finally {
418
+ setSaving(false);
419
+ }
420
+ };
421
+ const title = isEdit ? strings.editItemTitle(editingItem?.key ?? "") : strings.addItemTitle(storageName);
422
+ return /* @__PURE__ */ React7.createElement(Modal, { visible, transparent: true, animationType: "slide", onRequestClose: onCancel }, /* @__PURE__ */ React7.createElement(View, { style: styles3.formOverlay }, /* @__PURE__ */ React7.createElement(
423
+ TouchableOpacity,
424
+ {
425
+ style: { flex: 1, width: "100%" },
426
+ activeOpacity: 1,
427
+ onPress: onCancel
428
+ }
429
+ ), /* @__PURE__ */ React7.createElement(View, { style: styles3.formModal }, /* @__PURE__ */ React7.createElement(View, { style: styles3.formHeader }, /* @__PURE__ */ React7.createElement(Text, { style: styles3.formTitle, numberOfLines: 1 }, title), /* @__PURE__ */ React7.createElement(
430
+ IconButton,
431
+ {
432
+ name: "close",
433
+ onPress: onCancel,
434
+ size: 24,
435
+ tintColor: theme.colors.textSecondary,
436
+ style: styles3.formCloseButton,
437
+ hitSlop: LAYOUT.hitSlopLarge
438
+ }
439
+ )), /* @__PURE__ */ React7.createElement(Text, { style: styles3.formStorageType }, strings.storageTypeLabel(storageName)), /* @__PURE__ */ React7.createElement(Text, { style: styles3.formLabel }, strings.keyLabel), /* @__PURE__ */ React7.createElement(
440
+ TextInput,
441
+ {
442
+ style: [styles3.formInput, isEdit && styles3.formInputDisabled],
443
+ value: key,
444
+ onChangeText: setKey,
445
+ placeholder: strings.enterKeyPlaceholder,
446
+ placeholderTextColor: theme.colors.textMuted,
447
+ editable: !isEdit,
448
+ autoCapitalize: "none",
449
+ multiline: true
450
+ }
451
+ ), /* @__PURE__ */ React7.createElement(Text, { style: styles3.formLabel }, strings.valueLabel), /* @__PURE__ */ React7.createElement(
452
+ TextInput,
453
+ {
454
+ style: styles3.formInput,
455
+ value,
456
+ onChangeText: setValue,
457
+ placeholder: strings.enterValuePlaceholder,
458
+ placeholderTextColor: theme.colors.textMuted,
459
+ multiline: true,
460
+ numberOfLines: 3
461
+ }
462
+ ), error ? /* @__PURE__ */ React7.createElement(Text, { style: [styles3.errorText, { marginBottom: 12 }] }, error) : null, /* @__PURE__ */ React7.createElement(View, { style: styles3.formActions }, /* @__PURE__ */ React7.createElement(
463
+ TouchableOpacity,
464
+ {
465
+ style: [styles3.formButton, styles3.formButtonCancel],
466
+ onPress: onCancel,
467
+ disabled: saving
468
+ },
469
+ /* @__PURE__ */ React7.createElement(Text, { style: [styles3.formButtonText, styles3.formButtonTextCancel] }, strings.cancel)
470
+ ), /* @__PURE__ */ React7.createElement(
471
+ TouchableOpacity,
472
+ {
473
+ style: [styles3.formButton, styles3.formButtonSubmit],
474
+ onPress: handleSubmit,
475
+ disabled: saving
476
+ },
477
+ /* @__PURE__ */ React7.createElement(Text, { style: [styles3.formButtonText, styles3.formButtonTextSubmit] }, saving ? strings.saving : isEdit ? strings.save : strings.add)
478
+ )))));
479
+ }
480
+ var { colors } = theme;
481
+ var styles3 = StyleSheet.create({
482
+ formOverlay: {
296
483
  flex: 1,
297
- width: "100%",
298
- backgroundColor: colors.background
299
- },
300
- content: {
301
- flex: 1
484
+ backgroundColor: colors.overlayBackdrop,
485
+ justifyContent: "flex-end",
486
+ alignItems: "center"
302
487
  },
303
- scroll: {
304
- flex: 1
488
+ formModal: {
489
+ width: "100%",
490
+ maxWidth: LAYOUT.screenWidth,
491
+ backgroundColor: colors.background,
492
+ borderTopLeftRadius: LAYOUT.modalRadius,
493
+ borderTopRightRadius: LAYOUT.modalRadius,
494
+ padding: LAYOUT.padding,
495
+ paddingBottom: LAYOUT.padding + 34
305
496
  },
306
- header: {
307
- height: LAYOUT.headerHeight,
497
+ formHeader: {
308
498
  flexDirection: "row",
309
499
  alignItems: "center",
310
500
  justifyContent: "space-between",
311
- paddingHorizontal: LAYOUT.padding,
312
- borderBottomWidth: StyleSheet.hairlineWidth,
313
- borderBottomColor: colors.border,
314
- backgroundColor: colors.background
315
- },
316
- headerLeft: {
317
- minWidth: 44,
318
- alignItems: "flex-start"
319
- },
320
- headerCenter: {
321
- flex: 1,
322
- alignItems: "center",
323
- justifyContent: "center"
324
- },
325
- headerRight: {
326
- minWidth: 44,
327
- alignItems: "flex-end"
328
- },
329
- sectionHeaderLabelWrap: {
330
- flex: 1
501
+ marginBottom: LAYOUT.padding
331
502
  },
332
- headerTitle: {
503
+ formTitle: {
333
504
  fontSize: 17,
334
505
  fontWeight: "600",
335
- color: colors.text
506
+ color: colors.text,
507
+ flex: 1
336
508
  },
337
- headerButton: {
509
+ formCloseButton: {
338
510
  width: 44,
339
511
  height: 44,
340
- alignItems: "center",
512
+ alignItems: "flex-end",
341
513
  justifyContent: "center"
342
514
  },
343
- scrollContent: {
344
- flexGrow: 1,
345
- paddingBottom: LAYOUT.fabSize + LAYOUT.padding + 20,
346
- paddingTop: LAYOUT.padding
347
- },
348
- sectionHeader: {
349
- height: LAYOUT.sectionHeaderHeight,
350
- flexDirection: "row",
351
- alignItems: "center",
352
- paddingHorizontal: LAYOUT.padding,
353
- borderBottomWidth: StyleSheet.hairlineWidth,
354
- borderBottomColor: colors.border,
355
- backgroundColor: colors.backgroundSecondary,
356
- marginTop: LAYOUT.padding,
357
- marginHorizontal: LAYOUT.padding,
358
- borderRadius: LAYOUT.sectionRadius
515
+ formStorageType: {
516
+ fontSize: 13,
517
+ color: colors.textSecondary,
518
+ marginBottom: 12
359
519
  },
360
- sectionHeaderLabel: {
361
- fontSize: 15,
520
+ formLabel: {
521
+ fontSize: 12,
362
522
  fontWeight: "600",
363
- color: colors.text
364
- },
365
- sectionHeaderCount: {
366
523
  color: colors.textSecondary,
367
- fontWeight: "400"
524
+ marginBottom: 6
368
525
  },
369
- storageRowActions: {
370
- flexDirection: "row",
371
- alignItems: "center",
372
- gap: LAYOUT.iconGap
526
+ formInput: {
527
+ borderWidth: 1,
528
+ borderColor: colors.border,
529
+ borderRadius: 10,
530
+ padding: 12,
531
+ fontSize: LAYOUT.fontSize,
532
+ marginBottom: LAYOUT.padding,
533
+ color: colors.text,
534
+ backgroundColor: colors.background
373
535
  },
374
- iconButton: {
375
- width: LAYOUT.iconButtonSize,
376
- height: LAYOUT.iconButtonSize,
377
- alignItems: "center",
378
- justifyContent: "center"
536
+ formInputDisabled: {
537
+ backgroundColor: colors.backgroundSecondary,
538
+ color: colors.textSecondary
379
539
  },
380
- iconSlot: {
381
- width: LAYOUT.iconButtonSize,
382
- height: LAYOUT.iconButtonSize,
383
- alignItems: "center",
384
- justifyContent: "center"
385
- },
386
- itemRow: {
387
- minHeight: LAYOUT.rowMinHeight,
388
- paddingHorizontal: LAYOUT.padding,
389
- paddingVertical: 12,
390
- borderBottomWidth: StyleSheet.hairlineWidth,
391
- borderBottomColor: colors.borderLight,
392
- backgroundColor: colors.background,
393
- marginHorizontal: LAYOUT.padding,
394
- marginBottom: 4
395
- },
396
- itemRowCollapsed: {
397
- flexDirection: "row",
398
- alignItems: "center",
399
- alignSelf: "stretch"
400
- },
401
- itemKey: {
402
- flex: 1,
403
- fontSize: LAYOUT.fontSize,
404
- fontWeight: "500",
405
- color: colors.text
406
- },
407
- itemChars: {
408
- fontSize: 12,
409
- color: colors.textSecondary,
410
- marginTop: 2
411
- },
412
- itemRowActions: {
413
- flexDirection: "row",
414
- alignItems: "center",
415
- marginLeft: LAYOUT.iconGap,
416
- gap: LAYOUT.iconGap
417
- },
418
- itemRowExpanded: {
419
- paddingTop: 4
420
- },
421
- valueBox: {
422
- backgroundColor: colors.backgroundSecondary,
423
- borderRadius: 8,
424
- padding: 12,
425
- marginTop: 8,
426
- marginBottom: 8,
427
- borderWidth: StyleSheet.hairlineWidth,
428
- borderColor: colors.border
429
- },
430
- valueBoxLabel: {
431
- fontSize: 11,
432
- fontWeight: "600",
433
- color: colors.textSecondary,
434
- marginBottom: 4,
435
- textTransform: "uppercase"
436
- },
437
- valueBoxText: {
438
- fontSize: LAYOUT.fontSize,
439
- color: colors.text
440
- },
441
- empty: {
442
- padding: LAYOUT.padding * 2,
443
- alignItems: "center",
444
- marginHorizontal: LAYOUT.padding
445
- },
446
- emptyText: {
447
- fontSize: LAYOUT.fontSize,
448
- color: colors.textMuted
449
- },
450
- loading: {
451
- padding: LAYOUT.padding * 2,
452
- alignItems: "center",
453
- marginHorizontal: LAYOUT.padding
454
- },
455
- loadingText: {
456
- fontSize: LAYOUT.fontSize,
457
- color: colors.textSecondary
458
- },
459
- error: {
460
- padding: LAYOUT.padding,
461
- backgroundColor: colors.backgroundTertiary,
462
- marginHorizontal: LAYOUT.padding,
463
- marginVertical: 8,
464
- borderRadius: 8
465
- },
466
- errorText: {
467
- fontSize: LAYOUT.fontSize,
468
- color: colors.text
469
- },
470
- keychainHint: {
471
- padding: LAYOUT.padding,
472
- marginHorizontal: LAYOUT.padding,
473
- marginTop: 8,
474
- marginBottom: 4,
475
- backgroundColor: colors.backgroundSecondary,
476
- borderRadius: 8
477
- },
478
- keychainHintText: {
479
- fontSize: LAYOUT.fontSize - 1,
480
- color: colors.textSecondary
481
- },
482
- list: {
483
- flex: 1
484
- },
485
- listContent: {
486
- flexGrow: 1,
487
- paddingBottom: LAYOUT.fabSize + LAYOUT.padding + 20
488
- },
489
- row: {
490
- flexDirection: "row",
491
- alignItems: "center",
492
- minHeight: LAYOUT.rowMinHeight,
493
- paddingHorizontal: LAYOUT.padding,
494
- paddingVertical: 12,
495
- borderBottomWidth: StyleSheet.hairlineWidth,
496
- borderBottomColor: colors.borderLight,
497
- backgroundColor: colors.background,
498
- marginHorizontal: LAYOUT.padding
499
- },
500
- rowKey: {
501
- flex: 1,
502
- fontSize: LAYOUT.fontSize,
503
- fontWeight: "500",
504
- color: colors.text
505
- },
506
- rowValue: {
507
- fontSize: LAYOUT.fontSize - 1,
508
- color: colors.textSecondary
509
- },
510
- rowActions: {
511
- flexDirection: "row",
512
- alignItems: "center",
513
- marginLeft: LAYOUT.iconGap,
514
- gap: LAYOUT.iconGap
515
- },
516
- rowButton: {
517
- paddingVertical: 8,
518
- paddingHorizontal: 12
519
- },
520
- rowButtonText: {
521
- fontSize: 14,
522
- fontWeight: "600",
523
- color: colors.text
524
- },
525
- rowButtonDanger: {
526
- color: colors.text
527
- },
528
- addButton: {
529
- position: "absolute",
530
- bottom: LAYOUT.padding + 16,
531
- right: LAYOUT.padding + 16,
532
- width: LAYOUT.fabSize,
533
- height: LAYOUT.fabSize,
534
- borderRadius: LAYOUT.fabSize / 2,
535
- backgroundColor: colors.text,
536
- alignItems: "center",
537
- justifyContent: "center"
538
- },
539
- addButtonText: {
540
- fontSize: 16,
541
- fontWeight: "600",
542
- color: colors.inverted
543
- },
544
- fab: {
545
- position: "absolute",
546
- bottom: LAYOUT.padding + 16,
547
- right: LAYOUT.padding + 16,
548
- width: LAYOUT.fabSize,
549
- height: LAYOUT.fabSize,
550
- borderRadius: LAYOUT.fabSize / 2,
551
- backgroundColor: colors.text,
552
- alignItems: "center",
553
- justifyContent: "center"
554
- },
555
- formOverlay: {
556
- flex: 1,
557
- backgroundColor: "rgba(0,0,0,0.5)",
558
- justifyContent: "flex-end",
559
- alignItems: "center"
560
- },
561
- formModal: {
562
- width: "100%",
563
- maxWidth: LAYOUT.screenWidth,
564
- backgroundColor: colors.background,
565
- borderTopLeftRadius: LAYOUT.modalRadius,
566
- borderTopRightRadius: LAYOUT.modalRadius,
567
- padding: LAYOUT.padding,
568
- paddingBottom: LAYOUT.padding + 34
569
- },
570
- formHeader: {
571
- flexDirection: "row",
572
- alignItems: "center",
573
- justifyContent: "space-between",
574
- marginBottom: LAYOUT.padding
575
- },
576
- formTitle: {
577
- fontSize: 17,
578
- fontWeight: "600",
579
- color: colors.text,
580
- flex: 1
581
- },
582
- formCloseButton: {
583
- width: 44,
584
- height: 44,
585
- alignItems: "flex-end",
586
- justifyContent: "center"
587
- },
588
- formStorageType: {
589
- fontSize: 13,
590
- color: colors.textSecondary,
591
- marginBottom: 12
592
- },
593
- formLabel: {
594
- fontSize: 12,
595
- fontWeight: "600",
596
- color: colors.textSecondary,
597
- marginBottom: 6
598
- },
599
- formInput: {
600
- borderWidth: 1,
601
- borderColor: colors.border,
602
- borderRadius: 10,
603
- padding: 12,
604
- fontSize: LAYOUT.fontSize,
605
- marginBottom: LAYOUT.padding,
606
- color: colors.text,
607
- backgroundColor: colors.background
608
- },
609
- formInputDisabled: {
610
- backgroundColor: colors.backgroundSecondary,
611
- color: colors.textSecondary
612
- },
613
- formActions: {
614
- flexDirection: "row",
615
- justifyContent: "flex-end",
616
- gap: 12,
617
- marginTop: 8
540
+ formActions: {
541
+ flexDirection: "row",
542
+ justifyContent: "flex-end",
543
+ gap: 12,
544
+ marginTop: 8
618
545
  },
619
546
  formButton: {
620
547
  paddingVertical: 12,
@@ -641,16 +568,79 @@ var styles2 = StyleSheet.create({
641
568
  formButtonTextSubmit: {
642
569
  color: colors.inverted
643
570
  },
571
+ errorText: {
572
+ fontSize: LAYOUT.fontSize,
573
+ color: colors.text
574
+ }
575
+ });
576
+ function ConfirmModal(props) {
577
+ const {
578
+ visible,
579
+ title,
580
+ message,
581
+ confirmLabel = strings.confirmDelete,
582
+ cancelLabel = strings.cancelKeep,
583
+ danger = true,
584
+ onConfirm,
585
+ onCancel
586
+ } = props;
587
+ return /* @__PURE__ */ React7.createElement(Modal, { visible, transparent: true, animationType: "slide", onRequestClose: onCancel }, /* @__PURE__ */ React7.createElement(View, { style: styles4.confirmOverlay }, /* @__PURE__ */ React7.createElement(
588
+ TouchableOpacity,
589
+ {
590
+ style: { flex: 1, width: "100%" },
591
+ activeOpacity: 1,
592
+ onPress: onCancel
593
+ }
594
+ ), /* @__PURE__ */ React7.createElement(View, { style: styles4.confirmModal }, /* @__PURE__ */ React7.createElement(View, { style: styles4.confirmHeader }, /* @__PURE__ */ React7.createElement(Text, { style: styles4.confirmTitle }, title), /* @__PURE__ */ React7.createElement(
595
+ IconButton,
596
+ {
597
+ name: "close",
598
+ onPress: onCancel,
599
+ size: 24,
600
+ tintColor: theme.colors.textSecondary,
601
+ style: styles4.formCloseButton,
602
+ hitSlop: LAYOUT.hitSlopLarge
603
+ }
604
+ )), /* @__PURE__ */ React7.createElement(Text, { style: styles4.confirmMessage }, message), /* @__PURE__ */ React7.createElement(View, { style: styles4.confirmActions }, /* @__PURE__ */ React7.createElement(
605
+ TouchableOpacity,
606
+ {
607
+ style: [styles4.confirmButton, styles4.confirmButtonSecondary],
608
+ onPress: onCancel
609
+ },
610
+ /* @__PURE__ */ React7.createElement(Text, { style: [styles4.confirmButtonText, styles4.confirmButtonTextSecondary] }, cancelLabel)
611
+ ), /* @__PURE__ */ React7.createElement(
612
+ TouchableOpacity,
613
+ {
614
+ style: [
615
+ styles4.confirmButton,
616
+ danger ? styles4.confirmButtonDanger : styles4.formButtonSubmit
617
+ ],
618
+ onPress: onConfirm
619
+ },
620
+ /* @__PURE__ */ React7.createElement(
621
+ Text,
622
+ {
623
+ style: [
624
+ styles4.confirmButtonText,
625
+ danger ? styles4.confirmButtonTextDanger : styles4.formButtonTextSubmit
626
+ ]
627
+ },
628
+ confirmLabel
629
+ )
630
+ )))));
631
+ }
632
+ var { colors: colors2 } = theme;
633
+ var styles4 = StyleSheet.create({
644
634
  confirmOverlay: {
645
635
  flex: 1,
646
- backgroundColor: "rgba(0,0,0,0.5)",
636
+ backgroundColor: colors2.overlayBackdrop,
647
637
  justifyContent: "flex-end",
648
638
  alignItems: "center"
649
639
  },
650
640
  confirmModal: {
651
641
  width: "100%",
652
642
  maxWidth: LAYOUT.screenWidth,
653
- backgroundColor: colors.background,
643
+ backgroundColor: colors2.background,
654
644
  borderTopLeftRadius: LAYOUT.modalRadius,
655
645
  borderTopRightRadius: LAYOUT.modalRadius,
656
646
  padding: LAYOUT.padding,
@@ -665,12 +655,18 @@ var styles2 = StyleSheet.create({
665
655
  confirmTitle: {
666
656
  fontSize: 17,
667
657
  fontWeight: "600",
668
- color: colors.text,
658
+ color: colors2.text,
669
659
  flex: 1
670
660
  },
661
+ formCloseButton: {
662
+ width: 44,
663
+ height: 44,
664
+ alignItems: "flex-end",
665
+ justifyContent: "center"
666
+ },
671
667
  confirmMessage: {
672
668
  fontSize: 15,
673
- color: colors.textSecondary,
669
+ color: colors2.textSecondary,
674
670
  lineHeight: 22,
675
671
  marginBottom: 20
676
672
  },
@@ -686,278 +682,171 @@ var styles2 = StyleSheet.create({
686
682
  },
687
683
  confirmButtonSecondary: {
688
684
  backgroundColor: "transparent",
689
- borderWidth: 1,
690
- borderColor: colors.text
691
- },
692
- confirmButtonDanger: {
693
- backgroundColor: colors.text
694
- },
695
- confirmButtonText: {
696
- fontSize: 16,
697
- fontWeight: "600"
698
- },
699
- confirmButtonTextSecondary: {
700
- color: colors.text
701
- },
702
- confirmButtonTextDanger: {
703
- color: colors.inverted
704
- }
705
- });
706
-
707
- // src/components/IconButton.tsx
708
- function IconButton({
709
- name,
710
- onPress,
711
- size = LAYOUT.iconSize,
712
- tintColor = theme.colors.text,
713
- disabled = false,
714
- hitSlop = LAYOUT.hitSlop,
715
- style,
716
- activeOpacity = 0.6
717
- }) {
718
- return /* @__PURE__ */ React6.createElement(
719
- TouchableOpacity,
720
- {
721
- style: [styles2.iconButton, style],
722
- onPress,
723
- disabled,
724
- hitSlop,
725
- activeOpacity
726
- },
727
- /* @__PURE__ */ React6.createElement(
728
- Icon,
729
- {
730
- name,
731
- size,
732
- tintColor: disabled ? theme.colors.textMuted : tintColor
733
- }
734
- )
735
- );
736
- }
737
-
738
- // src/strings.ts
739
- var strings = {
740
- // StorageInspector
741
- noAdapterAvailable: "No storage adapter available. Install at least one of: react-native-mmkv, @react-native-async-storage/async-storage, react-native-keychain, expo-secure-store",
742
- // StorageSection
743
- keychainHint: "No generic password items yet. Add a key using + above, or pass keychainKeys for internet credentials.",
744
- secureStoreHint: "Secure Store has no list API. Pass secureStoreKeys prop with known keys, or add a key using + above.",
745
- loading: "Loading\u2026",
746
- noItems: "No items",
747
- valueLabel: "Value",
748
- emptyValue: "(empty)",
749
- charCount: (n) => n === 1 ? "1 char" : `${n} chars`,
750
- deleteItemTitle: (key) => `Delete ${key}?`,
751
- deleteItemMessage: (key) => `This will permanently delete the ${key} storage item. Do you wish to continue?`,
752
- clearAllTitle: (name) => `Clear All ${name}?`,
753
- clearAllMessage: (count, name) => `This will permanently delete all ${count} ${name} items. Do you wish to continue?`,
754
- // StorageList
755
- storageNotAvailable: "This storage is not available.",
756
- keychainHintShort: "No items yet. Add a key below, or pass keychainKeys for internet credentials.",
757
- edit: "Edit",
758
- delete: "Delete",
759
- addItem: "Add item",
760
- // ItemForm
761
- keyRequired: "Key is required",
762
- saveFailed: "Save failed",
763
- editItemTitle: (key) => `Edit ${key}`,
764
- addItemTitle: (storageName) => `Add ${storageName} Item`,
765
- storageTypeLabel: (name) => `Storage Type: ${name}`,
766
- keyLabel: "Key",
767
- enterKeyPlaceholder: "Enter key",
768
- enterValuePlaceholder: "Enter value",
769
- cancel: "Cancel",
770
- saving: "Saving\u2026",
771
- save: "Save",
772
- add: "Add",
773
- // ConfirmModal defaults
774
- confirmDelete: "Yes, delete",
775
- cancelKeep: "No, keep it"
776
- };
777
-
778
- // src/components/ItemForm.tsx
779
- function ItemForm({
780
- visible,
781
- storageName,
782
- editingItem,
783
- onSave,
784
- onCancel
785
- }) {
786
- const [key, setKey] = useState("");
787
- const [value, setValue] = useState("");
788
- const [saving, setSaving] = useState(false);
789
- const [error, setError] = useState(null);
790
- const isEdit = editingItem !== null;
791
- useEffect(() => {
792
- if (visible) {
793
- setKey(editingItem?.key ?? "");
794
- setValue(editingItem?.value ?? "");
795
- setError(null);
796
- }
797
- }, [visible, editingItem]);
798
- const handleSubmit = async () => {
799
- const k = key.trim();
800
- if (!k) {
801
- setError(strings.keyRequired);
802
- return;
803
- }
804
- setSaving(true);
805
- setError(null);
806
- try {
807
- await onSave(k, value);
808
- onCancel();
809
- } catch (e) {
810
- setError(e instanceof Error ? e.message : strings.saveFailed);
811
- } finally {
812
- setSaving(false);
813
- }
814
- };
815
- const title = isEdit ? strings.editItemTitle(editingItem?.key ?? "") : strings.addItemTitle(storageName);
816
- return /* @__PURE__ */ React6.createElement(Modal, { visible, transparent: true, animationType: "slide", onRequestClose: onCancel }, /* @__PURE__ */ React6.createElement(View, { style: styles2.formOverlay }, /* @__PURE__ */ React6.createElement(
817
- TouchableOpacity,
818
- {
819
- style: { flex: 1, width: "100%" },
820
- activeOpacity: 1,
821
- onPress: onCancel
822
- }
823
- ), /* @__PURE__ */ React6.createElement(View, { style: styles2.formModal }, /* @__PURE__ */ React6.createElement(View, { style: styles2.formHeader }, /* @__PURE__ */ React6.createElement(Text, { style: styles2.formTitle, numberOfLines: 1 }, title), /* @__PURE__ */ React6.createElement(
824
- IconButton,
825
- {
826
- name: "close",
827
- onPress: onCancel,
828
- size: 24,
829
- tintColor: theme.colors.textSecondary,
830
- style: styles2.formCloseButton,
831
- hitSlop: LAYOUT.hitSlopLarge
832
- }
833
- )), /* @__PURE__ */ React6.createElement(Text, { style: styles2.formStorageType }, strings.storageTypeLabel(storageName)), /* @__PURE__ */ React6.createElement(Text, { style: styles2.formLabel }, strings.keyLabel), /* @__PURE__ */ React6.createElement(
834
- TextInput,
835
- {
836
- style: [styles2.formInput, isEdit && styles2.formInputDisabled],
837
- value: key,
838
- onChangeText: setKey,
839
- placeholder: strings.enterKeyPlaceholder,
840
- placeholderTextColor: theme.colors.textMuted,
841
- editable: !isEdit,
842
- autoCapitalize: "none",
843
- multiline: true
844
- }
845
- ), /* @__PURE__ */ React6.createElement(Text, { style: styles2.formLabel }, strings.valueLabel), /* @__PURE__ */ React6.createElement(
846
- TextInput,
685
+ borderWidth: 1,
686
+ borderColor: colors2.text
687
+ },
688
+ confirmButtonDanger: {
689
+ backgroundColor: colors2.text
690
+ },
691
+ formButtonSubmit: {
692
+ backgroundColor: colors2.text
693
+ },
694
+ confirmButtonText: {
695
+ fontSize: 16,
696
+ fontWeight: "600"
697
+ },
698
+ confirmButtonTextSecondary: {
699
+ color: colors2.text
700
+ },
701
+ confirmButtonTextDanger: {
702
+ color: colors2.inverted
703
+ },
704
+ formButtonTextSubmit: {
705
+ color: colors2.inverted
706
+ }
707
+ });
708
+ function ItemRowActions(props) {
709
+ const {
710
+ item,
711
+ onCopy,
712
+ onEdit,
713
+ onDelete,
714
+ showChevron = false,
715
+ chevronDirection = "down"
716
+ } = props;
717
+ return /* @__PURE__ */ React7.createElement(View, { style: styles5.itemRowActions }, /* @__PURE__ */ React7.createElement(IconButton, { name: "copy", onPress: () => onCopy(item) }), /* @__PURE__ */ React7.createElement(IconButton, { name: "edit", onPress: () => onEdit(item) }), /* @__PURE__ */ React7.createElement(IconButton, { name: "trash", onPress: () => onDelete(item) }), showChevron && /* @__PURE__ */ React7.createElement(View, { style: styles5.iconSlot }, /* @__PURE__ */ React7.createElement(
718
+ Icon,
847
719
  {
848
- style: styles2.formInput,
849
- value,
850
- onChangeText: setValue,
851
- placeholder: strings.enterValuePlaceholder,
852
- placeholderTextColor: theme.colors.textMuted,
853
- multiline: true,
854
- numberOfLines: 3
720
+ name: chevronDirection === "up" ? "chevronUp" : "chevronDown",
721
+ size: LAYOUT.chevronSize,
722
+ tintColor: theme.colors.text
855
723
  }
856
- ), error ? /* @__PURE__ */ React6.createElement(Text, { style: [styles2.errorText, { marginBottom: 12 }] }, error) : null, /* @__PURE__ */ React6.createElement(View, { style: styles2.formActions }, /* @__PURE__ */ React6.createElement(
857
- TouchableOpacity,
858
- {
859
- style: [styles2.formButton, styles2.formButtonCancel],
860
- onPress: onCancel,
861
- disabled: saving
862
- },
863
- /* @__PURE__ */ React6.createElement(Text, { style: [styles2.formButtonText, styles2.formButtonTextCancel] }, strings.cancel)
864
- ), /* @__PURE__ */ React6.createElement(
865
- TouchableOpacity,
866
- {
867
- style: [styles2.formButton, styles2.formButtonSubmit],
868
- onPress: handleSubmit,
869
- disabled: saving
870
- },
871
- /* @__PURE__ */ React6.createElement(Text, { style: [styles2.formButtonText, styles2.formButtonTextSubmit] }, saving ? strings.saving : isEdit ? strings.save : strings.add)
872
- )))));
724
+ )));
873
725
  }
874
- function ConfirmModal({
875
- visible,
876
- title,
877
- message,
878
- confirmLabel = strings.confirmDelete,
879
- cancelLabel = strings.cancelKeep,
880
- danger = true,
881
- onConfirm,
882
- onCancel
883
- }) {
884
- const handleConfirm = () => {
885
- void Promise.resolve(onConfirm());
726
+ var styles5 = StyleSheet.create({
727
+ itemRowActions: {
728
+ flexDirection: "row",
729
+ alignItems: "center",
730
+ marginLeft: LAYOUT.iconGap,
731
+ gap: LAYOUT.iconGap
732
+ },
733
+ iconSlot: {
734
+ width: LAYOUT.iconButtonSize,
735
+ height: LAYOUT.iconButtonSize,
736
+ alignItems: "center",
737
+ justifyContent: "center"
738
+ }
739
+ });
740
+
741
+ // src/components/StorageList.tsx
742
+ function StorageList(props) {
743
+ const { item, onCopy, onEdit, onDelete } = props;
744
+ const [expandedKeys, setExpandedKeys] = useState(/* @__PURE__ */ new Set());
745
+ const charCount = item.value.length;
746
+ const handleToggleExpanded = () => {
747
+ setExpandedKeys((prev) => {
748
+ const next = new Set(prev);
749
+ if (next.has(item.key)) next.delete(item.key);
750
+ else next.add(item.key);
751
+ return next;
752
+ });
886
753
  };
887
- return /* @__PURE__ */ React6.createElement(Modal, { visible, transparent: true, animationType: "slide", onRequestClose: onCancel }, /* @__PURE__ */ React6.createElement(View, { style: styles2.confirmOverlay }, /* @__PURE__ */ React6.createElement(
888
- TouchableOpacity,
889
- {
890
- style: { flex: 1, width: "100%" },
891
- activeOpacity: 1,
892
- onPress: onCancel
893
- }
894
- ), /* @__PURE__ */ React6.createElement(View, { style: styles2.confirmModal }, /* @__PURE__ */ React6.createElement(View, { style: styles2.confirmHeader }, /* @__PURE__ */ React6.createElement(Text, { style: styles2.confirmTitle }, title), /* @__PURE__ */ React6.createElement(
895
- IconButton,
754
+ const isExpanded = expandedKeys.has(item.key);
755
+ return /* @__PURE__ */ React7.createElement(View, { style: styles6.itemRow }, /* @__PURE__ */ React7.createElement(TouchableOpacity, { onPress: handleToggleExpanded, activeOpacity: 0.7 }, /* @__PURE__ */ React7.createElement(View, { style: styles6.itemRowCollapsed }, /* @__PURE__ */ React7.createElement(View, { style: { flex: 1 } }, /* @__PURE__ */ React7.createElement(Text, { style: styles6.itemKey, numberOfLines: 1 }, item.key), /* @__PURE__ */ React7.createElement(Text, { style: styles6.itemChars }, strings.charCount(charCount))), !isExpanded ? /* @__PURE__ */ React7.createElement(
756
+ ItemRowActions,
896
757
  {
897
- name: "close",
898
- onPress: onCancel,
899
- size: 24,
900
- tintColor: theme.colors.textSecondary,
901
- style: styles2.formCloseButton,
902
- hitSlop: LAYOUT.hitSlopLarge
758
+ item,
759
+ onCopy,
760
+ onEdit,
761
+ onDelete,
762
+ showChevron: true,
763
+ chevronDirection: "down"
903
764
  }
904
- )), /* @__PURE__ */ React6.createElement(Text, { style: styles2.confirmMessage }, message), /* @__PURE__ */ React6.createElement(View, { style: styles2.confirmActions }, /* @__PURE__ */ React6.createElement(
905
- TouchableOpacity,
906
- {
907
- style: [styles2.confirmButton, styles2.confirmButtonSecondary],
908
- onPress: onCancel
909
- },
910
- /* @__PURE__ */ React6.createElement(Text, { style: [styles2.confirmButtonText, styles2.confirmButtonTextSecondary] }, cancelLabel)
911
- ), /* @__PURE__ */ React6.createElement(
912
- TouchableOpacity,
913
- {
914
- style: [
915
- styles2.confirmButton,
916
- danger ? styles2.confirmButtonDanger : styles2.formButtonSubmit
917
- ],
918
- onPress: handleConfirm
919
- },
920
- /* @__PURE__ */ React6.createElement(
921
- Text,
922
- {
923
- style: [
924
- styles2.confirmButtonText,
925
- danger ? styles2.confirmButtonTextDanger : styles2.formButtonTextSubmit
926
- ]
927
- },
928
- confirmLabel
929
- )
930
- )))));
931
- }
932
- function ItemRowActions({
933
- item,
934
- onCopy,
935
- onEdit,
936
- onDelete,
937
- showChevron = false,
938
- chevronDirection = "down"
939
- }) {
940
- return /* @__PURE__ */ React6.createElement(View, { style: styles2.itemRowActions }, /* @__PURE__ */ React6.createElement(IconButton, { name: "copy", onPress: () => onCopy(item) }), /* @__PURE__ */ React6.createElement(IconButton, { name: "edit", onPress: () => onEdit(item) }), /* @__PURE__ */ React6.createElement(IconButton, { name: "trash", onPress: () => onDelete(item) }), showChevron && /* @__PURE__ */ React6.createElement(View, { style: styles2.iconSlot }, /* @__PURE__ */ React6.createElement(
765
+ ) : /* @__PURE__ */ React7.createElement(View, { style: styles6.iconSlot }, /* @__PURE__ */ React7.createElement(
941
766
  Icon,
942
767
  {
943
- name: chevronDirection === "up" ? "chevronUp" : "chevronDown",
768
+ name: "chevronUp",
944
769
  size: LAYOUT.chevronSize,
945
770
  tintColor: theme.colors.text
946
771
  }
772
+ )))), isExpanded && /* @__PURE__ */ React7.createElement(View, { style: styles6.itemRowExpanded }, /* @__PURE__ */ React7.createElement(TouchableOpacity, { onPress: () => onEdit(item), style: styles6.valueBox }, /* @__PURE__ */ React7.createElement(Text, { style: styles6.valueBoxLabel }, strings.valueLabel), /* @__PURE__ */ React7.createElement(Text, { style: styles6.valueBoxText, selectable: true }, item.value)), /* @__PURE__ */ React7.createElement(
773
+ ItemRowActions,
774
+ {
775
+ item,
776
+ onCopy,
777
+ onEdit,
778
+ onDelete
779
+ }
947
780
  )));
948
781
  }
782
+ var { colors: colors3 } = theme;
783
+ var styles6 = StyleSheet.create({
784
+ itemRow: {
785
+ minHeight: LAYOUT.rowMinHeight,
786
+ paddingHorizontal: LAYOUT.padding,
787
+ paddingVertical: 12,
788
+ borderBottomWidth: StyleSheet.hairlineWidth,
789
+ borderBottomColor: colors3.borderLight,
790
+ backgroundColor: colors3.background,
791
+ marginHorizontal: LAYOUT.padding,
792
+ marginBottom: 4
793
+ },
794
+ itemRowCollapsed: {
795
+ flexDirection: "row",
796
+ alignItems: "center",
797
+ alignSelf: "stretch"
798
+ },
799
+ itemKey: {
800
+ flex: 1,
801
+ fontSize: LAYOUT.fontSize,
802
+ fontWeight: "500",
803
+ color: colors3.text
804
+ },
805
+ itemChars: {
806
+ fontSize: 12,
807
+ color: colors3.textSecondary,
808
+ marginTop: 2
809
+ },
810
+ iconSlot: {
811
+ width: LAYOUT.iconButtonSize,
812
+ height: LAYOUT.iconButtonSize,
813
+ alignItems: "center",
814
+ justifyContent: "center"
815
+ },
816
+ itemRowExpanded: {
817
+ paddingTop: 4
818
+ },
819
+ valueBox: {
820
+ backgroundColor: colors3.backgroundSecondary,
821
+ borderRadius: 8,
822
+ padding: 12,
823
+ marginTop: 8,
824
+ marginBottom: 8,
825
+ borderWidth: StyleSheet.hairlineWidth,
826
+ borderColor: colors3.border
827
+ },
828
+ valueBoxLabel: {
829
+ fontSize: 11,
830
+ fontWeight: "600",
831
+ color: colors3.textSecondary,
832
+ marginBottom: 4,
833
+ textTransform: "uppercase"
834
+ },
835
+ valueBoxText: {
836
+ fontSize: LAYOUT.fontSize,
837
+ color: colors3.text
838
+ }
839
+ });
949
840
 
950
841
  // src/components/StorageSection.tsx
951
- function StorageSection({
952
- adapter,
953
- keychainKeys,
954
- onKeychainKeyAdded,
955
- onSecureStoreKeyAdded,
956
- defaultExpanded = true,
957
- expanded: expandedProp,
958
- onToggleExpanded,
959
- refreshTrigger
960
- }) {
842
+ function StorageSection(props) {
843
+ const {
844
+ adapter,
845
+ defaultExpanded = true,
846
+ expanded: expandedProp,
847
+ onToggleExpanded,
848
+ refreshTrigger
849
+ } = props;
961
850
  const { items, loading, error, refresh } = useStorageItems(adapter);
962
851
  const [expandedInternal, setExpandedInternal] = useState(defaultExpanded);
963
852
  const expanded = expandedProp !== void 0 ? expandedProp : expandedInternal;
@@ -968,7 +857,6 @@ function StorageSection({
968
857
  useEffect(() => {
969
858
  if (refreshTrigger !== void 0) refresh();
970
859
  }, [refreshTrigger, refresh]);
971
- const [expandedKeys, setExpandedKeys] = useState(/* @__PURE__ */ new Set());
972
860
  const [formVisible, setFormVisible] = useState(false);
973
861
  const [editingItem, setEditingItem] = useState(null);
974
862
  const [deleteItem, setDeleteItem] = useState(null);
@@ -982,14 +870,7 @@ function StorageSection({
982
870
  setFormVisible(true);
983
871
  };
984
872
  const handleSave = async (key, value) => {
985
- const isNewKey = !editingItem || editingItem.key !== key;
986
873
  await adapter.setItem(key, value);
987
- if (adapter.type === "keychain" && isNewKey) {
988
- onKeychainKeyAdded?.(key);
989
- }
990
- if (adapter.type === "expo-secure-store" && isNewKey) {
991
- onSecureStoreKeyAdded?.(key);
992
- }
993
874
  await refresh();
994
875
  };
995
876
  const handleDeleteItem = async () => {
@@ -1017,25 +898,25 @@ function StorageSection({
1017
898
  };
1018
899
  const isKeychain = adapter.type === "keychain";
1019
900
  const isSecureStore = adapter.type === "expo-secure-store";
1020
- const showKeychainHint = isKeychain && items.length === 0 && (keychainKeys?.length ?? 0) === 0;
901
+ const showKeychainHint = isKeychain && items.length === 0;
1021
902
  const showSecureStoreHint = isSecureStore && items.length === 0;
1022
903
  if (!adapter.isAvailable()) return null;
1023
- return /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(
904
+ return /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(
1024
905
  TouchableOpacity,
1025
906
  {
1026
- style: styles2.sectionHeader,
907
+ style: styles7.sectionHeader,
1027
908
  onPress: handleToggleExpanded,
1028
909
  activeOpacity: 0.7
1029
910
  },
1030
- /* @__PURE__ */ React6.createElement(View, { style: styles2.sectionHeaderLabelWrap }, /* @__PURE__ */ React6.createElement(Text, { style: styles2.sectionHeaderLabel }, adapter.name, /* @__PURE__ */ React6.createElement(Text, { style: styles2.sectionHeaderCount }, " (", items.length, ")"))),
1031
- /* @__PURE__ */ React6.createElement(View, { style: styles2.storageRowActions }, /* @__PURE__ */ React6.createElement(IconButton, { name: "plus", onPress: handleAdd }), /* @__PURE__ */ React6.createElement(
911
+ /* @__PURE__ */ React7.createElement(View, { style: styles7.sectionHeaderLabelWrap }, /* @__PURE__ */ React7.createElement(Text, { style: styles7.sectionHeaderLabel }, adapter.name, /* @__PURE__ */ React7.createElement(Text, { style: styles7.sectionHeaderCount }, " (", items.length, ")"))),
912
+ /* @__PURE__ */ React7.createElement(View, { style: styles7.storageRowActions }, /* @__PURE__ */ React7.createElement(IconButton, { name: "plus", onPress: handleAdd }), /* @__PURE__ */ React7.createElement(
1032
913
  IconButton,
1033
914
  {
1034
915
  name: "trash",
1035
916
  onPress: () => items.length > 0 && setClearAllVisible(true),
1036
917
  disabled: items.length === 0
1037
918
  }
1038
- ), /* @__PURE__ */ React6.createElement(View, { style: styles2.iconSlot }, /* @__PURE__ */ React6.createElement(
919
+ ), /* @__PURE__ */ React7.createElement(View, { style: styles7.iconSlot }, /* @__PURE__ */ React7.createElement(
1039
920
  Icon,
1040
921
  {
1041
922
  name: expanded ? "chevronUp" : "chevronDown",
@@ -1043,60 +924,16 @@ function StorageSection({
1043
924
  tintColor: theme.colors.text
1044
925
  }
1045
926
  )))
1046
- ), expanded && /* @__PURE__ */ React6.createElement(React6.Fragment, null, showKeychainHint ? /* @__PURE__ */ React6.createElement(View, { style: styles2.keychainHint }, /* @__PURE__ */ React6.createElement(Text, { style: styles2.keychainHintText }, strings.keychainHint)) : null, showSecureStoreHint ? /* @__PURE__ */ React6.createElement(View, { style: styles2.keychainHint }, /* @__PURE__ */ React6.createElement(Text, { style: styles2.keychainHintText }, strings.secureStoreHint)) : null, error ? /* @__PURE__ */ React6.createElement(View, { style: styles2.error }, /* @__PURE__ */ React6.createElement(Text, { style: styles2.errorText }, error)) : null, loading ? /* @__PURE__ */ React6.createElement(View, { style: styles2.loading }, /* @__PURE__ */ React6.createElement(Text, { style: styles2.loadingText }, strings.loading)) : items.map((item) => {
1047
- const isItemExpanded = expandedKeys.has(item.key);
1048
- const charCount = item.value.length;
1049
- const toggleItemExpanded = () => {
1050
- setExpandedKeys((prev) => {
1051
- const next = new Set(prev);
1052
- if (next.has(item.key)) next.delete(item.key);
1053
- else next.add(item.key);
1054
- return next;
1055
- });
1056
- };
1057
- return /* @__PURE__ */ React6.createElement(View, { key: item.key, style: styles2.itemRow }, /* @__PURE__ */ React6.createElement(
1058
- TouchableOpacity,
1059
- {
1060
- key: item.key,
1061
- onPress: toggleItemExpanded,
1062
- activeOpacity: 0.7
1063
- },
1064
- /* @__PURE__ */ React6.createElement(View, { style: styles2.itemRowCollapsed }, /* @__PURE__ */ React6.createElement(View, { style: { flex: 1 } }, /* @__PURE__ */ React6.createElement(Text, { style: styles2.itemKey, numberOfLines: 1 }, item.key), /* @__PURE__ */ React6.createElement(Text, { style: styles2.itemChars }, strings.charCount(charCount))), !isItemExpanded ? /* @__PURE__ */ React6.createElement(
1065
- ItemRowActions,
1066
- {
1067
- item,
1068
- onCopy: handleCopy,
1069
- onEdit: handleEdit,
1070
- onDelete: setDeleteItem,
1071
- showChevron: true,
1072
- chevronDirection: "down"
1073
- }
1074
- ) : /* @__PURE__ */ React6.createElement(View, { style: styles2.iconSlot }, /* @__PURE__ */ React6.createElement(
1075
- Icon,
1076
- {
1077
- name: "chevronUp",
1078
- size: LAYOUT.chevronSize,
1079
- tintColor: theme.colors.text
1080
- }
1081
- )))
1082
- ), isItemExpanded && /* @__PURE__ */ React6.createElement(View, { style: styles2.itemRowExpanded }, /* @__PURE__ */ React6.createElement(
1083
- TouchableOpacity,
1084
- {
1085
- onPress: () => handleEdit(item),
1086
- style: styles2.valueBox
1087
- },
1088
- /* @__PURE__ */ React6.createElement(Text, { style: styles2.valueBoxLabel }, strings.valueLabel),
1089
- /* @__PURE__ */ React6.createElement(Text, { style: styles2.valueBoxText, selectable: true }, item.value || strings.emptyValue)
1090
- ), /* @__PURE__ */ React6.createElement(
1091
- ItemRowActions,
1092
- {
1093
- item,
1094
- onCopy: handleCopy,
1095
- onEdit: handleEdit,
1096
- onDelete: setDeleteItem
1097
- }
1098
- )));
1099
- }), !loading && items.length === 0 && !showKeychainHint && !showSecureStoreHint ? /* @__PURE__ */ React6.createElement(View, { style: styles2.empty }, /* @__PURE__ */ React6.createElement(Text, { style: styles2.emptyText }, strings.noItems)) : null), /* @__PURE__ */ React6.createElement(
927
+ ), expanded && /* @__PURE__ */ React7.createElement(React7.Fragment, null, showKeychainHint ? /* @__PURE__ */ React7.createElement(View, { style: styles7.keychainHint }, /* @__PURE__ */ React7.createElement(Text, { style: styles7.keychainHintText }, strings.keychainHint)) : null, showSecureStoreHint ? /* @__PURE__ */ React7.createElement(View, { style: styles7.keychainHint }, /* @__PURE__ */ React7.createElement(Text, { style: styles7.keychainHintText }, strings.secureStoreHint)) : null, error ? /* @__PURE__ */ React7.createElement(View, { style: styles7.error }, /* @__PURE__ */ React7.createElement(Text, { style: styles7.errorText }, error)) : null, loading ? /* @__PURE__ */ React7.createElement(View, { style: styles7.loading }, /* @__PURE__ */ React7.createElement(Text, { style: styles7.loadingText }, strings.loading)) : items.map((item) => /* @__PURE__ */ React7.createElement(
928
+ StorageList,
929
+ {
930
+ key: item.key,
931
+ item,
932
+ onCopy: handleCopy,
933
+ onEdit: handleEdit,
934
+ onDelete: setDeleteItem
935
+ }
936
+ )), !loading && items.length === 0 && !showKeychainHint && !showSecureStoreHint ? /* @__PURE__ */ React7.createElement(View, { style: styles7.empty }, /* @__PURE__ */ React7.createElement(Text, { style: styles7.emptyText }, strings.noItems)) : null), /* @__PURE__ */ React7.createElement(
1100
937
  ItemForm,
1101
938
  {
1102
939
  visible: formVisible,
@@ -1108,7 +945,7 @@ function StorageSection({
1108
945
  setEditingItem(null);
1109
946
  }
1110
947
  }
1111
- ), /* @__PURE__ */ React6.createElement(
948
+ ), /* @__PURE__ */ React7.createElement(
1112
949
  ConfirmModal,
1113
950
  {
1114
951
  visible: deleteItem !== null,
@@ -1117,7 +954,7 @@ function StorageSection({
1117
954
  onConfirm: handleDeleteItem,
1118
955
  onCancel: () => setDeleteItem(null)
1119
956
  }
1120
- ), /* @__PURE__ */ React6.createElement(
957
+ ), /* @__PURE__ */ React7.createElement(
1121
958
  ConfirmModal,
1122
959
  {
1123
960
  visible: clearAllVisible,
@@ -1128,30 +965,92 @@ function StorageSection({
1128
965
  }
1129
966
  ));
1130
967
  }
968
+ var { colors: colors4 } = theme;
969
+ var styles7 = StyleSheet.create({
970
+ sectionHeader: {
971
+ height: LAYOUT.sectionHeaderHeight,
972
+ flexDirection: "row",
973
+ alignItems: "center",
974
+ paddingHorizontal: LAYOUT.padding,
975
+ borderBottomWidth: StyleSheet.hairlineWidth,
976
+ borderBottomColor: colors4.border,
977
+ backgroundColor: colors4.backgroundSecondary,
978
+ marginTop: LAYOUT.padding,
979
+ marginHorizontal: LAYOUT.padding,
980
+ borderRadius: LAYOUT.sectionRadius
981
+ },
982
+ sectionHeaderLabelWrap: {
983
+ flex: 1
984
+ },
985
+ sectionHeaderLabel: {
986
+ fontSize: 15,
987
+ fontWeight: "600",
988
+ color: colors4.text
989
+ },
990
+ sectionHeaderCount: {
991
+ color: colors4.textSecondary,
992
+ fontWeight: "400"
993
+ },
994
+ storageRowActions: {
995
+ flexDirection: "row",
996
+ alignItems: "center",
997
+ gap: LAYOUT.iconGap
998
+ },
999
+ iconSlot: {
1000
+ width: LAYOUT.iconButtonSize,
1001
+ height: LAYOUT.iconButtonSize,
1002
+ alignItems: "center",
1003
+ justifyContent: "center"
1004
+ },
1005
+ keychainHint: {
1006
+ padding: LAYOUT.padding,
1007
+ marginHorizontal: LAYOUT.padding,
1008
+ marginTop: 8,
1009
+ marginBottom: 4,
1010
+ backgroundColor: colors4.backgroundSecondary,
1011
+ borderRadius: 8
1012
+ },
1013
+ keychainHintText: {
1014
+ fontSize: LAYOUT.fontSize - 1,
1015
+ color: colors4.textSecondary
1016
+ },
1017
+ error: {
1018
+ padding: LAYOUT.padding,
1019
+ backgroundColor: colors4.backgroundTertiary,
1020
+ marginHorizontal: LAYOUT.padding,
1021
+ marginVertical: 8,
1022
+ borderRadius: 8
1023
+ },
1024
+ errorText: {
1025
+ fontSize: LAYOUT.fontSize,
1026
+ color: colors4.text
1027
+ },
1028
+ loading: {
1029
+ padding: LAYOUT.padding * 2,
1030
+ alignItems: "center",
1031
+ marginHorizontal: LAYOUT.padding
1032
+ },
1033
+ loadingText: {
1034
+ fontSize: LAYOUT.fontSize,
1035
+ color: colors4.textSecondary
1036
+ },
1037
+ empty: {
1038
+ padding: LAYOUT.padding * 2,
1039
+ alignItems: "center",
1040
+ marginHorizontal: LAYOUT.padding
1041
+ },
1042
+ emptyText: {
1043
+ fontSize: LAYOUT.fontSize,
1044
+ color: colors4.textMuted
1045
+ }
1046
+ });
1131
1047
 
1132
1048
  // src/components/StorageInspector.tsx
1133
- function StorageInspector({
1134
- mmkvInstances = [],
1135
- asyncStorageInstance,
1136
- keychainKeys: keychainKeysProp,
1137
- keychainInstance,
1138
- secureStoreKeys: secureStoreKeysProp,
1139
- secureStoreInstance,
1140
- customAdapters = []
1141
- }) {
1142
- const [keychainKeysAdded, setKeychainKeysAdded] = useState([]);
1143
- const [secureStoreKeysAdded, setSecureStoreKeysAdded] = useState([]);
1049
+ function StorageInspector(props) {
1050
+ const { mmkvInstances = [], secureStoreKeys, customAdapters = [] } = props;
1144
1051
  const [refreshKey, setRefreshKey] = useState(0);
1145
1052
  const [refreshing, setRefreshing] = useState(false);
1146
1053
  const [expandedIndices, setExpandedIndices] = useState(() => /* @__PURE__ */ new Set([0]));
1147
- const keychainKeys = useMemo(
1148
- () => [...keychainKeysProp ?? [], ...keychainKeysAdded],
1149
- [keychainKeysProp, keychainKeysAdded]
1150
- );
1151
- const secureStoreKeys = useMemo(
1152
- () => [...secureStoreKeysProp ?? [], ...secureStoreKeysAdded],
1153
- [secureStoreKeysProp, secureStoreKeysAdded]
1154
- );
1155
1054
  const adapters = useMemo(() => {
1156
1055
  const list = [];
1157
1056
  mmkvInstances.forEach((inst, i) => {
@@ -1159,58 +1058,34 @@ function StorageInspector({
1159
1058
  createMMKVAdapter(inst, mmkvInstances.length > 1 ? `MMKV ${i + 1}` : "MMKV")
1160
1059
  );
1161
1060
  });
1162
- const asyncAdapter = createAsyncStorageAdapter(asyncStorageInstance);
1061
+ const asyncAdapter = createAsyncStorageAdapter();
1163
1062
  if (asyncAdapter.isAvailable()) list.push(asyncAdapter);
1164
- const keychainAdapter = createKeychainAdapter(keychainKeys, keychainInstance);
1063
+ const keychainAdapter = createKeychainAdapter();
1165
1064
  if (keychainAdapter.isAvailable()) list.push(keychainAdapter);
1166
- const secureStoreAdapter = createSecureStoreAdapter(
1167
- secureStoreKeys,
1168
- secureStoreInstance
1169
- );
1065
+ const secureStoreAdapter = createSecureStoreAdapter(secureStoreKeys ?? []);
1170
1066
  if (secureStoreAdapter.isAvailable()) list.push(secureStoreAdapter);
1171
1067
  list.push(...customAdapters);
1172
- return list;
1173
- }, [
1174
- mmkvInstances,
1175
- asyncStorageInstance,
1176
- keychainKeys,
1177
- keychainInstance,
1178
- secureStoreKeys,
1179
- secureStoreInstance,
1180
- customAdapters
1181
- ]);
1182
- const handleKeychainKeyAdded = (key) => {
1183
- setKeychainKeysAdded(
1184
- (prev) => prev.includes(key) ? prev : [...prev, key]
1185
- );
1186
- };
1187
- const handleSecureStoreKeyAdded = (key) => {
1188
- setSecureStoreKeysAdded(
1189
- (prev) => prev.includes(key) ? prev : [...prev, key]
1190
- );
1191
- };
1068
+ return list.sort((a, b) => a.name.localeCompare(b.name));
1069
+ }, [mmkvInstances, secureStoreKeys, customAdapters]);
1192
1070
  const handleRefresh = () => {
1193
1071
  setRefreshing(true);
1194
1072
  setRefreshKey((k) => k + 1);
1195
1073
  setTimeout(() => setRefreshing(false), 400);
1196
1074
  };
1197
- return /* @__PURE__ */ React6.createElement(View, { style: styles2.container }, /* @__PURE__ */ React6.createElement(View, { style: styles2.content }, adapters.length > 0 ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(
1075
+ return /* @__PURE__ */ React7.createElement(View, { style: styles8.container }, /* @__PURE__ */ React7.createElement(View, { style: styles8.content }, adapters.length > 0 ? /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(
1198
1076
  ScrollView,
1199
1077
  {
1200
- style: styles2.scroll,
1201
- contentContainerStyle: styles2.scrollContent,
1078
+ style: styles8.scroll,
1079
+ contentContainerStyle: styles8.scrollContent,
1202
1080
  keyboardShouldPersistTaps: "handled",
1203
1081
  showsVerticalScrollIndicator: true,
1204
- refreshControl: /* @__PURE__ */ React6.createElement(RefreshControl, { refreshing, onRefresh: handleRefresh })
1082
+ refreshControl: /* @__PURE__ */ React7.createElement(RefreshControl, { refreshing, onRefresh: handleRefresh })
1205
1083
  },
1206
- adapters.map((adapter, index) => /* @__PURE__ */ React6.createElement(
1084
+ adapters.map((adapter, index) => /* @__PURE__ */ React7.createElement(
1207
1085
  StorageSection,
1208
1086
  {
1209
1087
  key: `${adapter.type}-${index}`,
1210
1088
  adapter,
1211
- keychainKeys: keychainKeysProp,
1212
- onKeychainKeyAdded: handleKeychainKeyAdded,
1213
- onSecureStoreKeyAdded: handleSecureStoreKeyAdded,
1214
1089
  expanded: expandedIndices.has(index),
1215
1090
  onToggleExpanded: () => {
1216
1091
  setExpandedIndices((prev) => {
@@ -1223,18 +1098,57 @@ function StorageInspector({
1223
1098
  refreshTrigger: refreshKey
1224
1099
  }
1225
1100
  ))
1226
- ), /* @__PURE__ */ React6.createElement(
1101
+ ), /* @__PURE__ */ React7.createElement(
1227
1102
  IconButton,
1228
1103
  {
1229
1104
  name: "refresh",
1230
1105
  onPress: handleRefresh,
1231
1106
  size: 24,
1232
1107
  tintColor: theme.colors.inverted,
1233
- style: styles2.fab,
1108
+ style: styles8.fab,
1234
1109
  activeOpacity: 0.85
1235
1110
  }
1236
- )) : /* @__PURE__ */ React6.createElement(View, { style: styles2.empty }, /* @__PURE__ */ React6.createElement(Text, { style: styles2.emptyText }, strings.noAdapterAvailable))));
1111
+ )) : /* @__PURE__ */ React7.createElement(View, { style: styles8.empty }, /* @__PURE__ */ React7.createElement(Text, { style: styles8.emptyText }, strings.noAdapterAvailable))));
1237
1112
  }
1113
+ var { colors: colors5 } = theme;
1114
+ var styles8 = StyleSheet.create({
1115
+ container: {
1116
+ flex: 1,
1117
+ width: "100%",
1118
+ backgroundColor: colors5.background
1119
+ },
1120
+ content: {
1121
+ flex: 1
1122
+ },
1123
+ scroll: {
1124
+ flex: 1
1125
+ },
1126
+ scrollContent: {
1127
+ flexGrow: 1,
1128
+ paddingBottom: LAYOUT.fabSize + LAYOUT.padding + 20,
1129
+ paddingTop: LAYOUT.padding
1130
+ },
1131
+ fab: {
1132
+ position: "absolute",
1133
+ bottom: LAYOUT.padding + 16,
1134
+ right: LAYOUT.padding + 16,
1135
+ width: LAYOUT.fabSize,
1136
+ height: LAYOUT.fabSize,
1137
+ borderRadius: LAYOUT.fabSize / 2,
1138
+ backgroundColor: colors5.text,
1139
+ alignItems: "center",
1140
+ justifyContent: "center"
1141
+ },
1142
+ empty: {
1143
+ padding: LAYOUT.padding * 2,
1144
+ alignItems: "center",
1145
+ marginHorizontal: LAYOUT.padding
1146
+ },
1147
+ emptyText: {
1148
+ fontSize: LAYOUT.fontSize,
1149
+ color: colors5.textMuted
1150
+ }
1151
+ });
1238
1152
 
1239
1153
  export { StorageInspector, createAsyncStorageAdapter, createKeychainAdapter, createMMKVAdapter, createSecureStoreAdapter, strings, theme };
1240
1154
  //# sourceMappingURL=index.mjs.map