Files
orca/src/relay/git-working-file-read.ts
T
JinjingandOrca a03a3dd51b Render png on mobile (#9087)
* Add mobile image-diff previews via shared data-URI builder

- Extracts a `buildImageDataUri` helper (src/shared/image-data-uri.ts) shared by
  the desktop ImageViewer and mobile, so both trim whitespace-wrapped base64 and
  skip non-previewable mimes (e.g. application/pdf) the same way.
- Adds mobile-diff-image-preview.ts to render binary git.diff results (add/modify/
  delete) as images instead of falling back to "Binary preview unavailable".
- Extracts resolveMobileFileTabDoc to consolidate the session file-tab loading
  logic (diff/image/html/text) out of the route file for testability.

* Fix stale binary image fallback for empty modified diffs and relay reads

- mobileDiffImageDataUri now distinguishes a true deletion (modified
  side absent) from a modify whose binary bytes arrived empty
  (relay/size-cap cases), returning null instead of the stale
  pre-change image
- readWorkingDiffFile passes the file path to bufferToBlob so relay
  working-tree reads can detect previewable image extensions instead
  of always reporting empty binary content
- add mobile-file-tab-doc.test.ts covering diff/image/binary/text
  resolution paths

* Regenerate skill bundle manifest for 1.4.144-rc.2

Co-authored-by: Orca <help@stably.ai>

* fix(review): trim comments to AGENTS.md's one/two-line why-only rule

Comments in mobile-diff-image-preview.ts and mobile-file-tab-doc.ts ran
3-6 lines and narrated mechanism instead of stating only the non-obvious
reason, per AGENTS.md's "Code Comments: Document the Why, Briefly" rule.

Co-authored-by: Orca <help@stably.ai>

* Distinguish read failures from true deletions in binary diff results

- Working-tree stat/readFile errors and relay reads previously collapsed
  onto the same empty-content signal as a genuine deletion, letting
  previewers fall back to stale original bytes on a failed read.
- Add modifiedDeleted/missing flags through status.ts, git-handler-ops,
  and git-working-file-read so only proven deletions trigger the
  original-bytes fallback; failed reads now return null.
- Tighten buildImageDataUri to accept only image/* mimes instead of
  special-casing application/pdf.

* fix(relay): expect missing:false on index blob maxBuffer overflow

readBlobAtIndex now returns a missing flag so staged deletions are
distinct from size-capped binary reads; update the overflow test.

* Allow opening deleted files to show pre-delete text or image diffs

Deleted files can now be opened to view their pre-delete content via
git.diff (including images via modifiedDeleted). Only unresolved conflicts
remain unopenable. Centralizes the canOpen rule in canOpenMobileGitStatusEntry()
to keep opener guards consistent across the mobile source control UI.

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-16 18:21:08 -07:00

36 lines
1.4 KiB
TypeScript

import { readFile, stat } from 'node:fs/promises'
import { bufferToBlob } from './git-handler-utils'
const MAX_RELAY_DIFF_WORKING_FILE_BYTES = 10 * 1024 * 1024
export async function readWorkingDiffFile(
absPath: string
): Promise<{ content: string; isBinary: boolean; missing: boolean }> {
let fileStat
try {
fileStat = await stat(absPath)
} catch (error) {
// Why: ENOENT means the working-tree file is genuinely gone (a deletion);
// any other stat error is a read failure we must not mistake for one, since
// callers fall back to the original bytes only for a proven deletion.
const missing = (error as NodeJS.ErrnoException)?.code === 'ENOENT'
return { content: '', isBinary: false, missing }
}
if (!fileStat.isFile()) {
return { content: '', isBinary: false, missing: true }
}
if (fileStat.size > MAX_RELAY_DIFF_WORKING_FILE_BYTES) {
// Why: mirror local git diff reads, which cap blob transfer at 10MB.
return { content: '', isBinary: true, missing: false }
}
try {
const buffer = await readFile(absPath)
// Why: bufferToBlob needs the path's extension to know an image is
// previewable; omitting it made every relay-side binary diff empty.
return { ...bufferToBlob(buffer, absPath), missing: false }
} catch {
// Why: the file exists but could not be read — a read failure, not a deletion.
return { content: '', isBinary: false, missing: false }
}
}