feat: add adaptive agent workspace windows
This commit is contained in:
@@ -35,7 +35,6 @@ import { MapDevPanel } from "./components/map-dev-panel";
|
||||
import { MobileWorkbenchSheet } from "./components/mobile-workbench-sheet";
|
||||
import { FeaturePopover } from "./components/feature-popover";
|
||||
import { ScheduledConditionFeed } from "./components/scheduled-condition-feed";
|
||||
import { WorkbenchAgentPanels } from "./components/workbench-agent-panels";
|
||||
import {
|
||||
ToolbarPanel,
|
||||
type ExportViewPreset,
|
||||
@@ -43,6 +42,10 @@ import {
|
||||
type ToolbarToolId
|
||||
} from "./components/toolbar-panel";
|
||||
import { WorkbenchTopBar } from "./components/workbench-top-bar";
|
||||
import {
|
||||
WorkbenchMainFrame,
|
||||
type WorkbenchNavigationSection
|
||||
} from "./workspace/workbench-main-frame";
|
||||
import { WORKBENCH_SCENARIOS, WORKBENCH_USER } from "./data/workbench-session";
|
||||
import { useWorkbenchAgent } from "./hooks/use-workbench-agent";
|
||||
import { useWorkbenchDrawing, type WorkbenchDrawMode } from "./hooks/use-workbench-drawing";
|
||||
@@ -101,6 +104,8 @@ export function MapWorkbenchPage({
|
||||
const devPanelEnabled = env.TJWATER_ENABLE_DEV_PANEL;
|
||||
const mapContainerRef = useRef<HTMLDivElement | null>(null);
|
||||
const [detailFeature, setDetailFeature] = useState<DetailFeature | null>(null);
|
||||
const [mainFrameSection, setMainFrameSection] = useState<WorkbenchNavigationSection>("agent");
|
||||
const [mainFrameNavigationCollapsed, setMainFrameNavigationCollapsed] = useState(false);
|
||||
const [devPanelOpen, setDevPanelOpen] = useState(false);
|
||||
const [impactVisible, setImpactVisible] = useState(false);
|
||||
const [activeToolId, setActiveToolId] = useState<ToolbarToolId | null>(null);
|
||||
@@ -149,7 +154,6 @@ export function MapWorkbenchPage({
|
||||
agentPanelWidth,
|
||||
closeMobileSheet,
|
||||
conditionFeedExpanded,
|
||||
conditionFeedMounted,
|
||||
handleConditionExpandedChange,
|
||||
isLargeScreen,
|
||||
leftPanelOpen,
|
||||
@@ -159,11 +163,8 @@ export function MapWorkbenchPage({
|
||||
openConditionFeedForViewport,
|
||||
rightPanelExpanded,
|
||||
rightPanelOpen,
|
||||
setAgentPanelWidth,
|
||||
setMobileSheetSnap,
|
||||
shouldShowConditionFeed,
|
||||
toggleConditionFeedForViewport,
|
||||
viewportWidth
|
||||
toggleConditionFeedForViewport
|
||||
} = useWorkbenchResponsiveLayout({
|
||||
activeToolOpen: activeToolId !== null,
|
||||
agentPanelOpen: agent.panelOpen,
|
||||
@@ -173,6 +174,31 @@ export function MapWorkbenchPage({
|
||||
onClearActiveTool: clearActiveTool
|
||||
});
|
||||
|
||||
function openAgentWorkspaceForViewport() {
|
||||
if (isLargeScreen) {
|
||||
setMainFrameSection("agent");
|
||||
setMainFrameNavigationCollapsed(false);
|
||||
}
|
||||
openAgentPanelForViewport();
|
||||
}
|
||||
|
||||
function openConditionWorkspaceForViewport() {
|
||||
if (isLargeScreen) {
|
||||
setMainFrameSection("conditions");
|
||||
setMainFrameNavigationCollapsed(false);
|
||||
}
|
||||
openConditionFeedForViewport();
|
||||
}
|
||||
|
||||
function toggleConditionWorkspaceForViewport() {
|
||||
if (isLargeScreen) {
|
||||
setMainFrameSection((current) => current === "conditions" ? "agent" : "conditions");
|
||||
setMainFrameNavigationCollapsed(false);
|
||||
return;
|
||||
}
|
||||
toggleConditionFeedForViewport();
|
||||
}
|
||||
|
||||
const { mapRef, mapReady, mapError, sourceStatuses, fitNetworkBounds } = useWorkbenchMap({
|
||||
containerRef: mapContainerRef,
|
||||
impactVisible,
|
||||
@@ -182,9 +208,9 @@ export function MapWorkbenchPage({
|
||||
const { controller: mapController, state: mapControllerState } = useWorkbenchMapController({
|
||||
mapRef,
|
||||
mapReady,
|
||||
leftPanelOpen,
|
||||
rightPanelOpen,
|
||||
conditionPanelExpanded: rightPanelExpanded,
|
||||
leftPanelOpen: isLargeScreen ? false : leftPanelOpen,
|
||||
rightPanelOpen: isLargeScreen ? devPanelOpen : rightPanelOpen,
|
||||
conditionPanelExpanded: isLargeScreen ? false : rightPanelExpanded,
|
||||
agentPanelWidth
|
||||
});
|
||||
|
||||
@@ -442,7 +468,7 @@ export function MapWorkbenchPage({
|
||||
return;
|
||||
}
|
||||
|
||||
openAgentPanelForViewport();
|
||||
openAgentWorkspaceForViewport();
|
||||
|
||||
try {
|
||||
await agent.submitPrompt(
|
||||
@@ -479,7 +505,7 @@ export function MapWorkbenchPage({
|
||||
return;
|
||||
}
|
||||
|
||||
openConditionFeedForViewport();
|
||||
openConditionWorkspaceForViewport();
|
||||
setConditionFocusRequest((current) => ({
|
||||
conditionId,
|
||||
requestId: (current?.requestId ?? 0) + 1
|
||||
@@ -601,9 +627,9 @@ export function MapWorkbenchPage({
|
||||
zoom: trustedAction.zoom ?? Math.max(map.getZoom(), 14),
|
||||
padding: getResponsiveWorkbenchPadding(
|
||||
map,
|
||||
leftPanelOpen,
|
||||
rightPanelOpen,
|
||||
rightPanelExpanded,
|
||||
isLargeScreen ? false : leftPanelOpen,
|
||||
isLargeScreen ? devPanelOpen : rightPanelOpen,
|
||||
isLargeScreen ? false : rightPanelExpanded,
|
||||
agentPanelWidth
|
||||
),
|
||||
duration: trustedAction.durationMs ?? 500
|
||||
@@ -764,13 +790,6 @@ export function MapWorkbenchPage({
|
||||
} as CSSProperties
|
||||
}
|
||||
>
|
||||
<div
|
||||
ref={mapContainerRef}
|
||||
className="map-grid"
|
||||
style={{ position: "absolute", inset: 0 }}
|
||||
aria-label="供水管网地图"
|
||||
/>
|
||||
|
||||
<WorkbenchTopBar
|
||||
dataTime={headerDataTime}
|
||||
modelName="AquaDispatch v1.0"
|
||||
@@ -778,14 +797,14 @@ export function MapWorkbenchPage({
|
||||
activeScenarioId={activeScenarioId}
|
||||
alerts={workbenchAlerts}
|
||||
user={user}
|
||||
conditionFeedVisible={isLargeScreen ? shouldShowConditionFeed : mobileSheet === "condition"}
|
||||
conditionFeedVisible={isLargeScreen ? mainFrameSection === "conditions" && !mainFrameNavigationCollapsed : mobileSheet === "condition"}
|
||||
taskTickerAvailable={taskTickerAvailable}
|
||||
taskTickerVisible={taskTickerVisible}
|
||||
devPanelEnabled={devPanelEnabled}
|
||||
devPanelOpen={devPanelOpen}
|
||||
onSelectScenario={handleSelectScenario}
|
||||
onSelectAlert={handleSelectAlert}
|
||||
onToggleConditionFeed={toggleConditionFeedForViewport}
|
||||
onToggleConditionFeed={toggleConditionWorkspaceForViewport}
|
||||
onToggleTaskTicker={() => setTaskTickerVisible((current) => !current)}
|
||||
onToggleDevPanel={() => setDevPanelOpen((current) => !current)}
|
||||
onPreviewScenario={handlePreviewScenario}
|
||||
@@ -799,50 +818,73 @@ export function MapWorkbenchPage({
|
||||
onLogout={onLogout}
|
||||
/>
|
||||
|
||||
<WorkbenchAgentPanels
|
||||
panelProps={agentPanelProps}
|
||||
panelOpen={agent.panelOpen}
|
||||
panelCollapsing={agent.panelCollapsing}
|
||||
conditionExpanded={shouldShowConditionFeed && conditionFeedExpanded}
|
||||
personaState={agent.personaState}
|
||||
statusLabel={agent.statusLabel}
|
||||
viewportWidth={viewportWidth}
|
||||
onCollapsePanel={agent.collapsePanel}
|
||||
onExpandPanel={agent.expandPanel}
|
||||
onWidthCommit={setAgentPanelWidth}
|
||||
/>
|
||||
|
||||
{!devPanelOpen ? (
|
||||
<div className="absolute right-2 top-24 z-20 hidden lg:block">
|
||||
<MapToolbar items={toolbarItems} className="hidden lg:block" />
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{conditionFeedMounted && !devPanelOpen ? (
|
||||
<div className="absolute right-16 top-24 z-20 hidden lg:block 2xl:[--workbench-condition-width:var(--workbench-condition-width-wide)]">
|
||||
<WorkbenchMainFrame
|
||||
testEnabled={devPanelEnabled}
|
||||
activeSection={mainFrameSection}
|
||||
navigationCollapsed={mainFrameNavigationCollapsed}
|
||||
onActiveSectionChange={setMainFrameSection}
|
||||
onNavigationCollapsedChange={setMainFrameNavigationCollapsed}
|
||||
renderAgentPanel={(onCollapse) => (
|
||||
<AgentCommandPanel
|
||||
{...agentPanelProps}
|
||||
presentation="desktop-dock"
|
||||
onCollapse={onCollapse}
|
||||
/>
|
||||
)}
|
||||
conditionsPanel={(
|
||||
<ScheduledConditionFeed
|
||||
presentation="desktop-floating"
|
||||
presentation="desktop-dock"
|
||||
conditions={scheduledConditions}
|
||||
expanded={conditionFeedExpanded}
|
||||
focusRequest={conditionFocusRequest}
|
||||
loading={scheduledConditionsLoading}
|
||||
visible={shouldShowConditionFeed}
|
||||
visible
|
||||
selectedConditionId={selectedConditionId}
|
||||
onExpandedChange={handleConditionExpandedChange}
|
||||
onFocusRequestHandled={handleConditionFocusRequestHandled}
|
||||
onSelectedConditionChange={setSelectedConditionId}
|
||||
onExpandAgent={openAgentPanelForViewport}
|
||||
onExpandAgent={openAgentWorkspaceForViewport}
|
||||
onLoadHistorySession={handleLoadAgentHistorySession}
|
||||
onSubmitPrompt={agent.submitPrompt}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{!devPanelOpen ? (
|
||||
<div className="absolute right-16 top-24 z-20 hidden lg:block">
|
||||
<ToolbarPanel {...toolbarPanelProps} />
|
||||
</div>
|
||||
) : null}
|
||||
)}
|
||||
layersPanel={(
|
||||
<div className="flex h-full min-h-0 flex-col gap-3 overflow-y-auto p-3">
|
||||
<MapToolbar items={toolbarItems} orientation="horizontal" className="w-full flex-wrap" />
|
||||
<ToolbarPanel {...toolbarPanelProps} panelWidthClassName="w-full" />
|
||||
</div>
|
||||
)}
|
||||
toolsPanel={(
|
||||
<div className="flex h-full min-h-0 flex-col gap-3 overflow-y-auto p-3">
|
||||
<MapToolbar items={toolbarItems} orientation="horizontal" className="w-full flex-wrap" />
|
||||
<ToolbarPanel {...toolbarPanelProps} panelWidthClassName="w-full" />
|
||||
</div>
|
||||
)}
|
||||
mapContent={(
|
||||
<div
|
||||
ref={mapContainerRef}
|
||||
className="map-grid absolute inset-0"
|
||||
style={{ position: "absolute", inset: 0 }}
|
||||
aria-label="供水管网地图"
|
||||
/>
|
||||
)}
|
||||
mapOverlay={(
|
||||
<div className="pointer-events-none absolute inset-0">
|
||||
<div className="pointer-events-auto absolute right-3 top-3 hidden lg:block">
|
||||
<MapToolbar items={toolbarItems} />
|
||||
</div>
|
||||
<div className="pointer-events-auto absolute right-16 top-3 hidden lg:block">
|
||||
<ToolbarPanel {...toolbarPanelProps} />
|
||||
</div>
|
||||
<div className="absolute bottom-0 right-0 flex flex-col items-end gap-2">
|
||||
<div className="flex items-end gap-2 pr-2">
|
||||
<MapZoom mapRef={mapRef} mapReady={mapReady} onHome={fitNetworkBounds} />
|
||||
</div>
|
||||
<MapScaleLine mapRef={mapRef} mapReady={mapReady} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
|
||||
{devPanelEnabled && devPanelOpen ? (
|
||||
<MapDevPanel
|
||||
@@ -862,13 +904,6 @@ export function MapWorkbenchPage({
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="pointer-events-none absolute bottom-0 right-0 z-30 hidden flex-col items-end gap-2 md:flex">
|
||||
<div className="flex items-end gap-2 pr-2">
|
||||
<MapZoom mapRef={mapRef} mapReady={mapReady} onHome={fitNetworkBounds} />
|
||||
</div>
|
||||
<MapScaleLine mapRef={mapRef} mapReady={mapReady} />
|
||||
</div>
|
||||
|
||||
<AnimatePresence initial={false}>
|
||||
{shouldShowTaskTicker ? (
|
||||
<motion.div
|
||||
|
||||
Reference in New Issue
Block a user