Packages

phoenix_kit

1.7.132
1.7.210 1.7.209 1.7.208 1.7.207 1.7.206 1.7.205 1.7.204 1.7.203 1.7.202 1.7.201 1.7.200 1.7.199 1.7.198 1.7.197 1.7.196 1.7.194 1.7.193 1.7.192 1.7.191 1.7.190 1.7.189 1.7.187 1.7.186 1.7.185 1.7.184 1.7.183 1.7.182 1.7.181 1.7.180 1.7.179 1.7.178 1.7.177 1.7.176 1.7.175 1.7.174 1.7.173 1.7.172 1.7.171 1.7.170 1.7.169 1.7.168 1.7.167 1.7.166 1.7.165 1.7.164 1.7.162 1.7.161 1.7.160 1.7.159 1.7.157 1.7.156 1.7.155 1.7.154 1.7.153 1.7.152 1.7.151 1.7.150 1.7.149 1.7.146 1.7.145 1.7.144 1.7.143 1.7.138 1.7.133 1.7.132 1.7.131 1.7.130 1.7.128 1.7.126 1.7.125 1.7.121 1.7.120 1.7.119 1.7.118 1.7.117 1.7.116 1.7.115 1.7.114 1.7.113 1.7.112 1.7.111 1.7.110 1.7.109 1.7.108 1.7.107 1.7.106 1.7.105 1.7.104 1.7.103 1.7.102 1.7.101 1.7.100 1.7.99 1.7.98 1.7.97 1.7.96 1.7.95 1.7.94 1.7.93 1.7.92 1.7.91 1.7.90 1.7.89 1.7.88 1.7.87 1.7.86 1.7.85 1.7.84 1.7.83 1.7.82 1.7.81 1.7.80 1.7.79 1.7.78 1.7.77 1.7.76 1.7.75 1.7.74 1.7.71 1.7.70 1.7.69 1.7.66 1.7.65 1.7.64 1.7.63 1.7.62 1.7.61 1.7.59 1.7.58 1.7.57 1.7.56 1.7.55 1.7.54 1.7.53 1.7.52 1.7.51 1.7.49 1.7.44 1.7.43 1.7.42 1.7.41 1.7.39 1.7.38 1.7.37 1.7.36 1.7.34 1.7.33 1.7.31 1.7.30 1.7.29 1.7.28 1.7.27 1.7.26 1.7.25 1.7.24 1.7.23 1.7.22 1.7.21 1.7.20 1.7.19 1.7.18 1.7.17 1.7.16 1.7.15 1.7.14 1.7.13 1.7.12 1.7.11 1.7.10 1.7.9 1.7.8 1.7.7 1.7.6 1.7.5 1.7.4 1.7.3 1.7.2 1.7.1 1.7.0 1.6.20 1.6.19 1.6.18 1.6.17 1.6.16 1.6.15 1.6.14 1.6.13 1.6.12 1.6.11 1.6.10 1.6.9 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.5.2 1.5.1 1.5.0 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.2 1.3.1 1.3.0 1.2.10 1.2.9 1.2.8 1.2.7 1.2.5 1.2.4 1.2.2 1.2.1 1.2.0 1.1.0 1.0.0

A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more

Current section

Files

Jump to
phoenix_kit lib phoenix_kit_web components ai_translate embed.ex
Raw

lib/phoenix_kit_web/components/ai_translate/embed.ex

defmodule PhoenixKitWeb.Components.AITranslate.Embed do
@moduledoc """
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_event` clauses 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
matching `FormGlue` helpers, 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.
"""
alias PhoenixKitWeb.Components.AITranslate.FormGlue
defmacro __using__(_opts) do
quote do
on_mount(PhoenixKitWeb.Components.AITranslate.Embed)
end
end
@doc false
def on_mount(:default, _params, _session, socket) do
socket =
socket
|> Phoenix.LiveView.attach_hook(
:phoenix_kit_ai_translate_events,
:handle_event,
&__handle_event__/3
)
|> Phoenix.LiveView.attach_hook(
:phoenix_kit_ai_translate_info,
:handle_info,
&__handle_info__/2
)
{:cont, socket}
end
@doc false
def __handle_event__("ai_translate_lang", %{"lang" => lang}, socket),
do: {:halt, FormGlue.dispatch_ai_translate(socket, lang)}
def __handle_event__("ai_toggle_modal", _params, socket),
do: {:halt, FormGlue.toggle_ai_modal(socket)}
def __handle_event__("ai_select_endpoint", %{"endpoint_uuid" => uuid}, socket),
do: {:halt, FormGlue.select_ai_endpoint(socket, uuid)}
def __handle_event__("ai_select_prompt", %{"prompt_uuid" => uuid}, socket),
do: {:halt, FormGlue.select_ai_prompt(socket, uuid)}
def __handle_event__("ai_select_scope", %{"scope" => scope}, socket),
do: {:halt, FormGlue.select_ai_scope(socket, scope)}
def __handle_event__("ai_generate_prompt", _params, socket),
do: {:halt, FormGlue.generate_ai_prompt(socket)}
def __handle_event__(_event, _params, socket), do: {:cont, socket}
@doc false
def __handle_info__({:ai_translation, event, payload}, socket) do
{:halt, FormGlue.handle_ai_translation_event(socket, event, payload, &resync_form/2)}
end
def __handle_info__(_msg, socket), do: {:cont, socket}
# Re-assign the form after a translation patches the changeset. Honors a
# host-defined `ai_translate_assign_form/2` override; otherwise sets both
# `:changeset` and `:form`.
defp resync_form(socket, changeset) do
view = socket.view
if is_atom(view) and function_exported?(view, :ai_translate_assign_form, 2) do
view.ai_translate_assign_form(socket, changeset)
else
socket
|> Phoenix.Component.assign(:changeset, changeset)
|> Phoenix.Component.assign(:form, Phoenix.Component.to_form(changeset))
end
end
end