@midscene/visualizer 1.5.2 → 1.5.3-beta-20260305031559.0
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/dist/es/component/blackboard/index.css +82 -4
- package/dist/es/component/blackboard/index.mjs +73 -301
- package/dist/es/component/player/index.css +144 -119
- package/dist/es/component/player/index.mjs +468 -830
- package/dist/es/component/player/remotion/StepScene.mjs +190 -0
- package/dist/es/component/player/remotion/derive-frame-state.mjs +207 -0
- package/dist/es/component/player/remotion/export-branded-video.mjs +210 -0
- package/dist/es/component/player/remotion/frame-calculator.mjs +149 -0
- package/dist/es/component/player/use-frame-player.mjs +88 -0
- package/dist/es/component/universal-playground/index.mjs +14 -1
- package/dist/es/hooks/usePlaygroundExecution.mjs +11 -7
- package/dist/es/store/store.mjs +9 -0
- package/dist/es/utils/replay-scripts.mjs +2 -1
- package/dist/lib/component/blackboard/index.css +82 -4
- package/dist/lib/component/blackboard/index.js +73 -307
- package/dist/lib/component/player/index.css +144 -119
- package/dist/lib/component/player/index.js +466 -828
- package/dist/lib/component/player/remotion/StepScene.js +224 -0
- package/dist/lib/component/player/remotion/derive-frame-state.js +241 -0
- package/dist/lib/component/player/remotion/export-branded-video.js +244 -0
- package/dist/lib/component/player/remotion/frame-calculator.js +186 -0
- package/dist/lib/component/player/use-frame-player.js +122 -0
- package/dist/lib/component/universal-playground/index.js +14 -1
- package/dist/lib/hooks/usePlaygroundExecution.js +11 -7
- package/dist/lib/store/store.js +9 -0
- package/dist/lib/utils/replay-scripts.js +2 -1
- package/dist/types/component/blackboard/index.d.ts +0 -4
- package/dist/types/component/player/index.d.ts +0 -1
- package/dist/types/component/player/remotion/StepScene.d.ts +9 -0
- package/dist/types/component/player/remotion/derive-frame-state.d.ts +38 -0
- package/dist/types/component/player/remotion/export-branded-video.d.ts +2 -0
- package/dist/types/component/player/remotion/frame-calculator.d.ts +35 -0
- package/dist/types/component/player/use-frame-player.d.ts +17 -0
- package/dist/types/hooks/usePlaygroundExecution.d.ts +15 -1
- package/dist/types/store/store.d.ts +2 -0
- package/dist/types/utils/replay-scripts.d.ts +1 -0
- package/package.json +5 -8
- package/dist/es/utils/pixi-loader.mjs +0 -42
- package/dist/lib/utils/pixi-loader.js +0 -82
- package/dist/types/utils/pixi-loader.d.ts +0 -5
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
const FPS = 30;
|
|
2
|
+
function calculateFrameMap(scripts) {
|
|
3
|
+
let baseImageWidth = 1920;
|
|
4
|
+
let baseImageHeight = 1080;
|
|
5
|
+
for (const s of scripts)if (('img' === s.type || 'insight' === s.type) && s.img) {
|
|
6
|
+
baseImageWidth = s.imageWidth || 1920;
|
|
7
|
+
baseImageHeight = s.imageHeight || 1080;
|
|
8
|
+
break;
|
|
9
|
+
}
|
|
10
|
+
const scriptFrames = [];
|
|
11
|
+
let currentFrame = 0;
|
|
12
|
+
for (const script of scripts){
|
|
13
|
+
const durationMs = script.duration;
|
|
14
|
+
switch(script.type){
|
|
15
|
+
case 'sleep':
|
|
16
|
+
{
|
|
17
|
+
const frames = Math.ceil(durationMs / 1000 * FPS);
|
|
18
|
+
scriptFrames.push({
|
|
19
|
+
type: 'sleep',
|
|
20
|
+
startFrame: currentFrame,
|
|
21
|
+
durationInFrames: frames,
|
|
22
|
+
title: script.title,
|
|
23
|
+
subTitle: script.subTitle,
|
|
24
|
+
taskId: script.taskId
|
|
25
|
+
});
|
|
26
|
+
currentFrame += frames;
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
case 'img':
|
|
30
|
+
{
|
|
31
|
+
const frames = Math.max(Math.ceil(durationMs / 1000 * FPS), 1);
|
|
32
|
+
const camera = script.camera;
|
|
33
|
+
const iw = script.imageWidth || baseImageWidth;
|
|
34
|
+
const ih = script.imageHeight || baseImageHeight;
|
|
35
|
+
const sf = {
|
|
36
|
+
type: 'img',
|
|
37
|
+
startFrame: currentFrame,
|
|
38
|
+
durationInFrames: frames,
|
|
39
|
+
img: script.img,
|
|
40
|
+
imageWidth: iw,
|
|
41
|
+
imageHeight: ih,
|
|
42
|
+
title: script.title,
|
|
43
|
+
subTitle: script.subTitle,
|
|
44
|
+
taskId: script.taskId
|
|
45
|
+
};
|
|
46
|
+
if (camera) {
|
|
47
|
+
var _camera_pointerLeft, _camera_pointerTop;
|
|
48
|
+
sf.cameraTarget = {
|
|
49
|
+
left: camera.left,
|
|
50
|
+
top: camera.top,
|
|
51
|
+
width: camera.width,
|
|
52
|
+
pointerLeft: null != (_camera_pointerLeft = camera.pointerLeft) ? _camera_pointerLeft : Math.round(iw / 2),
|
|
53
|
+
pointerTop: null != (_camera_pointerTop = camera.pointerTop) ? _camera_pointerTop : Math.round(ih / 2)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
scriptFrames.push(sf);
|
|
57
|
+
currentFrame += frames;
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
case 'insight':
|
|
61
|
+
{
|
|
62
|
+
const insightPhaseFrames = Math.max(Math.ceil(durationMs / 1000 * FPS), 1);
|
|
63
|
+
const cameraDurationMs = script.insightCameraDuration || 0;
|
|
64
|
+
const cameraPhaseFrames = Math.ceil(cameraDurationMs / 1000 * FPS);
|
|
65
|
+
const totalFrames = insightPhaseFrames + cameraPhaseFrames;
|
|
66
|
+
const iw = script.imageWidth || baseImageWidth;
|
|
67
|
+
const ih = script.imageHeight || baseImageHeight;
|
|
68
|
+
const camera = script.camera;
|
|
69
|
+
const sf = {
|
|
70
|
+
type: 'insight',
|
|
71
|
+
startFrame: currentFrame,
|
|
72
|
+
durationInFrames: totalFrames,
|
|
73
|
+
img: script.img,
|
|
74
|
+
imageWidth: iw,
|
|
75
|
+
imageHeight: ih,
|
|
76
|
+
insightPhaseFrames,
|
|
77
|
+
cameraPhaseFrames,
|
|
78
|
+
highlightElement: script.highlightElement,
|
|
79
|
+
searchArea: script.searchArea,
|
|
80
|
+
title: script.title,
|
|
81
|
+
subTitle: script.subTitle,
|
|
82
|
+
taskId: script.taskId
|
|
83
|
+
};
|
|
84
|
+
if (camera) {
|
|
85
|
+
var _camera_pointerLeft1, _camera_pointerTop1;
|
|
86
|
+
sf.cameraTarget = {
|
|
87
|
+
left: camera.left,
|
|
88
|
+
top: camera.top,
|
|
89
|
+
width: camera.width,
|
|
90
|
+
pointerLeft: null != (_camera_pointerLeft1 = camera.pointerLeft) ? _camera_pointerLeft1 : Math.round(iw / 2),
|
|
91
|
+
pointerTop: null != (_camera_pointerTop1 = camera.pointerTop) ? _camera_pointerTop1 : Math.round(ih / 2)
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
scriptFrames.push(sf);
|
|
95
|
+
currentFrame += totalFrames;
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
case 'clear-insight':
|
|
99
|
+
{
|
|
100
|
+
const frames = Math.max(Math.ceil(durationMs / 1000 * FPS), 1);
|
|
101
|
+
scriptFrames.push({
|
|
102
|
+
type: 'clear-insight',
|
|
103
|
+
startFrame: currentFrame,
|
|
104
|
+
durationInFrames: frames,
|
|
105
|
+
title: script.title,
|
|
106
|
+
subTitle: script.subTitle,
|
|
107
|
+
taskId: script.taskId
|
|
108
|
+
});
|
|
109
|
+
currentFrame += frames;
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
case 'spinning-pointer':
|
|
113
|
+
{
|
|
114
|
+
const frames = Math.max(Math.ceil(durationMs / 1000 * FPS), 1);
|
|
115
|
+
scriptFrames.push({
|
|
116
|
+
type: 'spinning-pointer',
|
|
117
|
+
startFrame: currentFrame,
|
|
118
|
+
durationInFrames: frames,
|
|
119
|
+
title: script.title,
|
|
120
|
+
subTitle: script.subTitle,
|
|
121
|
+
taskId: script.taskId
|
|
122
|
+
});
|
|
123
|
+
currentFrame += frames;
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
case 'pointer':
|
|
127
|
+
scriptFrames.push({
|
|
128
|
+
type: 'pointer',
|
|
129
|
+
startFrame: currentFrame,
|
|
130
|
+
durationInFrames: 0,
|
|
131
|
+
pointerImg: script.img,
|
|
132
|
+
title: script.title,
|
|
133
|
+
subTitle: script.subTitle,
|
|
134
|
+
taskId: script.taskId
|
|
135
|
+
});
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const stepsDurationInFrames = Math.max(currentFrame, 1);
|
|
140
|
+
return {
|
|
141
|
+
scriptFrames,
|
|
142
|
+
totalDurationInFrames: stepsDurationInFrames,
|
|
143
|
+
fps: FPS,
|
|
144
|
+
stepsDurationInFrames,
|
|
145
|
+
imageWidth: baseImageWidth,
|
|
146
|
+
imageHeight: baseImageHeight
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
export { FPS, calculateFrameMap };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
function useFramePlayer(options) {
|
|
3
|
+
const { durationInFrames, fps, autoPlay = false, loop = false } = options;
|
|
4
|
+
const [currentFrame, setCurrentFrame] = useState(0);
|
|
5
|
+
const [playing, setPlaying] = useState(autoPlay);
|
|
6
|
+
const playingRef = useRef(playing);
|
|
7
|
+
const frameRef = useRef(currentFrame);
|
|
8
|
+
var _options_playbackRate;
|
|
9
|
+
const rateRef = useRef(null != (_options_playbackRate = options.playbackRate) ? _options_playbackRate : 1);
|
|
10
|
+
const durationRef = useRef(durationInFrames);
|
|
11
|
+
const fpsRef = useRef(fps);
|
|
12
|
+
const loopRef = useRef(loop);
|
|
13
|
+
playingRef.current = playing;
|
|
14
|
+
frameRef.current = currentFrame;
|
|
15
|
+
var _options_playbackRate1;
|
|
16
|
+
rateRef.current = null != (_options_playbackRate1 = options.playbackRate) ? _options_playbackRate1 : 1;
|
|
17
|
+
durationRef.current = durationInFrames;
|
|
18
|
+
fpsRef.current = fps;
|
|
19
|
+
loopRef.current = loop;
|
|
20
|
+
useEffect(()=>{
|
|
21
|
+
if (!playing) return;
|
|
22
|
+
let rafId;
|
|
23
|
+
let lastTime = null;
|
|
24
|
+
let accumulated = 0;
|
|
25
|
+
const tick = (now)=>{
|
|
26
|
+
if (null !== lastTime) {
|
|
27
|
+
const delta = (now - lastTime) * rateRef.current;
|
|
28
|
+
accumulated += delta;
|
|
29
|
+
const frameDuration = 1000 / fpsRef.current;
|
|
30
|
+
while(accumulated >= frameDuration){
|
|
31
|
+
accumulated -= frameDuration;
|
|
32
|
+
const next = frameRef.current + 1;
|
|
33
|
+
if (next >= durationRef.current) if (loopRef.current) {
|
|
34
|
+
frameRef.current = 0;
|
|
35
|
+
setCurrentFrame(0);
|
|
36
|
+
} else {
|
|
37
|
+
frameRef.current = durationRef.current - 1;
|
|
38
|
+
setCurrentFrame(durationRef.current - 1);
|
|
39
|
+
setPlaying(false);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
frameRef.current = next;
|
|
44
|
+
setCurrentFrame(next);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
lastTime = now;
|
|
49
|
+
rafId = requestAnimationFrame(tick);
|
|
50
|
+
};
|
|
51
|
+
rafId = requestAnimationFrame(tick);
|
|
52
|
+
return ()=>cancelAnimationFrame(rafId);
|
|
53
|
+
}, [
|
|
54
|
+
playing
|
|
55
|
+
]);
|
|
56
|
+
const play = useCallback(()=>{
|
|
57
|
+
if (frameRef.current >= durationRef.current - 1) {
|
|
58
|
+
frameRef.current = 0;
|
|
59
|
+
setCurrentFrame(0);
|
|
60
|
+
}
|
|
61
|
+
setPlaying(true);
|
|
62
|
+
}, []);
|
|
63
|
+
const pause = useCallback(()=>setPlaying(false), []);
|
|
64
|
+
const toggle = useCallback(()=>{
|
|
65
|
+
if (playingRef.current) setPlaying(false);
|
|
66
|
+
else {
|
|
67
|
+
if (frameRef.current >= durationRef.current - 1) {
|
|
68
|
+
frameRef.current = 0;
|
|
69
|
+
setCurrentFrame(0);
|
|
70
|
+
}
|
|
71
|
+
setPlaying(true);
|
|
72
|
+
}
|
|
73
|
+
}, []);
|
|
74
|
+
const seekTo = useCallback((frame)=>{
|
|
75
|
+
const clamped = Math.max(0, Math.min(frame, durationRef.current - 1));
|
|
76
|
+
frameRef.current = clamped;
|
|
77
|
+
setCurrentFrame(clamped);
|
|
78
|
+
}, []);
|
|
79
|
+
return {
|
|
80
|
+
currentFrame,
|
|
81
|
+
playing,
|
|
82
|
+
play,
|
|
83
|
+
pause,
|
|
84
|
+
toggle,
|
|
85
|
+
seekTo
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
export { useFramePlayer };
|
|
@@ -100,7 +100,20 @@ function UniversalPlayground({ playgroundSDK, storage, contextProvider, config:
|
|
|
100
100
|
playgroundSDK
|
|
101
101
|
]);
|
|
102
102
|
const { loading, setLoading, infoList, setInfoList, actionSpace, actionSpaceLoading, uiContextPreview, setUiContextPreview, showScrollToBottomButton, verticalMode, replayCounter, setReplayCounter, infoListRef, currentRunningIdRef, interruptedFlagRef, clearInfoList, handleScrollToBottom } = usePlaygroundState(playgroundSDK, effectiveStorage, contextProvider, branding.targetName);
|
|
103
|
-
const { handleRun: executeAction, handleStop, canStop } = usePlaygroundExecution(
|
|
103
|
+
const { handleRun: executeAction, handleStop, canStop } = usePlaygroundExecution({
|
|
104
|
+
playgroundSDK,
|
|
105
|
+
storage: effectiveStorage,
|
|
106
|
+
actionSpace,
|
|
107
|
+
loading,
|
|
108
|
+
setLoading,
|
|
109
|
+
setInfoList,
|
|
110
|
+
replayCounter,
|
|
111
|
+
setReplayCounter,
|
|
112
|
+
verticalMode,
|
|
113
|
+
currentRunningIdRef,
|
|
114
|
+
interruptedFlagRef,
|
|
115
|
+
deviceType: componentConfig.deviceType
|
|
116
|
+
});
|
|
104
117
|
useEffect(()=>{
|
|
105
118
|
if ((null == playgroundSDK ? void 0 : playgroundSDK.overrideConfig) && config) playgroundSDK.overrideConfig(config).catch((error)=>{
|
|
106
119
|
console.error('Failed to override SDK config:', error);
|
|
@@ -88,7 +88,7 @@ function buildProgressContent(task) {
|
|
|
88
88
|
const description = paramStr(task);
|
|
89
89
|
return description ? `${action} - ${description}` : action;
|
|
90
90
|
}
|
|
91
|
-
function wrapExecutionDumpForReplay(dump) {
|
|
91
|
+
function wrapExecutionDumpForReplay(dump, deviceType) {
|
|
92
92
|
const modelBriefsSet = new Set();
|
|
93
93
|
if ((null == dump ? void 0 : dump.tasks) && Array.isArray(dump.tasks)) dump.tasks.forEach((task)=>{
|
|
94
94
|
if (task.usage) {
|
|
@@ -106,10 +106,12 @@ function wrapExecutionDumpForReplay(dump) {
|
|
|
106
106
|
modelBriefs,
|
|
107
107
|
executions: [
|
|
108
108
|
dump
|
|
109
|
-
]
|
|
109
|
+
],
|
|
110
|
+
deviceType
|
|
110
111
|
};
|
|
111
112
|
}
|
|
112
|
-
function usePlaygroundExecution(
|
|
113
|
+
function usePlaygroundExecution(options) {
|
|
114
|
+
const { playgroundSDK, storage, actionSpace, loading, setLoading, setInfoList, replayCounter, setReplayCounter, verticalMode, currentRunningIdRef, interruptedFlagRef, deviceType } = options;
|
|
113
115
|
const { deepLocate, deepThink, screenshotIncluded, domIncluded } = useEnvConfig();
|
|
114
116
|
const handleRun = useCallback((value)=>_async_to_generator(function*() {
|
|
115
117
|
if (!playgroundSDK) return void console.warn('PlaygroundSDK is not available');
|
|
@@ -206,7 +208,7 @@ function usePlaygroundExecution(playgroundSDK, storage, actionSpace, loading, se
|
|
|
206
208
|
let counter = replayCounter;
|
|
207
209
|
if (null == result ? void 0 : result.dump) {
|
|
208
210
|
if (result.dump.tasks && Array.isArray(result.dump.tasks)) {
|
|
209
|
-
const groupedDump = wrapExecutionDumpForReplay(result.dump);
|
|
211
|
+
const groupedDump = wrapExecutionDumpForReplay(result.dump, deviceType);
|
|
210
212
|
const info = allScriptsFromDump(groupedDump);
|
|
211
213
|
setReplayCounter((c)=>c + 1);
|
|
212
214
|
replayInfo = info;
|
|
@@ -264,7 +266,8 @@ function usePlaygroundExecution(playgroundSDK, storage, actionSpace, loading, se
|
|
|
264
266
|
deepLocate,
|
|
265
267
|
deepThink,
|
|
266
268
|
screenshotIncluded,
|
|
267
|
-
domIncluded
|
|
269
|
+
domIncluded,
|
|
270
|
+
deviceType
|
|
268
271
|
]);
|
|
269
272
|
const handleStop = useCallback(()=>_async_to_generator(function*() {
|
|
270
273
|
const thisRunningId = currentRunningIdRef.current;
|
|
@@ -291,7 +294,7 @@ function usePlaygroundExecution(playgroundSDK, storage, actionSpace, loading, se
|
|
|
291
294
|
let replayInfo = null;
|
|
292
295
|
let counter = replayCounter;
|
|
293
296
|
if ((null == (_executionData_dump = executionData.dump) ? void 0 : _executionData_dump.tasks) && Array.isArray(executionData.dump.tasks)) {
|
|
294
|
-
const groupedDump = wrapExecutionDumpForReplay(executionData.dump);
|
|
297
|
+
const groupedDump = wrapExecutionDumpForReplay(executionData.dump, deviceType);
|
|
295
298
|
replayInfo = allScriptsFromDump(groupedDump);
|
|
296
299
|
setReplayCounter((c)=>c + 1);
|
|
297
300
|
counter = replayCounter + 1;
|
|
@@ -349,7 +352,8 @@ function usePlaygroundExecution(playgroundSDK, storage, actionSpace, loading, se
|
|
|
349
352
|
setLoading,
|
|
350
353
|
setInfoList,
|
|
351
354
|
verticalMode,
|
|
352
|
-
replayCounter
|
|
355
|
+
replayCounter,
|
|
356
|
+
deviceType
|
|
353
357
|
]);
|
|
354
358
|
const canStop = loading && !!currentRunningIdRef.current && !!playgroundSDK && !!playgroundSDK.cancelExecution;
|
|
355
359
|
return {
|
package/dist/es/store/store.mjs
CHANGED
|
@@ -33,6 +33,7 @@ const ELEMENTS_VISIBLE_KEY = 'midscene-elements-visible';
|
|
|
33
33
|
const MODEL_CALL_DETAILS_KEY = 'midscene-model-call-details';
|
|
34
34
|
const DARK_MODE_KEY = 'midscene-dark-mode';
|
|
35
35
|
const PLAYBACK_SPEED_KEY = 'midscene-playback-speed';
|
|
36
|
+
const SUBTITLE_ENABLED_KEY = 'midscene-subtitle-enabled';
|
|
36
37
|
const parseBooleanParam = (value)=>{
|
|
37
38
|
if (null === value) return;
|
|
38
39
|
const normalized = value.trim().toLowerCase();
|
|
@@ -62,6 +63,7 @@ const useGlobalPreference = store_create((set)=>{
|
|
|
62
63
|
const savedDarkMode = 'true' === localStorage.getItem(DARK_MODE_KEY);
|
|
63
64
|
const parsedPlaybackSpeed = Number.parseFloat(localStorage.getItem(PLAYBACK_SPEED_KEY) || '1');
|
|
64
65
|
const savedPlaybackSpeed = Number.isNaN(parsedPlaybackSpeed) ? 1 : parsedPlaybackSpeed;
|
|
66
|
+
const savedSubtitleEnabled = 'false' !== localStorage.getItem(SUBTITLE_ENABLED_KEY);
|
|
65
67
|
const autoZoomFromQuery = getQueryPreference('focusOnCursor');
|
|
66
68
|
const elementsVisibleFromQuery = getQueryPreference('showElementMarkers');
|
|
67
69
|
const darkModeFromQuery = getQueryPreference('darkMode');
|
|
@@ -114,6 +116,13 @@ const useGlobalPreference = store_create((set)=>{
|
|
|
114
116
|
playbackSpeed: speed
|
|
115
117
|
});
|
|
116
118
|
localStorage.setItem(PLAYBACK_SPEED_KEY, speed.toString());
|
|
119
|
+
},
|
|
120
|
+
subtitleEnabled: savedSubtitleEnabled,
|
|
121
|
+
setSubtitleEnabled: (enabled)=>{
|
|
122
|
+
set({
|
|
123
|
+
subtitleEnabled: enabled
|
|
124
|
+
});
|
|
125
|
+
localStorage.setItem(SUBTITLE_ENABLED_KEY, enabled.toString());
|
|
117
126
|
}
|
|
118
127
|
};
|
|
119
128
|
});
|
|
@@ -158,7 +158,8 @@ const allScriptsFromDump = (dump)=>{
|
|
|
158
158
|
width: firstWidth,
|
|
159
159
|
height: firstHeight,
|
|
160
160
|
sdkVersion,
|
|
161
|
-
modelBriefs
|
|
161
|
+
modelBriefs,
|
|
162
|
+
deviceType: normalizedDump.deviceType
|
|
162
163
|
};
|
|
163
164
|
};
|
|
164
165
|
const generateAnimationScripts = (execution, task, imageWidth, imageHeight, executionIndex = 0)=>{
|
|
@@ -21,21 +21,99 @@
|
|
|
21
21
|
max-width: 500px;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
.blackboard-
|
|
25
|
-
|
|
24
|
+
.blackboard-main-content {
|
|
25
|
+
position: relative;
|
|
26
|
+
overflow: hidden;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
.blackboard-
|
|
29
|
+
.blackboard-screenshot {
|
|
29
30
|
box-sizing: border-box;
|
|
30
31
|
border: 1px solid #888;
|
|
31
32
|
width: 100%;
|
|
33
|
+
display: block;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.blackboard-overlay {
|
|
37
|
+
pointer-events: none;
|
|
38
|
+
width: 100%;
|
|
39
|
+
position: absolute;
|
|
40
|
+
top: 0;
|
|
41
|
+
left: 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.blackboard-rect {
|
|
45
|
+
box-sizing: border-box;
|
|
46
|
+
pointer-events: none;
|
|
47
|
+
position: absolute;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.blackboard-rect-label {
|
|
51
|
+
white-space: nowrap;
|
|
52
|
+
padding: 1px 4px;
|
|
53
|
+
font-size: 14px;
|
|
54
|
+
font-weight: 600;
|
|
55
|
+
line-height: 1.4;
|
|
56
|
+
position: absolute;
|
|
57
|
+
bottom: 100%;
|
|
58
|
+
left: 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.blackboard-rect-search {
|
|
62
|
+
background: rgba(2, 131, 145, .4);
|
|
63
|
+
border: 1px solid #028391;
|
|
64
|
+
box-shadow: 4px 4px 2px rgba(51, 51, 51, .4);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.blackboard-rect-search .blackboard-rect-label {
|
|
68
|
+
color: #028391;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.blackboard-rect-highlight {
|
|
72
|
+
background: rgba(253, 89, 7, .4);
|
|
73
|
+
border: 1px solid #fd5907;
|
|
74
|
+
animation: 1.2s ease-in-out infinite blackboard-pulse;
|
|
75
|
+
box-shadow: 4px 4px 2px rgba(51, 51, 51, .4);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.blackboard-rect-highlight .blackboard-rect-label {
|
|
79
|
+
color: #000;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.blackboard-point {
|
|
83
|
+
pointer-events: none;
|
|
84
|
+
background: rgba(253, 89, 7, .4);
|
|
85
|
+
border: 1px solid #fd5907;
|
|
86
|
+
border-radius: 50%;
|
|
87
|
+
width: 20px;
|
|
88
|
+
height: 20px;
|
|
89
|
+
margin-top: -10px;
|
|
90
|
+
margin-left: -10px;
|
|
91
|
+
animation: 1.2s ease-in-out infinite blackboard-pulse;
|
|
92
|
+
position: absolute;
|
|
93
|
+
box-shadow: 0 0 8px rgba(253, 89, 7, .5);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@keyframes blackboard-pulse {
|
|
97
|
+
0%, 100% {
|
|
98
|
+
opacity: .4;
|
|
99
|
+
box-shadow: 4px 4px 2px rgba(51, 51, 51, .4);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
50% {
|
|
103
|
+
opacity: 1;
|
|
104
|
+
box-shadow: 4px 4px 2px rgba(51, 51, 51, .4), 0 0 16px rgba(253, 89, 7, .5);
|
|
105
|
+
}
|
|
32
106
|
}
|
|
33
107
|
|
|
34
108
|
[data-theme="dark"] .blackboard .footer, [data-theme="dark"] .blackboard .bottom-tip-item {
|
|
35
109
|
color: rgba(255, 255, 255, .45);
|
|
36
110
|
}
|
|
37
111
|
|
|
38
|
-
[data-theme="dark"] .blackboard-
|
|
112
|
+
[data-theme="dark"] .blackboard-screenshot {
|
|
39
113
|
border-color: rgba(255, 255, 255, .12);
|
|
40
114
|
}
|
|
41
115
|
|
|
116
|
+
[data-theme="dark"] .blackboard-rect-highlight .blackboard-rect-label {
|
|
117
|
+
color: #fff;
|
|
118
|
+
}
|
|
119
|
+
|