Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
d34e168
feat(web): preview archives, sqlite databases, and decoded images
maria-rcks Oct 19, 2026
26597b3
fix(web): keep binary previews lazy and align heif file icons
maria-rcks Oct 19, 2026
1b88a01
fix(web): keep lazy image decoding compatible with react compiler
maria-rcks Oct 19, 2026
98422ab
test(shared): cover binary file downloads across clients
maria-rcks Oct 19, 2026
4e08914
chore: merge t3/large-file-previews
maria-rcks Oct 19, 2026
54d0a12
chore: merge large file preview base
maria-rcks Oct 20, 2026
5de6c34
chore: merge t3/large-file-previews
maria-rcks Oct 20, 2026
b5891fe
chore: merge t3/large-file-previews
maria-rcks Oct 20, 2026
5b24af6
fix(web): bound binary parsers and preserve archive entries
maria-rcks Oct 20, 2026
fbcbe07
chore: merge large file preview fixes
maria-rcks Oct 20, 2026
53395e9
fix(web): preserve pax payloads and generated sqlite columns
maria-rcks Oct 20, 2026
2ebed51
chore: merge tall row preview fix
maria-rcks Oct 20, 2026
1ca6db3
chore: merge native row scrolling fix
maria-rcks Oct 20, 2026
c8957ed
fix(web): preserve pax attributes and sqlite table names
maria-rcks Oct 20, 2026
d38615d
chore: merge trailing row scrolling fix
maria-rcks Oct 20, 2026
337b5c1
test(web): allow cold preview workers to finish in CI
maria-rcks Oct 20, 2026
b1aeeb6
chore: merge current large file preview base
maria-rcks Oct 20, 2026
04bce05
fix(web): preserve invalid office document errors
maria-rcks Oct 20, 2026
1ce8c0a
chore: merge t3/large-file-previews
maria-rcks Oct 20, 2026
286339f
chore: merge large file preview fixes
maria-rcks Oct 20, 2026
54bf5e8
fix(web): validate zip64 targets and retry binary previews
maria-rcks Oct 20, 2026
c73439a
fix(web): carry workspace refresh fixes into binary previews
maria-rcks Oct 20, 2026
0be0d40
fix(web): carry parser notices into binary previews
maria-rcks Oct 20, 2026
a0478a5
chore: merge large previews into binary file previews
maria-rcks Oct 21, 2026
d937010
chore: merge large preview test fix
maria-rcks Oct 21, 2026
1e80fa9
chore: merge large previews
maria-rcks Oct 21, 2026
132ee24
fix(web): preserve sqlite table picker keyboard focus
maria-rcks Oct 21, 2026
844e590
chore: merge lazy large previews into binary file previews
maria-rcks Oct 21, 2026
1d674ea
fix(shared): remove unused preview metadata reexports
maria-rcks Oct 21, 2026
db71405
chore: merge inline audio pills
maria-rcks Oct 21, 2026
7d42822
fix(web): merge audio duration readiness into binary previews
maria-rcks Oct 21, 2026
64b685d
fix(web): merge large file viewer improvements into binary previews
maria-rcks Oct 25, 2026
de6bd02
fix(web): add image fit controls and binary preview metadata
maria-rcks Oct 25, 2026
2612448
fix(web): merge office engines into binary file previews
maria-rcks Oct 25, 2026
199e984
chore: merge large file preview fixes
maria-rcks Oct 25, 2026
5550a2f
chore: merge file preview review fixes
maria-rcks Oct 25, 2026
dd7b6b2
chore: merge wrapped file tail fixes
maria-rcks Oct 25, 2026
32de835
chore: merge t3/large-file-previews
maria-rcks Oct 25, 2026
33c7a0c
fix(web): align binary previews with current lint rules
maria-rcks Oct 25, 2026
13fd5ec
chore: merge t3/large-file-previews
maria-rcks Oct 25, 2026
51f5de7
chore: merge t3/large-file-previews
maria-rcks Oct 25, 2026
dec4674
chore: merge t3/large-file-previews
maria-rcks Oct 25, 2026
12e7e98
fix(web): reject redirected ZIP64 archive comments
maria-rcks Oct 25, 2026
37842c9
chore: merge t3/large-file-previews
maria-rcks Oct 25, 2026
d47f223
chore: merge t3/large-file-previews
maria-rcks Oct 25, 2026
5d8f1ac
chore: merge Office fixes through large file previews
maria-rcks Oct 25, 2026
d4b9a8a
refactor(web): reuse ZIP directory preflight for binary previews
maria-rcks Oct 25, 2026
c1deef5
chore: merge large file preview fixes
maria-rcks Oct 25, 2026
ce3d108
chore: reconcile binary viewer lockfile peers
maria-rcks Oct 25, 2026
4f51d3d
chore: merge latest large file base
maria-rcks Oct 25, 2026
0300612
chore: retain mobile audio compatibility
maria-rcks Oct 25, 2026
12a35e1
chore: merge large file preview base
maria-rcks Oct 25, 2026
fe6181a
Merge branch 't3/large-file-previews' into t3/binary-file-previews
maria-rcks Oct 25, 2026
bd20ac6
chore: merge large file preview updates
maria-rcks Oct 25, 2026
1164429
fix(web): copy cached tar entry bytes
maria-rcks Oct 25, 2026
f914a03
chore: merge large file preview retry
maria-rcks Oct 25, 2026
2c9fca5
chore: merge large file previews into binary file previews
maria-rcks Oct 26, 2026
c031c7d
chore(web): binary viewer effects state their reasons
maria-rcks Oct 26, 2026
1b0022d
chore: merge current large file base into binary file previews
maria-rcks Oct 26, 2026
4313a53
Merge branch 'fix/large-r6' into fix/binary-r6
maria-rcks Oct 26, 2026
39cb737
Merge branch 'fix/large-r6' into fix/binary-r6
maria-rcks Oct 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/desktop/src/ipc/methods/window.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ describe("pickProjectFavicon", () => {
"gif",
"ico",
"heic",
"heif",
"jpeg",
"jpg",
"png",
Expand Down
10 changes: 9 additions & 1 deletion apps/mobile/src/features/threads/fileChipMenu.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ describe("file chip downloads", () => {
["/tmp/take%2520%23one.mp4:12", "/tmp/take%20#one.mp4", "video/mp4"],
["/tmp/report.pdf", "/tmp/report.pdf", "application/pdf"],
["screens/image.PNG", "/repo/screens/image.PNG", "image/png"],
["/tmp/archive.zip", "/tmp/archive.zip", "application/zip"],
["/tmp/archive.jar", "/tmp/archive.jar", "application/java-archive"],
["/tmp/archive.tar", "/tmp/archive.tar", "application/x-tar"],
["/tmp/archive.gz", "/tmp/archive.gz", "application/gzip"],
["/tmp/archive.tgz", "/tmp/archive.tgz", "application/gzip"],
["/tmp/data.sqlite", "/tmp/data.sqlite", "application/vnd.sqlite3"],
["/tmp/data.sqlite3", "/tmp/data.sqlite3", "application/vnd.sqlite3"],
["/tmp/data.db", "/tmp/data.db", "application/vnd.sqlite3"],
])("offers a host download for %s", (href, path, mimeType) => {
const target = resolveFileChipTarget(href, "/repo")!;
expect(fileChipMenu(target).actions).toContainEqual({
Expand All @@ -94,7 +102,7 @@ describe("file chip downloads", () => {
});

it("does not offer downloads the host asset endpoint cannot serve", () => {
for (const href of ["src/app.ts", "/tmp/archive.zip", "/tmp/clip.mp4.txt"]) {
for (const href of ["src/app.ts", "/tmp/archive.7z", "/tmp/clip.mp4.txt"]) {
const target = resolveFileChipTarget(href, "/repo")!;
expect(fileChipShareSource(target, threadId)).toBeNull();
expect(fileChipMenu(target).actions.some(({ id }) => id === "save")).toBe(false);
Expand Down
24 changes: 20 additions & 4 deletions apps/server/src/assets/AssetAccess.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,14 @@ describe("AssetAccess", () => {
["report.pdf", "application/pdf"],
["tone.wav", "audio/wav"],
["tone.webm-audio", "audio/webm"],
["archive.zip", "application/zip"],
["archive.jar", "application/java-archive"],
["archive.tar", "application/x-tar"],
["archive.gz", "application/gzip"],
["archive.tgz", "application/gzip"],
["data.sqlite", "application/vnd.sqlite3"],
["data.sqlite3", "application/vnd.sqlite3"],
["data.db", "application/vnd.sqlite3"],
] as const) {
const filePath = path.join(outside, name);
yield* fs.writeFileString(filePath, "media");
Expand Down Expand Up @@ -1270,6 +1278,14 @@ describe("AssetAccess", () => {
["woff", "font/woff"],
["woff2", "font/woff2"],
["weba", "audio/webm"],
["zip", "application/zip"],
["jar", "application/java-archive"],
["tar", "application/x-tar"],
["gz", "application/gzip"],
["tgz", "application/gzip"],
["sqlite", "application/vnd.sqlite3"],
["sqlite3", "application/vnd.sqlite3"],
["db", "application/vnd.sqlite3"],
] as const) {
const attachmentId = `thread-1-00000000-0000-4000-8000-000000000001-${extension}`;
const attachmentPath = path.join(config.attachmentsDir, `${attachmentId}.${extension}`);
Expand All @@ -1280,7 +1296,7 @@ describe("AssetAccess", () => {
_tag: "attachment",
attachmentId,
fileName: `report.${extension}`,
mimeType: "application/octet-stream",
mimeType: "text/html",
disposition: "inline",
},
});
Expand Down Expand Up @@ -1338,16 +1354,16 @@ describe("AssetAccess", () => {
const config = yield* ServerConfig.ServerConfig;
const fileSystem = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const attachmentId = "thread-1-00000000-0000-4000-8000-000000000002-zip";
const attachmentPath = path.join(config.attachmentsDir, `${attachmentId}.zip`);
const attachmentId = "thread-1-00000000-0000-4000-8000-000000000002-7z";
const attachmentPath = path.join(config.attachmentsDir, `${attachmentId}.7z`);
yield* fileSystem.makeDirectory(config.attachmentsDir, { recursive: true });
yield* fileSystem.writeFile(attachmentPath, new Uint8Array([1, 2, 3]));

const result = yield* issueAssetUrl({
resource: {
_tag: "attachment",
attachmentId,
fileName: "archive.zip",
fileName: "archive.7z",
mimeType: "text/html",
disposition: "inline",
},
Expand Down
3 changes: 3 additions & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@
"remark-gfm": "catalog:",
"remark-math": "6.0.0",
"remark-parse": "catalog:",
"sql.js": "1.15.0",
"tailwind-merge": "^3.4.0",
"tiff": "7.1.3",
"unbash": "^5.0.0",
"unified": "catalog:",
"use-sync-external-store": "^1.6.0",
Expand All @@ -93,6 +95,7 @@
"@types/mdast": "^4.0.4",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"@types/sql.js": "1.4.11",
"@types/use-sync-external-store": "^0.0.6",
"@vercel/config": "^0.3.0",
"@vitejs/plugin-react": "^6.1.2",
Expand Down
14 changes: 5 additions & 9 deletions apps/web/src/components/files/AttachmentFilePreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import { useCopyToClipboard } from "~/hooks/useCopyToClipboard";
import { useClientSettings, updateClientSettings } from "~/hooks/useSettings";
import { cn } from "~/lib/utils";

import { BinaryFilePreview } from "./BinaryFilePreview";
import { PreviewImage } from "./PreviewImage";
import { AudioPreview } from "./AudioPreview";
import { BrowserDocumentFrame } from "./BrowserDocumentFrame";
import { DelimitedTablePreview } from "./DelimitedTablePreview";
Expand Down Expand Up @@ -263,6 +265,8 @@ export function AttachmentFilePreview(props: {
) : (
<ReadOnlySourcePreview name={props.name} text={content.text} />
)
) : kind === "archive" || kind === "sqlite" ? (
<BinaryFilePreview src={url} name={props.name} kind={kind} />
) : kind === "font" ? (
<FontFilePreview src={url} name={props.name} />
) : officeKind ? (
Expand All @@ -289,15 +293,7 @@ export function AttachmentFilePreview(props: {
/>
</div>
) : kind === "image" ? (
// oxlint-disable-next-line t3code/require-centered-scroll-gutter -- The image is capped at max-h-full max-w-full, so this never scrolls.
<div className="flex min-h-0 flex-1 items-center justify-center overflow-auto p-4">
<img
src={url}
alt={props.name}
className="max-h-full max-w-full object-contain"
onError={() => setError("Unable to load image.")}
/>
</div>
<PreviewImage key={url} src={url} name={props.name} mimeType={props.mimeType} />
Comment thread
macroscopeapp[bot] marked this conversation as resolved.
Outdated
) : (
<div className="flex min-h-0 flex-1 flex-col items-center justify-center gap-1 px-6 text-center">
<p className="text-sm font-medium">No preview for this file</p>
Expand Down
17 changes: 17 additions & 0 deletions apps/web/src/components/files/BinaryFilePreview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { lazy, Suspense } from "react";

import { FileSurfaceLoading } from "./fileSurfaceChrome";

const BinaryViewer = lazy(() => import("./BinaryViewer"));

export function BinaryFilePreview(props: {
readonly src: string;
readonly name: string;
readonly kind: "archive" | "sqlite";
}) {
return (
<Suspense fallback={<FileSurfaceLoading />}>
<BinaryViewer key={props.src} {...props} />
</Suspense>
);
}
Loading
Loading