Files
multica/packages/views/common/format-bytes.ts
1d94166dc2 MUL-7642: full-window attachment viewer that pages through every file (#8763)
* feat(attachments): full-window viewer that pages through every file (MUL-7642)

The preview was a centered card capped at max-w-6xl, so a screenshot never
showed larger than 1152px wide whatever the screen, and prev / next only
walked images.

- The viewer takes the whole window: no card, a near-black stage, a top bar
  in the dark token set, prev / next in 64px side gutters, type / pixel size
  / file size instead of the MIME type. Markdown and text read on a centered
  sheet in the app theme; PDF and HTML fill the stage.
- The surface sequence now covers every previewable kind, so an issue pages
  through its screenshots, PDFs, reports and HTML in render order.
  `collectAttachmentSequence` takes the inclusion rule; mobile keeps the
  images-only `collectImageSequence`.
- The issue description block no longer lists every issue attachment as its
  own: comment uploads keep issue_id, so they were counted at the
  description's position.
- A focused video / field keeps its arrow keys.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(attachments): viewer image swap and desktop window chrome (MUL-7642)

Review of #8763:

- Paging from a PDF (or any non-image) onto an image that had not decoded
  yet handed the PDF's URL to <img>; its load error was blamed on the image
  being opened, which got marked broken and skipped. The settled-URL hook now
  only ever holds a URL that decoded as an image, and the <img> reports errors
  only when it shows the current file.
- On desktop the full-window top bar sat under the macOS traffic lights and
  over the app's top-bar drag region. The viewer now enters immersive mode
  while open, marks itself no-drag, lets its top bar drag the window, and
  opts the bar's controls back out.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(attachments): viewer waits for the media re-sign before loading (MUL-7642)

On clients that cannot load `/api/attachments/{id}/download` natively
(desktop, split-origin web, self-host proxy mode) the viewer handed that
endpoint to <img> while the re-sign / authenticated byte fetch was still in
flight. The native load failed, and in a sequence the failure read as a
broken image: it was marked unavailable and skipped before the loadable URL
arrived.

`useResignedInlineMedia` now also reports whether the upgrade is pending.
The viewer holds the previous frame (or shows loading) until it settles, and
the neighbour prefetch waits too. The inline figure keeps its behaviour.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(attachments): no focus ring around the viewer stage on open (MUL-7642)

The viewer focuses its zoom canvas on open so +/-/0 and the arrows work
without a click. That programmatic focus can match :focus-visible, and on the
full-window viewer the canvas ring drew a light frame around the whole stage.
The canvas now marks focus it placed itself; the stylesheet drops the ring
for it until focus leaves, so tabbing back in still shows one.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-09-24 03:28:42 +08:00

7 lines
279 B
TypeScript

/** Compact file size for UI metadata: `412 KB`, `3.2 MB`. */
export function formatBytes(bytes: number): string {
if (bytes < 1024) return `${bytes} B`;
if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}