Skip to content

Commit

Permalink
Merge branch feat/enhanced-share into preview-fork
Browse files Browse the repository at this point in the history
    Aesthetic tweaks to output format

    Ensure adjacent codeblocks separated by newline

    Tidy regex to capture filename

    Reformat markdown code blocks

    Consolidate to single replace regex

    Change "Continue" to "Assistant" in export

    Create user-specified directory if necessary

    Ensure replacement only at start of string

    Add description of outputDir param for /share

    Use `.`, `./`, or `.\` for current workspace

    Add datetimestamp to exported session filename

    Add ability to specify workspace for /share

    Enable basic tilde expansion for /share outputDir

    Add outputDir param to /share

    Fix slash command params loading
  • Loading branch information
pzaback committed Apr 22, 2024
1 parent bb71479 commit 93fbc10
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 25 deletions.
3 changes: 2 additions & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"tabWidth": 2,
"useTabs": false
"useTabs": false,
"trailingComma": "all"
}
100 changes: 87 additions & 13 deletions core/commands/slash/share.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,98 @@
import path from "path";
import * as fs from "fs";
import { homedir } from "os";
import { SlashCommand } from "../..";
import { languageForFilepath } from "../../autocomplete/constructPrompt";
import { stripImages } from "../../llm/countTokens";

// If useful elsewhere, helper funcs should move to core/util/index.ts or similar
function getOffsetDatetime(date: Date): Date {
const offset = date.getTimezoneOffset();
const offsetHours = Math.floor(offset / 60);
const offsetMinutes = offset % 60;
date.setHours(date.getHours() - offsetHours);
date.setMinutes(date.getMinutes() - offsetMinutes);

return date;
}

function asBasicISOString(date: Date): string {
const isoString = date.toISOString();

return isoString.replace(/[-:]|(\.\d+Z)/g, "");
}

function reformatCodeBlocks(msgText: string): string {
const codeBlockFenceRegex = /```((.*?\.(\w+))\s*.*)\n/g;
msgText = msgText.replace(codeBlockFenceRegex,
(match, metadata, filename, extension) => {
const lang = languageForFilepath(filename);
return `\`\`\`${extension}\n${lang.comment} ${metadata}\n`;
},
);
// Appease the markdown linter
return msgText.replace(/```\n```/g, '```\n\n```');
}

const ShareSlashCommand: SlashCommand = {
name: "share",
description: "Download and share this session",
run: async function* ({ ide, history }) {
let content = `This is a session transcript from [Continue](https://continue.dev) on ${new Date().toLocaleString()}.`;

for (const msg of history) {
content += `\n\n## ${
msg.role === "user" ? "User" : "Continue"
}\n\n${stripImages(msg.content)}`;
description: "Export the current chat session to markdown",
run: async function* ({ ide, history, params }) {
const now = new Date();

let content = `### [Continue](https://continue.dev) session transcript\n Exported: ${now.toLocaleString()}`;

// As currently implemented, the /share command is by definition the last
// message in the chat history, this will omit it
for (const msg of history.slice(0, history.length - 1)) {
let msgText = msg.content;
msgText = stripImages(msg.content);

if (msg.role === "user" && msgText.search("```") > -1) {
msgText = reformatCodeBlocks(msgText);
}

// format messages as blockquotes
msgText = msgText.replace(/^/gm, "> ");

content += `\n\n#### ${
msg.role === "user" ? "_User_" : "_Assistant_"
}\n\n${msgText}`;
}

let outputDir: string = params?.outputDir;
if (!outputDir) {
outputDir = await ide.getContinueDir();
}

if (outputDir.startsWith("~")) {
outputDir = outputDir.replace(/^~/, homedir);
} else if (
outputDir.startsWith("./") ||
outputDir.startsWith(`.\\`) ||
outputDir === "."
) {
const workspaceDirs = await ide.getWorkspaceDirs();
// Although the most common situation is to have one directory open in a
// workspace it's also possible to have just a file open without an
// associated directory or to use multi-root workspaces in which multiple
// folders are included. We default to using the first item in the list, if
// it exists.
const workspaceDirectory = workspaceDirs?.[0] || "";
outputDir = outputDir.replace(/^./, workspaceDirectory);
}

if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}

const continueDir = await ide.getContinueDir();
const path = `${continueDir}/session.md`;
await ide.writeFile(path, content);
await ide.openFile(path);
const dtString = asBasicISOString(getOffsetDatetime(now));
const outPath = path.join(outputDir, `${dtString}_session.md`); //TODO: more flexible naming?

await ide.writeFile(outPath, content);
await ide.openFile(outPath);

yield `The session transcript has been saved to a markdown file at \`${path}\`.`;
yield `The session transcript has been saved to a markdown file at \`${outPath}\`.`;
},
};

Expand Down
6 changes: 4 additions & 2 deletions core/config/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ export const defaultConfig: SerializedContinueConfig = {
},
{
name: "share",
description: "Export this session as markdown",
description: "Export the current chat session to markdown",
params: { ouputDir: "Directory in which to export this session" },
},
{
name: "cmd",
Expand Down Expand Up @@ -110,7 +111,8 @@ export const defaultConfigJetBrains: SerializedContinueConfig = {
},
{
name: "share",
description: "Export this session as markdown",
description: "Export the current chat session to markdown",
params: { ouputDir: "Directory in which to export this session" },
},
],
customCommands: [
Expand Down
8 changes: 4 additions & 4 deletions core/config/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -334,10 +334,10 @@ function finalToBrowserConfig(
})),
systemMessage: final.systemMessage,
completionOptions: final.completionOptions,
slashCommands: final.slashCommands?.map((m) => ({
name: m.name,
description: m.description,
options: m.params,
slashCommands: final.slashCommands?.map((s) => ({
name: s.name,
description: s.description,
params: s.params, //PZTODO: is this why params aren't referenced properly by slash commands?
})),
contextProviders: final.contextProviders?.map((c) => c.description),
disableIndexing: final.disableIndexing,
Expand Down
3 changes: 2 additions & 1 deletion docs/docs/config-file-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ After the "Full example" these examples will only show the relevant portion of t
},
{
"name": "share",
"description": "Download and share this session",
"description": "Export the current chat session to markdown",
"params": { "ouputDir": "Directory in which to export this session" },
"step": "ShareSessionStep"
},
{
Expand Down
3 changes: 2 additions & 1 deletion docs/docs/customization/slash-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ Type "/share" to generate a shareable markdown transcript of your current chat h
```json
{
"name": "share",
"description": "Download and share this session"
"description": "Export the current chat session to markdown",
"params": { "ouputDir": "Directory in which to export this session" }
}
```

Expand Down
3 changes: 2 additions & 1 deletion docs/docs/walkthroughs/config-file-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@ After the "Full example" these examples will only show the relevant portion of t
},
{
"name": "share",
"description": "Download and share this session",
"description": "Export the current chat session to markdown",
"params": { "ouputDir": "Directory in which to save this session" },
"step": "ShareSessionStep"
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const val DEFAULT_CONFIG = """
},
{
"name": "share",
"description": "Download and share this session",
"description": "Export the current chat session to markdown",
"step": "ShareSessionStep"
},
{
Expand Down
23 changes: 23 additions & 0 deletions extensions/vscode/config_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1102,6 +1102,29 @@
}
}
}
},
{
"if": {
"properties": {
"name": {
"enum": [
"share"
]
}
}
},
"then": {
"properties": {
"params": {
"properties": {
"outputDir": {
"type": "string",
"markdownDescription": "If outputDir is set to `.` or begins with `./` or `.\\`, file will be saved to the current workspace or a subdirectory thereof, respectively. `~` can similarly be used to specify the user's home directory."
}
}
}
}
}
}
],
"required": ["name", "description"]
Expand Down
3 changes: 2 additions & 1 deletion gui/src/redux/slices/stateSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ const initialState: State = {
},
{
name: "share",
description: "Download and share this session",
description: "Export the current chat session to markdown",
params: { ouputDir: "Directory in which to save this session" },
},
{
name: "cmd",
Expand Down

0 comments on commit 93fbc10

Please sign in to comment.