Skip to content

feat(llm): chat surface defaults to dialog + Alt+T auto toggle + chrome polish#180

Merged
fentas merged 3 commits into
masterfrom
feat/chat-defaults-and-toggles
May 21, 2026
Merged

feat(llm): chat surface defaults to dialog + Alt+T auto toggle + chrome polish#180
fentas merged 3 commits into
masterfrom
feat/chat-defaults-and-toggles

Conversation

@fentas
Copy link
Copy Markdown
Owner

@fentas fentas commented May 21, 2026

Requests addressed

From live testing of the chat panel:

opening chat mode (inline or fullscreen) should default to dialog mode (no sense to show the short code here), only thing to toggle in this mode is auto on/off (show short code for this instead - missing right now) + shortcode for provider change is missing here

alt+c does not be in statusbar (in inline chat) as it is in the header when open

Changes

  • Chat surface defaults to dialog mode. currentDispatchMode(rt) returns .dialog (or .auto if rt.auto_mode_active) when a chat panel/overlay is open, instead of the prose-only .chat. The LLM now uses the full exec/question/done protocol in chat surfaces — the panel itself is the richer surface.

  • New action llm_chat_toggle_auto bound to Alt+T. Toggles rt.auto_mode_active. Only acts while a chat surface is open. Dual-encoded (kitty kbd CSI-u + legacy).

  • Chat panel chrome surfaces the shortcuts. Trailing hint now Alt+T auto · Alt+M model · Alt+C close · Enter send (51 cols). Mode word shows live auto state: atty chat (auto), atty chat (auto, incognito). Overlay title + footer mirror.

  • Statusbar drops LLM hint while chat is open. statusText returns null when chat_inline_open or chat_overlay_open — the panel's own chrome owns the shortcut discovery surface; doubling them in the statusbar would advertise Alt+C chat as an open-hint when the panel is already open.

  • .done chat-bypass updated to check chat_inline_open or chat_overlay_open directly (was via currentDispatchMode == .chat which no longer fires).

Test plan

  • 817/817 unit tests (+1 Alt+T toggle, +2 chrome regression: trailing hint shows all four labels, auto-on flips mode word)
  • zig fmt --check clean
  • Manual: chat panel open, press Alt+T → mode word flips to (auto); press again → flips back
  • Manual: Alt+M cycles provider; header divider updates immediately (already wired in PR fix(llm): chat panel UX — divider repaint on cycle + drop 3-row truncation #176)
  • Manual: statusbar contributions from other modules still render when chat is open

🤖 Generated with Claude Code

…me polish

Live testing surfaced UX gaps in the chat surface. Four changes:

- **Default to dialog mode when chat is open.** `currentDispatchMode`
  used to return `.chat` whenever a chat panel/overlay was visible,
  which routed through the prose-only `prompt_chat`. The user wants
  the richer exec/question/done protocol available in chat too. Now
  returns `.dialog` (or `.auto` if the auto flag is set) for any
  chat-open state. The standalone `.chat` mode stays in the type
  system for users who configure it explicitly.

- **Alt+T toggles auto-exec within chat.** New action
  `llm_chat_toggle_auto` flips `rt.auto_mode_active`. Bound to
  Alt+T (dual-encoded kitty kbd CSI-u + legacy `\x1ba`). Only
  acts while a chat surface is open; outside chat the persistent
  Alt+S / Alt+Shift+S bindings own the dialog/auto distinction.

- **Chrome shows live state + shortcuts.** Inline panel divider
  trailing hint expanded from `Alt+C close · Enter send` (25 cols)
  to `Alt+T auto · Alt+M model · Alt+C close · Enter send` (51
  cols). When auto is on, mode word becomes `atty chat (auto)` /
  `atty chat (auto, incognito)`. Overlay title + footer carry the
  same shortcut hints.

- **Statusbar suppresses LLM hint while chat is open.** The
  panel's own chrome already shows the active shortcuts; the
  statusbar's `Alt+C chat` discovery hint becomes redundant + AND
  misleading ("Alt+C opens the panel" is wrong when it's open).
  `statusText` returns null in that state; other modules still
  contribute.

`.done` chat-bypass updated to check `chat_inline_open or
chat_overlay_open` directly (was via `currentDispatchMode == .chat`
which no longer fires).

817/817 unit tests (+2 chrome regression tests, +1 Alt+T toggle
test) + fmt clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the LLM chat UI/behavior so chat surfaces behave like full dialog/auto flows by default, adds an in-chat auto toggle, and adjusts chat chrome to make key shortcuts more discoverable while reducing duplicated hints in the status bar.

Changes:

  • Default chat-surface dispatch now uses dialog/auto semantics (instead of a prose-only .chat mode) and updates .done handling accordingly.
  • Adds llm_chat_toggle_auto (Alt+T) to toggle auto-exec while a chat surface is open.
  • Updates inline/overlay chrome + tests to surface Alt+T / Alt+M / Alt+C / Enter shortcuts and suppress the LLM statusbar hint while chat is open.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/proxy.zig Routes the new llm_chat_toggle_auto action through the LLM module dispatch path.
src/modules/llm/paint.zig Updates overlay title/footer and inline divider chrome (mode word + shortcut hints).
src/modules/llm/paint_tests.zig Adds/updates regression tests for the updated chrome and auto mode word behavior.
src/modules/llm/hooks.zig Implements Alt+T toggle handler; changes dispatch mode selection for chat surfaces; adjusts .done chat-bypass logic.
src/modules/llm/hooks_tests.zig Adds a regression test for Alt+T toggling behavior and repaint latching.
src/modules/llm.zig Adds default keybindings for Alt+T (dual-encoded) and suppresses LLM status text while chat is open.
src/keymap.zig Adds the new llm_chat_toggle_auto action with documentation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/modules/llm/paint.zig
Comment thread src/modules/llm/paint.zig Outdated
Comment thread src/modules/llm/paint.zig
Comment thread src/modules/llm/hooks.zig
fentas and others added 2 commits May 21, 2026 14:01
…rome + in-flight indicator

Three subagent blockers addressed:

- **Dead `prompt_chat` + `effective_chat_system_prompt` deleted.**
  `currentDispatchMode` doesn't return `.chat` for chat-open state
  any more (returns `.dialog`/`.auto`), so the chat-specific system
  prompt and its `effective_*` wrapper became unreachable. Per
  Suckless ethos: delete rather than ship dead paths. `Mode.chat`
  stays in the enum because `for_modes` provider masks still
  reference it; `selectPrompt(.chat)` and the request-build
  `live_mode` switch arm now fall through to `prompt_dialog` /
  `effective_dialog_system_prompt`. Sibling tests updated.

- **In-flight indicator kept while chat is open.** Round-1 fix
  suppressed all LLM statusbar text when a chat surface was open —
  which also hid the `thinking…` indicator during a request. Real
  UX regression (chat panel doesn't paint its own spinner). Now
  `statusText` returns `thinking_hint` when `rt.in_flight` AND
  chat is open; only the idle/discovery hint is suppressed.

- **Trailing-chrome hint shrinks progressively.** Round-1 fix
  hardcoded `trail_min_clearance = 51`. On an 80-col terminal with
  the `(auto, incognito)` mode word, the label alone hit 35 cols,
  leaving 45 < 51 — chrome overran into the panel body. Now three
  hint sizes: full (51), medium (38, drops `Enter send`), short
  (22, drops labels but keeps the keys). Picked dynamically from
  `cols - label_visible`. Narrow-terminal test pins behaviour at
  80 cols + auto + incognito.

817/817 unit tests + fmt clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…shows incognito

Two Copilot findings on ec9fb72:

- **Inline chrome provider resolution used `.chat`** but
  `currentDispatchMode` now returns `.dialog`/`.auto` for chat
  surfaces. A user with a chat-only entry in `cfg.providers[]`
  would see the chrome label show one provider while the request
  actually fired through a different one (the dialog match). Now
  resolves with the live mode (`.dialog` or `.auto` based on
  `rt.auto_mode_active`).

- **Overlay title only reflected auto-mode** — incognito state
  was ignored, unlike the inline divider that shows the 🕶 glyph
  + `(incognito)` annotation. Threaded `ctx` into
  `paintChatOverlay`; title now mirrors the inline divider's
  icon + parenthetical-state logic so the two surfaces stay
  visually consistent.

Off-by-one nit on the `trail_min_clearance = 51` doc comment vs
actual byte count is cosmetic — the runtime math uses the same
constant throughout, so a 52-col reality is internally consistent.
Skipping that one.

Dead-`Mode.chat` from finding #4 is partially addressed
(provider resolution no longer relies on `.chat`); full deletion
of `Mode.chat` + `for_modes.chat` would be a bigger cleanup
landing in a follow-up.

817/817 unit tests + fmt clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@fentas
Copy link
Copy Markdown
Owner Author

fentas commented May 21, 2026

Review loop: 3 rounds. 12 findings addressed across rounds:

  • Round 1 (subagent, fix-and-ship): chat surface dispatches as .dialog/.auto (not .chat), Alt+T toggle action + binding + chrome shortcut visibility, statusbar suppression while chat open, .done bypass updated to direct flag check
  • Round 2 (subagent + Copilot): deleted dead prompt_chat / effective_chat_system_prompt, kept thinking_hint visible when chat open + in_flight, progressive chrome shrink (3 hint sizes by cols)
  • Round 3 (Copilot): chrome provider resolution uses live mode instead of hardcoded .chat, overlay title mirrors inline divider's icon + (auto, incognito) display; subagent verdict: ship

Mode.chat enum value + for_modes.chat mask survive but no longer reach a live dispatch — full deletion deferred to follow-up. 817/817 unit + 6/6 itest + fmt clean. Merging.

@fentas fentas merged commit c65381d into master May 21, 2026
6 checks passed
@fentas fentas deleted the feat/chat-defaults-and-toggles branch May 21, 2026 12:12
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.

Comment thread src/modules/llm/paint.zig
Comment on lines +838 to +842
// Provider resolution uses the LIVE dispatch mode so the
// chrome label matches the provider that will actually
// serve the next request. Chat surfaces now dispatch as
// `.dialog`/`.auto` (per `currentDispatchMode` in
// hooks.zig); hardcoding `.chat` here was a relic of the
Comment thread src/modules/llm/paint.zig
Comment on lines +908 to +922
const min_dashes: usize = 4;
const hint: []const u8 = if (cols_usize >= label_visible + 51 + min_dashes)
hint_full
else if (cols_usize >= label_visible + 38 + min_dashes)
hint_med
else if (cols_usize >= label_visible + 22 + min_dashes)
hint_short
else
"\x1B[0m"; // No room for any hint — leave the row blank past the label.
const hint_cols: usize = if (hint.ptr == hint_full.ptr)
51
else if (hint.ptr == hint_med.ptr)
38
else if (hint.ptr == hint_short.ptr)
22
Comment thread src/modules/llm/paint.zig
Comment on lines +909 to +924
const hint: []const u8 = if (cols_usize >= label_visible + 51 + min_dashes)
hint_full
else if (cols_usize >= label_visible + 38 + min_dashes)
hint_med
else if (cols_usize >= label_visible + 22 + min_dashes)
hint_short
else
"\x1B[0m"; // No room for any hint — leave the row blank past the label.
const hint_cols: usize = if (hint.ptr == hint_full.ptr)
51
else if (hint.ptr == hint_med.ptr)
38
else if (hint.ptr == hint_short.ptr)
22
else
0;
Comment thread src/proxy.zig
Comment on lines 1020 to 1024
.chat_scroll_page_down,
.llm_chat_inline_grow,
.llm_chat_inline_shrink,
.llm_chat_toggle_auto,
=> {
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.

2 participants