fix: show slash-command fallback when terminal lacks Ctrl+Alt support

Terminals like macOS Terminal.app and JetBrains IDEs don't support
the Kitty keyboard protocol, so Ctrl+Alt shortcuts silently fail.
Shortcut descriptions now detect unsupported terminals and surface
the equivalent slash command (e.g. /gsd status, /bg, /voice).

Closes #100, closes #104

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lex Christopherson 2026-03-12 09:20:10 -06:00
parent 46c88e6494
commit 0dc0ccbacb
4 changed files with 29 additions and 5 deletions

View file

@ -48,6 +48,7 @@ import { createConnection } from "node:net";
import { randomUUID } from "node:crypto";
import { writeFileSync, readFileSync, existsSync, mkdirSync } from "node:fs";
import { join } from "node:path";
import { shortcutDesc } from "../shared/terminal.js";
import { createRequire } from "node:module";
// ── Windows VT Input Restoration ────────────────────────────────────────────
@ -2356,7 +2357,7 @@ export default function (pi: ExtensionAPI) {
// ── Ctrl+Alt+B shortcut ──────────────────────────────────────────────
pi.registerShortcut(Key.ctrlAlt("b"), {
description: "Open background process manager",
description: shortcutDesc("Open background process manager", "/bg"),
handler: async (ctx) => {
latestCtx = ctx;
await ctx.ui.custom<void>(

View file

@ -47,6 +47,7 @@ import {
import { Key } from "@mariozechner/pi-tui";
import { join } from "node:path";
import { existsSync } from "node:fs";
import { shortcutDesc } from "../shared/terminal.js";
import { Text } from "@mariozechner/pi-tui";
// ── ASCII logo ────────────────────────────────────────────────────────────
@ -184,10 +185,8 @@ export default function (pi: ExtensionAPI) {
});
// ── Ctrl+Alt+G shortcut — GSD dashboard overlay ────────────────────────
// Requires Kitty keyboard protocol or modifyOtherKeys support.
// Terminals without support (macOS Terminal.app, JetBrains): use /gsd status instead.
pi.registerShortcut(Key.ctrlAlt("g"), {
description: "Open GSD dashboard (or use /gsd status)",
description: shortcutDesc("Open GSD dashboard", "/gsd status"),
handler: async (ctx) => {
// Only show if .gsd/ exists
if (!existsSync(join(process.cwd(), ".gsd"))) {

View file

@ -0,0 +1,23 @@
/**
* Terminal capability detection for keyboard shortcut support.
*
* Ctrl+Alt shortcuts require the Kitty keyboard protocol or modifyOtherKeys.
* Terminals that lack this support silently swallow the key combos.
*/
const UNSUPPORTED_TERMS = ["apple_terminal"];
export function supportsCtrlAltShortcuts(): boolean {
const term = (process.env.TERM_PROGRAM || "").toLowerCase();
const jetbrains = (process.env.TERMINAL_EMULATOR || "").toLowerCase().includes("jetbrains");
return !UNSUPPORTED_TERMS.some((t) => term.includes(t)) && !jetbrains;
}
/**
* Returns a shortcut description that includes a slash-command fallback hint
* when the current terminal likely can't fire Ctrl+Alt combos.
*/
export function shortcutDesc(base: string, fallbackCmd: string): string {
if (supportsCtrlAltShortcuts()) return base;
return `${base} — shortcut may not work in this terminal, use ${fallbackCmd}`;
}

View file

@ -1,4 +1,5 @@
import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
import { shortcutDesc } from "../shared/terminal.js";
import type { AssistantMessage } from "@mariozechner/pi-ai";
import { isKeyRelease, Key, matchesKey, truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
import { spawn, execSync, type ChildProcess } from "node:child_process";
@ -131,7 +132,7 @@ export default function (pi: ExtensionAPI) {
});
pi.registerShortcut("ctrl+alt+v", {
description: "Toggle voice mode",
description: shortcutDesc("Toggle voice mode", "/voice"),
handler: async (ctx) => toggleVoice(ctx),
});