A commit's hover hint is its subject, cut on a word past 160 characters (commitHint, lib/commit-message.ts): on flagon-io/g1t's Files list a commit whose subject runs to a paragraph filled the screen with its tooltip. The Files list, Branches, Compare and Tags each use it; Branches, Compare and Tags showed the whole message, body and trailers too. The commit's page has it all. Checked: apps/web tsc -b; npm test (988; new: a commit's hint).
- G1t-Session:
- ses_01m501jq3vetja2s4a904rs068
6 files+37−50/6 viewed
| 59 | 59 | import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./ui/table"; | |
| 60 | 60 | import { PullIcon } from "./work-icons"; | |
| 61 | 61 | import { isMerged } from "../lib/branches"; | |
| 62 | + | import { commitHint } from "../lib/commit-message"; | |
| 62 | 63 | ||
| 63 | 64 | /** What the page's action answers. */ | |
| 64 | 65 | export type BranchActionData = { intent: string; branch?: string; hash?: string; error?: string }; | |
| ⋯ | |||
| 149 | 150 | // browser, and the page then fails to hydrate. | |
| 150 | 151 | <span className="inline-flex items-center gap-1.5 whitespace-nowrap"> | |
| 151 | 152 | <CommitAvatars commit={row.commit} size={18} max={2} /> | |
| 152 | − | <Hint label={row.commit.message}> | |
| 153 | + | <Hint label={commitHint(row.commit.message)}> | |
| 153 | 154 | <Link to={`${base}/commit/${row.commit.hash}`} className="text-muted hover:text-fg"> | |
| 154 | 155 | <TimeAgo at={row.commit.at} /> | |
| 155 | 156 | </Link> | |
| 15 | 15 | import { CodeLines } from "./code-lines"; | |
| 16 | 16 | ||
| 17 | 17 | import { useAddresses } from "../lib/addresses"; | |
| 18 | + | import { commitHint } from "../lib/commit-message"; | |
| 18 | 19 | import { type Failed, isFailed } from "../lib/partial"; | |
| 19 | 20 | import { Boundary, Unavailable } from "./boundary"; | |
| 20 | 21 | import { isImagePath } from "../lib/usercontent"; | |
| ⋯ | |||
| 321 | 322 | </Link> | |
| 322 | 323 | <span className="hidden min-w-0 sm:block"> | |
| 323 | 324 | {commit ? ( | |
| 324 | − | <Hint label={commit.message.split("\n")[0]}> | |
| 325 | + | <Hint label={commitHint(commit.message)}> | |
| 325 | 326 | <span className="block animate-fade-in truncate text-muted"> | |
| 326 | 327 | <CommitText text={commit.message.split("\n")[0] ?? ""} base={base} to={`${base}/commit/${commit.hash}`} linkClassName="hover:text-fg hover:underline" /> | |
| 327 | 328 | </span> | |
| 1 | 1 | import assert from "node:assert/strict"; | |
| 2 | 2 | import { test } from "node:test"; | |
| 3 | 3 | ||
| 4 | − | import { messageParts, parseCommitMessage } from "./commit-message.ts"; | |
| 4 | + | import { HINT_LIMIT, commitHint, messageParts, parseCommitMessage } from "./commit-message.ts"; | |
| 5 | 5 | ||
| 6 | 6 | test("co-authors leave the body and keep only their names", () => { | |
| 7 | 7 | const parsed = parseCommitMessage( | |
| ⋯ | |||
| 69 | 69 | assert.deepEqual(messageParts(text), [{ kind: "text", text }], text); | |
| 70 | 70 | } | |
| 71 | 71 | }); | |
| 72 | + | ||
| 73 | + | test("a commit's hint is its subject, cut on a word when it runs long", () => { | |
| 74 | + | assert.equal(commitHint("Fix the login\n\nBecause it broke."), "Fix the login"); | |
| 75 | + | assert.equal(commitHint("\n\nOnly a body line"), "Only a body line"); | |
| 76 | + | const long = `Sessions show by who is looking: ${"word ".repeat(80)}end`; | |
| 77 | + | const hint = commitHint(long); | |
| 78 | + | assert.ok(hint.length <= HINT_LIMIT, `${hint.length}`); | |
| 79 | + | assert.ok(hint.endsWith("…")); | |
| 80 | + | assert.ok(hint.startsWith("Sessions show by who is looking:")); | |
| 81 | + | assert.ok(!/\s…$/.test(hint)); | |
| 82 | + | assert.equal(commitHint("x".repeat(400)).length, HINT_LIMIT); | |
| 83 | + | }); | |
| 69 | 69 | if (at < text.length) parts.push({ kind: "text", text: text.slice(at) }); | |
| 70 | 70 | return parts; | |
| 71 | 71 | } | |
| 72 | + | ||
| 73 | + | /** The most of a commit's subject a hover hint shows. */ | |
| 74 | + | export const HINT_LIMIT = 160; | |
| 75 | + | ||
| 76 | + | /** | |
| 77 | + | * A commit message as a hover hint: its subject, cut on a word with `…` | |
| 78 | + | * past [HINT_LIMIT] characters. Some subjects run to a paragraph, and a | |
| 79 | + | * hint is a glance, not the message: the commit's page has it all. | |
| 80 | + | */ | |
| 81 | + | export function commitHint(message: string, limit = HINT_LIMIT): string { | |
| 82 | + | const subject = (message.split("\n").find((line) => line.trim()) ?? "").trim(); | |
| 83 | + | if (subject.length <= limit) return subject; | |
| 84 | + | const cut = subject.slice(0, limit - 1); | |
| 85 | + | const space = cut.lastIndexOf(" "); | |
| 86 | + | return `${(space > limit / 2 ? cut.slice(0, space) : cut).replace(/[\s,;:.–—-]+$/, "")}…`; | |
| 87 | + | } |
| 11 | 11 | import { Hint } from "../../components/ui/hint"; | |
| 12 | 12 | import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../../components/ui/select"; | |
| 13 | 13 | import { type HighlightedComparison, firstScreens } from "../../lib/diff"; | |
| 14 | + | import { commitHint } from "../../lib/commit-message"; | |
| 14 | 15 | import { page } from "../../lib/meta"; | |
| 15 | 16 | import { showCommits } from "../../lib/commit-people.server"; | |
| 16 | 17 | import { repos } from "../../lib/services.server"; | |
| ⋯ | |||
| 161 | 162 | <li key={commit.hash} className="flex items-center gap-3 px-4 py-2.5 text-sm"> | |
| 162 | 163 | <GitCommitHorizontal size={15} className="shrink-0 text-faint" /> | |
| 163 | 164 | <CommitAvatars commit={commit} size={16} max={2} /> | |
| 164 | − | <Hint label={commit.message}> | |
| 165 | + | <Hint label={commitHint(commit.message)}> | |
| 165 | 166 | <span className="min-w-0 grow truncate"> | |
| 166 | 167 | <CommitText text={commit.message} base={repoBase} to={`${repoBase}/commit/${commit.hash}`} linkClassName="hover:text-accent" /> | |
| 167 | 168 | </span> | |
| 8 | 8 | import { EmptyState, TimeAgo } from "../../components/ui"; | |
| 9 | 9 | import { Card } from "../../components/ui/card"; | |
| 10 | 10 | import { Hint } from "../../components/ui/hint"; | |
| 11 | + | import { commitHint } from "../../lib/commit-message"; | |
| 11 | 12 | import { page } from "../../lib/meta"; | |
| 12 | 13 | import { commitChecksFor } from "../../lib/commit-checks.server"; | |
| 13 | 14 | import { showCommits } from "../../lib/commit-people.server"; | |
| ⋯ | |||
| 66 | 67 | <CommitNames commit={tag.commit} className="hover:text-fg" /> | |
| 67 | 68 | </span> | |
| 68 | 69 | <span className="text-faint">·</span> | |
| 69 | − | <Hint label={tag.commit.message}> | |
| 70 | + | <Hint label={commitHint(tag.commit.message)}> | |
| 70 | 71 | <span className="min-w-0 truncate"> | |
| 71 | 72 | <CommitText text={tag.commit.message.split("\n")[0] ?? ""} base={base} to={`${base}/commit/${tag.commit.hash}`} linkClassName="hover:text-fg" /> | |
| 72 | 73 | </span> | |
Comments
Sign in to comment.