@polderlabs/bizar 7.0.4 → 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 (489) 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 -1
  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/setup.ts +26 -0
  196. package/bizar-dash/vite.config.ts +26 -2
  197. package/bizar-dash/vitest.config.ts +5 -3
  198. package/package.json +36 -3
  199. package/bizar-dash/dist/assets/EnvVarsSection-BfhAz3ax.js +0 -114
  200. package/bizar-dash/dist/assets/EnvVarsSection-BfhAz3ax.js.map +0 -1
  201. package/bizar-dash/dist/assets/MobileChat-DLWdv6tQ.js +0 -1
  202. package/bizar-dash/dist/assets/MobileChat-DLWdv6tQ.js.map +0 -1
  203. package/bizar-dash/dist/assets/MobileSettings-Bxv62uPY.js +0 -1
  204. package/bizar-dash/dist/assets/MobileSettings-Bxv62uPY.js.map +0 -1
  205. package/bizar-dash/dist/assets/Toast-ATSAmbdi.js +0 -26
  206. package/bizar-dash/dist/assets/Toast-ATSAmbdi.js.map +0 -1
  207. package/bizar-dash/dist/assets/__vite-browser-external-BIHI7g3E.js +0 -1
  208. package/bizar-dash/dist/assets/__vite-browser-external-BIHI7g3E.js.map +0 -1
  209. package/bizar-dash/dist/assets/main-CJAtrosO.css +0 -1
  210. package/bizar-dash/dist/assets/main-DKKj9dsq.js +0 -430
  211. package/bizar-dash/dist/assets/main-DKKj9dsq.js.map +0 -1
  212. package/bizar-dash/dist/assets/mobile-C_naUN91.js +0 -1
  213. package/bizar-dash/dist/assets/mobile-C_naUN91.js.map +0 -1
  214. package/bizar-dash/dist/assets/mobile-layout-BWLtwtfQ.css +0 -1
  215. package/bizar-dash/dist/assets/mobile-layout-GZ3bE8L6.js +0 -211
  216. package/bizar-dash/dist/assets/mobile-layout-GZ3bE8L6.js.map +0 -1
  217. package/bizar-dash/dist/assets/useSlashCommands-BOwD7lNb.js +0 -65
  218. package/bizar-dash/dist/assets/useSlashCommands-BOwD7lNb.js.map +0 -1
  219. package/bizar-dash/dist/index.html +0 -43
  220. package/bizar-dash/src/web/App.tsx +0 -915
  221. package/bizar-dash/src/web/MobileApp.tsx +0 -414
  222. package/bizar-dash/src/web/components/ArtifactCreateDialog.tsx +0 -95
  223. package/bizar-dash/src/web/components/ArtifactListDialog.tsx +0 -55
  224. package/bizar-dash/src/web/components/ArtifactViewer.tsx +0 -144
  225. package/bizar-dash/src/web/components/AuditDialog.tsx +0 -79
  226. package/bizar-dash/src/web/components/AutosaveField.tsx +0 -50
  227. package/bizar-dash/src/web/components/BackupRestore.tsx +0 -330
  228. package/bizar-dash/src/web/components/BgStatusBadge.tsx +0 -32
  229. package/bizar-dash/src/web/components/Button.tsx +0 -56
  230. package/bizar-dash/src/web/components/CanvasContextMenu.tsx +0 -68
  231. package/bizar-dash/src/web/components/Card.tsx +0 -40
  232. package/bizar-dash/src/web/components/CollapsibleSection.tsx +0 -72
  233. package/bizar-dash/src/web/components/CommandDialog.tsx +0 -66
  234. package/bizar-dash/src/web/components/DoctorPanel.tsx +0 -160
  235. package/bizar-dash/src/web/components/EmptyState.tsx +0 -30
  236. package/bizar-dash/src/web/components/EnvVarManager.tsx +0 -430
  237. package/bizar-dash/src/web/components/EvalDiff.tsx +0 -157
  238. package/bizar-dash/src/web/components/EvalExportButton.tsx +0 -27
  239. package/bizar-dash/src/web/components/EvalRunCard.tsx +0 -78
  240. package/bizar-dash/src/web/components/FileBrowser.tsx +0 -633
  241. package/bizar-dash/src/web/components/HeadroomSettings.tsx +0 -418
  242. package/bizar-dash/src/web/components/HeadroomStatus.tsx +0 -158
  243. package/bizar-dash/src/web/components/HelpDialog.tsx +0 -55
  244. package/bizar-dash/src/web/components/InviteDialog.tsx +0 -205
  245. package/bizar-dash/src/web/components/KillConfirmDialog.tsx +0 -88
  246. package/bizar-dash/src/web/components/MobileBottomNav.tsx +0 -38
  247. package/bizar-dash/src/web/components/MobileDrawer.tsx +0 -68
  248. package/bizar-dash/src/web/components/MobileHeader.tsx +0 -50
  249. package/bizar-dash/src/web/components/MobileLayout.tsx +0 -63
  250. package/bizar-dash/src/web/components/Modal.tsx +0 -215
  251. package/bizar-dash/src/web/components/Notifications.tsx +0 -242
  252. package/bizar-dash/src/web/components/ScheduleTemplateCard.tsx +0 -72
  253. package/bizar-dash/src/web/components/ScreenshotCapture.tsx +0 -138
  254. package/bizar-dash/src/web/components/ScreenshotOCR.tsx +0 -42
  255. package/bizar-dash/src/web/components/SearchModal.tsx +0 -227
  256. package/bizar-dash/src/web/components/SettingsNav.tsx +0 -117
  257. package/bizar-dash/src/web/components/SettingsSearch.tsx +0 -328
  258. package/bizar-dash/src/web/components/Sidebar.tsx +0 -89
  259. package/bizar-dash/src/web/components/Spinner.tsx +0 -19
  260. package/bizar-dash/src/web/components/StatusBadge.tsx +0 -56
  261. package/bizar-dash/src/web/components/Tag.tsx +0 -41
  262. package/bizar-dash/src/web/components/TailscaleSettings.tsx +0 -161
  263. package/bizar-dash/src/web/components/TaskCard.tsx +0 -69
  264. package/bizar-dash/src/web/components/TaskCreateSheet.tsx +0 -76
  265. package/bizar-dash/src/web/components/Toast.tsx +0 -150
  266. package/bizar-dash/src/web/components/Toggle.tsx +0 -31
  267. package/bizar-dash/src/web/components/Topbar.tsx +0 -455
  268. package/bizar-dash/src/web/components/UsageChart.tsx +0 -250
  269. package/bizar-dash/src/web/components/UsageTable.tsx +0 -90
  270. package/bizar-dash/src/web/components/VirtualList.tsx +0 -53
  271. package/bizar-dash/src/web/components/VisualPlanDialog.tsx +0 -58
  272. package/bizar-dash/src/web/components/VoiceNotesPanel.tsx +0 -211
  273. package/bizar-dash/src/web/components/VoiceRecorder.tsx +0 -105
  274. package/bizar-dash/src/web/components/WorkspaceSelector.tsx +0 -158
  275. package/bizar-dash/src/web/components/agents/CommunicationLog.tsx +0 -126
  276. package/bizar-dash/src/web/components/agents/DependencyGraph.tsx +0 -163
  277. package/bizar-dash/src/web/components/agents/QualityGates.tsx +0 -123
  278. package/bizar-dash/src/web/components/agents/RealTimeEventLog.tsx +0 -127
  279. package/bizar-dash/src/web/components/agents/RoutingDecisions.test.tsx +0 -148
  280. package/bizar-dash/src/web/components/agents/RoutingDecisions.tsx +0 -160
  281. package/bizar-dash/src/web/components/background/AttachButton.tsx +0 -96
  282. package/bizar-dash/src/web/components/background/TmuxAttachCard.tsx +0 -122
  283. package/bizar-dash/src/web/components/chat/AgentChip.tsx +0 -68
  284. package/bizar-dash/src/web/components/chat/AgentNode.tsx +0 -127
  285. package/bizar-dash/src/web/components/chat/AgentTree.tsx +0 -80
  286. package/bizar-dash/src/web/components/chat/ChatBubble.tsx +0 -60
  287. package/bizar-dash/src/web/components/chat/ChatComposer.tsx +0 -72
  288. package/bizar-dash/src/web/components/chat/ChatInfoPanel.tsx +0 -370
  289. package/bizar-dash/src/web/components/chat/ChatRail.tsx +0 -387
  290. package/bizar-dash/src/web/components/chat/ChatThread.tsx +0 -127
  291. package/bizar-dash/src/web/components/chat/ChatTopBar.tsx +0 -50
  292. package/bizar-dash/src/web/components/chat/Composer.tsx +0 -166
  293. package/bizar-dash/src/web/components/chat/ConfirmModal.tsx +0 -54
  294. package/bizar-dash/src/web/components/chat/EmptyState.tsx +0 -21
  295. package/bizar-dash/src/web/components/chat/FirstRunGreeting.tsx +0 -18
  296. package/bizar-dash/src/web/components/chat/FloatingComposer.tsx +0 -38
  297. package/bizar-dash/src/web/components/chat/InfoPanel.tsx +0 -184
  298. package/bizar-dash/src/web/components/chat/JumpToLatest.tsx +0 -58
  299. package/bizar-dash/src/web/components/chat/LoadingSkeleton.tsx +0 -19
  300. package/bizar-dash/src/web/components/chat/MessageBlock.tsx +0 -260
  301. package/bizar-dash/src/web/components/chat/MessageBubble.tsx +0 -60
  302. package/bizar-dash/src/web/components/chat/SessionList.tsx +0 -163
  303. package/bizar-dash/src/web/components/chat/SessionRowMenu.tsx +0 -206
  304. package/bizar-dash/src/web/components/chat/StreamingIndicator.tsx +0 -37
  305. package/bizar-dash/src/web/components/chat/SuggestionCards.tsx +0 -55
  306. package/bizar-dash/src/web/components/chat/WelcomeScreen.tsx +0 -36
  307. package/bizar-dash/src/web/components/chat/_legacy.ts +0 -30
  308. package/bizar-dash/src/web/components/chat/index.ts +0 -31
  309. package/bizar-dash/src/web/components/chat/useAutoGrowTextarea.ts +0 -18
  310. package/bizar-dash/src/web/components/chat/useChat.ts +0 -1190
  311. package/bizar-dash/src/web/components/chat/useSlashCommands.ts +0 -47
  312. package/bizar-dash/src/web/components/goals/GoalInput.tsx +0 -86
  313. package/bizar-dash/src/web/components/goals/PlanVisualization.tsx +0 -111
  314. package/bizar-dash/src/web/components/tasks/BacklogPanel.css +0 -109
  315. package/bizar-dash/src/web/components/tasks/BacklogPanel.tsx +0 -209
  316. package/bizar-dash/src/web/hooks/useAutosave.ts +0 -107
  317. package/bizar-dash/src/web/hooks/useI18n.ts +0 -13
  318. package/bizar-dash/src/web/index.html +0 -37
  319. package/bizar-dash/src/web/lib/api.ts +0 -237
  320. package/bizar-dash/src/web/lib/goapPlanner.ts +0 -614
  321. package/bizar-dash/src/web/lib/i18n.ts +0 -25
  322. package/bizar-dash/src/web/lib/logger.ts +0 -30
  323. package/bizar-dash/src/web/lib/markdown.tsx +0 -59
  324. package/bizar-dash/src/web/lib/search.ts +0 -115
  325. package/bizar-dash/src/web/lib/types.ts +0 -894
  326. package/bizar-dash/src/web/lib/utils.ts +0 -114
  327. package/bizar-dash/src/web/lib/ws.ts +0 -177
  328. package/bizar-dash/src/web/locales/en.json +0 -52
  329. package/bizar-dash/src/web/main.tsx +0 -43
  330. package/bizar-dash/src/web/mobile/MobileApp.tsx +0 -309
  331. package/bizar-dash/src/web/mobile/MobileBottomNav.tsx +0 -38
  332. package/bizar-dash/src/web/mobile/MobileChat.tsx +0 -138
  333. package/bizar-dash/src/web/mobile/MobileDoctor.tsx +0 -80
  334. package/bizar-dash/src/web/mobile/MobileEval.tsx +0 -167
  335. package/bizar-dash/src/web/mobile/MobileMemory.tsx +0 -108
  336. package/bizar-dash/src/web/mobile/MobileOverview.tsx +0 -213
  337. package/bizar-dash/src/web/mobile/MobileSettings.tsx +0 -251
  338. package/bizar-dash/src/web/mobile/MobileTasks.tsx +0 -94
  339. package/bizar-dash/src/web/mobile/MobileTopbar.tsx +0 -115
  340. package/bizar-dash/src/web/mobile/components/MobileBottomSheet.tsx +0 -182
  341. package/bizar-dash/src/web/mobile/components/MobileListItem.tsx +0 -64
  342. package/bizar-dash/src/web/mobile/components/MobileModal.tsx +0 -123
  343. package/bizar-dash/src/web/mobile/views/MobileActivity.tsx +0 -273
  344. package/bizar-dash/src/web/mobile/views/MobileAgents.tsx +0 -374
  345. package/bizar-dash/src/web/mobile/views/MobileArtifactCanvas.tsx +0 -439
  346. package/bizar-dash/src/web/mobile/views/MobileArtifacts.tsx +0 -143
  347. package/bizar-dash/src/web/mobile/views/MobileChat.tsx +0 -257
  348. package/bizar-dash/src/web/mobile/views/MobileConfig.tsx +0 -175
  349. package/bizar-dash/src/web/mobile/views/MobileHistory.tsx +0 -191
  350. package/bizar-dash/src/web/mobile/views/MobileMods.tsx +0 -149
  351. package/bizar-dash/src/web/mobile/views/MobileMore.tsx +0 -121
  352. package/bizar-dash/src/web/mobile/views/MobileNotifications.tsx +0 -137
  353. package/bizar-dash/src/web/mobile/views/MobilePlans.tsx +0 -26
  354. package/bizar-dash/src/web/mobile/views/MobileSchedules.tsx +0 -685
  355. package/bizar-dash/src/web/mobile/views/MobileSearchModal.tsx +0 -172
  356. package/bizar-dash/src/web/mobile/views/MobileSettings.tsx +0 -363
  357. package/bizar-dash/src/web/mobile/views/MobileSkills.tsx +0 -153
  358. package/bizar-dash/src/web/mobile/views/MobileTasks.tsx +0 -564
  359. package/bizar-dash/src/web/mobile/views/QrCodePanel.tsx +0 -69
  360. package/bizar-dash/src/web/mobile.tsx +0 -14
  361. package/bizar-dash/src/web/styles/agents-redesign.css +0 -311
  362. package/bizar-dash/src/web/styles/chat.css +0 -1740
  363. package/bizar-dash/src/web/styles/glyphs.css +0 -960
  364. package/bizar-dash/src/web/styles/main.css +0 -10389
  365. package/bizar-dash/src/web/styles/memory.css +0 -1177
  366. package/bizar-dash/src/web/styles/minimax-usage.css +0 -842
  367. package/bizar-dash/src/web/styles/mobile-chat.css +0 -5
  368. package/bizar-dash/src/web/styles/mobile-layout.css +0 -233
  369. package/bizar-dash/src/web/styles/mobile.css +0 -2181
  370. package/bizar-dash/src/web/styles/settings.css +0 -775
  371. package/bizar-dash/src/web/styles/skills.css +0 -302
  372. package/bizar-dash/src/web/styles/tasks-redesign.css +0 -253
  373. package/bizar-dash/src/web/styles/tasks.css +0 -605
  374. package/bizar-dash/src/web/ui/controls/Button.tsx +0 -77
  375. package/bizar-dash/src/web/ui/controls/Checkbox.tsx +0 -95
  376. package/bizar-dash/src/web/ui/controls/IconButton.tsx +0 -66
  377. package/bizar-dash/src/web/ui/controls/Kbd.tsx +0 -24
  378. package/bizar-dash/src/web/ui/controls/NumberInput.tsx +0 -77
  379. package/bizar-dash/src/web/ui/controls/RadioGroup.tsx +0 -109
  380. package/bizar-dash/src/web/ui/controls/SearchInput.tsx +0 -91
  381. package/bizar-dash/src/web/ui/controls/Select.tsx +0 -92
  382. package/bizar-dash/src/web/ui/controls/Slider.tsx +0 -70
  383. package/bizar-dash/src/web/ui/controls/TextInput.tsx +0 -67
  384. package/bizar-dash/src/web/ui/controls/Textarea.tsx +0 -56
  385. package/bizar-dash/src/web/ui/controls/Toggle.tsx +0 -79
  386. package/bizar-dash/src/web/ui/controls/controls.css +0 -476
  387. package/bizar-dash/src/web/ui/controls/index.ts +0 -35
  388. package/bizar-dash/src/web/ui/data/BarChart.tsx +0 -60
  389. package/bizar-dash/src/web/ui/data/DataTable.tsx +0 -216
  390. package/bizar-dash/src/web/ui/data/EmptyState.tsx +0 -60
  391. package/bizar-dash/src/web/ui/data/ErrorState.tsx +0 -56
  392. package/bizar-dash/src/web/ui/data/KeyValueList.tsx +0 -106
  393. package/bizar-dash/src/web/ui/data/LoadingState.tsx +0 -65
  394. package/bizar-dash/src/web/ui/data/Sparkline.tsx +0 -87
  395. package/bizar-dash/src/web/ui/data/StatTile.tsx +0 -87
  396. package/bizar-dash/src/web/ui/data/data.css +0 -540
  397. package/bizar-dash/src/web/ui/data/index.ts +0 -31
  398. package/bizar-dash/src/web/ui/feedback/Badge.tsx +0 -49
  399. package/bizar-dash/src/web/ui/feedback/Dialog.tsx +0 -209
  400. package/bizar-dash/src/web/ui/feedback/ProgressBar.tsx +0 -69
  401. package/bizar-dash/src/web/ui/feedback/StatusDot.tsx +0 -50
  402. package/bizar-dash/src/web/ui/feedback/Toast.tsx +0 -200
  403. package/bizar-dash/src/web/ui/feedback/Tooltip.tsx +0 -126
  404. package/bizar-dash/src/web/ui/feedback/feedback.css +0 -314
  405. package/bizar-dash/src/web/ui/feedback/index.ts +0 -43
  406. package/bizar-dash/src/web/ui/index.ts +0 -57
  407. package/bizar-dash/src/web/ui/layout/AppShell.tsx +0 -51
  408. package/bizar-dash/src/web/ui/layout/Breadcrumbs.tsx +0 -97
  409. package/bizar-dash/src/web/ui/layout/Panel.tsx +0 -73
  410. package/bizar-dash/src/web/ui/layout/PanelHeader.tsx +0 -39
  411. package/bizar-dash/src/web/ui/layout/Sidebar.tsx +0 -131
  412. package/bizar-dash/src/web/ui/layout/Tabs.tsx +0 -133
  413. package/bizar-dash/src/web/ui/layout/Topbar.tsx +0 -42
  414. package/bizar-dash/src/web/ui/layout/ViewHeader.tsx +0 -48
  415. package/bizar-dash/src/web/ui/layout/index.ts +0 -34
  416. package/bizar-dash/src/web/ui/layout/layout.css +0 -464
  417. package/bizar-dash/src/web/ui/navigation/CommandPalette.tsx +0 -199
  418. package/bizar-dash/src/web/ui/navigation/NavGroup.tsx +0 -95
  419. package/bizar-dash/src/web/ui/navigation/NavLink.tsx +0 -93
  420. package/bizar-dash/src/web/ui/navigation/index.ts +0 -17
  421. package/bizar-dash/src/web/ui/navigation/navigation.css +0 -343
  422. package/bizar-dash/src/web/ui/primitives/Box.tsx +0 -91
  423. package/bizar-dash/src/web/ui/primitives/Grid.tsx +0 -49
  424. package/bizar-dash/src/web/ui/primitives/Inline.tsx +0 -55
  425. package/bizar-dash/src/web/ui/primitives/Separator.tsx +0 -46
  426. package/bizar-dash/src/web/ui/primitives/Stack.tsx +0 -54
  427. package/bizar-dash/src/web/ui/primitives/VisuallyHidden.tsx +0 -26
  428. package/bizar-dash/src/web/ui/primitives/index.ts +0 -23
  429. package/bizar-dash/src/web/ui/primitives/primitives.css +0 -183
  430. package/bizar-dash/src/web/ui/styles/globals.css +0 -86
  431. package/bizar-dash/src/web/ui/styles/reset.css +0 -125
  432. package/bizar-dash/src/web/ui/styles/tokens.css +0 -208
  433. package/bizar-dash/src/web/ui/theme/ThemeProvider.tsx +0 -142
  434. package/bizar-dash/src/web/ui/theme/tokens.ts +0 -131
  435. package/bizar-dash/src/web/ui/theme/useTheme.ts +0 -22
  436. package/bizar-dash/src/web/ui/utils/cx.ts +0 -42
  437. package/bizar-dash/src/web/views/Activity.tsx +0 -1221
  438. package/bizar-dash/src/web/views/Agents.tsx +0 -964
  439. package/bizar-dash/src/web/views/Artifacts.tsx +0 -1494
  440. package/bizar-dash/src/web/views/BackgroundAgents.tsx +0 -822
  441. package/bizar-dash/src/web/views/Chat.tsx +0 -532
  442. package/bizar-dash/src/web/views/Config.tsx +0 -3
  443. package/bizar-dash/src/web/views/Doctor.tsx +0 -317
  444. package/bizar-dash/src/web/views/Eval.tsx +0 -372
  445. package/bizar-dash/src/web/views/EvalReport.tsx +0 -353
  446. package/bizar-dash/src/web/views/GoalPlanner.tsx +0 -158
  447. package/bizar-dash/src/web/views/Harness.tsx +0 -306
  448. package/bizar-dash/src/web/views/History.tsx +0 -353
  449. package/bizar-dash/src/web/views/Memory.tsx +0 -193
  450. package/bizar-dash/src/web/views/MiniMaxUsage.tsx +0 -942
  451. package/bizar-dash/src/web/views/ModView.tsx +0 -207
  452. package/bizar-dash/src/web/views/Mods.tsx +0 -706
  453. package/bizar-dash/src/web/views/Overview.tsx +0 -1343
  454. package/bizar-dash/src/web/views/Providers.tsx +0 -302
  455. package/bizar-dash/src/web/views/Schedules.tsx +0 -902
  456. package/bizar-dash/src/web/views/Settings.tsx +0 -308
  457. package/bizar-dash/src/web/views/Skills.tsx +0 -383
  458. package/bizar-dash/src/web/views/SpawnAgentModal.tsx +0 -221
  459. package/bizar-dash/src/web/views/Tasks.tsx +0 -789
  460. package/bizar-dash/src/web/views/Workspace.tsx +0 -294
  461. package/bizar-dash/src/web/views/glyphs/GlyphRenderer.tsx +0 -844
  462. package/bizar-dash/src/web/views/glyphs/components.tsx +0 -1098
  463. package/bizar-dash/src/web/views/memory/ConfigPanel.tsx +0 -426
  464. package/bizar-dash/src/web/views/memory/FromScreenshotPanel.tsx +0 -23
  465. package/bizar-dash/src/web/views/memory/GitSyncPanel.tsx +0 -220
  466. package/bizar-dash/src/web/views/memory/LightragPanel.tsx +0 -310
  467. package/bizar-dash/src/web/views/memory/MemoryGraphLegend.tsx +0 -29
  468. package/bizar-dash/src/web/views/memory/MemoryGraphPanel.tsx +0 -192
  469. package/bizar-dash/src/web/views/memory/MemoryGraphView.tsx +0 -336
  470. package/bizar-dash/src/web/views/memory/MemoryOverview.tsx +0 -367
  471. package/bizar-dash/src/web/views/memory/MemoryStatusCard.tsx +0 -160
  472. package/bizar-dash/src/web/views/memory/ObsidianPanel.tsx +0 -650
  473. package/bizar-dash/src/web/views/memory/SemanticSearchPanel.tsx +0 -197
  474. package/bizar-dash/src/web/views/memory/VaultFromClipboardPanel.tsx +0 -101
  475. package/bizar-dash/src/web/views/settings/ActivitySection.tsx +0 -205
  476. package/bizar-dash/src/web/views/settings/AgentSection.tsx +0 -305
  477. package/bizar-dash/src/web/views/settings/AuthSection.tsx +0 -159
  478. package/bizar-dash/src/web/views/settings/EnvVarsSection.tsx +0 -15
  479. package/bizar-dash/src/web/views/settings/GeneralSection.tsx +0 -128
  480. package/bizar-dash/src/web/views/settings/HeadroomSection.tsx +0 -39
  481. package/bizar-dash/src/web/views/settings/MemorySection.tsx +0 -154
  482. package/bizar-dash/src/web/views/settings/NetworkSection.tsx +0 -87
  483. package/bizar-dash/src/web/views/settings/NotificationsSection.tsx +0 -34
  484. package/bizar-dash/src/web/views/settings/SystemLlmSection.tsx +0 -81
  485. package/bizar-dash/src/web/views/settings/ThemeSection.tsx +0 -168
  486. package/bizar-dash/src/web/views/settings/UpdatesSection.tsx +0 -256
  487. package/bizar-dash/src/web/views/settings/WorkspacesSection.tsx +0 -189
  488. package/packages/sdk/.harness/agents.json +0 -17
  489. package/packages/sdk/.harness/topology.json +0 -4
@@ -1,915 +0,0 @@
1
- // src/App.tsx — root shell. Wires data + contexts + tab routing.
2
-
3
- import { Component, Suspense, useCallback, useEffect, useMemo, useRef, useState } from 'react';
4
- import { Topbar, TABS } from './components/Topbar';
5
- import { Sidebar } from './components/Sidebar';
6
- import { ModalProvider, useModal } from './components/Modal';
7
- import { ToastProvider, useToast } from './components/Toast';
8
- import { SearchModal } from './components/SearchModal';
9
- import { Notifications } from './components/Notifications';
10
- import { api, ApiError } from './lib/api';
11
- import { CommandDialog, type DialogDescriptor } from './components/CommandDialog';
12
- import { Ws } from './lib/ws';
13
- import {
14
- applyTheme,
15
- applyThemeTokens,
16
- type Artifact,
17
- type Settings,
18
- type SettingsResponse,
19
- type Snapshot,
20
- type WsMessage,
21
- type WsStatus,
22
- type ProjectRecord,
23
- type SearchResult,
24
- } from './lib/types';
25
- import { Overview } from './views/Overview';
26
- import { Chat } from './views/Chat';
27
- import { Agents } from './views/Agents';
28
- import { Artifacts } from './views/Artifacts';
29
- import { Tasks } from './views/Tasks';
30
- import { Activity } from './views/Activity';
31
- import { Config } from './views/Config';
32
- import { SettingsView } from './views/Settings';
33
- import { Mods } from './views/Mods';
34
- import { ModView, type ModView as ModViewType } from './views/ModView';
35
- import { Schedules } from './views/Schedules';
36
- import { Skills } from './views/Skills';
37
- import { Memory } from './views/Memory';
38
- import { History } from './views/History';
39
- import { MiniMaxUsage } from './views/MiniMaxUsage';
40
- import { BackgroundAgents } from './views/BackgroundAgents';
41
- import { Doctor } from './views/Doctor';
42
- import { Harness } from './views/Harness';
43
- import { Eval } from './views/Eval';
44
- import { EvalReport } from './views/EvalReport';
45
- // v6.4.0 — F-036 Goal Planner UI.
46
- import { GoalPlanner } from './views/GoalPlanner';
47
- import { SettingsNav } from './components/SettingsNav';
48
- import { Spinner } from './components/Spinner';
49
- import { Button } from './components/Button';
50
- import { AlertTriangle, Globe, LayoutTemplate, X } from 'lucide-react';
51
- import { logger } from './lib/logger';
52
- import './styles/main.css';
53
- import './styles/settings.css';
54
-
55
- type ViewProps = {
56
- snapshot: Snapshot;
57
- settings: Settings;
58
- activeTab: string;
59
- setActiveTab: (id: string) => void;
60
- refreshSnapshot: () => Promise<void>;
61
- /**
62
- * v4.9.0 — When true, the sidebar shows the full settings navigation
63
- * instead of the normal tab rail. Toggled by clicking the Settings tab.
64
- */
65
- settingsMode: boolean;
66
- /**
67
- * v4.9.0 — The active settings section highlighted in the sidebar nav.
68
- * Null means "show all". Managed at App level so Sidebar can read it.
69
- */
70
- settingsActiveSection: string | null;
71
- setSettingsActiveSection: (id: string | null) => void;
72
- /**
73
- * v3.3.0 — Cross-view scratch state. The "Open in chat" button
74
- * on the Agents tab sets `pendingInitialAgent`, which the Chat
75
- * view reads on mount (and clears). Keeps the chat a true
76
- * one-click handoff without needing URL state.
77
- */
78
- crossState?: CrossViewState;
79
- setCrossState?: (patch: Partial<CrossViewState>) => void;
80
- };
81
-
82
- export type CrossViewState = {
83
- initialAgent?: string | null;
84
- };
85
-
86
- const VIEW_MAP: Record<string, (p: ViewProps) => React.ReactNode> = {
87
- overview: Overview,
88
- chat: Chat,
89
- agents: Agents,
90
- artifacts: Artifacts,
91
- memory: Memory,
92
- tasks: Tasks,
93
- activity: Activity,
94
- background: BackgroundAgents,
95
- config: Config,
96
- settings: SettingsView,
97
- mods: Mods,
98
- schedules: Schedules,
99
- skills: Skills,
100
- history: History,
101
- minimax: MiniMaxUsage,
102
- // v6.0.0 — Doctor page (system health + diagnostics surface).
103
- // Registered here so the tab id 'doctor' from Topbar.tsx TABS
104
- // resolves to a real view instead of falling back to Overview.
105
- doctor: Doctor,
106
- // v6.0.0 — Harness engineering dashboard (audit score, subsystem status, quick actions).
107
- harness: Harness,
108
- // v5.2.0 — Eval framework web UI. `eval` is the sidebar tab
109
- // (recent runs summary), `evalReport` is reachable from the
110
- // Eval view's "Open Eval Report" button (full report + diff).
111
- eval: Eval,
112
- evalReport: EvalReport,
113
- // v6.4.0 — F-036 Goal Planner UI. Plain-English goal → A* plan.
114
- 'goal-planner': GoalPlanner,
115
- };
116
-
117
- const VERSION = 'v6.0.0';
118
-
119
- /**
120
- * Render the active view. If `activeTab` matches a built-in tab id,
121
- * use VIEW_MAP. If it matches a mod view id (any id not in VIEW_MAP),
122
- * render the ModView component with that view id.
123
- */
124
- function renderActiveView(
125
- activeTab: string,
126
- viewProps: ViewProps,
127
- modViews: ModViewType[],
128
- reloadKey: number,
129
- ): React.ReactNode {
130
- if (VIEW_MAP[activeTab]) {
131
- const V = VIEW_MAP[activeTab];
132
- return <V {...viewProps} />;
133
- }
134
- // Mod view — find by id
135
- const mv = modViews.find((v) => v.id === activeTab);
136
- if (mv) {
137
- return (
138
- <ModView
139
- viewId={mv.id}
140
- reloadKey={reloadKey}
141
- activeTab={viewProps.activeTab}
142
- setActiveTab={viewProps.setActiveTab}
143
- />
144
- );
145
- }
146
- // Fallback to Overview
147
- const V = VIEW_MAP.overview;
148
- return <V {...viewProps} />;
149
- }
150
-
151
- // v3.6.2 — Inline token-entry form shown on the boot-error screen when
152
- // the server returns 401 (reverse-proxy / Tailscale Serve scenario).
153
- // The user pastes the token, it is saved to localStorage, and boot is
154
- // retried. The form closes on Escape or on successful save.
155
- function TokenEntryForm({ onSaved }: { onSaved: () => void }) {
156
- const [value, setValue] = useState('');
157
- const inputRef = useRef<HTMLInputElement>(null);
158
-
159
- // Auto-focus the input on mount
160
- useEffect(() => {
161
- inputRef.current?.focus();
162
- }, []);
163
-
164
- const submit = () => {
165
- const tok = value.trim();
166
- if (!tok) return;
167
- api.setToken(tok);
168
- onSaved();
169
- };
170
-
171
- const handleKeyDown = (e: React.KeyboardEvent) => {
172
- if (e.key === 'Escape') {
173
- e.preventDefault();
174
- onSaved();
175
- }
176
- if (e.key === 'Enter') {
177
- e.preventDefault();
178
- submit();
179
- }
180
- };
181
-
182
- return (
183
- <form
184
- className="token-entry-form"
185
- onSubmit={(e) => { e.preventDefault(); submit(); }}
186
- >
187
- <div className="token-entry-row">
188
- <input
189
- ref={inputRef}
190
- type="password"
191
- className="input mono"
192
- value={value}
193
- onChange={(e) => setValue(e.target.value)}
194
- placeholder="Paste auth token"
195
- spellCheck={false}
196
- autoComplete="off"
197
- onKeyDown={handleKeyDown}
198
- />
199
- <Button type="submit" variant="primary" size="sm" disabled={!value.trim()}>
200
- Save &amp; retry
201
- </Button>
202
- </div>
203
- <p className="token-entry-hint">
204
- Where do I find this token? Check the file{' '}
205
- <code>~/.config/bizar/dashboard-secret</code> on the machine
206
- running the dashboard, or look for it in the server output.
207
- </p>
208
- </form>
209
- );
210
- }
211
-
212
- export function App() {
213
- return (
214
- <ToastProvider>
215
- <ModalProvider>
216
- <Shell />
217
- </ModalProvider>
218
- </ToastProvider>
219
- );
220
- }
221
-
222
- function Shell() {
223
- const toast = useToast();
224
- const modalApi = useModal();
225
- const { isModalOpen } = modalApi;
226
- const isModalOpenRef = useRef(false);
227
- const [activeTab, setActiveTab] = useState<string>('overview');
228
- const [snapshot, setSnapshot] = useState<Snapshot | null>(null);
229
- const [settings, setSettings] = useState<Settings | null>(null);
230
- const [wsStatus, setWsStatus] = useState<WsStatus>('connecting');
231
- const [bootError, setBootError] = useState<string | null>(null);
232
- // v4.9.0 — Settings mode: when true, sidebar shows all settings sections
233
- const [settingsMode, setSettingsMode] = useState(false);
234
- // v4.9.0 — The active settings section shown in the sidebar nav.
235
- // Null means "show all sections". Managed here so Sidebar can read it.
236
- const [settingsActiveSection, setSettingsActiveSection] = useState<string | null>(null);
237
- const [authPromptOpen, setAuthPromptOpen] = useState(false);
238
- const [searchOpen, setSearchOpen] = useState(false);
239
- const [stuckAgents, setStuckAgents] = useState<{ name: string }[]>([]);
240
- const [stuckBannerDismissed, setStuckBannerDismissed] = useState(false);
241
- // v3.20.3 — Mod views become first-class nav entries. Fetched on mount
242
- // and after every mod install/uninstall/enable toggle.
243
- const [modViews, setModViews] = useState<ModViewType[]>([]);
244
- const [modViewsReloadKey, setModViewsReloadKey] = useState(0);
245
- const wsRef = useRef<Ws | null>(null);
246
- const [ws, setWs] = useState<Ws | null>(null);
247
-
248
- // Fetch installed mod views (any enabled mod with a web/index.html or
249
- // a views/registry.json entry). These become top-level nav tabs.
250
- useEffect(() => {
251
- let cancelled = false;
252
- (async () => {
253
- try {
254
- const r = await api.get<{ views: ModViewType[] }>('/mods/views');
255
- if (cancelled) return;
256
- setModViews(r.views || []);
257
- } catch {
258
- if (!cancelled) setModViews([]);
259
- }
260
- })();
261
- return () => { cancelled = true; };
262
- }, [modViewsReloadKey, snapshot?.mods]);
263
-
264
- // Re-fetch mod views when ws events arrive about mod changes.
265
- useEffect(() => {
266
- if (!ws) return;
267
- const off = ws.on((msg: { type?: string }) => {
268
- if (msg?.type === 'mod:change' || msg?.type === 'mod:enabled' || msg?.type === 'mod:installed') {
269
- setModViewsReloadKey((n) => n + 1);
270
- }
271
- });
272
- return () => off();
273
- }, [ws]);
274
-
275
- // Apply theme tokens
276
- useEffect(() => {
277
- if (settings?.theme) {
278
- applyTheme(settings.theme);
279
- applyThemeTokens(settings.theme);
280
- }
281
- }, [settings?.theme]);
282
-
283
- // v3.6.1 — Keep a ref in sync with isModalOpen so the keyboard handler
284
- // always reads the current value even between state updates and effect
285
- // re-runs. This closes the race window where modal.close() has cleared
286
- // the state but the old keyboard handler (with isModalOpen=false in
287
- // its closure) hasn't been replaced yet.
288
- useEffect(() => {
289
- isModalOpenRef.current = isModalOpen;
290
- }, [isModalOpen]);
291
-
292
- // System-mode listener
293
- useEffect(() => {
294
- if (settings?.theme?.mode !== 'system') return;
295
- const mql = window.matchMedia('(prefers-color-scheme: light)');
296
- const handler = () => applyTheme(settings.theme.mode);
297
- mql.addEventListener('change', handler);
298
- return () => mql.removeEventListener('change', handler);
299
- }, [settings?.theme?.mode]);
300
-
301
- // v3.6.2 — Boot logic extracted to retryBoot so it can be re-invoked
302
- // after the user pastes a token in the inline recovery form.
303
- const retryBoot = useCallback(async () => {
304
- let cancelled = false;
305
-
306
- const loadBootData = () =>
307
- Promise.all([
308
- api.get<Snapshot>('/snapshot').catch(() => null),
309
- api.get<SettingsResponse>('/settings').catch(() => null),
310
- api.get<{ stuck: { name: string }[] }>('/agents/stuck').catch(() => null),
311
- ]);
312
-
313
- const applyBootData = (
314
- snap: Snapshot | null,
315
- set: SettingsResponse | null,
316
- stuck: { stuck: { name: string }[] } | null,
317
- ) => {
318
- if (snap) setSnapshot(snap);
319
- if (set?.data) setSettings(set.data);
320
- if (stuck?.stuck) setStuckAgents(stuck.stuck);
321
- };
322
-
323
- try {
324
- const auth = await api.probeAuthStatus();
325
- const [snap, set, stuck] = await loadBootData();
326
- if (cancelled) return;
327
-
328
- applyBootData(snap, set, stuck);
329
- if (snap || set) {
330
- setBootError(null);
331
- return;
332
- }
333
-
334
- if (!auth.loopback) {
335
- setBootError('Dashboard server unreachable.');
336
- return;
337
- }
338
-
339
- await new Promise((resolve) => setTimeout(resolve, 1000));
340
- if (cancelled) return;
341
-
342
- const [retrySnap, retrySet, retryStuck] = await loadBootData();
343
- if (cancelled) return;
344
-
345
- applyBootData(retrySnap, retrySet, retryStuck);
346
- if (!retrySnap && !retrySet) {
347
- setBootError('Dashboard server unreachable.');
348
- }
349
- } catch (err) {
350
- if (cancelled) return;
351
- const msg = (err as Error)?.message ?? 'unknown error';
352
- // v3.6.2 — If we got a 401, the user needs to supply a token.
353
- // Surface the form inline instead of just the error message.
354
- if (err instanceof ApiError && err.status === 401) {
355
- setBootError(msg);
356
- setAuthPromptOpen(true);
357
- toast.error(`Auth required: ${msg}`);
358
- return;
359
- }
360
- setBootError(msg);
361
- toast.error(`Failed to load: ${msg}`);
362
- }
363
- }, [toast]);
364
-
365
- // Initial fetch — runs once on mount
366
- useEffect(() => {
367
- let cancelled = false;
368
- const id = setTimeout(() => {
369
- if (!cancelled) retryBoot();
370
- }, 0);
371
- return () => {
372
- cancelled = true;
373
- clearTimeout(id);
374
- };
375
- }, [retryBoot]);
376
-
377
- // Apply defaultTab ONCE on mount only — must NOT re-fire on toast changes
378
- useEffect(() => {
379
- let cancelled = false;
380
- api.get<SettingsResponse>('/settings')
381
- .then((set) => {
382
- if (cancelled) return;
383
- const defaultTab = set?.data?.ui?.defaultTab;
384
- if (defaultTab) setActiveTab(defaultTab);
385
- })
386
- .catch(() => undefined);
387
- return () => {
388
- cancelled = true;
389
- };
390
- }, []); // empty deps — runs once on mount
391
-
392
- // v3.1.0 — Periodic stuck-agent poll. Light-weight; the server does
393
- // the threshold math and only returns the list.
394
- useEffect(() => {
395
- let cancelled = false;
396
- const tick = async () => {
397
- try {
398
- const r = await api.get<{ stuck: { name: string }[] }>('/agents/stuck');
399
- if (!cancelled) {
400
- setStuckAgents((cur) => {
401
- const next = r.stuck || [];
402
- const wasEmpty = cur.length === 0;
403
- if (wasEmpty && next.length > 0) {
404
- toast.warning(`${next.length} agent${next.length === 1 ? '' : 's'} stuck`, 5000);
405
- }
406
- return next;
407
- });
408
- }
409
- } catch {
410
- /* best-effort */
411
- }
412
- };
413
- const id = setInterval(tick, 30_000);
414
- tick();
415
- return () => {
416
- cancelled = true;
417
- clearInterval(id);
418
- };
419
- // eslint-disable-next-line react-hooks/exhaustive-deps
420
- }, []);
421
-
422
- // WebSocket lifecycle
423
- useEffect(() => {
424
- const ws = new Ws();
425
- wsRef.current = ws;
426
- setWs(ws);
427
- const offStatus = ws.onStatus((s) => setWsStatus(s));
428
- const offMsg = ws.on((msg: WsMessage) => {
429
- if (msg.type === 'snapshot' && 'data' in msg && msg.data) {
430
- setSnapshot((cur) => ({ ...(cur ?? ({} as Snapshot)), ...msg.data }));
431
- } else if (msg.type === 'change') {
432
- const m = msg;
433
- const file = m.path?.split('/').pop() || m.path || '';
434
- toast.info(`File changed: ${file}`, 2500);
435
- } else if (msg.type === 'tasks:change') {
436
- const m = msg;
437
- setSnapshot((cur) => {
438
- if (!cur) return cur;
439
- const tasks = (cur.tasks || []).map((t) =>
440
- t.id === m.task.id ? m.task : t,
441
- );
442
- const exists = tasks.some((t) => t.id === m.task.id);
443
- return { ...cur, tasks: exists ? tasks : [m.task, ...tasks] };
444
- });
445
- } else if (msg.type === 'tasks:delete') {
446
- const m = msg;
447
- setSnapshot((cur) =>
448
- cur
449
- ? { ...cur, tasks: (cur.tasks || []).filter((t) => t.id !== m.id) }
450
- : cur,
451
- );
452
- } else if (msg.type === 'settings:change') {
453
- const m = msg;
454
- if (m.settings) setSettings(m.settings);
455
- } else if (msg.type === 'project:change') {
456
- // Refresh the whole snapshot to pick up the new active project
457
- api
458
- .get<Snapshot>('/snapshot')
459
- .then((s) => setSnapshot((cur) => ({ ...(cur ?? ({} as Snapshot)), ...s })))
460
- .catch(() => undefined);
461
- } else if (msg.type === 'agents:change' || msg.type === 'schedules:change') {
462
- api
463
- .get<Snapshot>('/snapshot')
464
- .then((s) => setSnapshot((cur) => ({ ...(cur ?? ({} as Snapshot)), ...s })))
465
- .catch(() => undefined);
466
- } else if (msg.type === 'agent:status' || msg.type === 'agent:restarted') {
467
- const m = msg;
468
- setSnapshot((cur) => {
469
- if (!cur) return cur;
470
- const agents = (cur.agents || []).map((a) => (a.name === m.agent.name ? m.agent : a));
471
- return { ...cur, agents };
472
- });
473
- } else if (msg.type === 'artifact:change') {
474
- // Artifacts list refreshes after any artifact mutation.
475
- api
476
- .get<{ artifacts: Artifact[] }>('/artifacts')
477
- .then((d) => {
478
- setSnapshot((cur) => (cur ? { ...cur, artifacts: d.artifacts || [] } : cur));
479
- })
480
- .catch(() => undefined);
481
- } else if (msg.type === 'agent:stuck') {
482
- const m = msg;
483
- setStuckAgents(m.agents || []);
484
- } else if (msg.type === 'dialog:show') {
485
- // v0.5.1: Open a command dialog received from the plugin via WS broadcast.
486
- const m = msg as { type: 'dialog:show'; dialog: DialogDescriptor };
487
- if (m.dialog) {
488
- modalApi.open({
489
- title: m.dialog.title,
490
- width: 520,
491
- children: <CommandDialog dialog={m.dialog} onClose={() => modalApi.close()} />,
492
- });
493
- }
494
- }
495
- });
496
- return () => {
497
- offStatus();
498
- offMsg();
499
- ws.close();
500
- setWs(null);
501
- };
502
- }, [toast]);
503
-
504
- const subscribeToWs = useCallback((cb: (msg: WsMessage) => void) => {
505
- if (!ws) return () => undefined;
506
- return ws.on(cb);
507
- }, [ws]);
508
-
509
- // v3.3.1 — Track recent user interactions. After any mousedown/click/
510
- // focusin/keydown we set a 1500ms "safe" window during which digit-key
511
- // shortcuts are suppressed. This protects against the case where a modal
512
- // closes on a click and the user's next key event (often a key-repeat)
513
- // would otherwise trigger setActiveTab("overview").
514
- // v3.3.0 used 250ms which was too short (keyboard repeat can fire later).
515
- //
516
- // v3.5.4 (bug #4) — The safe window alone was not enough. Users were
517
- // still being bounced to overview after action grace windows expired
518
- // because the transientFocus check (activeElement === body) was nested
519
- // inside the safe-window branch. See the handler below for the new
520
- // two-gate model: (1) body/null focus ALWAYS blocks digit shortcuts;
521
- // (2) the safe window relaxes that block only for the first 1500ms
522
- // after an interaction. After the window passes, the user must
523
- // explicitly focus a real element before digit-key tab switches work.
524
- const safeUntilRef = useRef(0);
525
- useEffect(() => {
526
- const bump = () => {
527
- safeUntilRef.current = Date.now() + 1500;
528
- };
529
- document.addEventListener('mousedown', bump, true);
530
- document.addEventListener('click', bump, true);
531
- document.addEventListener('focusin', bump, true);
532
- document.addEventListener('keydown', bump, true);
533
- return () => {
534
- document.removeEventListener('mousedown', bump, true);
535
- document.removeEventListener('click', bump, true);
536
- document.removeEventListener('focusin', bump, true);
537
- document.removeEventListener('keydown', bump, true);
538
- };
539
- }, []);
540
-
541
- // Keyboard shortcuts
542
- useEffect(() => {
543
- const map: Record<string, string> = {};
544
- TABS.forEach((t, i) => {
545
- map[String(i + 1)] = t.id;
546
- });
547
- const handler = (e: KeyboardEvent) => {
548
- if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') {
549
- e.preventDefault();
550
- setSearchOpen(true);
551
- return;
552
- }
553
- if (e.key === '/' && !e.metaKey && !e.ctrlKey) {
554
- const t = (e.target as HTMLElement | null)?.tagName?.toLowerCase();
555
- if (t !== 'input' && t !== 'textarea' && !((e.target as HTMLElement)?.isContentEditable)) {
556
- e.preventDefault();
557
- setSearchOpen(true);
558
- return;
559
- }
560
- }
561
- // v3.2.0 — broader guard. Digit-key shortcuts must not fire while
562
- // the user is interacting with ANY focusable form control or a
563
- // modal/contentEditable region. Previously this only excluded
564
- // <input> and <textarea>, which let digit presses through when
565
- // focus was on a <select>, <button>, <label>, or contenteditable
566
- // node — causing mysterious tab switches (most often back to
567
- // "overview" via the `1` shortcut).
568
- //
569
- // v3.3.1 — "click after modal close" guard (extended). When a modal
570
- // closes (e.g. the user clicks "Submit to Odin" in the Tasks tab),
571
- // React portals the modal out of the DOM and the previously-focused
572
- // button is unmounted. Focus falls back to <body>. If the user then
573
- // accidentally taps a digit key (1/2/3/...) — or a key-repeat fires
574
- // while they're still mid-click — the handler would switch tabs.
575
- // The safe window is now 1500ms (up from 250ms) and covers all
576
- // interaction types (mousedown/click/focusin/keydown).
577
- //
578
- // v3.3.1 — transient-focus check. If active element is body/null,
579
- // treat it as a transient focus state (modal close → body fallback)
580
- // and bail. v3.5.4 (bug #4): this check is now independent of the
581
- // safe window — it ALWAYS applies. Previously it was only effective
582
- // inside the 1500ms grace window, which let digit-key presses through
583
- // once the window expired. The user would see themselves bounced back
584
- // to overview (digit `1`) after every action whose grace window had
585
- // already elapsed. The safe window now only relaxes the rule
586
- // temporarily for the action that JUST happened; it must be combined
587
- // with a real focus element for digit-key shortcuts to fire.
588
- const activeEl = document.activeElement;
589
- const transientFocus = !activeEl || activeEl === document.body || activeEl === document.documentElement;
590
- if (transientFocus) {
591
- // Even within the grace window, body/null focus means there is
592
- // no real input target — refuse to switch tabs. The user must
593
- // explicitly click into a focusable element (input, button, etc.)
594
- // to enable digit-key tab switching again.
595
- return;
596
- }
597
- const target = e.target as HTMLElement | null;
598
- const tag = target?.tagName?.toLowerCase();
599
- const isFormControl =
600
- tag === 'input' ||
601
- tag === 'textarea' ||
602
- tag === 'select' ||
603
- tag === 'button' ||
604
- tag === 'option' ||
605
- tag === 'label' ||
606
- !!target?.isContentEditable;
607
- // Inside a form? Even safer: walk up to check.
608
- let inForm = false;
609
- if (target && typeof target.closest === 'function') {
610
- inForm = !!target.closest('form, [role="dialog"], [contenteditable], [data-no-key]');
611
- }
612
- // v3.5.2 — reject key-repeat events. When a user presses and holds
613
- // a digit key the browser fires repeated keydown events; we must only
614
- // act on the first (e.repeat === false). This prevents the modal-close
615
- // + key-repeat scenario from switching tabs after the safe window closes.
616
- if (e.repeat) return;
617
- // v3.6.1 — Check isModalOpenRef (not the closure variable) so we always
618
- // see the current value even between state update and effect re-run.
619
- if (isModalOpenRef.current) return;
620
- if (
621
- isFormControl ||
622
- inForm ||
623
- e.metaKey ||
624
- e.ctrlKey ||
625
- e.altKey ||
626
- e.shiftKey ||
627
- Date.now() < safeUntilRef.current
628
- )
629
- return;
630
- const id = map[e.key];
631
- if (id) {
632
- e.preventDefault();
633
- handleTabChange(id);
634
- }
635
- };
636
- document.addEventListener('keydown', handler);
637
- return () => document.removeEventListener('keydown', handler);
638
- }, []);
639
-
640
- const refreshSnapshot = useMemo(
641
- () => async () => {
642
- try {
643
- const s = await api.get<Snapshot>('/snapshot');
644
- setSnapshot((cur) => ({ ...(cur ?? ({} as Snapshot)), ...s }));
645
- } catch (err) {
646
- toast.error(`Refresh failed: ${(err as Error).message}`);
647
- }
648
- },
649
- [toast],
650
- );
651
-
652
- // v4.9.0 — Settings mode: entering settings tab activates the sidebar layout.
653
- // Navigating to any other tab exits settings mode.
654
- const handleTabChange = useCallback((id: string) => {
655
- setActiveTab(id);
656
- if (id === 'settings') setSettingsMode(true);
657
- else setSettingsMode(false);
658
- }, []);
659
-
660
- // v3.20.3 — Merge built-in tabs with installed mod views. Mod views
661
- // are appended after the built-in tabs and use the same rendering
662
- // surface; their tab ids are mod view ids (e.g. 'graphify:web').
663
- // Declared AFTER refreshSnapshot so the deps list has the right value.
664
- const mergedTabs = useMemo(() => {
665
- const modTabs = modViews.map((v) => ({
666
- id: v.id,
667
- label: v.label,
668
- icon: v.kind === 'iframe' ? Globe : LayoutTemplate,
669
- isMod: true,
670
- modId: v.modId,
671
- }));
672
- return [...TABS, ...modTabs];
673
- }, [modViews]);
674
-
675
- // Surface the underlying view for the active tab (built-in or mod).
676
-
677
- // v6.0.0 — ClineCore is in-process; reflect that in the topbar badge.
678
- // The plugin's setup() always brings up ClineCore (or fails fast), so
679
- // by the time the dashboard renders, the runtime is active.
680
- const clineStatus = useMemo(() => ({
681
- state: 'active' as const,
682
- label: 'in-process',
683
- detail: 'ClineCore running in-process (v6.0.0, no subprocess)',
684
- }), []);
685
- // Declared AFTER refreshSnapshot so its useMemo factory can capture it.
686
- const renderedView = useMemo(() => {
687
- if (snapshot && settings) {
688
- const viewProps: ViewProps = {
689
- snapshot,
690
- settings,
691
- activeTab,
692
- setActiveTab: handleTabChange,
693
- refreshSnapshot,
694
- settingsMode,
695
- settingsActiveSection,
696
- setSettingsActiveSection,
697
- };
698
- return renderActiveView(activeTab, viewProps, modViews, modViewsReloadKey);
699
- }
700
- return null;
701
- }, [activeTab, snapshot, settings, modViews, modViewsReloadKey, refreshSnapshot, settingsMode, settingsActiveSection, setSettingsActiveSection, handleTabChange]);
702
-
703
- const refreshProjects = async () => {
704
- try {
705
- const r = await api.get<{ projects: ProjectRecord[]; active: string | null }>('/projects');
706
- setSnapshot((cur) =>
707
- cur ? { ...cur, projects: r.projects || [], activeProject: r.projects?.find((p) => p.id === r.active) || null } : cur,
708
- );
709
- } catch (err) {
710
- toast.error(`Projects refresh failed: ${(err as Error).message}`);
711
- }
712
- };
713
-
714
- const onActivateProject = async (id: string) => {
715
- try {
716
- await api.post(`/projects/${encodeURIComponent(id)}/activate`);
717
- // WS will fire project:change and refresh; but also reflect locally
718
- setSnapshot((cur) => {
719
- if (!cur) return cur;
720
- return {
721
- ...cur,
722
- activeProject: cur.projects.find((p) => p.id === id) || null,
723
- };
724
- });
725
- } catch (err) {
726
- toast.error(`Activate failed: ${(err as Error).message}`);
727
- }
728
- };
729
-
730
- const onSearchSelect = (r: SearchResult) => {
731
- const t = r.type;
732
- if (t === 'agent') handleTabChange('agents');
733
- else if (t === 'task') handleTabChange('tasks');
734
- else if (t === 'mod') handleTabChange('mods');
735
- else if (t === 'schedule') handleTabChange('schedules');
736
- else if (t === 'project') {
737
- const id = (r.item as ProjectRecord).id;
738
- onActivateProject(id);
739
- } else if (t === 'command') {
740
- // No command page; just toast
741
- toast.info(`/${(r.item as { name: string }).name} — run from the TUI`, 2500);
742
- } else if (t === 'setting') {
743
- // v3.0.4 — Jump to Settings view and scroll to the matching row.
744
- const settingId = (r.item as { id?: string; path?: string }).id
745
- || (r.item as { path?: string }).path
746
- || '';
747
- handleTabChange('settings');
748
- // Defer the scroll to allow the view to mount, then apply a brief
749
- // CSS highlight. We poll a few times because the Settings view
750
- // mounts lazily after tab switch.
751
- const tryScroll = (tries: number) => {
752
- if (tries <= 0) return;
753
- const el = settingId
754
- ? document.querySelector(`[data-setting-id="${CSS.escape(settingId)}"]`)
755
- : null;
756
- if (el) {
757
- (el as HTMLElement).scrollIntoView({ behavior: 'smooth', block: 'center' });
758
- el.classList.remove('setting-flash');
759
- // Force reflow so the animation re-triggers on rapid jumps.
760
- void (el as HTMLElement).offsetWidth;
761
- el.classList.add('setting-flash');
762
- window.setTimeout(() => el.classList.remove('setting-flash'), 1500);
763
- } else {
764
- window.setTimeout(() => tryScroll(tries - 1), 80);
765
- }
766
- };
767
- window.setTimeout(() => tryScroll(15), 60);
768
- }
769
- };
770
-
771
- const layout = settings?.ui?.layout || 'topnav';
772
- const showHeader = settings?.ui?.showHeader !== false;
773
-
774
- return (
775
- <div className="app" data-layout={layout} data-active-tab={activeTab}>
776
- {/* v4.8.0 — WCAG 2.2 AA: skip-to-main-content link. Hidden visually
777
- until focused; clicking moves keyboard focus past the nav into
778
- the active view. Screen readers and keyboard users can jump
779
- past the topbar + sidebar without tabbing through every tab. */}
780
- <a className="skip-to-main" href="#main-content">
781
- Skip to main content
782
- </a>
783
- {showHeader && (
784
- <Topbar
785
- activeTab={activeTab}
786
- onTabChange={handleTabChange}
787
- wsStatus={wsStatus}
788
- version={VERSION}
789
- activeProject={snapshot?.activeProject || null}
790
- projects={snapshot?.projects || []}
791
- onProjectChange={onActivateProject}
792
- onProjectsRefresh={refreshProjects}
793
- onOpenSearch={() => setSearchOpen(true)}
794
- settings={settings}
795
- notificationsSlot={<Notifications wsSubscribe={subscribeToWs} />}
796
- showTabs={layout === 'topnav'}
797
- extraTabs={mergedTabs}
798
- clineStatus={clineStatus}
799
- />
800
- )}
801
- {stuckAgents.length > 0 && !stuckBannerDismissed && (
802
- <div className="stuck-banner" role="alert">
803
- <AlertTriangle size={16} />
804
- <span>
805
- <strong>{stuckAgents.length}</strong> agent{stuckAgents.length === 1 ? '' : 's'} stuck:{' '}
806
- <span className="mono">
807
- {stuckAgents.map((a) => a.name).join(', ')}
808
- </span>
809
- </span>
810
- <Button
811
- variant="secondary"
812
- size="sm"
813
- onClick={() => {
814
- handleTabChange('agents');
815
- setStuckBannerDismissed(true);
816
- }}
817
- >
818
- Review
819
- </Button>
820
- <button
821
- type="button"
822
- className="icon-btn"
823
- aria-label="Dismiss"
824
- onClick={() => setStuckBannerDismissed(true)}
825
- >
826
- <X size={14} />
827
- </button>
828
- </div>
829
- )}
830
- <div className="layout-body">
831
- {layout !== 'topnav' && (
832
- <Sidebar
833
- tabs={mergedTabs}
834
- activeTab={activeTab}
835
- onTabChange={handleTabChange}
836
- settingsMode={settingsMode}
837
- settingsActiveSection={settingsActiveSection}
838
- onSettingsSectionChange={setSettingsActiveSection}
839
- onExitSettings={() => handleTabChange('overview')}
840
- />
841
- )}
842
- <main className="content" id="main-content" tabIndex={-1}>
843
- {bootError && (
844
- <div className="boot-error">
845
- <h2>Dashboard unavailable</h2>
846
- <p>{bootError}</p>
847
- {authPromptOpen && (
848
- <TokenEntryForm
849
- onSaved={() => {
850
- setAuthPromptOpen(false);
851
- retryBoot();
852
- }}
853
- />
854
- )}
855
- <p className="boot-error-hint">
856
- Make sure the Bizar dashboard server is running. Try{' '}
857
- <code>bizar-dash start</code> in your terminal.
858
- </p>
859
- </div>
860
- )}
861
- {!bootError && (!snapshot || !settings) && (
862
- <div className="loading">
863
- <Spinner size="lg" />
864
- <p>Loading Bizar…</p>
865
- </div>
866
- )}
867
- {renderedView && (
868
- <Suspense fallback={<div className="loading"><Spinner size="lg" /><p>Loading…</p></div>}>
869
- <ViewErrorBoundary>{renderedView}</ViewErrorBoundary>
870
- </Suspense>
871
- )}
872
- </main>
873
- </div>
874
- <SearchModal
875
- open={searchOpen}
876
- onClose={() => setSearchOpen(false)}
877
- onSelect={onSearchSelect}
878
- />
879
- </div>
880
- );
881
- }
882
-
883
- // v4.5.x — Catches render-time errors in the active view so a single bad
884
- // component doesn't bring down the whole dashboard (topbar, sidebar, WS).
885
- // Falls back to a friendly error card with a Retry button that re-mounts
886
- // the wrapped subtree by remounting the boundary via React `key` bumps.
887
- class ViewErrorBoundary extends Component<
888
- { children: React.ReactNode },
889
- { err: Error | null }
890
- > {
891
- state: { err: Error | null } = { err: null };
892
- static getDerivedStateFromError(err: Error): { err: Error | null } {
893
- return { err };
894
- }
895
- componentDidCatch(err: Error, info: { componentStack?: string }) {
896
- // eslint-disable-next-line no-console
897
- logger.error('[ViewErrorBoundary] caught render error:', err, info?.componentStack);
898
- }
899
- reset = () => this.setState({ err: null });
900
- override render() {
901
- if (this.state.err) {
902
- return (
903
- <div className="view-error-fallback" role="alert">
904
- <AlertTriangle size={20} />
905
- <div className="view-error-body">
906
- <strong>This view crashed.</strong>
907
- <pre>{this.state.err.message}</pre>
908
- <Button variant="secondary" size="sm" onClick={this.reset}>Retry</Button>
909
- </div>
910
- </div>
911
- );
912
- }
913
- return this.props.children;
914
- }
915
- }