From 1fb950806a413fefde14980841d87196b6bce439 Mon Sep 17 00:00:00 2001 From: Huarch Date: Wed, 19 Aug 2026 17:43:14 +0800 Subject: [PATCH] fix: unify analysis workspace controls --- .../workspace/analysis-document-view.tsx | 69 ++++++++++++------ .../workbench/workspace/artifact-peek-bar.tsx | 12 ++-- .../workspace/artifact-view-control-styles.ts | 14 ++++ .../workspace/workbench-main-frame.tsx | 72 +++++++++++++------ 4 files changed, 119 insertions(+), 48 deletions(-) create mode 100644 src/features/workbench/workspace/artifact-view-control-styles.ts diff --git a/src/features/workbench/workspace/analysis-document-view.tsx b/src/features/workbench/workspace/analysis-document-view.tsx index 1fa843d..a3a197e 100644 --- a/src/features/workbench/workspace/analysis-document-view.tsx +++ b/src/features/workbench/workspace/analysis-document-view.tsx @@ -8,16 +8,23 @@ import { Database, FileSearch, Focus, - Map as MapIcon, Minimize2, ShieldCheck, Sparkles, Trash2, X } from "lucide-react"; +import { motion, useReducedMotion } from "motion/react"; import { useEffect, useState } from "react"; import { showMapNotice } from "@/features/map/core"; import { cn } from "@/shared/ui/cn"; +import { + ARTIFACT_DANGER_ICON_BUTTON_CLASS_NAME, + ARTIFACT_ICON_BUTTON_CLASS_NAME, + ARTIFACT_PRIMARY_ACTION_BUTTON_CLASS_NAME, + ARTIFACT_VIEW_CONTROL_BUTTON_CLASS_NAME, + ARTIFACT_VIEW_CONTROL_SHELL_CLASS_NAME +} from "./artifact-view-control-styles"; import type { AnalysisArtifact, AnalysisArtifactAction, @@ -30,7 +37,6 @@ import type { type AnalysisArtifactWorkspaceProps = { artifact: AnalysisArtifact; surfaceMode: WorkbenchSurfaceMode; - mapSplitAvailable: boolean; onSetView: (view: ArtifactRequestedView) => void; onCollapse: () => void; onDestroy: () => void; @@ -41,7 +47,6 @@ type EvidenceBlock = Exclude; export function AnalysisArtifactWorkspace({ artifact, surfaceMode, - mapSplitAvailable, onSetView, onCollapse, onDestroy @@ -72,7 +77,10 @@ export function AnalysisArtifactWorkspace({ className="acrylic-panel relative m-3 grid h-[calc(100%-1.5rem)] min-h-0 grid-rows-[auto_minmax(0,1fr)_auto] overflow-hidden rounded-2xl border text-slate-900" lang="zh-CN" > - +
@@ -116,7 +124,6 @@ export function AnalysisArtifactWorkspace({ +
@@ -195,11 +213,11 @@ function AnalysisBlockView({ >
@@ -431,32 +449,39 @@ function formatAnalyticalTimeRange(range: AnalysisArtifact["analyticalTimeRange" function ArtifactActionBar({ artifact, surfaceMode, - mapSplitAvailable, onSetView, onCollapse, onDestroy }: { artifact: AnalysisArtifact; surfaceMode: WorkbenchSurfaceMode; - mapSplitAvailable: boolean; onSetView: (view: ArtifactRequestedView) => void; onCollapse: () => void; onDestroy: () => void; }) { + const prefersReducedMotion = useReducedMotion(); + return (
- {artifact.mapRelation !== "none" ? ( - + {artifact.mapRelation !== "none" && surfaceMode === "map_split" ? ( + + + ) : null}
{artifact.actions.slice(0, 1).map((action) => )} - - + +
); @@ -467,9 +492,9 @@ function ArtifactPrimaryAction({ action, artifact }: { action: AnalysisArtifactA ); diff --git a/src/features/workbench/workspace/artifact-peek-bar.tsx b/src/features/workbench/workspace/artifact-peek-bar.tsx index 8991928..10afdb6 100644 --- a/src/features/workbench/workspace/artifact-peek-bar.tsx +++ b/src/features/workbench/workspace/artifact-peek-bar.tsx @@ -1,5 +1,9 @@ import { ChevronUp, FileChartColumnIncreasing, Sparkles, Trash2 } from "lucide-react"; import { cn } from "@/shared/ui/cn"; +import { + ARTIFACT_DANGER_ICON_BUTTON_CLASS_NAME, + ARTIFACT_PRIMARY_ACTION_BUTTON_CLASS_NAME +} from "./artifact-view-control-styles"; import type { AnalysisArtifact, WorkbenchSurfaceMode } from "./workspace-model"; export function ArtifactPeekBar({ @@ -36,9 +40,9 @@ export function ArtifactPeekBar({ ); diff --git a/src/features/workbench/workspace/artifact-view-control-styles.ts b/src/features/workbench/workspace/artifact-view-control-styles.ts new file mode 100644 index 0000000..1208ace --- /dev/null +++ b/src/features/workbench/workspace/artifact-view-control-styles.ts @@ -0,0 +1,14 @@ +export const ARTIFACT_VIEW_CONTROL_SHELL_CLASS_NAME = + "surface-control inline-flex shrink-0 items-center rounded-xl border border-white/70 p-0.5 shadow-[0_4px_14px_rgba(15,23,42,0.08)]"; + +export const ARTIFACT_VIEW_CONTROL_BUTTON_CLASS_NAME = + "inline-flex h-7 items-center gap-1.5 rounded-lg px-2 text-[10px] font-medium text-slate-500 transition-colors hover:bg-white/80 hover:text-blue-700 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-blue-500/60"; + +export const ARTIFACT_PRIMARY_ACTION_BUTTON_CLASS_NAME = + "inline-flex h-8 shrink-0 items-center gap-1.5 rounded-lg bg-blue-600 px-2.5 text-[11px] font-semibold text-white shadow-[0_2px_6px_rgba(37,99,235,0.2)] transition-colors hover:bg-blue-700 active:bg-blue-800 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-blue-500/60 focus-visible:ring-offset-2"; + +export const ARTIFACT_ICON_BUTTON_CLASS_NAME = + "grid h-8 w-8 shrink-0 place-items-center rounded-lg text-slate-500 transition-colors hover:bg-white/80 hover:text-blue-700 active:bg-slate-200/70 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-blue-500/60"; + +export const ARTIFACT_DANGER_ICON_BUTTON_CLASS_NAME = + "grid h-8 w-8 shrink-0 place-items-center rounded-lg text-slate-500 transition-colors hover:bg-rose-50 hover:text-rose-700 active:bg-rose-100 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-rose-500/60"; diff --git a/src/features/workbench/workspace/workbench-main-frame.tsx b/src/features/workbench/workspace/workbench-main-frame.tsx index c85d2b3..2feeb7d 100644 --- a/src/features/workbench/workspace/workbench-main-frame.tsx +++ b/src/features/workbench/workspace/workbench-main-frame.tsx @@ -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 (
{mapContent} {mapOverlay} - {activeArtifact && surfaceMode === "map_only" ? ( - - ) : null}
{activeArtifact ? ( @@ -112,7 +105,6 @@ export function WorkbenchMainFrame({ ) : null} + + + {showArtifactViewControl ? ( + + + + ) : null} +