fix: unify analysis workspace controls

This commit is contained in:
2026-08-19 17:43:14 +08:00
parent 073832034a
commit 1fb950806a
4 changed files with 119 additions and 48 deletions
@@ -1,15 +1,14 @@
import { ArrowLeft } from "lucide-react";
import { ArrowLeft, Map as MapIcon } from "lucide-react";
import { AnimatePresence, motion, useReducedMotion } from "motion/react";
import type { ReactNode } from "react";
import {
MAP_CONTROL_HOVER_CLASS_NAME,
MAP_CONTROL_RADIUS_CLASS_NAME,
MAP_CONTROL_SURFACE_CLASS_NAME
} from "@/features/map/core";
import { cn } from "@/shared/ui/cn";
import { OPERATIONAL_TIMELINE_LAYOUT } from "../operational-timeline/operational-timeline-model";
import { AnalysisArtifactWorkspace } from "./analysis-document-view";
import { ArtifactPeekBar } from "./artifact-peek-bar";
import {
ARTIFACT_VIEW_CONTROL_BUTTON_CLASS_NAME,
ARTIFACT_VIEW_CONTROL_SHELL_CLASS_NAME
} from "./artifact-view-control-styles";
import type {
AnalysisArtifact,
ArtifactRequestedView,
@@ -52,9 +51,18 @@ export function WorkbenchMainFrame({
const prefersReducedMotion = useReducedMotion();
const showMap = surfaceMode !== "focus";
const showArtifact = activeArtifact && surfaceMode !== "map_only";
const showArtifactViewControl = Boolean(
activeArtifact &&
activeArtifact.mapRelation !== "none" &&
(surfaceMode === "focus" || surfaceMode === "map_only")
);
const mapOnly = surfaceMode === "map_only";
const timelineTransition = prefersReducedMotion
? { duration: 0 }
: { duration: 0.28, ease: [0.22, 1, 0.36, 1] as const };
const viewControlTransition = prefersReducedMotion
? { duration: 0 }
: { duration: 0.18, ease: [0.22, 1, 0.36, 1] as const };
return (
<div
@@ -82,21 +90,6 @@ export function WorkbenchMainFrame({
>
{mapContent}
{mapOverlay}
{activeArtifact && surfaceMode === "map_only" ? (
<button
type="button"
onClick={() => onSetArtifactView("focus")}
className={cn(
"pointer-events-auto absolute left-3 top-3 z-20 inline-flex h-10 items-center gap-2 px-3 text-xs font-semibold lg:left-[max(1rem,calc(6rem-var(--workbench-agent-current-width)))] lg:top-4",
MAP_CONTROL_RADIUS_CLASS_NAME,
MAP_CONTROL_SURFACE_CLASS_NAME,
MAP_CONTROL_HOVER_CLASS_NAME
)}
>
<ArrowLeft size={15} aria-hidden="true" />
</button>
) : null}
</section>
{activeArtifact ? (
@@ -112,7 +105,6 @@ export function WorkbenchMainFrame({
<AnalysisArtifactWorkspace
artifact={activeArtifact}
surfaceMode={surfaceMode}
mapSplitAvailable={mapSplitAvailable}
onSetView={onSetArtifactView}
onCollapse={onCollapseArtifact}
onDestroy={onDestroyArtifact}
@@ -128,6 +120,42 @@ export function WorkbenchMainFrame({
onDestroy={onDestroyPendingArtifact}
/>
) : null}
<AnimatePresence initial={false}>
{showArtifactViewControl ? (
<motion.div
className={cn(
"pointer-events-auto absolute left-6 top-4 z-40 lg:left-[max(1.5rem,calc(6rem-var(--workbench-agent-current-width)))]",
ARTIFACT_VIEW_CONTROL_SHELL_CLASS_NAME
)}
initial={prefersReducedMotion ? false : { opacity: 0, y: 6, scale: 0.98 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={prefersReducedMotion ? { opacity: 0 } : { opacity: 0, y: 5, scale: 0.98 }}
transition={viewControlTransition}
>
<button
type="button"
aria-label={mapOnly ? "返回分析成果" : "显示地图"}
onClick={() => onSetArtifactView(mapOnly ? "focus" : mapSplitAvailable ? "map_split" : "map_only")}
className={ARTIFACT_VIEW_CONTROL_BUTTON_CLASS_NAME}
>
<AnimatePresence initial={false} mode="wait">
<motion.span
key={mapOnly ? "return-analysis" : "show-map"}
className="inline-flex items-center gap-1.5"
initial={prefersReducedMotion ? false : { opacity: 0, x: mapOnly ? 5 : -5 }}
animate={{ opacity: 1, x: 0 }}
exit={prefersReducedMotion ? { opacity: 0 } : { opacity: 0, x: mapOnly ? -5 : 5 }}
transition={viewControlTransition}
>
{mapOnly ? <ArrowLeft size={13} aria-hidden="true" /> : <MapIcon size={13} aria-hidden="true" />}
{mapOnly ? "返回分析成果" : "显示地图"}
</motion.span>
</AnimatePresence>
</button>
</motion.div>
) : null}
</AnimatePresence>
</div>
<motion.div