react-native-inapp-inspector 2.3.18 → 2.3.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.kt +47 -0
  2. package/dist/commonjs/capture.d.ts +2 -0
  3. package/dist/commonjs/capture.js +3 -0
  4. package/dist/commonjs/components/EmptyState.d.ts +1 -0
  5. package/dist/commonjs/components/EmptyState.js +2 -2
  6. package/dist/commonjs/components/Inspector/FabLauncher.js +494 -39
  7. package/dist/commonjs/components/Inspector/InspectorHeader.js +45 -26
  8. package/dist/commonjs/components/Inspector/MainScreen.js +4 -3
  9. package/dist/commonjs/components/Inspector/MediaGalleryTab.js +418 -13
  10. package/dist/commonjs/components/Inspector/MediaPreviewModal.js +376 -88
  11. package/dist/commonjs/components/Inspector/NetworkDetail.js +49 -63
  12. package/dist/commonjs/components/Inspector/SettingsPanel.js +499 -119
  13. package/dist/commonjs/components/Inspector/TabBar.js +166 -4
  14. package/dist/commonjs/components/JsonViewer.js +4 -3
  15. package/dist/commonjs/components/NetworkIcons.d.ts +4 -0
  16. package/dist/commonjs/components/NetworkIcons.js +26 -1
  17. package/dist/commonjs/components/SegmentedTabs.js +1 -0
  18. package/dist/commonjs/constants/version.d.ts +1 -1
  19. package/dist/commonjs/constants/version.js +1 -1
  20. package/dist/commonjs/i18n/locales/en.json +20 -2
  21. package/dist/commonjs/index.js +45 -30
  22. package/dist/commonjs/native/NativeInspector.d.ts +3 -0
  23. package/dist/commonjs/native/NativeInspector.js +14 -1
  24. package/dist/commonjs/native/NativeNetworkInspector.d.ts +1 -0
  25. package/dist/commonjs/styles/AppColors.js +78 -78
  26. package/dist/commonjs/types/enums.d.ts +2 -0
  27. package/dist/commonjs/types/enums.js +2 -0
  28. package/dist/commonjs/types/interfaces.d.ts +5 -0
  29. package/dist/esm/capture.d.ts +2 -0
  30. package/dist/esm/capture.js +4 -1
  31. package/dist/esm/components/EmptyState.d.ts +1 -0
  32. package/dist/esm/components/EmptyState.js +2 -2
  33. package/dist/esm/components/Inspector/FabLauncher.js +462 -40
  34. package/dist/esm/components/Inspector/InspectorHeader.js +46 -27
  35. package/dist/esm/components/Inspector/MainScreen.js +4 -3
  36. package/dist/esm/components/Inspector/MediaGalleryTab.js +419 -14
  37. package/dist/esm/components/Inspector/MediaPreviewModal.js +379 -91
  38. package/dist/esm/components/Inspector/NetworkDetail.js +49 -63
  39. package/dist/esm/components/Inspector/SettingsPanel.js +500 -120
  40. package/dist/esm/components/Inspector/TabBar.js +135 -6
  41. package/dist/esm/components/JsonViewer.js +4 -3
  42. package/dist/esm/components/NetworkIcons.d.ts +4 -0
  43. package/dist/esm/components/NetworkIcons.js +21 -0
  44. package/dist/esm/components/SegmentedTabs.js +1 -0
  45. package/dist/esm/constants/version.d.ts +1 -1
  46. package/dist/esm/constants/version.js +1 -1
  47. package/dist/esm/i18n/locales/en.json +20 -2
  48. package/dist/esm/index.js +44 -29
  49. package/dist/esm/native/NativeInspector.d.ts +3 -0
  50. package/dist/esm/native/NativeInspector.js +12 -0
  51. package/dist/esm/native/NativeNetworkInspector.d.ts +1 -0
  52. package/dist/esm/styles/AppColors.js +78 -78
  53. package/dist/esm/types/enums.d.ts +2 -0
  54. package/dist/esm/types/enums.js +2 -0
  55. package/dist/esm/types/interfaces.d.ts +5 -0
  56. package/ios/NetworkInspectorModule.mm +241 -136
  57. package/package.json +1 -1
  58. package/react-native-inapp-inspector.podspec +1 -1
  59. package/src/capture.ts +8 -0
  60. package/src/components/EmptyState.tsx +3 -1
  61. package/src/components/Inspector/FabLauncher.tsx +562 -52
  62. package/src/components/Inspector/InspectorHeader.tsx +60 -22
  63. package/src/components/Inspector/MainScreen.tsx +3 -2
  64. package/src/components/Inspector/MediaGalleryTab.tsx +502 -28
  65. package/src/components/Inspector/MediaPreviewModal.tsx +465 -117
  66. package/src/components/Inspector/NetworkDetail.tsx +80 -97
  67. package/src/components/Inspector/SettingsPanel.tsx +708 -152
  68. package/src/components/Inspector/TabBar.tsx +174 -6
  69. package/src/components/JsonViewer.tsx +4 -2
  70. package/src/components/NetworkIcons.tsx +56 -0
  71. package/src/components/SegmentedTabs.tsx +1 -0
  72. package/src/constants/version.ts +1 -1
  73. package/src/i18n/locales/en.json +20 -4
  74. package/src/index.tsx +46 -31
  75. package/src/native/NativeInspector.ts +17 -0
  76. package/src/native/NativeNetworkInspector.ts +1 -0
  77. package/src/styles/AppColors.ts +80 -80
  78. package/src/types/enums.ts +2 -0
  79. package/src/types/interfaces.ts +5 -0
@@ -5,7 +5,6 @@ import {
5
5
  Image,
6
6
  Modal,
7
7
  Platform,
8
- Share,
9
8
  StyleSheet,
10
9
  Text,
11
10
  TouchableOpacity,
@@ -15,16 +14,18 @@ import {AppColors} from '../../styles/AppColors';
15
14
  import {AppFonts} from '../../styles/AppFonts';
16
15
  import TouchableScale from '../TouchableScale';
17
16
  import {
18
- CloseWhite,
19
- CopyIcon,
17
+ ChevronDownIcon,
18
+ ExpandCollapseIcon,
20
19
  FilmIcon,
21
20
  GifIcon,
22
21
  ImageIcon,
23
- SendIcon,
22
+ PauseIcon,
23
+ PlayIcon,
24
+ RepeatIcon,
24
25
  TrashIcon,
25
26
  } from '../NetworkIcons';
26
27
  import {CapturedMediaItem, ScreenCapture} from '../../capture';
27
- import {copyToClipboard, formatBytes} from '../../helpers';
28
+ import {formatBytes} from '../../helpers';
28
29
  import {showToast} from '../../helpers/toast';
29
30
  import {useTranslation} from '../../i18n';
30
31
 
@@ -46,26 +47,25 @@ export const MediaPreviewModal: React.FC<MediaPreviewModalProps> = ({
46
47
  const {t} = useTranslation();
47
48
  const [isConverting, setIsConverting] = useState(false);
48
49
  const [imageError, setImageError] = useState(false);
50
+ const [isExpanded, setIsExpanded] = useState(false);
51
+ const [isPlaying, setIsPlaying] = useState(false);
52
+ const [playbackSpeed, setPlaybackSpeed] = useState<'1.0x' | '1.5x' | '2.0x'>('1.0x');
53
+ const [isLooping, setIsLooping] = useState(true);
54
+ const [scrubPosition, setScrubPosition] = useState(0.4);
49
55
 
50
56
  if (!item) return null;
51
57
 
52
- const handleShare = async () => {
58
+ const handlePlayVideo = async () => {
53
59
  try {
54
- await Share.share(
55
- Platform.OS === 'ios'
56
- ? {url: item.uri, title: item.filename}
57
- : {message: `Captured media: ${item.filename}`, url: item.uri},
58
- );
60
+ setIsPlaying(true);
61
+ await ScreenCapture.playVideo(item.uri);
59
62
  } catch {
60
- showToast(t('mediaGallery.shareUnavailable'));
63
+ showToast(t('mediaGallery.playError', 'Unable to play video'));
64
+ } finally {
65
+ setIsPlaying(false);
61
66
  }
62
67
  };
63
68
 
64
- const handleCopyUri = () => {
65
- copyToClipboard(item.uri);
66
- showToast(t('mediaGallery.uriCopied'));
67
- };
68
-
69
69
  const handleDelete = () => {
70
70
  Alert.alert(
71
71
  t('mediaGallery.deleteTitle'),
@@ -100,6 +100,10 @@ export const MediaPreviewModal: React.FC<MediaPreviewModalProps> = ({
100
100
  const isVideo = item.type === 'video';
101
101
  const isGif = item.type === 'gif';
102
102
  const durationSec = item.durationMs ? (item.durationMs / 1000).toFixed(1) : null;
103
+ const formattedDuration = durationSec
104
+ ? `00:${Number(durationSec) < 10 ? '0' : ''}${durationSec}`
105
+ : '00:05.0';
106
+ const hasBottomToolbar = isVideo || isGif;
103
107
 
104
108
  return (
105
109
  <Modal
@@ -157,23 +161,107 @@ export const MediaPreviewModal: React.FC<MediaPreviewModalProps> = ({
157
161
  </Text>
158
162
  </View>
159
163
  </View>
160
- <TouchableOpacity
161
- onPress={onClose}
162
- style={previewStyles.closeBtn}
163
- hitSlop={{top: 10, bottom: 10, left: 10, right: 10}}>
164
- <CloseWhite size={14} color={AppColors.white} />
165
- </TouchableOpacity>
164
+
165
+ {/* Top Actions: Delete, Expand/Contract, Minimize */}
166
+ <View style={previewStyles.headerRight}>
167
+ <TouchableScale
168
+ accessible={true}
169
+ accessibilityRole="button"
170
+ accessibilityLabel={t('mediaGallery.delete')}
171
+ onPress={handleDelete}
172
+ style={previewStyles.headerDeleteBtn}
173
+ hitSlop={{top: 10, bottom: 10, left: 10, right: 10}}>
174
+ <TrashIcon size={14} color={AppColors.red500} />
175
+ </TouchableScale>
176
+
177
+ <TouchableOpacity
178
+ accessible={true}
179
+ accessibilityRole="button"
180
+ accessibilityLabel={isExpanded ? t('mediaGallery.collapse', 'Exit Fullscreen') : t('mediaGallery.expand', 'Expand Fullscreen')}
181
+ onPress={() => setIsExpanded(!isExpanded)}
182
+ style={[
183
+ previewStyles.headerIconBtn,
184
+ isExpanded && {backgroundColor: `${AppColors.sky400}30`, borderColor: `${AppColors.sky400}70`},
185
+ ]}
186
+ hitSlop={{top: 10, bottom: 10, left: 10, right: 10}}>
187
+ <ExpandCollapseIcon
188
+ size={14}
189
+ isExpanded={isExpanded}
190
+ color={isExpanded ? AppColors.sky400 : AppColors.white}
191
+ />
192
+ </TouchableOpacity>
193
+
194
+ <TouchableOpacity
195
+ accessible={true}
196
+ accessibilityRole="button"
197
+ accessibilityLabel={t('mediaGallery.minimize', 'Minimize')}
198
+ onPress={onClose}
199
+ style={previewStyles.headerIconBtn}
200
+ hitSlop={{top: 10, bottom: 10, left: 10, right: 10}}>
201
+ <ChevronDownIcon size={15} color={AppColors.white} />
202
+ </TouchableOpacity>
203
+ </View>
166
204
  </View>
167
205
 
168
206
  {/* Media Preview Stage */}
169
- <View style={previewStyles.stage}>
170
- <View style={previewStyles.previewCard}>
171
- <Image
172
- source={{uri: item.uri}}
173
- style={previewStyles.image}
174
- resizeMode="contain"
175
- onError={() => setImageError(true)}
176
- />
207
+ <View
208
+ style={[
209
+ previewStyles.stage,
210
+ isExpanded && previewStyles.stageExpanded,
211
+ !hasBottomToolbar && {
212
+ paddingBottom: Platform.OS === 'ios' ? 34 : 16,
213
+ },
214
+ ]}>
215
+ <TouchableOpacity
216
+ activeOpacity={isVideo ? 0.9 : 1}
217
+ onPress={isVideo ? handlePlayVideo : undefined}
218
+ style={[
219
+ previewStyles.previewCard,
220
+ isExpanded && previewStyles.previewCardExpanded,
221
+ ]}>
222
+ {isVideo ? (
223
+ item.thumbnailUri && !imageError ? (
224
+ <>
225
+ <Image
226
+ source={{uri: item.thumbnailUri}}
227
+ style={previewStyles.image}
228
+ resizeMode="contain"
229
+ onError={() => setImageError(true)}
230
+ />
231
+ <View style={previewStyles.videoImageOverlay} />
232
+ </>
233
+ ) : (
234
+ <View style={previewStyles.videoFallbackBackdrop}>
235
+ <View style={previewStyles.videoFallbackGlow} />
236
+ <FilmIcon size={52} color={AppColors.sky400} />
237
+ <Text style={previewStyles.videoFallbackTitle}>
238
+ {t('mediaGallery.playVideo', 'Play Video')}
239
+ </Text>
240
+ <Text style={previewStyles.videoFallbackSubtitle}>
241
+ {item.width && item.height ? `${item.width} × ${item.height}` : 'HD Video'} • MP4
242
+ </Text>
243
+ </View>
244
+ )
245
+ ) : (
246
+ <Image
247
+ source={{uri: item.uri}}
248
+ style={previewStyles.image}
249
+ resizeMode="contain"
250
+ onError={() => setImageError(true)}
251
+ />
252
+ )}
253
+
254
+ {/* Central Play Button Overlay for Videos */}
255
+ {isVideo && (
256
+ <View style={previewStyles.centerPlayOverlay}>
257
+ <View style={previewStyles.centerPlayCircle}>
258
+ <PlayIcon size={30} color={AppColors.white} />
259
+ </View>
260
+ <Text style={previewStyles.centerPlayText}>
261
+ {t('mediaGallery.playVideo', 'Play Video')}
262
+ </Text>
263
+ </View>
264
+ )}
177
265
 
178
266
  {/* Video overlay badge with playback duration */}
179
267
  {isVideo && (
@@ -196,81 +284,150 @@ export const MediaPreviewModal: React.FC<MediaPreviewModalProps> = ({
196
284
  </View>
197
285
  </View>
198
286
  )}
199
- </View>
200
287
 
201
- {/* Quick Details Card */}
202
- <View style={previewStyles.metaPillRow}>
203
- {item.width && item.height ? (
204
- <View style={previewStyles.metaPill}>
205
- <Text style={previewStyles.metaPillLabel}>DIMENSIONS</Text>
206
- <Text style={previewStyles.metaPillValue}>{`${item.width} × ${item.height}`}</Text>
207
- </View>
208
- ) : null}
209
- <View style={previewStyles.metaPill}>
210
- <Text style={previewStyles.metaPillLabel}>SIZE</Text>
211
- <Text style={previewStyles.metaPillValue}>{formatBytes(item.sizeBytes)}</Text>
288
+ {/* Floating Meta Chip inside Card */}
289
+ <View style={previewStyles.floatingMetaBadge}>
290
+ <Text style={previewStyles.floatingMetaText}>
291
+ {item.width && item.height ? `${item.width} × ${item.height} • ` : ''}
292
+ {formatBytes(item.sizeBytes)}
293
+ {durationSec ? ` • ${durationSec}s` : ''}
294
+ </Text>
212
295
  </View>
213
- {durationSec ? (
214
- <View style={previewStyles.metaPill}>
215
- <Text style={previewStyles.metaPillLabel}>DURATION</Text>
216
- <Text style={previewStyles.metaPillValue}>{`${durationSec}s`}</Text>
217
- </View>
218
- ) : null}
219
- </View>
296
+ </TouchableOpacity>
220
297
  </View>
221
298
 
222
- {/* Bottom Toolbar */}
223
- <View style={previewStyles.footer}>
224
- {isVideo && onConvertToGif && (
225
- <TouchableScale
226
- onPress={handleConvert}
227
- disabled={isConverting}
228
- style={[
229
- previewStyles.actionBtn,
230
- {
231
- backgroundColor: `${AppColors.warningAmber}25`,
232
- borderColor: `${AppColors.warningAmber}70`,
233
- },
234
- ]}>
235
- <GifIcon size={15} color={AppColors.warningAmber} />
236
- <Text style={[previewStyles.actionText, {color: AppColors.warningAmber}]}>
237
- {isConverting ? t('mediaGallery.converting') : t('mediaGallery.convertToGif')}
299
+ {/* Video Controls & Scrub Bar (For Videos) */}
300
+ {isVideo && (
301
+ <View style={previewStyles.videoControlsPanel}>
302
+ {/* Timeline Progress Bar */}
303
+ <View style={previewStyles.progressSection}>
304
+ <Text style={previewStyles.timeLabel}>
305
+ 00:0{Math.floor(scrubPosition * (Number(durationSec) || 5))}
238
306
  </Text>
239
- </TouchableScale>
240
- )}
307
+ <TouchableOpacity
308
+ activeOpacity={0.8}
309
+ onPress={() => setScrubPosition(pos => (pos >= 0.9 ? 0.1 : pos + 0.3))}
310
+ style={previewStyles.progressTrack}>
311
+ <View
312
+ style={[
313
+ previewStyles.progressFill,
314
+ {width: `${Math.max(10, Math.min(100, scrubPosition * 100))}%`},
315
+ ]}
316
+ />
317
+ <View
318
+ style={[
319
+ previewStyles.scrubberThumb,
320
+ {left: `${Math.max(8, Math.min(96, scrubPosition * 100))}%`},
321
+ ]}
322
+ />
323
+ </TouchableOpacity>
324
+ <Text style={previewStyles.timeLabel}>{formattedDuration}</Text>
325
+ </View>
241
326
 
242
- <TouchableScale onPress={handleShare} style={previewStyles.actionBtn}>
243
- <SendIcon size={15} color={AppColors.sky400} />
244
- <Text style={previewStyles.actionText}>{t('mediaGallery.share')}</Text>
245
- </TouchableScale>
327
+ {/* Extra Controls: Play/Pause, Speed, Loop */}
328
+ <View style={previewStyles.controlsRow}>
329
+ {/* Play / Pause toggle */}
330
+ <TouchableOpacity
331
+ onPress={handlePlayVideo}
332
+ style={previewStyles.controlActionPill}>
333
+ {isPlaying ? (
334
+ <PauseIcon size={14} color={AppColors.sky400} />
335
+ ) : (
336
+ <PlayIcon size={14} color={AppColors.sky400} />
337
+ )}
338
+ <Text style={[previewStyles.controlActionText, {color: AppColors.sky400}]}>
339
+ {isPlaying ? t('mediaGallery.pause', 'Pause') : t('mediaGallery.play', 'Play')}
340
+ </Text>
341
+ </TouchableOpacity>
246
342
 
247
- <TouchableScale onPress={handleCopyUri} style={previewStyles.actionBtn}>
248
- <CopyIcon size={15} color={AppColors.white} />
249
- <Text style={previewStyles.actionText}>{t('mediaGallery.copyUri')}</Text>
250
- </TouchableScale>
343
+ {/* Playback Speed selector */}
344
+ <View style={previewStyles.speedGroup}>
345
+ {(['1.0x', '1.5x', '2.0x'] as const).map(speed => (
346
+ <TouchableOpacity
347
+ key={speed}
348
+ onPress={() => setPlaybackSpeed(speed)}
349
+ style={[
350
+ previewStyles.speedBtn,
351
+ playbackSpeed === speed && previewStyles.speedBtnActive,
352
+ ]}>
353
+ <Text
354
+ style={[
355
+ previewStyles.speedText,
356
+ playbackSpeed === speed && previewStyles.speedTextActive,
357
+ ]}>
358
+ {speed}
359
+ </Text>
360
+ </TouchableOpacity>
361
+ ))}
362
+ </View>
251
363
 
252
- <TouchableScale
253
- onPress={handleDelete}
254
- style={[
255
- previewStyles.actionBtn,
256
- {
257
- backgroundColor: `${AppColors.red500}25`,
258
- borderColor: `${AppColors.red500}70`,
259
- },
260
- ]}>
261
- <TrashIcon size={15} color={AppColors.red500} />
262
- <Text style={[previewStyles.actionText, {color: AppColors.red500}]}>
263
- {t('mediaGallery.delete')}
264
- </Text>
265
- </TouchableScale>
266
- </View>
364
+ {/* Loop Toggle */}
365
+ <TouchableOpacity
366
+ onPress={() => setIsLooping(!isLooping)}
367
+ style={[
368
+ previewStyles.loopPill,
369
+ isLooping && previewStyles.loopPillActive,
370
+ ]}>
371
+ <RepeatIcon
372
+ size={12}
373
+ color={isLooping ? AppColors.sky400 : AppColors.whiteAlpha60}
374
+ />
375
+ <Text
376
+ style={[
377
+ previewStyles.loopText,
378
+ isLooping && previewStyles.loopTextActive,
379
+ ]}>
380
+ {isLooping ? 'Loop: ON' : 'Loop: OFF'}
381
+ </Text>
382
+ </TouchableOpacity>
383
+ </View>
384
+ </View>
385
+ )}
386
+
387
+ {/* Bottom Primary Actions */}
388
+ {hasBottomToolbar && (
389
+ <View style={previewStyles.footer}>
390
+ {isVideo && (
391
+ <TouchableScale
392
+ onPress={handlePlayVideo}
393
+ style={[
394
+ previewStyles.actionBtn,
395
+ {
396
+ backgroundColor: `${AppColors.sky400}25`,
397
+ borderColor: `${AppColors.sky400}70`,
398
+ },
399
+ ]}>
400
+ <PlayIcon size={15} color={AppColors.sky400} />
401
+ <Text style={[previewStyles.actionText, {color: AppColors.sky400, fontFamily: AppFonts.interSemiBold}]}>
402
+ {t('mediaGallery.playVideo', 'Play Video')}
403
+ </Text>
404
+ </TouchableScale>
405
+ )}
406
+
407
+ {onConvertToGif && isVideo && (
408
+ <TouchableScale
409
+ onPress={handleConvert}
410
+ disabled={isConverting}
411
+ style={[
412
+ previewStyles.actionBtn,
413
+ {
414
+ backgroundColor: `${AppColors.warningAmber}25`,
415
+ borderColor: `${AppColors.warningAmber}70`,
416
+ },
417
+ ]}>
418
+ <GifIcon size={15} color={AppColors.warningAmber} />
419
+ <Text style={[previewStyles.actionText, {color: AppColors.warningAmber}]}>
420
+ {isConverting ? t('mediaGallery.converting') : t('mediaGallery.convertToGif')}
421
+ </Text>
422
+ </TouchableScale>
423
+ )}
424
+ </View>
425
+ )}
267
426
  </View>
268
427
  </Modal>
269
428
  );
270
429
  };
271
430
 
272
- const {width: SCREEN_WIDTH, height: SCREEN_HEIGHT} = Dimensions.get('window');
273
-
274
431
  const previewStyles = StyleSheet.create({
275
432
  overlay: {
276
433
  flex: 1,
@@ -296,6 +453,11 @@ const previewStyles = StyleSheet.create({
296
453
  minWidth: 0,
297
454
  marginRight: 10,
298
455
  },
456
+ headerRight: {
457
+ flexDirection: 'row',
458
+ alignItems: 'center',
459
+ gap: 8,
460
+ },
299
461
  badge: {
300
462
  flexDirection: 'row',
301
463
  alignItems: 'center',
@@ -321,11 +483,23 @@ const previewStyles = StyleSheet.create({
321
483
  color: AppColors.whiteAlpha60,
322
484
  marginTop: 1,
323
485
  },
324
- closeBtn: {
486
+ headerDeleteBtn: {
487
+ width: 32,
488
+ height: 32,
489
+ borderRadius: 16,
490
+ backgroundColor: `${AppColors.red500}20`,
491
+ borderWidth: 1,
492
+ borderColor: `${AppColors.red500}60`,
493
+ alignItems: 'center',
494
+ justifyContent: 'center',
495
+ },
496
+ headerIconBtn: {
325
497
  width: 32,
326
498
  height: 32,
327
499
  borderRadius: 16,
328
500
  backgroundColor: AppColors.whiteAlpha12,
501
+ borderWidth: 1,
502
+ borderColor: AppColors.whiteAlpha20,
329
503
  alignItems: 'center',
330
504
  justifyContent: 'center',
331
505
  },
@@ -333,13 +507,19 @@ const previewStyles = StyleSheet.create({
333
507
  flex: 1,
334
508
  alignItems: 'center',
335
509
  justifyContent: 'center',
336
- paddingHorizontal: 16,
337
- paddingVertical: 12,
510
+ paddingHorizontal: 10,
511
+ paddingTop: 10,
512
+ paddingBottom: 10,
513
+ },
514
+ stageExpanded: {
515
+ paddingHorizontal: 0,
516
+ paddingTop: 0,
517
+ paddingBottom: 0,
338
518
  },
339
519
  previewCard: {
340
- width: SCREEN_WIDTH - 32,
341
- height: SCREEN_HEIGHT * 0.60,
342
- borderRadius: 12,
520
+ flex: 1,
521
+ width: '100%',
522
+ borderRadius: 14,
343
523
  overflow: 'hidden',
344
524
  backgroundColor: '#05070D',
345
525
  borderWidth: 1,
@@ -348,10 +528,73 @@ const previewStyles = StyleSheet.create({
348
528
  justifyContent: 'center',
349
529
  position: 'relative',
350
530
  },
531
+ previewCardExpanded: {
532
+ borderRadius: 0,
533
+ borderWidth: 0,
534
+ },
351
535
  image: {
352
536
  width: '100%',
353
537
  height: '100%',
354
538
  },
539
+ videoImageOverlay: {
540
+ ...StyleSheet.absoluteFillObject,
541
+ backgroundColor: 'rgba(5, 7, 13, 0.25)',
542
+ },
543
+ videoFallbackBackdrop: {
544
+ flex: 1,
545
+ width: '100%',
546
+ backgroundColor: '#070C18',
547
+ alignItems: 'center',
548
+ justifyContent: 'center',
549
+ gap: 8,
550
+ },
551
+ videoFallbackGlow: {
552
+ position: 'absolute',
553
+ width: 140,
554
+ height: 140,
555
+ borderRadius: 70,
556
+ backgroundColor: `${AppColors.sky400}18`,
557
+ },
558
+ videoFallbackTitle: {
559
+ fontFamily: AppFonts.interSemiBold,
560
+ fontSize: 15,
561
+ color: AppColors.white,
562
+ marginTop: 8,
563
+ },
564
+ videoFallbackSubtitle: {
565
+ fontFamily: AppFonts.interRegular,
566
+ fontSize: 12,
567
+ color: AppColors.whiteAlpha60,
568
+ },
569
+ centerPlayOverlay: {
570
+ position: 'absolute',
571
+ alignItems: 'center',
572
+ justifyContent: 'center',
573
+ gap: 8,
574
+ },
575
+ centerPlayCircle: {
576
+ width: 64,
577
+ height: 64,
578
+ borderRadius: 32,
579
+ backgroundColor: `${AppColors.sky400}E0`,
580
+ alignItems: 'center',
581
+ justifyContent: 'center',
582
+ paddingLeft: 3,
583
+ shadowColor: AppColors.sky400,
584
+ shadowOffset: {width: 0, height: 4},
585
+ shadowOpacity: 0.5,
586
+ shadowRadius: 10,
587
+ elevation: 8,
588
+ },
589
+ centerPlayText: {
590
+ fontFamily: AppFonts.interSemiBold,
591
+ fontSize: 13,
592
+ color: AppColors.white,
593
+ letterSpacing: 0.3,
594
+ textShadowColor: 'rgba(0,0,0,0.7)',
595
+ textShadowOffset: {width: 0, height: 1},
596
+ textShadowRadius: 3,
597
+ },
355
598
  videoBadgeOverlay: {
356
599
  position: 'absolute',
357
600
  bottom: 12,
@@ -374,34 +617,139 @@ const previewStyles = StyleSheet.create({
374
617
  color: AppColors.white,
375
618
  letterSpacing: 0.3,
376
619
  },
377
- metaPillRow: {
378
- flexDirection: 'row',
620
+ floatingMetaBadge: {
621
+ position: 'absolute',
622
+ bottom: 10,
623
+ backgroundColor: `${AppColors.slate900}D9`,
624
+ paddingHorizontal: 10,
625
+ paddingVertical: 4.5,
626
+ borderRadius: 8,
627
+ borderWidth: 1,
628
+ borderColor: AppColors.whiteAlpha15,
379
629
  alignItems: 'center',
380
630
  justifyContent: 'center',
631
+ },
632
+ floatingMetaText: {
633
+ fontFamily: AppFonts.interMedium,
634
+ fontSize: 10.5,
635
+ color: AppColors.slate200,
636
+ letterSpacing: 0.2,
637
+ },
638
+ videoControlsPanel: {
639
+ paddingHorizontal: 16,
640
+ paddingTop: 8,
641
+ paddingBottom: 6,
642
+ backgroundColor: AppColors.headerGlassDark,
643
+ borderTopWidth: 1,
644
+ borderTopColor: AppColors.borderGlassLight,
381
645
  gap: 8,
382
- marginTop: 14,
383
- flexWrap: 'wrap',
384
646
  },
385
- metaPill: {
386
- backgroundColor: AppColors.whiteAlpha08,
647
+ progressSection: {
648
+ flexDirection: 'row',
649
+ alignItems: 'center',
650
+ gap: 10,
651
+ },
652
+ timeLabel: {
653
+ fontFamily: AppFonts.interMedium,
654
+ fontSize: 11,
655
+ color: AppColors.whiteAlpha60,
656
+ minWidth: 42,
657
+ },
658
+ progressTrack: {
659
+ flex: 1,
660
+ height: 6,
661
+ borderRadius: 3,
662
+ backgroundColor: AppColors.whiteAlpha15,
663
+ justifyContent: 'center',
664
+ position: 'relative',
665
+ },
666
+ progressFill: {
667
+ height: '100%',
668
+ borderRadius: 3,
669
+ backgroundColor: AppColors.sky400,
670
+ },
671
+ scrubberThumb: {
672
+ position: 'absolute',
673
+ width: 12,
674
+ height: 12,
675
+ borderRadius: 6,
676
+ backgroundColor: AppColors.white,
677
+ borderWidth: 2,
678
+ borderColor: AppColors.sky400,
679
+ top: -3,
680
+ marginLeft: -6,
681
+ },
682
+ controlsRow: {
683
+ flexDirection: 'row',
684
+ alignItems: 'center',
685
+ justifyContent: 'space-between',
686
+ paddingVertical: 2,
687
+ },
688
+ controlActionPill: {
689
+ flexDirection: 'row',
690
+ alignItems: 'center',
691
+ gap: 5,
387
692
  paddingHorizontal: 10,
388
693
  paddingVertical: 5,
389
- borderRadius: 8,
694
+ borderRadius: 6,
695
+ backgroundColor: `${AppColors.sky400}18`,
696
+ borderWidth: 1,
697
+ borderColor: `${AppColors.sky400}50`,
698
+ },
699
+ controlActionText: {
700
+ fontFamily: AppFonts.interSemiBold,
701
+ fontSize: 11.5,
702
+ },
703
+ speedGroup: {
704
+ flexDirection: 'row',
705
+ alignItems: 'center',
706
+ backgroundColor: AppColors.whiteAlpha08,
707
+ borderRadius: 6,
390
708
  borderWidth: 1,
391
709
  borderColor: AppColors.whiteAlpha12,
710
+ padding: 2,
711
+ gap: 2,
712
+ },
713
+ speedBtn: {
714
+ paddingHorizontal: 7,
715
+ paddingVertical: 3,
716
+ borderRadius: 4,
717
+ },
718
+ speedBtnActive: {
719
+ backgroundColor: `${AppColors.sky400}30`,
720
+ },
721
+ speedText: {
722
+ fontFamily: AppFonts.interMedium,
723
+ fontSize: 10.5,
724
+ color: AppColors.whiteAlpha60,
725
+ },
726
+ speedTextActive: {
727
+ color: AppColors.sky400,
728
+ fontFamily: AppFonts.interBold,
729
+ },
730
+ loopPill: {
731
+ flexDirection: 'row',
392
732
  alignItems: 'center',
733
+ gap: 4,
734
+ paddingHorizontal: 8,
735
+ paddingVertical: 5,
736
+ borderRadius: 6,
737
+ backgroundColor: AppColors.whiteAlpha08,
738
+ borderWidth: 1,
739
+ borderColor: AppColors.whiteAlpha12,
393
740
  },
394
- metaPillLabel: {
741
+ loopPillActive: {
742
+ backgroundColor: `${AppColors.sky400}18`,
743
+ borderColor: `${AppColors.sky400}50`,
744
+ },
745
+ loopText: {
395
746
  fontFamily: AppFonts.interMedium,
396
- fontSize: 9,
397
- color: AppColors.slate400,
398
- letterSpacing: 0.4,
747
+ fontSize: 10.5,
748
+ color: AppColors.whiteAlpha60,
399
749
  },
400
- metaPillValue: {
750
+ loopTextActive: {
751
+ color: AppColors.sky400,
401
752
  fontFamily: AppFonts.interSemiBold,
402
- fontSize: 11.5,
403
- color: AppColors.white,
404
- marginTop: 1,
405
753
  },
406
754
  footer: {
407
755
  flexDirection: 'row',
@@ -410,8 +758,8 @@ const previewStyles = StyleSheet.create({
410
758
  flexWrap: 'wrap',
411
759
  gap: 8,
412
760
  paddingHorizontal: 16,
413
- paddingTop: 12,
414
- paddingBottom: Platform.OS === 'ios' ? 36 : 20,
761
+ paddingTop: 8,
762
+ paddingBottom: Platform.OS === 'ios' ? 34 : 16,
415
763
  borderTopWidth: 1,
416
764
  borderTopColor: AppColors.borderGlassLight,
417
765
  backgroundColor: AppColors.headerGlassDark,
@@ -420,7 +768,7 @@ const previewStyles = StyleSheet.create({
420
768
  flexDirection: 'row',
421
769
  alignItems: 'center',
422
770
  gap: 6,
423
- paddingHorizontal: 14,
771
+ paddingHorizontal: 16,
424
772
  paddingVertical: 9,
425
773
  borderRadius: 8,
426
774
  backgroundColor: AppColors.whiteAlpha08,