@burdenoff/microfe-vibecontrols 2026.623.1 → 2026.623.3

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 (295) hide show
  1. package/dist/components/actions/ActionCard.js +2 -2
  2. package/dist/components/actions/ActionCard.js.map +1 -1
  3. package/dist/components/actions/ActionTypeBadge.js +1 -1
  4. package/dist/components/actions/ActionTypeBadge.js.map +1 -1
  5. package/dist/components/actions/ExecutionHistory.js +1 -1
  6. package/dist/components/actions/ExecutionHistory.js.map +1 -1
  7. package/dist/components/agent-manager/AgentManagerAdminBar.js +1 -1
  8. package/dist/components/agent-manager/AgentManagerAdminBar.js.map +1 -1
  9. package/dist/components/agent-manager/AgentManagerChatPane.js +2 -2
  10. package/dist/components/agent-manager/AgentManagerChatPane.js.map +1 -1
  11. package/dist/components/agent-manager/AgentManagerInputArea.js +4 -4
  12. package/dist/components/agent-manager/AgentManagerInputArea.js.map +1 -1
  13. package/dist/components/agent-manager/AgentManagerLayout.js +1 -1
  14. package/dist/components/agent-manager/AgentManagerLayout.js.map +1 -1
  15. package/dist/components/agent-manager/AgentManagerMessageItem.js +1 -1
  16. package/dist/components/agent-manager/AgentManagerMessageItem.js.map +1 -1
  17. package/dist/components/agent-manager/AgentManagerSidebar.js +5 -5
  18. package/dist/components/agent-manager/AgentManagerSidebar.js.map +1 -1
  19. package/dist/components/agent-manager/AgentManagerTab.js +1 -1
  20. package/dist/components/agent-manager/AgentManagerTab.js.map +1 -1
  21. package/dist/components/agent-manager/AgentManagerTabBar.js +1 -1
  22. package/dist/components/agent-manager/AgentManagerTabBar.js.map +1 -1
  23. package/dist/components/agent-manager/BridleConfigPanel.js +24 -24
  24. package/dist/components/agent-manager/BridleConfigPanel.js.map +1 -1
  25. package/dist/components/agent-manager/PluginInstallModal.js +1 -1
  26. package/dist/components/agent-manager/PluginInstallModal.js.map +1 -1
  27. package/dist/components/agent-manager/ProfilesPanel.js +5 -5
  28. package/dist/components/agent-manager/ProfilesPanel.js.map +1 -1
  29. package/dist/components/agent-manager/controls/AutonomySelector.js +1 -1
  30. package/dist/components/agent-manager/controls/AutonomySelector.js.map +1 -1
  31. package/dist/components/agent-manager/controls/SdkSelector.js +1 -1
  32. package/dist/components/agent-manager/controls/SdkSelector.js.map +1 -1
  33. package/dist/components/agent-manager/controls/VoiceToggle.js +1 -1
  34. package/dist/components/agent-manager/controls/VoiceToggle.js.map +1 -1
  35. package/dist/components/agent-manager/detail/SessionConfigPanel.js +6 -6
  36. package/dist/components/agent-manager/detail/SessionConfigPanel.js.map +1 -1
  37. package/dist/components/agent-manager/detail/SessionLogsPanel.js +5 -5
  38. package/dist/components/agent-manager/detail/SessionLogsPanel.js.map +1 -1
  39. package/dist/components/agent-manager/detail/SessionToolsPanel.js +1 -1
  40. package/dist/components/agent-manager/detail/SessionToolsPanel.js.map +1 -1
  41. package/dist/components/agent-manager/dialogs/NewSessionDialog.js +2 -2
  42. package/dist/components/agent-manager/dialogs/NewSessionDialog.js.map +1 -1
  43. package/dist/components/agent-manager/dialogs/RenameSessionDialog.js +1 -1
  44. package/dist/components/agent-manager/dialogs/RenameSessionDialog.js.map +1 -1
  45. package/dist/components/agents/AgentCard.js +14 -11
  46. package/dist/components/agents/AgentCard.js.map +1 -1
  47. package/dist/components/ai/AIContextsTab.js +1 -1
  48. package/dist/components/ai/AIContextsTab.js.map +1 -1
  49. package/dist/components/ai/AIOverviewTab.js +4 -4
  50. package/dist/components/ai/AIOverviewTab.js.map +1 -1
  51. package/dist/components/ai/AIPlanPanel.js +4 -4
  52. package/dist/components/ai/AIPlanPanel.js.map +1 -1
  53. package/dist/components/ai/AIPlaygroundTab.js +1 -1
  54. package/dist/components/ai/AIPlaygroundTab.js.map +1 -1
  55. package/dist/components/ai/AIPromptsTab.js +2 -2
  56. package/dist/components/ai/AIPromptsTab.js.map +1 -1
  57. package/dist/components/ai/AIProviderCredentialsCard.js +4 -4
  58. package/dist/components/ai/AIProviderCredentialsCard.js.map +1 -1
  59. package/dist/components/ai/AIQueueTab.js +4 -4
  60. package/dist/components/ai/AIQueueTab.js.map +1 -1
  61. package/dist/components/ai/AISessionsTab.js +4 -4
  62. package/dist/components/ai/AISessionsTab.js.map +1 -1
  63. package/dist/components/ai/AITasksTab.js +1 -1
  64. package/dist/components/ai/AITasksTab.js.map +1 -1
  65. package/dist/components/ai/ClaudeCredentialsCard.js +3 -3
  66. package/dist/components/ai/ClaudeCredentialsCard.js.map +1 -1
  67. package/dist/components/ai/PluginHarnessPicker.js +3 -3
  68. package/dist/components/ai/PluginHarnessPicker.js.map +1 -1
  69. package/dist/components/analytics/AnalyticsBreakdownChart.js +8 -8
  70. package/dist/components/analytics/AnalyticsBreakdownChart.js.map +1 -1
  71. package/dist/components/analytics/AnalyticsTimeSeriesChart.js +5 -5
  72. package/dist/components/analytics/AnalyticsTimeSeriesChart.js.map +1 -1
  73. package/dist/components/audit/AuditDeleteDialog.js +2 -2
  74. package/dist/components/audit/AuditDeleteDialog.js.map +1 -1
  75. package/dist/components/audit/AuditExportButton.js +5 -5
  76. package/dist/components/audit/AuditExportButton.js.map +1 -1
  77. package/dist/components/audit/AuditLogDetail.js +9 -9
  78. package/dist/components/audit/AuditLogDetail.js.map +1 -1
  79. package/dist/components/audit/AuditLogFilters.js +1 -1
  80. package/dist/components/audit/AuditLogFilters.js.map +1 -1
  81. package/dist/components/audit/AuditLogStats.js +1 -1
  82. package/dist/components/audit/AuditLogStats.js.map +1 -1
  83. package/dist/components/audit/AuditLogTable.js +28 -28
  84. package/dist/components/audit/AuditLogTable.js.map +1 -1
  85. package/dist/components/catalog/CatalogCard.js +3 -3
  86. package/dist/components/catalog/CatalogCard.js.map +1 -1
  87. package/dist/components/catalog/CatalogForm.js +1 -1
  88. package/dist/components/catalog/CatalogForm.js.map +1 -1
  89. package/dist/components/common/ActionAgentTargetNotice.js +6 -6
  90. package/dist/components/common/ActionAgentTargetNotice.js.map +1 -1
  91. package/dist/components/common/CommandEditor.js +1 -1
  92. package/dist/components/common/CommandEditor.js.map +1 -1
  93. package/dist/components/docs/DocsPageTree.js +1 -1
  94. package/dist/components/docs/DocsPageTree.js.map +1 -1
  95. package/dist/components/docs/DocsPreviewPanel.js +1 -1
  96. package/dist/components/docs/DocsPreviewPanel.js.map +1 -1
  97. package/dist/components/docs/DocsRevisionHistory.js +4 -4
  98. package/dist/components/docs/DocsRevisionHistory.js.map +1 -1
  99. package/dist/components/docs/DocsSettingsPanel.js +7 -7
  100. package/dist/components/docs/DocsSettingsPanel.js.map +1 -1
  101. package/dist/components/docs/DocsTabPanel.js +1 -1
  102. package/dist/components/docs/DocsTabPanel.js.map +1 -1
  103. package/dist/components/graph/ConnectionPermissionsDialog.js +2 -2
  104. package/dist/components/graph/ConnectionPermissionsDialog.js.map +1 -1
  105. package/dist/components/graph/MeshDeployWizard.js +3 -3
  106. package/dist/components/graph/MeshDeployWizard.js.map +1 -1
  107. package/dist/components/graph/MeshRequestPanel.js +2 -2
  108. package/dist/components/graph/MeshRequestPanel.js.map +1 -1
  109. package/dist/components/idp-components/ComponentCard.js +5 -5
  110. package/dist/components/idp-components/ComponentCard.js.map +1 -1
  111. package/dist/components/idp-components/ComponentForm.js +1 -1
  112. package/dist/components/idp-components/ComponentForm.js.map +1 -1
  113. package/dist/components/idp-components/DependencyGraphTab.js +7 -7
  114. package/dist/components/idp-components/DependencyGraphTab.js.map +1 -1
  115. package/dist/components/logs/LogViewer.js +14 -14
  116. package/dist/components/logs/LogViewer.js.map +1 -1
  117. package/dist/components/notes/NoteCard.js +1 -1
  118. package/dist/components/notes/NoteCard.js.map +1 -1
  119. package/dist/components/notes/NoteEditor.js +2 -2
  120. package/dist/components/notes/NoteEditor.js.map +1 -1
  121. package/dist/components/notes/NoteList.js +1 -1
  122. package/dist/components/notes/NoteList.js.map +1 -1
  123. package/dist/components/notes/NotesTabPanel.js +1 -1
  124. package/dist/components/notes/NotesTabPanel.js.map +1 -1
  125. package/dist/components/scorecard/ScorecardEmptyState.js +1 -1
  126. package/dist/components/scorecard/ScorecardEmptyState.js.map +1 -1
  127. package/dist/components/scorecard/ScorecardPanel.js +3 -3
  128. package/dist/components/scorecard/ScorecardPanel.js.map +1 -1
  129. package/dist/components/security/SecurityScanList.js +11 -11
  130. package/dist/components/security/SecurityScanList.js.map +1 -1
  131. package/dist/components/sessions/SessionCard.js +26 -19
  132. package/dist/components/sessions/SessionCard.js.map +1 -1
  133. package/dist/components/sessions/SessionStatusBadge.js +7 -7
  134. package/dist/components/sessions/SessionStatusBadge.js.map +1 -1
  135. package/dist/components/shared/AgentSelector.js +4 -4
  136. package/dist/components/shared/AgentSelector.js.map +1 -1
  137. package/dist/components/shared/AgentSetupBanner.js +4 -4
  138. package/dist/components/shared/AgentSetupBanner.js.map +1 -1
  139. package/dist/components/shared/Card.js +4 -4
  140. package/dist/components/shared/Card.js.map +1 -1
  141. package/dist/components/shared/ErrorState.js +15 -9
  142. package/dist/components/shared/ErrorState.js.map +1 -1
  143. package/dist/components/shared/FilterDropdown.js +2 -2
  144. package/dist/components/shared/FilterDropdown.js.map +1 -1
  145. package/dist/components/shared/PageSkeleton.js +6 -6
  146. package/dist/components/shared/PageSkeleton.js.map +1 -1
  147. package/dist/components/shared/SearchInput.js +1 -1
  148. package/dist/components/shared/SearchInput.js.map +1 -1
  149. package/dist/components/shared/StatCard.js +7 -7
  150. package/dist/components/shared/StatCard.js.map +1 -1
  151. package/dist/components/templates/PublishTemplateForm.js +2 -2
  152. package/dist/components/templates/PublishTemplateForm.js.map +1 -1
  153. package/dist/components/templates/TemplateCard.js +5 -5
  154. package/dist/components/templates/TemplateCard.js.map +1 -1
  155. package/dist/components/templates/TemplateWizard.js +2 -2
  156. package/dist/components/templates/TemplateWizard.js.map +1 -1
  157. package/dist/components/tunnels/DomainVerificationBadge.js +4 -4
  158. package/dist/components/tunnels/DomainVerificationBadge.js.map +1 -1
  159. package/dist/components/tunnels/ProviderBadge.js +1 -1
  160. package/dist/components/tunnels/ProviderBadge.js.map +1 -1
  161. package/dist/components/tunnels/TunnelCard.js +16 -16
  162. package/dist/components/tunnels/TunnelCard.js.map +1 -1
  163. package/dist/components/tunnels/TunnelCreateWizard.js +1 -1
  164. package/dist/components/tunnels/TunnelCreateWizard.js.map +1 -1
  165. package/dist/components/tunnels/TunnelPublicAccessPanel.js +1 -1
  166. package/dist/components/tunnels/TunnelPublicAccessPanel.js.map +1 -1
  167. package/dist/components/tunnels/TunnelStatusBadge.js +4 -4
  168. package/dist/components/tunnels/TunnelStatusBadge.js.map +1 -1
  169. package/dist/components/vibecalendar/LogsDrawer.js +2 -2
  170. package/dist/components/vibecalendar/LogsDrawer.js.map +1 -1
  171. package/dist/components/vibecalendar/ScheduleTaskModal.js +5 -5
  172. package/dist/components/vibecalendar/ScheduleTaskModal.js.map +1 -1
  173. package/dist/components/vibecalendar/VibeCalAgendaView.js +2 -2
  174. package/dist/components/vibecalendar/VibeCalAgendaView.js.map +1 -1
  175. package/dist/components/vibecalendar/VibeCalDayView.js +3 -3
  176. package/dist/components/vibecalendar/VibeCalDayView.js.map +1 -1
  177. package/dist/components/vibecalendar/VibeCalMonthView.js +1 -1
  178. package/dist/components/vibecalendar/VibeCalMonthView.js.map +1 -1
  179. package/dist/components/vibecalendar/VibeCalWeekView.js +4 -4
  180. package/dist/components/vibecalendar/VibeCalWeekView.js.map +1 -1
  181. package/dist/components/vibecalendar/VibeCalendarHeader.js +4 -4
  182. package/dist/components/vibecalendar/VibeCalendarHeader.js.map +1 -1
  183. package/dist/components/vibecalendar/VibeTaskBlock.js +2 -2
  184. package/dist/components/vibecalendar/VibeTaskBlock.js.map +1 -1
  185. package/dist/components/vibecalendar/types.js +7 -7
  186. package/dist/components/vibecalendar/types.js.map +1 -1
  187. package/dist/components/vibedeck/VibeDeckButtonForm.js +9 -9
  188. package/dist/components/vibedeck/VibeDeckButtonForm.js.map +1 -1
  189. package/dist/components/vibedeck/VibeDeckButtonTile.js +6 -6
  190. package/dist/components/vibedeck/VibeDeckButtonTile.js.map +1 -1
  191. package/dist/components/vibedeck/VibeDeckExecutionLog.js +10 -10
  192. package/dist/components/vibedeck/VibeDeckExecutionLog.js.map +1 -1
  193. package/dist/components/vibedeck/VibeDeckForm.js +8 -8
  194. package/dist/components/vibedeck/VibeDeckForm.js.map +1 -1
  195. package/dist/components/vibedeck/VibeDeckGrid.js +2 -2
  196. package/dist/components/vibedeck/VibeDeckGrid.js.map +1 -1
  197. package/dist/components/vibedeck/VibeDeckPublicLinkPanel.js +2 -2
  198. package/dist/components/vibedeck/VibeDeckPublicLinkPanel.js.map +1 -1
  199. package/dist/components/vibedeck/VibeDeckSwitcher.js +8 -8
  200. package/dist/components/vibedeck/VibeDeckSwitcher.js.map +1 -1
  201. package/dist/components/vibes/VibeCard.js +22 -18
  202. package/dist/components/vibes/VibeCard.js.map +1 -1
  203. package/dist/components/vibes/VibeList.js +16 -12
  204. package/dist/components/vibes/VibeList.js.map +1 -1
  205. package/dist/pages/ActionsPage.js +4 -3
  206. package/dist/pages/ActionsPage.js.map +1 -1
  207. package/dist/pages/AgentDetailsPage.js +9 -9
  208. package/dist/pages/AgentDetailsPage.js.map +1 -1
  209. package/dist/pages/AgentsLandingPage.js +4 -4
  210. package/dist/pages/AgentsLandingPage.js.map +1 -1
  211. package/dist/pages/AgentsPage.js +14 -14
  212. package/dist/pages/AgentsPage.js.map +1 -1
  213. package/dist/pages/AnalyticsPage.js +8 -8
  214. package/dist/pages/AnalyticsPage.js.map +1 -1
  215. package/dist/pages/CatalogDetailPage.js +11 -11
  216. package/dist/pages/CatalogDetailPage.js.map +1 -1
  217. package/dist/pages/CatalogPage.js +12 -9
  218. package/dist/pages/CatalogPage.js.map +1 -1
  219. package/dist/pages/ComponentDetailPage.js +7 -7
  220. package/dist/pages/ComponentDetailPage.js.map +1 -1
  221. package/dist/pages/ComponentsPage.js +12 -9
  222. package/dist/pages/ComponentsPage.js.map +1 -1
  223. package/dist/pages/EnvTopologyPage.js +2 -2
  224. package/dist/pages/EnvTopologyPage.js.map +1 -1
  225. package/dist/pages/GettingStartedPage.js +12 -12
  226. package/dist/pages/GettingStartedPage.js.map +1 -1
  227. package/dist/pages/HealthPage.js +12 -12
  228. package/dist/pages/HealthPage.js.map +1 -1
  229. package/dist/pages/LogsPage.js +4 -4
  230. package/dist/pages/LogsPage.js.map +1 -1
  231. package/dist/pages/NoteDetailsPage.js +5 -5
  232. package/dist/pages/NoteDetailsPage.js.map +1 -1
  233. package/dist/pages/OverviewPage.js +76 -43
  234. package/dist/pages/OverviewPage.js.map +1 -1
  235. package/dist/pages/PluginsCatalogPage.js +7 -4
  236. package/dist/pages/PluginsCatalogPage.js.map +1 -1
  237. package/dist/pages/ScheduledJobDetailPage.js +7 -7
  238. package/dist/pages/ScheduledJobDetailPage.js.map +1 -1
  239. package/dist/pages/SessionDetailsPage.js +21 -21
  240. package/dist/pages/SessionDetailsPage.js.map +1 -1
  241. package/dist/pages/SessionsPage.js +20 -20
  242. package/dist/pages/SessionsPage.js.map +1 -1
  243. package/dist/pages/SettingsPage.js +64 -52
  244. package/dist/pages/SettingsPage.js.map +1 -1
  245. package/dist/pages/SharedSessionPage.js +21 -21
  246. package/dist/pages/SharedSessionPage.js.map +1 -1
  247. package/dist/pages/SharedVibeDeckPage.js +5 -5
  248. package/dist/pages/SharedVibeDeckPage.js.map +1 -1
  249. package/dist/pages/TargetDetailsPage.js +56 -65
  250. package/dist/pages/TargetDetailsPage.js.map +1 -1
  251. package/dist/pages/TargetsPage.js +4 -4
  252. package/dist/pages/TargetsPage.js.map +1 -1
  253. package/dist/pages/TemplateDetailPage.js +10 -10
  254. package/dist/pages/TemplateDetailPage.js.map +1 -1
  255. package/dist/pages/TemplatesPage.js +6 -5
  256. package/dist/pages/TemplatesPage.js.map +1 -1
  257. package/dist/pages/VibeAuditPage.js +3 -3
  258. package/dist/pages/VibeAuditPage.js.map +1 -1
  259. package/dist/pages/VibeCalendarPage.js +3 -3
  260. package/dist/pages/VibeCalendarPage.js.map +1 -1
  261. package/dist/pages/VibeDeckPage.js +3 -3
  262. package/dist/pages/VibeDeckPage.js.map +1 -1
  263. package/dist/pages/VibeDetailsPage.js +15 -15
  264. package/dist/pages/VibeDetailsPage.js.map +1 -1
  265. package/dist/pages/VibeTaskDetailPage.js +5 -5
  266. package/dist/pages/VibeTaskDetailPage.js.map +1 -1
  267. package/dist/pages/VibesPage.js +18 -18
  268. package/dist/pages/VibesPage.js.map +1 -1
  269. package/dist/pages/WebhooksPage.js +11 -11
  270. package/dist/pages/WebhooksPage.js.map +1 -1
  271. package/dist/pages/ai/AIBookmarksPage.js +1 -1
  272. package/dist/pages/ai/AIBookmarksPage.js.map +1 -1
  273. package/dist/pages/ai/AISessionDetailPage.js +6 -6
  274. package/dist/pages/ai/AISessionDetailPage.js.map +1 -1
  275. package/dist/pages/tunnels/AuditTab.js +1 -1
  276. package/dist/pages/tunnels/AuditTab.js.map +1 -1
  277. package/dist/pages/tunnels/DomainsTab.js +1 -1
  278. package/dist/pages/tunnels/DomainsTab.js.map +1 -1
  279. package/dist/pages/tunnels/ProvidersTab.js +1 -1
  280. package/dist/pages/tunnels/ProvidersTab.js.map +1 -1
  281. package/dist/pages/tunnels/SessionsTab.js +1 -1
  282. package/dist/pages/tunnels/SessionsTab.js.map +1 -1
  283. package/dist/pages/tunnels/TunnelDetailsPage.js +3 -3
  284. package/dist/pages/tunnels/TunnelDetailsPage.js.map +1 -1
  285. package/dist/pages/tunnels/UsageTab.js +1 -1
  286. package/dist/pages/tunnels/UsageTab.js.map +1 -1
  287. package/dist/pages/tunnels/tabs/TunnelDomainsTab.js +16 -16
  288. package/dist/pages/tunnels/tabs/TunnelDomainsTab.js.map +1 -1
  289. package/dist/pages/tunnels/tabs/TunnelOverviewTab.js +1 -1
  290. package/dist/pages/tunnels/tabs/TunnelOverviewTab.js.map +1 -1
  291. package/dist/pages/tunnels/tabs/TunnelSessionsTab.js +52 -49
  292. package/dist/pages/tunnels/tabs/TunnelSessionsTab.js.map +1 -1
  293. package/dist/pages/tunnels/tabs/TunnelSettingsTab.js +9 -9
  294. package/dist/pages/tunnels/tabs/TunnelSettingsTab.js.map +1 -1
  295. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"LogsPage.js","names":[],"sources":["../../src/pages/LogsPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { Bot } from 'lucide-react';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { EmptyState, GetHelpButton, PageHeader } from '@/components/shared';\nimport { LogViewer } from '@/components/logs/LogViewer';\nimport { SearchableSelect } from '@/components/shared/SearchableSelect';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport { useCachedInitialData } from '@/hooks/useCachedInitialData';\nimport { useActiveAgentProfile } from '@/hooks/useActiveAgentProfile';\nimport { useVibeControlsStore } from '@/store/vibeControlsStore';\n\nexport function LogsPage() {\n const { t } = useI18n();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const { agents: cachedAgents, loading } = useCachedInitialData();\n const [selectedAgentId, setSelectedAgentId] = useState('');\n\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n // Phase 5B — store-backed active-profile lookup keyed by agent id. The\n // hook can only be called once at the top level, so the store map is\n // the only safe way to attach a per-row `profile` inside the .map()\n // below. Each agent row will fall back to the literal \"default\" string\n // until the user opens that agent's Profiles tab and the AgentProfile\n // rows for it land in the store.\n const profileMap = useVibeControlsStore((s) => s.activeProfilePerAgent);\n\n // Pre-warm the store for the currently-selected agent so its row shows\n // the correct profile once profiles are loaded.\n useActiveAgentProfile(selectedAgentId || null);\n\n const agents = useMemo(\n () =>\n cachedAgents\n .filter((agent) => agent.tunnelUrl)\n .map((agent) => ({\n id: agent.id,\n name: agent.name || agent.hostname || agent.id,\n isActive: agent.isActive,\n hostname: agent.hostname,\n platform: agent.platform,\n profile: profileMap[agent.id] ?? 'default',\n })),\n [cachedAgents, profileMap]\n );\n\n const selectedAgent = agents.find((agent) => agent.id === selectedAgentId);\n\n if (permissionsLoading)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <p className=\"text-text-secondary\">Loading permissions…</p>\n </div>\n );\n if (!hasPermission(VIBE_PERMISSIONS.LOG_LIST))\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold text-foreground\">Access Denied</h2>\n <p className=\"text-sm text-text-secondary\">\n You don't have permission to access this page.\n </p>\n </div>\n </div>\n );\n\n if (loading && agents.length === 0) {\n return (\n <MotionPage className=\"flex h-full flex-col gap-4 p-4\">\n <PageHeader\n title={tr('nav.logs', 'Logs')}\n description={tr('logs.description', 'Gateway-proxied log streaming from your agents')}\n />\n <PageSkeleton variant=\"rows\" count={6} />\n </MotionPage>\n );\n }\n\n return (\n <MotionPage className=\"flex h-full flex-col gap-4 p-4\">\n <PageHeader\n title={tr('nav.logs', 'Logs')}\n description={tr('logs.description', 'Gateway-proxied log streaming from your agents')}\n />\n\n <div className=\"flex flex-col items-start gap-3 sm:flex-row sm:items-center\">\n <label className=\"text-sm font-medium text-text-secondary\">\n {tr('logs.agentLabel', 'Agent:')}\n </label>\n\n {agents.length > 0 ? (\n <div className=\"w-full sm:w-80\">\n <SearchableSelect\n options={agents.map((agent) => ({\n value: agent.id,\n label: agent.name,\n description: `${agent.platform ?? ''} · ${agent.hostname ?? ''}`,\n }))}\n value={selectedAgentId}\n onChange={(value) => setSelectedAgentId(value)}\n placeholder={tr('logs.selectAgent', 'Select an agent...')}\n />\n </div>\n ) : (\n <span className=\"text-sm text-text-tertiary\">\n {tr('logs.noAgentsAvailable', 'No agents with tunnel access available')}\n </span>\n )}\n\n {selectedAgent ? (\n <div className=\"flex items-center gap-1.5 text-xs text-text-tertiary\">\n <div\n className={`size-2 rounded-full ${selectedAgent.isActive ? 'bg-status-success-bg' : 'bg-bg-sunken'}`}\n />\n {selectedAgent.hostname}\n </div>\n ) : null}\n </div>\n\n {selectedAgent ? (\n <LogViewer agentId={selectedAgent.id} profile={selectedAgent.profile} className=\"flex-1\" />\n ) : (\n <EmptyState\n icon={<Bot size={48} />}\n title={tr('logs.noAgentSelected', 'Select an agent to view logs')}\n description={tr(\n 'logs.noAgentSelectedDescription',\n 'Logs are fetched on demand. Choose an agent above to start streaming its gateway-proxied logs.'\n )}\n action={\n <GetHelpButton prompt=\"What are Logs in VibeControls and how do I investigate them?\" />\n }\n />\n )}\n </MotionPage>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAcA,SAAgB,IAAW;CACzB,IAAM,EAAE,SAAM,GAAS,EACjB,EAAE,kBAAe,WAAW,MAAuB,GAAgB,EACnE,EAAE,QAAQ,GAAc,eAAY,GAAsB,EAC1D,CAAC,GAAiB,KAAsB,EAAS,GAAG,EAEpD,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAQ,EAAE,EAAI;AACpB,SAAO,MAAU,IAAM,IAAW;IAS9B,IAAa,GAAsB,MAAM,EAAE,sBAAsB;AAIvE,GAAsB,KAAmB,KAAK;CAE9C,IAAM,IAAS,QAEX,EACG,QAAQ,MAAU,EAAM,UAAU,CAClC,KAAK,OAAW;EACf,IAAI,EAAM;EACV,MAAM,EAAM,QAAQ,EAAM,YAAY,EAAM;EAC5C,UAAU,EAAM;EAChB,UAAU,EAAM;EAChB,UAAU,EAAM;EAChB,SAAS,EAAW,EAAM,OAAO;EAClC,EAAE,EACP,CAAC,GAAc,EAAW,CAC3B,EAEK,IAAgB,EAAO,MAAM,MAAU,EAAM,OAAO,EAAgB;AAgC1E,QA9BI,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,KAAD;GAAG,WAAU;aAAsB;GAAwB,CAAA;EACvD,CAAA,GAEL,EAAc,EAAiB,SAAS,GAYzC,KAAW,EAAO,WAAW,IAE7B,kBAAC,GAAD;EAAY,WAAU;YAAtB,CACE,kBAAC,GAAD;GACE,OAAO,EAAG,YAAY,OAAO;GAC7B,aAAa,EAAG,oBAAoB,iDAAiD;GACrF,CAAA,EACF,kBAAC,GAAD;GAAc,SAAQ;GAAO,OAAO;GAAK,CAAA,CAC9B;MAKf,kBAAC,GAAD;EAAY,WAAU;YAAtB;GACE,kBAAC,GAAD;IACE,OAAO,EAAG,YAAY,OAAO;IAC7B,aAAa,EAAG,oBAAoB,iDAAiD;IACrF,CAAA;GAEF,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,SAAD;MAAO,WAAU;gBACd,EAAG,mBAAmB,SAAS;MAC1B,CAAA;KAEP,EAAO,SAAS,IACf,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD;OACE,SAAS,EAAO,KAAK,OAAW;QAC9B,OAAO,EAAM;QACb,OAAO,EAAM;QACb,aAAa,GAAG,EAAM,YAAY,GAAG,KAAK,EAAM,YAAY;QAC7D,EAAE;OACH,OAAO;OACP,WAAW,MAAU,EAAmB,EAAM;OAC9C,aAAa,EAAG,oBAAoB,qBAAqB;OACzD,CAAA;MACE,CAAA,GAEN,kBAAC,QAAD;MAAM,WAAU;gBACb,EAAG,0BAA0B,yCAAyC;MAClE,CAAA;KAGR,IACC,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD,EACE,WAAW,uBAAuB,EAAc,WAAW,yBAAyB,kBACpF,CAAA,EACD,EAAc,SACX;UACJ;KACA;;GAEL,IACC,kBAAC,GAAD;IAAW,SAAS,EAAc;IAAI,SAAS,EAAc;IAAS,WAAU;IAAW,CAAA,GAE3F,kBAAC,GAAD;IACE,MAAM,kBAAC,GAAD,EAAK,MAAM,IAAM,CAAA;IACvB,OAAO,EAAG,wBAAwB,+BAA+B;IACjE,aAAa,EACX,mCACA,iGACD;IACD,QACE,kBAAC,GAAD,EAAe,QAAO,gEAAiE,CAAA;IAEzF,CAAA;GAEO;MA9EX,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,MAAD;IAAI,WAAU;cAAwC;IAAkB,CAAA,EACxE,kBAAC,KAAD;IAAG,WAAU;cAA8B;IAEvC,CAAA,CACA;;EACF,CAAA"}
1
+ {"version":3,"file":"LogsPage.js","names":[],"sources":["../../src/pages/LogsPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { Bot } from 'lucide-react';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { EmptyState, GetHelpButton, PageHeader } from '@/components/shared';\nimport { LogViewer } from '@/components/logs/LogViewer';\nimport { SearchableSelect } from '@/components/shared/SearchableSelect';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport { useCachedInitialData } from '@/hooks/useCachedInitialData';\nimport { useActiveAgentProfile } from '@/hooks/useActiveAgentProfile';\nimport { useVibeControlsStore } from '@/store/vibeControlsStore';\n\nexport function LogsPage() {\n const { t } = useI18n();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const { agents: cachedAgents, loading } = useCachedInitialData();\n const [selectedAgentId, setSelectedAgentId] = useState('');\n\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n // Phase 5B — store-backed active-profile lookup keyed by agent id. The\n // hook can only be called once at the top level, so the store map is\n // the only safe way to attach a per-row `profile` inside the .map()\n // below. Each agent row will fall back to the literal \"default\" string\n // until the user opens that agent's Profiles tab and the AgentProfile\n // rows for it land in the store.\n const profileMap = useVibeControlsStore((s) => s.activeProfilePerAgent);\n\n // Pre-warm the store for the currently-selected agent so its row shows\n // the correct profile once profiles are loaded.\n useActiveAgentProfile(selectedAgentId || null);\n\n const agents = useMemo(\n () =>\n cachedAgents\n .filter((agent) => agent.tunnelUrl)\n .map((agent) => ({\n id: agent.id,\n name: agent.name || agent.hostname || agent.id,\n isActive: agent.isActive,\n hostname: agent.hostname,\n platform: agent.platform,\n profile: profileMap[agent.id] ?? 'default',\n })),\n [cachedAgents, profileMap]\n );\n\n const selectedAgent = agents.find((agent) => agent.id === selectedAgentId);\n\n if (permissionsLoading)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <p className=\"text-text-secondary\">Loading permissions…</p>\n </div>\n );\n if (!hasPermission(VIBE_PERMISSIONS.LOG_LIST))\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Access Denied</h2>\n <p className=\"text-sm text-text-secondary\">\n You don't have permission to access this page.\n </p>\n </div>\n </div>\n );\n\n if (loading && agents.length === 0) {\n return (\n <MotionPage className=\"flex h-full flex-col gap-6 px-6 lg:px-8 py-6\">\n <PageHeader\n title={tr('nav.logs', 'Logs')}\n description={tr('logs.description', 'Gateway-proxied log streaming from your agents')}\n />\n <PageSkeleton variant=\"rows\" count={6} />\n </MotionPage>\n );\n }\n\n return (\n <MotionPage className=\"flex h-full flex-col gap-6 px-6 lg:px-8 py-6\">\n <PageHeader\n title={tr('nav.logs', 'Logs')}\n description={tr('logs.description', 'Gateway-proxied log streaming from your agents')}\n />\n\n <div className=\"flex flex-col items-start gap-3 rounded-card border border-border-subtle bg-bg-surface p-4 shadow-elevation-1 sm:flex-row sm:items-center\">\n <label className=\"text-sm font-medium text-text-secondary\">\n {tr('logs.agentLabel', 'Agent:')}\n </label>\n\n {agents.length > 0 ? (\n <div className=\"w-full sm:w-80\">\n <SearchableSelect\n options={agents.map((agent) => ({\n value: agent.id,\n label: agent.name,\n description: `${agent.platform ?? ''} · ${agent.hostname ?? ''}`,\n }))}\n value={selectedAgentId}\n onChange={(value) => setSelectedAgentId(value)}\n placeholder={tr('logs.selectAgent', 'Select an agent...')}\n />\n </div>\n ) : (\n <span className=\"text-sm text-text-tertiary\">\n {tr('logs.noAgentsAvailable', 'No agents with tunnel access available')}\n </span>\n )}\n\n {selectedAgent ? (\n <div className=\"flex items-center gap-1.5 text-xs text-text-tertiary\">\n <div\n className={`size-2 rounded-full ${selectedAgent.isActive ? 'bg-status-success-bg' : 'bg-bg-sunken'}`}\n />\n {selectedAgent.hostname}\n </div>\n ) : null}\n </div>\n\n {selectedAgent ? (\n <LogViewer agentId={selectedAgent.id} profile={selectedAgent.profile} className=\"flex-1\" />\n ) : (\n <EmptyState\n icon={<Bot size={48} />}\n title={tr('logs.noAgentSelected', 'Select an agent to view logs')}\n description={tr(\n 'logs.noAgentSelectedDescription',\n 'Logs are fetched on demand. Choose an agent above to start streaming its gateway-proxied logs.'\n )}\n action={\n <GetHelpButton prompt=\"What are Logs in VibeControls and how do I investigate them?\" />\n }\n />\n )}\n </MotionPage>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAcA,SAAgB,IAAW;CACzB,IAAM,EAAE,SAAM,GAAS,EACjB,EAAE,kBAAe,WAAW,MAAuB,GAAgB,EACnE,EAAE,QAAQ,GAAc,eAAY,GAAsB,EAC1D,CAAC,GAAiB,KAAsB,EAAS,GAAG,EAEpD,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAQ,EAAE,EAAI;AACpB,SAAO,MAAU,IAAM,IAAW;IAS9B,IAAa,GAAsB,MAAM,EAAE,sBAAsB;AAIvE,GAAsB,KAAmB,KAAK;CAE9C,IAAM,IAAS,QAEX,EACG,QAAQ,MAAU,EAAM,UAAU,CAClC,KAAK,OAAW;EACf,IAAI,EAAM;EACV,MAAM,EAAM,QAAQ,EAAM,YAAY,EAAM;EAC5C,UAAU,EAAM;EAChB,UAAU,EAAM;EAChB,UAAU,EAAM;EAChB,SAAS,EAAW,EAAM,OAAO;EAClC,EAAE,EACP,CAAC,GAAc,EAAW,CAC3B,EAEK,IAAgB,EAAO,MAAM,MAAU,EAAM,OAAO,EAAgB;AAgC1E,QA9BI,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,KAAD;GAAG,WAAU;aAAsB;GAAwB,CAAA;EACvD,CAAA,GAEL,EAAc,EAAiB,SAAS,GAYzC,KAAW,EAAO,WAAW,IAE7B,kBAAC,GAAD;EAAY,WAAU;YAAtB,CACE,kBAAC,GAAD;GACE,OAAO,EAAG,YAAY,OAAO;GAC7B,aAAa,EAAG,oBAAoB,iDAAiD;GACrF,CAAA,EACF,kBAAC,GAAD;GAAc,SAAQ;GAAO,OAAO;GAAK,CAAA,CAC9B;MAKf,kBAAC,GAAD;EAAY,WAAU;YAAtB;GACE,kBAAC,GAAD;IACE,OAAO,EAAG,YAAY,OAAO;IAC7B,aAAa,EAAG,oBAAoB,iDAAiD;IACrF,CAAA;GAEF,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,SAAD;MAAO,WAAU;gBACd,EAAG,mBAAmB,SAAS;MAC1B,CAAA;KAEP,EAAO,SAAS,IACf,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD;OACE,SAAS,EAAO,KAAK,OAAW;QAC9B,OAAO,EAAM;QACb,OAAO,EAAM;QACb,aAAa,GAAG,EAAM,YAAY,GAAG,KAAK,EAAM,YAAY;QAC7D,EAAE;OACH,OAAO;OACP,WAAW,MAAU,EAAmB,EAAM;OAC9C,aAAa,EAAG,oBAAoB,qBAAqB;OACzD,CAAA;MACE,CAAA,GAEN,kBAAC,QAAD;MAAM,WAAU;gBACb,EAAG,0BAA0B,yCAAyC;MAClE,CAAA;KAGR,IACC,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD,EACE,WAAW,uBAAuB,EAAc,WAAW,yBAAyB,kBACpF,CAAA,EACD,EAAc,SACX;UACJ;KACA;;GAEL,IACC,kBAAC,GAAD;IAAW,SAAS,EAAc;IAAI,SAAS,EAAc;IAAS,WAAU;IAAW,CAAA,GAE3F,kBAAC,GAAD;IACE,MAAM,kBAAC,GAAD,EAAK,MAAM,IAAM,CAAA;IACvB,OAAO,EAAG,wBAAwB,+BAA+B;IACjE,aAAa,EACX,mCACA,iGACD;IACD,QACE,kBAAC,GAAD,EAAe,QAAO,gEAAiE,CAAA;IAEzF,CAAA;GAEO;MA9EX,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,MAAD;IAAI,WAAU;cAA0C;IAAkB,CAAA,EAC1E,kBAAC,KAAD;IAAG,WAAU;cAA8B;IAEvC,CAAA,CACA;;EACF,CAAA"}
@@ -31,7 +31,7 @@ function b() {
31
31
  children: x("noteDetails.loadingPermissions", "Loading permissions...")
32
32
  })
33
33
  }) : E ? O && !j ? /* @__PURE__ */ v(y, {
34
- className: "space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen",
34
+ className: "space-y-6 px-6 lg:px-8 py-6 bg-bg-canvas min-h-screen",
35
35
  children: [/* @__PURE__ */ v("button", {
36
36
  type: "button",
37
37
  onClick: () => C("/vibes"),
@@ -39,7 +39,7 @@ function b() {
39
39
  children: [/* @__PURE__ */ _(u, { className: "size-4" }), x("noteDetails.back", "Back")]
40
40
  }), /* @__PURE__ */ _(s, { variant: "detail" })]
41
41
  }) : k && !j ? /* @__PURE__ */ v(y, {
42
- className: "space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen",
42
+ className: "space-y-6 px-6 lg:px-8 py-6 bg-bg-canvas min-h-screen",
43
43
  children: [/* @__PURE__ */ v("button", {
44
44
  type: "button",
45
45
  onClick: () => C("/vibes"),
@@ -51,7 +51,7 @@ function b() {
51
51
  onRetry: () => A()
52
52
  })]
53
53
  }) : j ? /* @__PURE__ */ v(y, {
54
- className: "space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen",
54
+ className: "space-y-6 px-6 lg:px-8 py-6 bg-bg-canvas min-h-screen",
55
55
  children: [/* @__PURE__ */ _(n, {
56
56
  title: j.title,
57
57
  description: j.vibe ? `${x("noteDetails.attachedTo", "Attached to")} ${j.vibe.name}` : x("noteDetails.workspaceNote", "Workspace note"),
@@ -137,7 +137,7 @@ function b() {
137
137
  })]
138
138
  })]
139
139
  }) : /* @__PURE__ */ v(y, {
140
- className: "space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen",
140
+ className: "space-y-6 px-6 lg:px-8 py-6 bg-bg-canvas min-h-screen",
141
141
  children: [/* @__PURE__ */ v("button", {
142
142
  type: "button",
143
143
  onClick: () => C("/vibes"),
@@ -153,7 +153,7 @@ function b() {
153
153
  children: /* @__PURE__ */ v("div", {
154
154
  className: "text-center space-y-2",
155
155
  children: [/* @__PURE__ */ _("h2", {
156
- className: "text-lg font-semibold text-foreground",
156
+ className: "text-lg font-semibold text-text-primary",
157
157
  children: x("noteDetails.accessDenied", "Access Denied")
158
158
  }), /* @__PURE__ */ _("p", {
159
159
  className: "text-sm text-text-secondary",
@@ -1 +1 @@
1
- {"version":3,"file":"NoteDetailsPage.js","names":[],"sources":["../../src/pages/NoteDetailsPage.tsx"],"sourcesContent":["import { useCallback } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { ArrowLeft, ExternalLink, FileText, Pin, Tag } from 'lucide-react';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { Card, EmptyState, ErrorState, PageHeader } from '@/components/shared';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useGetNoteQuery } from '@/generated/wspace-operations';\nimport { useParams } from 'react-router-dom';\n\nexport function NoteDetailsPage() {\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n const { id } = useParams<{ id: string }>();\n const navigate = useVibeNavigate();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n\n const canReadNote = hasPermission(VIBE_PERMISSIONS.NOTE_READ);\n const { data, loading, error, refetch } = useGetNoteQuery({\n variables: { id: id || '' },\n skip: !id || permissionsLoading || !canReadNote,\n fetchPolicy: 'cache-and-network',\n });\n\n const note = data?.note;\n\n if (permissionsLoading)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <p className=\"text-text-secondary\">\n {tr('noteDetails.loadingPermissions', 'Loading permissions...')}\n </p>\n </div>\n );\n if (!canReadNote)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold text-foreground\">\n {tr('noteDetails.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr('noteDetails.accessDeniedDesc', \"You don't have permission to access this page.\")}\n </p>\n </div>\n </div>\n );\n\n if (loading && !note) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n type=\"button\"\n onClick={() => navigate('/vibes')}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('noteDetails.back', 'Back')}\n </button>\n <PageSkeleton variant=\"detail\" />\n </MotionPage>\n );\n }\n\n if (error && !note) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n type=\"button\"\n onClick={() => navigate('/vibes')}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </button>\n <ErrorState\n title={tr('noteDetails.failedLoad', 'Failed to load note')}\n message={error.message}\n onRetry={() => refetch()}\n />\n </MotionPage>\n );\n }\n\n if (!note) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n type=\"button\"\n onClick={() => navigate('/vibes')}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </button>\n <EmptyState\n icon={<FileText className=\"size-8 text-text-secondary\" />}\n title={tr('noteDetails.notFound', 'Note not found')}\n description={tr(\n 'noteDetails.notFoundDesc',\n 'This note may have been deleted or you may no longer have access to it.'\n )}\n />\n </MotionPage>\n );\n }\n\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <PageHeader\n title={note.title}\n description={\n note.vibe\n ? `${tr('noteDetails.attachedTo', 'Attached to')} ${note.vibe.name}`\n : tr('noteDetails.workspaceNote', 'Workspace note')\n }\n actions={\n <div className=\"flex items-center gap-2\">\n {note.vibe && (\n <button\n type=\"button\"\n onClick={() => navigate(`/vibes/${note.vibe?.id}`)}\n className=\"inline-flex items-center gap-2 px-3 py-2 rounded-md border border-border-default bg-bg-surface hover:bg-bg-muted text-sm text-text-primary transition-colors\"\n >\n <ExternalLink className=\"size-4\" />\n {tr('noteDetails.openVibe', 'Open Vibe')}\n </button>\n )}\n </div>\n }\n />\n\n <div className=\"grid gap-6 lg:grid-cols-[minmax(0,1fr)_320px]\">\n <Card>\n <div className=\"p-5 space-y-4\">\n <div className=\"flex flex-wrap items-center gap-2 text-xs text-text-tertiary\">\n {note.isPinned && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-primary-default/10 px-2 py-1 text-primary-default\">\n <Pin className=\"size-3.5\" />\n {tr('noteDetails.pinned', 'Pinned')}\n </span>\n )}\n <span>\n {tr('noteDetails.created', 'Created')} {new Date(note.createdAt).toLocaleString()}\n </span>\n <span>\n {tr('noteDetails.updated', 'Updated')} {new Date(note.updatedAt).toLocaleString()}\n </span>\n </div>\n\n <div className=\"rounded-lg border border-border-default bg-bg-canvas p-4\">\n <pre className=\"whitespace-pre-wrap break-words text-sm text-text-primary font-sans leading-6\">\n {note.content}\n </pre>\n </div>\n </div>\n </Card>\n\n <div className=\"space-y-6\">\n <Card>\n <div className=\"p-5 space-y-4\">\n <h2 className=\"text-sm font-semibold text-text-primary\">\n {tr('noteDetails.details', 'Details')}\n </h2>\n <div className=\"space-y-3 text-sm\">\n <div>\n <div className=\"text-text-tertiary\">{tr('noteDetails.noteId', 'Note ID')}</div>\n <div className=\"font-mono text-text-primary break-all\">{note.id}</div>\n </div>\n {note.vibe && (\n <div>\n <div className=\"text-text-tertiary\">{tr('noteDetails.vibe', 'Vibe')}</div>\n <button\n type=\"button\"\n onClick={() => navigate(`/vibes/${note.vibe?.id}`)}\n className=\"text-primary-default hover:underline\"\n >\n {note.vibe.name}\n </button>\n </div>\n )}\n </div>\n </div>\n </Card>\n\n <Card>\n <div className=\"p-5 space-y-4\">\n <h2 className=\"text-sm font-semibold text-text-primary\">\n {tr('noteDetails.tags', 'Tags')}\n </h2>\n {note.tags.length > 0 ? (\n <div className=\"flex flex-wrap gap-2\">\n {note.tags.map((tag: string) => (\n <span\n key={tag}\n className=\"inline-flex items-center gap-1 rounded-full bg-bg-muted px-2 py-1 text-xs text-text-secondary\"\n >\n <Tag className=\"size-3\" />\n {tag}\n </span>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-text-tertiary\">\n {tr('noteDetails.noTags', 'No tags assigned.')}\n </p>\n )}\n </div>\n </Card>\n </div>\n </div>\n </MotionPage>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"NoteDetailsPage.js","names":[],"sources":["../../src/pages/NoteDetailsPage.tsx"],"sourcesContent":["import { useCallback } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { ArrowLeft, ExternalLink, FileText, Pin, Tag } from 'lucide-react';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { Card, EmptyState, ErrorState, PageHeader } from '@/components/shared';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useGetNoteQuery } from '@/generated/wspace-operations';\nimport { useParams } from 'react-router-dom';\n\nexport function NoteDetailsPage() {\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n const { id } = useParams<{ id: string }>();\n const navigate = useVibeNavigate();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n\n const canReadNote = hasPermission(VIBE_PERMISSIONS.NOTE_READ);\n const { data, loading, error, refetch } = useGetNoteQuery({\n variables: { id: id || '' },\n skip: !id || permissionsLoading || !canReadNote,\n fetchPolicy: 'cache-and-network',\n });\n\n const note = data?.note;\n\n if (permissionsLoading)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <p className=\"text-text-secondary\">\n {tr('noteDetails.loadingPermissions', 'Loading permissions...')}\n </p>\n </div>\n );\n if (!canReadNote)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('noteDetails.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr('noteDetails.accessDeniedDesc', \"You don't have permission to access this page.\")}\n </p>\n </div>\n </div>\n );\n\n if (loading && !note) {\n return (\n <MotionPage className=\"space-y-6 px-6 lg:px-8 py-6 bg-bg-canvas min-h-screen\">\n <button\n type=\"button\"\n onClick={() => navigate('/vibes')}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('noteDetails.back', 'Back')}\n </button>\n <PageSkeleton variant=\"detail\" />\n </MotionPage>\n );\n }\n\n if (error && !note) {\n return (\n <MotionPage className=\"space-y-6 px-6 lg:px-8 py-6 bg-bg-canvas min-h-screen\">\n <button\n type=\"button\"\n onClick={() => navigate('/vibes')}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </button>\n <ErrorState\n title={tr('noteDetails.failedLoad', 'Failed to load note')}\n message={error.message}\n onRetry={() => refetch()}\n />\n </MotionPage>\n );\n }\n\n if (!note) {\n return (\n <MotionPage className=\"space-y-6 px-6 lg:px-8 py-6 bg-bg-canvas min-h-screen\">\n <button\n type=\"button\"\n onClick={() => navigate('/vibes')}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </button>\n <EmptyState\n icon={<FileText className=\"size-8 text-text-secondary\" />}\n title={tr('noteDetails.notFound', 'Note not found')}\n description={tr(\n 'noteDetails.notFoundDesc',\n 'This note may have been deleted or you may no longer have access to it.'\n )}\n />\n </MotionPage>\n );\n }\n\n return (\n <MotionPage className=\"space-y-6 px-6 lg:px-8 py-6 bg-bg-canvas min-h-screen\">\n <PageHeader\n title={note.title}\n description={\n note.vibe\n ? `${tr('noteDetails.attachedTo', 'Attached to')} ${note.vibe.name}`\n : tr('noteDetails.workspaceNote', 'Workspace note')\n }\n actions={\n <div className=\"flex items-center gap-2\">\n {note.vibe && (\n <button\n type=\"button\"\n onClick={() => navigate(`/vibes/${note.vibe?.id}`)}\n className=\"inline-flex items-center gap-2 px-3 py-2 rounded-md border border-border-default bg-bg-surface hover:bg-bg-muted text-sm text-text-primary transition-colors\"\n >\n <ExternalLink className=\"size-4\" />\n {tr('noteDetails.openVibe', 'Open Vibe')}\n </button>\n )}\n </div>\n }\n />\n\n <div className=\"grid gap-6 lg:grid-cols-[minmax(0,1fr)_320px]\">\n <Card>\n <div className=\"p-5 space-y-4\">\n <div className=\"flex flex-wrap items-center gap-2 text-xs text-text-tertiary\">\n {note.isPinned && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-primary-default/10 px-2 py-1 text-primary-default\">\n <Pin className=\"size-3.5\" />\n {tr('noteDetails.pinned', 'Pinned')}\n </span>\n )}\n <span>\n {tr('noteDetails.created', 'Created')} {new Date(note.createdAt).toLocaleString()}\n </span>\n <span>\n {tr('noteDetails.updated', 'Updated')} {new Date(note.updatedAt).toLocaleString()}\n </span>\n </div>\n\n <div className=\"rounded-lg border border-border-default bg-bg-canvas p-4\">\n <pre className=\"whitespace-pre-wrap break-words text-sm text-text-primary font-sans leading-6\">\n {note.content}\n </pre>\n </div>\n </div>\n </Card>\n\n <div className=\"space-y-6\">\n <Card>\n <div className=\"p-5 space-y-4\">\n <h2 className=\"text-sm font-semibold text-text-primary\">\n {tr('noteDetails.details', 'Details')}\n </h2>\n <div className=\"space-y-3 text-sm\">\n <div>\n <div className=\"text-text-tertiary\">{tr('noteDetails.noteId', 'Note ID')}</div>\n <div className=\"font-mono text-text-primary break-all\">{note.id}</div>\n </div>\n {note.vibe && (\n <div>\n <div className=\"text-text-tertiary\">{tr('noteDetails.vibe', 'Vibe')}</div>\n <button\n type=\"button\"\n onClick={() => navigate(`/vibes/${note.vibe?.id}`)}\n className=\"text-primary-default hover:underline\"\n >\n {note.vibe.name}\n </button>\n </div>\n )}\n </div>\n </div>\n </Card>\n\n <Card>\n <div className=\"p-5 space-y-4\">\n <h2 className=\"text-sm font-semibold text-text-primary\">\n {tr('noteDetails.tags', 'Tags')}\n </h2>\n {note.tags.length > 0 ? (\n <div className=\"flex flex-wrap gap-2\">\n {note.tags.map((tag: string) => (\n <span\n key={tag}\n className=\"inline-flex items-center gap-1 rounded-full bg-bg-muted px-2 py-1 text-xs text-text-secondary\"\n >\n <Tag className=\"size-3\" />\n {tag}\n </span>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-text-tertiary\">\n {tr('noteDetails.noTags', 'No tags assigned.')}\n </p>\n )}\n </div>\n </Card>\n </div>\n </div>\n </MotionPage>\n );\n}\n"],"mappings":"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"}
@@ -139,11 +139,11 @@ function D() {
139
139
  children: /* @__PURE__ */ S(a, { message: A("overviewPage.loadingPermissions", "Loading permissions...") })
140
140
  });
141
141
  if (!k(u.VIBE_LIST)) return /* @__PURE__ */ S("div", {
142
- className: "flex items-center justify-center min-h-[400px]",
142
+ className: "flex items-center justify-center min-h-[400px] px-4",
143
143
  children: /* @__PURE__ */ C("div", {
144
- className: "text-center space-y-2",
144
+ className: "text-center space-y-2 max-w-sm",
145
145
  children: [/* @__PURE__ */ S("h2", {
146
- className: "text-lg font-semibold text-foreground",
146
+ className: "text-lg font-semibold tracking-tight text-text-primary",
147
147
  children: A("common.accessDeniedTitle", "Access Denied")
148
148
  }), /* @__PURE__ */ S("p", {
149
149
  className: "text-sm text-text-secondary",
@@ -162,16 +162,16 @@ function D() {
162
162
  note_created: te,
163
163
  module_installed: g
164
164
  }[e] || g, { className: "size-4" }), ce = (e) => ({
165
- green: "bg-status-success-bg text-status-success-text",
166
- blue: "bg-status-info-bg text-status-info-text",
167
- purple: "bg-accent-purple-subtle text-accent-purple",
168
- orange: "bg-status-warning-bg text-status-warning-text",
165
+ green: "bg-status-success-bg-subtle text-status-success-text",
166
+ blue: "bg-[var(--color-accent-soft)] text-text-primary",
167
+ purple: "bg-accent-purple-subtle text-text-primary",
168
+ orange: "bg-status-warning-bg-subtle text-status-warning-text",
169
169
  cyan: "bg-accent-teal-subtle text-accent-teal",
170
- yellow: "bg-status-warning-bg text-status-warning-text",
171
- pink: "bg-accent-purple-subtle text-accent-purple"
170
+ yellow: "bg-status-warning-bg-subtle text-status-warning-text",
171
+ pink: "bg-accent-purple-subtle text-text-primary"
172
172
  })[e] || "bg-bg-sunken text-text-secondary";
173
173
  return /* @__PURE__ */ C(E, {
174
- className: "space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen",
174
+ className: "space-y-6 px-6 lg:px-8 py-6 bg-bg-canvas min-h-screen",
175
175
  children: [
176
176
  /* @__PURE__ */ S(i, {
177
177
  title: A("overviewPage.title", "VibeControls Overview"),
@@ -180,8 +180,11 @@ function D() {
180
180
  type: "button",
181
181
  onClick: V,
182
182
  disabled: P,
183
- className: "flex items-center gap-2 px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary hover:bg-bg-sunken/80 transition-colors disabled:opacity-50",
184
- children: [/* @__PURE__ */ S(ie, { className: `size-4 ${P ? "animate-spin" : ""}` }), A("overviewPage.refresh", "Refresh")]
183
+ className: "inline-flex items-center gap-2 px-3 py-2 text-sm bg-bg-surface border border-border-subtle rounded-button text-text-primary hover:bg-bg-sunken hover:border-border-strong transition-all duration-200 disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
184
+ children: [/* @__PURE__ */ S(ie, {
185
+ className: `size-4 ${P ? "animate-spin" : ""}`,
186
+ "aria-hidden": "true"
187
+ }), A("overviewPage.refresh", "Refresh")]
185
188
  })
186
189
  }),
187
190
  F && !P && /* @__PURE__ */ S(o, {
@@ -251,31 +254,40 @@ function D() {
251
254
  children: /* @__PURE__ */ C(s, {
252
255
  className: "flex h-full flex-col",
253
256
  children: [/* @__PURE__ */ C("div", {
254
- className: "flex items-center justify-between mb-4",
257
+ className: "flex items-center justify-between mb-5",
255
258
  children: [/* @__PURE__ */ C("h2", {
256
- className: "text-lg font-semibold text-text-primary flex items-center gap-2",
257
- children: [/* @__PURE__ */ S(_, { className: "size-5" }), A("overviewPage.sections.activeAgents", "Active Agents")]
259
+ className: "text-lg font-semibold tracking-tight text-text-primary flex items-center gap-2.5",
260
+ children: [/* @__PURE__ */ S("span", {
261
+ className: "inline-flex size-8 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary",
262
+ children: /* @__PURE__ */ S(_, { className: "size-5" })
263
+ }), A("overviewPage.sections.activeAgents", "Active Agents")]
258
264
  }), /* @__PURE__ */ S("button", {
259
265
  type: "button",
260
266
  onClick: () => D("/agents"),
261
- className: "text-sm text-text-link hover:underline",
267
+ className: "text-sm font-medium text-text-link hover:underline",
262
268
  "aria-label": A("overviewPage.sections.viewAllAgents", "View all agents"),
263
269
  children: A("overviewPage.sections.viewAll", "View all")
264
270
  })]
265
271
  }), Y.length > 0 ? /* @__PURE__ */ S(T, {
266
- className: "grid grid-cols-1 sm:grid-cols-2 gap-3 flex-1 content-start",
272
+ className: "grid grid-cols-1 sm:grid-cols-2 gap-4 flex-1 content-start",
267
273
  stagger: .04,
268
274
  children: Y.slice(0, 4).map((e) => /* @__PURE__ */ S(w, { children: /* @__PURE__ */ S(d, {
269
275
  agent: e,
270
276
  onSelect: (e) => D(`/agents/${e.id}`)
271
277
  }) }, e.id))
272
278
  }) : /* @__PURE__ */ C("div", {
273
- className: "flex flex-1 flex-col items-center justify-center text-center py-6 text-text-secondary",
279
+ className: "flex flex-1 flex-col items-center justify-center text-center py-8 text-text-secondary",
274
280
  children: [
275
- /* @__PURE__ */ S(_, { className: "size-12 mx-auto mb-2 opacity-50" }),
276
- /* @__PURE__ */ S("p", { children: A("overviewPage.empty.noActiveAgents", "No active agents") }),
281
+ /* @__PURE__ */ S("span", {
282
+ className: "inline-flex size-12 items-center justify-center rounded-full bg-[var(--color-accent-soft)] text-text-primary mb-3",
283
+ children: /* @__PURE__ */ S(_, { className: "size-6" })
284
+ }),
285
+ /* @__PURE__ */ S("p", {
286
+ className: "text-base font-semibold text-text-primary",
287
+ children: A("overviewPage.empty.noActiveAgents", "No active agents")
288
+ }),
277
289
  /* @__PURE__ */ S("p", {
278
- className: "text-xs text-text-muted mt-1 mb-3",
290
+ className: "text-xs text-text-muted mt-1 mb-4",
279
291
  children: A("overviewPage.empty.installCliHint", "Install the agent CLI to get started")
280
292
  }),
281
293
  /* @__PURE__ */ C("div", {
@@ -307,14 +319,17 @@ function D() {
307
319
  children: /* @__PURE__ */ C(s, {
308
320
  className: "flex h-full flex-col",
309
321
  children: [/* @__PURE__ */ C("div", {
310
- className: "flex items-center justify-between mb-4",
322
+ className: "flex items-center justify-between mb-5",
311
323
  children: [/* @__PURE__ */ C("h2", {
312
- className: "text-lg font-semibold text-text-primary flex items-center gap-2",
313
- children: [/* @__PURE__ */ S(g, { className: "size-5" }), A("overviewPage.sections.recentActivity", "Recent Activity")]
324
+ className: "text-lg font-semibold tracking-tight text-text-primary flex items-center gap-2.5",
325
+ children: [/* @__PURE__ */ S("span", {
326
+ className: "inline-flex size-8 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary",
327
+ children: /* @__PURE__ */ S(g, { className: "size-5" })
328
+ }), A("overviewPage.sections.recentActivity", "Recent Activity")]
314
329
  }), /* @__PURE__ */ S("button", {
315
330
  type: "button",
316
331
  onClick: () => D("/audit"),
317
- className: "text-sm text-text-link hover:underline",
332
+ className: "text-sm font-medium text-text-link hover:underline",
318
333
  "aria-label": A("overviewPage.sections.viewAllActivity", "View all activity"),
319
334
  children: "View all"
320
335
  })]
@@ -360,63 +375,81 @@ function D() {
360
375
  })]
361
376
  }),
362
377
  /* @__PURE__ */ S(w, { children: /* @__PURE__ */ C(s, { children: [/* @__PURE__ */ C("div", {
363
- className: "flex items-center justify-between mb-4",
378
+ className: "flex items-center justify-between mb-5",
364
379
  children: [/* @__PURE__ */ C("h2", {
365
- className: "text-lg font-semibold text-text-primary flex items-center gap-2",
366
- children: [/* @__PURE__ */ S(y, { className: "size-5" }), A("overviewPage.sections.runningSessions", "Running Sessions")]
380
+ className: "text-lg font-semibold tracking-tight text-text-primary flex items-center gap-2.5",
381
+ children: [/* @__PURE__ */ S("span", {
382
+ className: "inline-flex size-8 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary",
383
+ children: /* @__PURE__ */ S(y, { className: "size-5" })
384
+ }), A("overviewPage.sections.runningSessions", "Running Sessions")]
367
385
  }), /* @__PURE__ */ S("button", {
368
386
  type: "button",
369
387
  onClick: () => D("/sessions"),
370
- className: "text-sm text-text-link hover:underline",
388
+ className: "text-sm font-medium text-text-link hover:underline",
371
389
  children: A("overviewPage.sections.viewAll", "View all")
372
390
  })]
373
391
  }), X.length > 0 ? /* @__PURE__ */ S(T, {
374
- className: "grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-3",
392
+ className: "grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-4",
375
393
  stagger: .04,
376
394
  children: X.slice(0, 6).map((e) => /* @__PURE__ */ S(w, { children: /* @__PURE__ */ S(ee, {
377
395
  session: e,
378
396
  onOpen: (e) => D(`/sessions/${e.id}`)
379
397
  }) }, e.id))
380
398
  }) : /* @__PURE__ */ C("div", {
381
- className: "text-center py-8 text-text-secondary",
399
+ className: "flex flex-col items-center text-center py-10 text-text-secondary",
382
400
  children: [
383
- /* @__PURE__ */ S(y, { className: "size-12 mx-auto mb-2 opacity-50" }),
384
- /* @__PURE__ */ S("p", { children: A("overviewPage.empty.noRunningSessions", "No running sessions") }),
401
+ /* @__PURE__ */ S("span", {
402
+ className: "inline-flex size-12 items-center justify-center rounded-full bg-[var(--color-accent-soft)] text-text-primary mb-3",
403
+ children: /* @__PURE__ */ S(y, { className: "size-6" })
404
+ }),
405
+ /* @__PURE__ */ S("p", {
406
+ className: "text-base font-semibold text-text-primary",
407
+ children: A("overviewPage.empty.noRunningSessions", "No running sessions")
408
+ }),
385
409
  /* @__PURE__ */ S("button", {
386
410
  type: "button",
387
411
  onClick: () => D("/sessions"),
388
- className: "mt-2 text-sm text-text-link hover:underline",
412
+ className: "mt-3 text-sm font-medium text-text-link hover:underline",
389
413
  children: A("overviewPage.empty.startSession", "Start a session")
390
414
  })
391
415
  ]
392
416
  })] }) }),
393
417
  /* @__PURE__ */ S(w, { children: /* @__PURE__ */ C(s, { children: [/* @__PURE__ */ C("div", {
394
- className: "flex items-center justify-between mb-4",
418
+ className: "flex items-center justify-between mb-5",
395
419
  children: [/* @__PURE__ */ C("h2", {
396
- className: "text-lg font-semibold text-text-primary flex items-center gap-2",
397
- children: [/* @__PURE__ */ S(b, { className: "size-5" }), A("overviewPage.sections.activeVibes", "Active Vibes")]
420
+ className: "text-lg font-semibold tracking-tight text-text-primary flex items-center gap-2.5",
421
+ children: [/* @__PURE__ */ S("span", {
422
+ className: "inline-flex size-8 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary",
423
+ children: /* @__PURE__ */ S(b, { className: "size-5" })
424
+ }), A("overviewPage.sections.activeVibes", "Active Vibes")]
398
425
  }), /* @__PURE__ */ S("button", {
399
426
  type: "button",
400
427
  onClick: () => D("/vibes"),
401
- className: "text-sm text-text-link hover:underline",
428
+ className: "text-sm font-medium text-text-link hover:underline",
402
429
  children: A("overviewPage.sections.viewAll", "View all")
403
430
  })]
404
431
  }), Z.length > 0 ? /* @__PURE__ */ S(T, {
405
- className: "grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-3",
432
+ className: "grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-4",
406
433
  stagger: .04,
407
434
  children: Z.slice(0, 6).map((e) => /* @__PURE__ */ S(w, { children: /* @__PURE__ */ S(f, {
408
435
  vibe: e,
409
436
  onOpen: (e) => D(`/vibes/${e.id}`)
410
437
  }) }, e.id))
411
438
  }) : /* @__PURE__ */ C("div", {
412
- className: "text-center py-8 text-text-secondary",
439
+ className: "flex flex-col items-center text-center py-10 text-text-secondary",
413
440
  children: [
414
- /* @__PURE__ */ S(b, { className: "size-12 mx-auto mb-2 opacity-50" }),
415
- /* @__PURE__ */ S("p", { children: A("overviewPage.empty.noActiveVibes", "No active vibes") }),
441
+ /* @__PURE__ */ S("span", {
442
+ className: "inline-flex size-12 items-center justify-center rounded-full bg-[var(--color-accent-soft)] text-text-primary mb-3",
443
+ children: /* @__PURE__ */ S(b, { className: "size-6" })
444
+ }),
445
+ /* @__PURE__ */ S("p", {
446
+ className: "text-base font-semibold text-text-primary",
447
+ children: A("overviewPage.empty.noActiveVibes", "No active vibes")
448
+ }),
416
449
  /* @__PURE__ */ S("button", {
417
450
  type: "button",
418
451
  onClick: () => D("/vibes"),
419
- className: "mt-2 text-sm text-text-link hover:underline",
452
+ className: "mt-3 text-sm font-medium text-text-link hover:underline",
420
453
  children: A("overviewPage.empty.addVibe", "Add a vibe")
421
454
  })
422
455
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"OverviewPage.js","names":[],"sources":["../../src/pages/OverviewPage.tsx"],"sourcesContent":["import { useCallback, useMemo } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport {\n Bot,\n Zap,\n Terminal,\n FileText,\n Activity,\n Server,\n Play,\n GitBranch,\n RefreshCw,\n} from 'lucide-react';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { AgentCard } from '@/components/agents';\nimport { SessionCard } from '@/components/sessions';\nimport { VibeCard } from '@/components/vibes';\nimport {\n PageHeader,\n StatCard,\n Card,\n AgentSetupBanner,\n ErrorState,\n LoadingState,\n} from '@/components/shared';\nimport { useVibeNavigate, useVibeControls } from '@/providers/VibeControlsProvider';\nimport { useCachedInitialData } from '@/hooks/useCachedInitialData';\nimport { useListTargetsQuery } from '@/generated/wspace-operations';\nimport type { Vibe } from '@/components/vibes/types';\nimport type { Session } from '@/components/sessions/types';\n\n/**\n * Overview page showing summary dashboard with stats, active resources, and recent activity\n *\n * This page now uses cached initial data from Apollo instead of independent queries,\n * reducing GraphQL calls from 5 to 0 for the initial render.\n * The refresh button triggers a refetch of the initial data.\n */\nexport function OverviewPage() {\n const handleNavigate = useVibeNavigate();\n const { t } = useI18n();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n\n const tr = (key: string, fallback: string, params?: Record<string, string | number>): string => {\n const translated = t(key, params);\n const template = translated === key ? fallback : translated;\n\n if (!params) return template;\n\n return Object.entries(params).reduce(\n (acc, [paramKey, paramValue]) => acc.replace(`{{${paramKey}}}`, String(paramValue)),\n template\n );\n };\n\n // Use cached initial data - reads from Apollo cache, no network request\n // Loading state reflects whether initial data has been loaded\n const {\n agents: cachedAgents,\n vibes: cachedVibes,\n sessions: cachedSessions,\n loading,\n error: dataError,\n totals,\n refetch,\n } = useCachedInitialData();\n\n // Targets is intentionally NOT part of VibeControlsInitialData (it caused a 25s\n // timeout there), so fetch the count with a lightweight standalone query — the\n // same pattern HealthPage uses.\n const { workspaceId } = useVibeControls();\n const targetsCountQuery = useListTargetsQuery({\n variables: { workspaceId: workspaceId || '', limit: 50 },\n skip: !workspaceId,\n fetchPolicy: 'cache-and-network',\n });\n\n const refetchTargets = targetsCountQuery.refetch;\n // The header Refresh button must refetch BOTH the cached initial data and the\n // standalone targets query — otherwise the Targets stat card stays stale after\n // a manual refresh.\n const handleRefresh = useCallback(() => {\n void refetch();\n void refetchTargets();\n }, [refetch, refetchTargets]);\n\n const targetItems: Array<{ status: string }> = targetsCountQuery.data?.targets.items ?? [];\n const targetsTotal = targetsCountQuery.data?.targets.total ?? 0;\n // `targetsConnected` is derived from `targetItems`, which is capped by the\n // query `limit`. When the workspace has more targets than the page size the\n // connected count would only reflect the first page and contradict the\n // server-side `targetsTotal`. Track truncation so we only surface the\n // connected detail when it is accurate for the full set.\n const targetsTruncated = targetsTotal > targetItems.length;\n const targetsConnected = targetItems.filter(\n (t) => t.status === 'connected' || t.status === 'active'\n ).length;\n\n if (dataError) {\n console.error('[overview] failed to load initial workspace data', dataError);\n }\n\n // Map cached data to component types\n // Using type assertion since we know the structure from the GraphQL query\n const agents = useMemo(() => {\n return cachedAgents.map((agent) => ({\n id: agent.id,\n workspaceId: '',\n name: agent.name,\n hostname: agent.hostname,\n platform: agent.platform,\n architecture: agent.architecture || '',\n version: agent.version || '',\n isActive: agent.isActive,\n status: agent.isActive ? ('ACTIVE' as const) : ('OFFLINE' as const),\n lastHeartbeat: agent.lastHeartbeat || null,\n tunnelUrl: agent.tunnelUrl ?? null,\n tunnelStatus: agent.tunnelStatus ?? null,\n agentApiKey: null,\n aiContext: null,\n configuration: {},\n capabilities: [],\n metadata: {},\n createdAt: agent.createdAt || '',\n updatedAt: '',\n }));\n }, [cachedAgents]);\n\n const vibes = useMemo(() => {\n return cachedVibes.map((vibe) => ({\n id: vibe.id,\n workspaceId: '',\n name: vibe.name,\n description: '',\n type: (vibe.type || 'PROJECT') as Vibe['type'],\n status: vibe.status as Vibe['status'],\n path: '',\n parentId: null,\n gitBranch: null,\n gitRemote: null,\n gitStatus: null,\n environmentVariables: {},\n tags: [],\n metadata: {},\n createdAt: '',\n updatedAt: '',\n archivedAt: null,\n // Multi-agent: surface attached agents so Overview cards can render a\n // count badge (\"+N agents\") or single name. Empty when none.\n agents: vibe.agents ?? [],\n }));\n }, [cachedVibes]);\n\n const sessions: Session[] = useMemo(() => {\n return cachedSessions.map((session) => ({\n id: session.id,\n workspaceId: '',\n agentId: session.agentId ?? null,\n vibeId: session.vibeId ?? null,\n name: session.name,\n type: (session.type || 'TERMINAL') as Session['type'],\n status: session.status as Session['status'],\n command: null,\n workingDirectory: null,\n environmentVariables: {},\n autoStart: false,\n pid: null,\n exitCode: null,\n lastOutput: null,\n metadata: {},\n createdAt: session.createdAt || '',\n updatedAt: '',\n agent: undefined,\n vibe: undefined,\n }));\n }, [cachedSessions]);\n\n // Calculate stats from cached totals\n const activeAgents = agents.filter((a) => a.isActive);\n const runningSessions = sessions.filter((s) => s.status === 'RUNNING');\n const activeVibes = vibes.filter((v) => v.status === 'ACTIVE');\n\n const stats = {\n agents: {\n total: totals.agents,\n active: activeAgents.length,\n offline: agents.filter((a) => !a.isActive).length,\n },\n vibes: {\n total: totals.vibes,\n active: activeVibes.length,\n archived: vibes.filter((v) => v.status === 'ARCHIVED').length,\n },\n sessions: {\n total: totals.sessions,\n running: runningSessions.length,\n stopped: sessions.filter((s) => s.status === 'STOPPED').length,\n },\n };\n\n // Recent activity - derive from session/agent data (simplified for now)\n const recentActivity = useMemo(() => {\n const activities: Array<{\n id: string;\n type: string;\n title: string;\n description: string;\n timestamp: string;\n color: string;\n }> = [];\n\n // Add recent sessions as activity\n sessions.slice(0, 3).forEach((session) => {\n activities.push({\n id: `session-${session.id}`,\n type: 'session_started',\n title:\n session.status === 'RUNNING'\n ? tr('overviewPage.activity.sessionRunning', 'Session Running')\n : tr('overviewPage.activity.sessionStatus', 'Session {{status}}', {\n status: session.status.toLowerCase(),\n }),\n description: `${session.name}`,\n timestamp: session.createdAt,\n color: session.status === 'RUNNING' ? 'green' : 'blue',\n });\n });\n\n // Add active agents as activity\n activeAgents.slice(0, 2).forEach((agent) => {\n activities.push({\n id: `agent-${agent.id}`,\n type: 'agent_connected',\n title: tr('overviewPage.activity.agentOnline', 'Agent Online'),\n description: tr('overviewPage.activity.agentOnlineDescription', '{{name}} is active', {\n name: agent.name,\n }),\n timestamp: agent.lastHeartbeat || agent.updatedAt,\n color: 'blue',\n });\n });\n\n // Sort by timestamp descending\n return activities\n .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())\n .slice(0, 6);\n }, [sessions, activeAgents]);\n\n if (\n permissionsLoading ||\n (loading &&\n cachedAgents.length === 0 &&\n cachedVibes.length === 0 &&\n cachedSessions.length === 0)\n )\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <LoadingState message={tr('overviewPage.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n if (!hasPermission(VIBE_PERMISSIONS.VIBE_LIST))\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold text-foreground\">\n {tr('common.accessDeniedTitle', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'overviewPage.accessDeniedDescription',\n \"You don't have permission to access this page.\"\n )}\n </p>\n </div>\n </div>\n );\n\n const formatTimestamp = (timestamp: string) => {\n if (!timestamp) return tr('overviewPage.time.unknown', 'Unknown');\n const diff = Date.now() - new Date(timestamp).getTime();\n if (diff < 60000) return tr('overviewPage.time.justNow', 'Just now');\n if (diff < 3600000) {\n return tr('overviewPage.time.minutesAgo', '{{count}}m ago', {\n count: Math.floor(diff / 60000),\n });\n }\n if (diff < 86400000) {\n return tr('overviewPage.time.hoursAgo', '{{count}}h ago', {\n count: Math.floor(diff / 3600000),\n });\n }\n return tr('overviewPage.time.daysAgo', '{{count}}d ago', {\n count: Math.floor(diff / 86400000),\n });\n };\n\n const getActivityIcon = (type: string) => {\n const icons: Record<string, typeof Activity> = {\n session_started: Play,\n agent_connected: Server,\n vibe_updated: GitBranch,\n note_created: FileText,\n module_installed: Activity,\n };\n const Icon = icons[type] || Activity;\n return <Icon className=\"size-4\" />;\n };\n\n const getActivityColor = (color: string) => {\n const colors: Record<string, string> = {\n green: 'bg-status-success-bg text-status-success-text',\n blue: 'bg-status-info-bg text-status-info-text',\n purple: 'bg-accent-purple-subtle text-accent-purple',\n orange: 'bg-status-warning-bg text-status-warning-text',\n cyan: 'bg-accent-teal-subtle text-accent-teal',\n yellow: 'bg-status-warning-bg text-status-warning-text',\n pink: 'bg-accent-purple-subtle text-accent-purple',\n };\n return colors[color] || 'bg-bg-sunken text-text-secondary';\n };\n\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <PageHeader\n title={tr('overviewPage.title', 'VibeControls Overview')}\n description={tr(\n 'overviewPage.description',\n 'Monitor and manage your development environments'\n )}\n actions={\n <button\n type=\"button\"\n onClick={handleRefresh}\n disabled={loading}\n className=\"flex items-center gap-2 px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary hover:bg-bg-sunken/80 transition-colors disabled:opacity-50\"\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />\n {tr('overviewPage.refresh', 'Refresh')}\n </button>\n }\n />\n\n {/* Friendly fallback when the initial workspace load fails — keeps the\n page header / refresh button rendered above so the user can recover\n without a hard reload. */}\n {dataError && !loading && (\n <ErrorState\n title={tr('overviewPage.loadFailed.title', 'Could not load workspace data')}\n message={tr(\n 'overviewPage.loadFailed.message',\n \"We couldn't load your VibeControls overview. Please try again in a moment.\"\n )}\n onRetry={handleRefresh}\n />\n )}\n\n {/* Agent CLI Setup Banner — shown when no agents exist */}\n {!dataError && agents.length === 0 && !loading && (\n <AgentSetupBanner variant=\"full\" dismissKey=\"vibecontrols-overview-setup-banner\" />\n )}\n\n {/* Stats Grid */}\n <MotionList\n data-tour=\"overview-stats\"\n className=\"grid grid-cols-2 sm:grid-cols-4 gap-4\"\n stagger={0.06}\n >\n <MotionItem variant=\"fadeScale\">\n <StatCard\n title={tr('overviewPage.stats.targets', 'Targets')}\n value={loading ? '-' : targetsTotal}\n subtitle={\n loading || targetsTruncated\n ? ''\n : tr('overviewPage.stats.connected', '{{count}} connected', {\n count: targetsConnected,\n })\n }\n icon={<Server className=\"size-6\" />}\n variant={!loading && targetsTotal > 0 ? 'success' : 'default'}\n onClick={() => handleNavigate('/targets')}\n />\n </MotionItem>\n <MotionItem variant=\"fadeScale\">\n <StatCard\n title={tr('overviewPage.stats.agents', 'Agents')}\n value={loading ? '-' : stats.agents.total}\n subtitle={\n loading\n ? ''\n : tr('overviewPage.stats.active', '{{count}} active', {\n count: stats.agents.active,\n })\n }\n icon={<Bot className=\"size-6\" />}\n variant={!loading && stats.agents.active > 0 ? 'success' : 'default'}\n onClick={() => handleNavigate('/agents')}\n />\n </MotionItem>\n <MotionItem variant=\"fadeScale\">\n <StatCard\n title={tr('overviewPage.stats.vibes', 'Vibes')}\n value={loading ? '-' : stats.vibes.total}\n subtitle={\n loading\n ? ''\n : tr('overviewPage.stats.active', '{{count}} active', {\n count: stats.vibes.active,\n })\n }\n icon={<Zap className=\"size-6\" />}\n onClick={() => handleNavigate('/vibes')}\n />\n </MotionItem>\n <MotionItem variant=\"fadeScale\">\n <StatCard\n title={tr('overviewPage.stats.sessions', 'Sessions')}\n value={loading ? '-' : stats.sessions.total}\n subtitle={\n loading\n ? ''\n : tr('overviewPage.stats.running', '{{count}} running', {\n count: stats.sessions.running,\n })\n }\n icon={<Terminal className=\"size-6\" />}\n variant={!loading && stats.sessions.running > 0 ? 'success' : 'default'}\n onClick={() => handleNavigate('/sessions')}\n />\n </MotionItem>\n </MotionList>\n\n {/* Main Content Grid */}\n <MotionList className=\"grid grid-cols-1 gap-6 xl:grid-cols-3\" stagger={0.08}>\n {/* Active Agents */}\n <MotionItem className=\"xl:col-span-2 h-full\">\n <Card className=\"flex h-full flex-col\">\n <div className=\"flex items-center justify-between mb-4\">\n <h2 className=\"text-lg font-semibold text-text-primary flex items-center gap-2\">\n <Bot className=\"size-5\" />\n {tr('overviewPage.sections.activeAgents', 'Active Agents')}\n </h2>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/agents')}\n className=\"text-sm text-text-link hover:underline\"\n aria-label={tr('overviewPage.sections.viewAllAgents', 'View all agents')}\n >\n {tr('overviewPage.sections.viewAll', 'View all')}\n </button>\n </div>\n {activeAgents.length > 0 ? (\n <MotionList\n className=\"grid grid-cols-1 sm:grid-cols-2 gap-3 flex-1 content-start\"\n stagger={0.04}\n >\n {activeAgents.slice(0, 4).map((agent) => (\n <MotionItem key={agent.id}>\n <AgentCard agent={agent} onSelect={(a) => handleNavigate(`/agents/${a.id}`)} />\n </MotionItem>\n ))}\n </MotionList>\n ) : (\n <div className=\"flex flex-1 flex-col items-center justify-center text-center py-6 text-text-secondary\">\n <Bot className=\"size-12 mx-auto mb-2 opacity-50\" />\n <p>{tr('overviewPage.empty.noActiveAgents', 'No active agents')}</p>\n <p className=\"text-xs text-text-muted mt-1 mb-3\">\n {tr('overviewPage.empty.installCliHint', 'Install the agent CLI to get started')}\n </p>\n <div className=\"flex flex-wrap items-center justify-center gap-2\">\n <button\n type=\"button\"\n onClick={() => handleNavigate('/getting-started')}\n className=\"text-sm font-medium text-text-link hover:underline\"\n >\n {tr('overviewPage.empty.setupGuide', 'Setup guide')}\n </button>\n <span className=\"text-text-muted\">&middot;</span>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/agents')}\n className=\"text-sm text-text-link hover:underline\"\n >\n {tr('overviewPage.empty.addAgent', 'Add agent')}\n </button>\n </div>\n </div>\n )}\n </Card>\n </MotionItem>\n\n {/* Recent Activity */}\n <MotionItem className=\"h-full\">\n <Card className=\"flex h-full flex-col\">\n <div className=\"flex items-center justify-between mb-4\">\n <h2 className=\"text-lg font-semibold text-text-primary flex items-center gap-2\">\n <Activity className=\"size-5\" />\n {tr('overviewPage.sections.recentActivity', 'Recent Activity')}\n </h2>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/audit')}\n className=\"text-sm text-text-link hover:underline\"\n aria-label={tr('overviewPage.sections.viewAllActivity', 'View all activity')}\n >\n View all\n </button>\n </div>\n <MotionList className=\"space-y-3 flex-1\" stagger={0.04}>\n {recentActivity.length > 0 ? (\n recentActivity.map((activity) => (\n <MotionItem key={activity.id} variant=\"fadeRight\">\n <div className=\"flex items-start gap-3\">\n <div\n className={`flex-shrink-0 size-8 rounded-full flex items-center justify-center ${getActivityColor(activity.color)}`}\n >\n {getActivityIcon(activity.type)}\n </div>\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">\n {activity.title}\n </p>\n <p className=\"text-xs text-text-secondary truncate\">\n {activity.description}\n </p>\n </div>\n <span className=\"text-xs text-text-secondary flex-shrink-0\">\n {formatTimestamp(activity.timestamp)}\n </span>\n </div>\n </MotionItem>\n ))\n ) : (\n <div className=\"flex h-full flex-col items-center justify-center text-center py-4 text-text-secondary text-sm\">\n <p>{tr('overviewPage.empty.noRecentActivity', 'No recent activity')}</p>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/audit')}\n className=\"mt-2 text-sm text-text-link hover:underline\"\n >\n {tr('overviewPage.empty.viewAuditLog', 'View audit log')}\n </button>\n </div>\n )}\n </MotionList>\n </Card>\n </MotionItem>\n </MotionList>\n\n {/* Running Sessions */}\n <MotionItem>\n <Card>\n <div className=\"flex items-center justify-between mb-4\">\n <h2 className=\"text-lg font-semibold text-text-primary flex items-center gap-2\">\n <Terminal className=\"size-5\" />\n {tr('overviewPage.sections.runningSessions', 'Running Sessions')}\n </h2>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/sessions')}\n className=\"text-sm text-text-link hover:underline\"\n >\n {tr('overviewPage.sections.viewAll', 'View all')}\n </button>\n </div>\n {runningSessions.length > 0 ? (\n <MotionList\n className=\"grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-3\"\n stagger={0.04}\n >\n {runningSessions.slice(0, 6).map((session) => (\n <MotionItem key={session.id}>\n <SessionCard\n session={session}\n onOpen={(s) => handleNavigate(`/sessions/${s.id}`)}\n />\n </MotionItem>\n ))}\n </MotionList>\n ) : (\n <div className=\"text-center py-8 text-text-secondary\">\n <Terminal className=\"size-12 mx-auto mb-2 opacity-50\" />\n <p>{tr('overviewPage.empty.noRunningSessions', 'No running sessions')}</p>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/sessions')}\n className=\"mt-2 text-sm text-text-link hover:underline\"\n >\n {tr('overviewPage.empty.startSession', 'Start a session')}\n </button>\n </div>\n )}\n </Card>\n </MotionItem>\n\n {/* Active Vibes */}\n <MotionItem>\n <Card>\n <div className=\"flex items-center justify-between mb-4\">\n <h2 className=\"text-lg font-semibold text-text-primary flex items-center gap-2\">\n <Zap className=\"size-5\" />\n {tr('overviewPage.sections.activeVibes', 'Active Vibes')}\n </h2>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/vibes')}\n className=\"text-sm text-text-link hover:underline\"\n >\n {tr('overviewPage.sections.viewAll', 'View all')}\n </button>\n </div>\n {activeVibes.length > 0 ? (\n <MotionList\n className=\"grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-3\"\n stagger={0.04}\n >\n {activeVibes.slice(0, 6).map((vibe) => (\n <MotionItem key={vibe.id}>\n <VibeCard vibe={vibe} onOpen={(v) => handleNavigate(`/vibes/${v.id}`)} />\n </MotionItem>\n ))}\n </MotionList>\n ) : (\n <div className=\"text-center py-8 text-text-secondary\">\n <Zap className=\"size-12 mx-auto mb-2 opacity-50\" />\n <p>{tr('overviewPage.empty.noActiveVibes', 'No active vibes')}</p>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/vibes')}\n className=\"mt-2 text-sm text-text-link hover:underline\"\n >\n {tr('overviewPage.empty.addVibe', 'Add a vibe')}\n </button>\n </div>\n )}\n </Card>\n </MotionItem>\n </MotionPage>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"OverviewPage.js","names":[],"sources":["../../src/pages/OverviewPage.tsx"],"sourcesContent":["import { useCallback, useMemo } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport {\n Bot,\n Zap,\n Terminal,\n FileText,\n Activity,\n Server,\n Play,\n GitBranch,\n RefreshCw,\n} from 'lucide-react';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { AgentCard } from '@/components/agents';\nimport { SessionCard } from '@/components/sessions';\nimport { VibeCard } from '@/components/vibes';\nimport {\n PageHeader,\n StatCard,\n Card,\n AgentSetupBanner,\n ErrorState,\n LoadingState,\n} from '@/components/shared';\nimport { useVibeNavigate, useVibeControls } from '@/providers/VibeControlsProvider';\nimport { useCachedInitialData } from '@/hooks/useCachedInitialData';\nimport { useListTargetsQuery } from '@/generated/wspace-operations';\nimport type { Vibe } from '@/components/vibes/types';\nimport type { Session } from '@/components/sessions/types';\n\n/**\n * Overview page showing summary dashboard with stats, active resources, and recent activity\n *\n * This page now uses cached initial data from Apollo instead of independent queries,\n * reducing GraphQL calls from 5 to 0 for the initial render.\n * The refresh button triggers a refetch of the initial data.\n */\nexport function OverviewPage() {\n const handleNavigate = useVibeNavigate();\n const { t } = useI18n();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n\n const tr = (key: string, fallback: string, params?: Record<string, string | number>): string => {\n const translated = t(key, params);\n const template = translated === key ? fallback : translated;\n\n if (!params) return template;\n\n return Object.entries(params).reduce(\n (acc, [paramKey, paramValue]) => acc.replace(`{{${paramKey}}}`, String(paramValue)),\n template\n );\n };\n\n // Use cached initial data - reads from Apollo cache, no network request\n // Loading state reflects whether initial data has been loaded\n const {\n agents: cachedAgents,\n vibes: cachedVibes,\n sessions: cachedSessions,\n loading,\n error: dataError,\n totals,\n refetch,\n } = useCachedInitialData();\n\n // Targets is intentionally NOT part of VibeControlsInitialData (it caused a 25s\n // timeout there), so fetch the count with a lightweight standalone query — the\n // same pattern HealthPage uses.\n const { workspaceId } = useVibeControls();\n const targetsCountQuery = useListTargetsQuery({\n variables: { workspaceId: workspaceId || '', limit: 50 },\n skip: !workspaceId,\n fetchPolicy: 'cache-and-network',\n });\n\n const refetchTargets = targetsCountQuery.refetch;\n // The header Refresh button must refetch BOTH the cached initial data and the\n // standalone targets query — otherwise the Targets stat card stays stale after\n // a manual refresh.\n const handleRefresh = useCallback(() => {\n void refetch();\n void refetchTargets();\n }, [refetch, refetchTargets]);\n\n const targetItems: Array<{ status: string }> = targetsCountQuery.data?.targets.items ?? [];\n const targetsTotal = targetsCountQuery.data?.targets.total ?? 0;\n // `targetsConnected` is derived from `targetItems`, which is capped by the\n // query `limit`. When the workspace has more targets than the page size the\n // connected count would only reflect the first page and contradict the\n // server-side `targetsTotal`. Track truncation so we only surface the\n // connected detail when it is accurate for the full set.\n const targetsTruncated = targetsTotal > targetItems.length;\n const targetsConnected = targetItems.filter(\n (t) => t.status === 'connected' || t.status === 'active'\n ).length;\n\n if (dataError) {\n console.error('[overview] failed to load initial workspace data', dataError);\n }\n\n // Map cached data to component types\n // Using type assertion since we know the structure from the GraphQL query\n const agents = useMemo(() => {\n return cachedAgents.map((agent) => ({\n id: agent.id,\n workspaceId: '',\n name: agent.name,\n hostname: agent.hostname,\n platform: agent.platform,\n architecture: agent.architecture || '',\n version: agent.version || '',\n isActive: agent.isActive,\n status: agent.isActive ? ('ACTIVE' as const) : ('OFFLINE' as const),\n lastHeartbeat: agent.lastHeartbeat || null,\n tunnelUrl: agent.tunnelUrl ?? null,\n tunnelStatus: agent.tunnelStatus ?? null,\n agentApiKey: null,\n aiContext: null,\n configuration: {},\n capabilities: [],\n metadata: {},\n createdAt: agent.createdAt || '',\n updatedAt: '',\n }));\n }, [cachedAgents]);\n\n const vibes = useMemo(() => {\n return cachedVibes.map((vibe) => ({\n id: vibe.id,\n workspaceId: '',\n name: vibe.name,\n description: '',\n type: (vibe.type || 'PROJECT') as Vibe['type'],\n status: vibe.status as Vibe['status'],\n path: '',\n parentId: null,\n gitBranch: null,\n gitRemote: null,\n gitStatus: null,\n environmentVariables: {},\n tags: [],\n metadata: {},\n createdAt: '',\n updatedAt: '',\n archivedAt: null,\n // Multi-agent: surface attached agents so Overview cards can render a\n // count badge (\"+N agents\") or single name. Empty when none.\n agents: vibe.agents ?? [],\n }));\n }, [cachedVibes]);\n\n const sessions: Session[] = useMemo(() => {\n return cachedSessions.map((session) => ({\n id: session.id,\n workspaceId: '',\n agentId: session.agentId ?? null,\n vibeId: session.vibeId ?? null,\n name: session.name,\n type: (session.type || 'TERMINAL') as Session['type'],\n status: session.status as Session['status'],\n command: null,\n workingDirectory: null,\n environmentVariables: {},\n autoStart: false,\n pid: null,\n exitCode: null,\n lastOutput: null,\n metadata: {},\n createdAt: session.createdAt || '',\n updatedAt: '',\n agent: undefined,\n vibe: undefined,\n }));\n }, [cachedSessions]);\n\n // Calculate stats from cached totals\n const activeAgents = agents.filter((a) => a.isActive);\n const runningSessions = sessions.filter((s) => s.status === 'RUNNING');\n const activeVibes = vibes.filter((v) => v.status === 'ACTIVE');\n\n const stats = {\n agents: {\n total: totals.agents,\n active: activeAgents.length,\n offline: agents.filter((a) => !a.isActive).length,\n },\n vibes: {\n total: totals.vibes,\n active: activeVibes.length,\n archived: vibes.filter((v) => v.status === 'ARCHIVED').length,\n },\n sessions: {\n total: totals.sessions,\n running: runningSessions.length,\n stopped: sessions.filter((s) => s.status === 'STOPPED').length,\n },\n };\n\n // Recent activity - derive from session/agent data (simplified for now)\n const recentActivity = useMemo(() => {\n const activities: Array<{\n id: string;\n type: string;\n title: string;\n description: string;\n timestamp: string;\n color: string;\n }> = [];\n\n // Add recent sessions as activity\n sessions.slice(0, 3).forEach((session) => {\n activities.push({\n id: `session-${session.id}`,\n type: 'session_started',\n title:\n session.status === 'RUNNING'\n ? tr('overviewPage.activity.sessionRunning', 'Session Running')\n : tr('overviewPage.activity.sessionStatus', 'Session {{status}}', {\n status: session.status.toLowerCase(),\n }),\n description: `${session.name}`,\n timestamp: session.createdAt,\n color: session.status === 'RUNNING' ? 'green' : 'blue',\n });\n });\n\n // Add active agents as activity\n activeAgents.slice(0, 2).forEach((agent) => {\n activities.push({\n id: `agent-${agent.id}`,\n type: 'agent_connected',\n title: tr('overviewPage.activity.agentOnline', 'Agent Online'),\n description: tr('overviewPage.activity.agentOnlineDescription', '{{name}} is active', {\n name: agent.name,\n }),\n timestamp: agent.lastHeartbeat || agent.updatedAt,\n color: 'blue',\n });\n });\n\n // Sort by timestamp descending\n return activities\n .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())\n .slice(0, 6);\n }, [sessions, activeAgents]);\n\n if (\n permissionsLoading ||\n (loading &&\n cachedAgents.length === 0 &&\n cachedVibes.length === 0 &&\n cachedSessions.length === 0)\n )\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <LoadingState message={tr('overviewPage.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n if (!hasPermission(VIBE_PERMISSIONS.VIBE_LIST))\n return (\n <div className=\"flex items-center justify-center min-h-[400px] px-4\">\n <div className=\"text-center space-y-2 max-w-sm\">\n <h2 className=\"text-lg font-semibold tracking-tight text-text-primary\">\n {tr('common.accessDeniedTitle', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'overviewPage.accessDeniedDescription',\n \"You don't have permission to access this page.\"\n )}\n </p>\n </div>\n </div>\n );\n\n const formatTimestamp = (timestamp: string) => {\n if (!timestamp) return tr('overviewPage.time.unknown', 'Unknown');\n const diff = Date.now() - new Date(timestamp).getTime();\n if (diff < 60000) return tr('overviewPage.time.justNow', 'Just now');\n if (diff < 3600000) {\n return tr('overviewPage.time.minutesAgo', '{{count}}m ago', {\n count: Math.floor(diff / 60000),\n });\n }\n if (diff < 86400000) {\n return tr('overviewPage.time.hoursAgo', '{{count}}h ago', {\n count: Math.floor(diff / 3600000),\n });\n }\n return tr('overviewPage.time.daysAgo', '{{count}}d ago', {\n count: Math.floor(diff / 86400000),\n });\n };\n\n const getActivityIcon = (type: string) => {\n const icons: Record<string, typeof Activity> = {\n session_started: Play,\n agent_connected: Server,\n vibe_updated: GitBranch,\n note_created: FileText,\n module_installed: Activity,\n };\n const Icon = icons[type] || Activity;\n return <Icon className=\"size-4\" />;\n };\n\n const getActivityColor = (color: string) => {\n const colors: Record<string, string> = {\n green: 'bg-status-success-bg-subtle text-status-success-text',\n blue: 'bg-[var(--color-accent-soft)] text-text-primary',\n purple: 'bg-accent-purple-subtle text-text-primary',\n orange: 'bg-status-warning-bg-subtle text-status-warning-text',\n cyan: 'bg-accent-teal-subtle text-accent-teal',\n yellow: 'bg-status-warning-bg-subtle text-status-warning-text',\n pink: 'bg-accent-purple-subtle text-text-primary',\n };\n return colors[color] || 'bg-bg-sunken text-text-secondary';\n };\n\n return (\n <MotionPage className=\"space-y-6 px-6 lg:px-8 py-6 bg-bg-canvas min-h-screen\">\n <PageHeader\n title={tr('overviewPage.title', 'VibeControls Overview')}\n description={tr(\n 'overviewPage.description',\n 'Monitor and manage your development environments'\n )}\n actions={\n <button\n type=\"button\"\n onClick={handleRefresh}\n disabled={loading}\n className=\"inline-flex items-center gap-2 px-3 py-2 text-sm bg-bg-surface border border-border-subtle rounded-button text-text-primary hover:bg-bg-sunken hover:border-border-strong transition-all duration-200 disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} aria-hidden=\"true\" />\n {tr('overviewPage.refresh', 'Refresh')}\n </button>\n }\n />\n\n {/* Friendly fallback when the initial workspace load fails — keeps the\n page header / refresh button rendered above so the user can recover\n without a hard reload. */}\n {dataError && !loading && (\n <ErrorState\n title={tr('overviewPage.loadFailed.title', 'Could not load workspace data')}\n message={tr(\n 'overviewPage.loadFailed.message',\n \"We couldn't load your VibeControls overview. Please try again in a moment.\"\n )}\n onRetry={handleRefresh}\n />\n )}\n\n {/* Agent CLI Setup Banner — shown when no agents exist */}\n {!dataError && agents.length === 0 && !loading && (\n <AgentSetupBanner variant=\"full\" dismissKey=\"vibecontrols-overview-setup-banner\" />\n )}\n\n {/* Stats Grid */}\n <MotionList\n data-tour=\"overview-stats\"\n className=\"grid grid-cols-2 sm:grid-cols-4 gap-4\"\n stagger={0.06}\n >\n <MotionItem variant=\"fadeScale\">\n <StatCard\n title={tr('overviewPage.stats.targets', 'Targets')}\n value={loading ? '-' : targetsTotal}\n subtitle={\n loading || targetsTruncated\n ? ''\n : tr('overviewPage.stats.connected', '{{count}} connected', {\n count: targetsConnected,\n })\n }\n icon={<Server className=\"size-6\" />}\n variant={!loading && targetsTotal > 0 ? 'success' : 'default'}\n onClick={() => handleNavigate('/targets')}\n />\n </MotionItem>\n <MotionItem variant=\"fadeScale\">\n <StatCard\n title={tr('overviewPage.stats.agents', 'Agents')}\n value={loading ? '-' : stats.agents.total}\n subtitle={\n loading\n ? ''\n : tr('overviewPage.stats.active', '{{count}} active', {\n count: stats.agents.active,\n })\n }\n icon={<Bot className=\"size-6\" />}\n variant={!loading && stats.agents.active > 0 ? 'success' : 'default'}\n onClick={() => handleNavigate('/agents')}\n />\n </MotionItem>\n <MotionItem variant=\"fadeScale\">\n <StatCard\n title={tr('overviewPage.stats.vibes', 'Vibes')}\n value={loading ? '-' : stats.vibes.total}\n subtitle={\n loading\n ? ''\n : tr('overviewPage.stats.active', '{{count}} active', {\n count: stats.vibes.active,\n })\n }\n icon={<Zap className=\"size-6\" />}\n onClick={() => handleNavigate('/vibes')}\n />\n </MotionItem>\n <MotionItem variant=\"fadeScale\">\n <StatCard\n title={tr('overviewPage.stats.sessions', 'Sessions')}\n value={loading ? '-' : stats.sessions.total}\n subtitle={\n loading\n ? ''\n : tr('overviewPage.stats.running', '{{count}} running', {\n count: stats.sessions.running,\n })\n }\n icon={<Terminal className=\"size-6\" />}\n variant={!loading && stats.sessions.running > 0 ? 'success' : 'default'}\n onClick={() => handleNavigate('/sessions')}\n />\n </MotionItem>\n </MotionList>\n\n {/* Main Content Grid */}\n <MotionList className=\"grid grid-cols-1 gap-6 xl:grid-cols-3\" stagger={0.08}>\n {/* Active Agents */}\n <MotionItem className=\"xl:col-span-2 h-full\">\n <Card className=\"flex h-full flex-col\">\n <div className=\"flex items-center justify-between mb-5\">\n <h2 className=\"text-lg font-semibold tracking-tight text-text-primary flex items-center gap-2.5\">\n <span className=\"inline-flex size-8 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <Bot className=\"size-5\" />\n </span>\n {tr('overviewPage.sections.activeAgents', 'Active Agents')}\n </h2>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/agents')}\n className=\"text-sm font-medium text-text-link hover:underline\"\n aria-label={tr('overviewPage.sections.viewAllAgents', 'View all agents')}\n >\n {tr('overviewPage.sections.viewAll', 'View all')}\n </button>\n </div>\n {activeAgents.length > 0 ? (\n <MotionList\n className=\"grid grid-cols-1 sm:grid-cols-2 gap-4 flex-1 content-start\"\n stagger={0.04}\n >\n {activeAgents.slice(0, 4).map((agent) => (\n <MotionItem key={agent.id}>\n <AgentCard agent={agent} onSelect={(a) => handleNavigate(`/agents/${a.id}`)} />\n </MotionItem>\n ))}\n </MotionList>\n ) : (\n <div className=\"flex flex-1 flex-col items-center justify-center text-center py-8 text-text-secondary\">\n <span className=\"inline-flex size-12 items-center justify-center rounded-full bg-[var(--color-accent-soft)] text-text-primary mb-3\">\n <Bot className=\"size-6\" />\n </span>\n <p className=\"text-base font-semibold text-text-primary\">\n {tr('overviewPage.empty.noActiveAgents', 'No active agents')}\n </p>\n <p className=\"text-xs text-text-muted mt-1 mb-4\">\n {tr('overviewPage.empty.installCliHint', 'Install the agent CLI to get started')}\n </p>\n <div className=\"flex flex-wrap items-center justify-center gap-2\">\n <button\n type=\"button\"\n onClick={() => handleNavigate('/getting-started')}\n className=\"text-sm font-medium text-text-link hover:underline\"\n >\n {tr('overviewPage.empty.setupGuide', 'Setup guide')}\n </button>\n <span className=\"text-text-muted\">&middot;</span>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/agents')}\n className=\"text-sm text-text-link hover:underline\"\n >\n {tr('overviewPage.empty.addAgent', 'Add agent')}\n </button>\n </div>\n </div>\n )}\n </Card>\n </MotionItem>\n\n {/* Recent Activity */}\n <MotionItem className=\"h-full\">\n <Card className=\"flex h-full flex-col\">\n <div className=\"flex items-center justify-between mb-5\">\n <h2 className=\"text-lg font-semibold tracking-tight text-text-primary flex items-center gap-2.5\">\n <span className=\"inline-flex size-8 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <Activity className=\"size-5\" />\n </span>\n {tr('overviewPage.sections.recentActivity', 'Recent Activity')}\n </h2>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/audit')}\n className=\"text-sm font-medium text-text-link hover:underline\"\n aria-label={tr('overviewPage.sections.viewAllActivity', 'View all activity')}\n >\n View all\n </button>\n </div>\n <MotionList className=\"space-y-3 flex-1\" stagger={0.04}>\n {recentActivity.length > 0 ? (\n recentActivity.map((activity) => (\n <MotionItem key={activity.id} variant=\"fadeRight\">\n <div className=\"flex items-start gap-3\">\n <div\n className={`flex-shrink-0 size-8 rounded-full flex items-center justify-center ${getActivityColor(activity.color)}`}\n >\n {getActivityIcon(activity.type)}\n </div>\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">\n {activity.title}\n </p>\n <p className=\"text-xs text-text-secondary truncate\">\n {activity.description}\n </p>\n </div>\n <span className=\"text-xs text-text-secondary flex-shrink-0\">\n {formatTimestamp(activity.timestamp)}\n </span>\n </div>\n </MotionItem>\n ))\n ) : (\n <div className=\"flex h-full flex-col items-center justify-center text-center py-4 text-text-secondary text-sm\">\n <p>{tr('overviewPage.empty.noRecentActivity', 'No recent activity')}</p>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/audit')}\n className=\"mt-2 text-sm text-text-link hover:underline\"\n >\n {tr('overviewPage.empty.viewAuditLog', 'View audit log')}\n </button>\n </div>\n )}\n </MotionList>\n </Card>\n </MotionItem>\n </MotionList>\n\n {/* Running Sessions */}\n <MotionItem>\n <Card>\n <div className=\"flex items-center justify-between mb-5\">\n <h2 className=\"text-lg font-semibold tracking-tight text-text-primary flex items-center gap-2.5\">\n <span className=\"inline-flex size-8 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <Terminal className=\"size-5\" />\n </span>\n {tr('overviewPage.sections.runningSessions', 'Running Sessions')}\n </h2>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/sessions')}\n className=\"text-sm font-medium text-text-link hover:underline\"\n >\n {tr('overviewPage.sections.viewAll', 'View all')}\n </button>\n </div>\n {runningSessions.length > 0 ? (\n <MotionList\n className=\"grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-4\"\n stagger={0.04}\n >\n {runningSessions.slice(0, 6).map((session) => (\n <MotionItem key={session.id}>\n <SessionCard\n session={session}\n onOpen={(s) => handleNavigate(`/sessions/${s.id}`)}\n />\n </MotionItem>\n ))}\n </MotionList>\n ) : (\n <div className=\"flex flex-col items-center text-center py-10 text-text-secondary\">\n <span className=\"inline-flex size-12 items-center justify-center rounded-full bg-[var(--color-accent-soft)] text-text-primary mb-3\">\n <Terminal className=\"size-6\" />\n </span>\n <p className=\"text-base font-semibold text-text-primary\">\n {tr('overviewPage.empty.noRunningSessions', 'No running sessions')}\n </p>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/sessions')}\n className=\"mt-3 text-sm font-medium text-text-link hover:underline\"\n >\n {tr('overviewPage.empty.startSession', 'Start a session')}\n </button>\n </div>\n )}\n </Card>\n </MotionItem>\n\n {/* Active Vibes */}\n <MotionItem>\n <Card>\n <div className=\"flex items-center justify-between mb-5\">\n <h2 className=\"text-lg font-semibold tracking-tight text-text-primary flex items-center gap-2.5\">\n <span className=\"inline-flex size-8 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <Zap className=\"size-5\" />\n </span>\n {tr('overviewPage.sections.activeVibes', 'Active Vibes')}\n </h2>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/vibes')}\n className=\"text-sm font-medium text-text-link hover:underline\"\n >\n {tr('overviewPage.sections.viewAll', 'View all')}\n </button>\n </div>\n {activeVibes.length > 0 ? (\n <MotionList\n className=\"grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-4\"\n stagger={0.04}\n >\n {activeVibes.slice(0, 6).map((vibe) => (\n <MotionItem key={vibe.id}>\n <VibeCard vibe={vibe} onOpen={(v) => handleNavigate(`/vibes/${v.id}`)} />\n </MotionItem>\n ))}\n </MotionList>\n ) : (\n <div className=\"flex flex-col items-center text-center py-10 text-text-secondary\">\n <span className=\"inline-flex size-12 items-center justify-center rounded-full bg-[var(--color-accent-soft)] text-text-primary mb-3\">\n <Zap className=\"size-6\" />\n </span>\n <p className=\"text-base font-semibold text-text-primary\">\n {tr('overviewPage.empty.noActiveVibes', 'No active vibes')}\n </p>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/vibes')}\n className=\"mt-3 text-sm font-medium text-text-link hover:underline\"\n >\n {tr('overviewPage.empty.addVibe', 'Add a vibe')}\n </button>\n </div>\n )}\n </Card>\n </MotionItem>\n </MotionPage>\n );\n}\n"],"mappings":"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"}
@@ -88,7 +88,7 @@ function v(e) {
88
88
  }
89
89
  function y(e) {
90
90
  switch (e) {
91
- case "Meta": return "bg-accent-purple-subtle text-accent-purple border-accent-purple/30";
91
+ case "Meta": return "bg-accent-purple-subtle text-text-primary border-accent-purple/30";
92
92
  case "Provider": return "bg-status-info-bg text-status-info-text border-status-info-border";
93
93
  default: return "bg-bg-sunken text-text-secondary border-border-default";
94
94
  }
@@ -205,14 +205,17 @@ function S() {
205
205
  children: /* @__PURE__ */ p(g, {
206
206
  className: "flex flex-col h-full",
207
207
  children: [/* @__PURE__ */ p("div", {
208
- className: "sticky top-0 z-10 border-b border-border-default bg-bg-surface px-4 sm:px-6 pt-4 pb-3 space-y-3",
208
+ className: "sticky top-0 z-10 border-b border-border-subtle bg-bg-surface px-6 lg:px-8 pt-5 pb-4 space-y-3",
209
209
  children: [
210
210
  /* @__PURE__ */ p("div", {
211
211
  className: "flex items-start gap-3",
212
- children: [/* @__PURE__ */ f(l, { className: "size-6 text-text-primary mt-0.5 flex-shrink-0" }), /* @__PURE__ */ p("div", {
212
+ children: [/* @__PURE__ */ f("div", {
213
+ className: "flex size-9 flex-shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)]",
214
+ children: /* @__PURE__ */ f(l, { className: "size-5 text-action-primary-bg" })
215
+ }), /* @__PURE__ */ p("div", {
213
216
  className: "min-w-0 flex-1",
214
217
  children: [/* @__PURE__ */ f("h1", {
215
- className: "text-xl font-bold text-text-primary",
218
+ className: "text-2xl font-semibold tracking-tight text-text-primary",
216
219
  children: n("vibecontrols.plugins.title", "Plugins")
217
220
  }), /* @__PURE__ */ f("p", {
218
221
  className: "text-sm text-text-secondary mt-0.5",