diff --git a/lib/bds/desktop/menu_bar.ex b/lib/bds/desktop/menu_bar.ex index 73ce56a..6199652 100644 --- a/lib/bds/desktop/menu_bar.ex +++ b/lib/bds/desktop/menu_bar.ex @@ -2,19 +2,20 @@ defmodule BDS.Desktop.MenuBar do @moduledoc false use Desktop.Menu + alias BDS.UI.MenuBar, as: ShellMenuBar + alias Desktop.OS alias Desktop.Window def groups(opts \\ []) do - dev_mode? = Keyword.get(opts, :dev_mode?, false) - - [ - %{id: :app, label: "App", items: [%{id: :about, label: "About"}]}, - %{id: :file, label: "File", items: [%{id: :new_post, label: "New Post"}, %{id: :close_tab, label: "Close Tab"}]}, - %{id: :edit, label: "Edit", items: [%{id: :undo, label: "Undo"}, %{id: :redo, label: "Redo"}]}, - %{id: :view, label: "View", items: view_items(dev_mode?)}, - %{id: :window, label: "Window", items: [%{id: :minimize, label: "Minimize"}]}, - %{id: :help, label: "Help", items: [%{id: :documentation, label: "Documentation"}]} - ] + opts + |> ShellMenuBar.default_groups() + |> Enum.map(fn group -> + %{ + id: group.id, + label: group_label(group.id), + items: Enum.map(group.items, &normalize_item/1) + } + end) end @impl true @@ -34,7 +35,11 @@ defmodule BDS.Desktop.MenuBar do <%= for group <- @groups do %>
<% end %> @@ -48,7 +53,18 @@ defmodule BDS.Desktop.MenuBar do {:noreply, menu} end - def handle_event(_, menu) do + def handle_event("view_on_github", menu) do + OS.launch_default_browser("https://github.com/rfc1437/bDS") + {:noreply, menu} + end + + def handle_event("report_issue", menu) do + OS.launch_default_browser("https://github.com/rfc1437/bDS/issues") + {:noreply, menu} + end + + def handle_event(command, menu) do + dispatch_shell_menu_action(command) {:noreply, menu} end @@ -57,17 +73,84 @@ defmodule BDS.Desktop.MenuBar do {:noreply, menu} end - defp view_items(dev_mode?) do - items = [ - %{id: :toggle_sidebar, label: "Toggle Sidebar"}, - %{id: :toggle_panel, label: "Toggle Panel"}, - %{id: :toggle_assistant_sidebar, label: "Toggle Assistant Sidebar"} - ] - - if dev_mode? do - items ++ [%{id: :toggle_dev_tools, label: "Toggle Dev Tools"}] + defp dispatch_shell_menu_action(command) when is_binary(command) do + with webview when not is_nil(webview) <- webview(), + payload <- Jason.encode!(%{action: command}), + script <- + "window.dispatchEvent(new CustomEvent('bds:native-menu-action', { detail: #{payload} })); true;" do + :wx.set_env(Desktop.Env.wx_env()) + :wxWebView.runScript(webview, script) else - items + _ -> :ok end end + + defp webview do + try do + Window.webview(BDS.Desktop.MainWindow.window_id()) + catch + :exit, _ -> nil + end + end + + defp normalize_item(%{separator: true}), do: %{separator: true} + + defp normalize_item(item) do + %{id: item.id, label: item_label(item.id), separator: false} + end + + defp group_label(:file), do: "File" + defp group_label(:edit), do: "Edit" + defp group_label(:view), do: "View" + defp group_label(:blog), do: "Blog" + defp group_label(:help), do: "Help" + + defp item_label(:new_post), do: "New Post" + defp item_label(:import_media), do: "Import Media" + defp item_label(:save), do: "Save" + defp item_label(:open_in_browser), do: "Open in Browser" + defp item_label(:open_data_folder), do: "Open Data Folder" + defp item_label(:close_tab), do: "Close Tab" + defp item_label(:quit), do: "Quit" + defp item_label(:undo), do: "Undo" + defp item_label(:redo), do: "Redo" + defp item_label(:cut), do: "Cut" + defp item_label(:copy), do: "Copy" + defp item_label(:paste), do: "Paste" + defp item_label(:delete), do: "Delete" + defp item_label(:select_all), do: "Select All" + defp item_label(:find), do: "Find" + defp item_label(:replace), do: "Replace" + defp item_label(:edit_preferences), do: "Preferences" + defp item_label(:view_posts), do: "Posts" + defp item_label(:view_media), do: "Media" + defp item_label(:toggle_sidebar), do: "Toggle Sidebar" + defp item_label(:toggle_panel), do: "Toggle Panel" + defp item_label(:toggle_assistant_sidebar), do: "Toggle Assistant Sidebar" + defp item_label(:toggle_dev_tools), do: "Toggle Dev Tools" + defp item_label(:reload), do: "Reload" + defp item_label(:force_reload), do: "Force Reload" + defp item_label(:reset_zoom), do: "Reset Zoom" + defp item_label(:zoom_in), do: "Zoom In" + defp item_label(:zoom_out), do: "Zoom Out" + defp item_label(:toggle_full_screen), do: "Toggle Full Screen" + defp item_label(:publish_selected), do: "Publish Selected" + defp item_label(:preview_post), do: "Preview Post" + defp item_label(:edit_menu), do: "Edit Menu" + defp item_label(:rebuild_database), do: "Rebuild Database" + defp item_label(:reindex_text), do: "Reindex Text" + defp item_label(:rebuild_embedding_index), do: "Rebuild Embedding Index" + defp item_label(:metadata_diff), do: "Metadata Diff" + defp item_label(:regenerate_calendar), do: "Regenerate Calendar" + defp item_label(:validate_translations), do: "Validate Translations" + defp item_label(:fill_missing_translations), do: "Fill Missing Translations" + defp item_label(:find_duplicates), do: "Find Duplicate Posts" + defp item_label(:generate_sitemap), do: "Generate Sitemap" + defp item_label(:validate_site), do: "Validate Site" + defp item_label(:upload_site), do: "Upload Site" + defp item_label(:about), do: "About" + defp item_label(:documentation), do: "Documentation" + defp item_label(:api_documentation), do: "API Documentation" + defp item_label(:view_on_github), do: "View on GitHub" + defp item_label(:report_issue), do: "Report Issue" end diff --git a/lib/bds/ui/menu_bar.ex b/lib/bds/ui/menu_bar.ex index 3d2f259..fa11dc6 100644 --- a/lib/bds/ui/menu_bar.ex +++ b/lib/bds/ui/menu_bar.ex @@ -7,12 +7,75 @@ defmodule BDS.UI.MenuBar do dev_mode? = Keyword.get(opts, :dev_mode?, false) [ - %{id: :app, items: [%{id: :about}, %{id: :settings}]}, - %{id: :file, items: [%{id: :new_post}, %{id: :new_page}, %{id: :close_tab}]}, - %{id: :edit, items: [%{id: :undo}, %{id: :redo}]}, + %{ + id: :file, + items: [ + %{id: :new_post}, + %{id: :import_media}, + %{separator: true}, + %{id: :save}, + %{separator: true}, + %{id: :open_in_browser}, + %{separator: true}, + %{id: :open_data_folder}, + %{separator: true}, + %{id: :close_tab}, + %{id: :quit} + ] + }, + %{ + id: :edit, + items: [ + %{id: :undo}, + %{id: :redo}, + %{separator: true}, + %{id: :cut}, + %{id: :copy}, + %{id: :paste}, + %{id: :delete}, + %{separator: true}, + %{id: :select_all}, + %{separator: true}, + %{id: :find}, + %{id: :replace}, + %{id: :edit_preferences} + ] + }, %{id: :view, items: view_items(dev_mode?)}, - %{id: :window, items: [%{id: :minimize}, %{id: :zoom}]}, - %{id: :help, items: [%{id: :documentation}, %{id: :api_documentation}]} + %{ + id: :blog, + items: [ + %{id: :publish_selected}, + %{separator: true}, + %{id: :preview_post}, + %{id: :edit_menu}, + %{separator: true}, + %{id: :rebuild_database}, + %{id: :reindex_text}, + %{id: :rebuild_embedding_index}, + %{separator: true}, + %{id: :metadata_diff}, + %{id: :regenerate_calendar}, + %{id: :validate_translations}, + %{id: :fill_missing_translations}, + %{id: :find_duplicates}, + %{separator: true}, + %{id: :generate_sitemap}, + %{id: :validate_site}, + %{id: :upload_site} + ] + }, + %{ + id: :help, + items: [ + %{id: :about}, + %{id: :documentation}, + %{id: :api_documentation}, + %{separator: true}, + %{id: :view_on_github}, + %{id: :report_issue} + ] + } ] end @@ -30,12 +93,28 @@ defmodule BDS.UI.MenuBar do def execute(state, _command_id), do: state defp view_items(dev_mode?) do - base = [ + items = [ + %{id: :view_posts}, + %{id: :view_media}, %{id: :toggle_sidebar}, %{id: :toggle_panel}, %{id: :toggle_assistant_sidebar} ] - if dev_mode?, do: base ++ [%{id: :toggle_dev_tools}], else: base + items = + if dev_mode?, do: items ++ [%{id: :toggle_dev_tools}], else: items + + items ++ + [ + %{separator: true}, + %{id: :reload}, + %{id: :force_reload}, + %{separator: true}, + %{id: :reset_zoom}, + %{id: :zoom_in}, + %{id: :zoom_out}, + %{separator: true}, + %{id: :toggle_full_screen} + ] end -end \ No newline at end of file +end diff --git a/lib/bds/ui/shell_page.ex b/lib/bds/ui/shell_page.ex index e822e8a..e1bd1d2 100644 --- a/lib/bds/ui/shell_page.ex +++ b/lib/bds/ui/shell_page.ex @@ -104,7 +104,9 @@ defmodule BDS.UI.ShellPage do id: Atom.to_string(group.id), label: humanize(group.id), items: - Enum.map(group.items, fn item -> + group.items + |> Enum.reject(&Map.get(&1, :separator, false)) + |> Enum.map(fn item -> %{id: Atom.to_string(item.id), label: humanize(item.id)} end) } diff --git a/priv/ui/app.css b/priv/ui/app.css index b422624..5f4f442 100644 --- a/priv/ui/app.css +++ b/priv/ui/app.css @@ -102,6 +102,10 @@ button { z-index: 2; } +.window-titlebar-menu-bar.is-hidden { + display: none; +} + .window-titlebar-menu-button { height: 24px; border: none; @@ -694,10 +698,30 @@ button { .status-bar-right { display: flex; align-items: center; - gap: 10px; + gap: 4px; + flex-shrink: 0; +} + +.status-bar-left { + flex-shrink: 1; min-width: 0; } +.status-bar-item { + display: flex; + align-items: center; + gap: 6px; + padding: 0 8px; + height: 100%; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +.status-bar-item:hover { + background-color: rgba(255, 255, 255, 0.1); +} + .status-bar-item.brand { font-weight: 600; } diff --git a/priv/ui/app.js b/priv/ui/app.js index 542c202..f9e4382 100644 --- a/priv/ui/app.js +++ b/priv/ui/app.js @@ -8,11 +8,13 @@ if (!root || !bootstrapNode) { const SIDEBAR_STORAGE_KEY = "bds-panel-sidebar"; const ASSISTANT_STORAGE_KEY = "bds-panel-assistant-sidebar"; const bootstrap = JSON.parse(bootstrapNode.textContent); +const isMac = typeof navigator !== "undefined" && navigator.platform.toLowerCase().includes("mac"); const state = { session: hydrateSession(clone(bootstrap.session)), tabMeta: {}, }; +bindNativeMenuBridge(); render(); function render() { @@ -32,8 +34,10 @@ function render() { } function renderTitlebar() { + const menuBarClass = isMac ? "window-titlebar-menu-bar is-hidden" : "window-titlebar-menu-bar"; + root.querySelector(".window-titlebar").innerHTML = ` -