fix: resolve 4 bugs in termui - #3713
Conversation
📝 WalkthroughWalkthroughThe PR updates four examples. It resets stored refresh intervals, changes a form shortcut condition, and limits hexadecimal entity parsing to eight digits. ChangesExample behavior updates
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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/forms-and-validation/src/index.tsx`:
- Line 125: Replace the postfix non-null assertion in the clear-form shortcut
condition within the event-key handler with logical negation of event.ctrl, so
the clear action runs only for the plain “c” key and remains distinct from the
Ctrl+C quit branch.
In `@examples/rss-reader/src/index.tsx`:
- Line 30: Update the entity-decoding logic around codePoint and both numeric
entity branches to accept values only when finite and within the inclusive range
0 through 0x10FFFF before calling String.fromCodePoint. Reuse one shared range
guard so out-of-range positive and negative entities remain safely unreplaced
instead of throwing.
- Line 30: Update the entity decoding logic around codePoint to reject
hexadecimal entities whose digit portion exceeds eight characters by returning
match unchanged, and parse the complete digit portion from entity.slice(2) for
valid entities. Preserve existing decoding behavior for entities within the
allowed length.
🪄 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: 61920c0b-7011-4821-b9f5-e93eef524d20
📒 Files selected for processing (4)
examples/ai-streaming/src/index.tsxexamples/forms-and-validation/src/index.tsxexamples/rss-reader/src/index.tsxexamples/weather/src/index.tsx
| } | ||
|
|
||
| if (event.key === 'c' && event.ctrl === false) { | ||
| if (event.key === 'c' && event.ctrl !) { |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file="examples/forms-and-validation/src/index.tsx"
if rg -nP 'event\.ctrl\s*!' "$file"; then
echo "Postfix assertion remains; use !event.ctrl." >&2
exit 1
fi
rg -nF "event.key === 'c' && !event.ctrl" "$file"Repository: Karanjot786/TermUI
Length of output: 253
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file="examples/forms-and-validation/src/index.tsx"
# Show the surrounding event handler and related shortcuts.
sed -n '112,132p' "$file"
echo "---"
# Static verifier: identify this event.ctrl ! token and its likely token context.
python3 - <<'PY'
from pathlib import Path
p = Path("examples/forms-and-validation/src/index.tsx")
text = p.read_text()
needle = "event.ctrl !"
line_no = text.count("\n", 0, text.find(needle)) + 1 if needle in text else -1
print(f"needle={needle!r}")
print(f"line_no={line_no}")
print(f"in_event_ctrl_assertion=", needle in text)
print(text[text.find(needle)-30:text.find(needle)+60] if needle in text else "")
PY
# Deterministic JavaScript semantics probe: remove ! from a postfix non-null expression
# and compare it to logical negation for the same inputs.
node - <<'JS'
function originalExpr(event) {
return event.key === 'c' && event.ctrl !;
}
JSRepository: Karanjot786/TermUI
Length of output: 999
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Use Node only for a runtime probe of the JavaScript expression shape,
# without executing repository code or assuming dependencies.
node - <<'JS'
function runtimeValue(event) {
return event.key === 'c' && event.ctrl;
}
function negation(event) {
return event.key === 'c' && !event.ctrl;
}
for (const event of [
{ key: 'c', ctrl: false },
{ key: 'c', ctrl: true },
{ key: 'C', ctrl: false }
]) {
console.log(JSON.stringify(event) + ' => runtimeValue=' + runtimeValue(event) + ', negation=' + negation(event));
}
JSRepository: Karanjot786/TermUI
Length of output: 342
Use logical negation for the clear-form shortcut.
event.ctrl ! is a postfix non-null assertion, so at runtime it is equivalent to event.ctrl. This skips the clear form for plain c, while event.key === 'c' && event.ctrl is already handled as quit. Use !event.ctrl and remove the forbidden type-assertion style.
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.
| 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, Replace the postfix
non-null assertion in the clear-form shortcut condition within the event-key
handler with logical negation of event.ctrl, so the clear action runs only for
the plain “c” key and remains distinct from the Ctrl+C quit branch.
Source: Coding guidelines
| 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); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file="examples/rss-reader/src/index.tsx"
if ! rg -n 'codePoint\s*<=\s*0x10FFFF' "$file"; then
echo "Unicode range validation is missing." >&2
exit 1
fiRepository: Karanjot786/TermUI
Length of output: 193
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file="examples/rss-reader/src/index.tsx"
echo "== file size =="
wc -l "$file"
echo "== target section =="
sed -n '1,80p' "$file" | cat -n
echo "== regex entity references to codePoint =="
rg -n 'codePoint|fromCodePoint|parseInt|entity|decode|replace' "$file"Repository: Karanjot786/TermUI
Length of output: 4002
🌐 Web query:
MDN String.fromCodePoint RangeError code point above 0x10FFFF
💡 Result:
The String.fromCodePoint method in JavaScript throws a RangeError when it is passed a value that is not a valid Unicode code point [1][2]. The valid range for Unicode code points is from 0 to 0x10FFFF (inclusive) [1][3]. Specifically, a RangeError is thrown if an argument meets any of the following conditions: - It is not an integer [1][2]. - It is less than 0 [1][2]. - It is greater than 0x10FFFF (which is 1114111 in decimal) [1][2]. This behavior is consistent with the ECMAScript specification, which mandates that if a code point provided to String.fromCodePoint is not an integral number or falls outside the 0 to 0x10FFFF range, a RangeError exception must be thrown [4][5][6][7]. Examples of invalid inputs that trigger this error include NaN, negative integers (e.g., -1), non-integers (e.g., 3.14), and values like Infinity or anything exceeding 0x10FFFF [1][2].
Citations:
- 1: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint
- 2: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Not_a_valid_code_point
- 3: https://github.com/mdn/content/blob/main/files/en-us/web/javascript/reference/errors/not_a_valid_code_point/index.md
- 4: https://tc39.es/ecma262/pr/3783/multipage/text-processing.html
- 5: https://tc39.es/ecma262/multipage/text-processing.html
- 6: https://tc39.es/ecma262/2023/multipage/text-processing.html
- 7: https://tc39.es/ecma262/2022/multipage/text-processing.html
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== JavaScript probe =="
node - <<'JS'
console.log(
'isFinite(0x110000)=', Number.isFinite(0x110000),
'isFinite(-1)=', Number.isFinite(-1),
'isFinite(+Infinity)=', Number.isFinite(Infinity)
);
for (const code of [-1, 0, 0x10FFFF, 0x110000, 0x110001]) {
try {
const out = String.fromCodePoint(code);
console.log(`fromCodePoint(${code})=ok ${JSON.stringify(out)}`);
} catch (error) {
console.log(`fromCodePoint(${code})=${error.name}: ${error.message}`);
}
}
JSRepository: Karanjot786/TermUI
Length of output: 486
Validate the decoded code point range before String.fromCodePoint.
Number.isFinite(codePoint) still accepts out-of-range values, so inputs such as � or negative entities reach String.fromCodePoint and throw RangeError, aborting text replacement. Add one shared 0 to 0x10FFFF guard for both numeric entity branches.
🤖 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 and both numeric entity branches to accept values only
when finite and within the inclusive range 0 through 0x10FFFF before calling
String.fromCodePoint. Reuse one shared range guard so out-of-range positive and
negative entities remain safely unreplaced instead of throwing.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file="examples/rss-reader/src/index.tsx"
if rg -n 'slice\(2,\s*10\)' "$file"; then
echo "Hexadecimal entities are still truncated." >&2
exit 1
fiRepository: Karanjot786/TermUI
Length of output: 267
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file="examples/rss-reader/src/index.tsx"
printf '--- outline ---\n'
ast-grep outline "$file" --view expanded || true
printf '\n--- relevant source ---\n'
sed -n '1,80p' "$file" | cat -n
printf '\n--- deterministic behavior probe ---\n'
node - <<'JS'
const entities = ['&`#x41`;', '&`#x000000041`;', '&`#x110000`;'];
for (const entity of entities) {
const digits = entity.slice(2, 10);
const codePoint = Number.parseInt(digits, 16);
let result;
try {
result = String.fromCodePoint(codePoint);
} catch (er) {
result = `${er.name}: ${er.message}`;
}
console.log(JSON.stringify({entity, digits, codePoint, result}));
}
JSRepository: Karanjot786/TermUI
Length of output: 247
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file="examples/rss-reader/src/index.tsx"
printf '%s\n' '--- outline ---'
ast-grep outline "$file" --view expanded || true
printf '%s\n' ''
printf '%s\n' '--- relevant source ---'
sed -n '1,120p' "$file" | cat -n
printf '%s\n' ''
printf '%s\n' '--- deterministic behavior probe ---'
node - <<'JS'
const cases = [
['&`#x41`;', 0x41],
['&`#x000000041`;', 0x41],
['&`#x00000000000000000000000000000000041`;2', 0x2 /* Number.parseInt ignores trailing junk */],
['&`#x110000`;', 0x110000],
['&`#x123456789`;', 0x23456789],
];
for (const [entity, expectedParsedFromSlice] of cases) {
const digits = entity.slice(2, 10);
const codePoint = Number.parseInt(digits, 16);
let result;
try {
result = String.fromCodePoint(codePoint);
} catch (er) {
result = er.name + ': ' + er.message;
}
console.log(JSON.stringify({entity, digits, codePoint, parsedMatchesExpectedSlice, expectedParsedFromSlice, result}));
}
JSRepository: Karanjot786/TermUI
Length of output: 6724
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- deterministic behavior probe ---'
node - <<'JS'
const entities = [
['&`#x41`;', 0x41],
['&`#x000000041`;', 0x41],
['&`#x110000`;', 0x110000],
['&`#x123456789`;', 0x123456789],
];
for (const [entity, expectedParsedFromSlice] of entities) {
const digits = entity.slice(2, 10);
const codePoint = Number.parseInt(digits, 16);
let result;
try {
result = String.fromCodePoint(codePoint);
} catch (er) {
result = er.name + ': ' + er.message;
}
console.log(JSON.stringify({entity, digits, codePoint, expectedParsedFromSlice, result}));
}
JSRepository: Karanjot786/TermUI
Length of output: 748
Reject overlong hexadecimal entities instead of truncating them.
The regex allows more than eight hex digits, but entity.slice(2, 10) drops the remainder. For example, A matches, but decodeEntities() keeps the original match because Number.parseInt('x0000000', 16) returns NaN, so overlong entities are not decoded. Return match when the digit part exceeds eight and continue parsing the digits from entity.slice(2).
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);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);🤖 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 to reject hexadecimal entities whose digit portion
exceeds eight characters by returning match unchanged, and parse the complete
digit portion from entity.slice(2) for valid entities. Preserve existing
decoding behavior for entities within the allowed length.
Description
This PR fixes real bugs found in the codebase:
x === trueis equivalent tox(andx === falseto!x), and shorter to read.parseInt: without10, strings like'0x1F'or'08'parse in unintended bases.Type of Change
How Has This Been Tested?
Checklist
Related Issue
Ref: #3712
Summary by CodeRabbit