feat: git sidebar shows the commit history in its lower half like the GUI (issue #30)

This commit is contained in:
2026-07-16 22:10:27 +02:00
parent 5433cb59ac
commit 46fd6c1b85
3 changed files with 69 additions and 17 deletions

View File

@@ -11,9 +11,9 @@ defmodule BDS.TUI do
Sidebar focus: `↑/↓`/`j/k` navigate, `enter` open, `n` new post,
`1..5` switch view (posts/media/templates/scripts/tags), `7` git panel
(changed files in the sidebar, scrollable whole-folder diff in the main
area; `c` commit all, `u` pull, `s` push, enter jumps the diff to the
selected file), `r` refresh,
(changed files plus commit history in the sidebar, scrollable
whole-folder diff in the main area; `c` commit all, `u` pull, `s` push,
enter jumps the diff to the selected file), `r` refresh,
`p` projects overlay (switch the active project or open an existing
blog folder by path, with bash-style tab completion — opening one
queues a full database rebuild), `/` vi-style search that live-filters
@@ -901,7 +901,27 @@ defmodule BDS.TUI do
]
end
defp sidebar_widgets(state, rect) do
# The git sidebar mirrors the GUI: changed files on top, commit history
# in the lower half (↑ needs push, ↓ remote-only, blank when synced).
defp sidebar_widgets(%{view: "git", sidebar: %{git_state: "active"} = sidebar} = state, rect) do
[changes_rect, history_rect] =
Layout.split(rect, :vertical, [{:percentage, 50}, {:percentage, 50}])
history_items = Enum.map(sidebar.history_entries, &history_line/1)
[
sidebar_list_widget(state, changes_rect),
{%List{
items: history_items,
selected: nil,
block: %Block{title: dgettext("ui", "History"), borders: [:all]}
}, history_rect}
]
end
defp sidebar_widgets(state, rect), do: [sidebar_list_widget(state, rect)]
defp sidebar_list_widget(state, rect) do
items =
Enum.map(state.items, fn
{:header, title} -> "── #{title}"
@@ -910,7 +930,6 @@ defmodule BDS.TUI do
focused? = state.focus == :sidebar
[
{%List{
items: items,
selected: list_selected(items, state.selected),
@@ -921,7 +940,17 @@ defmodule BDS.TUI do
),
block: %Block{title: sidebar_title(state), borders: [:all]}
}, rect}
]
end
defp history_line(entry) do
marker =
case entry.sync_status do
"local_only" -> ""
"remote_only" -> ""
_synced -> " "
end
marker <> entry.short_hash <> " " <> to_string(entry.subject || "")
end
defp sidebar_title(%{view: "git", sidebar: %{git_state: "active"} = sidebar}) do

View File

@@ -114,7 +114,9 @@ rule GitPanel {
when: TuiKeyPressed(code: "7")
-- "7" opens the git panel (issue #30) for content sync: the sidebar
-- lists the changed files from git status (code + path, branch with
-- ahead/behind counts in the title), the main area shows a
-- ahead/behind counts in the title) with the commit history in its
-- lower half, like the GUI (short hash + subject; ↑ marks commits
-- that still need a push, ↓ remote-only ones), the main area shows a
-- scrollable whole-folder diff (staged + unstaged, capped) paged
-- with pgup/pgdn; enter on a file jumps the diff to it. "c" opens a
-- status-line commit prompt (git add -A + commit, empty messages

View File

@@ -800,6 +800,27 @@ defmodule BDS.TUITest do
assert_receive {:git_result, :push, {:error, _reason}}, 5_000
end
test "the sidebar shows the commit history in its lower half", %{project: project} do
dir = BDS.Projects.project_data_dir(project)
init_repo!(dir)
state = mount!() |> press("7")
assert [entry | _] = state.sidebar.history_entries
assert entry.subject == "initial"
text = screen_text(state, 140, 40)
assert text =~ "History"
# No remote exists, so the commit is local-only and marked for push.
assert text =~ "#{entry.short_hash} initial"
# A commit from the panel shows up in the history immediately.
modify!(dir, "tracked.md", "changed\n")
state = state |> press("r") |> press("c") |> type("sync commit") |> press("enter")
assert screen_text(state, 140, 40) =~ "sync commit"
end
test "git keys in a non-repo project only report", %{project: project} do
state = mount!() |> press("7")