Files
RenewCMS/docs/design/screens/media.md
T

19 KiB
Raw Blame History

Media

  • Route: /media (verified users)
  • API: GET /v1/media/purge-preview · POST /v1/media/purge · GET /v1/media/ · POST /v1/media/ · PATCH /v1/media/{id} · DELETE /v1/media/{id} · POST /v1/media/merge · GET /v1/media/{id}/usage
  • Visual reference: source/RenewCMS Screens Back-office.dc.html, group "Media", drawn by source/MediaFrame.dc.html (open it in a browser; each state is drawn at desktop and mobile, with a light/dark toggle).

The library of every uploaded image. It replaces "images belong to one article": an image belongs to the library and can be used in any number of articles. Deleting an article never deletes images. The sidebar gains Media (images icon) between Articles and Users, breadcrumb "Media".

New route for verified users, in the sidebar between Articles and Users with the images icon. Images belong to the library, not to an article: one image can be used in many articles, and deleting an article never deletes images. Uploads from this page are not cropped or resized, and the file is stored as chosen. "Used in" is read from article bodies each time the list loads. Duplicates are images with identical content, whatever their filenames. Alt text is not stored on the image; it is set when inserting into an article. Search, filter, sort and view are stored in the query string. Keyboard: grid items are buttons in one tab stop each; Enter opens the detail panel, Space toggles selection, and Escape closes the panel or clears the selection. Selection changes are announced ("north-wall.jpg selected, 1 selected").

Layout

Header: title "Media", subtitle "Every image you've uploaded, ready to use in any article.", primary "Upload" button (upload icon) on the right. On mobile the button is a full-width 48 px button pinned to the bottom (it turns into "Try again in 0:42" while rate limited, and is replaced by the bulk bar while items are selected).

Toolbar: tabs All, Unused and Duplicates (count badge of duplicate groups, only while groups exist), then search ("Search by filename"), sort and the grid/list toggle. Sort options: Newest (default), Oldest, Name, Largest. On the Duplicates tab, owners and admins also get a secondary "Purge duplicates" button (merge icon) after the sort menu (full width, 44 px, in the stacked toolbar on mobile), hidden while there are no groups; it opens the PurgeDuplicatesDialog described in settings. Below md the toolbar stacks: search, tabs, then sort and toggle.

Grid (default): 5 columns on desktop, 2 on mobile, 16 px gap (10 px on mobile). Each card is one button: 4:3 thumbnail (checkerboard behind transparent PNG and SVG), filename (medium, ellipsis), size · dimensions (SVG dimensions read "Vector"), then badges. The accessible name reads "north-wall.jpg, 3.9 MB, used in 2 articles" or "…, unused". A "Select all" row with the image count sits above the grid.

Badge Look
Unused warning-soft fill, unlink icon
Duplicate info-soft fill, copy icon
Used in N outline, toned text

List view: table from md up, columns Filename (with 40 px thumbnail and Duplicate badge), Dimensions, Size, Type, Used in (underlined count, opens the detail panel), Uploaded by, Uploaded (sorted, arrow-down); 56 px rows, 36 px header, first column is the checkbox. Below md it is a card list with a 44 px checkbox column, a 56 px thumbnail, filename, size · dimensions · type, badges plus uploader · date, and a trailing chevron-right.

Selection: selected items take the sand fill (--rc-sand) with a primary outline. The checkbox shows on hover, on keyboard focus, on mobile, and whenever anything is selected. The bulk bar is sticky at the bottom (400 px wide on desktop, full width above the bottom edge on mobile): inverted fill, "3 selected", a danger "Delete" button (trash-2) and an inverted "Clear".

Detail panel: right-hand slideover 460 px wide on desktop (x to close), full screen on mobile with an arrow-left. Title is the filename. Body: 16:10 preview (4:3 on mobile), actions Copy URL (link), Rename (pencil), Delete (trash-2, error text), an info alert for duplicates, a metadata list (Type, Size, Dimensions, Uploaded, Uploaded by, URL in mono), then the "Used in" section.

Detail panel copy:

Where Copy
Duplicate alert "Identical to 2 other images" / "floorboards-copy.jpg and IMG_2208.jpg have the same content." / action "Review duplicates" (arrow-right)
Used "Used in 2 articles", then one row per article: title (underlined link to the editor), badge "Online" (globe, inverted) or "Draft" (pencil-line, outline), arrow-up-right. Footnote: "Counted from the text of your articles each time you open this page."
Not used "Not used in any article" / "This image is safe to delete. No article shows it."

Dialogs

All dialogs use the existing ConfirmDialog look (UModal, sm:max-w-md, bottom sheet on mobile, cancel first). While a request runs the confirm button shows a loader-circle, both buttons are disabled and the dialog cannot be dismissed. A failure shows an error alert at the top of the dialog body and the dialog stays open.

Dialog Title Description Body Cancel / Confirm (pending)
Rename "Rename image" "Only the filename changes. The URL stays the same, so articles that use this image keep working." Field "Filename" with the extension fixed as a mono suffix (.jpg); help "Letters, numbers, dashes and underscores. The .jpg ending stays."; error "Enter a filename." on empty or only symbols "Cancel" / "Rename" ("Renaming…")
Delete, unused "Delete this image?" ""window-frame.jpg" will be deleted from your library. No article uses it. This can't be undone." none "Cancel" / "Delete image" ("Deleting…"), danger
Delete, in use "Delete an image that's in use?" ""north-wall.jpg" is used in 2 articles. If you delete it, they will show a broken image. This can't be undone." list of affected articles with Online or Draft badges "Cancel" / "Delete image" ("Deleting…"), danger
Bulk delete "Delete 3 images?" "2 of them are used in 4 articles, which will show a broken image. The other one isn't used anywhere. This can't be undone." one row per image: filename and "Used in N" or "Unused" "Cancel" / "Delete 3 images" ("Deleting…"), danger
Merge "Merge into floorboards.jpg?" "Articles that use the other 2 copies will be switched to floorboards.jpg, and the other 2 copies will be deleted. 5 articles will be updated. This can't be undone." the kept image: thumbnail, filename, "Kept · used in 4 articles now" "Cancel" / "Merge 2 copies" ("Merging…"), danger

Dialog errors: rename "Couldn't rename the image. Your text is still here. Try again in a moment."; delete "Couldn't delete the image, so it's still there. Try again in a moment."; merge (500) "Couldn't merge the copies. Nothing was changed. Try again in a moment."; merge (409) "One of these images changed, so nothing was merged. The list is up to date now. Check it and try again."

Upload panel

A panel above the images while files are in flight and until dismissed. Header: "Uploading 6 images" with "2 of 6 done" and "Cancel all" (after it settles: "Upload finished" / "5 images" and "Dismiss"; if nothing was added: "Nothing was uploaded"). Files upload two at a time, one row each: 40 px thumbnail, filename, size, then per status:

Status Row
Waiting "Waiting"
Uploading "Uploading 1.1 of 2.6 MB" with a 4 px progress bar and a cancel x
Added circle-check "Added to your library"
Duplicate "Already in your library" / Same as "floorboards.jpg", uploaded 14 Jun 2026 by Inès Duarte. / "Use existing" and "Upload anyway"
Rejected file-x title, then "Images can be up to 10 MB. Use a PNG, JPEG, WEBP or SVG image." Titles: scan.tif is 14.2 MB (too large), notes.pdf isn't an image (type), broken.png couldn't be read with "The server couldn't read this image. Save it again as a PNG or JPEG, then try again." (400)
Failed circle-x "Upload failed" / "The server didn't accept the file. Nothing was added to your library." / "Try again" (rotate-cw)

Summary line when settled: "2 uploaded · 1 already in your library · 2 need attention". Type and size are checked in the browser before anything is sent (PNG, JPEG, WEBP, SVG; 10 MB), by MIME type and by extension, exactly as the editor's uploader does. Dropping files onto the page shows a dashed primary-bordered area "Drop images to upload" / "PNG, JPEG, WEBP, SVG · max 10 MB each".

Duplicates view

Groups stacked with 16 px gap. Each group is a bordered card: header "Group 1" and "3 identical images · 2.4 MB each · 3600 × 2400"; one column per copy (stacked on mobile) with thumbnail, filename, "Uploaded 14 Jun 2026 by Inès Duarte", underlined "Used in 4 articles", and a "Merge into this one" button (merge icon). The suggested copy has the sand fill, a primary outline, a "Suggested" tag and a primary button; the others are outline. Footer: "Suggested: the most used copy, then the oldest. Choosing another copy switches articles to it." Empty: copy icon, "No duplicates" / "Every image in your library is different." / "Back to all images".

Other copy

Where Copy
Empty library images icon, "No images yet" / "Upload your first image. You can use it in any article." / primary "Upload an image"; under it "PNG, JPEG, WEBP, SVG · max 10 MB each"
No results search icon, No images match "wall" / "Nothing matches in Unused. Check the spelling, or clear the filters." / "Clear filters"
List error error alert "Couldn't load your images" / "Check your connection and try again." / "Try again"
Rate limited info alert timer "Too many uploads" / "For your security, wait a moment before uploading again."; the Upload button reads "Try again in 0:42" and is disabled
Offline banner wifi-off "You're offline. You can look through the images that are loaded, but uploading, renaming and deleting need a connection."

Toasts (success 5 s unless noted):

Trigger Icon, title, description
Copy URL circle-check "URL copied" / "Paste it wherever you need the image address."
Rename circle-check "Image renamed" / It's now "north-wall-01.jpg". The URL is the same.
Delete circle-check "Image deleted" / "window-frame.jpg" is gone from your library.
Bulk delete circle-check "3 images deleted" / "They're gone from your library."
Bulk delete, partial error, stays until retried or dismissed: circle-x "Couldn't delete 1 image" / "2 images were deleted. window-frame.jpg is still in your library. Try again." / "Retry"
Merge circle-check "Copies merged" / "2 copies merged. 5 articles updated."
404 info info "That image no longer exists" / "It was deleted somewhere else, so we removed it from the library."

Status to copy

Status Where Copy
200 / 201 Upload, rename, merge Upload: row reads "Added to your library". Rename and merge: toasts above.
204 Delete, bulk delete Toasts above.
400 Upload row "broken.png couldn't be read" / "The server couldn't read this image. Save it again as a PNG or JPEG, then try again." In a dialog: "Something in the form isn't valid. Check the highlighted fields."
401 any Global: /login?redirect=/media&reason=expired. No local copy.
403 any, unverified Global: /register/pending.
404 rename, delete, merge, panel The info toast above; the panel or dialog closes.
409 merge Dialog alert above.
413 upload row "scan.tif is 14.2 MB" / "Images can be up to 10 MB. Use a PNG, JPEG, WEBP or SVG image."
415 upload row "notes.pdf isn't an image" / same description.
429 upload Rate-limited alert above, counting down Retry-After.
500 / network list, upload row, rename, delete, merge The copy for each place above.
Offline whole page Banner above.

Keyboard and accessibility

Grid items are buttons, one tab stop each. Enter opens the detail panel, Space toggles selection, arrow keys move focus across the grid, Escape closes the panel or clears the selection. Focus returns to the item when the panel closes, and moves to the next item after a delete. Selection changes are announced ("north-wall.jpg selected, 1 selected"), as are upload completion and the merge result. Search, filter, sort and view live in the query string.

States

State Trigger and behaviour
Grid, many images Default view. 12 images, newest first. Each item is a button labelled like "north-wall.jpg, 3.9 MB, used in 2 articles". Badges: "Unused" (warm), "Duplicate" (info), "Used in N" (outline). The Duplicates tab carries a count of groups only while duplicates exist.
Grid, few images Three images. Cards keep their size instead of stretching. Checkerboard shows behind the SVG.
List view The list icon in the toggle. A table from md up (Filename, Dimensions, Size, Type, Used in, Uploaded by, Uploaded) and a card list below md. The "Used in" count opens the article list in the detail panel.
Loading The first load, a retry, or a change of search or filter. Skeleton cards appear after 150 ms.
Empty library, first run Zero images. Toolbar and tabs are hidden; one call to action. Copy: "No images yet" / "Upload your first image. You can use it in any article." / "Upload an image".
No results Search or filter matched nothing. "Clear filters" resets the search and goes back to All.
Hover, focus and keyboard Hover (second item) darkens the border, adds the pop shadow and reveals the checkbox. Keyboard focus (third item) shows the 2 px primary ring with a 2 px offset and reveals the checkbox. Tab moves between items, Enter opens the detail panel, Space toggles selection, and arrow keys move focus across the grid.
Selection and bulk bar Checkbox on any item, or Space. Selected items take the sand fill with a primary outline, and Select all shows a partial state. The bar is sticky at the bottom (above the bottom edge on mobile, replacing the Upload button). Announced: "3 selected". Escape or Clear empties it.
Bulk delete: confirm Delete in the bulk bar. The dialog says how many of the selection are in use and lists them. The confirm button carries the count.
Bulk delete: in progress Requests in flight. The dialog can't be dismissed.
Bulk delete: partly failed Some requests failed. Deleted images leave the grid, failed ones stay selected, and the toast stays until retried or dismissed.
Bulk delete: done 204 for all. Focus moves to the next item.
Detail panel: in use Click or Enter on an item. Right-hand slideover on desktop, full screen on mobile with a back arrow. Actions: Copy URL, Rename, Delete. "Used in 2 articles" lists title, Online or Draft, and links to the editor. Focus returns to the item on close.
Detail panel: unused Used in nothing. The section reads "Not used in any article" and says the image is safe to delete.
Detail panel: duplicate The image has identical copies. An info alert links to the duplicates view.
Copy URL Copy URL. Clipboard write succeeded. Success toasts last 5 s.
Rename: dialog Rename in the panel. The field holds the name without its extension, and the .jpg ending is fixed. The description says the URL stays the same.
Rename: saving PATCH in flight. Field and buttons disabled, dialog can't be dismissed.
Rename: validation Empty or only symbols, on submit or blur. Nothing is sent.
Rename: failed 500 or network failure. The error shows inside the dialog.
Rename: done 200: the panel title and list update in place.
Delete: unused image Delete in the panel. Plain confirm. Says what happens and that it can't be undone.
Delete: image in use Stronger confirm. Lists the affected articles and states that they will show a broken image.
Delete: in progress DELETE in flight. The dialog can't be dismissed.
Delete: failed 500 or network failure. The error shows inside the dialog, as in the article delete.
Delete: done 204: the panel closes, the item leaves the grid and focus moves to the next item.
Image already gone (404) Any action on an image that was deleted elsewhere. The panel closes and the item leaves the list.
Upload: drag over the page Files dragged onto the page. A dashed drop area covers the content. Dropping starts the upload straight away; there is no crop step on this page.
Upload: several files, mixed Upload button or drop, several files. Each file has its own row and progress. Files upload two at a time. A row can end as added, "Already in your library", rejected in the browser, or failed. Rejected and failed rows stay until dismissed.
Upload: summary All rows settled. The summary counts each outcome. "Use existing" selects the existing image and drops the row; "Upload anyway" stores a second copy, which then appears under Duplicates. "Try again" retries one file. Announced when it finishes.
Upload: rejected files (413, 400) Type and size are checked in the browser first, so nothing is sent. A server 413 or 400 for a file that got through shows the same row with its own message.
Upload: too many requests (429) Rate limited. Retry-After is counted down in the button. It re-enables at zero. Already-added files are kept.
Duplicates: groups The Duplicates tab. Groups of identical images side by side, each with its uploader, upload date and "Used in N articles". The suggested copy to keep is the most used, then the oldest.
Duplicates: purge all Owner or admin, groups exist. "Purge duplicates" opens the purge dialog (preview, confirm, progress, results), the same one as Settings "Purge now". "Merge into this one" is unchanged.
Duplicates: empty No groups. The Duplicates tab loses its count badge.
Merge: confirm "Merge into this one" on floorboards.jpg. The dialog says articles using the other copies will switch to this image and the other copies will be deleted, with the article count.
Merge: in progress Request in flight. The dialog can't be dismissed.
Merge: failed 500 or network failure. Nothing is switched or deleted.
Merge: conflict (409) A copy was deleted, or a new copy appeared, since the page loaded.
Merge: done 200: the group leaves the list. When it was the last one, "No duplicates" shows.
List failed to load A 500 or network failure on the list request. The alert replaces the images; Try again repeats the request.
Offline navigator.onLine is false. Loaded images stay browsable. Upload, Rename, Delete and Merge are disabled, and the banner says why.