Skip to content

feat(btw): browse mode, token tracking, inline commands, and SDK fix#41

Open
Fatih0234 wants to merge 2 commits into
mitsuhiko:mainfrom
Fatih0234:feat/btw-overlay-ux
Open

feat(btw): browse mode, token tracking, inline commands, and SDK fix#41
Fatih0234 wants to merge 2 commits into
mitsuhiko:mainfrom
Fatih0234:feat/btw-overlay-ux

Conversation

@Fatih0234
Copy link
Copy Markdown

@Fatih0234 Fatih0234 commented Apr 24, 2026

Summary

This PR improves the BTW side-chat overlay with transcript navigation, token visibility, inline commands, and an SDK compatibility fix.

Bug Fixes

  • SDK API fix: session.agent.replaceMessages() was not a valid method. Changed to session.agent.state.messages = ... which matches the current SDK.
  • Overlay lifecycle: Fixed a bug where injecting selected text into the main chat (i) left the overlay runtime in a stale state, preventing /btw from reopening until the agent was restarted.

New Features

Tab Browse Mode

  • Press Tab to toggle between input mode and transcript browse mode
  • ↑/↓ navigate line-by-line through the transcript
  • PgUp/PgDn scroll by a full page
  • Viewport auto-scrolls to keep the cursor visible

Multi-select & Inject

  • v toggles multi-select mode
  • s toggles selection on the current line
  • i injects selected (or current cursor) lines into the main chat input as quoted text, then closes the overlay
  • c copies selected text to the clipboard

Token & Model Visibility

  • Overlay title shows the active [provider/model]
  • Each completed response shows provider/model · X tokens
  • Status bar shows running total: "Ready · X tokens"

Error Recovery

  • Empty Enter retries the last failed prompt
  • Status shows "Error — Enter to retry"

Inline Slash Commands

  • /clear — resets the side thread
  • /inject — summarizes the thread and injects into main chat

Other

  • Removed the hardcoded 6-message transcript limit (now shows full thread)
  • Dynamic footer hints that change based on current mode

Testing

  • Open with /btw, ask questions, press Tab to browse
  • Select lines with v + s, press i to inject into main chat
  • Verify /btw can be reopened after inject
  • Verify /btw → "Continue previous conversation" works after overlay close

- Fix SDK API: replaceMessages -> state.messages assignment
- Add Tab toggle for transcript browse mode (↑↓ navigate, PgUp/PgDn)
- Add multi-select: v toggles, s toggles line, i injects into main chat
- Add copy to clipboard (c) in browse mode
- Add model/provider badge in overlay title
- Add per-message and total token tracking
- Add error recovery: empty Enter retries failed prompt
- Add inline slash commands: /clear, /inject
- Remove hardcoded 6-message transcript limit
- Fix overlay runtime cleanup after inject close
Copilot AI review requested due to automatic review settings April 24, 2026 23:42
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 enhances the BTW side-chat overlay by adding transcript browse/selection capabilities, token/model visibility, inline commands, improved error retry behavior, and an SDK compatibility fix for seeding messages.

Changes:

  • Added transcript browse mode (Tab), navigation (↑/↓, PgUp/PgDn), multi-select, copy, and inject-to-main-chat behavior.
  • Displayed per-response model/token info plus a running token total in the overlay status/title.
  • Fixed SDK usage by replacing session.agent.replaceMessages(...) with session.agent.state.messages = ..., and improved overlay lifecycle cleanup.

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

Comment thread extensions/btw.ts
if (!question) {
if (pendingError && pendingQuestion) {
pendingError = null;
setOverlayDraft("");
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

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

The retry path calls runBtwPrompt(ctx, pendingQuestion) even though ctx is typed as ExtensionContext | ExtensionCommandContext, while runBtwPrompt requires an ExtensionCommandContext. This will either fail type-checking or allow calling runBtwPrompt with a non-command context (which you already guard against later via "waitForIdle" in ctx). Add the same guard here or widen runBtwPrompt to accept the union and handle the non-command case.

Suggested change
setOverlayDraft("");
setOverlayDraft("");
if (!("waitForIdle" in ctx)) {
setOverlayStatus("BTW submit requires command context. Re-open with /btw.");
return;
}

Copilot uses AI. Check for mistakes.
Comment thread extensions/btw.ts Outdated
overlayRuntime = null;
}
if (nextPrompt && ctx.hasUI) {
ctx.ui.setEditorText(nextPrompt);
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

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

Injecting selected text uses ctx.ui.setEditorText(nextPrompt), which overwrites whatever the user already has in the main editor. Other extensions append to the existing editor text via ctx.ui.getEditorText() first. Consider appending (or at least preserving existing content) so injection doesn’t unintentionally discard a draft prompt.

Suggested change
ctx.ui.setEditorText(nextPrompt);
const existingPrompt = ctx.ui.getEditorText();
const combinedPrompt =
existingPrompt.length > 0
? `${existingPrompt}${existingPrompt.endsWith("\n") ? "\n" : "\n\n"}${nextPrompt}`
: nextPrompt;
ctx.ui.setEditorText(combinedPrompt);

Copilot uses AI. Check for mistakes.
Comment thread extensions/btw.ts
Comment on lines 562 to 579
@@ -406,6 +569,13 @@ export default function (pi: ExtensionAPI) {
// Assistant message rendered as markdown
const mdLines = renderMarkdownLines(item.answer, width);
lines.push(...mdLines);

// Model & token info
if (item.usage) {
const modelName = `${item.provider}/${item.model}`;
const tk = formatTokens(item.usage.totalTokens);
lines.push(theme.fg("dim", ` ${modelName} · ${tk} tokens`));
}
lines.push("");
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

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

getTranscriptLinesInner now renders the entire thread on every refresh (including Markdown rendering per item). For long side threads this can become noticeably slow and increase CPU usage during streaming (since renders are frequent). Consider caching rendered lines per thread entry and/or only rendering the visible window (based on scroll offset + height) instead of re-rendering the full transcript each time.

Copilot uses AI. Check for mistakes.
Comment thread extensions/btw.ts
Comment on lines +265 to 275
const kb = this.keybindings;

if (kb.matches(data, "selectCancel")) {
if (this.selectMode) {
this.selectMode = false;
this.selectedLines.clear();
this.tui.requestRender();
return;
}
this.onDismissCallback();
return;
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

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

handleInput checks kb.matches(data, "selectCancel"), but the rest of the codebase uses the standard cancel binding key (tui.select.cancel). If selectCancel isn't a real binding, Esc won’t work in browse/select mode (since Input.onEscape is bypassed there). Use the same cancel binding key used elsewhere (e.g., tui.select.cancel) so Esc reliably exits select/browse or closes the overlay.

Copilot uses AI. Check for mistakes.
- Fix cancel binding from nonexistent selectCancel to tui.select.cancel
- Add ExtensionCommandContext guard before retrying failed prompts
- Append injected BTW text to existing editor draft instead of overwriting
- Cache rendered Markdown lines per thread entry to avoid re-rendering the full transcript on every refresh
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