feat: tighten scene 11-12 factual evidence
This commit is contained in:
@@ -73,6 +73,7 @@ describe("GuidedProductMoment", () => {
|
||||
|
||||
expect(screen.getByRole("region", { name: /current product moment/i })).toHaveAttribute("data-moment", "approval");
|
||||
expect(screen.getByRole("region", { name: /current product moment/i })).toHaveAttribute("data-primary-surface", "interrupt-approval");
|
||||
expect(screen.getByRole("region", { name: /current product moment/i })).toHaveAttribute("data-approval-focus", "decision");
|
||||
expect(screen.getByText(/Run is paused/i)).toBeInTheDocument();
|
||||
expect(screen.getByText("Workflow input")).toBeInTheDocument();
|
||||
expect(screen.getByText("project-brief.md")).toBeInTheDocument();
|
||||
@@ -94,6 +95,7 @@ describe("GuidedProductMoment", () => {
|
||||
|
||||
expect(screen.getByRole("region", { name: /current product moment/i })).toHaveAttribute("data-moment", "resume");
|
||||
expect(screen.getByRole("region", { name: /current product moment/i })).toHaveAttribute("data-primary-surface", "resume-output");
|
||||
expect(screen.getByRole("region", { name: /current product moment/i })).toHaveAttribute("data-continuation-focus", "output");
|
||||
expect(screen.getByLabelText("workflow.runs.resume operation")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -117,6 +119,7 @@ describe("GuidedProductMoment", () => {
|
||||
|
||||
expect(screen.getByText("Workflow input")).toBeInTheDocument();
|
||||
expect(screen.getByRole("group", { name: /operator resume decision/i })).toBeInTheDocument();
|
||||
expect(screen.getByRole("region", { name: /operator resume decision/i })).toBeInTheDocument();
|
||||
expect(screen.queryByText("Output not created yet")).not.toBeInTheDocument();
|
||||
expect(screen.getAllByText(/lda.chat Thesis And Project Readiness Report/i).length).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
@@ -191,6 +194,7 @@ describe("GuidedProductMoment", () => {
|
||||
expect(screen.getByRole("group", { name: /operator resume decision/i })).toBeInTheDocument();
|
||||
expect(screen.queryByText("Output")).not.toBeInTheDocument();
|
||||
expect(screen.queryByText("Trace frames")).not.toBeInTheDocument();
|
||||
expect(document.querySelector(".interrupt-decision-form__report-preview")).toBeNull();
|
||||
});
|
||||
|
||||
it("resume makes output primary and operation/resume payload supporting", () => {
|
||||
@@ -260,9 +264,11 @@ describe("GuidedProductMoment", () => {
|
||||
);
|
||||
|
||||
expect(screen.getByRole("region", { name: /current product moment/i })).toHaveAttribute("data-support-surface", "output-summary");
|
||||
expect(screen.getByRole("region", { name: /current product moment/i })).toHaveAttribute("data-continuation-focus", "trace");
|
||||
expect(screen.getByRole("region", { name: /workflow trace proof/i })).toBeInTheDocument();
|
||||
expect(screen.getByRole("region", { name: /workflow output summary/i })).toHaveAttribute("data-output-priority", "summary");
|
||||
expect(screen.queryByText("No trace frames captured.")).not.toBeInTheDocument();
|
||||
expect(document.querySelectorAll(".run-trace-frame")).toHaveLength(3);
|
||||
});
|
||||
|
||||
it("trace beat shows trace frames instead of the empty fallback after trace is primed", () => {
|
||||
|
||||
@@ -75,6 +75,8 @@ export const GuidedProductMoment = ({
|
||||
data-moment={moment}
|
||||
data-primary-surface={hierarchy.primary}
|
||||
data-support-surface={hierarchy.support}
|
||||
data-approval-focus={moment === "approval" ? "decision" : undefined}
|
||||
data-continuation-focus={moment === "resume" ? "output" : moment === "trace" ? "trace" : undefined}
|
||||
>
|
||||
<header className="guided-product-moment__header">
|
||||
<span>{lens.eyebrow}</span>
|
||||
@@ -89,14 +91,17 @@ export const GuidedProductMoment = ({
|
||||
<RunInputFacts facts={facts} density="compact" />
|
||||
</aside>
|
||||
<InterruptPayloadFacts facts={facts} priority="primary" />
|
||||
<InterruptDecisionForm
|
||||
interrupt={facts.interrupt}
|
||||
runId={demo.state.events.find((e) => e.stage === "run_start")?.resultingIds.runId ?? "unknown"}
|
||||
onSubmit={approvalActions?.canSubmit ? (ids, comment) => approvalActions.submit(ids, comment) : undefined}
|
||||
onCancel={approvalActions?.canCancel ? () => approvalActions.cancel() : undefined}
|
||||
terminalOutcome={approvalActions?.state === "submitted" ? "submitted" :
|
||||
approvalActions?.state === "cancelled" ? "cancelled" : undefined}
|
||||
/>
|
||||
<div className="guided-product-moment__decision-column" role="region" aria-label="operator resume decision">
|
||||
<InterruptDecisionForm
|
||||
interrupt={facts.interrupt}
|
||||
runId={demo.state.events.find((e) => e.stage === "run_start")?.resultingIds.runId ?? "unknown"}
|
||||
onSubmit={approvalActions?.canSubmit ? (ids, comment) => approvalActions.submit(ids, comment) : undefined}
|
||||
onCancel={approvalActions?.canCancel ? () => approvalActions.cancel() : undefined}
|
||||
terminalOutcome={approvalActions?.state === "submitted" ? "submitted" :
|
||||
approvalActions?.state === "cancelled" ? "cancelled" : undefined}
|
||||
showReportPreview={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
{moment === "resume" && runResume ? (
|
||||
|
||||
@@ -127,6 +127,21 @@ describe("InterruptDecisionForm", () => {
|
||||
expect(screen.queryByRole("button", { name: /cancel/i })).toBeFalsy();
|
||||
});
|
||||
|
||||
it("can leave report evidence to the surrounding interrupt panel", () => {
|
||||
render(
|
||||
<InterruptDecisionForm
|
||||
interrupt={interrupt}
|
||||
runId="run_recorded_lda_report"
|
||||
onSubmit={vi.fn()}
|
||||
onCancel={vi.fn()}
|
||||
showReportPreview={false}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.queryByText(/The workflow substrate is ready/i)).not.toBeInTheDocument();
|
||||
expect(screen.getAllByRole("checkbox")).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("shows terminal outcome label when state is cancelled", () => {
|
||||
render(
|
||||
<InterruptDecisionForm
|
||||
|
||||
@@ -7,6 +7,7 @@ type InterruptDecisionFormProps = {
|
||||
readonly onSubmit?: ((selectedIssueIds: ReadonlyArray<string>, comment: string) => void) | undefined;
|
||||
readonly onCancel?: (() => void) | undefined;
|
||||
readonly terminalOutcome?: "submitted" | "cancelled" | undefined;
|
||||
readonly showReportPreview?: boolean;
|
||||
};
|
||||
|
||||
export const InterruptDecisionForm = ({
|
||||
@@ -15,6 +16,7 @@ export const InterruptDecisionForm = ({
|
||||
onSubmit,
|
||||
onCancel,
|
||||
terminalOutcome,
|
||||
showReportPreview = true,
|
||||
}: InterruptDecisionFormProps) => {
|
||||
const [selectedIds, setSelectedIds] = useState<Set<string>>(() =>
|
||||
new Set(interrupt.proposedIssues.map((issue) => issue.id)),
|
||||
@@ -71,7 +73,7 @@ export const InterruptDecisionForm = ({
|
||||
<dd>{interrupt.outcomes.join(", ")}</dd>
|
||||
</dl>
|
||||
|
||||
{interrupt.reportMarkdownPreview && (
|
||||
{showReportPreview && interrupt.reportMarkdownPreview && (
|
||||
<pre className="interrupt-decision-form__report-preview">
|
||||
{interrupt.reportMarkdownPreview}
|
||||
</pre>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { act, cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
import { callOperation } from "../connection/api.js";
|
||||
|
||||
vi.mock("../connection/api.js", () => ({
|
||||
callOperation: vi.fn().mockResolvedValue({
|
||||
@@ -20,6 +21,21 @@ class MockResizeObserver {
|
||||
disconnect() {}
|
||||
}
|
||||
|
||||
const mockedCallOperation = vi.mocked(callOperation);
|
||||
const healthyResponse = {
|
||||
ok: true as const,
|
||||
operation: "workflow.health" as const,
|
||||
label: "Health",
|
||||
interpreted: { status: "ok" },
|
||||
exchange: { request: {}, response: {} },
|
||||
equivalentCli: "uv run wf status",
|
||||
durationMs: 2,
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
mockedCallOperation.mockResolvedValue(healthyResponse);
|
||||
});
|
||||
|
||||
beforeAll(() => {
|
||||
globalThis.ResizeObserver = MockResizeObserver as unknown as typeof ResizeObserver;
|
||||
globalThis.DOMRect = {
|
||||
@@ -50,6 +66,8 @@ const graphNodeByLabel = (label: RegExp): HTMLElement => {
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
mockedCallOperation.mockReset();
|
||||
mockedCallOperation.mockResolvedValue(healthyResponse);
|
||||
window.sessionStorage.clear();
|
||||
window.location.hash = "";
|
||||
});
|
||||
@@ -218,6 +236,21 @@ describe("PresentationRoute", () => {
|
||||
expect(screen.queryByRole("button", { name: "Submit" })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("switches to replay evidence when a configured target fails health", async () => {
|
||||
mockedCallOperation.mockResolvedValue({
|
||||
ok: false as const,
|
||||
error: { code: "upstream_unreachable", message: "connection refused" },
|
||||
exchange: { request: {}, response: {} },
|
||||
});
|
||||
window.sessionStorage.setItem("lda.workflowConsole.target", "http://127.0.0.1:8766/rpc");
|
||||
window.location.hash = "#scene/resume-output-evidence/resume";
|
||||
const { PresentationRoute } = await import("./PresentationRoute.js");
|
||||
render(<PresentationRoute />);
|
||||
|
||||
expect(await screen.findByText(/Replay fallback/i)).toBeInTheDocument();
|
||||
expect(await screen.findByRole("region", { name: /workflow output report/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("updates the chat intro after the live health probe succeeds", async () => {
|
||||
window.sessionStorage.setItem("lda.workflowConsole.target", "http://127.0.0.1:8765/rpc");
|
||||
const { PresentationRoute } = await import("./PresentationRoute.js");
|
||||
|
||||
@@ -97,9 +97,12 @@ export const PresentationRoute = () => {
|
||||
useEffect(() => {
|
||||
if (demo.state.phase !== "ready") return;
|
||||
// The resolved target owns the initial mode: a healthy loopback target must
|
||||
// remain live, while an invalid target starts from the offline recording.
|
||||
if (demo.state.mode !== presentationTarget.mode) demo.setMode(presentationTarget.mode);
|
||||
}, [demo.setMode, demo.state.mode, demo.state.phase, presentationTarget.mode]);
|
||||
// remain live, while an invalid or unreachable target starts from the
|
||||
// offline recording. The health hook is the point where an HTTP target
|
||||
// becomes known to be unreachable; URL shape alone is not enough.
|
||||
const desiredMode = targetStatus.kind === "failed" ? "replay" : presentationTarget.mode;
|
||||
if (demo.state.mode !== desiredMode) demo.setMode(desiredMode);
|
||||
}, [demo.setMode, demo.state.mode, demo.state.phase, presentationTarget.mode, targetStatus.kind]);
|
||||
|
||||
useEffect(() => {
|
||||
if (demo.state.phase === "ready" && demo.state.mode === "replay") {
|
||||
|
||||
@@ -234,6 +234,11 @@ describe("RunTraceFacts", () => {
|
||||
expect(screen.getByText("review_issues")).toBeDefined();
|
||||
expect(screen.getByText("2 captured")).toBeInTheDocument();
|
||||
expect(screen.getAllByText("captured as empty object").length).toBeGreaterThanOrEqual(2);
|
||||
expect(screen.getAllByText("Resolved input").length).toBe(2);
|
||||
expect(screen.getAllByText("captured as empty object")[0]!.closest(".run-trace-frame__fact")).toHaveAttribute(
|
||||
"data-value-kind",
|
||||
"empty-object",
|
||||
);
|
||||
});
|
||||
|
||||
it("renders trace frames inside a scrollable list", () => {
|
||||
@@ -244,4 +249,18 @@ describe("RunTraceFacts", () => {
|
||||
expect(screen.getByRole("region", { name: /workflow trace frames/i })).toHaveClass("run-facts-scroll-region");
|
||||
expect(screen.getByText("node-7")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("keeps repeated node IDs as separate trace frames", () => {
|
||||
const traceFacts = makeTraceFacts(2).trace;
|
||||
const repeatedTraceFacts: DemoRunFacts = {
|
||||
...baseFacts,
|
||||
trace: {
|
||||
frames: traceFacts.frames.map((frame) => ({ ...frame, nodeId: "review_issues" })),
|
||||
},
|
||||
};
|
||||
|
||||
render(<RunTraceFacts facts={repeatedTraceFacts} />);
|
||||
|
||||
expect(document.querySelectorAll(".run-trace-frame")).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Fragment } from "react";
|
||||
import type { DemoRunFacts } from "./demo-run-facts.js";
|
||||
import { factValueKind, type DemoRunFacts } from "./demo-run-facts.js";
|
||||
|
||||
type RunInputFactsProps = {
|
||||
readonly facts: DemoRunFacts;
|
||||
@@ -138,6 +138,13 @@ type RunTraceFactsProps = {
|
||||
readonly facts: DemoRunFacts;
|
||||
};
|
||||
|
||||
const TraceFact = ({ label, value }: { readonly label: string; readonly value: string }) => (
|
||||
<div className="run-trace-frame__fact" data-value-kind={factValueKind(value)}>
|
||||
<dt>{label}</dt>
|
||||
<dd><code>{value}</code></dd>
|
||||
</div>
|
||||
);
|
||||
|
||||
export const RunTraceFacts = ({ facts }: RunTraceFactsProps) => (
|
||||
<div className="run-facts-card run-trace-facts" role="region" aria-label="workflow trace proof">
|
||||
<h3>
|
||||
@@ -148,18 +155,17 @@ export const RunTraceFacts = ({ facts }: RunTraceFactsProps) => (
|
||||
) : (
|
||||
<div className="run-facts-scroll-region run-facts-scroll-region--trace" role="region" aria-label="workflow trace frames">
|
||||
<ul className="run-facts-list">
|
||||
{facts.trace.frames.map((frame) => (
|
||||
<li key={frame.nodeId} className="run-trace-frame">
|
||||
<strong>{frame.nodeId}</strong>
|
||||
<span className="run-trace-step-type">{frame.stepType}</span>
|
||||
<span className="run-trace-outcome">{frame.outcome}</span>
|
||||
<dl className="run-facts-dl">
|
||||
<dt>Resolved input</dt>
|
||||
<dd>{frame.resolvedInputLabel}</dd>
|
||||
<dt>Output</dt>
|
||||
<dd>{frame.outputLabel}</dd>
|
||||
<dt>State changes</dt>
|
||||
<dd>{frame.stateChangesLabel}</dd>
|
||||
{facts.trace.frames.map((frame, index) => (
|
||||
<li key={`${frame.nodeId}-${index}`} className="run-trace-frame" data-trace-node={frame.nodeId}>
|
||||
<header className="run-trace-frame__header">
|
||||
<strong>{frame.nodeId}</strong>
|
||||
<span className="run-trace-step-type">{frame.stepType}</span>
|
||||
<span className="run-trace-outcome">{frame.outcome}</span>
|
||||
</header>
|
||||
<dl className="run-trace-frame__facts">
|
||||
<TraceFact label="Resolved input" value={frame.resolvedInputLabel} />
|
||||
<TraceFact label="Output" value={frame.outputLabel} />
|
||||
<TraceFact label="State changes" value={frame.stateChangesLabel} />
|
||||
</dl>
|
||||
</li>
|
||||
))}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, expect, it, vi } from "vitest";
|
||||
import type { DemoTimelineController } from "../demo/useDemoTimeline.js";
|
||||
import { initialDemoTimelineState } from "../demo/timeline/reducer.js";
|
||||
import { loadCanonicalDemoRecording } from "../demo/timeline/replay.js";
|
||||
import { formatFactValue, projectDemoRunFacts } from "./demo-run-facts.js";
|
||||
import { factValueKind, formatFactValue, projectDemoRunFacts } from "./demo-run-facts.js";
|
||||
|
||||
const controller = (overrides: Partial<DemoTimelineController> = {}): DemoTimelineController => {
|
||||
const recording = loadCanonicalDemoRecording();
|
||||
@@ -189,4 +189,10 @@ describe("demo-run-facts", () => {
|
||||
expect(formatFactValue({}, "not captured in this recording")).toBe("captured as empty object");
|
||||
expect(formatFactValue(undefined, "not captured in this recording")).toBe("not captured in this recording");
|
||||
});
|
||||
|
||||
it("classifies captured values for compact trace rendering", () => {
|
||||
expect(factValueKind("captured as empty object")).toBe("empty-object");
|
||||
expect(factValueKind("not captured in this recording")).toBe("missing");
|
||||
expect(factValueKind('{"documents":3}')).toBe("value");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -52,6 +52,8 @@ export type RunFactsTrace = {
|
||||
readonly frames: ReadonlyArray<RunFactsTraceFrame>;
|
||||
};
|
||||
|
||||
export type FactValueKind = "value" | "empty-object" | "missing";
|
||||
|
||||
export type DemoRunFacts = {
|
||||
readonly input: RunFactsInput;
|
||||
readonly interrupt: RunFactsInterrupt;
|
||||
@@ -109,6 +111,12 @@ export const formatFactValue = (value: unknown, absentLabel: string): string =>
|
||||
return JSON.stringify(value);
|
||||
};
|
||||
|
||||
export const factValueKind = (value: string): FactValueKind => {
|
||||
if (value === EMPTY_OBJECT_LABEL) return "empty-object";
|
||||
if (value === "not captured in this recording") return "missing";
|
||||
return "value";
|
||||
};
|
||||
|
||||
const findEvent = (
|
||||
events: ReadonlyArray<DemoEvent>,
|
||||
stage: DemoEventStage,
|
||||
|
||||
@@ -2514,6 +2514,7 @@
|
||||
display: flex;
|
||||
gap: 0.8rem;
|
||||
align-items: baseline;
|
||||
min-width: 0;
|
||||
margin-bottom: 0.8rem;
|
||||
}
|
||||
|
||||
@@ -2526,6 +2527,10 @@
|
||||
}
|
||||
|
||||
.interrupt-decision-form__run-id {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font: 0.82rem/1.4 var(--font-mono);
|
||||
color: color-mix(in srgb, var(--text-primary), white 20%);
|
||||
}
|
||||
@@ -2733,7 +2738,7 @@
|
||||
}
|
||||
|
||||
.guided-product-moment[data-moment="approval"] .interrupt-decision-form__meta {
|
||||
grid-template-columns: auto minmax(0, 1fr) auto minmax(0, 1fr);
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
gap: 0.15rem 0.45rem;
|
||||
margin-bottom: 0.45rem;
|
||||
font-size: 0.74rem;
|
||||
|
||||
@@ -1286,16 +1286,64 @@
|
||||
|
||||
.guided-product-moment__approval-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(12.5rem, 0.42fr) minmax(0, 1.25fr) minmax(20rem, 0.62fr);
|
||||
grid-template-columns: 14rem minmax(0, 1.45fr) minmax(18rem, 0.78fr);
|
||||
gap: 0.85rem;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.guided-product-moment__approval-grid > * {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.guided-product-moment__input-rail {
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.guided-product-moment__decision-column {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.guided-product-moment[data-approval-focus="decision"] .guided-product-moment__decision-column {
|
||||
border-left: 1px solid color-mix(in srgb, var(--accent-cyan) 36%, transparent);
|
||||
padding-left: 0.85rem;
|
||||
}
|
||||
|
||||
.guided-product-moment[data-approval-focus="decision"] .interrupt-decision-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
flex-wrap: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.guided-product-moment[data-approval-focus="decision"] .interrupt-decision-form > * {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.guided-product-moment[data-approval-focus="decision"] .interrupt-decision-form__issues {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.guided-product-moment[data-approval-focus="decision"] .interrupt-decision-form__header,
|
||||
.guided-product-moment[data-approval-focus="decision"] .interrupt-decision-form__meta,
|
||||
.guided-product-moment[data-approval-focus="decision"] .interrupt-decision-form__comment-label,
|
||||
.guided-product-moment[data-approval-focus="decision"] .interrupt-decision-form__actions {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.guided-product-moment[data-approval-focus="decision"] .interrupt-decision-form__issue-title {
|
||||
min-width: 0;
|
||||
overflow-wrap: anywhere;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.run-facts-card[data-density="compact"] {
|
||||
padding: 0.8rem;
|
||||
font-size: 0.82rem;
|
||||
@@ -1384,6 +1432,11 @@
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.guided-product-moment[data-continuation-focus="output"] .run-facts-card[data-output-priority="report"] {
|
||||
border-color: color-mix(in srgb, var(--accent-cyan) 62%, var(--stage-line));
|
||||
box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent-cyan) 12%, transparent);
|
||||
}
|
||||
|
||||
.guided-product-moment__resume-support .operation-command {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
@@ -1427,13 +1480,75 @@
|
||||
|
||||
.run-trace-frame {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(8rem, 0.26fr) auto auto minmax(0, 1fr);
|
||||
gap: 0.5rem 0.75rem;
|
||||
align-items: start;
|
||||
gap: 0.65rem;
|
||||
padding: 0.7rem 0.8rem;
|
||||
border: 1px solid var(--stage-line);
|
||||
border-radius: 0.7rem;
|
||||
background: color-mix(in srgb, var(--stage-surface) 84%, transparent);
|
||||
}
|
||||
|
||||
.run-trace-frame .run-facts-dl {
|
||||
grid-column: 4;
|
||||
.guided-product-moment[data-continuation-focus="trace"] .run-trace-facts {
|
||||
border-color: color-mix(in srgb, var(--accent-cyan) 62%, var(--stage-line));
|
||||
box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent-cyan) 12%, transparent);
|
||||
}
|
||||
|
||||
.run-trace-frame__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.run-trace-frame__header strong {
|
||||
min-width: 0;
|
||||
overflow-wrap: anywhere;
|
||||
color: var(--text-primary);
|
||||
font: 700 0.82rem/1.2 var(--font-mono, monospace);
|
||||
}
|
||||
|
||||
.run-trace-frame__facts {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 0.45rem;
|
||||
min-width: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.run-trace-frame__fact {
|
||||
min-width: 0;
|
||||
padding: 0.42rem 0.5rem;
|
||||
border-radius: 0.45rem;
|
||||
background: color-mix(in srgb, var(--stage-inset) 72%, transparent);
|
||||
}
|
||||
|
||||
.run-trace-frame__fact dt {
|
||||
margin: 0 0 0.25rem;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.62rem;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.run-trace-frame__fact dd {
|
||||
min-width: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.run-trace-frame__fact code {
|
||||
display: block;
|
||||
min-width: 0;
|
||||
overflow-wrap: anywhere;
|
||||
color: var(--text-primary);
|
||||
font: 600 0.67rem/1.35 var(--font-mono, monospace);
|
||||
}
|
||||
|
||||
.run-trace-frame__fact[data-value-kind="empty-object"] code {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.run-trace-frame__fact[data-value-kind="missing"] code {
|
||||
color: var(--accent-amber);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.guided-product-moment[data-moment="trace"] .run-facts-card[data-output-priority="summary"] {
|
||||
@@ -1447,6 +1562,12 @@
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
@container presentation-canvas (max-width: 820px) {
|
||||
.run-trace-frame__facts {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@container presentation-canvas (max-width: 1050px) {
|
||||
.guided-product-moment__approval-grid,
|
||||
.guided-product-moment__resume-grid,
|
||||
|
||||
Reference in New Issue
Block a user