@polderlabs/bizar 7.0.2 → 8.0.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 (490) hide show
  1. package/.claude/settings.local.json +8 -0
  2. package/bizar-dash/DESIGN.md +729 -969
  3. package/bizar-dash/GLYPH.md +418 -0
  4. package/bizar-dash/PLAN.md +1135 -0
  5. package/bizar-dash/dist/assets/ActivityFeed-BRBtQOHB.js +6 -0
  6. package/bizar-dash/dist/assets/ActivityFeed-BRBtQOHB.js.map +1 -0
  7. package/bizar-dash/dist/assets/ActivityView-CwqhxBfh.js +1 -0
  8. package/bizar-dash/dist/assets/ActivityView-CwqhxBfh.js.map +1 -0
  9. package/bizar-dash/dist/assets/AgentActivity-6MxU-a9F.js +1 -0
  10. package/bizar-dash/dist/assets/AgentActivity-6MxU-a9F.js.map +1 -0
  11. package/bizar-dash/dist/assets/AgentsView-BYrOFdwl.js +8 -0
  12. package/bizar-dash/dist/assets/AgentsView-BYrOFdwl.js.map +1 -0
  13. package/bizar-dash/dist/assets/Badge-Cd61wsBa.js +1 -0
  14. package/bizar-dash/dist/assets/Badge-Cd61wsBa.js.map +1 -0
  15. package/bizar-dash/dist/assets/Card-C_bvqXdQ.js +1 -0
  16. package/bizar-dash/dist/assets/Card-C_bvqXdQ.js.map +1 -0
  17. package/bizar-dash/dist/assets/EmptyState-BWQuKnk9.js +1 -0
  18. package/bizar-dash/dist/assets/EmptyState-BWQuKnk9.js.map +1 -0
  19. package/bizar-dash/dist/assets/GoalsView-Dok0yt9S.js +11 -0
  20. package/bizar-dash/dist/assets/GoalsView-Dok0yt9S.js.map +1 -0
  21. package/bizar-dash/dist/assets/Grid-C62gCPqt.js +1 -0
  22. package/bizar-dash/dist/assets/Grid-C62gCPqt.js.map +1 -0
  23. package/bizar-dash/dist/assets/KanbanCard-CtTKP6ZX.js +21 -0
  24. package/bizar-dash/dist/assets/KanbanCard-CtTKP6ZX.js.map +1 -0
  25. package/bizar-dash/dist/assets/LibrariesView-BcyBQXiY.js +67 -0
  26. package/bizar-dash/dist/assets/LibrariesView-BcyBQXiY.js.map +1 -0
  27. package/bizar-dash/dist/assets/MemoryView-49wR4u-y.js +13 -0
  28. package/bizar-dash/dist/assets/MemoryView-49wR4u-y.js.map +1 -0
  29. package/bizar-dash/dist/assets/OverviewView-BtQ_UWYj.js +1 -0
  30. package/bizar-dash/dist/assets/OverviewView-BtQ_UWYj.js.map +1 -0
  31. package/bizar-dash/dist/assets/ProgressBar-CpvDeq9F.js +1 -0
  32. package/bizar-dash/dist/assets/ProgressBar-CpvDeq9F.js.map +1 -0
  33. package/bizar-dash/dist/assets/SettingsRow-DmIe5OUu.js +30 -0
  34. package/bizar-dash/dist/assets/SettingsRow-DmIe5OUu.js.map +1 -0
  35. package/bizar-dash/dist/assets/SettingsView-D1KmGWbi.js +48 -0
  36. package/bizar-dash/dist/assets/SettingsView-D1KmGWbi.js.map +1 -0
  37. package/bizar-dash/dist/assets/StatTile-BkIP32gG.js +11 -0
  38. package/bizar-dash/dist/assets/StatTile-BkIP32gG.js.map +1 -0
  39. package/bizar-dash/dist/assets/TasksView-Cba3GJ42.js +1 -0
  40. package/bizar-dash/dist/assets/TasksView-Cba3GJ42.js.map +1 -0
  41. package/bizar-dash/dist/assets/ViewHeader-D6c2cw1b.js +6 -0
  42. package/bizar-dash/dist/assets/ViewHeader-D6c2cw1b.js.map +1 -0
  43. package/bizar-dash/dist/assets/brain-DeoXNDE2.js +6 -0
  44. package/bizar-dash/dist/assets/brain-DeoXNDE2.js.map +1 -0
  45. package/bizar-dash/dist/assets/calendar-DRlQkGJc.js +6 -0
  46. package/bizar-dash/dist/assets/calendar-DRlQkGJc.js.map +1 -0
  47. package/bizar-dash/dist/assets/circle-check-DmYQ3vGp.js +6 -0
  48. package/bizar-dash/dist/assets/circle-check-DmYQ3vGp.js.map +1 -0
  49. package/bizar-dash/dist/assets/cmdk-BwN8hcKQ.js +80 -0
  50. package/bizar-dash/dist/assets/cmdk-BwN8hcKQ.js.map +1 -0
  51. package/bizar-dash/dist/assets/dnd-kit-DR2Qzzxk.js +5 -0
  52. package/bizar-dash/dist/assets/dnd-kit-DR2Qzzxk.js.map +1 -0
  53. package/bizar-dash/dist/assets/git-merge-OoqBKQer.js +6 -0
  54. package/bizar-dash/dist/assets/git-merge-OoqBKQer.js.map +1 -0
  55. package/bizar-dash/dist/assets/git-pull-request-CXBQTR2f.js +6 -0
  56. package/bizar-dash/dist/assets/git-pull-request-CXBQTR2f.js.map +1 -0
  57. package/bizar-dash/dist/assets/main-COdgjqbZ.css +1 -0
  58. package/bizar-dash/dist/assets/main-Cw6gybzS.js +109 -0
  59. package/bizar-dash/dist/assets/main-Cw6gybzS.js.map +1 -0
  60. package/bizar-dash/dist/assets/message-square-DUTeIQAR.js +6 -0
  61. package/bizar-dash/dist/assets/message-square-DUTeIQAR.js.map +1 -0
  62. package/bizar-dash/dist/assets/minus-CrStJTZ7.js +6 -0
  63. package/bizar-dash/dist/assets/minus-CrStJTZ7.js.map +1 -0
  64. package/bizar-dash/dist/assets/wrench-BJ1WIC4u.js +6 -0
  65. package/bizar-dash/dist/assets/wrench-BJ1WIC4u.js.map +1 -0
  66. package/bizar-dash/dist/{mobile.html → v8.html} +6 -9
  67. package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  68. package/bizar-dash/src/server/server.mjs +1 -57
  69. package/bizar-dash/src/web/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  70. package/bizar-dash/src/web/v8/App.tsx +188 -0
  71. package/bizar-dash/src/web/v8/__tests__/App.test.tsx +102 -0
  72. package/bizar-dash/src/web/v8/__tests__/AppShell.test.tsx +79 -0
  73. package/bizar-dash/src/web/v8/__tests__/PageSkeleton.test.tsx +12 -0
  74. package/bizar-dash/src/web/v8/__tests__/SettingsView.test.tsx +84 -0
  75. package/bizar-dash/src/web/v8/__tests__/Sidebar.test.tsx +95 -0
  76. package/bizar-dash/src/web/v8/__tests__/activity.test.tsx +63 -0
  77. package/bizar-dash/src/web/v8/__tests__/agents.test.tsx +90 -0
  78. package/bizar-dash/src/web/v8/__tests__/controls.test.tsx +135 -0
  79. package/bizar-dash/src/web/v8/__tests__/cx.test.ts +25 -0
  80. package/bizar-dash/src/web/v8/__tests__/data.test.tsx +293 -0
  81. package/bizar-dash/src/web/v8/__tests__/feedback.test.tsx +213 -0
  82. package/bizar-dash/src/web/v8/__tests__/goals.test.tsx +105 -0
  83. package/bizar-dash/src/web/v8/__tests__/kanban.test.tsx +176 -0
  84. package/bizar-dash/src/web/v8/__tests__/libraries.test.tsx +70 -0
  85. package/bizar-dash/src/web/v8/__tests__/memory.test.tsx +60 -0
  86. package/bizar-dash/src/web/v8/__tests__/navigation.test.tsx +137 -0
  87. package/bizar-dash/src/web/v8/__tests__/settings.test.tsx +104 -0
  88. package/bizar-dash/src/web/v8/__tests__/theme.test.tsx +98 -0
  89. package/bizar-dash/src/web/v8/__tests__/views.test.tsx +112 -0
  90. package/bizar-dash/src/web/v8/main.tsx +28 -0
  91. package/bizar-dash/src/web/v8/shell/AppShell.tsx +90 -0
  92. package/bizar-dash/src/web/v8/shell/PageSkeleton.tsx +29 -0
  93. package/bizar-dash/src/web/v8/shell/Sidebar.tsx +361 -0
  94. package/bizar-dash/src/web/v8/shell/StatusBar.tsx +63 -0
  95. package/bizar-dash/src/web/v8/shell/Topbar.tsx +113 -0
  96. package/bizar-dash/src/web/v8/shell/index.ts +15 -0
  97. package/bizar-dash/src/web/v8/ui/activity/ActivityFeed.tsx +137 -0
  98. package/bizar-dash/src/web/v8/ui/agents/AgentActivity.tsx +101 -0
  99. package/bizar-dash/src/web/v8/ui/agents/AgentCard.tsx +158 -0
  100. package/bizar-dash/src/web/v8/ui/controls/Button.tsx +141 -0
  101. package/bizar-dash/src/web/v8/ui/controls/ButtonGroup.tsx +32 -0
  102. package/bizar-dash/src/web/v8/ui/controls/Checkbox.tsx +49 -0
  103. package/bizar-dash/src/web/v8/ui/controls/Field.tsx +78 -0
  104. package/bizar-dash/src/web/v8/ui/controls/Form.tsx +23 -0
  105. package/bizar-dash/src/web/v8/ui/controls/IconButton.tsx +112 -0
  106. package/bizar-dash/src/web/v8/ui/controls/Input.tsx +94 -0
  107. package/bizar-dash/src/web/v8/ui/controls/RadioGroup.tsx +74 -0
  108. package/bizar-dash/src/web/v8/ui/controls/Select.tsx +171 -0
  109. package/bizar-dash/src/web/v8/ui/controls/Slider.tsx +68 -0
  110. package/bizar-dash/src/web/v8/ui/controls/Switch.tsx +56 -0
  111. package/bizar-dash/src/web/v8/ui/controls/Textarea.tsx +64 -0
  112. package/bizar-dash/src/web/v8/ui/controls/Toggle.tsx +36 -0
  113. package/bizar-dash/src/web/v8/ui/controls/ToggleGroup.tsx +85 -0
  114. package/bizar-dash/src/web/v8/ui/data/Accordion.tsx +134 -0
  115. package/bizar-dash/src/web/v8/ui/data/Avatar.tsx +175 -0
  116. package/bizar-dash/src/web/v8/ui/data/Badge.tsx +93 -0
  117. package/bizar-dash/src/web/v8/ui/data/BarList.tsx +101 -0
  118. package/bizar-dash/src/web/v8/ui/data/Card.tsx +140 -0
  119. package/bizar-dash/src/web/v8/ui/data/Chip.tsx +96 -0
  120. package/bizar-dash/src/web/v8/ui/data/Kbd.tsx +42 -0
  121. package/bizar-dash/src/web/v8/ui/data/ProgressBar.tsx +93 -0
  122. package/bizar-dash/src/web/v8/ui/data/Sparkline.tsx +98 -0
  123. package/bizar-dash/src/web/v8/ui/data/StatTile.tsx +150 -0
  124. package/bizar-dash/src/web/v8/ui/data/Table.tsx +170 -0
  125. package/bizar-dash/src/web/v8/ui/data/Timeline.tsx +130 -0
  126. package/bizar-dash/src/web/v8/ui/data/ViewHeader.tsx +122 -0
  127. package/bizar-dash/src/web/v8/ui/feedback/Alert.tsx +95 -0
  128. package/bizar-dash/src/web/v8/ui/feedback/Banner.tsx +39 -0
  129. package/bizar-dash/src/web/v8/ui/feedback/ContextMenu.tsx +143 -0
  130. package/bizar-dash/src/web/v8/ui/feedback/Dialog.tsx +149 -0
  131. package/bizar-dash/src/web/v8/ui/feedback/Drawer.tsx +8 -0
  132. package/bizar-dash/src/web/v8/ui/feedback/DropdownMenu.tsx +231 -0
  133. package/bizar-dash/src/web/v8/ui/feedback/EmptyState.tsx +48 -0
  134. package/bizar-dash/src/web/v8/ui/feedback/Popover.tsx +56 -0
  135. package/bizar-dash/src/web/v8/ui/feedback/Sheet.tsx +103 -0
  136. package/bizar-dash/src/web/v8/ui/feedback/Skeleton.tsx +67 -0
  137. package/bizar-dash/src/web/v8/ui/feedback/Spinner.tsx +30 -0
  138. package/bizar-dash/src/web/v8/ui/feedback/Toast.tsx +66 -0
  139. package/bizar-dash/src/web/v8/ui/feedback/Tooltip.tsx +89 -0
  140. package/bizar-dash/src/web/v8/ui/goals/GoalCard.tsx +176 -0
  141. package/bizar-dash/src/web/v8/ui/goals/KeyResult.tsx +115 -0
  142. package/bizar-dash/src/web/v8/ui/index.ts +322 -0
  143. package/bizar-dash/src/web/v8/ui/kanban/KanbanBoard.tsx +80 -0
  144. package/bizar-dash/src/web/v8/ui/kanban/KanbanCard.tsx +247 -0
  145. package/bizar-dash/src/web/v8/ui/kanban/KanbanColumn.tsx +181 -0
  146. package/bizar-dash/src/web/v8/ui/kanban/KanbanContextMenu.tsx +122 -0
  147. package/bizar-dash/src/web/v8/ui/kanban/KanbanQuickAdd.tsx +141 -0
  148. package/bizar-dash/src/web/v8/ui/libraries/LibraryGrid.tsx +30 -0
  149. package/bizar-dash/src/web/v8/ui/libraries/LibraryItem.tsx +161 -0
  150. package/bizar-dash/src/web/v8/ui/memory/MemoryVault.tsx +144 -0
  151. package/bizar-dash/src/web/v8/ui/navigation/CommandPalette.tsx +224 -0
  152. package/bizar-dash/src/web/v8/ui/navigation/NavLink.tsx +81 -0
  153. package/bizar-dash/src/web/v8/ui/navigation/Pagination.tsx +164 -0
  154. package/bizar-dash/src/web/v8/ui/navigation/Tabs.tsx +128 -0
  155. package/bizar-dash/src/web/v8/ui/navigation/useCommandPaletteHotkey.test.ts +79 -0
  156. package/bizar-dash/src/web/v8/ui/primitives/Box.tsx +144 -0
  157. package/bizar-dash/src/web/v8/ui/primitives/Center.tsx +40 -0
  158. package/bizar-dash/src/web/v8/ui/primitives/Cluster.tsx +67 -0
  159. package/bizar-dash/src/web/v8/ui/primitives/Grid.tsx +104 -0
  160. package/bizar-dash/src/web/v8/ui/primitives/Inline.tsx +77 -0
  161. package/bizar-dash/src/web/v8/ui/primitives/Portal.tsx +33 -0
  162. package/bizar-dash/src/web/v8/ui/primitives/ScrollArea.tsx +62 -0
  163. package/bizar-dash/src/web/v8/ui/primitives/Separator.tsx +42 -0
  164. package/bizar-dash/src/web/v8/ui/primitives/Stack.tsx +73 -0
  165. package/bizar-dash/src/web/v8/ui/primitives/VisuallyHidden.tsx +49 -0
  166. package/bizar-dash/src/web/v8/ui/settings/SettingsNav.tsx +91 -0
  167. package/bizar-dash/src/web/v8/ui/settings/SettingsRow.tsx +88 -0
  168. package/bizar-dash/src/web/v8/ui/settings/SettingsSection.tsx +109 -0
  169. package/bizar-dash/src/web/v8/ui/styles/globals.css +220 -0
  170. package/bizar-dash/src/web/v8/ui/styles/reset.css +57 -0
  171. package/bizar-dash/src/web/v8/ui/styles/tokens.css +217 -0
  172. package/bizar-dash/src/web/v8/ui/theme/DensityProvider.test.tsx +74 -0
  173. package/bizar-dash/src/web/v8/ui/theme/DensityProvider.tsx +66 -0
  174. package/bizar-dash/src/web/v8/ui/theme/ThemeProvider.test.tsx +86 -0
  175. package/bizar-dash/src/web/v8/ui/theme/ThemeProvider.tsx +94 -0
  176. package/bizar-dash/src/web/v8/ui/theme/ThemeToggle.tsx +88 -0
  177. package/bizar-dash/src/web/v8/ui/theme/useDensity.ts +15 -0
  178. package/bizar-dash/src/web/v8/ui/theme/useTheme.ts +16 -0
  179. package/bizar-dash/src/web/v8/ui/utils/cx.ts +19 -0
  180. package/bizar-dash/src/web/v8/views/Activity/ActivityView.tsx +33 -0
  181. package/bizar-dash/src/web/v8/views/Agents/AgentsView.tsx +108 -0
  182. package/bizar-dash/src/web/v8/views/CommandPalette/AppCommandPalette.test.tsx +142 -0
  183. package/bizar-dash/src/web/v8/views/CommandPalette/AppCommandPalette.tsx +144 -0
  184. package/bizar-dash/src/web/v8/views/Goals/GoalsView.tsx +84 -0
  185. package/bizar-dash/src/web/v8/views/Libraries/LibrariesView.tsx +41 -0
  186. package/bizar-dash/src/web/v8/views/Memory/MemoryView.tsx +61 -0
  187. package/bizar-dash/src/web/v8/views/Overview/OverviewView.tsx +92 -0
  188. package/bizar-dash/src/web/v8/views/Router.tsx +96 -0
  189. package/bizar-dash/src/web/v8/views/Settings/SettingsView.tsx +305 -0
  190. package/bizar-dash/src/web/v8/views/Tasks/CommandPalettePlaceholder.tsx +106 -0
  191. package/bizar-dash/src/web/v8/views/Tasks/TasksKanbanPlaceholder.tsx +62 -0
  192. package/bizar-dash/src/web/v8/views/Tasks/TasksView.tsx +74 -0
  193. package/bizar-dash/src/web/v8/vite-env.d.ts +15 -0
  194. package/bizar-dash/src/web/{mobile.html → v8.html} +4 -7
  195. package/bizar-dash/tests/settings-layout.test.tsx +2 -2
  196. package/bizar-dash/tests/settings-nav.test.tsx +6 -6
  197. package/bizar-dash/tests/setup.ts +26 -0
  198. package/bizar-dash/vite.config.ts +26 -2
  199. package/bizar-dash/vitest.config.ts +5 -3
  200. package/cli/provision-claude.mjs +108 -5
  201. package/package.json +36 -3
  202. package/bizar-dash/dist/assets/EnvVarsSection-BfhAz3ax.js +0 -114
  203. package/bizar-dash/dist/assets/EnvVarsSection-BfhAz3ax.js.map +0 -1
  204. package/bizar-dash/dist/assets/MobileChat-DLWdv6tQ.js +0 -1
  205. package/bizar-dash/dist/assets/MobileChat-DLWdv6tQ.js.map +0 -1
  206. package/bizar-dash/dist/assets/MobileSettings-Bxv62uPY.js +0 -1
  207. package/bizar-dash/dist/assets/MobileSettings-Bxv62uPY.js.map +0 -1
  208. package/bizar-dash/dist/assets/Toast-ATSAmbdi.js +0 -26
  209. package/bizar-dash/dist/assets/Toast-ATSAmbdi.js.map +0 -1
  210. package/bizar-dash/dist/assets/__vite-browser-external-BIHI7g3E.js +0 -1
  211. package/bizar-dash/dist/assets/__vite-browser-external-BIHI7g3E.js.map +0 -1
  212. package/bizar-dash/dist/assets/main-306CZfn6.js +0 -430
  213. package/bizar-dash/dist/assets/main-306CZfn6.js.map +0 -1
  214. package/bizar-dash/dist/assets/main-G3bNDpY3.css +0 -1
  215. package/bizar-dash/dist/assets/mobile-C_naUN91.js +0 -1
  216. package/bizar-dash/dist/assets/mobile-C_naUN91.js.map +0 -1
  217. package/bizar-dash/dist/assets/mobile-layout-BWLtwtfQ.css +0 -1
  218. package/bizar-dash/dist/assets/mobile-layout-GZ3bE8L6.js +0 -211
  219. package/bizar-dash/dist/assets/mobile-layout-GZ3bE8L6.js.map +0 -1
  220. package/bizar-dash/dist/assets/useSlashCommands-BOwD7lNb.js +0 -65
  221. package/bizar-dash/dist/assets/useSlashCommands-BOwD7lNb.js.map +0 -1
  222. package/bizar-dash/dist/index.html +0 -43
  223. package/bizar-dash/src/web/App.tsx +0 -915
  224. package/bizar-dash/src/web/MobileApp.tsx +0 -414
  225. package/bizar-dash/src/web/components/ArtifactCreateDialog.tsx +0 -95
  226. package/bizar-dash/src/web/components/ArtifactListDialog.tsx +0 -55
  227. package/bizar-dash/src/web/components/ArtifactViewer.tsx +0 -144
  228. package/bizar-dash/src/web/components/AuditDialog.tsx +0 -79
  229. package/bizar-dash/src/web/components/AutosaveField.tsx +0 -50
  230. package/bizar-dash/src/web/components/BackupRestore.tsx +0 -330
  231. package/bizar-dash/src/web/components/BgStatusBadge.tsx +0 -32
  232. package/bizar-dash/src/web/components/Button.tsx +0 -56
  233. package/bizar-dash/src/web/components/CanvasContextMenu.tsx +0 -68
  234. package/bizar-dash/src/web/components/Card.tsx +0 -40
  235. package/bizar-dash/src/web/components/CollapsibleSection.tsx +0 -72
  236. package/bizar-dash/src/web/components/CommandDialog.tsx +0 -66
  237. package/bizar-dash/src/web/components/DoctorPanel.tsx +0 -160
  238. package/bizar-dash/src/web/components/EmptyState.tsx +0 -30
  239. package/bizar-dash/src/web/components/EnvVarManager.tsx +0 -430
  240. package/bizar-dash/src/web/components/EvalDiff.tsx +0 -157
  241. package/bizar-dash/src/web/components/EvalExportButton.tsx +0 -27
  242. package/bizar-dash/src/web/components/EvalRunCard.tsx +0 -78
  243. package/bizar-dash/src/web/components/FileBrowser.tsx +0 -633
  244. package/bizar-dash/src/web/components/HeadroomSettings.tsx +0 -418
  245. package/bizar-dash/src/web/components/HeadroomStatus.tsx +0 -158
  246. package/bizar-dash/src/web/components/HelpDialog.tsx +0 -55
  247. package/bizar-dash/src/web/components/InviteDialog.tsx +0 -205
  248. package/bizar-dash/src/web/components/KillConfirmDialog.tsx +0 -88
  249. package/bizar-dash/src/web/components/MobileBottomNav.tsx +0 -38
  250. package/bizar-dash/src/web/components/MobileDrawer.tsx +0 -68
  251. package/bizar-dash/src/web/components/MobileHeader.tsx +0 -50
  252. package/bizar-dash/src/web/components/MobileLayout.tsx +0 -63
  253. package/bizar-dash/src/web/components/Modal.tsx +0 -215
  254. package/bizar-dash/src/web/components/Notifications.tsx +0 -242
  255. package/bizar-dash/src/web/components/ScheduleTemplateCard.tsx +0 -72
  256. package/bizar-dash/src/web/components/ScreenshotCapture.tsx +0 -138
  257. package/bizar-dash/src/web/components/ScreenshotOCR.tsx +0 -42
  258. package/bizar-dash/src/web/components/SearchModal.tsx +0 -227
  259. package/bizar-dash/src/web/components/SettingsNav.tsx +0 -112
  260. package/bizar-dash/src/web/components/SettingsSearch.tsx +0 -328
  261. package/bizar-dash/src/web/components/Sidebar.tsx +0 -89
  262. package/bizar-dash/src/web/components/Spinner.tsx +0 -19
  263. package/bizar-dash/src/web/components/StatusBadge.tsx +0 -56
  264. package/bizar-dash/src/web/components/Tag.tsx +0 -41
  265. package/bizar-dash/src/web/components/TailscaleSettings.tsx +0 -161
  266. package/bizar-dash/src/web/components/TaskCard.tsx +0 -69
  267. package/bizar-dash/src/web/components/TaskCreateSheet.tsx +0 -76
  268. package/bizar-dash/src/web/components/Toast.tsx +0 -150
  269. package/bizar-dash/src/web/components/Toggle.tsx +0 -31
  270. package/bizar-dash/src/web/components/Topbar.tsx +0 -410
  271. package/bizar-dash/src/web/components/UsageChart.tsx +0 -250
  272. package/bizar-dash/src/web/components/UsageTable.tsx +0 -90
  273. package/bizar-dash/src/web/components/VirtualList.tsx +0 -53
  274. package/bizar-dash/src/web/components/VisualPlanDialog.tsx +0 -58
  275. package/bizar-dash/src/web/components/VoiceNotesPanel.tsx +0 -211
  276. package/bizar-dash/src/web/components/VoiceRecorder.tsx +0 -105
  277. package/bizar-dash/src/web/components/WorkspaceSelector.tsx +0 -158
  278. package/bizar-dash/src/web/components/agents/CommunicationLog.tsx +0 -126
  279. package/bizar-dash/src/web/components/agents/DependencyGraph.tsx +0 -163
  280. package/bizar-dash/src/web/components/agents/QualityGates.tsx +0 -123
  281. package/bizar-dash/src/web/components/agents/RealTimeEventLog.tsx +0 -127
  282. package/bizar-dash/src/web/components/agents/RoutingDecisions.test.tsx +0 -148
  283. package/bizar-dash/src/web/components/agents/RoutingDecisions.tsx +0 -160
  284. package/bizar-dash/src/web/components/background/AttachButton.tsx +0 -96
  285. package/bizar-dash/src/web/components/background/TmuxAttachCard.tsx +0 -122
  286. package/bizar-dash/src/web/components/chat/AgentChip.tsx +0 -68
  287. package/bizar-dash/src/web/components/chat/AgentNode.tsx +0 -127
  288. package/bizar-dash/src/web/components/chat/AgentTree.tsx +0 -80
  289. package/bizar-dash/src/web/components/chat/ChatBubble.tsx +0 -60
  290. package/bizar-dash/src/web/components/chat/ChatComposer.tsx +0 -72
  291. package/bizar-dash/src/web/components/chat/ChatInfoPanel.tsx +0 -370
  292. package/bizar-dash/src/web/components/chat/ChatRail.tsx +0 -387
  293. package/bizar-dash/src/web/components/chat/ChatThread.tsx +0 -127
  294. package/bizar-dash/src/web/components/chat/ChatTopBar.tsx +0 -50
  295. package/bizar-dash/src/web/components/chat/Composer.tsx +0 -166
  296. package/bizar-dash/src/web/components/chat/ConfirmModal.tsx +0 -54
  297. package/bizar-dash/src/web/components/chat/EmptyState.tsx +0 -21
  298. package/bizar-dash/src/web/components/chat/FirstRunGreeting.tsx +0 -18
  299. package/bizar-dash/src/web/components/chat/FloatingComposer.tsx +0 -38
  300. package/bizar-dash/src/web/components/chat/InfoPanel.tsx +0 -184
  301. package/bizar-dash/src/web/components/chat/JumpToLatest.tsx +0 -58
  302. package/bizar-dash/src/web/components/chat/LoadingSkeleton.tsx +0 -19
  303. package/bizar-dash/src/web/components/chat/MessageBlock.tsx +0 -260
  304. package/bizar-dash/src/web/components/chat/MessageBubble.tsx +0 -60
  305. package/bizar-dash/src/web/components/chat/SessionList.tsx +0 -163
  306. package/bizar-dash/src/web/components/chat/SessionRowMenu.tsx +0 -206
  307. package/bizar-dash/src/web/components/chat/StreamingIndicator.tsx +0 -37
  308. package/bizar-dash/src/web/components/chat/SuggestionCards.tsx +0 -55
  309. package/bizar-dash/src/web/components/chat/WelcomeScreen.tsx +0 -36
  310. package/bizar-dash/src/web/components/chat/_legacy.ts +0 -30
  311. package/bizar-dash/src/web/components/chat/index.ts +0 -31
  312. package/bizar-dash/src/web/components/chat/useAutoGrowTextarea.ts +0 -18
  313. package/bizar-dash/src/web/components/chat/useChat.ts +0 -1190
  314. package/bizar-dash/src/web/components/chat/useSlashCommands.ts +0 -47
  315. package/bizar-dash/src/web/components/goals/GoalInput.tsx +0 -86
  316. package/bizar-dash/src/web/components/goals/PlanVisualization.tsx +0 -111
  317. package/bizar-dash/src/web/components/tasks/BacklogPanel.css +0 -109
  318. package/bizar-dash/src/web/components/tasks/BacklogPanel.tsx +0 -209
  319. package/bizar-dash/src/web/hooks/useAutosave.ts +0 -107
  320. package/bizar-dash/src/web/hooks/useI18n.ts +0 -13
  321. package/bizar-dash/src/web/index.html +0 -37
  322. package/bizar-dash/src/web/lib/api.ts +0 -237
  323. package/bizar-dash/src/web/lib/goapPlanner.ts +0 -614
  324. package/bizar-dash/src/web/lib/i18n.ts +0 -25
  325. package/bizar-dash/src/web/lib/logger.ts +0 -30
  326. package/bizar-dash/src/web/lib/markdown.tsx +0 -59
  327. package/bizar-dash/src/web/lib/search.ts +0 -115
  328. package/bizar-dash/src/web/lib/types.ts +0 -894
  329. package/bizar-dash/src/web/lib/utils.ts +0 -114
  330. package/bizar-dash/src/web/lib/ws.ts +0 -177
  331. package/bizar-dash/src/web/locales/en.json +0 -52
  332. package/bizar-dash/src/web/main.tsx +0 -43
  333. package/bizar-dash/src/web/mobile/MobileApp.tsx +0 -309
  334. package/bizar-dash/src/web/mobile/MobileBottomNav.tsx +0 -38
  335. package/bizar-dash/src/web/mobile/MobileChat.tsx +0 -138
  336. package/bizar-dash/src/web/mobile/MobileDoctor.tsx +0 -80
  337. package/bizar-dash/src/web/mobile/MobileEval.tsx +0 -167
  338. package/bizar-dash/src/web/mobile/MobileMemory.tsx +0 -108
  339. package/bizar-dash/src/web/mobile/MobileOverview.tsx +0 -213
  340. package/bizar-dash/src/web/mobile/MobileSettings.tsx +0 -251
  341. package/bizar-dash/src/web/mobile/MobileTasks.tsx +0 -94
  342. package/bizar-dash/src/web/mobile/MobileTopbar.tsx +0 -115
  343. package/bizar-dash/src/web/mobile/components/MobileBottomSheet.tsx +0 -182
  344. package/bizar-dash/src/web/mobile/components/MobileListItem.tsx +0 -64
  345. package/bizar-dash/src/web/mobile/components/MobileModal.tsx +0 -123
  346. package/bizar-dash/src/web/mobile/views/MobileActivity.tsx +0 -273
  347. package/bizar-dash/src/web/mobile/views/MobileAgents.tsx +0 -374
  348. package/bizar-dash/src/web/mobile/views/MobileArtifactCanvas.tsx +0 -439
  349. package/bizar-dash/src/web/mobile/views/MobileArtifacts.tsx +0 -143
  350. package/bizar-dash/src/web/mobile/views/MobileChat.tsx +0 -257
  351. package/bizar-dash/src/web/mobile/views/MobileConfig.tsx +0 -175
  352. package/bizar-dash/src/web/mobile/views/MobileHistory.tsx +0 -191
  353. package/bizar-dash/src/web/mobile/views/MobileMods.tsx +0 -149
  354. package/bizar-dash/src/web/mobile/views/MobileMore.tsx +0 -121
  355. package/bizar-dash/src/web/mobile/views/MobileNotifications.tsx +0 -137
  356. package/bizar-dash/src/web/mobile/views/MobilePlans.tsx +0 -26
  357. package/bizar-dash/src/web/mobile/views/MobileSchedules.tsx +0 -685
  358. package/bizar-dash/src/web/mobile/views/MobileSearchModal.tsx +0 -172
  359. package/bizar-dash/src/web/mobile/views/MobileSettings.tsx +0 -363
  360. package/bizar-dash/src/web/mobile/views/MobileSkills.tsx +0 -153
  361. package/bizar-dash/src/web/mobile/views/MobileTasks.tsx +0 -564
  362. package/bizar-dash/src/web/mobile/views/QrCodePanel.tsx +0 -69
  363. package/bizar-dash/src/web/mobile.tsx +0 -14
  364. package/bizar-dash/src/web/styles/agents-redesign.css +0 -311
  365. package/bizar-dash/src/web/styles/chat.css +0 -1740
  366. package/bizar-dash/src/web/styles/glyphs.css +0 -960
  367. package/bizar-dash/src/web/styles/main.css +0 -10391
  368. package/bizar-dash/src/web/styles/memory.css +0 -1182
  369. package/bizar-dash/src/web/styles/minimax-usage.css +0 -842
  370. package/bizar-dash/src/web/styles/mobile-chat.css +0 -5
  371. package/bizar-dash/src/web/styles/mobile-layout.css +0 -233
  372. package/bizar-dash/src/web/styles/mobile.css +0 -2181
  373. package/bizar-dash/src/web/styles/settings.css +0 -795
  374. package/bizar-dash/src/web/styles/skills.css +0 -302
  375. package/bizar-dash/src/web/styles/tasks-redesign.css +0 -253
  376. package/bizar-dash/src/web/styles/tasks.css +0 -605
  377. package/bizar-dash/src/web/ui/controls/Button.tsx +0 -77
  378. package/bizar-dash/src/web/ui/controls/Checkbox.tsx +0 -95
  379. package/bizar-dash/src/web/ui/controls/IconButton.tsx +0 -66
  380. package/bizar-dash/src/web/ui/controls/Kbd.tsx +0 -24
  381. package/bizar-dash/src/web/ui/controls/NumberInput.tsx +0 -77
  382. package/bizar-dash/src/web/ui/controls/RadioGroup.tsx +0 -109
  383. package/bizar-dash/src/web/ui/controls/SearchInput.tsx +0 -91
  384. package/bizar-dash/src/web/ui/controls/Select.tsx +0 -92
  385. package/bizar-dash/src/web/ui/controls/Slider.tsx +0 -70
  386. package/bizar-dash/src/web/ui/controls/TextInput.tsx +0 -67
  387. package/bizar-dash/src/web/ui/controls/Textarea.tsx +0 -56
  388. package/bizar-dash/src/web/ui/controls/Toggle.tsx +0 -79
  389. package/bizar-dash/src/web/ui/controls/controls.css +0 -476
  390. package/bizar-dash/src/web/ui/controls/index.ts +0 -35
  391. package/bizar-dash/src/web/ui/data/BarChart.tsx +0 -60
  392. package/bizar-dash/src/web/ui/data/DataTable.tsx +0 -216
  393. package/bizar-dash/src/web/ui/data/EmptyState.tsx +0 -60
  394. package/bizar-dash/src/web/ui/data/ErrorState.tsx +0 -56
  395. package/bizar-dash/src/web/ui/data/KeyValueList.tsx +0 -106
  396. package/bizar-dash/src/web/ui/data/LoadingState.tsx +0 -65
  397. package/bizar-dash/src/web/ui/data/Sparkline.tsx +0 -87
  398. package/bizar-dash/src/web/ui/data/StatTile.tsx +0 -87
  399. package/bizar-dash/src/web/ui/data/data.css +0 -535
  400. package/bizar-dash/src/web/ui/data/index.ts +0 -31
  401. package/bizar-dash/src/web/ui/feedback/Badge.tsx +0 -49
  402. package/bizar-dash/src/web/ui/feedback/Dialog.tsx +0 -209
  403. package/bizar-dash/src/web/ui/feedback/ProgressBar.tsx +0 -69
  404. package/bizar-dash/src/web/ui/feedback/StatusDot.tsx +0 -50
  405. package/bizar-dash/src/web/ui/feedback/Toast.tsx +0 -200
  406. package/bizar-dash/src/web/ui/feedback/Tooltip.tsx +0 -126
  407. package/bizar-dash/src/web/ui/feedback/feedback.css +0 -314
  408. package/bizar-dash/src/web/ui/feedback/index.ts +0 -43
  409. package/bizar-dash/src/web/ui/index.ts +0 -56
  410. package/bizar-dash/src/web/ui/layout/AppShell.tsx +0 -51
  411. package/bizar-dash/src/web/ui/layout/Breadcrumbs.tsx +0 -97
  412. package/bizar-dash/src/web/ui/layout/Panel.tsx +0 -73
  413. package/bizar-dash/src/web/ui/layout/PanelHeader.tsx +0 -39
  414. package/bizar-dash/src/web/ui/layout/Sidebar.tsx +0 -131
  415. package/bizar-dash/src/web/ui/layout/Tabs.tsx +0 -133
  416. package/bizar-dash/src/web/ui/layout/Topbar.tsx +0 -42
  417. package/bizar-dash/src/web/ui/layout/ViewHeader.tsx +0 -48
  418. package/bizar-dash/src/web/ui/layout/index.ts +0 -34
  419. package/bizar-dash/src/web/ui/layout/layout.css +0 -464
  420. package/bizar-dash/src/web/ui/navigation/CommandPalette.tsx +0 -199
  421. package/bizar-dash/src/web/ui/navigation/NavGroup.tsx +0 -95
  422. package/bizar-dash/src/web/ui/navigation/NavLink.tsx +0 -93
  423. package/bizar-dash/src/web/ui/navigation/index.ts +0 -17
  424. package/bizar-dash/src/web/ui/navigation/navigation.css +0 -343
  425. package/bizar-dash/src/web/ui/primitives/Box.tsx +0 -91
  426. package/bizar-dash/src/web/ui/primitives/Grid.tsx +0 -49
  427. package/bizar-dash/src/web/ui/primitives/Inline.tsx +0 -55
  428. package/bizar-dash/src/web/ui/primitives/Separator.tsx +0 -46
  429. package/bizar-dash/src/web/ui/primitives/Stack.tsx +0 -54
  430. package/bizar-dash/src/web/ui/primitives/VisuallyHidden.tsx +0 -26
  431. package/bizar-dash/src/web/ui/primitives/index.ts +0 -23
  432. package/bizar-dash/src/web/ui/primitives/primitives.css +0 -183
  433. package/bizar-dash/src/web/ui/styles/globals.css +0 -86
  434. package/bizar-dash/src/web/ui/styles/reset.css +0 -125
  435. package/bizar-dash/src/web/ui/styles/tokens.css +0 -208
  436. package/bizar-dash/src/web/ui/theme/ThemeProvider.tsx +0 -142
  437. package/bizar-dash/src/web/ui/theme/tokens.ts +0 -131
  438. package/bizar-dash/src/web/ui/theme/useTheme.ts +0 -22
  439. package/bizar-dash/src/web/ui/utils/cx.ts +0 -42
  440. package/bizar-dash/src/web/views/Activity.tsx +0 -1221
  441. package/bizar-dash/src/web/views/Agents.tsx +0 -964
  442. package/bizar-dash/src/web/views/Artifacts.tsx +0 -1494
  443. package/bizar-dash/src/web/views/BackgroundAgents.tsx +0 -822
  444. package/bizar-dash/src/web/views/Chat.tsx +0 -532
  445. package/bizar-dash/src/web/views/Config.tsx +0 -3
  446. package/bizar-dash/src/web/views/Doctor.tsx +0 -317
  447. package/bizar-dash/src/web/views/Eval.tsx +0 -372
  448. package/bizar-dash/src/web/views/EvalReport.tsx +0 -353
  449. package/bizar-dash/src/web/views/GoalPlanner.tsx +0 -158
  450. package/bizar-dash/src/web/views/Harness.tsx +0 -306
  451. package/bizar-dash/src/web/views/History.tsx +0 -353
  452. package/bizar-dash/src/web/views/Memory.tsx +0 -161
  453. package/bizar-dash/src/web/views/MiniMaxUsage.tsx +0 -942
  454. package/bizar-dash/src/web/views/ModView.tsx +0 -207
  455. package/bizar-dash/src/web/views/Mods.tsx +0 -706
  456. package/bizar-dash/src/web/views/Overview.tsx +0 -1364
  457. package/bizar-dash/src/web/views/Providers.tsx +0 -302
  458. package/bizar-dash/src/web/views/Schedules.tsx +0 -902
  459. package/bizar-dash/src/web/views/Settings.tsx +0 -308
  460. package/bizar-dash/src/web/views/Skills.tsx +0 -383
  461. package/bizar-dash/src/web/views/SpawnAgentModal.tsx +0 -221
  462. package/bizar-dash/src/web/views/Tasks.tsx +0 -789
  463. package/bizar-dash/src/web/views/Workspace.tsx +0 -294
  464. package/bizar-dash/src/web/views/glyphs/GlyphRenderer.tsx +0 -844
  465. package/bizar-dash/src/web/views/glyphs/components.tsx +0 -1098
  466. package/bizar-dash/src/web/views/memory/ConfigPanel.tsx +0 -426
  467. package/bizar-dash/src/web/views/memory/FromScreenshotPanel.tsx +0 -23
  468. package/bizar-dash/src/web/views/memory/GitSyncPanel.tsx +0 -220
  469. package/bizar-dash/src/web/views/memory/LightragPanel.tsx +0 -310
  470. package/bizar-dash/src/web/views/memory/MemoryGraphLegend.tsx +0 -29
  471. package/bizar-dash/src/web/views/memory/MemoryGraphPanel.tsx +0 -192
  472. package/bizar-dash/src/web/views/memory/MemoryGraphView.tsx +0 -336
  473. package/bizar-dash/src/web/views/memory/MemoryOverview.tsx +0 -367
  474. package/bizar-dash/src/web/views/memory/MemoryStatusCard.tsx +0 -160
  475. package/bizar-dash/src/web/views/memory/ObsidianPanel.tsx +0 -650
  476. package/bizar-dash/src/web/views/memory/SemanticSearchPanel.tsx +0 -197
  477. package/bizar-dash/src/web/views/memory/VaultFromClipboardPanel.tsx +0 -101
  478. package/bizar-dash/src/web/views/settings/ActivitySection.tsx +0 -205
  479. package/bizar-dash/src/web/views/settings/AgentSection.tsx +0 -305
  480. package/bizar-dash/src/web/views/settings/AuthSection.tsx +0 -159
  481. package/bizar-dash/src/web/views/settings/EnvVarsSection.tsx +0 -15
  482. package/bizar-dash/src/web/views/settings/GeneralSection.tsx +0 -128
  483. package/bizar-dash/src/web/views/settings/HeadroomSection.tsx +0 -39
  484. package/bizar-dash/src/web/views/settings/MemorySection.tsx +0 -154
  485. package/bizar-dash/src/web/views/settings/NetworkSection.tsx +0 -87
  486. package/bizar-dash/src/web/views/settings/NotificationsSection.tsx +0 -34
  487. package/bizar-dash/src/web/views/settings/SystemLlmSection.tsx +0 -81
  488. package/bizar-dash/src/web/views/settings/ThemeSection.tsx +0 -168
  489. package/bizar-dash/src/web/views/settings/UpdatesSection.tsx +0 -256
  490. package/bizar-dash/src/web/views/settings/WorkspacesSection.tsx +0 -189
@@ -1,1364 +0,0 @@
1
- // src/views/Overview.tsx — Wave 3 redesign.
2
- //
3
- // Hero dashboard, rebuilt on the new design system (../ui). Same external
4
- // surface (the `Overview({ snapshot, settings, activeTab, setActiveTab,
5
- // refreshSnapshot })` signature + named export) so App.tsx and every test
6
- // continues to import without change. The layout now reads top-to-bottom
7
- // as five sections, each rendered with the new primitives:
8
- //
9
- // 1. Header — title, active-project selector, refresh, theme toggle.
10
- // 2. Stat tile row — five KPIs (Agents / Tasks / Projects / Mods /
11
- // Background) using <StatTile>, mono values, single-accent icon.
12
- // 3. Chart grid — three panels of trend/sparkline + <BarChart>
13
- // (Tasks 30d / Agent activity / Tasks by status), then three
14
- // <Panel> surfaces (Memory / Active project / Recent mods). The
15
- // Memory slot wraps the existing <MemoryStatusCard> unchanged so
16
- // the Wave-3 redesign covers it without forking the card.
17
- // 4. Activity stream — <Panel> + compact inline list (mono timestamp,
18
- // severity-coloured icon, humanised title, slug summary, per-row
19
- // hide IconButton). Hidden keys are reconciled against
20
- // /activity/hidden (GET on mount) and POST /activity/hide (with
21
- // rollback on failure), matching the v3.15.0 server contract.
22
- // 5. Quick prompts — textarea + "Submit to Odin" button + eight
23
- // suggestion chips. Calls POST /tasks/submit and refreshes.
24
- //
25
- // All modals (AddProjectDialog, AddModDialog, etc.) still use the
26
- // legacy <Modal> stack — Wave 3 only reskins the chrome, not the
27
- // dialog layer. Everything else (Card, Button, VirtualList, useToast,
28
- // useModal, applyTheme, MemoryStatusCard) stays imported from its
29
- // existing path so the v6.x surface contracts are preserved.
30
- //
31
- // Tokens come exclusively from `ui/styles/tokens.css`; the only inline
32
- // styles used are narrow one-off decisions (mono font for timestamps,
33
- // overflow ellipsis on long paths). No gradients, no shadows beyond
34
- // hairline, no radius > 6px, no hardcoded colours. Dark mode is free
35
- // because the design system ships its own overrides.
36
-
37
- import React, { useEffect, useMemo, useRef, useState } from 'react';
38
- import {
39
- Bot,
40
- CheckSquare,
41
- Folder,
42
- Puzzle,
43
- PlayCircle,
44
- RefreshCw,
45
- Map,
46
- Send,
47
- Sparkles,
48
- AlertOctagon,
49
- AlertTriangle,
50
- Activity,
51
- Zap,
52
- EyeOff,
53
- Eye,
54
- X,
55
- Sun,
56
- Moon,
57
- } from 'lucide-react';
58
-
59
- import {
60
- Box,
61
- Stack,
62
- Inline,
63
- Grid,
64
- Panel,
65
- StatTile,
66
- Sparkline,
67
- BarChart,
68
- KeyValueList,
69
- Textarea,
70
- IconButton,
71
- Button as UiButton,
72
- Badge,
73
- StatusDot,
74
- EmptyState,
75
- LoadingState,
76
- } from '../ui';
77
-
78
- // Legacy components kept on purpose — the dialogs + their footers
79
- // remain tied to the legacy <Modal> stack until a Wave-4 migration.
80
- import { Button } from '../components/Button';
81
- import { MemoryStatusCard } from './memory/MemoryStatusCard';
82
- import { useToast } from '../components/Toast';
83
- import { useModal } from '../components/Modal';
84
- import { FileBrowser } from '../components/FileBrowser';
85
- import { api } from '../lib/api';
86
- import { formatRelative } from '../lib/utils';
87
- import type {
88
- ActivityItem,
89
- Mod,
90
- Overview,
91
- ProjectRecord,
92
- ScanResult,
93
- Settings,
94
- Snapshot,
95
- DirectoryListing,
96
- } from '../lib/types';
97
-
98
- type Props = {
99
- snapshot: Snapshot;
100
- settings: Settings;
101
- activeTab: string;
102
- setActiveTab: (id: string) => void;
103
- refreshSnapshot: () => Promise<void>;
104
- };
105
-
106
- function OverviewInner({
107
- snapshot,
108
- settings,
109
- setActiveTab,
110
- refreshSnapshot,
111
- }: Props) {
112
- const toast = useToast();
113
- const modal = useModal();
114
- const inputRef = useRef<HTMLTextAreaElement | null>(null);
115
-
116
- const [overview, setOverview] = useState<Overview | null>(
117
- snapshot.overview ?? null,
118
- );
119
- const [loading, setLoading] = useState(!snapshot.overview);
120
- const [projects, setProjects] = useState<ProjectRecord[]>(
121
- snapshot.projects || [],
122
- );
123
- const [activeId, setActiveId] = useState<string | null>(
124
- snapshot.activeProject?.id || null,
125
- );
126
- const [mods, setMods] = useState<Mod[]>(snapshot.mods || []);
127
- const [submitting, setSubmitting] = useState(false);
128
-
129
- // Live activity feed via SSE — see v3.7.0.
130
- const [activityItems, setActivityItems] = useState<ActivityItem[]>(
131
- snapshot.overview?.recentActivity ?? [],
132
- );
133
- // Hidden set lives server-side; we mirror a Set locally so the UI can
134
- // hide without losing the row from the underlying store. v3.15.0.
135
- const [hiddenKeys, setHiddenKeys] = useState<Set<string>>(new Set());
136
-
137
- // Theme toggle — local because ThemeProvider doesn't wrap the app at
138
- // the App.tsx level yet. Toggles `data-theme` on <html>; tokens.css
139
- // handles the actual colour swap.
140
- const [isDark, setIsDark] = useState<boolean>(() => {
141
- if (typeof document === 'undefined') return true;
142
- const attr = document.documentElement.getAttribute('data-theme');
143
- return attr !== 'light';
144
- });
145
-
146
- useEffect(() => {
147
- if (typeof document === 'undefined') return;
148
- if (isDark) document.documentElement.removeAttribute('data-theme');
149
- else document.documentElement.setAttribute('data-theme', 'light');
150
- }, [isDark]);
151
-
152
- // Load hidden set once on mount — v3.15.0.
153
- useEffect(() => {
154
- let cancelled = false;
155
- (async () => {
156
- try {
157
- const r = await api.get<{ hidden: string[] }>('/activity/hidden');
158
- if (!cancelled) setHiddenKeys(new Set(r.hidden || []));
159
- } catch {
160
- /* non-fatal — fall back to empty set */
161
- }
162
- })();
163
- return () => {
164
- cancelled = true;
165
- };
166
- }, []);
167
-
168
- // Stable per-row key (matches the server-side derivation in v3.15.0).
169
- const itemKey = (it: ActivityItem, idx: number): string => {
170
- const k = `${it.kind || ''}|${it.ts || ''}|${(it.slug as string) || (it.title as string) || ''}|${idx}`;
171
- let h = 0;
172
- for (let i = 0; i < k.length; i++) h = ((h << 5) - h + k.charCodeAt(i)) | 0;
173
- return Math.abs(h).toString(16).padStart(8, '0').slice(0, 16);
174
- };
175
-
176
- const onHide = async (key: string) => {
177
- const previous = hiddenKeys;
178
- const next = new Set(previous);
179
- next.add(key);
180
- setHiddenKeys(next);
181
- try {
182
- await api.post('/activity/hide', { keys: [key] });
183
- } catch (err) {
184
- setHiddenKeys(previous); // rollback
185
- toast.error(`Hide failed: ${(err as Error).message}`);
186
- }
187
- };
188
-
189
- const onClearAll = async () => {
190
- if (
191
- !window.confirm(
192
- 'Hide every recent activity item from the overview? The full log stays in Settings → Activity Log.',
193
- )
194
- )
195
- return;
196
- const all = activityItems.map((it, idx) => itemKey(it, idx));
197
- const next = new Set(hiddenKeys);
198
- all.forEach((k) => next.add(k));
199
- setHiddenKeys(next);
200
- try {
201
- await api.post('/activity/hide', { keys: all });
202
- toast.success(`Hidden ${all.length} item(s).`);
203
- } catch (err) {
204
- toast.error(`Clear failed: ${(err as Error).message}`);
205
- }
206
- };
207
-
208
- const onRestoreAll = async () => {
209
- setHiddenKeys(new Set());
210
- try {
211
- await api.del('/activity/hide');
212
- toast.success('All hidden activity restored.');
213
- } catch (err) {
214
- toast.error(`Restore failed: ${(err as Error).message}`);
215
- }
216
- };
217
-
218
- // Pull initial snapshot data into local state. v3.7.0.
219
- useEffect(() => {
220
- if (snapshot.overview) {
221
- setOverview(snapshot.overview);
222
- setActivityItems(snapshot.overview.recentActivity ?? []);
223
- setLoading(false);
224
- }
225
- setProjects(snapshot.projects || []);
226
- setActiveId(snapshot.activeProject?.id || null);
227
- setMods(snapshot.mods || []);
228
- }, [snapshot.overview, snapshot.projects, snapshot.activeProject, snapshot.mods]);
229
-
230
- // SSE activity stream — appends on `activity`, replaces on `snapshot`.
231
- useEffect(() => {
232
- let es: EventSource | null = null;
233
- try {
234
- const tok = api.getToken();
235
- const url = tok
236
- ? `/api/activity/stream?token=${encodeURIComponent(tok)}`
237
- : '/api/activity/stream';
238
- es = new EventSource(url);
239
- es.addEventListener('snapshot', (e) => {
240
- try {
241
- const parsed = JSON.parse((e as MessageEvent).data) as {
242
- events: ActivityItem[];
243
- };
244
- setActivityItems(
245
- Array.isArray(parsed.events) ? parsed.events.slice(0, 50) : [],
246
- );
247
- } catch {
248
- /* ignore */
249
- }
250
- });
251
- es.addEventListener('activity', (e) => {
252
- try {
253
- const entry = JSON.parse((e as MessageEvent).data) as ActivityItem;
254
- setActivityItems((cur) => [entry, ...cur].slice(0, 50));
255
- } catch {
256
- /* ignore */
257
- }
258
- });
259
- } catch {
260
- /* SSE unavailable — fallback to snapshot data */
261
- }
262
- return () => {
263
- try {
264
- es?.close();
265
- } catch {
266
- /* ignore */
267
- }
268
- };
269
- }, []);
270
-
271
- const onRefresh = async () => {
272
- toast.info('Refreshing…', 1500);
273
- await refreshSnapshot();
274
- try {
275
- const data = await api.get<{
276
- projects: ProjectRecord[];
277
- active: string | null;
278
- }>('/projects');
279
- setProjects(data.projects || []);
280
- setActiveId(data.active || null);
281
- } catch {
282
- /* ignore */
283
- }
284
- };
285
-
286
- const onAddProject = () => {
287
- modal.open({
288
- title: 'Add project',
289
- children: (
290
- <AddProjectDialog
291
- settings={settings}
292
- onAdd={async (path: string, name: string | null) => {
293
- try {
294
- const r = await api.post<ProjectRecord>('/projects', {
295
- path,
296
- name,
297
- });
298
- setProjects((cur) => [...cur.filter((p) => p.id !== r.id), r]);
299
- toast.success('Project added.');
300
- modal.close();
301
- } catch (err) {
302
- toast.error(`Add failed: ${(err as Error).message}`);
303
- }
304
- }}
305
- />
306
- ),
307
- footer: (
308
- <div className="modal-footer-actions">
309
- <Button variant="ghost" onClick={() => modal.close()}>
310
- Cancel
311
- </Button>
312
- </div>
313
- ),
314
- });
315
- };
316
-
317
- const onUseCurrentDir = async () => {
318
- try {
319
- const data = await api.post<{
320
- projects: ProjectRecord[];
321
- active: string | null;
322
- }>('/projects/auto-detect');
323
- setProjects(data.projects || []);
324
- setActiveId(data.active || null);
325
- const newActive = data.projects?.find((p) => p.id === data.active);
326
- toast.success(newActive ? `Active: ${newActive.name}` : 'Projects refreshed.');
327
- await refreshSnapshot();
328
- } catch (err) {
329
- toast.error(`Auto-detect failed: ${(err as Error).message}`);
330
- }
331
- };
332
-
333
- const onActivate = async (id: string) => {
334
- try {
335
- await api.post(`/projects/${encodeURIComponent(id)}/activate`);
336
- setActiveId(id);
337
- toast.success('Project activated.');
338
- await refreshSnapshot();
339
- } catch (err) {
340
- toast.error(`Activate failed: ${(err as Error).message}`);
341
- }
342
- };
343
-
344
- const onScan = async () => {
345
- if (!settings?.dashboard?.projectsDirectory) {
346
- toast.warning('No projects directory configured.');
347
- return;
348
- }
349
- try {
350
- const r = await api.post<ScanResult>('/projects/scan');
351
- if (r.error) {
352
- toast.error(r.error);
353
- } else {
354
- toast.success(`Added ${r.added.length}, skipped ${r.skipped}.`);
355
- }
356
- await refreshSnapshot();
357
- const data = await api.get<{
358
- projects: ProjectRecord[];
359
- active: string | null;
360
- }>('/projects');
361
- setProjects(data.projects || []);
362
- setActiveId(data.active || null);
363
- } catch (err) {
364
- toast.error(`Scan failed: ${(err as Error).message}`);
365
- }
366
- };
367
-
368
- // ─── Derived chart data ─────────────────────────────────────────────
369
-
370
- // Tasks by status — straight group-by on the live Task[] payload.
371
- const tasksByStatus = useMemo(() => {
372
- const buckets: Record<string, number> = {};
373
- (snapshot.tasks || []).forEach((t) => {
374
- buckets[t.status] = (buckets[t.status] || 0) + 1;
375
- });
376
- const palette: Record<string, string> = {
377
- queued: 'var(--chart-3)',
378
- doing: 'var(--info)',
379
- done: 'var(--chart-1)',
380
- blocked: 'var(--warning)',
381
- archived: 'var(--text-tertiary)',
382
- };
383
- return Object.entries(buckets).map(([status, value]) => ({
384
- label: status,
385
- value,
386
- color: palette[status] ?? 'var(--chart-2)',
387
- }));
388
- }, [snapshot.tasks]);
389
-
390
- // Tasks over the last 30 days — buckets by createdAt, padded to length 30.
391
- // We seed earlier days from the most recent bucket to keep the sparkline
392
- // visibly anchored to the current count instead of flat-zero.
393
- const tasksSparkline = useMemo(() => {
394
- const days = 30;
395
- const series = new Array(days).fill(0);
396
- const now = Date.now();
397
- (snapshot.tasks || []).forEach((t) => {
398
- const ts = Date.parse(t.createdAt || '');
399
- if (Number.isNaN(ts)) return;
400
- const offset = Math.floor((now - ts) / (24 * 3600 * 1000));
401
- if (offset >= 0 && offset < days) {
402
- series[days - 1 - offset] += 1;
403
- }
404
- });
405
- // pad earlier days with a smoothed copy of the most recent bucket
406
- const tail = series[series.length - 1] || 1;
407
- for (let i = 0; i < days - 1; i++) {
408
- if (series[i] === 0) series[i] = Math.max(1, Math.round(tail * (0.6 + i / days * 0.4)));
409
- }
410
- return series;
411
- }, [snapshot.tasks]);
412
-
413
- // Agent activity sparkline — built from distinct assignee names per day.
414
- const agentsSparkline = useMemo(() => {
415
- const days = 30;
416
- const series = new Array(days).fill(0);
417
- const now = Date.now();
418
- (snapshot.tasks || []).forEach((t) => {
419
- if (!t.assignee) return;
420
- const ts = Date.parse(t.updatedAt || t.createdAt || '');
421
- if (Number.isNaN(ts)) return;
422
- const offset = Math.floor((now - ts) / (24 * 3600 * 1000));
423
- if (offset >= 0 && offset < days) series[days - 1 - offset] += 1;
424
- });
425
- const tail = series[series.length - 1] || 1;
426
- for (let i = 0; i < days - 1; i++) {
427
- if (series[i] === 0) series[i] = Math.max(1, Math.round(tail * (0.6 + i / days * 0.4)));
428
- }
429
- return series;
430
- }, [snapshot.tasks]);
431
-
432
- // Background count proxy — assigned tasks whose status is in flight.
433
- const backgroundCount = useMemo(() => {
434
- return (snapshot.tasks || []).filter(
435
- (t) => !!t.assignee && (t.status === 'doing' || t.status === 'queued'),
436
- ).length;
437
- }, [snapshot.tasks]);
438
-
439
- const tasksTotal = (snapshot.tasks || []).length;
440
-
441
- // Active project — derive from activeProject or fall back to the first.
442
- const activeProject = useMemo<ProjectRecord | null>(() => {
443
- if (snapshot.activeProject) return snapshot.activeProject;
444
- if (!activeId) return projects[0] ?? null;
445
- return projects.find((p) => p.id === activeId) ?? null;
446
- }, [snapshot.activeProject, activeId, projects]);
447
-
448
- const visibleActivity = useMemo(
449
- () =>
450
- activityItems
451
- .slice(0, 30)
452
- .map((it, idx) => ({ it, idx, key: itemKey(it, idx) }))
453
- .filter(({ key }) => !hiddenKeys.has(key)),
454
- [activityItems, hiddenKeys],
455
- );
456
-
457
- if (loading || !overview) {
458
- return (
459
- <Box as="div" className="view view-overview" bg="0" p={7}>
460
- <Stack direction="row" align="center" justify="center">
461
- <LoadingState label="Loading overview…" />
462
- </Stack>
463
- </Box>
464
- );
465
- }
466
-
467
- return (
468
- <Box as="div" className="view view-overview" bg="0" p={7}>
469
- <Stack gap={6}>
470
- {/* ─── Section 1: Header ─────────────────────────────────────── */}
471
- <Inline justify="between" align="center" gap={3} wrap>
472
- <Stack gap={1}>
473
- <h1
474
- className="overview-title"
475
- style={{
476
- fontSize: 'var(--text-2xl)',
477
- fontWeight: 'var(--weight-semibold)',
478
- margin: 0,
479
- }}
480
- >
481
- Overview
482
- </h1>
483
- <span
484
- className="overview-subtitle muted"
485
- style={{ fontSize: 'var(--text-sm)', color: 'var(--text-secondary)' }}
486
- >
487
- {activeProject
488
- ? `Active: ${activeProject.name}`
489
- : 'No active project — pick one below.'}
490
- </span>
491
- </Stack>
492
-
493
- <Inline gap={2} align="center" wrap>
494
- {projects.length > 0 && (
495
- <span
496
- className="select select-size-sm overview-project-select"
497
- aria-label="Switch active project"
498
- >
499
- <select
500
- className="select-native"
501
- value={activeId ?? ''}
502
- onChange={async (e) => {
503
- const id = e.target.value;
504
- if (!id || id === activeId) return;
505
- await onActivate(id);
506
- }}
507
- aria-label="Switch active project"
508
- data-testid="overview-project-select"
509
- >
510
- {projects.map((p) => (
511
- <option key={p.id} value={p.id}>
512
- {p.name}
513
- </option>
514
- ))}
515
- </select>
516
- <svg
517
- className="select-caret"
518
- width="14"
519
- height="14"
520
- viewBox="0 0 24 24"
521
- fill="none"
522
- stroke="currentColor"
523
- strokeWidth="2"
524
- aria-hidden
525
- >
526
- <polyline points="6 9 12 15 18 9" />
527
- </svg>
528
- </span>
529
- )}
530
- <UiButton
531
- variant="secondary"
532
- size="sm"
533
- onClick={onRefresh}
534
- icon={<RefreshCw size={12} />}
535
- data-testid="overview-refresh"
536
- >
537
- Refresh
538
- </UiButton>
539
- <IconButton
540
- variant="ghost"
541
- size="md"
542
- onClick={() => setIsDark((v) => !v)}
543
- aria-label={isDark ? 'Switch to light theme' : 'Switch to dark theme'}
544
- icon={isDark ? <Sun size={14} /> : <Moon size={14} />}
545
- />
546
- </Inline>
547
- </Inline>
548
-
549
- {/* ─── Section 2: Stat tiles ────────────────────────────────── */}
550
- <Grid
551
- cols={5}
552
- autoFit
553
- gap={3}
554
- style={{ minHeight: 'unset' }}
555
- data-testid="overview-stat-row"
556
- >
557
- <StatTile
558
- label="Agents"
559
- value={overview.counts.agents}
560
- icon={<Bot size={18} />}
561
- href="#"
562
- data-testid="overview-stat-agents"
563
- />
564
- <StatTile
565
- label="Tasks"
566
- value={tasksTotal}
567
- icon={<CheckSquare size={18} />}
568
- data-testid="overview-stat-tasks"
569
- />
570
- <StatTile
571
- label="Projects"
572
- value={projects.length}
573
- icon={<Folder size={18} />}
574
- data-testid="overview-stat-projects"
575
- />
576
- <StatTile
577
- label="Mods"
578
- value={mods.length}
579
- icon={<Puzzle size={18} />}
580
- data-testid="overview-stat-mods"
581
- />
582
- <StatTile
583
- label="Background"
584
- value={backgroundCount}
585
- icon={<PlayCircle size={18} />}
586
- data-testid="overview-stat-background"
587
- />
588
- </Grid>
589
-
590
- {/* ─── Section 3: Chart grid (3 × 2) ────────────────────────── */}
591
- <Grid cols={3} autoFit gap={3}>
592
- <Panel
593
- title="Tasks · last 30 days"
594
- description="Bucketed from snapshot.tasks createdAt"
595
- padding={3}
596
- data-testid="overview-panel-tasks-spark"
597
- >
598
- <Box p={3}>
599
- <Stack direction="row" justify="between" align="end" gap={3}>
600
- <Sparkline
601
- data={tasksSparkline}
602
- width={220}
603
- height={56}
604
- ariaLabel="Tasks created per day, last 30"
605
- />
606
- <Box
607
- as="div"
608
- className="overview-spark-meta"
609
- style={{
610
- fontFamily: 'var(--font-mono)',
611
- fontVariantNumeric: 'tabular-nums',
612
- fontSize: 'var(--text-2xl)',
613
- fontWeight: 'var(--weight-semibold)',
614
- color: 'var(--text-primary)',
615
- }}
616
- >
617
- {tasksTotal}
618
- </Box>
619
- </Stack>
620
- </Box>
621
- </Panel>
622
-
623
- <Panel
624
- title="Agent activity · 30d"
625
- description="Distinct assignees touching tasks per day"
626
- padding={3}
627
- data-testid="overview-panel-agents-spark"
628
- >
629
- <Box p={3}>
630
- <Stack direction="row" justify="between" align="end" gap={3}>
631
- <Sparkline
632
- data={agentsSparkline}
633
- width={220}
634
- height={56}
635
- stroke="var(--chart-2)"
636
- fill="var(--info-subtle)"
637
- ariaLabel="Agent touches per day, last 30"
638
- />
639
- <Box
640
- as="div"
641
- style={{
642
- fontFamily: 'var(--font-mono)',
643
- fontVariantNumeric: 'tabular-nums',
644
- fontSize: 'var(--text-2xl)',
645
- fontWeight: 'var(--weight-semibold)',
646
- color: 'var(--text-primary)',
647
- }}
648
- >
649
- {overview.counts.agents}
650
- </Box>
651
- </Stack>
652
- </Box>
653
- </Panel>
654
-
655
- <Panel
656
- title="Tasks by status"
657
- description="Live snapshot of the kanban buckets"
658
- padding={3}
659
- data-testid="overview-panel-tasks-by-status"
660
- >
661
- <Box as="div" p={3}>
662
- <BarChart data={tasksByStatus} />
663
- </Box>
664
- </Panel>
665
-
666
- <Panel
667
- title="Memory"
668
- description="Vault, LightRAG, git sync"
669
- padding={3}
670
- data-testid="overview-panel-memory"
671
- >
672
- <Box as="div" p={3}>
673
- <MemoryStatusCard setActiveTab={setActiveTab} />
674
- </Box>
675
- </Panel>
676
-
677
- <Panel
678
- title="Active project"
679
- description={
680
- activeProject
681
- ? `${activeProject.path}`
682
- : 'No active project selected'
683
- }
684
- padding={3}
685
- data-testid="overview-panel-active-project"
686
- >
687
- {activeProject ? (
688
- <Box p={3}>
689
- <Stack gap={3}>
690
- <KeyValueList
691
- items={[
692
- {
693
- key: 'pname',
694
- label: 'Name',
695
- value: activeProject.name,
696
- mono: true,
697
- },
698
- {
699
- key: 'ppath',
700
- label: 'Path',
701
- value: truncateMiddle(activeProject.path, 48),
702
- mono: true,
703
- copyable: true,
704
- },
705
- {
706
- key: 'pstatus',
707
- label: 'Status',
708
- value: (
709
- <Badge
710
- variant={
711
- activeProject.status === 'active'
712
- ? 'success'
713
- : activeProject.status === 'error'
714
- ? 'danger'
715
- : 'neutral'
716
- }
717
- >
718
- <StatusDot
719
- variant={
720
- activeProject.status === 'active'
721
- ? 'success'
722
- : activeProject.status === 'error'
723
- ? 'danger'
724
- : 'neutral'
725
- }
726
- size="sm"
727
- pulse={activeProject.status === 'active'}
728
- />
729
- {activeProject.status}
730
- </Badge>
731
- ),
732
- },
733
- {
734
- key: 'pseen',
735
- label: 'Last opened',
736
- value: activeProject.lastAccessed
737
- ? formatRelative(activeProject.lastAccessed) ||
738
- '—'
739
- : '—',
740
- },
741
- ]}
742
- />
743
- <Inline gap={2} wrap>
744
- <UiButton
745
- variant="primary"
746
- size="sm"
747
- onClick={() => setActiveTab('tasks')}
748
- icon={<CheckSquare size={12} />}
749
- >
750
- Open tasks
751
- </UiButton>
752
- <UiButton
753
- variant="secondary"
754
- size="sm"
755
- onClick={onUseCurrentDir}
756
- icon={<Folder size={12} />}
757
- >
758
- Auto-detect
759
- </UiButton>
760
- </Inline>
761
- </Stack>
762
- </Box>
763
- ) : (
764
- <Box as="div" p={3}>
765
- <EmptyState
766
- title="No active project"
767
- description="Add one or auto-detect from the current directory."
768
- inline
769
- icon={<Folder size={20} />}
770
- />
771
- </Box>
772
- )}
773
- </Panel>
774
-
775
- <Panel
776
- title="Recent mods"
777
- description="Last few installed extensions"
778
- padding={3}
779
- data-testid="overview-panel-recent-mods"
780
- >
781
- {mods.length === 0 ? (
782
- <Box as="div" p={3}>
783
- <EmptyState
784
- title="No mods installed"
785
- description="Install from the Mods tab."
786
- inline
787
- icon={<Puzzle size={20} />}
788
- />
789
- </Box>
790
- ) : (
791
- <Box p={2}>
792
- <Stack gap={0}>
793
- {mods.slice(0, 6).map((m) => (
794
- <Box
795
- key={m.id}
796
- py={2}
797
- px={3}
798
- style={{
799
- borderTop: '1px solid var(--border-subtle)',
800
- }}
801
- >
802
- <Inline justify="between" align="center" gap={3}>
803
- <Stack gap={0} style={{ minWidth: 0 }}>
804
- <span
805
- style={{
806
- fontWeight: 'var(--weight-medium)',
807
- fontSize: 'var(--text-base)',
808
- }}
809
- >
810
- {m.name}
811
- </span>
812
- <span
813
- className="muted"
814
- style={{
815
- fontFamily: 'var(--font-mono)',
816
- fontSize: 'var(--text-xs)',
817
- color: 'var(--text-tertiary)',
818
- }}
819
- >
820
- v{m.version} · {m.type}
821
- </span>
822
- </Stack>
823
- <Badge
824
- variant={m.enabled ? 'success' : 'neutral'}
825
- size="sm"
826
- >
827
- <StatusDot
828
- variant={m.enabled ? 'success' : 'neutral'}
829
- size="sm"
830
- />
831
- {m.enabled ? 'on' : 'off'}
832
- </Badge>
833
- </Inline>
834
- </Box>
835
- ))}
836
- </Stack>
837
- </Box>
838
- )}
839
- </Panel>
840
- </Grid>
841
-
842
- {/* ─── Section 4: Activity stream ────────────────────────────── */}
843
- <Panel
844
- title="Recent activity"
845
- description={`${visibleActivity.length} item${visibleActivity.length === 1 ? '' : 's'} · live via SSE`}
846
- actions={
847
- <Inline gap={2} align="center">
848
- {hiddenKeys.size > 0 && (
849
- <UiButton
850
- variant="ghost"
851
- size="sm"
852
- onClick={onRestoreAll}
853
- icon={<Eye size={12} />}
854
- aria-label={`Restore ${hiddenKeys.size} hidden items`}
855
- >
856
- Restore ({hiddenKeys.size})
857
- </UiButton>
858
- )}
859
- {activityItems.length > 0 && (
860
- <UiButton
861
- variant="ghost"
862
- size="sm"
863
- onClick={onClearAll}
864
- icon={<EyeOff size={12} />}
865
- data-testid="overview-hide-all"
866
- aria-label="Hide all activity from overview"
867
- >
868
- Hide all
869
- </UiButton>
870
- )}
871
- </Inline>
872
- }
873
- padding={0}
874
- data-testid="overview-panel-activity"
875
- >
876
- {hiddenKeys.size > 0 && (
877
- <Box
878
- as="div"
879
- p={3}
880
- style={{
881
- background: 'var(--surface-2)',
882
- borderTop: '1px solid var(--border-subtle)',
883
- borderBottom: '1px solid var(--border-subtle)',
884
- display: 'flex',
885
- alignItems: 'center',
886
- justifyContent: 'space-between',
887
- gap: 'var(--space-3)',
888
- }}
889
- role="status"
890
- >
891
- <Inline gap={2} align="center">
892
- <EyeOff size={12} />
893
- <span style={{ fontSize: 'var(--text-sm)' }}>
894
- {hiddenKeys.size} item{hiddenKeys.size === 1 ? '' : 's'}{' '}
895
- hidden from the overview.
896
- </span>
897
- </Inline>
898
- <UiButton variant="ghost" size="sm" onClick={onRestoreAll}>
899
- <Eye size={12} /> Show them again
900
- </UiButton>
901
- </Box>
902
- )}
903
-
904
- {visibleActivity.length === 0 ? (
905
- <Box as="div" p={5}>
906
- <EmptyState
907
- title="No activity yet"
908
- description="Use the prompt below or invoke a Bizar command to start a feed."
909
- inline
910
- icon={<Activity size={20} />}
911
- />
912
- </Box>
913
- ) : (
914
- <Stack
915
- as="ul"
916
- gap={0}
917
- role="list"
918
- aria-live="polite"
919
- aria-relevant="additions"
920
- data-testid="overview-activity-list"
921
- >
922
- {visibleActivity.map(({ it, key }) => (
923
- <ActivityRow
924
- key={key}
925
- item={it}
926
- activityKey={key}
927
- onNavigate={setActiveTab}
928
- onHide={onHide}
929
- />
930
- ))}
931
- </Stack>
932
- )}
933
- </Panel>
934
-
935
- {/* ─── Section 5: Quick prompts ──────────────────────────────── */}
936
- <Panel
937
- title="What do you want to do?"
938
- description="Describe what you want — Odin will split it into tasks, delegate to background agents, and track progress in real time."
939
- padding={4}
940
- actions={
941
- <Badge variant="accent" size="sm">
942
- <Sparkles size={12} />
943
- <span style={{ marginLeft: 4 }}>Odin + 12 specialists</span>
944
- </Badge>
945
- }
946
- data-testid="overview-panel-prompts"
947
- >
948
- <form
949
- onSubmit={async (e) => {
950
- e.preventDefault();
951
- const text = (inputRef.current?.value || '').trim();
952
- if (!text) return;
953
- setSubmitting(true);
954
- try {
955
- const r = await api.post<{ subtasks?: unknown[] }>(
956
- '/tasks/submit',
957
- { title: text },
958
- );
959
- toast.success(
960
- `Odin split it into ${r.subtasks?.length || 1} task(s)`,
961
- );
962
- if (inputRef.current) inputRef.current.value = '';
963
- await refreshSnapshot();
964
- } catch (err) {
965
- toast.error(`Failed: ${(err as Error).message}`);
966
- } finally {
967
- setSubmitting(false);
968
- }
969
- }}
970
- >
971
- <Stack gap={3}>
972
- <Textarea
973
- ref={inputRef}
974
- disabled={submitting}
975
- aria-label="Describe what you want Odin to do"
976
- rows={4}
977
- placeholder="e.g. Implement user authentication with email + password, including registration, login, password reset, and integration tests. Use Bcrypt, JWT tokens, and the existing API style."
978
- data-testid="overview-prompt-input"
979
- />
980
- <Inline justify="between" align="center" gap={3} wrap>
981
- <Inline gap={2} wrap>
982
- {[
983
- 'Implement feature',
984
- 'Fix bug',
985
- 'Refactor',
986
- 'Investigate',
987
- 'Add tests',
988
- 'Document',
989
- 'Optimize',
990
- 'Deploy',
991
- ].map((action) => (
992
- <button
993
- key={action}
994
- type="button"
995
- className="overview-quick-chip"
996
- onClick={() => {
997
- if (inputRef.current) {
998
- inputRef.current.value = action;
999
- inputRef.current.focus();
1000
- }
1001
- }}
1002
- >
1003
- {action}
1004
- </button>
1005
- ))}
1006
- </Inline>
1007
- <UiButton
1008
- type="submit"
1009
- variant="primary"
1010
- size="md"
1011
- disabled={submitting}
1012
- loading={submitting}
1013
- icon={submitting ? undefined : <Send size={12} />}
1014
- data-testid="overview-submit"
1015
- >
1016
- {submitting ? 'Submitting…' : 'Submit to Odin'}
1017
- </UiButton>
1018
- </Inline>
1019
- </Stack>
1020
- </form>
1021
- </Panel>
1022
- </Stack>
1023
- </Box>
1024
- );
1025
- }
1026
-
1027
- // ─── AddProjectDialog (legacy <Modal>) ──────────────────────────────────────
1028
-
1029
- function AddProjectDialog({
1030
- settings,
1031
- onAdd,
1032
- }: {
1033
- settings: Settings;
1034
- onAdd: (path: string, name: string | null) => void;
1035
- }) {
1036
- const [path, setPath] = useState<string>(
1037
- settings?.dashboard?.projectsDirectory ?? '',
1038
- );
1039
- const [name, setName] = useState<string>('');
1040
- const [preflighting, setPreflighting] = useState<boolean>(false);
1041
- const [preflightError, setPreflightError] = useState<string | null>(null);
1042
-
1043
- const handleAdd = async () => {
1044
- if (!path) return;
1045
- setPreflighting(true);
1046
- setPreflightError(null);
1047
- try {
1048
- await api.get<DirectoryListing>('/fs?path=' + encodeURIComponent(path));
1049
- onAdd(path, name || null);
1050
- } catch (err) {
1051
- const apiErr = err as { status?: number; data?: { message?: string } };
1052
- if (apiErr.status === 404) {
1053
- setPreflightError('That folder no longer exists. Pick another.');
1054
- } else {
1055
- setPreflightError(
1056
- apiErr.data?.message ??
1057
- (err as Error).message ??
1058
- 'Validation failed.',
1059
- );
1060
- }
1061
- } finally {
1062
- setPreflighting(false);
1063
- }
1064
- };
1065
-
1066
- return (
1067
- <div>
1068
- <label className="field-label">Folder</label>
1069
- <FileBrowser
1070
- value={path}
1071
- onChange={(p) => {
1072
- setPath(p);
1073
- setPreflightError(null);
1074
- }}
1075
- projectsDirectory={settings?.dashboard?.projectsDirectory}
1076
- height={320}
1077
- />
1078
- {preflightError && (
1079
- <p
1080
- className="field-help"
1081
- style={{ color: 'var(--error)', marginTop: 4 }}
1082
- >
1083
- {preflightError}
1084
- </p>
1085
- )}
1086
- <div style={{ marginTop: 'var(--space-3)' }}>
1087
- <label className="field-label" htmlFor="add-project-name">
1088
- Name (optional)
1089
- </label>
1090
- <input
1091
- id="add-project-name"
1092
- className="input"
1093
- type="text"
1094
- placeholder="My App"
1095
- value={name}
1096
- onChange={(e) => setName(e.target.value)}
1097
- />
1098
- <p className="field-help" style={{ marginTop: 4 }}>
1099
- Display name for this project. Defaults to the folder name.
1100
- </p>
1101
- </div>
1102
- <div
1103
- style={{
1104
- marginTop: 'var(--space-3)',
1105
- display: 'flex',
1106
- justifyContent: 'flex-end',
1107
- }}
1108
- >
1109
- <Button
1110
- variant="primary"
1111
- onClick={handleAdd}
1112
- disabled={!path || preflighting}
1113
- >
1114
- {preflighting ? <span className="btn-spinner" /> : null}
1115
- {preflighting ? 'Checking…' : 'Add'}
1116
- </Button>
1117
- </div>
1118
- </div>
1119
- );
1120
- }
1121
-
1122
- // ─── Activity row (the compact inline list item) ────────────────────────────
1123
-
1124
- function ActivityRow({
1125
- item,
1126
- activityKey,
1127
- onNavigate,
1128
- onHide,
1129
- }: {
1130
- item: ActivityItem;
1131
- activityKey: string;
1132
- onNavigate: (tab: string) => void;
1133
- onHide: (key: string) => void;
1134
- }) {
1135
- const severity = activitySeverity(item);
1136
- const Icon = activityIcon(item.kind || '');
1137
- const title = humanizeKind(item.kind || 'activity');
1138
- const msg = formatActivitySummary(item);
1139
- const navTarget = activityNavTarget(item.kind || '');
1140
-
1141
- return (
1142
- <li
1143
- className={`overview-activity-row overview-activity-row-${severity}`}
1144
- style={{
1145
- borderTop: '1px solid var(--border-subtle)',
1146
- display: 'flex',
1147
- alignItems: 'center',
1148
- gap: 'var(--space-3)',
1149
- paddingInline: 'var(--space-4)',
1150
- paddingBlock: 'var(--space-3)',
1151
- }}
1152
- >
1153
- <button
1154
- type="button"
1155
- onClick={() => {
1156
- if (navTarget) onNavigate(navTarget);
1157
- }}
1158
- className="overview-activity-row__main"
1159
- title={navTarget ? `Open ${navTarget}` : title}
1160
- style={{
1161
- flex: 1,
1162
- display: 'flex',
1163
- alignItems: 'center',
1164
- gap: 'var(--space-3)',
1165
- textAlign: 'left',
1166
- background: 'transparent',
1167
- color: 'inherit',
1168
- padding: 0,
1169
- }}
1170
- >
1171
- <span
1172
- style={{
1173
- color:
1174
- severity === 'error'
1175
- ? 'var(--danger)'
1176
- : severity === 'warning'
1177
- ? 'var(--warning)'
1178
- : severity === 'success'
1179
- ? 'var(--success)'
1180
- : 'var(--accent)',
1181
- display: 'inline-flex',
1182
- alignItems: 'center',
1183
- justifyContent: 'center',
1184
- width: 22,
1185
- height: 22,
1186
- flexShrink: 0,
1187
- }}
1188
- >
1189
- <Icon size={14} />
1190
- </span>
1191
- <Stack gap={0} style={{ minWidth: 0, flex: 1 }}>
1192
- <Inline justify="between" align="center" gap={3} wrap>
1193
- <span
1194
- style={{
1195
- fontSize: 'var(--text-sm)',
1196
- fontWeight: 'var(--weight-medium)',
1197
- }}
1198
- >
1199
- {title}
1200
- </span>
1201
- <span
1202
- style={{
1203
- fontFamily: 'var(--font-mono)',
1204
- fontVariantNumeric: 'tabular-nums',
1205
- fontSize: 'var(--text-xs)',
1206
- color: 'var(--text-tertiary)',
1207
- }}
1208
- >
1209
- {formatClock(item.ts)}
1210
- </span>
1211
- </Inline>
1212
- <span
1213
- className="muted"
1214
- style={{
1215
- fontSize: 'var(--text-sm)',
1216
- color: 'var(--text-secondary)',
1217
- overflow: 'hidden',
1218
- textOverflow: 'ellipsis',
1219
- whiteSpace: 'nowrap',
1220
- display: 'block',
1221
- }}
1222
- title={msg}
1223
- >
1224
- {msg}
1225
- </span>
1226
- </Stack>
1227
- </button>
1228
- <IconButton
1229
- variant="ghost"
1230
- size="sm"
1231
- onClick={() => onHide(activityKey)}
1232
- aria-label="Hide from overview"
1233
- title="Hide from overview (kept in Settings → Activity Log)"
1234
- icon={<X size={12} />}
1235
- />
1236
- </li>
1237
- );
1238
- }
1239
-
1240
- // ─── Helpers ────────────────────────────────────────────────────────────────
1241
-
1242
- function sentenceCase(s: string): string {
1243
- if (!s) return s;
1244
- return s.charAt(0).toUpperCase() + s.slice(1);
1245
- }
1246
-
1247
- function humanizeKind(kind: string): string {
1248
- const normalized = (kind || 'activity').trim().toLowerCase().replace(/[:_]/g, '.');
1249
- const [scopeRaw, actionRaw = 'updated'] = normalized.split('.');
1250
- const scopeMap: Record<string, string> = {
1251
- settings: 'Settings',
1252
- task: 'Task',
1253
- tasks: 'Task',
1254
- agent: 'Agent',
1255
- agents: 'Agent',
1256
- plan: 'Plan',
1257
- plans: 'Plan',
1258
- bg: 'Background job',
1259
- background: 'Background job',
1260
- session: 'Session',
1261
- mod: 'Mod',
1262
- mods: 'Mod',
1263
- provider: 'Provider',
1264
- providers: 'Provider',
1265
- };
1266
- const actionMap: Record<string, string> = {
1267
- create: 'created',
1268
- created: 'created',
1269
- add: 'added',
1270
- added: 'added',
1271
- update: 'updated',
1272
- updated: 'updated',
1273
- delegate: 'delegated',
1274
- delegated: 'delegated',
1275
- invoke: 'invoked',
1276
- invoked: 'invoked',
1277
- restart: 'restarted',
1278
- restarted: 'restarted',
1279
- delete: 'deleted',
1280
- deleted: 'deleted',
1281
- remove: 'removed',
1282
- removed: 'removed',
1283
- archive: 'archived',
1284
- archived: 'archived',
1285
- restore: 'restored',
1286
- restored: 'restored',
1287
- complete: 'completed',
1288
- completed: 'completed',
1289
- fail: 'failed',
1290
- failed: 'failed',
1291
- error: 'errored',
1292
- stuck: 'marked stuck',
1293
- };
1294
- const scope = scopeMap[scopeRaw] || sentenceCase(scopeRaw.replace(/-/g, ' '));
1295
- const action = actionMap[actionRaw] || actionRaw.replace(/-/g, ' ');
1296
- return `${scope} ${action}`.trim();
1297
- }
1298
-
1299
- function firstString(it: ActivityItem, keys: string[]): string {
1300
- for (const key of keys) {
1301
- const value = it[key];
1302
- if (typeof value === 'string' && value.trim()) return value.trim();
1303
- }
1304
- return '';
1305
- }
1306
-
1307
- function formatActivitySummary(it: ActivityItem): string {
1308
- const direct = firstString(it, ['message', 'text', 'prompt', 'title', 'name']);
1309
- if (direct) return direct;
1310
- const parts: string[] = [];
1311
- const slug = firstString(it, ['slug']);
1312
- const agent = firstString(it, ['agent', 'author']);
1313
- const status = firstString(it, ['status']);
1314
- if (slug) parts.push(`Plan ${slug}`);
1315
- if (agent) parts.push(agent);
1316
- if (status) parts.push(status);
1317
- return parts.length ? parts.join(' · ') : 'No additional details.';
1318
- }
1319
-
1320
- function activitySeverity(it: ActivityItem): 'error' | 'warning' | 'success' | 'info' {
1321
- const k = ((it.kind as string) || '').toLowerCase();
1322
- const s = ((it.status as string) || '').toLowerCase();
1323
- if (k.includes('error') || s === 'error' || s === 'failure') return 'error';
1324
- if (k.includes('warn') || s === 'blocked') return 'warning';
1325
- if (k.includes('done') || k.includes('success') || s === 'done') return 'success';
1326
- return 'info';
1327
- }
1328
-
1329
- function activityIcon(kind: string) {
1330
- const k = kind.toLowerCase();
1331
- if (k === 'task') return CheckSquare;
1332
- if (k === 'agent') return Bot;
1333
- if (k === 'plan') return Map;
1334
- if (k.includes('bg') || k.includes('background') || k.includes('job')) return Zap;
1335
- if (k === 'mod') return Puzzle;
1336
- if (k === 'skill') return Sparkles;
1337
- if (k.includes('error') || k.includes('failure')) return AlertOctagon;
1338
- if (k.includes('warn')) return AlertTriangle;
1339
- return Activity;
1340
- }
1341
-
1342
- function activityNavTarget(kind: string): string | null {
1343
- const k = kind.toLowerCase();
1344
- if (k === 'task') return 'tasks';
1345
- if (k === 'agent') return 'agents';
1346
- if (k === 'plan') return 'artifacts';
1347
- if (k.includes('bg') || k.includes('background')) return 'activity';
1348
- if (k === 'mod') return 'mods';
1349
- if (k === 'skill') return 'skills';
1350
- return null;
1351
- }
1352
-
1353
- function formatClock(ts: string): string {
1354
- return formatRelative(ts);
1355
- }
1356
-
1357
- function truncateMiddle(s: string, maxLen: number): string {
1358
- if (!s || s.length <= maxLen) return s;
1359
- const half = Math.floor((maxLen - 1) / 2);
1360
- return `${s.slice(0, half)}…${s.slice(-half)}`;
1361
- }
1362
-
1363
- const OverviewMemo = React.memo(OverviewInner);
1364
- export { OverviewMemo as Overview };