import './index.less'; import { ConfigProvider, message, Upload, Button } from 'antd'; import type { UploadProps } from 'antd'; import { useEffect, useRef, useState } from 'react'; import { Helmet } from '@modern-js/runtime/head'; import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels'; import { GroupedActionDump } from '@midscene/core'; import Timeline from './component/timeline'; import DetailPanel from './component/detail-panel'; import Logo from './component/assets/logo-plain.svg'; import GlobalHoverPreview from './component/global-hover-preview'; import { useExecutionDump } from '@/component/store'; import DetailSide from '@/component/detail-side'; import Sidebar from '@/component/sidebar'; const { Dragger } = Upload; let globalRenderCount = 1; export function Visualizer(props: { hideLogo?: boolean; logoAction?: () => void; dump?: GroupedActionDump[]; }): JSX.Element { const { dump } = props; const executionDump = useExecutionDump((store) => store.dump); const setGroupedDump = useExecutionDump((store) => store.setGroupedDump); const reset = useExecutionDump((store) => store.reset); const [mainLayoutChangeFlag, setMainLayoutChangeFlag] = useState(0); const mainLayoutChangedRef = useRef(false); useEffect(() => { if (dump) { setGroupedDump(dump); } return () => { reset(); }; }, []); useEffect(() => { const onResize = () => { setMainLayoutChangeFlag((prev) => prev + 1); }; window.addEventListener('resize', onResize); return () => { window.removeEventListener('resize', onResize); }; }, []); const uploadProps: UploadProps = { name: 'file', multiple: false, capture: false, customRequest: () => { // noop }, beforeUpload(file) { const ifValidFile = file.name.endsWith('web-dump.json'); // || file.name.endsWith('.insight.json'); // 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); // setMainLayoutChangeFlag((prev) => prev + 1); 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 = (

Click or drag the{' '} .web-dump.json {' '} {/* or{' '} .actions.json {' '} */} file into this area.

The latest dump file is usually placed in{' '} ./midscene_run/

All data will be processed locally by the browser. No data will be sent to the server.

); // dump } else { mainContent = ( { if (!mainLayoutChangedRef.current) { setMainLayoutChangeFlag((prev) => prev + 1); } }} > { if (mainLayoutChangedRef.current && !isChanging) { // not changing anymore setMainLayoutChangeFlag((prev) => prev + 1); } mainLayoutChangedRef.current = isChanging; }} />
); } const [containerHeight, setContainerHeight] = useState('100%'); useEffect(() => { const ifInRspressPage = document.querySelector('.rspress-nav'); // modify rspress theme const navHeightKey = '--rp-nav-height'; const originalNavHeight = getComputedStyle(document.documentElement).getPropertyValue(navHeightKey); if (ifInRspressPage) { const newNavHeight = '42px'; setContainerHeight(`calc(100vh - ${newNavHeight})`); document.documentElement.style.setProperty(navHeightKey, newNavHeight); } // Cleanup function to revert the change return () => { if (ifInRspressPage) { document.documentElement.style.setProperty(navHeightKey, originalNavHeight); } }; }, []); useEffect(() => { return () => { globalRenderCount += 1; }; }, []); return ( MidScene.js - Visualization Tool
{mainContent}
); } export default Visualizer;