Problem
:Opencode toggle closes windows and deletes buffers every time. Reopening recreates the UI from scratch, re-renders all output, and loses input draft, scroll/cursor position, and focused pane.
Expected Behavior
When ui.persist_state = true (proposed default):
- Hide UI windows without deleting buffers
- Restore existing buffers into new split windows on next toggle
- Preserve: input draft, scroll/cursor position, focused pane,
input_hidden state
- Respect user intent: if user clicks a line after restore, do NOT force-scroll to bottom
When ui.persist_state = false, behavior remains unchanged.
Design: Three-State Window Model
closed ──(open)──> visible ──(hide)──> hidden ──(restore)──> visible
^ | | |
+──(close)─────────+ +──(close_hidden)─────+
State is derived — get_window_status() checks window validity + hidden buffer existence. A pure-function decision engine (resolve_toggle_decision) maps (status, persist_state) → action.
Performance
100 toggle cycles, same session with output content:
| Metric |
persist_state=false |
persist_state=true |
| Mean |
27.15 ms |
15.09 ms |
| P50 |
~22 ms |
~12.4 ms |
| P95 |
~37 ms |
~24.6 ms |
~44% mean improvement from skipping buffer recreation and full session re-render.
Pre-requisite Fixes Discovered
Issues found during implementation that should be fixed independently:
curl.lua: job.pid persists after exit — health check false positive. Fix: explicit is_running flag
topbar.lua: missing pcall + win_is_valid guard on async callbacks
loading_animation.lua: missing buf_is_valid guard on extmark operations
output_window.lua: is_at_bottom() viewport check always true in streaming — fix: cursor-based check
output_window.lua: update_dimensions errors on split windows — fix: detect floating vs split
input_window.lua: same floating/split issue in apply_dimensions
renderer.lua: render_full_session has no debounce — rapid toggles cause request storms
event_manager.lua: _poll_external_messages hidden→visible transition causes redundant render + scroll override
reference_picker.lua: normal! zz on jump causes visual noise — make configurable
Implementation Status
Problem
:Opencode togglecloses windows and deletes buffers every time. Reopening recreates the UI from scratch, re-renders all output, and loses input draft, scroll/cursor position, and focused pane.Expected Behavior
When
ui.persist_state = true(proposed default):input_hiddenstateWhen
ui.persist_state = false, behavior remains unchanged.Design: Three-State Window Model
State is derived —
get_window_status()checks window validity + hidden buffer existence. A pure-function decision engine (resolve_toggle_decision) maps(status, persist_state)→ action.Performance
100 toggle cycles, same session with output content:
~44% mean improvement from skipping buffer recreation and full session re-render.
Pre-requisite Fixes Discovered
Issues found during implementation that should be fixed independently:
curl.lua:job.pidpersists after exit — health check false positive. Fix: explicitis_runningflagtopbar.lua: missingpcall+win_is_validguard on async callbacksloading_animation.lua: missingbuf_is_validguard on extmark operationsoutput_window.lua:is_at_bottom()viewport check always true in streaming — fix: cursor-based checkoutput_window.lua:update_dimensionserrors on split windows — fix: detect floating vs splitinput_window.lua: same floating/split issue inapply_dimensionsrenderer.lua:render_full_sessionhas no debounce — rapid toggles cause request stormsevent_manager.lua:_poll_external_messageshidden→visible transition causes redundant render + scroll overridereference_picker.lua:normal! zzon jump causes visual noise — make configurableImplementation Status
feat/hide-window-toggle(commitc894fd9)persist_state_spec.lua)scripts/bench_toggle.lua)