@peers-app/peers-ui 0.7.39 → 0.8.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.
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare function UserConnect(): React.JSX.Element;
@@ -0,0 +1,312 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.UserConnect = UserConnect;
37
+ const peers_sdk_1 = require("@peers-app/peers-sdk");
38
+ const react_1 = __importStar(require("react"));
39
+ const globals_1 = require("../../globals");
40
+ const hooks_1 = require("../../hooks");
41
+ const ui_loader_1 = require("../../ui-router/ui-loader");
42
+ function UserConnect() {
43
+ const [mode, setMode] = (0, react_1.useState)('select');
44
+ const [status, setStatus] = (0, react_1.useState)('idle');
45
+ const [connectionCode, setConnectionCode] = (0, react_1.useState)('');
46
+ const [inputCode, setInputCode] = (0, react_1.useState)('');
47
+ const [result, setResult] = (0, react_1.useState)(null);
48
+ const [error, setError] = (0, react_1.useState)('');
49
+ const [copied, setCopied] = (0, react_1.useState)(false);
50
+ // Subscribe to userConnectStatus from the device layer
51
+ const [connectStatus] = (0, hooks_1.useObservable)(peers_sdk_1.userConnectStatus);
52
+ // Also set up a direct subscription after loading is complete
53
+ (0, react_1.useEffect)(() => {
54
+ let disposed = false;
55
+ let subscription;
56
+ peers_sdk_1.userConnectStatus.loadingPromise.then(() => {
57
+ if (disposed)
58
+ return;
59
+ subscription = peers_sdk_1.userConnectStatus.subscribe(() => {
60
+ // Force a re-render by checking the current value
61
+ const currentStatus = (0, peers_sdk_1.userConnectStatus)();
62
+ if (currentStatus && typeof currentStatus === 'string') {
63
+ handleConnectStatusChange(currentStatus);
64
+ }
65
+ });
66
+ });
67
+ return () => {
68
+ disposed = true;
69
+ subscription?.dispose();
70
+ };
71
+ }, []);
72
+ // Handle connect status changes
73
+ const handleConnectStatusChange = (0, react_1.useCallback)(async (connectStatusValue) => {
74
+ if (!connectStatusValue || status === 'success')
75
+ return;
76
+ if (connectStatusValue.startsWith('Error:')) {
77
+ // Error status
78
+ setError(connectStatusValue.replace('Error: ', ''));
79
+ setStatus('error');
80
+ }
81
+ else if (connectStatusValue.length > 0) {
82
+ // Success - connectStatus is the remote userId
83
+ const remoteUserId = connectStatusValue;
84
+ try {
85
+ const userContext = await (0, peers_sdk_1.getUserContext)();
86
+ const me = await (0, peers_sdk_1.getMe)();
87
+ const remoteUser = await (0, peers_sdk_1.Users)(userContext.userDataContext).get(remoteUserId);
88
+ if (!remoteUser) {
89
+ setError('Could not find connected user');
90
+ setStatus('error');
91
+ return;
92
+ }
93
+ const remoteDevice = await (0, peers_sdk_1.Devices)(userContext.userDataContext).findOne({ userId: remoteUserId });
94
+ if (!remoteDevice) {
95
+ setError('Could not find connected device but connection was successful');
96
+ setStatus('error');
97
+ return;
98
+ }
99
+ // Build user connect info for confirmation hash
100
+ const myInfo = {
101
+ userId: me.userId,
102
+ publicKey: me.publicKey,
103
+ publicBoxKey: me.publicBoxKey,
104
+ deviceId: userContext.deviceId(),
105
+ };
106
+ const remoteInfo = {
107
+ userId: remoteUser.userId,
108
+ publicKey: remoteUser.publicKey,
109
+ publicBoxKey: remoteUser.publicBoxKey,
110
+ deviceId: remoteDevice.deviceId,
111
+ };
112
+ const confirmationHash = (0, peers_sdk_1.generateConfirmationHash)(myInfo, remoteInfo);
113
+ setResult({ remoteUser, confirmationHash });
114
+ setStatus('success');
115
+ // Clear the codes
116
+ (0, peers_sdk_1.userConnectCodeOffer)('');
117
+ (0, peers_sdk_1.userConnectCodeAnswer)('');
118
+ }
119
+ catch (err) {
120
+ setError(err.message || 'Failed to complete connection');
121
+ setStatus('error');
122
+ }
123
+ }
124
+ }, [status]);
125
+ // React to userConnectStatus changes from useObservable
126
+ (0, react_1.useEffect)(() => {
127
+ if (connectStatus && typeof connectStatus === 'string') {
128
+ handleConnectStatusChange(connectStatus);
129
+ }
130
+ }, [connectStatus, handleConnectStatusChange]);
131
+ // Clean up on unmount
132
+ (0, react_1.useEffect)(() => {
133
+ return () => {
134
+ if (mode === 'initiate' && status === 'waiting') {
135
+ (0, peers_sdk_1.userConnectCodeOffer)('');
136
+ (0, peers_sdk_1.userConnectCodeAnswer)('');
137
+ }
138
+ };
139
+ }, [mode, status]);
140
+ const handleInitiate = (0, react_1.useCallback)(async () => {
141
+ setMode('initiate');
142
+ setStatus('waiting');
143
+ setError('');
144
+ (0, peers_sdk_1.userConnectStatus)(''); // Clear any previous status
145
+ // Generate the connection code
146
+ const code = (0, peers_sdk_1.generateConnectionCode)();
147
+ (0, peers_sdk_1.userConnectCodeOffer)(code.code);
148
+ const formattedCode = (0, peers_sdk_1.formatConnectionCode)(code.code);
149
+ setConnectionCode(formattedCode);
150
+ }, []);
151
+ const handleRespond = (0, react_1.useCallback)(async () => {
152
+ if (inputCode.replace(/[^0-9A-Za-z]/g, '').length !== 12) {
153
+ setError('Please enter a valid 12-character connection code');
154
+ return;
155
+ }
156
+ setStatus('waiting');
157
+ setError('');
158
+ (0, peers_sdk_1.userConnectStatus)(''); // Clear any previous status
159
+ (0, peers_sdk_1.userConnectCodeAnswer)(inputCode);
160
+ }, [inputCode]);
161
+ const handleCancel = (0, react_1.useCallback)(async () => {
162
+ (0, peers_sdk_1.userConnectCodeOffer)('');
163
+ (0, peers_sdk_1.userConnectCodeAnswer)('');
164
+ (0, peers_sdk_1.userConnectStatus)('');
165
+ setMode('select');
166
+ setStatus('idle');
167
+ setConnectionCode('');
168
+ setError('');
169
+ }, []);
170
+ const handleReset = (0, react_1.useCallback)(() => {
171
+ (0, peers_sdk_1.userConnectCodeOffer)('');
172
+ (0, peers_sdk_1.userConnectCodeAnswer)('');
173
+ (0, peers_sdk_1.userConnectStatus)('');
174
+ setMode('select');
175
+ setStatus('idle');
176
+ setConnectionCode('');
177
+ setInputCode('');
178
+ setResult(null);
179
+ setError('');
180
+ setCopied(false);
181
+ }, []);
182
+ const handleSaveContact = (0, react_1.useCallback)(async () => {
183
+ if (!result)
184
+ return;
185
+ try {
186
+ const userContext = await (0, peers_sdk_1.getUserContext)();
187
+ // Contact is already saved by the device layer, just set trust level
188
+ await (0, peers_sdk_1.setUserTrustLevel)(result.remoteUser.userId, peers_sdk_1.TrustLevel.Trusted, userContext.userDataContext);
189
+ // Navigate to contact details
190
+ (0, globals_1.mainContentPath)(`contacts/${result.remoteUser.userId}`);
191
+ }
192
+ catch (err) {
193
+ setError(err.message || 'Failed to save contact');
194
+ }
195
+ }, [result]);
196
+ // Copy connection code to clipboard
197
+ const handleCopyCode = (0, react_1.useCallback)(async () => {
198
+ if (!connectionCode)
199
+ return;
200
+ try {
201
+ await navigator.clipboard.writeText(connectionCode);
202
+ setCopied(true);
203
+ setTimeout(() => setCopied(false), 2000);
204
+ }
205
+ catch (err) {
206
+ // Fallback for older browsers
207
+ const textArea = document.createElement('textarea');
208
+ textArea.value = connectionCode;
209
+ document.body.appendChild(textArea);
210
+ textArea.select();
211
+ document.execCommand('copy');
212
+ document.body.removeChild(textArea);
213
+ setCopied(true);
214
+ setTimeout(() => setCopied(false), 2000);
215
+ }
216
+ }, [connectionCode]);
217
+ // Format input code as user types
218
+ const handleCodeInput = (value) => {
219
+ // Remove non-alphanumeric characters
220
+ const cleaned = value.toUpperCase().replace(/[^0-9A-Z]/g, '');
221
+ // Format as XXXX-YYYY-ZZZZ
222
+ let formatted = '';
223
+ for (let i = 0; i < cleaned.length && i < 12; i++) {
224
+ if (i === 4 || i === 8)
225
+ formatted += '-';
226
+ formatted += cleaned[i];
227
+ }
228
+ setInputCode(formatted);
229
+ };
230
+ return (react_1.default.createElement("div", { className: "container-fluid p-3" },
231
+ react_1.default.createElement("div", { className: "d-flex justify-content-between align-items-center mb-4" },
232
+ react_1.default.createElement("h4", null,
233
+ react_1.default.createElement("i", { className: "bi-person-plus-fill me-2" }),
234
+ "Connect to New User"),
235
+ mode !== 'select' && (react_1.default.createElement("button", { className: "btn btn-outline-secondary btn-sm", onClick: handleReset },
236
+ react_1.default.createElement("i", { className: "bi-arrow-left me-1" }),
237
+ "Back"))),
238
+ mode === 'select' && (react_1.default.createElement("div", { className: "row g-3" },
239
+ react_1.default.createElement("div", { className: "col-md-6" },
240
+ react_1.default.createElement("div", { className: "card h-100 border-primary", style: { cursor: 'pointer' }, onClick: handleInitiate },
241
+ react_1.default.createElement("div", { className: "card-body text-center p-4" },
242
+ react_1.default.createElement("i", { className: "bi-qr-code display-4 text-primary mb-3" }),
243
+ react_1.default.createElement("h5", { className: "card-title" }, "Create Connection Code"),
244
+ react_1.default.createElement("p", { className: "card-text text-muted" }, "Generate a code to share with someone who wants to connect with you")))),
245
+ react_1.default.createElement("div", { className: "col-md-6" },
246
+ react_1.default.createElement("div", { className: "card h-100 border-success", style: { cursor: 'pointer' }, onClick: () => setMode('respond') },
247
+ react_1.default.createElement("div", { className: "card-body text-center p-4" },
248
+ react_1.default.createElement("i", { className: "bi-keyboard display-4 text-success mb-3" }),
249
+ react_1.default.createElement("h5", { className: "card-title" }, "Enter Connection Code"),
250
+ react_1.default.createElement("p", { className: "card-text text-muted" }, "Enter a code that someone shared with you to connect")))))),
251
+ mode === 'initiate' && status === 'waiting' && (react_1.default.createElement("div", { className: "text-center" },
252
+ react_1.default.createElement("div", { className: "mb-4" },
253
+ react_1.default.createElement("p", { className: "text-muted" }, "Share this code with the person you want to connect with:")),
254
+ react_1.default.createElement("div", { className: "mb-4", style: { maxWidth: '400px', margin: '0 auto' } },
255
+ react_1.default.createElement("div", { className: "input-group" },
256
+ react_1.default.createElement("input", { type: "text", className: "form-control form-control-lg text-center font-monospace", value: connectionCode || 'XXXX-YYYY-ZZZZ', readOnly: true, style: { letterSpacing: '0.15em', fontSize: '1.5rem' } }),
257
+ react_1.default.createElement("button", { className: "btn btn-outline-primary", onClick: handleCopyCode, title: "Copy to clipboard" },
258
+ react_1.default.createElement("i", { className: copied ? "bi-check-lg" : "bi-clipboard" }))),
259
+ copied && (react_1.default.createElement("small", { className: "text-success mt-1 d-block" }, "Copied!"))),
260
+ react_1.default.createElement("div", { className: "mb-4" },
261
+ react_1.default.createElement("div", { className: "spinner-border spinner-border-sm text-primary me-2", role: "status" }),
262
+ react_1.default.createElement("span", { className: "text-muted" }, "Waiting for connection...")),
263
+ react_1.default.createElement("p", { className: "text-muted small" }, "This code will expire in 10 minutes"),
264
+ react_1.default.createElement("button", { className: "btn btn-outline-secondary", onClick: handleCancel }, "Cancel"))),
265
+ mode === 'respond' && status !== 'success' && (react_1.default.createElement("div", { className: "text-center" },
266
+ react_1.default.createElement("div", { className: "mb-4" },
267
+ react_1.default.createElement("p", { className: "text-muted" }, "Enter the connection code shared with you:")),
268
+ react_1.default.createElement("div", { className: "mb-4", style: { maxWidth: '400px', margin: '0 auto' } },
269
+ react_1.default.createElement("input", { type: "text", className: "form-control form-control-lg text-center font-monospace", placeholder: "XXXX-YYYY-ZZZZ", value: inputCode, onChange: (e) => handleCodeInput(e.target.value), maxLength: 14, style: { letterSpacing: '0.15em', fontSize: '1.5rem' }, autoFocus: true })),
270
+ error && (react_1.default.createElement("div", { className: "alert alert-danger", style: { maxWidth: '400px', margin: '0 auto 1rem' } }, error)),
271
+ react_1.default.createElement("button", { className: "btn btn-primary btn-lg", onClick: handleRespond, disabled: status === 'waiting' || inputCode.replace(/[^0-9A-Z]/gi, '').length !== 12 }, status === 'waiting' ? (react_1.default.createElement(react_1.default.Fragment, null,
272
+ react_1.default.createElement("span", { className: "spinner-border spinner-border-sm me-2", role: "status" }),
273
+ "Connecting...")) : (react_1.default.createElement(react_1.default.Fragment, null,
274
+ react_1.default.createElement("i", { className: "bi-link-45deg me-2" }),
275
+ "Connect"))))),
276
+ status === 'success' && result && (react_1.default.createElement("div", { className: "text-center" },
277
+ react_1.default.createElement("div", { className: "mb-4" },
278
+ react_1.default.createElement("i", { className: "bi-check-circle-fill text-success display-3" })),
279
+ react_1.default.createElement("h5", { className: "mb-4" }, "Connection Successful!"),
280
+ react_1.default.createElement("div", { className: "card mb-4", style: { maxWidth: '400px', margin: '0 auto' } },
281
+ react_1.default.createElement("div", { className: "card-body" },
282
+ react_1.default.createElement("p", { className: "text-muted mb-2" }, "Verify with the other person that you both see:"),
283
+ react_1.default.createElement("h3", { className: "font-monospace text-primary mb-3", style: { letterSpacing: '0.2em' } }, result.confirmationHash),
284
+ react_1.default.createElement("hr", null),
285
+ react_1.default.createElement("div", { className: "text-start" },
286
+ react_1.default.createElement("small", { className: "text-muted" }, "Name:"),
287
+ react_1.default.createElement("p", { className: "mb-2" }, result.remoteUser.name),
288
+ react_1.default.createElement("small", { className: "text-muted" }, "User ID:"),
289
+ react_1.default.createElement("p", { className: "font-monospace small mb-0" }, result.remoteUser.userId)))),
290
+ react_1.default.createElement("div", { className: "d-flex gap-2 justify-content-center" },
291
+ react_1.default.createElement("button", { className: "btn btn-primary", onClick: handleSaveContact },
292
+ react_1.default.createElement("i", { className: "bi-check-lg me-2" }),
293
+ "Trust & View Contact"),
294
+ react_1.default.createElement("button", { className: "btn btn-outline-secondary", onClick: handleReset }, "Connect Another")))),
295
+ status === 'error' && (react_1.default.createElement("div", { className: "text-center" },
296
+ react_1.default.createElement("div", { className: "mb-4" },
297
+ react_1.default.createElement("i", { className: "bi-x-circle-fill text-danger display-3" })),
298
+ react_1.default.createElement("h5", { className: "mb-4" }, "Connection Failed"),
299
+ react_1.default.createElement("div", { className: "alert alert-danger", style: { maxWidth: '400px', margin: '0 auto 1rem' } }, error),
300
+ react_1.default.createElement("button", { className: "btn btn-primary", onClick: handleReset }, "Try Again")))));
301
+ }
302
+ (0, ui_loader_1.registerInternalPeersUI)({
303
+ peersUIId: '000user00connect0screen01',
304
+ component: UserConnect,
305
+ routes: [
306
+ {
307
+ isMatch: (props, context) => context.path === 'contacts/connect',
308
+ uiCategory: 'screen',
309
+ priority: 3
310
+ }
311
+ ]
312
+ });
@@ -39,10 +39,14 @@ const hooks_1 = require("../../hooks");
39
39
  const color_mode_dropdown_1 = require("../settings/color-mode-dropdown");
40
40
  const tabs_state_1 = require("../../tabs-layout/tabs-state");
41
41
  const mention_configs_1 = require("../../mention-configs");
42
+ const routes_loader_1 = require("../../ui-router/routes-loader");
43
+ const system_apps_1 = require("../../system-apps");
42
44
  function GlobalSearch() {
43
45
  const [_colorMode] = (0, hooks_1.useObservable)(color_mode_dropdown_1.colorMode);
46
+ const [packages] = (0, hooks_1.useObservable)(routes_loader_1.allPackages);
44
47
  const [searchQuery, setSearchQuery] = (0, react_1.useState)('');
45
48
  const [searchResults, setSearchResults] = (0, react_1.useState)([]);
49
+ const [appResults, setAppResults] = (0, react_1.useState)([]);
46
50
  const [isSearching, setIsSearching] = (0, react_1.useState)(false);
47
51
  const inputRef = (0, react_1.useRef)(null);
48
52
  const isDark = _colorMode === 'dark';
@@ -62,10 +66,39 @@ function GlobalSearch() {
62
66
  { config: mention_configs_1.valueTypeMentionConfig, category: 'Types', navigationPath: 'peer-types' },
63
67
  { config: mention_configs_1.userMentionConfig, category: 'Users', navigationPath: 'profile' },
64
68
  ];
69
+ // Get all apps (system and user)
70
+ const getAllApps = () => {
71
+ const allPackages_ = [...packages, system_apps_1.systemPackage];
72
+ return allPackages_
73
+ .filter(p => !p.disabled && p.appNavs && p.appNavs.length > 0)
74
+ .flatMap(pkg => pkg.appNavs.map(navItem => {
75
+ // Construct path - use direct path for system apps, package-nav for others
76
+ let path;
77
+ if (pkg.packageId === 'system-apps') {
78
+ path = navItem.navigationPath ?? navItem.name.replace(/\s/g, '-').toLowerCase();
79
+ }
80
+ else {
81
+ path = `package-nav/${pkg.packageId}/${(navItem.navigationPath ?? navItem.name).replace(/[^a-zA-Z0-9]/g, '-').toLowerCase()}`;
82
+ while (path.includes('//')) {
83
+ path = path.replace('//', '/');
84
+ }
85
+ }
86
+ return {
87
+ packageId: pkg.packageId,
88
+ packageName: pkg.name,
89
+ navItem,
90
+ path,
91
+ name: navItem.name,
92
+ displayName: navItem.displayName || navItem.name,
93
+ iconClassName: navItem.iconClassName || 'bi-box-seam'
94
+ };
95
+ }));
96
+ };
65
97
  // Debounced search effect
66
98
  (0, react_1.useEffect)(() => {
67
99
  if (!searchQuery.trim()) {
68
100
  setSearchResults([]);
101
+ setAppResults([]);
69
102
  return;
70
103
  }
71
104
  const timeoutId = setTimeout(async () => {
@@ -88,13 +121,20 @@ function GlobalSearch() {
88
121
  const searchResults = await Promise.all(searchPromises);
89
122
  const filteredResults = searchResults.filter(Boolean);
90
123
  setSearchResults(filteredResults);
124
+ // Search apps
125
+ const allApps = getAllApps();
126
+ const lowerQuery = searchQuery.toLowerCase();
127
+ const filteredApps = allApps.filter(app => app.name.toLowerCase().includes(lowerQuery) ||
128
+ app.displayName.toLowerCase().includes(lowerQuery) ||
129
+ app.packageName.toLowerCase().includes(lowerQuery));
130
+ setAppResults(filteredApps);
91
131
  }
92
132
  finally {
93
133
  setIsSearching(false);
94
134
  }
95
135
  }, 300); // 300ms debounce
96
136
  return () => clearTimeout(timeoutId);
97
- }, [searchQuery]);
137
+ }, [searchQuery, packages]);
98
138
  const handleItemClick = (result, item) => {
99
139
  // Try using the config's onClick first
100
140
  if (result.config.onClick) {
@@ -114,7 +154,10 @@ function GlobalSearch() {
114
154
  }
115
155
  }
116
156
  };
117
- const totalResults = searchResults.reduce((sum, result) => sum + result.items.length, 0);
157
+ const handleAppClick = (app) => {
158
+ (0, tabs_state_1.goToTabPath)(app.path);
159
+ };
160
+ const totalResults = searchResults.reduce((sum, result) => sum + result.items.length, 0) + appResults.length;
118
161
  return (react_1.default.createElement("div", { className: "container-fluid h-100 p-4", style: { maxHeight: '100vh', overflowY: 'auto' } },
119
162
  react_1.default.createElement("div", { className: "mb-4" },
120
163
  react_1.default.createElement("h1", { className: "h3 mb-3 d-flex align-items-center" },
@@ -129,7 +172,7 @@ function GlobalSearch() {
129
172
  zIndex: 1,
130
173
  fontSize: '18px'
131
174
  } }),
132
- react_1.default.createElement("input", { ref: inputRef, type: "text", className: `form-control form-control-lg ${isDark ? 'bg-dark text-light border-secondary' : ''}`, placeholder: "Search across tools, assistants, workflows, events, and more...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), style: {
175
+ react_1.default.createElement("input", { ref: inputRef, type: "text", className: `form-control form-control-lg ${isDark ? 'bg-dark text-light border-secondary' : ''}`, placeholder: "Search across apps, tools, assistants, workflows, events, and more...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), style: {
133
176
  paddingLeft: '50px',
134
177
  fontSize: '18px',
135
178
  borderRadius: '12px',
@@ -142,44 +185,75 @@ function GlobalSearch() {
142
185
  transform: 'translateY(-50%)'
143
186
  } },
144
187
  react_1.default.createElement("div", { className: "spinner-border spinner-border-sm text-muted" })))),
145
- searchQuery && (react_1.default.createElement("div", { className: "mt-3 text-muted small" }, isSearching ? ('Searching...') : totalResults > 0 ? (`Found ${totalResults} result${totalResults !== 1 ? 's' : ''} across ${searchResults.length} categor${searchResults.length !== 1 ? 'ies' : 'y'}`) : searchQuery.trim() ? ('No results found') : null))),
146
- searchQuery && !isSearching && (react_1.default.createElement("div", null, searchResults.length === 0 ? (react_1.default.createElement("div", { className: "text-center py-5" },
188
+ searchQuery && (react_1.default.createElement("div", { className: "mt-3 text-muted small" }, isSearching ? ('Searching...') : totalResults > 0 ? (`Found ${totalResults} result${totalResults !== 1 ? 's' : ''}`) : searchQuery.trim() ? ('No results found') : null))),
189
+ searchQuery && !isSearching && (react_1.default.createElement("div", null, searchResults.length === 0 && appResults.length === 0 ? (react_1.default.createElement("div", { className: "text-center py-5" },
147
190
  react_1.default.createElement("i", { className: "bi-search mb-3 d-block text-muted", style: { fontSize: '48px' } }),
148
191
  react_1.default.createElement("h4", { className: "text-muted" }, "No results found"),
149
- react_1.default.createElement("p", { className: "text-muted" }, "Try different keywords or check your spelling"))) : (react_1.default.createElement("div", null, searchResults.map((result) => (react_1.default.createElement("div", { key: result.category, className: "mb-5" },
150
- react_1.default.createElement("div", { className: "d-flex align-items-center mb-3" },
151
- react_1.default.createElement("i", { className: `${result.config.iconClass} me-3`, style: { fontSize: '20px', color: '#6c757d' } }),
152
- react_1.default.createElement("h4", { className: "mb-0 me-3" }, result.category),
153
- react_1.default.createElement("span", { className: "badge bg-secondary" }, result.items.length)),
154
- react_1.default.createElement("div", { className: "row g-3" }, result.items.map((item) => (react_1.default.createElement("div", { key: item.id, className: "col-12 col-md-6 col-lg-4" },
155
- react_1.default.createElement("div", { className: `card h-100 ${isDark ? 'bg-dark border-secondary' : 'bg-light'}`, style: {
156
- cursor: 'pointer',
157
- transition: 'all 0.15s ease',
158
- borderRadius: '8px'
159
- }, onClick: () => handleItemClick(result, item), onMouseEnter: (e) => {
160
- e.currentTarget.style.transform = 'translateY(-2px)';
161
- e.currentTarget.style.boxShadow = isDark
162
- ? '0 4px 12px rgba(0,0,0,0.3)'
163
- : '0 4px 12px rgba(0,0,0,0.1)';
164
- }, onMouseLeave: (e) => {
165
- e.currentTarget.style.transform = 'translateY(0)';
166
- e.currentTarget.style.boxShadow = 'none';
167
- } },
168
- react_1.default.createElement("div", { className: "card-body p-3" },
169
- react_1.default.createElement("div", { className: "d-flex align-items-start" },
170
- react_1.default.createElement("i", { className: `${result.config.iconClass} me-3 mt-1`, style: {
171
- fontSize: '16px',
172
- color: isDark ? '#0d6efd' : '#0d6efd',
173
- minWidth: '16px'
174
- } }),
175
- react_1.default.createElement("div", { className: "flex-grow-1" },
176
- react_1.default.createElement("h6", { className: "card-title mb-1 fw-medium" }, item.name),
177
- react_1.default.createElement("small", { className: "text-muted text-uppercase", style: { fontSize: '11px', letterSpacing: '0.5px' } }, result.category.slice(0, -1))),
178
- react_1.default.createElement("i", { className: "bi-arrow-right text-muted", style: { fontSize: '12px' } }))))))))))))))),
192
+ react_1.default.createElement("p", { className: "text-muted" }, "Try different keywords or check your spelling"))) : (react_1.default.createElement("div", null,
193
+ appResults.length > 0 && (react_1.default.createElement("div", { className: "mb-5" },
194
+ react_1.default.createElement("div", { className: "d-flex align-items-center mb-3" },
195
+ react_1.default.createElement("i", { className: "bi-grid-3x3-gap me-3", style: { fontSize: '20px', color: '#6c757d' } }),
196
+ react_1.default.createElement("h4", { className: "mb-0 me-3" }, "Apps"),
197
+ react_1.default.createElement("span", { className: "badge bg-secondary" }, appResults.length)),
198
+ react_1.default.createElement("div", { className: "row g-3" }, appResults.map((app) => (react_1.default.createElement("div", { key: `${app.packageId}-${app.path}`, className: "col-12 col-md-6 col-lg-4" },
199
+ react_1.default.createElement("div", { className: `card h-100 ${isDark ? 'bg-dark border-secondary' : 'bg-light'}`, style: {
200
+ cursor: 'pointer',
201
+ transition: 'all 0.15s ease',
202
+ borderRadius: '8px'
203
+ }, onClick: () => handleAppClick(app), onMouseEnter: (e) => {
204
+ e.currentTarget.style.transform = 'translateY(-2px)';
205
+ e.currentTarget.style.boxShadow = isDark
206
+ ? '0 4px 12px rgba(0,0,0,0.3)'
207
+ : '0 4px 12px rgba(0,0,0,0.1)';
208
+ }, onMouseLeave: (e) => {
209
+ e.currentTarget.style.transform = 'translateY(0)';
210
+ e.currentTarget.style.boxShadow = 'none';
211
+ } },
212
+ react_1.default.createElement("div", { className: "card-body p-3" },
213
+ react_1.default.createElement("div", { className: "d-flex align-items-start" },
214
+ react_1.default.createElement("i", { className: `${app.iconClassName} me-3 mt-1`, style: {
215
+ fontSize: '16px',
216
+ color: isDark ? '#0d6efd' : '#0d6efd',
217
+ minWidth: '16px'
218
+ } }),
219
+ react_1.default.createElement("div", { className: "flex-grow-1" },
220
+ react_1.default.createElement("h6", { className: "card-title mb-1 fw-medium" }, app.displayName),
221
+ react_1.default.createElement("small", { className: "text-muted text-uppercase", style: { fontSize: '11px', letterSpacing: '0.5px' } }, app.packageId === 'system-apps' ? 'System App' : app.packageName)),
222
+ react_1.default.createElement("i", { className: "bi-arrow-right text-muted", style: { fontSize: '12px' } })))))))))),
223
+ searchResults.map((result) => (react_1.default.createElement("div", { key: result.category, className: "mb-5" },
224
+ react_1.default.createElement("div", { className: "d-flex align-items-center mb-3" },
225
+ react_1.default.createElement("i", { className: `${result.config.iconClass} me-3`, style: { fontSize: '20px', color: '#6c757d' } }),
226
+ react_1.default.createElement("h4", { className: "mb-0 me-3" }, result.category),
227
+ react_1.default.createElement("span", { className: "badge bg-secondary" }, result.items.length)),
228
+ react_1.default.createElement("div", { className: "row g-3" }, result.items.map((item) => (react_1.default.createElement("div", { key: item.id, className: "col-12 col-md-6 col-lg-4" },
229
+ react_1.default.createElement("div", { className: `card h-100 ${isDark ? 'bg-dark border-secondary' : 'bg-light'}`, style: {
230
+ cursor: 'pointer',
231
+ transition: 'all 0.15s ease',
232
+ borderRadius: '8px'
233
+ }, onClick: () => handleItemClick(result, item), onMouseEnter: (e) => {
234
+ e.currentTarget.style.transform = 'translateY(-2px)';
235
+ e.currentTarget.style.boxShadow = isDark
236
+ ? '0 4px 12px rgba(0,0,0,0.3)'
237
+ : '0 4px 12px rgba(0,0,0,0.1)';
238
+ }, onMouseLeave: (e) => {
239
+ e.currentTarget.style.transform = 'translateY(0)';
240
+ e.currentTarget.style.boxShadow = 'none';
241
+ } },
242
+ react_1.default.createElement("div", { className: "card-body p-3" },
243
+ react_1.default.createElement("div", { className: "d-flex align-items-start" },
244
+ react_1.default.createElement("i", { className: `${result.config.iconClass} me-3 mt-1`, style: {
245
+ fontSize: '16px',
246
+ color: isDark ? '#0d6efd' : '#0d6efd',
247
+ minWidth: '16px'
248
+ } }),
249
+ react_1.default.createElement("div", { className: "flex-grow-1" },
250
+ react_1.default.createElement("h6", { className: "card-title mb-1 fw-medium" }, item.name),
251
+ react_1.default.createElement("small", { className: "text-muted text-uppercase", style: { fontSize: '11px', letterSpacing: '0.5px' } }, result.category.slice(0, -1))),
252
+ react_1.default.createElement("i", { className: "bi-arrow-right text-muted", style: { fontSize: '12px' } }))))))))))))))),
179
253
  !searchQuery && (react_1.default.createElement("div", { className: "text-center py-5" },
180
254
  react_1.default.createElement("i", { className: "bi-search mb-3 d-block text-muted", style: { fontSize: '64px' } }),
181
255
  react_1.default.createElement("h3", { className: "text-muted mb-3" }, "Search across everything"),
182
- react_1.default.createElement("p", { className: "text-muted mb-4", style: { maxWidth: '400px', margin: '0 auto' } }, "Find tools, assistants, workflows, events, predicates, types, and users all in one place."),
256
+ react_1.default.createElement("p", { className: "text-muted mb-4", style: { maxWidth: '400px', margin: '0 auto' } }, "Find apps, tools, assistants, workflows, events, predicates, types, and users all in one place."),
183
257
  react_1.default.createElement("div", { className: "d-flex flex-wrap justify-content-center gap-2" }, searchConfigs.map(({ config, category }) => (react_1.default.createElement("span", { key: category, className: `badge ${isDark ? 'bg-secondary' : 'bg-light text-dark'} px-3 py-2 d-flex align-items-center`, style: { fontSize: '12px' } },
184
258
  react_1.default.createElement("i", { className: `${config.iconClass} me-2` }),
185
259
  category))))))));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peers-app/peers-ui",
3
- "version": "0.7.39",
3
+ "version": "0.8.0",
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.7.39",
30
+ "@peers-app/peers-sdk": "^0.8.0",
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.7.39",
60
+ "@peers-app/peers-sdk": "0.8.0",
61
61
  "react": "^18.0.0",
62
62
  "react-dom": "^18.0.0",
63
63
  "string-width": "^7.1.0",