docs: complete adaptive presentation canvas slice
This commit is contained in:
@@ -102,12 +102,12 @@ Implementation order:
|
||||
[`defense presentation storyboard`](superpowers/specs/2026-07-04-defense-presentation-storyboard-design.md).
|
||||
Implementation:
|
||||
[`editorial canvas and Interactive Figure plan`](historical/superpowers/plans/2026-07-05-editorial-canvas-interactive-figure.md).
|
||||
12. Then: adapt the logical presentation canvas continuously from `4:3` to
|
||||
12. Completed: adapt the logical presentation canvas continuously from `4:3` to
|
||||
`16:9` and replace the resizing evidence drawer with a progress-row receipt
|
||||
and centered inspector. Design:
|
||||
[`adaptive presentation canvas and evidence inspector`](superpowers/specs/2026-07-05-adaptive-presentation-canvas-design.md).
|
||||
Implementation:
|
||||
[`adaptive presentation canvas plan`](superpowers/plans/2026-07-06-adaptive-presentation-canvas.md).
|
||||
[`adaptive presentation canvas plan`](historical/superpowers/plans/2026-07-06-adaptive-presentation-canvas.md).
|
||||
13. Then: adopt source-owned AI Elements chat primitives against existing
|
||||
`AgentMessagePart` / `AgentDriver` contracts.
|
||||
14. Future: implement Schema Form Surface and synchronized Approval Session.
|
||||
|
||||
+28
-7
@@ -111,7 +111,7 @@ With the Python server running, verify in the browser:
|
||||
1. The initial page makes no upstream request
|
||||
2. Connect succeeds against `http://127.0.0.1:8765/rpc`
|
||||
3. Source rows appear after connection
|
||||
4. Raw health and source-list exchanges are selectable in the evidence drawer
|
||||
4. Raw health and source-list exchanges are selectable in the evidence inspector
|
||||
5. Equivalent CLI text is visible
|
||||
6. `http://example.com:8765/rpc` is rejected without upstream fetch
|
||||
7. Stopping the Python server produces the unreachable state while preserving
|
||||
@@ -170,13 +170,14 @@ both modes. Replay is visibly labeled and does not create real issues.
|
||||
|
||||
The console exposes `/present`, a 720p no-scroll defense compositor for the
|
||||
prepared `lda_report_workflow` story. It renders a 12-scene, multi-beat
|
||||
storyboard with stable stage regions, discussion branches, act themes, a chat
|
||||
dock, and keyboard navigation.
|
||||
storyboard with an adaptive aspect-ratio canvas, stable stage regions, discussion
|
||||
branches, act themes, a chat dock, and keyboard navigation.
|
||||
|
||||
Scenes 8 through 10 use the canonical replay as their only execution evidence.
|
||||
The handoff expands an interpreted run operation into the center stage, then
|
||||
keeps one workflow graph mounted while execution reaches the typed interrupt
|
||||
and approval boundary. Raw protocol payloads remain in the evidence drawer.
|
||||
and approval boundary. Raw protocol payloads are available through the evidence
|
||||
receipt and inspector.
|
||||
|
||||
The key defense states are directly addressable:
|
||||
|
||||
@@ -230,9 +231,16 @@ committed `lda-report-success-v1` recording. No RPC server is required.
|
||||
|
||||
#### Editorial Canvas
|
||||
|
||||
The presentation renders on a fixed 1280x720 editorial canvas that scales and
|
||||
letterboxes to fit any viewport without reflowing scene content. Scene 6 uses a
|
||||
recursive Interactive Figure with expand/collapse and breadcrumb navigation.
|
||||
The presentation renders on an adaptive editorial canvas that derives its aspect
|
||||
ratio from the browser viewport. The canvas preserves a fixed logical height of
|
||||
720px and clamps the logical width between 960px (4:3) and 1280px (16:9),
|
||||
scaling the result to fit the viewport with letterboxing. Viewports wider than
|
||||
~1.78:1 fill the maximum 1280x720 logical region; narrower viewports receive a
|
||||
proportionally narrower canvas without reflowing scene content. No URL query
|
||||
parameters or local-storage settings control the ratio.
|
||||
|
||||
Scene 6 uses a recursive Interactive Figure with expand/collapse and breadcrumb
|
||||
navigation.
|
||||
|
||||
Key Scene 6 deep links:
|
||||
|
||||
@@ -247,6 +255,19 @@ Key Scene 6 deep links:
|
||||
- **Tab / arrows**: move focus between figure nodes without advancing the presentation
|
||||
- **Breadcrumbs**: jump to any ancestor focus level
|
||||
|
||||
#### Evidence Presentation
|
||||
|
||||
Evidence availability never resizes the primary presentation region. Each beat
|
||||
may request an evidence presentation state (`hidden`, `receipt`, or `inspector`),
|
||||
and a global override can be set through the presentation tool. The state is
|
||||
cleared on scene navigation (next, previous, jump) but preserved through
|
||||
discussion transitions.
|
||||
|
||||
- **Receipt**: a compact bottom-row button showing the count and label of
|
||||
available evidence records. Clicking opens the inspector.
|
||||
- **Inspector**: a centered modal dialog with `Interpreted` and `Raw` tabs, a
|
||||
record selector, and focus trapping. `Escape` closes the inspector.
|
||||
|
||||
#### Constraints
|
||||
|
||||
This plan deliberately defers:
|
||||
|
||||
@@ -28,6 +28,63 @@ beforeAll(() => {
|
||||
afterEach(() => cleanup());
|
||||
|
||||
describe("PresentationRoute", () => {
|
||||
it("renders the presentation stage entry point", async () => {
|
||||
const { PresentationRoute } = await import("./PresentationRoute.js");
|
||||
render(<PresentationRoute />);
|
||||
expect(screen.getByRole("main", { name: /lda.chat presentation/i })).toBeInTheDocument();
|
||||
expect(screen.getByRole("heading", { name: /Thesis/ })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("starts from a scene hash and advances with keyboard", async () => {
|
||||
window.location.hash = "#scene/agent-handoff/request";
|
||||
const { PresentationRoute } = await import("./PresentationRoute.js");
|
||||
render(<PresentationRoute />);
|
||||
expect(screen.getByRole("heading", { name: /Agent Handoff/i })).toBeInTheDocument();
|
||||
await userEvent.keyboard("{ArrowRight}");
|
||||
expect(await screen.findByText(/The interface delegates durable work to lda\.chat/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders audience progress chrome without rail or mode label", async () => {
|
||||
const { PresentationRoute } = await import("./PresentationRoute.js");
|
||||
render(<PresentationRoute />);
|
||||
expect(screen.getByText("Prepare the thesis readiness report.")).toBeInTheDocument();
|
||||
expect(screen.queryByLabelText(/presentation scene rail/i)).not.toBeInTheDocument();
|
||||
expect(screen.getByLabelText("scene position")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows node spotlight when a graph node is selected", async () => {
|
||||
window.location.hash = "#scene/workflow-demo/graph";
|
||||
const { PresentationRoute } = await import("./PresentationRoute.js");
|
||||
render(<PresentationRoute />);
|
||||
await userEvent.click(screen.getByRole("button", { name: /issue review/i }));
|
||||
expect(screen.getByRole("dialog", { name: /issue review/i })).toBeInTheDocument();
|
||||
expect(screen.getByText("Workflow node")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("can advance replay far enough to show a product operation block", async () => {
|
||||
window.location.hash = "#scene/workflow-demo/operation";
|
||||
const { PresentationRoute } = await import("./PresentationRoute.js");
|
||||
render(<PresentationRoute />);
|
||||
expect(await screen.findByText(/workflow.runs.start/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("opens a positioning branch via hash and returns to the parent scene first beat", async () => {
|
||||
window.location.hash = "#discuss/hosted-automation";
|
||||
const { PresentationRoute } = await import("./PresentationRoute.js");
|
||||
render(<PresentationRoute />);
|
||||
expect(await screen.findByRole("button", { name: /return to positioning/i })).toBeInTheDocument();
|
||||
await userEvent.click(screen.getByRole("button", { name: /return to positioning/i }));
|
||||
expect(window.location.hash).toBe("#scene/positioning/landscape");
|
||||
});
|
||||
|
||||
it("uses the parent scene as return location for a directly linked branch", async () => {
|
||||
window.location.hash = "#discuss/mcp-agent-scale";
|
||||
const { PresentationRoute } = await import("./PresentationRoute.js");
|
||||
render(<PresentationRoute />);
|
||||
await userEvent.click(screen.getByRole("button", { name: /return to positioning/i }));
|
||||
expect(window.location.hash).toBe("#scene/positioning/landscape");
|
||||
});
|
||||
|
||||
it("renders stable chat, primary, progress, and transient evidence surfaces", async () => {
|
||||
const { PresentationRoute } = await import("./PresentationRoute.js");
|
||||
render(<PresentationRoute />);
|
||||
|
||||
@@ -409,6 +409,8 @@
|
||||
place-items: center;
|
||||
padding: 2.5rem 5%;
|
||||
background: oklch(0.08 0.02 250 / 0.68);
|
||||
opacity: 1;
|
||||
transition: opacity 150ms ease-in-out;
|
||||
}
|
||||
|
||||
.evidence-inspector {
|
||||
|
||||
Reference in New Issue
Block a user