* 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>
* feat(git-status): batch, cancel, and cache git status polling to cut idl
- Add a single duty-cycled refresh scheduler (activity debounce + 60s
safety timer) replacing multiple overlapping intervals, so status
polling no longer runs near-continuously on large repos (#7983).
- Let safety refreshes reuse cached numstat line counts instead of
re-running diff --numstat every cycle, invalidated by head change,
known mutations, and a bounded TTL.
- Thread AbortSignal/request-token cancellation through IPC, RPC, and
relay layers so a superseded or backgrounded git:status call is
killed instead of finishing wastefully.
- Fix automatic upstream/status apply ordering so a slow, older
refresh can no longer clobber a newer result, and so an earlier
refresh still applies when a later one fails.
* Fix aborted git status scans being mistaken for completed empty results
- An aborted scan/numstat pass now always rejects instead of silently
resolving, so a cancelled request can't look like a valid empty status.
- Stop clearing the line-stats cache key on abort, since an aborted pass
never wrote to it — clearing was evicting a concurrent scan's healthy
snapshot and forcing a redundant numstat recompute.
* Fix aborted git status scans resolving as completed results
Cancelled scans could still resolve with partial or stale data instead of
rejecting, letting callers treat an aborted refresh as a valid status. Also
stop counting aborted scan duration toward catch-up refresh pacing, which
was stretching the next refresh interval by the full length of a cancelled
(often slow) scan.
* Add cancellable, generation-aware git status polling to cut stale scans
- Route git.status through an abortable subscription per requestToken so
cancelStatus can actually abort the remote scan instead of being a
no-op, preventing wasted work and stale responses overwriting fresher
state.
- Bump the git status polling generation on push-target changes so an
in-flight refresh against the old remote/branch can't apply stale
upstream data to the new one.
- Guard the stale-conflict poller against writes after unmount.
- Retire pre-purge line-stat scans in the cache so an older in-flight
scan can't repopulate a key after a token-scoped purge.
Enable three unicorn rules — one correctness, two performance — and fix every
existing violation repo-wide so the rules pass as errors.
prefer-number-properties (76 sites)
- parseInt/parseFloat/NaN -> Number.* : safe aliases (autofixed).
- isNaN -> Number.isNaN (12 sites, hand-converted): global isNaN coerces its
argument, Number.isNaN does not. Verified every call site already passes a
number (Number.parseInt results, number-typed fields, Date.getTime()), so the
conversion is behavior-preserving today and guards against a future non-numeric
argument silently coercing.
prefer-array-find (26 sites)
- .filter(pred)[0] -> .find(pred); .filter(pred).at(-1) / .pop() -> .findLast(pred).
Drops the intermediate array and short-circuits.
prefer-array-index-of (5 sites)
- .findIndex(x => x === v) -> .indexOf(v).
Verified: typecheck (node/cli/web) clean, 53 affected suites pass (1679 tests),
oxlint clean repo-wide. mobile/ uses findLast safely (already ships ES2023
.toReversed()); config scripts and e2e helpers run on Node 24.
* chore(lint): upgrade oxlint to 1.71 and enable 7 new rules
Upgrade oxlint 1.67.0 -> 1.71.0 (1.72 was blocked by the repo's 3-day
minimum-release-age supply-chain guard; nothing here needs it). The
bump is a no-op on the existing config.
Enable 3 error rules (backlog autofixed to zero in this commit) and
4 warn rules (surface signal without gating CI):
error (autofixed, behavior-preserving):
- unicorn/prefer-node-protocol (~1531 sites: bare builtin -> node:)
- typescript/no-import-type-side-effects (~36: all-inline-type -> import type)
- unicorn/no-array-reverse (19: copy-then-reverse -> toReversed)
warn (real signal, current fires are test-only/correct):
- unicorn/no-array-fill-with-reference-type (aliasing footgun guard)
- typescript/no-unsafe-function-type (bans bare Function type)
- unicorn/prefer-array-flat-map (map().flat() -> flatMap())
- unicorn/prefer-regexp-test (.match() in bool ctx -> .test())
mobile/.oxlintrc.json extends root, so it inherits all 7; the autofix
ran from root and covered mobile/ too.
Verification (all green): oxlint 0 errors (root+mobile+aux configs),
oxfmt clean, typecheck (node+cli+web), vitest 22795 passed / 0 failed,
builds (electron-vite + web + cli) succeed. node: rewrites confirmed to
skip embedded SSH/CLI string payloads (AST-only); all toReversed sites
verified to operate on fresh copies or write-once locals.
* chore(lint): bump mobile oxlint to 1.71 so inherited rules parse
mobile/ is a standalone pnpm project pinning its own oxlint@1.67, which
lacks unicorn/no-array-fill-with-reference-type (needs >=1.70). Since
mobile/.oxlintrc.json extends the root config, mobile CI's 'cd mobile &&
oxlint' failed to parse the new rule. Bump mobile to match root (1.71).
Verified in mobile/: oxlint 0 errors, oxfmt --check clean, tsc --noEmit
pass, vitest 978 passed / 0 failed.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Orca <help@stably.ai>
* Limit large git diff payloads in main process before IPC transfer
Move the large diff rendering limit check to a shared module so that
the main process can evaluate diff sizes before transferring them. If
a diff exceeds the limits, its text content is dropped prior to IPC
serialization, and only the limit metadata is sent. This prevents the
application from freezing or crashing when loading massive diff files.
* Gracefully handle and prune oversized files in diff viewer
Prevent UI freezes and out-of-memory errors when viewing or editing
extremely large diffs or files. Working-tree files above 10MB and git
buffer overflows are treated as binary. Text diffs exceeding safe
rendering limits have their contents pruned before IPC transport, and
the UI is updated to show fallback states and disable invalid saves.
* Document save action check for large diffs and fix test import
Explain why saveContentAvailable is required for oversized diffs, as
stripped text bodies before IPC prevent complete saves. Also update the
large-diff-render-limit import in E2E tests to use the shared path.
- Treat new remote worktrees without a HEAD commit as an empty compare when
the base ref exists, avoiding a broken source-control compare state
- Keep the existing unborn-head error for cases where the base cannot resolve
* Squashed commits
- WIP: uncommitted changes before rebase
- ci
- Show inline PR check details in task drawer
- Add a Checks tab that opens from the PR checks cell and expands runs inline
- Fetch check output, annotations, and workflow job steps through IPC/RPC
- Improve markdown/comment wrapping so long PR content stays within the drawer
* Use app-styled confirmations for PR actions (#2324)
Co-authored-by: Orca <help@stably.ai>
* fix: pr-bug-scan validated finding from #2274 (#2296)
Co-authored-by: orca-bug-scan-bot <orca-bug-scan-bot@stably.ai>
* fix: avoid optional git locks during status checks
---------
Co-authored-by: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: buf0-bot[bot] <252831055+buf0-bot[bot]@users.noreply.github.com>
Co-authored-by: orca-bug-scan-bot <orca-bug-scan-bot@stably.ai>