fix(vscode): make session markdown export save/reveal work
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import type { Message, Part } from '@opencode-ai/sdk/v2';
|
import type { Message, Part } from '@opencode-ai/sdk/v2';
|
||||||
import { getRegisteredRuntimeAPIs } from '@/contexts/runtimeAPIRegistry';
|
import { getRegisteredRuntimeAPIs } from '@/contexts/runtimeAPIRegistry';
|
||||||
import { openDesktopPath, saveDesktopMarkdownFile } from '@/lib/desktop';
|
import { isVSCodeRuntime, openDesktopPath, saveDesktopMarkdownFile } from '@/lib/desktop';
|
||||||
import { getRevealLabel } from '@/lib/utils';
|
import { getRevealLabel } from '@/lib/utils';
|
||||||
|
|
||||||
type SessionMessageRecord = { info: Message; parts: Part[] };
|
type SessionMessageRecord = { info: Message; parts: Part[] };
|
||||||
@@ -95,7 +95,36 @@ export function downloadAsMarkdown(content: string, filename: string): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function saveAsMarkdownDesktop(content: string, filename: string): Promise<string | null> {
|
export async function saveAsMarkdownDesktop(content: string, filename: string): Promise<string | null> {
|
||||||
return saveDesktopMarkdownFile(filename, content);
|
const desktopPath = await saveDesktopMarkdownFile(filename, content);
|
||||||
|
if (desktopPath) {
|
||||||
|
return desktopPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isVSCodeRuntime()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/vscode/save-markdown', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ fileName: filename, content }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = await response.json() as { saved?: boolean; path?: string };
|
||||||
|
if (payload.saved !== true) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const savedPath = typeof payload.path === 'string' ? payload.path.trim() : '';
|
||||||
|
return savedPath.length > 0 ? savedPath : null;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function revealExportedMarkdown(path: string): Promise<boolean> {
|
export async function revealExportedMarkdown(path: string): Promise<boolean> {
|
||||||
|
|||||||
@@ -446,6 +446,51 @@ export async function handleFsBridgeMessage(
|
|||||||
return { id, type, success: true, data: { saved: true, path: saveUri.fsPath || saveUri.toString() } };
|
return { id, type, success: true, data: { saved: true, path: saveUri.fsPath || saveUri.toString() } };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'api:files/save-markdown': {
|
||||||
|
const rawFileName = (payload as { fileName?: unknown })?.fileName;
|
||||||
|
const rawContent = (payload as { content?: unknown })?.content;
|
||||||
|
const content = typeof rawContent === 'string' ? rawContent : '';
|
||||||
|
if (!content) {
|
||||||
|
return { id, type, success: false, error: 'Invalid markdown payload' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultFileName = typeof rawFileName === 'string' && rawFileName.trim().length > 0
|
||||||
|
? rawFileName.trim()
|
||||||
|
: `session-${Date.now()}.md`;
|
||||||
|
|
||||||
|
const saveUri = await vscode.window.showSaveDialog({
|
||||||
|
saveLabel: 'Export session',
|
||||||
|
defaultUri: vscode.workspace.workspaceFolders?.[0]
|
||||||
|
? vscode.Uri.joinPath(vscode.workspace.workspaceFolders[0].uri, defaultFileName)
|
||||||
|
: undefined,
|
||||||
|
filters: { Markdown: ['md'] },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!saveUri) {
|
||||||
|
return { id, type, success: true, data: { saved: false, canceled: true } };
|
||||||
|
}
|
||||||
|
|
||||||
|
await vscode.workspace.fs.writeFile(saveUri, Buffer.from(content, 'utf8'));
|
||||||
|
|
||||||
|
return { id, type, success: true, data: { saved: true, path: saveUri.fsPath || saveUri.toString() } };
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'api:fs:reveal': {
|
||||||
|
const targetPath = (payload as { path?: unknown })?.path;
|
||||||
|
const value = typeof targetPath === 'string' ? targetPath.trim() : '';
|
||||||
|
if (!value) {
|
||||||
|
return { id, type, success: false, error: 'Path is required' };
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const uri = value.includes('://') ? vscode.Uri.parse(value) : vscode.Uri.file(value);
|
||||||
|
await vscode.commands.executeCommand('revealFileInOS', uri);
|
||||||
|
return { id, type, success: true, data: { success: true } };
|
||||||
|
} catch {
|
||||||
|
return { id, type, success: false, error: 'Failed to reveal path' };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,6 +113,12 @@ export const createVSCodeFilesAPI = (): FilesAPI => ({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async revealPath(path: string): Promise<{ success: boolean }> {
|
||||||
|
const target = normalizePath(path);
|
||||||
|
const data = await sendBridgeMessage<{ success?: boolean }>('api:fs:reveal', { path: target });
|
||||||
|
return { success: Boolean(data?.success) };
|
||||||
|
},
|
||||||
|
|
||||||
async execCommands(commands: string[], cwd: string): Promise<{ success: boolean; results: CommandExecResult[] }> {
|
async execCommands(commands: string[], cwd: string): Promise<{ success: boolean; results: CommandExecResult[] }> {
|
||||||
const targetCwd = normalizePath(cwd);
|
const targetCwd = normalizePath(cwd);
|
||||||
const data = await sendBridgeMessageWithOptions<{ success: boolean; results?: CommandExecResult[] }>('api:fs:exec', {
|
const data = await sendBridgeMessageWithOptions<{ success: boolean; results?: CommandExecResult[] }>('api:fs:exec', {
|
||||||
|
|||||||
@@ -589,6 +589,18 @@ const handleLocalApiRequest = async (url: URL, init?: RequestInit) => {
|
|||||||
return new Response(JSON.stringify(data), { status: 200, headers: { 'Content-Type': 'application/json' } });
|
return new Response(JSON.stringify(data), { status: 200, headers: { 'Content-Type': 'application/json' } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pathname.startsWith('/api/vscode/save-markdown') && method === 'POST') {
|
||||||
|
const body = init?.body ? JSON.parse(init.body as string) : {};
|
||||||
|
const fileName = typeof (body as { fileName?: unknown }).fileName === 'string'
|
||||||
|
? (body as { fileName: string }).fileName
|
||||||
|
: undefined;
|
||||||
|
const content = typeof (body as { content?: unknown }).content === 'string'
|
||||||
|
? (body as { content: string }).content
|
||||||
|
: undefined;
|
||||||
|
const data = await sendBridgeMessage('api:files/save-markdown', { fileName, content });
|
||||||
|
return new Response(JSON.stringify(data), { status: 200, headers: { 'Content-Type': 'application/json' } });
|
||||||
|
}
|
||||||
|
|
||||||
if (pathname.startsWith('/api/config/agents/')) {
|
if (pathname.startsWith('/api/config/agents/')) {
|
||||||
const encodedName = pathname.slice('/api/config/agents/'.length);
|
const encodedName = pathname.slice('/api/config/agents/'.length);
|
||||||
const name = decodeURIComponent(encodedName);
|
const name = decodeURIComponent(encodedName);
|
||||||
|
|||||||
Reference in New Issue
Block a user