Provides a component for resolving and rendering media file thumbnail URLs.
Handles file type detection (image, video, PDF, document) and selects the appropriate thumbnail variant from the file's URL map.
Summary
Functions
Normalizes a raw saved-rotation value (metadata["rotation"]) to one of the
four snapped angles.
Resolves the best thumbnail URL for a media file.
Tailwind class rotating a thumbnail to the image's saved orientation.
Resolves the best thumbnail URL for a media file.
Functions
@spec normalized_rotation(term()) :: 0 | 90 | 180 | 270
Normalizes a raw saved-rotation value (metadata["rotation"]) to one of the
four snapped angles.
Mirrors MediaCanvasViewer's own normalization: only the four snapped
angles Fresco emits count; anything else (nil, garbage, a legacy string)
reads as unrotated (0).
Resolves the best thumbnail URL for a media file.
Returns nil when no suitable URL is available.
Examples
resolve_url(file) # small thumbnail
resolve_url(file, :medium) # medium preview
Tailwind class rotating a thumbnail to the image's saved orientation.
The orientation lives on the file row (metadata["rotation"], written by
the viewer's persist_rotation bridge) and applies to the whole image, so
every rendering of it — canvas and thumbnail — should honor it. Baked
variants stay unrotated: rendering the saved angle with a CSS transform
costs nothing, needs no re-encode, and keeps annotations (which live in
the image's own coordinate space) aligned as they rotate along.
In a square box (aspect-square cards, w-10 h-10 list cells) a
quarter turn needs nothing else: rotating a square leaves the same square,
and because object-cover crops about the center — which rotation maps
onto itself — cropping-then-rotating shows exactly what
rotating-then-cropping would.
A non-square box needs the quarter turn to scale back up, or the
covered image (now turned onto its side) would letterbox: pass box: and
the scale rides along. Half turns never need it. The scale literal per box
shape is hardcoded rather than computed — Tailwind only emits arbitrary
values it can see in source, so an interpolated scale-[…] would
silently render as no scale at all.
Nil-tolerant: unrotated, garbage, and maps carrying no :rotation at all
return nil.
Options
:box— the thumbnail box's aspect::square(default) or:landscape_4_3(the stacks preview pile'sw-32 h-24).
Examples
<img src={url} class={["w-full h-full object-cover", rotation_class(file)]} />
<img src={url} class={["w-full h-full object-cover",
rotation_class(file, box: :landscape_4_3)]} />
Resolves the best thumbnail URL for a media file.
Size modes:
:small(default) — tiny cells (list rows, selectors): prefers the baked Etcher thumbnail, then the 150px thumbnail:card— large grid/stack cards: prefers the baked Etcher thumbnail (400px), then the 300pxsmall, thenmedium; only after those falls back to the light 150px thumbnail, keeping the full-res original (which would force a live vector overlay) as the last resort:medium— for gallery/preview: prefers medium/thumbnail variants
Attributes
file- Media file map withfile_typeandurlsfields (required)size- Thumbnail size preference::small,:card, or:medium(default::small)
Examples
<.thumbnail_url file={file} :let={url}>
<img src={url} />
</.thumbnail_url>
<.thumbnail_url file={file} size={:medium} :let={url}>
<img src={url} />
</.thumbnail_url>Attributes
file(:map) (required)size(:atom) - Defaults to:small. Must be one of:small,:card, or:medium.
Slots
inner_block(required)