@burdenoff/microfe-vibecontrols 2026.623.1 → 2026.623.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (295) hide show
  1. package/dist/components/actions/ActionCard.js +2 -2
  2. package/dist/components/actions/ActionCard.js.map +1 -1
  3. package/dist/components/actions/ActionTypeBadge.js +1 -1
  4. package/dist/components/actions/ActionTypeBadge.js.map +1 -1
  5. package/dist/components/actions/ExecutionHistory.js +1 -1
  6. package/dist/components/actions/ExecutionHistory.js.map +1 -1
  7. package/dist/components/agent-manager/AgentManagerAdminBar.js +1 -1
  8. package/dist/components/agent-manager/AgentManagerAdminBar.js.map +1 -1
  9. package/dist/components/agent-manager/AgentManagerChatPane.js +2 -2
  10. package/dist/components/agent-manager/AgentManagerChatPane.js.map +1 -1
  11. package/dist/components/agent-manager/AgentManagerInputArea.js +4 -4
  12. package/dist/components/agent-manager/AgentManagerInputArea.js.map +1 -1
  13. package/dist/components/agent-manager/AgentManagerLayout.js +1 -1
  14. package/dist/components/agent-manager/AgentManagerLayout.js.map +1 -1
  15. package/dist/components/agent-manager/AgentManagerMessageItem.js +1 -1
  16. package/dist/components/agent-manager/AgentManagerMessageItem.js.map +1 -1
  17. package/dist/components/agent-manager/AgentManagerSidebar.js +5 -5
  18. package/dist/components/agent-manager/AgentManagerSidebar.js.map +1 -1
  19. package/dist/components/agent-manager/AgentManagerTab.js +1 -1
  20. package/dist/components/agent-manager/AgentManagerTab.js.map +1 -1
  21. package/dist/components/agent-manager/AgentManagerTabBar.js +1 -1
  22. package/dist/components/agent-manager/AgentManagerTabBar.js.map +1 -1
  23. package/dist/components/agent-manager/BridleConfigPanel.js +24 -24
  24. package/dist/components/agent-manager/BridleConfigPanel.js.map +1 -1
  25. package/dist/components/agent-manager/PluginInstallModal.js +1 -1
  26. package/dist/components/agent-manager/PluginInstallModal.js.map +1 -1
  27. package/dist/components/agent-manager/ProfilesPanel.js +5 -5
  28. package/dist/components/agent-manager/ProfilesPanel.js.map +1 -1
  29. package/dist/components/agent-manager/controls/AutonomySelector.js +1 -1
  30. package/dist/components/agent-manager/controls/AutonomySelector.js.map +1 -1
  31. package/dist/components/agent-manager/controls/SdkSelector.js +1 -1
  32. package/dist/components/agent-manager/controls/SdkSelector.js.map +1 -1
  33. package/dist/components/agent-manager/controls/VoiceToggle.js +1 -1
  34. package/dist/components/agent-manager/controls/VoiceToggle.js.map +1 -1
  35. package/dist/components/agent-manager/detail/SessionConfigPanel.js +6 -6
  36. package/dist/components/agent-manager/detail/SessionConfigPanel.js.map +1 -1
  37. package/dist/components/agent-manager/detail/SessionLogsPanel.js +5 -5
  38. package/dist/components/agent-manager/detail/SessionLogsPanel.js.map +1 -1
  39. package/dist/components/agent-manager/detail/SessionToolsPanel.js +1 -1
  40. package/dist/components/agent-manager/detail/SessionToolsPanel.js.map +1 -1
  41. package/dist/components/agent-manager/dialogs/NewSessionDialog.js +2 -2
  42. package/dist/components/agent-manager/dialogs/NewSessionDialog.js.map +1 -1
  43. package/dist/components/agent-manager/dialogs/RenameSessionDialog.js +1 -1
  44. package/dist/components/agent-manager/dialogs/RenameSessionDialog.js.map +1 -1
  45. package/dist/components/agents/AgentCard.js +14 -11
  46. package/dist/components/agents/AgentCard.js.map +1 -1
  47. package/dist/components/ai/AIContextsTab.js +1 -1
  48. package/dist/components/ai/AIContextsTab.js.map +1 -1
  49. package/dist/components/ai/AIOverviewTab.js +4 -4
  50. package/dist/components/ai/AIOverviewTab.js.map +1 -1
  51. package/dist/components/ai/AIPlanPanel.js +4 -4
  52. package/dist/components/ai/AIPlanPanel.js.map +1 -1
  53. package/dist/components/ai/AIPlaygroundTab.js +1 -1
  54. package/dist/components/ai/AIPlaygroundTab.js.map +1 -1
  55. package/dist/components/ai/AIPromptsTab.js +2 -2
  56. package/dist/components/ai/AIPromptsTab.js.map +1 -1
  57. package/dist/components/ai/AIProviderCredentialsCard.js +4 -4
  58. package/dist/components/ai/AIProviderCredentialsCard.js.map +1 -1
  59. package/dist/components/ai/AIQueueTab.js +4 -4
  60. package/dist/components/ai/AIQueueTab.js.map +1 -1
  61. package/dist/components/ai/AISessionsTab.js +4 -4
  62. package/dist/components/ai/AISessionsTab.js.map +1 -1
  63. package/dist/components/ai/AITasksTab.js +1 -1
  64. package/dist/components/ai/AITasksTab.js.map +1 -1
  65. package/dist/components/ai/ClaudeCredentialsCard.js +3 -3
  66. package/dist/components/ai/ClaudeCredentialsCard.js.map +1 -1
  67. package/dist/components/ai/PluginHarnessPicker.js +3 -3
  68. package/dist/components/ai/PluginHarnessPicker.js.map +1 -1
  69. package/dist/components/analytics/AnalyticsBreakdownChart.js +8 -8
  70. package/dist/components/analytics/AnalyticsBreakdownChart.js.map +1 -1
  71. package/dist/components/analytics/AnalyticsTimeSeriesChart.js +5 -5
  72. package/dist/components/analytics/AnalyticsTimeSeriesChart.js.map +1 -1
  73. package/dist/components/audit/AuditDeleteDialog.js +2 -2
  74. package/dist/components/audit/AuditDeleteDialog.js.map +1 -1
  75. package/dist/components/audit/AuditExportButton.js +5 -5
  76. package/dist/components/audit/AuditExportButton.js.map +1 -1
  77. package/dist/components/audit/AuditLogDetail.js +9 -9
  78. package/dist/components/audit/AuditLogDetail.js.map +1 -1
  79. package/dist/components/audit/AuditLogFilters.js +1 -1
  80. package/dist/components/audit/AuditLogFilters.js.map +1 -1
  81. package/dist/components/audit/AuditLogStats.js +1 -1
  82. package/dist/components/audit/AuditLogStats.js.map +1 -1
  83. package/dist/components/audit/AuditLogTable.js +28 -28
  84. package/dist/components/audit/AuditLogTable.js.map +1 -1
  85. package/dist/components/catalog/CatalogCard.js +3 -3
  86. package/dist/components/catalog/CatalogCard.js.map +1 -1
  87. package/dist/components/catalog/CatalogForm.js +1 -1
  88. package/dist/components/catalog/CatalogForm.js.map +1 -1
  89. package/dist/components/common/ActionAgentTargetNotice.js +6 -6
  90. package/dist/components/common/ActionAgentTargetNotice.js.map +1 -1
  91. package/dist/components/common/CommandEditor.js +1 -1
  92. package/dist/components/common/CommandEditor.js.map +1 -1
  93. package/dist/components/docs/DocsPageTree.js +1 -1
  94. package/dist/components/docs/DocsPageTree.js.map +1 -1
  95. package/dist/components/docs/DocsPreviewPanel.js +1 -1
  96. package/dist/components/docs/DocsPreviewPanel.js.map +1 -1
  97. package/dist/components/docs/DocsRevisionHistory.js +4 -4
  98. package/dist/components/docs/DocsRevisionHistory.js.map +1 -1
  99. package/dist/components/docs/DocsSettingsPanel.js +7 -7
  100. package/dist/components/docs/DocsSettingsPanel.js.map +1 -1
  101. package/dist/components/docs/DocsTabPanel.js +1 -1
  102. package/dist/components/docs/DocsTabPanel.js.map +1 -1
  103. package/dist/components/graph/ConnectionPermissionsDialog.js +2 -2
  104. package/dist/components/graph/ConnectionPermissionsDialog.js.map +1 -1
  105. package/dist/components/graph/MeshDeployWizard.js +3 -3
  106. package/dist/components/graph/MeshDeployWizard.js.map +1 -1
  107. package/dist/components/graph/MeshRequestPanel.js +2 -2
  108. package/dist/components/graph/MeshRequestPanel.js.map +1 -1
  109. package/dist/components/idp-components/ComponentCard.js +5 -5
  110. package/dist/components/idp-components/ComponentCard.js.map +1 -1
  111. package/dist/components/idp-components/ComponentForm.js +1 -1
  112. package/dist/components/idp-components/ComponentForm.js.map +1 -1
  113. package/dist/components/idp-components/DependencyGraphTab.js +7 -7
  114. package/dist/components/idp-components/DependencyGraphTab.js.map +1 -1
  115. package/dist/components/logs/LogViewer.js +14 -14
  116. package/dist/components/logs/LogViewer.js.map +1 -1
  117. package/dist/components/notes/NoteCard.js +1 -1
  118. package/dist/components/notes/NoteCard.js.map +1 -1
  119. package/dist/components/notes/NoteEditor.js +2 -2
  120. package/dist/components/notes/NoteEditor.js.map +1 -1
  121. package/dist/components/notes/NoteList.js +1 -1
  122. package/dist/components/notes/NoteList.js.map +1 -1
  123. package/dist/components/notes/NotesTabPanel.js +1 -1
  124. package/dist/components/notes/NotesTabPanel.js.map +1 -1
  125. package/dist/components/scorecard/ScorecardEmptyState.js +1 -1
  126. package/dist/components/scorecard/ScorecardEmptyState.js.map +1 -1
  127. package/dist/components/scorecard/ScorecardPanel.js +3 -3
  128. package/dist/components/scorecard/ScorecardPanel.js.map +1 -1
  129. package/dist/components/security/SecurityScanList.js +11 -11
  130. package/dist/components/security/SecurityScanList.js.map +1 -1
  131. package/dist/components/sessions/SessionCard.js +26 -19
  132. package/dist/components/sessions/SessionCard.js.map +1 -1
  133. package/dist/components/sessions/SessionStatusBadge.js +7 -7
  134. package/dist/components/sessions/SessionStatusBadge.js.map +1 -1
  135. package/dist/components/shared/AgentSelector.js +4 -4
  136. package/dist/components/shared/AgentSelector.js.map +1 -1
  137. package/dist/components/shared/AgentSetupBanner.js +4 -4
  138. package/dist/components/shared/AgentSetupBanner.js.map +1 -1
  139. package/dist/components/shared/Card.js +4 -4
  140. package/dist/components/shared/Card.js.map +1 -1
  141. package/dist/components/shared/ErrorState.js +15 -9
  142. package/dist/components/shared/ErrorState.js.map +1 -1
  143. package/dist/components/shared/FilterDropdown.js +2 -2
  144. package/dist/components/shared/FilterDropdown.js.map +1 -1
  145. package/dist/components/shared/PageSkeleton.js +6 -6
  146. package/dist/components/shared/PageSkeleton.js.map +1 -1
  147. package/dist/components/shared/SearchInput.js +1 -1
  148. package/dist/components/shared/SearchInput.js.map +1 -1
  149. package/dist/components/shared/StatCard.js +7 -7
  150. package/dist/components/shared/StatCard.js.map +1 -1
  151. package/dist/components/templates/PublishTemplateForm.js +2 -2
  152. package/dist/components/templates/PublishTemplateForm.js.map +1 -1
  153. package/dist/components/templates/TemplateCard.js +5 -5
  154. package/dist/components/templates/TemplateCard.js.map +1 -1
  155. package/dist/components/templates/TemplateWizard.js +2 -2
  156. package/dist/components/templates/TemplateWizard.js.map +1 -1
  157. package/dist/components/tunnels/DomainVerificationBadge.js +4 -4
  158. package/dist/components/tunnels/DomainVerificationBadge.js.map +1 -1
  159. package/dist/components/tunnels/ProviderBadge.js +1 -1
  160. package/dist/components/tunnels/ProviderBadge.js.map +1 -1
  161. package/dist/components/tunnels/TunnelCard.js +16 -16
  162. package/dist/components/tunnels/TunnelCard.js.map +1 -1
  163. package/dist/components/tunnels/TunnelCreateWizard.js +1 -1
  164. package/dist/components/tunnels/TunnelCreateWizard.js.map +1 -1
  165. package/dist/components/tunnels/TunnelPublicAccessPanel.js +1 -1
  166. package/dist/components/tunnels/TunnelPublicAccessPanel.js.map +1 -1
  167. package/dist/components/tunnels/TunnelStatusBadge.js +4 -4
  168. package/dist/components/tunnels/TunnelStatusBadge.js.map +1 -1
  169. package/dist/components/vibecalendar/LogsDrawer.js +2 -2
  170. package/dist/components/vibecalendar/LogsDrawer.js.map +1 -1
  171. package/dist/components/vibecalendar/ScheduleTaskModal.js +5 -5
  172. package/dist/components/vibecalendar/ScheduleTaskModal.js.map +1 -1
  173. package/dist/components/vibecalendar/VibeCalAgendaView.js +2 -2
  174. package/dist/components/vibecalendar/VibeCalAgendaView.js.map +1 -1
  175. package/dist/components/vibecalendar/VibeCalDayView.js +3 -3
  176. package/dist/components/vibecalendar/VibeCalDayView.js.map +1 -1
  177. package/dist/components/vibecalendar/VibeCalMonthView.js +1 -1
  178. package/dist/components/vibecalendar/VibeCalMonthView.js.map +1 -1
  179. package/dist/components/vibecalendar/VibeCalWeekView.js +4 -4
  180. package/dist/components/vibecalendar/VibeCalWeekView.js.map +1 -1
  181. package/dist/components/vibecalendar/VibeCalendarHeader.js +4 -4
  182. package/dist/components/vibecalendar/VibeCalendarHeader.js.map +1 -1
  183. package/dist/components/vibecalendar/VibeTaskBlock.js +2 -2
  184. package/dist/components/vibecalendar/VibeTaskBlock.js.map +1 -1
  185. package/dist/components/vibecalendar/types.js +7 -7
  186. package/dist/components/vibecalendar/types.js.map +1 -1
  187. package/dist/components/vibedeck/VibeDeckButtonForm.js +9 -9
  188. package/dist/components/vibedeck/VibeDeckButtonForm.js.map +1 -1
  189. package/dist/components/vibedeck/VibeDeckButtonTile.js +6 -6
  190. package/dist/components/vibedeck/VibeDeckButtonTile.js.map +1 -1
  191. package/dist/components/vibedeck/VibeDeckExecutionLog.js +10 -10
  192. package/dist/components/vibedeck/VibeDeckExecutionLog.js.map +1 -1
  193. package/dist/components/vibedeck/VibeDeckForm.js +8 -8
  194. package/dist/components/vibedeck/VibeDeckForm.js.map +1 -1
  195. package/dist/components/vibedeck/VibeDeckGrid.js +2 -2
  196. package/dist/components/vibedeck/VibeDeckGrid.js.map +1 -1
  197. package/dist/components/vibedeck/VibeDeckPublicLinkPanel.js +2 -2
  198. package/dist/components/vibedeck/VibeDeckPublicLinkPanel.js.map +1 -1
  199. package/dist/components/vibedeck/VibeDeckSwitcher.js +8 -8
  200. package/dist/components/vibedeck/VibeDeckSwitcher.js.map +1 -1
  201. package/dist/components/vibes/VibeCard.js +22 -18
  202. package/dist/components/vibes/VibeCard.js.map +1 -1
  203. package/dist/components/vibes/VibeList.js +16 -12
  204. package/dist/components/vibes/VibeList.js.map +1 -1
  205. package/dist/pages/ActionsPage.js +4 -3
  206. package/dist/pages/ActionsPage.js.map +1 -1
  207. package/dist/pages/AgentDetailsPage.js +9 -9
  208. package/dist/pages/AgentDetailsPage.js.map +1 -1
  209. package/dist/pages/AgentsLandingPage.js +4 -4
  210. package/dist/pages/AgentsLandingPage.js.map +1 -1
  211. package/dist/pages/AgentsPage.js +14 -14
  212. package/dist/pages/AgentsPage.js.map +1 -1
  213. package/dist/pages/AnalyticsPage.js +8 -8
  214. package/dist/pages/AnalyticsPage.js.map +1 -1
  215. package/dist/pages/CatalogDetailPage.js +11 -11
  216. package/dist/pages/CatalogDetailPage.js.map +1 -1
  217. package/dist/pages/CatalogPage.js +12 -9
  218. package/dist/pages/CatalogPage.js.map +1 -1
  219. package/dist/pages/ComponentDetailPage.js +7 -7
  220. package/dist/pages/ComponentDetailPage.js.map +1 -1
  221. package/dist/pages/ComponentsPage.js +12 -9
  222. package/dist/pages/ComponentsPage.js.map +1 -1
  223. package/dist/pages/EnvTopologyPage.js +2 -2
  224. package/dist/pages/EnvTopologyPage.js.map +1 -1
  225. package/dist/pages/GettingStartedPage.js +12 -12
  226. package/dist/pages/GettingStartedPage.js.map +1 -1
  227. package/dist/pages/HealthPage.js +12 -12
  228. package/dist/pages/HealthPage.js.map +1 -1
  229. package/dist/pages/LogsPage.js +4 -4
  230. package/dist/pages/LogsPage.js.map +1 -1
  231. package/dist/pages/NoteDetailsPage.js +5 -5
  232. package/dist/pages/NoteDetailsPage.js.map +1 -1
  233. package/dist/pages/OverviewPage.js +76 -43
  234. package/dist/pages/OverviewPage.js.map +1 -1
  235. package/dist/pages/PluginsCatalogPage.js +7 -4
  236. package/dist/pages/PluginsCatalogPage.js.map +1 -1
  237. package/dist/pages/ScheduledJobDetailPage.js +7 -7
  238. package/dist/pages/ScheduledJobDetailPage.js.map +1 -1
  239. package/dist/pages/SessionDetailsPage.js +21 -21
  240. package/dist/pages/SessionDetailsPage.js.map +1 -1
  241. package/dist/pages/SessionsPage.js +20 -20
  242. package/dist/pages/SessionsPage.js.map +1 -1
  243. package/dist/pages/SettingsPage.js +64 -52
  244. package/dist/pages/SettingsPage.js.map +1 -1
  245. package/dist/pages/SharedSessionPage.js +21 -21
  246. package/dist/pages/SharedSessionPage.js.map +1 -1
  247. package/dist/pages/SharedVibeDeckPage.js +5 -5
  248. package/dist/pages/SharedVibeDeckPage.js.map +1 -1
  249. package/dist/pages/TargetDetailsPage.js +56 -65
  250. package/dist/pages/TargetDetailsPage.js.map +1 -1
  251. package/dist/pages/TargetsPage.js +4 -4
  252. package/dist/pages/TargetsPage.js.map +1 -1
  253. package/dist/pages/TemplateDetailPage.js +10 -10
  254. package/dist/pages/TemplateDetailPage.js.map +1 -1
  255. package/dist/pages/TemplatesPage.js +6 -5
  256. package/dist/pages/TemplatesPage.js.map +1 -1
  257. package/dist/pages/VibeAuditPage.js +3 -3
  258. package/dist/pages/VibeAuditPage.js.map +1 -1
  259. package/dist/pages/VibeCalendarPage.js +3 -3
  260. package/dist/pages/VibeCalendarPage.js.map +1 -1
  261. package/dist/pages/VibeDeckPage.js +3 -3
  262. package/dist/pages/VibeDeckPage.js.map +1 -1
  263. package/dist/pages/VibeDetailsPage.js +15 -15
  264. package/dist/pages/VibeDetailsPage.js.map +1 -1
  265. package/dist/pages/VibeTaskDetailPage.js +5 -5
  266. package/dist/pages/VibeTaskDetailPage.js.map +1 -1
  267. package/dist/pages/VibesPage.js +18 -18
  268. package/dist/pages/VibesPage.js.map +1 -1
  269. package/dist/pages/WebhooksPage.js +11 -11
  270. package/dist/pages/WebhooksPage.js.map +1 -1
  271. package/dist/pages/ai/AIBookmarksPage.js +1 -1
  272. package/dist/pages/ai/AIBookmarksPage.js.map +1 -1
  273. package/dist/pages/ai/AISessionDetailPage.js +6 -6
  274. package/dist/pages/ai/AISessionDetailPage.js.map +1 -1
  275. package/dist/pages/tunnels/AuditTab.js +1 -1
  276. package/dist/pages/tunnels/AuditTab.js.map +1 -1
  277. package/dist/pages/tunnels/DomainsTab.js +1 -1
  278. package/dist/pages/tunnels/DomainsTab.js.map +1 -1
  279. package/dist/pages/tunnels/ProvidersTab.js +1 -1
  280. package/dist/pages/tunnels/ProvidersTab.js.map +1 -1
  281. package/dist/pages/tunnels/SessionsTab.js +1 -1
  282. package/dist/pages/tunnels/SessionsTab.js.map +1 -1
  283. package/dist/pages/tunnels/TunnelDetailsPage.js +3 -3
  284. package/dist/pages/tunnels/TunnelDetailsPage.js.map +1 -1
  285. package/dist/pages/tunnels/UsageTab.js +1 -1
  286. package/dist/pages/tunnels/UsageTab.js.map +1 -1
  287. package/dist/pages/tunnels/tabs/TunnelDomainsTab.js +16 -16
  288. package/dist/pages/tunnels/tabs/TunnelDomainsTab.js.map +1 -1
  289. package/dist/pages/tunnels/tabs/TunnelOverviewTab.js +1 -1
  290. package/dist/pages/tunnels/tabs/TunnelOverviewTab.js.map +1 -1
  291. package/dist/pages/tunnels/tabs/TunnelSessionsTab.js +52 -49
  292. package/dist/pages/tunnels/tabs/TunnelSessionsTab.js.map +1 -1
  293. package/dist/pages/tunnels/tabs/TunnelSettingsTab.js +9 -9
  294. package/dist/pages/tunnels/tabs/TunnelSettingsTab.js.map +1 -1
  295. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ComponentForm.js","names":[],"sources":["../../../src/components/idp-components/ComponentForm.tsx"],"sourcesContent":["import { useRef, useState } from 'react';\nimport type { FC, ReactNode } from 'react';\nimport { X, ChevronDown, ChevronRight, Github } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nexport interface ComponentFormData {\n name: string;\n description: string;\n type:\n | 'SERVICE'\n | 'FRONTEND'\n | 'LIBRARY'\n | 'API'\n | 'MOBILE'\n | 'DATABASE'\n | 'INFRASTRUCTURE'\n | 'CUSTOM';\n lifecycle: 'EXPERIMENTAL' | 'ACTIVE' | 'DEPRECATED' | 'END_OF_LIFE';\n language: string;\n framework: string;\n version: string;\n ownerName: string;\n tags: string[];\n repositoryUrl?: string;\n documentationUrl?: string;\n dependencies?: Array<{ name: string; version?: string; type?: string }> | null;\n sbomData?: Record<string, unknown> | null;\n}\n\nexport interface ComponentFormProps {\n isOpen: boolean;\n onClose: () => void;\n onSubmit: (data: ComponentFormData) => Promise<void>;\n initialData?: Partial<ComponentFormData>;\n title?: string;\n /** Optional extra content (e.g. a draft EntityTagPicker) rendered above the footer. */\n extraSection?: ReactNode;\n}\n\nexport const ComponentForm: FC<ComponentFormProps> = ({\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 typeRef = useRef<HTMLSelectElement>(null);\n const lifecycleRef = useRef<HTMLSelectElement>(null);\n const languageRef = useRef<HTMLInputElement>(null);\n const frameworkRef = useRef<HTMLInputElement>(null);\n const versionRef = useRef<HTMLInputElement>(null);\n const ownerNameRef = useRef<HTMLInputElement>(null);\n const tagsRef = useRef<HTMLInputElement>(null);\n const repositoryUrlRef = useRef<HTMLInputElement>(null);\n const documentationUrlRef = useRef<HTMLInputElement>(null);\n const dependenciesRef = useRef<HTMLTextAreaElement>(null);\n const sbomDataRef = useRef<HTMLTextAreaElement>(null);\n\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [fieldErrors, setFieldErrors] = useState<{\n name?: string;\n repositoryUrl?: string;\n dependencies?: string;\n sbomData?: string;\n }>({});\n const [showAdvanced, setShowAdvanced] = useState(false);\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('component.nameRequired', 'Name is required') });\n setError(tr('component.fillRequired', 'Please fill in all required fields'));\n return;\n }\n\n const repoUrl = repositoryUrlRef.current?.value?.trim();\n if (!repoUrl) {\n setFieldErrors({\n repositoryUrl: tr('component.repositoryUrlRequired', 'Repository URL is required'),\n });\n setError(tr('component.fillRequired', 'Please fill in all required fields'));\n return;\n }\n if (!/^https?:\\/\\/.+\\/.+/.test(repoUrl)) {\n setFieldErrors({\n repositoryUrl: tr(\n 'component.repositoryUrlInvalid',\n 'Enter a valid repository URL (e.g. https://github.com/org/repo)'\n ),\n });\n setError(tr('component.fillRequired', 'Please fix the errors above'));\n return;\n }\n\n const newFieldErrors: {\n name?: string;\n repositoryUrl?: string;\n dependencies?: string;\n sbomData?: string;\n } = {};\n\n let parsedDependencies: ComponentFormData['dependencies'] = null;\n const depsRaw = dependenciesRef.current?.value?.trim();\n if (depsRaw) {\n try {\n parsedDependencies = JSON.parse(depsRaw);\n if (!Array.isArray(parsedDependencies)) throw new Error('Must be a JSON array');\n } catch {\n newFieldErrors.dependencies =\n 'Invalid JSON — must be an array like [{\"name\":\"react\",\"version\":\"19.0.0\"}]';\n }\n }\n\n let parsedSbom: ComponentFormData['sbomData'] = null;\n const sbomRaw = sbomDataRef.current?.value?.trim();\n if (sbomRaw) {\n try {\n parsedSbom = JSON.parse(sbomRaw);\n } catch {\n newFieldErrors.sbomData = 'Invalid JSON — paste a valid CycloneDX or SPDX document';\n }\n }\n\n if (Object.keys(newFieldErrors).length > 0) {\n setFieldErrors(newFieldErrors);\n setError(tr('component.fillRequired', 'Please fix the errors above'));\n return;\n }\n\n setFieldErrors({});\n\n const data: ComponentFormData = {\n name,\n description: descriptionRef.current?.value?.trim() || '',\n type: (typeRef.current?.value as ComponentFormData['type']) || 'SERVICE',\n lifecycle: (lifecycleRef.current?.value as ComponentFormData['lifecycle']) || 'EXPERIMENTAL',\n language: languageRef.current?.value?.trim() || '',\n framework: frameworkRef.current?.value?.trim() || '',\n version: versionRef.current?.value?.trim() || '',\n ownerName: ownerNameRef.current?.value?.trim() || '',\n tags: tagsRef.current?.value\n ? tagsRef.current.value\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n : [],\n repositoryUrl: repositoryUrlRef.current?.value?.trim() || undefined,\n documentationUrl: documentationUrlRef.current?.value?.trim() || undefined,\n dependencies: parsedDependencies,\n sbomData: parsedSbom,\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('component.failedSave', 'Failed to save component')\n );\n } finally {\n setLoading(false);\n }\n };\n\n return (\n <div\n className=\"fixed inset-0 flex items-center justify-center z-50 p-4\"\n style={{ backgroundColor: 'rgba(0,0,0,0.55)' }}\n >\n <div className=\"bg-bg-surface rounded-lg p-6 w-full max-w-lg max-h-[90vh] overflow-y-auto\">\n <div className=\"flex items-center justify-between mb-4\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {title || tr('component.createComponent', 'Create Component')}\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('component.nameLabel', 'Name *')}\n </label>\n <input\n aria-label=\"control\"\n ref={nameRef}\n type=\"text\"\n defaultValue={initialData?.name || ''}\n placeholder={tr('component.namePlaceholder', 'my-service')}\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('component.descriptionLabel', 'Description')}\n </label>\n <textarea\n aria-label=\"control\"\n ref={descriptionRef}\n defaultValue={initialData?.description || ''}\n placeholder={tr('component.descriptionPlaceholder', 'Describe this component...')}\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 <span className=\"flex items-center gap-1.5\">\n <Github className=\"size-3.5\" />\n {tr('component.repositoryUrl', 'Repository URL')}{' '}\n <span className=\"text-status-error-text\">*</span>\n </span>\n </label>\n <input\n aria-label=\"https://github.com/org/repo\"\n ref={repositoryUrlRef}\n type=\"url\"\n defaultValue={initialData?.repositoryUrl || ''}\n placeholder=\"https://github.com/org/repo\"\n onChange={() =>\n fieldErrors.repositoryUrl &&\n setFieldErrors((e) => ({ ...e, repositoryUrl: undefined }))\n }\n className={`w-full px-3 py-2 bg-bg-sunken border rounded-md text-text-primary placeholder:text-text-placeholder text-sm ${fieldErrors.repositoryUrl ? 'border-status-error-border' : 'border-border-default'}`}\n />\n {fieldErrors.repositoryUrl && (\n <p className=\"mt-1 text-xs text-status-error-text\">{fieldErrors.repositoryUrl}</p>\n )}\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.typeLabel', 'Type')}\n </label>\n <select\n ref={typeRef}\n defaultValue={initialData?.type || 'SERVICE'}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary\"\n >\n <option value=\"SERVICE\">{tr('component.typeService', 'Service')}</option>\n <option value=\"FRONTEND\">{tr('component.typeFrontend', 'Frontend')}</option>\n <option value=\"LIBRARY\">{tr('component.typeLibrary', 'Library')}</option>\n <option value=\"API\">{tr('component.typeApi', 'API')}</option>\n <option value=\"MOBILE\">{tr('component.typeMobile', 'Mobile')}</option>\n <option value=\"DATABASE\">{tr('component.typeDatabase', 'Database')}</option>\n <option value=\"INFRASTRUCTURE\">\n {tr('component.typeInfrastructure', 'Infrastructure')}\n </option>\n <option value=\"CUSTOM\">{tr('component.typeCustom', 'Custom')}</option>\n </select>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.lifecycleLabel', 'Lifecycle')}\n </label>\n <select\n ref={lifecycleRef}\n defaultValue={initialData?.lifecycle || 'EXPERIMENTAL'}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary\"\n >\n <option value=\"EXPERIMENTAL\">\n {tr('component.lifecycleExperimental', 'Experimental')}\n </option>\n <option value=\"ACTIVE\">{tr('component.lifecycleActive', 'Active')}</option>\n <option value=\"DEPRECATED\">\n {tr('component.lifecycleDeprecated', 'Deprecated')}\n </option>\n <option value=\"END_OF_LIFE\">\n {tr('component.lifecycleEndOfLife', 'End of Life')}\n </option>\n </select>\n </div>\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.languageLabel', 'Language')}\n </label>\n <input\n aria-label=\"control\"\n ref={languageRef}\n type=\"text\"\n defaultValue={initialData?.language || ''}\n placeholder={tr('component.languagePlaceholder', 'TypeScript')}\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('component.frameworkLabel', 'Framework')}\n </label>\n <input\n aria-label=\"control\"\n ref={frameworkRef}\n type=\"text\"\n defaultValue={initialData?.framework || ''}\n placeholder={tr('component.frameworkPlaceholder', 'React')}\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 <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.versionLabel', 'Version')}\n </label>\n <input\n aria-label=\"control\"\n ref={versionRef}\n type=\"text\"\n defaultValue={initialData?.version || ''}\n placeholder={tr('component.versionPlaceholder', '1.0.0')}\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('component.ownerLabel', 'Owner')}\n </label>\n <input\n aria-label=\"control\"\n ref={ownerNameRef}\n type=\"text\"\n defaultValue={initialData?.ownerName || ''}\n placeholder={tr('component.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 <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.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('component.tagsPlaceholder', 'backend, api, graphql')}\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\n {/* Advanced section */}\n <div className=\"border border-border-default rounded-md overflow-hidden\">\n <button\n type=\"button\"\n onClick={() => setShowAdvanced((v) => !v)}\n className=\"w-full flex items-center justify-between px-3 py-2 bg-bg-sunken hover:bg-bg-sunken/80 transition-colors text-sm font-medium text-text-secondary\"\n >\n <span>{tr('component.advancedSection', 'Advanced (Docs, Dependencies, SBOM)')}</span>\n {showAdvanced ? (\n <ChevronDown className=\"size-4\" />\n ) : (\n <ChevronRight className=\"size-4\" />\n )}\n </button>\n {showAdvanced && (\n <div className=\"p-3 space-y-4 border-t border-border-default\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.documentationUrl', 'Documentation URL')}\n </label>\n <input\n aria-label=\"https://docs.example.com\"\n ref={documentationUrlRef}\n type=\"url\"\n defaultValue={initialData?.documentationUrl || ''}\n placeholder=\"https://docs.example.com\"\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder text-sm\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.dependencies', 'Dependencies (JSON array)')}\n </label>\n <textarea\n aria-label=\"control\"\n ref={dependenciesRef}\n rows={4}\n defaultValue={\n initialData?.dependencies\n ? JSON.stringify(initialData.dependencies, null, 2)\n : ''\n }\n placeholder={\n '[\\n {\"name\": \"react\", \"version\": \"19.0.0\", \"type\": \"runtime\"},\\n {\"name\": \"typescript\", \"version\": \"5.9.0\", \"type\": \"dev\"}\\n]'\n }\n className={`w-full px-3 py-2 bg-bg-sunken border rounded-md text-text-primary placeholder:text-text-muted font-mono text-xs resize-y ${fieldErrors.dependencies ? 'border-status-error-border' : 'border-border-default'}`}\n />\n {fieldErrors.dependencies && (\n <p className=\"mt-1 text-xs text-status-error-text\">\n {fieldErrors.dependencies}\n </p>\n )}\n <p className=\"mt-1 text-xs text-text-muted\">\n {tr(\n 'component.dependenciesHint',\n 'Used to build the Dependency Graph in Catalogs. Each entry: {name, version, type}.'\n )}\n </p>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.sbomData', 'SBOM Data (CycloneDX / SPDX JSON)')}\n </label>\n <textarea\n aria-label=\"control\"\n ref={sbomDataRef}\n rows={5}\n defaultValue={\n initialData?.sbomData ? JSON.stringify(initialData.sbomData, null, 2) : ''\n }\n placeholder={\n '{\\n \"bomFormat\": \"CycloneDX\",\\n \"specVersion\": \"1.4\",\\n \"components\": [...]\\n}'\n }\n className={`w-full px-3 py-2 bg-bg-sunken border rounded-md text-text-primary placeholder:text-text-muted font-mono text-xs resize-y ${fieldErrors.sbomData ? 'border-status-error-border' : 'border-border-default'}`}\n />\n {fieldErrors.sbomData && (\n <p className=\"mt-1 text-xs text-status-error-text\">{fieldErrors.sbomData}</p>\n )}\n <p className=\"mt-1 text-xs text-text-muted\">\n {tr(\n 'component.sbomDataHint',\n 'Paste output from syft, trivy, or npm sbom. Aggregated per-catalog in the SBOM tab.'\n )}\n </p>\n </div>\n </div>\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('component.createComponent', 'Create Component')}\n </button>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"ComponentForm.js","names":[],"sources":["../../../src/components/idp-components/ComponentForm.tsx"],"sourcesContent":["import { useRef, useState } from 'react';\nimport type { FC, ReactNode } from 'react';\nimport { X, ChevronDown, ChevronRight, Github } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nexport interface ComponentFormData {\n name: string;\n description: string;\n type:\n | 'SERVICE'\n | 'FRONTEND'\n | 'LIBRARY'\n | 'API'\n | 'MOBILE'\n | 'DATABASE'\n | 'INFRASTRUCTURE'\n | 'CUSTOM';\n lifecycle: 'EXPERIMENTAL' | 'ACTIVE' | 'DEPRECATED' | 'END_OF_LIFE';\n language: string;\n framework: string;\n version: string;\n ownerName: string;\n tags: string[];\n repositoryUrl?: string;\n documentationUrl?: string;\n dependencies?: Array<{ name: string; version?: string; type?: string }> | null;\n sbomData?: Record<string, unknown> | null;\n}\n\nexport interface ComponentFormProps {\n isOpen: boolean;\n onClose: () => void;\n onSubmit: (data: ComponentFormData) => Promise<void>;\n initialData?: Partial<ComponentFormData>;\n title?: string;\n /** Optional extra content (e.g. a draft EntityTagPicker) rendered above the footer. */\n extraSection?: ReactNode;\n}\n\nexport const ComponentForm: FC<ComponentFormProps> = ({\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 typeRef = useRef<HTMLSelectElement>(null);\n const lifecycleRef = useRef<HTMLSelectElement>(null);\n const languageRef = useRef<HTMLInputElement>(null);\n const frameworkRef = useRef<HTMLInputElement>(null);\n const versionRef = useRef<HTMLInputElement>(null);\n const ownerNameRef = useRef<HTMLInputElement>(null);\n const tagsRef = useRef<HTMLInputElement>(null);\n const repositoryUrlRef = useRef<HTMLInputElement>(null);\n const documentationUrlRef = useRef<HTMLInputElement>(null);\n const dependenciesRef = useRef<HTMLTextAreaElement>(null);\n const sbomDataRef = useRef<HTMLTextAreaElement>(null);\n\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [fieldErrors, setFieldErrors] = useState<{\n name?: string;\n repositoryUrl?: string;\n dependencies?: string;\n sbomData?: string;\n }>({});\n const [showAdvanced, setShowAdvanced] = useState(false);\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('component.nameRequired', 'Name is required') });\n setError(tr('component.fillRequired', 'Please fill in all required fields'));\n return;\n }\n\n const repoUrl = repositoryUrlRef.current?.value?.trim();\n if (!repoUrl) {\n setFieldErrors({\n repositoryUrl: tr('component.repositoryUrlRequired', 'Repository URL is required'),\n });\n setError(tr('component.fillRequired', 'Please fill in all required fields'));\n return;\n }\n if (!/^https?:\\/\\/.+\\/.+/.test(repoUrl)) {\n setFieldErrors({\n repositoryUrl: tr(\n 'component.repositoryUrlInvalid',\n 'Enter a valid repository URL (e.g. https://github.com/org/repo)'\n ),\n });\n setError(tr('component.fillRequired', 'Please fix the errors above'));\n return;\n }\n\n const newFieldErrors: {\n name?: string;\n repositoryUrl?: string;\n dependencies?: string;\n sbomData?: string;\n } = {};\n\n let parsedDependencies: ComponentFormData['dependencies'] = null;\n const depsRaw = dependenciesRef.current?.value?.trim();\n if (depsRaw) {\n try {\n parsedDependencies = JSON.parse(depsRaw);\n if (!Array.isArray(parsedDependencies)) throw new Error('Must be a JSON array');\n } catch {\n newFieldErrors.dependencies =\n 'Invalid JSON — must be an array like [{\"name\":\"react\",\"version\":\"19.0.0\"}]';\n }\n }\n\n let parsedSbom: ComponentFormData['sbomData'] = null;\n const sbomRaw = sbomDataRef.current?.value?.trim();\n if (sbomRaw) {\n try {\n parsedSbom = JSON.parse(sbomRaw);\n } catch {\n newFieldErrors.sbomData = 'Invalid JSON — paste a valid CycloneDX or SPDX document';\n }\n }\n\n if (Object.keys(newFieldErrors).length > 0) {\n setFieldErrors(newFieldErrors);\n setError(tr('component.fillRequired', 'Please fix the errors above'));\n return;\n }\n\n setFieldErrors({});\n\n const data: ComponentFormData = {\n name,\n description: descriptionRef.current?.value?.trim() || '',\n type: (typeRef.current?.value as ComponentFormData['type']) || 'SERVICE',\n lifecycle: (lifecycleRef.current?.value as ComponentFormData['lifecycle']) || 'EXPERIMENTAL',\n language: languageRef.current?.value?.trim() || '',\n framework: frameworkRef.current?.value?.trim() || '',\n version: versionRef.current?.value?.trim() || '',\n ownerName: ownerNameRef.current?.value?.trim() || '',\n tags: tagsRef.current?.value\n ? tagsRef.current.value\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n : [],\n repositoryUrl: repositoryUrlRef.current?.value?.trim() || undefined,\n documentationUrl: documentationUrlRef.current?.value?.trim() || undefined,\n dependencies: parsedDependencies,\n sbomData: parsedSbom,\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('component.failedSave', 'Failed to save component')\n );\n } finally {\n setLoading(false);\n }\n };\n\n return (\n <div\n className=\"fixed inset-0 flex items-center justify-center z-50 p-4\"\n style={{ backgroundColor: 'rgba(0,0,0,0.55)' }}\n >\n <div className=\"bg-bg-surface rounded-lg p-6 w-full max-w-lg max-h-[90vh] overflow-y-auto\">\n <div className=\"flex items-center justify-between mb-4\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {title || tr('component.createComponent', 'Create Component')}\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('component.nameLabel', 'Name *')}\n </label>\n <input\n aria-label=\"control\"\n ref={nameRef}\n type=\"text\"\n defaultValue={initialData?.name || ''}\n placeholder={tr('component.namePlaceholder', 'my-service')}\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('component.descriptionLabel', 'Description')}\n </label>\n <textarea\n aria-label=\"control\"\n ref={descriptionRef}\n defaultValue={initialData?.description || ''}\n placeholder={tr('component.descriptionPlaceholder', 'Describe this component...')}\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 <span className=\"flex items-center gap-1.5\">\n <Github className=\"size-3.5\" />\n {tr('component.repositoryUrl', 'Repository URL')}{' '}\n <span className=\"text-status-error-text\">*</span>\n </span>\n </label>\n <input\n aria-label=\"https://github.com/org/repo\"\n ref={repositoryUrlRef}\n type=\"url\"\n defaultValue={initialData?.repositoryUrl || ''}\n placeholder=\"https://github.com/org/repo\"\n onChange={() =>\n fieldErrors.repositoryUrl &&\n setFieldErrors((e) => ({ ...e, repositoryUrl: undefined }))\n }\n className={`w-full px-3 py-2 bg-bg-sunken border rounded-md text-text-primary placeholder:text-text-placeholder text-sm ${fieldErrors.repositoryUrl ? 'border-status-error-border' : 'border-border-default'}`}\n />\n {fieldErrors.repositoryUrl && (\n <p className=\"mt-1 text-xs text-status-error-text\">{fieldErrors.repositoryUrl}</p>\n )}\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.typeLabel', 'Type')}\n </label>\n <select\n ref={typeRef}\n defaultValue={initialData?.type || 'SERVICE'}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary\"\n >\n <option value=\"SERVICE\">{tr('component.typeService', 'Service')}</option>\n <option value=\"FRONTEND\">{tr('component.typeFrontend', 'Frontend')}</option>\n <option value=\"LIBRARY\">{tr('component.typeLibrary', 'Library')}</option>\n <option value=\"API\">{tr('component.typeApi', 'API')}</option>\n <option value=\"MOBILE\">{tr('component.typeMobile', 'Mobile')}</option>\n <option value=\"DATABASE\">{tr('component.typeDatabase', 'Database')}</option>\n <option value=\"INFRASTRUCTURE\">\n {tr('component.typeInfrastructure', 'Infrastructure')}\n </option>\n <option value=\"CUSTOM\">{tr('component.typeCustom', 'Custom')}</option>\n </select>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.lifecycleLabel', 'Lifecycle')}\n </label>\n <select\n ref={lifecycleRef}\n defaultValue={initialData?.lifecycle || 'EXPERIMENTAL'}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary\"\n >\n <option value=\"EXPERIMENTAL\">\n {tr('component.lifecycleExperimental', 'Experimental')}\n </option>\n <option value=\"ACTIVE\">{tr('component.lifecycleActive', 'Active')}</option>\n <option value=\"DEPRECATED\">\n {tr('component.lifecycleDeprecated', 'Deprecated')}\n </option>\n <option value=\"END_OF_LIFE\">\n {tr('component.lifecycleEndOfLife', 'End of Life')}\n </option>\n </select>\n </div>\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.languageLabel', 'Language')}\n </label>\n <input\n aria-label=\"control\"\n ref={languageRef}\n type=\"text\"\n defaultValue={initialData?.language || ''}\n placeholder={tr('component.languagePlaceholder', 'TypeScript')}\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('component.frameworkLabel', 'Framework')}\n </label>\n <input\n aria-label=\"control\"\n ref={frameworkRef}\n type=\"text\"\n defaultValue={initialData?.framework || ''}\n placeholder={tr('component.frameworkPlaceholder', 'React')}\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 <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.versionLabel', 'Version')}\n </label>\n <input\n aria-label=\"control\"\n ref={versionRef}\n type=\"text\"\n defaultValue={initialData?.version || ''}\n placeholder={tr('component.versionPlaceholder', '1.0.0')}\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('component.ownerLabel', 'Owner')}\n </label>\n <input\n aria-label=\"control\"\n ref={ownerNameRef}\n type=\"text\"\n defaultValue={initialData?.ownerName || ''}\n placeholder={tr('component.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 <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.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('component.tagsPlaceholder', 'backend, api, graphql')}\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\n {/* Advanced section */}\n <div className=\"border border-border-default rounded-md overflow-hidden\">\n <button\n type=\"button\"\n onClick={() => setShowAdvanced((v) => !v)}\n className=\"w-full flex items-center justify-between px-3 py-2 bg-bg-sunken hover:bg-bg-sunken/80 transition-colors text-sm font-medium text-text-secondary\"\n >\n <span>{tr('component.advancedSection', 'Advanced (Docs, Dependencies, SBOM)')}</span>\n {showAdvanced ? (\n <ChevronDown className=\"size-4\" />\n ) : (\n <ChevronRight className=\"size-4\" />\n )}\n </button>\n {showAdvanced && (\n <div className=\"p-3 space-y-4 border-t border-border-default\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.documentationUrl', 'Documentation URL')}\n </label>\n <input\n aria-label=\"https://docs.example.com\"\n ref={documentationUrlRef}\n type=\"url\"\n defaultValue={initialData?.documentationUrl || ''}\n placeholder=\"https://docs.example.com\"\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder text-sm\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.dependencies', 'Dependencies (JSON array)')}\n </label>\n <textarea\n aria-label=\"control\"\n ref={dependenciesRef}\n rows={4}\n defaultValue={\n initialData?.dependencies\n ? JSON.stringify(initialData.dependencies, null, 2)\n : ''\n }\n placeholder={\n '[\\n {\"name\": \"react\", \"version\": \"19.0.0\", \"type\": \"runtime\"},\\n {\"name\": \"typescript\", \"version\": \"5.9.0\", \"type\": \"dev\"}\\n]'\n }\n className={`w-full px-3 py-2 bg-bg-sunken border rounded-md text-text-primary placeholder:text-text-muted font-mono text-xs resize-y ${fieldErrors.dependencies ? 'border-status-error-border' : 'border-border-default'}`}\n />\n {fieldErrors.dependencies && (\n <p className=\"mt-1 text-xs text-status-error-text\">\n {fieldErrors.dependencies}\n </p>\n )}\n <p className=\"mt-1 text-xs text-text-muted\">\n {tr(\n 'component.dependenciesHint',\n 'Used to build the Dependency Graph in Catalogs. Each entry: {name, version, type}.'\n )}\n </p>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.sbomData', 'SBOM Data (CycloneDX / SPDX JSON)')}\n </label>\n <textarea\n aria-label=\"control\"\n ref={sbomDataRef}\n rows={5}\n defaultValue={\n initialData?.sbomData ? JSON.stringify(initialData.sbomData, null, 2) : ''\n }\n placeholder={\n '{\\n \"bomFormat\": \"CycloneDX\",\\n \"specVersion\": \"1.4\",\\n \"components\": [...]\\n}'\n }\n className={`w-full px-3 py-2 bg-bg-sunken border rounded-md text-text-primary placeholder:text-text-muted font-mono text-xs resize-y ${fieldErrors.sbomData ? 'border-status-error-border' : 'border-border-default'}`}\n />\n {fieldErrors.sbomData && (\n <p className=\"mt-1 text-xs text-status-error-text\">{fieldErrors.sbomData}</p>\n )}\n <p className=\"mt-1 text-xs text-text-muted\">\n {tr(\n 'component.sbomDataHint',\n 'Paste output from syft, trivy, or npm sbom. Aggregated per-catalog in the SBOM tab.'\n )}\n </p>\n </div>\n </div>\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('component.createComponent', 'Create Component')}\n </button>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -45,12 +45,12 @@ function p(e) {
45
45
  case "peer": return {
46
46
  ...t,
47
47
  backgroundColor: "var(--color-status-info-border)",
48
- color: "#fff"
48
+ color: "var(--color-text-inverse)"
49
49
  };
50
50
  case "dev": return {
51
51
  ...t,
52
52
  backgroundColor: "var(--color-border-strong)",
53
- color: "#fff"
53
+ color: "var(--color-text-inverse)"
54
54
  };
55
55
  case "optional": return {
56
56
  ...t,
@@ -60,7 +60,7 @@ function p(e) {
60
60
  default: return {
61
61
  ...t,
62
62
  backgroundColor: "var(--color-action-primary-bg)",
63
- color: "#fff"
63
+ color: "var(--color-action-primary-text)"
64
64
  };
65
65
  }
66
66
  }
@@ -106,7 +106,7 @@ function h(e, t) {
106
106
  style: {
107
107
  fontSize: 10,
108
108
  marginTop: 3,
109
- backgroundColor: "rgba(255,255,255,0.2)",
109
+ backgroundColor: "var(--color-accent-soft)",
110
110
  borderRadius: 4,
111
111
  padding: "1px 6px",
112
112
  display: "inline-block",
@@ -128,13 +128,13 @@ function g(e, t, n) {
128
128
  data: { label: h(e, t) },
129
129
  style: {
130
130
  backgroundColor: "var(--color-action-primary-bg)",
131
- color: "#fff",
131
+ color: "var(--color-action-primary-text)",
132
132
  borderRadius: 10,
133
133
  padding: "10px 16px",
134
134
  width: 160,
135
135
  fontWeight: 700,
136
136
  fontSize: 13,
137
- boxShadow: "0 2px 8px rgba(0,0,0,0.15)",
137
+ boxShadow: "var(--shadow-elevation-2)",
138
138
  border: "none",
139
139
  textAlign: "center"
140
140
  }
@@ -178,7 +178,7 @@ function _({ componentName: t, componentType: s, deps: d, canvasHeight: f }) {
178
178
  d
179
179
  ]), [h, , _] = u(p), [v, , y] = l(m);
180
180
  return /* @__PURE__ */ n("div", {
181
- className: "w-full rounded-lg border border-border-default overflow-hidden bg-bg-secondary",
181
+ className: "w-full rounded-lg border border-border-default overflow-hidden bg-bg-surface",
182
182
  style: { height: f },
183
183
  children: /* @__PURE__ */ r(c, {
184
184
  nodes: h,
@@ -1 +1 @@
1
- {"version":3,"file":"DependencyGraphTab.js","names":[],"sources":["../../../src/components/idp-components/DependencyGraphTab.tsx"],"sourcesContent":["/**\n * DependencyGraphTab — React Flow visualization of a component's dependency graph.\n *\n * Root component node sits on the RIGHT; dependency nodes stack on the LEFT.\n * Edges flow LEFT → RIGHT with type-specific styling.\n * A compact table below the canvas lists all dependencies.\n */\n\nimport { useMemo } from 'react';\nimport { PackageOpen } from 'lucide-react';\nimport '@xyflow/react/dist/style.css';\nimport {\n ReactFlow,\n Background,\n Controls,\n useNodesState,\n useEdgesState,\n type Node,\n type Edge,\n MarkerType,\n BackgroundVariant,\n} from '@xyflow/react';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface DependencyGraphTabProps {\n componentName: string;\n componentType: string;\n dependencies: Array<{ name: string; version?: string | null; type?: string | null }> | null;\n}\n\ntype DepType = 'runtime' | 'dev' | 'peer' | 'optional' | null;\n\nconst MAX_GRAPH_DEPS = 40;\n\n// ---------------------------------------------------------------------------\n// Inline style helpers (React Flow nodes render in a portal — no Tailwind)\n// ---------------------------------------------------------------------------\n\nfunction depNodeStyle(type: DepType): React.CSSProperties {\n switch (type) {\n case 'peer':\n return {\n backgroundColor: 'var(--color-status-info-bg)',\n border: '1px solid var(--color-status-info-border)',\n color: 'var(--color-text-primary)',\n };\n case 'dev':\n return {\n backgroundColor: 'var(--color-bg-sunken)',\n border: '1px dashed var(--color-border-default)',\n color: 'var(--color-text-secondary)',\n };\n case 'optional':\n return {\n backgroundColor: 'var(--color-bg-sunken)',\n border: '1px solid var(--color-border-default)',\n color: 'var(--color-text-primary)',\n opacity: 0.7,\n };\n default:\n // runtime or null\n return {\n backgroundColor: 'var(--color-bg-sunken)',\n border: '1px solid var(--color-border-default)',\n color: 'var(--color-text-primary)',\n };\n }\n}\n\nfunction depTypeBadgeStyle(type: DepType): React.CSSProperties {\n const base: React.CSSProperties = {\n display: 'inline-block',\n fontSize: 10,\n fontWeight: 600,\n borderRadius: 4,\n padding: '1px 5px',\n marginLeft: 4,\n textTransform: 'uppercase',\n letterSpacing: '0.04em',\n };\n switch (type) {\n case 'peer':\n return { ...base, backgroundColor: 'var(--color-status-info-border)', color: '#fff' };\n case 'dev':\n return { ...base, backgroundColor: 'var(--color-border-strong)', color: '#fff' };\n case 'optional':\n return {\n ...base,\n backgroundColor: 'var(--color-border-default)',\n color: 'var(--color-text-secondary)',\n };\n default:\n return { ...base, backgroundColor: 'var(--color-action-primary-bg)', color: '#fff' };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Label renderers (ReactNode — React Flow default node accepts ReactNode label)\n// ---------------------------------------------------------------------------\n\nfunction makeDepLabel(dep: {\n name: string;\n version?: string | null;\n type?: string | null;\n}): React.ReactNode {\n const depType = (dep.type ?? null) as DepType;\n return (\n <div style={{ lineHeight: 1.4 }}>\n <div\n style={{\n fontWeight: 600,\n maxWidth: 180,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {dep.name}\n {depType && <span style={depTypeBadgeStyle(depType)}>{depType}</span>}\n </div>\n {dep.version && (\n <div style={{ fontSize: 10, color: 'var(--color-text-muted)', marginTop: 2 }}>\n {dep.version}\n </div>\n )}\n </div>\n );\n}\n\nfunction makeRootLabel(componentName: string, componentType: string): React.ReactNode {\n return (\n <div style={{ lineHeight: 1.4, textAlign: 'center' }}>\n <div style={{ fontWeight: 700, fontSize: 13 }}>{componentName}</div>\n <div\n style={{\n fontSize: 10,\n marginTop: 3,\n backgroundColor: 'rgba(255,255,255,0.2)',\n borderRadius: 4,\n padding: '1px 6px',\n display: 'inline-block',\n letterSpacing: '0.05em',\n textTransform: 'uppercase',\n }}\n >\n {componentType}\n </div>\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Graph element builder\n// ---------------------------------------------------------------------------\n\nfunction buildGraphElements(\n componentName: string,\n componentType: string,\n deps: Array<{ name: string; version?: string | null; type?: string | null }>\n): { nodes: Node[]; edges: Edge[] } {\n const visible = deps.slice(0, MAX_GRAPH_DEPS);\n const totalHeight = Math.max(visible.length * 80, 80);\n const rootY = totalHeight / 2 - 24;\n\n const nodes: Node[] = [];\n const edges: Edge[] = [];\n\n // Root node\n nodes.push({\n id: 'root',\n position: { x: 520, y: rootY },\n data: { label: makeRootLabel(componentName, componentType) },\n style: {\n backgroundColor: 'var(--color-action-primary-bg)',\n color: '#fff',\n borderRadius: 10,\n padding: '10px 16px',\n width: 160,\n fontWeight: 700,\n fontSize: 13,\n boxShadow: '0 2px 8px rgba(0,0,0,0.15)',\n border: 'none',\n textAlign: 'center',\n },\n });\n\n // Dependency nodes and edges\n visible.forEach((dep, i) => {\n const depType = (dep.type ?? null) as DepType;\n const nodeId = `dep-${i}`;\n const isRuntime = !depType || depType === 'runtime';\n const isDev = depType === 'dev';\n\n nodes.push({\n id: nodeId,\n position: { x: 40, y: 20 + i * 80 },\n data: { label: makeDepLabel(dep) },\n style: {\n ...depNodeStyle(depType),\n borderRadius: 8,\n padding: '8px 12px',\n width: 220,\n fontSize: 12,\n },\n });\n\n edges.push({\n id: `edge-${i}`,\n source: nodeId,\n target: 'root',\n animated: isRuntime,\n markerEnd: { type: MarkerType.ArrowClosed },\n style: {\n stroke: 'var(--color-border-strong)',\n strokeWidth: 1.5,\n ...(isDev ? { strokeDasharray: '4 2' } : {}),\n },\n });\n });\n\n return { nodes, edges };\n}\n\n// ---------------------------------------------------------------------------\n// Inner React Flow component (needs its own hook calls — can't be conditional)\n// ---------------------------------------------------------------------------\n\ninterface DependencyFlowProps {\n componentName: string;\n componentType: string;\n deps: Array<{ name: string; version?: string | null; type?: string | null }>;\n canvasHeight: number;\n}\n\nfunction DependencyFlow({ componentName, componentType, deps, canvasHeight }: DependencyFlowProps) {\n const { nodes: initialNodes, edges: initialEdges } = useMemo(\n () => buildGraphElements(componentName, componentType, deps),\n [componentName, componentType, deps]\n );\n\n const [nodes, , onNodesChange] = useNodesState(initialNodes);\n const [edges, , onEdgesChange] = useEdgesState(initialEdges);\n\n return (\n <div\n className=\"w-full rounded-lg border border-border-default overflow-hidden bg-bg-secondary\"\n style={{ height: canvasHeight }}\n >\n <ReactFlow\n nodes={nodes}\n edges={edges}\n onNodesChange={onNodesChange}\n onEdgesChange={onEdgesChange}\n nodesDraggable={false}\n fitView\n fitViewOptions={{ padding: 0.15 }}\n minZoom={0.2}\n maxZoom={2}\n panOnDrag\n proOptions={{ hideAttribution: true }}\n >\n <Background\n variant={BackgroundVariant.Dots}\n gap={20}\n size={1}\n className=\"!fill-text-tertiary/10\"\n />\n <Controls\n showInteractive={false}\n style={{\n background: 'var(--color-bg-primary)',\n border: '1px solid var(--color-border-default)',\n borderRadius: 8,\n boxShadow: '0 1px 4px var(--color-border-default)',\n }}\n className=\"[&_button]:!bg-[var(--color-bg-primary)] [&_button]:!border-[var(--color-border-default)] [&_button]:!fill-[var(--color-text-secondary)] [&_button:hover]:!bg-[var(--color-bg-sunken)]\"\n />\n </ReactFlow>\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Main export\n// ---------------------------------------------------------------------------\n\nexport function DependencyGraphTab({\n componentName,\n componentType,\n dependencies,\n}: DependencyGraphTabProps) {\n const deps = dependencies ?? [];\n const visibleCount = Math.min(deps.length, MAX_GRAPH_DEPS);\n const hiddenCount = deps.length - visibleCount;\n const canvasHeight = Math.max(400, Math.min(deps.length * 80 + 100, 600));\n\n if (deps.length === 0) {\n return (\n <div className=\"flex flex-col items-center justify-center py-20 text-center gap-4\">\n <PackageOpen className=\"size-10 text-text-muted\" />\n <p className=\"text-sm text-text-secondary max-w-xs\">\n No dependencies recorded. Add them via Settings.\n </p>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6\">\n {/* Graph canvas */}\n <div>\n {hiddenCount > 0 && (\n <p className=\"text-xs text-text-muted mb-2 text-right\">\n Showing {visibleCount} of {deps.length} dependencies (+{hiddenCount} more in table\n below)\n </p>\n )}\n <DependencyFlow\n componentName={componentName}\n componentType={componentType}\n deps={deps}\n canvasHeight={canvasHeight}\n />\n </div>\n\n {/* All-dependencies table */}\n <div>\n <h3 className=\"text-sm font-semibold text-text-primary mb-3\">\n All Dependencies ({deps.length})\n </h3>\n <div className=\"overflow-x-auto rounded-lg border border-border-default\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"bg-bg-sunken border-b border-border-default\">\n <th className=\"px-4 py-2.5 text-left font-medium text-text-secondary\">Name</th>\n <th className=\"px-4 py-2.5 text-left font-medium text-text-secondary\">Version</th>\n <th className=\"px-4 py-2.5 text-left font-medium text-text-secondary\">Type</th>\n </tr>\n </thead>\n <tbody>\n {deps.map((dep, i) => (\n <tr\n key={`${dep.name}-${i}`}\n className=\"border-b border-border-default last:border-0 hover:bg-bg-sunken/50 transition-colors\"\n >\n <td className=\"px-4 py-2.5 font-mono text-xs text-text-primary\">{dep.name}</td>\n <td className=\"px-4 py-2.5 text-text-secondary\">{dep.version ?? '—'}</td>\n <td className=\"px-4 py-2.5\">\n {dep.type ? (\n <span className=\"inline-flex items-center px-2 py-0.5 text-xs font-medium rounded bg-bg-sunken text-text-secondary\">\n {dep.type}\n </span>\n ) : (\n <span className=\"text-text-muted\">runtime</span>\n )}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"DependencyGraphTab.js","names":[],"sources":["../../../src/components/idp-components/DependencyGraphTab.tsx"],"sourcesContent":["/**\n * DependencyGraphTab — React Flow visualization of a component's dependency graph.\n *\n * Root component node sits on the RIGHT; dependency nodes stack on the LEFT.\n * Edges flow LEFT → RIGHT with type-specific styling.\n * A compact table below the canvas lists all dependencies.\n */\n\nimport { useMemo } from 'react';\nimport { PackageOpen } from 'lucide-react';\nimport '@xyflow/react/dist/style.css';\nimport {\n ReactFlow,\n Background,\n Controls,\n useNodesState,\n useEdgesState,\n type Node,\n type Edge,\n MarkerType,\n BackgroundVariant,\n} from '@xyflow/react';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface DependencyGraphTabProps {\n componentName: string;\n componentType: string;\n dependencies: Array<{ name: string; version?: string | null; type?: string | null }> | null;\n}\n\ntype DepType = 'runtime' | 'dev' | 'peer' | 'optional' | null;\n\nconst MAX_GRAPH_DEPS = 40;\n\n// ---------------------------------------------------------------------------\n// Inline style helpers (React Flow nodes render in a portal — no Tailwind)\n// ---------------------------------------------------------------------------\n\nfunction depNodeStyle(type: DepType): React.CSSProperties {\n switch (type) {\n case 'peer':\n return {\n backgroundColor: 'var(--color-status-info-bg)',\n border: '1px solid var(--color-status-info-border)',\n color: 'var(--color-text-primary)',\n };\n case 'dev':\n return {\n backgroundColor: 'var(--color-bg-sunken)',\n border: '1px dashed var(--color-border-default)',\n color: 'var(--color-text-secondary)',\n };\n case 'optional':\n return {\n backgroundColor: 'var(--color-bg-sunken)',\n border: '1px solid var(--color-border-default)',\n color: 'var(--color-text-primary)',\n opacity: 0.7,\n };\n default:\n // runtime or null\n return {\n backgroundColor: 'var(--color-bg-sunken)',\n border: '1px solid var(--color-border-default)',\n color: 'var(--color-text-primary)',\n };\n }\n}\n\nfunction depTypeBadgeStyle(type: DepType): React.CSSProperties {\n const base: React.CSSProperties = {\n display: 'inline-block',\n fontSize: 10,\n fontWeight: 600,\n borderRadius: 4,\n padding: '1px 5px',\n marginLeft: 4,\n textTransform: 'uppercase',\n letterSpacing: '0.04em',\n };\n switch (type) {\n case 'peer':\n return {\n ...base,\n backgroundColor: 'var(--color-status-info-border)',\n color: 'var(--color-text-inverse)',\n };\n case 'dev':\n return {\n ...base,\n backgroundColor: 'var(--color-border-strong)',\n color: 'var(--color-text-inverse)',\n };\n case 'optional':\n return {\n ...base,\n backgroundColor: 'var(--color-border-default)',\n color: 'var(--color-text-secondary)',\n };\n default:\n return {\n ...base,\n backgroundColor: 'var(--color-action-primary-bg)',\n color: 'var(--color-action-primary-text)',\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Label renderers (ReactNode — React Flow default node accepts ReactNode label)\n// ---------------------------------------------------------------------------\n\nfunction makeDepLabel(dep: {\n name: string;\n version?: string | null;\n type?: string | null;\n}): React.ReactNode {\n const depType = (dep.type ?? null) as DepType;\n return (\n <div style={{ lineHeight: 1.4 }}>\n <div\n style={{\n fontWeight: 600,\n maxWidth: 180,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {dep.name}\n {depType && <span style={depTypeBadgeStyle(depType)}>{depType}</span>}\n </div>\n {dep.version && (\n <div style={{ fontSize: 10, color: 'var(--color-text-muted)', marginTop: 2 }}>\n {dep.version}\n </div>\n )}\n </div>\n );\n}\n\nfunction makeRootLabel(componentName: string, componentType: string): React.ReactNode {\n return (\n <div style={{ lineHeight: 1.4, textAlign: 'center' }}>\n <div style={{ fontWeight: 700, fontSize: 13 }}>{componentName}</div>\n <div\n style={{\n fontSize: 10,\n marginTop: 3,\n backgroundColor: 'var(--color-accent-soft)',\n borderRadius: 4,\n padding: '1px 6px',\n display: 'inline-block',\n letterSpacing: '0.05em',\n textTransform: 'uppercase',\n }}\n >\n {componentType}\n </div>\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Graph element builder\n// ---------------------------------------------------------------------------\n\nfunction buildGraphElements(\n componentName: string,\n componentType: string,\n deps: Array<{ name: string; version?: string | null; type?: string | null }>\n): { nodes: Node[]; edges: Edge[] } {\n const visible = deps.slice(0, MAX_GRAPH_DEPS);\n const totalHeight = Math.max(visible.length * 80, 80);\n const rootY = totalHeight / 2 - 24;\n\n const nodes: Node[] = [];\n const edges: Edge[] = [];\n\n // Root node\n nodes.push({\n id: 'root',\n position: { x: 520, y: rootY },\n data: { label: makeRootLabel(componentName, componentType) },\n style: {\n backgroundColor: 'var(--color-action-primary-bg)',\n color: 'var(--color-action-primary-text)',\n borderRadius: 10,\n padding: '10px 16px',\n width: 160,\n fontWeight: 700,\n fontSize: 13,\n boxShadow: 'var(--shadow-elevation-2)',\n border: 'none',\n textAlign: 'center',\n },\n });\n\n // Dependency nodes and edges\n visible.forEach((dep, i) => {\n const depType = (dep.type ?? null) as DepType;\n const nodeId = `dep-${i}`;\n const isRuntime = !depType || depType === 'runtime';\n const isDev = depType === 'dev';\n\n nodes.push({\n id: nodeId,\n position: { x: 40, y: 20 + i * 80 },\n data: { label: makeDepLabel(dep) },\n style: {\n ...depNodeStyle(depType),\n borderRadius: 8,\n padding: '8px 12px',\n width: 220,\n fontSize: 12,\n },\n });\n\n edges.push({\n id: `edge-${i}`,\n source: nodeId,\n target: 'root',\n animated: isRuntime,\n markerEnd: { type: MarkerType.ArrowClosed },\n style: {\n stroke: 'var(--color-border-strong)',\n strokeWidth: 1.5,\n ...(isDev ? { strokeDasharray: '4 2' } : {}),\n },\n });\n });\n\n return { nodes, edges };\n}\n\n// ---------------------------------------------------------------------------\n// Inner React Flow component (needs its own hook calls — can't be conditional)\n// ---------------------------------------------------------------------------\n\ninterface DependencyFlowProps {\n componentName: string;\n componentType: string;\n deps: Array<{ name: string; version?: string | null; type?: string | null }>;\n canvasHeight: number;\n}\n\nfunction DependencyFlow({ componentName, componentType, deps, canvasHeight }: DependencyFlowProps) {\n const { nodes: initialNodes, edges: initialEdges } = useMemo(\n () => buildGraphElements(componentName, componentType, deps),\n [componentName, componentType, deps]\n );\n\n const [nodes, , onNodesChange] = useNodesState(initialNodes);\n const [edges, , onEdgesChange] = useEdgesState(initialEdges);\n\n return (\n <div\n className=\"w-full rounded-lg border border-border-default overflow-hidden bg-bg-surface\"\n style={{ height: canvasHeight }}\n >\n <ReactFlow\n nodes={nodes}\n edges={edges}\n onNodesChange={onNodesChange}\n onEdgesChange={onEdgesChange}\n nodesDraggable={false}\n fitView\n fitViewOptions={{ padding: 0.15 }}\n minZoom={0.2}\n maxZoom={2}\n panOnDrag\n proOptions={{ hideAttribution: true }}\n >\n <Background\n variant={BackgroundVariant.Dots}\n gap={20}\n size={1}\n className=\"!fill-text-tertiary/10\"\n />\n <Controls\n showInteractive={false}\n style={{\n background: 'var(--color-bg-primary)',\n border: '1px solid var(--color-border-default)',\n borderRadius: 8,\n boxShadow: '0 1px 4px var(--color-border-default)',\n }}\n className=\"[&_button]:!bg-[var(--color-bg-primary)] [&_button]:!border-[var(--color-border-default)] [&_button]:!fill-[var(--color-text-secondary)] [&_button:hover]:!bg-[var(--color-bg-sunken)]\"\n />\n </ReactFlow>\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Main export\n// ---------------------------------------------------------------------------\n\nexport function DependencyGraphTab({\n componentName,\n componentType,\n dependencies,\n}: DependencyGraphTabProps) {\n const deps = dependencies ?? [];\n const visibleCount = Math.min(deps.length, MAX_GRAPH_DEPS);\n const hiddenCount = deps.length - visibleCount;\n const canvasHeight = Math.max(400, Math.min(deps.length * 80 + 100, 600));\n\n if (deps.length === 0) {\n return (\n <div className=\"flex flex-col items-center justify-center py-20 text-center gap-4\">\n <PackageOpen className=\"size-10 text-text-muted\" />\n <p className=\"text-sm text-text-secondary max-w-xs\">\n No dependencies recorded. Add them via Settings.\n </p>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6\">\n {/* Graph canvas */}\n <div>\n {hiddenCount > 0 && (\n <p className=\"text-xs text-text-muted mb-2 text-right\">\n Showing {visibleCount} of {deps.length} dependencies (+{hiddenCount} more in table\n below)\n </p>\n )}\n <DependencyFlow\n componentName={componentName}\n componentType={componentType}\n deps={deps}\n canvasHeight={canvasHeight}\n />\n </div>\n\n {/* All-dependencies table */}\n <div>\n <h3 className=\"text-sm font-semibold text-text-primary mb-3\">\n All Dependencies ({deps.length})\n </h3>\n <div className=\"overflow-x-auto rounded-lg border border-border-default\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"bg-bg-sunken border-b border-border-default\">\n <th className=\"px-4 py-2.5 text-left font-medium text-text-secondary\">Name</th>\n <th className=\"px-4 py-2.5 text-left font-medium text-text-secondary\">Version</th>\n <th className=\"px-4 py-2.5 text-left font-medium text-text-secondary\">Type</th>\n </tr>\n </thead>\n <tbody>\n {deps.map((dep, i) => (\n <tr\n key={`${dep.name}-${i}`}\n className=\"border-b border-border-default last:border-0 hover:bg-bg-sunken/50 transition-colors\"\n >\n <td className=\"px-4 py-2.5 font-mono text-xs text-text-primary\">{dep.name}</td>\n <td className=\"px-4 py-2.5 text-text-secondary\">{dep.version ?? '—'}</td>\n <td className=\"px-4 py-2.5\">\n {dep.type ? (\n <span className=\"inline-flex items-center px-2 py-0.5 text-xs font-medium rounded bg-bg-sunken text-text-secondary\">\n {dep.type}\n </span>\n ) : (\n <span className=\"text-text-muted\">runtime</span>\n )}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -13,10 +13,10 @@ var h = {
13
13
  warn: "text-status-warning-text",
14
14
  error: "text-status-error-text"
15
15
  }, g = {
16
- debug: "bg-bg-tertiary/30",
17
- info: "bg-status-info-bg/10",
18
- warn: "bg-status-warning-bg/10",
19
- error: "bg-status-error-bg/10"
16
+ debug: "bg-bg-sunken",
17
+ info: "bg-status-info-bg-subtle",
18
+ warn: "bg-status-warning-bg-subtle",
19
+ error: "bg-status-error-bg-subtle"
20
20
  }, _ = ({ agentId: _, profile: v, level: y = "info", source: b, maxEntries: x = 1e3, className: S = "", compact: C = !1 }) => {
21
21
  let { t: w } = a(), [T, E] = i([]), [D, O] = i(!1), [k, A] = i(y), [j, M] = i(""), [N, P] = i(null), F = r(null), I = r(!0), { data: L, error: R, loading: z } = e({
22
22
  variables: {
@@ -94,10 +94,10 @@ var h = {
94
94
  }
95
95
  };
96
96
  return /* @__PURE__ */ p("div", {
97
- className: `flex flex-col overflow-hidden rounded-lg border border-border-primary bg-bg-elevated ${S}`,
97
+ className: `flex flex-col overflow-hidden rounded-lg border border-border-default bg-bg-elevated ${S}`,
98
98
  children: [
99
99
  /* @__PURE__ */ p("div", {
100
- className: "flex flex-wrap items-center gap-2 border-b border-border-primary bg-bg-secondary/50 px-3 py-2",
100
+ className: "flex flex-wrap items-center gap-2 border-b border-border-default bg-bg-sunken px-3 py-2",
101
101
  children: [
102
102
  /* @__PURE__ */ p("div", {
103
103
  className: "mr-2 flex items-center gap-1.5",
@@ -109,7 +109,7 @@ var h = {
109
109
  /* @__PURE__ */ p("select", {
110
110
  value: k,
111
111
  onChange: (e) => A(e.target.value),
112
- className: "rounded border border-border-secondary bg-bg-tertiary px-2 py-1 text-xs text-text-primary",
112
+ className: "rounded border border-border-subtle bg-bg-sunken px-2 py-1 text-xs text-text-primary",
113
113
  "aria-label": B("logs.levelFilter", "Log level filter"),
114
114
  children: [
115
115
  /* @__PURE__ */ f("option", {
@@ -141,7 +141,7 @@ var h = {
141
141
  placeholder: B("logs.filterLogs", "Filter logs..."),
142
142
  value: j,
143
143
  onChange: (e) => M(e.target.value),
144
- className: "w-full rounded border border-border-secondary bg-bg-tertiary py-1 pl-6 pr-2 text-xs text-text-primary placeholder:text-text-tertiary"
144
+ className: "w-full rounded border border-border-subtle bg-bg-sunken py-1 pl-6 pr-2 text-xs text-text-primary placeholder:text-text-tertiary"
145
145
  })]
146
146
  }),
147
147
  /* @__PURE__ */ p("div", {
@@ -159,7 +159,7 @@ var h = {
159
159
  type: "button",
160
160
  onClick: () => void W(),
161
161
  disabled: U,
162
- className: "rounded p-1 text-text-tertiary transition-colors hover:bg-bg-tertiary disabled:opacity-50",
162
+ className: "rounded p-1 text-text-tertiary transition-colors hover:bg-bg-sunken disabled:opacity-50",
163
163
  title: B("common.refresh", "Refresh"),
164
164
  children: /* @__PURE__ */ f(l, {
165
165
  size: 14,
@@ -169,21 +169,21 @@ var h = {
169
169
  /* @__PURE__ */ f("button", {
170
170
  type: "button",
171
171
  onClick: () => O((e) => !e),
172
- className: `rounded p-1 transition-colors hover:bg-bg-tertiary ${D ? "text-status-warning-text" : "text-text-tertiary"}`,
172
+ className: `rounded p-1 transition-colors hover:bg-bg-sunken ${D ? "text-status-warning-text" : "text-text-tertiary"}`,
173
173
  title: D ? B("logs.resume", "Resume") : B("logs.pause", "Pause"),
174
174
  children: f(D ? c : s, { size: 14 })
175
175
  }),
176
176
  /* @__PURE__ */ f("button", {
177
177
  type: "button",
178
178
  onClick: K,
179
- className: "rounded p-1 text-text-tertiary transition-colors hover:bg-bg-tertiary",
179
+ className: "rounded p-1 text-text-tertiary transition-colors hover:bg-bg-sunken",
180
180
  title: B("logs.clear", "Clear"),
181
181
  children: /* @__PURE__ */ f(d, { size: 14 })
182
182
  }),
183
183
  /* @__PURE__ */ f("button", {
184
184
  type: "button",
185
185
  onClick: q,
186
- className: "rounded p-1 text-text-tertiary transition-colors hover:bg-bg-tertiary",
186
+ className: "rounded p-1 text-text-tertiary transition-colors hover:bg-bg-sunken",
187
187
  title: B("logs.export", "Export"),
188
188
  children: /* @__PURE__ */ f(o, { size: 14 })
189
189
  })
@@ -193,7 +193,7 @@ var h = {
193
193
  }),
194
194
  N ? /* @__PURE__ */ f("div", {
195
195
  role: "alert",
196
- className: "border-b border-border-primary bg-status-error-bg/10 px-3 py-2 text-xs text-status-error-text",
196
+ className: "border-b border-border-default bg-status-error-bg-subtle px-3 py-2 text-xs text-status-error-text",
197
197
  children: N
198
198
  }) : null,
199
199
  /* @__PURE__ */ f("div", {
@@ -205,7 +205,7 @@ var h = {
205
205
  className: "flex h-full min-h-[200px] items-center justify-center text-text-tertiary",
206
206
  children: _ ? N ? `${B("logs.subscriptionError", "Log stream error")}: ${N}` : z ? B("logs.connecting", "Connecting to log stream…") : B("logs.waitingForEntries", "Waiting for log entries…") : B("logs.connectToAgent", "Connect to an agent to view logs")
207
207
  }) : G.map((e, t) => /* @__PURE__ */ p("div", {
208
- className: `flex gap-2 border-b border-border-primary/30 px-3 py-0.5 hover:bg-bg-tertiary/50 ${g[e.level] ?? ""}`,
208
+ className: `flex gap-2 border-b border-border-default/30 px-3 py-0.5 hover:bg-bg-sunken ${g[e.level] ?? ""}`,
209
209
  children: [
210
210
  /* @__PURE__ */ f("span", {
211
211
  className: "w-[85px] shrink-0 text-text-tertiary",
@@ -1 +1 @@
1
- {"version":3,"file":"LogViewer.js","names":[],"sources":["../../../src/components/logs/LogViewer.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef, useState, type FC } from 'react';\nimport { Download, Pause, Play, RefreshCw, Search, Trash2 } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useVibecontrolsAgentLogStreamSubscription } from '@/generated/wspace-operations';\n\ninterface LogEntry {\n timestamp: string;\n level: 'debug' | 'info' | 'warn' | 'error';\n source: string;\n message: string;\n metadata?: Record<string, unknown>;\n}\n\ninterface LogViewerProps {\n agentId: string;\n /** Canonical CLI profile name on the remote agent. */\n profile: string;\n level?: 'debug' | 'info' | 'warn' | 'error';\n source?: string;\n maxEntries?: number;\n className?: string;\n compact?: boolean;\n}\n\nfunction getEntryKey(entry: LogEntry): string {\n return `${entry.timestamp}:${entry.level}:${entry.source}:${entry.message}`;\n}\n\nconst LEVEL_COLORS: Record<string, string> = {\n debug: 'text-text-tertiary',\n info: 'text-status-info-text',\n warn: 'text-status-warning-text',\n error: 'text-status-error-text',\n};\n\nconst LEVEL_BG: Record<string, string> = {\n debug: 'bg-bg-tertiary/30',\n info: 'bg-status-info-bg/10',\n warn: 'bg-status-warning-bg/10',\n error: 'bg-status-error-bg/10',\n};\n\nexport const LogViewer: FC<LogViewerProps> = ({\n agentId,\n profile,\n level: initialLevel = 'info',\n source: initialSource,\n maxEntries = 1000,\n className = '',\n compact = false,\n}) => {\n const { t } = useI18n();\n const [entries, setEntries] = useState<LogEntry[]>([]);\n const [paused, setPaused] = useState(false);\n const [level, setLevel] = useState(initialLevel);\n const [searchQuery, setSearchQuery] = useState('');\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const autoScrollRef = useRef(true);\n\n // GraphQL subscription — the svc polls the agent server-side every\n // ~2s and pushes only new entries here. Replaces the previous client\n // setInterval + fetch, which was firing infinitely because\n // `tr` recreated on every render mutated fetchLogs' identity, which\n // re-ran the useEffect, which re-armed the interval, which called\n // fetchLogs immediately. The subscription has no client polling at\n // all — Apollo opens one WS, the svc owns the cadence.\n const {\n data: streamData,\n error: subscriptionError,\n loading: streamLoading,\n } = useVibecontrolsAgentLogStreamSubscription({\n variables: {\n agentId,\n profile,\n level,\n source: initialSource,\n },\n skip: !agentId || paused,\n shouldResubscribe: true,\n });\n\n const tr = (key: string, fallback: string) => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n const handleScroll = useCallback(() => {\n if (!containerRef.current) {\n return;\n }\n\n const element = containerRef.current;\n autoScrollRef.current = element.scrollHeight - element.scrollTop - element.clientHeight < 50;\n }, []);\n\n const scrollToBottom = useCallback(() => {\n if (containerRef.current) {\n containerRef.current.scrollTop = containerRef.current.scrollHeight;\n }\n }, []);\n\n // Drain incoming subscription batches into the entries list. Each\n // payload is the NEW entries since the last server-side tick; merge\n // by key so re-deliveries (e.g. on reconnect backfill) don't dupe.\n useEffect(() => {\n const batch = streamData?.vibecontrolsAgentLogStream;\n if (!batch || batch.length === 0) return;\n const incoming: LogEntry[] = batch.map(\n (row: {\n timestamp: string;\n level: string;\n source?: string | null;\n message: string;\n metadataJson?: string | null;\n }) => {\n let metadata: Record<string, unknown> | undefined;\n if (row.metadataJson) {\n try {\n metadata = JSON.parse(row.metadataJson) as Record<string, unknown>;\n } catch {\n metadata = undefined;\n }\n }\n return {\n timestamp: row.timestamp,\n level: (row.level as LogEntry['level']) ?? 'info',\n source: row.source ?? '',\n message: row.message,\n metadata,\n };\n }\n );\n setEntries((current) => {\n const merged = new Map<string, LogEntry>();\n for (const entry of current) merged.set(getEntryKey(entry), entry);\n for (const entry of incoming) merged.set(getEntryKey(entry), entry);\n return Array.from(merged.values())\n .sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime())\n .slice(-maxEntries);\n });\n setErrorMessage(null);\n if (autoScrollRef.current) {\n requestAnimationFrame(() => {\n if (containerRef.current) {\n containerRef.current.scrollTop = containerRef.current.scrollHeight;\n }\n });\n }\n }, [streamData, maxEntries]);\n\n useEffect(() => {\n if (subscriptionError) {\n setErrorMessage(subscriptionError.message);\n }\n }, [subscriptionError]);\n\n const connected = !subscriptionError && !streamLoading;\n const isRefreshing = streamLoading;\n\n // Reset the entries list when the agent / filter set actually changes.\n useEffect(() => {\n setEntries([]);\n setErrorMessage(null);\n }, [agentId, profile, level, initialSource]);\n\n // Manual refresh: no-op since the subscription pushes continuously.\n // Kept as a callback so the existing \"Refresh\" button doesn't have to\n // be conditionally hidden. Wraps in setEntries([]) so the operator's\n // intent of \"give me a fresh window\" is honoured — the next push\n // backfills.\n const fetchLogs = useCallback(() => {\n setEntries([]);\n }, []);\n\n const filteredEntries = searchQuery\n ? entries.filter(\n (entry) =>\n entry.message.toLowerCase().includes(searchQuery.toLowerCase()) ||\n entry.source.toLowerCase().includes(searchQuery.toLowerCase())\n )\n : entries;\n\n const handleClear = () => setEntries([]);\n\n const handleExport = () => {\n const blob = new Blob([filteredEntries.map((entry) => JSON.stringify(entry)).join('\\n')], {\n type: 'application/x-ndjson',\n });\n const url = URL.createObjectURL(blob);\n const anchor = document.createElement('a');\n anchor.href = url;\n anchor.download = `agent-logs-${new Date().toISOString().slice(0, 19).replace(/:/g, '-')}.jsonl`;\n anchor.click();\n URL.revokeObjectURL(url);\n };\n\n const formatTime = (timestamp: string) => {\n try {\n const date = new Date(timestamp);\n return (\n date.toLocaleTimeString('en-US', {\n hour12: false,\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n }) +\n '.' +\n String(date.getMilliseconds()).padStart(3, '0')\n );\n } catch {\n return timestamp;\n }\n };\n\n return (\n <div\n className={`flex flex-col overflow-hidden rounded-lg border border-border-primary bg-bg-elevated ${className}`}\n >\n <div className=\"flex flex-wrap items-center gap-2 border-b border-border-primary bg-bg-secondary/50 px-3 py-2\">\n <div className=\"mr-2 flex items-center gap-1.5\">\n <div\n className={`size-2 rounded-full ${connected ? 'animate-pulse bg-status-success-bg' : 'bg-status-error-bg'}`}\n />\n <span className=\"text-xs text-text-tertiary\">\n {connected ? tr('logs.live', 'Live') : tr('logs.disconnected', 'Disconnected')}\n </span>\n </div>\n\n <select\n value={level}\n onChange={(event) => setLevel(event.target.value as LogEntry['level'])}\n className=\"rounded border border-border-secondary bg-bg-tertiary px-2 py-1 text-xs text-text-primary\"\n aria-label={tr('logs.levelFilter', 'Log level filter')}\n >\n <option value=\"debug\">DEBUG+</option>\n <option value=\"info\">INFO+</option>\n <option value=\"warn\">WARN+</option>\n <option value=\"error\">ERROR</option>\n </select>\n\n <div className=\"relative min-w-[120px] max-w-xs flex-1\">\n <Search\n size={12}\n className=\"absolute left-2 top-1/2 -translate-y-1/2 text-text-tertiary\"\n />\n <input\n aria-label=\"control\"\n type=\"text\"\n placeholder={tr('logs.filterLogs', 'Filter logs...')}\n value={searchQuery}\n onChange={(event) => setSearchQuery(event.target.value)}\n className=\"w-full rounded border border-border-secondary bg-bg-tertiary py-1 pl-6 pr-2 text-xs text-text-primary placeholder:text-text-tertiary\"\n />\n </div>\n\n <div className=\"ml-auto flex items-center gap-1\">\n <span className=\"text-xs text-text-tertiary\">\n {filteredEntries.length} {tr('logs.entries', 'entries')}\n </span>\n <button\n type=\"button\"\n onClick={() => void fetchLogs()}\n disabled={isRefreshing}\n className=\"rounded p-1 text-text-tertiary transition-colors hover:bg-bg-tertiary disabled:opacity-50\"\n title={tr('common.refresh', 'Refresh')}\n >\n <RefreshCw size={14} className={isRefreshing ? 'animate-spin' : ''} />\n </button>\n <button\n type=\"button\"\n onClick={() => setPaused((current) => !current)}\n className={`rounded p-1 transition-colors hover:bg-bg-tertiary ${paused ? 'text-status-warning-text' : 'text-text-tertiary'}`}\n title={paused ? tr('logs.resume', 'Resume') : tr('logs.pause', 'Pause')}\n >\n {paused ? <Play size={14} /> : <Pause size={14} />}\n </button>\n <button\n type=\"button\"\n onClick={handleClear}\n className=\"rounded p-1 text-text-tertiary transition-colors hover:bg-bg-tertiary\"\n title={tr('logs.clear', 'Clear')}\n >\n <Trash2 size={14} />\n </button>\n <button\n type=\"button\"\n onClick={handleExport}\n className=\"rounded p-1 text-text-tertiary transition-colors hover:bg-bg-tertiary\"\n title={tr('logs.export', 'Export')}\n >\n <Download size={14} />\n </button>\n </div>\n </div>\n\n {errorMessage ? (\n <div\n role=\"alert\"\n className=\"border-b border-border-primary bg-status-error-bg/10 px-3 py-2 text-xs text-status-error-text\"\n >\n {errorMessage}\n </div>\n ) : null}\n\n <div\n ref={containerRef}\n onScroll={handleScroll}\n className={`overflow-y-auto font-mono text-xs leading-relaxed ${compact ? 'max-h-[300px]' : 'min-h-[400px] flex-1'}`}\n style={{ maxHeight: compact ? '300px' : undefined }}\n >\n {filteredEntries.length === 0 ? (\n <div className=\"flex h-full min-h-[200px] items-center justify-center text-text-tertiary\">\n {!agentId\n ? tr('logs.connectToAgent', 'Connect to an agent to view logs')\n : errorMessage\n ? `${tr('logs.subscriptionError', 'Log stream error')}: ${errorMessage}`\n : streamLoading\n ? tr('logs.connecting', 'Connecting to log stream…')\n : tr('logs.waitingForEntries', 'Waiting for log entries…')}\n </div>\n ) : (\n filteredEntries.map((entry, index) => (\n <div\n key={`${entry.timestamp}-${entry.source}-${index}`}\n className={`flex gap-2 border-b border-border-primary/30 px-3 py-0.5 hover:bg-bg-tertiary/50 ${LEVEL_BG[entry.level] ?? ''}`}\n >\n <span className=\"w-[85px] shrink-0 text-text-tertiary\">\n {formatTime(entry.timestamp)}\n </span>\n <span\n className={`w-[42px] shrink-0 uppercase font-semibold ${LEVEL_COLORS[entry.level] ?? 'text-text-primary'}`}\n >\n {entry.level}\n </span>\n <span\n className=\"w-[100px] shrink-0 truncate text-text-secondary\"\n title={entry.source}\n >\n [{entry.source}]\n </span>\n <span className=\"flex-1 break-all text-text-primary\">\n {entry.message}\n {entry.metadata && Object.keys(entry.metadata).length > 0 ? (\n <span className=\"ml-2 text-text-tertiary\">{JSON.stringify(entry.metadata)}</span>\n ) : null}\n </span>\n </div>\n ))\n )}\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"LogViewer.js","names":[],"sources":["../../../src/components/logs/LogViewer.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef, useState, type FC } from 'react';\nimport { Download, Pause, Play, RefreshCw, Search, Trash2 } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useVibecontrolsAgentLogStreamSubscription } from '@/generated/wspace-operations';\n\ninterface LogEntry {\n timestamp: string;\n level: 'debug' | 'info' | 'warn' | 'error';\n source: string;\n message: string;\n metadata?: Record<string, unknown>;\n}\n\ninterface LogViewerProps {\n agentId: string;\n /** Canonical CLI profile name on the remote agent. */\n profile: string;\n level?: 'debug' | 'info' | 'warn' | 'error';\n source?: string;\n maxEntries?: number;\n className?: string;\n compact?: boolean;\n}\n\nfunction getEntryKey(entry: LogEntry): string {\n return `${entry.timestamp}:${entry.level}:${entry.source}:${entry.message}`;\n}\n\nconst LEVEL_COLORS: Record<string, string> = {\n debug: 'text-text-tertiary',\n info: 'text-status-info-text',\n warn: 'text-status-warning-text',\n error: 'text-status-error-text',\n};\n\nconst LEVEL_BG: Record<string, string> = {\n debug: 'bg-bg-sunken',\n info: 'bg-status-info-bg-subtle',\n warn: 'bg-status-warning-bg-subtle',\n error: 'bg-status-error-bg-subtle',\n};\n\nexport const LogViewer: FC<LogViewerProps> = ({\n agentId,\n profile,\n level: initialLevel = 'info',\n source: initialSource,\n maxEntries = 1000,\n className = '',\n compact = false,\n}) => {\n const { t } = useI18n();\n const [entries, setEntries] = useState<LogEntry[]>([]);\n const [paused, setPaused] = useState(false);\n const [level, setLevel] = useState(initialLevel);\n const [searchQuery, setSearchQuery] = useState('');\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const autoScrollRef = useRef(true);\n\n // GraphQL subscription — the svc polls the agent server-side every\n // ~2s and pushes only new entries here. Replaces the previous client\n // setInterval + fetch, which was firing infinitely because\n // `tr` recreated on every render mutated fetchLogs' identity, which\n // re-ran the useEffect, which re-armed the interval, which called\n // fetchLogs immediately. The subscription has no client polling at\n // all — Apollo opens one WS, the svc owns the cadence.\n const {\n data: streamData,\n error: subscriptionError,\n loading: streamLoading,\n } = useVibecontrolsAgentLogStreamSubscription({\n variables: {\n agentId,\n profile,\n level,\n source: initialSource,\n },\n skip: !agentId || paused,\n shouldResubscribe: true,\n });\n\n const tr = (key: string, fallback: string) => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n const handleScroll = useCallback(() => {\n if (!containerRef.current) {\n return;\n }\n\n const element = containerRef.current;\n autoScrollRef.current = element.scrollHeight - element.scrollTop - element.clientHeight < 50;\n }, []);\n\n const scrollToBottom = useCallback(() => {\n if (containerRef.current) {\n containerRef.current.scrollTop = containerRef.current.scrollHeight;\n }\n }, []);\n\n // Drain incoming subscription batches into the entries list. Each\n // payload is the NEW entries since the last server-side tick; merge\n // by key so re-deliveries (e.g. on reconnect backfill) don't dupe.\n useEffect(() => {\n const batch = streamData?.vibecontrolsAgentLogStream;\n if (!batch || batch.length === 0) return;\n const incoming: LogEntry[] = batch.map(\n (row: {\n timestamp: string;\n level: string;\n source?: string | null;\n message: string;\n metadataJson?: string | null;\n }) => {\n let metadata: Record<string, unknown> | undefined;\n if (row.metadataJson) {\n try {\n metadata = JSON.parse(row.metadataJson) as Record<string, unknown>;\n } catch {\n metadata = undefined;\n }\n }\n return {\n timestamp: row.timestamp,\n level: (row.level as LogEntry['level']) ?? 'info',\n source: row.source ?? '',\n message: row.message,\n metadata,\n };\n }\n );\n setEntries((current) => {\n const merged = new Map<string, LogEntry>();\n for (const entry of current) merged.set(getEntryKey(entry), entry);\n for (const entry of incoming) merged.set(getEntryKey(entry), entry);\n return Array.from(merged.values())\n .sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime())\n .slice(-maxEntries);\n });\n setErrorMessage(null);\n if (autoScrollRef.current) {\n requestAnimationFrame(() => {\n if (containerRef.current) {\n containerRef.current.scrollTop = containerRef.current.scrollHeight;\n }\n });\n }\n }, [streamData, maxEntries]);\n\n useEffect(() => {\n if (subscriptionError) {\n setErrorMessage(subscriptionError.message);\n }\n }, [subscriptionError]);\n\n const connected = !subscriptionError && !streamLoading;\n const isRefreshing = streamLoading;\n\n // Reset the entries list when the agent / filter set actually changes.\n useEffect(() => {\n setEntries([]);\n setErrorMessage(null);\n }, [agentId, profile, level, initialSource]);\n\n // Manual refresh: no-op since the subscription pushes continuously.\n // Kept as a callback so the existing \"Refresh\" button doesn't have to\n // be conditionally hidden. Wraps in setEntries([]) so the operator's\n // intent of \"give me a fresh window\" is honoured — the next push\n // backfills.\n const fetchLogs = useCallback(() => {\n setEntries([]);\n }, []);\n\n const filteredEntries = searchQuery\n ? entries.filter(\n (entry) =>\n entry.message.toLowerCase().includes(searchQuery.toLowerCase()) ||\n entry.source.toLowerCase().includes(searchQuery.toLowerCase())\n )\n : entries;\n\n const handleClear = () => setEntries([]);\n\n const handleExport = () => {\n const blob = new Blob([filteredEntries.map((entry) => JSON.stringify(entry)).join('\\n')], {\n type: 'application/x-ndjson',\n });\n const url = URL.createObjectURL(blob);\n const anchor = document.createElement('a');\n anchor.href = url;\n anchor.download = `agent-logs-${new Date().toISOString().slice(0, 19).replace(/:/g, '-')}.jsonl`;\n anchor.click();\n URL.revokeObjectURL(url);\n };\n\n const formatTime = (timestamp: string) => {\n try {\n const date = new Date(timestamp);\n return (\n date.toLocaleTimeString('en-US', {\n hour12: false,\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n }) +\n '.' +\n String(date.getMilliseconds()).padStart(3, '0')\n );\n } catch {\n return timestamp;\n }\n };\n\n return (\n <div\n className={`flex flex-col overflow-hidden rounded-lg border border-border-default bg-bg-elevated ${className}`}\n >\n <div className=\"flex flex-wrap items-center gap-2 border-b border-border-default bg-bg-sunken px-3 py-2\">\n <div className=\"mr-2 flex items-center gap-1.5\">\n <div\n className={`size-2 rounded-full ${connected ? 'animate-pulse bg-status-success-bg' : 'bg-status-error-bg'}`}\n />\n <span className=\"text-xs text-text-tertiary\">\n {connected ? tr('logs.live', 'Live') : tr('logs.disconnected', 'Disconnected')}\n </span>\n </div>\n\n <select\n value={level}\n onChange={(event) => setLevel(event.target.value as LogEntry['level'])}\n className=\"rounded border border-border-subtle bg-bg-sunken px-2 py-1 text-xs text-text-primary\"\n aria-label={tr('logs.levelFilter', 'Log level filter')}\n >\n <option value=\"debug\">DEBUG+</option>\n <option value=\"info\">INFO+</option>\n <option value=\"warn\">WARN+</option>\n <option value=\"error\">ERROR</option>\n </select>\n\n <div className=\"relative min-w-[120px] max-w-xs flex-1\">\n <Search\n size={12}\n className=\"absolute left-2 top-1/2 -translate-y-1/2 text-text-tertiary\"\n />\n <input\n aria-label=\"control\"\n type=\"text\"\n placeholder={tr('logs.filterLogs', 'Filter logs...')}\n value={searchQuery}\n onChange={(event) => setSearchQuery(event.target.value)}\n className=\"w-full rounded border border-border-subtle bg-bg-sunken py-1 pl-6 pr-2 text-xs text-text-primary placeholder:text-text-tertiary\"\n />\n </div>\n\n <div className=\"ml-auto flex items-center gap-1\">\n <span className=\"text-xs text-text-tertiary\">\n {filteredEntries.length} {tr('logs.entries', 'entries')}\n </span>\n <button\n type=\"button\"\n onClick={() => void fetchLogs()}\n disabled={isRefreshing}\n className=\"rounded p-1 text-text-tertiary transition-colors hover:bg-bg-sunken disabled:opacity-50\"\n title={tr('common.refresh', 'Refresh')}\n >\n <RefreshCw size={14} className={isRefreshing ? 'animate-spin' : ''} />\n </button>\n <button\n type=\"button\"\n onClick={() => setPaused((current) => !current)}\n className={`rounded p-1 transition-colors hover:bg-bg-sunken ${paused ? 'text-status-warning-text' : 'text-text-tertiary'}`}\n title={paused ? tr('logs.resume', 'Resume') : tr('logs.pause', 'Pause')}\n >\n {paused ? <Play size={14} /> : <Pause size={14} />}\n </button>\n <button\n type=\"button\"\n onClick={handleClear}\n className=\"rounded p-1 text-text-tertiary transition-colors hover:bg-bg-sunken\"\n title={tr('logs.clear', 'Clear')}\n >\n <Trash2 size={14} />\n </button>\n <button\n type=\"button\"\n onClick={handleExport}\n className=\"rounded p-1 text-text-tertiary transition-colors hover:bg-bg-sunken\"\n title={tr('logs.export', 'Export')}\n >\n <Download size={14} />\n </button>\n </div>\n </div>\n\n {errorMessage ? (\n <div\n role=\"alert\"\n className=\"border-b border-border-default bg-status-error-bg-subtle px-3 py-2 text-xs text-status-error-text\"\n >\n {errorMessage}\n </div>\n ) : null}\n\n <div\n ref={containerRef}\n onScroll={handleScroll}\n className={`overflow-y-auto font-mono text-xs leading-relaxed ${compact ? 'max-h-[300px]' : 'min-h-[400px] flex-1'}`}\n style={{ maxHeight: compact ? '300px' : undefined }}\n >\n {filteredEntries.length === 0 ? (\n <div className=\"flex h-full min-h-[200px] items-center justify-center text-text-tertiary\">\n {!agentId\n ? tr('logs.connectToAgent', 'Connect to an agent to view logs')\n : errorMessage\n ? `${tr('logs.subscriptionError', 'Log stream error')}: ${errorMessage}`\n : streamLoading\n ? tr('logs.connecting', 'Connecting to log stream…')\n : tr('logs.waitingForEntries', 'Waiting for log entries…')}\n </div>\n ) : (\n filteredEntries.map((entry, index) => (\n <div\n key={`${entry.timestamp}-${entry.source}-${index}`}\n className={`flex gap-2 border-b border-border-default/30 px-3 py-0.5 hover:bg-bg-sunken ${LEVEL_BG[entry.level] ?? ''}`}\n >\n <span className=\"w-[85px] shrink-0 text-text-tertiary\">\n {formatTime(entry.timestamp)}\n </span>\n <span\n className={`w-[42px] shrink-0 uppercase font-semibold ${LEVEL_COLORS[entry.level] ?? 'text-text-primary'}`}\n >\n {entry.level}\n </span>\n <span\n className=\"w-[100px] shrink-0 truncate text-text-secondary\"\n title={entry.source}\n >\n [{entry.source}]\n </span>\n <span className=\"flex-1 break-all text-text-primary\">\n {entry.message}\n {entry.metadata && Object.keys(entry.metadata).length > 0 ? (\n <span className=\"ml-2 text-text-tertiary\">{JSON.stringify(entry.metadata)}</span>\n ) : null}\n </span>\n </div>\n ))\n )}\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -156,7 +156,7 @@ function g({ note: g, onOpen: _, onEdit: v, onDelete: y, onPin: b, onUnpin: x, o
156
156
  g.tagAssignments && g.tagAssignments.length > 0 ? /* @__PURE__ */ h("div", {
157
157
  className: "mt-3 flex flex-wrap gap-1",
158
158
  children: [g.tagAssignments.slice(0, 3).map((e) => /* @__PURE__ */ h("span", {
159
- className: "inline-flex items-center gap-1 px-2 py-0.5 text-xs bg-action-primary-bg/10 text-action-primary-bg rounded",
159
+ className: "inline-flex items-center gap-1 px-2 py-0.5 text-xs bg-[var(--color-accent-soft)] text-text-primary rounded",
160
160
  style: e.tag?.color ? { borderLeft: `3px solid ${e.tag.color}` } : void 0,
161
161
  children: [/* @__PURE__ */ m(u, { className: "size-3" }), e.tag?.label ?? e.tagId.slice(0, 8)]
162
162
  }, e.id)), g.tagAssignments.length > 3 && /* @__PURE__ */ h("span", {
@@ -1 +1 @@
1
- {"version":3,"file":"NoteCard.js","names":[],"sources":["../../../src/components/notes/NoteCard.tsx"],"sourcesContent":["import { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n FileText,\n Pin,\n Tag,\n MoreVertical,\n Edit,\n Trash2,\n PinOff,\n Lock,\n Globe,\n Share2,\n} from 'lucide-react';\nimport { useState } from 'react';\nimport { motion, AnimatePresence } from 'framer-motion';\nimport type { Note } from './types';\n\ninterface NoteCardProps {\n note: Note;\n onOpen?: (note: Note) => void;\n onEdit?: (note: Note) => void;\n onDelete?: (note: Note) => void;\n onPin?: (note: Note) => void;\n onUnpin?: (note: Note) => void;\n onShare?: (note: Note) => void;\n /**\n * Generic-sharing entry point: opens the full Share dialog (users / link /\n * vibe). Distinct from `onShare`, which only toggles PRIVATE/WORKSPACE\n * visibility. Provided only when the caller has share permission + the\n * `vibecontrols.sharing.note` flag is on.\n */\n onShareAccess?: (note: Note) => void;\n}\n\nexport function NoteCard({\n note,\n onOpen,\n onEdit,\n onDelete,\n onPin,\n onUnpin,\n onShare,\n onShareAccess,\n}: NoteCardProps) {\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 formatDate = (dateStr: string) => {\n return new Date(dateStr).toLocaleDateString('en-US', {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n });\n };\n\n const getPreview = (content: string) => {\n // Remove markdown headers and get first 100 chars\n const text = content\n .replace(/^#+\\s+/gm, '')\n .replace(/\\n/g, ' ')\n .trim();\n return text.length > 100 ? text.slice(0, 100) + '...' : text;\n };\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\"\n onClick={() => onOpen?.(note)}\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex items-start gap-3 min-w-0\">\n <div className=\"flex-shrink-0 size-10 rounded-lg bg-status-warning-bg flex items-center justify-center\">\n <FileText className=\"size-5 text-status-warning-text\" />\n </div>\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"font-semibold text-text-primary truncate\">{note.title}</h3>\n {note.isPinned && <Pin className=\"size-4 text-status-warning-text flex-shrink-0\" />}\n {note.visibility === 'PRIVATE' ? (\n <span title=\"Private note\">\n <Lock className=\"size-3.5 text-text-muted flex-shrink-0\" />\n </span>\n ) : (\n <span title=\"Workspace note\">\n <Globe className=\"size-3.5 text-text-muted flex-shrink-0\" />\n </span>\n )}\n </div>\n <p className=\"text-xs text-text-secondary\">{formatDate(note.updatedAt)}</p>\n </div>\n </div>\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n setShowMenu(!showMenu);\n }}\n className=\"p-1 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-36 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(note);\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 <Edit className=\"size-4\" />\n Edit\n </motion.button>\n )}\n {onShare && note.visibility === 'PRIVATE' && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onShare(note);\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\"\n >\n <Share2 className=\"size-4\" />\n Share with workspace\n </motion.button>\n )}\n {onShare && note.visibility === 'WORKSPACE' && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onShare(note);\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\"\n >\n <Lock className=\"size-4\" />\n Make private\n </motion.button>\n )}\n {onShareAccess && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n data-testid=\"note-share-access\"\n onClick={(e) => {\n e.stopPropagation();\n onShareAccess(note);\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\"\n >\n <Share2 className=\"size-4\" />\n Share access…\n </motion.button>\n )}\n {note.isPinned && onUnpin && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onUnpin(note);\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\"\n >\n <PinOff className=\"size-4\" />\n Unpin\n </motion.button>\n )}\n {!note.isPinned && onPin && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onPin(note);\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\"\n >\n <Pin className=\"size-4\" />\n Pin\n </motion.button>\n )}\n {onDelete && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onDelete(note);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-status-error-text hover:bg-bg-sunken rounded-b-lg\"\n >\n <Trash2 className=\"size-4\" />\n Delete\n </motion.button>\n )}\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n </div>\n\n <p className=\"mt-3 text-sm text-text-secondary line-clamp-2\">{getPreview(note.content)}</p>\n\n {/* Render tagAssignments if available, otherwise fall back to legacy string tags */}\n {note.tagAssignments && note.tagAssignments.length > 0 ? (\n <div className=\"mt-3 flex flex-wrap gap-1\">\n {note.tagAssignments.slice(0, 3).map((assignment) => (\n <span\n key={assignment.id}\n className=\"inline-flex items-center gap-1 px-2 py-0.5 text-xs bg-action-primary-bg/10 text-action-primary-bg rounded\"\n style={\n assignment.tag?.color\n ? { borderLeft: `3px solid ${assignment.tag.color}` }\n : undefined\n }\n >\n <Tag className=\"size-3\" />\n {assignment.tag?.label ?? assignment.tagId.slice(0, 8)}\n </span>\n ))}\n {note.tagAssignments.length > 3 && (\n <span className=\"px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\">\n +{note.tagAssignments.length - 3}\n </span>\n )}\n </div>\n ) : note.tags && note.tags.length > 0 ? (\n <div className=\"mt-3 flex flex-wrap gap-1\">\n {note.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 {note.tags.length > 3 && (\n <span className=\"px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\">\n +{note.tags.length - 3}\n </span>\n )}\n </div>\n ) : null}\n </motion.div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"NoteCard.js","names":[],"sources":["../../../src/components/notes/NoteCard.tsx"],"sourcesContent":["import { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n FileText,\n Pin,\n Tag,\n MoreVertical,\n Edit,\n Trash2,\n PinOff,\n Lock,\n Globe,\n Share2,\n} from 'lucide-react';\nimport { useState } from 'react';\nimport { motion, AnimatePresence } from 'framer-motion';\nimport type { Note } from './types';\n\ninterface NoteCardProps {\n note: Note;\n onOpen?: (note: Note) => void;\n onEdit?: (note: Note) => void;\n onDelete?: (note: Note) => void;\n onPin?: (note: Note) => void;\n onUnpin?: (note: Note) => void;\n onShare?: (note: Note) => void;\n /**\n * Generic-sharing entry point: opens the full Share dialog (users / link /\n * vibe). Distinct from `onShare`, which only toggles PRIVATE/WORKSPACE\n * visibility. Provided only when the caller has share permission + the\n * `vibecontrols.sharing.note` flag is on.\n */\n onShareAccess?: (note: Note) => void;\n}\n\nexport function NoteCard({\n note,\n onOpen,\n onEdit,\n onDelete,\n onPin,\n onUnpin,\n onShare,\n onShareAccess,\n}: NoteCardProps) {\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 formatDate = (dateStr: string) => {\n return new Date(dateStr).toLocaleDateString('en-US', {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n });\n };\n\n const getPreview = (content: string) => {\n // Remove markdown headers and get first 100 chars\n const text = content\n .replace(/^#+\\s+/gm, '')\n .replace(/\\n/g, ' ')\n .trim();\n return text.length > 100 ? text.slice(0, 100) + '...' : text;\n };\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\"\n onClick={() => onOpen?.(note)}\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex items-start gap-3 min-w-0\">\n <div className=\"flex-shrink-0 size-10 rounded-lg bg-status-warning-bg flex items-center justify-center\">\n <FileText className=\"size-5 text-status-warning-text\" />\n </div>\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"font-semibold text-text-primary truncate\">{note.title}</h3>\n {note.isPinned && <Pin className=\"size-4 text-status-warning-text flex-shrink-0\" />}\n {note.visibility === 'PRIVATE' ? (\n <span title=\"Private note\">\n <Lock className=\"size-3.5 text-text-muted flex-shrink-0\" />\n </span>\n ) : (\n <span title=\"Workspace note\">\n <Globe className=\"size-3.5 text-text-muted flex-shrink-0\" />\n </span>\n )}\n </div>\n <p className=\"text-xs text-text-secondary\">{formatDate(note.updatedAt)}</p>\n </div>\n </div>\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n setShowMenu(!showMenu);\n }}\n className=\"p-1 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-36 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(note);\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 <Edit className=\"size-4\" />\n Edit\n </motion.button>\n )}\n {onShare && note.visibility === 'PRIVATE' && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onShare(note);\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\"\n >\n <Share2 className=\"size-4\" />\n Share with workspace\n </motion.button>\n )}\n {onShare && note.visibility === 'WORKSPACE' && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onShare(note);\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\"\n >\n <Lock className=\"size-4\" />\n Make private\n </motion.button>\n )}\n {onShareAccess && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n data-testid=\"note-share-access\"\n onClick={(e) => {\n e.stopPropagation();\n onShareAccess(note);\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\"\n >\n <Share2 className=\"size-4\" />\n Share access…\n </motion.button>\n )}\n {note.isPinned && onUnpin && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onUnpin(note);\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\"\n >\n <PinOff className=\"size-4\" />\n Unpin\n </motion.button>\n )}\n {!note.isPinned && onPin && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onPin(note);\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\"\n >\n <Pin className=\"size-4\" />\n Pin\n </motion.button>\n )}\n {onDelete && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onDelete(note);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-status-error-text hover:bg-bg-sunken rounded-b-lg\"\n >\n <Trash2 className=\"size-4\" />\n Delete\n </motion.button>\n )}\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n </div>\n\n <p className=\"mt-3 text-sm text-text-secondary line-clamp-2\">{getPreview(note.content)}</p>\n\n {/* Render tagAssignments if available, otherwise fall back to legacy string tags */}\n {note.tagAssignments && note.tagAssignments.length > 0 ? (\n <div className=\"mt-3 flex flex-wrap gap-1\">\n {note.tagAssignments.slice(0, 3).map((assignment) => (\n <span\n key={assignment.id}\n className=\"inline-flex items-center gap-1 px-2 py-0.5 text-xs bg-[var(--color-accent-soft)] text-text-primary rounded\"\n style={\n assignment.tag?.color\n ? { borderLeft: `3px solid ${assignment.tag.color}` }\n : undefined\n }\n >\n <Tag className=\"size-3\" />\n {assignment.tag?.label ?? assignment.tagId.slice(0, 8)}\n </span>\n ))}\n {note.tagAssignments.length > 3 && (\n <span className=\"px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\">\n +{note.tagAssignments.length - 3}\n </span>\n )}\n </div>\n ) : note.tags && note.tags.length > 0 ? (\n <div className=\"mt-3 flex flex-wrap gap-1\">\n {note.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 {note.tags.length > 3 && (\n <span className=\"px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\">\n +{note.tags.length - 3}\n </span>\n )}\n </div>\n ) : null}\n </motion.div>\n );\n}\n"],"mappings":"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"}
@@ -186,7 +186,7 @@ function v({ note: v, vibeOptions: y = [], defaultVibeId: b, hideVibeSelect: x,
186
186
  /* @__PURE__ */ g("div", {
187
187
  className: "flex flex-wrap gap-1.5 min-h-[38px] px-3 py-2 bg-bg-sunken border border-border-default rounded-md",
188
188
  children: [z.filter((e) => j.has(e.id)).map((e) => /* @__PURE__ */ g("span", {
189
- className: "inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-action-primary-bg/10 text-action-primary-bg rounded",
189
+ className: "inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-[var(--color-accent-soft)] text-text-primary rounded",
190
190
  children: [
191
191
  /* @__PURE__ */ h(p, { className: "size-3" }),
192
192
  e.label,
@@ -231,7 +231,7 @@ function v({ note: v, vibeOptions: y = [], defaultVibeId: b, hideVibeSelect: x,
231
231
  type: "button",
232
232
  onClick: q,
233
233
  disabled: !V.trim() || G,
234
- className: "p-1 text-action-primary-bg hover:bg-action-primary-bg/10 rounded transition-colors disabled:opacity-30",
234
+ className: "p-1 text-action-primary-bg hover:bg-[var(--color-accent-soft)] rounded transition-colors disabled:opacity-30",
235
235
  children: /* @__PURE__ */ h(d, { className: "size-4" })
236
236
  })
237
237
  ]