348 lines
10 KiB
Elixir
348 lines
10 KiB
Elixir
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/*")
|
|
|
|
def assign_socket(socket) do
|
|
socket
|
|
|> assign(:script_editor, build_script(socket.assigns))
|
|
|> assign(:template_editor, build_template(socket.assigns))
|
|
end
|
|
|
|
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
|
|
|
|
def save_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.validate(draft["content"] || "") do
|
|
:ok ->
|
|
case Scripts.update_script(script.id, script_attrs(draft)) 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
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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
|
|
|
|
def save_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 ->
|
|
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)) 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
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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"]),
|
|
created_at: script.created_at,
|
|
updated_at: script.updated_at
|
|
}
|
|
end
|
|
end
|
|
|
|
def build_script(_assigns), do: nil
|
|
|
|
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"],
|
|
created_at: template.created_at,
|
|
updated_at: template.updated_at
|
|
}
|
|
end
|
|
end
|
|
|
|
def build_template(_assigns), do: nil
|
|
|
|
def translated(text, bindings \\ %{}),
|
|
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
|
|
|
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 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
|