mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-16 13:03:31 +08:00
Desktop file attachment selection was inlining every selected file as a data URL before sending the websocket message. Large multi-file sends could inflate the renderer request body by tens of megabytes before the server had a chance to materialize uploads. Route Tauri file selection through the native dialog so desktop sends absolute paths, while preserving browser fallback data URLs for H5. Cover both active-session and draft-session composers plus a payload-size regression case. Constraint: Browser/H5 cannot rely on local absolute file paths, so the existing FileReader fallback remains for non-Tauri runtimes. Rejected: Raise websocket/body limits | would keep renderer memory pressure and still send file bytes unnecessarily. Confidence: high Scope-risk: narrow Directive: Do not reintroduce FileReader data URLs for Tauri desktop file-picker attachments without measuring websocket payload size. Tested: cd desktop && bun run test src/lib/composerAttachments.test.ts src/components/chat/ChatInput.test.tsx src/pages/EmptySession.test.tsx Tested: /tmp complex-project reproduction showed 12x3MB old inline payload at 50,333,174 bytes versus path-only payload around 1-2KB Not-tested: Full desktop lint/build due unrelated existing Sidebar.tsx type errors in the dirty worktree Related: https://github.com/NanmiCoder/cc-haha/issues/444
35 lines
1.3 KiB
TypeScript
35 lines
1.3 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { pathToComposerAttachment } from './composerAttachments'
|
|
|
|
describe('composer attachment payloads', () => {
|
|
it('keeps many selected desktop project files as paths instead of request-body data', () => {
|
|
const projectRoot = '/tmp/cc-haha-issue-444-regression'
|
|
const files = Array.from({ length: 12 }, (_, index) => (
|
|
`${projectRoot}/assets/large-${index + 1}.bin`
|
|
))
|
|
|
|
const oldInlineAttachments = files.map((filePath) => ({
|
|
type: 'file',
|
|
name: filePath.split('/').pop(),
|
|
data: `data:application/octet-stream;base64,${'A'.repeat(256 * 1024)}`,
|
|
mimeType: 'application/octet-stream',
|
|
}))
|
|
const oldInlinePayload = JSON.stringify({
|
|
type: 'user_message',
|
|
content: 'analyze these files',
|
|
attachments: oldInlineAttachments,
|
|
})
|
|
|
|
const pathOnlyAttachments = files.map(pathToComposerAttachment)
|
|
const pathOnlyPayload = JSON.stringify({
|
|
type: 'user_message',
|
|
content: 'analyze these files',
|
|
attachments: pathOnlyAttachments,
|
|
})
|
|
|
|
expect(oldInlinePayload.length).toBeGreaterThan(3 * 1024 * 1024)
|
|
expect(pathOnlyPayload.length).toBeLessThan(3 * 1024)
|
|
expect(pathOnlyAttachments.every((attachment) => attachment.path && !attachment.data)).toBe(true)
|
|
})
|
|
})
|