feat: add copy action to AI responses

Completed AI responses now expose a bottom copy action that copies their Markdown source without reasoning, tool logs, or citation metadata. Clipboard writes use a validated Preload/Main bridge so sandboxed Electron sessions no longer fail with a renderer clipboard permission error; full-conversation copy uses the same path.

Release note: AI 回复底部现在可以直接复制 Markdown 原文,并修复了沙箱环境下回复和完整会话可能无法写入剪贴板的问题。
This commit is contained in:
mesalogo
2026-09-02 16:35:51 +08:00
parent 4d9ab72778
commit 42cbfa43f1
15 changed files with 243 additions and 19 deletions
+13
View File
@@ -5,10 +5,23 @@ import {
browserNavigateRequestSchema,
browserStopLoadingRequestSchema,
builtinEmbeddingConnectionId,
clipboardTextSchema,
legacyEmbeddingConnectionId,
runtimeSettingsInputSchema
} from './contracts'
describe('clipboard text contract', () => {
it('accepts text and rejects non-text input', () => {
expect(clipboardTextSchema.parse('Markdown response')).toBe(
'Markdown response'
)
expect(clipboardTextSchema.parse('')).toBe('')
expect(clipboardTextSchema.safeParse({ text: 'no' }).success).toBe(
false
)
})
})
describe('browser control contracts', () => {
it('requires authoritative toolbar metadata and accepts committed URLs up to 8192 characters', () => {
const base = {
+5
View File
@@ -1116,6 +1116,8 @@ export type ContextFileSelectionProgress = {
export const maximumPastedImageBytes = 12 * 1024 * 1024
export const clipboardTextSchema = z.string()
export const pastedImageInputSchema = z
.object({
data: z
@@ -1561,6 +1563,9 @@ export type DesktopApi = {
onNewConversation: (listener: () => void) => () => void
onOpenSettings: (listener: () => void) => () => void
}
clipboard: {
writeText: (text: string) => Promise<void>
}
agent: {
getStatus: (
selection?: AgentRuntimeSelection
+1
View File
@@ -11,6 +11,7 @@ export const ipcChannels = {
appRendererPersistenceRequest: 'app:renderer-persistence-request',
appRendererPersistenceComplete: 'app:renderer-persistence-complete',
appClearLocalData: 'app:clear-local-data',
clipboardWriteText: 'clipboard:write-text',
conversationNew: 'conversation:new',
settingsOpen: 'settings:open',
agentStatus: 'agent:get-status',