Annotai (Annotai v0.2.0)

Copy Markdown View Source

Dev-only Plug. Mount it in your endpoint, guarded so it never reaches prod:

if Mix.env() == :dev do
  plug Annotai
end

For requests under /annotai/* it serves the widget asset, the annotation API, and the MCP endpoint (via Annotai.Router). For every other HTML response it injects a single <script src="/annotai/app.js"> before </body> — the only thing added to the host page; the entire widget UI is built client-side in a Shadow DOM root.

Place plug Annotai before Plug.Parsers (the MCP endpoint needs the raw body) and before the code-reloading block. mix annotai.install does this for you.

Disabling

Annotai is on by default. Set config :annotai, enabled: false to make the plug fully inert — no widget injection and no /annotai/* routes. To control it via an environment variable instead, drive the flag from one in the host app's config:

# config/dev.exs
config :annotai, enabled: System.get_env("DISABLE_ANNOTAI") != "true"

Then run with DISABLE_ANNOTAI=true mix phx.server and Annotai disappears.

Placement

The widget sits in the bottom-right corner by default. If another dev tool lives there too (e.g. LiveDebugger), move Annotai out of the way with :position — a keyword list of CSS insets, exactly like position: fixed:

# config/dev.exs
config :annotai, position: [bottom: 20, right: 220]   # slide left of the other widget
config :annotai, position: [bottom: 20, left: 20]     # dodge to the other corner

Each key (:top / :bottom / :left / :right) is a distance from that edge; integers are pixels, strings pass through as-is ("2rem"). The corner the widget anchors to — which also decides how its panels open — falls out of the edges you name. Set at most one of :top/:bottom and one of :left/:right; omit an axis to keep its 20px default. An invalid value is ignored (with a log warning) and the default placement is used.

Summary

Functions

Whether Annotai is active. Controlled by config :annotai, enabled: bool (default true). When false, call/2 returns the conn untouched, so the widget is never injected and /annotai/* is never served. See the module doc for the per-developer opt-out pattern.

The installed Annotai package version, as a string (e.g. "0.1.0"). Sourced from the loaded application spec so it tracks mix.exs without duplication.

Functions

enabled?()

@spec enabled?() :: boolean()

Whether Annotai is active. Controlled by config :annotai, enabled: bool (default true). When false, call/2 returns the conn untouched, so the widget is never injected and /annotai/* is never served. See the module doc for the per-developer opt-out pattern.

version()

@spec version() :: String.t()

The installed Annotai package version, as a string (e.g. "0.1.0"). Sourced from the loaded application spec so it tracks mix.exs without duplication.