@burdenoff/microfe-vibecontrols 2026.526.6 → 2026.526.7
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionDetailsPage.js","names":[],"sources":["../../src/pages/SessionDetailsPage.tsx"],"sourcesContent":["import { useState, useCallback, useEffect } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport { useParams } from 'react-router-dom';\nimport { useVibeControlsStore } from '@/store/vibeControlsStore';\nimport {\n ArrowLeft,\n Terminal,\n Play,\n Square,\n RotateCcw,\n ExternalLink,\n Server,\n Zap,\n Clock,\n Folder,\n Code,\n Variable,\n Hash,\n RefreshCw,\n Loader2,\n AlertCircle,\n Maximize2,\n Minimize2,\n ChevronDown,\n ChevronUp,\n Pencil,\n Check,\n X,\n Tag,\n} from 'lucide-react';\nimport {\n PageHeader,\n Card,\n StatusBadge,\n EmptyState,\n ErrorState,\n ConfirmDialog,\n} from '@/components/shared';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { useVibeControls, useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport {\n useGetSessionQuery,\n useStartSessionMutation,\n useStopSessionMutation,\n useRestartSessionMutation,\n useStartSessionTerminalMutation,\n useRestartSessionTerminalMutation,\n useUpdateSessionMutation,\n} from '@/generated/wspace-operations';\nimport { getQuotaDialogContent, isQuotaExhaustedError } from '@/utils/quotaUtils';\n\n/**\n * Resolves the terminal URL for a session.\n * Each session gets its own ttyd instance + cloudflared tunnel.\n * The URL is set by the backend's startSessionTerminal mutation.\n */\nfunction getTerminalUrl(session: { ttydUrl?: string | null }): string | null {\n return session.ttydUrl || null;\n}\n\n/**\n * Session details page - shows comprehensive information about a single session\n * with an integrated terminal panel.\n */\nexport function SessionDetailsPage() {\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n const { id } = useParams<{ id: string }>();\n const { basePath: basePathProp } = useVibeControls();\n const basePath = basePathProp || '';\n const navigate = useVibeNavigate();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n\n const [startSession] = useStartSessionMutation({ refetchQueries: ['GetSession'] });\n const [stopSession] = useStopSessionMutation({ refetchQueries: ['GetSession'] });\n const [restartSession] = useRestartSessionMutation({ refetchQueries: ['GetSession'] });\n const [startSessionTerminal] = useStartSessionTerminalMutation({\n refetchQueries: ['GetSession'],\n });\n const [restartSessionTerminal] = useRestartSessionTerminalMutation({\n refetchQueries: ['GetSession'],\n });\n const [updateSession] = useUpdateSessionMutation({ refetchQueries: ['GetSession'] });\n\n // Command/working-directory inline edit state\n const [editingCommand, setEditingCommand] = useState(false);\n const [editCommand, setEditCommand] = useState('');\n const [editWorkingDirectory, setEditWorkingDirectory] = useState('');\n const [editSaving, setEditSaving] = useState(false);\n\n const handleEditCommandOpen = () => {\n setEditCommand(session?.command || '');\n setEditWorkingDirectory(session?.workingDirectory || '');\n setEditingCommand(true);\n };\n\n const handleEditCommandSave = async () => {\n if (!session?.id) return;\n setEditSaving(true);\n try {\n await updateSession({\n variables: {\n id: session.id,\n input: {\n command: editCommand || null,\n workingDirectory: editWorkingDirectory || null,\n },\n },\n });\n setEditingCommand(false);\n } finally {\n setEditSaving(false);\n }\n };\n\n // Fetch session by ID from GraphQL API\n const { data, loading, error, refetch } = useGetSessionQuery({\n variables: { id: id || '' },\n skip: !id,\n fetchPolicy: 'cache-and-network',\n });\n\n // Get session from response\n const session = data?.session;\n\n // Get related agent and vibe from the session response\n const agent = session?.agent || null;\n const vibe = session?.vibe || null;\n\n // ── Sync active context ────────────────────────────────────────────────\n useEffect(() => {\n if (session) {\n useVibeControlsStore.setState({\n activeAgentId: (agent?.id as string) ?? null,\n selectedAgentId: (agent?.id as string) ?? null,\n activeVibeId: (vibe?.id as string) ?? null,\n selectedVibeId: (vibe?.id as string) ?? null,\n activeSessionId: session.id,\n selectedSessionId: session.id,\n });\n }\n }, [session?.id, agent?.id, vibe?.id]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // Terminal state\n const [terminalOpen, setTerminalOpen] = useState(true);\n const [terminalMaximized, setTerminalMaximized] = useState(false);\n const [terminalLoading, setTerminalLoading] = useState(false);\n const [terminalError, setTerminalError] = useState<string | null>(null);\n const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);\n\n const terminalUrl = session ? getTerminalUrl(session) : null;\n\n const handleBack = () => {\n navigate('/sessions');\n };\n\n const handleStartSession = async () => {\n if (!session?.id) return;\n try {\n await startSession({ variables: { id: session.id } });\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setShowQuotaExhausted(true);\n return;\n }\n\n console.error('Failed to start session:', err);\n }\n };\n\n const handleStopSession = async () => {\n if (!session?.id) return;\n try {\n await stopSession({ variables: { id: session.id } });\n } catch (err) {\n console.error('Failed to stop session:', err);\n }\n };\n\n const handleRestartSession = async () => {\n if (!session?.id) return;\n try {\n await restartSession({ variables: { id: session.id } });\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setShowQuotaExhausted(true);\n return;\n }\n\n console.error('Failed to restart session:', err);\n }\n };\n\n const handleStartTerminal = useCallback(async () => {\n if (!session) return;\n setTerminalLoading(true);\n setTerminalError(null);\n try {\n // Call the backend mutation which orchestrates agent API calls server-side\n // (creates tmux session, starts ttyd, starts cloudflared tunnel, saves ttydUrl)\n const result = await startSessionTerminal({ variables: { id: session.id } });\n if (result.error && isQuotaExhaustedError(result.error)) {\n setShowQuotaExhausted(true);\n } else if (result.error) {\n setTerminalError(result.error.message);\n } else if (!result.data?.startSessionTerminal) {\n setTerminalError('Failed to start terminal');\n } else {\n setTerminalOpen(true);\n }\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setShowQuotaExhausted(true);\n return;\n }\n\n const message = err instanceof Error ? err.message : 'Failed to start terminal';\n setTerminalError(message);\n } finally {\n setTerminalLoading(false);\n }\n }, [session, startSessionTerminal]);\n\n const handleRestartTerminal = useCallback(async () => {\n if (!session) return;\n setTerminalLoading(true);\n setTerminalError(null);\n try {\n const result = await restartSessionTerminal({ variables: { id: session.id } });\n if (result.error && isQuotaExhaustedError(result.error)) {\n setShowQuotaExhausted(true);\n } else if (result.error) {\n setTerminalError(result.error.message);\n } else if (!result.data?.restartSessionTerminal) {\n setTerminalError('Failed to restart terminal');\n } else {\n setTerminalOpen(true);\n }\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setShowQuotaExhausted(true);\n return;\n }\n\n const message = err instanceof Error ? err.message : 'Failed to restart terminal';\n setTerminalError(message);\n } finally {\n setTerminalLoading(false);\n }\n }, [session, restartSessionTerminal]);\n\n const handleOpenInNewTab = useCallback(() => {\n if (terminalUrl) {\n window.open(terminalUrl, '_blank');\n }\n }, [terminalUrl]);\n\n if (permissionsLoading)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <p className=\"text-text-secondary\">\n {tr('sessionDetails.loadingPermissions', 'Loading permissions...')}\n </p>\n </div>\n );\n if (!hasPermission(VIBE_PERMISSIONS.SESSION_READ))\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold text-foreground\">\n {tr('sessionDetails.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'sessionDetails.accessDeniedDesc',\n \"You don't have permission to access this page.\"\n )}\n </p>\n </div>\n </div>\n );\n\n const handleAgentClick = () => {\n if (!agent) return;\n navigate(`/agents/${agent.id}`);\n };\n\n const handleVibeClick = () => {\n if (!vibe) return;\n navigate(`/vibes/${vibe.id}`);\n };\n\n const formatTimestamp = (timestamp: string | null | undefined) => {\n if (!timestamp) return 'Never';\n const diff = Date.now() - new Date(timestamp).getTime();\n if (diff < 60000) return 'Just now';\n if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;\n if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`;\n return new Date(timestamp).toLocaleDateString();\n };\n\n // Guard states wrapped in MotionPage to prevent unmount/remount animation replay\n if (loading && !session) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {tr('sessionDetails.backToSessions', 'Back to Sessions')}\n </button>\n <PageSkeleton variant=\"detail\" />\n </MotionPage>\n );\n }\n\n if (error && !session) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n Back to Sessions\n </button>\n <ErrorState\n title={tr('sessionDetails.failedLoad', 'Failed to load session')}\n message={error.message}\n onRetry={() => refetch()}\n />\n </MotionPage>\n );\n }\n\n if (!session) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n Back to Sessions\n </button>\n <EmptyState\n icon={<Terminal className=\"w-8 h-8 text-text-secondary\" />}\n title={tr('sessionDetails.notFound', 'Session not found')}\n description={`${tr('sessionDetails.notFoundDescPrefix', 'No session with ID')} \"${id}\" ${tr('sessionDetails.notFoundDescSuffix', 'exists')}`}\n action={\n <button\n onClick={handleBack}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('sessionDetails.backToSessions', 'Back to Sessions')}\n </button>\n }\n />\n </MotionPage>\n );\n }\n\n const isRunning = session.status === 'RUNNING';\n const terminalHeight = terminalMaximized ? 'h-[calc(100vh-200px)]' : 'h-[280px] sm:h-[420px]';\n\n return (\n <MotionPage className=\"flex flex-col bg-bg-surface min-h-screen\">\n {/* Main content - scrollable */}\n <div\n className={`flex-1 overflow-y-auto space-y-6 p-4 sm:p-6 ${terminalOpen && terminalUrl ? 'pb-2' : ''}`}\n >\n {/* Back Button */}\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n Back to Sessions\n </button>\n\n {/* Header */}\n <PageHeader\n title={session.name}\n description={session.command || 'No command specified'}\n breadcrumb={[\n { label: 'Sessions', path: basePath ? `${basePath}/sessions` : '/sessions' },\n { label: session.name },\n ]}\n actions={\n <div className=\"flex items-center gap-2 flex-wrap\">\n <button\n onClick={() => refetch()}\n disabled={loading}\n className=\"flex items-center gap-2 px-3 py-2 bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken/80 transition-colors disabled:opacity-50\"\n title=\"Refresh\"\n >\n <RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n {isRunning ? (\n <>\n <button\n onClick={handleStopSession}\n className=\"flex items-center gap-2 px-3 py-2 bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken/80 transition-colors\"\n >\n <Square className=\"w-4 h-4\" />\n {tr('sessionDetails.stopCommand', 'Stop Command')}\n </button>\n <button\n onClick={handleRestartSession}\n className=\"flex items-center gap-2 px-3 py-2 bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken/80 transition-colors\"\n >\n <RotateCcw className=\"w-4 h-4\" />\n {tr('sessionDetails.restartCommand', 'Restart Command')}\n </button>\n </>\n ) : (\n <button\n onClick={handleStartSession}\n className=\"flex items-center gap-2 px-3 py-2 bg-status-success-bg text-status-success-text rounded-md hover:opacity-80 transition-colors\"\n >\n <Play className=\"w-4 h-4\" />\n {tr('sessionDetails.runCommand', 'Run Command')}\n </button>\n )}\n {/* Terminal toggle + open in new tab */}\n {terminalUrl ? (\n <>\n <button\n onClick={() => setTerminalOpen((prev) => !prev)}\n className={`flex items-center gap-2 px-3 py-2 rounded-md transition-colors ${\n terminalOpen\n ? 'bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover'\n : 'bg-bg-sunken text-text-primary hover:bg-bg-sunken/80'\n }`}\n title={\n terminalOpen\n ? tr('sessionDetails.hideTerminal', 'Hide terminal')\n : tr('sessionDetails.showTerminal', 'Show terminal')\n }\n >\n <Terminal className=\"w-4 h-4\" />\n {tr('sessionDetails.terminal', 'Terminal')}\n </button>\n <button\n onClick={handleRestartTerminal}\n disabled={terminalLoading}\n className=\"flex items-center px-2 py-2 bg-bg-sunken text-text-secondary rounded-md hover:text-text-primary hover:bg-bg-sunken/80 transition-colors disabled:opacity-50\"\n title={tr(\n 'sessionDetails.restartTerminalTitle',\n 'Restart terminal (new tunnel)'\n )}\n >\n {terminalLoading ? (\n <Loader2 className=\"w-3.5 h-3.5 animate-spin\" />\n ) : (\n <RotateCcw className=\"w-3.5 h-3.5\" />\n )}\n </button>\n <button\n onClick={handleOpenInNewTab}\n className=\"flex items-center px-2 py-2 bg-bg-sunken text-text-secondary rounded-md hover:text-text-primary hover:bg-bg-sunken/80 transition-colors\"\n title={tr('sessionDetails.openInNewTab', 'Open terminal in new tab')}\n >\n <ExternalLink className=\"w-4 h-4\" />\n </button>\n </>\n ) : (\n <button\n onClick={handleStartTerminal}\n disabled={terminalLoading || !agent}\n className=\"flex items-center gap-2 px-3 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n title={\n !agent\n ? tr('sessionDetails.noAgentAssigned', 'No agent assigned')\n : !agent.tunnelUrl\n ? tr(\n 'sessionDetails.agentNoTunnelUrl',\n 'Agent has no tunnel URL configured'\n )\n : tr('sessionDetails.openTerminal', 'Open terminal')\n }\n >\n {terminalLoading ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : (\n <Terminal className=\"w-4 h-4\" />\n )}\n {tr('sessionDetails.openTerminal', 'Open Terminal')}\n </button>\n )}\n </div>\n }\n />\n\n {/* Status Cards */}\n <MotionList stagger={0.04} className=\"grid grid-cols-2 sm:grid-cols-4 gap-4\">\n <MotionItem>\n <Card className=\"p-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-10 h-10 rounded-lg bg-bg-sunken flex items-center justify-center\">\n <Terminal className=\"w-5 h-5 text-text-secondary\" />\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {tr('sessionDetails.statusLabel', 'Status')}\n </p>\n <StatusBadge status={session.status} size=\"md\" />\n </div>\n </div>\n </Card>\n </MotionItem>\n <MotionItem>\n <Card className=\"p-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-10 h-10 rounded-lg bg-bg-sunken flex items-center justify-center\">\n <Code className=\"w-5 h-5 text-text-secondary\" />\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {tr('sessionDetails.typeLabel', 'Type')}\n </p>\n <p className=\"font-semibold text-text-primary\">{session.type}</p>\n </div>\n </div>\n </Card>\n </MotionItem>\n <MotionItem>\n <Card className=\"p-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-10 h-10 rounded-lg bg-bg-sunken flex items-center justify-center\">\n <Hash className=\"w-5 h-5 text-text-secondary\" />\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {tr('sessionDetails.pidLabel', 'PID')}\n </p>\n <p className=\"font-semibold text-text-primary\">{session.pid || 'N/A'}</p>\n </div>\n </div>\n </Card>\n </MotionItem>\n <MotionItem>\n <Card className=\"p-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-10 h-10 rounded-lg bg-bg-sunken flex items-center justify-center\">\n <Server className=\"w-5 h-5 text-text-secondary\" />\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {tr('sessionDetails.agentLabel', 'Agent')}\n </p>\n <p className=\"font-semibold text-text-primary truncate\">\n {agent?.name || 'Unknown'}\n </p>\n </div>\n </div>\n </Card>\n </MotionItem>\n </MotionList>\n\n {/* Command */}\n <Card>\n <div className=\"p-4 border-b border-border-default flex items-center justify-between\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Code className=\"w-5 h-5\" />\n {tr('sessionDetails.commandTitle', 'Command')}\n </h3>\n {!editingCommand && (\n <button\n onClick={handleEditCommandOpen}\n className=\"p-1.5 rounded text-text-secondary hover:text-text-primary hover:bg-bg-sunken transition-colors\"\n title={tr('sessionDetails.editCommandTitle', 'Edit command and working directory')}\n >\n <Pencil className=\"w-4 h-4\" />\n </button>\n )}\n </div>\n <div className=\"p-4\">\n {editingCommand ? (\n <div className=\"space-y-3\">\n <div>\n <label className=\"block text-xs font-medium text-text-secondary mb-1\">\n {tr('sessionDetails.commandLabel', 'Command')}\n </label>\n <input\n type=\"text\"\n value={editCommand}\n onChange={(e) => setEditCommand(e.target.value)}\n placeholder={tr('sessionDetails.commandPlaceholder', 'bun run dev')}\n className=\"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 font-mono\"\n autoFocus\n />\n </div>\n <div>\n <label className=\"block text-xs font-medium text-text-secondary mb-1\">\n {tr('sessionDetails.workingDirLabel', 'Working Directory')}\n </label>\n <input\n type=\"text\"\n value={editWorkingDirectory}\n onChange={(e) => setEditWorkingDirectory(e.target.value)}\n placeholder={tr('sessionDetails.workingDirPlaceholder', '/home/user/myproject')}\n className=\"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 font-mono\"\n />\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n onClick={handleEditCommandSave}\n disabled={editSaving}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n {editSaving ? (\n <Loader2 className=\"w-3.5 h-3.5 animate-spin\" />\n ) : (\n <Check className=\"w-3.5 h-3.5\" />\n )}\n {tr('sessionDetails.save', 'Save')}\n </button>\n <button\n onClick={() => setEditingCommand(false)}\n disabled={editSaving}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-sm bg-bg-sunken text-text-primary rounded-md hover:bg-bg-elevated transition-colors disabled:opacity-50\"\n >\n <X className=\"w-3.5 h-3.5\" />\n {tr('sessionDetails.cancel', 'Cancel')}\n </button>\n </div>\n </div>\n ) : (\n <>\n <div className=\"bg-bg-sunken rounded-md p-3 font-mono text-sm text-text-primary overflow-x-auto\">\n ${' '}\n {session.command ||\n tr('sessionDetails.noCommandSpecified', 'No command specified')}\n </div>\n <div className=\"mt-3 flex items-center gap-2 text-sm text-text-secondary\">\n <Folder className=\"w-4 h-4\" />\n {tr('sessionDetails.workingDirectory', 'Working Directory')}:{' '}\n <span className=\"text-text-primary font-mono\">\n {session.workingDirectory || (\n <em className=\"not-italic text-text-placeholder\">\n {tr('sessionDetails.workingDirDefault', 'not set — defaults to OS home')}\n </em>\n )}\n </span>\n </div>\n </>\n )}\n </div>\n </Card>\n\n {/* Environment Variables */}\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Variable className=\"w-5 h-5\" />\n {tr('sessionDetails.envVars', 'Environment Variables')}\n </h3>\n </div>\n <div className=\"p-4\">\n {session.environmentVariables &&\n Object.keys(session.environmentVariables).length > 0 ? (\n <div className=\"flex flex-wrap gap-2\">\n {Object.entries(session.environmentVariables).map(([key, value]) => (\n <span\n key={key}\n className=\"px-3 py-1 bg-bg-sunken text-text-primary text-sm rounded-full font-mono\"\n >\n {key}={String(value)}\n </span>\n ))}\n </div>\n ) : (\n <p className=\"text-text-secondary\">\n {tr('sessionDetails.noEnvVars', 'No environment variables configured')}\n </p>\n )}\n </div>\n </Card>\n\n {/* Last Output */}\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Terminal className=\"w-5 h-5\" />\n {tr('sessionDetails.lastOutput', 'Last Output')}\n </h3>\n </div>\n <div className=\"p-4\">\n {session.lastOutput ? (\n <div className=\"bg-bg-sunken rounded-md p-3 font-mono text-sm text-text-primary overflow-x-auto whitespace-pre-wrap\">\n {session.lastOutput}\n </div>\n ) : (\n <p className=\"text-text-secondary\">\n {tr('sessionDetails.noOutput', 'No output captured')}\n </p>\n )}\n </div>\n </Card>\n\n {/* Related Vibe */}\n {vibe && (\n <Card onClick={handleVibeClick} hoverable>\n <div className=\"p-4 flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-10 h-10 rounded-lg bg-bg-sunken flex items-center justify-center\">\n <Zap className=\"w-5 h-5 text-text-secondary\" />\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {tr('sessionDetails.relatedVibe', 'Related Vibe')}\n </p>\n <p className=\"font-medium text-text-primary\">{vibe.name}</p>\n {vibe.path && (\n <p className=\"text-sm text-text-secondary truncate max-w-md\">{vibe.path}</p>\n )}\n </div>\n </div>\n <StatusBadge status={vibe.status} />\n </div>\n </Card>\n )}\n\n {/* Related Agent */}\n {agent && (\n <Card onClick={handleAgentClick} hoverable>\n <div className=\"p-4 flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-10 h-10 rounded-lg bg-bg-sunken flex items-center justify-center\">\n <Server className=\"w-5 h-5 text-text-secondary\" />\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {tr('sessionDetails.runningOnAgent', 'Running on Agent')}\n </p>\n <p className=\"font-medium text-text-primary\">{agent.name}</p>\n <p className=\"text-sm text-text-secondary\">{agent.hostname}</p>\n </div>\n </div>\n <StatusBadge status={agent.isActive ? 'ACTIVE' : 'OFFLINE'} />\n </div>\n </Card>\n )}\n\n {/* Metadata */}\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Clock className=\"w-5 h-5\" />\n {tr('sessionDetails.timeline', 'Timeline')}\n </h3>\n </div>\n <div className=\"p-4\">\n <div className=\"grid grid-cols-1 sm:grid-cols-3 gap-4 text-sm\">\n <div>\n <p className=\"text-text-secondary\">\n {tr('sessionDetails.autoStart', 'Auto Start')}\n </p>\n <p className=\"font-medium text-text-primary\">\n {session.autoStart\n ? tr('sessionDetails.yes', 'Yes')\n : tr('sessionDetails.no', 'No')}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">{tr('sessionDetails.created', 'Created')}</p>\n <p className=\"font-medium text-text-primary\">\n {formatTimestamp(session.createdAt)}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">{tr('sessionDetails.updated', 'Updated')}</p>\n <p className=\"font-medium text-text-primary\">\n {formatTimestamp(session.updatedAt)}\n </p>\n </div>\n </div>\n {session.exitCode !== null && session.exitCode !== undefined && (\n <div className=\"mt-4 pt-4 border-t border-border-default\">\n <p className=\"text-sm text-text-secondary\">\n {tr('sessionDetails.exitCode', 'Exit Code')}\n </p>\n <p\n className={`font-medium ${session.exitCode === 0 ? 'text-status-success-text' : 'text-status-error-text'}`}\n >\n {session.exitCode}\n </p>\n </div>\n )}\n </div>\n </Card>\n\n {/* Tags */}\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Tag className=\"w-5 h-5\" />\n {tr('sessionDetails.tags', 'Tags')}\n </h3>\n </div>\n <div className=\"p-4\">\n <EntityTagPicker entityType=\"VIBECONTROLS_SESSION\" entityId={session.id} />\n </div>\n </Card>\n </div>\n\n {/* Integrated Terminal Panel - docked at bottom */}\n {terminalOpen && (\n <div className=\"border-t border-border-default bg-bg-surface shrink-0\">\n {/* Terminal header bar */}\n <div className=\"flex items-center bg-bg-sunken border-b border-border-default\">\n <button\n onClick={() => setTerminalOpen(false)}\n className=\"flex items-center gap-1.5 px-3 py-2 text-text-secondary hover:text-text-primary transition-colors text-xs font-medium uppercase tracking-wide\"\n title={tr('sessionDetails.collapseTerminal', 'Collapse terminal')}\n >\n <Terminal className=\"w-3.5 h-3.5\" />\n {tr('sessionDetails.terminal', 'Terminal')}\n <ChevronDown className=\"w-3 h-3\" />\n </button>\n <div className=\"flex items-center px-3 py-1.5 text-xs bg-bg-surface text-text-primary border-r border-border-subtle border-b-2 border-b-action-primary-bg\">\n <Terminal className=\"w-3 h-3 mr-1.5\" />\n {session.name}\n </div>\n <div className=\"flex-1\" />\n <div className=\"flex items-center gap-1 px-2\">\n {terminalUrl && (\n <button\n onClick={handleOpenInNewTab}\n className=\"p-1 text-text-secondary hover:text-text-primary transition-colors\"\n title={tr('sessionDetails.openInNewTabShort', 'Open in new tab')}\n >\n <ExternalLink className=\"w-3.5 h-3.5\" />\n </button>\n )}\n <button\n onClick={() => setTerminalMaximized((prev) => !prev)}\n className=\"p-1 text-text-secondary hover:text-text-primary transition-colors\"\n title={\n terminalMaximized\n ? tr('sessionDetails.restoreSize', 'Restore size')\n : tr('sessionDetails.maximize', 'Maximize')\n }\n >\n {terminalMaximized ? (\n <Minimize2 className=\"w-3.5 h-3.5\" />\n ) : (\n <Maximize2 className=\"w-3.5 h-3.5\" />\n )}\n </button>\n </div>\n </div>\n\n {/* Terminal content */}\n <div className={`${terminalHeight} bg-bg-inverse text-text-inverse`}>\n {terminalLoading ? (\n <div className=\"flex items-center justify-center h-full\">\n <div className=\"text-center\">\n <Loader2 className=\"w-8 h-8 text-text-secondary animate-spin mx-auto mb-3\" />\n <p className=\"text-text-secondary text-sm\">\n {tr('sessionDetails.startingTerminal', 'Starting terminal for')} {session.name}\n ...\n </p>\n <p className=\"text-text-tertiary text-xs mt-1\">\n {tr(\n 'sessionDetails.settingUpTunnel',\n 'Setting up tmux + ttyd + cloudflared tunnel'\n )}\n </p>\n </div>\n </div>\n ) : terminalError ? (\n <div className=\"flex items-center justify-center h-full\">\n <div className=\"text-center max-w-md\">\n <AlertCircle className=\"w-8 h-8 text-status-error-text mx-auto mb-3\" />\n <p className=\"text-status-error-text text-sm font-medium\">\n {tr('sessionDetails.failedStartTerminal', 'Failed to start terminal')}\n </p>\n <p className=\"text-text-secondary text-xs mt-2\">{terminalError}</p>\n <button\n onClick={handleStartTerminal}\n className=\"mt-4 px-4 py-2 bg-bg-muted text-text-primary text-sm rounded-md hover:bg-bg-sunken transition-colors\"\n >\n {tr('sessionDetails.retry', 'Retry')}\n </button>\n </div>\n </div>\n ) : terminalUrl ? (\n <iframe\n src={terminalUrl}\n title={`Terminal - ${session.name}`}\n className=\"w-full h-full border-0\"\n allow=\"clipboard-read; clipboard-write\"\n />\n ) : (\n <div className=\"flex items-center justify-center h-full\">\n <div className=\"text-center\">\n <Terminal className=\"w-8 h-8 text-text-tertiary mx-auto mb-3\" />\n <p className=\"text-text-secondary text-sm\">\n {tr('sessionDetails.noTerminalRunning', 'No terminal running')}\n </p>\n <p className=\"text-text-tertiary text-xs mt-1\">\n {agent\n ? tr(\n 'sessionDetails.clickOpenTerminalHint',\n 'Click \"Open Terminal\" to create a tmux session with a browser terminal.'\n )\n : tr(\n 'sessionDetails.noAgentForSession',\n 'No agent is assigned to this session.'\n )}\n </p>\n {agent && (\n <button\n onClick={handleStartTerminal}\n disabled={terminalLoading}\n className=\"mt-4 px-4 py-2 bg-bg-muted text-text-primary text-sm rounded-md hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n {terminalLoading\n ? tr('sessionDetails.opening', 'Opening...')\n : tr('sessionDetails.openTerminal', 'Open Terminal')}\n </button>\n )}\n </div>\n </div>\n )}\n </div>\n </div>\n )}\n\n {/* Terminal collapsed indicator - show a clickable bar */}\n {!terminalOpen && (terminalUrl || terminalError) && (\n <div className=\"border-t border-border-default bg-bg-sunken shrink-0\">\n <button\n onClick={() => setTerminalOpen(true)}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-text-secondary hover:text-text-primary transition-colors text-xs font-medium uppercase tracking-wide w-full\"\n title={tr('sessionDetails.expandTerminal', 'Expand terminal')}\n >\n <Terminal className=\"w-3.5 h-3.5\" />\n {tr('sessionDetails.terminal', 'Terminal')} - {session.name}\n <ChevronUp className=\"w-3 h-3 ml-1\" />\n </button>\n </div>\n )}\n\n <ConfirmDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing');\n }}\n title={getQuotaDialogContent('session').title}\n message={getQuotaDialogContent('session').message}\n confirmLabel=\"View Billing\"\n cancelLabel=\"Dismiss\"\n variant=\"warning\"\n />\n </MotionPage>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SessionDetailsPage.js","names":[],"sources":["../../src/pages/SessionDetailsPage.tsx"],"sourcesContent":["import { useState, useCallback, useEffect, useRef } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport { useParams } from 'react-router-dom';\nimport { useVibeControlsStore } from '@/store/vibeControlsStore';\nimport {\n ArrowLeft,\n Terminal,\n Play,\n Square,\n RotateCcw,\n ExternalLink,\n Server,\n Zap,\n Clock,\n Folder,\n Code,\n Variable,\n Hash,\n RefreshCw,\n Loader2,\n AlertCircle,\n Maximize2,\n Minimize2,\n ChevronDown,\n ChevronUp,\n Pencil,\n Check,\n X,\n Tag,\n} from 'lucide-react';\nimport {\n PageHeader,\n Card,\n StatusBadge,\n EmptyState,\n ErrorState,\n ConfirmDialog,\n} from '@/components/shared';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { useVibeControls, useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport {\n useGetSessionQuery,\n useStartSessionMutation,\n useStopSessionMutation,\n useRestartSessionMutation,\n useStartSessionTerminalMutation,\n useRestartSessionTerminalMutation,\n useUpdateSessionMutation,\n} from '@/generated/wspace-operations';\nimport { getQuotaDialogContent, isQuotaExhaustedError } from '@/utils/quotaUtils';\n\n/**\n * Resolves the terminal URL for a session.\n * Each session gets its own ttyd instance + cloudflared tunnel.\n * The URL is set by the backend's startSessionTerminal mutation.\n */\nfunction getTerminalUrl(session: { ttydUrl?: string | null }): string | null {\n return session.ttydUrl || null;\n}\n\n// Escape regex meta-chars for use inside `new RegExp(...)`. Pure,\n// no closure state, so hoisted to module scope to avoid re-creating\n// the function on every `tr()` invocation.\nconst escapeRe = (s: string) => s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\n/**\n * Session details page - shows comprehensive information about a single session\n * with an integrated terminal panel.\n */\nexport function SessionDetailsPage() {\n const { t } = useI18n();\n // tr(key, fallback, params?) — forwards params to useI18n().t so\n // `{{name}}` placeholders are interpolated. Also interpolates into\n // the fallback when the i18n key isn't in the catalog yet, so a\n // missing key doesn't render literal `{{name}}` text to users.\n const tr = useCallback(\n (key: string, fallback: string, params?: Record<string, string | number>): string => {\n const value = t(key, params);\n if (value !== key) return value;\n if (!params) return fallback;\n // Mirror the i18n layer's `{{key}}` placeholder syntax. Replacer\n // function (not a string) so values containing `$$`/`$&` aren't\n // interpreted as `String.prototype.replace` specials.\n return Object.entries(params).reduce(\n (acc, [k, v]) =>\n acc.replace(new RegExp(`\\\\{\\\\{${escapeRe(k)}\\\\}\\\\}`, 'g'), () => String(v)),\n fallback\n );\n },\n [t]\n );\n const { id } = useParams<{ id: string }>();\n const { basePath: basePathProp } = useVibeControls();\n const basePath = basePathProp || '';\n const navigate = useVibeNavigate();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n\n const [startSession] = useStartSessionMutation({ refetchQueries: ['GetSession'] });\n const [stopSession] = useStopSessionMutation({ refetchQueries: ['GetSession'] });\n const [restartSession] = useRestartSessionMutation({ refetchQueries: ['GetSession'] });\n const [startSessionTerminal] = useStartSessionTerminalMutation({\n refetchQueries: ['GetSession'],\n });\n const [restartSessionTerminal] = useRestartSessionTerminalMutation({\n refetchQueries: ['GetSession'],\n });\n const [updateSession] = useUpdateSessionMutation({ refetchQueries: ['GetSession'] });\n\n // Command/working-directory inline edit state\n const [editingCommand, setEditingCommand] = useState(false);\n const [editCommand, setEditCommand] = useState('');\n const [editWorkingDirectory, setEditWorkingDirectory] = useState('');\n const [editSaving, setEditSaving] = useState(false);\n\n const handleEditCommandOpen = () => {\n setEditCommand(session?.command || '');\n setEditWorkingDirectory(session?.workingDirectory || '');\n setEditingCommand(true);\n };\n\n const handleEditCommandSave = async () => {\n if (!session?.id) return;\n setEditSaving(true);\n try {\n await updateSession({\n variables: {\n id: session.id,\n input: {\n command: editCommand || null,\n workingDirectory: editWorkingDirectory || null,\n },\n },\n });\n setEditingCommand(false);\n } finally {\n setEditSaving(false);\n }\n };\n\n // Fetch session by ID from GraphQL API\n const { data, loading, error, refetch } = useGetSessionQuery({\n variables: { id: id || '' },\n skip: !id,\n fetchPolicy: 'cache-and-network',\n });\n\n // Get session from response\n const session = data?.session;\n\n // Get related agent and vibe from the session response\n const agent = session?.agent || null;\n const vibe = session?.vibe || null;\n\n // ── Sync active context ────────────────────────────────────────────────\n useEffect(() => {\n if (session) {\n useVibeControlsStore.setState({\n activeAgentId: (agent?.id as string) ?? null,\n selectedAgentId: (agent?.id as string) ?? null,\n activeVibeId: (vibe?.id as string) ?? null,\n selectedVibeId: (vibe?.id as string) ?? null,\n activeSessionId: session.id,\n selectedSessionId: session.id,\n });\n }\n }, [session?.id, agent?.id, vibe?.id]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // Terminal state\n const [terminalOpen, setTerminalOpen] = useState(true);\n const [terminalMaximized, setTerminalMaximized] = useState(false);\n const [terminalLoading, setTerminalLoading] = useState(false);\n const [terminalError, setTerminalError] = useState<string | null>(null);\n const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);\n const [showRestartTerminalConfirm, setShowRestartTerminalConfirm] = useState(false);\n const [exitedNotice, setExitedNotice] = useState<{\n exitCode: number | null;\n } | null>(null);\n const prevStatusRef = useRef<string | null>(null);\n\n // Ref target for scrollIntoView when the terminal opens.\n const terminalPanelRef = useRef<HTMLDivElement | null>(null);\n\n // Track first-render + the previous open state so the toggle-driven\n // scroll only fires on a deliberate closed→open transition, not on\n // initial mount (terminalOpen defaults to true).\n const didInitialMountRef = useRef(false);\n const prevTerminalOpenRef = useRef(false);\n\n const terminalUrl = session ? getTerminalUrl(session) : null;\n\n const handleBack = () => {\n navigate('/sessions');\n };\n\n const handleStartSession = async () => {\n if (!session?.id) return;\n try {\n await startSession({ variables: { id: session.id } });\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setShowQuotaExhausted(true);\n return;\n }\n\n console.error('Failed to start session:', err);\n }\n };\n\n const handleStopSession = async () => {\n if (!session?.id) return;\n try {\n await stopSession({ variables: { id: session.id } });\n } catch (err) {\n console.error('Failed to stop session:', err);\n }\n };\n\n const handleRestartSession = async () => {\n if (!session?.id) return;\n try {\n await restartSession({ variables: { id: session.id } });\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setShowQuotaExhausted(true);\n return;\n }\n\n console.error('Failed to restart session:', err);\n }\n };\n\n // Scroll the terminal panel into view as a fire-and-forget op,\n // gated on the panel ref being mounted. Owned by the handler that\n // triggered the action, not by an effect — so a successful\n // start/restart always scrolls exactly once, even when the URL\n // didn't change (no useEffect re-run otherwise).\n const scrollTerminalIntoView = useCallback(() => {\n requestAnimationFrame(() => {\n terminalPanelRef.current?.scrollIntoView({ behavior: 'smooth', block: 'end' });\n });\n }, []);\n\n // Shared bookkeeping for start/restart-terminal: identical try/catch\n // /finally shape, identical quota-exhausted + generic-error handling;\n // only the mutation and the fallback error string differ. Takes the\n // session id as an explicit arg so callers don't need a non-null\n // assertion against `session` — the helper owns the null guard.\n const withTerminalAction = useCallback(\n async <T,>(\n action: (id: string) => Promise<{\n error?: { message: string } | null | undefined;\n data?: T | null | undefined;\n }>,\n dataKey: keyof T,\n fallbackErrorMsg: string\n ) => {\n const id = session?.id;\n if (!id) return;\n setTerminalLoading(true);\n setTerminalError(null);\n let succeeded = false;\n try {\n const result = await action(id);\n if (result.error && isQuotaExhaustedError(result.error)) {\n setShowQuotaExhausted(true);\n } else if (result.error) {\n setTerminalError(result.error.message);\n } else if (!result.data || !result.data[dataKey]) {\n setTerminalError(fallbackErrorMsg);\n } else {\n setTerminalOpen(true);\n succeeded = true;\n }\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setShowQuotaExhausted(true);\n return;\n }\n const message = err instanceof Error ? err.message : fallbackErrorMsg;\n setTerminalError(message);\n } finally {\n setTerminalLoading(false);\n if (succeeded) scrollTerminalIntoView();\n }\n },\n [session?.id, scrollTerminalIntoView]\n );\n\n const handleStartTerminal = useCallback(\n () =>\n withTerminalAction(\n (id) => startSessionTerminal({ variables: { id } }),\n 'startSessionTerminal',\n 'Failed to start terminal'\n ),\n [startSessionTerminal, withTerminalAction]\n );\n\n const handleRestartTerminal = useCallback(\n () =>\n withTerminalAction(\n (id) => restartSessionTerminal({ variables: { id } }),\n 'restartSessionTerminal',\n 'Failed to restart terminal'\n ),\n [restartSessionTerminal, withTerminalAction]\n );\n\n // Scroll on the toggle button — closed→open. User-initiated start/\n // restart paths scroll inline from their handlers (see\n // `scrollTerminalIntoView`), which avoids the stale-flag problem\n // where a no-op success leaves a ref stuck true and a later\n // background refetch fires an unexpected scroll.\n useEffect(() => {\n if (!didInitialMountRef.current) {\n didInitialMountRef.current = true;\n prevTerminalOpenRef.current = terminalOpen;\n return;\n }\n const justOpened = terminalOpen && !prevTerminalOpenRef.current;\n prevTerminalOpenRef.current = terminalOpen;\n if (justOpened) scrollTerminalIntoView();\n }, [terminalOpen, scrollTerminalIntoView]);\n\n // Track the last session whose status we processed, so a route\n // change to a sibling /sessions/:id reseeds `prevStatusRef` with\n // the new session's *current* status — not `null`. Reseeding to\n // null was the bug: if the next status update happened to equal\n // the seed, the transition guard (prev === 'RUNNING'/'STARTING')\n // never matched and the command-exited notice never fired.\n const sessionId = session?.id ?? null;\n const lastProcessedSessionRef = useRef<string | null>(null);\n\n // Surface \"command exited\" inline when the session's status flips\n // to STOPPED while the terminal panel is still open — otherwise\n // the empty iframe looks like a crash.\n useEffect(() => {\n const status = session?.status ?? null;\n const exitCode = session?.exitCode ?? null;\n\n // Session changed: reseed prev to the new session's current\n // status and clear any stale notice. Don't fire a transition on\n // this first observation — we have no \"before\" state yet.\n if (sessionId !== lastProcessedSessionRef.current) {\n lastProcessedSessionRef.current = sessionId;\n prevStatusRef.current = status;\n setExitedNotice(null);\n return;\n }\n\n if (status === 'RUNNING' || status === 'STARTING') {\n // Clear stale notice once the session is running again, then\n // record the status. Mutually exclusive with the STOPPED paths\n // below, so handle it with an early return.\n setExitedNotice(null);\n prevStatusRef.current = status;\n return;\n }\n\n const prev = prevStatusRef.current;\n if (status === 'STOPPED' && (prev === 'RUNNING' || prev === 'STARTING') && terminalOpen) {\n setExitedNotice({ exitCode });\n } else if (status === 'STOPPED' && terminalOpen && exitCode !== null) {\n // exitCode can land in a later GraphQL response than the\n // status transition (poll N returns STOPPED + null code, poll\n // N+1 returns the real code). Patch the existing notice so\n // the banner shows the real code rather than \"(exit null)\".\n setExitedNotice((cur) => (cur && cur.exitCode === null ? { exitCode } : cur));\n }\n prevStatusRef.current = status;\n }, [sessionId, session?.status, session?.exitCode, terminalOpen]);\n\n const handleOpenInNewTab = useCallback(() => {\n if (terminalUrl) {\n window.open(terminalUrl, '_blank');\n }\n }, [terminalUrl]);\n\n if (permissionsLoading)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <p className=\"text-text-secondary\">\n {tr('sessionDetails.loadingPermissions', 'Loading permissions...')}\n </p>\n </div>\n );\n if (!hasPermission(VIBE_PERMISSIONS.SESSION_READ))\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold text-foreground\">\n {tr('sessionDetails.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'sessionDetails.accessDeniedDesc',\n \"You don't have permission to access this page.\"\n )}\n </p>\n </div>\n </div>\n );\n\n const handleAgentClick = () => {\n if (!agent) return;\n navigate(`/agents/${agent.id}`);\n };\n\n const handleVibeClick = () => {\n if (!vibe) return;\n navigate(`/vibes/${vibe.id}`);\n };\n\n const formatTimestamp = (timestamp: string | null | undefined) => {\n if (!timestamp) return 'Never';\n const diff = Date.now() - new Date(timestamp).getTime();\n if (diff < 60000) return 'Just now';\n if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;\n if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`;\n return new Date(timestamp).toLocaleDateString();\n };\n\n // Guard states wrapped in MotionPage to prevent unmount/remount animation replay\n if (loading && !session) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {tr('sessionDetails.backToSessions', 'Back to Sessions')}\n </button>\n <PageSkeleton variant=\"detail\" />\n </MotionPage>\n );\n }\n\n if (error && !session) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n Back to Sessions\n </button>\n <ErrorState\n title={tr('sessionDetails.failedLoad', 'Failed to load session')}\n message={error.message}\n onRetry={() => refetch()}\n />\n </MotionPage>\n );\n }\n\n if (!session) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n Back to Sessions\n </button>\n <EmptyState\n icon={<Terminal className=\"w-8 h-8 text-text-secondary\" />}\n title={tr('sessionDetails.notFound', 'Session not found')}\n description={`${tr('sessionDetails.notFoundDescPrefix', 'No session with ID')} \"${id}\" ${tr('sessionDetails.notFoundDescSuffix', 'exists')}`}\n action={\n <button\n onClick={handleBack}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('sessionDetails.backToSessions', 'Back to Sessions')}\n </button>\n }\n />\n </MotionPage>\n );\n }\n\n const isRunning = session.status === 'RUNNING';\n const terminalHeight = terminalMaximized ? 'h-[calc(100vh-200px)]' : 'h-[280px] sm:h-[420px]';\n\n return (\n <MotionPage className=\"flex flex-col bg-bg-surface min-h-screen\">\n {/* Main content - scrollable */}\n <div\n className={`flex-1 overflow-y-auto space-y-6 p-4 sm:p-6 ${terminalOpen && terminalUrl ? 'pb-2' : ''}`}\n >\n {/* Back Button */}\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n Back to Sessions\n </button>\n\n {/* Header */}\n <PageHeader\n title={session.name}\n description={session.command || 'No command specified'}\n breadcrumb={[\n { label: 'Sessions', path: basePath ? `${basePath}/sessions` : '/sessions' },\n { label: session.name },\n ]}\n actions={\n <div className=\"flex items-center gap-2 flex-wrap\">\n <button\n onClick={() => refetch()}\n disabled={loading}\n className=\"flex items-center gap-2 px-3 py-2 bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken/80 transition-colors disabled:opacity-50\"\n title=\"Refresh\"\n >\n <RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n {isRunning ? (\n <>\n <button\n onClick={handleStopSession}\n className=\"flex items-center gap-2 px-3 py-2 bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken/80 transition-colors\"\n >\n <Square className=\"w-4 h-4\" />\n {tr('sessionDetails.stopCommand', 'Stop Command')}\n </button>\n <button\n onClick={handleRestartSession}\n className=\"flex items-center gap-2 px-3 py-2 bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken/80 transition-colors\"\n >\n <RotateCcw className=\"w-4 h-4\" />\n {tr('sessionDetails.restartCommand', 'Restart Command')}\n </button>\n </>\n ) : (\n <button\n onClick={handleStartSession}\n className=\"flex items-center gap-2 px-3 py-2 bg-status-success-bg text-status-success-text rounded-md hover:opacity-80 transition-colors\"\n >\n <Play className=\"w-4 h-4\" />\n {tr('sessionDetails.runCommand', 'Run Command')}\n </button>\n )}\n {/* Terminal toggle + open in new tab */}\n {terminalUrl ? (\n <>\n <button\n onClick={() => setTerminalOpen((prev) => !prev)}\n className={`flex items-center gap-2 px-3 py-2 rounded-md transition-colors ${\n terminalOpen\n ? 'bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover'\n : 'bg-bg-sunken text-text-primary hover:bg-bg-sunken/80'\n }`}\n title={\n terminalOpen\n ? tr('sessionDetails.hideTerminal', 'Hide terminal')\n : tr('sessionDetails.showTerminal', 'Show terminal')\n }\n >\n <Terminal className=\"w-4 h-4\" />\n {tr('sessionDetails.terminal', 'Terminal')}\n </button>\n <button\n onClick={() => setShowRestartTerminalConfirm(true)}\n disabled={terminalLoading}\n className=\"flex items-center gap-1.5 px-2 py-2 bg-bg-sunken text-text-secondary rounded-md hover:text-text-primary hover:bg-bg-sunken/80 transition-colors disabled:opacity-50\"\n title={tr(\n 'sessionDetails.restartTerminalTitle',\n 'Restart terminal — kills the tmux session and re-runs the command'\n )}\n >\n {terminalLoading ? (\n <Loader2 className=\"w-3.5 h-3.5 animate-spin\" />\n ) : (\n <RotateCcw className=\"w-3.5 h-3.5\" />\n )}\n <span className=\"text-xs\">\n {tr('sessionDetails.restartTerminalShort', 'Restart')}\n </span>\n </button>\n <button\n onClick={handleOpenInNewTab}\n className=\"flex items-center px-2 py-2 bg-bg-sunken text-text-secondary rounded-md hover:text-text-primary hover:bg-bg-sunken/80 transition-colors\"\n title={tr('sessionDetails.openInNewTab', 'Open terminal in new tab')}\n >\n <ExternalLink className=\"w-4 h-4\" />\n </button>\n </>\n ) : (\n <button\n onClick={handleStartTerminal}\n disabled={terminalLoading || !agent}\n className=\"flex items-center gap-2 px-3 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n title={\n !agent\n ? tr('sessionDetails.noAgentAssigned', 'No agent assigned')\n : !agent.tunnelUrl\n ? tr(\n 'sessionDetails.agentNoTunnelUrl',\n 'Agent has no tunnel URL configured'\n )\n : tr('sessionDetails.openTerminal', 'Open terminal')\n }\n >\n {terminalLoading ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : (\n <Terminal className=\"w-4 h-4\" />\n )}\n {tr('sessionDetails.openTerminal', 'Open Terminal')}\n </button>\n )}\n </div>\n }\n />\n\n {/* Status Cards */}\n <MotionList stagger={0.04} className=\"grid grid-cols-2 sm:grid-cols-4 gap-4\">\n <MotionItem>\n <Card className=\"p-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-10 h-10 rounded-lg bg-bg-sunken flex items-center justify-center\">\n <Terminal className=\"w-5 h-5 text-text-secondary\" />\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {tr('sessionDetails.statusLabel', 'Status')}\n </p>\n <StatusBadge status={session.status} size=\"md\" />\n </div>\n </div>\n </Card>\n </MotionItem>\n <MotionItem>\n <Card className=\"p-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-10 h-10 rounded-lg bg-bg-sunken flex items-center justify-center\">\n <Code className=\"w-5 h-5 text-text-secondary\" />\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {tr('sessionDetails.typeLabel', 'Type')}\n </p>\n <p className=\"font-semibold text-text-primary\">{session.type}</p>\n </div>\n </div>\n </Card>\n </MotionItem>\n <MotionItem>\n <Card className=\"p-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-10 h-10 rounded-lg bg-bg-sunken flex items-center justify-center\">\n <Hash className=\"w-5 h-5 text-text-secondary\" />\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {tr('sessionDetails.pidLabel', 'PID')}\n </p>\n <p className=\"font-semibold text-text-primary\">{session.pid || 'N/A'}</p>\n </div>\n </div>\n </Card>\n </MotionItem>\n <MotionItem>\n <Card className=\"p-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-10 h-10 rounded-lg bg-bg-sunken flex items-center justify-center\">\n <Server className=\"w-5 h-5 text-text-secondary\" />\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {tr('sessionDetails.agentLabel', 'Agent')}\n </p>\n <p className=\"font-semibold text-text-primary truncate\">\n {agent?.name || 'Unknown'}\n </p>\n </div>\n </div>\n </Card>\n </MotionItem>\n </MotionList>\n\n {/* Command */}\n <Card>\n <div className=\"p-4 border-b border-border-default flex items-center justify-between\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Code className=\"w-5 h-5\" />\n {tr('sessionDetails.commandTitle', 'Command')}\n </h3>\n {!editingCommand && (\n <button\n onClick={handleEditCommandOpen}\n className=\"p-1.5 rounded text-text-secondary hover:text-text-primary hover:bg-bg-sunken transition-colors\"\n title={tr('sessionDetails.editCommandTitle', 'Edit command and working directory')}\n >\n <Pencil className=\"w-4 h-4\" />\n </button>\n )}\n </div>\n <div className=\"p-4\">\n {editingCommand ? (\n <div className=\"space-y-3\">\n <div>\n <label className=\"block text-xs font-medium text-text-secondary mb-1\">\n {tr('sessionDetails.commandLabel', 'Command')}\n </label>\n <input\n type=\"text\"\n value={editCommand}\n onChange={(e) => setEditCommand(e.target.value)}\n placeholder={tr('sessionDetails.commandPlaceholder', 'bun run dev')}\n className=\"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 font-mono\"\n autoFocus\n />\n </div>\n <div>\n <label className=\"block text-xs font-medium text-text-secondary mb-1\">\n {tr('sessionDetails.workingDirLabel', 'Working Directory')}\n </label>\n <input\n type=\"text\"\n value={editWorkingDirectory}\n onChange={(e) => setEditWorkingDirectory(e.target.value)}\n placeholder={tr('sessionDetails.workingDirPlaceholder', '/home/user/myproject')}\n className=\"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 font-mono\"\n />\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n onClick={handleEditCommandSave}\n disabled={editSaving}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n {editSaving ? (\n <Loader2 className=\"w-3.5 h-3.5 animate-spin\" />\n ) : (\n <Check className=\"w-3.5 h-3.5\" />\n )}\n {tr('sessionDetails.save', 'Save')}\n </button>\n <button\n onClick={() => setEditingCommand(false)}\n disabled={editSaving}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-sm bg-bg-sunken text-text-primary rounded-md hover:bg-bg-elevated transition-colors disabled:opacity-50\"\n >\n <X className=\"w-3.5 h-3.5\" />\n {tr('sessionDetails.cancel', 'Cancel')}\n </button>\n </div>\n </div>\n ) : (\n <>\n <div className=\"bg-bg-sunken rounded-md p-3 font-mono text-sm text-text-primary overflow-x-auto\">\n ${' '}\n {session.command ||\n tr('sessionDetails.noCommandSpecified', 'No command specified')}\n </div>\n <div className=\"mt-3 flex items-center gap-2 text-sm text-text-secondary\">\n <Folder className=\"w-4 h-4\" />\n {tr('sessionDetails.workingDirectory', 'Working Directory')}:{' '}\n <span className=\"text-text-primary font-mono\">\n {session.workingDirectory || (\n <em className=\"not-italic text-text-placeholder\">\n {tr('sessionDetails.workingDirDefault', 'not set — defaults to OS home')}\n </em>\n )}\n </span>\n </div>\n </>\n )}\n </div>\n </Card>\n\n {/* Environment Variables */}\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Variable className=\"w-5 h-5\" />\n {tr('sessionDetails.envVars', 'Environment Variables')}\n </h3>\n </div>\n <div className=\"p-4\">\n {session.environmentVariables &&\n Object.keys(session.environmentVariables).length > 0 ? (\n <div className=\"flex flex-wrap gap-2\">\n {Object.entries(session.environmentVariables).map(([key, value]) => (\n <span\n key={key}\n className=\"px-3 py-1 bg-bg-sunken text-text-primary text-sm rounded-full font-mono\"\n >\n {key}={String(value)}\n </span>\n ))}\n </div>\n ) : (\n <p className=\"text-text-secondary\">\n {tr('sessionDetails.noEnvVars', 'No environment variables configured')}\n </p>\n )}\n </div>\n </Card>\n\n {/* Last Output */}\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Terminal className=\"w-5 h-5\" />\n {tr('sessionDetails.lastOutput', 'Last Output')}\n </h3>\n </div>\n <div className=\"p-4\">\n {session.lastOutput ? (\n <div className=\"bg-bg-sunken rounded-md p-3 font-mono text-sm text-text-primary overflow-x-auto whitespace-pre-wrap\">\n {session.lastOutput}\n </div>\n ) : (\n <p className=\"text-text-secondary\">\n {tr('sessionDetails.noOutput', 'No output captured')}\n </p>\n )}\n </div>\n </Card>\n\n {/* Related Vibe */}\n {vibe && (\n <Card onClick={handleVibeClick} hoverable>\n <div className=\"p-4 flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-10 h-10 rounded-lg bg-bg-sunken flex items-center justify-center\">\n <Zap className=\"w-5 h-5 text-text-secondary\" />\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {tr('sessionDetails.relatedVibe', 'Related Vibe')}\n </p>\n <p className=\"font-medium text-text-primary\">{vibe.name}</p>\n {vibe.path && (\n <p className=\"text-sm text-text-secondary truncate max-w-md\">{vibe.path}</p>\n )}\n </div>\n </div>\n <StatusBadge status={vibe.status} />\n </div>\n </Card>\n )}\n\n {/* Related Agent */}\n {agent && (\n <Card onClick={handleAgentClick} hoverable>\n <div className=\"p-4 flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-10 h-10 rounded-lg bg-bg-sunken flex items-center justify-center\">\n <Server className=\"w-5 h-5 text-text-secondary\" />\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {tr('sessionDetails.runningOnAgent', 'Running on Agent')}\n </p>\n <p className=\"font-medium text-text-primary\">{agent.name}</p>\n <p className=\"text-sm text-text-secondary\">{agent.hostname}</p>\n </div>\n </div>\n <StatusBadge status={agent.isActive ? 'ACTIVE' : 'OFFLINE'} />\n </div>\n </Card>\n )}\n\n {/* Metadata */}\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Clock className=\"w-5 h-5\" />\n {tr('sessionDetails.timeline', 'Timeline')}\n </h3>\n </div>\n <div className=\"p-4\">\n <div className=\"grid grid-cols-1 sm:grid-cols-3 gap-4 text-sm\">\n <div>\n <p className=\"text-text-secondary\">\n {tr('sessionDetails.autoStart', 'Auto Start')}\n </p>\n <p className=\"font-medium text-text-primary\">\n {session.autoStart\n ? tr('sessionDetails.yes', 'Yes')\n : tr('sessionDetails.no', 'No')}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">{tr('sessionDetails.created', 'Created')}</p>\n <p className=\"font-medium text-text-primary\">\n {formatTimestamp(session.createdAt)}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">{tr('sessionDetails.updated', 'Updated')}</p>\n <p className=\"font-medium text-text-primary\">\n {formatTimestamp(session.updatedAt)}\n </p>\n </div>\n </div>\n {session.exitCode !== null && session.exitCode !== undefined && (\n <div className=\"mt-4 pt-4 border-t border-border-default\">\n <p className=\"text-sm text-text-secondary\">\n {tr('sessionDetails.exitCode', 'Exit Code')}\n </p>\n <p\n className={`font-medium ${session.exitCode === 0 ? 'text-status-success-text' : 'text-status-error-text'}`}\n >\n {session.exitCode}\n </p>\n </div>\n )}\n </div>\n </Card>\n\n {/* Tags */}\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Tag className=\"w-5 h-5\" />\n {tr('sessionDetails.tags', 'Tags')}\n </h3>\n </div>\n <div className=\"p-4\">\n <EntityTagPicker entityType=\"VIBECONTROLS_SESSION\" entityId={session.id} />\n </div>\n </Card>\n </div>\n\n {/* Integrated Terminal Panel - docked at bottom */}\n {terminalOpen && (\n <div\n ref={terminalPanelRef}\n className=\"border-t border-border-default bg-bg-surface shrink-0\"\n >\n {/* Terminal header bar */}\n <div className=\"flex items-center bg-bg-sunken border-b border-border-default\">\n <button\n onClick={() => setTerminalOpen(false)}\n className=\"flex items-center gap-1.5 px-3 py-2 text-text-secondary hover:text-text-primary transition-colors text-xs font-medium uppercase tracking-wide\"\n title={tr('sessionDetails.collapseTerminal', 'Collapse terminal')}\n >\n <Terminal className=\"w-3.5 h-3.5\" />\n {tr('sessionDetails.terminal', 'Terminal')}\n <ChevronDown className=\"w-3 h-3\" />\n </button>\n <div className=\"flex items-center px-3 py-1.5 text-xs bg-bg-surface text-text-primary border-r border-border-subtle border-b-2 border-b-action-primary-bg\">\n <Terminal className=\"w-3 h-3 mr-1.5\" />\n {session.name}\n </div>\n <div className=\"flex-1\" />\n <div className=\"flex items-center gap-1 px-2\">\n {terminalUrl && (\n <button\n onClick={handleOpenInNewTab}\n className=\"p-1 text-text-secondary hover:text-text-primary transition-colors\"\n title={tr('sessionDetails.openInNewTabShort', 'Open in new tab')}\n >\n <ExternalLink className=\"w-3.5 h-3.5\" />\n </button>\n )}\n <button\n onClick={() => setTerminalMaximized((prev) => !prev)}\n className=\"p-1 text-text-secondary hover:text-text-primary transition-colors\"\n title={\n terminalMaximized\n ? tr('sessionDetails.restoreSize', 'Restore size')\n : tr('sessionDetails.maximize', 'Maximize')\n }\n >\n {terminalMaximized ? (\n <Minimize2 className=\"w-3.5 h-3.5\" />\n ) : (\n <Maximize2 className=\"w-3.5 h-3.5\" />\n )}\n </button>\n </div>\n </div>\n\n {exitedNotice && (\n <div\n role=\"status\"\n className=\"flex items-start justify-between gap-3 px-3 py-2 bg-status-warning-bg/30 text-status-warning-text border-b border-status-warning-bg/50 text-xs\"\n >\n <div className=\"flex items-center gap-2 flex-1\">\n <AlertCircle className=\"w-3.5 h-3.5 shrink-0\" />\n <span>\n {tr(\n 'sessionDetails.commandExited',\n 'Command exited{{code}} — click Run Command to run it again.',\n {\n code:\n exitedNotice.exitCode !== null ? ` (exit ${exitedNotice.exitCode})` : '',\n }\n )}\n </span>\n </div>\n <button\n type=\"button\"\n onClick={() => setExitedNotice(null)}\n className=\"text-text-secondary hover:text-text-primary\"\n title={tr('common.dismiss', 'Dismiss')}\n >\n <X className=\"w-3.5 h-3.5\" />\n </button>\n </div>\n )}\n\n {/* Terminal content */}\n <div className={`${terminalHeight} bg-bg-inverse text-text-inverse`}>\n {terminalLoading ? (\n <div className=\"flex items-center justify-center h-full\">\n <div className=\"text-center\">\n <Loader2 className=\"w-8 h-8 text-text-secondary animate-spin mx-auto mb-3\" />\n <p className=\"text-text-secondary text-sm\">\n {tr('sessionDetails.startingTerminal', 'Starting terminal for')} {session.name}\n ...\n </p>\n <p className=\"text-text-tertiary text-xs mt-1\">\n {tr(\n 'sessionDetails.settingUpTunnel',\n 'Setting up tmux + ttyd + cloudflared tunnel'\n )}\n </p>\n </div>\n </div>\n ) : terminalError ? (\n <div className=\"flex items-center justify-center h-full\">\n <div className=\"text-center max-w-md\">\n <AlertCircle className=\"w-8 h-8 text-status-error-text mx-auto mb-3\" />\n <p className=\"text-status-error-text text-sm font-medium\">\n {tr('sessionDetails.failedStartTerminal', 'Failed to start terminal')}\n </p>\n <p className=\"text-text-secondary text-xs mt-2\">{terminalError}</p>\n <button\n onClick={handleStartTerminal}\n className=\"mt-4 px-4 py-2 bg-bg-muted text-text-primary text-sm rounded-md hover:bg-bg-sunken transition-colors\"\n >\n {tr('sessionDetails.retry', 'Retry')}\n </button>\n </div>\n </div>\n ) : terminalUrl ? (\n <iframe\n src={terminalUrl}\n title={`Terminal - ${session.name}`}\n className=\"w-full h-full border-0\"\n allow=\"clipboard-read; clipboard-write\"\n />\n ) : (\n <div className=\"flex items-center justify-center h-full\">\n <div className=\"text-center\">\n <Terminal className=\"w-8 h-8 text-text-tertiary mx-auto mb-3\" />\n <p className=\"text-text-secondary text-sm\">\n {tr('sessionDetails.noTerminalRunning', 'No terminal running')}\n </p>\n <p className=\"text-text-tertiary text-xs mt-1\">\n {agent\n ? tr(\n 'sessionDetails.clickOpenTerminalHint',\n 'Click \"Open Terminal\" to create a tmux session with a browser terminal.'\n )\n : tr(\n 'sessionDetails.noAgentForSession',\n 'No agent is assigned to this session.'\n )}\n </p>\n {agent && (\n <button\n onClick={handleStartTerminal}\n disabled={terminalLoading}\n className=\"mt-4 px-4 py-2 bg-bg-muted text-text-primary text-sm rounded-md hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n {terminalLoading\n ? tr('sessionDetails.opening', 'Opening...')\n : tr('sessionDetails.openTerminal', 'Open Terminal')}\n </button>\n )}\n </div>\n </div>\n )}\n </div>\n </div>\n )}\n\n {/* Terminal collapsed indicator - show a clickable bar */}\n {!terminalOpen && (terminalUrl || terminalError) && (\n <div className=\"border-t border-border-default bg-bg-sunken shrink-0\">\n <button\n onClick={() => setTerminalOpen(true)}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-text-secondary hover:text-text-primary transition-colors text-xs font-medium uppercase tracking-wide w-full\"\n title={tr('sessionDetails.expandTerminal', 'Expand terminal')}\n >\n <Terminal className=\"w-3.5 h-3.5\" />\n {tr('sessionDetails.terminal', 'Terminal')} - {session.name}\n <ChevronUp className=\"w-3 h-3 ml-1\" />\n </button>\n </div>\n )}\n\n <ConfirmDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing');\n }}\n title={getQuotaDialogContent('session').title}\n message={getQuotaDialogContent('session').message}\n confirmLabel=\"View Billing\"\n cancelLabel=\"Dismiss\"\n variant=\"warning\"\n />\n\n {/* Restart-terminal kills tmux and re-runs the command, so\n confirm before firing. */}\n <ConfirmDialog\n isOpen={showRestartTerminalConfirm}\n onClose={() => setShowRestartTerminalConfirm(false)}\n onConfirm={() => {\n setShowRestartTerminalConfirm(false);\n void handleRestartTerminal();\n }}\n title={tr('sessionDetails.restartTerminalConfirmTitle', 'Restart terminal?')}\n message={tr(\n 'sessionDetails.restartTerminalConfirmBody',\n 'This kills the current tmux session and re-runs the command from scratch. Any in-progress output will be lost.'\n )}\n confirmLabel={tr('sessionDetails.restartTerminalConfirmLabel', 'Restart terminal')}\n cancelLabel={tr('common.cancel', 'Cancel')}\n variant=\"warning\"\n />\n </MotionPage>\n );\n}\n"],"mappings":"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"}
|