Skip to content

Placeholder tiles are content: printing over the cell removes the tile - #156

Open
tomlm wants to merge 1 commit into
mainfrom
tomlm/placeholder-tiles-are-content
Open

Placeholder tiles are content: printing over the cell removes the tile#156
tomlm wants to merge 1 commit into
mainfrom
tomlm/placeholder-tiles-are-content

Conversation

@tomlm

@tomlm tomlm commented Sep 5, 2026

Copy link
Copy Markdown
Owner

A Unicode-placeholder tile exists because its cell holds the placeholder character; the protocol has no command to remove one tile, because overwriting the cell IS the removal. Tiles were stored as classic Kitty overlay runs, which text never modifies, so anything drawn across a placeholder picture (a dialog, a new screen) kept the picture on top of it permanently.

  • New PlacementKind.Placeholder with content semantics: the split that printing performs on Sixel runs now takes placeholder runs too (a one-column run simply goes). Classic placements keep overlay behaviour and z-ordering untouched, pinned by a new test.
  • Writing a placeholder cell also clears any stale tile another picture left at that cell, since the cell write itself now splits.
  • The test asserting overlay behaviour for placeholders encoded the implementation rather than the protocol (kitty itself removes the tile; image.nvim depends on it) and now asserts the removal, with the reasoning in its remarks.

Perf: the only hot-path-adjacent change is the kind test inside SplitPlacementsAt, which flipped from != Sixel to == Kitty — same single compare, and the method only runs on lines that hold placements.

Found from the consumer side: Consolonia's kitty renderer overwrites occluded placeholder cells exactly as the spec prescribes, and the pictures stayed on top of its dialogs.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CxS1BnzZ6BhuGovMLNtRQH

A Unicode-placeholder tile exists because its cell holds the placeholder
character; the protocol offers no command to remove one tile, because
overwriting the cell IS the removal. The tiles were stored as classic
Kitty overlay runs, which text never modifies, so an application that
drew a dialog across a picture kept the picture on top of it permanently
and navigating away could not erase it.

A third PlacementKind, Placeholder, carries the content semantics: the
split that printing performs on Sixel runs now takes placeholder runs
too (a one-column run simply goes), while classic Kitty placements keep
their overlay behaviour and z-ordering untouched. Writing a placeholder
cell also clears any stale tile another picture left there, since the
cell write itself now splits.

The test asserting the old behaviour encoded the bug rather than the
protocol and now asserts the removal; classic-overlay survival under
text is pinned by its own new test so the two semantics cannot be
conflated again.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CxS1BnzZ6BhuGovMLNtRQH
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Perf comparison — this change, against its base

3 run(s) of each side, alternating on one machine. Allocation is a count and is gated exactly. Time is a measurement, so its gate is derived from the spread this job just observed in itself rather than fixed in advance.

corpus bytes/char gen0/Mchar ns/char Δ time noise gate
scroll-ascii 0.00 → 0.00 0.00 → 0.00 3.50 → 3.49 -0.5% ±1% 4%
sgr-churn 0.00 → 0.00 0.00 → 0.00 9.35 → 9.29 -0.6% ±1% 4%
truecolor 0.00 → 0.00 0.00 → 0.00 9.87 → 9.99 +1.2% ±2% 5%
alt-redraw 0.00 → 0.00 0.00 → 0.00 14.75 → 14.72 -0.2% ±5% 15%
unicode 7.66 → 7.66 0.45 → 0.45 35.80 → 35.49 -0.9% ±4% 11%
flood 0.00 → 0.00 0.00 → 0.00 105.14 → 105.01 -0.1% ±0% 4%

Each corpus is gated at max(4%, 3 × its own noise). A wide noise column means this runner was busy and the timing half of the table should be read as advisory; the allocation half is exact either way.

assemblies measured
  • base: XTerm.NET 2.0.1.0 mvid:66758d33-46a9-4d79-a288-5482acbfe77f
  • head: XTerm.NET 2.0.1.0 mvid:dc29f018-2613-4cb5-9e00-4a1b11a3e67e

Perf comparison — cumulative, everything since 2.0.0

3 run(s) of each side, alternating on one machine. Allocation is a count and is gated exactly. Time is a measurement, so its gate is derived from the spread this job just observed in itself rather than fixed in advance.

corpus bytes/char gen0/Mchar ns/char Δ time noise gate
scroll-ascii 0.00 → 0.00 0.00 → 0.00 3.49 → 3.49 -0.0% ±1% 5%
sgr-churn 0.00 → 0.00 0.00 → 0.00 9.28 → 9.29 +0.1% ±1% 5%
truecolor 0.00 → 0.00 0.00 → 0.00 9.89 → 9.99 +1.1% ±2% 5%
alt-redraw 0.00 → 0.00 0.00 → 0.00 14.73 → 14.72 -0.1% ±1% 5%
unicode 7.66 → 7.66 0.45 → 0.45 35.60 → 35.49 -0.3% ±4% 11%
flood 0.00 → 0.00 0.00 → 0.00 105.11 → 105.01 -0.1% ±0% 5%

Each corpus is gated at max(5%, 3 × its own noise). A wide noise column means this runner was busy and the timing half of the table should be read as advisory; the allocation half is exact either way.

assemblies measured
  • base: XTerm.NET 2.0.0.0 mvid:a26a50e5-3343-4b31-bada-ce2a03923fe4
  • head: XTerm.NET 2.0.1.0 mvid:dc29f018-2613-4cb5-9e00-4a1b11a3e67e

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