Repository navigation
fix(app): retain raw media bytes up to 256 MiB for artifact previews - #55018
Conversation
There was a problem hiding this comment.
Looks good. Media files over 25 MB (up to 256 MiB) now play in the file viewer instead of showing the Binary file placeholder. I ran the updated review.spec.ts video case on the base, where it fails and the viewer shows Binary file · 38.4 MB, and with this PR, where it passes and the video renders. The unit tests and typecheck pass in packages/app and packages/gui-extensions.
The cache stays bounded: one large entry pushes the other cached files out of the 20 MiB LRU. bytesFromContent returns a copy, so viewers that move the buffer to a worker still behave as before.
Public API: FileContent in the private @opencode/gui-extensions SDK gains an optional bytes?: Uint8Array, and media now comes through bytes with empty content instead of base64 content + encoding: "base64". Only the in-repo helpers (contentBytes, bytesFromContent, blobUrlFromContent) read it, and they are all updated. No server, SDK, plugin or config surface changes.
Small cleanup: the FileContent type in packages/app/src/runtime/server/types.ts is no longer used and could be deleted.


Why
Opening a workspace video or other binary media file larger than 25 MiB (such as a 38.4 MB
arcade-update-draft.mp4) falls back to the binary placeholder (Binary file · 38.4 MB) instead of rendering in the media viewer. Even thoughserverSDK.api.file.readalready receives the file as aUint8Array,fileContentFromBytespreviously encoded binary media to a base64 string on the main thread andblobUrlFromContent/bytesFromContentdecoded that base64 string back into aUint8Array, which required a tight 25 MiB cap (MAX_MEDIA_BYTES = 25 * 1024 * 1024).What Changes
arcade-update-draft.mp4(38.4 MB)fileContentFromBytesdrops bytes (> 25 MiB) andArtifactViewrendersBinary file · 38.4 MBfileContentFromByteskeepsbytes: Uint8ArraywithmimeType: "video/mp4", andArtifactVideoplays the video directly from aBlobURLbytesToBase64->base64ToBytesround-trip (up to 25 MiB) or placeholder (> 25 MiB)Uint8Arraystored onFileContent.bytes;blobUrlFromContentbuilds theBlobdirectly andbytesFromContentreturns an independent slice for worker transferBinary file · <size>placeholderBinary file · <size>placeholderflowchart LR Read["serverSDK.api.file.read (Uint8Array)"] --> FromBytes["fileContentFromBytes"] FromBytes -->|"< 256 MiB media"| Direct["FileContent { type: 'binary', bytes, mimeType }"] FromBytes -->|"> 256 MiB media"| Placeholder["FileContent { type: 'binary', size }"] Direct --> BlobUrl["blobUrlFromContent -> new Blob([view], { type })"] Direct --> ViewerBytes["bytesFromContent -> bytes.slice() (for worker transfer)"]Demo
Comparison of opening
arcade-update-draft.mp4(38.4 MB) in the file viewer before (origin/dev,b4a57f6849) and after (large-media-preview):large-media-preview-demo.mp4
Scope
Covers binary media retention in
FileContent, cache byte accounting inapproxBytes, blob/byte extraction inpackages/gui-extensions/src/file/artifact.ts, and raisingMAX_MEDIA_BYTESto 256 MiB. Streaming range requests for multi-gigabyte media remain a separate follow-up.Verification
sdk-docs: 155 declarations, 645 members, and 190 parameters documented; 0 problems.lint:changed: 0 problems in 8 changed files.bun run check: 35/35 workspace packages passedtsgotypecheck.packages/appandpackages/gui-extensions): 22/22 passed.e2e/regression/review.spec.ts): 21/21 passed, including the 38.4 MBarcade-update-draft.mp4video playback and theatre overlay regression case (verified to fail againstorigin/dev).extension-host.spec.ts,extension-graph.spec.ts): 25/25 passed.