From 46fd6c1b85101d2b5206d3602d37953276b88672 Mon Sep 17 00:00:00 2001 From: Chili Palmer Date: Thu, 16 Jul 2026 22:10:27 +0200 Subject: [PATCH] feat: git sidebar shows the commit history in its lower half like the GUI (issue #30) --- lib/bds/tui.ex | 61 +++++++++++++++++++++++++++++++------------ specs/tui.allium | 4 ++- test/bds/tui_test.exs | 21 +++++++++++++++ 3 files changed, 69 insertions(+), 17 deletions(-) diff --git a/lib/bds/tui.ex b/lib/bds/tui.ex index 9237dbf..68b36df 100644 --- a/lib/bds/tui.ex +++ b/lib/bds/tui.ex @@ -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,18 +930,27 @@ defmodule BDS.TUI do focused? = state.focus == :sidebar - [ - {%List{ - items: items, - selected: list_selected(items, state.selected), - highlight_style: - if(focused?, - do: %Style{fg: :black, bg: :cyan}, - else: %Style{modifiers: [:bold]} - ), - block: %Block{title: sidebar_title(state), borders: [:all]} - }, rect} - ] + {%List{ + items: items, + selected: list_selected(items, state.selected), + highlight_style: + if(focused?, + do: %Style{fg: :black, bg: :cyan}, + else: %Style{modifiers: [:bold]} + ), + 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 diff --git a/specs/tui.allium b/specs/tui.allium index db1e9fd..72abc8a 100644 --- a/specs/tui.allium +++ b/specs/tui.allium @@ -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 diff --git a/test/bds/tui_test.exs b/test/bds/tui_test.exs index e004232..0586b9f 100644 --- a/test/bds/tui_test.exs +++ b/test/bds/tui_test.exs @@ -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")