fix(frontend): guard paste, delete and create/rename against double submit #6

Merged
thisilike merged 3 commits from fix/double-submit into main 2026-09-23 15:42:56 +02:00
Owner

Copy, move and delete are wait actions, so a second click queued the same work again instead of dropping it.

  • Paste, Delete and the create/rename prompt now return early while their request is in flight. They show errors on the page instead of leaving the rejection unhandled.
  • The prompt's Cancel no longer submits the form. Button has no type, so both buttons were submit buttons. Create fired twice on every click, and Cancel with a name typed created or renamed the item.
  • ActionProgress fires onFinished once, and only on a terminal status. cancelling now counts as still running.

This branch also carries two commits that hadn't been pushed from feat/scan-overview:

  • 09bcf0a feat(preview): syntax-highlighted file preview. Needs core's isHighlightLang (core #68, already merged).
  • 2e0b926 fix(scan): overview of every root.

Checks: 76 backend tests pass. svelte-check has 0 errors against core main. deno fmt --check flags frontend/api.ts, which this branch didn't touch.

🤖 Generated with Claude Code

Copy, move and delete are `wait` actions, so a second click queued the same work again instead of dropping it. - Paste, Delete and the create/rename prompt now return early while their request is in flight. They show errors on the page instead of leaving the rejection unhandled. - The prompt's Cancel no longer submits the form. `Button` has no `type`, so both buttons were submit buttons. Create fired twice on every click, and Cancel with a name typed created or renamed the item. - `ActionProgress` fires `onFinished` once, and only on a terminal status. `cancelling` now counts as still running. This branch also carries two commits that hadn't been pushed from `feat/scan-overview`: - `09bcf0a` feat(preview): syntax-highlighted file preview. Needs core's `isHighlightLang` (core #68, already merged). - `2e0b926` fix(scan): overview of every root. Checks: 76 backend tests pass. svelte-check has 0 errors against core main. `deno fmt --check` flags `frontend/api.ts`, which this branch didn't touch. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The kit grew a grammar table, so this module's part is one thing: name the
language. `langOf` answers in @opsdeck/ui's spelling now (`ts` is
"typescript", not "ts"), and the map covers what a host actually holds —
toml, css, go, rust, every systemd unit suffix rather than only .service,
and the shell dotfiles, whose "extension" is `bashrc`.

`md` and `sql` keep their real names even though nothing colours them yet.
The caller asks the kit whether it has a grammar and renders plain when it
does not, so naming them "text" here would throw away the answer for the day
core learns them — and no edit here is needed on that day.

The schema says codeList instead of logText: the portable surface passes the
language too, so the two surfaces disagree about a file's colours only as far
as their renderers differ. On web both reach the same grammar; the Android
app has its own line-based colouriser, which the README now names as the
limit it is.

The preview scrolls sideways rather than wrapping, because a wrapped line
makes indentation lie about structure, which is most of what a config file
says.
Three faults, one symptom: a scanned host whose listing said "—" for two
thirds of its directories.

**The index dropped the directories anybody opens.** It is filled post-order
— a directory's size is known only once its children are — so the cap drops
the LAST entries written, which are the shallow ones. A whole-host scan of a
machine running docker filled the cap inside /var/lib/docker/overlay2 and
recorded nothing after it: on the real deployment /etc, /home, /var and the
root itself had no size, while /boot and /opt, walked earlier, did.
Directories within ALWAYS_INDEXED_DEPTH of the scanned path are now kept
whatever the cap says. They are a handful of entries.

**The overlay asked one scan.** With host=/ and pool=/fast-pool, every
listing was sized from whichever result the cache yielded first — and a scan
does not cross filesystems, so the host scan has no entry for the pool at
all. `overlayFor()` now asks every scan that has something to say: the most
specific covering one wins, and a root that begins right here contributes its
own total, which is the only measurement of a mount a wider scan stopped at.
It answers with this directory's children rather than the whole index, and
names the scan that covers it, so the listing's note credits the right one.

**A full index reported itself as a partial walk.** The totals, the
categories and the top-N lists are exact when only the index stops short, and
calling that "partial" sent people to rescan a scan that was already right.
That is `indexTruncated` now, carried into the summary and said plainly by
both surfaces — including the storage trim, which is the second way an index
ends up shorter than its tree.

The scan page is also an overview. It lists every root with what was
measured, how long ago, and whether that answer is stale, partial, truncated
or still running; picking one switches the detail below. Switching now
forgets the previous root's run, which used to keep reporting under the new
root's numbers with a Cancel button that cancelled the run you were no longer
looking at.

The schema page could not switch at all: it read policy.roots[0], so on a
two-root deployment the second root could not be scanned, inspected or even
named from a phone. It is two pages now — an overview and a per-root detail
whose root arrives as a path parameter, resolved like every other
client-supplied path.

Stored results move to format 2. A version-1 result came from the walk that
dropped its shallow directories, so keeping it would mean rendering the holes
with no field to explain them; the first scan after an upgrade writes a whole
one.
Copy, move and delete are "wait" actions, so a second click queued the
same work again instead of being dropped.

- paste, delete and the create/rename prompt return early while in
  flight, and show their errors instead of rejecting unhandled
- the prompt's Cancel no longer submits the form: Button has no type, so
  both buttons were submit buttons and Create fired twice per click
- ActionProgress fires onFinished once, on a terminal status; cancelling
  is still running
thisilike deleted branch fix/double-submit 2026-09-23 15:42:56 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
OpsDeck/module_filebrowser!6
No description provided.