defmodule BDS.Desktop.ShellLive.PanelRenderer do @moduledoc false use Phoenix.Component alias BDS.Desktop.ShellData alias BDS.Git alias BDS.Media.Media alias BDS.PostLinks alias BDS.Posts.Post alias BDS.Repo @doc "Render the active panel tab body." def render_panel_body(assigns) do case assigns.workbench.panel.active_tab do :tasks -> render_task_entries(assigns) :output -> render_output_entries(assigns) :post_links -> render_post_links(assigns) :git_log -> render_git_log(assigns) other -> render_generic_panel(assigns, other) end end @doc "Render the editor toolbar for the current tab." def render_editor_toolbar(assigns) do buttons = editor_toolbar_buttons(assigns.current_tab) assigns = assign(assigns, :editor_toolbar_buttons, buttons) ~H""" <%= if Enum.any?(@editor_toolbar_buttons) do %>
<%= for button <- @editor_toolbar_buttons do %> <% end %>
<% end %> """ end defp render_task_entries(assigns) do ~H""" <%= if Enum.empty?(Map.get(@task_status, :tasks, [])) do %>
<%= translated("Tasks") %> <%= translated("No background tasks running") %>
<% else %>
<%= for task <- Map.get(@task_status, :tasks, []) do %>
<%= task.name %> <%= Map.get(task, :status_label, task.status |> to_string() |> String.capitalize()) %>
<%= task.message || task.group_name || "" %> <%= if is_number(task.progress) do %>
<%= Map.get(task, :progress_label, progress_percent(task.progress)) %>
<% end %>
<% end %>
<% end %> """ end defp render_output_entries(assigns) do ~H""" <%= if Enum.empty?(@output_entries) do %>
<%= translated("Output") %> <%= translated("No shell output yet") %>
<% else %>
<%= for entry <- @output_entries do %>
<%= entry.title %> <%= entry.message %> <%= if present?(entry.details) do %> <%= entry.details %> <% end %>
<% end %>
<% end %> """ end defp render_post_links(assigns) do links = post_link_entries(assigns) assigns = assigns |> assign(:backlinks, Map.get(links, :backlinks, [])) |> assign(:outlinks, Map.get(links, :outlinks, [])) ~H""" <%= if Enum.empty?(@backlinks) and Enum.empty?(@outlinks) do %>
<%= translated("Post Links") %> <%= translated("No post links yet") %>
<% else %>
<%= if Enum.any?(@backlinks) do %>
<%= translated("Backlinks") %>
<%= for entry <- @backlinks do %> <% end %> <% end %> <%= if Enum.any?(@outlinks) do %>
<%= translated("Links To") %>
<%= for entry <- @outlinks do %> <% end %> <% end %>
<% end %> """ end defp render_git_log(assigns) do entries = git_log_entries(assigns) assigns = assign(assigns, :git_entries, entries) ~H""" <%= if Enum.empty?(@git_entries) do %>
<%= translated("Git Log") %> <%= translated("No git history yet") %>
<% else %>
<%= for entry <- @git_entries do %>
<%= short_commit_hash(entry.hash) %> <%= entry.subject || translated("No commit subject") %> <%= entry.hash %>
<% end %>
<% end %> """ end defp render_generic_panel(assigns, tab) do assigns = assign(assigns, :panel_label, ShellData.route_label(tab)) ~H"""
<%= @panel_label %> <%= translated("The shared lower panel is available for tasks, output, git details, and editor-specific diagnostics.") %>
""" end defp post_link_entries(assigns) do case assigns.current_tab do %{type: :post, id: post_id} -> %{ backlinks: related_posts(PostLinks.list_incoming_links(post_id), :source_post_id), outlinks: related_posts(PostLinks.list_outgoing_links(post_id), :target_post_id) } _other -> %{backlinks: [], outlinks: []} end end defp related_posts(links, key) do Enum.map(links, fn link -> case Repo.get(Post, Map.fetch!(link, key)) do %Post{} = post -> %{id: post.id, title: post.title || post.slug || post.id, text: link.link_text || post.slug || post.id} _other -> nil end end) |> Enum.reject(&is_nil/1) end defp git_log_entries(assigns) do case git_history_target(assigns.current_tab) do nil -> [] {project_id, file_path} -> case Git.file_history(project_id, file_path) do {:ok, %{commits: commits}} -> commits _other -> [] end end end defp git_history_target(%{type: :post, id: post_id}) do case Repo.get(Post, post_id) do %Post{project_id: project_id, file_path: file_path} when file_path not in [nil, ""] -> {project_id, file_path} _other -> nil end end defp git_history_target(%{type: :media, id: media_id}) do case Repo.get(Media, media_id) do %Media{project_id: project_id, file_path: file_path} when file_path not in [nil, ""] -> {project_id, file_path} _other -> nil end end defp git_history_target(_tab), do: nil def editor_toolbar_buttons(nil), do: [] def editor_toolbar_buttons(%{type: :post}) do [ %{kind: "ai_suggestions", label: "AI Suggestions", destructive: false}, %{kind: "insert_link", label: "Insert Link", destructive: false}, %{kind: "insert_media", label: "Insert Media", destructive: false}, %{kind: "language_picker", label: "Translate", destructive: false}, %{kind: "gallery", label: "Gallery", destructive: false} ] end def editor_toolbar_buttons(%{type: :media}) do [ %{kind: "ai_suggestions", label: "AI Suggestions", destructive: false}, %{kind: "language_picker", label: "Translate", destructive: false}, %{kind: "confirm_delete", label: "Delete Media", destructive: true} ] end def editor_toolbar_buttons(%{type: :tags}) do [ %{kind: "confirm_merge", label: "Merge Tags", destructive: false}, %{kind: "confirm_delete", label: "Delete Tag", destructive: true} ] end def editor_toolbar_buttons(_tab), do: [] defp short_commit_hash(hash) when is_binary(hash), do: String.slice(hash, 0, 7) defp short_commit_hash(_hash), do: "-------" defp progress_percent(progress) when is_number(progress) do rounded = progress |> Kernel.*(100) |> Float.round(0) |> trunc() "#{rounded}%" end defp progress_percent(_), do: "" defp present?(value), do: value not in [nil, ""] defp translated(text, bindings \\ %{}), do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current()) end