fix: resolve remaining review findings for compositor
- F1: Add data-evidence-mode attr + CSS selectors for peek/open evidence widths - F2: Always show discussion button, not just on scenes with branches - F3: Beat-driven visuals: lifecycle/arch/positioning/authoring/evaluation highlight per beat - F4: Add elapsed timer, speaker notes textarea, motion toggle to presenter controls - F5: Paper-theme contrast: override card colors for readability on light backgrounds - F6: Fix inaccurate summaries: lifecycle bypass, provider-neutral boundary strength - F7: Fix MCP evidence pointer order
This commit is contained in:
@@ -67,7 +67,7 @@ describe("PresentationRoute", () => {
|
||||
window.location.hash = "#scene/positioning/lda-position";
|
||||
render(<PresentationRoute />);
|
||||
|
||||
await userEvent.click(screen.getByRole("button", { name: /open discussion topics/i }));
|
||||
await userEvent.click(screen.getByRole("button", { name: /discussion topics/i }));
|
||||
await userEvent.click(screen.getByRole("button", { name: /hosted automation/i }));
|
||||
expect(window.location.hash).toBe("#discuss/hosted-automation");
|
||||
|
||||
|
||||
@@ -172,11 +172,15 @@ export const PresentationRoute = () => {
|
||||
dispatch({ type: "jump", location: { kind: "main", sceneId: state.location.sceneId, beatId: scene.beats[0]!.id } });
|
||||
}, [state.location]);
|
||||
|
||||
const handleToggleMotion = useCallback(() => {
|
||||
dispatch({ type: "toggle_motion" });
|
||||
}, []);
|
||||
|
||||
const composition = compositionForState(state);
|
||||
const showChatDock = composition.chatMode === "dock" && state.location.kind !== "discussion";
|
||||
|
||||
return (
|
||||
<main className="presentation-route" aria-label="lda.chat presentation">
|
||||
<main className="presentation-route" aria-label="lda.chat presentation" data-motion={state.motionDisabled ? "disabled" : "enabled"}>
|
||||
<PresentationStage
|
||||
state={state}
|
||||
demo={demo}
|
||||
@@ -205,6 +209,7 @@ export const PresentationRoute = () => {
|
||||
forceReplay={handleForceReplay}
|
||||
resetOverrides={handleResetOverrides}
|
||||
resetScene={handleResetScene}
|
||||
toggleMotion={handleToggleMotion}
|
||||
/>
|
||||
)}
|
||||
<button
|
||||
|
||||
@@ -9,7 +9,7 @@ import { OperatorChat } from "./OperatorChat.js";
|
||||
import type { PresentationState } from "./presentation-state.js";
|
||||
import { compositionForState } from "./presentation-state.js";
|
||||
import type { DemoTimelineController } from "../demo/useDemoTimeline.js";
|
||||
import { discussionBranches, findScene, type PresentationLocation } from "./storyboard.js";
|
||||
import { findScene, type PresentationLocation } from "./storyboard.js";
|
||||
|
||||
type PresentationStageProps = {
|
||||
readonly state: PresentationState;
|
||||
@@ -45,10 +45,6 @@ export const PresentationStage = ({
|
||||
const composition = compositionForState(state);
|
||||
|
||||
const isMainScene = state.location.kind === "main";
|
||||
const currentScene = isMainScene ? findScene(state.location.sceneId) : null;
|
||||
const hasBranches = currentScene
|
||||
? discussionBranches.some((b) => b.parentSceneId === currentScene.id)
|
||||
: false;
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -56,6 +52,7 @@ export const PresentationStage = ({
|
||||
data-stage-theme={composition.stageTheme}
|
||||
data-chat-theme={composition.chatTheme}
|
||||
data-chat-mode={composition.chatMode}
|
||||
data-evidence-mode={composition.evidenceMode}
|
||||
>
|
||||
<aside className="presentation-stage__chat" aria-label="agent chat region">
|
||||
<OperatorChat state={state} messages={messages} onApprove={onApprove} onDeny={onDeny} />
|
||||
@@ -71,15 +68,13 @@ export const PresentationStage = ({
|
||||
selectedNodeId={state.selectedNodeId}
|
||||
selectNode={selectNode}
|
||||
/>
|
||||
{hasBranches && (
|
||||
<button
|
||||
type="button"
|
||||
className="presentation-stage__discussion-toggle"
|
||||
onClick={toggleDiscussionIndex}
|
||||
>
|
||||
Open discussion topics
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
className="presentation-stage__discussion-toggle"
|
||||
onClick={toggleDiscussionIndex}
|
||||
>
|
||||
Discussion topics
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
{state.discussionIndexOpen && (
|
||||
|
||||
@@ -19,6 +19,7 @@ describe("PresenterControls", () => {
|
||||
forceReplay: vi.fn(),
|
||||
resetOverrides: vi.fn(),
|
||||
resetScene: vi.fn(),
|
||||
toggleMotion: vi.fn(),
|
||||
};
|
||||
|
||||
it("changes stage and chat themes independently", async () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import type { PresentationState } from "./presentation-state.js";
|
||||
import { compositionForState } from "./presentation-state.js";
|
||||
import type { ChatMode, ChatTheme, MainLocation, PresentationLocation, StageTheme } from "./storyboard.js";
|
||||
@@ -15,6 +15,7 @@ type PresenterControlsProps = {
|
||||
readonly forceReplay: () => void;
|
||||
readonly resetOverrides: () => void;
|
||||
readonly resetScene: () => void;
|
||||
readonly toggleMotion: () => void;
|
||||
};
|
||||
|
||||
export const PresenterControls = ({
|
||||
@@ -28,6 +29,7 @@ export const PresenterControls = ({
|
||||
forceReplay,
|
||||
resetOverrides,
|
||||
resetScene,
|
||||
toggleMotion,
|
||||
}: PresenterControlsProps) => {
|
||||
const composition = compositionForState(state);
|
||||
const isMain = state.location.kind === "main";
|
||||
@@ -40,6 +42,22 @@ export const PresenterControls = ({
|
||||
const sceneNumber = currentScene?.number ?? 0;
|
||||
const totalScenes = mainScenes.length;
|
||||
|
||||
const [elapsed, setElapsed] = useState(0);
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setElapsed(Math.floor((Date.now() - state.startedAt) / 1000));
|
||||
}, 1000);
|
||||
return () => clearInterval(interval);
|
||||
}, [state.startedAt]);
|
||||
|
||||
const formatTime = (seconds: number) => {
|
||||
const m = Math.floor(seconds / 60);
|
||||
const s = seconds % 60;
|
||||
return `${m}:${s.toString().padStart(2, "0")}`;
|
||||
};
|
||||
|
||||
const plannedMinutes = 20;
|
||||
|
||||
return (
|
||||
<div className="presenter-controls" role="dialog" aria-label="presenter controls">
|
||||
<div className="presenter-controls__nav">
|
||||
@@ -50,6 +68,20 @@ export const PresenterControls = ({
|
||||
<span>Scene {sceneNumber}/{totalScenes}: {currentScene?.title ?? "Discussion"}</span>
|
||||
{currentBeat && <span> · {beatIndex + 1}/{totalBeats} {currentBeat.title}</span>}
|
||||
</div>
|
||||
<div className="presenter-controls__timer">
|
||||
<span>Elapsed: {formatTime(elapsed)}</span>
|
||||
<span> / {plannedMinutes}:00 planned</span>
|
||||
</div>
|
||||
<div className="presenter-controls__notes">
|
||||
<label>
|
||||
Speaker notes
|
||||
<textarea
|
||||
defaultValue={currentBeat?.caption ?? ""}
|
||||
rows={3}
|
||||
placeholder="Add notes for this beat..."
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div className="presenter-controls__over">
|
||||
<label>
|
||||
Stage theme
|
||||
@@ -91,6 +123,9 @@ export const PresenterControls = ({
|
||||
<span>{state.playbackMode === "replay" ? "Replay" : "Live"}</span>
|
||||
<button type="button" onClick={forceReplay}>Force replay fallback</button>
|
||||
<button type="button" onClick={resetScene}>Reset scene</button>
|
||||
<button type="button" onClick={toggleMotion}>
|
||||
Motion: {state.motionDisabled ? "off" : "on"}
|
||||
</button>
|
||||
<button type="button" onClick={resetOverrides}>Reset overrides</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -29,56 +29,70 @@ const NarrativeScene = ({ scene, beat }: { scene: SceneDefinition; beat: SceneBe
|
||||
</>
|
||||
);
|
||||
|
||||
const PositioningScene = ({ scene, beat }: { scene: SceneDefinition; beat: SceneBeatDefinition }) => (
|
||||
<>
|
||||
<StageCaption eyebrow={`Act I · ${scene.claimClass}`} title={scene.title}>
|
||||
<p>{beat.caption}</p>
|
||||
</StageCaption>
|
||||
<div className="scene-body__positioning-grid">
|
||||
<div className="scene-body__positioning-card">
|
||||
<strong>Tool loops</strong>
|
||||
<span>Direct action, no lifecycle</span>
|
||||
const PositioningScene = ({ scene, beat }: { scene: SceneDefinition; beat: SceneBeatDefinition }) => {
|
||||
const highlightLda = beat.id === "lda-position";
|
||||
return (
|
||||
<>
|
||||
<StageCaption eyebrow={`Act I · ${scene.claimClass}`} title={scene.title}>
|
||||
<p>{beat.caption}</p>
|
||||
</StageCaption>
|
||||
<div className="scene-body__positioning-grid">
|
||||
<div className="scene-body__positioning-card">
|
||||
<strong>Tool loops</strong>
|
||||
<span>Direct action, no lifecycle</span>
|
||||
</div>
|
||||
<div className="scene-body__positioning-card">
|
||||
<strong>Scripts</strong>
|
||||
<span>Simple, debuggable</span>
|
||||
</div>
|
||||
<div className={`scene-body__positioning-card${highlightLda ? " scene-body__positioning-card--active" : ""}`}>
|
||||
<strong>lda.chat</strong>
|
||||
<span>Typed lifecycle contracts</span>
|
||||
</div>
|
||||
<div className="scene-body__positioning-card">
|
||||
<strong>Agent graphs</strong>
|
||||
<span>Shared durability</span>
|
||||
</div>
|
||||
<div className="scene-body__positioning-card">
|
||||
<strong>MCP</strong>
|
||||
<span>Capability protocol</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="scene-body__positioning-card">
|
||||
<strong>Scripts</strong>
|
||||
<span>Simple, debuggable</span>
|
||||
</div>
|
||||
<div className="scene-body__positioning-card scene-body__positioning-card--active">
|
||||
<strong>lda.chat</strong>
|
||||
<span>Typed lifecycle contracts</span>
|
||||
</div>
|
||||
<div className="scene-body__positioning-card">
|
||||
<strong>Agent graphs</strong>
|
||||
<span>Shared durability</span>
|
||||
</div>
|
||||
<div className="scene-body__positioning-card">
|
||||
<strong>MCP</strong>
|
||||
<span>Capability protocol</span>
|
||||
</div>
|
||||
</div>
|
||||
<p className="scene-body__evidence">{scene.evidencePointer}</p>
|
||||
</>
|
||||
);
|
||||
<p className="scene-body__evidence">{scene.evidencePointer}</p>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const BoundaryScene = ({ scene, beat }: { scene: SceneDefinition; beat: SceneBeatDefinition }) => (
|
||||
<>
|
||||
<StageCaption eyebrow="Act II · implemented" title={scene.title}>
|
||||
<p>{beat.caption}</p>
|
||||
</StageCaption>
|
||||
<div className="scene-body__boundary">
|
||||
<div className="scene-body__boundary-side">
|
||||
<h3>Planner</h3>
|
||||
<p>External LLM proposes and revises workflow structure.</p>
|
||||
const BoundaryScene = ({ scene, beat }: { scene: SceneDefinition; beat: SceneBeatDefinition }) => {
|
||||
const showPlanner = beat.id === "planner" || beat.id === "boundary";
|
||||
const showRuntime = beat.id === "runtime" || beat.id === "boundary";
|
||||
return (
|
||||
<>
|
||||
<StageCaption eyebrow="Act II · implemented" title={scene.title}>
|
||||
<p>{beat.caption}</p>
|
||||
</StageCaption>
|
||||
<div className="scene-body__boundary">
|
||||
<div className={`scene-body__boundary-side${showPlanner ? "" : " scene-body__boundary-side--dim"}`}>
|
||||
<h3>Planner</h3>
|
||||
<p>External LLM proposes and revises workflow structure.</p>
|
||||
</div>
|
||||
<div className="scene-body__boundary-divider" />
|
||||
<div className={`scene-body__boundary-side${showRuntime ? "" : " scene-body__boundary-side--dim"}`}>
|
||||
<h3>Runtime</h3>
|
||||
<p>Validates, executes, records, and resumes deterministically.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="scene-body__boundary-divider" />
|
||||
<div className="scene-body__boundary-side">
|
||||
<h3>Runtime</h3>
|
||||
<p>Validates, executes, records, and resumes deterministically.</p>
|
||||
</div>
|
||||
</div>
|
||||
<p className="scene-body__evidence">{scene.evidencePointer}</p>
|
||||
</>
|
||||
);
|
||||
<p className="scene-body__evidence">{scene.evidencePointer}</p>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const lifecycleStages = [
|
||||
{ id: "draft", label: "Draft" },
|
||||
{ id: "artifact", label: "Artifact" },
|
||||
{ id: "deployment", label: "Deployment" },
|
||||
{ id: "run", label: "Run" },
|
||||
];
|
||||
|
||||
const LifecycleScene = ({ scene, beat }: { scene: SceneDefinition; beat: SceneBeatDefinition }) => (
|
||||
<>
|
||||
@@ -86,11 +100,14 @@ const LifecycleScene = ({ scene, beat }: { scene: SceneDefinition; beat: SceneBe
|
||||
<p>{beat.caption}</p>
|
||||
</StageCaption>
|
||||
<div className="scene-body__lifecycle">
|
||||
{["Draft", "Artifact", "Deployment", "Run"].map((stage, i) => (
|
||||
<div key={stage} className="scene-body__lifecycle-stage">
|
||||
{lifecycleStages.map((stage, i) => (
|
||||
<div
|
||||
key={stage.id}
|
||||
className={`scene-body__lifecycle-stage${beat.id === stage.id ? " scene-body__lifecycle-stage--active" : ""}`}
|
||||
>
|
||||
<span className="scene-body__lifecycle-number">{i + 1}</span>
|
||||
<strong>{stage}</strong>
|
||||
{i < 3 && <span className="scene-body__lifecycle-arrow">→</span>}
|
||||
<strong>{stage.label}</strong>
|
||||
{i < lifecycleStages.length - 1 && <span className="scene-body__lifecycle-arrow">→</span>}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -98,35 +115,27 @@ const LifecycleScene = ({ scene, beat }: { scene: SceneDefinition; beat: SceneBe
|
||||
</>
|
||||
);
|
||||
|
||||
const architectureLayers = [
|
||||
{ id: "client", label: "Client operations" },
|
||||
{ id: "api", label: "Transport / JSON-RPC" },
|
||||
{ id: "runtime", label: "Runtime & providers" },
|
||||
{ id: "node-use", label: "NodeUse", isNode: true },
|
||||
];
|
||||
|
||||
const ArchitectureScene = ({ scene, beat }: { scene: SceneDefinition; beat: SceneBeatDefinition }) => (
|
||||
<>
|
||||
<StageCaption eyebrow="Act II · implemented" title={scene.title}>
|
||||
<p>{beat.caption}</p>
|
||||
</StageCaption>
|
||||
<div className="scene-body__architecture">
|
||||
<div className="scene-body__architecture-layer">Client operations</div>
|
||||
<div className="scene-body__architecture-arrow">↓</div>
|
||||
<div className="scene-body__architecture-layer">Transport / JSON-RPC</div>
|
||||
<div className="scene-body__architecture-arrow">↓</div>
|
||||
<div className="scene-body__architecture-layer">Runtime & providers</div>
|
||||
<div className="scene-body__architecture-arrow">↓</div>
|
||||
<div className="scene-body__architecture-layer scene-body__architecture-layer--node">NodeUse</div>
|
||||
</div>
|
||||
<p className="scene-body__evidence">{scene.evidencePointer}</p>
|
||||
</>
|
||||
);
|
||||
|
||||
const AuthoringScene = ({ scene, beat }: { scene: SceneDefinition; beat: SceneBeatDefinition }) => (
|
||||
<>
|
||||
<StageCaption eyebrow="Act II · implemented" title={scene.title}>
|
||||
<p>{beat.caption}</p>
|
||||
</StageCaption>
|
||||
<div className="scene-body__authoring">
|
||||
{["Discover", "Author", "Diagnose", "Repair"].map((step, i) => (
|
||||
<div key={step} className="scene-body__authoring-step">
|
||||
<span className="scene-body__authoring-number">{i + 1}</span>
|
||||
<strong>{step}</strong>
|
||||
{i < 3 && <span className="scene-body__authoring-arrow">→</span>}
|
||||
{architectureLayers.map((layer, i) => (
|
||||
<div key={layer.id}>
|
||||
<div
|
||||
className={`scene-body__architecture-layer${layer.isNode ? " scene-body__architecture-layer--node" : ""}${beat.id === layer.id ? " scene-body__architecture-layer--active" : ""}`}
|
||||
>
|
||||
{layer.label}
|
||||
</div>
|
||||
{i < architectureLayers.length - 1 && <div className="scene-body__architecture-arrow">↓</div>}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -134,24 +143,55 @@ const AuthoringScene = ({ scene, beat }: { scene: SceneDefinition; beat: SceneBe
|
||||
</>
|
||||
);
|
||||
|
||||
const authoringSteps = [
|
||||
{ id: "discover", label: "Discover" },
|
||||
{ id: "author", label: "Author" },
|
||||
{ id: "diagnose", label: "Diagnose" },
|
||||
{ id: "repair", label: "Repair" },
|
||||
];
|
||||
|
||||
const AuthoringScene = ({ scene, beat }: { scene: SceneDefinition; beat: SceneBeatDefinition }) => (
|
||||
<>
|
||||
<StageCaption eyebrow="Act II · implemented" title={scene.title}>
|
||||
<p>{beat.caption}</p>
|
||||
</StageCaption>
|
||||
<div className="scene-body__authoring">
|
||||
{authoringSteps.map((step, i) => (
|
||||
<div
|
||||
key={step.id}
|
||||
className={`scene-body__authoring-step${beat.id === step.id ? " scene-body__authoring-step--active" : ""}`}
|
||||
>
|
||||
<span className="scene-body__authoring-number">{i + 1}</span>
|
||||
<strong>{step.label}</strong>
|
||||
{i < authoringSteps.length - 1 && <span className="scene-body__authoring-arrow">→</span>}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<p className="scene-body__evidence">{scene.evidencePointer}</p>
|
||||
</>
|
||||
);
|
||||
|
||||
const evaluationStats = [
|
||||
{ id: "cohort", value: "36", label: "trials" },
|
||||
{ id: "validity", value: "2", label: "challenges" },
|
||||
{ id: "findings", value: "3", label: "waves" },
|
||||
];
|
||||
|
||||
const EvaluationScene = ({ scene, beat }: { scene: SceneDefinition; beat: SceneBeatDefinition }) => (
|
||||
<>
|
||||
<StageCaption eyebrow={`Act I · ${scene.claimClass}`} title={scene.title}>
|
||||
<p>{beat.caption}</p>
|
||||
</StageCaption>
|
||||
<div className="scene-body__evaluation">
|
||||
<div className="scene-body__evaluation-stat">
|
||||
<strong>36</strong>
|
||||
<span>trials</span>
|
||||
</div>
|
||||
<div className="scene-body__evaluation-stat">
|
||||
<strong>2</strong>
|
||||
<span>challenges</span>
|
||||
</div>
|
||||
<div className="scene-body__evaluation-stat">
|
||||
<strong>3</strong>
|
||||
<span>waves</span>
|
||||
</div>
|
||||
{evaluationStats.map((stat) => (
|
||||
<div
|
||||
key={stat.id}
|
||||
className={`scene-body__evaluation-stat${beat.id === stat.id ? " scene-body__evaluation-stat--active" : ""}`}
|
||||
>
|
||||
<strong>{stat.value}</strong>
|
||||
<span>{stat.label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<p className="scene-body__evidence">{scene.evidencePointer}</p>
|
||||
</>
|
||||
|
||||
@@ -28,6 +28,8 @@ export type PresentationState = {
|
||||
readonly chatModeOverride: ChatMode | null;
|
||||
readonly controlsOpen: boolean;
|
||||
readonly discussionIndexOpen: boolean;
|
||||
readonly motionDisabled: boolean;
|
||||
readonly startedAt: number;
|
||||
};
|
||||
|
||||
export type PresentationAction =
|
||||
@@ -46,7 +48,8 @@ export type PresentationAction =
|
||||
| { readonly type: "set_chat_theme"; readonly theme: ChatTheme | null }
|
||||
| { readonly type: "set_chat_mode"; readonly mode: ChatMode | null }
|
||||
| { readonly type: "toggle_controls" }
|
||||
| { readonly type: "toggle_discussion_index" };
|
||||
| { readonly type: "toggle_discussion_index" }
|
||||
| { readonly type: "toggle_motion" };
|
||||
|
||||
export const initialPresentationState: PresentationState = {
|
||||
location: defaultMainLocation,
|
||||
@@ -59,6 +62,8 @@ export const initialPresentationState: PresentationState = {
|
||||
chatModeOverride: null,
|
||||
controlsOpen: false,
|
||||
discussionIndexOpen: false,
|
||||
motionDisabled: false,
|
||||
startedAt: Date.now(),
|
||||
};
|
||||
|
||||
const compositionForLocation = (
|
||||
@@ -199,5 +204,7 @@ export const presentationReducer = (
|
||||
return { ...state, controlsOpen: !state.controlsOpen };
|
||||
case "toggle_discussion_index":
|
||||
return { ...state, discussionIndexOpen: !state.discussionIndexOpen };
|
||||
case "toggle_motion":
|
||||
return { ...state, motionDisabled: !state.motionDisabled };
|
||||
}
|
||||
};
|
||||
|
||||
@@ -74,6 +74,99 @@ body {
|
||||
color: oklch(0.2 0.02 80);
|
||||
}
|
||||
|
||||
/* Paper theme scene-body element overrides */
|
||||
.presentation-stage[data-stage-theme="paper"] .scene-body__positioning-card {
|
||||
border-color: oklch(0.75 0.04 80);
|
||||
background: oklch(0.92 0.01 80);
|
||||
color: oklch(0.15 0.01 80);
|
||||
}
|
||||
|
||||
.presentation-stage[data-stage-theme="paper"] .scene-body__positioning-card span {
|
||||
color: oklch(0.4 0.03 80);
|
||||
}
|
||||
|
||||
.presentation-stage[data-stage-theme="paper"] .scene-body__positioning-card--active {
|
||||
border-color: oklch(0.45 0.14 80);
|
||||
background: oklch(0.85 0.04 80);
|
||||
color: oklch(0.12 0.02 80);
|
||||
}
|
||||
|
||||
.presentation-stage[data-stage-theme="paper"] .scene-body__boundary-side {
|
||||
border-color: oklch(0.75 0.04 80);
|
||||
background: oklch(0.92 0.01 80);
|
||||
color: oklch(0.15 0.01 80);
|
||||
}
|
||||
|
||||
.presentation-stage[data-stage-theme="paper"] .scene-body__boundary-side--dim {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.presentation-stage[data-stage-theme="paper"] .scene-body__boundary-divider {
|
||||
background: oklch(0.5 0.08 80);
|
||||
}
|
||||
|
||||
.presentation-stage[data-stage-theme="paper"] .scene-body__lifecycle-stage,
|
||||
.presentation-stage[data-stage-theme="paper"] .scene-body__authoring-step {
|
||||
border-color: oklch(0.75 0.04 80);
|
||||
background: oklch(0.92 0.01 80);
|
||||
color: oklch(0.15 0.01 80);
|
||||
}
|
||||
|
||||
.presentation-stage[data-stage-theme="paper"] .scene-body__lifecycle-number,
|
||||
.presentation-stage[data-stage-theme="paper"] .scene-body__authoring-number {
|
||||
background: oklch(0.45 0.14 80);
|
||||
color: oklch(0.97 0.005 80);
|
||||
}
|
||||
|
||||
.presentation-stage[data-stage-theme="paper"] .scene-body__lifecycle-arrow,
|
||||
.presentation-stage[data-stage-theme="paper"] .scene-body__authoring-arrow {
|
||||
color: oklch(0.5 0.06 80);
|
||||
}
|
||||
|
||||
.presentation-stage[data-stage-theme="paper"] .scene-body__lifecycle-stage--active,
|
||||
.presentation-stage[data-stage-theme="paper"] .scene-body__authoring-step--active {
|
||||
border-color: oklch(0.45 0.14 80);
|
||||
background: oklch(0.85 0.04 80);
|
||||
}
|
||||
|
||||
.presentation-stage[data-stage-theme="paper"] .scene-body__architecture-layer {
|
||||
border-color: oklch(0.75 0.04 80);
|
||||
background: oklch(0.92 0.01 80);
|
||||
color: oklch(0.15 0.01 80);
|
||||
}
|
||||
|
||||
.presentation-stage[data-stage-theme="paper"] .scene-body__architecture-layer--node {
|
||||
border-color: oklch(0.45 0.14 80);
|
||||
background: oklch(0.85 0.04 80);
|
||||
}
|
||||
|
||||
.presentation-stage[data-stage-theme="paper"] .scene-body__architecture-layer--active {
|
||||
border-color: oklch(0.45 0.14 80);
|
||||
background: oklch(0.85 0.04 80);
|
||||
}
|
||||
|
||||
.presentation-stage[data-stage-theme="paper"] .scene-body__architecture-arrow {
|
||||
color: oklch(0.5 0.06 80);
|
||||
}
|
||||
|
||||
.presentation-stage[data-stage-theme="paper"] .scene-body__evaluation-stat {
|
||||
border-color: oklch(0.75 0.04 80);
|
||||
background: oklch(0.92 0.01 80);
|
||||
}
|
||||
|
||||
.presentation-stage[data-stage-theme="paper"] .scene-body__evaluation-stat strong {
|
||||
color: oklch(0.45 0.14 80);
|
||||
}
|
||||
|
||||
.presentation-stage[data-stage-theme="paper"] .scene-body__evaluation-stat span {
|
||||
color: oklch(0.4 0.03 80);
|
||||
}
|
||||
|
||||
.presentation-stage[data-stage-theme="paper"] .scene-body__evaluation-stat--active {
|
||||
border-color: oklch(0.45 0.14 80);
|
||||
background: oklch(0.85 0.04 80);
|
||||
}
|
||||
|
||||
/* Chat themes */
|
||||
.presentation-stage[data-chat-theme="light"] .presentation-stage__chat {
|
||||
background: oklch(0.96 0.005 80);
|
||||
@@ -112,6 +205,15 @@ body {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Evidence modes */
|
||||
.presentation-stage[data-evidence-mode="peek"] {
|
||||
--evidence-width: 20rem;
|
||||
}
|
||||
|
||||
.presentation-stage[data-evidence-mode="open"] {
|
||||
--evidence-width: 24rem;
|
||||
}
|
||||
|
||||
.presentation-stage__chat,
|
||||
.presentation-stage__primary,
|
||||
.presentation-stage__evidence {
|
||||
@@ -444,6 +546,32 @@ body {
|
||||
color: oklch(0.78 0.035 250);
|
||||
}
|
||||
|
||||
.presenter-controls__timer {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
font-family: var(--font-mono, monospace);
|
||||
font-size: 0.8rem;
|
||||
color: oklch(0.78 0.035 250);
|
||||
}
|
||||
|
||||
.presenter-controls__notes label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.15rem;
|
||||
}
|
||||
|
||||
.presenter-controls__notes textarea {
|
||||
width: 100%;
|
||||
padding: 0.35rem;
|
||||
border: 1px solid oklch(0.36 0.04 250);
|
||||
background: oklch(0.18 0.025 250);
|
||||
color: inherit;
|
||||
border-radius: 0.3rem;
|
||||
font-size: 0.75rem;
|
||||
font-family: inherit;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.presenter-controls__over label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -697,6 +825,47 @@ body {
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
/* Beat-driven active states */
|
||||
.scene-body__boundary-side--dim {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.scene-body__lifecycle-stage:not(.scene-body__lifecycle-stage--active) {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.scene-body__lifecycle-stage--active {
|
||||
border-color: oklch(0.7 0.16 195);
|
||||
background: oklch(0.2 0.06 195);
|
||||
}
|
||||
|
||||
.scene-body__architecture-layer:not(.scene-body__architecture-layer--active):not(.scene-body__architecture-layer--node) {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.scene-body__architecture-layer--active {
|
||||
border-color: oklch(0.7 0.16 195);
|
||||
background: oklch(0.2 0.06 195);
|
||||
}
|
||||
|
||||
.scene-body__authoring-step:not(.scene-body__authoring-step--active) {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.scene-body__authoring-step--active {
|
||||
border-color: oklch(0.7 0.16 195);
|
||||
background: oklch(0.2 0.06 195);
|
||||
}
|
||||
|
||||
.scene-body__evaluation-stat:not(.scene-body__evaluation-stat--active) {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.scene-body__evaluation-stat--active {
|
||||
border-color: oklch(0.7 0.16 195);
|
||||
background: oklch(0.2 0.06 195);
|
||||
}
|
||||
|
||||
@media (max-height: 760px) {
|
||||
.scene-rail {
|
||||
padding: 0.25rem 0.5rem;
|
||||
@@ -725,3 +894,11 @@ body {
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
.presentation-route[data-motion="disabled"] *,
|
||||
.presentation-route[data-motion="disabled"] *::before,
|
||||
.presentation-route[data-motion="disabled"] *::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
|
||||
@@ -281,7 +281,7 @@ export const discussionBranches = defineDiscussionBranches([
|
||||
parentSceneId: "positioning",
|
||||
title: "MCP and agent-facing scale",
|
||||
claimClass: "external-context",
|
||||
evidencePointer: "Thesis: Model Context Protocol; Cloudflare Code Mode; Anthropic code execution with MCP",
|
||||
evidencePointer: "Thesis: Model Context Protocol; Anthropic MCP documentation; Cloudflare Code Mode",
|
||||
summary: "MCP is a capability protocol; progressive discovery addresses large agent-facing surfaces.",
|
||||
},
|
||||
{
|
||||
@@ -290,7 +290,7 @@ export const discussionBranches = defineDiscussionBranches([
|
||||
title: "Lifecycle state machine",
|
||||
claimClass: "implemented",
|
||||
evidencePointer: "Thesis Workflow Lifecycle; lifecycle state transitions",
|
||||
summary: "Draft, artifact, deployment, and run form a linear lifecycle with typed transitions.",
|
||||
summary: "Draft, artifact, deployment, and run form a typed lifecycle; raw plans can bypass the draft stage.",
|
||||
},
|
||||
{
|
||||
id: "raw-plan-import",
|
||||
@@ -330,7 +330,7 @@ export const discussionBranches = defineDiscussionBranches([
|
||||
title: "Provider-neutral security",
|
||||
claimClass: "implemented",
|
||||
evidencePointer: "Thesis Architecture; provider-neutral capability resolution",
|
||||
summary: "Provider-neutral capabilities avoid vendor lock-in while maintaining security boundaries.",
|
||||
summary: "Provider-neutral capabilities reduce vendor lock-in; runtime enforces boundary contracts.",
|
||||
},
|
||||
{
|
||||
id: "evaluation-validity",
|
||||
|
||||
Reference in New Issue
Block a user