Skip to content

Merge claude/vibrant-meitner-84kb0a into main - #2

Closed
ryangarber wants to merge 5 commits into
mainfrom
claude/vibrant-meitner-84kb0a
Closed

ryangarber wants to merge 5 commits into
mainfrom
claude/vibrant-meitner-84kb0a

Conversation

@ryangarber

Copy link
Copy Markdown
Owner

No description provided.

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
@ryangarber ryangarber self-assigned this Oct 21, 2026
Copilot AI balanced review requested due to automatic review settings October 21, 2026 00:42

Copilot AI 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.

🟡 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

Comment thread patches/ink@8.0.0.patch Outdated
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
@ryangarber ryangarber closed this Oct 21, 2026
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.

3 participants