fix: fixed CSM-017

This commit is contained in:
2026-05-09 17:33:51 +02:00
parent e4452ca504
commit 5c17751d55
15 changed files with 463 additions and 301 deletions

View File

@@ -9,25 +9,73 @@ defmodule BDS.Desktop.ShellLive.Bridges do
alias BDS.Desktop.ShellLive.{CliSync, SessionUtil}
alias BDS.UI.Workbench
@refreshable_tab_meta_types [:import, :chat]
@spec handle_info(tuple() | atom(), Phoenix.LiveView.Socket.t(), map()) ::
{:noreply, Phoenix.LiveView.Socket.t()}
def handle_info({:import_editor_output, title, message, level}, socket, callbacks) do
{:noreply, callbacks.append_output.(socket, title, message, nil, level)}
# ── Generic editor notifications (sent via Notify module) ────────────────
def handle_info({:editor_output, title, message, detail, level}, socket, callbacks) do
{:noreply, callbacks.append_output.(socket, title, message, detail, level)}
end
def handle_info({:import_editor_tab_meta, definition_id, title, subtitle}, socket, callbacks) do
tab_meta =
Map.put(socket.assigns.tab_meta, {:import, definition_id}, %{
title: title,
subtitle: subtitle || ""
})
def handle_info({:editor_tab_meta, type, id, updates}, socket, callbacks)
when is_atom(type) and is_map(updates) do
key = {type, id}
current_meta = Map.get(socket.assigns.tab_meta, key, %{})
next_meta = Map.merge(current_meta, updates)
tab_meta = Map.put(socket.assigns.tab_meta, key, next_meta)
socket = assign(socket, :tab_meta, tab_meta)
if type in @refreshable_tab_meta_types do
{:noreply, callbacks.refresh_sidebar.(socket, socket.assigns.workbench)}
else
{:noreply, socket}
end
end
def handle_info({:editor_dirty, type, id, dirty?}, socket, _callbacks) do
workbench =
if dirty? do
Workbench.mark_dirty(socket.assigns.workbench, type, id)
else
Workbench.clear_dirty(socket.assigns.workbench, type, id)
end
{:noreply, assign(socket, :workbench, workbench)}
end
def handle_info({:editor_command, action, params}, socket, callbacks) do
{:noreply, callbacks.apply_shell_command.(socket, action, params)}
end
# ── Shared actions (already generic) ─────────────────────────────────────
def handle_info({:open_sidebar_item, params, intent}, socket, callbacks) do
{:noreply, callbacks.open_sidebar.(socket, params, intent)}
end
def handle_info(:reload_shell, socket, callbacks) do
{:noreply, callbacks.reload.(socket, socket.assigns.workbench)}
end
def handle_info({:close_tab, type, id}, socket, callbacks) do
{:noreply,
socket
|> assign(:tab_meta, tab_meta)
|> callbacks.refresh_sidebar.(socket.assigns.workbench)}
callbacks.refresh_layout.(socket, Workbench.close_tab(socket.assigns.workbench, type, id))}
end
def handle_info(:tags_changed, socket, callbacks) do
{:noreply, callbacks.refresh_content.(socket, socket.assigns.workbench)}
end
def handle_info(:settings_changed, socket, callbacks) do
{:noreply, callbacks.reload.(socket, socket.assigns.workbench)}
end
# ── Chat editor messages (sent from AI streaming, not from Notify) ──────
def handle_info({:chat_tool_call, conversation_id, tool_call}, socket, _callbacks) do
send_update(ChatEditor,
id: "chat-editor-#{conversation_id}",
@@ -68,27 +116,6 @@ defmodule BDS.Desktop.ShellLive.Bridges do
{:noreply, assign(socket, :chat_editor_request_refs, refs)}
end
def handle_info({:chat_editor_output, title, message, level}, socket, callbacks) do
{:noreply, callbacks.append_output.(socket, title, message, nil, level)}
end
def handle_info({:chat_editor_tab_meta, conversation_id, title, subtitle}, socket, callbacks) do
tab_meta =
Map.put(socket.assigns.tab_meta, {:chat, conversation_id}, %{
title: title,
subtitle: subtitle || ""
})
{:noreply,
socket
|> assign(:tab_meta, tab_meta)
|> callbacks.refresh_sidebar.(socket.assigns.workbench)}
end
def handle_info({:open_sidebar_item, params, intent}, socket, callbacks) do
{:noreply, callbacks.open_sidebar.(socket, params, intent)}
end
def handle_info({:chat_editor_toggle_sidebar}, socket, callbacks) do
{:noreply,
callbacks.refresh_layout.(socket, Workbench.toggle_sidebar(socket.assigns.workbench))}
@@ -112,6 +139,35 @@ defmodule BDS.Desktop.ShellLive.Bridges do
callbacks.refresh_sidebar.(socket, Workbench.click_activity(socket.assigns.workbench, view))}
end
# ── Post editor cross-component messages (sent from OverlayManager) ─────
def handle_info({:post_editor_insert_content, post_id, content}, socket, _callbacks) do
send_update(PostEditor,
id: "post-editor-#{post_id}",
action: :insert_content,
content: content
)
{:noreply, socket}
end
def handle_info({:post_editor_translate, post_id, language}, socket, _callbacks) do
send_update(PostEditor, id: "post-editor-#{post_id}", action: :translate, language: language)
{:noreply, socket}
end
def handle_info({:post_editor_apply_ai_suggestions, post_id, fields}, socket, _callbacks) do
send_update(PostEditor,
id: "post-editor-#{post_id}",
action: :apply_ai_suggestions,
fields: fields
)
{:noreply, socket}
end
# ── External system messages ─────────────────────────────────────────────
def handle_info({:entity_changed, payload}, socket, callbacks) when is_map(payload) do
{:noreply, CliSync.apply_entity_change(socket, payload, callbacks.refresh_content)}
end
@@ -155,126 +211,5 @@ defmodule BDS.Desktop.ShellLive.Bridges do
{:noreply, socket}
end
def handle_info({:tags_editor_output, title, message, level}, socket, callbacks) do
{:noreply, callbacks.append_output.(socket, title, message, nil, level)}
end
def handle_info(:tags_changed, socket, callbacks) do
{:noreply, callbacks.refresh_content.(socket, socket.assigns.workbench)}
end
def handle_info({:settings_output, title, message, level}, socket, callbacks) do
{:noreply, callbacks.append_output.(socket, title, message, nil, level)}
end
def handle_info(:settings_changed, socket, callbacks) do
{:noreply, callbacks.reload.(socket, socket.assigns.workbench)}
end
def handle_info({:menu_editor_output, title, message, level}, socket, callbacks) do
{:noreply, callbacks.append_output.(socket, title, message, nil, level)}
end
def handle_info({:script_editor_output, title, message, level}, socket, callbacks) do
{:noreply, callbacks.append_output.(socket, title, message, nil, level)}
end
def handle_info({:template_editor_output, title, message, level}, socket, callbacks) do
{:noreply, callbacks.append_output.(socket, title, message, nil, level)}
end
def handle_info({:misc_editor_output, title, message, _detail, level}, socket, callbacks) do
{:noreply, callbacks.append_output.(socket, title, message, nil, level)}
end
def handle_info({:misc_editor_command, action, params}, socket, callbacks) do
{:noreply, callbacks.apply_shell_command.(socket, action, params)}
end
def handle_info({:misc_editor_tab_meta, tab_type, tab_id, updates}, socket, _callbacks) do
key = {tab_type, tab_id}
current_meta = Map.get(socket.assigns.tab_meta, key, %{})
next_meta = Map.merge(current_meta, updates)
{:noreply, assign(socket, :tab_meta, Map.put(socket.assigns.tab_meta, key, next_meta))}
end
def handle_info({:post_editor_output, title, message, level}, socket, callbacks) do
{:noreply, callbacks.append_output.(socket, title, message, nil, level)}
end
def handle_info({:post_editor_dirty, post_id, dirty?}, socket, _callbacks) do
workbench =
if dirty? do
Workbench.mark_dirty(socket.assigns.workbench, :post, post_id)
else
Workbench.clear_dirty(socket.assigns.workbench, :post, post_id)
end
{:noreply, assign(socket, :workbench, workbench)}
end
def handle_info({:post_editor_tab_meta, post_id, title, subtitle}, socket, _callbacks) do
tab_meta =
Map.put(socket.assigns.tab_meta, {:post, post_id}, %{title: title, subtitle: subtitle})
{:noreply, assign(socket, :tab_meta, tab_meta)}
end
def handle_info({:post_editor_insert_content, post_id, content}, socket, _callbacks) do
send_update(PostEditor,
id: "post-editor-#{post_id}",
action: :insert_content,
content: content
)
{:noreply, socket}
end
def handle_info({:post_editor_translate, post_id, language}, socket, _callbacks) do
send_update(PostEditor, id: "post-editor-#{post_id}", action: :translate, language: language)
{:noreply, socket}
end
def handle_info({:post_editor_apply_ai_suggestions, post_id, fields}, socket, _callbacks) do
send_update(PostEditor,
id: "post-editor-#{post_id}",
action: :apply_ai_suggestions,
fields: fields
)
{:noreply, socket}
end
def handle_info({:media_editor_output, title, message, level}, socket, callbacks) do
{:noreply, callbacks.append_output.(socket, title, message, nil, level)}
end
def handle_info({:media_editor_dirty, media_id, dirty?}, socket, _callbacks) do
workbench =
if dirty? do
Workbench.mark_dirty(socket.assigns.workbench, :media, media_id)
else
Workbench.clear_dirty(socket.assigns.workbench, :media, media_id)
end
{:noreply, assign(socket, :workbench, workbench)}
end
def handle_info({:media_editor_tab_meta, media_id, title, subtitle}, socket, _callbacks) do
tab_meta =
Map.put(socket.assigns.tab_meta, {:media, media_id}, %{title: title, subtitle: subtitle})
{:noreply, assign(socket, :tab_meta, tab_meta)}
end
def handle_info(:reload_shell, socket, callbacks) do
{:noreply, callbacks.reload.(socket, socket.assigns.workbench)}
end
def handle_info({:close_tab, type, id}, socket, callbacks) do
{:noreply,
callbacks.refresh_layout.(socket, Workbench.close_tab(socket.assigns.workbench, type, id))}
end
def handle_info(_message, socket, _callbacks), do: {:noreply, socket}
end

View File

@@ -7,6 +7,7 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
alias BDS.{AI, BoundedAtoms, MapUtils, Persistence}
alias BDS.Desktop.ShellLive.ChatEditor.{MessageBuild, ModelSelection, ToolTracking}
alias BDS.Desktop.ShellLive.Notify
alias BDS.Desktop.ShellLive.TabHelpers
use Gettext, backend: BDS.Gettext
@@ -77,7 +78,7 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
{:noreply, assign(socket, :model_selector_open?, false) |> build_data()}
{:error, reason} ->
notify_parent({:chat_editor_output, dgettext("ui", "Chat"), inspect(reason), "error"})
Notify.output(dgettext("ui", "Chat"), inspect(reason), "error")
{:noreply, assign(socket, :model_selector_open?, false) |> build_data()}
end
end
@@ -129,14 +130,14 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
end
def handle_event("open_chat_settings", _params, socket) do
notify_parent(
{:open_sidebar_item,
%{
"route" => "settings",
"id" => "settings-ai",
"title" => "Settings",
"subtitle" => "AI"
}, :pin}
Notify.open_sidebar_item(
%{
"route" => "settings",
"id" => "settings-ai",
"title" => "Settings",
"subtitle" => "AI"
},
:pin
)
{:noreply, socket}
@@ -203,10 +204,8 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
build_data(socket)
socket.assigns.offline_mode ->
notify_parent(
{:chat_editor_output, dgettext("ui", "Chat"),
dgettext("ui", "Automatic AI actions stay gated by airplane mode."), "info"}
)
Notify.output(dgettext("ui", "Chat"),
dgettext("ui", "Automatic AI actions stay gated by airplane mode."), "info")
build_data(socket)
@@ -227,7 +226,7 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
:ok = allow_repo_sandbox(task.pid)
notify_parent({:chat_editor_task_started, conversation_id, task.ref})
Notify.parent({:chat_editor_task_started, conversation_id, task.ref})
socket
|> assign(:input, "")
@@ -254,7 +253,7 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
%{ref: ref} = _request ->
:ok = AI.cancel_chat(conversation_id)
notify_parent({:chat_editor_task_cancelled, conversation_id, ref})
Notify.parent({:chat_editor_task_cancelled, conversation_id, ref})
socket
|> assign(:request, nil)
@@ -293,9 +292,7 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
assign(socket, :request, nil) |> build_data()
{:error, reason} ->
notify_parent(
{:chat_editor_output, dgettext("ui", "Chat"), format_error(reason), "error"}
)
Notify.output(dgettext("ui", "Chat"), format_error(reason), "error")
assign(socket, :request, nil) |> build_data()
end
@@ -347,7 +344,7 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
|> MapUtils.attr(:title)
if is_binary(title) and String.trim(title) != "" do
notify_parent({:chat_editor_tab_meta, socket.assigns.conversation_id, title, ""})
Notify.tab_meta(:chat, socket.assigns.conversation_id, title, "")
end
socket
@@ -368,14 +365,14 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
:open_post ->
case Map.get(payload, "postId") || Map.get(payload, "post_id") do
post_id when is_binary(post_id) and post_id != "" ->
notify_parent(
{:open_sidebar_item,
%{
"route" => "post",
"id" => post_id,
"title" => TabHelpers.post_title(post_id),
"subtitle" => TabHelpers.post_subtitle(post_id)
}, :pin}
Notify.open_sidebar_item(
%{
"route" => "post",
"id" => post_id,
"title" => TabHelpers.post_title(post_id),
"subtitle" => TabHelpers.post_subtitle(post_id)
},
:pin
)
assign(socket, :action_error, nil) |> build_data()
@@ -387,14 +384,14 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
:open_media ->
case Map.get(payload, "mediaId") || Map.get(payload, "media_id") do
media_id when is_binary(media_id) and media_id != "" ->
notify_parent(
{:open_sidebar_item,
%{
"route" => "media",
"id" => media_id,
"title" => TabHelpers.media_title(media_id),
"subtitle" => TabHelpers.media_subtitle(media_id)
}, :pin}
Notify.open_sidebar_item(
%{
"route" => "media",
"id" => media_id,
"title" => TabHelpers.media_title(media_id),
"subtitle" => TabHelpers.media_subtitle(media_id)
},
:pin
)
assign(socket, :action_error, nil) |> build_data()
@@ -404,14 +401,14 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
end
:open_settings ->
notify_parent(
{:open_sidebar_item,
%{
"route" => "settings",
"id" => "settings-ai",
"title" => "Settings",
"subtitle" => "AI"
}, :pin}
Notify.open_sidebar_item(
%{
"route" => "settings",
"id" => "settings-ai",
"title" => "Settings",
"subtitle" => "AI"
},
:pin
)
assign(socket, :action_error, nil) |> build_data()
@@ -421,14 +418,14 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
Map.get(payload, "conversationId") || Map.get(payload, "conversation_id") ||
socket.assigns.conversation_id
notify_parent(
{:open_sidebar_item,
%{
"route" => "chat",
"id" => chat_id,
"title" => Map.get(payload, "title", "Chat"),
"subtitle" => Map.get(payload, "subtitle", "")
}, :pin}
Notify.open_sidebar_item(
%{
"route" => "chat",
"id" => chat_id,
"title" => Map.get(payload, "title", "Chat"),
"subtitle" => Map.get(payload, "subtitle", "")
},
:pin
)
assign(socket, :action_error, nil) |> build_data()
@@ -439,20 +436,20 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
set_action_error(socket, "Invalid payload for switchView action")
view ->
notify_parent({:chat_editor_switch_view, view})
Notify.parent({:chat_editor_switch_view, view})
assign(socket, :action_error, nil) |> build_data()
end
:toggle_sidebar ->
notify_parent({:chat_editor_toggle_sidebar})
Notify.parent({:chat_editor_toggle_sidebar})
assign(socket, :action_error, nil) |> build_data()
:toggle_panel ->
notify_parent({:chat_editor_toggle_panel})
Notify.parent({:chat_editor_toggle_panel})
assign(socket, :action_error, nil) |> build_data()
:toggle_assistant_sidebar ->
notify_parent({:chat_editor_toggle_assistant_sidebar})
Notify.parent({:chat_editor_toggle_assistant_sidebar})
assign(socket, :action_error, nil) |> build_data()
:unknown ->
@@ -822,9 +819,6 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
# ── Private helpers ───────────────────────────────────────────────────────
defp notify_parent(message) do
send(self(), message)
end
defp active_project_id(socket) do
socket.assigns[:project_id]

View File

@@ -5,6 +5,7 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
alias BDS.{AI, ImportAnalysis, ImportDefinitions, ImportExecution}
alias BDS.Desktop.{FilePicker, FolderPicker, ShellData}
alias BDS.Desktop.ShellLive.Notify
alias BDS.Desktop.ShellLive.ImportEditor.{
AnalysisState,
@@ -641,12 +642,11 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
defp maybe_update_tab_meta(socket, name) do
title = name || dgettext("ui", "Untitled Import")
notify_parent(
{:import_editor_tab_meta, socket.assigns.definition_id, title,
dgettext(
"ui",
"Select a WordPress export file (WXR) and an uploads folder to analyze what would be imported."
)}
Notify.tab_meta(:import, socket.assigns.definition_id, title,
dgettext(
"ui",
"Select a WordPress export file (WXR) and an uploads folder to analyze what would be imported."
)
)
socket
@@ -1404,12 +1404,8 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
# ── Private helpers ───────────────────────────────────────────────────────
defp notify_parent(message) do
send(self(), message)
end
defp notify_output(socket, title, message, level \\ "info") do
notify_parent({:import_editor_output, title, message, level})
Notify.output(title, message, level)
socket
end

View File

@@ -6,6 +6,7 @@ defmodule BDS.Desktop.ShellLive.MediaEditor do
import Ecto.Query
alias BDS.Desktop.{FilePicker}
alias BDS.Desktop.ShellLive.Notify
alias BDS.{AI, I18n, Media}
alias BDS.Media.Media, as: MediaRecord
alias BDS.Media.Translation
@@ -90,7 +91,7 @@ defmodule BDS.Desktop.ShellLive.MediaEditor do
|> build_data()
if dirty? != was_dirty? do
notify_parent({:media_editor_dirty, socket.assigns.media_id, dirty?})
Notify.dirty(:media, socket.assigns.media_id, dirty?)
end
{:noreply, socket}
@@ -123,12 +124,10 @@ defmodule BDS.Desktop.ShellLive.MediaEditor do
|> assign(:dirty?, false)
|> build_data()
notify_parent({:media_editor_dirty, media.id, false})
Notify.dirty(:media, media.id, false)
notify_parent(
{:media_editor_tab_meta, media.id, display_title(updated_media),
updated_media.original_name || updated_media.mime_type || ""}
)
Notify.tab_meta(:media, media.id, display_title(updated_media),
updated_media.original_name || updated_media.mime_type || "")
{:noreply, socket}
@@ -483,12 +482,10 @@ defmodule BDS.Desktop.ShellLive.MediaEditor do
|> assign(:save_state, :saved)
|> build_data()
notify_parent({:media_editor_dirty, media.id, false})
Notify.dirty(:media, media.id, false)
notify_parent(
{:media_editor_tab_meta, media.id, display_title(updated_media),
updated_media.original_name || updated_media.mime_type || ""}
)
Notify.tab_meta(:media, media.id, display_title(updated_media),
updated_media.original_name || updated_media.mime_type || "")
notify_output(socket, dgettext("ui", "Media"), dgettext("ui", "Media saved"))
socket
@@ -528,7 +525,7 @@ defmodule BDS.Desktop.ShellLive.MediaEditor do
|> assign(:quick_actions_open?, false)
|> build_data()
notify_parent({:media_editor_dirty, media.id, dirty?})
Notify.dirty(:media, media.id, dirty?)
socket
end
end
@@ -569,12 +566,8 @@ defmodule BDS.Desktop.ShellLive.MediaEditor do
end
end
defp notify_parent(message) do
send(self(), message)
end
defp notify_output(socket, title, message, level \\ "info") do
send(self(), {:media_editor_output, title, message, level})
Notify.output(title, message, level)
socket
end

View File

@@ -5,6 +5,8 @@ defmodule BDS.Desktop.ShellLive.MenuEditor do
use Gettext, backend: BDS.Gettext
alias BDS.Desktop.ShellLive.Notify
alias BDS.Desktop.ShellLive.MenuEditor.{
DraftManagement,
PageCategory,
@@ -251,7 +253,7 @@ defmodule BDS.Desktop.ShellLive.MenuEditor do
end
defp notify_output(title, message, level) do
send(self(), {:menu_editor_output, title, message, level})
Notify.output(title, message, level)
end
attr(:menu_editor, :map, required: true)

View File

@@ -7,6 +7,7 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
import Ecto.Query
alias BDS.{Embeddings, Generation, Git, HelpDocs, Posts, Repo}
alias BDS.Desktop.ShellLive.Notify
alias BDS.MapUtils
alias BDS.Settings.Setting
use Gettext, backend: BDS.Gettext
@@ -358,19 +359,19 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
# ── Private helpers ────────────────────────────────────────────────────────
defp notify_command(action, params \\ %{}) do
send(self(), {:misc_editor_command, action, params})
Notify.command(action, params)
end
defp notify_output(title, message, detail \\ nil, level \\ "info") do
send(self(), {:misc_editor_output, title, message, detail, level})
Notify.output(title, message, detail, level)
end
defp notify_tab_meta(tab_type, tab_id, updates) do
send(self(), {:misc_editor_tab_meta, tab_type, tab_id, updates})
Notify.tab_meta_merge(tab_type, tab_id, updates)
end
defp notify_open_sidebar_item(params, intent) do
send(self(), {:open_sidebar_item, params, intent})
Notify.open_sidebar_item(params, intent)
end
defp rerun_action(assigns) do

View File

@@ -0,0 +1,70 @@
defmodule BDS.Desktop.ShellLive.Notify do
@moduledoc """
Standardized parent notification API for LiveComponent editors.
Instead of each editor defining its own `notify_parent/1` and sending
editor-specific message atoms (e.g. `{:post_editor_output, ...}`),
all editors call functions from this module, which sends generic
messages that Bridges handles with a single clause per action type.
"""
@spec output(String.t(), String.t(), String.t()) :: :ok
def output(title, message, level) do
send(self(), {:editor_output, title, message, nil, level})
:ok
end
@spec output(String.t(), String.t(), String.t() | nil, String.t()) :: :ok
def output(title, message, detail, level) do
send(self(), {:editor_output, title, message, detail, level})
:ok
end
@spec tab_meta(atom(), term(), String.t(), String.t()) :: :ok
def tab_meta(type, id, title, subtitle) do
send(self(), {:editor_tab_meta, type, id, %{title: title, subtitle: subtitle || ""}})
:ok
end
@spec tab_meta_merge(atom(), term(), map()) :: :ok
def tab_meta_merge(type, id, updates) when is_map(updates) do
send(self(), {:editor_tab_meta, type, id, updates})
:ok
end
@spec close_tab(atom(), term()) :: :ok
def close_tab(type, id) do
send(self(), {:close_tab, type, id})
:ok
end
@spec reload :: :ok
def reload do
send(self(), :reload_shell)
:ok
end
@spec dirty(atom(), term(), boolean()) :: :ok
def dirty(type, id, dirty?) do
send(self(), {:editor_dirty, type, id, dirty?})
:ok
end
@spec command(atom() | String.t(), map()) :: :ok
def command(action, params \\ %{}) do
send(self(), {:editor_command, action, params})
:ok
end
@spec open_sidebar_item(map(), atom() | nil) :: :ok
def open_sidebar_item(params, intent \\ nil) do
send(self(), {:open_sidebar_item, params, intent})
:ok
end
@spec parent(term()) :: :ok
def parent(message) do
send(self(), message)
:ok
end
end

View File

@@ -11,6 +11,7 @@ defmodule BDS.Desktop.ShellLive.OverlayManager do
alias BDS.Desktop.ShellLive.{
MediaEditor,
Notify,
PostEditor,
TabHelpers
}
@@ -170,7 +171,7 @@ defmodule BDS.Desktop.ShellLive.OverlayManager do
"[#{result.original_name}](bds-media://#{result.media_id})"
end
send(self(), {:post_editor_insert_content, post_id, syntax})
Notify.parent({:post_editor_insert_content, post_id, syntax})
socket
end
@@ -195,7 +196,7 @@ defmodule BDS.Desktop.ShellLive.OverlayManager do
end
if details do
send(self(), {:post_editor_insert_content, post_id, details})
Notify.parent({:post_editor_insert_content, post_id, details})
end
socket
@@ -213,7 +214,7 @@ defmodule BDS.Desktop.ShellLive.OverlayManager do
socket =
case {socket.assigns[:shell_overlay], current_tab} do
{%{kind: :language_picker}, %{type: :post, id: post_id}} ->
send(self(), {:post_editor_translate, post_id, code})
Notify.parent({:post_editor_translate, post_id, code})
socket
{%{kind: :language_picker}, %{type: :media, id: media_id}} ->

View File

@@ -5,6 +5,7 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
alias BDS.{AI, Posts, Preview}
alias BDS.Desktop.ShellData
alias BDS.Desktop.ShellLive.Notify
alias BDS.Desktop.ShellLive.PostEditor.{DraftManagement, ListValues, Persistence, PostMetadata}
alias BDS.Posts.Post
alias BDS.Tags
@@ -181,7 +182,7 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|> build_data()
if dirty? != was_dirty? do
notify_parent({:post_editor_dirty, post_id, dirty?})
Notify.dirty(:post, post_id, dirty?)
end
{:noreply, socket}
@@ -456,12 +457,10 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|> assign(:dirty?, false)
|> build_data()
notify_parent(
{:post_editor_tab_meta, post.id, record_title(record, refreshed_post),
Atom.to_string(record_status(record))}
)
Notify.tab_meta(:post, post.id, record_title(record, refreshed_post),
Atom.to_string(record_status(record)))
notify_parent({:post_editor_dirty, post.id, false})
Notify.dirty(:post, post.id, false)
notify_output(socket, dgettext("ui", "Post"), dgettext("ui", "Post saved"))
socket
@@ -497,12 +496,10 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|> assign(:dirty?, false)
|> build_data()
notify_parent(
{:post_editor_tab_meta, post.id, record_title(record, refreshed_post),
Atom.to_string(record_status(record))}
)
Notify.tab_meta(:post, post.id, record_title(record, refreshed_post),
Atom.to_string(record_status(record)))
notify_parent({:post_editor_dirty, post.id, false})
Notify.dirty(:post, post.id, false)
notify_output(socket, dgettext("ui", "Post"), dgettext("ui", "Post published"))
socket
@@ -534,13 +531,11 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|> assign(:dirty?, false)
|> build_data()
notify_parent(
{:post_editor_tab_meta, post.id,
restored_post.title || restored_post.slug || restored_post.id,
Atom.to_string(restored_post.status || :draft)}
)
Notify.tab_meta(:post, post.id,
restored_post.title || restored_post.slug || restored_post.id,
Atom.to_string(restored_post.status || :draft))
notify_parent({:post_editor_dirty, post.id, false})
Notify.dirty(:post, post.id, false)
socket
{:error, reason} ->
@@ -555,7 +550,7 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
case Posts.delete_post(post_id) do
{:ok, :deleted} ->
notify_parent({:close_tab, :post, post_id})
Notify.close_tab(:post, post_id)
socket
{:error, reason} ->
@@ -642,7 +637,7 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|> assign(:quick_actions_open?, false)
|> build_data()
notify_parent({:post_editor_dirty, post_id, false})
Notify.dirty(:post, post_id, false)
socket
else
{:error, reason} ->
@@ -698,7 +693,7 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|> assign(:shell_overlay, nil)
|> build_data()
notify_parent({:post_editor_dirty, post_id, true})
Notify.dirty(:post, post_id, true)
socket
{:error, reason} ->
@@ -741,7 +736,7 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|> put_component_draft_field(field_key(kind), updated)
|> build_data()
notify_parent({:post_editor_dirty, socket.assigns.post_id, true})
Notify.dirty(:post, socket.assigns.post_id, true)
assign(socket, :dirty?, true)
end
end
@@ -771,7 +766,7 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|> put_component_draft_field(field_key(kind), updated)
|> build_data()
notify_parent({:post_editor_dirty, socket.assigns.post_id, true})
Notify.dirty(:post, socket.assigns.post_id, true)
assign(socket, :dirty?, true)
end
end
@@ -807,12 +802,8 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
defp assign_query(socket, :tags, value), do: assign(socket, :tag_query, value)
defp assign_query(socket, :categories, value), do: assign(socket, :category_query, value)
defp notify_parent(message) do
send(self(), message)
end
defp notify_output(socket, title, message, level \\ "info") do
send(self(), {:post_editor_output, title, message, level})
Notify.output(title, message, level)
socket
end

View File

@@ -4,6 +4,7 @@ defmodule BDS.Desktop.ShellLive.ScriptEditor do
use Phoenix.LiveComponent
alias BDS.{Scripts, Scripting}
alias BDS.Desktop.ShellLive.Notify
alias BDS.Scripts.Script
use Gettext, backend: BDS.Gettext
@@ -225,7 +226,7 @@ defmodule BDS.Desktop.ShellLive.ScriptEditor do
case Scripts.delete_script(script_id) do
{:ok, _deleted} ->
send(self(), {:close_tab, :scripts, script_id})
Notify.close_tab(:scripts, script_id)
socket
{:error, reason} ->
@@ -282,12 +283,12 @@ defmodule BDS.Desktop.ShellLive.ScriptEditor do
end
defp notify_output(socket, title, message, level \\ "info") do
send(self(), {:script_editor_output, title, message, level})
Notify.output(title, message, level)
socket
end
defp notify_reload(socket) do
send(self(), :reload_shell)
Notify.reload()
socket
end
end

View File

@@ -12,6 +12,7 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor do
alias BDS.Desktop.ShellLive.SettingsEditor.AISettings
alias BDS.Desktop.ShellLive.SettingsEditor.EditorSettings
alias BDS.Desktop.ShellLive.SettingsEditor.ManagedCategories
alias BDS.Desktop.ShellLive.Notify
alias BDS.Desktop.ShellLive.SettingsEditor.MCPConfig
alias BDS.Desktop.ShellLive.SettingsEditor.ProjectSettings
alias BDS.Desktop.ShellLive.SettingsEditor.PublishingSettings
@@ -308,13 +309,13 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor do
defp append_output_callback do
fn socket, title, message, _details, level ->
send(self(), {:settings_output, title, message, level})
Notify.output(title, message, level)
socket
end
end
defp notify_parent(message) do
send(self(), message)
Notify.parent(message)
end
defp current_settings_section(assigns) do

View File

@@ -6,6 +6,7 @@ defmodule BDS.Desktop.ShellLive.TagsEditor do
import Ecto.Query
alias BDS.{Repo, Tags}
alias BDS.Desktop.ShellLive.Notify
alias BDS.Posts.Post
alias BDS.Tags.Tag
alias BDS.Templates.Template
@@ -292,11 +293,11 @@ defmodule BDS.Desktop.ShellLive.TagsEditor do
defp noreply(socket), do: {:noreply, socket}
defp notify_parent(message) do
send(self(), message)
Notify.parent(message)
end
defp notify_output(title, message, level) do
send(self(), {:tags_editor_output, title, message, level})
Notify.output(title, message, level)
end
@spec tag_font_size(term(), term()) :: term()

View File

@@ -4,6 +4,7 @@ defmodule BDS.Desktop.ShellLive.TemplateEditor do
use Phoenix.LiveComponent
alias BDS.{MCP, Templates}
alias BDS.Desktop.ShellLive.Notify
alias BDS.Templates.Template
use Gettext, backend: BDS.Gettext
@@ -182,7 +183,7 @@ defmodule BDS.Desktop.ShellLive.TemplateEditor do
case Templates.delete_template(template_id, force: true) do
{:ok, _deleted} ->
send(self(), {:close_tab, :templates, template_id})
Notify.close_tab(:templates, template_id)
socket
{:error, reason} ->
@@ -231,12 +232,12 @@ defmodule BDS.Desktop.ShellLive.TemplateEditor do
defp normalize_template_kind(_kind), do: :post
defp notify_output(socket, title, message, level \\ "info") do
send(self(), {:template_editor_output, title, message, level})
Notify.output(title, message, level)
socket
end
defp notify_reload(socket) do
send(self(), :reload_shell)
Notify.reload()
socket
end
end