Skip to content

Enhance Pomodoro Timer UI and improve terminal UX#1926

Merged
Karanjot786 merged 1 commit into
Karanjot786:mainfrom
RosheshChaware:feature/pomodoro-ui-enhancement
Jul 3, 2026
Merged

Enhance Pomodoro Timer UI and improve terminal UX#1926
Karanjot786 merged 1 commit into
Karanjot786:mainfrom
RosheshChaware:feature/pomodoro-ui-enhancement

Conversation

@RosheshChaware

@RosheshChaware RosheshChaware commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Description

This PR enhances the Pomodoro Timer example by improving its terminal UI and overall user experience while preserving the existing timer functionality. The update introduces a cleaner layout, improved visual hierarchy, dynamic progress states, refined status indicators, and better keyboard shortcut presentation. All changes are strictly limited to examples/pomodoro-timer.


Related Issue

Closes #1925


Which package(s)?

examples/pomodoro-timer


Type of Change

  • 🐛 Bug fix (type:bug)
  • ✨ Feature (type:feature)
  • 📝 Docs (type:docs)
  • 🧪 Tests (type:testing)
  • ♻️ Refactor (type:refactor)
  • 🎨 Design / UX (type:design)
  • ♿ Accessibility (type:accessibility)
  • ⚡ Performance (type:performance)
  • 🔧 DevOps / CI (type:devops)
  • 🔒 Security (type:security)

Checklist

  • ⭐ You starred the repo. The needs-star check blocks your merge otherwise.
  • Tests pass locally: bun vitest run
  • Build passes: bun run build
  • Typecheck passes: bun run typecheck
  • You read CONTRIBUTING.md.
  • Your PR title follows type: short description.
  • Widget state mutators call markDirty() (if your change affects rendering).
  • No new any types without an inline comment explaining why.
  • No unrelated refactors bundled into this PR.

GSSoC 2026 Participation

  • You are a GSSoC 2026 contributor.

  • Your GSSoC profile:
    https://gssoc.girlscript.org/profile/79bbc5b9-0fdc-457e-b8cb-23220ab16312


Screenshots / Recordings (UI changes)

Screenshot 2026-07-02 002059

Notes for the Reviewer

  • This PR only modifies files inside examples/pomodoro-timer.
  • No shared components, packages, or project configuration files were changed.
  • Existing timer behavior and keyboard shortcuts remain unchanged.
  • The focus of this PR is to provide a cleaner and more polished terminal UI while maintaining backward compatibility.

Summary by CodeRabbit

  • New Features

    • Updated the Pomodoro timer display with larger, more prominent time digits and a richer status label.
    • Added a color-shifting progress indicator that changes with timer progress for a more dynamic experience.
  • Bug Fixes

    • Improved timer and toast behavior so the display refreshes correctly when notifications end.
    • Refined the timer’s visual transitions to keep the interface responsive and consistent across running, paused, and finished states.

@github-actions github-actions Bot added the area:examples Example apps. label Jul 1, 2026
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The Pomodoro timer example is refactored to introduce custom widgets: BigTimer for large digit rendering, GradientProgressBar for a color-interpolated progress bar, and StatusBadge for color-coded status labels. Dynamic color logic drives display refresh, pulse animation, and toast timeout behavior.

Changes

Pomodoro Timer UI Refactor

Layer / File(s) Summary
Big-digit font and dynamic color foundation
examples/pomodoro-timer/src/index.tsx
Adds BIG_DIGITS map, getBigDigitLines, and getDynamicColor for fraction-to-color mapping.
BigTimer widget
examples/pomodoro-timer/src/index.tsx
Renders time as centered large digits using the digit font map.
GradientProgressBar widget
examples/pomodoro-timer/src/index.tsx
Draws a filled/empty progress bar with interpolated per-cell gradient colors and optional percentage label.
StatusBadge widget
examples/pomodoro-timer/src/index.tsx
Renders a colored, centered badge for Running, Paused, and Finished states.
PomodoroApp field and layout wiring
examples/pomodoro-timer/src/index.tsx
Replaces old display fields with _bigTimer, _progressBar, _statusBadge; rebuilds constructor layout with updated container styling, phase label, time-remaining text, and hints box.
Pulse animation and display refresh
examples/pomodoro-timer/src/index.tsx
Updates phase-change pulse to flash white then fade via dynamic color; toast timeout and _updateDisplay now drive phase label, timer, progress bar, border color, and status badge from elapsed fraction.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • Karanjot786/TermUI#739: Directly related as it modifies the same examples/pomodoro-timer/src/index.tsx file, establishing the original timer/progress/status rendering that this PR refactors.

Suggested labels: type:feature, type:design, level:advanced

Suggested reviewers: Karanjot786

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is short, specific, and accurately reflects the main UI and UX changes in the Pomodoro timer example.
Description check ✅ Passed The description includes all required sections, links the issue, identifies the package, and documents the change, checklist, screenshots, and reviewer notes.
Linked Issues check ✅ Passed The changes match the issue goals: larger timer, dynamic progress color, status badges, time remaining display, and refined terminal styling without altering timer behavior.
Out of Scope Changes check ✅ Passed The PR stays within the pomodoro-timer example and shows no unrelated file or package changes beyond the requested UI improvements.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
examples/pomodoro-timer/src/index.tsx (1)

440-486: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

StatusBadge shows "FINISHED" while the toast simultaneously announces the new phase "started".

_updateDisplay sets status = 'Finished' whenever this._toastTimer !== null (line 477-478). _toastTimer is set non-null on every phase transition — both work→break and break→work — via _showToast(), whose toast text reads ">> Work/Break phase started! <<". For the full 3-second toast window after every transition, the badge contradicts the toast: one says the phase "started", the other says "FINISHED", even though the timer is actively counting down a fresh, running phase. This directly undermines the PR's stated goal of clear Running/Paused/Finished indicators.

Separately, if (this._toastTimer !== null) { clearTimeout(this._toastTimer); } (line 447-449) doesn't reset _toastTimer to null before the immediate _updateDisplay() call on line 451 — it's only reassigned afterward by the new setTimeout on line 453, so the stale non-null handle is what _updateDisplay() observes at that instant too.

Consider decoupling "toast currently showing" from the "Finished" badge state — e.g. only mark Finished when the countdown itself reaches zero at the exact transition instant, not for the full toast display duration of the next phase.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/pomodoro-timer/src/index.tsx` around lines 440 - 486, The status
logic in _updateDisplay is incorrectly using _toastTimer to mean “Finished,”
which makes the StatusBadge show FINISHED during the 3-second phase-start toast
even though the timer is running. Update _updateDisplay and the _showToast
transition flow so the badge is driven by the actual timer state (_running and
whether the countdown has reached zero), not by whether a toast is visible; keep
_toastTimer only for toast display timing and avoid letting it affect Finished
vs Running.
🧹 Nitpick comments (3)
examples/pomodoro-timer/src/index.tsx (3)

313-321: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Fixed container width: 54 / height: 23 conflicts with the "responds well to terminal resizing" objective.

The linked issue explicitly asks that the layout respond well to terminal resizing, and this constructor block (part of this change) hardcodes absolute dimensions. hintsBox elsewhere in the same constructor already uses width: '100%', showing percentage sizing is supported — the outer container could adopt the same approach (or clamp to terminal size) to actually satisfy this requirement instead of rendering a fixed-size box regardless of terminal dimensions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/pomodoro-timer/src/index.tsx` around lines 313 - 321, The
constructor for the main container is using fixed absolute dimensions, which
prevents the layout from adapting when the terminal is resized. Update the
container setup in the class constructor so it uses responsive sizing like the
existing hints box pattern (for example percentage-based width/height or
terminal-clamped dimensions) instead of hardcoded values, while keeping the rest
of the styling intact.

120-122: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Style spread order lets callers override the widget's required fixed height.

super({ height: 5, ...style }) (and the analogous height: 1 calls in GradientProgressBar and StatusBadge) puts the forced height before the spread, so any height passed in style silently overrides it. BigTimer._renderSelf unconditionally writes 5 rows — if height ever gets overridden smaller, digit rows past the content rect would be clipped/misaligned with no guard. Not triggered today (no caller currently passes height), but it's a latent footgun for a value these widgets structurally depend on.

🛡️ Proposed fix (apply the same pattern to all three constructors)
-        super({ height: 5, ...style });
+        super({ ...style, height: 5 });

Also applies to: 163-166, 250-252

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/pomodoro-timer/src/index.tsx` around lines 120 - 122, The
fixed-height widgets are allowing caller styles to override their required
height because the spread order in the constructors puts the default height
before the incoming style. Update the constructors for BigTimer,
GradientProgressBar, and StatusBadge so their required height is applied after
merging style (or otherwise enforced), keeping the widget height immutable from
callers and matching the assumptions in BigTimer._renderSelf.

103-113: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate/diverging color-threshold logic between getDynamicColor and GradientProgressBar.getColorAtFraction.

Both functions map a 0-1 fraction to a color using the same 0.25/0.50/0.75 breakpoints, but with different endpoints (getDynamicColor ends flat at #22c55e; getColorAtFraction interpolates further to emerald #10b981 past 0.75). At value=1, BigTimer/phaseLabel/border show #22c55e while the progress bar's tail and label show #10b981 — a visible palette mismatch across widgets that are all meant to represent the same "elapsed fraction". Consider consolidating into one shared color-mapping utility to keep visuals consistent and avoid future drift.

Also applies to: 191-219

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/pomodoro-timer/src/index.tsx` around lines 103 - 113, The
elapsed-fraction color logic is duplicated and has drifted between
getDynamicColor and GradientProgressBar.getColorAtFraction, causing mismatched
colors at the same fraction value. Unify the threshold-to-color mapping into a
single shared utility and have both getDynamicColor and
GradientProgressBar.getColorAtFraction use it so BigTimer, phaseLabel, border,
and the progress bar all resolve the same palette at 0.25/0.50/0.75 and 1.0.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@examples/pomodoro-timer/src/index.tsx`:
- Around line 428-433: The white flash assignment in the timer update logic uses
unnecessary `as const` assertions, which should be removed because
`this._bigTimer.style.fg` already accepts a `Color`. Update the `progress < 0.5`
branch in the `index.tsx` timer rendering code so the `white` color object is
expressed plainly, and keep the `getDynamicColor(elapsed)` path unchanged.

---

Outside diff comments:
In `@examples/pomodoro-timer/src/index.tsx`:
- Around line 440-486: The status logic in _updateDisplay is incorrectly using
_toastTimer to mean “Finished,” which makes the StatusBadge show FINISHED during
the 3-second phase-start toast even though the timer is running. Update
_updateDisplay and the _showToast transition flow so the badge is driven by the
actual timer state (_running and whether the countdown has reached zero), not by
whether a toast is visible; keep _toastTimer only for toast display timing and
avoid letting it affect Finished vs Running.

---

Nitpick comments:
In `@examples/pomodoro-timer/src/index.tsx`:
- Around line 313-321: The constructor for the main container is using fixed
absolute dimensions, which prevents the layout from adapting when the terminal
is resized. Update the container setup in the class constructor so it uses
responsive sizing like the existing hints box pattern (for example
percentage-based width/height or terminal-clamped dimensions) instead of
hardcoded values, while keeping the rest of the styling intact.
- Around line 120-122: The fixed-height widgets are allowing caller styles to
override their required height because the spread order in the constructors puts
the default height before the incoming style. Update the constructors for
BigTimer, GradientProgressBar, and StatusBadge so their required height is
applied after merging style (or otherwise enforced), keeping the widget height
immutable from callers and matching the assumptions in BigTimer._renderSelf.
- Around line 103-113: The elapsed-fraction color logic is duplicated and has
drifted between getDynamicColor and GradientProgressBar.getColorAtFraction,
causing mismatched colors at the same fraction value. Unify the
threshold-to-color mapping into a single shared utility and have both
getDynamicColor and GradientProgressBar.getColorAtFraction use it so BigTimer,
phaseLabel, border, and the progress bar all resolve the same palette at
0.25/0.50/0.75 and 1.0.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: aa50b944-7f82-4369-9b22-fcbd38f2c45b

📥 Commits

Reviewing files that changed from the base of the PR and between 85e8d36 and 1644cb6.

📒 Files selected for processing (1)
  • examples/pomodoro-timer/src/index.tsx

Comment on lines +428 to +433
// Flash the timer white then fade back to dynamic progress color
if (progress < 0.5) {
this._timerDisplay.style.fg = { type: 'named', name: 'white' };
this._bigTimer.style.fg = { type: 'named' as const, name: 'white' as const };
} else {
this._timerDisplay.style.fg = this._phase === 'work'
? { type: 'named' as const, name: 'red' as const }
: { type: 'named' as const, name: 'green' as const };
const elapsed = this._elapsedFraction();
this._bigTimer.style.fg = getDynamicColor(elapsed);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the target file structure first.
ast-grep outline examples/pomodoro-timer/src/index.tsx --view expanded || true

# Read the relevant region around the cited lines.
sed -n '240,290p' examples/pomodoro-timer/src/index.tsx
echo '---'
sed -n '420,440p' examples/pomodoro-timer/src/index.tsx

# Locate Color type / fg declarations.
rg -n "type Color|interface Color|fg: Color|style\.fg|named.*white|named.*black" examples/pomodoro-timer/src/index.tsx . --glob '!**/node_modules/**'

Repository: Karanjot786/TermUI

Length of output: 24943


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,120p' packages/core/src/style/Color.ts
echo '---'
sed -n '150,210p' packages/core/src/style/Style.ts
echo '---'
sed -n '1,80p' examples/calculator/src/index.tsx

Repository: Karanjot786/TermUI

Length of output: 8563


Remove the unnecessary as const assertions
this._bigTimer.style.fg is already typed as Color, so { type: 'named', name: 'white' } is enough here. The current assertions add noise and break the TypeScript guideline about inline-explained assertions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/pomodoro-timer/src/index.tsx` around lines 428 - 433, The white
flash assignment in the timer update logic uses unnecessary `as const`
assertions, which should be removed because `this._bigTimer.style.fg` already
accepts a `Color`. Update the `progress < 0.5` branch in the `index.tsx` timer
rendering code so the `white` color object is expressed plainly, and keep the
`getDynamicColor(elapsed)` path unchanged.

Source: Coding guidelines

@Karanjot786 Karanjot786 added gssoc:approved Approved PR. Earns +50 base points. quality:clean x 1.2 multiplier. Clean implementation. level:intermediate +35 pts. Moderate task. labels Jul 3, 2026
@Karanjot786 Karanjot786 merged commit fbf79ed into Karanjot786:main Jul 3, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:examples Example apps. gssoc:approved Approved PR. Earns +50 base points. level:intermediate +35 pts. Moderate task. quality:clean x 1.2 multiplier. Clean implementation.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat : Enhance Pomodoro Timer Example UI with Dynamic Progress States and Improved Terminal UX

2 participants