react-native-vconsole 0.3.0 → 0.4.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.
@@ -1,14 +1,14 @@
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, 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
14
  const LOG_THEME = {
@@ -45,6 +45,33 @@ function getDisplayValue(value) {
45
45
  function copyToClipboard(value) {
46
46
  Clipboard.setString(value);
47
47
  }
48
+ function copyToClipboardWithFeedback(value) {
49
+ copyToClipboard(value);
50
+ if (Platform.OS === 'android') {
51
+ ToastAndroid.show('Copied', ToastAndroid.SHORT);
52
+ }
53
+ }
54
+ function formatMemorySize(bytes) {
55
+ if (typeof bytes !== 'number' || !Number.isFinite(bytes) || bytes < 0) {
56
+ return '-';
57
+ }
58
+ const mb = bytes / (1024 * 1024);
59
+ if (mb >= 1024) {
60
+ return `${(mb / 1024).toFixed(2)} GB`;
61
+ }
62
+ return `${mb.toFixed(2)} MB`;
63
+ }
64
+ function formatLogTime(timestamp) {
65
+ const date = new Date(timestamp);
66
+ if (Number.isNaN(date.getTime())) {
67
+ return '--:--:--.---';
68
+ }
69
+ const hh = String(date.getHours()).padStart(2, '0');
70
+ const mm = String(date.getMinutes()).padStart(2, '0');
71
+ const ss = String(date.getSeconds()).padStart(2, '0');
72
+ const ms = String(date.getMilliseconds()).padStart(3, '0');
73
+ return `${hh}:${mm}:${ss}.${ms}`;
74
+ }
48
75
  function prettyText(value) {
49
76
  if (value === undefined) {
50
77
  return '';
@@ -58,10 +85,74 @@ function prettyText(value) {
58
85
  return String(value);
59
86
  }
60
87
  }
88
+ function isNetworkErrorEntry(item) {
89
+ return item.isError === true;
90
+ }
61
91
  function buildNetworkCopyText(item) {
62
92
  const status = item.status ?? '-';
63
93
  const duration = typeof item.durationMs === 'number' ? `${item.durationMs}ms` : '-';
64
- 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');
94
+ const isError = isNetworkErrorEntry(item);
95
+ const segments = [`${item.method} ${item.url}`, `status ${status} duration ${duration}`, `request headers\n${prettyText(item.requestHeaders)}`, `request body\n${prettyText(item.requestBody)}`];
96
+ if (isError) {
97
+ segments.push(`error reason\n${item.errorReason ?? 'Network request failed'}`);
98
+ } else {
99
+ segments.push(`response headers\n${prettyText(item.responseHeaders)}`);
100
+ segments.push(`response data\n${prettyText(item.responseData)}`);
101
+ }
102
+ return segments.join('\n');
103
+ }
104
+ const FORBIDDEN_RETRY_HEADERS = new Set(['host', 'content-length', 'accept-encoding', 'connection', 'origin', 'referer']);
105
+ function normalizeRetryUrl(rawUrl) {
106
+ if (!rawUrl) {
107
+ return '';
108
+ }
109
+ if (/^\/\//.test(rawUrl)) {
110
+ return `https:${rawUrl}`;
111
+ }
112
+ return rawUrl;
113
+ }
114
+ function buildRetryHeaders(headers) {
115
+ const nextHeaders = {};
116
+ if (!headers) {
117
+ return nextHeaders;
118
+ }
119
+ Object.entries(headers).forEach(([key, value]) => {
120
+ if (!FORBIDDEN_RETRY_HEADERS.has(key.toLowerCase())) {
121
+ nextHeaders[key] = value;
122
+ }
123
+ });
124
+ return nextHeaders;
125
+ }
126
+ function buildRetryBody(payload, method) {
127
+ if (method === 'GET' || method === 'HEAD' || payload == null) {
128
+ return undefined;
129
+ }
130
+ if (typeof payload === 'string') {
131
+ return payload;
132
+ }
133
+ if (typeof payload === 'number' || typeof payload === 'boolean') {
134
+ return String(payload);
135
+ }
136
+ if (typeof FormData !== 'undefined' && payload instanceof FormData) {
137
+ return payload;
138
+ }
139
+ if (typeof URLSearchParams !== 'undefined' && payload instanceof URLSearchParams) {
140
+ return payload;
141
+ }
142
+ if (typeof Blob !== 'undefined' && payload instanceof Blob) {
143
+ return payload;
144
+ }
145
+ if (typeof ArrayBuffer !== 'undefined' && payload instanceof ArrayBuffer) {
146
+ return payload;
147
+ }
148
+ if (ArrayBuffer.isView(payload)) {
149
+ return payload;
150
+ }
151
+ try {
152
+ return JSON.stringify(payload);
153
+ } catch {
154
+ return String(payload);
155
+ }
65
156
  }
66
157
  function ObjectTree({
67
158
  value,
@@ -77,10 +168,24 @@ function ObjectTree({
77
168
  }
78
169
  const valueType = typeof value;
79
170
  if (valueType !== 'object') {
171
+ const displayValue = getDisplayValue(value);
172
+ if (Platform.OS === 'android') {
173
+ return /*#__PURE__*/_jsx(Pressable, {
174
+ onLongPress: () => copyToClipboardWithFeedback(displayValue),
175
+ delayLongPress: 250,
176
+ android_ripple: {
177
+ color: '#D0D0D0'
178
+ },
179
+ children: /*#__PURE__*/_jsx(Text, {
180
+ style: styles.valuePrimitive,
181
+ children: displayValue
182
+ })
183
+ });
184
+ }
80
185
  return /*#__PURE__*/_jsx(Text, {
81
186
  style: styles.valuePrimitive,
82
187
  selectable: true,
83
- children: getDisplayValue(value)
188
+ children: displayValue
84
189
  });
85
190
  }
86
191
  const isArray = Array.isArray(value);
@@ -136,7 +241,7 @@ function useFlatListRefs() {
136
241
  }
137
242
  export function VConsole({
138
243
  enable = true,
139
- filter = EMPTY_FILTER
244
+ exclude = EMPTY_EXCLUDE
140
245
  }) {
141
246
  const nativeModule = NativeModules.Vconsole;
142
247
  const {
@@ -171,6 +276,11 @@ export function VConsole({
171
276
  const [logSubTab, setLogSubTab] = useState('All');
172
277
  const [logEntries, setLogEntries] = useState([]);
173
278
  const [networkEntries, setNetworkEntries] = useState([]);
279
+ const [logFilterInput, setLogFilterInput] = useState('');
280
+ const [networkFilterInput, setNetworkFilterInput] = useState('');
281
+ const [debouncedLogFilter, setDebouncedLogFilter] = useState('');
282
+ const [debouncedNetworkFilter, setDebouncedNetworkFilter] = useState('');
283
+ const [keyboardHeight, setKeyboardHeight] = useState(0);
174
284
  const [expandedMap, setExpandedMap] = useState({});
175
285
  const [systemInfo, setSystemInfo] = useState(null);
176
286
  const [appInfo, setAppInfo] = useState(null);
@@ -178,7 +288,8 @@ export function VConsole({
178
288
  const panelTranslateY = useRef(new Animated.Value(panelHeight)).current;
179
289
  const logListRefs = useFlatListRefs();
180
290
  const networkListRef = useRef(null);
181
- const normalizedFilter = useMemo(() => filter.map(item => item.trim().toLowerCase()).filter(Boolean), [filter]);
291
+ const normalizedExcludeDomains = useMemo(() => (exclude.domains ?? []).map(item => item.trim().toLowerCase()).filter(Boolean), [exclude.domains]);
292
+ const shouldExcludeIp = exclude.ip === true;
182
293
  useEffect(() => {
183
294
  if (!enable) {
184
295
  setPanelVisible(false);
@@ -186,7 +297,8 @@ export function VConsole({
186
297
  }
187
298
  installConsoleProxy();
188
299
  installXhrProxy({
189
- filterHosts: normalizedFilter
300
+ excludeHosts: normalizedExcludeDomains,
301
+ excludeIp: shouldExcludeIp
190
302
  });
191
303
  const unsubscribeLog = subscribeLogEntries(setLogEntries);
192
304
  const unsubscribeNetwork = subscribeNetworkEntries(setNetworkEntries);
@@ -198,7 +310,7 @@ export function VConsole({
198
310
  uninstallConsoleProxy();
199
311
  uninstallXhrProxy();
200
312
  };
201
- }, [enable, normalizedFilter]);
313
+ }, [enable, normalizedExcludeDomains, shouldExcludeIp]);
202
314
  useEffect(() => {
203
315
  dragPosition.stopAnimation(value => {
204
316
  const nextX = clamp(value.x, minX, maxX);
@@ -221,6 +333,32 @@ export function VConsole({
221
333
  nativeModule?.getAppInfo?.().then(result => setAppInfo(result)).catch(() => undefined);
222
334
  }
223
335
  }, [activeTab, appInfo, nativeModule, panelVisible, systemInfo]);
336
+ useEffect(() => {
337
+ const showEvent = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';
338
+ const hideEvent = Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide';
339
+ const showSubscription = Keyboard.addListener(showEvent, event => {
340
+ setKeyboardHeight(event.endCoordinates?.height ?? 0);
341
+ });
342
+ const hideSubscription = Keyboard.addListener(hideEvent, () => {
343
+ setKeyboardHeight(0);
344
+ });
345
+ return () => {
346
+ showSubscription.remove();
347
+ hideSubscription.remove();
348
+ };
349
+ }, []);
350
+ useEffect(() => {
351
+ const timer = setTimeout(() => {
352
+ setDebouncedLogFilter(logFilterInput);
353
+ }, 1000);
354
+ return () => clearTimeout(timer);
355
+ }, [logFilterInput]);
356
+ useEffect(() => {
357
+ const timer = setTimeout(() => {
358
+ setDebouncedNetworkFilter(networkFilterInput);
359
+ }, 1000);
360
+ return () => clearTimeout(timer);
361
+ }, [networkFilterInput]);
224
362
  const panResponder = useMemo(() => PanResponder.create({
225
363
  onMoveShouldSetPanResponder: () => true,
226
364
  onPanResponderGrant: () => {
@@ -270,13 +408,27 @@ export function VConsole({
270
408
  }
271
409
  });
272
410
  };
411
+ const normalizedLogFilter = debouncedLogFilter.trim().toLowerCase();
412
+ const normalizedNetworkFilter = debouncedNetworkFilter.trim().toLowerCase();
413
+ const filteredLogEntries = useMemo(() => {
414
+ if (!normalizedLogFilter) {
415
+ return logEntries;
416
+ }
417
+ return logEntries.filter(item => item.text.toLowerCase().includes(normalizedLogFilter));
418
+ }, [logEntries, normalizedLogFilter]);
419
+ const filteredNetworkEntries = useMemo(() => {
420
+ if (!normalizedNetworkFilter) {
421
+ return networkEntries;
422
+ }
423
+ return networkEntries.filter(item => item.url.toLowerCase().includes(normalizedNetworkFilter));
424
+ }, [networkEntries, normalizedNetworkFilter]);
273
425
  const logDataByTab = useMemo(() => ({
274
- All: logEntries,
275
- log: logEntries.filter(item => item.level === 'log'),
276
- info: logEntries.filter(item => item.level === 'info'),
277
- warn: logEntries.filter(item => item.level === 'warn'),
278
- error: logEntries.filter(item => item.level === 'error')
279
- }), [logEntries]);
426
+ All: filteredLogEntries,
427
+ log: filteredLogEntries.filter(item => item.level === 'log'),
428
+ info: filteredLogEntries.filter(item => item.level === 'info'),
429
+ warn: filteredLogEntries.filter(item => item.level === 'warn'),
430
+ error: filteredLogEntries.filter(item => item.level === 'error')
431
+ }), [filteredLogEntries]);
280
432
  const onToggleNode = key => {
281
433
  setExpandedMap(prev => ({
282
434
  ...prev,
@@ -305,6 +457,27 @@ export function VConsole({
305
457
  animated: true
306
458
  });
307
459
  };
460
+ const retryNetworkRequest = item => {
461
+ const method = (item.method || 'GET').toUpperCase();
462
+ const url = normalizeRetryUrl(item.url);
463
+ if (!url) {
464
+ console.error('[vConsole] Retry failed: empty request URL');
465
+ return;
466
+ }
467
+ const headers = buildRetryHeaders(item.requestHeaders);
468
+ const body = buildRetryBody(item.requestBody, method);
469
+ const hasContentType = Object.keys(headers).some(key => key.toLowerCase() === 'content-type');
470
+ if (body && typeof body === 'string' && typeof item.requestBody === 'object' && item.requestBody !== null && !hasContentType) {
471
+ headers['Content-Type'] = 'application/json';
472
+ }
473
+ fetch(url, {
474
+ method,
475
+ headers,
476
+ body: body
477
+ }).catch(error => {
478
+ console.error('[vConsole] Retry request failed', error);
479
+ });
480
+ };
308
481
  const renderRootTab = tab => /*#__PURE__*/_jsx(Pressable, {
309
482
  style: [styles.topTabButton, activeTab === tab && styles.topTabButtonActive],
310
483
  onPress: () => setActiveTab(tab),
@@ -335,7 +508,10 @@ export function VConsole({
335
508
  style: [styles.logLevelText, {
336
509
  color: levelTheme.color
337
510
  }],
338
- children: ["[", item.level.toUpperCase(), "]"]
511
+ children: ["[", item.level.toUpperCase(), "]", /*#__PURE__*/_jsxs(Text, {
512
+ style: styles.logTimeText,
513
+ children: [' ', formatLogTime(item.timestamp)]
514
+ })]
339
515
  }), /*#__PURE__*/_jsx(View, {
340
516
  style: styles.logPayload,
341
517
  children: item.args.map((arg, index) => /*#__PURE__*/_jsx(ObjectTree, {
@@ -347,10 +523,10 @@ export function VConsole({
347
523
  })]
348
524
  }), /*#__PURE__*/_jsx(Pressable, {
349
525
  style: styles.copyButton,
350
- onPress: () => copyToClipboard(item.text),
526
+ onPress: () => copyToClipboardWithFeedback(item.text),
351
527
  children: /*#__PURE__*/_jsx(Text, {
352
528
  style: styles.copyButtonText,
353
- children: "\u590D\u5236"
529
+ children: "Copy"
354
530
  })
355
531
  })]
356
532
  });
@@ -358,8 +534,13 @@ export function VConsole({
358
534
  const renderNetworkItem = ({
359
535
  item
360
536
  }) => {
537
+ const isError = isNetworkErrorEntry(item);
538
+ const startedTime = formatLogTime(item.startedAt);
539
+ const finishedTime = typeof item.finishedAt === 'number' ? formatLogTime(item.finishedAt) : '-';
361
540
  return /*#__PURE__*/_jsxs(View, {
362
- style: styles.listItem,
541
+ style: [styles.listItem, isError ? {
542
+ backgroundColor: LOG_THEME.error.backgroundColor
543
+ } : null],
363
544
  children: [/*#__PURE__*/_jsxs(View, {
364
545
  style: styles.listItemMain,
365
546
  children: [/*#__PURE__*/_jsxs(Text, {
@@ -367,7 +548,10 @@ export function VConsole({
367
548
  children: [item.method, " ", item.url]
368
549
  }), /*#__PURE__*/_jsxs(Text, {
369
550
  style: styles.networkLabel,
370
- children: ["Status: ", item.status ?? '-', ' ', "Duration:", ' ', typeof item.durationMs === 'number' ? `${item.durationMs}ms` : '-']
551
+ children: ["Time: ", startedTime, finishedTime !== '-' ? ` ~ ${finishedTime}` : '', ' ', "Duration:", ' ', typeof item.durationMs === 'number' ? `${item.durationMs}ms` : '-']
552
+ }), /*#__PURE__*/_jsxs(Text, {
553
+ style: styles.networkLabel,
554
+ children: ["Status: ", item.status ?? '-']
371
555
  }), /*#__PURE__*/_jsxs(View, {
372
556
  style: styles.networkBlock,
373
557
  children: [/*#__PURE__*/_jsx(Text, {
@@ -390,44 +574,62 @@ export function VConsole({
390
574
  expandedMap: expandedMap,
391
575
  onToggle: onToggleNode
392
576
  })]
393
- }), /*#__PURE__*/_jsxs(View, {
577
+ }), isError ? /*#__PURE__*/_jsxs(View, {
394
578
  style: styles.networkBlock,
395
579
  children: [/*#__PURE__*/_jsx(Text, {
396
- style: styles.networkLabel,
397
- children: "Response Headers"
398
- }), /*#__PURE__*/_jsx(ObjectTree, {
399
- value: item.responseHeaders,
400
- nodeKey: `${item.id}.responseHeaders`,
401
- expandedMap: expandedMap,
402
- onToggle: onToggleNode
580
+ style: [styles.networkLabel, styles.networkErrorLabel],
581
+ children: "Error Reason"
582
+ }), /*#__PURE__*/_jsx(Text, {
583
+ style: styles.networkErrorText,
584
+ children: item.errorReason ?? 'Network request failed'
403
585
  })]
404
- }), /*#__PURE__*/_jsxs(View, {
405
- style: styles.networkBlock,
406
- children: [/*#__PURE__*/_jsx(Text, {
407
- style: styles.networkLabel,
408
- children: "Response Data"
409
- }), /*#__PURE__*/_jsx(ScrollView, {
410
- horizontal: true,
411
- children: /*#__PURE__*/_jsx(ObjectTree, {
412
- value: item.responseData ?? '',
413
- nodeKey: `${item.id}.responseData`,
586
+ }) : /*#__PURE__*/_jsxs(_Fragment, {
587
+ children: [/*#__PURE__*/_jsxs(View, {
588
+ style: styles.networkBlock,
589
+ children: [/*#__PURE__*/_jsx(Text, {
590
+ style: styles.networkLabel,
591
+ children: "Response Headers"
592
+ }), /*#__PURE__*/_jsx(ObjectTree, {
593
+ value: item.responseHeaders,
594
+ nodeKey: `${item.id}.responseHeaders`,
414
595
  expandedMap: expandedMap,
415
596
  onToggle: onToggleNode
416
- })
597
+ })]
598
+ }), /*#__PURE__*/_jsxs(View, {
599
+ style: styles.networkBlock,
600
+ children: [/*#__PURE__*/_jsx(Text, {
601
+ style: styles.networkLabel,
602
+ children: "Response Data"
603
+ }), /*#__PURE__*/_jsx(ScrollView, {
604
+ horizontal: true,
605
+ children: /*#__PURE__*/_jsx(ObjectTree, {
606
+ value: item.responseData ?? '',
607
+ nodeKey: `${item.id}.responseData`,
608
+ expandedMap: expandedMap,
609
+ onToggle: onToggleNode
610
+ })
611
+ })]
417
612
  })]
418
613
  })]
419
614
  }), /*#__PURE__*/_jsx(Pressable, {
420
615
  style: styles.copyButton,
421
- onPress: () => copyToClipboard(buildNetworkCopyText(item)),
616
+ onPress: () => copyToClipboardWithFeedback(buildNetworkCopyText(item)),
422
617
  children: /*#__PURE__*/_jsx(Text, {
423
618
  style: styles.copyButtonText,
424
- children: "\u590D\u5236"
619
+ children: "Copy"
620
+ })
621
+ }), /*#__PURE__*/_jsx(Pressable, {
622
+ style: styles.retryButton,
623
+ onPress: () => retryNetworkRequest(item),
624
+ children: /*#__PURE__*/_jsx(Text, {
625
+ style: styles.retryButtonText,
626
+ children: "Retry"
425
627
  })
426
628
  })]
427
629
  });
428
630
  };
429
- const renderLogPanel = () => /*#__PURE__*/_jsxs(View, {
430
- style: styles.contentArea,
631
+ const renderLogPanel = visible => /*#__PURE__*/_jsxs(View, {
632
+ style: [styles.contentArea, visible ? {} : styles.hidden],
431
633
  children: [/*#__PURE__*/_jsx(View, {
432
634
  style: styles.subTabRow,
433
635
  children: LOG_SUB_TABS.map(tab => /*#__PURE__*/_jsx(Pressable, {
@@ -450,6 +652,16 @@ export function VConsole({
450
652
  ItemSeparatorComponent: ListSeparator
451
653
  })
452
654
  }, tab))
655
+ }), /*#__PURE__*/_jsx(View, {
656
+ style: styles.filterInputWrap,
657
+ children: /*#__PURE__*/_jsx(TextInput, {
658
+ style: styles.filterInput,
659
+ textAlignVertical: "center",
660
+ value: logFilterInput,
661
+ onChangeText: setLogFilterInput,
662
+ placeholder: "filter...",
663
+ placeholderTextColor: "#999999"
664
+ })
453
665
  }), /*#__PURE__*/_jsxs(View, {
454
666
  style: styles.actionsRow,
455
667
  children: [renderActionButton('Clear', () => {
@@ -458,14 +670,23 @@ export function VConsole({
458
670
  }), renderActionButton('Top', scrollLogTop), renderActionButton('Bottom', scrollLogBottom), renderActionButton('Hide', closePanel)]
459
671
  })]
460
672
  });
461
- const renderNetworkPanel = () => /*#__PURE__*/_jsxs(View, {
462
- style: styles.contentArea,
673
+ const renderNetworkPanel = visible => /*#__PURE__*/_jsxs(View, {
674
+ style: [styles.contentArea, visible ? {} : styles.hidden],
463
675
  children: [/*#__PURE__*/_jsx(FlatList, {
464
676
  ref: networkListRef,
465
- data: networkEntries,
677
+ data: filteredNetworkEntries,
466
678
  keyExtractor: item => `network-${item.id}`,
467
679
  renderItem: renderNetworkItem,
468
680
  ItemSeparatorComponent: ListSeparator
681
+ }), /*#__PURE__*/_jsx(View, {
682
+ style: styles.filterInputWrap,
683
+ children: /*#__PURE__*/_jsx(TextInput, {
684
+ style: styles.filterInput,
685
+ value: networkFilterInput,
686
+ onChangeText: setNetworkFilterInput,
687
+ placeholder: "filter",
688
+ placeholderTextColor: "#999999"
689
+ })
469
690
  }), /*#__PURE__*/_jsxs(View, {
470
691
  style: styles.actionsRow,
471
692
  children: [renderActionButton('Clear', () => {
@@ -474,39 +695,39 @@ export function VConsole({
474
695
  }), renderActionButton('Top', scrollNetworkTop), renderActionButton('Bottom', scrollNetworkBottom), renderActionButton('Hide', closePanel)]
475
696
  })]
476
697
  });
477
- const renderSystemPanel = () => /*#__PURE__*/_jsxs(View, {
478
- style: styles.contentArea,
698
+ const renderSystemPanel = visible => /*#__PURE__*/_jsxs(View, {
699
+ style: [styles.contentArea, visible ? {} : styles.hidden],
479
700
  children: [/*#__PURE__*/_jsxs(View, {
480
701
  style: styles.infoCard,
481
702
  children: [/*#__PURE__*/_jsxs(Text, {
482
703
  style: styles.infoText,
483
- children: ["\u5382\u5546/\u54C1\u724C: ", systemInfo?.manufacturer ?? '-']
704
+ children: ["Brand: ", systemInfo?.manufacturer ?? '-']
484
705
  }), /*#__PURE__*/_jsxs(Text, {
485
706
  style: styles.infoText,
486
- children: ["\u673A\u578B: ", systemInfo?.model ?? '-']
707
+ children: ["Model: ", systemInfo?.model ?? '-']
487
708
  }), /*#__PURE__*/_jsxs(Text, {
488
709
  style: styles.infoText,
489
- children: ["\u7CFB\u7EDF\u7248\u672C: ", systemInfo?.osVersion ?? '-']
710
+ children: ["System Version: ", Platform.OS === 'android' ? 'Android' : 'iOS', ' ', systemInfo?.osVersion ?? '-']
490
711
  }), Platform.OS === 'android' ? /*#__PURE__*/_jsxs(Text, {
491
712
  style: styles.infoText,
492
- children: ["\u7F51\u7EDC\u7C7B\u578B: ", systemInfo?.networkType ?? '-']
713
+ children: ["Network Type: ", systemInfo?.networkType ?? '-']
493
714
  }) : null, Platform.OS === 'android' ? /*#__PURE__*/_jsxs(Text, {
494
715
  style: styles.infoText,
495
- children: ["\u7F51\u7EDC\u53EF\u8FBE: ", systemInfo?.isNetworkReachable ? 'true' : 'false']
716
+ children: ["Network Reachable: ", systemInfo?.isNetworkReachable ?? 'unknown']
496
717
  }) : null, /*#__PURE__*/_jsxs(Text, {
497
718
  style: styles.infoText,
498
- children: ["\u603B\u5185\u5B58: ", systemInfo?.totalMemory ?? 0]
719
+ children: ["Total Memory: ", formatMemorySize(systemInfo?.totalMemory)]
499
720
  }), Platform.OS === 'android' ? /*#__PURE__*/_jsxs(Text, {
500
721
  style: styles.infoText,
501
- children: ["\u53EF\u7528\u5185\u5B58: ", systemInfo?.availableMemory ?? 0]
722
+ children: ["Available Memory: ", formatMemorySize(systemInfo?.availableMemory)]
502
723
  }) : null]
503
724
  }), /*#__PURE__*/_jsx(View, {
504
725
  style: styles.actionsRow,
505
726
  children: renderActionButton('Hide', closePanel)
506
727
  })]
507
728
  });
508
- const renderAppPanel = () => /*#__PURE__*/_jsxs(View, {
509
- style: styles.contentArea,
729
+ const renderAppPanel = visible => /*#__PURE__*/_jsxs(View, {
730
+ style: [styles.contentArea, visible ? {} : styles.hidden],
510
731
  children: [/*#__PURE__*/_jsxs(View, {
511
732
  style: styles.infoCard,
512
733
  children: [/*#__PURE__*/_jsxs(Text, {
@@ -548,6 +769,7 @@ export function VConsole({
548
769
  }), /*#__PURE__*/_jsxs(Animated.View, {
549
770
  style: [styles.panel, {
550
771
  height: panelHeight,
772
+ marginBottom: keyboardHeight,
551
773
  transform: [{
552
774
  translateY: panelTranslateY
553
775
  }]
@@ -555,7 +777,7 @@ export function VConsole({
555
777
  children: [/*#__PURE__*/_jsx(View, {
556
778
  style: styles.topTabRow,
557
779
  children: ROOT_TABS.map(renderRootTab)
558
- }), activeTab === 'Log' ? renderLogPanel() : null, activeTab === 'Network' ? renderNetworkPanel() : null, activeTab === 'System' ? renderSystemPanel() : null, activeTab === 'App' ? renderAppPanel() : null]
780
+ }), renderLogPanel(activeTab === 'Log'), renderNetworkPanel(activeTab === 'Network'), renderSystemPanel(activeTab === 'System'), renderAppPanel(activeTab === 'App')]
559
781
  })]
560
782
  }) : null]
561
783
  });
@@ -568,7 +790,7 @@ const styles = StyleSheet.create({
568
790
  floatingButton: {
569
791
  width: BUTTON_WIDTH,
570
792
  height: BUTTON_HEIGHT,
571
- borderRadius: 12,
793
+ borderRadius: 8,
572
794
  backgroundColor: '#22A455',
573
795
  justifyContent: 'center',
574
796
  alignItems: 'center'
@@ -618,7 +840,7 @@ const styles = StyleSheet.create({
618
840
  },
619
841
  contentArea: {
620
842
  flex: 1,
621
- paddingBottom: 16
843
+ paddingBottom: Platform.OS === 'android' ? 42 : 16
622
844
  },
623
845
  subTabRow: {
624
846
  flexDirection: 'row',
@@ -671,6 +893,11 @@ const styles = StyleSheet.create({
671
893
  fontWeight: '700',
672
894
  marginBottom: 4
673
895
  },
896
+ logTimeText: {
897
+ fontSize: 11,
898
+ fontWeight: '400',
899
+ color: '#888888'
900
+ },
674
901
  logPayload: {
675
902
  flex: 1
676
903
  },
@@ -688,11 +915,31 @@ const styles = StyleSheet.create({
688
915
  fontSize: 11,
689
916
  color: '#333333'
690
917
  },
918
+ retryButton: {
919
+ position: 'absolute',
920
+ right: 8,
921
+ top: 40,
922
+ borderWidth: StyleSheet.hairlineWidth,
923
+ borderColor: '#D0D0D0',
924
+ borderRadius: 6,
925
+ paddingVertical: 4,
926
+ paddingHorizontal: 8
927
+ },
928
+ retryButtonText: {
929
+ fontSize: 11,
930
+ color: '#333333'
931
+ },
691
932
  valuePrimitive: {
692
933
  color: '#222222',
693
934
  fontSize: 12,
694
935
  flexShrink: 1
695
936
  },
937
+ valuePrimitiveInput: {
938
+ paddingVertical: 0,
939
+ paddingHorizontal: 0,
940
+ margin: 0,
941
+ textAlignVertical: 'top'
942
+ },
696
943
  treeNode: {
697
944
  flexDirection: 'column',
698
945
  marginBottom: 4
@@ -744,6 +991,32 @@ const styles = StyleSheet.create({
744
991
  color: '#444444',
745
992
  marginBottom: 2
746
993
  },
994
+ networkErrorLabel: {
995
+ color: LOG_THEME.error.color,
996
+ fontWeight: '600'
997
+ },
998
+ networkErrorText: {
999
+ color: LOG_THEME.error.color,
1000
+ fontSize: 12
1001
+ },
1002
+ filterInputWrap: {
1003
+ borderTopWidth: StyleSheet.hairlineWidth,
1004
+ borderTopColor: '#E1E1E1',
1005
+ paddingHorizontal: 8,
1006
+ paddingTop: 8,
1007
+ paddingBottom: 6
1008
+ },
1009
+ filterInput: {
1010
+ height: 34,
1011
+ borderWidth: StyleSheet.hairlineWidth,
1012
+ borderColor: '#D0D0D0',
1013
+ borderRadius: 8,
1014
+ paddingHorizontal: 10,
1015
+ fontSize: 12,
1016
+ color: '#222222',
1017
+ backgroundColor: '#FFFFFF',
1018
+ paddingVertical: 0
1019
+ },
747
1020
  actionsRow: {
748
1021
  borderTopWidth: StyleSheet.hairlineWidth,
749
1022
  borderTopColor: '#E1E1E1',