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