@peers-app/peers-ui 0.8.3 → 0.8.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.
@@ -48,6 +48,8 @@ const color_mode_dropdown_1 = require("../settings/color-mode-dropdown");
48
48
  const log_display_1 = require("./log-display");
49
49
  const log_filters_1 = require("./log-filters");
50
50
  const resizable_table_header_1 = require("./resizable-table-header");
51
+ const globals_1 = require("../../globals");
52
+ const mobile_log_card_1 = require("./mobile-log-card");
51
53
  const windowHeight = () => window.innerHeight;
52
54
  const DEFAULT_COLUMNS = [
53
55
  { key: 'timestamp', label: 'Timestamp', width: 150 },
@@ -68,6 +70,8 @@ const ConsoleLogsList = () => {
68
70
  const [_colorMode] = (0, hooks_1.useObservable)(color_mode_dropdown_1.colorMode);
69
71
  const logsEndRef = react_1.default.useRef(null);
70
72
  const containerRef = react_1.default.useRef(null);
73
+ (0, hooks_1.useObservable)(globals_1.isDesktop);
74
+ const isMobile = !(0, globals_1.isDesktop)();
71
75
  const batchSize = 50;
72
76
  // Track fixed column widths to trigger message column recalculation
73
77
  const fixedColumnsWidthKey = (0, react_1.useMemo)(() => {
@@ -225,7 +229,7 @@ const ConsoleLogsList = () => {
225
229
  marginBottom: '50px',
226
230
  overflow: 'hidden',
227
231
  } },
228
- react_1.default.createElement(resizable_table_header_1.ResizableTableHeader, { columns: columns, onColumnsChange: setColumns, colorMode: _colorMode }),
232
+ !isMobile && (react_1.default.createElement(resizable_table_header_1.ResizableTableHeader, { columns: columns, onColumnsChange: setColumns, colorMode: _colorMode })),
229
233
  react_1.default.createElement("div", { id: "scrollableLogsDiv", style: {
230
234
  flex: 1,
231
235
  overflow: 'auto',
@@ -239,10 +243,12 @@ const ConsoleLogsList = () => {
239
243
  react_1.default.createElement("div", { ref: logsEndRef }),
240
244
  _logs.length === 0 && allLogsLoaded ? (react_1.default.createElement("div", { className: "text-center p-5 text-muted" },
241
245
  react_1.default.createElement("i", { className: "bi bi-inbox display-1" }),
242
- react_1.default.createElement("p", { className: "mt-2" }, "No logs found"))) : (react_1.default.createElement("table", { className: "table table-sm table-hover mb-0", style: { fontSize: '0.85rem', tableLayout: 'fixed', width: '100%' } },
243
- react_1.default.createElement("tbody", null, _logs.map((log) => (
244
- // log.logId
245
- react_1.default.createElement(log_display_1.LogDisplay, { key: log.logId, log: log, columns: columns }))))))))),
246
+ react_1.default.createElement("p", { className: "mt-2" }, "No logs found"))) : isMobile ? (
247
+ /* Mobile: Card-based layout */
248
+ react_1.default.createElement("div", null, _logs.map((log) => (react_1.default.createElement(mobile_log_card_1.MobileLogCard, { key: log.logId, log: log, colorMode: _colorMode }))))) : (
249
+ /* Desktop: Table layout */
250
+ react_1.default.createElement("table", { className: "table table-sm table-hover mb-0", style: { fontSize: '0.85rem', tableLayout: 'fixed', width: '100%' } },
251
+ react_1.default.createElement("tbody", null, _logs.map((log) => (react_1.default.createElement(log_display_1.LogDisplay, { key: log.logId, log: log, columns: columns }))))))))),
246
252
  react_1.default.createElement("div", { style: {
247
253
  position: 'fixed',
248
254
  bottom: 0,
@@ -0,0 +1,8 @@
1
+ import { IConsoleLog } from "@peers-app/peers-sdk";
2
+ import React from 'react';
3
+ interface MobileLogCardProps {
4
+ log: IConsoleLog;
5
+ colorMode: string;
6
+ }
7
+ export declare const MobileLogCard: ({ log, colorMode }: MobileLogCardProps) => React.JSX.Element;
8
+ export {};
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.MobileLogCard = void 0;
7
+ const peers_sdk_1 = require("@peers-app/peers-sdk");
8
+ const moment_1 = __importDefault(require("moment"));
9
+ const react_1 = __importDefault(require("react"));
10
+ const getLevelColor = (level) => {
11
+ switch (level) {
12
+ case 'error': return '#dc3545';
13
+ case 'warn': return '#ffc107';
14
+ case 'info': return '#0dcaf0';
15
+ case 'log': return '#6c757d';
16
+ case 'debug': return '#6c757d';
17
+ default: return '#6c757d';
18
+ }
19
+ };
20
+ const getProcessColor = (process) => {
21
+ switch (process) {
22
+ case 'ui':
23
+ case 'renderer': return '#0dcaf0';
24
+ case 'electron':
25
+ case 'main': return '#d946ef';
26
+ case 'react-native': return '#61dafb';
27
+ default: return '#6c757d';
28
+ }
29
+ };
30
+ const getLevelIcon = (level) => {
31
+ switch (level) {
32
+ case 'error': return 'bi-x-circle-fill';
33
+ case 'warn': return 'bi-exclamation-triangle-fill';
34
+ case 'info': return 'bi-info-circle-fill';
35
+ case 'debug': return 'bi-bug-fill';
36
+ default: return 'bi-chat-square-text';
37
+ }
38
+ };
39
+ const MobileLogCard = ({ log, colorMode }) => {
40
+ const isDark = colorMode === 'dark';
41
+ // Parse context if it's a string
42
+ let context = log.context;
43
+ if (typeof context === 'string') {
44
+ try {
45
+ context = (0, peers_sdk_1.fromJSONString)(context);
46
+ }
47
+ catch (err) { }
48
+ }
49
+ return (react_1.default.createElement("div", { style: {
50
+ padding: '10px 12px',
51
+ borderBottom: `1px solid ${isDark ? '#333' : '#e9ecef'}`,
52
+ backgroundColor: isDark ? '#1a1a1a' : '#fff',
53
+ } },
54
+ react_1.default.createElement("div", { className: "d-flex align-items-center gap-2 mb-1", style: { flexWrap: 'wrap' } },
55
+ react_1.default.createElement("span", { style: { fontSize: '0.7rem', color: isDark ? '#888' : '#666' } }, (0, moment_1.default)(log.timestamp).format('HH:mm:ss.SSS')),
56
+ react_1.default.createElement("span", { className: "badge text-white", style: {
57
+ backgroundColor: getLevelColor(log.level),
58
+ fontSize: '0.65rem',
59
+ padding: '2px 6px'
60
+ } },
61
+ react_1.default.createElement("i", { className: `${getLevelIcon(log.level)} me-1` }),
62
+ log.level),
63
+ react_1.default.createElement("span", { className: "badge text-white", style: {
64
+ backgroundColor: getProcessColor(log.process),
65
+ fontSize: '0.65rem',
66
+ padding: '2px 6px'
67
+ } }, log.process),
68
+ log.source && (react_1.default.createElement("span", { style: { fontSize: '0.65rem', color: isDark ? '#666' : '#999' } }, log.source))),
69
+ react_1.default.createElement("div", { style: {
70
+ fontSize: '0.8rem',
71
+ wordBreak: 'break-word',
72
+ color: isDark ? '#e0e0e0' : '#333'
73
+ } }, log.message),
74
+ context && (react_1.default.createElement("details", { className: "mt-1" },
75
+ react_1.default.createElement("summary", { className: "text-muted", style: { cursor: 'pointer', fontSize: '0.7rem' } },
76
+ react_1.default.createElement("i", { className: "bi bi-code-square me-1" }),
77
+ "Context"),
78
+ react_1.default.createElement("pre", { className: "mt-1 p-2 rounded", style: {
79
+ fontSize: '0.65rem',
80
+ backgroundColor: isDark ? '#252525' : '#f8f9fa',
81
+ color: isDark ? '#aaa' : '#333',
82
+ overflow: 'auto',
83
+ maxHeight: '150px'
84
+ } }, JSON.stringify(context, null, 2)))),
85
+ log.stackTrace && (react_1.default.createElement("details", { className: "mt-1" },
86
+ react_1.default.createElement("summary", { className: "text-danger", style: { cursor: 'pointer', fontSize: '0.7rem' } },
87
+ react_1.default.createElement("i", { className: "bi bi-bug me-1" }),
88
+ "Stack Trace"),
89
+ react_1.default.createElement("pre", { className: "mt-1 p-2 rounded text-danger", style: {
90
+ fontSize: '0.6rem',
91
+ backgroundColor: isDark ? '#2a1a1a' : '#fff0f0',
92
+ overflow: 'auto',
93
+ maxHeight: '150px'
94
+ } }, log.stackTrace)))));
95
+ };
96
+ exports.MobileLogCard = MobileLogCard;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peers-app/peers-ui",
3
- "version": "0.8.3",
3
+ "version": "0.8.4",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/peers-app/peers-ui.git"
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "peerDependencies": {
29
29
  "bootstrap": "^5.3.3",
30
- "@peers-app/peers-sdk": "^0.8.3",
30
+ "@peers-app/peers-sdk": "^0.8.4",
31
31
  "react": "^18.0.0",
32
32
  "react-dom": "^18.0.0"
33
33
  },
@@ -57,7 +57,7 @@
57
57
  "jest": "^29.7.0",
58
58
  "jest-environment-jsdom": "^30.0.5",
59
59
  "path-browserify": "^1.0.1",
60
- "@peers-app/peers-sdk": "0.8.3",
60
+ "@peers-app/peers-sdk": "0.8.4",
61
61
  "react": "^18.0.0",
62
62
  "react-dom": "^18.0.0",
63
63
  "string-width": "^7.1.0",
@@ -9,6 +9,8 @@ import { colorMode } from '../settings/color-mode-dropdown';
9
9
  import { LogDisplay } from './log-display';
10
10
  import { LogFilters } from './log-filters';
11
11
  import { ResizableTableHeader } from './resizable-table-header';
12
+ import { isDesktop } from '../../globals';
13
+ import { MobileLogCard } from './mobile-log-card';
12
14
 
13
15
  const windowHeight = () => window.innerHeight;
14
16
 
@@ -37,7 +39,9 @@ export const ConsoleLogsList = () => {
37
39
  const [totalLogCount, setTotalLogCount] = useState<number>(0);
38
40
  const [_colorMode] = useObservable(colorMode);
39
41
  const logsEndRef = React.useRef<HTMLDivElement>(null);
40
- const containerRef = React.useRef<HTMLDivElement>(null);
42
+ const containerRef = React.useRef<HTMLDivElement>(null);
43
+ useObservable(isDesktop);
44
+ const isMobile = !isDesktop();
41
45
 
42
46
  const batchSize = 50;
43
47
 
@@ -226,12 +230,14 @@ export const ConsoleLogsList = () => {
226
230
  overflow: 'hidden',
227
231
  }}
228
232
  >
229
- {/* Resizable table header outside scroll area */}
230
- <ResizableTableHeader
231
- columns={columns}
232
- onColumnsChange={setColumns}
233
- colorMode={_colorMode}
234
- />
233
+ {/* Desktop: Resizable table header outside scroll area */}
234
+ {!isMobile && (
235
+ <ResizableTableHeader
236
+ columns={columns}
237
+ onColumnsChange={setColumns}
238
+ colorMode={_colorMode}
239
+ />
240
+ )}
235
241
 
236
242
  {/* Scrollable content area */}
237
243
  <div
@@ -267,11 +273,18 @@ export const ConsoleLogsList = () => {
267
273
  <i className="bi bi-inbox display-1"></i>
268
274
  <p className="mt-2">No logs found</p>
269
275
  </div>
276
+ ) : isMobile ? (
277
+ /* Mobile: Card-based layout */
278
+ <div>
279
+ {_logs.map((log) => (
280
+ <MobileLogCard key={log.logId} log={log} colorMode={_colorMode} />
281
+ ))}
282
+ </div>
270
283
  ) : (
284
+ /* Desktop: Table layout */
271
285
  <table className="table table-sm table-hover mb-0" style={{ fontSize: '0.85rem', tableLayout: 'fixed', width: '100%' }}>
272
286
  <tbody>
273
287
  {_logs.map((log) => (
274
- // log.logId
275
288
  <LogDisplay key={log.logId} log={log} columns={columns} />
276
289
  ))}
277
290
  </tbody>
@@ -0,0 +1,154 @@
1
+ import { fromJSONString, IConsoleLog } from "@peers-app/peers-sdk";
2
+ import moment from 'moment';
3
+ import React from 'react';
4
+
5
+ interface MobileLogCardProps {
6
+ log: IConsoleLog;
7
+ colorMode: string;
8
+ }
9
+
10
+ const getLevelColor = (level: string) => {
11
+ switch (level) {
12
+ case 'error': return '#dc3545';
13
+ case 'warn': return '#ffc107';
14
+ case 'info': return '#0dcaf0';
15
+ case 'log': return '#6c757d';
16
+ case 'debug': return '#6c757d';
17
+ default: return '#6c757d';
18
+ }
19
+ };
20
+
21
+ const getProcessColor = (process: string) => {
22
+ switch (process) {
23
+ case 'ui':
24
+ case 'renderer': return '#0dcaf0';
25
+ case 'electron':
26
+ case 'main': return '#d946ef';
27
+ case 'react-native': return '#61dafb';
28
+ default: return '#6c757d';
29
+ }
30
+ };
31
+
32
+ const getLevelIcon = (level: string) => {
33
+ switch (level) {
34
+ case 'error': return 'bi-x-circle-fill';
35
+ case 'warn': return 'bi-exclamation-triangle-fill';
36
+ case 'info': return 'bi-info-circle-fill';
37
+ case 'debug': return 'bi-bug-fill';
38
+ default: return 'bi-chat-square-text';
39
+ }
40
+ };
41
+
42
+ export const MobileLogCard = ({ log, colorMode }: MobileLogCardProps) => {
43
+ const isDark = colorMode === 'dark';
44
+
45
+ // Parse context if it's a string
46
+ let context = log.context;
47
+ if (typeof context === 'string') {
48
+ try {
49
+ context = fromJSONString(context);
50
+ } catch (err) {}
51
+ }
52
+
53
+ return (
54
+ <div
55
+ style={{
56
+ padding: '10px 12px',
57
+ borderBottom: `1px solid ${isDark ? '#333' : '#e9ecef'}`,
58
+ backgroundColor: isDark ? '#1a1a1a' : '#fff',
59
+ }}
60
+ >
61
+ {/* Header row: timestamp, level, process */}
62
+ <div className="d-flex align-items-center gap-2 mb-1" style={{ flexWrap: 'wrap' }}>
63
+ <span style={{ fontSize: '0.7rem', color: isDark ? '#888' : '#666' }}>
64
+ {moment(log.timestamp).format('HH:mm:ss.SSS')}
65
+ </span>
66
+ <span
67
+ className="badge text-white"
68
+ style={{
69
+ backgroundColor: getLevelColor(log.level),
70
+ fontSize: '0.65rem',
71
+ padding: '2px 6px'
72
+ }}
73
+ >
74
+ <i className={`${getLevelIcon(log.level)} me-1`}></i>
75
+ {log.level}
76
+ </span>
77
+ <span
78
+ className="badge text-white"
79
+ style={{
80
+ backgroundColor: getProcessColor(log.process),
81
+ fontSize: '0.65rem',
82
+ padding: '2px 6px'
83
+ }}
84
+ >
85
+ {log.process}
86
+ </span>
87
+ {log.source && (
88
+ <span style={{ fontSize: '0.65rem', color: isDark ? '#666' : '#999' }}>
89
+ {log.source}
90
+ </span>
91
+ )}
92
+ </div>
93
+
94
+ {/* Message */}
95
+ <div style={{
96
+ fontSize: '0.8rem',
97
+ wordBreak: 'break-word',
98
+ color: isDark ? '#e0e0e0' : '#333'
99
+ }}>
100
+ {log.message}
101
+ </div>
102
+
103
+ {/* Context (collapsible) */}
104
+ {context && (
105
+ <details className="mt-1">
106
+ <summary
107
+ className="text-muted"
108
+ style={{ cursor: 'pointer', fontSize: '0.7rem' }}
109
+ >
110
+ <i className="bi bi-code-square me-1"></i>
111
+ Context
112
+ </summary>
113
+ <pre
114
+ className="mt-1 p-2 rounded"
115
+ style={{
116
+ fontSize: '0.65rem',
117
+ backgroundColor: isDark ? '#252525' : '#f8f9fa',
118
+ color: isDark ? '#aaa' : '#333',
119
+ overflow: 'auto',
120
+ maxHeight: '150px'
121
+ }}
122
+ >
123
+ {JSON.stringify(context, null, 2)}
124
+ </pre>
125
+ </details>
126
+ )}
127
+
128
+ {/* Stack trace (collapsible) */}
129
+ {log.stackTrace && (
130
+ <details className="mt-1">
131
+ <summary
132
+ className="text-danger"
133
+ style={{ cursor: 'pointer', fontSize: '0.7rem' }}
134
+ >
135
+ <i className="bi bi-bug me-1"></i>
136
+ Stack Trace
137
+ </summary>
138
+ <pre
139
+ className="mt-1 p-2 rounded text-danger"
140
+ style={{
141
+ fontSize: '0.6rem',
142
+ backgroundColor: isDark ? '#2a1a1a' : '#fff0f0',
143
+ overflow: 'auto',
144
+ maxHeight: '150px'
145
+ }}
146
+ >
147
+ {log.stackTrace}
148
+ </pre>
149
+ </details>
150
+ )}
151
+ </div>
152
+ );
153
+ };
154
+