fix: more alignment
This commit is contained in:
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -2,6 +2,8 @@
|
|||||||
"chat.tools.terminal.autoApprove": {
|
"chat.tools.terminal.autoApprove": {
|
||||||
"mix": true,
|
"mix": true,
|
||||||
"allium": true,
|
"allium": true,
|
||||||
"command": true
|
"command": true,
|
||||||
|
"printf": true,
|
||||||
|
"git ls-tree": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@ defmodule BDS.Desktop.ShellLive do
|
|||||||
import Phoenix.HTML
|
import Phoenix.HTML
|
||||||
|
|
||||||
alias BDS.Desktop.{FolderPicker, ShellCommands, ShellData}
|
alias BDS.Desktop.{FolderPicker, ShellCommands, ShellData}
|
||||||
|
alias BDS.Desktop.MenuBar, as: DesktopMenuBar
|
||||||
alias BDS.Git
|
alias BDS.Git
|
||||||
alias BDS.Media.Media
|
alias BDS.Media.Media
|
||||||
alias BDS.PostLinks
|
alias BDS.PostLinks
|
||||||
@@ -46,6 +47,9 @@ defmodule BDS.Desktop.ShellLive do
|
|||||||
|> assign(:page_language, ShellData.ui_language())
|
|> assign(:page_language, ShellData.ui_language())
|
||||||
|> assign(:client_shortcuts, Commands.client_shortcuts())
|
|> assign(:client_shortcuts, Commands.client_shortcuts())
|
||||||
|> assign(:offline_mode, true)
|
|> assign(:offline_mode, true)
|
||||||
|
|> assign(:is_mac_ui, mac_ui?())
|
||||||
|
|> assign(:menu_groups, titlebar_menu_groups())
|
||||||
|
|> assign(:titlebar_menu_group, nil)
|
||||||
|> assign(:tab_meta, %{})
|
|> assign(:tab_meta, %{})
|
||||||
|> assign(:project_menu_open, false)
|
|> assign(:project_menu_open, false)
|
||||||
|> assign(:sidebar_filters_by_view, %{})
|
|> assign(:sidebar_filters_by_view, %{})
|
||||||
@@ -337,6 +341,33 @@ defmodule BDS.Desktop.ShellLive do
|
|||||||
{:noreply, handle_native_menu_action(socket, action)}
|
{:noreply, handle_native_menu_action(socket, action)}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_event("toggle_titlebar_menu", %{"group" => group}, socket) do
|
||||||
|
next_group = if socket.assigns.titlebar_menu_group == group, do: nil, else: group
|
||||||
|
{:noreply, assign(socket, :titlebar_menu_group, next_group)}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_event("hover_titlebar_menu", %{"group" => group}, socket) do
|
||||||
|
socket =
|
||||||
|
if socket.assigns.titlebar_menu_group do
|
||||||
|
assign(socket, :titlebar_menu_group, group)
|
||||||
|
else
|
||||||
|
socket
|
||||||
|
end
|
||||||
|
|
||||||
|
{:noreply, socket}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_event("close_titlebar_menu", _params, socket) do
|
||||||
|
{:noreply, assign(socket, :titlebar_menu_group, nil)}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_event("titlebar_menu_action", %{"action" => action}, socket) do
|
||||||
|
{:noreply,
|
||||||
|
socket
|
||||||
|
|> assign(:titlebar_menu_group, nil)
|
||||||
|
|> handle_native_menu_action(action)}
|
||||||
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_info(:refresh_task_status, socket) do
|
def handle_info(:refresh_task_status, socket) do
|
||||||
task_status = BDS.Tasks.status_snapshot()
|
task_status = BDS.Tasks.status_snapshot()
|
||||||
@@ -395,6 +426,7 @@ defmodule BDS.Desktop.ShellLive do
|
|||||||
|> assign(:activity_buttons, activity_buttons)
|
|> assign(:activity_buttons, activity_buttons)
|
||||||
|> assign(:panel_tabs, ShellData.panel_tabs(workbench))
|
|> assign(:panel_tabs, ShellData.panel_tabs(workbench))
|
||||||
|> assign(:supported_ui_languages, ShellData.supported_ui_languages())
|
|> assign(:supported_ui_languages, ShellData.supported_ui_languages())
|
||||||
|
|> assign(:menu_groups, socket.assigns[:menu_groups] || titlebar_menu_groups())
|
||||||
|> assign(:current_tab, current_tab(workbench))
|
|> assign(:current_tab, current_tab(workbench))
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1349,6 +1381,21 @@ defmodule BDS.Desktop.ShellLive do
|
|||||||
|> Enum.map_join(" ", &String.capitalize/1)
|
|> Enum.map_join(" ", &String.capitalize/1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp titlebar_menu_groups do
|
||||||
|
DesktopMenuBar.groups(dev_mode?: Application.get_env(:bds, :dev_routes, false))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp active_titlebar_menu_group(assigns) do
|
||||||
|
Enum.find(assigns.menu_groups || [], fn group -> Atom.to_string(group.id) == assigns.titlebar_menu_group end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp mac_ui? do
|
||||||
|
case Application.get_env(:bds, :shell_platform) do
|
||||||
|
nil -> match?({:unix, :darwin}, :os.type())
|
||||||
|
platform -> match?({:unix, :darwin}, platform)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp post_link_entries(assigns) do
|
defp post_link_entries(assigns) do
|
||||||
case assigns.current_tab do
|
case assigns.current_tab do
|
||||||
%{type: :post, id: post_id} ->
|
%{type: :post, id: post_id} ->
|
||||||
|
|||||||
@@ -1,12 +1,29 @@
|
|||||||
<div class="app" id="bds-shell-app" phx-hook="AppShell" data-shortcuts={encoded_shortcuts(@client_shortcuts)}>
|
<div class="app" id="bds-shell-app" phx-hook="AppShell" data-shortcuts={encoded_shortcuts(@client_shortcuts)}>
|
||||||
<div class="window-titlebar" data-region="title-bar">
|
<div
|
||||||
<div class="window-titlebar-menu-bar is-hidden">
|
class={["window-titlebar", if(@is_mac_ui, do: "is-mac")]}
|
||||||
<button class="window-titlebar-menu-button" type="button">File</button>
|
data-region="title-bar"
|
||||||
<button class="window-titlebar-menu-button" type="button">Edit</button>
|
data-testid="window-titlebar"
|
||||||
<button class="window-titlebar-menu-button" type="button">View</button>
|
data-open-menu-group={@titlebar_menu_group || ""}
|
||||||
<button class="window-titlebar-menu-button" type="button">Blog</button>
|
>
|
||||||
<button class="window-titlebar-menu-button" type="button">Help</button>
|
<%= unless @is_mac_ui do %>
|
||||||
</div>
|
<div class="window-titlebar-menu-bar" data-testid="window-titlebar-menu-bar">
|
||||||
|
<%= for group <- @menu_groups do %>
|
||||||
|
<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>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
<div class="window-titlebar-drag-region"></div>
|
<div class="window-titlebar-drag-region"></div>
|
||||||
<div class="window-titlebar-title" data-testid="window-title"><%= @page_title %></div>
|
<div class="window-titlebar-title" data-testid="window-title"><%= @page_title %></div>
|
||||||
<div class="window-titlebar-actions">
|
<div class="window-titlebar-actions">
|
||||||
@@ -47,6 +64,36 @@
|
|||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<%= if not @is_mac_ui do %>
|
||||||
|
<%= if group = active_titlebar_menu_group(assigns) do %>
|
||||||
|
<div
|
||||||
|
class="window-titlebar-menu-dropdown"
|
||||||
|
data-testid="window-titlebar-menu-dropdown"
|
||||||
|
phx-click-away="close_titlebar_menu"
|
||||||
|
>
|
||||||
|
<%= for item <- group.items do %>
|
||||||
|
<%= if item.separator do %>
|
||||||
|
<div class="window-titlebar-menu-separator"></div>
|
||||||
|
<% else %>
|
||||||
|
<button
|
||||||
|
class="window-titlebar-menu-item"
|
||||||
|
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 %>
|
||||||
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="app-main">
|
<div class="app-main">
|
||||||
|
|||||||
@@ -146,6 +146,10 @@ button {
|
|||||||
background-color: var(--vscode-toolbar-hoverBackground);
|
background-color: var(--vscode-toolbar-hoverBackground);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.window-titlebar-menu-button.is-active {
|
||||||
|
background-color: var(--vscode-toolbar-hoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
.window-titlebar-menu-button:focus,
|
.window-titlebar-menu-button:focus,
|
||||||
.window-titlebar-menu-button:focus-visible,
|
.window-titlebar-menu-button:focus-visible,
|
||||||
.window-titlebar-action-button:focus,
|
.window-titlebar-action-button:focus,
|
||||||
@@ -154,6 +158,61 @@ button {
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.window-titlebar-menu-dropdown {
|
||||||
|
position: absolute;
|
||||||
|
top: 30px;
|
||||||
|
left: 6px;
|
||||||
|
min-width: 210px;
|
||||||
|
padding: 6px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
background-color: var(--vscode-menu-background, var(--vscode-editorWidget-background));
|
||||||
|
border: 1px solid var(--vscode-menu-border, var(--vscode-panel-border));
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: var(--vscode-widget-shadow, 0 8px 24px rgba(0, 0, 0, 0.4));
|
||||||
|
app-region: no-drag;
|
||||||
|
-webkit-app-region: no-drag;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-menu-item {
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--vscode-menu-foreground, var(--vscode-foreground));
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 16px;
|
||||||
|
text-align: left;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 6px 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-menu-item:focus,
|
||||||
|
.window-titlebar-menu-item:focus-visible {
|
||||||
|
outline: none;
|
||||||
|
box-shadow: none;
|
||||||
|
background-color: var(--vscode-toolbar-hoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-menu-item:hover,
|
||||||
|
.window-titlebar-menu-item.is-keyboard-active {
|
||||||
|
background-color: var(--vscode-menu-selectionBackground, var(--vscode-toolbar-hoverBackground));
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-menu-item-accelerator {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-menu-separator {
|
||||||
|
height: 1px;
|
||||||
|
margin: 4px 2px;
|
||||||
|
background-color: var(--vscode-menu-separatorBackground, rgba(255, 255, 255, 0.08));
|
||||||
|
}
|
||||||
|
|
||||||
.window-titlebar-drag-region {
|
.window-titlebar-drag-region {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
@@ -183,6 +183,27 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.menuIsOpen = () => {
|
||||||
|
const titlebar = this.el.querySelector("[data-testid='window-titlebar']");
|
||||||
|
return Boolean(titlebar?.dataset.openMenuGroup);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.handleTitlebarPointerDown = (event) => {
|
||||||
|
if (!this.menuIsOpen()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.target.closest("[data-testid='window-titlebar-menu-button']")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.target.closest("[data-testid='window-titlebar-menu-dropdown']")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pushEvent("close_titlebar_menu", {});
|
||||||
|
};
|
||||||
|
|
||||||
this.handleChange = (event) => {
|
this.handleChange = (event) => {
|
||||||
const select = event.target.closest(".status-bar-language-select");
|
const select = event.target.closest(".status-bar-language-select");
|
||||||
|
|
||||||
@@ -216,8 +237,16 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.handleEscapeKey = (event) => {
|
||||||
|
if (event.key === "Escape" && this.menuIsOpen()) {
|
||||||
|
this.pushEvent("close_titlebar_menu", {});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
window.addEventListener("bds:native-menu-action", this.handleNativeMenuAction);
|
window.addEventListener("bds:native-menu-action", this.handleNativeMenuAction);
|
||||||
window.addEventListener("keydown", this.handleShortcutKeyDown, true);
|
window.addEventListener("keydown", this.handleShortcutKeyDown, true);
|
||||||
|
window.addEventListener("keydown", this.handleEscapeKey, true);
|
||||||
|
window.addEventListener("pointerdown", this.handleTitlebarPointerDown, true);
|
||||||
this.el.addEventListener("change", this.handleChange);
|
this.el.addEventListener("change", this.handleChange);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -226,6 +255,8 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
this.el.removeEventListener("change", this.handleChange);
|
this.el.removeEventListener("change", this.handleChange);
|
||||||
window.removeEventListener("bds:native-menu-action", this.handleNativeMenuAction);
|
window.removeEventListener("bds:native-menu-action", this.handleNativeMenuAction);
|
||||||
window.removeEventListener("keydown", this.handleShortcutKeyDown, true);
|
window.removeEventListener("keydown", this.handleShortcutKeyDown, true);
|
||||||
|
window.removeEventListener("keydown", this.handleEscapeKey, true);
|
||||||
|
window.removeEventListener("pointerdown", this.handleTitlebarPointerDown, true);
|
||||||
if (this.destroyOverlaySync) {
|
if (this.destroyOverlaySync) {
|
||||||
this.destroyOverlaySync();
|
this.destroyOverlaySync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,16 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
{:ok, project} = Projects.create_project(%{name: "Shell Project", data_path: temp_dir})
|
{:ok, project} = Projects.create_project(%{name: "Shell Project", data_path: temp_dir})
|
||||||
{:ok, _project} = Projects.set_active_project(project.id)
|
{:ok, _project} = Projects.set_active_project(project.id)
|
||||||
|
|
||||||
|
original_shell_platform = Application.get_env(:bds, :shell_platform)
|
||||||
|
|
||||||
|
on_exit(fn ->
|
||||||
|
if is_nil(original_shell_platform) do
|
||||||
|
Application.delete_env(:bds, :shell_platform)
|
||||||
|
else
|
||||||
|
Application.put_env(:bds, :shell_platform, original_shell_platform)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
%{project: project, temp_dir: temp_dir}
|
%{project: project, temp_dir: temp_dir}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -95,6 +105,59 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
assert html =~ ~s(class="tab-bar-empty")
|
assert html =~ ~s(class="tab-bar-empty")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "titlebar menu stays hidden on macos because the native menu owns it" do
|
||||||
|
{:ok, view, html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
|
||||||
|
|
||||||
|
assert html =~ ~s(class="window-titlebar is-mac")
|
||||||
|
refute html =~ ~s(data-testid="window-titlebar-menu-bar")
|
||||||
|
refute html =~ ~s(data-testid="window-titlebar-menu-button")
|
||||||
|
refute html =~ ~s(data-testid="window-titlebar-menu-dropdown")
|
||||||
|
|
||||||
|
html =
|
||||||
|
render_hook(view, "native_menu_action", %{"action" => "edit_preferences"})
|
||||||
|
|
||||||
|
assert html =~ ~s(data-tab-type="settings")
|
||||||
|
assert html =~ ">Settings<"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "titlebar menu matches the old shell contract on windows and linux" do
|
||||||
|
Application.put_env(:bds, :shell_platform, {:unix, :linux})
|
||||||
|
|
||||||
|
{:ok, view, html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
|
||||||
|
|
||||||
|
refute html =~ ~s(class="window-titlebar is-mac")
|
||||||
|
assert html =~ ~s(data-testid="window-titlebar-menu-bar")
|
||||||
|
assert html =~ ~s(data-testid="window-titlebar-menu-button")
|
||||||
|
assert html =~ ~s(data-menu-group="file")
|
||||||
|
assert html =~ ~s(>File<)
|
||||||
|
|
||||||
|
html =
|
||||||
|
view
|
||||||
|
|> element("[data-testid='window-titlebar-menu-button'][data-menu-group='file']")
|
||||||
|
|> render_click()
|
||||||
|
|
||||||
|
assert html =~ ~s(data-testid="window-titlebar-menu-dropdown")
|
||||||
|
assert html =~ ~s(data-testid="window-titlebar-menu-item")
|
||||||
|
assert html =~ ~s(data-menu-action="new_post")
|
||||||
|
assert html =~ ~s(>New Post<)
|
||||||
|
|
||||||
|
html =
|
||||||
|
view
|
||||||
|
|> element("[data-testid='window-titlebar-menu-button'][data-menu-group='edit']")
|
||||||
|
|> render_click()
|
||||||
|
|
||||||
|
assert html =~ ~s(data-menu-action="edit_preferences")
|
||||||
|
|
||||||
|
html =
|
||||||
|
view
|
||||||
|
|> element("[data-testid='window-titlebar-menu-item'][data-menu-action='edit_preferences']")
|
||||||
|
|> render_click()
|
||||||
|
|
||||||
|
assert html =~ ~s(data-tab-type="settings")
|
||||||
|
assert html =~ ">Settings<"
|
||||||
|
refute html =~ ~s(data-testid="window-titlebar-menu-dropdown")
|
||||||
|
end
|
||||||
|
|
||||||
test "sidebar open supports preview and pin intents for entity tabs" do
|
test "sidebar open supports preview and pin intents for entity tabs" do
|
||||||
{:ok, view, _html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
|
{:ok, view, _html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
|
||||||
|
|
||||||
|
|||||||
@@ -101,7 +101,9 @@ defmodule BDS.DesktopTest do
|
|||||||
|
|
||||||
assert conn.status == 200
|
assert conn.status == 200
|
||||||
assert conn.resp_body =~ ~s(class="app")
|
assert conn.resp_body =~ ~s(class="app")
|
||||||
assert conn.resp_body =~ ~s(class="window-titlebar")
|
assert conn.resp_body =~ ~s(data-testid="window-titlebar")
|
||||||
|
assert conn.resp_body =~ ~s(class="window-titlebar is-mac")
|
||||||
|
refute conn.resp_body =~ ~s(data-testid="window-titlebar-menu-bar")
|
||||||
assert conn.resp_body =~ ~s(class="activity-bar")
|
assert conn.resp_body =~ ~s(class="activity-bar")
|
||||||
assert conn.resp_body =~ ~s(class="sidebar")
|
assert conn.resp_body =~ ~s(class="sidebar")
|
||||||
assert conn.resp_body =~ ~s(class="status-bar")
|
assert conn.resp_body =~ ~s(class="status-bar")
|
||||||
|
|||||||
Reference in New Issue
Block a user