docs: plan presentation alignment polish
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
# Presentation Alignment And Layout Polish Design
|
||||
|
||||
## Purpose
|
||||
|
||||
The presentation route now has stronger scene compositions, but two classes of
|
||||
visual problems remain:
|
||||
|
||||
- Scene 6's React Flow architecture figure can show node/edge offset because
|
||||
Dagre layout dimensions do not match the rendered stage-node dimensions.
|
||||
- Several presentation surfaces can visually collide or float awkwardly:
|
||||
discussion chips overlap Scene 3 evidence text, and chat rail behavior is
|
||||
awkward in narrower 4:3-adjacent layouts.
|
||||
|
||||
This slice fixes alignment and collision problems that make the presentation
|
||||
look broken. It does not replace the chat framework or redesign the whole
|
||||
presentation style.
|
||||
|
||||
## Scope
|
||||
|
||||
In scope:
|
||||
|
||||
- React Flow architecture figure alignment in Scene 6.
|
||||
- Size-aware Dagre layout dimensions for `InteractiveFigure`.
|
||||
- A more stable `fitView` timing after layout/container changes.
|
||||
- Scene discussion chip/evidence collision prevention.
|
||||
- 4:3-adjacent chat rail behavior so chat does not float detached beside the
|
||||
slide.
|
||||
- Correct Scene 10 approval smoke route: `#scene/interrupt-evidence/approval`.
|
||||
- Focused tests plus screenshot smoke.
|
||||
|
||||
Out of scope:
|
||||
|
||||
- Replacing chat with AI SDK/AI Elements or another chat framework.
|
||||
- Redesigning tool-call bubbles.
|
||||
- Rewriting the recursive figure catalog.
|
||||
- Replacing React Flow or Dagre.
|
||||
- Building a presenter companion.
|
||||
- Full motion choreography.
|
||||
|
||||
## Design Direction
|
||||
|
||||
Treat this as a correctness and layout-stability slice. The goal is not to make
|
||||
every slide beautiful; the goal is to stop visible misalignment and collision.
|
||||
|
||||
The implementation should be conservative:
|
||||
|
||||
- Keep React Flow.
|
||||
- Keep Dagre.
|
||||
- Keep existing scene data and storyboard hashes.
|
||||
- Add size-aware layout only where the rendered node dimensions differ.
|
||||
- Use CSS custom properties or exported TypeScript presets so layout math and
|
||||
CSS stay in sync.
|
||||
- Prefer simple layout rules over clever responsive choreography.
|
||||
|
||||
## Figure Alignment
|
||||
|
||||
Current problem:
|
||||
|
||||
- `figures/layout.ts` lays out every auto-layout figure as `236 x 102`.
|
||||
- `interactive-figure.css` renders stage figure nodes as `256 x 112`.
|
||||
- Scene 6 uses `size="stage"`.
|
||||
|
||||
That mismatch means Dagre calculates node centers for one box while React Flow
|
||||
renders a larger box. Edge anchors and node centers can drift.
|
||||
|
||||
Required behavior:
|
||||
|
||||
- `layoutFigure` accepts an optional size preset or dimensions.
|
||||
- Standard/wide figures keep `236 x 102`.
|
||||
- Stage figures use `256 x 112`.
|
||||
- `InteractiveFigure` passes the layout size based on its `size` prop.
|
||||
- CSS node dimensions and TypeScript layout dimensions are explicitly aligned.
|
||||
|
||||
Implementation preference:
|
||||
|
||||
```ts
|
||||
export type FigureLayoutSize = "standard" | "wide" | "stage";
|
||||
|
||||
export const FIGURE_NODE_DIMENSIONS: Record<FigureLayoutSize, {
|
||||
readonly width: number;
|
||||
readonly height: number;
|
||||
}> = {
|
||||
standard: { width: 236, height: 102 },
|
||||
wide: { width: 236, height: 102 },
|
||||
stage: { width: 256, height: 112 },
|
||||
};
|
||||
```
|
||||
|
||||
Then:
|
||||
|
||||
```ts
|
||||
layoutFigure(focus.figure, size)
|
||||
```
|
||||
|
||||
The exact type names can differ, but the concept should be explicit and tested.
|
||||
|
||||
## FitView Timing
|
||||
|
||||
Current behavior:
|
||||
|
||||
- `FitViewOnLayoutChange` calls `fitView()` once in an effect.
|
||||
|
||||
Problem:
|
||||
|
||||
- In stage mode, the canvas, container query, scroll container, breadcrumb row,
|
||||
and parent presentation scale can all settle after first render. A single
|
||||
immediate `fitView()` can measure too early.
|
||||
|
||||
Required behavior:
|
||||
|
||||
- Keep the immediate `fitView`.
|
||||
- Schedule a second `fitView` with `requestAnimationFrame`.
|
||||
- Clean up/cancel the frame on unmount or layout-key change.
|
||||
- Do not add a large measurement framework in this slice.
|
||||
|
||||
The second call should be tiny and commented, because it is deliberately
|
||||
working around layout settlement timing around React Flow.
|
||||
|
||||
## Discussion Chip Collision
|
||||
|
||||
Current problem:
|
||||
|
||||
- Scene 3 screenshots show discussion chips overlapping the evidence label at
|
||||
the bottom-left.
|
||||
|
||||
Required behavior:
|
||||
|
||||
- Evidence text and discussion chips must not overlap at 1280x720.
|
||||
- Discussion chips remain available but secondary.
|
||||
- The fix should work for all scenes, not only Scene 3.
|
||||
|
||||
Acceptable design:
|
||||
|
||||
- Make the primary scene region reserve bottom space for chips.
|
||||
- Move evidence text above chips when both are present.
|
||||
- Or make the discussion chip lane participate in normal layout instead of
|
||||
absolute/fixed positioning.
|
||||
|
||||
Do not hide discussion chips entirely.
|
||||
|
||||
## 4:3-Adjacent Chat Rail
|
||||
|
||||
Current problem:
|
||||
|
||||
- On slides with chat, narrower canvas ratios can make the chat rail feel like
|
||||
a floating, detached side object.
|
||||
|
||||
Required behavior:
|
||||
|
||||
- At narrower presentation canvas widths, chat rail should become a compact
|
||||
dock/overlay instead of a detached side rail.
|
||||
- This should be CSS-driven where possible.
|
||||
- The standard 1280x720 view should not regress.
|
||||
|
||||
Acceptable design:
|
||||
|
||||
- Use existing `data-chat-mode` and stage width/container queries.
|
||||
- For narrow layouts, position chat as a bottom-left compact dock with a
|
||||
bounded width and max height.
|
||||
- Keep it readable and out of the primary diagram path.
|
||||
|
||||
Do not add new chat state or a new chat framework.
|
||||
|
||||
## Scene 10 Smoke Route
|
||||
|
||||
The correct approval route is:
|
||||
|
||||
```text
|
||||
/present#scene/interrupt-evidence/approval
|
||||
```
|
||||
|
||||
The route below is invalid and must not be used in live specs or future smoke
|
||||
commands:
|
||||
|
||||
```text
|
||||
/present#scene/workflow-demo/approval
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
Required focused tests:
|
||||
|
||||
```powershell
|
||||
pnpm --dir web --filter @lda/console test -- src/presentation/figures/layout.test.ts src/presentation/figures/InteractiveFigure.test.tsx src/presentation/PresentationStage.test.tsx src/presentation/SceneBody.test.tsx
|
||||
pnpm --dir web --filter @lda/console typecheck
|
||||
pnpm --dir web --filter @lda/console build
|
||||
```
|
||||
|
||||
Required screenshot smoke:
|
||||
|
||||
- `/present#scene/architecture/client`
|
||||
- `/present#scene/architecture/runtime`
|
||||
- `/present#scene/positioning/landscape`
|
||||
- `/present#scene/positioning/lda-position`
|
||||
- `/present#scene/interrupt-evidence/approval`
|
||||
|
||||
Capture at:
|
||||
|
||||
- `1280x720`.
|
||||
- `1024x768`.
|
||||
|
||||
Save screenshots under `web/apps/console/.visual-smoke/`.
|
||||
|
||||
Acceptance checks:
|
||||
|
||||
- Scene 6 nodes and edges visually align.
|
||||
- Scene 6 stage figure remains keyboard navigable.
|
||||
- Scene 3 evidence and discussion chips do not overlap.
|
||||
- Slides with chat do not show an obviously detached floating rail at 1024x768.
|
||||
- Scene 10 screenshot opens Scene 10, not Scene 1 fallback.
|
||||
|
||||
## Self-Review
|
||||
|
||||
- No placeholders remain.
|
||||
- The slice is bounded to visual correctness and layout stability.
|
||||
- The design does not require replacing React Flow, Dagre, or chat.
|
||||
- The corrected Scene 10 route is explicit.
|
||||
Reference in New Issue
Block a user