Add a jump-to-newest button and highlight the row you came from

4b717b0 · Gabriel Morell · 2026-09-09 20:29

3 files +90 -12
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}&amp;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}&amp;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