package main import "core:c" import "core:fmt" import "core:sys/posix" import "core:time" foreign import libc "system:c" @(default_calling_convention="c") foreign libc { system :: proc(command: cstring) -> c.int --- } Phase :: enum { WORK, BREAK, } State :: enum { IDLE, RUNNING, PAUSED, } App :: struct { work_s, break_s: int, phase: Phase, state: State, accumulated: f64, // seconds elapsed before last resume start_ts: time.Time, // when resumed last_rendered_sec: int, // track when to re-render preset_idx: int, // current preset index (0-3) } orig_termios: posix.termios raw_on :: proc() { posix.tcgetattr(posix.STDIN_FILENO, &orig_termios) raw := orig_termios raw.c_lflag &= ~{.ECHO, .ICANON} raw.c_cc[.VMIN] = 0 raw.c_cc[.VTIME] = 0 posix.tcsetattr(posix.STDIN_FILENO, .TCSANOW, &raw) fmt.print("\x1b[?25l") // hide cursor } raw_off :: proc() { posix.tcsetattr(posix.STDIN_FILENO, .TCSANOW, &orig_termios) fmt.print("\x1b[?25h") // show cursor } now_s :: proc() -> f64 { return time.duration_seconds(time.since(time.Time{})) } elapsed :: proc(a: ^App) -> f64 { if a.state == .RUNNING { return a.accumulated + time.duration_seconds(time.since(a.start_ts)) } return a.accumulated } duration :: proc(a: ^App) -> int { return a.work_s if a.phase == .WORK else a.break_s } poll_key :: proc() -> (c: u8, ok: bool) { fds: posix.fd_set posix.FD_ZERO(&fds) posix.FD_SET(posix.STDIN_FILENO, &fds) tv := posix.timeval{0, 0} if posix.select(posix.STDIN_FILENO + 1, &fds, nil, nil, &tv) > 0 { buf: [1]u8 if posix.read(posix.STDIN_FILENO, raw_data(&buf), 1) == 1 { return buf[0], true } } return 0, false } rgb :: proc(r, g, b: u8) -> string { return fmt.tprintf("\x1b[38;2;%d;%d;%dm", r, g, b) } bg_rgb :: proc(r, g, b: u8) -> string { return fmt.tprintf("\x1b[48;2;%d;%d;%dm", r, g, b) } gradient_color :: proc(pos: f64, is_work: bool) -> (r, g, b: u8) { t := clamp(pos, 0, 1) if is_work { // Work: green (50, 205, 50) -> yellow (255, 215, 0) -> red (255, 50, 50) if t < 0.5 { t2 := t * 2 r = u8(50 + (255 - 50) * t2) g = u8(205 + (215 - 205) * t2) b = u8(50 + (0 - 50) * t2) } else { t2 := (t - 0.5) * 2 r = 255 g = u8(215 + (50 - 215) * t2) b = u8(0 + (50 - 0) * t2) } } else { // Break: blue (30, 144, 255) -> cyan (0, 255, 255) -> green (50, 205, 50) if t < 0.5 { t2 := t * 2 r = u8(30 + (0 - 30) * t2) g = u8(144 + (255 - 144) * t2) b = 255 } else { t2 := (t - 0.5) * 2 r = u8(0 + (50 - 0) * t2) g = 255 b = u8(255 + (50 - 255) * t2) } } return } // Terminal theme colors (ANSI 16-color palette) C_BORDER :: "\x1b[36m" // Cyan C_GOLD :: "\x1b[33m" // Yellow C_WHITE :: "\x1b[97m" // Bright white C_GRAY :: "\x1b[37m" // White (normal) C_DIM :: "\x1b[90m" // Bright black (gray) C_DARK :: "\x1b[37m" // White for inactive presets C_GREEN :: "\x1b[92m" // Bright green for active preset C_RESET :: "\x1b[0m" // Preset data Preset :: struct { key: u8, name: string, work_min: int, break_min: int, } presets :: [?]Preset{ {'1', "Testing", 2, 1}, {'2', "Classic", 25, 5}, {'3', "Long", 50, 10}, } apply_preset :: proc(a: ^App, idx: int) { a.preset_idx = idx switch idx { case 0: a.work_s = 2 * 60 a.break_s = 1 * 60 case 1: a.work_s = 25 * 60 a.break_s = 5 * 60 case 2: a.work_s = 50 * 60 a.break_s = 10 * 60 } a.phase = .WORK a.state = .IDLE a.accumulated = 0 } render :: proc(a: ^App) { dur := duration(a) el := int(elapsed(a)) rem := max(0, dur - el) pct := f64(el) / f64(dur) if dur > 0 else 0.0 pct = clamp(pct, 0, 1) bar_width :: 26 fill := int(pct * f64(bar_width)) fmt.print("\x1b[H\x1b[2J") // Top border with SUPERMEMORY fmt.printfln( " %s┌─[%sPOMODORO TIMER%s]────────────────────┐%s", C_BORDER, C_GOLD, C_BORDER, C_RESET, ) fmt.printfln( " %s│%s %s│%s", C_BORDER, C_RESET, C_BORDER, C_RESET, ) fmt.printfln( " %s│%s %s│%s", C_BORDER, C_RESET, C_BORDER, C_RESET, ) // Phase and state - hardcoded for perfect alignment // Content width = 37 chars if a.phase == .WORK && a.state == .IDLE { fmt.printfln(" %s│%s %sPhase: 💼 WORK State: ⏹️ IDLE %s│%s", C_BORDER, C_RESET, C_GRAY, C_BORDER, C_RESET) } else if a.phase == .WORK && a.state == .RUNNING { fmt.printfln(" %s│%s %sPhase: 💼 WORK State: ▶️ RUNNING %s│%s", C_BORDER, C_RESET, C_GRAY, C_BORDER, C_RESET) } else if a.phase == .WORK && a.state == .PAUSED { fmt.printfln(" %s│%s %sPhase: 💼 WORK State: ⏸️ PAUSED %s│%s", C_BORDER, C_RESET, C_GRAY, C_BORDER, C_RESET) } else if a.phase == .BREAK && a.state == .IDLE { fmt.printfln(" %s│%s %sPhase: ☕ BREAK State: ⏹️ IDLE %s│%s", C_BORDER, C_RESET, C_GRAY, C_BORDER, C_RESET) } else if a.phase == .BREAK && a.state == .RUNNING { fmt.printfln(" %s│%s %sPhase: ☕ BREAK State: ▶️ RUNNING %s│%s", C_BORDER, C_RESET, C_GRAY, C_BORDER, C_RESET) } else if a.phase == .BREAK && a.state == .PAUSED { fmt.printfln(" %s│%s %sPhase: ☕ BREAK State: ⏸️ PAUSED %s│%s", C_BORDER, C_RESET, C_GRAY, C_BORDER, C_RESET) } fmt.printfln( " %s│%s %s│%s", C_BORDER, C_RESET, C_BORDER, C_RESET, ) // Time mins_el, secs_el := el / 60, el % 60 mins_rem, secs_rem := rem / 60, rem % 60 fmt.printfln( " %s│%s %sElapsed: %02d:%02d %s│%s", C_BORDER, C_RESET, C_GRAY, mins_el, secs_el, C_BORDER, C_RESET, ) fmt.printfln( " %s│%s %sRemaining: %02d:%02d %s│%s", C_BORDER, C_RESET, C_GRAY, mins_rem, secs_rem, C_BORDER, C_RESET, ) fmt.printfln( " %s│%s %s│%s", C_BORDER, C_RESET, C_BORDER, C_RESET, ) // Progress bar fmt.print(" "); fmt.print(C_BORDER); fmt.print("│ ["); fmt.print(C_RESET) for i in 0 ..< bar_width { if i < fill { pos := f64(i) / f64(bar_width - 1) if bar_width > 1 else 0 r, g, b := gradient_color(pos, a.phase == .WORK) fmt.print(rgb(r, g, b)) fmt.print("=") } else { fmt.print(rgb(60, 60, 60)) fmt.print("-") } } fmt.print(C_RESET); fmt.print("]") fmt.printfln(" %3d%% %s│%s", int(pct * 100), C_BORDER, C_RESET) fmt.printfln( " %s│%s %s│%s", C_BORDER, C_RESET, C_BORDER, C_RESET, ) // Presets fmt.printfln( " %s│%s %s--- PRESETS --- %s│%s", C_BORDER, C_RESET, C_DIM, C_BORDER, C_RESET, ) preset_keys := [?]u8{'1', '2', '3'} preset_names := [?]string{"Testing", "Classic", "Long"} preset_work := [?]int{2, 25, 50} preset_break := [?]int{1, 5, 10} for idx in 0..<3 { marker := "" is_active := idx == a.preset_idx if is_active { marker = " <--" } val := fmt.tprintf("%d/%d", preset_work[idx], preset_break[idx]) line := fmt.tprintf("[%c] %-8s %-5s%-4s", preset_keys[idx], preset_names[idx], val, marker) color := C_GREEN if is_active else C_DARK fmt.printfln(" %s│%s %s%-35s%s│%s", C_BORDER, C_RESET, color, line, C_BORDER, C_RESET) } fmt.printfln( " %s│%s %s│%s", C_BORDER, C_RESET, C_BORDER, C_RESET, ) // Controls fmt.printfln( " %s│%s %s--- CONTROLS --- %s│%s", C_BORDER, C_RESET, C_DIM, C_BORDER, C_RESET, ) fmt.printfln( " %s│%s %s[s] start [p] pause %s│%s", C_BORDER, C_RESET, C_GRAY, C_BORDER, C_RESET, ) fmt.printfln( " %s│%s %s[x] stop [q] quit %s│%s", C_BORDER, C_RESET, C_GRAY, C_BORDER, C_RESET, ) fmt.printfln( " %s│%s %s│%s", C_BORDER, C_RESET, C_BORDER, C_RESET, ) fmt.printfln(" %s└─────────────────────────────────────┘%s", C_BORDER, C_RESET) fmt.println("") a.last_rendered_sec = el } main :: proc() { app := App { work_s = 2 * 60, break_s = 1 * 60, phase = .WORK, state = .IDLE, accumulated = 0, last_rendered_sec = -1, preset_idx = 0, } raw_on() defer raw_off() render(&app) for { dirty := false // poll input for { c, ok := poll_key() if !ok do break // Handle escape sequences (arrow keys) if c == 27 { // ESC // Check for arrow key sequence ESC [ X c2, ok2 := poll_key() if ok2 && c2 == '[' { c3, ok3 := poll_key() if ok3 { switch c3 { case 'A': // Up arrow - previous preset new_idx := app.preset_idx - 1 if new_idx < 0 { new_idx = len(presets) - 1 } apply_preset(&app, new_idx) dirty = true case 'B': // Down arrow - next preset new_idx := app.preset_idx + 1 if new_idx >= len(presets) { new_idx = 0 } apply_preset(&app, new_idx) dirty = true } } } continue } switch c { case 'q': return case '1': apply_preset(&app, 0) dirty = true case '2': apply_preset(&app, 1) dirty = true case '3': apply_preset(&app, 2) dirty = true case 'x': app.phase = .WORK app.state = .IDLE app.accumulated = 0 dirty = true case 'p': if app.state == .RUNNING { app.accumulated = elapsed(&app) app.state = .PAUSED dirty = true } case 's': if app.state == .IDLE || app.state == .PAUSED { app.start_ts = time.now() app.state = .RUNNING dirty = true } } } // check phase transition if app.state == .RUNNING { if int(elapsed(&app)) >= duration(&app) { app.phase = .BREAK if app.phase == .WORK else .WORK app.accumulated = 0 app.start_ts = time.now() dirty = true // Play sound.ogg at 20% volume using ffplay system("ffplay -nodisp -autoexit -volume 20 sound.ogg > /dev/null 2>&1 &") } } // check if second changed (only re-render on tick) if app.state == .RUNNING { cur_sec := int(elapsed(&app)) if cur_sec != app.last_rendered_sec { dirty = true } } if dirty { render(&app) } // sleep ~50ms to avoid busy loop time.sleep(50 * time.Millisecond) } }