Skip to content

feat(router): add notFound fallback — renders DefaultNotFound on unmatched routes (#2120)#2146

Open
divyanshim27 wants to merge 1 commit into
Karanjot786:mainfrom
divyanshim27:feat/router-not-found-fallback
Open

feat(router): add notFound fallback — renders DefaultNotFound on unmatched routes (#2120)#2146
divyanshim27 wants to merge 1 commit into
Karanjot786:mainfrom
divyanshim27:feat/router-not-found-fallback

Conversation

@divyanshim27

@divyanshim27 divyanshim27 commented Jul 7, 2026

Copy link
Copy Markdown

Summary

Adds notFound config option to <Router> — renders DefaultNotFound
(or a custom component) when navigation targets an unregistered path,
instead of rendering nothing and freezing the terminal app.

Problem

When navigate('/unregistered-path') was called, resolveRoute() returned
null and the Router rendered null. In a terminal application, a null render
produces a blank screen with no key inputs responding — the user has no way
to recover without killing the process.

Changes

packages/router/src/DefaultNotFound.tsx (new file)

Built-in 404 screen that:

  • Shows "Route Not Found" + the unmatched path
  • Binds Backspace/bnavigate(-1) to go back
  • Binds q/ctrl+cprocess.exit(0) to quit
  • Displays a help text line with both key options

packages/router/src/Router.tsx

  • Added notFound?: ComponentType<{ path: string }> to RouterConfig
  • Changed if (!resolved) return null to render config.notFound ?? DefaultNotFound

packages/router/src/index.ts

  • Exported DefaultNotFound and NotFoundProps for consumers who want to compose with the default

packages/router/src/__tests__/not-found.test.ts (new file)

5 tests: registered route renders correctly, unregistered renders DefaultNotFound,
custom notFound prop overrides default, navigate-to-unknown shows correct path,
DefaultNotFound renders key hint text.

docs/router.md

Documented notFound config option with default behavior screenshot-comment and custom usage example.

Tests

598 existing tests pass. 5 new tests added.

Closes #2120

Summary by CodeRabbit

  • New Features
    • Added customizable “not found” handling for unknown routes, with a built-in default screen showing the missing path.
    • Added new router hooks for accessing the current router, location, and query data.
  • Bug Fixes
    • Updated route fallback behavior so the displayed missing-path screen stays in sync after navigation.
  • Documentation
    • Documented how to use the new not-found customization option and default fallback behavior.

…tched routes (Karanjot786#2120)

Previously, navigating to an unregistered path returned null from the Router
component. In a terminal app, rendering null means a blank screen with no
key input responding — effectively a frozen application. There was no
documented mechanism to handle 404-equivalent navigation events.

Changes:
- RouterConfig: added optional notFound?: ComponentType<{ path: string }>
- Router.tsx: if resolveRoute() returns null, render config.notFound
  if provided, otherwise render DefaultNotFound
- DefaultNotFound.tsx: new built-in 404 screen with:
  - Displays the unmatched path
  - Backspace / b → navigate(-1) — go back to previous screen
  - q / ctrl+c → process.exit(0)
  - Box with round border, red title, yellow path, dim help text
- packages/router/src/index.ts: exports DefaultNotFound and NotFoundProps
- docs/router.md: documented notFound config option with examples
- 5 new tests: registered route, unregistered route, custom notFound,
  navigate-to-unknown, DefaultNotFound key hint text

Closes Karanjot786#2120
@divyanshim27 divyanshim27 requested a review from Karanjot786 as a code owner July 7, 2026 09:20
@github-actions github-actions Bot added type:feature +10 pts. New feature. type:docs +5 pts. Documentation. area:data @termuijs/data type:testing +10 pts. Tests. needs-star PR author has not starred the repo. labels Jul 7, 2026
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

Hi @divyanshim27 👋

Star this repo before your PR merges.

Why? GSSoC 2026 contributors who star get priority review and points credit. After you star, push any commit (or re-run this check). The needs-star label lifts automatically.

Thanks for your contribution to TermUI.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds a configurable notFound fallback for the router package. It introduces DefaultNotFound components, threads an optional customNotFound/notFound handler through router navigation, wrapScreen, and RouterView, expands hooks (useRouter, useQuery, useLocation) and exports, adds tests, a CI test script, and documentation.

Changes

Router 404 fallback feature

Layer / File(s) Summary
DefaultNotFound components
packages/data/src/DefaultNotFound.tsx, packages/router/src/DefaultNotFound.tsx
New DefaultNotFound components render a bordered "Route Not Found" UI showing the unmatched path, with back/quit key bindings via useKeymap.
Router navigation wiring
packages/router/src/router.ts
wrapScreen, _createNotFoundMatch, and _executeNavigation accept a customNotFound option, falling back through customNotFound ?? this._notFound ?? DefaultNotFound; JSDoc comments condensed around navigation methods.
RouterView integration
packages/router/src/RouterView.tsx
RouterViewProps gains an optional notFound prop passed into router.wrapScreen calls; effect dependencies updated to include notFound.
Hooks and exports
packages/router/src/hooks.ts, packages/router/src/index.ts
Adds useRouter, useQuery, useLocation hooks (useQueryParams aliases useQuery); reorganizes and expands index.ts exports for DefaultNotFound, RouterView/RouterViewProps, Router types, route utilities, and hooks.
Tests, CI script, and docs
packages/router/package.json, packages/router/src/__tests__/not-found.test.ts, docs/router.md
Adds a vitest run test script, a new not-found.test.ts suite covering default/custom NotFound rendering and navigation, and documents the notFound option.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant App
  participant RouterView
  participant Router
  participant DefaultNotFound

  App->>RouterView: render(notFound?)
  RouterView->>Router: wrapScreen(current, notFound)
  App->>Router: navigate(unregisteredPath)
  Router->>Router: _executeNavigation(options)
  Router->>Router: no route matched
  Router->>Router: _createNotFoundMatch(path, customNotFound)
  Router->>Router: wrapScreen(notFoundMatch, customNotFound)
  alt customNotFound provided
    Router->>App: render custom notFound(path)
  else no custom handler
    Router->>DefaultNotFound: render(path)
    DefaultNotFound-->>App: "Route Not Found" view
  end
Loading

Possibly related PRs

  • Karanjot786/TermUI#760: Both PRs center on the router's unmatched-route "notFound" fallback, with #760 adding tests for the fallback behavior this PR implements.
  • Karanjot786/TermUI#1461: Both PRs wire a notFound handler through navigation/rendering including synthetic "not found" matches and related tests.
  • Karanjot786/TermUI#1940: Both PRs modify wrapScreen in router.ts/RouterView.tsx, with this PR adding a customNotFound parameter building on prior wrapScreen exposure.

Suggested labels: quality:clean, level:intermediate, area:router

Suggested reviewers: Karanjot786

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The hooks.ts API expansion and some re-exports appear unrelated to the notFound fallback requirements. Split the unrelated hooks/export changes into a separate PR, or remove them if they are not needed for the fallback feature.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise, specific, and matches the main change: adding a notFound fallback for unmatched routes.
Description check ✅ Passed The description covers the feature, problem, changed files, tests, and linked issue, though several template sections are omitted.
Linked Issues check ✅ Passed The PR implements the requested notFound fallback, default component, documentation, and tests for unmatched routes and custom overrides.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.1)
packages/router/src/__tests__/not-found.test.ts

File contains syntax errors that prevent linting: Line 9: Expected a semicolon or an implicit semicolon after a statement, but found none; Line 9: unterminated regex literal; Line 10: Expected a semicolon or an implicit semicolon after a statement, but found none; Line 10: unterminated regex literal; Line 24: expected > but instead found config; Line 24: Invalid assignment to <Router config; Line 24: Expected a property, a shorthand property, a getter, a setter, or a method but instead found '{ ...baseConfig'.; Line 24: expected , but instead found }; Line 32: expected > but instead found config; Line 32: Invalid assignment to <Router config; Line 32: Expected a property, a shorthand property, a getter, a setter, or a method but instead found '{ ...baseConfig'.; Line 32: expected , but instead found }; Line 43: Expected a semicolon or an implicit semicolon after a statement, but found none; Line 43: Expected a semicolon or an implicit semicolon after a statement, bu

... [truncated 219 characters] ...

to <Router config; Line 47: Expected a property, a shorthand property, a getter, a setter, or a method but instead found '{'.; Line 48: expected , but instead found ...; Line 51: expected , but instead found }; Line 61: expected > but instead found config; Line 61: Invalid assignment to <Router config; Line 61: Expected a property, a shorthand property, a getter, a setter, or a method but instead found '{ ...baseConfig'.; Line 61: expected , but instead found }; Line 76: expected > but instead found config; Line 76: Invalid assignment to <Router config; Line 76: Expected a property, a shorthand property, a getter, a setter, or a method but instead found '{ ...baseConfig'.; Line 76: expected , but instead found }; Line 82: expected } but instead the file ends


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.

Warning

⚠️ This pull request shows signs of AI-generated slop (phantom_api, description_diff_mismatch). It has been flagged by CodeRabbit slop detection and should be reviewed carefully.

@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: 6

♻️ Duplicate comments (1)
docs/router.md (1)

31-36: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Custom fallback example uses ({ path }) => ... destructuring, consistent with the same prop-shape mismatch flagged above.

If the router actually calls the handler as notFound(path) (string), this example will break for anyone copying it verbatim.

🤖 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 `@docs/router.md` around lines 31 - 36, The fallback example in the router docs
uses object destructuring in the notFound handler, but if the router passes a
plain path string this sample is misleading and will break when copied. Update
the notFound example to match the actual handler signature used by the router
(the same prop-shape referenced by notFound) so it accepts the path value
directly rather than destructuring it from an object.
🧹 Nitpick comments (3)
packages/data/src/DefaultNotFound.tsx (1)

26-33: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Direct process.exit(0) inside a component's key handler complicates testing and can crash the host process.

Calling process.exit(0) synchronously on q/ctrl+c makes this component effectively untestable without mocking process.exit, and if this component is ever embedded in a larger process (not a standalone terminal app), it will kill the whole process rather than just the router's screen. Consider exposing an injectable onQuit callback (defaulting to process.exit(0)) instead.

🤖 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/data/src/DefaultNotFound.tsx` around lines 26 - 33, The key handler
in DefaultNotFound’s useKeymap is calling process.exit(0) directly for q and
ctrl+c, which makes the component hard to test and unsafe in embedded contexts.
Refactor DefaultNotFound to accept an injectable onQuit callback (defaulting to
quitting behavior), and have the useKeymap handlers invoke that callback instead
of process.exit; keep the backspace/b navigation unchanged.
packages/router/src/router.ts (1)

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

Passing options.customNotFound to wrapScreen here is pointless.

This is the matched-route path (a real route was found); wrapScreen never needs a not-found fallback here, and per the earlier finding, the parameter is unused inside wrapScreen regardless. Follows from the wrapScreen finding above.

🤖 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/router/src/router.ts` at line 307, The matched-route branch in
router.ts is passing a not-found fallback into wrapScreen unnecessarily, and
wrapScreen does not use that parameter. Update the call in the route-matching
flow to invoke wrapScreen(match) without options.customNotFound, and if needed
remove the now-unused parameter from wrapScreen’s signature and any related
callers so the API matches its actual behavior.
packages/router/src/RouterView.tsx (1)

96-96: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

notFound added to the effect's dependency array but never used inside it.

handleNavigate/onNav/onBack only reference e.screen; notFound isn't read anywhere in the effect body. If callers pass an inline arrow (as shown in the prop's own JSDoc example: notFound={(path) => <Text>...</Text>}), its identity changes every render, causing this effect to tear down and re-establish router.events listeners (and toggle router.autoUnmount) on every re-render for no functional benefit.

♻️ Suggested fix
-    }, [router, notFound]);
+    }, [router]);
🤖 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/router/src/RouterView.tsx` at line 96, The effect in RouterView is
re-running unnecessarily because notFound is listed in the dependency array even
though the effect body only uses router and the navigation handlers’ e.screen
values. Remove notFound from that useEffect dependency list so the router.events
listeners and router.autoUnmount setup in RouterView remain stable across
re-renders, especially when callers pass an inline notFound prop.
🤖 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 `@packages/data/src/DefaultNotFound.tsx`:
- Around line 1-11: Move the DefaultNotFound component out of packages/data and
into packages/router so its relative imports resolve correctly. Update the
component module to live alongside useRouter in packages/router/src, and make
sure DefaultNotFound imports useRouter from the router package’s hooks module
instead of a non-existent local ./useRouter path. Keep the existing
DefaultNotFoundProps/NotFoundProps and component behavior intact while replacing
the stale placeholder with the real router fallback.

In `@packages/router/src/__tests__/not-found.test.ts`:
- Line 1: The test file contains JSX, so it cannot be parsed as a .ts file;
rename the not-found test from .ts to .tsx so the JSX in the test body is valid.
Update the test artifact identified by not-found.test to use the .tsx extension
and keep the existing test content unchanged so the router test suite can
compile and run.
- Around line 3-6: The not-found test suite is using the testing harness from
`@termuijs/testing` instead of the required real Screen API. Update the Router
test setup to use Screen from `@termuijs/core`, and render widgets through
Screen.updateRect() and Screen.render() so the assertions exercise the real
terminal rendering flow. Replace any getByText/navigate/waitFor helpers with
Screen-based inspection in the Router and Text test cases.

In `@packages/router/src/DefaultNotFound.tsx`:
- Line 32: The hand-built VNode object in DefaultNotFound uses an unexplained
`as any` cast, which violates the typing guideline and hides a shape mismatch.
Update the component to construct the element using JSX like the sibling
implementation instead of a raw object literal, and remove the type assertion
entirely; if a cast is still unavoidable, add an inline comment explaining why
it is necessary.
- Line 19: The DefaultNotFound component is emitting a hardcoded en dash in its
404 text without an ASCII fallback. Update the string in DefaultNotFound to use
caps.unicode to choose between the non-ASCII version and an ASCII-safe fallback,
following the existing caps.unicode pattern used elsewhere in the codebase.
- Around line 4-33: Restore the interactive fallback in DefaultNotFound by
adding the Backspace/b and q/ctrl+c key handling back into DefaultNotFound or by
reusing the implementation from packages/data/src/DefaultNotFound.tsx. The
current DefaultNotFound(path) only renders static text, so update the VNode it
returns to include the key handlers that navigate back or quit, while keeping
the existing 404 UI and path display intact.

---

Duplicate comments:
In `@docs/router.md`:
- Around line 31-36: The fallback example in the router docs uses object
destructuring in the notFound handler, but if the router passes a plain path
string this sample is misleading and will break when copied. Update the notFound
example to match the actual handler signature used by the router (the same
prop-shape referenced by notFound) so it accepts the path value directly rather
than destructuring it from an object.

---

Nitpick comments:
In `@packages/data/src/DefaultNotFound.tsx`:
- Around line 26-33: The key handler in DefaultNotFound’s useKeymap is calling
process.exit(0) directly for q and ctrl+c, which makes the component hard to
test and unsafe in embedded contexts. Refactor DefaultNotFound to accept an
injectable onQuit callback (defaulting to quitting behavior), and have the
useKeymap handlers invoke that callback instead of process.exit; keep the
backspace/b navigation unchanged.

In `@packages/router/src/router.ts`:
- Line 307: The matched-route branch in router.ts is passing a not-found
fallback into wrapScreen unnecessarily, and wrapScreen does not use that
parameter. Update the call in the route-matching flow to invoke
wrapScreen(match) without options.customNotFound, and if needed remove the
now-unused parameter from wrapScreen’s signature and any related callers so the
API matches its actual behavior.

In `@packages/router/src/RouterView.tsx`:
- Line 96: The effect in RouterView is re-running unnecessarily because notFound
is listed in the dependency array even though the effect body only uses router
and the navigation handlers’ e.screen values. Remove notFound from that
useEffect dependency list so the router.events listeners and router.autoUnmount
setup in RouterView remain stable across re-renders, especially when callers
pass an inline notFound prop.
🪄 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: 9ddd79d6-0f3c-4d1a-b08c-723925714664

📥 Commits

Reviewing files that changed from the base of the PR and between 65d1f7e and 1e408be.

📒 Files selected for processing (9)
  • docs/router.md
  • packages/data/src/DefaultNotFound.tsx
  • packages/router/package.json
  • packages/router/src/DefaultNotFound.tsx
  • packages/router/src/RouterView.tsx
  • packages/router/src/__tests__/not-found.test.ts
  • packages/router/src/hooks.ts
  • packages/router/src/index.ts
  • packages/router/src/router.ts

Comment on lines +1 to +11
// packages/router/src/DefaultNotFound.tsx
// New file for Issue #2120 — default fallback rendered when no route matches

import { Box, Text } from '@termuijs/widgets';
import { useKeymap } from '@termuijs/jsx';
import { useRouter } from './useRouter';

export interface NotFoundProps {
/** The path that was requested but had no registered route */
path: string;
}

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
# Verify existence and export location of useRouter, and confirm which DefaultNotFound router.ts imports
fd -a 'useRouter.ts' packages
rg -n 'export function useRouter|export const useRouter' packages/router/src
rg -n "import.*DefaultNotFound" packages/router/src/router.ts

Repository: Karanjot786/TermUI

Length of output: 291


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== files ==\n'
git ls-files 'packages/**/DefaultNotFound.tsx' 'packages/router/src/hooks.ts' 'packages/router/src/router.ts' 'packages/router/src/DefaultNotFound.tsx' 'packages/data/src/DefaultNotFound.tsx'

printf '\n== line counts ==\n'
wc -l packages/data/src/DefaultNotFound.tsx packages/router/src/DefaultNotFound.tsx packages/router/src/hooks.ts packages/router/src/router.ts

printf '\n== packages/data/src/DefaultNotFound.tsx ==\n'
cat -n packages/data/src/DefaultNotFound.tsx

printf '\n== packages/router/src/DefaultNotFound.tsx ==\n'
cat -n packages/router/src/DefaultNotFound.tsx

printf '\n== packages/router/src/hooks.ts ==\n'
cat -n packages/router/src/hooks.ts

printf '\n== packages/router/src/router.ts excerpt ==\n'
sed -n '1,80p' packages/router/src/router.ts

Repository: Karanjot786/TermUI

Length of output: 9068


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== references to DefaultNotFound ==\n'
rg -n "DefaultNotFound" packages -g '!**/dist/**'

printf '\n== useRouter refs in packages/data ==\n'
rg -n "useRouter" packages/data packages/router/src -g '!**/dist/**'

printf '\n== package entrypoints for data and router ==\n'
git ls-files 'packages/data/src/*' 'packages/router/src/*' | sort

Repository: Karanjot786/TermUI

Length of output: 4401


Move DefaultNotFound into packages/router
packages/data/src/DefaultNotFound.tsx imports ./useRouter, but that module doesn’t exist in packages/data; useRouter lives in packages/router/src/hooks.ts. This component belongs in packages/router/src/DefaultNotFound.tsx, replacing the stale placeholder there.

🤖 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/data/src/DefaultNotFound.tsx` around lines 1 - 11, Move the
DefaultNotFound component out of packages/data and into packages/router so its
relative imports resolve correctly. Update the component module to live
alongside useRouter in packages/router/src, and make sure DefaultNotFound
imports useRouter from the router package’s hooks module instead of a
non-existent local ./useRouter path. Keep the existing
DefaultNotFoundProps/NotFoundProps and component behavior intact while replacing
the stale placeholder with the real router fallback.

@@ -0,0 +1,82 @@
// packages/router/src/__tests__/not-found.test.ts

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

File must be .tsx, not .ts — JSX is unparseable in a .ts file.

Static analysis confirms this: parse errors on every JSX-containing line (9, 10, 24, 32, 43, 47, 61, 76). This file will fail to compile/run as-is.

🐛 Proposed fix

Rename not-found.test.tsnot-found.test.tsx.

Also applies to: 9-10, 22-81

🤖 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/router/src/__tests__/not-found.test.ts` at line 1, The test file
contains JSX, so it cannot be parsed as a .ts file; rename the not-found test
from .ts to .tsx so the JSX in the test body is valid. Update the test artifact
identified by not-found.test to use the .tsx extension and keep the existing
test content unchanged so the router test suite can compile and run.

Source: Linters/SAST tools

Comment on lines +3 to +6
import { describe, it, expect } from 'vitest';
import { render } from '@termuijs/testing';
import { Router } from '../Router';
import { Text } from '@termuijs/widgets';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Tests should use the real Screen API per repo guidelines, not @termuijs/testing's render/getByText harness.

As per coding guidelines, packages/**/*.test.{ts,tsx} tests "must use the real Screen from @termuijs/core" and "Render widgets in tests using Screen, updateRect(), and render() methods." This suite instead uses a different render from @termuijs/testing with getByText/navigate/waitFor helpers, which doesn't follow the prescribed testing pattern.

Also applies to: 23-81

🤖 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/router/src/__tests__/not-found.test.ts` around lines 3 - 6, The
not-found test suite is using the testing harness from `@termuijs/testing` instead
of the required real Screen API. Update the Router test setup to use Screen from
`@termuijs/core`, and render widgets through Screen.updateRect() and
Screen.render() so the assertions exercise the real terminal rendering flow.
Replace any getByText/navigate/waitFor helpers with Screen-based inspection in
the Router and Text test cases.

Source: Coding guidelines

Comment on lines +4 to +33
export function DefaultNotFound({ path }: { path: string }): VNode {
return {
type: 'box',
props: {
border: 'single',
borderColor: 'yellow',
padding: 2,
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
},
children: [
{
type: 'text',
props: { color: 'yellow', bold: true, size: 2 },
children: ['404 – Page Not Found']
},
{
type: 'text',
props: { color: 'gray', dim: true },
children: [`Path: ${path}`]
},
{
type: 'text',
props: { color: 'gray' },
children: ['The route you are looking for does not exist.']
}
]
} as any;
} No newline at end of file

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

printf '\n== files ==\n'
git ls-files 'packages/router/src/*NotFound*' 'packages/data/src/*NotFound*' 'packages/router/src/router.ts'

printf '\n== outline: router DefaultNotFound ==\n'
ast-grep outline packages/router/src/DefaultNotFound.tsx --view expanded || true

printf '\n== outline: data DefaultNotFound ==\n'
ast-grep outline packages/data/src/DefaultNotFound.tsx --view expanded || true

printf '\n== outline: router.ts ==\n'
ast-grep outline packages/router/src/router.ts --view expanded || true

printf '\n== router DefaultNotFound snippet ==\n'
sed -n '1,220p' packages/router/src/DefaultNotFound.tsx

printf '\n== data DefaultNotFound snippet ==\n'
sed -n '1,260p' packages/data/src/DefaultNotFound.tsx

printf '\n== router.ts relevant snippet ==\n'
sed -n '1,260p' packages/router/src/router.ts

printf '\n== keymap / notfound references ==\n'
rg -n "useKeymap|Backspace|ctrl\\+c|\\bq\\b|DefaultNotFound|NotFound" packages/router/src packages/data/src

Repository: Karanjot786/TermUI

Length of output: 19757


Restore the interactive not-found fallback in packages/router/src/DefaultNotFound.tsx. This is the default screen for unmatched routes, but it only renders static text. Move the Backspace/b and q/ctrl+c key handling here (or share the implementation from packages/data/src/DefaultNotFound.tsx) so users can navigate back or quit from the fallback screen.

🤖 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/router/src/DefaultNotFound.tsx` around lines 4 - 33, Restore the
interactive fallback in DefaultNotFound by adding the Backspace/b and q/ctrl+c
key handling back into DefaultNotFound or by reusing the implementation from
packages/data/src/DefaultNotFound.tsx. The current DefaultNotFound(path) only
renders static text, so update the VNode it returns to include the key handlers
that navigate back or quit, while keeping the existing 404 UI and path display
intact.

{
type: 'text',
props: { color: 'yellow', bold: true, size: 2 },
children: ['404 – Page Not Found']

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Hardcoded non-ASCII character without caps.unicode fallback.

The en dash in '404 – Page Not Found' is emitted unconditionally. As per coding guidelines, packages/**/*.{ts,tsx}: "Use caps.unicode for non-ASCII characters with an ASCII fallback. Example: caps.unicode ? '█' : '#'."

🌐 Suggested fix
-                children: ['404 – Page Not Found']
+                children: [caps.unicode ? '404 – Page Not Found' : '404 - Page Not Found']
📝 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
children: ['404 – Page Not Found']
children: [caps.unicode ? '404 – Page Not Found' : '404 - Page Not Found']
🤖 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/router/src/DefaultNotFound.tsx` at line 19, The DefaultNotFound
component is emitting a hardcoded en dash in its 404 text without an ASCII
fallback. Update the string in DefaultNotFound to use caps.unicode to choose
between the non-ASCII version and an ASCII-safe fallback, following the existing
caps.unicode pattern used elsewhere in the codebase.

Source: Coding guidelines

children: ['The route you are looking for does not exist.']
}
]
} as any;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

as any cast without justification.

As per coding guidelines, **/*.{ts,tsx}: "No any without an inline comment explaining why... No type assertions without an inline comment explaining why." The cast on Line 32 has no explanation, and its need signals the hand-built VNode object doesn't actually satisfy the VNode/VElement shape — worth constructing this via JSX (as the sibling implementation does) instead of raw object literals plus a silent cast.

🤖 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/router/src/DefaultNotFound.tsx` at line 32, The hand-built VNode
object in DefaultNotFound uses an unexplained `as any` cast, which violates the
typing guideline and hides a shape mismatch. Update the component to construct
the element using JSX like the sibling implementation instead of a raw object
literal, and remove the type assertion entirely; if a cast is still unavoidable,
add an inline comment explaining why it is necessary.

Source: Coding guidelines

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

Labels

area:data @termuijs/data needs-star PR author has not starred the repo. type:docs +5 pts. Documentation. type:feature +10 pts. New feature. type:testing +10 pts. Tests.

Projects

None yet

1 participant