Per-file viewer LiveComponent: <Fresco.canvas> + <Etcher.layer> for
images, video / PDF / icon fallback for other types, plus a sidebar
with filename + Download + metadata + comments thread. Owns the
annotation lifecycle (composer popover, persistence sync via
EtcherAdapter, the patch-shape / delete-shape JS bridges).
Shared between MediaBrowser (admin file grid → click image → modal)
and MediaViewer (lightbox embedded by MediaGallery and other
consumers). Both parents own their own modal shell + prev/next
navigation; this component is just the per-file content.
Required assigns
:id— DOM id, must include the file uuid so the parent's prev/next navigation remounts the component on file change (and Fresco'sphx-update="ignore"canvas DOM is replaced wholesale with the new file's image).:file— curated file map (%{file_uuid, mime_type, urls, filename, file_type, size, inserted_at, ...}). Same shapeMediaBrowser'suploaded_filescarries.:current_user— logged-in user; nil-tolerant. When nil the composer + comments thread don't render (no user to attribute a comment to).:parent_id— DOM id of the outer LiveComponent (the modal host). Currently unused by this component, but kept on the assigns map so future cross-component send_updates have a target without re-plumbing the API.
Optional assigns
:viewer_only(defaultfalse) — render only the canvas / composer column; suppresses the close button and the sidebar (filename + Download + metadata + comments). Used by standalone-page hosts likeMediaDetailthat have their own surrounding chrome (admin actions, metadata editor, file details).
Summary
Functions
Build the comment_decorations registry that CommentsComponent
uses to render external labels above comments. The comments
package speaks a generic %{metadata_key => %{value => entry}}
vocabulary; we package annotation titles under the
"annotation_uuid" metadata key (matching the back-reference
comments already store via metadata["annotation_uuid"]).
Runtime check for whether the optional phoenix_kit_comments package
is installed AND enabled. Public so standalone-page hosts can gate
their own embed of CommentsComponent the same way the sidebar does.
Loads annotations for a file into the curated map shape this component
uses internally (uuid / kind / geometry / style / metadata with
comment_* + title injected). Public so standalone-page hosts like
MediaDetail can build their own decoration registry off the same
shape without re-querying the schema by hand.
Callback implementation for Phoenix.LiveComponent.render/1.
Functions
Build the comment_decorations registry that CommentsComponent
uses to render external labels above comments. The comments
package speaks a generic %{metadata_key => %{value => entry}}
vocabulary; we package annotation titles under the
"annotation_uuid" metadata key (matching the back-reference
comments already store via metadata["annotation_uuid"]).
Source for the label: load_annotations_for/1 projects the
schema's title column into metadata["title"] (so Etcher's
JS tooltip can read it as a single map). We read from the same
place rather than bolt a top-level field onto the curated
annotation map. Only entries with non-empty titles are
included; an annotation without a title contributes nothing
(and the comment renders without a label block).
Public so standalone-page hosts (MediaDetail) can build the
same registry against the annotations they loaded via
load_annotations_for/1.
Runtime check for whether the optional phoenix_kit_comments package
is installed AND enabled. Public so standalone-page hosts can gate
their own embed of CommentsComponent the same way the sidebar does.
Loads annotations for a file into the curated map shape this component
uses internally (uuid / kind / geometry / style / metadata with
comment_* + title injected). Public so standalone-page hosts like
MediaDetail can build their own decoration registry off the same
shape without re-querying the schema by hand.
Callback implementation for Phoenix.LiveComponent.render/1.