fix: stabilize conclusion map layout

This commit is contained in:
lda
2026-07-10 18:48:03 +07:00 Verified
parent 20b12c346f
commit 966837713b
5 changed files with 121 additions and 3 deletions
+66
View File
@@ -58,3 +58,69 @@ The model test passed with 3 tests. The component test passed with 7 tests. The
- Confirmed the limits beat marks non-claims, and the conclusion beat marks future work as `receded`.
- Confirmed the diagram uses semantic HTML and CSS connectors rather than React Flow, with responsive wrapping and a single cyan substrate emphasis.
- Confirmed no factual production, scheduler, or broad benchmark claim was added.
## Review Fixes
### RED
Command:
```text
pnpm --dir web --filter @lda/console test -- src/presentation/conclusion/ConclusionScene.test.tsx src/presentation/presentation-css.test.ts
```
Result: `3 failed, 10 passed` across 2 files. The failures were the missing explicit 1080px node placements, the cyan future-work SVG rule, and missing neutral icon attributes.
### GREEN
Command:
```text
pnpm --dir web --filter @lda/console test -- src/presentation/conclusion/ConclusionScene.test.tsx src/presentation/presentation-css.test.ts
```
Result: `2 passed`, `13 passed`.
### Requested Verification
Command:
```text
pnpm --dir web --filter @lda/console test -- src/presentation/conclusion/conclusion-model.test.ts src/presentation/conclusion/ConclusionScene.test.tsx
```
Result: `2 passed`, `12 passed`.
Command:
```text
pnpm --dir web --filter @lda/console test -- src/presentation/presentation-css.test.ts
```
Result: `1 passed`, `4 passed`.
Command:
```text
pnpm --dir web --filter @lda/console typecheck
```
Result: `tsc -b --pretty false` passed.
### Final Rerun After Connector Contract
```text
pnpm --dir web --filter @lda/console test -- src/presentation/conclusion/conclusion-model.test.ts src/presentation/conclusion/ConclusionScene.test.tsx
Test Files 2 passed (2)
Tests 12 passed (12)
pnpm --dir web --filter @lda/console test -- src/presentation/presentation-css.test.ts
Test Files 1 passed (1)
Tests 4 passed (4)
pnpm --dir web --filter @lda/console typecheck
$ tsc -b --pretty false
git diff --check
passed
```
@@ -76,4 +76,24 @@ describe("ConclusionScene", () => {
expect(screen.getByText("Planner proposes; runtime executes.")).toBeInTheDocument();
expect(screen.getByRole("list", { name: "future work layers" })).toHaveAttribute("data-state", "receded");
});
it("keeps evidence as a separately identified node after the runtime", () => {
render(<ConclusionScene scene={scene} beat={beat("future")} />);
const nodes = [...screen.getByLabelText("contribution flow").children];
expect(nodes.map((node) => node.getAttribute("data-node-id"))).toEqual([
"planner",
"substrate",
"runtime",
"evidence",
]);
expect(nodes[2]).toHaveAttribute("data-node-id", "runtime");
expect(nodes[3]).toHaveAttribute("data-node-id", "evidence");
});
it("marks every future-work icon neutral while substrate stays the sole emphasis", () => {
render(<ConclusionScene scene={scene} beat={beat("future")} />);
const map = screen.getByRole("region", { name: "thesis contribution boundary" });
expect(map.querySelector('[data-node-id="substrate"]')).toHaveAttribute("data-emphasis", "substrate");
expect([...map.querySelectorAll(".conclusion-map__future svg")].every((icon) => icon.getAttribute("data-emphasis") === "neutral")).toBe(true);
});
});
@@ -61,7 +61,7 @@ export const ConclusionScene: FC<ConclusionSceneProps> = ({ scene, beat }) => {
const Icon = futureWorkIcons[branch.icon];
return (
<li key={branch.id} data-future-work-id={branch.id}>
<Icon aria-hidden="true" />
<Icon aria-hidden="true" data-emphasis="neutral" />
<span>
<strong>{branch.label}</strong>
<small>{branch.example}</small>
@@ -21,4 +21,17 @@ describe("presentation.css", () => {
expect(breakpointBlock).toMatch(/\.evaluation-board__audit-row\s*\{\s*grid-template-columns: 1fr;/);
});
it("places the conclusion evidence beneath substrate at the 1080px breakpoint", () => {
expect(css).toMatch(/\.conclusion-map__node--planner\s*\{\s*grid-column: 1;\s*grid-row: 1;/);
expect(css).toMatch(/\.conclusion-map__node--substrate\s*\{\s*grid-column: 2;\s*grid-row: 1;/);
expect(css).toMatch(/\.conclusion-map__node--runtime\s*\{\s*grid-column: 3;\s*grid-row: 1;/);
expect(css).toMatch(/\.conclusion-map__node--evidence\s*\{\s*grid-column: 2;\s*grid-row: 2;/);
expect(css).not.toMatch(/\.conclusion-map__node--runtime::after/);
});
it("keeps future-work icons neutral by default", () => {
expect(css).toMatch(/\.conclusion-map__future svg\s*\{[^}]*color: var\(--text-secondary\);/s);
expect(css).not.toMatch(/\.conclusion-map__future svg\s*\{[^}]*color: var\(--accent-cyan\);/s);
});
});
@@ -287,7 +287,8 @@
background: oklch(0.19 0.03 250 / 88%);
}
.conclusion-map__node:not(:last-child)::after {
.conclusion-map__node--planner::after,
.conclusion-map__node--substrate::after {
position: absolute;
top: 50%;
right: -0.85rem;
@@ -352,7 +353,12 @@
transition: opacity 180ms ease, transform 180ms ease;
}
.conclusion-map__future svg { flex: 0 0 auto; width: 1.1rem; height: 1.1rem; color: var(--accent-cyan); }
.conclusion-map__future svg {
flex: 0 0 auto;
width: 1.1rem;
height: 1.1rem;
color: var(--text-secondary);
}
.conclusion-map__future span { display: grid; gap: 0.2rem; }
.conclusion-map__future strong { font-size: 0.8rem; }
.conclusion-map__future small { color: var(--text-secondary); font-family: "Source Sans 3", sans-serif; font-size: 0.76rem; }
@@ -371,6 +377,19 @@
.conclusion-map__future { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 1080px) {
.conclusion-map__flow {
grid-template-columns: repeat(3, minmax(0, 1fr));
grid-template-rows: auto auto;
gap: 0.8rem 1.7rem;
}
.conclusion-map__node--planner { grid-column: 1; grid-row: 1; }
.conclusion-map__node--substrate { grid-column: 2; grid-row: 1; }
.conclusion-map__node--runtime { grid-column: 3; grid-row: 1; }
.conclusion-map__node--evidence { grid-column: 2; grid-row: 2; }
}
@media (max-width: 640px) {
.conclusion-map__flow { grid-template-columns: 1fr; gap: 0.85rem; }
.conclusion-map__node:not(:last-child)::after {