Improve workspace file viewer previews

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-06-09 21:35:48 +00:00
1 parent d503982075
commit fec3c19a0e
8 files changed
+185 -63

No files matched your search

@@ -215,6 +215,28 @@ describeEmbeddedPostgres("workspace file resources", () => {
expect(resolved.capabilities.preview).toBe(true);
});
it("resolves and reads video workspace files as base64 previews", async () => {
const { projectRoot, executionRoot } = await makeWorkspace();
const graph = await seedGraph(db, { projectRoot, executionRoot });
await fs.writeFile(path.join(projectRoot, "demo.mp4"), Buffer.from([0, 0, 0, 24, 102, 116, 121, 112]));
const resolved = await workspaceFileResourceService(db).resolve(graph.issueId, {
path: "demo.mp4",
workspace: "project",
});
expect(resolved.previewKind).toBe("video");
expect(resolved.contentType).toBe("video/mp4");
expect(resolved.capabilities.preview).toBe(true);
const content = await workspaceFileResourceService(db).readContent(graph.issueId, {
path: "demo.mp4",
workspace: "project",
});
expect(content.resource.previewKind).toBe("video");
expect(content.content.encoding).toBe("base64");
expect(content.content.data).toBe(Buffer.from([0, 0, 0, 24, 102, 116, 121, 112]).toString("base64"));
});
it("lists and searches safe file candidates from the preferred execution workspace", async () => {
const { root, projectRoot, executionRoot } = await makeWorkspace();
const graph = await seedGraph(db, { projectRoot, executionRoot });
@@ -87,6 +87,13 @@ const IMAGE_CONTENT_TYPES = new Map([
[".webp", "image/webp"],
]);
const VIDEO_CONTENT_TYPES = new Map([
[".m4v", "video/mp4"],
[".mov", "video/quicktime"],
[".mp4", "video/mp4"],
[".webm", "video/webm"],
]);
type IssueRow = typeof issues.$inferSelect;
type ExecutionWorkspaceRow = typeof executionWorkspaces.$inferSelect;
type ProjectWorkspaceRow = typeof projectWorkspaces.$inferSelect;
@@ -118,7 +125,9 @@ type WorkspaceFileListQueryInput = {
};
function previewCapForKind(kind: WorkspaceFilePreviewKind) {
return kind === "image" || kind === "pdf" ? WORKSPACE_FILE_MEDIA_MAX_BYTES : WORKSPACE_FILE_TEXT_MAX_BYTES;
return kind === "image" || kind === "video" || kind === "pdf"
? WORKSPACE_FILE_MEDIA_MAX_BYTES
: WORKSPACE_FILE_TEXT_MAX_BYTES;
}
function relativePathFromReal(rootReal: string, targetReal: string) {
@@ -184,6 +193,7 @@ function shouldPruneSegments(segments: string[]) {
function contentTypeForPath(filePath: string): string | null {
const ext = path.extname(filePath).toLowerCase();
if (IMAGE_CONTENT_TYPES.has(ext)) return IMAGE_CONTENT_TYPES.get(ext) ?? null;
if (VIDEO_CONTENT_TYPES.has(ext)) return VIDEO_CONTENT_TYPES.get(ext) ?? null;
if (ext === ".pdf") return "application/pdf";
if (ext === ".svg") return "image/svg+xml";
if (ext === ".html" || ext === ".htm") return "text/html";
@@ -194,6 +204,7 @@ function contentTypeForPath(filePath: string): string | null {
function previewKindForKnownContentType(contentType: string | null): WorkspaceFilePreviewKind | null {
if (!contentType) return null;
if (contentType.startsWith("image/") && contentType !== "image/svg+xml") return "image";
if (contentType.startsWith("video/")) return "video";
if (contentType === "application/pdf") return "pdf";
if (contentType === "text/html") return "unsupported";
if (contentType === "image/svg+xml" || contentType.startsWith("text/")) return "text";
+1
View File
@@ -1936,6 +1936,7 @@ async function isRuntimeServiceUrlHealthy(
) {
if (!url) return true;
const healthUrl = resolveRuntimeServiceHealthUrl(url, input);
if (!healthUrl) return true;
try {
const response = await fetch(healthUrl, { signal: AbortSignal.timeout(2_000) });
return response.ok;