删除realtime 的一个组件
This commit is contained in:
Generated
+12
-1
@@ -15,7 +15,8 @@
|
||||
"@xyflow/react": "^12.4.4",
|
||||
"elkjs": "^0.9.3",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1"
|
||||
"react-dom": "^18.3.1",
|
||||
"react-resizable-panels": "^4.12.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.51.1",
|
||||
@@ -3396,6 +3397,16 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-resizable-panels": {
|
||||
"version": "4.12.2",
|
||||
"resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-4.12.2.tgz",
|
||||
"integrity": "sha512-NwY5LCo4WrxVvDh0xoMML6EMLPONP/8ckKcIdpnojxexoatZdjLiRqLJQjQK5CPkd4SYiB/2M5BVrjZBQtOO7Q==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-transition-group": {
|
||||
"version": "4.4.5",
|
||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
"@xyflow/react": "^12.4.4",
|
||||
"elkjs": "^0.9.3",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1"
|
||||
"react-dom": "^18.3.1",
|
||||
"react-resizable-panels": "^4.12.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.51.1",
|
||||
|
||||
+141
-14
@@ -1,17 +1,144 @@
|
||||
import {Alert,Box,CircularProgress,Container,Stack,Typography} from "@mui/material";
|
||||
import {useEffect,useMemo,useState} from "react";
|
||||
import {Alert, Box, CircularProgress, Container, Stack, Typography, useMediaQuery} from "@mui/material";
|
||||
import {useTheme} from "@mui/material/styles";
|
||||
import {useEffect, useMemo, useState} from "react";
|
||||
import {Group, Panel, Separator} from "react-resizable-panels";
|
||||
import {Category_Filter} from "./gallery/category_filter";
|
||||
import {Frame_Mode_Tabs} from "./gallery/frame_mode_tabs";
|
||||
import {Gallery_Page} from "./gallery/gallery_page";
|
||||
import {Gallery_Summary} from "./gallery/gallery_summary";
|
||||
import {Gallery_Toolbar} from "./gallery/gallery_toolbar";
|
||||
import type {Selected_Plot} from "./gallery/plot_card";
|
||||
import {use_gallery_catalog} from "./hooks/use_gallery_catalog";
|
||||
import {Gallery_Toolbar} from "./gallery/gallery_toolbar";import {Gallery_Summary} from "./gallery/gallery_summary";import {Frame_Mode_Tabs} from "./gallery/frame_mode_tabs";import {Category_Filter} from "./gallery/category_filter";import {Gallery_Page} from "./gallery/gallery_page";import type {Selected_Plot} from "./gallery/plot_card";import {Inspector_Drawer} from "./inspector/inspector_drawer";
|
||||
import {Inspector_Panel} from "./inspector/inspector_panel";
|
||||
|
||||
export function App() {
|
||||
const {catalog,state:socket_state,message:socket_message}=use_gallery_catalog();
|
||||
const [active_mode,set_active_mode]=useState(""),[active_category,set_active_category]=useState(""),[streams_paused,set_streams_paused]=useState(false),[selected_plot,set_selected_plot]=useState<Selected_Plot|null>(null),[selected_inspector_tab,set_selected_inspector_tab]=useState("controls");
|
||||
const [,set_visibility_epoch]=useState(0);
|
||||
useEffect(()=>{const change=()=>set_visibility_epoch(value=>value+1);document.addEventListener("visibilitychange",change);return()=>document.removeEventListener("visibilitychange",change);},[]);
|
||||
useEffect(()=>{if(catalog){set_active_mode(catalog.frame_modes.some(mode=>mode.id===catalog.navigation.default_mode)?catalog.navigation.default_mode:catalog.frame_modes[0]?.id??"");set_active_category(catalog.navigation.all_categories_label);}},[catalog]);
|
||||
const modes=useMemo(()=>[...(catalog?.frame_modes??[])].sort((a,b)=>a.order-b.order),[catalog]);
|
||||
const cases=useMemo(()=>[...(catalog?.cases??[])].sort((a,b)=>a.order-b.order),[catalog]);
|
||||
const frame_mode=modes.find(mode=>mode.id===active_mode);
|
||||
const categories=catalog?[catalog.navigation.all_categories_label,...new Set(cases.map(item=>item.category))]:[];
|
||||
const visible_cases=catalog?cases.filter(item=>active_category===catalog.navigation.all_categories_label||item.category===active_category):[];
|
||||
return <><Gallery_Toolbar socket_state={socket_state} socket_message={socket_message} streams_paused={streams_paused} on_toggle_streams={()=>set_streams_paused(value=>!value)}/><Container maxWidth={false} sx={{py:3}}><Stack spacing={2.5}>{!catalog&&socket_state!=="error"&&<Box sx={{display:"grid",placeItems:"center",minHeight:"55vh",gap:2}}><CircularProgress/><Typography>等待后端返回控件与帧策略目录</Typography></Box>}{!catalog&&socket_state==="error"&&<Alert severity="error">{socket_message}</Alert>}{catalog&&<><Gallery_Summary catalog={catalog}/><Frame_Mode_Tabs frame_modes={modes} active_mode={active_mode} on_change={id=>{set_active_mode(id);set_selected_plot(null);}}/><Category_Filter categories={categories} active_category={active_category} on_change={set_active_category}/>{frame_mode&&<><Typography color="text.secondary">{frame_mode.description}</Typography><Gallery_Page cases={visible_cases} frame_mode={frame_mode} streams_paused={streams_paused} on_open_inspector={set_selected_plot}/></>}</>}</Stack></Container><Inspector_Drawer open={selected_plot!==null} session={selected_plot?.session??null} active_tab={selected_inspector_tab} on_change_tab={set_selected_inspector_tab} on_close={()=>set_selected_plot(null)}/></>;
|
||||
const {catalog, state: socket_state, message: socket_message} = use_gallery_catalog();
|
||||
const [active_mode, set_active_mode] = useState("");
|
||||
const [active_category, set_active_category] = useState("");
|
||||
const [streams_paused, set_streams_paused] = useState(false);
|
||||
const [selected_plot, set_selected_plot] = useState<Selected_Plot | null>(null);
|
||||
const [selected_inspector_tab, set_selected_inspector_tab] = useState("controls");
|
||||
const [, set_visibility_epoch] = useState(0);
|
||||
const theme = useTheme();
|
||||
const compact_layout = useMediaQuery(theme.breakpoints.down("md"));
|
||||
|
||||
useEffect(() => {
|
||||
const change = () => set_visibility_epoch(value => value + 1);
|
||||
document.addEventListener("visibilitychange", change);
|
||||
return () => document.removeEventListener("visibilitychange", change);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!catalog)
|
||||
return;
|
||||
set_active_mode(
|
||||
catalog.frame_modes.some(mode => mode.id === catalog.navigation.default_mode)
|
||||
? catalog.navigation.default_mode
|
||||
: catalog.frame_modes[0]?.id ?? "",
|
||||
);
|
||||
set_active_category(catalog.navigation.all_categories_label);
|
||||
}, [catalog]);
|
||||
|
||||
const modes = useMemo(
|
||||
() => [...(catalog?.frame_modes ?? [])].sort((left, right) => left.order - right.order),
|
||||
[catalog],
|
||||
);
|
||||
const cases = useMemo(
|
||||
() => [...(catalog?.cases ?? [])].sort((left, right) => left.order - right.order),
|
||||
[catalog],
|
||||
);
|
||||
const frame_mode = modes.find(mode => mode.id === active_mode);
|
||||
const categories = catalog
|
||||
? [catalog.navigation.all_categories_label, ...new Set(cases.map(item => item.category))]
|
||||
: [];
|
||||
const visible_cases = catalog
|
||||
? cases.filter(item => active_category === catalog.navigation.all_categories_label || item.category === active_category)
|
||||
: [];
|
||||
|
||||
const gallery = <Box sx={{height: "100%", overflow: "auto"}}>
|
||||
<Container maxWidth={false} sx={{py: 3}}>
|
||||
<Stack spacing={2.5}>
|
||||
{!catalog && socket_state !== "error" && <Box sx={{display: "grid", placeItems: "center", minHeight: "55vh", gap: 2}}>
|
||||
<CircularProgress/>
|
||||
<Typography>等待后端返回控件与帧策略目录</Typography>
|
||||
</Box>}
|
||||
{!catalog && socket_state === "error" && <Alert severity="error">{socket_message}</Alert>}
|
||||
{catalog && <>
|
||||
<Gallery_Summary catalog={catalog}/>
|
||||
<Frame_Mode_Tabs
|
||||
frame_modes={modes}
|
||||
active_mode={active_mode}
|
||||
on_change={id => {
|
||||
set_active_mode(id);
|
||||
set_selected_plot(null);
|
||||
}}
|
||||
/>
|
||||
<Category_Filter
|
||||
categories={categories}
|
||||
active_category={active_category}
|
||||
on_change={set_active_category}
|
||||
/>
|
||||
{frame_mode && <>
|
||||
<Typography color="text.secondary">{frame_mode.description}</Typography>
|
||||
<Gallery_Page
|
||||
cases={visible_cases}
|
||||
frame_mode={frame_mode}
|
||||
streams_paused={streams_paused}
|
||||
on_open_inspector={set_selected_plot}
|
||||
/>
|
||||
</>}
|
||||
</>}
|
||||
</Stack>
|
||||
</Container>
|
||||
</Box>;
|
||||
|
||||
return <Box sx={{height: "100dvh", display: "flex", flexDirection: "column", overflow: "hidden"}}>
|
||||
<Gallery_Toolbar
|
||||
socket_state={socket_state}
|
||||
socket_message={socket_message}
|
||||
streams_paused={streams_paused}
|
||||
on_toggle_streams={() => set_streams_paused(value => !value)}
|
||||
/>
|
||||
<Box component="main" sx={{flex: 1, minHeight: 0}}>
|
||||
<Group
|
||||
id="gallery-inspector-layout"
|
||||
orientation={compact_layout ? "vertical" : "horizontal"}
|
||||
defaultLayout={selected_plot ? {gallery: 60, inspector: 40} : {gallery: 100}}
|
||||
style={{height: "100%"}}
|
||||
>
|
||||
<Panel id="gallery" minSize={compact_layout ? "220px" : "420px"}>
|
||||
{gallery}
|
||||
</Panel>
|
||||
{selected_plot && <>
|
||||
<Separator
|
||||
id="inspector-resize-handle"
|
||||
aria-label="拖动调整图库和操作面板占比"
|
||||
style={{
|
||||
background: "rgba(89, 214, 197, .32)",
|
||||
width: compact_layout ? "100%" : 8,
|
||||
height: compact_layout ? 8 : "100%",
|
||||
cursor: compact_layout ? "row-resize" : "col-resize",
|
||||
touchAction: "none",
|
||||
transition: "background-color 120ms ease",
|
||||
}}
|
||||
/>
|
||||
<Panel
|
||||
id="inspector"
|
||||
defaultSize="40%"
|
||||
minSize={compact_layout ? "260px" : "360px"}
|
||||
maxSize="72%"
|
||||
collapsible
|
||||
collapsedSize={0}
|
||||
>
|
||||
<Inspector_Panel
|
||||
session={selected_plot.session}
|
||||
active_tab={selected_inspector_tab}
|
||||
on_change_tab={set_selected_inspector_tab}
|
||||
on_close={() => set_selected_plot(null)}
|
||||
/>
|
||||
</Panel>
|
||||
</>}
|
||||
</Group>
|
||||
</Box>
|
||||
</Box>;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,85 @@
|
||||
import {Background,Controls,MiniMap,ReactFlow} from "@xyflow/react";import {Box} from "@mui/material";import {useEffect,useMemo,useState} from "react";import type {Gallery_Captured_Frame,Gallery_Node_Statistics,Gallery_Render_Plan} from "../protocol/gallery_types";import {build_dag_model} from "./dag_model";import {layout_dag} from "./dag_layout";import {Dag_Node} from "./dag_node";import type {Dag_View_Model} from "./dag_types";
|
||||
const node_types={dag_node:Dag_Node};
|
||||
export function Render_Dag({plan,frame=null,statistics=[],selected_node_id,on_select_node}:{plan:Gallery_Render_Plan;frame?:Gallery_Captured_Frame|null;statistics?:Gallery_Node_Statistics[];selected_node_id:number|null;on_select_node:(id:number)=>void}) {const source=useMemo(()=>build_dag_model(plan,frame,statistics,selected_node_id),[plan,frame,statistics,selected_node_id]);const [model,set_model]=useState<Dag_View_Model>(source);useEffect(()=>{let current=true;void layout_dag(source).then(value=>current&&set_model(value));return()=>{current=false;};},[source]);return <Box sx={{height:520,border:"1px solid rgba(132,179,206,.2)",borderRadius:2,overflow:"hidden"}}><ReactFlow nodes={model.nodes} edges={model.edges} nodeTypes={node_types} fitView nodesDraggable={false} nodesConnectable={false} onNodeClick={(_,node)=>on_select_node(Number(node.id))}><Background/><MiniMap/><Controls/></ReactFlow></Box>;}
|
||||
import {Background, Controls, MiniMap, ReactFlow} from "@xyflow/react";
|
||||
import {Box, CircularProgress} from "@mui/material";
|
||||
import {useEffect, useMemo, useState} from "react";
|
||||
import type {
|
||||
Gallery_Captured_Frame,
|
||||
Gallery_Node_Statistics,
|
||||
Gallery_Render_Plan,
|
||||
} from "../protocol/gallery_types";
|
||||
import {layout_dag} from "./dag_layout";
|
||||
import {build_dag_model} from "./dag_model";
|
||||
import {Dag_Node} from "./dag_node";
|
||||
import type {Dag_View_Model} from "./dag_types";
|
||||
|
||||
const node_types = {dag_node: Dag_Node};
|
||||
|
||||
export function Render_Dag({
|
||||
plan,
|
||||
frame = null,
|
||||
statistics = [],
|
||||
selected_node_id,
|
||||
on_select_node,
|
||||
}: {
|
||||
plan: Gallery_Render_Plan;
|
||||
frame?: Gallery_Captured_Frame | null;
|
||||
statistics?: Gallery_Node_Statistics[];
|
||||
selected_node_id: number | null;
|
||||
on_select_node: (id: number) => void;
|
||||
}) {
|
||||
const source = useMemo(
|
||||
() => build_dag_model(plan, frame, statistics, selected_node_id),
|
||||
[plan, frame, statistics, selected_node_id],
|
||||
);
|
||||
const layout_identity = useMemo(
|
||||
() => `${plan.nodes.map(node => node.node_id).join(",")}:${plan.edges.map(edge => `${edge.from}>${edge.to}`).join(",")}`,
|
||||
[plan],
|
||||
);
|
||||
const [layout, set_layout] = useState<{identity: string; model: Dag_View_Model} | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let current = true;
|
||||
void layout_dag(source).then(value => {
|
||||
if (current)
|
||||
set_layout({identity: layout_identity, model: value});
|
||||
});
|
||||
return () => {
|
||||
current = false;
|
||||
};
|
||||
// Selection and telemetry only decorate nodes; they must not restart ELK layout.
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [layout_identity]);
|
||||
|
||||
const laid_out = layout?.identity === layout_identity ? layout.model : null;
|
||||
const model = useMemo(() => {
|
||||
if (!laid_out)
|
||||
return null;
|
||||
const positions = new Map(laid_out.nodes.map(node => [node.id, node.position]));
|
||||
return {
|
||||
...source,
|
||||
nodes: source.nodes.map(node => ({
|
||||
...node,
|
||||
position: positions.get(node.id) ?? node.position,
|
||||
})),
|
||||
};
|
||||
}, [source, laid_out]);
|
||||
|
||||
return <Box sx={{height: 520, border: "1px solid rgba(132,179,206,.2)", borderRadius: 2, overflow: "hidden"}}>
|
||||
{!model
|
||||
? <Box sx={{height: "100%", display: "grid", placeItems: "center"}}><CircularProgress size={28}/></Box>
|
||||
: <ReactFlow
|
||||
key={layout_identity}
|
||||
nodes={model.nodes}
|
||||
edges={model.edges}
|
||||
nodeTypes={node_types}
|
||||
fitView
|
||||
onlyRenderVisibleElements
|
||||
nodesDraggable={false}
|
||||
nodesConnectable={false}
|
||||
onNodeClick={(_, node) => on_select_node(Number(node.id))}
|
||||
>
|
||||
<Background/>
|
||||
<MiniMap/>
|
||||
<Controls/>
|
||||
</ReactFlow>}
|
||||
</Box>;
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
import CloseIcon from "@mui/icons-material/Close";import RefreshIcon from "@mui/icons-material/Refresh";import RestartAltIcon from "@mui/icons-material/RestartAlt";
|
||||
import {Box,Drawer,IconButton,Stack,Tooltip,Typography} from "@mui/material";import {useState,useSyncExternalStore} from "react";import type {Gallery_Plot_Session} from "../session/gallery_plot_session";import {EMPTY_PLOT_SNAPSHOT} from "../session/gallery_plot_snapshot";import {Inspector_Tabs} from "./inspector_tabs";import {Controls_Panel} from "./controls_panel";import {Actions_Panel} from "./actions_panel";import {Observer_Panel} from "./observer_panel";import {Performance_Panel} from "./performance_panel";import {Capture_Panel} from "../capture/capture_panel";import {Render_Dag} from "../dag/render_dag";import {Empty_State} from "../common/empty_state";
|
||||
const empty_subscribe=()=>()=>undefined;const empty_snapshot=()=>EMPTY_PLOT_SNAPSHOT;
|
||||
export function Inspector_Drawer({open,session,active_tab,on_change_tab,on_close}:{open:boolean;session:Gallery_Plot_Session|null;active_tab:string;on_change_tab:(tab:string)=>void;on_close:()=>void}) {const snapshot=useSyncExternalStore(session?.subscribe??empty_subscribe,session?.get_snapshot??empty_snapshot,session?.get_snapshot??empty_snapshot);const [selected_node_id,set_selected_node_id]=useState<number|null>(null);let body=null;if(session){if(active_tab==="controls")body=<Controls_Panel controls={snapshot.controls} on_patch={(target,patch)=>session.patch(target,patch)}/>;else if(active_tab==="actions")body=<Actions_Panel actions={snapshot.actions} on_action={(action,argument)=>{session.action(action,argument);setTimeout(()=>session.request_frame(performance.now(),true),40);}}/>;else if(active_tab==="observer")body=<Observer_Panel telemetry={snapshot.telemetry}/>;else if(active_tab==="performance")body=<Performance_Panel telemetry={snapshot.telemetry}/>;else if(active_tab==="performance_capture")body=snapshot.performance_capture?<Capture_Panel capture={snapshot.performance_capture} on_capture={(action,count)=>{session.action(action,count);setTimeout(()=>session.request_frame(performance.now(),true),40);}}/>:<Empty_State message="尚无 Performance Capture 数据"/>;else if(active_tab==="render_plan")body=snapshot.render_plan?<Render_Dag plan={snapshot.render_plan} selected_node_id={selected_node_id} on_select_node={set_selected_node_id}/>:<Empty_State message="场景尚未编译 Render Plan"/>;}return <Drawer anchor="right" open={open} onClose={on_close} PaperProps={{sx:{width:{xs:"100%",md:"min(900px,72vw)"}}}}><Stack direction="row" alignItems="center" sx={{p:2,pb:1}}><Box flex={1}><Typography variant="overline" color="primary.main">{session&&`${session.frame_mode.strategy} / ${session.gallery_case.component}`}</Typography><Typography variant="h5">{session?.gallery_case.title??"Inspector"}</Typography></Box><Tooltip title="重置监测"><IconButton onClick={()=>session?.reset_monitoring()}><RestartAltIcon/></IconButton></Tooltip><Tooltip title="刷新当前页"><IconButton onClick={()=>session?.refresh()}><RefreshIcon/></IconButton></Tooltip><IconButton onClick={on_close}><CloseIcon/></IconButton></Stack><Inspector_Tabs active_tab={active_tab} on_change={on_change_tab}/><Box sx={{p:2,overflow:"auto",flex:1}}>{body}</Box></Drawer>;}
|
||||
@@ -0,0 +1,90 @@
|
||||
import CloseIcon from "@mui/icons-material/Close";
|
||||
import RefreshIcon from "@mui/icons-material/Refresh";
|
||||
import RestartAltIcon from "@mui/icons-material/RestartAlt";
|
||||
import {Box, IconButton, Paper, Stack, Tooltip, Typography} from "@mui/material";
|
||||
import {useEffect, useState, useSyncExternalStore} from "react";
|
||||
import {Capture_Panel} from "../capture/capture_panel";
|
||||
import {Empty_State} from "../common/empty_state";
|
||||
import {Render_Dag} from "../dag/render_dag";
|
||||
import type {Gallery_Plot_Session} from "../session/gallery_plot_session";
|
||||
import {Actions_Panel} from "./actions_panel";
|
||||
import {Controls_Panel} from "./controls_panel";
|
||||
import {Inspector_Tabs} from "./inspector_tabs";
|
||||
import {Observer_Panel} from "./observer_panel";
|
||||
import {Performance_Panel} from "./performance_panel";
|
||||
|
||||
export function Inspector_Panel({
|
||||
session,
|
||||
active_tab,
|
||||
on_change_tab,
|
||||
on_close,
|
||||
}: {
|
||||
session: Gallery_Plot_Session;
|
||||
active_tab: string;
|
||||
on_change_tab: (tab: string) => void;
|
||||
on_close: () => void;
|
||||
}) {
|
||||
const snapshot = useSyncExternalStore(
|
||||
session.subscribe,
|
||||
session.get_snapshot,
|
||||
session.get_snapshot,
|
||||
);
|
||||
const [selected_node_id, set_selected_node_id] = useState<number | null>(null);
|
||||
|
||||
useEffect(() => set_selected_node_id(null), [session]);
|
||||
|
||||
let body = null;
|
||||
if (active_tab === "controls")
|
||||
body = <Controls_Panel controls={snapshot.controls} on_patch={(target, patch) => session.patch(target, patch)}/>;
|
||||
else if (active_tab === "actions")
|
||||
body = <Actions_Panel actions={snapshot.actions} on_action={(action, argument) => {
|
||||
session.action(action, argument);
|
||||
setTimeout(() => session.request_frame(performance.now(), true), 40);
|
||||
}}/>;
|
||||
else if (active_tab === "observer")
|
||||
body = <Observer_Panel telemetry={snapshot.telemetry}/>;
|
||||
else if (active_tab === "performance")
|
||||
body = <Performance_Panel telemetry={snapshot.telemetry}/>;
|
||||
else if (active_tab === "performance_capture")
|
||||
body = snapshot.performance_capture
|
||||
? <Capture_Panel capture={snapshot.performance_capture} on_capture={(action, count) => {
|
||||
session.action(action, count);
|
||||
setTimeout(() => session.request_frame(performance.now(), true), 40);
|
||||
}}/>
|
||||
: <Empty_State message="尚无 Performance Capture 数据"/>;
|
||||
else if (active_tab === "render_plan")
|
||||
body = snapshot.render_plan
|
||||
? <Render_Dag
|
||||
plan={snapshot.render_plan}
|
||||
selected_node_id={selected_node_id}
|
||||
on_select_node={set_selected_node_id}
|
||||
/>
|
||||
: <Empty_State message="场景尚未编译 Render Plan"/>;
|
||||
|
||||
return <Paper
|
||||
square
|
||||
elevation={0}
|
||||
data-testid="inspector-panel"
|
||||
sx={{height: "100%", minWidth: 0, display: "flex", flexDirection: "column", overflow: "hidden"}}
|
||||
>
|
||||
<Stack direction="row" alignItems="center" sx={{p: 2, pb: 1}}>
|
||||
<Box minWidth={0} flex={1}>
|
||||
<Typography variant="overline" color="primary.main" noWrap>
|
||||
{`${session.frame_mode.strategy} / ${session.gallery_case.component}`}
|
||||
</Typography>
|
||||
<Typography variant="h5" noWrap>{session.gallery_case.title}</Typography>
|
||||
</Box>
|
||||
<Tooltip title="重置监测">
|
||||
<IconButton onClick={() => session.reset_monitoring()}><RestartAltIcon/></IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title="刷新当前页">
|
||||
<IconButton onClick={() => session.refresh()}><RefreshIcon/></IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title="隐藏操作面板">
|
||||
<IconButton aria-label="隐藏操作面板" onClick={on_close}><CloseIcon/></IconButton>
|
||||
</Tooltip>
|
||||
</Stack>
|
||||
<Inspector_Tabs active_tab={active_tab} on_change={on_change_tab}/>
|
||||
<Box sx={{p: 2, overflow: "auto", flex: 1, minHeight: 0}}>{body}</Box>
|
||||
</Paper>;
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
{"root":["./src/app.tsx","./src/main.tsx","./src/theme.ts","./src/capture/capture_frames.tsx","./src/capture/capture_panel.tsx","./src/capture/capture_sessions.tsx","./src/capture/capture_toolbar.tsx","./src/capture/frame_summary.tsx","./src/capture/node_detail.tsx","./src/capture/plan_comparison.tsx","./src/capture/worker_timeline.tsx","./src/common/copy_button.tsx","./src/common/empty_state.tsx","./src/common/json_viewer.tsx","./src/common/metric_grid.tsx","./src/common/metric_value.tsx","./src/common/status_chip.tsx","./src/dag/dag_layout.ts","./src/dag/dag_legend.tsx","./src/dag/dag_model.ts","./src/dag/dag_node.tsx","./src/dag/dag_types.ts","./src/dag/render_dag.tsx","./src/gallery/category_filter.tsx","./src/gallery/frame_mode_tabs.tsx","./src/gallery/gallery_page.tsx","./src/gallery/gallery_summary.tsx","./src/gallery/gallery_toolbar.tsx","./src/gallery/plot_card.tsx","./src/hooks/use_element_size.ts","./src/hooks/use_gallery_catalog.ts","./src/hooks/use_plot_session.ts","./src/inspector/actions_panel.tsx","./src/inspector/control_field.tsx","./src/inspector/controls_panel.tsx","./src/inspector/inspector_drawer.tsx","./src/inspector/inspector_tabs.tsx","./src/inspector/observer_panel.tsx","./src/inspector/performance_panel.tsx","./src/plot/kernel_observer_summary.tsx","./src/plot/performance_strip.tsx","./src/plot/plot_canvas.tsx","./src/plot/plot_status.tsx","./src/protocol/format.ts","./src/protocol/gallery_descriptor.ts","./src/protocol/gallery_messages.ts","./src/protocol/gallery_parser.ts","./src/protocol/gallery_types.ts","./src/protocol/pixel_frame.ts","./src/runtime/client_performance.ts","./src/runtime/frame_request_controller.ts","./src/runtime/pixel_presenter.ts","./src/session/gallery_plot_session.ts","./src/session/gallery_plot_snapshot.ts","./src/transport/catalog_loader.ts","./src/transport/gallery_socket.ts","./tests/setup.ts","./tests/components/control_field.test.tsx","./tests/dag/dag_model.test.ts","./tests/e2e/gallery.spec.ts","./tests/protocol/gallery_parser.test.ts","./tests/protocol/pixel_frame.test.ts","./tests/runtime/client_performance.test.ts","./tests/runtime/frame_request_controller.test.ts","./vite.config.ts"],"version":"5.9.3"}
|
||||
{"root":["./src/app.tsx","./src/main.tsx","./src/theme.ts","./src/capture/capture_frames.tsx","./src/capture/capture_panel.tsx","./src/capture/capture_sessions.tsx","./src/capture/capture_toolbar.tsx","./src/capture/frame_summary.tsx","./src/capture/node_detail.tsx","./src/capture/plan_comparison.tsx","./src/capture/worker_timeline.tsx","./src/common/copy_button.tsx","./src/common/empty_state.tsx","./src/common/json_viewer.tsx","./src/common/metric_grid.tsx","./src/common/metric_value.tsx","./src/common/status_chip.tsx","./src/dag/dag_layout.ts","./src/dag/dag_legend.tsx","./src/dag/dag_model.ts","./src/dag/dag_node.tsx","./src/dag/dag_types.ts","./src/dag/render_dag.tsx","./src/gallery/category_filter.tsx","./src/gallery/frame_mode_tabs.tsx","./src/gallery/gallery_page.tsx","./src/gallery/gallery_summary.tsx","./src/gallery/gallery_toolbar.tsx","./src/gallery/plot_card.tsx","./src/hooks/use_element_size.ts","./src/hooks/use_gallery_catalog.ts","./src/hooks/use_plot_session.ts","./src/inspector/actions_panel.tsx","./src/inspector/control_field.tsx","./src/inspector/controls_panel.tsx","./src/inspector/inspector_panel.tsx","./src/inspector/inspector_tabs.tsx","./src/inspector/observer_panel.tsx","./src/inspector/performance_panel.tsx","./src/plot/kernel_observer_summary.tsx","./src/plot/performance_strip.tsx","./src/plot/plot_canvas.tsx","./src/plot/plot_status.tsx","./src/protocol/format.ts","./src/protocol/gallery_descriptor.ts","./src/protocol/gallery_messages.ts","./src/protocol/gallery_parser.ts","./src/protocol/gallery_types.ts","./src/protocol/pixel_frame.ts","./src/runtime/client_performance.ts","./src/runtime/frame_request_controller.ts","./src/runtime/pixel_presenter.ts","./src/session/gallery_plot_session.ts","./src/session/gallery_plot_snapshot.ts","./src/transport/catalog_loader.ts","./src/transport/gallery_socket.ts","./tests/setup.ts","./tests/components/control_field.test.tsx","./tests/dag/dag_model.test.ts","./tests/e2e/gallery.spec.ts","./tests/protocol/gallery_parser.test.ts","./tests/protocol/pixel_frame.test.ts","./tests/runtime/client_performance.test.ts","./tests/runtime/frame_request_controller.test.ts","./vite.config.ts"],"version":"5.9.3"}
|
||||
Reference in New Issue
Block a user