fix(report): show highlight elment when cache hit (#723)

* fix(report): show highlight elment when cache hit

* fix(core): replay animation

---------

Co-authored-by: yutao <yutao.tao@bytedance.com>
This commit is contained in:
Leyang 2025-05-16 22:28:29 +08:00 committed by GitHub
parent b261ed7f2a
commit b10a7751d4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 92634 additions and 30 deletions

View File

@ -38,8 +38,7 @@ const DetailPanel = (): JSX.Element => {
(store) => store._executionDumpLoadId, (store) => store._executionDumpLoadId,
); );
const activeTask = useExecutionDump((store) => store.activeTask); const activeTask = useExecutionDump((store) => store.activeTask);
const blackboardViewAvailable = const blackboardViewAvailable = Boolean(activeTask?.pageContext);
Boolean(activeTask?.pageContext) && insightDump;
const [preferredViewType, setViewType] = useState(VIEW_TYPE_REPLAY); const [preferredViewType, setViewType] = useState(VIEW_TYPE_REPLAY);
const animationScripts = useExecutionDump( const animationScripts = useExecutionDump(
(store) => store.activeExecutionAnimation, (store) => store.activeExecutionAnimation,
@ -87,12 +86,21 @@ const DetailPanel = (): JSX.Element => {
</div> </div>
); );
} else if (viewType === VIEW_TYPE_BLACKBOARD) { } else if (viewType === VIEW_TYPE_BLACKBOARD) {
if (blackboardViewAvailable && insightDump) { if (blackboardViewAvailable) {
let highlightElements;
if (insightDump?.matchedElement) {
highlightElements = insightDump?.matchedElement;
} else {
highlightElements = activeTask.output.element // hit cache
? [activeTask.output.element]
: [];
}
content = ( content = (
<Blackboard <Blackboard
uiContext={activeTask.pageContext} uiContext={activeTask.pageContext}
highlightElements={insightDump.matchedElement} highlightElements={highlightElements}
highlightRect={insightDump!.taskInfo?.searchArea} highlightRect={insightDump?.taskInfo?.searchArea}
key={`${dumpId}`} key={`${dumpId}`}
/> />
); );

File diff suppressed because one or more lines are too long

View File

@ -9,7 +9,7 @@ import {
DownloadOutlined, DownloadOutlined,
LoadingOutlined, LoadingOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import type { BaseElement, Rect } from '@midscene/core'; import type { BaseElement, LocateResultElement, Rect } from '@midscene/core';
import { Spin, Tooltip } from 'antd'; import { Spin, Tooltip } from 'antd';
import { rectMarkForItem } from './blackboard'; import { rectMarkForItem } from './blackboard';
import { getTextureFromCache, loadTexture } from './pixi-loader'; import { getTextureFromCache, loadTexture } from './pixi-loader';
@ -444,7 +444,7 @@ export function Player(props?: {
const insightElementsAnimation = async ( const insightElementsAnimation = async (
elements: BaseElement[], elements: BaseElement[],
highlightElements: BaseElement[], highlightElements: (BaseElement | LocateResultElement)[],
searchArea: Rect | undefined, searchArea: Rect | undefined,
duration: number, duration: number,
frame: FrameFn, frame: FrameFn,
@ -516,7 +516,7 @@ export function Player(props?: {
highlightElements.map((element) => { highlightElements.map((element) => {
const [insightMarkGraphic] = rectMarkForItem( const [insightMarkGraphic] = rectMarkForItem(
element.rect, element.rect,
element.content, (element as BaseElement).content || '',
'highlight', 'highlight',
); );
insightMarkGraphic.alpha = 1; insightMarkGraphic.alpha = 1;
@ -615,18 +615,20 @@ export function Player(props?: {
if (item.type === 'sleep') { if (item.type === 'sleep') {
await sleep(item.duration); await sleep(item.duration);
} else if (item.type === 'insight') { } else if (item.type === 'insight') {
if (!item.insightDump || !item.img) { if (!item.img) {
throw new Error('insight dump or img is required'); throw new Error('img is required');
} }
currentImg.current = item.img; currentImg.current = item.img;
await repaintImage(); await repaintImage();
const elements = item.context?.content || []; const elements = item.context?.content || [];
const highlightElements = item.insightDump.matchedElement; const highlightElements = item.highlightElement
? [item.highlightElement]
: [];
await insightElementsAnimation( await insightElementsAnimation(
elements, elements,
highlightElements, highlightElements,
item.insightDump.taskInfo?.searchArea, item.searchArea,
item.duration, item.duration,
frame, frame,
); );

View File

@ -9,7 +9,7 @@ import type {
ExecutionTaskInsightLocate, ExecutionTaskInsightLocate,
ExecutionTaskPlanning, ExecutionTaskPlanning,
GroupedActionDump, GroupedActionDump,
InsightDump, LocateResultElement,
Rect, Rect,
UIContext, UIContext,
} from '@midscene/core'; } from '@midscene/core';
@ -38,8 +38,9 @@ export interface AnimationScript {
| 'sleep'; | 'sleep';
img?: string; img?: string;
camera?: TargetCameraState; camera?: TargetCameraState;
insightDump?: InsightDump;
context?: UIContext; context?: UIContext;
highlightElement?: LocateResultElement;
searchArea?: Rect;
duration: number; duration: number;
insightCameraDuration?: number; insightCameraDuration?: number;
title?: string; title?: string;
@ -117,8 +118,8 @@ export const mergeTwoCameraState = (
export interface ReplayScriptsInfo { export interface ReplayScriptsInfo {
scripts: AnimationScript[]; scripts: AnimationScript[];
width: number; width?: number;
height: number; height?: number;
sdkVersion?: string; sdkVersion?: string;
modelName?: string; modelName?: string;
modelDescription?: string; modelDescription?: string;
@ -128,13 +129,25 @@ export const allScriptsFromDump = (
dump: GroupedActionDump, dump: GroupedActionDump,
): ReplayScriptsInfo | null => { ): ReplayScriptsInfo | null => {
// find out the width and height of the screenshot // find out the width and height of the screenshot
let width = 0; let width: number | undefined = undefined;
let height = 0; let height: number | undefined = undefined;
let sdkVersion = ''; let sdkVersion: string | undefined = undefined;
let modelName = ''; let modelName: string | undefined = undefined;
let modelDescription = ''; let modelDescription: string | undefined = undefined;
dump.executions.forEach((execution) => { dump.executions.forEach((execution) => {
if (execution.sdkVersion) {
sdkVersion = execution.sdkVersion;
}
if (execution.model_name) {
modelName = execution.model_name;
}
if (execution.model_description) {
modelDescription = execution.model_description;
}
execution.tasks.forEach((task) => { execution.tasks.forEach((task) => {
const insightTask = task as ExecutionTaskInsightLocate; const insightTask = task as ExecutionTaskInsightLocate;
if (insightTask.pageContext?.size?.width) { if (insightTask.pageContext?.size?.width) {
@ -142,28 +155,33 @@ export const allScriptsFromDump = (
height = insightTask.pageContext.size.height; height = insightTask.pageContext.size.height;
} }
if (insightTask.log?.dump?.sdkVersion) { if (insightTask.log?.dump?.sdkVersion && !sdkVersion) {
sdkVersion = insightTask.log.dump.sdkVersion; sdkVersion = insightTask.log.dump.sdkVersion;
} }
if (insightTask.log?.dump?.model_name) { if (insightTask.log?.dump?.model_name && !modelName) {
modelName = insightTask.log.dump.model_name; modelName = insightTask.log.dump.model_name;
} }
if (insightTask.log?.dump?.model_description) { if (insightTask.log?.dump?.model_description && !modelDescription) {
modelDescription = insightTask.log.dump.model_description; modelDescription = insightTask.log.dump.model_description;
} }
}); });
}); });
if (!width || !height) { if (!width || !height) {
console.error('width or height is missing in dump file'); console.warn('width or height is missing in dump file');
return null; return {
scripts: [],
sdkVersion,
modelName,
modelDescription,
};
} }
const allScripts: AnimationScript[] = []; const allScripts: AnimationScript[] = [];
dump.executions.forEach((execution) => { dump.executions.forEach((execution) => {
const scripts = generateAnimationScripts(execution, -1, width, height); const scripts = generateAnimationScripts(execution, -1, width!, height!);
if (scripts) { if (scripts) {
allScripts.push(...scripts); allScripts.push(...scripts);
} }
@ -297,8 +315,8 @@ export const generateAnimationScripts = (
}; };
} }
const context = insightTask.pageContext; const context = insightTask.pageContext;
if (insightTask.log?.dump && context?.screenshotBase64) { if (context?.screenshotBase64) {
const insightDump = insightTask.log.dump; const insightDump = insightTask.log?.dump;
const insightContentLength = context.content.length; const insightContentLength = context.content.length;
if (context.screenshotBase64) { if (context.screenshotBase64) {
@ -328,8 +346,9 @@ export const generateAnimationScripts = (
type: 'insight', type: 'insight',
img: context.screenshotBase64, img: context.screenshotBase64,
context: context, context: context,
insightDump: insightDump,
camera: cameraState, camera: cameraState,
highlightElement: insightTask.output?.element || undefined,
searchArea: insightDump?.taskInfo?.searchArea,
duration: duration:
insightContentLength > 20 ? locateDuration : locateDuration * 0.5, insightContentLength > 20 ? locateDuration : locateDuration * 0.5,
insightCameraDuration: locateDuration, insightCameraDuration: locateDuration,