Skip to content

fix: resolve 4 bugs in termui - #3444

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

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

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.
  • Fixed default sort: .sort() coerces elements to strings, so [10, 9, 2] sorts as [10, 2, 9]; numeric comparator sorts correctly.
  • Fixed default sort: .sort() coerces elements to strings, so [10, 9, 2] sorts as [10, 2, 9]; numeric comparator sorts correctly.

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: #3443

Summary by CodeRabbit

  • Bug Fixes
    • Prevented stale streaming processes from continuing when streaming is restarted.
    • Improved form keyboard shortcut handling while preserving existing modal behavior.
    • Ensured selected items in multi-select controls appear in consistent numeric order.
  • Chores
    • Made generated dependency listings use a consistent ordering for more predictable builds.

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

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The changes clear an existing streaming interval, alter a form shortcut condition, and add explicit numeric comparators for option indices and dependency names.

Changes

Behavior corrections

Layer / File(s) Summary
Streaming interval cleanup
examples/ai-streaming/src/index.tsx
The constructor clears the previous global interval before creating a new streaming interval.
Form shortcut condition
examples/forms-and-validation/src/index.tsx
The clear-form shortcut condition changes to event.ctrl !.
Numeric sorting updates
packages/ui/src/MultiSelect.ts, scripts/build-registry.ts
Checked option indices and dependency package names use explicit numeric comparator callbacks.

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 covers the changes and testing but omits required package, GSSoC, and complete checklist details, and does not use the required issue-closing format. Add the missing template sections, identify affected packages, complete the required checklist, and change the issue reference to Closes #3443``.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies a bug-fix change and summarizes the four TermUI fixes.
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/MultiSelect.ts (1)

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

Add coverage for an option index greater than nine.

The existing test in packages/ui/src/MultiSelect.test.ts Lines 104-114 checks only indices 0-2. That case passes with lexicographic sorting. Add an option at index 10 or greater to verify this numeric comparator.

🤖 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/MultiSelect.ts` at line 33, Extend the test covering
MultiSelect checked-option ordering in MultiSelect.test.ts to include an option
at index 10 or higher, and assert the returned options remain numerically
ordered. Keep the existing indices 0–2 while ensuring the test exercises the
numeric comparator in MultiSelect’s checked-options mapping.
🤖 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`:
- Line 43: Update AIStreamingApp to store its streaming interval handle on the
component instance instead of the shared window.__interval global. Ensure each
instance clears only its own timer during unmount/destroy, while preserving the
existing interval setup and cleanup behavior.

In `@examples/forms-and-validation/src/index.tsx`:
- Line 125: Update the keyboard condition in the event handler so the clear-form
shortcut checks for c while Ctrl is not pressed by negating event.ctrl,
preserving the existing Ctrl+C quit branch and allowing plain c to open the form
modal.

In `@scripts/build-registry.ts`:
- Line 47: Update the sort comparator in collectDeps to compare dependency
strings alphabetically rather than subtracting them numerically. Preserve the
returned array of unique dependencies while ensuring the comparator satisfies
TypeScript strict-mode typing and produces the expected lexical order.

---

Nitpick comments:
In `@packages/ui/src/MultiSelect.ts`:
- Line 33: Extend the test covering MultiSelect checked-option ordering in
MultiSelect.test.ts to include an option at index 10 or higher, and assert the
returned options remain numerically ordered. Keep the existing indices 0–2 while
ensuring the test exercises the numeric comparator in MultiSelect’s
checked-options mapping.
🪄 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: f892d73d-0b3c-4f29-a3a3-dccaa0273606

📥 Commits

Reviewing files that changed from the base of the PR and between 6c7584e and 3ddc162.

📒 Files selected for processing (4)
  • examples/ai-streaming/src/index.tsx
  • examples/forms-and-validation/src/index.tsx
  • packages/ui/src/MultiSelect.ts
  • scripts/build-registry.ts

this.addChild(this._streamingText);

setInterval(() => {
clearInterval(window.__interval); window.__interval = setInterval(() => {

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: either one live AIStreamingApp instance or instance-specific interval cleanup.
rg -n -C 5 '\bAIStreamingApp\b|__interval|dispose|unmount|destroy|onUnmount' \
  examples/ai-streaming packages

Repository: Karanjot786/TermUI

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Tracked matches for AIStreamingApp and window.__interval in examples/ai-streaming only:\n'
rg -n -C 4 'AIStreamingApp|__interval|clearInterval|setInterval' examples/ai-streaming || true

printf '\nindex.tsx with line numbers:\n'
cat -n examples/ai-streaming/src/index.tsx

printf '\nPackage files in examples/ai-streaming:\n'
git ls-files examples/ai-streaming

Repository: Karanjot786/TermUI

Length of output: 5944


Keep the streaming interval tied to the app lifecycle.

AIStreamingApp currently uses a single global window.__interval, but TypeScript allows another instance to be created and clear the previous timer. Store the handle on the component and clear it from unmount/destroy so the timer cannot cancel another mounted instance.

🤖 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` at line 43, Update AIStreamingApp to
store its streaming interval handle on the component instance instead of the
shared window.__interval global. Ensure each instance clears only its own timer
during unmount/destroy, while preserving the existing interval setup and cleanup
behavior.

}

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 | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files | grep -F 'examples/forms-and-validation/src/index.tsx' || true

echo "== target lines =="
if [ -f examples/forms-and-validation/src/index.tsx ]; then
  nl -ba examples/forms-and-validation/src/index.tsx | sed -n '105,135p'
fi

echo "== parser semantics probe =="
node - <<'JS'
function parseLike(event) {
  // Simulate JavaScript logical condition parsed as event.ctrl! : postfix non-null assertion.
  // event.ctrl! evaluates event.ctrl as a truthy/falsy value and does not change it.
  return {
    eventCtrl: event.ctrl,
    eventCtrlBang: event.ctrl, // postfix non-null assertion has the same runtime value as event.ctrl here
  }
}
console.log(JSON.stringify({
  plainC: parseLike({key: 'c', ctrl: false}),
  ctrlC: parseLike({key: 'c', ctrl: true}),
}, null, 2))
JS

echo "== exact token/search =="
rg -n "event\.ctrl\s*!|\bkey === 'c'|\bkey ===\"c\"" examples/forms-and-validation/src/index.tsx || true

Repository: Karanjot786/TermUI

Length of output: 271


🏁 Script executed:

#!/bin/sh
set -euo pipefail

echo "== target lines =="
awk 'NR>=105 && NR<=135 { printf "%6d\t%s\n", NR, $0 }' examples/forms-and-validation/src/index.tsx

echo "== exact token/search =="
grep -nE "event\.ctrl\s*!|key === 'c'|key ===\"c\"" examples/forms-and-validation/src/index.tsx || true

echo "== parse semantics probe =="
node - <<'JS'
function evaluateCondition(event) {
  const keyIsC = event.key === 'c'
  const ctrlIsTruthy = Boolean(event.ctrl) // event.ctrl! has the same runtime boolean semantics as event.ctrl
  return keyIsC && ctrlIsTruthy
}
console.log(JSON.stringify({
  plainC: { eventKey: 'c', eventCtrl: false, conditionTrue: evaluateCondition({key: 'c', ctrl: false}) },
  ctrlC: { eventKey: 'c', eventCtrl: true, conditionTrue: evaluateCondition({key: 'c', ctrl: true}) },
}, null, 2))
JS

Repository: Karanjot786/TermUI

Length of output: 1716


Negate event.ctrl to restore the clear-form shortcut.

event.ctrl ! is parsed as the postfix non-null assertion event.ctrl!, so the condition only matches when Ctrl is already held. Since Ctrl+C is handled by the quit branch above, plain c does not open the form modal. Change it to !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, Update the keyboard
condition in the event handler so the clear-form shortcut checks for c while
Ctrl is not pressed by negating event.ctrl, preserving the existing Ctrl+C quit
branch and allowing plain c to open the form modal.

Comment thread scripts/build-registry.ts
let m: RegExpExecArray | null;
while ((m = re.exec(content)) !== null) deps.add(m[1]!);
return [...deps].sort();
return [...deps].sort((a, b) => a - b);

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

if rg -n 'sort\(\(a, b\) => a - b\)' scripts/build-registry.ts; then
  echo "Invalid string subtraction comparator remains." >&2
  exit 1
fi

rg -n -C 3 'collectDeps|sort\(' \
  scripts/build-registry.ts \
  scripts/build-registry.test.ts

Repository: Karanjot786/TermUI

Length of output: 248


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file excerpt =="
sed -n '1,90p' scripts/build-registry.ts 2>/dev/null || true

echo
echo "== test excerpts =="
sed -n '110,150p' scripts/build-registry.test.ts 2>/dev/null || true

echo
echo "== TypeScript availability =="
if command -v tsc >/dev/null 2>&1 && command -v ts-node >/dev/null 2>&1; then
  tmp="$(mktemp -d)"
  yes 'n' | npm init - >/tmp/npm-init.log 2>&1 || true
  npm install typescript --save-dev >/tmp/npm-install.log 2>&1 || true
  cat > "$tmp/test-string-compare.ts" <<'TS'
type SetLike<T> = Iterable<T>;
declare const deps: Set<string>;
const result = [...deps].sort((a: string, b: string) => a - b);
TS
  npx tsc --noEmit --strict "$tmp/test-string-compare.ts" 2>&1 | sed -n '1,40p'
  exit 0
else
  echo "tsc/ts-node not available"
fi

echo
echo "== direct JavaScript probe =="
node - <<'JS'
const deps = new Set(['pkg-b', 'pkg-a', 'pkg-c']);
const result = [...deps].sort((a, b) => a - b);
console.log(JSON.stringify(Array.from(result)));
JS

Repository: Karanjot786/TermUI

Length of output: 5345


🌐 Web query:

Node.js V table function object arithmetic comparison string subtract returns NaN spec

💡 Result:

In JavaScript, and by extension Node.js, the subtraction operator (-) returns NaN (Not-a-Number) when applied to strings that cannot be converted into valid numbers [1][2]. This occurs because the subtraction operator is strictly for numeric or BigInt operations, prompting the engine to perform implicit type conversion using the ToNumber abstract operation on both operands [1][3][4]. When the subtraction operator encounters a string, it attempts to convert that string to a number [1][2]. If the string does not contain a valid numeric representation (e.g., "hello"), the conversion result is NaN [1][5]. Because any mathematical operation performed with NaN results in NaN, the entire expression evaluates to NaN [5][6]. This behavior is explicitly defined in the ECMAScript Language Specification [7][8]. The evaluation of the subtraction operator invokes the EvaluateStringOrNumericBinaryExpression operation [7][8], which eventually relies on the ToNumber abstract operation [4]. Key points regarding this behavior: 1. Implicit Conversion: The subtraction operator triggers an attempt to convert operands to numbers (Number type) [1][4]. 2. NaN Result: If a string is not a valid number, it converts to NaN [1][6]. Mathematically, x - NaN or NaN - y results in NaN [5][6]. 3. Relational Comparisons: Unlike subtraction, relational operators (like < or >) also trigger conversion to numbers but treat operands differently if both are strings, performing a lexicographical (alphabetical) comparison instead of a numeric one [9][10]. If at least one operand is NaN, relational comparisons return false [9][6]. 4. TypeOf NaN: Note that NaN is a value of the Number type, which is why the result of an invalid string subtraction is considered a number type in JavaScript [5]. For predictable results, it is recommended to explicitly convert inputs to numbers using Number, parseInt, or parseFloat before performing arithmetic, rather than relying on implicit coercion [11][2].

Citations:


Replace the numeric comparator with a string comparator.

collectDeps returns strings from Set<string>, and a - b violates TypeScript strict-mode type checking. It also returns NaN, so the dependency list does not sort alphabetically as the test expects.

Proposed fix
-  return [...deps].sort((a, b) => a - b);
+  return [...deps].sort((a, b) => a.localeCompare(b));
📝 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
return [...deps].sort((a, b) => a - b);
return [...deps].sort((a, b) => a.localeCompare(b));
🤖 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 sort comparator in
collectDeps to compare dependency strings alphabetically rather than subtracting
them numerically. Preserve the returned array of unique dependencies while
ensuring the comparator satisfies TypeScript strict-mode typing and produces the
expected lexical order.

Source: Coding guidelines

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