fix: close remaining compositor correctness gaps
- F1: Preload replayEvidence on mount so evidence drawer shows records in replay mode - F2: close_overlay forces evidenceModeOverride to hidden when beat-default is open - F3: Add CSS transitions (opacity, scale, border, background) for beat-driven elements - F4: Fix planned time from 20 to 15 minutes for defense presentation - F5: Fix stale speaker notes by keying textarea on beat.id - F6: Lazy-initialize startedAt via useReducer initializer instead of module-level Date.now() - F7: Fix MCP evidence pointer to Anthropic + Cloudflare
This commit is contained in:
@@ -36,13 +36,16 @@ export const PresentationRoute = () => {
|
|||||||
const [state, dispatch] = useReducer(
|
const [state, dispatch] = useReducer(
|
||||||
presentationReducer,
|
presentationReducer,
|
||||||
initialPresentationState,
|
initialPresentationState,
|
||||||
(initial) => presentationReducer(initial, { type: "jump_hash", hash: window.location.hash }),
|
(initial) => presentationReducer(
|
||||||
|
{ ...initial, startedAt: Date.now() },
|
||||||
|
{ type: "jump_hash", hash: window.location.hash },
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
const recording = useMemo(() => loadCanonicalDemoRecording(), []);
|
const recording = useMemo(() => loadCanonicalDemoRecording(), []);
|
||||||
const replayEvidence = useMemo(() => projectRecordingToEvidence(recording), [recording]);
|
const replayEvidence = useMemo(() => projectRecordingToEvidence(recording), [recording]);
|
||||||
|
|
||||||
const [evidence, setEvidence] = useState<readonly EvidenceRecord[]>([]);
|
const [evidence, setEvidence] = useState<readonly EvidenceRecord[]>(replayEvidence);
|
||||||
const recordEvidence = useCallback((record: EvidenceRecord) => {
|
const recordEvidence = useCallback((record: EvidenceRecord) => {
|
||||||
setEvidence((records) => [...records, record]);
|
setEvidence((records) => [...records, record]);
|
||||||
}, []);
|
}, []);
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export const PresenterControls = ({
|
|||||||
return `${m}:${s.toString().padStart(2, "0")}`;
|
return `${m}:${s.toString().padStart(2, "0")}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const plannedMinutes = 20;
|
const plannedMinutes = 15;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="presenter-controls" role="dialog" aria-label="presenter controls">
|
<div className="presenter-controls" role="dialog" aria-label="presenter controls">
|
||||||
@@ -76,6 +76,7 @@ export const PresenterControls = ({
|
|||||||
<label>
|
<label>
|
||||||
Speaker notes
|
Speaker notes
|
||||||
<textarea
|
<textarea
|
||||||
|
key={currentBeat?.id ?? "none"}
|
||||||
defaultValue={currentBeat?.caption ?? ""}
|
defaultValue={currentBeat?.caption ?? ""}
|
||||||
rows={3}
|
rows={3}
|
||||||
placeholder="Add notes for this beat..."
|
placeholder="Add notes for this beat..."
|
||||||
|
|||||||
@@ -181,6 +181,13 @@ export const presentationReducer = (
|
|||||||
case "close_overlay": {
|
case "close_overlay": {
|
||||||
if (state.selectedNodeId !== null) return { ...state, selectedNodeId: null };
|
if (state.selectedNodeId !== null) return { ...state, selectedNodeId: null };
|
||||||
if (state.evidenceModeOverride !== null) return { ...state, evidenceModeOverride: null };
|
if (state.evidenceModeOverride !== null) return { ...state, evidenceModeOverride: null };
|
||||||
|
const derivedEvidenceMode = (() => {
|
||||||
|
if (state.location.kind === "discussion") return "hidden" as const;
|
||||||
|
const scene = findScene(state.location.sceneId);
|
||||||
|
const beat = scene?.beats.find((b) => b.id === (state.location as MainLocation).beatId);
|
||||||
|
return beat?.evidenceMode ?? "hidden";
|
||||||
|
})();
|
||||||
|
if (derivedEvidenceMode !== "hidden") return { ...state, evidenceModeOverride: "hidden" };
|
||||||
if (state.discussionIndexOpen) return { ...state, discussionIndexOpen: false };
|
if (state.discussionIndexOpen) return { ...state, discussionIndexOpen: false };
|
||||||
if (state.controlsOpen) return { ...state, controlsOpen: false };
|
if (state.controlsOpen) return { ...state, controlsOpen: false };
|
||||||
if (state.location.kind === "discussion") {
|
if (state.location.kind === "discussion") {
|
||||||
|
|||||||
@@ -828,42 +828,77 @@ body {
|
|||||||
/* Beat-driven active states */
|
/* Beat-driven active states */
|
||||||
.scene-body__boundary-side--dim {
|
.scene-body__boundary-side--dim {
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
|
transform: scale(0.97);
|
||||||
|
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scene-body__boundary-side {
|
||||||
|
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scene-body__lifecycle-stage,
|
||||||
|
.scene-body__authoring-step {
|
||||||
|
transition: opacity 0.3s ease, transform 0.25s ease, border-color 0.2s ease, background 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scene-body__lifecycle-stage:not(.scene-body__lifecycle-stage--active) {
|
.scene-body__lifecycle-stage:not(.scene-body__lifecycle-stage--active) {
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
|
transform: scale(0.96);
|
||||||
}
|
}
|
||||||
|
|
||||||
.scene-body__lifecycle-stage--active {
|
.scene-body__lifecycle-stage--active {
|
||||||
border-color: oklch(0.7 0.16 195);
|
border-color: oklch(0.7 0.16 195);
|
||||||
background: oklch(0.2 0.06 195);
|
background: oklch(0.2 0.06 195);
|
||||||
|
transform: scale(1.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.scene-body__architecture-layer {
|
||||||
|
transition: opacity 0.3s ease, transform 0.25s ease, border-color 0.2s ease, background 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scene-body__architecture-layer:not(.scene-body__architecture-layer--active):not(.scene-body__architecture-layer--node) {
|
.scene-body__architecture-layer:not(.scene-body__architecture-layer--active):not(.scene-body__architecture-layer--node) {
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
|
transform: scale(0.97);
|
||||||
}
|
}
|
||||||
|
|
||||||
.scene-body__architecture-layer--active {
|
.scene-body__architecture-layer--active {
|
||||||
border-color: oklch(0.7 0.16 195);
|
border-color: oklch(0.7 0.16 195);
|
||||||
background: oklch(0.2 0.06 195);
|
background: oklch(0.2 0.06 195);
|
||||||
|
transform: scale(1.03);
|
||||||
}
|
}
|
||||||
|
|
||||||
.scene-body__authoring-step:not(.scene-body__authoring-step--active) {
|
.scene-body__authoring-step:not(.scene-body__authoring-step--active) {
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
|
transform: scale(0.96);
|
||||||
}
|
}
|
||||||
|
|
||||||
.scene-body__authoring-step--active {
|
.scene-body__authoring-step--active {
|
||||||
border-color: oklch(0.7 0.16 195);
|
border-color: oklch(0.7 0.16 195);
|
||||||
background: oklch(0.2 0.06 195);
|
background: oklch(0.2 0.06 195);
|
||||||
|
transform: scale(1.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.scene-body__evaluation-stat {
|
||||||
|
transition: opacity 0.3s ease, transform 0.25s ease, border-color 0.2s ease, background 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scene-body__evaluation-stat:not(.scene-body__evaluation-stat--active) {
|
.scene-body__evaluation-stat:not(.scene-body__evaluation-stat--active) {
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
|
transform: scale(0.96);
|
||||||
}
|
}
|
||||||
|
|
||||||
.scene-body__evaluation-stat--active {
|
.scene-body__evaluation-stat--active {
|
||||||
border-color: oklch(0.7 0.16 195);
|
border-color: oklch(0.7 0.16 195);
|
||||||
background: oklch(0.2 0.06 195);
|
background: oklch(0.2 0.06 195);
|
||||||
|
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) {
|
||||||
|
|||||||
@@ -281,7 +281,7 @@ export const discussionBranches = defineDiscussionBranches([
|
|||||||
parentSceneId: "positioning",
|
parentSceneId: "positioning",
|
||||||
title: "MCP and agent-facing scale",
|
title: "MCP and agent-facing scale",
|
||||||
claimClass: "external-context",
|
claimClass: "external-context",
|
||||||
evidencePointer: "Thesis: Model Context Protocol; Anthropic MCP documentation; Cloudflare Code Mode",
|
evidencePointer: "Thesis: Model Context Protocol; Anthropic MCP; Cloudflare Code Mode",
|
||||||
summary: "MCP is a capability protocol; progressive discovery addresses large agent-facing surfaces.",
|
summary: "MCP is a capability protocol; progressive discovery addresses large agent-facing surfaces.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user