diff --git a/lib/bds/desktop/shell_live.ex b/lib/bds/desktop/shell_live.ex index 1bbf505..2ee3e24 100644 --- a/lib/bds/desktop/shell_live.ex +++ b/lib/bds/desktop/shell_live.ex @@ -11,13 +11,14 @@ defmodule BDS.Desktop.ShellLive do alias BDS.Desktop.ShellLive.{ ChatEditor, - CodeEntityEditor, ImportEditor, MediaEditor, MenuEditor, MiscEditor, + ScriptEditor, SettingsEditor, - TagsEditor + TagsEditor, + TemplateEditor } alias BDS.Desktop.ShellLive.OverlayComponents, as: ShellOverlayComponents @@ -175,8 +176,6 @@ defmodule BDS.Desktop.ShellLive do |> assign(:media_editor_post_picker_queries, %{}) |> assign(:media_editor_save_states, %{}) |> assign(:media_editor_translation_forms, %{}) - |> assign(:script_editor_drafts, %{}) - |> assign(:template_editor_drafts, %{}) |> assign(:chat_editor_inputs, %{}) |> assign(:chat_model_selectors_open, %{}) |> assign(:chat_editor_requests, %{}) @@ -527,53 +526,6 @@ defmodule BDS.Desktop.ShellLive do {:noreply, apply_shell_command(socket, action)} end - def handle_event("change_script_editor", %{"script_editor" => params}, socket) do - {:noreply, CodeEntityEditor.update_script(socket, params, &reload_shell/2)} - end - - def handle_event("save_script_editor", _params, socket) do - {:noreply, CodeEntityEditor.save_script(socket, &reload_shell/2, &append_output_entry/5)} - end - - def handle_event("publish_script_editor", %{"id" => _script_id}, socket) do - {:noreply, - CodeEntityEditor.publish_script(socket, &reload_shell/2, &append_output_entry/5)} - end - - def handle_event("run_script_editor", _params, socket) do - {:noreply, CodeEntityEditor.run_script(socket, &reload_shell/2, &append_output_entry/5)} - end - - def handle_event("check_script_editor", _params, socket) do - {:noreply, CodeEntityEditor.check_script(socket, &reload_shell/2, &append_output_entry/5)} - end - - def handle_event("delete_script_editor", _params, socket) do - {:noreply, CodeEntityEditor.delete_script(socket, &reload_shell/2, &append_output_entry/5)} - end - - def handle_event("change_template_editor", %{"template_editor" => params}, socket) do - {:noreply, CodeEntityEditor.update_template(socket, params, &reload_shell/2)} - end - - def handle_event("save_template_editor", _params, socket) do - {:noreply, CodeEntityEditor.save_template(socket, &reload_shell/2, &append_output_entry/5)} - end - - def handle_event("publish_template_editor", %{"id" => _template_id}, socket) do - {:noreply, - CodeEntityEditor.publish_template(socket, &reload_shell/2, &append_output_entry/5)} - end - - def handle_event("validate_template_editor", _params, socket) do - {:noreply, - CodeEntityEditor.validate_template(socket, &reload_shell/2, &append_output_entry/5)} - end - - def handle_event("delete_template_editor", _params, socket) do - {:noreply, CodeEntityEditor.delete_template(socket, &reload_shell/2, &append_output_entry/5)} - end - def handle_event("change_chat_editor_input", %{"message" => message}, socket) do {:noreply, ChatEditor.update_input(socket, message, &reload_shell/2)} end @@ -1312,6 +1264,22 @@ defmodule BDS.Desktop.ShellLive do {:noreply, append_output_entry(socket, title, message, nil, level)} end + def handle_info({:script_editor_output, title, message, level}, socket) do + {:noreply, append_output_entry(socket, title, message, nil, level)} + end + + def handle_info({:template_editor_output, title, message, level}, socket) do + {:noreply, append_output_entry(socket, title, message, nil, level)} + end + + def handle_info(:reload_shell, socket) do + {:noreply, reload_shell(socket, socket.assigns.workbench)} + end + + def handle_info({:close_tab, type, id}, socket) do + {:noreply, reload_shell(socket, BDS.UI.Workbench.close_tab(socket.assigns.workbench, type, id))} + end + @impl true def render(assigns) do UILocale.put(assigns.page_language) @@ -1382,7 +1350,6 @@ defmodule BDS.Desktop.ShellLive do |> assign(:current_tab, current_tab(workbench)) |> assign_post_editor() |> assign_media_editor() - |> assign_code_entity_editor() |> assign_chat_editor() |> assign_import_editor() |> assign_misc_editor() @@ -1435,10 +1402,6 @@ defmodule BDS.Desktop.ShellLive do MediaEditor.assign_socket(socket) end - defp assign_code_entity_editor(socket) do - CodeEntityEditor.assign_socket(socket) - end - defp assign_chat_editor(socket) do ChatEditor.assign_socket(socket) end @@ -1621,12 +1584,14 @@ defmodule BDS.Desktop.ShellLive do socket end - defp save_current_tab(%{assigns: %{current_tab: %{type: :scripts}}} = socket) do - CodeEntityEditor.save_script(socket, &reload_shell/2, &append_output_entry/5) + defp save_current_tab(%{assigns: %{current_tab: %{type: :scripts, id: script_id}}} = socket) do + send_update(ScriptEditor, id: "script-editor-#{script_id}", action: :save) + socket end - defp save_current_tab(%{assigns: %{current_tab: %{type: :templates}}} = socket) do - CodeEntityEditor.save_template(socket, &reload_shell/2, &append_output_entry/5) + defp save_current_tab(%{assigns: %{current_tab: %{type: :templates, id: template_id}}} = socket) do + send_update(TemplateEditor, id: "template-editor-#{template_id}", action: :save) + socket end defp save_current_tab(socket), do: reload_shell(socket, socket.assigns.workbench) @@ -1729,7 +1694,6 @@ defmodule BDS.Desktop.ShellLive do socket |> assign(:shell_overlay, nil) |> assign(:tab_meta, Map.delete(socket.assigns.tab_meta, {:scripts, script_id})) - |> assign(:script_editor_drafts, Map.delete(socket.assigns.script_editor_drafts, script_id)) |> reload_shell(workbench) {:error, reason} -> @@ -1748,10 +1712,6 @@ defmodule BDS.Desktop.ShellLive do socket |> assign(:shell_overlay, nil) |> assign(:tab_meta, Map.delete(socket.assigns.tab_meta, {:templates, template_id})) - |> assign( - :template_editor_drafts, - Map.delete(socket.assigns.template_editor_drafts, template_id) - ) |> reload_shell(workbench) {:error, reason} -> diff --git a/lib/bds/desktop/shell_live/code_entity_editor.ex b/lib/bds/desktop/shell_live/code_entity_editor.ex index e48847e..e69de29 100644 --- a/lib/bds/desktop/shell_live/code_entity_editor.ex +++ b/lib/bds/desktop/shell_live/code_entity_editor.ex @@ -1,397 +0,0 @@ -defmodule BDS.Desktop.ShellLive.CodeEntityEditor do - @moduledoc false - - use Phoenix.Component - - alias BDS.Desktop.ShellData - alias BDS.{MCP, Scripts, Scripting, Templates} - alias BDS.Scripts.Script - alias BDS.Templates.Template - - embed_templates("code_entity_editor_html/*") - - @spec assign_socket(term()) :: term() - def assign_socket(socket) do - socket - |> assign(:script_editor, build_script(socket.assigns)) - |> assign(:template_editor, build_template(socket.assigns)) - end - - @spec update_script(term(), term(), term()) :: term() - def update_script(socket, params, reload) do - %{id: script_id} = socket.assigns.current_tab - - socket - |> assign( - :script_editor_drafts, - Map.put(socket.assigns.script_editor_drafts, script_id, normalize_script_params(params)) - ) - |> reload.(socket.assigns.workbench) - end - - @spec save_script(term(), term(), term()) :: term() - def save_script(socket, reload, append_output) do - persist_script(socket, :save, reload, append_output) - end - - @spec publish_script(term(), term(), term()) :: term() - def publish_script(socket, reload, append_output) do - persist_script(socket, :publish, reload, append_output) - end - - @spec check_script(term(), term(), term()) :: term() - def check_script(socket, reload, append_output) do - %{id: script_id} = socket.assigns.current_tab - - case Scripts.get_script(script_id) do - nil -> - reload.(socket, socket.assigns.workbench) - - %Script{} = script -> - case Scripting.validate(current_script_draft(socket.assigns, script)["content"] || "") do - :ok -> - append_output.(socket, translated("Scripts"), translated("Syntax is valid")) - |> reload.(socket.assigns.workbench) - - {:error, reason} -> - append_output.(socket, translated("Scripts"), inspect(reason), nil, "error") - |> reload.(socket.assigns.workbench) - end - end - end - - @spec run_script(term(), term(), term()) :: term() - def run_script(socket, reload, append_output) do - %{id: script_id} = socket.assigns.current_tab - - case Scripts.get_script(script_id) do - nil -> - reload.(socket, socket.assigns.workbench) - - %Script{} = script -> - draft = current_script_draft(socket.assigns, script) - - case Scripting.execute_project_script( - script.project_id, - draft["content"] || "", - draft["entrypoint"] || "main", - [] - ) do - {:ok, result} -> - socket - |> append_output.(translated("Scripts"), inspect(result)) - |> reload.(socket.assigns.workbench) - - {:error, reason} -> - socket - |> append_output.(translated("Scripts"), inspect(reason), nil, "error") - |> reload.(socket.assigns.workbench) - end - end - end - - @spec delete_script(term(), term(), term()) :: term() - def delete_script(socket, reload, append_output) do - %{id: script_id} = socket.assigns.current_tab - - case Scripts.delete_script(script_id) do - {:ok, _deleted} -> - reload.(socket, BDS.UI.Workbench.close_tab(socket.assigns.workbench, :scripts, script_id)) - - {:error, reason} -> - append_output.(socket, translated("Scripts"), inspect(reason), nil, "error") - |> reload.(socket.assigns.workbench) - end - end - - @spec update_template(term(), term(), term()) :: term() - def update_template(socket, params, reload) do - %{id: template_id} = socket.assigns.current_tab - - socket - |> assign( - :template_editor_drafts, - Map.put( - socket.assigns.template_editor_drafts, - template_id, - normalize_template_params(params) - ) - ) - |> reload.(socket.assigns.workbench) - end - - @spec save_template(term(), term(), term()) :: term() - def save_template(socket, reload, append_output) do - persist_template(socket, :save, reload, append_output) - end - - @spec publish_template(term(), term(), term()) :: term() - def publish_template(socket, reload, append_output) do - persist_template(socket, :publish, reload, append_output) - end - - @spec validate_template(term(), term(), term()) :: term() - def validate_template(socket, reload, append_output) do - %{id: template_id} = socket.assigns.current_tab - - case Templates.get_template(template_id) do - nil -> - reload.(socket, socket.assigns.workbench) - - %Template{} = template -> - case MCP.validate_template( - current_template_draft(socket.assigns, template)["content"] || "" - ) do - {:ok, %{valid: true}} -> - append_output.( - socket, - translated("Templates"), - translated("Template syntax is valid") - ) - |> reload.(socket.assigns.workbench) - - {:ok, %{valid: false, errors: errors}} -> - append_output.(socket, translated("Templates"), Enum.join(errors, "; "), nil, "error") - |> reload.(socket.assigns.workbench) - end - end - end - - @spec delete_template(term(), term(), term()) :: term() - def delete_template(socket, reload, append_output) do - %{id: template_id} = socket.assigns.current_tab - - case Templates.delete_template(template_id, force: true) do - {:ok, _deleted} -> - reload.( - socket, - BDS.UI.Workbench.close_tab(socket.assigns.workbench, :templates, template_id) - ) - - {:error, reason} -> - append_output.(socket, translated("Templates"), inspect(reason), nil, "error") - |> reload.(socket.assigns.workbench) - end - end - - @spec build_script(term()) :: term() - def build_script(%{current_tab: %{type: :scripts, id: script_id}} = assigns) do - case Scripts.get_script(script_id) do - nil -> - nil - - %Script{} = script -> - draft = current_script_draft(assigns, script) - - %{ - id: script.id, - title: draft["title"], - slug: draft["slug"], - kind: draft["kind"], - entrypoint: draft["entrypoint"], - enabled: draft["enabled"], - content: draft["content"], - entrypoints: discover_entrypoints(draft["content"]), - status: script.status || :draft, - can_publish?: script.status == :draft, - created_at: script.created_at, - updated_at: script.updated_at - } - end - end - - def build_script(_assigns), do: nil - - @spec build_template(term()) :: term() - def build_template(%{current_tab: %{type: :templates, id: template_id}} = assigns) do - case Templates.get_template(template_id) do - nil -> - nil - - %Template{} = template -> - draft = current_template_draft(assigns, template) - - %{ - id: template.id, - title: draft["title"], - slug: draft["slug"], - kind: draft["kind"], - enabled: draft["enabled"], - content: draft["content"], - status: template.status || :draft, - can_publish?: template.status == :draft, - created_at: template.created_at, - updated_at: template.updated_at - } - end - end - - def build_template(_assigns), do: nil - - @spec status_label(term()) :: term() - def status_label(status), do: ShellData.dashboard_status_label(status) - - @spec translated(term(), term()) :: term() - def translated(text, bindings \\ %{}), - do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current()) - - @spec format_timestamp(term()) :: term() - def format_timestamp(nil), do: "" - def format_timestamp(timestamp), do: BDS.Persistence.timestamp_to_iso8601(timestamp) - - defp normalize_script_params(params) do - %{ - "title" => Map.get(params, "title", ""), - "slug" => Map.get(params, "slug", ""), - "kind" => Map.get(params, "kind", "utility"), - "entrypoint" => Map.get(params, "entrypoint", "main"), - "enabled" => Map.get(params, "enabled") in [true, "true", "on", "1", 1], - "content" => Map.get(params, "content", "") - } - end - - defp normalize_template_params(params) do - %{ - "title" => Map.get(params, "title", ""), - "slug" => Map.get(params, "slug", ""), - "kind" => Map.get(params, "kind", "post"), - "enabled" => Map.get(params, "enabled") in [true, "true", "on", "1", 1], - "content" => Map.get(params, "content", "") - } - end - - defp current_script_draft(assigns, %Script{} = script) do - Map.get(assigns.script_editor_drafts, script.id, %{ - "title" => script.title || "", - "slug" => script.slug || "", - "kind" => to_string(script.kind || :utility), - "entrypoint" => script.entrypoint || "main", - "enabled" => script.enabled != false, - "content" => script.content || "" - }) - end - - defp current_template_draft(assigns, %Template{} = template) do - Map.get(assigns.template_editor_drafts, template.id, %{ - "title" => template.title || "", - "slug" => template.slug || "", - "kind" => to_string(template.kind || :post), - "enabled" => template.enabled != false, - "content" => template.content || "" - }) - end - - defp script_attrs(draft) do - %{ - title: draft["title"], - slug: draft["slug"], - kind: BDS.BoundedAtoms.script_kind(draft["kind"], :utility), - entrypoint: draft["entrypoint"], - enabled: draft["enabled"], - content: draft["content"] - } - end - - defp template_attrs(draft) do - %{ - title: draft["title"], - slug: draft["slug"], - kind: normalize_template_kind(draft["kind"]), - enabled: draft["enabled"], - content: draft["content"] - } - end - - defp persist_script(socket, action, reload, append_output) do - %{id: script_id} = socket.assigns.current_tab - - case Scripts.get_script(script_id) do - nil -> - reload.(socket, socket.assigns.workbench) - - %Script{} = script -> - draft = current_script_draft(socket.assigns, script) - - case Scripting.validate(draft["content"] || "") do - :ok -> - case Scripts.update_script(script.id, script_attrs(draft)) - |> maybe_publish_script(script.id, action) do - {:ok, _updated} -> - socket - |> assign( - :script_editor_drafts, - Map.delete(socket.assigns.script_editor_drafts, script.id) - ) - |> reload.(socket.assigns.workbench) - - {:error, reason} -> - socket - |> append_output.(translated("Scripts"), inspect(reason), nil, "error") - |> reload.(socket.assigns.workbench) - end - - {:error, reason} -> - socket - |> append_output.(translated("Scripts"), inspect(reason), nil, "error") - |> reload.(socket.assigns.workbench) - end - end - end - - defp persist_template(socket, action, reload, append_output) do - %{id: template_id} = socket.assigns.current_tab - - case Templates.get_template(template_id) do - nil -> - reload.(socket, socket.assigns.workbench) - - %Template{} = template -> - draft = current_template_draft(socket.assigns, template) - - with {:ok, %{valid: true}} <- MCP.validate_template(draft["content"] || ""), - {:ok, _updated} <- - Templates.update_template(template.id, template_attrs(draft)) - |> maybe_publish_template(template.id, action) do - socket - |> assign( - :template_editor_drafts, - Map.delete(socket.assigns.template_editor_drafts, template.id) - ) - |> reload.(socket.assigns.workbench) - else - {:ok, %{valid: false, errors: errors}} -> - append_output.(socket, translated("Templates"), Enum.join(errors, "; "), nil, "error") - |> reload.(socket.assigns.workbench) - - {:error, reason} -> - append_output.(socket, translated("Templates"), inspect(reason), nil, "error") - |> reload.(socket.assigns.workbench) - end - end - end - - defp maybe_publish_script({:ok, _script}, script_id, :publish), do: Scripts.publish_script(script_id) - defp maybe_publish_script(result, _script_id, _action), do: result - - defp maybe_publish_template({:ok, _template}, template_id, :publish), - do: Templates.publish_template(template_id) - - defp maybe_publish_template(result, _template_id, _action), do: result - - defp normalize_template_kind("post"), do: :post - defp normalize_template_kind("list"), do: :list - defp normalize_template_kind("not-found"), do: :"not-found" - defp normalize_template_kind("partial"), do: :partial - defp normalize_template_kind(_kind), do: :post - - defp discover_entrypoints(content) do - [ - "main" - | Regex.scan(~r/function\s+([A-Za-z_][A-Za-z0-9_]*)\s*\(/, content || "", - capture: :all_but_first - ) - |> List.flatten() - |> Enum.reject(&(&1 == "main")) - ] - end -end diff --git a/lib/bds/desktop/shell_live/code_entity_editor_html/script_editor.html.heex b/lib/bds/desktop/shell_live/code_entity_editor_html/script_editor.html.heex deleted file mode 100644 index 7beb855..0000000 --- a/lib/bds/desktop/shell_live/code_entity_editor_html/script_editor.html.heex +++ /dev/null @@ -1,49 +0,0 @@ -