Zen mode - #139
Closed
abosnjakovic wants to merge 1 commit into
Closed
Conversation
abosnjakovic
marked this pull request as ready for review
August 29, 2026 09:33
…oggle
Hides every Collie surface (header, strips, statusline, composer and its
docks) and leaves only the terminal mirror plus one floating exit pill.
Entry is a row in the pane's View dock, present only while the new
Settings toggle is on — the optional callback's absence IS the gate.
Zen itself is transient: the persisted half is availability only, and
DetailRoute's key={paneId} remount resets the active state per pane.
abosnjakovic
force-pushed
the
feat/zen-mode
branch
from
August 29, 2026 10:05
28cb532 to
dc9b831
Compare
Owner
|
I like this! Didn't have time yet to check out the details of the implementation, but I'll get back to you soon. |
Owner
|
Merged onto v1 as 44f343d, shipping in 1.0.0-beta.48. Thank you, this is a great feature and the design is all yours. Two things moved in the port: entry now lives in the pane's actions menu (the v1 header has no free icon slot), and every surface leaves through our collapse primitive so the exit animates as one motion. Your commit and authorship are preserved. Closing since the work is on v1. |
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.
Zen mode
collie-zen-mode.mp4
Adds an opt-in zen mode to the pane view: one tap hides every Collie surface and leaves the
terminal mirror alone on the screen, with a single floating button to come back.
On a phone the chrome is most of the viewport — header, tab strip, pane strip, statusline, the
controls row and the composer. When you're reading a long build log or a wide TUI, all of it is in
the way. Zen gives the mirror the whole screen without changing anything about how it renders.
Demo: settings toggle → one-tap entry → zen → scrollback → exit. Video attached below.
How it works
Off by default, behind a Settings toggle.
Settings → Zen mode(per device,localStorage,mirrors the Haptics pattern in
lib/haptics.ts+haptics-control.tsx). While it's off nothingchanges anywhere — no new button, no new row. Zen removes every way back except one floating
control, so it seemed wrong to hand that to people who didn't ask for it.
One entry point, in the pane header. A
Maximize2button joins the header cluster next to Find,gated on
zenAvailable && display. It sits with Find and History because it's the same family —"look at the output differently" — and because it must be one tap: this is the only control here
you toggle repeatedly in a session, so it can't live behind the ⚙. It's
size-8to match itssiblings rather than the 44px tap-target size, since a 44px button in a row of 32px ones reads as a
mistake. The status pill stays the rightmost item, per the existing note in
agent-chat.tsx.Exit is a floating pill in the same corner,
size-11(44px — it's a standalone control and theonly way out, so it gets the full tap target). Top-right so entering and leaving happen in one
place; opposite corners meant re-aiming on every toggle. It's
absoluteinside the safe-area-paddedwrapper, so
top-3clears the notch on its own and lands within a few px of the header button'svertical centre.
Transient by design. The setting persists; the state doesn't.
DetailRoute'skey={paneId}remount resets it on a pane switch, and a reload clears it, so a pane always openswith its chrome. It's local
useState, deliberately not aDisplayPrefsfield — if stickiness isever wanted that's the place for it, but it isn't built here.
What stays mounted in zen, on purpose:
StatusArea— renders nothing when idle, and it's the only surface for prompt-tap failures("menu changed", read-only errors). Hiding it would silently eat errors.
"Load older" / "Show entire history" affordances. Those are content inside the scroller, not
Collie chrome. Polling continues untouched.
UpdateAvailableBanner/ConnectionBanner(they live inRootLayout) — they onlyappear in exceptional states, and hiding a connection-lost bar would be dishonest.
Safe areas. The header carried
env(safe-area-inset-top)and the composer the bottom inset;with both unmounted the content wrapper takes them over, so the mirror doesn't run under the notch
or the home indicator.
Focus.
enterZen()blurs the active element before the chrome unmounts. "Tap to type" is on bydefault, so the composer's
<textarea>is often focused at that moment, and on iOS the softkeyboard belongs to the focused node — unmounting it focused can leave the keyboard up over a
screen that no longer has an input.
Tests
web/src/components/agent-chat.test.tsx, using the existingrenderChatharness:mode" button exists in the document (guards against a second entry point creeping back)
agent-chat.test.tsx37/37,composer.test.tsx82/82, backendbun test717/0, rootbun run build(both typechecks) clean. Verified end-to-end on a real device against live Herdr panes:gate off/on, one-tap entry, repeated enter/exit cycles, polling continuing in zen, scrollback and
"Load older" still working, reset on pane switch and on reload.