# `PhoenixKitWeb.Components.MediaCanvasViewer`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v1.7.164/lib/phoenix_kit_web/components/media_canvas_viewer.ex#L1)

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's `phx-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 shape
    `MediaBrowser`'s `uploaded_files` carries.
  * `: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` (default `false`) — render only the canvas /
    composer column; suppresses the close button and the sidebar
    (filename + Download + metadata + comments). Used by
    standalone-page hosts like `MediaDetail` that have their own
    surrounding chrome (admin actions, metadata editor, file
    details).

# `build_comment_decorations`

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`.

# `comments_enabled?`

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.

# `load_annotations_for`

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.

# `render`

---

*Consult [api-reference.md](api-reference.md) for complete listing*
