@selesai/code 0.8.2 → 0.8.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 +10 -0
- package/README.md +4 -2
- package/dist/cli/args.d.ts +2 -0
- package/dist/cli/args.js +18 -0
- package/dist/cli/config-selector.js +2 -2
- package/dist/cli/startup-ui.d.ts +1 -1
- package/dist/cli/startup-ui.js +2 -2
- package/dist/core/keybindings.d.ts +44 -4
- package/dist/core/llama/provider.js +22 -7
- package/dist/core/model-resolver.js +2 -0
- package/dist/core/model-runtime.d.ts +4 -2
- package/dist/core/model-runtime.js +23 -2
- package/dist/core/remote-catalog-provider.js +72 -67
- package/dist/core/settings-manager.d.ts +8 -0
- package/dist/core/settings-manager.js +18 -0
- package/dist/extensions/pi-powerline-footer/CHANGELOG.md +2 -1
- package/dist/extensions/pi-powerline-footer/index.ts +3 -272
- package/dist/extensions/pi-powerline-footer/package.json +4 -5
- package/dist/extensions/pi-powerline-footer/powerline-config.ts +0 -6
- package/dist/extensions/pi-powerline-footer/tests/fixed-editor-border.test.ts +2 -2
- package/dist/extensions/pi-powerline-footer/tests/guide.test.ts +1 -1
- package/dist/extensions/pi-powerline-footer/tsconfig.json +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/main.js +1 -0
- package/dist/modes/interactive/components/settings-selector.d.ts +6 -2
- package/dist/modes/interactive/components/settings-selector.js +20 -0
- package/dist/modes/interactive/interactive-mode.d.ts +28 -0
- package/dist/modes/interactive/interactive-mode.js +214 -28
- package/dist/modes/interactive/theme/dark.json +1 -0
- package/dist/modes/interactive/theme/light.json +1 -0
- package/dist/modes/interactive/theme/theme-controller.d.ts +3 -0
- package/dist/modes/interactive/theme/theme-controller.js +10 -1
- package/dist/modes/interactive/theme/theme.d.ts +2 -2
- package/dist/modes/interactive/theme/theme.js +24 -6
- package/docs/keybindings.md +35 -7
- package/docs/settings.md +16 -14
- package/docs/usage.md +5 -0
- package/package.json +5 -5
- package/dist/extensions/pi-powerline-footer/fixed-editor/cluster.ts +0 -113
- package/dist/extensions/pi-powerline-footer/fixed-editor/terminal-split.ts +0 -1080
- package/dist/extensions/pi-powerline-footer/tests/fixed-editor.test.ts +0 -1452
- package/dist/extensions/pi-powerline-footer/tests/jump-shortcuts.test.ts +0 -49
|
@@ -70,8 +70,6 @@ import {
|
|
|
70
70
|
} from "./working-vibes.ts";
|
|
71
71
|
import { setupTpsTracker } from "./tps.ts";
|
|
72
72
|
import { readAsyncSubagentUsage } from "./session-usage.ts";
|
|
73
|
-
import { renderFixedEditorCluster } from "./fixed-editor/cluster.ts";
|
|
74
|
-
import { emergencyTerminalModeReset, TerminalSplitCompositor } from "./fixed-editor/terminal-split.ts";
|
|
75
73
|
import { PowerlineQueueStore, currentQueueContext, formatIdeaIssuePrompt, formatQueueDeliveryText, parseCompactQueuedPrompt, parseSigilIdeaCapture, parseTargetPrefix, targetForIdea } from "./queue/store.ts";
|
|
76
74
|
import type { PowerlineQueueItem, QueueContext, QueueIntent, QueueTarget } from "./queue/types.ts";
|
|
77
75
|
|
|
@@ -82,8 +80,6 @@ import type { PowerlineQueueItem, QueueContext, QueueIntent, QueueTarget } from
|
|
|
82
80
|
let config: PowerlineConfig = {
|
|
83
81
|
preset: "default",
|
|
84
82
|
customItems: [],
|
|
85
|
-
mouseScroll: true,
|
|
86
|
-
fixedEditor: true,
|
|
87
83
|
fixedEditorPromptGlyph: "",
|
|
88
84
|
disabledSegments: [],
|
|
89
85
|
invalidDisabledSegments: [],
|
|
@@ -109,38 +105,18 @@ export interface PowerlineShortcuts {
|
|
|
109
105
|
cutEditor: ShortcutBinding;
|
|
110
106
|
ideaCapture: ShortcutBinding;
|
|
111
107
|
queueOpen: ShortcutBinding;
|
|
112
|
-
jumpPreviousUserMessage: ShortcutBinding;
|
|
113
|
-
jumpNextUserMessage: ShortcutBinding;
|
|
114
|
-
jumpPreviousLlmMessage: ShortcutBinding;
|
|
115
|
-
jumpNextLlmMessage: ShortcutBinding;
|
|
116
|
-
jumpChatBottom: ShortcutBinding;
|
|
117
|
-
scrollChatUp: ShortcutBinding;
|
|
118
|
-
scrollChatDown: ShortcutBinding;
|
|
119
108
|
editorStart: ShortcutBinding;
|
|
120
109
|
editorEnd: ShortcutBinding;
|
|
121
110
|
}
|
|
122
111
|
|
|
123
112
|
type PowerlineShortcutKey = keyof PowerlineShortcuts;
|
|
124
|
-
type ChatJumpShortcutKey = Extract<PowerlineShortcutKey,
|
|
125
|
-
| "jumpPreviousUserMessage"
|
|
126
|
-
| "jumpNextUserMessage"
|
|
127
|
-
| "jumpPreviousLlmMessage"
|
|
128
|
-
| "jumpNextLlmMessage"
|
|
129
|
-
| "jumpChatBottom"
|
|
130
|
-
>;
|
|
131
|
-
type ChatJumpRole = "user" | "assistant";
|
|
132
|
-
type ChatJumpDirection = "previous" | "next";
|
|
133
|
-
type ChatJumpShortcutAction =
|
|
134
|
-
| { kind: "message"; role: ChatJumpRole; direction: ChatJumpDirection }
|
|
135
|
-
| { kind: "bottom" };
|
|
136
113
|
type PowerlineShortcutAction =
|
|
137
114
|
| { kind: "stashHistory" }
|
|
138
115
|
| { kind: "copyEditor" }
|
|
139
116
|
| { kind: "cutEditor" }
|
|
140
117
|
| { kind: "ideaCapture" }
|
|
141
118
|
| { kind: "queueOpen" }
|
|
142
|
-
| { kind: "bashMode" }
|
|
143
|
-
| { kind: "chat"; action: ChatJumpShortcutAction };
|
|
119
|
+
| { kind: "bashMode" };
|
|
144
120
|
const STASH_HISTORY_LIMIT = 12;
|
|
145
121
|
const PROJECT_PROMPT_HISTORY_LIMIT = 50;
|
|
146
122
|
const STASH_PREVIEW_WIDTH = 72;
|
|
@@ -150,13 +126,6 @@ const DEFAULT_SHORTCUTS: PowerlineShortcuts = {
|
|
|
150
126
|
cutEditor: "ctrl+alt+x",
|
|
151
127
|
ideaCapture: null,
|
|
152
128
|
queueOpen: "ctrl+alt+q",
|
|
153
|
-
jumpPreviousUserMessage: "ctrl+shift+u",
|
|
154
|
-
jumpNextUserMessage: "ctrl+shift+i",
|
|
155
|
-
jumpPreviousLlmMessage: "ctrl+alt+,",
|
|
156
|
-
jumpNextLlmMessage: "ctrl+alt+.",
|
|
157
|
-
jumpChatBottom: "ctrl+shift+g",
|
|
158
|
-
scrollChatUp: "super+up",
|
|
159
|
-
scrollChatDown: "super+down",
|
|
160
129
|
editorStart: "super+shift+up",
|
|
161
130
|
editorEnd: "super+shift+down",
|
|
162
131
|
};
|
|
@@ -165,38 +134,7 @@ const DEFAULT_BASH_MODE_SETTINGS = {
|
|
|
165
134
|
transcriptMaxLines: 2000,
|
|
166
135
|
transcriptMaxBytes: 512 * 1024,
|
|
167
136
|
} as const satisfies BashModeSettings;
|
|
168
|
-
const SHORTCUT_KEYS: PowerlineShortcutKey[] = ["stashHistory", "copyEditor", "cutEditor", "ideaCapture", "queueOpen", "
|
|
169
|
-
const CHAT_JUMP_SHORTCUTS: Array<{
|
|
170
|
-
shortcutKey: ChatJumpShortcutKey;
|
|
171
|
-
description: string;
|
|
172
|
-
action: ChatJumpShortcutAction;
|
|
173
|
-
}> = [
|
|
174
|
-
{
|
|
175
|
-
shortcutKey: "jumpPreviousUserMessage",
|
|
176
|
-
description: "Jump to previous user message",
|
|
177
|
-
action: { kind: "message", role: "user", direction: "previous" },
|
|
178
|
-
},
|
|
179
|
-
{
|
|
180
|
-
shortcutKey: "jumpNextUserMessage",
|
|
181
|
-
description: "Jump to next user message",
|
|
182
|
-
action: { kind: "message", role: "user", direction: "next" },
|
|
183
|
-
},
|
|
184
|
-
{
|
|
185
|
-
shortcutKey: "jumpPreviousLlmMessage",
|
|
186
|
-
description: "Jump to previous LLM message",
|
|
187
|
-
action: { kind: "message", role: "assistant", direction: "previous" },
|
|
188
|
-
},
|
|
189
|
-
{
|
|
190
|
-
shortcutKey: "jumpNextLlmMessage",
|
|
191
|
-
description: "Jump to next LLM message",
|
|
192
|
-
action: { kind: "message", role: "assistant", direction: "next" },
|
|
193
|
-
},
|
|
194
|
-
{
|
|
195
|
-
shortcutKey: "jumpChatBottom",
|
|
196
|
-
description: "Jump chat to bottom",
|
|
197
|
-
action: { kind: "bottom" },
|
|
198
|
-
},
|
|
199
|
-
];
|
|
137
|
+
const SHORTCUT_KEYS: PowerlineShortcutKey[] = ["stashHistory", "copyEditor", "cutEditor", "ideaCapture", "queueOpen", "editorStart", "editorEnd"];
|
|
200
138
|
const APP_RESERVED_SHORTCUTS = [
|
|
201
139
|
"escape",
|
|
202
140
|
"ctrl+c",
|
|
@@ -1070,11 +1008,6 @@ export default function powerlineFooter(pi: ExtensionAPI) {
|
|
|
1070
1008
|
let bashTranscript = new BashTranscriptStore(bashModeSettings);
|
|
1071
1009
|
let bashCompletionEngine = new BashCompletionEngine();
|
|
1072
1010
|
let shellSession: ManagedShellSession | null = null;
|
|
1073
|
-
let fixedEditorCompositor: TerminalSplitCompositor | null = null;
|
|
1074
|
-
let fixedStatusContainer: any = null;
|
|
1075
|
-
let fixedEditorContainer: any = null;
|
|
1076
|
-
let fixedWidgetContainerAbove: any = null;
|
|
1077
|
-
let fixedWidgetContainerBelow: any = null;
|
|
1078
1011
|
const queueStore = new PowerlineQueueStore();
|
|
1079
1012
|
let powerlineCompacting = false;
|
|
1080
1013
|
let deliverAfterRetrySettles = false;
|
|
@@ -2100,12 +2033,7 @@ export default function powerlineFooter(pi: ExtensionAPI) {
|
|
|
2100
2033
|
return { kind: "bashMode" };
|
|
2101
2034
|
}
|
|
2102
2035
|
|
|
2103
|
-
|
|
2104
|
-
return chatJumpAction ? { kind: "chat", action: chatJumpAction } : null;
|
|
2105
|
-
}
|
|
2106
|
-
|
|
2107
|
-
function getChatJumpShortcutAction(data: string): ChatJumpShortcutAction | null {
|
|
2108
|
-
return CHAT_JUMP_SHORTCUTS.find(({ shortcutKey }) => matchesConfiguredShortcut(data, resolvedShortcuts[shortcutKey]))?.action ?? null;
|
|
2036
|
+
return null;
|
|
2109
2037
|
}
|
|
2110
2038
|
|
|
2111
2039
|
function runPowerlineShortcut(ctx: any, action: PowerlineShortcutAction): void {
|
|
@@ -2146,13 +2074,6 @@ export default function powerlineFooter(pi: ExtensionAPI) {
|
|
|
2146
2074
|
void setBashModeActive(!bashModeActive, ctx);
|
|
2147
2075
|
return;
|
|
2148
2076
|
}
|
|
2149
|
-
|
|
2150
|
-
if (action.action.kind === "bottom") {
|
|
2151
|
-
jumpChatToBottom(ctx);
|
|
2152
|
-
return;
|
|
2153
|
-
}
|
|
2154
|
-
|
|
2155
|
-
jumpToChatMessage(ctx, action.action.role, action.action.direction);
|
|
2156
2077
|
}
|
|
2157
2078
|
|
|
2158
2079
|
function stashOrRestoreEditorText(ctx: any): void {
|
|
@@ -2988,192 +2909,6 @@ export default function powerlineFooter(pi: ExtensionAPI) {
|
|
|
2988
2909
|
return [truncateToWidth(line, width, "…")];
|
|
2989
2910
|
}
|
|
2990
2911
|
|
|
2991
|
-
function teardownFixedEditorCompositor(options?: { resetExtendedKeyboardModes?: boolean }) {
|
|
2992
|
-
const hadCompositor = fixedEditorCompositor !== null;
|
|
2993
|
-
fixedEditorCompositor?.dispose(options);
|
|
2994
|
-
if (!hadCompositor && options?.resetExtendedKeyboardModes) {
|
|
2995
|
-
try {
|
|
2996
|
-
process.stdout.write(emergencyTerminalModeReset());
|
|
2997
|
-
} catch {
|
|
2998
|
-
// Shutdown cleanup cannot surface useful terminal write failures.
|
|
2999
|
-
}
|
|
3000
|
-
}
|
|
3001
|
-
fixedEditorCompositor = null;
|
|
3002
|
-
fixedStatusContainer = null;
|
|
3003
|
-
fixedEditorContainer = null;
|
|
3004
|
-
fixedWidgetContainerAbove = null;
|
|
3005
|
-
fixedWidgetContainerBelow = null;
|
|
3006
|
-
}
|
|
3007
|
-
|
|
3008
|
-
function findContainerWithChild(tui: any, child: any): { container: any; index: number } | null {
|
|
3009
|
-
const children = Array.isArray(tui?.children) ? tui.children : [];
|
|
3010
|
-
const index = children.findIndex((candidate: any) => Array.isArray(candidate?.children) && candidate.children.includes(child));
|
|
3011
|
-
if (index === -1) return null;
|
|
3012
|
-
|
|
3013
|
-
return { container: children[index], index };
|
|
3014
|
-
}
|
|
3015
|
-
|
|
3016
|
-
function installFixedEditorCompositor(ctx: any, tui: any) {
|
|
3017
|
-
teardownFixedEditorCompositor();
|
|
3018
|
-
|
|
3019
|
-
if (!ctx.hasUI || !config.fixedEditor) return;
|
|
3020
|
-
if (!tui?.terminal || typeof tui.terminal.write !== "function") {
|
|
3021
|
-
throw new Error("[powerline-footer] Fixed editor compositor could not find tui.terminal.write()");
|
|
3022
|
-
}
|
|
3023
|
-
if (!currentEditor) {
|
|
3024
|
-
throw new Error("[powerline-footer] Fixed editor compositor expected the custom editor to be installed first");
|
|
3025
|
-
}
|
|
3026
|
-
|
|
3027
|
-
const editorContainerMatch = findContainerWithChild(tui, currentEditor);
|
|
3028
|
-
if (!editorContainerMatch) {
|
|
3029
|
-
throw new Error("[powerline-footer] Fixed editor compositor could not find the editor container in TUI children");
|
|
3030
|
-
}
|
|
3031
|
-
|
|
3032
|
-
const tuiChildren = Array.isArray(tui.children) ? tui.children : [];
|
|
3033
|
-
fixedEditorContainer = editorContainerMatch.container;
|
|
3034
|
-
const statusContainerCandidate = tuiChildren[editorContainerMatch.index - 2] ?? null;
|
|
3035
|
-
fixedStatusContainer = statusContainerCandidate && typeof statusContainerCandidate.render === "function"
|
|
3036
|
-
? statusContainerCandidate
|
|
3037
|
-
: null;
|
|
3038
|
-
fixedWidgetContainerAbove = tuiChildren[editorContainerMatch.index - 1] ?? null;
|
|
3039
|
-
fixedWidgetContainerBelow = tuiChildren[editorContainerMatch.index + 1] ?? null;
|
|
3040
|
-
|
|
3041
|
-
let compositor: TerminalSplitCompositor;
|
|
3042
|
-
compositor = new TerminalSplitCompositor({
|
|
3043
|
-
tui,
|
|
3044
|
-
terminal: tui.terminal,
|
|
3045
|
-
mouseScroll: config.mouseScroll,
|
|
3046
|
-
keyboardScrollShortcuts: {
|
|
3047
|
-
up: resolvedShortcuts.scrollChatUp ?? "super+up",
|
|
3048
|
-
down: resolvedShortcuts.scrollChatDown ?? "super+down",
|
|
3049
|
-
},
|
|
3050
|
-
shouldCaptureKeyboardScroll: () => Reflect.get(tui, "focusedComponent") === currentEditor,
|
|
3051
|
-
onCopySelection: (text) => copyTextToClipboard(ctx, text),
|
|
3052
|
-
getShowHardwareCursor: () => typeof tui.getShowHardwareCursor === "function" && tui.getShowHardwareCursor(),
|
|
3053
|
-
renderCluster: (width, terminalRows) => {
|
|
3054
|
-
const theme = currentCtx?.ui?.theme ?? ctx.ui.theme;
|
|
3055
|
-
const statusContainerLines = fixedStatusContainer
|
|
3056
|
-
? compositor.renderHidden(fixedStatusContainer, width).filter((line) => visibleWidth(line) > 0)
|
|
3057
|
-
: [];
|
|
3058
|
-
const aboveWidgetLines = fixedWidgetContainerAbove ? compositor.renderHidden(fixedWidgetContainerAbove, width) : [];
|
|
3059
|
-
const belowWidgetLines = fixedWidgetContainerBelow ? compositor.renderHidden(fixedWidgetContainerBelow, width) : [];
|
|
3060
|
-
return renderFixedEditorCluster({
|
|
3061
|
-
width,
|
|
3062
|
-
terminalRows,
|
|
3063
|
-
statusLines: [...aboveWidgetLines, ...renderPowerlineStatusLines(width), ...statusContainerLines],
|
|
3064
|
-
topLines: renderPowerlinePrimaryLines(width, theme),
|
|
3065
|
-
editorLines: fixedEditorContainer ? compositor.renderHidden(fixedEditorContainer, width) : [],
|
|
3066
|
-
secondaryLines: [...renderPowerlineSecondaryLines(width, theme), ...belowWidgetLines],
|
|
3067
|
-
transcriptLines: renderBashTranscriptLines(width, theme),
|
|
3068
|
-
lastPromptLines: renderLastPromptLines(width),
|
|
3069
|
-
});
|
|
3070
|
-
},
|
|
3071
|
-
});
|
|
3072
|
-
|
|
3073
|
-
fixedEditorCompositor = compositor;
|
|
3074
|
-
if (fixedStatusContainer?.render) compositor.hideRenderable(fixedStatusContainer);
|
|
3075
|
-
if (fixedWidgetContainerAbove?.render) compositor.hideRenderable(fixedWidgetContainerAbove);
|
|
3076
|
-
compositor.hideRenderable(fixedEditorContainer);
|
|
3077
|
-
if (fixedWidgetContainerBelow?.render) compositor.hideRenderable(fixedWidgetContainerBelow);
|
|
3078
|
-
compositor.install();
|
|
3079
|
-
tui.requestRender(true);
|
|
3080
|
-
}
|
|
3081
|
-
|
|
3082
|
-
function isChatMessageComponentForRole(component: unknown, role: ChatJumpRole): boolean {
|
|
3083
|
-
const componentName = typeof component === "object" && component !== null ? component.constructor?.name : undefined;
|
|
3084
|
-
if (role === "assistant") {
|
|
3085
|
-
return componentName === "AssistantMessageComponent";
|
|
3086
|
-
}
|
|
3087
|
-
|
|
3088
|
-
return componentName === "UserMessageComponent" || componentName === "SkillInvocationMessageComponent";
|
|
3089
|
-
}
|
|
3090
|
-
|
|
3091
|
-
function renderLineCount(component: unknown, width: number): number {
|
|
3092
|
-
if (typeof component !== "object" || component === null) return 0;
|
|
3093
|
-
|
|
3094
|
-
const render = Reflect.get(component, "render");
|
|
3095
|
-
if (typeof render !== "function") return 0;
|
|
3096
|
-
|
|
3097
|
-
const lines = render.call(component, width);
|
|
3098
|
-
return Array.isArray(lines) ? lines.length : 0;
|
|
3099
|
-
}
|
|
3100
|
-
|
|
3101
|
-
function collectMessageStartLines(component: unknown, width: number, role: ChatJumpRole, offset: number): {
|
|
3102
|
-
targets: number[];
|
|
3103
|
-
lineCount: number;
|
|
3104
|
-
} {
|
|
3105
|
-
const lineCount = renderLineCount(component, width);
|
|
3106
|
-
if (isChatMessageComponentForRole(component, role)) {
|
|
3107
|
-
return { targets: [offset], lineCount };
|
|
3108
|
-
}
|
|
3109
|
-
|
|
3110
|
-
const children = typeof component === "object" && component !== null ? Reflect.get(component, "children") : null;
|
|
3111
|
-
if (!Array.isArray(children) || children.length === 0) {
|
|
3112
|
-
return { targets: [], lineCount };
|
|
3113
|
-
}
|
|
3114
|
-
|
|
3115
|
-
const targets: number[] = [];
|
|
3116
|
-
let childOffset = offset;
|
|
3117
|
-
let childrenLineCount = 0;
|
|
3118
|
-
for (const child of children) {
|
|
3119
|
-
const result = collectMessageStartLines(child, width, role, childOffset);
|
|
3120
|
-
targets.push(...result.targets);
|
|
3121
|
-
childOffset += result.lineCount;
|
|
3122
|
-
childrenLineCount += result.lineCount;
|
|
3123
|
-
}
|
|
3124
|
-
|
|
3125
|
-
return { targets, lineCount: Math.max(lineCount, childrenLineCount) };
|
|
3126
|
-
}
|
|
3127
|
-
|
|
3128
|
-
function collectChatMessageStartLines(role: ChatJumpRole): number[] {
|
|
3129
|
-
const children = Array.isArray(tuiRef?.children) ? tuiRef.children : [];
|
|
3130
|
-
const width = Math.max(1, tuiRef?.terminal?.columns ?? 80);
|
|
3131
|
-
const targets: number[] = [];
|
|
3132
|
-
let offset = 0;
|
|
3133
|
-
|
|
3134
|
-
for (const child of children) {
|
|
3135
|
-
const result = collectMessageStartLines(child, width, role, offset);
|
|
3136
|
-
targets.push(...result.targets);
|
|
3137
|
-
offset += result.lineCount;
|
|
3138
|
-
}
|
|
3139
|
-
|
|
3140
|
-
return [...new Set(targets)].sort((a, b) => a - b);
|
|
3141
|
-
}
|
|
3142
|
-
|
|
3143
|
-
function jumpToChatMessage(ctx: any, role: ChatJumpRole, direction: ChatJumpDirection): void {
|
|
3144
|
-
if (!fixedEditorCompositor) {
|
|
3145
|
-
ctx.ui.notify("Chat message jumps require /powerline fixed-editor on", "warning");
|
|
3146
|
-
return;
|
|
3147
|
-
}
|
|
3148
|
-
|
|
3149
|
-
const targets = collectChatMessageStartLines(role);
|
|
3150
|
-
const label = role === "assistant" ? "LLM" : "user";
|
|
3151
|
-
if (targets.length === 0) {
|
|
3152
|
-
ctx.ui.notify(`No ${label} messages found`, "info");
|
|
3153
|
-
return;
|
|
3154
|
-
}
|
|
3155
|
-
|
|
3156
|
-
const jumped = direction === "previous"
|
|
3157
|
-
? fixedEditorCompositor.jumpToPreviousRootTarget(targets)
|
|
3158
|
-
: fixedEditorCompositor.jumpToNextRootTarget(targets);
|
|
3159
|
-
if (!jumped) {
|
|
3160
|
-
ctx.ui.notify(`No ${direction} ${label} message`, "info");
|
|
3161
|
-
}
|
|
3162
|
-
}
|
|
3163
|
-
|
|
3164
|
-
function jumpChatToBottom(ctx: any): void {
|
|
3165
|
-
if (!fixedEditorCompositor) {
|
|
3166
|
-
ctx.ui.notify("Chat bottom jump requires /powerline fixed-editor on", "warning");
|
|
3167
|
-
return;
|
|
3168
|
-
}
|
|
3169
|
-
|
|
3170
|
-
fixedEditorCompositor.jumpToRootBottom();
|
|
3171
|
-
}
|
|
3172
|
-
|
|
3173
|
-
function followSubmittedEditorToBottom(): void {
|
|
3174
|
-
fixedEditorCompositor?.jumpToRootBottom();
|
|
3175
|
-
}
|
|
3176
|
-
|
|
3177
2912
|
function installPowerlineWidgets(ctx: any) {
|
|
3178
2913
|
ctx.ui.setWidget("powerline-status", () => ({
|
|
3179
2914
|
dispose() {},
|
|
@@ -3424,12 +3159,8 @@ export default function powerlineFooter(pi: ExtensionAPI) {
|
|
|
3424
3159
|
}
|
|
3425
3160
|
|
|
3426
3161
|
attachAutocompleteProvider();
|
|
3427
|
-
const followUpText = keybindings.matches(data, "app.message.followUp") ? getCurrentEditorText(ctx, editor) : "";
|
|
3428
3162
|
scheduleDismissWelcome(ctx);
|
|
3429
3163
|
originalHandleInput(data);
|
|
3430
|
-
if (hasNonWhitespaceText(followUpText) && !hasNonWhitespaceText(getCurrentEditorText(ctx, editor))) {
|
|
3431
|
-
followSubmittedEditorToBottom();
|
|
3432
|
-
}
|
|
3433
3164
|
};
|
|
3434
3165
|
|
|
3435
3166
|
const originalRender = editor.render.bind(editor);
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
"*.json",
|
|
10
10
|
"!progress.md",
|
|
11
11
|
"bash-mode/**/*.ts",
|
|
12
|
-
"fixed-editor/**/*.ts",
|
|
13
12
|
"queue/**/*.ts"
|
|
14
13
|
],
|
|
15
14
|
"keywords": [
|
|
@@ -31,14 +30,14 @@
|
|
|
31
30
|
"test": "node --experimental-strip-types --test tests/**/*.test.ts"
|
|
32
31
|
},
|
|
33
32
|
"peerDependencies": {
|
|
34
|
-
"@earendil-works/pi-ai": ">=0.
|
|
33
|
+
"@earendil-works/pi-ai": ">=0.84.1 <0.85.0",
|
|
35
34
|
"@selesai/code": "*",
|
|
36
|
-
"@earendil-works/pi-tui": ">=0.
|
|
35
|
+
"@earendil-works/pi-tui": ">=0.84.1 <0.85.0"
|
|
37
36
|
},
|
|
38
37
|
"devDependencies": {
|
|
39
|
-
"@earendil-works/pi-ai": "^0.
|
|
38
|
+
"@earendil-works/pi-ai": "^0.84.1",
|
|
40
39
|
"@selesai/code": "^0.1.0",
|
|
41
|
-
"@earendil-works/pi-tui": "^0.
|
|
40
|
+
"@earendil-works/pi-tui": "^0.84.1",
|
|
42
41
|
"@types/node": "24.13.3",
|
|
43
42
|
"typescript": "5.9.3"
|
|
44
43
|
},
|
|
@@ -6,8 +6,6 @@ import type { ColorValue, CustomItemPosition, CustomStatusItem, PowerlinePlaceme
|
|
|
6
6
|
export interface PowerlineConfig {
|
|
7
7
|
preset: StatusLinePreset;
|
|
8
8
|
customItems: CustomStatusItem[];
|
|
9
|
-
mouseScroll: boolean;
|
|
10
|
-
fixedEditor: boolean;
|
|
11
9
|
fixedEditorPromptGlyph: string;
|
|
12
10
|
disabledSegments: StatusLineSegmentId[];
|
|
13
11
|
invalidDisabledSegments: string[];
|
|
@@ -305,8 +303,6 @@ export function parsePowerlineConfig(value: unknown, presets: readonly StatusLin
|
|
|
305
303
|
const defaultConfig: PowerlineConfig = {
|
|
306
304
|
preset: "default",
|
|
307
305
|
customItems: [],
|
|
308
|
-
mouseScroll: true,
|
|
309
|
-
fixedEditor: true,
|
|
310
306
|
fixedEditorPromptGlyph: "",
|
|
311
307
|
disabledSegments: [],
|
|
312
308
|
invalidDisabledSegments: [],
|
|
@@ -337,8 +333,6 @@ export function parsePowerlineConfig(value: unknown, presets: readonly StatusLin
|
|
|
337
333
|
return {
|
|
338
334
|
preset: normalizePreset(value.preset, presets) ?? defaultConfig.preset,
|
|
339
335
|
customItems,
|
|
340
|
-
mouseScroll: value.mouseScroll !== false,
|
|
341
|
-
fixedEditor: value.fixedEditor !== false,
|
|
342
336
|
fixedEditorPromptGlyph: typeof value.fixedEditorPromptGlyph === "string" ? value.fixedEditorPromptGlyph : defaultConfig.fixedEditorPromptGlyph,
|
|
343
337
|
disabledSegments,
|
|
344
338
|
invalidDisabledSegments,
|
|
@@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest";
|
|
|
2
2
|
import { initTheme, theme } from "../../../modes/interactive/theme/theme.ts";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* The powerline
|
|
5
|
+
* The powerline editor render draws its own border. It must respect the
|
|
6
6
|
* editor's `borderColor` (set by interactive-mode to the bash-mode / thinking
|
|
7
7
|
* color) instead of always using the gray "sep" color, so typing `!` visibly
|
|
8
8
|
* switches the input field to bash mode.
|
|
@@ -13,7 +13,7 @@ function powerlineBorderColor(editor: { borderColor: unknown }, s: string): stri
|
|
|
13
13
|
: `\x1b[38;5;244m${s}\x1b[0m`;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
describe("powerline
|
|
16
|
+
describe("powerline editor border respects editor.borderColor", () => {
|
|
17
17
|
it("uses the bash-mode color when interactive-mode sets it (typing !)", () => {
|
|
18
18
|
initTheme("dark");
|
|
19
19
|
const editorWithBashBorder = {
|
|
@@ -36,7 +36,7 @@ test("guide dismissal and compact preferences change automatic display", () => {
|
|
|
36
36
|
assert.equal(resolveGuideDisplayMode({ reason: "startup", hasUI: false, guideMode: "full" }), "none");
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
-
test("guide defaults to a compact header so
|
|
39
|
+
test("guide defaults to a compact header so the native dock stays uncluttered", () => {
|
|
40
40
|
const preferences = getGuidePreferences({});
|
|
41
41
|
assert.equal(preferences.mode, "compact");
|
|
42
42
|
assert.equal(resolveGuideDisplayMode({ reason: "startup", hasUI: true, guideMode: preferences.mode }), "compact");
|
package/dist/index.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export type { ResourceCollision, ResourceDiagnostic, ResourceLoader } from "./co
|
|
|
18
18
|
export { DefaultResourceLoader, loadProjectContextFiles } from "./core/resource-loader.ts";
|
|
19
19
|
export { AgentSessionRuntime, type AgentSessionRuntimeDiagnostic, type AgentSessionServices, type CreateAgentSessionFromServicesOptions, type CreateAgentSessionOptions, type CreateAgentSessionResult, type CreateAgentSessionRuntimeFactory, type CreateAgentSessionRuntimeResult, type CreateAgentSessionServicesOptions, createAgentSession, createAgentSessionFromServices, createAgentSessionRuntime, createAgentSessionServices, createBashTool, createCodingTools, createEditTool, createFindTool, createGrepTool, createLsTool, createReadOnlyTools, createReadTool, createWriteTool, type PromptTemplate, } from "./core/sdk.ts";
|
|
20
20
|
export { type BranchSummaryEntry, buildContextEntries, buildSessionContext, type CompactionEntry, CURRENT_SESSION_VERSION, type CustomEntry, type CustomMessageEntry, type FileEntry, getLatestCompactionEntry, type ModelChangeEntry, migrateSessionEntries, type NewSessionOptions, parseSessionEntries, type SessionContext, type SessionEntry, type SessionEntryBase, type SessionHeader, type SessionInfo, type SessionInfoEntry, SessionManager, type SessionMessageEntry, type SessionTreeNode, sessionEntryToContextMessages, type ThinkingLevelChangeEntry, } from "./core/session-manager.ts";
|
|
21
|
-
export { type AutoHandoffSettings, type CompactionSettings, type DefaultProjectTrust, type ImageSettings, type PackageSource, type RetrySettings, SettingsManager, type SettingsManagerCreateOptions, } from "./core/settings-manager.ts";
|
|
21
|
+
export { type AutoHandoffSettings, type CompactionSettings, type DefaultProjectTrust, type ImageSettings, type PackageSource, type RetrySettings, SettingsManager, type SettingsManagerCreateOptions, type TuiMode, } from "./core/settings-manager.ts";
|
|
22
22
|
export { formatSkillsForPrompt, type LoadSkillsFromDirOptions, type LoadSkillsResult, loadSkills, loadSkillsFromDir, type Skill, type SkillFrontmatter, } from "./core/skills.ts";
|
|
23
23
|
export { createSyntheticSourceInfo } from "./core/source-info.ts";
|
|
24
24
|
export { type EditDiffResult, generateDiffString, generateUnifiedPatch } from "./core/tools/edit-diff.ts";
|
package/dist/main.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
|
|
2
2
|
import type { Transport } from "@earendil-works/pi-ai";
|
|
3
|
-
import { Container, SettingsList } from "@earendil-works/pi-tui";
|
|
4
|
-
import type { DefaultProjectTrust, WarningSettings } from "../../../core/settings-manager.ts";
|
|
3
|
+
import { Container, type ScrollViewScrollbar, SettingsList } from "@earendil-works/pi-tui";
|
|
4
|
+
import type { DefaultProjectTrust, TuiMode, WarningSettings } from "../../../core/settings-manager.ts";
|
|
5
5
|
import { type TerminalTheme } from "../theme/theme.ts";
|
|
6
6
|
export interface SettingsConfig {
|
|
7
7
|
autoCompact: boolean;
|
|
@@ -34,6 +34,8 @@ export interface SettingsConfig {
|
|
|
34
34
|
defaultProjectTrust: DefaultProjectTrust;
|
|
35
35
|
clearOnShrink: boolean;
|
|
36
36
|
showTerminalProgress: boolean;
|
|
37
|
+
tuiMode: TuiMode;
|
|
38
|
+
fullscreenScrollbar: ScrollViewScrollbar;
|
|
37
39
|
warnings: WarningSettings;
|
|
38
40
|
}
|
|
39
41
|
export interface SettingsCallbacks {
|
|
@@ -65,6 +67,8 @@ export interface SettingsCallbacks {
|
|
|
65
67
|
onDefaultProjectTrustChange: (defaultProjectTrust: DefaultProjectTrust) => void;
|
|
66
68
|
onClearOnShrinkChange: (enabled: boolean) => void;
|
|
67
69
|
onShowTerminalProgressChange: (enabled: boolean) => void;
|
|
70
|
+
onTuiModeChange: (mode: TuiMode) => void;
|
|
71
|
+
onFullscreenScrollbarChange: (mode: ScrollViewScrollbar) => void;
|
|
68
72
|
onWarningsChange: (warnings: WarningSettings) => void;
|
|
69
73
|
onCancel: () => void;
|
|
70
74
|
}
|
|
@@ -402,6 +402,20 @@ export class SettingsSelectorComponent extends Container {
|
|
|
402
402
|
done(value);
|
|
403
403
|
}, () => done()),
|
|
404
404
|
},
|
|
405
|
+
{
|
|
406
|
+
id: "tui-mode",
|
|
407
|
+
label: "TUI mode",
|
|
408
|
+
description: "Switches immediately; fullscreen is the Selesai default",
|
|
409
|
+
currentValue: config.tuiMode,
|
|
410
|
+
values: ["fullscreen", "regular"],
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
id: "fullscreen-scrollbar",
|
|
414
|
+
label: "Fullscreen scrollbar",
|
|
415
|
+
description: "Scrollbar behavior in fullscreen mode; has no effect in regular mode",
|
|
416
|
+
currentValue: config.fullscreenScrollbar,
|
|
417
|
+
values: ["auto", "always", "hidden"],
|
|
418
|
+
},
|
|
405
419
|
{
|
|
406
420
|
id: "theme",
|
|
407
421
|
label: "Theme",
|
|
@@ -586,6 +600,12 @@ export class SettingsSelectorComponent extends Container {
|
|
|
586
600
|
case "terminal-progress":
|
|
587
601
|
callbacks.onShowTerminalProgressChange(newValue === "true");
|
|
588
602
|
break;
|
|
603
|
+
case "tui-mode":
|
|
604
|
+
callbacks.onTuiModeChange(newValue);
|
|
605
|
+
break;
|
|
606
|
+
case "fullscreen-scrollbar":
|
|
607
|
+
callbacks.onFullscreenScrollbarChange(newValue);
|
|
608
|
+
break;
|
|
589
609
|
case "theme":
|
|
590
610
|
callbacks.onThemeChange(newValue);
|
|
591
611
|
break;
|
|
@@ -3,8 +3,11 @@
|
|
|
3
3
|
* Handles TUI rendering and user interaction, delegating business logic to AgentSession.
|
|
4
4
|
*/
|
|
5
5
|
import type { ImageContent } from "@earendil-works/pi-ai/compat";
|
|
6
|
+
import type { Terminal } from "@earendil-works/pi-tui";
|
|
7
|
+
import { type TUI, TuiAltScreen, TuiMainScreen } from "@earendil-works/pi-tui";
|
|
6
8
|
import { type AgentSessionRuntime } from "../../core/agent-session-runtime.ts";
|
|
7
9
|
import { SessionManager } from "../../core/session-manager.ts";
|
|
10
|
+
import type { TuiMode } from "../../core/settings-manager.ts";
|
|
8
11
|
import { type LatestPiRelease } from "../../utils/version-check.ts";
|
|
9
12
|
type PendingUserInput = {
|
|
10
13
|
text: string;
|
|
@@ -29,14 +32,32 @@ export interface InteractiveModeOptions {
|
|
|
29
32
|
initialMessages?: string[];
|
|
30
33
|
/** Force verbose startup (overrides quietStartup setting) */
|
|
31
34
|
verbose?: boolean;
|
|
35
|
+
/** TUI layout mode. */
|
|
36
|
+
tuiMode?: TuiMode;
|
|
32
37
|
}
|
|
38
|
+
interface InteractiveTuiOptions {
|
|
39
|
+
tuiMode: TuiMode;
|
|
40
|
+
showHardwareCursor: boolean;
|
|
41
|
+
logDirectory: string;
|
|
42
|
+
terminal?: Terminal;
|
|
43
|
+
onRightClickPaste?: () => void;
|
|
44
|
+
}
|
|
45
|
+
/** Composition root for selecting the interactive terminal renderer. */
|
|
46
|
+
export declare function createInteractiveTui(options: InteractiveTuiOptions): TuiMainScreen | TuiAltScreen;
|
|
47
|
+
/** Stable reference for components while InteractiveMode replaces the active renderer. */
|
|
48
|
+
export declare function createInteractiveTuiReference(getTui: () => TUI): TUI;
|
|
33
49
|
export declare class InteractiveMode {
|
|
34
50
|
private runtimeHost;
|
|
51
|
+
private renderer;
|
|
35
52
|
private ui;
|
|
53
|
+
private mainScreenRenderState;
|
|
54
|
+
private documentContainer;
|
|
36
55
|
private loadedResourcesContainer;
|
|
37
56
|
private chatContainer;
|
|
38
57
|
private pendingMessagesContainer;
|
|
39
58
|
private statusContainer;
|
|
59
|
+
private transcriptScrollView;
|
|
60
|
+
private fullscreenLayoutRoot;
|
|
40
61
|
private defaultEditor;
|
|
41
62
|
private editor;
|
|
42
63
|
private editorComponentFactory;
|
|
@@ -45,6 +66,7 @@ export declare class InteractiveMode {
|
|
|
45
66
|
private fdPath;
|
|
46
67
|
private editorContainer;
|
|
47
68
|
private footer;
|
|
69
|
+
private footerContainer;
|
|
48
70
|
private footerDataProvider;
|
|
49
71
|
private keybindings;
|
|
50
72
|
private version;
|
|
@@ -95,6 +117,7 @@ export declare class InteractiveMode {
|
|
|
95
117
|
private builtInHeader;
|
|
96
118
|
private customHeader;
|
|
97
119
|
private options;
|
|
120
|
+
private readonly onRightClickPaste;
|
|
98
121
|
private autoTrustOnReloadCwd;
|
|
99
122
|
private themeController;
|
|
100
123
|
private get session();
|
|
@@ -108,6 +131,9 @@ export declare class InteractiveMode {
|
|
|
108
131
|
private createBaseAutocompleteProvider;
|
|
109
132
|
private setupAutocompleteProvider;
|
|
110
133
|
private showStartupNoticesIfNeeded;
|
|
134
|
+
private mountInteractiveTui;
|
|
135
|
+
private stopInteractiveTui;
|
|
136
|
+
private switchTuiMode;
|
|
111
137
|
init(): Promise<void>;
|
|
112
138
|
/**
|
|
113
139
|
* Update terminal title with session name and cwd.
|
|
@@ -193,6 +219,7 @@ export declare class InteractiveMode {
|
|
|
193
219
|
*/
|
|
194
220
|
private setExtensionHeader;
|
|
195
221
|
private addExtensionTerminalInputListener;
|
|
222
|
+
private rebindExtensionTerminalInputListeners;
|
|
196
223
|
private clearExtensionTerminalInputListeners;
|
|
197
224
|
/**
|
|
198
225
|
* Create the ExtensionUIContext for extensions.
|
|
@@ -240,6 +267,7 @@ export declare class InteractiveMode {
|
|
|
240
267
|
*/
|
|
241
268
|
private showExtensionError;
|
|
242
269
|
private setupKeyHandlers;
|
|
270
|
+
private handleRightClickPaste;
|
|
243
271
|
private handleClipboardPaste;
|
|
244
272
|
private consumePendingInputImages;
|
|
245
273
|
private handleGitCommand;
|