From 4e2d978eb5fc9457d5b913bc10faf1266e6dcda4 Mon Sep 17 00:00:00 2001 From: Solstice Date: Tue, 9 Jun 2026 01:02:30 -0700 Subject: chore: final polish and preparation for release --- src/components/AmbientControl.tsx | 9 ++++++-- src/components/NotificationOverlay.tsx | 4 +++- src/components/SettingsPanel.tsx | 38 +++++++++++++++++++++++++--------- src/components/TaskList.tsx | 9 +++++--- src/components/TimerView.tsx | 37 ++++++++++++++++----------------- 5 files changed, 62 insertions(+), 35 deletions(-) (limited to 'src/components') diff --git a/src/components/AmbientControl.tsx b/src/components/AmbientControl.tsx index 9eabb09..dd58235 100644 --- a/src/components/AmbientControl.tsx +++ b/src/components/AmbientControl.tsx @@ -24,15 +24,18 @@ export function AmbientControl() { style={{ display: 'flex', alignItems: 'center', + flexWrap: 'wrap', gap: '12px', + rowGap: '8px', padding: '8px 10px', border: '1px solid var(--line-2)', borderRadius: 'var(--r-3)', background: 'rgba(17, 21, 28, 0.85)', boxShadow: 'var(--shadow-1)', + minWidth: 0, }} > -
+
Ambient @@ -41,6 +44,7 @@ export function AmbientControl() { fontFamily: 'var(--font-sans)', fontSize: '12px', color: available ? 'var(--fg-3)' : 'var(--negative)', + lineHeight: 1.4, }} > {available ? 'Looping background audio' : 'Add rain.ogg, cafe.ogg, or white_noise.ogg'} @@ -61,6 +65,7 @@ export function AmbientControl() { padding: '6px 10px', outline: 'none', minWidth: '132px', + flex: '0 0 auto', }} > {SOUND_OPTIONS.map((option) => ( @@ -70,7 +75,7 @@ export function AmbientControl() { ))} -
+
Math.min(max, Math.max(min, value)); + interface SettingsPanelProps { open: boolean; onClose: () => void; @@ -22,21 +24,31 @@ export function SettingsPanel({ open, onClose }: SettingsPanelProps) { useEffect(() => { if (settings) { - setWorkMins(Math.round(settings.work_duration_secs / 60)); - setShortBreakMins(Math.round(settings.short_break_secs / 60)); - setLongBreakMins(Math.round(settings.long_break_secs / 60)); - setSessionsBeforeLong(settings.sessions_before_long_break); + setWorkMins(clampValue(Math.round(settings.work_duration_secs / 60), 1, 120)); + setShortBreakMins(clampValue(Math.round(settings.short_break_secs / 60), 1, 60)); + setLongBreakMins(clampValue(Math.round(settings.long_break_secs / 60), 1, 60)); + setSessionsBeforeLong(clampValue(settings.sessions_before_long_break, 1, 10)); } }, [settings]); if (!open) return null; const handleSave = async () => { + const nextWorkMins = clampValue(workMins, 1, 120); + const nextShortBreakMins = clampValue(shortBreakMins, 1, 60); + const nextLongBreakMins = clampValue(longBreakMins, 1, 60); + const nextSessionsBeforeLong = clampValue(sessionsBeforeLong, 1, 10); + + setWorkMins(nextWorkMins); + setShortBreakMins(nextShortBreakMins); + setLongBreakMins(nextLongBreakMins); + setSessionsBeforeLong(nextSessionsBeforeLong); + const s: Settings = { - work_duration_secs: workMins * 60, - short_break_secs: shortBreakMins * 60, - long_break_secs: longBreakMins * 60, - sessions_before_long_break: sessionsBeforeLong, + work_duration_secs: nextWorkMins * 60, + short_break_secs: nextShortBreakMins * 60, + long_break_secs: nextLongBreakMins * 60, + sessions_before_long_break: nextSessionsBeforeLong, }; await updateSettings(s); onClose(); @@ -64,7 +76,9 @@ export function SettingsPanel({ open, onClose }: SettingsPanelProps) { borderRadius: 'var(--r-3)', boxShadow: 'var(--shadow-3)', padding: '32px', - width: '360px', + width: 'min(360px, calc(100vw - 32px))', + maxHeight: 'calc(100vh - 32px)', + overflowY: 'auto', display: 'flex', flexDirection: 'column', gap: '24px', @@ -109,6 +123,10 @@ export function SettingsPanel({ open, onClose }: SettingsPanelProps) { />
+ + Values are clamped to sensible minimums before saving. + +
)} {tasks.map((task) => { diff --git a/src/components/TimerView.tsx b/src/components/TimerView.tsx index 973f09f..896c03a 100644 --- a/src/components/TimerView.tsx +++ b/src/components/TimerView.tsx @@ -1,3 +1,4 @@ +import type { CSSProperties } from 'react'; import { invoke } from '@tauri-apps/api/core'; import { useTimerStore, TimerPhase } from '../store/timerStore'; import { useTaskStore } from '../store/taskStore'; @@ -7,6 +8,18 @@ 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'; @@ -60,9 +73,9 @@ export function TimerView() { flexDirection: 'column', alignItems: 'center', justifyContent: 'center', - gap: '24px', + gap: '20px', flex: 1, - padding: '32px', + padding: '24px 20px', }} > {/* Eyebrow */} @@ -142,25 +155,10 @@ export function TimerView() {
{/* Current task name */} - {currentTask && ( - - {currentTask.name} - - )} + {currentTask ? currentTask.name : 'No task selected'} {/* Controls */} -
+