refactor: enhance TimerPage layout and functionality; update theme and tokens for improved design consistency

- Refactored TimerPage component to improve layout and user experience.
- Updated theme configuration to utilize design tokens for better maintainability.
- Introduced new tokens for colors and typography to align with design principles.
- Simplified state management and improved timer functionality.
- Enhanced accessibility and responsiveness of UI elements.
This commit is contained in:
timka123 committed 2026-10-18 23:57:36 +03:00
1 parent 17a0a3c4c9
commit 77b26757bf
84 files changed
+4501 -2682

No files matched your search

+1
View File
@@ -27,6 +27,7 @@ export const KIND_LABEL: Record<string, string> = {
export const BUCKET_LABEL: Record<string, string> = {
overdue: "Overdue",
today: "Due today",
active: "Would be nice to start",
upcoming: "Coming up",
someday: "No deadline",
};
+18 -5
View File
@@ -29,6 +29,7 @@ export interface TimerState {
finished?: TimerSession | null;
auto_started?: TimerSession | null;
celebration?: string | null;
timer_enabled?: boolean;
}
export interface Task {
@@ -50,7 +51,7 @@ export interface Task {
stale: boolean;
estimated_pomodoros: number | null;
days_overdue: number;
bucket: "overdue" | "today" | "upcoming" | "someday";
bucket: "overdue" | "today" | "active" | "upcoming" | "someday";
}
export interface TaskListResponse {
@@ -64,11 +65,21 @@ export interface RefreshResult {
refreshed_at: string;
date: string;
total: number;
sources: { source: string; label: string; ok: boolean; error: string | null; count: number }[];
closed: number;
sources: {
source: string;
label: string;
ok: boolean;
error: string | null;
count: number;
/** How many tasks this refresh found closed in that tool. */
closed?: number;
}[];
}
export interface BlockerPolicy {
active: boolean;
unexpandable?: string[];
reason: string;
mode: string;
enabled: boolean;
@@ -165,12 +176,13 @@ export interface AppSettings {
alarm_sound: boolean;
alarm_volume: number;
desktop_notifications: boolean;
timer_enabled: boolean;
keep_on_top_during_work: boolean;
};
sources: {
jira: Record<string, unknown> & { enabled: boolean; base_url: string; email: string; api_token: string; extra_jql: string; start_date_field: string; include_all_open: boolean; writeback_done: boolean; done_transition_name: string; max_issues: number };
google_tasks: { enabled: boolean; client_id: string; client_secret: string; refresh_token: string; lists: string[]; include_undated: boolean; writeback_done: boolean };
todoist: { enabled: boolean; api_token: string; projects: string[]; filter_query: string; writeback_done: boolean };
jira: Record<string, unknown> & { enabled: boolean; base_url: string; email: string; api_token: string; extra_jql: string; start_date_field: string; include_all_open: boolean; max_issues: number };
google_tasks: { enabled: boolean; client_id: string; client_secret: string; refresh_token: string; lists: string[]; include_undated: boolean };
todoist: { enabled: boolean; api_token: string; projects: string[]; filter_query: string };
laundry: { enabled: boolean; base_url: string; session_cookie: string };
refresh_interval_minutes: number;
request_timeout_seconds: number;
@@ -181,6 +193,7 @@ export interface AppSettings {
block_during_breaks: boolean;
blocked_domains: string[];
allowed_domains: string[];
subdomain_expansions: string[];
blocked_apps: string[];
kill_blocked_apps: boolean;
schedule: { label: string; start: string; end: string; days: number[] }[];
+23 -18
View File
@@ -1,10 +1,12 @@
/**
* Chart colours.
*
* These are the validated data-viz slots, not the Material theme's palette: the
* UI's accent can be anything, but a chart has to stay readable for colour-blind
* readers in both themes. Work/short/long are categorical slots 1–3 (validated
* all-pairs in both modes); magnitude charts use the single-hue blue ramp.
* These are the validated data-viz slots, not the brand accent: the UI is warm
* ember on paper, but a chart has to stay readable for colour-blind readers in
* both themes, so identity colours come from the reference palette. Focus takes
* the orange slot (nearest the brand's warmth), breaks take aqua, and anything
* that encodes *magnitude* uses the single-hue blue ramp — this ordering was run
* through the palette validator for all pairs in both modes.
*/
export interface VizPalette {
@@ -14,28 +16,28 @@ export interface VizPalette {
sequential: string[];
grid: string;
axis: string;
surface: string;
empty: string;
}
const LIGHT: VizPalette = {
work: "#2a78d6",
shortBreak: "#eb6834",
work: "#eb6834",
shortBreak: "#2a78d6",
longBreak: "#1baf7a",
// blue 100 → 600, light → dark: "more pomodoros" reads as "darker".
// blue 100 → 600: "more pomodoros" reads as "darker".
sequential: ["#cde2fb", "#9ec5f4", "#6da7ec", "#3987e5", "#256abf", "#184f95"],
grid: "rgba(0,0,0,0.08)",
axis: "rgba(0,0,0,0.45)",
surface: "#fcfcfb",
grid: "rgba(23, 21, 15, 0.12)",
axis: "rgba(23, 21, 15, 0.45)",
empty: "rgba(23, 21, 15, 0.07)",
};
const DARK: VizPalette = {
work: "#3987e5",
shortBreak: "#d95926",
work: "#d95926",
shortBreak: "#3987e5",
longBreak: "#199e70",
sequential: ["#104281", "#184f95", "#256abf", "#3987e5", "#6da7ec", "#9ec5f4"],
grid: "rgba(255,255,255,0.10)",
axis: "rgba(255,255,255,0.55)",
surface: "#1a1a19",
grid: "rgba(244, 241, 234, 0.14)",
axis: "rgba(244, 241, 234, 0.55)",
empty: "rgba(244, 241, 234, 0.08)",
};
export function vizPalette(dark: boolean): VizPalette {
@@ -48,10 +50,13 @@ export function kindColor(kind: string, palette: VizPalette): string {
return palette.shortBreak;
}
/** Pick a ramp step for a count, 0 meaning "empty". */
/** Pick a ramp step for a count; null means "nothing happened". */
export function rampStep(count: number, goal: number, palette: VizPalette): string | null {
if (count <= 0) return null;
const ratio = goal > 0 ? count / goal : count / 8;
const index = Math.min(palette.sequential.length - 1, Math.max(0, Math.round(ratio * (palette.sequential.length - 1))));
const index = Math.min(
palette.sequential.length - 1,
Math.max(0, Math.round(ratio * (palette.sequential.length - 1))),
);
return palette.sequential[index];
}