@peers-app/peers-ui 0.27.3 → 0.27.5

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 (311) hide show
  1. package/dist/screens/network-viewer/group-details-modal.d.ts +4 -0
  2. package/dist/screens/network-viewer/group-details-modal.js +6 -46
  3. package/dist/screens/network-viewer/network-viewer-ipc.d.ts +3 -2
  4. package/dist/screens/network-viewer/network-viewer-ipc.js +6 -0
  5. package/dist/screens/network-viewer/sync-recovery.js +103 -1
  6. package/dist/screens/variables/variable-details.js +2 -1
  7. package/dist/tabs-layout/network-status-indicator.js +26 -12
  8. package/dist/tabs-layout/use-network-status.d.ts +6 -1
  9. package/dist/tabs-layout/use-network-status.js +27 -4
  10. package/package.json +9 -3
  11. package/.claude/settings.local.json +0 -34
  12. package/.github/README.md +0 -52
  13. package/.github/workflows/delete-all-artifacts.yml +0 -49
  14. package/.github/workflows/publish.yml +0 -51
  15. package/babel.config.js +0 -7
  16. package/biome.json +0 -198
  17. package/dist/app-updates/app-updates-consumer.test.d.ts +0 -1
  18. package/dist/app-updates/app-updates-consumer.test.js +0 -56
  19. package/dist/app-updates/app-updates-settings.test.d.ts +0 -1
  20. package/dist/app-updates/app-updates-settings.test.js +0 -93
  21. package/dist/app-updates/remote-app-update-panel.test.d.ts +0 -1
  22. package/dist/app-updates/remote-app-update-panel.test.js +0 -165
  23. package/dist/components/access-level-field.test.d.ts +0 -1
  24. package/dist/components/access-level-field.test.js +0 -28
  25. package/dist/components/message-logs/tool-access-approval-card.test.d.ts +0 -1
  26. package/dist/components/message-logs/tool-access-approval-card.test.js +0 -102
  27. package/dist/components/responsive-section-nav.test.d.ts +0 -1
  28. package/dist/components/responsive-section-nav.test.js +0 -45
  29. package/dist/globals-hash.test.d.ts +0 -1
  30. package/dist/globals-hash.test.js +0 -16
  31. package/dist/hooks/use-observable-state.test.d.ts +0 -1
  32. package/dist/hooks/use-observable-state.test.js +0 -64
  33. package/dist/hooks/use-observable.test.d.ts +0 -1
  34. package/dist/hooks/use-observable.test.js +0 -191
  35. package/dist/hooks/use-rows.test.d.ts +0 -1
  36. package/dist/hooks/use-rows.test.js +0 -175
  37. package/dist/hooks/use-tool.test.d.ts +0 -1
  38. package/dist/hooks/use-tool.test.js +0 -190
  39. package/dist/host-shell/host-shell-consumer.test.d.ts +0 -1
  40. package/dist/host-shell/host-shell-consumer.test.js +0 -28
  41. package/dist/root-layout/root-layout-host.test.d.ts +0 -1
  42. package/dist/root-layout/root-layout-host.test.js +0 -86
  43. package/dist/screens/account/account-screen.test.d.ts +0 -1
  44. package/dist/screens/account/account-screen.test.js +0 -51
  45. package/dist/screens/assistants/assistant-config.test.d.ts +0 -1
  46. package/dist/screens/assistants/assistant-config.test.js +0 -57
  47. package/dist/screens/contacts/contact-list.test.d.ts +0 -1
  48. package/dist/screens/contacts/contact-list.test.js +0 -48
  49. package/dist/screens/device-pairing/device-pairing.test.d.ts +0 -1
  50. package/dist/screens/device-pairing/device-pairing.test.js +0 -119
  51. package/dist/screens/devices/devices-list.test.d.ts +0 -1
  52. package/dist/screens/devices/devices-list.test.js +0 -53
  53. package/dist/screens/groups/group-list.test.d.ts +0 -1
  54. package/dist/screens/groups/group-list.test.js +0 -38
  55. package/dist/screens/identity/identity-routes.test.d.ts +0 -1
  56. package/dist/screens/identity/identity-routes.test.js +0 -51
  57. package/dist/screens/identity/identity-screen.test.d.ts +0 -1
  58. package/dist/screens/identity/identity-screen.test.js +0 -103
  59. package/dist/screens/invites/accept-invite.test.d.ts +0 -1
  60. package/dist/screens/invites/accept-invite.test.js +0 -103
  61. package/dist/screens/invites/activity-view.test.d.ts +0 -1
  62. package/dist/screens/invites/activity-view.test.js +0 -108
  63. package/dist/screens/invites/invite-classify.test.d.ts +0 -1
  64. package/dist/screens/invites/invite-classify.test.js +0 -115
  65. package/dist/screens/network-viewer/network-viewer-ipc.test.d.ts +0 -1
  66. package/dist/screens/network-viewer/network-viewer-ipc.test.js +0 -61
  67. package/dist/screens/network-viewer/network-viewer.section.test.d.ts +0 -1
  68. package/dist/screens/network-viewer/network-viewer.section.test.js +0 -63
  69. package/dist/screens/network-viewer/sync-recovery.test.d.ts +0 -1
  70. package/dist/screens/network-viewer/sync-recovery.test.js +0 -104
  71. package/dist/screens/packages/package-helpers.test.d.ts +0 -1
  72. package/dist/screens/packages/package-helpers.test.js +0 -53
  73. package/dist/screens/packages/package-runtime-status.test.d.ts +0 -1
  74. package/dist/screens/packages/package-runtime-status.test.js +0 -50
  75. package/dist/screens/packages/packages-ipc.test.d.ts +0 -1
  76. package/dist/screens/packages/packages-ipc.test.js +0 -43
  77. package/dist/screens/settings/settings-page.test.d.ts +0 -1
  78. package/dist/screens/settings/settings-page.test.js +0 -19
  79. package/dist/screens/tools/tool-access-ipc.test.d.ts +0 -1
  80. package/dist/screens/tools/tool-access-ipc.test.js +0 -40
  81. package/dist/screens/tools/tool-catalog-ipc.test.d.ts +0 -1
  82. package/dist/screens/tools/tool-catalog-ipc.test.js +0 -52
  83. package/dist/tabs-layout/network-status-indicator.test.d.ts +0 -1
  84. package/dist/tabs-layout/network-status-indicator.test.js +0 -665
  85. package/dist/tabs-layout/tabs-state.test.d.ts +0 -1
  86. package/dist/tabs-layout/tabs-state.test.js +0 -110
  87. package/dist/tabs-layout/use-network-status.test.d.ts +0 -1
  88. package/dist/tabs-layout/use-network-status.test.js +0 -45
  89. package/dist/ui-router/ui-loader.test.d.ts +0 -1
  90. package/dist/ui-router/ui-loader.test.js +0 -91
  91. package/jest.config.js +0 -25
  92. package/release-guard.js +0 -10
  93. package/src/app-updates/app-update-controls.tsx +0 -73
  94. package/src/app-updates/app-updates-consumer.test.ts +0 -83
  95. package/src/app-updates/app-updates-consumer.ts +0 -121
  96. package/src/app-updates/app-updates-settings.test.tsx +0 -109
  97. package/src/app-updates/app-updates-settings.tsx +0 -136
  98. package/src/app-updates/remote-app-update-panel.test.tsx +0 -205
  99. package/src/app-updates/remote-app-update-panel.tsx +0 -210
  100. package/src/app-updates/use-app-update-status.ts +0 -16
  101. package/src/command-palette/command-palette-ui.tsx +0 -564
  102. package/src/command-palette/command-palette.ts +0 -242
  103. package/src/components/access-level-field.test.tsx +0 -32
  104. package/src/components/access-level-field.tsx +0 -68
  105. package/src/components/checkbox.tsx +0 -25
  106. package/src/components/group-switcher.tsx +0 -526
  107. package/src/components/input-date.tsx +0 -29
  108. package/src/components/input-number.tsx +0 -83
  109. package/src/components/input.tsx +0 -25
  110. package/src/components/inverse-lazy-list.tsx +0 -107
  111. package/src/components/io-schema-values.tsx +0 -103
  112. package/src/components/io-schema.tsx +0 -259
  113. package/src/components/lazy-list.tsx +0 -115
  114. package/src/components/list-screen.tsx +0 -126
  115. package/src/components/loading-indicator.tsx +0 -7
  116. package/src/components/markdown-editor/autolink-plugin.tsx +0 -29
  117. package/src/components/markdown-editor/editor-inline.tsx +0 -9
  118. package/src/components/markdown-editor/editor.tsx +0 -158
  119. package/src/components/markdown-editor/markdown-plugin.tsx +0 -202
  120. package/src/components/markdown-editor/mention-node.ts +0 -204
  121. package/src/components/markdown-editor/mentions-plugin.tsx +0 -354
  122. package/src/components/markdown-editor/move-line-plugin.tsx +0 -186
  123. package/src/components/markdown-editor/select-line-boundary-plugin.tsx +0 -95
  124. package/src/components/markdown-editor/theme.ts +0 -46
  125. package/src/components/markdown-editor/toolbar.tsx +0 -269
  126. package/src/components/markdown-with-mentions.tsx +0 -198
  127. package/src/components/message-logs/message-logs.tsx +0 -523
  128. package/src/components/message-logs/tool-access-approval-card.test.tsx +0 -127
  129. package/src/components/message-logs/tool-access-approval-card.tsx +0 -178
  130. package/src/components/messages/avatar.tsx +0 -171
  131. package/src/components/messages/channel-message-list.tsx +0 -189
  132. package/src/components/messages/channel-view.tsx +0 -92
  133. package/src/components/messages/message-compose.tsx +0 -192
  134. package/src/components/messages/message-display.tsx +0 -231
  135. package/src/components/messages/thread-message-list.tsx +0 -135
  136. package/src/components/qr-scanner.tsx +0 -180
  137. package/src/components/responsive-section-nav.test.tsx +0 -88
  138. package/src/components/responsive-section-nav.tsx +0 -92
  139. package/src/components/router.tsx +0 -175
  140. package/src/components/save-button.tsx +0 -134
  141. package/src/components/sortable-list.tsx +0 -130
  142. package/src/components/tabs.tsx +0 -76
  143. package/src/components/tooltip.tsx +0 -43
  144. package/src/components/trust-level-badge.tsx +0 -44
  145. package/src/components/trust-level-dropdown.tsx +0 -136
  146. package/src/components/typeahead.tsx +0 -357
  147. package/src/globals-hash.test.ts +0 -19
  148. package/src/globals.tsx +0 -226
  149. package/src/hooks/index.ts +0 -8
  150. package/src/hooks/use-host-contract.ts +0 -75
  151. package/src/hooks/use-observable-state.test.tsx +0 -101
  152. package/src/hooks/use-observable-state.ts +0 -29
  153. package/src/hooks/use-observable.test.tsx +0 -258
  154. package/src/hooks/use-observable.ts +0 -67
  155. package/src/hooks/use-on-screen.ts +0 -24
  156. package/src/hooks/use-promise.ts +0 -34
  157. package/src/hooks/use-rows.test.tsx +0 -196
  158. package/src/hooks/use-rows.ts +0 -120
  159. package/src/hooks/use-subscription.ts +0 -24
  160. package/src/hooks/use-tool.test.tsx +0 -225
  161. package/src/hooks/use-tool.ts +0 -116
  162. package/src/host-shell/host-shell-consumer.test.ts +0 -32
  163. package/src/host-shell/host-shell-consumer.ts +0 -53
  164. package/src/index.tsx +0 -42
  165. package/src/mention-configs.ts +0 -185
  166. package/src/operator-console/operator-console-state.ts +0 -119
  167. package/src/operator-console/operator-console.tsx +0 -233
  168. package/src/peers-services-registration.ts +0 -6
  169. package/src/root-layout/global-overlays.ts +0 -50
  170. package/src/root-layout/layout-picker.tsx +0 -47
  171. package/src/root-layout/layouts/full-screen-layout.tsx +0 -144
  172. package/src/root-layout/layouts/tabs-with-console-layout.tsx +0 -260
  173. package/src/root-layout/root-layout-host.test.tsx +0 -108
  174. package/src/root-layout/root-layout-host.tsx +0 -147
  175. package/src/root-layout/root-layout-registry.ts +0 -51
  176. package/src/root-layout/root-layout-state.ts +0 -55
  177. package/src/root-layout/root-layout.ts +0 -32
  178. package/src/screens/account/account-screen.test.tsx +0 -65
  179. package/src/screens/account/account-screen.tsx +0 -942
  180. package/src/screens/assistants/assistant-config.test.tsx +0 -54
  181. package/src/screens/assistants/assistant-config.tsx +0 -121
  182. package/src/screens/assistants/assistant-details.tsx +0 -89
  183. package/src/screens/assistants/assistant-info.tsx +0 -66
  184. package/src/screens/assistants/assistant-list.tsx +0 -145
  185. package/src/screens/assistants/assistant-tools.tsx +0 -82
  186. package/src/screens/console-logs/console-logs-list.tsx +0 -374
  187. package/src/screens/console-logs/log-display.tsx +0 -137
  188. package/src/screens/console-logs/log-filters.tsx +0 -64
  189. package/src/screens/console-logs/mobile-log-card.tsx +0 -161
  190. package/src/screens/console-logs/resizable-table-header.tsx +0 -110
  191. package/src/screens/contacts/contact-details.tsx +0 -123
  192. package/src/screens/contacts/contact-list.test.tsx +0 -59
  193. package/src/screens/contacts/contact-list.tsx +0 -286
  194. package/src/screens/contacts/index.ts +0 -2
  195. package/src/screens/data-explorer/data-explorer.tsx +0 -633
  196. package/src/screens/data-explorer/index.ts +0 -6
  197. package/src/screens/data-explorer/query-executor.tsx +0 -520
  198. package/src/screens/device-pairing/device-pairing.test.tsx +0 -143
  199. package/src/screens/device-pairing/device-pairing.tsx +0 -461
  200. package/src/screens/device-pairing/identity-ipc.ts +0 -25
  201. package/src/screens/device-pairing/index.ts +0 -2
  202. package/src/screens/device-pairing/use-device-pairing-state.ts +0 -15
  203. package/src/screens/devices/devices-list.test.tsx +0 -68
  204. package/src/screens/devices/devices-list.tsx +0 -365
  205. package/src/screens/devices/index.ts +0 -1
  206. package/src/screens/groups/group-details.tsx +0 -160
  207. package/src/screens/groups/group-invite-panel.tsx +0 -311
  208. package/src/screens/groups/group-list.test.tsx +0 -44
  209. package/src/screens/groups/group-list.tsx +0 -317
  210. package/src/screens/groups/group-members.tsx +0 -343
  211. package/src/screens/groups/index.ts +0 -3
  212. package/src/screens/identity/identity-routes.test.ts +0 -70
  213. package/src/screens/identity/identity-routes.ts +0 -301
  214. package/src/screens/identity/identity-screen.test.tsx +0 -138
  215. package/src/screens/identity/identity-screen.tsx +0 -282
  216. package/src/screens/identity/index.ts +0 -2
  217. package/src/screens/identity/invite-contact-panel.tsx +0 -45
  218. package/src/screens/identity/profile-invite.tsx +0 -93
  219. package/src/screens/invites/accept-invite.test.tsx +0 -115
  220. package/src/screens/invites/accept-invite.tsx +0 -318
  221. package/src/screens/invites/activity-view.test.tsx +0 -127
  222. package/src/screens/invites/activity-view.tsx +0 -162
  223. package/src/screens/invites/index.ts +0 -6
  224. package/src/screens/invites/invite-capture.ts +0 -104
  225. package/src/screens/invites/invite-classify.test.ts +0 -129
  226. package/src/screens/invites/invite-share.tsx +0 -104
  227. package/src/screens/invites/invites-ipc.ts +0 -25
  228. package/src/screens/invites/pending-invites-list.tsx +0 -256
  229. package/src/screens/network-viewer/connection-troubleshooter.tsx +0 -395
  230. package/src/screens/network-viewer/cpu-usage-graph.tsx +0 -294
  231. package/src/screens/network-viewer/device-details-modal.tsx +0 -339
  232. package/src/screens/network-viewer/group-details-modal.tsx +0 -421
  233. package/src/screens/network-viewer/index.ts +0 -8
  234. package/src/screens/network-viewer/network-viewer-ipc.test.ts +0 -72
  235. package/src/screens/network-viewer/network-viewer-ipc.ts +0 -39
  236. package/src/screens/network-viewer/network-viewer-section.ts +0 -12
  237. package/src/screens/network-viewer/network-viewer.section.test.tsx +0 -68
  238. package/src/screens/network-viewer/network-viewer.tsx +0 -708
  239. package/src/screens/network-viewer/remote-logs-panel.tsx +0 -190
  240. package/src/screens/network-viewer/sync-recovery.test.tsx +0 -114
  241. package/src/screens/network-viewer/sync-recovery.tsx +0 -461
  242. package/src/screens/network-viewer/usage-graph.tsx +0 -455
  243. package/src/screens/packages/local-folder-grants-panel.tsx +0 -290
  244. package/src/screens/packages/local-folders-ipc.ts +0 -28
  245. package/src/screens/packages/package-details.tsx +0 -166
  246. package/src/screens/packages/package-helpers.test.ts +0 -57
  247. package/src/screens/packages/package-helpers.ts +0 -137
  248. package/src/screens/packages/package-info.tsx +0 -354
  249. package/src/screens/packages/package-list.tsx +0 -519
  250. package/src/screens/packages/package-network-panel.tsx +0 -82
  251. package/src/screens/packages/package-new-local.tsx +0 -100
  252. package/src/screens/packages/package-runtime-status.test.tsx +0 -58
  253. package/src/screens/packages/package-runtime-status.tsx +0 -68
  254. package/src/screens/packages/package-versions.tsx +0 -298
  255. package/src/screens/packages/packages-ipc.test.ts +0 -47
  256. package/src/screens/packages/packages-ipc.ts +0 -125
  257. package/src/screens/peer-types/peer-type-details.tsx +0 -172
  258. package/src/screens/peer-types/peer-type-list.tsx +0 -64
  259. package/src/screens/search/global-search.tsx +0 -406
  260. package/src/screens/settings/color-mode-dropdown.tsx +0 -82
  261. package/src/screens/settings/settings-page.test.tsx +0 -24
  262. package/src/screens/settings/settings-page.tsx +0 -426
  263. package/src/screens/setup-user.tsx +0 -355
  264. package/src/screens/tools/tool-access-ipc.test.ts +0 -55
  265. package/src/screens/tools/tool-access-ipc.ts +0 -27
  266. package/src/screens/tools/tool-catalog-ipc.test.ts +0 -52
  267. package/src/screens/tools/tool-catalog-ipc.ts +0 -51
  268. package/src/screens/tools/tool-details.tsx +0 -52
  269. package/src/screens/tools/tool-info.tsx +0 -45
  270. package/src/screens/tools/tool-list.tsx +0 -106
  271. package/src/screens/tools/tool-schema.tsx +0 -44
  272. package/src/screens/tools/tool-test-details.tsx +0 -89
  273. package/src/screens/tools/tool-test-list.tsx +0 -73
  274. package/src/screens/variables/variable-details.tsx +0 -289
  275. package/src/screens/variables/variable-list.tsx +0 -110
  276. package/src/screens/welcome-modal.tsx +0 -294
  277. package/src/screens/workflows/workflow-details.tsx +0 -115
  278. package/src/screens/workflows/workflow-info.tsx +0 -22
  279. package/src/screens/workflows/workflow-instructions.tsx +0 -134
  280. package/src/screens/workflows/workflow-list.tsx +0 -117
  281. package/src/setupTests.ts +0 -38
  282. package/src/system-apps/assistants.app.ts +0 -7
  283. package/src/system-apps/console-logs.app.ts +0 -7
  284. package/src/system-apps/data-explorer.app.ts +0 -8
  285. package/src/system-apps/identity.app.ts +0 -9
  286. package/src/system-apps/index.ts +0 -77
  287. package/src/system-apps/mobile-settings.app.ts +0 -7
  288. package/src/system-apps/network-viewer.app.ts +0 -8
  289. package/src/system-apps/packages.app.ts +0 -7
  290. package/src/system-apps/search.app.ts +0 -8
  291. package/src/system-apps/settings.app.ts +0 -7
  292. package/src/system-apps/threads.app.ts +0 -7
  293. package/src/system-apps/tools.app.ts +0 -7
  294. package/src/system-apps/types.app.ts +0 -7
  295. package/src/system-apps/variables.app.ts +0 -7
  296. package/src/system-apps/workflows.app.ts +0 -7
  297. package/src/tabs-layout/network-status-indicator.test.tsx +0 -799
  298. package/src/tabs-layout/network-status-indicator.tsx +0 -756
  299. package/src/tabs-layout/tabs-layout.tsx +0 -857
  300. package/src/tabs-layout/tabs-state.test.ts +0 -152
  301. package/src/tabs-layout/tabs-state.ts +0 -587
  302. package/src/tabs-layout/use-network-status.test.tsx +0 -58
  303. package/src/tabs-layout/use-network-status.ts +0 -68
  304. package/src/ui-defaults/index.ts +0 -2
  305. package/src/ui-defaults/list-screen.tsx +0 -94
  306. package/src/ui-defaults/markdown-field.tsx +0 -87
  307. package/src/ui-router/routes-loader.ts +0 -193
  308. package/src/ui-router/ui-loader.test.tsx +0 -108
  309. package/src/ui-router/ui-loader.tsx +0 -606
  310. package/src/utils.ts +0 -95
  311. 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
- };