@astralyn/pi 0.82.5 → 0.82.7
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 +55 -0
- package/README.md +1 -1
- package/dist/cli/project-trust.d.ts.map +1 -1
- package/dist/cli/project-trust.js +4 -1
- package/dist/cli/project-trust.js.map +1 -1
- package/dist/cli/session-picker.d.ts.map +1 -1
- package/dist/cli/session-picker.js +11 -13
- package/dist/cli/session-picker.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +4 -22
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/types.d.ts +32 -5
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +2 -2
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/tools/bash.d.ts +1 -5
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +16 -41
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +42 -11
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +6 -7
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +17 -8
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +3 -3
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/output-accumulator.d.ts +3 -13
- package/dist/core/tools/output-accumulator.d.ts.map +1 -1
- package/dist/core/tools/output-accumulator.js +6 -36
- package/dist/core/tools/output-accumulator.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +4 -4
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/render-utils.d.ts +7 -0
- package/dist/core/tools/render-utils.d.ts.map +1 -1
- package/dist/core/tools/render-utils.js +11 -0
- package/dist/core/tools/render-utils.js.map +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +2 -3
- package/dist/core/tools/write.js.map +1 -1
- package/dist/extensions/deepwiki/render.d.ts.map +1 -1
- package/dist/extensions/deepwiki/render.js +34 -22
- package/dist/extensions/deepwiki/render.js.map +1 -1
- package/dist/extensions/plan/constants.d.ts +43 -6
- package/dist/extensions/plan/constants.d.ts.map +1 -1
- package/dist/extensions/plan/constants.js +51 -8
- package/dist/extensions/plan/constants.js.map +1 -1
- package/dist/extensions/plan/index.d.ts +12 -3
- package/dist/extensions/plan/index.d.ts.map +1 -1
- package/dist/extensions/plan/index.js +173 -35
- package/dist/extensions/plan/index.js.map +1 -1
- package/dist/extensions/plan/schema.d.ts +5 -0
- package/dist/extensions/plan/schema.d.ts.map +1 -1
- package/dist/extensions/plan/schema.js.map +1 -1
- package/dist/extensions/plan/state.d.ts +11 -2
- package/dist/extensions/plan/state.d.ts.map +1 -1
- package/dist/extensions/plan/state.js +31 -9
- package/dist/extensions/plan/state.js.map +1 -1
- package/dist/extensions/plan/storage.d.ts +11 -3
- package/dist/extensions/plan/storage.d.ts.map +1 -1
- package/dist/extensions/plan/storage.js +45 -20
- package/dist/extensions/plan/storage.js.map +1 -1
- package/dist/extensions/plan/view.d.ts +44 -0
- package/dist/extensions/plan/view.d.ts.map +1 -0
- package/dist/extensions/plan/view.js +149 -0
- package/dist/extensions/plan/view.js.map +1 -0
- package/dist/extensions/question/dialog.d.ts.map +1 -1
- package/dist/extensions/question/dialog.js +29 -10
- package/dist/extensions/question/dialog.js.map +1 -1
- package/dist/extensions/question/index.d.ts.map +1 -1
- package/dist/extensions/question/index.js +7 -2
- package/dist/extensions/question/index.js.map +1 -1
- package/dist/extensions/question/render.d.ts +4 -8
- package/dist/extensions/question/render.d.ts.map +1 -1
- package/dist/extensions/question/render.js +202 -29
- package/dist/extensions/question/render.js.map +1 -1
- package/dist/extensions/question/results.d.ts.map +1 -1
- package/dist/extensions/question/results.js +1 -0
- package/dist/extensions/question/results.js.map +1 -1
- package/dist/extensions/question/schema.d.ts.map +1 -1
- package/dist/extensions/question/schema.js +2 -2
- package/dist/extensions/question/schema.js.map +1 -1
- package/dist/extensions/question/types.d.ts +1 -0
- package/dist/extensions/question/types.d.ts.map +1 -1
- package/dist/extensions/question/types.js +1 -1
- package/dist/extensions/question/types.js.map +1 -1
- package/dist/extensions/router/stream.d.ts +1 -0
- package/dist/extensions/router/stream.d.ts.map +1 -1
- package/dist/extensions/router/stream.js +75 -0
- package/dist/extensions/router/stream.js.map +1 -1
- package/dist/extensions/subagent/activity.d.ts.map +1 -1
- package/dist/extensions/subagent/activity.js +25 -4
- package/dist/extensions/subagent/activity.js.map +1 -1
- package/dist/extensions/subagent/constants.d.ts +1 -0
- package/dist/extensions/subagent/constants.d.ts.map +1 -1
- package/dist/extensions/subagent/constants.js +1 -0
- package/dist/extensions/subagent/constants.js.map +1 -1
- package/dist/extensions/subagent/index.d.ts.map +1 -1
- package/dist/extensions/subagent/index.js +16 -0
- package/dist/extensions/subagent/index.js.map +1 -1
- package/dist/extensions/subagent/render.d.ts.map +1 -1
- package/dist/extensions/subagent/render.js +352 -152
- package/dist/extensions/subagent/render.js.map +1 -1
- package/dist/extensions/subagent/resolve.d.ts.map +1 -1
- package/dist/extensions/subagent/resolve.js +13 -9
- package/dist/extensions/subagent/resolve.js.map +1 -1
- package/dist/extensions/subagent/retry.d.ts +17 -0
- package/dist/extensions/subagent/retry.d.ts.map +1 -0
- package/dist/extensions/subagent/retry.js +41 -0
- package/dist/extensions/subagent/retry.js.map +1 -0
- package/dist/extensions/subagent/runner.d.ts.map +1 -1
- package/dist/extensions/subagent/runner.js +37 -7
- package/dist/extensions/subagent/runner.js.map +1 -1
- package/dist/extensions/subagent/schema.d.ts.map +1 -1
- package/dist/extensions/subagent/schema.js +2 -2
- package/dist/extensions/subagent/schema.js.map +1 -1
- package/dist/extensions/subagent/sdk-runner.d.ts +6 -0
- package/dist/extensions/subagent/sdk-runner.d.ts.map +1 -1
- package/dist/extensions/subagent/sdk-runner.js +46 -24
- package/dist/extensions/subagent/sdk-runner.js.map +1 -1
- package/dist/extensions/subagent/types.d.ts +7 -0
- package/dist/extensions/subagent/types.d.ts.map +1 -1
- package/dist/extensions/subagent/types.js.map +1 -1
- package/dist/extensions/todo/constants.d.ts +17 -6
- package/dist/extensions/todo/constants.d.ts.map +1 -1
- package/dist/extensions/todo/constants.js +22 -9
- package/dist/extensions/todo/constants.js.map +1 -1
- package/dist/extensions/todo/index.d.ts +0 -14
- package/dist/extensions/todo/index.d.ts.map +1 -1
- package/dist/extensions/todo/index.js +13 -1
- package/dist/extensions/todo/index.js.map +1 -1
- package/dist/extensions/todo/schema.d.ts +53 -2
- package/dist/extensions/todo/schema.d.ts.map +1 -1
- package/dist/extensions/todo/schema.js +57 -8
- package/dist/extensions/todo/schema.js.map +1 -1
- package/dist/extensions/todo/state.d.ts +16 -5
- package/dist/extensions/todo/state.d.ts.map +1 -1
- package/dist/extensions/todo/state.js +874 -130
- package/dist/extensions/todo/state.js.map +1 -1
- package/dist/extensions/todo/view.d.ts +16 -1
- package/dist/extensions/todo/view.d.ts.map +1 -1
- package/dist/extensions/todo/view.js +428 -1
- package/dist/extensions/todo/view.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +2 -1
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/components/armin.d.ts.map +1 -1
- package/dist/modes/interactive/components/armin.js +5 -0
- package/dist/modes/interactive/components/armin.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts +1 -0
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +4 -2
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/countdown-timer.d.ts +13 -6
- package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -1
- package/dist/modes/interactive/components/countdown-timer.js +54 -17
- package/dist/modes/interactive/components/countdown-timer.js.map +1 -1
- package/dist/modes/interactive/components/extension-selector.d.ts +6 -1
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-selector.js +23 -11
- package/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.d.ts +11 -1
- package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.js +56 -0
- package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts +3 -0
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +52 -2
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +10 -0
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +139 -18
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tool-group.d.ts +1 -0
- package/dist/modes/interactive/components/tool-group.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-group.js +5 -0
- package/dist/modes/interactive/components/tool-group.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts +10 -1
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +55 -2
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +5 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +79 -19
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +3 -2
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +8 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/utils/paths.d.ts +6 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +8 -0
- package/dist/utils/paths.js.map +1 -1
- package/docs/bundled/README.md +1 -0
- package/docs/bundled/extensions/deepwiki.md +1 -1
- package/docs/bundled/extensions/plan.md +13 -10
- package/docs/bundled/extensions/question.md +7 -5
- package/docs/bundled/extensions/router.md +3 -4
- package/docs/bundled/extensions/statusline.md +0 -1
- package/docs/bundled/extensions/subagent.md +5 -4
- package/docs/bundled/extensions/todo.md +9 -7
- package/docs/bundled/tool-presentation.md +17 -16
- package/docs/extensions.md +34 -5
- package/docs/rpc.md +1 -1
- package/docs/usage.md +1 -1
- package/npm-shrinkwrap.json +6 -6
- package/package.json +1 -1
- package/dist/utils/output-decoder.d.ts +0 -52
- package/dist/utils/output-decoder.d.ts.map +0 -1
- package/dist/utils/output-decoder.js +0 -134
- package/dist/utils/output-decoder.js.map +0 -1
|
@@ -1,32 +1,69 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Shared deadline-based countdown timer for interactive components.
|
|
3
3
|
*/
|
|
4
4
|
export class CountdownTimer {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
timeoutId;
|
|
6
|
+
deadline;
|
|
7
7
|
tui;
|
|
8
8
|
onTick;
|
|
9
9
|
onExpire;
|
|
10
|
+
lastReportedSeconds;
|
|
11
|
+
disposed = false;
|
|
10
12
|
constructor(timeoutMs, tui, onTick, onExpire) {
|
|
11
13
|
this.tui = tui;
|
|
12
14
|
this.onTick = onTick;
|
|
13
15
|
this.onExpire = onExpire;
|
|
14
|
-
this.
|
|
15
|
-
this.
|
|
16
|
-
this.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
this.deadline = Date.now() + Math.max(0, timeoutMs);
|
|
17
|
+
this.report(this.remainingSeconds());
|
|
18
|
+
this.scheduleNextUpdate();
|
|
19
|
+
}
|
|
20
|
+
remainingMilliseconds() {
|
|
21
|
+
return Math.max(0, this.deadline - Date.now());
|
|
22
|
+
}
|
|
23
|
+
remainingSeconds() {
|
|
24
|
+
return Math.ceil(this.remainingMilliseconds() / 1000);
|
|
25
|
+
}
|
|
26
|
+
report(seconds) {
|
|
27
|
+
if (seconds === this.lastReportedSeconds)
|
|
28
|
+
return;
|
|
29
|
+
this.lastReportedSeconds = seconds;
|
|
30
|
+
this.onTick(seconds);
|
|
31
|
+
}
|
|
32
|
+
scheduleNextUpdate() {
|
|
33
|
+
if (this.disposed)
|
|
34
|
+
return;
|
|
35
|
+
const remainingMs = this.remainingMilliseconds();
|
|
36
|
+
if (remainingMs <= 0) {
|
|
37
|
+
this.timeoutId = setTimeout(() => this.update(), 0);
|
|
38
|
+
this.timeoutId.unref?.();
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const remainingSeconds = Math.ceil(remainingMs / 1000);
|
|
42
|
+
const untilNextSecond = remainingMs - (remainingSeconds - 1) * 1000;
|
|
43
|
+
this.timeoutId = setTimeout(() => this.update(), Math.max(1, Math.min(remainingMs, untilNextSecond)));
|
|
44
|
+
this.timeoutId.unref?.();
|
|
45
|
+
}
|
|
46
|
+
update() {
|
|
47
|
+
if (this.disposed)
|
|
48
|
+
return;
|
|
49
|
+
this.timeoutId = undefined;
|
|
50
|
+
const remainingSeconds = this.remainingSeconds();
|
|
51
|
+
this.report(remainingSeconds);
|
|
52
|
+
this.tui?.requestRender();
|
|
53
|
+
if (remainingSeconds <= 0) {
|
|
54
|
+
this.dispose();
|
|
55
|
+
this.onExpire();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
this.scheduleNextUpdate();
|
|
25
59
|
}
|
|
26
60
|
dispose() {
|
|
27
|
-
if (this.
|
|
28
|
-
|
|
29
|
-
|
|
61
|
+
if (this.disposed)
|
|
62
|
+
return;
|
|
63
|
+
this.disposed = true;
|
|
64
|
+
if (this.timeoutId !== undefined) {
|
|
65
|
+
clearTimeout(this.timeoutId);
|
|
66
|
+
this.timeoutId = undefined;
|
|
30
67
|
}
|
|
31
68
|
}
|
|
32
69
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"countdown-timer.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/countdown-timer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,MAAM,OAAO,cAAc;IAClB,
|
|
1
|
+
{"version":3,"file":"countdown-timer.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/countdown-timer.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shared deadline-based countdown timer for interactive components.\n */\n\nimport type { TUI } from \"@earendil-works/pi-tui\";\n\nexport class CountdownTimer {\n\tprivate timeoutId: ReturnType<typeof setTimeout> | undefined;\n\tprivate readonly deadline: number;\n\tprivate readonly tui: TUI | undefined;\n\tprivate readonly onTick: (seconds: number) => void;\n\tprivate readonly onExpire: () => void;\n\tprivate lastReportedSeconds: number | undefined;\n\tprivate disposed = false;\n\n\tconstructor(timeoutMs: number, tui: TUI | undefined, onTick: (seconds: number) => void, onExpire: () => void) {\n\t\tthis.tui = tui;\n\t\tthis.onTick = onTick;\n\t\tthis.onExpire = onExpire;\n\t\tthis.deadline = Date.now() + Math.max(0, timeoutMs);\n\t\tthis.report(this.remainingSeconds());\n\t\tthis.scheduleNextUpdate();\n\t}\n\n\tprivate remainingMilliseconds(): number {\n\t\treturn Math.max(0, this.deadline - Date.now());\n\t}\n\n\tprivate remainingSeconds(): number {\n\t\treturn Math.ceil(this.remainingMilliseconds() / 1000);\n\t}\n\n\tprivate report(seconds: number): void {\n\t\tif (seconds === this.lastReportedSeconds) return;\n\t\tthis.lastReportedSeconds = seconds;\n\t\tthis.onTick(seconds);\n\t}\n\n\tprivate scheduleNextUpdate(): void {\n\t\tif (this.disposed) return;\n\t\tconst remainingMs = this.remainingMilliseconds();\n\t\tif (remainingMs <= 0) {\n\t\t\tthis.timeoutId = setTimeout(() => this.update(), 0);\n\t\t\tthis.timeoutId.unref?.();\n\t\t\treturn;\n\t\t}\n\n\t\tconst remainingSeconds = Math.ceil(remainingMs / 1000);\n\t\tconst untilNextSecond = remainingMs - (remainingSeconds - 1) * 1000;\n\t\tthis.timeoutId = setTimeout(() => this.update(), Math.max(1, Math.min(remainingMs, untilNextSecond)));\n\t\tthis.timeoutId.unref?.();\n\t}\n\n\tprivate update(): void {\n\t\tif (this.disposed) return;\n\t\tthis.timeoutId = undefined;\n\t\tconst remainingSeconds = this.remainingSeconds();\n\t\tthis.report(remainingSeconds);\n\t\tthis.tui?.requestRender();\n\n\t\tif (remainingSeconds <= 0) {\n\t\t\tthis.dispose();\n\t\t\tthis.onExpire();\n\t\t\treturn;\n\t\t}\n\t\tthis.scheduleNextUpdate();\n\t}\n\n\tdispose(): void {\n\t\tif (this.disposed) return;\n\t\tthis.disposed = true;\n\t\tif (this.timeoutId !== undefined) {\n\t\t\tclearTimeout(this.timeoutId);\n\t\t\tthis.timeoutId = undefined;\n\t\t}\n\t}\n}\n"]}
|
|
@@ -3,10 +3,15 @@
|
|
|
3
3
|
* Displays a list of string options with keyboard navigation.
|
|
4
4
|
*/
|
|
5
5
|
import { Container, type TUI } from "@earendil-works/pi-tui";
|
|
6
|
+
import type { SelectOption } from "../../../core/extensions/types.ts";
|
|
6
7
|
export interface ExtensionSelectorOptions {
|
|
7
8
|
tui?: TUI;
|
|
8
9
|
timeout?: number;
|
|
9
10
|
onToggleToolsExpanded?: () => void;
|
|
11
|
+
/** Muted line under the title, for context that is not itself a choice. */
|
|
12
|
+
subtitle?: string;
|
|
13
|
+
/** Overrides the "cancel" wording in the key hints (e.g. when Esc means "keep planning"). */
|
|
14
|
+
cancelHint?: string;
|
|
10
15
|
}
|
|
11
16
|
export declare class ExtensionSelectorComponent extends Container {
|
|
12
17
|
private options;
|
|
@@ -18,7 +23,7 @@ export declare class ExtensionSelectorComponent extends Container {
|
|
|
18
23
|
private baseTitle;
|
|
19
24
|
private countdown;
|
|
20
25
|
private onToggleToolsExpanded;
|
|
21
|
-
constructor(title: string, options: string
|
|
26
|
+
constructor(title: string, options: ReadonlyArray<string | SelectOption>, onSelect: (option: string) => void, onCancel: () => void, opts?: ExtensionSelectorOptions);
|
|
22
27
|
private updateList;
|
|
23
28
|
handleInput(keyData: string): void;
|
|
24
29
|
dispose(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/extension-selector.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAgC,KAAK,GAAG,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"extension-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/extension-selector.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAgC,KAAK,GAAG,EAAE,MAAM,wBAAwB,CAAC;AAC3F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AAMtE,MAAM,WAAW,wBAAwB;IACxC,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;IACnC,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6FAA6F;IAC7F,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAOD,qBAAa,0BAA2B,SAAQ,SAAS;IACxD,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,gBAAgB,CAA2B;IACnD,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,SAAS,CAAO;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAA6B;IAC9C,OAAO,CAAC,qBAAqB,CAA2B;IAExD,YACC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,aAAa,CAAC,MAAM,GAAG,YAAY,CAAC,EAC7C,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,EAClC,QAAQ,EAAE,MAAM,IAAI,EACpB,IAAI,CAAC,EAAE,wBAAwB,EA2C/B;IAED,OAAO,CAAC,UAAU;IAYlB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAgBjC;IAED,OAAO,IAAI,IAAI,CAEd;CACD","sourcesContent":["/**\n * Generic selector component for extensions.\n * Displays a list of string options with keyboard navigation.\n */\n\nimport { Container, getKeybindings, Spacer, Text, type TUI } from \"@earendil-works/pi-tui\";\nimport type { SelectOption } from \"../../../core/extensions/types.ts\";\nimport { theme } from \"../theme/theme.ts\";\nimport { CountdownTimer } from \"./countdown-timer.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyLabel, rawKeyHint } from \"./keybinding-hints.ts\";\n\nexport interface ExtensionSelectorOptions {\n\ttui?: TUI;\n\ttimeout?: number;\n\tonToggleToolsExpanded?: () => void;\n\t/** Muted line under the title, for context that is not itself a choice. */\n\tsubtitle?: string;\n\t/** Overrides the \"cancel\" wording in the key hints (e.g. when Esc means \"keep planning\"). */\n\tcancelHint?: string;\n}\n\n/** Normalize the string shorthand so rendering only deals with one shape. */\nfunction toSelectOption(option: string | SelectOption): SelectOption {\n\treturn typeof option === \"string\" ? { label: option } : option;\n}\n\nexport class ExtensionSelectorComponent extends Container {\n\tprivate options: SelectOption[];\n\tprivate selectedIndex = 0;\n\tprivate listContainer: Container;\n\tprivate onSelectCallback: (option: string) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate titleText: Text;\n\tprivate baseTitle: string;\n\tprivate countdown: CountdownTimer | undefined;\n\tprivate onToggleToolsExpanded: (() => void) | undefined;\n\n\tconstructor(\n\t\ttitle: string,\n\t\toptions: ReadonlyArray<string | SelectOption>,\n\t\tonSelect: (option: string) => void,\n\t\tonCancel: () => void,\n\t\topts?: ExtensionSelectorOptions,\n\t) {\n\t\tsuper();\n\n\t\tthis.options = options.map(toSelectOption);\n\t\tthis.onSelectCallback = onSelect;\n\t\tthis.onCancelCallback = onCancel;\n\t\tthis.onToggleToolsExpanded = opts?.onToggleToolsExpanded;\n\t\tthis.baseTitle = title;\n\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\n\t\tthis.titleText = new Text(theme.fg(\"accent\", theme.bold(title)), 1, 0);\n\t\tthis.addChild(this.titleText);\n\t\tif (opts?.subtitle) this.addChild(new Text(theme.fg(\"muted\", opts.subtitle), 1, 0));\n\t\tthis.addChild(new Spacer(1));\n\n\t\tif (opts?.timeout && opts.timeout > 0 && opts.tui) {\n\t\t\tthis.countdown = new CountdownTimer(\n\t\t\t\topts.timeout,\n\t\t\t\topts.tui,\n\t\t\t\t(s) => this.titleText.setText(theme.fg(\"accent\", theme.bold(`${this.baseTitle} (${s}s)`))),\n\t\t\t\t() => this.onCancelCallback(),\n\t\t\t);\n\t\t}\n\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\t\tthis.addChild(new Spacer(1));\n\t\tconst navigationKeys = [keyLabel(\"tui.select.up\"), keyLabel(\"tui.select.down\")].filter(Boolean).join(\"/\");\n\t\tconst confirmKey = keyLabel(\"tui.select.confirm\");\n\t\tconst cancelKey = keyLabel(\"tui.select.cancel\");\n\t\tconst hints = [\n\t\t\tnavigationKeys ? rawKeyHint(navigationKeys, \"navigate\") : \"\",\n\t\t\tconfirmKey ? rawKeyHint(confirmKey, \"select\") : \"\",\n\t\t\tcancelKey ? rawKeyHint(cancelKey, opts?.cancelHint ?? \"cancel\") : \"\",\n\t\t].filter(Boolean);\n\t\tthis.addChild(new Text(hints.join(theme.fg(\"muted\", \" • \")), 1, 0));\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\n\t\tthis.updateList();\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\t\tfor (let i = 0; i < this.options.length; i++) {\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst { label, description } = this.options[i];\n\t\t\tconst text = isSelected ? theme.fg(\"accent\", `→ ${label}`) : ` ${theme.fg(\"text\", label)}`;\n\t\t\tthis.listContainer.addChild(new Text(text, 1, 0));\n\t\t\t// Aligned under the label, muted so the action stays the thing you scan.\n\t\t\tif (description) this.listContainer.addChild(new Text(` ${theme.fg(\"muted\", description)}`, 1, 0));\n\t\t}\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"app.tools.expand\")) {\n\t\t\tthis.onToggleToolsExpanded?.();\n\t\t} else if (kb.matches(keyData, \"tui.select.up\") || keyData === \"k\") {\n\t\t\tthis.selectedIndex = Math.max(0, this.selectedIndex - 1);\n\t\t\tthis.updateList();\n\t\t} else if (kb.matches(keyData, \"tui.select.down\") || keyData === \"j\") {\n\t\t\tthis.selectedIndex = Math.min(this.options.length - 1, this.selectedIndex + 1);\n\t\t\tthis.updateList();\n\t\t} else if (kb.matches(keyData, \"tui.select.confirm\") || keyData === \"\\n\") {\n\t\t\tconst selected = this.options[this.selectedIndex];\n\t\t\tif (selected) this.onSelectCallback(selected.label);\n\t\t} else if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tthis.onCancelCallback();\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tthis.countdown?.dispose();\n\t}\n}\n"]}
|
|
@@ -6,7 +6,11 @@ import { Container, getKeybindings, Spacer, Text } from "@earendil-works/pi-tui"
|
|
|
6
6
|
import { theme } from "../theme/theme.js";
|
|
7
7
|
import { CountdownTimer } from "./countdown-timer.js";
|
|
8
8
|
import { DynamicBorder } from "./dynamic-border.js";
|
|
9
|
-
import {
|
|
9
|
+
import { keyLabel, rawKeyHint } from "./keybinding-hints.js";
|
|
10
|
+
/** Normalize the string shorthand so rendering only deals with one shape. */
|
|
11
|
+
function toSelectOption(option) {
|
|
12
|
+
return typeof option === "string" ? { label: option } : option;
|
|
13
|
+
}
|
|
10
14
|
export class ExtensionSelectorComponent extends Container {
|
|
11
15
|
options;
|
|
12
16
|
selectedIndex = 0;
|
|
@@ -19,7 +23,7 @@ export class ExtensionSelectorComponent extends Container {
|
|
|
19
23
|
onToggleToolsExpanded;
|
|
20
24
|
constructor(title, options, onSelect, onCancel, opts) {
|
|
21
25
|
super();
|
|
22
|
-
this.options = options;
|
|
26
|
+
this.options = options.map(toSelectOption);
|
|
23
27
|
this.onSelectCallback = onSelect;
|
|
24
28
|
this.onCancelCallback = onCancel;
|
|
25
29
|
this.onToggleToolsExpanded = opts?.onToggleToolsExpanded;
|
|
@@ -28,6 +32,8 @@ export class ExtensionSelectorComponent extends Container {
|
|
|
28
32
|
this.addChild(new Spacer(1));
|
|
29
33
|
this.titleText = new Text(theme.fg("accent", theme.bold(title)), 1, 0);
|
|
30
34
|
this.addChild(this.titleText);
|
|
35
|
+
if (opts?.subtitle)
|
|
36
|
+
this.addChild(new Text(theme.fg("muted", opts.subtitle), 1, 0));
|
|
31
37
|
this.addChild(new Spacer(1));
|
|
32
38
|
if (opts?.timeout && opts.timeout > 0 && opts.tui) {
|
|
33
39
|
this.countdown = new CountdownTimer(opts.timeout, opts.tui, (s) => this.titleText.setText(theme.fg("accent", theme.bold(`${this.baseTitle} (${s}s)`))), () => this.onCancelCallback());
|
|
@@ -35,11 +41,15 @@ export class ExtensionSelectorComponent extends Container {
|
|
|
35
41
|
this.listContainer = new Container();
|
|
36
42
|
this.addChild(this.listContainer);
|
|
37
43
|
this.addChild(new Spacer(1));
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
44
|
+
const navigationKeys = [keyLabel("tui.select.up"), keyLabel("tui.select.down")].filter(Boolean).join("/");
|
|
45
|
+
const confirmKey = keyLabel("tui.select.confirm");
|
|
46
|
+
const cancelKey = keyLabel("tui.select.cancel");
|
|
47
|
+
const hints = [
|
|
48
|
+
navigationKeys ? rawKeyHint(navigationKeys, "navigate") : "",
|
|
49
|
+
confirmKey ? rawKeyHint(confirmKey, "select") : "",
|
|
50
|
+
cancelKey ? rawKeyHint(cancelKey, opts?.cancelHint ?? "cancel") : "",
|
|
51
|
+
].filter(Boolean);
|
|
52
|
+
this.addChild(new Text(hints.join(theme.fg("muted", " • ")), 1, 0));
|
|
43
53
|
this.addChild(new Spacer(1));
|
|
44
54
|
this.addChild(new DynamicBorder());
|
|
45
55
|
this.updateList();
|
|
@@ -48,10 +58,12 @@ export class ExtensionSelectorComponent extends Container {
|
|
|
48
58
|
this.listContainer.clear();
|
|
49
59
|
for (let i = 0; i < this.options.length; i++) {
|
|
50
60
|
const isSelected = i === this.selectedIndex;
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
: ` ${theme.fg("text", this.options[i])}`;
|
|
61
|
+
const { label, description } = this.options[i];
|
|
62
|
+
const text = isSelected ? theme.fg("accent", `→ ${label}`) : ` ${theme.fg("text", label)}`;
|
|
54
63
|
this.listContainer.addChild(new Text(text, 1, 0));
|
|
64
|
+
// Aligned under the label, muted so the action stays the thing you scan.
|
|
65
|
+
if (description)
|
|
66
|
+
this.listContainer.addChild(new Text(` ${theme.fg("muted", description)}`, 1, 0));
|
|
55
67
|
}
|
|
56
68
|
}
|
|
57
69
|
handleInput(keyData) {
|
|
@@ -70,7 +82,7 @@ export class ExtensionSelectorComponent extends Container {
|
|
|
70
82
|
else if (kb.matches(keyData, "tui.select.confirm") || keyData === "\n") {
|
|
71
83
|
const selected = this.options[this.selectedIndex];
|
|
72
84
|
if (selected)
|
|
73
|
-
this.onSelectCallback(selected);
|
|
85
|
+
this.onSelectCallback(selected.label);
|
|
74
86
|
}
|
|
75
87
|
else if (kb.matches(keyData, "tui.select.cancel")) {
|
|
76
88
|
this.onCancelCallback();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/extension-selector.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,EAAE,IAAI,EAAY,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"extension-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/extension-selector.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Generic selector component for extensions.\n * Displays a list of string options with keyboard navigation.\n */\n\nimport { Container, getKeybindings, Spacer, Text, type TUI } from \"@earendil-works/pi-tui\";\nimport type { SelectOption } from \"../../../core/extensions/types.ts\";\nimport { theme } from \"../theme/theme.ts\";\nimport { CountdownTimer } from \"./countdown-timer.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyLabel, rawKeyHint } from \"./keybinding-hints.ts\";\n\nexport interface ExtensionSelectorOptions {\n\ttui?: TUI;\n\ttimeout?: number;\n\tonToggleToolsExpanded?: () => void;\n\t/** Muted line under the title, for context that is not itself a choice. */\n\tsubtitle?: string;\n\t/** Overrides the \"cancel\" wording in the key hints (e.g. when Esc means \"keep planning\"). */\n\tcancelHint?: string;\n}\n\n/** Normalize the string shorthand so rendering only deals with one shape. */\nfunction toSelectOption(option: string | SelectOption): SelectOption {\n\treturn typeof option === \"string\" ? { label: option } : option;\n}\n\nexport class ExtensionSelectorComponent extends Container {\n\tprivate options: SelectOption[];\n\tprivate selectedIndex = 0;\n\tprivate listContainer: Container;\n\tprivate onSelectCallback: (option: string) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate titleText: Text;\n\tprivate baseTitle: string;\n\tprivate countdown: CountdownTimer | undefined;\n\tprivate onToggleToolsExpanded: (() => void) | undefined;\n\n\tconstructor(\n\t\ttitle: string,\n\t\toptions: ReadonlyArray<string | SelectOption>,\n\t\tonSelect: (option: string) => void,\n\t\tonCancel: () => void,\n\t\topts?: ExtensionSelectorOptions,\n\t) {\n\t\tsuper();\n\n\t\tthis.options = options.map(toSelectOption);\n\t\tthis.onSelectCallback = onSelect;\n\t\tthis.onCancelCallback = onCancel;\n\t\tthis.onToggleToolsExpanded = opts?.onToggleToolsExpanded;\n\t\tthis.baseTitle = title;\n\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\n\t\tthis.titleText = new Text(theme.fg(\"accent\", theme.bold(title)), 1, 0);\n\t\tthis.addChild(this.titleText);\n\t\tif (opts?.subtitle) this.addChild(new Text(theme.fg(\"muted\", opts.subtitle), 1, 0));\n\t\tthis.addChild(new Spacer(1));\n\n\t\tif (opts?.timeout && opts.timeout > 0 && opts.tui) {\n\t\t\tthis.countdown = new CountdownTimer(\n\t\t\t\topts.timeout,\n\t\t\t\topts.tui,\n\t\t\t\t(s) => this.titleText.setText(theme.fg(\"accent\", theme.bold(`${this.baseTitle} (${s}s)`))),\n\t\t\t\t() => this.onCancelCallback(),\n\t\t\t);\n\t\t}\n\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\t\tthis.addChild(new Spacer(1));\n\t\tconst navigationKeys = [keyLabel(\"tui.select.up\"), keyLabel(\"tui.select.down\")].filter(Boolean).join(\"/\");\n\t\tconst confirmKey = keyLabel(\"tui.select.confirm\");\n\t\tconst cancelKey = keyLabel(\"tui.select.cancel\");\n\t\tconst hints = [\n\t\t\tnavigationKeys ? rawKeyHint(navigationKeys, \"navigate\") : \"\",\n\t\t\tconfirmKey ? rawKeyHint(confirmKey, \"select\") : \"\",\n\t\t\tcancelKey ? rawKeyHint(cancelKey, opts?.cancelHint ?? \"cancel\") : \"\",\n\t\t].filter(Boolean);\n\t\tthis.addChild(new Text(hints.join(theme.fg(\"muted\", \" • \")), 1, 0));\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\n\t\tthis.updateList();\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\t\tfor (let i = 0; i < this.options.length; i++) {\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst { label, description } = this.options[i];\n\t\t\tconst text = isSelected ? theme.fg(\"accent\", `→ ${label}`) : ` ${theme.fg(\"text\", label)}`;\n\t\t\tthis.listContainer.addChild(new Text(text, 1, 0));\n\t\t\t// Aligned under the label, muted so the action stays the thing you scan.\n\t\t\tif (description) this.listContainer.addChild(new Text(` ${theme.fg(\"muted\", description)}`, 1, 0));\n\t\t}\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"app.tools.expand\")) {\n\t\t\tthis.onToggleToolsExpanded?.();\n\t\t} else if (kb.matches(keyData, \"tui.select.up\") || keyData === \"k\") {\n\t\t\tthis.selectedIndex = Math.max(0, this.selectedIndex - 1);\n\t\t\tthis.updateList();\n\t\t} else if (kb.matches(keyData, \"tui.select.down\") || keyData === \"j\") {\n\t\t\tthis.selectedIndex = Math.min(this.options.length - 1, this.selectedIndex + 1);\n\t\t\tthis.updateList();\n\t\t} else if (kb.matches(keyData, \"tui.select.confirm\") || keyData === \"\\n\") {\n\t\t\tconst selected = this.options[this.selectedIndex];\n\t\t\tif (selected) this.onSelectCallback(selected.label);\n\t\t} else if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tthis.onCancelCallback();\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tthis.countdown?.dispose();\n\t}\n}\n"]}
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Utilities for formatting keybinding hints in the UI.
|
|
3
3
|
*/
|
|
4
|
-
import { type Keybinding } from "@earendil-works/pi-tui";
|
|
4
|
+
import { type Keybinding, type KeybindingsManager } from "@earendil-works/pi-tui";
|
|
5
5
|
export interface KeyTextFormatOptions {
|
|
6
6
|
capitalize?: boolean;
|
|
7
7
|
}
|
|
8
8
|
export declare function formatKeyText(key: string, options?: KeyTextFormatOptions): string;
|
|
9
|
+
/** Format one key id for compact UI labels without treating literal `/` or `+` keys as separators. */
|
|
10
|
+
export declare function formatKeyLabel(key: string): string;
|
|
11
|
+
export interface KeyLabelOptions {
|
|
12
|
+
/** Use an injected manager for custom components instead of the global manager. */
|
|
13
|
+
keybindings?: Pick<KeybindingsManager, "getKeys">;
|
|
14
|
+
/** Display only when the action has no configured key. */
|
|
15
|
+
fallback?: string;
|
|
16
|
+
}
|
|
17
|
+
/** Display the first configured key for a compact hint, with human-friendly names. */
|
|
18
|
+
export declare function keyLabel(keybinding: Keybinding, options?: KeyLabelOptions): string;
|
|
9
19
|
export declare function keyText(keybinding: Keybinding): string;
|
|
10
20
|
export declare function keyDisplayText(keybinding: Keybinding): string;
|
|
11
21
|
export declare function keyHint(keybinding: Keybinding, description: string): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keybinding-hints.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/keybinding-hints.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAkB,KAAK,UAAU,EAAc,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"keybinding-hints.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/keybinding-hints.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAkB,KAAK,UAAU,EAAE,KAAK,kBAAkB,EAAc,MAAM,wBAAwB,CAAC;AAG9G,MAAM,WAAW,oBAAoB;IACpC,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB;AAOD,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAyB,GAAG,MAAM,CAUrF;AA2CD,sGAAsG;AACtG,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAYlD;AAED,MAAM,WAAW,eAAe;IAC/B,mFAAmF;IACnF,WAAW,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,SAAS,CAAC,CAAC;IAClD,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,sFAAsF;AACtF,wBAAgB,QAAQ,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,GAAE,eAAoB,GAAG,MAAM,CAGtF;AAED,wBAAgB,OAAO,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAEtD;AAED,wBAAgB,cAAc,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAE7D;AAED,wBAAgB,OAAO,CAAC,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAE3E;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAEnE","sourcesContent":["/**\n * Utilities for formatting keybinding hints in the UI.\n */\n\nimport { getKeybindings, type Keybinding, type KeybindingsManager, type KeyId } from \"@earendil-works/pi-tui\";\nimport { theme } from \"../theme/theme.ts\";\n\nexport interface KeyTextFormatOptions {\n\tcapitalize?: boolean;\n}\n\nfunction formatKeyPart(part: string, options: KeyTextFormatOptions): string {\n\tconst displayPart = process.platform === \"darwin\" && part.toLowerCase() === \"alt\" ? \"option\" : part;\n\treturn options.capitalize ? displayPart.charAt(0).toUpperCase() + displayPart.slice(1) : displayPart;\n}\n\nexport function formatKeyText(key: string, options: KeyTextFormatOptions = {}): string {\n\treturn key\n\t\t.split(\"/\")\n\t\t.map((k) =>\n\t\t\tk\n\t\t\t\t.split(\"+\")\n\t\t\t\t.map((part) => formatKeyPart(part, options))\n\t\t\t\t.join(\"+\"),\n\t\t)\n\t\t.join(\"/\");\n}\n\nfunction formatKeys(keys: KeyId[], options: KeyTextFormatOptions = {}): string {\n\tif (keys.length === 0) return \"\";\n\treturn formatKeyText(keys.join(\"/\"), options);\n}\n\nfunction formatKeyLabelPart(part: string): string {\n\tconst normalized = part.toLowerCase();\n\tswitch (normalized) {\n\t\tcase \"alt\":\n\t\t\treturn process.platform === \"darwin\" ? \"Option\" : \"Alt\";\n\t\tcase \"ctrl\":\n\t\t\treturn \"Ctrl\";\n\t\tcase \"shift\":\n\t\t\treturn \"Shift\";\n\t\tcase \"escape\":\n\t\tcase \"esc\":\n\t\t\treturn \"Esc\";\n\t\tcase \"enter\":\n\t\tcase \"return\":\n\t\t\treturn \"Enter\";\n\t\tcase \"space\":\n\t\t\treturn \"Space\";\n\t\tcase \"tab\":\n\t\t\treturn \"Tab\";\n\t\tcase \"left\":\n\t\t\treturn \"←\";\n\t\tcase \"right\":\n\t\t\treturn \"→\";\n\t\tcase \"up\":\n\t\t\treturn \"↑\";\n\t\tcase \"down\":\n\t\t\treturn \"↓\";\n\t\tcase \"pageup\":\n\t\t\treturn \"PgUp\";\n\t\tcase \"pagedown\":\n\t\t\treturn \"PgDn\";\n\t\tdefault:\n\t\t\treturn part.length === 1 ? part.toUpperCase() : part.charAt(0).toUpperCase() + part.slice(1);\n\t}\n}\n\n/** Format one key id for compact UI labels without treating literal `/` or `+` keys as separators. */\nexport function formatKeyLabel(key: string): string {\n\tif (!key) return \"\";\n\tconst modifiers: string[] = [];\n\tlet remaining = key;\n\twhile (true) {\n\t\tconst match = /^(ctrl|shift|alt)\\+/i.exec(remaining);\n\t\tif (!match) break;\n\t\tmodifiers.push(formatKeyLabelPart(match[1]));\n\t\tremaining = remaining.slice(match[0].length);\n\t}\n\tconst keyPart = formatKeyLabelPart(remaining || \"+\");\n\treturn [...modifiers, keyPart].join(\"+\");\n}\n\nexport interface KeyLabelOptions {\n\t/** Use an injected manager for custom components instead of the global manager. */\n\tkeybindings?: Pick<KeybindingsManager, \"getKeys\">;\n\t/** Display only when the action has no configured key. */\n\tfallback?: string;\n}\n\n/** Display the first configured key for a compact hint, with human-friendly names. */\nexport function keyLabel(keybinding: Keybinding, options: KeyLabelOptions = {}): string {\n\tconst key = (options.keybindings ?? getKeybindings()).getKeys(keybinding)[0];\n\treturn key !== undefined ? formatKeyLabel(key) : options.fallback ? formatKeyLabel(options.fallback) : \"\";\n}\n\nexport function keyText(keybinding: Keybinding): string {\n\treturn formatKeys(getKeybindings().getKeys(keybinding));\n}\n\nexport function keyDisplayText(keybinding: Keybinding): string {\n\treturn formatKeys(getKeybindings().getKeys(keybinding), { capitalize: true });\n}\n\nexport function keyHint(keybinding: Keybinding, description: string): string {\n\treturn theme.fg(\"dim\", keyText(keybinding)) + theme.fg(\"muted\", ` ${description}`);\n}\n\nexport function rawKeyHint(key: string, description: string): string {\n\treturn theme.fg(\"dim\", formatKeyText(key)) + theme.fg(\"muted\", ` ${description}`);\n}\n"]}
|
|
@@ -21,6 +21,62 @@ function formatKeys(keys, options = {}) {
|
|
|
21
21
|
return "";
|
|
22
22
|
return formatKeyText(keys.join("/"), options);
|
|
23
23
|
}
|
|
24
|
+
function formatKeyLabelPart(part) {
|
|
25
|
+
const normalized = part.toLowerCase();
|
|
26
|
+
switch (normalized) {
|
|
27
|
+
case "alt":
|
|
28
|
+
return process.platform === "darwin" ? "Option" : "Alt";
|
|
29
|
+
case "ctrl":
|
|
30
|
+
return "Ctrl";
|
|
31
|
+
case "shift":
|
|
32
|
+
return "Shift";
|
|
33
|
+
case "escape":
|
|
34
|
+
case "esc":
|
|
35
|
+
return "Esc";
|
|
36
|
+
case "enter":
|
|
37
|
+
case "return":
|
|
38
|
+
return "Enter";
|
|
39
|
+
case "space":
|
|
40
|
+
return "Space";
|
|
41
|
+
case "tab":
|
|
42
|
+
return "Tab";
|
|
43
|
+
case "left":
|
|
44
|
+
return "←";
|
|
45
|
+
case "right":
|
|
46
|
+
return "→";
|
|
47
|
+
case "up":
|
|
48
|
+
return "↑";
|
|
49
|
+
case "down":
|
|
50
|
+
return "↓";
|
|
51
|
+
case "pageup":
|
|
52
|
+
return "PgUp";
|
|
53
|
+
case "pagedown":
|
|
54
|
+
return "PgDn";
|
|
55
|
+
default:
|
|
56
|
+
return part.length === 1 ? part.toUpperCase() : part.charAt(0).toUpperCase() + part.slice(1);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** Format one key id for compact UI labels without treating literal `/` or `+` keys as separators. */
|
|
60
|
+
export function formatKeyLabel(key) {
|
|
61
|
+
if (!key)
|
|
62
|
+
return "";
|
|
63
|
+
const modifiers = [];
|
|
64
|
+
let remaining = key;
|
|
65
|
+
while (true) {
|
|
66
|
+
const match = /^(ctrl|shift|alt)\+/i.exec(remaining);
|
|
67
|
+
if (!match)
|
|
68
|
+
break;
|
|
69
|
+
modifiers.push(formatKeyLabelPart(match[1]));
|
|
70
|
+
remaining = remaining.slice(match[0].length);
|
|
71
|
+
}
|
|
72
|
+
const keyPart = formatKeyLabelPart(remaining || "+");
|
|
73
|
+
return [...modifiers, keyPart].join("+");
|
|
74
|
+
}
|
|
75
|
+
/** Display the first configured key for a compact hint, with human-friendly names. */
|
|
76
|
+
export function keyLabel(keybinding, options = {}) {
|
|
77
|
+
const key = (options.keybindings ?? getKeybindings()).getKeys(keybinding)[0];
|
|
78
|
+
return key !== undefined ? formatKeyLabel(key) : options.fallback ? formatKeyLabel(options.fallback) : "";
|
|
79
|
+
}
|
|
24
80
|
export function keyText(keybinding) {
|
|
25
81
|
return formatKeys(getKeybindings().getKeys(keybinding));
|
|
26
82
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keybinding-hints.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/keybinding-hints.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"keybinding-hints.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/keybinding-hints.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Utilities for formatting keybinding hints in the UI.\n */\n\nimport { getKeybindings, type Keybinding, type KeybindingsManager, type KeyId } from \"@earendil-works/pi-tui\";\nimport { theme } from \"../theme/theme.ts\";\n\nexport interface KeyTextFormatOptions {\n\tcapitalize?: boolean;\n}\n\nfunction formatKeyPart(part: string, options: KeyTextFormatOptions): string {\n\tconst displayPart = process.platform === \"darwin\" && part.toLowerCase() === \"alt\" ? \"option\" : part;\n\treturn options.capitalize ? displayPart.charAt(0).toUpperCase() + displayPart.slice(1) : displayPart;\n}\n\nexport function formatKeyText(key: string, options: KeyTextFormatOptions = {}): string {\n\treturn key\n\t\t.split(\"/\")\n\t\t.map((k) =>\n\t\t\tk\n\t\t\t\t.split(\"+\")\n\t\t\t\t.map((part) => formatKeyPart(part, options))\n\t\t\t\t.join(\"+\"),\n\t\t)\n\t\t.join(\"/\");\n}\n\nfunction formatKeys(keys: KeyId[], options: KeyTextFormatOptions = {}): string {\n\tif (keys.length === 0) return \"\";\n\treturn formatKeyText(keys.join(\"/\"), options);\n}\n\nfunction formatKeyLabelPart(part: string): string {\n\tconst normalized = part.toLowerCase();\n\tswitch (normalized) {\n\t\tcase \"alt\":\n\t\t\treturn process.platform === \"darwin\" ? \"Option\" : \"Alt\";\n\t\tcase \"ctrl\":\n\t\t\treturn \"Ctrl\";\n\t\tcase \"shift\":\n\t\t\treturn \"Shift\";\n\t\tcase \"escape\":\n\t\tcase \"esc\":\n\t\t\treturn \"Esc\";\n\t\tcase \"enter\":\n\t\tcase \"return\":\n\t\t\treturn \"Enter\";\n\t\tcase \"space\":\n\t\t\treturn \"Space\";\n\t\tcase \"tab\":\n\t\t\treturn \"Tab\";\n\t\tcase \"left\":\n\t\t\treturn \"←\";\n\t\tcase \"right\":\n\t\t\treturn \"→\";\n\t\tcase \"up\":\n\t\t\treturn \"↑\";\n\t\tcase \"down\":\n\t\t\treturn \"↓\";\n\t\tcase \"pageup\":\n\t\t\treturn \"PgUp\";\n\t\tcase \"pagedown\":\n\t\t\treturn \"PgDn\";\n\t\tdefault:\n\t\t\treturn part.length === 1 ? part.toUpperCase() : part.charAt(0).toUpperCase() + part.slice(1);\n\t}\n}\n\n/** Format one key id for compact UI labels without treating literal `/` or `+` keys as separators. */\nexport function formatKeyLabel(key: string): string {\n\tif (!key) return \"\";\n\tconst modifiers: string[] = [];\n\tlet remaining = key;\n\twhile (true) {\n\t\tconst match = /^(ctrl|shift|alt)\\+/i.exec(remaining);\n\t\tif (!match) break;\n\t\tmodifiers.push(formatKeyLabelPart(match[1]));\n\t\tremaining = remaining.slice(match[0].length);\n\t}\n\tconst keyPart = formatKeyLabelPart(remaining || \"+\");\n\treturn [...modifiers, keyPart].join(\"+\");\n}\n\nexport interface KeyLabelOptions {\n\t/** Use an injected manager for custom components instead of the global manager. */\n\tkeybindings?: Pick<KeybindingsManager, \"getKeys\">;\n\t/** Display only when the action has no configured key. */\n\tfallback?: string;\n}\n\n/** Display the first configured key for a compact hint, with human-friendly names. */\nexport function keyLabel(keybinding: Keybinding, options: KeyLabelOptions = {}): string {\n\tconst key = (options.keybindings ?? getKeybindings()).getKeys(keybinding)[0];\n\treturn key !== undefined ? formatKeyLabel(key) : options.fallback ? formatKeyLabel(options.fallback) : \"\";\n}\n\nexport function keyText(keybinding: Keybinding): string {\n\treturn formatKeys(getKeybindings().getKeys(keybinding));\n}\n\nexport function keyDisplayText(keybinding: Keybinding): string {\n\treturn formatKeys(getKeybindings().getKeys(keybinding), { capitalize: true });\n}\n\nexport function keyHint(keybinding: Keybinding, description: string): string {\n\treturn theme.fg(\"dim\", keyText(keybinding)) + theme.fg(\"muted\", ` ${description}`);\n}\n\nexport function rawKeyHint(key: string, description: string): string {\n\treturn theme.fg(\"dim\", formatKeyText(key)) + theme.fg(\"muted\", ` ${description}`);\n}\n"]}
|
|
@@ -68,6 +68,8 @@ export declare class SessionSelectorComponent extends Container implements Focus
|
|
|
68
68
|
private currentLoading;
|
|
69
69
|
private allLoading;
|
|
70
70
|
private allLoadSeq;
|
|
71
|
+
private relativeAgeInterval;
|
|
72
|
+
private disposed;
|
|
71
73
|
private mode;
|
|
72
74
|
private renameInput;
|
|
73
75
|
private renameTargetPath;
|
|
@@ -90,6 +92,7 @@ export declare class SessionSelectorComponent extends Container implements Focus
|
|
|
90
92
|
private refreshSessionsAfterMutation;
|
|
91
93
|
private toggleScope;
|
|
92
94
|
getSessionList(): SessionList;
|
|
95
|
+
dispose(): void;
|
|
93
96
|
}
|
|
94
97
|
export {};
|
|
95
98
|
//# sourceMappingURL=session-selector.d.ts.map
|