@oh-my-pi/pi-tui 18.3.2 → 18.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/dist/types/app-keybindings.d.ts +2 -17
- package/dist/types/autocomplete.d.ts +18 -3
- package/dist/types/chrome/keybinding-hints.d.ts +16 -10
- package/dist/types/components/editor.d.ts +11 -0
- package/dist/types/key-hint-format.d.ts +29 -0
- package/dist/types/keys.d.ts +2 -1
- package/dist/types/overlays/agent-hub.d.ts +1 -1
- package/dist/types/overlays/agent-transcript-viewer.d.ts +1 -1
- package/dist/types/overlays/model-picker.d.ts +1 -3
- package/dist/types/prompt/composer-hints.d.ts +42 -0
- package/dist/types/prompt/composer.d.ts +2 -1
- package/dist/types/prompt/custom-editor.d.ts +8 -2
- package/dist/types/prompt/macos-spelling.d.ts +7 -16
- package/dist/types/prompt/prompt-action-autocomplete.d.ts +4 -1
- package/dist/types/prompt/prose-gate.d.ts +24 -0
- package/dist/types/prompt/word-completion.d.ts +46 -0
- package/dist/types/render/render-utils.d.ts +1 -1
- package/dist/types/theme/active-symbols.d.ts +12 -0
- package/dist/types/theme/symbols.d.ts +1 -1
- package/dist/types/theme/theme-class.d.ts +0 -1
- package/dist/types/tools/eval.d.ts +1 -1
- package/dist/types/tools/mcp.d.ts +3 -1
- package/dist/types/tools/task-yield-assembly.d.ts +10 -2
- package/dist/types/tools/task.d.ts +4 -0
- package/package.json +11 -10
- package/src/app-keybindings.ts +11 -104
- package/src/apps/autoresearch-dashboard.ts +10 -3
- package/src/apps/debug/log-viewer.ts +3 -2
- package/src/apps/debug/raw-sse.ts +2 -1
- package/src/apps/git/git-tui.ts +9 -5
- package/src/apps/live-visualizer.ts +2 -1
- package/src/apps/ps-top.ts +13 -4
- package/src/autocomplete.ts +42 -7
- package/src/chat/compaction-summary-message.ts +2 -1
- package/src/chat/execution-shared.ts +4 -2
- package/src/chat/tool-execution.ts +3 -1
- package/src/chat/ttsr-notification.ts +4 -3
- package/src/chat/user-message.ts +2 -2
- package/src/chrome/keybinding-hints.ts +38 -13
- package/src/components/editor.ts +180 -58
- package/src/components/settings-list.ts +10 -3
- package/src/components/tab-bar.ts +2 -1
- package/src/debug-server.ts +4 -1
- package/src/hotkeys-markdown.ts +14 -17
- package/src/key-hint-format.ts +122 -0
- package/src/keys.ts +2 -1
- package/src/overlays/advisor-config.ts +28 -6
- package/src/overlays/agent-hub.ts +8 -5
- package/src/overlays/agent-transcript-viewer.ts +6 -4
- package/src/overlays/agents-hub.ts +22 -8
- package/src/overlays/annotation-overlay.ts +31 -17
- package/src/overlays/ask-dialog.ts +14 -19
- package/src/overlays/bordered-loader.ts +2 -1
- package/src/overlays/btw-history-panel.ts +15 -12
- package/src/overlays/btw-panel.ts +12 -7
- package/src/overlays/cleanse-panel.ts +9 -6
- package/src/overlays/codex-reset-fireworks.ts +5 -3
- package/src/overlays/copy-selector.ts +13 -5
- package/src/overlays/extensions/extension-dashboard.ts +6 -1
- package/src/overlays/history-search.ts +3 -2
- package/src/overlays/hook-editor.ts +7 -2
- package/src/overlays/hook-input.ts +2 -1
- package/src/overlays/hook-selector.ts +5 -1
- package/src/overlays/login-dialog.ts +17 -4
- package/src/overlays/logout-account-selector.ts +10 -1
- package/src/overlays/mcp-add-wizard.ts +32 -20
- package/src/overlays/model-hub.ts +55 -28
- package/src/overlays/model-picker.ts +27 -12
- package/src/overlays/move-overlay.ts +5 -1
- package/src/overlays/omfg-panel.ts +8 -5
- package/src/overlays/pause-screen.ts +7 -3
- package/src/overlays/plan-review-overlay.ts +40 -14
- package/src/overlays/plan-save-overlay.ts +3 -1
- package/src/overlays/plugin-settings.ts +15 -8
- package/src/overlays/reset-usage-selector.ts +10 -3
- package/src/overlays/rewind-selector.ts +11 -5
- package/src/overlays/session-info-overlay.ts +4 -3
- package/src/overlays/session-selector.ts +12 -2
- package/src/overlays/settings-selector.ts +18 -11
- package/src/overlays/tree-selector.ts +27 -4
- package/src/overlays/usage-dashboard.ts +8 -2
- package/src/prompt/composer-cache.ts +2 -1
- package/src/prompt/composer-hints.ts +75 -0
- package/src/prompt/composer.ts +3 -2
- package/src/prompt/custom-editor.ts +22 -4
- package/src/prompt/macos-spelling.ts +17 -158
- package/src/prompt/prompt-action-autocomplete.ts +9 -2
- package/src/prompt/prose-gate.ts +87 -0
- package/src/prompt/tips.txt +6 -6
- package/src/prompt/welcome.ts +35 -2
- package/src/prompt/word-completion.ts +205 -0
- package/src/render/render-utils.ts +4 -4
- package/src/setup/scenes/composer.ts +5 -1
- package/src/setup/scenes/model.ts +3 -1
- package/src/setup/scenes/providers.ts +4 -1
- package/src/setup/scenes/sign-in.ts +4 -2
- package/src/setup/scenes/splash.ts +9 -4
- package/src/setup/scenes/theme.ts +18 -5
- package/src/setup/wizard-overlay.ts +5 -4
- package/src/theme/active-symbols.ts +20 -0
- package/src/theme/glyph-bundle.json +28 -1
- package/src/theme/symbols.ts +88 -6
- package/src/theme/theme-class.ts +0 -1
- package/src/theme/theme.ts +2 -0
- package/src/tools/bash-interactive.ts +2 -1
- package/src/tools/eval.ts +3 -3
- package/src/tools/mcp.ts +3 -1
- package/src/tools/task-yield-assembly.ts +34 -8
- package/src/tools/task.ts +14 -5
- package/dist/types/overlays/tiny-title-download-progress.d.ts +0 -21
- package/src/overlays/tiny-title-download-progress.ts +0 -102
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Key-hint formatting, kept free of the native addon: it imports only types
|
|
3
|
+
* plus the theme symbol mirror, so addon-free CLI paths (`omp --version`,
|
|
4
|
+
* help text in cli/command-help.ts) can format keys without loading
|
|
5
|
+
* `@oh-my-pi/pi-natives` through the keybindings registry.
|
|
6
|
+
*/
|
|
7
|
+
import type { KeyId } from "./keybindings";
|
|
8
|
+
import type { ModifierName } from "./keys";
|
|
9
|
+
import { activeThemeSymbol } from "./theme/active-symbols";
|
|
10
|
+
import type { SymbolKey } from "./theme/symbols";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Key hint formatting: every key shown to the user renders through
|
|
14
|
+
* {@link formatKeyHint}, so a key reads the same everywhere in the UI.
|
|
15
|
+
*
|
|
16
|
+
* Keys resolve through the active theme's `key.*` symbols — words in the
|
|
17
|
+
* ascii preset, keycap glyphs in unicode, icons in nerd — falling back to the
|
|
18
|
+
* ascii words before a theme loads. Modifiers are platform-aware: macOS
|
|
19
|
+
* keycaps are labelled ⌃ ⌥ ⌘ (`key.*Mac`), while other platforms keep
|
|
20
|
+
* `Ctrl`/`Alt`/`Super`, since those glyphs name no key on a PC keyboard.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Platform override for key-hint rendering; `undefined` resolves to the host
|
|
25
|
+
* `process.platform`. Mirrors `setKittyProtocolActive` in the TUI keys module:
|
|
26
|
+
* a single seam that keeps hint output deterministic in tests without mutating
|
|
27
|
+
* the global `process.platform`.
|
|
28
|
+
*/
|
|
29
|
+
let keyHintPlatformOverride: NodeJS.Platform | undefined;
|
|
30
|
+
|
|
31
|
+
/** Pin the platform used to render modifier labels (test seam). */
|
|
32
|
+
export function setKeyHintPlatform(platform: NodeJS.Platform | undefined): void {
|
|
33
|
+
keyHintPlatformOverride = platform;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Platform currently used for key-hint rendering. */
|
|
37
|
+
export function keyHintPlatform(): NodeJS.Platform {
|
|
38
|
+
return keyHintPlatformOverride ?? process.platform;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Modifier → [generic symbol, macOS symbol], in display order (⌃⌥⇧⌘, as macOS menus list them). */
|
|
42
|
+
const MODIFIER_SYMBOLS: Record<string, readonly [SymbolKey, SymbolKey] | undefined> = {
|
|
43
|
+
ctrl: ["key.ctrl", "key.ctrlMac"],
|
|
44
|
+
alt: ["key.alt", "key.altMac"],
|
|
45
|
+
shift: ["key.shift", "key.shift"],
|
|
46
|
+
super: ["key.super", "key.superMac"],
|
|
47
|
+
};
|
|
48
|
+
const KEY_SYMBOLS: Record<string, SymbolKey | undefined> = {
|
|
49
|
+
esc: "key.esc",
|
|
50
|
+
escape: "key.esc",
|
|
51
|
+
enter: "key.enter",
|
|
52
|
+
return: "key.enter",
|
|
53
|
+
space: "key.space",
|
|
54
|
+
tab: "key.tab",
|
|
55
|
+
backspace: "key.backspace",
|
|
56
|
+
delete: "key.delete",
|
|
57
|
+
up: "key.up",
|
|
58
|
+
down: "key.down",
|
|
59
|
+
left: "key.left",
|
|
60
|
+
right: "key.right",
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/** Keys without a keycap glyph; spelled out in every preset. */
|
|
64
|
+
const KEY_WORDS: Record<string, string | undefined> = {
|
|
65
|
+
home: "Home",
|
|
66
|
+
end: "End",
|
|
67
|
+
pageup: "PgUp",
|
|
68
|
+
pagedown: "PgDn",
|
|
69
|
+
insert: "Ins",
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
function keySymbol(key: SymbolKey): string {
|
|
73
|
+
return activeThemeSymbol(key);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const HAS_LETTER = /[A-Za-z0-9]/;
|
|
77
|
+
|
|
78
|
+
/** A displayable key: a chord, or a bare modifier for "hold ⇧" style hints. */
|
|
79
|
+
export type KeyName = KeyId | ModifierName;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Format one key for display: `"ctrl+shift+c"` → `⌃⇧C` (macOS) / `Ctrl+⇧C`,
|
|
83
|
+
* `"escape"` → `⎋`, `"alt+up"` → `⌥↑` / `Alt+↑`, `"shift"` → `⇧`.
|
|
84
|
+
* A glyph modifier is followed by the preset's `key.joiner` (nothing in
|
|
85
|
+
* unicode, a space in nerd, whose icons blur together when adjacent); a word
|
|
86
|
+
* modifier takes a `+`. Modifiers
|
|
87
|
+
* render in canonical order whatever the binding says (`shift+ctrl+p` → `⌃⇧P`).
|
|
88
|
+
* A bare letter stays lowercase (`q`) since `Q` would read as ⇧Q; in a chord it
|
|
89
|
+
* is capitalized. Other keys are capitalized (`f5` → `F5`).
|
|
90
|
+
*/
|
|
91
|
+
export function formatKeyHint(key: KeyName): string {
|
|
92
|
+
const mac = keyHintPlatform() === "darwin";
|
|
93
|
+
// A trailing `+` is the plus key itself (`+`, `ctrl++`), not a separator.
|
|
94
|
+
const parts = key.endsWith("+") ? [...key.slice(0, -1).split("+").slice(0, -1), "+"] : key.split("+");
|
|
95
|
+
const base = parts.pop()!;
|
|
96
|
+
let out = "";
|
|
97
|
+
for (const modifier in MODIFIER_SYMBOLS) {
|
|
98
|
+
if (!parts.includes(modifier)) continue;
|
|
99
|
+
const label = keySymbol(MODIFIER_SYMBOLS[modifier]![mac ? 1 : 0]);
|
|
100
|
+
out += HAS_LETTER.test(label) ? `${label}+` : label + keySymbol("key.joiner");
|
|
101
|
+
}
|
|
102
|
+
const lower = base.toLowerCase();
|
|
103
|
+
const modifier = MODIFIER_SYMBOLS[lower];
|
|
104
|
+
const symbol = KEY_SYMBOLS[lower];
|
|
105
|
+
if (modifier) out += keySymbol(modifier[mac ? 1 : 0]);
|
|
106
|
+
else if (symbol !== undefined) out += keySymbol(symbol);
|
|
107
|
+
else if (KEY_WORDS[lower] !== undefined) out += KEY_WORDS[lower];
|
|
108
|
+
else if (base.length === 1) out += parts.length === 0 ? base : base.toUpperCase();
|
|
109
|
+
else out += base[0]!.toUpperCase() + base.slice(1);
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Format alternative keys as a slash-separated hint (`["f5", "alt+r"]` → `F5/⌥R`). */
|
|
114
|
+
export function formatKeyHints(keys: KeyName | readonly KeyName[]): string {
|
|
115
|
+
return typeof keys === "string" ? formatKeyHint(keys) : keys.map(formatKeyHint).join("/");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Format a double-tap gesture: `←←` with glyphs, `Left Left` with words. */
|
|
119
|
+
export function formatDoubleTap(key: KeyName): string {
|
|
120
|
+
const label = formatKeyHint(key);
|
|
121
|
+
return `${label}${HAS_LETTER.test(label) ? " " : keySymbol("key.joiner")}${label}`;
|
|
122
|
+
}
|
package/src/keys.ts
CHANGED
|
@@ -177,7 +177,8 @@ type SpecialKey =
|
|
|
177
177
|
| "f12";
|
|
178
178
|
|
|
179
179
|
type BaseKey = Letter | Digit | KeySymbol | SpecialKey;
|
|
180
|
-
|
|
180
|
+
/** Modifier key names as they appear in {@link KeyId} chords. */
|
|
181
|
+
export type ModifierName = "ctrl" | "shift" | "alt" | "super";
|
|
181
182
|
|
|
182
183
|
type ModifiedKeyId<Key extends string, RemainingModifiers extends ModifierName = ModifierName> = {
|
|
183
184
|
[M in RemainingModifiers]: `${M}+${Key}` | `${M}+${ModifiedKeyId<Key, Exclude<RemainingModifiers, M>>}`;
|
|
@@ -31,6 +31,8 @@ import {
|
|
|
31
31
|
} from "../index";
|
|
32
32
|
import { getSelectListTheme, theme } from "../theme";
|
|
33
33
|
import { sanitizeDisplayWarnings } from "../render/render-utils";
|
|
34
|
+
import { formatKeyHint } from "../app-keybindings";
|
|
35
|
+
import { editorKey, editorKeys } from "../chrome/keybinding-hints";
|
|
34
36
|
import { HookEditorComponent } from "./hook-editor";
|
|
35
37
|
import { buildBrowserItems, ModelBrowser, type ModelBrowserSource, sortModelItems } from "./model-browser";
|
|
36
38
|
import { bottomBorder, divider, dividerSplit, PanelRows, row, topBorder, topBorderSplit } from "../chrome/overlay-box";
|
|
@@ -514,7 +516,11 @@ export class AdvisorConfigOverlayComponent implements Component {
|
|
|
514
516
|
this.#cb.notify(`Advisor config: ${err instanceof Error ? err.message : String(err)}`);
|
|
515
517
|
});
|
|
516
518
|
list.onCancel = () => this.#cb.close();
|
|
517
|
-
this.#setScreen(
|
|
519
|
+
this.#setScreen(
|
|
520
|
+
"list",
|
|
521
|
+
list,
|
|
522
|
+
`${editorKeys("tui.select.up", "tui.select.down")} move · ${editorKey("tui.select.confirm")} / click select · scroll preview on the right · ${editorKey("tui.select.cancel")} close`,
|
|
523
|
+
);
|
|
518
524
|
}
|
|
519
525
|
|
|
520
526
|
async #onListSelect(value: string): Promise<void> {
|
|
@@ -596,7 +602,11 @@ export class AdvisorConfigOverlayComponent implements Component {
|
|
|
596
602
|
const list = new SelectList(items, Math.max(1, items.length), getSelectListTheme());
|
|
597
603
|
list.onSelect = item => this.#onDetailSelect(index, item.value);
|
|
598
604
|
list.onCancel = () => this.#showList();
|
|
599
|
-
this.#setScreen(
|
|
605
|
+
this.#setScreen(
|
|
606
|
+
"detail",
|
|
607
|
+
list,
|
|
608
|
+
`Editing "${advisor.name}" · ${editorKey("tui.select.confirm")} / click edit field · ${editorKey("tui.select.cancel")} back`,
|
|
609
|
+
);
|
|
600
610
|
}
|
|
601
611
|
|
|
602
612
|
#onDetailSelect(index: number, field: string): void {
|
|
@@ -647,7 +657,11 @@ export class AdvisorConfigOverlayComponent implements Component {
|
|
|
647
657
|
this.#showDetail(index);
|
|
648
658
|
};
|
|
649
659
|
input.onEscape = () => this.#showDetail(index);
|
|
650
|
-
this.#setScreen(
|
|
660
|
+
this.#setScreen(
|
|
661
|
+
"name",
|
|
662
|
+
input,
|
|
663
|
+
`Type a name · ${editorKey("tui.input.submit")} save · ${editorKey("tui.select.cancel")} cancel`,
|
|
664
|
+
);
|
|
651
665
|
}
|
|
652
666
|
|
|
653
667
|
#showModelPicker(index: number): void {
|
|
@@ -680,7 +694,11 @@ export class AdvisorConfigOverlayComponent implements Component {
|
|
|
680
694
|
}
|
|
681
695
|
};
|
|
682
696
|
picker.onCancel = () => this.#showDetail(index);
|
|
683
|
-
this.#setScreen(
|
|
697
|
+
this.#setScreen(
|
|
698
|
+
"model",
|
|
699
|
+
picker,
|
|
700
|
+
`Type to search · ${formatKeyHint("enter")} / click twice picks · ${editorKey("tui.select.cancel")} back`,
|
|
701
|
+
);
|
|
684
702
|
}
|
|
685
703
|
|
|
686
704
|
#showThinkingPicker(index: number, selector: string, efforts: readonly string[]): void {
|
|
@@ -694,7 +712,11 @@ export class AdvisorConfigOverlayComponent implements Component {
|
|
|
694
712
|
this.#showDetail(index);
|
|
695
713
|
};
|
|
696
714
|
list.onCancel = () => this.#showModelPicker(index);
|
|
697
|
-
this.#setScreen(
|
|
715
|
+
this.#setScreen(
|
|
716
|
+
"thinking",
|
|
717
|
+
list,
|
|
718
|
+
`Thinking effort for ${selector} · ${editorKey("tui.select.confirm")} / click pick · ${editorKey("tui.select.cancel")} back`,
|
|
719
|
+
);
|
|
698
720
|
}
|
|
699
721
|
|
|
700
722
|
#showToolsEditor(index: number, selected: Set<string>, cursor: number): void {
|
|
@@ -729,7 +751,7 @@ export class AdvisorConfigOverlayComponent implements Component {
|
|
|
729
751
|
this.#setScreen(
|
|
730
752
|
"tools",
|
|
731
753
|
list,
|
|
732
|
-
"
|
|
754
|
+
`${editorKey("tui.select.confirm")} / click toggle · select Done or ${editorKey("tui.select.cancel")} to apply (empty = no tools; read/grep/glob = default)`,
|
|
733
755
|
);
|
|
734
756
|
}
|
|
735
757
|
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
type AgentActivityRow,
|
|
26
26
|
activityRowsFromProgress,
|
|
27
27
|
} from "./agent-activity";
|
|
28
|
-
import type
|
|
28
|
+
import { formatKeyHint, formatKeyHints, type KeyId } from "../app-keybindings";
|
|
29
29
|
import type { MessageRenderer } from "../chat/extension-types";
|
|
30
30
|
import type { AgentLifecycleLike, IrcBusLike } from "./agent-hub-types";
|
|
31
31
|
import { type AgentRecordLike, type AgentHubRegistry, type AgentStatus, MAIN_AGENT_ID } from "./agent-hub-types";
|
|
@@ -767,7 +767,7 @@ export class AgentHubOverlayComponent<TRecord extends AgentRecordLike = AgentRec
|
|
|
767
767
|
row(
|
|
768
768
|
theme.fg(
|
|
769
769
|
"dim",
|
|
770
|
-
|
|
770
|
+
`1:agents ${formatKeyHints(["j", "k"])}:select ${formatKeyHint("enter")}:transcript ${formatKeyHint("space")}:follow ${formatKeyHint("f")}:filter ${formatKeyHint("s")}:scope /:search ${formatKeyHint("escape")}:close`,
|
|
771
771
|
),
|
|
772
772
|
width,
|
|
773
773
|
),
|
|
@@ -834,15 +834,18 @@ export class AgentHubOverlayComponent<TRecord extends AgentRecordLike = AgentRec
|
|
|
834
834
|
if (showingNarrowDetails) {
|
|
835
835
|
return theme.fg(
|
|
836
836
|
"dim",
|
|
837
|
-
`${filter}1:agents 2:activity
|
|
837
|
+
`${filter}1:agents 2:activity ${formatKeyHint("tab")}:roster ${formatKeyHints(["pageUp", "pageDown"])}:scroll ${formatKeyHint("enter")}:open ${formatKeyHint("t")}:${nextView} ${formatKeyHint("escape")}:roster`,
|
|
838
838
|
);
|
|
839
839
|
}
|
|
840
840
|
if (availableWidth < 96) {
|
|
841
|
-
return theme.fg(
|
|
841
|
+
return theme.fg(
|
|
842
|
+
"dim",
|
|
843
|
+
`${filter}${formatKeyHints(["j", "k"])}:select ${formatKeyHint("enter")}:open ${formatKeyHint("t")}:${nextView} ${formatKeyHint("tab")}:details ${formatKeyHints(["r", "x"])}:manage ${formatKeyHint("escape")}:close`,
|
|
844
|
+
);
|
|
842
845
|
}
|
|
843
846
|
return theme.fg(
|
|
844
847
|
"dim",
|
|
845
|
-
`${filter}1:agents 2:activity j
|
|
848
|
+
`${filter}1:agents 2:activity ${formatKeyHints(["j", "k"])}/wheel:select ${formatKeyHints(["pageUp", "pageDown"])}:details ${formatKeyHint("enter")}/click:open ${formatKeyHint("t")}:${nextView} ${formatKeyHint("r")}:revive ${formatKeyHint("x")}:kill ${formatKeyHint("escape")}:close`,
|
|
846
849
|
);
|
|
847
850
|
}
|
|
848
851
|
|
|
@@ -20,7 +20,8 @@ import { Editor } from "../components/editor";
|
|
|
20
20
|
import { matchesKey } from "../keys";
|
|
21
21
|
import { routeSgrMouseInput } from "../mouse";
|
|
22
22
|
import { formatDuration, formatNumber, logger } from "@oh-my-pi/pi-utils";
|
|
23
|
-
import type
|
|
23
|
+
import { formatKeyHint, formatKeyHints, type KeyId } from "../app-keybindings";
|
|
24
|
+
import { editorKey } from "../chrome/keybinding-hints";
|
|
24
25
|
import type { MessageRenderer } from "../chat/extension-types";
|
|
25
26
|
import type { AgentLifecycleLike } from "./agent-hub-types";
|
|
26
27
|
import type { AgentHubRegistry, AgentStatus } from "./agent-hub-types";
|
|
@@ -627,9 +628,10 @@ export class AgentTranscriptViewer implements Component {
|
|
|
627
628
|
const lines: string[] = [];
|
|
628
629
|
const statsLine = this.#statsLine();
|
|
629
630
|
if (statsLine) lines.push(statsLine);
|
|
630
|
-
const
|
|
631
|
-
|
|
632
|
-
|
|
631
|
+
const keys =
|
|
632
|
+
`${formatKeyHint("escape")}:close ${formatKeyHint(this.#deps.expandKeys[0] ?? "ctrl+o")}:expand ` +
|
|
633
|
+
`${this.#editor ? "empty input → " : ""}${formatKeyHints(["j", "k"])}:scroll ${formatKeyHints(["g", "shift+g"])}:top/bottom`;
|
|
634
|
+
const hint = this.#editor ? `${editorKey("tui.input.submit")}:send ${keys}` : keys;
|
|
633
635
|
lines.push(theme.fg("dim", hint));
|
|
634
636
|
return lines;
|
|
635
637
|
}
|
|
@@ -29,6 +29,8 @@ import type { AgentSource } from "../tools/task";
|
|
|
29
29
|
import { shortenPath } from "../render/render-utils";
|
|
30
30
|
import { getEditorTheme, theme } from "../theme";
|
|
31
31
|
import { matchesAppFollowUp, matchesSelectCancel, matchesSelectDown, matchesSelectUp } from "../keybinding-matchers";
|
|
32
|
+
import { formatKeyHint, formatKeyHints } from "../app-keybindings";
|
|
33
|
+
import { boundKeys, editorKey, editorKeys } from "../chrome/keybinding-hints";
|
|
32
34
|
import {
|
|
33
35
|
buildBrowserItems,
|
|
34
36
|
ModelBrowser,
|
|
@@ -980,7 +982,10 @@ export class AgentsHubComponent implements Component {
|
|
|
980
982
|
const { agent, property } = this.#assigning;
|
|
981
983
|
const what = property === "model" ? "model override" : `${property} model`;
|
|
982
984
|
return truncateToWidth(
|
|
983
|
-
theme.fg(
|
|
985
|
+
theme.fg(
|
|
986
|
+
"accent",
|
|
987
|
+
` Picking ${what} for ${theme.bold(agent.name)} — ${formatKeyHint("enter")} assigns, ${editorKey("tui.select.cancel")} cancels`,
|
|
988
|
+
),
|
|
984
989
|
width,
|
|
985
990
|
);
|
|
986
991
|
}
|
|
@@ -1152,26 +1157,35 @@ export class AgentsHubComponent implements Component {
|
|
|
1152
1157
|
}
|
|
1153
1158
|
|
|
1154
1159
|
#footerHint(): string {
|
|
1160
|
+
const enter = formatKeyHint("enter");
|
|
1161
|
+
const cancel = editorKey("tui.select.cancel");
|
|
1162
|
+
const upDown = editorKeys("tui.select.up", "tui.select.down");
|
|
1155
1163
|
if (this.#strip) {
|
|
1156
1164
|
if (this.#strip.kind === "pattern") {
|
|
1157
1165
|
const property = this.#strip.property;
|
|
1158
1166
|
const values = property === "model" ? "a model pattern" : '"on", "off", or a model pattern';
|
|
1159
|
-
return `Enter ${values} (role aliases like @smol and :level suffixes work; empty clears) ·
|
|
1167
|
+
return `Enter ${values} (role aliases like @smol and :level suffixes work; empty clears) · ${cancel} back`;
|
|
1160
1168
|
}
|
|
1161
|
-
|
|
1169
|
+
const choose = formatKeyHints(["left", "right"]);
|
|
1170
|
+
return this.#strip.property
|
|
1171
|
+
? `${choose} choose · ${enter} apply · ${cancel} back`
|
|
1172
|
+
: `${choose} choose · ${enter} open · ${cancel} cancel`;
|
|
1162
1173
|
}
|
|
1163
1174
|
if (this.#assigning) {
|
|
1164
|
-
return
|
|
1175
|
+
return `${enter} pick · ${upDown} models · type to search · ${cancel} cancel`;
|
|
1165
1176
|
}
|
|
1166
1177
|
if (this.#createActive) {
|
|
1167
|
-
|
|
1178
|
+
const tab = formatKeyHint("tab");
|
|
1179
|
+
if (this.#createSpec)
|
|
1180
|
+
return `${enter} save · ${tab} scope · ${formatKeyHint("r")} regenerate · ${cancel} cancel`;
|
|
1168
1181
|
if (this.#createGenerating) return "Generating…";
|
|
1169
|
-
|
|
1182
|
+
const generate = formatKeyHints(boundKeys("app.message.followUp", ["ctrl+q", "ctrl+enter"]));
|
|
1183
|
+
return `${generate} generate · ${enter} newline · ${tab} scope · ${cancel} cancel`;
|
|
1170
1184
|
}
|
|
1171
1185
|
if (this.#focus === "scope") {
|
|
1172
|
-
return
|
|
1186
|
+
return `${upDown} scopes · ${formatKeyHints(["right", "enter"])} agents · ${cancel} close`;
|
|
1173
1187
|
}
|
|
1174
|
-
return
|
|
1188
|
+
return `${enter} configure · ${formatKeyHint("space")} enable/disable · ${upDown} rows · type to search · ${formatKeyHint("ctrl+r")} reload · ${cancel} close`;
|
|
1175
1189
|
}
|
|
1176
1190
|
|
|
1177
1191
|
#renderFooter(width: number): string {
|
|
@@ -11,7 +11,9 @@ import {
|
|
|
11
11
|
visibleWidth,
|
|
12
12
|
wrapTextWithAnsi,
|
|
13
13
|
} from "../index";
|
|
14
|
-
import {
|
|
14
|
+
import { appKey, editorKey } from "../chrome/keybinding-hints";
|
|
15
|
+
import { formatKeyHint, formatKeyHints, type KeybindingsManager } from "../app-keybindings";
|
|
16
|
+
import type { Keybinding } from "../keybindings";
|
|
15
17
|
import { sanitizeText } from "@oh-my-pi/pi-utils";
|
|
16
18
|
import { type Theme } from "../theme/theme";
|
|
17
19
|
import type {
|
|
@@ -157,7 +159,6 @@ export class AnnotationOverlay implements Component {
|
|
|
157
159
|
#textViewportDriven = false;
|
|
158
160
|
#textRenderedRowBySource: readonly number[] = [];
|
|
159
161
|
#staticRenderedDiffBodies = new WeakMap<ReviewDiffFile, RenderedDiffBody>();
|
|
160
|
-
#externalEditorLabel: string;
|
|
161
162
|
|
|
162
163
|
readonly #tui: TUI;
|
|
163
164
|
readonly #theme: Theme;
|
|
@@ -253,7 +254,6 @@ export class AnnotationOverlay implements Component {
|
|
|
253
254
|
this.#editor.cursorOverride = " ";
|
|
254
255
|
this.#editor.setScrollbarVisible(true);
|
|
255
256
|
this.#editor.onSubmit = value => this.#commitAnnotation(value);
|
|
256
|
-
this.#externalEditorLabel = keybindings.getDisplayString("app.editor.external");
|
|
257
257
|
this.#resetSourceCursor();
|
|
258
258
|
}
|
|
259
259
|
|
|
@@ -328,6 +328,12 @@ export class AnnotationOverlay implements Component {
|
|
|
328
328
|
else this.#handleActions(data);
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
+
/** Primary key bound to `action` in this overlay's keybindings, formatted for footer hints. */
|
|
332
|
+
#key(action: Keybinding): string {
|
|
333
|
+
const [key] = this.#keybindings.getKeys(action);
|
|
334
|
+
return key ? formatKeyHint(key) : "";
|
|
335
|
+
}
|
|
336
|
+
|
|
331
337
|
#finish(result: CodeReviewOverlayResult | TextReviewOverlayResult | undefined): void {
|
|
332
338
|
if (this.#finished) return;
|
|
333
339
|
this.#finished = true;
|
|
@@ -962,14 +968,11 @@ export class AnnotationOverlay implements Component {
|
|
|
962
968
|
const options = chooser.entries
|
|
963
969
|
.slice(start, start + optionLimit)
|
|
964
970
|
.map((index, windowIndex) => this.#renderAnnotationChooserEntry(chooser, index, start + windowIndex, width));
|
|
971
|
+
const keysHint = `${this.#key("tui.select.up")}/${this.#key("tui.select.down")} choose · ${this.#key("tui.select.confirm")} edit · ${this.#key("tui.select.cancel")} cancel`;
|
|
965
972
|
const lines =
|
|
966
973
|
availableRows === 2
|
|
967
|
-
? [this.#theme.fg("dim",
|
|
968
|
-
: [
|
|
969
|
-
this.#theme.fg("dim", "Edit annotation · ↑↓ choose · enter edit · esc cancel"),
|
|
970
|
-
...options,
|
|
971
|
-
this.#theme.fg("dim", "↑↓ choose · enter edit · esc cancel"),
|
|
972
|
-
];
|
|
974
|
+
? [this.#theme.fg("dim", `Edit annotation · ${keysHint}`), ...options.slice(0, 1)]
|
|
975
|
+
: [this.#theme.fg("dim", `Edit annotation · ${keysHint}`), ...options, this.#theme.fg("dim", keysHint)];
|
|
973
976
|
return Number.isFinite(availableRows) ? lines.slice(0, availableRows) : lines;
|
|
974
977
|
}
|
|
975
978
|
|
|
@@ -1034,20 +1037,31 @@ export class AnnotationOverlay implements Component {
|
|
|
1034
1037
|
width,
|
|
1035
1038
|
Ellipsis.Unicode,
|
|
1036
1039
|
);
|
|
1037
|
-
const hints = [
|
|
1038
|
-
|
|
1040
|
+
const hints = [
|
|
1041
|
+
`${editorKey("tui.input.submit")} save`,
|
|
1042
|
+
`${editorKey("tui.input.newLine")} newline`,
|
|
1043
|
+
`${this.#key("tui.select.cancel")} cancel`,
|
|
1044
|
+
];
|
|
1045
|
+
const externalEditorKey = appKey(this.#keybindings, "app.editor.external");
|
|
1046
|
+
if (externalEditorKey) hints.push(`${externalEditorKey} editor`);
|
|
1039
1047
|
this.#editor.focused = true;
|
|
1040
1048
|
return [caption, ...this.#editor.render(width), this.#theme.fg("dim", hints.join(" · "))];
|
|
1041
1049
|
}
|
|
1050
|
+
const upDown = `${this.#key("tui.select.up")}/${this.#key("tui.select.down")}`;
|
|
1051
|
+
const confirm = this.#key("tui.select.confirm");
|
|
1052
|
+
const editNote = `${formatKeyHint("e")} edit note`;
|
|
1042
1053
|
const focusHelp =
|
|
1043
1054
|
this.#focus === "files"
|
|
1044
|
-
?
|
|
1055
|
+
? `${upDown} file · ${confirm} diff · ${formatKeyHints(["a", "shift+a"])} file note · ${editNote}`
|
|
1045
1056
|
: this.#focus === "diff"
|
|
1046
|
-
? this.#textSource
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1057
|
+
? `${upDown} line · ${formatKeyHint("shift")} faster · ${this.#key("tui.select.pageUp")}/${this.#key("tui.select.pageDown")} · ${formatKeyHints(["g", "shift+g"])} ends · ${formatKeyHint("a")} line note · ${formatKeyHint("shift+a")} ${this.#textSource ? "text" : "file"} note · ${editNote}`
|
|
1058
|
+
: `${upDown} select · ${confirm} confirm`;
|
|
1059
|
+
return [
|
|
1060
|
+
this.#theme.fg(
|
|
1061
|
+
"dim",
|
|
1062
|
+
`${focusHelp} · ${formatKeyHint("u")} undo · ${formatKeyHint("tab")} regions · ${this.#key("tui.select.cancel")} cancel`,
|
|
1063
|
+
),
|
|
1064
|
+
];
|
|
1051
1065
|
}
|
|
1052
1066
|
|
|
1053
1067
|
#ensureCursorVisible(renderedRowBySource: readonly number[]): void {
|
|
@@ -71,7 +71,8 @@ import {
|
|
|
71
71
|
} from "../keybinding-matchers";
|
|
72
72
|
import { optionMarker } from "../tools/ask";
|
|
73
73
|
import { CountdownTimer } from "../chrome/countdown-timer";
|
|
74
|
-
import { editorKey } from "../chrome/keybinding-hints";
|
|
74
|
+
import { editorKey, editorKeys } from "../chrome/keybinding-hints";
|
|
75
|
+
import { formatKeyHint, formatKeyHints } from "../app-keybindings";
|
|
75
76
|
import { OverlayPanel, PanelDivider, PanelRows } from "../chrome/overlay-box";
|
|
76
77
|
import { handleTabSwitchKey } from "../chrome/selector-helpers";
|
|
77
78
|
|
|
@@ -309,17 +310,6 @@ function renderCachedPreview(cache: PreviewRenderCache, preview: string, width:
|
|
|
309
310
|
return rendered;
|
|
310
311
|
}
|
|
311
312
|
|
|
312
|
-
function pageKeysLabel(): string {
|
|
313
|
-
const pageUp = editorKey("tui.select.pageUp");
|
|
314
|
-
const pageDown = editorKey("tui.select.pageDown");
|
|
315
|
-
return `${pageUp === "pageup" ? "PgUp" : pageUp}/${pageDown === "pagedown" ? "PgDn" : pageDown}`;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
function cancelKeyLabel(): string {
|
|
319
|
-
const [key = ""] = editorKey("tui.select.cancel").split("/");
|
|
320
|
-
return key === "escape" ? "Esc" : key;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
313
|
function normalizedInlineInput(input: string): string {
|
|
324
314
|
return replaceTabs(input).replace(/\s+/g, " ").trim();
|
|
325
315
|
}
|
|
@@ -767,24 +757,29 @@ export class AskDialogComponent implements Component {
|
|
|
767
757
|
}
|
|
768
758
|
|
|
769
759
|
#footerHintText(indicator: string): string {
|
|
770
|
-
const cancel = `${
|
|
760
|
+
const cancel = `${editorKey("tui.select.cancel")} cancel`;
|
|
761
|
+
const enter = formatKeyHint("enter");
|
|
762
|
+
const upDown = editorKeys("tui.select.up", "tui.select.down");
|
|
771
763
|
const inputGuard = this.options.inputGuard;
|
|
772
764
|
if (inputGuard?.isBlocked()) return `${inputGuard.hint}${this.#expandHint()} · ${cancel}`;
|
|
773
765
|
if (this.#isSubmitTab()) {
|
|
774
766
|
const scroll = indicator ? ` ${indicator} scroll ·` : "";
|
|
775
|
-
return
|
|
767
|
+
return `${enter} submit · ${upDown} scroll ·${scroll} ${cancel}`;
|
|
776
768
|
}
|
|
777
769
|
const question = this.#questions[this.#currentQuestionIndex()];
|
|
778
770
|
// Enter advances in multi-question dialogs and submits single-question ones.
|
|
779
771
|
const enterAction = this.#questions.length > 1 ? "next" : "submit";
|
|
780
|
-
const action = question?.multi
|
|
781
|
-
|
|
772
|
+
const action = question?.multi
|
|
773
|
+
? `${formatKeyHint("space")} toggle · ${enter} ${enterAction}`
|
|
774
|
+
: `${enter} select · ${formatKeyHint("n")} note`;
|
|
775
|
+
const tabs = this.#hasSubmitTab() ? ` · ${formatKeyHints(["tab", "left", "right"])}` : "";
|
|
782
776
|
const expand = this.#expandHint();
|
|
783
777
|
if (this.#questionCanPage && indicator) {
|
|
784
|
-
|
|
778
|
+
const pageKeys = editorKeys("tui.select.pageUp", "tui.select.pageDown");
|
|
779
|
+
return `${action} · ${upDown}${tabs} · ${cancel}${expand} · ${pageKeys} ${indicator}`;
|
|
785
780
|
}
|
|
786
781
|
const scroll = indicator ? ` ${indicator} scroll ·` : "";
|
|
787
|
-
return `${action} ·
|
|
782
|
+
return `${action} · ${upDown} move${tabs} ·${scroll} ${cancel}${expand}`;
|
|
788
783
|
}
|
|
789
784
|
|
|
790
785
|
#questionRows(question: ExtensionAskDialogQuestion): QuestionRow[] {
|
|
@@ -1051,7 +1046,7 @@ export class AskDialogComponent implements Component {
|
|
|
1051
1046
|
allLines.push(
|
|
1052
1047
|
theme.fg(
|
|
1053
1048
|
"warning",
|
|
1054
|
-
`${unanswered} unanswered question${unanswered === 1 ? "" : "s"};
|
|
1049
|
+
`${unanswered} unanswered question${unanswered === 1 ? "" : "s"}; ${formatKeyHint("enter")} still submits.`,
|
|
1055
1050
|
),
|
|
1056
1051
|
);
|
|
1057
1052
|
allLines.push("");
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CancellableLoader, Container, Spacer, Text, type TUI } from "../index";
|
|
2
2
|
import type { Theme } from "../theme/theme";
|
|
3
3
|
import { DynamicBorder } from "../chrome/dynamic-border";
|
|
4
|
+
import { editorKey } from "../chrome/keybinding-hints";
|
|
4
5
|
|
|
5
6
|
/** Loader wrapped with borders for hook UI */
|
|
6
7
|
export class BorderedLoader extends Container {
|
|
@@ -18,7 +19,7 @@ export class BorderedLoader extends Container {
|
|
|
18
19
|
);
|
|
19
20
|
this.addChild(this.#loader);
|
|
20
21
|
this.addChild(new Spacer(1));
|
|
21
|
-
this.addChild(new Text(theme.fg("muted", "
|
|
22
|
+
this.addChild(new Text(theme.fg("muted", `${editorKey("tui.select.cancel")} cancel`), 1, 0));
|
|
22
23
|
this.addChild(new Spacer(1));
|
|
23
24
|
this.addChild(new DynamicBorder(borderColor));
|
|
24
25
|
}
|
|
@@ -28,7 +28,8 @@ import {
|
|
|
28
28
|
import type { SpaceHoldHandler } from "../space-hold";
|
|
29
29
|
import { sanitizeErrorLine } from "../chrome/error-block";
|
|
30
30
|
import { sanitizeDisplayLine, sanitizeDisplayText } from "./extensions/display-text";
|
|
31
|
-
import { editorKey, rawKeyHint } from "../chrome/keybinding-hints";
|
|
31
|
+
import { editorKey, editorKeys, keyHint, rawKeyHint } from "../chrome/keybinding-hints";
|
|
32
|
+
import { formatKeyHint } from "../app-keybindings";
|
|
32
33
|
import { bottomBorder, row, topBorder } from "../chrome/overlay-box";
|
|
33
34
|
import { padToWidth } from "../render/utils";
|
|
34
35
|
import { SplitPane } from "../components/layout/split-pane";
|
|
@@ -267,11 +268,11 @@ export class BtwHistoryPanel implements Component, Focusable {
|
|
|
267
268
|
this.#focus = "answer";
|
|
268
269
|
this.#followLatest = true;
|
|
269
270
|
} else {
|
|
270
|
-
composer.notice =
|
|
271
|
+
composer.notice = `Follow-up was not started. Your draft is kept; ${editorKey("tui.input.submit")} to retry.`;
|
|
271
272
|
}
|
|
272
273
|
} catch {
|
|
273
274
|
if (this.#composer === composer) {
|
|
274
|
-
composer.notice =
|
|
275
|
+
composer.notice = `Could not start the follow-up. Your draft is kept; ${editorKey("tui.input.submit")} to retry.`;
|
|
275
276
|
}
|
|
276
277
|
} finally {
|
|
277
278
|
this.#followUpPending = false;
|
|
@@ -507,15 +508,19 @@ export class BtwHistoryPanel implements Component, Focusable {
|
|
|
507
508
|
const composer = this.#composer;
|
|
508
509
|
const latest = record ? getBtwLatestTurn(record) : undefined;
|
|
509
510
|
const actions = composer
|
|
510
|
-
? [
|
|
511
|
+
? [
|
|
512
|
+
keyHint("tui.input.submit", this.#followUpPending ? "starting…" : "send"),
|
|
513
|
+
keyHint("tui.select.cancel", "cancel"),
|
|
514
|
+
]
|
|
511
515
|
: [
|
|
512
|
-
|
|
513
|
-
rawKeyHint("
|
|
516
|
+
keyHint("tui.select.cancel", latest?.status === "running" ? "cancel" : "close"),
|
|
517
|
+
rawKeyHint(["tab", "ctrl+/"], "switch pane"),
|
|
514
518
|
];
|
|
515
519
|
if (!composer) {
|
|
516
|
-
if (record && this.#canFollowUp(record)) actions.push(rawKeyHint("f
|
|
520
|
+
if (record && this.#canFollowUp(record)) actions.push(rawKeyHint(["f", "enter"], "follow up"));
|
|
517
521
|
if (record && getBtwCopyText(record) !== undefined) {
|
|
518
|
-
if (this.#isCopied(record))
|
|
522
|
+
if (this.#isCopied(record))
|
|
523
|
+
actions.push(theme.fg("success", `✓ copied · ${formatKeyHint("c")} to copy again`));
|
|
519
524
|
else actions.push(rawKeyHint("c", inner < 40 ? "copy" : "copy answer"));
|
|
520
525
|
}
|
|
521
526
|
}
|
|
@@ -577,10 +582,8 @@ export class BtwHistoryPanel implements Component, Focusable {
|
|
|
577
582
|
if (showNavigation) {
|
|
578
583
|
lines.push(
|
|
579
584
|
row(
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
this.#focus === "list" ? "select" : "scroll",
|
|
583
|
-
),
|
|
585
|
+
theme.fg("dim", editorKeys("tui.select.up", "tui.select.down")) +
|
|
586
|
+
theme.fg("muted", ` ${this.#focus === "list" ? "select" : "scroll"}`),
|
|
584
587
|
width,
|
|
585
588
|
),
|
|
586
589
|
);
|
|
@@ -4,6 +4,8 @@ import { getMarkdownTheme, theme } from "../theme/theme";
|
|
|
4
4
|
import { sanitizeErrorLine } from "../chrome/error-block";
|
|
5
5
|
import { OverlayPanel } from "../chrome/overlay-box";
|
|
6
6
|
import { StreamingPanelContent } from "../chrome/streaming-panel";
|
|
7
|
+
import { interruptKey } from "../chrome/keybinding-hints";
|
|
8
|
+
import { formatKeyHint } from "../app-keybindings";
|
|
7
9
|
|
|
8
10
|
type BtwPanelState = "running" | "complete" | "branching" | "aborted" | "error";
|
|
9
11
|
|
|
@@ -129,15 +131,18 @@ export class BtwPanelComponent extends OverlayPanel {
|
|
|
129
131
|
}
|
|
130
132
|
|
|
131
133
|
#footerLine(): string {
|
|
134
|
+
// The main editor routes `app.interrupt` (Escape by default) to the panel.
|
|
135
|
+
const esc = interruptKey();
|
|
132
136
|
switch (this.#state) {
|
|
133
137
|
case "running":
|
|
134
|
-
return theme.fg("muted",
|
|
138
|
+
return theme.fg("muted", `${esc} to cancel`);
|
|
135
139
|
case "complete": {
|
|
136
140
|
const actions: string[] = [];
|
|
137
|
-
|
|
138
|
-
if (this
|
|
139
|
-
if (this.#
|
|
140
|
-
actions.push("
|
|
141
|
+
const copyKey = formatKeyHint("c");
|
|
142
|
+
if (this.isCopyable()) actions.push(this.#copied ? `${copyKey} to copy again` : `${copyKey} to copy`);
|
|
143
|
+
if (this.#canFollowUp?.()) actions.push(`${formatKeyHint("f")} to follow up`);
|
|
144
|
+
if (this.#canBranch?.() ?? this.isBranchable()) actions.push(`${formatKeyHint("b")} to branch`);
|
|
145
|
+
actions.push(`${esc} to close`);
|
|
141
146
|
if (this.#copied) {
|
|
142
147
|
return `${theme.fg("success", "✓ Copied to clipboard")}${theme.fg("muted", actions.length > 0 ? ` · ${actions.join(" · ")}` : "")}`;
|
|
143
148
|
}
|
|
@@ -146,9 +151,9 @@ export class BtwPanelComponent extends OverlayPanel {
|
|
|
146
151
|
case "branching":
|
|
147
152
|
return theme.fg("muted", `${theme.status.pending} Branching to chat…`);
|
|
148
153
|
case "aborted":
|
|
149
|
-
return theme.fg("warning", `${theme.status.warning} Cancelled ·
|
|
154
|
+
return theme.fg("warning", `${theme.status.warning} Cancelled · ${esc} to close`);
|
|
150
155
|
case "error":
|
|
151
|
-
return theme.fg("error", `${theme.status.error} Error ·
|
|
156
|
+
return theme.fg("error", `${theme.status.error} Error · ${esc} to close`);
|
|
152
157
|
}
|
|
153
158
|
}
|
|
154
159
|
|