Skip to content

Commit cc47b06

Browse files
author
Calvinn Ng
committed
feature: export chat session to Markdown
1 parent aec92f4 commit cc47b06

File tree

6 files changed

+55
-6
lines changed

6 files changed

+55
-6
lines changed

core/web/webviewProtocol.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import { Message } from "ollama";
12
import {
3+
ChatHistory,
24
ContextItemWithId,
35
ContextSubmenuItem,
46
ContinueRcJson,
@@ -100,6 +102,7 @@ export type WebviewProtocol = Protocol &
100102
},
101103
void,
102104
];
105+
saveSessionChatHistory: [ { chatHistory: ChatHistory, defaultTitle: string }, void];
103106
};
104107

105108
export type ReverseWebviewProtocol = {
@@ -133,4 +136,5 @@ export type ReverseWebviewProtocol = {
133136
setTheme: [{ theme: any }, void];
134137
setColors: [{ [key: string]: string }, void];
135138
"jetbrains/editorInsetRefresh": [undefined, void];
139+
sendSessionChatHistory: [undefined, void]
136140
};

extensions/vscode/package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,12 @@
170170
"icon": "$(fullscreen)",
171171
"group": "Ahrefs-Continue"
172172
},
173+
{
174+
"command": "ahrefs-continue.saveChatSession",
175+
"category": "Ahrefs-Continue",
176+
"title": "Save Chat Session",
177+
"group": "Ahrefs-Continue"
178+
},
173179
{
174180
"command": "ahrefs-continue.selectFilesAsContext",
175181
"category": "Ahrefs-Continue",

extensions/vscode/src/commands.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as os from "os";
33
import * as path from "path";
44
import * as vscode from "vscode";
55

6-
import { IDE } from "core";
6+
import { IDE, ContinueSDK } from "core";
77
import { AutocompleteOutcome } from "core/autocomplete/completionProvider";
88
import { ConfigHandler } from "core/config/handler";
99
import { logDevData } from "core/util/devdata";
@@ -369,9 +369,9 @@ const commandsMap: (
369369
input: text,
370370
});
371371
},
372-
// "ahrefs-continue.shareSession": () => {
373-
// sidebar.sendMainUserInput("/share");
374-
// },
372+
"ahrefs-continue.saveChatSession": () => {
373+
sidebar.webviewProtocol.request("sendSessionChatHistory", undefined);
374+
},
375375
"ahrefs-continue.selectRange": (startLine: number, endLine: number) => {
376376
if (!vscode.window.activeTextEditor) {
377377
return;

extensions/vscode/src/webviewProtocol.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@ import {
1919
WebviewProtocol,
2020
} from "core/web/webviewProtocol";
2121
import fs from "fs";
22-
import path from "path";
22+
import * as path from "path";
2323
import { v4 as uuidv4 } from "uuid";
2424
import * as vscode from "vscode";
2525
import { VerticalPerLineDiffManager } from "./diff/verticalPerLine/manager";
2626
import { getExtensionUri } from "./util/vscode";
27+
import { stripImages } from "../../../core/llm/countTokens";
2728

2829
async function showTutorial() {
2930
const tutorialPath = path.join(
@@ -248,6 +249,36 @@ export class VsCodeWebviewProtocol {
248249
this.on("saveFile", async (msg) => {
249250
return await ide.saveFile(msg.data.filepath);
250251
});
252+
this.on("saveSessionChatHistory", async (msg) => {
253+
const datetime = new Date();
254+
const year = datetime.getFullYear();
255+
const month = String(datetime.getMonth() + 1).padStart(2, '0');
256+
const day = String(datetime.getDate()).padStart(2, '0');
257+
const hours = String(datetime.getHours()).padStart(2, '0');
258+
const minutes = String(datetime.getMinutes()).padStart(2, '0');
259+
const seconds = String(datetime.getSeconds()).padStart(2, '0');
260+
261+
const datetime_filename = `${year}${month}${day}_${hours}${minutes}${seconds}`;
262+
263+
let content = `This is a session transcript from Ahrefs-Continue on ${datetime.toLocaleString()}.`;
264+
265+
for (const m of msg.data.chatHistory) {
266+
content += `\n\n## ${
267+
m.message.role === "user" ? "User" : `Ahrefs-Continue: ${msg.data.defaultTitle}`
268+
}\n\n${stripImages(m.message.content)}`;
269+
}
270+
271+
const continueDir = await ide.getContinueDir();
272+
const savedSessionsPath = `${continueDir}/saved_sessions`
273+
if (!fs.existsSync(savedSessionsPath)) {
274+
fs.mkdirSync(savedSessionsPath);
275+
}
276+
const path = `${savedSessionsPath}/${datetime_filename}_session.md`;
277+
await ide.writeFile(path, content);
278+
await ide.openFile(path);
279+
280+
vscode.window.showInformationMessage(`Chat session saved to ${path}`);
281+
})
251282
this.on("readFile", async (msg) => {
252283
return await ide.readFile(msg.data.filepath);
253284
});

gui/src/hooks/useNavigationListener.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const openGUITypes: (keyof ReverseWebviewProtocol)[] = [
99
"focusContinueInput",
1010
"focusContinueInputWithoutClear",
1111
"newSession",
12+
"sendSessionChatHistory"
1213
];
1314

1415
export const useNavigationListener = () => {

gui/src/pages/gui.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ import {
4242
} from "../redux/slices/uiStateSlice";
4343
import { RootState } from "../redux/store";
4444
import { getMetaKeyLabel, isMetaEquivalentKeyPressed } from "../util";
45-
import { isJetBrains } from "../util/ide";
45+
import { isJetBrains, ideRequest } from "../util/ide";
4646
import { getLocalStorage, setLocalStorage } from "../util/localStorage";
4747

4848
const TopGuiDiv = styled.div`
@@ -337,6 +337,13 @@ function GUI(props: GUIProps) {
337337
},
338338
[saveSession],
339339
);
340+
341+
useWebviewListener(
342+
"sendSessionChatHistory",
343+
async () => {
344+
ideRequest("saveSessionChatHistory", { "chatHistory" : state.history, "defaultTitle" : state.defaultModelTitle} );
345+
}
346+
);
340347

341348
const isLastUserInput = useCallback(
342349
(index: number): boolean => {

0 commit comments

Comments
 (0)