feat(files): upload files with drag and drop
This commit is contained in:
@@ -92,6 +92,39 @@ describe('createWebFilesAPI', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('uploads binary file contents to the active workspace', async () => {
|
||||
const { createWebFilesAPI } = await import('./files');
|
||||
const api = createWebFilesAPI({ urls, getDirectory: () => '/workspace' });
|
||||
const file = new Blob([new Uint8Array([0, 1, 255])]);
|
||||
runtimeFetchMock.mockResolvedValueOnce(Response.json({ success: true, path: '/workspace/image.bin' }));
|
||||
|
||||
await api.uploadFile?.('/workspace/image.bin', file, { directory: '/workspace' });
|
||||
|
||||
expect(runtimeFetchMock).toHaveBeenLastCalledWith('/api/fs/upload', {
|
||||
method: 'POST',
|
||||
query: { path: '/workspace/image.bin', overwrite: undefined },
|
||||
headers: {
|
||||
'Content-Type': 'application/octet-stream',
|
||||
'x-opencode-directory': '/workspace',
|
||||
},
|
||||
body: file,
|
||||
});
|
||||
});
|
||||
|
||||
it('preserves upload conflict details for explicit overwrite handling', async () => {
|
||||
const { createWebFilesAPI } = await import('./files');
|
||||
const api = createWebFilesAPI({ urls, getDirectory: () => '/workspace' });
|
||||
runtimeFetchMock.mockResolvedValueOnce(Response.json(
|
||||
{ error: 'File already exists', reason: 'already-exists' },
|
||||
{ status: 409 },
|
||||
));
|
||||
|
||||
await expect(api.uploadFile?.('/workspace/file.txt', new Blob(['new']))).rejects.toMatchObject({
|
||||
reason: 'already-exists',
|
||||
status: 409,
|
||||
});
|
||||
});
|
||||
|
||||
it('opens the native share sheet for downloads in the Capacitor app', async () => {
|
||||
const { createWebFilesAPI } = await import('./files');
|
||||
const api = createWebFilesAPI({ urls, getDirectory: () => '/workspace' });
|
||||
|
||||
@@ -7,6 +7,7 @@ import type {
|
||||
import {
|
||||
FilesystemError,
|
||||
parseFilesystemErrorReason,
|
||||
type FilesystemErrorReason,
|
||||
} from '@openchamber/ui/lib/api/files-errors';
|
||||
import { runtimeFetch } from '@openchamber/ui/lib/runtime-fetch';
|
||||
|
||||
@@ -31,6 +32,13 @@ type WebDirectoryListResponse = {
|
||||
entries?: WebDirectoryEntry[];
|
||||
};
|
||||
|
||||
type WebFileUploadResponse = {
|
||||
success?: boolean;
|
||||
path?: string;
|
||||
error?: string;
|
||||
reason?: FilesystemErrorReason;
|
||||
};
|
||||
|
||||
const toDirectoryListResult = (fallbackDirectory: string, payload: WebDirectoryListResponse): DirectoryListResult => {
|
||||
if (!payload || !Array.isArray(payload.entries)) {
|
||||
throw new FilesystemError('Directory listing returned an invalid response', {
|
||||
@@ -222,6 +230,36 @@ export const createWebFilesAPI = ({ getDirectory }: WebFilesAPIOptions): FilesAP
|
||||
};
|
||||
},
|
||||
|
||||
async uploadFile(path: string, file: Blob, options): Promise<{ success: boolean; path: string }> {
|
||||
const target = normalizePath(path);
|
||||
const response = await runtimeFetch('/api/fs/upload', {
|
||||
method: 'POST',
|
||||
query: {
|
||||
path: target,
|
||||
overwrite: options?.overwrite ? 'true' : undefined,
|
||||
},
|
||||
headers: {
|
||||
'Content-Type': 'application/octet-stream',
|
||||
...directoryHeaders(getDirectory, options?.directory),
|
||||
},
|
||||
body: file,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error: WebFileUploadResponse = await response.json().catch(() => ({ error: response.statusText }));
|
||||
throw new FilesystemError(error.error || 'Failed to upload file', {
|
||||
reason: parseFilesystemErrorReason(error.reason),
|
||||
status: response.status,
|
||||
});
|
||||
}
|
||||
|
||||
const result: WebFileUploadResponse = await response.json().catch(() => ({}));
|
||||
return {
|
||||
success: Boolean(result.success),
|
||||
path: result.path ? normalizePath(result.path) : target,
|
||||
};
|
||||
},
|
||||
|
||||
async delete(path: string): Promise<{ success: boolean }> {
|
||||
const target = normalizePath(path);
|
||||
const response = await runtimeFetch('/api/fs/delete', {
|
||||
|
||||
Reference in New Issue
Block a user