@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.
Files changed (262) hide show
  1. package/bizar-dash/dist/assets/{ActivityFeed-DtRtg-JN.js → ActivityFeed-RQeyFifx.js} +1 -1
  2. package/bizar-dash/dist/assets/{ActivityFeed-DtRtg-JN.js.map → ActivityFeed-RQeyFifx.js.map} +1 -1
  3. package/bizar-dash/dist/assets/{ActivityView-CoTXDyU_.js → ActivityView-CibyssPM.js} +1 -1
  4. package/bizar-dash/dist/assets/{ActivityView-CoTXDyU_.js.map → ActivityView-CibyssPM.js.map} +1 -1
  5. package/bizar-dash/dist/assets/AdminView-BjczABeB.js +11 -0
  6. package/bizar-dash/dist/assets/AdminView-BjczABeB.js.map +1 -0
  7. package/bizar-dash/dist/assets/{AgentActivity-CrX7LmQ6.js → AgentActivity-Bywt1twv.js} +1 -1
  8. package/bizar-dash/dist/assets/{AgentActivity-CrX7LmQ6.js.map → AgentActivity-Bywt1twv.js.map} +1 -1
  9. package/bizar-dash/dist/assets/AgentsView-BnOjjM74.js +15 -0
  10. package/bizar-dash/dist/assets/AgentsView-BnOjjM74.js.map +1 -0
  11. package/bizar-dash/dist/assets/{Alert-B6DjfRFI.js → Alert-BZGk39mB.js} +1 -1
  12. package/bizar-dash/dist/assets/{Alert-B6DjfRFI.js.map → Alert-BZGk39mB.js.map} +1 -1
  13. package/bizar-dash/dist/assets/{ArtifactsView-znhN8GTR.js → ArtifactsView-CfYB1le1.js} +3 -3
  14. package/bizar-dash/dist/assets/ArtifactsView-CfYB1le1.js.map +1 -0
  15. package/bizar-dash/dist/assets/{AuthView-Rk8eKUPr.js → AuthView-D4frSShw.js} +1 -1
  16. package/bizar-dash/dist/assets/{AuthView-Rk8eKUPr.js.map → AuthView-D4frSShw.js.map} +1 -1
  17. package/bizar-dash/dist/assets/Avatar-BYg0rkPA.js +6 -0
  18. package/bizar-dash/dist/assets/Avatar-BYg0rkPA.js.map +1 -0
  19. package/bizar-dash/dist/assets/{BackgroundJobsView-BLWuN9qz.js → BackgroundJobsView-DEStvILz.js} +1 -1
  20. package/bizar-dash/dist/assets/{BackgroundJobsView-BLWuN9qz.js.map → BackgroundJobsView-DEStvILz.js.map} +1 -1
  21. package/bizar-dash/dist/assets/{BackupView-CqbtcnuQ.js → BackupView-B875KQa4.js} +1 -1
  22. package/bizar-dash/dist/assets/{BackupView-CqbtcnuQ.js.map → BackupView-B875KQa4.js.map} +1 -1
  23. package/bizar-dash/dist/assets/Banner-C8HEcT5l.js +1 -0
  24. package/bizar-dash/dist/assets/Banner-C8HEcT5l.js.map +1 -0
  25. package/bizar-dash/dist/assets/{Card-rF59wHiO.js → Card-BtsmkhJa.js} +1 -1
  26. package/bizar-dash/dist/assets/{Card-rF59wHiO.js.map → Card-BtsmkhJa.js.map} +1 -1
  27. package/bizar-dash/dist/assets/ChatView-CkNS0SD8.js +9 -0
  28. package/bizar-dash/dist/assets/ChatView-CkNS0SD8.js.map +1 -0
  29. package/bizar-dash/dist/assets/{Chip-Dc2KmeWl.js → Chip-XPOk0d_E.js} +1 -1
  30. package/bizar-dash/dist/assets/{Chip-Dc2KmeWl.js.map → Chip-XPOk0d_E.js.map} +1 -1
  31. package/bizar-dash/dist/assets/{ClaudeSessionsView-CiaHQdDZ.js → ClaudeSessionsView-BprGo67K.js} +1 -1
  32. package/bizar-dash/dist/assets/{ClaudeSessionsView-CiaHQdDZ.js.map → ClaudeSessionsView-BprGo67K.js.map} +1 -1
  33. package/bizar-dash/dist/assets/ClipboardView-CPlTU5VG.js +1 -0
  34. package/bizar-dash/dist/assets/ClipboardView-CPlTU5VG.js.map +1 -0
  35. package/bizar-dash/dist/assets/{ConfigView-B2ysnTZU.js → ConfigView-BntUrW7s.js} +1 -1
  36. package/bizar-dash/dist/assets/{ConfigView-B2ysnTZU.js.map → ConfigView-BntUrW7s.js.map} +1 -1
  37. package/bizar-dash/dist/assets/DiagnosticsView-4x0Zu3yP.js +2 -0
  38. package/bizar-dash/dist/assets/DiagnosticsView-4x0Zu3yP.js.map +1 -0
  39. package/bizar-dash/dist/assets/DialogsView-Xd02dVAX.js +6 -0
  40. package/bizar-dash/dist/assets/DialogsView-Xd02dVAX.js.map +1 -0
  41. package/bizar-dash/dist/assets/{DoctorView-BQwqvMAh.js → DoctorView-DepBdQGR.js} +2 -2
  42. package/bizar-dash/dist/assets/{DoctorView-BQwqvMAh.js.map → DoctorView-DepBdQGR.js.map} +1 -1
  43. package/bizar-dash/dist/assets/{EmptyState-D65J5FQN.js → EmptyState-DjKtY1Qi.js} +1 -1
  44. package/bizar-dash/dist/assets/{EmptyState-D65J5FQN.js.map → EmptyState-DjKtY1Qi.js.map} +1 -1
  45. package/bizar-dash/dist/assets/{EnvVarsView-DX-3oGH-.js → EnvVarsView-S4Pbm128.js} +3 -3
  46. package/bizar-dash/dist/assets/{EnvVarsView-DX-3oGH-.js.map → EnvVarsView-S4Pbm128.js.map} +1 -1
  47. package/bizar-dash/dist/assets/{ErrorState-DULZnttJ.js → ErrorState-CBJbEQ8B.js} +1 -1
  48. package/bizar-dash/dist/assets/{ErrorState-DULZnttJ.js.map → ErrorState-CBJbEQ8B.js.map} +1 -1
  49. package/bizar-dash/dist/assets/EvalView-BeAyMCD1.js +1 -0
  50. package/bizar-dash/dist/assets/{EvalView-CpdFu_ID.js.map → EvalView-BeAyMCD1.js.map} +1 -1
  51. package/bizar-dash/dist/assets/{GoalsView-Br8oPPfb.js → GoalsView-BZr8In4c.js} +1 -1
  52. package/bizar-dash/dist/assets/{GoalsView-Br8oPPfb.js.map → GoalsView-BZr8In4c.js.map} +1 -1
  53. package/bizar-dash/dist/assets/{Grid-BSXhsCPC.js → Grid--WKvqu5h.js} +1 -1
  54. package/bizar-dash/dist/assets/{Grid-BSXhsCPC.js.map → Grid--WKvqu5h.js.map} +1 -1
  55. package/bizar-dash/dist/assets/{HistoryView-D0J3aoZF.js → HistoryView-djEbzqtk.js} +1 -1
  56. package/bizar-dash/dist/assets/{HistoryView-D0J3aoZF.js.map → HistoryView-djEbzqtk.js.map} +1 -1
  57. package/bizar-dash/dist/assets/{Input-CZmRw5QX.js → Input-h_nSOtwm.js} +1 -1
  58. package/bizar-dash/dist/assets/{Input-CZmRw5QX.js.map → Input-h_nSOtwm.js.map} +1 -1
  59. package/bizar-dash/dist/assets/LibrariesView-Vez8CvNq.js +6 -0
  60. package/bizar-dash/dist/assets/{LibrariesView-B1YX9wzp.js.map → LibrariesView-Vez8CvNq.js.map} +1 -1
  61. package/bizar-dash/dist/assets/{LightRAGView-CykR0t9y.js → LightRAGView-DvqzQoBp.js} +1 -1
  62. package/bizar-dash/dist/assets/{LightRAGView-CykR0t9y.js.map → LightRAGView-DvqzQoBp.js.map} +1 -1
  63. package/bizar-dash/dist/assets/{ListHeader-Dpun-r36.js → ListHeader-CM8QoIWG.js} +1 -1
  64. package/bizar-dash/dist/assets/{ListHeader-Dpun-r36.js.map → ListHeader-CM8QoIWG.js.map} +1 -1
  65. package/bizar-dash/dist/assets/{MemoryView-BbER5ZKt.js → MemoryView-DnL8E2vI.js} +1 -1
  66. package/bizar-dash/dist/assets/{MemoryView-BbER5ZKt.js.map → MemoryView-DnL8E2vI.js.map} +1 -1
  67. package/bizar-dash/dist/assets/{MessageBubble-5vUAquDx.js → MessageBubble-CrCJ0SYG.js} +1 -1
  68. package/bizar-dash/dist/assets/{MessageBubble-5vUAquDx.js.map → MessageBubble-CrCJ0SYG.js.map} +1 -1
  69. package/bizar-dash/dist/assets/MiscView-B2YQXhZ_.js +1 -0
  70. package/bizar-dash/dist/assets/MiscView-B2YQXhZ_.js.map +1 -0
  71. package/bizar-dash/dist/assets/{ModsView-P7gDeNHM.js → ModsView-BVM3X5xc.js} +1 -1
  72. package/bizar-dash/dist/assets/{ModsView-P7gDeNHM.js.map → ModsView-BVM3X5xc.js.map} +1 -1
  73. package/bizar-dash/dist/assets/NotificationsView-A-Xs8BL1.js +6 -0
  74. package/bizar-dash/dist/assets/NotificationsView-A-Xs8BL1.js.map +1 -0
  75. package/bizar-dash/dist/assets/ObsidianView-mI3qW9OP.js +1 -0
  76. package/bizar-dash/dist/assets/{ObsidianView-Ci80gnwS.js.map → ObsidianView-mI3qW9OP.js.map} +1 -1
  77. package/bizar-dash/dist/assets/OverviewView-By69Cr5H.js +1 -0
  78. package/bizar-dash/dist/assets/OverviewView-By69Cr5H.js.map +1 -0
  79. package/bizar-dash/dist/assets/{ProgressBar-BahmeJhP.js → ProgressBar-BnaBqv-W.js} +1 -1
  80. package/bizar-dash/dist/assets/{ProgressBar-BahmeJhP.js.map → ProgressBar-BnaBqv-W.js.map} +1 -1
  81. package/bizar-dash/dist/assets/ProjectsView-C81WTFka.js +11 -0
  82. package/bizar-dash/dist/assets/ProjectsView-C81WTFka.js.map +1 -0
  83. package/bizar-dash/dist/assets/{ProvidersView-BftGn8Cr.js → ProvidersView-2cn-TALE.js} +2 -2
  84. package/bizar-dash/dist/assets/{ProvidersView-BftGn8Cr.js.map → ProvidersView-2cn-TALE.js.map} +1 -1
  85. package/bizar-dash/dist/assets/{SchedulesView-CtNxtzN6.js → SchedulesView-o9AtIsH9.js} +1 -1
  86. package/bizar-dash/dist/assets/{SchedulesView-CtNxtzN6.js.map → SchedulesView-o9AtIsH9.js.map} +1 -1
  87. package/bizar-dash/dist/assets/{Select-0VcfTDeL.js → Select-CiGWEN8I.js} +1 -1
  88. package/bizar-dash/dist/assets/{Select-0VcfTDeL.js.map → Select-CiGWEN8I.js.map} +1 -1
  89. package/bizar-dash/dist/assets/{SettingsRow-BLic3DQl.js → SettingsRow-CflGJymA.js} +1 -1
  90. package/bizar-dash/dist/assets/{SettingsRow-BLic3DQl.js.map → SettingsRow-CflGJymA.js.map} +1 -1
  91. package/bizar-dash/dist/assets/{SettingsView-BjS3i4nr.js → SettingsView-2HSFJPPK.js} +1 -1
  92. package/bizar-dash/dist/assets/{SettingsView-BjS3i4nr.js.map → SettingsView-2HSFJPPK.js.map} +1 -1
  93. package/bizar-dash/dist/assets/{Sheet-BOProMs2.js → Sheet-B-CMP9-_.js} +1 -1
  94. package/bizar-dash/dist/assets/{Sheet-BOProMs2.js.map → Sheet-B-CMP9-_.js.map} +1 -1
  95. package/bizar-dash/dist/assets/{Slider-B0eLEEUt.js → Slider-Z4TP_CXY.js} +1 -1
  96. package/bizar-dash/dist/assets/{Slider-B0eLEEUt.js.map → Slider-Z4TP_CXY.js.map} +1 -1
  97. package/bizar-dash/dist/assets/{Sparkline-SSrdgyW0.js → Sparkline-cvkv2HbU.js} +1 -1
  98. package/bizar-dash/dist/assets/{Sparkline-SSrdgyW0.js.map → Sparkline-cvkv2HbU.js.map} +1 -1
  99. package/bizar-dash/dist/assets/{Switch-B3EQzb_q.js → Switch-jcYo4lmp.js} +1 -1
  100. package/bizar-dash/dist/assets/{Switch-B3EQzb_q.js.map → Switch-jcYo4lmp.js.map} +1 -1
  101. package/bizar-dash/dist/assets/{TasksView-SWjF_ZUQ.js → TasksView-CQaSFzWY.js} +2 -2
  102. package/bizar-dash/dist/assets/{TasksView-SWjF_ZUQ.js.map → TasksView-CQaSFzWY.js.map} +1 -1
  103. package/bizar-dash/dist/assets/{Textarea-QuofpMlq.js → Textarea-xnAybFGd.js} +1 -1
  104. package/bizar-dash/dist/assets/{Textarea-QuofpMlq.js.map → Textarea-xnAybFGd.js.map} +1 -1
  105. package/bizar-dash/dist/assets/UpdateView-BUAEcRCX.js +1 -0
  106. package/bizar-dash/dist/assets/UpdateView-BUAEcRCX.js.map +1 -0
  107. package/bizar-dash/dist/assets/{UsageView-BqOw6FBg.js → UsageView-DMFbBOfH.js} +1 -1
  108. package/bizar-dash/dist/assets/{UsageView-BqOw6FBg.js.map → UsageView-DMFbBOfH.js.map} +1 -1
  109. package/bizar-dash/dist/assets/{ViewHeader-B6P-C4GD.js → ViewHeader-C-t1U2cQ.js} +1 -1
  110. package/bizar-dash/dist/assets/{ViewHeader-B6P-C4GD.js.map → ViewHeader-C-t1U2cQ.js.map} +1 -1
  111. package/bizar-dash/dist/assets/VoiceView-aoPVT6LJ.js +1 -0
  112. package/bizar-dash/dist/assets/VoiceView-aoPVT6LJ.js.map +1 -0
  113. package/bizar-dash/dist/assets/{brain-CwZt9Kta.js → brain-DhAzcHUk.js} +1 -1
  114. package/bizar-dash/dist/assets/{brain-CwZt9Kta.js.map → brain-DhAzcHUk.js.map} +1 -1
  115. package/bizar-dash/dist/assets/{calendar-C5oK0_nM.js → calendar-B9Wbemh7.js} +1 -1
  116. package/bizar-dash/dist/assets/{calendar-C5oK0_nM.js.map → calendar-B9Wbemh7.js.map} +1 -1
  117. package/bizar-dash/dist/assets/{circle-D1Lc_P2a.js → circle-6NJF_Xl6.js} +1 -1
  118. package/bizar-dash/dist/assets/{circle-D1Lc_P2a.js.map → circle-6NJF_Xl6.js.map} +1 -1
  119. package/bizar-dash/dist/assets/{circle-alert-HdHe7m8K.js → circle-alert-Dz8lGfCZ.js} +1 -1
  120. package/bizar-dash/dist/assets/{circle-alert-HdHe7m8K.js.map → circle-alert-Dz8lGfCZ.js.map} +1 -1
  121. package/bizar-dash/dist/assets/{circle-check-Cs-xoV7R.js → circle-check-DFB50ojt.js} +1 -1
  122. package/bizar-dash/dist/assets/{circle-check-Cs-xoV7R.js.map → circle-check-DFB50ojt.js.map} +1 -1
  123. package/bizar-dash/dist/assets/{clock-DEIuPV60.js → clock-ClVA6-hQ.js} +1 -1
  124. package/bizar-dash/dist/assets/{clock-DEIuPV60.js.map → clock-ClVA6-hQ.js.map} +1 -1
  125. package/bizar-dash/dist/assets/{cmdk-DVxytz9i.js → cmdk-C3F6LD6N.js} +5 -5
  126. package/bizar-dash/dist/assets/{cmdk-DVxytz9i.js.map → cmdk-C3F6LD6N.js.map} +1 -1
  127. package/bizar-dash/dist/assets/{dnd-kit-DrHErSkw.js → dnd-kit-BAOLODOI.js} +1 -1
  128. package/bizar-dash/dist/assets/{dnd-kit-DrHErSkw.js.map → dnd-kit-BAOLODOI.js.map} +1 -1
  129. package/bizar-dash/dist/assets/{download-Clkq90Ct.js → download-BvJddD8D.js} +1 -1
  130. package/bizar-dash/dist/assets/{download-Clkq90Ct.js.map → download-BvJddD8D.js.map} +1 -1
  131. package/bizar-dash/dist/assets/{external-link-D5tq2o_I.js → external-link-DvL89gaZ.js} +1 -1
  132. package/bizar-dash/dist/assets/{external-link-D5tq2o_I.js.map → external-link-DvL89gaZ.js.map} +1 -1
  133. package/bizar-dash/dist/assets/{file-text-CQ12t7Oc.js → file-text-BOroDtZE.js} +1 -1
  134. package/bizar-dash/dist/assets/{file-text-CQ12t7Oc.js.map → file-text-BOroDtZE.js.map} +1 -1
  135. package/bizar-dash/dist/assets/{filter-BcbE37wP.js → filter-DwPjMRQt.js} +1 -1
  136. package/bizar-dash/dist/assets/{filter-BcbE37wP.js.map → filter-DwPjMRQt.js.map} +1 -1
  137. package/bizar-dash/dist/assets/{git-pull-request-D1qNDRia.js → git-pull-request-TDb1inux.js} +1 -1
  138. package/bizar-dash/dist/assets/{git-pull-request-D1qNDRia.js.map → git-pull-request-TDb1inux.js.map} +1 -1
  139. package/bizar-dash/dist/assets/{index-CASP1n-5.js → index-B1OIS316.js} +1 -1
  140. package/bizar-dash/dist/assets/{index-CASP1n-5.js.map → index-B1OIS316.js.map} +1 -1
  141. package/bizar-dash/dist/assets/{index-C4Vq_2LM.js → index-CR0TeJmc.js} +1 -1
  142. package/bizar-dash/dist/assets/{index-C4Vq_2LM.js.map → index-CR0TeJmc.js.map} +1 -1
  143. package/bizar-dash/dist/assets/{main-CcQfRfIJ.css → main-B7D_IMTh.css} +1 -1
  144. package/bizar-dash/dist/assets/main-BURAlktW.js +277 -0
  145. package/bizar-dash/dist/assets/main-BURAlktW.js.map +1 -0
  146. package/bizar-dash/dist/assets/{network-DJdvVaHx.js → network-BcCoCzIY.js} +1 -1
  147. package/bizar-dash/dist/assets/{network-DJdvVaHx.js.map → network-BcCoCzIY.js.map} +1 -1
  148. package/bizar-dash/dist/assets/{pause-DUn9L5PB.js → pause-BllItT2_.js} +1 -1
  149. package/bizar-dash/dist/assets/{pause-DUn9L5PB.js.map → pause-BllItT2_.js.map} +1 -1
  150. package/bizar-dash/dist/assets/{pencil-DVrBlYV3.js → pencil-DAwM-CC3.js} +1 -1
  151. package/bizar-dash/dist/assets/{pencil-DVrBlYV3.js.map → pencil-DAwM-CC3.js.map} +1 -1
  152. package/bizar-dash/dist/assets/{play-CZjsr3XM.js → play-mJh67AXo.js} +2 -2
  153. package/bizar-dash/dist/assets/{play-CZjsr3XM.js.map → play-mJh67AXo.js.map} +1 -1
  154. package/bizar-dash/dist/assets/{plug-BKuyIe5W.js → plug-BjfntCMC.js} +1 -1
  155. package/bizar-dash/dist/assets/{plug-BKuyIe5W.js.map → plug-BjfntCMC.js.map} +1 -1
  156. package/bizar-dash/dist/assets/{plus-77h62zZm.js → plus-Cm6-fo14.js} +1 -1
  157. package/bizar-dash/dist/assets/{plus-77h62zZm.js.map → plus-Cm6-fo14.js.map} +1 -1
  158. package/bizar-dash/dist/assets/{power-DLmx-gCP.js → power-CXN9y2KV.js} +1 -1
  159. package/bizar-dash/dist/assets/{power-DLmx-gCP.js.map → power-CXN9y2KV.js.map} +1 -1
  160. package/bizar-dash/dist/assets/{power-off-BFKzHXic.js → power-off-DUZqwMf9.js} +1 -1
  161. package/bizar-dash/dist/assets/{power-off-BFKzHXic.js.map → power-off-DUZqwMf9.js.map} +1 -1
  162. package/bizar-dash/dist/assets/{refresh-ccw-DenGBOcq.js → refresh-ccw-DA8_IB9l.js} +1 -1
  163. package/bizar-dash/dist/assets/{refresh-ccw-DenGBOcq.js.map → refresh-ccw-DA8_IB9l.js.map} +1 -1
  164. package/bizar-dash/dist/assets/{refresh-cw-ENBVR8hW.js → refresh-cw-DwlP59en.js} +1 -1
  165. package/bizar-dash/dist/assets/{refresh-cw-ENBVR8hW.js.map → refresh-cw-DwlP59en.js.map} +1 -1
  166. package/bizar-dash/dist/assets/{rotate-ccw-MxryaY7q.js → rotate-ccw-DMTASfAn.js} +1 -1
  167. package/bizar-dash/dist/assets/{rotate-ccw-MxryaY7q.js.map → rotate-ccw-DMTASfAn.js.map} +1 -1
  168. package/bizar-dash/dist/assets/{rotate-cw-CDOlRtUR.js → rotate-cw-BDX5SE2s.js} +1 -1
  169. package/bizar-dash/dist/assets/{rotate-cw-CDOlRtUR.js.map → rotate-cw-BDX5SE2s.js.map} +1 -1
  170. package/bizar-dash/dist/assets/{save-Db1b-1Uq.js → save-Y0jPK5D6.js} +1 -1
  171. package/bizar-dash/dist/assets/{save-Db1b-1Uq.js.map → save-Y0jPK5D6.js.map} +1 -1
  172. package/bizar-dash/dist/assets/{send-yz6HFZM5.js → send-C8wwPHhR.js} +2 -2
  173. package/bizar-dash/dist/assets/{send-yz6HFZM5.js.map → send-C8wwPHhR.js.map} +1 -1
  174. package/bizar-dash/dist/assets/{shield-alert-DY6RAaC8.js → shield-alert-DEvq9UG0.js} +1 -1
  175. package/bizar-dash/dist/assets/{shield-alert-DY6RAaC8.js.map → shield-alert-DEvq9UG0.js.map} +1 -1
  176. package/bizar-dash/dist/assets/{trash-2-Cv7U38jw.js → trash-2-D6qePV_q.js} +2 -2
  177. package/bizar-dash/dist/assets/{trash-2-Cv7U38jw.js.map → trash-2-D6qePV_q.js.map} +1 -1
  178. package/bizar-dash/dist/assets/{trending-up-CRPNagyX.js → trending-up-BZzo_1ic.js} +1 -1
  179. package/bizar-dash/dist/assets/{trending-up-CRPNagyX.js.map → trending-up-BZzo_1ic.js.map} +1 -1
  180. package/bizar-dash/dist/assets/{triangle-alert-tUTXbcEP.js → triangle-alert-B2oHbmoh.js} +1 -1
  181. package/bizar-dash/dist/assets/{triangle-alert-tUTXbcEP.js.map → triangle-alert-B2oHbmoh.js.map} +1 -1
  182. package/bizar-dash/dist/assets/{upload-Cq7uTsrT.js → upload-Dy59aZn6.js} +1 -1
  183. package/bizar-dash/dist/assets/{upload-Cq7uTsrT.js.map → upload-Dy59aZn6.js.map} +1 -1
  184. package/bizar-dash/dist/assets/{zap-5lAQBAPX.js → zap-DENVhJnO.js} +2 -2
  185. package/bizar-dash/dist/assets/{zap-5lAQBAPX.js.map → zap-DENVhJnO.js.map} +1 -1
  186. package/bizar-dash/dist/index.html +3 -3
  187. package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  188. package/bizar-dash/src/web/v8/App.tsx +87 -6
  189. package/bizar-dash/src/web/v8/__tests__/chat-view.test.tsx +4 -4
  190. package/bizar-dash/src/web/v8/shell/AppShell.tsx +38 -2
  191. package/bizar-dash/src/web/v8/shell/Sidebar.tsx +52 -38
  192. package/bizar-dash/src/web/v8/shell/Topbar.tsx +62 -3
  193. package/bizar-dash/src/web/v8/ui/styles/globals.css +40 -0
  194. package/bizar-dash/src/web/v8/ui/theme/ThemeToggle.tsx +11 -6
  195. package/bizar-dash/src/web/v8/views/Admin/AdminView.tsx +32 -5
  196. package/bizar-dash/src/web/v8/views/Agents/AgentsView.tsx +36 -4
  197. package/bizar-dash/src/web/v8/views/Artifacts/ArtifactsView.tsx +1 -1
  198. package/bizar-dash/src/web/v8/views/Chat/ChatView.tsx +13 -1
  199. package/bizar-dash/src/web/v8/views/ClaudeSessions/NewSessionModal.tsx +136 -0
  200. package/bizar-dash/src/web/v8/views/Clipboard/ClipboardView.tsx +1 -1
  201. package/bizar-dash/src/web/v8/views/CommandPalette/AppCommandPalette.tsx +14 -4
  202. package/bizar-dash/src/web/v8/views/Diagnostics/DiagnosticsView.tsx +50 -7
  203. package/bizar-dash/src/web/v8/views/Dialogs/DialogsView.tsx +1 -1
  204. package/bizar-dash/src/web/v8/views/Libraries/LibrariesView.tsx +6 -1
  205. package/bizar-dash/src/web/v8/views/Misc/MiscView.tsx +1 -1
  206. package/bizar-dash/src/web/v8/views/Notifications/NotificationsView.tsx +82 -13
  207. package/bizar-dash/src/web/v8/views/Overview/OverviewView.tsx +19 -0
  208. package/bizar-dash/src/web/v8/views/Projects/ProjectsView.tsx +21 -1
  209. package/bizar-dash/src/web/v8/views/Voice/VoiceView.tsx +1 -1
  210. package/bizar-dash/tests/admin-labels.test.mjs +42 -0
  211. package/bizar-dash/tests/admin-run-grouping.test.mjs +42 -0
  212. package/bizar-dash/tests/agents-preview-truncate.test.mjs +35 -0
  213. package/bizar-dash/tests/agents-summary-sanitize.test.ts +51 -0
  214. package/bizar-dash/tests/chat-new-session.test.mjs +63 -0
  215. package/bizar-dash/tests/empty-state-docs.test.mjs +55 -0
  216. package/bizar-dash/tests/focus-visible.test.mjs +30 -0
  217. package/bizar-dash/tests/kanban-empty-column.test.mjs +56 -0
  218. package/bizar-dash/tests/library-dedupe.test.mjs +62 -0
  219. package/bizar-dash/tests/notifications-dismiss-restore.test.mjs +85 -0
  220. package/bizar-dash/tests/palette-shortcut-hint.test.mjs +58 -0
  221. package/bizar-dash/tests/projects-filter.test.mjs +48 -0
  222. package/bizar-dash/tests/settings-labels.test.mjs +80 -0
  223. package/bizar-dash/tests/sidebar-distinct-icons.test.mjs +28 -0
  224. package/bizar-dash/tests/sidebar-responsive.test.tsx +48 -0
  225. package/bizar-dash/tests/sidebar-section-toggle.test.tsx +63 -0
  226. package/bizar-dash/tests/theme-toggle-icon.test.mjs +28 -0
  227. package/bizar-dash/tests/topbar-design-system.test.mjs +56 -0
  228. package/bizar-dash/vite.config.ts +8 -0
  229. package/package.json +1 -1
  230. package/packages/sdk/package.json +1 -1
  231. package/bizar-dash/dist/assets/AdminView-DNc5Ll7L.js +0 -11
  232. package/bizar-dash/dist/assets/AdminView-DNc5Ll7L.js.map +0 -1
  233. package/bizar-dash/dist/assets/AgentsView-Du-G_skK.js +0 -13
  234. package/bizar-dash/dist/assets/AgentsView-Du-G_skK.js.map +0 -1
  235. package/bizar-dash/dist/assets/ArtifactsView-znhN8GTR.js.map +0 -1
  236. package/bizar-dash/dist/assets/Avatar-B41upSD1.js +0 -6
  237. package/bizar-dash/dist/assets/Avatar-B41upSD1.js.map +0 -1
  238. package/bizar-dash/dist/assets/ChatView-CUriVEgU.js +0 -9
  239. package/bizar-dash/dist/assets/ChatView-CUriVEgU.js.map +0 -1
  240. package/bizar-dash/dist/assets/ClipboardView-DdP6aVso.js +0 -1
  241. package/bizar-dash/dist/assets/ClipboardView-DdP6aVso.js.map +0 -1
  242. package/bizar-dash/dist/assets/DiagnosticsView-88OZ5k0f.js +0 -2
  243. package/bizar-dash/dist/assets/DiagnosticsView-88OZ5k0f.js.map +0 -1
  244. package/bizar-dash/dist/assets/DialogsView-BcqDDCZQ.js +0 -6
  245. package/bizar-dash/dist/assets/DialogsView-BcqDDCZQ.js.map +0 -1
  246. package/bizar-dash/dist/assets/EvalView-CpdFu_ID.js +0 -1
  247. package/bizar-dash/dist/assets/LibrariesView-B1YX9wzp.js +0 -6
  248. package/bizar-dash/dist/assets/MiscView-DLNEGdML.js +0 -1
  249. package/bizar-dash/dist/assets/MiscView-DLNEGdML.js.map +0 -1
  250. package/bizar-dash/dist/assets/NotificationsView-DAm2O0yf.js +0 -6
  251. package/bizar-dash/dist/assets/NotificationsView-DAm2O0yf.js.map +0 -1
  252. package/bizar-dash/dist/assets/ObsidianView-Ci80gnwS.js +0 -1
  253. package/bizar-dash/dist/assets/OverviewView-rriw1GjA.js +0 -1
  254. package/bizar-dash/dist/assets/OverviewView-rriw1GjA.js.map +0 -1
  255. package/bizar-dash/dist/assets/ProjectsView-ChzM12bs.js +0 -11
  256. package/bizar-dash/dist/assets/ProjectsView-ChzM12bs.js.map +0 -1
  257. package/bizar-dash/dist/assets/UpdateView-CEHW2nd6.js +0 -6
  258. package/bizar-dash/dist/assets/UpdateView-CEHW2nd6.js.map +0 -1
  259. package/bizar-dash/dist/assets/VoiceView-CwrPwWgi.js +0 -1
  260. package/bizar-dash/dist/assets/VoiceView-CwrPwWgi.js.map +0 -1
  261. package/bizar-dash/dist/assets/main-Cxeu4LhG.js +0 -244
  262. 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 projects = payload.data?.projects ?? [];
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 recording to get started." />
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
+ });