Skip to content

Updates to media browser - #544

Merged
ddon merged 24 commits into
BeamLabEU:devfrom
alexdont:dev
May 15, 2026
Merged

Updates to media browser#544
ddon merged 24 commits into
BeamLabEU:devfrom
alexdont:dev

Conversation

@alexdont

Copy link
Copy Markdown
Contributor

No description provided.

Alexander Donand others added 24 commits May 14, 2026 18:38
Two bugs were collaborating. `Storage.folder_breadcrumbs/2` returns
the full ancestor chain INCLUDING the target folder (scope dropped
when scoped), but the heex iterated `@breadcrumbs` as links AND
appended `@current_folder.name` separately — duplicating the last
entry. And the leading "root" button hardcoded "All Media" even
when the browser was constrained to a scope, hiding the scope
folder's name entirely.
For a MediaBrowser scoped to `banana` viewing folder `wendalina`,
the old render was `All Media > wendalina > wendalina`. Now it
correctly shows `banana > wendalina`.
- Iterate `Enum.drop(@breadcrumbs, -1)` for the clickable middle
ancestors, render the current folder once at the trailing `<li>`.
- Leading button reads `@scope_folder_name` when `@scope_folder_id`
is set, falling back to `gettext("All Media")` otherwise. The
`:scope_folder_name` assign is already computed in `init_socket`,
it just wasn't being read.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Files in both grid and list views now carry the same `...` overflow
menu folders have — Download (when an "original" URL is present) and
Delete. Closes the long-standing gap where the only way to delete a
single file was to enter select mode, tick it, and hit the toolbar
trash.
- Grid view: kebab fades in at top-right on hover, sibling of the
click target so its buttons don't trigger `click_file`.
- List view: kebab in the trailing `w-10` column reserved in the
thead but previously empty. Wrapped in `phx-click="noop"` to swallow
the bubble before it hits the row's `click_file`, mirroring the
folder list-view kebab.
- Both menus hidden in `@select_mode` (the row is a selection target
in that state).
- Delete label flips with `@filter_trash`: "Move to Trash" outside
(no confirm — reversible), "Delete Permanently" inside trash (with
`data-confirm` showing the filename).
- `onclick="this.blur()"` on each menu button releases the
`:focus-within` daisyUI uses to keep the dropdown open, so the menu
collapses immediately after a click. Runs alongside `phx-click`
without conflict.
New `media_browser.ex` handlers:
- `delete_file` — single-file scope-guarded delete that mirrors
`delete_selected`'s trash-vs-permanent branching.
- `download_file` — pushes a one-entry `download_files` event
(same JS hook the bulk download uses).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Four related changes that converge folder and file rows in list view
toward visual parity, plus a new single-item Move flow available from
both kebabs.
- **Column alignment.** Folder list rows previously had a stray empty
`<td>` between the name (colspan=5) and the kebab, pushing the
kebab into a phantom 9th column past the thead. Removed it so the
folder kebab now lands in the actions column directly above the
file kebab.
- **Type + Date for folders.** Replaced the folder name's colspan=5
with discrete cells so the Type and Date columns now render —
"Folder" badge (matching the file type badge style) and
`folder.inserted_at` (same strftime format as file rows).
- **Path for folders.** New private `folder_parent_path/1` helper
mirrors how `enrich_files` builds `folder_path` for files
(`Storage.folder_breadcrumbs/1` joined with " / "). Folder rows
now show their parent path in the Path column, matching the file
pattern (`/` placeholder at root).
- **Move action in kebabs.** New `prepare_move_file` /
`prepare_move_folder` handlers seed `selected_files` /
`selected_folders` with a single-item set and open the existing
move-target modal, reusing `move_selected_to_folder` without
touching select_mode. `close_move_modal` now clears those sets
when not in bulk select mode so a cancelled kebab move doesn't
leave a stale single-item selection behind. Modal title
generalised from "Move N file(s)" to "Move N item(s)" so it
reads correctly for files, folders, and mixed selections. Added
to all four kebab spots (file grid, file list, folder grid,
folder list) with `hero-folder-arrow-down` icon, between
Download/Color and Delete. `onclick="this.blur()"` on each new
button closes the dropdown before the modal opens.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
In a scoped MediaBrowser, the move modal's "root" button sent
`phx-value-folder_uuid=""`, which both move handlers converted to
`target = nil`. For files, `move_file_to_folder/3` then rejected
`within_scope?(nil, scope_folder_id)` and returned `:out_of_scope`,
silently failing the move. For folders, the scoped `update_folder/3`
clause skips the parent-scope check when `new_parent` is nil, so the
folder *would* move — but to the system's true root, escaping the
scope entirely. Both reachable from the per-item kebab Move flow and
the bulk-select Move toolbar action; the same pattern also applies to
the drag-drop `move_file_to_folder` handler.
Both handlers now resolve "" → `scope` (the scope folder's uuid)
before calling Storage. Unscoped browsers are unchanged because
`scope` is nil there.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Defence-in-depth follow-up to 77d84e6. The scoped `update_folder/3`
clause used `new_parent && not within_scope?(...)` to gate the scope
check — short-circuiting when `new_parent` was nil. That let a caller
silently reparent a scoped folder to the system root by passing
`%{parent_uuid: nil}`, escaping its virtual scope.
The MediaBrowser caller no longer hits this path (77d84e6 resolves
empty → scope at the handler level), but `Storage.update_folder/3`
is public and could be called by any future consumer, so the storage
layer should enforce the invariant itself.
Now uses `Map.has_key?` to distinguish "attrs omits parent_uuid"
(rename/recolor — no move attempted) from "attrs has parent_uuid: nil"
(an explicit move to the system root). Any explicit `parent_uuid` in
attrs runs the scope check, and `within_scope?(nil, scope)` is false
when scope is set, so move-to-true-root attempts now correctly fail
with `:out_of_scope`.
Added a regression test covering both atom and string keys, since the
scope_test suite is the canonical surface for this invariant.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three small cleanups from /simplify review of the MediaBrowser
session changes:
- Replaced `folder_parent_path/1` (called per row in the folder list)
with a single `folder_list_path/2` precomputed once in heex. All
displayed folders share the same parent, so the previous helper did
N identical breadcrumb walks per render. With `folder_list_path/2`
it's one walk; ~3-10x query reduction on typical folder lists.
- Extracted `breadcrumb_path/1` private helper for the
`Storage.folder_breadcrumbs |> Enum.map_join(" / ", & &1.name)`
pattern. Used by both `enrich_files` (per-file Path) and the new
`folder_list_path` (per-render folder list Path). Single point of
change for the path-string format.
- Documented the `parent_uuid: nil` vs omit-key contract on
`Storage.update_folder/3`. Non-obvious Elixir idiom that the
scope tightening in 376737f depends on.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
**Feature.** Files can now be dragged onto the sidebar Trash button to
soft-delete them. Reuses the existing `MediaDragDrop` JS hook's
dataTransfer pipeline — new `[data-drop-trash]` block mirrors the
existing `[data-drop-folder]` wiring, with error-colored hover
feedback (`ring-error`, `bg-error/10`) to make the destructive action
read as different from a folder move. New `trash_file` server event
handler scope-guards via `Storage.within_scope?/2` and always calls
`Storage.trash_file/1` (drag-to-trash is "put in trash", not
"permanently delete" — permanent deletion stays explicit via the
kebab or Empty Trash button).
**Crash fix.** `load_trashed_files` and `load_orphaned_files` were
hand-rolling the per-file display map and omitted `:folder_path`
entirely. The list-view template at media_browser.html.heex:1310
reads `file.folder_path` unconditionally, so opening Trash or
Orphans in list mode crashed with `KeyError`. Pre-existing latent
bug; surfaced by drag-to-trash because before this feature the
trash view often had no rows in the test setup.
Both loaders now delegate to `enrich_files/1` for the per-file shape
(`urls`, `folder_path`, `variant_widths`, etc.). ~78 lines of
duplicated map construction removed. The unused `:trashed_at` field
the trash loader added is gone — verified nothing in the heex
reads it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When the user toggled into the trash view, the sidebar continued to
highlight whichever folder, the Root entry, or the All Files entry
they came from — visually it read as "in trash AND in some folder",
two active selections at once.
The internal `@current_folder` / `@file_view` state is intentionally
kept so toggling trash back off restores the previous view, but the
sidebar highlight is now gated on `not @filter_trash`. In trash view
only the Trash button carries the active highlight; flipping trash
off restores the previous folder/root/all-files highlight.
Threaded `filter_trash` through `folder_tree_node` (recursive
component) so `:is_active` accounts for trash mode, and added the
same `and not @filter_trash` guard to the All Files and Root
buttons in the sidebar.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Folders are now drag sources alongside files. Drag any folder from
grid, list, or sidebar onto any other folder (in grid, list, or
sidebar) — full four-way drop matrix. The drop targets pre-existed;
this adds the source-side wiring and the backend handler.
**Drag protocol.** Folder drags carry the uuid in `text/plain` (same
as files) plus a marker `application/x-pk-folder` type so drop
targets can branch on folder-vs-file at dragover time without
parsing the payload (dataTransfer values aren't readable on
dragover, only types are). The source uuid is stashed on
`self._draggedFolderUuid` for self-drop suppression.
**Self / cycle protection.** JS suppresses the drop indicator when
hovering the source folder onto itself. Drop-on-descendant still
slips through (tree topology isn't reachable from JS) but the server
rejects with `{:error, :cycle}` and surfaces a flash.
**Trash drop refuses folders.** Folders don't trash (no soft-delete
for folders), so the trash drop target rejects folder drags at both
dragover and drop — user sees a no-drop cursor instead of a
misleading red ring.
**Folder mutations now refresh `@folders` + `@folder_tree`.** Added
`reload_folder_lists/1` helper alongside `reload_current_page/1` and
wired it into the three handlers that mutate folder rows:
`move_folder_to_folder` (new), `move_selected_to_folder` (bulk
move), and `delete_selected` (bulk delete). Without it the UI showed
the moved/deleted folder in its old position until a manual page
refresh. The existing single-folder mutators (`delete_folder`,
`rename_folder`, `change_folder_color`) already refreshed inline.
**Drop indicator visual fixes.**
- Grid / list folder cards carry an inline
`style="background-color: ..."` from `folder_bg_style` which beat
any class-based bg. Stash + clear the inline so `bg-primary/10`
can take effect, restore on dragleave / drop.
- `<tr>` (list-view row) doesn't render `box-shadow` reliably, so
switched from Tailwind's `ring-*` to inline CSS `outline` —
works on any element type and respects border-radius in modern
browsers.
- Used daisyUI 5's `var(--color-primary)` directly. The legacy
`oklch(var(--p))` form (still present in some heex files) is from
daisyUI 4 where `--p` was raw oklch components; daisyUI 5 ships
the full oklch() in `--color-primary` so no wrapping needed.
- `outlineOffset: -2px` insets the outline so the table's
`overflow-x-auto` wrapper can't clip the left/right edges of
list-view rows.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dropping a folder or file onto the body of MediaBrowser (empty space
or anywhere outside a specific folder card) now moves the item into
the currently-viewed folder. Visual is a single blue outline around
the entire body region — matches the user's screenshot. Nested drop
targets behave exclusively: only the innermost target highlights at
any time.
**Heex** — new `<div class="rounded-lg" data-drop-folder={...}
data-drop-no-bg>` wrapping the empty-state / grid / list block. The
`data-drop-folder` expression resolves to `@current_folder.uuid` /
`"root"`, or `nil` in trash and all-files views (where dropping into
"current folder" doesn't make sense). The formatter re-indented the
wrapped content — large diff, small logical change.
**JS** —
- New `data-drop-no-bg` opt-out gate on the bg fill in
dragover/dragleave/drop. The 10% primary tint on folder cards is
great; on a whole content-area wrapper it'd be overwhelming.
- New `self._activeDropTarget` tracker for exclusivity between
nested targets (wrapper + folder card). When dragover fires on a
new target, the previous active is cleared first. dragleave / drop
release the tracker; file + folder dragend also clear it so a
cancelled drag (Esc, drop outside) doesn't leave a lingering
highlight.
- Extracted `clearHighlight(t)` helper inside `setupDragDrop` so the
same teardown happens consistently from dragleave, drop, and
dragend, and honours `data-drop-no-bg`.
No backend changes — `move_file_to_folder` / `move_folder_to_folder`
already accept any folder uuid as target (including the current
one). A drop onto the body where the item's already in that folder
is a no-op `update_folder` server-side, no flash anomaly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Picking up any item that's part of the current select-mode selection
now drags the *whole selection* — drop anywhere (folder card,
sidebar, main wrapper) and every selected file/folder moves
together. Picking up unselected items still single-drags as before.
Visual feedback: every selected item grays out (opacity-50) on
dragstart so it's obvious what's coming along, not just the one
under the cursor. Restored on dragend.
**Server-side change**: none. The drop fires the existing
`move_selected_to_folder` event, which already iterates
`@selected_files` + `@selected_folders`, scope-resolves the target,
skips drop-on-self for folders, and clears select state + refreshes
folder lists / current page. The bulk move modal has been firing
this for a while — drag-drop is just a new trigger.
**Heex** — `data-selected={if @select_mode && MapSet.member?(...,
uuid), do: "true"}` added to all four draggable element sites
(grid file, grid folder, list file, list folder).
**JS — `setupDragDrop`**
- New `setBatchVisuals(active)` helper toggles `opacity-50` on every
`[data-selected="true"]` element. Single query covers all four
rendering paths.
- File + folder dragstart: if the source element carries
`data-selected="true"`, set the `application/x-pk-batch` marker
type, flag `self._draggedBatch = true`, and call
`setBatchVisuals(true)`. Otherwise the existing single-item path
(only the dragged element grays out).
- File + folder dragend: if `_draggedBatch`, clear batch visuals and
reset the flag; otherwise the existing single-item path.
- Drop handler branches on the batch marker FIRST. Batch → push
`move_selected_to_folder` with just `{folder_uuid: resolvedTarget}`.
Otherwise the existing single-item branching by folder vs file.
- Trash drop refuses batch drags at both dragover and drop — the
batch may include folders (no soft-trash) and bulk-trash-via-drag
conflates move and destroy. Users wanting bulk trash use the
toolbar delete button.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Symptom: in views with few items, the kebab dropdown was clipped by
the parent card's `overflow-hidden` (daisyUI's `.card` wrapper hides
overflow for rounded corners). User reported only "Download" was
visible — the rest of the menu vanished below the card boundary.
Root cause: daisyUI's `dropdown-content` uses `position: absolute`,
so it's confined to the parent stacking context. Any ancestor with
`overflow: hidden` clips it.
Fix: refactor all four kebabs (folder grid, file grid, folder list,
file list) from daisyUI's inline `dropdown` to the existing
`<.table_row_menu>` component, which uses `phx-hook="RowMenu"` to
position the menu with `position: fixed; z-[9999]`. That escapes
every `overflow-hidden` ancestor — including the daisyUI parent card
the user hits in kit_test. The hook also auto-flips the menu above
the trigger when there's no viewport space below.
**Side fixes that come for free:**
- `onclick="this.blur()"` dropped from every menu item — the hook
closes via `_onMenuClick` after any item click.
- `phx-click="noop"` wrappers dropped from list-view kebabs — the
hook's trigger handler calls `stopPropagation` directly so clicks
don't bubble to the row's `click_file`.
- Esc + arrow-key navigation now work in the menu (RowMenu hook
features that the daisyUI dropdown didn't have).
**Side fix to TableRowMenu**: added optional `trigger_class` attr so
callers can customize the trigger button. The file grid kebab uses
it to preserve its dark overlay (`!bg-black/40 ...`) needed for
visibility against thumbnails.
**Folder color picker** stays as inline `<li role="none">` content
inside `<.table_row_menu>` — it's a custom UI (color circles row)
that doesn't fit `<.table_row_menu_button>`. The slot accepts any
HTML so this works cleanly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two features bundled — they touch overlapping handlers and the
trash work supersedes some of the untitled-folder paths.
## Instant folder creation
Replaces the inline-input "New Folder" UX with Finder/Explorer-style
instant creation. Clicking "+" creates a folder named `untitled`
(or `untitled 1`, `untitled 2`, ... if conflicts) immediately, no
prompt. User renames via the kebab → Rename action afterward.
- New `create_untitled_folder` event handler + `next_untitled_name/2`
helper. Helper walks existing siblings, picks the first available
name — gaps fill before extending (if `untitled` and `untitled 5`
exist, picks `untitled 1`).
- Guards `filter_trash` and `file_view == "all"` with explanatory
flash errors.
- Removes the old `toggle_new_folder` / `create_folder` event
handlers and all three inline-input UI blocks (sidebar root, grid,
list, plus the tree-node child input). `@show_new_folder` assign +
the same-named `folder_tree_node` attr are gone.
## Folder trash (recursive)
User asked for "move a folder or selected folders to the trash and
their contents." Folder soft-delete didn't exist before — folders
were always permanently destroyed with reparenting. Now they get the
same trash/restore/permanent-delete cycle files have had since V99.
### Migration V119
Adds `trashed_at TIMESTAMPTZ` to `phoenix_kit_media_folders` with a
partial index on `trashed_at IS NOT NULL` for fast trash-view
queries. All idempotent. Bumps `@current_version` 118 → 119.
### Schema + storage
- `Folder` schema gets the `trashed_at` field + cast permission.
- `Storage.trash_folder/2(folder, scope)` — recursive soft-trash via
`folder_subtree_uuids/1` (breadth-first walk). Marks the folder +
every descendant as trashed and stamps `status: "trashed",
trashed_at: now` on every file in the subtree. Scope-guarded.
- `Storage.restore_folder/2` — reverses the trash, recursively.
- `Storage.delete_folder_completely/2` — hard-deletes the subtree
bottom-up (leaves first so FKs stay happy), routing files through
`delete_file_completely/1` so storage-backend cleanup runs.
- `Storage.list_trashed_folders/2`, `count_trashed_folders/1` — for
the trash view + sidebar badge.
- `list_folders/2`, `list_folder_tree/1`, `list_all_folders/0` now
filter `is_nil(trashed_at)`, hiding trashed folders from every
non-trash view without explicit caller changes.
### Event handlers
- `delete_folder` event: branches on `filter_trash` — soft-trash
outside the trash view, recursive permanent delete inside. Mirrors
the file behavior.
- `delete_selected`: folders now soft-trash too (outside trash) /
recursive permanent delete (inside). The CLAUDE.md "no trash for
folders" comment is obsoleted.
- `restore_selected`: extended to handle folders alongside files.
- New `trash_folder` event handler for drag-to-trash on a single
folder. Always soft-deletes regardless of view.
- `toggle_trash_filter`: populates `@folders` with
`list_trashed_folders/2` when entering the trash view, so trashed
folders render as rows alongside trashed files.
- `reload_folder_lists/1` mirrors that branch.
- New `full_trash_count/1` helper (files + folders) used in the four
spots that assign `@trash_count`. Sidebar badge now reflects the
whole trash bucket.
### JS
`MediaDragDrop`'s trash drop target previously rejected folder and
batch drags. Now both are accepted:
- Single folder → push `trash_folder` (new event).
- Single file → push `trash_file` (existing).
- Batch → push `delete_selected` (existing; reads `@selected_files`
+ `@selected_folders` + branches on `@filter_trash` server-side).
### Side fix
`Storage.other_files_share_path?/1` blew up on files with `nil`
`file_path` (Ecto forbids `column == nil`). Surfaced by the new
`delete_folder_completely` test; fixed by short-circuiting on nil.
### Tests
`test/integration/storage/scope_test.exs` gains four describe blocks
covering `trash_folder`, `restore_folder`, and
`delete_folder_completely`. Recursive subtree behavior, scope
guards, and effects on `list_folders` / `list_folder_tree` /
`list_trashed_folders` are all asserted. 53/53 pass.
## Deferred
- Single-folder kebab "Restore" — restore is bulk-only via
select-mode in the trash view today. Folder kebab in trash view
still shows Rename / Color / Move; weird but harmless. Tightening
to "Restore + Delete Permanently only" is a separate UI pass.
- Trashed folders are clickable in trash view (could navigate into a
trashed folder via the inner button). Edge case; haven't seen
reports yet.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two UX changes to the sidebar tree's inline rename:
- Input swapped from the previous transparent
`bg-transparent border-none outline-none ...` styling (visually
indistinguishable from plain text) to a thin primary-bordered field
on a white bg. Sits flush with the row's natural height — keeps the
folder icon and tree indentation proportionate while reading clearly
as "editable". Tried daisyUI's `input input-bordered input-xs` first
but the chunkier border + larger height made the row layout look
squished; the minimal `border border-primary/60 rounded px-1.5
py-0` reads cleaner.
- `phx-blur="cancel_rename_folder"` on the input — clicking anywhere
outside the input now cancels the rename, alongside the existing
Esc keybind. The `cancel_rename_folder` handler clears state
idempotently, so a blur after Enter-submit (when the input is
removed from DOM) is a harmless no-op against already-cleared
state.
Also tried a row-wide `ring-2 ring-primary` to flag the rename mode
visually but the user found it too noisy — the input border alone
is enough signal.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Folder icons in the main content area were noticeably small compared
to file thumbnails — folders read as a second-class element. Bumping
them to match.
- Grid view folder card: w-12 → w-20 (48px → 80px). Roughly half the
typical ~150px aspect-square card; folder name still has room below.
- List view folder row (icon in a w-10 h-10 wrapper): w-6 → w-10
(24px → 40px). Fills the wrapper now, matching the file thumbnail
size so folders and files line up cleanly in the same column.
Each appears twice per view (normal display + inline-rename branch),
both bumped together.
Sidebar tree icons stay at w-4 h-4 — tree nav is intentionally tight
and bigger icons there would break the indentation grid.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The folder tree sidebar (toolbar + All Files / Root / Trash buttons +
inline rename) is now its own function component so other LiveViews
can embed folder navigation without duplicating ~170 lines of markup.
MediaBrowser becomes the first consumer; events stay wired through
@Myself so all 13 handle_event clauses remain unchanged.
Color helpers (folder_color_hex/1, folder_icon_style/2, folder_bg_style/1)
move with the component since the grid/list folder cards consume them
too — MediaBrowser imports them back from FolderExplorer.
Adds three reuse flags (show_create, show_all_files, show_trash, all
default true) so folder-picker consumers can drop MediaBrowser-specific
buttons without forking the markup.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bump child UL pl-1 → pl-1.5 so each nested level's chevron sits at the
parent's folder-icon x-position. The connecting line stays at the parent
chevron tip (ml-3 unchanged).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps fresco CDN to v0.1.5 and uses its new `theme={:inherit}` mode
on the MediaBrowser zoom viewer. Inherit mode opts the viewer out of
Fresco's own --fresco-* declarations; phoenix_kit supplies the
mapping to daisyUI tokens (base-100 / base-200 / base-300 /
base-content / primary) so background, dot grid, and nav buttons
follow whichever daisyUI theme is active on <html> — light gray
chips on light themes, dark gray on dark.
The mapping is shipped via two paths so it works in every parent
app: in phoenix_kit's own app.css for standalone pages, and injected
at runtime from phoenix_kit.js for parent apps (kit_test) that
serve their own compiled CSS and don't load phoenix_kit's app.css.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Exposes etcher 0.2.5's new dimension tool (line with arrows on both
ends + slidable label) in the MediaBrowser viewer's annotation
toolbar. Server-side bookkeeping to accept the new kind:
- PhoenixKit.Annotations.Annotation: 'dimension' added to @kinds
so the changeset's validate_inclusion accepts it.
- V119 migration: widen phoenix_kit_annotations_kind_check to
include 'dimension'. Folded into the existing V119 (folder
trash) since neither has shipped to a tagged release yet.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Picks up etcher's new dimension annotation (line + arrows + slidable
label) and the cross-browser baseline fixes in v0.2.4 / v0.2.5. Also
refreshes the comment that documents the matching hex requirement.
mix.exs stays at `~> 0.2` for now since 0.2.5 isn't published yet —
will tighten to `~> 0.2.5` once published.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
phoenix_kit now relies on features only in fresco 0.1.5 (theme: :inherit)
and etcher 0.2.5 (dimension annotation, callout/text/dimension in the
schema's @kinds). Bumps the requirements from `~> 0.1` / `~> 0.2` to
`~> 0.1.5` / `~> 0.2.5` so the resolver enforces the floor.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Picks up the dimension-creation flow fix: dimension shapes no longer
auto-open an inline label editor that was blocking the composer
popup. The composer now handles label + comment in one flow, so
clicking an existing dimension in cursor mode highlights its linked
comment via the existing data-annotation-uuid chain.
mix.exs req still at `~> 0.2.5` until 0.2.6 lands on hex.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Picks up the dimension-creation flow fix (no auto-inline-editor on
creation) on the resolver side. The CDN URL was already bumped in
the prior commit; this just enforces the floor.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ddon
ddon merged commit b1e90a6 into BeamLabEU:devMay 15, 2026
ddon pushed a commit that referenced this pull request May 18, 2026
Complete the 1.7.112 entry to cover all unreleased work since v1.7.111:
PR #548 (table_default sort/DnD, bulk_actions_bar, empty_state,
sort_selector, form_section/form_actions, Reorder/Values/Format utils),
PR #544 (MediaBrowser folder management overhaul), PR #545 (V120
document-creator taxonomy), PR #547/#542 (i18n manifests), and this
session's post-merge cleanup. Re-dated to 2026-05-18.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@alexdont@ddon