@midscene/visualizer 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +9 -0
- package/README.md +24 -0
- package/dist/es/assets/logo-plain.16842bbc.svg +70 -0
- package/dist/es/assets/logo-plain2.16842bbc.svg +70 -0
- package/dist/es/component/blackboard.css +25 -0
- package/dist/es/component/blackboard.js +256 -0
- package/dist/es/component/color.js +34 -0
- package/dist/es/component/common.css +0 -0
- package/dist/es/component/detail-panel.css +34 -0
- package/dist/es/component/detail-panel.js +106 -0
- package/dist/es/component/detail-side.css +99 -0
- package/dist/es/component/detail-side.js +285 -0
- package/dist/es/component/global-hover-preview.css +19 -0
- package/dist/es/component/global-hover-preview.js +44 -0
- package/dist/es/component/misc.js +24 -0
- package/dist/es/component/panel-title.css +8 -0
- package/dist/es/component/panel-title.js +9 -0
- package/dist/es/component/side-item.js +0 -0
- package/dist/es/component/sidebar.css +87 -0
- package/dist/es/component/sidebar.js +175 -0
- package/dist/es/component/store.js +128 -0
- package/dist/es/component/timeline.css +18 -0
- package/dist/es/component/timeline.js +438 -0
- package/dist/es/index.css +89 -0
- package/dist/es/index.js +174 -0
- package/dist/es/utils.js +76 -0
- package/dist/lib/assets/logo-plain.16842bbc.svg +70 -0
- package/dist/lib/assets/logo-plain2.16842bbc.svg +70 -0
- package/dist/lib/component/blackboard.css +25 -0
- package/dist/lib/component/blackboard.js +286 -0
- package/dist/lib/component/color.js +59 -0
- package/dist/lib/component/common.css +0 -0
- package/dist/lib/component/detail-panel.css +34 -0
- package/dist/lib/component/detail-panel.js +136 -0
- package/dist/lib/component/detail-side.css +99 -0
- package/dist/lib/component/detail-side.js +313 -0
- package/dist/lib/component/global-hover-preview.css +19 -0
- package/dist/lib/component/global-hover-preview.js +64 -0
- package/dist/lib/component/misc.js +48 -0
- package/dist/lib/component/panel-title.css +8 -0
- package/dist/lib/component/panel-title.js +29 -0
- package/dist/lib/component/side-item.js +1 -0
- package/dist/lib/component/sidebar.css +87 -0
- package/dist/lib/component/sidebar.js +198 -0
- package/dist/lib/component/store.js +153 -0
- package/dist/lib/component/timeline.css +18 -0
- package/dist/lib/component/timeline.js +466 -0
- package/dist/lib/index.css +89 -0
- package/dist/lib/index.js +202 -0
- package/dist/lib/utils.js +111 -0
- package/dist/types/component/blackboard.d.ts +4 -0
- package/dist/types/component/color.d.ts +2 -0
- package/dist/types/component/detail-panel.d.ts +4 -0
- package/dist/types/component/detail-side.d.ts +4 -0
- package/dist/types/component/global-hover-preview.d.ts +4 -0
- package/dist/types/component/misc.d.ts +2 -0
- package/dist/types/component/panel-title.d.ts +6 -0
- package/dist/types/component/side-item.d.ts +0 -0
- package/dist/types/component/sidebar.d.ts +4 -0
- package/dist/types/component/store.d.ts +35 -0
- package/dist/types/component/timeline.d.ts +4 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/utils.d.ts +5 -0
- package/docs/index.tsx +6 -0
- package/modern.config.ts +15 -0
- package/package.json +46 -0
- package/src/component/assets/logo-plain.svg +70 -0
- package/src/component/assets/logo-plain2.svg +70 -0
- package/src/component/blackboard.less +37 -0
- package/src/component/blackboard.tsx +293 -0
- package/src/component/color.tsx +34 -0
- package/src/component/common.less +21 -0
- package/src/component/detail-panel.less +47 -0
- package/src/component/detail-panel.tsx +124 -0
- package/src/component/detail-side.less +131 -0
- package/src/component/detail-side.tsx +361 -0
- package/src/component/global-hover-preview.less +23 -0
- package/src/component/global-hover-preview.tsx +50 -0
- package/src/component/misc.tsx +20 -0
- package/src/component/panel-title.less +11 -0
- package/src/component/panel-title.tsx +11 -0
- package/src/component/side-item.tsx +0 -0
- package/src/component/sidebar.less +122 -0
- package/src/component/sidebar.tsx +205 -0
- package/src/component/store.tsx +151 -0
- package/src/component/timeline.less +25 -0
- package/src/component/timeline.tsx +486 -0
- package/src/global.d.ts +11 -0
- package/src/index.less +113 -0
- package/src/index.tsx +210 -0
- package/src/utils.ts +58 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defProps = Object.defineProperties;
|
|
3
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
7
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
+
var __spreadValues = (a, b) => {
|
|
9
|
+
for (var prop in b || (b = {}))
|
|
10
|
+
if (__hasOwnProp.call(b, prop))
|
|
11
|
+
__defNormalProp(a, prop, b[prop]);
|
|
12
|
+
if (__getOwnPropSymbols)
|
|
13
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
14
|
+
if (__propIsEnum.call(b, prop))
|
|
15
|
+
__defNormalProp(a, prop, b[prop]);
|
|
16
|
+
}
|
|
17
|
+
return a;
|
|
18
|
+
};
|
|
19
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
|
+
var __async = (__this, __arguments, generator) => {
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
var fulfilled = (value) => {
|
|
23
|
+
try {
|
|
24
|
+
step(generator.next(value));
|
|
25
|
+
} catch (e) {
|
|
26
|
+
reject(e);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
var rejected = (value) => {
|
|
30
|
+
try {
|
|
31
|
+
step(generator.throw(value));
|
|
32
|
+
} catch (e) {
|
|
33
|
+
reject(e);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
37
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
import { jsx } from "react/jsx-runtime";
|
|
41
|
+
import { useEffect, useMemo, useRef } from "react";
|
|
42
|
+
import * as PIXI from "pixi.js";
|
|
43
|
+
import "./timeline.css";
|
|
44
|
+
import { useAllCurrentTasks, useExecutionDump } from "./store";
|
|
45
|
+
function cloneSprite(sprite) {
|
|
46
|
+
const clonedSprite = new PIXI.Sprite(sprite.texture);
|
|
47
|
+
clonedSprite.position.copyFrom(sprite.position);
|
|
48
|
+
clonedSprite.scale.copyFrom(sprite.scale);
|
|
49
|
+
clonedSprite.rotation = sprite.rotation;
|
|
50
|
+
clonedSprite.alpha = sprite.alpha;
|
|
51
|
+
clonedSprite.visible = sprite.visible;
|
|
52
|
+
return clonedSprite;
|
|
53
|
+
}
|
|
54
|
+
const TimelineWidget = (props) => {
|
|
55
|
+
var _a, _b, _c, _d;
|
|
56
|
+
const domRef = useRef(null);
|
|
57
|
+
const app = useMemo(() => new PIXI.Application(), []);
|
|
58
|
+
const gridsContainer = useMemo(() => new PIXI.Container(), []);
|
|
59
|
+
const screenshotsContainer = useMemo(() => new PIXI.Container(), []);
|
|
60
|
+
const highlightMaskContainer = useMemo(() => new PIXI.Container(), []);
|
|
61
|
+
const containerUpdaterRef = useRef(
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
63
|
+
(_s, _e, _hs, _he) => {
|
|
64
|
+
}
|
|
65
|
+
);
|
|
66
|
+
const indicatorContainer = useMemo(() => new PIXI.Container(), []);
|
|
67
|
+
const allScreenshots = props.screenshots || [];
|
|
68
|
+
const maxTime = allScreenshots[allScreenshots.length - 1].timeOffset;
|
|
69
|
+
const sizeRatio = 2;
|
|
70
|
+
const titleBg = 14540253;
|
|
71
|
+
const sideBg = 15527148;
|
|
72
|
+
const gridTextColor = 0;
|
|
73
|
+
const shotBorderColor = 7829367;
|
|
74
|
+
const gridLineColor = 13421772;
|
|
75
|
+
const gridHighlightColor = 438699;
|
|
76
|
+
const timeContentFontSize = 20;
|
|
77
|
+
const commonPadding = 12;
|
|
78
|
+
const timeTextTop = commonPadding;
|
|
79
|
+
const timeTitleBottom = timeTextTop * 2 + timeContentFontSize;
|
|
80
|
+
const highlightMaskAlpha = 0.6;
|
|
81
|
+
const hoverMaskAlpha = 0.3;
|
|
82
|
+
const closestScreenshotItemOnXY = (x, _y) => {
|
|
83
|
+
let closestScreenshot;
|
|
84
|
+
let closestIndex = -1;
|
|
85
|
+
for (let i = 0; i < allScreenshots.length; i++) {
|
|
86
|
+
const shot = allScreenshots[i];
|
|
87
|
+
if (shot.x <= x) {
|
|
88
|
+
closestScreenshot = allScreenshots[i];
|
|
89
|
+
closestIndex = i;
|
|
90
|
+
} else {
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
closestScreenshot,
|
|
96
|
+
closestIndex
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
useMemo(() => {
|
|
100
|
+
const { startMs, endMs } = props.highlightMask || {};
|
|
101
|
+
const { startMs: hoverStartMs, endMs: hoverEndMs } = props.hoverMask || {};
|
|
102
|
+
const fn = containerUpdaterRef.current;
|
|
103
|
+
fn(startMs, endMs, hoverStartMs, hoverEndMs);
|
|
104
|
+
}, [
|
|
105
|
+
(_a = props.highlightMask) == null ? void 0 : _a.startMs,
|
|
106
|
+
(_b = props.highlightMask) == null ? void 0 : _b.endMs,
|
|
107
|
+
(_c = props.hoverMask) == null ? void 0 : _c.startMs,
|
|
108
|
+
(_d = props.hoverMask) == null ? void 0 : _d.endMs
|
|
109
|
+
]);
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
Promise.resolve(
|
|
112
|
+
(() => __async(void 0, null, function* () {
|
|
113
|
+
var _a2, _b2;
|
|
114
|
+
if (!domRef.current) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const { clientWidth, clientHeight } = domRef.current;
|
|
118
|
+
const canvasWidth = clientWidth * sizeRatio;
|
|
119
|
+
const canvasHeight = clientHeight * sizeRatio;
|
|
120
|
+
let singleGridWidth = 100 * sizeRatio;
|
|
121
|
+
let gridCount = Math.floor(canvasWidth / singleGridWidth);
|
|
122
|
+
const stepCandidate = [
|
|
123
|
+
50,
|
|
124
|
+
100,
|
|
125
|
+
200,
|
|
126
|
+
300,
|
|
127
|
+
500,
|
|
128
|
+
1e3,
|
|
129
|
+
2e3,
|
|
130
|
+
3e3,
|
|
131
|
+
5e3,
|
|
132
|
+
6e3,
|
|
133
|
+
8e3,
|
|
134
|
+
9e3,
|
|
135
|
+
1e4,
|
|
136
|
+
2e4,
|
|
137
|
+
3e4,
|
|
138
|
+
4e4,
|
|
139
|
+
6e4,
|
|
140
|
+
9e4,
|
|
141
|
+
12e3,
|
|
142
|
+
3e5
|
|
143
|
+
];
|
|
144
|
+
let timeStep = stepCandidate[0];
|
|
145
|
+
for (let i = stepCandidate.length - 1; i >= 0; i--) {
|
|
146
|
+
if (gridCount * stepCandidate[i] >= maxTime) {
|
|
147
|
+
timeStep = stepCandidate[i];
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const gridRatio = maxTime / (gridCount * timeStep);
|
|
151
|
+
if (gridRatio <= 0.8) {
|
|
152
|
+
singleGridWidth = Math.floor(singleGridWidth * (1 / gridRatio) * 0.9);
|
|
153
|
+
gridCount = Math.floor(canvasWidth / singleGridWidth);
|
|
154
|
+
}
|
|
155
|
+
const leftForTimeOffset = (timeOffset) => {
|
|
156
|
+
return Math.floor(singleGridWidth * timeOffset / timeStep);
|
|
157
|
+
};
|
|
158
|
+
const timeOffsetForLeft = (left) => {
|
|
159
|
+
return Math.floor(left * timeStep / singleGridWidth);
|
|
160
|
+
};
|
|
161
|
+
yield app.init({
|
|
162
|
+
width: canvasWidth,
|
|
163
|
+
height: canvasHeight,
|
|
164
|
+
backgroundColor: sideBg
|
|
165
|
+
});
|
|
166
|
+
if (!domRef.current) {
|
|
167
|
+
app.destroy();
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
domRef.current.replaceChildren(app.canvas);
|
|
171
|
+
const pixiTextForNumber = (num) => {
|
|
172
|
+
const textContent = `${num}ms`;
|
|
173
|
+
const text = new PIXI.Text(`${textContent}`, {
|
|
174
|
+
fontSize: timeContentFontSize,
|
|
175
|
+
fill: gridTextColor
|
|
176
|
+
});
|
|
177
|
+
return text;
|
|
178
|
+
};
|
|
179
|
+
gridsContainer.removeChildren();
|
|
180
|
+
const titleBgSection = new PIXI.Graphics();
|
|
181
|
+
titleBgSection.beginFill(titleBg);
|
|
182
|
+
titleBgSection.drawRect(0, 0, canvasWidth, timeTitleBottom);
|
|
183
|
+
titleBgSection.endFill();
|
|
184
|
+
gridsContainer.addChild(titleBgSection);
|
|
185
|
+
const gridHeight = canvasHeight;
|
|
186
|
+
for (let i = 1; i <= gridCount; i++) {
|
|
187
|
+
const gridLine = new PIXI.Graphics();
|
|
188
|
+
const gridLineLeft = leftForTimeOffset(i * timeStep);
|
|
189
|
+
gridLine.beginFill(gridLineColor);
|
|
190
|
+
gridLine.drawRect(gridLineLeft, 0, sizeRatio, gridHeight);
|
|
191
|
+
gridLine.endFill();
|
|
192
|
+
gridsContainer.addChild(gridLine);
|
|
193
|
+
const text = pixiTextForNumber(i * timeStep);
|
|
194
|
+
const textLeft = gridLineLeft - text.width - commonPadding;
|
|
195
|
+
text.x = textLeft;
|
|
196
|
+
text.y = timeTextTop;
|
|
197
|
+
gridsContainer.addChild(text);
|
|
198
|
+
}
|
|
199
|
+
app.stage.addChild(gridsContainer);
|
|
200
|
+
if (!allScreenshots.length) {
|
|
201
|
+
console.warn("No screenshots found");
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const shotContainers = [];
|
|
205
|
+
screenshotsContainer.removeChildren();
|
|
206
|
+
const screenshotTop = timeTitleBottom + commonPadding * 1.5;
|
|
207
|
+
const screenshotMaxHeight = canvasHeight - screenshotTop - commonPadding * 1.5;
|
|
208
|
+
allScreenshots.forEach((screenshot, index) => {
|
|
209
|
+
const container = new PIXI.Container();
|
|
210
|
+
shotContainers.push(container);
|
|
211
|
+
app.stage.addChild(container);
|
|
212
|
+
const img = new Image();
|
|
213
|
+
img.src = screenshot.img;
|
|
214
|
+
img.onload = () => {
|
|
215
|
+
const screenshotTexture = PIXI.Texture.from(img);
|
|
216
|
+
const screenshotSprite = new PIXI.Sprite(screenshotTexture);
|
|
217
|
+
const originalWidth = img.width;
|
|
218
|
+
const originalHeight = img.height;
|
|
219
|
+
const screenshotHeight = screenshotMaxHeight;
|
|
220
|
+
const screenshotWidth = Math.floor(screenshotHeight / originalHeight * originalWidth);
|
|
221
|
+
const screenshotX = leftForTimeOffset(screenshot.timeOffset);
|
|
222
|
+
allScreenshots[index].x = screenshotX;
|
|
223
|
+
allScreenshots[index].y = screenshotTop;
|
|
224
|
+
allScreenshots[index].width = screenshotWidth;
|
|
225
|
+
allScreenshots[index].height = screenshotMaxHeight;
|
|
226
|
+
const border = new PIXI.Graphics();
|
|
227
|
+
border.lineStyle(sizeRatio, shotBorderColor, 1);
|
|
228
|
+
border.drawRect(screenshotX, screenshotTop, screenshotWidth, screenshotMaxHeight);
|
|
229
|
+
border.endFill();
|
|
230
|
+
container.addChild(border);
|
|
231
|
+
screenshotSprite.x = screenshotX;
|
|
232
|
+
screenshotSprite.y = screenshotTop;
|
|
233
|
+
screenshotSprite.width = screenshotWidth;
|
|
234
|
+
screenshotSprite.height = screenshotMaxHeight;
|
|
235
|
+
container.addChild(screenshotSprite);
|
|
236
|
+
};
|
|
237
|
+
});
|
|
238
|
+
const highlightMaskUpdater = (start, end, hoverStart, hoverEnd) => {
|
|
239
|
+
highlightMaskContainer.removeChildren();
|
|
240
|
+
const mask = (start2, end2, alpha) => {
|
|
241
|
+
if (typeof start2 === "undefined" || typeof end2 === "undefined" || end2 === 0) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const leftBorder = new PIXI.Graphics();
|
|
245
|
+
leftBorder.beginFill(gridHighlightColor, 1);
|
|
246
|
+
leftBorder.drawRect(leftForTimeOffset(start2), 0, sizeRatio, canvasHeight);
|
|
247
|
+
leftBorder.endFill();
|
|
248
|
+
highlightMaskContainer.addChild(leftBorder);
|
|
249
|
+
const rightBorder = new PIXI.Graphics();
|
|
250
|
+
rightBorder.beginFill(gridHighlightColor, 1);
|
|
251
|
+
rightBorder.drawRect(leftForTimeOffset(end2), 0, sizeRatio, canvasHeight);
|
|
252
|
+
rightBorder.endFill();
|
|
253
|
+
highlightMaskContainer.addChild(rightBorder);
|
|
254
|
+
const mask2 = new PIXI.Graphics();
|
|
255
|
+
mask2.beginFill(gridHighlightColor, alpha);
|
|
256
|
+
mask2.drawRect(
|
|
257
|
+
leftForTimeOffset(start2),
|
|
258
|
+
0,
|
|
259
|
+
leftForTimeOffset(end2) - leftForTimeOffset(start2),
|
|
260
|
+
canvasHeight
|
|
261
|
+
);
|
|
262
|
+
mask2.endFill();
|
|
263
|
+
highlightMaskContainer.addChild(mask2);
|
|
264
|
+
};
|
|
265
|
+
mask(start, end, highlightMaskAlpha);
|
|
266
|
+
mask(hoverStart, hoverEnd, hoverMaskAlpha);
|
|
267
|
+
};
|
|
268
|
+
highlightMaskUpdater((_a2 = props.highlightMask) == null ? void 0 : _a2.startMs, (_b2 = props.highlightMask) == null ? void 0 : _b2.endMs, 0, 0);
|
|
269
|
+
containerUpdaterRef.current = highlightMaskUpdater;
|
|
270
|
+
app.stage.interactive = true;
|
|
271
|
+
const onPointerMove = (event) => {
|
|
272
|
+
var _a3, _b3;
|
|
273
|
+
const x = event.offsetX * sizeRatio;
|
|
274
|
+
const y = event.offsetY * sizeRatio;
|
|
275
|
+
indicatorContainer.removeChildren();
|
|
276
|
+
const { closestScreenshot, closestIndex } = closestScreenshotItemOnXY(x, y);
|
|
277
|
+
if (closestIndex < 0) {
|
|
278
|
+
(_a3 = props.onUnhighlight) == null ? void 0 : _a3.call(props);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
const closestContainer = shotContainers[closestIndex];
|
|
282
|
+
closestContainer.children.forEach((child) => {
|
|
283
|
+
if (child instanceof PIXI.Sprite) {
|
|
284
|
+
const newSpirit = new PIXI.Graphics();
|
|
285
|
+
newSpirit.lineStyle(2, gridHighlightColor, 1);
|
|
286
|
+
newSpirit.drawRect(
|
|
287
|
+
x,
|
|
288
|
+
// follow mouse
|
|
289
|
+
closestScreenshot.y,
|
|
290
|
+
closestScreenshot.width,
|
|
291
|
+
closestScreenshot.height
|
|
292
|
+
);
|
|
293
|
+
newSpirit.endFill();
|
|
294
|
+
indicatorContainer.addChild(newSpirit);
|
|
295
|
+
const screenshotSpirit = cloneSprite(child);
|
|
296
|
+
screenshotSpirit.x = x;
|
|
297
|
+
indicatorContainer.addChild(screenshotSpirit);
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
const indicator = new PIXI.Graphics();
|
|
301
|
+
indicator.beginFill(gridHighlightColor, 1);
|
|
302
|
+
indicator.drawRect(x - 1, 0, 3, canvasHeight);
|
|
303
|
+
indicator.endFill();
|
|
304
|
+
indicatorContainer.addChild(indicator);
|
|
305
|
+
const text = pixiTextForNumber(timeOffsetForLeft(x));
|
|
306
|
+
text.x = x + 5;
|
|
307
|
+
text.y = timeTextTop;
|
|
308
|
+
const textBg = new PIXI.Graphics();
|
|
309
|
+
textBg.beginFill(titleBg, 1);
|
|
310
|
+
textBg.drawRect(text.x, text.y, text.width + 10, text.height);
|
|
311
|
+
textBg.endFill();
|
|
312
|
+
indicatorContainer.addChild(textBg);
|
|
313
|
+
indicatorContainer.addChild(text);
|
|
314
|
+
(_b3 = props.onHighlight) == null ? void 0 : _b3.call(props, {
|
|
315
|
+
mouseX: x / sizeRatio,
|
|
316
|
+
mouseY: y / sizeRatio,
|
|
317
|
+
item: closestScreenshot
|
|
318
|
+
});
|
|
319
|
+
};
|
|
320
|
+
const onPointerOut = () => {
|
|
321
|
+
var _a3;
|
|
322
|
+
indicatorContainer.removeChildren();
|
|
323
|
+
(_a3 = props.onUnhighlight) == null ? void 0 : _a3.call(props);
|
|
324
|
+
};
|
|
325
|
+
const onPointerTap = (event) => {
|
|
326
|
+
var _a3;
|
|
327
|
+
const x = event.offsetX * sizeRatio;
|
|
328
|
+
const y = event.offsetY * sizeRatio;
|
|
329
|
+
const { closestScreenshot } = closestScreenshotItemOnXY(x, y);
|
|
330
|
+
if (closestScreenshot) {
|
|
331
|
+
(_a3 = props.onTap) == null ? void 0 : _a3.call(props, closestScreenshot);
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
app.stage.addChild(screenshotsContainer);
|
|
335
|
+
app.stage.addChild(highlightMaskContainer);
|
|
336
|
+
app.stage.addChild(indicatorContainer);
|
|
337
|
+
const canvas = app.view;
|
|
338
|
+
canvas.addEventListener("pointermove", onPointerMove);
|
|
339
|
+
canvas.addEventListener("pointerout", onPointerOut);
|
|
340
|
+
canvas.addEventListener("pointerdown", onPointerTap);
|
|
341
|
+
}))()
|
|
342
|
+
);
|
|
343
|
+
}, []);
|
|
344
|
+
return /* @__PURE__ */ jsx("div", { className: "timeline-canvas-wrapper", ref: domRef });
|
|
345
|
+
};
|
|
346
|
+
const Timeline = () => {
|
|
347
|
+
const allTasks = useAllCurrentTasks();
|
|
348
|
+
const wrapper = useRef(null);
|
|
349
|
+
const setActiveTask = useExecutionDump((store) => store.setActiveTask);
|
|
350
|
+
const activeTask = useExecutionDump((store) => store.activeTask);
|
|
351
|
+
const hoverTask = useExecutionDump((store) => store.hoverTask);
|
|
352
|
+
const setHoverTask = useExecutionDump((store) => store.setHoverTask);
|
|
353
|
+
const setHoverPreviewConfig = useExecutionDump((store) => store.setHoverPreviewConfig);
|
|
354
|
+
let startingTime = -1;
|
|
355
|
+
let idCount = 1;
|
|
356
|
+
const idTaskMap = {};
|
|
357
|
+
const allScreenshots = allTasks.reduce((acc, current) => {
|
|
358
|
+
var _a;
|
|
359
|
+
const recorders = current.recorder || [];
|
|
360
|
+
recorders.forEach((item) => {
|
|
361
|
+
if (startingTime === -1 || startingTime > item.ts) {
|
|
362
|
+
startingTime = item.ts;
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
if (((_a = current.timing) == null ? void 0 : _a.start) && (startingTime === -1 || startingTime > current.timing.start)) {
|
|
366
|
+
startingTime = current.timing.start;
|
|
367
|
+
}
|
|
368
|
+
const recorderItemWithId = recorders.map((item) => {
|
|
369
|
+
const idStr = `id_${idCount++}`;
|
|
370
|
+
idTaskMap[idStr] = current;
|
|
371
|
+
return __spreadProps(__spreadValues({}, item), {
|
|
372
|
+
id: idStr
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
return acc.concat(recorderItemWithId || []);
|
|
376
|
+
}, []).filter((item) => {
|
|
377
|
+
return item.screenshot;
|
|
378
|
+
}).map((recorderItem) => {
|
|
379
|
+
return {
|
|
380
|
+
id: recorderItem.id,
|
|
381
|
+
img: recorderItem.screenshot,
|
|
382
|
+
timeOffset: recorderItem.ts - startingTime
|
|
383
|
+
};
|
|
384
|
+
}).sort((a, b) => a.timeOffset - b.timeOffset);
|
|
385
|
+
const itemOnTap = (item) => {
|
|
386
|
+
const task = idTaskMap[item.id];
|
|
387
|
+
if (task) {
|
|
388
|
+
setActiveTask(task);
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
const onHighlightItem = (param) => {
|
|
392
|
+
var _a;
|
|
393
|
+
const { mouseX, item } = param;
|
|
394
|
+
const refBounding = (_a = wrapper.current) == null ? void 0 : _a.getBoundingClientRect();
|
|
395
|
+
const task = idTaskMap[item.id];
|
|
396
|
+
if (task) {
|
|
397
|
+
setHoverTask(task);
|
|
398
|
+
setHoverPreviewConfig({
|
|
399
|
+
x: mouseX + ((refBounding == null ? void 0 : refBounding.left) || 0),
|
|
400
|
+
y: ((refBounding == null ? void 0 : refBounding.bottom) || 1) - 1
|
|
401
|
+
});
|
|
402
|
+
} else {
|
|
403
|
+
setHoverTask(null);
|
|
404
|
+
setHoverPreviewConfig(null);
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
const unhighlight = () => {
|
|
408
|
+
setHoverTask(null);
|
|
409
|
+
setHoverPreviewConfig(null);
|
|
410
|
+
};
|
|
411
|
+
const maskConfigForTask = (task) => {
|
|
412
|
+
var _a, _b;
|
|
413
|
+
if (!task) {
|
|
414
|
+
return void 0;
|
|
415
|
+
}
|
|
416
|
+
return ((_a = task.timing) == null ? void 0 : _a.start) && ((_b = task.timing) == null ? void 0 : _b.end) ? {
|
|
417
|
+
startMs: task.timing.start - startingTime || 0,
|
|
418
|
+
endMs: task.timing.end - startingTime || 0
|
|
419
|
+
} : void 0;
|
|
420
|
+
};
|
|
421
|
+
const highlightMaskConfig = maskConfigForTask(activeTask);
|
|
422
|
+
const hoverMaskConfig = maskConfigForTask(hoverTask);
|
|
423
|
+
return /* @__PURE__ */ jsx("div", { className: "timeline-wrapper", ref: wrapper, children: /* @__PURE__ */ jsx(
|
|
424
|
+
TimelineWidget,
|
|
425
|
+
{
|
|
426
|
+
screenshots: allScreenshots,
|
|
427
|
+
onTap: itemOnTap,
|
|
428
|
+
onHighlight: onHighlightItem,
|
|
429
|
+
onUnhighlight: unhighlight,
|
|
430
|
+
highlightMask: highlightMaskConfig,
|
|
431
|
+
hoverMask: hoverMaskConfig
|
|
432
|
+
}
|
|
433
|
+
) });
|
|
434
|
+
};
|
|
435
|
+
var timeline_default = Timeline;
|
|
436
|
+
export {
|
|
437
|
+
timeline_default as default
|
|
438
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
html,
|
|
2
|
+
body {
|
|
3
|
+
padding: 0;
|
|
4
|
+
margin: 0;
|
|
5
|
+
font-family:
|
|
6
|
+
PingFang SC,
|
|
7
|
+
Hiragino Sans GB,
|
|
8
|
+
Microsoft YaHei,
|
|
9
|
+
Arial,
|
|
10
|
+
sans-serif;
|
|
11
|
+
font-size: 14px;
|
|
12
|
+
}
|
|
13
|
+
:root {
|
|
14
|
+
--modern-sidebar-width: 0 !important;
|
|
15
|
+
--modern-aside-width: 0 !important;
|
|
16
|
+
--modern-preview-padding: 0 !important;
|
|
17
|
+
}
|
|
18
|
+
.modern-doc-layout,
|
|
19
|
+
.modern-doc {
|
|
20
|
+
width: 100% !important;
|
|
21
|
+
margin: 0 !important;
|
|
22
|
+
padding: 0 !important;
|
|
23
|
+
height: 100vh;
|
|
24
|
+
}
|
|
25
|
+
.modern-sidebar,
|
|
26
|
+
header.w-full {
|
|
27
|
+
display: none !important;
|
|
28
|
+
}
|
|
29
|
+
.modern-doc-container {
|
|
30
|
+
padding: 0 !important;
|
|
31
|
+
}
|
|
32
|
+
footer.mt-8 {
|
|
33
|
+
display: none;
|
|
34
|
+
}
|
|
35
|
+
.page-container {
|
|
36
|
+
display: flex;
|
|
37
|
+
flex-direction: column;
|
|
38
|
+
height: 100%;
|
|
39
|
+
color: #000;
|
|
40
|
+
}
|
|
41
|
+
.ant-layout {
|
|
42
|
+
flex-grow: 1;
|
|
43
|
+
height: 100%;
|
|
44
|
+
}
|
|
45
|
+
.main-right {
|
|
46
|
+
display: flex;
|
|
47
|
+
flex-direction: column;
|
|
48
|
+
width: 100%;
|
|
49
|
+
height: 100%;
|
|
50
|
+
box-sizing: border-box;
|
|
51
|
+
}
|
|
52
|
+
.main-right .main-content {
|
|
53
|
+
display: flex;
|
|
54
|
+
flex-direction: row;
|
|
55
|
+
flex-grow: 1;
|
|
56
|
+
overflow: hidden;
|
|
57
|
+
background: #ECECEC;
|
|
58
|
+
}
|
|
59
|
+
.main-right.uploader-wrapper {
|
|
60
|
+
box-sizing: border-box;
|
|
61
|
+
margin: auto;
|
|
62
|
+
max-width: 800px;
|
|
63
|
+
flex-direction: column;
|
|
64
|
+
justify-content: center;
|
|
65
|
+
}
|
|
66
|
+
.main-right.uploader-wrapper .uploader {
|
|
67
|
+
width: 100%;
|
|
68
|
+
}
|
|
69
|
+
.main-right.uploader-wrapper .demo-loader {
|
|
70
|
+
width: 100%;
|
|
71
|
+
text-align: center;
|
|
72
|
+
margin-top: 10px;
|
|
73
|
+
}
|
|
74
|
+
.main-right .main-canvas-container {
|
|
75
|
+
flex-grow: 1;
|
|
76
|
+
height: 100%;
|
|
77
|
+
background: #F5F5F5;
|
|
78
|
+
overflow-x: hidden;
|
|
79
|
+
overflow-y: scroll;
|
|
80
|
+
border-left: 1px solid #CCCCCC;
|
|
81
|
+
}
|
|
82
|
+
.main-right .main-side {
|
|
83
|
+
box-sizing: border-box;
|
|
84
|
+
overflow-y: scroll;
|
|
85
|
+
}
|
|
86
|
+
.main-right .json-content {
|
|
87
|
+
word-wrap: break-word;
|
|
88
|
+
white-space: pre-wrap;
|
|
89
|
+
}
|
package/dist/es/index.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defProps = Object.defineProperties;
|
|
3
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
7
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
+
var __spreadValues = (a, b) => {
|
|
9
|
+
for (var prop in b || (b = {}))
|
|
10
|
+
if (__hasOwnProp.call(b, prop))
|
|
11
|
+
__defNormalProp(a, prop, b[prop]);
|
|
12
|
+
if (__getOwnPropSymbols)
|
|
13
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
14
|
+
if (__propIsEnum.call(b, prop))
|
|
15
|
+
__defNormalProp(a, prop, b[prop]);
|
|
16
|
+
}
|
|
17
|
+
return a;
|
|
18
|
+
};
|
|
19
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
21
|
+
import "./index.css";
|
|
22
|
+
import { ConfigProvider, message, Upload, Button } from "antd";
|
|
23
|
+
import { useEffect, useRef, useState } from "react";
|
|
24
|
+
import { Helmet } from "@modern-js/runtime/head";
|
|
25
|
+
import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
|
|
26
|
+
import Timeline from "./component/timeline";
|
|
27
|
+
import DetailPanel from "./component/detail-panel";
|
|
28
|
+
import logo from "./assets/logo-plain.16842bbc.svg";
|
|
29
|
+
import GlobalHoverPreview from "./component/global-hover-preview";
|
|
30
|
+
import { useExecutionDump } from "./component/store";
|
|
31
|
+
import DetailSide from "./component/detail-side";
|
|
32
|
+
import Sidebar from "./component/sidebar";
|
|
33
|
+
const { Dragger } = Upload;
|
|
34
|
+
const Index = () => {
|
|
35
|
+
const executionDump = useExecutionDump((store) => store.dump);
|
|
36
|
+
const setGroupedDump = useExecutionDump((store) => store.setGroupedDump);
|
|
37
|
+
const reset = useExecutionDump((store) => store.reset);
|
|
38
|
+
const [mainLayoutChangeFlag, setMainLayoutChangeFlag] = useState(0);
|
|
39
|
+
const mainLayoutChangedRef = useRef(false);
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
return () => {
|
|
42
|
+
reset();
|
|
43
|
+
};
|
|
44
|
+
}, []);
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
const onResize = () => {
|
|
47
|
+
setMainLayoutChangeFlag((prev) => prev + 1);
|
|
48
|
+
};
|
|
49
|
+
window.addEventListener("resize", onResize);
|
|
50
|
+
return () => {
|
|
51
|
+
window.removeEventListener("resize", onResize);
|
|
52
|
+
};
|
|
53
|
+
}, []);
|
|
54
|
+
const uploadProps = {
|
|
55
|
+
name: "file",
|
|
56
|
+
multiple: false,
|
|
57
|
+
capture: false,
|
|
58
|
+
customRequest: () => {
|
|
59
|
+
},
|
|
60
|
+
beforeUpload(file) {
|
|
61
|
+
const ifValidFile = file.name.endsWith("web-dump.json");
|
|
62
|
+
if (!ifValidFile) {
|
|
63
|
+
message.error("invalid file extension");
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
const reader = new FileReader();
|
|
67
|
+
reader.readAsText(file);
|
|
68
|
+
reader.onload = (e) => {
|
|
69
|
+
var _a;
|
|
70
|
+
const result = (_a = e.target) == null ? void 0 : _a.result;
|
|
71
|
+
if (typeof result === "string") {
|
|
72
|
+
try {
|
|
73
|
+
const data = JSON.parse(result);
|
|
74
|
+
setGroupedDump(data);
|
|
75
|
+
} catch (e2) {
|
|
76
|
+
console.error(e2);
|
|
77
|
+
message.error("failed to parse dump data", e2.message);
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
message.error("Invalid dump file");
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
const loadTasksDemo = () => {
|
|
87
|
+
};
|
|
88
|
+
const loadInsightDemo = () => {
|
|
89
|
+
};
|
|
90
|
+
let mainContent;
|
|
91
|
+
if (!executionDump) {
|
|
92
|
+
mainContent = /* @__PURE__ */ jsxs("div", { className: "main-right uploader-wrapper", children: [
|
|
93
|
+
/* @__PURE__ */ jsxs(Dragger, __spreadProps(__spreadValues({ className: "uploader" }, uploadProps), { children: [
|
|
94
|
+
/* @__PURE__ */ jsx("p", { className: "ant-upload-drag-icon", children: /* @__PURE__ */ jsx("img", { src: logo, alt: "Logo", style: { width: 100, height: 100, margin: "auto" } }) }),
|
|
95
|
+
/* @__PURE__ */ jsxs("p", { className: "ant-upload-text", children: [
|
|
96
|
+
"Click or drag the",
|
|
97
|
+
" ",
|
|
98
|
+
/* @__PURE__ */ jsx("b", { children: /* @__PURE__ */ jsx("i", { children: ".web-dump.json" }) }),
|
|
99
|
+
" ",
|
|
100
|
+
"file into this area."
|
|
101
|
+
] }),
|
|
102
|
+
/* @__PURE__ */ jsxs("p", { className: "ant-upload-text", children: [
|
|
103
|
+
"The latest dump file is usually placed in",
|
|
104
|
+
" ",
|
|
105
|
+
/* @__PURE__ */ jsx("b", { children: /* @__PURE__ */ jsx("i", { children: "./midscene_run/" }) })
|
|
106
|
+
] }),
|
|
107
|
+
/* @__PURE__ */ jsx("p", { className: "ant-upload-text", children: "All data will be processed locally by the browser. No data will be sent to the server." })
|
|
108
|
+
] })),
|
|
109
|
+
/* @__PURE__ */ jsxs("div", { className: "demo-loader", children: [
|
|
110
|
+
/* @__PURE__ */ jsx(Button, { type: "link", onClick: loadTasksDemo, children: "Load Tasks Demo" }),
|
|
111
|
+
/* @__PURE__ */ jsx(Button, { type: "link", onClick: loadInsightDemo, children: "Load Insight Demo" })
|
|
112
|
+
] })
|
|
113
|
+
] });
|
|
114
|
+
} else {
|
|
115
|
+
mainContent = /* @__PURE__ */ jsxs(
|
|
116
|
+
PanelGroup,
|
|
117
|
+
{
|
|
118
|
+
autoSaveId: "main-page-layout",
|
|
119
|
+
direction: "horizontal",
|
|
120
|
+
onLayout: () => {
|
|
121
|
+
if (!mainLayoutChangedRef.current) {
|
|
122
|
+
setMainLayoutChangeFlag((prev) => prev + 1);
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
children: [
|
|
126
|
+
/* @__PURE__ */ jsx(Panel, { maxSize: 95, children: /* @__PURE__ */ jsx(Sidebar, {}) }),
|
|
127
|
+
/* @__PURE__ */ jsx(
|
|
128
|
+
PanelResizeHandle,
|
|
129
|
+
{
|
|
130
|
+
onDragging: (isChanging) => {
|
|
131
|
+
if (mainLayoutChangedRef.current && !isChanging) {
|
|
132
|
+
setMainLayoutChangeFlag((prev) => prev + 1);
|
|
133
|
+
}
|
|
134
|
+
mainLayoutChangedRef.current = isChanging;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
),
|
|
138
|
+
/* @__PURE__ */ jsx(Panel, { defaultSize: 80, maxSize: 95, children: /* @__PURE__ */ jsxs("div", { className: "main-right", children: [
|
|
139
|
+
/* @__PURE__ */ jsx(Timeline, {}, mainLayoutChangeFlag),
|
|
140
|
+
/* @__PURE__ */ jsx("div", { className: "main-content", children: /* @__PURE__ */ jsxs(PanelGroup, { autoSaveId: "page-detail-layout", direction: "horizontal", children: [
|
|
141
|
+
/* @__PURE__ */ jsx(Panel, { maxSize: 95, children: /* @__PURE__ */ jsx("div", { className: "main-side", children: /* @__PURE__ */ jsx(DetailSide, {}) }) }),
|
|
142
|
+
/* @__PURE__ */ jsx(PanelResizeHandle, {}),
|
|
143
|
+
/* @__PURE__ */ jsx(Panel, { defaultSize: 75, maxSize: 95, children: /* @__PURE__ */ jsx("div", { className: "main-canvas-container", children: /* @__PURE__ */ jsx(DetailPanel, {}) }) })
|
|
144
|
+
] }) })
|
|
145
|
+
] }) })
|
|
146
|
+
]
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
return /* @__PURE__ */ jsxs(
|
|
151
|
+
ConfigProvider,
|
|
152
|
+
{
|
|
153
|
+
theme: {
|
|
154
|
+
components: {
|
|
155
|
+
Layout: {
|
|
156
|
+
headerHeight: 60,
|
|
157
|
+
headerPadding: "0 30px",
|
|
158
|
+
headerBg: "#FFF",
|
|
159
|
+
bodyBg: "#FFF"
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
children: [
|
|
164
|
+
/* @__PURE__ */ jsx(Helmet, { children: /* @__PURE__ */ jsx("title", { children: "MidScene.js - Visualization Tool" }) }),
|
|
165
|
+
/* @__PURE__ */ jsx("div", { className: "page-container", children: mainContent }),
|
|
166
|
+
/* @__PURE__ */ jsx(GlobalHoverPreview, {})
|
|
167
|
+
]
|
|
168
|
+
}
|
|
169
|
+
);
|
|
170
|
+
};
|
|
171
|
+
var src_default = Index;
|
|
172
|
+
export {
|
|
173
|
+
src_default as default
|
|
174
|
+
};
|