Files
bDS2/lib/bds/desktop/shell_live/index.html.heex

691 lines
34 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<div
class="app flex h-full w-full flex-col"
id="bds-shell-app"
phx-hook="AppShell"
data-shortcuts={encoded_shortcuts(@client_shortcuts)}
data-project-id={@projects.active_project_id || ""}
data-workbench-session={encoded_workbench_session(@workbench)}
>
<%= if @is_mac_ui do %>
<span data-testid="window-title" hidden><%= @page_title %></span>
<% end %>
<%= unless @is_mac_ui do %>
<div
class="window-titlebar"
data-region="title-bar"
data-testid="window-titlebar"
data-open-menu-group={@titlebar_menu_group || ""}
phx-window-keydown={if(@titlebar_menu_group, do: "titlebar_menu_keydown")}
>
<div class="window-titlebar-menu-bar" data-testid="window-titlebar-menu-bar">
<%= for group <- @menu_groups do %>
<div class="window-titlebar-menu-group">
<button
class={[
"window-titlebar-menu-button",
if(@titlebar_menu_group == Atom.to_string(group.id), do: "is-active")
]}
data-testid="window-titlebar-menu-button"
data-menu-group={group.id}
type="button"
phx-click="toggle_titlebar_menu"
phx-mouseenter="hover_titlebar_menu"
phx-value-group={group.id}
aria-label={group.label}
><%= group.label %></button>
<%= if @titlebar_menu_group == Atom.to_string(group.id) do %>
<div
class="window-titlebar-menu-dropdown"
data-testid="window-titlebar-menu-dropdown"
phx-click-away="close_titlebar_menu"
>
<%= for item <- BDS.Desktop.ShellLive.TitlebarMenu.dropdown_items(group) do %>
<%= if item.separator do %>
<div class="window-titlebar-menu-separator" role="separator"></div>
<% else %>
<button
class={[
"window-titlebar-menu-item",
if(BDS.Desktop.ShellLive.TitlebarMenu.item_active?(group, item, @titlebar_menu_item_index), do: "is-keyboard-active")
]}
data-testid="window-titlebar-menu-item"
data-menu-action={item.id}
type="button"
phx-click="titlebar_menu_action"
phx-value-action={item.id}
aria-label={item.label}
>
<span class="window-titlebar-menu-item-label"><%= item.label %></span>
<%= if item.shortcut do %>
<span class="window-titlebar-menu-item-accelerator"><%= item.shortcut %></span>
<% end %>
</button>
<% end %>
<% end %>
</div>
<% end %>
</div>
<% end %>
</div>
<div class="window-titlebar-drag-region"></div>
<div class="window-titlebar-title" data-testid="window-title"><%= @page_title %></div>
<div class="window-titlebar-actions">
<button
class="window-titlebar-action-button"
data-testid="toggle-sidebar"
type="button"
phx-click="toggle_sidebar"
aria-label={dgettext("ui", "Toggle sidebar")}
title={dgettext("ui", "Toggle sidebar")}
>
<span class={["window-titlebar-sidebar-icon", if(@workbench.sidebar_visible, do: "is-active", else: "is-inactive")]}>
<span class="window-titlebar-sidebar-pane"></span>
</span>
</button>
<button
class="window-titlebar-action-button"
data-testid="toggle-panel"
type="button"
phx-click="toggle_panel"
aria-label={dgettext("ui", "Toggle panel")}
title={dgettext("ui", "Toggle panel")}
>
<span class={["window-titlebar-panel-icon", if(@workbench.panel.visible, do: "is-active", else: "is-inactive")]}>
<span class="window-titlebar-panel-pane"></span>
</span>
</button>
<button
class="window-titlebar-action-button"
data-testid="toggle-assistant"
type="button"
phx-click="toggle_assistant_sidebar"
aria-label={dgettext("ui", "Toggle assistant")}
title={dgettext("ui", "Toggle assistant")}
>
<span class={["window-titlebar-assistant-icon", if(@workbench.assistant_sidebar_visible, do: "is-active", else: "is-inactive")]}>
<span class="window-titlebar-assistant-pane"></span>
</span>
</button>
</div>
</div>
<% end %>
<div class="app-main flex min-h-0 flex-1 overflow-hidden">
<aside class="activity-bar flex h-full shrink-0 flex-col items-center justify-between" data-region="activity-bar">
<div class="activity-bar-top flex flex-col items-center gap-1">
<%= for button <- Enum.filter(@activity_buttons, &(&1.activity_group == :top)) do %>
<button
class={["activity-bar-item inline-flex h-12 w-12 items-center justify-center", if(button.active, do: "active")]}
data-testid="activity-button"
data-view={button.id}
data-active={to_string(button.active)}
type="button"
phx-click="select_view"
phx-value-view={button.id}
title={activity_label(button.label)}
aria-label={activity_label(button.label)}
>
<%= raw(ShellData.activity_icon(button.id)) %>
<%= if button.badge do %>
<span class="activity-bar-badge"><%= button.badge.display %></span>
<% end %>
</button>
<% end %>
</div>
<div class="activity-bar-bottom flex flex-col items-center gap-1">
<%= for button <- Enum.filter(@activity_buttons, &(&1.activity_group == :bottom)) do %>
<button
class={["activity-bar-item inline-flex h-12 w-12 items-center justify-center", if(button.active, do: "active")]}
data-testid="activity-button"
data-view={button.id}
data-active={to_string(button.active)}
type="button"
phx-click="select_view"
phx-value-view={button.id}
title={activity_label(button.label)}
aria-label={activity_label(button.label)}
>
<%= raw(ShellData.activity_icon(button.id)) %>
<%= if button.badge do %>
<span class="activity-bar-badge"><%= button.badge.display %></span>
<% end %>
</button>
<% end %>
</div>
</aside>
<section
class={["sidebar-shell flex min-w-0 shrink-0 overflow-hidden", if(not @workbench.sidebar_visible, do: "is-hidden")]}
data-testid="sidebar-shell"
style={"width: #{if(@workbench.sidebar_visible, do: @workbench.sidebar_width, else: 0)}px;"}
>
<div class="sidebar flex min-w-0 flex-1 overflow-hidden" data-region="sidebar">
<div id="sidebar-content" class="sidebar-content sidebar-body flex min-h-0 flex-1 flex-col overflow-y-auto" phx-hook="SidebarInteractions">
<div class="sidebar-section">
<% create_action = sidebar_create_action(@workbench.active_view) %>
<div class="sidebar-section-header flex items-center justify-between gap-2">
<span><%= String.upcase(sidebar_header_label(@sidebar_header)) %></span>
<%= if create_action || ShellSidebarComponents.filters_enabled?(@sidebar_data) do %>
<div class="sidebar-actions flex items-center gap-1">
<%= if ShellSidebarComponents.filters_enabled?(@sidebar_data) do %>
<button
class={[
"sidebar-action inline-flex h-8 w-8 items-center justify-center",
if(ShellSidebarComponents.filters_visible?(@sidebar_data), do: "active")
]}
data-testid="sidebar-filter-toggle"
type="button"
phx-click="toggle_sidebar_filters"
aria-label={Map.get(@sidebar_data.filters, :toggle_filters_label)}
title={Map.get(@sidebar_data.filters, :toggle_filters_label)}
>
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
<path d="M6 12v-1h4v1H6zM4 8v-1h8v1H4zm-2-4v-1h12v1H2z"/>
</svg>
</button>
<% end %>
<%= if create_action do %>
<button
class="sidebar-action inline-flex h-8 w-8 items-center justify-center"
data-testid="sidebar-create-action"
data-sidebar-action={create_action.kind}
type="button"
phx-click="create_sidebar_item"
phx-value-kind={create_action.kind}
aria-label={create_action.label}
title={create_action.label}
>
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
<path d="M14 7v1H8v6H7V8H1V7h6V1h1v6h6z"/>
</svg>
</button>
<% end %>
</div>
<% end %>
</div>
</div>
<ShellSidebarComponents.sidebar_content sidebar_data={@sidebar_data} workbench={@workbench} page_language={@page_language} />
</div>
</div>
<div class="resizable-panel-divider sidebar-divider" data-resize="sidebar" data-role="resize-handle"></div>
</section>
<main class="app-content flex min-w-0 flex-1 flex-col overflow-hidden" data-region="content">
<div class="tab-bar flex h-[35px] shrink-0 items-center overflow-hidden" data-region="tab-bar">
<%= if Enum.empty?(@workbench.tabs) do %>
<div class="tab-bar-empty flex h-full items-center px-3 text-sm"><%= dgettext("ui", "Dashboard") %></div>
<% else %>
<div class="tab-bar-tabs flex min-w-0 flex-1 items-stretch overflow-x-auto">
<%= for tab <- @workbench.tabs do %>
<div
class={[
"tab flex min-w-0 max-w-[240px] shrink-0 items-stretch",
if(@workbench.active_tab == {tab.type, tab.id}, do: "active"),
if(tab.is_transient, do: "transient"),
if(Workbench.dirty?(@workbench, tab.type, tab.id), do: "dirty")
]}
data-tab-type={tab.type}
data-tab-id={tab.id}
tabindex="0"
>
<button
class="tab-select flex min-w-0 flex-1 items-center gap-2 overflow-hidden px-3 text-sm"
type="button"
phx-click="select_tab"
phx-value-type={tab.type}
phx-value-id={tab.id}
>
<span class="tab-icon shrink-0"><%= raw(ShellData.activity_icon(BDS.Desktop.ShellLive.TabHelpers.tab_icon_id(tab))) %></span>
<span class="tab-title truncate"><%= BDS.Desktop.ShellLive.TabHelpers.tab_title(tab, @tab_meta) %></span>
</button>
<div class="tab-actions flex items-center gap-1 pr-2">
<%= if Workbench.dirty?(@workbench, tab.type, tab.id) do %>
<span class="tab-dirty-indicator">●</span>
<% end %>
<button
class="tab-close inline-flex h-6 w-6 items-center justify-center"
data-testid="tab-close"
data-tab-type={tab.type}
data-tab-id={tab.id}
type="button"
phx-click="close_tab"
phx-value-type={tab.type}
phx-value-id={tab.id}
aria-label={dgettext("ui", "Close tab")}
title={dgettext("ui", "Close tab")}
>
×
</button>
</div>
</div>
<% end %>
</div>
<% end %>
</div>
<section class="editor-shell flex min-h-0 flex-1 flex-col overflow-hidden" data-region="editor">
<%= if is_nil(@current_tab) do %>
<div class="editor-empty">
<div class="dashboard-content">
<h1 data-testid="editor-title"><%= dgettext("ui", "Dashboard") %></h1>
<p class="text-muted"><%= dgettext("ui", "Overview of your blog database") %></p>
<div class="dashboard-stats">
<div class="stat-card">
<div class="stat-number"><%= @dashboard.post_stats.total_posts || 0 %></div>
<div class="stat-label"><%= dgettext("ui", "Total Posts") %></div>
<div class="stat-breakdown">
<span class="stat-tag stat-published"><%= dgettext("ui", "dashboard.stats.published", count: @dashboard.post_stats.published_count || 0) %></span>
<span class="stat-tag stat-draft"><%= dgettext("ui", "dashboard.stats.drafts", count: @dashboard.post_stats.draft_count || 0) %></span>
<%= if (@dashboard.post_stats.archived_count || 0) > 0 do %>
<span class="stat-tag stat-archived"><%= dgettext("ui", "dashboard.stats.archived", count: @dashboard.post_stats.archived_count || 0) %></span>
<% end %>
</div>
</div>
<div class="stat-card">
<div class="stat-number"><%= @dashboard.media_stats.media_count || 0 %></div>
<div class="stat-label"><%= dgettext("ui", "Media Files") %></div>
<div class="stat-breakdown">
<span class="stat-tag"><%= dgettext("ui", "dashboard.stats.images", count: @dashboard.media_stats.image_count || 0) %></span>
<span class="stat-tag"><%= ShellData.format_bytes(@dashboard.media_stats.total_bytes || 0) %></span>
</div>
</div>
<div class="stat-card">
<div class="stat-number"><%= length(@dashboard_tag_cloud_items) %></div>
<div class="stat-label"><%= dgettext("ui", "Tags") %></div>
<div class="stat-breakdown">
<span class="stat-tag"><%= dgettext("ui", "dashboard.stats.categories", count: length(@dashboard_category_counts)) %></span>
</div>
</div>
</div>
<%= if Enum.any?(@dashboard_timeline_entries) do %>
<div class="dashboard-section">
<h4><%= dgettext("ui", "Posts Over Time") %></h4>
<div class="timeline-chart">
<%= for entry <- @dashboard_timeline_entries do %>
<div class="timeline-bar-container">
<div class="timeline-bar" style={"height: #{timeline_height(entry, @dashboard_timeline_entries)}%"}>
<span class="timeline-bar-count"><%= entry.count || 0 %></span>
</div>
<div class="timeline-bar-label">
<span class="timeline-bar-label-month"><%= ShellData.format_dashboard_month(entry.year, entry.month) %></span>
<span class="timeline-bar-label-year"><%= entry.year %></span>
</div>
</div>
<% end %>
</div>
</div>
<% end %>
<%= if Enum.any?(@dashboard_tag_cloud_items) do %>
<div class="dashboard-section">
<h4><%= dgettext("ui", "Tags") %></h4>
<div class="tag-cloud">
<%= for item <- @dashboard_tag_cloud_items do %>
<span class={["dashboard-tag", if(item.color, do: "has-color")]} style={ShellData.render_dashboard_tag_style(item)} title={ShellData.dashboard_post_count_label(item.count)}><%= item.tag %></span>
<% end %>
</div>
</div>
<% end %>
<%= if Enum.any?(@dashboard_category_counts) do %>
<div class="dashboard-section">
<h4><%= dgettext("ui", "Categories") %></h4>
<div class="tag-cloud">
<%= for category <- @dashboard_category_counts do %>
<span class="dashboard-tag dashboard-category" title={ShellData.dashboard_post_count_label(category.count || 0)}>
<%= category.category || "" %>
<span class="tag-count"><%= category.count || 0 %></span>
</span>
<% end %>
</div>
</div>
<% end %>
<%= if Enum.any?(@dashboard_recent_posts) do %>
<div class="dashboard-section">
<h4><%= dgettext("ui", "Recently Updated") %></h4>
<div class="recent-posts-list">
<%= for post <- @dashboard_recent_posts do %>
<button
class="recent-post-item"
data-testid="recent-post-item"
data-post-id={post.id}
type="button"
phx-click="open_sidebar_item"
phx-value-route="post"
phx-value-id={post.id}
phx-value-title={post.title || ""}
phx-value-subtitle={post.status || "draft"}
>
<span class="recent-post-title"><%= post.title || "" %></span>
<span class={"recent-post-status status-#{post.status || "draft"}"}><%= ShellData.dashboard_status_label(post.status || "draft") %></span>
<span class="recent-post-date"><%= ShellData.format_dashboard_date(post.updated_at) %></span>
</button>
<% end %>
</div>
</div>
<% end %>
<div class="dashboard-inspector-meta" hidden>
<%= for item <- @editor_meta do %>
<section class="editor-meta-row">
<strong data-testid="editor-meta-label"><%= item.label %></strong>
<span><%= item.value %></span>
</section>
<% end %>
</div>
</div>
</div>
<% else %>
<%= cond do %>
<% @current_tab.type == :post -> %>
<.live_component module={PostEditor} id={"post-editor-#{@current_tab.id}"} current_tab={@current_tab} offline_mode={@offline_mode} />
<% @current_tab.type == :media -> %>
<.live_component module={MediaEditor} id={"media-editor-#{@current_tab.id}"} current_tab={@current_tab} offline_mode={@offline_mode} />
<% @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 @current_project -> %>
<.live_component module={MenuEditor} id="menu-editor"
project_id={@current_project.id}
current_tab={@current_tab}
tab_meta={@tab_meta}
/>
<% @current_tab.type == :tags and @current_project -> %>
<.live_component module={TagsEditor} id="tags-editor" project_id={@current_project.id} current_tab={@current_tab} tab_meta={@tab_meta} />
<% @current_tab.type == :scripts -> %>
<.live_component module={ScriptEditor} id={"script-editor-#{@current_tab.id}"} current_tab={@current_tab} />
<% @current_tab.type == :templates -> %>
<.live_component module={TemplateEditor} id={"template-editor-#{@current_tab.id}"} current_tab={@current_tab} />
<% @current_tab.type == :chat -> %>
<.live_component module={ChatEditor} id={"chat-editor-#{@current_tab.id}"} current_tab={@current_tab} offline_mode={@offline_mode} project_id={@projects.active_project_id} />
<% @current_tab.type == :import -> %>
<.live_component module={ImportEditor} id={"import-editor-#{@current_tab.id}"} current_tab={@current_tab} offline_mode={@offline_mode} project_id={@projects.active_project_id} />
<% @current_tab.type in [:site_validation, :metadata_diff, :translation_validation, :find_duplicates, :git_diff, :documentation, :api_documentation] -> %>
<.live_component module={MiscEditor} id={"misc-editor-#{@current_tab.type}-#{@current_tab.id}"} current_tab={@current_tab} tab_meta={@tab_meta} project_id={@projects.active_project_id} />
<% true -> %>
<div class="editor-frame">
<section class="editor-main">
<div class="editor-kicker"><%= BDS.Desktop.ShellLive.TabHelpers.tab_route_label(@current_tab) %></div>
<h1 class="editor-title" data-testid="editor-title"><%= BDS.Desktop.ShellLive.TabHelpers.tab_title(@current_tab, @tab_meta) %></h1>
<p class="editor-subtitle"><%= BDS.Desktop.ShellLive.TabHelpers.tab_subtitle(@current_tab, @tab_meta) %></p>
<%= BDS.Desktop.ShellLive.PanelRenderer.render_editor_toolbar(assigns) %>
<div class="editor-section">
<h2><%= BDS.Desktop.ShellLive.TabHelpers.tab_title(@current_tab, @tab_meta) %></h2>
<p>Desktop workbench content routed through the Elixir shell.</p>
</div>
</section>
<aside class="editor-meta">
<%= for item <- @editor_meta do %>
<section class="editor-meta-row">
<strong data-testid="editor-meta-label"><%= item.label %></strong>
<span><%= item.value %></span>
</section>
<% end %>
</aside>
</div>
<% end %>
<% end %>
</section>
<section class={["panel-shell flex min-h-0 shrink-0 flex-col overflow-hidden", if(not @workbench.panel.visible, do: "is-hidden")]} data-region="panel">
<div class="panel-header flex items-center justify-between gap-2">
<div class="panel-tabs flex min-w-0 items-center overflow-x-auto">
<%= for tab <- @panel_tabs do %>
<button
class={["panel-tab", "ui-tab", if(@workbench.panel.active_tab == tab, do: "ui-tab-active"), "inline-flex h-9 items-center px-3 text-xs uppercase tracking-wide", if(@workbench.panel.active_tab == tab, do: "active")]}
type="button"
phx-click="select_panel_tab"
phx-value-tab={tab}
>
<%= panel_tab_label(tab) %>
</button>
<% end %>
</div>
<button
class="panel-close ui-button ui-button-secondary inline-flex h-8 w-8 items-center justify-center"
data-testid="panel-close"
type="button"
phx-click="toggle_panel"
aria-label={dgettext("ui", "Close panel")}
title={dgettext("ui", "Close panel")}
>
×
</button>
</div>
<div class="panel-content min-h-0 flex-1 overflow-auto">
<%= BDS.Desktop.ShellLive.PanelRenderer.render_panel_body(assigns) %>
</div>
</section>
</main>
<section
class={["assistant-sidebar-shell flex min-w-0 shrink-0 overflow-hidden", if(not @workbench.assistant_sidebar_visible, do: "is-hidden")]}
data-testid="assistant-shell"
style={"width: #{if(@workbench.assistant_sidebar_visible, do: @workbench.assistant_sidebar_width, else: 0)}px;"}
>
<div class="resizable-panel-divider assistant-divider" data-resize="assistant" data-role="resize-handle"></div>
<aside class="assistant-sidebar flex min-w-0 flex-1 overflow-hidden" data-region="assistant-sidebar">
<div class="assistant-content flex min-h-0 flex-1 flex-col">
<header class="assistant-sidebar-header flex items-start justify-between gap-3">
<div class="assistant-sidebar-heading">
<strong><%= dgettext("ui", "AI Assistant") %></strong>
<span class="assistant-sidebar-description"><%= dgettext("ui", "AI conversations") %></span>
</div>
<span class={[
"assistant-sidebar-status",
if(@offline_mode, do: "is-offline", else: "is-online")
]}>
<%= if @offline_mode, do: dgettext("ui", "Offline"), else: dgettext("ui", "Chat") %>
</span>
</header>
<section class="assistant-sidebar-context flex shrink-0 flex-col gap-2" data-testid="assistant-context">
<div class="assistant-sidebar-context-row flex items-center justify-between gap-2">
<span class="assistant-sidebar-context-label"><%= dgettext("ui", "Project") %></span>
<span class="assistant-sidebar-context-value"><%= BDS.Desktop.ShellLive.ChatSurface.assistant_project_name(@current_project) %></span>
</div>
<div class="assistant-sidebar-context-row flex items-center justify-between gap-2">
<span class="assistant-sidebar-context-label"><%= dgettext("ui", "Editor") %></span>
<span class="assistant-sidebar-context-value"><%= BDS.Desktop.ShellLive.TabHelpers.tab_title(@current_tab, @tab_meta) %></span>
</div>
<p class="assistant-sidebar-context-text"><%= BDS.Desktop.ShellLive.TabHelpers.tab_subtitle(@current_tab, @tab_meta) %></p>
</section>
<form
class="assistant-sidebar-prompt-form flex shrink-0 flex-col gap-3"
data-testid="assistant-prompt-form"
phx-change="update_assistant_prompt"
phx-submit="submit_assistant_prompt"
>
<textarea
class="assistant-sidebar-prompt min-h-[8rem] w-full resize-y"
data-testid="assistant-prompt-input"
name="assistant[prompt]"
rows="6"
placeholder={dgettext("ui", "Ask the assistant about the active project or editor.")}
><%= @assistant_prompt %></textarea>
<button
class="assistant-sidebar-start-button ui-button ui-button-primary"
data-testid="assistant-start-button"
type="submit"
disabled={String.trim(@assistant_prompt || "") == ""}
>
<%= dgettext("ui", "Start chat") %>
</button>
</form>
<%= if Enum.empty?(@assistant_messages) do %>
<div class="assistant-sidebar-welcome min-h-0 flex-1 overflow-auto">
<%= for card <- @assistant_cards do %>
<section class="assistant-card flex flex-col gap-1">
<strong><%= card.label %></strong>
<span><%= card.text %></span>
</section>
<% end %>
</div>
<% else %>
<div class="assistant-sidebar-transcript min-h-0 flex-1 overflow-auto">
<%= for message <- @assistant_messages do %>
<article
class={["assistant-sidebar-message flex flex-col gap-1", message.role]}
data-testid={BDS.Desktop.ShellLive.ChatSurface.assistant_message_testid(message.role)}
>
<span class="assistant-sidebar-message-role"><%= BDS.Desktop.ShellLive.ChatSurface.assistant_message_label(message.role) %></span>
<p class="assistant-sidebar-message-content"><%= message.content %></p>
</article>
<% end %>
</div>
<% end %>
</div>
</aside>
</section>
</div>
<footer class="status-bar flex h-[22px] shrink-0 items-center justify-between gap-2" data-region="status-bar" data-testid="status-bar">
<div class="status-bar-left flex min-w-0 items-center gap-2 overflow-hidden">
<%= if @is_mac_ui do %>
<div class="status-shell-controls flex items-center gap-1" data-testid="status-shell-controls">
<button
class="status-shell-toggle-button inline-flex h-6 w-6 items-center justify-center"
data-testid="toggle-sidebar"
type="button"
phx-click="toggle_sidebar"
aria-label={dgettext("ui", "Toggle sidebar")}
title={dgettext("ui", "Toggle sidebar")}
>
<span class={["window-titlebar-sidebar-icon", if(@workbench.sidebar_visible, do: "is-active", else: "is-inactive")]}>
<span class="window-titlebar-sidebar-pane"></span>
</span>
</button>
<button
class="status-shell-toggle-button inline-flex h-6 w-6 items-center justify-center"
data-testid="toggle-panel"
type="button"
phx-click="toggle_panel"
aria-label={dgettext("ui", "Toggle panel")}
title={dgettext("ui", "Toggle panel")}
>
<span class={["window-titlebar-panel-icon", if(@workbench.panel.visible, do: "is-active", else: "is-inactive")]}>
<span class="window-titlebar-panel-pane"></span>
</span>
</button>
<button
class="status-shell-toggle-button inline-flex h-6 w-6 items-center justify-center"
data-testid="toggle-assistant"
type="button"
phx-click="toggle_assistant_sidebar"
aria-label={dgettext("ui", "Toggle assistant")}
title={dgettext("ui", "Toggle assistant")}
>
<span class={["window-titlebar-assistant-icon", if(@workbench.assistant_sidebar_visible, do: "is-active", else: "is-inactive")]}>
<span class="window-titlebar-assistant-pane"></span>
</span>
</button>
</div>
<% end %>
<div class="project-selector relative shrink-0">
<button
class="project-selector-trigger inline-flex items-center gap-2"
data-testid="project-selector-trigger"
type="button"
title={dgettext("ui", "Switch project")}
phx-click="toggle_project_menu"
>
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" class="project-icon">
<path d="M14.5 3H7.71l-.85-.85A.5.5 0 0 0 6.5 2h-5a.5.5 0 0 0-.5.5v11a.5.5 0 0 0 .5.5h13a.5.5 0 0 0 .5-.5v-10a.5.5 0 0 0-.5-.5zm-13 1h5.29l.85.85c.1.1.23.15.36.15h6.5v9h-13V4z"></path>
</svg>
<span class="project-name"><%= if @current_project, do: @current_project.name, else: "My Blog" %></span>
<svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor" class="dropdown-arrow">
<path d="M4.5 5.5L8 9l3.5-3.5h-7z"></path>
</svg>
</button>
<%= if @project_menu_open do %>
<div class="project-dropdown" data-testid="project-dropdown" phx-click-away="close_project_menu">
<div class="project-dropdown-header">
<span><%= dgettext("ui", "Projects") %></span>
</div>
<div class="project-list">
<%= for project <- @projects.projects do %>
<button
class={["project-item", if(project.id == @projects.active_project_id, do: "active")]}
data-testid="project-item"
data-project-id={project.id}
type="button"
phx-click="select_project"
phx-value-project_id={project.id}
>
<span class="project-item-name"><%= project.name %></span>
<%= if project.id == @projects.active_project_id do %>
<span class="project-check-icon">✓</span>
<% end %>
</button>
<% end %>
</div>
<div class="project-dropdown-footer">
<button class="existing-project-btn" type="button" phx-click="import_project">
<span><%= dgettext("ui", "Open Existing Blog") %></span>
</button>
<button class="create-project-btn" type="button" phx-click="create_project">
<span><%= dgettext("ui", "New Project") %></span>
</button>
</div>
</div>
<% end %>
</div>
<button class="status-bar-item status-bar-task-button inline-flex items-center gap-2" data-testid="status-task-button" type="button" phx-click="open_tasks_panel">
<%= if @status.left.running_task_message do %>
<span class="task-spinner"></span>
<% end %>
<span class="task-message-text"><%= @status.left.running_task_message || dgettext("ui", "Idle") %></span>
<%= if (@status.left.running_task_overflow || 0) > 0 do %>
<span class="status-bar-count">+<%= @status.left.running_task_overflow %></span>
<% end %>
</button>
</div>
<div class="status-bar-right flex items-center gap-2 overflow-hidden">
<span class="status-bar-item"><%= @status.right.post_count %></span>
<span class="status-bar-item"><%= @status.right.media_count %></span>
<span class="status-bar-item theme-badge"><%= @status.right.theme_badge %></span>
<button class={["status-bar-item", "offline-badge", if(@status.right.offline_mode, do: "active")]} data-testid="status-offline-button" type="button" phx-click="toggle_offline_mode" title={dgettext("ui", "Toggle offline mode")}>✈</button>
<form class="status-bar-item language-badge flex items-center gap-1" data-testid="status-language-form" phx-change="change_ui_language">
<span><%= dgettext("ui", "UI") %></span>
<select class="status-bar-language-select" name="ui_language" data-testid="status-language-select">
<%= for language <- @supported_ui_languages do %>
<option selected={language.code == @page_language} value={language.code}><%= language.flag %></option>
<% end %>
</select>
</form>
<span class="status-bar-item brand"><%= @status.right.brand %></span>
</div>
</footer>
<ShellOverlayComponents.shell_overlay shell_overlay={@shell_overlay} />
</div>