pi-ui-extend 0.1.19 → 0.1.21
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/dist/app/app.d.ts +3 -0
- package/dist/app/app.js +68 -8
- package/dist/app/constants.js +1 -1
- package/dist/app/extensions/extension-ui-controller.js +2 -2
- package/dist/app/input/voice-controller.d.ts +3 -2
- package/dist/app/input/voice-controller.js +9 -0
- package/dist/app/rendering/conversation-entry-renderer.js +39 -9
- package/dist/app/rendering/conversation-tool-renderer.js +1 -1
- package/dist/app/rendering/conversation-viewport.d.ts +1 -5
- package/dist/app/rendering/conversation-viewport.js +9 -16
- package/dist/app/rendering/editor-layout-renderer.js +5 -5
- package/dist/app/rendering/render-controller.js +14 -24
- package/dist/app/rendering/status-line-renderer.d.ts +2 -0
- package/dist/app/rendering/status-line-renderer.js +75 -29
- package/dist/app/rendering/tool-block-renderer.d.ts +2 -0
- package/dist/app/rendering/tool-block-renderer.js +13 -1
- package/dist/app/runtime.d.ts +2 -0
- package/dist/app/runtime.js +27 -4
- package/dist/app/screen/mouse-controller.d.ts +1 -1
- package/dist/app/screen/mouse-controller.js +9 -3
- package/dist/app/screen/screen-styler.js +3 -3
- package/dist/app/session/session-lifecycle-controller.d.ts +2 -0
- package/dist/app/session/session-lifecycle-controller.js +43 -16
- package/dist/app/session/tabs-controller.d.ts +1 -1
- package/dist/app/session/tabs-controller.js +3 -7
- package/dist/app/types.d.ts +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.js +19 -7
- package/dist/markdown-format.d.ts +2 -0
- package/dist/markdown-format.js +5 -2
- package/dist/syntax-highlight.js +3 -1
- package/dist/theme.d.ts +11 -0
- package/dist/theme.js +56 -15
- package/extensions/question/tui.ts +1 -1
- package/external/pi-tools-suite/src/antigravity-auth/auth-store.ts +6 -1
- package/external/pi-tools-suite/src/antigravity-auth/oauth.ts +1 -1
- package/external/pi-tools-suite/src/glm-coding-discipline/index.ts +123 -20
- package/package.json +1 -1
package/dist/app/app.d.ts
CHANGED
|
@@ -60,6 +60,9 @@ export declare class PiUiExtendApp {
|
|
|
60
60
|
private resumeLoading;
|
|
61
61
|
constructor(options: AppOptions);
|
|
62
62
|
private createRuntime;
|
|
63
|
+
private loadStartupConfig;
|
|
64
|
+
private applyPixConfig;
|
|
65
|
+
private updateOutputFilters;
|
|
63
66
|
start(): Promise<void>;
|
|
64
67
|
private checkPixUpdateOnStartup;
|
|
65
68
|
private bindCurrentSession;
|
package/dist/app/app.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { THEMES } from "../theme.js";
|
|
2
2
|
import { InputEditor } from "../input-editor.js";
|
|
3
|
-
import { compileOutputFilterPatterns, loadPixConfig, resolveToolRule, } from "../config.js";
|
|
3
|
+
import { compileOutputFilterPatterns, defaultPixConfig, loadPixConfig, resolveToolRule, } from "../config.js";
|
|
4
4
|
import { AppCommandController } from "./commands/command-controller.js";
|
|
5
5
|
import { ConversationViewport } from "./rendering/conversation-viewport.js";
|
|
6
6
|
import { EditorLayoutRenderer } from "./rendering/editor-layout-renderer.js";
|
|
@@ -68,7 +68,7 @@ export class PiUiExtendApp {
|
|
|
68
68
|
extensionUiController;
|
|
69
69
|
extensionActions;
|
|
70
70
|
pixConfig;
|
|
71
|
-
outputFilters;
|
|
71
|
+
outputFilters = [];
|
|
72
72
|
commandController;
|
|
73
73
|
inputActions;
|
|
74
74
|
inputController;
|
|
@@ -127,7 +127,7 @@ export class PiUiExtendApp {
|
|
|
127
127
|
constructor(options) {
|
|
128
128
|
this.options = options;
|
|
129
129
|
this.theme = THEMES[options.themeName];
|
|
130
|
-
this.pixConfig =
|
|
130
|
+
this.pixConfig = defaultPixConfig();
|
|
131
131
|
setAppIconTheme(this.pixConfig.iconTheme.name);
|
|
132
132
|
const app = this;
|
|
133
133
|
this.blinkController = new AppBlinkController({
|
|
@@ -160,7 +160,7 @@ export class PiUiExtendApp {
|
|
|
160
160
|
});
|
|
161
161
|
this.tabsController = new AppTabsController({
|
|
162
162
|
options: this.options,
|
|
163
|
-
maxProjectSessions: this.pixConfig.maxProjectSessions,
|
|
163
|
+
maxProjectSessions: () => this.pixConfig.maxProjectSessions,
|
|
164
164
|
blinkController: this.blinkController,
|
|
165
165
|
runtime: () => this.runtime,
|
|
166
166
|
createRuntimeForNewSession: () => this.createRuntime(newTabRuntimeOptions(this.options)),
|
|
@@ -398,18 +398,16 @@ export class PiUiExtendApp {
|
|
|
398
398
|
get subagentsWidgetState() { return app.subagentsWidgetController.widgetState; },
|
|
399
399
|
get voicePartialText() { return app.voicePartialText; },
|
|
400
400
|
get autocompleteSuggestion() { return app.autocompleteController.suggestionText(); },
|
|
401
|
-
get queuedMessageWidgetEntries() { return app.queuedMessages.
|
|
401
|
+
get queuedMessageWidgetEntries() { return app.queuedMessages.queuedEntries(); },
|
|
402
402
|
renderExtensionInputComponent: (width) => this.extensionUiController.renderActiveCustomUi(width),
|
|
403
403
|
extensionInputUsesEditor: () => this.extensionUiController.activeCustomUiUsesEditor(),
|
|
404
404
|
widgetTuiHandle: () => this.extensionUiController.widgetTuiHandle(),
|
|
405
405
|
createExtensionTheme: () => this.extensionUiController.createExtensionTheme(),
|
|
406
406
|
suppressExtensionWidget: (key) => this.extensionUiController.suppressWidget(key),
|
|
407
407
|
});
|
|
408
|
-
this.
|
|
408
|
+
this.updateOutputFilters();
|
|
409
409
|
this.conversationViewport = new ConversationViewport({
|
|
410
410
|
get entries() { return app.entries; },
|
|
411
|
-
get session() { return app.runtime?.session; },
|
|
412
|
-
get deferredUserMessages() { return app.queuedMessages.deferredUserMessages; },
|
|
413
411
|
get entryRenderVersions() { return app.sessionEvents.entryRenderVersions; },
|
|
414
412
|
get superCompactTools() { return app.superCompactTools; },
|
|
415
413
|
get allThinkingExpanded() { return app.allThinkingExpanded; },
|
|
@@ -700,6 +698,7 @@ export class PiUiExtendApp {
|
|
|
700
698
|
inputEditor: () => this.inputEditor,
|
|
701
699
|
enableTerminal: () => this.terminalController.enableTerminal(),
|
|
702
700
|
disposeRuntimeForQuit: (runtime) => this.terminalController.disposeRuntimeForQuit(runtime),
|
|
701
|
+
loadStartupConfig: () => this.loadStartupConfig(),
|
|
703
702
|
loadRequestHistory: () => this.requestHistory.load(),
|
|
704
703
|
startSubagentsPolling: () => this.subagentsWidgetController.startPolling(),
|
|
705
704
|
closeSdkMenuForBind: () => this.popupMenus.closeSdkMenu(undefined, { render: false, restoreStatus: false }),
|
|
@@ -750,8 +749,35 @@ export class PiUiExtendApp {
|
|
|
750
749
|
createRuntime(options) {
|
|
751
750
|
return createPixRuntime(options, {
|
|
752
751
|
eventBus: this.createExtensionEventBus(),
|
|
752
|
+
config: this.pixConfig,
|
|
753
753
|
});
|
|
754
754
|
}
|
|
755
|
+
async loadStartupConfig() {
|
|
756
|
+
await yieldToEventLoop();
|
|
757
|
+
this.applyPixConfig(loadPixConfig(this.options.cwd));
|
|
758
|
+
}
|
|
759
|
+
applyPixConfig(config) {
|
|
760
|
+
replaceRecord(this.pixConfig.toolRenderer, config.toolRenderer);
|
|
761
|
+
replaceRecord(this.pixConfig.outputFilters, config.outputFilters);
|
|
762
|
+
replaceRecord(this.pixConfig.promptEnhancer, config.promptEnhancer);
|
|
763
|
+
replaceRecord(this.pixConfig.autocomplete, config.autocomplete);
|
|
764
|
+
replaceRecord(this.pixConfig.modelColors, config.modelColors);
|
|
765
|
+
replaceRecord(this.pixConfig.iconTheme, config.iconTheme);
|
|
766
|
+
replaceRecord(this.pixConfig.dictation, config.dictation);
|
|
767
|
+
if (config.defaultModel === undefined)
|
|
768
|
+
delete this.pixConfig.defaultModel;
|
|
769
|
+
else
|
|
770
|
+
this.pixConfig.defaultModel = { ...config.defaultModel };
|
|
771
|
+
this.pixConfig.ignoreContextFiles = config.ignoreContextFiles;
|
|
772
|
+
this.pixConfig.maxProjectSessions = config.maxProjectSessions;
|
|
773
|
+
this.updateOutputFilters();
|
|
774
|
+
this.voiceController.updateDictationConfig(this.pixConfig.dictation);
|
|
775
|
+
setAppIconTheme(this.pixConfig.iconTheme.name);
|
|
776
|
+
this.conversationViewport.clear();
|
|
777
|
+
}
|
|
778
|
+
updateOutputFilters() {
|
|
779
|
+
this.outputFilters.splice(0, this.outputFilters.length, ...compileOutputFilterPatterns(this.pixConfig.outputFilters.patterns));
|
|
780
|
+
}
|
|
755
781
|
async start() {
|
|
756
782
|
await this.sessionLifecycle.start();
|
|
757
783
|
this.modelUsageController.startPolling();
|
|
@@ -1053,3 +1079,37 @@ function newTabRuntimeOptions(options) {
|
|
|
1053
1079
|
...(options.modelRef === undefined ? {} : { modelRef: options.modelRef }),
|
|
1054
1080
|
};
|
|
1055
1081
|
}
|
|
1082
|
+
async function yieldToEventLoop() {
|
|
1083
|
+
await new Promise((resolve) => { setTimeout(resolve, 0); });
|
|
1084
|
+
}
|
|
1085
|
+
function replaceRecord(target, source) {
|
|
1086
|
+
for (const key of Object.keys(target)) {
|
|
1087
|
+
if (!(key in source))
|
|
1088
|
+
delete target[key];
|
|
1089
|
+
}
|
|
1090
|
+
for (const [key, value] of Object.entries(source)) {
|
|
1091
|
+
const existing = target[key];
|
|
1092
|
+
if (isMutableRecord(existing) && isMutableRecord(value)) {
|
|
1093
|
+
replaceRecord(existing, value);
|
|
1094
|
+
}
|
|
1095
|
+
else {
|
|
1096
|
+
target[key] = cloneConfigValue(value);
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
function cloneConfigValue(value) {
|
|
1101
|
+
if (Array.isArray(value))
|
|
1102
|
+
return value.map(cloneConfigValue);
|
|
1103
|
+
if (isMutableRecord(value))
|
|
1104
|
+
return cloneConfigRecord(value);
|
|
1105
|
+
return value;
|
|
1106
|
+
}
|
|
1107
|
+
function cloneConfigRecord(value) {
|
|
1108
|
+
const cloned = {};
|
|
1109
|
+
for (const [key, nested] of Object.entries(value))
|
|
1110
|
+
cloned[key] = cloneConfigValue(nested);
|
|
1111
|
+
return cloned;
|
|
1112
|
+
}
|
|
1113
|
+
function isMutableRecord(value) {
|
|
1114
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1115
|
+
}
|
package/dist/app/constants.js
CHANGED
|
@@ -73,7 +73,7 @@ export const SUBAGENTS_WIDGET_MAX_ROWS = 8;
|
|
|
73
73
|
export const DEFAULT_THINKING_TOOL_RULE = {
|
|
74
74
|
previewLines: 0,
|
|
75
75
|
direction: "head",
|
|
76
|
-
color: "
|
|
76
|
+
color: "thinkingForeground",
|
|
77
77
|
};
|
|
78
78
|
export const TERMINAL_COMMAND_MODIFIER_FLAG = 8;
|
|
79
79
|
export const GIT_BRANCH_CACHE_MS = 30_000;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ANSI_RESET, colorize, THEMES } from "../../theme.js";
|
|
1
|
+
import { ANSI_RESET, ansiStylePrefix, colorize, THEMES } from "../../theme.js";
|
|
2
2
|
import { isToastKind } from "../../ui.js";
|
|
3
3
|
import { ellipsizeDisplay, sanitizeText } from "../rendering/render-text.js";
|
|
4
4
|
const CUSTOM_UI_WIDGET_KEY = "pix-custom-ui";
|
|
@@ -20,7 +20,7 @@ export class ExtensionUiController {
|
|
|
20
20
|
const colors = this.host.theme.colors;
|
|
21
21
|
const foreground = (color) => extensionForegroundColor(colors, String(color));
|
|
22
22
|
const background = (color) => extensionBackgroundColor(colors, String(color));
|
|
23
|
-
const prefix = (options) => (
|
|
23
|
+
const prefix = (options) => ansiStylePrefix(options);
|
|
24
24
|
return {
|
|
25
25
|
fg: (color, text) => colorize(text, { foreground: foreground(color) }),
|
|
26
26
|
bg: (color, text) => colorize(text, { background: background(color) }),
|
|
@@ -54,8 +54,8 @@ type VoiceControllerTestDeps = {
|
|
|
54
54
|
export declare function setVoiceControllerTestDeps(overrides?: Partial<VoiceControllerTestDeps>): void;
|
|
55
55
|
export declare class AppVoiceController {
|
|
56
56
|
private readonly host;
|
|
57
|
-
private
|
|
58
|
-
private
|
|
57
|
+
private modelDefinitions;
|
|
58
|
+
private languages;
|
|
59
59
|
private language;
|
|
60
60
|
private state;
|
|
61
61
|
private readonly modelCache;
|
|
@@ -69,6 +69,7 @@ export declare class AppVoiceController {
|
|
|
69
69
|
private partialTranscriptTimer;
|
|
70
70
|
private startGeneration;
|
|
71
71
|
constructor(host: AppVoiceControllerHost, dictationConfig: DictationConfig);
|
|
72
|
+
updateDictationConfig(dictationConfig: DictationConfig): void;
|
|
72
73
|
statusWidgetText(): string;
|
|
73
74
|
showLanguageSwitcher(): boolean;
|
|
74
75
|
statusWidgetActive(): boolean;
|
|
@@ -51,6 +51,15 @@ export class AppVoiceController {
|
|
|
51
51
|
this.languages = Object.keys(this.modelDefinitions);
|
|
52
52
|
this.language = this.initialLanguage(dictationConfig.language);
|
|
53
53
|
}
|
|
54
|
+
updateDictationConfig(dictationConfig) {
|
|
55
|
+
this.modelDefinitions = dictationConfig.languages;
|
|
56
|
+
this.languages = Object.keys(this.modelDefinitions);
|
|
57
|
+
this.language = this.initialLanguage(dictationConfig.language ?? this.language);
|
|
58
|
+
for (const language of this.modelCache.keys()) {
|
|
59
|
+
if (!this.modelDefinitions[language])
|
|
60
|
+
this.modelCache.delete(language);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
54
63
|
statusWidgetText() {
|
|
55
64
|
const languageLabel = this.showLanguageSwitcher() ? ` ${this.language.toUpperCase()}` : "";
|
|
56
65
|
switch (this.state) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { applyOutputFilters } from "../../config.js";
|
|
2
2
|
import { renderMarkdownTextLines } from "../../markdown-format.js";
|
|
3
|
+
import { stringDisplayWidth } from "../../terminal-width.js";
|
|
3
4
|
import { attachImageClickTargets } from "../screen/image-click-targets.js";
|
|
4
5
|
import { APP_ICONS } from "../icons.js";
|
|
5
6
|
import { horizontalPaddingLayout, padHorizontalText, wrapText } from "./render-text.js";
|
|
@@ -7,16 +8,39 @@ import { renderConversationShellEntry } from "./conversation-shell-renderer.js";
|
|
|
7
8
|
import { renderConversationToolEntry, renderThinkingEntry } from "./conversation-tool-renderer.js";
|
|
8
9
|
export function renderConversationEntry(entry, width, options) {
|
|
9
10
|
const { left: userContentLeft, contentWidth: userContentWidth } = horizontalPaddingLayout(width);
|
|
10
|
-
const userLine = (text, entryId, syntaxHighlight, segments) =>
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
const userLine = (text, entryId, syntaxHighlight, segments) => {
|
|
12
|
+
const textWidth = stringDisplayWidth(text);
|
|
13
|
+
const padding = Math.max(0, width - textWidth);
|
|
14
|
+
const paddedText = " ".repeat(padding) + text;
|
|
15
|
+
const offset = padding;
|
|
16
|
+
return {
|
|
17
|
+
text: paddedText,
|
|
18
|
+
colorOverride: options.colors.userForeground,
|
|
19
|
+
backgroundOverride: options.colors.userMessageBackground,
|
|
20
|
+
...(segments && segments.length > 0
|
|
21
|
+
? {
|
|
22
|
+
segments: segments.map((segment) => ({
|
|
23
|
+
...segment,
|
|
24
|
+
start: segment.start + offset,
|
|
25
|
+
end: segment.end + offset,
|
|
26
|
+
foreground: options.colors.userForeground,
|
|
27
|
+
})),
|
|
28
|
+
}
|
|
29
|
+
: {}),
|
|
30
|
+
...(syntaxHighlight === undefined
|
|
31
|
+
? {}
|
|
32
|
+
: {
|
|
33
|
+
syntaxHighlight: {
|
|
34
|
+
...syntaxHighlight,
|
|
35
|
+
start: syntaxHighlight.start + offset,
|
|
36
|
+
},
|
|
37
|
+
}),
|
|
38
|
+
...(entryId === undefined ? {} : { target: { kind: "user-message", id: entryId } }),
|
|
39
|
+
};
|
|
40
|
+
};
|
|
17
41
|
const queuedLine = (text, entryId, segments) => ({
|
|
18
42
|
text,
|
|
19
|
-
colorOverride: options.colors.
|
|
43
|
+
colorOverride: options.colors.userForeground,
|
|
20
44
|
...(segments && segments.length > 0 ? { segments } : {}),
|
|
21
45
|
target: { kind: "queue-message", id: entryId },
|
|
22
46
|
});
|
|
@@ -69,10 +93,16 @@ function renderAssistantLines(text, width, options) {
|
|
|
69
93
|
return [];
|
|
70
94
|
const lines = [];
|
|
71
95
|
for (const line of contentLines) {
|
|
96
|
+
const headingSegment = line.heading
|
|
97
|
+
? { start: contentLeft, end: contentLeft + line.text.length, foreground: options.colors.assistantForeground, bold: true }
|
|
98
|
+
: undefined;
|
|
99
|
+
const existingSegments = line.segments?.map((segment) => ({ ...segment, start: segment.start + contentLeft, end: segment.end + contentLeft })) ?? [];
|
|
100
|
+
const allSegments = headingSegment ? [headingSegment, ...existingSegments] : existingSegments;
|
|
72
101
|
lines.push({
|
|
73
102
|
text: padHorizontalText(line.text, width),
|
|
74
103
|
colorOverride: options.colors.assistantForeground,
|
|
75
|
-
|
|
104
|
+
backgroundOverride: options.colors.assistantMessageBackground,
|
|
105
|
+
...(allSegments.length > 0 ? { segments: allSegments } : {}),
|
|
76
106
|
...(line.syntaxHighlight ? { syntaxHighlight: line.syntaxHighlight } : {}),
|
|
77
107
|
});
|
|
78
108
|
}
|
|
@@ -63,7 +63,7 @@ export function renderThinkingEntry(entry, width, options) {
|
|
|
63
63
|
expandedText: compactExpandedText || "(empty)",
|
|
64
64
|
bodyWrap: "word",
|
|
65
65
|
syntaxHighlight: compactExpandedText ? markdownSyntaxHighlightsForText(compactExpandedText) : undefined,
|
|
66
|
-
}, rule, width, options.colors, { superCompact: Boolean(options.superCompactTools && !forceExpanded) });
|
|
66
|
+
}, rule, width, options.colors, { superCompact: Boolean(options.superCompactTools && !forceExpanded), backgroundOverride: options.colors.thinkingMessageBackground, skipHeaderBackground: true });
|
|
67
67
|
}
|
|
68
68
|
function trimTrailingBlankLines(text) {
|
|
69
69
|
return text.replace(/(?:\r?\n[ \t]*)+$/u, "");
|
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
import type { AgentSession } from "@earendil-works/pi-coding-agent";
|
|
2
1
|
import { type PixConfig } from "../../config.js";
|
|
3
2
|
import type { Theme } from "../../theme.js";
|
|
4
3
|
import { type InlineUserMessageMenuContext } from "./conversation-entry-renderer.js";
|
|
5
|
-
import type { ConversationBlockCache, Entry, RenderedLine
|
|
4
|
+
import type { ConversationBlockCache, Entry, RenderedLine } from "../types.js";
|
|
6
5
|
export type ConversationViewportHost = {
|
|
7
6
|
readonly entries: readonly Entry[];
|
|
8
|
-
readonly session: AgentSession | undefined;
|
|
9
|
-
readonly deferredUserMessages: readonly SubmittedUserMessage[];
|
|
10
7
|
readonly entryRenderVersions: ReadonlyMap<string, number>;
|
|
11
8
|
readonly cwd: string;
|
|
12
9
|
readonly colors: Theme["colors"];
|
|
@@ -40,7 +37,6 @@ export declare class ConversationViewport {
|
|
|
40
37
|
blockForEntry(entry: Entry, width: number): ConversationBlockCache;
|
|
41
38
|
entryBlockPositions(width: number): ConversationEntryBlockPosition[];
|
|
42
39
|
measuredLineCountForEntries(width: number, entryIds: readonly string[]): number;
|
|
43
|
-
private queuedEntries;
|
|
44
40
|
private layoutForWidth;
|
|
45
41
|
private buildLayout;
|
|
46
42
|
private previousMeasuredLineCount;
|
|
@@ -2,7 +2,6 @@ import { resolveToolRule } from "../../config.js";
|
|
|
2
2
|
import { stringDisplayWidth } from "../../terminal-width.js";
|
|
3
3
|
import { renderConversationEntry as renderConversationEntryLines } from "./conversation-entry-renderer.js";
|
|
4
4
|
import { horizontalPaddingLayout } from "./render-text.js";
|
|
5
|
-
import { sdkQueuedMessageEntries } from "../session/queued-message-entries.js";
|
|
6
5
|
export class ConversationViewport {
|
|
7
6
|
host;
|
|
8
7
|
blockCachesByWidth = new Map();
|
|
@@ -63,8 +62,7 @@ export class ConversationViewport {
|
|
|
63
62
|
return { lines: visible, changed: false };
|
|
64
63
|
}
|
|
65
64
|
entries() {
|
|
66
|
-
|
|
67
|
-
return queued.length === 0 ? [...this.host.entries] : [...this.host.entries, ...queued];
|
|
65
|
+
return [...this.host.entries];
|
|
68
66
|
}
|
|
69
67
|
blockForEntry(entry, width) {
|
|
70
68
|
const blockCache = this.blockCacheForWidth(width);
|
|
@@ -119,21 +117,16 @@ export class ConversationViewport {
|
|
|
119
117
|
}
|
|
120
118
|
return lineCount;
|
|
121
119
|
}
|
|
122
|
-
queuedEntries() {
|
|
123
|
-
return sdkQueuedMessageEntries(this.host.session);
|
|
124
|
-
}
|
|
125
120
|
layoutForWidth(width) {
|
|
126
|
-
const
|
|
127
|
-
const entries = queued.length === 0 ? this.host.entries : [...this.host.entries, ...queued];
|
|
128
|
-
const queuedSignature = queued.map((entry) => entry.id).join("\n");
|
|
121
|
+
const entries = this.host.entries;
|
|
129
122
|
const superCompactTools = Boolean(this.host.superCompactTools);
|
|
130
123
|
const allThinkingExpanded = Boolean(this.host.allThinkingExpanded);
|
|
131
124
|
let layout = this.layoutCachesByWidth.get(width);
|
|
132
|
-
if (!layout || this.layoutStructureChanged(layout, entries,
|
|
133
|
-
const previousLayout = layout && layout.
|
|
125
|
+
if (!layout || this.layoutStructureChanged(layout, entries, superCompactTools, allThinkingExpanded)) {
|
|
126
|
+
const previousLayout = layout && layout.superCompactTools === superCompactTools && layout.allThinkingExpanded === allThinkingExpanded
|
|
134
127
|
? layout
|
|
135
128
|
: undefined;
|
|
136
|
-
layout = this.buildLayout(entries, width,
|
|
129
|
+
layout = this.buildLayout(entries, width, superCompactTools, allThinkingExpanded, previousLayout);
|
|
137
130
|
this.layoutCachesByWidth.set(width, layout);
|
|
138
131
|
}
|
|
139
132
|
else {
|
|
@@ -144,7 +137,7 @@ export class ConversationViewport {
|
|
|
144
137
|
}
|
|
145
138
|
return layout;
|
|
146
139
|
}
|
|
147
|
-
buildLayout(entries, width,
|
|
140
|
+
buildLayout(entries, width, superCompactTools, allThinkingExpanded, previousLayout) {
|
|
148
141
|
const entryIds = [];
|
|
149
142
|
const lineCounts = [];
|
|
150
143
|
const measuredLineCounts = [];
|
|
@@ -163,7 +156,7 @@ export class ConversationViewport {
|
|
|
163
156
|
totalLineCount += lineCount;
|
|
164
157
|
}
|
|
165
158
|
offsets.push(totalLineCount);
|
|
166
|
-
return { entries, entryIds, lineCounts, measuredLineCounts, offsets, positions, dirtyEntryIds: new Set(), totalLineCount,
|
|
159
|
+
return { entries, entryIds, lineCounts, measuredLineCounts, offsets, positions, dirtyEntryIds: new Set(), totalLineCount, superCompactTools, allThinkingExpanded };
|
|
167
160
|
}
|
|
168
161
|
previousMeasuredLineCount(previousLayout, entries, index, entry) {
|
|
169
162
|
const previousIndex = previousLayout?.positions.get(entry.id);
|
|
@@ -179,8 +172,8 @@ export class ConversationViewport {
|
|
|
179
172
|
return undefined;
|
|
180
173
|
return previousLayout.lineCounts[previousIndex];
|
|
181
174
|
}
|
|
182
|
-
layoutStructureChanged(layout, entries,
|
|
183
|
-
if (layout.entries.length !== entries.length || layout.
|
|
175
|
+
layoutStructureChanged(layout, entries, superCompactTools, allThinkingExpanded) {
|
|
176
|
+
if (layout.entries.length !== entries.length || layout.superCompactTools !== superCompactTools || layout.allThinkingExpanded !== allThinkingExpanded)
|
|
184
177
|
return true;
|
|
185
178
|
if (layout.entries.length === 0)
|
|
186
179
|
return false;
|
|
@@ -8,7 +8,7 @@ export class EditorLayoutRenderer {
|
|
|
8
8
|
this.host = host;
|
|
9
9
|
}
|
|
10
10
|
computeLayout(width, rows) {
|
|
11
|
-
const maxAvailableInputRows = Math.max(1, rows -
|
|
11
|
+
const maxAvailableInputRows = Math.max(1, rows - 4);
|
|
12
12
|
const renderedInput = this.renderInput(width, Math.min(INPUT_MAX_ROWS, maxAvailableInputRows), maxAvailableInputRows);
|
|
13
13
|
const maxEntityRows = Math.max(0, rows - renderedInput.lines.length - 4);
|
|
14
14
|
const editorEntityWidth = inputFrameContentWidth(width);
|
|
@@ -18,9 +18,9 @@ export class EditorLayoutRenderer {
|
|
|
18
18
|
aboveEditorLines = [...aboveEditorLines, { text: "", variant: "normal" }];
|
|
19
19
|
}
|
|
20
20
|
const belowEditorLines = this.limitEntityLines(this.renderExtensionWidgets("belowEditor", editorEntityWidth), maxEntityRows - aboveEditorLines.length);
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
const inputStartRow =
|
|
21
|
+
const belowEditorStartRow = rows - belowEditorLines.length;
|
|
22
|
+
const inputBottomSeparatorRow = belowEditorStartRow - 1;
|
|
23
|
+
const inputStartRow = inputBottomSeparatorRow - renderedInput.lines.length;
|
|
24
24
|
const inputSeparatorRow = inputStartRow - aboveEditorLines.length - 1;
|
|
25
25
|
return {
|
|
26
26
|
renderedInput,
|
|
@@ -111,7 +111,7 @@ export class EditorLayoutRenderer {
|
|
|
111
111
|
for (const [index, text] of wrapped.entries()) {
|
|
112
112
|
lines.push({
|
|
113
113
|
text: padHorizontalText(text, width),
|
|
114
|
-
colorOverride: this.host.theme.colors.
|
|
114
|
+
colorOverride: this.host.theme.colors.userForeground,
|
|
115
115
|
target: { kind: "queue-message", id: entry.id },
|
|
116
116
|
...(index === 0 ? { segments: [{ start: 0, end: icon.length, foreground: this.host.theme.colors.info }] } : {}),
|
|
117
117
|
});
|
|
@@ -150,7 +150,7 @@ export class AppRenderController {
|
|
|
150
150
|
if (row < statusRow) {
|
|
151
151
|
this.deps.mouseController.renderedRowTexts.set(row, separatorText);
|
|
152
152
|
appendFrameOutput("inputStatus", row, this.renderFrameRow(row, this.deps.screenStyler.styleLine(row, separatorText, columns, {
|
|
153
|
-
foreground: this.deps.theme.colors.
|
|
153
|
+
foreground: this.deps.theme.colors.tabBorder,
|
|
154
154
|
})));
|
|
155
155
|
}
|
|
156
156
|
}
|
|
@@ -176,7 +176,15 @@ export class AppRenderController {
|
|
|
176
176
|
})}`);
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
|
-
|
|
179
|
+
if (inputBottomSeparatorRow && inputBottomSeparatorRow > inputSeparatorRow && inputBottomSeparatorRow < statusRow) {
|
|
180
|
+
const separatorText = inputFrameLine(columns, "bottom");
|
|
181
|
+
const row = toScreenRow(inputBottomSeparatorRow);
|
|
182
|
+
this.deps.mouseController.renderedRowTexts.set(row, separatorText);
|
|
183
|
+
appendFrameOutput("inputStatus", row, this.renderFrameRow(row, this.deps.screenStyler.styleLine(row, separatorText, columns, {
|
|
184
|
+
foreground: this.deps.theme.colors.tabBorder,
|
|
185
|
+
})));
|
|
186
|
+
}
|
|
187
|
+
const belowEditorStartRow = (inputBottomSeparatorRow ?? (inputStartRow + renderedInput.lines.length - 1)) + 1;
|
|
180
188
|
for (let index = 0; index < belowEditorLines.length; index += 1) {
|
|
181
189
|
const rendered = frameRenderedLine(belowEditorLines[index], columns, this.deps.theme, this.deps.screenStyler);
|
|
182
190
|
const row = toScreenRow(belowEditorStartRow + index);
|
|
@@ -191,25 +199,7 @@ export class AppRenderController {
|
|
|
191
199
|
appendFrameOutput("inputStatus", row, this.renderFrameRow(row, rendered.output(row)));
|
|
192
200
|
}
|
|
193
201
|
const statusLayout = this.deps.statusLineRenderer.layout(columns);
|
|
194
|
-
|
|
195
|
-
const inputBorderWidgetsLayout = statusLineRenderer.inputBorderWidgetsLayout?.(columns);
|
|
196
|
-
if (inputBottomSeparatorRow > 1) {
|
|
197
|
-
const separatorText = inputFrameLine(columns, "bottom");
|
|
198
|
-
const row = toScreenRow(inputBottomSeparatorRow);
|
|
199
|
-
if (row < statusRow) {
|
|
200
|
-
const text = inputBorderWidgetsLayout
|
|
201
|
-
? overlayText(separatorText, inputBorderWidgetsLayout.inputBorderWidgetStartColumn ?? 1, inputBorderWidgetsLayout.text)
|
|
202
|
-
: separatorText;
|
|
203
|
-
this.deps.mouseController.renderedRowTexts.set(row, text);
|
|
204
|
-
const output = inputBorderWidgetsLayout && statusLineRenderer.renderInputBorderWidgets
|
|
205
|
-
? statusLineRenderer.renderInputBorderWidgets(row, inputBorderWidgetsLayout, separatorText, columns)
|
|
206
|
-
: this.deps.screenStyler.styleLine(row, separatorText, columns, {
|
|
207
|
-
foreground: this.deps.theme.colors.inputBorder,
|
|
208
|
-
});
|
|
209
|
-
appendFrameOutput("inputStatus", row, this.renderFrameRow(row, output));
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
this.updateStatusMouseState(statusLayout, statusRow, inputBorderWidgetsLayout, toScreenRow(inputBottomSeparatorRow));
|
|
202
|
+
this.updateStatusMouseState(statusLayout, statusRow);
|
|
213
203
|
appendFrameOutput("inputStatus", statusRow, this.renderFrameRow(statusRow, this.deps.statusLineRenderer.render(statusRow, statusLayout, columns)));
|
|
214
204
|
const voiceProgressOverlay = this.renderVoiceProgressOverlay(this.deps.voiceProgressOverlayText(), columns, statusRow);
|
|
215
205
|
if (voiceProgressOverlay) {
|
|
@@ -287,9 +277,9 @@ export class AppRenderController {
|
|
|
287
277
|
const text = fixedCellText(flash.text, width);
|
|
288
278
|
return `\x1b[${flash.y};${flash.startColumn}H\x1b[7m${text}${ANSI_RESET}`;
|
|
289
279
|
}
|
|
290
|
-
updateStatusMouseState(statusLayout, statusRow
|
|
291
|
-
const widgetLayout =
|
|
292
|
-
const widgetRow =
|
|
280
|
+
updateStatusMouseState(statusLayout, statusRow) {
|
|
281
|
+
const widgetLayout = statusLayout;
|
|
282
|
+
const widgetRow = statusRow;
|
|
293
283
|
this.deps.mouseController.statusModelTarget = this.deps.statusLineRenderer.modelTarget(statusLayout.text, statusRow);
|
|
294
284
|
this.deps.mouseController.statusThinkingTarget = this.deps.statusLineRenderer.thinkingTarget(statusLayout.text, statusRow);
|
|
295
285
|
this.deps.mouseController.statusContextTarget = this.deps.statusLineRenderer.contextTarget(statusLayout.text, statusRow, statusLayout);
|
|
@@ -53,6 +53,8 @@ export declare class StatusLineRenderer {
|
|
|
53
53
|
terminalBellSoundTarget(layout: StatusLineLayout, row: number): StatusTerminalBellSoundTarget | undefined;
|
|
54
54
|
sessionTarget(statusText: string, row: number, label: string, workspaceLabel: string): StatusSessionTarget | undefined;
|
|
55
55
|
private segments;
|
|
56
|
+
private fitWorkspaceLabel;
|
|
57
|
+
private pushStatusWidgetSegments;
|
|
56
58
|
private pushPromptEnhancerWidgetSegment;
|
|
57
59
|
private pushUserJumpWidgetSegment;
|
|
58
60
|
private pushDraftQueueWidgetSegment;
|