also flash trace-viewer
This commit is contained in:
parent
13d89e45cb
commit
299a52e0e1
|
|
@ -55,3 +55,11 @@
|
||||||
a.codicon-cloud-download:hover{
|
a.codicon-cloud-download:hover{
|
||||||
background-color: var(--vscode-list-inactiveSelectionBackground)
|
background-color: var(--vscode-list-inactiveSelectionBackground)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.yellow-flash {
|
||||||
|
animation: yellowflash-bg 2s;
|
||||||
|
}
|
||||||
|
@keyframes yellowflash-bg {
|
||||||
|
from { background: var(--vscode-peekViewEditor-matchHighlightBackground); }
|
||||||
|
to { background: transparent; }
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import './attachmentsTab.css';
|
import './attachmentsTab.css';
|
||||||
import { ImageDiffView } from '@web/shared/imageDiffView';
|
import { ImageDiffView } from '@web/shared/imageDiffView';
|
||||||
import type { ActionTraceEventInContext, MultiTraceModel } from './modelUtil';
|
import type { MultiTraceModel } from './modelUtil';
|
||||||
import { PlaceholderPanel } from './placeholderPanel';
|
import { PlaceholderPanel } from './placeholderPanel';
|
||||||
import type { AfterActionTraceEventAttachment } from '@trace/trace';
|
import type { AfterActionTraceEventAttachment } from '@trace/trace';
|
||||||
import { CodeMirrorWrapper, lineHeight } from '@web/components/codeMirrorWrapper';
|
import { CodeMirrorWrapper, lineHeight } from '@web/components/codeMirrorWrapper';
|
||||||
|
|
@ -26,15 +26,27 @@ import { Expandable } from '@web/components/expandable';
|
||||||
import { linkifyText } from '@web/renderUtils';
|
import { linkifyText } from '@web/renderUtils';
|
||||||
import { clsx } from '@web/uiUtils';
|
import { clsx } from '@web/uiUtils';
|
||||||
|
|
||||||
|
// flash is retriggered whenever the value changes
|
||||||
|
function useFlash(flash: any | undefined) {
|
||||||
|
const [flashState, setFlashState] = React.useState(false);
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (flash) {
|
||||||
|
setFlashState(true);
|
||||||
|
const timeout = setTimeout(() => setFlashState(false), 1000);
|
||||||
|
return () => clearTimeout(timeout);
|
||||||
|
}
|
||||||
|
}, [flash]);
|
||||||
|
return flashState;
|
||||||
|
}
|
||||||
|
|
||||||
type Attachment = AfterActionTraceEventAttachment & { traceUrl: string };
|
type Attachment = AfterActionTraceEventAttachment & { traceUrl: string };
|
||||||
|
|
||||||
type ExpandableAttachmentProps = {
|
type ExpandableAttachmentProps = {
|
||||||
attachment: Attachment;
|
attachment: Attachment;
|
||||||
reveal: boolean;
|
reveal?: any;
|
||||||
highlight: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const ExpandableAttachment: React.FunctionComponent<ExpandableAttachmentProps> = ({ attachment, reveal, highlight }) => {
|
const ExpandableAttachment: React.FunctionComponent<ExpandableAttachmentProps> = ({ attachment, reveal }) => {
|
||||||
const [expanded, setExpanded] = React.useState(false);
|
const [expanded, setExpanded] = React.useState(false);
|
||||||
const [attachmentText, setAttachmentText] = React.useState<string | null>(null);
|
const [attachmentText, setAttachmentText] = React.useState<string | null>(null);
|
||||||
const [placeholder, setPlaceholder] = React.useState<string | null>(null);
|
const [placeholder, setPlaceholder] = React.useState<string | null>(null);
|
||||||
|
|
@ -47,6 +59,7 @@ const ExpandableAttachment: React.FunctionComponent<ExpandableAttachmentProps> =
|
||||||
if (reveal)
|
if (reveal)
|
||||||
ref.current?.scrollIntoView({ behavior: 'smooth' });
|
ref.current?.scrollIntoView({ behavior: 'smooth' });
|
||||||
}, [reveal]);
|
}, [reveal]);
|
||||||
|
const flash = useFlash(reveal);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (expanded && attachmentText === null && placeholder === null) {
|
if (expanded && attachmentText === null && placeholder === null) {
|
||||||
|
|
@ -66,14 +79,14 @@ const ExpandableAttachment: React.FunctionComponent<ExpandableAttachmentProps> =
|
||||||
}, [attachmentText]);
|
}, [attachmentText]);
|
||||||
|
|
||||||
const title = <span style={{ marginLeft: 5 }} ref={ref} aria-label={attachment.name}>
|
const title = <span style={{ marginLeft: 5 }} ref={ref} aria-label={attachment.name}>
|
||||||
<span className={clsx(highlight && 'attachment-title-highlight')}>{linkifyText(attachment.name)}</span>
|
<span>{linkifyText(attachment.name)}</span>
|
||||||
{hasContent && <a style={{ marginLeft: 5 }} href={downloadURL(attachment)}>download</a>}
|
{hasContent && <a style={{ marginLeft: 5 }} href={downloadURL(attachment)}>download</a>}
|
||||||
</span>;
|
</span>;
|
||||||
|
|
||||||
if (!isTextAttachment || !hasContent)
|
if (!isTextAttachment || !hasContent)
|
||||||
return <div style={{ marginLeft: 20 }}>{title}</div>;
|
return <div style={{ marginLeft: 20 }}>{title}</div>;
|
||||||
|
|
||||||
return <>
|
return <div className={clsx(flash && 'yellow-flash')}>
|
||||||
<Expandable title={title} expanded={expanded} setExpanded={setExpanded} expandOnTitleClick={true}>
|
<Expandable title={title} expanded={expanded} setExpanded={setExpanded} expandOnTitleClick={true}>
|
||||||
{placeholder && <i>{placeholder}</i>}
|
{placeholder && <i>{placeholder}</i>}
|
||||||
</Expandable>
|
</Expandable>
|
||||||
|
|
@ -87,14 +100,13 @@ const ExpandableAttachment: React.FunctionComponent<ExpandableAttachmentProps> =
|
||||||
wrapLines={false}>
|
wrapLines={false}>
|
||||||
</CodeMirrorWrapper>
|
</CodeMirrorWrapper>
|
||||||
</div>}
|
</div>}
|
||||||
</>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AttachmentsTab: React.FunctionComponent<{
|
export const AttachmentsTab: React.FunctionComponent<{
|
||||||
model: MultiTraceModel | undefined,
|
model: MultiTraceModel | undefined,
|
||||||
selectedAction: ActionTraceEventInContext | undefined,
|
|
||||||
revealedAttachment?: AfterActionTraceEventAttachment,
|
revealedAttachment?: AfterActionTraceEventAttachment,
|
||||||
}> = ({ model, selectedAction, revealedAttachment }) => {
|
}> = ({ model, revealedAttachment }) => {
|
||||||
const { diffMap, screenshots, attachments } = React.useMemo(() => {
|
const { diffMap, screenshots, attachments } = React.useMemo(() => {
|
||||||
const attachments = new Set<Attachment>();
|
const attachments = new Set<Attachment>();
|
||||||
const screenshots = new Set<Attachment>();
|
const screenshots = new Set<Attachment>();
|
||||||
|
|
@ -153,8 +165,7 @@ export const AttachmentsTab: React.FunctionComponent<{
|
||||||
return <div className='attachment-item' key={attachmentKey(a, i)}>
|
return <div className='attachment-item' key={attachmentKey(a, i)}>
|
||||||
<ExpandableAttachment
|
<ExpandableAttachment
|
||||||
attachment={a}
|
attachment={a}
|
||||||
highlight={selectedAction?.attachments?.some(selected => isEqualAttachment(a, selected)) ?? false}
|
reveal={(!!revealedAttachment && isEqualAttachment(a, revealedAttachment)) ? revealedAttachment : undefined}
|
||||||
reveal={!!revealedAttachment && isEqualAttachment(a, revealedAttachment)}
|
|
||||||
/>
|
/>
|
||||||
</div>;
|
</div>;
|
||||||
})}
|
})}
|
||||||
|
|
|
||||||
|
|
@ -148,7 +148,7 @@ export const Workbench: React.FunctionComponent<{
|
||||||
|
|
||||||
const revealAttachment = React.useCallback((attachment: AfterActionTraceEventAttachment) => {
|
const revealAttachment = React.useCallback((attachment: AfterActionTraceEventAttachment) => {
|
||||||
selectPropertiesTab('attachments');
|
selectPropertiesTab('attachments');
|
||||||
setRevealedAttachment(attachment);
|
setRevealedAttachment({ ...attachment }); // copy to force re-render
|
||||||
}, [selectPropertiesTab]);
|
}, [selectPropertiesTab]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
|
@ -238,7 +238,7 @@ export const Workbench: React.FunctionComponent<{
|
||||||
id: 'attachments',
|
id: 'attachments',
|
||||||
title: 'Attachments',
|
title: 'Attachments',
|
||||||
count: attachments.length,
|
count: attachments.length,
|
||||||
render: () => <AttachmentsTab model={model} selectedAction={selectedAction} revealedAttachment={revealedAttachment} />
|
render: () => <AttachmentsTab model={model} revealedAttachment={revealedAttachment} />
|
||||||
};
|
};
|
||||||
|
|
||||||
const tabs: TabbedPaneTabModel[] = [
|
const tabs: TabbedPaneTabModel[] = [
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue