chore: converted ai chat to a live component

This commit is contained in:
2026-05-03 17:20:52 +02:00
parent 98243cbd16
commit fa76cdf11d
6 changed files with 576 additions and 545 deletions

View File

@@ -3,144 +3,7 @@ defmodule BDS.Desktop.ShellLive.ChatSurface do
import Phoenix.Component, only: [assign: 3]
alias BDS.BoundedAtoms
alias BDS.Desktop.ShellData
alias BDS.Desktop.ShellLive.{ChatEditor, TabHelpers}
alias BDS.UI.Workbench
@doc """
Handle a chat-surface action from a chat message. Receives callbacks for
`reload_shell/2` and `open_sidebar_item/3` to remain decoupled from
`BDS.Desktop.ShellLive` private state.
"""
def handle_action(socket, params, callbacks) do
surface_id = Map.get(params, "surface-id", "")
payload =
params
|> Map.get("payload")
|> decode_payload()
|> maybe_put_form_data(socket, surface_id)
case normalize_action(Map.get(params, "action", "")) do
:open_post ->
case Map.get(payload, "postId") || Map.get(payload, "post_id") do
post_id when is_binary(post_id) and post_id != "" ->
socket
|> clear_action_error()
|> callbacks.open_sidebar.(
%{
"route" => "post",
"id" => post_id,
"title" => TabHelpers.post_title(post_id),
"subtitle" => TabHelpers.post_subtitle(post_id)
},
:pin
)
_other ->
ChatEditor.set_action_error(
socket,
socket.assigns.current_tab.id,
"Invalid payload for openPost action",
callbacks.reload
)
end
:open_media ->
case Map.get(payload, "mediaId") || Map.get(payload, "media_id") do
media_id when is_binary(media_id) and media_id != "" ->
socket
|> clear_action_error()
|> callbacks.open_sidebar.(
%{
"route" => "media",
"id" => media_id,
"title" => TabHelpers.media_title(media_id),
"subtitle" => TabHelpers.media_subtitle(media_id)
},
:pin
)
_other ->
ChatEditor.set_action_error(
socket,
socket.assigns.current_tab.id,
"Invalid payload for openMedia action",
callbacks.reload
)
end
:open_settings ->
socket
|> clear_action_error()
|> callbacks.open_sidebar.(
%{
"route" => "settings",
"id" => "settings-ai",
"title" => "Settings",
"subtitle" => "AI"
},
:pin
)
:open_chat ->
chat_id =
Map.get(payload, "conversationId") || Map.get(payload, "conversation_id") ||
socket.assigns.current_tab.id
socket
|> clear_action_error()
|> callbacks.open_sidebar.(
%{
"route" => "chat",
"id" => chat_id,
"title" => Map.get(payload, "title", "Chat"),
"subtitle" => Map.get(payload, "subtitle", "")
},
:pin
)
:switch_view ->
case BoundedAtoms.sidebar_view(Map.get(payload, "view")) do
nil ->
ChatEditor.set_action_error(
socket,
socket.assigns.current_tab.id,
"Invalid payload for switchView action",
callbacks.reload
)
view ->
socket
|> clear_action_error()
|> callbacks.reload.(Workbench.click_activity(socket.assigns.workbench, view))
end
:toggle_sidebar ->
socket
|> clear_action_error()
|> callbacks.reload.(Workbench.toggle_sidebar(socket.assigns.workbench))
:toggle_panel ->
socket
|> clear_action_error()
|> callbacks.reload.(Workbench.toggle_panel(socket.assigns.workbench))
:toggle_assistant_sidebar ->
socket
|> clear_action_error()
|> callbacks.reload.(Workbench.toggle_assistant_sidebar(socket.assigns.workbench))
:unknown ->
ChatEditor.set_action_error(
socket,
socket.assigns.current_tab.id,
"Unsupported assistant action",
callbacks.reload
)
end
end
def assistant_turn(prompt, socket) do
[
@@ -165,61 +28,6 @@ defmodule BDS.Desktop.ShellLive.ChatSurface do
end
end
def clear_action_error(%{assigns: %{current_tab: %{type: :chat, id: conversation_id}}} = socket) do
assign(
socket,
:chat_editor_action_errors,
Map.delete(socket.assigns.chat_editor_action_errors, conversation_id)
)
end
def clear_action_error(socket), do: socket
defp decode_payload(nil), do: %{}
defp decode_payload(""), do: %{}
defp decode_payload(payload) when is_binary(payload) do
case Jason.decode(payload) do
{:ok, decoded} when is_map(decoded) -> decoded
_other -> %{}
end
end
defp decode_payload(_payload), do: %{}
defp maybe_put_form_data(payload, socket, surface_id)
when is_binary(surface_id) and surface_id != "" do
form_data = ChatEditor.current_surface_data(socket, surface_id)
if form_data == %{} do
payload
else
Map.put(payload, "formData", form_data)
end
end
defp maybe_put_form_data(payload, _socket, _surface_id), do: payload
defp normalize_action(action) do
action
|> to_string()
|> String.replace("_", "")
|> String.downcase()
|> case do
"openpost" -> :open_post
"openmedia" -> :open_media
"opensettings" -> :open_settings
"openchat" -> :open_chat
"switchview" -> :switch_view
"setactiveview" -> :switch_view
"togglesidebar" -> :toggle_sidebar
"togglepanel" -> :toggle_panel
"openpanel" -> :toggle_panel
"toggleassistantsidebar" -> :toggle_assistant_sidebar
_other -> :unknown
end
end
defp assistant_reply(socket) do
if socket.assigns.offline_mode do
ShellData.translate(