I forked Zellij so my terminal could show images again
I live in Zellij under Ghostty. Yazi, Codex, Grok... half the tools I use every day want to put an image in the pane. On a normal terminal that works. Inside Zellij it was just empty space.
Outside the multiplexer those apps draw fine. Inside it they speak the Kitty Graphics Protocol over APC, and the vte path Zellij uses throws APC away before the grid ever sees it. Zellij already had Sixel. It did not have this path. Agents sit in my panes for hours. I was done opening a raw window every time something needed a picture.
So I forked it.
I could have pasted a thin passthrough and hoped. I have paid for that class of fix before. This time I wanted something that still worked after resize, multi-client attach, and the dumb edge cases those tools actually hit. That meant treating it like real Rust work, and running the agents under a loop I could trust.
How I ran the agents
Ultracode on. Superpowers brainstorm with a long, boring brief: what KGP does on the wire, how Sixel already works in this codebase, Ghostty as the host, and the acceptance desk (Yazi previews, Codex pets, Grok images). Then Fable-5 at max effort on the design. If the review found holes, back into brainstorming. When the design held, write-plan. Same Fable gate on the plan. Issues reopen the plan. Only after that did subagent-driven development start, and every milestone got the same review until it passed.
While that loop ran I kept the same rules in the brief every time: no errors, no warnings, no red tests. No warning suppressions in production code (test-only allow with a reason is fine). No stubs, no TODO left as the real work. Use the skills that match the job. Latest deps when I touch crates and workflows. Do not trust a stale local tool-version cache.
That is the agent story. It only mattered because the desk needed a real multiplexer change, not a demo.
What had to be true in the code
Ghostty still draws the pixels. Zellij is a bad place to become a graphics engine. The job is capture, bookkeeping, and re-emit.
Catch ESC _ G … ST in front of vte so the stream is not dropped. Store image bytes and placement metadata under ids Zellij owns (apps use their own ids; rewrite once on the way in). Put Unicode placeholder cells in the grid (U+10EEEE plus the diacritics the protocol uses) so scroll, resize, and occlusion stay honest. Re-emit the right sequences per attached client. If a client cannot do placeholders, stay dormant. Quiet beats a pane full of broken characters.
Early review passes killed the cute shortcuts. Id schemes that fall over on quiet transmits. Probes that lie about capability. Inventing a reverse channel when host_query already exists. Direct placements still get turned into placeholders under the hood so the grid stays the source of truth.
Then the milestones ran under that plan. Same bar. Same review. CI was green while a pet still sat on the wrong cells, so the human half stayed in the loop: screenshots, a kgp-log trail, resize, detach a second client, scroll, try again. Pets fighting the grid. Backgrounds wrong after a grid tweak. Capability checks that made an app decide KGP was dead forever. Yazi zero-extent placements that looked fine until you noticed the preview never painted. Agent proposes the next fix. I attach and try to break it. Fail again. Repeat until the daily tools stop lying.
What works now
Public fork on main: kaankoken/zellij. On Ghostty, Yazi previews, Codex pets, and Grok images show up inside the session I already live in. I am not jumping to a second terminal for pictures anymore.
Upstream already said something useful
PR 4851 was a Kitty-only passthrough. Maintainer imsnif closed the conversation on 2026-03-14 with a clear long-term preference: generic, protocol-agnostic passthrough, so Zellij does not grow a special case for every graphics dialect that shows up next year. Fair call for the project.
My fork is deeper KGP bookkeeping on purpose: placeholders, zellij-owned ids, local answers for app queries from a host capability probe. It keeps the desk honest today. It is also a protocol-specific shape, which is the maintenance cost he wants to keep out of tree. Both of those stand.
Next step for me is to honor that decision and aim a contribution at the generic direction he described. The fork stays the daily-drive bridge while that work exists.