react-native-vconsole 0.3.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,12 +18,27 @@ export default function App() {
18
18
  return (
19
19
  <>
20
20
  {/* your app content */}
21
- <VConsole enable={true} exclude={['localhost:8081']} />
21
+ <VConsole
22
+ enable={true}
23
+ exclude={{
24
+ domains: ['localhost:8081'],
25
+ ip: true,
26
+ }}
27
+ />
22
28
  </>
23
29
  );
24
30
  }
25
31
  ```
26
32
 
33
+ ## VConsole Props
34
+
35
+ | Prop | Type | Default | Description |
36
+ | --- | --- | --- | --- |
37
+ | `enable` | `boolean` | `true` | Whether to enable and render vConsole. |
38
+ | `exclude` | `{ domains?: string[]; ip?: boolean }` | `{}` | Network capture exclusion rules. |
39
+ | `exclude.domains` | `string[]` | `[]` | Hosts to exclude from Network tab capture, keeping previous host-based matching behavior (e.g. `localhost:8081`). |
40
+ | `exclude.ip` | `boolean` | `false` | When `true`, requests whose hostname is an IP address (IPv4/IPv6) will be skipped in Network tab capture. |
41
+
27
42
  ## Features
28
43
 
29
44
  - Draggable floating button (`vConsole`) with screen-boundary constraints.
@@ -1,16 +1,18 @@
1
1
  "use strict";
2
2
 
3
3
  import { useEffect, useMemo, useRef, useState } from 'react';
4
- import { Animated, Clipboard, Dimensions, FlatList, NativeModules, PanResponder, Platform, Pressable, StatusBar, StyleSheet, Text, ToastAndroid, View, ScrollView } from 'react-native';
4
+ import { Animated, Clipboard, Dimensions, FlatList, Keyboard, NativeModules, PanResponder, Platform, Pressable, TextInput, StatusBar, StyleSheet, Text, ToastAndroid, View, ScrollView } from 'react-native';
5
5
  import { clearLogEntries, getLogEntries, installConsoleProxy, subscribeLogEntries, uninstallConsoleProxy } from "./core/consoleProxy.js";
6
6
  import { clearNetworkEntries, getNetworkEntries, installXhrProxy, subscribeNetworkEntries, uninstallXhrProxy } from "./core/xhrProxy.js";
7
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
8
8
  const BUTTON_WIDTH = 88;
9
9
  const BUTTON_HEIGHT = 36;
10
10
  const PANEL_HEIGHT_RATIO = 7 / 9;
11
- const EMPTY_FILTER = [];
11
+ const EMPTY_EXCLUDE = {};
12
12
  const LOG_SUB_TABS = ['All', 'log', 'info', 'warn', 'error'];
13
13
  const ROOT_TABS = ['Log', 'Network', 'System', 'App'];
14
+ const NETWORK_DURATION_WARN_THRESHOLD_MS = 1000;
15
+ const NETWORK_DURATION_SEVERE_THRESHOLD_MS = 3000;
14
16
  const LOG_THEME = {
15
17
  log: {
16
18
  backgroundColor: '#FFFFFF',
@@ -61,6 +63,17 @@ function formatMemorySize(bytes) {
61
63
  }
62
64
  return `${mb.toFixed(2)} MB`;
63
65
  }
66
+ function formatLogTime(timestamp) {
67
+ const date = new Date(timestamp);
68
+ if (Number.isNaN(date.getTime())) {
69
+ return '--:--:--.---';
70
+ }
71
+ const hh = String(date.getHours()).padStart(2, '0');
72
+ const mm = String(date.getMinutes()).padStart(2, '0');
73
+ const ss = String(date.getSeconds()).padStart(2, '0');
74
+ const ms = String(date.getMilliseconds()).padStart(3, '0');
75
+ return `${hh}:${mm}:${ss}.${ms}`;
76
+ }
64
77
  function prettyText(value) {
65
78
  if (value === undefined) {
66
79
  return '';
@@ -74,10 +87,89 @@ function prettyText(value) {
74
87
  return String(value);
75
88
  }
76
89
  }
90
+ function isNetworkErrorEntry(item) {
91
+ return item.isError === true;
92
+ }
93
+ function getNetworkItemBackgroundColor(item) {
94
+ if (isNetworkErrorEntry(item)) {
95
+ return LOG_THEME.error.backgroundColor;
96
+ }
97
+ if (typeof item.durationMs !== 'number') {
98
+ return undefined;
99
+ }
100
+ if (item.durationMs >= NETWORK_DURATION_SEVERE_THRESHOLD_MS) {
101
+ return LOG_THEME.error.backgroundColor;
102
+ }
103
+ if (item.durationMs >= NETWORK_DURATION_WARN_THRESHOLD_MS) {
104
+ return LOG_THEME.warn.backgroundColor;
105
+ }
106
+ return undefined;
107
+ }
77
108
  function buildNetworkCopyText(item) {
78
109
  const status = item.status ?? '-';
79
110
  const duration = typeof item.durationMs === 'number' ? `${item.durationMs}ms` : '-';
80
- return [`${item.method} ${item.url}`, `status ${status} duration ${duration}`, `request headers\n${prettyText(item.requestHeaders)}`, `request body\n${prettyText(item.requestBody)}`, `response headers\n${prettyText(item.responseHeaders)}`, `response data\n${prettyText(item.responseData)}`].join('\n');
111
+ const isError = isNetworkErrorEntry(item);
112
+ const segments = [`${item.method} ${item.url}`, `status ${status} duration ${duration}`, `request headers\n${prettyText(item.requestHeaders)}`, `request body\n${prettyText(item.requestBody)}`];
113
+ if (isError) {
114
+ segments.push(`error reason\n${item.errorReason ?? 'Network request failed'}`);
115
+ } else {
116
+ segments.push(`response headers\n${prettyText(item.responseHeaders)}`);
117
+ segments.push(`response data\n${prettyText(item.responseData)}`);
118
+ }
119
+ return segments.join('\n');
120
+ }
121
+ const FORBIDDEN_RETRY_HEADERS = new Set(['host', 'content-length', 'accept-encoding', 'connection', 'origin', 'referer']);
122
+ function normalizeRetryUrl(rawUrl) {
123
+ if (!rawUrl) {
124
+ return '';
125
+ }
126
+ if (/^\/\//.test(rawUrl)) {
127
+ return `https:${rawUrl}`;
128
+ }
129
+ return rawUrl;
130
+ }
131
+ function buildRetryHeaders(headers) {
132
+ const nextHeaders = {};
133
+ if (!headers) {
134
+ return nextHeaders;
135
+ }
136
+ Object.entries(headers).forEach(([key, value]) => {
137
+ if (!FORBIDDEN_RETRY_HEADERS.has(key.toLowerCase())) {
138
+ nextHeaders[key] = value;
139
+ }
140
+ });
141
+ return nextHeaders;
142
+ }
143
+ function buildRetryBody(payload, method) {
144
+ if (method === 'GET' || method === 'HEAD' || payload == null) {
145
+ return undefined;
146
+ }
147
+ if (typeof payload === 'string') {
148
+ return payload;
149
+ }
150
+ if (typeof payload === 'number' || typeof payload === 'boolean') {
151
+ return String(payload);
152
+ }
153
+ if (typeof FormData !== 'undefined' && payload instanceof FormData) {
154
+ return payload;
155
+ }
156
+ if (typeof URLSearchParams !== 'undefined' && payload instanceof URLSearchParams) {
157
+ return payload;
158
+ }
159
+ if (typeof Blob !== 'undefined' && payload instanceof Blob) {
160
+ return payload;
161
+ }
162
+ if (typeof ArrayBuffer !== 'undefined' && payload instanceof ArrayBuffer) {
163
+ return payload;
164
+ }
165
+ if (ArrayBuffer.isView(payload)) {
166
+ return payload;
167
+ }
168
+ try {
169
+ return JSON.stringify(payload);
170
+ } catch {
171
+ return String(payload);
172
+ }
81
173
  }
82
174
  function ObjectTree({
83
175
  value,
@@ -166,7 +258,7 @@ function useFlatListRefs() {
166
258
  }
167
259
  export function VConsole({
168
260
  enable = true,
169
- exclude = EMPTY_FILTER
261
+ exclude = EMPTY_EXCLUDE
170
262
  }) {
171
263
  const nativeModule = NativeModules.Vconsole;
172
264
  const {
@@ -201,6 +293,11 @@ export function VConsole({
201
293
  const [logSubTab, setLogSubTab] = useState('All');
202
294
  const [logEntries, setLogEntries] = useState([]);
203
295
  const [networkEntries, setNetworkEntries] = useState([]);
296
+ const [logFilterInput, setLogFilterInput] = useState('');
297
+ const [networkFilterInput, setNetworkFilterInput] = useState('');
298
+ const [debouncedLogFilter, setDebouncedLogFilter] = useState('');
299
+ const [debouncedNetworkFilter, setDebouncedNetworkFilter] = useState('');
300
+ const [keyboardHeight, setKeyboardHeight] = useState(0);
204
301
  const [expandedMap, setExpandedMap] = useState({});
205
302
  const [systemInfo, setSystemInfo] = useState(null);
206
303
  const [appInfo, setAppInfo] = useState(null);
@@ -208,7 +305,8 @@ export function VConsole({
208
305
  const panelTranslateY = useRef(new Animated.Value(panelHeight)).current;
209
306
  const logListRefs = useFlatListRefs();
210
307
  const networkListRef = useRef(null);
211
- const normalizedExclude = useMemo(() => exclude.map(item => item.trim().toLowerCase()).filter(Boolean), [exclude]);
308
+ const normalizedExcludeDomains = useMemo(() => (exclude.domains ?? []).map(item => item.trim().toLowerCase()).filter(Boolean), [exclude.domains]);
309
+ const shouldExcludeIp = exclude.ip === true;
212
310
  useEffect(() => {
213
311
  if (!enable) {
214
312
  setPanelVisible(false);
@@ -216,7 +314,8 @@ export function VConsole({
216
314
  }
217
315
  installConsoleProxy();
218
316
  installXhrProxy({
219
- excludeHosts: normalizedExclude
317
+ excludeHosts: normalizedExcludeDomains,
318
+ excludeIp: shouldExcludeIp
220
319
  });
221
320
  const unsubscribeLog = subscribeLogEntries(setLogEntries);
222
321
  const unsubscribeNetwork = subscribeNetworkEntries(setNetworkEntries);
@@ -228,7 +327,7 @@ export function VConsole({
228
327
  uninstallConsoleProxy();
229
328
  uninstallXhrProxy();
230
329
  };
231
- }, [enable, normalizedExclude]);
330
+ }, [enable, normalizedExcludeDomains, shouldExcludeIp]);
232
331
  useEffect(() => {
233
332
  dragPosition.stopAnimation(value => {
234
333
  const nextX = clamp(value.x, minX, maxX);
@@ -251,6 +350,32 @@ export function VConsole({
251
350
  nativeModule?.getAppInfo?.().then(result => setAppInfo(result)).catch(() => undefined);
252
351
  }
253
352
  }, [activeTab, appInfo, nativeModule, panelVisible, systemInfo]);
353
+ useEffect(() => {
354
+ const showEvent = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';
355
+ const hideEvent = Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide';
356
+ const showSubscription = Keyboard.addListener(showEvent, event => {
357
+ setKeyboardHeight(event.endCoordinates?.height ?? 0);
358
+ });
359
+ const hideSubscription = Keyboard.addListener(hideEvent, () => {
360
+ setKeyboardHeight(0);
361
+ });
362
+ return () => {
363
+ showSubscription.remove();
364
+ hideSubscription.remove();
365
+ };
366
+ }, []);
367
+ useEffect(() => {
368
+ const timer = setTimeout(() => {
369
+ setDebouncedLogFilter(logFilterInput);
370
+ }, 1000);
371
+ return () => clearTimeout(timer);
372
+ }, [logFilterInput]);
373
+ useEffect(() => {
374
+ const timer = setTimeout(() => {
375
+ setDebouncedNetworkFilter(networkFilterInput);
376
+ }, 1000);
377
+ return () => clearTimeout(timer);
378
+ }, [networkFilterInput]);
254
379
  const panResponder = useMemo(() => PanResponder.create({
255
380
  onMoveShouldSetPanResponder: () => true,
256
381
  onPanResponderGrant: () => {
@@ -300,13 +425,27 @@ export function VConsole({
300
425
  }
301
426
  });
302
427
  };
428
+ const normalizedLogFilter = debouncedLogFilter.trim().toLowerCase();
429
+ const normalizedNetworkFilter = debouncedNetworkFilter.trim().toLowerCase();
430
+ const filteredLogEntries = useMemo(() => {
431
+ if (!normalizedLogFilter) {
432
+ return logEntries;
433
+ }
434
+ return logEntries.filter(item => item.text.toLowerCase().includes(normalizedLogFilter));
435
+ }, [logEntries, normalizedLogFilter]);
436
+ const filteredNetworkEntries = useMemo(() => {
437
+ if (!normalizedNetworkFilter) {
438
+ return networkEntries;
439
+ }
440
+ return networkEntries.filter(item => item.url.toLowerCase().includes(normalizedNetworkFilter));
441
+ }, [networkEntries, normalizedNetworkFilter]);
303
442
  const logDataByTab = useMemo(() => ({
304
- All: logEntries,
305
- log: logEntries.filter(item => item.level === 'log'),
306
- info: logEntries.filter(item => item.level === 'info'),
307
- warn: logEntries.filter(item => item.level === 'warn'),
308
- error: logEntries.filter(item => item.level === 'error')
309
- }), [logEntries]);
443
+ All: filteredLogEntries,
444
+ log: filteredLogEntries.filter(item => item.level === 'log'),
445
+ info: filteredLogEntries.filter(item => item.level === 'info'),
446
+ warn: filteredLogEntries.filter(item => item.level === 'warn'),
447
+ error: filteredLogEntries.filter(item => item.level === 'error')
448
+ }), [filteredLogEntries]);
310
449
  const onToggleNode = key => {
311
450
  setExpandedMap(prev => ({
312
451
  ...prev,
@@ -335,6 +474,27 @@ export function VConsole({
335
474
  animated: true
336
475
  });
337
476
  };
477
+ const retryNetworkRequest = item => {
478
+ const method = (item.method || 'GET').toUpperCase();
479
+ const url = normalizeRetryUrl(item.url);
480
+ if (!url) {
481
+ console.error('[vConsole] Retry failed: empty request URL');
482
+ return;
483
+ }
484
+ const headers = buildRetryHeaders(item.requestHeaders);
485
+ const body = buildRetryBody(item.requestBody, method);
486
+ const hasContentType = Object.keys(headers).some(key => key.toLowerCase() === 'content-type');
487
+ if (body && typeof body === 'string' && typeof item.requestBody === 'object' && item.requestBody !== null && !hasContentType) {
488
+ headers['Content-Type'] = 'application/json';
489
+ }
490
+ fetch(url, {
491
+ method,
492
+ headers,
493
+ body: body
494
+ }).catch(error => {
495
+ console.error('[vConsole] Retry request failed', error);
496
+ });
497
+ };
338
498
  const renderRootTab = tab => /*#__PURE__*/_jsx(Pressable, {
339
499
  style: [styles.topTabButton, activeTab === tab && styles.topTabButtonActive],
340
500
  onPress: () => setActiveTab(tab),
@@ -365,7 +525,10 @@ export function VConsole({
365
525
  style: [styles.logLevelText, {
366
526
  color: levelTheme.color
367
527
  }],
368
- children: ["[", item.level.toUpperCase(), "]"]
528
+ children: ["[", item.level.toUpperCase(), "]", /*#__PURE__*/_jsxs(Text, {
529
+ style: styles.logTimeText,
530
+ children: [' ', formatLogTime(item.timestamp)]
531
+ })]
369
532
  }), /*#__PURE__*/_jsx(View, {
370
533
  style: styles.logPayload,
371
534
  children: item.args.map((arg, index) => /*#__PURE__*/_jsx(ObjectTree, {
@@ -388,8 +551,14 @@ export function VConsole({
388
551
  const renderNetworkItem = ({
389
552
  item
390
553
  }) => {
554
+ const isError = isNetworkErrorEntry(item);
555
+ const backgroundColor = getNetworkItemBackgroundColor(item);
556
+ const startedTime = formatLogTime(item.startedAt);
557
+ const finishedTime = typeof item.finishedAt === 'number' ? formatLogTime(item.finishedAt) : '-';
391
558
  return /*#__PURE__*/_jsxs(View, {
392
- style: styles.listItem,
559
+ style: [styles.listItem, backgroundColor ? {
560
+ backgroundColor
561
+ } : null],
393
562
  children: [/*#__PURE__*/_jsxs(View, {
394
563
  style: styles.listItemMain,
395
564
  children: [/*#__PURE__*/_jsxs(Text, {
@@ -397,7 +566,10 @@ export function VConsole({
397
566
  children: [item.method, " ", item.url]
398
567
  }), /*#__PURE__*/_jsxs(Text, {
399
568
  style: styles.networkLabel,
400
- children: ["Status: ", item.status ?? '-', ' ', "Duration:", ' ', typeof item.durationMs === 'number' ? `${item.durationMs}ms` : '-']
569
+ children: ["Time: ", startedTime, finishedTime !== '-' ? ` ~ ${finishedTime}` : '', ' ', "Duration:", ' ', typeof item.durationMs === 'number' ? `${item.durationMs}ms` : '-']
570
+ }), /*#__PURE__*/_jsxs(Text, {
571
+ style: styles.networkLabel,
572
+ children: ["Status: ", item.status ?? '-']
401
573
  }), /*#__PURE__*/_jsxs(View, {
402
574
  style: styles.networkBlock,
403
575
  children: [/*#__PURE__*/_jsx(Text, {
@@ -420,30 +592,41 @@ export function VConsole({
420
592
  expandedMap: expandedMap,
421
593
  onToggle: onToggleNode
422
594
  })]
423
- }), /*#__PURE__*/_jsxs(View, {
595
+ }), isError ? /*#__PURE__*/_jsxs(View, {
424
596
  style: styles.networkBlock,
425
597
  children: [/*#__PURE__*/_jsx(Text, {
426
- style: styles.networkLabel,
427
- children: "Response Headers"
428
- }), /*#__PURE__*/_jsx(ObjectTree, {
429
- value: item.responseHeaders,
430
- nodeKey: `${item.id}.responseHeaders`,
431
- expandedMap: expandedMap,
432
- onToggle: onToggleNode
598
+ style: [styles.networkLabel, styles.networkErrorLabel],
599
+ children: "Error Reason"
600
+ }), /*#__PURE__*/_jsx(Text, {
601
+ style: styles.networkErrorText,
602
+ children: item.errorReason ?? 'Network request failed'
433
603
  })]
434
- }), /*#__PURE__*/_jsxs(View, {
435
- style: styles.networkBlock,
436
- children: [/*#__PURE__*/_jsx(Text, {
437
- style: styles.networkLabel,
438
- children: "Response Data"
439
- }), /*#__PURE__*/_jsx(ScrollView, {
440
- horizontal: true,
441
- children: /*#__PURE__*/_jsx(ObjectTree, {
442
- value: item.responseData ?? '',
443
- nodeKey: `${item.id}.responseData`,
604
+ }) : /*#__PURE__*/_jsxs(_Fragment, {
605
+ children: [/*#__PURE__*/_jsxs(View, {
606
+ style: styles.networkBlock,
607
+ children: [/*#__PURE__*/_jsx(Text, {
608
+ style: styles.networkLabel,
609
+ children: "Response Headers"
610
+ }), /*#__PURE__*/_jsx(ObjectTree, {
611
+ value: item.responseHeaders,
612
+ nodeKey: `${item.id}.responseHeaders`,
444
613
  expandedMap: expandedMap,
445
614
  onToggle: onToggleNode
446
- })
615
+ })]
616
+ }), /*#__PURE__*/_jsxs(View, {
617
+ style: styles.networkBlock,
618
+ children: [/*#__PURE__*/_jsx(Text, {
619
+ style: styles.networkLabel,
620
+ children: "Response Data"
621
+ }), /*#__PURE__*/_jsx(ScrollView, {
622
+ horizontal: true,
623
+ children: /*#__PURE__*/_jsx(ObjectTree, {
624
+ value: item.responseData ?? '',
625
+ nodeKey: `${item.id}.responseData`,
626
+ expandedMap: expandedMap,
627
+ onToggle: onToggleNode
628
+ })
629
+ })]
447
630
  })]
448
631
  })]
449
632
  }), /*#__PURE__*/_jsx(Pressable, {
@@ -453,11 +636,18 @@ export function VConsole({
453
636
  style: styles.copyButtonText,
454
637
  children: "Copy"
455
638
  })
639
+ }), /*#__PURE__*/_jsx(Pressable, {
640
+ style: styles.retryButton,
641
+ onPress: () => retryNetworkRequest(item),
642
+ children: /*#__PURE__*/_jsx(Text, {
643
+ style: styles.retryButtonText,
644
+ children: "Retry"
645
+ })
456
646
  })]
457
647
  });
458
648
  };
459
- const renderLogPanel = () => /*#__PURE__*/_jsxs(View, {
460
- style: styles.contentArea,
649
+ const renderLogPanel = visible => /*#__PURE__*/_jsxs(View, {
650
+ style: [styles.contentArea, visible ? {} : styles.hidden],
461
651
  children: [/*#__PURE__*/_jsx(View, {
462
652
  style: styles.subTabRow,
463
653
  children: LOG_SUB_TABS.map(tab => /*#__PURE__*/_jsx(Pressable, {
@@ -480,6 +670,16 @@ export function VConsole({
480
670
  ItemSeparatorComponent: ListSeparator
481
671
  })
482
672
  }, tab))
673
+ }), /*#__PURE__*/_jsx(View, {
674
+ style: styles.filterInputWrap,
675
+ children: /*#__PURE__*/_jsx(TextInput, {
676
+ style: styles.filterInput,
677
+ textAlignVertical: "center",
678
+ value: logFilterInput,
679
+ onChangeText: setLogFilterInput,
680
+ placeholder: "filter...",
681
+ placeholderTextColor: "#999999"
682
+ })
483
683
  }), /*#__PURE__*/_jsxs(View, {
484
684
  style: styles.actionsRow,
485
685
  children: [renderActionButton('Clear', () => {
@@ -488,14 +688,23 @@ export function VConsole({
488
688
  }), renderActionButton('Top', scrollLogTop), renderActionButton('Bottom', scrollLogBottom), renderActionButton('Hide', closePanel)]
489
689
  })]
490
690
  });
491
- const renderNetworkPanel = () => /*#__PURE__*/_jsxs(View, {
492
- style: styles.contentArea,
691
+ const renderNetworkPanel = visible => /*#__PURE__*/_jsxs(View, {
692
+ style: [styles.contentArea, visible ? {} : styles.hidden],
493
693
  children: [/*#__PURE__*/_jsx(FlatList, {
494
694
  ref: networkListRef,
495
- data: networkEntries,
695
+ data: filteredNetworkEntries,
496
696
  keyExtractor: item => `network-${item.id}`,
497
697
  renderItem: renderNetworkItem,
498
698
  ItemSeparatorComponent: ListSeparator
699
+ }), /*#__PURE__*/_jsx(View, {
700
+ style: styles.filterInputWrap,
701
+ children: /*#__PURE__*/_jsx(TextInput, {
702
+ style: styles.filterInput,
703
+ value: networkFilterInput,
704
+ onChangeText: setNetworkFilterInput,
705
+ placeholder: "filter",
706
+ placeholderTextColor: "#999999"
707
+ })
499
708
  }), /*#__PURE__*/_jsxs(View, {
500
709
  style: styles.actionsRow,
501
710
  children: [renderActionButton('Clear', () => {
@@ -504,8 +713,8 @@ export function VConsole({
504
713
  }), renderActionButton('Top', scrollNetworkTop), renderActionButton('Bottom', scrollNetworkBottom), renderActionButton('Hide', closePanel)]
505
714
  })]
506
715
  });
507
- const renderSystemPanel = () => /*#__PURE__*/_jsxs(View, {
508
- style: styles.contentArea,
716
+ const renderSystemPanel = visible => /*#__PURE__*/_jsxs(View, {
717
+ style: [styles.contentArea, visible ? {} : styles.hidden],
509
718
  children: [/*#__PURE__*/_jsxs(View, {
510
719
  style: styles.infoCard,
511
720
  children: [/*#__PURE__*/_jsxs(Text, {
@@ -535,8 +744,8 @@ export function VConsole({
535
744
  children: renderActionButton('Hide', closePanel)
536
745
  })]
537
746
  });
538
- const renderAppPanel = () => /*#__PURE__*/_jsxs(View, {
539
- style: styles.contentArea,
747
+ const renderAppPanel = visible => /*#__PURE__*/_jsxs(View, {
748
+ style: [styles.contentArea, visible ? {} : styles.hidden],
540
749
  children: [/*#__PURE__*/_jsxs(View, {
541
750
  style: styles.infoCard,
542
751
  children: [/*#__PURE__*/_jsxs(Text, {
@@ -578,6 +787,7 @@ export function VConsole({
578
787
  }), /*#__PURE__*/_jsxs(Animated.View, {
579
788
  style: [styles.panel, {
580
789
  height: panelHeight,
790
+ marginBottom: keyboardHeight,
581
791
  transform: [{
582
792
  translateY: panelTranslateY
583
793
  }]
@@ -585,7 +795,7 @@ export function VConsole({
585
795
  children: [/*#__PURE__*/_jsx(View, {
586
796
  style: styles.topTabRow,
587
797
  children: ROOT_TABS.map(renderRootTab)
588
- }), activeTab === 'Log' ? renderLogPanel() : null, activeTab === 'Network' ? renderNetworkPanel() : null, activeTab === 'System' ? renderSystemPanel() : null, activeTab === 'App' ? renderAppPanel() : null]
798
+ }), renderLogPanel(activeTab === 'Log'), renderNetworkPanel(activeTab === 'Network'), renderSystemPanel(activeTab === 'System'), renderAppPanel(activeTab === 'App')]
589
799
  })]
590
800
  }) : null]
591
801
  });
@@ -701,6 +911,11 @@ const styles = StyleSheet.create({
701
911
  fontWeight: '700',
702
912
  marginBottom: 4
703
913
  },
914
+ logTimeText: {
915
+ fontSize: 11,
916
+ fontWeight: '400',
917
+ color: '#888888'
918
+ },
704
919
  logPayload: {
705
920
  flex: 1
706
921
  },
@@ -718,6 +933,20 @@ const styles = StyleSheet.create({
718
933
  fontSize: 11,
719
934
  color: '#333333'
720
935
  },
936
+ retryButton: {
937
+ position: 'absolute',
938
+ right: 8,
939
+ top: 40,
940
+ borderWidth: StyleSheet.hairlineWidth,
941
+ borderColor: '#D0D0D0',
942
+ borderRadius: 6,
943
+ paddingVertical: 4,
944
+ paddingHorizontal: 8
945
+ },
946
+ retryButtonText: {
947
+ fontSize: 11,
948
+ color: '#333333'
949
+ },
721
950
  valuePrimitive: {
722
951
  color: '#222222',
723
952
  fontSize: 12,
@@ -780,6 +1009,32 @@ const styles = StyleSheet.create({
780
1009
  color: '#444444',
781
1010
  marginBottom: 2
782
1011
  },
1012
+ networkErrorLabel: {
1013
+ color: LOG_THEME.error.color,
1014
+ fontWeight: '600'
1015
+ },
1016
+ networkErrorText: {
1017
+ color: LOG_THEME.error.color,
1018
+ fontSize: 12
1019
+ },
1020
+ filterInputWrap: {
1021
+ borderTopWidth: StyleSheet.hairlineWidth,
1022
+ borderTopColor: '#E1E1E1',
1023
+ paddingHorizontal: 8,
1024
+ paddingTop: 8,
1025
+ paddingBottom: 6
1026
+ },
1027
+ filterInput: {
1028
+ height: 34,
1029
+ borderWidth: StyleSheet.hairlineWidth,
1030
+ borderColor: '#D0D0D0',
1031
+ borderRadius: 8,
1032
+ paddingHorizontal: 10,
1033
+ fontSize: 12,
1034
+ color: '#222222',
1035
+ backgroundColor: '#FFFFFF',
1036
+ paddingVertical: 0
1037
+ },
783
1038
  actionsRow: {
784
1039
  borderTopWidth: StyleSheet.hairlineWidth,
785
1040
  borderTopColor: '#E1E1E1',