chore: converted preferences editor to live component
This commit is contained in:
@@ -175,13 +175,6 @@ defmodule BDS.Desktop.ShellLive do
|
||||
|> assign(:media_editor_post_picker_queries, %{})
|
||||
|> assign(:media_editor_save_states, %{})
|
||||
|> assign(:media_editor_translation_forms, %{})
|
||||
|> assign(:settings_editor_search, "")
|
||||
|> assign(:settings_editor_project_draft, %{})
|
||||
|> assign(:settings_editor_endpoint_models, %{})
|
||||
|> assign(:settings_editor_publishing_draft, %{})
|
||||
|> assign(:settings_editor_new_category, "")
|
||||
|> assign(:style_editor_theme, nil)
|
||||
|> assign(:style_editor_preview_mode, "auto")
|
||||
|> assign(:script_editor_drafts, %{})
|
||||
|> assign(:template_editor_drafts, %{})
|
||||
|> assign(:chat_editor_inputs, %{})
|
||||
@@ -530,109 +523,10 @@ defmodule BDS.Desktop.ShellLive do
|
||||
end
|
||||
end
|
||||
|
||||
def handle_event("change_settings_search", %{"query" => query}, socket) do
|
||||
{:noreply, SettingsEditor.update_search(socket, query, &reload_shell/2)}
|
||||
end
|
||||
|
||||
def handle_event("change_settings_project", %{"settings_project" => params}, socket) do
|
||||
{:noreply, SettingsEditor.update_project_draft(socket, params, &reload_shell/2)}
|
||||
end
|
||||
|
||||
def handle_event("change_settings_editor", %{"settings_editor" => params}, socket) do
|
||||
{:noreply, SettingsEditor.update_editor_draft(socket, params, &reload_shell/2)}
|
||||
end
|
||||
|
||||
def handle_event("save_settings_editor", _params, socket) do
|
||||
{:noreply, SettingsEditor.save_editor(socket, &reload_shell/2, &append_output_entry/5)}
|
||||
end
|
||||
|
||||
def handle_event("save_settings_project", _params, socket) do
|
||||
{:noreply, SettingsEditor.save_project(socket, &reload_shell/2, &append_output_entry/5)}
|
||||
end
|
||||
|
||||
def handle_event("change_settings_publishing", %{"settings_publishing" => params}, socket) do
|
||||
{:noreply, SettingsEditor.update_publishing_draft(socket, params, &reload_shell/2)}
|
||||
end
|
||||
|
||||
def handle_event("change_settings_ai", %{"settings_ai" => params}, socket) do
|
||||
{:noreply, SettingsEditor.update_ai_draft(socket, params, &reload_shell/2)}
|
||||
end
|
||||
|
||||
def handle_event("refresh_settings_ai_models", %{"endpoint" => endpoint}, socket) do
|
||||
case BoundedAtoms.ai_endpoint(endpoint) do
|
||||
nil ->
|
||||
{:noreply, reload_shell(socket, socket.assigns.workbench)}
|
||||
|
||||
endpoint_key ->
|
||||
{:noreply,
|
||||
SettingsEditor.refresh_ai_models(
|
||||
socket,
|
||||
endpoint_key,
|
||||
&reload_shell/2,
|
||||
&append_output_entry/5
|
||||
)}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_event("save_settings_ai", _params, socket) do
|
||||
{:noreply, SettingsEditor.save_ai(socket, &reload_shell/2, &append_output_entry/5)}
|
||||
end
|
||||
|
||||
def handle_event("reset_settings_ai_prompt", _params, socket) do
|
||||
{:noreply, SettingsEditor.reset_ai_prompt(socket, &reload_shell/2, &append_output_entry/5)}
|
||||
end
|
||||
|
||||
def handle_event("save_settings_publishing", _params, socket) do
|
||||
{:noreply, SettingsEditor.save_publishing(socket, &reload_shell/2, &append_output_entry/5)}
|
||||
end
|
||||
|
||||
def handle_event("clear_settings_publishing", _params, socket) do
|
||||
{:noreply, SettingsEditor.clear_publishing(socket, &reload_shell/2, &append_output_entry/5)}
|
||||
end
|
||||
|
||||
def handle_event("change_settings_new_category", %{"name" => name}, socket) do
|
||||
{:noreply, SettingsEditor.update_new_category(socket, name, &reload_shell/2)}
|
||||
end
|
||||
|
||||
def handle_event("add_settings_category", _params, socket) do
|
||||
{:noreply, SettingsEditor.add_category(socket, &reload_shell/2, &append_output_entry/5)}
|
||||
end
|
||||
|
||||
def handle_event("reset_settings_categories", _params, socket) do
|
||||
{:noreply, SettingsEditor.reset_categories(socket, &reload_shell/2, &append_output_entry/5)}
|
||||
end
|
||||
|
||||
def handle_event("save_settings_category", %{"category_settings" => params}, socket) do
|
||||
{:noreply,
|
||||
SettingsEditor.save_category(socket, params, &reload_shell/2, &append_output_entry/5)}
|
||||
end
|
||||
|
||||
def handle_event("remove_settings_category", %{"category" => category}, socket) do
|
||||
{:noreply,
|
||||
SettingsEditor.remove_category(socket, category, &reload_shell/2, &append_output_entry/5)}
|
||||
end
|
||||
|
||||
def handle_event("settings_shell_command", %{"action" => action}, socket) do
|
||||
{:noreply, apply_shell_command(socket, action)}
|
||||
end
|
||||
|
||||
def handle_event("toggle_settings_mcp_agent", %{"agent" => agent}, socket) do
|
||||
{:noreply,
|
||||
SettingsEditor.toggle_mcp_agent(socket, agent, &reload_shell/2, &append_output_entry/5)}
|
||||
end
|
||||
|
||||
def handle_event("select_style_theme", %{"theme" => theme}, socket) do
|
||||
{:noreply, SettingsEditor.select_style_theme(socket, theme, &reload_shell/2)}
|
||||
end
|
||||
|
||||
def handle_event("change_style_preview_mode", %{"mode" => mode}, socket) do
|
||||
{:noreply, SettingsEditor.change_style_preview_mode(socket, mode, &reload_shell/2)}
|
||||
end
|
||||
|
||||
def handle_event("apply_style_theme", _params, socket) do
|
||||
{:noreply, SettingsEditor.apply_style_theme(socket, &reload_shell/2, &append_output_entry/5)}
|
||||
end
|
||||
|
||||
def handle_event("menu_editor_select_item", %{"item_id" => item_id}, socket) do
|
||||
{:noreply, MenuEditor.select_item(socket, item_id, &reload_shell/2)}
|
||||
end
|
||||
@@ -1451,6 +1345,14 @@ defmodule BDS.Desktop.ShellLive do
|
||||
{:noreply, reload_shell(socket, socket.assigns.workbench)}
|
||||
end
|
||||
|
||||
def handle_info({:settings_output, title, message, level}, socket) do
|
||||
{:noreply, append_output_entry(socket, title, message, nil, level)}
|
||||
end
|
||||
|
||||
def handle_info(:settings_changed, socket) do
|
||||
{:noreply, reload_shell(socket, socket.assigns.workbench)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def render(assigns) do
|
||||
UILocale.put(assigns.page_language)
|
||||
@@ -1521,7 +1423,6 @@ defmodule BDS.Desktop.ShellLive do
|
||||
|> assign(:current_tab, current_tab(workbench))
|
||||
|> assign_post_editor()
|
||||
|> assign_media_editor()
|
||||
|> assign_settings_editor()
|
||||
|> assign_menu_editor()
|
||||
|> assign_code_entity_editor()
|
||||
|> assign_chat_editor()
|
||||
@@ -1576,10 +1477,6 @@ defmodule BDS.Desktop.ShellLive do
|
||||
MediaEditor.assign_socket(socket)
|
||||
end
|
||||
|
||||
defp assign_settings_editor(socket) do
|
||||
SettingsEditor.assign_socket(socket)
|
||||
end
|
||||
|
||||
defp assign_menu_editor(socket) do
|
||||
MenuEditor.assign_socket(socket)
|
||||
end
|
||||
@@ -1756,7 +1653,8 @@ defmodule BDS.Desktop.ShellLive do
|
||||
end
|
||||
|
||||
defp save_current_tab(%{assigns: %{current_tab: %{type: :settings}}} = socket) do
|
||||
SettingsEditor.save_project(socket, &reload_shell/2, &append_output_entry/5)
|
||||
send_update(SettingsEditor, id: "settings-editor", action: :save_project)
|
||||
socket
|
||||
end
|
||||
|
||||
defp save_current_tab(%{assigns: %{current_tab: %{type: :menu_editor}}} = socket) do
|
||||
|
||||
@@ -388,11 +388,16 @@
|
||||
<% @current_tab.type == :media and @media_editor -> %>
|
||||
<MediaEditor.media_editor media_editor={@media_editor} />
|
||||
|
||||
<% @current_tab.type == :settings and @settings_editor -> %>
|
||||
<SettingsEditor.settings_editor settings_editor={@settings_editor} />
|
||||
|
||||
<% @current_tab.type == :style and @style_editor -> %>
|
||||
<SettingsEditor.style_editor style_editor={@style_editor} />
|
||||
<% @current_tab.type in [:settings, :style] and @current_project -> %>
|
||||
<.live_component module={SettingsEditor} id="settings-editor"
|
||||
project_id={@current_project.id}
|
||||
current_project={@current_project}
|
||||
projects={@projects}
|
||||
workbench={@workbench}
|
||||
current_tab={@current_tab}
|
||||
tab_meta={@tab_meta}
|
||||
offline_mode={@offline_mode}
|
||||
/>
|
||||
|
||||
<% @current_tab.type == :menu_editor and @menu_editor -> %>
|
||||
<MenuEditor.menu_editor menu_editor={@menu_editor} />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
defmodule BDS.Desktop.ShellLive.SettingsEditor do
|
||||
@moduledoc false
|
||||
|
||||
use Phoenix.Component
|
||||
use Phoenix.LiveComponent
|
||||
|
||||
import Ecto.Query
|
||||
|
||||
@@ -22,57 +22,188 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor do
|
||||
@settings_sections ~w(project editor content ai technology publishing data mcp)
|
||||
@supported_languages ["en", "de", "fr", "it", "es"]
|
||||
|
||||
defdelegate update_project_draft(socket, params, reload), to: ProjectSettings
|
||||
defdelegate save_project(socket, reload, append_output), to: ProjectSettings
|
||||
defdelegate update_editor_draft(socket, params, reload), to: EditorSettings
|
||||
defdelegate save_editor(socket, reload, append_output), to: EditorSettings
|
||||
defdelegate update_publishing_draft(socket, params, reload), to: PublishingSettings
|
||||
defdelegate save_publishing(socket, reload, append_output), to: PublishingSettings
|
||||
defdelegate clear_publishing(socket, reload, append_output), to: PublishingSettings
|
||||
defdelegate update_ai_draft(socket, params, reload), to: AISettings
|
||||
defdelegate refresh_ai_models(socket, endpoint_key, reload, append_output), to: AISettings
|
||||
defdelegate save_ai(socket, reload, append_output), to: AISettings
|
||||
defdelegate reset_ai_prompt(socket, reload, append_output), to: AISettings
|
||||
defdelegate update_new_category(socket, name, reload), to: ManagedCategories
|
||||
defdelegate add_category(socket, reload, append_output), to: ManagedCategories
|
||||
defdelegate reset_categories(socket, reload, append_output), to: ManagedCategories
|
||||
defdelegate save_category(socket, params, reload, append_output), to: ManagedCategories
|
||||
defdelegate remove_category(socket, category, reload, append_output), to: ManagedCategories
|
||||
defdelegate toggle_mcp_agent(socket, agent, reload, append_output), to: MCPConfig
|
||||
defdelegate select_style_theme(socket, theme, reload), to: StyleEditor
|
||||
defdelegate change_style_preview_mode(socket, mode, reload), to: StyleEditor
|
||||
defdelegate apply_style_theme(socket, reload, append_output), to: StyleEditor
|
||||
defdelegate theme_display_name(theme), to: StyleEditor
|
||||
defdelegate protected_category?(category), to: ManagedCategories
|
||||
|
||||
@spec assign_socket(term()) :: term()
|
||||
def assign_socket(socket) do
|
||||
case socket.assigns[:current_tab] do
|
||||
%{type: :settings} ->
|
||||
socket
|
||||
|> assign(:settings_editor, build_settings(socket.assigns))
|
||||
|> assign(:style_editor, nil)
|
||||
@spec update(map(), Phoenix.LiveView.Socket.t()) :: {:ok, Phoenix.LiveView.Socket.t()}
|
||||
@impl true
|
||||
def update(%{action: :save_project} = assigns, socket) do
|
||||
socket = assign(socket, Map.drop(assigns, [:action]))
|
||||
socket = ProjectSettings.save_project(socket, reload_callback(), append_output_callback())
|
||||
{:ok, socket}
|
||||
end
|
||||
|
||||
%{type: :style} ->
|
||||
def update(assigns, socket) do
|
||||
socket =
|
||||
socket
|
||||
|> assign(:settings_editor, nil)
|
||||
|> assign(:style_editor, StyleEditor.build_style(socket.assigns))
|
||||
|> assign(assigns)
|
||||
|> initialize_state()
|
||||
|> build_data()
|
||||
|
||||
_other ->
|
||||
socket
|
||||
|> assign(:settings_editor, nil)
|
||||
|> assign(:style_editor, nil)
|
||||
{:ok, socket}
|
||||
end
|
||||
|
||||
@spec render(map()) :: Phoenix.LiveView.Rendered.t()
|
||||
@impl true
|
||||
def render(assigns) do
|
||||
case assigns.current_tab do
|
||||
%{type: :settings} -> settings_editor(assigns)
|
||||
%{type: :style} -> style_editor(assigns)
|
||||
_other -> ~H""
|
||||
end
|
||||
end
|
||||
|
||||
@spec update_search(term(), term(), term()) :: term()
|
||||
def update_search(socket, query, reload) do
|
||||
@spec handle_event(String.t(), map(), Phoenix.LiveView.Socket.t()) ::
|
||||
{:noreply, Phoenix.LiveView.Socket.t()}
|
||||
@impl true
|
||||
def handle_event("change_settings_search", %{"query" => query}, socket) do
|
||||
socket =
|
||||
socket
|
||||
|> assign(:settings_editor_search, to_string(query || ""))
|
||||
|> reload.(socket.assigns.workbench)
|
||||
|> build_data()
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
@spec build_settings(term()) :: term()
|
||||
def handle_event("change_settings_project", %{"settings_project" => params}, socket) do
|
||||
{:noreply, ProjectSettings.update_project_draft(socket, params, reload_callback())}
|
||||
end
|
||||
|
||||
def handle_event("change_settings_editor", %{"settings_editor" => params}, socket) do
|
||||
{:noreply, EditorSettings.update_editor_draft(socket, params, reload_callback())}
|
||||
end
|
||||
|
||||
def handle_event("save_settings_editor", _params, socket) do
|
||||
{:noreply, EditorSettings.save_editor(socket, reload_callback(), append_output_callback())}
|
||||
end
|
||||
|
||||
def handle_event("save_settings_project", _params, socket) do
|
||||
socket = ProjectSettings.save_project(socket, reload_callback(), append_output_callback())
|
||||
notify_parent(:settings_changed)
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_event("change_settings_publishing", %{"settings_publishing" => params}, socket) do
|
||||
{:noreply, PublishingSettings.update_publishing_draft(socket, params, reload_callback())}
|
||||
end
|
||||
|
||||
def handle_event("change_settings_ai", %{"settings_ai" => params}, socket) do
|
||||
{:noreply, AISettings.update_ai_draft(socket, params, reload_callback())}
|
||||
end
|
||||
|
||||
def handle_event("refresh_settings_ai_models", %{"endpoint" => endpoint}, socket) do
|
||||
case BDS.BoundedAtoms.ai_endpoint(endpoint) do
|
||||
nil ->
|
||||
{:noreply, build_data(socket)}
|
||||
|
||||
endpoint_key ->
|
||||
{:noreply,
|
||||
AISettings.refresh_ai_models(
|
||||
socket,
|
||||
endpoint_key,
|
||||
reload_callback(),
|
||||
append_output_callback()
|
||||
)}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_event("save_settings_ai", _params, socket) do
|
||||
socket = AISettings.save_ai(socket, reload_callback(), append_output_callback())
|
||||
notify_parent(:settings_changed)
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_event("reset_settings_ai_prompt", _params, socket) do
|
||||
{:noreply, AISettings.reset_ai_prompt(socket, reload_callback(), append_output_callback())}
|
||||
end
|
||||
|
||||
def handle_event("save_settings_publishing", _params, socket) do
|
||||
socket = PublishingSettings.save_publishing(socket, reload_callback(), append_output_callback())
|
||||
notify_parent(:settings_changed)
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_event("clear_settings_publishing", _params, socket) do
|
||||
{:noreply, PublishingSettings.clear_publishing(socket, reload_callback(), append_output_callback())}
|
||||
end
|
||||
|
||||
def handle_event("change_settings_new_category", %{"name" => name}, socket) do
|
||||
{:noreply, ManagedCategories.update_new_category(socket, name, reload_callback())}
|
||||
end
|
||||
|
||||
def handle_event("add_settings_category", _params, socket) do
|
||||
socket = ManagedCategories.add_category(socket, reload_callback(), append_output_callback())
|
||||
notify_parent(:settings_changed)
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_event("reset_settings_categories", _params, socket) do
|
||||
socket = ManagedCategories.reset_categories(socket, reload_callback(), append_output_callback())
|
||||
notify_parent(:settings_changed)
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_event("save_settings_category", %{"category_settings" => params}, socket) do
|
||||
socket = ManagedCategories.save_category(socket, params, reload_callback(), append_output_callback())
|
||||
notify_parent(:settings_changed)
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_event("remove_settings_category", %{"category" => category}, socket) do
|
||||
socket = ManagedCategories.remove_category(socket, category, reload_callback(), append_output_callback())
|
||||
notify_parent(:settings_changed)
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_event("toggle_settings_mcp_agent", %{"agent" => agent}, socket) do
|
||||
socket = MCPConfig.toggle_mcp_agent(socket, agent, reload_callback(), append_output_callback())
|
||||
notify_parent(:settings_changed)
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_event("select_style_theme", %{"theme" => theme}, socket) do
|
||||
{:noreply, StyleEditor.select_style_theme(socket, theme, reload_callback())}
|
||||
end
|
||||
|
||||
def handle_event("change_style_preview_mode", %{"mode" => mode}, socket) do
|
||||
{:noreply, StyleEditor.change_style_preview_mode(socket, mode, reload_callback())}
|
||||
end
|
||||
|
||||
def handle_event("apply_style_theme", _params, socket) do
|
||||
socket = StyleEditor.apply_style_theme(socket, reload_callback(), append_output_callback())
|
||||
notify_parent(:settings_changed)
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
defp initialize_state(socket) do
|
||||
defaults = %{
|
||||
settings_editor_search: "",
|
||||
settings_editor_project_draft: %{},
|
||||
settings_editor_editor_draft: %{},
|
||||
settings_editor_ai_draft: %{},
|
||||
settings_editor_publishing_draft: %{},
|
||||
settings_editor_new_category: "",
|
||||
settings_editor_endpoint_models: %{},
|
||||
style_editor_theme: nil,
|
||||
style_editor_preview_mode: "auto"
|
||||
}
|
||||
|
||||
Enum.reduce(defaults, socket, fn {key, default}, acc ->
|
||||
if is_nil(Map.get(acc.assigns, key)) do
|
||||
assign(acc, key, default)
|
||||
else
|
||||
acc
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
defp build_data(socket) do
|
||||
socket
|
||||
|> assign(:settings_editor, build_settings(socket.assigns))
|
||||
|> assign(:style_editor, build_style(socket.assigns))
|
||||
end
|
||||
|
||||
@spec build_settings(map()) :: term()
|
||||
def build_settings(%{projects: %{active_project_id: nil}}), do: nil
|
||||
|
||||
def build_settings(assigns) do
|
||||
@@ -148,9 +279,27 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor do
|
||||
}
|
||||
end
|
||||
|
||||
@spec translated(term(), term()) :: term()
|
||||
def translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
@spec build_style(map()) :: term()
|
||||
def build_style(%{projects: %{active_project_id: nil}}), do: nil
|
||||
|
||||
def build_style(assigns) do
|
||||
StyleEditor.build_style(assigns)
|
||||
end
|
||||
|
||||
defp reload_callback do
|
||||
fn socket, _workbench -> build_data(socket) end
|
||||
end
|
||||
|
||||
defp append_output_callback do
|
||||
fn socket, title, message, _details, level ->
|
||||
send(self(), {:settings_output, title, message, level})
|
||||
socket
|
||||
end
|
||||
end
|
||||
|
||||
defp notify_parent(message) do
|
||||
send(self(), message)
|
||||
end
|
||||
|
||||
defp current_settings_section(assigns) do
|
||||
meta = current_tab_meta(assigns)
|
||||
@@ -236,4 +385,8 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor do
|
||||
|
||||
defp section_matches?(query, keywords),
|
||||
do: Enum.any?(keywords, &String.contains?(&1, String.downcase(query)))
|
||||
|
||||
@spec translated(term(), term()) :: term()
|
||||
def translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
end
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<div class="settings-view">
|
||||
<div class="settings-header">
|
||||
<h2 data-testid="editor-title"><%= translated("Settings") %></h2>
|
||||
<form class="settings-search" phx-change="change_settings_search">
|
||||
<form class="settings-search" phx-change="change_settings_search" phx-target={@myself}>
|
||||
<input type="text" name="query" value={@settings_editor.search_query} placeholder={translated("Search settings")} />
|
||||
</form>
|
||||
</div>
|
||||
@@ -27,7 +27,7 @@
|
||||
<h3><%= translated("Project") %></h3>
|
||||
<p class="setting-section-description"><%= translated("Blog identity, URLs, authoring defaults, and bookmarklet setup") %></p>
|
||||
</div>
|
||||
<form class="setting-section-content" phx-change="change_settings_project">
|
||||
<form class="setting-section-content" phx-change="change_settings_project" phx-target={@myself}>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info">
|
||||
<label class="setting-label"><%= translated("Project Name") %></label>
|
||||
@@ -97,7 +97,7 @@
|
||||
<div class="setting-control"><p class="setting-description"><%= translated("Bookmarklet copy support is wired through the desktop runtime and project public URL.") %></p></div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="setting-actions"><button class="primary" type="button" phx-click="save_settings_project"><%= translated("Save") %></button></div>
|
||||
<div class="setting-actions"><button class="primary" type="button" phx-click="save_settings_project" phx-target={@myself}><%= translated("Save") %></button></div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
<h3><%= translated("Editor") %></h3>
|
||||
<p class="setting-section-description"><%= translated("Default editing mode and diff presentation") %></p>
|
||||
</div>
|
||||
<form class="setting-section-content" phx-change="change_settings_editor">
|
||||
<form class="setting-section-content" phx-change="change_settings_editor" phx-target={@myself}>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Default Editor Mode") %></label></div>
|
||||
<div class="setting-control">
|
||||
@@ -136,7 +136,7 @@
|
||||
<div class="setting-control"><label><input type="checkbox" name="settings_editor[hide_unchanged_regions]" checked={@settings_editor.editor["hide_unchanged_regions"]} /> <%= translated("Collapse unchanged diff hunks") %></label></div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="setting-actions"><button class="primary" type="button" phx-click="save_settings_editor"><%= translated("Save") %></button></div>
|
||||
<div class="setting-actions"><button class="primary" type="button" phx-click="save_settings_editor" phx-target={@myself}><%= translated("Save") %></button></div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
@@ -183,11 +183,11 @@
|
||||
</td>
|
||||
<td>
|
||||
<div class="setting-input-group">
|
||||
<form id={"category-form-#{category.name}"} phx-submit="save_settings_category">
|
||||
<form id={"category-form-#{category.name}"} phx-submit="save_settings_category" phx-target={@myself}>
|
||||
<input type="hidden" name="category_settings[category]" value={category.name} />
|
||||
</form>
|
||||
<button class="secondary" type="submit" form={"category-form-#{category.name}"}><%= translated("Save") %></button>
|
||||
<button class="secondary" type="button" phx-click="remove_settings_category" phx-value-category={category.name} disabled={category.protected?}><%= translated("Remove") %></button>
|
||||
<button class="secondary" type="button" phx-click="remove_settings_category" phx-target={@myself} phx-value-category={category.name} disabled={category.protected?}><%= translated("Remove") %></button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -198,12 +198,12 @@
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Add Category") %></label></div>
|
||||
<div class="setting-control">
|
||||
<div class="setting-input-group">
|
||||
<input type="text" value={@settings_editor.new_category} phx-change="change_settings_new_category" name="name" />
|
||||
<button class="primary" type="button" phx-click="add_settings_category"><%= translated("Add") %></button>
|
||||
<input type="text" value={@settings_editor.new_category} phx-change="change_settings_new_category" phx-target={@myself} name="name" />
|
||||
<button class="primary" type="button" phx-click="add_settings_category" phx-target={@myself}><%= translated("Add") %></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-actions"><button class="secondary" type="button" phx-click="reset_settings_categories"><%= translated("Reset to Defaults") %></button></div>
|
||||
<div class="setting-actions"><button class="secondary" type="button" phx-click="reset_settings_categories" phx-target={@myself}><%= translated("Reset to Defaults") %></button></div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
@@ -211,13 +211,13 @@
|
||||
<%= if @settings_editor.ai_visible? do %>
|
||||
<div class="setting-section" id="settings-section-ai">
|
||||
<div class="setting-section-header"><h3><%= translated("AI") %></h3><p class="setting-section-description"><%= translated("OpenAI-compatible endpoints, model routing, airplane mode, and system prompt") %></p></div>
|
||||
<form class="setting-section-content" phx-change="change_settings_ai">
|
||||
<form class="setting-section-content" phx-change="change_settings_ai" phx-target={@myself}>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Online Endpoint URL") %></label></div>
|
||||
<div class="setting-control">
|
||||
<div class="setting-input-group">
|
||||
<input type="url" name="settings_ai[online_url]" value={@settings_editor.ai["online_url"]} />
|
||||
<button class="secondary" type="button" phx-click="refresh_settings_ai_models" phx-value-endpoint="online"><%= translated("Refresh Online Models") %></button>
|
||||
<button class="secondary" type="button" phx-click="refresh_settings_ai_models" phx-target={@myself} phx-value-endpoint="online"><%= translated("Refresh Online Models") %></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -250,7 +250,7 @@
|
||||
<div class="setting-control">
|
||||
<div class="setting-input-group">
|
||||
<input type="url" name="settings_ai[offline_url]" value={@settings_editor.ai["offline_url"]} />
|
||||
<button class="secondary" type="button" phx-click="refresh_settings_ai_models" phx-value-endpoint="airplane"><%= translated("Refresh Offline Models") %></button>
|
||||
<button class="secondary" type="button" phx-click="refresh_settings_ai_models" phx-target={@myself} phx-value-endpoint="airplane"><%= translated("Refresh Offline Models") %></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -297,14 +297,14 @@
|
||||
<% end %>
|
||||
</datalist>
|
||||
</form>
|
||||
<div class="setting-actions"><button class="primary" type="button" phx-click="save_settings_ai"><%= translated("Save") %></button><button class="secondary" type="button" phx-click="reset_settings_ai_prompt"><%= translated("Reset to Default") %></button></div>
|
||||
<div class="setting-actions"><button class="primary" type="button" phx-click="save_settings_ai" phx-target={@myself}><%= translated("Save") %></button><button class="secondary" type="button" phx-click="reset_settings_ai_prompt" phx-target={@myself}><%= translated("Reset to Default") %></button></div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= if @settings_editor.technology_visible? do %>
|
||||
<div class="setting-section" id="settings-section-technology">
|
||||
<div class="setting-section-header"><h3><%= translated("Technology") %></h3><p class="setting-section-description"><%= translated("Application-level runtime behavior and semantic indexing") %></p></div>
|
||||
<form class="setting-section-content" phx-change="change_settings_project">
|
||||
<form class="setting-section-content" phx-change="change_settings_project" phx-target={@myself}>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Semantic Similarity") %></label></div>
|
||||
<div class="setting-control"><label><input type="checkbox" name="settings_project[semantic_similarity_enabled]" checked={@settings_editor.technology["semantic_similarity_enabled"]} /> <%= translated("Enable duplicate search and related-post embeddings") %></label></div>
|
||||
@@ -314,20 +314,20 @@
|
||||
<div class="setting-control"><p class="setting-description"><%= translated("Scripting capabilities are configured at the application layer in the rewrite and do not expose runtime switching here.") %></p></div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="setting-actions"><button class="primary" type="button" phx-click="save_settings_project"><%= translated("Save") %></button></div>
|
||||
<div class="setting-actions"><button class="primary" type="button" phx-click="save_settings_project" phx-target={@myself}><%= translated("Save") %></button></div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= if @settings_editor.publishing_visible? do %>
|
||||
<div class="setting-section" id="settings-section-publishing">
|
||||
<div class="setting-section-header"><h3><%= translated("Publishing") %></h3><p class="setting-section-description"><%= translated("Deployment credentials for upload tasks") %></p></div>
|
||||
<form class="setting-section-content" phx-change="change_settings_publishing">
|
||||
<form class="setting-section-content" phx-change="change_settings_publishing" phx-target={@myself}>
|
||||
<div class="setting-row"><div class="setting-info"><label class="setting-label"><%= translated("SSH Mode") %></label></div><div class="setting-control"><select name="settings_publishing[ssh_mode]"><option value="scp" selected={@settings_editor.publishing["ssh_mode"] == "scp"}>scp</option><option value="rsync" selected={@settings_editor.publishing["ssh_mode"] == "rsync"}>rsync</option></select></div></div>
|
||||
<div class="setting-row"><div class="setting-info"><label class="setting-label"><%= translated("Host") %></label></div><div class="setting-control"><input type="text" name="settings_publishing[ssh_host]" value={@settings_editor.publishing["ssh_host"]} /></div></div>
|
||||
<div class="setting-row"><div class="setting-info"><label class="setting-label"><%= translated("Username") %></label></div><div class="setting-control"><input type="text" name="settings_publishing[ssh_user]" value={@settings_editor.publishing["ssh_user"]} /></div></div>
|
||||
<div class="setting-row"><div class="setting-info"><label class="setting-label"><%= translated("Remote Path") %></label></div><div class="setting-control"><input type="text" name="settings_publishing[ssh_remote_path]" value={@settings_editor.publishing["ssh_remote_path"]} /></div></div>
|
||||
</form>
|
||||
<div class="setting-actions"><button class="primary" type="button" phx-click="save_settings_publishing"><%= translated("Save") %></button><button class="secondary" type="button" phx-click="clear_settings_publishing"><%= translated("Clear") %></button></div>
|
||||
<div class="setting-actions"><button class="primary" type="button" phx-click="save_settings_publishing" phx-target={@myself}><%= translated("Save") %></button><button class="secondary" type="button" phx-click="clear_settings_publishing" phx-target={@myself}><%= translated("Clear") %></button></div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
@@ -342,7 +342,7 @@
|
||||
<p class="setting-description"><%= agent.config_path || translated("Not supported in the rewrite yet") %></p>
|
||||
</div>
|
||||
<div class="setting-control">
|
||||
<button class="secondary" type="button" phx-click="toggle_settings_mcp_agent" phx-value-agent={agent.id} disabled={not agent.supported?}>
|
||||
<button class="secondary" type="button" phx-click="toggle_settings_mcp_agent" phx-target={@myself} phx-value-agent={agent.id} disabled={not agent.supported?}>
|
||||
<%= if agent.configured?, do: translated("Remove"), else: translated("Add") %>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<div class="style-theme-picker" role="group" aria-label={translated("Theme picker")}>
|
||||
<%= for theme <- @style_editor.themes do %>
|
||||
<button type="button" class={["style-theme-option", if(theme.name == @style_editor.selected_theme, do: "selected")]} phx-click="select_style_theme" phx-value-theme={theme.name} aria-pressed={theme.name == @style_editor.selected_theme}>
|
||||
<button type="button" class={["style-theme-option", if(theme.name == @style_editor.selected_theme, do: "selected")]} phx-click="select_style_theme" phx-target={@myself} phx-value-theme={theme.name} aria-pressed={theme.name == @style_editor.selected_theme}>
|
||||
<span class="style-theme-swatch">
|
||||
<span class="style-theme-tones" aria-hidden="true">
|
||||
<span class="style-theme-tone style-theme-tone-accent" style={"background: linear-gradient(135deg, #{theme.accent_color}, #{theme.dark_bg_color})"}></span>
|
||||
@@ -22,13 +22,13 @@
|
||||
<div class="style-apply-row">
|
||||
<label class="style-preview-mode-control">
|
||||
<span><%= translated("Preview Mode") %></span>
|
||||
<select phx-change="change_style_preview_mode" name="mode">
|
||||
<select phx-change="change_style_preview_mode" phx-target={@myself} name="mode">
|
||||
<option value="auto" selected={@style_editor.preview_mode == "auto"}><%= translated("Auto") %></option>
|
||||
<option value="light" selected={@style_editor.preview_mode == "light"}><%= translated("Light") %></option>
|
||||
<option value="dark" selected={@style_editor.preview_mode == "dark"}><%= translated("Dark") %></option>
|
||||
</select>
|
||||
</label>
|
||||
<button class="primary" type="button" phx-click="apply_style_theme" disabled={@style_editor.selected_theme == @style_editor.applied_theme}><%= translated("Apply Theme") %></button>
|
||||
<button class="primary" type="button" phx-click="apply_style_theme" phx-target={@myself} disabled={@style_editor.selected_theme == @style_editor.applied_theme}><%= translated("Apply Theme") %></button>
|
||||
</div>
|
||||
|
||||
<div class="style-preview-container">
|
||||
|
||||
@@ -1200,7 +1200,9 @@ defmodule BDS.Desktop.ShellLiveTest do
|
||||
refute html =~ "Anthropic / Online API Key"
|
||||
|
||||
_html =
|
||||
render_change(view, "change_settings_ai", %{
|
||||
view
|
||||
|> element("#settings-editor-shell form[phx-change='change_settings_ai']")
|
||||
|> render_change(%{
|
||||
"settings_ai" => %{
|
||||
"online_url" => "https://api.example.test/v1",
|
||||
"online_api_key" => "online-secret",
|
||||
@@ -1221,7 +1223,10 @@ defmodule BDS.Desktop.ShellLiveTest do
|
||||
}
|
||||
})
|
||||
|
||||
_html = render_click(view, "save_settings_ai")
|
||||
_html =
|
||||
view
|
||||
|> element("#settings-editor-shell button[phx-click='save_settings_ai']")
|
||||
|> render_click()
|
||||
|
||||
assert {:ok, online_endpoint} = AI.get_endpoint(:online)
|
||||
assert online_endpoint.url == "https://api.example.test/v1"
|
||||
@@ -1268,7 +1273,9 @@ defmodule BDS.Desktop.ShellLiveTest do
|
||||
assert html =~ "Refresh Offline Models"
|
||||
|
||||
_html =
|
||||
render_change(view, "change_settings_ai", %{
|
||||
view
|
||||
|> element("#settings-editor-shell form[phx-change='change_settings_ai']")
|
||||
|> render_change(%{
|
||||
"settings_ai" => %{
|
||||
"online_url" => "https://api.example.test/v1",
|
||||
"offline_url" => "http://localhost:11434/v1"
|
||||
@@ -1277,7 +1284,7 @@ defmodule BDS.Desktop.ShellLiveTest do
|
||||
|
||||
html =
|
||||
view
|
||||
|> element("button[phx-click='refresh_settings_ai_models'][phx-value-endpoint='online']")
|
||||
|> element("#settings-editor-shell button[phx-click='refresh_settings_ai_models'][phx-value-endpoint='online']")
|
||||
|> render_click()
|
||||
|
||||
assert html =~ ~s(<option value="gpt-4.1"></option>)
|
||||
@@ -1285,7 +1292,7 @@ defmodule BDS.Desktop.ShellLiveTest do
|
||||
|
||||
html =
|
||||
view
|
||||
|> element("button[phx-click='refresh_settings_ai_models'][phx-value-endpoint='airplane']")
|
||||
|> element("#settings-editor-shell button[phx-click='refresh_settings_ai_models'][phx-value-endpoint='airplane']")
|
||||
|> render_click()
|
||||
|
||||
assert html =~ ~s(<option value="llama3.3"></option>)
|
||||
|
||||
Reference in New Issue
Block a user