@polderlabs/bizar 10.4.2 → 10.4.4
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.
- package/bizar-dash/dist/assets/{ActivityFeed-DtRtg-JN.js → ActivityFeed-RQeyFifx.js} +1 -1
- package/bizar-dash/dist/assets/{ActivityFeed-DtRtg-JN.js.map → ActivityFeed-RQeyFifx.js.map} +1 -1
- package/bizar-dash/dist/assets/{ActivityView-CoTXDyU_.js → ActivityView-CibyssPM.js} +1 -1
- package/bizar-dash/dist/assets/{ActivityView-CoTXDyU_.js.map → ActivityView-CibyssPM.js.map} +1 -1
- package/bizar-dash/dist/assets/AdminView-BjczABeB.js +11 -0
- package/bizar-dash/dist/assets/AdminView-BjczABeB.js.map +1 -0
- package/bizar-dash/dist/assets/{AgentActivity-CrX7LmQ6.js → AgentActivity-Bywt1twv.js} +1 -1
- package/bizar-dash/dist/assets/{AgentActivity-CrX7LmQ6.js.map → AgentActivity-Bywt1twv.js.map} +1 -1
- package/bizar-dash/dist/assets/AgentsView-BnOjjM74.js +15 -0
- package/bizar-dash/dist/assets/AgentsView-BnOjjM74.js.map +1 -0
- package/bizar-dash/dist/assets/{Alert-B6DjfRFI.js → Alert-BZGk39mB.js} +1 -1
- package/bizar-dash/dist/assets/{Alert-B6DjfRFI.js.map → Alert-BZGk39mB.js.map} +1 -1
- package/bizar-dash/dist/assets/{ArtifactsView-znhN8GTR.js → ArtifactsView-CfYB1le1.js} +3 -3
- package/bizar-dash/dist/assets/ArtifactsView-CfYB1le1.js.map +1 -0
- package/bizar-dash/dist/assets/{AuthView-Rk8eKUPr.js → AuthView-D4frSShw.js} +1 -1
- package/bizar-dash/dist/assets/{AuthView-Rk8eKUPr.js.map → AuthView-D4frSShw.js.map} +1 -1
- package/bizar-dash/dist/assets/Avatar-BYg0rkPA.js +6 -0
- package/bizar-dash/dist/assets/Avatar-BYg0rkPA.js.map +1 -0
- package/bizar-dash/dist/assets/{BackgroundJobsView-BLWuN9qz.js → BackgroundJobsView-DEStvILz.js} +1 -1
- package/bizar-dash/dist/assets/{BackgroundJobsView-BLWuN9qz.js.map → BackgroundJobsView-DEStvILz.js.map} +1 -1
- package/bizar-dash/dist/assets/{BackupView-CqbtcnuQ.js → BackupView-B875KQa4.js} +1 -1
- package/bizar-dash/dist/assets/{BackupView-CqbtcnuQ.js.map → BackupView-B875KQa4.js.map} +1 -1
- package/bizar-dash/dist/assets/Banner-C8HEcT5l.js +1 -0
- package/bizar-dash/dist/assets/Banner-C8HEcT5l.js.map +1 -0
- package/bizar-dash/dist/assets/{Card-rF59wHiO.js → Card-BtsmkhJa.js} +1 -1
- package/bizar-dash/dist/assets/{Card-rF59wHiO.js.map → Card-BtsmkhJa.js.map} +1 -1
- package/bizar-dash/dist/assets/ChatView-CkNS0SD8.js +9 -0
- package/bizar-dash/dist/assets/ChatView-CkNS0SD8.js.map +1 -0
- package/bizar-dash/dist/assets/{Chip-Dc2KmeWl.js → Chip-XPOk0d_E.js} +1 -1
- package/bizar-dash/dist/assets/{Chip-Dc2KmeWl.js.map → Chip-XPOk0d_E.js.map} +1 -1
- package/bizar-dash/dist/assets/{ClaudeSessionsView-CiaHQdDZ.js → ClaudeSessionsView-BprGo67K.js} +1 -1
- package/bizar-dash/dist/assets/{ClaudeSessionsView-CiaHQdDZ.js.map → ClaudeSessionsView-BprGo67K.js.map} +1 -1
- package/bizar-dash/dist/assets/ClipboardView-CPlTU5VG.js +1 -0
- package/bizar-dash/dist/assets/ClipboardView-CPlTU5VG.js.map +1 -0
- package/bizar-dash/dist/assets/{ConfigView-B2ysnTZU.js → ConfigView-BntUrW7s.js} +1 -1
- package/bizar-dash/dist/assets/{ConfigView-B2ysnTZU.js.map → ConfigView-BntUrW7s.js.map} +1 -1
- package/bizar-dash/dist/assets/DiagnosticsView-4x0Zu3yP.js +2 -0
- package/bizar-dash/dist/assets/DiagnosticsView-4x0Zu3yP.js.map +1 -0
- package/bizar-dash/dist/assets/DialogsView-Xd02dVAX.js +6 -0
- package/bizar-dash/dist/assets/DialogsView-Xd02dVAX.js.map +1 -0
- package/bizar-dash/dist/assets/{DoctorView-BQwqvMAh.js → DoctorView-DepBdQGR.js} +2 -2
- package/bizar-dash/dist/assets/{DoctorView-BQwqvMAh.js.map → DoctorView-DepBdQGR.js.map} +1 -1
- package/bizar-dash/dist/assets/{EmptyState-D65J5FQN.js → EmptyState-DjKtY1Qi.js} +1 -1
- package/bizar-dash/dist/assets/{EmptyState-D65J5FQN.js.map → EmptyState-DjKtY1Qi.js.map} +1 -1
- package/bizar-dash/dist/assets/{EnvVarsView-DX-3oGH-.js → EnvVarsView-S4Pbm128.js} +3 -3
- package/bizar-dash/dist/assets/{EnvVarsView-DX-3oGH-.js.map → EnvVarsView-S4Pbm128.js.map} +1 -1
- package/bizar-dash/dist/assets/{ErrorState-DULZnttJ.js → ErrorState-CBJbEQ8B.js} +1 -1
- package/bizar-dash/dist/assets/{ErrorState-DULZnttJ.js.map → ErrorState-CBJbEQ8B.js.map} +1 -1
- package/bizar-dash/dist/assets/EvalView-BeAyMCD1.js +1 -0
- package/bizar-dash/dist/assets/{EvalView-CpdFu_ID.js.map → EvalView-BeAyMCD1.js.map} +1 -1
- package/bizar-dash/dist/assets/{GoalsView-Br8oPPfb.js → GoalsView-BZr8In4c.js} +1 -1
- package/bizar-dash/dist/assets/{GoalsView-Br8oPPfb.js.map → GoalsView-BZr8In4c.js.map} +1 -1
- package/bizar-dash/dist/assets/{Grid-BSXhsCPC.js → Grid--WKvqu5h.js} +1 -1
- package/bizar-dash/dist/assets/{Grid-BSXhsCPC.js.map → Grid--WKvqu5h.js.map} +1 -1
- package/bizar-dash/dist/assets/{HistoryView-D0J3aoZF.js → HistoryView-djEbzqtk.js} +1 -1
- package/bizar-dash/dist/assets/{HistoryView-D0J3aoZF.js.map → HistoryView-djEbzqtk.js.map} +1 -1
- package/bizar-dash/dist/assets/{Input-CZmRw5QX.js → Input-h_nSOtwm.js} +1 -1
- package/bizar-dash/dist/assets/{Input-CZmRw5QX.js.map → Input-h_nSOtwm.js.map} +1 -1
- package/bizar-dash/dist/assets/LibrariesView-Vez8CvNq.js +6 -0
- package/bizar-dash/dist/assets/{LibrariesView-B1YX9wzp.js.map → LibrariesView-Vez8CvNq.js.map} +1 -1
- package/bizar-dash/dist/assets/{LightRAGView-CykR0t9y.js → LightRAGView-DvqzQoBp.js} +1 -1
- package/bizar-dash/dist/assets/{LightRAGView-CykR0t9y.js.map → LightRAGView-DvqzQoBp.js.map} +1 -1
- package/bizar-dash/dist/assets/{ListHeader-Dpun-r36.js → ListHeader-CM8QoIWG.js} +1 -1
- package/bizar-dash/dist/assets/{ListHeader-Dpun-r36.js.map → ListHeader-CM8QoIWG.js.map} +1 -1
- package/bizar-dash/dist/assets/{MemoryView-BbER5ZKt.js → MemoryView-DnL8E2vI.js} +1 -1
- package/bizar-dash/dist/assets/{MemoryView-BbER5ZKt.js.map → MemoryView-DnL8E2vI.js.map} +1 -1
- package/bizar-dash/dist/assets/{MessageBubble-5vUAquDx.js → MessageBubble-CrCJ0SYG.js} +1 -1
- package/bizar-dash/dist/assets/{MessageBubble-5vUAquDx.js.map → MessageBubble-CrCJ0SYG.js.map} +1 -1
- package/bizar-dash/dist/assets/MiscView-B2YQXhZ_.js +1 -0
- package/bizar-dash/dist/assets/MiscView-B2YQXhZ_.js.map +1 -0
- package/bizar-dash/dist/assets/{ModsView-P7gDeNHM.js → ModsView-BVM3X5xc.js} +1 -1
- package/bizar-dash/dist/assets/{ModsView-P7gDeNHM.js.map → ModsView-BVM3X5xc.js.map} +1 -1
- package/bizar-dash/dist/assets/NotificationsView-A-Xs8BL1.js +6 -0
- package/bizar-dash/dist/assets/NotificationsView-A-Xs8BL1.js.map +1 -0
- package/bizar-dash/dist/assets/ObsidianView-mI3qW9OP.js +1 -0
- package/bizar-dash/dist/assets/{ObsidianView-Ci80gnwS.js.map → ObsidianView-mI3qW9OP.js.map} +1 -1
- package/bizar-dash/dist/assets/OverviewView-By69Cr5H.js +1 -0
- package/bizar-dash/dist/assets/OverviewView-By69Cr5H.js.map +1 -0
- package/bizar-dash/dist/assets/{ProgressBar-BahmeJhP.js → ProgressBar-BnaBqv-W.js} +1 -1
- package/bizar-dash/dist/assets/{ProgressBar-BahmeJhP.js.map → ProgressBar-BnaBqv-W.js.map} +1 -1
- package/bizar-dash/dist/assets/ProjectsView-C81WTFka.js +11 -0
- package/bizar-dash/dist/assets/ProjectsView-C81WTFka.js.map +1 -0
- package/bizar-dash/dist/assets/{ProvidersView-BftGn8Cr.js → ProvidersView-2cn-TALE.js} +2 -2
- package/bizar-dash/dist/assets/{ProvidersView-BftGn8Cr.js.map → ProvidersView-2cn-TALE.js.map} +1 -1
- package/bizar-dash/dist/assets/{SchedulesView-CtNxtzN6.js → SchedulesView-o9AtIsH9.js} +1 -1
- package/bizar-dash/dist/assets/{SchedulesView-CtNxtzN6.js.map → SchedulesView-o9AtIsH9.js.map} +1 -1
- package/bizar-dash/dist/assets/{Select-0VcfTDeL.js → Select-CiGWEN8I.js} +1 -1
- package/bizar-dash/dist/assets/{Select-0VcfTDeL.js.map → Select-CiGWEN8I.js.map} +1 -1
- package/bizar-dash/dist/assets/{SettingsRow-BLic3DQl.js → SettingsRow-CflGJymA.js} +1 -1
- package/bizar-dash/dist/assets/{SettingsRow-BLic3DQl.js.map → SettingsRow-CflGJymA.js.map} +1 -1
- package/bizar-dash/dist/assets/{SettingsView-BjS3i4nr.js → SettingsView-2HSFJPPK.js} +1 -1
- package/bizar-dash/dist/assets/{SettingsView-BjS3i4nr.js.map → SettingsView-2HSFJPPK.js.map} +1 -1
- package/bizar-dash/dist/assets/{Sheet-BOProMs2.js → Sheet-B-CMP9-_.js} +1 -1
- package/bizar-dash/dist/assets/{Sheet-BOProMs2.js.map → Sheet-B-CMP9-_.js.map} +1 -1
- package/bizar-dash/dist/assets/{Slider-B0eLEEUt.js → Slider-Z4TP_CXY.js} +1 -1
- package/bizar-dash/dist/assets/{Slider-B0eLEEUt.js.map → Slider-Z4TP_CXY.js.map} +1 -1
- package/bizar-dash/dist/assets/{Sparkline-SSrdgyW0.js → Sparkline-cvkv2HbU.js} +1 -1
- package/bizar-dash/dist/assets/{Sparkline-SSrdgyW0.js.map → Sparkline-cvkv2HbU.js.map} +1 -1
- package/bizar-dash/dist/assets/{Switch-B3EQzb_q.js → Switch-jcYo4lmp.js} +1 -1
- package/bizar-dash/dist/assets/{Switch-B3EQzb_q.js.map → Switch-jcYo4lmp.js.map} +1 -1
- package/bizar-dash/dist/assets/{TasksView-SWjF_ZUQ.js → TasksView-CQaSFzWY.js} +2 -2
- package/bizar-dash/dist/assets/{TasksView-SWjF_ZUQ.js.map → TasksView-CQaSFzWY.js.map} +1 -1
- package/bizar-dash/dist/assets/{Textarea-QuofpMlq.js → Textarea-xnAybFGd.js} +1 -1
- package/bizar-dash/dist/assets/{Textarea-QuofpMlq.js.map → Textarea-xnAybFGd.js.map} +1 -1
- package/bizar-dash/dist/assets/UpdateView-BUAEcRCX.js +1 -0
- package/bizar-dash/dist/assets/UpdateView-BUAEcRCX.js.map +1 -0
- package/bizar-dash/dist/assets/{UsageView-BqOw6FBg.js → UsageView-DMFbBOfH.js} +1 -1
- package/bizar-dash/dist/assets/{UsageView-BqOw6FBg.js.map → UsageView-DMFbBOfH.js.map} +1 -1
- package/bizar-dash/dist/assets/{ViewHeader-B6P-C4GD.js → ViewHeader-C-t1U2cQ.js} +1 -1
- package/bizar-dash/dist/assets/{ViewHeader-B6P-C4GD.js.map → ViewHeader-C-t1U2cQ.js.map} +1 -1
- package/bizar-dash/dist/assets/VoiceView-aoPVT6LJ.js +1 -0
- package/bizar-dash/dist/assets/VoiceView-aoPVT6LJ.js.map +1 -0
- package/bizar-dash/dist/assets/{brain-CwZt9Kta.js → brain-DhAzcHUk.js} +1 -1
- package/bizar-dash/dist/assets/{brain-CwZt9Kta.js.map → brain-DhAzcHUk.js.map} +1 -1
- package/bizar-dash/dist/assets/{calendar-C5oK0_nM.js → calendar-B9Wbemh7.js} +1 -1
- package/bizar-dash/dist/assets/{calendar-C5oK0_nM.js.map → calendar-B9Wbemh7.js.map} +1 -1
- package/bizar-dash/dist/assets/{circle-D1Lc_P2a.js → circle-6NJF_Xl6.js} +1 -1
- package/bizar-dash/dist/assets/{circle-D1Lc_P2a.js.map → circle-6NJF_Xl6.js.map} +1 -1
- package/bizar-dash/dist/assets/{circle-alert-HdHe7m8K.js → circle-alert-Dz8lGfCZ.js} +1 -1
- package/bizar-dash/dist/assets/{circle-alert-HdHe7m8K.js.map → circle-alert-Dz8lGfCZ.js.map} +1 -1
- package/bizar-dash/dist/assets/{circle-check-Cs-xoV7R.js → circle-check-DFB50ojt.js} +1 -1
- package/bizar-dash/dist/assets/{circle-check-Cs-xoV7R.js.map → circle-check-DFB50ojt.js.map} +1 -1
- package/bizar-dash/dist/assets/{clock-DEIuPV60.js → clock-ClVA6-hQ.js} +1 -1
- package/bizar-dash/dist/assets/{clock-DEIuPV60.js.map → clock-ClVA6-hQ.js.map} +1 -1
- package/bizar-dash/dist/assets/{cmdk-DVxytz9i.js → cmdk-C3F6LD6N.js} +5 -5
- package/bizar-dash/dist/assets/{cmdk-DVxytz9i.js.map → cmdk-C3F6LD6N.js.map} +1 -1
- package/bizar-dash/dist/assets/{dnd-kit-DrHErSkw.js → dnd-kit-BAOLODOI.js} +1 -1
- package/bizar-dash/dist/assets/{dnd-kit-DrHErSkw.js.map → dnd-kit-BAOLODOI.js.map} +1 -1
- package/bizar-dash/dist/assets/{download-Clkq90Ct.js → download-BvJddD8D.js} +1 -1
- package/bizar-dash/dist/assets/{download-Clkq90Ct.js.map → download-BvJddD8D.js.map} +1 -1
- package/bizar-dash/dist/assets/{external-link-D5tq2o_I.js → external-link-DvL89gaZ.js} +1 -1
- package/bizar-dash/dist/assets/{external-link-D5tq2o_I.js.map → external-link-DvL89gaZ.js.map} +1 -1
- package/bizar-dash/dist/assets/{file-text-CQ12t7Oc.js → file-text-BOroDtZE.js} +1 -1
- package/bizar-dash/dist/assets/{file-text-CQ12t7Oc.js.map → file-text-BOroDtZE.js.map} +1 -1
- package/bizar-dash/dist/assets/{filter-BcbE37wP.js → filter-DwPjMRQt.js} +1 -1
- package/bizar-dash/dist/assets/{filter-BcbE37wP.js.map → filter-DwPjMRQt.js.map} +1 -1
- package/bizar-dash/dist/assets/{git-pull-request-D1qNDRia.js → git-pull-request-TDb1inux.js} +1 -1
- package/bizar-dash/dist/assets/{git-pull-request-D1qNDRia.js.map → git-pull-request-TDb1inux.js.map} +1 -1
- package/bizar-dash/dist/assets/{index-CASP1n-5.js → index-B1OIS316.js} +1 -1
- package/bizar-dash/dist/assets/{index-CASP1n-5.js.map → index-B1OIS316.js.map} +1 -1
- package/bizar-dash/dist/assets/{index-C4Vq_2LM.js → index-CR0TeJmc.js} +1 -1
- package/bizar-dash/dist/assets/{index-C4Vq_2LM.js.map → index-CR0TeJmc.js.map} +1 -1
- package/bizar-dash/dist/assets/{main-CcQfRfIJ.css → main-B7D_IMTh.css} +1 -1
- package/bizar-dash/dist/assets/main-BURAlktW.js +277 -0
- package/bizar-dash/dist/assets/main-BURAlktW.js.map +1 -0
- package/bizar-dash/dist/assets/{network-DJdvVaHx.js → network-BcCoCzIY.js} +1 -1
- package/bizar-dash/dist/assets/{network-DJdvVaHx.js.map → network-BcCoCzIY.js.map} +1 -1
- package/bizar-dash/dist/assets/{pause-DUn9L5PB.js → pause-BllItT2_.js} +1 -1
- package/bizar-dash/dist/assets/{pause-DUn9L5PB.js.map → pause-BllItT2_.js.map} +1 -1
- package/bizar-dash/dist/assets/{pencil-DVrBlYV3.js → pencil-DAwM-CC3.js} +1 -1
- package/bizar-dash/dist/assets/{pencil-DVrBlYV3.js.map → pencil-DAwM-CC3.js.map} +1 -1
- package/bizar-dash/dist/assets/{play-CZjsr3XM.js → play-mJh67AXo.js} +2 -2
- package/bizar-dash/dist/assets/{play-CZjsr3XM.js.map → play-mJh67AXo.js.map} +1 -1
- package/bizar-dash/dist/assets/{plug-BKuyIe5W.js → plug-BjfntCMC.js} +1 -1
- package/bizar-dash/dist/assets/{plug-BKuyIe5W.js.map → plug-BjfntCMC.js.map} +1 -1
- package/bizar-dash/dist/assets/{plus-77h62zZm.js → plus-Cm6-fo14.js} +1 -1
- package/bizar-dash/dist/assets/{plus-77h62zZm.js.map → plus-Cm6-fo14.js.map} +1 -1
- package/bizar-dash/dist/assets/{power-DLmx-gCP.js → power-CXN9y2KV.js} +1 -1
- package/bizar-dash/dist/assets/{power-DLmx-gCP.js.map → power-CXN9y2KV.js.map} +1 -1
- package/bizar-dash/dist/assets/{power-off-BFKzHXic.js → power-off-DUZqwMf9.js} +1 -1
- package/bizar-dash/dist/assets/{power-off-BFKzHXic.js.map → power-off-DUZqwMf9.js.map} +1 -1
- package/bizar-dash/dist/assets/{refresh-ccw-DenGBOcq.js → refresh-ccw-DA8_IB9l.js} +1 -1
- package/bizar-dash/dist/assets/{refresh-ccw-DenGBOcq.js.map → refresh-ccw-DA8_IB9l.js.map} +1 -1
- package/bizar-dash/dist/assets/{refresh-cw-ENBVR8hW.js → refresh-cw-DwlP59en.js} +1 -1
- package/bizar-dash/dist/assets/{refresh-cw-ENBVR8hW.js.map → refresh-cw-DwlP59en.js.map} +1 -1
- package/bizar-dash/dist/assets/{rotate-ccw-MxryaY7q.js → rotate-ccw-DMTASfAn.js} +1 -1
- package/bizar-dash/dist/assets/{rotate-ccw-MxryaY7q.js.map → rotate-ccw-DMTASfAn.js.map} +1 -1
- package/bizar-dash/dist/assets/{rotate-cw-CDOlRtUR.js → rotate-cw-BDX5SE2s.js} +1 -1
- package/bizar-dash/dist/assets/{rotate-cw-CDOlRtUR.js.map → rotate-cw-BDX5SE2s.js.map} +1 -1
- package/bizar-dash/dist/assets/{save-Db1b-1Uq.js → save-Y0jPK5D6.js} +1 -1
- package/bizar-dash/dist/assets/{save-Db1b-1Uq.js.map → save-Y0jPK5D6.js.map} +1 -1
- package/bizar-dash/dist/assets/{send-yz6HFZM5.js → send-C8wwPHhR.js} +2 -2
- package/bizar-dash/dist/assets/{send-yz6HFZM5.js.map → send-C8wwPHhR.js.map} +1 -1
- package/bizar-dash/dist/assets/{shield-alert-DY6RAaC8.js → shield-alert-DEvq9UG0.js} +1 -1
- package/bizar-dash/dist/assets/{shield-alert-DY6RAaC8.js.map → shield-alert-DEvq9UG0.js.map} +1 -1
- package/bizar-dash/dist/assets/{trash-2-Cv7U38jw.js → trash-2-D6qePV_q.js} +2 -2
- package/bizar-dash/dist/assets/{trash-2-Cv7U38jw.js.map → trash-2-D6qePV_q.js.map} +1 -1
- package/bizar-dash/dist/assets/{trending-up-CRPNagyX.js → trending-up-BZzo_1ic.js} +1 -1
- package/bizar-dash/dist/assets/{trending-up-CRPNagyX.js.map → trending-up-BZzo_1ic.js.map} +1 -1
- package/bizar-dash/dist/assets/{triangle-alert-tUTXbcEP.js → triangle-alert-B2oHbmoh.js} +1 -1
- package/bizar-dash/dist/assets/{triangle-alert-tUTXbcEP.js.map → triangle-alert-B2oHbmoh.js.map} +1 -1
- package/bizar-dash/dist/assets/{upload-Cq7uTsrT.js → upload-Dy59aZn6.js} +1 -1
- package/bizar-dash/dist/assets/{upload-Cq7uTsrT.js.map → upload-Dy59aZn6.js.map} +1 -1
- package/bizar-dash/dist/assets/{zap-5lAQBAPX.js → zap-DENVhJnO.js} +2 -2
- package/bizar-dash/dist/assets/{zap-5lAQBAPX.js.map → zap-DENVhJnO.js.map} +1 -1
- package/bizar-dash/dist/index.html +3 -3
- package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/bizar-dash/src/web/v8/App.tsx +87 -6
- package/bizar-dash/src/web/v8/__tests__/chat-view.test.tsx +4 -4
- package/bizar-dash/src/web/v8/shell/AppShell.tsx +38 -2
- package/bizar-dash/src/web/v8/shell/Sidebar.tsx +52 -38
- package/bizar-dash/src/web/v8/shell/Topbar.tsx +62 -3
- package/bizar-dash/src/web/v8/ui/styles/globals.css +40 -0
- package/bizar-dash/src/web/v8/ui/theme/ThemeToggle.tsx +11 -6
- package/bizar-dash/src/web/v8/views/Admin/AdminView.tsx +32 -5
- package/bizar-dash/src/web/v8/views/Agents/AgentsView.tsx +36 -4
- package/bizar-dash/src/web/v8/views/Artifacts/ArtifactsView.tsx +1 -1
- package/bizar-dash/src/web/v8/views/Chat/ChatView.tsx +13 -1
- package/bizar-dash/src/web/v8/views/ClaudeSessions/NewSessionModal.tsx +136 -0
- package/bizar-dash/src/web/v8/views/Clipboard/ClipboardView.tsx +1 -1
- package/bizar-dash/src/web/v8/views/CommandPalette/AppCommandPalette.tsx +14 -4
- package/bizar-dash/src/web/v8/views/Diagnostics/DiagnosticsView.tsx +50 -7
- package/bizar-dash/src/web/v8/views/Dialogs/DialogsView.tsx +1 -1
- package/bizar-dash/src/web/v8/views/Libraries/LibrariesView.tsx +6 -1
- package/bizar-dash/src/web/v8/views/Misc/MiscView.tsx +1 -1
- package/bizar-dash/src/web/v8/views/Notifications/NotificationsView.tsx +82 -13
- package/bizar-dash/src/web/v8/views/Overview/OverviewView.tsx +19 -0
- package/bizar-dash/src/web/v8/views/Projects/ProjectsView.tsx +21 -1
- package/bizar-dash/src/web/v8/views/Voice/VoiceView.tsx +1 -1
- package/bizar-dash/tests/admin-labels.test.mjs +42 -0
- package/bizar-dash/tests/admin-run-grouping.test.mjs +42 -0
- package/bizar-dash/tests/agents-preview-truncate.test.mjs +35 -0
- package/bizar-dash/tests/agents-summary-sanitize.test.ts +51 -0
- package/bizar-dash/tests/chat-new-session.test.mjs +63 -0
- package/bizar-dash/tests/empty-state-docs.test.mjs +55 -0
- package/bizar-dash/tests/focus-visible.test.mjs +30 -0
- package/bizar-dash/tests/kanban-empty-column.test.mjs +56 -0
- package/bizar-dash/tests/library-dedupe.test.mjs +62 -0
- package/bizar-dash/tests/notifications-dismiss-restore.test.mjs +85 -0
- package/bizar-dash/tests/palette-shortcut-hint.test.mjs +58 -0
- package/bizar-dash/tests/projects-filter.test.mjs +48 -0
- package/bizar-dash/tests/settings-labels.test.mjs +80 -0
- package/bizar-dash/tests/sidebar-distinct-icons.test.mjs +28 -0
- package/bizar-dash/tests/sidebar-responsive.test.tsx +48 -0
- package/bizar-dash/tests/sidebar-section-toggle.test.tsx +63 -0
- package/bizar-dash/tests/theme-toggle-icon.test.mjs +28 -0
- package/bizar-dash/tests/topbar-design-system.test.mjs +56 -0
- package/bizar-dash/vite.config.ts +8 -0
- package/package.json +1 -1
- package/packages/sdk/package.json +1 -1
- package/bizar-dash/dist/assets/AdminView-DNc5Ll7L.js +0 -11
- package/bizar-dash/dist/assets/AdminView-DNc5Ll7L.js.map +0 -1
- package/bizar-dash/dist/assets/AgentsView-Du-G_skK.js +0 -13
- package/bizar-dash/dist/assets/AgentsView-Du-G_skK.js.map +0 -1
- package/bizar-dash/dist/assets/ArtifactsView-znhN8GTR.js.map +0 -1
- package/bizar-dash/dist/assets/Avatar-B41upSD1.js +0 -6
- package/bizar-dash/dist/assets/Avatar-B41upSD1.js.map +0 -1
- package/bizar-dash/dist/assets/ChatView-CUriVEgU.js +0 -9
- package/bizar-dash/dist/assets/ChatView-CUriVEgU.js.map +0 -1
- package/bizar-dash/dist/assets/ClipboardView-DdP6aVso.js +0 -1
- package/bizar-dash/dist/assets/ClipboardView-DdP6aVso.js.map +0 -1
- package/bizar-dash/dist/assets/DiagnosticsView-88OZ5k0f.js +0 -2
- package/bizar-dash/dist/assets/DiagnosticsView-88OZ5k0f.js.map +0 -1
- package/bizar-dash/dist/assets/DialogsView-BcqDDCZQ.js +0 -6
- package/bizar-dash/dist/assets/DialogsView-BcqDDCZQ.js.map +0 -1
- package/bizar-dash/dist/assets/EvalView-CpdFu_ID.js +0 -1
- package/bizar-dash/dist/assets/LibrariesView-B1YX9wzp.js +0 -6
- package/bizar-dash/dist/assets/MiscView-DLNEGdML.js +0 -1
- package/bizar-dash/dist/assets/MiscView-DLNEGdML.js.map +0 -1
- package/bizar-dash/dist/assets/NotificationsView-DAm2O0yf.js +0 -6
- package/bizar-dash/dist/assets/NotificationsView-DAm2O0yf.js.map +0 -1
- package/bizar-dash/dist/assets/ObsidianView-Ci80gnwS.js +0 -1
- package/bizar-dash/dist/assets/OverviewView-rriw1GjA.js +0 -1
- package/bizar-dash/dist/assets/OverviewView-rriw1GjA.js.map +0 -1
- package/bizar-dash/dist/assets/ProjectsView-ChzM12bs.js +0 -11
- package/bizar-dash/dist/assets/ProjectsView-ChzM12bs.js.map +0 -1
- package/bizar-dash/dist/assets/UpdateView-CEHW2nd6.js +0 -6
- package/bizar-dash/dist/assets/UpdateView-CEHW2nd6.js.map +0 -1
- package/bizar-dash/dist/assets/VoiceView-CwrPwWgi.js +0 -1
- package/bizar-dash/dist/assets/VoiceView-CwrPwWgi.js.map +0 -1
- package/bizar-dash/dist/assets/main-Cxeu4LhG.js +0 -244
- package/bizar-dash/dist/assets/main-Cxeu4LhG.js.map +0 -1
|
@@ -21,6 +21,31 @@ import type { BizarAgent, CCAgent, WsMessage } from '../../data/types.js';
|
|
|
21
21
|
import { AlertTriangle, Bot, Cpu, Pause, Play, Plus, RotateCw } from 'lucide-react';
|
|
22
22
|
import { AgentHierarchy } from './AgentHierarchy.js';
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Strip leaked LLM interior monologue from agent card snippets.
|
|
26
|
+
* Clips to the first sentence, caps at 140 chars.
|
|
27
|
+
*/
|
|
28
|
+
export function sanitizeSummary(raw: string | undefined): string {
|
|
29
|
+
if (!raw) return '';
|
|
30
|
+
let s = raw.replace(/<thinking>[\s\S]*?<\/thinking>/gi, '');
|
|
31
|
+
s = s.split('\n').filter((l) => !l.includes('```')).join('\n');
|
|
32
|
+
s = s.replace(/\bworktree:\s*/g, '');
|
|
33
|
+
s = s.replace(/\/tmp\//g, '');
|
|
34
|
+
s = s.replace(/\bReason:\s*/g, '');
|
|
35
|
+
s = s.replace(/^\s*→\s*/gm, '');
|
|
36
|
+
s = s.trim();
|
|
37
|
+
if (!s) return '';
|
|
38
|
+
// Strip leading bracketed prefix — [Premise — …] or [Analysis] etc.
|
|
39
|
+
s = s.replace(/^\[[\w\s]+[-—]\s*/, '');
|
|
40
|
+
s = s.replace(/^\[[\w\s]+\]\s*/gi, '');
|
|
41
|
+
s = s.trim();
|
|
42
|
+
if (!s) return '';
|
|
43
|
+
const m = s.match(/^[^.!?\n]+[.!?]/);
|
|
44
|
+
const first = m ? m[0] : s;
|
|
45
|
+
if (first.length <= 140) return first;
|
|
46
|
+
return first.slice(0, 137) + '…';
|
|
47
|
+
}
|
|
48
|
+
|
|
24
49
|
const SOURCE_STORAGE_KEY = 'bizar.agents.sourceFilter';
|
|
25
50
|
const SOURCE_LABELS = {
|
|
26
51
|
all: 'All',
|
|
@@ -83,24 +108,31 @@ function mapBizar(a: BizarAgent): AgentCardProps {
|
|
|
83
108
|
};
|
|
84
109
|
}
|
|
85
110
|
|
|
111
|
+
function truncatePreview(s: string, max = 80): string {
|
|
112
|
+
if (!s) return '';
|
|
113
|
+
return s.length <= max ? s : s.slice(0, max - 1) + '…';
|
|
114
|
+
}
|
|
115
|
+
|
|
86
116
|
function mapCC(a: CCAgent): AgentCardProps {
|
|
87
117
|
const raw = (a.status || a.state || 'idle').toLowerCase();
|
|
88
118
|
const status: AgentStatus =
|
|
89
119
|
raw === 'busy' || raw === 'working' ? 'busy' :
|
|
90
120
|
raw === 'error' ? 'error' :
|
|
91
121
|
raw === 'paused' ? 'paused' : 'idle';
|
|
122
|
+
const kind = a.kind === 'background' ? 'background' : 'interactive';
|
|
92
123
|
return {
|
|
93
124
|
id: `cc:${a.sessionId || a.id}`,
|
|
94
125
|
name: a.name || a.sessionId?.slice(0, 8) || 'unknown',
|
|
95
|
-
role:
|
|
126
|
+
role: kind === 'background' ? 'CC background' : 'CC interactive',
|
|
96
127
|
status,
|
|
97
|
-
currentTask: a.lastMessageSnippet
|
|
128
|
+
currentTask: truncatePreview(sanitizeSummary(a.lastMessageSnippet)),
|
|
98
129
|
lastActivity: a.lastMessageAt ? new Date(a.lastMessageAt).toLocaleTimeString() : (a.startedAt ? new Date(a.startedAt).toLocaleTimeString() : undefined),
|
|
99
130
|
tasksToday: a.messageCount || 0,
|
|
100
131
|
// CC agents don't expose successRate/tasksSucceeded — only messageCount.
|
|
101
132
|
// Skip the metric strip for them; the badge + lastActivity carry the
|
|
102
|
-
// signal.
|
|
103
|
-
|
|
133
|
+
// signal. Include a kind chip so users can distinguish CC background
|
|
134
|
+
// sessions from interactive ones.
|
|
135
|
+
badges: [kind, a.cwd?.split('/').slice(-2).join('/') || ''].filter(Boolean),
|
|
104
136
|
};
|
|
105
137
|
}
|
|
106
138
|
|
|
@@ -94,7 +94,7 @@ export function ArtifactsView(): JSX.Element {
|
|
|
94
94
|
<Skeleton style={{ height: 48 }} />
|
|
95
95
|
</Stack>
|
|
96
96
|
) : artifacts.length === 0 ? (
|
|
97
|
-
<EmptyState icon={<Layers size={28} aria-hidden />} title="No artifacts" description="
|
|
97
|
+
<EmptyState icon={<Layers size={28} aria-hidden />} title="No artifacts" description="Artifacts are generated code — HTML canvases, SVG diagrams, and React components — created by the agent during task work. Add one to get started." />
|
|
98
98
|
) : (
|
|
99
99
|
<Stack gap={1}>
|
|
100
100
|
{artifacts.map((a) => {
|
|
@@ -26,6 +26,7 @@ import { fetchJson, FetchError } from '../../data/fetcher.js';
|
|
|
26
26
|
import { MessageBubble, EmptyTranscript } from '../../ui/chat/MessageBubble.js';
|
|
27
27
|
import { readEventStream } from '../../ui/chat/EventStream.js';
|
|
28
28
|
import type { ChatMessage, ChatSession, WsMessage } from '../../data/types.js';
|
|
29
|
+
import { NewSessionModal } from '../ClaudeSessions/NewSessionModal.js';
|
|
29
30
|
|
|
30
31
|
interface ChatPayload {
|
|
31
32
|
messages: ChatMessage[];
|
|
@@ -55,6 +56,7 @@ export function ChatView(): JSX.Element {
|
|
|
55
56
|
const [busy, setBusy] = useState<boolean>(false);
|
|
56
57
|
const [error, setError] = useState<string | null>(null);
|
|
57
58
|
const [confirmDeleteId, setConfirmDeleteId] = useState<string | null>(null);
|
|
59
|
+
const [newSessionOpen, setNewSessionOpen] = useState<boolean>(false);
|
|
58
60
|
|
|
59
61
|
// Seed active session from the first row in the response.
|
|
60
62
|
useEffect(() => {
|
|
@@ -198,7 +200,7 @@ export function ChatView(): JSX.Element {
|
|
|
198
200
|
<Button variant="ghost" onClick={() => void audit()} data-testid="chat-audit">
|
|
199
201
|
<ShieldCheck size={14} aria-hidden /> Audit
|
|
200
202
|
</Button>
|
|
201
|
-
<Button variant="primary" onClick={() =>
|
|
203
|
+
<Button variant="primary" onClick={() => setNewSessionOpen(true)} data-testid="chat-create-session">
|
|
202
204
|
<MessageSquarePlus size={14} aria-hidden /> New session
|
|
203
205
|
</Button>
|
|
204
206
|
</Inline>
|
|
@@ -351,6 +353,16 @@ export function ChatView(): JSX.Element {
|
|
|
351
353
|
</CardBody>
|
|
352
354
|
</Card>
|
|
353
355
|
</Inline>
|
|
356
|
+
|
|
357
|
+
<NewSessionModal
|
|
358
|
+
open={newSessionOpen}
|
|
359
|
+
onOpenChange={setNewSessionOpen}
|
|
360
|
+
onCreated={(id) => {
|
|
361
|
+
setActiveId(id);
|
|
362
|
+
void payload.refetch();
|
|
363
|
+
void sessionsPayload.refetch();
|
|
364
|
+
}}
|
|
365
|
+
/>
|
|
354
366
|
</Stack>
|
|
355
367
|
);
|
|
356
368
|
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NewSessionModal — shared new-session Sheet, extracted from
|
|
3
|
+
* ClaudeSessionsView so ChatView can reuse the same form.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useState } from 'react';
|
|
7
|
+
import { Stack } from '../../ui/primitives/Stack.js';
|
|
8
|
+
import { Inline } from '../../ui/primitives/Inline.js';
|
|
9
|
+
import { Button } from '../../ui/controls/Button.js';
|
|
10
|
+
import { Input } from '../../ui/controls/Input.js';
|
|
11
|
+
import { Sheet, SheetClose, SheetContent } from '../../ui/feedback/Sheet.js';
|
|
12
|
+
import { fetchJson, FetchError } from '../../data/fetcher.js';
|
|
13
|
+
|
|
14
|
+
interface NewSessionModalProps {
|
|
15
|
+
open: boolean;
|
|
16
|
+
onOpenChange: (open: boolean) => void;
|
|
17
|
+
onCreated: (id: string) => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function NewSessionModal({ open, onOpenChange, onCreated }: NewSessionModalProps): JSX.Element {
|
|
21
|
+
return (
|
|
22
|
+
<Sheet open={open} onOpenChange={onOpenChange}>
|
|
23
|
+
<SheetContent side="right" title="New Claude session" description="Spawn a Claude Code session via the runner.">
|
|
24
|
+
<NewSessionForm
|
|
25
|
+
onCreated={(id) => {
|
|
26
|
+
onOpenChange(false);
|
|
27
|
+
onCreated(id);
|
|
28
|
+
}}
|
|
29
|
+
onCancel={() => onOpenChange(false)}
|
|
30
|
+
/>
|
|
31
|
+
</SheetContent>
|
|
32
|
+
</Sheet>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface NewSessionFormProps {
|
|
37
|
+
onCreated: (id: string) => void;
|
|
38
|
+
onCancel: () => void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function NewSessionForm({ onCreated, onCancel }: NewSessionFormProps): JSX.Element {
|
|
42
|
+
const [title, setTitle] = useState<string>('');
|
|
43
|
+
const [agent, setAgent] = useState<string>('general');
|
|
44
|
+
const [prompt, setPrompt] = useState<string>('');
|
|
45
|
+
const [directory, setDirectory] = useState<string>('');
|
|
46
|
+
const [busy, setBusy] = useState<boolean>(false);
|
|
47
|
+
const [error, setError] = useState<string | null>(null);
|
|
48
|
+
|
|
49
|
+
const submit = async (): Promise<void> => {
|
|
50
|
+
if (!agent.trim() || !prompt.trim() || busy) return;
|
|
51
|
+
setBusy(true);
|
|
52
|
+
setError(null);
|
|
53
|
+
try {
|
|
54
|
+
const res = await fetchJson<{ id: string }>('/api/claude-sessions/new', {
|
|
55
|
+
method: 'POST',
|
|
56
|
+
body: {
|
|
57
|
+
title: title.trim() || null,
|
|
58
|
+
agent: agent.trim(),
|
|
59
|
+
prompt: prompt.trim(),
|
|
60
|
+
directory: directory.trim() || undefined,
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
onCreated(res.id);
|
|
64
|
+
} catch (err) {
|
|
65
|
+
setError(err instanceof FetchError ? err.message : (err as Error).message);
|
|
66
|
+
} finally {
|
|
67
|
+
setBusy(false);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<Stack gap={3} style={{ padding: 'var(--space-4)' }}>
|
|
73
|
+
<label style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-1)' }}>
|
|
74
|
+
<span style={{ fontSize: 'var(--fs-12)', color: 'var(--fg-muted)' }}>Title (optional)</span>
|
|
75
|
+
<Input
|
|
76
|
+
value={title}
|
|
77
|
+
data-testid="claude-sessions-new-title"
|
|
78
|
+
onChange={(e) => setTitle((e.target as HTMLInputElement).value)}
|
|
79
|
+
/>
|
|
80
|
+
</label>
|
|
81
|
+
<label style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-1)' }}>
|
|
82
|
+
<span style={{ fontSize: 'var(--fs-12)', color: 'var(--fg-muted)' }}>Agent</span>
|
|
83
|
+
<Input
|
|
84
|
+
value={agent}
|
|
85
|
+
data-testid="claude-sessions-new-agent"
|
|
86
|
+
onChange={(e) => setAgent((e.target as HTMLInputElement).value)}
|
|
87
|
+
/>
|
|
88
|
+
</label>
|
|
89
|
+
<label style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-1)' }}>
|
|
90
|
+
<span style={{ fontSize: 'var(--fs-12)', color: 'var(--fg-muted)' }}>Seed prompt</span>
|
|
91
|
+
<textarea
|
|
92
|
+
value={prompt}
|
|
93
|
+
rows={4}
|
|
94
|
+
data-testid="claude-sessions-new-prompt"
|
|
95
|
+
onChange={(e) => setPrompt(e.target.value)}
|
|
96
|
+
style={{
|
|
97
|
+
background: 'var(--surface-0)',
|
|
98
|
+
border: '1px solid var(--border)',
|
|
99
|
+
borderRadius: 'var(--radius-md)',
|
|
100
|
+
padding: 'var(--space-2)',
|
|
101
|
+
color: 'var(--fg)',
|
|
102
|
+
fontFamily: 'var(--font-sans)',
|
|
103
|
+
fontSize: 'var(--fs-13)',
|
|
104
|
+
resize: 'vertical',
|
|
105
|
+
}}
|
|
106
|
+
/>
|
|
107
|
+
</label>
|
|
108
|
+
<label style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-1)' }}>
|
|
109
|
+
<span style={{ fontSize: 'var(--fs-12)', color: 'var(--fg-muted)' }}>Working directory (optional)</span>
|
|
110
|
+
<Input
|
|
111
|
+
value={directory}
|
|
112
|
+
data-testid="claude-sessions-new-directory"
|
|
113
|
+
onChange={(e) => setDirectory((e.target as HTMLInputElement).value)}
|
|
114
|
+
/>
|
|
115
|
+
</label>
|
|
116
|
+
{error !== null && (
|
|
117
|
+
<span role="alert" data-testid="claude-sessions-new-error" style={{ color: 'var(--danger)', fontSize: 'var(--fs-12)' }}>
|
|
118
|
+
{error}
|
|
119
|
+
</span>
|
|
120
|
+
)}
|
|
121
|
+
<Inline justify="end" gap={2}>
|
|
122
|
+
<SheetClose asChild>
|
|
123
|
+
<Button variant="ghost" onClick={onCancel} data-testid="claude-sessions-new-cancel">Cancel</Button>
|
|
124
|
+
</SheetClose>
|
|
125
|
+
<Button
|
|
126
|
+
variant="primary"
|
|
127
|
+
onClick={() => void submit()}
|
|
128
|
+
disabled={busy || !agent.trim() || !prompt.trim()}
|
|
129
|
+
data-testid="claude-sessions-new-submit"
|
|
130
|
+
>
|
|
131
|
+
Create
|
|
132
|
+
</Button>
|
|
133
|
+
</Inline>
|
|
134
|
+
</Stack>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
@@ -81,7 +81,7 @@ export function ClipboardView(): JSX.Element {
|
|
|
81
81
|
<Skeleton style={{ height: 60 }} />
|
|
82
82
|
</Stack>
|
|
83
83
|
) : clips.length === 0 ? (
|
|
84
|
-
<EmptyState icon={<ClipboardPaste size={28} aria-hidden />} title="No saved clips" description="
|
|
84
|
+
<EmptyState icon={<ClipboardPaste size={28} aria-hidden />} title="No saved clips" description="Clips are saved URL + text pairs captured from the clipboard during agent work. Each becomes a markdown note under clips/. Save one to get started." />
|
|
85
85
|
) : (
|
|
86
86
|
<Stack gap={1}>
|
|
87
87
|
{clips.map((c) => {
|
|
@@ -110,6 +110,14 @@ interface Project {
|
|
|
110
110
|
cwd?: string;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
/** Mirror of ProjectsView.isTestProject — keeps test projects out of the palette. */
|
|
114
|
+
function isTestProject(p: Project): boolean {
|
|
115
|
+
return (
|
|
116
|
+
/^(bizar-(e2e|real-env|admin)|bh-(cold-boot|walk-proj))/.test(p.id) ||
|
|
117
|
+
(p.name != null && p.name === p.id)
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
113
121
|
async function spawnAgent(
|
|
114
122
|
kind: string,
|
|
115
123
|
onToast?: AppCommandPaletteProps['onToast'],
|
|
@@ -197,10 +205,12 @@ export function AppCommandPalette(props: AppCommandPaletteProps): JSX.Element {
|
|
|
197
205
|
void newTask(title, onToast);
|
|
198
206
|
};
|
|
199
207
|
|
|
200
|
-
const projectItems: NavItem[] = projects
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
208
|
+
const projectItems: NavItem[] = projects
|
|
209
|
+
.filter((p) => !isTestProject(p))
|
|
210
|
+
.map((p) => ({
|
|
211
|
+
id: `project:${p.id}`,
|
|
212
|
+
label: `Switch to ${p.name || p.id}`,
|
|
213
|
+
}));
|
|
204
214
|
|
|
205
215
|
return (
|
|
206
216
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
@@ -20,11 +20,13 @@ interface DiagnosticsHealth {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
interface DiagnosticsSnapshot {
|
|
23
|
-
health?: DiagnosticsHealth;
|
|
24
|
-
uptime?: number;
|
|
25
23
|
version?: string;
|
|
26
|
-
|
|
24
|
+
uptime?: number;
|
|
25
|
+
memory?: { rss?: number; heapUsed?: number; heapTotal?: number };
|
|
27
26
|
checks?: Array<{ name: string; status: string; message?: string }>;
|
|
27
|
+
counts?: Record<string, number | string | null>;
|
|
28
|
+
errors?: Array<{ line: string; ts: string | null }>;
|
|
29
|
+
service?: { running?: boolean; pid?: number };
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
interface LogsResponse {
|
|
@@ -53,9 +55,9 @@ export function DiagnosticsView(): JSX.Element {
|
|
|
53
55
|
return () => clearInterval(id);
|
|
54
56
|
}, [logs]);
|
|
55
57
|
|
|
56
|
-
const health = snap.data?.health;
|
|
57
58
|
const uptime = snap.data?.uptime;
|
|
58
59
|
const checks = snap.data?.checks ?? [];
|
|
60
|
+
const counts = snap.data?.counts ?? {};
|
|
59
61
|
const lines = logs.data?.lines ?? [];
|
|
60
62
|
const logFile = logs.data?.file ?? null;
|
|
61
63
|
|
|
@@ -78,7 +80,13 @@ export function DiagnosticsView(): JSX.Element {
|
|
|
78
80
|
<span style={{ fontSize: 'var(--fs-12)', color: 'var(--fg-muted)' }}>Status</span>
|
|
79
81
|
<Inline align="center" gap={2}>
|
|
80
82
|
<Activity size={14} aria-hidden />
|
|
81
|
-
<strong style={{ fontFamily: 'var(--font-mono)' }}>
|
|
83
|
+
<strong style={{ fontFamily: 'var(--font-mono)' }}>
|
|
84
|
+
{snap.loading ? '…' : (
|
|
85
|
+
snap.error ? 'error' :
|
|
86
|
+
!snap.data?.service?.running ? 'stopped' :
|
|
87
|
+
(snap.data?.errors?.length ?? 0) > 0 ? 'warn' : 'ok'
|
|
88
|
+
)}
|
|
89
|
+
</strong>
|
|
82
90
|
</Inline>
|
|
83
91
|
</Stack>
|
|
84
92
|
</CardBody>
|
|
@@ -87,7 +95,7 @@ export function DiagnosticsView(): JSX.Element {
|
|
|
87
95
|
<CardBody>
|
|
88
96
|
<Stack gap={1}>
|
|
89
97
|
<span style={{ fontSize: 'var(--fs-12)', color: 'var(--fg-muted)' }}>Uptime</span>
|
|
90
|
-
<strong style={{ fontFamily: 'var(--font-mono)' }}>{fmtUptime(uptime)}</strong>
|
|
98
|
+
<strong style={{ fontFamily: 'var(--font-mono)' }}>{snap.loading ? '…' : fmtUptime(uptime)}</strong>
|
|
91
99
|
</Stack>
|
|
92
100
|
</CardBody>
|
|
93
101
|
</Card>
|
|
@@ -95,12 +103,47 @@ export function DiagnosticsView(): JSX.Element {
|
|
|
95
103
|
<CardBody>
|
|
96
104
|
<Stack gap={1}>
|
|
97
105
|
<span style={{ fontSize: 'var(--fs-12)', color: 'var(--fg-muted)' }}>Version</span>
|
|
98
|
-
<strong style={{ fontFamily: 'var(--font-mono)' }}>{snap.data?.version ?? '—'}</strong>
|
|
106
|
+
<strong style={{ fontFamily: 'var(--font-mono)' }}>{snap.loading ? '…' : (snap.data?.version ?? '—')}</strong>
|
|
99
107
|
</Stack>
|
|
100
108
|
</CardBody>
|
|
101
109
|
</Card>
|
|
102
110
|
</Grid>
|
|
103
111
|
|
|
112
|
+
{snap.error && (
|
|
113
|
+
<Card variant="default" style={{ borderColor: 'var(--danger)' }}>
|
|
114
|
+
<CardBody>
|
|
115
|
+
<Stack gap={2}>
|
|
116
|
+
<Inline align="center" gap={2}>
|
|
117
|
+
<Activity size={14} aria-hidden style={{ color: 'var(--danger)' }} />
|
|
118
|
+
<strong style={{ color: 'var(--danger)' }}>Diagnostics fetch failed</strong>
|
|
119
|
+
</Inline>
|
|
120
|
+
<span style={{ fontSize: 'var(--fs-12)', color: 'var(--fg-muted)' }}>{snap.error.message ?? String(snap.error)}</span>
|
|
121
|
+
<Button variant="secondary" size="sm" onClick={() => { void snap.refetch(); }}>
|
|
122
|
+
<RefreshCw size={12} aria-hidden /> Retry
|
|
123
|
+
</Button>
|
|
124
|
+
</Stack>
|
|
125
|
+
</CardBody>
|
|
126
|
+
</Card>
|
|
127
|
+
)}
|
|
128
|
+
|
|
129
|
+
{Object.keys(counts).length > 0 && (
|
|
130
|
+
<Card variant="default">
|
|
131
|
+
<CardBody>
|
|
132
|
+
<Stack gap={2}>
|
|
133
|
+
<strong>Counts</strong>
|
|
134
|
+
<Grid cols={4}>
|
|
135
|
+
{Object.entries(counts).map(([k, v]) => (
|
|
136
|
+
<Stack key={k} gap={0}>
|
|
137
|
+
<span style={{ fontSize: 'var(--fs-11)', color: 'var(--fg-muted)', textTransform: 'uppercase', letterSpacing: '0.04em' }}>{k}</span>
|
|
138
|
+
<strong style={{ fontFamily: 'var(--font-mono)', fontSize: 'var(--fs-13)' }}>{String(v ?? '—')}</strong>
|
|
139
|
+
</Stack>
|
|
140
|
+
))}
|
|
141
|
+
</Grid>
|
|
142
|
+
</Stack>
|
|
143
|
+
</CardBody>
|
|
144
|
+
</Card>
|
|
145
|
+
)}
|
|
146
|
+
|
|
104
147
|
{checks.length > 0 && (
|
|
105
148
|
<Card variant="default">
|
|
106
149
|
<CardBody>
|
|
@@ -103,7 +103,7 @@ export function DialogsView(): JSX.Element {
|
|
|
103
103
|
<EmptyState
|
|
104
104
|
icon={<MessageSquare size={28} aria-hidden />}
|
|
105
105
|
title="No active dialogs"
|
|
106
|
-
description="Dialogs
|
|
106
|
+
description="Dialogs are confirmations and prompts the agent raises when it needs user input before proceeding. They appear here when the slash-command plugin queues one."
|
|
107
107
|
/>
|
|
108
108
|
) : (
|
|
109
109
|
<Stack gap={1}>
|
|
@@ -84,7 +84,12 @@ export function LibrariesView(props: LibrariesViewProps): JSX.Element {
|
|
|
84
84
|
if (bust > 0) res.refetch();
|
|
85
85
|
}, [bust, res]);
|
|
86
86
|
|
|
87
|
-
const items: LibraryItemProps[] = (res.data?.skills || res.data?.items || [])
|
|
87
|
+
const items: LibraryItemProps[] = (res.data?.skills || res.data?.items || [])
|
|
88
|
+
.map(mapItem)
|
|
89
|
+
.reduce<LibraryItemProps[]>((acc, item) => {
|
|
90
|
+
if (!acc.some((existing) => existing.name === item.name)) acc.push(item);
|
|
91
|
+
return acc;
|
|
92
|
+
}, []);
|
|
88
93
|
|
|
89
94
|
return (
|
|
90
95
|
<Stack gap={5} data-testid={`${kind}-view`}>
|
|
@@ -45,7 +45,7 @@ interface TailscaleStatus {
|
|
|
45
45
|
export function MiscView(): JSX.Element {
|
|
46
46
|
return (
|
|
47
47
|
<Stack gap={4} data-testid="misc-view">
|
|
48
|
-
<ViewHeader title="Misc" description="Global search
|
|
48
|
+
<ViewHeader title="Misc" description="Global fuzzy search across projects, tasks, and agents — plus Tailscale VPN control." />
|
|
49
49
|
<SearchPanel />
|
|
50
50
|
<TailscalePanel />
|
|
51
51
|
</Stack>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from 'react';
|
|
2
|
-
import { Bell, CheckCheck, Trash2, RefreshCw } from 'lucide-react';
|
|
2
|
+
import { Bell, CheckCheck, Trash2, RefreshCw, RotateCcw } from 'lucide-react';
|
|
3
3
|
import { Stack } from '../../ui/primitives/Stack.js';
|
|
4
4
|
import { Inline } from '../../ui/primitives/Inline.js';
|
|
5
5
|
import { ViewHeader } from '../../ui/data/ViewHeader.js';
|
|
@@ -16,7 +16,8 @@ import type { WsMessage } from '../../data/types.js';
|
|
|
16
16
|
/**
|
|
17
17
|
* NotificationsView — Sprint S35. Per-user notification stream backed
|
|
18
18
|
* by `/api/notifications`. Pulls unread + read mixed, supports
|
|
19
|
-
* mark-one-read, mark-all-read, and
|
|
19
|
+
* mark-one-read, mark-all-read, dismiss with 5s undo, and
|
|
20
|
+
* "Show dismissed" with restore.
|
|
20
21
|
*/
|
|
21
22
|
|
|
22
23
|
type Tone = 'neutral' | 'info' | 'success' | 'warning' | 'danger';
|
|
@@ -36,6 +37,12 @@ interface NotificationsResponse {
|
|
|
36
37
|
stats?: { total: number; unread: number };
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
interface UndoToast {
|
|
41
|
+
id: string;
|
|
42
|
+
item: NotificationItem;
|
|
43
|
+
timer: ReturnType<typeof setTimeout>;
|
|
44
|
+
}
|
|
45
|
+
|
|
39
46
|
function toneFor(item: NotificationItem): Tone {
|
|
40
47
|
if (item.tone && ['info', 'success', 'warning', 'danger', 'neutral'].includes(item.tone)) {
|
|
41
48
|
return item.tone as Tone;
|
|
@@ -57,6 +64,9 @@ function fmtRel(iso: string | undefined): string {
|
|
|
57
64
|
export function NotificationsView(): JSX.Element {
|
|
58
65
|
const res = useFetch<NotificationsResponse>('/api/notifications?limit=200');
|
|
59
66
|
const [items, setItems] = useState<NotificationItem[]>([]);
|
|
67
|
+
const [dismissed, setDismissed] = useState<NotificationItem[]>([]);
|
|
68
|
+
const [showDismissed, setShowDismissed] = useState(false);
|
|
69
|
+
const [undoToast, setUndoToast] = useState<UndoToast | null>(null);
|
|
60
70
|
const [busy, setBusy] = useState<string | null>(null);
|
|
61
71
|
const [error, setError] = useState<string | null>(null);
|
|
62
72
|
|
|
@@ -95,12 +105,29 @@ export function NotificationsView(): JSX.Element {
|
|
|
95
105
|
}
|
|
96
106
|
};
|
|
97
107
|
|
|
108
|
+
const restore = (item: NotificationItem): void => {
|
|
109
|
+
clearTimeout(undoToast?.timer);
|
|
110
|
+
setUndoToast(null);
|
|
111
|
+
setDismissed((prev) => prev.filter((n) => n.id !== item.id));
|
|
112
|
+
setItems((prev) => [item, ...prev]);
|
|
113
|
+
};
|
|
114
|
+
|
|
98
115
|
const dismiss = async (id: string): Promise<void> => {
|
|
116
|
+
const item = items.find((n) => n.id === id);
|
|
117
|
+
if (!item) return;
|
|
99
118
|
setBusy(id);
|
|
100
119
|
setError(null);
|
|
101
120
|
try {
|
|
102
121
|
await fetchJson(`/api/notifications/${encodeURIComponent(id)}`, { method: 'DELETE' });
|
|
103
122
|
setItems((prev) => prev.filter((n) => n.id !== id));
|
|
123
|
+
setDismissed((prev) => [item, ...prev]);
|
|
124
|
+
|
|
125
|
+
// 5-second undo window
|
|
126
|
+
const timer = setTimeout(() => {
|
|
127
|
+
setDismissed((prev) => prev.filter((n) => n.id !== id));
|
|
128
|
+
setUndoToast((cur) => (cur?.id === id ? null : cur));
|
|
129
|
+
}, 5_000);
|
|
130
|
+
setUndoToast({ id, item, timer });
|
|
104
131
|
} catch (err) {
|
|
105
132
|
setError(err instanceof FetchError ? err.message : (err as Error).message);
|
|
106
133
|
} finally {
|
|
@@ -109,12 +136,13 @@ export function NotificationsView(): JSX.Element {
|
|
|
109
136
|
};
|
|
110
137
|
|
|
111
138
|
const unread = items.filter((n) => !n.read).length;
|
|
139
|
+
const visibleItems = showDismissed ? [...items, ...dismissed] : items;
|
|
112
140
|
|
|
113
141
|
return (
|
|
114
142
|
<Stack gap={5} data-testid="notifications-view">
|
|
115
143
|
<ViewHeader
|
|
116
144
|
title="Notifications"
|
|
117
|
-
description={`${unread} unread of ${items.length} total. Backed by ~/.config/bizar/notifications.jsonl.`}
|
|
145
|
+
description={`${unread} unread of ${items.length} total${dismissed.length > 0 ? `, ${dismissed.length} dismissed` : ''}. Backed by ~/.config/bizar/notifications.jsonl.`}
|
|
118
146
|
actions={
|
|
119
147
|
<Inline gap={2} align="center">
|
|
120
148
|
{error !== null && <span role="alert" style={{ color: 'var(--danger)', fontSize: 'var(--fs-12)' }}>{error}</span>}
|
|
@@ -124,6 +152,11 @@ export function NotificationsView(): JSX.Element {
|
|
|
124
152
|
<Button variant="secondary" onClick={() => void markAll()} disabled={busy === 'all' || unread === 0} data-testid="notifications-mark-all">
|
|
125
153
|
<CheckCheck size={14} aria-hidden /> Mark all read
|
|
126
154
|
</Button>
|
|
155
|
+
{dismissed.length > 0 && (
|
|
156
|
+
<Button variant="ghost" onClick={() => setShowDismissed((v) => !v)} data-testid="notifications-toggle-dismissed">
|
|
157
|
+
{showDismissed ? 'Hide dismissed' : `Show dismissed (${dismissed.length})`}
|
|
158
|
+
</Button>
|
|
159
|
+
)}
|
|
127
160
|
</Inline>
|
|
128
161
|
}
|
|
129
162
|
/>
|
|
@@ -133,7 +166,7 @@ export function NotificationsView(): JSX.Element {
|
|
|
133
166
|
<Skeleton style={{ height: 64 }} />
|
|
134
167
|
<Skeleton style={{ height: 64 }} />
|
|
135
168
|
</Stack>
|
|
136
|
-
) : items.length === 0 ? (
|
|
169
|
+
) : items.length === 0 && dismissed.length === 0 ? (
|
|
137
170
|
<EmptyState
|
|
138
171
|
icon={<Bell size={32} aria-hidden />}
|
|
139
172
|
title="No notifications"
|
|
@@ -141,15 +174,17 @@ export function NotificationsView(): JSX.Element {
|
|
|
141
174
|
/>
|
|
142
175
|
) : (
|
|
143
176
|
<Stack gap={2}>
|
|
144
|
-
{
|
|
177
|
+
{visibleItems.map((n) => {
|
|
178
|
+
const isDismissed = dismissed.some((d) => d.id === n.id);
|
|
145
179
|
const tone = toneFor(n);
|
|
146
180
|
return (
|
|
147
|
-
<Card key={n.id} variant="default" style={{ opacity: n.read ? 0.7 : 1 }}>
|
|
181
|
+
<Card key={n.id} variant="default" style={{ opacity: isDismissed || n.read ? 0.7 : 1 }}>
|
|
148
182
|
<CardBody>
|
|
149
183
|
<Inline align="start" justify="between" gap={3}>
|
|
150
184
|
<Stack gap={1} style={{ flex: 1, minWidth: 0 }}>
|
|
151
185
|
<Inline align="center" gap={2}>
|
|
152
|
-
{
|
|
186
|
+
{isDismissed && <span data-testid={`notification-dismissed-badge-${n.id}`} style={{ fontSize: 'var(--fs-11)', color: 'var(--fg-subtle)' }}>dismissed</span>}
|
|
187
|
+
{!n.read && !isDismissed && <span aria-label="Unread" style={{ width: 8, height: 8, borderRadius: 'var(--radius-pill)', background: 'var(--accent)' }} />}
|
|
153
188
|
<strong style={{ fontSize: 'var(--fs-13)' }}>{n.title || n.source || n.id}</strong>
|
|
154
189
|
{n.source && <Badge tone={tone}>{n.source}</Badge>}
|
|
155
190
|
<span style={{ color: 'var(--fg-muted)', fontSize: 'var(--fs-12)' }}>{fmtRel(n.createdAt)}</span>
|
|
@@ -157,14 +192,22 @@ export function NotificationsView(): JSX.Element {
|
|
|
157
192
|
{n.body && <span style={{ fontSize: 'var(--fs-13)', color: 'var(--fg-muted)' }}>{n.body}</span>}
|
|
158
193
|
</Stack>
|
|
159
194
|
<Inline gap={1}>
|
|
160
|
-
{
|
|
161
|
-
<Button variant="ghost" onClick={() =>
|
|
162
|
-
|
|
195
|
+
{isDismissed ? (
|
|
196
|
+
<Button variant="ghost" onClick={() => restore(n)} data-testid={`notification-restore-${n.id}`}>
|
|
197
|
+
<RotateCcw size={12} aria-hidden /> Restore
|
|
163
198
|
</Button>
|
|
199
|
+
) : (
|
|
200
|
+
<>
|
|
201
|
+
{!n.read && (
|
|
202
|
+
<Button variant="ghost" onClick={() => void markRead(n.id)} disabled={busy === n.id} data-testid={`notification-read-${n.id}`}>
|
|
203
|
+
Mark read
|
|
204
|
+
</Button>
|
|
205
|
+
)}
|
|
206
|
+
<Button variant="ghost" onClick={() => void dismiss(n.id)} disabled={busy === n.id} data-testid={`notification-dismiss-${n.id}`}>
|
|
207
|
+
<Trash2 size={12} aria-hidden /> Dismiss
|
|
208
|
+
</Button>
|
|
209
|
+
</>
|
|
164
210
|
)}
|
|
165
|
-
<Button variant="ghost" onClick={() => void dismiss(n.id)} disabled={busy === n.id} data-testid={`notification-dismiss-${n.id}`}>
|
|
166
|
-
<Trash2 size={12} aria-hidden /> Dismiss
|
|
167
|
-
</Button>
|
|
168
211
|
</Inline>
|
|
169
212
|
</Inline>
|
|
170
213
|
</CardBody>
|
|
@@ -173,6 +216,32 @@ export function NotificationsView(): JSX.Element {
|
|
|
173
216
|
})}
|
|
174
217
|
</Stack>
|
|
175
218
|
)}
|
|
219
|
+
{undoToast !== null && (
|
|
220
|
+
<Card variant="default" data-testid="notifications-undo-toast" style={{ border: '1px solid var(--accent)', background: 'var(--surface-1)' }}>
|
|
221
|
+
<CardBody>
|
|
222
|
+
<Inline align="center" justify="between" gap={3}>
|
|
223
|
+
<span style={{ fontSize: 'var(--fs-13)' }}>
|
|
224
|
+
Notification dismissed.{' '}
|
|
225
|
+
<button
|
|
226
|
+
type="button"
|
|
227
|
+
onClick={() => {
|
|
228
|
+
clearTimeout(undoToast.timer);
|
|
229
|
+
setUndoToast(null);
|
|
230
|
+
restore(undoToast.item);
|
|
231
|
+
}}
|
|
232
|
+
style={{ background: 'none', border: 'none', color: 'var(--accent)', cursor: 'pointer', fontSize: 'inherit', padding: 0, textDecoration: 'underline' }}
|
|
233
|
+
data-testid="notifications-undo-btn"
|
|
234
|
+
>
|
|
235
|
+
Undo
|
|
236
|
+
</button>
|
|
237
|
+
</span>
|
|
238
|
+
<Button variant="ghost" size="sm" onClick={() => { clearTimeout(undoToast.timer); setUndoToast(null); }} data-testid="notifications-undo-close">
|
|
239
|
+
✕
|
|
240
|
+
</Button>
|
|
241
|
+
</Inline>
|
|
242
|
+
</CardBody>
|
|
243
|
+
</Card>
|
|
244
|
+
)}
|
|
176
245
|
</Stack>
|
|
177
246
|
);
|
|
178
247
|
}
|