@selesai/code 0.8.1 → 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 +15 -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 +3 -1
- package/dist/extensions/pi-powerline-footer/guide.ts +11 -3
- 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 +24 -1
- package/dist/extensions/pi-powerline-footer/tsconfig.json +1 -1
- package/dist/extensions/pi-subagents/package-lock.json +2 -2
- package/dist/extensions/pi-subagents/package.json +1 -1
- package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +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 = {
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
getGuideFeatureLines,
|
|
11
11
|
getGuidePreferences,
|
|
12
12
|
getNewGuideFeatures,
|
|
13
|
+
markGuideVersionSeen,
|
|
13
14
|
resolveGuideDisplayMode,
|
|
14
15
|
saveGuidePreferences,
|
|
15
16
|
type GuideFeature,
|
|
@@ -35,7 +36,7 @@ test("guide dismissal and compact preferences change automatic display", () => {
|
|
|
35
36
|
assert.equal(resolveGuideDisplayMode({ reason: "startup", hasUI: false, guideMode: "full" }), "none");
|
|
36
37
|
});
|
|
37
38
|
|
|
38
|
-
test("guide defaults to a compact header so
|
|
39
|
+
test("guide defaults to a compact header so the native dock stays uncluttered", () => {
|
|
39
40
|
const preferences = getGuidePreferences({});
|
|
40
41
|
assert.equal(preferences.mode, "compact");
|
|
41
42
|
assert.equal(resolveGuideDisplayMode({ reason: "startup", hasUI: true, guideMode: preferences.mode }), "compact");
|
|
@@ -99,6 +100,28 @@ test("guide dismissal persists only the mode and version marker", () => {
|
|
|
99
100
|
}
|
|
100
101
|
});
|
|
101
102
|
|
|
103
|
+
test("version marking never persists an implicit guide mode", () => {
|
|
104
|
+
const dir = mkdtempSync(join(tmpdir(), "selesai-guide-"));
|
|
105
|
+
const settingsPath = join(dir, "settings.json");
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
// markGuideVersionSeen writes only the version marker; the mode stays
|
|
109
|
+
// unset so the default (compact) applies instead of being locked in.
|
|
110
|
+
assert.equal(markGuideVersionSeen("0.5.3", settingsPath), true);
|
|
111
|
+
const settings = JSON.parse(readFileSync(settingsPath, "utf8")) as Record<string, unknown>;
|
|
112
|
+
assert.deepEqual(settings.selesaiGuide, { lastSeenVersion: "0.5.3" });
|
|
113
|
+
assert.equal(getGuidePreferences(settings).mode, "compact");
|
|
114
|
+
|
|
115
|
+
// An explicitly chosen mode is preserved across version-only writes.
|
|
116
|
+
assert.equal(saveGuidePreferences({ mode: "off" }, settingsPath), true);
|
|
117
|
+
assert.equal(saveGuidePreferences({ lastSeenVersion: "0.6.0" }, settingsPath), true);
|
|
118
|
+
const after = JSON.parse(readFileSync(settingsPath, "utf8")) as Record<string, unknown>;
|
|
119
|
+
assert.deepEqual(after.selesaiGuide, { mode: "off", lastSeenVersion: "0.6.0" });
|
|
120
|
+
} finally {
|
|
121
|
+
rmSync(dir, { recursive: true, force: true });
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
102
125
|
test("invalid persisted guide settings fall back to the compact guide", () => {
|
|
103
126
|
assert.deepEqual(getGuidePreferences({ selesaiGuide: { mode: "unexpected", lastSeenVersion: 42 } }), {
|
|
104
127
|
mode: "compact",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"@earendil-works/pi-agent-core": "0.81.0",
|
|
21
21
|
"@earendil-works/pi-ai": "0.81.0",
|
|
22
22
|
"@earendil-works/pi-tui": "0.81.0",
|
|
23
|
-
"@selesai/code": "0.8.
|
|
23
|
+
"@selesai/code": "0.8.2",
|
|
24
24
|
"@types/node": "24.13.3",
|
|
25
25
|
"typescript": "5.9.3"
|
|
26
26
|
},
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
"../../..": {
|
|
49
49
|
"name": "@selesai/code",
|
|
50
|
-
"version": "0.8.
|
|
50
|
+
"version": "0.8.2",
|
|
51
51
|
"dev": true,
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@earendil-works/pi-agent-core": "0.83.0",
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
"devDependencies": {
|
|
98
98
|
"@earendil-works/pi-agent-core": "0.81.0",
|
|
99
99
|
"@earendil-works/pi-ai": "0.81.0",
|
|
100
|
-
"@selesai/code": "0.8.
|
|
100
|
+
"@selesai/code": "0.8.2",
|
|
101
101
|
"@earendil-works/pi-tui": "0.81.0",
|
|
102
102
|
"@types/node": "24.13.3",
|
|
103
103
|
"typescript": "5.9.3"
|
|
@@ -26,7 +26,7 @@ const expectedHostPeerRanges = {
|
|
|
26
26
|
const expectedHostDevVersions = {
|
|
27
27
|
"@earendil-works/pi-agent-core": "0.81.0",
|
|
28
28
|
"@earendil-works/pi-ai": "0.81.0",
|
|
29
|
-
"@selesai/code": "0.8.
|
|
29
|
+
"@selesai/code": "0.8.2",
|
|
30
30
|
"@earendil-works/pi-tui": "0.81.0",
|
|
31
31
|
} satisfies Record<(typeof hostPeerPackages)[number], string>;
|
|
32
32
|
|
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;
|