feat: base app now working better
This commit is contained in:
@@ -2,19 +2,20 @@ defmodule BDS.Desktop.MenuBar do
|
|||||||
@moduledoc false
|
@moduledoc false
|
||||||
|
|
||||||
use Desktop.Menu
|
use Desktop.Menu
|
||||||
|
alias BDS.UI.MenuBar, as: ShellMenuBar
|
||||||
|
alias Desktop.OS
|
||||||
alias Desktop.Window
|
alias Desktop.Window
|
||||||
|
|
||||||
def groups(opts \\ []) do
|
def groups(opts \\ []) do
|
||||||
dev_mode? = Keyword.get(opts, :dev_mode?, false)
|
opts
|
||||||
|
|> ShellMenuBar.default_groups()
|
||||||
[
|
|> Enum.map(fn group ->
|
||||||
%{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: group.id,
|
||||||
%{id: :edit, label: "Edit", items: [%{id: :undo, label: "Undo"}, %{id: :redo, label: "Redo"}]},
|
label: group_label(group.id),
|
||||||
%{id: :view, label: "View", items: view_items(dev_mode?)},
|
items: Enum.map(group.items, &normalize_item/1)
|
||||||
%{id: :window, label: "Window", items: [%{id: :minimize, label: "Minimize"}]},
|
}
|
||||||
%{id: :help, label: "Help", items: [%{id: :documentation, label: "Documentation"}]}
|
end)
|
||||||
]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
@@ -34,8 +35,12 @@ defmodule BDS.Desktop.MenuBar do
|
|||||||
<%= for group <- @groups do %>
|
<%= for group <- @groups do %>
|
||||||
<menu label={group.label}>
|
<menu label={group.label}>
|
||||||
<%= for item <- group.items do %>
|
<%= for item <- group.items do %>
|
||||||
|
<%= if item.separator do %>
|
||||||
|
<hr />
|
||||||
|
<% else %>
|
||||||
<item onclick={Atom.to_string(item.id)}>{item.label}</item>
|
<item onclick={Atom.to_string(item.id)}>{item.label}</item>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
<% end %>
|
||||||
</menu>
|
</menu>
|
||||||
<% end %>
|
<% end %>
|
||||||
</menubar>
|
</menubar>
|
||||||
@@ -48,7 +53,18 @@ defmodule BDS.Desktop.MenuBar do
|
|||||||
{:noreply, menu}
|
{:noreply, menu}
|
||||||
end
|
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}
|
{:noreply, menu}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -57,17 +73,84 @@ defmodule BDS.Desktop.MenuBar do
|
|||||||
{:noreply, menu}
|
{:noreply, menu}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp view_items(dev_mode?) do
|
defp dispatch_shell_menu_action(command) when is_binary(command) do
|
||||||
items = [
|
with webview when not is_nil(webview) <- webview(),
|
||||||
%{id: :toggle_sidebar, label: "Toggle Sidebar"},
|
payload <- Jason.encode!(%{action: command}),
|
||||||
%{id: :toggle_panel, label: "Toggle Panel"},
|
script <-
|
||||||
%{id: :toggle_assistant_sidebar, label: "Toggle Assistant Sidebar"}
|
"window.dispatchEvent(new CustomEvent('bds:native-menu-action', { detail: #{payload} })); true;" do
|
||||||
]
|
:wx.set_env(Desktop.Env.wx_env())
|
||||||
|
:wxWebView.runScript(webview, script)
|
||||||
if dev_mode? do
|
|
||||||
items ++ [%{id: :toggle_dev_tools, label: "Toggle Dev Tools"}]
|
|
||||||
else
|
else
|
||||||
items
|
_ -> :ok
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|||||||
@@ -7,12 +7,75 @@ defmodule BDS.UI.MenuBar do
|
|||||||
dev_mode? = Keyword.get(opts, :dev_mode?, false)
|
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: :file,
|
||||||
%{id: :edit, items: [%{id: :undo}, %{id: :redo}]},
|
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: :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
|
end
|
||||||
|
|
||||||
@@ -30,12 +93,28 @@ defmodule BDS.UI.MenuBar do
|
|||||||
def execute(state, _command_id), do: state
|
def execute(state, _command_id), do: state
|
||||||
|
|
||||||
defp view_items(dev_mode?) do
|
defp view_items(dev_mode?) do
|
||||||
base = [
|
items = [
|
||||||
|
%{id: :view_posts},
|
||||||
|
%{id: :view_media},
|
||||||
%{id: :toggle_sidebar},
|
%{id: :toggle_sidebar},
|
||||||
%{id: :toggle_panel},
|
%{id: :toggle_panel},
|
||||||
%{id: :toggle_assistant_sidebar}
|
%{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
|
||||||
end
|
end
|
||||||
@@ -104,7 +104,9 @@ defmodule BDS.UI.ShellPage do
|
|||||||
id: Atom.to_string(group.id),
|
id: Atom.to_string(group.id),
|
||||||
label: humanize(group.id),
|
label: humanize(group.id),
|
||||||
items:
|
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)}
|
%{id: Atom.to_string(item.id), label: humanize(item.id)}
|
||||||
end)
|
end)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,6 +102,10 @@ button {
|
|||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.window-titlebar-menu-bar.is-hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.window-titlebar-menu-button {
|
.window-titlebar-menu-button {
|
||||||
height: 24px;
|
height: 24px;
|
||||||
border: none;
|
border: none;
|
||||||
@@ -694,10 +698,30 @@ button {
|
|||||||
.status-bar-right {
|
.status-bar-right {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 4px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-bar-left {
|
||||||
|
flex-shrink: 1;
|
||||||
min-width: 0;
|
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 {
|
.status-bar-item.brand {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|||||||
152
priv/ui/app.js
152
priv/ui/app.js
@@ -8,11 +8,13 @@ if (!root || !bootstrapNode) {
|
|||||||
const SIDEBAR_STORAGE_KEY = "bds-panel-sidebar";
|
const SIDEBAR_STORAGE_KEY = "bds-panel-sidebar";
|
||||||
const ASSISTANT_STORAGE_KEY = "bds-panel-assistant-sidebar";
|
const ASSISTANT_STORAGE_KEY = "bds-panel-assistant-sidebar";
|
||||||
const bootstrap = JSON.parse(bootstrapNode.textContent);
|
const bootstrap = JSON.parse(bootstrapNode.textContent);
|
||||||
|
const isMac = typeof navigator !== "undefined" && navigator.platform.toLowerCase().includes("mac");
|
||||||
const state = {
|
const state = {
|
||||||
session: hydrateSession(clone(bootstrap.session)),
|
session: hydrateSession(clone(bootstrap.session)),
|
||||||
tabMeta: {},
|
tabMeta: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bindNativeMenuBridge();
|
||||||
render();
|
render();
|
||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
@@ -32,8 +34,10 @@ function render() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderTitlebar() {
|
function renderTitlebar() {
|
||||||
|
const menuBarClass = isMac ? "window-titlebar-menu-bar is-hidden" : "window-titlebar-menu-bar";
|
||||||
|
|
||||||
root.querySelector(".window-titlebar").innerHTML = `
|
root.querySelector(".window-titlebar").innerHTML = `
|
||||||
<div class="window-titlebar-menu-bar">
|
<div class="${menuBarClass}">
|
||||||
${bootstrap.menu_groups
|
${bootstrap.menu_groups
|
||||||
.map((group) => `<button class="window-titlebar-menu-button" type="button">${escapeHtml(group.label)}</button>`)
|
.map((group) => `<button class="window-titlebar-menu-button" type="button">${escapeHtml(group.label)}</button>`)
|
||||||
.join("")}
|
.join("")}
|
||||||
@@ -330,28 +334,6 @@ function bindEvents() {
|
|||||||
button.onclick = () => {
|
button.onclick = () => {
|
||||||
const next = button.dataset.activity;
|
const next = button.dataset.activity;
|
||||||
|
|
||||||
bindResizeHandle("sidebar", {
|
|
||||||
key: SIDEBAR_STORAGE_KEY,
|
|
||||||
min: 200,
|
|
||||||
max: 500,
|
|
||||||
get: () => state.session.sidebar_width,
|
|
||||||
set: (value) => {
|
|
||||||
state.session.sidebar_width = value;
|
|
||||||
state.session.sidebar_visible = true;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
bindResizeHandle("assistant", {
|
|
||||||
key: ASSISTANT_STORAGE_KEY,
|
|
||||||
min: 280,
|
|
||||||
max: 640,
|
|
||||||
get: () => state.session.assistant_sidebar_width,
|
|
||||||
set: (value) => {
|
|
||||||
state.session.assistant_sidebar_width = value;
|
|
||||||
state.session.assistant_sidebar_visible = true;
|
|
||||||
},
|
|
||||||
invert: true,
|
|
||||||
});
|
|
||||||
if (state.session.active_view === next && state.session.sidebar_visible) {
|
if (state.session.active_view === next && state.session.sidebar_visible) {
|
||||||
state.session.sidebar_visible = false;
|
state.session.sidebar_visible = false;
|
||||||
} else {
|
} else {
|
||||||
@@ -386,6 +368,130 @@ function bindEvents() {
|
|||||||
render();
|
render();
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
bindResizeHandle("sidebar", {
|
||||||
|
key: SIDEBAR_STORAGE_KEY,
|
||||||
|
min: 200,
|
||||||
|
max: 500,
|
||||||
|
get: () => state.session.sidebar_width,
|
||||||
|
set: (value) => {
|
||||||
|
state.session.sidebar_width = value;
|
||||||
|
state.session.sidebar_visible = true;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
bindResizeHandle("assistant", {
|
||||||
|
key: ASSISTANT_STORAGE_KEY,
|
||||||
|
min: 280,
|
||||||
|
max: 640,
|
||||||
|
get: () => state.session.assistant_sidebar_width,
|
||||||
|
set: (value) => {
|
||||||
|
state.session.assistant_sidebar_width = value;
|
||||||
|
state.session.assistant_sidebar_visible = true;
|
||||||
|
},
|
||||||
|
invert: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindNativeMenuBridge() {
|
||||||
|
if (window.__BDS_NATIVE_MENU_BRIDGE__) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.__BDS_NATIVE_MENU_BRIDGE__ = true;
|
||||||
|
window.addEventListener("bds:native-menu-action", (event) => {
|
||||||
|
handleNativeMenuAction(event.detail?.action);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleNativeMenuAction(action) {
|
||||||
|
if (!action) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case "toggle_sidebar":
|
||||||
|
state.session.sidebar_visible = !state.session.sidebar_visible;
|
||||||
|
persistSessionWidths();
|
||||||
|
break;
|
||||||
|
case "toggle_panel":
|
||||||
|
state.session.panel.visible = !state.session.panel.visible;
|
||||||
|
break;
|
||||||
|
case "toggle_assistant_sidebar":
|
||||||
|
state.session.assistant_sidebar_visible = !state.session.assistant_sidebar_visible;
|
||||||
|
persistSessionWidths();
|
||||||
|
break;
|
||||||
|
case "view_posts":
|
||||||
|
state.session.active_view = "posts";
|
||||||
|
state.session.sidebar_visible = true;
|
||||||
|
break;
|
||||||
|
case "view_media":
|
||||||
|
state.session.active_view = "media";
|
||||||
|
state.session.sidebar_visible = true;
|
||||||
|
break;
|
||||||
|
case "close_tab":
|
||||||
|
closeActiveTab();
|
||||||
|
break;
|
||||||
|
case "edit_preferences":
|
||||||
|
openSingletonTab("settings");
|
||||||
|
break;
|
||||||
|
case "edit_menu":
|
||||||
|
openSingletonTab("menu_editor");
|
||||||
|
break;
|
||||||
|
case "metadata_diff":
|
||||||
|
openSingletonTab("metadata_diff");
|
||||||
|
break;
|
||||||
|
case "documentation":
|
||||||
|
openSingletonTab("documentation");
|
||||||
|
break;
|
||||||
|
case "api_documentation":
|
||||||
|
openSingletonTab("api_documentation");
|
||||||
|
break;
|
||||||
|
case "validate_site":
|
||||||
|
openSingletonTab("site_validation");
|
||||||
|
break;
|
||||||
|
case "validate_translations":
|
||||||
|
openSingletonTab("translation_validation");
|
||||||
|
break;
|
||||||
|
case "find_duplicates":
|
||||||
|
openSingletonTab("find_duplicates");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
|
||||||
|
function openSingletonTab(type) {
|
||||||
|
openTab(type, type, routeLabel(type), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeActiveTab() {
|
||||||
|
const active = currentTabRef();
|
||||||
|
if (!active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const index = state.session.tabs.findIndex((tab) => tab.type === active.type && tab.id === active.id);
|
||||||
|
if (index < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.session.tabs.splice(index, 1);
|
||||||
|
|
||||||
|
if (state.session.tabs.length === 0) {
|
||||||
|
state.session.active_tab = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index < state.session.tabs.length) {
|
||||||
|
const next = state.session.tabs[index];
|
||||||
|
state.session.active_tab = { type: next.type, id: next.id };
|
||||||
|
} else {
|
||||||
|
const next = state.session.tabs[state.session.tabs.length - 1];
|
||||||
|
state.session.active_tab = { type: next.type, id: next.id };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function openTab(type, id, title, transient) {
|
function openTab(type, id, title, transient) {
|
||||||
|
|||||||
@@ -35,13 +35,33 @@ defmodule BDS.DesktopTest do
|
|||||||
|
|
||||||
test "desktop menu bar exposes the native menu groups for the shell window" do
|
test "desktop menu bar exposes the native menu groups for the shell window" do
|
||||||
groups = BDS.Desktop.MenuBar.groups(dev_mode?: false)
|
groups = BDS.Desktop.MenuBar.groups(dev_mode?: false)
|
||||||
|
item_ids = fn items ->
|
||||||
|
items
|
||||||
|
|> Enum.reject(&Map.get(&1, :separator, false))
|
||||||
|
|> Enum.map(& &1.id)
|
||||||
|
end
|
||||||
|
|
||||||
assert Enum.map(groups, & &1.id) == [:app, :file, :edit, :view, :window, :help]
|
assert Enum.map(groups, & &1.id) == [:file, :edit, :view, :blog, :help]
|
||||||
|
|
||||||
view_group = Enum.find(groups, &(&1.id == :view))
|
view_group = Enum.find(groups, &(&1.id == :view))
|
||||||
assert :toggle_sidebar in Enum.map(view_group.items, & &1.id)
|
assert :toggle_sidebar in item_ids.(view_group.items)
|
||||||
assert :toggle_panel in Enum.map(view_group.items, & &1.id)
|
assert :toggle_panel in item_ids.(view_group.items)
|
||||||
assert :toggle_assistant_sidebar in Enum.map(view_group.items, & &1.id)
|
assert :toggle_assistant_sidebar in item_ids.(view_group.items)
|
||||||
|
|
||||||
|
blog_group = Enum.find(groups, &(&1.id == :blog))
|
||||||
|
blog_actions = item_ids.(blog_group.items)
|
||||||
|
|
||||||
|
assert :metadata_diff in blog_actions
|
||||||
|
assert :edit_menu in blog_actions
|
||||||
|
assert :rebuild_database in blog_actions
|
||||||
|
assert :find_duplicates in blog_actions
|
||||||
|
assert :validate_site in blog_actions
|
||||||
|
|
||||||
|
help_group = Enum.find(groups, &(&1.id == :help))
|
||||||
|
help_actions = item_ids.(help_group.items)
|
||||||
|
|
||||||
|
assert :documentation in help_actions
|
||||||
|
assert :api_documentation in help_actions
|
||||||
end
|
end
|
||||||
|
|
||||||
test "desktop shell html follows the old app frame regions and references bundled assets" do
|
test "desktop shell html follows the old app frame regions and references bundled assets" do
|
||||||
|
|||||||
@@ -124,4 +124,19 @@ defmodule BDS.UI.ShellTest do
|
|||||||
assert js =~ "activity-bar-top"
|
assert js =~ "activity-bar-top"
|
||||||
assert js =~ "activity-bar-bottom"
|
assert js =~ "activity-bar-bottom"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "static shell bundle hides the fake titlebar menu on macOS and keeps old status-bar alignment rules" do
|
||||||
|
css = File.read!("/Users/gb/Projects/bDS2/priv/ui/app.css")
|
||||||
|
js = File.read!("/Users/gb/Projects/bDS2/priv/ui/app.js")
|
||||||
|
|
||||||
|
assert js =~ "navigator.platform"
|
||||||
|
assert js =~ "isMac"
|
||||||
|
assert js =~ "window-titlebar-menu-bar is-hidden"
|
||||||
|
|
||||||
|
assert css =~ ".window-titlebar-menu-bar.is-hidden"
|
||||||
|
assert css =~ ".status-bar-left,"
|
||||||
|
assert css =~ "gap: 4px"
|
||||||
|
assert css =~ "padding: 0 8px"
|
||||||
|
assert css =~ "height: 100%"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -161,16 +161,24 @@ defmodule BDS.UI.WorkbenchTest do
|
|||||||
test "menu commands expose generic shell controls through a shared command model" do
|
test "menu commands expose generic shell controls through a shared command model" do
|
||||||
state = Workbench.new(sidebar_visible: false, panel_visible: false)
|
state = Workbench.new(sidebar_visible: false, panel_visible: false)
|
||||||
groups = MenuBar.default_groups(dev_mode?: false)
|
groups = MenuBar.default_groups(dev_mode?: false)
|
||||||
|
item_ids = fn items ->
|
||||||
|
items
|
||||||
|
|> Enum.reject(&Map.get(&1, :separator, false))
|
||||||
|
|> Enum.map(& &1.id)
|
||||||
|
end
|
||||||
|
|
||||||
assert Enum.map(groups, & &1.id) == [:app, :file, :edit, :view, :window, :help]
|
assert Enum.map(groups, & &1.id) == [:file, :edit, :view, :blog, :help]
|
||||||
|
|
||||||
view_group = Enum.find(groups, &(&1.id == :view))
|
view_group = Enum.find(groups, &(&1.id == :view))
|
||||||
command_ids = Enum.map(view_group.items, & &1.id)
|
command_ids = item_ids.(view_group.items)
|
||||||
|
|
||||||
assert :toggle_sidebar in command_ids
|
assert :toggle_sidebar in command_ids
|
||||||
assert :toggle_panel in command_ids
|
assert :toggle_panel in command_ids
|
||||||
refute :toggle_dev_tools in command_ids
|
refute :toggle_dev_tools in command_ids
|
||||||
|
|
||||||
|
blog_group = Enum.find(groups, &(&1.id == :blog))
|
||||||
|
assert :metadata_diff in item_ids.(blog_group.items)
|
||||||
|
|
||||||
state = MenuBar.execute(state, :toggle_sidebar)
|
state = MenuBar.execute(state, :toggle_sidebar)
|
||||||
state = MenuBar.execute(state, :toggle_panel)
|
state = MenuBar.execute(state, :toggle_panel)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user