Skip to content

fix: prevent TUI crashes when terminal is resized to narrow widths#791

Open
liruifengv wants to merge 3 commits into
mainfrom
fix/tui-narrow-width-crash
Open

fix: prevent TUI crashes when terminal is resized to narrow widths#791
liruifengv wants to merge 3 commits into
mainfrom
fix/tui-narrow-width-crash

Conversation

@liruifengv

@liruifengv liruifengv commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

Related Issue

No related issue. This PR fixes crashes reported via ~/.pi/agent/pi-crash.log when the terminal is resized to a very narrow width.

Problem

pi-tui enforces that every line returned by Component.render(width) must fit within width; otherwise it throws a hard crash. Several TUI components built lines manually without a final truncation guard, so resizing the terminal below the component's fixed minimum width (e.g. the live ⠋ thinking... spinner at 13 columns) caused the application to crash and write a debug log to ~/.pi/agent/pi-crash.log.

Separately, pi-tui's Editor.wordWrapLine recurses infinitely when the layout width becomes 1 and the editor text contains a wide grapheme (e.g. CJK characters), producing a RangeError: Maximum call stack size exceeded.

What changed

  • Added a final truncateToWidth(line, width, '…') guard to ThinkingComponent so the live spinner and all thinking lines fit the available width.
  • Added the same final guard to GoalCompletionMessageComponent and CronMessageComponent to handle bullet/indent + content overflow at extreme narrow widths.
  • Changed GutterContainer to return a placeholder when the requested width is smaller than the gutters themselves, instead of clamping the inner width to 1 and producing an over-wide line.
  • Guarded CustomEditor.render to skip rendering when the terminal is narrower than the editor's padding; this avoids the upstream pi-tui wordWrapLine infinite recursion on wide graphemes at a 1-column layout width.
  • Patched @earendil-works/pi-tui@0.74.0 via pnpm patch to break the wordWrapLine recursion when maxWidth <= 1 and a wide grapheme cannot be split further, emitting the grapheme as its own overflow chunk instead.
  • Added narrow-width unit tests for all affected components (including history recall with CJK text) and documented the width-safety rule in apps/kimi-code/AGENTS.md and the write-tui skill so future components follow the same pattern.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

- Truncate ThinkingComponent live spinner and all rendered lines to width

- Truncate GoalCompletionMessageComponent output to width

- Truncate CronMessageComponent output to width

- Return placeholder in GutterContainer when gutters exceed width

- Add narrow-width tests and update width-safety rules in AGENTS.md and write-tui skill
@changeset-bot

changeset-bot Bot commented Jun 15, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a418845

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@moonshot-ai/kimi-code Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Jun 15, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@moonshot-ai/kimi-code@a418845
npx https://pkg.pr.new/@moonshot-ai/kimi-code@a418845

commit: a418845

… narrow widths

pi-tui's editor wordWrapLine recurses infinitely when layoutWidth is 1 and

the text contains wide graphemes (e.g. CJK). Skip rendering CustomEditor

when the terminal is narrower than paddingX to avoid the crash.
@github-actions

Copy link
Copy Markdown
Contributor

❌ Nix build failed

    copying path '/nix/store/cw3a3yz8vrfsz53ypjw8hspy5hzzx2py-diffutils-3.12' from 'https://cache.nixos.org'...
    copying path '/nix/store/55c9z5n6flgs7mcsw9rsy0ldnxkbxwnn-perl-5.40.0' from 'https://cache.nixos.org'...
    copying path '/nix/store/8xhaz2ysixijy8sgzmwmhlialqqind1p-findutils-4.10.0' from 'https://cache.nixos.org'...
    copying path '/nix/store/lfbzxs5wyqd2122mpbj5azkxhxspw9cd-bash-interactive-5.3p3' from 'https://cache.nixos.org'...
    copying path '/nix/store/sdfysgb89zdysrknjavcr0crs4qxpk8r-python3-3.13.12' from 'https://cache.nixos.org'...
    copying path '/nix/store/7wlxfjp63zs4wipznx3pb11n9jiplqw6-pnpm-config-hook' from 'https://cache.nixos.org'...
    copying path '/nix/store/pvir9l70j66xb9prq9cvq313r2n2na58-stdenv-linux' from 'https://cache.nixos.org'...
    copying path '/nix/store/iz5lckcsg66r223si2gck7csk2hihj0m-binutils-wrapper-2.44' from 'https://cache.nixos.org'...
    copying path '/nix/store/8pr908sqkiw8ljbvy97w3dkzyc6gp33c-icu4c-76.1-dev' from 'https://cache.nixos.org'...
    copying path '/nix/store/chsxa1iw7s3m51v4kj43yd1nw26xvi21-nodejs-24.15.0' from 'https://cache.nixos.org'...
    copying path '/nix/store/329mivmib3802jlxh28x068s5w12l814-perl-5.40.0-env' from 'https://cache.nixos.org'...
    copying path '/nix/store/l0qhyid5kagg1b8zss8im3brp6xmbj8s-moreutils-0.70' from 'https://cache.nixos.org'...
    copying path '/nix/store/1a4jkdafcas9xw363p17fjcgd0fb205m-python3.13-argcomplete-3.6.2' from 'https://cache.nixos.org'...
    copying path '/nix/store/5zqzyagaa1ymr62g083vg2nnr6b2qqh3-python3.13-pyyaml-6.0.3' from 'https://cache.nixos.org'...
    copying path '/nix/store/blffcdwvc826412388r2ap32bknwvdwl-python3.13-tomlkit-0.13.3' from 'https://cache.nixos.org'...
    copying path '/nix/store/pg34f6cwza35vjr9dqhvrw23x89jy3a0-python3.13-xmltodict-1.0.2' from 'https://cache.nixos.org'...
    copying path '/nix/store/dhvczfimxz72bjhap1qkh7c5x9jd1j7w-python3.13-yq-3.4.3' from 'https://cache.nixos.org'...
    copying path '/nix/store/lxlqvhwxfw9kzkjl7cwxkls9khgsmc3x-nodejs-24.15.0-dev' from 'https://cache.nixos.org'...
    copying path '/nix/store/92l9h2h7gbynan4fk6rh8jz7dv9iw23s-pnpm-10.28.0' from 'https://cache.nixos.org'...
    building '/nix/store/v4m9za5wfi753qswxbnlqqlzqxxm3p25-kimi-code-pnpm-deps.drv'...
    kimi-code-pnpm-deps> Running phase: unpackPhase
    kimi-code-pnpm-deps> unpacking source archive /nix/store/4bp4k8y7zvzvsg6pygkzxbvfyfsjxvwd-source
    kimi-code-pnpm-deps> source root is source
    kimi-code-pnpm-deps> Running phase: patchPhase
    kimi-code-pnpm-deps> Running phase: updateAutotoolsGnuConfigScriptsPhase
    kimi-code-pnpm-deps> Running phase: installPhase
    kimi-code-pnpm-deps> /build /build/source
    copying path '/nix/store/8v97ngkcpfzgghwnnr7fsz33p2x22gy9-gcc-wrapper-14.3.0' from 'https://cache.nixos.org'...
    kimi-code-pnpm-deps> /build/source
    copying path '/nix/store/0i25iw4373868kjqhafvmd6pn1wwzkh3-stdenv-linux' from 'https://cache.nixos.org'...
    kimi-code-pnpm-deps> .                                        |  WARN  using --force I sure hope you know what you are doing
    kimi-code-pnpm-deps> Scope: all 15 workspace projects
    kimi-code-pnpm-deps>  ENOENT  ENOENT: no such file or directory, open '/build/source/patches/@earendil-works__pi-tui@0.74.0.patch'
    kimi-code-pnpm-deps> 
    kimi-code-pnpm-deps> pnpm: ENOENT: no such file or directory, open '/build/source/patches/@earendil-works__pi-tui@0.74.0.patch'
    kimi-code-pnpm-deps>     at async open (node:internal/fs/promises:640:25)
    kimi-code-pnpm-deps>     at async Object.readFile (node:internal/fs/promises:1287:14)
    kimi-code-pnpm-deps>     at async readNormalizedFile (/nix/store/92l9h2h7gbynan4fk6rh8jz7dv9iw23s-pnpm-10.28.0/libexec/pnpm/dist/pnpm.cjs:55390:23)
    kimi-code-pnpm-deps>     at async createHexHashFromFile (/nix/store/92l9h2h7gbynan4fk6rh8jz7dv9iw23s-pnpm-10.28.0/libexec/pnpm/dist/pnpm.cjs:55387:28)
    kimi-code-pnpm-deps>     at async /nix/store/92l9h2h7gbynan4fk6rh8jz7dv9iw23s-pnpm-10.28.0/libexec/pnpm/dist/pnpm.cjs:136623:17
    kimi-code-pnpm-deps>     at async /nix/store/92l9h2h7gbynan4fk6rh8jz7dv9iw23s-pnpm-10.28.0/libexec/pnpm/dist/pnpm.cjs:136600:24
    kimi-code-pnpm-deps>     at async Promise.all (index 0)
    kimi-code-pnpm-deps>     at async pMapValue (/nix/store/92l9h2h7gbynan4fk6rh8jz7dv9iw23s-pnpm-10.28.0/libexec/pnpm/dist/pnpm.cjs:136599:7)
    kimi-code-pnpm-deps>     at async _install (/nix/store/92l9h2h7gbynan4fk6rh8jz7dv9iw23s-pnpm-10.28.0/libexec/pnpm/dist/pnpm.cjs:159124:134)
    kimi-code-pnpm-deps>     at async mutateModules (/nix/store/92l9h2h7gbynan4fk6rh8jz7dv9iw23s-pnpm-10.28.0/libexec/pnpm/dist/pnpm.cjs:159072:23)
    error: Cannot build '/nix/store/v4m9za5wfi753qswxbnlqqlzqxxm3p25-kimi-code-pnpm-deps.drv'.
           Reason: builder failed with exit code 254.
           Output paths:
             /nix/store/z61pggqxh2ax4sllrmz0ngznmvfxkvpd-kimi-code-pnpm-deps
           Last 23 log lines:
           > Running phase: unpackPhase
           > unpacking source archive /nix/store/4bp4k8y7zvzvsg6pygkzxbvfyfsjxvwd-source
           > source root is source
           > Running phase: patchPhase
           > Running phase: updateAutotoolsGnuConfigScriptsPhase
           > Running phase: installPhase
           > /build /build/source
           > /build/source
           > .                                        |  WARN  using --force I sure hope you know what you are doing
           > Scope: all 15 workspace projects
           >  ENOENT  ENOENT: no such file or directory, open '/build/source/patches/@earendil-works__pi-tui@0.74.0.patch'
           >
           > pnpm: ENOENT: no such file or directory, open '/build/source/patches/@earendil-works__pi-tui@0.74.0.patch'
           >     at async open (node:internal/fs/promises:640:25)
           >     at async Object.readFile (node:internal/fs/promises:1287:14)
           >     at async readNormalizedFile (/nix/store/92l9h2h7gbynan4fk6rh8jz7dv9iw23s-pnpm-10.28.0/libexec/pnpm/dist/pnpm.cjs:55390:23)
           >     at async createHexHashFromFile (/nix/store/92l9h2h7gbynan4fk6rh8jz7dv9iw23s-pnpm-10.28.0/libexec/pnpm/dist/pnpm.cjs:55387:28)
           >     at async /nix/store/92l9h2h7gbynan4fk6rh8jz7dv9iw23s-pnpm-10.28.0/libexec/pnpm/dist/pnpm.cjs:136623:17
           >     at async /nix/store/92l9h2h7gbynan4fk6rh8jz7dv9iw23s-pnpm-10.28.0/libexec/pnpm/dist/pnpm.cjs:136600:24
           >     at async Promise.all (index 0)
           >     at async pMapValue (/nix/store/92l9h2h7gbynan4fk6rh8jz7dv9iw23s-pnpm-10.28.0/libexec/pnpm/dist/pnpm.cjs:136599:7)
           >     at async _install (/nix/store/92l9h2h7gbynan4fk6rh8jz7dv9iw23s-pnpm-10.28.0/libexec/pnpm/dist/pnpm.cjs:159124:134)
           >     at async mutateModules (/nix/store/92l9h2h7gbynan4fk6rh8jz7dv9iw23s-pnpm-10.28.0/libexec/pnpm/dist/pnpm.cjs:159072:23)
           For full logs, run:
             nix log /nix/store/v4m9za5wfi753qswxbnlqqlzqxxm3p25-kimi-code-pnpm-deps.drv
    error: Cannot build '/nix/store/0qk63l8wi5l0xq7v6q6j4yvfb7vz3vs9-kimi-code-0.15.0.drv'.
           Reason: 1 dependency failed.
           Output paths:
             /nix/store/gg14i083hm1yzlj0n3dk3nc24qkzpxl9-kimi-code-0.15.0
    error: Build failed due to failed dependency

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant