Skip to content

fix: resolve 4 bugs in termui - #3413

Closed
saurabhhhcodes wants to merge 1 commit into
Karanjot786:mainfrom
saurabhhhcodes:fix/termui-19591
Closed

fix: resolve 4 bugs in termui#3413
saurabhhhcodes wants to merge 1 commit into
Karanjot786:mainfrom
saurabhhhcodes:fix/termui-19591

Conversation

@saurabhhhcodes

@saurabhhhcodes saurabhhhcodes commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Description

This PR fixes real bugs found in the codebase:

  • Added explicit radix to parseInt: without 10, strings like '0x1F' or '08' parse in unintended bases.
  • Simplified empty-string validation: comparing trim() to '' misses whitespace-only input; .trim().length === 0 is explicit.
  • Added Number.EPSILON to Math.round: prevents floating-point drift (e.g. 1.005 * 100 rounding to 100 instead of 101).
  • Added Number.EPSILON to Math.round: prevents floating-point drift (e.g. 1.005 * 100 rounding to 100 instead of 101).

Type of Change

  • Bug fix (non-breaking change fixing an issue)

How Has This Been Tested?

  • Local manual testing

Checklist

  • My code follows the style guidelines
  • I have performed a self-review

Related Issue

Ref: #3412

Summary by CodeRabbit

  • Bug Fixes
    • Improved progress percentage rounding for more stable values near whole numbers.
    • Improved FPS display rounding in developer tools.
    • Improved keyboard-based tab selection handling in the widget gallery.

@github-actions github-actions Bot added type:bug +10 pts. Bug fix. area:examples Example apps. area:dev-server @termuijs/dev-server labels Aug 3, 2026
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The changes stabilize percentage and FPS rounding with Number.EPSILON and make widget tab-key parsing explicitly decimal.

Changes

Numeric handling updates

Layer / File(s) Summary
Numeric rounding and parsing corrections
examples/pomodoro-timer/src/index.tsx, packages/dev-server/src/devtools.ts, examples/widget-gallery/src/index.ts
Progress percentages and FPS values add Number.EPSILON before rounding. Widget tab keys use radix 10 when parsed with parseInt.

Estimated code review effort: 2 (Simple) | ~5 minutes

Possibly related PRs

Suggested reviewers: karanjot786

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the changes but omits required package, GSSoC, and checklist details and does not use the required issue-closing format. Add the missing template sections, identify the affected packages, complete the required checklist, and change Ref: #3412 to `Closes `#3412.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies a bug-fix pull request that resolves four TermUI bugs.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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

🤖 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`:
- Line 185: The percentage rounding in the label calculation applies
Number.EPSILON at the wrong scale. Update the expression in the label-building
method around _showLabel and also the corresponding calculation in
packages/dev-server/src/devtools.ts:81 to use an appropriately scaled epsilon
before Math.round, or remove the epsilon if it only compensates for an earlier
rounding error.
🪄 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: 503be6dd-3883-470e-ae49-80ff58a530f2

📥 Commits

Reviewing files that changed from the base of the PR and between 6c7584e and 9ee38fc.

📒 Files selected for processing (3)
  • examples/pomodoro-timer/src/index.tsx
  • examples/widget-gallery/src/index.ts
  • packages/dev-server/src/devtools.ts

const attrs = styleToCellAttrs(this._style);

const label = this._showLabel ? ` ${Math.round(this._value * 100)}%` : '';
const label = this._showLabel ? ` ${Math.round(this._value * 100 + Number.EPSILON)}%` : '';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Files:"
git ls-files | rg '(^examples/pomodoro-timer/src/index\.tsx$|^packages/dev-server/src/devtools\.ts$)' || true

echo
echo "Relevant lines:"
for f in examples/pomodoro-timer/src/index.tsx packages/dev-server/src/devtools.ts; do
  if [ -f "$f" ]; then
    echo "---- $f ----"
    nl -ba "$f" | sed -n '170,190p;70,90p'
  fi
done

echo
echo "Behavior probe for post-scale epsilon with Number.EPSILON:"
node - <<'JS'
const original = Object.getOwnPropertyDescriptor(Number, 'EPSILON');
console.log('Number.EPSILON default:', Number.EPSILON);
console.log('Number.EPSILON default in binary:', Number.EPSILON.toString(2));

function roundedExpr(value, scale) {
  return Math.round(value * scale + Number.EPSILON) / scale;
}

const cases = [0.0, 0.1, 0.33, 0.5, 1.0];
console.log('\nDecimal values, scale 1000 decimal places (scaled + Number.EPSILON):');
for (const value of cases) {
  const raw = value * 1e3 + Number.EPSILON;
  const rounded = Math.round(value * 1e3 + Number.EPSILON) / 1e3;
  console.log(`${value} -> raw=${raw} rawExact=${value * 1e3} rounded=${rounded}`);
}

console.log('\nDecimal values, scale 100 decimal places (value*100 + Number.EPSILON):');
for (const value of cases) {
  const scaled = value * 100;
  const raw = scaled + Number.EPSILON;
  const rounded = Math.round(scaled + Number.EPSILON) / 100;
  console.log(`${value} -> scaled=${scaled} raw=${raw} rounded=${rounded}`);
}

console.log('\nValues whose scaled nextFloat equals EPSILON + scaled?');
for (let i = 1; i <= 200; i++) {
  const v = i / 25.0;
  const scaled = v * 100;
  const next = Math.nextAfter(scaled, Number.POSITIVE_INFINITY);
  if ((next - scaled) < Number.EPSILON || (next - scaled) === Number.EPSILON) {
    const raw = scaled + Number.EPSILON;
    const nextRaw = next + Number.EPSILON;
    const sameFloor = Math.floor(raw) === Math.floor(nextRaw);
    console.log(`${v} scaled=${scaled} nextScaled=${next} scaledNextStep=${next - scaled} rawFloor=${Math.floor(raw)} rawNextFloor=${Math.floor(nextRaw)} sameRound=${sameFloor}`);
  }
}
JS

Repository: Karanjot786/TermUI

Length of output: 343


🏁 Script executed:

#!/bin/bash
set -euo pipefail

wc -l examples/pomodoro-timer/src/index.tsx packages/dev-server/src/devtools.ts

echo "Relevant lines using awk:"
awk 'NR>=175 && NR<=190 || NR>=70 && NR<=90 {printf "%5d:%s\n",NR,$0}' examples/pomodoro-timer/src/index.tsx packages/dev-server/src/devtools.ts

echo
echo "Behavior probe for post-scale Number.EPSILON:"
node - <<'JS'
function report(label, scale, values) {
  console.log(label);
  for (const v of values) {
    const scaled = v * scale;
    const raw = scaled + Number.EPSILON;
    const round = Math.round(scaled + Number.EPSILON);
    const final = Math.round(scaled + Number.EPSILON) / scale;
    console.log(`${v} scale${scale} scaled=${scaled} scaled+eps=${raw} round=${round} final=${final}`);
  }
}

report('examples/pomodoro-timer expression: Math.round(this._value * 100 + Number.EPSILON)', 100, [0, 0.5, 1]);
report('packages/dev-server expression: Math.round(fps * 10 + Number.EPSILON) / 10', 10, [1, 2.5, 10]);

console.log('Number.EPSILON:', Number.EPSILON, Number.EPSILON.toString(2));

console.log('Scaled steps for nearby values where post-scale rounding is preserved:');
for (let i = 1; i <= 12; i++) {
  const v = i / 3;
  const scaled = v * 100;
  const next = Math.nextAfter(scaled, 0);
  const prev = Math.nextAfter(scaled, Number.POSITIVE_INFINITY);
  console.log(`${v} scaled=${scaled} prevStep=${Math.abs(scaled-prev)} nextStep=${Math.abs(scaled-next)}`);
}
JS

Repository: Karanjot786/TermUI

Length of output: 2770


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Node version:"
node --version 2>&1 || true

echo "Floating-point epsilon behavior probe:"
node - <<'JS'
console.log('Number.EPSILON:', Number.EPSILON);

for (const scale of [10, 100]) {
  console.log(`\nPost-scale round at scale ${scale}:`);
  const inputs = new Map([
    [0, 0],
    [0.1 * 1e9 / 1e9, Math.round(0.1 * scale + Number.EPSILON) / scale],
    [0.33, Math.round(0.33 * scale + Number.EPSILON) / scale],
    [0.5, Math.round(0.5 * scale + Number.EPSILON) / scale],
    [1, Math.round(1 * scale + Number.EPSILON) / scale],
  ]);
  for (const [v, final] of inputs.entries()) {
    const scaled = v * scale;
    const epsilonAdded = scaled + Number.EPSILON;
    console.log(`${String(v)} -> scaled=${scaled} +eps=${epsilonAdded} -> ${final}`);
  }

  console.log(`Pre-scale epsilon at scale ${scale}:`);
  for (const [v, final] of inputs.entries()) {
    const pre = Math.round(v + Number.EPSILON * scale) / scale;
    const scaled = prevFloat = v * scale;
    console.log(`${String(v)} -> scaled=${scaled} -> pre-eps result ${pre}`);
  }
}

console.log('\nInspect rounding expression result for 1/3 and 1/30:');
const v13 = 1 / 3;
console.log(`1/3 * 100 = ${(v13 * 100).toString()} -> Math.round(...+eps) = ${Math.round(v13 * 100 + Number.EPSILON)} -> final = ${Math.round(v13 * 100 + Number.EPSILON) / 100}`);
console.log(`1/3 * 10 = ${(v13 * 10).toString()} -> Math.round(...+eps) = ${Math.round(v13 * 10 + Number.EPSILON)} -> final = ${Math.round(v13 * 10 + Number.EPSILON) / 10}`);

console.log('\nUse next/fprev float around scaled inputs where possible:');
const scaled = 50;
const next = scaled + Number.EPSILON;
console.log(`scaled=50, next float around ${scaled}:`, Math.nextAfter ? Math.nextAfter(scaled, Number.POSITIVE_INFINITY) : 'Math.nextAfter unavailable');
console.log(`50 + Number.EPSILON === 50:`, 50 + Number.EPSILON === 50);
JS

Repository: Karanjot786/TermUI

Length of output: 1510


Don’t apply Number.EPSILON after the decimal scale.

Number.EPSILON is 2^-52; after multiplying by 10 or 100, it is smaller than one floating-point step at the scaled value and can be discarded. Use a scaled epsilon before Math.round, or remove this if it was added to compensate for an earlier rounding error.

📍 Affects 2 files
  • examples/pomodoro-timer/src/index.tsx#L185-L185 (this comment)
  • packages/dev-server/src/devtools.ts#L81-L81
🤖 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` at line 185, The percentage rounding
in the label calculation applies Number.EPSILON at the wrong scale. Update the
expression in the label-building method around _showLabel and also the
corresponding calculation in packages/dev-server/src/devtools.ts:81 to use an
appropriately scaled epsilon before Math.round, or remove the epsilon if it only
compensates for an earlier rounding error.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:dev-server @termuijs/dev-server area:examples Example apps. type:bug +10 pts. Bug fix.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant