fix: more alignments still
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
defmodule BDS.Desktop.ShellData do
|
defmodule BDS.Desktop.ShellData do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
|
|
||||||
|
alias BDS.Git
|
||||||
alias BDS.I18n
|
alias BDS.I18n
|
||||||
alias BDS.Projects
|
alias BDS.Projects
|
||||||
alias BDS.UI.Dashboard
|
alias BDS.UI.Dashboard
|
||||||
@@ -100,6 +101,30 @@ defmodule BDS.Desktop.ShellData do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def git_badge_count(project_id, opts \\ [])
|
||||||
|
|
||||||
|
def git_badge_count(nil, _opts), do: 0
|
||||||
|
def git_badge_count("default", _opts), do: 0
|
||||||
|
|
||||||
|
def git_badge_count(project_id, opts) when is_binary(project_id) do
|
||||||
|
provider = Keyword.get(opts, :provider, git_remote_state_provider())
|
||||||
|
|
||||||
|
try do
|
||||||
|
case provider.(project_id, []) do
|
||||||
|
{:ok, %{behind: behind}} when is_integer(behind) and behind > 0 -> behind
|
||||||
|
{:ok, %{behind: behind}} when is_binary(behind) -> parse_positive_count(behind)
|
||||||
|
_other -> 0
|
||||||
|
end
|
||||||
|
rescue
|
||||||
|
error in [DBConnection.OwnershipError, Exqlite.Error] ->
|
||||||
|
if match?(%Exqlite.Error{}, error) and not String.contains?(Exception.message(error), "no such table") do
|
||||||
|
reraise error, __STACKTRACE__
|
||||||
|
end
|
||||||
|
|
||||||
|
0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def panel_tabs(workbench) do
|
def panel_tabs(workbench) do
|
||||||
[:tasks, :output]
|
[:tasks, :output]
|
||||||
|> maybe_add_panel_tab(workbench.editor_route, :post_links)
|
|> maybe_add_panel_tab(workbench.editor_route, :post_links)
|
||||||
@@ -108,6 +133,17 @@ defmodule BDS.Desktop.ShellData do
|
|||||||
|> Enum.uniq()
|
|> Enum.uniq()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp git_remote_state_provider do
|
||||||
|
Application.get_env(:bds, :git_remote_state_provider, &Git.remote_state/2)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp parse_positive_count(value) do
|
||||||
|
case Integer.parse(value) do
|
||||||
|
{count, _rest} when count > 0 -> count
|
||||||
|
_other -> 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def activity_icon(id) do
|
def activity_icon(id) do
|
||||||
case to_string(id) do
|
case to_string(id) do
|
||||||
"posts" -> ~s(<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6zM6 20V4h7v5h5v11H6z"></path><path d="M8 12h8v2H8zm0 4h8v2H8z"></path></svg>)
|
"posts" -> ~s(<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6zM6 20V4h7v5h5v11H6z"></path><path d="M8 12h8v2H8zm0 4h8v2H8z"></path></svg>)
|
||||||
|
|||||||
@@ -394,11 +394,12 @@ defmodule BDS.Desktop.ShellLive do
|
|||||||
defp reload_shell(socket, workbench) do
|
defp reload_shell(socket, workbench) do
|
||||||
projects = ShellData.project_snapshot()
|
projects = ShellData.project_snapshot()
|
||||||
dashboard = ShellData.dashboard(projects.active_project_id)
|
dashboard = ShellData.dashboard(projects.active_project_id)
|
||||||
|
git_badge_count = ShellData.git_badge_count(projects.active_project_id)
|
||||||
active_view_id = Atom.to_string(workbench.active_view)
|
active_view_id = Atom.to_string(workbench.active_view)
|
||||||
sidebar_data = ShellData.sidebar_view(projects.active_project_id, active_view_id, current_sidebar_filters(socket, active_view_id))
|
sidebar_data = ShellData.sidebar_view(projects.active_project_id, active_view_id, current_sidebar_filters(socket, active_view_id))
|
||||||
sidebar_data = merge_sidebar_ui_state(socket, active_view_id, sidebar_data)
|
sidebar_data = merge_sidebar_ui_state(socket, active_view_id, sidebar_data)
|
||||||
task_status = BDS.Tasks.status_snapshot()
|
task_status = BDS.Tasks.status_snapshot()
|
||||||
activity_buttons = Workbench.activity_buttons(workbench, 0)
|
activity_buttons = Workbench.activity_buttons(workbench, git_badge_count)
|
||||||
page_language = socket.assigns[:page_language] || ShellData.ui_language()
|
page_language = socket.assigns[:page_language] || ShellData.ui_language()
|
||||||
offline_mode = Map.get(socket.assigns, :offline_mode, true)
|
offline_mode = Map.get(socket.assigns, :offline_mode, true)
|
||||||
|
|
||||||
|
|||||||
@@ -112,6 +112,9 @@
|
|||||||
aria-label={activity_label(button.label)}
|
aria-label={activity_label(button.label)}
|
||||||
>
|
>
|
||||||
<%= raw(ShellData.activity_icon(button.id)) %>
|
<%= raw(ShellData.activity_icon(button.id)) %>
|
||||||
|
<%= if button.badge do %>
|
||||||
|
<span class="activity-bar-badge"><%= button.badge.display %></span>
|
||||||
|
<% end %>
|
||||||
</button>
|
</button>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
@@ -129,6 +132,9 @@
|
|||||||
aria-label={activity_label(button.label)}
|
aria-label={activity_label(button.label)}
|
||||||
>
|
>
|
||||||
<%= raw(ShellData.activity_icon(button.id)) %>
|
<%= raw(ShellData.activity_icon(button.id)) %>
|
||||||
|
<%= if button.badge do %>
|
||||||
|
<span class="activity-bar-badge"><%= button.badge.display %></span>
|
||||||
|
<% end %>
|
||||||
</button>
|
</button>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -171,6 +171,26 @@ defmodule BDS.Git do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def remote_state(project_id, opts \\ []) when is_binary(project_id) and is_list(opts) do
|
||||||
|
with {:ok, project_dir} <- project_dir(project_id),
|
||||||
|
{:ok, local_branch} <- current_branch(project_dir, opts) do
|
||||||
|
case upstream_branch(project_dir, opts) do
|
||||||
|
{:ok, nil} ->
|
||||||
|
{:ok, %{local_branch: local_branch, upstream_branch: nil, has_upstream: false, ahead: 0, behind: 0}}
|
||||||
|
|
||||||
|
{:ok, upstream_branch} ->
|
||||||
|
{:ok,
|
||||||
|
%{
|
||||||
|
local_branch: local_branch,
|
||||||
|
upstream_branch: upstream_branch,
|
||||||
|
has_upstream: true,
|
||||||
|
ahead: revision_count(project_dir, "#{upstream_branch}..HEAD", opts),
|
||||||
|
behind: revision_count(project_dir, "HEAD..#{upstream_branch}", opts)
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp project_dir(project_id) do
|
defp project_dir(project_id) do
|
||||||
case Projects.get_project(project_id) do
|
case Projects.get_project(project_id) do
|
||||||
nil -> {:error, :not_found}
|
nil -> {:error, :not_found}
|
||||||
@@ -274,6 +294,13 @@ defmodule BDS.Git do
|
|||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp upstream_branch(project_dir, opts) do
|
||||||
|
case run_git(project_dir, ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{upstream}"], opts) do
|
||||||
|
{:ok, output} -> {:ok, blank_to_nil(output)}
|
||||||
|
{:error, {:git_failed, _message}} -> {:ok, nil}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp parse_status(output) do
|
defp parse_status(output) do
|
||||||
output
|
output
|
||||||
|> String.split("\n", trim: true)
|
|> String.split("\n", trim: true)
|
||||||
@@ -334,6 +361,20 @@ defmodule BDS.Git do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp revision_count(project_dir, revision_range, opts) do
|
||||||
|
case run_git(project_dir, ["rev-list", "--count", revision_range], opts) do
|
||||||
|
{:ok, output} -> parse_count(output)
|
||||||
|
{:error, {:git_failed, _message}} -> 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp parse_count(value) do
|
||||||
|
case Integer.parse(to_string(value || "")) do
|
||||||
|
{count, _rest} -> count
|
||||||
|
:error -> 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp category_for_path("posts/" <> _rest), do: :posts
|
defp category_for_path("posts/" <> _rest), do: :posts
|
||||||
defp category_for_path("scripts/" <> _rest), do: :scripts
|
defp category_for_path("scripts/" <> _rest), do: :scripts
|
||||||
defp category_for_path("templates/" <> _rest), do: :templates
|
defp category_for_path("templates/" <> _rest), do: :templates
|
||||||
|
|||||||
@@ -393,6 +393,23 @@ button {
|
|||||||
background-color: var(--vscode-activityBar-foreground);
|
background-color: var(--vscode-activityBar-foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.activity-bar-badge {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
right: 8px;
|
||||||
|
min-width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
padding: 0 4px;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
background-color: var(--vscode-activityBarBadge-background);
|
||||||
|
color: var(--vscode-activityBarBadge-foreground);
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
.activity-bar-item svg,
|
.activity-bar-item svg,
|
||||||
.tab-icon svg {
|
.tab-icon svg {
|
||||||
display: block;
|
display: block;
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
{: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)
|
original_shell_platform = Application.get_env(:bds, :shell_platform)
|
||||||
|
original_git_remote_state_provider = Application.get_env(:bds, :git_remote_state_provider)
|
||||||
|
|
||||||
on_exit(fn ->
|
on_exit(fn ->
|
||||||
if is_nil(original_shell_platform) do
|
if is_nil(original_shell_platform) do
|
||||||
@@ -33,6 +34,12 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
else
|
else
|
||||||
Application.put_env(:bds, :shell_platform, original_shell_platform)
|
Application.put_env(:bds, :shell_platform, original_shell_platform)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if is_nil(original_git_remote_state_provider) do
|
||||||
|
Application.delete_env(:bds, :git_remote_state_provider)
|
||||||
|
else
|
||||||
|
Application.put_env(:bds, :git_remote_state_provider, original_git_remote_state_provider)
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
%{project: project, temp_dir: temp_dir}
|
%{project: project, temp_dir: temp_dir}
|
||||||
@@ -158,6 +165,18 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
refute html =~ ~s(data-testid="window-titlebar-menu-dropdown")
|
refute html =~ ~s(data-testid="window-titlebar-menu-dropdown")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "shell live renders the legacy git activity badge from remote behind count" do
|
||||||
|
Application.put_env(:bds, :git_remote_state_provider, fn _project_id, _opts ->
|
||||||
|
{:ok, %{local_branch: "main", upstream_branch: "origin/main", has_upstream: true, ahead: 0, behind: 7}}
|
||||||
|
end)
|
||||||
|
|
||||||
|
{:ok, _view, html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
|
||||||
|
|
||||||
|
assert html =~ ~s(data-view="git")
|
||||||
|
assert html =~ ~s(class="activity-bar-badge")
|
||||||
|
assert html =~ ">7<"
|
||||||
|
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)
|
||||||
|
|
||||||
|
|||||||
@@ -72,6 +72,22 @@ defmodule BDS.GitTest do
|
|||||||
assert repo.current_branch == "main"
|
assert repo.current_branch == "main"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "remote_state reports upstream ahead and behind counts", %{project: project} do
|
||||||
|
runner = fake_runner(fn
|
||||||
|
"git", ["rev-parse", "--abbrev-ref", "HEAD"], _opts -> {"main\n", 0}
|
||||||
|
"git", ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{upstream}"], _opts -> {"origin/main\n", 0}
|
||||||
|
"git", ["rev-list", "--count", "origin/main..HEAD"], _opts -> {"2\n", 0}
|
||||||
|
"git", ["rev-list", "--count", "HEAD..origin/main"], _opts -> {"5\n", 0}
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert {:ok, remote_state} = Git.remote_state(project.id, runner: runner)
|
||||||
|
assert remote_state.local_branch == "main"
|
||||||
|
assert remote_state.upstream_branch == "origin/main"
|
||||||
|
assert remote_state.has_upstream == true
|
||||||
|
assert remote_state.ahead == 2
|
||||||
|
assert remote_state.behind == 5
|
||||||
|
end
|
||||||
|
|
||||||
test "fetch, pull, push, commit_all, reconcile, and prune_lfs_cache run non-interactively", %{
|
test "fetch, pull, push, commit_all, reconcile, and prune_lfs_cache run non-interactively", %{
|
||||||
project: project
|
project: project
|
||||||
} do
|
} do
|
||||||
|
|||||||
@@ -136,11 +136,17 @@ defmodule BDS.UI.ShellTest do
|
|||||||
template = File.read!("/Users/gb/Projects/bDS2/lib/bds/desktop/shell_live/index.html.heex")
|
template = File.read!("/Users/gb/Projects/bDS2/lib/bds/desktop/shell_live/index.html.heex")
|
||||||
|
|
||||||
assert css =~ "color: var(--vscode-activityBar-foreground)"
|
assert css =~ "color: var(--vscode-activityBar-foreground)"
|
||||||
|
assert css =~ ".activity-bar-badge"
|
||||||
assert css =~ ".tab-actions"
|
assert css =~ ".tab-actions"
|
||||||
assert css =~ ".tab-dirty-indicator"
|
assert css =~ ".tab-dirty-indicator"
|
||||||
assert css =~ ".tab.dirty .tab-close"
|
assert css =~ ".tab.dirty .tab-close"
|
||||||
assert css =~ ".tab:focus-visible"
|
assert css =~ ".tab:focus-visible"
|
||||||
assert css =~ ".window-titlebar-action-button:focus"
|
assert css =~ ".window-titlebar-action-button:focus"
|
||||||
|
assert css =~ ".panel-tab.active"
|
||||||
|
assert css =~ "border-bottom-color: var(--vscode-focusBorder);"
|
||||||
|
assert css =~ ".sidebar-section-header"
|
||||||
|
assert css =~ "justify-content: space-between"
|
||||||
|
assert css =~ "align-items: center"
|
||||||
assert css =~ "padding-right: calc(10px + var(--bds-titlebar-overlay-right, 0px));"
|
assert css =~ "padding-right: calc(10px + var(--bds-titlebar-overlay-right, 0px));"
|
||||||
assert live_js =~ "windowControlsOverlay"
|
assert live_js =~ "windowControlsOverlay"
|
||||||
assert live_js =~ "geometrychange"
|
assert live_js =~ "geometrychange"
|
||||||
@@ -150,6 +156,7 @@ defmodule BDS.UI.ShellTest do
|
|||||||
assert live_js =~ "event.preventDefault()"
|
assert live_js =~ "event.preventDefault()"
|
||||||
assert live_js =~ "this.pushEvent(\"shortcut\""
|
assert live_js =~ "this.pushEvent(\"shortcut\""
|
||||||
assert template =~ "data-shortcuts={encoded_shortcuts(@client_shortcuts)}"
|
assert template =~ "data-shortcuts={encoded_shortcuts(@client_shortcuts)}"
|
||||||
|
assert template =~ "activity-bar-badge"
|
||||||
assert template =~ "tab-actions"
|
assert template =~ "tab-actions"
|
||||||
assert template =~ "tab-dirty-indicator"
|
assert template =~ "tab-dirty-indicator"
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user