Redesign PR conversation page to match the issue page's design language (header chip, markdown bodies, merged state) #521
Labels
No labels
actions
bug
cli
duplicate
enhancement
fork
forum
git storage
help wanted
insights
invalid
issues
moderation
oidc
ownership transfer
packages
pr/merge protection rules
projects
pull requests
question
releases
sponsorships
tags
webhooks
wiki
wontfix
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
crueber/walhub#521
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What's requested
Redesign the PR conversation page (
web/src/pages/Pull.jsx) to match the design language the issue page already establishes (web/src/pages/Issue.jsx), so the two conversation surfaces read as siblings. Three areas: page structure/state display, PR-body rendering, and open/merged state consistency.Reference implementation: the issue page (
web/src/pages/Issue.jsx), which is the canonical pattern for this app's conversation surfaces: anissue-pagegrid (Issue.jsx:422), a bordered header block with#num titleheading and a state chip (Issue.jsx:434–442), a muted byline, and a right sidebar of grouped metadata cards (Labels / Assignees / Milestone, Issue.jsx:549+).Evidence (static read of the current tree)
1. Header/state display diverges from the issue page
<h1>#{num} {title}</h1>followed by a plain text line{thread()?.state} · {author} · {date}— the state renders as lowercase prose, not the app's state chip. Issue.jsx:436–442 pairs the same heading withchip chip-open/chip chip-closedand aborder-bheader block.web/src/ui.css(lines ~108–116) defineschip-open,chip-closed,chip-draft,chip-prerelease— but nochip-merged. The PR list (Pulls.jsx:124) already emitschip chip-${pr.state}, so a merged PR renders a chip with an unstyled class (the known unstyled-class failure mode). The conversation page needs a merged chip and so does the list.2. PR-body rendering is inconsistent
renderBody(markdown + DOMPurify) via ThreadTimeline.jsx:83 — correct.ThreadComments) render rawwhitespace-pre-wraptext — no markdown, unlike the issue thread. Staged line comments in the finish-review modal (Pull.jsx:514) are plain text, acceptable as a draft preview but the submitted review body is not.openedevent row inside the timeline; there is no dedicated, styled PR-body block like the issue page's first-comment treatment (Issue.jsx:430–433 describes the intended pattern: number + title header with byline, body as first comment).3. Page structure
grid gap-6 lg:grid-cols-[1fr_320px]rather than the issue page'sissue-pageidiom; the Review summary / Reviews / Reviewers / Mergeability / Checks cards carry ad-hoch2 text-smheadings at mixed hierarchy with no unified card-header treatment, and the ReviewSummaryBar floats between header and timeline rather than composing into the header/sidebar structure.Architecture notes
ui.css); addingchip-mergedthere covers the list page for free.chip-${state}interpolation in Pulls.jsx:124 needs the CSS rule to exist or a mapping formerged.renderBodyfromweb/src/lib/render-md.jslike the timeline does. Review bodies carry the repo context for the autolinker the same way Pull.jsx:668 already passesmdCtx.Acceptance criteria
#num titleheading, state chip (open/closed/merged) beside the title, muted byline below.chip-mergeddefined inui.cssconsistent with the other chips (purple or the app's chosen merged hue, light + dark), and merged PRs render it on both the conversation page and the pulls list (chip-${state}path).renderBodywith the same mdCtx the timeline gets; PR description renders as a styled first-comment body, not just a timeline row.anchorContextShauntouched), or the ThreadTimeline contract; existing markdown/DOMPurify behavior on comment bodies unchanged.Fix ready for review: #528 (branch fix/issue-521). Client-only, no wire change, ThreadTimeline untouched; full node --test 1191/1190 (1 pre-existing live-server smoke failure, verified head-to-head); vite + esbuild green; no new deps.
Review of PR #528 (fix/issue-521) — verified in scratch worktree @
f1caa05No browser used (per review instructions: node tests + reasoning; browser proof explicitly open). No .go touched, no live instance touched.
(1) Header idiom — PASS
Pull.jsx:754-766: bordered header block (
mb-4 border-b ... pb-3), h1 with muted#numspan + title, single badge right (badge().cls, exactly 1 surface — no stacked second badge), muted byline author · date below. Matches Issue.jsx:434-449 idiom (h1-vs-h2 level difference is correct: page title vs card titles). #517 badge unified viapullBadgeView(open/closed/merged) — no double badge.(2) chip-merged — PASS (pre-existing from #517, not this PR)
chip-mergedalready ships in main's ui.css:114 (purple light+dark). This PR only uses it via the #517 badge path. Nothing to add.(3) LIST merged chip — DECLINE VERIFIED CORRECT, contradiction in the issue itself
Claim checked against source:
PROut(internal/pulls/service.go:731-741) carriesNum/Title/State/Author/BaseRef/HeadRef/HeadSHA/Draft/UpdatedAtonly — no merged signal.ListPRs(service.go:801-805) loads each sidecar (pr.Mergedin hand) but drops it;Statecomes from the index Card (open|closed). So merged vs plain-closed are indistinguishable on the list. Wire-clean alternatives: per-rowGET pulls/{num}= N+1 round trips (violates laws 4+6); inferring merged fromstate==closed= wrong. A list chip genuinely needs a wire change (addmergedto PROut), which criterion 6 forbids. Ruling: declining is the right call — criteria "list merged chip" and "no wire change" are mutually exclusive given the PROut shape, and the issue's "all inputs already in the payloads" premise is wrong for the list payload (true only for the conversation payload). Thechip-mergedCSS stands ready. Recommend: close #521 on the conversation-page scope (all achievable criteria met) + open a follow-up ticket forPROut.mergedwire addition + list chip; or keep #521 open solely for the list part — maintainer's call.(4) Markdown bodies — PASS
Review bodies (Pull.jsx:146) + thread comments (Pull.jsx:482) go through
renderBodywith one shared repomdCtx(Pull.jsx:728, the #340 contract); exactly onewhitespace-pre-wrapsite remains — the finish-review modal staged-line draft preview (Pull.jsx:526), plain by design. Timeline bodies unchanged.(5) PR description first-comment — PASS, reasoning sound
PRDescription(Pull.jsx:30-49) renders livepr.body, hidden on empty (never an empty box), byline from the opened event's actor/at with thread fallback. Stale-row claim verified: opened event carries ORIGINAL body (service.go:406Body: strPtr(in.Body), seq 0);UpdatePR(§8 PUT) writes pr.json only, appends events solely for title/state — so the old timeline body row would go stale AND duplicate.pullEventTextopened→system row keeps the event-0 anchor (ThreadTimeline.jsx:62,74id=event-{seq}untouched) — deep links hold.(6) Structure — PASS
issue-page grid gap-4 md:grid-cols-[1fr_16rem]+min-w-0main (Pull.jsx:747, byte-identical to Issue.jsx:422); sidebargrid content-start gap-3aria-label "Details" with ReviewSummaryBar composed first (Pull.jsx:868-869); one.card-header(ui.css:128) on all 8 titles incl. MergeBox.jsx:159; Checks composesflex items-center gap-2alongside. No straymb-2 text-sm font-semiboldh2 remains.(7) Untouched contracts — PASS
ThreadTimeline.jsx: zero diff.
anchorContextSha: zero diff hits.pullEventTextmoved verbatim from Pull.jsx (sole delta: opened→system row, as designed); no localeventTextfork remains.(8) #517 no-regress — PASS
pullBadgeView/pullCloseVisibilityuntouched (diff only addspullEventText); Close/Reopen/comment-and-close wiring intact (Pull.jsx:706-737,789-794);pull-state-517tests pass.(9) Laws/docs — PASS
Law 1: no new deps (no package.json/pnpm change). Law 7: no task/async change (pure render). Law 8: no new seams (no registry additions). Law 12: decision appended to docs/go/12_web_ui.md; doc claims spot-checked true (chip-merged pre-existence, PROut shape, UpdatePR behavior, opened-event body). No wire change (no .go files in diff).
Tests (scratch worktree, node_modules symlinked from main)
vite build: green (2.58s).MERGE RECOMMENDATION: ready to merge
Only open question is the process call on criterion (3): accept the decline (recommend close #521 + follow-up ticket for PROut.merged) or keep #521 open for the list part. Either way, #528 itself is correct and complete — do not hold it for the wire change.
Fixed by PR #528 (review clean — all 9 checks pass; list-chip scoped to follow-up #530 since it needs a wire change), merged. Closing.