import type { CSSProperties } from 'react'; import { invoke } from '@tauri-apps/api/core'; import { useTimerStore, TimerPhase } from '../store/timerStore'; import { useTaskStore } from '../store/taskStore'; import { AmbientControl } from './AmbientControl'; const RING_SIZE = 280; const STROKE = 8; const RADIUS = (RING_SIZE - STROKE) / 2; const CIRCUMFERENCE = 2 * Math.PI * RADIUS; const TASK_LABEL_STYLE: CSSProperties = { fontFamily: 'var(--font-sans)', fontSize: '14px', color: 'var(--fg-3)', maxWidth: '320px', textAlign: 'center', minHeight: '20px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', }; function phaseLabel(phase: TimerPhase): string { switch (phase) { case 'work': return 'Focus'; case 'short_break': return 'Short Break'; case 'long_break': return 'Long Break'; } } function phaseColor(phase: TimerPhase): string { switch (phase) { case 'work': return 'var(--brass)'; case 'short_break': return 'var(--positive)'; case 'long_break': return 'var(--info)'; } } function eyebrowText(phase: TimerPhase, sessionCount: number): string { if (phase === 'work') { return `WORK ยท SESSION ${sessionCount + 1}`; } if (phase === 'short_break') return 'SHORT BREAK'; return 'LONG BREAK'; } function formatTime(secs: number): string { const m = Math.floor(secs / 60); const s = secs % 60; return `${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`; } export function TimerView() { const { phase, remainingSecs, totalSecs, running, sessionCount, currentTaskId } = useTimerStore(); const tasks = useTaskStore((s) => s.tasks); const syncFromBackend = useTimerStore((s) => s.syncFromBackend); const currentTask = tasks.find((t) => t.id === currentTaskId) ?? null; const progress = totalSecs > 0 ? remainingSecs / totalSecs : 1; const dashOffset = CIRCUMFERENCE * (1 - progress); const arcColor = phaseColor(phase); const handleStart = async () => { await invoke('start_timer'); await syncFromBackend(); }; const handlePause = async () => { await invoke('pause_timer'); await syncFromBackend(); }; const handleSkip = async () => { await invoke('skip_phase'); await syncFromBackend(); }; const handleReset = async () => { await invoke('reset_timer'); await syncFromBackend(); }; const handleClearSessions = async () => { await invoke('clear_session_count'); await syncFromBackend(); }; return (
{/* Eyebrow */} {eyebrowText(phase, sessionCount)} {/* Circular ring */}
{/* Background track */} {/* Progress arc */} {/* Center content */}
{formatTime(remainingSecs)} {phaseLabel(phase)}
{/* Current task name */} {currentTask ? currentTask.name : 'No task selected'} {/* Controls */}
Skip Reset Clear Streak
); } function GhostButton({ onClick, children, }: { onClick: () => Promise; children: React.ReactNode; }) { return ( ); }