@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":"CatalogCard.js","names":[],"sources":["../../../src/components/catalog/CatalogCard.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState } from 'react';\nimport { motion, AnimatePresence } from 'framer-motion';\nimport {\n BookOpen,\n Eye,\n EyeOff,\n Globe,\n Tag,\n MoreVertical,\n Settings,\n Archive,\n Layers,\n Zap,\n} from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nexport interface CatalogCardData {\n id: string;\n name: string;\n description: string | null;\n visibility: 'PRIVATE' | 'INTERNAL' | 'PUBLIC';\n componentCount: number;\n vibeCount: number;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CatalogCardProps {\n catalog: CatalogCardData;\n onOpen?: (catalog: CatalogCardData) => void;\n onEdit?: (catalog: CatalogCardData) => void;\n onArchive?: (catalog: CatalogCardData) => void;\n className?: string;\n}\n\nconst visibilityConfig = {\n PRIVATE: { icon: EyeOff, label: 'Private', className: 'bg-bg-sunken text-text-secondary' },\n INTERNAL: {\n icon: Eye,\n label: 'Internal',\n className: 'bg-status-warning-bg text-status-warning-text',\n },\n PUBLIC: {\n icon: Globe,\n label: 'Public',\n className: 'bg-status-success-bg text-status-success-text',\n },\n};\n\nexport const CatalogCard: FC<CatalogCardProps> = ({\n catalog,\n onOpen,\n onEdit,\n onArchive,\n className = '',\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n const [showMenu, setShowMenu] = useState(false);\n\n const visConfig = visibilityConfig[catalog.visibility] ?? visibilityConfig.PRIVATE;\n const VisIcon = visConfig.icon;\n\n return (\n <motion.div\n whileHover={{ y: -2, transition: { duration: 0.15, ease: [0.4, 0, 0.2, 1] } }}\n whileTap={{ scale: 0.98 }}\n className={`bg-bg-surface border border-border-default rounded-lg p-4 hover:border-border-strong hover:shadow-md transition-all cursor-pointer ${className}`}\n onClick={() => onOpen?.(catalog)}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex items-start gap-2 sm:gap-3 min-w-0 flex-1\">\n <div className=\"flex-shrink-0 size-8 sm:w-10 sm:h-10 rounded-lg bg-action-primary-bg/10 flex items-center justify-center\">\n <BookOpen className=\"size-4 sm:w-5 sm:h-5 text-action-primary-bg\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"font-semibold text-text-primary truncate text-sm sm:text-base\">\n {catalog.name}\n </h3>\n <div className=\"flex items-center gap-2 mt-0.5\">\n <span\n className={`inline-flex items-center gap-1 px-1.5 py-0.5 text-xs rounded ${visConfig.className}`}\n >\n <VisIcon className=\"size-3\" />\n {tr(`catalog.visibility.${catalog.visibility.toLowerCase()}`, visConfig.label)}\n </span>\n </div>\n </div>\n </div>\n {(onEdit || onArchive) && (\n <div className=\"relative flex-shrink-0\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n setShowMenu(!showMenu);\n }}\n className=\"p-1 sm:p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n >\n <MoreVertical className=\"size-4 text-text-secondary\" />\n </button>\n <AnimatePresence>\n {showMenu && (\n <motion.div\n initial={{ opacity: 0, scale: 0.95, y: -4 }}\n animate={{ opacity: 1, scale: 1, y: 0 }}\n exit={{ opacity: 0, scale: 0.95, y: -4 }}\n transition={{ duration: 0.15 }}\n className=\"absolute right-0 mt-1 w-40 bg-bg-surface border border-border-default rounded-lg shadow-lg z-10\"\n >\n {onEdit && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onEdit(catalog);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-t-lg\"\n >\n <Settings className=\"size-4\" />\n {tr('common.edit', 'Edit')}\n </motion.button>\n )}\n {onArchive && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onArchive(catalog);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-b-lg\"\n >\n <Archive className=\"size-4\" />\n {tr('common.archive', 'Archive')}\n </motion.button>\n )}\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n )}\n </div>\n\n {catalog.description && (\n <p className=\"mt-2 text-sm text-text-secondary line-clamp-2\">{catalog.description}</p>\n )}\n\n {/* Stats row */}\n <div className=\"mt-3 flex items-center gap-4 text-xs text-text-secondary\">\n <span className=\"inline-flex items-center gap-1\">\n <Layers className=\"size-3.5\" />\n {catalog.componentCount} {tr('catalog.components', 'components')}\n </span>\n <span className=\"inline-flex items-center gap-1\">\n <Zap className=\"size-3.5\" />\n {catalog.vibeCount} {tr('catalog.vibes', 'vibes')}\n </span>\n </div>\n\n {catalog.tags.length > 0 && (\n <div className=\"mt-3 flex flex-wrap gap-1\">\n {catalog.tags.slice(0, 3).map((tag) => (\n <span\n key={tag}\n className=\"inline-flex items-center gap-1 px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\"\n >\n <Tag className=\"size-3\" />\n {tag}\n </span>\n ))}\n {catalog.tags.length > 3 && (\n <span className=\"px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\">\n +{catalog.tags.length - 3}\n </span>\n )}\n </div>\n )}\n </motion.div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"CatalogCard.js","names":[],"sources":["../../../src/components/catalog/CatalogCard.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState } from 'react';\nimport { motion, AnimatePresence } from 'framer-motion';\nimport {\n BookOpen,\n Eye,\n EyeOff,\n Globe,\n Tag,\n MoreVertical,\n Settings,\n Archive,\n Layers,\n Zap,\n} from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nexport interface CatalogCardData {\n id: string;\n name: string;\n description: string | null;\n visibility: 'PRIVATE' | 'INTERNAL' | 'PUBLIC';\n componentCount: number;\n vibeCount: number;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CatalogCardProps {\n catalog: CatalogCardData;\n onOpen?: (catalog: CatalogCardData) => void;\n onEdit?: (catalog: CatalogCardData) => void;\n onArchive?: (catalog: CatalogCardData) => void;\n className?: string;\n}\n\nconst visibilityConfig = {\n PRIVATE: { icon: EyeOff, label: 'Private', className: 'bg-bg-sunken text-text-secondary' },\n INTERNAL: {\n icon: Eye,\n label: 'Internal',\n className: 'bg-status-warning-bg-subtle text-status-warning-text',\n },\n PUBLIC: {\n icon: Globe,\n label: 'Public',\n className: 'bg-status-success-bg-subtle text-status-success-text',\n },\n};\n\nexport const CatalogCard: FC<CatalogCardProps> = ({\n catalog,\n onOpen,\n onEdit,\n onArchive,\n className = '',\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n const [showMenu, setShowMenu] = useState(false);\n\n const visConfig = visibilityConfig[catalog.visibility] ?? visibilityConfig.PRIVATE;\n const VisIcon = visConfig.icon;\n\n return (\n <motion.div\n whileHover={{ y: -2, transition: { duration: 0.15, ease: [0.4, 0, 0.2, 1] } }}\n whileTap={{ scale: 0.98 }}\n className={`bg-bg-surface border border-border-default rounded-lg p-4 hover:border-border-strong hover:shadow-md transition-all cursor-pointer ${className}`}\n onClick={() => onOpen?.(catalog)}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex items-start gap-2 sm:gap-3 min-w-0 flex-1\">\n <div className=\"flex-shrink-0 size-8 sm:w-10 sm:h-10 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <BookOpen className=\"size-4 sm:w-5 sm:h-5 text-action-primary-bg\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"font-semibold text-text-primary truncate text-sm sm:text-base\">\n {catalog.name}\n </h3>\n <div className=\"flex items-center gap-2 mt-0.5\">\n <span\n className={`inline-flex items-center gap-1 px-1.5 py-0.5 text-xs rounded ${visConfig.className}`}\n >\n <VisIcon className=\"size-3\" />\n {tr(`catalog.visibility.${catalog.visibility.toLowerCase()}`, visConfig.label)}\n </span>\n </div>\n </div>\n </div>\n {(onEdit || onArchive) && (\n <div className=\"relative flex-shrink-0\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n setShowMenu(!showMenu);\n }}\n className=\"p-1 sm:p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n >\n <MoreVertical className=\"size-4 text-text-secondary\" />\n </button>\n <AnimatePresence>\n {showMenu && (\n <motion.div\n initial={{ opacity: 0, scale: 0.95, y: -4 }}\n animate={{ opacity: 1, scale: 1, y: 0 }}\n exit={{ opacity: 0, scale: 0.95, y: -4 }}\n transition={{ duration: 0.15 }}\n className=\"absolute right-0 mt-1 w-40 bg-bg-surface border border-border-default rounded-lg shadow-lg z-10\"\n >\n {onEdit && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onEdit(catalog);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-t-lg\"\n >\n <Settings className=\"size-4\" />\n {tr('common.edit', 'Edit')}\n </motion.button>\n )}\n {onArchive && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onArchive(catalog);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-b-lg\"\n >\n <Archive className=\"size-4\" />\n {tr('common.archive', 'Archive')}\n </motion.button>\n )}\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n )}\n </div>\n\n {catalog.description && (\n <p className=\"mt-2 text-sm text-text-secondary line-clamp-2\">{catalog.description}</p>\n )}\n\n {/* Stats row */}\n <div className=\"mt-3 flex items-center gap-4 text-xs text-text-secondary\">\n <span className=\"inline-flex items-center gap-1\">\n <Layers className=\"size-3.5\" />\n {catalog.componentCount} {tr('catalog.components', 'components')}\n </span>\n <span className=\"inline-flex items-center gap-1\">\n <Zap className=\"size-3.5\" />\n {catalog.vibeCount} {tr('catalog.vibes', 'vibes')}\n </span>\n </div>\n\n {catalog.tags.length > 0 && (\n <div className=\"mt-3 flex flex-wrap gap-1\">\n {catalog.tags.slice(0, 3).map((tag) => (\n <span\n key={tag}\n className=\"inline-flex items-center gap-1 px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\"\n >\n <Tag className=\"size-3\" />\n {tag}\n </span>\n ))}\n {catalog.tags.length > 3 && (\n <span className=\"px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\">\n +{catalog.tags.length - 3}\n </span>\n )}\n </div>\n )}\n </motion.div>\n );\n};\n"],"mappings":"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"}
@@ -29,7 +29,7 @@ var o = ({ isOpen: o, onClose: s, onSubmit: c, initialData: l, title: u, extraSe
29
29
  className: "space-y-4",
30
30
  children: [
31
31
  x && /* @__PURE__ */ i("div", {
32
- className: "p-3 bg-status-error-bg text-status-error-text text-sm rounded-md",
32
+ className: "p-3 bg-status-error-bg-subtle text-status-error-text text-sm rounded-md",
33
33
  children: x
34
34
  }),
35
35
  /* @__PURE__ */ a("div", { children: [
@@ -1 +1 @@
1
- {"version":3,"file":"CatalogForm.js","names":[],"sources":["../../../src/components/catalog/CatalogForm.tsx"],"sourcesContent":["import { useRef, useState } from 'react';\nimport type { FC, ReactNode } from 'react';\nimport { X } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nexport interface CatalogFormData {\n name: string;\n description: string;\n visibility: 'PRIVATE' | 'INTERNAL' | 'PUBLIC';\n owner: string;\n tags: string[];\n}\n\nexport interface CatalogFormProps {\n isOpen: boolean;\n onClose: () => void;\n onSubmit: (data: CatalogFormData) => Promise<void>;\n initialData?: Partial<CatalogFormData>;\n title?: string;\n /** Optional extra content (e.g. a draft EntityTagPicker) rendered above the footer. */\n extraSection?: ReactNode;\n}\n\nexport const CatalogForm: FC<CatalogFormProps> = ({\n isOpen,\n onClose,\n onSubmit,\n initialData,\n title,\n extraSection,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n\n const nameRef = useRef<HTMLInputElement>(null);\n const descriptionRef = useRef<HTMLTextAreaElement>(null);\n const visibilityRef = useRef<HTMLSelectElement>(null);\n const ownerRef = useRef<HTMLInputElement>(null);\n const tagsRef = useRef<HTMLInputElement>(null);\n\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [fieldErrors, setFieldErrors] = useState<{ name?: string }>({});\n\n if (!isOpen) return null;\n\n const handleSubmit = async () => {\n const name = nameRef.current?.value?.trim();\n if (!name) {\n setFieldErrors({ name: tr('catalog.nameRequired', 'Name is required') });\n setError(tr('catalog.fillRequired', 'Please fill in all required fields'));\n return;\n }\n setFieldErrors({});\n\n const data: CatalogFormData = {\n name,\n description: descriptionRef.current?.value?.trim() || '',\n visibility: (visibilityRef.current?.value as CatalogFormData['visibility']) || 'PRIVATE',\n owner: ownerRef.current?.value?.trim() || '',\n tags: tagsRef.current?.value\n ? tagsRef.current.value\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n : [],\n };\n\n setLoading(true);\n setError(null);\n try {\n await onSubmit(data);\n onClose();\n } catch (err) {\n setError(\n err instanceof Error ? err.message : tr('catalog.failedSave', 'Failed to save catalog')\n );\n } finally {\n setLoading(false);\n }\n };\n\n return (\n <div className=\"fixed inset-0 bg-bg-overlay flex items-center justify-center z-50 p-4\">\n <div className=\"bg-bg-surface rounded-lg p-6 w-full max-w-lg\">\n <div className=\"flex items-center justify-between mb-4\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {title || tr('catalog.createCatalog', 'Create Catalog')}\n </h2>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors text-text-secondary\"\n >\n <X className=\"size-5\" />\n </button>\n </div>\n <div className=\"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 <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('catalog.nameLabel', 'Name *')}\n </label>\n <input\n aria-label=\"control\"\n ref={nameRef}\n type=\"text\"\n defaultValue={initialData?.name || ''}\n placeholder={tr('catalog.namePlaceholder', 'My Catalog')}\n onChange={() =>\n fieldErrors.name && setFieldErrors((e) => ({ ...e, name: undefined }))\n }\n className={`w-full px-3 py-2 bg-bg-sunken border rounded-md text-text-primary placeholder:text-text-placeholder ${fieldErrors.name ? 'border-status-error-border' : 'border-border-default'}`}\n />\n {fieldErrors.name && (\n <p className=\"mt-1 text-xs text-status-error-text\">{fieldErrors.name}</p>\n )}\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('catalog.descriptionLabel', 'Description')}\n </label>\n <textarea\n aria-label=\"control\"\n ref={descriptionRef}\n defaultValue={initialData?.description || ''}\n placeholder={tr('catalog.descriptionPlaceholder', 'Describe this catalog...')}\n rows={3}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder resize-none\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('catalog.visibilityLabel', 'Visibility')}\n </label>\n <select\n ref={visibilityRef}\n defaultValue={initialData?.visibility || 'PRIVATE'}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary\"\n >\n <option value=\"PRIVATE\">{tr('catalog.visibilityPrivate', 'Private')}</option>\n <option value=\"INTERNAL\">{tr('catalog.visibilityInternal', 'Internal')}</option>\n <option value=\"PUBLIC\">{tr('catalog.visibilityPublic', 'Public')}</option>\n </select>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('catalog.ownerLabel', 'Owner')}\n </label>\n <input\n aria-label=\"control\"\n ref={ownerRef}\n type=\"text\"\n defaultValue={initialData?.owner || ''}\n placeholder={tr('catalog.ownerPlaceholder', 'team-name')}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('catalog.tagsLabel', 'Tags (comma-separated)')}\n </label>\n <input\n aria-label=\"control\"\n ref={tagsRef}\n type=\"text\"\n defaultValue={initialData?.tags?.join(', ') || ''}\n placeholder={tr('catalog.tagsPlaceholder', 'frontend, microservice, api')}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder\"\n />\n </div>\n </div>\n {extraSection && (\n <div className=\"mt-4 border-t border-border-default pt-4\">{extraSection}</div>\n )}\n <div className=\"flex flex-col sm:flex-row justify-end gap-3 mt-6\">\n <button\n type=\"button\"\n onClick={onClose}\n disabled={loading}\n className=\"px-4 py-2 text-text-primary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={loading}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n {loading\n ? tr('common.saving', 'Saving...')\n : initialData?.name\n ? tr('common.save', 'Save')\n : tr('catalog.createCatalog', 'Create Catalog')}\n </button>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"CatalogForm.js","names":[],"sources":["../../../src/components/catalog/CatalogForm.tsx"],"sourcesContent":["import { useRef, useState } from 'react';\nimport type { FC, ReactNode } from 'react';\nimport { X } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nexport interface CatalogFormData {\n name: string;\n description: string;\n visibility: 'PRIVATE' | 'INTERNAL' | 'PUBLIC';\n owner: string;\n tags: string[];\n}\n\nexport interface CatalogFormProps {\n isOpen: boolean;\n onClose: () => void;\n onSubmit: (data: CatalogFormData) => Promise<void>;\n initialData?: Partial<CatalogFormData>;\n title?: string;\n /** Optional extra content (e.g. a draft EntityTagPicker) rendered above the footer. */\n extraSection?: ReactNode;\n}\n\nexport const CatalogForm: FC<CatalogFormProps> = ({\n isOpen,\n onClose,\n onSubmit,\n initialData,\n title,\n extraSection,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n\n const nameRef = useRef<HTMLInputElement>(null);\n const descriptionRef = useRef<HTMLTextAreaElement>(null);\n const visibilityRef = useRef<HTMLSelectElement>(null);\n const ownerRef = useRef<HTMLInputElement>(null);\n const tagsRef = useRef<HTMLInputElement>(null);\n\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [fieldErrors, setFieldErrors] = useState<{ name?: string }>({});\n\n if (!isOpen) return null;\n\n const handleSubmit = async () => {\n const name = nameRef.current?.value?.trim();\n if (!name) {\n setFieldErrors({ name: tr('catalog.nameRequired', 'Name is required') });\n setError(tr('catalog.fillRequired', 'Please fill in all required fields'));\n return;\n }\n setFieldErrors({});\n\n const data: CatalogFormData = {\n name,\n description: descriptionRef.current?.value?.trim() || '',\n visibility: (visibilityRef.current?.value as CatalogFormData['visibility']) || 'PRIVATE',\n owner: ownerRef.current?.value?.trim() || '',\n tags: tagsRef.current?.value\n ? tagsRef.current.value\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n : [],\n };\n\n setLoading(true);\n setError(null);\n try {\n await onSubmit(data);\n onClose();\n } catch (err) {\n setError(\n err instanceof Error ? err.message : tr('catalog.failedSave', 'Failed to save catalog')\n );\n } finally {\n setLoading(false);\n }\n };\n\n return (\n <div className=\"fixed inset-0 bg-bg-overlay flex items-center justify-center z-50 p-4\">\n <div className=\"bg-bg-surface rounded-lg p-6 w-full max-w-lg\">\n <div className=\"flex items-center justify-between mb-4\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {title || tr('catalog.createCatalog', 'Create Catalog')}\n </h2>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors text-text-secondary\"\n >\n <X className=\"size-5\" />\n </button>\n </div>\n <div className=\"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 <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('catalog.nameLabel', 'Name *')}\n </label>\n <input\n aria-label=\"control\"\n ref={nameRef}\n type=\"text\"\n defaultValue={initialData?.name || ''}\n placeholder={tr('catalog.namePlaceholder', 'My Catalog')}\n onChange={() =>\n fieldErrors.name && setFieldErrors((e) => ({ ...e, name: undefined }))\n }\n className={`w-full px-3 py-2 bg-bg-sunken border rounded-md text-text-primary placeholder:text-text-placeholder ${fieldErrors.name ? 'border-status-error-border' : 'border-border-default'}`}\n />\n {fieldErrors.name && (\n <p className=\"mt-1 text-xs text-status-error-text\">{fieldErrors.name}</p>\n )}\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('catalog.descriptionLabel', 'Description')}\n </label>\n <textarea\n aria-label=\"control\"\n ref={descriptionRef}\n defaultValue={initialData?.description || ''}\n placeholder={tr('catalog.descriptionPlaceholder', 'Describe this catalog...')}\n rows={3}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder resize-none\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('catalog.visibilityLabel', 'Visibility')}\n </label>\n <select\n ref={visibilityRef}\n defaultValue={initialData?.visibility || 'PRIVATE'}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary\"\n >\n <option value=\"PRIVATE\">{tr('catalog.visibilityPrivate', 'Private')}</option>\n <option value=\"INTERNAL\">{tr('catalog.visibilityInternal', 'Internal')}</option>\n <option value=\"PUBLIC\">{tr('catalog.visibilityPublic', 'Public')}</option>\n </select>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('catalog.ownerLabel', 'Owner')}\n </label>\n <input\n aria-label=\"control\"\n ref={ownerRef}\n type=\"text\"\n defaultValue={initialData?.owner || ''}\n placeholder={tr('catalog.ownerPlaceholder', 'team-name')}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('catalog.tagsLabel', 'Tags (comma-separated)')}\n </label>\n <input\n aria-label=\"control\"\n ref={tagsRef}\n type=\"text\"\n defaultValue={initialData?.tags?.join(', ') || ''}\n placeholder={tr('catalog.tagsPlaceholder', 'frontend, microservice, api')}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder\"\n />\n </div>\n </div>\n {extraSection && (\n <div className=\"mt-4 border-t border-border-default pt-4\">{extraSection}</div>\n )}\n <div className=\"flex flex-col sm:flex-row justify-end gap-3 mt-6\">\n <button\n type=\"button\"\n onClick={onClose}\n disabled={loading}\n className=\"px-4 py-2 text-text-primary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={loading}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n {loading\n ? tr('common.saving', 'Saving...')\n : initialData?.name\n ? tr('common.save', 'Save')\n : tr('catalog.createCatalog', 'Create Catalog')}\n </button>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -8,10 +8,10 @@ import { useNavigate as l } from "react-router-dom";
8
8
  import { Fragment as u, jsx as d, jsxs as f } from "react/jsx-runtime";
9
9
  //#region src/components/common/ActionAgentTargetNotice.tsx
10
10
  var p = {
11
- info: "border-status-info-border bg-status-info-bg text-status-info-text",
12
- success: "border-status-success-border bg-status-success-bg text-status-success-text",
13
- warning: "border-status-warning-border bg-status-warning-bg text-status-warning-text",
14
- error: "border-status-error-border bg-status-error-bg text-status-error-text"
11
+ info: "border-border-subtle bg-bg-sunken text-text-secondary",
12
+ success: "border-status-success-border bg-status-success-bg-subtle text-status-success-text",
13
+ warning: "border-status-warning-border bg-status-warning-bg-subtle text-status-warning-text",
14
+ error: "border-status-error-border bg-status-error-bg-subtle text-status-error-text"
15
15
  };
16
16
  function m({ action: m, className: h = "" }) {
17
17
  let g = n(), _ = l(), v = m.actionType === "COMMAND" || m.actionType === "SCRIPT", { data: y } = e({
@@ -44,7 +44,7 @@ function m({ action: m, className: h = "" }) {
44
44
  x
45
45
  ]), T = () => _("/vibecontrols/agents"), E = (e) => _(`/vibecontrols/agents/${encodeURIComponent(e)}`), D = () => _(`/vibecontrols/actions?editAction=${encodeURIComponent(m.id)}`);
46
46
  if (w.mode === "no-agent") return m.actionType === "WEBHOOK" ? /* @__PURE__ */ f("div", {
47
- className: `flex items-center gap-2 rounded-lg border border-border-default bg-bg-secondary px-3 py-2 text-xs text-text-tertiary ${h}`,
47
+ className: `flex items-center gap-2 rounded-card border border-border-subtle bg-bg-sunken px-3 py-2 text-xs text-text-secondary ${h}`,
48
48
  children: [/* @__PURE__ */ d(c, {
49
49
  size: 14,
50
50
  className: "flex-shrink-0"
@@ -90,7 +90,7 @@ function m({ action: m, className: h = "" }) {
90
90
  })] });
91
91
  }
92
92
  return /* @__PURE__ */ d("div", {
93
- className: `rounded-lg border px-3 py-2 text-xs ${p[k]} ${h}`,
93
+ className: `rounded-card border px-3 py-2.5 text-xs ${p[k]} ${h}`,
94
94
  children: /* @__PURE__ */ f("div", {
95
95
  className: "flex items-start gap-2",
96
96
  children: [A, /* @__PURE__ */ f("div", {
@@ -1 +1 @@
1
- {"version":3,"file":"ActionAgentTargetNotice.js","names":[],"sources":["../../../src/components/common/ActionAgentTargetNotice.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { AlertTriangle, ArrowUpRight, ServerCog } from 'lucide-react';\n\nimport {\n useListAgentsQuery,\n useVibecontrolsAgentHealthProbesQuery,\n} from '@/generated/wspace-operations';\nimport type { AgentHealthProbe } from '@/utils/agentStatus';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { AgentStatusBadge } from '../agents/AgentStatusBadge';\nimport {\n resolveAgentTarget,\n type AgentTargetActionLike,\n type AgentTargetAgentLike,\n} from './actionAgentTarget';\n\ninterface ActionAgentTargetNoticeProps {\n /** The action being linked. Needs id + type + agent targeting fields. */\n action: AgentTargetActionLike & { id: string };\n className?: string;\n}\n\ntype Tone = 'info' | 'success' | 'warning' | 'error';\n\nconst TONE_CLASS: Record<Tone, string> = {\n info: 'border-status-info-border bg-status-info-bg text-status-info-text',\n success: 'border-status-success-border bg-status-success-bg text-status-success-text',\n warning: 'border-status-warning-border bg-status-warning-bg text-status-warning-text',\n error: 'border-status-error-border bg-status-error-bg text-status-error-text',\n};\n\n/**\n * Shown when an action is being linked to another surface (vibe-deck button,\n * scheduler, …). It tells the user which agent the action will run on and, when\n * that agent is unhealthy or no longer exists, warns them right in the linking\n * flow and links to the page where they can set the right agent.\n */\nexport function ActionAgentTargetNotice({ action, className = '' }: ActionAgentTargetNoticeProps) {\n const tr = useTr();\n const navigate = useNavigate();\n\n // Only COMMAND/SCRIPT actions run on an agent — skip the agent + health-probe\n // fetches entirely for webhook/workflow actions where the result is unused.\n const needsAgent = action.actionType === 'COMMAND' || action.actionType === 'SCRIPT';\n const { data: agentsData } = useListAgentsQuery({\n variables: { pagination: { limit: 200 } },\n fetchPolicy: 'cache-and-network',\n skip: !needsAgent,\n });\n const probeQuery = useVibecontrolsAgentHealthProbesQuery({\n fetchPolicy: 'cache-and-network',\n nextFetchPolicy: 'cache-first',\n pollInterval: 60_000,\n skip: !needsAgent,\n });\n const probesLoading = probeQuery.data === undefined;\n\n const agents = useMemo<AgentTargetAgentLike[]>(() => {\n const raw = agentsData?.vibecontrolsAgents ?? [];\n return raw.map((a: NonNullable<typeof raw>[number]) => ({\n id: a.id,\n name: a.name,\n isActive: a.isActive,\n platform: a.platform ?? null,\n metadata: (a.metadata as Record<string, unknown> | null) ?? null,\n }));\n }, [agentsData]);\n\n const probesById = useMemo(() => {\n const m = new Map<string, AgentHealthProbe>();\n for (const p of probeQuery.data?.vibecontrolsAgentHealthProbes ?? []) {\n m.set(p.agentId, { agentId: p.agentId, healthy: p.healthy, errorMessage: p.errorMessage });\n }\n return m;\n }, [probeQuery.data]);\n\n const res = useMemo(\n () => resolveAgentTarget(action, agents, probesById, probesLoading),\n [action, agents, probesById, probesLoading]\n );\n\n const goAgents = () => navigate('/vibecontrols/agents');\n const goAgent = (id: string) => navigate(`/vibecontrols/agents/${encodeURIComponent(id)}`);\n const goEditAction = () =>\n navigate(`/vibecontrols/actions?editAction=${encodeURIComponent(action.id)}`);\n\n // Webhook / workflow actions don't run on an agent.\n if (res.mode === 'no-agent') {\n if (action.actionType === 'WEBHOOK') {\n return (\n <div\n className={`flex items-center gap-2 rounded-lg border border-border-default bg-bg-secondary px-3 py-2 text-xs text-text-tertiary ${className}`}\n >\n <ServerCog size={14} className=\"flex-shrink-0\" />\n <span>\n {tr(\n 'vibecontrols.actions.agentTarget.webhook',\n 'Runs as a webhook request — no agent needed.'\n )}\n </span>\n </div>\n );\n }\n return null;\n }\n\n const LinkButton = ({ label, onClick }: { label: string; onClick: () => void }) => (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"inline-flex items-center gap-0.5 font-medium underline-offset-2 hover:underline\"\n >\n {label}\n <ArrowUpRight size={12} />\n </button>\n );\n\n let tone: Tone = 'info';\n let icon = <ServerCog size={14} className=\"mt-0.5 flex-shrink-0\" />;\n let title = '';\n let body: React.ReactNode = null;\n let links: React.ReactNode = null;\n\n if (res.mode === 'any-available') {\n if (res.noHealthyAgent) {\n tone = 'warning';\n icon = <AlertTriangle size={14} className=\"mt-0.5 flex-shrink-0\" />;\n title = tr('vibecontrols.actions.agentTarget.anyTitle', 'Runs on: any available agent');\n body = tr(\n 'vibecontrols.actions.agentTarget.anyNone',\n 'No healthy agent is available right now, so this action will fail until you add or start one.'\n );\n links = (\n <LinkButton\n label={tr('vibecontrols.actions.agentTarget.manageAgents', 'Manage agents')}\n onClick={goAgents}\n />\n );\n } else {\n tone = 'info';\n title = tr('vibecontrols.actions.agentTarget.anyTitle', 'Runs on: any available agent');\n body = probesLoading\n ? tr('vibecontrols.actions.agentTarget.anyChecking', 'Checking agent availability…')\n : tr(\n 'vibecontrols.actions.agentTarget.anyOk',\n 'A healthy agent is picked automatically when this runs.'\n ) + (res.healthyCount > 0 ? ` (${res.healthyCount} available)` : '');\n }\n } else if (res.missing) {\n tone = 'error';\n icon = <AlertTriangle size={14} className=\"mt-0.5 flex-shrink-0\" />;\n title = tr(\n 'vibecontrols.actions.agentTarget.missingTitle',\n 'The agent set for this action no longer exists'\n );\n body = tr(\n 'vibecontrols.actions.agentTarget.missingBody',\n 'Update the action to choose an available agent, otherwise it will fail when it runs.'\n );\n links = (\n <>\n <LinkButton\n label={tr('vibecontrols.actions.agentTarget.setAgent', 'Set agent')}\n onClick={goEditAction}\n />\n <LinkButton\n label={tr('vibecontrols.actions.agentTarget.manageAgents', 'Manage agents')}\n onClick={goAgents}\n />\n </>\n );\n } else if (res.agent) {\n const healthy = res.healthy;\n tone = healthy ? 'success' : 'warning';\n icon = healthy ? (\n <ServerCog size={14} className=\"mt-0.5 flex-shrink-0\" />\n ) : (\n <AlertTriangle size={14} className=\"mt-0.5 flex-shrink-0\" />\n );\n title = `${tr('vibecontrols.actions.agentTarget.runsOn', 'Runs on')}: ${res.agent.name}`;\n body = healthy\n ? null\n : tr(\n 'vibecontrols.actions.agentTarget.unhealthyBody',\n \"This agent isn't healthy, so the action may fail. Pick a different agent or fix this one before linking.\"\n );\n links = healthy ? null : (\n <>\n <LinkButton\n label={tr('vibecontrols.actions.agentTarget.setAgent', 'Set agent')}\n onClick={goEditAction}\n />\n <LinkButton\n label={tr('vibecontrols.actions.agentTarget.viewAgent', 'View agent')}\n onClick={() => res.agent && goAgent(res.agent.id)}\n />\n </>\n );\n }\n\n return (\n <div className={`rounded-lg border px-3 py-2 text-xs ${TONE_CLASS[tone]} ${className}`}>\n <div className=\"flex items-start gap-2\">\n {icon}\n <div className=\"min-w-0 flex-1 space-y-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"font-medium\">{title}</span>\n {res.mode === 'specific' && res.agent && res.status && (\n <AgentStatusBadge status={res.status} reason={res.reason} />\n )}\n </div>\n {body && <p className=\"opacity-90\">{body}</p>}\n {links && <div className=\"flex flex-wrap items-center gap-3 pt-0.5\">{links}</div>}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ActionAgentTargetNotice.js","names":[],"sources":["../../../src/components/common/ActionAgentTargetNotice.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { AlertTriangle, ArrowUpRight, ServerCog } from 'lucide-react';\n\nimport {\n useListAgentsQuery,\n useVibecontrolsAgentHealthProbesQuery,\n} from '@/generated/wspace-operations';\nimport type { AgentHealthProbe } from '@/utils/agentStatus';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { AgentStatusBadge } from '../agents/AgentStatusBadge';\nimport {\n resolveAgentTarget,\n type AgentTargetActionLike,\n type AgentTargetAgentLike,\n} from './actionAgentTarget';\n\ninterface ActionAgentTargetNoticeProps {\n /** The action being linked. Needs id + type + agent targeting fields. */\n action: AgentTargetActionLike & { id: string };\n className?: string;\n}\n\ntype Tone = 'info' | 'success' | 'warning' | 'error';\n\nconst TONE_CLASS: Record<Tone, string> = {\n info: 'border-border-subtle bg-bg-sunken text-text-secondary',\n success: 'border-status-success-border bg-status-success-bg-subtle text-status-success-text',\n warning: 'border-status-warning-border bg-status-warning-bg-subtle text-status-warning-text',\n error: 'border-status-error-border bg-status-error-bg-subtle text-status-error-text',\n};\n\n/**\n * Shown when an action is being linked to another surface (vibe-deck button,\n * scheduler, …). It tells the user which agent the action will run on and, when\n * that agent is unhealthy or no longer exists, warns them right in the linking\n * flow and links to the page where they can set the right agent.\n */\nexport function ActionAgentTargetNotice({ action, className = '' }: ActionAgentTargetNoticeProps) {\n const tr = useTr();\n const navigate = useNavigate();\n\n // Only COMMAND/SCRIPT actions run on an agent — skip the agent + health-probe\n // fetches entirely for webhook/workflow actions where the result is unused.\n const needsAgent = action.actionType === 'COMMAND' || action.actionType === 'SCRIPT';\n const { data: agentsData } = useListAgentsQuery({\n variables: { pagination: { limit: 200 } },\n fetchPolicy: 'cache-and-network',\n skip: !needsAgent,\n });\n const probeQuery = useVibecontrolsAgentHealthProbesQuery({\n fetchPolicy: 'cache-and-network',\n nextFetchPolicy: 'cache-first',\n pollInterval: 60_000,\n skip: !needsAgent,\n });\n const probesLoading = probeQuery.data === undefined;\n\n const agents = useMemo<AgentTargetAgentLike[]>(() => {\n const raw = agentsData?.vibecontrolsAgents ?? [];\n return raw.map((a: NonNullable<typeof raw>[number]) => ({\n id: a.id,\n name: a.name,\n isActive: a.isActive,\n platform: a.platform ?? null,\n metadata: (a.metadata as Record<string, unknown> | null) ?? null,\n }));\n }, [agentsData]);\n\n const probesById = useMemo(() => {\n const m = new Map<string, AgentHealthProbe>();\n for (const p of probeQuery.data?.vibecontrolsAgentHealthProbes ?? []) {\n m.set(p.agentId, { agentId: p.agentId, healthy: p.healthy, errorMessage: p.errorMessage });\n }\n return m;\n }, [probeQuery.data]);\n\n const res = useMemo(\n () => resolveAgentTarget(action, agents, probesById, probesLoading),\n [action, agents, probesById, probesLoading]\n );\n\n const goAgents = () => navigate('/vibecontrols/agents');\n const goAgent = (id: string) => navigate(`/vibecontrols/agents/${encodeURIComponent(id)}`);\n const goEditAction = () =>\n navigate(`/vibecontrols/actions?editAction=${encodeURIComponent(action.id)}`);\n\n // Webhook / workflow actions don't run on an agent.\n if (res.mode === 'no-agent') {\n if (action.actionType === 'WEBHOOK') {\n return (\n <div\n className={`flex items-center gap-2 rounded-card border border-border-subtle bg-bg-sunken px-3 py-2 text-xs text-text-secondary ${className}`}\n >\n <ServerCog size={14} className=\"flex-shrink-0\" />\n <span>\n {tr(\n 'vibecontrols.actions.agentTarget.webhook',\n 'Runs as a webhook request — no agent needed.'\n )}\n </span>\n </div>\n );\n }\n return null;\n }\n\n const LinkButton = ({ label, onClick }: { label: string; onClick: () => void }) => (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"inline-flex items-center gap-0.5 font-medium underline-offset-2 hover:underline\"\n >\n {label}\n <ArrowUpRight size={12} />\n </button>\n );\n\n let tone: Tone = 'info';\n let icon = <ServerCog size={14} className=\"mt-0.5 flex-shrink-0\" />;\n let title = '';\n let body: React.ReactNode = null;\n let links: React.ReactNode = null;\n\n if (res.mode === 'any-available') {\n if (res.noHealthyAgent) {\n tone = 'warning';\n icon = <AlertTriangle size={14} className=\"mt-0.5 flex-shrink-0\" />;\n title = tr('vibecontrols.actions.agentTarget.anyTitle', 'Runs on: any available agent');\n body = tr(\n 'vibecontrols.actions.agentTarget.anyNone',\n 'No healthy agent is available right now, so this action will fail until you add or start one.'\n );\n links = (\n <LinkButton\n label={tr('vibecontrols.actions.agentTarget.manageAgents', 'Manage agents')}\n onClick={goAgents}\n />\n );\n } else {\n tone = 'info';\n title = tr('vibecontrols.actions.agentTarget.anyTitle', 'Runs on: any available agent');\n body = probesLoading\n ? tr('vibecontrols.actions.agentTarget.anyChecking', 'Checking agent availability…')\n : tr(\n 'vibecontrols.actions.agentTarget.anyOk',\n 'A healthy agent is picked automatically when this runs.'\n ) + (res.healthyCount > 0 ? ` (${res.healthyCount} available)` : '');\n }\n } else if (res.missing) {\n tone = 'error';\n icon = <AlertTriangle size={14} className=\"mt-0.5 flex-shrink-0\" />;\n title = tr(\n 'vibecontrols.actions.agentTarget.missingTitle',\n 'The agent set for this action no longer exists'\n );\n body = tr(\n 'vibecontrols.actions.agentTarget.missingBody',\n 'Update the action to choose an available agent, otherwise it will fail when it runs.'\n );\n links = (\n <>\n <LinkButton\n label={tr('vibecontrols.actions.agentTarget.setAgent', 'Set agent')}\n onClick={goEditAction}\n />\n <LinkButton\n label={tr('vibecontrols.actions.agentTarget.manageAgents', 'Manage agents')}\n onClick={goAgents}\n />\n </>\n );\n } else if (res.agent) {\n const healthy = res.healthy;\n tone = healthy ? 'success' : 'warning';\n icon = healthy ? (\n <ServerCog size={14} className=\"mt-0.5 flex-shrink-0\" />\n ) : (\n <AlertTriangle size={14} className=\"mt-0.5 flex-shrink-0\" />\n );\n title = `${tr('vibecontrols.actions.agentTarget.runsOn', 'Runs on')}: ${res.agent.name}`;\n body = healthy\n ? null\n : tr(\n 'vibecontrols.actions.agentTarget.unhealthyBody',\n \"This agent isn't healthy, so the action may fail. Pick a different agent or fix this one before linking.\"\n );\n links = healthy ? null : (\n <>\n <LinkButton\n label={tr('vibecontrols.actions.agentTarget.setAgent', 'Set agent')}\n onClick={goEditAction}\n />\n <LinkButton\n label={tr('vibecontrols.actions.agentTarget.viewAgent', 'View agent')}\n onClick={() => res.agent && goAgent(res.agent.id)}\n />\n </>\n );\n }\n\n return (\n <div className={`rounded-card border px-3 py-2.5 text-xs ${TONE_CLASS[tone]} ${className}`}>\n <div className=\"flex items-start gap-2\">\n {icon}\n <div className=\"min-w-0 flex-1 space-y-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"font-medium\">{title}</span>\n {res.mode === 'specific' && res.agent && res.status && (\n <AgentStatusBadge status={res.status} reason={res.reason} />\n )}\n </div>\n {body && <p className=\"opacity-90\">{body}</p>}\n {links && <div className=\"flex flex-wrap items-center gap-3 pt-0.5\">{links}</div>}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -15,7 +15,7 @@ var s = t(function({ value: t, defaultValue: s, onChange: c, language: l = "shel
15
15
  scrollbarGutter: "stable"
16
16
  };
17
17
  return /* @__PURE__ */ o("div", {
18
- className: `relative overflow-hidden rounded-md border border-border-default bg-bg-sunken transition-all focus-within:border-transparent focus-within:ring-2 focus-within:ring-action-primary-bg ${u}`,
18
+ className: `relative overflow-hidden rounded-input border border-border-subtle bg-bg-sunken transition-all duration-200 focus-within:border-border-strong focus-within:ring-1 focus-within:ring-[var(--color-focus-ring)] ${u}`,
19
19
  children: [/* @__PURE__ */ a("pre", {
20
20
  ref: b,
21
21
  "aria-hidden": "true",
@@ -1 +1 @@
1
- {"version":3,"file":"CommandEditor.js","names":[],"sources":["../../../src/components/common/CommandEditor.tsx"],"sourcesContent":["import {\n forwardRef,\n useMemo,\n useRef,\n useState,\n type ChangeEvent,\n type CSSProperties,\n type MutableRefObject,\n type TextareaHTMLAttributes,\n type UIEvent,\n} from 'react';\n\nimport { tokenize, type CommandLanguage } from './commandHighlight';\n\ntype NativeTextareaProps = Omit<\n TextareaHTMLAttributes<HTMLTextAreaElement>,\n 'onChange' | 'value' | 'defaultValue' | 'style'\n>;\n\nexport interface CommandEditorProps extends NativeTextareaProps {\n /** Controlled value. Omit (with `defaultValue`) for uncontrolled usage. */\n value?: string;\n defaultValue?: string;\n onChange?: (event: ChangeEvent<HTMLTextAreaElement>) => void;\n /** Highlight language. Defaults to shell. */\n language?: CommandLanguage;\n /** Extra classes for the bordered wrapper (e.g. a different background). */\n className?: string;\n rows?: number;\n /** Inline styles forwarded to the textarea (e.g. minHeight). */\n style?: CSSProperties;\n}\n\n/**\n * A drop-in replacement for a command/script `<textarea>` that renders live\n * syntax highlighting. It overlays a transparent textarea (the real input,\n * which keeps native editing, selection, IME and a11y) on top of a `<pre>` that\n * paints the highlighted tokens. The two layers share an identical box model so\n * the colored text sits exactly under the caret.\n *\n * Works controlled (`value` + `onChange`) or uncontrolled (`defaultValue` + a\n * forwarded `ref`, so callers that read `ref.current.value` keep working).\n */\nexport const CommandEditor = forwardRef<HTMLTextAreaElement, CommandEditorProps>(\n function CommandEditor(\n {\n value,\n defaultValue,\n onChange,\n language = 'shell',\n className = '',\n rows = 3,\n style,\n spellCheck = false,\n ...rest\n },\n forwardedRef\n ) {\n const isControlled = value !== undefined;\n const [internal, setInternal] = useState<string>(defaultValue ?? '');\n const text = isControlled ? (value ?? '') : internal;\n\n const preRef = useRef<HTMLPreElement>(null);\n const textareaRef = useRef<HTMLTextAreaElement | null>(null);\n\n const setRefs = (node: HTMLTextAreaElement | null) => {\n textareaRef.current = node;\n if (typeof forwardedRef === 'function') {\n forwardedRef(node);\n } else if (forwardedRef) {\n (forwardedRef as MutableRefObject<HTMLTextAreaElement | null>).current = node;\n }\n };\n\n const tokens = useMemo(() => tokenize(text, language), [text, language]);\n\n const handleChange = (event: ChangeEvent<HTMLTextAreaElement>) => {\n if (!isControlled) setInternal(event.target.value);\n onChange?.(event);\n };\n\n const syncScroll = (event: UIEvent<HTMLTextAreaElement>) => {\n const pre = preRef.current;\n if (!pre) return;\n pre.scrollTop = event.currentTarget.scrollTop;\n pre.scrollLeft = event.currentTarget.scrollLeft;\n };\n\n // Both layers MUST carry identical typography + box metrics so the painted\n // tokens line up under the caret. `scrollbarGutter: stable` is critical: the\n // textarea is overflow-auto and grows a (gutter-consuming) scrollbar once\n // content overflows the fixed rows, which would otherwise shrink ITS wrap\n // width relative to the overflow-hidden <pre> and drift every wrapped line.\n // Reserving the gutter on both keeps their content-box widths identical in\n // every state, so wrapping — and therefore the highlight overlay — stays aligned.\n const layerClass =\n 'm-0 w-full px-3 py-2 font-mono text-sm leading-relaxed whitespace-pre-wrap break-words';\n const layerStyle: CSSProperties = { tabSize: 2, scrollbarGutter: 'stable' };\n\n return (\n <div\n className={`relative overflow-hidden rounded-md border border-border-default bg-bg-sunken transition-all focus-within:border-transparent focus-within:ring-2 focus-within:ring-action-primary-bg ${className}`}\n >\n <pre\n ref={preRef}\n aria-hidden=\"true\"\n className={`${layerClass} pointer-events-none absolute inset-0 overflow-hidden text-text-primary`}\n style={layerStyle}\n >\n <code>\n {tokens.map((token, index) =>\n token.className ? (\n <span key={index} className={token.className}>\n {token.value}\n </span>\n ) : (\n <span key={index}>{token.value}</span>\n )\n )}\n </code>\n </pre>\n <textarea\n {...rest}\n ref={setRefs}\n rows={rows}\n spellCheck={spellCheck}\n value={isControlled ? text : undefined}\n defaultValue={isControlled ? undefined : defaultValue}\n onChange={handleChange}\n onScroll={syncScroll}\n className={`${layerClass} relative block resize-none overflow-auto bg-transparent text-transparent placeholder:text-text-placeholder focus:outline-none`}\n style={{ ...layerStyle, caretColor: 'var(--color-text-primary)', ...style }}\n />\n </div>\n );\n }\n);\n"],"mappings":";;;;AA2CA,IAAa,IAAgB,EAC3B,SACE,EACE,UACA,iBACA,aACA,cAAW,SACX,eAAY,IACZ,UAAO,GACP,UACA,gBAAa,IACb,GAAG,KAEL,GACA;CACA,IAAM,IAAe,MAAU,KAAA,GACzB,CAAC,GAAU,KAAe,EAAiB,KAAgB,GAAG,EAC9D,IAAO,IAAgB,KAAS,KAAM,GAEtC,IAAS,EAAuB,KAAK,EACrC,IAAc,EAAmC,KAAK,EAEtD,KAAW,MAAqC;AAEpD,EADA,EAAY,UAAU,GAClB,OAAO,KAAiB,aAC1B,EAAa,EAAK,GACT,MACR,EAA8D,UAAU;IAIvE,IAAS,QAAc,EAAS,GAAM,EAAS,EAAE,CAAC,GAAM,EAAS,CAAC,EAElE,KAAgB,MAA4C;AAEhE,EADK,KAAc,EAAY,EAAM,OAAO,MAAM,EAClD,IAAW,EAAM;IAGb,KAAc,MAAwC;EAC1D,IAAM,IAAM,EAAO;AACd,QACL,EAAI,YAAY,EAAM,cAAc,WACpC,EAAI,aAAa,EAAM,cAAc;IAUjC,IACJ,0FACI,IAA4B;EAAE,SAAS;EAAG,iBAAiB;EAAU;AAE3E,QACE,kBAAC,OAAD;EACE,WAAW,wLAAwL;YADrM,CAGE,kBAAC,OAAD;GACE,KAAK;GACL,eAAY;GACZ,WAAW,GAAG,EAAW;GACzB,OAAO;aAEP,kBAAC,QAAD,EAAA,UACG,EAAO,KAAK,GAAO,MAClB,EAAM,YACJ,kBAAC,QAAD;IAAkB,WAAW,EAAM;cAChC,EAAM;IACF,EAFI,EAEJ,GAEP,kBAAC,QAAD,EAAA,UAAmB,EAAM,OAAa,EAA3B,EAA2B,CAEzC,EACI,CAAA;GACH,CAAA,EACN,kBAAC,YAAD;GACE,GAAI;GACJ,KAAK;GACC;GACM;GACZ,OAAO,IAAe,IAAO,KAAA;GAC7B,cAAc,IAAe,KAAA,IAAY;GACzC,UAAU;GACV,UAAU;GACV,WAAW,GAAG,EAAW;GACzB,OAAO;IAAE,GAAG;IAAY,YAAY;IAA6B,GAAG;IAAO;GAC3E,CAAA,CACE;;EAGX"}
1
+ {"version":3,"file":"CommandEditor.js","names":[],"sources":["../../../src/components/common/CommandEditor.tsx"],"sourcesContent":["import {\n forwardRef,\n useMemo,\n useRef,\n useState,\n type ChangeEvent,\n type CSSProperties,\n type MutableRefObject,\n type TextareaHTMLAttributes,\n type UIEvent,\n} from 'react';\n\nimport { tokenize, type CommandLanguage } from './commandHighlight';\n\ntype NativeTextareaProps = Omit<\n TextareaHTMLAttributes<HTMLTextAreaElement>,\n 'onChange' | 'value' | 'defaultValue' | 'style'\n>;\n\nexport interface CommandEditorProps extends NativeTextareaProps {\n /** Controlled value. Omit (with `defaultValue`) for uncontrolled usage. */\n value?: string;\n defaultValue?: string;\n onChange?: (event: ChangeEvent<HTMLTextAreaElement>) => void;\n /** Highlight language. Defaults to shell. */\n language?: CommandLanguage;\n /** Extra classes for the bordered wrapper (e.g. a different background). */\n className?: string;\n rows?: number;\n /** Inline styles forwarded to the textarea (e.g. minHeight). */\n style?: CSSProperties;\n}\n\n/**\n * A drop-in replacement for a command/script `<textarea>` that renders live\n * syntax highlighting. It overlays a transparent textarea (the real input,\n * which keeps native editing, selection, IME and a11y) on top of a `<pre>` that\n * paints the highlighted tokens. The two layers share an identical box model so\n * the colored text sits exactly under the caret.\n *\n * Works controlled (`value` + `onChange`) or uncontrolled (`defaultValue` + a\n * forwarded `ref`, so callers that read `ref.current.value` keep working).\n */\nexport const CommandEditor = forwardRef<HTMLTextAreaElement, CommandEditorProps>(\n function CommandEditor(\n {\n value,\n defaultValue,\n onChange,\n language = 'shell',\n className = '',\n rows = 3,\n style,\n spellCheck = false,\n ...rest\n },\n forwardedRef\n ) {\n const isControlled = value !== undefined;\n const [internal, setInternal] = useState<string>(defaultValue ?? '');\n const text = isControlled ? (value ?? '') : internal;\n\n const preRef = useRef<HTMLPreElement>(null);\n const textareaRef = useRef<HTMLTextAreaElement | null>(null);\n\n const setRefs = (node: HTMLTextAreaElement | null) => {\n textareaRef.current = node;\n if (typeof forwardedRef === 'function') {\n forwardedRef(node);\n } else if (forwardedRef) {\n (forwardedRef as MutableRefObject<HTMLTextAreaElement | null>).current = node;\n }\n };\n\n const tokens = useMemo(() => tokenize(text, language), [text, language]);\n\n const handleChange = (event: ChangeEvent<HTMLTextAreaElement>) => {\n if (!isControlled) setInternal(event.target.value);\n onChange?.(event);\n };\n\n const syncScroll = (event: UIEvent<HTMLTextAreaElement>) => {\n const pre = preRef.current;\n if (!pre) return;\n pre.scrollTop = event.currentTarget.scrollTop;\n pre.scrollLeft = event.currentTarget.scrollLeft;\n };\n\n // Both layers MUST carry identical typography + box metrics so the painted\n // tokens line up under the caret. `scrollbarGutter: stable` is critical: the\n // textarea is overflow-auto and grows a (gutter-consuming) scrollbar once\n // content overflows the fixed rows, which would otherwise shrink ITS wrap\n // width relative to the overflow-hidden <pre> and drift every wrapped line.\n // Reserving the gutter on both keeps their content-box widths identical in\n // every state, so wrapping — and therefore the highlight overlay — stays aligned.\n const layerClass =\n 'm-0 w-full px-3 py-2 font-mono text-sm leading-relaxed whitespace-pre-wrap break-words';\n const layerStyle: CSSProperties = { tabSize: 2, scrollbarGutter: 'stable' };\n\n return (\n <div\n className={`relative overflow-hidden rounded-input border border-border-subtle bg-bg-sunken transition-all duration-200 focus-within:border-border-strong focus-within:ring-1 focus-within:ring-[var(--color-focus-ring)] ${className}`}\n >\n <pre\n ref={preRef}\n aria-hidden=\"true\"\n className={`${layerClass} pointer-events-none absolute inset-0 overflow-hidden text-text-primary`}\n style={layerStyle}\n >\n <code>\n {tokens.map((token, index) =>\n token.className ? (\n <span key={index} className={token.className}>\n {token.value}\n </span>\n ) : (\n <span key={index}>{token.value}</span>\n )\n )}\n </code>\n </pre>\n <textarea\n {...rest}\n ref={setRefs}\n rows={rows}\n spellCheck={spellCheck}\n value={isControlled ? text : undefined}\n defaultValue={isControlled ? undefined : defaultValue}\n onChange={handleChange}\n onScroll={syncScroll}\n className={`${layerClass} relative block resize-none overflow-auto bg-transparent text-transparent placeholder:text-text-placeholder focus:outline-none`}\n style={{ ...layerStyle, caretColor: 'var(--color-text-primary)', ...style }}\n />\n </div>\n );\n }\n);\n"],"mappings":";;;;AA2CA,IAAa,IAAgB,EAC3B,SACE,EACE,UACA,iBACA,aACA,cAAW,SACX,eAAY,IACZ,UAAO,GACP,UACA,gBAAa,IACb,GAAG,KAEL,GACA;CACA,IAAM,IAAe,MAAU,KAAA,GACzB,CAAC,GAAU,KAAe,EAAiB,KAAgB,GAAG,EAC9D,IAAO,IAAgB,KAAS,KAAM,GAEtC,IAAS,EAAuB,KAAK,EACrC,IAAc,EAAmC,KAAK,EAEtD,KAAW,MAAqC;AAEpD,EADA,EAAY,UAAU,GAClB,OAAO,KAAiB,aAC1B,EAAa,EAAK,GACT,MACR,EAA8D,UAAU;IAIvE,IAAS,QAAc,EAAS,GAAM,EAAS,EAAE,CAAC,GAAM,EAAS,CAAC,EAElE,KAAgB,MAA4C;AAEhE,EADK,KAAc,EAAY,EAAM,OAAO,MAAM,EAClD,IAAW,EAAM;IAGb,KAAc,MAAwC;EAC1D,IAAM,IAAM,EAAO;AACd,QACL,EAAI,YAAY,EAAM,cAAc,WACpC,EAAI,aAAa,EAAM,cAAc;IAUjC,IACJ,0FACI,IAA4B;EAAE,SAAS;EAAG,iBAAiB;EAAU;AAE3E,QACE,kBAAC,OAAD;EACE,WAAW,iNAAiN;YAD9N,CAGE,kBAAC,OAAD;GACE,KAAK;GACL,eAAY;GACZ,WAAW,GAAG,EAAW;GACzB,OAAO;aAEP,kBAAC,QAAD,EAAA,UACG,EAAO,KAAK,GAAO,MAClB,EAAM,YACJ,kBAAC,QAAD;IAAkB,WAAW,EAAM;cAChC,EAAM;IACF,EAFI,EAEJ,GAEP,kBAAC,QAAD,EAAA,UAAmB,EAAM,OAAa,EAA3B,EAA2B,CAEzC,EACI,CAAA;GACH,CAAA,EACN,kBAAC,YAAD;GACE,GAAI;GACJ,KAAK;GACC;GACM;GACZ,OAAO,IAAe,IAAO,KAAA;GAC7B,cAAc,IAAe,KAAA,IAAY;GACzC,UAAU;GACV,UAAU;GACV,WAAW,GAAG,EAAW;GACzB,OAAO;IAAE,GAAG;IAAY,YAAY;IAA6B,GAAG;IAAO;GAC3E,CAAA,CACE;;EAGX"}
@@ -23,7 +23,7 @@ function f({ node: e, depth: t, selectedPageId: o, onSelectPage: s, onDeletePage
23
23
  onKeyDown: (t) => {
24
24
  (t.key === "Enter" || t.key === " ") && (t.preventDefault(), s(e.page.id));
25
25
  },
26
- className: `flex items-center gap-1 px-2 py-1.5 cursor-pointer rounded text-sm transition-colors group ${h ? "bg-action-primary-bg/10 text-action-primary-bg" : "text-text-secondary hover:bg-bg-sunken hover:text-text-primary"}`,
26
+ className: `flex items-center gap-1 px-2 py-1.5 cursor-pointer rounded text-sm transition-colors group ${h ? "bg-[var(--color-accent-soft)] text-action-primary-bg" : "text-text-secondary hover:bg-bg-sunken hover:text-text-primary"}`,
27
27
  style: { paddingLeft: `${8 + t * 16}px` },
28
28
  onClick: () => s(e.page.id),
29
29
  children: [
@@ -1 +1 @@
1
- {"version":3,"file":"DocsPageTree.js","names":[],"sources":["../../../src/components/docs/DocsPageTree.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n FileText,\n FolderOpen,\n Plus,\n Trash2,\n ChevronRight,\n ChevronDown,\n Loader2,\n} from 'lucide-react';\nimport type { DocsPage } from './types';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface DocsPageTreeProps {\n pages: DocsPage[];\n selectedPageId: string | null;\n onSelectPage: (pageId: string) => void;\n onAddPage: (parentId?: string, category?: string) => void;\n pageAdding?: boolean;\n onDeletePage: (pageId: string) => void;\n}\n\ninterface TreeNode {\n page: DocsPage;\n children: TreeNode[];\n}\n\nfunction buildTree(pages: DocsPage[]): TreeNode[] {\n const map = new Map<string, TreeNode>();\n const roots: TreeNode[] = [];\n\n for (const page of pages) {\n map.set(page.id, { page, children: [] });\n }\n\n for (const page of pages) {\n const node = map.get(page.id)!;\n if (page.parentId && map.has(page.parentId)) {\n map.get(page.parentId)!.children.push(node);\n } else {\n roots.push(node);\n }\n }\n\n return roots;\n}\n\nfunction TreeItem({\n node,\n depth,\n selectedPageId,\n onSelectPage,\n onDeletePage,\n expandedIds,\n toggleExpand,\n}: {\n node: TreeNode;\n depth: number;\n selectedPageId: string | null;\n onSelectPage: (id: string) => void;\n onDeletePage: (id: string) => void;\n expandedIds: Set<string>;\n toggleExpand: (id: string) => void;\n}) {\n const isSelected = selectedPageId === node.page.id;\n const hasChildren = node.children.length > 0;\n const isExpanded = expandedIds.has(node.page.id);\n\n return (\n <div>\n <div\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (() => onSelectPage(node.page.id))();\n }\n }}\n className={`flex items-center gap-1 px-2 py-1.5 cursor-pointer rounded text-sm transition-colors group ${\n isSelected\n ? 'bg-action-primary-bg/10 text-action-primary-bg'\n : 'text-text-secondary hover:bg-bg-sunken hover:text-text-primary'\n }`}\n style={{ paddingLeft: `${8 + depth * 16}px` }}\n onClick={() => onSelectPage(node.page.id)}\n >\n {hasChildren ? (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n toggleExpand(node.page.id);\n }}\n className=\"p-0.5 -ml-1\"\n >\n {isExpanded ? <ChevronDown className=\"size-3\" /> : <ChevronRight className=\"size-3\" />}\n </button>\n ) : (\n <span className=\"w-4\" />\n )}\n {node.page.category ? (\n <FolderOpen className=\"size-3.5 flex-shrink-0\" />\n ) : (\n <FileText className=\"size-3.5 flex-shrink-0\" />\n )}\n <span className=\"truncate flex-1\">{node.page.title}</span>\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onDeletePage(node.page.id);\n }}\n className=\"opacity-0 group-hover:opacity-100 p-0.5 text-text-muted hover:text-status-error-text transition-all\"\n title=\"Delete\"\n >\n <Trash2 className=\"size-3\" />\n </button>\n </div>\n {hasChildren && isExpanded && (\n <div>\n {node.children.map((child) => (\n <TreeItem\n key={child.page.id}\n node={child}\n depth={depth + 1}\n selectedPageId={selectedPageId}\n onSelectPage={onSelectPage}\n onDeletePage={onDeletePage}\n expandedIds={expandedIds}\n toggleExpand={toggleExpand}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n\nexport function DocsPageTree({\n pages,\n selectedPageId,\n onSelectPage,\n onAddPage,\n pageAdding,\n onDeletePage,\n}: DocsPageTreeProps) {\n const tr = useTr();\n const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set(pages.map((p) => p.id)));\n\n const toggleExpand = (id: string) => {\n setExpandedIds((prev) => {\n const next = new Set(prev);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n };\n\n const tree = buildTree(pages);\n\n return (\n <div className=\"flex flex-col h-full\">\n <div className=\"flex items-center justify-between px-3 py-2 border-b border-border-default\">\n <span className=\"text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.docs.pageTree.pages', 'Pages')}\n </span>\n <button\n type=\"button\"\n onClick={() => onAddPage()}\n disabled={pageAdding}\n className=\"p-1 text-text-secondary hover:text-text-primary transition-colors disabled:opacity-50\"\n title=\"Add page\"\n >\n {pageAdding ? <Loader2 className=\"size-4 animate-spin\" /> : <Plus className=\"size-4\" />}\n </button>\n </div>\n <div className=\"flex-1 overflow-y-auto py-1\">\n {tree.length > 0 ? (\n tree.map((node) => (\n <TreeItem\n key={node.page.id}\n node={node}\n depth={0}\n selectedPageId={selectedPageId}\n onSelectPage={onSelectPage}\n onDeletePage={onDeletePage}\n expandedIds={expandedIds}\n toggleExpand={toggleExpand}\n />\n ))\n ) : (\n <div className=\"p-4 text-center\">\n <p className=\"text-sm text-text-muted mb-2\">\n {tr('vibecontrols.docs.pageTree.noPages', 'No pages yet')}\n </p>\n <button\n type=\"button\"\n onClick={() => onAddPage()}\n className=\"text-sm text-action-primary-bg hover:underline\"\n >\n {tr('vibecontrols.docs.pageTree.addFirstPage', 'Add your first page')}\n </button>\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"DocsPageTree.js","names":[],"sources":["../../../src/components/docs/DocsPageTree.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n FileText,\n FolderOpen,\n Plus,\n Trash2,\n ChevronRight,\n ChevronDown,\n Loader2,\n} from 'lucide-react';\nimport type { DocsPage } from './types';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface DocsPageTreeProps {\n pages: DocsPage[];\n selectedPageId: string | null;\n onSelectPage: (pageId: string) => void;\n onAddPage: (parentId?: string, category?: string) => void;\n pageAdding?: boolean;\n onDeletePage: (pageId: string) => void;\n}\n\ninterface TreeNode {\n page: DocsPage;\n children: TreeNode[];\n}\n\nfunction buildTree(pages: DocsPage[]): TreeNode[] {\n const map = new Map<string, TreeNode>();\n const roots: TreeNode[] = [];\n\n for (const page of pages) {\n map.set(page.id, { page, children: [] });\n }\n\n for (const page of pages) {\n const node = map.get(page.id)!;\n if (page.parentId && map.has(page.parentId)) {\n map.get(page.parentId)!.children.push(node);\n } else {\n roots.push(node);\n }\n }\n\n return roots;\n}\n\nfunction TreeItem({\n node,\n depth,\n selectedPageId,\n onSelectPage,\n onDeletePage,\n expandedIds,\n toggleExpand,\n}: {\n node: TreeNode;\n depth: number;\n selectedPageId: string | null;\n onSelectPage: (id: string) => void;\n onDeletePage: (id: string) => void;\n expandedIds: Set<string>;\n toggleExpand: (id: string) => void;\n}) {\n const isSelected = selectedPageId === node.page.id;\n const hasChildren = node.children.length > 0;\n const isExpanded = expandedIds.has(node.page.id);\n\n return (\n <div>\n <div\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (() => onSelectPage(node.page.id))();\n }\n }}\n className={`flex items-center gap-1 px-2 py-1.5 cursor-pointer rounded text-sm transition-colors group ${\n isSelected\n ? 'bg-[var(--color-accent-soft)] text-action-primary-bg'\n : 'text-text-secondary hover:bg-bg-sunken hover:text-text-primary'\n }`}\n style={{ paddingLeft: `${8 + depth * 16}px` }}\n onClick={() => onSelectPage(node.page.id)}\n >\n {hasChildren ? (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n toggleExpand(node.page.id);\n }}\n className=\"p-0.5 -ml-1\"\n >\n {isExpanded ? <ChevronDown className=\"size-3\" /> : <ChevronRight className=\"size-3\" />}\n </button>\n ) : (\n <span className=\"w-4\" />\n )}\n {node.page.category ? (\n <FolderOpen className=\"size-3.5 flex-shrink-0\" />\n ) : (\n <FileText className=\"size-3.5 flex-shrink-0\" />\n )}\n <span className=\"truncate flex-1\">{node.page.title}</span>\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onDeletePage(node.page.id);\n }}\n className=\"opacity-0 group-hover:opacity-100 p-0.5 text-text-muted hover:text-status-error-text transition-all\"\n title=\"Delete\"\n >\n <Trash2 className=\"size-3\" />\n </button>\n </div>\n {hasChildren && isExpanded && (\n <div>\n {node.children.map((child) => (\n <TreeItem\n key={child.page.id}\n node={child}\n depth={depth + 1}\n selectedPageId={selectedPageId}\n onSelectPage={onSelectPage}\n onDeletePage={onDeletePage}\n expandedIds={expandedIds}\n toggleExpand={toggleExpand}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n\nexport function DocsPageTree({\n pages,\n selectedPageId,\n onSelectPage,\n onAddPage,\n pageAdding,\n onDeletePage,\n}: DocsPageTreeProps) {\n const tr = useTr();\n const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set(pages.map((p) => p.id)));\n\n const toggleExpand = (id: string) => {\n setExpandedIds((prev) => {\n const next = new Set(prev);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n };\n\n const tree = buildTree(pages);\n\n return (\n <div className=\"flex flex-col h-full\">\n <div className=\"flex items-center justify-between px-3 py-2 border-b border-border-default\">\n <span className=\"text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.docs.pageTree.pages', 'Pages')}\n </span>\n <button\n type=\"button\"\n onClick={() => onAddPage()}\n disabled={pageAdding}\n className=\"p-1 text-text-secondary hover:text-text-primary transition-colors disabled:opacity-50\"\n title=\"Add page\"\n >\n {pageAdding ? <Loader2 className=\"size-4 animate-spin\" /> : <Plus className=\"size-4\" />}\n </button>\n </div>\n <div className=\"flex-1 overflow-y-auto py-1\">\n {tree.length > 0 ? (\n tree.map((node) => (\n <TreeItem\n key={node.page.id}\n node={node}\n depth={0}\n selectedPageId={selectedPageId}\n onSelectPage={onSelectPage}\n onDeletePage={onDeletePage}\n expandedIds={expandedIds}\n toggleExpand={toggleExpand}\n />\n ))\n ) : (\n <div className=\"p-4 text-center\">\n <p className=\"text-sm text-text-muted mb-2\">\n {tr('vibecontrols.docs.pageTree.noPages', 'No pages yet')}\n </p>\n <button\n type=\"button\"\n onClick={() => onAddPage()}\n className=\"text-sm text-action-primary-bg hover:underline\"\n >\n {tr('vibecontrols.docs.pageTree.addFirstPage', 'Add your first page')}\n </button>\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -85,7 +85,7 @@ function g({ site: g, pages: _ = [], selectedPageSlug: v = "", onSelectPage: y,
85
85
  })]
86
86
  }),
87
87
  _.length > 1 && N && /* @__PURE__ */ m("div", {
88
- className: "flex items-center gap-1 overflow-x-auto pb-1 border-b border-border-primary",
88
+ className: "flex items-center gap-1 overflow-x-auto pb-1 border-b border-border-default",
89
89
  children: _.map((e, t) => /* @__PURE__ */ m("button", {
90
90
  type: "button",
91
91
  onClick: () => y?.(t === 0 ? "" : e.slug),
@@ -1 +1 @@
1
- {"version":3,"file":"DocsPreviewPanel.js","names":[],"sources":["../../../src/components/docs/DocsPreviewPanel.tsx"],"sourcesContent":["import { useState, type ReactNode } from 'react';\nimport {\n Edit3,\n RefreshCw,\n Settings,\n ExternalLink,\n Loader2,\n AlertTriangle,\n XCircle,\n Link2,\n Check,\n} from 'lucide-react';\nimport { Card } from '@/components/shared';\nimport { DocsCompileStatus } from './DocsCompileStatus';\nimport { createDocsPreviewHtml } from './previewHtml';\nimport type { DocsSite, DocsRevision, DocsPage } from './types';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface DocsPreviewPanelProps {\n site: DocsSite;\n pages?: DocsPage[];\n selectedPageSlug?: string;\n onSelectPage?: (slug: string) => void;\n docsBaseUrl: string | null;\n docsHtml: string | null;\n docsContentLoading?: boolean;\n onEdit: () => void;\n onCompile: () => void;\n onSettings: () => void;\n onGetShareUrl?: () => Promise<string | null>;\n onOpenFullScreen: () => void;\n compileRevision: DocsRevision | null;\n onPollRevision?: (id: string) => Promise<DocsRevision | null>;\n compiling: boolean;\n /** Generic grant-based Share affordance (owner-keyed; supplied by the host). */\n shareSlot?: ReactNode;\n}\n\nexport function DocsPreviewPanel({\n site,\n pages = [],\n selectedPageSlug = '',\n onSelectPage,\n docsBaseUrl,\n docsHtml,\n docsContentLoading,\n onEdit,\n onCompile,\n onSettings,\n onGetShareUrl,\n onOpenFullScreen,\n compileRevision,\n onPollRevision,\n compiling,\n shareSlot,\n}: DocsPreviewPanelProps) {\n const tr = useTr();\n const currentRev = site.currentRevision;\n const [copying, setCopying] = useState(false);\n const [copied, setCopied] = useState(false);\n\n const handleCopyLink = async () => {\n if (!onGetShareUrl) return;\n setCopying(true);\n try {\n const url = await onGetShareUrl();\n if (url) {\n await navigator.clipboard.writeText(url);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n }\n } finally {\n setCopying(false);\n }\n };\n const previewHtml = docsHtml ? createDocsPreviewHtml(docsHtml) : null;\n\n const renderPreviewArea = () => {\n // Inline srcDoc preview: extracts prose content from the compiled HTML and renders\n // it with self-contained typography styles in a sandboxed iframe. The gateway src\n // approach requires the full gateway stack (not available in local dev) and cannot\n // send auth headers from a browser iframe, so srcdoc is the reliable path.\n if (previewHtml && currentRev) {\n return (\n <Card className=\"overflow-hidden\">\n <iframe\n key={`${currentRev.id}-${selectedPageSlug}`}\n srcDoc={previewHtml}\n className=\"w-full border-0\"\n style={{ height: 'calc(100vh - 380px)', minHeight: '400px' }}\n title=\"Documentation Preview\"\n sandbox=\"allow-same-origin allow-forms allow-popups\"\n />\n </Card>\n );\n }\n\n // Revision exists but content is still loading\n if (currentRev && docsContentLoading) {\n return (\n <Card className=\"p-8 text-center\">\n <div className=\"flex flex-col items-center gap-2 text-text-muted\">\n <Loader2 className=\"size-5 animate-spin\" />\n <p className=\"text-sm\">Loading documentation preview…</p>\n </div>\n </Card>\n );\n }\n\n // Revision exists but content failed to load (not loading, not available)\n if (currentRev && !docsContentLoading && !docsHtml) {\n return (\n <Card className=\"p-8 text-center\">\n <div className=\"flex flex-col items-center gap-3 text-text-secondary\">\n <AlertTriangle className=\"size-5 text-status-warning-text\" />\n <p className=\"text-sm\">\n The preview could not be loaded. The site may still be processing, or the build\n artifact may have expired.\n </p>\n <button\n type=\"button\"\n onClick={onCompile}\n disabled={compiling}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md text-sm hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n Recompile Docs\n </button>\n </div>\n </Card>\n );\n }\n\n // Last compile failed with no prior successful revision — show failure prominently\n if (compileRevision?.status === 'FAILED') {\n return (\n <Card className=\"p-8 text-center\">\n <div className=\"flex flex-col items-center gap-3\">\n <XCircle className=\"size-8 text-status-error-text\" />\n <div>\n <p className=\"font-medium text-text-primary mb-1\">Build failed</p>\n {compileRevision.errorMessage && (\n <p className=\"text-sm text-status-error-text bg-status-error-bg rounded px-3 py-2 text-left max-w-md mx-auto mb-3\">\n {compileRevision.errorMessage}\n </p>\n )}\n <p className=\"text-sm text-text-muted\">\n Fix your pages in the editor and try compiling again.\n </p>\n </div>\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={onEdit}\n className=\"px-4 py-2 bg-bg-sunken text-text-primary rounded-md text-sm hover:bg-bg-sunken/80 transition-colors\"\n >\n Edit Pages\n </button>\n <button\n type=\"button\"\n onClick={onCompile}\n disabled={compiling}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md text-sm hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n Retry Compile\n </button>\n </div>\n </div>\n </Card>\n );\n }\n\n // No revision yet — prompt to start editing\n return (\n <Card className=\"p-8 text-center\">\n <p className=\"text-text-secondary mb-1 font-medium\">\n {tr('vibecontrols.docs.preview.noPublished', 'No published documentation yet')}\n </p>\n <p className=\"text-sm text-text-muted mb-4\">\n Add pages in the editor, then click <strong>Compile</strong> to build and publish your\n docs site. Compiled output is stored and served from S3 — compiling takes 1–3 minutes.\n </p>\n <button\n type=\"button\"\n onClick={onEdit}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('vibecontrols.docs.preview.startEditing', 'Start Editing')}\n </button>\n </Card>\n );\n };\n\n return (\n <div className=\"space-y-4\">\n {/* Action bar */}\n <div className=\"flex items-center justify-between flex-wrap gap-2\">\n <div className=\"flex items-center gap-3\">\n <h3 className=\"font-semibold text-text-primary\">{site.name}</h3>\n {currentRev && (\n <span className=\"text-xs text-text-muted bg-bg-sunken px-2 py-0.5 rounded\">\n Rev #{currentRev.revisionNumber}\n </span>\n )}\n <div className=\"relative\">\n <DocsCompileStatus revision={compileRevision} onPoll={onPollRevision} />\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n {shareSlot}\n {currentRev && onGetShareUrl && (\n <button\n type=\"button\"\n onClick={handleCopyLink}\n disabled={copying}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-sm bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken/80 transition-colors disabled:opacity-50\"\n title={\n site.visibility === 'PUBLIC' ? 'Copy public link' : 'Copy signed link (expires)'\n }\n >\n {copied ? (\n <Check className=\"size-3.5 text-status-success-text\" />\n ) : copying ? (\n <Loader2 className=\"size-3.5 animate-spin\" />\n ) : (\n <Link2 className=\"size-3.5\" />\n )}\n {copied ? 'Copied!' : tr('vibecontrols.docs.preview.copyLink', 'Copy Link')}\n </button>\n )}\n {currentRev && (\n <button\n type=\"button\"\n onClick={onOpenFullScreen}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-sm bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken/80 transition-colors\"\n >\n <ExternalLink className=\"size-3.5\" />\n {tr('vibecontrols.docs.preview.openFullScreen', 'Open Full Screen')}\n </button>\n )}\n <button\n type=\"button\"\n onClick={onEdit}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-sm bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken/80 transition-colors\"\n >\n <Edit3 className=\"size-3.5\" />\n {tr('vibecontrols.docs.preview.edit', 'Edit')}\n </button>\n <button\n type=\"button\"\n onClick={onCompile}\n disabled={compiling}\n className=\"flex items-center gap-1.5 px-3 py-1.5 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 <RefreshCw className={`size-3.5 ${compiling ? 'animate-spin' : ''}`} />\n {tr('vibecontrols.docs.preview.compileDocs', 'Compile Docs')}\n </button>\n <button\n type=\"button\"\n onClick={onSettings}\n className=\"p-1.5 text-text-secondary hover:text-text-primary transition-colors\"\n title=\"Site settings\"\n >\n <Settings className=\"size-4\" />\n </button>\n </div>\n </div>\n\n {/* Page navigation tabs — shown when there are multiple compiled pages */}\n {pages.length > 1 && currentRev && (\n <div className=\"flex items-center gap-1 overflow-x-auto pb-1 border-b border-border-primary\">\n {pages.map((page, i) => {\n const isSelected = i === 0 ? selectedPageSlug === '' : selectedPageSlug === page.slug;\n return (\n <button\n type=\"button\"\n key={page.id}\n onClick={() => onSelectPage?.(i === 0 ? '' : page.slug)}\n className={`shrink-0 px-3 py-1.5 text-sm rounded-t-md whitespace-nowrap transition-colors ${\n isSelected\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'text-text-secondary hover:text-text-primary hover:bg-bg-sunken'\n }`}\n >\n {page.title}\n </button>\n );\n })}\n </div>\n )}\n\n {renderPreviewArea()}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"DocsPreviewPanel.js","names":[],"sources":["../../../src/components/docs/DocsPreviewPanel.tsx"],"sourcesContent":["import { useState, type ReactNode } from 'react';\nimport {\n Edit3,\n RefreshCw,\n Settings,\n ExternalLink,\n Loader2,\n AlertTriangle,\n XCircle,\n Link2,\n Check,\n} from 'lucide-react';\nimport { Card } from '@/components/shared';\nimport { DocsCompileStatus } from './DocsCompileStatus';\nimport { createDocsPreviewHtml } from './previewHtml';\nimport type { DocsSite, DocsRevision, DocsPage } from './types';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface DocsPreviewPanelProps {\n site: DocsSite;\n pages?: DocsPage[];\n selectedPageSlug?: string;\n onSelectPage?: (slug: string) => void;\n docsBaseUrl: string | null;\n docsHtml: string | null;\n docsContentLoading?: boolean;\n onEdit: () => void;\n onCompile: () => void;\n onSettings: () => void;\n onGetShareUrl?: () => Promise<string | null>;\n onOpenFullScreen: () => void;\n compileRevision: DocsRevision | null;\n onPollRevision?: (id: string) => Promise<DocsRevision | null>;\n compiling: boolean;\n /** Generic grant-based Share affordance (owner-keyed; supplied by the host). */\n shareSlot?: ReactNode;\n}\n\nexport function DocsPreviewPanel({\n site,\n pages = [],\n selectedPageSlug = '',\n onSelectPage,\n docsBaseUrl,\n docsHtml,\n docsContentLoading,\n onEdit,\n onCompile,\n onSettings,\n onGetShareUrl,\n onOpenFullScreen,\n compileRevision,\n onPollRevision,\n compiling,\n shareSlot,\n}: DocsPreviewPanelProps) {\n const tr = useTr();\n const currentRev = site.currentRevision;\n const [copying, setCopying] = useState(false);\n const [copied, setCopied] = useState(false);\n\n const handleCopyLink = async () => {\n if (!onGetShareUrl) return;\n setCopying(true);\n try {\n const url = await onGetShareUrl();\n if (url) {\n await navigator.clipboard.writeText(url);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n }\n } finally {\n setCopying(false);\n }\n };\n const previewHtml = docsHtml ? createDocsPreviewHtml(docsHtml) : null;\n\n const renderPreviewArea = () => {\n // Inline srcDoc preview: extracts prose content from the compiled HTML and renders\n // it with self-contained typography styles in a sandboxed iframe. The gateway src\n // approach requires the full gateway stack (not available in local dev) and cannot\n // send auth headers from a browser iframe, so srcdoc is the reliable path.\n if (previewHtml && currentRev) {\n return (\n <Card className=\"overflow-hidden\">\n <iframe\n key={`${currentRev.id}-${selectedPageSlug}`}\n srcDoc={previewHtml}\n className=\"w-full border-0\"\n style={{ height: 'calc(100vh - 380px)', minHeight: '400px' }}\n title=\"Documentation Preview\"\n sandbox=\"allow-same-origin allow-forms allow-popups\"\n />\n </Card>\n );\n }\n\n // Revision exists but content is still loading\n if (currentRev && docsContentLoading) {\n return (\n <Card className=\"p-8 text-center\">\n <div className=\"flex flex-col items-center gap-2 text-text-muted\">\n <Loader2 className=\"size-5 animate-spin\" />\n <p className=\"text-sm\">Loading documentation preview…</p>\n </div>\n </Card>\n );\n }\n\n // Revision exists but content failed to load (not loading, not available)\n if (currentRev && !docsContentLoading && !docsHtml) {\n return (\n <Card className=\"p-8 text-center\">\n <div className=\"flex flex-col items-center gap-3 text-text-secondary\">\n <AlertTriangle className=\"size-5 text-status-warning-text\" />\n <p className=\"text-sm\">\n The preview could not be loaded. The site may still be processing, or the build\n artifact may have expired.\n </p>\n <button\n type=\"button\"\n onClick={onCompile}\n disabled={compiling}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md text-sm hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n Recompile Docs\n </button>\n </div>\n </Card>\n );\n }\n\n // Last compile failed with no prior successful revision — show failure prominently\n if (compileRevision?.status === 'FAILED') {\n return (\n <Card className=\"p-8 text-center\">\n <div className=\"flex flex-col items-center gap-3\">\n <XCircle className=\"size-8 text-status-error-text\" />\n <div>\n <p className=\"font-medium text-text-primary mb-1\">Build failed</p>\n {compileRevision.errorMessage && (\n <p className=\"text-sm text-status-error-text bg-status-error-bg rounded px-3 py-2 text-left max-w-md mx-auto mb-3\">\n {compileRevision.errorMessage}\n </p>\n )}\n <p className=\"text-sm text-text-muted\">\n Fix your pages in the editor and try compiling again.\n </p>\n </div>\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={onEdit}\n className=\"px-4 py-2 bg-bg-sunken text-text-primary rounded-md text-sm hover:bg-bg-sunken/80 transition-colors\"\n >\n Edit Pages\n </button>\n <button\n type=\"button\"\n onClick={onCompile}\n disabled={compiling}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md text-sm hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n Retry Compile\n </button>\n </div>\n </div>\n </Card>\n );\n }\n\n // No revision yet — prompt to start editing\n return (\n <Card className=\"p-8 text-center\">\n <p className=\"text-text-secondary mb-1 font-medium\">\n {tr('vibecontrols.docs.preview.noPublished', 'No published documentation yet')}\n </p>\n <p className=\"text-sm text-text-muted mb-4\">\n Add pages in the editor, then click <strong>Compile</strong> to build and publish your\n docs site. Compiled output is stored and served from S3 — compiling takes 1–3 minutes.\n </p>\n <button\n type=\"button\"\n onClick={onEdit}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('vibecontrols.docs.preview.startEditing', 'Start Editing')}\n </button>\n </Card>\n );\n };\n\n return (\n <div className=\"space-y-4\">\n {/* Action bar */}\n <div className=\"flex items-center justify-between flex-wrap gap-2\">\n <div className=\"flex items-center gap-3\">\n <h3 className=\"font-semibold text-text-primary\">{site.name}</h3>\n {currentRev && (\n <span className=\"text-xs text-text-muted bg-bg-sunken px-2 py-0.5 rounded\">\n Rev #{currentRev.revisionNumber}\n </span>\n )}\n <div className=\"relative\">\n <DocsCompileStatus revision={compileRevision} onPoll={onPollRevision} />\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n {shareSlot}\n {currentRev && onGetShareUrl && (\n <button\n type=\"button\"\n onClick={handleCopyLink}\n disabled={copying}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-sm bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken/80 transition-colors disabled:opacity-50\"\n title={\n site.visibility === 'PUBLIC' ? 'Copy public link' : 'Copy signed link (expires)'\n }\n >\n {copied ? (\n <Check className=\"size-3.5 text-status-success-text\" />\n ) : copying ? (\n <Loader2 className=\"size-3.5 animate-spin\" />\n ) : (\n <Link2 className=\"size-3.5\" />\n )}\n {copied ? 'Copied!' : tr('vibecontrols.docs.preview.copyLink', 'Copy Link')}\n </button>\n )}\n {currentRev && (\n <button\n type=\"button\"\n onClick={onOpenFullScreen}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-sm bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken/80 transition-colors\"\n >\n <ExternalLink className=\"size-3.5\" />\n {tr('vibecontrols.docs.preview.openFullScreen', 'Open Full Screen')}\n </button>\n )}\n <button\n type=\"button\"\n onClick={onEdit}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-sm bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken/80 transition-colors\"\n >\n <Edit3 className=\"size-3.5\" />\n {tr('vibecontrols.docs.preview.edit', 'Edit')}\n </button>\n <button\n type=\"button\"\n onClick={onCompile}\n disabled={compiling}\n className=\"flex items-center gap-1.5 px-3 py-1.5 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 <RefreshCw className={`size-3.5 ${compiling ? 'animate-spin' : ''}`} />\n {tr('vibecontrols.docs.preview.compileDocs', 'Compile Docs')}\n </button>\n <button\n type=\"button\"\n onClick={onSettings}\n className=\"p-1.5 text-text-secondary hover:text-text-primary transition-colors\"\n title=\"Site settings\"\n >\n <Settings className=\"size-4\" />\n </button>\n </div>\n </div>\n\n {/* Page navigation tabs — shown when there are multiple compiled pages */}\n {pages.length > 1 && currentRev && (\n <div className=\"flex items-center gap-1 overflow-x-auto pb-1 border-b border-border-default\">\n {pages.map((page, i) => {\n const isSelected = i === 0 ? selectedPageSlug === '' : selectedPageSlug === page.slug;\n return (\n <button\n type=\"button\"\n key={page.id}\n onClick={() => onSelectPage?.(i === 0 ? '' : page.slug)}\n className={`shrink-0 px-3 py-1.5 text-sm rounded-t-md whitespace-nowrap transition-colors ${\n isSelected\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'text-text-secondary hover:text-text-primary hover:bg-bg-sunken'\n }`}\n >\n {page.title}\n </button>\n );\n })}\n </div>\n )}\n\n {renderPreviewArea()}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -5,17 +5,17 @@ import { Fragment as s, jsx as c, jsxs as l } from "react/jsx-runtime";
5
5
  var u = {
6
6
  PUBLISHED: {
7
7
  label: "Published",
8
- className: "bg-status-success-bg text-status-success-text",
8
+ className: "bg-status-success-bg-subtle text-status-success-text",
9
9
  icon: /* @__PURE__ */ c(n, { className: "size-3" })
10
10
  },
11
11
  SUCCEEDED: {
12
12
  label: "Built",
13
- className: "bg-status-info-bg text-status-info-text",
13
+ className: "bg-status-info-bg-subtle text-status-info-text",
14
14
  icon: /* @__PURE__ */ c(n, { className: "size-3" })
15
15
  },
16
16
  FAILED: {
17
17
  label: "Failed",
18
- className: "bg-status-error-bg text-status-error-text",
18
+ className: "bg-status-error-bg-subtle text-status-error-text",
19
19
  icon: /* @__PURE__ */ c(o, { className: "size-3" })
20
20
  },
21
21
  BUILDING: {
@@ -77,7 +77,7 @@ function m({ revisions: n, currentRevisionId: o, onRollback: u, rollingBack: m }
77
77
  children: g.map((e) => {
78
78
  let n = e.id === o, r = !n && (e.status === "SUCCEEDED" || e.status === "PUBLISHED"), h = m === e.id;
79
79
  return /* @__PURE__ */ l("li", {
80
- className: `flex items-center gap-3 px-3 py-2.5 text-sm ${n ? "bg-action-primary-bg/5" : "hover:bg-bg-sunken"}`,
80
+ className: `flex items-center gap-3 px-3 py-2.5 text-sm ${n ? "bg-[var(--color-accent-soft)]" : "hover:bg-bg-sunken"}`,
81
81
  children: [/* @__PURE__ */ l("div", {
82
82
  className: "flex-1 min-w-0",
83
83
  children: [
@@ -1 +1 @@
1
- {"version":3,"file":"DocsRevisionHistory.js","names":[],"sources":["../../../src/components/docs/DocsRevisionHistory.tsx"],"sourcesContent":["import { Check, History, RotateCcw, X, Loader2, AlertCircle } from 'lucide-react';\nimport type { DocsRevision } from './types';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface DocsRevisionHistoryProps {\n revisions: DocsRevision[];\n currentRevisionId: string | null;\n onRollback: (revisionId: string) => Promise<void>;\n rollingBack: string | null;\n}\n\nconst STATUS_CONFIG: Record<string, { label: string; className: string; icon: React.ReactNode }> = {\n PUBLISHED: {\n label: 'Published',\n className: 'bg-status-success-bg text-status-success-text',\n icon: <Check className=\"size-3\" />,\n },\n SUCCEEDED: {\n label: 'Built',\n className: 'bg-status-info-bg text-status-info-text',\n icon: <Check className=\"size-3\" />,\n },\n FAILED: {\n label: 'Failed',\n className: 'bg-status-error-bg text-status-error-text',\n icon: <X className=\"size-3\" />,\n },\n BUILDING: {\n label: 'Building',\n className: 'bg-bg-sunken text-text-secondary',\n icon: <Loader2 className=\"size-3 animate-spin\" />,\n },\n};\n\nfunction StatusBadge({ status }: { status: string }) {\n const config = STATUS_CONFIG[status] ?? {\n label: status,\n className: 'bg-bg-sunken text-text-muted',\n icon: null,\n };\n return (\n <span\n className={`inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] font-medium ${config.className}`}\n >\n {config.icon}\n {config.label}\n </span>\n );\n}\n\nfunction formatDate(iso: string): string {\n return new Date(iso).toLocaleString(undefined, {\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\nfunction formatDuration(ms: number | null): string {\n if (ms == null) return '';\n const s = Math.round(ms / 1000);\n return s < 60 ? `${s}s` : `${Math.floor(s / 60)}m ${s % 60}s`;\n}\n\nexport function DocsRevisionHistory({\n revisions,\n currentRevisionId,\n onRollback,\n rollingBack,\n}: DocsRevisionHistoryProps) {\n const tr = useTr();\n const sorted = [...revisions].sort((a, b) => b.revisionNumber - a.revisionNumber);\n\n if (sorted.length === 0) {\n return (\n <div className=\"p-4 text-center text-sm text-text-muted\">\n {tr('vibecontrols.docs.history.empty', 'No builds yet.')}\n </div>\n );\n }\n\n return (\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2 px-3 py-2 border-b border-border-default\">\n <History className=\"size-4 text-text-secondary\" />\n <span className=\"text-sm font-medium text-text-primary\">\n {tr('vibecontrols.docs.history.title', 'Build history')}\n </span>\n <span className=\"text-xs text-text-muted\">({sorted.length})</span>\n </div>\n <ul className=\"divide-y divide-border-default\">\n {sorted.map((rev) => {\n const isCurrent = rev.id === currentRevisionId;\n const canRollback =\n !isCurrent && (rev.status === 'SUCCEEDED' || rev.status === 'PUBLISHED');\n const isRollingBack = rollingBack === rev.id;\n\n return (\n <li\n key={rev.id}\n className={`flex items-center gap-3 px-3 py-2.5 text-sm ${isCurrent ? 'bg-action-primary-bg/5' : 'hover:bg-bg-sunken'}`}\n >\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <span className=\"font-medium text-text-primary\">#{rev.revisionNumber}</span>\n <StatusBadge status={rev.status} />\n {isCurrent && (\n <span className=\"text-[10px] font-medium px-1.5 py-0.5 rounded bg-action-primary-bg text-action-primary-text\">\n current\n </span>\n )}\n </div>\n <div className=\"flex items-center gap-2 mt-0.5 text-xs text-text-muted\">\n <span>{formatDate(rev.createdAt)}</span>\n {rev.buildDurationMs != null && (\n <>\n <span>·</span>\n <span>{formatDuration(rev.buildDurationMs)}</span>\n </>\n )}\n </div>\n {rev.status === 'FAILED' && rev.errorMessage && (\n <div className=\"mt-1 flex items-start gap-1 text-xs text-status-error-text\">\n <AlertCircle className=\"size-3 mt-0.5 shrink-0\" />\n <span className=\"truncate\">{rev.errorMessage}</span>\n </div>\n )}\n </div>\n {canRollback && (\n <button\n type=\"button\"\n onClick={() => onRollback(rev.id)}\n disabled={!!rollingBack}\n className=\"shrink-0 flex items-center gap-1 px-2 py-1 text-xs text-text-secondary border border-border-default rounded hover:border-action-primary-bg hover:text-action-primary-text-alt transition-colors disabled:opacity-50\"\n title={`Rollback to revision #${rev.revisionNumber}`}\n >\n {isRollingBack ? (\n <Loader2 className=\"size-3 animate-spin\" />\n ) : (\n <RotateCcw className=\"size-3\" />\n )}\n Rollback\n </button>\n )}\n </li>\n );\n })}\n </ul>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"DocsRevisionHistory.js","names":[],"sources":["../../../src/components/docs/DocsRevisionHistory.tsx"],"sourcesContent":["import { Check, History, RotateCcw, X, Loader2, AlertCircle } from 'lucide-react';\nimport type { DocsRevision } from './types';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface DocsRevisionHistoryProps {\n revisions: DocsRevision[];\n currentRevisionId: string | null;\n onRollback: (revisionId: string) => Promise<void>;\n rollingBack: string | null;\n}\n\nconst STATUS_CONFIG: Record<string, { label: string; className: string; icon: React.ReactNode }> = {\n PUBLISHED: {\n label: 'Published',\n className: 'bg-status-success-bg-subtle text-status-success-text',\n icon: <Check className=\"size-3\" />,\n },\n SUCCEEDED: {\n label: 'Built',\n className: 'bg-status-info-bg-subtle text-status-info-text',\n icon: <Check className=\"size-3\" />,\n },\n FAILED: {\n label: 'Failed',\n className: 'bg-status-error-bg-subtle text-status-error-text',\n icon: <X className=\"size-3\" />,\n },\n BUILDING: {\n label: 'Building',\n className: 'bg-bg-sunken text-text-secondary',\n icon: <Loader2 className=\"size-3 animate-spin\" />,\n },\n};\n\nfunction StatusBadge({ status }: { status: string }) {\n const config = STATUS_CONFIG[status] ?? {\n label: status,\n className: 'bg-bg-sunken text-text-muted',\n icon: null,\n };\n return (\n <span\n className={`inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] font-medium ${config.className}`}\n >\n {config.icon}\n {config.label}\n </span>\n );\n}\n\nfunction formatDate(iso: string): string {\n return new Date(iso).toLocaleString(undefined, {\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\nfunction formatDuration(ms: number | null): string {\n if (ms == null) return '';\n const s = Math.round(ms / 1000);\n return s < 60 ? `${s}s` : `${Math.floor(s / 60)}m ${s % 60}s`;\n}\n\nexport function DocsRevisionHistory({\n revisions,\n currentRevisionId,\n onRollback,\n rollingBack,\n}: DocsRevisionHistoryProps) {\n const tr = useTr();\n const sorted = [...revisions].sort((a, b) => b.revisionNumber - a.revisionNumber);\n\n if (sorted.length === 0) {\n return (\n <div className=\"p-4 text-center text-sm text-text-muted\">\n {tr('vibecontrols.docs.history.empty', 'No builds yet.')}\n </div>\n );\n }\n\n return (\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2 px-3 py-2 border-b border-border-default\">\n <History className=\"size-4 text-text-secondary\" />\n <span className=\"text-sm font-medium text-text-primary\">\n {tr('vibecontrols.docs.history.title', 'Build history')}\n </span>\n <span className=\"text-xs text-text-muted\">({sorted.length})</span>\n </div>\n <ul className=\"divide-y divide-border-default\">\n {sorted.map((rev) => {\n const isCurrent = rev.id === currentRevisionId;\n const canRollback =\n !isCurrent && (rev.status === 'SUCCEEDED' || rev.status === 'PUBLISHED');\n const isRollingBack = rollingBack === rev.id;\n\n return (\n <li\n key={rev.id}\n className={`flex items-center gap-3 px-3 py-2.5 text-sm ${isCurrent ? 'bg-[var(--color-accent-soft)]' : 'hover:bg-bg-sunken'}`}\n >\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <span className=\"font-medium text-text-primary\">#{rev.revisionNumber}</span>\n <StatusBadge status={rev.status} />\n {isCurrent && (\n <span className=\"text-[10px] font-medium px-1.5 py-0.5 rounded bg-action-primary-bg text-action-primary-text\">\n current\n </span>\n )}\n </div>\n <div className=\"flex items-center gap-2 mt-0.5 text-xs text-text-muted\">\n <span>{formatDate(rev.createdAt)}</span>\n {rev.buildDurationMs != null && (\n <>\n <span>·</span>\n <span>{formatDuration(rev.buildDurationMs)}</span>\n </>\n )}\n </div>\n {rev.status === 'FAILED' && rev.errorMessage && (\n <div className=\"mt-1 flex items-start gap-1 text-xs text-status-error-text\">\n <AlertCircle className=\"size-3 mt-0.5 shrink-0\" />\n <span className=\"truncate\">{rev.errorMessage}</span>\n </div>\n )}\n </div>\n {canRollback && (\n <button\n type=\"button\"\n onClick={() => onRollback(rev.id)}\n disabled={!!rollingBack}\n className=\"shrink-0 flex items-center gap-1 px-2 py-1 text-xs text-text-secondary border border-border-default rounded hover:border-action-primary-bg hover:text-action-primary-text-alt transition-colors disabled:opacity-50\"\n title={`Rollback to revision #${rev.revisionNumber}`}\n >\n {isRollingBack ? (\n <Loader2 className=\"size-3 animate-spin\" />\n ) : (\n <RotateCcw className=\"size-3\" />\n )}\n Rollback\n </button>\n )}\n </li>\n );\n })}\n </ul>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -113,13 +113,13 @@ function f({ site: f, open: p, onClose: m, onSave: fe }) {
113
113
  }, xe = (e) => {
114
114
  let t = "inline-flex items-center gap-1 px-2 py-0.5 rounded text-[10px] font-medium";
115
115
  return e === "APPROVED" ? /* @__PURE__ */ d("span", {
116
- className: `${t} bg-status-success-bg text-status-success-text`,
116
+ className: `${t} bg-status-success-bg-subtle text-status-success-text`,
117
117
  children: [/* @__PURE__ */ u(o, { className: "size-3" }), "Approved"]
118
118
  }) : e === "VALIDATING" ? /* @__PURE__ */ d("span", {
119
- className: `${t} bg-status-info-bg text-status-info-text`,
119
+ className: `${t} bg-status-info-bg-subtle text-status-info-text`,
120
120
  children: [/* @__PURE__ */ u(ae, { className: "size-3 animate-spin" }), "Validating"]
121
121
  }) : e === "REJECTED" ? /* @__PURE__ */ d("span", {
122
- className: `${t} bg-status-error-bg text-status-error-text`,
122
+ className: `${t} bg-status-error-bg-subtle text-status-error-text`,
123
123
  children: [/* @__PURE__ */ u(le, { className: "size-3" }), "Rejected"]
124
124
  }) : e === "DISABLED" ? /* @__PURE__ */ u("span", {
125
125
  className: `${t} bg-bg-sunken text-text-muted`,
@@ -434,7 +434,7 @@ function f({ site: f, open: p, onClose: m, onSave: fe }) {
434
434
  }) : /* @__PURE__ */ u("ul", {
435
435
  className: "divide-y divide-border-default",
436
436
  children: Q.map((e) => /* @__PURE__ */ u("li", {
437
- className: `p-2 ${e.status === "REJECTED" ? "bg-status-error-bg/30" : ""}`,
437
+ className: `p-2 ${e.status === "REJECTED" ? "bg-status-error-bg-subtle" : ""}`,
438
438
  "data-testid": `docs-build-image-${e.id}`,
439
439
  children: /* @__PURE__ */ d("div", {
440
440
  className: "flex items-start justify-between gap-2",
@@ -451,7 +451,7 @@ function f({ site: f, open: p, onClose: m, onSave: fe }) {
451
451
  children: e.imageRef
452
452
  }),
453
453
  e.lastValidationError && e.status === "REJECTED" && /* @__PURE__ */ d("div", {
454
- className: "mt-1.5 flex items-start gap-1 p-1.5 rounded bg-status-error-bg text-status-error-text",
454
+ className: "mt-1.5 flex items-start gap-1 p-1.5 rounded bg-status-error-bg-subtle text-status-error-text",
455
455
  children: [/* @__PURE__ */ u(re, { className: "size-3 mt-0.5 shrink-0" }), /* @__PURE__ */ u("p", {
456
456
  className: "text-[10px] leading-snug",
457
457
  children: e.lastValidationError
@@ -465,7 +465,7 @@ function f({ site: f, open: p, onClose: m, onSave: fe }) {
465
465
  e.status === "REJECTED" && /* @__PURE__ */ d("button", {
466
466
  type: "button",
467
467
  onClick: () => $(e.id),
468
- className: "flex items-center gap-1 px-2 py-0.5 rounded border border-status-error-text text-[10px] text-status-error-text hover:bg-status-error-bg transition-colors",
468
+ className: "flex items-center gap-1 px-2 py-0.5 rounded border border-status-error-border text-[10px] text-status-error-text hover:bg-status-error-bg-subtle transition-colors",
469
469
  "data-testid": `docs-validate-image-${e.id}`,
470
470
  children: [/* @__PURE__ */ u(ce, { className: "size-2.5" }), "Re-validate"]
471
471
  }),
@@ -493,7 +493,7 @@ function f({ site: f, open: p, onClose: m, onSave: fe }) {
493
493
  ]
494
494
  }),
495
495
  J && /* @__PURE__ */ u("div", {
496
- className: "p-3 bg-status-error-bg text-status-error-text rounded-md text-xs",
496
+ className: "p-3 bg-status-error-bg-subtle text-status-error-text rounded-md text-xs",
497
497
  children: J
498
498
  }),
499
499
  /* @__PURE__ */ u("button", {