Skip to content

Converge keyboard dispatch slice 3: terminal / PTY key routing (#351) #758

Description

@JDonaghy

Continuation of #734, slice 3 of 7. #734 declared a 7-slice plan and was
closed when slice 1's PR merged — the remaining slices were never filed. This issue is
one of them. Measured on develop @ 44882e9 (2026-09-02):

handle_key_press (src/gtk/mod.rs) 274 ·
dispatch_gtk_panel_accelerator 80 · handle_key_pressed (src/tui_main/shell_app.rs)
604 · handle_sidebar_focused_key 442 · dispatch_panel_accelerator_sizeless 120
1,520 lines total, against the 1,622 #734 was filed on. 13 of the 19
mirrors mod.rs:NNNN comments survive, and every surviving reference is still wrong.

Locate everything below by symbol, not by line number#734's own body warns that
its numbers drift, and they already have.

The rung

TUI: ── Terminal (PTY) key routing (#351, mirrors mod.rs:2439-:2513) ── in
handle_key_pressed. The cited GTK lines now land on | Msg::OpenWorkspaceDialog — a match
arm in a dispatch that #732 has since deleted outright. The pointer is not merely stale;
its target no longer exists.

GTK's twin lives in handle_key_press, and the two disagree about which keys are forwarded
to the PTY versus handled by the app. #471 (typing in the right pane of a split terminal
makes text in that pane disappear) is a live GTK-only bug in this rung.

Adjacent, and in scope because they are the same decision: ── Bracketed paste (mirrors mod.rs:3032-:3035) ── and ── Resize → PTY resize (mirrors mod.rs:3036-:3045) ──
(shell_app.rs) — both carry stale pointers to the same deleted function.

Note

TerminalSession (quadraui) owns the encoder question — see quadraui#342 ("lift the
keyboard/mouse → PTY encoder out of the example into the engine") and quadraui#343
(bracketed-paste as an input-readiness signal). Check quadraui first (CLAUDE.md): if
the encoder belongs upstream, this slice becomes an adoption issue and the shared half is a
thin render::route_terminal_key. Say which it is in the PR.

Acceptance criteria

Inherited verbatim from #734 — these are the rules that make a slice done:

  • The rung is implemented once, in shared code (src/render.rs), and called from both
    backends.
  • Both backends' bespoke arms for that rung are deleted in the same PR. A migration that
    leaves both paths is not done (GOALS.md, "How to use this doc").
  • Black-box coverage on both backendsTuiDriver via driver_with_shell(TuiShellApp, …)
    in src/tui_main/shell_app.rs, and GtkDriver in src/gtk/testing.rs. Assert on rendered
    output
    , never on state being populated (CLAUDE.md rule 1).
  • The PR states the new tests were verified RED against unfixed develop (CLAUDE.md rule 2).
  • Combined production line count of the entry points above strictly decreases; the PR states
    before/after. (Converge the two mouse routers — ~4,800 lines implementing the same precedence ladder twice #733 slice 1 met this only barely — 4,994 → 4,861 — and Converge frame composition — ~4,500 lines laying down the same surfaces in two hand-kept orders #735 slice 1 did not
    meet it at all. State the numbers.)
  • cargo build && cargo test && cargo clippy -- -D warnings && cargo fmt --check EXIT=0.

Scope discipline

This is one slice. Do not attempt the rungs belonging to the sibling issues listed in
Sequencing — they conflict in the same files and are queued behind this one on purpose.

Files

  • src/render.rs (the shared router)
  • src/tui_main/shell_app.rs
  • src/gtk/mod.rs
  • src/gtk/testing.rs (GtkDriver coverage)

Sequencing

After slice 2 (focus owners) — terminal focus sits inside the focus ladder that slice
rewrites.

Relates: #351, #471, quadraui#342, quadraui#343.

Milestone

#7 Platform-Neutral

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    coordTracked by coord-tui pipelineenhancementNew feature or requeststatus:readyRefined and ready to enter the work pipelinesub-taskSub-task of a parent issuetier:largeMulti-module / algorithm-heavy work. Routes to opus via models.labels.uiUI/rendering

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions