Prototype the media picker in the post editor #185

Closed
opened 2026-08-12 04:14:04 +00:00 by rosa · 2 comments
Owner

Map: #181
Blocked by: #183 (Does Media grow user-facing metadata?) — what each pickable item shows depends on what metadata exists.

Question

Prototype the media picker in the post editor. The Photo field today is a bare file input (crates/web/src/pages.rs:895); the picker must let the author select already-uploaded Media alongside inline upload, which stays (standing decision: complement, not replace).

The unclear part is the interaction: how do "upload new" and "pick existing" coexist in one form field without cluttering the common case? Modal gallery? Inline expandable grid? How does a picked Media preview, and how does Remove/Replace/Keep precedence (photo_action, crates/web/src/handlers/posts.rs:46) extend to a picked-not-uploaded Photo?

Build a throwaway UI prototype (via /prototype) to react to; the reaction settles the design. Note the picker needs the URL-resolving ownership check that is currently pub(crate) Micropub-only (resolve_photo) — the prototype can fake it, but the spec must expose it.

Map: #181 Blocked by: #183 (Does Media grow user-facing metadata?) — what each pickable item shows depends on what metadata exists. ## Question Prototype the media picker in the post editor. The Photo field today is a bare file input (`crates/web/src/pages.rs:895`); the picker must let the author select already-uploaded Media *alongside* inline upload, which stays (standing decision: complement, not replace). The unclear part is the interaction: how do "upload new" and "pick existing" coexist in one form field without cluttering the common case? Modal gallery? Inline expandable grid? How does a picked Media preview, and how does Remove/Replace/Keep precedence (`photo_action`, `crates/web/src/handlers/posts.rs:46`) extend to a picked-not-uploaded Photo? Build a throwaway UI prototype (via /prototype) to react to; the reaction settles the design. Note the picker needs the URL-resolving ownership check that is currently `pub(crate)` Micropub-only (`resolve_photo`) — the prototype can fake it, but the spec must expose it.
rosa self-assigned this 2026-08-12 05:19:56 +00:00
Author
Owner

Prototype is up on branch prototype/media-picker (commit 822f329). It renders in place of the real new-post form, so: check out the branch, mise run serve, log in, and open:

  • /new/post?variant=aInline expandable library: today's file input untouched, plus a <details> "Or pick from your library" that expands a tile grid in the form.
  • /new/post?variant=bCompact slot + modal gallery: a preview slot with two peer buttons ("Upload from device" / "Choose from library"); the library opens as a <dialog> with a staged selection committed by "Use this photo".
  • /new/post?variant=cTabbed field: "Upload new" / "From your library" as tabs; the grid scrolls in place instead of growing the form.

Append &current=set (or use the toggle in the floating pill) to fake an already-attached photo and exercise the full remove/replace/pick/keep interaction. / cycle variants; the pill at the bottom shows which one you're on.

Interaction model implemented, for reaction:

  • Precedence extends to remove > upload > pick > keep, with upload and pick mutually exclusive — the last action wins and clears the other. A live line under the field narrates what the form would do on save.
  • Processing items appear as disabled tiles with a badge; Failed items are excluded from the picker entirely (the library page still shows them, per the library design).
  • Tiles show the metadata settled earlier: filename label falling back to upload date, plus dimensions · date.
  • One constraint found while building: CSP is img-src 'self', which also blocks blob:, so a freshly uploaded (not yet saved) file can't be previewed client-side — variants show its filename instead. If we want live upload preview in the real build, the CSP needs blob: added to img-src.

The picked selection is display-only — submitting ignores photo_media_id; the real handler still needs resolve_photo exposed beyond Micropub, which goes in the spec.

Unrelated heads-up: mise run serve currently fails on this machine with table media_rendition already exists — dev-DB migration state, happens on main too; the prototype branch touches no migrations.

Prototype is up on branch [`prototype/media-picker`](https://code.cosmicrose.dev/rosa/vernier/src/branch/prototype/media-picker) (commit 822f329). It renders in place of the real new-post form, so: check out the branch, `mise run serve`, log in, and open: - `/new/post?variant=a` — **Inline expandable library**: today's file input untouched, plus a `<details>` "Or pick from your library" that expands a tile grid in the form. - `/new/post?variant=b` — **Compact slot + modal gallery**: a preview slot with two peer buttons ("Upload from device" / "Choose from library"); the library opens as a `<dialog>` with a staged selection committed by "Use this photo". - `/new/post?variant=c` — **Tabbed field**: "Upload new" / "From your library" as tabs; the grid scrolls in place instead of growing the form. Append `&current=set` (or use the toggle in the floating pill) to fake an already-attached photo and exercise the full remove/replace/pick/keep interaction. `←`/`→` cycle variants; the pill at the bottom shows which one you're on. Interaction model implemented, for reaction: - **Precedence** extends to `remove > upload > pick > keep`, with upload and pick mutually exclusive — the last action wins and clears the other. A live line under the field narrates what the form would do on save. - **Processing** items appear as disabled tiles with a badge; **Failed** items are excluded from the picker entirely (the library page still shows them, per the library design). - Tiles show the metadata settled earlier: filename label falling back to upload date, plus dimensions · date. - One constraint found while building: CSP is `img-src 'self'`, which also blocks `blob:`, so a freshly *uploaded* (not yet saved) file can't be previewed client-side — variants show its filename instead. If we want live upload preview in the real build, the CSP needs `blob:` added to `img-src`. The picked selection is display-only — submitting ignores `photo_media_id`; the real handler still needs `resolve_photo` exposed beyond Micropub, which goes in the spec. Unrelated heads-up: `mise run serve` currently fails on this machine with `table media_rendition already exists` — dev-DB migration state, happens on `main` too; the prototype branch touches no migrations.
Author
Owner

Resolved: variant A — inline expandable library. Reaction on the running prototype picked A over B (compact slot + modal gallery) and C (tabbed field).

The settled design:

  • The Photo field keeps today's file input as the primary affordance. Beneath it, a collapsed "Or pick from your library" <details> expands an inline tile grid — the common case (plain upload, or no photo) looks exactly like the current editor.
  • Tiles are newest-first squares showing the filename label (falling back to upload date), plus dimensions · upload date. Processing items render as disabled tiles with a badge; Failed items do not appear in the picker at all (the Library page still shows them).
  • Picking shows an inline preview — thumbnail, label, metadata — with a "Clear selection" control. The pick travels as a hidden photo_media_id form field.
  • Precedence extends to remove > upload > pick > keep. Upload and pick are mutually exclusive in the UI: the last action wins and clears the other. The existing remove_photo checkbox beats both, as today.
  • No client-side preview of a freshly chosen upload; the file input's native filename display suffices, so img-src 'self' stays untouched — this design needs no CSP change.

Carried forward into Assemble the implementation spec: expose the URL-resolving ownership check (resolve_photo, today pub(crate) Micropub-only) to the web editor path; the new-post and edit-post handlers accept photo_media_id; the picker grid shares the list-by-user domain op with the Library.

Primary source: all three variants plus the switcher are preserved on branch prototype/media-picker (commit 822f329). Nothing from that branch merges — the winner gets rebuilt properly during implementation.

**Resolved: variant A — inline expandable library.** Reaction on the running prototype picked A over B (compact slot + modal gallery) and C (tabbed field). The settled design: - The Photo field keeps today's file input as the primary affordance. Beneath it, a collapsed "Or pick from your library" `<details>` expands an inline tile grid — the common case (plain upload, or no photo) looks exactly like the current editor. - Tiles are newest-first squares showing the filename label (falling back to upload date), plus dimensions · upload date. Processing items render as disabled tiles with a badge; Failed items do not appear in the picker at all (the Library page still shows them). - Picking shows an inline preview — thumbnail, label, metadata — with a "Clear selection" control. The pick travels as a hidden `photo_media_id` form field. - Precedence extends to **remove > upload > pick > keep**. Upload and pick are mutually exclusive in the UI: the last action wins and clears the other. The existing `remove_photo` checkbox beats both, as today. - No client-side preview of a freshly chosen upload; the file input's native filename display suffices, so `img-src 'self'` stays untouched — this design needs no CSP change. Carried forward into [Assemble the implementation spec](https://code.cosmicrose.dev/rosa/vernier/issues/187): expose the URL-resolving ownership check (`resolve_photo`, today `pub(crate)` Micropub-only) to the web editor path; the new-post and edit-post handlers accept `photo_media_id`; the picker grid shares the list-by-user domain op with the Library. Primary source: all three variants plus the switcher are preserved on branch [`prototype/media-picker`](https://code.cosmicrose.dev/rosa/vernier/src/branch/prototype/media-picker) (commit 822f329). Nothing from that branch merges — the winner gets rebuilt properly during implementation.
rosa closed this issue 2026-08-12 05:35:48 +00:00
Sign in to join this conversation.
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
rosa/vernier#185
No description provided.