fix: resolve 4 bugs in termui - #3693
Conversation
📝 WalkthroughWalkthroughThe changes add interval cleanup, improve percentage rounding, log rejected form operations, and make dependency sorting use an explicit comparator. ChangesStreaming timer cleanup
UI and example correctness fixes
Registry dependency sorting
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 Biome (2.5.6)packages/ui/src/Form.tsFile contains syntax errors that prevent linting: Line 142: Expected a statement but instead found '.catch(err => console.error("Promise.all failed:", err))'. 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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: Update the percentage label calculation in the timer’s
label-rendering logic so the floating-point correction is applied to the 0–1
value before multiplying by 100, or use an equivalent scale-aware adjustment.
Preserve the existing conditional label behavior and rounding.
In `@packages/ui/src/Form.ts`:
- Around line 141-142: Fix the Promise.all error-handling flow in Form by
attaching the catch to the Promise.all expression or wrapping its await in
try/catch, removing the standalone invalid .catch syntax. On validation
rejection, reset _isValidating and call markDirty(), ensuring every
state-mutating path follows the widget state-update convention.
In `@scripts/build-registry.ts`:
- Line 47: Update the sorting in collectDeps to compare dependency names with a
string comparator using localeCompare, preserving the tested alphabetical order
of package names.
🪄 Autofix
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: ae664683-002b-40d9-9310-ba402e74e6c7
📒 Files selected for processing (4)
examples/ai-streaming/src/index.tsxexamples/pomodoro-timer/src/index.tsxpackages/ui/src/Form.tsscripts/build-registry.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)}%` : ''; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
node <<'NODE'
const value = 0.145;
const current = Math.round(value * 100 + Number.EPSILON);
const corrected = Math.round((value + Number.EPSILON) * 100);
console.log({ current, corrected });
if (current !== 14 || corrected !== 15) process.exit(1);
NODERepository: Karanjot786/TermUI
Length of output: 187
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '--- candidate file ---\n'
wc -l examples/pomodoro-timer/src/index.tsx
printf '\n--- relevant lines ---\n'
sed -n '1,230p' examples/pomodoro-timer/src/index.tsx
printf '\n--- _value references ---\n'
rg -n "_value|set(Value|Interval)" examples/pomodoro-timer/src/index.tsxRepository: Karanjot786/TermUI
Length of output: 247
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- candidate file ---'
wc -l examples/pomodoro-timer/src/index.tsx || true
printf '%s\n' ''
printf '%s\n' '--- relevant lines ---'
sed -n '1,230p' examples/pomodoro-timer/src/index.tsx || true
printf '%s\n' ''
printf '%s\n' '--- _value and setValue references ---'
rg -n '_value|setValue|setInterval|setInterval' examples/pomodoro-timer/src/index.tsx || trueRepository: Karanjot786/TermUI
Length of output: 7114
Apply Number.EPSILON before converting to percentage.
setValue() stores values in the 0–1 range, but Number.EPSILON is added after _value * 100. The added value is too small to affect rounding for inputs just below half-integer percentage boundaries. Add it to _value before multiplication, or use a scale-aware correction.
Proposed fix
-const label = this._showLabel ? ` ${Math.round(this._value * 100 + Number.EPSILON)}%` : '';
+const label = this._showLabel ? ` ${Math.round((this._value + Number.EPSILON) * 100)}%` : '';📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const label = this._showLabel ? ` ${Math.round(this._value * 100 + Number.EPSILON)}%` : ''; | |
| const label = this._showLabel ? ` ${Math.round((this._value + Number.EPSILON) * 100)}%` : ''; |
🤖 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, Update the percentage
label calculation in the timer’s label-rendering logic so the floating-point
correction is applied to the 0–1 value before multiplying by 100, or use an
equivalent scale-aware adjustment. Preserve the existing conditional label
behavior and rounding.
|
|
||
| .catch(err => console.error("Promise.all failed:", err)); No newline at end of file |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win
Fix the invalid .catch placement before merging.
Line 142 starts a standalone .catch expression, so packages/ui/src/Form.ts does not parse. Attach the handler to the Promise.all(...) expression at Line 83, or use try/catch around await Promise.all(...).
When validation rejects, also reset _isValidating and call markDirty(). The cleanup at Lines 92 and97 runs only after a successful await. Logging the error alone leaves the form in the validating state.
As per coding guidelines, every state-mutating method on a widget calls this.markDirty().
🧰 Tools
🪛 Biome (2.5.6)
[error] 142-142: Expected a statement but instead found '.catch(err => console.error("Promise.all failed:", err))'.
(parse)
🪛 GitHub Actions: CI / 0_build-and-test.txt
[error] 142-142: Build failed during 'tsup' due to a syntax error: Unexpected '.' at '.catch(err => console.error("Promise.all failed:", err));'. TypeScript also reported TS1128, TS1005, and TS2304.
🪛 GitHub Actions: CI / build-and-test
[error] 142-142: tsup/esbuild build failed: Unexpected '.' at the standalone '.catch(err => console.error("Promise.all failed:", err));'. TypeScript also reports TS1128, TS1005, and TS2304 syntax errors. The failing command was 'bun run build'.
🤖 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 `@packages/ui/src/Form.ts` around lines 141 - 142, Fix the Promise.all
error-handling flow in Form by attaching the catch to the Promise.all expression
or wrapping its await in try/catch, removing the standalone invalid .catch
syntax. On validation rejection, reset _isValidating and call markDirty(),
ensuring every state-mutating path follows the widget state-update convention.
Sources: Coding guidelines, Linters/SAST tools
| let m: RegExpExecArray | null; | ||
| while ((m = re.exec(content)) !== null) deps.add(m[1]!); | ||
| return [...deps].sort(); | ||
| return [...deps].sort((a, b) => a - b); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== files =="
git ls-files | rg '(^|/)scripts/(build-registry|build-registry\.test)\.(ts|tsx)$|(^|/)tsconfig.*\.json$|(^|/)package\.json$' || true
echo "== build-registry outline =="
ast-grep outline scripts/build-registry.ts 2>/dev/null || true
echo "== relevant build-registry lines =="
sed -n '1,140p' scripts/build-registry.ts
echo "== relevant build-registry test lines =="
sed -n '110,155p' scripts/build-registry.test.ts
echo "== tsconfig strict settings =="
for f in $(git ls-files | rg '(^|/)tsconfig.*\.json$'); do
echo "-- $f"
sed -n '1,220p' "$f"
doneRepository: Karanjot786/TermUI
Length of output: 28868
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
deps = ['`@termuijs/core`', '`@termuijs/motion`', '`@termuijs/widgets`']
print("numeric comparator returns:", [(a, b, a < b or a > b or a == b, a == b or a > b) for a, b in zip(deps, deps[1:])])
print("NaN comparison behavior:", (float('nan') < 0), (float('nan') > 0), (float('nan') == float('nan')))
print("local compare:", sorted(deps, key=None))
PYRepository: Karanjot786/TermUI
Length of output: 412
🌐 Web query:
TypeScript string subtraction compile error strict comparison operators
💡 Result:
In TypeScript, attempting to perform subtraction on strings (e.g., "10" - "5") results in a compilation error because the language restricts arithmetic operations to specific types [1][2][3]. Why TypeScript Prevents String Subtraction The TypeScript compiler enforces strict type checking for arithmetic operators, including subtraction (-). To prevent common programming errors and unintended implicit type coercion—which is a common source of bugs in JavaScript—TypeScript requires that both the left-hand side and right-hand side of an arithmetic operation be of type number, bigint, any, or a numeric enum [1][2][3]. When you attempt to subtract strings, the compiler throws error TS2362 or TS2363, stating that the operand must be of one of these permitted types [1][4][2]. If you need to perform numeric subtraction with string data, you must explicitly convert the strings to a numeric type using functions like Number, parseFloat, parseInt, or the unary plus (+) operator [4][2][5]. Strict Comparison Operators Regarding comparison, TypeScript distinguishes between strict (===) and loose (==) operators, and leverages them for type narrowing [6][7]. 1. Strict Equality (===): This operator checks both value and type without performing implicit coercion [8][9]. In TypeScript, if the compiler can statically determine that the types of the two operands have no overlap (e.g., comparing a string literal to a number), it will issue a compile-time error (ts(2367)) [8]. 2. Loose Equality (==): This operator performs type coercion before comparison [6][9]. While this can sometimes be useful (e.g., the common null-check idiom value == null), it is generally discouraged in TypeScript because it can mask type-related bugs [6][10]. Best Practices - Arithmetic: Always ensure operands are explicitly numeric before performing subtraction [2][3]. - Comparisons: Default to using strict equality (===) throughout your code to avoid the pitfalls of implicit type coercion [10][9]. If you need to safely handle data of varying types, utilize Type Guards (e.g., typeof, instanceof) to narrow types before performing operations [6][7].
Citations:
- 1: https://docs.syntblaze.com/typescript/arithmetic-operators/subtraction
- 2: https://bobbyhadz.com/blog/typescript-left-hand-side-of-arithmetic-operation-must-be-type
- 3: https://ngoclb.com/2023/03/typescript-the-left-hand-side-of-an-arithmetic-operation-must-be-type-any-number-bigint-or-an-enum-type/
- 4: https://stackoverflow.com/questions/69228914/how-to-sort-strings-in-typescript-if-subtraction-cannot-be-performed-on-two-stri
- 5: https://stackoverflow.com/questions/36560806/the-left-hand-side-of-an-arithmetic-operation-must-be-of-type-any-number-or
- 6: https://www.codefixeshub.com/typescript/equality-narrowing-using-in-typescript
- 7: https://github.com/microsoft/TypeScript-Website/blob/v2/packages/documentation/copy/en/handbook-v2/Narrowing.md
- 8: https://docs.syntblaze.com/typescript/comparison-operators/strict-equality
- 9: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Equality_comparisons_and_sameness
- 10: https://drs-web.co.uk/why-use-triple-equal-in-typescript-stack-overflow/
Use a string comparator for dependency names.
collectDeps returns string package names, so a - b is invalid in strict TypeScript and can produce NaN at runtime. Array.sort needs a comparator returning a negative value, zero, or a positive value; use a.localeCompare(b) so @termuijs/core, @termuijs/motion, and @termuijs/widgets stay in tested alphabetical order.
🤖 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 `@scripts/build-registry.ts` at line 47, Update the sorting in collectDeps to
compare dependency names with a string comparator using localeCompare,
preserving the tested alphabetical order of package names.
Description
This PR fixes real bugs found in the codebase:
Number.EPSILONtoMath.round: prevents floating-point drift (e.g.1.005 * 100rounding to 100 instead of 101).Promise.all: an unhandled rejection in any input promise previously crashed silently..sort()coerces elements to strings, so[10, 9, 2]sorts as[10, 2, 9]; numeric comparator sorts correctly.Type of Change
How Has This Been Tested?
Checklist
Related Issue
Ref: #3692
Summary by CodeRabbit