neiam /gitgud
Git Gud
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Packages
⭐
Log in to mark this repository.
Add a jump-to-newest button and highlight the row you came from
4b717b0 · Gabriel Morell · 2026-09-09 20:29
Message
{commit_body(@commit)}
Files changed
modified
lib/git_gud_web/live/repo_live/blob.ex
+46
−11
@@ -126,6 +126,7 @@ defmodule GitGudWeb.RepoLive.Blob do
| 126 | 126 | |> assign(:current_version, nil) |
| 127 | 127 | |> assign(:older_version, nil) |
| 128 | 128 | |> assign(:newer_version, nil) |
| 129 | + |> assign(:newest_version, nil) | |
| 129 | 130 | end |
| 130 | 131 | |
| 131 | 132 | defp assign_versions(socket, versions, idx) do |
@@ -135,6 +136,7 @@ defmodule GitGudWeb.RepoLive.Blob do
| 135 | 136 | |> assign(:current_version, Enum.at(versions, idx)) |
| 136 | 137 | |> assign(:older_version, Enum.at(versions, idx + 1)) |
| 137 | 138 | |> assign(:newer_version, idx > 0 && Enum.at(versions, idx - 1)) |
| 139 | + |> assign(:newest_version, idx > 0 && Enum.at(versions, 0)) | |
| 138 | 140 | end |
| 139 | 141 | |
| 140 | 142 | # A full page of results probably means there are more behind it. |
@@ -143,6 +145,16 @@ defmodule GitGudWeb.RepoLive.Blob do
| 143 | 145 | |
| 144 | 146 | defp version_count(versions), do: length(versions) |
| 145 | 147 | |
| 148 | + # `at` tells the history page which revision we came from so it can | |
| 149 | + # highlight that row; the fragment scrolls to it on a full page load. | |
| 150 | + defp history_path(handle, repo, segs, from, nil), | |
| 151 | + do: ~p"/r/#{handle}/#{repo.name}/commits/#{from}/#{segs}" | |
| 152 | + | |
| 153 | + defp history_path(handle, repo, segs, from, version) do | |
| 154 | + ~p"/r/#{handle}/#{repo.name}/commits/#{from}/#{segs}?#{[at: version.id]}" <> | |
| 155 | + "#commits-#{version.id}" | |
| 156 | + end | |
| 157 | + | |
| 146 | 158 | # `pager` rides along so stepping through versions doesn't collapse the |
| 147 | 159 | # pager you just opened. |
| 148 | 160 | defp version_path(handle, repo, segs, sha, from, pager?) do |
@@ -203,7 +215,7 @@ defmodule GitGudWeb.RepoLive.Blob do
| 203 | 215 | <span class="flex items-center gap-3"> |
| 204 | 216 | <span class="join"> |
| 205 | 217 | <.link |
| 206 | − navigate={~p"/r/#{@handle}/#{@repo.name}/commits/#{@from_ref}/#{@path_segs}"} | |
| 218 | + navigate={history_path(@handle, @repo, @path_segs, @from_ref, @current_version)} | |
| 207 | 219 | class="btn btn-xs join-item" |
| 208 | 220 | > |
| 209 | 221 | History |
@@ -257,16 +269,39 @@ defmodule GitGudWeb.RepoLive.Blob do
| 257 | 269 | </p> |
| 258 | 270 | </div> |
| 259 | 271 | |
| 260 | − <.link | |
| 261 | − :if={@newer_version} | |
| 262 | − navigate={ | |
| 263 | − version_path(@handle, @repo, @path_segs, @newer_version.id, @from_ref, @show_pager?) | |
| 264 | − } | |
| 265 | − class="btn btn-xs" | |
| 266 | − > | |
| 267 | − Newer → | |
| 268 | − </.link> | |
| 269 | − <span :if={!@newer_version} class="btn btn-xs btn-disabled">Newer →</span> | |
| 272 | + <span class="join shrink-0"> | |
| 273 | + <.link | |
| 274 | + :if={@newer_version} | |
| 275 | + navigate={ | |
| 276 | + version_path(@handle, @repo, @path_segs, @newer_version.id, @from_ref, @show_pager?) | |
| 277 | + } | |
| 278 | + class="btn btn-xs join-item" | |
| 279 | + > | |
| 280 | + Newer → | |
| 281 | + </.link> | |
| 282 | + <span :if={!@newer_version} class="btn btn-xs btn-disabled join-item">Newer →</span> | |
| 283 | + | |
| 284 | + <.link | |
| 285 | + :if={@newest_version} | |
| 286 | + navigate={ | |
| 287 | + version_path( | |
| 288 | + @handle, | |
| 289 | + @repo, | |
| 290 | + @path_segs, | |
| 291 | + @newest_version.id, | |
| 292 | + @from_ref, | |
| 293 | + @show_pager? | |
| 294 | + ) | |
| 295 | + } | |
| 296 | + title="Jump to the newest version" | |
| 297 | + class="btn btn-xs join-item" | |
| 298 | + > | |
| 299 | + Newest →| | |
| 300 | + </.link> | |
| 301 | + <span :if={!@newest_version} class="btn btn-xs btn-disabled join-item"> | |
| 302 | + Newest →| | |
| 303 | + </span> | |
| 304 | + </span> | |
| 270 | 305 | </div> |
| 271 | 306 | |
| 272 | 307 | <article class="border border-base-300 rounded-md overflow-hidden"> |
modified
lib/git_gud_web/live/repo_live/log.ex
+12
−1
@@ -15,6 +15,9 @@ defmodule GitGudWeb.RepoLive.Log do
| 15 | 15 | |
| 16 | 16 | # `*path` scopes the walk to one file or directory (GitHub's |
| 17 | 17 | # /commits/<ref>/<path>). Absent on the plain branch history route. |
| 18 | + # `at` is the revision the blob view came from, so we can point the | |
| 19 | + # user at the row they clicked out of. | |
| 20 | + highlight = params["at"] | |
| 18 | 21 | path_segs = params |> Map.get("path") |> List.wrap() |
| 19 | 22 | path = Enum.join(path_segs, "/") |
| 20 | 23 |
@@ -40,6 +43,7 @@ defmodule GitGudWeb.RepoLive.Log do
| 40 | 43 | |> assign(:ref, ref) |
| 41 | 44 | |> assign(:path, path) |
| 42 | 45 | |> assign(:path_segs, path_segs) |
| 46 | + |> assign(:highlight, highlight) | |
| 43 | 47 | |> assign(:head_sha, head_sha) |
| 44 | 48 | |> assign(:skip, 0) |
| 45 | 49 | |> stream(:commits, Enum.map(commits, &decorate/1)) |
@@ -110,7 +114,14 @@ defmodule GitGudWeb.RepoLive.Log do
| 110 | 114 | </p> |
| 111 | 115 | |
| 112 | 116 | <ul id="commits" phx-update="stream" class="divide-y divide-base-300"> |
| 113 | − <li :for={{id, c} <- @streams.commits} id={id} class="py-3"> | |
| 117 | + <li | |
| 118 | + :for={{id, c} <- @streams.commits} | |
| 119 | + id={id} | |
| 120 | + class={[ | |
| 121 | + "py-3 px-2 -mx-2 rounded scroll-mt-4", | |
| 122 | + c.id == @highlight && "bg-base-200 ring-1 ring-primary/40" | |
| 123 | + ]} | |
| 124 | + > | |
| 114 | 125 | <div class="flex items-baseline justify-between gap-3"> |
| 115 | 126 | <.link |
| 116 | 127 | navigate={~p"/r/#{@handle}/#{@repo.name}/commit/#{c.id}"} |
modified
test/git_gud_web/live/repo_live/log_path_test.exs
+32
−0
@@ -150,6 +150,38 @@ defmodule GitGudWeb.RepoLive.LogPathTest do
| 150 | 150 | assert html =~ "/blob/#{newest}/a.txt?from=#{branch}&pager=1" |
| 151 | 151 | end |
| 152 | 152 | |
| 153 | + test "the pager jumps straight to the newest version", %{conn: conn, repo: repo, base: base} do | |
| 154 | + [newest, oldest] = versions_of(repo, "a.txt") | |
| 155 | + branch = repo.default_branch | |
| 156 | + | |
| 157 | + {:ok, _lv, html} = live(conn, "#{base}/blob/#{oldest}/a.txt?from=#{branch}&pager=1") | |
| 158 | + assert html =~ "Newest →|" | |
| 159 | + assert html =~ "/blob/#{newest}/a.txt?from=#{branch}&pager=1" | |
| 160 | + | |
| 161 | + # Already newest: the buttons are there but lead nowhere. | |
| 162 | + {:ok, lv, html} = live(conn, "#{base}/blob/#{newest}/a.txt?from=#{branch}&pager=1") | |
| 163 | + assert html =~ "Newest →|" | |
| 164 | + refute lv |> element("a[href*='/blob/#{newest}/a.txt']") |> has_element?() | |
| 165 | + end | |
| 166 | + | |
| 167 | + test "history highlights the revision we clicked out of", %{ | |
| 168 | + conn: conn, | |
| 169 | + repo: repo, | |
| 170 | + base: base | |
| 171 | + } do | |
| 172 | + [newest, oldest] = versions_of(repo, "a.txt") | |
| 173 | + branch = repo.default_branch | |
| 174 | + | |
| 175 | + # The blob view hands the history page the version it was showing. | |
| 176 | + {:ok, _lv, html} = live(conn, "#{base}/blob/#{oldest}/a.txt?from=#{branch}&pager=1") | |
| 177 | + assert html =~ "/commits/#{branch}/a.txt?at=#{oldest}#commits-#{oldest}" | |
| 178 | + | |
| 179 | + {:ok, lv, _html} = live(conn, "#{base}/commits/#{branch}/a.txt?at=#{oldest}") | |
| 180 | + | |
| 181 | + assert lv |> element("li#commits-#{oldest}.bg-base-200") |> has_element?() | |
| 182 | + refute lv |> element("li#commits-#{newest}.bg-base-200") |> has_element?() | |
| 183 | + end | |
| 184 | + | |
| 153 | 185 | test "a commit outside the file's history gets no pager", %{ |
| 154 | 186 | conn: conn, |
| 155 | 187 | repo: repo, |
Parents: cab81d0