diff --git a/packages/trace-viewer/src/index.tsx b/packages/trace-viewer/src/index.tsx index a737d9017f..671a2235ae 100644 --- a/packages/trace-viewer/src/index.tsx +++ b/packages/trace-viewer/src/index.tsx @@ -19,11 +19,14 @@ import { applyTheme } from '@web/theme'; import '@web/third_party/vscode/codicon.css'; import * as ReactDOM from 'react-dom/client'; import { WorkbenchLoader } from './ui/workbenchLoader'; +import { LiveWorkbenchLoader } from './ui/liveWorkbenchLoader'; (async () => { + const queryParams = new URLSearchParams(window.location.search); + applyTheme(); if (window.location.protocol !== 'file:') { - if (window.location.href.includes('isUnderTest=true')) + if (queryParams.get('isUnderTest') === 'true') await new Promise(f => setTimeout(f, 1000)); if (!navigator.serviceWorker) throw new Error(`Service workers are not supported.\nMake sure to serve the Trace Viewer (${window.location}) via HTTPS or localhost.`); @@ -38,5 +41,8 @@ import { WorkbenchLoader } from './ui/workbenchLoader'; setInterval(function() { fetch('ping'); }, 10000); } - ReactDOM.createRoot(document.querySelector('#root')!).render(); + const trace = queryParams.get('trace'); + const traceIsLive = trace?.endsWith('.json'); + const workbench = traceIsLive ? : ; + ReactDOM.createRoot(document.querySelector('#root')!).render(workbench); })(); diff --git a/packages/trace-viewer/src/ui/liveWorkbenchLoader.tsx b/packages/trace-viewer/src/ui/liveWorkbenchLoader.tsx new file mode 100644 index 0000000000..ba44a66c30 --- /dev/null +++ b/packages/trace-viewer/src/ui/liveWorkbenchLoader.tsx @@ -0,0 +1,61 @@ +/* + Copyright (c) Microsoft Corporation. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import * as React from 'react'; +import { MultiTraceModel } from './modelUtil'; +import './workbenchLoader.css'; +import { Workbench } from './workbench'; + +import type { ContextEntry } from '../types/entries'; + +export const LiveWorkbenchLoader: React.FC<{ traceJson: string }> = ({ traceJson }) => { + const [model, setModel] = React.useState(undefined); + const [counter, setCounter] = React.useState(0); + const pollTimer = React.useRef(null); + + React.useEffect(() => { + if (pollTimer.current) + clearTimeout(pollTimer.current); + + // Start polling running test. + pollTimer.current = setTimeout(async () => { + try { + const model = await loadSingleTraceFile(traceJson); + setModel(model); + } catch { + const model = new MultiTraceModel([]); + setModel(model); + } finally { + setCounter(counter + 1); + } + }, 500); + return () => { + if (pollTimer.current) + clearTimeout(pollTimer.current); + }; + }, [traceJson, counter]); + + return ; +}; + +async function loadSingleTraceFile(traceJson: string): Promise { + const params = new URLSearchParams(); + params.set('trace', traceJson); + params.set('limit', '1'); + const response = await fetch(`contexts?${params.toString()}`); + const contextEntries = await response.json() as ContextEntry[]; + return new MultiTraceModel(contextEntries); +}