@peers-app/peers-ui 0.27.3 → 0.27.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.
Files changed (309) hide show
  1. package/dist/screens/network-viewer/network-viewer-ipc.d.ts +3 -1
  2. package/dist/screens/network-viewer/network-viewer-ipc.js +6 -0
  3. package/dist/screens/network-viewer/sync-recovery.js +103 -1
  4. package/dist/screens/variables/variable-details.js +2 -1
  5. package/dist/tabs-layout/network-status-indicator.js +26 -12
  6. package/dist/tabs-layout/use-network-status.d.ts +6 -1
  7. package/dist/tabs-layout/use-network-status.js +27 -4
  8. package/package.json +9 -3
  9. package/.claude/settings.local.json +0 -34
  10. package/.github/README.md +0 -52
  11. package/.github/workflows/delete-all-artifacts.yml +0 -49
  12. package/.github/workflows/publish.yml +0 -51
  13. package/babel.config.js +0 -7
  14. package/biome.json +0 -198
  15. package/dist/app-updates/app-updates-consumer.test.d.ts +0 -1
  16. package/dist/app-updates/app-updates-consumer.test.js +0 -56
  17. package/dist/app-updates/app-updates-settings.test.d.ts +0 -1
  18. package/dist/app-updates/app-updates-settings.test.js +0 -93
  19. package/dist/app-updates/remote-app-update-panel.test.d.ts +0 -1
  20. package/dist/app-updates/remote-app-update-panel.test.js +0 -165
  21. package/dist/components/access-level-field.test.d.ts +0 -1
  22. package/dist/components/access-level-field.test.js +0 -28
  23. package/dist/components/message-logs/tool-access-approval-card.test.d.ts +0 -1
  24. package/dist/components/message-logs/tool-access-approval-card.test.js +0 -102
  25. package/dist/components/responsive-section-nav.test.d.ts +0 -1
  26. package/dist/components/responsive-section-nav.test.js +0 -45
  27. package/dist/globals-hash.test.d.ts +0 -1
  28. package/dist/globals-hash.test.js +0 -16
  29. package/dist/hooks/use-observable-state.test.d.ts +0 -1
  30. package/dist/hooks/use-observable-state.test.js +0 -64
  31. package/dist/hooks/use-observable.test.d.ts +0 -1
  32. package/dist/hooks/use-observable.test.js +0 -191
  33. package/dist/hooks/use-rows.test.d.ts +0 -1
  34. package/dist/hooks/use-rows.test.js +0 -175
  35. package/dist/hooks/use-tool.test.d.ts +0 -1
  36. package/dist/hooks/use-tool.test.js +0 -190
  37. package/dist/host-shell/host-shell-consumer.test.d.ts +0 -1
  38. package/dist/host-shell/host-shell-consumer.test.js +0 -28
  39. package/dist/root-layout/root-layout-host.test.d.ts +0 -1
  40. package/dist/root-layout/root-layout-host.test.js +0 -86
  41. package/dist/screens/account/account-screen.test.d.ts +0 -1
  42. package/dist/screens/account/account-screen.test.js +0 -51
  43. package/dist/screens/assistants/assistant-config.test.d.ts +0 -1
  44. package/dist/screens/assistants/assistant-config.test.js +0 -57
  45. package/dist/screens/contacts/contact-list.test.d.ts +0 -1
  46. package/dist/screens/contacts/contact-list.test.js +0 -48
  47. package/dist/screens/device-pairing/device-pairing.test.d.ts +0 -1
  48. package/dist/screens/device-pairing/device-pairing.test.js +0 -119
  49. package/dist/screens/devices/devices-list.test.d.ts +0 -1
  50. package/dist/screens/devices/devices-list.test.js +0 -53
  51. package/dist/screens/groups/group-list.test.d.ts +0 -1
  52. package/dist/screens/groups/group-list.test.js +0 -38
  53. package/dist/screens/identity/identity-routes.test.d.ts +0 -1
  54. package/dist/screens/identity/identity-routes.test.js +0 -51
  55. package/dist/screens/identity/identity-screen.test.d.ts +0 -1
  56. package/dist/screens/identity/identity-screen.test.js +0 -103
  57. package/dist/screens/invites/accept-invite.test.d.ts +0 -1
  58. package/dist/screens/invites/accept-invite.test.js +0 -103
  59. package/dist/screens/invites/activity-view.test.d.ts +0 -1
  60. package/dist/screens/invites/activity-view.test.js +0 -108
  61. package/dist/screens/invites/invite-classify.test.d.ts +0 -1
  62. package/dist/screens/invites/invite-classify.test.js +0 -115
  63. package/dist/screens/network-viewer/network-viewer-ipc.test.d.ts +0 -1
  64. package/dist/screens/network-viewer/network-viewer-ipc.test.js +0 -61
  65. package/dist/screens/network-viewer/network-viewer.section.test.d.ts +0 -1
  66. package/dist/screens/network-viewer/network-viewer.section.test.js +0 -63
  67. package/dist/screens/network-viewer/sync-recovery.test.d.ts +0 -1
  68. package/dist/screens/network-viewer/sync-recovery.test.js +0 -104
  69. package/dist/screens/packages/package-helpers.test.d.ts +0 -1
  70. package/dist/screens/packages/package-helpers.test.js +0 -53
  71. package/dist/screens/packages/package-runtime-status.test.d.ts +0 -1
  72. package/dist/screens/packages/package-runtime-status.test.js +0 -50
  73. package/dist/screens/packages/packages-ipc.test.d.ts +0 -1
  74. package/dist/screens/packages/packages-ipc.test.js +0 -43
  75. package/dist/screens/settings/settings-page.test.d.ts +0 -1
  76. package/dist/screens/settings/settings-page.test.js +0 -19
  77. package/dist/screens/tools/tool-access-ipc.test.d.ts +0 -1
  78. package/dist/screens/tools/tool-access-ipc.test.js +0 -40
  79. package/dist/screens/tools/tool-catalog-ipc.test.d.ts +0 -1
  80. package/dist/screens/tools/tool-catalog-ipc.test.js +0 -52
  81. package/dist/tabs-layout/network-status-indicator.test.d.ts +0 -1
  82. package/dist/tabs-layout/network-status-indicator.test.js +0 -665
  83. package/dist/tabs-layout/tabs-state.test.d.ts +0 -1
  84. package/dist/tabs-layout/tabs-state.test.js +0 -110
  85. package/dist/tabs-layout/use-network-status.test.d.ts +0 -1
  86. package/dist/tabs-layout/use-network-status.test.js +0 -45
  87. package/dist/ui-router/ui-loader.test.d.ts +0 -1
  88. package/dist/ui-router/ui-loader.test.js +0 -91
  89. package/jest.config.js +0 -25
  90. package/release-guard.js +0 -10
  91. package/src/app-updates/app-update-controls.tsx +0 -73
  92. package/src/app-updates/app-updates-consumer.test.ts +0 -83
  93. package/src/app-updates/app-updates-consumer.ts +0 -121
  94. package/src/app-updates/app-updates-settings.test.tsx +0 -109
  95. package/src/app-updates/app-updates-settings.tsx +0 -136
  96. package/src/app-updates/remote-app-update-panel.test.tsx +0 -205
  97. package/src/app-updates/remote-app-update-panel.tsx +0 -210
  98. package/src/app-updates/use-app-update-status.ts +0 -16
  99. package/src/command-palette/command-palette-ui.tsx +0 -564
  100. package/src/command-palette/command-palette.ts +0 -242
  101. package/src/components/access-level-field.test.tsx +0 -32
  102. package/src/components/access-level-field.tsx +0 -68
  103. package/src/components/checkbox.tsx +0 -25
  104. package/src/components/group-switcher.tsx +0 -526
  105. package/src/components/input-date.tsx +0 -29
  106. package/src/components/input-number.tsx +0 -83
  107. package/src/components/input.tsx +0 -25
  108. package/src/components/inverse-lazy-list.tsx +0 -107
  109. package/src/components/io-schema-values.tsx +0 -103
  110. package/src/components/io-schema.tsx +0 -259
  111. package/src/components/lazy-list.tsx +0 -115
  112. package/src/components/list-screen.tsx +0 -126
  113. package/src/components/loading-indicator.tsx +0 -7
  114. package/src/components/markdown-editor/autolink-plugin.tsx +0 -29
  115. package/src/components/markdown-editor/editor-inline.tsx +0 -9
  116. package/src/components/markdown-editor/editor.tsx +0 -158
  117. package/src/components/markdown-editor/markdown-plugin.tsx +0 -202
  118. package/src/components/markdown-editor/mention-node.ts +0 -204
  119. package/src/components/markdown-editor/mentions-plugin.tsx +0 -354
  120. package/src/components/markdown-editor/move-line-plugin.tsx +0 -186
  121. package/src/components/markdown-editor/select-line-boundary-plugin.tsx +0 -95
  122. package/src/components/markdown-editor/theme.ts +0 -46
  123. package/src/components/markdown-editor/toolbar.tsx +0 -269
  124. package/src/components/markdown-with-mentions.tsx +0 -198
  125. package/src/components/message-logs/message-logs.tsx +0 -523
  126. package/src/components/message-logs/tool-access-approval-card.test.tsx +0 -127
  127. package/src/components/message-logs/tool-access-approval-card.tsx +0 -178
  128. package/src/components/messages/avatar.tsx +0 -171
  129. package/src/components/messages/channel-message-list.tsx +0 -189
  130. package/src/components/messages/channel-view.tsx +0 -92
  131. package/src/components/messages/message-compose.tsx +0 -192
  132. package/src/components/messages/message-display.tsx +0 -231
  133. package/src/components/messages/thread-message-list.tsx +0 -135
  134. package/src/components/qr-scanner.tsx +0 -180
  135. package/src/components/responsive-section-nav.test.tsx +0 -88
  136. package/src/components/responsive-section-nav.tsx +0 -92
  137. package/src/components/router.tsx +0 -175
  138. package/src/components/save-button.tsx +0 -134
  139. package/src/components/sortable-list.tsx +0 -130
  140. package/src/components/tabs.tsx +0 -76
  141. package/src/components/tooltip.tsx +0 -43
  142. package/src/components/trust-level-badge.tsx +0 -44
  143. package/src/components/trust-level-dropdown.tsx +0 -136
  144. package/src/components/typeahead.tsx +0 -357
  145. package/src/globals-hash.test.ts +0 -19
  146. package/src/globals.tsx +0 -226
  147. package/src/hooks/index.ts +0 -8
  148. package/src/hooks/use-host-contract.ts +0 -75
  149. package/src/hooks/use-observable-state.test.tsx +0 -101
  150. package/src/hooks/use-observable-state.ts +0 -29
  151. package/src/hooks/use-observable.test.tsx +0 -258
  152. package/src/hooks/use-observable.ts +0 -67
  153. package/src/hooks/use-on-screen.ts +0 -24
  154. package/src/hooks/use-promise.ts +0 -34
  155. package/src/hooks/use-rows.test.tsx +0 -196
  156. package/src/hooks/use-rows.ts +0 -120
  157. package/src/hooks/use-subscription.ts +0 -24
  158. package/src/hooks/use-tool.test.tsx +0 -225
  159. package/src/hooks/use-tool.ts +0 -116
  160. package/src/host-shell/host-shell-consumer.test.ts +0 -32
  161. package/src/host-shell/host-shell-consumer.ts +0 -53
  162. package/src/index.tsx +0 -42
  163. package/src/mention-configs.ts +0 -185
  164. package/src/operator-console/operator-console-state.ts +0 -119
  165. package/src/operator-console/operator-console.tsx +0 -233
  166. package/src/peers-services-registration.ts +0 -6
  167. package/src/root-layout/global-overlays.ts +0 -50
  168. package/src/root-layout/layout-picker.tsx +0 -47
  169. package/src/root-layout/layouts/full-screen-layout.tsx +0 -144
  170. package/src/root-layout/layouts/tabs-with-console-layout.tsx +0 -260
  171. package/src/root-layout/root-layout-host.test.tsx +0 -108
  172. package/src/root-layout/root-layout-host.tsx +0 -147
  173. package/src/root-layout/root-layout-registry.ts +0 -51
  174. package/src/root-layout/root-layout-state.ts +0 -55
  175. package/src/root-layout/root-layout.ts +0 -32
  176. package/src/screens/account/account-screen.test.tsx +0 -65
  177. package/src/screens/account/account-screen.tsx +0 -942
  178. package/src/screens/assistants/assistant-config.test.tsx +0 -54
  179. package/src/screens/assistants/assistant-config.tsx +0 -121
  180. package/src/screens/assistants/assistant-details.tsx +0 -89
  181. package/src/screens/assistants/assistant-info.tsx +0 -66
  182. package/src/screens/assistants/assistant-list.tsx +0 -145
  183. package/src/screens/assistants/assistant-tools.tsx +0 -82
  184. package/src/screens/console-logs/console-logs-list.tsx +0 -374
  185. package/src/screens/console-logs/log-display.tsx +0 -137
  186. package/src/screens/console-logs/log-filters.tsx +0 -64
  187. package/src/screens/console-logs/mobile-log-card.tsx +0 -161
  188. package/src/screens/console-logs/resizable-table-header.tsx +0 -110
  189. package/src/screens/contacts/contact-details.tsx +0 -123
  190. package/src/screens/contacts/contact-list.test.tsx +0 -59
  191. package/src/screens/contacts/contact-list.tsx +0 -286
  192. package/src/screens/contacts/index.ts +0 -2
  193. package/src/screens/data-explorer/data-explorer.tsx +0 -633
  194. package/src/screens/data-explorer/index.ts +0 -6
  195. package/src/screens/data-explorer/query-executor.tsx +0 -520
  196. package/src/screens/device-pairing/device-pairing.test.tsx +0 -143
  197. package/src/screens/device-pairing/device-pairing.tsx +0 -461
  198. package/src/screens/device-pairing/identity-ipc.ts +0 -25
  199. package/src/screens/device-pairing/index.ts +0 -2
  200. package/src/screens/device-pairing/use-device-pairing-state.ts +0 -15
  201. package/src/screens/devices/devices-list.test.tsx +0 -68
  202. package/src/screens/devices/devices-list.tsx +0 -365
  203. package/src/screens/devices/index.ts +0 -1
  204. package/src/screens/groups/group-details.tsx +0 -160
  205. package/src/screens/groups/group-invite-panel.tsx +0 -311
  206. package/src/screens/groups/group-list.test.tsx +0 -44
  207. package/src/screens/groups/group-list.tsx +0 -317
  208. package/src/screens/groups/group-members.tsx +0 -343
  209. package/src/screens/groups/index.ts +0 -3
  210. package/src/screens/identity/identity-routes.test.ts +0 -70
  211. package/src/screens/identity/identity-routes.ts +0 -301
  212. package/src/screens/identity/identity-screen.test.tsx +0 -138
  213. package/src/screens/identity/identity-screen.tsx +0 -282
  214. package/src/screens/identity/index.ts +0 -2
  215. package/src/screens/identity/invite-contact-panel.tsx +0 -45
  216. package/src/screens/identity/profile-invite.tsx +0 -93
  217. package/src/screens/invites/accept-invite.test.tsx +0 -115
  218. package/src/screens/invites/accept-invite.tsx +0 -318
  219. package/src/screens/invites/activity-view.test.tsx +0 -127
  220. package/src/screens/invites/activity-view.tsx +0 -162
  221. package/src/screens/invites/index.ts +0 -6
  222. package/src/screens/invites/invite-capture.ts +0 -104
  223. package/src/screens/invites/invite-classify.test.ts +0 -129
  224. package/src/screens/invites/invite-share.tsx +0 -104
  225. package/src/screens/invites/invites-ipc.ts +0 -25
  226. package/src/screens/invites/pending-invites-list.tsx +0 -256
  227. package/src/screens/network-viewer/connection-troubleshooter.tsx +0 -395
  228. package/src/screens/network-viewer/cpu-usage-graph.tsx +0 -294
  229. package/src/screens/network-viewer/device-details-modal.tsx +0 -339
  230. package/src/screens/network-viewer/group-details-modal.tsx +0 -421
  231. package/src/screens/network-viewer/index.ts +0 -8
  232. package/src/screens/network-viewer/network-viewer-ipc.test.ts +0 -72
  233. package/src/screens/network-viewer/network-viewer-ipc.ts +0 -39
  234. package/src/screens/network-viewer/network-viewer-section.ts +0 -12
  235. package/src/screens/network-viewer/network-viewer.section.test.tsx +0 -68
  236. package/src/screens/network-viewer/network-viewer.tsx +0 -708
  237. package/src/screens/network-viewer/remote-logs-panel.tsx +0 -190
  238. package/src/screens/network-viewer/sync-recovery.test.tsx +0 -114
  239. package/src/screens/network-viewer/sync-recovery.tsx +0 -461
  240. package/src/screens/network-viewer/usage-graph.tsx +0 -455
  241. package/src/screens/packages/local-folder-grants-panel.tsx +0 -290
  242. package/src/screens/packages/local-folders-ipc.ts +0 -28
  243. package/src/screens/packages/package-details.tsx +0 -166
  244. package/src/screens/packages/package-helpers.test.ts +0 -57
  245. package/src/screens/packages/package-helpers.ts +0 -137
  246. package/src/screens/packages/package-info.tsx +0 -354
  247. package/src/screens/packages/package-list.tsx +0 -519
  248. package/src/screens/packages/package-network-panel.tsx +0 -82
  249. package/src/screens/packages/package-new-local.tsx +0 -100
  250. package/src/screens/packages/package-runtime-status.test.tsx +0 -58
  251. package/src/screens/packages/package-runtime-status.tsx +0 -68
  252. package/src/screens/packages/package-versions.tsx +0 -298
  253. package/src/screens/packages/packages-ipc.test.ts +0 -47
  254. package/src/screens/packages/packages-ipc.ts +0 -125
  255. package/src/screens/peer-types/peer-type-details.tsx +0 -172
  256. package/src/screens/peer-types/peer-type-list.tsx +0 -64
  257. package/src/screens/search/global-search.tsx +0 -406
  258. package/src/screens/settings/color-mode-dropdown.tsx +0 -82
  259. package/src/screens/settings/settings-page.test.tsx +0 -24
  260. package/src/screens/settings/settings-page.tsx +0 -426
  261. package/src/screens/setup-user.tsx +0 -355
  262. package/src/screens/tools/tool-access-ipc.test.ts +0 -55
  263. package/src/screens/tools/tool-access-ipc.ts +0 -27
  264. package/src/screens/tools/tool-catalog-ipc.test.ts +0 -52
  265. package/src/screens/tools/tool-catalog-ipc.ts +0 -51
  266. package/src/screens/tools/tool-details.tsx +0 -52
  267. package/src/screens/tools/tool-info.tsx +0 -45
  268. package/src/screens/tools/tool-list.tsx +0 -106
  269. package/src/screens/tools/tool-schema.tsx +0 -44
  270. package/src/screens/tools/tool-test-details.tsx +0 -89
  271. package/src/screens/tools/tool-test-list.tsx +0 -73
  272. package/src/screens/variables/variable-details.tsx +0 -289
  273. package/src/screens/variables/variable-list.tsx +0 -110
  274. package/src/screens/welcome-modal.tsx +0 -294
  275. package/src/screens/workflows/workflow-details.tsx +0 -115
  276. package/src/screens/workflows/workflow-info.tsx +0 -22
  277. package/src/screens/workflows/workflow-instructions.tsx +0 -134
  278. package/src/screens/workflows/workflow-list.tsx +0 -117
  279. package/src/setupTests.ts +0 -38
  280. package/src/system-apps/assistants.app.ts +0 -7
  281. package/src/system-apps/console-logs.app.ts +0 -7
  282. package/src/system-apps/data-explorer.app.ts +0 -8
  283. package/src/system-apps/identity.app.ts +0 -9
  284. package/src/system-apps/index.ts +0 -77
  285. package/src/system-apps/mobile-settings.app.ts +0 -7
  286. package/src/system-apps/network-viewer.app.ts +0 -8
  287. package/src/system-apps/packages.app.ts +0 -7
  288. package/src/system-apps/search.app.ts +0 -8
  289. package/src/system-apps/settings.app.ts +0 -7
  290. package/src/system-apps/threads.app.ts +0 -7
  291. package/src/system-apps/tools.app.ts +0 -7
  292. package/src/system-apps/types.app.ts +0 -7
  293. package/src/system-apps/variables.app.ts +0 -7
  294. package/src/system-apps/workflows.app.ts +0 -7
  295. package/src/tabs-layout/network-status-indicator.test.tsx +0 -799
  296. package/src/tabs-layout/network-status-indicator.tsx +0 -756
  297. package/src/tabs-layout/tabs-layout.tsx +0 -857
  298. package/src/tabs-layout/tabs-state.test.ts +0 -152
  299. package/src/tabs-layout/tabs-state.ts +0 -587
  300. package/src/tabs-layout/use-network-status.test.tsx +0 -58
  301. package/src/tabs-layout/use-network-status.ts +0 -68
  302. package/src/ui-defaults/index.ts +0 -2
  303. package/src/ui-defaults/list-screen.tsx +0 -94
  304. package/src/ui-defaults/markdown-field.tsx +0 -87
  305. package/src/ui-router/routes-loader.ts +0 -193
  306. package/src/ui-router/ui-loader.test.tsx +0 -108
  307. package/src/ui-router/ui-loader.tsx +0 -606
  308. package/src/utils.ts +0 -95
  309. package/tsconfig.json +0 -18
@@ -1,180 +0,0 @@
1
- import { useEffect, useRef, useState } from "react";
2
-
3
- /** Props for {@link QrScanner}. */
4
- export interface QrScannerProps {
5
- /** Called once with the decoded text of the first QR code seen; scanning stops after this. */
6
- onScan: (text: string) => void;
7
- /** Called when the camera cannot be opened. */
8
- onError?: (message: string) => void;
9
- /** Optional filter; return `false` to keep scanning past a code that is not ours. */
10
- accept?: (text: string) => boolean;
11
- /** Square edge length in CSS pixels. */
12
- size?: number;
13
- }
14
-
15
- type BarcodeDetectorLike = {
16
- detect(source: ImageBitmapSource): Promise<Array<{ rawValue: string }>>;
17
- };
18
-
19
- type BarcodeDetectorCtor = new (options?: { formats?: string[] }) => BarcodeDetectorLike;
20
-
21
- function nativeDetector(): BarcodeDetectorLike | undefined {
22
- const ctor = (globalThis as { BarcodeDetector?: BarcodeDetectorCtor }).BarcodeDetector;
23
- if (!ctor) {
24
- return undefined;
25
- }
26
- try {
27
- return new ctor({ formats: ["qr_code"] });
28
- } catch {
29
- return undefined;
30
- }
31
- }
32
-
33
- /** Whether this host can plausibly scan (secure context with a camera API). */
34
- export function canScanQr(): boolean {
35
- return (
36
- typeof navigator !== "undefined" &&
37
- Boolean(navigator.mediaDevices?.getUserMedia) &&
38
- (typeof window === "undefined" || window.isSecureContext !== false)
39
- );
40
- }
41
-
42
- /**
43
- * Live camera QR scanner. Uses the platform `BarcodeDetector` when available and falls
44
- * back to `jsqr` on a canvas. Stops the camera on unmount or after the first accepted code.
45
- */
46
- export function QrScanner(props: QrScannerProps) {
47
- const { onScan, onError, accept, size = 280 } = props;
48
- const videoRef = useRef<HTMLVideoElement>(null);
49
- const [status, setStatus] = useState<"starting" | "scanning" | "error">("starting");
50
-
51
- useEffect(() => {
52
- let cancelled = false;
53
- let stream: MediaStream | undefined;
54
- let frame = 0;
55
- const canvas = document.createElement("canvas");
56
- const detector = nativeDetector();
57
- let jsqr: typeof import("jsqr").default | undefined;
58
-
59
- const stop = () => {
60
- cancelled = true;
61
- if (frame) {
62
- cancelAnimationFrame(frame);
63
- }
64
- for (const track of stream?.getTracks() ?? []) {
65
- track.stop();
66
- }
67
- };
68
-
69
- const deliver = (text: string) => {
70
- if (cancelled || (accept && !accept(text))) {
71
- return false;
72
- }
73
- stop();
74
- onScan(text);
75
- return true;
76
- };
77
-
78
- const tick = async () => {
79
- const video = videoRef.current;
80
- if (cancelled || !video || video.readyState < 2) {
81
- frame = requestAnimationFrame(() => void tick());
82
- return;
83
- }
84
- try {
85
- if (detector) {
86
- const codes = await detector.detect(video);
87
- for (const code of codes) {
88
- if (deliver(code.rawValue)) {
89
- return;
90
- }
91
- }
92
- } else if (jsqr) {
93
- const width = video.videoWidth;
94
- const height = video.videoHeight;
95
- if (width && height) {
96
- canvas.width = width;
97
- canvas.height = height;
98
- const ctx = canvas.getContext("2d", { willReadFrequently: true });
99
- if (ctx) {
100
- ctx.drawImage(video, 0, 0, width, height);
101
- const image = ctx.getImageData(0, 0, width, height);
102
- const result = jsqr(image.data, width, height, { inversionAttempts: "dontInvert" });
103
- if (result?.data && deliver(result.data)) {
104
- return;
105
- }
106
- }
107
- }
108
- }
109
- } catch {
110
- // A single bad frame is not fatal; keep scanning.
111
- }
112
- if (!cancelled) {
113
- frame = requestAnimationFrame(() => void tick());
114
- }
115
- };
116
-
117
- (async () => {
118
- try {
119
- if (!detector) {
120
- jsqr = (await import("jsqr")).default;
121
- }
122
- stream = await navigator.mediaDevices.getUserMedia({
123
- video: { facingMode: { ideal: "environment" } },
124
- audio: false,
125
- });
126
- if (cancelled) {
127
- stop();
128
- return;
129
- }
130
- const video = videoRef.current;
131
- if (!video) {
132
- return;
133
- }
134
- video.srcObject = stream;
135
- await video.play();
136
- setStatus("scanning");
137
- frame = requestAnimationFrame(() => void tick());
138
- } catch (error) {
139
- if (cancelled) {
140
- return;
141
- }
142
- setStatus("error");
143
- onError?.(
144
- error instanceof DOMException && error.name === "NotAllowedError"
145
- ? "Camera access was denied."
146
- : "Could not open the camera.",
147
- );
148
- }
149
- })();
150
-
151
- return stop;
152
- }, [onScan, onError, accept]);
153
-
154
- return (
155
- <div
156
- className="position-relative bg-black rounded overflow-hidden mx-auto"
157
- style={{ width: size, height: size }}
158
- >
159
- <video
160
- ref={videoRef}
161
- muted
162
- playsInline
163
- style={{ width: "100%", height: "100%", objectFit: "cover" }}
164
- aria-label="Camera preview"
165
- />
166
- {status === "starting" ? (
167
- <output className="position-absolute top-50 start-50 translate-middle text-light small">
168
- Starting camera…
169
- </output>
170
- ) : null}
171
- {status === "scanning" ? (
172
- <div
173
- aria-hidden="true"
174
- className="position-absolute top-50 start-50 translate-middle border border-2 border-light rounded"
175
- style={{ width: "60%", height: "60%", opacity: 0.7 }}
176
- />
177
- ) : null}
178
- </div>
179
- );
180
- }
@@ -1,88 +0,0 @@
1
- import { render, screen } from "@testing-library/react";
2
- import userEvent from "@testing-library/user-event";
3
- import { ResponsiveSectionNav } from "./responsive-section-nav";
4
-
5
- jest.mock("../globals", () => {
6
- const { observable: createObservable } =
7
- jest.requireActual<typeof import("@peers-app/peers-sdk")>("@peers-app/peers-sdk");
8
- return { isDesktop: createObservable(true) };
9
- });
10
-
11
- import { isDesktop } from "../globals";
12
-
13
- const sections = [
14
- { id: "activity", label: "Activity", iconClassName: "bi-inbox", badge: 2 },
15
- { id: "people", label: "People", iconClassName: "bi-person" },
16
- { id: "groups", label: "Groups" },
17
- { id: "devices", label: "Devices" },
18
- ];
19
-
20
- describe("ResponsiveSectionNav", () => {
21
- beforeEach(() => {
22
- isDesktop(true);
23
- });
24
-
25
- it("uses a vertical sidebar on desktop and marks the selected section", () => {
26
- render(
27
- <ResponsiveSectionNav
28
- label="Identity"
29
- sections={sections}
30
- activeSectionId="people"
31
- onSelect={() => undefined}
32
- toolbar={<div>Quick actions</div>}
33
- >
34
- <div>People body</div>
35
- </ResponsiveSectionNav>,
36
- );
37
-
38
- expect(screen.getByRole("navigation", { name: "Identity" }).querySelector("ul")).toHaveClass(
39
- "nav-pills",
40
- "flex-column",
41
- );
42
- expect(screen.getByRole("button", { name: /People/ })).toHaveAttribute("aria-current", "page");
43
- expect(screen.getByText("People body")).toBeInTheDocument();
44
- expect(screen.getByText("Quick actions")).toBeInTheDocument();
45
- expect(screen.getByText("Quick actions").closest("[data-section-scroll-container]")).toBeNull();
46
- expect(screen.getByText("People body").closest("[data-section-scroll-container]")).toHaveClass(
47
- "overflow-auto",
48
- );
49
- });
50
-
51
- it("uses a non-wrapping horizontal strip on a narrow screen", () => {
52
- isDesktop(false);
53
- render(
54
- <ResponsiveSectionNav
55
- label="Identity"
56
- sections={sections}
57
- activeSectionId="activity"
58
- onSelect={() => undefined}
59
- >
60
- <div>Activity body</div>
61
- </ResponsiveSectionNav>,
62
- );
63
-
64
- expect(screen.getByRole("navigation", { name: "Identity" }).querySelector("ul")).toHaveClass(
65
- "nav-tabs",
66
- "flex-nowrap",
67
- "overflow-x-auto",
68
- "overflow-y-hidden",
69
- );
70
- });
71
-
72
- it("reports the section chosen from the keyboard-focusable button", async () => {
73
- const onSelect = jest.fn();
74
- render(
75
- <ResponsiveSectionNav
76
- label="Identity"
77
- sections={sections}
78
- activeSectionId="activity"
79
- onSelect={onSelect}
80
- >
81
- <div>Activity body</div>
82
- </ResponsiveSectionNav>,
83
- );
84
-
85
- await userEvent.click(screen.getByRole("button", { name: /Groups/ }));
86
- expect(onSelect).toHaveBeenCalledWith("groups");
87
- });
88
- });
@@ -1,92 +0,0 @@
1
- import type { ReactNode } from "react";
2
- import { isDesktop } from "../globals";
3
- import { useObservable } from "../hooks";
4
-
5
- /** One destination in {@link ResponsiveSectionNav}. */
6
- export interface ISectionNavItem {
7
- /** Stable id passed to {@link ResponsiveSectionNavProps.onSelect}. */
8
- id: string;
9
- /** Visible label. */
10
- label: string;
11
- /** Bootstrap icon class, without a leading markup wrapper. */
12
- iconClassName?: string;
13
- /** Optional count. Hidden when zero or omitted. */
14
- badge?: number;
15
- }
16
-
17
- /** Props for {@link ResponsiveSectionNav}. */
18
- export interface ResponsiveSectionNavProps {
19
- /** Accessible name for the section navigation. */
20
- label: string;
21
- /** Sections in display order. */
22
- sections: ISectionNavItem[];
23
- /** Id of the selected section. */
24
- activeSectionId: string;
25
- /** Called when a section button is activated. */
26
- onSelect: (sectionId: string) => void;
27
- /** Optional controls that remain visible above the scrolling section body. */
28
- toolbar?: ReactNode;
29
- /** Selected section body. */
30
- children: ReactNode;
31
- }
32
-
33
- /**
34
- * Section navigation that uses a left sidebar on a wide screen and a
35
- * non-wrapping horizontal strip on a narrow one.
36
- *
37
- * The parent owns the route. This component only reports the selected id.
38
- */
39
- export function ResponsiveSectionNav(props: ResponsiveSectionNavProps) {
40
- const [desktop] = useObservable(isDesktop);
41
- return (
42
- <div
43
- className={`d-flex h-100 overflow-hidden ${desktop ? "flex-row" : "flex-column"}`}
44
- style={{ minHeight: 0, minWidth: 0 }}
45
- >
46
- <nav
47
- aria-label={props.label}
48
- className={desktop ? "border-end p-3 overflow-auto" : "border-bottom px-2 pt-2 pb-1"}
49
- style={desktop ? { flex: "0 0 220px", width: 220 } : undefined}
50
- >
51
- <ul
52
- className={`nav ${desktop ? "nav-pills flex-column" : "nav-tabs flex-nowrap overflow-x-auto overflow-y-hidden"}`}
53
- >
54
- {props.sections.map((section) => {
55
- const selected = section.id === props.activeSectionId;
56
- return (
57
- <li className="nav-item" key={section.id}>
58
- <button
59
- type="button"
60
- className={`nav-link d-flex align-items-center gap-2 w-100 text-start ${selected ? "active" : ""}`}
61
- aria-current={selected ? "page" : undefined}
62
- onClick={() => props.onSelect(section.id)}
63
- >
64
- {section.iconClassName ? <i className={section.iconClassName} /> : null}
65
- <span>{section.label}</span>
66
- {section.badge ? (
67
- <span className={`badge ${selected ? "bg-light text-primary" : "bg-primary"}`}>
68
- {section.badge}
69
- </span>
70
- ) : null}
71
- </button>
72
- </li>
73
- );
74
- })}
75
- </ul>
76
- </nav>
77
- <div
78
- className="d-flex flex-column flex-grow-1 overflow-hidden"
79
- style={{ minHeight: 0, minWidth: 0 }}
80
- >
81
- {props.toolbar ? <div className="flex-shrink-0">{props.toolbar}</div> : null}
82
- <div
83
- className="flex-grow-1 overflow-auto"
84
- style={{ minHeight: 0, minWidth: 0 }}
85
- data-section-scroll-container
86
- >
87
- {props.children}
88
- </div>
89
- </div>
90
- </div>
91
- );
92
- }
@@ -1,175 +0,0 @@
1
- import * as globals from "../globals";
2
- import { useObservable } from "../hooks";
3
- import { AssistantDetails } from "../screens/assistants/assistant-details";
4
- import { AssistantList } from "../screens/assistants/assistant-list";
5
- import { canonicalizeRoute } from "../screens/identity/identity-routes";
6
- import { PackageDetails } from "../screens/packages/package-details";
7
- import { PackageList } from "../screens/packages/package-list";
8
- import { PackageNewLocal } from "../screens/packages/package-new-local";
9
- import { PeerTypeDetails } from "../screens/peer-types/peer-type-details";
10
- import { PeerTypeLists } from "../screens/peer-types/peer-type-list";
11
- import { GlobalSearch } from "../screens/search/global-search";
12
- import { SettingsPage } from "../screens/settings/settings-page";
13
- // import { TaskDetails } from "../screens/tasks/task-details";
14
- // import { TaskList } from "../screens/tasks/task-list";
15
- import { ToolDetails } from "../screens/tools/tool-details";
16
- import { ToolList } from "../screens/tools/tool-list";
17
- import { ToolTestDetails } from "../screens/tools/tool-test-details";
18
- import { VariableDetails } from "../screens/variables/variable-details";
19
- import { VariableList } from "../screens/variables/variable-list";
20
- import { WorkflowDetails } from "../screens/workflows/workflow-details";
21
- import { WorkflowList } from "../screens/workflows/workflow-list";
22
- import { UIRouter } from "../ui-router/ui-loader";
23
- import { ChannelMessages } from "./messages/channel-view";
24
- import { ThreadMessageList } from "./messages/thread-message-list";
25
- import "../screens/console-logs/console-logs-list";
26
- import "../screens/identity";
27
- import "../screens/network-viewer";
28
- import "../screens/data-explorer";
29
-
30
- /** Render the built-in or registered UI that owns a canonicalized route. */
31
- export function Router({ path: providedPath }: { path?: string } = {}) {
32
- // Hooks must run unconditionally (same order every render)
33
- const [globalPath] = useObservable(globals.mainContentPath);
34
-
35
- try {
36
- // Use provided path or fall back to global mainContentPath.
37
- // Legacy contacts, groups, devices, invites, and account paths render as Identity.
38
- let path = canonicalizeRoute(providedPath ?? globalPath);
39
- const originalPath = path;
40
- path = path.toLowerCase().trim();
41
- path = path.split("?")[0]; // remove query string
42
-
43
- // remove leading '/'
44
- if (path.startsWith("/")) {
45
- path = path.substring(1);
46
- }
47
- // remove trailing '/'
48
- if (path.endsWith("/")) {
49
- path = path.substring(0, path.length - 1);
50
- }
51
-
52
- if (path === "search") {
53
- return <GlobalSearch />;
54
- }
55
- if (path === "settings" || path === "profile") {
56
- return <SettingsPage />;
57
- }
58
- if (path === "threads" || path === "") {
59
- return <ChannelMessages />;
60
- }
61
-
62
- const ui = UIRouter({ path, props: {}, uiCategory: "screen" });
63
- if (ui) {
64
- return ui;
65
- }
66
-
67
- if (path.match(/^channel\//)) {
68
- const parts = path.split("/");
69
- parts.shift();
70
- const channelId = parts[0];
71
- // todo add additional params as props if they exist
72
- return <ChannelMessages channelId={channelId} />;
73
- }
74
-
75
- if (path === "assistants") {
76
- return <AssistantList />;
77
- }
78
- if (path.match(/^assistants\//)) {
79
- const parts = path.split("/");
80
- parts.shift();
81
- const id = parts[0];
82
- return <AssistantDetails assistantId={id} />;
83
- }
84
-
85
- // if (path === 'tasks') {
86
- // return <TaskList />;
87
- // }
88
- // if (path.match(/^tasks\//)) {
89
- // const parts = path.split('/');
90
- // parts.shift();
91
- // const taskId = parts[0];
92
- // return <TaskDetails taskId={taskId} />;
93
- // }
94
-
95
- if (path === "workflows") {
96
- return <WorkflowList />;
97
- }
98
- if (path.match(/^workflows\//)) {
99
- const parts = path.split("/");
100
- parts.shift();
101
- const workflowId = parts[0];
102
- return <WorkflowDetails workflowId={workflowId} />;
103
- }
104
-
105
- if (path === "tools") {
106
- return <ToolList />;
107
- }
108
- if (path.match(/^tools\/tests\//)) {
109
- const parts = path.split("/");
110
- parts.shift();
111
- parts.shift();
112
- const toolTestId = parts[0];
113
- return <ToolTestDetails toolTestId={toolTestId} />;
114
- }
115
- if (path.match(/^tools\//)) {
116
- const parts = path.split("/");
117
- parts.shift();
118
- const toolId = parts[0];
119
- return <ToolDetails toolId={toolId} />;
120
- }
121
-
122
- if (path === "packages") {
123
- return <PackageList />;
124
- }
125
- if (path.match(/^packages\//)) {
126
- const parts = path.split("/");
127
- parts.shift();
128
- const id = parts[0];
129
- if (id.toLowerCase() === "newlocal") {
130
- return <PackageNewLocal />;
131
- }
132
- return <PackageDetails packageId={id} />;
133
- }
134
-
135
- if (path === "variables") {
136
- return <VariableList />;
137
- }
138
- if (path.match(/^variables\//)) {
139
- const parts = path.split("/");
140
- parts.shift();
141
- const id = parts[0];
142
- return <VariableDetails persistentVarId={id} />;
143
- }
144
-
145
- if (path === "peer-types") {
146
- return <PeerTypeLists />;
147
- }
148
- if (path.match(/^peer-types\//)) {
149
- const parts = path.split("/");
150
- parts.shift();
151
- const id = parts[0];
152
- return <PeerTypeDetails peerTypeId={id} />;
153
- }
154
-
155
- if (path.match(/^threads\//)) {
156
- const parts = path.split("/");
157
- parts.shift();
158
- const threadId = parts[0];
159
- return <ThreadMessageList threadId={threadId} />;
160
- }
161
-
162
- throw new Error(`Unrecognized route: ${originalPath}`);
163
- } catch (err) {
164
- return (
165
- <div className="p-3">
166
- <a href="#threads">Home</a>
167
- <h3>Error loading route</h3>
168
- <pre>
169
- route: {`${globals.mainContentPath()}\n`}
170
- {String(err)}
171
- </pre>
172
- </div>
173
- );
174
- }
175
- }
@@ -1,134 +0,0 @@
1
- import type { IDoc } from "@peers-app/peers-sdk";
2
- import type React from "react";
3
- import { isDesktop, mainContentPath } from "../globals";
4
- import { useObservable } from "../hooks";
5
-
6
- /** Props for {@link SaveButton}. */
7
- export interface ISaveButtonProps {
8
- /** Used with many concrete table row shapes; IDoc stays generic at call sites. */
9
- doc: IDoc<any>;
10
- /** Save handler; defaults to `doc.save`. */
11
- onClick?: () => void | Promise<void>;
12
- /** Extra dropdown actions: buttons, or arbitrary nodes such as dividers. */
13
- addActions?: (
14
- | {
15
- label: string;
16
- onClick: () => void;
17
- className?: string;
18
- }
19
- | React.ReactNode
20
- )[];
21
- /** Hide the built-in Discard Changes and Delete actions. */
22
- noBuiltInActions?: boolean;
23
- /** Label for the built-in delete action. Default: `Delete`. */
24
- deleteLabel?: string;
25
- /**
26
- * Replace the built-in delete (`doc.delete()`), for records whose removal must go through
27
- * a Runtime contract. Runs after the user confirms; the screen then navigates up.
28
- */
29
- onDelete?: () => void | Promise<void>;
30
- }
31
-
32
- /**
33
- * Save button with a dropdown of extra actions plus built-in Discard Changes and Delete.
34
- * Enabled only while the document has unsaved changes.
35
- */
36
- export const SaveButton = (props: ISaveButtonProps) => {
37
- let { doc, addActions } = props;
38
- useObservable(doc.q);
39
- const q = doc.q;
40
-
41
- if (!addActions) {
42
- addActions = [];
43
- }
44
- const allActions: ISaveButtonProps["addActions"] = props.addActions ? [...addActions] : [];
45
-
46
- if (!props.noBuiltInActions) {
47
- if (q()) {
48
- allActions.unshift({
49
- label: "Discard Changes",
50
- className: "text-bg-warning",
51
- onClick: () => {
52
- if (confirm("Are you sure you want to discard your changes?")) {
53
- doc.load().then(() => {
54
- setTimeout(() => doc.q(0), 100);
55
- });
56
- }
57
- },
58
- });
59
- }
60
- if (allActions.length) {
61
- allActions.push(<hr className="dropdown-divider mt-3" />);
62
- }
63
- const deleteLabel = props.deleteLabel ?? "Delete";
64
- allActions.push({
65
- label: deleteLabel,
66
- className: `text-bg-danger`,
67
- onClick: async () => {
68
- if (confirm(`Are you sure you want to ${deleteLabel.toLowerCase()} this?`)) {
69
- try {
70
- await (props.onDelete ? props.onDelete() : doc.delete());
71
- } catch (err) {
72
- alert(`${deleteLabel} failed: ${err instanceof Error ? err.message : String(err)}`);
73
- return;
74
- }
75
- const pathParts = mainContentPath().split("/");
76
- pathParts.pop();
77
- const path = pathParts.join("/") ?? "";
78
- mainContentPath(path);
79
- }
80
- },
81
- });
82
- }
83
-
84
- let className = `btn `;
85
- if (q()) {
86
- className += "btn-primary";
87
- } else {
88
- className += "disabled";
89
- }
90
-
91
- if (!addActions?.length && props.noBuiltInActions) {
92
- return (
93
- <button className={className} onClick={props.onClick ?? doc.save} disabled={!q()}>
94
- {isDesktop() ? "Save Changes" : "Save"}
95
- </button>
96
- );
97
- }
98
-
99
- return (
100
- <div style={{ display: "inline-block" }}>
101
- <div className="input-group">
102
- <button className={className} onClick={props.onClick ?? doc.save} disabled={!q()}>
103
- {isDesktop() ? "Save Changes" : "Save"}
104
- </button>
105
- <button
106
- type="button"
107
- data-bs-toggle="dropdown"
108
- aria-expanded="false"
109
- // className={`btn dropdown-toggle dropdown-toggle-split ${q() ? 'btn-primary' : 'btn-secondary'}`}
110
- className={`btn dropdown-toggle dropdown-toggle-split btn-secondary`}
111
- >
112
- <span className="visually-hidden">Toggle Dropdown</span>
113
- </button>
114
- <ul className="dropdown-menu dropdown-menu-end">
115
- {allActions.map((action) => {
116
- if (typeof action !== "object" || action === null || !("label" in action)) {
117
- return <li key={Math.random()}>{action}</li>;
118
- }
119
- return (
120
- <li key={action.label}>
121
- <button
122
- className={`dropdown-item rounded mt-2 ${action.className || ""}`}
123
- onClick={action.onClick}
124
- >
125
- {action.label}
126
- </button>
127
- </li>
128
- );
129
- })}
130
- </ul>
131
- </div>
132
- </div>
133
- );
134
- };