chore: highlight actions and har entries in timeline (#26781)

This commit is contained in:
Pavel Feldman 2023-08-29 22:23:08 -07:00 committed by GitHub
parent c209d7e708
commit a34deffa43
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 39 additions and 19 deletions

View file

@ -30,7 +30,8 @@ type Filter = 'status' | 'method' | 'file' | 'time' | 'size' | 'content-type';
export const NetworkTab: React.FunctionComponent<{ export const NetworkTab: React.FunctionComponent<{
model: modelUtil.MultiTraceModel | undefined, model: modelUtil.MultiTraceModel | undefined,
selectedTime: Boundaries | undefined, selectedTime: Boundaries | undefined,
}> = ({ model, selectedTime }) => { onEntryHovered: (entry: Entry | undefined) => void,
}> = ({ model, selectedTime, onEntryHovered }) => {
const [resource, setResource] = React.useState<Entry | undefined>(); const [resource, setResource] = React.useState<Entry | undefined>();
const [filter, setFilter] = React.useState<Filter | undefined>(undefined); const [filter, setFilter] = React.useState<Filter | undefined>(undefined);
const [negateFilter, setNegateFilter] = React.useState<boolean>(false); const [negateFilter, setNegateFilter] = React.useState<boolean>(false);
@ -64,6 +65,7 @@ export const NetworkTab: React.FunctionComponent<{
items={resources} items={resources}
render={entry => <NetworkResource resource={entry}></NetworkResource>} render={entry => <NetworkResource resource={entry}></NetworkResource>}
onSelected={setResource} onSelected={setResource}
onHighlighted={onEntryHovered}
/> />
</div>} </div>}
{resource && <NetworkResourceDetails resource={resource} onClose={() => setResource(undefined)} />} {resource && <NetworkResourceDetails resource={resource} onClose={() => setResource(undefined)} />}

View file

@ -62,26 +62,33 @@
left: 0; left: 0;
} }
.timeline-lane.timeline-bars { .timeline-bars {
pointer-events: auto; position: absolute;
margin-bottom: 5px; top: 0;
height: 5px; bottom: 0;
overflow: visible; left: 0;
right: 0;
} }
.timeline-bar { .timeline-bar {
position: absolute; position: absolute;
height: 2px;
--action-color: gray; --action-color: gray;
background-color: var(--action-color); --action-background-color: #88888802;
border-top: 2px solid var(--action-color);
}
.timeline-bar.active {
background-color: var(--action-background-color);
} }
.timeline-bar.action { .timeline-bar.action {
--action-color: var(--vscode-charts-red); --action-color: var(--vscode-charts-red);
--action-background-color: #e5140033;
} }
.timeline-bar.network { .timeline-bar.network {
--action-color: var(--vscode-charts-blue); --action-color: var(--vscode-charts-blue);
--action-background-color: #1a85ff33;
} }
.timeline-label { .timeline-label {

View file

@ -32,16 +32,19 @@ type TimelineBar = {
rightPosition: number; rightPosition: number;
leftTime: number; leftTime: number;
rightTime: number; rightTime: number;
active: boolean;
}; };
export const Timeline: React.FunctionComponent<{ export const Timeline: React.FunctionComponent<{
model: MultiTraceModel | undefined, model: MultiTraceModel | undefined,
boundaries: Boundaries, boundaries: Boundaries,
highlightedAction: ActionTraceEventInContext | undefined,
highlightedEntry: Entry | undefined,
onSelected: (action: ActionTraceEventInContext) => void, onSelected: (action: ActionTraceEventInContext) => void,
selectedTime: Boundaries | undefined, selectedTime: Boundaries | undefined,
setSelectedTime: (time: Boundaries | undefined) => void, setSelectedTime: (time: Boundaries | undefined) => void,
sdkLanguage: Language, sdkLanguage: Language,
}> = ({ model, boundaries, onSelected, selectedTime, setSelectedTime, sdkLanguage }) => { }> = ({ model, boundaries, onSelected, highlightedAction, highlightedEntry, selectedTime, setSelectedTime, sdkLanguage }) => {
const [measure, ref] = useMeasure<HTMLDivElement>(); const [measure, ref] = useMeasure<HTMLDivElement>();
const [dragWindow, setDragWindow] = React.useState<{ startX: number, endX: number, pivot?: number, type: 'resize' | 'move' } | undefined>(); const [dragWindow, setDragWindow] = React.useState<{ startX: number, endX: number, pivot?: number, type: 'resize' | 'move' } | undefined>();
const [previewPoint, setPreviewPoint] = React.useState<FilmStripPreviewPoint | undefined>(); const [previewPoint, setPreviewPoint] = React.useState<FilmStripPreviewPoint | undefined>();
@ -70,6 +73,7 @@ export const Timeline: React.FunctionComponent<{
rightTime: entry.endTime || boundaries.maximum, rightTime: entry.endTime || boundaries.maximum,
leftPosition: timeToPosition(measure.width, boundaries, entry.startTime), leftPosition: timeToPosition(measure.width, boundaries, entry.startTime),
rightPosition: timeToPosition(measure.width, boundaries, entry.endTime || boundaries.maximum), rightPosition: timeToPosition(measure.width, boundaries, entry.endTime || boundaries.maximum),
active: highlightedAction === entry,
}); });
} }
@ -82,10 +86,11 @@ export const Timeline: React.FunctionComponent<{
rightTime: endTime, rightTime: endTime,
leftPosition: timeToPosition(measure.width, boundaries, startTime), leftPosition: timeToPosition(measure.width, boundaries, startTime),
rightPosition: timeToPosition(measure.width, boundaries, endTime), rightPosition: timeToPosition(measure.width, boundaries, endTime),
active: highlightedEntry === resource,
}); });
} }
return bars; return bars;
}, [model, boundaries, measure]); }, [model, boundaries, measure, highlightedAction, highlightedEntry]);
const onMouseDown = React.useCallback((event: React.MouseEvent) => { const onMouseDown = React.useCallback((event: React.MouseEvent) => {
setPreviewPoint(undefined); setPreviewPoint(undefined);
@ -215,19 +220,21 @@ export const Timeline: React.FunctionComponent<{
</div>; </div>;
}) })
}</div> }</div>
{<div className='timeline-lane timeline-bars'>{ <div style={{ height: 8 }}></div>
<FilmStrip model={model} boundaries={boundaries} previewPoint={previewPoint} />
<div className='timeline-bars'>{
bars.map((bar, index) => { bars.map((bar, index) => {
return <div key={index} return <div key={index}
className={'timeline-bar ' + (bar.action ? 'action ' : '') + (bar.resource ? 'network ' : '')} className={'timeline-bar' + (bar.action ? ' action' : '') + (bar.resource ? ' network' : '') + (bar.active ? ' active' : '')}
style={{ style={{
left: bar.leftPosition + 'px', left: bar.leftPosition,
width: Math.max(1, bar.rightPosition - bar.leftPosition) + 'px', width: Math.max(1, bar.rightPosition - bar.leftPosition),
top: barTop(bar) + 'px', top: barTop(bar),
bottom: 0,
}} }}
></div>; ></div>;
}) })
}</div>} }</div>
<FilmStrip model={model} boundaries={boundaries} previewPoint={previewPoint} />
<div className='timeline-marker' style={{ <div className='timeline-marker' style={{
display: (previewPoint !== undefined) ? 'block' : 'none', display: (previewPoint !== undefined) ? 'block' : 'none',
left: (previewPoint?.x || 0) + 'px', left: (previewPoint?.x || 0) + 'px',
@ -284,5 +291,5 @@ function positionToTime(clientWidth: number, boundaries: Boundaries, x: number):
} }
function barTop(bar: TimelineBar): number { function barTop(bar: TimelineBar): number {
return bar.resource ? 5 : 0; return bar.resource ? 25 : 20;
} }

View file

@ -33,6 +33,7 @@ import type { Boundaries } from '../geometry';
import { InspectorTab } from './inspectorTab'; import { InspectorTab } from './inspectorTab';
import { ToolbarButton } from '@web/components/toolbarButton'; import { ToolbarButton } from '@web/components/toolbarButton';
import { useSetting } from '@web/uiUtils'; import { useSetting } from '@web/uiUtils';
import type { Entry } from '@trace/har';
export const Workbench: React.FunctionComponent<{ export const Workbench: React.FunctionComponent<{
model?: MultiTraceModel, model?: MultiTraceModel,
@ -46,6 +47,7 @@ export const Workbench: React.FunctionComponent<{
}> = ({ model, hideStackFrames, showSourcesFirst, rootDir, fallbackLocation, initialSelection, onSelectionChanged, isLive }) => { }> = ({ model, hideStackFrames, showSourcesFirst, rootDir, fallbackLocation, initialSelection, onSelectionChanged, isLive }) => {
const [selectedAction, setSelectedAction] = React.useState<ActionTraceEventInContext | undefined>(undefined); const [selectedAction, setSelectedAction] = React.useState<ActionTraceEventInContext | undefined>(undefined);
const [highlightedAction, setHighlightedAction] = React.useState<ActionTraceEventInContext | undefined>(); const [highlightedAction, setHighlightedAction] = React.useState<ActionTraceEventInContext | undefined>();
const [highlightedEntry, setHighlightedEntry] = React.useState<Entry | undefined>();
const [selectedNavigatorTab, setSelectedNavigatorTab] = React.useState<string>('actions'); const [selectedNavigatorTab, setSelectedNavigatorTab] = React.useState<string>('actions');
const [selectedPropertiesTab, setSelectedPropertiesTab] = React.useState<string>(showSourcesFirst ? 'source' : 'call'); const [selectedPropertiesTab, setSelectedPropertiesTab] = React.useState<string>(showSourcesFirst ? 'source' : 'call');
const [isInspecting, setIsInspecting] = React.useState(false); const [isInspecting, setIsInspecting] = React.useState(false);
@ -122,7 +124,7 @@ export const Workbench: React.FunctionComponent<{
const networkTab: TabbedPaneTabModel = { const networkTab: TabbedPaneTabModel = {
id: 'network', id: 'network',
title: 'Network', title: 'Network',
render: () => <NetworkTab model={model} selectedTime={selectedTime} /> render: () => <NetworkTab model={model} selectedTime={selectedTime} onEntryHovered={setHighlightedEntry}/>
}; };
const attachmentsTab: TabbedPaneTabModel = { const attachmentsTab: TabbedPaneTabModel = {
id: 'attachments', id: 'attachments',
@ -161,6 +163,8 @@ export const Workbench: React.FunctionComponent<{
<Timeline <Timeline
model={model} model={model}
boundaries={boundaries} boundaries={boundaries}
highlightedAction={highlightedAction}
highlightedEntry={highlightedEntry}
onSelected={onActionSelected} onSelected={onActionSelected}
sdkLanguage={sdkLanguage} sdkLanguage={sdkLanguage}
selectedTime={selectedTime} selectedTime={selectedTime}