Four related fixes to stop buffering files in page memory and stop
hammering layout from the selection paths:
- Inline viewer media: video/audio fetched the ENTIRE file into a blob
before the first frame (a 2 GB video = 2 GB of tab heap, no
progressive playback, no seek). The <video>/<audio> src now points
straight at the same-origin API URL — cookies travel automatically
and the browser streams with native Range requests, exactly like the
music player already did.
- Downloads (file, folder ZIP, batch ZIP, viewer button): the
fetch → blob → objectURL pattern materialized the whole payload in
RAM before the save dialog appeared (a 10 GB batch ZIP risked
crashing the tab) with no download progress UI. New shared
utils/download.js hands the URL to the browser, which streams to
disk with its own progress UI. Batch download uses the existing GET
endpoint (same URL contract as the drag-out DownloadURL, now shared
via buildBatchDownloadUrl); selections whose id list cannot fit in a
URL keep the buffered POST fallback. Trade-off: failed downloads now
surface in the browser's download shelf instead of an in-app toast.
- Rubber-band lasso: every mousemove (>100/s) walked all cards
interleaving getBoundingClientRect() reads with class writes — up to
N forced reflows per event, freezing the frame rate on folders with
thousands of loaded rows. Card rects (+ item info) are now snapshot
once per drag (rebuilt on scroll), and a single rAF pass per frame
compares against the cached geometry, touching only cards whose
selection state changed.
- batchToolbar: getSelection() rebuilt the selection by scanning every
.file-item in the document (the TODO admitted it); it now reads the
_selected Map that every selection path already keeps in sync.
clear() scopes its DOM sweep to #files-list (the only container the
toolbar manages) instead of the whole document.
https://claude.ai/code/session_01Dp3oWon5GBMVn4j3QXZdgx
displaySearchResults inserted up to 100 results one at a time via
addItem({ scroll: true, highlight: true }) — each insert ran two DOM
scans (duplicate guard + row lookup), a smooth scrollIntoView and a
highlight pulse, so every live-search keystroke triggered ~100 competing
smooth-scrolls and O(n²) container scans.
Add filesView.renderItems(): a single component.render() pass (one
DocumentFragment, zero per-item scans/scrolls). addItem keeps the
scroll/highlight affordances for its documented purpose — optimistic
single-item inserts after upload/create.
https://claude.ai/code/session_01Dp3oWon5GBMVn4j3QXZdgx
`uploadFiles` sent files one at a time (await per file), so dropping N
files meant N sequential round-trips. Reuse the bounded-concurrency pool
already proven in `uploadFolderEntries` (CONCURRENCY = 10): independent
files now upload up to 10 at a time — ~10x faster for many small files.
Per-file progress (XHR → bell), the legacy progress bar, timeout
notifications and the quota short-circuit are preserved. On a quota error
the pool stops pulling new files while in-flight uploads finish (matching
`uploadFolderEntries`) instead of the old hard `break`.
https://claude.ai/code/session_01Dp3oWon5GBMVn4j3QXZdgx
- protect file_management_service::rename_file with validate_storage_name
- remove specific rename modal and use the generic modal class (less duplicate)
- handle errors on modal action: do not close the modal on error and display this error
- hide "Go to parent folder" contextMenu if section is files and folder is the same as current one
- change worker: do not cache html pages (not necessary)
- remove use of window.XXX and maximize import/export,
this will provide more clarety,
show circular dependencies + you will benefit IDE help
The folder upload path (uploadFolderEntries) used a flat 10s fetch
timeout for all files regardless of size. Large files like .CR2 RAW
images (~25MB) on slower networks (e.g. Raspberry Pi) exceeded this
timeout, causing the AbortController to kill the request before the
server received the full body — hence no server logs and 'unknown
error' on the frontend.
Changed to size-scaled timeout: 30s base + 2s per MB (min 10s).
A 25MB CR2 now gets ~80s instead of 10s.
* permits multiple drag & drop
* synchronize grid & list view on selection
* permits copy during ddrag & drop (use sift/alt key according your OS)
* use batch move / copy on drag & drop
Add download progress tracking to createBlobUrlViewer to fix incorrect
progress display for large files. Changes:
- Add xhr.onprogress handler to track download bytes
- Use 64-bit float division to avoid 32-bit integer overflow
- Show progress bar UI for files >10MB with percentage
- Add CSS styles with dark theme support
The progress calculation now correctly handles files larger than 2GB
by using JavaScript's native double-precision floats instead of
operations that might truncate to 32-bit integers.
Fixes#82
Add column-gap to list header/items, prevent rubber-band selection from
swallowing clicks on list headers and file items, preserve rubber-band
selection after mouseup, and extract type aliases to resolve clippy
type_complexity warnings.