feat: add operational timeline workspace
This commit is contained in:
@@ -1,8 +1,6 @@
|
||||
import { AgentCollapsedRail, AgentCommandPanel, AgentPersona } from "@/features/agent";
|
||||
import { cn } from "@/shared/ui/cn";
|
||||
import { useEffect, type ComponentProps } from "react";
|
||||
import { getAgentPanelDefaultWidth, getAgentPanelMaxWidth } from "../layout/workbench-layout";
|
||||
import { useAgentPanelResize } from "../hooks/use-agent-panel-resize";
|
||||
import { getWorkbenchViewportLayout } from "../layout/workbench-layout";
|
||||
|
||||
type AgentPanelProps = Omit<ComponentProps<typeof AgentCommandPanel>, "collapsing" | "onCollapse">;
|
||||
|
||||
@@ -10,8 +8,6 @@ type WorkbenchAgentPanelsProps = {
|
||||
panelProps: AgentPanelProps;
|
||||
panelOpen: boolean;
|
||||
panelCollapsing: boolean;
|
||||
artifactActive: boolean;
|
||||
conditionExpanded: boolean;
|
||||
personaState: ComponentProps<typeof AgentPersona>["state"];
|
||||
statusLabel: string;
|
||||
viewportWidth: number;
|
||||
@@ -24,8 +20,6 @@ export function WorkbenchAgentPanels({
|
||||
panelProps,
|
||||
panelOpen,
|
||||
panelCollapsing,
|
||||
artifactActive,
|
||||
conditionExpanded,
|
||||
personaState,
|
||||
statusLabel,
|
||||
viewportWidth,
|
||||
@@ -33,9 +27,7 @@ export function WorkbenchAgentPanels({
|
||||
onExpandPanel,
|
||||
onWidthCommit
|
||||
}: WorkbenchAgentPanelsProps) {
|
||||
const resize = useAgentPanelResize(viewportWidth, conditionExpanded);
|
||||
const defaultWidth = getAgentPanelDefaultWidth(viewportWidth, conditionExpanded);
|
||||
const committedWidth = resize.committedWidth ?? defaultWidth;
|
||||
const committedWidth = getWorkbenchViewportLayout(viewportWidth).agentWidth;
|
||||
|
||||
useEffect(() => {
|
||||
onWidthCommit?.(committedWidth);
|
||||
@@ -43,43 +35,16 @@ export function WorkbenchAgentPanels({
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={resize.panelRef}
|
||||
className={cn(
|
||||
"pointer-events-none absolute z-50 hidden lg:block",
|
||||
!artifactActive && panelOpen && "workbench-agent-dock",
|
||||
artifactActive && panelOpen
|
||||
? "bottom-4 left-3 top-20 max-w-[720px]"
|
||||
: "bottom-0 left-0 top-14"
|
||||
)}
|
||||
style={{ width: panelOpen ? resize.width ?? defaultWidth : 72 }}
|
||||
className="pointer-events-none absolute bottom-0 left-0 top-14 z-50 hidden lg:block"
|
||||
style={{ width: panelOpen ? committedWidth : 72 }}
|
||||
>
|
||||
{panelOpen ? (
|
||||
<>
|
||||
<AgentCommandPanel
|
||||
{...panelProps}
|
||||
presentation={artifactActive ? "desktop-floating" : "desktop-dock"}
|
||||
collapsing={panelCollapsing}
|
||||
onCollapse={onCollapsePanel}
|
||||
/>
|
||||
<div
|
||||
aria-label="调整 Agent 面板宽度"
|
||||
aria-orientation="vertical"
|
||||
aria-valuemax={getAgentPanelMaxWidth(viewportWidth, conditionExpanded)}
|
||||
aria-valuemin={defaultWidth}
|
||||
aria-valuenow={Math.round(resize.width ?? defaultWidth)}
|
||||
className={cn(
|
||||
"group pointer-events-auto absolute -right-3 top-4 bottom-4 z-10 flex w-6 cursor-ew-resize touch-none items-center justify-center outline-hidden",
|
||||
resize.resizing && "[&>span]:bg-blue-500 [&>span]:opacity-100"
|
||||
)}
|
||||
role="separator"
|
||||
tabIndex={0}
|
||||
title="拖拽调整 Agent 面板宽度"
|
||||
onKeyDown={resize.handleKeyDown}
|
||||
onPointerDown={resize.handlePointerDown}
|
||||
>
|
||||
<span className="h-14 w-1 rounded-full bg-slate-500/60 opacity-50 shadow-xs transition-[height,background-color,opacity] group-hover:h-20 group-hover:bg-blue-500 group-hover:opacity-100 group-focus-visible:h-20 group-focus-visible:bg-blue-500 group-focus-visible:opacity-100" />
|
||||
</div>
|
||||
</>
|
||||
<AgentCommandPanel
|
||||
{...panelProps}
|
||||
presentation="desktop-dock"
|
||||
collapsing={panelCollapsing}
|
||||
onCollapse={onCollapsePanel}
|
||||
/>
|
||||
) : (
|
||||
<div className="h-full bg-[#e8eef4] px-1.5 pt-5 shadow-[1px_0_0_rgba(15,23,42,0.08)]">
|
||||
<AgentCollapsedRail
|
||||
|
||||
@@ -28,6 +28,7 @@ export type WorkbenchTopBarProps = {
|
||||
alerts: WorkbenchAlert[];
|
||||
user: WorkbenchUser;
|
||||
conditionFeedVisible: boolean;
|
||||
timelineVisible: boolean;
|
||||
taskTickerAvailable: boolean;
|
||||
taskTickerVisible: boolean;
|
||||
devPanelEnabled: boolean;
|
||||
@@ -35,6 +36,7 @@ export type WorkbenchTopBarProps = {
|
||||
onSelectScenario: (scenarioId: string) => void;
|
||||
onSelectAlert: (alert: WorkbenchAlert) => void;
|
||||
onToggleConditionFeed: () => void;
|
||||
onToggleTimeline: () => void;
|
||||
onToggleTaskTicker: () => void;
|
||||
onToggleDevPanel: () => void;
|
||||
onPreviewScenario: () => void;
|
||||
@@ -59,6 +61,7 @@ export function WorkbenchTopBar({
|
||||
alerts,
|
||||
user,
|
||||
conditionFeedVisible,
|
||||
timelineVisible,
|
||||
taskTickerAvailable,
|
||||
taskTickerVisible,
|
||||
devPanelEnabled,
|
||||
@@ -66,6 +69,7 @@ export function WorkbenchTopBar({
|
||||
onSelectScenario,
|
||||
onSelectAlert,
|
||||
onToggleConditionFeed,
|
||||
onToggleTimeline,
|
||||
onToggleTaskTicker,
|
||||
onToggleDevPanel,
|
||||
onPreviewScenario,
|
||||
@@ -144,6 +148,12 @@ export function WorkbenchTopBar({
|
||||
</div>
|
||||
|
||||
<div className="flex shrink-0 items-center gap-2">
|
||||
<div className="lg:hidden">
|
||||
<TimelineToggle compact visible={timelineVisible} onToggle={onToggleTimeline} />
|
||||
</div>
|
||||
<div className="hidden lg:block">
|
||||
<TimelineToggle visible={timelineVisible} onToggle={onToggleTimeline} />
|
||||
</div>
|
||||
{onCreateTestAnalysis ? (
|
||||
<div className="hidden lg:block">
|
||||
<button
|
||||
@@ -257,6 +267,33 @@ function ConditionFeedToggle({ visible, onToggle }: { visible: boolean; onToggle
|
||||
);
|
||||
}
|
||||
|
||||
function TimelineToggle({
|
||||
visible,
|
||||
compact = false,
|
||||
onToggle
|
||||
}: {
|
||||
visible: boolean;
|
||||
compact?: boolean;
|
||||
onToggle: () => void;
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
aria-label={visible ? "隐藏运行时间轴" : "显示运行时间轴"}
|
||||
aria-pressed={visible}
|
||||
data-selection-tone="soft"
|
||||
title={visible ? "隐藏运行时间轴" : "显示运行时间轴"}
|
||||
onClick={onToggle}
|
||||
className={cn("group", compact ? "px-1.5" : "px-2", headerControlButtonClassName)}
|
||||
>
|
||||
<span className={headerControlIconClassName}>
|
||||
<Clock3 size={14} aria-hidden="true" />
|
||||
</span>
|
||||
{!compact ? <span className={compactHeaderTextClassName}>时间轴</span> : null}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
function HeaderReadout({
|
||||
icon: Icon,
|
||||
label,
|
||||
|
||||
Reference in New Issue
Block a user