@peers-app/peers-ui 0.8.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/command-palette/command-palette.d.ts +2 -2
- package/dist/command-palette/command-palette.js +3 -7
- package/dist/components/group-switcher.d.ts +2 -1
- package/dist/components/group-switcher.js +7 -6
- package/dist/screens/network-viewer/device-details-modal.js +44 -0
- package/dist/screens/network-viewer/group-details-modal.js +80 -2
- package/dist/screens/network-viewer/network-viewer.js +36 -16
- package/dist/screens/settings/settings-page.js +13 -7
- package/dist/screens/setup-user.js +8 -6
- package/dist/system-apps/index.d.ts +1 -0
- package/dist/system-apps/index.js +10 -1
- package/dist/system-apps/mobile-settings.app.d.ts +2 -0
- package/dist/system-apps/mobile-settings.app.js +8 -0
- package/dist/tabs-layout/tabs-layout.js +60 -38
- package/dist/tabs-layout/tabs-state.d.ts +10 -4
- package/dist/tabs-layout/tabs-state.js +41 -4
- package/dist/ui-router/ui-loader.js +45 -12
- package/package.json +3 -3
- package/src/command-palette/command-palette.ts +4 -8
- package/src/components/group-switcher.tsx +12 -8
- package/src/screens/network-viewer/device-details-modal.tsx +55 -0
- package/src/screens/network-viewer/group-details-modal.tsx +144 -1
- package/src/screens/network-viewer/network-viewer.tsx +36 -29
- package/src/screens/settings/settings-page.tsx +17 -9
- package/src/screens/setup-user.tsx +9 -6
- package/src/system-apps/index.ts +9 -0
- package/src/system-apps/mobile-settings.app.ts +8 -0
- package/src/tabs-layout/tabs-layout.tsx +108 -82
- package/src/tabs-layout/tabs-state.ts +54 -5
- package/src/ui-router/ui-loader.tsx +50 -11
|
@@ -8,8 +8,8 @@ export interface Command {
|
|
|
8
8
|
action: () => void;
|
|
9
9
|
isAvailable?: () => boolean;
|
|
10
10
|
}
|
|
11
|
-
export declare const isCommandPaletteOpen: import("@peers-app/peers-sdk").
|
|
12
|
-
export declare const commandSearchQuery: import("@peers-app/peers-sdk").
|
|
11
|
+
export declare const isCommandPaletteOpen: import("@peers-app/peers-sdk").Observable<boolean>;
|
|
12
|
+
export declare const commandSearchQuery: import("@peers-app/peers-sdk").Observable<string>;
|
|
13
13
|
export declare function registerCommand(command: Command): void;
|
|
14
14
|
export declare function unregisterCommand(commandId: string): void;
|
|
15
15
|
export declare function getCommand(commandId: string): Command | undefined;
|
|
@@ -47,13 +47,9 @@ exports.initializeCommandPalette = initializeCommandPalette;
|
|
|
47
47
|
exports.destroyCommandPalette = destroyCommandPalette;
|
|
48
48
|
const peers_sdk_1 = require("@peers-app/peers-sdk");
|
|
49
49
|
const tabs_state_1 = require("../tabs-layout/tabs-state");
|
|
50
|
-
// Command palette state (
|
|
51
|
-
exports.isCommandPaletteOpen = (0, peers_sdk_1.
|
|
52
|
-
|
|
53
|
-
});
|
|
54
|
-
exports.commandSearchQuery = (0, peers_sdk_1.deviceVar)('commandSearchQuery', {
|
|
55
|
-
defaultValue: '',
|
|
56
|
-
});
|
|
50
|
+
// Command palette state (in-memory only, no persistence needed)
|
|
51
|
+
exports.isCommandPaletteOpen = (0, peers_sdk_1.observable)(false);
|
|
52
|
+
exports.commandSearchQuery = (0, peers_sdk_1.observable)('');
|
|
57
53
|
// Command registry
|
|
58
54
|
const registeredCommands = new Map();
|
|
59
55
|
// Detect platform for keyboard shortcuts
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
interface GroupSwitcherProps {
|
|
3
3
|
colorMode: string;
|
|
4
|
+
isMobile?: boolean;
|
|
4
5
|
}
|
|
5
|
-
export declare function GroupSwitcher({ colorMode }: GroupSwitcherProps): React.JSX.Element;
|
|
6
|
+
export declare function GroupSwitcher({ colorMode, isMobile }: GroupSwitcherProps): React.JSX.Element;
|
|
6
7
|
export {};
|
|
@@ -37,7 +37,7 @@ exports.GroupSwitcher = GroupSwitcher;
|
|
|
37
37
|
const peers_sdk_1 = require("@peers-app/peers-sdk");
|
|
38
38
|
const react_1 = __importStar(require("react"));
|
|
39
39
|
const hooks_1 = require("../hooks");
|
|
40
|
-
function GroupSwitcher({ colorMode }) {
|
|
40
|
+
function GroupSwitcher({ colorMode, isMobile = false }) {
|
|
41
41
|
const [showDropdown, setShowDropdown] = (0, react_1.useState)(false);
|
|
42
42
|
const [showCreateModal, setShowCreateModal] = (0, react_1.useState)(false);
|
|
43
43
|
const [allGroups, setAllGroups] = (0, react_1.useState)([]);
|
|
@@ -84,13 +84,14 @@ function GroupSwitcher({ colorMode }) {
|
|
|
84
84
|
const isDark = colorMode === 'dark';
|
|
85
85
|
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
86
86
|
react_1.default.createElement("div", { className: "dropdown" },
|
|
87
|
-
react_1.default.createElement("button", { className:
|
|
88
|
-
padding: '4px 8px',
|
|
87
|
+
react_1.default.createElement("button", { className: `btn btn-sm ${isMobile ? '' : 'me-2'} d-flex align-items-center`, onClick: () => setShowDropdown(!showDropdown), title: `Current group: ${getGroupName(currentGroup)}`, style: {
|
|
88
|
+
padding: isMobile ? '4px' : '4px 8px',
|
|
89
89
|
fontSize: '12px',
|
|
90
90
|
borderRadius: '6px',
|
|
91
91
|
border: 'none',
|
|
92
92
|
background: 'transparent',
|
|
93
|
-
color: isDark ? '#adb5bd' : '#6c757d'
|
|
93
|
+
color: isDark ? '#adb5bd' : '#6c757d',
|
|
94
|
+
minWidth: isMobile ? '36px' : undefined
|
|
94
95
|
}, onMouseEnter: (e) => {
|
|
95
96
|
e.currentTarget.style.backgroundColor = isDark ? '#495057' : '#f8f9fa';
|
|
96
97
|
e.currentTarget.style.color = isDark ? '#ffffff' : '#0d6efd';
|
|
@@ -98,8 +99,8 @@ function GroupSwitcher({ colorMode }) {
|
|
|
98
99
|
e.currentTarget.style.backgroundColor = 'transparent';
|
|
99
100
|
e.currentTarget.style.color = isDark ? '#adb5bd' : '#6c757d';
|
|
100
101
|
} },
|
|
101
|
-
react_1.default.createElement("i", { className: `${getGroupIcon(currentGroup)} me-1`, style: { fontSize: '14px' } }),
|
|
102
|
-
react_1.default.createElement("span", { className: "text-truncate", style: { maxWidth: '80px' } }, getGroupName(currentGroup)),
|
|
102
|
+
react_1.default.createElement("i", { className: `${getGroupIcon(currentGroup)} ${isMobile ? '' : 'me-1'}`, style: { fontSize: '14px' } }),
|
|
103
|
+
!isMobile && (react_1.default.createElement("span", { className: "text-truncate", style: { maxWidth: '80px' } }, getGroupName(currentGroup))),
|
|
103
104
|
react_1.default.createElement("i", { className: "bi-chevron-down ms-1", style: { fontSize: '10px' } })),
|
|
104
105
|
showDropdown && (react_1.default.createElement("div", { className: `dropdown-menu show position-absolute ${isDark ? 'dropdown-menu-dark' : ''}`, style: {
|
|
105
106
|
left: 0,
|
|
@@ -38,6 +38,27 @@ const react_1 = __importStar(require("react"));
|
|
|
38
38
|
const peers_sdk_1 = require("@peers-app/peers-sdk");
|
|
39
39
|
const loading_indicator_1 = require("../../components/loading-indicator");
|
|
40
40
|
const trust_level_badge_1 = require("../../components/trust-level-badge");
|
|
41
|
+
/** Format bytes to human-readable string (KB, MB, GB) */
|
|
42
|
+
function formatBytes(bytes) {
|
|
43
|
+
if (bytes < 1024)
|
|
44
|
+
return `${bytes} B`;
|
|
45
|
+
if (bytes < 1024 * 1024)
|
|
46
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
47
|
+
if (bytes < 1024 * 1024 * 1024)
|
|
48
|
+
return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
49
|
+
return `${(bytes / 1024 / 1024 / 1024).toFixed(2)} GB`;
|
|
50
|
+
}
|
|
51
|
+
/** Format transfer rate to human-readable string (KB/s or MB/s) */
|
|
52
|
+
function formatRate(mbps) {
|
|
53
|
+
if (mbps === undefined || mbps === 0)
|
|
54
|
+
return '0 KB/s';
|
|
55
|
+
// Show KB/s if less than 0.1 MB/s (100 KB/s)
|
|
56
|
+
if (mbps < 0.1) {
|
|
57
|
+
const kbps = mbps * 1024;
|
|
58
|
+
return `${kbps.toFixed(1)} KB/s`;
|
|
59
|
+
}
|
|
60
|
+
return `${mbps.toFixed(2)} MB/s`;
|
|
61
|
+
}
|
|
41
62
|
function DeviceDetailsModal({ deviceId, onClose, onDisconnect }) {
|
|
42
63
|
const [device, setDevice] = (0, react_1.useState)(null);
|
|
43
64
|
const [loading, setLoading] = (0, react_1.useState)(true);
|
|
@@ -133,6 +154,29 @@ function DeviceDetailsModal({ deviceId, onClose, onDisconnect }) {
|
|
|
133
154
|
react_1.default.createElement("strong", null, "Path:"),
|
|
134
155
|
react_1.default.createElement("br", null),
|
|
135
156
|
react_1.default.createElement("small", { className: "text-muted" }, device.throughDeviceIds.join(' → ')))))),
|
|
157
|
+
react_1.default.createElement("div", { className: "mb-4" },
|
|
158
|
+
react_1.default.createElement("h6", { className: "border-bottom pb-2 mb-3" }, "Throughput"),
|
|
159
|
+
react_1.default.createElement("div", { className: "row" },
|
|
160
|
+
react_1.default.createElement("div", { className: "col-md-6 mb-3" },
|
|
161
|
+
react_1.default.createElement("strong", null, "Send Rate:"),
|
|
162
|
+
react_1.default.createElement("br", null),
|
|
163
|
+
react_1.default.createElement("span", { className: "text-success" },
|
|
164
|
+
"\u2191 ",
|
|
165
|
+
formatRate(device.sendRateMBps))),
|
|
166
|
+
react_1.default.createElement("div", { className: "col-md-6 mb-3" },
|
|
167
|
+
react_1.default.createElement("strong", null, "Receive Rate:"),
|
|
168
|
+
react_1.default.createElement("br", null),
|
|
169
|
+
react_1.default.createElement("span", { className: "text-primary" },
|
|
170
|
+
"\u2193 ",
|
|
171
|
+
formatRate(device.receiveRateMBps))),
|
|
172
|
+
react_1.default.createElement("div", { className: "col-md-6 mb-3" },
|
|
173
|
+
react_1.default.createElement("strong", null, "Total Sent:"),
|
|
174
|
+
react_1.default.createElement("br", null),
|
|
175
|
+
react_1.default.createElement("span", null, formatBytes(device.bytesSent || 0))),
|
|
176
|
+
react_1.default.createElement("div", { className: "col-md-6 mb-3" },
|
|
177
|
+
react_1.default.createElement("strong", null, "Total Received:"),
|
|
178
|
+
react_1.default.createElement("br", null),
|
|
179
|
+
react_1.default.createElement("span", null, formatBytes(device.bytesReceived || 0))))),
|
|
136
180
|
device.sharedGroups.length > 0 && (react_1.default.createElement("div", { className: "mb-4" },
|
|
137
181
|
react_1.default.createElement("h6", { className: "border-bottom pb-2 mb-3" },
|
|
138
182
|
"Shared Groups (",
|
|
@@ -40,8 +40,12 @@ const loading_indicator_1 = require("../../components/loading-indicator");
|
|
|
40
40
|
const trust_level_badge_1 = require("../../components/trust-level-badge");
|
|
41
41
|
function GroupDetailsModal({ groupId, onClose }) {
|
|
42
42
|
const [group, setGroup] = (0, react_1.useState)(null);
|
|
43
|
+
const [syncInfo, setSyncInfo] = (0, react_1.useState)(null);
|
|
43
44
|
const [loading, setLoading] = (0, react_1.useState)(true);
|
|
44
45
|
const [notFound, setNotFound] = (0, react_1.useState)(false);
|
|
46
|
+
const [downloading, setDownloading] = (0, react_1.useState)(false);
|
|
47
|
+
const [downloadProgress, setDownloadProgress] = (0, react_1.useState)(null);
|
|
48
|
+
const [downloadResult, setDownloadResult] = (0, react_1.useState)(null);
|
|
45
49
|
const loadData = async () => {
|
|
46
50
|
try {
|
|
47
51
|
const api = window.electronAPI?.networkViewer;
|
|
@@ -49,7 +53,10 @@ function GroupDetailsModal({ groupId, onClose }) {
|
|
|
49
53
|
console.warn('Network Viewer API not available');
|
|
50
54
|
return;
|
|
51
55
|
}
|
|
52
|
-
const groupData = await
|
|
56
|
+
const [groupData, syncData] = await Promise.all([
|
|
57
|
+
api.getGroupDetails(groupId),
|
|
58
|
+
api.getGroupSyncStatus(groupId)
|
|
59
|
+
]);
|
|
53
60
|
if (!groupData) {
|
|
54
61
|
setNotFound(true);
|
|
55
62
|
}
|
|
@@ -57,6 +64,9 @@ function GroupDetailsModal({ groupId, onClose }) {
|
|
|
57
64
|
setGroup(groupData);
|
|
58
65
|
setNotFound(false);
|
|
59
66
|
}
|
|
67
|
+
if (syncData && syncData.length > 0) {
|
|
68
|
+
setSyncInfo(syncData[0]);
|
|
69
|
+
}
|
|
60
70
|
}
|
|
61
71
|
catch (error) {
|
|
62
72
|
console.error('Error loading group details:', error);
|
|
@@ -66,6 +76,38 @@ function GroupDetailsModal({ groupId, onClose }) {
|
|
|
66
76
|
setLoading(false);
|
|
67
77
|
}
|
|
68
78
|
};
|
|
79
|
+
const handleDownloadDatabase = async (deviceId) => {
|
|
80
|
+
try {
|
|
81
|
+
setDownloading(true);
|
|
82
|
+
setDownloadProgress('Preparing download...');
|
|
83
|
+
setDownloadResult(null);
|
|
84
|
+
const api = window.electronAPI?.networkViewer;
|
|
85
|
+
if (!api?.downloadRemoteDatabase) {
|
|
86
|
+
setDownloadResult({
|
|
87
|
+
success: false,
|
|
88
|
+
message: 'Download API not available'
|
|
89
|
+
});
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
setDownloadProgress('Downloading database...');
|
|
93
|
+
const result = await api.downloadRemoteDatabase(groupId, deviceId);
|
|
94
|
+
setDownloadResult(result);
|
|
95
|
+
if (result.success) {
|
|
96
|
+
setDownloadProgress(null);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
console.error('Error downloading database:', error);
|
|
101
|
+
setDownloadResult({
|
|
102
|
+
success: false,
|
|
103
|
+
message: error?.message || 'Download failed'
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
setDownloading(false);
|
|
108
|
+
setDownloadProgress(null);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
69
111
|
(0, react_1.useEffect)(() => {
|
|
70
112
|
loadData();
|
|
71
113
|
}, [groupId]);
|
|
@@ -123,5 +165,41 @@ function GroupDetailsModal({ groupId, onClose }) {
|
|
|
123
165
|
react_1.default.createElement("td", null,
|
|
124
166
|
react_1.default.createElement("span", { className: "badge bg-primary" }, member.role)),
|
|
125
167
|
react_1.default.createElement("td", null,
|
|
126
|
-
react_1.default.createElement(trust_level_badge_1.TrustLevelBadge, { level: member.trustLevel || peers_sdk_1.TrustLevel.Unknown }))))))))))
|
|
168
|
+
react_1.default.createElement(trust_level_badge_1.TrustLevelBadge, { level: member.trustLevel || peers_sdk_1.TrustLevel.Unknown })))))))))),
|
|
169
|
+
react_1.default.createElement("div", { className: "mb-4" },
|
|
170
|
+
react_1.default.createElement("h6", { className: "border-bottom pb-2 mb-3" },
|
|
171
|
+
react_1.default.createElement("i", { className: "bi bi-download me-2" }),
|
|
172
|
+
"Download Database (Fast Sync)"),
|
|
173
|
+
react_1.default.createElement("p", { className: "text-muted small mb-3" }, "Download the entire database from a connected device. This is useful for fast initial sync on a new device. The database will be saved as a backup file."),
|
|
174
|
+
syncInfo && syncInfo.connectedDevices.length > 0 ? (react_1.default.createElement("div", null,
|
|
175
|
+
react_1.default.createElement("label", { className: "form-label" }, "Select a connected device:"),
|
|
176
|
+
react_1.default.createElement("div", { className: "d-flex flex-wrap gap-2" }, syncInfo.connectedDevices.map((device) => (react_1.default.createElement("button", { key: device.deviceId, className: "btn btn-outline-primary btn-sm", onClick: () => handleDownloadDatabase(device.deviceId), disabled: downloading }, downloading ? (react_1.default.createElement(react_1.default.Fragment, null,
|
|
177
|
+
react_1.default.createElement("span", { className: "spinner-border spinner-border-sm me-1", role: "status", "aria-hidden": "true" }),
|
|
178
|
+
"Downloading...")) : (react_1.default.createElement(react_1.default.Fragment, null,
|
|
179
|
+
react_1.default.createElement("i", { className: "bi bi-cloud-download me-1" }),
|
|
180
|
+
device.deviceId.substring(0, 8),
|
|
181
|
+
"...",
|
|
182
|
+
react_1.default.createElement("span", { className: "badge bg-secondary ms-1" },
|
|
183
|
+
Math.round(device.latencyMs),
|
|
184
|
+
"ms"))))))))) : (react_1.default.createElement("div", { className: "alert alert-info mb-0" },
|
|
185
|
+
react_1.default.createElement("i", { className: "bi bi-info-circle me-2" }),
|
|
186
|
+
"No devices are currently connected to this group. Connect to a device first to download its database.")),
|
|
187
|
+
downloadProgress && (react_1.default.createElement("div", { className: "mt-3" },
|
|
188
|
+
react_1.default.createElement("div", { className: "d-flex align-items-center" },
|
|
189
|
+
react_1.default.createElement("span", { className: "spinner-border spinner-border-sm me-2", role: "status", "aria-hidden": "true" }),
|
|
190
|
+
react_1.default.createElement("span", null, downloadProgress)))),
|
|
191
|
+
downloadResult && (react_1.default.createElement("div", { className: `alert mt-3 mb-0 ${downloadResult.success ? 'alert-success' : 'alert-danger'}` }, downloadResult.success ? (react_1.default.createElement(react_1.default.Fragment, null,
|
|
192
|
+
react_1.default.createElement("i", { className: "bi bi-check-circle me-2" }),
|
|
193
|
+
react_1.default.createElement("strong", null, "Download complete!"),
|
|
194
|
+
react_1.default.createElement("br", null),
|
|
195
|
+
react_1.default.createElement("small", null,
|
|
196
|
+
"File: ",
|
|
197
|
+
react_1.default.createElement("code", null, downloadResult.filePath),
|
|
198
|
+
react_1.default.createElement("br", null),
|
|
199
|
+
"Size: ",
|
|
200
|
+
downloadResult.size ? `${(downloadResult.size / (1024 * 1024)).toFixed(2)} MB` : 'Unknown'))) : (react_1.default.createElement(react_1.default.Fragment, null,
|
|
201
|
+
react_1.default.createElement("i", { className: "bi bi-exclamation-circle me-2" }),
|
|
202
|
+
react_1.default.createElement("strong", null, "Download failed:"),
|
|
203
|
+
" ",
|
|
204
|
+
downloadResult.message)))))))))));
|
|
127
205
|
}
|
|
@@ -43,6 +43,27 @@ const globals_1 = require("../../globals");
|
|
|
43
43
|
const device_details_modal_1 = require("./device-details-modal");
|
|
44
44
|
const group_details_modal_1 = require("./group-details-modal");
|
|
45
45
|
const usage_graph_1 = require("./usage-graph");
|
|
46
|
+
/** Format bytes to human-readable string (KB, MB, GB) */
|
|
47
|
+
function formatBytes(bytes) {
|
|
48
|
+
if (bytes < 1024)
|
|
49
|
+
return `${bytes} B`;
|
|
50
|
+
if (bytes < 1024 * 1024)
|
|
51
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
52
|
+
if (bytes < 1024 * 1024 * 1024)
|
|
53
|
+
return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
54
|
+
return `${(bytes / 1024 / 1024 / 1024).toFixed(2)} GB`;
|
|
55
|
+
}
|
|
56
|
+
/** Format transfer rate to human-readable string (KB/s or MB/s) */
|
|
57
|
+
function formatRate(mbps) {
|
|
58
|
+
if (mbps === undefined || mbps === 0)
|
|
59
|
+
return '0 KB/s';
|
|
60
|
+
// Show KB/s if less than 0.1 MB/s (100 KB/s)
|
|
61
|
+
if (mbps < 0.1) {
|
|
62
|
+
const kbps = mbps * 1024;
|
|
63
|
+
return `${kbps.toFixed(1)} KB/s`;
|
|
64
|
+
}
|
|
65
|
+
return `${mbps.toFixed(2)} MB/s`;
|
|
66
|
+
}
|
|
46
67
|
function NetworkViewerList() {
|
|
47
68
|
const [overview, setOverview] = (0, react_1.useState)(null);
|
|
48
69
|
const [connections, setConnections] = (0, react_1.useState)([]);
|
|
@@ -238,22 +259,6 @@ function NetworkViewerList() {
|
|
|
238
259
|
{ maxValue: 80, color: 'rgba(255, 193, 7, 1)' },
|
|
239
260
|
{ maxValue: Infinity, color: 'rgba(220, 53, 69, 1)' }
|
|
240
261
|
] }))))),
|
|
241
|
-
overview.groups.length > 0 && (react_1.default.createElement("div", { className: "card mb-4" },
|
|
242
|
-
react_1.default.createElement("div", { className: "card-body" },
|
|
243
|
-
react_1.default.createElement("h5", { className: "card-title" },
|
|
244
|
-
"Groups: ",
|
|
245
|
-
overview.groups.length),
|
|
246
|
-
react_1.default.createElement("div", { className: "table-responsive" },
|
|
247
|
-
react_1.default.createElement("table", { className: "table table-sm" },
|
|
248
|
-
react_1.default.createElement("thead", null,
|
|
249
|
-
react_1.default.createElement("tr", null,
|
|
250
|
-
react_1.default.createElement("th", null, "Group Name"),
|
|
251
|
-
react_1.default.createElement("th", null, "Connected Devices"),
|
|
252
|
-
react_1.default.createElement("th", null, "Total Members"))),
|
|
253
|
-
react_1.default.createElement("tbody", null, overview.groups.map(group => (react_1.default.createElement("tr", { key: group.groupId },
|
|
254
|
-
react_1.default.createElement("td", null, group.groupName),
|
|
255
|
-
react_1.default.createElement("td", null, group.connectedDevices),
|
|
256
|
-
react_1.default.createElement("td", null, group.totalMembers)))))))))),
|
|
257
262
|
react_1.default.createElement("div", { className: "card mb-4" },
|
|
258
263
|
react_1.default.createElement("div", { className: "card-body" },
|
|
259
264
|
react_1.default.createElement("h5", { className: "card-title" }, "Active Connections"),
|
|
@@ -270,6 +275,7 @@ function NetworkViewerList() {
|
|
|
270
275
|
react_1.default.createElement("th", null, "Shared Groups"),
|
|
271
276
|
react_1.default.createElement("th", null, "Latency"),
|
|
272
277
|
react_1.default.createElement("th", null, "Error Rate"),
|
|
278
|
+
react_1.default.createElement("th", null, "Throughput"),
|
|
273
279
|
react_1.default.createElement("th", null, "Actions"))),
|
|
274
280
|
react_1.default.createElement("tbody", null, connections.map(conn => {
|
|
275
281
|
return (react_1.default.createElement("tr", { key: conn.deviceId },
|
|
@@ -300,6 +306,20 @@ function NetworkViewerList() {
|
|
|
300
306
|
conn.errorRate < 0.2 ? 'warning' : 'danger'}` },
|
|
301
307
|
(conn.errorRate * 100).toFixed(1),
|
|
302
308
|
"%")),
|
|
309
|
+
react_1.default.createElement("td", null,
|
|
310
|
+
react_1.default.createElement("small", null,
|
|
311
|
+
react_1.default.createElement("span", { className: "text-success" },
|
|
312
|
+
"\u2191",
|
|
313
|
+
formatRate(conn.sendRateMBps)),
|
|
314
|
+
' / ',
|
|
315
|
+
react_1.default.createElement("span", { className: "text-primary" },
|
|
316
|
+
"\u2193",
|
|
317
|
+
formatRate(conn.receiveRateMBps))),
|
|
318
|
+
react_1.default.createElement("br", null),
|
|
319
|
+
react_1.default.createElement("small", { className: "text-muted" },
|
|
320
|
+
formatBytes(conn.bytesSent || 0),
|
|
321
|
+
" / ",
|
|
322
|
+
formatBytes(conn.bytesReceived || 0))),
|
|
303
323
|
react_1.default.createElement("td", null,
|
|
304
324
|
react_1.default.createElement("button", { className: "btn btn-sm btn-outline-primary me-2", onClick: () => handleViewDetails(conn.deviceId), title: "View Details" },
|
|
305
325
|
react_1.default.createElement("i", { className: "bi bi-info-circle" })),
|
|
@@ -34,12 +34,12 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.SettingsPage = void 0;
|
|
37
|
-
const react_1 = __importStar(require("react"));
|
|
38
37
|
const peers_sdk_1 = require("@peers-app/peers-sdk");
|
|
39
|
-
const
|
|
40
|
-
const color_mode_dropdown_1 = require("./color-mode-dropdown");
|
|
38
|
+
const react_1 = __importStar(require("react"));
|
|
41
39
|
const input_1 = require("../../components/input");
|
|
40
|
+
const tooltip_1 = require("../../components/tooltip");
|
|
42
41
|
const hooks_1 = require("../../hooks");
|
|
42
|
+
const color_mode_dropdown_1 = require("./color-mode-dropdown");
|
|
43
43
|
const SettingsPage = () => {
|
|
44
44
|
return (react_1.default.createElement("div", { className: 'container-fluid' },
|
|
45
45
|
react_1.default.createElement(color_mode_dropdown_1.ColorModeDropdown, null),
|
|
@@ -71,13 +71,19 @@ const DeviceName = () => {
|
|
|
71
71
|
}, [deviceId]);
|
|
72
72
|
const handleSave = async () => {
|
|
73
73
|
try {
|
|
74
|
-
if (!deviceId)
|
|
74
|
+
if (!deviceId) {
|
|
75
|
+
console.error('No device id');
|
|
75
76
|
return;
|
|
77
|
+
}
|
|
76
78
|
const userContext = await (0, peers_sdk_1.getUserContext)();
|
|
77
79
|
const devicesTable = (0, peers_sdk_1.Devices)(userContext.userDataContext);
|
|
78
|
-
const device = await devicesTable.get(deviceId)
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
const device = await devicesTable.get(deviceId) || {
|
|
81
|
+
deviceId,
|
|
82
|
+
userId: userContext.userId,
|
|
83
|
+
firstSeen: new Date(),
|
|
84
|
+
lastSeen: new Date(),
|
|
85
|
+
trustLevel: peers_sdk_1.TrustLevel.NewDevice,
|
|
86
|
+
};
|
|
81
87
|
device.name = deviceName() || undefined;
|
|
82
88
|
await devicesTable.save(device);
|
|
83
89
|
currentDeviceName(deviceName());
|
|
@@ -103,7 +103,7 @@ const SetupUser = () => {
|
|
|
103
103
|
};
|
|
104
104
|
// Step 1: Select User Type
|
|
105
105
|
if (step === 'select-user-type' && isExistingUser === null) {
|
|
106
|
-
return (react_1.default.createElement("div", { className: "container-fluid d-flex align-items-start justify-content-center", style: {
|
|
106
|
+
return (react_1.default.createElement("div", { className: "container-fluid d-flex align-items-start justify-content-center", style: { paddingTop: '20px', backgroundColor: isDark ? '#212529' : '#f8f9fa' } },
|
|
107
107
|
react_1.default.createElement("div", { className: "card shadow", style: { maxWidth: '500px', width: '100%', backgroundColor: isDark ? '#2d3238' : '#ffffff' } },
|
|
108
108
|
react_1.default.createElement("div", { className: "card-body p-4 p-md-5" },
|
|
109
109
|
react_1.default.createElement("div", { className: "text-center mb-4" },
|
|
@@ -125,7 +125,7 @@ const SetupUser = () => {
|
|
|
125
125
|
}
|
|
126
126
|
// Step 2: New User Confirmation
|
|
127
127
|
if (isExistingUser === false) {
|
|
128
|
-
return (react_1.default.createElement("div", { className: "container-fluid d-flex align-items-start justify-content-center", style: {
|
|
128
|
+
return (react_1.default.createElement("div", { className: "container-fluid d-flex align-items-start justify-content-center", style: { paddingTop: '20px', backgroundColor: isDark ? '#212529' : '#f8f9fa' } },
|
|
129
129
|
react_1.default.createElement("div", { className: "card shadow", style: { maxWidth: '500px', width: '100%', backgroundColor: isDark ? '#2d3238' : '#ffffff' } },
|
|
130
130
|
react_1.default.createElement("div", { className: "card-body p-4 p-md-5" },
|
|
131
131
|
react_1.default.createElement("div", { className: "text-center mb-4" },
|
|
@@ -147,12 +147,14 @@ const SetupUser = () => {
|
|
|
147
147
|
}
|
|
148
148
|
// Step 3: Existing User Sign In
|
|
149
149
|
if (isExistingUser === true) {
|
|
150
|
-
return (react_1.default.createElement("div", { className: "container-fluid d-flex align-items-start justify-content-center", style: {
|
|
150
|
+
return (react_1.default.createElement("div", { className: "container-fluid d-flex align-items-start justify-content-center", style: { paddingTop: '20px', backgroundColor: isDark ? '#212529' : '#f8f9fa' } },
|
|
151
151
|
react_1.default.createElement("div", { className: "card shadow", style: { maxWidth: '500px', width: '100%', backgroundColor: isDark ? '#2d3238' : '#ffffff' } },
|
|
152
152
|
react_1.default.createElement("div", { className: "card-body p-4 p-md-5" },
|
|
153
|
-
react_1.default.createElement("div", { className: "text-center mb-
|
|
154
|
-
react_1.default.createElement("
|
|
155
|
-
|
|
153
|
+
react_1.default.createElement("div", { className: "text-center mb-2" },
|
|
154
|
+
react_1.default.createElement("h3", { className: `fw-bold mb-2 ${isDark ? 'text-light' : ''}` },
|
|
155
|
+
react_1.default.createElement("span", null, "Sign In"),
|
|
156
|
+
"\u00A0\u00A0",
|
|
157
|
+
react_1.default.createElement("i", { className: "bi bi-box-arrow-in-right text-primary" })),
|
|
156
158
|
react_1.default.createElement("p", { className: isDark ? 'text-light opacity-75' : 'text-muted' }, "Enter your existing credentials")),
|
|
157
159
|
error && (react_1.default.createElement("div", { className: "alert alert-danger", role: "alert" },
|
|
158
160
|
react_1.default.createElement("i", { className: "bi bi-exclamation-triangle me-2" }),
|
|
@@ -18,5 +18,6 @@ export { contactsApp } from './contacts.app';
|
|
|
18
18
|
export { consoleLogsApp } from './console-logs.app';
|
|
19
19
|
export { networkViewerApp } from './network-viewer.app';
|
|
20
20
|
export { dataExplorerApp } from './data-explorer.app';
|
|
21
|
+
export { mobileSettingsApp } from './mobile-settings.app';
|
|
21
22
|
export declare const systemApps: IAppNav[];
|
|
22
23
|
export declare const systemPackage: IPackage;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.systemPackage = exports.systemApps = exports.dataExplorerApp = exports.networkViewerApp = exports.consoleLogsApp = exports.contactsApp = exports.groupsApp = exports.settingsApp = exports.profileApp = exports.predicatesApp = exports.knowledgeFramesApp = exports.knowledgeValuesApp = exports.threadsApp = exports.packagesApp = exports.typesApp = exports.variablesApp = exports.eventsApp = exports.workflowsApp = exports.toolsApp = exports.assistantsApp = exports.searchApp = void 0;
|
|
3
|
+
exports.systemPackage = exports.systemApps = exports.mobileSettingsApp = exports.dataExplorerApp = exports.networkViewerApp = exports.consoleLogsApp = exports.contactsApp = exports.groupsApp = exports.settingsApp = exports.profileApp = exports.predicatesApp = exports.knowledgeFramesApp = exports.knowledgeValuesApp = exports.threadsApp = exports.packagesApp = exports.typesApp = exports.variablesApp = exports.eventsApp = exports.workflowsApp = exports.toolsApp = exports.assistantsApp = exports.searchApp = void 0;
|
|
4
4
|
// Import all system apps
|
|
5
5
|
var search_app_1 = require("./search.app");
|
|
6
6
|
Object.defineProperty(exports, "searchApp", { enumerable: true, get: function () { return search_app_1.searchApp; } });
|
|
@@ -40,6 +40,8 @@ var network_viewer_app_1 = require("./network-viewer.app");
|
|
|
40
40
|
Object.defineProperty(exports, "networkViewerApp", { enumerable: true, get: function () { return network_viewer_app_1.networkViewerApp; } });
|
|
41
41
|
var data_explorer_app_1 = require("./data-explorer.app");
|
|
42
42
|
Object.defineProperty(exports, "dataExplorerApp", { enumerable: true, get: function () { return data_explorer_app_1.dataExplorerApp; } });
|
|
43
|
+
var mobile_settings_app_1 = require("./mobile-settings.app");
|
|
44
|
+
Object.defineProperty(exports, "mobileSettingsApp", { enumerable: true, get: function () { return mobile_settings_app_1.mobileSettingsApp; } });
|
|
43
45
|
// Import individual apps
|
|
44
46
|
const search_app_2 = require("./search.app");
|
|
45
47
|
const assistants_app_2 = require("./assistants.app");
|
|
@@ -60,6 +62,11 @@ const contacts_app_2 = require("./contacts.app");
|
|
|
60
62
|
const console_logs_app_2 = require("./console-logs.app");
|
|
61
63
|
const network_viewer_app_2 = require("./network-viewer.app");
|
|
62
64
|
const data_explorer_app_2 = require("./data-explorer.app");
|
|
65
|
+
const mobile_settings_app_2 = require("./mobile-settings.app");
|
|
66
|
+
// Helper to check if running in React Native
|
|
67
|
+
function isReactNative() {
|
|
68
|
+
return typeof window.__NATIVE_THEME !== 'undefined';
|
|
69
|
+
}
|
|
63
70
|
// Collection of all system apps
|
|
64
71
|
exports.systemApps = [
|
|
65
72
|
// Core Navigation & Search
|
|
@@ -82,6 +89,8 @@ exports.systemApps = [
|
|
|
82
89
|
// User & Settings Apps
|
|
83
90
|
profile_app_2.profileApp,
|
|
84
91
|
settings_app_2.settingsApp,
|
|
92
|
+
// Mobile Settings (only in React Native)
|
|
93
|
+
...(isReactNative() ? [mobile_settings_app_2.mobileSettingsApp] : []),
|
|
85
94
|
// System Tools & Debugging
|
|
86
95
|
console_logs_app_2.consoleLogsApp,
|
|
87
96
|
network_viewer_app_2.networkViewerApp,
|
|
@@ -134,7 +134,7 @@ function TabsLayoutInternal() {
|
|
|
134
134
|
borderBottomWidth: '1px',
|
|
135
135
|
borderBottomColor: _colorMode === 'light' ? '#dee2e6' : '#495057'
|
|
136
136
|
} }, isMobile ? (react_1.default.createElement(MobileTabsHeader, { tabs: tabs, activeTab: activeTab, onSwitch: switchTab, onClose: closeTab, colorMode: _colorMode })) : (react_1.default.createElement("div", { className: "d-flex align-items-center px-1", style: { height: '36px' } },
|
|
137
|
-
react_1.default.createElement(group_switcher_1.GroupSwitcher, { colorMode: _colorMode }),
|
|
137
|
+
react_1.default.createElement(group_switcher_1.GroupSwitcher, { colorMode: _colorMode, isMobile: false }),
|
|
138
138
|
react_1.default.createElement("button", { className: "btn btn-sm me-2 d-flex align-items-center", onClick: command_palette_1.openCommandPalette, title: "Search everything (Cmd+K)", style: {
|
|
139
139
|
padding: '4px 8px',
|
|
140
140
|
fontSize: '12px',
|
|
@@ -158,47 +158,52 @@ function TabsLayoutInternal() {
|
|
|
158
158
|
react_1.default.createElement(TabContent, { tab: tab, isMobile: isMobile, isActive: activeTab === tab.tabId })))))));
|
|
159
159
|
}
|
|
160
160
|
function MobileTabsHeader({ tabs, activeTab, onSwitch, onClose, colorMode }) {
|
|
161
|
-
const [
|
|
161
|
+
const [showMenuDropdown, setShowMenuDropdown] = react_1.default.useState(false);
|
|
162
162
|
const currentTab = tabs.find(t => t.tabId === activeTab);
|
|
163
163
|
const nonLauncherTabs = tabs.filter(t => t.packageId !== 'launcher');
|
|
164
|
-
return (react_1.default.createElement("div", { className: "d-flex align-items-center justify-content-between px-2", style: { height: '36px' } },
|
|
164
|
+
return (react_1.default.createElement("div", { className: "d-flex align-items-center justify-content-between px-2 position-relative", style: { height: '36px' } },
|
|
165
165
|
react_1.default.createElement("div", { className: "d-flex align-items-center gap-1" },
|
|
166
|
-
react_1.default.createElement(group_switcher_1.GroupSwitcher, { colorMode: colorMode }),
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
e.currentTarget.style.backgroundColor = colorMode === 'light' ? '#f8f9fa' : '#495057';
|
|
174
|
-
e.currentTarget.style.color = colorMode === 'light' ? '#0d6efd' : '#ffffff';
|
|
175
|
-
}, onMouseLeave: (e) => {
|
|
176
|
-
e.currentTarget.style.backgroundColor = 'transparent';
|
|
177
|
-
e.currentTarget.style.color = colorMode === 'light' ? '#6c757d' : '#adb5bd';
|
|
178
|
-
} },
|
|
179
|
-
react_1.default.createElement("i", { className: "bi-search" })),
|
|
180
|
-
react_1.default.createElement("button", { className: `btn btn-sm ${colorMode === 'light' ? 'btn-outline-primary' : 'btn-outline-light'}`, onClick: () => onSwitch('launcher'), style: { minWidth: '36px' } },
|
|
181
|
-
react_1.default.createElement("i", { className: "bi-grid-3x3-gap" }))),
|
|
182
|
-
react_1.default.createElement("div", { className: "d-flex align-items-center flex-grow-1 justify-content-center" },
|
|
166
|
+
react_1.default.createElement(group_switcher_1.GroupSwitcher, { colorMode: colorMode, isMobile: true })),
|
|
167
|
+
react_1.default.createElement("div", { className: "d-flex align-items-center justify-content-center position-absolute", style: {
|
|
168
|
+
left: '50%',
|
|
169
|
+
transform: 'translateX(-50%)',
|
|
170
|
+
pointerEvents: 'none',
|
|
171
|
+
maxWidth: 'calc(100% - 140px)'
|
|
172
|
+
} },
|
|
183
173
|
currentTab?.iconClassName && currentTab.packageId !== 'launcher' && (react_1.default.createElement("i", { className: `${currentTab.iconClassName} me-2` })),
|
|
184
|
-
react_1.default.createElement("span", { className: "fw-medium text-truncate
|
|
185
|
-
react_1.default.createElement("div", { className: "d-flex align-items-center
|
|
186
|
-
react_1.default.createElement("
|
|
187
|
-
react_1.default.createElement("
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
174
|
+
react_1.default.createElement("span", { className: "fw-medium text-truncate small" }, currentTab?.title || 'Apps')),
|
|
175
|
+
react_1.default.createElement("div", { className: "d-flex align-items-center" },
|
|
176
|
+
react_1.default.createElement("div", { className: "dropdown" },
|
|
177
|
+
react_1.default.createElement("button", { className: `btn btn-sm ${colorMode === 'light' ? 'btn-outline-dark' : 'btn-outline-light'}`, onClick: () => setShowMenuDropdown(!showMenuDropdown), style: { minWidth: '36px' } },
|
|
178
|
+
react_1.default.createElement("i", { className: "bi-list" }),
|
|
179
|
+
nonLauncherTabs.length > 0 && (react_1.default.createElement("span", { className: "ms-1" }, nonLauncherTabs.length))),
|
|
180
|
+
showMenuDropdown && (react_1.default.createElement("div", { className: `dropdown-menu show position-absolute ${colorMode === 'light' ? '' : 'dropdown-menu-dark'}`, style: { right: 0, top: '100%', zIndex: 1000, minWidth: '250px' } },
|
|
181
|
+
react_1.default.createElement("div", { className: `dropdown-item d-flex align-items-center ${activeTab === 'launcher' ? 'active' : ''}`, style: { cursor: 'pointer' }, onClick: () => {
|
|
182
|
+
onSwitch('launcher');
|
|
183
|
+
setShowMenuDropdown(false);
|
|
184
|
+
} },
|
|
185
|
+
react_1.default.createElement("i", { className: "bi-grid-3x3-gap me-2" }),
|
|
186
|
+
react_1.default.createElement("span", null, "Apps")),
|
|
187
|
+
react_1.default.createElement("div", { className: "dropdown-item d-flex align-items-center", style: { cursor: 'pointer' }, onClick: () => {
|
|
188
|
+
(0, command_palette_1.openCommandPalette)();
|
|
189
|
+
setShowMenuDropdown(false);
|
|
190
|
+
} },
|
|
191
|
+
react_1.default.createElement("i", { className: "bi-search me-2" }),
|
|
192
|
+
react_1.default.createElement("span", null, "Search")),
|
|
193
|
+
nonLauncherTabs.length > 0 && (react_1.default.createElement("div", { className: "dropdown-divider" })),
|
|
194
|
+
nonLauncherTabs.slice().reverse().map(tab => (react_1.default.createElement("div", { key: tab.tabId, className: `dropdown-item d-flex align-items-center justify-content-between ${activeTab === tab.tabId ? 'active' : ''}`, style: { cursor: 'pointer' }, onClick: () => {
|
|
195
|
+
onSwitch(tab.tabId);
|
|
196
|
+
setShowMenuDropdown(false);
|
|
197
|
+
} },
|
|
198
|
+
react_1.default.createElement("div", { className: "d-flex align-items-center" },
|
|
199
|
+
tab.iconClassName && react_1.default.createElement("i", { className: `${tab.iconClassName} me-2` }),
|
|
200
|
+
react_1.default.createElement("span", null, tab.title)),
|
|
201
|
+
react_1.default.createElement("button", { className: "btn btn-sm p-0 ms-2", style: { width: '20px', height: '20px' }, onClick: (e) => {
|
|
202
|
+
e.stopPropagation();
|
|
203
|
+
onClose(tab.tabId);
|
|
204
|
+
} },
|
|
205
|
+
react_1.default.createElement("i", { className: "bi-x" }))))))))),
|
|
206
|
+
showMenuDropdown && (react_1.default.createElement("div", { className: "position-fixed w-100 h-100", style: { top: 0, left: 0, zIndex: 999 }, onClick: () => setShowMenuDropdown(false) }))));
|
|
202
207
|
}
|
|
203
208
|
function TabHeader({ tab, isActive, onSwitch, onClose, colorMode }) {
|
|
204
209
|
const activeClass = isActive
|
|
@@ -318,6 +323,23 @@ function AppLauncherTab({ isMobile }) {
|
|
|
318
323
|
.map(path => filteredApps.find(app => app.path === path))
|
|
319
324
|
.filter(Boolean);
|
|
320
325
|
const openApp = (appItem) => {
|
|
326
|
+
// Check if this is the mobile-settings app and we're in React Native
|
|
327
|
+
if (appItem.path === 'mobile-settings' && typeof window.__NATIVE_THEME !== 'undefined') {
|
|
328
|
+
// Use expo-linking to navigate to native screen
|
|
329
|
+
// @ts-ignore
|
|
330
|
+
if (window.ReactNativeWebView) {
|
|
331
|
+
// @ts-ignore
|
|
332
|
+
window.ReactNativeWebView.postMessage(JSON.stringify({
|
|
333
|
+
type: 'navigate',
|
|
334
|
+
path: 'mobile-settings'
|
|
335
|
+
}));
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
// Fallback to deep link
|
|
339
|
+
window.location.href = 'peers://mobile-settings';
|
|
340
|
+
}
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
321
343
|
(0, tabs_state_1.goToTabPath)(appItem.path);
|
|
322
344
|
};
|
|
323
345
|
return (react_1.default.createElement("div", { className: `container-fluid ${isMobile ? 'p-2' : 'p-4'}`, style: { maxHeight: '100%', overflowY: 'auto' } },
|