@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
|
@@ -18,19 +18,19 @@ function m({ tunnel: m, isPending: h = !1, onOpen: g, onStart: _, onStop: v, onD
|
|
|
18
18
|
opacity: 1,
|
|
19
19
|
y: 0
|
|
20
20
|
},
|
|
21
|
-
className: "rounded-
|
|
21
|
+
className: "rounded-card border border-border-subtle bg-bg-surface shadow-elevation-1 p-5 hover:border-border-strong hover:shadow-elevation-2 hover:-translate-y-0.5 transition-all duration-200 ease-[var(--motion-easing-out)]",
|
|
22
22
|
children: [/* @__PURE__ */ p("div", {
|
|
23
|
-
className: "flex flex-col sm:flex-row sm:items-start sm:justify-between gap-
|
|
23
|
+
className: "flex flex-col sm:flex-row sm:items-start sm:justify-between gap-4",
|
|
24
24
|
children: [/* @__PURE__ */ p("button", {
|
|
25
25
|
type: "button",
|
|
26
26
|
onClick: () => g(m.id),
|
|
27
|
-
className: "flex-1 text-left min-w-0",
|
|
27
|
+
className: "flex-1 text-left min-w-0 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus rounded-button",
|
|
28
28
|
children: [
|
|
29
29
|
/* @__PURE__ */ p("div", {
|
|
30
|
-
className: "flex flex-wrap items-center gap-2 mb-1",
|
|
30
|
+
className: "flex flex-wrap items-center gap-2 mb-1.5",
|
|
31
31
|
children: [
|
|
32
32
|
/* @__PURE__ */ f("h3", {
|
|
33
|
-
className: "text-base font-semibold text-text-primary truncate",
|
|
33
|
+
className: "text-base font-semibold text-text-primary truncate tracking-tight",
|
|
34
34
|
children: m.name
|
|
35
35
|
}),
|
|
36
36
|
/* @__PURE__ */ f(i, { status: m.status }),
|
|
@@ -45,7 +45,7 @@ function m({ tunnel: m, isPending: h = !1, onOpen: g, onStart: _, onStop: v, onD
|
|
|
45
45
|
children: b("vibecontrols.tunnels.noDomain", "No domain attached")
|
|
46
46
|
}),
|
|
47
47
|
/* @__PURE__ */ p("p", {
|
|
48
|
-
className: "text-xs text-text-muted mt-1",
|
|
48
|
+
className: "text-xs font-mono text-text-muted mt-1 tabular-nums",
|
|
49
49
|
children: [
|
|
50
50
|
m.protocol,
|
|
51
51
|
":",
|
|
@@ -62,25 +62,25 @@ function m({ tunnel: m, isPending: h = !1, onOpen: g, onStart: _, onStop: v, onD
|
|
|
62
62
|
href: w,
|
|
63
63
|
target: "_blank",
|
|
64
64
|
rel: "noreferrer",
|
|
65
|
-
className: "inline-flex items-center rounded-
|
|
65
|
+
className: "inline-flex items-center rounded-button border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken hover:border-border-strong transition-colors",
|
|
66
66
|
onClick: (e) => e.stopPropagation(),
|
|
67
|
-
children: [/* @__PURE__ */ f(o, { className: "size-3.5 mr-1" }), b("vibecontrols.tunnels.open", "Open")]
|
|
67
|
+
children: [/* @__PURE__ */ f(o, { className: "size-3.5 mr-1.5" }), b("vibecontrols.tunnels.open", "Open")]
|
|
68
68
|
}),
|
|
69
69
|
h && /* @__PURE__ */ p("span", {
|
|
70
|
-
className: "inline-flex items-center rounded-
|
|
71
|
-
children: [/* @__PURE__ */ f(s, { className: "size-3.5 mr-1 animate-spin" }), m.status === "ACTIVE" ? b("vibecontrols.tunnels.stopping", "Stopping…") : b("vibecontrols.tunnels.starting", "Starting…")]
|
|
70
|
+
className: "inline-flex items-center rounded-button border border-border-subtle bg-bg-sunken px-3 py-1.5 text-xs text-text-muted",
|
|
71
|
+
children: [/* @__PURE__ */ f(s, { className: "size-3.5 mr-1.5 animate-spin" }), m.status === "ACTIVE" ? b("vibecontrols.tunnels.stopping", "Stopping…") : b("vibecontrols.tunnels.starting", "Starting…")]
|
|
72
72
|
}),
|
|
73
73
|
x && _ && /* @__PURE__ */ p("button", {
|
|
74
74
|
type: "button",
|
|
75
75
|
onClick: () => _(m.id),
|
|
76
|
-
className: "inline-flex items-center rounded-
|
|
77
|
-
children: [/* @__PURE__ */ f(c, { className: "size-3.5 mr-1" }), b("vibecontrols.tunnels.start", "Start")]
|
|
76
|
+
className: "inline-flex items-center rounded-button bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text hover:bg-action-primary-bgHover transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
|
|
77
|
+
children: [/* @__PURE__ */ f(c, { className: "size-3.5 mr-1.5" }), b("vibecontrols.tunnels.start", "Start")]
|
|
78
78
|
}),
|
|
79
79
|
S && v && /* @__PURE__ */ p("button", {
|
|
80
80
|
type: "button",
|
|
81
81
|
onClick: () => v(m.id),
|
|
82
|
-
className: "inline-flex items-center rounded-
|
|
83
|
-
children: [/* @__PURE__ */ f(l, { className: "size-3.5 mr-1" }), b("vibecontrols.tunnels.stop", "Stop")]
|
|
82
|
+
className: "inline-flex items-center rounded-button border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken hover:border-border-strong transition-colors",
|
|
83
|
+
children: [/* @__PURE__ */ f(l, { className: "size-3.5 mr-1.5" }), b("vibecontrols.tunnels.stop", "Stop")]
|
|
84
84
|
}),
|
|
85
85
|
/* @__PURE__ */ f(r, {
|
|
86
86
|
entityType: n.Tunnel,
|
|
@@ -92,14 +92,14 @@ function m({ tunnel: m, isPending: h = !1, onOpen: g, onStart: _, onStop: v, onD
|
|
|
92
92
|
y && /* @__PURE__ */ f("button", {
|
|
93
93
|
type: "button",
|
|
94
94
|
onClick: () => y(m.id, m.name),
|
|
95
|
-
className: "inline-flex items-center rounded-
|
|
95
|
+
className: "inline-flex items-center rounded-button border border-border-subtle px-2 py-1.5 text-xs text-status-error-text hover:bg-status-error-bg-subtle hover:border-status-error-border transition-colors",
|
|
96
96
|
"aria-label": "Delete tunnel",
|
|
97
97
|
children: /* @__PURE__ */ f(u, { className: "size-3.5" })
|
|
98
98
|
})
|
|
99
99
|
]
|
|
100
100
|
})]
|
|
101
101
|
}), m.lastError && /* @__PURE__ */ f("div", {
|
|
102
|
-
className: "mt-
|
|
102
|
+
className: "mt-4 rounded-button border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-xs text-status-error-text",
|
|
103
103
|
children: m.lastError
|
|
104
104
|
})]
|
|
105
105
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TunnelCard.js","names":[],"sources":["../../../src/components/tunnels/TunnelCard.tsx"],"sourcesContent":["import { motion } from 'framer-motion';\nimport { Loader2, Play, Square, Trash2, ExternalLink } from 'lucide-react';\nimport { useTr } from '../../shared/hooks/useTr';\n\nimport type { ListTunnelsQuery } from '@/generated/wspace-operations';\nimport { TunnelStatus, VibeEntityType } from '@/generated/wspace-types';\nimport { TunnelStatusBadge } from './TunnelStatusBadge';\nimport { ProviderBadge } from './ProviderBadge';\nimport { EntityVibeLinkControl } from '../shared/EntityVibeLinkControl';\n\ntype TunnelRow = NonNullable<ListTunnelsQuery['tunnels']>['items'][number];\n\ninterface TunnelCardProps {\n tunnel: TunnelRow;\n isPending?: boolean;\n onOpen: (id: string) => void;\n // Mutation callbacks are optional: the parent omits them when the actor\n // lacks the permission (tunnel:execute for start/stop, tunnel:delete), so\n // the corresponding control is hidden rather than 403-ing on click.\n onStart?: (id: string) => void;\n onStop?: (id: string) => void;\n onDelete?: (id: string, name: string) => void;\n}\n\nexport function TunnelCard({\n tunnel,\n isPending = false,\n onOpen,\n onStart,\n onStop,\n onDelete,\n}: TunnelCardProps) {\n const tr = useTr();\n const canStart =\n !isPending &&\n tunnel.status !== TunnelStatus.Active &&\n tunnel.status !== TunnelStatus.Provisioning;\n const canStop = !isPending && tunnel.status === TunnelStatus.Active;\n const hostname = tunnel.primaryDomain?.hostname;\n const publicUrl = hostname\n ? `${tunnel.protocol.toLowerCase() === 'tcp' ? 'tcp' : 'https'}://${hostname}`\n : null;\n\n return (\n <motion.div\n initial={{ opacity: 0, y: 4 }}\n animate={{ opacity: 1, y: 0 }}\n className=\"rounded-
|
|
1
|
+
{"version":3,"file":"TunnelCard.js","names":[],"sources":["../../../src/components/tunnels/TunnelCard.tsx"],"sourcesContent":["import { motion } from 'framer-motion';\nimport { Loader2, Play, Square, Trash2, ExternalLink } from 'lucide-react';\nimport { useTr } from '../../shared/hooks/useTr';\n\nimport type { ListTunnelsQuery } from '@/generated/wspace-operations';\nimport { TunnelStatus, VibeEntityType } from '@/generated/wspace-types';\nimport { TunnelStatusBadge } from './TunnelStatusBadge';\nimport { ProviderBadge } from './ProviderBadge';\nimport { EntityVibeLinkControl } from '../shared/EntityVibeLinkControl';\n\ntype TunnelRow = NonNullable<ListTunnelsQuery['tunnels']>['items'][number];\n\ninterface TunnelCardProps {\n tunnel: TunnelRow;\n isPending?: boolean;\n onOpen: (id: string) => void;\n // Mutation callbacks are optional: the parent omits them when the actor\n // lacks the permission (tunnel:execute for start/stop, tunnel:delete), so\n // the corresponding control is hidden rather than 403-ing on click.\n onStart?: (id: string) => void;\n onStop?: (id: string) => void;\n onDelete?: (id: string, name: string) => void;\n}\n\nexport function TunnelCard({\n tunnel,\n isPending = false,\n onOpen,\n onStart,\n onStop,\n onDelete,\n}: TunnelCardProps) {\n const tr = useTr();\n const canStart =\n !isPending &&\n tunnel.status !== TunnelStatus.Active &&\n tunnel.status !== TunnelStatus.Provisioning;\n const canStop = !isPending && tunnel.status === TunnelStatus.Active;\n const hostname = tunnel.primaryDomain?.hostname;\n const publicUrl = hostname\n ? `${tunnel.protocol.toLowerCase() === 'tcp' ? 'tcp' : 'https'}://${hostname}`\n : null;\n\n return (\n <motion.div\n initial={{ opacity: 0, y: 4 }}\n animate={{ opacity: 1, y: 0 }}\n className=\"rounded-card border border-border-subtle bg-bg-surface shadow-elevation-1 p-5 hover:border-border-strong hover:shadow-elevation-2 hover:-translate-y-0.5 transition-all duration-200 ease-[var(--motion-easing-out)]\"\n >\n <div className=\"flex flex-col sm:flex-row sm:items-start sm:justify-between gap-4\">\n <button\n type=\"button\"\n onClick={() => onOpen(tunnel.id)}\n className=\"flex-1 text-left min-w-0 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus rounded-button\"\n >\n <div className=\"flex flex-wrap items-center gap-2 mb-1.5\">\n <h3 className=\"text-base font-semibold text-text-primary truncate tracking-tight\">\n {tunnel.name}\n </h3>\n <TunnelStatusBadge status={tunnel.status} />\n <ProviderBadge providerKey={tunnel.providerKey} />\n </div>\n {publicUrl ? (\n <p className=\"text-sm text-text-secondary truncate\">{publicUrl}</p>\n ) : (\n <p className=\"text-sm text-text-muted\">\n {tr('vibecontrols.tunnels.noDomain', 'No domain attached')}\n </p>\n )}\n <p className=\"text-xs font-mono text-text-muted mt-1 tabular-nums\">\n {tunnel.protocol}:{tunnel.localPort} on {tunnel.localHost}\n </p>\n </button>\n\n <div className=\"flex items-center gap-2 flex-shrink-0 flex-wrap\">\n {publicUrl && (\n <a\n href={publicUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"inline-flex items-center rounded-button border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken hover:border-border-strong transition-colors\"\n onClick={(e) => e.stopPropagation()}\n >\n <ExternalLink className=\"size-3.5 mr-1.5\" />\n {tr('vibecontrols.tunnels.open', 'Open')}\n </a>\n )}\n {isPending && (\n <span className=\"inline-flex items-center rounded-button border border-border-subtle bg-bg-sunken px-3 py-1.5 text-xs text-text-muted\">\n <Loader2 className=\"size-3.5 mr-1.5 animate-spin\" />\n {tunnel.status === 'ACTIVE'\n ? tr('vibecontrols.tunnels.stopping', 'Stopping…')\n : tr('vibecontrols.tunnels.starting', 'Starting…')}\n </span>\n )}\n {canStart && onStart && (\n <button\n type=\"button\"\n onClick={() => onStart(tunnel.id)}\n className=\"inline-flex items-center rounded-button bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text hover:bg-action-primary-bgHover transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <Play className=\"size-3.5 mr-1.5\" />\n {tr('vibecontrols.tunnels.start', 'Start')}\n </button>\n )}\n {canStop && onStop && (\n <button\n type=\"button\"\n onClick={() => onStop(tunnel.id)}\n className=\"inline-flex items-center rounded-button border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken hover:border-border-strong transition-colors\"\n >\n <Square className=\"size-3.5 mr-1.5\" />\n {tr('vibecontrols.tunnels.stop', 'Stop')}\n </button>\n )}\n <EntityVibeLinkControl\n entityType={VibeEntityType.Tunnel}\n entityId={tunnel.id}\n currentVibeId={tunnel.vibeId ?? null}\n currentVisibility={tunnel.visibility ?? null}\n refetchQueries={['ListTunnels']}\n />\n {onDelete && (\n <button\n type=\"button\"\n onClick={() => onDelete(tunnel.id, tunnel.name)}\n className=\"inline-flex items-center rounded-button border border-border-subtle px-2 py-1.5 text-xs text-status-error-text hover:bg-status-error-bg-subtle hover:border-status-error-border transition-colors\"\n aria-label=\"Delete tunnel\"\n >\n <Trash2 className=\"size-3.5\" />\n </button>\n )}\n </div>\n </div>\n {tunnel.lastError && (\n <div className=\"mt-4 rounded-button border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-xs text-status-error-text\">\n {tunnel.lastError}\n </div>\n )}\n </motion.div>\n );\n}\n"],"mappings":";;;;;;;;;AAwBA,SAAgB,EAAW,EACzB,WACA,eAAY,IACZ,WACA,YACA,WACA,eACkB;CAClB,IAAM,IAAK,GAAO,EACZ,IACJ,CAAC,KACD,EAAO,WAAW,EAAa,UAC/B,EAAO,WAAW,EAAa,cAC3B,IAAU,CAAC,KAAa,EAAO,WAAW,EAAa,QACvD,IAAW,EAAO,eAAe,UACjC,IAAY,IACd,GAAG,EAAO,SAAS,aAAa,KAAK,QAAQ,QAAQ,QAAQ,KAAK,MAClE;AAEJ,QACE,kBAAC,EAAO,KAAR;EACE,SAAS;GAAE,SAAS;GAAG,GAAG;GAAG;EAC7B,SAAS;GAAE,SAAS;GAAG,GAAG;GAAG;EAC7B,WAAU;YAHZ,CAKE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,EAAO,EAAO,GAAG;IAChC,WAAU;cAHZ;KAKE,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,MAAD;QAAI,WAAU;kBACX,EAAO;QACL,CAAA;OACL,kBAAC,GAAD,EAAmB,QAAQ,EAAO,QAAU,CAAA;OAC5C,kBAAC,GAAD,EAAe,aAAa,EAAO,aAAe,CAAA;OAC9C;;KACL,IACC,kBAAC,KAAD;MAAG,WAAU;gBAAwC;MAAc,CAAA,GAEnE,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAG,iCAAiC,qBAAqB;MACxD,CAAA;KAEN,kBAAC,KAAD;MAAG,WAAU;gBAAb;OACG,EAAO;OAAS;OAAE,EAAO;OAAU;OAAK,EAAO;OAC9C;;KACG;OAET,kBAAC,OAAD;IAAK,WAAU;cAAf;KACG,KACC,kBAAC,KAAD;MACE,MAAM;MACN,QAAO;MACP,KAAI;MACJ,WAAU;MACV,UAAU,MAAM,EAAE,iBAAiB;gBALrC,CAOE,kBAAC,GAAD,EAAc,WAAU,mBAAoB,CAAA,EAC3C,EAAG,6BAA6B,OAAO,CACtC;;KAEL,KACC,kBAAC,QAAD;MAAM,WAAU;gBAAhB,CACE,kBAAC,GAAD,EAAS,WAAU,gCAAiC,CAAA,EACnD,EAAO,WAAW,WACf,EAAG,iCAAiC,YAAY,GAChD,EAAG,iCAAiC,YAAY,CAC/C;;KAER,KAAY,KACX,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAQ,EAAO,GAAG;MACjC,WAAU;gBAHZ,CAKE,kBAAC,GAAD,EAAM,WAAU,mBAAoB,CAAA,EACnC,EAAG,8BAA8B,QAAQ,CACnC;;KAEV,KAAW,KACV,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAO,EAAO,GAAG;MAChC,WAAU;gBAHZ,CAKE,kBAAC,GAAD,EAAQ,WAAU,mBAAoB,CAAA,EACrC,EAAG,6BAA6B,OAAO,CACjC;;KAEX,kBAAC,GAAD;MACE,YAAY,EAAe;MAC3B,UAAU,EAAO;MACjB,eAAe,EAAO,UAAU;MAChC,mBAAmB,EAAO,cAAc;MACxC,gBAAgB,CAAC,cAAc;MAC/B,CAAA;KACD,KACC,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAS,EAAO,IAAI,EAAO,KAAK;MAC/C,WAAU;MACV,cAAW;gBAEX,kBAAC,GAAD,EAAQ,WAAU,YAAa,CAAA;MACxB,CAAA;KAEP;MACF;MACL,EAAO,aACN,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAO;GACJ,CAAA,CAEG"}
|
|
@@ -479,7 +479,7 @@ function E() {
|
|
|
479
479
|
})]
|
|
480
480
|
}),
|
|
481
481
|
I && /* @__PURE__ */ y("div", {
|
|
482
|
-
className: "rounded-
|
|
482
|
+
className: "rounded-card border border-status-error-border bg-status-error-bg-subtle p-3 text-sm text-status-error-text",
|
|
483
483
|
children: I
|
|
484
484
|
})
|
|
485
485
|
]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TunnelCreateWizard.js","names":[],"sources":["../../../src/components/tunnels/TunnelCreateWizard.tsx"],"sourcesContent":["import { useState, useMemo, useCallback } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { motion, AnimatePresence } from 'framer-motion';\nimport { Stepper } from '@burdenoff/fe-libs/ui';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport { useVibeControls } from '@/providers/VibeControlsProvider';\n\nimport { PageHeader } from '@/components/shared/PageHeader';\nimport { LoadingState } from '@/components/shared/LoadingState';\nimport { ErrorState } from '@/components/shared/ErrorState';\nimport { QuotaExhaustedDialog } from '@/components/shared/QuotaExhaustedDialog';\nimport {\n TunnelEnvironment,\n TunnelProtocol,\n TunnelProviderKey,\n type CreateTunnelInput,\n} from '@/generated/wspace-types';\nimport {\n useCreateTunnelMutation,\n useListTunnelProvidersQuery,\n useListAgentsQuery,\n} from '@/generated/wspace-operations';\nimport {\n getQuotaDialogContent,\n markQuotaHandledLocally,\n isQuotaExhaustedError,\n} from '@/utils/quotaUtils';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { useDraftEntityTags } from '@/hooks/useDraftEntityTags';\nimport {\n LinkToVibePanel,\n LINK_TO_VIBE_DEFAULT,\n linkToVibeInput,\n type LinkToVibeValue,\n} from '@/components/shared';\n\nconst SUBDOMAIN_RE = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;\n\ntype HostnameKind = 'managed' | 'custom' | 'auto';\n\ninterface WizardState {\n providerKey: TunnelProviderKey | '';\n agentId: string;\n name: string;\n description: string;\n protocol: TunnelProtocol;\n localHost: string;\n localPort: number;\n hostnameKind: HostnameKind;\n subdomain: string;\n environment: TunnelEnvironment;\n customDomain: string;\n basicAuthUser: string;\n basicAuthPassword: string;\n}\n\nconst INITIAL_STATE: WizardState = {\n // Default to Vibe Tunnels as the preferred provider; users can still pick\n // Cloudflare Tunnels from the provider list.\n providerKey: TunnelProviderKey.Vibetunnels,\n agentId: '',\n name: '',\n description: '',\n protocol: TunnelProtocol.Http,\n localHost: '127.0.0.1',\n localPort: 3000,\n hostnameKind: 'managed',\n subdomain: '',\n environment: TunnelEnvironment.Prod,\n customDomain: '',\n basicAuthUser: '',\n basicAuthPassword: '',\n};\n\nconst STEPS = [\n { id: 'provider', label: 'Provider' },\n { id: 'target', label: 'Protocol & target' },\n { id: 'hostname', label: 'Hostname' },\n { id: 'policies', label: 'Policies' },\n { id: 'review', label: 'Review' },\n] as const;\n\nexport function TunnelCreateWizard() {\n const tr = useTr();\n const navigate = useNavigate();\n const bus = useEventBus();\n const { workspaceId } = useVibeControls();\n const [step, setStep] = useState(0);\n const [state, setState] = useState<WizardState>(INITIAL_STATE);\n const [submitError, setSubmitError] = useState<string | null>(null);\n const [isFlushingTags, setIsFlushingTags] = useState(false);\n const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);\n // G7: Optional link to a Vibe + visibility, threaded into CreateTunnelInput.\n const [vibeLink, setVibeLink] = useState<LinkToVibeValue>(LINK_TO_VIBE_DEFAULT);\n // Tags selected before the tunnel exists — persisted right after create.\n const { setDraftTagIds, flushTags } = useDraftEntityTags('VIBECONTROLS_TUNNEL');\n\n const {\n data: providersData,\n loading: providersLoading,\n error: providersError,\n } = useListTunnelProvidersQuery();\n const { data: agentsData } = useListAgentsQuery({ variables: { pagination: { limit: 100 } } });\n\n const [createTunnel, { loading: creating }] = useCreateTunnelMutation();\n\n const update = useCallback(<K extends keyof WizardState>(key: K, value: WizardState[K]) => {\n setState((prev) => ({ ...prev, [key]: value }));\n }, []);\n\n const selectProvider = useCallback((providerKey: TunnelProviderKey) => {\n setState((prev) => ({\n ...prev,\n providerKey,\n hostnameKind: providerKey === TunnelProviderKey.Cloudflare ? 'auto' : 'managed',\n }));\n }, []);\n\n // VibeTunnels first, Cloudflare second, everything else alphabetically.\n const orderedProviders = useMemo(\n () =>\n [...(providersData?.tunnelProviders ?? [])].sort((a: { key: string }, b: { key: string }) => {\n const rank = (key: string) => (key === 'VIBETUNNELS' ? 0 : key === 'CLOUDFLARE' ? 1 : 2);\n const diff = rank(a.key) - rank(b.key);\n if (diff !== 0) return diff;\n return a.key.localeCompare(b.key);\n }),\n [providersData]\n );\n\n const selectedProvider = useMemo(\n () => orderedProviders.find((p: { key: string }) => p.key === state.providerKey),\n [orderedProviders, state.providerKey]\n );\n\n const caps = selectedProvider?.capabilities;\n const supportsAutoHostname = state.providerKey === TunnelProviderKey.Cloudflare;\n const protocolOptions = useMemo(() => {\n if (!caps) return [TunnelProtocol.Http];\n const list: TunnelProtocol[] = [];\n if (caps.supportsHttp) list.push(TunnelProtocol.Http);\n if (caps.supportsHttps) list.push(TunnelProtocol.Https);\n if (caps.supportsTcp) list.push(TunnelProtocol.Tcp);\n return list;\n }, [caps]);\n\n const canAdvance = useMemo(() => {\n if (step === 0) return Boolean(state.providerKey && state.agentId && state.name.trim());\n if (step === 1) return state.localPort > 0 && state.localPort < 65536;\n if (step === 2) {\n if (state.hostnameKind === 'managed')\n return Boolean(state.subdomain && SUBDOMAIN_RE.test(state.subdomain));\n if (state.hostnameKind === 'custom') return state.customDomain.trim().includes('.');\n return supportsAutoHostname;\n }\n return true;\n }, [step, state, supportsAutoHostname]);\n\n const next = () => setStep((s) => Math.min(s + 1, STEPS.length - 1));\n const back = () => setStep((s) => Math.max(s - 1, 0));\n\n const submit = async () => {\n setSubmitError(null);\n try {\n const input: CreateTunnelInput = {\n agentId: state.agentId,\n name: state.name,\n description: state.description || undefined,\n providerKey: state.providerKey as TunnelProviderKey,\n protocol: state.protocol,\n localHost: state.localHost,\n localPort: state.localPort,\n environment: state.environment,\n basicAuthUser: state.basicAuthUser || undefined,\n basicAuthPassword: state.basicAuthPassword || undefined,\n managedSubdomain: state.hostnameKind === 'managed' ? state.subdomain : undefined,\n ...linkToVibeInput(vibeLink),\n };\n const result = await createTunnel({ variables: { input } });\n\n if (result.error) {\n if (isQuotaExhaustedError(result.error)) {\n markQuotaHandledLocally();\n setShowQuotaExhausted(true);\n bus.emitCustom('vibecontrols.quota.tunnel_limit_hit', {\n workspaceId: workspaceId ?? undefined,\n });\n return;\n }\n\n setSubmitError(result.error.message);\n return;\n }\n\n const id = result.data?.createTunnel.id;\n if (id) {\n bus.emitCustom('vibecontrols.tunnel.created', {\n workspaceId: workspaceId ?? undefined,\n tunnelId: id,\n });\n setIsFlushingTags(true);\n try {\n await flushTags(id);\n } finally {\n setIsFlushingTags(false);\n }\n // Land on the detail page and immediately prompt to start — a freshly\n // created tunnel is a DRAFT, so offer to bring it online instead of\n // making the user hunt for the Start button.\n navigate(`/vibecontrols/tunnels/${id}`, { state: { autoPromptStart: true } });\n }\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setSubmitError(null);\n markQuotaHandledLocally();\n setShowQuotaExhausted(true);\n bus.emitCustom('vibecontrols.quota.tunnel_limit_hit', {\n workspaceId: workspaceId ?? undefined,\n });\n return;\n }\n\n setSubmitError(err instanceof Error ? err.message : String(err));\n }\n };\n\n if (providersLoading) return <LoadingState message=\"Loading providers\" />;\n if (providersError) {\n return (\n <ErrorState\n title=\"Failed to load providers\"\n message={providersError.message}\n onRetry={() => window.location.reload()}\n />\n );\n }\n\n return (\n <div className=\"p-6 max-w-3xl mx-auto\">\n <PageHeader\n breadcrumb={[{ label: 'Tunnels', path: '/vibecontrols/tunnels' }, { label: 'New tunnel' }]}\n title={tr('vibecontrols.tunnels.createTitle', 'Create tunnel')}\n description={tr(\n 'vibecontrols.tunnels.createDescription',\n 'Expose a local service through a managed tunnel.'\n )}\n />\n\n <Stepper\n className=\"mb-6\"\n active={step}\n onStepClick={(i) => i <= step && setStep(i)}\n steps={STEPS.map((s) => ({ key: s.id, label: s.label }))}\n />\n\n <AnimatePresence mode=\"wait\">\n <motion.div\n key={step}\n initial={{ opacity: 0, x: 6 }}\n animate={{ opacity: 1, x: 0 }}\n exit={{ opacity: 0, x: -6 }}\n transition={{ duration: 0.15 }}\n className=\"rounded-lg border border-border-subtle bg-bg-surface p-6\"\n >\n {step === 0 && (\n <div className=\"space-y-4\">\n <h3 className=\"text-sm font-semibold text-text-primary\">Provider</h3>\n <div className=\"grid gap-3 sm:grid-cols-2\">\n {orderedProviders.map(\n (p: {\n id: string;\n key: TunnelProviderKey;\n displayName: string;\n description?: string | null;\n }) => (\n <button\n key={p.id}\n type=\"button\"\n onClick={() => selectProvider(p.key)}\n className={`rounded-lg border p-4 text-left transition-colors ${\n state.providerKey === p.key\n ? 'border-action-primary-bg bg-bg-sunken'\n : 'border-border-subtle hover:border-border-default'\n }`}\n >\n <div className=\"font-semibold text-text-primary\">{p.displayName}</div>\n <div className=\"text-xs text-text-muted mt-1\">{p.key}</div>\n {p.description && (\n <div className=\"text-xs text-text-secondary mt-2\">{p.description}</div>\n )}\n </button>\n )\n )}\n </div>\n <LabeledField label=\"Agent\">\n <select\n value={state.agentId}\n onChange={(e) => update('agentId', e.target.value)}\n className=\"w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n >\n <option value=\"\">Select agent</option>\n {(agentsData?.vibecontrolsAgents ?? []).map((a: { id: string; name: string }) => (\n <option key={a.id} value={a.id}>\n {a.name}\n </option>\n ))}\n </select>\n </LabeledField>\n <LabeledField label=\"Name\">\n <input\n aria-label=\"My dev tunnel\"\n type=\"text\"\n value={state.name}\n onChange={(e) => update('name', e.target.value)}\n placeholder=\"My dev tunnel\"\n className=\"w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-muted\"\n />\n </LabeledField>\n <LabeledField label=\"Description (optional)\">\n <input\n aria-label=\"control\"\n type=\"text\"\n value={state.description}\n onChange={(e) => update('description', e.target.value)}\n className=\"w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-muted\"\n />\n </LabeledField>\n </div>\n )}\n\n {step === 1 && (\n <div className=\"space-y-4\">\n <h3 className=\"text-sm font-semibold text-text-primary\">Protocol & target</h3>\n <LabeledField label=\"Protocol\">\n <div className=\"flex gap-2\">\n {protocolOptions.map((proto) => (\n <button\n key={proto}\n type=\"button\"\n onClick={() => update('protocol', proto)}\n className={`rounded-md border px-4 py-2 text-sm ${\n state.protocol === proto\n ? 'border-action-primary-bg bg-bg-sunken text-text-primary'\n : 'border-border-subtle text-text-secondary hover:border-border-default'\n }`}\n >\n {proto}\n </button>\n ))}\n </div>\n </LabeledField>\n <LabeledField label=\"Local host\">\n <input\n aria-label=\"control\"\n type=\"text\"\n value={state.localHost}\n onChange={(e) => update('localHost', e.target.value)}\n className=\"w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n </LabeledField>\n <LabeledField label=\"Local port\">\n <input\n aria-label=\"control\"\n type=\"number\"\n value={state.localPort}\n onChange={(e) => update('localPort', parseInt(e.target.value, 10) || 0)}\n min={1}\n max={65535}\n className=\"w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n </LabeledField>\n </div>\n )}\n\n {step === 2 && (\n <div className=\"space-y-4\">\n <h3 className=\"text-sm font-semibold text-text-primary\">Hostname</h3>\n <div className=\"flex gap-2\">\n {caps?.supportsManagedSubdomains && (\n <HostnameKindButton\n active={state.hostnameKind === 'managed'}\n onClick={() => update('hostnameKind', 'managed')}\n label=\"Managed subdomain\"\n hint=\"*.vibetunnels.com\"\n />\n )}\n {caps?.supportsCustomDomains && (\n <HostnameKindButton\n active={state.hostnameKind === 'custom'}\n onClick={() => update('hostnameKind', 'custom')}\n label=\"Custom domain\"\n hint=\"TXT verification\"\n />\n )}\n {supportsAutoHostname && (\n <HostnameKindButton\n active={state.hostnameKind === 'auto'}\n onClick={() => update('hostnameKind', 'auto')}\n label=\"Auto\"\n hint=\"assigned by Cloudflare\"\n />\n )}\n </div>\n\n {state.hostnameKind === 'managed' && (\n <>\n <LabeledField label=\"Environment\">\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={() => update('environment', TunnelEnvironment.Prod)}\n className={`rounded-md border px-4 py-2 text-sm ${\n state.environment === TunnelEnvironment.Prod\n ? 'border-action-primary-bg bg-bg-sunken text-text-primary'\n : 'border-border-subtle text-text-secondary hover:border-border-default'\n }`}\n >\n Prod (vibetunnels.com)\n </button>\n <button\n type=\"button\"\n onClick={() => update('environment', TunnelEnvironment.Alpha)}\n className={`rounded-md border px-4 py-2 text-sm ${\n state.environment === TunnelEnvironment.Alpha\n ? 'border-action-primary-bg bg-bg-sunken text-text-primary'\n : 'border-border-subtle text-text-secondary hover:border-border-default'\n }`}\n >\n Alpha\n </button>\n </div>\n </LabeledField>\n <LabeledField label=\"Subdomain label\">\n <input\n aria-label=\"demo\"\n type=\"text\"\n value={state.subdomain}\n onChange={(e) => update('subdomain', e.target.value.toLowerCase())}\n placeholder=\"demo\"\n className=\"w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n </LabeledField>\n {state.subdomain && (\n <p className=\"text-xs text-text-secondary\">\n Will be reserved as{' '}\n <span className=\"font-mono text-text-primary\">\n {state.subdomain}\n {state.environment === TunnelEnvironment.Alpha\n ? '.alpha.vibetunnels.com'\n : '.vibetunnels.com'}\n </span>\n </p>\n )}\n </>\n )}\n\n {state.hostnameKind === 'custom' && (\n <LabeledField label=\"Custom domain\">\n <input\n aria-label=\"preview.example.com\"\n type=\"text\"\n value={state.customDomain}\n onChange={(e) => update('customDomain', e.target.value.toLowerCase())}\n placeholder=\"preview.example.com\"\n className=\"w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n <p className=\"text-xs text-text-muted mt-2\">\n Custom domains are registered on create but must be verified via a TXT record\n before they can be attached. You'll see the verification challenge in the\n Domains tab after creation.\n </p>\n </LabeledField>\n )}\n </div>\n )}\n\n {step === 3 && (\n <div className=\"space-y-4\">\n <h3 className=\"text-sm font-semibold text-text-primary\">Policies (optional)</h3>\n <LabeledField label=\"Basic auth user\">\n <input\n aria-label=\"control\"\n type=\"text\"\n value={state.basicAuthUser}\n onChange={(e) => update('basicAuthUser', e.target.value)}\n className=\"w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n </LabeledField>\n <LabeledField label=\"Basic auth password\">\n <input\n aria-label=\"control\"\n type=\"password\"\n value={state.basicAuthPassword}\n onChange={(e) => update('basicAuthPassword', e.target.value)}\n className=\"w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n </LabeledField>\n <LinkToVibePanel value={vibeLink} onChange={setVibeLink} disabled={creating} />\n </div>\n )}\n\n {step === 4 && (\n <div className=\"space-y-3\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tr('vibecontrols.tunnels.review', 'Review')}\n </h3>\n <dl className=\"grid grid-cols-[auto_1fr] gap-x-4 gap-y-2 text-sm\">\n <dt className=\"text-text-muted\">Provider</dt>\n <dd className=\"text-text-primary\">{state.providerKey}</dd>\n <dt className=\"text-text-muted\">Name</dt>\n <dd className=\"text-text-primary\">{state.name}</dd>\n <dt className=\"text-text-muted\">Agent</dt>\n <dd className=\"text-text-primary font-mono\">{state.agentId}</dd>\n <dt className=\"text-text-muted\">Protocol</dt>\n <dd className=\"text-text-primary\">{state.protocol}</dd>\n <dt className=\"text-text-muted\">Target</dt>\n <dd className=\"text-text-primary font-mono\">\n {state.localHost}:{state.localPort}\n </dd>\n <dt className=\"text-text-muted\">Hostname</dt>\n <dd className=\"text-text-primary\">\n {state.hostnameKind === 'managed'\n ? `${state.subdomain}.${state.environment === 'ALPHA' ? 'alpha.' : ''}vibetunnels.com`\n : state.hostnameKind === 'custom'\n ? state.customDomain\n : 'auto-assigned'}\n </dd>\n <dt className=\"text-text-muted\">Basic auth</dt>\n <dd className=\"text-text-primary\">{state.basicAuthUser ? 'yes' : 'no'}</dd>\n </dl>\n <div className=\"border-t border-border-subtle pt-3\">\n <h4 className=\"mb-2 text-sm font-medium text-text-secondary\">\n {tr('vibecontrols.tunnels.tags', 'Tags')}\n </h4>\n <EntityTagPicker entityType=\"VIBECONTROLS_TUNNEL\" onDraftChange={setDraftTagIds} />\n </div>\n {submitError && (\n <div className=\"rounded-md bg-status-error-bg p-3 text-sm text-status-error-fg\">\n {submitError}\n </div>\n )}\n </div>\n )}\n </motion.div>\n </AnimatePresence>\n\n <div className=\"flex items-center justify-between mt-4\">\n <button\n type=\"button\"\n disabled={step === 0}\n onClick={back}\n className=\"rounded-md border border-border-default px-4 py-2 text-sm text-text-primary hover:bg-bg-elevated disabled:opacity-50\"\n >\n {tr('vibecontrols.tunnels.back', 'Back')}\n </button>\n {step < STEPS.length - 1 ? (\n <button\n type=\"button\"\n disabled={!canAdvance}\n onClick={next}\n className=\"rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-fg hover:bg-action-primary-bg-hover disabled:opacity-50\"\n >\n {tr('vibecontrols.tunnels.next', 'Next')}\n </button>\n ) : (\n <button\n type=\"button\"\n disabled={creating || isFlushingTags}\n onClick={() => void submit()}\n className=\"rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-fg hover:bg-action-primary-bg-hover disabled:opacity-50\"\n >\n {creating\n ? tr('vibecontrols.tunnels.creating', 'Creating...')\n : tr('vibecontrols.tunnels.createTunnel', 'Create tunnel')}\n </button>\n )}\n </div>\n\n <QuotaExhaustedDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing/plans');\n }}\n {...getQuotaDialogContent('tunnel-subdomain')}\n />\n </div>\n );\n}\n\nfunction LabeledField({ label, children }: { label: string; children: React.ReactNode }) {\n return (\n <label className=\"block\">\n <span className=\"text-xs font-medium text-text-secondary\">{label}</span>\n <div className=\"mt-1\">{children}</div>\n </label>\n );\n}\n\nfunction HostnameKindButton({\n active,\n onClick,\n label,\n hint,\n}: {\n active: boolean;\n onClick: () => void;\n label: string;\n hint: string;\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className={`flex-1 rounded-md border p-3 text-left transition-colors ${\n active\n ? 'border-action-primary-bg bg-bg-sunken'\n : 'border-border-subtle hover:border-border-default'\n }`}\n >\n <div className=\"text-sm font-medium text-text-primary\">{label}</div>\n <div className=\"text-xs text-text-muted mt-0.5\">{hint}</div>\n </button>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TunnelCreateWizard.js","names":[],"sources":["../../../src/components/tunnels/TunnelCreateWizard.tsx"],"sourcesContent":["import { useState, useMemo, useCallback } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { motion, AnimatePresence } from 'framer-motion';\nimport { Stepper } from '@burdenoff/fe-libs/ui';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport { useVibeControls } from '@/providers/VibeControlsProvider';\n\nimport { PageHeader } from '@/components/shared/PageHeader';\nimport { LoadingState } from '@/components/shared/LoadingState';\nimport { ErrorState } from '@/components/shared/ErrorState';\nimport { QuotaExhaustedDialog } from '@/components/shared/QuotaExhaustedDialog';\nimport {\n TunnelEnvironment,\n TunnelProtocol,\n TunnelProviderKey,\n type CreateTunnelInput,\n} from '@/generated/wspace-types';\nimport {\n useCreateTunnelMutation,\n useListTunnelProvidersQuery,\n useListAgentsQuery,\n} from '@/generated/wspace-operations';\nimport {\n getQuotaDialogContent,\n markQuotaHandledLocally,\n isQuotaExhaustedError,\n} from '@/utils/quotaUtils';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { useDraftEntityTags } from '@/hooks/useDraftEntityTags';\nimport {\n LinkToVibePanel,\n LINK_TO_VIBE_DEFAULT,\n linkToVibeInput,\n type LinkToVibeValue,\n} from '@/components/shared';\n\nconst SUBDOMAIN_RE = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;\n\ntype HostnameKind = 'managed' | 'custom' | 'auto';\n\ninterface WizardState {\n providerKey: TunnelProviderKey | '';\n agentId: string;\n name: string;\n description: string;\n protocol: TunnelProtocol;\n localHost: string;\n localPort: number;\n hostnameKind: HostnameKind;\n subdomain: string;\n environment: TunnelEnvironment;\n customDomain: string;\n basicAuthUser: string;\n basicAuthPassword: string;\n}\n\nconst INITIAL_STATE: WizardState = {\n // Default to Vibe Tunnels as the preferred provider; users can still pick\n // Cloudflare Tunnels from the provider list.\n providerKey: TunnelProviderKey.Vibetunnels,\n agentId: '',\n name: '',\n description: '',\n protocol: TunnelProtocol.Http,\n localHost: '127.0.0.1',\n localPort: 3000,\n hostnameKind: 'managed',\n subdomain: '',\n environment: TunnelEnvironment.Prod,\n customDomain: '',\n basicAuthUser: '',\n basicAuthPassword: '',\n};\n\nconst STEPS = [\n { id: 'provider', label: 'Provider' },\n { id: 'target', label: 'Protocol & target' },\n { id: 'hostname', label: 'Hostname' },\n { id: 'policies', label: 'Policies' },\n { id: 'review', label: 'Review' },\n] as const;\n\nexport function TunnelCreateWizard() {\n const tr = useTr();\n const navigate = useNavigate();\n const bus = useEventBus();\n const { workspaceId } = useVibeControls();\n const [step, setStep] = useState(0);\n const [state, setState] = useState<WizardState>(INITIAL_STATE);\n const [submitError, setSubmitError] = useState<string | null>(null);\n const [isFlushingTags, setIsFlushingTags] = useState(false);\n const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);\n // G7: Optional link to a Vibe + visibility, threaded into CreateTunnelInput.\n const [vibeLink, setVibeLink] = useState<LinkToVibeValue>(LINK_TO_VIBE_DEFAULT);\n // Tags selected before the tunnel exists — persisted right after create.\n const { setDraftTagIds, flushTags } = useDraftEntityTags('VIBECONTROLS_TUNNEL');\n\n const {\n data: providersData,\n loading: providersLoading,\n error: providersError,\n } = useListTunnelProvidersQuery();\n const { data: agentsData } = useListAgentsQuery({ variables: { pagination: { limit: 100 } } });\n\n const [createTunnel, { loading: creating }] = useCreateTunnelMutation();\n\n const update = useCallback(<K extends keyof WizardState>(key: K, value: WizardState[K]) => {\n setState((prev) => ({ ...prev, [key]: value }));\n }, []);\n\n const selectProvider = useCallback((providerKey: TunnelProviderKey) => {\n setState((prev) => ({\n ...prev,\n providerKey,\n hostnameKind: providerKey === TunnelProviderKey.Cloudflare ? 'auto' : 'managed',\n }));\n }, []);\n\n // VibeTunnels first, Cloudflare second, everything else alphabetically.\n const orderedProviders = useMemo(\n () =>\n [...(providersData?.tunnelProviders ?? [])].sort((a: { key: string }, b: { key: string }) => {\n const rank = (key: string) => (key === 'VIBETUNNELS' ? 0 : key === 'CLOUDFLARE' ? 1 : 2);\n const diff = rank(a.key) - rank(b.key);\n if (diff !== 0) return diff;\n return a.key.localeCompare(b.key);\n }),\n [providersData]\n );\n\n const selectedProvider = useMemo(\n () => orderedProviders.find((p: { key: string }) => p.key === state.providerKey),\n [orderedProviders, state.providerKey]\n );\n\n const caps = selectedProvider?.capabilities;\n const supportsAutoHostname = state.providerKey === TunnelProviderKey.Cloudflare;\n const protocolOptions = useMemo(() => {\n if (!caps) return [TunnelProtocol.Http];\n const list: TunnelProtocol[] = [];\n if (caps.supportsHttp) list.push(TunnelProtocol.Http);\n if (caps.supportsHttps) list.push(TunnelProtocol.Https);\n if (caps.supportsTcp) list.push(TunnelProtocol.Tcp);\n return list;\n }, [caps]);\n\n const canAdvance = useMemo(() => {\n if (step === 0) return Boolean(state.providerKey && state.agentId && state.name.trim());\n if (step === 1) return state.localPort > 0 && state.localPort < 65536;\n if (step === 2) {\n if (state.hostnameKind === 'managed')\n return Boolean(state.subdomain && SUBDOMAIN_RE.test(state.subdomain));\n if (state.hostnameKind === 'custom') return state.customDomain.trim().includes('.');\n return supportsAutoHostname;\n }\n return true;\n }, [step, state, supportsAutoHostname]);\n\n const next = () => setStep((s) => Math.min(s + 1, STEPS.length - 1));\n const back = () => setStep((s) => Math.max(s - 1, 0));\n\n const submit = async () => {\n setSubmitError(null);\n try {\n const input: CreateTunnelInput = {\n agentId: state.agentId,\n name: state.name,\n description: state.description || undefined,\n providerKey: state.providerKey as TunnelProviderKey,\n protocol: state.protocol,\n localHost: state.localHost,\n localPort: state.localPort,\n environment: state.environment,\n basicAuthUser: state.basicAuthUser || undefined,\n basicAuthPassword: state.basicAuthPassword || undefined,\n managedSubdomain: state.hostnameKind === 'managed' ? state.subdomain : undefined,\n ...linkToVibeInput(vibeLink),\n };\n const result = await createTunnel({ variables: { input } });\n\n if (result.error) {\n if (isQuotaExhaustedError(result.error)) {\n markQuotaHandledLocally();\n setShowQuotaExhausted(true);\n bus.emitCustom('vibecontrols.quota.tunnel_limit_hit', {\n workspaceId: workspaceId ?? undefined,\n });\n return;\n }\n\n setSubmitError(result.error.message);\n return;\n }\n\n const id = result.data?.createTunnel.id;\n if (id) {\n bus.emitCustom('vibecontrols.tunnel.created', {\n workspaceId: workspaceId ?? undefined,\n tunnelId: id,\n });\n setIsFlushingTags(true);\n try {\n await flushTags(id);\n } finally {\n setIsFlushingTags(false);\n }\n // Land on the detail page and immediately prompt to start — a freshly\n // created tunnel is a DRAFT, so offer to bring it online instead of\n // making the user hunt for the Start button.\n navigate(`/vibecontrols/tunnels/${id}`, { state: { autoPromptStart: true } });\n }\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setSubmitError(null);\n markQuotaHandledLocally();\n setShowQuotaExhausted(true);\n bus.emitCustom('vibecontrols.quota.tunnel_limit_hit', {\n workspaceId: workspaceId ?? undefined,\n });\n return;\n }\n\n setSubmitError(err instanceof Error ? err.message : String(err));\n }\n };\n\n if (providersLoading) return <LoadingState message=\"Loading providers\" />;\n if (providersError) {\n return (\n <ErrorState\n title=\"Failed to load providers\"\n message={providersError.message}\n onRetry={() => window.location.reload()}\n />\n );\n }\n\n return (\n <div className=\"p-6 max-w-3xl mx-auto\">\n <PageHeader\n breadcrumb={[{ label: 'Tunnels', path: '/vibecontrols/tunnels' }, { label: 'New tunnel' }]}\n title={tr('vibecontrols.tunnels.createTitle', 'Create tunnel')}\n description={tr(\n 'vibecontrols.tunnels.createDescription',\n 'Expose a local service through a managed tunnel.'\n )}\n />\n\n <Stepper\n className=\"mb-6\"\n active={step}\n onStepClick={(i) => i <= step && setStep(i)}\n steps={STEPS.map((s) => ({ key: s.id, label: s.label }))}\n />\n\n <AnimatePresence mode=\"wait\">\n <motion.div\n key={step}\n initial={{ opacity: 0, x: 6 }}\n animate={{ opacity: 1, x: 0 }}\n exit={{ opacity: 0, x: -6 }}\n transition={{ duration: 0.15 }}\n className=\"rounded-lg border border-border-subtle bg-bg-surface p-6\"\n >\n {step === 0 && (\n <div className=\"space-y-4\">\n <h3 className=\"text-sm font-semibold text-text-primary\">Provider</h3>\n <div className=\"grid gap-3 sm:grid-cols-2\">\n {orderedProviders.map(\n (p: {\n id: string;\n key: TunnelProviderKey;\n displayName: string;\n description?: string | null;\n }) => (\n <button\n key={p.id}\n type=\"button\"\n onClick={() => selectProvider(p.key)}\n className={`rounded-lg border p-4 text-left transition-colors ${\n state.providerKey === p.key\n ? 'border-action-primary-bg bg-bg-sunken'\n : 'border-border-subtle hover:border-border-default'\n }`}\n >\n <div className=\"font-semibold text-text-primary\">{p.displayName}</div>\n <div className=\"text-xs text-text-muted mt-1\">{p.key}</div>\n {p.description && (\n <div className=\"text-xs text-text-secondary mt-2\">{p.description}</div>\n )}\n </button>\n )\n )}\n </div>\n <LabeledField label=\"Agent\">\n <select\n value={state.agentId}\n onChange={(e) => update('agentId', e.target.value)}\n className=\"w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n >\n <option value=\"\">Select agent</option>\n {(agentsData?.vibecontrolsAgents ?? []).map((a: { id: string; name: string }) => (\n <option key={a.id} value={a.id}>\n {a.name}\n </option>\n ))}\n </select>\n </LabeledField>\n <LabeledField label=\"Name\">\n <input\n aria-label=\"My dev tunnel\"\n type=\"text\"\n value={state.name}\n onChange={(e) => update('name', e.target.value)}\n placeholder=\"My dev tunnel\"\n className=\"w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-muted\"\n />\n </LabeledField>\n <LabeledField label=\"Description (optional)\">\n <input\n aria-label=\"control\"\n type=\"text\"\n value={state.description}\n onChange={(e) => update('description', e.target.value)}\n className=\"w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-muted\"\n />\n </LabeledField>\n </div>\n )}\n\n {step === 1 && (\n <div className=\"space-y-4\">\n <h3 className=\"text-sm font-semibold text-text-primary\">Protocol & target</h3>\n <LabeledField label=\"Protocol\">\n <div className=\"flex gap-2\">\n {protocolOptions.map((proto) => (\n <button\n key={proto}\n type=\"button\"\n onClick={() => update('protocol', proto)}\n className={`rounded-md border px-4 py-2 text-sm ${\n state.protocol === proto\n ? 'border-action-primary-bg bg-bg-sunken text-text-primary'\n : 'border-border-subtle text-text-secondary hover:border-border-default'\n }`}\n >\n {proto}\n </button>\n ))}\n </div>\n </LabeledField>\n <LabeledField label=\"Local host\">\n <input\n aria-label=\"control\"\n type=\"text\"\n value={state.localHost}\n onChange={(e) => update('localHost', e.target.value)}\n className=\"w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n </LabeledField>\n <LabeledField label=\"Local port\">\n <input\n aria-label=\"control\"\n type=\"number\"\n value={state.localPort}\n onChange={(e) => update('localPort', parseInt(e.target.value, 10) || 0)}\n min={1}\n max={65535}\n className=\"w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n </LabeledField>\n </div>\n )}\n\n {step === 2 && (\n <div className=\"space-y-4\">\n <h3 className=\"text-sm font-semibold text-text-primary\">Hostname</h3>\n <div className=\"flex gap-2\">\n {caps?.supportsManagedSubdomains && (\n <HostnameKindButton\n active={state.hostnameKind === 'managed'}\n onClick={() => update('hostnameKind', 'managed')}\n label=\"Managed subdomain\"\n hint=\"*.vibetunnels.com\"\n />\n )}\n {caps?.supportsCustomDomains && (\n <HostnameKindButton\n active={state.hostnameKind === 'custom'}\n onClick={() => update('hostnameKind', 'custom')}\n label=\"Custom domain\"\n hint=\"TXT verification\"\n />\n )}\n {supportsAutoHostname && (\n <HostnameKindButton\n active={state.hostnameKind === 'auto'}\n onClick={() => update('hostnameKind', 'auto')}\n label=\"Auto\"\n hint=\"assigned by Cloudflare\"\n />\n )}\n </div>\n\n {state.hostnameKind === 'managed' && (\n <>\n <LabeledField label=\"Environment\">\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={() => update('environment', TunnelEnvironment.Prod)}\n className={`rounded-md border px-4 py-2 text-sm ${\n state.environment === TunnelEnvironment.Prod\n ? 'border-action-primary-bg bg-bg-sunken text-text-primary'\n : 'border-border-subtle text-text-secondary hover:border-border-default'\n }`}\n >\n Prod (vibetunnels.com)\n </button>\n <button\n type=\"button\"\n onClick={() => update('environment', TunnelEnvironment.Alpha)}\n className={`rounded-md border px-4 py-2 text-sm ${\n state.environment === TunnelEnvironment.Alpha\n ? 'border-action-primary-bg bg-bg-sunken text-text-primary'\n : 'border-border-subtle text-text-secondary hover:border-border-default'\n }`}\n >\n Alpha\n </button>\n </div>\n </LabeledField>\n <LabeledField label=\"Subdomain label\">\n <input\n aria-label=\"demo\"\n type=\"text\"\n value={state.subdomain}\n onChange={(e) => update('subdomain', e.target.value.toLowerCase())}\n placeholder=\"demo\"\n className=\"w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n </LabeledField>\n {state.subdomain && (\n <p className=\"text-xs text-text-secondary\">\n Will be reserved as{' '}\n <span className=\"font-mono text-text-primary\">\n {state.subdomain}\n {state.environment === TunnelEnvironment.Alpha\n ? '.alpha.vibetunnels.com'\n : '.vibetunnels.com'}\n </span>\n </p>\n )}\n </>\n )}\n\n {state.hostnameKind === 'custom' && (\n <LabeledField label=\"Custom domain\">\n <input\n aria-label=\"preview.example.com\"\n type=\"text\"\n value={state.customDomain}\n onChange={(e) => update('customDomain', e.target.value.toLowerCase())}\n placeholder=\"preview.example.com\"\n className=\"w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n <p className=\"text-xs text-text-muted mt-2\">\n Custom domains are registered on create but must be verified via a TXT record\n before they can be attached. You'll see the verification challenge in the\n Domains tab after creation.\n </p>\n </LabeledField>\n )}\n </div>\n )}\n\n {step === 3 && (\n <div className=\"space-y-4\">\n <h3 className=\"text-sm font-semibold text-text-primary\">Policies (optional)</h3>\n <LabeledField label=\"Basic auth user\">\n <input\n aria-label=\"control\"\n type=\"text\"\n value={state.basicAuthUser}\n onChange={(e) => update('basicAuthUser', e.target.value)}\n className=\"w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n </LabeledField>\n <LabeledField label=\"Basic auth password\">\n <input\n aria-label=\"control\"\n type=\"password\"\n value={state.basicAuthPassword}\n onChange={(e) => update('basicAuthPassword', e.target.value)}\n className=\"w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n </LabeledField>\n <LinkToVibePanel value={vibeLink} onChange={setVibeLink} disabled={creating} />\n </div>\n )}\n\n {step === 4 && (\n <div className=\"space-y-3\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tr('vibecontrols.tunnels.review', 'Review')}\n </h3>\n <dl className=\"grid grid-cols-[auto_1fr] gap-x-4 gap-y-2 text-sm\">\n <dt className=\"text-text-muted\">Provider</dt>\n <dd className=\"text-text-primary\">{state.providerKey}</dd>\n <dt className=\"text-text-muted\">Name</dt>\n <dd className=\"text-text-primary\">{state.name}</dd>\n <dt className=\"text-text-muted\">Agent</dt>\n <dd className=\"text-text-primary font-mono\">{state.agentId}</dd>\n <dt className=\"text-text-muted\">Protocol</dt>\n <dd className=\"text-text-primary\">{state.protocol}</dd>\n <dt className=\"text-text-muted\">Target</dt>\n <dd className=\"text-text-primary font-mono\">\n {state.localHost}:{state.localPort}\n </dd>\n <dt className=\"text-text-muted\">Hostname</dt>\n <dd className=\"text-text-primary\">\n {state.hostnameKind === 'managed'\n ? `${state.subdomain}.${state.environment === 'ALPHA' ? 'alpha.' : ''}vibetunnels.com`\n : state.hostnameKind === 'custom'\n ? state.customDomain\n : 'auto-assigned'}\n </dd>\n <dt className=\"text-text-muted\">Basic auth</dt>\n <dd className=\"text-text-primary\">{state.basicAuthUser ? 'yes' : 'no'}</dd>\n </dl>\n <div className=\"border-t border-border-subtle pt-3\">\n <h4 className=\"mb-2 text-sm font-medium text-text-secondary\">\n {tr('vibecontrols.tunnels.tags', 'Tags')}\n </h4>\n <EntityTagPicker entityType=\"VIBECONTROLS_TUNNEL\" onDraftChange={setDraftTagIds} />\n </div>\n {submitError && (\n <div className=\"rounded-card border border-status-error-border bg-status-error-bg-subtle p-3 text-sm text-status-error-text\">\n {submitError}\n </div>\n )}\n </div>\n )}\n </motion.div>\n </AnimatePresence>\n\n <div className=\"flex items-center justify-between mt-4\">\n <button\n type=\"button\"\n disabled={step === 0}\n onClick={back}\n className=\"rounded-md border border-border-default px-4 py-2 text-sm text-text-primary hover:bg-bg-elevated disabled:opacity-50\"\n >\n {tr('vibecontrols.tunnels.back', 'Back')}\n </button>\n {step < STEPS.length - 1 ? (\n <button\n type=\"button\"\n disabled={!canAdvance}\n onClick={next}\n className=\"rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-fg hover:bg-action-primary-bg-hover disabled:opacity-50\"\n >\n {tr('vibecontrols.tunnels.next', 'Next')}\n </button>\n ) : (\n <button\n type=\"button\"\n disabled={creating || isFlushingTags}\n onClick={() => void submit()}\n className=\"rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-fg hover:bg-action-primary-bg-hover disabled:opacity-50\"\n >\n {creating\n ? tr('vibecontrols.tunnels.creating', 'Creating...')\n : tr('vibecontrols.tunnels.createTunnel', 'Create tunnel')}\n </button>\n )}\n </div>\n\n <QuotaExhaustedDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing/plans');\n }}\n {...getQuotaDialogContent('tunnel-subdomain')}\n />\n </div>\n );\n}\n\nfunction LabeledField({ label, children }: { label: string; children: React.ReactNode }) {\n return (\n <label className=\"block\">\n <span className=\"text-xs font-medium text-text-secondary\">{label}</span>\n <div className=\"mt-1\">{children}</div>\n </label>\n );\n}\n\nfunction HostnameKindButton({\n active,\n onClick,\n label,\n hint,\n}: {\n active: boolean;\n onClick: () => void;\n label: string;\n hint: string;\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className={`flex-1 rounded-md border p-3 text-left transition-colors ${\n active\n ? 'border-action-primary-bg bg-bg-sunken'\n : 'border-border-subtle hover:border-border-default'\n }`}\n >\n <div className=\"text-sm font-medium text-text-primary\">{label}</div>\n <div className=\"text-xs text-text-muted mt-0.5\">{hint}</div>\n </button>\n );\n}\n"],"mappings":"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"}
|
|
@@ -62,7 +62,7 @@ var f = [
|
|
|
62
62
|
className: "space-y-3",
|
|
63
63
|
children: [
|
|
64
64
|
/* @__PURE__ */ d("div", {
|
|
65
|
-
className: "flex items-center gap-2 rounded-
|
|
65
|
+
className: "flex items-center gap-2 rounded-card border border-status-success-border bg-status-success-bg-subtle px-3 py-2 text-sm text-status-success-text",
|
|
66
66
|
children: [/* @__PURE__ */ u(o, { className: "size-4 shrink-0" }), /* @__PURE__ */ d("span", { children: [
|
|
67
67
|
b("tunPublic.liveUntil", "Public until"),
|
|
68
68
|
" ",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TunnelPublicAccessPanel.js","names":[],"sources":["../../../src/components/tunnels/TunnelPublicAccessPanel.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState } from 'react';\nimport { Globe, Copy, Check, Clock, ShieldOff } from 'lucide-react';\n\nimport { notify } from '@/lib/toast';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n useSetTunnelPublicAccessMutation,\n useRevokeTunnelPublicAccessMutation,\n} from '@/generated/wspace-operations';\n\ninterface TunnelPublicAccessPanelProps {\n tunnelId: string;\n tunnelStatus: string;\n publicAccessExpiresAt?: string | null;\n publicUrl?: string | null;\n canUpdate: boolean;\n onChanged: () => void;\n}\n\n// TTL choices in seconds, inside the svc's 300 (5m) … 2592000 (30d) bounds.\nconst TTL_OPTIONS = [\n { key: '1h', seconds: 3600 },\n { key: '4h', seconds: 14_400 },\n { key: '24h', seconds: 86_400 },\n { key: '7d', seconds: 604_800 },\n];\n\n/**\n * TunnelPublicAccessPanel — grant a time-boxed public link to a tunnel and have it tear itself down.\n * Picking a window stamps `publicAccessExpiresAt = now + ttl`; a backend sweep stops the tunnel (the\n * public URL goes dead) the moment it lapses, so a \"share my localhost for 4 hours\" or \"open this DB\n * port to a contractor until tonight\" link can't outlive its purpose. Revoke kills it immediately.\n * This is the UI half of the beta-tester-tunnel / local-port-share / database-port-forward stories.\n */\nexport const TunnelPublicAccessPanel: FC<TunnelPublicAccessPanelProps> = ({\n tunnelId,\n tunnelStatus,\n publicAccessExpiresAt,\n publicUrl,\n canUpdate,\n onChanged,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n\n const [ttlSeconds, setTtlSeconds] = useState(TTL_OPTIONS[1].seconds);\n const [copied, setCopied] = useState(false);\n const [setAccess, { loading: granting }] = useSetTunnelPublicAccessMutation();\n const [revokeAccess, { loading: revoking }] = useRevokeTunnelPublicAccessMutation();\n\n const expiresAt = publicAccessExpiresAt ? new Date(publicAccessExpiresAt) : null;\n const isActive = Boolean(expiresAt && expiresAt.getTime() > Date.now());\n const ttlLabel = (key: string): string =>\n ({ '1h': '1 hour', '4h': '4 hours', '24h': '24 hours', '7d': '7 days' })[key] ?? key;\n\n const handleGrant = async (): Promise<void> => {\n try {\n await setAccess({ variables: { tunnelId, ttlSeconds } });\n onChanged();\n notify.success(\n tr('tunPublic.granted', 'Public access granted'),\n tr('tunPublic.grantedDesc', 'The link auto-expires when the window ends.')\n );\n } catch (err) {\n notify.error(tr('tunPublic.grantFailed', 'Could not grant access'), (err as Error).message);\n }\n };\n\n const handleRevoke = async (): Promise<void> => {\n try {\n await revokeAccess({ variables: { tunnelId } });\n onChanged();\n notify.success(tr('tunPublic.revoked', 'Public access revoked — the link is dead'));\n } catch (err) {\n notify.error(tr('tunPublic.revokeFailed', 'Could not revoke'), (err as Error).message);\n }\n };\n\n const copyUrl = async (): Promise<void> => {\n if (!publicUrl) return;\n try {\n await navigator.clipboard.writeText(publicUrl);\n setCopied(true);\n notify.success(tr('tunPublic.copied', 'Public URL copied'));\n window.setTimeout(() => setCopied(false), 1500);\n } catch {\n notify.error(tr('tunPublic.copyFailed', 'Could not copy — copy it manually'), publicUrl);\n }\n };\n\n return (\n <section className=\"md:col-span-2 rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <h3 className=\"mb-3 flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <Globe className=\"size-4\" />\n {tr('tunPublic.title', 'Time-limited public access')}\n </h3>\n\n {isActive && expiresAt ? (\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2 rounded-md bg-status-success-bg px-3 py-2 text-sm text-status-success-text\">\n <Clock className=\"size-4 shrink-0\" />\n <span>\n {tr('tunPublic.liveUntil', 'Public until')}{' '}\n <strong>{expiresAt.toLocaleString()}</strong>\n {tunnelStatus !== 'ACTIVE' && (\n <span className=\"ml-1 text-text-tertiary\">\n {tr('tunPublic.notRunning', '(start the tunnel for the URL to serve)')}\n </span>\n )}\n </span>\n </div>\n {publicUrl && (\n <div className=\"flex items-center gap-2 rounded-md border border-border-default px-3 py-2\">\n <span className=\"flex-1 truncate font-mono text-xs text-text-secondary\">\n {publicUrl}\n </span>\n <button\n type=\"button\"\n onClick={() => void copyUrl()}\n className=\"p-1 text-text-secondary hover:text-text-primary\"\n aria-label={tr('common.copy', 'Copy')}\n >\n {copied ? (\n <Check className=\"size-4 text-status-success-text\" />\n ) : (\n <Copy className=\"size-4\" />\n )}\n </button>\n </div>\n )}\n {canUpdate && (\n <button\n type=\"button\"\n onClick={() => void handleRevoke()}\n disabled={revoking}\n className=\"flex items-center gap-1.5 rounded-md border border-border-default px-3 py-2 text-sm text-action-danger-text hover:bg-bg-sunken disabled:opacity-50\"\n >\n <ShieldOff className=\"size-4\" />\n {tr('tunPublic.revoke', 'Revoke now')}\n </button>\n )}\n </div>\n ) : (\n <div className=\"space-y-3\">\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'tunPublic.off',\n 'Public access is off. Grant a time-boxed window and the link tears itself down when it ends.'\n )}\n </p>\n <div className=\"flex flex-wrap items-end gap-3\">\n <label className=\"flex-1 min-w-[10rem]\">\n <span className=\"text-sm text-text-secondary\">\n {tr('tunPublic.window', 'Access window')}\n </span>\n <select\n value={ttlSeconds}\n onChange={(e) => setTtlSeconds(Number(e.target.value))}\n className=\"mt-1 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n >\n {TTL_OPTIONS.map((o) => (\n <option key={o.key} value={o.seconds}>\n {tr(`tunPublic.ttl.${o.key}`, ttlLabel(o.key))}\n </option>\n ))}\n </select>\n </label>\n {canUpdate && (\n <button\n type=\"button\"\n onClick={() => void handleGrant()}\n disabled={granting}\n className=\"flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm text-action-primary-text hover:opacity-90 disabled:opacity-50\"\n >\n <Globe className=\"size-4\" />\n {tr('tunPublic.grant', 'Grant public access')}\n </button>\n )}\n </div>\n </div>\n )}\n </section>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TunnelPublicAccessPanel.js","names":[],"sources":["../../../src/components/tunnels/TunnelPublicAccessPanel.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState } from 'react';\nimport { Globe, Copy, Check, Clock, ShieldOff } from 'lucide-react';\n\nimport { notify } from '@/lib/toast';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n useSetTunnelPublicAccessMutation,\n useRevokeTunnelPublicAccessMutation,\n} from '@/generated/wspace-operations';\n\ninterface TunnelPublicAccessPanelProps {\n tunnelId: string;\n tunnelStatus: string;\n publicAccessExpiresAt?: string | null;\n publicUrl?: string | null;\n canUpdate: boolean;\n onChanged: () => void;\n}\n\n// TTL choices in seconds, inside the svc's 300 (5m) … 2592000 (30d) bounds.\nconst TTL_OPTIONS = [\n { key: '1h', seconds: 3600 },\n { key: '4h', seconds: 14_400 },\n { key: '24h', seconds: 86_400 },\n { key: '7d', seconds: 604_800 },\n];\n\n/**\n * TunnelPublicAccessPanel — grant a time-boxed public link to a tunnel and have it tear itself down.\n * Picking a window stamps `publicAccessExpiresAt = now + ttl`; a backend sweep stops the tunnel (the\n * public URL goes dead) the moment it lapses, so a \"share my localhost for 4 hours\" or \"open this DB\n * port to a contractor until tonight\" link can't outlive its purpose. Revoke kills it immediately.\n * This is the UI half of the beta-tester-tunnel / local-port-share / database-port-forward stories.\n */\nexport const TunnelPublicAccessPanel: FC<TunnelPublicAccessPanelProps> = ({\n tunnelId,\n tunnelStatus,\n publicAccessExpiresAt,\n publicUrl,\n canUpdate,\n onChanged,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n\n const [ttlSeconds, setTtlSeconds] = useState(TTL_OPTIONS[1].seconds);\n const [copied, setCopied] = useState(false);\n const [setAccess, { loading: granting }] = useSetTunnelPublicAccessMutation();\n const [revokeAccess, { loading: revoking }] = useRevokeTunnelPublicAccessMutation();\n\n const expiresAt = publicAccessExpiresAt ? new Date(publicAccessExpiresAt) : null;\n const isActive = Boolean(expiresAt && expiresAt.getTime() > Date.now());\n const ttlLabel = (key: string): string =>\n ({ '1h': '1 hour', '4h': '4 hours', '24h': '24 hours', '7d': '7 days' })[key] ?? key;\n\n const handleGrant = async (): Promise<void> => {\n try {\n await setAccess({ variables: { tunnelId, ttlSeconds } });\n onChanged();\n notify.success(\n tr('tunPublic.granted', 'Public access granted'),\n tr('tunPublic.grantedDesc', 'The link auto-expires when the window ends.')\n );\n } catch (err) {\n notify.error(tr('tunPublic.grantFailed', 'Could not grant access'), (err as Error).message);\n }\n };\n\n const handleRevoke = async (): Promise<void> => {\n try {\n await revokeAccess({ variables: { tunnelId } });\n onChanged();\n notify.success(tr('tunPublic.revoked', 'Public access revoked — the link is dead'));\n } catch (err) {\n notify.error(tr('tunPublic.revokeFailed', 'Could not revoke'), (err as Error).message);\n }\n };\n\n const copyUrl = async (): Promise<void> => {\n if (!publicUrl) return;\n try {\n await navigator.clipboard.writeText(publicUrl);\n setCopied(true);\n notify.success(tr('tunPublic.copied', 'Public URL copied'));\n window.setTimeout(() => setCopied(false), 1500);\n } catch {\n notify.error(tr('tunPublic.copyFailed', 'Could not copy — copy it manually'), publicUrl);\n }\n };\n\n return (\n <section className=\"md:col-span-2 rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <h3 className=\"mb-3 flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <Globe className=\"size-4\" />\n {tr('tunPublic.title', 'Time-limited public access')}\n </h3>\n\n {isActive && expiresAt ? (\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2 rounded-card border border-status-success-border bg-status-success-bg-subtle px-3 py-2 text-sm text-status-success-text\">\n <Clock className=\"size-4 shrink-0\" />\n <span>\n {tr('tunPublic.liveUntil', 'Public until')}{' '}\n <strong>{expiresAt.toLocaleString()}</strong>\n {tunnelStatus !== 'ACTIVE' && (\n <span className=\"ml-1 text-text-tertiary\">\n {tr('tunPublic.notRunning', '(start the tunnel for the URL to serve)')}\n </span>\n )}\n </span>\n </div>\n {publicUrl && (\n <div className=\"flex items-center gap-2 rounded-md border border-border-default px-3 py-2\">\n <span className=\"flex-1 truncate font-mono text-xs text-text-secondary\">\n {publicUrl}\n </span>\n <button\n type=\"button\"\n onClick={() => void copyUrl()}\n className=\"p-1 text-text-secondary hover:text-text-primary\"\n aria-label={tr('common.copy', 'Copy')}\n >\n {copied ? (\n <Check className=\"size-4 text-status-success-text\" />\n ) : (\n <Copy className=\"size-4\" />\n )}\n </button>\n </div>\n )}\n {canUpdate && (\n <button\n type=\"button\"\n onClick={() => void handleRevoke()}\n disabled={revoking}\n className=\"flex items-center gap-1.5 rounded-md border border-border-default px-3 py-2 text-sm text-action-danger-text hover:bg-bg-sunken disabled:opacity-50\"\n >\n <ShieldOff className=\"size-4\" />\n {tr('tunPublic.revoke', 'Revoke now')}\n </button>\n )}\n </div>\n ) : (\n <div className=\"space-y-3\">\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'tunPublic.off',\n 'Public access is off. Grant a time-boxed window and the link tears itself down when it ends.'\n )}\n </p>\n <div className=\"flex flex-wrap items-end gap-3\">\n <label className=\"flex-1 min-w-[10rem]\">\n <span className=\"text-sm text-text-secondary\">\n {tr('tunPublic.window', 'Access window')}\n </span>\n <select\n value={ttlSeconds}\n onChange={(e) => setTtlSeconds(Number(e.target.value))}\n className=\"mt-1 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n >\n {TTL_OPTIONS.map((o) => (\n <option key={o.key} value={o.seconds}>\n {tr(`tunPublic.ttl.${o.key}`, ttlLabel(o.key))}\n </option>\n ))}\n </select>\n </label>\n {canUpdate && (\n <button\n type=\"button\"\n onClick={() => void handleGrant()}\n disabled={granting}\n className=\"flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm text-action-primary-text hover:opacity-90 disabled:opacity-50\"\n >\n <Globe className=\"size-4\" />\n {tr('tunPublic.grant', 'Grant public access')}\n </button>\n )}\n </div>\n </div>\n )}\n </section>\n );\n};\n"],"mappings":";;;;;;;AAqBA,IAAM,IAAc;CAClB;EAAE,KAAK;EAAM,SAAS;EAAM;CAC5B;EAAE,KAAK;EAAM,SAAS;EAAQ;CAC9B;EAAE,KAAK;EAAO,SAAS;EAAQ;CAC/B;EAAE,KAAK;EAAM,SAAS;EAAS;CAChC,EASY,KAA6D,EACxE,aACA,iBACA,0BACA,cACA,cACA,mBACI;CACJ,IAAM,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAI,EAAE,EAAI;AAChB,SAAO,MAAM,IAAM,IAAW;IAG1B,CAAC,GAAY,KAAiB,EAAS,EAAY,GAAG,QAAQ,EAC9D,CAAC,GAAQ,KAAa,EAAS,GAAM,EACrC,CAAC,GAAW,EAAE,SAAS,OAAc,GAAkC,EACvE,CAAC,GAAc,EAAE,SAAS,OAAc,GAAqC,EAE7E,IAAY,IAAwB,IAAI,KAAK,EAAsB,GAAG,MACtE,IAAW,GAAQ,KAAa,EAAU,SAAS,GAAG,KAAK,KAAK,GAChE,KAAY,OACf;EAAE,MAAM;EAAU,MAAM;EAAW,OAAO;EAAY,MAAM;EAAU,EAAE,MAAQ,GAE7E,IAAc,YAA2B;AAC7C,MAAI;AAGF,GAFA,MAAM,EAAU,EAAE,WAAW;IAAE;IAAU;IAAY,EAAE,CAAC,EACxD,GAAW,EACX,EAAO,QACL,EAAG,qBAAqB,wBAAwB,EAChD,EAAG,yBAAyB,8CAA8C,CAC3E;WACM,GAAK;AACZ,KAAO,MAAM,EAAG,yBAAyB,yBAAyB,EAAG,EAAc,QAAQ;;IAIzF,IAAe,YAA2B;AAC9C,MAAI;AAGF,GAFA,MAAM,EAAa,EAAE,WAAW,EAAE,aAAU,EAAE,CAAC,EAC/C,GAAW,EACX,EAAO,QAAQ,EAAG,qBAAqB,2CAA2C,CAAC;WAC5E,GAAK;AACZ,KAAO,MAAM,EAAG,0BAA0B,mBAAmB,EAAG,EAAc,QAAQ;;IAIpF,IAAU,YAA2B;AACpC,QACL,KAAI;AAIF,GAHA,MAAM,UAAU,UAAU,UAAU,EAAU,EAC9C,EAAU,GAAK,EACf,EAAO,QAAQ,EAAG,oBAAoB,oBAAoB,CAAC,EAC3D,OAAO,iBAAiB,EAAU,GAAM,EAAE,KAAK;UACzC;AACN,KAAO,MAAM,EAAG,wBAAwB,oCAAoC,EAAE,EAAU;;;AAI5F,QACE,kBAAC,WAAD;EAAS,WAAU;YAAnB,CACE,kBAAC,MAAD;GAAI,WAAU;aAAd,CACE,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA,EAC3B,EAAG,mBAAmB,6BAA6B,CACjD;MAEJ,KAAY,IACX,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAO,WAAU,mBAAoB,CAAA,EACrC,kBAAC,QAAD,EAAA,UAAA;MACG,EAAG,uBAAuB,eAAe;MAAE;MAC5C,kBAAC,UAAD,EAAA,UAAS,EAAU,gBAAgB,EAAU,CAAA;MAC5C,MAAiB,YAChB,kBAAC,QAAD;OAAM,WAAU;iBACb,EAAG,wBAAwB,0CAA0C;OACjE,CAAA;MAEJ,EAAA,CAAA,CACH;;IACL,KACC,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,QAAD;MAAM,WAAU;gBACb;MACI,CAAA,EACP,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,KAAK,GAAS;MAC7B,WAAU;MACV,cAAY,EAAG,eAAe,OAAO;gBAEpC,IACC,kBAAC,GAAD,EAAO,WAAU,mCAAoC,CAAA,GAErD,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA;MAEtB,CAAA,CACL;;IAEP,KACC,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,KAAK,GAAc;KAClC,UAAU;KACV,WAAU;eAJZ,CAME,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA,EAC/B,EAAG,oBAAoB,aAAa,CAC9B;;IAEP;OAEN,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,KAAD;IAAG,WAAU;cACV,EACC,iBACA,+FACD;IACC,CAAA,EACJ,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,SAAD;KAAO,WAAU;eAAjB,CACE,kBAAC,QAAD;MAAM,WAAU;gBACb,EAAG,oBAAoB,gBAAgB;MACnC,CAAA,EACP,kBAAC,UAAD;MACE,OAAO;MACP,WAAW,MAAM,EAAc,OAAO,EAAE,OAAO,MAAM,CAAC;MACtD,WAAU;gBAET,EAAY,KAAK,MAChB,kBAAC,UAAD;OAAoB,OAAO,EAAE;iBAC1B,EAAG,iBAAiB,EAAE,OAAO,EAAS,EAAE,IAAI,CAAC;OACvC,EAFI,EAAE,IAEN,CACT;MACK,CAAA,CACH;QACP,KACC,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,KAAK,GAAa;KACjC,UAAU;KACV,WAAU;eAJZ,CAME,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA,EAC3B,EAAG,mBAAmB,sBAAsB,CACtC;OAEP;MACF;KAEA"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { jsx as e } from "react/jsx-runtime";
|
|
2
2
|
//#region src/components/tunnels/TunnelStatusBadge.tsx
|
|
3
3
|
var t = {
|
|
4
|
-
ACTIVE: "bg-status-success-bg text-status-success-
|
|
4
|
+
ACTIVE: "bg-status-success-bg-subtle text-status-success-text",
|
|
5
5
|
DRAFT: "bg-bg-sunken text-text-secondary",
|
|
6
|
-
PROVISIONING: "bg-status-warning-bg text-status-warning-
|
|
7
|
-
DEGRADED: "bg-status-warning-bg text-status-warning-
|
|
6
|
+
PROVISIONING: "bg-status-warning-bg-subtle text-status-warning-text",
|
|
7
|
+
DEGRADED: "bg-status-warning-bg-subtle text-status-warning-text",
|
|
8
8
|
STOPPED: "bg-bg-sunken text-text-muted",
|
|
9
|
-
ERROR: "bg-status-error-bg text-status-error-
|
|
9
|
+
ERROR: "bg-status-error-bg-subtle text-status-error-text",
|
|
10
10
|
DELETING: "bg-bg-sunken text-text-muted"
|
|
11
11
|
};
|
|
12
12
|
function n({ status: n }) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TunnelStatusBadge.js","names":[],"sources":["../../../src/components/tunnels/TunnelStatusBadge.tsx"],"sourcesContent":["import type { TunnelStatus } from '@/generated/wspace-types';\n\nconst STATUS_STYLES: Record<string, string> = {\n ACTIVE: 'bg-status-success-bg text-status-success-
|
|
1
|
+
{"version":3,"file":"TunnelStatusBadge.js","names":[],"sources":["../../../src/components/tunnels/TunnelStatusBadge.tsx"],"sourcesContent":["import type { TunnelStatus } from '@/generated/wspace-types';\n\nconst STATUS_STYLES: Record<string, string> = {\n ACTIVE: 'bg-status-success-bg-subtle text-status-success-text',\n DRAFT: 'bg-bg-sunken text-text-secondary',\n PROVISIONING: 'bg-status-warning-bg-subtle text-status-warning-text',\n DEGRADED: 'bg-status-warning-bg-subtle text-status-warning-text',\n STOPPED: 'bg-bg-sunken text-text-muted',\n ERROR: 'bg-status-error-bg-subtle text-status-error-text',\n DELETING: 'bg-bg-sunken text-text-muted',\n};\n\nexport function TunnelStatusBadge({ status }: { status: TunnelStatus | string }) {\n const style = STATUS_STYLES[status] ?? 'bg-bg-sunken text-text-secondary';\n return (\n <span\n className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${style}`}\n >\n {status}\n </span>\n );\n}\n"],"mappings":";;AAEA,IAAM,IAAwC;CAC5C,QAAQ;CACR,OAAO;CACP,cAAc;CACd,UAAU;CACV,SAAS;CACT,OAAO;CACP,UAAU;CACX;AAED,SAAgB,EAAkB,EAAE,aAA6C;AAE/E,QACE,kBAAC,QAAD;EACE,WAAW,yEAHD,EAAc,MAAW;YAKlC;EACI,CAAA"}
|
|
@@ -29,7 +29,7 @@ function i({ isOpen: i, onClose: a, execution: o, taskTitle: s, agentId: c }) {
|
|
|
29
29
|
})] }), /* @__PURE__ */ n("button", {
|
|
30
30
|
type: "button",
|
|
31
31
|
onClick: a,
|
|
32
|
-
className: "p-1 rounded hover:bg-bg-
|
|
32
|
+
className: "p-1 rounded hover:bg-bg-surface",
|
|
33
33
|
children: /* @__PURE__ */ n(t, { size: 18 })
|
|
34
34
|
})]
|
|
35
35
|
}), /* @__PURE__ */ n("div", {
|
|
@@ -82,7 +82,7 @@ function i({ isOpen: i, onClose: a, execution: o, taskTitle: s, agentId: c }) {
|
|
|
82
82
|
className: "text-sm font-medium text-status-error-text mb-1",
|
|
83
83
|
children: "Error"
|
|
84
84
|
}), /* @__PURE__ */ n("pre", {
|
|
85
|
-
className: "bg-status-error-bg
|
|
85
|
+
className: "bg-status-error-bg-subtle border border-status-error-border/20 rounded-lg p-3 text-xs font-mono text-status-error-text overflow-auto max-h-40 whitespace-pre-wrap",
|
|
86
86
|
children: f
|
|
87
87
|
})] })
|
|
88
88
|
]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LogsDrawer.js","names":[],"sources":["../../../src/components/vibecalendar/LogsDrawer.tsx"],"sourcesContent":["import { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { X, Loader2 } from 'lucide-react';\nimport type { VibeCalendarExecution } from './types';\n\ninterface LogsDrawerProps {\n isOpen: boolean;\n onClose: () => void;\n execution: VibeCalendarExecution | null;\n taskTitle: string;\n agentId: string;\n}\n\nexport function LogsDrawer({\n isOpen,\n onClose,\n execution,\n taskTitle,\n agentId: _agentId,\n}: LogsDrawerProps) {\n const { t } = useI18n();\n const tr = (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n // Agent task logs are no longer fetched directly from the calendar —\n // tasks reference a VibeControls Action and execution details come\n // from the scheduler's execution records. Show what the execution\n // record carries (output/error) and defer richer logs to a future\n // Action-aware logs query.\n const logsLoading = false;\n\n if (!isOpen) return null;\n\n // Execution records from the new simplified schema carry a single\n // `output` field (stdout + stderr combined) and an `error` field.\n const stdout = execution?.output ?? null;\n const stderr: string | null = null;\n const exitCode = execution?.exitCode ?? null;\n const error = execution?.error ?? null;\n\n return (\n <div className=\"fixed inset-0 z-50 flex justify-end\">\n <div\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (onClose as (...a: unknown[]) => unknown)();\n }\n }}\n className=\"absolute inset-0 bg-bg-overlay\"\n onClick={onClose}\n />\n <div className=\"relative w-full max-w-full sm:max-w-2xl bg-bg-surface border-l border-border-default shadow-xl flex flex-col\">\n {/* Header */}\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-default\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Task Logs</h2>\n <p className=\"text-sm text-text-secondary mt-0.5\">{taskTitle}</p>\n </div>\n <button type=\"button\" onClick={onClose} className=\"p-1 rounded hover:bg-bg-
|
|
1
|
+
{"version":3,"file":"LogsDrawer.js","names":[],"sources":["../../../src/components/vibecalendar/LogsDrawer.tsx"],"sourcesContent":["import { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { X, Loader2 } from 'lucide-react';\nimport type { VibeCalendarExecution } from './types';\n\ninterface LogsDrawerProps {\n isOpen: boolean;\n onClose: () => void;\n execution: VibeCalendarExecution | null;\n taskTitle: string;\n agentId: string;\n}\n\nexport function LogsDrawer({\n isOpen,\n onClose,\n execution,\n taskTitle,\n agentId: _agentId,\n}: LogsDrawerProps) {\n const { t } = useI18n();\n const tr = (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n // Agent task logs are no longer fetched directly from the calendar —\n // tasks reference a VibeControls Action and execution details come\n // from the scheduler's execution records. Show what the execution\n // record carries (output/error) and defer richer logs to a future\n // Action-aware logs query.\n const logsLoading = false;\n\n if (!isOpen) return null;\n\n // Execution records from the new simplified schema carry a single\n // `output` field (stdout + stderr combined) and an `error` field.\n const stdout = execution?.output ?? null;\n const stderr: string | null = null;\n const exitCode = execution?.exitCode ?? null;\n const error = execution?.error ?? null;\n\n return (\n <div className=\"fixed inset-0 z-50 flex justify-end\">\n <div\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (onClose as (...a: unknown[]) => unknown)();\n }\n }}\n className=\"absolute inset-0 bg-bg-overlay\"\n onClick={onClose}\n />\n <div className=\"relative w-full max-w-full sm:max-w-2xl bg-bg-surface border-l border-border-default shadow-xl flex flex-col\">\n {/* Header */}\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-default\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Task Logs</h2>\n <p className=\"text-sm text-text-secondary mt-0.5\">{taskTitle}</p>\n </div>\n <button type=\"button\" onClick={onClose} className=\"p-1 rounded hover:bg-bg-surface\">\n <X size={18} />\n </button>\n </div>\n\n {/* Content */}\n <div className=\"flex-1 overflow-auto p-6\">\n {logsLoading ? (\n <div className=\"flex items-center justify-center h-full gap-2\">\n <Loader2 size={16} className=\"animate-spin text-text-tertiary\" />\n <p className=\"text-sm text-text-secondary\">Fetching logs…</p>\n </div>\n ) : execution ? (\n <div className=\"space-y-4\">\n {/* Execution Info */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div>\n <span className=\"text-xs text-text-tertiary\">Status</span>\n <p className=\"text-sm font-medium text-text-primary\">{execution.status}</p>\n </div>\n <div>\n <span className=\"text-xs text-text-tertiary\">Started At</span>\n <p className=\"text-sm text-text-primary\">\n {execution.startedAt\n ? new Date(execution.startedAt).toLocaleString()\n : 'Unavailable'}\n </p>\n </div>\n {execution.completedAt && (\n <div>\n <span className=\"text-xs text-text-tertiary\">Completed At</span>\n <p className=\"text-sm text-text-primary\">\n {new Date(execution.completedAt).toLocaleString()}\n </p>\n </div>\n )}\n {exitCode !== null && exitCode !== undefined && (\n <div>\n <span className=\"text-xs text-text-tertiary\">Exit Code</span>\n <p\n className={`text-sm font-mono ${exitCode === 0 ? 'text-status-success-text' : 'text-status-error-text'}`}\n >\n {exitCode}\n </p>\n </div>\n )}\n </div>\n\n {/* stdout */}\n {stdout && (\n <div>\n <h3 className=\"text-sm font-medium text-text-secondary mb-1\">stdout</h3>\n <pre className=\"bg-bg-sunken border border-border-default rounded-lg p-3 text-xs font-mono text-text-primary overflow-auto max-h-60 whitespace-pre-wrap\">\n {stdout}\n </pre>\n </div>\n )}\n\n {/* stderr */}\n {stderr && (\n <div>\n <h3 className=\"text-sm font-medium text-status-error-text mb-1\">stderr</h3>\n <pre className=\"bg-status-error-bg-subtle border border-status-error-border/20 rounded-lg p-3 text-xs font-mono text-status-error-text overflow-auto max-h-60 whitespace-pre-wrap\">\n {stderr}\n </pre>\n </div>\n )}\n\n {/* Error */}\n {error && (\n <div>\n <h3 className=\"text-sm font-medium text-status-error-text mb-1\">Error</h3>\n <pre className=\"bg-status-error-bg-subtle border border-status-error-border/20 rounded-lg p-3 text-xs font-mono text-status-error-text overflow-auto max-h-40 whitespace-pre-wrap\">\n {error}\n </pre>\n </div>\n )}\n </div>\n ) : (\n <div className=\"flex items-center justify-center h-full\">\n <p className=\"text-sm text-text-secondary\">No execution data available</p>\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;AAYA,SAAgB,EAAW,EACzB,WACA,YACA,cACA,cACA,SAAS,KACS;CAClB,IAAM,EAAE,SAAM,GAAS;AAYvB,KAAI,CAAC,EAAQ,QAAO;CAIpB,IAAM,IAAS,GAAW,UAAU,MAE9B,IAAW,GAAW,YAAY,MAClC,IAAQ,GAAW,SAAS;AAElC,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GACE,MAAK;GACL,UAAU;GACV,YAAY,MAAM;AAChB,KAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,SACjC,EAAE,gBAAgB,EACjB,GAA0C;;GAG/C,WAAU;GACV,SAAS;GACT,CAAA,EACF,kBAAC,OAAD;GAAK,WAAU;aAAf,CAEE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eAA0C;KAAc,CAAA,EACtE,kBAAC,KAAD;KAAG,WAAU;eAAsC;KAAc,CAAA,CAC7D,EAAA,CAAA,EACN,kBAAC,UAAD;KAAQ,MAAK;KAAS,SAAS;KAAS,WAAU;eAChD,kBAAC,GAAD,EAAG,MAAM,IAAM,CAAA;KACR,CAAA,CACL;OAGN,kBAAC,OAAD;IAAK,WAAU;cAMT,IACF,kBAAC,OAAD;KAAK,WAAU;eAAf;MAEE,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,QAAD;SAAM,WAAU;mBAA6B;SAAa,CAAA,EAC1D,kBAAC,KAAD;SAAG,WAAU;mBAAyC,EAAU;SAAW,CAAA,CACvE,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,QAAD;SAAM,WAAU;mBAA6B;SAAiB,CAAA,EAC9D,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAU,YACP,IAAI,KAAK,EAAU,UAAU,CAAC,gBAAgB,GAC9C;SACF,CAAA,CACA,EAAA,CAAA;QACL,EAAU,eACT,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,QAAD;SAAM,WAAU;mBAA6B;SAAmB,CAAA,EAChE,kBAAC,KAAD;SAAG,WAAU;mBACV,IAAI,KAAK,EAAU,YAAY,CAAC,gBAAgB;SAC/C,CAAA,CACA,EAAA,CAAA;QAEP,KAAa,QACZ,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,QAAD;SAAM,WAAU;mBAA6B;SAAgB,CAAA,EAC7D,kBAAC,KAAD;SACE,WAAW,qBAAqB,MAAa,IAAI,6BAA6B;mBAE7E;SACC,CAAA,CACA,EAAA,CAAA;QAEJ;;MAGL,KACC,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;OAAI,WAAU;iBAA+C;OAAW,CAAA,EACxE,kBAAC,OAAD;OAAK,WAAU;iBACZ;OACG,CAAA,CACF,EAAA,CAAA;MAIP;MAUA,KACC,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;OAAI,WAAU;iBAAkD;OAAU,CAAA,EAC1E,kBAAC,OAAD;OAAK,WAAU;iBACZ;OACG,CAAA,CACF,EAAA,CAAA;MAEJ;SAEN,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,KAAD;MAAG,WAAU;gBAA8B;MAA+B,CAAA;KACtE,CAAA;IAEJ,CAAA,CACF;KACF"}
|
|
@@ -5,7 +5,7 @@ import { useMemo as r, useState as i } from "react";
|
|
|
5
5
|
import { Search as a, X as o, Zap as s } from "lucide-react";
|
|
6
6
|
import { Fragment as c, jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
7
7
|
//#region src/components/vibecalendar/ScheduleTaskModal.tsx
|
|
8
|
-
var d = "w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-lg text-sm text-text-primary placeholder-text-placeholder focus:outline-none focus:border-border-
|
|
8
|
+
var d = "w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-lg text-sm text-text-primary placeholder-text-placeholder focus:outline-none focus:border-border-default", f = `${d} font-mono`, p = "block text-sm font-medium text-text-secondary mb-1";
|
|
9
9
|
function m({ isOpen: m, onClose: h, onSubmit: g, loading: _, prefillActionId: v }) {
|
|
10
10
|
let y = t(), [b, x] = i(v ?? ""), [S, C] = i(""), [w, T] = i(""), [E, D] = i(""), [O, k] = i("ONE_TIME"), [A, j] = i(""), [M, N] = i(""), [P, F] = i(60), [I, L] = i("minutes"), [R, z] = i(Intl.DateTimeFormat().resolvedOptions().timeZone), { data: B, loading: V } = e({
|
|
11
11
|
variables: { pagination: { limit: 100 } },
|
|
@@ -38,7 +38,7 @@ function m({ isOpen: m, onClose: h, onSubmit: g, loading: _, prefillActionId: v
|
|
|
38
38
|
}), /* @__PURE__ */ l("button", {
|
|
39
39
|
type: "button",
|
|
40
40
|
onClick: h,
|
|
41
|
-
className: "p-1 rounded hover:bg-bg-
|
|
41
|
+
className: "p-1 rounded hover:bg-bg-surface",
|
|
42
42
|
children: /* @__PURE__ */ l(o, { size: 18 })
|
|
43
43
|
})]
|
|
44
44
|
}), /* @__PURE__ */ u("form", {
|
|
@@ -70,7 +70,7 @@ function m({ isOpen: m, onClose: h, onSubmit: g, loading: _, prefillActionId: v
|
|
|
70
70
|
children: "Calendar entries trigger VibeControls Actions. Agent, command, and webhook details come from the Action definition."
|
|
71
71
|
}),
|
|
72
72
|
G && W && /* @__PURE__ */ u("div", {
|
|
73
|
-
className: "flex items-start gap-3 p-3 rounded-lg border border-border-default bg-bg-
|
|
73
|
+
className: "flex items-start gap-3 p-3 rounded-lg border border-border-default bg-bg-surface",
|
|
74
74
|
children: [/* @__PURE__ */ l("div", {
|
|
75
75
|
className: "p-1.5 rounded-md bg-primary/10 text-primary flex-shrink-0",
|
|
76
76
|
children: /* @__PURE__ */ l(s, { size: 16 })
|
|
@@ -97,7 +97,7 @@ function m({ isOpen: m, onClose: h, onSubmit: g, loading: _, prefillActionId: v
|
|
|
97
97
|
})]
|
|
98
98
|
}),
|
|
99
99
|
G && !W && V && /* @__PURE__ */ l("div", {
|
|
100
|
-
className: "p-3 rounded-lg border border-border-default bg-bg-
|
|
100
|
+
className: "p-3 rounded-lg border border-border-default bg-bg-surface text-xs text-text-secondary",
|
|
101
101
|
children: "Loading action…"
|
|
102
102
|
}),
|
|
103
103
|
G && !W && !V && /* @__PURE__ */ l("div", {
|
|
@@ -141,7 +141,7 @@ function m({ isOpen: m, onClose: h, onSubmit: g, loading: _, prefillActionId: v
|
|
|
141
141
|
return /* @__PURE__ */ l("button", {
|
|
142
142
|
type: "button",
|
|
143
143
|
onClick: () => x(e.id),
|
|
144
|
-
className: `w-full text-left px-3 py-2.5 hover:bg-bg-
|
|
144
|
+
className: `w-full text-left px-3 py-2.5 hover:bg-bg-surface transition-colors ${t ? "bg-primary/5" : ""}`,
|
|
145
145
|
children: /* @__PURE__ */ u("div", {
|
|
146
146
|
className: "flex items-start gap-2.5",
|
|
147
147
|
children: [
|