Public: anyone can read this; the crew can change it.
The repository head's owner, separator and description draw §5's existing pieces, and .owner, .slash and .repo-desc leave the stylesheet #4839
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/4808-the-repository-head-s-owner-separator-an → main 4 commits fork 2 threads · 2 unresolved
4 files
changed +15
−8
docs/design/sections/5d-page-local-classes.md −2
| @@ -51,12 +51,10 @@ the same line (#4888). | ||
| 51 | 51 | - `.commit-body` |
| 52 | 52 | - `.commit-head` |
| 53 | 53 | - `.filters` |
| 54 | - `.owner` | |
| 55 | 54 | - `.page-head` |
| 56 | 55 | - `.policy-line` |
| 57 | 56 | - `.repo-actions` |
| 58 | 57 | - `.repo-desc` |
| 59 | - `.slash` | |
| 60 | 58 | - **`style/content.ts`, apps, runs, and the crew** — run pages, their logs and checks (a pull's look is §5's **look**). |
| 61 | 59 | - **`pages/board.ts`** — the board itself is §5's **board**; the spend tables `spend.ts` draws wear §5's `.table` and `.stack`, no class of their own |
| 62 | 60 | - **`studio/feed_style.ts`** — the home feed's cards and its composer (the apps rail is §5's card, row and btn; an admin's tick boxes and Archive are always shown, deliberately, with no Select toggle): |
src/web/chrome.ts +3 −3
| @@ -309,8 +309,8 @@ export function scopeHeader( | ||
| 309 | 309 | )} |
| 310 | 310 | <span |
| 311 | 311 | >${scope.kind === "local" |
| 312 | ? html`<a class="owner" href="/${owner}">${owner}</a>` | |
| 313 | : html`<span class="owner">${owner}</span>`}<span class="slash">/</span | |
| 312 | ? html`<a class="muted" href="/${owner}">${owner}</a>` | |
| 313 | : html`<span class="muted">${owner}</span>`}<span class="sep">/</span | |
| 314 | 314 | ><a href="${base}">${facts.name}</a></span |
| 315 | 315 | > |
| 316 | 316 | ${facts.visibility === "private" |
| @@ -327,7 +327,7 @@ export function scopeHeader( | ||
| 327 | 327 | ${repo === undefined ? null : audienceLine(ctx, repo, admin, base)} |
| 328 | 328 | ${when( |
| 329 | 329 | !options.compact && facts.description !== "", |
| 330 | () => html`<p class="repo-desc">${facts.description}</p>`, | |
| 330 | () => html`<p class="muted small">${facts.description}</p>`, | |
| 331 | 331 | )} |
| 332 | 332 | ${repo === undefined ? null : lineageLine(ctx.app.db, repo, parent)} |
| 333 | 333 | ${when( |
src/web/style/content.ts +2 −2
| @@ -23,12 +23,12 @@ export const content = /* css */ ` | ||
| 23 | 23 | display: flex; align-items: center; gap: 8px; flex-wrap: wrap; |
| 24 | 24 | font-size: 20px; font-weight: 620; letter-spacing: -0.02em; line-height: 1.3; |
| 25 | 25 | } |
| 26 | .repo-title .owner { font-weight: 400; color: var(--muted-fg); } | |
| 27 | .repo-title .slash { color: var(--faint); font-weight: 400; } | |
| 26 | .repo-title .muted, .repo-title .sep { font-weight: 400; } | |
| 28 | 27 | /* A phone has no hover: the settings link is a full 44px line. */ |
| 29 | 28 | @media (max-width: 640px) { |
| 30 | 29 | .repo-head .repo-desc a { display: block; padding: 12px 0; min-height: 44px; box-sizing: border-box; } |
| 31 | 30 | } |
| 31 | .repo-head p.muted.small { margin-top: 4px; text-wrap: pretty; } | |
| 32 | 32 | .repo-desc { margin-top: 4px; color: var(--muted-fg); font-size: 13px; text-wrap: pretty; } |
| 33 | 33 | .repo-head .tabs { margin-top: 12px; } |
| 34 | 34 |
test/repo-audience.test.ts +10 −1
| @@ -59,7 +59,7 @@ beforeAll(async () => { | ||
| 59 | 59 | const promoted = setRole(app, users[0]!, "olive", "member"); |
| 60 | 60 | if (!promoted.ok) throw new Error(promoted.error); |
| 61 | 61 | olive = users[1]!; |
| 62 | const r = await createRepo(app, users[1]!, { name: "yard", visibility: "public" }); | |
| 62 | const r = await createRepo(app, users[1]!, { name: "yard", visibility: "public", description: "Where the mowers sleep" }); | |
| 63 | 63 | if (!r.ok) throw new Error(String(r.error)); |
| 64 | 64 | }); |
| 65 | 65 | |
| @@ -98,3 +98,12 @@ test("the title says Public or Private once; the audience line adds no second ba | ||
| 98 | 98 | expect(pub.match(/class="badge"/g)?.length).toBe(1); |
| 99 | 99 | expect(pub).toContain(">Public</span"); |
| 100 | 100 | }); |
| 101 | ||
| 102 | test("the head draws the owner, separator and description with shared classes", async () => { | |
| 103 | const h = await head("/olive/yard"); | |
| 104 | expect(h).toContain('<a class="muted" href="/olive">olive</a>'); | |
| 105 | expect(h).toContain('<span class="sep">/</span'); | |
| 106 | expect(h).toContain('<p class="muted small">Where the mowers sleep</p>'); | |
| 107 | expect(h).not.toContain('class="owner"'); | |
| 108 | expect(h).not.toContain('class="slash"'); | |
| 109 | }); |
new above
offline
This margin rule is order-independent but still applies to any p.muted.small in .repo-head, such as the audience line once it moves to shared classes. Scope it to the description if that happens.