Host-side wiring for the AI-translate modal — use this in a form
LiveView and it injects the boilerplate that every consumer of
PhoenixKitWeb.Components.AITranslate.FormGlue was hand-duplicating:
- the six
handle_eventclauses that delegate the modal's button clicks (ai_toggle_modal,ai_select_endpoint,ai_select_prompt,ai_select_scope,ai_generate_prompt,ai_translate_lang) to the matchingFormGluehelpers, and - the
handle_info({:ai_translation, event, payload}, socket)clause that folds PubSub progress/result events back into the form.
Forgetting any of these is a silent failure: the modal renders, but translations never run, progress never updates, or the form never re-syncs with the result. The macro makes that impossible to forget.
Usage
use MyAppWeb, :live_view
use PhoenixKitWeb.Components.AITranslate.Embed
# still call this yourself in mount/apply_action — the resource is
# dynamic (the loaded record on :edit, nil on :new):
|> FormGlue.assign_ai_translation("project", project_or_nil, MyBinding)
# render the button/progress/hint/modal from FormGlue.ai_translate_config(assigns)How it composes
The handlers are attached as :handle_event / :handle_info lifecycle
hooks via on_mount, so they compose with the form's own
handle_event/handle_info clauses — no clause-grouping warning, no
clobbering. Non-AI events/messages pass straight through
({:cont, socket}); the AI ones are handled and halted. Mirrors the
lifecycle-hook approach in PhoenixKitWeb.Components.MediaBrowser.Embed
and PhoenixKitComments.Embed.
Lifecycle hooks run before the LiveView's own callbacks, and the AI
hooks :halt on the events/messages they own. So a host that also
defines handle_info({:ai_translation, _, _}, socket) (or one of the
six ai_* handle_event clauses) will find that clause shadowed —
the hook handles and halts first, the host clause never fires. There is
no double-handling; just don't re-implement the AI clauses in the host.
Form re-sync
After a translation merges into the live changeset, the form must be
re-assigned. The default sets both :changeset and :form
(assign(socket, changeset: cs, form: to_form(cs))) — the superset
that fits every current consumer. A host whose sync differs can define
ai_translate_assign_form/2 ((socket, changeset) -> socket); the hook
uses it when present.