2024-07-23 16:25:11 +08:00
|
|
|
import './index.less';
|
2024-07-25 10:47:02 +08:00
|
|
|
import { ConfigProvider, message, Upload, Button } from 'antd';
|
2024-07-23 16:25:11 +08:00
|
|
|
import type { UploadProps } from 'antd';
|
2024-07-25 10:47:02 +08:00
|
|
|
import { useEffect, useRef, useState } from 'react';
|
2024-07-23 16:25:11 +08:00
|
|
|
import { Helmet } from '@modern-js/runtime/head';
|
2024-07-25 10:47:02 +08:00
|
|
|
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
|
2024-07-23 16:25:11 +08:00
|
|
|
import Timeline from './component/timeline';
|
|
|
|
import DetailPanel from './component/detail-panel';
|
2024-07-24 10:32:27 +08:00
|
|
|
import logo from './component/assets/logo-plain.svg';
|
2024-07-25 10:47:02 +08:00
|
|
|
import GlobalHoverPreview from './component/global-hover-preview';
|
2024-07-23 16:25:11 +08:00
|
|
|
import { useExecutionDump, useInsightDump } from '@/component/store';
|
|
|
|
import DetailSide from '@/component/detail-side';
|
|
|
|
import Sidebar from '@/component/sidebar';
|
|
|
|
|
|
|
|
const { Dragger } = Upload;
|
2024-07-28 08:49:57 +08:00
|
|
|
const Index = (props: { hideLogo?: boolean }): JSX.Element => {
|
2024-07-23 16:25:11 +08:00
|
|
|
const executionDump = useExecutionDump((store) => store.dump);
|
|
|
|
const setGroupedDump = useExecutionDump((store) => store.setGroupedDump);
|
|
|
|
const reset = useExecutionDump((store) => store.reset);
|
2024-07-25 10:47:02 +08:00
|
|
|
const [mainLayoutChangeFlag, setMainLayoutChangeFlag] = useState(0);
|
|
|
|
const mainLayoutChangedRef = useRef(false);
|
2024-07-23 16:25:11 +08:00
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
return () => {
|
|
|
|
reset();
|
|
|
|
};
|
|
|
|
}, []);
|
|
|
|
|
2024-07-25 10:47:02 +08:00
|
|
|
useEffect(() => {
|
|
|
|
const onResize = () => {
|
|
|
|
setMainLayoutChangeFlag((prev) => prev + 1);
|
|
|
|
};
|
|
|
|
window.addEventListener('resize', onResize);
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
window.removeEventListener('resize', onResize);
|
|
|
|
};
|
|
|
|
}, []);
|
|
|
|
|
2024-07-23 16:25:11 +08:00
|
|
|
// TODO
|
|
|
|
// const loadInsightDump = (dump: InsightDump) => {
|
|
|
|
// console.log('will convert insight dump to execution dump');
|
|
|
|
// const data = insightDumpToExecutionDump(dump);
|
|
|
|
// console.log(data);
|
|
|
|
|
|
|
|
// setExecutionDump(data);
|
|
|
|
// };
|
|
|
|
|
|
|
|
const uploadProps: UploadProps = {
|
|
|
|
name: 'file',
|
|
|
|
multiple: false,
|
2024-07-24 10:32:27 +08:00
|
|
|
capture: false,
|
2024-07-23 16:25:11 +08:00
|
|
|
customRequest: () => {
|
|
|
|
// noop
|
|
|
|
},
|
|
|
|
beforeUpload(file) {
|
2024-07-25 13:40:46 +08:00
|
|
|
const ifValidFile = file.name.endsWith('web-dump.json'); // || file.name.endsWith('.insight.json');
|
2024-07-23 16:25:11 +08:00
|
|
|
// const ifActionFile =
|
|
|
|
// file.name.endsWith('.actions.json') || /_force_regard_as_action_file/.test(location.href);
|
|
|
|
if (!ifValidFile) {
|
|
|
|
message.error('invalid file extension');
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const reader = new FileReader();
|
|
|
|
reader.readAsText(file);
|
|
|
|
reader.onload = (e) => {
|
|
|
|
const result = e.target?.result;
|
|
|
|
if (typeof result === 'string') {
|
|
|
|
try {
|
|
|
|
const data = JSON.parse(result);
|
2024-07-25 10:47:02 +08:00
|
|
|
// setMainLayoutChangeFlag((prev) => prev + 1);
|
2024-07-23 16:25:11 +08:00
|
|
|
setGroupedDump(data);
|
|
|
|
// if (ifActionFile) {
|
|
|
|
// } else {
|
|
|
|
// loadInsightDump(data);
|
|
|
|
// }
|
|
|
|
} catch (e: any) {
|
|
|
|
console.error(e);
|
|
|
|
message.error('failed to parse dump data', e.message);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
message.error('Invalid dump file');
|
|
|
|
}
|
|
|
|
};
|
|
|
|
return false;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
const loadTasksDemo = () => {
|
|
|
|
// setExecutionDump(actionDemo);
|
|
|
|
// message.info('Your are viewing the demo data.');
|
|
|
|
};
|
|
|
|
|
|
|
|
const loadInsightDemo = () => {
|
|
|
|
// loadInsightDump(InsightDemo);
|
|
|
|
// message.info('Your are viewing the demo data.');
|
|
|
|
};
|
|
|
|
|
|
|
|
let mainContent: JSX.Element;
|
|
|
|
if (!executionDump) {
|
|
|
|
mainContent = (
|
|
|
|
<div className="main-right uploader-wrapper">
|
|
|
|
<Dragger className="uploader" {...uploadProps}>
|
|
|
|
<p className="ant-upload-drag-icon">
|
2024-07-24 10:32:27 +08:00
|
|
|
<img src={logo} alt="Logo" style={{ width: 100, height: 100, margin: 'auto' }} />
|
2024-07-23 16:25:11 +08:00
|
|
|
</p>
|
|
|
|
<p className="ant-upload-text">
|
|
|
|
Click or drag the{' '}
|
|
|
|
<b>
|
2024-07-25 13:40:46 +08:00
|
|
|
<i>.web-dump.json</i>
|
2024-07-23 16:25:11 +08:00
|
|
|
</b>{' '}
|
2024-07-25 10:47:02 +08:00
|
|
|
{/* or{' '}
|
2024-07-23 16:25:11 +08:00
|
|
|
<b>
|
|
|
|
<i>.actions.json</i>
|
2024-07-25 10:47:02 +08:00
|
|
|
</b>{' '} */}
|
2024-07-23 16:25:11 +08:00
|
|
|
file into this area.
|
|
|
|
</p>
|
|
|
|
<p className="ant-upload-text">
|
|
|
|
The latest dump file is usually placed in{' '}
|
|
|
|
<b>
|
|
|
|
<i>./midscene_run/</i>
|
|
|
|
</b>
|
|
|
|
</p>
|
|
|
|
<p className="ant-upload-text">
|
|
|
|
All data will be processed locally by the browser. No data will be sent to the server.
|
|
|
|
</p>
|
|
|
|
</Dragger>
|
|
|
|
<div className="demo-loader">
|
|
|
|
<Button type="link" onClick={loadTasksDemo}>
|
|
|
|
Load Tasks Demo
|
|
|
|
</Button>
|
|
|
|
<Button type="link" onClick={loadInsightDemo}>
|
|
|
|
Load Insight Demo
|
|
|
|
</Button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
|
|
|
|
// dump
|
|
|
|
} else {
|
|
|
|
mainContent = (
|
2024-07-25 10:47:02 +08:00
|
|
|
<PanelGroup
|
|
|
|
autoSaveId="main-page-layout"
|
|
|
|
direction="horizontal"
|
|
|
|
onLayout={() => {
|
|
|
|
if (!mainLayoutChangedRef.current) {
|
|
|
|
setMainLayoutChangeFlag((prev) => prev + 1);
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
>
|
2024-07-28 08:49:57 +08:00
|
|
|
<Panel maxSize={95} defaultSize={20}>
|
|
|
|
<Sidebar hideLogo={props?.hideLogo} />
|
2024-07-25 10:47:02 +08:00
|
|
|
</Panel>
|
|
|
|
<PanelResizeHandle
|
|
|
|
onDragging={(isChanging) => {
|
|
|
|
if (mainLayoutChangedRef.current && !isChanging) {
|
|
|
|
// not changing anymore
|
|
|
|
setMainLayoutChangeFlag((prev) => prev + 1);
|
|
|
|
}
|
|
|
|
mainLayoutChangedRef.current = isChanging;
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
<Panel defaultSize={80} maxSize={95}>
|
|
|
|
<div className="main-right">
|
|
|
|
<Timeline key={mainLayoutChangeFlag} />
|
|
|
|
<div className="main-content">
|
|
|
|
<PanelGroup autoSaveId="page-detail-layout" direction="horizontal">
|
|
|
|
<Panel maxSize={95}>
|
|
|
|
<div className="main-side">
|
|
|
|
<DetailSide />
|
|
|
|
</div>
|
|
|
|
</Panel>
|
|
|
|
<PanelResizeHandle />
|
2024-07-23 16:25:11 +08:00
|
|
|
|
2024-07-25 10:47:02 +08:00
|
|
|
<Panel defaultSize={75} maxSize={95}>
|
|
|
|
<div className="main-canvas-container">
|
|
|
|
<DetailPanel />
|
|
|
|
</div>
|
|
|
|
</Panel>
|
|
|
|
</PanelGroup>
|
|
|
|
</div>
|
2024-07-23 16:25:11 +08:00
|
|
|
</div>
|
2024-07-25 10:47:02 +08:00
|
|
|
</Panel>
|
|
|
|
</PanelGroup>
|
2024-07-23 16:25:11 +08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<ConfigProvider
|
|
|
|
theme={{
|
|
|
|
components: {
|
|
|
|
Layout: {
|
|
|
|
headerHeight: 60,
|
|
|
|
headerPadding: '0 30px',
|
|
|
|
headerBg: '#FFF',
|
|
|
|
bodyBg: '#FFF',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<Helmet>
|
|
|
|
<title>MidScene.js - Visualization Tool</title>
|
|
|
|
</Helmet>
|
2024-07-25 10:47:02 +08:00
|
|
|
<div className="page-container">{mainContent}</div>
|
|
|
|
<GlobalHoverPreview />
|
2024-07-23 16:25:11 +08:00
|
|
|
</ConfigProvider>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
export default Index;
|