pi-ui-extend 0.1.20 → 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/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 +38 -8
- 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 +1 -1
- 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/screen-styler.js +2 -2
- 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/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 +3 -0
- package/dist/theme.js +53 -28
- 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/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
|
+
}
|
|
@@ -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,13 +8,36 @@ 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
43
|
colorOverride: options.colors.userForeground,
|
|
@@ -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;
|
|
@@ -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
|
});
|
|
@@ -23,5 +23,7 @@ export type ToolBlockEntry = {
|
|
|
23
23
|
};
|
|
24
24
|
export type ToolBlockRenderOptions = {
|
|
25
25
|
superCompact?: boolean;
|
|
26
|
+
backgroundOverride?: string;
|
|
27
|
+
skipHeaderBackground?: boolean;
|
|
26
28
|
};
|
|
27
29
|
export declare function renderToolBlock(entry: ToolBlockEntry, rule: ResolvedToolRule, width: number, colors: Theme["colors"], options?: ToolBlockRenderOptions): RenderedLine[];
|
|
@@ -10,7 +10,10 @@ export function renderToolBlock(entry, rule, width, colors, options = {}) {
|
|
|
10
10
|
const stateIcon = toolStatusIcon(entry);
|
|
11
11
|
const toolColor = resolveColor(rule.color, colors);
|
|
12
12
|
const toolOutputColor = colors.statusForeground;
|
|
13
|
-
const headerLabel = entry.headerLabel ?? entry.toolName;
|
|
13
|
+
const headerLabel = (entry.headerLabel ?? entry.toolName).toLowerCase();
|
|
14
|
+
const bg = options.backgroundOverride;
|
|
15
|
+
const applyBackground = bg ? (lines) => { for (const line of lines)
|
|
16
|
+
line.backgroundOverride = bg; } : (_lines) => { };
|
|
14
17
|
const headerPrefix = headerLabel ? `${stateIcon} ${headerLabel}` : stateIcon;
|
|
15
18
|
const headerArgs = formatToolHeaderArgs(entry.headerArgs);
|
|
16
19
|
const headerArgsWidth = width - stringDisplayWidth(headerPrefix) - 1;
|
|
@@ -22,14 +25,22 @@ export function renderToolBlock(entry, rule, width, colors, options = {}) {
|
|
|
22
25
|
text: header,
|
|
23
26
|
target,
|
|
24
27
|
colorOverride: toolColor,
|
|
28
|
+
...(options.backgroundOverride && !options.skipHeaderBackground ? { backgroundOverride: options.backgroundOverride } : {}),
|
|
25
29
|
segments: [
|
|
26
30
|
{ start: 0, end: stateIcon.length, foreground: toolStatusIconColor(entry, colors), bold: true },
|
|
31
|
+
{ start: stateIcon.length, end: headerPrefix.length, bold: true },
|
|
27
32
|
...headerArgsStyledSegments(headerArgsStart, clippedHeaderArgs.length, entry.headerArgsSegments, colors),
|
|
28
33
|
],
|
|
29
34
|
};
|
|
30
35
|
const headerLines = [headerLine];
|
|
31
36
|
if (expanded) {
|
|
32
37
|
headerLines.push(...renderToolBodyLines(entry.expandedText, width, target, toolOutputColor, entry.bodyStyle, colors, entry.syntaxHighlight, entry.bodyWrap, hasLspDiagnostics, entry.bodyLineStyles, entry.preserveAnsi));
|
|
38
|
+
if (options.skipHeaderBackground && headerLines.length > 1) {
|
|
39
|
+
applyBackground(headerLines.slice(1));
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
applyBackground(headerLines);
|
|
43
|
+
}
|
|
33
44
|
return headerLines;
|
|
34
45
|
}
|
|
35
46
|
if (rule.compactHidden || (rule.defaultExpanded === true && !options.superCompact))
|
|
@@ -39,6 +50,7 @@ export function renderToolBlock(entry, rule, width, colors, options = {}) {
|
|
|
39
50
|
return headerLines;
|
|
40
51
|
if (!options.superCompact) {
|
|
41
52
|
headerLines.push(...renderCollapsedPreviewLines(entry, body, rule, width, target, toolOutputColor, colors, hasLspDiagnostics));
|
|
53
|
+
applyBackground(headerLines);
|
|
42
54
|
return headerLines;
|
|
43
55
|
}
|
|
44
56
|
const preview = collapsedInlinePreview(body, rule, entry.preserveAnsi);
|
package/dist/app/runtime.d.ts
CHANGED
|
@@ -33,9 +33,11 @@ export declare function bundledSkillsInstallPath(homeDir?: string): string;
|
|
|
33
33
|
export declare function ensurePiToolsSuiteExtensionInstalled(options?: PiToolsSuiteInstallOptions): Promise<PiToolsSuiteInstallResult>;
|
|
34
34
|
export declare function ensureBundledSkillsInstalled(options?: BundledSkillsInstallOptions): Promise<BundledSkillsInstallResult>;
|
|
35
35
|
export declare function getBundledExtensionPaths(): string[];
|
|
36
|
+
export declare function getBundledExtensionPathsAsync(): Promise<string[]>;
|
|
36
37
|
export declare function prioritizeBundledQuestionExtension(base: LoadExtensionsResult, questionExtensionPath?: string): LoadExtensionsResult;
|
|
37
38
|
export type CreatePixRuntimeOptions = {
|
|
38
39
|
eventBus?: EventBus;
|
|
40
|
+
config?: PixConfig;
|
|
39
41
|
};
|
|
40
42
|
type RuntimeSessionManagerModelState = Pick<SessionManager, "getEntries" | "getBranch">;
|
|
41
43
|
export declare function resolvePixRuntimeModelRef(options: Pick<AppOptions, "modelRef">, sessionManager: RuntimeSessionManagerModelState, config?: PixConfig): string | undefined;
|
package/dist/app/runtime.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import { cp, lstat, mkdir, readlink, realpath, rm, symlink } from "node:fs/promises";
|
|
2
|
+
import { access, cp, lstat, mkdir, readlink, realpath, rm, symlink } from "node:fs/promises";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -40,7 +40,7 @@ export function bundledSkillsInstallPath(homeDir = homedir()) {
|
|
|
40
40
|
export async function ensurePiToolsSuiteExtensionInstalled(options = {}) {
|
|
41
41
|
const sourcePath = resolve(options.sourcePath ?? piToolsSuiteExtensionSourcePath());
|
|
42
42
|
const targetPath = resolve(options.targetPath ?? piToolsSuiteExtensionInstallPath(options.agentDir));
|
|
43
|
-
if (!
|
|
43
|
+
if (!(await extensionEntryExistsAsync(sourcePath))) {
|
|
44
44
|
return { action: "missing-source", sourcePath, targetPath };
|
|
45
45
|
}
|
|
46
46
|
await mkdir(dirname(targetPath), { recursive: true });
|
|
@@ -84,6 +84,14 @@ export function getBundledExtensionPaths() {
|
|
|
84
84
|
bundledTerminalBellExtensionPath(),
|
|
85
85
|
].filter(extensionEntryExists);
|
|
86
86
|
}
|
|
87
|
+
export async function getBundledExtensionPathsAsync() {
|
|
88
|
+
const paths = await Promise.all([
|
|
89
|
+
bundledQuestionExtensionPath(),
|
|
90
|
+
bundledSessionTitleExtensionPath(),
|
|
91
|
+
bundledTerminalBellExtensionPath(),
|
|
92
|
+
].map(async (extensionPath) => await extensionEntryExistsAsync(extensionPath) ? extensionPath : undefined));
|
|
93
|
+
return paths.filter((path) => path !== undefined);
|
|
94
|
+
}
|
|
87
95
|
export function prioritizeBundledQuestionExtension(base, questionExtensionPath = bundledQuestionExtensionPath()) {
|
|
88
96
|
const bundledQuestionExtensions = base.extensions.filter((extension) => isBundledQuestionExtension(extension, questionExtensionPath));
|
|
89
97
|
if (bundledQuestionExtensions.length === 0)
|
|
@@ -101,6 +109,21 @@ export function prioritizeBundledQuestionExtension(base, questionExtensionPath =
|
|
|
101
109
|
function extensionEntryExists(extensionPath) {
|
|
102
110
|
return existsSync(join(extensionPath, "index.ts")) || existsSync(join(extensionPath, "index.js"));
|
|
103
111
|
}
|
|
112
|
+
async function extensionEntryExistsAsync(extensionPath) {
|
|
113
|
+
try {
|
|
114
|
+
await access(join(extensionPath, "index.ts"));
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
try {
|
|
119
|
+
await access(join(extensionPath, "index.js"));
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
104
127
|
function extensionSymlinkType() {
|
|
105
128
|
return process.platform === "win32" ? "junction" : "dir";
|
|
106
129
|
}
|
|
@@ -198,13 +221,13 @@ export function resolveSessionModelRefFromTail(entries) {
|
|
|
198
221
|
export async function createPixRuntime(options, runtimeOptions = {}) {
|
|
199
222
|
const agentDir = getAgentDir();
|
|
200
223
|
const createRuntime = async ({ cwd, sessionManager, sessionStartEvent }) => {
|
|
201
|
-
const config = loadPixConfig(cwd);
|
|
224
|
+
const config = runtimeOptions.config ?? loadPixConfig(cwd);
|
|
202
225
|
const effectiveModelRef = resolvePixRuntimeModelRef(options, sessionManager, config);
|
|
203
226
|
const parsedModel = effectiveModelRef ? parseModelRef(effectiveModelRef) : undefined;
|
|
204
227
|
const initialThinkingLevel = resolvePixRuntimeInitialThinkingLevel(options, sessionManager, config);
|
|
205
228
|
await ensureBundledSkillsInstalledOnce();
|
|
206
229
|
await ensurePiToolsSuiteExtensionInstalledOnce({ agentDir });
|
|
207
|
-
const bundledExtensionPaths =
|
|
230
|
+
const bundledExtensionPaths = await getBundledExtensionPathsAsync();
|
|
208
231
|
const services = await createAgentSessionServices({
|
|
209
232
|
cwd,
|
|
210
233
|
agentDir,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ANSI_RESET, colorize } from "../../theme.js";
|
|
1
|
+
import { ANSI_RESET, ansiStylePrefix, colorize } from "../../theme.js";
|
|
2
2
|
import { renderMarkdownLine } from "../../markdown-format.js";
|
|
3
3
|
import { syntaxHighlightSegmentsForLine } from "../../syntax-highlight.js";
|
|
4
4
|
import { displayIndexForColumn } from "../../terminal-width.js";
|
|
@@ -105,7 +105,7 @@ export class ScreenStyler {
|
|
|
105
105
|
return chunks.join("");
|
|
106
106
|
}
|
|
107
107
|
styleAnsiLine(text, options) {
|
|
108
|
-
const prefix =
|
|
108
|
+
const prefix = ansiStylePrefix(options);
|
|
109
109
|
if (!prefix)
|
|
110
110
|
return text;
|
|
111
111
|
return `${prefix}${text.replaceAll(ANSI_RESET, `${ANSI_RESET}${prefix}`)}${ANSI_RESET}`;
|
|
@@ -14,6 +14,7 @@ export type AppSessionLifecycleHost = {
|
|
|
14
14
|
inputEditor(): InputEditor;
|
|
15
15
|
enableTerminal(): void;
|
|
16
16
|
disposeRuntimeForQuit(runtime: AgentSessionRuntime): Promise<void>;
|
|
17
|
+
loadStartupConfig(): Promise<void>;
|
|
17
18
|
loadRequestHistory(): Promise<void>;
|
|
18
19
|
startSubagentsPolling(): void;
|
|
19
20
|
closeSdkMenuForBind(): void;
|
|
@@ -67,6 +68,7 @@ export declare class AppSessionLifecycleController {
|
|
|
67
68
|
loadSessionHistory(): void;
|
|
68
69
|
requireRuntime(): AgentSessionRuntime;
|
|
69
70
|
private bindSessionExtensions;
|
|
71
|
+
private collectAvailabilityIssues;
|
|
70
72
|
private isCurrentRuntimeSession;
|
|
71
73
|
private extensionUiScope;
|
|
72
74
|
}
|
|
@@ -16,11 +16,17 @@ export class AppSessionLifecycleController {
|
|
|
16
16
|
throw new Error("pi-ui-extend needs an interactive TTY");
|
|
17
17
|
}
|
|
18
18
|
this.host.enableTerminal();
|
|
19
|
-
await this.host.loadRequestHistory();
|
|
20
19
|
this.host.setRunning(true);
|
|
21
20
|
this.host.startSubagentsPolling();
|
|
22
21
|
this.host.render();
|
|
22
|
+
void this.host.loadRequestHistory().catch((error) => {
|
|
23
|
+
if (!this.host.isRunning())
|
|
24
|
+
return;
|
|
25
|
+
this.host.addEntry({ id: createId("warning"), kind: "system", text: `Request history failed to load: ${stringifyUnknown(error)}` });
|
|
26
|
+
this.host.render();
|
|
27
|
+
});
|
|
23
28
|
try {
|
|
29
|
+
await this.host.loadStartupConfig();
|
|
24
30
|
const runtime = await this.host.createRuntime();
|
|
25
31
|
if (!this.host.isRunning()) {
|
|
26
32
|
await this.host.disposeRuntimeForQuit(runtime);
|
|
@@ -34,21 +40,6 @@ export class AppSessionLifecycleController {
|
|
|
34
40
|
if (isEmptyStartupSession(runtime)) {
|
|
35
41
|
this.host.addEntry({ id: createId("system"), kind: "system", text: createStartupInfoMessage(runtime) });
|
|
36
42
|
}
|
|
37
|
-
await this.host.restoreTabsAfterStartup();
|
|
38
|
-
const availabilityIssues = await collectStartupAvailabilityIssues(runtime);
|
|
39
|
-
for (const issue of availabilityIssues) {
|
|
40
|
-
this.host.addEntry({
|
|
41
|
-
id: createId(issue.kind),
|
|
42
|
-
kind: issue.kind === "error" ? "error" : "system",
|
|
43
|
-
text: issue.message,
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
if (availabilityIssues.some((issue) => issue.kind === "error")) {
|
|
47
|
-
this.host.showToast("Startup dependency unavailable", "error");
|
|
48
|
-
}
|
|
49
|
-
else if (availabilityIssues.length > 0) {
|
|
50
|
-
this.host.showToast("Startup dependency warning", "warning");
|
|
51
|
-
}
|
|
52
43
|
if (runtime.modelFallbackMessage) {
|
|
53
44
|
this.host.addEntry({ id: createId("system"), kind: "system", text: runtime.modelFallbackMessage });
|
|
54
45
|
}
|
|
@@ -59,6 +50,14 @@ export class AppSessionLifecycleController {
|
|
|
59
50
|
this.host.setSessionStatus(runtime.session);
|
|
60
51
|
this.host.setSessionActivity(runtime.session.isStreaming ? "running" : "idle");
|
|
61
52
|
this.host.render();
|
|
53
|
+
void this.collectAvailabilityIssues(runtime);
|
|
54
|
+
void this.host.restoreTabsAfterStartup().catch((error) => {
|
|
55
|
+
if (!this.host.isRunning())
|
|
56
|
+
return;
|
|
57
|
+
this.host.addEntry({ id: createId("warning"), kind: "system", text: `Tab restore failed: ${stringifyUnknown(error)}` });
|
|
58
|
+
this.host.showToast("Could not restore tabs", "warning");
|
|
59
|
+
this.host.render();
|
|
60
|
+
});
|
|
62
61
|
}
|
|
63
62
|
catch (error) {
|
|
64
63
|
this.host.addEntry({ id: createId("error"), kind: "error", text: stringifyUnknown(error) });
|
|
@@ -154,6 +153,34 @@ export class AppSessionLifecycleController {
|
|
|
154
153
|
this.extensionBindSession = session;
|
|
155
154
|
return promise;
|
|
156
155
|
}
|
|
156
|
+
async collectAvailabilityIssues(runtime) {
|
|
157
|
+
try {
|
|
158
|
+
const availabilityIssues = await collectStartupAvailabilityIssues(runtime);
|
|
159
|
+
if (!this.host.isRunning() || this.host.runtime() !== runtime)
|
|
160
|
+
return;
|
|
161
|
+
for (const issue of availabilityIssues) {
|
|
162
|
+
this.host.addEntry({
|
|
163
|
+
id: createId(issue.kind),
|
|
164
|
+
kind: issue.kind === "error" ? "error" : "system",
|
|
165
|
+
text: issue.message,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
if (availabilityIssues.some((issue) => issue.kind === "error")) {
|
|
169
|
+
this.host.showToast("Startup dependency unavailable", "error");
|
|
170
|
+
}
|
|
171
|
+
else if (availabilityIssues.length > 0) {
|
|
172
|
+
this.host.showToast("Startup dependency warning", "warning");
|
|
173
|
+
}
|
|
174
|
+
if (availabilityIssues.length > 0)
|
|
175
|
+
this.host.render();
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
if (!this.host.isRunning() || this.host.runtime() !== runtime)
|
|
179
|
+
return;
|
|
180
|
+
this.host.addEntry({ id: createId("warning"), kind: "system", text: `Startup dependency check failed: ${stringifyUnknown(error)}` });
|
|
181
|
+
this.host.render();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
157
184
|
isCurrentRuntimeSession(runtime, session) {
|
|
158
185
|
return this.host.isRunning() && this.host.runtime() === runtime && runtime.session === session;
|
|
159
186
|
}
|
|
@@ -6,7 +6,7 @@ import type { AppOptions, Entry, SessionActivity, SessionTab, SubmittedUserMessa
|
|
|
6
6
|
export type TabInputState = InputEditorDraftState;
|
|
7
7
|
export type AppTabsControllerHost = {
|
|
8
8
|
readonly options: AppOptions;
|
|
9
|
-
readonly maxProjectSessions?: number;
|
|
9
|
+
readonly maxProjectSessions?: number | (() => number | undefined);
|
|
10
10
|
readonly blinkController: AppBlinkController;
|
|
11
11
|
runtime(): AgentSessionRuntime | undefined;
|
|
12
12
|
createRuntimeForNewSession(): Promise<AgentSessionRuntime>;
|
|
@@ -220,17 +220,12 @@ export class AppTabsController {
|
|
|
220
220
|
}
|
|
221
221
|
this.syncActiveTabFromRuntime({ save: false });
|
|
222
222
|
this.settleStartupTabPlaceholders();
|
|
223
|
-
this.host.resetSessionView();
|
|
224
|
-
if (this.activeTabId)
|
|
225
|
-
this.restoreDeferredUserMessages(this.activeTabId);
|
|
226
|
-
this.host.loadSessionHistory();
|
|
227
|
-
this.host.setSessionStatus(restoredRuntime.session);
|
|
228
|
-
this.host.setSessionActivity(this.sessionActivity(restoredRuntime.session));
|
|
229
223
|
if (this.activeTabId)
|
|
230
224
|
this.restoreInputState(this.activeTabId);
|
|
231
225
|
await this.saveTabs();
|
|
232
226
|
this.scheduleProjectSessionRetention();
|
|
233
227
|
this.scheduleTabPrewarm();
|
|
228
|
+
await this.loadActiveSessionHistory(restoredRuntime);
|
|
234
229
|
}
|
|
235
230
|
async openNewTab() {
|
|
236
231
|
if (this.pendingActiveTabId) {
|
|
@@ -1377,7 +1372,8 @@ export class AppTabsController {
|
|
|
1377
1372
|
return preserved;
|
|
1378
1373
|
}
|
|
1379
1374
|
maxProjectSessions() {
|
|
1380
|
-
const
|
|
1375
|
+
const configured = this.host.maxProjectSessions;
|
|
1376
|
+
const value = typeof configured === "function" ? configured() : configured;
|
|
1381
1377
|
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
|
|
1382
1378
|
}
|
|
1383
1379
|
}
|
package/dist/config.d.ts
CHANGED
|
@@ -72,6 +72,7 @@ export type PixConfig = {
|
|
|
72
72
|
};
|
|
73
73
|
export declare function getPixConfigPath(homeDir?: string): string;
|
|
74
74
|
export declare function getProjectPixConfigPath(cwd: string): string;
|
|
75
|
+
export declare function defaultPixConfig(): PixConfig;
|
|
75
76
|
export declare function resolveDefaultModelRef(config: PixConfig): string | undefined;
|
|
76
77
|
export declare function savePixDefaultModel(modelRef: string): DefaultModelConfig | undefined;
|
|
77
78
|
export declare function savePixDefaultThinking(thinking: string, fallbackModelRef?: string): DefaultModelConfig | undefined;
|
package/dist/config.js
CHANGED
|
@@ -285,19 +285,31 @@ function numberInRange(value, fallback, min, max) {
|
|
|
285
285
|
const rounded = Math.round(value);
|
|
286
286
|
return Math.min(max, Math.max(min, rounded));
|
|
287
287
|
}
|
|
288
|
-
function defaultPixConfig() {
|
|
288
|
+
export function defaultPixConfig() {
|
|
289
289
|
return {
|
|
290
|
-
toolRenderer: DEFAULT_TOOL_RENDERER,
|
|
291
|
-
outputFilters: DEFAULT_OUTPUT_FILTERS,
|
|
292
|
-
promptEnhancer: DEFAULT_PROMPT_ENHANCER,
|
|
293
|
-
autocomplete: DEFAULT_AUTOCOMPLETE,
|
|
294
|
-
modelColors: DEFAULT_MODEL_COLORS,
|
|
290
|
+
toolRenderer: cloneToolRendererConfig(DEFAULT_TOOL_RENDERER),
|
|
291
|
+
outputFilters: { patterns: [...DEFAULT_OUTPUT_FILTERS.patterns] },
|
|
292
|
+
promptEnhancer: { ...DEFAULT_PROMPT_ENHANCER },
|
|
293
|
+
autocomplete: { ...DEFAULT_AUTOCOMPLETE },
|
|
294
|
+
modelColors: { rules: { ...DEFAULT_MODEL_COLORS.rules } },
|
|
295
295
|
iconTheme: { name: resolveAppIconThemeNameFromEnv() },
|
|
296
|
-
dictation: DEFAULT_DICTATION,
|
|
296
|
+
dictation: cloneDictationConfig(DEFAULT_DICTATION),
|
|
297
297
|
ignoreContextFiles: false,
|
|
298
298
|
maxProjectSessions: 0,
|
|
299
299
|
};
|
|
300
300
|
}
|
|
301
|
+
function cloneToolRendererConfig(config) {
|
|
302
|
+
return {
|
|
303
|
+
default: { ...config.default },
|
|
304
|
+
tools: Object.fromEntries(Object.entries(config.tools).map(([name, rule]) => [name, { ...rule }])),
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
function cloneDictationConfig(config) {
|
|
308
|
+
return {
|
|
309
|
+
languages: Object.fromEntries(Object.entries(config.languages).map(([language, model]) => [language, { ...model }])),
|
|
310
|
+
...(config.language === undefined ? {} : { language: config.language }),
|
|
311
|
+
};
|
|
312
|
+
}
|
|
301
313
|
function pixConfigFromParsed(parsed, fallback = defaultPixConfig()) {
|
|
302
314
|
const toolRenderer = extractToolRendererConfig(parsed) ?? fallback.toolRenderer;
|
|
303
315
|
const outputFilters = extractOutputFiltersConfig(parsed) ?? fallback.outputFilters;
|
|
@@ -6,6 +6,7 @@ export type RenderedMarkdownLine = {
|
|
|
6
6
|
end: number;
|
|
7
7
|
bold: true;
|
|
8
8
|
}[];
|
|
9
|
+
heading?: boolean;
|
|
9
10
|
};
|
|
10
11
|
export type RenderedMarkdownTextLine = {
|
|
11
12
|
text: string;
|
|
@@ -15,6 +16,7 @@ export type RenderedMarkdownTextLine = {
|
|
|
15
16
|
bold: true;
|
|
16
17
|
}[] | undefined;
|
|
17
18
|
syntaxHighlight?: SyntaxLineHighlight | undefined;
|
|
19
|
+
heading?: boolean;
|
|
18
20
|
};
|
|
19
21
|
export declare function formatMarkdownTables(text: string, maxWidth?: number): string;
|
|
20
22
|
export declare function renderMarkdownLine(text: string, start?: number): RenderedMarkdownLine;
|
package/dist/markdown-format.js
CHANGED
|
@@ -46,6 +46,7 @@ export function renderMarkdownLine(text, start = 0) {
|
|
|
46
46
|
let rendered = text.slice(0, safeStart);
|
|
47
47
|
let index = safeStart;
|
|
48
48
|
let inCode = false;
|
|
49
|
+
const isHeading = /^\s{0,3}#{1,6}\s/.test(text);
|
|
49
50
|
while (index < text.length) {
|
|
50
51
|
const char = text[index] ?? "";
|
|
51
52
|
if (char === "`" && !isEscaped(text, index)) {
|
|
@@ -67,7 +68,7 @@ export function renderMarkdownLine(text, start = 0) {
|
|
|
67
68
|
rendered += char;
|
|
68
69
|
index += 1;
|
|
69
70
|
}
|
|
70
|
-
return { text: rendered, segments };
|
|
71
|
+
return { text: rendered, segments, ...(isHeading ? { heading: true } : {}) };
|
|
71
72
|
}
|
|
72
73
|
export function renderMarkdownTextLines(text, width, start = 0) {
|
|
73
74
|
const lines = [];
|
|
@@ -80,12 +81,14 @@ export function renderMarkdownTextLines(text, width, start = 0) {
|
|
|
80
81
|
const closesFence = Boolean(fence && nextFence && fence.marker === nextFence.marker && nextFence.length >= fence.length);
|
|
81
82
|
const opensFence = !fence && nextFence !== undefined;
|
|
82
83
|
const syntaxHighlight = markdownLineSyntaxHighlight(fence, Boolean(opensFence || closesFence), start);
|
|
83
|
-
const
|
|
84
|
+
const isHeadingLine = !fence && /^\s{0,3}#{1,6}\s/.test(rawLine);
|
|
85
|
+
const markdownLine = syntaxHighlight?.language === "markdown" || isHeadingLine ? renderMarkdownLine(rawLine) : undefined;
|
|
84
86
|
for (const wrapped of wrapRenderedMarkdownLine(markdownLine ?? { text: rawLine, segments: [] }, width)) {
|
|
85
87
|
lines.push({
|
|
86
88
|
text: wrapped.text,
|
|
87
89
|
...(wrapped.segments.length > 0 ? { segments: wrapped.segments } : {}),
|
|
88
90
|
...(syntaxHighlight ? { syntaxHighlight } : {}),
|
|
91
|
+
...(isHeadingLine ? { heading: true } : {}),
|
|
89
92
|
});
|
|
90
93
|
}
|
|
91
94
|
if (opensFence && nextFence) {
|
package/dist/syntax-highlight.js
CHANGED
|
@@ -44,6 +44,8 @@ const MARKDOWN_FENCE_LANGUAGES = {
|
|
|
44
44
|
javascript: "javascript",
|
|
45
45
|
js: "javascript",
|
|
46
46
|
json: "json",
|
|
47
|
+
markdown: "markdown",
|
|
48
|
+
md: "markdown",
|
|
47
49
|
jsonc: "json",
|
|
48
50
|
jsx: "javascript",
|
|
49
51
|
python: "python",
|
|
@@ -227,7 +229,7 @@ function markdownSegments(code, colors) {
|
|
|
227
229
|
const segments = [];
|
|
228
230
|
const heading = /^(\s{0,3}#{1,6}\s+)(.*)$/.exec(code);
|
|
229
231
|
if (heading) {
|
|
230
|
-
|
|
232
|
+
segments.push({ start: 0, end: code.length, foreground: colors.heading, bold: true });
|
|
231
233
|
return segments;
|
|
232
234
|
}
|
|
233
235
|
const fence = /^\s*`{3,}/.exec(code);
|
package/dist/theme.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export type Theme = {
|
|
|
17
17
|
tabBorder: string;
|
|
18
18
|
assistantMessageBackground: string;
|
|
19
19
|
userMessageBackground: string;
|
|
20
|
+
thinkingMessageBackground: string;
|
|
20
21
|
inputCursorBackground: string;
|
|
21
22
|
popupForeground: string;
|
|
22
23
|
popupBackground: string;
|
|
@@ -32,6 +33,7 @@ export type Theme = {
|
|
|
32
33
|
accent: string;
|
|
33
34
|
success: string;
|
|
34
35
|
warning: string;
|
|
36
|
+
heading: string;
|
|
35
37
|
info: string;
|
|
36
38
|
toolMutation: string;
|
|
37
39
|
toolSearch: string;
|
|
@@ -63,5 +65,6 @@ export type TextStyleOptions = {
|
|
|
63
65
|
};
|
|
64
66
|
export declare function parseThemeName(value: string): ThemeName | undefined;
|
|
65
67
|
export declare function colorize(text: string, options: TextStyleOptions): string;
|
|
68
|
+
export declare function ansiStylePrefix(options: TextStyleOptions): string;
|
|
66
69
|
export declare function colorLine(text: string, width: number, options: TextStyleOptions): string;
|
|
67
70
|
export declare function padOrTrimPlain(text: string, width: number): string;
|
package/dist/theme.js
CHANGED
|
@@ -5,7 +5,7 @@ export const THEMES = {
|
|
|
5
5
|
colors: {
|
|
6
6
|
background: "#090d13",
|
|
7
7
|
foreground: "#d6deeb",
|
|
8
|
-
assistantForeground: "#
|
|
8
|
+
assistantForeground: "#a4bce0",
|
|
9
9
|
muted: "#7d8590",
|
|
10
10
|
headerForeground: "#c9d1d9",
|
|
11
11
|
headerBackground: "#161b22",
|
|
@@ -16,8 +16,9 @@ export const THEMES = {
|
|
|
16
16
|
inputBorder: "#30363d",
|
|
17
17
|
inputBorderWidgetBackground: "#2a2f36",
|
|
18
18
|
tabBorder: "#7d8590",
|
|
19
|
-
assistantMessageBackground: "
|
|
20
|
-
userMessageBackground: "
|
|
19
|
+
assistantMessageBackground: "",
|
|
20
|
+
userMessageBackground: "",
|
|
21
|
+
thinkingMessageBackground: "",
|
|
21
22
|
inputCursorBackground: "#7fb3c8",
|
|
22
23
|
popupForeground: "#e6edf3",
|
|
23
24
|
popupBackground: "#1e1e1e",
|
|
@@ -33,18 +34,19 @@ export const THEMES = {
|
|
|
33
34
|
accent: "#7aa2d6",
|
|
34
35
|
success: "#7ca982",
|
|
35
36
|
warning: "#d49a4a",
|
|
37
|
+
heading: "#d4b35e",
|
|
36
38
|
info: "#7fb3c8",
|
|
37
|
-
toolMutation: "#
|
|
38
|
-
toolSearch: "#
|
|
39
|
-
toolTitle: "#
|
|
40
|
-
toolBash: "#
|
|
41
|
-
toolRead: "#
|
|
42
|
-
toolIndex: "#
|
|
43
|
-
toolEdit: "#
|
|
44
|
-
toolWeb: "#
|
|
45
|
-
toolMeta: "#
|
|
46
|
-
thinkingForeground: "#
|
|
47
|
-
userForeground: "#
|
|
39
|
+
toolMutation: "#b8899e", // ~10% brighter from #a67c8f
|
|
40
|
+
toolSearch: "#9d8abb", // ~10% brighter from #8c7aa8
|
|
41
|
+
toolTitle: "#899199", // ~10% brighter from #7b848c
|
|
42
|
+
toolBash: "#b89071", // ~10% brighter from #a68266
|
|
43
|
+
toolRead: "#6d9b82", // ~10% brighter from #628c76
|
|
44
|
+
toolIndex: "#7692b4", // ~10% brighter from #6a84a3
|
|
45
|
+
toolEdit: "#b47389", // ~10% brighter from #a3687c
|
|
46
|
+
toolWeb: "#8192b6", // ~10% brighter from #7584a6
|
|
47
|
+
toolMeta: "#7d8192", // ~10% brighter from #707485
|
|
48
|
+
thinkingForeground: "#64748b",
|
|
49
|
+
userForeground: "#d97706",
|
|
48
50
|
thinkingXHigh: "#ff8a86",
|
|
49
51
|
modelOpenAI: "#c8b45a",
|
|
50
52
|
statusDotBase: "#30363d",
|
|
@@ -69,8 +71,9 @@ export const THEMES = {
|
|
|
69
71
|
inputBorder: "#334155",
|
|
70
72
|
inputBorderWidgetBackground: "#f1f5f9",
|
|
71
73
|
tabBorder: "#64748b",
|
|
72
|
-
assistantMessageBackground: "
|
|
73
|
-
userMessageBackground: "
|
|
74
|
+
assistantMessageBackground: "",
|
|
75
|
+
userMessageBackground: "",
|
|
76
|
+
thinkingMessageBackground: "",
|
|
74
77
|
inputCursorBackground: "#0284c7",
|
|
75
78
|
popupForeground: "#0f172a",
|
|
76
79
|
popupBackground: "#ffffff",
|
|
@@ -86,18 +89,19 @@ export const THEMES = {
|
|
|
86
89
|
accent: "#315f9f",
|
|
87
90
|
success: "#47794c",
|
|
88
91
|
warning: "#9a631d",
|
|
92
|
+
heading: "#b88a28",
|
|
89
93
|
info: "#246b8e",
|
|
90
|
-
toolMutation: "#
|
|
91
|
-
toolSearch: "#
|
|
92
|
-
toolTitle: "#
|
|
93
|
-
toolBash: "#
|
|
94
|
-
toolRead: "#
|
|
95
|
-
toolIndex: "#
|
|
96
|
-
toolEdit: "#
|
|
97
|
-
toolWeb: "#
|
|
98
|
-
toolMeta: "#
|
|
94
|
+
toolMutation: "#8c5c70", // reverted
|
|
95
|
+
toolSearch: "#6e608c", // reverted
|
|
96
|
+
toolTitle: "#626c78", // reverted
|
|
97
|
+
toolBash: "#8c7556", // reverted
|
|
98
|
+
toolRead: "#507a62", // reverted
|
|
99
|
+
toolIndex: "#567a96", // reverted
|
|
100
|
+
toolEdit: "#8c586c", // reverted
|
|
101
|
+
toolWeb: "#5c6a8c", // reverted
|
|
102
|
+
toolMeta: "#787e8a", // reverted
|
|
99
103
|
thinkingForeground: "#6b5491",
|
|
100
|
-
userForeground: "#
|
|
104
|
+
userForeground: "#854d0e",
|
|
101
105
|
thinkingXHigh: "#cf333d",
|
|
102
106
|
modelOpenAI: "#75671f",
|
|
103
107
|
statusDotBase: "#334155",
|
|
@@ -108,10 +112,20 @@ export const THEMES = {
|
|
|
108
112
|
},
|
|
109
113
|
};
|
|
110
114
|
export const ANSI_RESET = "\x1b[0m";
|
|
115
|
+
const rgbCodeCache = new Map();
|
|
116
|
+
const stylePrefixCache = new Map();
|
|
111
117
|
export function parseThemeName(value) {
|
|
112
118
|
return value === "dark" || value === "light" ? value : undefined;
|
|
113
119
|
}
|
|
114
120
|
export function colorize(text, options) {
|
|
121
|
+
const prefix = ansiStylePrefix(options);
|
|
122
|
+
return prefix ? `${prefix}${text}${ANSI_RESET}` : text;
|
|
123
|
+
}
|
|
124
|
+
export function ansiStylePrefix(options) {
|
|
125
|
+
const cacheKey = styleCacheKey(options);
|
|
126
|
+
const cached = stylePrefixCache.get(cacheKey);
|
|
127
|
+
if (cached !== undefined)
|
|
128
|
+
return cached;
|
|
115
129
|
const codes = [];
|
|
116
130
|
if (options.bold)
|
|
117
131
|
codes.push("1");
|
|
@@ -123,7 +137,9 @@ export function colorize(text, options) {
|
|
|
123
137
|
codes.push(rgbCode("38", options.foreground));
|
|
124
138
|
if (options.background)
|
|
125
139
|
codes.push(rgbCode("48", options.background));
|
|
126
|
-
|
|
140
|
+
const prefix = codes.length === 0 ? "" : `\x1b[${codes.join(";")}m`;
|
|
141
|
+
stylePrefixCache.set(cacheKey, prefix);
|
|
142
|
+
return prefix;
|
|
127
143
|
}
|
|
128
144
|
export function colorLine(text, width, options) {
|
|
129
145
|
return colorize(padOrTrimPlain(text, width), options);
|
|
@@ -133,8 +149,17 @@ export function padOrTrimPlain(text, width) {
|
|
|
133
149
|
}
|
|
134
150
|
function rgbCode(prefix, hex) {
|
|
135
151
|
const normalized = hex.replace(/^#/, "");
|
|
152
|
+
const cacheKey = `${prefix}:${normalized}`;
|
|
153
|
+
const cached = rgbCodeCache.get(cacheKey);
|
|
154
|
+
if (cached)
|
|
155
|
+
return cached;
|
|
136
156
|
const red = Number.parseInt(normalized.slice(0, 2), 16);
|
|
137
157
|
const green = Number.parseInt(normalized.slice(2, 4), 16);
|
|
138
158
|
const blue = Number.parseInt(normalized.slice(4, 6), 16);
|
|
139
|
-
|
|
159
|
+
const code = `${prefix};2;${red};${green};${blue}`;
|
|
160
|
+
rgbCodeCache.set(cacheKey, code);
|
|
161
|
+
return code;
|
|
162
|
+
}
|
|
163
|
+
function styleCacheKey(options) {
|
|
164
|
+
return `${options.bold ? 1 : 0}|${options.underline ? 1 : 0}|${options.strikethrough ? 1 : 0}|${options.foreground ?? ""}|${options.background ?? ""}`;
|
|
140
165
|
}
|
|
@@ -96,6 +96,9 @@ export function getGoogleOAuthClientCredentials(...sources: Array<unknown>): Goo
|
|
|
96
96
|
const clientSecret = nestedClientSecret ?? stringProperty(source, ["clientSecret", "client_secret", "googleClientSecret", "google_client_secret", "oauthClientSecret", "oauth_client_secret"]);
|
|
97
97
|
if (clientId) return { clientId, ...(clientSecret ? { clientSecret } : {}) };
|
|
98
98
|
}
|
|
99
|
+
const clientId = process.env.PI_ANTIGRAVITY_GOOGLE_CLIENT_ID;
|
|
100
|
+
const clientSecret = process.env.PI_ANTIGRAVITY_GOOGLE_CLIENT_SECRET;
|
|
101
|
+
if (clientId) return { clientId, ...(clientSecret ? { clientSecret } : {}) };
|
|
99
102
|
return undefined;
|
|
100
103
|
}
|
|
101
104
|
|
|
@@ -215,13 +218,15 @@ export async function importOpencodeAntigravityAccount(options: {
|
|
|
215
218
|
};
|
|
216
219
|
}
|
|
217
220
|
|
|
221
|
+
const oauthClient = getGoogleOAuthClientCredentials(selected.account, existing);
|
|
218
222
|
piAuth[PROVIDER_ID] = {
|
|
223
|
+
...existing,
|
|
219
224
|
type: "oauth",
|
|
220
225
|
refresh,
|
|
221
226
|
access: "",
|
|
222
227
|
expires: 0,
|
|
223
228
|
email: selected.account.email,
|
|
224
|
-
...
|
|
229
|
+
...(oauthClient ? { oauthClient } : {}),
|
|
225
230
|
accounts: storage.accounts.filter((account) => account.enabled !== false && getAccountRefreshToken(account)),
|
|
226
231
|
activeIndex: selected.index,
|
|
227
232
|
};
|
|
@@ -224,7 +224,7 @@ export async function addAntigravityAccount(
|
|
|
224
224
|
async function refreshAccountToken(account: OpencodeAntigravityAccount, oauthClient?: GoogleOAuthClientCredentials): Promise<RefreshedAntigravityAccount> {
|
|
225
225
|
const refreshToken = getAccountRefreshToken(account);
|
|
226
226
|
if (!refreshToken) throw new Error(`Missing refresh token for Antigravity account ${account.email ?? "<unknown>"}`);
|
|
227
|
-
const clientCredentials = getGoogleOAuthClientCredentials(account
|
|
227
|
+
const clientCredentials = getGoogleOAuthClientCredentials(account, oauthClient);
|
|
228
228
|
assertGoogleOAuthCredentialsConfigured(clientCredentials);
|
|
229
229
|
const projectId = getAccountProjectId(account);
|
|
230
230
|
const start = Date.now();
|