fix(panel): equal-height conventions cards + scrollable module list

Each row of the Conventions editor grid now stretches its two cards to an equal
height (Waivers and Custom rules line up; Module boundaries and Rules line up),
and the Module-boundaries list scrolls internally so it matches the Rules card
instead of running long. Single column on mobile is unchanged.
This commit is contained in:
Renn F
2026-06-22 22:08:20 +02:00
parent 6456322746
commit c69dc914c2
2 changed files with 58 additions and 48 deletions
+1 -1
View File
@@ -27,7 +27,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
- **`ollama-init` is best-effort and gates startup on the models being present**, so a slow or unreachable model registry can no longer down a fully-cached deployment. - **`ollama-init` is best-effort and gates startup on the models being present**, so a slow or unreachable model registry can no longer down a fully-cached deployment.
- **A PM can recover its own coordination task from `needs_revision`**, and lifecycle-transition notes are kept off the human-facing `quick_context` / `dev_notes` columns. - **A PM can recover its own coordination task from `needs_revision`**, and lifecycle-transition notes are kept off the human-facing `quick_context` / `dev_notes` columns.
- **Panel:** a copyable task-id chip with a stable, non-shifting task header, clickable Branch / PR links with a branch-copy button, and clearer agent status badges. - **Panel:** a copyable task-id chip with a stable, non-shifting task header, clickable Branch / PR links with a branch-copy button, and clearer agent status badges.
- **Panel:** the per-project Conventions editor lays out in a responsive two-column grid (Module boundaries | Rules, then Waivers | Custom rules) with Recent violations full-width, inside a wider modal on large viewports — instead of one long single column. It collapses to a single column on mobile and is capped so it stays sane up to a 27" display. - **Panel:** the per-project Conventions editor lays out in a responsive two-column grid (Module boundaries | Rules, then Waivers | Custom rules) with Recent violations full-width, inside a wider modal on large viewports — instead of one long single column. Each row's two cards share an equal height, and the Module-boundaries list scrolls internally so it matches the Rules card instead of running long. It collapses to a single column on mobile and is capped so it stays sane up to a 27" display.
## [0.8.0] - 2026-06-20 ## [0.8.0] - 2026-06-20
@@ -165,7 +165,7 @@ export function ConventionsTab({ projectId }: { projectId: string }) {
const usingDefaults = status === "missing" || status === "unknown"; const usingDefaults = status === "missing" || status === "unknown";
const moduleBoundaries = ( const moduleBoundaries = (
<Card> <Card className="lg:flex lg:flex-col">
<CardHeader> <CardHeader>
<CardTitle className="text-sm">Module boundaries</CardTitle> <CardTitle className="text-sm">Module boundaries</CardTitle>
<CardDescription> <CardDescription>
@@ -173,7 +173,8 @@ export function ConventionsTab({ projectId }: { projectId: string }) {
toggle it. toggle it.
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent className="space-y-3"> <CardContent className="space-y-3 lg:flex lg:min-h-0 lg:flex-1 lg:flex-col">
<div className="space-y-3 lg:min-h-0 lg:flex-1 lg:overflow-y-auto lg:pr-1">
{standard.modules.length === 0 && ( {standard.modules.length === 0 && (
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
No modules mapped yet. No modules mapped yet.
@@ -188,7 +189,9 @@ export function ConventionsTab({ projectId }: { projectId: string }) {
<Input <Input
value={module.path} value={module.path}
placeholder="path/to/module" placeholder="path/to/module"
onChange={(e) => updateModule(index, { path: e.target.value })} onChange={(e) =>
updateModule(index, { path: e.target.value })
}
/> />
<Button <Button
variant="ghost" variant="ghost"
@@ -201,14 +204,18 @@ export function ConventionsTab({ projectId }: { projectId: string }) {
<Input <Input
value={module.purpose} value={module.purpose}
placeholder="what this module is for" placeholder="what this module is for"
onChange={(e) => updateModule(index, { purpose: e.target.value })} onChange={(e) =>
updateModule(index, { purpose: e.target.value })
}
/> />
<div className="flex flex-wrap gap-1"> <div className="flex flex-wrap gap-1">
{FORBIDDABLE_KINDS.map((kind) => ( {FORBIDDABLE_KINDS.map((kind) => (
<Badge <Badge
key={kind} key={kind}
variant={ variant={
module.forbidden.includes(kind) ? "destructive" : "outline" module.forbidden.includes(kind)
? "destructive"
: "outline"
} }
className="cursor-pointer" className="cursor-pointer"
onClick={() => toggleForbidden(index, kind)} onClick={() => toggleForbidden(index, kind)}
@@ -219,6 +226,7 @@ export function ConventionsTab({ projectId }: { projectId: string }) {
</div> </div>
</div> </div>
))} ))}
</div>
<Button variant="outline" size="sm" onClick={addModule}> <Button variant="outline" size="sm" onClick={addModule}>
Add module Add module
</Button> </Button>
@@ -430,9 +438,11 @@ export function ConventionsTab({ projectId }: { projectId: string }) {
)} )}
{/* Two columns on wide viewports so the modal isn't a long single column; {/* Two columns on wide viewports so the modal isn't a long single column;
Module boundaries | Rules, then Waivers | Custom rules. Each cell keeps Module boundaries | Rules, then Waivers | Custom rules. items-stretch
its natural height (items-start) and stacks to one column on mobile. */} makes each row's two cards equal height; Module boundaries scrolls
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2 lg:items-start"> internally (below) so it matches Rules instead of running long. Stacks
to one column on mobile. */}
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2 lg:items-stretch">
{moduleBoundaries} {moduleBoundaries}
{rules} {rules}
{waivers} {waivers}