fix(server): restore multi-file (bulk) upload in the Add Asset modal - #3049
Merged
Conversation
Multi-file upload (added in #2778 for the React UI) was lost in the #2818 React→Alpine/HTMX rewrite: the file picker accepted a single file only. The assets_upload endpoint already takes one file per POST, so this is a client-only fix. - Add `multiple` to the #add-file input. - Drive the upload from uploadFiles() in home.ts: iterate the selected files and POST them sequentially (one XHR per file) against the existing single-file endpoint, with "X of N" progress. htmx's single-form submit would only ever send the first file, so the file tab is no longer htmx-managed; toasts are replayed from the server's HX-Trigger header by hand. - Single-file uploads still flow through the same path unchanged. Adds an integration test (test_add_multiple_uploads_at_once) that selects two files in one go and asserts both persist. Fixes #3045 Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
There was a problem hiding this comment.
Pull request overview
Restores multi-file (bulk) upload support in the Add Asset modal after the React → Alpine/HTMX rewrite regressed the file picker to single-file selection, while keeping the existing single-file assets_upload endpoint (one file per POST) unchanged.
Changes:
- Re-enabled multi-select in the Add Asset upload input and switched the upload flow from HTMX form submit to a JS-driven sequential uploader (one XHR per file) with “File X of N” progress.
- Replays server toasts from the
HX-Triggerresponse header for the non-HTMX XHR path and triggers an explicitrefresh-assetsafter successful uploads. - Added a Playwright integration test to guard multi-file selection and persistence.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
tests/test_app.py |
Adds an integration test ensuring multi-file selection uploads and persists all files. |
src/anthias_server/app/templates/_asset_modal.html |
Adds multiple to the upload input and routes change handling to the new JS uploader (removing HTMX management for the file tab). |
src/anthias_server/app/static/src/home.ts |
Implements sequential multi-file upload via XHR, progress/index tracking, and manual toast replay from HX-Trigger. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Hiding the modal during an in-flight 'sending' upload cleared uploadState, which disarmed uploadFiles()'s re-entry guard and let a reopened modal start a second batch racing the first over the shared progress/index fields. Only reset upload state when nothing is in flight. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
assets_upload refused invalid/empty uploads with messages.error +
HTTP 200, which the HTMX/XHR path drops silently (the partial carries
no toast header) — so the operator saw nothing and the batch uploader
counted the rejection as a success. Pass the rejection through
_asset_table_response(toast=('error', …)) so it rides the HX-Trigger
header on every transport.
Client side, uploadOne() now distinguishes 'ok' / 'rejected' (2xx +
error toast) / 'error' (transport): a rejected file surfaces its
server toast and the batch skips it and keeps going, while a true
transport failure aborts. Addresses Copilot review feedback.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
The doc comment still described the old boolean return; it now documents the ok / rejected / error tri-state. Comment-only. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
The comments said htmx "would only ever send the first file", but htmx includes every selected file in the multipart body — the real single-file constraint is assets_upload reading request.FILES.get. Reword both comments. Comment-only. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
5 tasks
vpetersson
added a commit
that referenced
this pull request
Jun 11, 2026
* fix(server): polish home bulk-action & upload UI/UX Follow-up UI/UX pass over the recently shipped bulk asset management (#3048), multi-file upload (#3049), and ffmpeg/HandBrake rejection hints (#3040). - Reserve bottom space (.has-bulk-selection) while a selection is active so the fixed bulk-action bar never floats over the last rows or their action buttons — exactly the assets a bulk selection targets. - Cap .modal-card to the viewport and scroll inside it, with sticky header/footer, so a tall bulk-edit form (or the Edit modal with the failure alert + Advanced open) keeps its title and Apply/Save buttons reachable instead of pushing them below the fold. - Wire real drag-and-drop on the upload dropzone (dropFiles() feeds the same sequential uploadFiles() batch path); the dashed zone already read as a drop target but silently ignored drops. - Lift the selection checkbox contrast on the dark Enabled surface so the select-all (checked/indeterminate) and per-row boxes are legible against the purple gradient; scoped to .asset-select so the activity switch keeps its track styling. - Anchor the bulk bar's clear (x) to the top-right on phones so it no longer wraps beside the destructive Delete (mis-tap risk). - Stack the ffmpeg recipe's copy button under the command at narrow widths; make the empty-state CTA a <button> (action, not nav); drop the bulk duration field's placeholder that collided with its floating label. Presentational only — no API/model changes. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * fix(server): stop dropzone highlight flicker on child drag-over Copilot review on #3066: dragleave bubbles from the dropzone's child icon/paragraphs, toggling dragActive off while the cursor is still over the label and flickering the highlight. Add the .self modifier so the handler only runs when leaving the label itself. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * fix(server): clear dropzone drag highlight when the modal closes Second Copilot pass on #3066: dragActive could stay true if the user drags into the dropzone then closes the Add modal (Esc/backdrop/Cancel) before dragleave fires, so the dropzone re-opened still highlighted. Reset dragActive in closeModal() alongside the other modal state. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Issues Fixed
Fixes #3045
Description
Multi-file (bulk) upload was added for the React UI in #2778 but silently lost in the #2818 React→Alpine/HTMX rewrite — the Add Asset file picker accepted a single file only. The
assets_uploadendpoint already accepts one file per POST, so this is a client-only fix.multipleto the#add-fileinput.uploadFiles()inhome.tsiterates the selected files and POSTs them sequentially — one XHR per file — against the existing single-file endpoint, surfacing an "X of N" progress like the old React version did. Server toasts are replayed from theHX-Triggerheader by hand since the request isn't htmx-managed.Added an integration test (
test_add_multiple_uploads_at_once) that selects two files in one go and asserts both persist; it also implicitly guards themultipleattribute (Playwright rejects a multi-fileset_input_fileson a non-multiple input).Forum: https://forums.screenly.io/t/6579
Checklist
🤖 Generated with Claude Code