@peers-app/peers-ui 0.14.0 → 0.15.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.
Files changed (438) hide show
  1. package/.github/workflows/publish.yml +8 -5
  2. package/babel.config.js +4 -4
  3. package/biome.json +191 -0
  4. package/dist/command-palette/command-palette-ui.d.ts +1 -2
  5. package/dist/command-palette/command-palette-ui.js +175 -244
  6. package/dist/command-palette/command-palette.js +65 -64
  7. package/dist/components/chat-overlay.d.ts +2 -2
  8. package/dist/components/chat-overlay.js +160 -217
  9. package/dist/components/checkbox.d.ts +5 -4
  10. package/dist/components/checkbox.js +4 -7
  11. package/dist/components/group-switcher.d.ts +1 -2
  12. package/dist/components/group-switcher.js +119 -159
  13. package/dist/components/input-date.d.ts +3 -3
  14. package/dist/components/input-date.js +6 -6
  15. package/dist/components/input-number.d.ts +7 -6
  16. package/dist/components/input-number.js +25 -20
  17. package/dist/components/input.d.ts +5 -4
  18. package/dist/components/input.js +4 -7
  19. package/dist/components/inverse-lazy-list.d.ts +3 -3
  20. package/dist/components/inverse-lazy-list.js +13 -47
  21. package/dist/components/io-schema-values.d.ts +5 -6
  22. package/dist/components/io-schema-values.js +28 -65
  23. package/dist/components/io-schema.d.ts +4 -5
  24. package/dist/components/io-schema.js +42 -79
  25. package/dist/components/lazy-list.d.ts +3 -3
  26. package/dist/components/lazy-list.js +38 -58
  27. package/dist/components/list-screen.d.ts +3 -8
  28. package/dist/components/list-screen.js +28 -23
  29. package/dist/components/loading-indicator.d.ts +1 -2
  30. package/dist/components/loading-indicator.js +2 -6
  31. package/dist/components/markdown-editor/autolink-plugin.js +5 -8
  32. package/dist/components/markdown-editor/editor-inline.d.ts +2 -3
  33. package/dist/components/markdown-editor/editor-inline.js +2 -6
  34. package/dist/components/markdown-editor/editor.d.ts +6 -6
  35. package/dist/components/markdown-editor/editor.js +9 -19
  36. package/dist/components/markdown-editor/markdown-plugin.d.ts +1 -1
  37. package/dist/components/markdown-editor/markdown-plugin.js +20 -21
  38. package/dist/components/markdown-editor/mention-node.d.ts +2 -2
  39. package/dist/components/markdown-editor/mention-node.js +24 -24
  40. package/dist/components/markdown-editor/mentions-plugin.d.ts +2 -2
  41. package/dist/components/markdown-editor/mentions-plugin.js +61 -62
  42. package/dist/components/markdown-editor/theme.js +28 -28
  43. package/dist/components/markdown-editor/toolbar.d.ts +2 -3
  44. package/dist/components/markdown-editor/toolbar.js +32 -49
  45. package/dist/components/markdown-with-mentions.d.ts +1 -1
  46. package/dist/components/markdown-with-mentions.js +43 -43
  47. package/dist/components/message-logs/message-logs.d.ts +1 -2
  48. package/dist/components/message-logs/message-logs.js +91 -116
  49. package/dist/components/messages/avatar.d.ts +3 -4
  50. package/dist/components/messages/avatar.js +37 -46
  51. package/dist/components/messages/channel-message-list.d.ts +5 -6
  52. package/dist/components/messages/channel-message-list.js +34 -34
  53. package/dist/components/messages/channel-view.d.ts +1 -2
  54. package/dist/components/messages/channel-view.js +23 -57
  55. package/dist/components/messages/message-compose.d.ts +3 -4
  56. package/dist/components/messages/message-compose.js +27 -38
  57. package/dist/components/messages/message-display.d.ts +2 -3
  58. package/dist/components/messages/message-display.js +42 -95
  59. package/dist/components/messages/thread-message-list.d.ts +4 -5
  60. package/dist/components/messages/thread-message-list.js +29 -29
  61. package/dist/components/router.d.ts +1 -2
  62. package/dist/components/router.js +58 -66
  63. package/dist/components/save-button.d.ts +3 -3
  64. package/dist/components/save-button.js +23 -33
  65. package/dist/components/sortable-list.d.ts +11 -12
  66. package/dist/components/sortable-list.js +42 -22
  67. package/dist/components/tabs.d.ts +3 -3
  68. package/dist/components/tabs.js +16 -47
  69. package/dist/components/tooltip.d.ts +4 -4
  70. package/dist/components/tooltip.js +4 -9
  71. package/dist/components/trust-level-badge.d.ts +2 -3
  72. package/dist/components/trust-level-badge.js +16 -16
  73. package/dist/components/trust-level-dropdown.d.ts +3 -4
  74. package/dist/components/trust-level-dropdown.js +32 -55
  75. package/dist/components/typeahead.d.ts +3 -3
  76. package/dist/components/typeahead.js +48 -89
  77. package/dist/components/voice-indicator.d.ts +2 -2
  78. package/dist/components/voice-indicator.js +93 -106
  79. package/dist/components/voice-subscribe-events.d.ts +32 -0
  80. package/dist/components/voice-subscribe-events.js +2 -0
  81. package/dist/globals.d.ts +3 -5
  82. package/dist/globals.js +22 -33
  83. package/dist/hooks.d.ts +5 -5
  84. package/dist/hooks.js +22 -12
  85. package/dist/hooks.test.js +129 -145
  86. package/dist/index.d.ts +9 -8
  87. package/dist/index.js +13 -12
  88. package/dist/mention-configs.d.ts +2 -2
  89. package/dist/mention-configs.js +55 -42
  90. package/dist/screens/assistants/assistant-config.d.ts +2 -3
  91. package/dist/screens/assistants/assistant-config.js +9 -22
  92. package/dist/screens/assistants/assistant-details.d.ts +1 -2
  93. package/dist/screens/assistants/assistant-details.js +13 -28
  94. package/dist/screens/assistants/assistant-info.d.ts +2 -3
  95. package/dist/screens/assistants/assistant-info.js +3 -17
  96. package/dist/screens/assistants/assistant-list.d.ts +1 -2
  97. package/dist/screens/assistants/assistant-list.js +15 -56
  98. package/dist/screens/assistants/assistant-tools.d.ts +2 -3
  99. package/dist/screens/assistants/assistant-tools.js +10 -24
  100. package/dist/screens/console-logs/console-logs-list.d.ts +1 -2
  101. package/dist/screens/console-logs/console-logs-list.js +130 -134
  102. package/dist/screens/console-logs/log-display.d.ts +2 -3
  103. package/dist/screens/console-logs/log-display.js +40 -42
  104. package/dist/screens/console-logs/log-filters.d.ts +1 -2
  105. package/dist/screens/console-logs/log-filters.js +2 -24
  106. package/dist/screens/console-logs/mobile-log-card.d.ts +2 -3
  107. package/dist/screens/console-logs/mobile-log-card.js +64 -67
  108. package/dist/screens/console-logs/resizable-table-header.d.ts +1 -2
  109. package/dist/screens/console-logs/resizable-table-header.js +31 -67
  110. package/dist/screens/contacts/contact-details.d.ts +1 -2
  111. package/dist/screens/contacts/contact-details.js +16 -46
  112. package/dist/screens/contacts/contact-list.d.ts +1 -2
  113. package/dist/screens/contacts/contact-list.js +44 -103
  114. package/dist/screens/contacts/index.d.ts +4 -4
  115. package/dist/screens/contacts/index.js +1 -1
  116. package/dist/screens/contacts/user-connect.d.ts +1 -2
  117. package/dist/screens/contacts/user-connect.js +85 -186
  118. package/dist/screens/data-explorer/data-explorer.d.ts +1 -2
  119. package/dist/screens/data-explorer/data-explorer.js +61 -181
  120. package/dist/screens/data-explorer/index.d.ts +2 -2
  121. package/dist/screens/data-explorer/query-executor.d.ts +1 -2
  122. package/dist/screens/data-explorer/query-executor.js +56 -166
  123. package/dist/screens/groups/group-details.d.ts +1 -2
  124. package/dist/screens/groups/group-details.js +27 -122
  125. package/dist/screens/groups/group-invite-listener.d.ts +2 -3
  126. package/dist/screens/groups/group-invite-listener.js +8 -104
  127. package/dist/screens/groups/group-list.d.ts +1 -2
  128. package/dist/screens/groups/group-list.js +56 -133
  129. package/dist/screens/groups/group-members.d.ts +2 -3
  130. package/dist/screens/groups/group-members.js +62 -132
  131. package/dist/screens/groups/index.d.ts +4 -4
  132. package/dist/screens/groups/index.js +1 -1
  133. package/dist/screens/join-group/index.d.ts +2 -2
  134. package/dist/screens/join-group/join-group.d.ts +1 -2
  135. package/dist/screens/join-group/join-group.js +9 -109
  136. package/dist/screens/network-viewer/connection-troubleshooter.d.ts +2 -3
  137. package/dist/screens/network-viewer/connection-troubleshooter.js +89 -193
  138. package/dist/screens/network-viewer/cpu-usage-graph.d.ts +1 -2
  139. package/dist/screens/network-viewer/cpu-usage-graph.js +60 -99
  140. package/dist/screens/network-viewer/device-details-modal.d.ts +1 -2
  141. package/dist/screens/network-viewer/device-details-modal.js +25 -177
  142. package/dist/screens/network-viewer/group-details-modal.d.ts +1 -2
  143. package/dist/screens/network-viewer/group-details-modal.js +31 -142
  144. package/dist/screens/network-viewer/index.d.ts +4 -4
  145. package/dist/screens/network-viewer/index.js +3 -3
  146. package/dist/screens/network-viewer/network-viewer-ipc.d.ts +22 -0
  147. package/dist/screens/network-viewer/network-viewer-ipc.js +6 -0
  148. package/dist/screens/network-viewer/network-viewer.d.ts +1 -2
  149. package/dist/screens/network-viewer/network-viewer.js +91 -296
  150. package/dist/screens/network-viewer/usage-graph.d.ts +1 -2
  151. package/dist/screens/network-viewer/usage-graph.js +78 -110
  152. package/dist/screens/packages/package-details.d.ts +1 -2
  153. package/dist/screens/packages/package-details.js +35 -41
  154. package/dist/screens/packages/package-info.d.ts +2 -2
  155. package/dist/screens/packages/package-info.js +33 -86
  156. package/dist/screens/packages/package-list.d.ts +1 -2
  157. package/dist/screens/packages/package-list.js +42 -106
  158. package/dist/screens/packages/package-new-local.d.ts +1 -2
  159. package/dist/screens/packages/package-new-local.js +13 -19
  160. package/dist/screens/packages/package-versions.d.ts +2 -3
  161. package/dist/screens/packages/package-versions.js +29 -96
  162. package/dist/screens/peer-types/peer-type-details.d.ts +3 -4
  163. package/dist/screens/peer-types/peer-type-details.js +26 -78
  164. package/dist/screens/peer-types/peer-type-list.d.ts +1 -2
  165. package/dist/screens/peer-types/peer-type-list.js +13 -24
  166. package/dist/screens/search/global-search.d.ts +1 -2
  167. package/dist/screens/search/global-search.js +104 -182
  168. package/dist/screens/settings/color-mode-dropdown.d.ts +3 -4
  169. package/dist/screens/settings/color-mode-dropdown.js +18 -37
  170. package/dist/screens/settings/settings-page.d.ts +1 -1
  171. package/dist/screens/settings/settings-page.js +86 -213
  172. package/dist/screens/settings/voice-settings-agent.d.ts +1 -1
  173. package/dist/screens/settings/voice-settings-agent.js +7 -44
  174. package/dist/screens/settings/voice-settings-api-keys.d.ts +2 -2
  175. package/dist/screens/settings/voice-settings-api-keys.js +2 -29
  176. package/dist/screens/settings/voice-settings-output.d.ts +2 -2
  177. package/dist/screens/settings/voice-settings-output.js +2 -40
  178. package/dist/screens/settings/voice-settings-providers.d.ts +2 -2
  179. package/dist/screens/settings/voice-settings-providers.js +2 -19
  180. package/dist/screens/settings/voice-settings-types.d.ts +4 -4
  181. package/dist/screens/settings/voice-settings-types.js +31 -31
  182. package/dist/screens/settings/voice-settings-wake-word.d.ts +2 -2
  183. package/dist/screens/settings/voice-settings-wake-word.js +2 -33
  184. package/dist/screens/settings/voice-settings.d.ts +1 -1
  185. package/dist/screens/settings/voice-settings.js +35 -112
  186. package/dist/screens/setup-user.d.ts +1 -2
  187. package/dist/screens/setup-user.js +38 -116
  188. package/dist/screens/tools/tool-code.d.ts +2 -3
  189. package/dist/screens/tools/tool-code.js +9 -13
  190. package/dist/screens/tools/tool-details.d.ts +1 -2
  191. package/dist/screens/tools/tool-details.js +26 -39
  192. package/dist/screens/tools/tool-info.d.ts +2 -3
  193. package/dist/screens/tools/tool-info.js +9 -48
  194. package/dist/screens/tools/tool-list.d.ts +1 -2
  195. package/dist/screens/tools/tool-list.js +33 -65
  196. package/dist/screens/tools/tool-schema.d.ts +2 -3
  197. package/dist/screens/tools/tool-schema.js +2 -13
  198. package/dist/screens/tools/tool-test-details.d.ts +1 -2
  199. package/dist/screens/tools/tool-test-details.js +12 -28
  200. package/dist/screens/tools/tool-test-list.d.ts +1 -2
  201. package/dist/screens/tools/tool-test-list.js +17 -56
  202. package/dist/screens/variables/variable-details.d.ts +1 -2
  203. package/dist/screens/variables/variable-details.js +19 -86
  204. package/dist/screens/variables/variable-list.d.ts +1 -2
  205. package/dist/screens/variables/variable-list.js +16 -27
  206. package/dist/screens/welcome-modal.d.ts +1 -2
  207. package/dist/screens/welcome-modal.js +44 -111
  208. package/dist/screens/workflows/workflow-details.d.ts +1 -2
  209. package/dist/screens/workflows/workflow-details.js +17 -31
  210. package/dist/screens/workflows/workflow-info.d.ts +2 -3
  211. package/dist/screens/workflows/workflow-info.js +2 -9
  212. package/dist/screens/workflows/workflow-instructions.d.ts +2 -3
  213. package/dist/screens/workflows/workflow-instructions.js +23 -55
  214. package/dist/screens/workflows/workflow-list.d.ts +1 -2
  215. package/dist/screens/workflows/workflow-list.js +23 -62
  216. package/dist/setupTests.d.ts +1 -1
  217. package/dist/setupTests.js +10 -11
  218. package/dist/system-apps/assistants.app.d.ts +1 -1
  219. package/dist/system-apps/assistants.app.js +3 -3
  220. package/dist/system-apps/console-logs.app.d.ts +1 -1
  221. package/dist/system-apps/console-logs.app.js +3 -3
  222. package/dist/system-apps/contacts.app.d.ts +1 -1
  223. package/dist/system-apps/contacts.app.js +4 -4
  224. package/dist/system-apps/data-explorer.app.d.ts +1 -1
  225. package/dist/system-apps/data-explorer.app.js +4 -4
  226. package/dist/system-apps/groups.app.d.ts +1 -1
  227. package/dist/system-apps/groups.app.js +4 -4
  228. package/dist/system-apps/index.d.ts +17 -17
  229. package/dist/system-apps/index.js +52 -52
  230. package/dist/system-apps/join-group.app.d.ts +1 -1
  231. package/dist/system-apps/join-group.app.js +4 -4
  232. package/dist/system-apps/mobile-settings.app.d.ts +1 -1
  233. package/dist/system-apps/mobile-settings.app.js +3 -3
  234. package/dist/system-apps/network-viewer.app.d.ts +1 -1
  235. package/dist/system-apps/network-viewer.app.js +4 -4
  236. package/dist/system-apps/packages.app.d.ts +1 -1
  237. package/dist/system-apps/packages.app.js +3 -3
  238. package/dist/system-apps/search.app.d.ts +1 -1
  239. package/dist/system-apps/search.app.js +4 -4
  240. package/dist/system-apps/settings.app.d.ts +1 -1
  241. package/dist/system-apps/settings.app.js +3 -3
  242. package/dist/system-apps/threads.app.d.ts +1 -1
  243. package/dist/system-apps/threads.app.js +3 -3
  244. package/dist/system-apps/tools.app.d.ts +1 -1
  245. package/dist/system-apps/tools.app.js +3 -3
  246. package/dist/system-apps/types.app.d.ts +1 -1
  247. package/dist/system-apps/types.app.js +3 -3
  248. package/dist/system-apps/variables.app.d.ts +1 -1
  249. package/dist/system-apps/variables.app.js +3 -3
  250. package/dist/system-apps/workflows.app.d.ts +1 -1
  251. package/dist/system-apps/workflows.app.js +3 -3
  252. package/dist/tabs-layout/tabs-layout.d.ts +2 -3
  253. package/dist/tabs-layout/tabs-layout.js +215 -246
  254. package/dist/tabs-layout/tabs-state.d.ts +2 -2
  255. package/dist/tabs-layout/tabs-state.js +73 -61
  256. package/dist/ui-defaults/index.d.ts +2 -2
  257. package/dist/ui-defaults/list-screen.d.ts +2 -3
  258. package/dist/ui-defaults/list-screen.js +33 -37
  259. package/dist/ui-defaults/markdown-field.js +24 -56
  260. package/dist/ui-router/routes-loader.d.ts +1 -1
  261. package/dist/ui-router/routes-loader.js +17 -13
  262. package/dist/ui-router/ui-loader.d.ts +6 -6
  263. package/dist/ui-router/ui-loader.js +172 -268
  264. package/dist/utils.js +49 -39
  265. package/jest.config.js +16 -16
  266. package/package.json +16 -14
  267. package/src/command-palette/command-palette-ui.tsx +261 -237
  268. package/src/command-palette/command-palette.ts +81 -78
  269. package/src/components/chat-overlay.tsx +366 -261
  270. package/src/components/checkbox.tsx +15 -12
  271. package/src/components/group-switcher.tsx +150 -105
  272. package/src/components/input-date.tsx +17 -16
  273. package/src/components/input-number.tsx +47 -31
  274. package/src/components/input.tsx +15 -12
  275. package/src/components/inverse-lazy-list.tsx +14 -13
  276. package/src/components/io-schema-values.tsx +51 -69
  277. package/src/components/io-schema.tsx +94 -69
  278. package/src/components/lazy-list.tsx +51 -34
  279. package/src/components/list-screen.tsx +51 -35
  280. package/src/components/loading-indicator.tsx +2 -4
  281. package/src/components/markdown-editor/autolink-plugin.tsx +4 -11
  282. package/src/components/markdown-editor/editor-inline.tsx +3 -4
  283. package/src/components/markdown-editor/editor.tsx +53 -51
  284. package/src/components/markdown-editor/markdown-plugin.tsx +48 -40
  285. package/src/components/markdown-editor/mention-node.ts +39 -38
  286. package/src/components/markdown-editor/mentions-plugin.tsx +99 -101
  287. package/src/components/markdown-editor/theme.ts +28 -29
  288. package/src/components/markdown-editor/toolbar.tsx +53 -47
  289. package/src/components/markdown-with-mentions.tsx +56 -46
  290. package/src/components/message-logs/message-logs.tsx +225 -165
  291. package/src/components/messages/avatar.tsx +70 -52
  292. package/src/components/messages/channel-message-list.tsx +80 -68
  293. package/src/components/messages/channel-view.tsx +34 -33
  294. package/src/components/messages/message-compose.tsx +84 -67
  295. package/src/components/messages/message-display.tsx +103 -89
  296. package/src/components/messages/thread-message-list.tsx +53 -44
  297. package/src/components/router.tsx +42 -43
  298. package/src/components/save-button.tsx +43 -39
  299. package/src/components/sortable-list.tsx +77 -49
  300. package/src/components/tabs.tsx +31 -31
  301. package/src/components/tooltip.tsx +21 -28
  302. package/src/components/trust-level-badge.tsx +15 -11
  303. package/src/components/trust-level-dropdown.tsx +49 -19
  304. package/src/components/typeahead.tsx +57 -59
  305. package/src/components/voice-indicator.tsx +158 -141
  306. package/src/components/voice-subscribe-events.ts +20 -0
  307. package/src/globals.tsx +42 -40
  308. package/src/hooks.test.tsx +141 -134
  309. package/src/hooks.ts +80 -48
  310. package/src/index.tsx +17 -10
  311. package/src/mention-configs.ts +122 -68
  312. package/src/screens/assistants/assistant-config.tsx +28 -18
  313. package/src/screens/assistants/assistant-details.tsx +35 -36
  314. package/src/screens/assistants/assistant-info.tsx +16 -11
  315. package/src/screens/assistants/assistant-list.tsx +37 -34
  316. package/src/screens/assistants/assistant-tools.tsx +41 -20
  317. package/src/screens/console-logs/console-logs-list.tsx +173 -140
  318. package/src/screens/console-logs/log-display.tsx +65 -38
  319. package/src/screens/console-logs/log-filters.tsx +4 -3
  320. package/src/screens/console-logs/mobile-log-card.tsx +78 -71
  321. package/src/screens/console-logs/resizable-table-header.tsx +29 -21
  322. package/src/screens/contacts/contact-details.tsx +29 -30
  323. package/src/screens/contacts/contact-list.tsx +71 -60
  324. package/src/screens/contacts/index.ts +5 -5
  325. package/src/screens/contacts/user-connect.tsx +177 -171
  326. package/src/screens/data-explorer/data-explorer.tsx +134 -98
  327. package/src/screens/data-explorer/index.ts +2 -3
  328. package/src/screens/data-explorer/query-executor.tsx +90 -80
  329. package/src/screens/groups/group-details.tsx +120 -101
  330. package/src/screens/groups/group-invite-listener.tsx +34 -37
  331. package/src/screens/groups/group-list.tsx +119 -103
  332. package/src/screens/groups/group-members.tsx +225 -164
  333. package/src/screens/groups/index.ts +5 -6
  334. package/src/screens/join-group/index.ts +2 -2
  335. package/src/screens/join-group/join-group.tsx +41 -39
  336. package/src/screens/network-viewer/connection-troubleshooter.tsx +145 -104
  337. package/src/screens/network-viewer/cpu-usage-graph.tsx +39 -43
  338. package/src/screens/network-viewer/device-details-modal.tsx +46 -59
  339. package/src/screens/network-viewer/group-details-modal.tsx +68 -49
  340. package/src/screens/network-viewer/index.ts +4 -5
  341. package/src/screens/network-viewer/network-viewer-ipc.ts +23 -0
  342. package/src/screens/network-viewer/network-viewer.tsx +261 -236
  343. package/src/screens/network-viewer/usage-graph.tsx +57 -49
  344. package/src/screens/packages/package-details.tsx +43 -35
  345. package/src/screens/packages/package-info.tsx +107 -66
  346. package/src/screens/packages/package-list.tsx +175 -98
  347. package/src/screens/packages/package-new-local.tsx +28 -26
  348. package/src/screens/packages/package-versions.tsx +102 -77
  349. package/src/screens/peer-types/peer-type-details.tsx +60 -50
  350. package/src/screens/peer-types/peer-type-list.tsx +20 -30
  351. package/src/screens/search/global-search.tsx +153 -137
  352. package/src/screens/settings/color-mode-dropdown.tsx +52 -35
  353. package/src/screens/settings/settings-page.tsx +215 -141
  354. package/src/screens/settings/voice-settings-agent.tsx +13 -12
  355. package/src/screens/settings/voice-settings-api-keys.tsx +14 -12
  356. package/src/screens/settings/voice-settings-output.tsx +12 -11
  357. package/src/screens/settings/voice-settings-providers.tsx +7 -3
  358. package/src/screens/settings/voice-settings-types.ts +52 -49
  359. package/src/screens/settings/voice-settings-wake-word.tsx +25 -9
  360. package/src/screens/settings/voice-settings.tsx +66 -43
  361. package/src/screens/setup-user.tsx +88 -41
  362. package/src/screens/tools/tool-code.tsx +12 -17
  363. package/src/screens/tools/tool-details.tsx +28 -28
  364. package/src/screens/tools/tool-info.tsx +14 -19
  365. package/src/screens/tools/tool-list.tsx +58 -40
  366. package/src/screens/tools/tool-schema.tsx +16 -9
  367. package/src/screens/tools/tool-test-details.tsx +11 -22
  368. package/src/screens/tools/tool-test-list.tsx +29 -30
  369. package/src/screens/variables/variable-details.tsx +63 -51
  370. package/src/screens/variables/variable-list.tsx +29 -30
  371. package/src/screens/welcome-modal.tsx +68 -48
  372. package/src/screens/workflows/workflow-details.tsx +40 -30
  373. package/src/screens/workflows/workflow-info.tsx +4 -11
  374. package/src/screens/workflows/workflow-instructions.tsx +35 -28
  375. package/src/screens/workflows/workflow-list.tsx +50 -40
  376. package/src/setupTests.ts +14 -13
  377. package/src/system-apps/assistants.app.ts +5 -5
  378. package/src/system-apps/console-logs.app.ts +4 -4
  379. package/src/system-apps/contacts.app.ts +6 -6
  380. package/src/system-apps/data-explorer.app.ts +5 -5
  381. package/src/system-apps/groups.app.ts +6 -6
  382. package/src/system-apps/index.ts +49 -49
  383. package/src/system-apps/join-group.app.ts +5 -5
  384. package/src/system-apps/mobile-settings.app.ts +4 -5
  385. package/src/system-apps/network-viewer.app.ts +5 -5
  386. package/src/system-apps/packages.app.ts +5 -5
  387. package/src/system-apps/search.app.ts +6 -6
  388. package/src/system-apps/settings.app.ts +5 -5
  389. package/src/system-apps/threads.app.ts +5 -5
  390. package/src/system-apps/tools.app.ts +5 -5
  391. package/src/system-apps/types.app.ts +5 -5
  392. package/src/system-apps/variables.app.ts +5 -5
  393. package/src/system-apps/workflows.app.ts +5 -5
  394. package/src/tabs-layout/tabs-layout.tsx +345 -254
  395. package/src/tabs-layout/tabs-state.ts +100 -81
  396. package/src/ui-defaults/index.ts +2 -3
  397. package/src/ui-defaults/list-screen.tsx +45 -40
  398. package/src/ui-defaults/markdown-field.tsx +22 -26
  399. package/src/ui-router/routes-loader.ts +40 -24
  400. package/src/ui-router/ui-loader.tsx +312 -214
  401. package/src/utils.ts +68 -81
  402. package/tsconfig.json +5 -10
  403. package/dist/components/input-datetime.d.ts +0 -7
  404. package/dist/components/input-datetime.js +0 -35
  405. package/dist/components/lazy-sortable-list.d.ts +0 -29
  406. package/dist/components/lazy-sortable-list.js +0 -12
  407. package/dist/components/left-bar.d.ts +0 -5
  408. package/dist/components/left-bar.js +0 -207
  409. package/dist/components/main-content-container.d.ts +0 -2
  410. package/dist/components/main-content-container.js +0 -92
  411. package/dist/components/messages/thread-view.d.ts +0 -6
  412. package/dist/components/messages/thread-view.js +0 -174
  413. package/dist/components/off-canvas.d.ts +0 -13
  414. package/dist/components/off-canvas.js +0 -89
  415. package/dist/components/text-list-editor.tsx/text-list-editor.d.ts +0 -6
  416. package/dist/components/text-list-editor.tsx/text-list-editor.js +0 -13
  417. package/dist/components/top-bar.d.ts +0 -2
  418. package/dist/components/top-bar.js +0 -51
  419. package/dist/components/typeahead/mentions-plugin.d.ts +0 -7
  420. package/dist/components/typeahead/mentions-plugin.js +0 -203
  421. package/dist/components/typeahead/typeahead-editor.d.ts +0 -15
  422. package/dist/components/typeahead/typeahead-editor.js +0 -134
  423. package/dist/components/typeahead/typeahead.d.ts +0 -12
  424. package/dist/components/typeahead/typeahead.js +0 -94
  425. package/dist/screens/profile.d.ts +0 -2
  426. package/dist/screens/profile.js +0 -76
  427. package/src/components/input-datetime.tsx +0 -41
  428. package/src/components/lazy-sortable-list.tsx +0 -51
  429. package/src/components/left-bar.tsx +0 -322
  430. package/src/components/main-content-container.tsx +0 -79
  431. package/src/components/messages/thread-view.tsx +0 -214
  432. package/src/components/off-canvas.tsx +0 -83
  433. package/src/components/text-list-editor.tsx/text-list-editor.tsx +0 -13
  434. package/src/components/top-bar.tsx +0 -119
  435. package/src/components/typeahead/mentions-plugin.tsx +0 -265
  436. package/src/components/typeahead/typeahead-editor.tsx +0 -140
  437. package/src/components/typeahead/typeahead.tsx +0 -77
  438. package/src/screens/profile.tsx +0 -75
@@ -1,18 +1,32 @@
1
- import { getUserContext, hasShownWelcomeModal, IAppNav, rpcServerCalls, setDefaultClientUserContext, Subscription } from "@peers-app/peers-sdk";
2
- import React, { Component, useEffect, useState } from 'react';
3
- import { openCommandPalette } from '../command-palette/command-palette';
4
- import { CommandPaletteOverlay } from '../command-palette/command-palette-ui';
5
- import { GroupSwitcher } from '../components/group-switcher';
6
- import { Router } from '../components/router';
7
- import { isDesktop, loadGlobals, me } from '../globals';
8
- import { useObservable, usePromise } from '../hooks';
9
- import { colorMode } from '../screens/settings/color-mode-dropdown';
10
- import { SetupUser } from '../screens/setup-user';
11
- import { WelcomeModal } from '../screens/welcome-modal';
12
- import { systemPackage } from '../system-apps';
13
- import { allPackages, loadAllRoutes } from '../ui-router/routes-loader';
14
- import { UIRouter } from '../ui-router/ui-loader';
15
- import { activeTabId, activeTabs, goToTabPath, initializedTabs, recentlyUsedApps, TabState } from './tabs-state';
1
+ import {
2
+ getUserContext,
3
+ hasShownWelcomeModal,
4
+ type IAppNav,
5
+ rpcServerCalls,
6
+ type Subscription,
7
+ setDefaultClientUserContext,
8
+ } from "@peers-app/peers-sdk";
9
+ import React, { Component, useEffect, useState } from "react";
10
+ import { openCommandPalette } from "../command-palette/command-palette";
11
+ import { CommandPaletteOverlay } from "../command-palette/command-palette-ui";
12
+ import { GroupSwitcher } from "../components/group-switcher";
13
+ import { Router } from "../components/router";
14
+ import { isDesktop, loadGlobals } from "../globals";
15
+ import { useObservable, usePromise } from "../hooks";
16
+ import { colorMode } from "../screens/settings/color-mode-dropdown";
17
+ import { SetupUser } from "../screens/setup-user";
18
+ import { WelcomeModal } from "../screens/welcome-modal";
19
+ import { systemPackage } from "../system-apps";
20
+ import { allPackages, loadAllRoutes } from "../ui-router/routes-loader";
21
+ import { UIRouter } from "../ui-router/ui-loader";
22
+ import {
23
+ activeTabId,
24
+ activeTabs,
25
+ goToTabPath,
26
+ initializedTabs,
27
+ recentlyUsedApps,
28
+ type TabState,
29
+ } from "./tabs-state";
16
30
 
17
31
  // Error boundary that wraps each tab's content so a crash in one tab
18
32
  // doesn't take down the entire app.
@@ -38,18 +52,30 @@ class TabErrorBoundary extends Component<TabErrorBoundaryProps, TabErrorBoundary
38
52
  }
39
53
 
40
54
  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
41
- console.error(`TabErrorBoundary caught error in tab "${this.props.tabTitle}":`, error, errorInfo);
55
+ console.error(
56
+ `TabErrorBoundary caught error in tab "${this.props.tabTitle}":`,
57
+ error,
58
+ errorInfo,
59
+ );
42
60
  }
43
61
 
44
62
  render() {
45
63
  if (this.state.hasError) {
46
64
  return (
47
- <div style={{ padding: '24px', textAlign: 'center' }}>
48
- <h4 style={{ marginBottom: '12px' }}>Something went wrong in "{this.props.tabTitle}"</h4>
49
- <p style={{ opacity: 0.7, marginBottom: '16px', fontFamily: 'monospace', fontSize: '13px' }}>
50
- {this.state.error?.message || 'Unknown error'}
65
+ <div style={{ padding: "24px", textAlign: "center" }}>
66
+ <h4 style={{ marginBottom: "12px" }}>Something went wrong in "{this.props.tabTitle}"</h4>
67
+ <p
68
+ style={{
69
+ opacity: 0.7,
70
+ marginBottom: "16px",
71
+ fontFamily: "monospace",
72
+ fontSize: "13px",
73
+ }}
74
+ >
75
+ {this.state.error?.message || "Unknown error"}
51
76
  </p>
52
77
  <button
78
+ type="button"
53
79
  className="btn btn-outline-primary btn-sm"
54
80
  onClick={() => this.setState({ hasError: false, error: null })}
55
81
  >
@@ -65,7 +91,7 @@ class TabErrorBoundary extends Component<TabErrorBoundaryProps, TabErrorBoundary
65
91
  export function TabsLayoutApp() {
66
92
  const userId = usePromise(async () => {
67
93
  const _userId = await rpcServerCalls.getUserId();
68
- return _userId || null as string | null;
94
+ return _userId || (null as string | null);
69
95
  });
70
96
 
71
97
  if (userId === undefined) {
@@ -73,7 +99,7 @@ export function TabsLayoutApp() {
73
99
  }
74
100
 
75
101
  if (!userId) {
76
- document.getElementById('appLoadingDiv')?.remove();
102
+ document.getElementById("appLoadingDiv")?.remove();
77
103
  return <SetupUser />;
78
104
  } else {
79
105
  return <TabsLayout userId={userId} />;
@@ -86,7 +112,7 @@ export function TabsLayout(props: { userId: string }) {
86
112
  setDefaultClientUserContext(props.userId);
87
113
  userContextInitialized = true;
88
114
  }
89
-
115
+
90
116
  const loaded = usePromise(async () => {
91
117
  await loadGlobals();
92
118
  await loadAllRoutes();
@@ -111,17 +137,14 @@ export function TabsLayout(props: { userId: string }) {
111
137
 
112
138
  if (!loaded) return false;
113
139
 
114
- document.getElementById('appLoadingDiv')?.remove();
140
+ document.getElementById("appLoadingDiv")?.remove();
115
141
 
116
142
  return (
117
143
  <>
118
144
  <TabsLayoutInternal />
119
145
  <CommandPaletteOverlay />
120
146
  {showWelcomeModal && (
121
- <WelcomeModal
122
- colorMode={_colorMode}
123
- onClose={() => setShowWelcomeModal(false)}
124
- />
147
+ <WelcomeModal colorMode={_colorMode} onClose={() => setShowWelcomeModal(false)} />
125
148
  )}
126
149
  </>
127
150
  );
@@ -131,14 +154,14 @@ function TabsLayoutInternal() {
131
154
  const [_colorMode] = useObservable(colorMode);
132
155
  const [tabs] = useObservable(activeTabs);
133
156
  const [activeTab] = useObservable(activeTabId);
134
- const [currentlyActiveGroupId, setCurrentlyActiveGroupId] = useState('');
157
+ const [currentlyActiveGroupId, setCurrentlyActiveGroupId] = useState("");
135
158
  const [switchingGroup, setSwitchingGroup] = useState(false);
136
-
159
+
137
160
  useEffect(() => {
138
161
  let sub: Subscription;
139
- getUserContext().then(userContext => {
140
- setCurrentlyActiveGroupId(userContext.currentlyActiveGroupId() || userContext.userId)
141
- sub = userContext.currentlyActiveGroupId.subscribe(async groupId => {
162
+ getUserContext().then((userContext) => {
163
+ setCurrentlyActiveGroupId(userContext.currentlyActiveGroupId() || userContext.userId);
164
+ sub = userContext.currentlyActiveGroupId.subscribe(async (_groupId) => {
142
165
  setCurrentlyActiveGroupId(userContext.currentlyActiveGroupId() || userContext.userId);
143
166
  setSwitchingGroup(true);
144
167
  await userContext.waitForActiveGroupPersistence();
@@ -152,24 +175,24 @@ function TabsLayoutInternal() {
152
175
  }
153
176
  };
154
177
  }, []);
155
-
178
+
156
179
  useObservable(isDesktop);
157
180
  const isMobile = !isDesktop();
158
181
 
159
182
  const closeTab = (tabId: string) => {
160
183
  const currentTabs = activeTabs();
161
- const tabToClose = currentTabs.find(t => t.tabId === tabId);
184
+ const tabToClose = currentTabs.find((t) => t.tabId === tabId);
162
185
 
163
186
  if (!tabToClose || tabToClose.tabId === "launcher") return;
164
187
 
165
- const closedIndex = currentTabs.findIndex(t => t.tabId === tabId);
166
- const newTabs = currentTabs.filter(t => t.tabId !== tabId);
188
+ const closedIndex = currentTabs.findIndex((t) => t.tabId === tabId);
189
+ const newTabs = currentTabs.filter((t) => t.tabId !== tabId);
167
190
  activeTabs(newTabs);
168
191
  initializedTabs.delete(tabId);
169
192
 
170
193
  if (activeTab === tabId) {
171
194
  if (newTabs.length === 0) {
172
- activeTabId('launcher');
195
+ activeTabId("launcher");
173
196
  } else if (closedIndex < newTabs.length) {
174
197
  activeTabId(newTabs[closedIndex].tabId);
175
198
  } else {
@@ -185,25 +208,35 @@ function TabsLayoutInternal() {
185
208
  return (
186
209
  <div
187
210
  key={currentlyActiveGroupId}
188
- className="d-flex flex-column" style={{ height: 'calc(100vh - 25px)', overflow: 'hidden' }}>
211
+ className="d-flex flex-column"
212
+ style={{ height: "calc(100vh - 25px)", overflow: "hidden" }}
213
+ >
189
214
  {switchingGroup && (
190
- <div style={{
191
- position: 'fixed', inset: 0, zIndex: 9999,
192
- display: 'flex', alignItems: 'center', justifyContent: 'center',
193
- backgroundColor: _colorMode === 'light' ? 'rgba(255,255,255,0.85)' : 'rgba(0,0,0,0.85)',
194
- }}>
215
+ <div
216
+ style={{
217
+ position: "fixed",
218
+ inset: 0,
219
+ zIndex: 9999,
220
+ display: "flex",
221
+ alignItems: "center",
222
+ justifyContent: "center",
223
+ backgroundColor: _colorMode === "light" ? "rgba(255,255,255,0.85)" : "rgba(0,0,0,0.85)",
224
+ }}
225
+ >
195
226
  <div className="text-center">
196
227
  <div className="spinner-border text-secondary mb-3" role="status" />
197
- <div className={`small ${_colorMode === 'light' ? 'text-muted' : 'text-secondary'}`}>Switching group...</div>
228
+ <div className={`small ${_colorMode === "light" ? "text-muted" : "text-secondary"}`}>
229
+ Switching group...
230
+ </div>
198
231
  </div>
199
232
  </div>
200
233
  )}
201
234
  {/* Tabs Header */}
202
235
  <div
203
- className={`border-bottom ${_colorMode === 'light' ? 'bg-light' : 'bg-dark'}`}
236
+ className={`border-bottom ${_colorMode === "light" ? "bg-light" : "bg-dark"}`}
204
237
  style={{
205
- borderBottomWidth: '1px',
206
- borderBottomColor: _colorMode === 'light' ? '#dee2e6' : '#495057'
238
+ borderBottomWidth: "1px",
239
+ borderBottomColor: _colorMode === "light" ? "#dee2e6" : "#495057",
207
240
  }}
208
241
  >
209
242
  {isMobile ? (
@@ -215,43 +248,45 @@ function TabsLayoutInternal() {
215
248
  colorMode={_colorMode}
216
249
  />
217
250
  ) : (
218
- <div className="d-flex align-items-center px-1" style={{ height: '36px' }}>
251
+ <div className="d-flex align-items-center px-1" style={{ height: "36px" }}>
219
252
  {/* Group Switcher */}
220
253
  <GroupSwitcher colorMode={_colorMode} isMobile={false} />
221
-
254
+
222
255
  {/* Search Button */}
223
256
  <button
257
+ type="button"
224
258
  className="btn btn-sm me-2 d-flex align-items-center"
225
259
  onClick={openCommandPalette}
226
260
  title="Search everything (Cmd+K)"
227
261
  style={{
228
- padding: '4px 8px',
229
- fontSize: '12px',
230
- borderRadius: '6px',
231
- border: 'none',
232
- background: 'transparent',
233
- color: _colorMode === 'light' ? '#6c757d' : '#adb5bd'
262
+ padding: "4px 8px",
263
+ fontSize: "12px",
264
+ borderRadius: "6px",
265
+ border: "none",
266
+ background: "transparent",
267
+ color: _colorMode === "light" ? "#6c757d" : "#adb5bd",
234
268
  }}
235
269
  onMouseEnter={(e) => {
236
- e.currentTarget.style.backgroundColor = _colorMode === 'light' ? '#f8f9fa' : '#495057';
237
- e.currentTarget.style.color = _colorMode === 'light' ? '#0d6efd' : '#ffffff';
270
+ e.currentTarget.style.backgroundColor =
271
+ _colorMode === "light" ? "#f8f9fa" : "#495057";
272
+ e.currentTarget.style.color = _colorMode === "light" ? "#0d6efd" : "#ffffff";
238
273
  }}
239
274
  onMouseLeave={(e) => {
240
- e.currentTarget.style.backgroundColor = 'transparent';
241
- e.currentTarget.style.color = _colorMode === 'light' ? '#6c757d' : '#adb5bd';
275
+ e.currentTarget.style.backgroundColor = "transparent";
276
+ e.currentTarget.style.color = _colorMode === "light" ? "#6c757d" : "#adb5bd";
242
277
  }}
243
278
  >
244
- <i className="bi-search" style={{ fontSize: '14px' }} />
279
+ <i className="bi-search" style={{ fontSize: "14px" }} />
245
280
  </button>
246
-
281
+
247
282
  <div
248
283
  className="d-flex flex-grow-1 overflow-auto"
249
284
  style={{
250
- scrollbarWidth: 'none',
251
- msOverflowStyle: 'none'
285
+ scrollbarWidth: "none",
286
+ msOverflowStyle: "none",
252
287
  }}
253
288
  >
254
- {tabs.map(tab => (
289
+ {tabs.map((tab) => (
255
290
  <TabHeader
256
291
  key={tab.tabId}
257
292
  tab={tab}
@@ -268,11 +303,11 @@ function TabsLayoutInternal() {
268
303
 
269
304
  {/* Tab Content */}
270
305
  <div className="flex-grow-1 overflow-hidden">
271
- {tabs.map(tab => (
306
+ {tabs.map((tab) => (
272
307
  <div
273
308
  key={tab.tabId}
274
- className={`h-100 ${activeTab === tab.tabId ? 'd-block' : 'd-none'}`}
275
- style={{ display: activeTab === tab.tabId ? 'block' : 'none' }}
309
+ className={`h-100 ${activeTab === tab.tabId ? "d-block" : "d-none"}`}
310
+ style={{ display: activeTab === tab.tabId ? "block" : "none" }}
276
311
  >
277
312
  <TabErrorBoundary tabTitle={tab.title} tabId={tab.tabId}>
278
313
  <TabContent tab={tab} isMobile={isMobile} isActive={activeTab === tab.tabId} />
@@ -292,112 +327,131 @@ interface MobileTabsHeaderProps {
292
327
  colorMode: string;
293
328
  }
294
329
 
295
- function MobileTabsHeader({ tabs, activeTab, onSwitch, onClose, colorMode }: MobileTabsHeaderProps) {
330
+ function MobileTabsHeader({
331
+ tabs,
332
+ activeTab,
333
+ onSwitch,
334
+ onClose,
335
+ colorMode,
336
+ }: MobileTabsHeaderProps) {
296
337
  const [showMenuDropdown, setShowMenuDropdown] = React.useState(false);
297
- const currentTab = tabs.find(t => t.tabId === activeTab);
298
- const nonLauncherTabs = tabs.filter(t => t.packageId !== 'launcher');
338
+ const currentTab = tabs.find((t) => t.tabId === activeTab);
339
+ const nonLauncherTabs = tabs.filter((t) => t.packageId !== "launcher");
299
340
 
300
341
  return (
301
- <div className="d-flex align-items-center justify-content-between px-2 position-relative" style={{ height: '36px' }}>
342
+ <div
343
+ className="d-flex align-items-center justify-content-between px-2 position-relative"
344
+ style={{ height: "36px" }}
345
+ >
302
346
  {/* Left Side - Group Switcher */}
303
347
  <div className="d-flex align-items-center gap-1">
304
348
  <GroupSwitcher colorMode={colorMode} isMobile={true} />
305
349
  </div>
306
350
 
307
351
  {/* Center - Current Tab Display (absolutely positioned to center on screen) */}
308
- <div
352
+ <div
309
353
  className="d-flex align-items-center justify-content-center position-absolute"
310
- style={{
311
- left: '50%',
312
- transform: 'translateX(-50%)',
313
- pointerEvents: 'none',
314
- maxWidth: 'calc(100% - 140px)'
354
+ style={{
355
+ left: "50%",
356
+ transform: "translateX(-50%)",
357
+ pointerEvents: "none",
358
+ maxWidth: "calc(100% - 140px)",
315
359
  }}
316
360
  >
317
- {currentTab?.iconClassName && currentTab.packageId !== 'launcher' && (
361
+ {currentTab?.iconClassName && currentTab.packageId !== "launcher" && (
318
362
  <i className={`${currentTab.iconClassName} me-2`} />
319
363
  )}
320
- <span className="fw-medium text-truncate small">
321
- {currentTab?.title || 'Apps'}
322
- </span>
364
+ <span className="fw-medium text-truncate small">{currentTab?.title || "Apps"}</span>
323
365
  </div>
324
366
 
325
367
  {/* Right Side - Hamburger Menu */}
326
368
  <div className="d-flex align-items-center">
327
369
  <div className="dropdown">
328
370
  <button
329
- className={`btn btn-sm ${colorMode === 'light' ? 'btn-outline-dark' : 'btn-outline-light'}`}
371
+ type="button"
372
+ className={`btn btn-sm ${colorMode === "light" ? "btn-outline-dark" : "btn-outline-light"}`}
330
373
  onClick={() => setShowMenuDropdown(!showMenuDropdown)}
331
- style={{ minWidth: '36px' }}
374
+ style={{ minWidth: "36px" }}
332
375
  >
333
376
  <i className="bi-list" />
334
- {nonLauncherTabs.length > 0 && (
335
- <span className="ms-1">{nonLauncherTabs.length}</span>
336
- )}
377
+ {nonLauncherTabs.length > 0 && <span className="ms-1">{nonLauncherTabs.length}</span>}
337
378
  </button>
338
379
  {showMenuDropdown && (
339
380
  <div
340
- className={`dropdown-menu show position-absolute ${colorMode === 'light' ? '' : 'dropdown-menu-dark'}`}
341
- style={{ right: 0, top: '100%', zIndex: 1000, minWidth: '280px', maxHeight: 'calc(100vh - 60px)', overflowY: 'auto' }}
381
+ className={`dropdown-menu show position-absolute ${colorMode === "light" ? "" : "dropdown-menu-dark"}`}
382
+ style={{
383
+ right: 0,
384
+ top: "100%",
385
+ zIndex: 1000,
386
+ minWidth: "280px",
387
+ maxHeight: "calc(100vh - 60px)",
388
+ overflowY: "auto",
389
+ }}
342
390
  >
343
391
  {/* Apps Option */}
344
392
  <div
345
- className={`dropdown-item d-flex align-items-center ${activeTab === 'launcher' ? 'active' : ''}`}
346
- style={{ cursor: 'pointer', padding: '14px 18px', fontSize: '17px' }}
393
+ className={`dropdown-item d-flex align-items-center ${activeTab === "launcher" ? "active" : ""}`}
394
+ style={{ cursor: "pointer", padding: "14px 18px", fontSize: "17px" }}
347
395
  onClick={() => {
348
- onSwitch('launcher');
396
+ onSwitch("launcher");
349
397
  setShowMenuDropdown(false);
350
398
  }}
351
399
  >
352
- <i className="bi-grid-3x3-gap me-3" style={{ fontSize: '20px' }} />
400
+ <i className="bi-grid-3x3-gap me-3" style={{ fontSize: "20px" }} />
353
401
  <span>Apps</span>
354
402
  </div>
355
-
403
+
356
404
  {/* Search Option */}
357
405
  <div
358
406
  className="dropdown-item d-flex align-items-center"
359
- style={{ cursor: 'pointer', padding: '14px 18px', fontSize: '17px' }}
407
+ style={{ cursor: "pointer", padding: "14px 18px", fontSize: "17px" }}
360
408
  onClick={() => {
361
409
  openCommandPalette();
362
410
  setShowMenuDropdown(false);
363
411
  }}
364
412
  >
365
- <i className="bi-search me-3" style={{ fontSize: '20px' }} />
413
+ <i className="bi-search me-3" style={{ fontSize: "20px" }} />
366
414
  <span>Search</span>
367
415
  </div>
368
-
416
+
369
417
  {/* Divider if there are open tabs */}
370
418
  {nonLauncherTabs.length > 0 && (
371
- <div className="dropdown-divider" style={{ margin: '8px 0' }} />
419
+ <div className="dropdown-divider" style={{ margin: "8px 0" }} />
372
420
  )}
373
-
421
+
374
422
  {/* Open Tabs */}
375
- {nonLauncherTabs.slice().reverse().map(tab => (
376
- <div
377
- key={tab.tabId}
378
- className={`dropdown-item d-flex align-items-center justify-content-between ${activeTab === tab.tabId ? 'active' : ''}`}
379
- style={{ cursor: 'pointer', padding: '14px 18px', fontSize: '17px' }}
380
- onClick={() => {
381
- onSwitch(tab.tabId);
382
- setShowMenuDropdown(false);
383
- }}
384
- >
385
- <div className="d-flex align-items-center">
386
- {tab.iconClassName && <i className={`${tab.iconClassName} me-3`} style={{ fontSize: '20px' }} />}
387
- <span>{tab.title}</span>
388
- </div>
389
- <button
390
- className="btn btn-sm p-0 ms-3"
391
- style={{ width: '32px', height: '32px', fontSize: '20px' }}
392
- onClick={(e) => {
393
- e.stopPropagation();
394
- onClose(tab.tabId);
423
+ {nonLauncherTabs
424
+ .slice()
425
+ .reverse()
426
+ .map((tab) => (
427
+ <div
428
+ key={tab.tabId}
429
+ className={`dropdown-item d-flex align-items-center justify-content-between ${activeTab === tab.tabId ? "active" : ""}`}
430
+ style={{ cursor: "pointer", padding: "14px 18px", fontSize: "17px" }}
431
+ onClick={() => {
432
+ onSwitch(tab.tabId);
433
+ setShowMenuDropdown(false);
395
434
  }}
396
435
  >
397
- <i className="bi-x" />
398
- </button>
399
- </div>
400
- ))}
436
+ <div className="d-flex align-items-center">
437
+ {tab.iconClassName && (
438
+ <i className={`${tab.iconClassName} me-3`} style={{ fontSize: "20px" }} />
439
+ )}
440
+ <span>{tab.title}</span>
441
+ </div>
442
+ <button
443
+ type="button"
444
+ className="btn btn-sm p-0 ms-3"
445
+ style={{ width: "32px", height: "32px", fontSize: "20px" }}
446
+ onClick={(e) => {
447
+ e.stopPropagation();
448
+ onClose(tab.tabId);
449
+ }}
450
+ >
451
+ <i className="bi-x" />
452
+ </button>
453
+ </div>
454
+ ))}
401
455
  </div>
402
456
  )}
403
457
  </div>
@@ -425,59 +479,70 @@ interface TabHeaderProps {
425
479
 
426
480
  function TabHeader({ tab, isActive, onSwitch, onClose, colorMode }: TabHeaderProps) {
427
481
  const activeClass = isActive
428
- ? (colorMode === 'light' ? 'bg-white' : 'bg-secondary')
429
- : (colorMode === 'light' ? 'bg-light' : 'bg-dark');
482
+ ? colorMode === "light"
483
+ ? "bg-white"
484
+ : "bg-secondary"
485
+ : colorMode === "light"
486
+ ? "bg-light"
487
+ : "bg-dark";
430
488
 
431
489
  return (
432
490
  <div
433
491
  className={`d-flex align-items-center px-2 py-1 user-select-none ${activeClass}`}
434
492
  style={{
435
- cursor: 'pointer',
436
- minWidth: '100px',
437
- maxWidth: '180px',
438
- height: '34px',
439
- fontSize: '13px',
440
- borderTop: isActive ? '2px solid #0d6efd' : '2px solid transparent',
441
- borderLeft: '1px solid transparent',
442
- borderRight: '1px solid ' + (colorMode === 'light' ? '#dee2e6' : '#495057'),
443
- borderBottom: isActive ? (colorMode === 'light' ? '1px solid white' : '1px solid #343a40') : 'none',
444
- marginBottom: isActive ? '-1px' : '0',
445
- borderRadius: isActive ? '6px 6px 0 0' : '0',
446
- transition: 'all 0.15s ease',
493
+ cursor: "pointer",
494
+ minWidth: "100px",
495
+ maxWidth: "180px",
496
+ height: "34px",
497
+ fontSize: "13px",
498
+ borderTop: isActive ? "2px solid #0d6efd" : "2px solid transparent",
499
+ borderLeft: "1px solid transparent",
500
+ borderRight: `1px solid ${colorMode === "light" ? "#dee2e6" : "#495057"}`,
501
+ borderBottom: isActive
502
+ ? colorMode === "light"
503
+ ? "1px solid white"
504
+ : "1px solid #343a40"
505
+ : "none",
506
+ marginBottom: isActive ? "-1px" : "0",
507
+ borderRadius: isActive ? "6px 6px 0 0" : "0",
508
+ transition: "all 0.15s ease",
447
509
  zIndex: isActive ? 1 : 0,
448
- boxShadow: isActive ? '0 2px 4px rgba(0,0,0,0.1)' : 'none'
510
+ boxShadow: isActive ? "0 2px 4px rgba(0,0,0,0.1)" : "none",
449
511
  }}
450
512
  onMouseEnter={(e) => {
451
513
  if (!isActive) {
452
- e.currentTarget.style.backgroundColor = colorMode === 'light' ? '#f8f9fa' : '#495057';
514
+ e.currentTarget.style.backgroundColor = colorMode === "light" ? "#f8f9fa" : "#495057";
453
515
  }
454
516
  }}
455
517
  onMouseLeave={(e) => {
456
518
  if (!isActive) {
457
- e.currentTarget.style.backgroundColor = colorMode === 'light' ? '#f1f3f4' : '#343a40';
519
+ e.currentTarget.style.backgroundColor = colorMode === "light" ? "#f1f3f4" : "#343a40";
458
520
  }
459
521
  }}
460
522
  onClick={() => onSwitch(tab.tabId)}
461
523
  >
462
- {tab.iconClassName && (
463
- <i className={`${tab.iconClassName} me-2`} />
464
- )}
465
- <span className="text-truncate" style={{ maxWidth: '120px', fontSize: '12px' }}>
524
+ {tab.iconClassName && <i className={`${tab.iconClassName} me-2`} />}
525
+ <span className="text-truncate" style={{ maxWidth: "120px", fontSize: "12px" }}>
466
526
  {tab.title}
467
527
  </span>
468
528
  {tab.tabId !== "launcher" && (
469
529
  <button
530
+ type="button"
470
531
  className="btn p-0 ms-1 opacity-75 hover-opacity-100"
471
532
  style={{
472
- width: '14px',
473
- height: '14px',
474
- fontSize: '11px',
475
- border: 'none',
476
- background: 'none',
477
- borderRadius: '2px'
533
+ width: "14px",
534
+ height: "14px",
535
+ fontSize: "11px",
536
+ border: "none",
537
+ background: "none",
538
+ borderRadius: "2px",
539
+ }}
540
+ onMouseEnter={(e) => {
541
+ e.currentTarget.style.backgroundColor = "rgba(0,0,0,0.1)";
542
+ }}
543
+ onMouseLeave={(e) => {
544
+ e.currentTarget.style.backgroundColor = "transparent";
478
545
  }}
479
- onMouseEnter={(e) => e.currentTarget.style.backgroundColor = 'rgba(0,0,0,0.1)'}
480
- onMouseLeave={(e) => e.currentTarget.style.backgroundColor = 'transparent'}
481
546
  onClick={(e) => {
482
547
  e.stopPropagation();
483
548
  onClose(tab.tabId);
@@ -499,7 +564,7 @@ interface TabContentProps {
499
564
  function TabContent({ tab, isMobile, isActive }: TabContentProps) {
500
565
  // Only render content if this tab is active OR has been previously initialized
501
566
  const shouldRender = isActive || initializedTabs.has(tab.tabId);
502
-
567
+
503
568
  // Mark tab as initialized when it becomes active for the first time
504
569
  React.useEffect(() => {
505
570
  if (isActive) {
@@ -515,30 +580,35 @@ function TabContent({ tab, isMobile, isActive }: TabContentProps) {
515
580
  }
516
581
 
517
582
  // Create individual tab content components that don't re-render
518
- const TabContentRenderer = React.memo(({ tab, isMobile }: { tab: TabState, isMobile: boolean }) => {
519
- if (tab.packageId === 'launcher') {
520
- return <AppLauncherTab isMobile={isMobile} />;
521
- }
583
+ const TabContentRenderer = React.memo(
584
+ ({ tab, isMobile }: { tab: TabState; isMobile: boolean }) => {
585
+ if (tab.packageId === "launcher") {
586
+ return <AppLauncherTab isMobile={isMobile} />;
587
+ }
522
588
 
523
- // System apps use the main Router (which reads from mainContentPath)
524
- // Package apps use UIRouter with explicit path
525
- const isSystemApp = tab.packageId === 'system-apps';
589
+ // System apps use the main Router (which reads from mainContentPath)
590
+ // Package apps use UIRouter with explicit path
591
+ const isSystemApp = tab.packageId === "system-apps";
526
592
 
527
- return (
528
- <div className={`h-100 overflow-auto ${isMobile ? 'p-2' : 'p-3'}`}>
529
- {isSystemApp ? (
530
- <Router path={tab.path} />
531
- ) : (
532
- <UIRouter path={tab.path} uiCategory="screen" props={{}} />
533
- )}
534
- </div>
535
- );
536
- }, (prevProps, nextProps) => {
537
- // Only re-render if the tab or isMobile changed
538
- return prevProps.tab.tabId === nextProps.tab.tabId &&
539
- prevProps.tab.path === nextProps.tab.path &&
540
- prevProps.isMobile === nextProps.isMobile;
541
- });
593
+ return (
594
+ <div className={`h-100 overflow-auto ${isMobile ? "p-2" : "p-3"}`}>
595
+ {isSystemApp ? (
596
+ <Router path={tab.path} />
597
+ ) : (
598
+ <UIRouter path={tab.path} uiCategory="screen" props={{}} />
599
+ )}
600
+ </div>
601
+ );
602
+ },
603
+ (prevProps, nextProps) => {
604
+ // Only re-render if the tab or isMobile changed
605
+ return (
606
+ prevProps.tab.tabId === nextProps.tab.tabId &&
607
+ prevProps.tab.path === nextProps.tab.path &&
608
+ prevProps.isMobile === nextProps.isMobile
609
+ );
610
+ },
611
+ );
542
612
 
543
613
  interface AppLauncherTabProps {
544
614
  isMobile: boolean;
@@ -558,26 +628,28 @@ interface AppItem {
558
628
  function AppLauncherTab({ isMobile }: AppLauncherTabProps) {
559
629
  const [packages] = useObservable(allPackages);
560
630
  const [recentApps] = useObservable(recentlyUsedApps);
561
- const [searchTerm, setSearchTerm] = React.useState('');
631
+ const [searchTerm, setSearchTerm] = React.useState("");
562
632
 
563
633
  // Combine regular packages with system apps package
564
634
  const allPackages_ = [...packages, systemPackage];
565
635
 
566
636
  // Create app items from nav items in packages
567
637
  const allApps: AppItem[] = allPackages_
568
- .filter(p => !p.disabled && p.appNavs && p.appNavs.length > 0)
569
- .flatMap(pkg =>
570
- pkg.appNavs!.map(navItem => {
638
+ .filter((p) => !p.disabled && p.appNavs && p.appNavs.length > 0)
639
+ .flatMap((pkg) => {
640
+ const navs = pkg.appNavs;
641
+ if (!navs) return [];
642
+ return navs.map((navItem) => {
571
643
  // Construct path - use direct path for system apps, package-nav for others
572
644
  let path: string;
573
- if (pkg.packageId === 'system-apps') {
645
+ if (pkg.packageId === "system-apps") {
574
646
  // System apps use direct routing (like the original left bar)
575
- path = navItem.navigationPath ?? navItem.name.replace(/\s/g, '-').toLowerCase();
647
+ path = navItem.navigationPath ?? navItem.name.replace(/\s/g, "-").toLowerCase();
576
648
  } else {
577
649
  // Regular packages use package-nav format
578
- path = `package-nav/${pkg.packageId}/${(navItem.navigationPath ?? navItem.name).replace(/[^a-zA-Z0-9]/g, '-').toLowerCase()}`;
579
- while (path.includes('//')) {
580
- path = path.replace('//', '/');
650
+ path = `package-nav/${pkg.packageId}/${(navItem.navigationPath ?? navItem.name).replace(/[^a-zA-Z0-9]/g, "-").toLowerCase()}`;
651
+ while (path.includes("//")) {
652
+ path = path.replace("//", "/");
581
653
  }
582
654
  }
583
655
 
@@ -588,42 +660,45 @@ function AppLauncherTab({ isMobile }: AppLauncherTabProps) {
588
660
  path,
589
661
  name: navItem.name,
590
662
  displayName: navItem.displayName || navItem.name,
591
- iconClassName: navItem.iconClassName || 'bi-box-seam'
663
+ iconClassName: navItem.iconClassName || "bi-box-seam",
592
664
  };
593
- })
594
- );
665
+ });
666
+ });
595
667
 
596
668
  // Filter apps based on search term
597
669
  const filteredApps = searchTerm.trim()
598
- ? allApps.filter(app =>
599
- app.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
600
- app.displayName.toLowerCase().includes(searchTerm.toLowerCase())
601
- )
670
+ ? allApps.filter(
671
+ (app) =>
672
+ app.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
673
+ app.displayName.toLowerCase().includes(searchTerm.toLowerCase()),
674
+ )
602
675
  : allApps;
603
676
 
604
677
  // Categorize filtered apps by package ID
605
- const systemApps = filteredApps.filter(app => app.packageId === 'system-apps');
606
- const userApps = filteredApps.filter(app => app.packageId !== 'system-apps');
678
+ const systemApps = filteredApps.filter((app) => app.packageId === "system-apps");
679
+ const userApps = filteredApps.filter((app) => app.packageId !== "system-apps");
607
680
 
608
681
  // Get recently used apps by matching paths (only show if they match search)
609
682
  const recentAppItems = recentApps
610
- .map(path => filteredApps.find(app => app.path === path))
683
+ .map((path) => filteredApps.find((app) => app.path === path))
611
684
  .filter(Boolean) as AppItem[];
612
685
 
613
686
  const openApp = (appItem: AppItem) => {
614
687
  // Check if this is the mobile-settings app and we're in React Native
615
- if (appItem.path === 'mobile-settings' && typeof (window as any).__NATIVE_THEME !== 'undefined') {
616
- // Use expo-linking to navigate to native screen
617
- // @ts-ignore
618
- if (window.ReactNativeWebView) {
619
- // @ts-ignore
620
- window.ReactNativeWebView.postMessage(JSON.stringify({
621
- type: 'navigate',
622
- path: 'mobile-settings'
623
- }));
688
+ const w = window as Window & {
689
+ __NATIVE_THEME?: unknown;
690
+ ReactNativeWebView?: { postMessage: (message: string) => void };
691
+ };
692
+ if (appItem.path === "mobile-settings" && typeof w.__NATIVE_THEME !== "undefined") {
693
+ if (w.ReactNativeWebView) {
694
+ w.ReactNativeWebView.postMessage(
695
+ JSON.stringify({
696
+ type: "navigate",
697
+ path: "mobile-settings",
698
+ }),
699
+ );
624
700
  } else {
625
- // Fallback to deep link
626
- window.location.href = 'peers://mobile-settings';
701
+ window.location.href = "peers://mobile-settings";
627
702
  }
628
703
  return;
629
704
  }
@@ -631,17 +706,23 @@ function AppLauncherTab({ isMobile }: AppLauncherTabProps) {
631
706
  };
632
707
 
633
708
  return (
634
- <div className={`container-fluid ${isMobile ? 'p-2' : 'p-4'}`} style={{ maxHeight: '100%', overflowY: 'auto' }}>
709
+ <div
710
+ className={`container-fluid ${isMobile ? "p-2" : "p-4"}`}
711
+ style={{ maxHeight: "100%", overflowY: "auto" }}
712
+ >
635
713
  {/* Search Bar */}
636
- <div className={isMobile ? 'mb-3' : 'mb-4'}>
714
+ <div className={isMobile ? "mb-3" : "mb-4"}>
637
715
  <div className="position-relative">
638
- <i className="bi-search position-absolute" style={{
639
- left: '12px',
640
- top: '50%',
641
- transform: 'translateY(-50%)',
642
- color: '#6c757d',
643
- zIndex: 1
644
- }} />
716
+ <i
717
+ className="bi-search position-absolute"
718
+ style={{
719
+ left: "12px",
720
+ top: "50%",
721
+ transform: "translateY(-50%)",
722
+ color: "#6c757d",
723
+ zIndex: 1,
724
+ }}
725
+ />
645
726
  <input
646
727
  type="text"
647
728
  className="form-control"
@@ -649,22 +730,23 @@ function AppLauncherTab({ isMobile }: AppLauncherTabProps) {
649
730
  value={searchTerm}
650
731
  onChange={(e) => setSearchTerm(e.target.value)}
651
732
  style={{
652
- paddingLeft: '40px',
653
- fontSize: isMobile ? '14px' : '15px'
733
+ paddingLeft: "40px",
734
+ fontSize: isMobile ? "14px" : "15px",
654
735
  }}
655
736
  />
656
737
  {searchTerm && (
657
738
  <button
739
+ type="button"
658
740
  className="btn btn-sm position-absolute"
659
741
  style={{
660
- right: '8px',
661
- top: '50%',
662
- transform: 'translateY(-50%)',
663
- border: 'none',
664
- background: 'none',
665
- color: '#6c757d'
742
+ right: "8px",
743
+ top: "50%",
744
+ transform: "translateY(-50%)",
745
+ border: "none",
746
+ background: "none",
747
+ color: "#6c757d",
666
748
  }}
667
- onClick={() => setSearchTerm('')}
749
+ onClick={() => setSearchTerm("")}
668
750
  >
669
751
  <i className="bi-x" />
670
752
  </button>
@@ -718,14 +800,22 @@ interface AppSectionProps {
718
800
 
719
801
  function AppSection({ title, iconClassName, apps, onOpenApp, isMobile }: AppSectionProps) {
720
802
  return (
721
- <div className='mb-5'>
722
- <h4 className={`${isMobile ? 'mb-3 h5' : 'mb-3'}`}>
803
+ <div className="mb-5">
804
+ <h4 className={`${isMobile ? "mb-3 h5" : "mb-3"}`}>
723
805
  <i className={`${iconClassName} me-2`} />
724
806
  {title}
725
807
  </h4>
726
- <div className={`d-flex flex-wrap ${isMobile ? 'gap-3' : 'gap-4'}`} style={{ gap: isMobile ? '16px' : '20px' }}>
727
- {apps.map(app => (
728
- <AppCard key={`${app.packageId}-${app.path}`} appItem={app} onOpenApp={onOpenApp} isMobile={isMobile} />
808
+ <div
809
+ className={`d-flex flex-wrap ${isMobile ? "gap-3" : "gap-4"}`}
810
+ style={{ gap: isMobile ? "16px" : "20px" }}
811
+ >
812
+ {apps.map((app) => (
813
+ <AppCard
814
+ key={`${app.packageId}-${app.path}`}
815
+ appItem={app}
816
+ onOpenApp={onOpenApp}
817
+ isMobile={isMobile}
818
+ />
729
819
  ))}
730
820
  </div>
731
821
  </div>
@@ -741,63 +831,64 @@ interface AppCardProps {
741
831
  function AppCard({ appItem, onOpenApp, isMobile }: AppCardProps) {
742
832
  const [_colorMode] = useObservable(colorMode);
743
833
 
744
- const isDark = _colorMode === 'dark';
834
+ const isDark = _colorMode === "dark";
745
835
 
746
836
  return (
747
837
  <button
838
+ type="button"
748
839
  className="d-flex flex-column align-items-center text-center"
749
840
  style={{
750
- cursor: 'pointer',
751
- width: isMobile ? '80px' : '90px',
752
- transition: 'all 0.15s ease',
753
- background: 'none',
754
- border: 'none',
841
+ cursor: "pointer",
842
+ width: isMobile ? "80px" : "90px",
843
+ transition: "all 0.15s ease",
844
+ background: "none",
845
+ border: "none",
755
846
  padding: 0,
756
847
  }}
757
848
  title={appItem.name}
758
849
  onMouseEnter={(e) => {
759
- e.currentTarget.style.transform = 'scale(1.05)';
850
+ e.currentTarget.style.transform = "scale(1.05)";
760
851
  }}
761
852
  onMouseLeave={(e) => {
762
- e.currentTarget.style.transform = 'scale(1)';
853
+ e.currentTarget.style.transform = "scale(1)";
763
854
  }}
764
855
  onClick={() => onOpenApp(appItem)}
765
856
  >
766
857
  <div
767
858
  className="d-flex align-items-center justify-content-center mb-2"
768
859
  style={{
769
- width: isMobile ? '64px' : '72px',
770
- height: isMobile ? '64px' : '72px',
771
- backgroundColor: isDark ? '#343a40' : '#f8f9fa',
772
- borderRadius: '12px',
773
- border: `1px solid ${isDark ? '#495057' : '#e9ecef'}`,
774
- boxShadow: isDark ? '0 2px 4px rgba(0,0,0,0.3)' : '0 2px 4px rgba(0,0,0,0.08)',
775
- transition: 'all 0.15s ease'
860
+ width: isMobile ? "64px" : "72px",
861
+ height: isMobile ? "64px" : "72px",
862
+ backgroundColor: isDark ? "#343a40" : "#f8f9fa",
863
+ borderRadius: "12px",
864
+ border: `1px solid ${isDark ? "#495057" : "#e9ecef"}`,
865
+ boxShadow: isDark ? "0 2px 4px rgba(0,0,0,0.3)" : "0 2px 4px rgba(0,0,0,0.08)",
866
+ transition: "all 0.15s ease",
776
867
  }}
777
868
  >
778
869
  <i
779
870
  className={appItem.iconClassName}
780
871
  style={{
781
- fontSize: isMobile ? '28px' : '32px',
782
- color: isDark ? '#0d6efd' : '#0d6efd'
872
+ fontSize: isMobile ? "28px" : "32px",
873
+ color: isDark ? "#0d6efd" : "#0d6efd",
783
874
  }}
784
875
  />
785
876
  </div>
786
877
 
787
878
  <span
788
- className={isDark ? 'text-light' : 'text-dark'}
879
+ className={isDark ? "text-light" : "text-dark"}
789
880
  style={{
790
- fontSize: isMobile ? '11px' : '12px',
791
- fontWeight: '500',
792
- lineHeight: '1.2',
793
- maxWidth: '100%',
794
- overflow: 'hidden',
795
- textOverflow: 'ellipsis',
796
- whiteSpace: 'nowrap'
881
+ fontSize: isMobile ? "11px" : "12px",
882
+ fontWeight: "500",
883
+ lineHeight: "1.2",
884
+ maxWidth: "100%",
885
+ overflow: "hidden",
886
+ textOverflow: "ellipsis",
887
+ whiteSpace: "nowrap",
797
888
  }}
798
889
  >
799
890
  {appItem.displayName}
800
891
  </span>
801
892
  </button>
802
893
  );
803
- }
894
+ }