@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.
- package/dist/components/actions/ActionCard.js +2 -2
- package/dist/components/actions/ActionCard.js.map +1 -1
- package/dist/components/actions/ActionTypeBadge.js +1 -1
- package/dist/components/actions/ActionTypeBadge.js.map +1 -1
- package/dist/components/actions/ExecutionHistory.js +1 -1
- package/dist/components/actions/ExecutionHistory.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerAdminBar.js +1 -1
- package/dist/components/agent-manager/AgentManagerAdminBar.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerChatPane.js +2 -2
- package/dist/components/agent-manager/AgentManagerChatPane.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerInputArea.js +4 -4
- package/dist/components/agent-manager/AgentManagerInputArea.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerLayout.js +1 -1
- package/dist/components/agent-manager/AgentManagerLayout.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerMessageItem.js +1 -1
- package/dist/components/agent-manager/AgentManagerMessageItem.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerSidebar.js +5 -5
- package/dist/components/agent-manager/AgentManagerSidebar.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerTab.js +1 -1
- package/dist/components/agent-manager/AgentManagerTab.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerTabBar.js +1 -1
- package/dist/components/agent-manager/AgentManagerTabBar.js.map +1 -1
- package/dist/components/agent-manager/BridleConfigPanel.js +24 -24
- package/dist/components/agent-manager/BridleConfigPanel.js.map +1 -1
- package/dist/components/agent-manager/PluginInstallModal.js +1 -1
- package/dist/components/agent-manager/PluginInstallModal.js.map +1 -1
- package/dist/components/agent-manager/ProfilesPanel.js +5 -5
- package/dist/components/agent-manager/ProfilesPanel.js.map +1 -1
- package/dist/components/agent-manager/controls/AutonomySelector.js +1 -1
- package/dist/components/agent-manager/controls/AutonomySelector.js.map +1 -1
- package/dist/components/agent-manager/controls/SdkSelector.js +1 -1
- package/dist/components/agent-manager/controls/SdkSelector.js.map +1 -1
- package/dist/components/agent-manager/controls/VoiceToggle.js +1 -1
- package/dist/components/agent-manager/controls/VoiceToggle.js.map +1 -1
- package/dist/components/agent-manager/detail/SessionConfigPanel.js +6 -6
- package/dist/components/agent-manager/detail/SessionConfigPanel.js.map +1 -1
- package/dist/components/agent-manager/detail/SessionLogsPanel.js +5 -5
- package/dist/components/agent-manager/detail/SessionLogsPanel.js.map +1 -1
- package/dist/components/agent-manager/detail/SessionToolsPanel.js +1 -1
- package/dist/components/agent-manager/detail/SessionToolsPanel.js.map +1 -1
- package/dist/components/agent-manager/dialogs/NewSessionDialog.js +2 -2
- package/dist/components/agent-manager/dialogs/NewSessionDialog.js.map +1 -1
- package/dist/components/agent-manager/dialogs/RenameSessionDialog.js +1 -1
- package/dist/components/agent-manager/dialogs/RenameSessionDialog.js.map +1 -1
- package/dist/components/agents/AgentCard.js +14 -11
- package/dist/components/agents/AgentCard.js.map +1 -1
- package/dist/components/ai/AIContextsTab.js +1 -1
- package/dist/components/ai/AIContextsTab.js.map +1 -1
- package/dist/components/ai/AIOverviewTab.js +4 -4
- package/dist/components/ai/AIOverviewTab.js.map +1 -1
- package/dist/components/ai/AIPlanPanel.js +4 -4
- package/dist/components/ai/AIPlanPanel.js.map +1 -1
- package/dist/components/ai/AIPlaygroundTab.js +1 -1
- package/dist/components/ai/AIPlaygroundTab.js.map +1 -1
- package/dist/components/ai/AIPromptsTab.js +2 -2
- package/dist/components/ai/AIPromptsTab.js.map +1 -1
- package/dist/components/ai/AIProviderCredentialsCard.js +4 -4
- package/dist/components/ai/AIProviderCredentialsCard.js.map +1 -1
- package/dist/components/ai/AIQueueTab.js +4 -4
- package/dist/components/ai/AIQueueTab.js.map +1 -1
- package/dist/components/ai/AISessionsTab.js +4 -4
- package/dist/components/ai/AISessionsTab.js.map +1 -1
- package/dist/components/ai/AITasksTab.js +1 -1
- package/dist/components/ai/AITasksTab.js.map +1 -1
- package/dist/components/ai/ClaudeCredentialsCard.js +3 -3
- package/dist/components/ai/ClaudeCredentialsCard.js.map +1 -1
- package/dist/components/ai/PluginHarnessPicker.js +3 -3
- package/dist/components/ai/PluginHarnessPicker.js.map +1 -1
- package/dist/components/analytics/AnalyticsBreakdownChart.js +8 -8
- package/dist/components/analytics/AnalyticsBreakdownChart.js.map +1 -1
- package/dist/components/analytics/AnalyticsTimeSeriesChart.js +5 -5
- package/dist/components/analytics/AnalyticsTimeSeriesChart.js.map +1 -1
- package/dist/components/audit/AuditDeleteDialog.js +2 -2
- package/dist/components/audit/AuditDeleteDialog.js.map +1 -1
- package/dist/components/audit/AuditExportButton.js +5 -5
- package/dist/components/audit/AuditExportButton.js.map +1 -1
- package/dist/components/audit/AuditLogDetail.js +9 -9
- package/dist/components/audit/AuditLogDetail.js.map +1 -1
- package/dist/components/audit/AuditLogFilters.js +1 -1
- package/dist/components/audit/AuditLogFilters.js.map +1 -1
- package/dist/components/audit/AuditLogStats.js +1 -1
- package/dist/components/audit/AuditLogStats.js.map +1 -1
- package/dist/components/audit/AuditLogTable.js +28 -28
- package/dist/components/audit/AuditLogTable.js.map +1 -1
- package/dist/components/catalog/CatalogCard.js +3 -3
- package/dist/components/catalog/CatalogCard.js.map +1 -1
- package/dist/components/catalog/CatalogForm.js +1 -1
- package/dist/components/catalog/CatalogForm.js.map +1 -1
- package/dist/components/common/ActionAgentTargetNotice.js +6 -6
- package/dist/components/common/ActionAgentTargetNotice.js.map +1 -1
- package/dist/components/common/CommandEditor.js +1 -1
- package/dist/components/common/CommandEditor.js.map +1 -1
- package/dist/components/docs/DocsPageTree.js +1 -1
- package/dist/components/docs/DocsPageTree.js.map +1 -1
- package/dist/components/docs/DocsPreviewPanel.js +1 -1
- package/dist/components/docs/DocsPreviewPanel.js.map +1 -1
- package/dist/components/docs/DocsRevisionHistory.js +4 -4
- package/dist/components/docs/DocsRevisionHistory.js.map +1 -1
- package/dist/components/docs/DocsSettingsPanel.js +7 -7
- package/dist/components/docs/DocsSettingsPanel.js.map +1 -1
- package/dist/components/docs/DocsTabPanel.js +1 -1
- package/dist/components/docs/DocsTabPanel.js.map +1 -1
- package/dist/components/graph/ConnectionPermissionsDialog.js +2 -2
- package/dist/components/graph/ConnectionPermissionsDialog.js.map +1 -1
- package/dist/components/graph/MeshDeployWizard.js +3 -3
- package/dist/components/graph/MeshDeployWizard.js.map +1 -1
- package/dist/components/graph/MeshRequestPanel.js +2 -2
- package/dist/components/graph/MeshRequestPanel.js.map +1 -1
- package/dist/components/idp-components/ComponentCard.js +5 -5
- package/dist/components/idp-components/ComponentCard.js.map +1 -1
- package/dist/components/idp-components/ComponentForm.js +1 -1
- package/dist/components/idp-components/ComponentForm.js.map +1 -1
- package/dist/components/idp-components/DependencyGraphTab.js +7 -7
- package/dist/components/idp-components/DependencyGraphTab.js.map +1 -1
- package/dist/components/logs/LogViewer.js +14 -14
- package/dist/components/logs/LogViewer.js.map +1 -1
- package/dist/components/notes/NoteCard.js +1 -1
- package/dist/components/notes/NoteCard.js.map +1 -1
- package/dist/components/notes/NoteEditor.js +2 -2
- package/dist/components/notes/NoteEditor.js.map +1 -1
- package/dist/components/notes/NoteList.js +1 -1
- package/dist/components/notes/NoteList.js.map +1 -1
- package/dist/components/notes/NotesTabPanel.js +1 -1
- package/dist/components/notes/NotesTabPanel.js.map +1 -1
- package/dist/components/scorecard/ScorecardEmptyState.js +1 -1
- package/dist/components/scorecard/ScorecardEmptyState.js.map +1 -1
- package/dist/components/scorecard/ScorecardPanel.js +3 -3
- package/dist/components/scorecard/ScorecardPanel.js.map +1 -1
- package/dist/components/security/SecurityScanList.js +11 -11
- package/dist/components/security/SecurityScanList.js.map +1 -1
- package/dist/components/sessions/SessionCard.js +26 -19
- package/dist/components/sessions/SessionCard.js.map +1 -1
- package/dist/components/sessions/SessionStatusBadge.js +7 -7
- package/dist/components/sessions/SessionStatusBadge.js.map +1 -1
- package/dist/components/shared/AgentSelector.js +4 -4
- package/dist/components/shared/AgentSelector.js.map +1 -1
- package/dist/components/shared/AgentSetupBanner.js +4 -4
- package/dist/components/shared/AgentSetupBanner.js.map +1 -1
- package/dist/components/shared/Card.js +4 -4
- package/dist/components/shared/Card.js.map +1 -1
- package/dist/components/shared/ErrorState.js +15 -9
- package/dist/components/shared/ErrorState.js.map +1 -1
- package/dist/components/shared/FilterDropdown.js +2 -2
- package/dist/components/shared/FilterDropdown.js.map +1 -1
- package/dist/components/shared/PageSkeleton.js +6 -6
- package/dist/components/shared/PageSkeleton.js.map +1 -1
- package/dist/components/shared/SearchInput.js +1 -1
- package/dist/components/shared/SearchInput.js.map +1 -1
- package/dist/components/shared/StatCard.js +7 -7
- package/dist/components/shared/StatCard.js.map +1 -1
- package/dist/components/templates/PublishTemplateForm.js +2 -2
- package/dist/components/templates/PublishTemplateForm.js.map +1 -1
- package/dist/components/templates/TemplateCard.js +5 -5
- package/dist/components/templates/TemplateCard.js.map +1 -1
- package/dist/components/templates/TemplateWizard.js +2 -2
- package/dist/components/templates/TemplateWizard.js.map +1 -1
- package/dist/components/tunnels/DomainVerificationBadge.js +4 -4
- package/dist/components/tunnels/DomainVerificationBadge.js.map +1 -1
- package/dist/components/tunnels/ProviderBadge.js +1 -1
- package/dist/components/tunnels/ProviderBadge.js.map +1 -1
- package/dist/components/tunnels/TunnelCard.js +16 -16
- package/dist/components/tunnels/TunnelCard.js.map +1 -1
- package/dist/components/tunnels/TunnelCreateWizard.js +1 -1
- package/dist/components/tunnels/TunnelCreateWizard.js.map +1 -1
- package/dist/components/tunnels/TunnelPublicAccessPanel.js +1 -1
- package/dist/components/tunnels/TunnelPublicAccessPanel.js.map +1 -1
- package/dist/components/tunnels/TunnelStatusBadge.js +4 -4
- package/dist/components/tunnels/TunnelStatusBadge.js.map +1 -1
- package/dist/components/vibecalendar/LogsDrawer.js +2 -2
- package/dist/components/vibecalendar/LogsDrawer.js.map +1 -1
- package/dist/components/vibecalendar/ScheduleTaskModal.js +5 -5
- package/dist/components/vibecalendar/ScheduleTaskModal.js.map +1 -1
- package/dist/components/vibecalendar/VibeCalAgendaView.js +2 -2
- package/dist/components/vibecalendar/VibeCalAgendaView.js.map +1 -1
- package/dist/components/vibecalendar/VibeCalDayView.js +3 -3
- package/dist/components/vibecalendar/VibeCalDayView.js.map +1 -1
- package/dist/components/vibecalendar/VibeCalMonthView.js +1 -1
- package/dist/components/vibecalendar/VibeCalMonthView.js.map +1 -1
- package/dist/components/vibecalendar/VibeCalWeekView.js +4 -4
- package/dist/components/vibecalendar/VibeCalWeekView.js.map +1 -1
- package/dist/components/vibecalendar/VibeCalendarHeader.js +4 -4
- package/dist/components/vibecalendar/VibeCalendarHeader.js.map +1 -1
- package/dist/components/vibecalendar/VibeTaskBlock.js +2 -2
- package/dist/components/vibecalendar/VibeTaskBlock.js.map +1 -1
- package/dist/components/vibecalendar/types.js +7 -7
- package/dist/components/vibecalendar/types.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckButtonForm.js +9 -9
- package/dist/components/vibedeck/VibeDeckButtonForm.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckButtonTile.js +6 -6
- package/dist/components/vibedeck/VibeDeckButtonTile.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckExecutionLog.js +10 -10
- package/dist/components/vibedeck/VibeDeckExecutionLog.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckForm.js +8 -8
- package/dist/components/vibedeck/VibeDeckForm.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckGrid.js +2 -2
- package/dist/components/vibedeck/VibeDeckGrid.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckPublicLinkPanel.js +2 -2
- package/dist/components/vibedeck/VibeDeckPublicLinkPanel.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckSwitcher.js +8 -8
- package/dist/components/vibedeck/VibeDeckSwitcher.js.map +1 -1
- package/dist/components/vibes/VibeCard.js +22 -18
- package/dist/components/vibes/VibeCard.js.map +1 -1
- package/dist/components/vibes/VibeList.js +16 -12
- package/dist/components/vibes/VibeList.js.map +1 -1
- package/dist/pages/ActionsPage.js +4 -3
- package/dist/pages/ActionsPage.js.map +1 -1
- package/dist/pages/AgentDetailsPage.js +9 -9
- package/dist/pages/AgentDetailsPage.js.map +1 -1
- package/dist/pages/AgentsLandingPage.js +4 -4
- package/dist/pages/AgentsLandingPage.js.map +1 -1
- package/dist/pages/AgentsPage.js +14 -14
- package/dist/pages/AgentsPage.js.map +1 -1
- package/dist/pages/AnalyticsPage.js +8 -8
- package/dist/pages/AnalyticsPage.js.map +1 -1
- package/dist/pages/CatalogDetailPage.js +11 -11
- package/dist/pages/CatalogDetailPage.js.map +1 -1
- package/dist/pages/CatalogPage.js +12 -9
- package/dist/pages/CatalogPage.js.map +1 -1
- package/dist/pages/ComponentDetailPage.js +7 -7
- package/dist/pages/ComponentDetailPage.js.map +1 -1
- package/dist/pages/ComponentsPage.js +12 -9
- package/dist/pages/ComponentsPage.js.map +1 -1
- package/dist/pages/EnvTopologyPage.js +2 -2
- package/dist/pages/EnvTopologyPage.js.map +1 -1
- package/dist/pages/GettingStartedPage.js +12 -12
- package/dist/pages/GettingStartedPage.js.map +1 -1
- package/dist/pages/HealthPage.js +12 -12
- package/dist/pages/HealthPage.js.map +1 -1
- package/dist/pages/LogsPage.js +4 -4
- package/dist/pages/LogsPage.js.map +1 -1
- package/dist/pages/NoteDetailsPage.js +5 -5
- package/dist/pages/NoteDetailsPage.js.map +1 -1
- package/dist/pages/OverviewPage.js +76 -43
- package/dist/pages/OverviewPage.js.map +1 -1
- package/dist/pages/PluginsCatalogPage.js +7 -4
- package/dist/pages/PluginsCatalogPage.js.map +1 -1
- package/dist/pages/ScheduledJobDetailPage.js +7 -7
- package/dist/pages/ScheduledJobDetailPage.js.map +1 -1
- package/dist/pages/SessionDetailsPage.js +21 -21
- package/dist/pages/SessionDetailsPage.js.map +1 -1
- package/dist/pages/SessionsPage.js +20 -20
- package/dist/pages/SessionsPage.js.map +1 -1
- package/dist/pages/SettingsPage.js +64 -52
- package/dist/pages/SettingsPage.js.map +1 -1
- package/dist/pages/SharedSessionPage.js +21 -21
- package/dist/pages/SharedSessionPage.js.map +1 -1
- package/dist/pages/SharedVibeDeckPage.js +5 -5
- package/dist/pages/SharedVibeDeckPage.js.map +1 -1
- package/dist/pages/TargetDetailsPage.js +56 -65
- package/dist/pages/TargetDetailsPage.js.map +1 -1
- package/dist/pages/TargetsPage.js +4 -4
- package/dist/pages/TargetsPage.js.map +1 -1
- package/dist/pages/TemplateDetailPage.js +10 -10
- package/dist/pages/TemplateDetailPage.js.map +1 -1
- package/dist/pages/TemplatesPage.js +6 -5
- package/dist/pages/TemplatesPage.js.map +1 -1
- package/dist/pages/VibeAuditPage.js +3 -3
- package/dist/pages/VibeAuditPage.js.map +1 -1
- package/dist/pages/VibeCalendarPage.js +3 -3
- package/dist/pages/VibeCalendarPage.js.map +1 -1
- package/dist/pages/VibeDeckPage.js +3 -3
- package/dist/pages/VibeDeckPage.js.map +1 -1
- package/dist/pages/VibeDetailsPage.js +15 -15
- package/dist/pages/VibeDetailsPage.js.map +1 -1
- package/dist/pages/VibeTaskDetailPage.js +5 -5
- package/dist/pages/VibeTaskDetailPage.js.map +1 -1
- package/dist/pages/VibesPage.js +18 -18
- package/dist/pages/VibesPage.js.map +1 -1
- package/dist/pages/WebhooksPage.js +11 -11
- package/dist/pages/WebhooksPage.js.map +1 -1
- package/dist/pages/ai/AIBookmarksPage.js +1 -1
- package/dist/pages/ai/AIBookmarksPage.js.map +1 -1
- package/dist/pages/ai/AISessionDetailPage.js +6 -6
- package/dist/pages/ai/AISessionDetailPage.js.map +1 -1
- package/dist/pages/tunnels/AuditTab.js +1 -1
- package/dist/pages/tunnels/AuditTab.js.map +1 -1
- package/dist/pages/tunnels/DomainsTab.js +1 -1
- package/dist/pages/tunnels/DomainsTab.js.map +1 -1
- package/dist/pages/tunnels/ProvidersTab.js +1 -1
- package/dist/pages/tunnels/ProvidersTab.js.map +1 -1
- package/dist/pages/tunnels/SessionsTab.js +1 -1
- package/dist/pages/tunnels/SessionsTab.js.map +1 -1
- package/dist/pages/tunnels/TunnelDetailsPage.js +3 -3
- package/dist/pages/tunnels/TunnelDetailsPage.js.map +1 -1
- package/dist/pages/tunnels/UsageTab.js +1 -1
- package/dist/pages/tunnels/UsageTab.js.map +1 -1
- package/dist/pages/tunnels/tabs/TunnelDomainsTab.js +16 -16
- package/dist/pages/tunnels/tabs/TunnelDomainsTab.js.map +1 -1
- package/dist/pages/tunnels/tabs/TunnelOverviewTab.js +1 -1
- package/dist/pages/tunnels/tabs/TunnelOverviewTab.js.map +1 -1
- package/dist/pages/tunnels/tabs/TunnelSessionsTab.js +52 -49
- package/dist/pages/tunnels/tabs/TunnelSessionsTab.js.map +1 -1
- package/dist/pages/tunnels/tabs/TunnelSettingsTab.js +9 -9
- package/dist/pages/tunnels/tabs/TunnelSettingsTab.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocsPageTree.js","names":[],"sources":["../../../src/components/docs/DocsPageTree.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n FileText,\n FolderOpen,\n Plus,\n Trash2,\n ChevronRight,\n ChevronDown,\n Loader2,\n} from 'lucide-react';\nimport type { DocsPage } from './types';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface DocsPageTreeProps {\n pages: DocsPage[];\n selectedPageId: string | null;\n onSelectPage: (pageId: string) => void;\n onAddPage: (parentId?: string, category?: string) => void;\n pageAdding?: boolean;\n onDeletePage: (pageId: string) => void;\n}\n\ninterface TreeNode {\n page: DocsPage;\n children: TreeNode[];\n}\n\nfunction buildTree(pages: DocsPage[]): TreeNode[] {\n const map = new Map<string, TreeNode>();\n const roots: TreeNode[] = [];\n\n for (const page of pages) {\n map.set(page.id, { page, children: [] });\n }\n\n for (const page of pages) {\n const node = map.get(page.id)!;\n if (page.parentId && map.has(page.parentId)) {\n map.get(page.parentId)!.children.push(node);\n } else {\n roots.push(node);\n }\n }\n\n return roots;\n}\n\nfunction TreeItem({\n node,\n depth,\n selectedPageId,\n onSelectPage,\n onDeletePage,\n expandedIds,\n toggleExpand,\n}: {\n node: TreeNode;\n depth: number;\n selectedPageId: string | null;\n onSelectPage: (id: string) => void;\n onDeletePage: (id: string) => void;\n expandedIds: Set<string>;\n toggleExpand: (id: string) => void;\n}) {\n const isSelected = selectedPageId === node.page.id;\n const hasChildren = node.children.length > 0;\n const isExpanded = expandedIds.has(node.page.id);\n\n return (\n <div>\n <div\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (() => onSelectPage(node.page.id))();\n }\n }}\n className={`flex items-center gap-1 px-2 py-1.5 cursor-pointer rounded text-sm transition-colors group ${\n isSelected\n ? 'bg-
|
|
1
|
+
{"version":3,"file":"DocsPageTree.js","names":[],"sources":["../../../src/components/docs/DocsPageTree.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n FileText,\n FolderOpen,\n Plus,\n Trash2,\n ChevronRight,\n ChevronDown,\n Loader2,\n} from 'lucide-react';\nimport type { DocsPage } from './types';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface DocsPageTreeProps {\n pages: DocsPage[];\n selectedPageId: string | null;\n onSelectPage: (pageId: string) => void;\n onAddPage: (parentId?: string, category?: string) => void;\n pageAdding?: boolean;\n onDeletePage: (pageId: string) => void;\n}\n\ninterface TreeNode {\n page: DocsPage;\n children: TreeNode[];\n}\n\nfunction buildTree(pages: DocsPage[]): TreeNode[] {\n const map = new Map<string, TreeNode>();\n const roots: TreeNode[] = [];\n\n for (const page of pages) {\n map.set(page.id, { page, children: [] });\n }\n\n for (const page of pages) {\n const node = map.get(page.id)!;\n if (page.parentId && map.has(page.parentId)) {\n map.get(page.parentId)!.children.push(node);\n } else {\n roots.push(node);\n }\n }\n\n return roots;\n}\n\nfunction TreeItem({\n node,\n depth,\n selectedPageId,\n onSelectPage,\n onDeletePage,\n expandedIds,\n toggleExpand,\n}: {\n node: TreeNode;\n depth: number;\n selectedPageId: string | null;\n onSelectPage: (id: string) => void;\n onDeletePage: (id: string) => void;\n expandedIds: Set<string>;\n toggleExpand: (id: string) => void;\n}) {\n const isSelected = selectedPageId === node.page.id;\n const hasChildren = node.children.length > 0;\n const isExpanded = expandedIds.has(node.page.id);\n\n return (\n <div>\n <div\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (() => onSelectPage(node.page.id))();\n }\n }}\n className={`flex items-center gap-1 px-2 py-1.5 cursor-pointer rounded text-sm transition-colors group ${\n isSelected\n ? 'bg-[var(--color-accent-soft)] text-text-primary'\n : 'text-text-secondary hover:bg-bg-sunken hover:text-text-primary'\n }`}\n style={{ paddingLeft: `${8 + depth * 16}px` }}\n onClick={() => onSelectPage(node.page.id)}\n >\n {hasChildren ? (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n toggleExpand(node.page.id);\n }}\n className=\"p-0.5 -ml-1\"\n >\n {isExpanded ? <ChevronDown className=\"size-3\" /> : <ChevronRight className=\"size-3\" />}\n </button>\n ) : (\n <span className=\"w-4\" />\n )}\n {node.page.category ? (\n <FolderOpen className=\"size-3.5 flex-shrink-0\" />\n ) : (\n <FileText className=\"size-3.5 flex-shrink-0\" />\n )}\n <span className=\"truncate flex-1\">{node.page.title}</span>\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onDeletePage(node.page.id);\n }}\n className=\"opacity-0 group-hover:opacity-100 p-0.5 text-text-muted hover:text-status-error-text transition-all\"\n title=\"Delete\"\n >\n <Trash2 className=\"size-3\" />\n </button>\n </div>\n {hasChildren && isExpanded && (\n <div>\n {node.children.map((child) => (\n <TreeItem\n key={child.page.id}\n node={child}\n depth={depth + 1}\n selectedPageId={selectedPageId}\n onSelectPage={onSelectPage}\n onDeletePage={onDeletePage}\n expandedIds={expandedIds}\n toggleExpand={toggleExpand}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n\nexport function DocsPageTree({\n pages,\n selectedPageId,\n onSelectPage,\n onAddPage,\n pageAdding,\n onDeletePage,\n}: DocsPageTreeProps) {\n const tr = useTr();\n const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set(pages.map((p) => p.id)));\n\n const toggleExpand = (id: string) => {\n setExpandedIds((prev) => {\n const next = new Set(prev);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n };\n\n const tree = buildTree(pages);\n\n return (\n <div className=\"flex flex-col h-full\">\n <div className=\"flex items-center justify-between px-3 py-2 border-b border-border-default\">\n <span className=\"text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.docs.pageTree.pages', 'Pages')}\n </span>\n <button\n type=\"button\"\n onClick={() => onAddPage()}\n disabled={pageAdding}\n className=\"p-1 text-text-secondary hover:text-text-primary transition-colors disabled:opacity-50\"\n title=\"Add page\"\n >\n {pageAdding ? <Loader2 className=\"size-4 animate-spin\" /> : <Plus className=\"size-4\" />}\n </button>\n </div>\n <div className=\"flex-1 overflow-y-auto py-1\">\n {tree.length > 0 ? (\n tree.map((node) => (\n <TreeItem\n key={node.page.id}\n node={node}\n depth={0}\n selectedPageId={selectedPageId}\n onSelectPage={onSelectPage}\n onDeletePage={onDeletePage}\n expandedIds={expandedIds}\n toggleExpand={toggleExpand}\n />\n ))\n ) : (\n <div className=\"p-4 text-center\">\n <p className=\"text-sm text-text-muted mb-2\">\n {tr('vibecontrols.docs.pageTree.noPages', 'No pages yet')}\n </p>\n <button\n type=\"button\"\n onClick={() => onAddPage()}\n className=\"text-sm text-action-primary-bg hover:underline\"\n >\n {tr('vibecontrols.docs.pageTree.addFirstPage', 'Add your first page')}\n </button>\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -85,7 +85,7 @@ function g({ site: g, pages: _ = [], selectedPageSlug: v = "", onSelectPage: y,
|
|
|
85
85
|
})]
|
|
86
86
|
}),
|
|
87
87
|
_.length > 1 && N && /* @__PURE__ */ m("div", {
|
|
88
|
-
className: "flex items-center gap-1 overflow-x-auto pb-1 border-b border-border-
|
|
88
|
+
className: "flex items-center gap-1 overflow-x-auto pb-1 border-b border-border-default",
|
|
89
89
|
children: _.map((e, t) => /* @__PURE__ */ m("button", {
|
|
90
90
|
type: "button",
|
|
91
91
|
onClick: () => y?.(t === 0 ? "" : e.slug),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocsPreviewPanel.js","names":[],"sources":["../../../src/components/docs/DocsPreviewPanel.tsx"],"sourcesContent":["import { useState, type ReactNode } from 'react';\nimport {\n Edit3,\n RefreshCw,\n Settings,\n ExternalLink,\n Loader2,\n AlertTriangle,\n XCircle,\n Link2,\n Check,\n} from 'lucide-react';\nimport { Card } from '@/components/shared';\nimport { DocsCompileStatus } from './DocsCompileStatus';\nimport { createDocsPreviewHtml } from './previewHtml';\nimport type { DocsSite, DocsRevision, DocsPage } from './types';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface DocsPreviewPanelProps {\n site: DocsSite;\n pages?: DocsPage[];\n selectedPageSlug?: string;\n onSelectPage?: (slug: string) => void;\n docsBaseUrl: string | null;\n docsHtml: string | null;\n docsContentLoading?: boolean;\n onEdit: () => void;\n onCompile: () => void;\n onSettings: () => void;\n onGetShareUrl?: () => Promise<string | null>;\n onOpenFullScreen: () => void;\n compileRevision: DocsRevision | null;\n onPollRevision?: (id: string) => Promise<DocsRevision | null>;\n compiling: boolean;\n /** Generic grant-based Share affordance (owner-keyed; supplied by the host). */\n shareSlot?: ReactNode;\n}\n\nexport function DocsPreviewPanel({\n site,\n pages = [],\n selectedPageSlug = '',\n onSelectPage,\n docsBaseUrl,\n docsHtml,\n docsContentLoading,\n onEdit,\n onCompile,\n onSettings,\n onGetShareUrl,\n onOpenFullScreen,\n compileRevision,\n onPollRevision,\n compiling,\n shareSlot,\n}: DocsPreviewPanelProps) {\n const tr = useTr();\n const currentRev = site.currentRevision;\n const [copying, setCopying] = useState(false);\n const [copied, setCopied] = useState(false);\n\n const handleCopyLink = async () => {\n if (!onGetShareUrl) return;\n setCopying(true);\n try {\n const url = await onGetShareUrl();\n if (url) {\n await navigator.clipboard.writeText(url);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n }\n } finally {\n setCopying(false);\n }\n };\n const previewHtml = docsHtml ? createDocsPreviewHtml(docsHtml) : null;\n\n const renderPreviewArea = () => {\n // Inline srcDoc preview: extracts prose content from the compiled HTML and renders\n // it with self-contained typography styles in a sandboxed iframe. The gateway src\n // approach requires the full gateway stack (not available in local dev) and cannot\n // send auth headers from a browser iframe, so srcdoc is the reliable path.\n if (previewHtml && currentRev) {\n return (\n <Card className=\"overflow-hidden\">\n <iframe\n key={`${currentRev.id}-${selectedPageSlug}`}\n srcDoc={previewHtml}\n className=\"w-full border-0\"\n style={{ height: 'calc(100vh - 380px)', minHeight: '400px' }}\n title=\"Documentation Preview\"\n sandbox=\"allow-same-origin allow-forms allow-popups\"\n />\n </Card>\n );\n }\n\n // Revision exists but content is still loading\n if (currentRev && docsContentLoading) {\n return (\n <Card className=\"p-8 text-center\">\n <div className=\"flex flex-col items-center gap-2 text-text-muted\">\n <Loader2 className=\"size-5 animate-spin\" />\n <p className=\"text-sm\">Loading documentation preview…</p>\n </div>\n </Card>\n );\n }\n\n // Revision exists but content failed to load (not loading, not available)\n if (currentRev && !docsContentLoading && !docsHtml) {\n return (\n <Card className=\"p-8 text-center\">\n <div className=\"flex flex-col items-center gap-3 text-text-secondary\">\n <AlertTriangle className=\"size-5 text-status-warning-text\" />\n <p className=\"text-sm\">\n The preview could not be loaded. The site may still be processing, or the build\n artifact may have expired.\n </p>\n <button\n type=\"button\"\n onClick={onCompile}\n disabled={compiling}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md text-sm hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n Recompile Docs\n </button>\n </div>\n </Card>\n );\n }\n\n // Last compile failed with no prior successful revision — show failure prominently\n if (compileRevision?.status === 'FAILED') {\n return (\n <Card className=\"p-8 text-center\">\n <div className=\"flex flex-col items-center gap-3\">\n <XCircle className=\"size-8 text-status-error-text\" />\n <div>\n <p className=\"font-medium text-text-primary mb-1\">Build failed</p>\n {compileRevision.errorMessage && (\n <p className=\"text-sm text-status-error-text bg-status-error-bg rounded px-3 py-2 text-left max-w-md mx-auto mb-3\">\n {compileRevision.errorMessage}\n </p>\n )}\n <p className=\"text-sm text-text-muted\">\n Fix your pages in the editor and try compiling again.\n </p>\n </div>\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={onEdit}\n className=\"px-4 py-2 bg-bg-sunken text-text-primary rounded-md text-sm hover:bg-bg-sunken/80 transition-colors\"\n >\n Edit Pages\n </button>\n <button\n type=\"button\"\n onClick={onCompile}\n disabled={compiling}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md text-sm hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n Retry Compile\n </button>\n </div>\n </div>\n </Card>\n );\n }\n\n // No revision yet — prompt to start editing\n return (\n <Card className=\"p-8 text-center\">\n <p className=\"text-text-secondary mb-1 font-medium\">\n {tr('vibecontrols.docs.preview.noPublished', 'No published documentation yet')}\n </p>\n <p className=\"text-sm text-text-muted mb-4\">\n Add pages in the editor, then click <strong>Compile</strong> to build and publish your\n docs site. Compiled output is stored and served from S3 — compiling takes 1–3 minutes.\n </p>\n <button\n type=\"button\"\n onClick={onEdit}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('vibecontrols.docs.preview.startEditing', 'Start Editing')}\n </button>\n </Card>\n );\n };\n\n return (\n <div className=\"space-y-4\">\n {/* Action bar */}\n <div className=\"flex items-center justify-between flex-wrap gap-2\">\n <div className=\"flex items-center gap-3\">\n <h3 className=\"font-semibold text-text-primary\">{site.name}</h3>\n {currentRev && (\n <span className=\"text-xs text-text-muted bg-bg-sunken px-2 py-0.5 rounded\">\n Rev #{currentRev.revisionNumber}\n </span>\n )}\n <div className=\"relative\">\n <DocsCompileStatus revision={compileRevision} onPoll={onPollRevision} />\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n {shareSlot}\n {currentRev && onGetShareUrl && (\n <button\n type=\"button\"\n onClick={handleCopyLink}\n disabled={copying}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-sm bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken/80 transition-colors disabled:opacity-50\"\n title={\n site.visibility === 'PUBLIC' ? 'Copy public link' : 'Copy signed link (expires)'\n }\n >\n {copied ? (\n <Check className=\"size-3.5 text-status-success-text\" />\n ) : copying ? (\n <Loader2 className=\"size-3.5 animate-spin\" />\n ) : (\n <Link2 className=\"size-3.5\" />\n )}\n {copied ? 'Copied!' : tr('vibecontrols.docs.preview.copyLink', 'Copy Link')}\n </button>\n )}\n {currentRev && (\n <button\n type=\"button\"\n onClick={onOpenFullScreen}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-sm bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken/80 transition-colors\"\n >\n <ExternalLink className=\"size-3.5\" />\n {tr('vibecontrols.docs.preview.openFullScreen', 'Open Full Screen')}\n </button>\n )}\n <button\n type=\"button\"\n onClick={onEdit}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-sm bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken/80 transition-colors\"\n >\n <Edit3 className=\"size-3.5\" />\n {tr('vibecontrols.docs.preview.edit', 'Edit')}\n </button>\n <button\n type=\"button\"\n onClick={onCompile}\n disabled={compiling}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n <RefreshCw className={`size-3.5 ${compiling ? 'animate-spin' : ''}`} />\n {tr('vibecontrols.docs.preview.compileDocs', 'Compile Docs')}\n </button>\n <button\n type=\"button\"\n onClick={onSettings}\n className=\"p-1.5 text-text-secondary hover:text-text-primary transition-colors\"\n title=\"Site settings\"\n >\n <Settings className=\"size-4\" />\n </button>\n </div>\n </div>\n\n {/* Page navigation tabs — shown when there are multiple compiled pages */}\n {pages.length > 1 && currentRev && (\n <div className=\"flex items-center gap-1 overflow-x-auto pb-1 border-b border-border-primary\">\n {pages.map((page, i) => {\n const isSelected = i === 0 ? selectedPageSlug === '' : selectedPageSlug === page.slug;\n return (\n <button\n type=\"button\"\n key={page.id}\n onClick={() => onSelectPage?.(i === 0 ? '' : page.slug)}\n className={`shrink-0 px-3 py-1.5 text-sm rounded-t-md whitespace-nowrap transition-colors ${\n isSelected\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'text-text-secondary hover:text-text-primary hover:bg-bg-sunken'\n }`}\n >\n {page.title}\n </button>\n );\n })}\n </div>\n )}\n\n {renderPreviewArea()}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DocsPreviewPanel.js","names":[],"sources":["../../../src/components/docs/DocsPreviewPanel.tsx"],"sourcesContent":["import { useState, type ReactNode } from 'react';\nimport {\n Edit3,\n RefreshCw,\n Settings,\n ExternalLink,\n Loader2,\n AlertTriangle,\n XCircle,\n Link2,\n Check,\n} from 'lucide-react';\nimport { Card } from '@/components/shared';\nimport { DocsCompileStatus } from './DocsCompileStatus';\nimport { createDocsPreviewHtml } from './previewHtml';\nimport type { DocsSite, DocsRevision, DocsPage } from './types';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface DocsPreviewPanelProps {\n site: DocsSite;\n pages?: DocsPage[];\n selectedPageSlug?: string;\n onSelectPage?: (slug: string) => void;\n docsBaseUrl: string | null;\n docsHtml: string | null;\n docsContentLoading?: boolean;\n onEdit: () => void;\n onCompile: () => void;\n onSettings: () => void;\n onGetShareUrl?: () => Promise<string | null>;\n onOpenFullScreen: () => void;\n compileRevision: DocsRevision | null;\n onPollRevision?: (id: string) => Promise<DocsRevision | null>;\n compiling: boolean;\n /** Generic grant-based Share affordance (owner-keyed; supplied by the host). */\n shareSlot?: ReactNode;\n}\n\nexport function DocsPreviewPanel({\n site,\n pages = [],\n selectedPageSlug = '',\n onSelectPage,\n docsBaseUrl,\n docsHtml,\n docsContentLoading,\n onEdit,\n onCompile,\n onSettings,\n onGetShareUrl,\n onOpenFullScreen,\n compileRevision,\n onPollRevision,\n compiling,\n shareSlot,\n}: DocsPreviewPanelProps) {\n const tr = useTr();\n const currentRev = site.currentRevision;\n const [copying, setCopying] = useState(false);\n const [copied, setCopied] = useState(false);\n\n const handleCopyLink = async () => {\n if (!onGetShareUrl) return;\n setCopying(true);\n try {\n const url = await onGetShareUrl();\n if (url) {\n await navigator.clipboard.writeText(url);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n }\n } finally {\n setCopying(false);\n }\n };\n const previewHtml = docsHtml ? createDocsPreviewHtml(docsHtml) : null;\n\n const renderPreviewArea = () => {\n // Inline srcDoc preview: extracts prose content from the compiled HTML and renders\n // it with self-contained typography styles in a sandboxed iframe. The gateway src\n // approach requires the full gateway stack (not available in local dev) and cannot\n // send auth headers from a browser iframe, so srcdoc is the reliable path.\n if (previewHtml && currentRev) {\n return (\n <Card className=\"overflow-hidden\">\n <iframe\n key={`${currentRev.id}-${selectedPageSlug}`}\n srcDoc={previewHtml}\n className=\"w-full border-0\"\n style={{ height: 'calc(100vh - 380px)', minHeight: '400px' }}\n title=\"Documentation Preview\"\n sandbox=\"allow-same-origin allow-forms allow-popups\"\n />\n </Card>\n );\n }\n\n // Revision exists but content is still loading\n if (currentRev && docsContentLoading) {\n return (\n <Card className=\"p-8 text-center\">\n <div className=\"flex flex-col items-center gap-2 text-text-muted\">\n <Loader2 className=\"size-5 animate-spin\" />\n <p className=\"text-sm\">Loading documentation preview…</p>\n </div>\n </Card>\n );\n }\n\n // Revision exists but content failed to load (not loading, not available)\n if (currentRev && !docsContentLoading && !docsHtml) {\n return (\n <Card className=\"p-8 text-center\">\n <div className=\"flex flex-col items-center gap-3 text-text-secondary\">\n <AlertTriangle className=\"size-5 text-status-warning-text\" />\n <p className=\"text-sm\">\n The preview could not be loaded. The site may still be processing, or the build\n artifact may have expired.\n </p>\n <button\n type=\"button\"\n onClick={onCompile}\n disabled={compiling}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md text-sm hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n Recompile Docs\n </button>\n </div>\n </Card>\n );\n }\n\n // Last compile failed with no prior successful revision — show failure prominently\n if (compileRevision?.status === 'FAILED') {\n return (\n <Card className=\"p-8 text-center\">\n <div className=\"flex flex-col items-center gap-3\">\n <XCircle className=\"size-8 text-status-error-text\" />\n <div>\n <p className=\"font-medium text-text-primary mb-1\">Build failed</p>\n {compileRevision.errorMessage && (\n <p className=\"text-sm text-status-error-text bg-status-error-bg rounded px-3 py-2 text-left max-w-md mx-auto mb-3\">\n {compileRevision.errorMessage}\n </p>\n )}\n <p className=\"text-sm text-text-muted\">\n Fix your pages in the editor and try compiling again.\n </p>\n </div>\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={onEdit}\n className=\"px-4 py-2 bg-bg-sunken text-text-primary rounded-md text-sm hover:bg-bg-sunken/80 transition-colors\"\n >\n Edit Pages\n </button>\n <button\n type=\"button\"\n onClick={onCompile}\n disabled={compiling}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md text-sm hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n Retry Compile\n </button>\n </div>\n </div>\n </Card>\n );\n }\n\n // No revision yet — prompt to start editing\n return (\n <Card className=\"p-8 text-center\">\n <p className=\"text-text-secondary mb-1 font-medium\">\n {tr('vibecontrols.docs.preview.noPublished', 'No published documentation yet')}\n </p>\n <p className=\"text-sm text-text-muted mb-4\">\n Add pages in the editor, then click <strong>Compile</strong> to build and publish your\n docs site. Compiled output is stored and served from S3 — compiling takes 1–3 minutes.\n </p>\n <button\n type=\"button\"\n onClick={onEdit}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('vibecontrols.docs.preview.startEditing', 'Start Editing')}\n </button>\n </Card>\n );\n };\n\n return (\n <div className=\"space-y-4\">\n {/* Action bar */}\n <div className=\"flex items-center justify-between flex-wrap gap-2\">\n <div className=\"flex items-center gap-3\">\n <h3 className=\"font-semibold text-text-primary\">{site.name}</h3>\n {currentRev && (\n <span className=\"text-xs text-text-muted bg-bg-sunken px-2 py-0.5 rounded\">\n Rev #{currentRev.revisionNumber}\n </span>\n )}\n <div className=\"relative\">\n <DocsCompileStatus revision={compileRevision} onPoll={onPollRevision} />\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n {shareSlot}\n {currentRev && onGetShareUrl && (\n <button\n type=\"button\"\n onClick={handleCopyLink}\n disabled={copying}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-sm bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken/80 transition-colors disabled:opacity-50\"\n title={\n site.visibility === 'PUBLIC' ? 'Copy public link' : 'Copy signed link (expires)'\n }\n >\n {copied ? (\n <Check className=\"size-3.5 text-status-success-text\" />\n ) : copying ? (\n <Loader2 className=\"size-3.5 animate-spin\" />\n ) : (\n <Link2 className=\"size-3.5\" />\n )}\n {copied ? 'Copied!' : tr('vibecontrols.docs.preview.copyLink', 'Copy Link')}\n </button>\n )}\n {currentRev && (\n <button\n type=\"button\"\n onClick={onOpenFullScreen}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-sm bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken/80 transition-colors\"\n >\n <ExternalLink className=\"size-3.5\" />\n {tr('vibecontrols.docs.preview.openFullScreen', 'Open Full Screen')}\n </button>\n )}\n <button\n type=\"button\"\n onClick={onEdit}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-sm bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken/80 transition-colors\"\n >\n <Edit3 className=\"size-3.5\" />\n {tr('vibecontrols.docs.preview.edit', 'Edit')}\n </button>\n <button\n type=\"button\"\n onClick={onCompile}\n disabled={compiling}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n <RefreshCw className={`size-3.5 ${compiling ? 'animate-spin' : ''}`} />\n {tr('vibecontrols.docs.preview.compileDocs', 'Compile Docs')}\n </button>\n <button\n type=\"button\"\n onClick={onSettings}\n className=\"p-1.5 text-text-secondary hover:text-text-primary transition-colors\"\n title=\"Site settings\"\n >\n <Settings className=\"size-4\" />\n </button>\n </div>\n </div>\n\n {/* Page navigation tabs — shown when there are multiple compiled pages */}\n {pages.length > 1 && currentRev && (\n <div className=\"flex items-center gap-1 overflow-x-auto pb-1 border-b border-border-default\">\n {pages.map((page, i) => {\n const isSelected = i === 0 ? selectedPageSlug === '' : selectedPageSlug === page.slug;\n return (\n <button\n type=\"button\"\n key={page.id}\n onClick={() => onSelectPage?.(i === 0 ? '' : page.slug)}\n className={`shrink-0 px-3 py-1.5 text-sm rounded-t-md whitespace-nowrap transition-colors ${\n isSelected\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'text-text-secondary hover:text-text-primary hover:bg-bg-sunken'\n }`}\n >\n {page.title}\n </button>\n );\n })}\n </div>\n )}\n\n {renderPreviewArea()}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;AAsCA,SAAgB,EAAiB,EAC/B,SACA,WAAQ,EAAE,EACV,sBAAmB,IACnB,iBACA,gBACA,aACA,uBACA,WACA,cACA,eACA,kBACA,qBACA,oBACA,mBACA,cACA,gBACwB;CACxB,IAAM,IAAK,GAAO,EACZ,IAAa,EAAK,iBAClB,CAAC,GAAS,KAAc,EAAS,GAAM,EACvC,CAAC,GAAQ,KAAa,EAAS,GAAM,EAErC,IAAiB,YAAY;AAC5B,SACL;KAAW,GAAK;AAChB,OAAI;IACF,IAAM,IAAM,MAAM,GAAe;AACjC,IAAI,MACF,MAAM,UAAU,UAAU,UAAU,EAAI,EACxC,EAAU,GAAK,EACf,iBAAiB,EAAU,GAAM,EAAE,IAAK;aAElC;AACR,MAAW,GAAM;;;IAGf,IAAc,IAAW,EAAsB,EAAS,GAAG;AAqHjE,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,MAAD;OAAI,WAAU;iBAAmC,EAAK;OAAU,CAAA;MAC/D,KACC,kBAAC,QAAD;OAAM,WAAU;iBAAhB,CAA2E,SACnE,EAAW,eACZ;;MAET,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD;QAAmB,UAAU;QAAiB,QAAQ;QAAkB,CAAA;OACpE,CAAA;MACF;QACN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACG;MACA,KAAc,KACb,kBAAC,UAAD;OACE,MAAK;OACL,SAAS;OACT,UAAU;OACV,WAAU;OACV,OACE,EAAK,eAAe,WAAW,qBAAqB;iBANxD,CASG,IACC,kBAAC,GAAD,EAAO,WAAU,qCAAsC,CAAA,GACrD,IACF,kBAAC,GAAD,EAAS,WAAU,yBAA0B,CAAA,GAE7C,kBAAC,GAAD,EAAO,WAAU,YAAa,CAAA,EAE/B,IAAS,YAAY,EAAG,sCAAsC,YAAY,CACpE;;MAEV,KACC,kBAAC,UAAD;OACE,MAAK;OACL,SAAS;OACT,WAAU;iBAHZ,CAKE,kBAAC,GAAD,EAAc,WAAU,YAAa,CAAA,EACpC,EAAG,4CAA4C,mBAAmB,CAC5D;;MAEX,kBAAC,UAAD;OACE,MAAK;OACL,SAAS;OACT,WAAU;iBAHZ,CAKE,kBAAC,GAAD,EAAO,WAAU,YAAa,CAAA,EAC7B,EAAG,kCAAkC,OAAO,CACtC;;MACT,kBAAC,UAAD;OACE,MAAK;OACL,SAAS;OACT,UAAU;OACV,WAAU;iBAJZ,CAME,kBAAC,GAAD,EAAW,WAAW,YAAY,IAAY,iBAAiB,MAAQ,CAAA,EACtE,EAAG,yCAAyC,eAAe,CACrD;;MACT,kBAAC,UAAD;OACE,MAAK;OACL,SAAS;OACT,WAAU;OACV,OAAM;iBAEN,kBAAC,GAAD,EAAU,WAAU,UAAW,CAAA;OACxB,CAAA;MACL;OACF;;GAGL,EAAM,SAAS,KAAK,KACnB,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAM,KAAK,GAAM,MAGd,kBAAC,UAAD;KACE,MAAK;KAEL,eAAe,IAAe,MAAM,IAAI,KAAK,EAAK,KAAK;KACvD,WAAW,kFANI,MAAM,IAAI,MAAqB,KAAK,MAAqB,EAAK,QAQvE,kDACA;eAGL,EAAK;KACC,EATF,EAAK,GASH,CAEX;IACE,CAAA;GA7MN,KAAe,IAEf,kBAAC,GAAD;IAAM,WAAU;cACd,kBAAC,UAAD;KAEE,QAAQ;KACR,WAAU;KACV,OAAO;MAAE,QAAQ;MAAuB,WAAW;MAAS;KAC5D,OAAM;KACN,SAAQ;KACR,EANK,GAAG,EAAW,GAAG,GAAG,IAMzB;IACG,CAAA,GAKP,KAAc,IAEd,kBAAC,GAAD;IAAM,WAAU;cACd,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAS,WAAU,uBAAwB,CAAA,EAC3C,kBAAC,KAAD;MAAG,WAAU;gBAAU;MAAkC,CAAA,CACrD;;IACD,CAAA,GAKP,KAAc,CAAC,KAAsB,CAAC,IAEtC,kBAAC,GAAD;IAAM,WAAU;cACd,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD,EAAe,WAAU,mCAAoC,CAAA;MAC7D,kBAAC,KAAD;OAAG,WAAU;iBAAU;OAGnB,CAAA;MACJ,kBAAC,UAAD;OACE,MAAK;OACL,SAAS;OACT,UAAU;OACV,WAAU;iBACX;OAEQ,CAAA;MACL;;IACD,CAAA,GAKP,GAAiB,WAAW,WAE5B,kBAAC,GAAD;IAAM,WAAU;cACd,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD,EAAS,WAAU,iCAAkC,CAAA;MACrD,kBAAC,OAAD,EAAA,UAAA;OACE,kBAAC,KAAD;QAAG,WAAU;kBAAqC;QAAgB,CAAA;OACjE,EAAgB,gBACf,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAgB;QACf,CAAA;OAEN,kBAAC,KAAD;QAAG,WAAU;kBAA0B;QAEnC,CAAA;OACA,EAAA,CAAA;MACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QACE,MAAK;QACL,SAAS;QACT,WAAU;kBACX;QAEQ,CAAA,EACT,kBAAC,UAAD;QACE,MAAK;QACL,SAAS;QACT,UAAU;QACV,WAAU;kBACX;QAEQ,CAAA,CACL;;MACF;;IACD,CAAA,GAMT,kBAAC,GAAD;IAAM,WAAU;cAAhB;KACE,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAG,yCAAyC,iCAAiC;MAC5E,CAAA;KACJ,kBAAC,KAAD;MAAG,WAAU;gBAAb;OAA4C;OACN,kBAAC,UAAD,EAAA,UAAQ,WAAgB,CAAA;;OAE1D;;KACJ,kBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,WAAU;gBAET,EAAG,0CAA0C,gBAAgB;MACvD,CAAA;KACJ;;GAuGH"}
|
|
@@ -5,17 +5,17 @@ import { Fragment as s, jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
|
5
5
|
var u = {
|
|
6
6
|
PUBLISHED: {
|
|
7
7
|
label: "Published",
|
|
8
|
-
className: "bg-status-success-bg text-status-success-text",
|
|
8
|
+
className: "bg-status-success-bg-subtle text-status-success-text",
|
|
9
9
|
icon: /* @__PURE__ */ c(n, { className: "size-3" })
|
|
10
10
|
},
|
|
11
11
|
SUCCEEDED: {
|
|
12
12
|
label: "Built",
|
|
13
|
-
className: "bg-status-info-bg text-status-info-text",
|
|
13
|
+
className: "bg-status-info-bg-subtle text-status-info-text",
|
|
14
14
|
icon: /* @__PURE__ */ c(n, { className: "size-3" })
|
|
15
15
|
},
|
|
16
16
|
FAILED: {
|
|
17
17
|
label: "Failed",
|
|
18
|
-
className: "bg-status-error-bg text-status-error-text",
|
|
18
|
+
className: "bg-status-error-bg-subtle text-status-error-text",
|
|
19
19
|
icon: /* @__PURE__ */ c(o, { className: "size-3" })
|
|
20
20
|
},
|
|
21
21
|
BUILDING: {
|
|
@@ -77,7 +77,7 @@ function m({ revisions: n, currentRevisionId: o, onRollback: u, rollingBack: m }
|
|
|
77
77
|
children: g.map((e) => {
|
|
78
78
|
let n = e.id === o, r = !n && (e.status === "SUCCEEDED" || e.status === "PUBLISHED"), h = m === e.id;
|
|
79
79
|
return /* @__PURE__ */ l("li", {
|
|
80
|
-
className: `flex items-center gap-3 px-3 py-2.5 text-sm ${n ? "bg-
|
|
80
|
+
className: `flex items-center gap-3 px-3 py-2.5 text-sm ${n ? "bg-[var(--color-accent-soft)]" : "hover:bg-bg-sunken"}`,
|
|
81
81
|
children: [/* @__PURE__ */ l("div", {
|
|
82
82
|
className: "flex-1 min-w-0",
|
|
83
83
|
children: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocsRevisionHistory.js","names":[],"sources":["../../../src/components/docs/DocsRevisionHistory.tsx"],"sourcesContent":["import { Check, History, RotateCcw, X, Loader2, AlertCircle } from 'lucide-react';\nimport type { DocsRevision } from './types';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface DocsRevisionHistoryProps {\n revisions: DocsRevision[];\n currentRevisionId: string | null;\n onRollback: (revisionId: string) => Promise<void>;\n rollingBack: string | null;\n}\n\nconst STATUS_CONFIG: Record<string, { label: string; className: string; icon: React.ReactNode }> = {\n PUBLISHED: {\n label: 'Published',\n className: 'bg-status-success-bg text-status-success-text',\n icon: <Check className=\"size-3\" />,\n },\n SUCCEEDED: {\n label: 'Built',\n className: 'bg-status-info-bg text-status-info-text',\n icon: <Check className=\"size-3\" />,\n },\n FAILED: {\n label: 'Failed',\n className: 'bg-status-error-bg text-status-error-text',\n icon: <X className=\"size-3\" />,\n },\n BUILDING: {\n label: 'Building',\n className: 'bg-bg-sunken text-text-secondary',\n icon: <Loader2 className=\"size-3 animate-spin\" />,\n },\n};\n\nfunction StatusBadge({ status }: { status: string }) {\n const config = STATUS_CONFIG[status] ?? {\n label: status,\n className: 'bg-bg-sunken text-text-muted',\n icon: null,\n };\n return (\n <span\n className={`inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] font-medium ${config.className}`}\n >\n {config.icon}\n {config.label}\n </span>\n );\n}\n\nfunction formatDate(iso: string): string {\n return new Date(iso).toLocaleString(undefined, {\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\nfunction formatDuration(ms: number | null): string {\n if (ms == null) return '';\n const s = Math.round(ms / 1000);\n return s < 60 ? `${s}s` : `${Math.floor(s / 60)}m ${s % 60}s`;\n}\n\nexport function DocsRevisionHistory({\n revisions,\n currentRevisionId,\n onRollback,\n rollingBack,\n}: DocsRevisionHistoryProps) {\n const tr = useTr();\n const sorted = [...revisions].sort((a, b) => b.revisionNumber - a.revisionNumber);\n\n if (sorted.length === 0) {\n return (\n <div className=\"p-4 text-center text-sm text-text-muted\">\n {tr('vibecontrols.docs.history.empty', 'No builds yet.')}\n </div>\n );\n }\n\n return (\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2 px-3 py-2 border-b border-border-default\">\n <History className=\"size-4 text-text-secondary\" />\n <span className=\"text-sm font-medium text-text-primary\">\n {tr('vibecontrols.docs.history.title', 'Build history')}\n </span>\n <span className=\"text-xs text-text-muted\">({sorted.length})</span>\n </div>\n <ul className=\"divide-y divide-border-default\">\n {sorted.map((rev) => {\n const isCurrent = rev.id === currentRevisionId;\n const canRollback =\n !isCurrent && (rev.status === 'SUCCEEDED' || rev.status === 'PUBLISHED');\n const isRollingBack = rollingBack === rev.id;\n\n return (\n <li\n key={rev.id}\n className={`flex items-center gap-3 px-3 py-2.5 text-sm ${isCurrent ? 'bg-
|
|
1
|
+
{"version":3,"file":"DocsRevisionHistory.js","names":[],"sources":["../../../src/components/docs/DocsRevisionHistory.tsx"],"sourcesContent":["import { Check, History, RotateCcw, X, Loader2, AlertCircle } from 'lucide-react';\nimport type { DocsRevision } from './types';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface DocsRevisionHistoryProps {\n revisions: DocsRevision[];\n currentRevisionId: string | null;\n onRollback: (revisionId: string) => Promise<void>;\n rollingBack: string | null;\n}\n\nconst STATUS_CONFIG: Record<string, { label: string; className: string; icon: React.ReactNode }> = {\n PUBLISHED: {\n label: 'Published',\n className: 'bg-status-success-bg-subtle text-status-success-text',\n icon: <Check className=\"size-3\" />,\n },\n SUCCEEDED: {\n label: 'Built',\n className: 'bg-status-info-bg-subtle text-status-info-text',\n icon: <Check className=\"size-3\" />,\n },\n FAILED: {\n label: 'Failed',\n className: 'bg-status-error-bg-subtle text-status-error-text',\n icon: <X className=\"size-3\" />,\n },\n BUILDING: {\n label: 'Building',\n className: 'bg-bg-sunken text-text-secondary',\n icon: <Loader2 className=\"size-3 animate-spin\" />,\n },\n};\n\nfunction StatusBadge({ status }: { status: string }) {\n const config = STATUS_CONFIG[status] ?? {\n label: status,\n className: 'bg-bg-sunken text-text-muted',\n icon: null,\n };\n return (\n <span\n className={`inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] font-medium ${config.className}`}\n >\n {config.icon}\n {config.label}\n </span>\n );\n}\n\nfunction formatDate(iso: string): string {\n return new Date(iso).toLocaleString(undefined, {\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\nfunction formatDuration(ms: number | null): string {\n if (ms == null) return '';\n const s = Math.round(ms / 1000);\n return s < 60 ? `${s}s` : `${Math.floor(s / 60)}m ${s % 60}s`;\n}\n\nexport function DocsRevisionHistory({\n revisions,\n currentRevisionId,\n onRollback,\n rollingBack,\n}: DocsRevisionHistoryProps) {\n const tr = useTr();\n const sorted = [...revisions].sort((a, b) => b.revisionNumber - a.revisionNumber);\n\n if (sorted.length === 0) {\n return (\n <div className=\"p-4 text-center text-sm text-text-muted\">\n {tr('vibecontrols.docs.history.empty', 'No builds yet.')}\n </div>\n );\n }\n\n return (\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2 px-3 py-2 border-b border-border-default\">\n <History className=\"size-4 text-text-secondary\" />\n <span className=\"text-sm font-medium text-text-primary\">\n {tr('vibecontrols.docs.history.title', 'Build history')}\n </span>\n <span className=\"text-xs text-text-muted\">({sorted.length})</span>\n </div>\n <ul className=\"divide-y divide-border-default\">\n {sorted.map((rev) => {\n const isCurrent = rev.id === currentRevisionId;\n const canRollback =\n !isCurrent && (rev.status === 'SUCCEEDED' || rev.status === 'PUBLISHED');\n const isRollingBack = rollingBack === rev.id;\n\n return (\n <li\n key={rev.id}\n className={`flex items-center gap-3 px-3 py-2.5 text-sm ${isCurrent ? 'bg-[var(--color-accent-soft)]' : 'hover:bg-bg-sunken'}`}\n >\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <span className=\"font-medium text-text-primary\">#{rev.revisionNumber}</span>\n <StatusBadge status={rev.status} />\n {isCurrent && (\n <span className=\"text-[10px] font-medium px-1.5 py-0.5 rounded bg-action-primary-bg text-action-primary-text\">\n current\n </span>\n )}\n </div>\n <div className=\"flex items-center gap-2 mt-0.5 text-xs text-text-muted\">\n <span>{formatDate(rev.createdAt)}</span>\n {rev.buildDurationMs != null && (\n <>\n <span>·</span>\n <span>{formatDuration(rev.buildDurationMs)}</span>\n </>\n )}\n </div>\n {rev.status === 'FAILED' && rev.errorMessage && (\n <div className=\"mt-1 flex items-start gap-1 text-xs text-status-error-text\">\n <AlertCircle className=\"size-3 mt-0.5 shrink-0\" />\n <span className=\"truncate\">{rev.errorMessage}</span>\n </div>\n )}\n </div>\n {canRollback && (\n <button\n type=\"button\"\n onClick={() => onRollback(rev.id)}\n disabled={!!rollingBack}\n className=\"shrink-0 flex items-center gap-1 px-2 py-1 text-xs text-text-secondary border border-border-default rounded hover:border-action-primary-bg hover:text-action-primary-text-alt transition-colors disabled:opacity-50\"\n title={`Rollback to revision #${rev.revisionNumber}`}\n >\n {isRollingBack ? (\n <Loader2 className=\"size-3 animate-spin\" />\n ) : (\n <RotateCcw className=\"size-3\" />\n )}\n Rollback\n </button>\n )}\n </li>\n );\n })}\n </ul>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -113,13 +113,13 @@ function f({ site: f, open: p, onClose: m, onSave: fe }) {
|
|
|
113
113
|
}, xe = (e) => {
|
|
114
114
|
let t = "inline-flex items-center gap-1 px-2 py-0.5 rounded text-[10px] font-medium";
|
|
115
115
|
return e === "APPROVED" ? /* @__PURE__ */ d("span", {
|
|
116
|
-
className: `${t} bg-status-success-bg text-status-success-text`,
|
|
116
|
+
className: `${t} bg-status-success-bg-subtle text-status-success-text`,
|
|
117
117
|
children: [/* @__PURE__ */ u(o, { className: "size-3" }), "Approved"]
|
|
118
118
|
}) : e === "VALIDATING" ? /* @__PURE__ */ d("span", {
|
|
119
|
-
className: `${t} bg-status-info-bg text-status-info-text`,
|
|
119
|
+
className: `${t} bg-status-info-bg-subtle text-status-info-text`,
|
|
120
120
|
children: [/* @__PURE__ */ u(ae, { className: "size-3 animate-spin" }), "Validating"]
|
|
121
121
|
}) : e === "REJECTED" ? /* @__PURE__ */ d("span", {
|
|
122
|
-
className: `${t} bg-status-error-bg text-status-error-text`,
|
|
122
|
+
className: `${t} bg-status-error-bg-subtle text-status-error-text`,
|
|
123
123
|
children: [/* @__PURE__ */ u(le, { className: "size-3" }), "Rejected"]
|
|
124
124
|
}) : e === "DISABLED" ? /* @__PURE__ */ u("span", {
|
|
125
125
|
className: `${t} bg-bg-sunken text-text-muted`,
|
|
@@ -434,7 +434,7 @@ function f({ site: f, open: p, onClose: m, onSave: fe }) {
|
|
|
434
434
|
}) : /* @__PURE__ */ u("ul", {
|
|
435
435
|
className: "divide-y divide-border-default",
|
|
436
436
|
children: Q.map((e) => /* @__PURE__ */ u("li", {
|
|
437
|
-
className: `p-2 ${e.status === "REJECTED" ? "bg-status-error-bg
|
|
437
|
+
className: `p-2 ${e.status === "REJECTED" ? "bg-status-error-bg-subtle" : ""}`,
|
|
438
438
|
"data-testid": `docs-build-image-${e.id}`,
|
|
439
439
|
children: /* @__PURE__ */ d("div", {
|
|
440
440
|
className: "flex items-start justify-between gap-2",
|
|
@@ -451,7 +451,7 @@ function f({ site: f, open: p, onClose: m, onSave: fe }) {
|
|
|
451
451
|
children: e.imageRef
|
|
452
452
|
}),
|
|
453
453
|
e.lastValidationError && e.status === "REJECTED" && /* @__PURE__ */ d("div", {
|
|
454
|
-
className: "mt-1.5 flex items-start gap-1 p-1.5 rounded bg-status-error-bg text-status-error-text",
|
|
454
|
+
className: "mt-1.5 flex items-start gap-1 p-1.5 rounded bg-status-error-bg-subtle text-status-error-text",
|
|
455
455
|
children: [/* @__PURE__ */ u(re, { className: "size-3 mt-0.5 shrink-0" }), /* @__PURE__ */ u("p", {
|
|
456
456
|
className: "text-[10px] leading-snug",
|
|
457
457
|
children: e.lastValidationError
|
|
@@ -465,7 +465,7 @@ function f({ site: f, open: p, onClose: m, onSave: fe }) {
|
|
|
465
465
|
e.status === "REJECTED" && /* @__PURE__ */ d("button", {
|
|
466
466
|
type: "button",
|
|
467
467
|
onClick: () => $(e.id),
|
|
468
|
-
className: "flex items-center gap-1 px-2 py-0.5 rounded border border-status-error-
|
|
468
|
+
className: "flex items-center gap-1 px-2 py-0.5 rounded border border-status-error-border text-[10px] text-status-error-text hover:bg-status-error-bg-subtle transition-colors",
|
|
469
469
|
"data-testid": `docs-validate-image-${e.id}`,
|
|
470
470
|
children: [/* @__PURE__ */ u(ce, { className: "size-2.5" }), "Re-validate"]
|
|
471
471
|
}),
|
|
@@ -493,7 +493,7 @@ function f({ site: f, open: p, onClose: m, onSave: fe }) {
|
|
|
493
493
|
]
|
|
494
494
|
}),
|
|
495
495
|
J && /* @__PURE__ */ u("div", {
|
|
496
|
-
className: "p-3 bg-status-error-bg text-status-error-text rounded-md text-xs",
|
|
496
|
+
className: "p-3 bg-status-error-bg-subtle text-status-error-text rounded-md text-xs",
|
|
497
497
|
children: J
|
|
498
498
|
}),
|
|
499
499
|
/* @__PURE__ */ u("button", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocsSettingsPanel.js","names":[],"sources":["../../../src/components/docs/DocsSettingsPanel.tsx"],"sourcesContent":["import { useState, useEffect } from 'react';\nimport {\n X,\n Globe,\n Lock,\n Plus,\n Check,\n XCircle,\n Loader2,\n AlertTriangle,\n RefreshCw,\n} from 'lucide-react';\nimport { AnimatePresence, motion } from 'framer-motion';\nimport {\n useListDocsBuildImagesQuery,\n useRegisterDocsBuildImageMutation,\n useValidateDocsBuildImageMutation,\n useDisableDocsBuildImageMutation,\n useUpdateDocsSiteRuntimeMutation,\n} from '@/generated/wspace-operations';\nimport type { DocsProvider, DocsSite } from './types';\nimport { DOCS_PROVIDERS } from './types';\nimport { useTr } from '../../shared/hooks/useTr';\n\n/**\n * Build a provider-specific config payload from the shared form fields.\n * Each provider has its own JSON shape — we keep the persisted shape narrow so\n * the backend assembler can rely on it without parsing the union.\n */\nfunction buildProviderConfig(\n provider: DocsProvider,\n prev: Record<string, unknown>,\n themeConfig: Record<string, unknown>,\n fields: { name: string; tagline: string; theme: string }\n): Record<string, unknown> {\n switch (provider) {\n case 'MKDOCS':\n return {\n ...prev,\n title: fields.name,\n tagline: fields.tagline,\n themePalette: (prev.themePalette as Record<string, unknown>) || {\n primary: 'indigo',\n accent: 'indigo',\n },\n };\n case 'VITEPRESS':\n return {\n ...prev,\n title: fields.name,\n tagline: fields.tagline,\n themeConfig: {\n ...((prev.themeConfig as Record<string, unknown>) || {}),\n nav: (themeConfig.nav as unknown[]) || [],\n },\n };\n case 'DOCUSAURUS':\n default:\n return {\n ...prev,\n title: fields.name,\n tagline: fields.tagline,\n themeConfig: {\n ...themeConfig,\n colorMode: {\n defaultMode: fields.theme,\n disableSwitch: false,\n respectPrefersColorScheme: true,\n },\n },\n };\n }\n}\n\ninterface DocsBuildImage {\n id: string;\n name: string;\n imageRef: string;\n imageDigest?: string | null;\n status: 'PENDING' | 'VALIDATING' | 'APPROVED' | 'REJECTED' | 'DISABLED';\n buildCommand?: string | null;\n outputDir?: string | null;\n description?: string | null;\n lastValidationError?: string | null;\n}\n\ninterface DocsSettingsPanelProps {\n site: DocsSite;\n open: boolean;\n onClose: () => void;\n onSave: (data: {\n name?: string;\n visibility?: 'PRIVATE' | 'PUBLIC';\n provider?: DocsProvider;\n providerConfig?: Record<string, unknown>;\n }) => Promise<void>;\n}\n\nexport function DocsSettingsPanel({ site, open, onClose, onSave }: DocsSettingsPanelProps) {\n const tr = useTr();\n const [name, setName] = useState(site.name);\n const [visibility, setVisibility] = useState<'PRIVATE' | 'PUBLIC'>(site.visibility);\n const [provider, setProvider] = useState<DocsProvider>(site.provider || 'DOCUSAURUS');\n const config = (site.providerConfig || site.docusaurusConfig || {}) as Record<string, unknown>;\n const [tagline, setTagline] = useState((config.tagline as string) || '');\n const themeConfig = (config.themeConfig || {}) as Record<string, unknown>;\n const colorMode = (themeConfig.colorMode || {}) as Record<string, unknown>;\n const [theme, setTheme] = useState<string>((colorMode.defaultMode as string) || 'light');\n const [saving, setSaving] = useState(false);\n\n const [runtime, setRuntime] = useState<'MANAGED' | 'CUSTOMER_IMAGE'>(\n (site as any).runtime || 'MANAGED'\n );\n const [selectedImageId, setSelectedImageId] = useState<string | null>(\n (site as any).buildImageId || null\n );\n const [showRegister, setShowRegister] = useState(false);\n const [regName, setRegName] = useState('');\n const [regImageRef, setRegImageRef] = useState('');\n const [regBuildCommand, setRegBuildCommand] = useState('');\n const [regOutputDir, setRegOutputDir] = useState('build');\n const [regDescription, setRegDescription] = useState('');\n const [registering, setRegistering] = useState(false);\n const [actionError, setActionError] = useState<string | null>(null);\n\n const { data: imagesData, refetch: refetchImages } = useListDocsBuildImagesQuery({\n fetchPolicy: 'cache-and-network',\n });\n const [registerImage] = useRegisterDocsBuildImageMutation();\n const [validateImage] = useValidateDocsBuildImageMutation();\n const [disableImage] = useDisableDocsBuildImageMutation();\n const [updateRuntime] = useUpdateDocsSiteRuntimeMutation();\n\n const images = (imagesData?.docsBuildImages?.items || []) as DocsBuildImage[];\n const approvedImages = images.filter((i) => i.status === 'APPROVED');\n\n useEffect(() => {\n setName(site.name);\n setVisibility(site.visibility);\n setProvider(site.provider || 'DOCUSAURUS');\n const cfg = (site.providerConfig || site.docusaurusConfig || {}) as Record<string, unknown>;\n setTagline((cfg.tagline as string) || '');\n const tc = (cfg.themeConfig || {}) as Record<string, unknown>;\n const cm = (tc.colorMode || {}) as Record<string, unknown>;\n setTheme((cm.defaultMode as string) || 'light');\n setRuntime(((site as any).runtime as 'MANAGED' | 'CUSTOMER_IMAGE') || 'MANAGED');\n setSelectedImageId((site as any).buildImageId || null);\n }, [site]);\n\n // Poll during VALIDATING state so the UI reflects status transitions live.\n useEffect(() => {\n if (!open) return;\n const hasValidating = images.some((i) => i.status === 'VALIDATING');\n if (!hasValidating) return;\n const interval = setInterval(() => {\n void refetchImages();\n }, 3000);\n return () => clearInterval(interval);\n }, [open, images, refetchImages]);\n\n const handleSave = async () => {\n setSaving(true);\n setActionError(null);\n try {\n await onSave({\n name,\n visibility,\n provider,\n providerConfig: buildProviderConfig(provider, config, themeConfig, {\n name,\n tagline,\n theme,\n }),\n });\n await updateRuntime({\n variables: {\n id: site.id,\n runtime: runtime as any,\n buildImageId: runtime === 'CUSTOMER_IMAGE' ? selectedImageId : null,\n },\n });\n onClose();\n } catch (err) {\n setActionError((err as Error).message);\n } finally {\n setSaving(false);\n }\n };\n\n const handleRegister = async () => {\n setRegistering(true);\n setActionError(null);\n try {\n const { data } = await registerImage({\n variables: {\n input: {\n name: regName,\n imageRef: regImageRef,\n description: regDescription || null,\n buildCommand: regBuildCommand || null,\n outputDir: regOutputDir || 'build',\n },\n },\n });\n const newId = (data as any)?.registerDocsBuildImage?.id;\n if (newId) {\n await validateImage({ variables: { id: newId } });\n }\n setRegName('');\n setRegImageRef('');\n setRegBuildCommand('');\n setRegOutputDir('build');\n setRegDescription('');\n setShowRegister(false);\n await refetchImages();\n } catch (err) {\n setActionError((err as Error).message);\n } finally {\n setRegistering(false);\n }\n };\n\n const handleValidate = async (id: string) => {\n setActionError(null);\n try {\n await validateImage({ variables: { id } });\n await refetchImages();\n } catch (err) {\n setActionError((err as Error).message);\n }\n };\n\n const handleDisable = async (id: string) => {\n setActionError(null);\n try {\n await disableImage({ variables: { id } });\n await refetchImages();\n } catch (err) {\n setActionError((err as Error).message);\n }\n };\n\n const statusBadge = (status: DocsBuildImage['status']) => {\n const base = 'inline-flex items-center gap-1 px-2 py-0.5 rounded text-[10px] font-medium';\n if (status === 'APPROVED')\n return (\n <span className={`${base} bg-status-success-bg text-status-success-text`}>\n <Check className=\"size-3\" />\n Approved\n </span>\n );\n if (status === 'VALIDATING')\n return (\n <span className={`${base} bg-status-info-bg text-status-info-text`}>\n <Loader2 className=\"size-3 animate-spin\" />\n Validating\n </span>\n );\n if (status === 'REJECTED')\n return (\n <span className={`${base} bg-status-error-bg text-status-error-text`}>\n <XCircle className=\"size-3\" />\n Rejected\n </span>\n );\n if (status === 'DISABLED')\n return <span className={`${base} bg-bg-sunken text-text-muted`}>Disabled</span>;\n return <span className={`${base} bg-bg-sunken text-text-secondary`}>Pending</span>;\n };\n\n return (\n <AnimatePresence>\n {open && (\n <>\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n className=\"fixed inset-0 bg-bg-overlay z-40\"\n onClick={onClose}\n />\n <motion.div\n initial={{ x: '100%' }}\n animate={{ x: 0 }}\n exit={{ x: '100%' }}\n transition={{ type: 'spring', damping: 30, stiffness: 300 }}\n className=\"fixed right-0 top-0 bottom-0 w-[28rem] bg-bg-surface border-l border-border-default z-50 overflow-y-auto\"\n >\n <div className=\"p-4 border-b border-border-default flex items-center justify-between\">\n <h3 className=\"font-semibold text-text-primary\">\n {tr('vibecontrols.docs.settings.title', 'Site Settings')}\n </h3>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1 text-text-secondary hover:text-text-primary\"\n >\n <X className=\"size-5\" />\n </button>\n </div>\n\n <div className=\"p-4 space-y-6\">\n <div>\n <label\n htmlFor=\"docssettingspanel-17\"\n className=\"block text-sm font-medium text-text-secondary mb-1\"\n >\n Site Title\n </label>\n <input\n id=\"docssettingspanel-17\"\n aria-label=\"control\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm focus:outline-none focus:ring-1 focus:ring-action-primary-bg\"\n />\n </div>\n\n <div>\n <label\n htmlFor=\"docssettingspanel-16\"\n className=\"block text-sm font-medium text-text-secondary mb-1\"\n >\n Tagline\n </label>\n <input\n id=\"docssettingspanel-16\"\n aria-label=\"A brief tagline for your docs\"\n type=\"text\"\n value={tagline}\n onChange={(e) => setTagline(e.target.value)}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm focus:outline-none focus:ring-1 focus:ring-action-primary-bg\"\n placeholder=\"A brief tagline for your docs\"\n />\n </div>\n\n <div>\n <label\n htmlFor=\"docssettingspanel-15\"\n className=\"block text-sm font-medium text-text-secondary mb-2\"\n >\n Visibility\n </label>\n <div className=\"space-y-2\">\n <label className=\"flex items-center gap-3 p-3 bg-bg-sunken rounded-md cursor-pointer border border-border-default hover:border-action-primary-bg transition-colors\">\n <input\n id=\"docssettingspanel-15\"\n aria-label=\"visibility\"\n type=\"radio\"\n name=\"visibility\"\n value=\"PRIVATE\"\n checked={visibility === 'PRIVATE'}\n onChange={() => setVisibility('PRIVATE')}\n className=\"accent-action-primary-bg\"\n />\n <Lock className=\"size-4 text-text-secondary\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">Private</p>\n <p className=\"text-xs text-text-muted\">\n Signed URLs that expire periodically\n </p>\n </div>\n </label>\n <label className=\"flex items-center gap-3 p-3 bg-bg-sunken rounded-md cursor-pointer border border-border-default hover:border-action-primary-bg transition-colors\">\n <input\n aria-label=\"visibility\"\n type=\"radio\"\n name=\"visibility\"\n value=\"PUBLIC\"\n checked={visibility === 'PUBLIC'}\n onChange={() => setVisibility('PUBLIC')}\n className=\"accent-action-primary-bg\"\n />\n <Globe className=\"size-4 text-text-secondary\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">Public</p>\n <p className=\"text-xs text-text-muted\">Static URL accessible to anyone</p>\n </div>\n </label>\n </div>\n </div>\n\n <div>\n <label\n htmlFor=\"docssettingspanel-14\"\n className=\"block text-sm font-medium text-text-secondary mb-2\"\n >\n Static-site generator\n </label>\n <div className=\"space-y-2\">\n {DOCS_PROVIDERS.map((p) => (\n <label\n key={p.value}\n data-testid={`docs-provider-${p.value.toLowerCase()}`}\n className=\"flex items-start gap-3 p-3 bg-bg-sunken rounded-md cursor-pointer border border-border-default hover:border-action-primary-bg transition-colors\"\n >\n <input\n id=\"docssettingspanel-14\"\n aria-label=\"provider\"\n type=\"radio\"\n name=\"provider\"\n value={p.value}\n checked={provider === p.value}\n onChange={() => setProvider(p.value)}\n className=\"mt-1 accent-action-primary-bg\"\n />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">{p.label}</p>\n <p className=\"text-xs text-text-muted\">{p.description}</p>\n </div>\n </label>\n ))}\n </div>\n {provider !== (site.provider || 'DOCUSAURUS') && (\n <p className=\"mt-2 text-xs text-text-warning\">\n Switching providers rebuilds the site from scratch. Existing artifacts stay\n until the next compile.\n </p>\n )}\n </div>\n\n {provider === 'DOCUSAURUS' && (\n <div>\n <label\n htmlFor=\"docssettingspanel-13\"\n className=\"block text-sm font-medium text-text-secondary mb-2\"\n >\n Theme\n </label>\n <div className=\"flex gap-2\">\n {(['light', 'dark'] as const).map((t) => (\n <button\n type=\"button\"\n key={t}\n onClick={() => setTheme(t)}\n className={`flex-1 px-3 py-2 text-sm rounded-md border transition-colors ${\n theme === t\n ? 'bg-action-primary-bg text-action-primary-text border-action-primary-bg'\n : 'bg-bg-sunken text-text-secondary border-border-default hover:border-action-primary-bg'\n }`}\n >\n {t.charAt(0).toUpperCase() + t.slice(1)}\n </button>\n ))}\n </div>\n </div>\n )}\n\n <div className=\"pt-4 border-t border-border-default\">\n <label\n htmlFor=\"docssettingspanel-13\"\n className=\"block text-sm font-medium text-text-secondary mb-2\"\n >\n Build runtime\n </label>\n <div className=\"space-y-2\">\n <label className=\"flex items-start gap-3 p-3 bg-bg-sunken rounded-md cursor-pointer border border-border-default hover:border-action-primary-bg transition-colors\">\n <input\n id=\"docssettingspanel-13\"\n aria-label=\"runtime\"\n type=\"radio\"\n name=\"runtime\"\n value=\"MANAGED\"\n checked={runtime === 'MANAGED'}\n onChange={() => setRuntime('MANAGED')}\n className=\"accent-action-primary-bg mt-1\"\n />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">Managed (default)</p>\n <p className=\"text-xs text-text-muted\">\n Uses the platform's built-in Docusaurus image. Zero setup — just compile and\n go.\n </p>\n </div>\n </label>\n <label className=\"flex items-start gap-3 p-3 bg-bg-sunken rounded-md cursor-pointer border border-border-default hover:border-action-primary-bg transition-colors\">\n <input\n aria-label=\"runtime\"\n type=\"radio\"\n name=\"runtime\"\n value=\"CUSTOMER_IMAGE\"\n checked={runtime === 'CUSTOMER_IMAGE'}\n onChange={() => setRuntime('CUSTOMER_IMAGE')}\n className=\"accent-action-primary-bg mt-1\"\n />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">Custom image</p>\n <p className=\"text-xs text-text-muted\">\n Bring your own Docker build image — useful for custom Docusaurus plugins,\n themes, or alternative generators (MkDocs, VitePress). The image must be\n registered and approved below before it can be used.\n </p>\n </div>\n </label>\n </div>\n\n {runtime === 'CUSTOMER_IMAGE' && (\n <div className=\"mt-3 space-y-3\">\n <div>\n <label\n htmlFor=\"docssettingspanel-12\"\n className=\"block text-xs font-medium text-text-secondary mb-1\"\n >\n Select approved image\n </label>\n <select\n id=\"docssettingspanel-12\"\n value={selectedImageId ?? ''}\n onChange={(e) => setSelectedImageId(e.target.value || null)}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm focus:outline-none focus:ring-1 focus:ring-action-primary-bg\"\n >\n <option value=\"\"> - Select an image - </option>\n {approvedImages.map((img) => (\n <option key={img.id} value={img.id}>\n {img.name} ({img.imageRef})\n </option>\n ))}\n </select>\n </div>\n\n <div className=\"border border-border-default rounded-md overflow-hidden\">\n <div className=\"p-2 bg-bg-sunken border-b border-border-default flex items-center justify-between\">\n <span className=\"text-xs font-medium text-text-secondary\">\n Build images ({images.length})\n </span>\n <button\n type=\"button\"\n onClick={() => setShowRegister((v) => !v)}\n className=\"text-xs text-action-primary-text-alt hover:underline flex items-center gap-1\"\n data-testid=\"docs-register-image-toggle\"\n >\n <Plus className=\"size-3\" />\n {showRegister ? 'Cancel' : 'Register'}\n </button>\n </div>\n\n {showRegister && (\n <div className=\"p-3 space-y-2 bg-bg-base\">\n <input\n aria-label=\"Name (e.g. custom-theme)\"\n type=\"text\"\n value={regName}\n onChange={(e) => setRegName(e.target.value)}\n placeholder=\"Name (e.g. custom-theme)\"\n className=\"w-full px-2 py-1 bg-bg-sunken border border-border-default rounded text-text-primary text-xs\"\n data-testid=\"docs-register-image-name\"\n />\n <input\n aria-label=\"harbor.tooling.internal.burdenoff.com/...\"\n type=\"text\"\n value={regImageRef}\n onChange={(e) => setRegImageRef(e.target.value)}\n placeholder=\"harbor.tooling.internal.burdenoff.com/...\"\n className=\"w-full px-2 py-1 bg-bg-sunken border border-border-default rounded text-text-primary text-xs\"\n data-testid=\"docs-register-image-ref\"\n />\n <input\n aria-label=\"Build command (default: npm install && npm run build)\"\n type=\"text\"\n value={regBuildCommand}\n onChange={(e) => setRegBuildCommand(e.target.value)}\n placeholder=\"Build command (default: npm install && npm run build)\"\n className=\"w-full px-2 py-1 bg-bg-sunken border border-border-default rounded text-text-primary text-xs\"\n data-testid=\"docs-register-image-command\"\n />\n <input\n aria-label=\"Output dir (default: build)\"\n type=\"text\"\n value={regOutputDir}\n onChange={(e) => setRegOutputDir(e.target.value)}\n placeholder=\"Output dir (default: build)\"\n className=\"w-full px-2 py-1 bg-bg-sunken border border-border-default rounded text-text-primary text-xs\"\n />\n <textarea\n aria-label=\"Description (optional)\"\n value={regDescription}\n onChange={(e) => setRegDescription(e.target.value)}\n placeholder=\"Description (optional)\"\n rows={2}\n className=\"w-full px-2 py-1 bg-bg-sunken border border-border-default rounded text-text-primary text-xs\"\n />\n <button\n type=\"button\"\n onClick={handleRegister}\n disabled={registering || !regName || !regImageRef}\n className=\"w-full px-2 py-1 bg-action-primary-bg text-action-primary-text rounded text-xs hover:bg-action-primary-bg-hover disabled:opacity-50\"\n data-testid=\"docs-register-image-submit\"\n >\n {registering ? 'Registering…' : 'Register & Validate'}\n </button>\n </div>\n )}\n\n {images.length === 0 ? (\n <p className=\"text-xs text-text-muted p-3\">\n No build images registered yet.\n </p>\n ) : (\n <ul className=\"divide-y divide-border-default\">\n {images.map((img) => (\n <li\n key={img.id}\n className={`p-2 ${img.status === 'REJECTED' ? 'bg-status-error-bg/30' : ''}`}\n data-testid={`docs-build-image-${img.id}`}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-xs font-medium text-text-primary truncate\">\n {img.name}\n </p>\n <p\n className=\"text-[10px] text-text-muted truncate\"\n title={img.imageRef}\n >\n {img.imageRef}\n </p>\n {img.lastValidationError && img.status === 'REJECTED' && (\n <div className=\"mt-1.5 flex items-start gap-1 p-1.5 rounded bg-status-error-bg text-status-error-text\">\n <AlertTriangle className=\"size-3 mt-0.5 shrink-0\" />\n <p className=\"text-[10px] leading-snug\">\n {img.lastValidationError}\n </p>\n </div>\n )}\n </div>\n <div className=\"flex flex-col items-end gap-1.5 shrink-0\">\n {statusBadge(img.status)}\n {img.status === 'REJECTED' && (\n <button\n type=\"button\"\n onClick={() => handleValidate(img.id)}\n className=\"flex items-center gap-1 px-2 py-0.5 rounded border border-status-error-text text-[10px] text-status-error-text hover:bg-status-error-bg transition-colors\"\n data-testid={`docs-validate-image-${img.id}`}\n >\n <RefreshCw className=\"size-2.5\" />\n Re-validate\n </button>\n )}\n {(img.status === 'PENDING' || img.status === 'DISABLED') && (\n <button\n type=\"button\"\n onClick={() => handleValidate(img.id)}\n className=\"flex items-center gap-1 px-2 py-0.5 rounded border border-border-default text-[10px] text-text-secondary hover:border-action-primary-bg hover:text-action-primary-text-alt transition-colors\"\n data-testid={`docs-validate-image-${img.id}`}\n >\n Validate\n </button>\n )}\n {img.status === 'APPROVED' && (\n <button\n type=\"button\"\n onClick={() => handleDisable(img.id)}\n className=\"text-[10px] text-text-muted hover:underline\"\n >\n Disable\n </button>\n )}\n </div>\n </div>\n </li>\n ))}\n </ul>\n )}\n </div>\n </div>\n )}\n </div>\n\n {actionError && (\n <div className=\"p-3 bg-status-error-bg text-status-error-text rounded-md text-xs\">\n {actionError}\n </div>\n )}\n\n <button\n type=\"button\"\n onClick={handleSave}\n disabled={saving || (runtime === 'CUSTOMER_IMAGE' && !selectedImageId)}\n className=\"w-full 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 data-testid=\"docs-settings-save\"\n >\n {saving\n ? tr('vibecontrols.docs.settings.saving', 'Saving...')\n : tr('vibecontrols.docs.settings.save', 'Save Settings')}\n </button>\n </div>\n </motion.div>\n </>\n )}\n </AnimatePresence>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DocsSettingsPanel.js","names":[],"sources":["../../../src/components/docs/DocsSettingsPanel.tsx"],"sourcesContent":["import { useState, useEffect } from 'react';\nimport {\n X,\n Globe,\n Lock,\n Plus,\n Check,\n XCircle,\n Loader2,\n AlertTriangle,\n RefreshCw,\n} from 'lucide-react';\nimport { AnimatePresence, motion } from 'framer-motion';\nimport {\n useListDocsBuildImagesQuery,\n useRegisterDocsBuildImageMutation,\n useValidateDocsBuildImageMutation,\n useDisableDocsBuildImageMutation,\n useUpdateDocsSiteRuntimeMutation,\n} from '@/generated/wspace-operations';\nimport type { DocsProvider, DocsSite } from './types';\nimport { DOCS_PROVIDERS } from './types';\nimport { useTr } from '../../shared/hooks/useTr';\n\n/**\n * Build a provider-specific config payload from the shared form fields.\n * Each provider has its own JSON shape — we keep the persisted shape narrow so\n * the backend assembler can rely on it without parsing the union.\n */\nfunction buildProviderConfig(\n provider: DocsProvider,\n prev: Record<string, unknown>,\n themeConfig: Record<string, unknown>,\n fields: { name: string; tagline: string; theme: string }\n): Record<string, unknown> {\n switch (provider) {\n case 'MKDOCS':\n return {\n ...prev,\n title: fields.name,\n tagline: fields.tagline,\n themePalette: (prev.themePalette as Record<string, unknown>) || {\n primary: 'indigo',\n accent: 'indigo',\n },\n };\n case 'VITEPRESS':\n return {\n ...prev,\n title: fields.name,\n tagline: fields.tagline,\n themeConfig: {\n ...((prev.themeConfig as Record<string, unknown>) || {}),\n nav: (themeConfig.nav as unknown[]) || [],\n },\n };\n case 'DOCUSAURUS':\n default:\n return {\n ...prev,\n title: fields.name,\n tagline: fields.tagline,\n themeConfig: {\n ...themeConfig,\n colorMode: {\n defaultMode: fields.theme,\n disableSwitch: false,\n respectPrefersColorScheme: true,\n },\n },\n };\n }\n}\n\ninterface DocsBuildImage {\n id: string;\n name: string;\n imageRef: string;\n imageDigest?: string | null;\n status: 'PENDING' | 'VALIDATING' | 'APPROVED' | 'REJECTED' | 'DISABLED';\n buildCommand?: string | null;\n outputDir?: string | null;\n description?: string | null;\n lastValidationError?: string | null;\n}\n\ninterface DocsSettingsPanelProps {\n site: DocsSite;\n open: boolean;\n onClose: () => void;\n onSave: (data: {\n name?: string;\n visibility?: 'PRIVATE' | 'PUBLIC';\n provider?: DocsProvider;\n providerConfig?: Record<string, unknown>;\n }) => Promise<void>;\n}\n\nexport function DocsSettingsPanel({ site, open, onClose, onSave }: DocsSettingsPanelProps) {\n const tr = useTr();\n const [name, setName] = useState(site.name);\n const [visibility, setVisibility] = useState<'PRIVATE' | 'PUBLIC'>(site.visibility);\n const [provider, setProvider] = useState<DocsProvider>(site.provider || 'DOCUSAURUS');\n const config = (site.providerConfig || site.docusaurusConfig || {}) as Record<string, unknown>;\n const [tagline, setTagline] = useState((config.tagline as string) || '');\n const themeConfig = (config.themeConfig || {}) as Record<string, unknown>;\n const colorMode = (themeConfig.colorMode || {}) as Record<string, unknown>;\n const [theme, setTheme] = useState<string>((colorMode.defaultMode as string) || 'light');\n const [saving, setSaving] = useState(false);\n\n const [runtime, setRuntime] = useState<'MANAGED' | 'CUSTOMER_IMAGE'>(\n (site as any).runtime || 'MANAGED'\n );\n const [selectedImageId, setSelectedImageId] = useState<string | null>(\n (site as any).buildImageId || null\n );\n const [showRegister, setShowRegister] = useState(false);\n const [regName, setRegName] = useState('');\n const [regImageRef, setRegImageRef] = useState('');\n const [regBuildCommand, setRegBuildCommand] = useState('');\n const [regOutputDir, setRegOutputDir] = useState('build');\n const [regDescription, setRegDescription] = useState('');\n const [registering, setRegistering] = useState(false);\n const [actionError, setActionError] = useState<string | null>(null);\n\n const { data: imagesData, refetch: refetchImages } = useListDocsBuildImagesQuery({\n fetchPolicy: 'cache-and-network',\n });\n const [registerImage] = useRegisterDocsBuildImageMutation();\n const [validateImage] = useValidateDocsBuildImageMutation();\n const [disableImage] = useDisableDocsBuildImageMutation();\n const [updateRuntime] = useUpdateDocsSiteRuntimeMutation();\n\n const images = (imagesData?.docsBuildImages?.items || []) as DocsBuildImage[];\n const approvedImages = images.filter((i) => i.status === 'APPROVED');\n\n useEffect(() => {\n setName(site.name);\n setVisibility(site.visibility);\n setProvider(site.provider || 'DOCUSAURUS');\n const cfg = (site.providerConfig || site.docusaurusConfig || {}) as Record<string, unknown>;\n setTagline((cfg.tagline as string) || '');\n const tc = (cfg.themeConfig || {}) as Record<string, unknown>;\n const cm = (tc.colorMode || {}) as Record<string, unknown>;\n setTheme((cm.defaultMode as string) || 'light');\n setRuntime(((site as any).runtime as 'MANAGED' | 'CUSTOMER_IMAGE') || 'MANAGED');\n setSelectedImageId((site as any).buildImageId || null);\n }, [site]);\n\n // Poll during VALIDATING state so the UI reflects status transitions live.\n useEffect(() => {\n if (!open) return;\n const hasValidating = images.some((i) => i.status === 'VALIDATING');\n if (!hasValidating) return;\n const interval = setInterval(() => {\n void refetchImages();\n }, 3000);\n return () => clearInterval(interval);\n }, [open, images, refetchImages]);\n\n const handleSave = async () => {\n setSaving(true);\n setActionError(null);\n try {\n await onSave({\n name,\n visibility,\n provider,\n providerConfig: buildProviderConfig(provider, config, themeConfig, {\n name,\n tagline,\n theme,\n }),\n });\n await updateRuntime({\n variables: {\n id: site.id,\n runtime: runtime as any,\n buildImageId: runtime === 'CUSTOMER_IMAGE' ? selectedImageId : null,\n },\n });\n onClose();\n } catch (err) {\n setActionError((err as Error).message);\n } finally {\n setSaving(false);\n }\n };\n\n const handleRegister = async () => {\n setRegistering(true);\n setActionError(null);\n try {\n const { data } = await registerImage({\n variables: {\n input: {\n name: regName,\n imageRef: regImageRef,\n description: regDescription || null,\n buildCommand: regBuildCommand || null,\n outputDir: regOutputDir || 'build',\n },\n },\n });\n const newId = (data as any)?.registerDocsBuildImage?.id;\n if (newId) {\n await validateImage({ variables: { id: newId } });\n }\n setRegName('');\n setRegImageRef('');\n setRegBuildCommand('');\n setRegOutputDir('build');\n setRegDescription('');\n setShowRegister(false);\n await refetchImages();\n } catch (err) {\n setActionError((err as Error).message);\n } finally {\n setRegistering(false);\n }\n };\n\n const handleValidate = async (id: string) => {\n setActionError(null);\n try {\n await validateImage({ variables: { id } });\n await refetchImages();\n } catch (err) {\n setActionError((err as Error).message);\n }\n };\n\n const handleDisable = async (id: string) => {\n setActionError(null);\n try {\n await disableImage({ variables: { id } });\n await refetchImages();\n } catch (err) {\n setActionError((err as Error).message);\n }\n };\n\n const statusBadge = (status: DocsBuildImage['status']) => {\n const base = 'inline-flex items-center gap-1 px-2 py-0.5 rounded text-[10px] font-medium';\n if (status === 'APPROVED')\n return (\n <span className={`${base} bg-status-success-bg-subtle text-status-success-text`}>\n <Check className=\"size-3\" />\n Approved\n </span>\n );\n if (status === 'VALIDATING')\n return (\n <span className={`${base} bg-status-info-bg-subtle text-status-info-text`}>\n <Loader2 className=\"size-3 animate-spin\" />\n Validating\n </span>\n );\n if (status === 'REJECTED')\n return (\n <span className={`${base} bg-status-error-bg-subtle text-status-error-text`}>\n <XCircle className=\"size-3\" />\n Rejected\n </span>\n );\n if (status === 'DISABLED')\n return <span className={`${base} bg-bg-sunken text-text-muted`}>Disabled</span>;\n return <span className={`${base} bg-bg-sunken text-text-secondary`}>Pending</span>;\n };\n\n return (\n <AnimatePresence>\n {open && (\n <>\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n className=\"fixed inset-0 bg-bg-overlay z-40\"\n onClick={onClose}\n />\n <motion.div\n initial={{ x: '100%' }}\n animate={{ x: 0 }}\n exit={{ x: '100%' }}\n transition={{ type: 'spring', damping: 30, stiffness: 300 }}\n className=\"fixed right-0 top-0 bottom-0 w-[28rem] bg-bg-surface border-l border-border-default z-50 overflow-y-auto\"\n >\n <div className=\"p-4 border-b border-border-default flex items-center justify-between\">\n <h3 className=\"font-semibold text-text-primary\">\n {tr('vibecontrols.docs.settings.title', 'Site Settings')}\n </h3>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1 text-text-secondary hover:text-text-primary\"\n >\n <X className=\"size-5\" />\n </button>\n </div>\n\n <div className=\"p-4 space-y-6\">\n <div>\n <label\n htmlFor=\"docssettingspanel-17\"\n className=\"block text-sm font-medium text-text-secondary mb-1\"\n >\n Site Title\n </label>\n <input\n id=\"docssettingspanel-17\"\n aria-label=\"control\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm focus:outline-none focus:ring-1 focus:ring-action-primary-bg\"\n />\n </div>\n\n <div>\n <label\n htmlFor=\"docssettingspanel-16\"\n className=\"block text-sm font-medium text-text-secondary mb-1\"\n >\n Tagline\n </label>\n <input\n id=\"docssettingspanel-16\"\n aria-label=\"A brief tagline for your docs\"\n type=\"text\"\n value={tagline}\n onChange={(e) => setTagline(e.target.value)}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm focus:outline-none focus:ring-1 focus:ring-action-primary-bg\"\n placeholder=\"A brief tagline for your docs\"\n />\n </div>\n\n <div>\n <label\n htmlFor=\"docssettingspanel-15\"\n className=\"block text-sm font-medium text-text-secondary mb-2\"\n >\n Visibility\n </label>\n <div className=\"space-y-2\">\n <label className=\"flex items-center gap-3 p-3 bg-bg-sunken rounded-md cursor-pointer border border-border-default hover:border-action-primary-bg transition-colors\">\n <input\n id=\"docssettingspanel-15\"\n aria-label=\"visibility\"\n type=\"radio\"\n name=\"visibility\"\n value=\"PRIVATE\"\n checked={visibility === 'PRIVATE'}\n onChange={() => setVisibility('PRIVATE')}\n className=\"accent-action-primary-bg\"\n />\n <Lock className=\"size-4 text-text-secondary\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">Private</p>\n <p className=\"text-xs text-text-muted\">\n Signed URLs that expire periodically\n </p>\n </div>\n </label>\n <label className=\"flex items-center gap-3 p-3 bg-bg-sunken rounded-md cursor-pointer border border-border-default hover:border-action-primary-bg transition-colors\">\n <input\n aria-label=\"visibility\"\n type=\"radio\"\n name=\"visibility\"\n value=\"PUBLIC\"\n checked={visibility === 'PUBLIC'}\n onChange={() => setVisibility('PUBLIC')}\n className=\"accent-action-primary-bg\"\n />\n <Globe className=\"size-4 text-text-secondary\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">Public</p>\n <p className=\"text-xs text-text-muted\">Static URL accessible to anyone</p>\n </div>\n </label>\n </div>\n </div>\n\n <div>\n <label\n htmlFor=\"docssettingspanel-14\"\n className=\"block text-sm font-medium text-text-secondary mb-2\"\n >\n Static-site generator\n </label>\n <div className=\"space-y-2\">\n {DOCS_PROVIDERS.map((p) => (\n <label\n key={p.value}\n data-testid={`docs-provider-${p.value.toLowerCase()}`}\n className=\"flex items-start gap-3 p-3 bg-bg-sunken rounded-md cursor-pointer border border-border-default hover:border-action-primary-bg transition-colors\"\n >\n <input\n id=\"docssettingspanel-14\"\n aria-label=\"provider\"\n type=\"radio\"\n name=\"provider\"\n value={p.value}\n checked={provider === p.value}\n onChange={() => setProvider(p.value)}\n className=\"mt-1 accent-action-primary-bg\"\n />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">{p.label}</p>\n <p className=\"text-xs text-text-muted\">{p.description}</p>\n </div>\n </label>\n ))}\n </div>\n {provider !== (site.provider || 'DOCUSAURUS') && (\n <p className=\"mt-2 text-xs text-text-warning\">\n Switching providers rebuilds the site from scratch. Existing artifacts stay\n until the next compile.\n </p>\n )}\n </div>\n\n {provider === 'DOCUSAURUS' && (\n <div>\n <label\n htmlFor=\"docssettingspanel-13\"\n className=\"block text-sm font-medium text-text-secondary mb-2\"\n >\n Theme\n </label>\n <div className=\"flex gap-2\">\n {(['light', 'dark'] as const).map((t) => (\n <button\n type=\"button\"\n key={t}\n onClick={() => setTheme(t)}\n className={`flex-1 px-3 py-2 text-sm rounded-md border transition-colors ${\n theme === t\n ? 'bg-action-primary-bg text-action-primary-text border-action-primary-bg'\n : 'bg-bg-sunken text-text-secondary border-border-default hover:border-action-primary-bg'\n }`}\n >\n {t.charAt(0).toUpperCase() + t.slice(1)}\n </button>\n ))}\n </div>\n </div>\n )}\n\n <div className=\"pt-4 border-t border-border-default\">\n <label\n htmlFor=\"docssettingspanel-13\"\n className=\"block text-sm font-medium text-text-secondary mb-2\"\n >\n Build runtime\n </label>\n <div className=\"space-y-2\">\n <label className=\"flex items-start gap-3 p-3 bg-bg-sunken rounded-md cursor-pointer border border-border-default hover:border-action-primary-bg transition-colors\">\n <input\n id=\"docssettingspanel-13\"\n aria-label=\"runtime\"\n type=\"radio\"\n name=\"runtime\"\n value=\"MANAGED\"\n checked={runtime === 'MANAGED'}\n onChange={() => setRuntime('MANAGED')}\n className=\"accent-action-primary-bg mt-1\"\n />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">Managed (default)</p>\n <p className=\"text-xs text-text-muted\">\n Uses the platform's built-in Docusaurus image. Zero setup — just compile and\n go.\n </p>\n </div>\n </label>\n <label className=\"flex items-start gap-3 p-3 bg-bg-sunken rounded-md cursor-pointer border border-border-default hover:border-action-primary-bg transition-colors\">\n <input\n aria-label=\"runtime\"\n type=\"radio\"\n name=\"runtime\"\n value=\"CUSTOMER_IMAGE\"\n checked={runtime === 'CUSTOMER_IMAGE'}\n onChange={() => setRuntime('CUSTOMER_IMAGE')}\n className=\"accent-action-primary-bg mt-1\"\n />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">Custom image</p>\n <p className=\"text-xs text-text-muted\">\n Bring your own Docker build image — useful for custom Docusaurus plugins,\n themes, or alternative generators (MkDocs, VitePress). The image must be\n registered and approved below before it can be used.\n </p>\n </div>\n </label>\n </div>\n\n {runtime === 'CUSTOMER_IMAGE' && (\n <div className=\"mt-3 space-y-3\">\n <div>\n <label\n htmlFor=\"docssettingspanel-12\"\n className=\"block text-xs font-medium text-text-secondary mb-1\"\n >\n Select approved image\n </label>\n <select\n id=\"docssettingspanel-12\"\n value={selectedImageId ?? ''}\n onChange={(e) => setSelectedImageId(e.target.value || null)}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm focus:outline-none focus:ring-1 focus:ring-action-primary-bg\"\n >\n <option value=\"\"> - Select an image - </option>\n {approvedImages.map((img) => (\n <option key={img.id} value={img.id}>\n {img.name} ({img.imageRef})\n </option>\n ))}\n </select>\n </div>\n\n <div className=\"border border-border-default rounded-md overflow-hidden\">\n <div className=\"p-2 bg-bg-sunken border-b border-border-default flex items-center justify-between\">\n <span className=\"text-xs font-medium text-text-secondary\">\n Build images ({images.length})\n </span>\n <button\n type=\"button\"\n onClick={() => setShowRegister((v) => !v)}\n className=\"text-xs text-action-primary-text-alt hover:underline flex items-center gap-1\"\n data-testid=\"docs-register-image-toggle\"\n >\n <Plus className=\"size-3\" />\n {showRegister ? 'Cancel' : 'Register'}\n </button>\n </div>\n\n {showRegister && (\n <div className=\"p-3 space-y-2 bg-bg-base\">\n <input\n aria-label=\"Name (e.g. custom-theme)\"\n type=\"text\"\n value={regName}\n onChange={(e) => setRegName(e.target.value)}\n placeholder=\"Name (e.g. custom-theme)\"\n className=\"w-full px-2 py-1 bg-bg-sunken border border-border-default rounded text-text-primary text-xs\"\n data-testid=\"docs-register-image-name\"\n />\n <input\n aria-label=\"harbor.tooling.internal.burdenoff.com/...\"\n type=\"text\"\n value={regImageRef}\n onChange={(e) => setRegImageRef(e.target.value)}\n placeholder=\"harbor.tooling.internal.burdenoff.com/...\"\n className=\"w-full px-2 py-1 bg-bg-sunken border border-border-default rounded text-text-primary text-xs\"\n data-testid=\"docs-register-image-ref\"\n />\n <input\n aria-label=\"Build command (default: npm install && npm run build)\"\n type=\"text\"\n value={regBuildCommand}\n onChange={(e) => setRegBuildCommand(e.target.value)}\n placeholder=\"Build command (default: npm install && npm run build)\"\n className=\"w-full px-2 py-1 bg-bg-sunken border border-border-default rounded text-text-primary text-xs\"\n data-testid=\"docs-register-image-command\"\n />\n <input\n aria-label=\"Output dir (default: build)\"\n type=\"text\"\n value={regOutputDir}\n onChange={(e) => setRegOutputDir(e.target.value)}\n placeholder=\"Output dir (default: build)\"\n className=\"w-full px-2 py-1 bg-bg-sunken border border-border-default rounded text-text-primary text-xs\"\n />\n <textarea\n aria-label=\"Description (optional)\"\n value={regDescription}\n onChange={(e) => setRegDescription(e.target.value)}\n placeholder=\"Description (optional)\"\n rows={2}\n className=\"w-full px-2 py-1 bg-bg-sunken border border-border-default rounded text-text-primary text-xs\"\n />\n <button\n type=\"button\"\n onClick={handleRegister}\n disabled={registering || !regName || !regImageRef}\n className=\"w-full px-2 py-1 bg-action-primary-bg text-action-primary-text rounded text-xs hover:bg-action-primary-bg-hover disabled:opacity-50\"\n data-testid=\"docs-register-image-submit\"\n >\n {registering ? 'Registering…' : 'Register & Validate'}\n </button>\n </div>\n )}\n\n {images.length === 0 ? (\n <p className=\"text-xs text-text-muted p-3\">\n No build images registered yet.\n </p>\n ) : (\n <ul className=\"divide-y divide-border-default\">\n {images.map((img) => (\n <li\n key={img.id}\n className={`p-2 ${img.status === 'REJECTED' ? 'bg-status-error-bg-subtle' : ''}`}\n data-testid={`docs-build-image-${img.id}`}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-xs font-medium text-text-primary truncate\">\n {img.name}\n </p>\n <p\n className=\"text-[10px] text-text-muted truncate\"\n title={img.imageRef}\n >\n {img.imageRef}\n </p>\n {img.lastValidationError && img.status === 'REJECTED' && (\n <div className=\"mt-1.5 flex items-start gap-1 p-1.5 rounded bg-status-error-bg-subtle text-status-error-text\">\n <AlertTriangle className=\"size-3 mt-0.5 shrink-0\" />\n <p className=\"text-[10px] leading-snug\">\n {img.lastValidationError}\n </p>\n </div>\n )}\n </div>\n <div className=\"flex flex-col items-end gap-1.5 shrink-0\">\n {statusBadge(img.status)}\n {img.status === 'REJECTED' && (\n <button\n type=\"button\"\n onClick={() => handleValidate(img.id)}\n className=\"flex items-center gap-1 px-2 py-0.5 rounded border border-status-error-border text-[10px] text-status-error-text hover:bg-status-error-bg-subtle transition-colors\"\n data-testid={`docs-validate-image-${img.id}`}\n >\n <RefreshCw className=\"size-2.5\" />\n Re-validate\n </button>\n )}\n {(img.status === 'PENDING' || img.status === 'DISABLED') && (\n <button\n type=\"button\"\n onClick={() => handleValidate(img.id)}\n className=\"flex items-center gap-1 px-2 py-0.5 rounded border border-border-default text-[10px] text-text-secondary hover:border-action-primary-bg hover:text-action-primary-text-alt transition-colors\"\n data-testid={`docs-validate-image-${img.id}`}\n >\n Validate\n </button>\n )}\n {img.status === 'APPROVED' && (\n <button\n type=\"button\"\n onClick={() => handleDisable(img.id)}\n className=\"text-[10px] text-text-muted hover:underline\"\n >\n Disable\n </button>\n )}\n </div>\n </div>\n </li>\n ))}\n </ul>\n )}\n </div>\n </div>\n )}\n </div>\n\n {actionError && (\n <div className=\"p-3 bg-status-error-bg-subtle text-status-error-text rounded-md text-xs\">\n {actionError}\n </div>\n )}\n\n <button\n type=\"button\"\n onClick={handleSave}\n disabled={saving || (runtime === 'CUSTOMER_IMAGE' && !selectedImageId)}\n className=\"w-full 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 data-testid=\"docs-settings-save\"\n >\n {saving\n ? tr('vibecontrols.docs.settings.saving', 'Saving...')\n : tr('vibecontrols.docs.settings.save', 'Save Settings')}\n </button>\n </div>\n </motion.div>\n </>\n )}\n </AnimatePresence>\n );\n}\n"],"mappings":"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"}
|
|
@@ -167,7 +167,7 @@ function w({ vibeId: w, ownerType: ue, ownerId: de }) {
|
|
|
167
167
|
message: "You have reached the documentation site limit for your current plan. Upgrade to create more docs sites."
|
|
168
168
|
})] });
|
|
169
169
|
let Z = N ? /* @__PURE__ */ C("div", {
|
|
170
|
-
className: "flex items-center gap-2 px-4 py-2 mb-2 bg-status-error-bg text-status-error-text text-sm rounded-md",
|
|
170
|
+
className: "flex items-center gap-2 px-4 py-2 mb-2 bg-status-error-bg-subtle text-status-error-text text-sm rounded-md",
|
|
171
171
|
children: [
|
|
172
172
|
/* @__PURE__ */ S(b, { className: "size-4 shrink-0" }),
|
|
173
173
|
/* @__PURE__ */ S("span", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocsTabPanel.js","names":[],"sources":["../../../src/components/docs/DocsTabPanel.tsx"],"sourcesContent":["import { useState, useEffect, useRef } from 'react';\nimport { AlertCircle, X } from 'lucide-react';\nimport { LoadingState, ErrorState, QuotaExhaustedDialog } from '@/components/shared';\nimport { EntityShareButton } from '@/components/sharing';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { resolveGatewayUrls } from '@burdenoff/fe-libs/shared/config';\nimport { DocsEmptyState } from './DocsEmptyState';\nimport { DocsPreviewPanel } from './DocsPreviewPanel';\nimport { DocsEditPanel } from './DocsEditPanel';\nimport type { DocsSite, DocsRevision } from './types';\nimport {\n isQuotaExhaustedError,\n markQuotaHandledLocally,\n getQuotaDialogContent,\n} from '@/utils/quotaUtils';\nimport { useTr } from '../../shared/hooks/useTr';\nimport {\n useGetDocsSiteQuery,\n useCreateDocsSiteMutation,\n useUpdateDocsSiteMutation,\n useUpdateDocsSiteVisibilityMutation,\n useCreateDocsPageMutation,\n useUpdateDocsPageMutation,\n useDeleteDocsPageMutation,\n useCompileDocsSiteMutation,\n useDocsContentQuery,\n useGetDocsRevisionLazyQuery,\n useGenerateDocsSignedUrlMutation,\n useRollbackDocsRevisionMutation,\n useDeleteDocsSiteMutation,\n} from '@/generated/wspace-operations';\n\ninterface DocsTabPanelProps {\n vibeId?: string;\n ownerType?: string;\n ownerId?: string;\n}\n\nexport function DocsTabPanel({ vibeId, ownerType, ownerId }: DocsTabPanelProps) {\n const tr = useTr();\n const effectiveOwnerType = ownerType || 'VIBE';\n const effectiveOwnerId = ownerId || vibeId || '';\n const [mode, setMode] = useState<'preview' | 'edit'>('preview');\n const [compileRevision, setCompileRevision] = useState<DocsRevision | null>(null);\n const [compiling, setCompiling] = useState(false);\n const [compileError, setCompileError] = useState<string | null>(null);\n const [selectedPageSlug, setSelectedPageSlug] = useState('');\n const [rollingBack, setRollingBack] = useState<string | null>(null);\n const navigate = useVibeNavigate();\n const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);\n const [showCompileQuotaExhausted, setShowCompileQuotaExhausted] = useState(false);\n\n // Queries\n const { data, loading, error, refetch } = useGetDocsSiteQuery({\n variables: { ownerType: effectiveOwnerType as any, ownerId: effectiveOwnerId },\n skip: !effectiveOwnerId,\n fetchPolicy: 'cache-and-network',\n });\n\n const site = (data?.vibecontrolsDocsSiteByOwner ?? null) as DocsSite | null;\n const pages = site?.pages || [];\n\n const {\n data: docsContentData,\n loading: docsContentLoading,\n refetch: refetchDocsContent,\n } = useDocsContentQuery({\n variables: { siteId: site?.id || '', path: selectedPageSlug },\n skip: !site?.id || !site.currentRevisionId,\n fetchPolicy: 'network-only',\n });\n const docsHtml = docsContentData?.docsContent?.html ?? null;\n\n // Re-fetch docs content whenever currentRevisionId changes (compile finished or first publish).\n // This runs post-render so skip:false is guaranteed — imperative calls during async handlers\n // are no-ops when skip is still true in the closure.\n const prevRevisionIdRef = useRef<string | null | undefined>(undefined);\n useEffect(() => {\n const currentId = site?.currentRevisionId ?? null;\n if (prevRevisionIdRef.current === undefined) {\n prevRevisionIdRef.current = currentId;\n return;\n }\n if (currentId && currentId !== prevRevisionIdRef.current) {\n prevRevisionIdRef.current = currentId;\n refetchDocsContent();\n }\n }, [site?.currentRevisionId]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // Compute gateway docs URL for iframe src (workspaceId prefix for isolation)\n const gatewayUrls = resolveGatewayUrls();\n const docsBaseUrl = site?.workspaceId\n ? `${gatewayUrls.workspaceBaseUrl}/workspaces/static/${site.workspaceId}/docs/${effectiveOwnerId}/`\n : null;\n\n // Mutations\n const [createSiteMutation, { loading: creating }] = useCreateDocsSiteMutation();\n const [updateSiteMutation] = useUpdateDocsSiteMutation();\n const [updateVisibilityMutation] = useUpdateDocsSiteVisibilityMutation();\n const [createPageMutation, { loading: pageAdding }] = useCreateDocsPageMutation();\n const [updatePageMutation] = useUpdateDocsPageMutation();\n const [deletePageMutation] = useDeleteDocsPageMutation();\n const [compileMutation] = useCompileDocsSiteMutation();\n const [generateSignedUrl] = useGenerateDocsSignedUrlMutation();\n const [rollbackRevision] = useRollbackDocsRevisionMutation();\n const [deleteSiteMutation] = useDeleteDocsSiteMutation();\n const [fetchRevision] = useGetDocsRevisionLazyQuery({\n fetchPolicy: 'network-only',\n });\n\n // Handlers\n const handleCreateSite = async (\n provider: 'DOCUSAURUS' | 'MKDOCS' | 'VITEPRESS' = 'DOCUSAURUS'\n ) => {\n try {\n await createSiteMutation({\n variables: {\n input: {\n ownerType: effectiveOwnerType as any,\n ownerId: effectiveOwnerId,\n name: 'Documentation',\n provider: provider as any,\n },\n },\n refetchQueries: ['GetDocsSite'],\n awaitRefetchQueries: true,\n });\n setMode('edit');\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n markQuotaHandledLocally();\n setShowQuotaExhausted(true);\n return;\n }\n throw err;\n }\n };\n\n const handleSavePage = async (\n id: string,\n data: { title?: string; content?: string; slug?: string }\n ) => {\n await updatePageMutation({\n variables: { id, input: data },\n context: { autoRefreshAfterMutation: false },\n });\n };\n\n const handleAddPage = async (parentId?: string, category?: string) => {\n if (!site) return;\n const pageCount = pages.length;\n const slug = `page-${pageCount + 1}`;\n await createPageMutation({\n variables: {\n input: {\n siteId: site.id,\n parentId: parentId || null,\n slug,\n title: `New Page ${pageCount + 1}`,\n content: '# New Page\\n\\nStart writing here...',\n status: 'DRAFT',\n sidebarPosition: pageCount,\n category: category || null,\n },\n },\n });\n await refetch();\n };\n\n const handleDeletePage = async (pageId: string) => {\n if (!confirm('Delete this page?')) return;\n await deletePageMutation({ variables: { id: pageId } });\n await refetch();\n };\n\n const handleCompile = async () => {\n if (!site) return;\n setCompiling(true);\n setCompileError(null);\n try {\n const result = await compileMutation({\n variables: { siteId: site.id, publish: true },\n });\n if (result.error && isQuotaExhaustedError(result.error)) {\n markQuotaHandledLocally();\n setCompiling(false);\n setShowCompileQuotaExhausted(true);\n return;\n }\n if (result.data?.compileDocsSite) {\n setCompileRevision(result.data.compileDocsSite as DocsRevision);\n } else {\n setCompiling(false);\n setCompileError(\n 'Build could not be started — the server returned an empty response. Please try again.'\n );\n }\n } catch (err) {\n setCompiling(false);\n if (isQuotaExhaustedError(err)) {\n markQuotaHandledLocally();\n setShowCompileQuotaExhausted(true);\n return;\n }\n setCompileError(\n err instanceof Error ? err.message : 'Failed to start the build. Please try again.'\n );\n }\n };\n\n const handlePollRevision = async (revisionId: string): Promise<DocsRevision | null> => {\n const { data: revData } = await fetchRevision({ variables: { id: revisionId } });\n const rev = revData?.docsRevision as DocsRevision | null;\n if (rev) {\n setCompileRevision(rev);\n const done = ['SUCCEEDED', 'PUBLISHED', 'FAILED'].includes(rev.status);\n if (done) {\n setCompiling(false);\n if (rev.status === 'PUBLISHED' || rev.status === 'SUCCEEDED') {\n setSelectedPageSlug('');\n await refetch();\n }\n }\n }\n return rev;\n };\n\n const handleSaveSettings = async (data: {\n name?: string;\n visibility?: 'PRIVATE' | 'PUBLIC';\n provider?: 'DOCUSAURUS' | 'MKDOCS' | 'VITEPRESS';\n providerConfig?: Record<string, unknown>;\n }) => {\n if (!site) return;\n if (data.visibility && data.visibility !== site.visibility) {\n await updateVisibilityMutation({ variables: { id: site.id, visibility: data.visibility } });\n }\n const rest = {\n name: data.name,\n provider: data.provider,\n providerConfig: data.providerConfig,\n };\n if (Object.keys(rest).length > 0) {\n await updateSiteMutation({ variables: { id: site.id, input: rest } });\n }\n await refetch();\n };\n\n const handleGetShareUrl = async (): Promise<string | null> => {\n if (!site?.currentRevisionId) return null;\n try {\n const { data: urlData } = await generateSignedUrl({ variables: { siteId: site.id } });\n return urlData?.generateDocsSignedUrl?.url ?? null;\n } catch {\n return null;\n }\n };\n\n const handleRollback = async (revisionId: string) => {\n if (!site) return;\n if (!confirm('Roll back to this revision? The current published docs will be replaced.'))\n return;\n setRollingBack(revisionId);\n try {\n await rollbackRevision({ variables: { siteId: site.id, revisionId } });\n await refetch();\n } finally {\n setRollingBack(null);\n }\n };\n\n const handleDeleteSite = async () => {\n if (!site) return;\n if (!confirm('Delete this docs site and all its pages and revisions? This cannot be undone.'))\n return;\n await deleteSiteMutation({ variables: { id: site.id } });\n await refetch();\n };\n\n // Loading / Error states\n if (loading && !site) {\n return (\n <LoadingState message={tr('vibecontrols.docs.tab.loading', 'Loading documentation...')} />\n );\n }\n\n if (error && !site) {\n return (\n <ErrorState\n title={tr('vibecontrols.docs.tab.loadFailed', 'Failed to load docs')}\n message={error.message}\n onRetry={() => refetch()}\n />\n );\n }\n\n // No site yet\n if (!site) {\n return (\n <>\n <DocsEmptyState onCreate={handleCreateSite} loading={creating} />\n <QuotaExhaustedDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing/plans');\n }}\n title=\"Docs Site Quota Reached\"\n message=\"You have reached the documentation site limit for your current plan. Upgrade to create more docs sites.\"\n />\n </>\n );\n }\n\n const compileErrorBanner = compileError ? (\n <div className=\"flex items-center gap-2 px-4 py-2 mb-2 bg-status-error-bg text-status-error-text text-sm rounded-md\">\n <AlertCircle className=\"size-4 shrink-0\" />\n <span className=\"flex-1\">{compileError}</span>\n <button\n type=\"button\"\n onClick={() => setCompileError(null)}\n className=\"p-0.5 hover:opacity-70\"\n >\n <X className=\"size-3.5\" />\n </button>\n </div>\n ) : null;\n\n const compileQuotaDialog = (\n <QuotaExhaustedDialog\n isOpen={showCompileQuotaExhausted}\n onClose={() => setShowCompileQuotaExhausted(false)}\n onConfirm={() => {\n setShowCompileQuotaExhausted(false);\n navigate('/billing/plans');\n }}\n title={getQuotaDialogContent('docs-compilation').title}\n message={getQuotaDialogContent('docs-compilation').message}\n />\n );\n\n // Generic grant-based sharing for the docs site. Owner-keyed (no read-by-id\n // query) so we pass the `(ownerId, ownerType)` pair the controller reads by.\n const shareSlot = (\n <EntityShareButton\n entity=\"docs\"\n resource={{\n id: site.id,\n name: site.name,\n vibeId: site.vibeId ?? null,\n owner: { ownerId: effectiveOwnerId, ownerType: effectiveOwnerType },\n }}\n label={tr('vibecontrols.docs.share', 'Share')}\n />\n );\n\n // Edit mode\n if (mode === 'edit') {\n return (\n <>\n {compileErrorBanner}\n {compileQuotaDialog}\n <DocsEditPanel\n site={site}\n pages={pages}\n shareSlot={shareSlot}\n onBack={() => setMode('preview')}\n onSavePage={handleSavePage}\n onAddPage={handleAddPage}\n pageAdding={pageAdding}\n onDeletePage={handleDeletePage}\n onCompile={handleCompile}\n onSaveSettings={handleSaveSettings}\n onRollback={handleRollback}\n onDeleteSite={handleDeleteSite}\n rollingBack={rollingBack}\n compileRevision={compileRevision}\n onPollRevision={handlePollRevision}\n compiling={compiling}\n />\n </>\n );\n }\n\n // Preview mode\n return (\n <>\n {compileErrorBanner}\n {compileQuotaDialog}\n <DocsPreviewPanel\n site={site}\n pages={pages}\n shareSlot={shareSlot}\n selectedPageSlug={selectedPageSlug}\n onSelectPage={setSelectedPageSlug}\n docsBaseUrl={site.currentRevisionId ? (docsBaseUrl ?? null) : null}\n docsHtml={site.currentRevisionId ? docsHtml : null}\n docsContentLoading={docsContentLoading}\n onEdit={() => setMode('edit')}\n onCompile={handleCompile}\n onSettings={() => setMode('edit')}\n onGetShareUrl={handleGetShareUrl}\n onOpenFullScreen={() => {\n const pathMap: Record<string, string> = {\n VIBE: 'vibes',\n CATALOG: 'catalogs',\n COMPONENT: 'components',\n };\n const segment = pathMap[effectiveOwnerType] || 'vibes';\n navigate(`/${segment}/${effectiveOwnerId}/docs`);\n }}\n compileRevision={compileRevision}\n onPollRevision={handlePollRevision}\n compiling={compiling}\n />\n <QuotaExhaustedDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing/plans');\n }}\n title=\"Docs Site Quota Reached\"\n message=\"You have reached the documentation site limit for your current plan. Upgrade to create more docs sites.\"\n />\n </>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DocsTabPanel.js","names":[],"sources":["../../../src/components/docs/DocsTabPanel.tsx"],"sourcesContent":["import { useState, useEffect, useRef } from 'react';\nimport { AlertCircle, X } from 'lucide-react';\nimport { LoadingState, ErrorState, QuotaExhaustedDialog } from '@/components/shared';\nimport { EntityShareButton } from '@/components/sharing';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { resolveGatewayUrls } from '@burdenoff/fe-libs/shared/config';\nimport { DocsEmptyState } from './DocsEmptyState';\nimport { DocsPreviewPanel } from './DocsPreviewPanel';\nimport { DocsEditPanel } from './DocsEditPanel';\nimport type { DocsSite, DocsRevision } from './types';\nimport {\n isQuotaExhaustedError,\n markQuotaHandledLocally,\n getQuotaDialogContent,\n} from '@/utils/quotaUtils';\nimport { useTr } from '../../shared/hooks/useTr';\nimport {\n useGetDocsSiteQuery,\n useCreateDocsSiteMutation,\n useUpdateDocsSiteMutation,\n useUpdateDocsSiteVisibilityMutation,\n useCreateDocsPageMutation,\n useUpdateDocsPageMutation,\n useDeleteDocsPageMutation,\n useCompileDocsSiteMutation,\n useDocsContentQuery,\n useGetDocsRevisionLazyQuery,\n useGenerateDocsSignedUrlMutation,\n useRollbackDocsRevisionMutation,\n useDeleteDocsSiteMutation,\n} from '@/generated/wspace-operations';\n\ninterface DocsTabPanelProps {\n vibeId?: string;\n ownerType?: string;\n ownerId?: string;\n}\n\nexport function DocsTabPanel({ vibeId, ownerType, ownerId }: DocsTabPanelProps) {\n const tr = useTr();\n const effectiveOwnerType = ownerType || 'VIBE';\n const effectiveOwnerId = ownerId || vibeId || '';\n const [mode, setMode] = useState<'preview' | 'edit'>('preview');\n const [compileRevision, setCompileRevision] = useState<DocsRevision | null>(null);\n const [compiling, setCompiling] = useState(false);\n const [compileError, setCompileError] = useState<string | null>(null);\n const [selectedPageSlug, setSelectedPageSlug] = useState('');\n const [rollingBack, setRollingBack] = useState<string | null>(null);\n const navigate = useVibeNavigate();\n const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);\n const [showCompileQuotaExhausted, setShowCompileQuotaExhausted] = useState(false);\n\n // Queries\n const { data, loading, error, refetch } = useGetDocsSiteQuery({\n variables: { ownerType: effectiveOwnerType as any, ownerId: effectiveOwnerId },\n skip: !effectiveOwnerId,\n fetchPolicy: 'cache-and-network',\n });\n\n const site = (data?.vibecontrolsDocsSiteByOwner ?? null) as DocsSite | null;\n const pages = site?.pages || [];\n\n const {\n data: docsContentData,\n loading: docsContentLoading,\n refetch: refetchDocsContent,\n } = useDocsContentQuery({\n variables: { siteId: site?.id || '', path: selectedPageSlug },\n skip: !site?.id || !site.currentRevisionId,\n fetchPolicy: 'network-only',\n });\n const docsHtml = docsContentData?.docsContent?.html ?? null;\n\n // Re-fetch docs content whenever currentRevisionId changes (compile finished or first publish).\n // This runs post-render so skip:false is guaranteed — imperative calls during async handlers\n // are no-ops when skip is still true in the closure.\n const prevRevisionIdRef = useRef<string | null | undefined>(undefined);\n useEffect(() => {\n const currentId = site?.currentRevisionId ?? null;\n if (prevRevisionIdRef.current === undefined) {\n prevRevisionIdRef.current = currentId;\n return;\n }\n if (currentId && currentId !== prevRevisionIdRef.current) {\n prevRevisionIdRef.current = currentId;\n refetchDocsContent();\n }\n }, [site?.currentRevisionId]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // Compute gateway docs URL for iframe src (workspaceId prefix for isolation)\n const gatewayUrls = resolveGatewayUrls();\n const docsBaseUrl = site?.workspaceId\n ? `${gatewayUrls.workspaceBaseUrl}/workspaces/static/${site.workspaceId}/docs/${effectiveOwnerId}/`\n : null;\n\n // Mutations\n const [createSiteMutation, { loading: creating }] = useCreateDocsSiteMutation();\n const [updateSiteMutation] = useUpdateDocsSiteMutation();\n const [updateVisibilityMutation] = useUpdateDocsSiteVisibilityMutation();\n const [createPageMutation, { loading: pageAdding }] = useCreateDocsPageMutation();\n const [updatePageMutation] = useUpdateDocsPageMutation();\n const [deletePageMutation] = useDeleteDocsPageMutation();\n const [compileMutation] = useCompileDocsSiteMutation();\n const [generateSignedUrl] = useGenerateDocsSignedUrlMutation();\n const [rollbackRevision] = useRollbackDocsRevisionMutation();\n const [deleteSiteMutation] = useDeleteDocsSiteMutation();\n const [fetchRevision] = useGetDocsRevisionLazyQuery({\n fetchPolicy: 'network-only',\n });\n\n // Handlers\n const handleCreateSite = async (\n provider: 'DOCUSAURUS' | 'MKDOCS' | 'VITEPRESS' = 'DOCUSAURUS'\n ) => {\n try {\n await createSiteMutation({\n variables: {\n input: {\n ownerType: effectiveOwnerType as any,\n ownerId: effectiveOwnerId,\n name: 'Documentation',\n provider: provider as any,\n },\n },\n refetchQueries: ['GetDocsSite'],\n awaitRefetchQueries: true,\n });\n setMode('edit');\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n markQuotaHandledLocally();\n setShowQuotaExhausted(true);\n return;\n }\n throw err;\n }\n };\n\n const handleSavePage = async (\n id: string,\n data: { title?: string; content?: string; slug?: string }\n ) => {\n await updatePageMutation({\n variables: { id, input: data },\n context: { autoRefreshAfterMutation: false },\n });\n };\n\n const handleAddPage = async (parentId?: string, category?: string) => {\n if (!site) return;\n const pageCount = pages.length;\n const slug = `page-${pageCount + 1}`;\n await createPageMutation({\n variables: {\n input: {\n siteId: site.id,\n parentId: parentId || null,\n slug,\n title: `New Page ${pageCount + 1}`,\n content: '# New Page\\n\\nStart writing here...',\n status: 'DRAFT',\n sidebarPosition: pageCount,\n category: category || null,\n },\n },\n });\n await refetch();\n };\n\n const handleDeletePage = async (pageId: string) => {\n if (!confirm('Delete this page?')) return;\n await deletePageMutation({ variables: { id: pageId } });\n await refetch();\n };\n\n const handleCompile = async () => {\n if (!site) return;\n setCompiling(true);\n setCompileError(null);\n try {\n const result = await compileMutation({\n variables: { siteId: site.id, publish: true },\n });\n if (result.error && isQuotaExhaustedError(result.error)) {\n markQuotaHandledLocally();\n setCompiling(false);\n setShowCompileQuotaExhausted(true);\n return;\n }\n if (result.data?.compileDocsSite) {\n setCompileRevision(result.data.compileDocsSite as DocsRevision);\n } else {\n setCompiling(false);\n setCompileError(\n 'Build could not be started — the server returned an empty response. Please try again.'\n );\n }\n } catch (err) {\n setCompiling(false);\n if (isQuotaExhaustedError(err)) {\n markQuotaHandledLocally();\n setShowCompileQuotaExhausted(true);\n return;\n }\n setCompileError(\n err instanceof Error ? err.message : 'Failed to start the build. Please try again.'\n );\n }\n };\n\n const handlePollRevision = async (revisionId: string): Promise<DocsRevision | null> => {\n const { data: revData } = await fetchRevision({ variables: { id: revisionId } });\n const rev = revData?.docsRevision as DocsRevision | null;\n if (rev) {\n setCompileRevision(rev);\n const done = ['SUCCEEDED', 'PUBLISHED', 'FAILED'].includes(rev.status);\n if (done) {\n setCompiling(false);\n if (rev.status === 'PUBLISHED' || rev.status === 'SUCCEEDED') {\n setSelectedPageSlug('');\n await refetch();\n }\n }\n }\n return rev;\n };\n\n const handleSaveSettings = async (data: {\n name?: string;\n visibility?: 'PRIVATE' | 'PUBLIC';\n provider?: 'DOCUSAURUS' | 'MKDOCS' | 'VITEPRESS';\n providerConfig?: Record<string, unknown>;\n }) => {\n if (!site) return;\n if (data.visibility && data.visibility !== site.visibility) {\n await updateVisibilityMutation({ variables: { id: site.id, visibility: data.visibility } });\n }\n const rest = {\n name: data.name,\n provider: data.provider,\n providerConfig: data.providerConfig,\n };\n if (Object.keys(rest).length > 0) {\n await updateSiteMutation({ variables: { id: site.id, input: rest } });\n }\n await refetch();\n };\n\n const handleGetShareUrl = async (): Promise<string | null> => {\n if (!site?.currentRevisionId) return null;\n try {\n const { data: urlData } = await generateSignedUrl({ variables: { siteId: site.id } });\n return urlData?.generateDocsSignedUrl?.url ?? null;\n } catch {\n return null;\n }\n };\n\n const handleRollback = async (revisionId: string) => {\n if (!site) return;\n if (!confirm('Roll back to this revision? The current published docs will be replaced.'))\n return;\n setRollingBack(revisionId);\n try {\n await rollbackRevision({ variables: { siteId: site.id, revisionId } });\n await refetch();\n } finally {\n setRollingBack(null);\n }\n };\n\n const handleDeleteSite = async () => {\n if (!site) return;\n if (!confirm('Delete this docs site and all its pages and revisions? This cannot be undone.'))\n return;\n await deleteSiteMutation({ variables: { id: site.id } });\n await refetch();\n };\n\n // Loading / Error states\n if (loading && !site) {\n return (\n <LoadingState message={tr('vibecontrols.docs.tab.loading', 'Loading documentation...')} />\n );\n }\n\n if (error && !site) {\n return (\n <ErrorState\n title={tr('vibecontrols.docs.tab.loadFailed', 'Failed to load docs')}\n message={error.message}\n onRetry={() => refetch()}\n />\n );\n }\n\n // No site yet\n if (!site) {\n return (\n <>\n <DocsEmptyState onCreate={handleCreateSite} loading={creating} />\n <QuotaExhaustedDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing/plans');\n }}\n title=\"Docs Site Quota Reached\"\n message=\"You have reached the documentation site limit for your current plan. Upgrade to create more docs sites.\"\n />\n </>\n );\n }\n\n const compileErrorBanner = compileError ? (\n <div className=\"flex items-center gap-2 px-4 py-2 mb-2 bg-status-error-bg-subtle text-status-error-text text-sm rounded-md\">\n <AlertCircle className=\"size-4 shrink-0\" />\n <span className=\"flex-1\">{compileError}</span>\n <button\n type=\"button\"\n onClick={() => setCompileError(null)}\n className=\"p-0.5 hover:opacity-70\"\n >\n <X className=\"size-3.5\" />\n </button>\n </div>\n ) : null;\n\n const compileQuotaDialog = (\n <QuotaExhaustedDialog\n isOpen={showCompileQuotaExhausted}\n onClose={() => setShowCompileQuotaExhausted(false)}\n onConfirm={() => {\n setShowCompileQuotaExhausted(false);\n navigate('/billing/plans');\n }}\n title={getQuotaDialogContent('docs-compilation').title}\n message={getQuotaDialogContent('docs-compilation').message}\n />\n );\n\n // Generic grant-based sharing for the docs site. Owner-keyed (no read-by-id\n // query) so we pass the `(ownerId, ownerType)` pair the controller reads by.\n const shareSlot = (\n <EntityShareButton\n entity=\"docs\"\n resource={{\n id: site.id,\n name: site.name,\n vibeId: site.vibeId ?? null,\n owner: { ownerId: effectiveOwnerId, ownerType: effectiveOwnerType },\n }}\n label={tr('vibecontrols.docs.share', 'Share')}\n />\n );\n\n // Edit mode\n if (mode === 'edit') {\n return (\n <>\n {compileErrorBanner}\n {compileQuotaDialog}\n <DocsEditPanel\n site={site}\n pages={pages}\n shareSlot={shareSlot}\n onBack={() => setMode('preview')}\n onSavePage={handleSavePage}\n onAddPage={handleAddPage}\n pageAdding={pageAdding}\n onDeletePage={handleDeletePage}\n onCompile={handleCompile}\n onSaveSettings={handleSaveSettings}\n onRollback={handleRollback}\n onDeleteSite={handleDeleteSite}\n rollingBack={rollingBack}\n compileRevision={compileRevision}\n onPollRevision={handlePollRevision}\n compiling={compiling}\n />\n </>\n );\n }\n\n // Preview mode\n return (\n <>\n {compileErrorBanner}\n {compileQuotaDialog}\n <DocsPreviewPanel\n site={site}\n pages={pages}\n shareSlot={shareSlot}\n selectedPageSlug={selectedPageSlug}\n onSelectPage={setSelectedPageSlug}\n docsBaseUrl={site.currentRevisionId ? (docsBaseUrl ?? null) : null}\n docsHtml={site.currentRevisionId ? docsHtml : null}\n docsContentLoading={docsContentLoading}\n onEdit={() => setMode('edit')}\n onCompile={handleCompile}\n onSettings={() => setMode('edit')}\n onGetShareUrl={handleGetShareUrl}\n onOpenFullScreen={() => {\n const pathMap: Record<string, string> = {\n VIBE: 'vibes',\n CATALOG: 'catalogs',\n COMPONENT: 'components',\n };\n const segment = pathMap[effectiveOwnerType] || 'vibes';\n navigate(`/${segment}/${effectiveOwnerId}/docs`);\n }}\n compileRevision={compileRevision}\n onPollRevision={handlePollRevision}\n compiling={compiling}\n />\n <QuotaExhaustedDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing/plans');\n }}\n title=\"Docs Site Quota Reached\"\n message=\"You have reached the documentation site limit for your current plan. Upgrade to create more docs sites.\"\n />\n </>\n );\n}\n"],"mappings":"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"}
|