@peers-app/peers-ui 0.0.14

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 (404) hide show
  1. package/.github/README.md +52 -0
  2. package/.github/workflows/publish.yml +45 -0
  3. package/babel.config.js +7 -0
  4. package/dist/app.d.ts +9 -0
  5. package/dist/app.js +54 -0
  6. package/dist/command-palette/command-palette-ui.d.ts +2 -0
  7. package/dist/command-palette/command-palette-ui.js +192 -0
  8. package/dist/command-palette/command-palette.d.ts +23 -0
  9. package/dist/command-palette/command-palette.js +371 -0
  10. package/dist/components/checkbox.d.ts +7 -0
  11. package/dist/components/checkbox.js +20 -0
  12. package/dist/components/group-switcher.d.ts +6 -0
  13. package/dist/components/group-switcher.js +301 -0
  14. package/dist/components/input-date.d.ts +7 -0
  15. package/dist/components/input-date.js +19 -0
  16. package/dist/components/input-datetime.d.ts +7 -0
  17. package/dist/components/input-datetime.js +35 -0
  18. package/dist/components/input-number.d.ts +9 -0
  19. package/dist/components/input-number.js +87 -0
  20. package/dist/components/input.d.ts +7 -0
  21. package/dist/components/input.js +20 -0
  22. package/dist/components/io-schema-values.d.ts +15 -0
  23. package/dist/components/io-schema-values.js +105 -0
  24. package/dist/components/io-schema.d.ts +13 -0
  25. package/dist/components/io-schema.js +163 -0
  26. package/dist/components/lazy-list.d.ts +13 -0
  27. package/dist/components/lazy-list.js +91 -0
  28. package/dist/components/lazy-sortable-list.d.ts +29 -0
  29. package/dist/components/lazy-sortable-list.js +12 -0
  30. package/dist/components/left-bar.d.ts +3 -0
  31. package/dist/components/left-bar.js +130 -0
  32. package/dist/components/list-screen.d.ts +16 -0
  33. package/dist/components/list-screen.js +100 -0
  34. package/dist/components/loading-indicator.d.ts +2 -0
  35. package/dist/components/loading-indicator.js +12 -0
  36. package/dist/components/main-content-container.d.ts +2 -0
  37. package/dist/components/main-content-container.js +90 -0
  38. package/dist/components/markdown-editor/autolink-plugin.d.ts +2 -0
  39. package/dist/components/markdown-editor/autolink-plugin.js +29 -0
  40. package/dist/components/markdown-editor/editor-inline.d.ts +3 -0
  41. package/dist/components/markdown-editor/editor-inline.js +13 -0
  42. package/dist/components/markdown-editor/editor.d.ts +18 -0
  43. package/dist/components/markdown-editor/editor.js +143 -0
  44. package/dist/components/markdown-editor/markdown-plugin.d.ts +9 -0
  45. package/dist/components/markdown-editor/markdown-plugin.js +194 -0
  46. package/dist/components/markdown-editor/mention-node.d.ts +21 -0
  47. package/dist/components/markdown-editor/mention-node.js +160 -0
  48. package/dist/components/markdown-editor/mentions-plugin.d.ts +7 -0
  49. package/dist/components/markdown-editor/mentions-plugin.js +268 -0
  50. package/dist/components/markdown-editor/theme.d.ts +46 -0
  51. package/dist/components/markdown-editor/theme.js +48 -0
  52. package/dist/components/markdown-editor/toolbar.d.ts +10 -0
  53. package/dist/components/markdown-editor/toolbar.js +112 -0
  54. package/dist/components/markdown-with-mentions.d.ts +4 -0
  55. package/dist/components/markdown-with-mentions.js +140 -0
  56. package/dist/components/message-logs/message-logs.d.ts +6 -0
  57. package/dist/components/message-logs/message-logs.js +307 -0
  58. package/dist/components/messages/avatar.d.ts +10 -0
  59. package/dist/components/messages/avatar.js +65 -0
  60. package/dist/components/messages/channel-message-list.d.ts +14 -0
  61. package/dist/components/messages/channel-message-list.js +158 -0
  62. package/dist/components/messages/channel-view.d.ts +6 -0
  63. package/dist/components/messages/channel-view.js +82 -0
  64. package/dist/components/messages/message-compose.d.ts +11 -0
  65. package/dist/components/messages/message-compose.js +152 -0
  66. package/dist/components/messages/message-display.d.ts +10 -0
  67. package/dist/components/messages/message-display.js +152 -0
  68. package/dist/components/messages/thread-message-list.d.ts +11 -0
  69. package/dist/components/messages/thread-message-list.js +122 -0
  70. package/dist/components/messages/thread-view.d.ts +6 -0
  71. package/dist/components/messages/thread-view.js +174 -0
  72. package/dist/components/off-canvas.d.ts +13 -0
  73. package/dist/components/off-canvas.js +89 -0
  74. package/dist/components/router.d.ts +6 -0
  75. package/dist/components/router.js +240 -0
  76. package/dist/components/save-button.d.ts +13 -0
  77. package/dist/components/save-button.js +75 -0
  78. package/dist/components/sortable-list.d.ts +36 -0
  79. package/dist/components/sortable-list.js +77 -0
  80. package/dist/components/tabs.d.ts +11 -0
  81. package/dist/components/tabs.js +69 -0
  82. package/dist/components/text-list-editor.tsx/text-list-editor.d.ts +6 -0
  83. package/dist/components/text-list-editor.tsx/text-list-editor.js +13 -0
  84. package/dist/components/tooltip.d.ts +11 -0
  85. package/dist/components/tooltip.js +20 -0
  86. package/dist/components/top-bar.d.ts +2 -0
  87. package/dist/components/top-bar.js +51 -0
  88. package/dist/components/typeahead/mentions-plugin.d.ts +7 -0
  89. package/dist/components/typeahead/mentions-plugin.js +203 -0
  90. package/dist/components/typeahead/typeahead-editor.d.ts +15 -0
  91. package/dist/components/typeahead/typeahead-editor.js +134 -0
  92. package/dist/components/typeahead/typeahead.d.ts +12 -0
  93. package/dist/components/typeahead/typeahead.js +94 -0
  94. package/dist/components/typeahead.d.ts +22 -0
  95. package/dist/components/typeahead.js +270 -0
  96. package/dist/globals.d.ts +29 -0
  97. package/dist/globals.js +148 -0
  98. package/dist/hooks.d.ts +34 -0
  99. package/dist/hooks.js +137 -0
  100. package/dist/index.d.ts +4 -0
  101. package/dist/index.js +20 -0
  102. package/dist/layout-vars.d.ts +6 -0
  103. package/dist/layout-vars.js +10 -0
  104. package/dist/mention-configs.d.ts +18 -0
  105. package/dist/mention-configs.js +149 -0
  106. package/dist/screens/assistants/assistant-config.d.ts +5 -0
  107. package/dist/screens/assistants/assistant-config.js +52 -0
  108. package/dist/screens/assistants/assistant-details.d.ts +4 -0
  109. package/dist/screens/assistants/assistant-details.js +85 -0
  110. package/dist/screens/assistants/assistant-info.d.ts +6 -0
  111. package/dist/screens/assistants/assistant-info.js +28 -0
  112. package/dist/screens/assistants/assistant-list.d.ts +2 -0
  113. package/dist/screens/assistants/assistant-list.js +114 -0
  114. package/dist/screens/assistants/assistant-tools.d.ts +5 -0
  115. package/dist/screens/assistants/assistant-tools.js +38 -0
  116. package/dist/screens/contacts/contact-details.d.ts +6 -0
  117. package/dist/screens/contacts/contact-details.js +100 -0
  118. package/dist/screens/contacts/contact-list.d.ts +2 -0
  119. package/dist/screens/contacts/contact-list.js +213 -0
  120. package/dist/screens/contacts/index.d.ts +4 -0
  121. package/dist/screens/contacts/index.js +21 -0
  122. package/dist/screens/events/cron.d.ts +3 -0
  123. package/dist/screens/events/cron.js +77 -0
  124. package/dist/screens/events/event-details.d.ts +6 -0
  125. package/dist/screens/events/event-details.js +112 -0
  126. package/dist/screens/events/event-handlers.d.ts +7 -0
  127. package/dist/screens/events/event-handlers.js +84 -0
  128. package/dist/screens/events/event-info.d.ts +5 -0
  129. package/dist/screens/events/event-info.js +19 -0
  130. package/dist/screens/events/event-list.d.ts +2 -0
  131. package/dist/screens/events/event-list.js +107 -0
  132. package/dist/screens/events/event-schedule.d.ts +5 -0
  133. package/dist/screens/events/event-schedule.js +124 -0
  134. package/dist/screens/groups/group-details.d.ts +6 -0
  135. package/dist/screens/groups/group-details.js +218 -0
  136. package/dist/screens/groups/group-list.d.ts +2 -0
  137. package/dist/screens/groups/group-list.js +275 -0
  138. package/dist/screens/groups/group-members.d.ts +8 -0
  139. package/dist/screens/groups/group-members.js +315 -0
  140. package/dist/screens/groups/index.d.ts +6 -0
  141. package/dist/screens/groups/index.js +23 -0
  142. package/dist/screens/knowledge/knowledge-frame-details.bk.d.ts +6 -0
  143. package/dist/screens/knowledge/knowledge-frame-details.bk.js +84 -0
  144. package/dist/screens/knowledge/knowledge-frame-details.d.ts +8 -0
  145. package/dist/screens/knowledge/knowledge-frame-details.js +143 -0
  146. package/dist/screens/knowledge/knowledge-frame-list.d.ts +2 -0
  147. package/dist/screens/knowledge/knowledge-frame-list.js +45 -0
  148. package/dist/screens/knowledge/knowledge-value-details.d.ts +6 -0
  149. package/dist/screens/knowledge/knowledge-value-details.js +150 -0
  150. package/dist/screens/knowledge/knowledge-value-list-item.d.ts +5 -0
  151. package/dist/screens/knowledge/knowledge-value-list-item.js +39 -0
  152. package/dist/screens/knowledge/knowledge-value-list.d.ts +3 -0
  153. package/dist/screens/knowledge/knowledge-value-list.js +123 -0
  154. package/dist/screens/packages/package-details.d.ts +6 -0
  155. package/dist/screens/packages/package-details.js +82 -0
  156. package/dist/screens/packages/package-info.d.ts +5 -0
  157. package/dist/screens/packages/package-info.js +42 -0
  158. package/dist/screens/packages/package-list.d.ts +2 -0
  159. package/dist/screens/packages/package-list.js +182 -0
  160. package/dist/screens/packages/package-new-local.d.ts +2 -0
  161. package/dist/screens/packages/package-new-local.js +82 -0
  162. package/dist/screens/peer-types/peer-type-details.d.ts +10 -0
  163. package/dist/screens/peer-types/peer-type-details.js +126 -0
  164. package/dist/screens/peer-types/peer-type-list.d.ts +2 -0
  165. package/dist/screens/peer-types/peer-type-list.js +57 -0
  166. package/dist/screens/predicates/predicate-details.d.ts +6 -0
  167. package/dist/screens/predicates/predicate-details.js +103 -0
  168. package/dist/screens/predicates/predicate-list.d.ts +2 -0
  169. package/dist/screens/predicates/predicate-list.js +46 -0
  170. package/dist/screens/profile.d.ts +2 -0
  171. package/dist/screens/profile.js +66 -0
  172. package/dist/screens/search/global-search.d.ts +2 -0
  173. package/dist/screens/search/global-search.js +186 -0
  174. package/dist/screens/settings/color-mode-dropdown.d.ts +6 -0
  175. package/dist/screens/settings/color-mode-dropdown.js +63 -0
  176. package/dist/screens/settings/settings-page.d.ts +2 -0
  177. package/dist/screens/settings/settings-page.js +49 -0
  178. package/dist/screens/setup-user.d.ts +2 -0
  179. package/dist/screens/setup-user.js +270 -0
  180. package/dist/screens/tools/tool-code.d.ts +5 -0
  181. package/dist/screens/tools/tool-code.js +32 -0
  182. package/dist/screens/tools/tool-details.d.ts +6 -0
  183. package/dist/screens/tools/tool-details.js +68 -0
  184. package/dist/screens/tools/tool-info.d.ts +5 -0
  185. package/dist/screens/tools/tool-info.js +74 -0
  186. package/dist/screens/tools/tool-list.d.ts +2 -0
  187. package/dist/screens/tools/tool-list.js +123 -0
  188. package/dist/screens/tools/tool-schema.d.ts +5 -0
  189. package/dist/screens/tools/tool-schema.js +30 -0
  190. package/dist/screens/tools/tool-test-details.d.ts +4 -0
  191. package/dist/screens/tools/tool-test-details.js +54 -0
  192. package/dist/screens/tools/tool-test-list.d.ts +4 -0
  193. package/dist/screens/tools/tool-test-list.js +82 -0
  194. package/dist/screens/variables/variable-details.d.ts +6 -0
  195. package/dist/screens/variables/variable-details.js +140 -0
  196. package/dist/screens/variables/variable-list.d.ts +2 -0
  197. package/dist/screens/variables/variable-list.js +58 -0
  198. package/dist/screens/workflows/workflow-details.d.ts +6 -0
  199. package/dist/screens/workflows/workflow-details.js +122 -0
  200. package/dist/screens/workflows/workflow-info.d.ts +5 -0
  201. package/dist/screens/workflows/workflow-info.js +18 -0
  202. package/dist/screens/workflows/workflow-instructions.d.ts +5 -0
  203. package/dist/screens/workflows/workflow-instructions.js +118 -0
  204. package/dist/screens/workflows/workflow-list.d.ts +2 -0
  205. package/dist/screens/workflows/workflow-list.js +109 -0
  206. package/dist/screens/workflows/workflow-subscriptions.d.ts +6 -0
  207. package/dist/screens/workflows/workflow-subscriptions.js +81 -0
  208. package/dist/setupTests.d.ts +1 -0
  209. package/dist/setupTests.js +31 -0
  210. package/dist/system-apps/assistants.app.d.ts +2 -0
  211. package/dist/system-apps/assistants.app.js +8 -0
  212. package/dist/system-apps/contacts.app.d.ts +2 -0
  213. package/dist/system-apps/contacts.app.js +9 -0
  214. package/dist/system-apps/events.app.d.ts +2 -0
  215. package/dist/system-apps/events.app.js +8 -0
  216. package/dist/system-apps/groups.app.d.ts +2 -0
  217. package/dist/system-apps/groups.app.js +9 -0
  218. package/dist/system-apps/index.d.ts +19 -0
  219. package/dist/system-apps/index.js +90 -0
  220. package/dist/system-apps/knowledge-frames.app.d.ts +2 -0
  221. package/dist/system-apps/knowledge-frames.app.js +9 -0
  222. package/dist/system-apps/knowledge-values.app.d.ts +2 -0
  223. package/dist/system-apps/knowledge-values.app.js +9 -0
  224. package/dist/system-apps/packages.app.d.ts +2 -0
  225. package/dist/system-apps/packages.app.js +8 -0
  226. package/dist/system-apps/predicates.app.d.ts +2 -0
  227. package/dist/system-apps/predicates.app.js +8 -0
  228. package/dist/system-apps/profile.app.d.ts +2 -0
  229. package/dist/system-apps/profile.app.js +8 -0
  230. package/dist/system-apps/search.app.d.ts +2 -0
  231. package/dist/system-apps/search.app.js +9 -0
  232. package/dist/system-apps/settings.app.d.ts +2 -0
  233. package/dist/system-apps/settings.app.js +8 -0
  234. package/dist/system-apps/threads.app.d.ts +2 -0
  235. package/dist/system-apps/threads.app.js +8 -0
  236. package/dist/system-apps/tools.app.d.ts +2 -0
  237. package/dist/system-apps/tools.app.js +8 -0
  238. package/dist/system-apps/types.app.d.ts +2 -0
  239. package/dist/system-apps/types.app.js +8 -0
  240. package/dist/system-apps/variables.app.d.ts +2 -0
  241. package/dist/system-apps/variables.app.js +8 -0
  242. package/dist/system-apps/workflows.app.d.ts +2 -0
  243. package/dist/system-apps/workflows.app.js +8 -0
  244. package/dist/tabs-layout/tabs-layout.d.ts +5 -0
  245. package/dist/tabs-layout/tabs-layout.js +374 -0
  246. package/dist/tabs-layout/tabs-state.d.ts +26 -0
  247. package/dist/tabs-layout/tabs-state.js +239 -0
  248. package/dist/three-bar-layout/left-bar-content.d.ts +7 -0
  249. package/dist/three-bar-layout/left-bar-content.js +151 -0
  250. package/dist/three-bar-layout/right-bar-content.d.ts +2 -0
  251. package/dist/three-bar-layout/right-bar-content.js +64 -0
  252. package/dist/three-bar-layout/three-bar-layout.d.ts +5 -0
  253. package/dist/three-bar-layout/three-bar-layout.js +218 -0
  254. package/dist/ui-defaults/index.d.ts +2 -0
  255. package/dist/ui-defaults/index.js +4 -0
  256. package/dist/ui-defaults/list-screen.d.ts +6 -0
  257. package/dist/ui-defaults/list-screen.js +74 -0
  258. package/dist/ui-defaults/notes-editor.d.ts +7 -0
  259. package/dist/ui-defaults/notes-editor.js +41 -0
  260. package/dist/ui-router/routes-loader.d.ts +25 -0
  261. package/dist/ui-router/routes-loader.js +97 -0
  262. package/dist/ui-router/ui-loader.d.ts +18 -0
  263. package/dist/ui-router/ui-loader.js +481 -0
  264. package/dist/utils.d.ts +9 -0
  265. package/dist/utils.js +250 -0
  266. package/docs/conversation-tab.md +201 -0
  267. package/docs/getting-started.md +284 -0
  268. package/docs/knowledge.md +187 -0
  269. package/docs/tabs-ui.md +696 -0
  270. package/docs/user-contacts-ui.md +384 -0
  271. package/jest.config.js +25 -0
  272. package/package.json +109 -0
  273. package/src/app.tsx +59 -0
  274. package/src/command-palette/command-palette-ui.tsx +264 -0
  275. package/src/command-palette/command-palette.ts +364 -0
  276. package/src/components/checkbox.tsx +22 -0
  277. package/src/components/group-switcher.tsx +469 -0
  278. package/src/components/input-date.tsx +28 -0
  279. package/src/components/input-datetime.tsx +41 -0
  280. package/src/components/input-number.tsx +67 -0
  281. package/src/components/input.tsx +22 -0
  282. package/src/components/io-schema-values.tsx +122 -0
  283. package/src/components/io-schema.tsx +234 -0
  284. package/src/components/lazy-list.tsx +98 -0
  285. package/src/components/lazy-sortable-list.tsx +51 -0
  286. package/src/components/left-bar.tsx +264 -0
  287. package/src/components/list-screen.tsx +105 -0
  288. package/src/components/loading-indicator.tsx +9 -0
  289. package/src/components/main-content-container.tsx +76 -0
  290. package/src/components/markdown-editor/autolink-plugin.tsx +36 -0
  291. package/src/components/markdown-editor/editor-inline.tsx +10 -0
  292. package/src/components/markdown-editor/editor.tsx +152 -0
  293. package/src/components/markdown-editor/markdown-plugin.tsx +224 -0
  294. package/src/components/markdown-editor/mention-node.ts +199 -0
  295. package/src/components/markdown-editor/mentions-plugin.tsx +356 -0
  296. package/src/components/markdown-editor/theme.ts +47 -0
  297. package/src/components/markdown-editor/toolbar.tsx +263 -0
  298. package/src/components/markdown-with-mentions.tsx +183 -0
  299. package/src/components/message-logs/message-logs.tsx +406 -0
  300. package/src/components/messages/avatar.tsx +95 -0
  301. package/src/components/messages/channel-message-list.tsx +177 -0
  302. package/src/components/messages/channel-view.tsx +74 -0
  303. package/src/components/messages/message-compose.tsx +162 -0
  304. package/src/components/messages/message-display.tsx +217 -0
  305. package/src/components/messages/thread-message-list.tsx +126 -0
  306. package/src/components/messages/thread-view.tsx +214 -0
  307. package/src/components/off-canvas.tsx +83 -0
  308. package/src/components/router.tsx +224 -0
  309. package/src/components/save-button.tsx +109 -0
  310. package/src/components/sortable-list.tsx +102 -0
  311. package/src/components/tabs.tsx +70 -0
  312. package/src/components/text-list-editor.tsx/text-list-editor.tsx +13 -0
  313. package/src/components/tooltip.tsx +50 -0
  314. package/src/components/top-bar.tsx +119 -0
  315. package/src/components/typeahead/mentions-plugin.tsx +265 -0
  316. package/src/components/typeahead/typeahead-editor.tsx +140 -0
  317. package/src/components/typeahead/typeahead.tsx +77 -0
  318. package/src/components/typeahead.tsx +359 -0
  319. package/src/globals.tsx +162 -0
  320. package/src/hooks.ts +144 -0
  321. package/src/index.tsx +8 -0
  322. package/src/layout-vars.ts +8 -0
  323. package/src/mention-configs.ts +166 -0
  324. package/src/screens/assistants/assistant-config.tsx +80 -0
  325. package/src/screens/assistants/assistant-details.tsx +77 -0
  326. package/src/screens/assistants/assistant-info.tsx +45 -0
  327. package/src/screens/assistants/assistant-list.tsx +115 -0
  328. package/src/screens/assistants/assistant-tools.tsx +61 -0
  329. package/src/screens/contacts/contact-details.tsx +175 -0
  330. package/src/screens/contacts/contact-list.tsx +251 -0
  331. package/src/screens/contacts/index.ts +6 -0
  332. package/src/screens/events/cron.ts +74 -0
  333. package/src/screens/events/event-details.tsx +117 -0
  334. package/src/screens/events/event-handlers.tsx +61 -0
  335. package/src/screens/events/event-info.tsx +29 -0
  336. package/src/screens/events/event-list.tsx +104 -0
  337. package/src/screens/events/event-schedule.tsx +130 -0
  338. package/src/screens/groups/group-details.tsx +306 -0
  339. package/src/screens/groups/group-list.tsx +366 -0
  340. package/src/screens/groups/group-members.tsx +455 -0
  341. package/src/screens/groups/index.ts +9 -0
  342. package/src/screens/knowledge/knowledge-frame-details.bk.tsx +160 -0
  343. package/src/screens/knowledge/knowledge-frame-details.tsx +176 -0
  344. package/src/screens/knowledge/knowledge-frame-list.tsx +49 -0
  345. package/src/screens/knowledge/knowledge-value-details.tsx +181 -0
  346. package/src/screens/knowledge/knowledge-value-list-item.tsx +48 -0
  347. package/src/screens/knowledge/knowledge-value-list.tsx +131 -0
  348. package/src/screens/packages/package-details.tsx +117 -0
  349. package/src/screens/packages/package-info.tsx +83 -0
  350. package/src/screens/packages/package-list.tsx +191 -0
  351. package/src/screens/packages/package-new-local.tsx +93 -0
  352. package/src/screens/peer-types/peer-type-details.tsx +162 -0
  353. package/src/screens/peer-types/peer-type-list.tsx +74 -0
  354. package/src/screens/predicates/predicate-details.tsx +125 -0
  355. package/src/screens/predicates/predicate-list.tsx +50 -0
  356. package/src/screens/profile.tsx +68 -0
  357. package/src/screens/search/global-search.tsx +274 -0
  358. package/src/screens/settings/color-mode-dropdown.tsx +57 -0
  359. package/src/screens/settings/settings-page.tsx +76 -0
  360. package/src/screens/setup-user.tsx +367 -0
  361. package/src/screens/tools/tool-code.tsx +35 -0
  362. package/src/screens/tools/tool-details.tsx +101 -0
  363. package/src/screens/tools/tool-info.tsx +60 -0
  364. package/src/screens/tools/tool-list.tsx +121 -0
  365. package/src/screens/tools/tool-schema.tsx +42 -0
  366. package/src/screens/tools/tool-test-details.tsx +100 -0
  367. package/src/screens/tools/tool-test-list.tsx +74 -0
  368. package/src/screens/variables/variable-details.tsx +183 -0
  369. package/src/screens/variables/variable-list.tsx +74 -0
  370. package/src/screens/workflows/workflow-details.tsx +130 -0
  371. package/src/screens/workflows/workflow-info.tsx +29 -0
  372. package/src/screens/workflows/workflow-instructions.tsx +127 -0
  373. package/src/screens/workflows/workflow-list.tsx +107 -0
  374. package/src/screens/workflows/workflow-subscriptions.tsx +58 -0
  375. package/src/setupTests.ts +32 -0
  376. package/src/system-apps/assistants.app.ts +7 -0
  377. package/src/system-apps/contacts.app.ts +8 -0
  378. package/src/system-apps/events.app.ts +7 -0
  379. package/src/system-apps/groups.app.ts +8 -0
  380. package/src/system-apps/index.ts +79 -0
  381. package/src/system-apps/knowledge-frames.app.ts +8 -0
  382. package/src/system-apps/knowledge-values.app.ts +8 -0
  383. package/src/system-apps/packages.app.ts +7 -0
  384. package/src/system-apps/predicates.app.ts +7 -0
  385. package/src/system-apps/profile.app.ts +7 -0
  386. package/src/system-apps/search.app.ts +8 -0
  387. package/src/system-apps/settings.app.ts +7 -0
  388. package/src/system-apps/threads.app.ts +7 -0
  389. package/src/system-apps/tools.app.ts +7 -0
  390. package/src/system-apps/types.app.ts +7 -0
  391. package/src/system-apps/variables.app.ts +7 -0
  392. package/src/system-apps/workflows.app.ts +7 -0
  393. package/src/tabs-layout/tabs-layout.tsx +672 -0
  394. package/src/tabs-layout/tabs-state.ts +269 -0
  395. package/src/three-bar-layout/left-bar-content.tsx +202 -0
  396. package/src/three-bar-layout/right-bar-content.tsx +67 -0
  397. package/src/three-bar-layout/three-bar-layout.tsx +297 -0
  398. package/src/ui-defaults/index.ts +3 -0
  399. package/src/ui-defaults/list-screen.tsx +92 -0
  400. package/src/ui-defaults/notes-editor.tsx +51 -0
  401. package/src/ui-router/routes-loader.ts +98 -0
  402. package/src/ui-router/ui-loader.tsx +497 -0
  403. package/src/utils.ts +266 -0
  404. package/tsconfig.json +24 -0
@@ -0,0 +1,672 @@
1
+ import { getUserContext, IAppNav, sleep, Subscription } from "@peers-app/peers-sdk";
2
+ import React, { 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, colorModePreference, ColorModePreference } from '../screens/settings/color-mode-dropdown';
10
+ import { SetupUser } from '../screens/setup-user';
11
+ import { systemPackage } from '../system-apps';
12
+ import { allPackages, loadAllRoutes } from '../ui-router/routes-loader';
13
+ import { UIRouter } from '../ui-router/ui-loader';
14
+ import { activeTabId, activeTabs, goToTabPath, initializedTabs, recentlyUsedApps, TabState } from './tabs-state';
15
+
16
+
17
+ export function TabsLayout(props: { colorMode?: ColorModePreference }) {
18
+ const loaded = usePromise(async () => {
19
+ await loadGlobals().then(() => console.log('Globals loaded'));
20
+ await loadAllRoutes().then(() => console.log('Routes loaded'));
21
+ // Wait for tab state to load from database
22
+ await Promise.all([
23
+ activeTabs.loadingPromise,
24
+ activeTabId.loadingPromise,
25
+ recentlyUsedApps.loadingPromise
26
+ ]);
27
+ return true;
28
+ });
29
+
30
+ if (!loaded) return false;
31
+ document.getElementById('appLoadingDiv')?.remove();
32
+
33
+ if (me.name === me.userId) {
34
+ return <SetupUser />
35
+ }
36
+
37
+ colorModePreference(props.colorMode);
38
+ return (
39
+ <>
40
+ <TabsLayoutInternal />
41
+ <CommandPaletteOverlay />
42
+ </>
43
+ );
44
+ }
45
+
46
+ function TabsLayoutInternal() {
47
+ const [_colorMode] = useObservable(colorMode);
48
+ const [tabs] = useObservable(activeTabs);
49
+ const [activeTab] = useObservable(activeTabId);
50
+ const [currentlyActiveGroupId, setCurrentlyActiveGroupId] = useState('');
51
+
52
+ useEffect(() => {
53
+ let sub: Subscription;
54
+ getUserContext().then(userContext => {
55
+ setCurrentlyActiveGroupId(userContext.currentlyActiveGroupId() || userContext.userId())
56
+ sub = userContext.currentlyActiveGroupId.subscribe(async groupId => {
57
+ setCurrentlyActiveGroupId(userContext.currentlyActiveGroupId() || userContext.userId());
58
+ // below reloading logic is a kludge to deal with different groups having different packages installed
59
+ await sleep(100);
60
+ window.location.reload();
61
+ });
62
+ });
63
+ return () => {
64
+ if (sub) {
65
+ sub.dispose();
66
+ }
67
+ };
68
+ }, []);
69
+
70
+ useObservable(isDesktop);
71
+ const isMobile = !isDesktop();
72
+
73
+ const closeTab = (tabId: string) => {
74
+ const currentTabs = activeTabs();
75
+ const tabToClose = currentTabs.find(t => t.tabId === tabId);
76
+
77
+ if (!tabToClose || tabToClose.tabId === "launcher") return;
78
+
79
+ const newTabs = currentTabs.filter(t => t.tabId !== tabId);
80
+ activeTabs(newTabs);
81
+
82
+ initializedTabs.delete(tabId);
83
+
84
+ // If closing active tab, switch to previous tab
85
+ if (activeTab === tabId) {
86
+ const newActiveTab = newTabs.length > 0 ? newTabs[newTabs.length - 1].tabId : 'launcher';
87
+ activeTabId(newActiveTab);
88
+ }
89
+ };
90
+
91
+ const switchTab = (tabId: string) => {
92
+ activeTabId(tabId);
93
+ };
94
+
95
+ return (
96
+ <div
97
+ key={currentlyActiveGroupId}
98
+ className="d-flex flex-column" style={{ height: '100vh', overflow: 'hidden' }}>
99
+ {/* Tabs Header */}
100
+ <div
101
+ className={`border-bottom ${_colorMode === 'light' ? 'bg-light' : 'bg-dark'}`}
102
+ style={{
103
+ borderBottomWidth: '1px',
104
+ borderBottomColor: _colorMode === 'light' ? '#dee2e6' : '#495057'
105
+ }}
106
+ >
107
+ {isMobile ? (
108
+ <MobileTabsHeader
109
+ tabs={tabs}
110
+ activeTab={activeTab}
111
+ onSwitch={switchTab}
112
+ onClose={closeTab}
113
+ colorMode={_colorMode}
114
+ />
115
+ ) : (
116
+ <div className="d-flex align-items-center px-1" style={{ height: '36px' }}>
117
+ {/* Group Switcher */}
118
+ <GroupSwitcher colorMode={_colorMode} />
119
+
120
+ {/* Search Button */}
121
+ <button
122
+ className="btn btn-sm me-2 d-flex align-items-center"
123
+ onClick={openCommandPalette}
124
+ title="Search everything (Cmd+K)"
125
+ style={{
126
+ padding: '4px 8px',
127
+ fontSize: '12px',
128
+ borderRadius: '6px',
129
+ border: 'none',
130
+ background: 'transparent',
131
+ color: _colorMode === 'light' ? '#6c757d' : '#adb5bd'
132
+ }}
133
+ onMouseEnter={(e) => {
134
+ e.currentTarget.style.backgroundColor = _colorMode === 'light' ? '#f8f9fa' : '#495057';
135
+ e.currentTarget.style.color = _colorMode === 'light' ? '#0d6efd' : '#ffffff';
136
+ }}
137
+ onMouseLeave={(e) => {
138
+ e.currentTarget.style.backgroundColor = 'transparent';
139
+ e.currentTarget.style.color = _colorMode === 'light' ? '#6c757d' : '#adb5bd';
140
+ }}
141
+ >
142
+ <i className="bi-search" style={{ fontSize: '14px' }} />
143
+ </button>
144
+
145
+ <div
146
+ className="d-flex flex-grow-1 overflow-auto"
147
+ style={{
148
+ scrollbarWidth: 'none',
149
+ msOverflowStyle: 'none'
150
+ }}
151
+ >
152
+ {tabs.map(tab => (
153
+ <TabHeader
154
+ key={tab.tabId}
155
+ tab={tab}
156
+ isActive={activeTab === tab.tabId}
157
+ onSwitch={switchTab}
158
+ onClose={closeTab}
159
+ colorMode={_colorMode}
160
+ />
161
+ ))}
162
+ </div>
163
+ </div>
164
+ )}
165
+ </div>
166
+
167
+ {/* Tab Content */}
168
+ <div className="flex-grow-1 overflow-hidden">
169
+ {tabs.map(tab => (
170
+ <div
171
+ key={tab.tabId}
172
+ className={`h-100 ${activeTab === tab.tabId ? 'd-block' : 'd-none'}`}
173
+ style={{ display: activeTab === tab.tabId ? 'block' : 'none' }}
174
+ >
175
+ <TabContent tab={tab} isMobile={isMobile} isActive={activeTab === tab.tabId} />
176
+ </div>
177
+ ))}
178
+ </div>
179
+ </div>
180
+ );
181
+ }
182
+
183
+ interface MobileTabsHeaderProps {
184
+ tabs: TabState[];
185
+ activeTab: string;
186
+ onSwitch: (tabId: string) => void;
187
+ onClose: (tabId: string) => void;
188
+ colorMode: string;
189
+ }
190
+
191
+ function MobileTabsHeader({ tabs, activeTab, onSwitch, onClose, colorMode }: MobileTabsHeaderProps) {
192
+ const [showTabDropdown, setShowTabDropdown] = React.useState(false);
193
+ const currentTab = tabs.find(t => t.tabId === activeTab);
194
+ const nonLauncherTabs = tabs.filter(t => t.packageId !== 'launcher');
195
+
196
+ return (
197
+ <div className="d-flex align-items-center justify-content-between px-2" style={{ height: '36px' }}>
198
+ {/* Left Side - Group Switcher, Search & App Launcher Buttons */}
199
+ <div className="d-flex align-items-center gap-1">
200
+ <GroupSwitcher colorMode={colorMode} />
201
+ <button
202
+ className="btn btn-sm"
203
+ onClick={openCommandPalette}
204
+ title="Search everything"
205
+ style={{
206
+ minWidth: '36px',
207
+ border: 'none',
208
+ background: 'transparent',
209
+ color: colorMode === 'light' ? '#6c757d' : '#adb5bd'
210
+ }}
211
+ onMouseEnter={(e) => {
212
+ e.currentTarget.style.backgroundColor = colorMode === 'light' ? '#f8f9fa' : '#495057';
213
+ e.currentTarget.style.color = colorMode === 'light' ? '#0d6efd' : '#ffffff';
214
+ }}
215
+ onMouseLeave={(e) => {
216
+ e.currentTarget.style.backgroundColor = 'transparent';
217
+ e.currentTarget.style.color = colorMode === 'light' ? '#6c757d' : '#adb5bd';
218
+ }}
219
+ >
220
+ <i className="bi-search" />
221
+ </button>
222
+ <button
223
+ className={`btn btn-sm ${colorMode === 'light' ? 'btn-outline-primary' : 'btn-outline-light'}`}
224
+ onClick={() => onSwitch('launcher')}
225
+ style={{ minWidth: '36px' }}
226
+ >
227
+ <i className="bi-grid-3x3-gap" />
228
+ </button>
229
+ </div>
230
+
231
+ {/* Center - Current Tab Display */}
232
+ <div className="d-flex align-items-center flex-grow-1 justify-content-center">
233
+ {currentTab?.iconClassName && currentTab.packageId !== 'launcher' && (
234
+ <i className={`${currentTab.iconClassName} me-2`} />
235
+ )}
236
+ <span className="fw-medium text-truncate me-2 small">
237
+ {currentTab?.title || 'Apps'}
238
+ </span>
239
+ </div>
240
+
241
+ {/* Right Side - Tab Actions */}
242
+ <div className="d-flex align-items-center gap-2">
243
+ {/* Tab Switcher Dropdown (excluding launcher) */}
244
+ {nonLauncherTabs.length > 0 && (
245
+ <div className="dropdown">
246
+ <button
247
+ className={`btn btn-sm ${colorMode === 'light' ? 'btn-outline-dark' : 'btn-outline-light'}`}
248
+ onClick={() => setShowTabDropdown(!showTabDropdown)}
249
+ >
250
+ <i className="bi-list" />
251
+ <span className="ms-1">{nonLauncherTabs.length}</span>
252
+ </button>
253
+ {showTabDropdown && (
254
+ <div
255
+ className={`dropdown-menu show position-absolute ${colorMode === 'light' ? '' : 'dropdown-menu-dark'}`}
256
+ style={{ right: 0, top: '100%', zIndex: 1000, minWidth: '250px' }}
257
+ >
258
+ {nonLauncherTabs.slice().reverse().map(tab => (
259
+ <div
260
+ key={tab.tabId}
261
+ className={`dropdown-item d-flex align-items-center justify-content-between ${activeTab === tab.tabId ? 'active' : ''}`}
262
+ style={{ cursor: 'pointer' }}
263
+ onClick={() => {
264
+ onSwitch(tab.tabId);
265
+ setShowTabDropdown(false);
266
+ }}
267
+ >
268
+ <div className="d-flex align-items-center">
269
+ {tab.iconClassName && <i className={`${tab.iconClassName} me-2`} />}
270
+ <span>{tab.title}</span>
271
+ </div>
272
+ {tab.tabId !== "launcher" && (
273
+ <button
274
+ className="btn btn-sm p-0 ms-2"
275
+ style={{ width: '20px', height: '20px' }}
276
+ onClick={(e) => {
277
+ e.stopPropagation();
278
+ onClose(tab.tabId);
279
+ }}
280
+ >
281
+ <i className="bi-x" />
282
+ </button>
283
+ )}
284
+ </div>
285
+ ))}
286
+ </div>
287
+ )}
288
+ </div>
289
+ )}
290
+ </div>
291
+
292
+ {/* Backdrop to close dropdown */}
293
+ {showTabDropdown && (
294
+ <div
295
+ className="position-fixed w-100 h-100"
296
+ style={{ top: 0, left: 0, zIndex: 999 }}
297
+ onClick={() => setShowTabDropdown(false)}
298
+ />
299
+ )}
300
+ </div>
301
+ );
302
+ }
303
+
304
+ interface TabHeaderProps {
305
+ tab: TabState;
306
+ isActive: boolean;
307
+ onSwitch: (tabId: string) => void;
308
+ onClose: (tabId: string) => void;
309
+ colorMode: string;
310
+ }
311
+
312
+ function TabHeader({ tab, isActive, onSwitch, onClose, colorMode }: TabHeaderProps) {
313
+ const activeClass = isActive
314
+ ? (colorMode === 'light' ? 'bg-white' : 'bg-secondary')
315
+ : (colorMode === 'light' ? 'bg-light' : 'bg-dark');
316
+
317
+ return (
318
+ <div
319
+ className={`d-flex align-items-center px-2 py-1 user-select-none ${activeClass}`}
320
+ style={{
321
+ cursor: 'pointer',
322
+ minWidth: '100px',
323
+ maxWidth: '180px',
324
+ height: '34px',
325
+ fontSize: '13px',
326
+ borderTop: isActive ? '2px solid #0d6efd' : '2px solid transparent',
327
+ borderLeft: '1px solid transparent',
328
+ borderRight: '1px solid ' + (colorMode === 'light' ? '#dee2e6' : '#495057'),
329
+ borderBottom: isActive ? (colorMode === 'light' ? '1px solid white' : '1px solid #343a40') : 'none',
330
+ marginBottom: isActive ? '-1px' : '0',
331
+ borderRadius: isActive ? '6px 6px 0 0' : '0',
332
+ transition: 'all 0.15s ease',
333
+ zIndex: isActive ? 1 : 0,
334
+ boxShadow: isActive ? '0 2px 4px rgba(0,0,0,0.1)' : 'none'
335
+ }}
336
+ onMouseEnter={(e) => {
337
+ if (!isActive) {
338
+ e.currentTarget.style.backgroundColor = colorMode === 'light' ? '#f8f9fa' : '#495057';
339
+ }
340
+ }}
341
+ onMouseLeave={(e) => {
342
+ if (!isActive) {
343
+ e.currentTarget.style.backgroundColor = colorMode === 'light' ? '#f1f3f4' : '#343a40';
344
+ }
345
+ }}
346
+ onClick={() => onSwitch(tab.tabId)}
347
+ >
348
+ {tab.iconClassName && (
349
+ <i className={`${tab.iconClassName} me-2`} />
350
+ )}
351
+ <span className="text-truncate" style={{ maxWidth: '120px', fontSize: '12px' }}>
352
+ {tab.title}
353
+ </span>
354
+ {tab.tabId !== "launcher" && (
355
+ <button
356
+ className="btn p-0 ms-1 opacity-75 hover-opacity-100"
357
+ style={{
358
+ width: '14px',
359
+ height: '14px',
360
+ fontSize: '11px',
361
+ border: 'none',
362
+ background: 'none',
363
+ borderRadius: '2px'
364
+ }}
365
+ onMouseEnter={(e) => e.currentTarget.style.backgroundColor = 'rgba(0,0,0,0.1)'}
366
+ onMouseLeave={(e) => e.currentTarget.style.backgroundColor = 'transparent'}
367
+ onClick={(e) => {
368
+ e.stopPropagation();
369
+ onClose(tab.tabId);
370
+ }}
371
+ >
372
+ <i className="bi-x" />
373
+ </button>
374
+ )}
375
+ </div>
376
+ );
377
+ }
378
+
379
+ interface TabContentProps {
380
+ tab: TabState;
381
+ isMobile: boolean;
382
+ isActive: boolean;
383
+ }
384
+
385
+ function TabContent({ tab, isMobile, isActive }: TabContentProps) {
386
+ // Only render content if this tab is active OR has been previously initialized
387
+ const shouldRender = isActive || initializedTabs.has(tab.tabId);
388
+
389
+ // Mark tab as initialized when it becomes active for the first time
390
+ React.useEffect(() => {
391
+ if (isActive) {
392
+ initializedTabs.add(tab.tabId);
393
+ }
394
+ }, [isActive, tab.tabId]);
395
+
396
+ if (!shouldRender) {
397
+ return null;
398
+ }
399
+
400
+ return <TabContentRenderer tab={tab} isMobile={isMobile} />;
401
+ }
402
+
403
+ // Create individual tab content components that don't re-render
404
+ const TabContentRenderer = React.memo(({ tab, isMobile }: { tab: TabState, isMobile: boolean }) => {
405
+ if (tab.packageId === 'launcher') {
406
+ return <AppLauncherTab isMobile={isMobile} />;
407
+ }
408
+
409
+ // System apps use the main Router (which reads from mainContentPath)
410
+ // Package apps use UIRouter with explicit path
411
+ const isSystemApp = tab.packageId === 'system-apps';
412
+
413
+ return (
414
+ <div className={`h-100 overflow-auto ${isMobile ? 'p-2' : 'p-3'}`}>
415
+ {isSystemApp ? (
416
+ <Router path={tab.path} />
417
+ ) : (
418
+ <UIRouter path={tab.path} uiCategory="screen" props={{}} />
419
+ )}
420
+ </div>
421
+ );
422
+ }, (prevProps, nextProps) => {
423
+ // Only re-render if the tab or isMobile changed
424
+ return prevProps.tab.tabId === nextProps.tab.tabId &&
425
+ prevProps.tab.path === nextProps.tab.path &&
426
+ prevProps.isMobile === nextProps.isMobile;
427
+ });
428
+
429
+ interface AppLauncherTabProps {
430
+ isMobile: boolean;
431
+ }
432
+
433
+ // Interface for app items (nav items)
434
+ interface AppItem {
435
+ packageId: string;
436
+ packageName: string;
437
+ navItem: IAppNav;
438
+ path: string;
439
+ name: string;
440
+ displayName: string;
441
+ iconClassName: string;
442
+ }
443
+
444
+ function AppLauncherTab({ isMobile }: AppLauncherTabProps) {
445
+ const [packages] = useObservable(allPackages);
446
+ const [recentApps] = useObservable(recentlyUsedApps);
447
+ const [searchTerm, setSearchTerm] = React.useState('');
448
+
449
+ // Combine regular packages with system apps package
450
+ const allPackages_ = [...packages, systemPackage];
451
+
452
+ // Create app items from nav items in packages
453
+ const allApps: AppItem[] = allPackages_
454
+ .filter(p => !p.disabled && p.appNavs && p.appNavs.length > 0)
455
+ .flatMap(pkg =>
456
+ pkg.appNavs!.map(navItem => {
457
+ // Construct path - use direct path for system apps, package-nav for others
458
+ let path: string;
459
+ if (pkg.packageId === 'system-apps') {
460
+ // System apps use direct routing (like the original left bar)
461
+ path = navItem.navigationPath ?? navItem.name.replace(/\s/g, '-').toLowerCase();
462
+ } else {
463
+ // Regular packages use package-nav format
464
+ path = `package-nav/${pkg.packageId}/${(navItem.navigationPath ?? navItem.name).replace(/[^a-zA-Z0-9]/g, '-').toLowerCase()}`;
465
+ while (path.includes('//')) {
466
+ path = path.replace('//', '/');
467
+ }
468
+ }
469
+
470
+ return {
471
+ packageId: pkg.packageId,
472
+ packageName: pkg.name,
473
+ navItem,
474
+ path,
475
+ name: navItem.name,
476
+ displayName: navItem.displayName || navItem.name,
477
+ iconClassName: navItem.iconClassName || 'bi-box-seam'
478
+ };
479
+ })
480
+ );
481
+
482
+ // Filter apps based on search term
483
+ const filteredApps = searchTerm.trim()
484
+ ? allApps.filter(app =>
485
+ app.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
486
+ app.displayName.toLowerCase().includes(searchTerm.toLowerCase())
487
+ )
488
+ : allApps;
489
+
490
+ // Categorize filtered apps by package ID
491
+ const systemApps = filteredApps.filter(app => app.packageId === 'system-apps');
492
+ const userApps = filteredApps.filter(app => app.packageId !== 'system-apps');
493
+
494
+ // Get recently used apps by matching paths (only show if they match search)
495
+ const recentAppItems = recentApps
496
+ .map(path => filteredApps.find(app => app.path === path))
497
+ .filter(Boolean) as AppItem[];
498
+
499
+ const openApp = (appItem: AppItem) => {
500
+ goToTabPath(appItem.path);
501
+ };
502
+
503
+ return (
504
+ <div className={`container-fluid ${isMobile ? 'p-2' : 'p-4'}`} style={{ maxHeight: '100%', overflowY: 'auto' }}>
505
+ {/* Search Bar */}
506
+ <div className={isMobile ? 'mb-3' : 'mb-4'}>
507
+ <div className="position-relative">
508
+ <i className="bi-search position-absolute" style={{
509
+ left: '12px',
510
+ top: '50%',
511
+ transform: 'translateY(-50%)',
512
+ color: '#6c757d',
513
+ zIndex: 1
514
+ }} />
515
+ <input
516
+ type="text"
517
+ className="form-control"
518
+ placeholder="Search apps..."
519
+ value={searchTerm}
520
+ onChange={(e) => setSearchTerm(e.target.value)}
521
+ style={{
522
+ paddingLeft: '40px',
523
+ fontSize: isMobile ? '14px' : '15px'
524
+ }}
525
+ />
526
+ {searchTerm && (
527
+ <button
528
+ className="btn btn-sm position-absolute"
529
+ style={{
530
+ right: '8px',
531
+ top: '50%',
532
+ transform: 'translateY(-50%)',
533
+ border: 'none',
534
+ background: 'none',
535
+ color: '#6c757d'
536
+ }}
537
+ onClick={() => setSearchTerm('')}
538
+ >
539
+ <i className="bi-x" />
540
+ </button>
541
+ )}
542
+ </div>
543
+ </div>
544
+
545
+ {/* Recently Used */}
546
+ {recentAppItems.length > 0 && (
547
+ <AppSection
548
+ title="Recently Used"
549
+ iconClassName="bi-clock"
550
+ apps={recentAppItems}
551
+ onOpenApp={openApp}
552
+ isMobile={isMobile}
553
+ />
554
+ )}
555
+
556
+ {/* User Apps */}
557
+ {userApps.length > 0 && (
558
+ <AppSection
559
+ title="User Apps"
560
+ iconClassName="bi-person"
561
+ apps={userApps}
562
+ onOpenApp={openApp}
563
+ isMobile={isMobile}
564
+ />
565
+ )}
566
+
567
+ {/* System Apps */}
568
+ {systemApps.length > 0 && (
569
+ <AppSection
570
+ title="System Apps"
571
+ iconClassName="bi-gear"
572
+ apps={systemApps}
573
+ onOpenApp={openApp}
574
+ isMobile={isMobile}
575
+ />
576
+ )}
577
+ </div>
578
+ );
579
+ }
580
+
581
+ interface AppSectionProps {
582
+ title: string;
583
+ iconClassName: string;
584
+ apps: AppItem[];
585
+ onOpenApp: (appItem: AppItem) => void;
586
+ isMobile: boolean;
587
+ }
588
+
589
+ function AppSection({ title, iconClassName, apps, onOpenApp, isMobile }: AppSectionProps) {
590
+ return (
591
+ <div className='mb-5'>
592
+ <h4 className={`${isMobile ? 'mb-3 h5' : 'mb-3'}`}>
593
+ <i className={`${iconClassName} me-2`} />
594
+ {title}
595
+ </h4>
596
+ <div className={`d-flex flex-wrap ${isMobile ? 'gap-3' : 'gap-4'}`} style={{ gap: isMobile ? '16px' : '20px' }}>
597
+ {apps.map(app => (
598
+ <AppCard key={`${app.packageId}-${app.path}`} appItem={app} onOpenApp={onOpenApp} isMobile={isMobile} />
599
+ ))}
600
+ </div>
601
+ </div>
602
+ );
603
+ }
604
+
605
+ interface AppCardProps {
606
+ appItem: AppItem;
607
+ onOpenApp: (appItem: AppItem) => void;
608
+ isMobile: boolean;
609
+ }
610
+
611
+ function AppCard({ appItem, onOpenApp, isMobile }: AppCardProps) {
612
+ const [_colorMode] = useObservable(colorMode);
613
+
614
+ const isDark = _colorMode === 'dark';
615
+
616
+ return (
617
+ <div
618
+ className="d-flex flex-column align-items-center text-center"
619
+ style={{
620
+ cursor: 'pointer',
621
+ width: isMobile ? '80px' : '90px',
622
+ transition: 'all 0.15s ease'
623
+ }}
624
+ title={appItem.name}
625
+ onMouseEnter={(e) => {
626
+ e.currentTarget.style.transform = 'scale(1.05)';
627
+ }}
628
+ onMouseLeave={(e) => {
629
+ e.currentTarget.style.transform = 'scale(1)';
630
+ }}
631
+ onClick={() => onOpenApp(appItem)}
632
+ >
633
+ {/* Icon Container */}
634
+ <div
635
+ className="d-flex align-items-center justify-content-center mb-2"
636
+ style={{
637
+ width: isMobile ? '64px' : '72px',
638
+ height: isMobile ? '64px' : '72px',
639
+ backgroundColor: isDark ? '#343a40' : '#f8f9fa',
640
+ borderRadius: '12px',
641
+ border: `1px solid ${isDark ? '#495057' : '#e9ecef'}`,
642
+ boxShadow: isDark ? '0 2px 4px rgba(0,0,0,0.3)' : '0 2px 4px rgba(0,0,0,0.08)',
643
+ transition: 'all 0.15s ease'
644
+ }}
645
+ >
646
+ <i
647
+ className={appItem.iconClassName}
648
+ style={{
649
+ fontSize: isMobile ? '28px' : '32px',
650
+ color: isDark ? '#0d6efd' : '#0d6efd' // Keep primary blue for both themes
651
+ }}
652
+ />
653
+ </div>
654
+
655
+ {/* Title */}
656
+ <span
657
+ className={isDark ? 'text-light' : 'text-dark'}
658
+ style={{
659
+ fontSize: isMobile ? '11px' : '12px',
660
+ fontWeight: '500',
661
+ lineHeight: '1.2',
662
+ maxWidth: '100%',
663
+ overflow: 'hidden',
664
+ textOverflow: 'ellipsis',
665
+ whiteSpace: 'nowrap'
666
+ }}
667
+ >
668
+ {appItem.displayName}
669
+ </span>
670
+ </div>
671
+ );
672
+ }