@polderlabs/bizar 10.2.0 → 10.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/README.md +31 -8
  2. package/bizar-dash/CONTROL_SURFACES.md +0 -1
  3. package/bizar-dash/dist/assets/{ActivityFeed-JjmOY2AQ.js → ActivityFeed-xXiUCZLB.js} +1 -1
  4. package/bizar-dash/dist/assets/{ActivityFeed-JjmOY2AQ.js.map → ActivityFeed-xXiUCZLB.js.map} +1 -1
  5. package/bizar-dash/dist/assets/{ActivityView-BIIMvGWo.js → ActivityView-nOYhCp3h.js} +1 -1
  6. package/bizar-dash/dist/assets/{ActivityView-BIIMvGWo.js.map → ActivityView-nOYhCp3h.js.map} +1 -1
  7. package/bizar-dash/dist/assets/{AdminView-DRidJtkq.js → AdminView-Coi4V3sq.js} +1 -1
  8. package/bizar-dash/dist/assets/{AdminView-DRidJtkq.js.map → AdminView-Coi4V3sq.js.map} +1 -1
  9. package/bizar-dash/dist/assets/{AgentActivity-79l4SBMC.js → AgentActivity-nEri4lbx.js} +1 -1
  10. package/bizar-dash/dist/assets/{AgentActivity-79l4SBMC.js.map → AgentActivity-nEri4lbx.js.map} +1 -1
  11. package/bizar-dash/dist/assets/{AgentsView-0vT4uPSc.js → AgentsView-ByiIJ0ej.js} +3 -3
  12. package/bizar-dash/dist/assets/{AgentsView-0vT4uPSc.js.map → AgentsView-ByiIJ0ej.js.map} +1 -1
  13. package/bizar-dash/dist/assets/{Alert-CG5q_s75.js → Alert-Dg_Vwxh5.js} +1 -1
  14. package/bizar-dash/dist/assets/{Alert-CG5q_s75.js.map → Alert-Dg_Vwxh5.js.map} +1 -1
  15. package/bizar-dash/dist/assets/{ArtifactsView-DM8ZmpR9.js → ArtifactsView-CGclRNnz.js} +1 -1
  16. package/bizar-dash/dist/assets/{ArtifactsView-DM8ZmpR9.js.map → ArtifactsView-CGclRNnz.js.map} +1 -1
  17. package/bizar-dash/dist/assets/{AuthView-BveN94MC.js → AuthView-utVm6M3D.js} +1 -1
  18. package/bizar-dash/dist/assets/{AuthView-BveN94MC.js.map → AuthView-utVm6M3D.js.map} +1 -1
  19. package/bizar-dash/dist/assets/{Avatar-ulUPIYTd.js → Avatar-CXDZdKy4.js} +1 -1
  20. package/bizar-dash/dist/assets/{Avatar-ulUPIYTd.js.map → Avatar-CXDZdKy4.js.map} +1 -1
  21. package/bizar-dash/dist/assets/{BackgroundJobsView-BWErhqFo.js → BackgroundJobsView-j603pz6o.js} +1 -1
  22. package/bizar-dash/dist/assets/{BackgroundJobsView-BWErhqFo.js.map → BackgroundJobsView-j603pz6o.js.map} +1 -1
  23. package/bizar-dash/dist/assets/{BackupView-CnBjzG9N.js → BackupView-DgvnntTE.js} +1 -1
  24. package/bizar-dash/dist/assets/{BackupView-CnBjzG9N.js.map → BackupView-DgvnntTE.js.map} +1 -1
  25. package/bizar-dash/dist/assets/{Card-B0imhxMk.js → Card-D9019JOD.js} +1 -1
  26. package/bizar-dash/dist/assets/{Card-B0imhxMk.js.map → Card-D9019JOD.js.map} +1 -1
  27. package/bizar-dash/dist/assets/{ChatView-D6zGytzx.js → ChatView-C0cTgj5d.js} +1 -1
  28. package/bizar-dash/dist/assets/{ChatView-D6zGytzx.js.map → ChatView-C0cTgj5d.js.map} +1 -1
  29. package/bizar-dash/dist/assets/{Chip-wwZSGwLE.js → Chip-DQtalZ4y.js} +1 -1
  30. package/bizar-dash/dist/assets/{Chip-wwZSGwLE.js.map → Chip-DQtalZ4y.js.map} +1 -1
  31. package/bizar-dash/dist/assets/{ClaudeSessionsView-DK1sOLhi.js → ClaudeSessionsView-H3Bqjo6b.js} +1 -1
  32. package/bizar-dash/dist/assets/{ClaudeSessionsView-DK1sOLhi.js.map → ClaudeSessionsView-H3Bqjo6b.js.map} +1 -1
  33. package/bizar-dash/dist/assets/{ClipboardView-CZ2lYPr0.js → ClipboardView-BAguaKbx.js} +1 -1
  34. package/bizar-dash/dist/assets/{ClipboardView-CZ2lYPr0.js.map → ClipboardView-BAguaKbx.js.map} +1 -1
  35. package/bizar-dash/dist/assets/{ConfigView-D-zT9eNA.js → ConfigView-DWHJgoe8.js} +1 -1
  36. package/bizar-dash/dist/assets/{ConfigView-D-zT9eNA.js.map → ConfigView-DWHJgoe8.js.map} +1 -1
  37. package/bizar-dash/dist/assets/{DiagnosticsView-BocV68Lb.js → DiagnosticsView-oCT624hk.js} +1 -1
  38. package/bizar-dash/dist/assets/{DiagnosticsView-BocV68Lb.js.map → DiagnosticsView-oCT624hk.js.map} +1 -1
  39. package/bizar-dash/dist/assets/{DialogsView-BVjPI5x7.js → DialogsView-CqHNals_.js} +1 -1
  40. package/bizar-dash/dist/assets/{DialogsView-BVjPI5x7.js.map → DialogsView-CqHNals_.js.map} +1 -1
  41. package/bizar-dash/dist/assets/{DoctorView-84gJcZwM.js → DoctorView-6kfQq_Mx.js} +1 -1
  42. package/bizar-dash/dist/assets/{DoctorView-84gJcZwM.js.map → DoctorView-6kfQq_Mx.js.map} +1 -1
  43. package/bizar-dash/dist/assets/{EmptyState-BjgVOrcO.js → EmptyState-DLmtBX_6.js} +1 -1
  44. package/bizar-dash/dist/assets/{EmptyState-BjgVOrcO.js.map → EmptyState-DLmtBX_6.js.map} +1 -1
  45. package/bizar-dash/dist/assets/{EnvVarsView-BJ2TEzF7.js → EnvVarsView-DySOXlP1.js} +2 -2
  46. package/bizar-dash/dist/assets/{EnvVarsView-BJ2TEzF7.js.map → EnvVarsView-DySOXlP1.js.map} +1 -1
  47. package/bizar-dash/dist/assets/{ErrorState-BE3WBFqF.js → ErrorState-Bump8diE.js} +1 -1
  48. package/bizar-dash/dist/assets/{ErrorState-BE3WBFqF.js.map → ErrorState-Bump8diE.js.map} +1 -1
  49. package/bizar-dash/dist/assets/{EvalView-BJJ2P-kK.js → EvalView-FnZ5d93z.js} +1 -1
  50. package/bizar-dash/dist/assets/{EvalView-BJJ2P-kK.js.map → EvalView-FnZ5d93z.js.map} +1 -1
  51. package/bizar-dash/dist/assets/{GoalsView-pwROHApi.js → GoalsView-C4m1WnVv.js} +1 -1
  52. package/bizar-dash/dist/assets/{GoalsView-pwROHApi.js.map → GoalsView-C4m1WnVv.js.map} +1 -1
  53. package/bizar-dash/dist/assets/{Grid-qcvbhx6e.js → Grid-dEnoGGws.js} +1 -1
  54. package/bizar-dash/dist/assets/{Grid-qcvbhx6e.js.map → Grid-dEnoGGws.js.map} +1 -1
  55. package/bizar-dash/dist/assets/{HistoryView-C7SSOLJA.js → HistoryView-xdRybjZ2.js} +1 -1
  56. package/bizar-dash/dist/assets/{HistoryView-C7SSOLJA.js.map → HistoryView-xdRybjZ2.js.map} +1 -1
  57. package/bizar-dash/dist/assets/{Input-Bgzxf_Hu.js → Input-BSa3lh_-.js} +1 -1
  58. package/bizar-dash/dist/assets/{Input-Bgzxf_Hu.js.map → Input-BSa3lh_-.js.map} +1 -1
  59. package/bizar-dash/dist/assets/{LibrariesView-CbFFksox.js → LibrariesView-1zR04Eci.js} +1 -1
  60. package/bizar-dash/dist/assets/{LibrariesView-CbFFksox.js.map → LibrariesView-1zR04Eci.js.map} +1 -1
  61. package/bizar-dash/dist/assets/{LightRAGView-BqDrJbRq.js → LightRAGView-PubmA9oL.js} +1 -1
  62. package/bizar-dash/dist/assets/{LightRAGView-BqDrJbRq.js.map → LightRAGView-PubmA9oL.js.map} +1 -1
  63. package/bizar-dash/dist/assets/{ListHeader-TjPJrUfY.js → ListHeader-Ak9WJd39.js} +1 -1
  64. package/bizar-dash/dist/assets/{ListHeader-TjPJrUfY.js.map → ListHeader-Ak9WJd39.js.map} +1 -1
  65. package/bizar-dash/dist/assets/{MemoryView-BlTeHLhX.js → MemoryView-DBGpuETw.js} +1 -1
  66. package/bizar-dash/dist/assets/{MemoryView-BlTeHLhX.js.map → MemoryView-DBGpuETw.js.map} +1 -1
  67. package/bizar-dash/dist/assets/MiscView-DufqBHkX.js +1 -0
  68. package/bizar-dash/dist/assets/{MiscView-B8QY82Pm.js.map → MiscView-DufqBHkX.js.map} +1 -1
  69. package/bizar-dash/dist/assets/{ModsView-CvW1OWts.js → ModsView-DcX6eVU2.js} +2 -2
  70. package/bizar-dash/dist/assets/{ModsView-CvW1OWts.js.map → ModsView-DcX6eVU2.js.map} +1 -1
  71. package/bizar-dash/dist/assets/{NotificationsView-CsBt5U7x.js → NotificationsView-B8IhHRQC.js} +1 -1
  72. package/bizar-dash/dist/assets/{NotificationsView-CsBt5U7x.js.map → NotificationsView-B8IhHRQC.js.map} +1 -1
  73. package/bizar-dash/dist/assets/ObsidianView-DEuk65zS.js +1 -0
  74. package/bizar-dash/dist/assets/{ObsidianView-BCTejEj2.js.map → ObsidianView-DEuk65zS.js.map} +1 -1
  75. package/bizar-dash/dist/assets/{OverviewView-BsuPxwnB.js → OverviewView-CTI2spTd.js} +1 -1
  76. package/bizar-dash/dist/assets/{OverviewView-BsuPxwnB.js.map → OverviewView-CTI2spTd.js.map} +1 -1
  77. package/bizar-dash/dist/assets/{ProgressBar-DlZTETEn.js → ProgressBar-D3lkz42E.js} +1 -1
  78. package/bizar-dash/dist/assets/{ProgressBar-DlZTETEn.js.map → ProgressBar-D3lkz42E.js.map} +1 -1
  79. package/bizar-dash/dist/assets/{ProjectsView-Soxtmpet.js → ProjectsView-Du0LzE7A.js} +1 -1
  80. package/bizar-dash/dist/assets/{ProjectsView-Soxtmpet.js.map → ProjectsView-Du0LzE7A.js.map} +1 -1
  81. package/bizar-dash/dist/assets/{ProvidersView-DaCEV9pM.js → ProvidersView-5bCgXwBo.js} +1 -1
  82. package/bizar-dash/dist/assets/{ProvidersView-DaCEV9pM.js.map → ProvidersView-5bCgXwBo.js.map} +1 -1
  83. package/bizar-dash/dist/assets/{SchedulesView-DY2ByBj_.js → SchedulesView-DFHdIz5P.js} +1 -1
  84. package/bizar-dash/dist/assets/{SchedulesView-DY2ByBj_.js.map → SchedulesView-DFHdIz5P.js.map} +1 -1
  85. package/bizar-dash/dist/assets/{Select-Dbd88_n8.js → Select-C8EJj4NM.js} +1 -1
  86. package/bizar-dash/dist/assets/{Select-Dbd88_n8.js.map → Select-C8EJj4NM.js.map} +1 -1
  87. package/bizar-dash/dist/assets/{SettingsRow-CLMNd434.js → SettingsRow-BdOOmYMD.js} +1 -1
  88. package/bizar-dash/dist/assets/{SettingsRow-CLMNd434.js.map → SettingsRow-BdOOmYMD.js.map} +1 -1
  89. package/bizar-dash/dist/assets/{SettingsView-b24oFq9x.js → SettingsView-CqwQ6Knv.js} +1 -1
  90. package/bizar-dash/dist/assets/{SettingsView-b24oFq9x.js.map → SettingsView-CqwQ6Knv.js.map} +1 -1
  91. package/bizar-dash/dist/assets/{Sheet-DnDid58j.js → Sheet-Cztsvgse.js} +1 -1
  92. package/bizar-dash/dist/assets/{Sheet-DnDid58j.js.map → Sheet-Cztsvgse.js.map} +1 -1
  93. package/bizar-dash/dist/assets/{Slider-BJCvKI1I.js → Slider-BexNfF6K.js} +1 -1
  94. package/bizar-dash/dist/assets/{Slider-BJCvKI1I.js.map → Slider-BexNfF6K.js.map} +1 -1
  95. package/bizar-dash/dist/assets/{Sparkline-ktqQLdyb.js → Sparkline-CLZBtvsN.js} +1 -1
  96. package/bizar-dash/dist/assets/{Sparkline-ktqQLdyb.js.map → Sparkline-CLZBtvsN.js.map} +1 -1
  97. package/bizar-dash/dist/assets/{Switch-CwXfsDii.js → Switch-B1tiRwNK.js} +1 -1
  98. package/bizar-dash/dist/assets/{Switch-CwXfsDii.js.map → Switch-B1tiRwNK.js.map} +1 -1
  99. package/bizar-dash/dist/assets/{TasksView-BHmpzFCf.js → TasksView-CT0fPlos.js} +1 -1
  100. package/bizar-dash/dist/assets/{TasksView-BHmpzFCf.js.map → TasksView-CT0fPlos.js.map} +1 -1
  101. package/bizar-dash/dist/assets/{Textarea-BzIAwSlp.js → Textarea-D_TPpVMy.js} +1 -1
  102. package/bizar-dash/dist/assets/{Textarea-BzIAwSlp.js.map → Textarea-D_TPpVMy.js.map} +1 -1
  103. package/bizar-dash/dist/assets/{UpdateView-CSmX9NN0.js → UpdateView-tD2q2OhW.js} +1 -1
  104. package/bizar-dash/dist/assets/{UpdateView-CSmX9NN0.js.map → UpdateView-tD2q2OhW.js.map} +1 -1
  105. package/bizar-dash/dist/assets/{UsageView-COyr_Gfi.js → UsageView-B4BuAFKA.js} +1 -1
  106. package/bizar-dash/dist/assets/{UsageView-COyr_Gfi.js.map → UsageView-B4BuAFKA.js.map} +1 -1
  107. package/bizar-dash/dist/assets/{ViewHeader-BzYZ3m-e.js → ViewHeader-V_SzbUpw.js} +1 -1
  108. package/bizar-dash/dist/assets/{ViewHeader-BzYZ3m-e.js.map → ViewHeader-V_SzbUpw.js.map} +1 -1
  109. package/bizar-dash/dist/assets/{VoiceView-D33_xG5t.js → VoiceView-D3vb5OM2.js} +1 -1
  110. package/bizar-dash/dist/assets/{VoiceView-D33_xG5t.js.map → VoiceView-D3vb5OM2.js.map} +1 -1
  111. package/bizar-dash/dist/assets/{brain-CM-Vls8r.js → brain-BmfWcitQ.js} +1 -1
  112. package/bizar-dash/dist/assets/{brain-CM-Vls8r.js.map → brain-BmfWcitQ.js.map} +1 -1
  113. package/bizar-dash/dist/assets/{calendar-C0lgZk69.js → calendar-VntJUzFB.js} +1 -1
  114. package/bizar-dash/dist/assets/{calendar-C0lgZk69.js.map → calendar-VntJUzFB.js.map} +1 -1
  115. package/bizar-dash/dist/assets/{circle-Bx_hQEBC.js → circle-KrQQySeK.js} +1 -1
  116. package/bizar-dash/dist/assets/{circle-Bx_hQEBC.js.map → circle-KrQQySeK.js.map} +1 -1
  117. package/bizar-dash/dist/assets/{circle-alert-DHvsqpw5.js → circle-alert-CQ9MLygI.js} +1 -1
  118. package/bizar-dash/dist/assets/{circle-alert-DHvsqpw5.js.map → circle-alert-CQ9MLygI.js.map} +1 -1
  119. package/bizar-dash/dist/assets/{circle-check-Bx8wmXR9.js → circle-check-COtVgsWw.js} +1 -1
  120. package/bizar-dash/dist/assets/{circle-check-Bx8wmXR9.js.map → circle-check-COtVgsWw.js.map} +1 -1
  121. package/bizar-dash/dist/assets/{clock-CejGoecX.js → clock-C0GlM4qp.js} +1 -1
  122. package/bizar-dash/dist/assets/{clock-CejGoecX.js.map → clock-C0GlM4qp.js.map} +1 -1
  123. package/bizar-dash/dist/assets/{download-4cEUWue1.js → download-CW4DmtRe.js} +1 -1
  124. package/bizar-dash/dist/assets/{download-4cEUWue1.js.map → download-CW4DmtRe.js.map} +1 -1
  125. package/bizar-dash/dist/assets/{external-link-0NqUhI4i.js → external-link-CAOnh_DH.js} +1 -1
  126. package/bizar-dash/dist/assets/{external-link-0NqUhI4i.js.map → external-link-CAOnh_DH.js.map} +1 -1
  127. package/bizar-dash/dist/assets/{file-text-yEMkrSoZ.js → file-text-CS1jB_oV.js} +1 -1
  128. package/bizar-dash/dist/assets/{file-text-yEMkrSoZ.js.map → file-text-CS1jB_oV.js.map} +1 -1
  129. package/bizar-dash/dist/assets/{filter-BXt60zAS.js → filter-DyQbml3L.js} +1 -1
  130. package/bizar-dash/dist/assets/{filter-BXt60zAS.js.map → filter-DyQbml3L.js.map} +1 -1
  131. package/bizar-dash/dist/assets/{git-pull-request-BjG5lNtR.js → git-pull-request-Co6gYfba.js} +1 -1
  132. package/bizar-dash/dist/assets/{git-pull-request-BjG5lNtR.js.map → git-pull-request-Co6gYfba.js.map} +1 -1
  133. package/bizar-dash/dist/assets/{main-CokHAXWM.js → main-CyAIirBB.js} +28 -33
  134. package/bizar-dash/dist/assets/main-CyAIirBB.js.map +1 -0
  135. package/bizar-dash/dist/assets/network-CDn43Gsj.js +6 -0
  136. package/bizar-dash/dist/assets/network-CDn43Gsj.js.map +1 -0
  137. package/bizar-dash/dist/assets/{pause-CAmulU0F.js → pause-CKYjSTGX.js} +1 -1
  138. package/bizar-dash/dist/assets/{pause-CAmulU0F.js.map → pause-CKYjSTGX.js.map} +1 -1
  139. package/bizar-dash/dist/assets/{pencil-eq5IqBmU.js → pencil-BUJ2DpWs.js} +1 -1
  140. package/bizar-dash/dist/assets/{pencil-eq5IqBmU.js.map → pencil-BUJ2DpWs.js.map} +1 -1
  141. package/bizar-dash/dist/assets/{play-BoLrrcBA.js → play-CGxZWqtW.js} +1 -1
  142. package/bizar-dash/dist/assets/{play-BoLrrcBA.js.map → play-CGxZWqtW.js.map} +1 -1
  143. package/bizar-dash/dist/assets/{plug-DBrVM9lK.js → plug-CQYGgGSy.js} +1 -1
  144. package/bizar-dash/dist/assets/{plug-DBrVM9lK.js.map → plug-CQYGgGSy.js.map} +1 -1
  145. package/bizar-dash/dist/assets/{plus-DENm0hqV.js → plus-fFxt-8z5.js} +1 -1
  146. package/bizar-dash/dist/assets/{plus-DENm0hqV.js.map → plus-fFxt-8z5.js.map} +1 -1
  147. package/bizar-dash/dist/assets/{power-DLjXF7zE.js → power-Cjgy2KsI.js} +1 -1
  148. package/bizar-dash/dist/assets/{power-DLjXF7zE.js.map → power-Cjgy2KsI.js.map} +1 -1
  149. package/bizar-dash/dist/assets/{power-off-D47cOnG_.js → power-off-B81hLRah.js} +1 -1
  150. package/bizar-dash/dist/assets/{power-off-D47cOnG_.js.map → power-off-B81hLRah.js.map} +1 -1
  151. package/bizar-dash/dist/assets/{refresh-ccw-DNrXX9gB.js → refresh-ccw-BCXx86xp.js} +1 -1
  152. package/bizar-dash/dist/assets/{refresh-ccw-DNrXX9gB.js.map → refresh-ccw-BCXx86xp.js.map} +1 -1
  153. package/bizar-dash/dist/assets/{refresh-cw-CoAHDpeS.js → refresh-cw-COTg1nZo.js} +1 -1
  154. package/bizar-dash/dist/assets/{refresh-cw-CoAHDpeS.js.map → refresh-cw-COTg1nZo.js.map} +1 -1
  155. package/bizar-dash/dist/assets/{rotate-ccw-ObX5awaG.js → rotate-ccw-BZrEVc0Z.js} +1 -1
  156. package/bizar-dash/dist/assets/{rotate-ccw-ObX5awaG.js.map → rotate-ccw-BZrEVc0Z.js.map} +1 -1
  157. package/bizar-dash/dist/assets/{rotate-cw-xpPJw9LP.js → rotate-cw-CsTtaLV2.js} +1 -1
  158. package/bizar-dash/dist/assets/{rotate-cw-xpPJw9LP.js.map → rotate-cw-CsTtaLV2.js.map} +1 -1
  159. package/bizar-dash/dist/assets/{save-D1iKiJJl.js → save-DtMTge1F.js} +1 -1
  160. package/bizar-dash/dist/assets/{save-D1iKiJJl.js.map → save-DtMTge1F.js.map} +1 -1
  161. package/bizar-dash/dist/assets/{send-BRcAhZUm.js → send-DyBhyk8t.js} +1 -1
  162. package/bizar-dash/dist/assets/{send-BRcAhZUm.js.map → send-DyBhyk8t.js.map} +1 -1
  163. package/bizar-dash/dist/assets/{shield-alert-C6vEcK-g.js → shield-alert-BPGJlRF8.js} +1 -1
  164. package/bizar-dash/dist/assets/{shield-alert-C6vEcK-g.js.map → shield-alert-BPGJlRF8.js.map} +1 -1
  165. package/bizar-dash/dist/assets/{trash-2-DZDjEnNh.js → trash-2-2NsyTU8c.js} +1 -1
  166. package/bizar-dash/dist/assets/{trash-2-DZDjEnNh.js.map → trash-2-2NsyTU8c.js.map} +1 -1
  167. package/bizar-dash/dist/assets/{trending-up-DMj-AJVf.js → trending-up-BXwP23VK.js} +1 -1
  168. package/bizar-dash/dist/assets/{trending-up-DMj-AJVf.js.map → trending-up-BXwP23VK.js.map} +1 -1
  169. package/bizar-dash/dist/assets/{triangle-alert-_2xe_tl4.js → triangle-alert-DFmQ_RXN.js} +1 -1
  170. package/bizar-dash/dist/assets/{triangle-alert-_2xe_tl4.js.map → triangle-alert-DFmQ_RXN.js.map} +1 -1
  171. package/bizar-dash/dist/assets/{upload-DdDljAeV.js → upload-Cj-HYRYV.js} +1 -1
  172. package/bizar-dash/dist/assets/{upload-DdDljAeV.js.map → upload-Cj-HYRYV.js.map} +1 -1
  173. package/bizar-dash/dist/assets/{zap-mHsgh0FP.js → zap-BaOiybcl.js} +1 -1
  174. package/bizar-dash/dist/assets/{zap-mHsgh0FP.js.map → zap-BaOiybcl.js.map} +1 -1
  175. package/bizar-dash/dist/index.html +1 -1
  176. package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  177. package/bizar-dash/src/server/routes/update.mjs +48 -21
  178. package/bizar-dash/src/web/v8/App.tsx +0 -4
  179. package/bizar-dash/src/web/v8/__tests__/new-views.test.tsx +0 -17
  180. package/bizar-dash/src/web/v8/shell/Sidebar.tsx +0 -1
  181. package/bizar-dash/src/web/v8/views/Router.tsx +0 -5
  182. package/bizar-dash/tests/_update-test-helpers.mjs +13 -0
  183. package/bizar-dash/tests/update-check.test.mjs +18 -13
  184. package/bizar-dash/tests/update-packages.test.mjs +154 -0
  185. package/bizar-dash/tests/update-run.test.mjs +17 -80
  186. package/cli/banner.mjs +1 -1
  187. package/cli/bin.mjs +8 -1
  188. package/cli/bootstrap.mjs +1 -1
  189. package/cli/commands/claude-cmd.mjs +1 -1
  190. package/cli/commands/deploy/docker.mjs +0 -3
  191. package/cli/install/banner.mjs +67 -0
  192. package/cli/install/banner.test.mjs +39 -0
  193. package/cli/install/index.mjs +32 -0
  194. package/cli/install/index.test.mjs +61 -0
  195. package/cli/install/paths.mjs +80 -0
  196. package/cli/install/paths.test.mjs +77 -0
  197. package/cli/install/plugin.mjs +161 -0
  198. package/cli/install/postinstall.mjs +111 -0
  199. package/cli/install.mjs +13 -600
  200. package/cli/migrate.mjs +157 -0
  201. package/cli/migrate.test.mjs +85 -0
  202. package/cli/provision.mjs +615 -1918
  203. package/cli/provision.test.mjs +1 -1
  204. package/cli/service-controller.test.mjs +0 -1
  205. package/cli/service-env.mjs +0 -1
  206. package/cli/utils.mjs +0 -1
  207. package/install.sh +54 -466
  208. package/package.json +1 -1
  209. package/packages/sdk/package.json +1 -1
  210. package/scripts/install-curl.sh +17 -0
  211. package/bizar-dash/dist/assets/HeadroomView-D1UiTYbt.js +0 -11
  212. package/bizar-dash/dist/assets/HeadroomView-D1UiTYbt.js.map +0 -1
  213. package/bizar-dash/dist/assets/MiscView-B8QY82Pm.js +0 -1
  214. package/bizar-dash/dist/assets/ObsidianView-BCTejEj2.js +0 -1
  215. package/bizar-dash/dist/assets/main-CokHAXWM.js.map +0 -1
  216. package/bizar-dash/skills/headroom/SKILL.md +0 -94
  217. package/bizar-dash/src/server/headroom.mjs +0 -649
  218. package/bizar-dash/src/server/routes/headroom.mjs +0 -126
  219. package/bizar-dash/src/web/v8/views/Headroom/HeadroomView.tsx +0 -201
  220. package/bizar-dash/tests/graphify-mod-spawn.node.test.mjs +0 -90
  221. package/bizar-dash/tests/headroom-install.test.mjs +0 -173
  222. package/bizar-dash/tests/headroom-settings.test.mjs +0 -126
  223. package/bizar-dash/tests/headroom-status.test.mjs +0 -117
  224. package/bizar-dash/tests/v7-archive/mobile-overview.test.tsx +0 -214
  225. package/bizar-dash/tests/v7-archive/mobile-settings.test.tsx +0 -181
  226. package/bizar-dash/tests/v7-archive/navigation/CommandPalette.test.tsx +0 -105
  227. package/bizar-dash/tests/v7-archive/settings-nav.test.tsx +0 -128
  228. package/bizar-dash/tests/v7-archive/views/Overview.test.tsx +0 -413
  229. package/cli/commands/headroom.mjs +0 -204
  230. package/cli/provision-claude.mjs +0 -1176
@@ -1 +1 @@
1
- {"version":3,"file":"NotificationsView-CsBt5U7x.js","sources":["../../../node_modules/lucide-react/dist/esm/icons/check-check.js","../../src/web/v8/views/Notifications/NotificationsView.tsx"],"sourcesContent":["/**\n * @license lucide-react v0.460.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst CheckCheck = createLucideIcon(\"CheckCheck\", [\n [\"path\", { d: \"M18 6 7 17l-5-5\", key: \"116fxf\" }],\n [\"path\", { d: \"m22 10-7.5 7.5L13 16\", key: \"ke71qq\" }]\n]);\n\nexport { CheckCheck as default };\n//# sourceMappingURL=check-check.js.map\n","import { useCallback, useEffect, useState } from 'react';\nimport { Bell, CheckCheck, Trash2, RefreshCw } from 'lucide-react';\nimport { Stack } from '../../ui/primitives/Stack.js';\nimport { Inline } from '../../ui/primitives/Inline.js';\nimport { ViewHeader } from '../../ui/data/ViewHeader.js';\nimport { Card, CardBody } from '../../ui/data/Card.js';\nimport { Badge } from '../../ui/data/Badge.js';\nimport { Button } from '../../ui/controls/Button.js';\nimport { Skeleton } from '../../ui/feedback/Skeleton.js';\nimport { EmptyState } from '../../ui/feedback/EmptyState.js';\nimport { useFetch } from '../../data/useFetch.js';\nimport { useWsMessage } from '../../data/useWebSocket.js';\nimport { fetchJson, FetchError } from '../../data/fetcher.js';\nimport type { WsMessage } from '../../data/types.js';\n\n/**\n * NotificationsView — Sprint S35. Per-user notification stream backed\n * by `/api/notifications`. Pulls unread + read mixed, supports\n * mark-one-read, mark-all-read, and dismiss.\n */\n\ntype Tone = 'neutral' | 'info' | 'success' | 'warning' | 'danger';\n\ninterface NotificationItem {\n id: string;\n title?: string;\n body?: string;\n source?: string;\n tone?: Tone;\n read?: boolean;\n createdAt?: string;\n}\n\ninterface NotificationsResponse {\n notifications: NotificationItem[];\n stats?: { total: number; unread: number };\n}\n\nfunction toneFor(item: NotificationItem): Tone {\n if (item.tone && ['info', 'success', 'warning', 'danger', 'neutral'].includes(item.tone)) {\n return item.tone as Tone;\n }\n return 'neutral';\n}\n\nfunction fmtRel(iso: string | undefined): string {\n if (!iso) return '';\n const t = Date.parse(iso);\n if (Number.isNaN(t)) return '';\n const delta = Math.max(0, Date.now() - t);\n if (delta < 60_000) return 'just now';\n if (delta < 3_600_000) return `${Math.round(delta / 60_000)}m ago`;\n if (delta < 86_400_000) return `${Math.round(delta / 3_600_000)}h ago`;\n return new Date(t).toLocaleString();\n}\n\nexport function NotificationsView(): JSX.Element {\n const res = useFetch<NotificationsResponse>('/api/notifications?limit=200');\n const [items, setItems] = useState<NotificationItem[]>([]);\n const [busy, setBusy] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n if (res.data?.notifications) setItems(res.data.notifications);\n }, [res.data]);\n\n const onChange = useCallback((msg: WsMessage) => {\n if (msg.type === 'notifications:change') res.refetch();\n }, [res]);\n useWsMessage('notifications:change', onChange);\n\n const markRead = async (id: string): Promise<void> => {\n setBusy(id);\n setError(null);\n try {\n await fetchJson(`/api/notifications/${encodeURIComponent(id)}/read`, { method: 'POST' });\n setItems((prev) => prev.map((n) => (n.id === id ? { ...n, read: true } : n)));\n } catch (err) {\n setError(err instanceof FetchError ? err.message : (err as Error).message);\n } finally {\n setBusy(null);\n }\n };\n\n const markAll = async (): Promise<void> => {\n setBusy('all');\n setError(null);\n try {\n await fetchJson('/api/notifications/read-all', { method: 'POST' });\n setItems((prev) => prev.map((n) => ({ ...n, read: true })));\n } catch (err) {\n setError(err instanceof FetchError ? err.message : (err as Error).message);\n } finally {\n setBusy(null);\n }\n };\n\n const dismiss = async (id: string): Promise<void> => {\n setBusy(id);\n setError(null);\n try {\n await fetchJson(`/api/notifications/${encodeURIComponent(id)}`, { method: 'DELETE' });\n setItems((prev) => prev.filter((n) => n.id !== id));\n } catch (err) {\n setError(err instanceof FetchError ? err.message : (err as Error).message);\n } finally {\n setBusy(null);\n }\n };\n\n const unread = items.filter((n) => !n.read).length;\n\n return (\n <Stack gap={5} data-testid=\"notifications-view\">\n <ViewHeader\n title=\"Notifications\"\n description={`${unread} unread of ${items.length} total. Backed by ~/.config/bizar/notifications.jsonl.`}\n actions={\n <Inline gap={2} align=\"center\">\n {error !== null && <span role=\"alert\" style={{ color: 'var(--danger)', fontSize: 'var(--fs-12)' }}>{error}</span>}\n <Button variant=\"ghost\" onClick={() => void res.refetch()} data-testid=\"notifications-refresh\">\n <RefreshCw size={14} aria-hidden /> Refresh\n </Button>\n <Button variant=\"secondary\" onClick={() => void markAll()} disabled={busy === 'all' || unread === 0} data-testid=\"notifications-mark-all\">\n <CheckCheck size={14} aria-hidden /> Mark all read\n </Button>\n </Inline>\n }\n />\n {res.loading && items.length === 0 ? (\n <Stack gap={2}>\n <Skeleton style={{ height: 64 }} />\n <Skeleton style={{ height: 64 }} />\n <Skeleton style={{ height: 64 }} />\n </Stack>\n ) : items.length === 0 ? (\n <EmptyState\n icon={<Bell size={32} aria-hidden />}\n title=\"No notifications\"\n description=\"Agent runs, task moves, and CI results will land here.\"\n />\n ) : (\n <Stack gap={2}>\n {items.map((n) => {\n const tone = toneFor(n);\n return (\n <Card key={n.id} variant=\"default\" style={{ opacity: n.read ? 0.7 : 1 }}>\n <CardBody>\n <Inline align=\"start\" justify=\"between\" gap={3}>\n <Stack gap={1} style={{ flex: 1, minWidth: 0 }}>\n <Inline align=\"center\" gap={2}>\n {!n.read && <span aria-label=\"Unread\" style={{ width: 8, height: 8, borderRadius: 'var(--radius-pill)', background: 'var(--accent)' }} />}\n <strong style={{ fontSize: 'var(--fs-13)' }}>{n.title || n.source || n.id}</strong>\n {n.source && <Badge tone={tone}>{n.source}</Badge>}\n <span style={{ color: 'var(--fg-muted)', fontSize: 'var(--fs-12)' }}>{fmtRel(n.createdAt)}</span>\n </Inline>\n {n.body && <span style={{ fontSize: 'var(--fs-13)', color: 'var(--fg-muted)' }}>{n.body}</span>}\n </Stack>\n <Inline gap={1}>\n {!n.read && (\n <Button variant=\"ghost\" onClick={() => void markRead(n.id)} disabled={busy === n.id} data-testid={`notification-read-${n.id}`}>\n Mark read\n </Button>\n )}\n <Button variant=\"ghost\" onClick={() => void dismiss(n.id)} disabled={busy === n.id} data-testid={`notification-dismiss-${n.id}`}>\n <Trash2 size={12} aria-hidden /> Dismiss\n </Button>\n </Inline>\n </Inline>\n </CardBody>\n </Card>\n );\n })}\n </Stack>\n )}\n </Stack>\n );\n}"],"names":["CheckCheck","createLucideIcon","toneFor","item","fmtRel","iso","t","delta","NotificationsView","res","useFetch","items","setItems","useState","busy","setBusy","error","setError","useEffect","_a","onChange","useCallback","msg","useWsMessage","markRead","id","fetchJson","prev","n","err","FetchError","markAll","dismiss","unread","jsxs","Stack","jsx","ViewHeader","Inline","Button","RefreshCw","Skeleton","EmptyState","Bell","tone","Card","CardBody","Badge","Trash2"],"mappings":"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","x_google_ignoreList":[0]}
1
+ {"version":3,"file":"NotificationsView-B8IhHRQC.js","sources":["../../../node_modules/lucide-react/dist/esm/icons/check-check.js","../../src/web/v8/views/Notifications/NotificationsView.tsx"],"sourcesContent":["/**\n * @license lucide-react v0.460.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst CheckCheck = createLucideIcon(\"CheckCheck\", [\n [\"path\", { d: \"M18 6 7 17l-5-5\", key: \"116fxf\" }],\n [\"path\", { d: \"m22 10-7.5 7.5L13 16\", key: \"ke71qq\" }]\n]);\n\nexport { CheckCheck as default };\n//# sourceMappingURL=check-check.js.map\n","import { useCallback, useEffect, useState } from 'react';\nimport { Bell, CheckCheck, Trash2, RefreshCw } from 'lucide-react';\nimport { Stack } from '../../ui/primitives/Stack.js';\nimport { Inline } from '../../ui/primitives/Inline.js';\nimport { ViewHeader } from '../../ui/data/ViewHeader.js';\nimport { Card, CardBody } from '../../ui/data/Card.js';\nimport { Badge } from '../../ui/data/Badge.js';\nimport { Button } from '../../ui/controls/Button.js';\nimport { Skeleton } from '../../ui/feedback/Skeleton.js';\nimport { EmptyState } from '../../ui/feedback/EmptyState.js';\nimport { useFetch } from '../../data/useFetch.js';\nimport { useWsMessage } from '../../data/useWebSocket.js';\nimport { fetchJson, FetchError } from '../../data/fetcher.js';\nimport type { WsMessage } from '../../data/types.js';\n\n/**\n * NotificationsView — Sprint S35. Per-user notification stream backed\n * by `/api/notifications`. Pulls unread + read mixed, supports\n * mark-one-read, mark-all-read, and dismiss.\n */\n\ntype Tone = 'neutral' | 'info' | 'success' | 'warning' | 'danger';\n\ninterface NotificationItem {\n id: string;\n title?: string;\n body?: string;\n source?: string;\n tone?: Tone;\n read?: boolean;\n createdAt?: string;\n}\n\ninterface NotificationsResponse {\n notifications: NotificationItem[];\n stats?: { total: number; unread: number };\n}\n\nfunction toneFor(item: NotificationItem): Tone {\n if (item.tone && ['info', 'success', 'warning', 'danger', 'neutral'].includes(item.tone)) {\n return item.tone as Tone;\n }\n return 'neutral';\n}\n\nfunction fmtRel(iso: string | undefined): string {\n if (!iso) return '';\n const t = Date.parse(iso);\n if (Number.isNaN(t)) return '';\n const delta = Math.max(0, Date.now() - t);\n if (delta < 60_000) return 'just now';\n if (delta < 3_600_000) return `${Math.round(delta / 60_000)}m ago`;\n if (delta < 86_400_000) return `${Math.round(delta / 3_600_000)}h ago`;\n return new Date(t).toLocaleString();\n}\n\nexport function NotificationsView(): JSX.Element {\n const res = useFetch<NotificationsResponse>('/api/notifications?limit=200');\n const [items, setItems] = useState<NotificationItem[]>([]);\n const [busy, setBusy] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n if (res.data?.notifications) setItems(res.data.notifications);\n }, [res.data]);\n\n const onChange = useCallback((msg: WsMessage) => {\n if (msg.type === 'notifications:change') res.refetch();\n }, [res]);\n useWsMessage('notifications:change', onChange);\n\n const markRead = async (id: string): Promise<void> => {\n setBusy(id);\n setError(null);\n try {\n await fetchJson(`/api/notifications/${encodeURIComponent(id)}/read`, { method: 'POST' });\n setItems((prev) => prev.map((n) => (n.id === id ? { ...n, read: true } : n)));\n } catch (err) {\n setError(err instanceof FetchError ? err.message : (err as Error).message);\n } finally {\n setBusy(null);\n }\n };\n\n const markAll = async (): Promise<void> => {\n setBusy('all');\n setError(null);\n try {\n await fetchJson('/api/notifications/read-all', { method: 'POST' });\n setItems((prev) => prev.map((n) => ({ ...n, read: true })));\n } catch (err) {\n setError(err instanceof FetchError ? err.message : (err as Error).message);\n } finally {\n setBusy(null);\n }\n };\n\n const dismiss = async (id: string): Promise<void> => {\n setBusy(id);\n setError(null);\n try {\n await fetchJson(`/api/notifications/${encodeURIComponent(id)}`, { method: 'DELETE' });\n setItems((prev) => prev.filter((n) => n.id !== id));\n } catch (err) {\n setError(err instanceof FetchError ? err.message : (err as Error).message);\n } finally {\n setBusy(null);\n }\n };\n\n const unread = items.filter((n) => !n.read).length;\n\n return (\n <Stack gap={5} data-testid=\"notifications-view\">\n <ViewHeader\n title=\"Notifications\"\n description={`${unread} unread of ${items.length} total. Backed by ~/.config/bizar/notifications.jsonl.`}\n actions={\n <Inline gap={2} align=\"center\">\n {error !== null && <span role=\"alert\" style={{ color: 'var(--danger)', fontSize: 'var(--fs-12)' }}>{error}</span>}\n <Button variant=\"ghost\" onClick={() => void res.refetch()} data-testid=\"notifications-refresh\">\n <RefreshCw size={14} aria-hidden /> Refresh\n </Button>\n <Button variant=\"secondary\" onClick={() => void markAll()} disabled={busy === 'all' || unread === 0} data-testid=\"notifications-mark-all\">\n <CheckCheck size={14} aria-hidden /> Mark all read\n </Button>\n </Inline>\n }\n />\n {res.loading && items.length === 0 ? (\n <Stack gap={2}>\n <Skeleton style={{ height: 64 }} />\n <Skeleton style={{ height: 64 }} />\n <Skeleton style={{ height: 64 }} />\n </Stack>\n ) : items.length === 0 ? (\n <EmptyState\n icon={<Bell size={32} aria-hidden />}\n title=\"No notifications\"\n description=\"Agent runs, task moves, and CI results will land here.\"\n />\n ) : (\n <Stack gap={2}>\n {items.map((n) => {\n const tone = toneFor(n);\n return (\n <Card key={n.id} variant=\"default\" style={{ opacity: n.read ? 0.7 : 1 }}>\n <CardBody>\n <Inline align=\"start\" justify=\"between\" gap={3}>\n <Stack gap={1} style={{ flex: 1, minWidth: 0 }}>\n <Inline align=\"center\" gap={2}>\n {!n.read && <span aria-label=\"Unread\" style={{ width: 8, height: 8, borderRadius: 'var(--radius-pill)', background: 'var(--accent)' }} />}\n <strong style={{ fontSize: 'var(--fs-13)' }}>{n.title || n.source || n.id}</strong>\n {n.source && <Badge tone={tone}>{n.source}</Badge>}\n <span style={{ color: 'var(--fg-muted)', fontSize: 'var(--fs-12)' }}>{fmtRel(n.createdAt)}</span>\n </Inline>\n {n.body && <span style={{ fontSize: 'var(--fs-13)', color: 'var(--fg-muted)' }}>{n.body}</span>}\n </Stack>\n <Inline gap={1}>\n {!n.read && (\n <Button variant=\"ghost\" onClick={() => void markRead(n.id)} disabled={busy === n.id} data-testid={`notification-read-${n.id}`}>\n Mark read\n </Button>\n )}\n <Button variant=\"ghost\" onClick={() => void dismiss(n.id)} disabled={busy === n.id} data-testid={`notification-dismiss-${n.id}`}>\n <Trash2 size={12} aria-hidden /> Dismiss\n </Button>\n </Inline>\n </Inline>\n </CardBody>\n </Card>\n );\n })}\n </Stack>\n )}\n </Stack>\n );\n}"],"names":["CheckCheck","createLucideIcon","toneFor","item","fmtRel","iso","t","delta","NotificationsView","res","useFetch","items","setItems","useState","busy","setBusy","error","setError","useEffect","_a","onChange","useCallback","msg","useWsMessage","markRead","id","fetchJson","prev","n","err","FetchError","markAll","dismiss","unread","jsxs","Stack","jsx","ViewHeader","Inline","Button","RefreshCw","Skeleton","EmptyState","Bell","tone","Card","CardBody","Badge","Trash2"],"mappings":"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","x_google_ignoreList":[0]}
@@ -0,0 +1 @@
1
+ import{r as i,j as e}from"./cmdk-DVxytz9i.js";import{u as f,S as l,I as r,d as o,$ as y,f as F,h as g,i as C,F as S}from"./main-CyAIirBB.js";import{V as D}from"./ViewHeader-V_SzbUpw.js";import{C as w,b as z}from"./Card-D9019JOD.js";import{I}from"./Input-BSa3lh_-.js";import{E as N}from"./EmptyState-DLmtBX_6.js";import{R as V}from"./refresh-ccw-BCXx86xp.js";import{R as B}from"./rotate-ccw-BZrEVc0Z.js";import{F as L}from"./file-text-CS1jB_oV.js";import{T as O}from"./trash-2-2NsyTU8c.js";function X(){const t=f("/api/obsidian"),s=f("/api/obsidian/notes"),[k,c]=i.useState(null),[$,v]=i.useState(null),[h,E]=i.useState(""),[j,m]=i.useState(!1),[b,d]=i.useState(null),p=i.useMemo(()=>{var a;return((a=s.data)==null?void 0:a.notes)??[]},[s.data]),u=i.useCallback(()=>{t.refetch(),s.refetch()},[t,s]),R=async a=>{d(null);try{await C(`/api/obsidian/notes/${a}`,{method:"DELETE"}),c(null),v(null),u()}catch(n){d((n instanceof S,n.message))}},T=async()=>{m(!0),d(null);try{await C("/api/obsidian/index",{method:"POST"}),u()}catch(a){d((a instanceof S,a.message))}finally{m(!1)}};return e.jsxs(l,{gap:4,"data-testid":"obsidian-view",children:[e.jsx(D,{title:"Obsidian",description:"Vault explorer + index. Notes stored as markdown.",actions:e.jsxs(r,{align:"center",gap:2,children:[b!==null&&e.jsx("span",{role:"alert","data-testid":"obsidian-error",style:{color:"var(--danger)",fontSize:"var(--fs-12)"},children:b}),e.jsxs(o,{variant:"ghost",onClick:()=>void u(),"data-testid":"obsidian-refresh",children:[e.jsx(V,{size:14,"aria-hidden":!0})," Refresh"]}),e.jsxs(o,{variant:"primary",onClick:()=>void T(),disabled:j,"data-testid":"obsidian-reindex",children:[e.jsx(B,{size:14,"aria-hidden":!0})," ",j?"Indexing…":"Rebuild index"]})]})}),t.data&&e.jsx(w,{variant:"default",children:e.jsx(z,{children:e.jsxs(r,{align:"center",gap:3,children:[e.jsx(y,{size:18,"aria-hidden":!0,style:{color:"var(--accent)"}}),e.jsxs(l,{gap:0,children:[e.jsx("strong",{children:t.data.exists?`${t.data.noteCount??0} notes`:"No vault"}),t.data.exists&&t.data.vaultDir&&e.jsx("code",{style:{fontSize:"var(--fs-11)",color:"var(--fg-subtle)"},children:t.data.vaultDir})]}),t.data.exists&&e.jsxs(r,{align:"center",gap:3,style:{fontSize:"var(--fs-12)",color:"var(--fg-muted)"},children:[e.jsxs("span",{children:[(t.data.totalSize??0).toLocaleString()," bytes"]}),t.data.folderCount!==void 0&&e.jsxs("span",{children:["· ",t.data.folderCount," folders"]})]})]})})}),e.jsx(w,{variant:"default",children:e.jsxs(z,{children:[e.jsxs(r,{gap:2,align:"center",children:[e.jsx(F,{size:14,"aria-hidden":!0,style:{color:"var(--fg-muted)"}}),e.jsx(I,{value:h,onChange:a=>E(a.target.value),placeholder:"Filter notes by path…","data-testid":"obsidian-search"})]}),s.loading&&p.length===0?e.jsxs(l,{gap:1,style:{marginTop:"var(--space-2)"},children:[e.jsx(g,{style:{height:28}}),e.jsx(g,{style:{height:28}})]}):p.length===0?e.jsx(N,{icon:e.jsx(y,{size:28,"aria-hidden":!0}),title:"No notes",description:"Vault is empty or not initialized."}):e.jsx(l,{gap:1,style:{marginTop:"var(--space-2)"},children:p.filter(a=>!h||a.path.toLowerCase().includes(h.toLowerCase())).slice(0,200).map(a=>{const n=k===a.path;return e.jsxs("div",{"data-testid":`obsidian-row-${a.path}`,style:{padding:"var(--space-2)",border:"1px solid var(--border)",borderRadius:"var(--radius-sm)"},children:[e.jsxs(r,{align:"center",justify:"between",gap:2,children:[e.jsxs(r,{align:"center",gap:2,children:[e.jsx(L,{size:12,"aria-hidden":!0,style:{color:"var(--fg-muted)"}}),e.jsx("button",{type:"button",onClick:()=>v(x=>x===a.path?null:a.path),"data-testid":`obsidian-view-${a.path}`,style:{background:"transparent",border:"none",color:"var(--fg)",cursor:"pointer",padding:0,fontFamily:"inherit",fontSize:"inherit"},children:e.jsx("code",{children:a.path})}),a.size!==void 0&&e.jsxs("span",{style:{fontSize:"var(--fs-11)",color:"var(--fg-subtle)"},children:[a.size," B"]})]}),e.jsx(o,{variant:"ghost",onClick:()=>c(x=>x===a.path?null:a.path),"data-testid":`obsidian-delete-${a.path}`,"aria-label":`Delete ${a.path}`,children:e.jsx(O,{size:14,"aria-hidden":!0})})]}),n&&e.jsxs(r,{gap:1,style:{marginTop:"var(--space-1)"},children:[e.jsx(o,{variant:"danger",onClick:()=>void R(a.path),"data-testid":`obsidian-confirm-delete-${a.path}`,children:"Confirm delete"}),e.jsx(o,{variant:"ghost",onClick:()=>c(null),children:"Cancel"})]}),$===a.path&&e.jsx(P,{path:a.path})]},a.path)})})]})})]})}function P({path:t}){const s=f(`/api/obsidian/notes/${t}`);return e.jsx("div",{"data-testid":`obsidian-note-view-${t}`,style:{marginTop:"var(--space-2)",padding:"var(--space-2)",background:"var(--surface-1)",borderRadius:"var(--radius-sm)"},children:s.loading&&s.data===null?e.jsx(g,{style:{height:60}}):s.data?e.jsx("pre",{style:{margin:0,fontSize:"var(--fs-12)",whiteSpace:"pre-wrap",color:"var(--fg)"},children:s.data.raw??s.data.body??""}):e.jsx("span",{style:{color:"var(--danger)",fontSize:"var(--fs-12)"},children:"Failed to load"})})}export{X as ObsidianView};
@@ -1 +1 @@
1
- {"version":3,"file":"ObsidianView-BCTejEj2.js","sources":["../../src/web/v8/views/Obsidian/ObsidianView.tsx"],"sourcesContent":["/**\n * v8/views/Obsidian/ObsidianView.tsx — Sprint S43, v9.3.0.\n *\n * Vault explorer. Stats card (GET /api/obsidian), notes list\n * (GET /api/obsidian/notes), per-note view (GET /api/obsidian/notes/*),\n * per-note delete (DELETE /api/obsidian/notes/*), Index button\n * (POST /api/obsidian/index), and search (GET /api/obsidian/search?q).\n */\n\nimport { useCallback, useMemo, useState } from 'react';\nimport { BookOpen, FileText, RefreshCcw, Trash2, Search, RotateCcw } from 'lucide-react';\nimport { Stack } from '../../ui/primitives/Stack.js';\nimport { Inline } from '../../ui/primitives/Inline.js';\nimport { ViewHeader } from '../../ui/data/ViewHeader.js';\nimport { Card, CardBody } from '../../ui/data/Card.js';\nimport { Button } from '../../ui/controls/Button.js';\nimport { Input } from '../../ui/controls/Input.js';\nimport { Skeleton } from '../../ui/feedback/Skeleton.js';\nimport { EmptyState } from '../../ui/feedback/EmptyState.js';\nimport { useFetch } from '../../data/useFetch.js';\nimport { fetchJson, FetchError } from '../../data/fetcher.js';\n\ninterface VaultStats {\n exists: boolean;\n vaultDir?: string;\n noteCount?: number;\n totalSize?: number;\n folderCount?: number;\n}\n\ninterface NoteMeta {\n path: string;\n relPath: string;\n mtime?: number | string;\n size?: number;\n}\n\nexport function ObsidianView(): JSX.Element {\n const statsPayload = useFetch<VaultStats>('/api/obsidian');\n const notesPayload = useFetch<{ notes: NoteMeta[] }>('/api/obsidian/notes');\n const [confirmDelete, setConfirmDelete] = useState<string | null>(null);\n const [activeNote, setActiveNote] = useState<string | null>(null);\n const [search, setSearch] = useState<string>('');\n const [indexing, setIndexing] = useState<boolean>(false);\n const [error, setError] = useState<string | null>(null);\n\n const notes = useMemo<NoteMeta[]>(() => notesPayload.data?.notes ?? [], [notesPayload.data]);\n const refresh = useCallback(() => { void statsPayload.refetch(); void notesPayload.refetch(); }, [statsPayload, notesPayload]);\n\n const drop = async (path: string): Promise<void> => {\n setError(null);\n try {\n await fetchJson(`/api/obsidian/notes/${path}`, { method: 'DELETE' });\n setConfirmDelete(null);\n setActiveNote(null);\n refresh();\n } catch (err) {\n setError(err instanceof FetchError ? err.message : (err as Error).message);\n }\n };\n\n const reindex = async (): Promise<void> => {\n setIndexing(true);\n setError(null);\n try {\n await fetchJson('/api/obsidian/index', { method: 'POST' });\n refresh();\n } catch (err) {\n setError(err instanceof FetchError ? err.message : (err as Error).message);\n } finally {\n setIndexing(false);\n }\n };\n\n return (\n <Stack gap={4} data-testid=\"obsidian-view\">\n <ViewHeader\n title=\"Obsidian\"\n description=\"Vault explorer + index. Notes stored as markdown.\"\n actions={\n <Inline align=\"center\" gap={2}>\n {error !== null && (\n <span role=\"alert\" data-testid=\"obsidian-error\" style={{ color: 'var(--danger)', fontSize: 'var(--fs-12)' }}>{error}</span>\n )}\n <Button variant=\"ghost\" onClick={() => void refresh()} data-testid=\"obsidian-refresh\">\n <RefreshCcw size={14} aria-hidden /> Refresh\n </Button>\n <Button variant=\"primary\" onClick={() => void reindex()} disabled={indexing} data-testid=\"obsidian-reindex\">\n <RotateCcw size={14} aria-hidden /> {indexing ? 'Indexing…' : 'Rebuild index'}\n </Button>\n </Inline>\n }\n />\n\n {statsPayload.data && (\n <Card variant=\"default\">\n <CardBody>\n <Inline align=\"center\" gap={3}>\n <BookOpen size={18} aria-hidden style={{ color: 'var(--accent)' }} />\n <Stack gap={0}>\n <strong>{statsPayload.data.exists ? `${statsPayload.data.noteCount ?? 0} notes` : 'No vault'}</strong>\n {statsPayload.data.exists && statsPayload.data.vaultDir && (\n <code style={{ fontSize: 'var(--fs-11)', color: 'var(--fg-subtle)' }}>{statsPayload.data.vaultDir}</code>\n )}\n </Stack>\n {statsPayload.data.exists && (\n <Inline align=\"center\" gap={3} style={{ fontSize: 'var(--fs-12)', color: 'var(--fg-muted)' }}>\n <span>{(statsPayload.data.totalSize ?? 0).toLocaleString()} bytes</span>\n {statsPayload.data.folderCount !== undefined && <span>· {statsPayload.data.folderCount} folders</span>}\n </Inline>\n )}\n </Inline>\n </CardBody>\n </Card>\n )}\n\n <Card variant=\"default\">\n <CardBody>\n <Inline gap={2} align=\"center\">\n <Search size={14} aria-hidden style={{ color: 'var(--fg-muted)' }} />\n <Input value={search} onChange={(e) => setSearch((e.target as HTMLInputElement).value)} placeholder=\"Filter notes by path…\" data-testid=\"obsidian-search\" />\n </Inline>\n {notesPayload.loading && notes.length === 0 ? (\n <Stack gap={1} style={{ marginTop: 'var(--space-2)' }}>\n <Skeleton style={{ height: 28 }} />\n <Skeleton style={{ height: 28 }} />\n </Stack>\n ) : notes.length === 0 ? (\n <EmptyState icon={<BookOpen size={28} aria-hidden />} title=\"No notes\" description=\"Vault is empty or not initialized.\" />\n ) : (\n <Stack gap={1} style={{ marginTop: 'var(--space-2)' }}>\n {notes\n .filter((n) => !search || n.path.toLowerCase().includes(search.toLowerCase()))\n .slice(0, 200)\n .map((n) => {\n const isConfirming = confirmDelete === n.path;\n return (\n <div\n key={n.path}\n data-testid={`obsidian-row-${n.path}`}\n style={{ padding: 'var(--space-2)', border: '1px solid var(--border)', borderRadius: 'var(--radius-sm)' }}\n >\n <Inline align=\"center\" justify=\"between\" gap={2}>\n <Inline align=\"center\" gap={2}>\n <FileText size={12} aria-hidden style={{ color: 'var(--fg-muted)' }} />\n <button\n type=\"button\"\n onClick={() => setActiveNote((cur) => (cur === n.path ? null : n.path))}\n data-testid={`obsidian-view-${n.path}`}\n style={{ background: 'transparent', border: 'none', color: 'var(--fg)', cursor: 'pointer', padding: 0, fontFamily: 'inherit', fontSize: 'inherit' }}\n >\n <code>{n.path}</code>\n </button>\n {n.size !== undefined && <span style={{ fontSize: 'var(--fs-11)', color: 'var(--fg-subtle)' }}>{n.size} B</span>}\n </Inline>\n <Button variant=\"ghost\" onClick={() => setConfirmDelete((cur) => (cur === n.path ? null : n.path))} data-testid={`obsidian-delete-${n.path}`} aria-label={`Delete ${n.path}`}>\n <Trash2 size={14} aria-hidden />\n </Button>\n </Inline>\n {isConfirming && (\n <Inline gap={1} style={{ marginTop: 'var(--space-1)' }}>\n <Button variant=\"danger\" onClick={() => void drop(n.path)} data-testid={`obsidian-confirm-delete-${n.path}`}>\n Confirm delete\n </Button>\n <Button variant=\"ghost\" onClick={() => setConfirmDelete(null)}>Cancel</Button>\n </Inline>\n )}\n {activeNote === n.path && <NoteView path={n.path} />}\n </div>\n );\n })}\n </Stack>\n )}\n </CardBody>\n </Card>\n </Stack>\n );\n}\n\nfunction NoteView({ path }: { path: string }): JSX.Element {\n const payload = useFetch<{ relPath: string; body?: string; frontmatter?: Record<string, unknown>; raw?: string }>(`/api/obsidian/notes/${path}`);\n return (\n <div data-testid={`obsidian-note-view-${path}`} style={{ marginTop: 'var(--space-2)', padding: 'var(--space-2)', background: 'var(--surface-1)', borderRadius: 'var(--radius-sm)' }}>\n {payload.loading && payload.data === null ? (\n <Skeleton style={{ height: 60 }} />\n ) : payload.data ? (\n <pre style={{ margin: 0, fontSize: 'var(--fs-12)', whiteSpace: 'pre-wrap', color: 'var(--fg)' }}>{payload.data.raw ?? payload.data.body ?? ''}</pre>\n ) : (\n <span style={{ color: 'var(--danger)', fontSize: 'var(--fs-12)' }}>Failed to load</span>\n )}\n </div>\n );\n}"],"names":["ObsidianView","statsPayload","useFetch","notesPayload","confirmDelete","setConfirmDelete","useState","activeNote","setActiveNote","search","setSearch","indexing","setIndexing","error","setError","notes","useMemo","_a","refresh","useCallback","drop","path","fetchJson","err","FetchError","reindex","jsxs","Stack","jsx","ViewHeader","Inline","Button","RefreshCcw","RotateCcw","Card","CardBody","BookOpen","Search","Input","e","Skeleton","EmptyState","n","isConfirming","FileText","cur","Trash2","NoteView","payload"],"mappings":"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"}
1
+ {"version":3,"file":"ObsidianView-DEuk65zS.js","sources":["../../src/web/v8/views/Obsidian/ObsidianView.tsx"],"sourcesContent":["/**\n * v8/views/Obsidian/ObsidianView.tsx — Sprint S43, v9.3.0.\n *\n * Vault explorer. Stats card (GET /api/obsidian), notes list\n * (GET /api/obsidian/notes), per-note view (GET /api/obsidian/notes/*),\n * per-note delete (DELETE /api/obsidian/notes/*), Index button\n * (POST /api/obsidian/index), and search (GET /api/obsidian/search?q).\n */\n\nimport { useCallback, useMemo, useState } from 'react';\nimport { BookOpen, FileText, RefreshCcw, Trash2, Search, RotateCcw } from 'lucide-react';\nimport { Stack } from '../../ui/primitives/Stack.js';\nimport { Inline } from '../../ui/primitives/Inline.js';\nimport { ViewHeader } from '../../ui/data/ViewHeader.js';\nimport { Card, CardBody } from '../../ui/data/Card.js';\nimport { Button } from '../../ui/controls/Button.js';\nimport { Input } from '../../ui/controls/Input.js';\nimport { Skeleton } from '../../ui/feedback/Skeleton.js';\nimport { EmptyState } from '../../ui/feedback/EmptyState.js';\nimport { useFetch } from '../../data/useFetch.js';\nimport { fetchJson, FetchError } from '../../data/fetcher.js';\n\ninterface VaultStats {\n exists: boolean;\n vaultDir?: string;\n noteCount?: number;\n totalSize?: number;\n folderCount?: number;\n}\n\ninterface NoteMeta {\n path: string;\n relPath: string;\n mtime?: number | string;\n size?: number;\n}\n\nexport function ObsidianView(): JSX.Element {\n const statsPayload = useFetch<VaultStats>('/api/obsidian');\n const notesPayload = useFetch<{ notes: NoteMeta[] }>('/api/obsidian/notes');\n const [confirmDelete, setConfirmDelete] = useState<string | null>(null);\n const [activeNote, setActiveNote] = useState<string | null>(null);\n const [search, setSearch] = useState<string>('');\n const [indexing, setIndexing] = useState<boolean>(false);\n const [error, setError] = useState<string | null>(null);\n\n const notes = useMemo<NoteMeta[]>(() => notesPayload.data?.notes ?? [], [notesPayload.data]);\n const refresh = useCallback(() => { void statsPayload.refetch(); void notesPayload.refetch(); }, [statsPayload, notesPayload]);\n\n const drop = async (path: string): Promise<void> => {\n setError(null);\n try {\n await fetchJson(`/api/obsidian/notes/${path}`, { method: 'DELETE' });\n setConfirmDelete(null);\n setActiveNote(null);\n refresh();\n } catch (err) {\n setError(err instanceof FetchError ? err.message : (err as Error).message);\n }\n };\n\n const reindex = async (): Promise<void> => {\n setIndexing(true);\n setError(null);\n try {\n await fetchJson('/api/obsidian/index', { method: 'POST' });\n refresh();\n } catch (err) {\n setError(err instanceof FetchError ? err.message : (err as Error).message);\n } finally {\n setIndexing(false);\n }\n };\n\n return (\n <Stack gap={4} data-testid=\"obsidian-view\">\n <ViewHeader\n title=\"Obsidian\"\n description=\"Vault explorer + index. Notes stored as markdown.\"\n actions={\n <Inline align=\"center\" gap={2}>\n {error !== null && (\n <span role=\"alert\" data-testid=\"obsidian-error\" style={{ color: 'var(--danger)', fontSize: 'var(--fs-12)' }}>{error}</span>\n )}\n <Button variant=\"ghost\" onClick={() => void refresh()} data-testid=\"obsidian-refresh\">\n <RefreshCcw size={14} aria-hidden /> Refresh\n </Button>\n <Button variant=\"primary\" onClick={() => void reindex()} disabled={indexing} data-testid=\"obsidian-reindex\">\n <RotateCcw size={14} aria-hidden /> {indexing ? 'Indexing…' : 'Rebuild index'}\n </Button>\n </Inline>\n }\n />\n\n {statsPayload.data && (\n <Card variant=\"default\">\n <CardBody>\n <Inline align=\"center\" gap={3}>\n <BookOpen size={18} aria-hidden style={{ color: 'var(--accent)' }} />\n <Stack gap={0}>\n <strong>{statsPayload.data.exists ? `${statsPayload.data.noteCount ?? 0} notes` : 'No vault'}</strong>\n {statsPayload.data.exists && statsPayload.data.vaultDir && (\n <code style={{ fontSize: 'var(--fs-11)', color: 'var(--fg-subtle)' }}>{statsPayload.data.vaultDir}</code>\n )}\n </Stack>\n {statsPayload.data.exists && (\n <Inline align=\"center\" gap={3} style={{ fontSize: 'var(--fs-12)', color: 'var(--fg-muted)' }}>\n <span>{(statsPayload.data.totalSize ?? 0).toLocaleString()} bytes</span>\n {statsPayload.data.folderCount !== undefined && <span>· {statsPayload.data.folderCount} folders</span>}\n </Inline>\n )}\n </Inline>\n </CardBody>\n </Card>\n )}\n\n <Card variant=\"default\">\n <CardBody>\n <Inline gap={2} align=\"center\">\n <Search size={14} aria-hidden style={{ color: 'var(--fg-muted)' }} />\n <Input value={search} onChange={(e) => setSearch((e.target as HTMLInputElement).value)} placeholder=\"Filter notes by path…\" data-testid=\"obsidian-search\" />\n </Inline>\n {notesPayload.loading && notes.length === 0 ? (\n <Stack gap={1} style={{ marginTop: 'var(--space-2)' }}>\n <Skeleton style={{ height: 28 }} />\n <Skeleton style={{ height: 28 }} />\n </Stack>\n ) : notes.length === 0 ? (\n <EmptyState icon={<BookOpen size={28} aria-hidden />} title=\"No notes\" description=\"Vault is empty or not initialized.\" />\n ) : (\n <Stack gap={1} style={{ marginTop: 'var(--space-2)' }}>\n {notes\n .filter((n) => !search || n.path.toLowerCase().includes(search.toLowerCase()))\n .slice(0, 200)\n .map((n) => {\n const isConfirming = confirmDelete === n.path;\n return (\n <div\n key={n.path}\n data-testid={`obsidian-row-${n.path}`}\n style={{ padding: 'var(--space-2)', border: '1px solid var(--border)', borderRadius: 'var(--radius-sm)' }}\n >\n <Inline align=\"center\" justify=\"between\" gap={2}>\n <Inline align=\"center\" gap={2}>\n <FileText size={12} aria-hidden style={{ color: 'var(--fg-muted)' }} />\n <button\n type=\"button\"\n onClick={() => setActiveNote((cur) => (cur === n.path ? null : n.path))}\n data-testid={`obsidian-view-${n.path}`}\n style={{ background: 'transparent', border: 'none', color: 'var(--fg)', cursor: 'pointer', padding: 0, fontFamily: 'inherit', fontSize: 'inherit' }}\n >\n <code>{n.path}</code>\n </button>\n {n.size !== undefined && <span style={{ fontSize: 'var(--fs-11)', color: 'var(--fg-subtle)' }}>{n.size} B</span>}\n </Inline>\n <Button variant=\"ghost\" onClick={() => setConfirmDelete((cur) => (cur === n.path ? null : n.path))} data-testid={`obsidian-delete-${n.path}`} aria-label={`Delete ${n.path}`}>\n <Trash2 size={14} aria-hidden />\n </Button>\n </Inline>\n {isConfirming && (\n <Inline gap={1} style={{ marginTop: 'var(--space-1)' }}>\n <Button variant=\"danger\" onClick={() => void drop(n.path)} data-testid={`obsidian-confirm-delete-${n.path}`}>\n Confirm delete\n </Button>\n <Button variant=\"ghost\" onClick={() => setConfirmDelete(null)}>Cancel</Button>\n </Inline>\n )}\n {activeNote === n.path && <NoteView path={n.path} />}\n </div>\n );\n })}\n </Stack>\n )}\n </CardBody>\n </Card>\n </Stack>\n );\n}\n\nfunction NoteView({ path }: { path: string }): JSX.Element {\n const payload = useFetch<{ relPath: string; body?: string; frontmatter?: Record<string, unknown>; raw?: string }>(`/api/obsidian/notes/${path}`);\n return (\n <div data-testid={`obsidian-note-view-${path}`} style={{ marginTop: 'var(--space-2)', padding: 'var(--space-2)', background: 'var(--surface-1)', borderRadius: 'var(--radius-sm)' }}>\n {payload.loading && payload.data === null ? (\n <Skeleton style={{ height: 60 }} />\n ) : payload.data ? (\n <pre style={{ margin: 0, fontSize: 'var(--fs-12)', whiteSpace: 'pre-wrap', color: 'var(--fg)' }}>{payload.data.raw ?? payload.data.body ?? ''}</pre>\n ) : (\n <span style={{ color: 'var(--danger)', fontSize: 'var(--fs-12)' }}>Failed to load</span>\n )}\n </div>\n );\n}"],"names":["ObsidianView","statsPayload","useFetch","notesPayload","confirmDelete","setConfirmDelete","useState","activeNote","setActiveNote","search","setSearch","indexing","setIndexing","error","setError","notes","useMemo","_a","refresh","useCallback","drop","path","fetchJson","err","FetchError","reindex","jsxs","Stack","jsx","ViewHeader","Inline","Button","RefreshCcw","RotateCcw","Card","CardBody","BookOpen","Search","Input","e","Skeleton","EmptyState","n","isConfirming","FileText","cur","Trash2","NoteView","payload"],"mappings":"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"}
@@ -1 +1 @@
1
- import{r as g,j as e}from"./cmdk-DVxytz9i.js";import{u as h,a as I,S as m,B as A,A as y,W as L,b as M}from"./main-CokHAXWM.js";import{S as E,a as u,A as F}from"./ActivityFeed-JjmOY2AQ.js";import{C as f,a as x,b as j}from"./Card-B0imhxMk.js";import{V as G}from"./ViewHeader-BzYZ3m-e.js";import{S as q}from"./Sparkline-ktqQLdyb.js";import{C as z}from"./circle-check-Bx8wmXR9.js";import{T as k}from"./triangle-alert-_2xe_tl4.js";import{G as C,a as B}from"./git-pull-request-BjG5lNtR.js";import"./trending-up-DMj-AJVf.js";const O={"git.merge":C,"git.pull-request":B,"git.merge.pr":C,"task.completed":z,"task.failed":k,"agent.run":M,"agent.tool":L,"agent.message":y};function P(t){const i=O[t||""]||y;return e.jsx(i,{size:14,"aria-hidden":"true"})}function V(t,i){const n=typeof t.ts=="number"?t.ts:Date.now(),c=t.meta||(n?new Date(n).toLocaleString():"");return{id:t.id||`${n}-${i}`,icon:P(t.iconKey||t.kind),title:t.title||t.description||t.kind||"event",description:t.description,meta:c,tone:t.tone}}function Z(){var S,$;const t=h("/api/snapshot"),i=h("/api/activity?limit=10"),n=h("/api/usage?range=7d"),c=g.useMemo(()=>{var a;const s=((a=n.data)==null?void 0:a.daily)??[];return s.length<2?[]:s.map(r=>typeof r.tokens=="number"?r.tokens:0)},[n.data]),[b,T]=g.useState([]),R=g.useCallback(s=>{const a=s.event;a&&T(r=>[...r.slice(-19),a])},[]);I("activity:new",R);const w=g.useMemo(()=>{var a;return[...b.slice(-10).reverse(),...(((a=i.data)==null?void 0:a.events)||[]).slice(0,10)].map(V)},[i.data,b]),o=((S=t.data)==null?void 0:S.overview)||{},l=o.tasks||{},v=o.goals||{},d=o.agents||{},p=o.tokens||{};return e.jsxs(m,{gap:5,"data-testid":"overview-view",children:[e.jsx(G,{title:"Overview",description:"What's moving in the harness right now.",actions:e.jsx(A,{tone:t.error?"danger":"success",dot:!0,children:t.loading?"Loading":t.error?"Offline":"Live"})}),e.jsxs(E,{cols:4,children:[e.jsx(u,{label:"Active tasks",value:String((l.queued||0)+(l.active||0)),trend:(l.active||0)>(l.queued||0)?"up":"flat",delta:`${l.done||0} done · ${l.blocked||0} blocked`}),e.jsx(u,{label:"Goals at risk",value:`${v.atRisk||0} / ${v.total||0}`,trend:(v.atRisk||0)>0?"down":"flat",delta:`${v.done||0} done`}),e.jsx(u,{label:"Agents running",value:`${d.running||0} / ${d.total||0}`,trend:(d.running||0)>0?"up":"flat",delta:`${d.idle||0} idle · ${d.error||0} error`}),e.jsx(u,{label:"Tokens (24h)",value:p.last24h?`${(p.last24h/1e6).toFixed(1)}M`:"—",trend:p.trend||"flat",delta:"last 24h",sparkline:c.length>=2?e.jsx("span",{"data-testid":"overview-tokens-sparkline",children:e.jsx(q,{data:c,width:140,height:28})}):void 0})]}),e.jsxs("div",{style:{display:"grid",gridTemplateColumns:"1fr 1fr",gap:"var(--space-4)"},children:[e.jsxs(f,{children:[e.jsx(x,{title:"Recent activity"}),e.jsx(j,{children:e.jsx(F,{items:w.length?w:[]})})]}),e.jsxs(f,{children:[e.jsx(x,{title:"Needs attention"}),e.jsx(j,{children:e.jsx(m,{gap:2,children:(o.needsAttention||[]).length===0?e.jsxs("div",{role:"status",style:{padding:"var(--space-3)",border:"1px solid color-mix(in oklch, var(--success) 40%, var(--border))",borderRadius:"var(--radius-md)",background:"color-mix(in oklch, var(--success) 8%, var(--surface-0))",color:"var(--success)",fontSize:"var(--fs-13)",display:"flex",alignItems:"center",gap:"var(--space-2)"},children:[e.jsx(z,{size:14,"aria-hidden":!0}),"All clear — nothing needs attention."]}):(o.needsAttention||[]).map((s,a)=>{const r=String(s.label||"").toLowerCase().includes("fail")?"danger":String(s.label||"").toLowerCase().includes("block")||String(s.label||"").toLowerCase().includes("risk")?"warning":"info",W=r==="danger"?k:r==="warning"?k:y;return e.jsxs("div",{role:"alert",style:{padding:"var(--space-3)",border:`1px solid color-mix(in oklch, var(--${r}) 40%, var(--border))`,borderRadius:"var(--radius-md)",background:`color-mix(in oklch, var(--${r}) 8%, var(--surface-0))`,display:"flex",alignItems:"flex-start",gap:"var(--space-2)"},children:[e.jsx(W,{size:16,"aria-hidden":!0,style:{color:`var(--${r})`,marginTop:2,flexShrink:0}}),e.jsxs("div",{style:{flex:1,minWidth:0},children:[e.jsx("div",{style:{fontSize:"var(--fs-13)",fontWeight:600,color:"var(--fg)"},children:s.label}),e.jsx("div",{style:{fontSize:"var(--fs-12)",color:"var(--fg-muted)",marginTop:2},children:s.value}),typeof s.hint=="string"&&s.hint.length>0&&e.jsx("div",{style:{fontSize:"var(--fs-12)",color:"var(--fg-muted)",marginTop:2},children:s.hint})]})]},a)})})})]})]}),Array.isArray(($=t.data)==null?void 0:$.projects)&&t.data.projects.length>0&&e.jsxs(f,{children:[e.jsx(x,{title:"Active projects"}),e.jsx(j,{children:e.jsx(m,{gap:1,children:t.data.projects.map(s=>{var a,r;return e.jsxs("div",{style:{padding:"var(--space-2) var(--space-3)",border:"1px solid var(--border)",borderRadius:"var(--radius-md)",background:"var(--surface-0)",display:"flex",alignItems:"center",justifyContent:"space-between",gap:"var(--space-2)"},children:[e.jsxs("div",{style:{minWidth:0},children:[e.jsx("div",{style:{fontSize:"var(--fs-13)",fontWeight:600},children:s.name||s.id}),e.jsx("code",{style:{fontFamily:"var(--font-mono)",fontSize:"var(--fs-12)",color:"var(--fg-muted)"},children:s.cwd})]}),((r=(a=t.data)==null?void 0:a.activeProject)==null?void 0:r.id)===s.id&&e.jsx(A,{tone:"success",dot:!0,children:"active"})]},s.id)})})})]})]})}export{Z as OverviewView};
1
+ import{r as g,j as e}from"./cmdk-DVxytz9i.js";import{u as h,a as I,S as m,B as A,A as y,W as L,b as M}from"./main-CyAIirBB.js";import{S as E,a as u,A as F}from"./ActivityFeed-xXiUCZLB.js";import{C as f,a as x,b as j}from"./Card-D9019JOD.js";import{V as G}from"./ViewHeader-V_SzbUpw.js";import{S as q}from"./Sparkline-CLZBtvsN.js";import{C as z}from"./circle-check-COtVgsWw.js";import{T as k}from"./triangle-alert-DFmQ_RXN.js";import{G as C,a as B}from"./git-pull-request-Co6gYfba.js";import"./trending-up-BXwP23VK.js";const O={"git.merge":C,"git.pull-request":B,"git.merge.pr":C,"task.completed":z,"task.failed":k,"agent.run":M,"agent.tool":L,"agent.message":y};function P(t){const i=O[t||""]||y;return e.jsx(i,{size:14,"aria-hidden":"true"})}function V(t,i){const n=typeof t.ts=="number"?t.ts:Date.now(),c=t.meta||(n?new Date(n).toLocaleString():"");return{id:t.id||`${n}-${i}`,icon:P(t.iconKey||t.kind),title:t.title||t.description||t.kind||"event",description:t.description,meta:c,tone:t.tone}}function Z(){var S,$;const t=h("/api/snapshot"),i=h("/api/activity?limit=10"),n=h("/api/usage?range=7d"),c=g.useMemo(()=>{var a;const s=((a=n.data)==null?void 0:a.daily)??[];return s.length<2?[]:s.map(r=>typeof r.tokens=="number"?r.tokens:0)},[n.data]),[b,T]=g.useState([]),R=g.useCallback(s=>{const a=s.event;a&&T(r=>[...r.slice(-19),a])},[]);I("activity:new",R);const w=g.useMemo(()=>{var a;return[...b.slice(-10).reverse(),...(((a=i.data)==null?void 0:a.events)||[]).slice(0,10)].map(V)},[i.data,b]),o=((S=t.data)==null?void 0:S.overview)||{},l=o.tasks||{},v=o.goals||{},d=o.agents||{},p=o.tokens||{};return e.jsxs(m,{gap:5,"data-testid":"overview-view",children:[e.jsx(G,{title:"Overview",description:"What's moving in the harness right now.",actions:e.jsx(A,{tone:t.error?"danger":"success",dot:!0,children:t.loading?"Loading":t.error?"Offline":"Live"})}),e.jsxs(E,{cols:4,children:[e.jsx(u,{label:"Active tasks",value:String((l.queued||0)+(l.active||0)),trend:(l.active||0)>(l.queued||0)?"up":"flat",delta:`${l.done||0} done · ${l.blocked||0} blocked`}),e.jsx(u,{label:"Goals at risk",value:`${v.atRisk||0} / ${v.total||0}`,trend:(v.atRisk||0)>0?"down":"flat",delta:`${v.done||0} done`}),e.jsx(u,{label:"Agents running",value:`${d.running||0} / ${d.total||0}`,trend:(d.running||0)>0?"up":"flat",delta:`${d.idle||0} idle · ${d.error||0} error`}),e.jsx(u,{label:"Tokens (24h)",value:p.last24h?`${(p.last24h/1e6).toFixed(1)}M`:"—",trend:p.trend||"flat",delta:"last 24h",sparkline:c.length>=2?e.jsx("span",{"data-testid":"overview-tokens-sparkline",children:e.jsx(q,{data:c,width:140,height:28})}):void 0})]}),e.jsxs("div",{style:{display:"grid",gridTemplateColumns:"1fr 1fr",gap:"var(--space-4)"},children:[e.jsxs(f,{children:[e.jsx(x,{title:"Recent activity"}),e.jsx(j,{children:e.jsx(F,{items:w.length?w:[]})})]}),e.jsxs(f,{children:[e.jsx(x,{title:"Needs attention"}),e.jsx(j,{children:e.jsx(m,{gap:2,children:(o.needsAttention||[]).length===0?e.jsxs("div",{role:"status",style:{padding:"var(--space-3)",border:"1px solid color-mix(in oklch, var(--success) 40%, var(--border))",borderRadius:"var(--radius-md)",background:"color-mix(in oklch, var(--success) 8%, var(--surface-0))",color:"var(--success)",fontSize:"var(--fs-13)",display:"flex",alignItems:"center",gap:"var(--space-2)"},children:[e.jsx(z,{size:14,"aria-hidden":!0}),"All clear — nothing needs attention."]}):(o.needsAttention||[]).map((s,a)=>{const r=String(s.label||"").toLowerCase().includes("fail")?"danger":String(s.label||"").toLowerCase().includes("block")||String(s.label||"").toLowerCase().includes("risk")?"warning":"info",W=r==="danger"?k:r==="warning"?k:y;return e.jsxs("div",{role:"alert",style:{padding:"var(--space-3)",border:`1px solid color-mix(in oklch, var(--${r}) 40%, var(--border))`,borderRadius:"var(--radius-md)",background:`color-mix(in oklch, var(--${r}) 8%, var(--surface-0))`,display:"flex",alignItems:"flex-start",gap:"var(--space-2)"},children:[e.jsx(W,{size:16,"aria-hidden":!0,style:{color:`var(--${r})`,marginTop:2,flexShrink:0}}),e.jsxs("div",{style:{flex:1,minWidth:0},children:[e.jsx("div",{style:{fontSize:"var(--fs-13)",fontWeight:600,color:"var(--fg)"},children:s.label}),e.jsx("div",{style:{fontSize:"var(--fs-12)",color:"var(--fg-muted)",marginTop:2},children:s.value}),typeof s.hint=="string"&&s.hint.length>0&&e.jsx("div",{style:{fontSize:"var(--fs-12)",color:"var(--fg-muted)",marginTop:2},children:s.hint})]})]},a)})})})]})]}),Array.isArray(($=t.data)==null?void 0:$.projects)&&t.data.projects.length>0&&e.jsxs(f,{children:[e.jsx(x,{title:"Active projects"}),e.jsx(j,{children:e.jsx(m,{gap:1,children:t.data.projects.map(s=>{var a,r;return e.jsxs("div",{style:{padding:"var(--space-2) var(--space-3)",border:"1px solid var(--border)",borderRadius:"var(--radius-md)",background:"var(--surface-0)",display:"flex",alignItems:"center",justifyContent:"space-between",gap:"var(--space-2)"},children:[e.jsxs("div",{style:{minWidth:0},children:[e.jsx("div",{style:{fontSize:"var(--fs-13)",fontWeight:600},children:s.name||s.id}),e.jsx("code",{style:{fontFamily:"var(--font-mono)",fontSize:"var(--fs-12)",color:"var(--fg-muted)"},children:s.cwd})]}),((r=(a=t.data)==null?void 0:a.activeProject)==null?void 0:r.id)===s.id&&e.jsx(A,{tone:"success",dot:!0,children:"active"})]},s.id)})})})]})]})}export{Z as OverviewView};
@@ -1 +1 @@
1
- {"version":3,"file":"OverviewView-BsuPxwnB.js","sources":["../../src/web/v8/views/Overview/OverviewView.tsx"],"sourcesContent":["import { useMemo, useState, useCallback } from 'react';\nimport {\n GitMerge,\n GitPullRequest,\n AlertTriangle,\n CheckCircle2,\n Bot,\n Wrench,\n Activity as ActivityIcon,\n type LucideIcon,\n} from 'lucide-react';\nimport { Stack } from '../../ui/primitives/Stack.js';\nimport { StatTile, StatGrid } from '../../ui/data/StatTile.js';\nimport { Card, CardBody, CardHeader } from '../../ui/data/Card.js';\nimport { Badge } from '../../ui/data/Badge.js';\nimport { ActivityFeed, type ActivityItem } from '../../ui/activity/ActivityFeed.js';\nimport { ViewHeader } from '../../ui/data/ViewHeader.js';\nimport { Sparkline } from '../../ui/data/Sparkline.js';\nimport { useFetch } from '../../data/useFetch.js';\nimport { useWsMessage } from '../../data/useWebSocket.js';\nimport type { ActivityEvent, Snapshot } from '../../data/types.js';\n\n/**\n * OverviewView — the home/dashboard landing page.\n *\n * S10 — pulls `/api/snapshot` for the headline counts and `/api/activity`\n * for the recent feed. Live-tails via `useWsMessage('activity:new', …)`\n * so a new event lands in the feed without a re-fetch.\n */\n\nconst ICON_MAP: Record<string, LucideIcon> = {\n 'git.merge': GitMerge,\n 'git.pull-request': GitPullRequest,\n 'git.merge.pr': GitMerge,\n 'task.completed': CheckCircle2,\n 'task.failed': AlertTriangle,\n 'agent.run': Bot,\n 'agent.tool': Wrench,\n 'agent.message': ActivityIcon,\n};\n\nfunction iconFor(key: string | undefined): JSX.Element {\n const Icon = ICON_MAP[key || ''] || ActivityIcon;\n return <Icon size={14} aria-hidden=\"true\" />;\n}\n\nfunction eventToItem(e: ActivityEvent, idx: number): ActivityItem {\n const ts = typeof e.ts === 'number' ? e.ts : Date.now();\n const meta = e.meta || (ts ? new Date(ts).toLocaleString() : '');\n return {\n id: e.id || `${ts}-${idx}`,\n icon: iconFor(e.iconKey || e.kind),\n title: e.title || e.description || e.kind || 'event',\n description: e.description,\n meta,\n tone: e.tone,\n };\n}\n\nexport function OverviewView(): JSX.Element {\n const snapshot = useFetch<Snapshot>('/api/snapshot');\n const activity = useFetch<{ events?: ActivityEvent[] }>('/api/activity?limit=10');\n\n // v10-S2 — real time-series trends. `/api/usage?range=24h` returns a\n // `daily` array (one bucket per day). We thread the token counts into\n // a Sparkline slot on the Tokens StatTile so the KPI isn't point-in-\n // time any more. Snapshot / activity / usage are independent requests\n // — if usage 500s the tile still renders.\n interface UsageDay { date?: string; tokens?: number }\n interface UsageResponse { daily?: UsageDay[]; totals?: { tokens?: number } }\n // v10.0.3-S2 — `range=24h` only ever returns 1 daily bucket so the\n // sparkline never renders. Use 7d so the time-series trends have ≥2\n // points even on freshly seeded servers (umbrella criterion #5).\n const usage = useFetch<UsageResponse>('/api/usage?range=7d');\n const tokenSeries = useMemo<readonly number[]>(() => {\n const days = usage.data?.daily ?? [];\n if (days.length < 2) return [];\n return days.map((d) => (typeof d.tokens === 'number' ? d.tokens : 0));\n }, [usage.data]);\n\n // Live-tail new events. Keep them in a small in-memory ring buffer.\n const [liveEvents, setLiveEvents] = useState<ActivityEvent[]>([]);\n const onEvent = useCallback((msg: { event?: ActivityEvent } & Record<string, unknown>) => {\n const ev = msg.event;\n if (!ev) return;\n setLiveEvents((prev) => [...prev.slice(-19), ev]);\n }, []);\n useWsMessage('activity:new', onEvent);\n\n const items = useMemo<ActivityItem[]>(() => {\n const list = [\n ...liveEvents.slice(-10).reverse(),\n ...((activity.data?.events || []).slice(0, 10)),\n ];\n return list.map(eventToItem);\n }, [activity.data, liveEvents]);\n\n const ov = snapshot.data?.overview || {};\n const tasks = ov.tasks || {};\n const goals = ov.goals || {};\n const agents = ov.agents || {};\n const tokens = ov.tokens || {};\n\n return (\n <Stack gap={5} data-testid=\"overview-view\">\n <ViewHeader\n title=\"Overview\"\n description=\"What's moving in the harness right now.\"\n actions={\n <Badge tone={snapshot.error ? 'danger' : 'success'} dot>\n {snapshot.loading ? 'Loading' : snapshot.error ? 'Offline' : 'Live'}\n </Badge>\n }\n />\n\n <StatGrid cols={4}>\n <StatTile\n label=\"Active tasks\"\n value={String((tasks.queued || 0) + (tasks.active || 0))}\n trend={(tasks.active || 0) > (tasks.queued || 0) ? 'up' : 'flat'}\n delta={`${tasks.done || 0} done · ${tasks.blocked || 0} blocked`}\n />\n <StatTile\n label=\"Goals at risk\"\n value={`${goals.atRisk || 0} / ${goals.total || 0}`}\n trend={(goals.atRisk || 0) > 0 ? 'down' : 'flat'}\n delta={`${goals.done || 0} done`}\n />\n <StatTile\n label=\"Agents running\"\n value={`${agents.running || 0} / ${agents.total || 0}`}\n trend={(agents.running || 0) > 0 ? 'up' : 'flat'}\n delta={`${agents.idle || 0} idle · ${agents.error || 0} error`}\n />\n <StatTile\n label=\"Tokens (24h)\"\n value={tokens.last24h ? `${(tokens.last24h / 1_000_000).toFixed(1)}M` : '—'}\n trend={tokens.trend || 'flat'}\n delta=\"last 24h\"\n sparkline={\n tokenSeries.length >= 2 ? (\n <span data-testid=\"overview-tokens-sparkline\">\n <Sparkline data={tokenSeries} width={140} height={28} />\n </span>\n ) : undefined\n }\n />\n </StatGrid>\n\n <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 'var(--space-4)' }}>\n <Card>\n <CardHeader title=\"Recent activity\" />\n <CardBody>\n <ActivityFeed items={items.length ? items : []} />\n </CardBody>\n </Card>\n <Card>\n <CardHeader title=\"Needs attention\" />\n <CardBody>\n <Stack gap={2}>\n {(ov.needsAttention || []).length === 0 ? (\n <div\n role=\"status\"\n style={{\n padding: 'var(--space-3)',\n border: '1px solid color-mix(in oklch, var(--success) 40%, var(--border))',\n borderRadius: 'var(--radius-md)',\n background: 'color-mix(in oklch, var(--success) 8%, var(--surface-0))',\n color: 'var(--success)',\n fontSize: 'var(--fs-13)',\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-2)',\n }}\n >\n <CheckCircle2 size={14} aria-hidden />\n All clear — nothing needs attention.\n </div>\n ) : (\n (ov.needsAttention || []).map((n, i) => {\n const tone =\n String(n.label || '').toLowerCase().includes('fail') ? 'danger' :\n String(n.label || '').toLowerCase().includes('block') ? 'warning' :\n String(n.label || '').toLowerCase().includes('risk') ? 'warning' :\n 'info';\n const Icon =\n tone === 'danger' ? AlertTriangle :\n tone === 'warning' ? AlertTriangle :\n ActivityIcon;\n return (\n <div\n key={i}\n role=\"alert\"\n style={{\n padding: 'var(--space-3)',\n border: `1px solid color-mix(in oklch, var(--${tone}) 40%, var(--border))`,\n borderRadius: 'var(--radius-md)',\n background: `color-mix(in oklch, var(--${tone}) 8%, var(--surface-0))`,\n display: 'flex',\n alignItems: 'flex-start',\n gap: 'var(--space-2)',\n }}\n >\n <Icon size={16} aria-hidden style={{ color: `var(--${tone})`, marginTop: 2, flexShrink: 0 }} />\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontSize: 'var(--fs-13)', fontWeight: 600, color: 'var(--fg)' }}>{n.label}</div>\n <div style={{ fontSize: 'var(--fs-12)', color: 'var(--fg-muted)', marginTop: 2 }}>{n.value}</div>\n {typeof n.hint === 'string' && n.hint.length > 0 && (\n <div style={{ fontSize: 'var(--fs-12)', color: 'var(--fg-muted)', marginTop: 2 }}>{n.hint}</div>\n )}\n </div>\n </div>\n );\n })\n )}\n </Stack>\n </CardBody>\n </Card>\n </div>\n\n {Array.isArray(snapshot.data?.projects) && snapshot.data.projects.length > 0 && (\n <Card>\n <CardHeader title=\"Active projects\" />\n <CardBody>\n <Stack gap={1}>\n {snapshot.data.projects.map((p) => (\n <div\n key={p.id}\n style={{\n padding: 'var(--space-2) var(--space-3)',\n border: '1px solid var(--border)',\n borderRadius: 'var(--radius-md)',\n background: 'var(--surface-0)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: 'var(--space-2)',\n }}\n >\n <div style={{ minWidth: 0 }}>\n <div style={{ fontSize: 'var(--fs-13)', fontWeight: 600 }}>{p.name || p.id}</div>\n <code style={{ fontFamily: 'var(--font-mono)', fontSize: 'var(--fs-12)', color: 'var(--fg-muted)' }}>{p.cwd}</code>\n </div>\n {snapshot.data?.activeProject?.id === p.id && (\n <Badge tone=\"success\" dot>active</Badge>\n )}\n </div>\n ))}\n </Stack>\n </CardBody>\n </Card>\n )}\n </Stack>\n );\n}"],"names":["ICON_MAP","GitMerge","GitPullRequest","CheckCircle2","AlertTriangle","Bot","Wrench","ActivityIcon","iconFor","key","Icon","jsx","eventToItem","e","idx","ts","meta","OverviewView","snapshot","useFetch","activity","usage","tokenSeries","useMemo","days","_a","d","liveEvents","setLiveEvents","useState","onEvent","useCallback","msg","ev","prev","useWsMessage","items","ov","tasks","goals","agents","tokens","jsxs","Stack","ViewHeader","Badge","StatGrid","StatTile","Sparkline","Card","CardHeader","CardBody","ActivityFeed","n","i","tone","_b","p"],"mappings":"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"}
1
+ {"version":3,"file":"OverviewView-CTI2spTd.js","sources":["../../src/web/v8/views/Overview/OverviewView.tsx"],"sourcesContent":["import { useMemo, useState, useCallback } from 'react';\nimport {\n GitMerge,\n GitPullRequest,\n AlertTriangle,\n CheckCircle2,\n Bot,\n Wrench,\n Activity as ActivityIcon,\n type LucideIcon,\n} from 'lucide-react';\nimport { Stack } from '../../ui/primitives/Stack.js';\nimport { StatTile, StatGrid } from '../../ui/data/StatTile.js';\nimport { Card, CardBody, CardHeader } from '../../ui/data/Card.js';\nimport { Badge } from '../../ui/data/Badge.js';\nimport { ActivityFeed, type ActivityItem } from '../../ui/activity/ActivityFeed.js';\nimport { ViewHeader } from '../../ui/data/ViewHeader.js';\nimport { Sparkline } from '../../ui/data/Sparkline.js';\nimport { useFetch } from '../../data/useFetch.js';\nimport { useWsMessage } from '../../data/useWebSocket.js';\nimport type { ActivityEvent, Snapshot } from '../../data/types.js';\n\n/**\n * OverviewView — the home/dashboard landing page.\n *\n * S10 — pulls `/api/snapshot` for the headline counts and `/api/activity`\n * for the recent feed. Live-tails via `useWsMessage('activity:new', …)`\n * so a new event lands in the feed without a re-fetch.\n */\n\nconst ICON_MAP: Record<string, LucideIcon> = {\n 'git.merge': GitMerge,\n 'git.pull-request': GitPullRequest,\n 'git.merge.pr': GitMerge,\n 'task.completed': CheckCircle2,\n 'task.failed': AlertTriangle,\n 'agent.run': Bot,\n 'agent.tool': Wrench,\n 'agent.message': ActivityIcon,\n};\n\nfunction iconFor(key: string | undefined): JSX.Element {\n const Icon = ICON_MAP[key || ''] || ActivityIcon;\n return <Icon size={14} aria-hidden=\"true\" />;\n}\n\nfunction eventToItem(e: ActivityEvent, idx: number): ActivityItem {\n const ts = typeof e.ts === 'number' ? e.ts : Date.now();\n const meta = e.meta || (ts ? new Date(ts).toLocaleString() : '');\n return {\n id: e.id || `${ts}-${idx}`,\n icon: iconFor(e.iconKey || e.kind),\n title: e.title || e.description || e.kind || 'event',\n description: e.description,\n meta,\n tone: e.tone,\n };\n}\n\nexport function OverviewView(): JSX.Element {\n const snapshot = useFetch<Snapshot>('/api/snapshot');\n const activity = useFetch<{ events?: ActivityEvent[] }>('/api/activity?limit=10');\n\n // v10-S2 — real time-series trends. `/api/usage?range=24h` returns a\n // `daily` array (one bucket per day). We thread the token counts into\n // a Sparkline slot on the Tokens StatTile so the KPI isn't point-in-\n // time any more. Snapshot / activity / usage are independent requests\n // — if usage 500s the tile still renders.\n interface UsageDay { date?: string; tokens?: number }\n interface UsageResponse { daily?: UsageDay[]; totals?: { tokens?: number } }\n // v10.0.3-S2 — `range=24h` only ever returns 1 daily bucket so the\n // sparkline never renders. Use 7d so the time-series trends have ≥2\n // points even on freshly seeded servers (umbrella criterion #5).\n const usage = useFetch<UsageResponse>('/api/usage?range=7d');\n const tokenSeries = useMemo<readonly number[]>(() => {\n const days = usage.data?.daily ?? [];\n if (days.length < 2) return [];\n return days.map((d) => (typeof d.tokens === 'number' ? d.tokens : 0));\n }, [usage.data]);\n\n // Live-tail new events. Keep them in a small in-memory ring buffer.\n const [liveEvents, setLiveEvents] = useState<ActivityEvent[]>([]);\n const onEvent = useCallback((msg: { event?: ActivityEvent } & Record<string, unknown>) => {\n const ev = msg.event;\n if (!ev) return;\n setLiveEvents((prev) => [...prev.slice(-19), ev]);\n }, []);\n useWsMessage('activity:new', onEvent);\n\n const items = useMemo<ActivityItem[]>(() => {\n const list = [\n ...liveEvents.slice(-10).reverse(),\n ...((activity.data?.events || []).slice(0, 10)),\n ];\n return list.map(eventToItem);\n }, [activity.data, liveEvents]);\n\n const ov = snapshot.data?.overview || {};\n const tasks = ov.tasks || {};\n const goals = ov.goals || {};\n const agents = ov.agents || {};\n const tokens = ov.tokens || {};\n\n return (\n <Stack gap={5} data-testid=\"overview-view\">\n <ViewHeader\n title=\"Overview\"\n description=\"What's moving in the harness right now.\"\n actions={\n <Badge tone={snapshot.error ? 'danger' : 'success'} dot>\n {snapshot.loading ? 'Loading' : snapshot.error ? 'Offline' : 'Live'}\n </Badge>\n }\n />\n\n <StatGrid cols={4}>\n <StatTile\n label=\"Active tasks\"\n value={String((tasks.queued || 0) + (tasks.active || 0))}\n trend={(tasks.active || 0) > (tasks.queued || 0) ? 'up' : 'flat'}\n delta={`${tasks.done || 0} done · ${tasks.blocked || 0} blocked`}\n />\n <StatTile\n label=\"Goals at risk\"\n value={`${goals.atRisk || 0} / ${goals.total || 0}`}\n trend={(goals.atRisk || 0) > 0 ? 'down' : 'flat'}\n delta={`${goals.done || 0} done`}\n />\n <StatTile\n label=\"Agents running\"\n value={`${agents.running || 0} / ${agents.total || 0}`}\n trend={(agents.running || 0) > 0 ? 'up' : 'flat'}\n delta={`${agents.idle || 0} idle · ${agents.error || 0} error`}\n />\n <StatTile\n label=\"Tokens (24h)\"\n value={tokens.last24h ? `${(tokens.last24h / 1_000_000).toFixed(1)}M` : '—'}\n trend={tokens.trend || 'flat'}\n delta=\"last 24h\"\n sparkline={\n tokenSeries.length >= 2 ? (\n <span data-testid=\"overview-tokens-sparkline\">\n <Sparkline data={tokenSeries} width={140} height={28} />\n </span>\n ) : undefined\n }\n />\n </StatGrid>\n\n <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 'var(--space-4)' }}>\n <Card>\n <CardHeader title=\"Recent activity\" />\n <CardBody>\n <ActivityFeed items={items.length ? items : []} />\n </CardBody>\n </Card>\n <Card>\n <CardHeader title=\"Needs attention\" />\n <CardBody>\n <Stack gap={2}>\n {(ov.needsAttention || []).length === 0 ? (\n <div\n role=\"status\"\n style={{\n padding: 'var(--space-3)',\n border: '1px solid color-mix(in oklch, var(--success) 40%, var(--border))',\n borderRadius: 'var(--radius-md)',\n background: 'color-mix(in oklch, var(--success) 8%, var(--surface-0))',\n color: 'var(--success)',\n fontSize: 'var(--fs-13)',\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-2)',\n }}\n >\n <CheckCircle2 size={14} aria-hidden />\n All clear — nothing needs attention.\n </div>\n ) : (\n (ov.needsAttention || []).map((n, i) => {\n const tone =\n String(n.label || '').toLowerCase().includes('fail') ? 'danger' :\n String(n.label || '').toLowerCase().includes('block') ? 'warning' :\n String(n.label || '').toLowerCase().includes('risk') ? 'warning' :\n 'info';\n const Icon =\n tone === 'danger' ? AlertTriangle :\n tone === 'warning' ? AlertTriangle :\n ActivityIcon;\n return (\n <div\n key={i}\n role=\"alert\"\n style={{\n padding: 'var(--space-3)',\n border: `1px solid color-mix(in oklch, var(--${tone}) 40%, var(--border))`,\n borderRadius: 'var(--radius-md)',\n background: `color-mix(in oklch, var(--${tone}) 8%, var(--surface-0))`,\n display: 'flex',\n alignItems: 'flex-start',\n gap: 'var(--space-2)',\n }}\n >\n <Icon size={16} aria-hidden style={{ color: `var(--${tone})`, marginTop: 2, flexShrink: 0 }} />\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontSize: 'var(--fs-13)', fontWeight: 600, color: 'var(--fg)' }}>{n.label}</div>\n <div style={{ fontSize: 'var(--fs-12)', color: 'var(--fg-muted)', marginTop: 2 }}>{n.value}</div>\n {typeof n.hint === 'string' && n.hint.length > 0 && (\n <div style={{ fontSize: 'var(--fs-12)', color: 'var(--fg-muted)', marginTop: 2 }}>{n.hint}</div>\n )}\n </div>\n </div>\n );\n })\n )}\n </Stack>\n </CardBody>\n </Card>\n </div>\n\n {Array.isArray(snapshot.data?.projects) && snapshot.data.projects.length > 0 && (\n <Card>\n <CardHeader title=\"Active projects\" />\n <CardBody>\n <Stack gap={1}>\n {snapshot.data.projects.map((p) => (\n <div\n key={p.id}\n style={{\n padding: 'var(--space-2) var(--space-3)',\n border: '1px solid var(--border)',\n borderRadius: 'var(--radius-md)',\n background: 'var(--surface-0)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: 'var(--space-2)',\n }}\n >\n <div style={{ minWidth: 0 }}>\n <div style={{ fontSize: 'var(--fs-13)', fontWeight: 600 }}>{p.name || p.id}</div>\n <code style={{ fontFamily: 'var(--font-mono)', fontSize: 'var(--fs-12)', color: 'var(--fg-muted)' }}>{p.cwd}</code>\n </div>\n {snapshot.data?.activeProject?.id === p.id && (\n <Badge tone=\"success\" dot>active</Badge>\n )}\n </div>\n ))}\n </Stack>\n </CardBody>\n </Card>\n )}\n </Stack>\n );\n}"],"names":["ICON_MAP","GitMerge","GitPullRequest","CheckCircle2","AlertTriangle","Bot","Wrench","ActivityIcon","iconFor","key","Icon","jsx","eventToItem","e","idx","ts","meta","OverviewView","snapshot","useFetch","activity","usage","tokenSeries","useMemo","days","_a","d","liveEvents","setLiveEvents","useState","onEvent","useCallback","msg","ev","prev","useWsMessage","items","ov","tasks","goals","agents","tokens","jsxs","Stack","ViewHeader","Badge","StatGrid","StatTile","Sparkline","Card","CardHeader","CardBody","ActivityFeed","n","i","tone","_b","p"],"mappings":"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"}
@@ -1 +1 @@
1
- import{r as v,j as r}from"./cmdk-DVxytz9i.js";import{g}from"./main-CokHAXWM.js";const h={accent:"var(--accent)",info:"var(--info)",success:"var(--success)",warning:"var(--warning)",danger:"var(--danger)"},x=v.forwardRef(function(s,e){const{value:t,max:i=1,tone:n="accent",showLabel:o,height:c=6,className:d,style:l,...u}=s,a=Math.min(100,Math.max(0,t/i*100));return r.jsxs("div",{ref:e,className:g("v8-progress",d),style:{position:"relative",display:"flex",alignItems:"center",gap:"var(--space-2)",...l},...u,children:[r.jsx("div",{style:{flex:1,height:c,background:"var(--surface-2)",borderRadius:"var(--radius-pill)",overflow:"hidden"},children:r.jsx("div",{style:{width:`${a}%`,height:"100%",background:h[n],borderRadius:"var(--radius-pill)",transition:"width var(--motion-base) var(--ease-out)"}})}),o===!0&&r.jsxs("span",{style:{fontSize:"var(--fs-12)",color:"var(--fg-muted)",fontVariantNumeric:"tabular-nums",minWidth:32,textAlign:"right"},children:[Math.round(a),"%"]})]})});export{x as P};
1
+ import{r as v,j as r}from"./cmdk-DVxytz9i.js";import{g}from"./main-CyAIirBB.js";const h={accent:"var(--accent)",info:"var(--info)",success:"var(--success)",warning:"var(--warning)",danger:"var(--danger)"},x=v.forwardRef(function(s,e){const{value:t,max:i=1,tone:n="accent",showLabel:o,height:c=6,className:d,style:l,...u}=s,a=Math.min(100,Math.max(0,t/i*100));return r.jsxs("div",{ref:e,className:g("v8-progress",d),style:{position:"relative",display:"flex",alignItems:"center",gap:"var(--space-2)",...l},...u,children:[r.jsx("div",{style:{flex:1,height:c,background:"var(--surface-2)",borderRadius:"var(--radius-pill)",overflow:"hidden"},children:r.jsx("div",{style:{width:`${a}%`,height:"100%",background:h[n],borderRadius:"var(--radius-pill)",transition:"width var(--motion-base) var(--ease-out)"}})}),o===!0&&r.jsxs("span",{style:{fontSize:"var(--fs-12)",color:"var(--fg-muted)",fontVariantNumeric:"tabular-nums",minWidth:32,textAlign:"right"},children:[Math.round(a),"%"]})]})});export{x as P};
@@ -1 +1 @@
1
- {"version":3,"file":"ProgressBar-DlZTETEn.js","sources":["../../src/web/v8/ui/data/ProgressBar.tsx"],"sourcesContent":["import { forwardRef, type HTMLAttributes } from 'react';\nimport { cx } from '../utils/cx.js';\n\n/**\n * ProgressBar — linear progress indicator.\n *\n * Use cases: Goal progress, task completion, capacity usage.\n * For circular progress use the inline circle ring (S9) or a third-party.\n */\n\nexport interface ProgressBarProps extends HTMLAttributes<HTMLDivElement> {\n /** 0..1 (or 0..100 if `max` is 100). */\n value: number;\n max?: number;\n /** Tone color for the filled portion. */\n tone?: 'accent' | 'info' | 'success' | 'warning' | 'danger';\n /** Render the value as a percentage label on the right. */\n showLabel?: boolean;\n /** Bar height in px. */\n height?: number;\n}\n\nconst TONE_BG: Record<NonNullable<ProgressBarProps['tone']>, string> = {\n accent: 'var(--accent)',\n info: 'var(--info)',\n success: 'var(--success)',\n warning: 'var(--warning)',\n danger: 'var(--danger)',\n};\n\nexport const ProgressBar = forwardRef<HTMLDivElement, ProgressBarProps>(function ProgressBar(\n props,\n ref,\n) {\n const {\n value,\n max = 1,\n tone = 'accent',\n showLabel,\n height = 6,\n className,\n style,\n ...rest\n } = props;\n const pct = Math.min(100, Math.max(0, (value / max) * 100));\n return (\n <div\n ref={ref}\n className={cx('v8-progress', className)}\n style={{\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-2)',\n ...style,\n }}\n {...rest}\n >\n <div\n style={{\n flex: 1,\n height,\n background: 'var(--surface-2)',\n borderRadius: 'var(--radius-pill)',\n overflow: 'hidden',\n }}\n >\n <div\n style={{\n width: `${pct}%`,\n height: '100%',\n background: TONE_BG[tone],\n borderRadius: 'var(--radius-pill)',\n transition: 'width var(--motion-base) var(--ease-out)',\n }}\n />\n </div>\n {showLabel === true && (\n <span\n style={{\n fontSize: 'var(--fs-12)',\n color: 'var(--fg-muted)',\n fontVariantNumeric: 'tabular-nums',\n minWidth: 32,\n textAlign: 'right',\n }}\n >\n {Math.round(pct)}%\n </span>\n )}\n </div>\n );\n});"],"names":["TONE_BG","ProgressBar","forwardRef","props","ref","value","max","tone","showLabel","height","className","style","rest","pct","jsxs","cx","jsx"],"mappings":"gFAsBA,MAAMA,EAAiE,CACrE,OAAQ,gBACR,KAAM,cACN,QAAS,iBACT,QAAS,iBACT,OAAQ,eACV,EAEaC,EAAcC,EAAAA,WAA6C,SACtEC,EACAC,EACA,CACA,KAAM,CACJ,MAAAC,EACA,IAAAC,EAAM,EACN,KAAAC,EAAO,SACP,UAAAC,EACA,OAAAC,EAAS,EACT,UAAAC,EACA,MAAAC,EACA,GAAGC,CAAA,EACDT,EACEU,EAAM,KAAK,IAAI,IAAK,KAAK,IAAI,EAAIR,EAAQC,EAAO,GAAG,CAAC,EAC1D,OACEQ,EAAAA,KAAC,MAAA,CACC,IAAAV,EACA,UAAWW,EAAG,cAAeL,CAAS,EACtC,MAAO,CACL,SAAU,WACV,QAAS,OACT,WAAY,SACZ,IAAK,iBACL,GAAGC,CAAA,EAEJ,GAAGC,EAEJ,SAAA,CAAAI,EAAAA,IAAC,MAAA,CACC,MAAO,CACL,KAAM,EACN,OAAAP,EACA,WAAY,mBACZ,aAAc,qBACd,SAAU,QAAA,EAGZ,SAAAO,EAAAA,IAAC,MAAA,CACC,MAAO,CACL,MAAO,GAAGH,CAAG,IACb,OAAQ,OACR,WAAYb,EAAQO,CAAI,EACxB,aAAc,qBACd,WAAY,0CAAA,CACd,CAAA,CACF,CAAA,EAEDC,IAAc,IACbM,EAAAA,KAAC,OAAA,CACC,MAAO,CACL,SAAU,eACV,MAAO,kBACP,mBAAoB,eACpB,SAAU,GACV,UAAW,OAAA,EAGZ,SAAA,CAAA,KAAK,MAAMD,CAAG,EAAE,GAAA,CAAA,CAAA,CACnB,CAAA,CAAA,CAIR,CAAC"}
1
+ {"version":3,"file":"ProgressBar-D3lkz42E.js","sources":["../../src/web/v8/ui/data/ProgressBar.tsx"],"sourcesContent":["import { forwardRef, type HTMLAttributes } from 'react';\nimport { cx } from '../utils/cx.js';\n\n/**\n * ProgressBar — linear progress indicator.\n *\n * Use cases: Goal progress, task completion, capacity usage.\n * For circular progress use the inline circle ring (S9) or a third-party.\n */\n\nexport interface ProgressBarProps extends HTMLAttributes<HTMLDivElement> {\n /** 0..1 (or 0..100 if `max` is 100). */\n value: number;\n max?: number;\n /** Tone color for the filled portion. */\n tone?: 'accent' | 'info' | 'success' | 'warning' | 'danger';\n /** Render the value as a percentage label on the right. */\n showLabel?: boolean;\n /** Bar height in px. */\n height?: number;\n}\n\nconst TONE_BG: Record<NonNullable<ProgressBarProps['tone']>, string> = {\n accent: 'var(--accent)',\n info: 'var(--info)',\n success: 'var(--success)',\n warning: 'var(--warning)',\n danger: 'var(--danger)',\n};\n\nexport const ProgressBar = forwardRef<HTMLDivElement, ProgressBarProps>(function ProgressBar(\n props,\n ref,\n) {\n const {\n value,\n max = 1,\n tone = 'accent',\n showLabel,\n height = 6,\n className,\n style,\n ...rest\n } = props;\n const pct = Math.min(100, Math.max(0, (value / max) * 100));\n return (\n <div\n ref={ref}\n className={cx('v8-progress', className)}\n style={{\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-2)',\n ...style,\n }}\n {...rest}\n >\n <div\n style={{\n flex: 1,\n height,\n background: 'var(--surface-2)',\n borderRadius: 'var(--radius-pill)',\n overflow: 'hidden',\n }}\n >\n <div\n style={{\n width: `${pct}%`,\n height: '100%',\n background: TONE_BG[tone],\n borderRadius: 'var(--radius-pill)',\n transition: 'width var(--motion-base) var(--ease-out)',\n }}\n />\n </div>\n {showLabel === true && (\n <span\n style={{\n fontSize: 'var(--fs-12)',\n color: 'var(--fg-muted)',\n fontVariantNumeric: 'tabular-nums',\n minWidth: 32,\n textAlign: 'right',\n }}\n >\n {Math.round(pct)}%\n </span>\n )}\n </div>\n );\n});"],"names":["TONE_BG","ProgressBar","forwardRef","props","ref","value","max","tone","showLabel","height","className","style","rest","pct","jsxs","cx","jsx"],"mappings":"gFAsBA,MAAMA,EAAiE,CACrE,OAAQ,gBACR,KAAM,cACN,QAAS,iBACT,QAAS,iBACT,OAAQ,eACV,EAEaC,EAAcC,EAAAA,WAA6C,SACtEC,EACAC,EACA,CACA,KAAM,CACJ,MAAAC,EACA,IAAAC,EAAM,EACN,KAAAC,EAAO,SACP,UAAAC,EACA,OAAAC,EAAS,EACT,UAAAC,EACA,MAAAC,EACA,GAAGC,CAAA,EACDT,EACEU,EAAM,KAAK,IAAI,IAAK,KAAK,IAAI,EAAIR,EAAQC,EAAO,GAAG,CAAC,EAC1D,OACEQ,EAAAA,KAAC,MAAA,CACC,IAAAV,EACA,UAAWW,EAAG,cAAeL,CAAS,EACtC,MAAO,CACL,SAAU,WACV,QAAS,OACT,WAAY,SACZ,IAAK,iBACL,GAAGC,CAAA,EAEJ,GAAGC,EAEJ,SAAA,CAAAI,EAAAA,IAAC,MAAA,CACC,MAAO,CACL,KAAM,EACN,OAAAP,EACA,WAAY,mBACZ,aAAc,qBACd,SAAU,QAAA,EAGZ,SAAAO,EAAAA,IAAC,MAAA,CACC,MAAO,CACL,MAAO,GAAGH,CAAG,IACb,OAAQ,OACR,WAAYb,EAAQO,CAAI,EACxB,aAAc,qBACd,WAAY,0CAAA,CACd,CAAA,CACF,CAAA,EAEDC,IAAc,IACbM,EAAAA,KAAC,OAAA,CACC,MAAO,CACL,SAAU,eACV,MAAO,kBACP,mBAAoB,eACpB,SAAU,GACV,UAAW,OAAA,EAGZ,SAAA,CAAA,KAAK,MAAMD,CAAG,EAAE,GAAA,CAAA,CAAA,CACnB,CAAA,CAAA,CAIR,CAAC"}
@@ -1,4 +1,4 @@
1
- import{r as n,j as e}from"./cmdk-DVxytz9i.js";import{c as P,u as B,a as L,S as d,I as o,d as r,w as N,h as w,B as V,i as l,F as h}from"./main-CokHAXWM.js";import{V as q}from"./ViewHeader-BzYZ3m-e.js";import{L as H}from"./ListHeader-TjPJrUfY.js";import{C as W,b as J}from"./Card-B0imhxMk.js";import{I as b}from"./Input-Bgzxf_Hu.js";import{S as Z,a as G,b as K}from"./Sheet-DnDid58j.js";import{E as Q}from"./EmptyState-BjgVOrcO.js";import{R as U}from"./refresh-ccw-DNrXX9gB.js";import{T as X}from"./trash-2-DZDjEnNh.js";/**
1
+ import{r as n,j as e}from"./cmdk-DVxytz9i.js";import{c as P,u as B,a as L,S as d,I as o,d as r,w as N,h as w,B as V,i as l,F as h}from"./main-CyAIirBB.js";import{V as q}from"./ViewHeader-V_SzbUpw.js";import{L as H}from"./ListHeader-Ak9WJd39.js";import{C as W,b as J}from"./Card-D9019JOD.js";import{I as b}from"./Input-BSa3lh_-.js";import{S as Z,a as G,b as K}from"./Sheet-Cztsvgse.js";import{E as Q}from"./EmptyState-DLmtBX_6.js";import{R as U}from"./refresh-ccw-BCXx86xp.js";import{T as X}from"./trash-2-2NsyTU8c.js";/**
2
2
  * @license lucide-react v0.460.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectsView-Soxtmpet.js","sources":["../../../node_modules/lucide-react/dist/esm/icons/folder-plus.js","../../../node_modules/lucide-react/dist/esm/icons/scan-search.js","../../src/web/v8/views/Projects/ProjectsView.tsx"],"sourcesContent":["/**\n * @license lucide-react v0.460.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst FolderPlus = createLucideIcon(\"FolderPlus\", [\n [\"path\", { d: \"M12 10v6\", key: \"1bos4e\" }],\n [\"path\", { d: \"M9 13h6\", key: \"1uhe8q\" }],\n [\n \"path\",\n {\n d: \"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z\",\n key: \"1kt360\"\n }\n ]\n]);\n\nexport { FolderPlus as default };\n//# sourceMappingURL=folder-plus.js.map\n","/**\n * @license lucide-react v0.460.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst ScanSearch = createLucideIcon(\"ScanSearch\", [\n [\"path\", { d: \"M3 7V5a2 2 0 0 1 2-2h2\", key: \"aa7l1z\" }],\n [\"path\", { d: \"M17 3h2a2 2 0 0 1 2 2v2\", key: \"4qcy5o\" }],\n [\"path\", { d: \"M21 17v2a2 2 0 0 1-2 2h-2\", key: \"6vwrx8\" }],\n [\"path\", { d: \"M7 21H5a2 2 0 0 1-2-2v-2\", key: \"ioqczr\" }],\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"3\", key: \"1v7zrd\" }],\n [\"path\", { d: \"m16 16-1.9-1.9\", key: \"1dq9hf\" }]\n]);\n\nexport { ScanSearch as default };\n//# sourceMappingURL=scan-search.js.map\n","/**\n * v8/views/Projects/ProjectsView.tsx — Sprint S39, v9.3.0.\n *\n * The project registry surface. Shows every registered project,\n * lets the user mark one active, add by path (Sheet form), and\n * remove (inline confirm). Two utility actions sit in the header:\n * - Auto-detect: POST /api/projects/auto-detect (adds cwd)\n * - Scan: POST /api/projects/scan (walks the configured\n * dashboard.projectsDirectory and adds any new\n * roots it finds).\n *\n * Live refresh on the `project:change` WS event the server already\n * broadcasts from routes/projects.mjs.\n */\n\nimport { useCallback, useEffect, useState } from 'react';\nimport { FolderPlus, RefreshCcw, ScanSearch, Sparkles, Trash2 } from 'lucide-react';\nimport { Stack } from '../../ui/primitives/Stack.js';\nimport { Inline } from '../../ui/primitives/Inline.js';\nimport { ViewHeader } from '../../ui/data/ViewHeader.js';\nimport { ListHeader } from '../../ui/data/ListHeader.js';\nimport { Card, CardBody } from '../../ui/data/Card.js';\nimport { Button } from '../../ui/controls/Button.js';\nimport { Input } from '../../ui/controls/Input.js';\nimport { Sheet, SheetClose, SheetContent } from '../../ui/feedback/Sheet.js';\nimport { Skeleton } from '../../ui/feedback/Skeleton.js';\nimport { EmptyState } from '../../ui/feedback/EmptyState.js';\nimport { Badge } from '../../ui/data/Badge.js';\nimport { useFetch } from '../../data/useFetch.js';\nimport { useWsMessage } from '../../data/useWebSocket.js';\nimport { fetchJson, FetchError } from '../../data/fetcher.js';\nimport type { Project, WsMessage } from '../../data/types.js';\n\ninterface ProjectsPayload {\n projects: Project[];\n}\n\nexport function ProjectsView(): JSX.Element {\n const payload = useFetch<ProjectsPayload>('/api/projects');\n const [adding, setAdding] = useState<boolean>(false);\n const [newPath, setNewPath] = useState<string>('');\n const [newName, setNewName] = useState<string>('');\n const [busy, setBusy] = useState<boolean>(false);\n const [error, setError] = useState<string | null>(null);\n const [confirmDeleteId, setConfirmDeleteId] = useState<string | null>(null);\n\n const projects = payload.data?.projects ?? [];\n const activeId = projects.find((p) => p.active)?.id ?? null;\n\n const refresh = useCallback(() => { void payload.refetch(); }, [payload]);\n useWsMessage('project:change', refresh);\n\n const add = async (): Promise<void> => {\n const path = newPath.trim();\n if (!path || busy) return;\n setBusy(true);\n setError(null);\n try {\n await fetchJson<Project>('/api/projects', {\n method: 'POST',\n body: { path, name: newName.trim() || null },\n });\n setNewPath('');\n setNewName('');\n setAdding(false);\n refresh();\n } catch (err) {\n setError(err instanceof FetchError ? err.message : (err as Error).message);\n } finally {\n setBusy(false);\n }\n };\n\n const activate = async (id: string): Promise<void> => {\n setError(null);\n try {\n await fetchJson(`/api/projects/${id}/activate`, { method: 'POST', body: {} });\n refresh();\n } catch (err) {\n setError(err instanceof FetchError ? err.message : (err as Error).message);\n }\n };\n\n const remove = async (id: string): Promise<void> => {\n setError(null);\n try {\n await fetchJson(`/api/projects/${id}`, { method: 'DELETE' });\n setConfirmDeleteId(null);\n refresh();\n } catch (err) {\n setError(err instanceof FetchError ? err.message : (err as Error).message);\n }\n };\n\n const autoDetect = async (): Promise<void> => {\n setError(null);\n try {\n await fetchJson('/api/projects/auto-detect', { method: 'POST', body: {} });\n refresh();\n } catch (err) {\n setError(err instanceof FetchError ? err.message : (err as Error).message);\n }\n };\n\n const scan = async (): Promise<void> => {\n setError(null);\n try {\n await fetchJson('/api/projects/scan', { method: 'POST', body: {} });\n refresh();\n } catch (err) {\n setError(err instanceof FetchError ? err.message : (err as Error).message);\n }\n };\n\n return (\n <Stack gap={4} data-testid=\"projects-view\">\n <ViewHeader\n title=\"Projects\"\n description=\"Registered project roots. One is active at a time — every active-project endpoint scopes to it.\"\n actions={\n <Inline align=\"center\" gap={2}>\n {error !== null && (\n <span role=\"alert\" data-testid=\"projects-error\" style={{ color: 'var(--danger)', fontSize: 'var(--fs-12)' }}>\n {error}\n </span>\n )}\n <Button variant=\"ghost\" onClick={() => void scan()} data-testid=\"projects-scan\">\n <ScanSearch size={14} aria-hidden /> Scan configured dir\n </Button>\n <Button variant=\"ghost\" onClick={() => void autoDetect()} data-testid=\"projects-auto-detect\">\n <Sparkles size={14} aria-hidden /> Auto-detect cwd\n </Button>\n <Button variant=\"ghost\" onClick={() => void refresh()} data-testid=\"projects-refresh\">\n <RefreshCcw size={14} aria-hidden /> Refresh\n </Button>\n <Button variant=\"primary\" onClick={() => setAdding(true)} data-testid=\"projects-add\">\n <FolderPlus size={14} aria-hidden /> Add project\n </Button>\n </Inline>\n }\n />\n <ListHeader title=\"Registered\" count={projects.length} testid=\"projects-list-header\" />\n\n <Card variant=\"default\">\n <CardBody>\n {payload.loading && projects.length === 0 ? (\n <Stack gap={2}>\n <Skeleton style={{ height: 36 }} />\n <Skeleton style={{ height: 36 }} />\n </Stack>\n ) : projects.length === 0 ? (\n <EmptyState\n icon={<FolderPlus size={28} aria-hidden />}\n title=\"No projects yet\"\n description=\"Add a path, auto-detect the server cwd, or scan the configured projects directory.\"\n />\n ) : (\n <Stack gap={2}>\n {projects.map((p) => {\n const isActive = p.id === activeId;\n const isConfirmingDelete = confirmDeleteId === p.id;\n return (\n <div\n key={p.id}\n data-testid={`project-row-${p.id}`}\n style={{\n padding: 'var(--space-3)',\n borderRadius: 'var(--radius-md)',\n border: isActive ? '1px solid var(--accent)' : '1px solid var(--border)',\n background: isActive ? 'color-mix(in oklch, var(--accent) 12%, transparent)' : 'var(--surface-0)',\n }}\n >\n <Inline align=\"center\" justify=\"between\" gap={3}>\n <Stack gap={1} style={{ minWidth: 0 }}>\n <Inline align=\"center\" gap={2}>\n <strong style={{ fontSize: 'var(--fs-13)' }}>\n {p.name || p.path.split('/').pop() || p.id}\n </strong>\n {isActive && <Badge tone=\"success\">Active</Badge>}\n </Inline>\n <span style={{ fontFamily: 'var(--font-mono)', fontSize: 'var(--fs-12)', color: 'var(--fg-muted)', overflowWrap: 'anywhere' }}>\n {p.path}\n </span>\n </Stack>\n <Inline gap={1}>\n {!isActive && (\n <Button variant=\"primary\" onClick={() => void activate(p.id)} data-testid={`project-activate-${p.id}`}>\n Activate\n </Button>\n )}\n <Button\n variant=\"ghost\"\n onClick={() => setConfirmDeleteId((cur) => (cur === p.id ? null : p.id))}\n data-testid={`project-delete-${p.id}`}\n aria-label={`Remove project ${p.id}`}\n >\n <Trash2 size={14} aria-hidden /> Remove\n </Button>\n </Inline>\n </Inline>\n {isConfirmingDelete && (\n <Inline gap={1} style={{ marginTop: 'var(--space-2)' }}>\n <Button variant=\"danger\" onClick={() => void remove(p.id)} data-testid={`project-confirm-delete-${p.id}`}>\n Confirm remove\n </Button>\n <Button variant=\"ghost\" onClick={() => setConfirmDeleteId(null)} data-testid={`project-cancel-delete-${p.id}`}>\n Cancel\n </Button>\n </Inline>\n )}\n </div>\n );\n })}\n </Stack>\n )}\n </CardBody>\n </Card>\n\n <Sheet open={adding} onOpenChange={setAdding}>\n <SheetContent side=\"right\" title=\"Add project\" description=\"Register an existing directory as a project root.\">\n <Stack gap={3} style={{ padding: 'var(--space-4)' }}>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-1)' }}>\n <span style={{ fontSize: 'var(--fs-12)', color: 'var(--fg-muted)' }}>Path</span>\n <Input\n placeholder=\"/absolute/path/to/project\"\n value={newPath}\n data-testid=\"projects-add-path\"\n onChange={(e) => setNewPath((e.target as HTMLInputElement).value)}\n />\n </label>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-1)' }}>\n <span style={{ fontSize: 'var(--fs-12)', color: 'var(--fg-muted)' }}>Display name (optional)</span>\n <Input\n placeholder=\"My project\"\n value={newName}\n data-testid=\"projects-add-name\"\n onChange={(e) => setNewName((e.target as HTMLInputElement).value)}\n />\n </label>\n <Inline justify=\"end\" gap={2}>\n <SheetClose asChild>\n <Button variant=\"ghost\" data-testid=\"projects-add-cancel\">Cancel</Button>\n </SheetClose>\n <Button variant=\"primary\" onClick={() => void add()} disabled={busy || newPath.trim() === ''} data-testid=\"projects-add-submit\">\n Add\n </Button>\n </Inline>\n </Stack>\n </SheetContent>\n </Sheet>\n </Stack>\n );\n}"],"names":["FolderPlus","createLucideIcon","ScanSearch","ProjectsView","payload","useFetch","adding","setAdding","useState","newPath","setNewPath","newName","setNewName","busy","setBusy","error","setError","confirmDeleteId","setConfirmDeleteId","projects","_a","activeId","_b","p","refresh","useCallback","useWsMessage","add","path","fetchJson","err","FetchError","activate","id","remove","autoDetect","scan","jsxs","Stack","jsx","ViewHeader","Inline","Button","Sparkles","RefreshCcw","ListHeader","Card","CardBody","Skeleton","EmptyState","isActive","isConfirmingDelete","Badge","cur","Trash2","Sheet","SheetContent","Input","e","SheetClose"],"mappings":"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","x_google_ignoreList":[0,1]}
1
+ {"version":3,"file":"ProjectsView-Du0LzE7A.js","sources":["../../../node_modules/lucide-react/dist/esm/icons/folder-plus.js","../../../node_modules/lucide-react/dist/esm/icons/scan-search.js","../../src/web/v8/views/Projects/ProjectsView.tsx"],"sourcesContent":["/**\n * @license lucide-react v0.460.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst FolderPlus = createLucideIcon(\"FolderPlus\", [\n [\"path\", { d: \"M12 10v6\", key: \"1bos4e\" }],\n [\"path\", { d: \"M9 13h6\", key: \"1uhe8q\" }],\n [\n \"path\",\n {\n d: \"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z\",\n key: \"1kt360\"\n }\n ]\n]);\n\nexport { FolderPlus as default };\n//# sourceMappingURL=folder-plus.js.map\n","/**\n * @license lucide-react v0.460.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst ScanSearch = createLucideIcon(\"ScanSearch\", [\n [\"path\", { d: \"M3 7V5a2 2 0 0 1 2-2h2\", key: \"aa7l1z\" }],\n [\"path\", { d: \"M17 3h2a2 2 0 0 1 2 2v2\", key: \"4qcy5o\" }],\n [\"path\", { d: \"M21 17v2a2 2 0 0 1-2 2h-2\", key: \"6vwrx8\" }],\n [\"path\", { d: \"M7 21H5a2 2 0 0 1-2-2v-2\", key: \"ioqczr\" }],\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"3\", key: \"1v7zrd\" }],\n [\"path\", { d: \"m16 16-1.9-1.9\", key: \"1dq9hf\" }]\n]);\n\nexport { ScanSearch as default };\n//# sourceMappingURL=scan-search.js.map\n","/**\n * v8/views/Projects/ProjectsView.tsx — Sprint S39, v9.3.0.\n *\n * The project registry surface. Shows every registered project,\n * lets the user mark one active, add by path (Sheet form), and\n * remove (inline confirm). Two utility actions sit in the header:\n * - Auto-detect: POST /api/projects/auto-detect (adds cwd)\n * - Scan: POST /api/projects/scan (walks the configured\n * dashboard.projectsDirectory and adds any new\n * roots it finds).\n *\n * Live refresh on the `project:change` WS event the server already\n * broadcasts from routes/projects.mjs.\n */\n\nimport { useCallback, useEffect, useState } from 'react';\nimport { FolderPlus, RefreshCcw, ScanSearch, Sparkles, Trash2 } from 'lucide-react';\nimport { Stack } from '../../ui/primitives/Stack.js';\nimport { Inline } from '../../ui/primitives/Inline.js';\nimport { ViewHeader } from '../../ui/data/ViewHeader.js';\nimport { ListHeader } from '../../ui/data/ListHeader.js';\nimport { Card, CardBody } from '../../ui/data/Card.js';\nimport { Button } from '../../ui/controls/Button.js';\nimport { Input } from '../../ui/controls/Input.js';\nimport { Sheet, SheetClose, SheetContent } from '../../ui/feedback/Sheet.js';\nimport { Skeleton } from '../../ui/feedback/Skeleton.js';\nimport { EmptyState } from '../../ui/feedback/EmptyState.js';\nimport { Badge } from '../../ui/data/Badge.js';\nimport { useFetch } from '../../data/useFetch.js';\nimport { useWsMessage } from '../../data/useWebSocket.js';\nimport { fetchJson, FetchError } from '../../data/fetcher.js';\nimport type { Project, WsMessage } from '../../data/types.js';\n\ninterface ProjectsPayload {\n projects: Project[];\n}\n\nexport function ProjectsView(): JSX.Element {\n const payload = useFetch<ProjectsPayload>('/api/projects');\n const [adding, setAdding] = useState<boolean>(false);\n const [newPath, setNewPath] = useState<string>('');\n const [newName, setNewName] = useState<string>('');\n const [busy, setBusy] = useState<boolean>(false);\n const [error, setError] = useState<string | null>(null);\n const [confirmDeleteId, setConfirmDeleteId] = useState<string | null>(null);\n\n const projects = payload.data?.projects ?? [];\n const activeId = projects.find((p) => p.active)?.id ?? null;\n\n const refresh = useCallback(() => { void payload.refetch(); }, [payload]);\n useWsMessage('project:change', refresh);\n\n const add = async (): Promise<void> => {\n const path = newPath.trim();\n if (!path || busy) return;\n setBusy(true);\n setError(null);\n try {\n await fetchJson<Project>('/api/projects', {\n method: 'POST',\n body: { path, name: newName.trim() || null },\n });\n setNewPath('');\n setNewName('');\n setAdding(false);\n refresh();\n } catch (err) {\n setError(err instanceof FetchError ? err.message : (err as Error).message);\n } finally {\n setBusy(false);\n }\n };\n\n const activate = async (id: string): Promise<void> => {\n setError(null);\n try {\n await fetchJson(`/api/projects/${id}/activate`, { method: 'POST', body: {} });\n refresh();\n } catch (err) {\n setError(err instanceof FetchError ? err.message : (err as Error).message);\n }\n };\n\n const remove = async (id: string): Promise<void> => {\n setError(null);\n try {\n await fetchJson(`/api/projects/${id}`, { method: 'DELETE' });\n setConfirmDeleteId(null);\n refresh();\n } catch (err) {\n setError(err instanceof FetchError ? err.message : (err as Error).message);\n }\n };\n\n const autoDetect = async (): Promise<void> => {\n setError(null);\n try {\n await fetchJson('/api/projects/auto-detect', { method: 'POST', body: {} });\n refresh();\n } catch (err) {\n setError(err instanceof FetchError ? err.message : (err as Error).message);\n }\n };\n\n const scan = async (): Promise<void> => {\n setError(null);\n try {\n await fetchJson('/api/projects/scan', { method: 'POST', body: {} });\n refresh();\n } catch (err) {\n setError(err instanceof FetchError ? err.message : (err as Error).message);\n }\n };\n\n return (\n <Stack gap={4} data-testid=\"projects-view\">\n <ViewHeader\n title=\"Projects\"\n description=\"Registered project roots. One is active at a time — every active-project endpoint scopes to it.\"\n actions={\n <Inline align=\"center\" gap={2}>\n {error !== null && (\n <span role=\"alert\" data-testid=\"projects-error\" style={{ color: 'var(--danger)', fontSize: 'var(--fs-12)' }}>\n {error}\n </span>\n )}\n <Button variant=\"ghost\" onClick={() => void scan()} data-testid=\"projects-scan\">\n <ScanSearch size={14} aria-hidden /> Scan configured dir\n </Button>\n <Button variant=\"ghost\" onClick={() => void autoDetect()} data-testid=\"projects-auto-detect\">\n <Sparkles size={14} aria-hidden /> Auto-detect cwd\n </Button>\n <Button variant=\"ghost\" onClick={() => void refresh()} data-testid=\"projects-refresh\">\n <RefreshCcw size={14} aria-hidden /> Refresh\n </Button>\n <Button variant=\"primary\" onClick={() => setAdding(true)} data-testid=\"projects-add\">\n <FolderPlus size={14} aria-hidden /> Add project\n </Button>\n </Inline>\n }\n />\n <ListHeader title=\"Registered\" count={projects.length} testid=\"projects-list-header\" />\n\n <Card variant=\"default\">\n <CardBody>\n {payload.loading && projects.length === 0 ? (\n <Stack gap={2}>\n <Skeleton style={{ height: 36 }} />\n <Skeleton style={{ height: 36 }} />\n </Stack>\n ) : projects.length === 0 ? (\n <EmptyState\n icon={<FolderPlus size={28} aria-hidden />}\n title=\"No projects yet\"\n description=\"Add a path, auto-detect the server cwd, or scan the configured projects directory.\"\n />\n ) : (\n <Stack gap={2}>\n {projects.map((p) => {\n const isActive = p.id === activeId;\n const isConfirmingDelete = confirmDeleteId === p.id;\n return (\n <div\n key={p.id}\n data-testid={`project-row-${p.id}`}\n style={{\n padding: 'var(--space-3)',\n borderRadius: 'var(--radius-md)',\n border: isActive ? '1px solid var(--accent)' : '1px solid var(--border)',\n background: isActive ? 'color-mix(in oklch, var(--accent) 12%, transparent)' : 'var(--surface-0)',\n }}\n >\n <Inline align=\"center\" justify=\"between\" gap={3}>\n <Stack gap={1} style={{ minWidth: 0 }}>\n <Inline align=\"center\" gap={2}>\n <strong style={{ fontSize: 'var(--fs-13)' }}>\n {p.name || p.path.split('/').pop() || p.id}\n </strong>\n {isActive && <Badge tone=\"success\">Active</Badge>}\n </Inline>\n <span style={{ fontFamily: 'var(--font-mono)', fontSize: 'var(--fs-12)', color: 'var(--fg-muted)', overflowWrap: 'anywhere' }}>\n {p.path}\n </span>\n </Stack>\n <Inline gap={1}>\n {!isActive && (\n <Button variant=\"primary\" onClick={() => void activate(p.id)} data-testid={`project-activate-${p.id}`}>\n Activate\n </Button>\n )}\n <Button\n variant=\"ghost\"\n onClick={() => setConfirmDeleteId((cur) => (cur === p.id ? null : p.id))}\n data-testid={`project-delete-${p.id}`}\n aria-label={`Remove project ${p.id}`}\n >\n <Trash2 size={14} aria-hidden /> Remove\n </Button>\n </Inline>\n </Inline>\n {isConfirmingDelete && (\n <Inline gap={1} style={{ marginTop: 'var(--space-2)' }}>\n <Button variant=\"danger\" onClick={() => void remove(p.id)} data-testid={`project-confirm-delete-${p.id}`}>\n Confirm remove\n </Button>\n <Button variant=\"ghost\" onClick={() => setConfirmDeleteId(null)} data-testid={`project-cancel-delete-${p.id}`}>\n Cancel\n </Button>\n </Inline>\n )}\n </div>\n );\n })}\n </Stack>\n )}\n </CardBody>\n </Card>\n\n <Sheet open={adding} onOpenChange={setAdding}>\n <SheetContent side=\"right\" title=\"Add project\" description=\"Register an existing directory as a project root.\">\n <Stack gap={3} style={{ padding: 'var(--space-4)' }}>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-1)' }}>\n <span style={{ fontSize: 'var(--fs-12)', color: 'var(--fg-muted)' }}>Path</span>\n <Input\n placeholder=\"/absolute/path/to/project\"\n value={newPath}\n data-testid=\"projects-add-path\"\n onChange={(e) => setNewPath((e.target as HTMLInputElement).value)}\n />\n </label>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-1)' }}>\n <span style={{ fontSize: 'var(--fs-12)', color: 'var(--fg-muted)' }}>Display name (optional)</span>\n <Input\n placeholder=\"My project\"\n value={newName}\n data-testid=\"projects-add-name\"\n onChange={(e) => setNewName((e.target as HTMLInputElement).value)}\n />\n </label>\n <Inline justify=\"end\" gap={2}>\n <SheetClose asChild>\n <Button variant=\"ghost\" data-testid=\"projects-add-cancel\">Cancel</Button>\n </SheetClose>\n <Button variant=\"primary\" onClick={() => void add()} disabled={busy || newPath.trim() === ''} data-testid=\"projects-add-submit\">\n Add\n </Button>\n </Inline>\n </Stack>\n </SheetContent>\n </Sheet>\n </Stack>\n );\n}"],"names":["FolderPlus","createLucideIcon","ScanSearch","ProjectsView","payload","useFetch","adding","setAdding","useState","newPath","setNewPath","newName","setNewName","busy","setBusy","error","setError","confirmDeleteId","setConfirmDeleteId","projects","_a","activeId","_b","p","refresh","useCallback","useWsMessage","add","path","fetchJson","err","FetchError","activate","id","remove","autoDetect","scan","jsxs","Stack","jsx","ViewHeader","Inline","Button","Sparkles","RefreshCcw","ListHeader","Card","CardBody","Skeleton","EmptyState","isActive","isConfirmingDelete","Badge","cur","Trash2","Sheet","SheetContent","Input","e","SheetClose"],"mappings":"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","x_google_ignoreList":[0,1]}
@@ -1,4 +1,4 @@
1
- import{r as c,j as r}from"./cmdk-DVxytz9i.js";import{c as P,u as R,i as n,S as m,I as l,d,w as B,h as z,j as M,F as u}from"./main-CokHAXWM.js";import{V as N}from"./ViewHeader-BzYZ3m-e.js";import{L as F}from"./ListHeader-TjPJrUfY.js";import{C as x,b as j}from"./Card-B0imhxMk.js";import{E as H}from"./EmptyState-BjgVOrcO.js";import{R as _}from"./refresh-ccw-DNrXX9gB.js";import{P as J}from"./plus-DENm0hqV.js";import{R as W}from"./rotate-cw-xpPJw9LP.js";import{P as Y}from"./pencil-eq5IqBmU.js";import{P as q}from"./power-off-D47cOnG_.js";import{P as G}from"./power-DLjXF7zE.js";import{T as Q}from"./trash-2-DZDjEnNh.js";/**
1
+ import{r as c,j as r}from"./cmdk-DVxytz9i.js";import{c as P,u as R,i as n,S as m,I as l,d,w as B,h as z,j as M,F as u}from"./main-CyAIirBB.js";import{V as N}from"./ViewHeader-V_SzbUpw.js";import{L as F}from"./ListHeader-Ak9WJd39.js";import{C as x,b as j}from"./Card-D9019JOD.js";import{E as H}from"./EmptyState-DLmtBX_6.js";import{R as _}from"./refresh-ccw-BCXx86xp.js";import{P as J}from"./plus-fFxt-8z5.js";import{R as W}from"./rotate-cw-CsTtaLV2.js";import{P as Y}from"./pencil-BUJ2DpWs.js";import{P as q}from"./power-off-B81hLRah.js";import{P as G}from"./power-Cjgy2KsI.js";import{T as Q}from"./trash-2-2NsyTU8c.js";/**
2
2
  * @license lucide-react v0.460.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.