@@ -120,8 +120,14 @@ export class UserMessageSelectorComponent extends Container {
|
||||
|
||||
// Add header
|
||||
this.addChild(new Spacer(1));
|
||||
this.addChild(new Text(theme.bold("Branch from Message"), 1, 0));
|
||||
this.addChild(new Text(theme.fg("muted", "Select a message or duplicate the current session"), 1, 0));
|
||||
this.addChild(new Text(theme.bold("Fork from Message"), 1, 0));
|
||||
this.addChild(
|
||||
new Text(
|
||||
theme.fg("muted", "Select a user message to copy the active path up to that point into a new session"),
|
||||
1,
|
||||
0,
|
||||
),
|
||||
);
|
||||
this.addChild(new Spacer(1));
|
||||
this.addChild(new DynamicBorder());
|
||||
this.addChild(new Spacer(1));
|
||||
|
||||
@@ -2425,6 +2425,11 @@ export class InteractiveMode {
|
||||
this.editor.setText("");
|
||||
return;
|
||||
}
|
||||
if (text === "/clone") {
|
||||
this.editor.setText("");
|
||||
await this.handleCloneCommand();
|
||||
return;
|
||||
}
|
||||
if (text === "/tree") {
|
||||
this.showTreeSelector();
|
||||
this.editor.setText("");
|
||||
@@ -3963,7 +3968,6 @@ export class InteractiveMode {
|
||||
}
|
||||
|
||||
private showUserMessageSelector(): void {
|
||||
const duplicateSessionOptionId = "__duplicate_session__";
|
||||
const userMessages = this.session.getUserMessagesForForking();
|
||||
|
||||
if (userMessages.length === 0) {
|
||||
@@ -3971,28 +3975,14 @@ export class InteractiveMode {
|
||||
return;
|
||||
}
|
||||
|
||||
const selectableEntries = [
|
||||
...userMessages.map((m) => ({ id: m.entryId, text: m.text })),
|
||||
{ id: duplicateSessionOptionId, text: "Duplicate session" },
|
||||
];
|
||||
const initialSelectedId = userMessages[userMessages.length - 1]?.entryId;
|
||||
|
||||
this.showSelector((done) => {
|
||||
const selector = new UserMessageSelectorComponent(
|
||||
selectableEntries,
|
||||
userMessages.map((m) => ({ id: m.entryId, text: m.text })),
|
||||
async (entryId) => {
|
||||
try {
|
||||
let result: { cancelled: boolean; selectedText?: string };
|
||||
if (entryId === duplicateSessionOptionId) {
|
||||
const leafId = this.sessionManager.getLeafId();
|
||||
if (!leafId) {
|
||||
throw new Error("Cannot duplicate session: no current entry selected");
|
||||
}
|
||||
result = await this.runtimeHost.fork(leafId, { position: "at" });
|
||||
} else {
|
||||
result = await this.runtimeHost.fork(entryId);
|
||||
}
|
||||
|
||||
const result = await this.runtimeHost.fork(entryId);
|
||||
if (result.cancelled) {
|
||||
done();
|
||||
this.ui.requestRender();
|
||||
@@ -4003,7 +3993,7 @@ export class InteractiveMode {
|
||||
this.renderCurrentSessionState();
|
||||
this.editor.setText(result.selectedText ?? "");
|
||||
done();
|
||||
this.showStatus("Branched to new session");
|
||||
this.showStatus("Forked to new session");
|
||||
} catch (error: unknown) {
|
||||
done();
|
||||
this.showError(error instanceof Error ? error.message : String(error));
|
||||
@@ -4019,6 +4009,29 @@ export class InteractiveMode {
|
||||
});
|
||||
}
|
||||
|
||||
private async handleCloneCommand(): Promise<void> {
|
||||
const leafId = this.sessionManager.getLeafId();
|
||||
if (!leafId) {
|
||||
this.showStatus("Nothing to clone yet");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await this.runtimeHost.fork(leafId, { position: "at" });
|
||||
if (result.cancelled) {
|
||||
this.ui.requestRender();
|
||||
return;
|
||||
}
|
||||
|
||||
await this.handleRuntimeSessionChange();
|
||||
this.renderCurrentSessionState();
|
||||
this.editor.setText("");
|
||||
this.showStatus("Cloned to new session");
|
||||
} catch (error: unknown) {
|
||||
this.showError(error instanceof Error ? error.message : String(error));
|
||||
}
|
||||
}
|
||||
|
||||
private showTreeSelector(initialSelectedId?: string): void {
|
||||
const tree = this.sessionManager.getTree();
|
||||
const realLeafId = this.sessionManager.getLeafId();
|
||||
|
||||
Reference in New Issue
Block a user