@burdenoff/microfe-vibecontrols 2026.623.1 → 2026.623.2

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 (293) 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/ExecutionHistory.js +1 -1
  4. package/dist/components/actions/ExecutionHistory.js.map +1 -1
  5. package/dist/components/agent-manager/AgentManagerAdminBar.js +1 -1
  6. package/dist/components/agent-manager/AgentManagerAdminBar.js.map +1 -1
  7. package/dist/components/agent-manager/AgentManagerChatPane.js +2 -2
  8. package/dist/components/agent-manager/AgentManagerChatPane.js.map +1 -1
  9. package/dist/components/agent-manager/AgentManagerInputArea.js +4 -4
  10. package/dist/components/agent-manager/AgentManagerInputArea.js.map +1 -1
  11. package/dist/components/agent-manager/AgentManagerLayout.js +1 -1
  12. package/dist/components/agent-manager/AgentManagerLayout.js.map +1 -1
  13. package/dist/components/agent-manager/AgentManagerMessageItem.js +1 -1
  14. package/dist/components/agent-manager/AgentManagerMessageItem.js.map +1 -1
  15. package/dist/components/agent-manager/AgentManagerSidebar.js +5 -5
  16. package/dist/components/agent-manager/AgentManagerSidebar.js.map +1 -1
  17. package/dist/components/agent-manager/AgentManagerTab.js +1 -1
  18. package/dist/components/agent-manager/AgentManagerTab.js.map +1 -1
  19. package/dist/components/agent-manager/AgentManagerTabBar.js +1 -1
  20. package/dist/components/agent-manager/AgentManagerTabBar.js.map +1 -1
  21. package/dist/components/agent-manager/BridleConfigPanel.js +23 -23
  22. package/dist/components/agent-manager/BridleConfigPanel.js.map +1 -1
  23. package/dist/components/agent-manager/PluginInstallModal.js +1 -1
  24. package/dist/components/agent-manager/PluginInstallModal.js.map +1 -1
  25. package/dist/components/agent-manager/ProfilesPanel.js +5 -5
  26. package/dist/components/agent-manager/ProfilesPanel.js.map +1 -1
  27. package/dist/components/agent-manager/controls/AutonomySelector.js +1 -1
  28. package/dist/components/agent-manager/controls/AutonomySelector.js.map +1 -1
  29. package/dist/components/agent-manager/controls/SdkSelector.js +1 -1
  30. package/dist/components/agent-manager/controls/SdkSelector.js.map +1 -1
  31. package/dist/components/agent-manager/controls/VoiceToggle.js +1 -1
  32. package/dist/components/agent-manager/controls/VoiceToggle.js.map +1 -1
  33. package/dist/components/agent-manager/detail/SessionConfigPanel.js +6 -6
  34. package/dist/components/agent-manager/detail/SessionConfigPanel.js.map +1 -1
  35. package/dist/components/agent-manager/detail/SessionLogsPanel.js +5 -5
  36. package/dist/components/agent-manager/detail/SessionLogsPanel.js.map +1 -1
  37. package/dist/components/agent-manager/detail/SessionToolsPanel.js +1 -1
  38. package/dist/components/agent-manager/detail/SessionToolsPanel.js.map +1 -1
  39. package/dist/components/agent-manager/dialogs/NewSessionDialog.js +2 -2
  40. package/dist/components/agent-manager/dialogs/NewSessionDialog.js.map +1 -1
  41. package/dist/components/agent-manager/dialogs/RenameSessionDialog.js +1 -1
  42. package/dist/components/agent-manager/dialogs/RenameSessionDialog.js.map +1 -1
  43. package/dist/components/agents/AgentCard.js +14 -11
  44. package/dist/components/agents/AgentCard.js.map +1 -1
  45. package/dist/components/ai/AIContextsTab.js +1 -1
  46. package/dist/components/ai/AIContextsTab.js.map +1 -1
  47. package/dist/components/ai/AIOverviewTab.js +4 -4
  48. package/dist/components/ai/AIOverviewTab.js.map +1 -1
  49. package/dist/components/ai/AIPlanPanel.js +4 -4
  50. package/dist/components/ai/AIPlanPanel.js.map +1 -1
  51. package/dist/components/ai/AIPlaygroundTab.js +1 -1
  52. package/dist/components/ai/AIPlaygroundTab.js.map +1 -1
  53. package/dist/components/ai/AIPromptsTab.js +2 -2
  54. package/dist/components/ai/AIPromptsTab.js.map +1 -1
  55. package/dist/components/ai/AIProviderCredentialsCard.js +4 -4
  56. package/dist/components/ai/AIProviderCredentialsCard.js.map +1 -1
  57. package/dist/components/ai/AIQueueTab.js +4 -4
  58. package/dist/components/ai/AIQueueTab.js.map +1 -1
  59. package/dist/components/ai/AISessionsTab.js +4 -4
  60. package/dist/components/ai/AISessionsTab.js.map +1 -1
  61. package/dist/components/ai/AITasksTab.js +1 -1
  62. package/dist/components/ai/AITasksTab.js.map +1 -1
  63. package/dist/components/ai/ClaudeCredentialsCard.js +3 -3
  64. package/dist/components/ai/ClaudeCredentialsCard.js.map +1 -1
  65. package/dist/components/ai/PluginHarnessPicker.js +3 -3
  66. package/dist/components/ai/PluginHarnessPicker.js.map +1 -1
  67. package/dist/components/analytics/AnalyticsBreakdownChart.js +8 -8
  68. package/dist/components/analytics/AnalyticsBreakdownChart.js.map +1 -1
  69. package/dist/components/analytics/AnalyticsTimeSeriesChart.js +5 -5
  70. package/dist/components/analytics/AnalyticsTimeSeriesChart.js.map +1 -1
  71. package/dist/components/audit/AuditDeleteDialog.js +2 -2
  72. package/dist/components/audit/AuditDeleteDialog.js.map +1 -1
  73. package/dist/components/audit/AuditExportButton.js +5 -5
  74. package/dist/components/audit/AuditExportButton.js.map +1 -1
  75. package/dist/components/audit/AuditLogDetail.js +9 -9
  76. package/dist/components/audit/AuditLogDetail.js.map +1 -1
  77. package/dist/components/audit/AuditLogFilters.js +1 -1
  78. package/dist/components/audit/AuditLogFilters.js.map +1 -1
  79. package/dist/components/audit/AuditLogStats.js +1 -1
  80. package/dist/components/audit/AuditLogStats.js.map +1 -1
  81. package/dist/components/audit/AuditLogTable.js +28 -28
  82. package/dist/components/audit/AuditLogTable.js.map +1 -1
  83. package/dist/components/catalog/CatalogCard.js +3 -3
  84. package/dist/components/catalog/CatalogCard.js.map +1 -1
  85. package/dist/components/catalog/CatalogForm.js +1 -1
  86. package/dist/components/catalog/CatalogForm.js.map +1 -1
  87. package/dist/components/common/ActionAgentTargetNotice.js +6 -6
  88. package/dist/components/common/ActionAgentTargetNotice.js.map +1 -1
  89. package/dist/components/common/CommandEditor.js +1 -1
  90. package/dist/components/common/CommandEditor.js.map +1 -1
  91. package/dist/components/docs/DocsPageTree.js +1 -1
  92. package/dist/components/docs/DocsPageTree.js.map +1 -1
  93. package/dist/components/docs/DocsPreviewPanel.js +1 -1
  94. package/dist/components/docs/DocsPreviewPanel.js.map +1 -1
  95. package/dist/components/docs/DocsRevisionHistory.js +4 -4
  96. package/dist/components/docs/DocsRevisionHistory.js.map +1 -1
  97. package/dist/components/docs/DocsSettingsPanel.js +7 -7
  98. package/dist/components/docs/DocsSettingsPanel.js.map +1 -1
  99. package/dist/components/docs/DocsTabPanel.js +1 -1
  100. package/dist/components/docs/DocsTabPanel.js.map +1 -1
  101. package/dist/components/graph/ConnectionPermissionsDialog.js +2 -2
  102. package/dist/components/graph/ConnectionPermissionsDialog.js.map +1 -1
  103. package/dist/components/graph/MeshDeployWizard.js +3 -3
  104. package/dist/components/graph/MeshDeployWizard.js.map +1 -1
  105. package/dist/components/graph/MeshRequestPanel.js +2 -2
  106. package/dist/components/graph/MeshRequestPanel.js.map +1 -1
  107. package/dist/components/idp-components/ComponentCard.js +5 -5
  108. package/dist/components/idp-components/ComponentCard.js.map +1 -1
  109. package/dist/components/idp-components/ComponentForm.js +1 -1
  110. package/dist/components/idp-components/ComponentForm.js.map +1 -1
  111. package/dist/components/idp-components/DependencyGraphTab.js +7 -7
  112. package/dist/components/idp-components/DependencyGraphTab.js.map +1 -1
  113. package/dist/components/logs/LogViewer.js +14 -14
  114. package/dist/components/logs/LogViewer.js.map +1 -1
  115. package/dist/components/notes/NoteCard.js +1 -1
  116. package/dist/components/notes/NoteCard.js.map +1 -1
  117. package/dist/components/notes/NoteEditor.js +2 -2
  118. package/dist/components/notes/NoteEditor.js.map +1 -1
  119. package/dist/components/notes/NoteList.js +1 -1
  120. package/dist/components/notes/NoteList.js.map +1 -1
  121. package/dist/components/notes/NotesTabPanel.js +1 -1
  122. package/dist/components/notes/NotesTabPanel.js.map +1 -1
  123. package/dist/components/scorecard/ScorecardEmptyState.js +1 -1
  124. package/dist/components/scorecard/ScorecardEmptyState.js.map +1 -1
  125. package/dist/components/scorecard/ScorecardPanel.js +3 -3
  126. package/dist/components/scorecard/ScorecardPanel.js.map +1 -1
  127. package/dist/components/security/SecurityScanList.js +11 -11
  128. package/dist/components/security/SecurityScanList.js.map +1 -1
  129. package/dist/components/sessions/SessionCard.js +26 -19
  130. package/dist/components/sessions/SessionCard.js.map +1 -1
  131. package/dist/components/sessions/SessionStatusBadge.js +7 -7
  132. package/dist/components/sessions/SessionStatusBadge.js.map +1 -1
  133. package/dist/components/shared/AgentSelector.js +4 -4
  134. package/dist/components/shared/AgentSelector.js.map +1 -1
  135. package/dist/components/shared/AgentSetupBanner.js +3 -3
  136. package/dist/components/shared/AgentSetupBanner.js.map +1 -1
  137. package/dist/components/shared/Card.js +4 -4
  138. package/dist/components/shared/Card.js.map +1 -1
  139. package/dist/components/shared/ErrorState.js +15 -9
  140. package/dist/components/shared/ErrorState.js.map +1 -1
  141. package/dist/components/shared/FilterDropdown.js +2 -2
  142. package/dist/components/shared/FilterDropdown.js.map +1 -1
  143. package/dist/components/shared/PageSkeleton.js +6 -6
  144. package/dist/components/shared/PageSkeleton.js.map +1 -1
  145. package/dist/components/shared/SearchInput.js +1 -1
  146. package/dist/components/shared/SearchInput.js.map +1 -1
  147. package/dist/components/shared/StatCard.js +7 -7
  148. package/dist/components/shared/StatCard.js.map +1 -1
  149. package/dist/components/templates/PublishTemplateForm.js +2 -2
  150. package/dist/components/templates/PublishTemplateForm.js.map +1 -1
  151. package/dist/components/templates/TemplateCard.js +5 -5
  152. package/dist/components/templates/TemplateCard.js.map +1 -1
  153. package/dist/components/templates/TemplateWizard.js +2 -2
  154. package/dist/components/templates/TemplateWizard.js.map +1 -1
  155. package/dist/components/tunnels/DomainVerificationBadge.js +4 -4
  156. package/dist/components/tunnels/DomainVerificationBadge.js.map +1 -1
  157. package/dist/components/tunnels/ProviderBadge.js +1 -1
  158. package/dist/components/tunnels/ProviderBadge.js.map +1 -1
  159. package/dist/components/tunnels/TunnelCard.js +16 -16
  160. package/dist/components/tunnels/TunnelCard.js.map +1 -1
  161. package/dist/components/tunnels/TunnelCreateWizard.js +1 -1
  162. package/dist/components/tunnels/TunnelCreateWizard.js.map +1 -1
  163. package/dist/components/tunnels/TunnelPublicAccessPanel.js +1 -1
  164. package/dist/components/tunnels/TunnelPublicAccessPanel.js.map +1 -1
  165. package/dist/components/tunnels/TunnelStatusBadge.js +4 -4
  166. package/dist/components/tunnels/TunnelStatusBadge.js.map +1 -1
  167. package/dist/components/vibecalendar/LogsDrawer.js +2 -2
  168. package/dist/components/vibecalendar/LogsDrawer.js.map +1 -1
  169. package/dist/components/vibecalendar/ScheduleTaskModal.js +5 -5
  170. package/dist/components/vibecalendar/ScheduleTaskModal.js.map +1 -1
  171. package/dist/components/vibecalendar/VibeCalAgendaView.js +2 -2
  172. package/dist/components/vibecalendar/VibeCalAgendaView.js.map +1 -1
  173. package/dist/components/vibecalendar/VibeCalDayView.js +3 -3
  174. package/dist/components/vibecalendar/VibeCalDayView.js.map +1 -1
  175. package/dist/components/vibecalendar/VibeCalMonthView.js +1 -1
  176. package/dist/components/vibecalendar/VibeCalMonthView.js.map +1 -1
  177. package/dist/components/vibecalendar/VibeCalWeekView.js +4 -4
  178. package/dist/components/vibecalendar/VibeCalWeekView.js.map +1 -1
  179. package/dist/components/vibecalendar/VibeCalendarHeader.js +4 -4
  180. package/dist/components/vibecalendar/VibeCalendarHeader.js.map +1 -1
  181. package/dist/components/vibecalendar/VibeTaskBlock.js +2 -2
  182. package/dist/components/vibecalendar/VibeTaskBlock.js.map +1 -1
  183. package/dist/components/vibecalendar/types.js +6 -6
  184. package/dist/components/vibecalendar/types.js.map +1 -1
  185. package/dist/components/vibedeck/VibeDeckButtonForm.js +9 -9
  186. package/dist/components/vibedeck/VibeDeckButtonForm.js.map +1 -1
  187. package/dist/components/vibedeck/VibeDeckButtonTile.js +6 -6
  188. package/dist/components/vibedeck/VibeDeckButtonTile.js.map +1 -1
  189. package/dist/components/vibedeck/VibeDeckExecutionLog.js +10 -10
  190. package/dist/components/vibedeck/VibeDeckExecutionLog.js.map +1 -1
  191. package/dist/components/vibedeck/VibeDeckForm.js +8 -8
  192. package/dist/components/vibedeck/VibeDeckForm.js.map +1 -1
  193. package/dist/components/vibedeck/VibeDeckGrid.js +2 -2
  194. package/dist/components/vibedeck/VibeDeckGrid.js.map +1 -1
  195. package/dist/components/vibedeck/VibeDeckPublicLinkPanel.js +2 -2
  196. package/dist/components/vibedeck/VibeDeckPublicLinkPanel.js.map +1 -1
  197. package/dist/components/vibedeck/VibeDeckSwitcher.js +8 -8
  198. package/dist/components/vibedeck/VibeDeckSwitcher.js.map +1 -1
  199. package/dist/components/vibes/VibeCard.js +22 -18
  200. package/dist/components/vibes/VibeCard.js.map +1 -1
  201. package/dist/components/vibes/VibeList.js +16 -12
  202. package/dist/components/vibes/VibeList.js.map +1 -1
  203. package/dist/pages/ActionsPage.js +4 -3
  204. package/dist/pages/ActionsPage.js.map +1 -1
  205. package/dist/pages/AgentDetailsPage.js +9 -9
  206. package/dist/pages/AgentDetailsPage.js.map +1 -1
  207. package/dist/pages/AgentsLandingPage.js +4 -4
  208. package/dist/pages/AgentsLandingPage.js.map +1 -1
  209. package/dist/pages/AgentsPage.js +14 -14
  210. package/dist/pages/AgentsPage.js.map +1 -1
  211. package/dist/pages/AnalyticsPage.js +8 -8
  212. package/dist/pages/AnalyticsPage.js.map +1 -1
  213. package/dist/pages/CatalogDetailPage.js +11 -11
  214. package/dist/pages/CatalogDetailPage.js.map +1 -1
  215. package/dist/pages/CatalogPage.js +12 -9
  216. package/dist/pages/CatalogPage.js.map +1 -1
  217. package/dist/pages/ComponentDetailPage.js +7 -7
  218. package/dist/pages/ComponentDetailPage.js.map +1 -1
  219. package/dist/pages/ComponentsPage.js +12 -9
  220. package/dist/pages/ComponentsPage.js.map +1 -1
  221. package/dist/pages/EnvTopologyPage.js +2 -2
  222. package/dist/pages/EnvTopologyPage.js.map +1 -1
  223. package/dist/pages/GettingStartedPage.js +12 -12
  224. package/dist/pages/GettingStartedPage.js.map +1 -1
  225. package/dist/pages/HealthPage.js +12 -12
  226. package/dist/pages/HealthPage.js.map +1 -1
  227. package/dist/pages/LogsPage.js +4 -4
  228. package/dist/pages/LogsPage.js.map +1 -1
  229. package/dist/pages/NoteDetailsPage.js +5 -5
  230. package/dist/pages/NoteDetailsPage.js.map +1 -1
  231. package/dist/pages/OverviewPage.js +74 -41
  232. package/dist/pages/OverviewPage.js.map +1 -1
  233. package/dist/pages/PluginsCatalogPage.js +6 -3
  234. package/dist/pages/PluginsCatalogPage.js.map +1 -1
  235. package/dist/pages/ScheduledJobDetailPage.js +7 -7
  236. package/dist/pages/ScheduledJobDetailPage.js.map +1 -1
  237. package/dist/pages/SessionDetailsPage.js +21 -21
  238. package/dist/pages/SessionDetailsPage.js.map +1 -1
  239. package/dist/pages/SessionsPage.js +20 -20
  240. package/dist/pages/SessionsPage.js.map +1 -1
  241. package/dist/pages/SettingsPage.js +64 -52
  242. package/dist/pages/SettingsPage.js.map +1 -1
  243. package/dist/pages/SharedSessionPage.js +21 -21
  244. package/dist/pages/SharedSessionPage.js.map +1 -1
  245. package/dist/pages/SharedVibeDeckPage.js +5 -5
  246. package/dist/pages/SharedVibeDeckPage.js.map +1 -1
  247. package/dist/pages/TargetDetailsPage.js +56 -65
  248. package/dist/pages/TargetDetailsPage.js.map +1 -1
  249. package/dist/pages/TargetsPage.js +4 -4
  250. package/dist/pages/TargetsPage.js.map +1 -1
  251. package/dist/pages/TemplateDetailPage.js +10 -10
  252. package/dist/pages/TemplateDetailPage.js.map +1 -1
  253. package/dist/pages/TemplatesPage.js +6 -5
  254. package/dist/pages/TemplatesPage.js.map +1 -1
  255. package/dist/pages/VibeAuditPage.js +3 -3
  256. package/dist/pages/VibeAuditPage.js.map +1 -1
  257. package/dist/pages/VibeCalendarPage.js +3 -3
  258. package/dist/pages/VibeCalendarPage.js.map +1 -1
  259. package/dist/pages/VibeDeckPage.js +3 -3
  260. package/dist/pages/VibeDeckPage.js.map +1 -1
  261. package/dist/pages/VibeDetailsPage.js +15 -15
  262. package/dist/pages/VibeDetailsPage.js.map +1 -1
  263. package/dist/pages/VibeTaskDetailPage.js +5 -5
  264. package/dist/pages/VibeTaskDetailPage.js.map +1 -1
  265. package/dist/pages/VibesPage.js +18 -18
  266. package/dist/pages/VibesPage.js.map +1 -1
  267. package/dist/pages/WebhooksPage.js +11 -11
  268. package/dist/pages/WebhooksPage.js.map +1 -1
  269. package/dist/pages/ai/AIBookmarksPage.js +1 -1
  270. package/dist/pages/ai/AIBookmarksPage.js.map +1 -1
  271. package/dist/pages/ai/AISessionDetailPage.js +6 -6
  272. package/dist/pages/ai/AISessionDetailPage.js.map +1 -1
  273. package/dist/pages/tunnels/AuditTab.js +1 -1
  274. package/dist/pages/tunnels/AuditTab.js.map +1 -1
  275. package/dist/pages/tunnels/DomainsTab.js +1 -1
  276. package/dist/pages/tunnels/DomainsTab.js.map +1 -1
  277. package/dist/pages/tunnels/ProvidersTab.js +1 -1
  278. package/dist/pages/tunnels/ProvidersTab.js.map +1 -1
  279. package/dist/pages/tunnels/SessionsTab.js +1 -1
  280. package/dist/pages/tunnels/SessionsTab.js.map +1 -1
  281. package/dist/pages/tunnels/TunnelDetailsPage.js +3 -3
  282. package/dist/pages/tunnels/TunnelDetailsPage.js.map +1 -1
  283. package/dist/pages/tunnels/UsageTab.js +1 -1
  284. package/dist/pages/tunnels/UsageTab.js.map +1 -1
  285. package/dist/pages/tunnels/tabs/TunnelDomainsTab.js +16 -16
  286. package/dist/pages/tunnels/tabs/TunnelDomainsTab.js.map +1 -1
  287. package/dist/pages/tunnels/tabs/TunnelOverviewTab.js +1 -1
  288. package/dist/pages/tunnels/tabs/TunnelOverviewTab.js.map +1 -1
  289. package/dist/pages/tunnels/tabs/TunnelSessionsTab.js +52 -49
  290. package/dist/pages/tunnels/tabs/TunnelSessionsTab.js.map +1 -1
  291. package/dist/pages/tunnels/tabs/TunnelSettingsTab.js +9 -9
  292. package/dist/pages/tunnels/tabs/TunnelSettingsTab.js.map +1 -1
  293. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"AgentSetupBanner.js","names":[],"sources":["../../../src/components/shared/AgentSetupBanner.tsx"],"sourcesContent":["import { useState, useCallback, useMemo } from 'react';\nimport { motion, AnimatePresence } from 'framer-motion';\nimport { Download, Copy, Check, ExternalLink, X, Terminal } from 'lucide-react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { getProfileScopedKey } from '@burdenoff/fe-libs/shared/utils';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { InstallTabs } from './InstallTabs';\n\ninterface AgentSetupBannerProps {\n /** Compact mode shows a single-line banner; full mode shows a multi-step card */\n variant?: 'compact' | 'full';\n /** Allow the banner to be dismissed */\n dismissible?: boolean;\n /** Storage key for dismissed state */\n dismissKey?: string;\n /** Additional CSS class names */\n className?: string;\n}\n\nconst NPM_URL = 'https://www.npmjs.com/package/@vibecontrols/agent';\n\n/**\n * Build the quick-install one-liner using the live host so the command shown\n * to the user reflects the environment they are actually on (local dev,\n * alpha, prod). InstallTabs uses the same `resolveHost()` shape — keep them\n * aligned by reading from `window.__VIBECONTROLS_HOST__` first, then\n * falling back to `window.location.host`.\n */\nfunction getQuickInstallCommand(): string {\n // Trailing `&& hash -r` runs in the user's shell to flush a stale cached\n // `vibe` path so the new binary resolves immediately (the install script also\n // rebinds it on disk; this is belt-and-braces, harmless when nothing stale).\n if (typeof window === 'undefined') {\n return 'curl -fsSL https://app.vibecontrols.com/install | bash && hash -r 2>/dev/null';\n }\n const injected = (window as { __VIBECONTROLS_HOST__?: string }).__VIBECONTROLS_HOST__;\n const host = injected ?? window.location.host;\n return `curl -fsSL https://${host}/install | bash && hash -r 2>/dev/null`;\n}\n\n/**\n * Reusable banner/card prompting users to install the VibeControls Agent CLI.\n * Used across multiple pages (Overview, Agents, Sessions, Tunnels, SSH, etc.)\n *\n * The full variant mirrors the Getting Started page's tabbed install widget\n * (`InstallTabs`) so users see a single consistent install flow wherever\n * the banner shows up.\n */\nexport function AgentSetupBanner({\n variant = 'compact',\n dismissible = true,\n dismissKey = 'vibecontrols-setup-banner-dismissed',\n className = '',\n}: AgentSetupBannerProps) {\n const tr = useTr();\n const handleNavigate = useVibeNavigate();\n const quickInstall = useMemo(() => getQuickInstallCommand(), []);\n const [copied, setCopied] = useState(false);\n const [dismissed, setDismissed] = useState(() => {\n try {\n return localStorage.getItem(getProfileScopedKey(dismissKey)) === 'true';\n } catch {\n return false;\n }\n });\n\n const handleCopy = useCallback(async (text: string) => {\n try {\n await navigator.clipboard.writeText(text);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n } catch {\n const el = document.createElement('textarea');\n el.value = text;\n document.body.appendChild(el);\n el.select();\n document.execCommand('copy');\n document.body.removeChild(el);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n }\n }, []);\n\n const handleDismiss = useCallback(() => {\n setDismissed(true);\n try {\n localStorage.setItem(getProfileScopedKey(dismissKey), 'true');\n } catch {\n // ignore\n }\n }, [dismissKey]);\n\n const bannerTransition = { duration: 0.3, ease: [0.4, 0, 0.2, 1] as const };\n\n // ─── Compact variant: single-line info banner ─────────────────────────\n if (variant === 'compact') {\n return (\n <AnimatePresence>\n {!dismissed && (\n <motion.div\n initial={{ opacity: 0, y: -8, height: 0 }}\n animate={{ opacity: 1, y: 0, height: 'auto' }}\n exit={{ opacity: 0, y: -8, height: 0 }}\n transition={bannerTransition}\n className={`relative flex flex-col items-start gap-3 px-4 py-3 bg-accent-purple-subtle border border-accent-purple/20 rounded-lg sm:flex-row sm:items-center ${className}`}\n >\n <Download className=\"size-5 text-accent-purple flex-shrink-0\" />\n <div className=\"flex min-w-0 flex-1 flex-wrap items-center gap-x-3 gap-y-1\">\n <span className=\"text-sm font-medium text-text-primary\">\n {tr(\n 'vibecontrols.shared.agentSetupBanner.installPrompt',\n 'Install the VibeControls Agent CLI to get started'\n )}\n </span>\n <code\n className=\"inline-flex min-w-0 max-w-full items-center gap-1.5 rounded border border-border-default bg-bg-sunken px-2.5 py-1 font-mono text-xs text-text-primary transition-colors hover:bg-bg-elevated\"\n onClick={() => handleCopy(quickInstall)}\n title={tr('vibecontrols.shared.agentSetupBanner.clickToCopy', 'Click to copy')}\n >\n <Terminal className=\"size-3\" />\n <span className=\"min-w-0 break-all\">{quickInstall}</span>\n {copied ? (\n <Check className=\"size-3 text-status-success-text\" />\n ) : (\n <Copy className=\"size-3 text-text-muted\" />\n )}\n </code>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/getting-started')}\n className=\"text-sm text-accent-purple hover:underline flex items-center gap-1\"\n >\n {tr('vibecontrols.shared.agentSetupBanner.setupGuide', 'Setup guide')}{' '}\n <ExternalLink className=\"size-3\" />\n </button>\n </div>\n {dismissible && (\n <button\n type=\"button\"\n onClick={handleDismiss}\n className=\"flex-shrink-0 p-1 text-text-muted hover:text-text-secondary rounded transition-colors\"\n title={tr('vibecontrols.shared.agentSetupBanner.dismiss', 'Dismiss')}\n >\n <X className=\"size-4\" />\n </button>\n )}\n </motion.div>\n )}\n </AnimatePresence>\n );\n }\n\n // ─── Full variant: tabbed install card (mirrors Getting Started) ──────\n return (\n <AnimatePresence>\n {!dismissed && (\n <motion.div\n initial={{ opacity: 0, y: -8, height: 0 }}\n animate={{ opacity: 1, y: 0, height: 'auto' }}\n exit={{ opacity: 0, y: -8, height: 0 }}\n transition={bannerTransition}\n className={`relative border border-accent-purple/20 bg-accent-purple-subtle rounded-lg overflow-hidden ${className}`}\n >\n {dismissible && (\n <button\n type=\"button\"\n onClick={handleDismiss}\n className=\"absolute top-3 right-3 p-1 text-text-muted hover:text-text-secondary rounded transition-colors z-10\"\n title={tr('vibecontrols.shared.agentSetupBanner.dismiss', 'Dismiss')}\n >\n <X className=\"size-4\" />\n </button>\n )}\n\n <div className=\"p-5 sm:p-6\">\n <div className=\"flex items-start gap-3 mb-4\">\n <div className=\"flex-shrink-0 size-10 rounded-lg bg-accent-purple/10 flex items-center justify-center\">\n <Download className=\"size-5 text-accent-purple\" />\n </div>\n <div>\n <h3 className=\"text-base font-semibold text-text-primary\">\n {tr(\n 'vibecontrols.shared.agentSetupBanner.title',\n 'Get Started with VibeControls Agent'\n )}\n </h3>\n <p className=\"text-sm text-text-secondary mt-0.5\">\n {tr(\n 'vibecontrols.shared.agentSetupBanner.description',\n 'Install the CLI on any machine you want to manage. The only prerequisite is bun — the curl script installs it for you.'\n )}\n </p>\n </div>\n </div>\n\n {/* Tabbed install widget mirrors the Getting Started page */}\n <div className=\"ml-0 sm:ml-13\">\n <InstallTabs compact />\n </div>\n\n {/* Actions */}\n <div className=\"flex flex-wrap items-center gap-3 mt-5 ml-0 sm:ml-13\">\n <Button\n variant=\"default\"\n onClick={() => handleNavigate('/getting-started')}\n className=\"gap-2\"\n >\n {tr(\n 'vibecontrols.shared.agentSetupBanner.viewFullSetupGuide',\n 'View full setup guide'\n )}\n <ExternalLink className=\"size-3.5\" />\n </Button>\n <Button variant=\"outline\" asChild className=\"gap-2\">\n <a href={NPM_URL} target=\"_blank\" rel=\"noopener noreferrer\">\n {tr('vibecontrols.shared.agentSetupBanner.npmPackage', 'npm package')}\n <ExternalLink className=\"size-3.5\" />\n </a>\n </Button>\n </div>\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AgentSetupBanner.js","names":[],"sources":["../../../src/components/shared/AgentSetupBanner.tsx"],"sourcesContent":["import { useState, useCallback, useMemo } from 'react';\nimport { motion, AnimatePresence } from 'framer-motion';\nimport { Download, Copy, Check, ExternalLink, X, Terminal } from 'lucide-react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { getProfileScopedKey } from '@burdenoff/fe-libs/shared/utils';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { InstallTabs } from './InstallTabs';\n\ninterface AgentSetupBannerProps {\n /** Compact mode shows a single-line banner; full mode shows a multi-step card */\n variant?: 'compact' | 'full';\n /** Allow the banner to be dismissed */\n dismissible?: boolean;\n /** Storage key for dismissed state */\n dismissKey?: string;\n /** Additional CSS class names */\n className?: string;\n}\n\nconst NPM_URL = 'https://www.npmjs.com/package/@vibecontrols/agent';\n\n/**\n * Build the quick-install one-liner using the live host so the command shown\n * to the user reflects the environment they are actually on (local dev,\n * alpha, prod). InstallTabs uses the same `resolveHost()` shape — keep them\n * aligned by reading from `window.__VIBECONTROLS_HOST__` first, then\n * falling back to `window.location.host`.\n */\nfunction getQuickInstallCommand(): string {\n // Trailing `&& hash -r` runs in the user's shell to flush a stale cached\n // `vibe` path so the new binary resolves immediately (the install script also\n // rebinds it on disk; this is belt-and-braces, harmless when nothing stale).\n if (typeof window === 'undefined') {\n return 'curl -fsSL https://app.vibecontrols.com/install | bash && hash -r 2>/dev/null';\n }\n const injected = (window as { __VIBECONTROLS_HOST__?: string }).__VIBECONTROLS_HOST__;\n const host = injected ?? window.location.host;\n return `curl -fsSL https://${host}/install | bash && hash -r 2>/dev/null`;\n}\n\n/**\n * Reusable banner/card prompting users to install the VibeControls Agent CLI.\n * Used across multiple pages (Overview, Agents, Sessions, Tunnels, SSH, etc.)\n *\n * The full variant mirrors the Getting Started page's tabbed install widget\n * (`InstallTabs`) so users see a single consistent install flow wherever\n * the banner shows up.\n */\nexport function AgentSetupBanner({\n variant = 'compact',\n dismissible = true,\n dismissKey = 'vibecontrols-setup-banner-dismissed',\n className = '',\n}: AgentSetupBannerProps) {\n const tr = useTr();\n const handleNavigate = useVibeNavigate();\n const quickInstall = useMemo(() => getQuickInstallCommand(), []);\n const [copied, setCopied] = useState(false);\n const [dismissed, setDismissed] = useState(() => {\n try {\n return localStorage.getItem(getProfileScopedKey(dismissKey)) === 'true';\n } catch {\n return false;\n }\n });\n\n const handleCopy = useCallback(async (text: string) => {\n try {\n await navigator.clipboard.writeText(text);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n } catch {\n const el = document.createElement('textarea');\n el.value = text;\n document.body.appendChild(el);\n el.select();\n document.execCommand('copy');\n document.body.removeChild(el);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n }\n }, []);\n\n const handleDismiss = useCallback(() => {\n setDismissed(true);\n try {\n localStorage.setItem(getProfileScopedKey(dismissKey), 'true');\n } catch {\n // ignore\n }\n }, [dismissKey]);\n\n const bannerTransition = { duration: 0.3, ease: [0.4, 0, 0.2, 1] as const };\n\n // ─── Compact variant: single-line info banner ─────────────────────────\n if (variant === 'compact') {\n return (\n <AnimatePresence>\n {!dismissed && (\n <motion.div\n initial={{ opacity: 0, y: -8, height: 0 }}\n animate={{ opacity: 1, y: 0, height: 'auto' }}\n exit={{ opacity: 0, y: -8, height: 0 }}\n transition={bannerTransition}\n className={`relative flex flex-col items-start gap-3 px-4 py-3 bg-accent-purple-subtle border border-accent-purple/20 rounded-card sm:flex-row sm:items-center ${className}`}\n >\n <Download className=\"size-5 text-accent-purple flex-shrink-0\" />\n <div className=\"flex min-w-0 flex-1 flex-wrap items-center gap-x-3 gap-y-1\">\n <span className=\"text-sm font-medium text-text-primary\">\n {tr(\n 'vibecontrols.shared.agentSetupBanner.installPrompt',\n 'Install the VibeControls Agent CLI to get started'\n )}\n </span>\n <code\n className=\"inline-flex min-w-0 max-w-full items-center gap-1.5 rounded border border-border-subtle bg-bg-sunken px-2.5 py-1 font-mono text-xs text-text-primary transition-colors hover:bg-bg-elevated cursor-pointer\"\n onClick={() => handleCopy(quickInstall)}\n title={tr('vibecontrols.shared.agentSetupBanner.clickToCopy', 'Click to copy')}\n >\n <Terminal className=\"size-3\" />\n <span className=\"min-w-0 break-all\">{quickInstall}</span>\n {copied ? (\n <Check className=\"size-3 text-status-success-text\" />\n ) : (\n <Copy className=\"size-3 text-text-muted\" />\n )}\n </code>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/getting-started')}\n className=\"text-sm text-accent-purple hover:underline flex items-center gap-1\"\n >\n {tr('vibecontrols.shared.agentSetupBanner.setupGuide', 'Setup guide')}{' '}\n <ExternalLink className=\"size-3\" />\n </button>\n </div>\n {dismissible && (\n <button\n type=\"button\"\n onClick={handleDismiss}\n className=\"flex-shrink-0 p-1 text-text-muted hover:text-text-secondary rounded transition-colors\"\n title={tr('vibecontrols.shared.agentSetupBanner.dismiss', 'Dismiss')}\n >\n <X className=\"size-4\" />\n </button>\n )}\n </motion.div>\n )}\n </AnimatePresence>\n );\n }\n\n // ─── Full variant: tabbed install card (mirrors Getting Started) ──────\n return (\n <AnimatePresence>\n {!dismissed && (\n <motion.div\n initial={{ opacity: 0, y: -8, height: 0 }}\n animate={{ opacity: 1, y: 0, height: 'auto' }}\n exit={{ opacity: 0, y: -8, height: 0 }}\n transition={bannerTransition}\n className={`relative border border-accent-purple/20 bg-accent-purple-subtle rounded-card overflow-hidden ${className}`}\n >\n {dismissible && (\n <button\n type=\"button\"\n onClick={handleDismiss}\n className=\"absolute top-3 right-3 p-1 text-text-muted hover:text-text-secondary rounded transition-colors z-10\"\n title={tr('vibecontrols.shared.agentSetupBanner.dismiss', 'Dismiss')}\n >\n <X className=\"size-4\" />\n </button>\n )}\n\n <div className=\"p-5 sm:p-6\">\n <div className=\"flex items-start gap-3 mb-4\">\n <div className=\"flex-shrink-0 size-10 rounded-lg bg-accent-purple/10 flex items-center justify-center\">\n <Download className=\"size-5 text-accent-purple\" />\n </div>\n <div>\n <h3 className=\"text-base font-semibold text-text-primary\">\n {tr(\n 'vibecontrols.shared.agentSetupBanner.title',\n 'Get Started with VibeControls Agent'\n )}\n </h3>\n <p className=\"text-sm text-text-secondary mt-0.5\">\n {tr(\n 'vibecontrols.shared.agentSetupBanner.description',\n 'Install the CLI on any machine you want to manage. The only prerequisite is bun — the curl script installs it for you.'\n )}\n </p>\n </div>\n </div>\n\n {/* Tabbed install widget mirrors the Getting Started page */}\n <div className=\"ml-0 sm:ml-13\">\n <InstallTabs compact />\n </div>\n\n {/* Actions */}\n <div className=\"flex flex-wrap items-center gap-3 mt-5 ml-0 sm:ml-13\">\n <Button\n variant=\"default\"\n onClick={() => handleNavigate('/getting-started')}\n className=\"gap-2\"\n >\n {tr(\n 'vibecontrols.shared.agentSetupBanner.viewFullSetupGuide',\n 'View full setup guide'\n )}\n <ExternalLink className=\"size-3.5\" />\n </Button>\n <Button variant=\"outline\" asChild className=\"gap-2\">\n <a href={NPM_URL} target=\"_blank\" rel=\"noopener noreferrer\">\n {tr('vibecontrols.shared.agentSetupBanner.npmPackage', 'npm package')}\n <ExternalLink className=\"size-3.5\" />\n </a>\n </Button>\n </div>\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n );\n}\n"],"mappings":"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"}
@@ -1,14 +1,14 @@
1
1
  import { jsx as e } from "react/jsx-runtime";
2
2
  //#region src/components/shared/Card.tsx
3
3
  function t({ children: t, className: n = "", onClick: r, hoverable: i = !1, padding: a = "md" }) {
4
- let o = `bg-bg-surface border border-border-default rounded-card ${{
4
+ let o = `bg-bg-surface border border-border-subtle rounded-card shadow-elevation-1 ${{
5
5
  none: "",
6
- sm: "p-3",
7
- md: "p-4",
6
+ sm: "p-4",
7
+ md: "p-5",
8
8
  lg: "p-6"
9
9
  }[a]}`;
10
10
  return /* @__PURE__ */ e(r ? "button" : "div", {
11
- className: `${o} ${i || r ? "hover:border-border-strong hover:shadow-[var(--shadow-elevation-1)] transition-[colors,box-shadow] duration-150 cursor-pointer" : ""} ${n}`,
11
+ className: `${o} ${i || r ? "hover:border-border-strong hover:shadow-elevation-2 hover:-translate-y-0.5 transition-all duration-200 ease-[var(--motion-easing-out)] cursor-pointer" : ""} ${n}`,
12
12
  onClick: r,
13
13
  children: t
14
14
  });
@@ -1 +1 @@
1
- {"version":3,"file":"Card.js","names":[],"sources":["../../../src/components/shared/Card.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\n\ninterface CardProps {\n children: ReactNode;\n className?: string;\n onClick?: () => void;\n hoverable?: boolean;\n padding?: 'none' | 'sm' | 'md' | 'lg';\n}\n\nexport function Card({\n children,\n className = '',\n onClick,\n hoverable = false,\n padding = 'md',\n}: CardProps) {\n const paddingStyles = {\n none: '',\n sm: 'p-3',\n md: 'p-4',\n lg: 'p-6',\n };\n\n const baseStyles = `bg-bg-surface border border-border-default rounded-card ${paddingStyles[padding]}`;\n const isInteractive = hoverable || !!onClick;\n\n // Hover affordance is a calm border + elevation change only (no transform /\n // movement); `transition-[colors,box-shadow]` keeps it from animating layout.\n const interactiveStyles = isInteractive\n ? 'hover:border-border-strong hover:shadow-[var(--shadow-elevation-1)] transition-[colors,box-shadow] duration-150 cursor-pointer'\n : '';\n\n const Component = onClick ? 'button' : 'div';\n\n return (\n <Component className={`${baseStyles} ${interactiveStyles} ${className}`} onClick={onClick}>\n {children}\n </Component>\n );\n}\n\ninterface CardHeaderProps {\n title: string;\n subtitle?: string;\n action?: ReactNode;\n icon?: ReactNode;\n}\n\nexport function CardHeader({ title, subtitle, action, icon }: CardHeaderProps) {\n return (\n <div className=\"flex items-start justify-between gap-3 mb-3\">\n <div className=\"flex items-start gap-3 min-w-0\">\n {icon && (\n <div className=\"flex-shrink-0 size-10 rounded-lg bg-bg-sunken flex items-center justify-center\">\n {icon}\n </div>\n )}\n <div className=\"min-w-0\">\n <h3 className=\"font-semibold text-text-primary truncate\">{title}</h3>\n {subtitle && <p className=\"text-sm text-text-secondary truncate\">{subtitle}</p>}\n </div>\n </div>\n {action && <div className=\"flex-shrink-0\">{action}</div>}\n </div>\n );\n}\n\ninterface CardContentProps {\n children: ReactNode;\n className?: string;\n}\n\nexport function CardContent({ children, className = '' }: CardContentProps) {\n return <div className={className}>{children}</div>;\n}\n\ninterface CardFooterProps {\n children: ReactNode;\n className?: string;\n}\n\nexport function CardFooter({ children, className = '' }: CardFooterProps) {\n return <div className={`mt-4 pt-3 border-t border-border-default ${className}`}>{children}</div>;\n}\n"],"mappings":";;AAUA,SAAgB,EAAK,EACnB,aACA,eAAY,IACZ,YACA,eAAY,IACZ,aAAU,QACE;CAQZ,IAAM,IAAa,2DAPG;EACpB,MAAM;EACN,IAAI;EACJ,IAAI;EACJ,IAAI;EACL,CAE2F;AAW5F,QACE,kBAHgB,IAAU,WAAW,OAGrC;EAAW,WAAW,GAAG,EAAW,GAXhB,KAAe,IAKjC,mIACA,GAKuD,GAAG;EAAsB;EAC/E;EACS,CAAA"}
1
+ {"version":3,"file":"Card.js","names":[],"sources":["../../../src/components/shared/Card.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\n\ninterface CardProps {\n children: ReactNode;\n className?: string;\n onClick?: () => void;\n hoverable?: boolean;\n padding?: 'none' | 'sm' | 'md' | 'lg';\n}\n\nexport function Card({\n children,\n className = '',\n onClick,\n hoverable = false,\n padding = 'md',\n}: CardProps) {\n const paddingStyles = {\n none: '',\n sm: 'p-4',\n md: 'p-5',\n lg: 'p-6',\n };\n\n const baseStyles = `bg-bg-surface border border-border-subtle rounded-card shadow-elevation-1 ${paddingStyles[padding]}`;\n const isInteractive = hoverable || !!onClick;\n\n // Boff UI interactive card: calm border + elevation lift + ≤2px translate on\n // hover. `transition-all duration-200` with the easing token; reduced-motion\n // is handled globally so the lift is safe.\n const interactiveStyles = isInteractive\n ? 'hover:border-border-strong hover:shadow-elevation-2 hover:-translate-y-0.5 transition-all duration-200 ease-[var(--motion-easing-out)] cursor-pointer'\n : '';\n\n const Component = onClick ? 'button' : 'div';\n\n return (\n <Component className={`${baseStyles} ${interactiveStyles} ${className}`} onClick={onClick}>\n {children}\n </Component>\n );\n}\n\ninterface CardHeaderProps {\n title: string;\n subtitle?: string;\n action?: ReactNode;\n icon?: ReactNode;\n}\n\nexport function CardHeader({ title, subtitle, action, icon }: CardHeaderProps) {\n return (\n <div className=\"flex items-start justify-between gap-3 mb-4\">\n <div className=\"flex items-start gap-3 min-w-0\">\n {icon && (\n <div className=\"flex-shrink-0 size-9 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center\">\n {icon}\n </div>\n )}\n <div className=\"min-w-0\">\n <h3 className=\"font-semibold text-text-primary truncate tracking-tight\">{title}</h3>\n {subtitle && <p className=\"text-sm text-text-secondary truncate\">{subtitle}</p>}\n </div>\n </div>\n {action && <div className=\"flex-shrink-0\">{action}</div>}\n </div>\n );\n}\n\ninterface CardContentProps {\n children: ReactNode;\n className?: string;\n}\n\nexport function CardContent({ children, className = '' }: CardContentProps) {\n return <div className={className}>{children}</div>;\n}\n\ninterface CardFooterProps {\n children: ReactNode;\n className?: string;\n}\n\nexport function CardFooter({ children, className = '' }: CardFooterProps) {\n return <div className={`mt-5 pt-4 border-t border-border-subtle ${className}`}>{children}</div>;\n}\n"],"mappings":";;AAUA,SAAgB,EAAK,EACnB,aACA,eAAY,IACZ,YACA,eAAY,IACZ,aAAU,QACE;CAQZ,IAAM,IAAa,6EAPG;EACpB,MAAM;EACN,IAAI;EACJ,IAAI;EACJ,IAAI;EACL,CAE6G;AAY9G,QACE,kBAHgB,IAAU,WAAW,OAGrC;EAAW,WAAW,GAAG,EAAW,GAZhB,KAAe,IAMjC,0JACA,GAKuD,GAAG;EAAsB;EAC/E;EACS,CAAA"}
@@ -9,15 +9,18 @@ function s({ title: s, message: c, onRetry: l, action: u }) {
9
9
  className: "flex flex-col items-center justify-center py-12 px-4 text-center",
10
10
  children: [
11
11
  /* @__PURE__ */ a("div", {
12
- className: "size-16 rounded-full bg-status-error-bg-subtle flex items-center justify-center mb-4",
13
- children: /* @__PURE__ */ a(t, { className: "size-8 text-status-error-text" })
12
+ className: "size-16 rounded-full bg-status-error-bg-subtle flex items-center justify-center mb-5",
13
+ children: /* @__PURE__ */ a(t, {
14
+ className: "size-8 text-status-error-text",
15
+ "aria-hidden": "true"
16
+ })
14
17
  }),
15
18
  /* @__PURE__ */ a("h3", {
16
- className: "text-lg font-semibold text-text-primary mb-1",
19
+ className: "text-lg font-semibold text-text-primary mb-1.5 tracking-tight",
17
20
  children: f
18
21
  }),
19
22
  /* @__PURE__ */ a("p", {
20
- className: "text-sm text-text-secondary mb-4 max-w-md",
23
+ className: "text-sm text-text-secondary mb-5 max-w-md",
21
24
  children: c
22
25
  }),
23
26
  /* @__PURE__ */ o("div", {
@@ -25,7 +28,7 @@ function s({ title: s, message: c, onRetry: l, action: u }) {
25
28
  children: [l && /* @__PURE__ */ o("button", {
26
29
  type: "button",
27
30
  onClick: l,
28
- className: "inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors",
31
+ className: "inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-button hover:bg-action-primary-bgHover transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
29
32
  children: [/* @__PURE__ */ a(n, { className: "size-4" }), d("vibecontrols.shared.errorState.tryAgain", "Try Again")]
30
33
  }), u]
31
34
  })
@@ -37,7 +40,7 @@ function s({ title: s, message: c, onRetry: l, action: u }) {
37
40
  transition: { duration: .25 },
38
41
  children: [
39
42
  /* @__PURE__ */ a(r.div, {
40
- className: "size-16 rounded-full bg-status-error-bg-subtle flex items-center justify-center mb-4",
43
+ className: "size-16 rounded-full bg-status-error-bg-subtle flex items-center justify-center mb-5",
41
44
  initial: {
42
45
  scale: .5,
43
46
  opacity: 0
@@ -52,10 +55,13 @@ function s({ title: s, message: c, onRetry: l, action: u }) {
52
55
  damping: 15,
53
56
  delay: .1
54
57
  },
55
- children: /* @__PURE__ */ a(t, { className: "size-8 text-status-error-text" })
58
+ children: /* @__PURE__ */ a(t, {
59
+ className: "size-8 text-status-error-text",
60
+ "aria-hidden": "true"
61
+ })
56
62
  }),
57
63
  /* @__PURE__ */ a(r.h3, {
58
- className: "text-lg font-semibold text-text-primary mb-1",
64
+ className: "text-lg font-semibold text-text-primary mb-1.5 tracking-tight",
59
65
  initial: {
60
66
  opacity: 0,
61
67
  y: 8
@@ -71,7 +77,7 @@ function s({ title: s, message: c, onRetry: l, action: u }) {
71
77
  children: f
72
78
  }),
73
79
  /* @__PURE__ */ a(r.p, {
74
- className: "text-sm text-text-secondary mb-4 max-w-md",
80
+ className: "text-sm text-text-secondary mb-5 max-w-md",
75
81
  initial: {
76
82
  opacity: 0,
77
83
  y: 8
@@ -1 +1 @@
1
- {"version":3,"file":"ErrorState.js","names":[],"sources":["../../../src/components/shared/ErrorState.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { AlertTriangle, RefreshCw } from 'lucide-react';\nimport { motion, useReducedMotion } from 'framer-motion';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface ErrorStateProps {\n title?: string;\n message: string;\n onRetry?: () => void;\n action?: ReactNode;\n}\n\nexport function ErrorState({ title, message, onRetry, action }: ErrorStateProps) {\n const tr = useTr();\n const resolvedTitle = title ?? tr('vibecontrols.shared.errorState.title', 'Something went wrong');\n const shouldReduceMotion = useReducedMotion();\n\n if (shouldReduceMotion) {\n return (\n <div className=\"flex flex-col items-center justify-center py-12 px-4 text-center\">\n <div className=\"size-16 rounded-full bg-status-error-bg-subtle flex items-center justify-center mb-4\">\n <AlertTriangle className=\"size-8 text-status-error-text\" />\n </div>\n <h3 className=\"text-lg font-semibold text-text-primary mb-1\">{resolvedTitle}</h3>\n <p className=\"text-sm text-text-secondary mb-4 max-w-md\">{message}</p>\n <div className=\"flex items-center gap-3\">\n {onRetry && (\n <button\n type=\"button\"\n onClick={onRetry}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <RefreshCw className=\"size-4\" />\n {tr('vibecontrols.shared.errorState.tryAgain', 'Try Again')}\n </button>\n )}\n {action}\n </div>\n </div>\n );\n }\n\n return (\n <motion.div\n className=\"flex flex-col items-center justify-center py-12 px-4 text-center\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n transition={{ duration: 0.25 }}\n >\n <motion.div\n className=\"size-16 rounded-full bg-status-error-bg-subtle flex items-center justify-center mb-4\"\n initial={{ scale: 0.5, opacity: 0 }}\n animate={{ scale: 1, opacity: 1 }}\n transition={{\n type: 'spring',\n stiffness: 400,\n damping: 15,\n delay: 0.1,\n }}\n >\n <AlertTriangle className=\"size-8 text-status-error-text\" />\n </motion.div>\n <motion.h3\n className=\"text-lg font-semibold text-text-primary mb-1\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ duration: 0.25, delay: 0.2 }}\n >\n {resolvedTitle}\n </motion.h3>\n <motion.p\n className=\"text-sm text-text-secondary mb-4 max-w-md\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ duration: 0.25, delay: 0.3 }}\n >\n {message}\n </motion.p>\n <motion.div\n className=\"flex items-center gap-3\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ duration: 0.25, delay: 0.4 }}\n >\n {onRetry && (\n <button\n type=\"button\"\n onClick={onRetry}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-button hover:bg-action-primary-bgHover active:scale-[0.98] transition-[colors,transform] duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <RefreshCw className=\"size-4\" aria-hidden=\"true\" />\n {tr('vibecontrols.shared.errorState.tryAgain', 'Try Again')}\n </button>\n )}\n {action}\n </motion.div>\n </motion.div>\n );\n}\n"],"mappings":";;;;;AAYA,SAAgB,EAAW,EAAE,UAAO,YAAS,YAAS,aAA2B;CAC/E,IAAM,IAAK,GAAO,EACZ,IAAgB,KAAS,EAAG,wCAAwC,uBAAuB;AA4BjG,QA3B2B,GAAkB,GAIzC,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD,EAAe,WAAU,iCAAkC,CAAA;IACvD,CAAA;GACN,kBAAC,MAAD;IAAI,WAAU;cAAgD;IAAmB,CAAA;GACjF,kBAAC,KAAD;IAAG,WAAU;cAA6C;IAAY,CAAA;GACtE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,KACC,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;eAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA,EAC/B,EAAG,2CAA2C,YAAY,CACpD;QAEV,EACG;;GACF;MAKR,kBAAC,EAAO,KAAR;EACE,WAAU;EACV,SAAS,EAAE,SAAS,GAAG;EACvB,SAAS,EAAE,SAAS,GAAG;EACvB,YAAY,EAAE,UAAU,KAAM;YAJhC;GAME,kBAAC,EAAO,KAAR;IACE,WAAU;IACV,SAAS;KAAE,OAAO;KAAK,SAAS;KAAG;IACnC,SAAS;KAAE,OAAO;KAAG,SAAS;KAAG;IACjC,YAAY;KACV,MAAM;KACN,WAAW;KACX,SAAS;KACT,OAAO;KACR;cAED,kBAAC,GAAD,EAAe,WAAU,iCAAkC,CAAA;IAChD,CAAA;GACb,kBAAC,EAAO,IAAR;IACE,WAAU;IACV,SAAS;KAAE,SAAS;KAAG,GAAG;KAAG;IAC7B,SAAS;KAAE,SAAS;KAAG,GAAG;KAAG;IAC7B,YAAY;KAAE,UAAU;KAAM,OAAO;KAAK;cAEzC;IACS,CAAA;GACZ,kBAAC,EAAO,GAAR;IACE,WAAU;IACV,SAAS;KAAE,SAAS;KAAG,GAAG;KAAG;IAC7B,SAAS;KAAE,SAAS;KAAG,GAAG;KAAG;IAC7B,YAAY;KAAE,UAAU;KAAM,OAAO;KAAK;cAEzC;IACQ,CAAA;GACX,kBAAC,EAAO,KAAR;IACE,WAAU;IACV,SAAS;KAAE,SAAS;KAAG,GAAG;KAAG;IAC7B,SAAS;KAAE,SAAS;KAAG,GAAG;KAAG;IAC7B,YAAY;KAAE,UAAU;KAAM,OAAO;KAAK;cAJ5C,CAMG,KACC,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;eAHZ,CAKE,kBAAC,GAAD;MAAW,WAAU;MAAS,eAAY;MAAS,CAAA,EAClD,EAAG,2CAA2C,YAAY,CACpD;QAEV,EACU;;GACF"}
1
+ {"version":3,"file":"ErrorState.js","names":[],"sources":["../../../src/components/shared/ErrorState.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { AlertTriangle, RefreshCw } from 'lucide-react';\nimport { motion, useReducedMotion } from 'framer-motion';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface ErrorStateProps {\n title?: string;\n message: string;\n onRetry?: () => void;\n action?: ReactNode;\n}\n\nexport function ErrorState({ title, message, onRetry, action }: ErrorStateProps) {\n const tr = useTr();\n const resolvedTitle = title ?? tr('vibecontrols.shared.errorState.title', 'Something went wrong');\n const shouldReduceMotion = useReducedMotion();\n\n if (shouldReduceMotion) {\n return (\n <div className=\"flex flex-col items-center justify-center py-12 px-4 text-center\">\n <div className=\"size-16 rounded-full bg-status-error-bg-subtle flex items-center justify-center mb-5\">\n <AlertTriangle className=\"size-8 text-status-error-text\" aria-hidden=\"true\" />\n </div>\n <h3 className=\"text-lg font-semibold text-text-primary mb-1.5 tracking-tight\">\n {resolvedTitle}\n </h3>\n <p className=\"text-sm text-text-secondary mb-5 max-w-md\">{message}</p>\n <div className=\"flex items-center gap-3\">\n {onRetry && (\n <button\n type=\"button\"\n onClick={onRetry}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-button hover:bg-action-primary-bgHover transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <RefreshCw className=\"size-4\" />\n {tr('vibecontrols.shared.errorState.tryAgain', 'Try Again')}\n </button>\n )}\n {action}\n </div>\n </div>\n );\n }\n\n return (\n <motion.div\n className=\"flex flex-col items-center justify-center py-12 px-4 text-center\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n transition={{ duration: 0.25 }}\n >\n <motion.div\n className=\"size-16 rounded-full bg-status-error-bg-subtle flex items-center justify-center mb-5\"\n initial={{ scale: 0.5, opacity: 0 }}\n animate={{ scale: 1, opacity: 1 }}\n transition={{\n type: 'spring',\n stiffness: 400,\n damping: 15,\n delay: 0.1,\n }}\n >\n <AlertTriangle className=\"size-8 text-status-error-text\" aria-hidden=\"true\" />\n </motion.div>\n <motion.h3\n className=\"text-lg font-semibold text-text-primary mb-1.5 tracking-tight\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ duration: 0.25, delay: 0.2 }}\n >\n {resolvedTitle}\n </motion.h3>\n <motion.p\n className=\"text-sm text-text-secondary mb-5 max-w-md\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ duration: 0.25, delay: 0.3 }}\n >\n {message}\n </motion.p>\n <motion.div\n className=\"flex items-center gap-3\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ duration: 0.25, delay: 0.4 }}\n >\n {onRetry && (\n <button\n type=\"button\"\n onClick={onRetry}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-button hover:bg-action-primary-bgHover active:scale-[0.98] transition-[colors,transform] duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <RefreshCw className=\"size-4\" aria-hidden=\"true\" />\n {tr('vibecontrols.shared.errorState.tryAgain', 'Try Again')}\n </button>\n )}\n {action}\n </motion.div>\n </motion.div>\n );\n}\n"],"mappings":";;;;;AAYA,SAAgB,EAAW,EAAE,UAAO,YAAS,YAAS,aAA2B;CAC/E,IAAM,IAAK,GAAO,EACZ,IAAgB,KAAS,EAAG,wCAAwC,uBAAuB;AA8BjG,QA7B2B,GAAkB,GAIzC,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KAAe,WAAU;KAAgC,eAAY;KAAS,CAAA;IAC1E,CAAA;GACN,kBAAC,MAAD;IAAI,WAAU;cACX;IACE,CAAA;GACL,kBAAC,KAAD;IAAG,WAAU;cAA6C;IAAY,CAAA;GACtE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,KACC,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;eAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA,EAC/B,EAAG,2CAA2C,YAAY,CACpD;QAEV,EACG;;GACF;MAKR,kBAAC,EAAO,KAAR;EACE,WAAU;EACV,SAAS,EAAE,SAAS,GAAG;EACvB,SAAS,EAAE,SAAS,GAAG;EACvB,YAAY,EAAE,UAAU,KAAM;YAJhC;GAME,kBAAC,EAAO,KAAR;IACE,WAAU;IACV,SAAS;KAAE,OAAO;KAAK,SAAS;KAAG;IACnC,SAAS;KAAE,OAAO;KAAG,SAAS;KAAG;IACjC,YAAY;KACV,MAAM;KACN,WAAW;KACX,SAAS;KACT,OAAO;KACR;cAED,kBAAC,GAAD;KAAe,WAAU;KAAgC,eAAY;KAAS,CAAA;IACnE,CAAA;GACb,kBAAC,EAAO,IAAR;IACE,WAAU;IACV,SAAS;KAAE,SAAS;KAAG,GAAG;KAAG;IAC7B,SAAS;KAAE,SAAS;KAAG,GAAG;KAAG;IAC7B,YAAY;KAAE,UAAU;KAAM,OAAO;KAAK;cAEzC;IACS,CAAA;GACZ,kBAAC,EAAO,GAAR;IACE,WAAU;IACV,SAAS;KAAE,SAAS;KAAG,GAAG;KAAG;IAC7B,SAAS;KAAE,SAAS;KAAG,GAAG;KAAG;IAC7B,YAAY;KAAE,UAAU;KAAM,OAAO;KAAK;cAEzC;IACQ,CAAA;GACX,kBAAC,EAAO,KAAR;IACE,WAAU;IACV,SAAS;KAAE,SAAS;KAAG,GAAG;KAAG;IAC7B,SAAS;KAAE,SAAS;KAAG,GAAG;KAAG;IAC7B,YAAY;KAAE,UAAU;KAAM,OAAO;KAAK;cAJ5C,CAMG,KACC,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;eAHZ,CAKE,kBAAC,GAAD;MAAW,WAAU;MAAS,eAAY;MAAS,CAAA,EAClD,EAAG,2CAA2C,YAAY,CACpD;QAEV,EACU;;GACF"}
@@ -19,7 +19,7 @@ function u({ label: u, options: d, value: f, onChange: p, className: m = "", all
19
19
  children: [/* @__PURE__ */ l("button", {
20
20
  type: "button",
21
21
  onClick: () => b(!y),
22
- className: "inline-flex items-center gap-1.5 sm:gap-2 px-2.5 sm:px-3 py-1.5 sm:py-2 text-xs sm:text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary hover:bg-bg-elevated transition-colors whitespace-nowrap flex-shrink-0",
22
+ className: "inline-flex items-center gap-1.5 sm:gap-2 px-2.5 sm:px-3 py-1.5 sm:py-2 text-xs sm:text-sm bg-bg-sunken border border-border-subtle rounded-input text-text-primary hover:bg-bg-elevated hover:border-border-strong transition-all duration-200 whitespace-nowrap flex-shrink-0",
23
23
  children: [
24
24
  _,
25
25
  /* @__PURE__ */ l("span", {
@@ -57,7 +57,7 @@ function u({ label: u, options: d, value: f, onChange: p, className: m = "", all
57
57
  1
58
58
  ]
59
59
  },
60
- className: "absolute z-50 mt-1 w-48 bg-bg-surface border border-border-default rounded-md shadow-lg",
60
+ className: "absolute z-50 mt-1.5 w-48 bg-bg-elevated border border-border-subtle rounded-card shadow-elevation-3 overflow-hidden",
61
61
  children: /* @__PURE__ */ l("div", {
62
62
  className: "py-1",
63
63
  children: [/* @__PURE__ */ l("button", {
@@ -1 +1 @@
1
- {"version":3,"file":"FilterDropdown.js","names":[],"sources":["../../../src/components/shared/FilterDropdown.tsx"],"sourcesContent":["import { useState, useRef, useEffect } from 'react';\nimport { ChevronDown, Check } from 'lucide-react';\nimport { motion, AnimatePresence } from 'framer-motion';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface FilterOption {\n value: string;\n label: string;\n}\n\ninterface FilterDropdownProps {\n label: string;\n /**\n * Options to display after the sentinel \"All\" button.\n * Any option whose value equals `allValue` is automatically skipped — the\n * component renders its own sentinel entry at the top, so callers may\n * include or omit it without causing a duplicate.\n */\n options: FilterOption[];\n value: string | null;\n onChange: (value: string) => void;\n className?: string;\n /**\n * Value emitted by the sentinel \"All\" entry. Defaults to `'all'`.\n * Override (e.g. `'none'`) for controls where the reset state isn't \"all\".\n */\n allValue?: string;\n /** Label for the sentinel entry. Defaults to the shared \"All\" string. */\n allLabel?: string;\n /** Optional leading icon rendered before the label in the trigger. */\n icon?: React.ReactNode;\n}\n\nexport function FilterDropdown({\n label,\n options,\n value,\n onChange,\n className = '',\n allValue = 'all',\n allLabel,\n icon,\n}: FilterDropdownProps) {\n const tr = useTr();\n const [isOpen, setIsOpen] = useState(false);\n const dropdownRef = useRef<HTMLDivElement>(null);\n const sentinelLabel = allLabel ?? tr('vibecontrols.shared.filterDropdown.all', 'All');\n\n useEffect(() => {\n function handleClickOutside(event: MouseEvent) {\n if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\n setIsOpen(false);\n }\n }\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, []);\n\n const selectedOption = options.find((opt) => opt.value === value);\n\n return (\n <div className={`relative ${className}`} ref={dropdownRef}>\n <button\n type=\"button\"\n onClick={() => setIsOpen(!isOpen)}\n className=\"inline-flex items-center gap-1.5 sm:gap-2 px-2.5 sm:px-3 py-1.5 sm:py-2 text-xs sm:text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary hover:bg-bg-elevated transition-colors whitespace-nowrap flex-shrink-0\"\n >\n {icon}\n <span className=\"text-text-secondary\">{label}:</span>\n <span className=\"font-medium max-w-[80px] sm:max-w-none truncate\">\n {selectedOption?.label || sentinelLabel}\n </span>\n <ChevronDown\n className={`size-3.5 sm:w-4 sm:h-4 transition-transform flex-shrink-0 ${isOpen ? 'rotate-180' : ''}`}\n />\n </button>\n\n <AnimatePresence>\n {isOpen && (\n <motion.div\n initial={{ opacity: 0, y: -4, scale: 0.97 }}\n animate={{ opacity: 1, y: 0, scale: 1 }}\n exit={{ opacity: 0, y: -4, scale: 0.97 }}\n transition={{ duration: 0.15, ease: [0.4, 0, 0.2, 1] }}\n className=\"absolute z-50 mt-1 w-48 bg-bg-surface border border-border-default rounded-md shadow-lg\"\n >\n <div className=\"py-1\">\n <button\n type=\"button\"\n onClick={() => {\n onChange(allValue);\n setIsOpen(false);\n }}\n className=\"flex items-center justify-between w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n {sentinelLabel}\n {(value === null || value === allValue) && (\n <Check className=\"size-4 text-action-primary-bg\" />\n )}\n </button>\n {options\n .filter((opt) => opt.value !== allValue)\n .map((option) => (\n <button\n type=\"button\"\n key={option.value}\n onClick={() => {\n onChange(option.value);\n setIsOpen(false);\n }}\n className=\"flex items-center justify-between w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n {option.label}\n {value === option.value && <Check className=\"size-4 text-action-primary-bg\" />}\n </button>\n ))}\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n );\n}\n"],"mappings":";;;;;;AAiCA,SAAgB,EAAe,EAC7B,UACA,YACA,UACA,aACA,eAAY,IACZ,cAAW,OACX,aACA,WACsB;CACtB,IAAM,IAAK,GAAO,EACZ,CAAC,GAAQ,KAAa,EAAS,GAAM,EACrC,IAAc,EAAuB,KAAK,EAC1C,IAAgB,KAAY,EAAG,0CAA0C,MAAM;AAErF,SAAgB;EACd,SAAS,EAAmB,GAAmB;AAC7C,GAAI,EAAY,WAAW,CAAC,EAAY,QAAQ,SAAS,EAAM,OAAe,IAC5E,EAAU,GAAM;;AAIpB,SADA,SAAS,iBAAiB,aAAa,EAAmB,QAC7C,SAAS,oBAAoB,aAAa,EAAmB;IACzE,EAAE,CAAC;CAEN,IAAM,IAAiB,EAAQ,MAAM,MAAQ,EAAI,UAAU,EAAM;AAEjE,QACE,kBAAC,OAAD;EAAK,WAAW,YAAY;EAAa,KAAK;YAA9C,CACE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,EAAU,CAAC,EAAO;GACjC,WAAU;aAHZ;IAKG;IACD,kBAAC,QAAD;KAAM,WAAU;eAAhB,CAAuC,GAAM,IAAQ;;IACrD,kBAAC,QAAD;KAAM,WAAU;eACb,GAAgB,SAAS;KACrB,CAAA;IACP,kBAAC,GAAD,EACE,WAAW,6DAA6D,IAAS,eAAe,MAChG,CAAA;IACK;MAET,kBAAC,GAAD,EAAA,UACG,KACC,kBAAC,EAAO,KAAR;GACE,SAAS;IAAE,SAAS;IAAG,GAAG;IAAI,OAAO;IAAM;GAC3C,SAAS;IAAE,SAAS;IAAG,GAAG;IAAG,OAAO;IAAG;GACvC,MAAM;IAAE,SAAS;IAAG,GAAG;IAAI,OAAO;IAAM;GACxC,YAAY;IAAE,UAAU;IAAM,MAAM;KAAC;KAAK;KAAG;KAAK;KAAE;IAAE;GACtD,WAAU;aAEV,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe;AAEb,MADA,EAAS,EAAS,EAClB,EAAU,GAAM;;KAElB,WAAU;eANZ,CAQG,IACC,MAAU,QAAQ,MAAU,MAC5B,kBAAC,GAAD,EAAO,WAAU,iCAAkC,CAAA,CAE9C;QACR,EACE,QAAQ,MAAQ,EAAI,UAAU,EAAS,CACvC,KAAK,MACJ,kBAAC,UAAD;KACE,MAAK;KAEL,eAAe;AAEb,MADA,EAAS,EAAO,MAAM,EACtB,EAAU,GAAM;;KAElB,WAAU;eAPZ,CASG,EAAO,OACP,MAAU,EAAO,SAAS,kBAAC,GAAD,EAAO,WAAU,iCAAkC,CAAA,CACvE;OATF,EAAO,MASL,CACT,CACA;;GACK,CAAA,EAEC,CAAA,CACd"}
1
+ {"version":3,"file":"FilterDropdown.js","names":[],"sources":["../../../src/components/shared/FilterDropdown.tsx"],"sourcesContent":["import { useState, useRef, useEffect } from 'react';\nimport { ChevronDown, Check } from 'lucide-react';\nimport { motion, AnimatePresence } from 'framer-motion';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface FilterOption {\n value: string;\n label: string;\n}\n\ninterface FilterDropdownProps {\n label: string;\n /**\n * Options to display after the sentinel \"All\" button.\n * Any option whose value equals `allValue` is automatically skipped — the\n * component renders its own sentinel entry at the top, so callers may\n * include or omit it without causing a duplicate.\n */\n options: FilterOption[];\n value: string | null;\n onChange: (value: string) => void;\n className?: string;\n /**\n * Value emitted by the sentinel \"All\" entry. Defaults to `'all'`.\n * Override (e.g. `'none'`) for controls where the reset state isn't \"all\".\n */\n allValue?: string;\n /** Label for the sentinel entry. Defaults to the shared \"All\" string. */\n allLabel?: string;\n /** Optional leading icon rendered before the label in the trigger. */\n icon?: React.ReactNode;\n}\n\nexport function FilterDropdown({\n label,\n options,\n value,\n onChange,\n className = '',\n allValue = 'all',\n allLabel,\n icon,\n}: FilterDropdownProps) {\n const tr = useTr();\n const [isOpen, setIsOpen] = useState(false);\n const dropdownRef = useRef<HTMLDivElement>(null);\n const sentinelLabel = allLabel ?? tr('vibecontrols.shared.filterDropdown.all', 'All');\n\n useEffect(() => {\n function handleClickOutside(event: MouseEvent) {\n if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\n setIsOpen(false);\n }\n }\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, []);\n\n const selectedOption = options.find((opt) => opt.value === value);\n\n return (\n <div className={`relative ${className}`} ref={dropdownRef}>\n <button\n type=\"button\"\n onClick={() => setIsOpen(!isOpen)}\n className=\"inline-flex items-center gap-1.5 sm:gap-2 px-2.5 sm:px-3 py-1.5 sm:py-2 text-xs sm:text-sm bg-bg-sunken border border-border-subtle rounded-input text-text-primary hover:bg-bg-elevated hover:border-border-strong transition-all duration-200 whitespace-nowrap flex-shrink-0\"\n >\n {icon}\n <span className=\"text-text-secondary\">{label}:</span>\n <span className=\"font-medium max-w-[80px] sm:max-w-none truncate\">\n {selectedOption?.label || sentinelLabel}\n </span>\n <ChevronDown\n className={`size-3.5 sm:w-4 sm:h-4 transition-transform flex-shrink-0 ${isOpen ? 'rotate-180' : ''}`}\n />\n </button>\n\n <AnimatePresence>\n {isOpen && (\n <motion.div\n initial={{ opacity: 0, y: -4, scale: 0.97 }}\n animate={{ opacity: 1, y: 0, scale: 1 }}\n exit={{ opacity: 0, y: -4, scale: 0.97 }}\n transition={{ duration: 0.15, ease: [0.4, 0, 0.2, 1] }}\n className=\"absolute z-50 mt-1.5 w-48 bg-bg-elevated border border-border-subtle rounded-card shadow-elevation-3 overflow-hidden\"\n >\n <div className=\"py-1\">\n <button\n type=\"button\"\n onClick={() => {\n onChange(allValue);\n setIsOpen(false);\n }}\n className=\"flex items-center justify-between w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n {sentinelLabel}\n {(value === null || value === allValue) && (\n <Check className=\"size-4 text-action-primary-bg\" />\n )}\n </button>\n {options\n .filter((opt) => opt.value !== allValue)\n .map((option) => (\n <button\n type=\"button\"\n key={option.value}\n onClick={() => {\n onChange(option.value);\n setIsOpen(false);\n }}\n className=\"flex items-center justify-between w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n {option.label}\n {value === option.value && <Check className=\"size-4 text-action-primary-bg\" />}\n </button>\n ))}\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n );\n}\n"],"mappings":";;;;;;AAiCA,SAAgB,EAAe,EAC7B,UACA,YACA,UACA,aACA,eAAY,IACZ,cAAW,OACX,aACA,WACsB;CACtB,IAAM,IAAK,GAAO,EACZ,CAAC,GAAQ,KAAa,EAAS,GAAM,EACrC,IAAc,EAAuB,KAAK,EAC1C,IAAgB,KAAY,EAAG,0CAA0C,MAAM;AAErF,SAAgB;EACd,SAAS,EAAmB,GAAmB;AAC7C,GAAI,EAAY,WAAW,CAAC,EAAY,QAAQ,SAAS,EAAM,OAAe,IAC5E,EAAU,GAAM;;AAIpB,SADA,SAAS,iBAAiB,aAAa,EAAmB,QAC7C,SAAS,oBAAoB,aAAa,EAAmB;IACzE,EAAE,CAAC;CAEN,IAAM,IAAiB,EAAQ,MAAM,MAAQ,EAAI,UAAU,EAAM;AAEjE,QACE,kBAAC,OAAD;EAAK,WAAW,YAAY;EAAa,KAAK;YAA9C,CACE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,EAAU,CAAC,EAAO;GACjC,WAAU;aAHZ;IAKG;IACD,kBAAC,QAAD;KAAM,WAAU;eAAhB,CAAuC,GAAM,IAAQ;;IACrD,kBAAC,QAAD;KAAM,WAAU;eACb,GAAgB,SAAS;KACrB,CAAA;IACP,kBAAC,GAAD,EACE,WAAW,6DAA6D,IAAS,eAAe,MAChG,CAAA;IACK;MAET,kBAAC,GAAD,EAAA,UACG,KACC,kBAAC,EAAO,KAAR;GACE,SAAS;IAAE,SAAS;IAAG,GAAG;IAAI,OAAO;IAAM;GAC3C,SAAS;IAAE,SAAS;IAAG,GAAG;IAAG,OAAO;IAAG;GACvC,MAAM;IAAE,SAAS;IAAG,GAAG;IAAI,OAAO;IAAM;GACxC,YAAY;IAAE,UAAU;IAAM,MAAM;KAAC;KAAK;KAAG;KAAK;KAAE;IAAE;GACtD,WAAU;aAEV,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe;AAEb,MADA,EAAS,EAAS,EAClB,EAAU,GAAM;;KAElB,WAAU;eANZ,CAQG,IACC,MAAU,QAAQ,MAAU,MAC5B,kBAAC,GAAD,EAAO,WAAU,iCAAkC,CAAA,CAE9C;QACR,EACE,QAAQ,MAAQ,EAAI,UAAU,EAAS,CACvC,KAAK,MACJ,kBAAC,UAAD;KACE,MAAK;KAEL,eAAe;AAEb,MADA,EAAS,EAAO,MAAM,EACtB,EAAU,GAAM;;KAElB,WAAU;eAPZ,CASG,EAAO,OACP,MAAU,EAAO,SAAS,kBAAC,GAAD,EAAO,WAAU,iCAAkC,CAAA,CACvE;OATF,EAAO,MASL,CACT,CACA;;GACK,CAAA,EAEC,CAAA,CACd"}
@@ -1,14 +1,14 @@
1
1
  import { jsx as e, jsxs as t } from "react/jsx-runtime";
2
2
  //#region src/components/shared/PageSkeleton.tsx
3
3
  function n({ width: t = "w-full", height: n = "h-4", className: r = "" }) {
4
- return /* @__PURE__ */ e("div", { className: `animate-pulse rounded bg-muted ${t} ${n} ${r}` });
4
+ return /* @__PURE__ */ e("div", { className: `animate-pulse rounded bg-bg-sunken ${t} ${n} ${r}` });
5
5
  }
6
6
  function r({ count: r = 6, rowClassName: i = "" }) {
7
7
  return /* @__PURE__ */ e("div", {
8
- className: "space-y-2",
8
+ className: "space-y-3",
9
9
  "aria-hidden": "true",
10
10
  children: Array.from({ length: r }).map((r, a) => /* @__PURE__ */ t("div", {
11
- className: `flex items-center gap-3 rounded-md border border-border-subtle bg-bg-surface p-3 ${i}`,
11
+ className: `flex items-center gap-3 rounded-card border border-border-subtle bg-bg-surface shadow-elevation-1 p-4 ${i}`,
12
12
  children: [
13
13
  /* @__PURE__ */ e(n, {
14
14
  width: "w-8",
@@ -32,10 +32,10 @@ function r({ count: r = 6, rowClassName: i = "" }) {
32
32
  }
33
33
  function i({ count: r = 6, columns: i = 3 }) {
34
34
  return /* @__PURE__ */ e("div", {
35
- className: `grid grid-cols-1 gap-3 ${i === 4 ? "sm:grid-cols-2 lg:grid-cols-4" : i === 2 ? "sm:grid-cols-2" : "sm:grid-cols-2 lg:grid-cols-3"}`,
35
+ className: `grid grid-cols-1 gap-4 ${i === 4 ? "sm:grid-cols-2 lg:grid-cols-4" : i === 2 ? "sm:grid-cols-2" : "sm:grid-cols-2 lg:grid-cols-3"}`,
36
36
  "aria-hidden": "true",
37
37
  children: Array.from({ length: r }).map((r, i) => /* @__PURE__ */ t("div", {
38
- className: "rounded-md border border-border-subtle bg-bg-surface p-4 space-y-3",
38
+ className: "rounded-card border border-border-subtle bg-bg-surface shadow-elevation-1 p-5 space-y-3",
39
39
  children: [
40
40
  /* @__PURE__ */ e(n, {
41
41
  width: "w-2/3",
@@ -55,7 +55,7 @@ function i({ count: r = 6, columns: i = 3 }) {
55
55
  }
56
56
  function a({ header: a, variant: o = "rows", count: s = 6 }) {
57
57
  return /* @__PURE__ */ t("div", {
58
- className: "space-y-4",
58
+ className: "space-y-6",
59
59
  role: "status",
60
60
  "aria-label": "Loading content",
61
61
  children: [a, o === "cards" ? /* @__PURE__ */ e(i, { count: s }) : o === "detail" ? /* @__PURE__ */ t("div", {
@@ -1 +1 @@
1
- {"version":3,"file":"PageSkeleton.js","names":[],"sources":["../../../src/components/shared/PageSkeleton.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\n\ninterface SkeletonLineProps {\n width?: string;\n height?: string;\n className?: string;\n}\n\nexport function SkeletonLine({\n width = 'w-full',\n height = 'h-4',\n className = '',\n}: SkeletonLineProps) {\n return <div className={`animate-pulse rounded bg-muted ${width} ${height} ${className}`} />;\n}\n\ninterface SkeletonRowsProps {\n count?: number;\n rowClassName?: string;\n}\n\nexport function SkeletonRows({ count = 6, rowClassName = '' }: SkeletonRowsProps) {\n return (\n <div className=\"space-y-2\" aria-hidden=\"true\">\n {Array.from({ length: count }).map((_, i) => (\n <div\n key={i}\n className={`flex items-center gap-3 rounded-md border border-border-subtle bg-bg-surface p-3 ${rowClassName}`}\n >\n <SkeletonLine width=\"w-8\" height=\"h-8\" className=\"rounded-full\" />\n <div className=\"flex-1 space-y-2\">\n <SkeletonLine width=\"w-1/3\" />\n <SkeletonLine width=\"w-1/2\" height=\"h-3\" />\n </div>\n <SkeletonLine width=\"w-16\" height=\"h-6\" />\n </div>\n ))}\n </div>\n );\n}\n\ninterface SkeletonCardsProps {\n count?: number;\n columns?: number;\n}\n\nexport function SkeletonCards({ count = 6, columns = 3 }: SkeletonCardsProps) {\n const gridCols =\n columns === 4\n ? 'sm:grid-cols-2 lg:grid-cols-4'\n : columns === 2\n ? 'sm:grid-cols-2'\n : 'sm:grid-cols-2 lg:grid-cols-3';\n return (\n <div className={`grid grid-cols-1 gap-3 ${gridCols}`} aria-hidden=\"true\">\n {Array.from({ length: count }).map((_, i) => (\n <div key={i} className=\"rounded-md border border-border-subtle bg-bg-surface p-4 space-y-3\">\n <SkeletonLine width=\"w-2/3\" height=\"h-5\" />\n <SkeletonLine width=\"w-full\" height=\"h-3\" />\n <SkeletonLine width=\"w-1/2\" height=\"h-3\" />\n </div>\n ))}\n </div>\n );\n}\n\ninterface PageSkeletonProps {\n header?: ReactNode;\n variant?: 'rows' | 'cards' | 'detail';\n count?: number;\n}\n\nexport function PageSkeleton({ header, variant = 'rows', count = 6 }: PageSkeletonProps) {\n return (\n <div className=\"space-y-4\" role=\"status\" aria-label=\"Loading content\">\n {header}\n {variant === 'cards' ? (\n <SkeletonCards count={count} />\n ) : variant === 'detail' ? (\n <div className=\"space-y-3\">\n <SkeletonLine width=\"w-1/3\" height=\"h-6\" />\n <SkeletonLine width=\"w-2/3\" height=\"h-3\" />\n <SkeletonLine width=\"w-full\" height=\"h-3\" />\n <SkeletonLine width=\"w-3/4\" height=\"h-3\" />\n <SkeletonRows count={Math.max(count - 2, 3)} />\n </div>\n ) : (\n <SkeletonRows count={count} />\n )}\n </div>\n );\n}\n"],"mappings":";;AAQA,SAAgB,EAAa,EAC3B,WAAQ,UACR,YAAS,OACT,eAAY,MACQ;AACpB,QAAO,kBAAC,OAAD,EAAK,WAAW,kCAAkC,EAAM,GAAG,EAAO,GAAG,KAAe,CAAA;;AAQ7F,SAAgB,EAAa,EAAE,WAAQ,GAAG,kBAAe,MAAyB;AAChF,QACE,kBAAC,OAAD;EAAK,WAAU;EAAY,eAAY;YACpC,MAAM,KAAK,EAAE,QAAQ,GAAO,CAAC,CAAC,KAAK,GAAG,MACrC,kBAAC,OAAD;GAEE,WAAW,oFAAoF;aAFjG;IAIE,kBAAC,GAAD;KAAc,OAAM;KAAM,QAAO;KAAM,WAAU;KAAiB,CAAA;IAClE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAc,OAAM,SAAU,CAAA,EAC9B,kBAAC,GAAD;MAAc,OAAM;MAAQ,QAAO;MAAQ,CAAA,CACvC;;IACN,kBAAC,GAAD;KAAc,OAAM;KAAO,QAAO;KAAQ,CAAA;IACtC;KATC,EASD,CACN;EACE,CAAA;;AASV,SAAgB,EAAc,EAAE,WAAQ,GAAG,aAAU,KAAyB;AAO5E,QACE,kBAAC,OAAD;EAAK,WAAW,0BANhB,MAAY,IACR,kCACA,MAAY,IACV,mBACA;EAEgD,eAAY;YAC/D,MAAM,KAAK,EAAE,QAAQ,GAAO,CAAC,CAAC,KAAK,GAAG,MACrC,kBAAC,OAAD;GAAa,WAAU;aAAvB;IACE,kBAAC,GAAD;KAAc,OAAM;KAAQ,QAAO;KAAQ,CAAA;IAC3C,kBAAC,GAAD;KAAc,OAAM;KAAS,QAAO;KAAQ,CAAA;IAC5C,kBAAC,GAAD;KAAc,OAAM;KAAQ,QAAO;KAAQ,CAAA;IACvC;KAJI,EAIJ,CACN;EACE,CAAA;;AAUV,SAAgB,EAAa,EAAE,WAAQ,aAAU,QAAQ,WAAQ,KAAwB;AACvF,QACE,kBAAC,OAAD;EAAK,WAAU;EAAY,MAAK;EAAS,cAAW;YAApD,CACG,GACA,MAAY,UACX,kBAAC,GAAD,EAAsB,UAAS,CAAA,GAC7B,MAAY,WACd,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD;KAAc,OAAM;KAAQ,QAAO;KAAQ,CAAA;IAC3C,kBAAC,GAAD;KAAc,OAAM;KAAQ,QAAO;KAAQ,CAAA;IAC3C,kBAAC,GAAD;KAAc,OAAM;KAAS,QAAO;KAAQ,CAAA;IAC5C,kBAAC,GAAD;KAAc,OAAM;KAAQ,QAAO;KAAQ,CAAA;IAC3C,kBAAC,GAAD,EAAc,OAAO,KAAK,IAAI,IAAQ,GAAG,EAAE,EAAI,CAAA;IAC3C;OAEN,kBAAC,GAAD,EAAqB,UAAS,CAAA,CAE5B"}
1
+ {"version":3,"file":"PageSkeleton.js","names":[],"sources":["../../../src/components/shared/PageSkeleton.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\n\ninterface SkeletonLineProps {\n width?: string;\n height?: string;\n className?: string;\n}\n\nexport function SkeletonLine({\n width = 'w-full',\n height = 'h-4',\n className = '',\n}: SkeletonLineProps) {\n return <div className={`animate-pulse rounded bg-bg-sunken ${width} ${height} ${className}`} />;\n}\n\ninterface SkeletonRowsProps {\n count?: number;\n rowClassName?: string;\n}\n\nexport function SkeletonRows({ count = 6, rowClassName = '' }: SkeletonRowsProps) {\n return (\n <div className=\"space-y-3\" aria-hidden=\"true\">\n {Array.from({ length: count }).map((_, i) => (\n <div\n key={i}\n className={`flex items-center gap-3 rounded-card border border-border-subtle bg-bg-surface shadow-elevation-1 p-4 ${rowClassName}`}\n >\n <SkeletonLine width=\"w-8\" height=\"h-8\" className=\"rounded-full\" />\n <div className=\"flex-1 space-y-2\">\n <SkeletonLine width=\"w-1/3\" />\n <SkeletonLine width=\"w-1/2\" height=\"h-3\" />\n </div>\n <SkeletonLine width=\"w-16\" height=\"h-6\" />\n </div>\n ))}\n </div>\n );\n}\n\ninterface SkeletonCardsProps {\n count?: number;\n columns?: number;\n}\n\nexport function SkeletonCards({ count = 6, columns = 3 }: SkeletonCardsProps) {\n const gridCols =\n columns === 4\n ? 'sm:grid-cols-2 lg:grid-cols-4'\n : columns === 2\n ? 'sm:grid-cols-2'\n : 'sm:grid-cols-2 lg:grid-cols-3';\n return (\n <div className={`grid grid-cols-1 gap-4 ${gridCols}`} aria-hidden=\"true\">\n {Array.from({ length: count }).map((_, i) => (\n <div\n key={i}\n className=\"rounded-card border border-border-subtle bg-bg-surface shadow-elevation-1 p-5 space-y-3\"\n >\n <SkeletonLine width=\"w-2/3\" height=\"h-5\" />\n <SkeletonLine width=\"w-full\" height=\"h-3\" />\n <SkeletonLine width=\"w-1/2\" height=\"h-3\" />\n </div>\n ))}\n </div>\n );\n}\n\ninterface PageSkeletonProps {\n header?: ReactNode;\n variant?: 'rows' | 'cards' | 'detail';\n count?: number;\n}\n\nexport function PageSkeleton({ header, variant = 'rows', count = 6 }: PageSkeletonProps) {\n return (\n <div className=\"space-y-6\" role=\"status\" aria-label=\"Loading content\">\n {header}\n {variant === 'cards' ? (\n <SkeletonCards count={count} />\n ) : variant === 'detail' ? (\n <div className=\"space-y-3\">\n <SkeletonLine width=\"w-1/3\" height=\"h-6\" />\n <SkeletonLine width=\"w-2/3\" height=\"h-3\" />\n <SkeletonLine width=\"w-full\" height=\"h-3\" />\n <SkeletonLine width=\"w-3/4\" height=\"h-3\" />\n <SkeletonRows count={Math.max(count - 2, 3)} />\n </div>\n ) : (\n <SkeletonRows count={count} />\n )}\n </div>\n );\n}\n"],"mappings":";;AAQA,SAAgB,EAAa,EAC3B,WAAQ,UACR,YAAS,OACT,eAAY,MACQ;AACpB,QAAO,kBAAC,OAAD,EAAK,WAAW,sCAAsC,EAAM,GAAG,EAAO,GAAG,KAAe,CAAA;;AAQjG,SAAgB,EAAa,EAAE,WAAQ,GAAG,kBAAe,MAAyB;AAChF,QACE,kBAAC,OAAD;EAAK,WAAU;EAAY,eAAY;YACpC,MAAM,KAAK,EAAE,QAAQ,GAAO,CAAC,CAAC,KAAK,GAAG,MACrC,kBAAC,OAAD;GAEE,WAAW,yGAAyG;aAFtH;IAIE,kBAAC,GAAD;KAAc,OAAM;KAAM,QAAO;KAAM,WAAU;KAAiB,CAAA;IAClE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAc,OAAM,SAAU,CAAA,EAC9B,kBAAC,GAAD;MAAc,OAAM;MAAQ,QAAO;MAAQ,CAAA,CACvC;;IACN,kBAAC,GAAD;KAAc,OAAM;KAAO,QAAO;KAAQ,CAAA;IACtC;KATC,EASD,CACN;EACE,CAAA;;AASV,SAAgB,EAAc,EAAE,WAAQ,GAAG,aAAU,KAAyB;AAO5E,QACE,kBAAC,OAAD;EAAK,WAAW,0BANhB,MAAY,IACR,kCACA,MAAY,IACV,mBACA;EAEgD,eAAY;YAC/D,MAAM,KAAK,EAAE,QAAQ,GAAO,CAAC,CAAC,KAAK,GAAG,MACrC,kBAAC,OAAD;GAEE,WAAU;aAFZ;IAIE,kBAAC,GAAD;KAAc,OAAM;KAAQ,QAAO;KAAQ,CAAA;IAC3C,kBAAC,GAAD;KAAc,OAAM;KAAS,QAAO;KAAQ,CAAA;IAC5C,kBAAC,GAAD;KAAc,OAAM;KAAQ,QAAO;KAAQ,CAAA;IACvC;KANC,EAMD,CACN;EACE,CAAA;;AAUV,SAAgB,EAAa,EAAE,WAAQ,aAAU,QAAQ,WAAQ,KAAwB;AACvF,QACE,kBAAC,OAAD;EAAK,WAAU;EAAY,MAAK;EAAS,cAAW;YAApD,CACG,GACA,MAAY,UACX,kBAAC,GAAD,EAAsB,UAAS,CAAA,GAC7B,MAAY,WACd,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD;KAAc,OAAM;KAAQ,QAAO;KAAQ,CAAA;IAC3C,kBAAC,GAAD;KAAc,OAAM;KAAQ,QAAO;KAAQ,CAAA;IAC3C,kBAAC,GAAD;KAAc,OAAM;KAAS,QAAO;KAAQ,CAAA;IAC5C,kBAAC,GAAD;KAAc,OAAM;KAAQ,QAAO;KAAQ,CAAA;IAC3C,kBAAC,GAAD,EAAc,OAAO,KAAK,IAAI,IAAQ,GAAG,EAAE,EAAI,CAAA;IAC3C;OAEN,kBAAC,GAAD,EAAqB,UAAS,CAAA,CAE5B"}
@@ -17,7 +17,7 @@ function c({ value: c, onChange: l, placeholder: u, className: d = "" }) {
17
17
  value: c,
18
18
  onChange: (e) => l(e.target.value),
19
19
  placeholder: p,
20
- className: "w-full pl-10 pr-10 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg focus:border-transparent transition-all"
20
+ className: "w-full pl-10 pr-10 py-2 text-sm bg-bg-sunken border border-border-subtle rounded-input text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-1 focus:ring-[var(--color-focus-ring)] focus:border-border-strong transition-all duration-200"
21
21
  }),
22
22
  /* @__PURE__ */ o(i, { children: c && /* @__PURE__ */ o(a.button, {
23
23
  initial: {
@@ -1 +1 @@
1
- {"version":3,"file":"SearchInput.js","names":[],"sources":["../../../src/components/shared/SearchInput.tsx"],"sourcesContent":["import { Search, X } from 'lucide-react';\nimport { useRef } from 'react';\nimport { motion, AnimatePresence } from 'framer-motion';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface SearchInputProps {\n value: string;\n onChange: (value: string) => void;\n placeholder?: string;\n className?: string;\n}\n\nexport function SearchInput({ value, onChange, placeholder, className = '' }: SearchInputProps) {\n const tr = useTr();\n const resolvedPlaceholder =\n placeholder ?? tr('vibecontrols.shared.searchInput.placeholder', 'Search...');\n const inputRef = useRef<HTMLInputElement>(null);\n\n const handleClear = () => {\n onChange('');\n inputRef.current?.focus();\n };\n\n return (\n <div className={`relative ${className}`}>\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 size-4 text-text-secondary\" />\n <input\n aria-label=\"control\"\n ref={inputRef}\n type=\"text\"\n value={value}\n onChange={(e) => onChange(e.target.value)}\n placeholder={resolvedPlaceholder}\n className=\"w-full pl-10 pr-10 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg focus:border-transparent transition-all\"\n />\n <AnimatePresence>\n {value && (\n <motion.button\n initial={{ opacity: 0, scale: 0.8 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.8 }}\n transition={{ duration: 0.15 }}\n onClick={handleClear}\n className=\"absolute right-3 top-1/2 -translate-y-1/2 p-0.5 rounded hover:bg-bg-elevated transition-colors\"\n >\n <X className=\"size-4 text-text-secondary\" />\n </motion.button>\n )}\n </AnimatePresence>\n </div>\n );\n}\n"],"mappings":";;;;;;AAYA,SAAgB,EAAY,EAAE,UAAO,aAAU,gBAAa,eAAY,MAAwB;CAC9F,IAAM,IAAK,GAAO,EACZ,IACJ,KAAe,EAAG,+CAA+C,YAAY,EACzE,IAAW,EAAyB,KAAK;AAO/C,QACE,kBAAC,OAAD;EAAK,WAAW,YAAY;YAA5B;GACE,kBAAC,GAAD,EAAQ,WAAU,uEAAwE,CAAA;GAC1F,kBAAC,SAAD;IACE,cAAW;IACX,KAAK;IACL,MAAK;IACE;IACP,WAAW,MAAM,EAAS,EAAE,OAAO,MAAM;IACzC,aAAa;IACb,WAAU;IACV,CAAA;GACF,kBAAC,GAAD,EAAA,UACG,KACC,kBAAC,EAAO,QAAR;IACE,SAAS;KAAE,SAAS;KAAG,OAAO;KAAK;IACnC,SAAS;KAAE,SAAS;KAAG,OAAO;KAAG;IACjC,MAAM;KAAE,SAAS;KAAG,OAAO;KAAK;IAChC,YAAY,EAAE,UAAU,KAAM;IAC9B,eAxBgB;AAExB,KADA,EAAS,GAAG,EACZ,EAAS,SAAS,OAAO;;IAuBjB,WAAU;cAEV,kBAAC,GAAD,EAAG,WAAU,8BAA+B,CAAA;IAC9B,CAAA,EAEF,CAAA;GACd"}
1
+ {"version":3,"file":"SearchInput.js","names":[],"sources":["../../../src/components/shared/SearchInput.tsx"],"sourcesContent":["import { Search, X } from 'lucide-react';\nimport { useRef } from 'react';\nimport { motion, AnimatePresence } from 'framer-motion';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface SearchInputProps {\n value: string;\n onChange: (value: string) => void;\n placeholder?: string;\n className?: string;\n}\n\nexport function SearchInput({ value, onChange, placeholder, className = '' }: SearchInputProps) {\n const tr = useTr();\n const resolvedPlaceholder =\n placeholder ?? tr('vibecontrols.shared.searchInput.placeholder', 'Search...');\n const inputRef = useRef<HTMLInputElement>(null);\n\n const handleClear = () => {\n onChange('');\n inputRef.current?.focus();\n };\n\n return (\n <div className={`relative ${className}`}>\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 size-4 text-text-secondary\" />\n <input\n aria-label=\"control\"\n ref={inputRef}\n type=\"text\"\n value={value}\n onChange={(e) => onChange(e.target.value)}\n placeholder={resolvedPlaceholder}\n className=\"w-full pl-10 pr-10 py-2 text-sm bg-bg-sunken border border-border-subtle rounded-input text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-1 focus:ring-[var(--color-focus-ring)] focus:border-border-strong transition-all duration-200\"\n />\n <AnimatePresence>\n {value && (\n <motion.button\n initial={{ opacity: 0, scale: 0.8 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.8 }}\n transition={{ duration: 0.15 }}\n onClick={handleClear}\n className=\"absolute right-3 top-1/2 -translate-y-1/2 p-0.5 rounded hover:bg-bg-elevated transition-colors\"\n >\n <X className=\"size-4 text-text-secondary\" />\n </motion.button>\n )}\n </AnimatePresence>\n </div>\n );\n}\n"],"mappings":";;;;;;AAYA,SAAgB,EAAY,EAAE,UAAO,aAAU,gBAAa,eAAY,MAAwB;CAC9F,IAAM,IAAK,GAAO,EACZ,IACJ,KAAe,EAAG,+CAA+C,YAAY,EACzE,IAAW,EAAyB,KAAK;AAO/C,QACE,kBAAC,OAAD;EAAK,WAAW,YAAY;YAA5B;GACE,kBAAC,GAAD,EAAQ,WAAU,uEAAwE,CAAA;GAC1F,kBAAC,SAAD;IACE,cAAW;IACX,KAAK;IACL,MAAK;IACE;IACP,WAAW,MAAM,EAAS,EAAE,OAAO,MAAM;IACzC,aAAa;IACb,WAAU;IACV,CAAA;GACF,kBAAC,GAAD,EAAA,UACG,KACC,kBAAC,EAAO,QAAR;IACE,SAAS;KAAE,SAAS;KAAG,OAAO;KAAK;IACnC,SAAS;KAAE,SAAS;KAAG,OAAO;KAAG;IACjC,MAAM;KAAE,SAAS;KAAG,OAAO;KAAK;IAChC,YAAY,EAAE,UAAU,KAAM;IAC9B,eAxBgB;AAExB,KADA,EAAS,GAAG,EACZ,EAAS,SAAS,OAAO;;IAuBjB,WAAU;cAEV,kBAAC,GAAD,EAAG,WAAU,8BAA+B,CAAA;IAC9B,CAAA,EAEF,CAAA;GACd"}
@@ -5,14 +5,14 @@ import { AnimatedCounter as a } from "@burdenoff/fe-libs/shared/motion";
5
5
  function o({ title: o, value: s, subtitle: c, icon: l, trend: u, variant: d = "default", onClick: f }) {
6
6
  return /* @__PURE__ */ r(f ? "button" : "div", {
7
7
  onClick: f,
8
- className: `p-4 rounded-card border border-border-default ${{
8
+ className: `p-5 rounded-card border border-border-subtle shadow-elevation-1 ${{
9
9
  default: "bg-bg-surface",
10
10
  success: "bg-status-success-bg-subtle",
11
11
  warning: "bg-status-warning-bg-subtle",
12
12
  error: "bg-status-error-bg-subtle"
13
- }[d]} ${f ? "hover:border-border-strong hover:shadow-[var(--shadow-elevation-1)] transition-[colors,box-shadow] duration-150 cursor-pointer w-full text-left" : ""}`,
13
+ }[d]} ${f ? "hover:border-border-strong hover:shadow-elevation-2 hover:-translate-y-0.5 transition-all duration-200 ease-[var(--motion-easing-out)] cursor-pointer w-full text-left" : ""}`,
14
14
  children: /* @__PURE__ */ i("div", {
15
- className: "flex flex-col gap-2",
15
+ className: "flex flex-col gap-2.5",
16
16
  children: [
17
17
  /* @__PURE__ */ i("div", {
18
18
  className: "flex items-center justify-between",
@@ -20,8 +20,8 @@ function o({ title: o, value: s, subtitle: c, icon: l, trend: u, variant: d = "d
20
20
  className: "text-sm font-medium text-text-secondary",
21
21
  children: o
22
22
  }), l && /* @__PURE__ */ r("div", {
23
- className: `flex-shrink-0 size-8 rounded-lg flex items-center justify-center ${{
24
- default: "bg-bg-sunken text-text-secondary",
23
+ className: `flex-shrink-0 size-9 rounded-lg flex items-center justify-center ${{
24
+ default: "bg-[var(--color-accent-soft)] text-text-primary",
25
25
  success: "bg-status-success-bg-subtle text-status-success-text",
26
26
  warning: "bg-status-warning-bg-subtle text-status-warning-text",
27
27
  error: "bg-status-error-bg-subtle text-status-error-text"
@@ -30,7 +30,7 @@ function o({ title: o, value: s, subtitle: c, icon: l, trend: u, variant: d = "d
30
30
  })]
31
31
  }),
32
32
  /* @__PURE__ */ r("div", {
33
- className: "text-2xl font-bold text-text-primary",
33
+ className: "text-2xl font-semibold text-text-primary tabular-nums",
34
34
  children: typeof s == "number" ? /* @__PURE__ */ r(a, { value: s }) : s
35
35
  }),
36
36
  c && /* @__PURE__ */ r("p", {
@@ -42,7 +42,7 @@ function o({ title: o, value: s, subtitle: c, icon: l, trend: u, variant: d = "d
42
42
  children: [
43
43
  u.value > 0 ? /* @__PURE__ */ r(n, { className: "size-4 text-status-success-text" }) : u.value < 0 ? /* @__PURE__ */ r(t, { className: "size-4 text-status-error-text" }) : /* @__PURE__ */ r(e, { className: "size-4 text-text-secondary" }),
44
44
  /* @__PURE__ */ i("span", {
45
- className: `text-xs font-medium ${u.value > 0 ? "text-status-success-text" : u.value < 0 ? "text-status-error-text" : "text-text-secondary"}`,
45
+ className: `text-xs font-medium tabular-nums ${u.value > 0 ? "text-status-success-text" : u.value < 0 ? "text-status-error-text" : "text-text-secondary"}`,
46
46
  children: [
47
47
  u.value > 0 ? "+" : "",
48
48
  u.value,
@@ -1 +1 @@
1
- {"version":3,"file":"StatCard.js","names":[],"sources":["../../../src/components/shared/StatCard.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { TrendingUp, TrendingDown, Minus } from 'lucide-react';\nimport { AnimatedCounter } from '@burdenoff/fe-libs/shared/motion';\n\ninterface StatCardProps {\n title: string;\n value: string | number;\n subtitle?: string;\n icon?: ReactNode;\n trend?: {\n value: number;\n label?: string;\n };\n variant?: 'default' | 'success' | 'warning' | 'error';\n onClick?: () => void;\n}\n\nexport function StatCard({\n title,\n value,\n subtitle,\n icon,\n trend,\n variant = 'default',\n onClick,\n}: StatCardProps) {\n const variantStyles = {\n default: 'bg-bg-surface',\n success: 'bg-status-success-bg-subtle',\n warning: 'bg-status-warning-bg-subtle',\n error: 'bg-status-error-bg-subtle',\n };\n\n // Subtle tint + same-hue saturated text (passes AA in both themes); the old\n // solid `bg-status-X-bg` + `text-status-X-text` was same-hue low-contrast.\n const iconVariantStyles = {\n default: 'bg-bg-sunken text-text-secondary',\n success: 'bg-status-success-bg-subtle text-status-success-text',\n warning: 'bg-status-warning-bg-subtle text-status-warning-text',\n error: 'bg-status-error-bg-subtle text-status-error-text',\n };\n\n const Component = onClick ? 'button' : 'div';\n\n return (\n <Component\n onClick={onClick}\n className={`p-4 rounded-card border border-border-default ${variantStyles[variant]} ${onClick ? 'hover:border-border-strong hover:shadow-[var(--shadow-elevation-1)] transition-[colors,box-shadow] duration-150 cursor-pointer w-full text-left' : ''}`}\n >\n <div className=\"flex flex-col gap-2\">\n <div className=\"flex items-center justify-between\">\n <p className=\"text-sm font-medium text-text-secondary\">{title}</p>\n {icon && (\n <div\n className={`flex-shrink-0 size-8 rounded-lg flex items-center justify-center ${iconVariantStyles[variant]} [&>svg]:w-4 [&>svg]:h-4`}\n >\n {icon}\n </div>\n )}\n </div>\n <div className=\"text-2xl font-bold text-text-primary\">\n {typeof value === 'number' ? <AnimatedCounter value={value} /> : value}\n </div>\n {subtitle && <p className=\"text-xs text-text-secondary\">{subtitle}</p>}\n {trend && (\n <div className=\"flex items-center gap-1\">\n {trend.value > 0 ? (\n <TrendingUp className=\"size-4 text-status-success-text\" />\n ) : trend.value < 0 ? (\n <TrendingDown className=\"size-4 text-status-error-text\" />\n ) : (\n <Minus className=\"size-4 text-text-secondary\" />\n )}\n <span\n className={`text-xs font-medium ${\n trend.value > 0\n ? 'text-status-success-text'\n : trend.value < 0\n ? 'text-status-error-text'\n : 'text-text-secondary'\n }`}\n >\n {trend.value > 0 ? '+' : ''}\n {trend.value}%\n </span>\n {trend.label && <span className=\"text-xs text-text-secondary\">{trend.label}</span>}\n </div>\n )}\n </div>\n </Component>\n );\n}\n"],"mappings":";;;;AAiBA,SAAgB,EAAS,EACvB,UACA,UACA,aACA,SACA,UACA,aAAU,WACV,cACgB;AAmBhB,QACE,kBAHgB,IAAU,WAAW,OAGrC;EACW;EACT,WAAW,iDArBO;GACpB,SAAS;GACT,SAAS;GACT,SAAS;GACT,OAAO;GACR,CAgB6E,GAAS,GAAG,IAAU,oJAAoJ;YAEpP,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBAA2C;MAAU,CAAA,EACjE,KACC,kBAAC,OAAD;MACE,WAAW,oEAnBG;OACxB,SAAS;OACT,SAAS;OACT,SAAS;OACT,OAAO;OACR,CAc4G,GAAS;gBAEzG;MACG,CAAA,CAEJ;;IACN,kBAAC,OAAD;KAAK,WAAU;eACZ,OAAO,KAAU,WAAW,kBAAC,GAAD,EAAwB,UAAS,CAAA,GAAG;KAC7D,CAAA;IACL,KAAY,kBAAC,KAAD;KAAG,WAAU;eAA+B;KAAa,CAAA;IACrE,KACC,kBAAC,OAAD;KAAK,WAAU;eAAf;MACG,EAAM,QAAQ,IACb,kBAAC,GAAD,EAAY,WAAU,mCAAoC,CAAA,GACxD,EAAM,QAAQ,IAChB,kBAAC,GAAD,EAAc,WAAU,iCAAkC,CAAA,GAE1D,kBAAC,GAAD,EAAO,WAAU,8BAA+B,CAAA;MAElD,kBAAC,QAAD;OACE,WAAW,uBACT,EAAM,QAAQ,IACV,6BACA,EAAM,QAAQ,IACZ,2BACA;iBANV;QASG,EAAM,QAAQ,IAAI,MAAM;QACxB,EAAM;QAAM;QACR;;MACN,EAAM,SAAS,kBAAC,QAAD;OAAM,WAAU;iBAA+B,EAAM;OAAa,CAAA;MAC9E;;IAEJ;;EACI,CAAA"}
1
+ {"version":3,"file":"StatCard.js","names":[],"sources":["../../../src/components/shared/StatCard.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { TrendingUp, TrendingDown, Minus } from 'lucide-react';\nimport { AnimatedCounter } from '@burdenoff/fe-libs/shared/motion';\n\ninterface StatCardProps {\n title: string;\n value: string | number;\n subtitle?: string;\n icon?: ReactNode;\n trend?: {\n value: number;\n label?: string;\n };\n variant?: 'default' | 'success' | 'warning' | 'error';\n onClick?: () => void;\n}\n\nexport function StatCard({\n title,\n value,\n subtitle,\n icon,\n trend,\n variant = 'default',\n onClick,\n}: StatCardProps) {\n const variantStyles = {\n default: 'bg-bg-surface',\n success: 'bg-status-success-bg-subtle',\n warning: 'bg-status-warning-bg-subtle',\n error: 'bg-status-error-bg-subtle',\n };\n\n // Subtle tint + same-hue saturated text (passes AA in both themes); the old\n // solid `bg-status-X-bg` + `text-status-X-text` was same-hue low-contrast.\n // The neutral/default tile uses the brand accent wash so metric cards lead\n // with the theme accent rather than a flat grey.\n const iconVariantStyles = {\n default: 'bg-[var(--color-accent-soft)] text-text-primary',\n success: 'bg-status-success-bg-subtle text-status-success-text',\n warning: 'bg-status-warning-bg-subtle text-status-warning-text',\n error: 'bg-status-error-bg-subtle text-status-error-text',\n };\n\n const Component = onClick ? 'button' : 'div';\n\n return (\n <Component\n onClick={onClick}\n className={`p-5 rounded-card border border-border-subtle shadow-elevation-1 ${variantStyles[variant]} ${onClick ? 'hover:border-border-strong hover:shadow-elevation-2 hover:-translate-y-0.5 transition-all duration-200 ease-[var(--motion-easing-out)] cursor-pointer w-full text-left' : ''}`}\n >\n <div className=\"flex flex-col gap-2.5\">\n <div className=\"flex items-center justify-between\">\n <p className=\"text-sm font-medium text-text-secondary\">{title}</p>\n {icon && (\n <div\n className={`flex-shrink-0 size-9 rounded-lg flex items-center justify-center ${iconVariantStyles[variant]} [&>svg]:w-4 [&>svg]:h-4`}\n >\n {icon}\n </div>\n )}\n </div>\n <div className=\"text-2xl font-semibold text-text-primary tabular-nums\">\n {typeof value === 'number' ? <AnimatedCounter value={value} /> : value}\n </div>\n {subtitle && <p className=\"text-xs text-text-secondary\">{subtitle}</p>}\n {trend && (\n <div className=\"flex items-center gap-1\">\n {trend.value > 0 ? (\n <TrendingUp className=\"size-4 text-status-success-text\" />\n ) : trend.value < 0 ? (\n <TrendingDown className=\"size-4 text-status-error-text\" />\n ) : (\n <Minus className=\"size-4 text-text-secondary\" />\n )}\n <span\n className={`text-xs font-medium tabular-nums ${\n trend.value > 0\n ? 'text-status-success-text'\n : trend.value < 0\n ? 'text-status-error-text'\n : 'text-text-secondary'\n }`}\n >\n {trend.value > 0 ? '+' : ''}\n {trend.value}%\n </span>\n {trend.label && <span className=\"text-xs text-text-secondary\">{trend.label}</span>}\n </div>\n )}\n </div>\n </Component>\n );\n}\n"],"mappings":";;;;AAiBA,SAAgB,EAAS,EACvB,UACA,UACA,aACA,SACA,UACA,aAAU,WACV,cACgB;AAqBhB,QACE,kBAHgB,IAAU,WAAW,OAGrC;EACW;EACT,WAAW,mEAvBO;GACpB,SAAS;GACT,SAAS;GACT,SAAS;GACT,OAAO;GACR,CAkB+F,GAAS,GAAG,IAAU,2KAA2K;YAE7R,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBAA2C;MAAU,CAAA,EACjE,KACC,kBAAC,OAAD;MACE,WAAW,oEAnBG;OACxB,SAAS;OACT,SAAS;OACT,SAAS;OACT,OAAO;OACR,CAc4G,GAAS;gBAEzG;MACG,CAAA,CAEJ;;IACN,kBAAC,OAAD;KAAK,WAAU;eACZ,OAAO,KAAU,WAAW,kBAAC,GAAD,EAAwB,UAAS,CAAA,GAAG;KAC7D,CAAA;IACL,KAAY,kBAAC,KAAD;KAAG,WAAU;eAA+B;KAAa,CAAA;IACrE,KACC,kBAAC,OAAD;KAAK,WAAU;eAAf;MACG,EAAM,QAAQ,IACb,kBAAC,GAAD,EAAY,WAAU,mCAAoC,CAAA,GACxD,EAAM,QAAQ,IAChB,kBAAC,GAAD,EAAc,WAAU,iCAAkC,CAAA,GAE1D,kBAAC,GAAD,EAAO,WAAU,8BAA+B,CAAA;MAElD,kBAAC,QAAD;OACE,WAAW,oCACT,EAAM,QAAQ,IACV,6BACA,EAAM,QAAQ,IACZ,2BACA;iBANV;QASG,EAAM,QAAQ,IAAI,MAAM;QACxB,EAAM;QAAM;QACR;;MACN,EAAM,SAAS,kBAAC,QAAD;OAAM,WAAU;iBAA+B,EAAM;OAAa,CAAA;MAC9E;;IAEJ;;EACI,CAAA"}
@@ -45,7 +45,7 @@ var u = [
45
45
  className: "px-6 py-5 space-y-4",
46
46
  children: [
47
47
  N && /* @__PURE__ */ c("div", {
48
- className: "p-3 bg-status-error-bg text-status-error-text text-sm rounded-md",
48
+ className: "p-3 bg-status-error-bg-subtle text-status-error-text text-sm rounded-md",
49
49
  children: N
50
50
  }),
51
51
  /* @__PURE__ */ l("div", { children: [/* @__PURE__ */ l("label", {
@@ -198,7 +198,7 @@ var u = [
198
198
  let t = `{{${e.name}}}`;
199
199
  navigator.clipboard?.writeText(t).catch(() => {}), w((e) => e + t);
200
200
  },
201
- className: "px-1.5 py-0.5 bg-bg-sunken hover:bg-action-primary-bg/10 border border-border-default rounded text-xs font-mono text-text-secondary hover:text-action-primary-bg transition-colors",
201
+ className: "px-1.5 py-0.5 bg-bg-sunken hover:bg-[var(--color-accent-soft)] border border-border-default rounded text-xs font-mono text-text-secondary hover:text-action-primary-bg transition-colors",
202
202
  children: `{{${e.name}}}`
203
203
  }, e.name))]
204
204
  })]
@@ -1 +1 @@
1
- {"version":3,"file":"PublishTemplateForm.js","names":[],"sources":["../../../src/components/templates/PublishTemplateForm.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState, useRef } from 'react';\nimport { motion } from 'framer-motion';\nimport { Upload, X, ChevronDown, Github } from 'lucide-react';\nimport { TemplateVariableEditor } from './TemplateVariableEditor';\nimport type { TemplateVariable } from './TemplateVariableEditor';\n\nexport interface PublishTemplateFormData {\n name: string;\n description: string;\n category: string;\n version: string;\n setupInstructions: string;\n variables: TemplateVariable[];\n sourceRepositoryUrl: string;\n}\n\ninterface PublishTemplateFormProps {\n componentName: string;\n componentRepositoryUrl?: string | null;\n onClose: () => void;\n onSubmit: (data: PublishTemplateFormData) => Promise<void>;\n}\n\nconst CATEGORIES = [\n 'STARTER',\n 'MICROSERVICE',\n 'FRONTEND',\n 'BACKEND',\n 'FULLSTACK',\n 'INFRASTRUCTURE',\n 'LIBRARY',\n 'CUSTOM',\n];\n\nconst inputCls =\n 'w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:border-action-primary-bg text-sm';\n\nexport const PublishTemplateForm: FC<PublishTemplateFormProps> = ({\n componentName,\n componentRepositoryUrl,\n onClose,\n onSubmit,\n}) => {\n const nameRef = useRef<HTMLInputElement>(null);\n const [description, setDescription] = useState('');\n const [category, setCategory] = useState('CUSTOM');\n const [version, setVersion] = useState('');\n const [setupInstructions, setSetupInstructions] = useState('');\n const [variables, setVariables] = useState<TemplateVariable[]>([]);\n const [sourceRepositoryUrl, setSourceRepositoryUrl] = useState(componentRepositoryUrl ?? '');\n const [showAdvanced, setShowAdvanced] = useState(false);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const handleSubmit = async () => {\n const name = nameRef.current?.value?.trim();\n if (!name) {\n setError('Template name is required');\n return;\n }\n const repoUrl = sourceRepositoryUrl.trim();\n if (!repoUrl) {\n setError(\n 'GitHub repository URL is required — templates without a source repo cannot be forked'\n );\n return;\n }\n if (!/^https?:\\/\\/github\\.com\\/.+\\/.+/.test(repoUrl)) {\n setError('Repository URL must be a valid GitHub URL (https://github.com/owner/repo)');\n return;\n }\n setLoading(true);\n setError(null);\n try {\n await onSubmit({\n name,\n description,\n category,\n version,\n setupInstructions,\n variables,\n sourceRepositoryUrl: repoUrl,\n });\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to publish template');\n } finally {\n setLoading(false);\n }\n };\n\n return (\n <div\n className=\"fixed inset-0 flex items-start justify-center z-50 p-4 overflow-y-auto\"\n style={{ backgroundColor: 'rgba(0,0,0,0.55)' }}\n >\n <motion.div\n initial={{ opacity: 0, y: -20 }}\n animate={{ opacity: 1, y: 0 }}\n className=\"bg-bg-surface rounded-lg w-full max-w-xl my-8 shadow-xl\"\n >\n {/* Header */}\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-default\">\n <h2 className=\"text-base font-semibold text-text-primary flex items-center gap-2\">\n <Upload className=\"size-4 text-action-primary-bg\" />\n Publish as Template\n </h2>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1.5 text-text-secondary hover:text-text-primary rounded-md hover:bg-bg-sunken transition-colors\"\n >\n <X className=\"size-4\" />\n </button>\n </div>\n\n {/* Body */}\n <div className=\"px-6 py-5 space-y-4\">\n {error && (\n <div className=\"p-3 bg-status-error-bg text-status-error-text text-sm rounded-md\">\n {error}\n </div>\n )}\n\n <div>\n <label\n htmlFor=\"publishtemplateform-29\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Template Name <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n id=\"publishtemplateform-29\"\n aria-label=\"My Reusable Service Template\"\n ref={nameRef}\n className={inputCls}\n defaultValue={componentName}\n placeholder=\"My Reusable Service Template\"\n />\n </div>\n\n <div>\n <label\n htmlFor=\"publishtemplateform-28\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n <span className=\"flex items-center gap-1.5\">\n <Github className=\"size-3.5\" />\n Source Repository <span className=\"text-status-error-text\">*</span>\n </span>\n </label>\n <input\n id=\"publishtemplateform-28\"\n aria-label=\"https://github.com/your-org/your-repo\"\n className={inputCls}\n placeholder=\"https://github.com/your-org/your-repo\"\n value={sourceRepositoryUrl}\n onChange={(e) => setSourceRepositoryUrl(e.target.value)}\n />\n <p className=\"mt-1 text-xs text-text-muted\">\n When someone uses this template, this repo will be forked into their new component\n repo.\n </p>\n </div>\n\n <div>\n <label\n htmlFor=\"publishtemplateform-27\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Description\n </label>\n <textarea\n id=\"publishtemplateform-27\"\n aria-label=\"What does this template create? Who is it for?\"\n className={`${inputCls} resize-none`}\n rows={2}\n placeholder=\"What does this template create? Who is it for?\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n />\n </div>\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label\n htmlFor=\"publishtemplateform-26\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Category\n </label>\n <select\n id=\"publishtemplateform-26\"\n className={inputCls}\n value={category}\n onChange={(e) => setCategory(e.target.value)}\n >\n {CATEGORIES.map((c) => (\n <option key={c} value={c}>\n {c.charAt(0) + c.slice(1).toLowerCase()}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label\n htmlFor=\"publishtemplateform-25\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Version\n </label>\n <input\n id=\"publishtemplateform-25\"\n aria-label=\"1.0.0\"\n className={inputCls}\n placeholder=\"1.0.0\"\n value={version}\n onChange={(e) => setVersion(e.target.value)}\n />\n </div>\n </div>\n\n {/* Advanced section */}\n <div>\n <button\n type=\"button\"\n onClick={() => setShowAdvanced(!showAdvanced)}\n className=\"flex items-center gap-2 text-sm text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ChevronDown\n className={`size-4 transition-transform ${showAdvanced ? 'rotate-180' : ''}`}\n />\n {showAdvanced ? 'Hide' : 'Show'} Advanced Options\n </button>\n\n {showAdvanced && (\n <div className=\"mt-4 space-y-4\">\n <div>\n <label\n htmlFor=\"publishtemplateform-24\"\n className=\"block text-sm font-medium text-text-primary mb-2\"\n >\n Template Variables\n <span className=\"text-text-muted font-normal ml-1\">\n — inputs users fill when cloning\n </span>\n </label>\n <TemplateVariableEditor value={variables} onChange={setVariables} />\n </div>\n\n <div>\n <label\n htmlFor=\"publishtemplateform-24\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Setup Instructions\n <span className=\"text-text-muted font-normal ml-1\">(Markdown)</span>\n </label>\n <textarea\n id=\"publishtemplateform-24\"\n aria-label=\"control\"\n className={`${inputCls} resize-none font-mono text-xs`}\n rows={6}\n placeholder={`## Getting Started\\n\\n1. Clone the repository\\n2. Run \\`bun install\\`\\n3. Set the image source: {{source_repo}}`}\n value={setupInstructions}\n onChange={(e) => setSetupInstructions(e.target.value)}\n />\n <div className=\"mt-1.5 space-y-1\">\n <p className=\"text-xs text-text-muted\">\n Supports Markdown. Use{' '}\n <code className=\"px-1 py-0.5 bg-bg-sunken rounded text-text-secondary font-mono\">\n {'{{variableName}}'}\n </code>{' '}\n to insert the value a user provides when cloning.\n </p>\n {variables.filter((v) => v.name.trim()).length > 0 && (\n <div className=\"flex flex-wrap gap-1 items-center\">\n <span className=\"text-xs text-text-muted\">Available:</span>\n {variables\n .filter((v) => v.name.trim())\n .map((v) => (\n <button\n key={v.name}\n type=\"button\"\n title=\"Click to copy\"\n onClick={() => {\n const token = `{{${v.name}}}`;\n navigator.clipboard?.writeText(token).catch(() => {});\n setSetupInstructions((prev) => prev + token);\n }}\n className=\"px-1.5 py-0.5 bg-bg-sunken hover:bg-action-primary-bg/10 border border-border-default rounded text-xs font-mono text-text-secondary hover:text-action-primary-bg transition-colors\"\n >\n {`{{${v.name}}}`}\n </button>\n ))}\n </div>\n )}\n </div>\n </div>\n </div>\n )}\n </div>\n </div>\n\n {/* Footer */}\n <div className=\"flex justify-end gap-3 px-6 py-4 border-t border-border-default\">\n <button\n type=\"button\"\n onClick={onClose}\n disabled={loading}\n className=\"px-4 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={loading}\n className=\"flex items-center gap-2 px-4 py-2 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n <Upload className=\"size-4\" />\n {loading ? 'Publishing...' : 'Publish Template'}\n </button>\n </div>\n </motion.div>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"PublishTemplateForm.js","names":[],"sources":["../../../src/components/templates/PublishTemplateForm.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState, useRef } from 'react';\nimport { motion } from 'framer-motion';\nimport { Upload, X, ChevronDown, Github } from 'lucide-react';\nimport { TemplateVariableEditor } from './TemplateVariableEditor';\nimport type { TemplateVariable } from './TemplateVariableEditor';\n\nexport interface PublishTemplateFormData {\n name: string;\n description: string;\n category: string;\n version: string;\n setupInstructions: string;\n variables: TemplateVariable[];\n sourceRepositoryUrl: string;\n}\n\ninterface PublishTemplateFormProps {\n componentName: string;\n componentRepositoryUrl?: string | null;\n onClose: () => void;\n onSubmit: (data: PublishTemplateFormData) => Promise<void>;\n}\n\nconst CATEGORIES = [\n 'STARTER',\n 'MICROSERVICE',\n 'FRONTEND',\n 'BACKEND',\n 'FULLSTACK',\n 'INFRASTRUCTURE',\n 'LIBRARY',\n 'CUSTOM',\n];\n\nconst inputCls =\n 'w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:border-action-primary-bg text-sm';\n\nexport const PublishTemplateForm: FC<PublishTemplateFormProps> = ({\n componentName,\n componentRepositoryUrl,\n onClose,\n onSubmit,\n}) => {\n const nameRef = useRef<HTMLInputElement>(null);\n const [description, setDescription] = useState('');\n const [category, setCategory] = useState('CUSTOM');\n const [version, setVersion] = useState('');\n const [setupInstructions, setSetupInstructions] = useState('');\n const [variables, setVariables] = useState<TemplateVariable[]>([]);\n const [sourceRepositoryUrl, setSourceRepositoryUrl] = useState(componentRepositoryUrl ?? '');\n const [showAdvanced, setShowAdvanced] = useState(false);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const handleSubmit = async () => {\n const name = nameRef.current?.value?.trim();\n if (!name) {\n setError('Template name is required');\n return;\n }\n const repoUrl = sourceRepositoryUrl.trim();\n if (!repoUrl) {\n setError(\n 'GitHub repository URL is required — templates without a source repo cannot be forked'\n );\n return;\n }\n if (!/^https?:\\/\\/github\\.com\\/.+\\/.+/.test(repoUrl)) {\n setError('Repository URL must be a valid GitHub URL (https://github.com/owner/repo)');\n return;\n }\n setLoading(true);\n setError(null);\n try {\n await onSubmit({\n name,\n description,\n category,\n version,\n setupInstructions,\n variables,\n sourceRepositoryUrl: repoUrl,\n });\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to publish template');\n } finally {\n setLoading(false);\n }\n };\n\n return (\n <div\n className=\"fixed inset-0 flex items-start justify-center z-50 p-4 overflow-y-auto\"\n style={{ backgroundColor: 'rgba(0,0,0,0.55)' }}\n >\n <motion.div\n initial={{ opacity: 0, y: -20 }}\n animate={{ opacity: 1, y: 0 }}\n className=\"bg-bg-surface rounded-lg w-full max-w-xl my-8 shadow-xl\"\n >\n {/* Header */}\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-default\">\n <h2 className=\"text-base font-semibold text-text-primary flex items-center gap-2\">\n <Upload className=\"size-4 text-action-primary-bg\" />\n Publish as Template\n </h2>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1.5 text-text-secondary hover:text-text-primary rounded-md hover:bg-bg-sunken transition-colors\"\n >\n <X className=\"size-4\" />\n </button>\n </div>\n\n {/* Body */}\n <div className=\"px-6 py-5 space-y-4\">\n {error && (\n <div className=\"p-3 bg-status-error-bg-subtle text-status-error-text text-sm rounded-md\">\n {error}\n </div>\n )}\n\n <div>\n <label\n htmlFor=\"publishtemplateform-29\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Template Name <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n id=\"publishtemplateform-29\"\n aria-label=\"My Reusable Service Template\"\n ref={nameRef}\n className={inputCls}\n defaultValue={componentName}\n placeholder=\"My Reusable Service Template\"\n />\n </div>\n\n <div>\n <label\n htmlFor=\"publishtemplateform-28\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n <span className=\"flex items-center gap-1.5\">\n <Github className=\"size-3.5\" />\n Source Repository <span className=\"text-status-error-text\">*</span>\n </span>\n </label>\n <input\n id=\"publishtemplateform-28\"\n aria-label=\"https://github.com/your-org/your-repo\"\n className={inputCls}\n placeholder=\"https://github.com/your-org/your-repo\"\n value={sourceRepositoryUrl}\n onChange={(e) => setSourceRepositoryUrl(e.target.value)}\n />\n <p className=\"mt-1 text-xs text-text-muted\">\n When someone uses this template, this repo will be forked into their new component\n repo.\n </p>\n </div>\n\n <div>\n <label\n htmlFor=\"publishtemplateform-27\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Description\n </label>\n <textarea\n id=\"publishtemplateform-27\"\n aria-label=\"What does this template create? Who is it for?\"\n className={`${inputCls} resize-none`}\n rows={2}\n placeholder=\"What does this template create? Who is it for?\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n />\n </div>\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label\n htmlFor=\"publishtemplateform-26\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Category\n </label>\n <select\n id=\"publishtemplateform-26\"\n className={inputCls}\n value={category}\n onChange={(e) => setCategory(e.target.value)}\n >\n {CATEGORIES.map((c) => (\n <option key={c} value={c}>\n {c.charAt(0) + c.slice(1).toLowerCase()}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label\n htmlFor=\"publishtemplateform-25\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Version\n </label>\n <input\n id=\"publishtemplateform-25\"\n aria-label=\"1.0.0\"\n className={inputCls}\n placeholder=\"1.0.0\"\n value={version}\n onChange={(e) => setVersion(e.target.value)}\n />\n </div>\n </div>\n\n {/* Advanced section */}\n <div>\n <button\n type=\"button\"\n onClick={() => setShowAdvanced(!showAdvanced)}\n className=\"flex items-center gap-2 text-sm text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ChevronDown\n className={`size-4 transition-transform ${showAdvanced ? 'rotate-180' : ''}`}\n />\n {showAdvanced ? 'Hide' : 'Show'} Advanced Options\n </button>\n\n {showAdvanced && (\n <div className=\"mt-4 space-y-4\">\n <div>\n <label\n htmlFor=\"publishtemplateform-24\"\n className=\"block text-sm font-medium text-text-primary mb-2\"\n >\n Template Variables\n <span className=\"text-text-muted font-normal ml-1\">\n — inputs users fill when cloning\n </span>\n </label>\n <TemplateVariableEditor value={variables} onChange={setVariables} />\n </div>\n\n <div>\n <label\n htmlFor=\"publishtemplateform-24\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Setup Instructions\n <span className=\"text-text-muted font-normal ml-1\">(Markdown)</span>\n </label>\n <textarea\n id=\"publishtemplateform-24\"\n aria-label=\"control\"\n className={`${inputCls} resize-none font-mono text-xs`}\n rows={6}\n placeholder={`## Getting Started\\n\\n1. Clone the repository\\n2. Run \\`bun install\\`\\n3. Set the image source: {{source_repo}}`}\n value={setupInstructions}\n onChange={(e) => setSetupInstructions(e.target.value)}\n />\n <div className=\"mt-1.5 space-y-1\">\n <p className=\"text-xs text-text-muted\">\n Supports Markdown. Use{' '}\n <code className=\"px-1 py-0.5 bg-bg-sunken rounded text-text-secondary font-mono\">\n {'{{variableName}}'}\n </code>{' '}\n to insert the value a user provides when cloning.\n </p>\n {variables.filter((v) => v.name.trim()).length > 0 && (\n <div className=\"flex flex-wrap gap-1 items-center\">\n <span className=\"text-xs text-text-muted\">Available:</span>\n {variables\n .filter((v) => v.name.trim())\n .map((v) => (\n <button\n key={v.name}\n type=\"button\"\n title=\"Click to copy\"\n onClick={() => {\n const token = `{{${v.name}}}`;\n navigator.clipboard?.writeText(token).catch(() => {});\n setSetupInstructions((prev) => prev + token);\n }}\n className=\"px-1.5 py-0.5 bg-bg-sunken hover:bg-[var(--color-accent-soft)] border border-border-default rounded text-xs font-mono text-text-secondary hover:text-action-primary-bg transition-colors\"\n >\n {`{{${v.name}}}`}\n </button>\n ))}\n </div>\n )}\n </div>\n </div>\n </div>\n )}\n </div>\n </div>\n\n {/* Footer */}\n <div className=\"flex justify-end gap-3 px-6 py-4 border-t border-border-default\">\n <button\n type=\"button\"\n onClick={onClose}\n disabled={loading}\n className=\"px-4 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={loading}\n className=\"flex items-center gap-2 px-4 py-2 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n <Upload className=\"size-4\" />\n {loading ? 'Publishing...' : 'Publish Template'}\n </button>\n </div>\n </motion.div>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -5,10 +5,10 @@ import { motion as s } from "framer-motion";
5
5
  import { jsx as c, jsxs as l } from "react/jsx-runtime";
6
6
  //#region src/components/templates/TemplateCard.tsx
7
7
  var u = {
8
- SERVICE: "bg-action-primary-bg/10 text-action-primary-bg",
9
- FRONTEND: "bg-status-info-bg text-status-info-text",
10
- LIBRARY: "bg-status-warning-bg text-status-warning-text",
11
- INFRASTRUCTURE: "bg-status-success-bg text-status-success-text",
8
+ SERVICE: "bg-[var(--color-accent-soft)] text-action-primary-bg",
9
+ FRONTEND: "bg-status-info-bg-subtle text-status-info-text",
10
+ LIBRARY: "bg-status-warning-bg-subtle text-status-warning-text",
11
+ INFRASTRUCTURE: "bg-status-success-bg-subtle text-status-success-text",
12
12
  STARTER: "bg-bg-sunken text-text-secondary"
13
13
  }, d = ({ template: d, onOpen: f, className: p = "" }) => {
14
14
  let { t: m } = t(), h = (e, t) => {
@@ -27,7 +27,7 @@ var u = {
27
27
  /* @__PURE__ */ l("div", {
28
28
  className: "flex items-start gap-2 sm:gap-3",
29
29
  children: [/* @__PURE__ */ c("div", {
30
- className: "flex-shrink-0 size-8 sm:w-10 sm:h-10 rounded-lg bg-action-primary-bg/10 flex items-center justify-center",
30
+ className: "flex-shrink-0 size-8 sm:w-10 sm:h-10 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center",
31
31
  children: /* @__PURE__ */ c(r, { className: "size-4 sm:w-5 sm:h-5 text-action-primary-bg" })
32
32
  }), /* @__PURE__ */ l("div", {
33
33
  className: "min-w-0 flex-1",