From 79c9870704df5ce77cb852bccec7dc780a248b2e Mon Sep 17 00:00:00 2001 From: nsfisis Date: Mon, 27 Oct 2025 06:08:21 +0900 Subject: feat: Add "countdown" subcommand --- cmd/countdown.go | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ cmd/root.go | 1 + 2 files changed, 106 insertions(+) create mode 100644 cmd/countdown.go (limited to 'cmd') diff --git a/cmd/countdown.go b/cmd/countdown.go new file mode 100644 index 0000000..61cd929 --- /dev/null +++ b/cmd/countdown.go @@ -0,0 +1,105 @@ +package cmd + +import ( + "log" + "math" + "strconv" + "time" + + "github.com/spf13/cobra" + + "github.com/nsfisis/term-clock/internal/term" +) + +func drawCountdown(scr *term.Screen, leftSeconds int, bgStyle, fgStyle term.Style) { + if leftSeconds <= 0 { + if int(math.Abs(float64(leftSeconds)))%2 == 0 { + bgStyle, fgStyle = fgStyle, bgStyle + } + leftSeconds = 0 + } + + // Clear the entire screen. + scr.Clear(bgStyle) + + // Calculate number of digits needed + numDigits := 1 + if leftSeconds > 0 { + n := leftSeconds + numDigits = 0 + for n > 0 { + numDigits++ + n /= 10 + } + } + + scrW, scrH := scr.Size() + // Calculate square size based on number of digits + squareW := scrW / (numDigits*4 + 2) + squareH := min(scrH/(5+2), squareW) + if squareW > squareH*3/2 { + squareW = squareH * 3 / 2 + } + xOffset := (scrW - squareW*numDigits*4) / 2 + yOffset := (scrH - squareH*5) / 2 + + // Extract and display each digit + n := leftSeconds + for i := numDigits - 1; i >= 0; i-- { + digit := n % 10 + term.DrawNumber(scr, digit, xOffset+squareW*i*4, yOffset, squareW, squareH, fgStyle) + n /= 10 + } +} + +func cmdCountdown(cmd *cobra.Command, args []string) { + totalSeconds, err := strconv.Atoi(args[0]) + if err != nil { + log.Fatalf("%+v", err) + } + + if totalSeconds > 9999 { + log.Fatal("Duration over 9999 seconds is not supported.") + } + + scr, err := term.NewScreen() + if err != nil { + log.Fatalf("%+v", err) + } + defer scr.Close() + + startTime := time.Now() + + calcLeftSeconds := func(t time.Time) int { + elapsed := int(t.Sub(startTime).Seconds()) + return totalSeconds - elapsed + } + + drawCountdown(scr, calcLeftSeconds(time.Now()), term.BgStyle, term.FgStyle) + + scr.OnResize(func() bool { + drawCountdown(scr, calcLeftSeconds(time.Now()), term.BgStyle, term.FgStyle) + return false + }) + go scr.DoEventLoop() + + t := time.NewTicker(1 * time.Second) + defer t.Stop() + + for { + select { + case <-scr.QuitC: + return + case now := <-t.C: + drawCountdown(scr, calcLeftSeconds(now), term.BgStyle, term.FgStyle) + scr.Show() + } + } +} + +var countdownCmd = &cobra.Command{ + Use: "countdown", + Short: "Countdown mode", + Run: cmdCountdown, + Args: cobra.ExactArgs(1), +} diff --git a/cmd/root.go b/cmd/root.go index 5a91eae..e3aa309 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -16,6 +16,7 @@ var rootCmd = &cobra.Command{ func init() { rootCmd.AddCommand(alarmCmd) rootCmd.AddCommand(clockCmd) + rootCmd.AddCommand(countdownCmd) rootCmd.AddCommand(timerCmd) } -- cgit v1.2.3-70-g09d2