From b31492e7ae857b855e1d9ce079868cca91d9ef50 Mon Sep 17 00:00:00 2001 From: rahul Date: Wed, 26 Aug 2026 18:47:57 +0530 Subject: [PATCH] Keep model labels inside the plot, not out in the axis gutter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The board's viewBox is extended left by GUTTER so the axis tick labels and the rotated title can hang there. The label placement search was bounded by that same extended edge, so a point label was free to land in the gutter too, well outside the plot frame. With every model selected, Claude Opus 5 and Claude Sonnet 4.6 ended up floating to the left of the chart. Point labels are now held to the plot column. Only the axis hangs. The fallback path had no horizontal bound at all, so it takes whichever side fits and drops the label if neither does, rather than placing it outside. This survived earlier checks because those measured labels against the svg element, whose bounds include the gutter by design — a label sitting out there was inside the element and counted as fine. The check now measures against the plot frame, taken from the gridlines the dots actually sit in. Against the merged code it fails on exactly the two labels reported; with this change it passes at 390 through 1920, in the default view and with all 30 selected, with no label overlaps and no model left unlabelled. Co-Authored-By: Claude Fable 5 --- .../charts/reliability-by-model.tsx | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/docs/components/charts/reliability-by-model.tsx b/docs/components/charts/reliability-by-model.tsx index 0515650e5..33d045a10 100644 --- a/docs/components/charts/reliability-by-model.tsx +++ b/docs/components/charts/reliability-by-model.tsx @@ -334,6 +334,12 @@ export function ReliabilityByModel({ const offsets = [0]; for (let step = 1; step <= 26; step++) offsets.push(step * 13, -step * 13); + /* The gutter belongs to the axis. Its width is spent on the tick labels + and the rotated axis title, so a model label placed out there reads as + floating outside the chart rather than sitting beside its dot. Point + labels are held inside the plot column; only the axis hangs. */ + const insidePlot = (left: number, right: number) => left >= PL && right <= PL + PW; + return visibleBoardPoints .filter((point) => labelledBoardIds.has(point.id)) .sort((a, b) => b.score - a.score || modelBoardCostPerTask(a) - modelBoardCostPerTask(b)) @@ -350,7 +356,7 @@ export function ReliabilityByModel({ const lx = px + (anchor === "start" ? gap + dotRadius : -(gap + dotRadius)); const left = anchor === "start" ? lx : lx - labelWidth; const right = anchor === "start" ? lx + labelWidth : lx; - if (left < PL - GUTTER || right > PL + PW) continue; + if (!insidePlot(left, right)) continue; /* baseline that centres the text on the dot, then the offset */ const ly = py + labelFontSize * 0.35 + offset; if (ly - labelFontSize < boardTop || ly > boardBottom) continue; @@ -368,11 +374,23 @@ export function ReliabilityByModel({ /* Every slot in range was taken. Climb from the plot floor looking for one last gap, measuring the box on the side the text actually runs. */ - const anchor = preferred; - const lx = px + (anchor === "start" ? gap + dotRadius : -(gap + dotRadius)); + const edges = (side: "start" | "end") => { + const x = px + (side === "start" ? gap + dotRadius : -(gap + dotRadius)); + return side === "start" + ? { lx: x, left: x, right: x + labelWidth } + : { lx: x, left: x - labelWidth, right: x }; + }; + /* the fallback has to respect the plot edge too, so take whichever + side fits; if neither does, this label has nowhere to go */ + const fit = [preferred, preferred === "start" ? "end" : "start"] + .map((side) => edges(side as "start" | "end")) + .find((e) => insidePlot(e.left, e.right)); + if (!fit) return null; + const anchor: "start" | "end" = fit.left === fit.lx ? "start" : "end"; + const lx = fit.lx; const boxAt = (y: number) => ({ - left: (anchor === "start" ? lx : lx - labelWidth) - 4, - right: (anchor === "start" ? lx + labelWidth : lx) + 4, + left: fit.left - 4, + right: fit.right + 4, top: y - labelFontSize - 3, bottom: y + 4, });