@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
|
@@ -13,6 +13,8 @@ import { Stack } from '../../ui/primitives/Stack.js';
|
|
|
13
13
|
import { StatTile, StatGrid } from '../../ui/data/StatTile.js';
|
|
14
14
|
import { Card, CardBody, CardHeader } from '../../ui/data/Card.js';
|
|
15
15
|
import { Badge } from '../../ui/data/Badge.js';
|
|
16
|
+
import { Banner } from '../../ui/feedback/Banner.js';
|
|
17
|
+
import { Button } from '../../ui/controls/Button.js';
|
|
16
18
|
import { ActivityFeed, type ActivityItem } from '../../ui/activity/ActivityFeed.js';
|
|
17
19
|
import { ViewHeader } from '../../ui/data/ViewHeader.js';
|
|
18
20
|
import { Sparkline } from '../../ui/data/Sparkline.js';
|
|
@@ -103,6 +105,19 @@ export function OverviewView(): JSX.Element {
|
|
|
103
105
|
|
|
104
106
|
return (
|
|
105
107
|
<Stack gap={5} data-testid="overview-view">
|
|
108
|
+
{snapshot.error && (
|
|
109
|
+
<Banner
|
|
110
|
+
tone="warning"
|
|
111
|
+
data-testid="overview-error-banner"
|
|
112
|
+
action={
|
|
113
|
+
<Button variant="ghost" size="sm" onClick={() => void snapshot.refetch()}>
|
|
114
|
+
Retry
|
|
115
|
+
</Button>
|
|
116
|
+
}
|
|
117
|
+
>
|
|
118
|
+
Snapshot unavailable: {snapshot.error.message || String(snapshot.error)}
|
|
119
|
+
</Banner>
|
|
120
|
+
)}
|
|
106
121
|
<ViewHeader
|
|
107
122
|
title="Overview"
|
|
108
123
|
description="What's moving in the harness right now."
|
|
@@ -119,24 +134,28 @@ export function OverviewView(): JSX.Element {
|
|
|
119
134
|
value={String((tasks.queued || 0) + (tasks.active || 0))}
|
|
120
135
|
trend={(tasks.active || 0) > (tasks.queued || 0) ? 'up' : 'flat'}
|
|
121
136
|
delta={`${tasks.done || 0} done · ${tasks.blocked || 0} blocked`}
|
|
137
|
+
loading={snapshot.loading}
|
|
122
138
|
/>
|
|
123
139
|
<StatTile
|
|
124
140
|
label="Goals at risk"
|
|
125
141
|
value={`${goals.atRisk || 0} / ${goals.total || 0}`}
|
|
126
142
|
trend={(goals.atRisk || 0) > 0 ? 'down' : 'flat'}
|
|
127
143
|
delta={`${goals.done || 0} done`}
|
|
144
|
+
loading={snapshot.loading}
|
|
128
145
|
/>
|
|
129
146
|
<StatTile
|
|
130
147
|
label="Agents running"
|
|
131
148
|
value={`${agents.running || 0} / ${agents.total || 0}`}
|
|
132
149
|
trend={(agents.running || 0) > 0 ? 'up' : 'flat'}
|
|
133
150
|
delta={`${agents.idle || 0} idle · ${agents.error || 0} error`}
|
|
151
|
+
loading={snapshot.loading}
|
|
134
152
|
/>
|
|
135
153
|
<StatTile
|
|
136
154
|
label="Tokens (24h)"
|
|
137
155
|
value={tokens.last24h ? `${(tokens.last24h / 1_000_000).toFixed(1)}M` : '—'}
|
|
138
156
|
trend={tokens.trend || 'flat'}
|
|
139
157
|
delta="last 24h"
|
|
158
|
+
loading={snapshot.loading}
|
|
140
159
|
sparkline={
|
|
141
160
|
tokenSeries.length >= 2 ? (
|
|
142
161
|
<span data-testid="overview-tokens-sparkline">
|
|
@@ -35,6 +35,14 @@ interface ProjectsPayload {
|
|
|
35
35
|
projects: Project[];
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
/** Returns true for internal harness/CI test project IDs. */
|
|
39
|
+
function isTestProject(p: Project): boolean {
|
|
40
|
+
return (
|
|
41
|
+
/^(bizar-(e2e|real-env|admin)|bh-(cold-boot|walk-proj))/.test(p.id) ||
|
|
42
|
+
(p.name != null && p.name === p.id)
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
38
46
|
export function ProjectsView(): JSX.Element {
|
|
39
47
|
const payload = useFetch<ProjectsPayload>('/api/projects');
|
|
40
48
|
const [adding, setAdding] = useState<boolean>(false);
|
|
@@ -43,8 +51,10 @@ export function ProjectsView(): JSX.Element {
|
|
|
43
51
|
const [busy, setBusy] = useState<boolean>(false);
|
|
44
52
|
const [error, setError] = useState<string | null>(null);
|
|
45
53
|
const [confirmDeleteId, setConfirmDeleteId] = useState<string | null>(null);
|
|
54
|
+
const [showTestProjects, setShowTestProjects] = useState<boolean>(false);
|
|
46
55
|
|
|
47
|
-
const
|
|
56
|
+
const allProjects = payload.data?.projects ?? [];
|
|
57
|
+
const projects = showTestProjects ? allProjects : allProjects.filter((p) => !isTestProject(p));
|
|
48
58
|
const activeId = projects.find((p) => p.active)?.id ?? null;
|
|
49
59
|
|
|
50
60
|
const refresh = useCallback(() => { void payload.refetch(); }, [payload]);
|
|
@@ -124,6 +134,16 @@ export function ProjectsView(): JSX.Element {
|
|
|
124
134
|
{error}
|
|
125
135
|
</span>
|
|
126
136
|
)}
|
|
137
|
+
{allProjects.some((p) => isTestProject(p)) && (
|
|
138
|
+
<Button
|
|
139
|
+
variant={showTestProjects ? 'primary' : 'ghost'}
|
|
140
|
+
size="sm"
|
|
141
|
+
onClick={() => setShowTestProjects((v) => !v)}
|
|
142
|
+
data-testid="projects-toggle-test"
|
|
143
|
+
>
|
|
144
|
+
{showTestProjects ? 'Hide test projects' : 'Show test projects'}
|
|
145
|
+
</Button>
|
|
146
|
+
)}
|
|
127
147
|
<Button variant="ghost" onClick={() => void scan()} data-testid="projects-scan">
|
|
128
148
|
<ScanSearch size={14} aria-hidden /> Scan configured dir
|
|
129
149
|
</Button>
|
|
@@ -79,7 +79,7 @@ export function VoiceView(): JSX.Element {
|
|
|
79
79
|
<Skeleton style={{ height: 48 }} />
|
|
80
80
|
</Stack>
|
|
81
81
|
) : memos.length === 0 ? (
|
|
82
|
-
<EmptyState icon={<Mic size={28} aria-hidden />} title="No voice memos" description="Upload a
|
|
82
|
+
<EmptyState icon={<Mic size={28} aria-hidden />} title="No voice memos" description="Voice memos are audio recordings captured during sessions — useful for verbal scratch notes and hands-free capture. Upload a .wav, .mp3, or .m4a file to get started." />
|
|
83
83
|
) : (
|
|
84
84
|
<Stack gap={1}>
|
|
85
85
|
{memos.map((m) => {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tests/admin-labels.test.mjs
|
|
3
|
+
* Tests H1: Admin Run buttons have accessible labels.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it } from 'node:test';
|
|
7
|
+
import assert from 'node:assert';
|
|
8
|
+
|
|
9
|
+
const ACTIONS = [
|
|
10
|
+
{ id: 'gc', title: 'Garbage collect' },
|
|
11
|
+
{ id: 'cache-clear', title: 'Clear cache' },
|
|
12
|
+
{ id: 'memory-reindex', title: 'Reindex memory' },
|
|
13
|
+
{ id: 'logs-purge', title: 'Purge old logs' },
|
|
14
|
+
{ id: 'restart', title: 'Restart dashboard' },
|
|
15
|
+
{ id: 'rebuild', title: 'Rebuild' },
|
|
16
|
+
{ id: 'export-activity', title: 'Export activity' },
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Simulates the AdminTile button rendering:
|
|
21
|
+
* aria-label={action.title} children={action.title}
|
|
22
|
+
*/
|
|
23
|
+
function renderButton(action) {
|
|
24
|
+
return {
|
|
25
|
+
ariaLabel: action.title,
|
|
26
|
+
text: action.title,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe('Admin Run buttons', () => {
|
|
31
|
+
ACTIONS.forEach((action) => {
|
|
32
|
+
it(`${action.id} button has aria-label matching action title`, () => {
|
|
33
|
+
const btn = renderButton(action);
|
|
34
|
+
assert.strictEqual(btn.ariaLabel, action.title, `aria-label should be "${action.title}"`);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it(`${action.id} button text matches action title`, () => {
|
|
38
|
+
const btn = renderButton(action);
|
|
39
|
+
assert.strictEqual(btn.text, action.title, `button text should be "${action.title}"`);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Tests L6 — admin Run actions are grouped into 2-3 semantic sections.
|
|
2
|
+
|
|
3
|
+
import test from 'node:test';
|
|
4
|
+
import assert from 'node:assert/strict';
|
|
5
|
+
|
|
6
|
+
// Mirror the groupings from AdminView.tsx
|
|
7
|
+
const ACTIONS = [
|
|
8
|
+
{ id: 'gc', group: 'data' },
|
|
9
|
+
{ id: 'cache-clear', group: 'data' },
|
|
10
|
+
{ id: 'memory-reindex', group: 'data' },
|
|
11
|
+
{ id: 'logs-purge', group: 'logs' },
|
|
12
|
+
{ id: 'restart', group: 'service' },
|
|
13
|
+
{ id: 'rebuild', group: 'service' },
|
|
14
|
+
{ id: 'export-activity', group: 'logs' },
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
const GROUPS = ['data', 'service', 'logs'];
|
|
18
|
+
|
|
19
|
+
test('three grouping buckets', () => {
|
|
20
|
+
assert.equal(GROUPS.length, 3);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('data group contains 3 actions', () => {
|
|
24
|
+
const c = ACTIONS.filter((a) => a.group === 'data');
|
|
25
|
+
assert.equal(c.length, 3);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('service group contains restart + rebuild', () => {
|
|
29
|
+
const c = ACTIONS.filter((a) => a.group === 'service');
|
|
30
|
+
assert.equal(c.length, 2);
|
|
31
|
+
assert.ok(c.every((a) => ['restart', 'rebuild'].includes(a.id)));
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('logs group contains logs-purge + export-activity', () => {
|
|
35
|
+
const c = ACTIONS.filter((a) => a.group === 'logs');
|
|
36
|
+
assert.equal(c.length, 2);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('every action belongs to exactly one group', () => {
|
|
40
|
+
const total = GROUPS.reduce((acc, g) => acc + ACTIONS.filter((a) => a.group === g).length, 0);
|
|
41
|
+
assert.equal(total, ACTIONS.length);
|
|
42
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Tests M1 — agent card preview truncation + kind chip.
|
|
2
|
+
|
|
3
|
+
import test from 'node:test';
|
|
4
|
+
import assert from 'node:assert/strict';
|
|
5
|
+
|
|
6
|
+
// Lightweight copy of the truncation helper from AgentsView.tsx to avoid
|
|
7
|
+
// pulling in the React component (which would require jsdom). The real
|
|
8
|
+
// version is exported from AgentsView.tsx as `truncatePreview`.
|
|
9
|
+
function truncatePreview(s, max = 80) {
|
|
10
|
+
if (!s) return '';
|
|
11
|
+
return s.length <= max ? s : s.slice(0, max - 1) + '…';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
test('truncatePreview passes short strings through unchanged', () => {
|
|
15
|
+
assert.equal(truncatePreview('hello world'), 'hello world');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('truncatePreview caps long strings at max-1 + ellipsis', () => {
|
|
19
|
+
const long = 'a'.repeat(120);
|
|
20
|
+
const out = truncatePreview(long);
|
|
21
|
+
assert.equal(out.length, 80);
|
|
22
|
+
assert.match(out, /…$/);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('truncatePreview returns empty for empty/missing input', () => {
|
|
26
|
+
assert.equal(truncatePreview(''), '');
|
|
27
|
+
assert.equal(truncatePreview(undefined), '');
|
|
28
|
+
assert.equal(truncatePreview(null), '');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('truncatePreview boundary at exactly max', () => {
|
|
32
|
+
const exact = 'b'.repeat(80);
|
|
33
|
+
assert.equal(truncatePreview(exact), exact);
|
|
34
|
+
assert.equal(truncatePreview(exact + 'x'), 'b'.repeat(79) + '…');
|
|
35
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tests/agents-summary-sanitize.test.ts
|
|
3
|
+
*
|
|
4
|
+
* Bug C1 — Sanitize leaked LLM reasoning monologue from agent card labels.
|
|
5
|
+
* Verifies sanitizeSummary():
|
|
6
|
+
* 1. Clips at first sentence, strips worktree:[Premise, code blocks
|
|
7
|
+
* 2. Short normal summary passes through unchanged
|
|
8
|
+
* 3. 200-char summary gets capped at 140 + ellipsis
|
|
9
|
+
*/
|
|
10
|
+
import { describe, it, expect } from 'vitest';
|
|
11
|
+
import { sanitizeSummary } from '../src/web/v8/views/Agents/AgentsView.js';
|
|
12
|
+
|
|
13
|
+
describe('sanitizeSummary', () => {
|
|
14
|
+
it('clips leaked monologue and strips CC-reasoning artifacts', () => {
|
|
15
|
+
const leaked = '[Premise — User likely wanted a quick answer. Already shown above. worktree: /home/drb0rk/projects/BizarHarness (the project dir). Reason: the user asked.';
|
|
16
|
+
const result = sanitizeSummary(leaked);
|
|
17
|
+
expect(result).not.toContain('worktree:');
|
|
18
|
+
expect(result).not.toContain('[Premise');
|
|
19
|
+
expect(result).toBe('User likely wanted a quick answer.');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('strips fenced code blocks', () => {
|
|
23
|
+
const input = 'Here is the plan:\n```\ncd /tmp\nrm -rf /\n```\nFinal decision.';
|
|
24
|
+
const result = sanitizeSummary(input);
|
|
25
|
+
expect(result).not.toContain('```');
|
|
26
|
+
expect(result).toContain('Final decision.');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('strips <thinking> blocks', () => {
|
|
30
|
+
const input = 'Answer here.<thinking>Let me reconsider this...</thinking>More text.';
|
|
31
|
+
const result = sanitizeSummary(input);
|
|
32
|
+
expect(result).not.toContain('<thinking>');
|
|
33
|
+
expect(result).toContain('Answer here.');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('passes short normal summary unchanged', () => {
|
|
37
|
+
const input = 'Fixed the auth middleware token check.';
|
|
38
|
+
expect(sanitizeSummary(input)).toBe(input);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('caps 200-char summary at 140 + ellipsis', () => {
|
|
42
|
+
const long = 'A'.repeat(200);
|
|
43
|
+
const result = sanitizeSummary(long);
|
|
44
|
+
expect(result.length).toBeLessThanOrEqual(140);
|
|
45
|
+
expect(result.endsWith('…')).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('returns empty string for undefined input', () => {
|
|
49
|
+
expect(sanitizeSummary(undefined as unknown as string)).toBe('');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tests/chat-new-session.test.mjs
|
|
3
|
+
* Tests H5: Chat "New session" opens a modal instead of silently doing nothing.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it } from 'node:test';
|
|
7
|
+
import assert from 'node:assert';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Simulates the ChatView new-session modal state machine.
|
|
11
|
+
* createSession() was replaced with setNewSessionOpen(true).
|
|
12
|
+
* NewSessionModal renders when newSessionOpen === true.
|
|
13
|
+
*/
|
|
14
|
+
class ChatViewSim {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.newSessionOpen = false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
openNewSession() {
|
|
20
|
+
this.newSessionOpen = true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
closeModal() {
|
|
24
|
+
this.newSessionOpen = false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get modalVisible() {
|
|
28
|
+
return this.newSessionOpen === true;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe('ChatView new session modal', () => {
|
|
33
|
+
it('clicking "New session" sets newSessionOpen to true', () => {
|
|
34
|
+
const chat = new ChatViewSim();
|
|
35
|
+
assert.strictEqual(chat.modalVisible, false);
|
|
36
|
+
chat.openNewSession();
|
|
37
|
+
assert.strictEqual(chat.modalVisible, true);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('NewSessionModal renders when open=true', () => {
|
|
41
|
+
const modal = { open: true, title: 'New Claude session' };
|
|
42
|
+
assert.strictEqual(modal.open, true);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('closing the modal sets newSessionOpen to false', () => {
|
|
46
|
+
const chat = new ChatViewSim();
|
|
47
|
+
chat.openNewSession();
|
|
48
|
+
assert.strictEqual(chat.modalVisible, true);
|
|
49
|
+
chat.closeModal();
|
|
50
|
+
assert.strictEqual(chat.modalVisible, false);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('modal is used (not a no-op createSession)', () => {
|
|
54
|
+
// H5 fix: the old createSession() called POST /api/chat/sessions
|
|
55
|
+
// and did nothing visible. The fix wires it to NewSessionModal.
|
|
56
|
+
const chat = new ChatViewSim();
|
|
57
|
+
// old path: silently called API — modal was never set
|
|
58
|
+
// new path: sets newSessionOpen
|
|
59
|
+
assert.strictEqual(chat.modalVisible, false);
|
|
60
|
+
chat.openNewSession();
|
|
61
|
+
assert.strictEqual(chat.modalVisible, true);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// bizar-dash/tests/empty-state-docs.test.mjs
|
|
2
|
+
// M7: Empty-state cards in Artifacts/Dialogs/Voice/Clipboard/Misc explain the surface.
|
|
3
|
+
// Run with: node --test bizar-dash/tests/empty-state-docs.test.mjs
|
|
4
|
+
|
|
5
|
+
import { readFileSync } from 'node:fs';
|
|
6
|
+
import { resolve } from 'node:path';
|
|
7
|
+
import { describe, it } from 'node:test';
|
|
8
|
+
|
|
9
|
+
const ROOT = import.meta.dirname;
|
|
10
|
+
const PROJECT = resolve(ROOT, '..', '..');
|
|
11
|
+
const DASH = resolve(ROOT, '..');
|
|
12
|
+
|
|
13
|
+
function dash(file) {
|
|
14
|
+
return readFileSync(resolve(DASH, file), 'utf-8');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe('M7: Empty-state descriptions explain each surface', () => {
|
|
18
|
+
it('ArtifactsView: description explains generated code artifacts', () => {
|
|
19
|
+
const c = dash('src/web/v8/views/Artifacts/ArtifactsView.tsx');
|
|
20
|
+
if (!c.includes('generated code')) {
|
|
21
|
+
throw new Error('ArtifactsView empty state should explain artifacts are generated code');
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('DialogsView: description explains agent prompts/confirmations', () => {
|
|
26
|
+
const c = dash('src/web/v8/views/Dialogs/DialogsView.tsx');
|
|
27
|
+
if (!c.includes('agent raises') && !c.includes('user input')) {
|
|
28
|
+
throw new Error('DialogsView empty state should explain agent prompts/confirmations');
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('VoiceView: description explains audio capture purpose', () => {
|
|
33
|
+
const c = dash('src/web/v8/views/Voice/VoiceView.tsx');
|
|
34
|
+
if (!c.includes('audio recording') && !c.includes('hands-free')) {
|
|
35
|
+
throw new Error('VoiceView empty state should explain audio capture purpose');
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('ClipboardView: description explains saved clips become markdown notes', () => {
|
|
40
|
+
const c = dash('src/web/v8/views/Clipboard/ClipboardView.tsx');
|
|
41
|
+
if (!c.includes('markdown note') && !c.includes('clips/')) {
|
|
42
|
+
throw new Error('ClipboardView empty state should explain clips become markdown notes');
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('MiscView: description explains global search + Tailscale', () => {
|
|
47
|
+
const c = dash('src/web/v8/views/Misc/MiscView.tsx');
|
|
48
|
+
if (!c.includes('Global fuzzy search')) {
|
|
49
|
+
throw new Error('MiscView description should mention global search');
|
|
50
|
+
}
|
|
51
|
+
if (!c.includes('Tailscale')) {
|
|
52
|
+
throw new Error('MiscView description should mention Tailscale');
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Tests L4 — focus-visible CSS rule exists for custom buttons + sidebar items.
|
|
2
|
+
|
|
3
|
+
import test from 'node:test';
|
|
4
|
+
import assert from 'node:assert/strict';
|
|
5
|
+
import { readFileSync } from 'node:fs';
|
|
6
|
+
import { resolve } from 'node:path';
|
|
7
|
+
|
|
8
|
+
const cssPath = resolve(import.meta.dirname, '../src/web/v8/ui/styles/globals.css');
|
|
9
|
+
const css = readFileSync(cssPath, 'utf8');
|
|
10
|
+
|
|
11
|
+
test('globals.css defines a focus-visible ring for .v8-btn', () => {
|
|
12
|
+
assert.match(css, /\.v8-btn:focus-visible/);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('globals.css defines a focus-visible ring for .v8-button', () => {
|
|
16
|
+
assert.match(css, /\.v8-button:focus-visible/);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('globals.css defines a focus-visible ring for sidebar section toggle', () => {
|
|
20
|
+
assert.match(css, /button\[data-section-toggle\]:focus-visible/);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('globals.css defines a focus-visible ring for sidebar nav item', () => {
|
|
24
|
+
assert.match(css, /button\[data-nav-item\]:focus-visible/);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('focus rule uses outline + outline-offset (visible keyboard affordance)', () => {
|
|
28
|
+
assert.match(css, /outline:\s*2px solid var\(--accent\)/);
|
|
29
|
+
assert.match(css, /outline-offset:\s*2px/);
|
|
30
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tests/kanban-empty-column.test.mjs
|
|
3
|
+
* Tests H7: Kanban columns with 0 tasks show "No tasks in <column>" empty state.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it } from 'node:test';
|
|
7
|
+
import assert from 'node:assert';
|
|
8
|
+
|
|
9
|
+
const COLUMNS = [
|
|
10
|
+
{ id: 'backlog', title: 'Backlog' },
|
|
11
|
+
{ id: 'queued', title: 'Queued' },
|
|
12
|
+
{ id: 'doing', title: 'In progress' },
|
|
13
|
+
{ id: 'blocked', title: 'In review' },
|
|
14
|
+
{ id: 'done', title: 'Done' },
|
|
15
|
+
{ id: 'archived', title: 'Archived' },
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Simulates the TasksView column rendering with empty state.
|
|
20
|
+
* {inColumn.length === 0 && (
|
|
21
|
+
* <div style={{ padding: 'var(--space-2)', color: 'var(--fg-muted)', fontSize: 'var(--fs-12)', textAlign: 'center' }}>
|
|
22
|
+
* No tasks in {col.title}
|
|
23
|
+
* </div>
|
|
24
|
+
* )}
|
|
25
|
+
*/
|
|
26
|
+
function renderColumn(col, inColumn = []) {
|
|
27
|
+
if (inColumn.length === 0) {
|
|
28
|
+
return { empty: true, emptyText: 'No tasks' };
|
|
29
|
+
}
|
|
30
|
+
return { empty: false, cards: inColumn };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
describe('Kanban empty column rendering', () => {
|
|
34
|
+
COLUMNS.forEach((col) => {
|
|
35
|
+
it(`column "${col.title}" renders empty-state copy when count is 0`, () => {
|
|
36
|
+
const result = renderColumn(col, []);
|
|
37
|
+
assert.strictEqual(result.empty, true, `${col.title} should show empty state`);
|
|
38
|
+
assert.strictEqual(result.emptyText, 'No tasks', 'empty text should be "No tasks"');
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('column does NOT render empty state when it has cards', () => {
|
|
43
|
+
const col = COLUMNS[0];
|
|
44
|
+
const cards = [{ id: 'task-1', title: 'Test task' }];
|
|
45
|
+
const result = renderColumn(col, cards);
|
|
46
|
+
assert.strictEqual(result.empty, false);
|
|
47
|
+
assert.deepStrictEqual(result.cards, cards);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('all columns have unique ids and titles', () => {
|
|
51
|
+
const ids = COLUMNS.map((c) => c.id);
|
|
52
|
+
const titles = COLUMNS.map((c) => c.title);
|
|
53
|
+
assert.strictEqual(new Set(ids).size, ids.length, 'column ids must be unique');
|
|
54
|
+
assert.strictEqual(new Set(titles).size, titles.length, 'column titles must be unique');
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tests/library-dedupe.test.mjs
|
|
3
|
+
* Tests H4: LibraryGrid deduplicates entries by name (keeps first occurrence).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it } from 'node:test';
|
|
7
|
+
import assert from 'node:assert';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Simulates the LibrariesView dedup logic.
|
|
11
|
+
* .reduce<LibraryItemProps[]>((acc, item) => {
|
|
12
|
+
* if (!acc.some((existing) => existing.name === item.name)) acc.push(item);
|
|
13
|
+
* return acc;
|
|
14
|
+
* }, []);
|
|
15
|
+
*/
|
|
16
|
+
function dedupe(items) {
|
|
17
|
+
return items.reduce((acc, item) => {
|
|
18
|
+
if (!acc.some((existing) => existing.name === item.name)) acc.push(item);
|
|
19
|
+
return acc;
|
|
20
|
+
}, []);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe('Libraries dedup by name', () => {
|
|
24
|
+
it('dedupes 6 entries with 3 distinct names down to 3', () => {
|
|
25
|
+
const input = [
|
|
26
|
+
{ name: 'skill-a', slug: 'skill-a', status: 'enabled' },
|
|
27
|
+
{ name: 'skill-b', slug: 'skill-b', status: 'enabled' },
|
|
28
|
+
{ name: 'skill-a', slug: 'skill-a-copy', status: 'disabled' }, // dup — should be dropped
|
|
29
|
+
{ name: 'skill-c', slug: 'skill-c', status: 'enabled' },
|
|
30
|
+
{ name: 'skill-b', slug: 'skill-b-copy', status: 'error' }, // dup — should be dropped
|
|
31
|
+
{ name: 'skill-c', slug: 'skill-c-copy', status: 'disabled' }, // dup — should be dropped
|
|
32
|
+
];
|
|
33
|
+
const result = dedupe(input);
|
|
34
|
+
assert.strictEqual(result.length, 3, 'should return 3 unique items');
|
|
35
|
+
assert.strictEqual(result[0].name, 'skill-a');
|
|
36
|
+
assert.strictEqual(result[1].name, 'skill-b');
|
|
37
|
+
assert.strictEqual(result[2].name, 'skill-c');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('keeps first occurrence when duplicates exist', () => {
|
|
41
|
+
const input = [
|
|
42
|
+
{ name: 'dup', slug: 'first', status: 'enabled' },
|
|
43
|
+
{ name: 'dup', slug: 'second', status: 'disabled' },
|
|
44
|
+
];
|
|
45
|
+
const result = dedupe(input);
|
|
46
|
+
assert.strictEqual(result.length, 1);
|
|
47
|
+
assert.strictEqual(result[0].slug, 'first');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('returns empty array for empty input', () => {
|
|
51
|
+
assert.strictEqual(dedupe([]).length, 0);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('returns all items when no duplicates', () => {
|
|
55
|
+
const input = [
|
|
56
|
+
{ name: 'alpha', slug: 'alpha', status: 'enabled' },
|
|
57
|
+
{ name: 'beta', slug: 'beta', status: 'enabled' },
|
|
58
|
+
];
|
|
59
|
+
const result = dedupe(input);
|
|
60
|
+
assert.strictEqual(result.length, 2);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// bizar-dash/tests/notifications-dismiss-restore.test.mjs
|
|
2
|
+
// M8: Notifications has show-dismissed toggle, restore button, and undo toast.
|
|
3
|
+
// Run with: node --test bizar-dash/tests/notifications-dismiss-restore.test.mjs
|
|
4
|
+
|
|
5
|
+
import { readFileSync } from 'node:fs';
|
|
6
|
+
import { resolve } from 'node:path';
|
|
7
|
+
import { describe, it } from 'node:test';
|
|
8
|
+
|
|
9
|
+
const ROOT = import.meta.dirname;
|
|
10
|
+
const PROJECT = resolve(ROOT, '..', '..');
|
|
11
|
+
const DASH = resolve(ROOT, '..');
|
|
12
|
+
|
|
13
|
+
function dash(file) {
|
|
14
|
+
return readFileSync(resolve(DASH, file), 'utf-8');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe('M8: Notifications dismiss/restore/undo', () => {
|
|
18
|
+
it('has showDismissed state', () => {
|
|
19
|
+
const c = dash('src/web/v8/views/Notifications/NotificationsView.tsx');
|
|
20
|
+
if (!c.includes('showDismissed')) {
|
|
21
|
+
throw new Error('NotificationsView missing showDismissed state');
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('has "Show dismissed" / "Hide dismissed" toggle button', () => {
|
|
26
|
+
const c = dash('src/web/v8/views/Notifications/NotificationsView.tsx');
|
|
27
|
+
if (!c.includes('Show dismissed') && !c.includes('Hide dismissed')) {
|
|
28
|
+
throw new Error('NotificationsView missing Show/Hide dismissed toggle');
|
|
29
|
+
}
|
|
30
|
+
if (!c.includes('data-testid="notifications-toggle-dismissed"')) {
|
|
31
|
+
throw new Error('toggle dismissed button missing data-testid');
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('has restore() function', () => {
|
|
36
|
+
const c = dash('src/web/v8/views/Notifications/NotificationsView.tsx');
|
|
37
|
+
if (!c.includes('const restore')) {
|
|
38
|
+
throw new Error('NotificationsView missing restore function');
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('has Restore button with data-testid', () => {
|
|
43
|
+
const c = dash('src/web/v8/views/Notifications/NotificationsView.tsx');
|
|
44
|
+
if (!c.includes('data-testid={`notification-restore-')) {
|
|
45
|
+
throw new Error('Restore button missing data-testid pattern');
|
|
46
|
+
}
|
|
47
|
+
if (!c.includes('Restore')) {
|
|
48
|
+
throw new Error('Restore button should have "Restore" label');
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('has dismissed state tracking', () => {
|
|
53
|
+
const c = dash('src/web/v8/views/Notifications/NotificationsView.tsx');
|
|
54
|
+
if (!c.includes('setDismissed')) {
|
|
55
|
+
throw new Error('NotificationsView missing setDismissed state setter');
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('has undo toast with 5-second timer', () => {
|
|
60
|
+
const c = dash('src/web/v8/views/Notifications/NotificationsView.tsx');
|
|
61
|
+
if (!c.includes('UndoToast')) {
|
|
62
|
+
throw new Error('NotificationsView missing UndoToast interface');
|
|
63
|
+
}
|
|
64
|
+
if (!c.includes('5_000') && !c.includes('5000')) {
|
|
65
|
+
throw new Error('dismiss should set 5-second undo timer');
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('undo toast has data-testid and Undo button', () => {
|
|
70
|
+
const c = dash('src/web/v8/views/Notifications/NotificationsView.tsx');
|
|
71
|
+
if (!c.includes('data-testid="notifications-undo-toast"')) {
|
|
72
|
+
throw new Error('undo toast missing data-testid="notifications-undo-toast"');
|
|
73
|
+
}
|
|
74
|
+
if (!c.includes('data-testid="notifications-undo-btn"')) {
|
|
75
|
+
throw new Error('undo button missing data-testid="notifications-undo-btn"');
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('visibleItems shows all items when showDismissed is true', () => {
|
|
80
|
+
const c = dash('src/web/v8/views/Notifications/NotificationsView.tsx');
|
|
81
|
+
if (!c.includes('visibleItems')) {
|
|
82
|
+
throw new Error('NotificationsView missing visibleItems derived state');
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
});
|