import React from 'react'; import { Icon } from '@/components/icon/Icon'; import { useSessionStatus } from '@/sync/sync-context'; import { useGoalObjectiveContent, useSessionGoal } from '@/hooks/useSessionGoal'; import { formatGoalTokens } from '@/lib/sessionGoalMetadata'; import { sessionGoalStatusColor, sessionGoalStatusLabelKey } from '@/lib/sessionGoalPresentation'; import { setSessionGoalStatus } from '@/lib/sessionGoalActions'; import { toast } from '@/components/ui'; import { useI18n } from '@/lib/i18n'; import { cn } from '@/lib/utils'; interface SessionGoalRowProps { sessionId: string | null; directory?: string; className?: string; } // Compact goal strip near the composer: informational only — status dot, // objective (or the latest audit note), token usage — plus an inline // pause/resume action. The manage dialog opens from the composer target // button, not from here. export const SessionGoalRow: React.FC = React.memo(({ sessionId, directory, className }) => { const { t } = useI18n(); const { goal, enabled } = useSessionGoal(sessionId ?? '', directory); const objectiveContent = useGoalObjectiveContent(sessionId ?? '', goal); const sessionStatus = useSessionStatus(sessionId ?? '', directory); const [busy, setBusy] = React.useState(false); const handleToggleStatus = React.useCallback(async (nextStatus: 'active' | 'paused') => { if (!sessionId || busy) return; setBusy(true); try { await setSessionGoalStatus(sessionId, directory, nextStatus); } catch (error) { console.warn('[session-goal] status change failed:', error); toast.error(t('chat.goal.toast.actionFailed')); } finally { setBusy(false); } }, [sessionId, directory, busy, t]); if (!sessionId || !enabled || !goal) { return null; } // Accounting only lands on idle ticks — hide the counter until there is a // real number (or a budget worth tracking against) instead of showing "0". const usage = goal.tokenBudget ? t('chat.goal.usage.tokensWithBudget', { used: formatGoalTokens(goal.tokensUsed), budget: formatGoalTokens(goal.tokenBudget), }) : (goal.tokensUsed > 0 ? t('chat.goal.usage.tokens', { used: formatGoalTokens(goal.tokensUsed) }) : null); const pauseResume = goal.status === 'active' ? { icon: 'pause' as const, labelKey: 'chat.goal.action.pause' as const, next: 'paused' as const } : (goal.status === 'paused' || goal.status === 'blocked' || goal.status === 'budgetLimited' ? { icon: 'play' as const, labelKey: 'chat.goal.action.resume' as const, next: 'active' as const } : null); return (
); }); SessionGoalRow.displayName = 'SessionGoalRow';