fix: address presentation review findings
This commit is contained in:
+6
-6
@@ -177,7 +177,7 @@ The final presentation beats frame the evaluation as bounded evidence, make
|
|||||||
claim boundaries and future work explicit, and end on the canonical defense
|
claim boundaries and future work explicit, and end on the canonical defense
|
||||||
discussion index rather than a benchmark or generic conclusion.
|
discussion index rather than a benchmark or generic conclusion.
|
||||||
|
|
||||||
Scenes 8 through 10 use the canonical replay as their only execution evidence.
|
Scenes 8 through 12 use the canonical replay as their only execution evidence.
|
||||||
The handoff expands an interpreted run operation into the center stage, then
|
The handoff expands an interpreted run operation into the center stage, then
|
||||||
keeps one workflow graph mounted while execution reaches the typed interrupt
|
keeps one workflow graph mounted while execution reaches the typed interrupt
|
||||||
and approval boundary. Raw protocol payloads are available through the evidence
|
and approval boundary. Raw protocol payloads are available through the evidence
|
||||||
@@ -196,7 +196,7 @@ The key defense states are directly addressable:
|
|||||||
- `/present#scene/workflow-demo/interrupt`
|
- `/present#scene/workflow-demo/interrupt`
|
||||||
- `/present#scene/interrupt-evidence/approval`
|
- `/present#scene/interrupt-evidence/approval`
|
||||||
|
|
||||||
Scene 10 is factual by design. It projects the reviewed/live run into visible
|
Scene 12 is factual by design. It projects the reviewed/live run into visible
|
||||||
workflow input, interrupt payload, resume decision, output, and trace facts.
|
workflow input, interrupt payload, resume decision, output, and trace facts.
|
||||||
Empty trace frame objects are shown as captured empty objects; absent fields are
|
Empty trace frame objects are shown as captured empty objects; absent fields are
|
||||||
called out as not captured rather than replaced by generic placeholders.
|
called out as not captured rather than replaced by generic placeholders.
|
||||||
@@ -229,8 +229,8 @@ chat to `rail`.
|
|||||||
|
|
||||||
#### Stage Themes
|
#### Stage Themes
|
||||||
|
|
||||||
Stage themes change by act: `paper` for scenes 1-3 and 11-12, `night` for
|
Stage themes change by act: `paper` for scenes 1-3 and 13-14, `night` for
|
||||||
scenes 4-10. Presenter controls allow overriding stage and chat themes
|
scenes 4-12. Presenter controls allow overriding stage and chat themes
|
||||||
independently during rehearsal.
|
independently during rehearsal.
|
||||||
|
|
||||||
#### Discussion Branches
|
#### Discussion Branches
|
||||||
@@ -307,9 +307,9 @@ The current prepared recipe can:
|
|||||||
This is intentionally not a general autonomous planner. A future server-side
|
This is intentionally not a general autonomous planner. A future server-side
|
||||||
Vercel AI SDK driver can feed the same message-part interface.
|
Vercel AI SDK driver can feed the same message-part interface.
|
||||||
|
|
||||||
### Demo Climax (Scenes 9–10)
|
### Demo Climax (Scenes 9–12)
|
||||||
|
|
||||||
Scenes 9 and 10 are the demo climax. They keep a continuity rail visible while
|
Scenes 9 through 12 are the demo climax. They keep a continuity rail visible while
|
||||||
the prepared replay moves from agent handoff, to persisted workflow run, to
|
the prepared replay moves from agent handoff, to persisted workflow run, to
|
||||||
typed human interrupt, to resume/output/evidence. The rail and outcome panel are
|
typed human interrupt, to resume/output/evidence. The rail and outcome panel are
|
||||||
presentation-only projections over the committed replay; they do not add live
|
presentation-only projections over the committed replay; they do not add live
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ export const useTimelineAgent = (
|
|||||||
const canRun = demo.canStart && !demo.inFlight && demo.state.phase !== "running";
|
const canRun = demo.canStart && !demo.inFlight && demo.state.phase !== "running";
|
||||||
|
|
||||||
const runPreparedWorkflow = useCallback(async () => {
|
const runPreparedWorkflow = useCallback(async () => {
|
||||||
if (!demo.canStart || demo.inFlight) return;
|
if (!demo.canStart || demo.inFlight || demo.state.phase === "running") return;
|
||||||
demo.start(modeLabel);
|
demo.start(modeLabel);
|
||||||
setMessages((current) => appendToolMessage(
|
setMessages((current) => appendToolMessage(
|
||||||
current,
|
current,
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ export const DemoOutcomePanel = ({
|
|||||||
<span>{lens.eyebrow}</span>
|
<span>{lens.eyebrow}</span>
|
||||||
<strong>Same persisted run</strong>
|
<strong>Same persisted run</strong>
|
||||||
<dl>
|
<dl>
|
||||||
|
<div><dt>Run</dt><dd><code>{runId}</code></dd></div>
|
||||||
<div><dt>Operation</dt><dd><code>{operation?.operation ?? lens.proofLabel}</code></dd></div>
|
<div><dt>Operation</dt><dd><code>{operation?.operation ?? lens.proofLabel}</code></dd></div>
|
||||||
<div><dt>Status</dt><dd>{operation?.status ?? "completed"}</dd></div>
|
<div><dt>Status</dt><dd>{operation?.status ?? "completed"}</dd></div>
|
||||||
</dl>
|
</dl>
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ export const DiscussionPanel = ({ branchId, onClose }: DiscussionPanelProps) =>
|
|||||||
<span className="discussion-panel__badge">{branch.claimClass}</span>
|
<span className="discussion-panel__badge">{branch.claimClass}</span>
|
||||||
<h2>{branch.title}</h2>
|
<h2>{branch.title}</h2>
|
||||||
</div>
|
</div>
|
||||||
<p>{branch.summary}</p>
|
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main className="discussion-panel__body" aria-label="discussion body">
|
<main className="discussion-panel__body" aria-label="discussion body">
|
||||||
|
|||||||
@@ -72,8 +72,8 @@ export const GuidedProductMoment = ({
|
|||||||
<InterruptDecisionForm
|
<InterruptDecisionForm
|
||||||
interrupt={facts.interrupt}
|
interrupt={facts.interrupt}
|
||||||
runId={demo.state.events.find((e) => e.stage === "run_start")?.resultingIds.runId ?? "unknown"}
|
runId={demo.state.events.find((e) => e.stage === "run_start")?.resultingIds.runId ?? "unknown"}
|
||||||
onSubmit={(ids, comment) => approvalActions?.submit(ids, comment)}
|
onSubmit={approvalActions?.canSubmit ? (ids, comment) => approvalActions.submit(ids, comment) : undefined}
|
||||||
onCancel={() => approvalActions?.cancel()}
|
onCancel={approvalActions?.canCancel ? () => approvalActions.cancel() : undefined}
|
||||||
terminalOutcome={approvalActions?.state === "submitted" ? "submitted" :
|
terminalOutcome={approvalActions?.state === "submitted" ? "submitted" :
|
||||||
approvalActions?.state === "cancelled" ? "cancelled" : undefined}
|
approvalActions?.state === "cancelled" ? "cancelled" : undefined}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import type { RunFactsInterrupt } from "./demo-run-facts.js";
|
|||||||
type InterruptDecisionFormProps = {
|
type InterruptDecisionFormProps = {
|
||||||
readonly interrupt: RunFactsInterrupt;
|
readonly interrupt: RunFactsInterrupt;
|
||||||
readonly runId: string;
|
readonly runId: string;
|
||||||
readonly onSubmit: (selectedIssueIds: ReadonlyArray<string>, comment: string) => void;
|
readonly onSubmit?: ((selectedIssueIds: ReadonlyArray<string>, comment: string) => void) | undefined;
|
||||||
readonly onCancel: () => void;
|
readonly onCancel?: (() => void) | undefined;
|
||||||
readonly terminalOutcome?: "submitted" | "cancelled" | undefined;
|
readonly terminalOutcome?: "submitted" | "cancelled" | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ export const InterruptDecisionForm = ({
|
|||||||
const handleSubmit = useCallback(
|
const handleSubmit = useCallback(
|
||||||
(e: React.FormEvent) => {
|
(e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
onSubmit([...selectedIds], comment);
|
onSubmit?.([...selectedIds], comment);
|
||||||
},
|
},
|
||||||
[selectedIds, comment, onSubmit],
|
[selectedIds, comment, onSubmit],
|
||||||
);
|
);
|
||||||
@@ -103,13 +103,14 @@ export const InterruptDecisionForm = ({
|
|||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div className="interrupt-decision-form__actions">
|
<div className="interrupt-decision-form__actions">
|
||||||
<button type="submit" className="interrupt-decision-form__submit">
|
<button type="submit" className="interrupt-decision-form__submit" disabled={!onSubmit}>
|
||||||
Submit
|
Submit
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="interrupt-decision-form__cancel"
|
className="interrupt-decision-form__cancel"
|
||||||
onClick={onCancel}
|
onClick={() => onCancel?.()}
|
||||||
|
disabled={!onCancel}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -36,8 +36,12 @@ export const OperatorChat = ({ state, messages, timelineAgent, onApprove, onDeny
|
|||||||
: timelineAgent && timelineAgent.messages.length > 0
|
: timelineAgent && timelineAgent.messages.length > 0
|
||||||
? timelineAgent.messages
|
? timelineAgent.messages
|
||||||
: fallbackMessages(state);
|
: fallbackMessages(state);
|
||||||
const submit = timelineAgent?.submitSelectedIssues ?? onApprove;
|
const submit = timelineAgent
|
||||||
const cancel = timelineAgent?.cancelReview ?? onDeny;
|
? () => { timelineAgent.submitSelectedIssues().catch(console.error); }
|
||||||
|
: onApprove;
|
||||||
|
const cancel = timelineAgent
|
||||||
|
? () => { timelineAgent.cancelReview().catch(console.error); }
|
||||||
|
: onDeny;
|
||||||
const composition = compositionForState(state);
|
const composition = compositionForState(state);
|
||||||
const presentationSurface = composition.chatTheme === "light" ? "editorial" : "night";
|
const presentationSurface = composition.chatTheme === "light" ? "editorial" : "night";
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const formatValue = (value: unknown): string | null => {
|
|||||||
if (value === undefined) return null;
|
if (value === undefined) return null;
|
||||||
if (typeof value === "string") return value;
|
if (typeof value === "string") return value;
|
||||||
if (typeof value === "number" || typeof value === "boolean" || value === null) return String(value);
|
if (typeof value === "number" || typeof value === "boolean" || value === null) return String(value);
|
||||||
return JSON.stringify(value);
|
return JSON.stringify(value) ?? null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const fieldKind = (propertySchema: unknown): SchemaApprovalFieldKind => {
|
const fieldKind = (propertySchema: unknown): SchemaApprovalFieldKind => {
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { render, screen } from "@testing-library/react";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { findBeat, findScene } from "../storyboard.js";
|
||||||
|
import { AgentHandoffScene } from "./AgentHandoffScene.js";
|
||||||
|
|
||||||
|
const renderBeat = (beatId: "request" | "handoff") => {
|
||||||
|
const scene = findScene("agent-handoff");
|
||||||
|
const beat = findBeat("agent-handoff", beatId);
|
||||||
|
if (!scene || !beat) throw new Error(`missing agent-handoff/${beatId}`);
|
||||||
|
return render(<AgentHandoffScene scene={scene} beat={beat} />);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("AgentHandoffScene", () => {
|
||||||
|
it("renders a log region named prepared authoring conversation", () => {
|
||||||
|
renderBeat("request");
|
||||||
|
expect(screen.getByRole("log", { name: "prepared authoring conversation" })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders separated user and assistant turns on the request beat", () => {
|
||||||
|
renderBeat("request");
|
||||||
|
const userMessages = screen.getAllByText(/report|workflow|prepare/i);
|
||||||
|
expect(userMessages.length).toBeGreaterThanOrEqual(1);
|
||||||
|
const assistantMessages = screen.getAllByText(/inspect|capabilities|sources|schemas|let me/i);
|
||||||
|
expect(assistantMessages.length).toBeGreaterThanOrEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders the full conversation on the handoff beat", () => {
|
||||||
|
renderBeat("handoff");
|
||||||
|
const userMessages = screen.getAllByText(/report|workflow|prepare|save/i);
|
||||||
|
expect(userMessages.length).toBeGreaterThanOrEqual(1);
|
||||||
|
const assistantMessages = screen.getAllByText(/inspect|sources|capabilities|compile|deployment|artifact/i);
|
||||||
|
expect(assistantMessages.length).toBeGreaterThanOrEqual(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not render prepared workflow lifecycle content", () => {
|
||||||
|
renderBeat("request");
|
||||||
|
expect(screen.queryByText("prepared workflow lifecycle")).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not include prepared workflow lifecycle content on handoff", () => {
|
||||||
|
renderBeat("handoff");
|
||||||
|
expect(screen.queryByText("prepared workflow lifecycle")).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import { useMemo } from "react";
|
||||||
|
import { agentTextMessage } from "../../demo/agent/events.js";
|
||||||
|
import { AssistantOperatorThread } from "../chat/AssistantOperatorThread.js";
|
||||||
|
import { projectPreparedAuthoring } from "./authoring-recording.js";
|
||||||
|
import type { SceneBeatDefinition, SceneDefinition } from "../storyboard.js";
|
||||||
|
|
||||||
|
type AgentHandoffSceneProps = {
|
||||||
|
readonly scene: SceneDefinition;
|
||||||
|
readonly beat: SceneBeatDefinition;
|
||||||
|
};
|
||||||
|
|
||||||
|
const requestMessages = [
|
||||||
|
agentTextMessage("handoff-user-1", "user", "We need to prepare a report workflow for the lda_report scenario. Use the available CLI tools to inspect, author, and deploy it."),
|
||||||
|
agentTextMessage("handoff-assistant-1", "assistant", "Let me inspect the available sources, capabilities, and schemas first."),
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Full-screen prepared-authoring conversation for Scene 8.
|
||||||
|
*
|
||||||
|
* The request beat shows the operator asking the agent to prepare a report;
|
||||||
|
* the handoff beat reveals the full completed conversation with all phases.
|
||||||
|
* Neither run actions nor approval actions are passed because this is a
|
||||||
|
* prepared recording, not a live agent interaction.
|
||||||
|
*/
|
||||||
|
export const AgentHandoffScene = ({ beat }: AgentHandoffSceneProps) => {
|
||||||
|
const messages = useMemo(() => {
|
||||||
|
if (beat.id === "handoff") {
|
||||||
|
const recording = projectPreparedAuthoring();
|
||||||
|
const result: ReturnType<typeof agentTextMessage>[] = [];
|
||||||
|
let index = 0;
|
||||||
|
for (const phase of recording) {
|
||||||
|
for (const turn of phase.conversation) {
|
||||||
|
result.push(
|
||||||
|
agentTextMessage(`msg-${index++}`, turn.role, turn.text),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return requestMessages;
|
||||||
|
}, [beat.id]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AssistantOperatorThread
|
||||||
|
mode="full"
|
||||||
|
messages={messages}
|
||||||
|
ariaLabel="prepared authoring conversation"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -130,7 +130,7 @@ describe("assistantRuntimeProjection", () => {
|
|||||||
expect(projected[0]?.content).toEqual([
|
expect(projected[0]?.content).toEqual([
|
||||||
{
|
{
|
||||||
type: "tool-call",
|
type: "tool-call",
|
||||||
toolCallId: "presentation-selectWorkflowNode",
|
toolCallId: "presentation-mixed-0-selectWorkflowNode",
|
||||||
toolName: "presentation.selectWorkflowNode",
|
toolName: "presentation.selectWorkflowNode",
|
||||||
args: { type: "selectWorkflowNode", nodeId: "review_issues" },
|
args: { type: "selectWorkflowNode", nodeId: "review_issues" },
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export type AssistantProjectedMessage = {
|
|||||||
readonly content: ReadonlyArray<AssistantContentPart>;
|
readonly content: ReadonlyArray<AssistantContentPart>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const projectPart = (part: AgentMessagePart): AssistantContentPart[] => {
|
const projectPart = (part: AgentMessagePart, messageId: string, partIndex: number): AssistantContentPart[] => {
|
||||||
switch (part.type) {
|
switch (part.type) {
|
||||||
case "text":
|
case "text":
|
||||||
return [{ type: "text", text: part.text }];
|
return [{ type: "text", text: part.text }];
|
||||||
@@ -52,7 +52,7 @@ const projectPart = (part: AgentMessagePart): AssistantContentPart[] => {
|
|||||||
case "presentation-action":
|
case "presentation-action":
|
||||||
return [{
|
return [{
|
||||||
type: "tool-call",
|
type: "tool-call",
|
||||||
toolCallId: `presentation-${part.action.type}`,
|
toolCallId: `presentation-${messageId}-${partIndex}-${part.action.type}`,
|
||||||
toolName: `presentation.${part.action.type}`,
|
toolName: `presentation.${part.action.type}`,
|
||||||
args: part.action,
|
args: part.action,
|
||||||
}];
|
}];
|
||||||
@@ -82,7 +82,7 @@ export const projectAgentMessagesForAssistant = (
|
|||||||
messages: ReadonlyArray<AgentMessage>,
|
messages: ReadonlyArray<AgentMessage>,
|
||||||
): AssistantProjectedMessage[] =>
|
): AssistantProjectedMessage[] =>
|
||||||
messages.map((message) => {
|
messages.map((message) => {
|
||||||
const content = message.parts.flatMap(projectPart);
|
const content = message.parts.flatMap((part, index) => projectPart(part, message.id, index));
|
||||||
return {
|
return {
|
||||||
id: message.id,
|
id: message.id,
|
||||||
role: messageRoleFor(message),
|
role: messageRoleFor(message),
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ const readOutputFacts = (
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatRecord = (record: Record<string, unknown>, absentLabel: string): string =>
|
const formatRecord = (record: Record<string, unknown> | undefined, absentLabel: string): string =>
|
||||||
formatFactValue(record, absentLabel);
|
formatFactValue(record, absentLabel);
|
||||||
|
|
||||||
const readTraceFacts = (
|
const readTraceFacts = (
|
||||||
@@ -225,11 +225,11 @@ const readTraceFacts = (
|
|||||||
stepType: stringField(frame, "stepType") ?? "unknown",
|
stepType: stringField(frame, "stepType") ?? "unknown",
|
||||||
outcome: stringField(frame, "outcome") ?? "unknown",
|
outcome: stringField(frame, "outcome") ?? "unknown",
|
||||||
resolvedInputLabel: formatRecord(
|
resolvedInputLabel: formatRecord(
|
||||||
recordField(frame, "resolvedInput") ?? {},
|
recordField(frame, "resolvedInput"),
|
||||||
"not captured in this recording",
|
"not captured in this recording",
|
||||||
),
|
),
|
||||||
outputLabel: formatRecord(recordField(frame, "output") ?? {}, "not captured in this recording"),
|
outputLabel: formatRecord(recordField(frame, "output"), "not captured in this recording"),
|
||||||
stateChangesLabel: formatRecord(recordField(frame, "stateChanges") ?? {}, "not captured in this recording"),
|
stateChangesLabel: formatRecord(recordField(frame, "stateChanges"), "not captured in this recording"),
|
||||||
}];
|
}];
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -89,7 +89,10 @@ const renderFigure = (overrides: Partial<React.ComponentProps<typeof Interactive
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
afterEach(() => cleanup());
|
afterEach(() => {
|
||||||
|
cleanup();
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
describe("InteractiveFigure", () => {
|
describe("InteractiveFigure", () => {
|
||||||
it("renders conceptual labels and hides evidence pointers by default", () => {
|
it("renders conceptual labels and hides evidence pointers by default", () => {
|
||||||
@@ -241,8 +244,5 @@ describe("InteractiveFigure", () => {
|
|||||||
renderFigure({ focusPath: [], size: "stage" });
|
renderFigure({ focusPath: [], size: "stage" });
|
||||||
|
|
||||||
expect(requestAnimationFrameSpy).toHaveBeenCalled();
|
expect(requestAnimationFrameSpy).toHaveBeenCalled();
|
||||||
|
|
||||||
requestAnimationFrameSpy.mockRestore();
|
|
||||||
cancelAnimationFrameSpy.mockRestore();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -163,7 +163,8 @@ export const demoSurfaceForBeat = (
|
|||||||
if (sceneId === "resume-output-evidence") {
|
if (sceneId === "resume-output-evidence") {
|
||||||
if (beatId === "resume") return { primarySurface: "resume-decision", supportSurface: "output-summary" };
|
if (beatId === "resume") return { primarySurface: "resume-decision", supportSurface: "output-summary" };
|
||||||
if (beatId === "output") return { primarySurface: "workflow-output", supportSurface: "none" };
|
if (beatId === "output") return { primarySurface: "workflow-output", supportSurface: "none" };
|
||||||
return { primarySurface: "trace-evidence", supportSurface: "output-summary" };
|
if (beatId === "trace") return { primarySurface: "trace-evidence", supportSurface: "output-summary" };
|
||||||
|
throw new Error(`Unknown beat ${beatId} for scene resume-output-evidence`);
|
||||||
}
|
}
|
||||||
return { primarySurface: "none", supportSurface: "none" };
|
return { primarySurface: "none", supportSurface: "none" };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { readFileSync } from "node:fs";
|
|||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
const css = readFileSync(join(import.meta.dirname, "presentation.css"), "utf8");
|
const css = readFileSync(join(import.meta.dirname, "presentation.css"), "utf8").replace(/\r\n/g, "\n");
|
||||||
|
|
||||||
describe("presentation.css", () => {
|
describe("presentation.css", () => {
|
||||||
it("allows hidden primary-region scrolling for browser zoom overflow", () => {
|
it("allows hidden primary-region scrolling for browser zoom overflow", () => {
|
||||||
|
|||||||
@@ -8,8 +8,12 @@ export type PresentationTargetHealth =
|
|||||||
export type TargetProbeState = "none" | "checking" | "ready" | "failed";
|
export type TargetProbeState = "none" | "checking" | "ready" | "failed";
|
||||||
|
|
||||||
const shortTarget = (target: string): string => {
|
const shortTarget = (target: string): string => {
|
||||||
const url = new URL(target);
|
try {
|
||||||
return `${url.hostname}:${url.port || (url.protocol === "https:" ? "443" : "80")}`;
|
const url = new URL(target);
|
||||||
|
return `${url.hostname}:${url.port || (url.protocol === "https:" ? "443" : "80")}`;
|
||||||
|
} catch {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const presentationTargetHealth = ({
|
export const presentationTargetHealth = ({
|
||||||
|
|||||||
@@ -2417,17 +2417,7 @@
|
|||||||
letter-spacing: 0.04em;
|
letter-spacing: 0.04em;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Factual Scene 10 layout grids */
|
/* Child min-height for all fact grids */
|
||||||
.guided-product-moment__approval-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: minmax(13rem, 0.56fr) minmax(0, 1.44fr);
|
|
||||||
gap: 0.75rem;
|
|
||||||
align-items: stretch;
|
|
||||||
min-height: 0;
|
|
||||||
max-height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.guided-product-moment__approval-grid > *,
|
.guided-product-moment__approval-grid > *,
|
||||||
.guided-product-moment__resume-grid > *,
|
.guided-product-moment__resume-grid > *,
|
||||||
.guided-product-moment[data-moment="output"] > .guided-product-moment__primary > * {
|
.guided-product-moment[data-moment="output"] > .guided-product-moment__primary > * {
|
||||||
@@ -2557,16 +2547,6 @@
|
|||||||
font-size: 0.78rem;
|
font-size: 0.78rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.guided-product-moment__resume-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1.4fr 1fr;
|
|
||||||
gap: 1rem;
|
|
||||||
align-items: stretch;
|
|
||||||
min-height: 0;
|
|
||||||
max-height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.guided-product-moment[data-moment="resume"] .run-facts-card,
|
.guided-product-moment[data-moment="resume"] .run-facts-card,
|
||||||
.guided-product-moment[data-moment="output"] .run-facts-card {
|
.guided-product-moment[data-moment="output"] .run-facts-card {
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
@@ -2595,9 +2575,4 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 900px) {
|
/* Responsive grid behavior lives in demo-workflow.css container query */
|
||||||
.guided-product-moment__approval-grid,
|
|
||||||
.guided-product-moment__resume-grid {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user