feat: unify prepared authoring lifecycle visual
This commit is contained in:
@@ -0,0 +1,36 @@
|
|||||||
|
# Task 3 Report
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
- Replaced the old five-item phase rail in `PreparedAuthoringLifecycleScene` with the six-step Prepared Lifecycle rail: Discover, Author, Diagnose, Repair, Artifact, Deployment.
|
||||||
|
- Added ordinal, readable label, short detail, active underline state, and completion state to each rail item.
|
||||||
|
- Migrated the scene to Task 2's `projectPreparedLifecycleStep`, including the distinct validate and output-map commands for Diagnose and Repair.
|
||||||
|
- Added one active operation frame containing the beat caption, factual method, equivalent CLI, and existing phase visual.
|
||||||
|
- Kept the prepared assistant/composer behavior and the 26/74 desktop, 28/72 compact hierarchy.
|
||||||
|
- Added editorial frame/rail styling, scoped content motion, reduced-motion handling, and narrow horizontal rail overflow at the 1050px container breakpoint.
|
||||||
|
- Added regression tests for the six-step rail, frame evidence, diagnose/repair rerender choreography, recorded validate phase marker, CSS hierarchy, and narrow behavior.
|
||||||
|
- Left room for the Task 4 bottom Q&A lane; no lower dock or duplicate Scene 7 shell was added.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- RED: the first focused run failed on the stale Scene 9 message-state import, missing frame/rail CSS, and missing recorded-phase visual marker.
|
||||||
|
- GREEN: `pnpm --dir web --filter @lda/console test -- src/presentation/authoring/PreparedAuthoringLifecycleScene.test.tsx src/presentation/authoring/AuthoringPhaseVisual.test.tsx src/presentation/presentation-css.test.ts` -> 3 files, 60 tests passed.
|
||||||
|
- Adjacent authoring regressions -> 4 files, 30 tests passed.
|
||||||
|
- `npx react-doctor@latest --verbose --scope changed` -> no issues, 100/100.
|
||||||
|
- `git diff --check` -> clean.
|
||||||
|
- `pnpm --dir web test` -> 799 passed, 22 failed across 6 files. The failures are stale shared Task 1/2 route, Scene 7, presenter-note, and old Scene 9 expectations, including removed `validate` beat routes and the old `authoring phase rail` label.
|
||||||
|
|
||||||
|
## Deviations
|
||||||
|
|
||||||
|
- The Task 2 transitional `SceneView` union was not tightened in this task. The remaining 9 typecheck diagnostics are in unowned `SceneBody`, presenter notes, route, and related tests, and changing that union here would cross ownership and risk Scene 8 recording-oriented callers. The lifecycle scene itself now consumes the typed `PreparedLifecycleStepId` projection.
|
||||||
|
- Browser smoke was not run. Responsive verification is encoded in CSS tests and rules: the primary split remains 26/74 on the editorial surface, compact mode uses 28/72, and the rail switches to 9.5rem minimum-width horizontal items at 1050px.
|
||||||
|
|
||||||
|
## Bugs And Concerns
|
||||||
|
|
||||||
|
- No Task 3-specific functional bug was found in the focused or adjacent suites.
|
||||||
|
- Full console typecheck remains blocked by the pre-existing 9 `SceneView` union diagnostics described above.
|
||||||
|
- A real 720p and narrow browser pass would still be useful to confirm the evidence header and visual region feel balanced at presentation zoom; the code keeps the frame scrollable and the rail horizontally scrollable.
|
||||||
|
|
||||||
|
## Commit
|
||||||
|
|
||||||
|
PENDING
|
||||||
@@ -51,6 +51,18 @@ describe("AuthoringPhaseVisual", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("keeps diagnosis and repair attached to the recorded validate phase", () => {
|
||||||
|
for (const focus of ["diagnose", "repair"] as const) {
|
||||||
|
render(<AuthoringPhaseVisual projection={projectPreparedAuthoringPhase("validate")} focus={focus} />);
|
||||||
|
|
||||||
|
expect(screen.getByRole("region", { name: /validation repair evidence/i })).toHaveAttribute(
|
||||||
|
"data-authoring-recording-phase",
|
||||||
|
"validate",
|
||||||
|
);
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it.each(["diagnose", "repair"] as const)("marks %s evidence as the primary visual", (focus) => {
|
it.each(["diagnose", "repair"] as const)("marks %s evidence as the primary visual", (focus) => {
|
||||||
render(<AuthoringPhaseVisual projection={projectPreparedAuthoringPhase("validate")} focus={focus} />);
|
render(<AuthoringPhaseVisual projection={projectPreparedAuthoringPhase("validate")} focus={focus} />);
|
||||||
expect(screen.getByRole("region", { name: /validation repair evidence/i })).toHaveAttribute(
|
expect(screen.getByRole("region", { name: /validation repair evidence/i })).toHaveAttribute(
|
||||||
|
|||||||
@@ -44,9 +44,11 @@ const GraphVisual = ({ visual }: { visual: Extract<AuthoringPhaseProjection["vis
|
|||||||
const RepairVisual = ({
|
const RepairVisual = ({
|
||||||
visual,
|
visual,
|
||||||
focus,
|
focus,
|
||||||
|
recordingPhase,
|
||||||
}: {
|
}: {
|
||||||
readonly visual: Extract<AuthoringPhaseProjection["visual"], { kind: "repair" }>;
|
readonly visual: Extract<AuthoringPhaseProjection["visual"], { kind: "repair" }>;
|
||||||
readonly focus: "full" | "diagnose" | "repair";
|
readonly focus: "full" | "diagnose" | "repair";
|
||||||
|
readonly recordingPhase: AuthoringPhaseProjection["phase"];
|
||||||
}) => (
|
}) => (
|
||||||
<section
|
<section
|
||||||
className="authoring-visual authoring-visual--repair"
|
className="authoring-visual authoring-visual--repair"
|
||||||
@@ -54,6 +56,7 @@ const RepairVisual = ({
|
|||||||
data-presentation-surface="editorial"
|
data-presentation-surface="editorial"
|
||||||
data-visual-role="primary"
|
data-visual-role="primary"
|
||||||
data-authoring-focus={focus}
|
data-authoring-focus={focus}
|
||||||
|
data-authoring-recording-phase={recordingPhase}
|
||||||
>
|
>
|
||||||
<div className="authoring-repair__diagnostic"><AlertTriangle aria-hidden="true" /><span>Diagnostic</span><strong>{visual.diagnostic}</strong></div>
|
<div className="authoring-repair__diagnostic"><AlertTriangle aria-hidden="true" /><span>Diagnostic</span><strong>{visual.diagnostic}</strong></div>
|
||||||
<ArrowRight className="authoring-repair__connector" aria-hidden="true" />
|
<ArrowRight className="authoring-repair__connector" aria-hidden="true" />
|
||||||
@@ -86,7 +89,9 @@ export const AuthoringPhaseVisual = ({ projection, focus = "full" }: AuthoringPh
|
|||||||
switch (projection.visual.kind) {
|
switch (projection.visual.kind) {
|
||||||
case "inventory": return <InventoryVisual visual={projection.visual} />;
|
case "inventory": return <InventoryVisual visual={projection.visual} />;
|
||||||
case "graph": return <GraphVisual visual={projection.visual} />;
|
case "graph": return <GraphVisual visual={projection.visual} />;
|
||||||
case "repair": return <RepairVisual visual={projection.visual} focus={focus} />;
|
// Diagnose and repair deliberately share this factual validate visual while
|
||||||
|
// the lifecycle scene supplies the distinct recorded command and focus.
|
||||||
|
case "repair": return <RepairVisual visual={projection.visual} focus={focus} recordingPhase={projection.phase} />;
|
||||||
case "artifact": return <ArtifactVisual visual={projection.visual} />;
|
case "artifact": return <ArtifactVisual visual={projection.visual} />;
|
||||||
case "bindings": return <BindingsVisual visual={projection.visual} />;
|
case "bindings": return <BindingsVisual visual={projection.visual} />;
|
||||||
}
|
}
|
||||||
|
|||||||
+62
-24
@@ -20,29 +20,56 @@ describe("PreparedAuthoringLifecycleScene", () => {
|
|||||||
"data-presentation-surface",
|
"data-presentation-surface",
|
||||||
"editorial",
|
"editorial",
|
||||||
);
|
);
|
||||||
expect(screen.getByText("Discover")).toBeInTheDocument();
|
expect(within(screen.getByRole("list", { name: /prepared authoring lifecycle/i })).getByText("Discover"))
|
||||||
|
.toBeInTheDocument();
|
||||||
expect(screen.getAllByText(/sources|capabilities|schema/i).length).toBeGreaterThanOrEqual(1);
|
expect(screen.getAllByText(/sources|capabilities|schema/i).length).toBeGreaterThanOrEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("draft shows graph or routes", () => {
|
it("draft shows graph or routes", () => {
|
||||||
renderBeat("draft");
|
renderBeat("draft");
|
||||||
expect(screen.getByText("Draft")).toBeInTheDocument();
|
expect(within(screen.getByRole("list", { name: /prepared authoring lifecycle/i })).getByText("Author"))
|
||||||
|
.toBeInTheDocument();
|
||||||
expect(screen.getAllByText(/graph|routes/i).length).toBeGreaterThanOrEqual(1);
|
expect(screen.getAllByText(/graph|routes/i).length).toBeGreaterThanOrEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("validate shows diagnosis and repair", () => {
|
it("diagnose shows the structured validation operation in its frame", () => {
|
||||||
renderBeat("validate");
|
renderBeat("diagnose");
|
||||||
expect(screen.getByRole("region", { name: "prepared workflow authoring lifecycle" })).toHaveAttribute(
|
expect(screen.getByRole("region", { name: "prepared workflow authoring lifecycle" })).toHaveAttribute(
|
||||||
"data-presentation-surface",
|
"data-presentation-surface",
|
||||||
"editorial",
|
"editorial",
|
||||||
);
|
);
|
||||||
expect(screen.getByText("Validate")).toBeInTheDocument();
|
const frame = screen.getByRole("region", { name: /active authoring operation/i });
|
||||||
expect(screen.getAllByText(/diagnos|repair/i).length).toBeGreaterThanOrEqual(1);
|
expect(frame).toHaveTextContent("workflow.draft_workspaces.validate");
|
||||||
|
expect(frame).toHaveTextContent("wf draft validate lda_report_workflow");
|
||||||
|
expect(frame).toHaveTextContent(/structured missing-output diagnostic/i);
|
||||||
|
expect(frame).toHaveAttribute("data-authoring-step", "diagnose");
|
||||||
|
expect(screen.getByRole("region", { name: "validation repair evidence" })).toHaveAttribute(
|
||||||
|
"data-authoring-focus",
|
||||||
|
"diagnose",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("repair shows the output-map operation over the same validation visual", () => {
|
||||||
|
const scene = findScene("prepared-lifecycle");
|
||||||
|
const repairBeat = findBeat("prepared-lifecycle", "repair");
|
||||||
|
if (!scene || !repairBeat) throw new Error("missing prepared-lifecycle/repair");
|
||||||
|
|
||||||
|
const { rerender } = renderBeat("diagnose");
|
||||||
|
rerender(<PreparedAuthoringLifecycleScene scene={scene} beat={repairBeat} />);
|
||||||
|
const frame = screen.getByRole("region", { name: /active authoring operation/i });
|
||||||
|
expect(frame).toHaveTextContent("workflow.draft_workspaces.set_step_output_map");
|
||||||
|
expect(frame).toHaveTextContent(/wf draft set-output lda_report_workflow/i);
|
||||||
|
expect(frame).toHaveAttribute("data-authoring-step", "repair");
|
||||||
|
expect(screen.getByRole("region", { name: "validation repair evidence" })).toHaveAttribute(
|
||||||
|
"data-authoring-focus",
|
||||||
|
"repair",
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("artifact shows immutable ID and version", () => {
|
it("artifact shows immutable ID and version", () => {
|
||||||
renderBeat("artifact");
|
renderBeat("artifact");
|
||||||
expect(screen.getByText("Artifact")).toBeInTheDocument();
|
expect(within(screen.getByRole("list", { name: /prepared authoring lifecycle/i })).getByText("Artifact"))
|
||||||
|
.toBeInTheDocument();
|
||||||
expect(screen.getAllByText(/lda_report_case_study/i).length).toBeGreaterThanOrEqual(1);
|
expect(screen.getAllByText(/lda_report_case_study/i).length).toBeGreaterThanOrEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -55,7 +82,8 @@ describe("PreparedAuthoringLifecycleScene", () => {
|
|||||||
it.each([
|
it.each([
|
||||||
["discover", "discovery evidence"],
|
["discover", "discovery evidence"],
|
||||||
["draft", "draft graph evidence"],
|
["draft", "draft graph evidence"],
|
||||||
["validate", "validation repair evidence"],
|
["diagnose", "validation repair evidence"],
|
||||||
|
["repair", "validation repair evidence"],
|
||||||
["artifact", "artifact evidence"],
|
["artifact", "artifact evidence"],
|
||||||
["deployment", "deployment binding evidence"],
|
["deployment", "deployment binding evidence"],
|
||||||
] as const)("renders %s as the primary phase visual", (beatId, label) => {
|
] as const)("renders %s as the primary phase visual", (beatId, label) => {
|
||||||
@@ -68,9 +96,11 @@ describe("PreparedAuthoringLifecycleScene", () => {
|
|||||||
|
|
||||||
const workspace = screen.getByRole("region", { name: "prepared workflow authoring lifecycle" });
|
const workspace = screen.getByRole("region", { name: "prepared workflow authoring lifecycle" });
|
||||||
const assistant = screen.getByRole("complementary", { name: /prepared authoring assistant/i });
|
const assistant = screen.getByRole("complementary", { name: /prepared authoring assistant/i });
|
||||||
|
const frame = screen.getByRole("region", { name: "active authoring operation" });
|
||||||
const visual = screen.getByRole("region", { name: "draft graph evidence" });
|
const visual = screen.getByRole("region", { name: "draft graph evidence" });
|
||||||
|
|
||||||
expect(workspace).toContainElement(assistant);
|
expect(workspace).toContainElement(assistant);
|
||||||
|
expect(workspace).toContainElement(frame);
|
||||||
expect(workspace).toContainElement(visual);
|
expect(workspace).toContainElement(visual);
|
||||||
expect(assistant).toHaveAttribute("data-phase", "draft");
|
expect(assistant).toHaveAttribute("data-phase", "draft");
|
||||||
expect(assistant).toHaveAttribute("data-surface", "prepared-replay");
|
expect(assistant).toHaveAttribute("data-surface", "prepared-replay");
|
||||||
@@ -78,7 +108,7 @@ describe("PreparedAuthoringLifecycleScene", () => {
|
|||||||
expect(assistant.querySelector('[data-surface="stage"]')).toBeInTheDocument();
|
expect(assistant.querySelector('[data-surface="stage"]')).toBeInTheDocument();
|
||||||
expect(workspace.querySelector(".prepared-lifecycle-scene__dock")).not.toBeInTheDocument();
|
expect(workspace.querySelector(".prepared-lifecycle-scene__dock")).not.toBeInTheDocument();
|
||||||
expect(workspace.querySelector("[role='dialog']")).not.toBeInTheDocument();
|
expect(workspace.querySelector("[role='dialog']")).not.toBeInTheDocument();
|
||||||
expect(workspace.querySelector("[aria-label='authoring phase rail']"))
|
expect(workspace.querySelector("[aria-label='prepared authoring lifecycle']"))
|
||||||
.toBeInTheDocument();
|
.toBeInTheDocument();
|
||||||
expect(visual).toHaveAttribute("data-presentation-surface", "editorial");
|
expect(visual).toHaveAttribute("data-presentation-surface", "editorial");
|
||||||
expect(visual).toHaveAttribute("data-visual-role", "primary");
|
expect(visual).toHaveAttribute("data-visual-role", "primary");
|
||||||
@@ -101,7 +131,8 @@ describe("PreparedAuthoringLifecycleScene", () => {
|
|||||||
it.each([
|
it.each([
|
||||||
["draft", true],
|
["draft", true],
|
||||||
["artifact", true],
|
["artifact", true],
|
||||||
["validate", false],
|
["diagnose", false],
|
||||||
|
["repair", false],
|
||||||
["deployment", false],
|
["deployment", false],
|
||||||
] as const)("%s submission advances only when its beat owns the transition", async (beatId, advances) => {
|
] as const)("%s submission advances only when its beat owns the transition", async (beatId, advances) => {
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
@@ -109,7 +140,9 @@ describe("PreparedAuthoringLifecycleScene", () => {
|
|||||||
renderBeat(beatId, onAdvance);
|
renderBeat(beatId, onAdvance);
|
||||||
|
|
||||||
const input = screen.getByRole("textbox", { name: /message to authoring assistant/i });
|
const input = screen.getByRole("textbox", { name: /message to authoring assistant/i });
|
||||||
if (beatId === "validate") await user.type(input, "Review the validation result.");
|
if (beatId === "diagnose" || beatId === "repair") {
|
||||||
|
await user.type(input, "Review the validation result.");
|
||||||
|
}
|
||||||
await user.click(screen.getByRole("button", { name: /send message/i }));
|
await user.click(screen.getByRole("button", { name: /send message/i }));
|
||||||
|
|
||||||
expect(onAdvance).toHaveBeenCalledTimes(advances ? 1 : 0);
|
expect(onAdvance).toHaveBeenCalledTimes(advances ? 1 : 0);
|
||||||
@@ -118,31 +151,33 @@ describe("PreparedAuthoringLifecycleScene", () => {
|
|||||||
it.each([
|
it.each([
|
||||||
["discover", "Discover"],
|
["discover", "Discover"],
|
||||||
["draft", "Draft"],
|
["draft", "Draft"],
|
||||||
["validate", "Validate"],
|
["diagnose", "Diagnose"],
|
||||||
|
["repair", "Repair"],
|
||||||
["artifact", "Artifact"],
|
["artifact", "Artifact"],
|
||||||
["deployment", "Deployment"],
|
["deployment", "Deployment"],
|
||||||
] as const)("marks %s as the active lifecycle evidence beat", (beatId, label) => {
|
] as const)("marks %s as the active lifecycle evidence beat", (beatId, label) => {
|
||||||
renderBeat(beatId);
|
renderBeat(beatId);
|
||||||
const evidence = screen.getByRole("region", { name: /active lifecycle evidence/i });
|
const evidence = screen.getByRole("region", { name: /active authoring operation/i });
|
||||||
const workspace = screen.getByRole("region", { name: "prepared workflow authoring lifecycle" });
|
const workspace = screen.getByRole("region", { name: "prepared workflow authoring lifecycle" });
|
||||||
|
|
||||||
expect(evidence).toHaveAttribute("data-visual-role", "lifecycle-primary");
|
expect(evidence).toHaveAttribute("data-visual-role", "lifecycle-primary");
|
||||||
expect(workspace.querySelectorAll('[data-visual-role="lifecycle-primary"]')).toHaveLength(1);
|
expect(workspace.querySelectorAll('[data-visual-role="lifecycle-primary"]')).toHaveLength(1);
|
||||||
expect(screen.getByLabelText("authoring phase rail").querySelector('[data-active="true"]'))
|
expect(screen.getByRole("list", { name: /prepared authoring lifecycle/i }).querySelector('[data-active="true"]'))
|
||||||
.toHaveTextContent(label);
|
.toHaveTextContent(label);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("synchronizes the assistant phase, active group, rail, and visual", () => {
|
it("synchronizes the assistant phase, active group, rail, and visual", () => {
|
||||||
renderBeat("validate");
|
renderBeat("diagnose");
|
||||||
|
|
||||||
expect(screen.getByRole("complementary", { name: /prepared authoring assistant/i }))
|
expect(screen.getByRole("complementary", { name: /prepared authoring assistant/i }))
|
||||||
.toHaveAttribute("data-phase", "validate");
|
.toHaveAttribute("data-phase", "diagnose");
|
||||||
expect(screen.getByRole("button", { name: /validate.*2 tool calls/i }))
|
expect(screen.getByRole("button", { name: /validate.*2 tool calls/i }))
|
||||||
.toHaveAttribute("aria-expanded", "true");
|
.toHaveAttribute("aria-expanded", "true");
|
||||||
expect(screen.getByRole("button", { name: /draft.*3 tool calls/i }))
|
expect(screen.getByRole("button", { name: /draft.*3 tool calls/i }))
|
||||||
.toHaveAttribute("aria-expanded", "false");
|
.toHaveAttribute("aria-expanded", "false");
|
||||||
expect(screen.getByLabelText("authoring phase rail").querySelector("[data-active='true']"))
|
expect(screen.getByRole("list", { name: /prepared authoring lifecycle/i })
|
||||||
.toHaveTextContent("Validate");
|
.querySelector("[data-active='true']"))
|
||||||
|
.toHaveTextContent("Diagnose");
|
||||||
const chat = screen.getByRole("log", { name: "prepared authoring conversation" });
|
const chat = screen.getByRole("log", { name: "prepared authoring conversation" });
|
||||||
expect(chat).toHaveAttribute("data-surface", "stage");
|
expect(chat).toHaveAttribute("data-surface", "stage");
|
||||||
expect(screen.getByRole("region", { name: "validation repair evidence" })).toHaveAttribute(
|
expect(screen.getByRole("region", { name: "validation repair evidence" })).toHaveAttribute(
|
||||||
@@ -152,22 +187,25 @@ describe("PreparedAuthoringLifecycleScene", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("does not render the obsolete trace modal or receipt", () => {
|
it("does not render the obsolete trace modal or receipt", () => {
|
||||||
renderBeat("validate");
|
renderBeat("diagnose");
|
||||||
expect(screen.queryByRole("button", { name: "Agent trace" })).not.toBeInTheDocument();
|
expect(screen.queryByRole("button", { name: "Agent trace" })).not.toBeInTheDocument();
|
||||||
expect(screen.queryByRole("dialog", { name: "Authoring trace" })).not.toBeInTheDocument();
|
expect(screen.queryByRole("dialog", { name: "Authoring trace" })).not.toBeInTheDocument();
|
||||||
expect(screen.queryByLabelText("prepared authoring receipt")).not.toBeInTheDocument();
|
expect(screen.queryByLabelText("prepared authoring receipt")).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders a compact orientation rail", () => {
|
it("renders the six-step prepared authoring lifecycle rail", () => {
|
||||||
renderBeat("artifact");
|
renderBeat("artifact");
|
||||||
const rail = screen.getByLabelText("authoring phase rail");
|
const rail = screen.getByRole("list", { name: /prepared authoring lifecycle/i });
|
||||||
expect(rail).toBeInTheDocument();
|
expect(rail).toBeInTheDocument();
|
||||||
expect(rail.children.length).toBeGreaterThanOrEqual(5);
|
expect(within(rail).getAllByRole("listitem")).toHaveLength(6);
|
||||||
|
expect(within(rail).getByText("Diagnose")).toBeInTheDocument();
|
||||||
|
expect(within(rail).getByText("Repair")).toBeInTheDocument();
|
||||||
|
expect(within(rail).getByText("Sources, capabilities, schemas")).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("highlights the active phase in the rail", () => {
|
it("highlights the active phase in the rail", () => {
|
||||||
renderBeat("deployment");
|
renderBeat("deployment");
|
||||||
const rail = screen.getByLabelText("authoring phase rail");
|
const rail = screen.getByRole("list", { name: /prepared authoring lifecycle/i });
|
||||||
const active = rail.querySelector("[data-active='true']");
|
const active = rail.querySelector("[data-active='true']");
|
||||||
expect(active).toBeInTheDocument();
|
expect(active).toBeInTheDocument();
|
||||||
expect(active).toHaveTextContent("Deployment");
|
expect(active).toHaveTextContent("Deployment");
|
||||||
@@ -181,7 +219,7 @@ describe("PreparedAuthoringLifecycleScene", () => {
|
|||||||
render(<PreparedAuthoringLifecycleScene scene={scene} beat={unexpectedBeat} />);
|
render(<PreparedAuthoringLifecycleScene scene={scene} beat={unexpectedBeat} />);
|
||||||
|
|
||||||
expect(screen.getByRole("region", { name: "discovery evidence" })).toBeInTheDocument();
|
expect(screen.getByRole("region", { name: "discovery evidence" })).toBeInTheDocument();
|
||||||
expect(screen.getByLabelText("authoring phase rail").querySelector("[data-active='true']"))
|
expect(screen.getByRole("list", { name: /prepared authoring lifecycle/i }).querySelector("[data-active='true']"))
|
||||||
.toHaveTextContent("Discover");
|
.toHaveTextContent("Discover");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import { useReducer } from "react";
|
import { useReducer } from "react";
|
||||||
import { projectPreparedAuthoringPhase } from "./authoring-projection.js";
|
import {
|
||||||
|
projectPreparedLifecycleStep,
|
||||||
|
type PreparedLifecycleStepId,
|
||||||
|
} from "./authoring-projection.js";
|
||||||
import { AuthoringPhaseVisual } from "./AuthoringPhaseVisual.js";
|
import { AuthoringPhaseVisual } from "./AuthoringPhaseVisual.js";
|
||||||
import { PresentationAssistantPane } from "./PresentationAssistantPane.js";
|
import { PresentationAssistantPane } from "./PresentationAssistantPane.js";
|
||||||
import type { AuthoringPhaseId } from "./authoring-recording.js";
|
|
||||||
import {
|
import {
|
||||||
initialScene9MessageState,
|
initialPreparedLifecycleMessageState,
|
||||||
projectScene9Message,
|
projectPreparedLifecycleMessage,
|
||||||
projectScene9SubmittedOverrides,
|
projectPreparedLifecycleSubmittedOverrides,
|
||||||
scene9MessageReducer,
|
preparedLifecycleMessageReducer,
|
||||||
} from "./scene9-message-state.js";
|
} from "./prepared-lifecycle-message-state.js";
|
||||||
import type { SceneBeatDefinition, SceneDefinition } from "../storyboard.js";
|
import type { SceneBeatDefinition, SceneDefinition } from "../storyboard.js";
|
||||||
import { StageCaption } from "../StageCaption.js";
|
import { StageCaption } from "../StageCaption.js";
|
||||||
|
|
||||||
@@ -18,13 +20,18 @@ type PreparedAuthoringLifecycleSceneProps = {
|
|||||||
readonly onAdvance?: (() => void) | undefined;
|
readonly onAdvance?: (() => void) | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const phases: readonly { readonly id: AuthoringPhaseId; readonly label: string }[] = [
|
const steps = [
|
||||||
{ id: "discover", label: "Discover" },
|
{ id: "discover", label: "Discover", detail: "Sources, capabilities, schemas" },
|
||||||
{ id: "draft", label: "Draft" },
|
{ id: "draft", label: "Author", detail: "Create and edit mutable Draft" },
|
||||||
{ id: "validate", label: "Validate" },
|
{ id: "diagnose", label: "Diagnose", detail: "Structured validation result" },
|
||||||
{ id: "artifact", label: "Artifact" },
|
{ id: "repair", label: "Repair", detail: "Focused output-map edit" },
|
||||||
{ id: "deployment", label: "Deployment" },
|
{ id: "artifact", label: "Artifact", detail: "Immutable versioned definition" },
|
||||||
];
|
{ id: "deployment", label: "Deployment", detail: "Bind and validate sources" },
|
||||||
|
] as const satisfies readonly {
|
||||||
|
readonly id: PreparedLifecycleStepId;
|
||||||
|
readonly label: string;
|
||||||
|
readonly detail: string;
|
||||||
|
}[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scene 9 — Prepared workflow authoring lifecycle.
|
* Scene 9 — Prepared workflow authoring lifecycle.
|
||||||
@@ -34,66 +41,96 @@ const phases: readonly { readonly id: AuthoringPhaseId; readonly label: string }
|
|||||||
*/
|
*/
|
||||||
export const PreparedAuthoringLifecycleScene = ({ scene, beat, onAdvance }: PreparedAuthoringLifecycleSceneProps) => {
|
export const PreparedAuthoringLifecycleScene = ({ scene, beat, onAdvance }: PreparedAuthoringLifecycleSceneProps) => {
|
||||||
const [messageState, dispatch] = useReducer(
|
const [messageState, dispatch] = useReducer(
|
||||||
scene9MessageReducer,
|
preparedLifecycleMessageReducer,
|
||||||
initialScene9MessageState,
|
initialPreparedLifecycleMessageState,
|
||||||
);
|
);
|
||||||
// Storyboard beats normally match these IDs. Discovery is a safe projection
|
// Storyboard beats normally match these IDs. Discovery is a safe projection
|
||||||
// if a future beat reaches this scene before its authoring mapping is added.
|
// if a future beat reaches this scene before its authoring mapping is added.
|
||||||
const beatId = phases.find((phase) => phase.id === beat.id)?.id ?? "discover";
|
const activeStep = steps.find((candidate) => candidate.id === beat.id) ?? steps[0]!;
|
||||||
const projection = projectPreparedAuthoringPhase(beatId);
|
const step = activeStep.id;
|
||||||
|
const projection = projectPreparedLifecycleStep(step);
|
||||||
|
const activeStepIndex = steps.findIndex((candidate) => candidate.id === step);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{/* Keep external chrome to scene orientation; beat-specific copy belongs in the frame. */}
|
||||||
<StageCaption eyebrow="Prepared workflow" title={scene.title}>
|
<StageCaption eyebrow="Prepared workflow" title={scene.title}>
|
||||||
<p>{beat.caption}</p>
|
{null}
|
||||||
</StageCaption>
|
</StageCaption>
|
||||||
<section
|
<section
|
||||||
className="prepared-lifecycle-scene"
|
className="prepared-lifecycle-scene"
|
||||||
aria-label="prepared workflow authoring lifecycle"
|
aria-label="prepared workflow authoring lifecycle"
|
||||||
data-active-phase={beatId}
|
data-active-phase={step}
|
||||||
|
data-recording-phase={projection.recordingPhase}
|
||||||
data-primary-surface="authoring-phase"
|
data-primary-surface="authoring-phase"
|
||||||
data-support-surface="prepared-chat"
|
data-support-surface="prepared-chat"
|
||||||
data-presentation-surface="editorial"
|
data-presentation-surface="editorial"
|
||||||
>
|
>
|
||||||
<PresentationAssistantPane
|
<PresentationAssistantPane
|
||||||
phase={beatId}
|
phase={step}
|
||||||
visualRole="support"
|
visualRole="support"
|
||||||
message={projectScene9Message(messageState, beatId)}
|
message={projectPreparedLifecycleMessage(messageState, step)}
|
||||||
submittedOverrides={projectScene9SubmittedOverrides(messageState)}
|
submittedOverrides={projectPreparedLifecycleSubmittedOverrides(messageState)}
|
||||||
runRequested={messageState.runRequested}
|
runRequested={messageState.runRequested}
|
||||||
onDraftChange={(draft) => dispatch({ type: "draft_edited", draft })}
|
onDraftChange={(draft) => dispatch({ type: "draft_edited", draft })}
|
||||||
onSubmit={(submittedText) => {
|
onSubmit={(submittedText) => {
|
||||||
dispatch({ type: "draft_edited", draft: submittedText });
|
dispatch({ type: "draft_edited", draft: submittedText });
|
||||||
if (beatId === "discover") {
|
if (step === "discover") {
|
||||||
dispatch({ type: "discover_submitted" });
|
dispatch({ type: "discover_submitted" });
|
||||||
}
|
}
|
||||||
if (beatId === "draft") {
|
if (step === "draft") {
|
||||||
dispatch({ type: "draft_submitted" });
|
dispatch({ type: "draft_submitted" });
|
||||||
onAdvance?.();
|
onAdvance?.();
|
||||||
}
|
}
|
||||||
if (beatId === "artifact") {
|
if (step === "artifact") {
|
||||||
dispatch({ type: "artifact_submitted" });
|
dispatch({ type: "artifact_submitted" });
|
||||||
onAdvance?.();
|
onAdvance?.();
|
||||||
}
|
}
|
||||||
if (beatId === "deployment") dispatch({ type: "run_requested" });
|
if (step === "deployment") dispatch({ type: "run_requested" });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="prepared-lifecycle-scene__presentation">
|
<div className="prepared-lifecycle-scene__presentation">
|
||||||
<ol className="prepared-lifecycle-scene__rail" aria-label="authoring phase rail">
|
<ol className="prepared-lifecycle-scene__rail" aria-label="prepared authoring lifecycle">
|
||||||
{phases.map((phase) => (
|
{steps.map((candidate, index) => (
|
||||||
<li key={phase.id} data-active={phase.id === beatId ? "true" : "false"}>
|
<li
|
||||||
<strong>{phase.label}</strong>
|
key={candidate.id}
|
||||||
|
data-active={candidate.id === step ? "true" : "false"}
|
||||||
|
data-complete={index < activeStepIndex ? "true" : "false"}
|
||||||
|
>
|
||||||
|
<span className="prepared-lifecycle-scene__ordinal" aria-hidden="true">
|
||||||
|
{String(index + 1).padStart(2, "0")}
|
||||||
|
</span>
|
||||||
|
<strong>{candidate.label}</strong>
|
||||||
|
<span className="prepared-lifecycle-scene__detail">{candidate.detail}</span>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<article
|
<article
|
||||||
className="prepared-lifecycle-scene__projection"
|
className="prepared-lifecycle-scene__frame"
|
||||||
role="region"
|
role="region"
|
||||||
aria-label="active lifecycle evidence"
|
aria-label="active authoring operation"
|
||||||
|
data-authoring-step={step}
|
||||||
|
data-recording-phase={projection.recordingPhase}
|
||||||
data-visual-role="lifecycle-primary"
|
data-visual-role="lifecycle-primary"
|
||||||
key={beatId}
|
|
||||||
>
|
>
|
||||||
<AuthoringPhaseVisual projection={projection} />
|
<header className="prepared-lifecycle-scene__frame-header">
|
||||||
|
<div>
|
||||||
|
<span className="prepared-lifecycle-scene__frame-step">{activeStep.label}</span>
|
||||||
|
<h2>{beat.title}</h2>
|
||||||
|
<p>{beat.caption}</p>
|
||||||
|
</div>
|
||||||
|
<dl className="prepared-lifecycle-scene__evidence">
|
||||||
|
<div>
|
||||||
|
<dt>Method</dt>
|
||||||
|
<dd><code>{projection.primaryCommand.title}</code></dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt>Equivalent CLI</dt>
|
||||||
|
<dd><code>{projection.primaryCommand.command}</code></dd>
|
||||||
|
</div>
|
||||||
|
</dl>
|
||||||
|
</header>
|
||||||
|
<AuthoringPhaseVisual projection={projection} focus={projection.focus} />
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ describe("presentation.css", () => {
|
|||||||
expect(boardBlock).toContain("flex-shrink: 0");
|
expect(boardBlock).toContain("flex-shrink: 0");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("keeps Scene 9 as a bounded adaptive split without a lower dock row", () => {
|
it("keeps Scene 9 as a bounded 26/74 split without a lower dock row", () => {
|
||||||
const sceneBlock = css.match(
|
const sceneBlock = css.match(
|
||||||
/^\.prepared-lifecycle-scene\s*\{(?<body>[\s\S]*?)\n\}/m,
|
/^\.prepared-lifecycle-scene\s*\{(?<body>[\s\S]*?)\n\}/m,
|
||||||
)?.groups?.body;
|
)?.groups?.body;
|
||||||
@@ -98,7 +98,7 @@ describe("presentation.css", () => {
|
|||||||
expect(css).not.toMatch(/prepared-lifecycle-scene__dock[\s\S]*position:\s*(absolute|fixed)/);
|
expect(css).not.toMatch(/prepared-lifecycle-scene__dock[\s\S]*position:\s*(absolute|fixed)/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("keeps every Scene 9 surface override on the clarity-focused 35/65 split", () => {
|
it("keeps every Scene 9 surface override on the editorial 26/74 split", () => {
|
||||||
const scene9Rules = css.match(/\.prepared-lifecycle-scene[^{}]*\{[^}]*\}/g) ?? [];
|
const scene9Rules = css.match(/\.prepared-lifecycle-scene[^{}]*\{[^}]*\}/g) ?? [];
|
||||||
|
|
||||||
expect(scene9Rules.filter((rule) => rule.includes("grid-template-columns:")).length).toBeGreaterThan(0);
|
expect(scene9Rules.filter((rule) => rule.includes("grid-template-columns:")).length).toBeGreaterThan(0);
|
||||||
@@ -106,6 +106,35 @@ describe("presentation.css", () => {
|
|||||||
expect(scene9Rules.join("\n")).toMatch(/minmax\(12rem, 0\.26fr\)\s+minmax\(0, 0\.74fr\)/);
|
expect(scene9Rules.join("\n")).toMatch(/minmax\(12rem, 0\.26fr\)\s+minmax\(0, 0\.74fr\)/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("gives the prepared lifecycle rail and operation frame the primary hierarchy", () => {
|
||||||
|
const frameRules = cssBlocks(css, '.prepared-lifecycle-scene[data-presentation-surface="editorial"] .prepared-lifecycle-scene__frame');
|
||||||
|
const frameContent = cssBlock(css, '.prepared-lifecycle-scene[data-presentation-surface="editorial"] .prepared-lifecycle-scene__frame > *');
|
||||||
|
|
||||||
|
expect(cssBlocks(css, '.prepared-lifecycle-scene[data-presentation-surface="editorial"] .prepared-lifecycle-scene__rail')
|
||||||
|
.some((body) => body.includes("grid-template-columns: repeat(6, minmax(0, 1fr));"))).toBe(true);
|
||||||
|
expect(cssBlocks(css, '.prepared-lifecycle-scene[data-presentation-surface="editorial"] .prepared-lifecycle-scene__rail')
|
||||||
|
.some((body) => body.includes("min-height: 5.4rem;"))).toBe(true);
|
||||||
|
expect(cssBlocks(css, '.prepared-lifecycle-scene[data-presentation-surface="editorial"] .prepared-lifecycle-scene__rail li')
|
||||||
|
.some((body) => body.includes("opacity: 0.78;"))).toBe(true);
|
||||||
|
expect(cssBlocks(css, ".prepared-lifecycle-scene__rail li")
|
||||||
|
.some((body) => body.includes("font: 600 1rem/1.1 var(--font-interface);"))).toBe(true);
|
||||||
|
expect(css).toContain("font: 500 0.72rem/1.25 var(--font-interface);");
|
||||||
|
expect(frameRules.some((body) => body.includes("grid-template-rows: auto minmax(0, 1fr);"))).toBe(true);
|
||||||
|
expect(frameContent).toContain("animation: authoring-frame-content-enter 220ms");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps the lifecycle rail scrollable at narrow presentation widths", () => {
|
||||||
|
const compactRail = cssBlock(
|
||||||
|
cssBlock(css, "@container presentation-canvas (max-width: 1050px)") ?? "",
|
||||||
|
'.prepared-lifecycle-scene[data-presentation-surface="editorial"] .prepared-lifecycle-scene__rail',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(compactRail).toContain("grid-template-columns: none;");
|
||||||
|
expect(compactRail).toContain("grid-auto-columns: minmax(9.5rem, 1fr);");
|
||||||
|
expect(compactRail).toContain("overflow-x: auto;");
|
||||||
|
expect(compactRail).toContain("scrollbar-width: none;");
|
||||||
|
});
|
||||||
|
|
||||||
it("bounds Scene 9 conversation scrolling and recenters Scene 8 at compact stage widths", () => {
|
it("bounds Scene 9 conversation scrolling and recenters Scene 8 at compact stage widths", () => {
|
||||||
expect(css.match(/\.presentation-stage\[data-scene-view="agent"\] \.presentation-stage__primary\s*\{/g)).toHaveLength(2);
|
expect(css.match(/\.presentation-stage\[data-scene-view="agent"\] \.presentation-stage__primary\s*\{/g)).toHaveLength(2);
|
||||||
expect(cssBlocks(css, ".presentation-stage__primary > .agent-handoff-scene")
|
expect(cssBlocks(css, ".presentation-stage__primary > .agent-handoff-scene")
|
||||||
|
|||||||
@@ -3694,8 +3694,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.prepared-lifecycle-scene__rail {
|
.prepared-lifecycle-scene__rail {
|
||||||
display: flex;
|
display: grid;
|
||||||
gap: 0.35rem;
|
grid-template-columns: repeat(6, minmax(0, 1fr));
|
||||||
|
gap: 0;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0.45rem 0.55rem;
|
padding: 0.45rem 0.55rem;
|
||||||
@@ -3705,30 +3706,129 @@
|
|||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.prepared-lifecycle-scene__rail::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.prepared-lifecycle-scene__rail li {
|
.prepared-lifecycle-scene__rail li {
|
||||||
display: flex;
|
display: grid;
|
||||||
align-items: center;
|
grid-template-columns: auto minmax(0, 1fr);
|
||||||
gap: 0.3rem;
|
grid-template-rows: auto auto;
|
||||||
padding: 0.25rem 0.45rem;
|
align-content: center;
|
||||||
border-radius: 0.3rem;
|
column-gap: 0.5rem;
|
||||||
font: 600 0.65rem/1 var(--font-interface);
|
row-gap: 0.2rem;
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 5.4rem;
|
||||||
|
padding: 0.65rem 0.7rem 0.55rem;
|
||||||
|
border-bottom: 2px solid transparent;
|
||||||
|
font: 600 1rem/1.1 var(--font-interface);
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
|
opacity: 0.78;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
transition: border-color 200ms ease, color 200ms ease, opacity 200ms ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prepared-lifecycle-scene__rail li[data-active="true"] {
|
.prepared-lifecycle-scene__rail li[data-active="true"] {
|
||||||
background: color-mix(in oklch, var(--accent-cyan) 14%, transparent);
|
|
||||||
color: var(--accent-cyan);
|
color: var(--accent-cyan);
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prepared-lifecycle-scene__projection {
|
.prepared-lifecycle-scene__rail li[data-complete="true"] {
|
||||||
|
color: var(--text-primary);
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prepared-lifecycle-scene__ordinal {
|
||||||
|
grid-row: 1 / -1;
|
||||||
|
color: var(--accent-cyan);
|
||||||
|
font: 600 0.68rem/1 var(--font-mono);
|
||||||
|
}
|
||||||
|
|
||||||
|
.prepared-lifecycle-scene__detail {
|
||||||
|
grid-column: 2;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font: 500 0.72rem/1.25 var(--font-interface);
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prepared-lifecycle-scene__frame {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: auto minmax(0, 1fr);
|
||||||
|
gap: 0.9rem;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
padding: 0.9rem 1rem;
|
padding: 0.9rem 1rem;
|
||||||
background: color-mix(in oklch, var(--stage-surface) 60%, var(--stage-inset));
|
background: color-mix(in oklch, var(--stage-surface) 60%, var(--stage-inset));
|
||||||
border: 1px solid var(--stage-line);
|
border: 1px solid var(--stage-line);
|
||||||
border-radius: 0.65rem;
|
border-radius: 0.65rem;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
animation: authoring-canvas-enter 180ms cubic-bezier(0.22, 1, 0.36, 1);
|
}
|
||||||
|
|
||||||
|
.prepared-lifecycle-scene__frame > * {
|
||||||
|
min-width: 0;
|
||||||
|
animation: authoring-frame-content-enter 220ms cubic-bezier(0.22, 1, 0.36, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.prepared-lifecycle-scene__frame-header {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) minmax(16rem, 0.7fr);
|
||||||
|
gap: 1.2rem;
|
||||||
|
align-items: start;
|
||||||
|
padding-bottom: 0.8rem;
|
||||||
|
border-bottom: 1px solid var(--stage-line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.prepared-lifecycle-scene__frame-header h2,
|
||||||
|
.prepared-lifecycle-scene__frame-header p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prepared-lifecycle-scene__frame-header h2 {
|
||||||
|
margin-top: 0.18rem;
|
||||||
|
font: 600 1.45rem/1.1 var(--font-display);
|
||||||
|
}
|
||||||
|
|
||||||
|
.prepared-lifecycle-scene__frame-header p {
|
||||||
|
max-width: 42rem;
|
||||||
|
margin-top: 0.45rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.84rem;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prepared-lifecycle-scene__frame-step {
|
||||||
|
color: var(--accent-cyan);
|
||||||
|
font: 600 0.68rem/1 var(--font-mono);
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prepared-lifecycle-scene__evidence {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prepared-lifecycle-scene__evidence > div {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.18rem;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prepared-lifecycle-scene__evidence dt {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font: 600 0.62rem/1 var(--font-mono);
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prepared-lifecycle-scene__evidence dd {
|
||||||
|
min-width: 0;
|
||||||
|
margin: 0;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prepared-lifecycle-scene__evidence code {
|
||||||
|
font: 600 0.72rem/1.3 var(--font-mono);
|
||||||
}
|
}
|
||||||
|
|
||||||
.stage-caption:has(+ .prepared-lifecycle-scene) {
|
.stage-caption:has(+ .prepared-lifecycle-scene) {
|
||||||
@@ -3852,6 +3952,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .prepared-lifecycle-scene__rail {
|
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .prepared-lifecycle-scene__rail {
|
||||||
|
grid-template-columns: repeat(6, minmax(0, 1fr));
|
||||||
|
min-height: 5.4rem;
|
||||||
padding: 0.45rem 0;
|
padding: 0.45rem 0;
|
||||||
border-bottom: 1px solid var(--authoring-rule);
|
border-bottom: 1px solid var(--authoring-rule);
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
@@ -3862,18 +3964,21 @@
|
|||||||
border-bottom: 2px solid transparent;
|
border-bottom: 2px solid transparent;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
color: var(--authoring-muted);
|
color: var(--authoring-muted);
|
||||||
|
opacity: 0.78;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .prepared-lifecycle-scene__rail li[data-active="true"] {
|
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .prepared-lifecycle-scene__rail li[data-active="true"] {
|
||||||
border-color: var(--authoring-accent);
|
border-color: var(--authoring-accent);
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: var(--authoring-ink);
|
color: var(--authoring-ink);
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .prepared-lifecycle-scene__projection {
|
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .prepared-lifecycle-scene__frame {
|
||||||
display: flex;
|
display: grid;
|
||||||
|
grid-template-rows: auto minmax(0, 1fr);
|
||||||
|
gap: 1rem;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
flex-direction: column;
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 1.35rem 1.4rem;
|
padding: 1.35rem 1.4rem;
|
||||||
border: 0;
|
border: 0;
|
||||||
@@ -3884,6 +3989,33 @@
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .prepared-lifecycle-scene__frame > * {
|
||||||
|
animation: authoring-frame-content-enter 220ms cubic-bezier(0.22, 1, 0.36, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .prepared-lifecycle-scene__frame-header {
|
||||||
|
border-color: var(--authoring-rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .prepared-lifecycle-scene__frame-header p,
|
||||||
|
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .prepared-lifecycle-scene__evidence dt {
|
||||||
|
color: var(--authoring-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .prepared-lifecycle-scene__frame-header h2,
|
||||||
|
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .prepared-lifecycle-scene__evidence dd {
|
||||||
|
color: var(--authoring-ink);
|
||||||
|
}
|
||||||
|
|
||||||
|
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .prepared-lifecycle-scene__frame-step,
|
||||||
|
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .prepared-lifecycle-scene__ordinal {
|
||||||
|
color: var(--authoring-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .prepared-lifecycle-scene__detail {
|
||||||
|
color: var(--authoring-muted);
|
||||||
|
}
|
||||||
|
|
||||||
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .presentation-assistant-pane {
|
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .presentation-assistant-pane {
|
||||||
background: color-mix(in oklch, var(--authoring-paper) 92%, var(--authoring-muted));
|
background: color-mix(in oklch, var(--authoring-paper) 92%, var(--authoring-muted));
|
||||||
border-color: var(--authoring-rule);
|
border-color: var(--authoring-rule);
|
||||||
@@ -3973,14 +4105,27 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.prepared-lifecycle-scene[data-presentation-surface="editorial"] {
|
.prepared-lifecycle-scene[data-presentation-surface="editorial"] {
|
||||||
grid-template-columns: minmax(11rem, 0.22fr) minmax(0, 0.78fr);
|
grid-template-columns: minmax(10.5rem, 0.28fr) minmax(0, 0.72fr);
|
||||||
gap: 0.6rem;
|
gap: 0.6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .prepared-lifecycle-scene__projection {
|
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .prepared-lifecycle-scene__rail {
|
||||||
|
grid-template-columns: none;
|
||||||
|
grid-auto-flow: column;
|
||||||
|
grid-auto-columns: minmax(9.5rem, 1fr);
|
||||||
|
overflow-x: auto;
|
||||||
|
scrollbar-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .prepared-lifecycle-scene__frame {
|
||||||
padding: 0.9rem;
|
padding: 0.9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .prepared-lifecycle-scene__frame-header {
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
gap: 0.7rem;
|
||||||
|
}
|
||||||
|
|
||||||
.problem-loop-scene[data-presentation-surface="editorial"] {
|
.problem-loop-scene[data-presentation-surface="editorial"] {
|
||||||
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -3993,13 +4138,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes authoring-canvas-enter {
|
@keyframes authoring-frame-content-enter {
|
||||||
from { opacity: 0; }
|
from { opacity: 0; }
|
||||||
to { opacity: 1; }
|
to { opacity: 1; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
.prepared-lifecycle-scene__projection {
|
.prepared-lifecycle-scene__frame > * {
|
||||||
animation: none;
|
animation: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user