fix: decouple timeline from workspace layout

The regression recurred because a floating timeline height was still exported as shared layout space and its z-index exceeded artifacts and notices. Reserve zero layout height and define the timeline below artifact and notification overlays.
This commit is contained in:
2026-08-19 16:58:55 +08:00
parent 760370c93f
commit 21dbfd82b6
5 changed files with 15 additions and 12 deletions
@@ -925,13 +925,7 @@ export function MapWorkbenchPage({
{
...WORKBENCH_LAYOUT_CSS_VARIABLES,
"--workbench-agent-current-width": `${currentAgentWidth}px`,
"--workbench-timeline-height": `${
!timelineVisible
? 0
: !isLargeScreen
? OPERATIONAL_TIMELINE_LAYOUT.mobileSummaryHeight + timelineBottom
: OPERATIONAL_TIMELINE_LAYOUT[timelineMode].height + timelineBottom
}px`
"--workbench-timeline-height": `${OPERATIONAL_TIMELINE_LAYOUT.overlayReservedHeight}px`
} as CSSProperties
}
>
@@ -1094,7 +1088,7 @@ export function MapWorkbenchPage({
{shouldShowTaskTicker ? (
<motion.div
key="agent-task-ticker-shell"
className="fixed left-1/2 right-auto top-24 z-20 w-[calc(100%-1.5rem)] [translate:-50%_0] md:absolute md:w-[var(--workbench-ticker-width)] 2xl:w-[var(--workbench-ticker-width-wide)]"
className="fixed left-1/2 right-auto top-24 z-50 w-[calc(100%-1.5rem)] [translate:-50%_0] md:absolute md:w-[var(--workbench-ticker-width)] 2xl:w-[var(--workbench-ticker-width-wide)]"
style={{ transformOrigin: "top center" }}
initial={
prefersReducedMotion
@@ -4,6 +4,7 @@ import {
canCenterTimelineBesideMapScale,
clusterOperationalEvents,
createOperationalEvents,
OPERATIONAL_TIMELINE_LAYOUT,
resolveTimelineScaleLayout
} from "./operational-timeline-model";
@@ -15,6 +16,10 @@ const workOrder: ScheduledConditionItem = {
};
describe("operational timeline model", () => {
it("does not reserve workspace layout space for the floating timeline", () => {
expect(OPERATIONAL_TIMELINE_LAYOUT.overlayReservedHeight).toBe(0);
});
it("creates linked plan and actual events for active work orders", () => {
const events = createOperationalEvents([workOrder]);
expect(events.map((event) => event.lane)).toEqual(["plan", "actual"]);
@@ -38,7 +38,8 @@ export const OPERATIONAL_TIMELINE_LAYOUT = {
mapScaleSafeWidth: 520,
floatingGap: 48,
edgeInset: 24,
scaleCollisionOffset: 56
scaleCollisionOffset: 56,
overlayReservedHeight: 0
} as const;
export type TimelineScaleLayout = {
@@ -17,7 +17,7 @@ export function ArtifactPeekBar({
<aside
aria-label="Agent 成果预览"
className={cn(
"acrylic-panel pointer-events-auto absolute bottom-[calc(var(--workbench-timeline-height)+1rem)] z-30 flex min-h-[76px] w-[min(680px,calc(100%-2rem))] items-center gap-3 rounded-2xl border px-3 py-2.5 text-slate-900",
"acrylic-panel pointer-events-auto absolute top-28 z-40 flex min-h-[76px] w-[min(680px,calc(100%-2rem))] items-center gap-3 rounded-2xl border px-3 py-2.5 text-slate-900",
"left-1/2 -translate-x-1/2",
surfaceMode === "map_split" && "2xl:left-[25%]"
)}
@@ -95,7 +95,10 @@ export function WorkbenchMainFrame({
id="artifact-workspace"
aria-label={`${activeArtifact.title}工作区`}
aria-hidden={!showArtifact}
className={cn("relative min-h-0 min-w-0 overflow-hidden", !showArtifact && "hidden")}
className={cn(
"relative z-30 min-h-0 min-w-0 overflow-hidden",
!showArtifact && "hidden"
)}
>
<AnalysisArtifactWorkspace
artifact={activeArtifact}
@@ -119,7 +122,7 @@ export function WorkbenchMainFrame({
</div>
<motion.div
className="pointer-events-auto absolute left-3 z-40 mx-auto min-h-0"
className="pointer-events-auto absolute left-3 z-20 mx-auto min-h-0"
initial={false}
animate={{
bottom: timelineBottom,