fix: turn discussion chips into presenter rail

This commit is contained in:
lda
2026-07-09 04:49:16 +07:00 Verified
parent 345d0f0670
commit fe1f3f90e1
3 changed files with 93 additions and 23 deletions
@@ -291,6 +291,50 @@ describe("SceneBody", () => {
expect(screen.getByText("Compile or save")).toBeInTheDocument();
});
it("renders discussion branches as a labelled presenter rail", () => {
const location: PresentationLocation = { kind: "main", sceneId: "positioning", beatId: "landscape", focusPath: [] };
render(
<SceneBody
location={location}
demo={demo}
selectedNodeId={null}
selectNode={noop}
openEvidence={noop}
openDiscussion={noop}
onFocusPathChange={noop}
motionDisabled={false}
/>,
);
const rail = screen.getByLabelText("defense discussion topics");
expect(rail).toHaveAttribute("data-discussion-rail", "true");
expect(within(rail).getByText("Defense questions")).toBeInTheDocument();
expect(within(rail).getByRole("list")).toBeInTheDocument();
expect(within(rail).getByRole("button", { name: /Hosted automation future-work/i })).toBeInTheDocument();
});
it("keeps discussion rail actions wired to branch ids", async () => {
const user = userEvent.setup();
const location: PresentationLocation = { kind: "main", sceneId: "positioning", beatId: "landscape", focusPath: [] };
const openDiscussion = vi.fn();
render(
<SceneBody
location={location}
demo={demo}
selectedNodeId={null}
selectNode={noop}
openEvidence={noop}
openDiscussion={openDiscussion}
onFocusPathChange={noop}
motionDisabled={false}
/>,
);
await user.click(screen.getByRole("button", { name: /Hosted automation future-work/i }));
expect(openDiscussion).toHaveBeenCalledWith("hosted-automation");
});
it("renders evidence before discussion links so the chip lane cannot cover evidence text", () => {
const location: PresentationLocation = { kind: "main", sceneId: "positioning", beatId: "landscape", focusPath: [] };
const { container } = render(
+15 -11
View File
@@ -32,17 +32,21 @@ const DiscussionLinks = ({
const branches = discussionBranches.filter((branch) => branch.parentSceneId === sceneId);
if (branches.length === 0) return null;
return (
<div className="scene-body__discussion-links" aria-label="discussion topics">
{branches.map((branch) => (
<button
key={branch.id}
type="button"
onClick={() => openDiscussion(branch.id)}
>
{branch.title}
</button>
))}
</div>
<aside className="scene-body__discussion-links" aria-label="defense discussion topics" data-discussion-rail="true">
<span className="scene-body__discussion-label">Defense questions</span>
<ul className="scene-body__discussion-list">
{branches.map((branch) => (
<li key={branch.id}>
<button
type="button"
onClick={() => openDiscussion(branch.id)}
>
<span>{branch.title}</span> <small>{branch.claimClass}</small>
</button>
</li>
))}
</ul>
</aside>
);
};
@@ -118,28 +118,50 @@
.scene-body__discussion-links {
flex-shrink: 0;
display: grid;
grid-template-columns: auto minmax(0, 1fr);
align-items: center;
gap: 0.7rem;
margin-top: 0.55rem;
border-top: 1px solid color-mix(in oklch, var(--color-editorial-muted, oklch(0.48 0.025 65)) 30%, transparent);
padding-top: 0.45rem;
}
.scene-body__discussion-label {
color: color-mix(in oklch, var(--color-editorial-ink, oklch(0.19 0.015 65)) 72%, var(--color-editorial-muted, oklch(0.48 0.025 65)));
font: 650 0.72rem/1 var(--font-interface);
}
.scene-body__discussion-list {
display: flex;
flex-wrap: wrap;
gap: 0.35rem;
margin-top: 0.45rem;
padding-bottom: 0.2rem;
margin: 0;
padding: 0;
list-style: none;
}
.scene-body__discussion-links button {
border: 1px solid oklch(0.82 0.04 82 / 0.22);
border-radius: 999px;
background: oklch(0.16 0.018 65 / 0.74);
color: oklch(0.9 0.025 82);
padding: 0.28rem 0.7rem;
font: 600 0.72rem/1 var(--font-interface);
letter-spacing: 0.04em;
text-transform: uppercase;
display: inline-flex;
align-items: center;
gap: 0.45rem;
border: 1px solid color-mix(in oklch, var(--color-editorial-muted, oklch(0.48 0.025 65)) 36%, transparent);
border-radius: 0.55rem;
background: color-mix(in oklch, var(--color-editorial-paper, oklch(0.975 0.012 82)) 72%, transparent);
color: var(--color-editorial-ink, oklch(0.19 0.015 65));
padding: 0.34rem 0.55rem;
font: 600 0.74rem/1 var(--font-interface);
}
.scene-body__discussion-links button small {
color: color-mix(in oklch, var(--color-editorial-ink, oklch(0.19 0.015 65)) 62%, var(--color-editorial-muted, oklch(0.48 0.025 65)));
font: 0.68rem/1 var(--font-interface);
}
.scene-body__discussion-links button:hover,
.scene-body__discussion-links button:focus-visible {
border-color: var(--accent-cyan);
color: white;
border-color: var(--color-intent, oklch(0.53 0.17 250));
background: color-mix(in oklch, var(--color-intent, oklch(0.53 0.17 250)) 8%, var(--color-editorial-paper, oklch(0.975 0.012 82)));
}
.operator-chat {