fix: sync pasted/uploaded images across collaborating tabs (#36)

* fix: sync pasted/uploaded images across collaborating tabs

- Implement file delta synchronization to broadcast image file data
- Add periodic file sync check to catch async file data arrival
- Wrap Excalidraw addFiles API to automatically emit file changes
- Enhance socket element-update to include file payloads
- Add comprehensive E2E test for image collaboration scenarios
- Improve CORS flexibility for development localhost ports

Fixes #25: New images not appearing when collaborating - collaborators
now see uploaded images immediately instead of placeholder until refresh.

* perf: increase file sync polling interval from 500ms to 1000ms

Reduces CPU overhead while still catching async file arrivals. Most
updates go through the addFiles wrapper anyway.

---------

Co-authored-by: Zimeng Xiong <zxzimeng@gmail.com>
This commit is contained in:
Adrian-Ryan Acala
2026-01-20 10:50:11 -08:00
committed by Zimeng Xiong
parent 77c22916a8
commit 865285fbb7
4 changed files with 535 additions and 55 deletions
+31
View File
@@ -174,6 +174,37 @@ export async function getDrawing(
return (await response.json()) as DrawingRecord;
}
export async function updateDrawing(
request: APIRequestContext,
id: string,
data: Partial<DrawingRecord>
): Promise<DrawingRecord> {
const headers = await withCsrfHeaders(request, { "Content-Type": "application/json" });
let response = await request.put(`${API_URL}/drawings/${id}`, {
headers,
data,
});
if (!response.ok() && response.status() === 403) {
await refreshCsrfInfo(request);
const retryHeaders = await withCsrfHeaders(request, {
"Content-Type": "application/json",
});
response = await request.put(`${API_URL}/drawings/${id}`, {
headers: retryHeaders,
data,
});
}
if (!response.ok()) {
const text = await response.text();
throw new Error(`Failed to update drawing ${id}: ${response.status()} ${text}`);
}
return (await response.json()) as DrawingRecord;
}
export async function deleteDrawing(
request: APIRequestContext,
id: string