@oh-my-pi/pi-tui 18.3.1 → 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/overlays/rewind-selector.d.ts +4 -2
- 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/status-line/host.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 +66 -27
- 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 +193 -13
- 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/status-line/host.ts +1 -1
- package/src/status-line/segments.ts +2 -2
- 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
|
@@ -19,7 +19,8 @@ import { MODEL_KINDS, modelKind, type ModelKind } from "@oh-my-pi/pi-catalog/typ
|
|
|
19
19
|
import type { Component, TUI } from "../tui";
|
|
20
20
|
import { extractPrintableText, matchesKey } from "../keys";
|
|
21
21
|
import { fuzzyFilter } from "../fuzzy";
|
|
22
|
-
import {
|
|
22
|
+
import { formatKeyHint, formatKeyHints } from "../app-keybindings";
|
|
23
|
+
import { editorKey, editorKeys } from "../chrome/keybinding-hints";
|
|
23
24
|
import { Input } from "../components/input";
|
|
24
25
|
import { routeSgrMouseInput, type SgrMouseEvent } from "../mouse";
|
|
25
26
|
import { truncateToWidth, visibleWidth } from "../utils";
|
|
@@ -1630,6 +1631,13 @@ export class ModelHubComponent implements Component {
|
|
|
1630
1631
|
return;
|
|
1631
1632
|
}
|
|
1632
1633
|
|
|
1634
|
+
// Enter on the sidebar is a pane switch, like →: it lands on the model
|
|
1635
|
+
// rows instead of acting on a row the user cannot see is selected.
|
|
1636
|
+
if (this.#focus === "scope" && (matchesKey(data, "enter") || matchesKey(data, "return") || data === "\n")) {
|
|
1637
|
+
this.#focus = "list";
|
|
1638
|
+
return;
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1633
1641
|
const beforeQuery = this.#browser.query;
|
|
1634
1642
|
const isPrintable = extractPrintableText(data) !== undefined;
|
|
1635
1643
|
this.#browser.handleInput(data);
|
|
@@ -2006,7 +2014,10 @@ export class ModelHubComponent implements Component {
|
|
|
2006
2014
|
MODEL_KIND_TABS.map(kind => ({ label: kind })),
|
|
2007
2015
|
Math.max(0, active),
|
|
2008
2016
|
);
|
|
2009
|
-
return truncateToWidth(
|
|
2017
|
+
return truncateToWidth(
|
|
2018
|
+
` ${theme.fg("dim", "Kind:")} ${track} ${theme.fg("dim", formatKeyHints(["alt+left", "alt+right"]))}`,
|
|
2019
|
+
width,
|
|
2020
|
+
);
|
|
2010
2021
|
}
|
|
2011
2022
|
|
|
2012
2023
|
#renderRoleTabs(width: number): string {
|
|
@@ -2015,7 +2026,10 @@ export class ModelHubComponent implements Component {
|
|
|
2015
2026
|
ROLE_TABS.map(tab => ({ label: tab === "kind" ? "kinds" : tab })),
|
|
2016
2027
|
Math.max(0, active),
|
|
2017
2028
|
);
|
|
2018
|
-
return truncateToWidth(
|
|
2029
|
+
return truncateToWidth(
|
|
2030
|
+
` ${theme.fg("dim", "Roles:")} ${track} ${theme.fg("dim", formatKeyHints(["alt+left", "alt+right"]))}`,
|
|
2031
|
+
width,
|
|
2032
|
+
);
|
|
2019
2033
|
}
|
|
2020
2034
|
|
|
2021
2035
|
#statusRow(width: number): string {
|
|
@@ -2023,9 +2037,11 @@ export class ModelHubComponent implements Component {
|
|
|
2023
2037
|
return truncateToWidth(theme.fg("accent", " Applying model…"), width);
|
|
2024
2038
|
}
|
|
2025
2039
|
if (this.#assigning !== null) {
|
|
2040
|
+
const enter = formatKeyHint("enter");
|
|
2041
|
+
const cancel = editorKey("tui.select.cancel");
|
|
2026
2042
|
if (this.#assigning.kind === "fallbackKey") {
|
|
2027
2043
|
return truncateToWidth(
|
|
2028
|
-
theme.fg("accent",
|
|
2044
|
+
theme.fg("accent", ` New fallback chain — ${enter} picks the model it protects, ${cancel} cancels`),
|
|
2029
2045
|
width,
|
|
2030
2046
|
);
|
|
2031
2047
|
}
|
|
@@ -2034,12 +2050,15 @@ export class ModelHubComponent implements Component {
|
|
|
2034
2050
|
if (this.#assigning.kind === "fallback") {
|
|
2035
2051
|
const verb = this.#assigning.index === null ? "Adding fallback for" : "Replacing fallback of";
|
|
2036
2052
|
return truncateToWidth(
|
|
2037
|
-
theme.fg(
|
|
2053
|
+
theme.fg(
|
|
2054
|
+
"accent",
|
|
2055
|
+
` ${verb} ${theme.bold(label)} — ${enter} picks the fallback model, ${cancel} cancels`,
|
|
2056
|
+
),
|
|
2038
2057
|
width,
|
|
2039
2058
|
);
|
|
2040
2059
|
}
|
|
2041
2060
|
return truncateToWidth(
|
|
2042
|
-
theme.fg("accent", ` Assigning ${theme.bold(label)} —
|
|
2061
|
+
theme.fg("accent", ` Assigning ${theme.bold(label)} — ${enter} assigns, ${cancel} cancels`),
|
|
2043
2062
|
width,
|
|
2044
2063
|
);
|
|
2045
2064
|
}
|
|
@@ -2051,7 +2070,7 @@ export class ModelHubComponent implements Component {
|
|
|
2051
2070
|
text = `Recently used models${scopedSuffix}`;
|
|
2052
2071
|
break;
|
|
2053
2072
|
case "roles":
|
|
2054
|
-
text =
|
|
2073
|
+
text = `Model roles — ${formatKeyHint("f")} adds a retry fallback, cleared roles fall back to auto-selection`;
|
|
2055
2074
|
break;
|
|
2056
2075
|
case "provider":
|
|
2057
2076
|
if (entry.locked) {
|
|
@@ -2206,7 +2225,7 @@ export class ModelHubComponent implements Component {
|
|
|
2206
2225
|
// segment track the ctrl+p status uses; the selected role's chip fills.
|
|
2207
2226
|
while (lines.length < rows - 1) lines.push("");
|
|
2208
2227
|
if (rows >= 2) {
|
|
2209
|
-
const cycleKey =
|
|
2228
|
+
const cycleKey = editorKey("app.model.cycleForward") || formatKeyHint("ctrl+p");
|
|
2210
2229
|
if (cycleOrder.length > 0) {
|
|
2211
2230
|
const selectedRow = this.#rolesRows[this.#roleIndex];
|
|
2212
2231
|
const selectedRole =
|
|
@@ -2219,7 +2238,7 @@ export class ModelHubComponent implements Component {
|
|
|
2219
2238
|
lines[rows - 1] = truncateToWidth(` ${theme.fg("dim", `${cycleKey} cycle:`)} ${track}`, width);
|
|
2220
2239
|
} else {
|
|
2221
2240
|
lines[rows - 1] = truncateToWidth(
|
|
2222
|
-
theme.fg("dim", ` ${cycleKey} cycle is empty — press c on a role to add it`),
|
|
2241
|
+
theme.fg("dim", ` ${cycleKey} cycle is empty — press ${formatKeyHint("c")} on a role to add it`),
|
|
2223
2242
|
width,
|
|
2224
2243
|
);
|
|
2225
2244
|
}
|
|
@@ -2246,7 +2265,12 @@ export class ModelHubComponent implements Component {
|
|
|
2246
2265
|
}
|
|
2247
2266
|
if (entry.oauth) {
|
|
2248
2267
|
this.#lockedLoginLine = lines.length + 1; // +1 for the status row offset handled by caller
|
|
2249
|
-
lines.push(
|
|
2268
|
+
lines.push(
|
|
2269
|
+
truncateToWidth(
|
|
2270
|
+
theme.fg("accent", ` ${theme.nav.cursor} Log in with OAuth (${formatKeyHint("enter")})`),
|
|
2271
|
+
width,
|
|
2272
|
+
),
|
|
2273
|
+
);
|
|
2250
2274
|
}
|
|
2251
2275
|
lines.push("");
|
|
2252
2276
|
const catalogCount = entry.catalogCount ?? 0;
|
|
@@ -2264,29 +2288,40 @@ export class ModelHubComponent implements Component {
|
|
|
2264
2288
|
}
|
|
2265
2289
|
|
|
2266
2290
|
#footerHint(): string {
|
|
2291
|
+
const enter = formatKeyHint("enter");
|
|
2292
|
+
const cancel = editorKey("tui.select.cancel");
|
|
2293
|
+
const upDown = editorKeys("tui.select.up", "tui.select.down");
|
|
2294
|
+
const left = formatKeyHint("left");
|
|
2295
|
+
const leftRight = formatKeyHints(["left", "right"]);
|
|
2296
|
+
const enterRight = formatKeyHints(["enter", "right"]);
|
|
2297
|
+
const altLeftRight = formatKeyHints(["alt+left", "alt+right"]);
|
|
2267
2298
|
const strip = this.#strip;
|
|
2268
2299
|
if (strip) {
|
|
2269
2300
|
if (strip.kind === "roleName") {
|
|
2270
|
-
return
|
|
2301
|
+
return `${enter} create + pick model · ${cancel} cancel`;
|
|
2271
2302
|
}
|
|
2272
|
-
if (strip.kind === "role") return
|
|
2273
|
-
if (strip.kind === "scope") return
|
|
2274
|
-
return
|
|
2303
|
+
if (strip.kind === "role") return `${leftRight} choose · ${enter} assign/clear · ${cancel} cancel`;
|
|
2304
|
+
if (strip.kind === "scope") return `${leftRight} save scope · ${enter} choose · ${cancel} cancel`;
|
|
2305
|
+
return `${leftRight} thinking level · ${enter} apply · ${cancel} keep`;
|
|
2275
2306
|
}
|
|
2276
2307
|
if (this.#assigning !== null) {
|
|
2308
|
+
if (this.#focus === "scope") {
|
|
2309
|
+
return `${enterRight} models · ${upDown} providers · type to search · ${altLeftRight} kind · ${cancel} cancel`;
|
|
2310
|
+
}
|
|
2311
|
+
const browse = `${upDown} models · ${left} providers · type to search · ${altLeftRight} kind · ${cancel} cancel`;
|
|
2277
2312
|
switch (this.#assigning.kind) {
|
|
2278
2313
|
case "fallback":
|
|
2279
|
-
return
|
|
2314
|
+
return `${enter} pick fallback · ${browse}`;
|
|
2280
2315
|
case "fallbackKey":
|
|
2281
|
-
return
|
|
2316
|
+
return `${enter} pick the protected model · ${browse}`;
|
|
2282
2317
|
default:
|
|
2283
|
-
return
|
|
2318
|
+
return `${enter} assign · ${browse}`;
|
|
2284
2319
|
}
|
|
2285
2320
|
}
|
|
2286
2321
|
const entry = this.#activeEntry();
|
|
2287
2322
|
if (entry.kind === "roles") {
|
|
2288
2323
|
if (this.#focus !== "list") {
|
|
2289
|
-
return
|
|
2324
|
+
return `${upDown} providers · ${enterRight} roles · ${altLeftRight} tabs · ${cancel} close`;
|
|
2290
2325
|
}
|
|
2291
2326
|
const row = this.#rolesRows[this.#roleIndex];
|
|
2292
2327
|
if (row?.kind === "fallback") {
|
|
@@ -2294,23 +2329,27 @@ export class ModelHubComponent implements Component {
|
|
|
2294
2329
|
// inherit and unknown models have no ladder to offer, so the
|
|
2295
2330
|
// action would be inert there.
|
|
2296
2331
|
const editable = this.#resolveFallbackEntry(row.role, row.chainIndex) !== undefined;
|
|
2297
|
-
const thinking = editable ?
|
|
2298
|
-
return
|
|
2332
|
+
const thinking = editable ? ` · ${formatKeyHint("t")} thinking` : "";
|
|
2333
|
+
return `${upDown} rows · ${enter} replace · ${formatKeyHint("f")} add another · ${formatKeyHint("x")} remove${thinking} · [/] reorder · ${left} providers`;
|
|
2299
2334
|
}
|
|
2300
2335
|
if (row?.kind === "chainKey") {
|
|
2301
|
-
return
|
|
2336
|
+
return `${upDown} rows · ${formatKeyHints(["enter", "f"])} add fallback · ${formatKeyHint("x")} clear chain · ${left} providers`;
|
|
2302
2337
|
}
|
|
2303
2338
|
if (row?.kind === "newFallback") {
|
|
2304
|
-
return
|
|
2339
|
+
return `${upDown} rows · ${enter} new model/provider fallback chain · ${left} providers`;
|
|
2305
2340
|
}
|
|
2306
|
-
return
|
|
2341
|
+
return `${upDown} rows · ${enter} pick · ${formatKeyHint("f")} fallback · ${formatKeyHint("x")} clear · ${formatKeyHint("t")} thinking · ${formatKeyHint("c")} cycle · [/] reorder · ${formatKeyHint("n")} new`;
|
|
2307
2342
|
}
|
|
2308
2343
|
if (entry.kind === "provider" && entry.locked) {
|
|
2309
|
-
return entry.oauth
|
|
2344
|
+
return entry.oauth
|
|
2345
|
+
? `${enter} log in · ${upDown} providers · ${cancel} close`
|
|
2346
|
+
: `${upDown} providers · ${cancel} close`;
|
|
2347
|
+
}
|
|
2348
|
+
const refresh = entry.kind === "provider" ? ` · ${formatKeyHint("f5")} refresh` : "";
|
|
2349
|
+
if (this.#focus === "scope") {
|
|
2350
|
+
return `${enterRight} models · ${upDown} providers · type to search · ${altLeftRight} kind${refresh} · ${cancel} close`;
|
|
2310
2351
|
}
|
|
2311
|
-
|
|
2312
|
-
const refresh = entry.kind === "provider" ? " · F5 refresh" : "";
|
|
2313
|
-
return `Enter assign roles · ${arrows} · type to search · Alt+←/→ kind${refresh} · Esc close`;
|
|
2352
|
+
return `${enter} assign roles · ${upDown} models · ${left} providers · type to search · ${altLeftRight} kind${refresh} · ${cancel} close`;
|
|
2314
2353
|
}
|
|
2315
2354
|
|
|
2316
2355
|
#renderFooter(width: number): string {
|
|
@@ -15,6 +15,8 @@ import type { ConfiguredThinkingLevel } from "../thinking";
|
|
|
15
15
|
import type { ScopedModelItem } from "./model-hub";
|
|
16
16
|
import { bottomBorder, row, topBorder } from "../chrome/overlay-box";
|
|
17
17
|
import { resolveSegmentPalette } from "../chrome/segment-track";
|
|
18
|
+
import { formatKeyHint } from "../app-keybindings";
|
|
19
|
+
import { editorKey, editorKeys } from "../chrome/keybinding-hints";
|
|
18
20
|
|
|
19
21
|
/** Configured role resolved to a concrete model. */
|
|
20
22
|
export interface ResolvedRoleModel {
|
|
@@ -58,10 +60,8 @@ export interface ModelPickerOptions {
|
|
|
58
60
|
quickRoleOrder?: ReadonlyArray<string>;
|
|
59
61
|
/** Active quick role, highlighted when the search begins with `@`. */
|
|
60
62
|
currentQuickRole?: string;
|
|
61
|
-
/** Keys that toggle task-subagent mode while the picker is open;
|
|
63
|
+
/** Keys that toggle task-subagent mode while the picker is open; the first is shown in the footer. */
|
|
62
64
|
taskModeKeys?: readonly KeyId[];
|
|
63
|
-
/** Human-readable label for the toggle key, shown in footer hints (e.g. "alt+p"). */
|
|
64
|
-
taskModeKeyLabel?: string;
|
|
65
65
|
/** `provider/id` highlighted and preselected in task mode (current Task subagent model). */
|
|
66
66
|
taskSelector?: string;
|
|
67
67
|
}
|
|
@@ -78,9 +78,21 @@ const HEIGHT_FRACTION = 0.4;
|
|
|
78
78
|
const STATUS_HINT = "Session-only switch — role models stay unchanged";
|
|
79
79
|
const QUICK_ROLE_STATUS_HINT = "Quick role switch — applies its model and thinking for this session";
|
|
80
80
|
const TASK_STATUS_HINT = "Task subagent switch — spawned task agents use this model (session-only)";
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
|
|
82
|
+
/** Footer hint for the active mode; keys resolve at render time so theme/keybinding changes apply. */
|
|
83
|
+
function footerHint(mode: "session" | "role" | "task"): string {
|
|
84
|
+
const upDown = editorKeys("tui.select.up", "tui.select.down");
|
|
85
|
+
const enter = formatKeyHint("enter");
|
|
86
|
+
const close = `${editorKey("tui.select.cancel")} close`;
|
|
87
|
+
switch (mode) {
|
|
88
|
+
case "role":
|
|
89
|
+
return `${upDown} roles · ${enter} apply role model · type to search · ${close}`;
|
|
90
|
+
case "task":
|
|
91
|
+
return `${upDown} models · ${enter} use for Task subagents · type to search · ${close}`;
|
|
92
|
+
default:
|
|
93
|
+
return `${upDown} models · ${enter} use for this session · type to search · @ quick roles · ${close}`;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
84
96
|
|
|
85
97
|
/**
|
|
86
98
|
* The alt+p picker component. Hosted as a non-fullscreen bottom-anchored
|
|
@@ -102,7 +114,7 @@ export class ModelPickerComponent implements Component {
|
|
|
102
114
|
#roleMode = false;
|
|
103
115
|
#taskMode = false;
|
|
104
116
|
#taskMatchKeys = new Set<string>();
|
|
105
|
-
#
|
|
117
|
+
#taskModeKey: KeyId | undefined;
|
|
106
118
|
#taskSelector: string | undefined;
|
|
107
119
|
|
|
108
120
|
constructor(
|
|
@@ -120,8 +132,8 @@ export class ModelPickerComponent implements Component {
|
|
|
120
132
|
this.#currentSelector = options.currentSelector;
|
|
121
133
|
this.#currentQuickRoleSelector = options.currentQuickRole ? `@${options.currentQuickRole}` : undefined;
|
|
122
134
|
this.#taskSelector = options.taskSelector;
|
|
123
|
-
this.#taskModeKeyLabel = options.taskModeKeyLabel ?? "alt+p";
|
|
124
135
|
if (callbacks.onPickTask) {
|
|
136
|
+
this.#taskModeKey = options.taskModeKeys?.[0];
|
|
125
137
|
for (const key of options.taskModeKeys ?? []) addKeyAliases(this.#taskMatchKeys, key);
|
|
126
138
|
}
|
|
127
139
|
this.#quickRoleItems = this.#buildQuickRoleItems(
|
|
@@ -132,7 +144,10 @@ export class ModelPickerComponent implements Component {
|
|
|
132
144
|
this.#browser = new ModelBrowser(settings, {
|
|
133
145
|
currentContextTokens: options.currentContextTokens,
|
|
134
146
|
markOverContext: true,
|
|
135
|
-
emptyText: () =>
|
|
147
|
+
emptyText: () =>
|
|
148
|
+
this.#roleMode
|
|
149
|
+
? ` No quick roles in the ${editorKey("app.model.cycleForward") || formatKeyHint("ctrl+p")} cycle`
|
|
150
|
+
: undefined,
|
|
136
151
|
});
|
|
137
152
|
this.#browser.onActivate = item => {
|
|
138
153
|
const quickRole = this.#quickRoles.get(item.selector);
|
|
@@ -267,9 +282,9 @@ export class ModelPickerComponent implements Component {
|
|
|
267
282
|
: theme.fg("muted", ` ${this.#roleMode ? QUICK_ROLE_STATUS_HINT : STATUS_HINT}`);
|
|
268
283
|
|
|
269
284
|
const borderColor: ThemeColor | undefined = this.#taskMode ? "error" : undefined;
|
|
270
|
-
let footer = this.#taskMode ?
|
|
271
|
-
if (this.#
|
|
272
|
-
footer += ` · ${this.#
|
|
285
|
+
let footer = footerHint(this.#taskMode ? "task" : this.#roleMode ? "role" : "session");
|
|
286
|
+
if (this.#taskModeKey !== undefined && !this.#roleMode) {
|
|
287
|
+
footer += ` · ${formatKeyHint(this.#taskModeKey)} ${this.#taskMode ? "session model" : "task model"}`;
|
|
273
288
|
}
|
|
274
289
|
|
|
275
290
|
const out: string[] = [];
|
|
@@ -11,6 +11,8 @@ import { matchesSelectCancel, matchesSelectDown, matchesSelectUp } from "../keyb
|
|
|
11
11
|
import { formTheme } from "../chrome/form-theme";
|
|
12
12
|
import { bottomBorder, row, topBorder } from "../chrome/overlay-box";
|
|
13
13
|
import { TextFormField } from "../components/form";
|
|
14
|
+
import { formatKeyHint } from "../app-keybindings";
|
|
15
|
+
import { editorKey, editorKeys } from "../chrome/keybinding-hints";
|
|
14
16
|
|
|
15
17
|
export interface MoveOverlayResult {
|
|
16
18
|
directory: string;
|
|
@@ -161,7 +163,9 @@ export class MoveOverlay implements Component, Focusable {
|
|
|
161
163
|
}
|
|
162
164
|
|
|
163
165
|
lines.push(row("", w));
|
|
164
|
-
|
|
166
|
+
const nav = editorKeys("tui.select.up", "tui.select.down");
|
|
167
|
+
const hint = `Type to filter · ${nav} navigate · ${formatKeyHint("tab")} accept · ${formatKeyHint("enter")} confirm · ${editorKey("tui.select.cancel")} cancel`;
|
|
168
|
+
lines.push(row(theme.fg("dim", hint), w));
|
|
165
169
|
lines.push(bottomBorder(w));
|
|
166
170
|
this.#renderMemo = { width: w, fieldLines, revision: this.#revision, lines };
|
|
167
171
|
return lines;
|
|
@@ -3,6 +3,7 @@ import { replaceTabs } from "../render/render-utils";
|
|
|
3
3
|
import { getMarkdownTheme, theme } from "../theme/theme";
|
|
4
4
|
import { OverlayPanel } from "../chrome/overlay-box";
|
|
5
5
|
import { StreamingPanelContent } from "../chrome/streaming-panel";
|
|
6
|
+
import { interruptKey } from "../chrome/keybinding-hints";
|
|
6
7
|
|
|
7
8
|
export type OmfgPanelState =
|
|
8
9
|
| "generating"
|
|
@@ -103,23 +104,25 @@ export class OmfgPanelComponent extends OverlayPanel {
|
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
#footerLine(): string {
|
|
107
|
+
// The composer's `app.interrupt` handler routes Esc to this panel.
|
|
108
|
+
const esc = interruptKey();
|
|
106
109
|
switch (this.#state) {
|
|
107
110
|
case "generating":
|
|
108
111
|
case "validating":
|
|
109
112
|
case "confirming":
|
|
110
113
|
case "saving":
|
|
111
|
-
return theme.fg("muted",
|
|
114
|
+
return theme.fg("muted", `${esc} cancel /omfg`);
|
|
112
115
|
case "saved":
|
|
113
116
|
return theme.fg(
|
|
114
117
|
"success",
|
|
115
|
-
`${theme.status.success} Registered live · ${replaceTabs(this.#savedPath ?? "saved")} ·
|
|
118
|
+
`${theme.status.success} Registered live · ${replaceTabs(this.#savedPath ?? "saved")} · ${esc} dismiss`,
|
|
116
119
|
);
|
|
117
120
|
case "rejected":
|
|
118
|
-
return theme.fg("warning", `${theme.status.warning} Not saved ·
|
|
121
|
+
return theme.fg("warning", `${theme.status.warning} Not saved · ${esc} dismiss`);
|
|
119
122
|
case "aborted":
|
|
120
|
-
return theme.fg("warning", `${theme.status.warning} Cancelled ·
|
|
123
|
+
return theme.fg("warning", `${theme.status.warning} Cancelled · ${esc} dismiss`);
|
|
121
124
|
case "error":
|
|
122
|
-
return theme.fg("error", `${theme.status.error} Error ·
|
|
125
|
+
return theme.fg("error", `${theme.status.error} Error · ${esc} dismiss`);
|
|
123
126
|
}
|
|
124
127
|
}
|
|
125
128
|
|
|
@@ -17,6 +17,8 @@ import { formatCoarseDuration } from "../chrome/format";
|
|
|
17
17
|
import { centerLine } from "../utils";
|
|
18
18
|
import { theme } from "../theme/theme";
|
|
19
19
|
import { matchesAppInterrupt } from "../keybinding-matchers";
|
|
20
|
+
import { formatKeyHint } from "../app-keybindings";
|
|
21
|
+
import { interruptKey } from "../chrome/keybinding-hints";
|
|
20
22
|
|
|
21
23
|
/**
|
|
22
24
|
* Slice of `InteractiveModeContext` the pause screen drives. Narrow so tests
|
|
@@ -50,7 +52,6 @@ const BODY_LINES = [
|
|
|
50
52
|
"Main agent, subagents, and advisor hold at their next step.",
|
|
51
53
|
"In-flight calls finish; nothing new starts until you resume.",
|
|
52
54
|
] as const;
|
|
53
|
-
const RESUME_HINT = "esc · enter · space — resume";
|
|
54
55
|
|
|
55
56
|
/** Live hold clock, seconds-precise: `0:07`, `12:34`, `1:02:03`. */
|
|
56
57
|
function formatClock(ms: number): string {
|
|
@@ -69,6 +70,8 @@ function formatClock(ms: number): string {
|
|
|
69
70
|
export function renderPauseScreen(width: number, height: number, elapsedMs: number, sessionName?: string): string[] {
|
|
70
71
|
const compact = width < MIN_FULL_WIDTH || height < MIN_FULL_HEIGHT;
|
|
71
72
|
const content: string[] = [];
|
|
73
|
+
// Resume keys mirror PauseScreenComponent#handleInput: `app.interrupt` (raw Escape when unbound), Enter, Space.
|
|
74
|
+
const esc = interruptKey();
|
|
72
75
|
|
|
73
76
|
if (compact) {
|
|
74
77
|
if (sessionName) {
|
|
@@ -78,7 +81,7 @@ export function renderPauseScreen(width: number, height: number, elapsedMs: numb
|
|
|
78
81
|
content.push(centerLine(theme.bold(theme.fg("accent", `▌▌ ${TITLE}`)), width).trimEnd());
|
|
79
82
|
content.push("");
|
|
80
83
|
content.push(centerLine(theme.fg("dim", `paused for ${formatClock(elapsedMs)}`), width).trimEnd());
|
|
81
|
-
content.push(centerLine(theme.fg("dim",
|
|
84
|
+
content.push(centerLine(theme.fg("dim", `${esc} to resume`), width).trimEnd());
|
|
82
85
|
} else {
|
|
83
86
|
if (sessionName) {
|
|
84
87
|
content.push(centerLine(theme.bold(sessionName), width).trimEnd());
|
|
@@ -99,7 +102,8 @@ export function renderPauseScreen(width: number, height: number, elapsedMs: numb
|
|
|
99
102
|
content.push("");
|
|
100
103
|
content.push(centerLine(theme.fg("dim", `paused for ${formatClock(elapsedMs)}`), width).trimEnd());
|
|
101
104
|
content.push("");
|
|
102
|
-
|
|
105
|
+
const resumeHint = `${esc} · ${formatKeyHint("enter")} · ${formatKeyHint("space")} — resume`;
|
|
106
|
+
content.push(centerLine(theme.fg("dim", resumeHint), width).trimEnd());
|
|
103
107
|
}
|
|
104
108
|
|
|
105
109
|
const topPad = Math.max(0, Math.floor((height - content.length) / 2));
|
|
@@ -52,6 +52,8 @@ import {
|
|
|
52
52
|
import { joinPlanSections, parsePlanSections, sectionDeletionSpan } from "./plan-toc";
|
|
53
53
|
import { padToWidth } from "../render/utils";
|
|
54
54
|
import { renderSegmentTrack } from "../chrome/segment-track";
|
|
55
|
+
import { formatKeyHint, formatKeyHints } from "../app-keybindings";
|
|
56
|
+
import { editorKey, editorKeys } from "../chrome/keybinding-hints";
|
|
55
57
|
|
|
56
58
|
/** Title shown in the overlay's top border. */
|
|
57
59
|
const OVERLAY_TITLE = "Plan Review";
|
|
@@ -166,9 +168,6 @@ export interface PlanReviewOverlayOptions {
|
|
|
166
168
|
annotationState?: PlanReviewAnnotationState;
|
|
167
169
|
}
|
|
168
170
|
|
|
169
|
-
/** Default trailing footer hint when the caller supplies none. */
|
|
170
|
-
const DEFAULT_HELP_SUFFIX = "esc cancel";
|
|
171
|
-
|
|
172
171
|
export class PlanReviewOverlay implements Component {
|
|
173
172
|
#mdTheme: MarkdownTheme;
|
|
174
173
|
#scrollView: ScrollView;
|
|
@@ -185,7 +184,7 @@ export class PlanReviewOverlay implements Component {
|
|
|
185
184
|
|
|
186
185
|
#options: string[];
|
|
187
186
|
#disabled: Set<number>;
|
|
188
|
-
#helpSuffix: string;
|
|
187
|
+
#helpSuffix: string | undefined;
|
|
189
188
|
#externalEditorLabel: string | undefined;
|
|
190
189
|
#promptTitle: string | undefined;
|
|
191
190
|
#selectedIndex: number;
|
|
@@ -240,7 +239,7 @@ export class PlanReviewOverlay implements Component {
|
|
|
240
239
|
this.#disabled = new Set(
|
|
241
240
|
(options.disabledIndices ?? []).filter(i => Number.isInteger(i) && i >= 0 && i < this.#options.length),
|
|
242
241
|
);
|
|
243
|
-
this.#helpSuffix = options.helpText
|
|
242
|
+
this.#helpSuffix = options.helpText;
|
|
244
243
|
this.#externalEditorLabel = options.externalEditorLabel;
|
|
245
244
|
this.#promptTitle = options.promptTitle;
|
|
246
245
|
this.#selectedIndex = this.#coerceIndex(options.initialIndex ?? 0);
|
|
@@ -1069,22 +1068,39 @@ export class PlanReviewOverlay implements Component {
|
|
|
1069
1068
|
#buildHelp(): string {
|
|
1070
1069
|
const sep = " · ";
|
|
1071
1070
|
const parts: string[] = [];
|
|
1071
|
+
const upDown = editorKeys("tui.select.up", "tui.select.down");
|
|
1072
|
+
const enter = formatKeyHint("enter");
|
|
1072
1073
|
switch (this.#focus) {
|
|
1073
1074
|
case "actions":
|
|
1074
|
-
parts.push(
|
|
1075
|
-
if (this.#slider) parts.push("
|
|
1075
|
+
parts.push(`${upDown} select`, `${enter} confirm`);
|
|
1076
|
+
if (this.#slider) parts.push(`${formatKeyHints(["left", "right"])} model`);
|
|
1076
1077
|
break;
|
|
1077
1078
|
case "toc":
|
|
1078
|
-
parts.push(
|
|
1079
|
+
parts.push(
|
|
1080
|
+
`${upDown} section`,
|
|
1081
|
+
`${enter} open`,
|
|
1082
|
+
`${formatKeyHint("a")} annotate`,
|
|
1083
|
+
`${formatKeyHint("e")} edit`,
|
|
1084
|
+
`${formatKeyHint("d")} delete`,
|
|
1085
|
+
`${formatKeyHint("u")} undo`,
|
|
1086
|
+
);
|
|
1079
1087
|
break;
|
|
1080
1088
|
case "body":
|
|
1081
|
-
parts.push(
|
|
1089
|
+
parts.push(
|
|
1090
|
+
`${upDown} scroll`,
|
|
1091
|
+
`${formatKeyHint("shift")} faster`,
|
|
1092
|
+
formatKeyHints(["pageUp", "pageDown"]),
|
|
1093
|
+
`${formatKeyHints(["g", "shift+g"])} ends`,
|
|
1094
|
+
`${formatKeyHint("a")} annotate`,
|
|
1095
|
+
`${formatKeyHint("e")} edit`,
|
|
1096
|
+
`${formatKeyHint("u")} undo`,
|
|
1097
|
+
);
|
|
1082
1098
|
break;
|
|
1083
1099
|
}
|
|
1084
|
-
if (this.callbacks.onCopyPlan) parts.push("c copy
|
|
1085
|
-
parts.push("tab regions
|
|
1100
|
+
if (this.callbacks.onCopyPlan) parts.push(`${formatKeyHint("c")} copy`);
|
|
1101
|
+
parts.push(`${formatKeyHint("tab")} regions`);
|
|
1086
1102
|
if (this.#externalEditorLabel && this.#focus !== "toc") parts.push(`${this.#externalEditorLabel} editor`);
|
|
1087
|
-
parts.push(this.#helpSuffix);
|
|
1103
|
+
parts.push(this.#helpSuffix ?? `${editorKey("tui.select.cancel")} cancel`);
|
|
1088
1104
|
return parts.join(sep);
|
|
1089
1105
|
}
|
|
1090
1106
|
|
|
@@ -1295,7 +1311,13 @@ export class PlanReviewOverlay implements Component {
|
|
|
1295
1311
|
lines.push(truncateToWidth(label, innerWidth, Ellipsis.Unicode));
|
|
1296
1312
|
}
|
|
1297
1313
|
if (!Number.isFinite(maxRows) || maxRows >= 2) {
|
|
1298
|
-
|
|
1314
|
+
const upDown = editorKeys("tui.select.up", "tui.select.down");
|
|
1315
|
+
lines.push(
|
|
1316
|
+
theme.fg(
|
|
1317
|
+
"dim",
|
|
1318
|
+
`${upDown} choose · ${formatKeyHint("enter")} edit · ${editorKey("tui.select.cancel")} cancel`,
|
|
1319
|
+
),
|
|
1320
|
+
);
|
|
1299
1321
|
}
|
|
1300
1322
|
return lines.slice(0, Math.max(0, Math.floor(maxRows)));
|
|
1301
1323
|
}
|
|
@@ -1315,7 +1337,11 @@ export class PlanReviewOverlay implements Component {
|
|
|
1315
1337
|
innerWidth,
|
|
1316
1338
|
Ellipsis.Unicode,
|
|
1317
1339
|
);
|
|
1318
|
-
const hintParts = [
|
|
1340
|
+
const hintParts = [
|
|
1341
|
+
`${editorKey("tui.input.submit")} save`,
|
|
1342
|
+
`${editorKey("tui.input.newLine")} newline`,
|
|
1343
|
+
`${editorKey("tui.select.cancel")} cancel`,
|
|
1344
|
+
];
|
|
1319
1345
|
if (this.#editingAnnotation) hintParts.push("empty deletes");
|
|
1320
1346
|
if (this.#externalEditorLabel) hintParts.push(`${this.#externalEditorLabel} editor`);
|
|
1321
1347
|
this.#editor.setMaxHeight(Math.max(1, Math.min(MAX_ANNOTATION_EDITOR_ROWS, (process.stdout.rows || 40) - 12)));
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type Component, CURSOR_MARKER, type Focusable, Input, truncateToWidth, visibleWidth } from "../index";
|
|
2
2
|
import { theme } from "../theme/theme";
|
|
3
3
|
import { OverlayPanel, PanelRows } from "../chrome/overlay-box";
|
|
4
|
+
import { editorKey } from "../chrome/keybinding-hints";
|
|
4
5
|
|
|
5
6
|
/** A confirmed destination chosen from {@link PlanSaveOverlay}. */
|
|
6
7
|
export interface PlanSaveOverlayResult {
|
|
@@ -58,7 +59,8 @@ export class PlanSaveOverlay implements Component, Focusable {
|
|
|
58
59
|
render(width: number): readonly string[] {
|
|
59
60
|
const innerWidth = Math.max(0, width - 4);
|
|
60
61
|
this.#input.focused = this.#focused;
|
|
61
|
-
|
|
62
|
+
const hint = `${editorKey("tui.input.submit")} save and quit · ${editorKey("tui.select.cancel")} cancel`;
|
|
63
|
+
this.#body.setLines([this.#renderInput(innerWidth), theme.fg("dim", hint)]);
|
|
62
64
|
return this.#panel.render(width);
|
|
63
65
|
}
|
|
64
66
|
|
|
@@ -23,6 +23,7 @@ import { OverlayPanel } from "../chrome/overlay-box";
|
|
|
23
23
|
import { FormField, SelectFormField, TextFormField } from "../components/form";
|
|
24
24
|
import { formTheme } from "../chrome/form-theme";
|
|
25
25
|
import { SettingsFormField } from "../components/settings-list";
|
|
26
|
+
import { editorKey } from "../chrome/keybinding-hints";
|
|
26
27
|
|
|
27
28
|
/** Setting metadata consumed by the plugin settings UI. */
|
|
28
29
|
export type PluginSettingSchema = {
|
|
@@ -270,7 +271,16 @@ export class PluginListComponent extends OverlayPanel {
|
|
|
270
271
|
|
|
271
272
|
this.addChild(this.#selectList);
|
|
272
273
|
this.addChild(new Spacer(1));
|
|
273
|
-
this.addChild(
|
|
274
|
+
this.addChild(
|
|
275
|
+
new Text(
|
|
276
|
+
theme.fg(
|
|
277
|
+
"dim",
|
|
278
|
+
`${editorKey("tui.select.confirm")} to configure · ${editorKey("tui.select.cancel")} to go back`,
|
|
279
|
+
),
|
|
280
|
+
0,
|
|
281
|
+
0,
|
|
282
|
+
),
|
|
283
|
+
);
|
|
274
284
|
}
|
|
275
285
|
|
|
276
286
|
#renderItem(entry: PluginListEntry): SelectItem {
|
|
@@ -408,7 +418,7 @@ export class PluginDetailComponent extends OverlayPanel {
|
|
|
408
418
|
items,
|
|
409
419
|
maxVisible: Math.min(items.length, 10),
|
|
410
420
|
settingsTheme: getSettingsListTheme(),
|
|
411
|
-
hint:
|
|
421
|
+
hint: `${editorKey("tui.select.confirm")} to edit · ${editorKey("tui.select.cancel")} to go back`,
|
|
412
422
|
onChange: (id, newValue) => {
|
|
413
423
|
if (id === "__enabled__") {
|
|
414
424
|
this.callbacks.onEnabledChange(newValue === "true");
|
|
@@ -540,7 +550,7 @@ export class MarketplacePluginDetailComponent extends OverlayPanel {
|
|
|
540
550
|
maxVisible: Math.min(items.length, 10),
|
|
541
551
|
settingsTheme: getSettingsListTheme(),
|
|
542
552
|
summary,
|
|
543
|
-
hint:
|
|
553
|
+
hint: `${editorKey("tui.select.confirm")} to edit · ${editorKey("tui.select.cancel")} to go back`,
|
|
544
554
|
onChange: (id, newValue) => {
|
|
545
555
|
if (id === "__enabled__") {
|
|
546
556
|
const next = newValue === "true";
|
|
@@ -571,9 +581,6 @@ export class MarketplacePluginDetailComponent extends OverlayPanel {
|
|
|
571
581
|
// Config Submenus
|
|
572
582
|
// =============================================================================
|
|
573
583
|
|
|
574
|
-
/** Shared footer hint for plugin detail lists. */
|
|
575
|
-
const DETAIL_FOOTER_HINT = "Enter to edit · Esc to go back";
|
|
576
|
-
|
|
577
584
|
/**
|
|
578
585
|
* Thin OverlayPanel boundary around a shared form field. The panel title stays
|
|
579
586
|
* the setting key; input, paste, focus, and mouse all forward to the field.
|
|
@@ -631,7 +638,7 @@ function createConfigEnumPanel(
|
|
|
631
638
|
currentValue,
|
|
632
639
|
maxVisible: 8,
|
|
633
640
|
selectTheme: getSelectListTheme(),
|
|
634
|
-
hint: "
|
|
641
|
+
hint: `${editorKey("tui.select.confirm")} to select · ${editorKey("tui.select.cancel")} to cancel`,
|
|
635
642
|
onSubmit: onSelect,
|
|
636
643
|
onCancel,
|
|
637
644
|
requestRender,
|
|
@@ -660,7 +667,7 @@ function createConfigInputPanel(
|
|
|
660
667
|
secret: schema.secret,
|
|
661
668
|
initialValue: !schema.secret ? currentValue : undefined,
|
|
662
669
|
empty: "cancel",
|
|
663
|
-
hint: "
|
|
670
|
+
hint: `${editorKey("tui.input.submit")} to save · ${editorKey("tui.select.cancel")} to cancel`,
|
|
664
671
|
onSubmit,
|
|
665
672
|
onCancel,
|
|
666
673
|
requestRender,
|
|
@@ -7,6 +7,8 @@ import { OverlayPanel } from "../chrome/overlay-box";
|
|
|
7
7
|
import { MenuSelection } from "../components/menu-selection";
|
|
8
8
|
import { centeredViewportRange } from "../components/scroll-viewport";
|
|
9
9
|
import { formatUsageResetWindow } from "./usage-display";
|
|
10
|
+
import { formatKeyHint } from "../app-keybindings";
|
|
11
|
+
import { editorKey, editorKeys } from "../chrome/keybinding-hints";
|
|
10
12
|
|
|
11
13
|
const RESET_SELECTOR_MAX_VISIBLE = 10;
|
|
12
14
|
|
|
@@ -142,7 +144,10 @@ export class ResetUsageSelectorComponent extends OverlayPanel {
|
|
|
142
144
|
const pending = items.find(item => this.#menu.isPending(item));
|
|
143
145
|
const hint = pending
|
|
144
146
|
? theme.fg("warning", oneLine(this.#confirmationMessage(pending)))
|
|
145
|
-
: theme.fg(
|
|
147
|
+
: theme.fg(
|
|
148
|
+
"muted",
|
|
149
|
+
`${editorKeys("tui.select.up", "tui.select.down")} select · ${formatKeyHint("enter")} spend a reset · ${editorKey("tui.select.cancel")} cancel`,
|
|
150
|
+
);
|
|
146
151
|
this.#listContainer.addChild(new Text(hint, 0, 0));
|
|
147
152
|
|
|
148
153
|
if (this.#statusMessage) {
|
|
@@ -153,7 +158,9 @@ export class ResetUsageSelectorComponent extends OverlayPanel {
|
|
|
153
158
|
|
|
154
159
|
#confirmationMessage(account: ResetUsageAccount): string {
|
|
155
160
|
const subject = account.credit?.title ? `“${account.credit.title}”` : "1 saved reset";
|
|
156
|
-
const messages = [
|
|
161
|
+
const messages = [
|
|
162
|
+
`Press ${formatKeyHint("enter")} again to spend ${subject} for ${account.label} (${account.providerLabel}).`,
|
|
163
|
+
];
|
|
157
164
|
if (account.credit?.program === "juniper_tide") {
|
|
158
165
|
messages.push("This resets Claude's 5h session limit only; weekly limits stay unchanged.");
|
|
159
166
|
} else if (account.credit?.clears?.length) {
|
|
@@ -162,7 +169,7 @@ export class ResetUsageSelectorComponent extends OverlayPanel {
|
|
|
162
169
|
if (account.credit?.requiresLimit === false) {
|
|
163
170
|
messages.push("Optional early use: this can be spent before the covered limit is reached.");
|
|
164
171
|
}
|
|
165
|
-
messages.push("
|
|
172
|
+
messages.push(`${editorKey("tui.select.cancel")} cancels.`);
|
|
166
173
|
return messages.join(" ");
|
|
167
174
|
}
|
|
168
175
|
|