Skip to content

fix: resolve 4 bugs in termui - #3446

Open
saurabhhhcodes wants to merge 1 commit into
Karanjot786:mainfrom
saurabhhhcodes:fix/termui-86988
Open

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

Conversation

@saurabhhhcodes

@saurabhhhcodes saurabhhhcodes commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Description

This PR fixes real bugs found in the codebase:

  • Prevented interval leak: repeated mounts now clear the previous interval before scheduling a new one.
  • Removed redundant boolean comparison: x === true is equivalent to x (and x === false to !x), and shorter to read.
  • Added explicit radix to parseInt: without 10, strings like '0x1F' or '08' parse in unintended bases.
  • 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: #3445

Summary by CodeRabbit

  • Bug Fixes
    • Prevented duplicate streaming timers when the streaming example is reinitialized.
    • Corrected the clear-form keyboard shortcut behavior for non-control key combinations.
    • Improved RSS feed decoding for hexadecimal character entities.
    • Made switch animations more consistent when positioning the control at fractional progress values.

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

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Four isolated fixes update streaming interval cleanup, form shortcut detection, RSS hexadecimal entity parsing, and switch knob rounding.

Changes

Streaming timer management

Layer / File(s) Summary
Reset the streaming interval
examples/ai-streaming/src/index.tsx
The constructor clears the existing global interval before creating a new streaming update interval.

Form shortcut detection

Layer / File(s) Summary
Update the clear-form condition
examples/forms-and-validation/src/index.tsx
The shortcut uses !event.ctrl while preserving modal behavior.

RSS entity parsing

Layer / File(s) Summary
Bound hexadecimal parsing
examples/rss-reader/src/index.tsx
Hexadecimal entity decoding parses at most eight digits after &#x``.

Switch positioning

Layer / File(s) Summary
Adjust knob rounding
packages/ui/src/Switch.ts
The knob-position calculation adds Number.EPSILON before rounding.

Estimated code review effort: 2 (Simple) | ~10 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 four fixes, but it omits the required package list, uses a non-closing issue reference, and leaves most required checklist and GSSoC fields unaddressed. Add the required package list, change the issue reference to Closes #3445``, and complete the repository checklist and GSSoC section.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies a bug-fix change and follows the required type: short description format.
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: 3

🧹 Nitpick comments (1)
packages/ui/src/Switch.ts (1)

117-117: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add regression coverage for the rounding thresholds.

Line 117 changes knob placement near the 0.25 and 0.75 progress boundaries. The existing packages/ui/src/Switch.test.ts test checks only rendering bounds. Add focused tests for values just below and at both thresholds to verify that Number.EPSILON corrects floating-point drift without changing behavior away from those boundaries.

🤖 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/Switch.ts` at line 117, Add focused regression tests in
Switch.test.ts for knob placement around the 0.25 and 0.75 animation-progress
thresholds, covering values just below and exactly at each boundary. Verify
Number.EPSILON preserves the expected rounded knob position at the thresholds
while leaving behavior unchanged for progress values away from them.
🤖 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/ai-streaming/src/index.tsx`:
- Around line 43-45: Update the interval handling around _streamingText.tick to
provide an explicit TypeScript declaration for window.__interval, either by
augmenting the Window interface or by using an explicitly typed interval handle
with an explanatory comment. Preserve the existing clearInterval and setInterval
behavior while ensuring the example passes strict DOM TypeScript checks.

In `@examples/forms-and-validation/src/index.tsx`:
- Line 125: In the keyboard event condition, update the `event.ctrl` negation in
the `if` statement so the `!` operator precedes the property access, producing
valid TypeScript syntax while preserving the existing key check.

In `@examples/rss-reader/src/index.tsx`:
- Line 30: Update the entity-decoding logic around codePoint so hexadecimal
entities longer than eight digits are rejected before parsing, or otherwise
parsed without silently truncating digits. Preserve valid entities such as
&`#x000000041`; by ensuring all captured hexadecimal digits are handled according
to the intended length limit.

---

Nitpick comments:
In `@packages/ui/src/Switch.ts`:
- Line 117: Add focused regression tests in Switch.test.ts for knob placement
around the 0.25 and 0.75 animation-progress thresholds, covering values just
below and exactly at each boundary. Verify Number.EPSILON preserves the expected
rounded knob position at the thresholds while leaving behavior unchanged for
progress values away from them.
🪄 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: 6df8cc6c-2271-475d-b9ff-9e32a7eec773

📥 Commits

Reviewing files that changed from the base of the PR and between 6c7584e and 931b804.

📒 Files selected for processing (4)
  • examples/ai-streaming/src/index.tsx
  • examples/forms-and-validation/src/index.tsx
  • examples/rss-reader/src/index.tsx
  • packages/ui/src/Switch.ts

Comment on lines +43 to 45
clearInterval(window.__interval); window.__interval = setInterval(() => {
this._streamingText.tick();
}, 50);

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

# Expect: a Window augmentation or another declaration for __interval.
rg -n -C 3 '(__interval|interface Window|declare global)' --glob '*.ts' --glob '*.tsx'

Repository: Karanjot786/TermUI

Length of output: 156


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Tracked TypeScript/TSX files containing __interval:"
git ls-files '*.ts' '*.tsx' | xargs grep -n '__interval' || true

echo
echo "Tracked TypeScript config files:"
git ls-files '*tsconfig*.json' 'tsconfig*.json' 'package.json' 'vite.config.*' 'astro.config.*'

echo
echo "Relevant file outline:"
if [ -f examples/ai-streaming/src/index.tsx ]; then
  ast-grep outline examples/ai-streaming/src/index.tsx || true
  echo
  sed -n '1,120p' examples/ai-streaming/src/index.tsx | nl -ba
fi

echo
echo "Other explicit window property declarations/augmentations:"
git ls-files '*.ts' '*.tsx' '*.d.ts' | xargs grep -nE 'interface\s+Window|declare\s+global|declare\s+namespace\s+Window|\.(__interval)|window\.__interval|ReturnType<.*setInterval|number' || true

Repository: Karanjot786/TermUI

Length of output: 2606


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "tsconfig files for ai-streaming/example base:"
while IFS= read -r f; do
  echo "--- $f"
  sed -n '1,220p' "$f"
done < <(git ls-files | grep -E '(^examples/ai-streaming/.*tsconfig\.json$|(^tsconfig\.base\.json$|(^package\.json$))')

echo
echo "All __interval occurrences under git:"
git ls-files | grep -E '\.(ts|tsx|d\.ts)$' | xargs grep -n -- '__interval' || true

echo
echo "Window type augmentations/decorations under git:"
git ls-files '*.ts' '*.tsx' '*.d.ts' | xargs grep -n -- -E 'interface\s+Window|declare\s+global|declare\s+namespace\s+Window|window\.__|__interval|setInterval' || true

echo
echo "TypeScript availability and toolchain:"
command -v npx || true
command -v tsc || true
node -e "try { console.log('node:', process.version); } catch (e) { console.log(e.message); }"

if command -v tsc >/dev/null 2>&1; then
  tmp="$(mktemp -d)"
  trap 'rm -rf "$tmp"' EXIT
  cat > "$tmp/check.ts" <<'TS'
function tick() {
  clearInterval(window.__interval as unknown as number | undefined);
  window.__interval = setInterval(() => {
    console.log('tick');
  }, 50);
}
function main() {
  tick();
}
TS
  echo "--- tsc version"
  tsc --version
  echo "--- tsc with defaults"
  tsc "$tmp/check.ts" --strict --noEmit || true
else
  echo "tsc not available in PATH"
fi

Repository: Karanjot786/TermUI

Length of output: 278


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Files under examples/ai-streaming tracked by git:"
git ls-files examples/ai-streaming | sed -n '1,120p'

echo
echo "Candidate tsconfig/package files:"
git ls-files | grep -E '(^|/)(tsconfig.*\.json|package\.json)$' | sed -n '1,160p'

echo
echo "Relevant ai-streaming tsconfig/package if present:"
for f in examples/ai-streaming/tsconfig.json examples/ai-streaming/tsconfig.*.json examples/ai-streaming/package.json tsconfig.base.json package.json; do
  if [ -f "$f" ]; then
    echo "--- $f"
    sed -n '1,220p' "$f"
  fi
done

echo
echo "All __interval occurrences in tracked source:"
while IFS= read -r f; do
  grep -n -- '__interval' "$f" || true
done < <(git ls-files '*.ts' '*.tsx' '*.d.ts')

echo
echo "TypeScript available:"
command -v tsc || true
tsc --version 2>/dev/null || true

if command -v tsc >/dev/null 2>&1; then
  tmp="$(mktemp -d)"
  cat > "$tmp/check.ts" <<'PY'
function tick() {
  clearInterval(window.__interval as unknown as number | undefined);
  window.__interval = setInterval(() => {
    console.log('tick');
  }, 50);
}
function main() {
  tick();
}
PY
  echo "--- tsc strict/noEmit test"
  tsc "$tmp/check.ts" --strict --noEmit --lib ES2020,DOM || true
fi

Repository: Karanjot786/TermUI

Length of output: 6672


Avoid using window.__interval without a type declaration.

Under strict TypeScript checks, window.__interval has no declaration in the repo, so this example can fail npx -y tsc --strict --lib ES2022,DOM unless the type definitions are intentionally relying on the run-time host. Add a Window augmentation or type the handle explicitly with a comment if the global is intentional.

🤖 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/ai-streaming/src/index.tsx` around lines 43 - 45, Update the
interval handling around _streamingText.tick to provide an explicit TypeScript
declaration for window.__interval, either by augmenting the Window interface or
by using an explicitly typed interval handle with an explanatory comment.
Preserve the existing clearInterval and setInterval behavior while ensuring the
example passes strict DOM TypeScript checks.

Source: Coding guidelines

}

if (event.key === 'c' && event.ctrl === false) {
if (event.key === 'c' && event.ctrl !) {

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 | 🔴 Critical | ⚡ Quick win

Fix the invalid TypeScript condition.

event.ctrl ! is invalid TypeScript syntax and prevents compilation. Move ! before event.ctrl.

Proposed fix
-        if (event.key === 'c' && event.ctrl !) {
+        if (event.key === 'c' && !event.ctrl) {
📝 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.

Suggested change
if (event.key === 'c' && event.ctrl !) {
if (event.key === 'c' && !event.ctrl) {
🤖 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/forms-and-validation/src/index.tsx` at line 125, In the keyboard
event condition, update the `event.ctrl` negation in the `if` statement so the
`!` operator precedes the property access, producing valid TypeScript syntax
while preserving the existing key check.

return value.replace(/&(#x?[0-9a-fA-F]+|[a-zA-Z]+);/g, (match, entity: string) => {
if (entity.startsWith('#x')) {
const codePoint = Number.parseInt(entity.slice(2), 16);
const codePoint = Number.parseInt(entity.slice(2, 10), 16);

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

Reject overlong hexadecimal entities instead of truncating them.

The regular expression captures every hexadecimal digit, but entity.slice(2, 10) silently drops digits after the eighth. For example, &#x000000041; becomes 0x00000004 instead of 0x41, which corrupts RSS text.

If eight digits is the intended limit, reject longer entities before parsing.

Proposed fix
-      const codePoint = Number.parseInt(entity.slice(2, 10), 16);
+      const digits = entity.slice(2);
+      if (digits.length > 8) {
+        return match;
+      }
+      const codePoint = Number.parseInt(digits, 16);
📝 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.

Suggested change
const codePoint = Number.parseInt(entity.slice(2, 10), 16);
const digits = entity.slice(2);
if (digits.length > 8) {
return match;
}
const codePoint = Number.parseInt(digits, 16);
🤖 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/rss-reader/src/index.tsx` at line 30, Update the entity-decoding
logic around codePoint so hexadecimal entities longer than eight digits are
rejected before parsing, or otherwise parsed without silently truncating digits.
Preserve valid entities such as &`#x000000041`; by ensuring all captured
hexadecimal digits are handled according to the intended length limit.

@coderabbitai coderabbitai Bot mentioned this pull request Aug 4, 2026
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant