feat: recompose positioning scene
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { cleanup, render, screen } from "@testing-library/react";
|
import { cleanup, render, screen, within } from "@testing-library/react";
|
||||||
import userEvent from "@testing-library/user-event";
|
import userEvent from "@testing-library/user-event";
|
||||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||||
import { loadCanonicalDemoRecording } from "../demo/timeline/replay.js";
|
import { loadCanonicalDemoRecording } from "../demo/timeline/replay.js";
|
||||||
@@ -119,6 +119,51 @@ describe("SceneBody", () => {
|
|||||||
expect(openDiscussion).toHaveBeenCalledWith("hosted-automation");
|
expect(openDiscussion).toHaveBeenCalledWith("hosted-automation");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("renders Scene 3 as a full positioning map", () => {
|
||||||
|
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 map = screen.getByLabelText("positioning map");
|
||||||
|
expect(map).toHaveAttribute("data-positioning-active-region", "landscape");
|
||||||
|
const withinMap = within(map);
|
||||||
|
expect(withinMap.getByText("Tool loops")).toBeInTheDocument();
|
||||||
|
expect(withinMap.getByText("Generated scripts")).toBeInTheDocument();
|
||||||
|
expect(withinMap.getByText("lda.chat")).toBeInTheDocument();
|
||||||
|
expect(withinMap.getByText("Agent graphs")).toBeInTheDocument();
|
||||||
|
expect(withinMap.getByText("MCP")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("emphasizes lda.chat in the positioning beat", () => {
|
||||||
|
const location: PresentationLocation = { kind: "main", sceneId: "positioning", beatId: "lda-position", focusPath: [] };
|
||||||
|
render(
|
||||||
|
<SceneBody
|
||||||
|
location={location}
|
||||||
|
demo={demo}
|
||||||
|
selectedNodeId={null}
|
||||||
|
selectNode={noop}
|
||||||
|
openEvidence={noop}
|
||||||
|
openDiscussion={noop}
|
||||||
|
onFocusPathChange={noop}
|
||||||
|
motionDisabled={false}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const substrate = screen.getByText("lda.chat").closest("[data-positioning-role='substrate']");
|
||||||
|
expect(substrate).toHaveAttribute("data-positioning-active", "true");
|
||||||
|
expect(screen.getByLabelText("positioning map")).toHaveAttribute("data-positioning-active-region", "lda");
|
||||||
|
});
|
||||||
|
|
||||||
it("renders Scene 7 as an agent authoring loop with beat-specific emphasis", () => {
|
it("renders Scene 7 as an agent authoring loop with beat-specific emphasis", () => {
|
||||||
const location: PresentationLocation = { kind: "main", sceneId: "authoring", beatId: "diagnose", focusPath: [] };
|
const location: PresentationLocation = { kind: "main", sceneId: "authoring", beatId: "diagnose", focusPath: [] };
|
||||||
render(
|
render(
|
||||||
|
|||||||
@@ -62,27 +62,51 @@ const PositioningScene = ({ scene, beat }: { scene: SceneDefinition; beat: Scene
|
|||||||
<StageCaption eyebrow={`Act I · ${scene.claimClass}`} title={scene.title}>
|
<StageCaption eyebrow={`Act I · ${scene.claimClass}`} title={scene.title}>
|
||||||
<p>{beat.caption}</p>
|
<p>{beat.caption}</p>
|
||||||
</StageCaption>
|
</StageCaption>
|
||||||
<div className="scene-body__positioning-grid">
|
<div
|
||||||
<div className="scene-body__positioning-card">
|
className="scene-body__positioning-map"
|
||||||
|
aria-label="positioning map"
|
||||||
|
data-positioning-active-region={highlightLda ? "lda" : "landscape"}
|
||||||
|
>
|
||||||
|
<section className="scene-body__positioning-column" aria-label="direct action patterns">
|
||||||
|
<p className="scene-body__positioning-label">Direct action</p>
|
||||||
|
<article className="scene-body__positioning-tile">
|
||||||
<strong>Tool loops</strong>
|
<strong>Tool loops</strong>
|
||||||
<span>Direct action, no lifecycle</span>
|
<span>Fast action, no durable lifecycle</span>
|
||||||
</div>
|
</article>
|
||||||
<div className="scene-body__positioning-card">
|
<article className="scene-body__positioning-tile">
|
||||||
<strong>Scripts</strong>
|
<strong>Generated scripts</strong>
|
||||||
<span>Simple, debuggable</span>
|
<span>Inspectable code, weak deployment records</span>
|
||||||
</div>
|
</article>
|
||||||
<div className={`scene-body__positioning-card${highlightLda ? " scene-body__positioning-card--active" : ""}`}>
|
</section>
|
||||||
|
<article
|
||||||
|
className="scene-body__positioning-substrate"
|
||||||
|
data-positioning-role="substrate"
|
||||||
|
data-positioning-active={highlightLda ? "true" : "false"}
|
||||||
|
>
|
||||||
|
<span className="scene-body__positioning-label">This thesis</span>
|
||||||
<strong>lda.chat</strong>
|
<strong>lda.chat</strong>
|
||||||
<span>Typed lifecycle contracts</span>
|
<p>Typed workflow substrate for external agents and human operators.</p>
|
||||||
</div>
|
<ul>
|
||||||
<div className="scene-body__positioning-card">
|
<li>Lifecycle</li>
|
||||||
|
<li>Validation</li>
|
||||||
|
<li>Persisted records</li>
|
||||||
|
</ul>
|
||||||
|
</article>
|
||||||
|
<section className="scene-body__positioning-column" aria-label="adjacent systems">
|
||||||
|
<p className="scene-body__positioning-label">Adjacent systems</p>
|
||||||
|
<article className="scene-body__positioning-tile">
|
||||||
|
<strong>Hosted automation</strong>
|
||||||
|
<span>Managed triggers and app integrations</span>
|
||||||
|
</article>
|
||||||
|
<article className="scene-body__positioning-tile">
|
||||||
<strong>Agent graphs</strong>
|
<strong>Agent graphs</strong>
|
||||||
<span>Shared durability</span>
|
<span>Durable planner loops</span>
|
||||||
</div>
|
</article>
|
||||||
<div className="scene-body__positioning-card">
|
<article className="scene-body__positioning-tile">
|
||||||
<strong>MCP</strong>
|
<strong>MCP</strong>
|
||||||
<span>Capability protocol</span>
|
<span>Capability protocol boundary</span>
|
||||||
</div>
|
</article>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<p className="scene-body__evidence">{scene.evidencePointer}</p>
|
<p className="scene-body__evidence">{scene.evidencePointer}</p>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -661,34 +661,100 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Scene body visual treatments */
|
/* Scene body visual treatments */
|
||||||
.scene-body__positioning-grid {
|
.scene-body__positioning-map {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
|
grid-template-columns: minmax(13rem, 0.9fr) minmax(20rem, 1.35fr) minmax(13rem, 0.9fr);
|
||||||
gap: 0.5rem;
|
gap: 1rem;
|
||||||
margin-top: 0.5rem;
|
align-items: stretch;
|
||||||
|
margin-top: 1.1rem;
|
||||||
|
min-height: 20rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scene-body__positioning-card {
|
.scene-body__positioning-column {
|
||||||
padding: 0.65rem 0.75rem;
|
display: grid;
|
||||||
border: 1px solid oklch(0.36 0.04 250);
|
align-content: start;
|
||||||
background: oklch(0.16 0.025 250);
|
gap: 0.7rem;
|
||||||
border-radius: 0.5rem;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.scene-body__positioning-card strong {
|
.scene-body__positioning-label {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--color-editorial-muted, oklch(0.48 0.025 65));
|
||||||
|
font: 700 0.68rem/1 var(--font-mono, monospace);
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scene-body__positioning-tile,
|
||||||
|
.scene-body__positioning-substrate {
|
||||||
|
border: 1px solid color-mix(in oklch, var(--color-editorial-muted, oklch(0.48 0.025 65)) 30%, transparent);
|
||||||
|
border-radius: 0.85rem;
|
||||||
|
background: color-mix(in oklch, var(--color-editorial-paper, oklch(0.975 0.012 82)) 78%, white);
|
||||||
|
color: var(--color-editorial-ink, oklch(0.19 0.015 65));
|
||||||
|
}
|
||||||
|
|
||||||
|
.scene-body__positioning-tile {
|
||||||
|
min-height: 5.8rem;
|
||||||
|
padding: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scene-body__positioning-tile strong {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 0.15rem;
|
margin-bottom: 0.35rem;
|
||||||
|
color: var(--color-editorial-ink, oklch(0.19 0.015 65));
|
||||||
}
|
}
|
||||||
|
|
||||||
.scene-body__positioning-card span {
|
.scene-body__positioning-tile span {
|
||||||
color: oklch(0.72 0.03 250);
|
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-size: 0.75rem;
|
font-size: 0.82rem;
|
||||||
|
line-height: 1.35;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scene-body__positioning-card--active {
|
.scene-body__positioning-substrate {
|
||||||
border-color: oklch(0.7 0.16 195);
|
display: grid;
|
||||||
background: oklch(0.2 0.06 195);
|
align-content: center;
|
||||||
|
gap: 0.8rem;
|
||||||
|
padding: 1.15rem;
|
||||||
|
box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--accent-cyan, oklch(0.7 0.16 195)) 18%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.scene-body__positioning-substrate strong {
|
||||||
|
color: var(--color-editorial-ink, oklch(0.19 0.015 65));
|
||||||
|
font-size: 2rem;
|
||||||
|
line-height: 0.95;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scene-body__positioning-substrate p {
|
||||||
|
max-width: 28ch;
|
||||||
|
margin: 0;
|
||||||
|
color: color-mix(in oklch, var(--color-editorial-ink, oklch(0.19 0.015 65)) 82%, var(--color-editorial-muted, oklch(0.48 0.025 65)));
|
||||||
|
}
|
||||||
|
|
||||||
|
.scene-body__positioning-substrate ul {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.45rem;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scene-body__positioning-substrate li {
|
||||||
|
border: 1px solid color-mix(in oklch, var(--accent-cyan, oklch(0.7 0.16 195)) 42%, transparent);
|
||||||
|
border-radius: 999px;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
color: var(--color-editorial-ink, oklch(0.19 0.015 65));
|
||||||
|
font-size: 0.72rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scene-body__positioning-map[data-positioning-active-region="lda"] .scene-body__positioning-tile {
|
||||||
|
opacity: 0.58;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scene-body__positioning-substrate[data-positioning-active="true"] {
|
||||||
|
transform: translateY(-0.25rem);
|
||||||
|
border-color: color-mix(in oklch, var(--accent-cyan, oklch(0.7 0.16 195)) 68%, transparent);
|
||||||
|
background: color-mix(in oklch, var(--accent-cyan, oklch(0.7 0.16 195)) 8%, var(--color-editorial-paper, oklch(0.975 0.012 82)));
|
||||||
}
|
}
|
||||||
|
|
||||||
.scene-body__boundary {
|
.scene-body__boundary {
|
||||||
@@ -938,14 +1004,6 @@
|
|||||||
transform: scale(1.05);
|
transform: scale(1.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.scene-body__positioning-card {
|
|
||||||
transition: opacity 0.3s ease, transform 0.25s ease, border-color 0.2s ease, background 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scene-body__positioning-card--active {
|
|
||||||
transform: scale(1.03);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-height: 760px) {
|
@media (max-height: 760px) {
|
||||||
.stage-caption h1 {
|
.stage-caption h1 {
|
||||||
font-size: clamp(1.4rem, 3vw, 2.2rem);
|
font-size: clamp(1.4rem, 3vw, 2.2rem);
|
||||||
|
|||||||
Reference in New Issue
Block a user