@averyyy/pi-coding-agent 0.84.1-piclient.2 → 0.84.2-piclient.1
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 +56 -0
- package/README.md +1 -0
- package/dist/cli/args.d.ts +1 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +11 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/auth-command.d.ts.map +1 -1
- package/dist/cli/auth-command.js +5 -4
- package/dist/cli/auth-command.js.map +1 -1
- package/dist/core/agent-session.d.ts +7 -2
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +6 -6
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/experimental.d.ts +4 -0
- package/dist/core/experimental.d.ts.map +1 -1
- package/dist/core/experimental.js +4 -0
- package/dist/core/experimental.js.map +1 -1
- package/dist/core/extensions/types.d.ts +4 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/keybindings.d.ts +29 -0
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +1 -0
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/model-config.d.ts +5 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +1 -0
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/project-trust.d.ts.map +1 -1
- package/dist/core/project-trust.js +2 -2
- package/dist/core/project-trust.js.map +1 -1
- package/dist/core/sdk.d.ts +5 -3
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +2 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +2 -2
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +6 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +12 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +1 -1
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +2 -0
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +2 -0
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +1 -1
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +1 -1
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +2 -0
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +2 -0
- package/dist/core/tools/write.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +5 -1
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +2 -1
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +3 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +10 -0
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +10 -1
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +17 -2
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +144 -28
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/model-catalog-refresh.d.ts +7 -0
- package/dist/modes/interactive/model-catalog-refresh.d.ts.map +1 -0
- package/dist/modes/interactive/model-catalog-refresh.js +34 -0
- package/dist/modes/interactive/model-catalog-refresh.js.map +1 -0
- package/dist/modes/interactive/theme/dark.json +2 -0
- package/dist/modes/interactive/theme/light.json +2 -0
- package/dist/modes/interactive/theme/theme-controller.d.ts +10 -2
- package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme-controller.js +25 -11
- package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
- package/dist/modes/interactive/theme/theme-schema.json +9 -1
- package/dist/modes/interactive/theme/theme.d.ts +5 -3
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +12 -2
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/json-event.d.ts +4 -1
- package/dist/modes/json-event.d.ts.map +1 -1
- package/dist/modes/json-event.js +5 -2
- package/dist/modes/json-event.js.map +1 -1
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +1 -1
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/server/create-harness.d.ts.map +1 -1
- package/dist/server/create-harness.js +2 -0
- package/dist/server/create-harness.js.map +1 -1
- package/dist/utils/tools-manager.d.ts +10 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +14 -19
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/compaction.md +6 -6
- package/docs/custom-provider.md +1 -1
- package/docs/environment-variables.md +8 -2
- package/docs/extensions.md +4 -0
- package/docs/json.md +6 -4
- package/docs/keybindings.md +9 -2
- package/docs/rpc.md +15 -4
- package/docs/sdk.md +9 -0
- package/docs/settings.md +31 -0
- package/docs/terminal-setup.md +23 -2
- package/docs/themes.md +24 -3
- package/docs/tui.md +2 -2
- package/docs/usage.md +3 -2
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/subagent/README.md +2 -0
- package/examples/extensions/subagent/agents.ts +40 -9
- package/examples/extensions/subagent/index.ts +21 -3
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +6 -6
|
@@ -69,9 +69,11 @@ import { TrustSelectorComponent } from "./components/trust-selector.js";
|
|
|
69
69
|
import { UserMessageComponent } from "./components/user-message.js";
|
|
70
70
|
import { UserMessageSelectorComponent } from "./components/user-message-selector.js";
|
|
71
71
|
import { editInExternalEditor } from "./external-editor.js";
|
|
72
|
+
import { refreshModelCatalogs } from "./model-catalog-refresh.js";
|
|
72
73
|
import { getModelSearchText } from "./model-search.js";
|
|
73
74
|
import { getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getThemeByName, onThemeChange, setRegisteredThemes, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
|
|
74
75
|
import { InteractiveThemeController } from "./theme/theme-controller.js";
|
|
76
|
+
const TRANSCRIPT_LAZY_LOAD_BATCH_SIZE = 20;
|
|
75
77
|
function isExpandable(obj) {
|
|
76
78
|
return typeof obj === "object" && obj !== null && "setExpanded" in obj && typeof obj.setExpanded === "function";
|
|
77
79
|
}
|
|
@@ -169,9 +171,21 @@ function formatLoginProviderCompletionDescription(provider) {
|
|
|
169
171
|
export function createInteractiveTui(options) {
|
|
170
172
|
const terminal = options.terminal ?? new ProcessTerminal();
|
|
171
173
|
if (options.tuiMode === "fullscreen") {
|
|
174
|
+
const styleSearchMatch = (text) => theme.bg("searchMatchBg", theme.fg("searchMatchText", text));
|
|
172
175
|
return new TuiAltScreen(terminal, options.showHardwareCursor, options.logDirectory, {
|
|
176
|
+
searchMatchStyle: (text) => theme.underline(styleSearchMatch(text)),
|
|
177
|
+
searchCurrentMatchStyle: (text) => theme.bold(theme.inverse(styleSearchMatch(text))),
|
|
173
178
|
openUrl: openBrowser,
|
|
174
179
|
onRightClickPaste: options.onRightClickPaste,
|
|
180
|
+
copySelection: async (text) => {
|
|
181
|
+
try {
|
|
182
|
+
await copyToClipboard(text);
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
},
|
|
175
189
|
});
|
|
176
190
|
}
|
|
177
191
|
return new TuiMainScreen(terminal, options.showHardwareCursor, options.logDirectory);
|
|
@@ -216,6 +230,11 @@ export class InteractiveMode {
|
|
|
216
230
|
chatContainer;
|
|
217
231
|
documentContainer;
|
|
218
232
|
transcriptScrollView;
|
|
233
|
+
transcriptLazyEntries = [];
|
|
234
|
+
loadedTranscriptEntryIds = new Set();
|
|
235
|
+
transcriptWindowInitialized = false;
|
|
236
|
+
transcriptLatestCompactionId;
|
|
237
|
+
loadingEarlierTranscript = false;
|
|
219
238
|
fullscreenLayoutRoot;
|
|
220
239
|
pendingMessagesContainer;
|
|
221
240
|
statusContainer;
|
|
@@ -253,6 +272,7 @@ export class InteractiveMode {
|
|
|
253
272
|
// Status line tracking (for mutating immediately-sequential status updates)
|
|
254
273
|
lastStatusSpacer = undefined;
|
|
255
274
|
lastStatusText = undefined;
|
|
275
|
+
managedToolStatusStarted = false;
|
|
256
276
|
// Streaming message tracking
|
|
257
277
|
streamingComponent = undefined;
|
|
258
278
|
streamingMessage = undefined;
|
|
@@ -333,6 +353,7 @@ export class InteractiveMode {
|
|
|
333
353
|
});
|
|
334
354
|
this.runtimeHost.setRebindSession(async () => {
|
|
335
355
|
await this.rebindCurrentSession({ renderBeforeBind: true });
|
|
356
|
+
await this.themeController.applyFromSettings();
|
|
336
357
|
});
|
|
337
358
|
this.version = VERSION;
|
|
338
359
|
this.renderer = createInteractiveTui({
|
|
@@ -375,7 +396,12 @@ export class InteractiveMode {
|
|
|
375
396
|
this.outputPad = this.settingsManager.getOutputPad();
|
|
376
397
|
// Register themes from resource loader and initialize
|
|
377
398
|
setRegisteredThemes(this.session.resourceLoader.getThemes().themes);
|
|
378
|
-
this.themeController = new InteractiveThemeController(this.ui,
|
|
399
|
+
this.themeController = new InteractiveThemeController(this.ui, {
|
|
400
|
+
getSettingsManager: () => this.settingsManager,
|
|
401
|
+
showError: (message) => this.showError(message),
|
|
402
|
+
onChanged: () => this.updateEditorBorderColor(),
|
|
403
|
+
initialThemeSetting: options.initialThemeSetting,
|
|
404
|
+
});
|
|
379
405
|
}
|
|
380
406
|
getAutocompleteSourceTag(sourceInfo) {
|
|
381
407
|
if (!sourceInfo) {
|
|
@@ -538,14 +564,14 @@ export class InteractiveMode {
|
|
|
538
564
|
tui.setLayoutRoot(this.fullscreenLayoutRoot);
|
|
539
565
|
}
|
|
540
566
|
}
|
|
541
|
-
stopInteractiveTui() {
|
|
542
|
-
if (this.renderer.mode === "fullscreen") {
|
|
567
|
+
stopInteractiveTui(fullscreenExitOutput) {
|
|
568
|
+
if (this.renderer.mode === "fullscreen" && fullscreenExitOutput === "transcript") {
|
|
543
569
|
while (this.renderer.hasOverlayEntries)
|
|
544
570
|
this.renderer.hideOverlay();
|
|
545
571
|
this.switchTuiMode("regular", false, false);
|
|
546
572
|
this.renderer.renderNow();
|
|
547
573
|
}
|
|
548
|
-
this.ui.stop();
|
|
574
|
+
this.ui.stop({ preserveScreen: this.renderer.mode === "fullscreen" });
|
|
549
575
|
}
|
|
550
576
|
switchTuiMode(mode, restoreProgress = true, startRenderer = true) {
|
|
551
577
|
const previousUi = this.renderer;
|
|
@@ -602,10 +628,6 @@ export class InteractiveMode {
|
|
|
602
628
|
this.registerSignalHandlers();
|
|
603
629
|
// Load changelog (only show new entries, skip for resumed sessions)
|
|
604
630
|
this.changelogMarkdown = this.getChangelogForDisplay();
|
|
605
|
-
// Ensure fd and rg are available (downloads if missing, adds to PATH via getBinDir)
|
|
606
|
-
// Both are needed: fd for autocomplete, rg for grep tool and bash commands
|
|
607
|
-
const [fdPath] = await Promise.all([ensureTool("fd"), ensureTool("rg")]);
|
|
608
|
-
this.fdPath = fdPath;
|
|
609
631
|
if (this.session.scopedModels.length > 0 && (this.options.verbose || !this.settingsManager.getQuietStartup())) {
|
|
610
632
|
const modelList = this.session.scopedModels
|
|
611
633
|
.map((sm) => {
|
|
@@ -625,6 +647,7 @@ export class InteractiveMode {
|
|
|
625
647
|
follow: "end",
|
|
626
648
|
primary: true,
|
|
627
649
|
overscroll: "chain",
|
|
650
|
+
onScroll: (scrollTop) => this.handleTranscriptScroll(scrollTop),
|
|
628
651
|
scrollbar: this.settingsManager.getFullscreenScrollbar(),
|
|
629
652
|
scrollbarStyle: (text) => theme.bg("scrollbarThumb", text),
|
|
630
653
|
});
|
|
@@ -649,9 +672,11 @@ export class InteractiveMode {
|
|
|
649
672
|
this.widgetContainerBelow,
|
|
650
673
|
this.footerContainer,
|
|
651
674
|
]);
|
|
675
|
+
// Accept text while startup completes, but only enable interrupt, exit, and submission feedback.
|
|
676
|
+
this.defaultEditor.onAction("app.clear", () => this.handleCtrlC());
|
|
677
|
+
this.defaultEditor.onCtrlD = () => this.handleCtrlD();
|
|
678
|
+
this.defaultEditor.onSubmit = (text) => this.handleStartupSubmit(text);
|
|
652
679
|
this.ui.setFocus(this.editor);
|
|
653
|
-
this.setupKeyHandlers();
|
|
654
|
-
this.setupEditorSubmitHandler();
|
|
655
680
|
// Start the UI before initializing extensions so session_start handlers can use interactive dialogs
|
|
656
681
|
this.ui.start();
|
|
657
682
|
this.isInitialized = true;
|
|
@@ -703,6 +728,18 @@ export class InteractiveMode {
|
|
|
703
728
|
this.headerContainer.addChild(this.builtInHeader);
|
|
704
729
|
}
|
|
705
730
|
this.ui.requestRender();
|
|
731
|
+
// Ensure fd and rg are available after mounting the TUI (downloads if missing, adds to PATH via getBinDir)
|
|
732
|
+
// so slow downloads do not make startup appear frozen.
|
|
733
|
+
// Both are needed: fd for autocomplete, rg for grep tool and bash commands.
|
|
734
|
+
const [fdPath] = await Promise.all([
|
|
735
|
+
ensureTool("fd", (status) => this.showManagedToolStatus(status)),
|
|
736
|
+
ensureTool("rg", (status) => this.showManagedToolStatus(status)),
|
|
737
|
+
]);
|
|
738
|
+
this.fdPath = fdPath;
|
|
739
|
+
// Enable the remaining input handlers only after managed-tool setup completes.
|
|
740
|
+
this.setupKeyHandlers();
|
|
741
|
+
this.setupEditorSubmitHandler();
|
|
742
|
+
this.ui.requestRender();
|
|
706
743
|
// Initialize extensions first so resources are shown before messages
|
|
707
744
|
await this.rebindCurrentSession();
|
|
708
745
|
// Render initial messages AFTER showing loaded resources
|
|
@@ -742,8 +779,7 @@ export class InteractiveMode {
|
|
|
742
779
|
if (!process.env.PI_OFFLINE) {
|
|
743
780
|
const controller = new AbortController();
|
|
744
781
|
const timeout = setTimeout(() => controller.abort(), 15_000);
|
|
745
|
-
void this.session.modelRuntime
|
|
746
|
-
.refresh({ signal: controller.signal })
|
|
782
|
+
void refreshModelCatalogs(this.session.modelRuntime, controller.signal)
|
|
747
783
|
.then(() => this.updateAvailableProviderCount())
|
|
748
784
|
.catch(() => { })
|
|
749
785
|
.finally(() => clearTimeout(timeout));
|
|
@@ -1500,7 +1536,7 @@ export class InteractiveMode {
|
|
|
1500
1536
|
const message = error instanceof Error ? error.message : String(error);
|
|
1501
1537
|
this.showError(`${prefix}: ${message}`);
|
|
1502
1538
|
stopThemeWatcher();
|
|
1503
|
-
this.stop();
|
|
1539
|
+
this.stop("transcript");
|
|
1504
1540
|
process.exit(1);
|
|
1505
1541
|
}
|
|
1506
1542
|
renderCurrentSessionState() {
|
|
@@ -2249,6 +2285,7 @@ export class InteractiveMode {
|
|
|
2249
2285
|
this.defaultEditor.onAction("app.session.tree", () => this.showTreeSelector());
|
|
2250
2286
|
this.defaultEditor.onAction("app.session.fork", () => this.showUserMessageSelector());
|
|
2251
2287
|
this.defaultEditor.onAction("app.session.resume", () => this.showSessionSelector());
|
|
2288
|
+
this.defaultEditor.onAction("app.session.loadEarlier", () => this.loadEarlierTranscript());
|
|
2252
2289
|
this.defaultEditor.onChange = (text) => {
|
|
2253
2290
|
const wasBashMode = this.isBashMode;
|
|
2254
2291
|
this.isBashMode = text.trimStart().startsWith("!");
|
|
@@ -2301,6 +2338,10 @@ export class InteractiveMode {
|
|
|
2301
2338
|
// Silently ignore clipboard errors (may not have permission, etc.)
|
|
2302
2339
|
}
|
|
2303
2340
|
}
|
|
2341
|
+
handleStartupSubmit(text) {
|
|
2342
|
+
this.editor.setText(text);
|
|
2343
|
+
this.showStatus("Startup is still in progress");
|
|
2344
|
+
}
|
|
2304
2345
|
setupEditorSubmitHandler() {
|
|
2305
2346
|
this.defaultEditor.onSubmit = async (text) => {
|
|
2306
2347
|
text = text.trim();
|
|
@@ -2783,6 +2824,19 @@ export class InteractiveMode {
|
|
|
2783
2824
|
: message.content.filter((c) => c.type === "text");
|
|
2784
2825
|
return textBlocks.map((c) => c.text).join("");
|
|
2785
2826
|
}
|
|
2827
|
+
/** Show a managed-tool status update in the chat. */
|
|
2828
|
+
showManagedToolStatus(status) {
|
|
2829
|
+
if (!this.managedToolStatusStarted) {
|
|
2830
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
2831
|
+
this.managedToolStatusStarted = true;
|
|
2832
|
+
}
|
|
2833
|
+
const message = status.type === "warning" ? `Warning: ${status.message}` : status.message;
|
|
2834
|
+
const color = status.type === "warning" ? "warning" : "dim";
|
|
2835
|
+
this.chatContainer.addChild(new Text(theme.fg(color, message), 1, 0));
|
|
2836
|
+
this.lastStatusSpacer = undefined;
|
|
2837
|
+
this.lastStatusText = undefined;
|
|
2838
|
+
this.ui.requestRender();
|
|
2839
|
+
}
|
|
2786
2840
|
/**
|
|
2787
2841
|
* Show a status message in the chat.
|
|
2788
2842
|
*
|
|
@@ -3019,9 +3073,66 @@ export class InteractiveMode {
|
|
|
3019
3073
|
this.chatContainer.addChild(new Spacer(1));
|
|
3020
3074
|
this.chatContainer.addChild(new Text(text, 1, 0));
|
|
3021
3075
|
}
|
|
3076
|
+
getTranscriptEntriesForRender(reset = false) {
|
|
3077
|
+
const branch = this.sessionManager.getBranch();
|
|
3078
|
+
const contextEntries = this.sessionManager.buildContextEntries();
|
|
3079
|
+
let latestCompactionId;
|
|
3080
|
+
for (let index = branch.length - 1; index >= 0; index--) {
|
|
3081
|
+
const entry = branch[index];
|
|
3082
|
+
if (entry?.type === "compaction") {
|
|
3083
|
+
latestCompactionId = entry.id;
|
|
3084
|
+
break;
|
|
3085
|
+
}
|
|
3086
|
+
}
|
|
3087
|
+
if (reset || (this.transcriptWindowInitialized && this.transcriptLatestCompactionId !== latestCompactionId)) {
|
|
3088
|
+
this.loadedTranscriptEntryIds.clear();
|
|
3089
|
+
}
|
|
3090
|
+
this.transcriptWindowInitialized = true;
|
|
3091
|
+
this.transcriptLatestCompactionId = latestCompactionId;
|
|
3092
|
+
const contextEntryIds = new Set(contextEntries.map((entry) => entry.id));
|
|
3093
|
+
const earlierEntries = branch.filter((entry) => !contextEntryIds.has(entry.id));
|
|
3094
|
+
const loadedEntries = earlierEntries.filter((entry) => this.loadedTranscriptEntryIds.has(entry.id));
|
|
3095
|
+
this.transcriptLazyEntries = earlierEntries.filter((entry) => !this.loadedTranscriptEntryIds.has(entry.id));
|
|
3096
|
+
return [...loadedEntries, ...contextEntries];
|
|
3097
|
+
}
|
|
3098
|
+
renderTranscriptEntries(entries, options = {}) {
|
|
3099
|
+
if (this.transcriptLazyEntries.length > 0) {
|
|
3100
|
+
const count = this.transcriptLazyEntries.length.toLocaleString();
|
|
3101
|
+
this.chatContainer.addChild(new Text(theme.fg("dim", `${count} earlier transcript entries hidden (${keyText("app.session.loadEarlier")} to load)`), 1, 0));
|
|
3102
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3103
|
+
}
|
|
3104
|
+
this.renderSessionEntries(entries, options);
|
|
3105
|
+
}
|
|
3106
|
+
handleTranscriptScroll(scrollTop) {
|
|
3107
|
+
if (scrollTop === 0) {
|
|
3108
|
+
this.loadEarlierTranscript();
|
|
3109
|
+
}
|
|
3110
|
+
}
|
|
3111
|
+
loadEarlierTranscript() {
|
|
3112
|
+
if (this.loadingEarlierTranscript || this.session.isStreaming || this.transcriptLazyEntries.length === 0) {
|
|
3113
|
+
return;
|
|
3114
|
+
}
|
|
3115
|
+
this.loadingEarlierTranscript = true;
|
|
3116
|
+
try {
|
|
3117
|
+
const firstLoadedIndex = Math.max(0, this.transcriptLazyEntries.length - TRANSCRIPT_LAZY_LOAD_BATCH_SIZE);
|
|
3118
|
+
const entriesToLoad = this.transcriptLazyEntries.slice(firstLoadedIndex);
|
|
3119
|
+
for (const entry of entriesToLoad) {
|
|
3120
|
+
this.loadedTranscriptEntryIds.add(entry.id);
|
|
3121
|
+
}
|
|
3122
|
+
this.rebuildChatFromMessages();
|
|
3123
|
+
const remaining = this.transcriptLazyEntries.length;
|
|
3124
|
+
this.showStatus(remaining > 0
|
|
3125
|
+
? `Loaded ${entriesToLoad.length} earlier transcript entries (${remaining} remaining)`
|
|
3126
|
+
: "Loaded the beginning of the transcript");
|
|
3127
|
+
this.ui.requestRender();
|
|
3128
|
+
}
|
|
3129
|
+
finally {
|
|
3130
|
+
this.loadingEarlierTranscript = false;
|
|
3131
|
+
}
|
|
3132
|
+
}
|
|
3022
3133
|
renderInitialMessages() {
|
|
3023
|
-
const entries = this.
|
|
3024
|
-
this.
|
|
3134
|
+
const entries = this.getTranscriptEntriesForRender(true);
|
|
3135
|
+
this.renderTranscriptEntries(entries, {
|
|
3025
3136
|
updateFooter: true,
|
|
3026
3137
|
populateHistory: true,
|
|
3027
3138
|
});
|
|
@@ -3057,7 +3168,7 @@ export class InteractiveMode {
|
|
|
3057
3168
|
}
|
|
3058
3169
|
rebuildChatFromMessages() {
|
|
3059
3170
|
this.chatContainer.clear();
|
|
3060
|
-
this.
|
|
3171
|
+
this.renderTranscriptEntries(this.getTranscriptEntriesForRender());
|
|
3061
3172
|
}
|
|
3062
3173
|
// =========================================================================
|
|
3063
3174
|
// Key handlers
|
|
@@ -3154,7 +3265,7 @@ export class InteractiveMode {
|
|
|
3154
3265
|
this.ui.stop();
|
|
3155
3266
|
}
|
|
3156
3267
|
catch { }
|
|
3157
|
-
console.error(
|
|
3268
|
+
console.error(`${APP_NAME} exiting due to uncaughtException:`);
|
|
3158
3269
|
console.error(error);
|
|
3159
3270
|
process.exit(1);
|
|
3160
3271
|
}
|
|
@@ -3639,7 +3750,7 @@ export class InteractiveMode {
|
|
|
3639
3750
|
httpIdleTimeoutMs: this.settingsManager.getHttpIdleTimeoutMs(),
|
|
3640
3751
|
thinkingLevel: this.session.thinkingLevel,
|
|
3641
3752
|
availableThinkingLevels: this.session.getAvailableThinkingLevels(),
|
|
3642
|
-
currentTheme: this.
|
|
3753
|
+
currentTheme: this.themeController.getThemeSelection() || "dark",
|
|
3643
3754
|
terminalTheme: this.themeController.getTerminalTheme(),
|
|
3644
3755
|
availableThemes: getAvailableThemes(),
|
|
3645
3756
|
hideThinkingBlock: this.hideThinkingBlock,
|
|
@@ -3658,6 +3769,7 @@ export class InteractiveMode {
|
|
|
3658
3769
|
clearOnShrink: this.settingsManager.getClearOnShrink(),
|
|
3659
3770
|
showTerminalProgress: this.settingsManager.getShowTerminalProgress(),
|
|
3660
3771
|
tuiMode: this.ui.mode,
|
|
3772
|
+
fullscreenExitOutput: this.settingsManager.getFullscreenExitOutput(),
|
|
3661
3773
|
fullscreenScrollbar: this.settingsManager.getFullscreenScrollbar(),
|
|
3662
3774
|
warnings: this.settingsManager.getWarnings(),
|
|
3663
3775
|
}, {
|
|
@@ -3713,7 +3825,7 @@ export class InteractiveMode {
|
|
|
3713
3825
|
},
|
|
3714
3826
|
onThemeChange: (themeSetting) => {
|
|
3715
3827
|
this.settingsManager.setTheme(themeSetting);
|
|
3716
|
-
void this.themeController.
|
|
3828
|
+
void this.themeController.setThemeSetting(themeSetting);
|
|
3717
3829
|
},
|
|
3718
3830
|
onThemePreview: (themeName) => this.themeController.preview(themeName),
|
|
3719
3831
|
onHideThinkingBlockChange: (hidden) => {
|
|
@@ -3812,6 +3924,9 @@ export class InteractiveMode {
|
|
|
3812
3924
|
this.statusContainer.clear();
|
|
3813
3925
|
this.showStatus(`TUI mode: ${mode}`);
|
|
3814
3926
|
},
|
|
3927
|
+
onFullscreenExitOutputChange: (output) => {
|
|
3928
|
+
this.settingsManager.setFullscreenExitOutput(output);
|
|
3929
|
+
},
|
|
3815
3930
|
onFullscreenScrollbarChange: (mode) => {
|
|
3816
3931
|
this.settingsManager.setFullscreenScrollbar(mode);
|
|
3817
3932
|
this.applyFullscreenScrollbarSetting();
|
|
@@ -3864,7 +3979,7 @@ export class InteractiveMode {
|
|
|
3864
3979
|
controller.abort();
|
|
3865
3980
|
}, 15_000);
|
|
3866
3981
|
try {
|
|
3867
|
-
const result = await this.session.modelRuntime
|
|
3982
|
+
const result = await refreshModelCatalogs(this.session.modelRuntime, controller.signal);
|
|
3868
3983
|
if (result.aborted && timedOut) {
|
|
3869
3984
|
this.showWarning("Model refresh timed out; searching cached models.");
|
|
3870
3985
|
}
|
|
@@ -3952,7 +4067,7 @@ export class InteractiveMode {
|
|
|
3952
4067
|
onSelect: (selection) => {
|
|
3953
4068
|
trustStore.setMany(selection.updates);
|
|
3954
4069
|
done();
|
|
3955
|
-
this.showStatus(`Saved trust decision: ${selection.trusted ? "trusted" : "untrusted"}. Restart
|
|
4070
|
+
this.showStatus(`Saved trust decision: ${selection.trusted ? "trusted" : "untrusted"}. Restart ${APP_NAME} for this to take effect.`);
|
|
3956
4071
|
},
|
|
3957
4072
|
onCancel: () => {
|
|
3958
4073
|
done();
|
|
@@ -4052,8 +4167,7 @@ export class InteractiveMode {
|
|
|
4052
4167
|
this.ui.requestRender();
|
|
4053
4168
|
},
|
|
4054
4169
|
});
|
|
4055
|
-
void this.session.modelRuntime
|
|
4056
|
-
.refresh({ signal: controller.signal })
|
|
4170
|
+
void refreshModelCatalogs(this.session.modelRuntime, controller.signal)
|
|
4057
4171
|
.then((result) => {
|
|
4058
4172
|
if (disposed)
|
|
4059
4173
|
return;
|
|
@@ -4607,7 +4721,7 @@ export class InteractiveMode {
|
|
|
4607
4721
|
this.ui.requestRender();
|
|
4608
4722
|
};
|
|
4609
4723
|
const dialog = new LoginDialogComponent(this.ui, providerOption.id, () => restoreEditor(), providerOption.name, `${providerOption.name} setup`);
|
|
4610
|
-
dialog.showInfo(`${providerOption.method?.name ?? "Authentication"} is configured outside
|
|
4724
|
+
dialog.showInfo(`${providerOption.method?.name ?? "Authentication"} is configured outside ${APP_NAME}.`, [], true);
|
|
4611
4725
|
this.editorContainer.clear();
|
|
4612
4726
|
this.editorContainer.addChild(dialog);
|
|
4613
4727
|
this.ui.setFocus(dialog);
|
|
@@ -4863,7 +4977,9 @@ export class InteractiveMode {
|
|
|
4863
4977
|
this.showStatus(`Session exported to: ${filePath}`);
|
|
4864
4978
|
}
|
|
4865
4979
|
else {
|
|
4866
|
-
const filePath = await this.session.exportToHtml(outputPath
|
|
4980
|
+
const filePath = await this.session.exportToHtml(outputPath, {
|
|
4981
|
+
themeName: theme.name,
|
|
4982
|
+
});
|
|
4867
4983
|
this.showStatus(`Session exported to: ${filePath}`);
|
|
4868
4984
|
}
|
|
4869
4985
|
}
|
|
@@ -4954,7 +5070,7 @@ export class InteractiveMode {
|
|
|
4954
5070
|
// Export to a temp file
|
|
4955
5071
|
const tmpFile = path.join(os.tmpdir(), "session.html");
|
|
4956
5072
|
try {
|
|
4957
|
-
await this.session.exportToHtml(tmpFile);
|
|
5073
|
+
await this.session.exportToHtml(tmpFile, { themeName: theme.name });
|
|
4958
5074
|
}
|
|
4959
5075
|
catch (error) {
|
|
4960
5076
|
this.showError(`Failed to export session: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
@@ -5400,7 +5516,7 @@ export class InteractiveMode {
|
|
|
5400
5516
|
// Ignore, will be emitted as an event
|
|
5401
5517
|
}
|
|
5402
5518
|
}
|
|
5403
|
-
stop() {
|
|
5519
|
+
stop(fullscreenExitOutput = this.settingsManager.getFullscreenExitOutput()) {
|
|
5404
5520
|
this.disposeActiveSelector();
|
|
5405
5521
|
if (this.settingsManager.getShowTerminalProgress()) {
|
|
5406
5522
|
this.ui.terminal.setProgress(false);
|
|
@@ -5414,7 +5530,7 @@ export class InteractiveMode {
|
|
|
5414
5530
|
this.unsubscribe();
|
|
5415
5531
|
}
|
|
5416
5532
|
if (this.isInitialized) {
|
|
5417
|
-
this.stopInteractiveTui();
|
|
5533
|
+
this.stopInteractiveTui(fullscreenExitOutput);
|
|
5418
5534
|
this.isInitialized = false;
|
|
5419
5535
|
}
|
|
5420
5536
|
this.unregisterSignalHandlers();
|