Repository navigation
Merge claude/vibrant-meitner-84kb0a into main - #2
Closed
ryangarber wants to merge 5 commits into
Closed
ryangarber wants to merge 5 commits into
ryangarber wants to merge 5 commits into
Conversation
Every Box handed its inherited background to Ink, so every box in the app painted its whole area on every frame, nested boxes repainting the same cells once per level, and the transcript's scrolled-away history costing as much as what is on screen. Profiled over a three-turn session this was 86% of all CPU, growing with every turn. The box a color comes from has already painted it; overlays still paint it over what they cover, and text still inherits it through Ink's context. It also stops content wider than its block from painting over that block's border. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018mPBzWaJnr8VavGtGDMxvQ
- ink: drop writes that fall outside the output or the clip before tokenizing them, and keep the tokenizing caches across frames (bounded) rather than building new ones for every frame. - @alcalzone/ansi-tokenize: skip diffing the styles of neighboring cells that are styled the same (their diff is empty), and share one styles array per run of characters instead of copying it per character. Both produce byte-identical output: checked against the originals over thousands of randomized frames and styled strings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018mPBzWaJnr8VavGtGDMxvQ
- Task.Status only animates while pending. Every settled tool call and thought ticked at 5 Hz, committing ~19 frames a second while idle. - MarkdownBlock is memoized explicitly: the compiler did not take it for a component, so every block of a message was parsed again on every render, each token while streaming. - useMarkdown keeps its parts while they are the same parts, so the blocks' props hold still between renders of the same source. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018mPBzWaJnr8VavGtGDMxvQ
Bun.build leaves NODE_ENV to the environment, so release builds shipped React's development builds, which time and record every component render. Dev builds keep the development builds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018mPBzWaJnr8VavGtGDMxvQ
There was a problem hiding this comment.
🟡 Changes recommended
Persistent rendering caches can retain excessive character data from successive streaming prefixes.
1 open issue
1 file not reviewed
pnpm-lock.yaml: Generated file
What changed in this PR
Improves Tiny Chat’s CLI rendering performance, especially during streaming responses.
Changes:
- Patches Ink and ANSI tokenization to reuse rendering work.
- Reduces redundant Markdown parsing, background painting, and animation updates.
- Selects production React builds for CLI releases.
| File | Description |
|---|---|
| pnpm-workspace.yaml | Registers rendering dependency patches. |
| pnpm-lock.yaml | Records patched dependency resolutions. |
| patches/ink@8.0.0.patch | Shares rendering caches and skips offscreen text processing. |
| patches/@alcalzone__ansi-tokenize@0.3.1.patch | Reuses styles and skips redundant style comparisons. |
| packages/tui/src/features/part/components/Task.tsx | Limits shimmer animation to pending tasks. |
| packages/tui/src/features/message/components/Markdown.tsx | Explicitly memoizes Markdown blocks. |
| packages/tui/src/core/components/Box.tsx | Avoids redundant inherited background painting. |
| packages/client/src/features/message/hooks/useMarkdown.ts | Stabilizes unchanged parts arrays. |
| apps/cli/scripts/compile.ts | Defines the React build environment explicitly. |
| AGENTS.md | Updates documentation about patched dependencies. |
Review effort: Balanced
The caches the Ink patch keeps across frames were bounded by entry count, not by what the entries hold, and three things let them grow without bound: - Parsed characters were kept for whole multiline blocks, so each change to one row of a block left a parsed copy of all of it behind. - Thousands of long entries (each prefix of a streaming paragraph) fit under a count of a few thousand. - Widths of lines split from a block were cached by those lines, which are slices that keep the whole block alive. Parsed characters are now kept only for single lines (a block is still tokenized whole on a miss, so styles spanning lines are reopened), every cache is bounded by the size of its text in two generations so a hit stays one lookup, and split lines are measured without being cached. Retained memory after 3,000 streaming updates: 3.7 GiB -> 4.9 MiB, with the same per-frame time; output unchanged against stock Ink in a differential fuzz. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018mPBzWaJnr8VavGtGDMxvQ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

No description provided.