Ecto schema for phoenix_kit_annotations.
Stores user-drawn shapes (rectangle, circle, polygon, freehand) tied to
a target: target_type + target_uuid, Etcher's own vocabulary.
A "file" target is a PhoenixKit.Modules.Storage.File and carries its
uuid in file_uuid as well (the hard FK every file-side feature — the
thumbnail job, has_annotations?/1, the comment threads — keys on).
Any other target (a projects whiteboard, say) carries NO file: the
shapes sit on an empty Fresco canvas. V183 pins the two shapes with a
CHECK. All geometry is in canvas-pixel coordinates; Fresco's coordinate
adapter rescales for pan/zoom at render time.
Comment thread linkage
An annotation's discussion lives in phoenix_kit_comments anchored to
the file (resource_type = "file", resource_uuid = file_uuid)
with metadata.annotation_uuid carrying the back-reference. This lets
annotation-rooted comments appear in the file's main comments thread
alongside non-annotated discussion. There is no comment_uuid column
on annotations — the relationship is one-directional from the comment
side, and a thread is created lazily when the first comment is posted.
Summary
Functions
Fields the Etcher storage adapter is allowed to take from event
payloads. Single source of truth so the adapter's whitelist doesn't
drift from the schema's @cast_fields. The target columns are
excluded — the adapter sets them server-side from the Etcher
target_type / target_uuid, never from the payload.
List of allowed kind strings.
Shape a target_type must have.
Types
@type t() :: %PhoenixKit.Annotations.Annotation{ __meta__: term(), creator_uuid: UUIDv7.t() | nil, file_uuid: UUIDv7.t() | nil, geometry: map(), inserted_at: DateTime.t() | nil, kind: String.t(), metadata: map() | nil, position: integer(), style: map() | nil, target_type: String.t(), target_uuid: UUIDv7.t() | nil, title: String.t() | nil, updated_at: DateTime.t() | nil, uuid: UUIDv7.t() | nil }
Functions
@spec adapter_writable_fields() :: [atom()]
Fields the Etcher storage adapter is allowed to take from event
payloads. Single source of truth so the adapter's whitelist doesn't
drift from the schema's @cast_fields. The target columns are
excluded — the adapter sets them server-side from the Etcher
target_type / target_uuid, never from the payload.
List of allowed kind strings.
@spec target_type_format() :: Regex.t()
Shape a target_type must have.
Shared with the Etcher adapter, which rejects a malformed target before
building attrs at all (the protocol wants {:error, :unsupported_target},
not a changeset). One regex so the two answers cannot disagree.