Public: anyone can read this; the crew can change it.
A repository tree's rows and total line draw §5's row, and .tree-row, .tree-total leave the markup #4203
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/4164-a-repository-tree-s-rows-and-total-line → main 1 commit fork
2 files
changed +52
−24
src/web/pages/repo.ts +17 −24
| @@ -346,30 +346,23 @@ export function treeListing( | ||
| 346 | 346 | const target = path === "" ? entry.name : `${path}/${entry.name}`; |
| 347 | 347 | const isTree = entry.type === "tree"; |
| 348 | 348 | rows.push( |
| 349 | html`<li> | |
| 350 | <a | |
| 351 | class="row tree-row" | |
| 352 | data-kind="${entry.type}" | |
| 353 | data-nav | |
| 354 | tabindex="-1" | |
| 355 | href="${base}/${isTree ? "tree" : "blob"}/${encoded}/${target}" | |
| 356 | > | |
| 357 | <span class="row-lead">${icon(isTree ? "folder" : "file")}</span> | |
| 358 | <span class="row-main" | |
| 359 | ><span class="row-title">${entry.name}</span></span | |
| 360 | > | |
| 361 | ${when( | |
| 362 | entry.size >= 0, | |
| 363 | () => | |
| 364 | html`<span class="row-trail muted small num" | |
| 349 | row({ | |
| 350 | href: `${base}/${isTree ? "tree" : "blob"}/${encoded}/${target}`, | |
| 351 | lead: icon(isTree ? "folder" : "file"), | |
| 352 | title: entry.name, | |
| 353 | trail: | |
| 354 | entry.size >= 0 | |
| 355 | ? html`<span class="muted small num" | |
| 365 | 356 | >${sizeWithTokens(entry.size, target)}</span |
| 366 | >`, | |
| 367 | )} | |
| 368 | </a> | |
| 369 | </li>`, | |
| 357 | >` | |
| 358 | : html``, | |
| 359 | nav: true, | |
| 360 | data: { kind: entry.type }, | |
| 361 | }), | |
| 370 | 362 | ); |
| 371 | 363 | } |
| 372 | return html`${listCard(rows)}${directoryTotal(entries)}`; | |
| 364 | const total = directoryTotal(entries); | |
| 365 | return listCard(rows, total.text.trim() === "" ? {} : { foot: total }); | |
| 373 | 366 | } |
| 374 | 367 | |
| 375 | 368 | /** |
| @@ -390,13 +383,13 @@ export function directoryTotal(entries: readonly TreeEntry[]): Html { | ||
| 390 | 383 | tokens += estimateTokens(entry.size, entry.name) ?? 0; |
| 391 | 384 | } |
| 392 | 385 | if (files === 0) return html``; |
| 393 | return html`<p | |
| 394 | class="muted small num tree-total" | |
| 386 | return html`<span | |
| 387 | class="muted small num" | |
| 395 | 388 | title="Files at this level only; subdirectories are not counted" |
| 396 | 389 | > |
| 397 | 390 | ${files.toLocaleString("en-US")} file${files === 1 ? "" : "s"} here · |
| 398 | 391 | ${fileSize(bytes)} · ${tokenCount(tokens, "estimated")} |
| 399 | </p>`; | |
| 392 | </span>`; | |
| 400 | 393 | } |
| 401 | 394 | |
| 402 | 395 | /** |
test/tree-listing.test.ts added +35
| @@ -0,0 +1,35 @@ | ||
| 1 | /** A directory listing is §5's rows and card foot, not page-local markup (#4164). */ | |
| 2 | ||
| 3 | import { describe, expect, test } from "bun:test"; | |
| 4 | import { treeListing } from "../src/web/pages/repo.ts"; | |
| 5 | ||
| 6 | const entries = [ | |
| 7 | { mode: "040000", type: "tree", sha: "a".repeat(40), size: -1, name: "src" }, | |
| 8 | { mode: "100644", type: "blob", sha: "b".repeat(40), size: 2048, name: "a<b>.md" }, | |
| 9 | { mode: "100644", type: "blob", sha: "c".repeat(40), size: 100, name: "x.ts" }, | |
| 10 | ] as const; | |
| 11 | ||
| 12 | describe("treeListing", () => { | |
| 13 | const out = treeListing("/o/r", "main", "lib", entries as never).text; | |
| 14 | ||
| 15 | test("the page-local names are gone", () => { | |
| 16 | expect(out).not.toContain("tree-row"); | |
| 17 | expect(out).not.toContain("tree-total"); | |
| 18 | }); | |
| 19 | ||
| 20 | test("rows, links, sizes and the total are all there", () => { | |
| 21 | expect(out).toContain('class="row"'); | |
| 22 | expect(out).toContain('href="/o/r/tree/main/lib/src"'); | |
| 23 | expect(out).toContain('href="/o/r/blob/main/lib/x.ts"'); | |
| 24 | expect(out).toContain('href="/o/r/tree/main"'); // .. | |
| 25 | expect(out).toContain("a<b>.md"); | |
| 26 | expect(out).toContain("row-trail"); | |
| 27 | expect(out).toContain('class="card-foot"'); | |
| 28 | expect(out).toMatch(/2 files here/); | |
| 29 | }); | |
| 30 | ||
| 31 | test("no files, no foot", () => { | |
| 32 | const none = treeListing("/o/r", "main", "", [entries[0]] as never).text; | |
| 33 | expect(none).not.toContain("card-foot"); | |
| 34 | }); | |
| 35 | }); |
new above
offline