@burdenoff/microfe-vibecontrols 2026.527.13 → 2026.527.15
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.map +1 -1
- package/dist/components/actions/ActionForm.js.map +1 -1
- package/dist/components/actions/ActionSchedulesList.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.map +1 -1
- package/dist/components/agent-manager/AgentManagerBookmarksDrawer.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerBulkActions.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerCenter.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerChatPane.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerInputArea.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerLayout.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerMessageItem.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerSessionItem.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerSessionList.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerSidebar.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerStreamingIndicator.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerTab.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerTabBar.js.map +1 -1
- package/dist/components/agent-manager/BridleConfigPanel.js.map +1 -1
- package/dist/components/agent-manager/ProfilesPanel.js.map +1 -1
- package/dist/components/agent-manager/controls/ContextLevelToggle.js.map +1 -1
- package/dist/components/agent-manager/controls/FileAttachButton.js.map +1 -1
- package/dist/components/agent-manager/controls/McpServerSelector.js.map +1 -1
- package/dist/components/agent-manager/controls/ModelSelector.js.map +1 -1
- package/dist/components/agent-manager/controls/SdkSelector.js.map +1 -1
- package/dist/components/agent-manager/detail/SessionConfigPanel.js.map +1 -1
- package/dist/components/agent-manager/dialogs/ExportSessionDialog.js.map +1 -1
- package/dist/components/agent-manager/dialogs/NewSessionDialog.js.map +1 -1
- package/dist/components/agent-manager/dialogs/RenameSessionDialog.js.map +1 -1
- package/dist/components/agent-manager/dialogs/SessionSearchDialog.js.map +1 -1
- package/dist/components/agents/AgentCard.js.map +1 -1
- package/dist/components/agents/AgentForm.js.map +1 -1
- package/dist/components/agents/AgentList.js.map +1 -1
- package/dist/components/agents/OpenInBrowserEditorButton.js.map +1 -1
- package/dist/components/agents/PluginUIPanel.js.map +1 -1
- package/dist/components/agents/backup/AgentBackupTab.js.map +1 -1
- package/dist/components/ai/AIContextsTab.js.map +1 -1
- package/dist/components/ai/AIModeToggle.js.map +1 -1
- package/dist/components/ai/AIOverviewTab.js.map +1 -1
- package/dist/components/ai/AIPlanPanel.js.map +1 -1
- package/dist/components/ai/AIPlaygroundTab.js.map +1 -1
- package/dist/components/ai/AIPromptsTab.js.map +1 -1
- package/dist/components/ai/AIProviderCredentialsCard.js.map +1 -1
- package/dist/components/ai/AISessionsTab.js.map +1 -1
- package/dist/components/ai/AIStatsTab.js.map +1 -1
- package/dist/components/ai/AITasksTab.js.map +1 -1
- package/dist/components/ai/AITemplatesTab.js.map +1 -1
- package/dist/components/ai/ClaudeCredentialsCard.js.map +1 -1
- package/dist/components/aliases/AliasManager.js.map +1 -1
- package/dist/components/analytics/AnalyticsFilterBar.js.map +1 -1
- package/dist/components/assistant/AIAssistantPanel.js.map +1 -1
- package/dist/components/assistant/AssistantChatInput.js.map +1 -1
- package/dist/components/assistant/AssistantConversationList.js.map +1 -1
- package/dist/components/assistant/AssistantExportMenu.js.map +1 -1
- package/dist/components/assistant/AssistantMarkdownRenderer.js.map +1 -1
- package/dist/components/assistant/AssistantModeSelector.js.map +1 -1
- package/dist/components/assistant/AssistantWelcome.js.map +1 -1
- package/dist/components/audit/AuditDeleteDialog.js.map +1 -1
- package/dist/components/audit/AuditLogDetail.js.map +1 -1
- package/dist/components/audit/AuditLogFilters.js.map +1 -1
- package/dist/components/catalog/CatalogCard.js.map +1 -1
- package/dist/components/catalog/CatalogForm.js.map +1 -1
- package/dist/components/docs/DocsCompileStatus.js.map +1 -1
- package/dist/components/docs/DocsEditPanel.js.map +1 -1
- package/dist/components/docs/DocsEmptyState.js.map +1 -1
- package/dist/components/docs/DocsFullScreenViewer.js.map +1 -1
- package/dist/components/docs/DocsPageEditor.js.map +1 -1
- package/dist/components/docs/DocsPageTree.js.map +1 -1
- package/dist/components/docs/DocsPreviewPanel.js.map +1 -1
- package/dist/components/docs/DocsRevisionHistory.js.map +1 -1
- package/dist/components/docs/DocsSettingsPanel.js.map +1 -1
- package/dist/components/docs/DocsTabPanel.js.map +1 -1
- package/dist/components/gitops/GitOpsSetup.js.map +1 -1
- package/dist/components/gitops/VibeGitOpsTab.js.map +1 -1
- package/dist/components/graph/ConnectionPermissionsDialog.js.map +1 -1
- package/dist/components/idp-components/ComponentCard.js.map +1 -1
- package/dist/components/idp-components/ComponentForm.js.map +1 -1
- package/dist/components/logs/LogViewer.js.map +1 -1
- package/dist/components/notes/NoteCard.js.map +1 -1
- package/dist/components/notes/NoteEditor.js.map +1 -1
- package/dist/components/notes/NoteList.js.map +1 -1
- package/dist/components/notes/NotesTabPanel.js.map +1 -1
- package/dist/components/scorecard/ScorecardPanel.js.map +1 -1
- package/dist/components/sessions/CollaboratorsList.js.map +1 -1
- package/dist/components/sessions/ControlStatusBar.js.map +1 -1
- package/dist/components/sessions/SessionCard.js.map +1 -1
- package/dist/components/sessions/SessionList.js.map +1 -1
- package/dist/components/sessions/SessionShareDialog.js.map +1 -1
- package/dist/components/sessions/SharedTerminalPanel.js.map +1 -1
- package/dist/components/sessions/TerminalPanel.js.map +1 -1
- package/dist/components/settings/AdvancedSection.js.map +1 -1
- package/dist/components/settings/SettingsForm.js.map +1 -1
- package/dist/components/shared/AgentSetupBanner.js.map +1 -1
- package/dist/components/shared/ConfirmDialog.js.map +1 -1
- package/dist/components/shared/EntityTagPicker.js.map +1 -1
- package/dist/components/shared/ErrorState.js.map +1 -1
- package/dist/components/shared/FilterDropdown.js.map +1 -1
- package/dist/components/shared/PluginGate.js.map +1 -1
- package/dist/components/shared/SearchInput.js.map +1 -1
- package/dist/components/shared/SearchableSelect.js.map +1 -1
- package/dist/components/shared/SessionNotificationBanner.js.map +1 -1
- package/dist/components/targets/TargetCard.js.map +1 -1
- package/dist/components/templates/PublishTemplateForm.js.map +1 -1
- package/dist/components/templates/TemplateVariableEditor.js.map +1 -1
- package/dist/components/templates/TemplateWizard.js.map +1 -1
- package/dist/components/tunnels/TunnelCreateWizard.js.map +1 -1
- package/dist/components/vibe/VibeAgentEmptyState.js.map +1 -1
- package/dist/components/vibe/VibeAgentSwitcher.js.map +1 -1
- package/dist/components/vibe/VibeLlmContextAdminPanel.js.map +1 -1
- package/dist/components/vibe/VibeLlmContextTabPanel.js.map +1 -1
- package/dist/components/vibecalendar/LogsDrawer.js.map +1 -1
- package/dist/components/vibecalendar/ScheduleTaskModal.js.map +1 -1
- package/dist/components/vibecalendar/VibeCalAgendaView.js.map +1 -1
- package/dist/components/vibecalendar/VibeCalDayView.js.map +1 -1
- package/dist/components/vibecalendar/VibeCalWeekView.js.map +1 -1
- package/dist/components/vibecalendar/VibeCalendarHeader.js.map +1 -1
- package/dist/components/vibecalendar/VibeTaskBlock.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckButtonForm.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckButtonTile.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckExecutionLog.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckForm.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckGrid.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckShareDialog.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckSwitcher.js.map +1 -1
- package/dist/components/vibes/VibeCard.js.map +1 -1
- package/dist/components/vibes/VibeList.js.map +1 -1
- package/dist/pages/ActionsPage.js +11 -11
- package/dist/pages/ActionsPage.js.map +1 -1
- package/dist/pages/AgentDetailsPage.js +873 -703
- package/dist/pages/AgentDetailsPage.js.map +1 -1
- package/dist/pages/AgentGraphPage.js +36 -36
- package/dist/pages/AgentGraphPage.js.map +1 -1
- package/dist/pages/AgentsLandingPage.js +1 -1
- package/dist/pages/AgentsLandingPage.js.map +1 -1
- package/dist/pages/AgentsPage.js.map +1 -1
- package/dist/pages/CatalogDetailPage.js.map +1 -1
- package/dist/pages/CatalogPage.js.map +1 -1
- package/dist/pages/ComponentDetailPage.js.map +1 -1
- package/dist/pages/ComponentsPage.js.map +1 -1
- package/dist/pages/GettingStartedPage.js.map +1 -1
- package/dist/pages/HealthPage.js.map +1 -1
- package/dist/pages/LogsPage.js +10 -5
- package/dist/pages/LogsPage.js.map +1 -1
- package/dist/pages/NoteDetailsPage.js.map +1 -1
- package/dist/pages/OverviewPage.js +192 -194
- package/dist/pages/OverviewPage.js.map +1 -1
- package/dist/pages/PluginStandalonePage.js +31 -25
- package/dist/pages/PluginStandalonePage.js.map +1 -1
- package/dist/pages/SessionDetailsPage.js +74 -74
- package/dist/pages/SessionDetailsPage.js.map +1 -1
- package/dist/pages/SessionsPage.js +5 -1
- package/dist/pages/SessionsPage.js.map +1 -1
- package/dist/pages/SettingsPage.js +30 -26
- package/dist/pages/SettingsPage.js.map +1 -1
- package/dist/pages/SharedSessionPage.js.map +1 -1
- package/dist/pages/SharedVibeDeckPage.js.map +1 -1
- package/dist/pages/TargetDetailsPage.js.map +1 -1
- package/dist/pages/TargetsPage.js.map +1 -1
- package/dist/pages/TemplateDetailPage.js.map +1 -1
- package/dist/pages/TemplatesPage.js.map +1 -1
- package/dist/pages/VibeAuditPage.js +50 -47
- package/dist/pages/VibeAuditPage.js.map +1 -1
- package/dist/pages/VibeCalendarPage.js +43 -42
- package/dist/pages/VibeCalendarPage.js.map +1 -1
- package/dist/pages/VibeDeckPage.js +199 -198
- package/dist/pages/VibeDeckPage.js.map +1 -1
- package/dist/pages/VibeDetailsPage.js.map +1 -1
- package/dist/pages/VibeTaskDetailPage.js +31 -31
- package/dist/pages/VibeTaskDetailPage.js.map +1 -1
- package/dist/pages/VibesPage.js.map +1 -1
- package/dist/pages/ai/AIBookmarksPage.js.map +1 -1
- package/dist/pages/ai/AIPage.js.map +1 -1
- package/dist/pages/ai/AISessionDetailPage.js.map +1 -1
- package/dist/pages/ai/AgentManagerPage.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/TunnelDetailsPage.js.map +1 -1
- package/dist/pages/tunnels/tabs/TunnelCliTab.js.map +1 -1
- package/dist/pages/tunnels/tabs/TunnelDomainsTab.js.map +1 -1
- package/dist/providers/VibeControlsProvider.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActionCard.js","names":[],"sources":["../../../src/components/actions/ActionCard.tsx"],"sourcesContent":["import { Play, Pencil, Copy, Trash2, Clock, Hash, CalendarClock, AlarmClock } from 'lucide-react';\nimport { motion, useReducedMotion } from 'framer-motion';\nimport { ActionTypeBadge, TriggerTypeBadge, SourceTypeBadge } from './ActionTypeBadge';\nimport { SelectionCheckbox } from '../shared/SelectionCheckbox';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface ActionCardAction {\n id: string;\n name: string;\n displayName?: string | null;\n description?: string | null;\n actionType: string;\n sourceType?: string | null;\n triggerType: string;\n category?: string | null;\n tags?: string[] | null;\n executionCount?: number | null;\n lastExecutedAt?: string | null;\n confirmationRequired?: boolean;\n}\n\ninterface ActionCardProps {\n action: ActionCardAction;\n onExecute: (id: string) => void;\n onEdit: (id: string) => void;\n onDuplicate: (id: string) => void;\n onDelete: (id: string) => void;\n onSchedule?: (id: string) => void;\n onAddToCalendar?: (id: string) => void;\n canUpdate: boolean;\n canDelete: boolean;\n canExecute: boolean;\n isExecuting?: boolean;\n selected?: boolean;\n onToggleSelect?: (checked: boolean) => void;\n}\n\nexport function ActionCard({\n action,\n onExecute,\n onEdit,\n onDuplicate,\n onDelete,\n onSchedule,\n onAddToCalendar,\n canUpdate,\n canDelete,\n canExecute,\n isExecuting = false,\n selected = false,\n onToggleSelect,\n}: ActionCardProps) {\n const shouldReduceMotion = useReducedMotion();\n const tr = useTr();\n\n const formatDate = (dateStr: string | null | undefined) => {\n if (!dateStr) return tr('vibecontrols.actions.card.never', 'Never');\n const date = new Date(dateStr);\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffMin = Math.floor(diffMs / 60000);\n if (diffMin < 1) return tr('vibecontrols.actions.card.justNow', 'Just now');\n if (diffMin < 60)\n return tr('vibecontrols.actions.card.minutesAgo', '{{count}}m ago', { count: diffMin });\n const diffHr = Math.floor(diffMin / 60);\n if (diffHr < 24)\n return tr('vibecontrols.actions.card.hoursAgo', '{{count}}h ago', { count: diffHr });\n const diffDays = Math.floor(diffHr / 24);\n if (diffDays < 7) return `${diffDays}d ago`;\n return date.toLocaleDateString();\n };\n\n return (\n <motion.div\n className={`bg-bg-surface border rounded-lg p-4 hover:border-border-strong transition-colors group ${\n selected\n ? 'border-action-primary-bg ring-1 ring-action-primary-bg/25'\n : 'border-border-default'\n }`}\n whileHover={\n shouldReduceMotion\n ? undefined\n : { y: -2, boxShadow: '0 4px 12px var(--color-border-default)' }\n }\n transition={{ duration: 0.15 }}\n layout\n role=\"group\"\n aria-label={`Action: ${action.displayName || action.name}`}\n >\n {/* Header */}\n <div className=\"flex items-start justify-between gap-3 mb-3\">\n <div className=\"flex items-start gap-3 min-w-0 flex-1\">\n {onToggleSelect && (\n <div onClick={(event) => event.stopPropagation()}>\n <SelectionCheckbox\n checked={selected}\n onChange={onToggleSelect}\n ariaLabel={`Select action ${action.displayName || action.name}`}\n />\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"font-semibold text-text-primary truncate\">\n {action.displayName || action.name}\n </h3>\n {action.description && (\n <p className=\"text-sm text-text-secondary mt-0.5 line-clamp-2\">\n {action.description}\n </p>\n )}\n </div>\n </div>\n {/* Action buttons */}\n <div className=\"flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity flex-shrink-0\">\n {canExecute && (\n <button type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n onExecute(action.id);\n }}\n disabled={isExecuting}\n className=\"p-1.5 rounded-md hover:bg-status-success-bg/20 text-status-success-text transition-colors disabled:opacity-50\"\n title={tr('vibecontrols.actions.card.execute', 'Execute')}\n >\n <Play size={14} />\n </button>\n )}\n {canUpdate && (\n <button type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n onEdit(action.id);\n }}\n className=\"p-1.5 rounded-md hover:bg-bg-elevated text-text-secondary transition-colors\"\n title={tr('vibecontrols.actions.card.edit', 'Edit')}\n >\n <Pencil size={14} />\n </button>\n )}\n {onSchedule && (\n <button type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n onSchedule(action.id);\n }}\n className=\"p-1.5 rounded-md hover:bg-bg-elevated text-text-secondary transition-colors\"\n title={tr('vibecontrols.actions.card.schedule', 'Schedule this action')}\n >\n <AlarmClock size={14} />\n </button>\n )}\n {onAddToCalendar && (\n <button type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n onAddToCalendar(action.id);\n }}\n className=\"p-1.5 rounded-md hover:bg-bg-elevated text-text-secondary transition-colors\"\n title={tr('vibecontrols.actions.card.addToCalendar', 'Add to calendar')}\n >\n <CalendarClock size={14} />\n </button>\n )}\n <button type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n onDuplicate(action.id);\n }}\n className=\"p-1.5 rounded-md hover:bg-bg-elevated text-text-secondary transition-colors\"\n title={tr('vibecontrols.actions.card.duplicate', 'Duplicate')}\n >\n <Copy size={14} />\n </button>\n {canDelete && (\n <button type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n onDelete(action.id);\n }}\n className=\"p-1.5 rounded-md hover:bg-status-error-bg/20 text-status-error-text transition-colors\"\n title={tr('vibecontrols.actions.card.delete', 'Delete')}\n >\n <Trash2 size={14} />\n </button>\n )}\n </div>\n </div>\n\n {/* Badges */}\n <div className=\"flex flex-wrap items-center gap-1.5 mb-3\">\n <ActionTypeBadge type={action.actionType} />\n <TriggerTypeBadge type={action.triggerType} />\n {action.sourceType && <SourceTypeBadge type={action.sourceType} />}\n </div>\n\n {/* Category and Tags */}\n {(action.category || (action.tags && action.tags.length > 0)) && (\n <div className=\"flex flex-wrap items-center gap-1.5 mb-3\">\n {action.category && (\n <span className=\"text-xs px-1.5 py-0.5 rounded bg-bg-sunken text-text-secondary\">\n {action.category}\n </span>\n )}\n {action.tags?.map((tag) => (\n <span\n key={tag}\n className=\"text-xs px-1.5 py-0.5 rounded bg-bg-sunken text-text-placeholder\"\n >\n #{tag}\n </span>\n ))}\n </div>\n )}\n\n {/* Stats */}\n <div className=\"flex items-center gap-4 text-xs text-text-placeholder\">\n <span className=\"inline-flex items-center gap-1\">\n <Hash size={12} />\n {tr('vibecontrols.actions.card.executionRuns', '{{count}} runs', {\n count: action.executionCount ?? 0,\n })}\n </span>\n <span className=\"inline-flex items-center gap-1\">\n <Clock size={12} />\n {formatDate(action.lastExecutedAt)}\n </span>\n </div>\n </motion.div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ActionCard.js","names":[],"sources":["../../../src/components/actions/ActionCard.tsx"],"sourcesContent":["import { Play, Pencil, Copy, Trash2, Clock, Hash, CalendarClock, AlarmClock } from 'lucide-react';\nimport { motion, useReducedMotion } from 'framer-motion';\nimport { ActionTypeBadge, TriggerTypeBadge, SourceTypeBadge } from './ActionTypeBadge';\nimport { SelectionCheckbox } from '../shared/SelectionCheckbox';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface ActionCardAction {\n id: string;\n name: string;\n displayName?: string | null;\n description?: string | null;\n actionType: string;\n sourceType?: string | null;\n triggerType: string;\n category?: string | null;\n tags?: string[] | null;\n executionCount?: number | null;\n lastExecutedAt?: string | null;\n confirmationRequired?: boolean;\n}\n\ninterface ActionCardProps {\n action: ActionCardAction;\n onExecute: (id: string) => void;\n onEdit: (id: string) => void;\n onDuplicate: (id: string) => void;\n onDelete: (id: string) => void;\n onSchedule?: (id: string) => void;\n onAddToCalendar?: (id: string) => void;\n canUpdate: boolean;\n canDelete: boolean;\n canExecute: boolean;\n isExecuting?: boolean;\n selected?: boolean;\n onToggleSelect?: (checked: boolean) => void;\n}\n\nexport function ActionCard({\n action,\n onExecute,\n onEdit,\n onDuplicate,\n onDelete,\n onSchedule,\n onAddToCalendar,\n canUpdate,\n canDelete,\n canExecute,\n isExecuting = false,\n selected = false,\n onToggleSelect,\n}: ActionCardProps) {\n const shouldReduceMotion = useReducedMotion();\n const tr = useTr();\n\n const formatDate = (dateStr: string | null | undefined) => {\n if (!dateStr) return tr('vibecontrols.actions.card.never', 'Never');\n const date = new Date(dateStr);\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffMin = Math.floor(diffMs / 60000);\n if (diffMin < 1) return tr('vibecontrols.actions.card.justNow', 'Just now');\n if (diffMin < 60)\n return tr('vibecontrols.actions.card.minutesAgo', '{{count}}m ago', { count: diffMin });\n const diffHr = Math.floor(diffMin / 60);\n if (diffHr < 24)\n return tr('vibecontrols.actions.card.hoursAgo', '{{count}}h ago', { count: diffHr });\n const diffDays = Math.floor(diffHr / 24);\n if (diffDays < 7) return `${diffDays}d ago`;\n return date.toLocaleDateString();\n };\n\n return (\n <motion.div\n className={`bg-bg-surface border rounded-lg p-4 hover:border-border-strong transition-colors group ${\n selected\n ? 'border-action-primary-bg ring-1 ring-action-primary-bg/25'\n : 'border-border-default'\n }`}\n whileHover={\n shouldReduceMotion\n ? undefined\n : { y: -2, boxShadow: '0 4px 12px var(--color-border-default)' }\n }\n transition={{ duration: 0.15 }}\n layout\n role=\"group\"\n aria-label={`Action: ${action.displayName || action.name}`}\n >\n {/* Header */}\n <div className=\"flex items-start justify-between gap-3 mb-3\">\n <div className=\"flex items-start gap-3 min-w-0 flex-1\">\n {onToggleSelect && (\n <div onClick={(event) => event.stopPropagation()}>\n <SelectionCheckbox\n checked={selected}\n onChange={onToggleSelect}\n ariaLabel={`Select action ${action.displayName || action.name}`}\n />\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"font-semibold text-text-primary truncate\">\n {action.displayName || action.name}\n </h3>\n {action.description && (\n <p className=\"text-sm text-text-secondary mt-0.5 line-clamp-2\">\n {action.description}\n </p>\n )}\n </div>\n </div>\n {/* Action buttons */}\n <div className=\"flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity flex-shrink-0\">\n {canExecute && (\n <button\n type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n onExecute(action.id);\n }}\n disabled={isExecuting}\n className=\"p-1.5 rounded-md hover:bg-status-success-bg/20 text-status-success-text transition-colors disabled:opacity-50\"\n title={tr('vibecontrols.actions.card.execute', 'Execute')}\n >\n <Play size={14} />\n </button>\n )}\n {canUpdate && (\n <button\n type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n onEdit(action.id);\n }}\n className=\"p-1.5 rounded-md hover:bg-bg-elevated text-text-secondary transition-colors\"\n title={tr('vibecontrols.actions.card.edit', 'Edit')}\n >\n <Pencil size={14} />\n </button>\n )}\n {onSchedule && (\n <button\n type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n onSchedule(action.id);\n }}\n className=\"p-1.5 rounded-md hover:bg-bg-elevated text-text-secondary transition-colors\"\n title={tr('vibecontrols.actions.card.schedule', 'Schedule this action')}\n >\n <AlarmClock size={14} />\n </button>\n )}\n {onAddToCalendar && (\n <button\n type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n onAddToCalendar(action.id);\n }}\n className=\"p-1.5 rounded-md hover:bg-bg-elevated text-text-secondary transition-colors\"\n title={tr('vibecontrols.actions.card.addToCalendar', 'Add to calendar')}\n >\n <CalendarClock size={14} />\n </button>\n )}\n <button\n type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n onDuplicate(action.id);\n }}\n className=\"p-1.5 rounded-md hover:bg-bg-elevated text-text-secondary transition-colors\"\n title={tr('vibecontrols.actions.card.duplicate', 'Duplicate')}\n >\n <Copy size={14} />\n </button>\n {canDelete && (\n <button\n type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n onDelete(action.id);\n }}\n className=\"p-1.5 rounded-md hover:bg-status-error-bg/20 text-status-error-text transition-colors\"\n title={tr('vibecontrols.actions.card.delete', 'Delete')}\n >\n <Trash2 size={14} />\n </button>\n )}\n </div>\n </div>\n\n {/* Badges */}\n <div className=\"flex flex-wrap items-center gap-1.5 mb-3\">\n <ActionTypeBadge type={action.actionType} />\n <TriggerTypeBadge type={action.triggerType} />\n {action.sourceType && <SourceTypeBadge type={action.sourceType} />}\n </div>\n\n {/* Category and Tags */}\n {(action.category || (action.tags && action.tags.length > 0)) && (\n <div className=\"flex flex-wrap items-center gap-1.5 mb-3\">\n {action.category && (\n <span className=\"text-xs px-1.5 py-0.5 rounded bg-bg-sunken text-text-secondary\">\n {action.category}\n </span>\n )}\n {action.tags?.map((tag) => (\n <span\n key={tag}\n className=\"text-xs px-1.5 py-0.5 rounded bg-bg-sunken text-text-placeholder\"\n >\n #{tag}\n </span>\n ))}\n </div>\n )}\n\n {/* Stats */}\n <div className=\"flex items-center gap-4 text-xs text-text-placeholder\">\n <span className=\"inline-flex items-center gap-1\">\n <Hash size={12} />\n {tr('vibecontrols.actions.card.executionRuns', '{{count}} runs', {\n count: action.executionCount ?? 0,\n })}\n </span>\n <span className=\"inline-flex items-center gap-1\">\n <Clock size={12} />\n {formatDate(action.lastExecutedAt)}\n </span>\n </div>\n </motion.div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActionForm.js","names":[],"sources":["../../../src/components/actions/ActionForm.tsx"],"sourcesContent":["import { useState, useEffect, useRef, type ReactNode } from 'react';\nimport { X } from 'lucide-react';\nimport { motion, AnimatePresence, useReducedMotion } from 'framer-motion';\nimport { useTr } from '../../shared/hooks/useTr';\n\nexport interface ActionFormData {\n name: string;\n displayName: string;\n description: string;\n actionType: string;\n triggerType: string;\n schedule: string;\n category: string;\n tags: string[];\n command: string;\n workingDirectory: string;\n targetAgentId: string;\n environmentVariables: Record<string, string>;\n script: string;\n scriptInterpreter: string;\n webhookUrl: string;\n webhookMethod: string;\n webhookHeaders: Record<string, string>;\n webhookBody: string;\n webhookAuthType: string;\n timeoutMs: number;\n retryCount: number;\n confirmationRequired: boolean;\n}\n\nconst DEFAULT_FORM: ActionFormData = {\n name: '',\n displayName: '',\n description: '',\n actionType: 'COMMAND',\n triggerType: 'MANUAL',\n schedule: '',\n category: '',\n tags: [],\n command: '',\n workingDirectory: '',\n targetAgentId: '',\n environmentVariables: {},\n script: '',\n scriptInterpreter: 'bash',\n webhookUrl: '',\n webhookMethod: 'POST',\n webhookHeaders: {},\n webhookBody: '',\n webhookAuthType: '',\n timeoutMs: 30000,\n retryCount: 0,\n confirmationRequired: false,\n};\n\ninterface ActionFormProps {\n title: string;\n initialData?: Partial<ActionFormData>;\n agents: Array<{ id: string; name: string; isActive: boolean }>;\n onSubmit: (data: ActionFormData) => void;\n onCancel: () => void;\n isLoading?: boolean;\n /** Optional extra content rendered below the form fields (e.g. tag picker in edit mode) */\n extraSection?: ReactNode;\n}\n\nexport function ActionForm({\n title,\n initialData,\n agents,\n onSubmit,\n onCancel,\n isLoading = false,\n extraSection,\n}: ActionFormProps) {\n const [form, setForm] = useState<ActionFormData>({ ...DEFAULT_FORM, ...initialData });\n const [tagsInput, setTagsInput] = useState(initialData?.tags?.join(', ') ?? '');\n const dialogRef = useRef<HTMLDialogElement>(null);\n const shouldReduceMotion = useReducedMotion();\n const tr = useTr();\n\n useEffect(() => {\n dialogRef.current?.showModal();\n }, []);\n\n const update = <K extends keyof ActionFormData>(key: K, value: ActionFormData[K]) =>\n setForm((prev) => ({ ...prev, [key]: value }));\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n const tags = tagsInput\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean);\n onSubmit({ ...form, tags });\n };\n\n const inputClass =\n 'w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg focus:border-transparent transition-all';\n const labelClass = 'block text-sm font-medium text-text-secondary mb-1';\n\n return (\n <dialog\n ref={dialogRef}\n className=\"fixed inset-0 z-50 m-auto p-0 bg-transparent backdrop:bg-transparent\"\n onClose={onCancel}\n >\n <AnimatePresence>\n <motion.div\n className=\"fixed inset-0 bg-bg-overlay\"\n initial={shouldReduceMotion ? undefined : { opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n onClick={onCancel}\n />\n <motion.div\n className=\"relative w-full max-w-2xl max-h-[85vh] overflow-y-auto bg-bg-surface rounded-lg shadow-xl\"\n initial={shouldReduceMotion ? undefined : { opacity: 0, scale: 0.95, y: 8 }}\n animate={{ opacity: 1, scale: 1, y: 0 }}\n exit={{ opacity: 0, scale: 0.95, y: 8 }}\n transition={{ duration: 0.2, ease: [0.2, 0, 0, 1] }}\n >\n {/* Header */}\n <div className=\"flex items-center justify-between p-6 border-b border-border-default sticky top-0 bg-bg-surface z-10\">\n <h2 className=\"text-lg font-semibold text-text-primary\">{title}</h2>\n <button type=\"button\" onClick={onCancel} className=\"p-1 rounded hover:bg-bg-sunken transition-colors\">\n <X className=\"size-5 text-text-secondary\" />\n </button>\n </div>\n\n <form onSubmit={handleSubmit} className=\"p-6 space-y-4\">\n {/* Basic fields */}\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.nameSlug', 'Name (slug)')}\n </label>\n <input aria-label=\"my-action\"\n className={inputClass}\n value={form.name}\n onChange={(e) => update('name', e.target.value)}\n placeholder=\"my-action\"\n required\n />\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.displayName', 'Display Name')}\n </label>\n <input aria-label=\"My Action\"\n className={inputClass}\n value={form.displayName}\n onChange={(e) => update('displayName', e.target.value)}\n placeholder=\"My Action\"\n required\n />\n </div>\n </div>\n\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.description', 'Description')}\n </label>\n <textarea aria-label=\"What does this action do?\"\n className={`${inputClass} resize-none`}\n rows={2}\n value={form.description}\n onChange={(e) => update('description', e.target.value)}\n placeholder=\"What does this action do?\"\n />\n </div>\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.actionType', 'Action Type')}\n </label>\n <select\n className={inputClass}\n value={form.actionType}\n onChange={(e) => update('actionType', e.target.value)}\n >\n <option value=\"COMMAND\">Command</option>\n <option value=\"SCRIPT\">Script</option>\n <option value=\"WEBHOOK\">Webhook</option>\n <option value=\"WORKFLOW\">Workflow</option>\n </select>\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.triggerType', 'Trigger Type')}\n </label>\n <select\n className={inputClass}\n value={form.triggerType}\n onChange={(e) => update('triggerType', e.target.value)}\n >\n <option value=\"MANUAL\">Manual</option>\n <option value=\"SCHEDULED\">Scheduled</option>\n <option value=\"EVENT\">Event</option>\n <option value=\"ON_DEMAND\">On Demand</option>\n </select>\n </div>\n </div>\n\n {form.triggerType === 'SCHEDULED' && (\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.schedule', 'Schedule (cron expression)')}\n </label>\n <input aria-label=\"*/5 * * * *\"\n className={inputClass}\n value={form.schedule}\n onChange={(e) => update('schedule', e.target.value)}\n placeholder=\"*/5 * * * *\"\n />\n </div>\n )}\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.category', 'Category')}\n </label>\n <input aria-label=\"deployment, testing, etc.\"\n className={inputClass}\n value={form.category}\n onChange={(e) => update('category', e.target.value)}\n placeholder=\"deployment, testing, etc.\"\n />\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.tags', 'Tags (comma separated)')}\n </label>\n <input aria-label=\"ci, deploy, infra\"\n className={inputClass}\n value={tagsInput}\n onChange={(e) => setTagsInput(e.target.value)}\n placeholder=\"ci, deploy, infra\"\n />\n </div>\n </div>\n\n {/* Type-specific fields */}\n {form.actionType === 'COMMAND' && (\n <div className=\"space-y-4 p-4 rounded-lg bg-bg-sunken/50 border border-border-default\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tr('vibecontrols.actions.form.commandSettings', 'Command Settings')}\n </h3>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.command', 'Command')}\n </label>\n <textarea aria-label=\"echo 'Hello World'\"\n className={`${inputClass} font-mono resize-none`}\n rows={3}\n value={form.command}\n onChange={(e) => update('command', e.target.value)}\n placeholder=\"echo 'Hello World'\"\n required\n />\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.workingDirectory', 'Working Directory')}\n </label>\n <input aria-label=\"/home/user/project\"\n className={inputClass}\n value={form.workingDirectory}\n onChange={(e) => update('workingDirectory', e.target.value)}\n placeholder=\"/home/user/project\"\n />\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.targetAgent', 'Target Agent')}\n </label>\n <select\n className={inputClass}\n value={form.targetAgentId}\n onChange={(e) => update('targetAgentId', e.target.value)}\n >\n <option value=\"\">\n {tr('vibecontrols.actions.form.anyAvailableAgent', 'Any available agent')}\n </option>\n {agents.map((a) => (\n <option key={a.id} value={a.id} disabled={!a.isActive}>\n {a.name}{' '}\n {!a.isActive ? tr('vibecontrols.actions.form.offline', '(offline)') : ''}\n </option>\n ))}\n </select>\n </div>\n </div>\n </div>\n )}\n\n {form.actionType === 'SCRIPT' && (\n <div className=\"space-y-4 p-4 rounded-lg bg-bg-sunken/50 border border-border-default\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tr('vibecontrols.actions.form.scriptSettings', 'Script Settings')}\n </h3>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.script', 'Script')}\n </label>\n <textarea aria-label=\"#!/bin/bash echo 'Hello'\"\n className={`${inputClass} font-mono resize-none`}\n rows={6}\n value={form.script}\n onChange={(e) => update('script', e.target.value)}\n placeholder=\"#!/bin/bash echo 'Hello'\"\n required\n />\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.interpreter', 'Interpreter')}\n </label>\n <select\n className={inputClass}\n value={form.scriptInterpreter}\n onChange={(e) => update('scriptInterpreter', e.target.value)}\n >\n <option value=\"bash\">Bash</option>\n <option value=\"python\">Python</option>\n <option value=\"node\">Node.js</option>\n </select>\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.targetAgent', 'Target Agent')}\n </label>\n <select\n className={inputClass}\n value={form.targetAgentId}\n onChange={(e) => update('targetAgentId', e.target.value)}\n >\n <option value=\"\">\n {tr('vibecontrols.actions.form.anyAvailableAgent', 'Any available agent')}\n </option>\n {agents.map((a) => (\n <option key={a.id} value={a.id} disabled={!a.isActive}>\n {a.name}{' '}\n {!a.isActive ? tr('vibecontrols.actions.form.offline', '(offline)') : ''}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.workingDirectory', 'Working Directory')}\n </label>\n <input aria-label=\"/home/user/project\"\n className={inputClass}\n value={form.workingDirectory}\n onChange={(e) => update('workingDirectory', e.target.value)}\n placeholder=\"/home/user/project\"\n />\n </div>\n </div>\n )}\n\n {form.actionType === 'WEBHOOK' && (\n <div className=\"space-y-4 p-4 rounded-lg bg-bg-sunken/50 border border-border-default\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tr('vibecontrols.actions.form.webhookSettings', 'Webhook Settings')}\n </h3>\n <div className=\"grid grid-cols-[1fr_auto] gap-4\">\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.url', 'URL')}\n </label>\n <input aria-label=\"https://api.example.com/hook\"\n className={inputClass}\n value={form.webhookUrl}\n onChange={(e) => update('webhookUrl', e.target.value)}\n placeholder=\"https://api.example.com/hook\"\n required\n />\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.method', 'Method')}\n </label>\n <select\n className={inputClass}\n value={form.webhookMethod}\n onChange={(e) => update('webhookMethod', e.target.value)}\n >\n <option value=\"GET\">GET</option>\n <option value=\"POST\">POST</option>\n <option value=\"PUT\">PUT</option>\n <option value=\"PATCH\">PATCH</option>\n <option value=\"DELETE\">DELETE</option>\n </select>\n </div>\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.bodyJson', 'Body (JSON)')}\n </label>\n <textarea aria-label=\"control\"\n className={`${inputClass} font-mono resize-none`}\n rows={4}\n value={form.webhookBody}\n onChange={(e) => update('webhookBody', e.target.value)}\n placeholder='{\"key\": \"value\"}'\n />\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.authType', 'Auth Type')}\n </label>\n <select\n className={inputClass}\n value={form.webhookAuthType}\n onChange={(e) => update('webhookAuthType', e.target.value)}\n >\n <option value=\"\">None</option>\n <option value=\"BEARER\">Bearer Token</option>\n <option value=\"BASIC\">Basic Auth</option>\n <option value=\"API_KEY\">API Key</option>\n </select>\n </div>\n </div>\n )}\n\n {/* Options */}\n <div className=\"grid grid-cols-3 gap-4\">\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.timeout', 'Timeout (ms)')}\n </label>\n <input aria-label=\"control\"\n className={inputClass}\n type=\"number\"\n min={1000}\n max={300000}\n value={form.timeoutMs}\n onChange={(e) => update('timeoutMs', parseInt(e.target.value) || 30000)}\n />\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.retryCount', 'Retry Count')}\n </label>\n <input aria-label=\"control\"\n className={inputClass}\n type=\"number\"\n min={0}\n max={10}\n value={form.retryCount}\n onChange={(e) => update('retryCount', parseInt(e.target.value) || 0)}\n />\n </div>\n <div className=\"flex items-end pb-2\">\n <label className=\"flex items-center gap-2 text-sm text-text-secondary cursor-pointer\">\n <input aria-label=\"control\"\n type=\"checkbox\"\n checked={form.confirmationRequired}\n onChange={(e) => update('confirmationRequired', e.target.checked)}\n className=\"rounded border-border-default\"\n />\n {tr('vibecontrols.actions.form.requireConfirmation', 'Require confirmation')}\n </label>\n </div>\n </div>\n\n {extraSection && (\n <div className=\"pt-4 border-t border-border-default\">{extraSection}</div>\n )}\n\n {/* Footer */}\n <div className=\"flex items-center justify-end gap-3 pt-4 border-t border-border-default\">\n <button\n type=\"button\"\n onClick={onCancel}\n disabled={isLoading}\n className=\"px-4 py-2 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-elevated transition-colors disabled:opacity-50\"\n >\n {tr('vibecontrols.actions.form.cancel', 'Cancel')}\n </button>\n <button\n type=\"submit\"\n disabled={isLoading}\n className=\"px-4 py-2 text-sm font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n {isLoading\n ? tr('vibecontrols.actions.form.saving', 'Saving...')\n : initialData\n ? tr('vibecontrols.actions.form.updateAction', 'Update Action')\n : tr('vibecontrols.actions.form.createAction', 'Create Action')}\n </button>\n </div>\n </form>\n </motion.div>\n </AnimatePresence>\n </dialog>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ActionForm.js","names":[],"sources":["../../../src/components/actions/ActionForm.tsx"],"sourcesContent":["import { useState, useEffect, useRef, type ReactNode } from 'react';\nimport { X } from 'lucide-react';\nimport { motion, AnimatePresence, useReducedMotion } from 'framer-motion';\nimport { useTr } from '../../shared/hooks/useTr';\n\nexport interface ActionFormData {\n name: string;\n displayName: string;\n description: string;\n actionType: string;\n triggerType: string;\n schedule: string;\n category: string;\n tags: string[];\n command: string;\n workingDirectory: string;\n targetAgentId: string;\n environmentVariables: Record<string, string>;\n script: string;\n scriptInterpreter: string;\n webhookUrl: string;\n webhookMethod: string;\n webhookHeaders: Record<string, string>;\n webhookBody: string;\n webhookAuthType: string;\n timeoutMs: number;\n retryCount: number;\n confirmationRequired: boolean;\n}\n\nconst DEFAULT_FORM: ActionFormData = {\n name: '',\n displayName: '',\n description: '',\n actionType: 'COMMAND',\n triggerType: 'MANUAL',\n schedule: '',\n category: '',\n tags: [],\n command: '',\n workingDirectory: '',\n targetAgentId: '',\n environmentVariables: {},\n script: '',\n scriptInterpreter: 'bash',\n webhookUrl: '',\n webhookMethod: 'POST',\n webhookHeaders: {},\n webhookBody: '',\n webhookAuthType: '',\n timeoutMs: 30000,\n retryCount: 0,\n confirmationRequired: false,\n};\n\ninterface ActionFormProps {\n title: string;\n initialData?: Partial<ActionFormData>;\n agents: Array<{ id: string; name: string; isActive: boolean }>;\n onSubmit: (data: ActionFormData) => void;\n onCancel: () => void;\n isLoading?: boolean;\n /** Optional extra content rendered below the form fields (e.g. tag picker in edit mode) */\n extraSection?: ReactNode;\n}\n\nexport function ActionForm({\n title,\n initialData,\n agents,\n onSubmit,\n onCancel,\n isLoading = false,\n extraSection,\n}: ActionFormProps) {\n const [form, setForm] = useState<ActionFormData>({ ...DEFAULT_FORM, ...initialData });\n const [tagsInput, setTagsInput] = useState(initialData?.tags?.join(', ') ?? '');\n const dialogRef = useRef<HTMLDialogElement>(null);\n const shouldReduceMotion = useReducedMotion();\n const tr = useTr();\n\n useEffect(() => {\n dialogRef.current?.showModal();\n }, []);\n\n const update = <K extends keyof ActionFormData>(key: K, value: ActionFormData[K]) =>\n setForm((prev) => ({ ...prev, [key]: value }));\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n const tags = tagsInput\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean);\n onSubmit({ ...form, tags });\n };\n\n const inputClass =\n 'w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg focus:border-transparent transition-all';\n const labelClass = 'block text-sm font-medium text-text-secondary mb-1';\n\n return (\n <dialog\n ref={dialogRef}\n className=\"fixed inset-0 z-50 m-auto p-0 bg-transparent backdrop:bg-transparent\"\n onClose={onCancel}\n >\n <AnimatePresence>\n <motion.div\n className=\"fixed inset-0 bg-bg-overlay\"\n initial={shouldReduceMotion ? undefined : { opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n onClick={onCancel}\n />\n <motion.div\n className=\"relative w-full max-w-2xl max-h-[85vh] overflow-y-auto bg-bg-surface rounded-lg shadow-xl\"\n initial={shouldReduceMotion ? undefined : { opacity: 0, scale: 0.95, y: 8 }}\n animate={{ opacity: 1, scale: 1, y: 0 }}\n exit={{ opacity: 0, scale: 0.95, y: 8 }}\n transition={{ duration: 0.2, ease: [0.2, 0, 0, 1] }}\n >\n {/* Header */}\n <div className=\"flex items-center justify-between p-6 border-b border-border-default sticky top-0 bg-bg-surface z-10\">\n <h2 className=\"text-lg font-semibold text-text-primary\">{title}</h2>\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n >\n <X className=\"size-5 text-text-secondary\" />\n </button>\n </div>\n\n <form onSubmit={handleSubmit} className=\"p-6 space-y-4\">\n {/* Basic fields */}\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.nameSlug', 'Name (slug)')}\n </label>\n <input\n aria-label=\"my-action\"\n className={inputClass}\n value={form.name}\n onChange={(e) => update('name', e.target.value)}\n placeholder=\"my-action\"\n required\n />\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.displayName', 'Display Name')}\n </label>\n <input\n aria-label=\"My Action\"\n className={inputClass}\n value={form.displayName}\n onChange={(e) => update('displayName', e.target.value)}\n placeholder=\"My Action\"\n required\n />\n </div>\n </div>\n\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.description', 'Description')}\n </label>\n <textarea\n aria-label=\"What does this action do?\"\n className={`${inputClass} resize-none`}\n rows={2}\n value={form.description}\n onChange={(e) => update('description', e.target.value)}\n placeholder=\"What does this action do?\"\n />\n </div>\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.actionType', 'Action Type')}\n </label>\n <select\n className={inputClass}\n value={form.actionType}\n onChange={(e) => update('actionType', e.target.value)}\n >\n <option value=\"COMMAND\">Command</option>\n <option value=\"SCRIPT\">Script</option>\n <option value=\"WEBHOOK\">Webhook</option>\n <option value=\"WORKFLOW\">Workflow</option>\n </select>\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.triggerType', 'Trigger Type')}\n </label>\n <select\n className={inputClass}\n value={form.triggerType}\n onChange={(e) => update('triggerType', e.target.value)}\n >\n <option value=\"MANUAL\">Manual</option>\n <option value=\"SCHEDULED\">Scheduled</option>\n <option value=\"EVENT\">Event</option>\n <option value=\"ON_DEMAND\">On Demand</option>\n </select>\n </div>\n </div>\n\n {form.triggerType === 'SCHEDULED' && (\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.schedule', 'Schedule (cron expression)')}\n </label>\n <input\n aria-label=\"*/5 * * * *\"\n className={inputClass}\n value={form.schedule}\n onChange={(e) => update('schedule', e.target.value)}\n placeholder=\"*/5 * * * *\"\n />\n </div>\n )}\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.category', 'Category')}\n </label>\n <input\n aria-label=\"deployment, testing, etc.\"\n className={inputClass}\n value={form.category}\n onChange={(e) => update('category', e.target.value)}\n placeholder=\"deployment, testing, etc.\"\n />\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.tags', 'Tags (comma separated)')}\n </label>\n <input\n aria-label=\"ci, deploy, infra\"\n className={inputClass}\n value={tagsInput}\n onChange={(e) => setTagsInput(e.target.value)}\n placeholder=\"ci, deploy, infra\"\n />\n </div>\n </div>\n\n {/* Type-specific fields */}\n {form.actionType === 'COMMAND' && (\n <div className=\"space-y-4 p-4 rounded-lg bg-bg-sunken/50 border border-border-default\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tr('vibecontrols.actions.form.commandSettings', 'Command Settings')}\n </h3>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.command', 'Command')}\n </label>\n <textarea\n aria-label=\"echo 'Hello World'\"\n className={`${inputClass} font-mono resize-none`}\n rows={3}\n value={form.command}\n onChange={(e) => update('command', e.target.value)}\n placeholder=\"echo 'Hello World'\"\n required\n />\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.workingDirectory', 'Working Directory')}\n </label>\n <input\n aria-label=\"/home/user/project\"\n className={inputClass}\n value={form.workingDirectory}\n onChange={(e) => update('workingDirectory', e.target.value)}\n placeholder=\"/home/user/project\"\n />\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.targetAgent', 'Target Agent')}\n </label>\n <select\n className={inputClass}\n value={form.targetAgentId}\n onChange={(e) => update('targetAgentId', e.target.value)}\n >\n <option value=\"\">\n {tr('vibecontrols.actions.form.anyAvailableAgent', 'Any available agent')}\n </option>\n {agents.map((a) => (\n <option key={a.id} value={a.id} disabled={!a.isActive}>\n {a.name}{' '}\n {!a.isActive ? tr('vibecontrols.actions.form.offline', '(offline)') : ''}\n </option>\n ))}\n </select>\n </div>\n </div>\n </div>\n )}\n\n {form.actionType === 'SCRIPT' && (\n <div className=\"space-y-4 p-4 rounded-lg bg-bg-sunken/50 border border-border-default\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tr('vibecontrols.actions.form.scriptSettings', 'Script Settings')}\n </h3>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.script', 'Script')}\n </label>\n <textarea\n aria-label=\"#!/bin/bash echo 'Hello'\"\n className={`${inputClass} font-mono resize-none`}\n rows={6}\n value={form.script}\n onChange={(e) => update('script', e.target.value)}\n placeholder=\"#!/bin/bash echo 'Hello'\"\n required\n />\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.interpreter', 'Interpreter')}\n </label>\n <select\n className={inputClass}\n value={form.scriptInterpreter}\n onChange={(e) => update('scriptInterpreter', e.target.value)}\n >\n <option value=\"bash\">Bash</option>\n <option value=\"python\">Python</option>\n <option value=\"node\">Node.js</option>\n </select>\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.targetAgent', 'Target Agent')}\n </label>\n <select\n className={inputClass}\n value={form.targetAgentId}\n onChange={(e) => update('targetAgentId', e.target.value)}\n >\n <option value=\"\">\n {tr('vibecontrols.actions.form.anyAvailableAgent', 'Any available agent')}\n </option>\n {agents.map((a) => (\n <option key={a.id} value={a.id} disabled={!a.isActive}>\n {a.name}{' '}\n {!a.isActive ? tr('vibecontrols.actions.form.offline', '(offline)') : ''}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.workingDirectory', 'Working Directory')}\n </label>\n <input\n aria-label=\"/home/user/project\"\n className={inputClass}\n value={form.workingDirectory}\n onChange={(e) => update('workingDirectory', e.target.value)}\n placeholder=\"/home/user/project\"\n />\n </div>\n </div>\n )}\n\n {form.actionType === 'WEBHOOK' && (\n <div className=\"space-y-4 p-4 rounded-lg bg-bg-sunken/50 border border-border-default\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tr('vibecontrols.actions.form.webhookSettings', 'Webhook Settings')}\n </h3>\n <div className=\"grid grid-cols-[1fr_auto] gap-4\">\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.url', 'URL')}\n </label>\n <input\n aria-label=\"https://api.example.com/hook\"\n className={inputClass}\n value={form.webhookUrl}\n onChange={(e) => update('webhookUrl', e.target.value)}\n placeholder=\"https://api.example.com/hook\"\n required\n />\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.method', 'Method')}\n </label>\n <select\n className={inputClass}\n value={form.webhookMethod}\n onChange={(e) => update('webhookMethod', e.target.value)}\n >\n <option value=\"GET\">GET</option>\n <option value=\"POST\">POST</option>\n <option value=\"PUT\">PUT</option>\n <option value=\"PATCH\">PATCH</option>\n <option value=\"DELETE\">DELETE</option>\n </select>\n </div>\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.bodyJson', 'Body (JSON)')}\n </label>\n <textarea\n aria-label=\"control\"\n className={`${inputClass} font-mono resize-none`}\n rows={4}\n value={form.webhookBody}\n onChange={(e) => update('webhookBody', e.target.value)}\n placeholder='{\"key\": \"value\"}'\n />\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.authType', 'Auth Type')}\n </label>\n <select\n className={inputClass}\n value={form.webhookAuthType}\n onChange={(e) => update('webhookAuthType', e.target.value)}\n >\n <option value=\"\">None</option>\n <option value=\"BEARER\">Bearer Token</option>\n <option value=\"BASIC\">Basic Auth</option>\n <option value=\"API_KEY\">API Key</option>\n </select>\n </div>\n </div>\n )}\n\n {/* Options */}\n <div className=\"grid grid-cols-3 gap-4\">\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.timeout', 'Timeout (ms)')}\n </label>\n <input\n aria-label=\"control\"\n className={inputClass}\n type=\"number\"\n min={1000}\n max={300000}\n value={form.timeoutMs}\n onChange={(e) => update('timeoutMs', parseInt(e.target.value) || 30000)}\n />\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.retryCount', 'Retry Count')}\n </label>\n <input\n aria-label=\"control\"\n className={inputClass}\n type=\"number\"\n min={0}\n max={10}\n value={form.retryCount}\n onChange={(e) => update('retryCount', parseInt(e.target.value) || 0)}\n />\n </div>\n <div className=\"flex items-end pb-2\">\n <label className=\"flex items-center gap-2 text-sm text-text-secondary cursor-pointer\">\n <input\n aria-label=\"control\"\n type=\"checkbox\"\n checked={form.confirmationRequired}\n onChange={(e) => update('confirmationRequired', e.target.checked)}\n className=\"rounded border-border-default\"\n />\n {tr('vibecontrols.actions.form.requireConfirmation', 'Require confirmation')}\n </label>\n </div>\n </div>\n\n {extraSection && (\n <div className=\"pt-4 border-t border-border-default\">{extraSection}</div>\n )}\n\n {/* Footer */}\n <div className=\"flex items-center justify-end gap-3 pt-4 border-t border-border-default\">\n <button\n type=\"button\"\n onClick={onCancel}\n disabled={isLoading}\n className=\"px-4 py-2 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-elevated transition-colors disabled:opacity-50\"\n >\n {tr('vibecontrols.actions.form.cancel', 'Cancel')}\n </button>\n <button\n type=\"submit\"\n disabled={isLoading}\n className=\"px-4 py-2 text-sm font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n {isLoading\n ? tr('vibecontrols.actions.form.saving', 'Saving...')\n : initialData\n ? tr('vibecontrols.actions.form.updateAction', 'Update Action')\n : tr('vibecontrols.actions.form.createAction', 'Create Action')}\n </button>\n </div>\n </form>\n </motion.div>\n </AnimatePresence>\n </dialog>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActionSchedulesList.js","names":[],"sources":["../../../src/components/actions/ActionSchedulesList.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { AlarmClock, ArrowUpRight, Plus } from 'lucide-react';\nimport { motion } from 'framer-motion';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useGetScheduledJobsForCalendarQuery } from '@/generated/wspace-operations';\nimport type { GetScheduledJobsForCalendarQuery } from '@/generated/wspace-operations';\nimport type { JobStatus, ScheduleType } from '@/generated/wspace-types';\nimport { useTr } from '@/shared/hooks/useTr';\n\ntype ScheduledJobItem = NonNullable<\n GetScheduledJobsForCalendarQuery['scheduledJobs']['jobs']\n>[number];\n\ninterface ActionSchedulesListProps {\n actionId: string;\n workspaceId: string;\n actionName: string;\n onClose: () => void;\n}\n\nconst STATUS_LABEL: Record<string, string> = {\n ACTIVE: 'active',\n PENDING: 'pending',\n PAUSED: 'paused',\n COMPLETED: 'completed',\n FAILED: 'failed',\n CANCELLED: 'cancelled',\n DISABLED: 'disabled',\n ARCHIVED: 'archived',\n DRAFT: 'draft',\n};\n\nconst STATUS_TONE: Record<string, string> = {\n ACTIVE: 'text-status-success-text',\n PENDING: 'text-status-info-text',\n PAUSED: 'text-text-secondary',\n COMPLETED: 'text-status-success-text',\n FAILED: 'text-status-error-text',\n CANCELLED: 'text-text-placeholder',\n DISABLED: 'text-text-placeholder',\n ARCHIVED: 'text-text-placeholder',\n DRAFT: 'text-text-secondary',\n};\n\nconst SCHEDULE_LABEL: Record<ScheduleType | string, string> = {\n CRON: 'Cron',\n RRULE: 'Recurrence',\n ONE_TIME: 'One-time',\n FIXED_INTERVAL: 'Interval',\n WINDOWED: 'Windowed',\n};\n\nfunction makeFormatTime(locale: string | undefined) {\n return (dateStr: string | null | undefined): string => {\n if (!dateStr) return '—';\n // Pass the app locale through so the displayed format matches\n // whatever the user picked in the shell, not the browser default.\n return new Date(dateStr).toLocaleString(locale || undefined);\n };\n}\n\nfunction makeDescribeSchedule(formatTime: (d: string | null | undefined) => string) {\n return (job: {\n scheduleType: ScheduleType;\n cronExpression?: string | null;\n scheduledAt?: string | null;\n }): string => {\n if (job.scheduleType === 'CRON' && job.cronExpression) return job.cronExpression;\n if (job.scheduleType === 'ONE_TIME' && job.scheduledAt) return formatTime(job.scheduledAt);\n return SCHEDULE_LABEL[job.scheduleType] ?? job.scheduleType;\n };\n}\n\nexport function ActionSchedulesList({\n actionId,\n workspaceId,\n actionName,\n onClose,\n}: ActionSchedulesListProps) {\n const tr = useTr();\n const { locale } = useI18n();\n const navigate = useNavigate();\n const formatTime = useMemo(() => makeFormatTime(locale), [locale]);\n const describeSchedule = useMemo(() => makeDescribeSchedule(formatTime), [formatTime]);\n\n // The scheduler's `JobFilterInput` doesn't support `actionId` today, so we\n // page through up to `pageSize` jobs and filter client-side. The action page\n // is unlikely to need more than a handful of schedules per action; if a\n // workspace ever scales past this, push `actionId` into JobFilterInput.\n const pageSize = 100;\n const { data, loading, error } = useGetScheduledJobsForCalendarQuery({\n variables: { workspaceId, pagination: { first: pageSize } },\n fetchPolicy: 'cache-and-network',\n });\n\n const totalFetched = data?.scheduledJobs?.jobs?.length ?? 0;\n // The query returns at most `pageSize` jobs and we filter client-side,\n // so if the result set hits the page boundary AND the workspace's\n // overall total exceeds it, schedules for this action could be on a\n // later page we never fetched. Surface a warning rather than silently\n // showing an incomplete list. (Long-term fix: add `actionId` to\n // `JobFilterInput` so the server can return the full result set.)\n const totalAvailable = data?.scheduledJobs?.total ?? totalFetched;\n const mayBeTruncated = totalFetched >= pageSize && totalAvailable > totalFetched;\n\n const schedules = useMemo<ScheduledJobItem[]>(() => {\n const jobs: ScheduledJobItem[] = data?.scheduledJobs?.jobs ?? [];\n return jobs\n .filter((j: ScheduledJobItem) => j.actionId === actionId)\n .filter((j: ScheduledJobItem) => (j.status as JobStatus) !== 'ARCHIVED');\n }, [data, actionId]);\n\n return (\n <motion.div\n className=\"bg-bg-surface border border-border-default rounded-lg overflow-hidden\"\n initial={{ opacity: 0, height: 0 }}\n animate={{ opacity: 1, height: 'auto' }}\n exit={{ opacity: 0, height: 0 }}\n transition={{ duration: 0.2 }}\n >\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-border-default bg-bg-sunken/50\">\n <h3 className=\"text-sm font-semibold text-text-primary inline-flex items-center gap-2\">\n <AlarmClock size={14} className=\"text-text-secondary\" />\n {tr('vibecontrols.actions.schedules.title', 'Schedules')} - {actionName}\n {schedules.length > 0 && (\n <span className=\"text-xs font-normal text-text-placeholder\">({schedules.length})</span>\n )}\n </h3>\n <div className=\"flex items-center gap-3\">\n <button type=\"button\"\n onClick={() => navigate(`/scheduler?actionId=${encodeURIComponent(actionId)}`)}\n className=\"text-xs text-text-secondary hover:text-text-primary transition-colors inline-flex items-center gap-1\"\n >\n <Plus size={12} />\n {tr('vibecontrols.actions.schedules.add', 'New schedule')}\n </button>\n <button type=\"button\"\n onClick={onClose}\n className=\"text-xs text-text-secondary hover:text-text-primary transition-colors\"\n >\n {tr('vibecontrols.actions.schedules.close', 'Close')}\n </button>\n </div>\n </div>\n\n {mayBeTruncated && (\n <div className=\"px-4 py-2 text-xs text-status-warning-text bg-status-warning-surface/40 border-b border-border-default\">\n {tr(\n 'vibecontrols.actions.schedules.truncated',\n `Showing the first ${pageSize} schedules across this workspace; some schedules for this action may not appear here.`\n )}\n </div>\n )}\n\n {loading && schedules.length === 0 ? (\n <div className=\"px-4 py-6 text-center text-sm text-text-secondary\">\n {tr('vibecontrols.actions.schedules.loading', 'Loading schedules…')}\n </div>\n ) : error ? (\n <div className=\"px-4 py-6 text-center text-sm text-status-error-text\">\n {tr('vibecontrols.actions.schedules.error', 'Failed to load schedules')}\n </div>\n ) : schedules.length === 0 ? (\n <div className=\"px-4 py-6 text-center text-sm text-text-secondary\">\n {tr('vibecontrols.actions.schedules.empty', 'No schedules attached to this action')}\n </div>\n ) : (\n <div className=\"divide-y divide-border-default\">\n {schedules.map((job: ScheduledJobItem) => {\n const tone = STATUS_TONE[job.status] ?? 'text-text-secondary';\n const statusKey = STATUS_LABEL[job.status] ?? job.status.toLowerCase();\n return (\n <button type=\"button\"\n key={job.id}\n onClick={() => navigate(`/scheduler/${encodeURIComponent(job.id)}`)}\n className=\"w-full text-left px-4 py-3 hover:bg-bg-sunken/30 transition-colors flex items-center justify-between gap-3\"\n >\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium text-text-primary truncate\">\n {job.name || job.id}\n </span>\n <span className={`text-xs capitalize ${tone}`}>{statusKey}</span>\n </div>\n <div className=\"text-xs text-text-placeholder mt-0.5 truncate\">\n {describeSchedule(job)} · next {formatTime(job.nextExecutionAt)}\n </div>\n </div>\n <ArrowUpRight size={14} className=\"text-text-placeholder flex-shrink-0\" />\n </button>\n );\n })}\n </div>\n )}\n </motion.div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ActionSchedulesList.js","names":[],"sources":["../../../src/components/actions/ActionSchedulesList.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { AlarmClock, ArrowUpRight, Plus } from 'lucide-react';\nimport { motion } from 'framer-motion';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useGetScheduledJobsForCalendarQuery } from '@/generated/wspace-operations';\nimport type { GetScheduledJobsForCalendarQuery } from '@/generated/wspace-operations';\nimport type { JobStatus, ScheduleType } from '@/generated/wspace-types';\nimport { useTr } from '@/shared/hooks/useTr';\n\ntype ScheduledJobItem = NonNullable<\n GetScheduledJobsForCalendarQuery['scheduledJobs']['jobs']\n>[number];\n\ninterface ActionSchedulesListProps {\n actionId: string;\n workspaceId: string;\n actionName: string;\n onClose: () => void;\n}\n\nconst STATUS_LABEL: Record<string, string> = {\n ACTIVE: 'active',\n PENDING: 'pending',\n PAUSED: 'paused',\n COMPLETED: 'completed',\n FAILED: 'failed',\n CANCELLED: 'cancelled',\n DISABLED: 'disabled',\n ARCHIVED: 'archived',\n DRAFT: 'draft',\n};\n\nconst STATUS_TONE: Record<string, string> = {\n ACTIVE: 'text-status-success-text',\n PENDING: 'text-status-info-text',\n PAUSED: 'text-text-secondary',\n COMPLETED: 'text-status-success-text',\n FAILED: 'text-status-error-text',\n CANCELLED: 'text-text-placeholder',\n DISABLED: 'text-text-placeholder',\n ARCHIVED: 'text-text-placeholder',\n DRAFT: 'text-text-secondary',\n};\n\nconst SCHEDULE_LABEL: Record<ScheduleType | string, string> = {\n CRON: 'Cron',\n RRULE: 'Recurrence',\n ONE_TIME: 'One-time',\n FIXED_INTERVAL: 'Interval',\n WINDOWED: 'Windowed',\n};\n\nfunction makeFormatTime(locale: string | undefined) {\n return (dateStr: string | null | undefined): string => {\n if (!dateStr) return '—';\n // Pass the app locale through so the displayed format matches\n // whatever the user picked in the shell, not the browser default.\n return new Date(dateStr).toLocaleString(locale || undefined);\n };\n}\n\nfunction makeDescribeSchedule(formatTime: (d: string | null | undefined) => string) {\n return (job: {\n scheduleType: ScheduleType;\n cronExpression?: string | null;\n scheduledAt?: string | null;\n }): string => {\n if (job.scheduleType === 'CRON' && job.cronExpression) return job.cronExpression;\n if (job.scheduleType === 'ONE_TIME' && job.scheduledAt) return formatTime(job.scheduledAt);\n return SCHEDULE_LABEL[job.scheduleType] ?? job.scheduleType;\n };\n}\n\nexport function ActionSchedulesList({\n actionId,\n workspaceId,\n actionName,\n onClose,\n}: ActionSchedulesListProps) {\n const tr = useTr();\n const { locale } = useI18n();\n const navigate = useNavigate();\n const formatTime = useMemo(() => makeFormatTime(locale), [locale]);\n const describeSchedule = useMemo(() => makeDescribeSchedule(formatTime), [formatTime]);\n\n // The scheduler's `JobFilterInput` doesn't support `actionId` today, so we\n // page through up to `pageSize` jobs and filter client-side. The action page\n // is unlikely to need more than a handful of schedules per action; if a\n // workspace ever scales past this, push `actionId` into JobFilterInput.\n const pageSize = 100;\n const { data, loading, error } = useGetScheduledJobsForCalendarQuery({\n variables: { workspaceId, pagination: { first: pageSize } },\n fetchPolicy: 'cache-and-network',\n });\n\n const totalFetched = data?.scheduledJobs?.jobs?.length ?? 0;\n // The query returns at most `pageSize` jobs and we filter client-side,\n // so if the result set hits the page boundary AND the workspace's\n // overall total exceeds it, schedules for this action could be on a\n // later page we never fetched. Surface a warning rather than silently\n // showing an incomplete list. (Long-term fix: add `actionId` to\n // `JobFilterInput` so the server can return the full result set.)\n const totalAvailable = data?.scheduledJobs?.total ?? totalFetched;\n const mayBeTruncated = totalFetched >= pageSize && totalAvailable > totalFetched;\n\n const schedules = useMemo<ScheduledJobItem[]>(() => {\n const jobs: ScheduledJobItem[] = data?.scheduledJobs?.jobs ?? [];\n return jobs\n .filter((j: ScheduledJobItem) => j.actionId === actionId)\n .filter((j: ScheduledJobItem) => (j.status as JobStatus) !== 'ARCHIVED');\n }, [data, actionId]);\n\n return (\n <motion.div\n className=\"bg-bg-surface border border-border-default rounded-lg overflow-hidden\"\n initial={{ opacity: 0, height: 0 }}\n animate={{ opacity: 1, height: 'auto' }}\n exit={{ opacity: 0, height: 0 }}\n transition={{ duration: 0.2 }}\n >\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-border-default bg-bg-sunken/50\">\n <h3 className=\"text-sm font-semibold text-text-primary inline-flex items-center gap-2\">\n <AlarmClock size={14} className=\"text-text-secondary\" />\n {tr('vibecontrols.actions.schedules.title', 'Schedules')} - {actionName}\n {schedules.length > 0 && (\n <span className=\"text-xs font-normal text-text-placeholder\">({schedules.length})</span>\n )}\n </h3>\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => navigate(`/scheduler?actionId=${encodeURIComponent(actionId)}`)}\n className=\"text-xs text-text-secondary hover:text-text-primary transition-colors inline-flex items-center gap-1\"\n >\n <Plus size={12} />\n {tr('vibecontrols.actions.schedules.add', 'New schedule')}\n </button>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"text-xs text-text-secondary hover:text-text-primary transition-colors\"\n >\n {tr('vibecontrols.actions.schedules.close', 'Close')}\n </button>\n </div>\n </div>\n\n {mayBeTruncated && (\n <div className=\"px-4 py-2 text-xs text-status-warning-text bg-status-warning-surface/40 border-b border-border-default\">\n {tr(\n 'vibecontrols.actions.schedules.truncated',\n `Showing the first ${pageSize} schedules across this workspace; some schedules for this action may not appear here.`\n )}\n </div>\n )}\n\n {loading && schedules.length === 0 ? (\n <div className=\"px-4 py-6 text-center text-sm text-text-secondary\">\n {tr('vibecontrols.actions.schedules.loading', 'Loading schedules…')}\n </div>\n ) : error ? (\n <div className=\"px-4 py-6 text-center text-sm text-status-error-text\">\n {tr('vibecontrols.actions.schedules.error', 'Failed to load schedules')}\n </div>\n ) : schedules.length === 0 ? (\n <div className=\"px-4 py-6 text-center text-sm text-text-secondary\">\n {tr('vibecontrols.actions.schedules.empty', 'No schedules attached to this action')}\n </div>\n ) : (\n <div className=\"divide-y divide-border-default\">\n {schedules.map((job: ScheduledJobItem) => {\n const tone = STATUS_TONE[job.status] ?? 'text-text-secondary';\n const statusKey = STATUS_LABEL[job.status] ?? job.status.toLowerCase();\n return (\n <button\n type=\"button\"\n key={job.id}\n onClick={() => navigate(`/scheduler/${encodeURIComponent(job.id)}`)}\n className=\"w-full text-left px-4 py-3 hover:bg-bg-sunken/30 transition-colors flex items-center justify-between gap-3\"\n >\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium text-text-primary truncate\">\n {job.name || job.id}\n </span>\n <span className={`text-xs capitalize ${tone}`}>{statusKey}</span>\n </div>\n <div className=\"text-xs text-text-placeholder mt-0.5 truncate\">\n {describeSchedule(job)} · next {formatTime(job.nextExecutionAt)}\n </div>\n </div>\n <ArrowUpRight size={14} className=\"text-text-placeholder flex-shrink-0\" />\n </button>\n );\n })}\n </div>\n )}\n </motion.div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExecutionHistory.js","names":[],"sources":["../../../src/components/actions/ExecutionHistory.tsx"],"sourcesContent":["import { CheckCircle, XCircle, Clock, Loader } from 'lucide-react';\nimport { motion } from 'framer-motion';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface Execution {\n id: string;\n status: string;\n triggerType?: string | null;\n triggerSource?: string | null;\n command?: string | null;\n script?: string | null;\n output?: string | null;\n exitCode?: number | null;\n webhookStatusCode?: number | null;\n errorMessage?: string | null;\n durationMs?: number | null;\n startedAt?: string | null;\n completedAt?: string | null;\n}\n\ninterface ExecutionHistoryProps {\n executions: Execution[];\n onClose: () => void;\n actionName: string;\n}\n\nconst STATUS_ICON: Record<string, typeof CheckCircle> = {\n SUCCESS: CheckCircle,\n COMPLETED: CheckCircle,\n FAILED: XCircle,\n ERROR: XCircle,\n RUNNING: Loader,\n PENDING: Clock,\n};\n\nconst STATUS_COLOR: Record<string, string> = {\n SUCCESS: 'text-status-success-text',\n COMPLETED: 'text-status-success-text',\n FAILED: 'text-status-error-text',\n ERROR: 'text-status-error-text',\n RUNNING: 'text-status-info-text',\n PENDING: 'text-text-secondary',\n};\n\nexport function ExecutionHistory({ executions, onClose, actionName }: ExecutionHistoryProps) {\n const tr = useTr();\n\n const formatDuration = (ms: number | null | undefined) => {\n if (!ms) return '-';\n if (ms < 1000) return `${ms}ms`;\n if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;\n return `${(ms / 60000).toFixed(1)}m`;\n };\n\n const formatTime = (dateStr: string | null | undefined) => {\n if (!dateStr) return '-';\n return new Date(dateStr).toLocaleString();\n };\n\n return (\n <motion.div\n className=\"bg-bg-surface border border-border-default rounded-lg overflow-hidden\"\n initial={{ opacity: 0, height: 0 }}\n animate={{ opacity: 1, height: 'auto' }}\n exit={{ opacity: 0, height: 0 }}\n transition={{ duration: 0.2 }}\n >\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-border-default bg-bg-sunken/50\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tr('vibecontrols.actions.executionHistory.recentExecutions', 'Recent Executions')} -
|
|
1
|
+
{"version":3,"file":"ExecutionHistory.js","names":[],"sources":["../../../src/components/actions/ExecutionHistory.tsx"],"sourcesContent":["import { CheckCircle, XCircle, Clock, Loader } from 'lucide-react';\nimport { motion } from 'framer-motion';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface Execution {\n id: string;\n status: string;\n triggerType?: string | null;\n triggerSource?: string | null;\n command?: string | null;\n script?: string | null;\n output?: string | null;\n exitCode?: number | null;\n webhookStatusCode?: number | null;\n errorMessage?: string | null;\n durationMs?: number | null;\n startedAt?: string | null;\n completedAt?: string | null;\n}\n\ninterface ExecutionHistoryProps {\n executions: Execution[];\n onClose: () => void;\n actionName: string;\n}\n\nconst STATUS_ICON: Record<string, typeof CheckCircle> = {\n SUCCESS: CheckCircle,\n COMPLETED: CheckCircle,\n FAILED: XCircle,\n ERROR: XCircle,\n RUNNING: Loader,\n PENDING: Clock,\n};\n\nconst STATUS_COLOR: Record<string, string> = {\n SUCCESS: 'text-status-success-text',\n COMPLETED: 'text-status-success-text',\n FAILED: 'text-status-error-text',\n ERROR: 'text-status-error-text',\n RUNNING: 'text-status-info-text',\n PENDING: 'text-text-secondary',\n};\n\nexport function ExecutionHistory({ executions, onClose, actionName }: ExecutionHistoryProps) {\n const tr = useTr();\n\n const formatDuration = (ms: number | null | undefined) => {\n if (!ms) return '-';\n if (ms < 1000) return `${ms}ms`;\n if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;\n return `${(ms / 60000).toFixed(1)}m`;\n };\n\n const formatTime = (dateStr: string | null | undefined) => {\n if (!dateStr) return '-';\n return new Date(dateStr).toLocaleString();\n };\n\n return (\n <motion.div\n className=\"bg-bg-surface border border-border-default rounded-lg overflow-hidden\"\n initial={{ opacity: 0, height: 0 }}\n animate={{ opacity: 1, height: 'auto' }}\n exit={{ opacity: 0, height: 0 }}\n transition={{ duration: 0.2 }}\n >\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-border-default bg-bg-sunken/50\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tr('vibecontrols.actions.executionHistory.recentExecutions', 'Recent Executions')} -{' '}\n {actionName}\n </h3>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"text-xs text-text-secondary hover:text-text-primary transition-colors\"\n >\n {tr('vibecontrols.actions.executionHistory.close', 'Close')}\n </button>\n </div>\n\n {executions.length === 0 ? (\n <div className=\"px-4 py-6 text-center text-sm text-text-secondary\">\n {tr('vibecontrols.actions.executionHistory.noExecutions', 'No executions yet')}\n </div>\n ) : (\n <div className=\"divide-y divide-border-default\">\n {executions.map((exec) => {\n const Icon = STATUS_ICON[exec.status] || Clock;\n const color = STATUS_COLOR[exec.status] || 'text-text-secondary';\n return (\n <div key={exec.id} className=\"px-4 py-3 space-y-1\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <Icon\n size={14}\n className={`${color} ${exec.status === 'RUNNING' ? 'animate-spin' : ''}`}\n />\n <span className={`text-sm font-medium capitalize ${color}`}>\n {exec.status.toLowerCase()}\n </span>\n {exec.triggerType && (\n <span className=\"text-xs text-text-placeholder\">\n via {exec.triggerType.toLowerCase()}\n </span>\n )}\n </div>\n <div className=\"flex items-center gap-3 text-xs text-text-placeholder\">\n <span>{formatDuration(exec.durationMs)}</span>\n <span>{formatTime(exec.startedAt)}</span>\n </div>\n </div>\n {exec.output && (\n <pre className=\"text-xs font-mono text-text-secondary bg-bg-sunken rounded p-2 max-h-20 overflow-y-auto whitespace-pre-wrap\">\n {exec.output}\n </pre>\n )}\n {exec.errorMessage && (\n <pre className=\"text-xs font-mono text-status-error-text bg-status-error-bg/10 rounded p-2 max-h-20 overflow-y-auto whitespace-pre-wrap\">\n {exec.errorMessage}\n </pre>\n )}\n </div>\n );\n })}\n </div>\n )}\n </motion.div>\n );\n}\n"],"mappings":";;;;;AA0BA,IAAM,IAAkD;CACtD,SAAS;CACT,WAAW;CACX,QAAQ;CACR,OAAO;CACP,SAAS;CACT,SAAS;CACV,EAEK,IAAuC;CAC3C,SAAS;CACT,WAAW;CACX,QAAQ;CACR,OAAO;CACP,SAAS;CACT,SAAS;CACV;AAED,SAAgB,EAAiB,EAAE,eAAY,YAAS,iBAAqC;CAC3F,IAAM,IAAK,GAAO,EAEZ,KAAkB,MACjB,IACD,IAAK,MAAa,GAAG,EAAG,MACxB,IAAK,MAAc,IAAI,IAAK,KAAM,QAAQ,EAAE,CAAC,KAC1C,IAAI,IAAK,KAAO,QAAQ,EAAE,CAAC,KAHlB,KAMZ,KAAc,MACb,IACE,IAAI,KAAK,EAAQ,CAAC,gBAAgB,GADpB;AAIvB,QACE,kBAAC,EAAO,KAAR;EACE,WAAU;EACV,SAAS;GAAE,SAAS;GAAG,QAAQ;GAAG;EAClC,SAAS;GAAE,SAAS;GAAG,QAAQ;GAAQ;EACvC,MAAM;GAAE,SAAS;GAAG,QAAQ;GAAG;EAC/B,YAAY,EAAE,UAAU,IAAK;YAL/B,CAOE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,MAAD;IAAI,WAAU;cAAd;KACG,EAAG,0DAA0D,oBAAoB;KAAC;KAAG;KACrF;KACE;OACL,kBAAC,UAAD;IACE,MAAK;IACL,SAAS;IACT,WAAU;cAET,EAAG,+CAA+C,QAAQ;IACpD,CAAA,CACL;MAEL,EAAW,WAAW,IACrB,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAG,sDAAsD,oBAAoB;GAC1E,CAAA,GAEN,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAW,KAAK,MAAS;IACxB,IAAM,IAAO,EAAY,EAAK,WAAW,GACnC,IAAQ,EAAa,EAAK,WAAW;AAC3C,WACE,kBAAC,OAAD;KAAmB,WAAU;eAA7B;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,GAAD;UACE,MAAM;UACN,WAAW,GAAG,EAAM,GAAG,EAAK,WAAW,YAAY,iBAAiB;UACpE,CAAA;SACF,kBAAC,QAAD;UAAM,WAAW,kCAAkC;oBAChD,EAAK,OAAO,aAAa;UACrB,CAAA;SACN,EAAK,eACJ,kBAAC,QAAD;UAAM,WAAU;oBAAhB,CAAgD,QACzC,EAAK,YAAY,aAAa,CAC9B;;SAEL;WACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD,EAAA,UAAO,EAAe,EAAK,WAAW,EAAQ,CAAA,EAC9C,kBAAC,QAAD,EAAA,UAAO,EAAW,EAAK,UAAU,EAAQ,CAAA,CACrC;UACF;;MACL,EAAK,UACJ,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAK;OACF,CAAA;MAEP,EAAK,gBACJ,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAK;OACF,CAAA;MAEJ;OA/BI,EAAK,GA+BT;KAER;GACE,CAAA,CAEG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentManagerAdminBar.js","names":[],"sources":["../../../src/components/agent-manager/AgentManagerAdminBar.tsx"],"sourcesContent":["/**\n * Admin bar for the Agent Manager.\n *\n * Horizontal tab strip rendered at the bottom of the layout. Each tab\n * opens an overlay panel with the corresponding AI management component\n * (Templates, Contexts, Prompts, Tasks, Stats, Playground).\n */\n\nimport { useState } from 'react';\nimport {\n X,\n FileText,\n BookOpen,\n MessageSquare,\n ListChecks,\n BarChart3,\n Play,\n Settings,\n ExternalLink,\n} from 'lucide-react';\nimport { motion, AnimatePresence, useReducedMotion } from 'framer-motion';\nimport { useAgentManagerStore } from '@/store/agentManagerStore';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport type { AdminTab } from '@/types/agentManager';\nimport { AITemplatesTab } from '@/components/ai/AITemplatesTab';\nimport { AIContextsTab } from '@/components/ai/AIContextsTab';\nimport { AIPromptsTab } from '@/components/ai/AIPromptsTab';\nimport { AITasksTab } from '@/components/ai/AITasksTab';\nimport { AIStatsTab } from '@/components/ai/AIStatsTab';\nimport { AIPlaygroundTab } from '@/components/ai/AIPlaygroundTab';\nimport { BridleConfigPanel } from './BridleConfigPanel';\nimport { AIProviderCredentialsCard } from '@/components/ai/AIProviderCredentialsCard';\nimport { useTr } from '../../shared/hooks/useTr';\nimport type { AgentRef } from '@/services/aiApi';\n\ninterface AgentManagerAdminBarProps {\n agent: AgentRef;\n}\n\nconst ADMIN_TABS: Array<{ key: AdminTab; label: string; icon: typeof FileText }> = [\n { key: 'templates', label: 'Templates', icon: FileText },\n { key: 'contexts', label: 'Contexts', icon: BookOpen },\n { key: 'prompts', label: 'Prompts', icon: MessageSquare },\n { key: 'tasks', label: 'Tasks', icon: ListChecks },\n { key: 'stats', label: 'Stats', icon: BarChart3 },\n { key: 'playground', label: 'Playground', icon: Play },\n // Split: AI provider API keys (Anthropic, OpenAI, Gemini, …) live here;\n // Bridle owns the harness-config files (~/.claude/, ~/.config/opencode/).\n { key: 'aiKeys', label: 'AI Keys', icon: Settings },\n { key: 'bridle', label: 'Bridle', icon: Settings },\n];\n\nexport function AgentManagerAdminBar({ agent }: AgentManagerAdminBarProps) {\n const adminTab = useAgentManagerStore((s) => s.adminTab);\n const setAdminTab = useAgentManagerStore((s) => s.setAdminTab);\n const [overlayOpen, setOverlayOpen] = useState(false);\n const shouldReduceMotion = useReducedMotion();\n const handleNavigate = useVibeNavigate();\n const tr = useTr();\n\n function handleTabClick(tab: AdminTab) {\n if (overlayOpen && adminTab === tab) {\n setOverlayOpen(false);\n } else {\n setAdminTab(tab);\n setOverlayOpen(true);\n }\n }\n\n function renderTabContent() {\n switch (adminTab) {\n case 'templates':\n return <AITemplatesTab agentId={agent.id} />;\n case 'contexts':\n return <AIContextsTab agent={agent} />;\n case 'prompts':\n return <AIPromptsTab agent={agent} />;\n case 'tasks':\n return <AITasksTab />;\n case 'stats':\n return <AIStatsTab agent={agent} />;\n case 'playground':\n return <AIPlaygroundTab agent={agent} />;\n // 'aiKeys' is the SDK-credentials surface: every AI provider plugin\n // that reads from the agent's config bag (POST /api/config) gets a\n // row here. Distinct from Bridle below, which writes harness config\n // files on disk under ~/.claude/, ~/.config/opencode/, etc.\n case 'aiKeys':\n case 'config': // back-compat alias for old saved adminTab='config'\n return <AIProviderCredentialsCard agent={agent} />;\n case 'bridle':\n return <BridleConfigPanel agent={agent} />;\n default:\n return null;\n }\n }\n\n return (\n <>\n {/* Admin overlay */}\n <AnimatePresence>\n {overlayOpen && (\n <motion.div\n className=\"fixed inset-0 z-40 flex items-center justify-center\"\n initial={shouldReduceMotion ? undefined : { opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.2 }}\n >\n {/* Backdrop */}\n <div role=\"button\" tabIndex={0} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); (() => setOverlayOpen(false))(); } }} className=\"absolute inset-0 bg-bg-overlay\" onClick={() => setOverlayOpen(false)} />\n\n {/* Content card */}\n <motion.div\n className=\"relative w-full max-w-4xl max-h-[80vh] mx-4 rounded-lg border border-border-default bg-bg-surface shadow-xl overflow-hidden flex flex-col\"\n initial={shouldReduceMotion ? undefined : { opacity: 0, scale: 0.96, y: 16 }}\n animate={{ opacity: 1, scale: 1, y: 0 }}\n exit={{ opacity: 0, scale: 0.96, y: 16 }}\n transition={{ duration: 0.2, ease: [0.2, 0, 0, 1] }}\n >\n {/* Header */}\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-border-default\">\n <h2 className=\"text-sm font-semibold text-text-primary capitalize\">{adminTab}</h2>\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={() => handleNavigate('/ai-tools/' + adminTab)}\n className=\"p-1 rounded hover:bg-bg-elevated transition-colors text-text-tertiary hover:text-text-secondary\"\n title={tr('vibecontrols.agentManager.adminBar.openFullPage', 'Open full page')}\n >\n <ExternalLink className=\"size-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setOverlayOpen(false)}\n className=\"p-1 rounded hover:bg-bg-elevated transition-colors text-text-tertiary hover:text-text-secondary\"\n >\n <X className=\"size-4\" />\n </button>\n </div>\n </div>\n\n {/* Tab content */}\n <div className=\"flex-1 overflow-y-auto p-4\">{renderTabContent()}</div>\n </motion.div>\n </motion.div>\n )}\n </AnimatePresence>\n\n {/* Bottom bar */}\n <div className=\"flex items-center gap-0.5 px-2 py-1 border-t border-border-default bg-bg-surface overflow-x-auto\">\n {ADMIN_TABS.map((tab) => {\n const Icon = tab.icon;\n const isActive = overlayOpen && adminTab === tab.key;\n return (\n <button\n key={tab.key}\n type=\"button\"\n onClick={() => handleTabClick(tab.key)}\n className={`flex items-center gap-1.5 px-2.5 py-1.5 text-xs font-medium rounded transition-colors whitespace-nowrap ${\n isActive\n ? 'bg-action-primary-bg/10 text-action-primary-text'\n : 'text-text-tertiary hover:text-text-secondary hover:bg-bg-elevated'\n }`}\n >\n <Icon className=\"size-3.5\" />\n <span>{tab.label}</span>\n </button>\n );\n })}\n </div>\n </>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AgentManagerAdminBar.js","names":[],"sources":["../../../src/components/agent-manager/AgentManagerAdminBar.tsx"],"sourcesContent":["/**\n * Admin bar for the Agent Manager.\n *\n * Horizontal tab strip rendered at the bottom of the layout. Each tab\n * opens an overlay panel with the corresponding AI management component\n * (Templates, Contexts, Prompts, Tasks, Stats, Playground).\n */\n\nimport { useState } from 'react';\nimport {\n X,\n FileText,\n BookOpen,\n MessageSquare,\n ListChecks,\n BarChart3,\n Play,\n Settings,\n ExternalLink,\n} from 'lucide-react';\nimport { motion, AnimatePresence, useReducedMotion } from 'framer-motion';\nimport { useAgentManagerStore } from '@/store/agentManagerStore';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport type { AdminTab } from '@/types/agentManager';\nimport { AITemplatesTab } from '@/components/ai/AITemplatesTab';\nimport { AIContextsTab } from '@/components/ai/AIContextsTab';\nimport { AIPromptsTab } from '@/components/ai/AIPromptsTab';\nimport { AITasksTab } from '@/components/ai/AITasksTab';\nimport { AIStatsTab } from '@/components/ai/AIStatsTab';\nimport { AIPlaygroundTab } from '@/components/ai/AIPlaygroundTab';\nimport { BridleConfigPanel } from './BridleConfigPanel';\nimport { AIProviderCredentialsCard } from '@/components/ai/AIProviderCredentialsCard';\nimport { useTr } from '../../shared/hooks/useTr';\nimport type { AgentRef } from '@/services/aiApi';\n\ninterface AgentManagerAdminBarProps {\n agent: AgentRef;\n}\n\nconst ADMIN_TABS: Array<{ key: AdminTab; label: string; icon: typeof FileText }> = [\n { key: 'templates', label: 'Templates', icon: FileText },\n { key: 'contexts', label: 'Contexts', icon: BookOpen },\n { key: 'prompts', label: 'Prompts', icon: MessageSquare },\n { key: 'tasks', label: 'Tasks', icon: ListChecks },\n { key: 'stats', label: 'Stats', icon: BarChart3 },\n { key: 'playground', label: 'Playground', icon: Play },\n // Split: AI provider API keys (Anthropic, OpenAI, Gemini, …) live here;\n // Bridle owns the harness-config files (~/.claude/, ~/.config/opencode/).\n { key: 'aiKeys', label: 'AI Keys', icon: Settings },\n { key: 'bridle', label: 'Bridle', icon: Settings },\n];\n\nexport function AgentManagerAdminBar({ agent }: AgentManagerAdminBarProps) {\n const adminTab = useAgentManagerStore((s) => s.adminTab);\n const setAdminTab = useAgentManagerStore((s) => s.setAdminTab);\n const [overlayOpen, setOverlayOpen] = useState(false);\n const shouldReduceMotion = useReducedMotion();\n const handleNavigate = useVibeNavigate();\n const tr = useTr();\n\n function handleTabClick(tab: AdminTab) {\n if (overlayOpen && adminTab === tab) {\n setOverlayOpen(false);\n } else {\n setAdminTab(tab);\n setOverlayOpen(true);\n }\n }\n\n function renderTabContent() {\n switch (adminTab) {\n case 'templates':\n return <AITemplatesTab agentId={agent.id} />;\n case 'contexts':\n return <AIContextsTab agent={agent} />;\n case 'prompts':\n return <AIPromptsTab agent={agent} />;\n case 'tasks':\n return <AITasksTab />;\n case 'stats':\n return <AIStatsTab agent={agent} />;\n case 'playground':\n return <AIPlaygroundTab agent={agent} />;\n // 'aiKeys' is the SDK-credentials surface: every AI provider plugin\n // that reads from the agent's config bag (POST /api/config) gets a\n // row here. Distinct from Bridle below, which writes harness config\n // files on disk under ~/.claude/, ~/.config/opencode/, etc.\n case 'aiKeys':\n case 'config': // back-compat alias for old saved adminTab='config'\n return <AIProviderCredentialsCard agent={agent} />;\n case 'bridle':\n return <BridleConfigPanel agent={agent} />;\n default:\n return null;\n }\n }\n\n return (\n <>\n {/* Admin overlay */}\n <AnimatePresence>\n {overlayOpen && (\n <motion.div\n className=\"fixed inset-0 z-40 flex items-center justify-center\"\n initial={shouldReduceMotion ? undefined : { opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.2 }}\n >\n {/* Backdrop */}\n <div\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (() => setOverlayOpen(false))();\n }\n }}\n className=\"absolute inset-0 bg-bg-overlay\"\n onClick={() => setOverlayOpen(false)}\n />\n\n {/* Content card */}\n <motion.div\n className=\"relative w-full max-w-4xl max-h-[80vh] mx-4 rounded-lg border border-border-default bg-bg-surface shadow-xl overflow-hidden flex flex-col\"\n initial={shouldReduceMotion ? undefined : { opacity: 0, scale: 0.96, y: 16 }}\n animate={{ opacity: 1, scale: 1, y: 0 }}\n exit={{ opacity: 0, scale: 0.96, y: 16 }}\n transition={{ duration: 0.2, ease: [0.2, 0, 0, 1] }}\n >\n {/* Header */}\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-border-default\">\n <h2 className=\"text-sm font-semibold text-text-primary capitalize\">{adminTab}</h2>\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={() => handleNavigate('/ai-tools/' + adminTab)}\n className=\"p-1 rounded hover:bg-bg-elevated transition-colors text-text-tertiary hover:text-text-secondary\"\n title={tr('vibecontrols.agentManager.adminBar.openFullPage', 'Open full page')}\n >\n <ExternalLink className=\"size-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setOverlayOpen(false)}\n className=\"p-1 rounded hover:bg-bg-elevated transition-colors text-text-tertiary hover:text-text-secondary\"\n >\n <X className=\"size-4\" />\n </button>\n </div>\n </div>\n\n {/* Tab content */}\n <div className=\"flex-1 overflow-y-auto p-4\">{renderTabContent()}</div>\n </motion.div>\n </motion.div>\n )}\n </AnimatePresence>\n\n {/* Bottom bar */}\n <div className=\"flex items-center gap-0.5 px-2 py-1 border-t border-border-default bg-bg-surface overflow-x-auto\">\n {ADMIN_TABS.map((tab) => {\n const Icon = tab.icon;\n const isActive = overlayOpen && adminTab === tab.key;\n return (\n <button\n key={tab.key}\n type=\"button\"\n onClick={() => handleTabClick(tab.key)}\n className={`flex items-center gap-1.5 px-2.5 py-1.5 text-xs font-medium rounded transition-colors whitespace-nowrap ${\n isActive\n ? 'bg-action-primary-bg/10 text-action-primary-text'\n : 'text-text-tertiary hover:text-text-secondary hover:bg-bg-elevated'\n }`}\n >\n <Icon className=\"size-3.5\" />\n <span>{tab.label}</span>\n </button>\n );\n })}\n </div>\n </>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentManagerBookmarksDrawer.js","names":[],"sources":["../../../src/components/agent-manager/AgentManagerBookmarksDrawer.tsx"],"sourcesContent":["/**\n * Side drawer listing the current user's bookmarks for ONE chat session.\n *\n * Each row resolves its message body from the currently-loaded session\n * messages. When the bookmarked message is no longer in the session\n * (agent log pruning / rotation), the row degrades to a \"Message no\n * longer available\" affordance with a remove-only action.\n */\n\nimport { useState, type FC } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { Bookmark, X, ChevronRight, Pencil, Trash2, Check, ExternalLink } from 'lucide-react';\nimport { relativeTime } from '@/utils/relativeTime';\nimport {\n useDeleteAgentMessageBookmarkMutation,\n useMyAgentMessageBookmarksQuery,\n useUpdateAgentMessageBookmarkMutation,\n type AgentMessageBookmarkFieldsFragment,\n} from '../../generated/wspace-operations';\nimport type { AgentManagerMessage } from '@/types/agentManager';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface AgentManagerBookmarksDrawerProps {\n open: boolean;\n onClose: () => void;\n sessionId: string;\n messages: AgentManagerMessage[];\n onJump: (messageId: string) => void;\n}\n\nconst PREVIEW_LEN = 80;\n\nfunction snippet(content: string): string {\n if (!content) return '';\n const trimmed = content.trim().replace(/\\s+/g, ' ');\n return trimmed.length > PREVIEW_LEN ? `${trimmed.slice(0, PREVIEW_LEN)}…` : trimmed;\n}\n\nexport const AgentManagerBookmarksDrawer: FC<AgentManagerBookmarksDrawerProps> = ({\n open,\n onClose,\n sessionId,\n messages,\n onJump,\n}) => {\n const navigate = useNavigate();\n const tr = useTr();\n const [editingId, setEditingId] = useState<string | null>(null);\n const [labelDraft, setLabelDraft] = useState('');\n\n const { data, loading } = useMyAgentMessageBookmarksQuery({\n variables: { filter: { sessionId } },\n skip: !open,\n fetchPolicy: 'cache-and-network',\n });\n\n const [updateMutation] = useUpdateAgentMessageBookmarkMutation({\n refetchQueries: ['MyAgentMessageBookmarks'],\n });\n const [deleteMutation] = useDeleteAgentMessageBookmarkMutation({\n refetchQueries: ['MyAgentMessageBookmarks'],\n });\n\n const bookmarks = data?.myAgentMessageBookmarks.items ?? [];\n const messageById = new Map(messages.map((m) => [m.id, m]));\n\n const startEdit = (id: string, current: string | null | undefined) => {\n setEditingId(id);\n setLabelDraft(current ?? '');\n };\n\n const saveEdit = async (id: string) => {\n await updateMutation({ variables: { id, input: { label: labelDraft || null } } });\n setEditingId(null);\n setLabelDraft('');\n };\n\n const removeBookmark = async (id: string) => {\n await deleteMutation({ variables: { id } });\n };\n\n if (!open) return null;\n\n return (\n <div className=\"fixed inset-0 z-40 flex\">\n <div className=\"flex-1 bg-bg-overlay/40\" onClick={onClose} aria-hidden />\n <aside\n className=\"w-full max-w-md h-full bg-bg-surface border-l border-border-default shadow-xl flex flex-col\"\n role=\"dialog\"\n aria-label={tr(\n 'vibecontrols.agentManager.bookmarksDrawer.title',\n 'Bookmarks in this session'\n )}\n data-testid=\"agent-manager-bookmarks-drawer\"\n >\n <header className=\"flex items-center justify-between px-4 py-3 border-b border-border-default flex-shrink-0\">\n <div className=\"flex items-center gap-2\">\n <Bookmark className=\"size-4 text-text-secondary\" />\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tr('vibecontrols.agentManager.bookmarksDrawer.title', 'Bookmarks in this session')}\n </h3>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n aria-label={tr('vibecontrols.common.close', 'Close')}\n >\n <X className=\"size-4 text-text-secondary\" />\n </button>\n </header>\n\n <div className=\"flex-1 min-h-0 overflow-y-auto\">\n {loading && bookmarks.length === 0 && (\n <div className=\"px-4 py-6 text-xs text-text-tertiary\">\n {tr('vibecontrols.common.loading', 'Loading...')}\n </div>\n )}\n\n {!loading && bookmarks.length === 0 && (\n <div className=\"px-4 py-10 flex flex-col items-center text-center gap-2\">\n <Bookmark className=\"size-8 text-text-muted\" />\n <p className=\"text-sm font-medium text-text-primary\">\n {tr(\n 'vibecontrols.agentManager.bookmarksDrawer.empty',\n 'No bookmarks in this session yet'\n )}\n </p>\n <p className=\"text-xs text-text-secondary max-w-xs\">\n {tr(\n 'vibecontrols.agentManager.bookmarksDrawer.emptyHint',\n 'Click the bookmark icon on any message to save it for later.'\n )}\n </p>\n </div>\n )}\n\n <ul className=\"divide-y divide-border-subtle\">\n {bookmarks.map((bm: AgentMessageBookmarkFieldsFragment) => {\n const msg = messageById.get(bm.messageId);\n const isStale = !msg;\n const isEditing = editingId === bm.id;\n\n return (\n <li key={bm.id} className=\"px-4 py-3 hover:bg-bg-elevated/40 transition-colors\">\n <div className=\"flex items-start gap-2\">\n <div className=\"flex-1 min-w-0\">\n {isEditing ? (\n <input aria-label=\"control\"\n\n value={labelDraft}\n onChange={(e) => setLabelDraft(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') void saveEdit(bm.id);\n if (e.key === 'Escape') {\n setEditingId(null);\n setLabelDraft('');\n }\n }}\n placeholder={tr(\n 'vibecontrols.agentManager.bookmarksDrawer.labelPlaceholder',\n 'Add a label (optional)'\n )}\n className=\"w-full bg-bg-sunken border border-border-subtle rounded px-2 py-1 text-xs text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-action-primary-bg\"\n />\n ) : (\n <p className=\"text-sm font-medium text-text-primary truncate\">\n {bm.label ||\n (msg?.role === 'user'\n ? tr('vibecontrols.agentManager.bookmarksDrawer.fromUser', 'From you')\n : tr(\n 'vibecontrols.agentManager.bookmarksDrawer.fromAssistant',\n 'Assistant reply'\n ))}\n </p>\n )}\n\n {isStale ? (\n <p className=\"text-xs text-text-tertiary italic mt-1\">\n {tr(\n 'vibecontrols.agentManager.bookmarksDrawer.stale',\n 'Message no longer available'\n )}\n </p>\n ) : (\n <p className=\"text-xs text-text-secondary mt-1 line-clamp-2\">\n {snippet(msg.content)}\n </p>\n )}\n\n <p className=\"text-[10px] text-text-tertiary mt-1\">\n {relativeTime(bm.createdAt)}\n </p>\n </div>\n\n <div className=\"flex items-center gap-0.5 shrink-0\">\n {!isStale && !isEditing && (\n <button\n type=\"button\"\n onClick={() => {\n onJump(bm.messageId);\n onClose();\n }}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n title={tr(\n 'vibecontrols.agentManager.bookmarksDrawer.jump',\n 'Jump to message'\n )}\n aria-label={tr(\n 'vibecontrols.agentManager.bookmarksDrawer.jump',\n 'Jump to message'\n )}\n >\n <ChevronRight className=\"size-3.5 text-text-secondary\" />\n </button>\n )}\n\n {isEditing ? (\n <button\n type=\"button\"\n onClick={() => void saveEdit(bm.id)}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n aria-label={tr('vibecontrols.common.save', 'Save')}\n >\n <Check className=\"size-3.5 text-status-success-text\" />\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={() => startEdit(bm.id, bm.label)}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n aria-label={tr(\n 'vibecontrols.agentManager.bookmarksDrawer.editLabel',\n 'Edit label'\n )}\n >\n <Pencil className=\"size-3.5 text-text-secondary\" />\n </button>\n )}\n\n <button\n type=\"button\"\n onClick={() => void removeBookmark(bm.id)}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n aria-label={tr(\n 'vibecontrols.agentManager.messageItem.removeBookmark',\n 'Remove bookmark'\n )}\n >\n <Trash2 className=\"size-3.5 text-status-danger-text\" />\n </button>\n </div>\n </div>\n </li>\n );\n })}\n </ul>\n </div>\n\n <footer className=\"px-4 py-3 border-t border-border-default flex-shrink-0\">\n <button\n type=\"button\"\n onClick={() => {\n onClose();\n navigate('/vibecontrols/ai/bookmarks');\n }}\n className=\"inline-flex items-center gap-1.5 text-xs font-medium text-action-primary-text hover:underline\"\n >\n <ExternalLink className=\"size-3\" />\n {tr(\n 'vibecontrols.agentManager.bookmarksDrawer.seeAll',\n 'See all bookmarks across sessions'\n )}\n </button>\n </footer>\n </aside>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AgentManagerBookmarksDrawer.js","names":[],"sources":["../../../src/components/agent-manager/AgentManagerBookmarksDrawer.tsx"],"sourcesContent":["/**\n * Side drawer listing the current user's bookmarks for ONE chat session.\n *\n * Each row resolves its message body from the currently-loaded session\n * messages. When the bookmarked message is no longer in the session\n * (agent log pruning / rotation), the row degrades to a \"Message no\n * longer available\" affordance with a remove-only action.\n */\n\nimport { useState, type FC } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { Bookmark, X, ChevronRight, Pencil, Trash2, Check, ExternalLink } from 'lucide-react';\nimport { relativeTime } from '@/utils/relativeTime';\nimport {\n useDeleteAgentMessageBookmarkMutation,\n useMyAgentMessageBookmarksQuery,\n useUpdateAgentMessageBookmarkMutation,\n type AgentMessageBookmarkFieldsFragment,\n} from '../../generated/wspace-operations';\nimport type { AgentManagerMessage } from '@/types/agentManager';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface AgentManagerBookmarksDrawerProps {\n open: boolean;\n onClose: () => void;\n sessionId: string;\n messages: AgentManagerMessage[];\n onJump: (messageId: string) => void;\n}\n\nconst PREVIEW_LEN = 80;\n\nfunction snippet(content: string): string {\n if (!content) return '';\n const trimmed = content.trim().replace(/\\s+/g, ' ');\n return trimmed.length > PREVIEW_LEN ? `${trimmed.slice(0, PREVIEW_LEN)}…` : trimmed;\n}\n\nexport const AgentManagerBookmarksDrawer: FC<AgentManagerBookmarksDrawerProps> = ({\n open,\n onClose,\n sessionId,\n messages,\n onJump,\n}) => {\n const navigate = useNavigate();\n const tr = useTr();\n const [editingId, setEditingId] = useState<string | null>(null);\n const [labelDraft, setLabelDraft] = useState('');\n\n const { data, loading } = useMyAgentMessageBookmarksQuery({\n variables: { filter: { sessionId } },\n skip: !open,\n fetchPolicy: 'cache-and-network',\n });\n\n const [updateMutation] = useUpdateAgentMessageBookmarkMutation({\n refetchQueries: ['MyAgentMessageBookmarks'],\n });\n const [deleteMutation] = useDeleteAgentMessageBookmarkMutation({\n refetchQueries: ['MyAgentMessageBookmarks'],\n });\n\n const bookmarks = data?.myAgentMessageBookmarks.items ?? [];\n const messageById = new Map(messages.map((m) => [m.id, m]));\n\n const startEdit = (id: string, current: string | null | undefined) => {\n setEditingId(id);\n setLabelDraft(current ?? '');\n };\n\n const saveEdit = async (id: string) => {\n await updateMutation({ variables: { id, input: { label: labelDraft || null } } });\n setEditingId(null);\n setLabelDraft('');\n };\n\n const removeBookmark = async (id: string) => {\n await deleteMutation({ variables: { id } });\n };\n\n if (!open) return null;\n\n return (\n <div className=\"fixed inset-0 z-40 flex\">\n <div className=\"flex-1 bg-bg-overlay/40\" onClick={onClose} aria-hidden />\n <aside\n className=\"w-full max-w-md h-full bg-bg-surface border-l border-border-default shadow-xl flex flex-col\"\n role=\"dialog\"\n aria-label={tr(\n 'vibecontrols.agentManager.bookmarksDrawer.title',\n 'Bookmarks in this session'\n )}\n data-testid=\"agent-manager-bookmarks-drawer\"\n >\n <header className=\"flex items-center justify-between px-4 py-3 border-b border-border-default flex-shrink-0\">\n <div className=\"flex items-center gap-2\">\n <Bookmark className=\"size-4 text-text-secondary\" />\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tr('vibecontrols.agentManager.bookmarksDrawer.title', 'Bookmarks in this session')}\n </h3>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n aria-label={tr('vibecontrols.common.close', 'Close')}\n >\n <X className=\"size-4 text-text-secondary\" />\n </button>\n </header>\n\n <div className=\"flex-1 min-h-0 overflow-y-auto\">\n {loading && bookmarks.length === 0 && (\n <div className=\"px-4 py-6 text-xs text-text-tertiary\">\n {tr('vibecontrols.common.loading', 'Loading...')}\n </div>\n )}\n\n {!loading && bookmarks.length === 0 && (\n <div className=\"px-4 py-10 flex flex-col items-center text-center gap-2\">\n <Bookmark className=\"size-8 text-text-muted\" />\n <p className=\"text-sm font-medium text-text-primary\">\n {tr(\n 'vibecontrols.agentManager.bookmarksDrawer.empty',\n 'No bookmarks in this session yet'\n )}\n </p>\n <p className=\"text-xs text-text-secondary max-w-xs\">\n {tr(\n 'vibecontrols.agentManager.bookmarksDrawer.emptyHint',\n 'Click the bookmark icon on any message to save it for later.'\n )}\n </p>\n </div>\n )}\n\n <ul className=\"divide-y divide-border-subtle\">\n {bookmarks.map((bm: AgentMessageBookmarkFieldsFragment) => {\n const msg = messageById.get(bm.messageId);\n const isStale = !msg;\n const isEditing = editingId === bm.id;\n\n return (\n <li key={bm.id} className=\"px-4 py-3 hover:bg-bg-elevated/40 transition-colors\">\n <div className=\"flex items-start gap-2\">\n <div className=\"flex-1 min-w-0\">\n {isEditing ? (\n <input\n aria-label=\"control\"\n value={labelDraft}\n onChange={(e) => setLabelDraft(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') void saveEdit(bm.id);\n if (e.key === 'Escape') {\n setEditingId(null);\n setLabelDraft('');\n }\n }}\n placeholder={tr(\n 'vibecontrols.agentManager.bookmarksDrawer.labelPlaceholder',\n 'Add a label (optional)'\n )}\n className=\"w-full bg-bg-sunken border border-border-subtle rounded px-2 py-1 text-xs text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-action-primary-bg\"\n />\n ) : (\n <p className=\"text-sm font-medium text-text-primary truncate\">\n {bm.label ||\n (msg?.role === 'user'\n ? tr('vibecontrols.agentManager.bookmarksDrawer.fromUser', 'From you')\n : tr(\n 'vibecontrols.agentManager.bookmarksDrawer.fromAssistant',\n 'Assistant reply'\n ))}\n </p>\n )}\n\n {isStale ? (\n <p className=\"text-xs text-text-tertiary italic mt-1\">\n {tr(\n 'vibecontrols.agentManager.bookmarksDrawer.stale',\n 'Message no longer available'\n )}\n </p>\n ) : (\n <p className=\"text-xs text-text-secondary mt-1 line-clamp-2\">\n {snippet(msg.content)}\n </p>\n )}\n\n <p className=\"text-[10px] text-text-tertiary mt-1\">\n {relativeTime(bm.createdAt)}\n </p>\n </div>\n\n <div className=\"flex items-center gap-0.5 shrink-0\">\n {!isStale && !isEditing && (\n <button\n type=\"button\"\n onClick={() => {\n onJump(bm.messageId);\n onClose();\n }}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n title={tr(\n 'vibecontrols.agentManager.bookmarksDrawer.jump',\n 'Jump to message'\n )}\n aria-label={tr(\n 'vibecontrols.agentManager.bookmarksDrawer.jump',\n 'Jump to message'\n )}\n >\n <ChevronRight className=\"size-3.5 text-text-secondary\" />\n </button>\n )}\n\n {isEditing ? (\n <button\n type=\"button\"\n onClick={() => void saveEdit(bm.id)}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n aria-label={tr('vibecontrols.common.save', 'Save')}\n >\n <Check className=\"size-3.5 text-status-success-text\" />\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={() => startEdit(bm.id, bm.label)}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n aria-label={tr(\n 'vibecontrols.agentManager.bookmarksDrawer.editLabel',\n 'Edit label'\n )}\n >\n <Pencil className=\"size-3.5 text-text-secondary\" />\n </button>\n )}\n\n <button\n type=\"button\"\n onClick={() => void removeBookmark(bm.id)}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n aria-label={tr(\n 'vibecontrols.agentManager.messageItem.removeBookmark',\n 'Remove bookmark'\n )}\n >\n <Trash2 className=\"size-3.5 text-status-danger-text\" />\n </button>\n </div>\n </div>\n </li>\n );\n })}\n </ul>\n </div>\n\n <footer className=\"px-4 py-3 border-t border-border-default flex-shrink-0\">\n <button\n type=\"button\"\n onClick={() => {\n onClose();\n navigate('/vibecontrols/ai/bookmarks');\n }}\n className=\"inline-flex items-center gap-1.5 text-xs font-medium text-action-primary-text hover:underline\"\n >\n <ExternalLink className=\"size-3\" />\n {tr(\n 'vibecontrols.agentManager.bookmarksDrawer.seeAll',\n 'See all bookmarks across sessions'\n )}\n </button>\n </footer>\n </aside>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentManagerBulkActions.js","names":[],"sources":["../../../src/components/agent-manager/AgentManagerBulkActions.tsx"],"sourcesContent":["/**\n * Bulk actions bar displayed when one or more sessions are selected.\n *\n * Provides delete-selected and export-selected actions with\n * a ConfirmDialog for destructive operations.\n */\n\nimport { useState } from 'react';\nimport { Trash2, Download, X } from 'lucide-react';\nimport { motion } from 'framer-motion';\nimport { ConfirmDialog } from '@/components/shared';\nimport { useAgentManagerStore } from '@/store/agentManagerStore';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface AgentManagerBulkActionsProps {\n onDeleteSelected: (sessionIds: string[]) => void;\n onExportSelected?: (sessionIds: string[]) => void;\n}\n\nexport function AgentManagerBulkActions({\n onDeleteSelected,\n onExportSelected,\n}: AgentManagerBulkActionsProps) {\n const selectedSessionIds = useAgentManagerStore((s) => s.selectedSessionIds);\n const clearSelection = useAgentManagerStore((s) => s.clearSelection);\n const [confirmOpen, setConfirmOpen] = useState(false);\n const tr = useTr();\n\n const count = selectedSessionIds.length;\n if (count === 0) return null;\n\n const handleDelete = () => {\n onDeleteSelected(selectedSessionIds);\n clearSelection();\n setConfirmOpen(false);\n };\n\n const handleExport = () => {\n onExportSelected?.(selectedSessionIds);\n clearSelection();\n };\n\n return (\n <>\n <motion.div\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: 8 }}\n transition={{ duration: 0.15 }}\n className=\"flex items-center gap-2 px-3 py-2 border-t border-border-default bg-bg-elevated\"\n >\n <span className=\"text-xs font-medium text-text-secondary\">\n {count} {tr('vibecontrols.agentManager.bulkActions.selected', 'selected')}\n </span>\n\n <div className=\"flex-1\" />\n\n {onExportSelected && (\n <button
|
|
1
|
+
{"version":3,"file":"AgentManagerBulkActions.js","names":[],"sources":["../../../src/components/agent-manager/AgentManagerBulkActions.tsx"],"sourcesContent":["/**\n * Bulk actions bar displayed when one or more sessions are selected.\n *\n * Provides delete-selected and export-selected actions with\n * a ConfirmDialog for destructive operations.\n */\n\nimport { useState } from 'react';\nimport { Trash2, Download, X } from 'lucide-react';\nimport { motion } from 'framer-motion';\nimport { ConfirmDialog } from '@/components/shared';\nimport { useAgentManagerStore } from '@/store/agentManagerStore';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface AgentManagerBulkActionsProps {\n onDeleteSelected: (sessionIds: string[]) => void;\n onExportSelected?: (sessionIds: string[]) => void;\n}\n\nexport function AgentManagerBulkActions({\n onDeleteSelected,\n onExportSelected,\n}: AgentManagerBulkActionsProps) {\n const selectedSessionIds = useAgentManagerStore((s) => s.selectedSessionIds);\n const clearSelection = useAgentManagerStore((s) => s.clearSelection);\n const [confirmOpen, setConfirmOpen] = useState(false);\n const tr = useTr();\n\n const count = selectedSessionIds.length;\n if (count === 0) return null;\n\n const handleDelete = () => {\n onDeleteSelected(selectedSessionIds);\n clearSelection();\n setConfirmOpen(false);\n };\n\n const handleExport = () => {\n onExportSelected?.(selectedSessionIds);\n clearSelection();\n };\n\n return (\n <>\n <motion.div\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: 8 }}\n transition={{ duration: 0.15 }}\n className=\"flex items-center gap-2 px-3 py-2 border-t border-border-default bg-bg-elevated\"\n >\n <span className=\"text-xs font-medium text-text-secondary\">\n {count} {tr('vibecontrols.agentManager.bulkActions.selected', 'selected')}\n </span>\n\n <div className=\"flex-1\" />\n\n {onExportSelected && (\n <button\n type=\"button\"\n onClick={handleExport}\n className=\"inline-flex items-center gap-1 px-2 py-1 text-xs font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-sunken transition-colors\"\n title={tr('vibecontrols.agentManager.bulkActions.exportSelected', 'Export selected')}\n >\n <Download className=\"size-3.5\" />\n {tr('vibecontrols.agentManager.bulkActions.export', 'Export')}\n </button>\n )}\n\n <button\n type=\"button\"\n onClick={() => setConfirmOpen(true)}\n className=\"inline-flex items-center gap-1 px-2 py-1 text-xs font-medium text-status-error-text bg-bg-surface border border-border-default rounded-md hover:bg-status-error-bg-subtle transition-colors\"\n title={tr('vibecontrols.agentManager.bulkActions.deleteSelected', 'Delete selected')}\n >\n <Trash2 className=\"size-3.5\" />\n {tr('vibecontrols.agentManager.bulkActions.delete', 'Delete')}\n </button>\n\n <button\n type=\"button\"\n onClick={clearSelection}\n className=\"p-1 rounded text-text-secondary hover:text-text-primary hover:bg-bg-sunken transition-colors\"\n title={tr('vibecontrols.agentManager.bulkActions.clearSelection', 'Clear selection')}\n >\n <X className=\"size-3.5\" />\n </button>\n </motion.div>\n\n <ConfirmDialog\n isOpen={confirmOpen}\n onClose={() => setConfirmOpen(false)}\n onConfirm={handleDelete}\n title={tr('vibecontrols.agentManager.bulkActions.deleteSessionsTitle', 'Delete Sessions')}\n message={tr(\n 'vibecontrols.agentManager.bulkActions.confirmDelete',\n 'Are you sure you want to delete {{count}} session(s)? This cannot be undone.',\n { count }\n )}\n confirmLabel={tr('vibecontrols.agentManager.bulkActions.delete', 'Delete')}\n cancelLabel={tr('vibecontrols.agentManager.bulkActions.cancel', 'Cancel')}\n variant=\"danger\"\n />\n </>\n );\n}\n"],"mappings":";;;;;;;;;AAmBA,SAAgB,EAAwB,EACtC,qBACA,uBAC+B;CAC/B,IAAM,IAAqB,GAAsB,MAAM,EAAE,mBAAmB,EACtE,IAAiB,GAAsB,MAAM,EAAE,eAAe,EAC9D,CAAC,GAAa,KAAkB,EAAS,GAAM,EAC/C,IAAK,GAAO,EAEZ,IAAQ,EAAmB;AAcjC,QAbI,MAAU,IAAU,OActB,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,EAAO,KAAR;EACE,SAAS;GAAE,SAAS;GAAG,GAAG;GAAG;EAC7B,SAAS;GAAE,SAAS;GAAG,GAAG;GAAG;EAC7B,MAAM;GAAE,SAAS;GAAG,GAAG;GAAG;EAC1B,YAAY,EAAE,UAAU,KAAM;EAC9B,WAAU;YALZ;GAOE,kBAAC,QAAD;IAAM,WAAU;cAAhB;KACG;KAAM;KAAE,EAAG,kDAAkD,WAAW;KACpE;;GAEP,kBAAC,OAAD,EAAK,WAAU,UAAW,CAAA;GAEzB,KACC,kBAAC,UAAD;IACE,MAAK;IACL,eAvBiB;AAEzB,KADA,IAAmB,EAAmB,EACtC,GAAgB;;IAsBR,WAAU;IACV,OAAO,EAAG,wDAAwD,kBAAkB;cAJtF,CAME,kBAAC,GAAD,EAAU,WAAU,YAAa,CAAA,EAChC,EAAG,gDAAgD,SAAS,CACtD;;GAGX,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,EAAe,GAAK;IACnC,WAAU;IACV,OAAO,EAAG,wDAAwD,kBAAkB;cAJtF,CAME,kBAAC,GAAD,EAAQ,WAAU,YAAa,CAAA,EAC9B,EAAG,gDAAgD,SAAS,CACtD;;GAET,kBAAC,UAAD;IACE,MAAK;IACL,SAAS;IACT,WAAU;IACV,OAAO,EAAG,wDAAwD,kBAAkB;cAEpF,kBAAC,GAAD,EAAG,WAAU,YAAa,CAAA;IACnB,CAAA;GACE;KAEb,kBAAC,GAAD;EACE,QAAQ;EACR,eAAe,EAAe,GAAM;EACpC,iBA7DqB;AAGzB,GAFA,EAAiB,EAAmB,EACpC,GAAgB,EAChB,EAAe,GAAM;;EA2DjB,OAAO,EAAG,6DAA6D,kBAAkB;EACzF,SAAS,EACP,uDACA,gFACA,EAAE,UAAO,CACV;EACD,cAAc,EAAG,gDAAgD,SAAS;EAC1E,aAAa,EAAG,gDAAgD,SAAS;EACzE,SAAQ;EACR,CAAA,CACD,EAAA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentManagerCenter.js","names":[],"sources":["../../../src/components/agent-manager/AgentManagerCenter.tsx"],"sourcesContent":["/**\n * Center container for the Agent Manager.\n *\n * Renders the tab bar at top and the active session's chat pane below.\n * Shows a welcome/empty state when no tabs are open.\n */\n\nimport { useState, useCallback, useEffect, useRef, type FC } from 'react';\nimport { MessageSquarePlus, X } from 'lucide-react';\nimport { useAgentManagerStore } from '@/store/agentManagerStore';\nimport { EmptyState } from '@/components/shared';\nimport { AgentManagerTabBar } from './AgentManagerTabBar';\nimport { AgentManagerChatPane } from './AgentManagerChatPane';\nimport { AgentManagerAdminBar } from './AgentManagerAdminBar';\nimport { SessionSearchDialog } from './dialogs/SessionSearchDialog';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { createSession, type AgentRef } from '@/services/aiApi';\n\n/* ────────────────────────────────────────────────────────────────────── */\n/* Keyboard shortcuts reference */\n/* ────────────────────────────────────────────────────────────────────── */\n\nconst SHORTCUTS: Array<{ keys: string; description: string }> = [\n { keys: 'Ctrl/Cmd+Shift+N', description: 'New session' },\n { keys: 'Ctrl/Cmd+Shift+K', description: 'Search sessions' },\n { keys: 'Ctrl/Cmd+Shift+F', description: 'In-session search' },\n { keys: 'Escape', description: 'Cancel / close modal' },\n { keys: 'Enter', description: 'Send message' },\n { keys: 'Shift+Enter', description: 'New line' },\n];\n\ninterface AgentManagerCenterProps {\n agent: AgentRef;\n}\n\nexport const AgentManagerCenter: FC<AgentManagerCenterProps> = ({ agent }) => {\n const tabs = useAgentManagerStore((s) => s.tabs);\n const activeTabSessionId = useAgentManagerStore((s) => s.activeTabSessionId);\n const openSession = useAgentManagerStore((s) => s.openSession);\n const setActiveTab = useAgentManagerStore((s) => s.setActiveTab);\n const tr = useTr();\n\n const [searchOpen, setSearchOpen] = useState(false);\n const [shortcutsOpen, setShortcutsOpen] = useState(false);\n const rootRef = useRef<HTMLDivElement>(null);\n\n const bumpSessionsRefresh = useAgentManagerStore((s) => s.bumpSessionsRefresh);\n const handleCreateSession = useCallback(async () => {\n try {\n const session = await createSession(agent, {\n name: `Session ${tabs.length + 1}`,\n agentType: 'claude',\n config: { model: 'default', mode: 'cli', tags: [] },\n });\n openSession(session.id, session.name);\n bumpSessionsRefresh();\n } catch (err) {\n console.error('[AgentManagerCenter] createSession failed:', err);\n }\n }, [agent, tabs.length, openSession, bumpSessionsRefresh]);\n\n const handleOpenSearch = useCallback(() => setSearchOpen(true), []);\n\n useEffect(() => {\n if (activeTabSessionId && tabs.some((tab) => tab.sessionId === activeTabSessionId)) {\n setActiveTab(activeTabSessionId);\n }\n }, [activeTabSessionId, setActiveTab, tabs]);\n\n // Wire the shortcuts advertised in the SHORTCUTS modal. Scoped to the\n // Agent Manager container so they don't clash with shortcuts on other\n // pages. Each shortcut uses Shift to avoid colliding with built-in\n // browser shortcuts (Ctrl+N opens a window, Ctrl+F opens find).\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n const handler = (e: KeyboardEvent) => {\n if (!root.contains(document.activeElement) && document.activeElement !== root) return;\n const mod = e.ctrlKey || e.metaKey;\n if (mod && e.shiftKey && (e.key === 'N' || e.key === 'n')) {\n e.preventDefault();\n void handleCreateSession();\n return;\n }\n if (mod && e.shiftKey && (e.key === 'K' || e.key === 'k')) {\n e.preventDefault();\n handleOpenSearch();\n return;\n }\n if (mod && e.shiftKey && (e.key === 'F' || e.key === 'f')) {\n e.preventDefault();\n const inSession = root.querySelector(\n '[data-testid=\"agent-manager-in-session-search\"]'\n ) as HTMLButtonElement | null;\n inSession?.click();\n }\n };\n document.addEventListener('keydown', handler);\n return () => document.removeEventListener('keydown', handler);\n }, [handleCreateSession, handleOpenSearch]);\n\n return (\n <div ref={rootRef} className=\"flex flex-col h-full min-h-0\" tabIndex={-1}>\n {/* Tab bar — occupies full row width, search/help live inside the tabBar area */}\n <AgentManagerTabBar\n agent={agent}\n onSearch={handleOpenSearch}\n onShowShortcuts={() => setShortcutsOpen(true)}\n />\n\n {/* Active session or empty state */}\n <div className=\"flex-1 min-h-0 overflow-hidden\">\n {activeTabSessionId && tabs.some((t) => t.sessionId === activeTabSessionId) ? (\n <AgentManagerChatPane\n key={activeTabSessionId}\n sessionId={activeTabSessionId}\n agentId={agent.id}\n />\n ) : (\n <div className=\"flex-1 flex items-center justify-center h-full\">\n <EmptyState\n icon={<MessageSquarePlus className=\"size-8 text-text-secondary\" />}\n title={tr('vibecontrols.agentManager.center.noActiveSession', 'No active session')}\n description={tr(\n 'vibecontrols.agentManager.center.createSessionDesc',\n 'Create a new session to start interacting with the AI agent.'\n )}\n action={\n <button type=\"button\"\n onClick={handleCreateSession}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('vibecontrols.agentManager.center.createNewSession', 'Create a new session')}\n </button>\n }\n />\n </div>\n )}\n </div>\n\n {/* Admin bar at bottom */}\n <div className=\"flex-shrink-0\">\n <AgentManagerAdminBar agent={agent} />\n </div>\n\n {/* Session search dialog */}\n <SessionSearchDialog isOpen={searchOpen} agent={agent} onClose={() => setSearchOpen(false)} />\n\n {/* Keyboard shortcuts modal */}\n {shortcutsOpen && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n <div role=\"button\" tabIndex={0} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); (() => setShortcutsOpen(false))(); } }} className=\"absolute inset-0 bg-bg-overlay\" onClick={() => setShortcutsOpen(false)} />\n <div className=\"relative w-full max-w-sm mx-4 rounded-lg border border-border-default bg-bg-surface shadow-xl\">\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-default\">\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {tr('vibecontrols.agentManager.center.keyboardShortcuts', 'Keyboard Shortcuts')}\n </h3>\n <button\n type=\"button\"\n onClick={() => setShortcutsOpen(false)}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n >\n <X className=\"size-5 text-text-secondary\" />\n </button>\n </div>\n <div className=\"px-6 py-4 space-y-2\">\n {SHORTCUTS.map((s) => (\n <div key={s.keys} className=\"flex items-center justify-between py-1\">\n <span className=\"text-sm text-text-secondary\">{s.description}</span>\n <kbd className=\"px-2 py-0.5 text-xs font-mono rounded bg-bg-elevated border border-border-default text-text-primary\">\n {s.keys}\n </kbd>\n </div>\n ))}\n </div>\n <div className=\"px-6 py-3 border-t border-border-default bg-bg-sunken rounded-b-lg\">\n <button\n type=\"button\"\n onClick={() => setShortcutsOpen(false)}\n className=\"w-full px-4 py-2 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-elevated transition-colors\"\n >\n {tr('vibecontrols.agentManager.center.close', 'Close')}\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;AAsBA,IAAM,IAA0D;CAC9D;EAAE,MAAM;EAAoB,aAAa;EAAe;CACxD;EAAE,MAAM;EAAoB,aAAa;EAAmB;CAC5D;EAAE,MAAM;EAAoB,aAAa;EAAqB;CAC9D;EAAE,MAAM;EAAU,aAAa;EAAwB;CACvD;EAAE,MAAM;EAAS,aAAa;EAAgB;CAC9C;EAAE,MAAM;EAAe,aAAa;EAAY;CACjD,EAMY,KAAmD,EAAE,eAAY;CAC5E,IAAM,IAAO,GAAsB,MAAM,EAAE,KAAK,EAC1C,IAAqB,GAAsB,MAAM,EAAE,mBAAmB,EACtE,IAAc,GAAsB,MAAM,EAAE,YAAY,EACxD,IAAe,GAAsB,MAAM,EAAE,aAAa,EAC1D,IAAK,GAAO,EAEZ,CAAC,GAAY,KAAiB,EAAS,GAAM,EAC7C,CAAC,GAAe,KAAoB,EAAS,GAAM,EACnD,IAAU,EAAuB,KAAK,EAEtC,IAAsB,GAAsB,MAAM,EAAE,oBAAoB,EACxE,IAAsB,EAAY,YAAY;AAClD,MAAI;GACF,IAAM,IAAU,MAAM,EAAc,GAAO;IACzC,MAAM,WAAW,EAAK,SAAS;IAC/B,WAAW;IACX,QAAQ;KAAE,OAAO;KAAW,MAAM;KAAO,MAAM,EAAE;KAAE;IACpD,CAAC;AAEF,GADA,EAAY,EAAQ,IAAI,EAAQ,KAAK,EACrC,GAAqB;WACd,GAAK;AACZ,WAAQ,MAAM,8CAA8C,EAAI;;IAEjE;EAAC;EAAO,EAAK;EAAQ;EAAa;EAAoB,CAAC,EAEpD,IAAmB,QAAkB,EAAc,GAAK,EAAE,EAAE,CAAC;AAwCnE,QAtCA,QAAgB;AACd,EAAI,KAAsB,EAAK,MAAM,MAAQ,EAAI,cAAc,EAAmB,IAChF,EAAa,EAAmB;IAEjC;EAAC;EAAoB;EAAc;EAAK,CAAC,EAM5C,QAAgB;EACd,IAAM,IAAO,EAAQ;AACrB,MAAI,CAAC,EAAM;EACX,IAAM,KAAW,MAAqB;AACpC,OAAI,CAAC,EAAK,SAAS,SAAS,cAAc,IAAI,SAAS,kBAAkB,EAAM;GAC/E,IAAM,IAAM,EAAE,WAAW,EAAE;AAC3B,OAAI,KAAO,EAAE,aAAa,EAAE,QAAQ,OAAO,EAAE,QAAQ,MAAM;AAEpD,IADL,EAAE,gBAAgB,EACb,GAAqB;AAC1B;;AAEF,OAAI,KAAO,EAAE,aAAa,EAAE,QAAQ,OAAO,EAAE,QAAQ,MAAM;AAEzD,IADA,EAAE,gBAAgB,EAClB,GAAkB;AAClB;;AAEF,GAAI,KAAO,EAAE,aAAa,EAAE,QAAQ,OAAO,EAAE,QAAQ,SACnD,EAAE,gBAAgB,EACA,EAAK,cACrB,oDACD,EACU,OAAO;;AAItB,SADA,SAAS,iBAAiB,WAAW,EAAQ,QAChC,SAAS,oBAAoB,WAAW,EAAQ;IAC5D,CAAC,GAAqB,EAAiB,CAAC,EAGzC,kBAAC,OAAD;EAAK,KAAK;EAAS,WAAU;EAA+B,UAAU;YAAtE;GAEE,kBAAC,GAAD;IACS;IACP,UAAU;IACV,uBAAuB,EAAiB,GAAK;IAC7C,CAAA;GAGF,kBAAC,OAAD;IAAK,WAAU;cACZ,KAAsB,EAAK,MAAM,MAAM,EAAE,cAAc,EAAmB,GACzE,kBAAC,GAAD;KAEE,WAAW;KACX,SAAS,EAAM;KACf,EAHK,EAGL,GAEF,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD;MACE,MAAM,kBAAC,GAAD,EAAmB,WAAU,8BAA+B,CAAA;MAClE,OAAO,EAAG,oDAAoD,oBAAoB;MAClF,aAAa,EACX,sDACA,+DACD;MACD,QACE,kBAAC,UAAD;OAAQ,MAAK;OACX,SAAS;OACT,WAAU;iBAET,EAAG,qDAAqD,uBAAuB;OACzE,CAAA;MAEX,CAAA;KACE,CAAA;IAEJ,CAAA;GAGN,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD,EAA6B,UAAS,CAAA;IAClC,CAAA;GAGN,kBAAC,GAAD;IAAqB,QAAQ;IAAmB;IAAO,eAAe,EAAc,GAAM;IAAI,CAAA;GAG7F,KACC,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,MAAK;KAAS,UAAU;KAAG,YAAY,MAAM;AAAE,OAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,SAAO,EAAE,gBAAgB,EAAS,EAAiB,GAAM;;KAAU,WAAU;KAAiC,eAAe,EAAiB,GAAM;KAAI,CAAA,EAC/O,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,MAAD;QAAI,WAAU;kBACX,EAAG,sDAAsD,qBAAqB;QAC5E,CAAA,EACL,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAiB,GAAM;QACtC,WAAU;kBAEV,kBAAC,GAAD,EAAG,WAAU,8BAA+B,CAAA;QACrC,CAAA,CACL;;MACN,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAU,KAAK,MACd,kBAAC,OAAD;QAAkB,WAAU;kBAA5B,CACE,kBAAC,QAAD;SAAM,WAAU;mBAA+B,EAAE;SAAmB,CAAA,EACpE,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAE;SACC,CAAA,CACF;UALI,EAAE,KAKN,CACN;OACE,CAAA;MACN,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAiB,GAAM;QACtC,WAAU;kBAET,EAAG,0CAA0C,QAAQ;QAC/C,CAAA;OACL,CAAA;MACF;OACF;;GAEJ"}
|
|
1
|
+
{"version":3,"file":"AgentManagerCenter.js","names":[],"sources":["../../../src/components/agent-manager/AgentManagerCenter.tsx"],"sourcesContent":["/**\n * Center container for the Agent Manager.\n *\n * Renders the tab bar at top and the active session's chat pane below.\n * Shows a welcome/empty state when no tabs are open.\n */\n\nimport { useState, useCallback, useEffect, useRef, type FC } from 'react';\nimport { MessageSquarePlus, X } from 'lucide-react';\nimport { useAgentManagerStore } from '@/store/agentManagerStore';\nimport { EmptyState } from '@/components/shared';\nimport { AgentManagerTabBar } from './AgentManagerTabBar';\nimport { AgentManagerChatPane } from './AgentManagerChatPane';\nimport { AgentManagerAdminBar } from './AgentManagerAdminBar';\nimport { SessionSearchDialog } from './dialogs/SessionSearchDialog';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { createSession, type AgentRef } from '@/services/aiApi';\n\n/* ────────────────────────────────────────────────────────────────────── */\n/* Keyboard shortcuts reference */\n/* ────────────────────────────────────────────────────────────────────── */\n\nconst SHORTCUTS: Array<{ keys: string; description: string }> = [\n { keys: 'Ctrl/Cmd+Shift+N', description: 'New session' },\n { keys: 'Ctrl/Cmd+Shift+K', description: 'Search sessions' },\n { keys: 'Ctrl/Cmd+Shift+F', description: 'In-session search' },\n { keys: 'Escape', description: 'Cancel / close modal' },\n { keys: 'Enter', description: 'Send message' },\n { keys: 'Shift+Enter', description: 'New line' },\n];\n\ninterface AgentManagerCenterProps {\n agent: AgentRef;\n}\n\nexport const AgentManagerCenter: FC<AgentManagerCenterProps> = ({ agent }) => {\n const tabs = useAgentManagerStore((s) => s.tabs);\n const activeTabSessionId = useAgentManagerStore((s) => s.activeTabSessionId);\n const openSession = useAgentManagerStore((s) => s.openSession);\n const setActiveTab = useAgentManagerStore((s) => s.setActiveTab);\n const tr = useTr();\n\n const [searchOpen, setSearchOpen] = useState(false);\n const [shortcutsOpen, setShortcutsOpen] = useState(false);\n const rootRef = useRef<HTMLDivElement>(null);\n\n const bumpSessionsRefresh = useAgentManagerStore((s) => s.bumpSessionsRefresh);\n const handleCreateSession = useCallback(async () => {\n try {\n const session = await createSession(agent, {\n name: `Session ${tabs.length + 1}`,\n agentType: 'claude',\n config: { model: 'default', mode: 'cli', tags: [] },\n });\n openSession(session.id, session.name);\n bumpSessionsRefresh();\n } catch (err) {\n console.error('[AgentManagerCenter] createSession failed:', err);\n }\n }, [agent, tabs.length, openSession, bumpSessionsRefresh]);\n\n const handleOpenSearch = useCallback(() => setSearchOpen(true), []);\n\n useEffect(() => {\n if (activeTabSessionId && tabs.some((tab) => tab.sessionId === activeTabSessionId)) {\n setActiveTab(activeTabSessionId);\n }\n }, [activeTabSessionId, setActiveTab, tabs]);\n\n // Wire the shortcuts advertised in the SHORTCUTS modal. Scoped to the\n // Agent Manager container so they don't clash with shortcuts on other\n // pages. Each shortcut uses Shift to avoid colliding with built-in\n // browser shortcuts (Ctrl+N opens a window, Ctrl+F opens find).\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n const handler = (e: KeyboardEvent) => {\n if (!root.contains(document.activeElement) && document.activeElement !== root) return;\n const mod = e.ctrlKey || e.metaKey;\n if (mod && e.shiftKey && (e.key === 'N' || e.key === 'n')) {\n e.preventDefault();\n void handleCreateSession();\n return;\n }\n if (mod && e.shiftKey && (e.key === 'K' || e.key === 'k')) {\n e.preventDefault();\n handleOpenSearch();\n return;\n }\n if (mod && e.shiftKey && (e.key === 'F' || e.key === 'f')) {\n e.preventDefault();\n const inSession = root.querySelector(\n '[data-testid=\"agent-manager-in-session-search\"]'\n ) as HTMLButtonElement | null;\n inSession?.click();\n }\n };\n document.addEventListener('keydown', handler);\n return () => document.removeEventListener('keydown', handler);\n }, [handleCreateSession, handleOpenSearch]);\n\n return (\n <div ref={rootRef} className=\"flex flex-col h-full min-h-0\" tabIndex={-1}>\n {/* Tab bar — occupies full row width, search/help live inside the tabBar area */}\n <AgentManagerTabBar\n agent={agent}\n onSearch={handleOpenSearch}\n onShowShortcuts={() => setShortcutsOpen(true)}\n />\n\n {/* Active session or empty state */}\n <div className=\"flex-1 min-h-0 overflow-hidden\">\n {activeTabSessionId && tabs.some((t) => t.sessionId === activeTabSessionId) ? (\n <AgentManagerChatPane\n key={activeTabSessionId}\n sessionId={activeTabSessionId}\n agentId={agent.id}\n />\n ) : (\n <div className=\"flex-1 flex items-center justify-center h-full\">\n <EmptyState\n icon={<MessageSquarePlus className=\"size-8 text-text-secondary\" />}\n title={tr('vibecontrols.agentManager.center.noActiveSession', 'No active session')}\n description={tr(\n 'vibecontrols.agentManager.center.createSessionDesc',\n 'Create a new session to start interacting with the AI agent.'\n )}\n action={\n <button\n type=\"button\"\n onClick={handleCreateSession}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('vibecontrols.agentManager.center.createNewSession', 'Create a new session')}\n </button>\n }\n />\n </div>\n )}\n </div>\n\n {/* Admin bar at bottom */}\n <div className=\"flex-shrink-0\">\n <AgentManagerAdminBar agent={agent} />\n </div>\n\n {/* Session search dialog */}\n <SessionSearchDialog isOpen={searchOpen} agent={agent} onClose={() => setSearchOpen(false)} />\n\n {/* Keyboard shortcuts modal */}\n {shortcutsOpen && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n <div\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (() => setShortcutsOpen(false))();\n }\n }}\n className=\"absolute inset-0 bg-bg-overlay\"\n onClick={() => setShortcutsOpen(false)}\n />\n <div className=\"relative w-full max-w-sm mx-4 rounded-lg border border-border-default bg-bg-surface shadow-xl\">\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-default\">\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {tr('vibecontrols.agentManager.center.keyboardShortcuts', 'Keyboard Shortcuts')}\n </h3>\n <button\n type=\"button\"\n onClick={() => setShortcutsOpen(false)}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n >\n <X className=\"size-5 text-text-secondary\" />\n </button>\n </div>\n <div className=\"px-6 py-4 space-y-2\">\n {SHORTCUTS.map((s) => (\n <div key={s.keys} className=\"flex items-center justify-between py-1\">\n <span className=\"text-sm text-text-secondary\">{s.description}</span>\n <kbd className=\"px-2 py-0.5 text-xs font-mono rounded bg-bg-elevated border border-border-default text-text-primary\">\n {s.keys}\n </kbd>\n </div>\n ))}\n </div>\n <div className=\"px-6 py-3 border-t border-border-default bg-bg-sunken rounded-b-lg\">\n <button\n type=\"button\"\n onClick={() => setShortcutsOpen(false)}\n className=\"w-full px-4 py-2 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-elevated transition-colors\"\n >\n {tr('vibecontrols.agentManager.center.close', 'Close')}\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|