feat(agent): support Responses steering and project-aware tools
Use the dedicated TJWATER_AGENT_URL and Responses stream protocol. Keep running turns active while users steer or queue follow-ups and edit pending delivery. Refresh the model catalog before generation and preserve approval mode selection. Integrate scoped investigation messaging/presentation, geographic place selection, current Agent composer styles and supporting workbench contracts. Validation: 77 focused Vitest checks, 21 browser cases, lint, typecheck and production build. Preserve direct Server/Agent routing and project authentication.
This commit is contained in:
@@ -13,6 +13,7 @@ import { CalendarClock, Clock3 } from "lucide-react";
|
||||
import {
|
||||
AgentCommandPanel,
|
||||
AgentPersona,
|
||||
parseFrontendChart,
|
||||
toTrustedMapAction,
|
||||
type FrontendActionRequest,
|
||||
type UIEnvelopePayload,
|
||||
@@ -26,18 +27,19 @@ import {
|
||||
MapSourceStatusNotice,
|
||||
MapToolbar,
|
||||
MapZoom,
|
||||
dismissMapNotice,
|
||||
showMapNotice,
|
||||
type MapToolbarItem,
|
||||
type MapSourceStatus
|
||||
} from "@/features/map/core";
|
||||
import { env } from "@/shared/config/env";
|
||||
import { waitForAgentPresentation } from "./map/agent-presentation";
|
||||
import type { AccessTokenProvider } from "@/shared/auth/keycloak-auth";
|
||||
import { AgentTaskTicker } from "./components/agent-task-ticker";
|
||||
import { AnalysisResultsPanel } from "./components/analysis-results-panel";
|
||||
import { PeripheralSamplePanel } from "./peripheral-sample/peripheral-sample-panel";
|
||||
import type { PeripheralWorkOrder } from "./peripheral-sample/client";
|
||||
import { MapDevPanel } from "./components/map-dev-panel";
|
||||
import { InvestigationPresentationPanel } from "./components/investigation-presentation-panel";
|
||||
import { MobileWorkbenchSheet } from "./components/mobile-workbench-sheet";
|
||||
import { FeaturePopover } from "./components/feature-popover";
|
||||
import { ScheduledConditionFeed } from "./components/scheduled-condition-feed";
|
||||
@@ -68,11 +70,16 @@ import {
|
||||
} from "./workspace/workspace-context";
|
||||
import { WORKBENCH_USER } from "./data/workbench-session";
|
||||
import { useWorkbenchAgent } from "./hooks/use-workbench-agent";
|
||||
import { useAgentPromptSuggestions } from "./hooks/use-agent-prompt-suggestions";
|
||||
import { operationalTaskConditionId, useOperationalTasks } from "./hooks/use-operational-tasks";
|
||||
import { useWorkbenchSync } from "./hooks/use-workbench-sync";
|
||||
import { useWorkbenchSyncNotice } from "./hooks/use-workbench-sync-notice";
|
||||
import { useInvestigationPresentation } from "./hooks/use-investigation-presentation";
|
||||
import type { InvestigationMapTarget } from "./api/investigation-report";
|
||||
import { useWorkbenchDrawing, type WorkbenchDrawMode } from "./hooks/use-workbench-drawing";
|
||||
import { useWorkbenchMap } from "./hooks/use-workbench-map";
|
||||
import { useWorkbenchMapController } from "./hooks/use-workbench-map-controller";
|
||||
import { useWorkbenchGeocoding } from "./hooks/use-workbench-geocoding";
|
||||
import { useWorkbenchProjectPresentation } from "./hooks/use-workbench-project-presentation";
|
||||
import { useWorkbenchResponsiveLayout } from "./hooks/use-workbench-responsive-layout";
|
||||
import { formatHeaderDataTime, useWorkbenchRuntimeData } from "./hooks/use-workbench-runtime-data";
|
||||
@@ -88,7 +95,7 @@ import {
|
||||
getWorkbenchLayoutCssVariables,
|
||||
resolveMobileWorkbenchOverlayLayout
|
||||
} from "./layout/workbench-layout";
|
||||
import { getResponsiveWorkbenchPadding } from "./map/camera";
|
||||
import { getResponsivePadding, getResponsiveWorkbenchPadding } from "./map/camera";
|
||||
import { exportMapViewImage } from "./map/export-view";
|
||||
import { parseScadaAnalysisItems } from "./map/scada-analysis";
|
||||
import {
|
||||
@@ -135,6 +142,8 @@ export function MapWorkbenchPage({
|
||||
const mapContainerRef = useRef<HTMLDivElement | null>(null);
|
||||
const [detailFeature, setDetailFeature] = useState<DetailFeature | null>(null);
|
||||
const [devPanelOpen, setDevPanelOpen] = useState(false);
|
||||
const [investigationFollow, setInvestigationFollow] = useState(true);
|
||||
const presentationLayerChangesRef = useRef(new Map<string, boolean>());
|
||||
const [impactVisible, setImpactVisible] = useState(false);
|
||||
const [activeToolId, setActiveToolId] = useState<ToolbarToolId | null>(null);
|
||||
const prefersReducedMotion = useReducedMotion();
|
||||
@@ -158,25 +167,7 @@ export function MapWorkbenchPage({
|
||||
useState<Record<string, boolean>>(INITIAL_LAYER_VISIBILITY);
|
||||
const [agentUiResults, setAgentUiResults] = useState<AgentUiResult[]>([]);
|
||||
const workbenchSync = useWorkbenchSync(getAccessToken);
|
||||
const workbenchSyncReconnecting =
|
||||
workbenchSync.status === "reconnecting" && workbenchSync.snapshot !== null;
|
||||
useEffect(() => {
|
||||
const noticeId = "workbench-sync-reconnecting";
|
||||
if (!workbenchSyncReconnecting) {
|
||||
dismissMapNotice(noticeId);
|
||||
return;
|
||||
}
|
||||
|
||||
showMapNotice({
|
||||
id: noticeId,
|
||||
tone: "warning",
|
||||
title: "业务同步正在重连",
|
||||
message: "当前数据可能陈旧,连接恢复后将自动更新。",
|
||||
duration: Infinity
|
||||
});
|
||||
|
||||
return () => dismissMapNotice(noticeId);
|
||||
}, [workbenchSyncReconnecting]);
|
||||
useWorkbenchSyncNotice(workbenchSync.status === "reconnecting" && workbenchSync.snapshot !== null);
|
||||
const {
|
||||
headerDataTime,
|
||||
scheduledConditions: runtimeScheduledConditions,
|
||||
@@ -192,6 +183,8 @@ export function MapWorkbenchPage({
|
||||
operationalCursorTime
|
||||
} = useWorkbenchRuntimeData(getAccessToken, workbenchSync);
|
||||
const operationalTasks = useOperationalTasks(getAccessToken, workbenchSync);
|
||||
const selectedInvestigationTask = operationalTasks.tasks.find((task) =>
|
||||
task.task_type === "event_investigation" && operationalTaskConditionId(task.task_id) === selectedConditionId) ?? null;
|
||||
const baseLayerOptions = useMemo(
|
||||
() =>
|
||||
BASE_LAYER_OPTIONS.map((option) => ({
|
||||
@@ -464,6 +457,36 @@ export function MapWorkbenchPage({
|
||||
void mapController.clearPipeFlowCalculations();
|
||||
}, [mapController, mapReady, realtimePipeFlows]);
|
||||
|
||||
const presentation = useInvestigationPresentation({
|
||||
enabled: investigationFollow, task: selectedInvestigationTask, projectId: projectPresentation.projectId,
|
||||
sync: workbenchSync, getAccessToken, mapRef, mapReady, controller: mapController,
|
||||
onClearLayers: restorePresentationLayers,
|
||||
getPadding: () => mapRef.current ? getResponsiveWorkbenchPadding(mapRef.current, leftPanelOpen, rightPanelOpen, rightPanelExpanded, agentPanelWidth) : { top: 48, right: 48, bottom: 48, left: 48 }
|
||||
});
|
||||
const geocoding = useWorkbenchGeocoding({
|
||||
active: activeToolId === "geocoding",
|
||||
projectId: projectPresentation.projectId,
|
||||
getAccessToken, mapRef, mapReady,
|
||||
reducedMotion: Boolean(prefersReducedMotion),
|
||||
onLocate: presentation.pauseCamera,
|
||||
getPadding: () => {
|
||||
const map = mapRef.current;
|
||||
if (!map) return { top: 48, right: 48, bottom: 48, left: 48 };
|
||||
const padding = getResponsiveWorkbenchPadding(map, leftPanelOpen, rightPanelOpen, rightPanelExpanded, agentPanelWidth);
|
||||
return getResponsivePadding(map, isLargeScreen
|
||||
? { ...padding, right: Math.max(padding.right, 400) }
|
||||
: { ...padding, bottom: Math.max(padding.bottom, 420) });
|
||||
}
|
||||
});
|
||||
function handleLocateInvestigationTargets(targets: InvestigationMapTarget[]) {
|
||||
void presentation.locateTargets(targets).catch((error) => showMapNotice({ tone: "warning", title: "关联要素暂不可定位", message: error instanceof Error ? error.message : "地图当前不可用" }));
|
||||
}
|
||||
useEffect(() => () => {
|
||||
restorePresentationLayers();
|
||||
// Layer restoration belongs to the followed task, not each detail refresh.
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [selectedInvestigationTask?.task_id, investigationFollow]);
|
||||
|
||||
const activeAgentUiResults = useMemo(
|
||||
() => agentUiResults.filter((result) => result.sessionId === agent.sessionId),
|
||||
[agent.sessionId, agentUiResults]
|
||||
@@ -622,6 +645,7 @@ export function MapWorkbenchPage({
|
||||
[layerControlItems]
|
||||
);
|
||||
function handleToggleLayer(layerControlId: string, visible: boolean) {
|
||||
presentationLayerChangesRef.current.delete(layerControlId);
|
||||
const map = mapRef.current;
|
||||
|
||||
setLayerVisibility((current) => ({ ...current, [layerControlId]: visible }));
|
||||
@@ -637,6 +661,20 @@ export function MapWorkbenchPage({
|
||||
map.setLayoutProperty(layerId, "visibility", visible ? "visible" : "none");
|
||||
});
|
||||
}
|
||||
function handleTogglePresentationLayer(id: string, visible: boolean) {
|
||||
const before = presentationLayerChangesRef.current.get(id) ?? (layerVisibility[id] !== false);
|
||||
handleToggleLayer(id, visible);
|
||||
presentationLayerChangesRef.current.set(id, before);
|
||||
}
|
||||
function restorePresentationLayers() {
|
||||
for (const [id, visible] of presentationLayerChangesRef.current) handleToggleLayer(id, visible);
|
||||
presentationLayerChangesRef.current.clear();
|
||||
}
|
||||
const presentationLayerIds = new Set([
|
||||
...presentation.highlightLayers,
|
||||
...(selectedInvestigationTask?.latest_progress?.progress.diagram?.node_refs.flatMap((node) => node.map_targets?.map((target) => target.source_id) ?? []) ?? [])
|
||||
].flatMap((id) => id === "scada" ? ["scada-pressure", "scada-flow"] : [id]));
|
||||
const presentationRelatedLayers = layerControlItems.filter((item) => presentationLayerIds.has(item.id)).map((item) => ({ id: item.id, label: item.label, visible: item.visible }));
|
||||
|
||||
async function handleExportView(preset: ExportViewPreset) {
|
||||
const map = mapRef.current;
|
||||
@@ -731,8 +769,8 @@ export function MapWorkbenchPage({
|
||||
if (workbenchAlerts.length === 0) {
|
||||
showMapNotice({
|
||||
tone: "info",
|
||||
title: "当前无待复核工况",
|
||||
message: "当前没有需要汇总的工况提醒。"
|
||||
title: "当前无待处理事项",
|
||||
message: "当前没有需要汇总的待办或告警。"
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -756,13 +794,13 @@ export function MapWorkbenchPage({
|
||||
showMapNotice({
|
||||
tone: "success",
|
||||
title: "已发起工况汇总",
|
||||
message: "已将待复核工况发送到 Agent。"
|
||||
message: "已将待办与告警发送给 Agent。"
|
||||
});
|
||||
} catch (error) {
|
||||
showMapNotice({
|
||||
tone: "error",
|
||||
title: "工况汇总发起失败",
|
||||
message: error instanceof Error ? error.message : "无法将待复核工况发送到 Agent。"
|
||||
message: error instanceof Error ? error.message : "无法将待办与告警发送给 Agent。"
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -837,6 +875,34 @@ export function MapWorkbenchPage({
|
||||
}
|
||||
|
||||
async function handleFrontendAction(request: FrontendActionRequest, signal: AbortSignal) {
|
||||
signal.throwIfAborted();
|
||||
if (request.name === "locate_features") {
|
||||
const action = toTrustedMapAction("locate_features", request.params);
|
||||
if (!action || action.type !== "locate_features" || !action.layer) throw new Error("INVALID_MAP_TARGET");
|
||||
const result = await mapController.locateFeatures(action.layer as Parameters<typeof mapController.locateFeatures>[0], action.featureIds, signal);
|
||||
return result;
|
||||
}
|
||||
if (request.name === "zoom_to_map") {
|
||||
const action = toTrustedMapAction("zoom_to_map", request.params);
|
||||
if (!action || action.type !== "zoom_to_map") throw new Error("INVALID_MAP_CAMERA");
|
||||
const map = mapRef.current;
|
||||
if (!map || !mapReady) throw new Error("MAP_NOT_READY");
|
||||
map.jumpTo({ center: action.center, zoom: action.zoom });
|
||||
await new Promise<void>((resolve) => requestAnimationFrame(() => resolve()));
|
||||
signal.throwIfAborted();
|
||||
return { fitted: true };
|
||||
}
|
||||
if (request.name === "show_chart" || request.name === "view_history") {
|
||||
const chart = parseFrontendChart(request.name === "view_history" ? request.params.chart : request.params);
|
||||
if (!chart) throw new Error("INVALID_CHART_DATA");
|
||||
const data = { x_data: chart.x_data, series: chart.series };
|
||||
const envelope: AgentUiResult["envelope"] = request.name === "view_history"
|
||||
? { kind: "registered_component", schemaVersion: "agent-ui@1", component: "HistoryPanel", surface: "side_panel", props: { feature_id: request.params.element_id, metric: chart.title, start_time: request.params.start, end_time: request.params.end }, data }
|
||||
: { kind: "chart", schemaVersion: "agent-ui@1", grammar: "echarts-safe-subset", surface: "side_panel", spec: { title: chart.title, chart_type: chart.chart_type }, data };
|
||||
setAgentUiResults((current) => [...current.slice(-5), { id: request.actionId, sessionId: request.sessionId, createdAt: Date.now(), envelope }]);
|
||||
await waitForAgentPresentation(request.actionId, signal);
|
||||
return { displayed: true, series_count: chart.series.length };
|
||||
}
|
||||
if (request.name === "render_scada_analysis") {
|
||||
const items = parseScadaAnalysisItems(request.params.items);
|
||||
if (!items) throw new Error("INVALID_SCADA_ANALYSIS");
|
||||
@@ -875,8 +941,7 @@ export function MapWorkbenchPage({
|
||||
|
||||
async function handleOpenTaskCollaboration(taskId: string, discussion?: InvestigationDiscussion) {
|
||||
mapController.clearScadaAnalysis();
|
||||
await agent.openTaskCollaboration(taskId, discussion?.context);
|
||||
if (discussion?.prompt) await agent.submitPrompt(discussion.prompt);
|
||||
await agent.openTaskCollaboration(taskId, discussion);
|
||||
}
|
||||
|
||||
async function handleDeleteAgentHistorySession(sessionId: string) {
|
||||
@@ -995,6 +1060,7 @@ export function MapWorkbenchPage({
|
||||
}
|
||||
|
||||
const toolbarPanelProps: ToolbarPanelProps = {
|
||||
geocoding,
|
||||
activeToolId,
|
||||
activeMeasureModeId,
|
||||
activeMeasureUnitId,
|
||||
@@ -1067,6 +1133,20 @@ export function MapWorkbenchPage({
|
||||
const collaborationReviewTask = agent.sessionPurpose !== "ordinary_chat" && agent.sessionTaskId
|
||||
? operationalTasks.tasks.find((item) => item.task_id === agent.sessionTaskId)
|
||||
: undefined;
|
||||
const promptSuggestions = useAgentPromptSuggestions({
|
||||
enabled: agent.conversationScope === "project" && (isLargeScreen ? agent.panelOpen : mobileSheet === "agent") && agent.messages.length === 0 && !agent.streaming &&
|
||||
agent.sessionPurpose === "ordinary_chat" && agent.connectionStatus === "online" && !agent.sessionHistoryLoading &&
|
||||
!scheduledConditionsLoading && !operationalTasks.loading,
|
||||
model: agent.selectedModel,
|
||||
sessionId: agent.sessionId,
|
||||
request: agent.generatePromptSuggestions,
|
||||
alerts: workbenchAlerts,
|
||||
alertDataSource,
|
||||
conditions: scheduledConditions,
|
||||
runtimeDataSource,
|
||||
artifact: latestAnalysisArtifact ?? null,
|
||||
sessions: agent.sessionHistory,
|
||||
});
|
||||
const agentPanelProps: Omit<
|
||||
ComponentProps<typeof AgentCommandPanel>,
|
||||
"collapsing" | "onCollapse"
|
||||
@@ -1075,15 +1155,24 @@ export function MapWorkbenchPage({
|
||||
sessionTitle: agent.sessionTitle,
|
||||
sessionHistory: agent.sessionHistory,
|
||||
sessionHistoryLoading: agent.sessionHistoryLoading,
|
||||
promptSuggestions: promptSuggestions.suggestions,
|
||||
promptSuggestionsLoading: promptSuggestions.loading,
|
||||
promptSuggestionsUnavailable: promptSuggestions.unavailable,
|
||||
activeSessionId: agent.sessionId,
|
||||
qaOnly: agent.sessionPurpose === "task_collaboration",
|
||||
statusLabel: agent.statusLabel,
|
||||
connectionStatus: agent.connectionStatus,
|
||||
streaming: agent.streaming,
|
||||
inputDelivery: agent.inputDelivery,
|
||||
onInputDeliveryChange: agent.setInputDelivery,
|
||||
pendingInputs: agent.pendingInputs,
|
||||
onPendingInputDeliveryChange: agent.updateInputDelivery,
|
||||
messages: agent.messages,
|
||||
streamRenderState: agent.streamRenderState,
|
||||
modelOptions: agent.modelOptions,
|
||||
selectedModel: agent.selectedModel,
|
||||
conversationScope: agent.conversationScope,
|
||||
onConversationScopeChange: agent.changeConversationScope,
|
||||
approvalMode: agent.approvalMode,
|
||||
uiResults: activeAgentUiResults,
|
||||
reviewPanel: collaborationReviewTask ? <TaskConversationReview task={collaborationReviewTask}
|
||||
@@ -1096,6 +1185,17 @@ export function MapWorkbenchPage({
|
||||
onRefresh={operationalTasks.refresh} /> : undefined,
|
||||
workspaceContext: agentWorkspaceContext,
|
||||
prompt: agent.promptDraft,
|
||||
promptReference: collaborationReviewTask ? {
|
||||
title: collaborationReviewTask.title,
|
||||
placeholder: "可询问调查进展、讨论报告或补充信息",
|
||||
detail: [
|
||||
agent.taskDiscussionContext?.report_revision && agent.taskDiscussionReportGeneration
|
||||
? `第 ${agent.taskDiscussionReportGeneration} 轮` : null,
|
||||
agent.taskDiscussionContext?.report_revision ? `报告第 ${agent.taskDiscussionContext.report_revision} 版` : null,
|
||||
agent.taskDiscussionContext?.evidence_id ? "已引用证据" : null,
|
||||
agent.taskDiscussionContext?.gap_id ? "已引用数据缺口" : null
|
||||
].filter(Boolean).join(" · ")
|
||||
} : undefined,
|
||||
onRefreshHistory: agent.refreshSessionHistory,
|
||||
onStartNewSession: handleStartNewAgentSession,
|
||||
onLoadHistorySession: handleLoadAgentHistorySession,
|
||||
@@ -1104,6 +1204,12 @@ export function MapWorkbenchPage({
|
||||
onSelectModel: agent.setSelectedModel,
|
||||
onApprovalModeChange: agent.setApprovalMode,
|
||||
onPromptChange: agent.setPromptDraft,
|
||||
promptFocusRequest: agent.promptFocusRequest,
|
||||
messageInbox: agent.sessionPurpose === "ordinary_chat" ? {
|
||||
...agent.agentMessageInbox,
|
||||
onLoadMore: agent.agentMessageInbox.loadMore,
|
||||
onRefresh: agent.agentMessageInbox.refresh
|
||||
} : undefined,
|
||||
onSubmitPrompt: agent.submitPrompt,
|
||||
onLocateAnswerFeatures: (target) => mapController.locateFeatures(target.layer, target.feature_ids, new AbortController().signal),
|
||||
onStopPrompt: agent.stopPrompt,
|
||||
@@ -1259,6 +1365,13 @@ export function MapWorkbenchPage({
|
||||
}
|
||||
mapOverlay={
|
||||
<div className="pointer-events-none absolute inset-x-0 bottom-0 top-14">
|
||||
{investigationFollow && selectedInvestigationTask && !devPanelOpen ? <InvestigationPresentationPanel
|
||||
task={selectedInvestigationTask} charts={presentation.charts} overlay={presentation.overlay} cameraPaused={presentation.cameraPaused}
|
||||
status={presentation.status} error={presentation.error}
|
||||
width={Math.min(380, Math.max(180, businessWorkspaceWidth - (rightPanelOpen ? (rightPanelExpanded ? (viewportWidth >= 1536 ? 960 : 880) : 432) + 64 : 64) - 24))}
|
||||
relatedLayers={presentationRelatedLayers} onToggleLayer={handleTogglePresentationLayer}
|
||||
onResumeCamera={presentation.resumeCamera} onCloseChart={presentation.closeChart} onToggleOverlay={presentation.toggleOverlay} onFitOverlay={presentation.fitOverlay}
|
||||
/> : null}
|
||||
{!devPanelOpen ? (
|
||||
<>
|
||||
<div className="pointer-events-auto absolute right-3 top-10 hidden lg:block">
|
||||
@@ -1278,7 +1391,9 @@ export function MapWorkbenchPage({
|
||||
} as CSSProperties
|
||||
}
|
||||
>
|
||||
<div className="flex items-end gap-2 lg:pr-2">
|
||||
<div className="flex items-end gap-2 lg:pr-2"
|
||||
onPointerDownCapture={() => { if (investigationFollow) presentation.pauseCamera(); }}
|
||||
onKeyDownCapture={() => { if (investigationFollow) presentation.pauseCamera(); }}>
|
||||
<MapZoom
|
||||
mapRef={mapRef}
|
||||
mapReady={mapReady}
|
||||
@@ -1305,8 +1420,8 @@ export function MapWorkbenchPage({
|
||||
onWidthCommit={setAgentPanelWidth}
|
||||
/>
|
||||
|
||||
{conditionFeedMounted && !devPanelOpen ? (
|
||||
<div className="absolute bottom-[calc(var(--workbench-timeline-height)+1.25rem)] right-16 top-24 z-50 hidden lg:block 2xl:[--workbench-condition-width:var(--workbench-condition-width-wide)]">
|
||||
{conditionFeedMounted ? (
|
||||
<div className="absolute bottom-[calc(var(--workbench-timeline-height)+1.25rem)] right-16 top-24 z-50 hidden lg:block 2xl:[--workbench-condition-width:var(--workbench-condition-width-wide)]" style={devPanelOpen ? { display: "none" } : undefined}>
|
||||
<ScheduledConditionFeed
|
||||
presentation="desktop-floating"
|
||||
conditions={scheduledConditions}
|
||||
@@ -1322,6 +1437,7 @@ export function MapWorkbenchPage({
|
||||
onExpandedChange={handleConditionExpandedChange}
|
||||
onFocusRequestHandled={handleConditionFocusRequestHandled}
|
||||
onSelectedConditionChange={setSelectedConditionId}
|
||||
onLocate={handleLocateInvestigationTargets}
|
||||
onExpandAgent={openAgentWorkspaceForViewport}
|
||||
onLoadHistorySession={handleLoadAgentHistorySession}
|
||||
onSubmitPrompt={agent.submitPrompt}
|
||||
@@ -1347,6 +1463,8 @@ export function MapWorkbenchPage({
|
||||
controllerState={mapControllerState}
|
||||
detailFeature={detailFeature}
|
||||
flowDataSource={flowDataSource}
|
||||
investigationFollow={investigationFollow}
|
||||
onInvestigationFollowChange={setInvestigationFollow}
|
||||
onClose={() => setDevPanelOpen(false)}
|
||||
/>
|
||||
) : null}
|
||||
@@ -1409,7 +1527,7 @@ export function MapWorkbenchPage({
|
||||
<MapToolbar
|
||||
items={toolbarItems}
|
||||
orientation="horizontal"
|
||||
className="h-11 justify-center"
|
||||
className="h-11 min-w-0 flex-1 justify-center [&>div]:min-w-0 [&>div]:flex-1 [&>div>div]:hidden [&_button]:w-full"
|
||||
/>
|
||||
) : null}
|
||||
<button
|
||||
@@ -1473,6 +1591,7 @@ export function MapWorkbenchPage({
|
||||
onExpandedChange={handleConditionExpandedChange}
|
||||
onFocusRequestHandled={handleConditionFocusRequestHandled}
|
||||
onSelectedConditionChange={setSelectedConditionId}
|
||||
onLocate={handleLocateInvestigationTargets}
|
||||
onExpandAgent={openAgentPanelForViewport}
|
||||
onClose={closeMobileSheet}
|
||||
onLoadHistorySession={handleLoadAgentHistorySession}
|
||||
|
||||
Reference in New Issue
Block a user