base 64 encode/decode urls with s3
This commit is contained in:
parent
ee8208beda
commit
7f25d1ac1a
|
|
@ -23,7 +23,7 @@
|
|||
navigator.serviceWorker.register('sw.bundle.js');
|
||||
if (!navigator.serviceWorker.controller)
|
||||
await new Promise(f => navigator.serviceWorker.oncontrollerchange = f);
|
||||
const traceUrl = new URL(location.href).searchParams.get('trace');
|
||||
let traceUrl = new URL(location.href).searchParams.get('trace');
|
||||
const params = new URLSearchParams();
|
||||
params.set('trace', traceUrl);
|
||||
await fetch('contexts?' + params.toString()).then(r => r.json());
|
||||
|
|
|
|||
|
|
@ -18,7 +18,11 @@ import { splitProgress } from './progress';
|
|||
import { unwrapPopoutUrl } from './snapshotRenderer';
|
||||
import { SnapshotServer } from './snapshotServer';
|
||||
import { TraceModel } from './traceModel';
|
||||
import { FetchTraceModelBackend, TraceViewerServer, ZipTraceModelBackend } from './traceModelBackends';
|
||||
import {
|
||||
FetchTraceModelBackend,
|
||||
TraceViewerServer,
|
||||
ZipTraceModelBackend,
|
||||
} from './traceModelBackends';
|
||||
import { TraceVersionError } from './traceModernizer';
|
||||
|
||||
// @ts-ignore
|
||||
|
|
@ -34,20 +38,47 @@ self.addEventListener('activate', function(event: any) {
|
|||
|
||||
const scopePath = new URL(self.registration.scope).pathname;
|
||||
|
||||
const loadedTraces = new Map<string, { traceModel: TraceModel, snapshotServer: SnapshotServer }>();
|
||||
const loadedTraces = new Map<
|
||||
string,
|
||||
{ traceModel: TraceModel; snapshotServer: SnapshotServer }
|
||||
>();
|
||||
|
||||
const clientIdToTraceUrls = new Map<string, { limit: number | undefined, traceUrls: Set<string>, traceViewerServer: TraceViewerServer }>();
|
||||
const clientIdToTraceUrls = new Map<
|
||||
string,
|
||||
{
|
||||
limit: number | undefined;
|
||||
traceUrls: Set<string>;
|
||||
traceViewerServer: TraceViewerServer;
|
||||
}
|
||||
>();
|
||||
|
||||
async function loadTrace(traceUrl: string, traceFileName: string | null, client: any | undefined, limit: number | undefined, progress: (done: number, total: number) => undefined): Promise<TraceModel> {
|
||||
async function loadTrace(
|
||||
traceUrl: string,
|
||||
traceFileName: string | null,
|
||||
client: any | undefined,
|
||||
limit: number | undefined,
|
||||
progress: (done: number, total: number) => undefined
|
||||
): Promise<TraceModel> {
|
||||
await gc();
|
||||
const clientId = client?.id ?? '';
|
||||
let data = clientIdToTraceUrls.get(clientId);
|
||||
if (!data) {
|
||||
let traceViewerServerBaseUrl = new URL('../', client?.url ?? self.registration.scope);
|
||||
if (traceViewerServerBaseUrl.searchParams.has('server'))
|
||||
traceViewerServerBaseUrl = new URL(traceViewerServerBaseUrl.searchParams.get('server')!, traceViewerServerBaseUrl);
|
||||
let traceViewerServerBaseUrl = new URL(
|
||||
'../',
|
||||
client?.url ?? self.registration.scope
|
||||
);
|
||||
if (traceViewerServerBaseUrl.searchParams.has('server')) {
|
||||
traceViewerServerBaseUrl = new URL(
|
||||
traceViewerServerBaseUrl.searchParams.get('server')!,
|
||||
traceViewerServerBaseUrl
|
||||
);
|
||||
}
|
||||
|
||||
data = { limit, traceUrls: new Set(), traceViewerServer: new TraceViewerServer(traceViewerServerBaseUrl) };
|
||||
data = {
|
||||
limit,
|
||||
traceUrls: new Set(),
|
||||
traceViewerServer: new TraceViewerServer(traceViewerServerBaseUrl),
|
||||
};
|
||||
clientIdToTraceUrls.set(clientId, data);
|
||||
}
|
||||
data.traceUrls.add(traceUrl);
|
||||
|
|
@ -55,21 +86,49 @@ async function loadTrace(traceUrl: string, traceFileName: string | null, client:
|
|||
const traceModel = new TraceModel();
|
||||
try {
|
||||
// Allow 10% to hop from sw to page.
|
||||
const [fetchProgress, unzipProgress] = splitProgress(progress, [0.5, 0.4, 0.1]);
|
||||
const backend = traceUrl.endsWith('json') ? new FetchTraceModelBackend(traceUrl, data.traceViewerServer) : new ZipTraceModelBackend(traceUrl, data.traceViewerServer, fetchProgress);
|
||||
const [fetchProgress, unzipProgress] = splitProgress(
|
||||
progress,
|
||||
[0.5, 0.4, 0.1]
|
||||
);
|
||||
const backend = traceUrl.endsWith('json')
|
||||
? new FetchTraceModelBackend(traceUrl, data.traceViewerServer)
|
||||
: new ZipTraceModelBackend(
|
||||
traceUrl,
|
||||
data.traceViewerServer,
|
||||
fetchProgress
|
||||
);
|
||||
await traceModel.load(backend, unzipProgress);
|
||||
} catch (error: any) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
if (error?.message?.includes('Cannot find .trace file') && await traceModel.hasEntry('index.html'))
|
||||
throw new Error('Could not load trace. Did you upload a Playwright HTML report instead? Make sure to extract the archive first and then double-click the index.html file or put it on a web server.');
|
||||
if (error instanceof TraceVersionError)
|
||||
throw new Error(`Could not load trace from ${traceFileName || traceUrl}. ${error.message}`);
|
||||
if (traceFileName)
|
||||
throw new Error(`Could not load trace from ${traceFileName}. Make sure to upload a valid Playwright trace.`);
|
||||
throw new Error(`Could not load trace from ${traceUrl}. Make sure a valid Playwright Trace is accessible over this url.`);
|
||||
if (
|
||||
error?.message?.includes('Cannot find .trace file') &&
|
||||
(await traceModel.hasEntry('index.html'))
|
||||
) {
|
||||
throw new Error(
|
||||
'Could not load trace. Did you upload a Playwright HTML report instead? Make sure to extract the archive first and then double-click the index.html file or put it on a web server.'
|
||||
);
|
||||
}
|
||||
const snapshotServer = new SnapshotServer(traceModel.storage(), sha1 => traceModel.resourceForSha1(sha1));
|
||||
if (error instanceof TraceVersionError) {
|
||||
throw new Error(
|
||||
`Could not load trace from ${traceFileName || traceUrl}. ${
|
||||
error.message
|
||||
}`
|
||||
);
|
||||
}
|
||||
if (traceFileName) {
|
||||
throw new Error(
|
||||
`Could not load trace from ${traceFileName}. Make sure to upload a valid Playwright trace.`
|
||||
);
|
||||
}
|
||||
throw new Error(
|
||||
`Could not load trace from ${traceUrl}. Make sure a valid Playwright Trace is accessible over this url.`
|
||||
);
|
||||
}
|
||||
const snapshotServer = new SnapshotServer(traceModel.storage(), sha1 =>
|
||||
traceModel.resourceForSha1(sha1)
|
||||
);
|
||||
|
||||
loadedTraces.set(traceUrl, { traceModel, snapshotServer });
|
||||
return traceModel;
|
||||
}
|
||||
|
|
@ -98,28 +157,43 @@ async function doFetch(event: FetchEvent): Promise<Response> {
|
|||
return new Response(null, { status: 200 });
|
||||
}
|
||||
|
||||
const traceUrl = url.searchParams.get('trace');
|
||||
let traceUrl = '';
|
||||
try {
|
||||
traceUrl = atob(url.searchParams.get('trace') ?? '');
|
||||
} catch (error) {
|
||||
traceUrl = url.searchParams.get('trace') ?? '';
|
||||
}
|
||||
|
||||
|
||||
if (relativePath === '/contexts') {
|
||||
try {
|
||||
const limit = url.searchParams.has('limit') ? +url.searchParams.get('limit')! : undefined;
|
||||
const traceModel = await loadTrace(traceUrl!, url.searchParams.get('traceFileName'), client, limit, (done: number, total: number) => {
|
||||
const limit = url.searchParams.has('limit')
|
||||
? +url.searchParams.get('limit')!
|
||||
: undefined;
|
||||
const traceModel = await loadTrace(
|
||||
traceUrl!,
|
||||
url.searchParams.get('traceFileName'),
|
||||
client,
|
||||
limit,
|
||||
(done: number, total: number) => {
|
||||
client.postMessage({ method: 'progress', params: { done, total } });
|
||||
});
|
||||
}
|
||||
);
|
||||
return new Response(JSON.stringify(traceModel!.contextEntries), {
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
} catch (error: any) {
|
||||
return new Response(JSON.stringify({ error: error?.message }), {
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (relativePath.startsWith('/snapshotInfo/')) {
|
||||
const { snapshotServer } = loadedTraces.get(traceUrl!) || {};
|
||||
|
||||
if (!snapshotServer)
|
||||
return new Response(null, { status: 404 });
|
||||
return snapshotServer.serveSnapshotInfo(relativePath, url.searchParams);
|
||||
|
|
@ -129,9 +203,17 @@ async function doFetch(event: FetchEvent): Promise<Response> {
|
|||
const { snapshotServer } = loadedTraces.get(traceUrl!) || {};
|
||||
if (!snapshotServer)
|
||||
return new Response(null, { status: 404 });
|
||||
const response = snapshotServer.serveSnapshot(relativePath, url.searchParams, url.href);
|
||||
if (isDeployedAsHttps)
|
||||
response.headers.set('Content-Security-Policy', 'upgrade-insecure-requests');
|
||||
const response = snapshotServer.serveSnapshot(
|
||||
relativePath,
|
||||
url.searchParams,
|
||||
url.href
|
||||
);
|
||||
if (isDeployedAsHttps) {
|
||||
response.headers.set(
|
||||
'Content-Security-Policy',
|
||||
'upgrade-insecure-requests'
|
||||
);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
|
|
@ -139,7 +221,10 @@ async function doFetch(event: FetchEvent): Promise<Response> {
|
|||
const { snapshotServer } = loadedTraces.get(traceUrl!) || {};
|
||||
if (!snapshotServer)
|
||||
return new Response(null, { status: 404 });
|
||||
return snapshotServer.serveClosestScreenshot(relativePath, url.searchParams);
|
||||
return snapshotServer.serveClosestScreenshot(
|
||||
relativePath,
|
||||
url.searchParams
|
||||
);
|
||||
}
|
||||
|
||||
if (relativePath.startsWith('/sha1/')) {
|
||||
|
|
@ -147,15 +232,21 @@ async function doFetch(event: FetchEvent): Promise<Response> {
|
|||
const sha1 = relativePath.slice('/sha1/'.length);
|
||||
for (const trace of loadedTraces.values()) {
|
||||
const blob = await trace.traceModel.resourceForSha1(sha1);
|
||||
if (blob)
|
||||
return new Response(blob, { status: 200, headers: downloadHeaders(url.searchParams) });
|
||||
if (blob) {
|
||||
return new Response(blob, {
|
||||
status: 200,
|
||||
headers: downloadHeaders(url.searchParams),
|
||||
});
|
||||
}
|
||||
}
|
||||
return new Response(null, { status: 404 });
|
||||
}
|
||||
|
||||
if (relativePath.startsWith('/file/')) {
|
||||
const path = url.searchParams.get('path')!;
|
||||
const traceViewerServer = clientIdToTraceUrls.get(event.clientId ?? '')?.traceViewerServer;
|
||||
const traceViewerServer = clientIdToTraceUrls.get(
|
||||
event.clientId ?? ''
|
||||
)?.traceViewerServer;
|
||||
if (!traceViewerServer)
|
||||
throw new Error('client is not initialized');
|
||||
const response = await traceViewerServer.readFile(path);
|
||||
|
|
@ -186,7 +277,12 @@ function downloadHeaders(searchParams: URLSearchParams): Headers | undefined {
|
|||
if (!name)
|
||||
return;
|
||||
const headers = new Headers();
|
||||
headers.set('Content-Disposition', `attachment; filename="attachment"; filename*=UTF-8''${encodeURIComponent(name)}`);
|
||||
headers.set(
|
||||
'Content-Disposition',
|
||||
`attachment; filename="attachment"; filename*=UTF-8''${encodeURIComponent(
|
||||
name
|
||||
)}`
|
||||
);
|
||||
if (contentType)
|
||||
headers.set('Content-Type', contentType);
|
||||
return headers;
|
||||
|
|
@ -214,6 +310,7 @@ async function gc() {
|
|||
if (!usedTraces.has(traceUrl))
|
||||
loadedTraces.delete(traceUrl);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Language } from '@isomorphic/locatorGenerators';
|
||||
import type * as actionTypes from '@recorder/actions';
|
||||
import { SourceChooser } from '@web/components/sourceChooser';
|
||||
import { SplitView } from '@web/components/splitView';
|
||||
|
|
@ -31,12 +32,10 @@ import type * as modelUtil from '../modelUtil';
|
|||
import type { SourceLocation } from '../modelUtil';
|
||||
import { NetworkTab, useNetworkTabModel } from '../networkTab';
|
||||
import { collectSnapshots, extendSnapshot, SnapshotView } from '../snapshotTab';
|
||||
import { SourceTab } from '../sourceTab';
|
||||
import { ModelContext, ModelProvider } from './modelContext';
|
||||
import './recorderView.css';
|
||||
import { ActionListView } from './actionListView';
|
||||
import { BackendContext, BackendProvider } from './backendContext';
|
||||
import type { Language } from '@isomorphic/locatorGenerators';
|
||||
import { ModelContext, ModelProvider } from './modelContext';
|
||||
import './recorderView.css';
|
||||
|
||||
export const RecorderView: React.FunctionComponent = () => {
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
|
|
@ -219,15 +218,15 @@ const PropertiesView: React.FunctionComponent<{
|
|||
setHighlightedLocator={setHighlightedLocator} />,
|
||||
};
|
||||
|
||||
const sourceTab: TabbedPaneTabModel = {
|
||||
id: 'source',
|
||||
title: 'Source',
|
||||
render: () => <SourceTab
|
||||
sources={sourceModel.current}
|
||||
stackFrameLocation={'right'}
|
||||
fallbackLocation={sourceLocation}
|
||||
/>
|
||||
};
|
||||
// const sourceTab: TabbedPaneTabModel = {
|
||||
// id: 'source',
|
||||
// title: 'Source',
|
||||
// render: () => <SourceTab
|
||||
// sources={sourceModel.current}
|
||||
// stackFrameLocation={'right'}
|
||||
// fallbackLocation={sourceLocation}
|
||||
// />
|
||||
// };
|
||||
const consoleTab: TabbedPaneTabModel = {
|
||||
id: 'console',
|
||||
title: 'Console',
|
||||
|
|
@ -242,7 +241,6 @@ const PropertiesView: React.FunctionComponent<{
|
|||
};
|
||||
|
||||
const tabs: TabbedPaneTabModel[] = [
|
||||
sourceTab,
|
||||
inspectorTab,
|
||||
consoleTab,
|
||||
networkTab,
|
||||
|
|
@ -283,6 +281,7 @@ const TraceView: React.FunctionComponent<{
|
|||
return snapshot ? extendSnapshot(snapshot) : undefined;
|
||||
}, [snapshot]);
|
||||
|
||||
|
||||
return <SnapshotView
|
||||
sdkLanguage={sdkLanguage}
|
||||
testIdAttributeName='data-testid'
|
||||
|
|
|
|||
|
|
@ -14,22 +14,22 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import './snapshotTab.css';
|
||||
import * as React from 'react';
|
||||
import ConsoleAPI from '@injected/consoleApi';
|
||||
import { InjectedScript } from '@injected/injectedScript';
|
||||
import { Recorder } from '@injected/recorder/recorder';
|
||||
import type { Language } from '@isomorphic/locatorGenerators';
|
||||
import { asLocator } from '@isomorphic/locatorGenerators';
|
||||
import { locatorOrSelectorAsSelector } from '@isomorphic/locatorParser';
|
||||
import type { ElementInfo } from '@recorder/recorderTypes';
|
||||
import type { ActionTraceEvent } from '@trace/trace';
|
||||
import { context, type MultiTraceModel, prevInList } from './modelUtil';
|
||||
import { TabbedPaneTab } from '@web/components/tabbedPane';
|
||||
import { Toolbar } from '@web/components/toolbar';
|
||||
import { ToolbarButton } from '@web/components/toolbarButton';
|
||||
import { clsx, useMeasure } from '@web/uiUtils';
|
||||
import { InjectedScript } from '@injected/injectedScript';
|
||||
import { Recorder } from '@injected/recorder/recorder';
|
||||
import ConsoleAPI from '@injected/consoleApi';
|
||||
import { asLocator } from '@isomorphic/locatorGenerators';
|
||||
import type { Language } from '@isomorphic/locatorGenerators';
|
||||
import { locatorOrSelectorAsSelector } from '@isomorphic/locatorParser';
|
||||
import { TabbedPaneTab } from '@web/components/tabbedPane';
|
||||
import * as React from 'react';
|
||||
import { BrowserFrame } from './browserFrame';
|
||||
import type { ElementInfo } from '@recorder/recorderTypes';
|
||||
import { context, type MultiTraceModel, prevInList } from './modelUtil';
|
||||
import './snapshotTab.css';
|
||||
|
||||
export const SnapshotTabsView: React.FunctionComponent<{
|
||||
action: ActionTraceEvent | undefined,
|
||||
|
|
@ -329,7 +329,7 @@ const serverParam = new URLSearchParams(window.location.search).get('server');
|
|||
|
||||
export function extendSnapshot(snapshot: Snapshot): SnapshotUrls {
|
||||
const params = new URLSearchParams();
|
||||
params.set('trace', context(snapshot.action).traceUrl);
|
||||
params.set('trace', btoa(context(snapshot.action).traceUrl));
|
||||
params.set('name', snapshot.snapshotName);
|
||||
if (isUnderTest)
|
||||
params.set('isUnderTest', 'true');
|
||||
|
|
@ -360,6 +360,7 @@ export async function fetchSnapshotInfo(snapshotInfoUrl: string | undefined) {
|
|||
const result = { url: '', viewport: kDefaultViewport, timestamp: undefined, wallTime: undefined };
|
||||
if (snapshotInfoUrl) {
|
||||
const response = await fetch(snapshotInfoUrl);
|
||||
|
||||
const info = await response.json();
|
||||
if (!info.error) {
|
||||
result.url = info.url;
|
||||
|
|
|
|||
|
|
@ -14,18 +14,18 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { StackFrame } from '@protocol/channels';
|
||||
import type { SourceHighlight } from '@web/components/codeMirrorWrapper';
|
||||
import { CodeMirrorWrapper } from '@web/components/codeMirrorWrapper';
|
||||
import { SplitView } from '@web/components/splitView';
|
||||
import * as React from 'react';
|
||||
import { Toolbar } from '@web/components/toolbar';
|
||||
import { ToolbarButton } from '@web/components/toolbarButton';
|
||||
import { useAsyncMemo } from '@web/uiUtils';
|
||||
import * as React from 'react';
|
||||
import { CopyToClipboard } from './copyToClipboard';
|
||||
import type { SourceLocation, SourceModel } from './modelUtil';
|
||||
import './sourceTab.css';
|
||||
import { StackTraceView } from './stackTrace';
|
||||
import { CodeMirrorWrapper } from '@web/components/codeMirrorWrapper';
|
||||
import type { SourceHighlight } from '@web/components/codeMirrorWrapper';
|
||||
import type { SourceLocation, SourceModel } from './modelUtil';
|
||||
import type { StackFrame } from '@protocol/channels';
|
||||
import { CopyToClipboard } from './copyToClipboard';
|
||||
import { ToolbarButton } from '@web/components/toolbarButton';
|
||||
import { Toolbar } from '@web/components/toolbar';
|
||||
|
||||
export const SourceTab: React.FunctionComponent<{
|
||||
stack?: StackFrame[],
|
||||
|
|
@ -98,7 +98,7 @@ export const SourceTab: React.FunctionComponent<{
|
|||
|
||||
const showStackFrames = (stack?.length ?? 0) > 1;
|
||||
const shortFileName = getFileName(fileName);
|
||||
|
||||
return null;
|
||||
return <SplitView
|
||||
sidebarSize={200}
|
||||
orientation={stackFrameLocation === 'bottom' ? 'vertical' : 'horizontal'}
|
||||
|
|
|
|||
|
|
@ -14,34 +14,33 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Entry } from '@trace/har';
|
||||
import type { AfterActionTraceEventAttachment } from '@trace/trace';
|
||||
import { SplitView } from '@web/components/splitView';
|
||||
import type { TabbedPaneTabModel } from '@web/components/tabbedPane';
|
||||
import { TabbedPane } from '@web/components/tabbedPane';
|
||||
import { ToolbarButton } from '@web/components/toolbarButton';
|
||||
import { clsx, msToString, useSetting } from '@web/uiUtils';
|
||||
import * as React from 'react';
|
||||
import { ActionList } from './actionList';
|
||||
import { AnnotationsTab } from './annotationsTab';
|
||||
import { AttachmentsTab } from './attachmentsTab';
|
||||
import { CallTab } from './callTab';
|
||||
import { LogTab } from './logTab';
|
||||
import { ErrorsTab, useErrorsTabModel } from './errorsTab';
|
||||
import type { ErrorDescription } from './errorsTab';
|
||||
import type { ConsoleEntry } from './consoleTab';
|
||||
import { ConsoleTab, useConsoleTabModel } from './consoleTab';
|
||||
import type { ErrorDescription } from './errorsTab';
|
||||
import { ErrorsTab, useErrorsTabModel } from './errorsTab';
|
||||
import type { Boundaries } from './geometry';
|
||||
import { InspectorTab } from './inspectorTab';
|
||||
import { LogTab } from './logTab';
|
||||
import { MetadataView } from './metadataView';
|
||||
import type * as modelUtil from './modelUtil';
|
||||
import { NetworkTab, useNetworkTabModel } from './networkTab';
|
||||
import { SnapshotTabsView } from './snapshotTab';
|
||||
import { SourceTab } from './sourceTab';
|
||||
import { TabbedPane } from '@web/components/tabbedPane';
|
||||
import type { TabbedPaneTabModel } from '@web/components/tabbedPane';
|
||||
import { Timeline } from './timeline';
|
||||
import { MetadataView } from './metadataView';
|
||||
import { AttachmentsTab } from './attachmentsTab';
|
||||
import { AnnotationsTab } from './annotationsTab';
|
||||
import type { Boundaries } from './geometry';
|
||||
import { InspectorTab } from './inspectorTab';
|
||||
import { ToolbarButton } from '@web/components/toolbarButton';
|
||||
import { useSetting, msToString, clsx } from '@web/uiUtils';
|
||||
import type { Entry } from '@trace/har';
|
||||
import './workbench.css';
|
||||
import { testStatusIcon, testStatusText } from './testUtils';
|
||||
import type { UITestStatus } from './testUtils';
|
||||
import type { AfterActionTraceEventAttachment } from '@trace/trace';
|
||||
import { testStatusIcon, testStatusText } from './testUtils';
|
||||
import { Timeline } from './timeline';
|
||||
import './workbench.css';
|
||||
|
||||
export const Workbench: React.FunctionComponent<{
|
||||
model?: modelUtil.MultiTraceModel,
|
||||
|
|
@ -202,19 +201,19 @@ export const Workbench: React.FunctionComponent<{
|
|||
if (!selectedAction && fallbackLocation)
|
||||
fallbackSourceErrorCount = fallbackLocation.source?.errors.length;
|
||||
|
||||
const sourceTab: TabbedPaneTabModel = {
|
||||
id: 'source',
|
||||
title: 'Source',
|
||||
errorCount: fallbackSourceErrorCount,
|
||||
render: () => <SourceTab
|
||||
stack={revealedStack}
|
||||
sources={sources}
|
||||
rootDir={rootDir}
|
||||
stackFrameLocation={sidebarLocation === 'bottom' ? 'right' : 'bottom'}
|
||||
fallbackLocation={fallbackLocation}
|
||||
onOpenExternally={onOpenExternally}
|
||||
/>
|
||||
};
|
||||
// const sourceTab: TabbedPaneTabModel = {
|
||||
// id: 'source',
|
||||
// title: 'Source',
|
||||
// errorCount: fallbackSourceErrorCount,
|
||||
// render: () => <SourceTab
|
||||
// stack={revealedStack}
|
||||
// sources={sources}
|
||||
// rootDir={rootDir}
|
||||
// stackFrameLocation={sidebarLocation === 'bottom' ? 'right' : 'bottom'}
|
||||
// fallbackLocation={fallbackLocation}
|
||||
// onOpenExternally={onOpenExternally}
|
||||
// />
|
||||
// };
|
||||
const consoleTab: TabbedPaneTabModel = {
|
||||
id: 'console',
|
||||
title: 'Console',
|
||||
|
|
@ -247,7 +246,6 @@ export const Workbench: React.FunctionComponent<{
|
|||
errorsTab,
|
||||
consoleTab,
|
||||
networkTab,
|
||||
sourceTab,
|
||||
attachmentsTab,
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -73,6 +73,28 @@ body.dark-mode .drop-target {
|
|||
z-index: 10;
|
||||
}
|
||||
|
||||
.loading-inset {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border: 5px solid rgba(255, 255, 255, 0.3);
|
||||
border-top-color: white;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
.inner-progress {
|
||||
background-color: var(--vscode-progressBar-background);
|
||||
height: 100%;
|
||||
|
|
|
|||
|
|
@ -14,14 +14,14 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { TestServerConnection, WebSocketTestServerTransport } from '@testIsomorphic/testServerConnection';
|
||||
import { ToolbarButton } from '@web/components/toolbarButton';
|
||||
import { toggleTheme } from '@web/theme';
|
||||
import * as React from 'react';
|
||||
import type { ContextEntry } from '../types/entries';
|
||||
import { MultiTraceModel } from './modelUtil';
|
||||
import './workbenchLoader.css';
|
||||
import { toggleTheme } from '@web/theme';
|
||||
import { Workbench } from './workbench';
|
||||
import { TestServerConnection, WebSocketTestServerTransport } from '@testIsomorphic/testServerConnection';
|
||||
import './workbenchLoader.css';
|
||||
|
||||
export const WorkbenchLoader: React.FunctionComponent<{
|
||||
}> = () => {
|
||||
|
|
@ -166,6 +166,9 @@ export const WorkbenchLoader: React.FunctionComponent<{
|
|||
<div className='progress'>
|
||||
<div className='inner-progress' style={{ width: progress.total ? (100 * progress.done / progress.total) + '%' : 0 }}></div>
|
||||
</div>
|
||||
{progress.done < progress.total && <div className='loading-inset'>
|
||||
<div className='spinner'></div>
|
||||
</div>}
|
||||
<Workbench model={model} inert={showFileUploadDropArea} />
|
||||
{fileForLocalModeError && <div className='drop-target'>
|
||||
<div>Trace Viewer uses Service Workers to show traces. To view trace:</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue