@midscene/visualizer 1.5.2 → 1.5.3-beta-20260305031416.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.
Files changed (43) hide show
  1. package/dist/es/component/blackboard/index.css +82 -4
  2. package/dist/es/component/blackboard/index.mjs +73 -301
  3. package/dist/es/component/player/index.css +144 -119
  4. package/dist/es/component/player/index.mjs +468 -830
  5. package/dist/es/component/player/remotion/StepScene.mjs +190 -0
  6. package/dist/es/component/player/remotion/derive-frame-state.mjs +207 -0
  7. package/dist/es/component/player/remotion/export-branded-video.mjs +210 -0
  8. package/dist/es/component/player/remotion/frame-calculator.mjs +149 -0
  9. package/dist/es/component/player/use-frame-player.mjs +88 -0
  10. package/dist/es/component/universal-playground/index.mjs +14 -1
  11. package/dist/es/hooks/usePlaygroundExecution.mjs +11 -7
  12. package/dist/es/index.mjs +2 -2
  13. package/dist/es/store/store.mjs +9 -0
  14. package/dist/es/utils/replay-scripts.mjs +78 -59
  15. package/dist/lib/component/blackboard/index.css +82 -4
  16. package/dist/lib/component/blackboard/index.js +73 -307
  17. package/dist/lib/component/player/index.css +144 -119
  18. package/dist/lib/component/player/index.js +466 -828
  19. package/dist/lib/component/player/remotion/StepScene.js +224 -0
  20. package/dist/lib/component/player/remotion/derive-frame-state.js +241 -0
  21. package/dist/lib/component/player/remotion/export-branded-video.js +244 -0
  22. package/dist/lib/component/player/remotion/frame-calculator.js +186 -0
  23. package/dist/lib/component/player/use-frame-player.js +122 -0
  24. package/dist/lib/component/universal-playground/index.js +14 -1
  25. package/dist/lib/hooks/usePlaygroundExecution.js +11 -7
  26. package/dist/lib/index.js +3 -0
  27. package/dist/lib/store/store.js +9 -0
  28. package/dist/lib/utils/replay-scripts.js +80 -58
  29. package/dist/types/component/blackboard/index.d.ts +0 -4
  30. package/dist/types/component/player/index.d.ts +0 -1
  31. package/dist/types/component/player/remotion/StepScene.d.ts +9 -0
  32. package/dist/types/component/player/remotion/derive-frame-state.d.ts +38 -0
  33. package/dist/types/component/player/remotion/export-branded-video.d.ts +2 -0
  34. package/dist/types/component/player/remotion/frame-calculator.d.ts +35 -0
  35. package/dist/types/component/player/use-frame-player.d.ts +17 -0
  36. package/dist/types/hooks/usePlaygroundExecution.d.ts +15 -1
  37. package/dist/types/index.d.ts +1 -1
  38. package/dist/types/store/store.d.ts +2 -0
  39. package/dist/types/utils/replay-scripts.d.ts +16 -1
  40. package/package.json +5 -8
  41. package/dist/es/utils/pixi-loader.mjs +0 -42
  42. package/dist/lib/utils/pixi-loader.js +0 -82
  43. package/dist/types/utils/pixi-loader.d.ts +0 -5
@@ -37,11 +37,8 @@ __webpack_require__.d(__webpack_exports__, {
37
37
  Player: ()=>Player
38
38
  });
39
39
  const jsx_runtime_namespaceObject = require("react/jsx-runtime");
40
- require("pixi.js/unsafe-eval");
41
- const external_pixi_js_namespaceObject = require("pixi.js");
42
40
  const external_react_namespaceObject = require("react");
43
41
  require("./index.css");
44
- const index_js_namespaceObject = require("../../utils/index.js");
45
42
  const icons_namespaceObject = require("@ant-design/icons");
46
43
  const external_antd_namespaceObject = require("antd");
47
44
  const global_perspective_js_namespaceObject = require("../../icons/global-perspective.js");
@@ -49,8 +46,11 @@ var global_perspective_js_default = /*#__PURE__*/ __webpack_require__.n(global_p
49
46
  const player_setting_js_namespaceObject = require("../../icons/player-setting.js");
50
47
  var player_setting_js_default = /*#__PURE__*/ __webpack_require__.n(player_setting_js_namespaceObject);
51
48
  const store_js_namespaceObject = require("../../store/store.js");
52
- const pixi_loader_js_namespaceObject = require("../../utils/pixi-loader.js");
53
- const external_blackboard_index_js_namespaceObject = require("../blackboard/index.js");
49
+ const StepScene_js_namespaceObject = require("./remotion/StepScene.js");
50
+ const derive_frame_state_js_namespaceObject = require("./remotion/derive-frame-state.js");
51
+ const export_branded_video_js_namespaceObject = require("./remotion/export-branded-video.js");
52
+ const frame_calculator_js_namespaceObject = require("./remotion/frame-calculator.js");
53
+ const external_use_frame_player_js_namespaceObject = require("./use-frame-player.js");
54
54
  function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
55
55
  try {
56
56
  var info = gen[key](arg);
@@ -77,101 +77,6 @@ function _async_to_generator(fn) {
77
77
  });
78
78
  };
79
79
  }
80
- function _define_property(obj, key, value) {
81
- if (key in obj) Object.defineProperty(obj, key, {
82
- value: value,
83
- enumerable: true,
84
- configurable: true,
85
- writable: true
86
- });
87
- else obj[key] = value;
88
- return obj;
89
- }
90
- function _object_spread(target) {
91
- for(var i = 1; i < arguments.length; i++){
92
- var source = null != arguments[i] ? arguments[i] : {};
93
- var ownKeys = Object.keys(source);
94
- if ("function" == typeof Object.getOwnPropertySymbols) ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
95
- return Object.getOwnPropertyDescriptor(source, sym).enumerable;
96
- }));
97
- ownKeys.forEach(function(key) {
98
- _define_property(target, key, source[key]);
99
- });
100
- }
101
- return target;
102
- }
103
- function player_ownKeys(object, enumerableOnly) {
104
- var keys = Object.keys(object);
105
- if (Object.getOwnPropertySymbols) {
106
- var symbols = Object.getOwnPropertySymbols(object);
107
- if (enumerableOnly) symbols = symbols.filter(function(sym) {
108
- return Object.getOwnPropertyDescriptor(object, sym).enumerable;
109
- });
110
- keys.push.apply(keys, symbols);
111
- }
112
- return keys;
113
- }
114
- function _object_spread_props(target, source) {
115
- source = null != source ? source : {};
116
- if (Object.getOwnPropertyDescriptors) Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
117
- else player_ownKeys(Object(source)).forEach(function(key) {
118
- Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
119
- });
120
- return target;
121
- }
122
- const canvasPaddingLeft = 0;
123
- const canvasPaddingTop = 0;
124
- const cubicBezier = (t, p0, p1, p2, p3)=>{
125
- const t2 = 1 - t;
126
- return p0 * t2 * t2 * t2 + 3 * p1 * t * t2 * t2 + 3 * p2 * t * t * t2 + p3 * t * t * t;
127
- };
128
- const cubicImage = (t)=>linear(t);
129
- const cubicInsightElement = (t)=>cubicBezier(t, 0, 0.5, 0.5, 1);
130
- const cubicMouse = (t)=>linear(t);
131
- const linear = (t)=>t;
132
- const ERROR_FRAME_CANCEL = 'frame cancel (this is an error on purpose)';
133
- const frameKit = ()=>{
134
- let cancelFlag = false;
135
- const pendingTimeouts = [];
136
- return {
137
- frame: (callback)=>{
138
- if (cancelFlag) throw new Error(ERROR_FRAME_CANCEL);
139
- requestAnimationFrame(()=>{
140
- if (cancelFlag) return;
141
- callback(performance.now());
142
- });
143
- },
144
- timeout: (callback, ms)=>{
145
- if (cancelFlag) throw new Error(ERROR_FRAME_CANCEL);
146
- const timeoutId = window.setTimeout(()=>{
147
- if (cancelFlag) return;
148
- callback();
149
- }, ms);
150
- pendingTimeouts.push(timeoutId);
151
- },
152
- sleep: (ms)=>{
153
- if (cancelFlag) return Promise.reject(new Error(ERROR_FRAME_CANCEL));
154
- return new Promise((resolve, reject)=>{
155
- const timeoutId = window.setTimeout(()=>{
156
- if (cancelFlag) reject(new Error(ERROR_FRAME_CANCEL));
157
- else resolve();
158
- }, ms);
159
- pendingTimeouts.push(timeoutId);
160
- });
161
- },
162
- isCancelled: ()=>cancelFlag,
163
- cancel: ()=>{
164
- cancelFlag = true;
165
- for (const id of pendingTimeouts)clearTimeout(id);
166
- pendingTimeouts.length = 0;
167
- }
168
- };
169
- };
170
- const singleElementFadeInDuration = 80;
171
- const LAYER_ORDER_IMG = 0;
172
- const LAYER_ORDER_INSIGHT = 1;
173
- const LAYER_ORDER_POINTER = 2;
174
- const LAYER_ORDER_SPINNING_POINTER = 3;
175
80
  const downloadReport = (content)=>{
176
81
  const blob = new Blob([
177
82
  content
@@ -183,67 +88,32 @@ const downloadReport = (content)=>{
183
88
  a.href = url;
184
89
  a.download = 'midscene_report.html';
185
90
  a.click();
91
+ setTimeout(()=>URL.revokeObjectURL(url), 0);
186
92
  };
187
- class RecordingSession {
188
- start() {
189
- const stream = this.canvas.captureStream(60);
190
- const mediaRecorder = new MediaRecorder(stream, {
191
- mimeType: 'video/webm'
192
- });
193
- mediaRecorder.ondataavailable = (event)=>{
194
- if (event.data.size > 0) this.chunks.push(event.data);
195
- };
196
- mediaRecorder.onerror = (event)=>{
197
- console.error('MediaRecorder error:', event);
198
- external_antd_namespaceObject.message.error('Video recording failed. Please try again.');
199
- this.recording = false;
200
- this.mediaRecorder = null;
201
- };
202
- this.mediaRecorder = mediaRecorder;
203
- this.recording = true;
204
- return this.mediaRecorder.start();
205
- }
206
- stop() {
207
- if (!this.recording || !this.mediaRecorder) return void console.warn('not recording');
208
- this.mediaRecorder.onstop = ()=>{
209
- if (0 === this.chunks.length) {
210
- console.error('No video data captured');
211
- external_antd_namespaceObject.message.error('Video export failed: No data captured.');
212
- return;
213
- }
214
- const blob = new Blob(this.chunks, {
215
- type: 'video/webm'
216
- });
217
- if (0 === blob.size) {
218
- console.error('Video blob is empty');
219
- external_antd_namespaceObject.message.error('Video export failed: Empty file.');
220
- return;
93
+ function deriveTaskId(scriptFrames, stepsFrame) {
94
+ let taskId = null;
95
+ for (const sf of scriptFrames){
96
+ if (0 === sf.durationInFrames) {
97
+ if (sf.startFrame <= stepsFrame) {
98
+ var _sf_taskId;
99
+ taskId = null != (_sf_taskId = sf.taskId) ? _sf_taskId : taskId;
221
100
  }
222
- const url = URL.createObjectURL(blob);
223
- const a = document.createElement('a');
224
- a.href = url;
225
- a.download = 'midscene_replay.webm';
226
- a.click();
227
- URL.revokeObjectURL(url);
228
- };
229
- this.mediaRecorder.stop();
230
- this.recording = false;
231
- this.mediaRecorder = null;
232
- }
233
- constructor(canvas){
234
- _define_property(this, "canvas", void 0);
235
- _define_property(this, "mediaRecorder", null);
236
- _define_property(this, "chunks", void 0);
237
- _define_property(this, "recording", false);
238
- this.canvas = canvas;
239
- this.chunks = [];
101
+ continue;
102
+ }
103
+ if (stepsFrame < sf.startFrame) break;
104
+ var _sf_taskId1;
105
+ taskId = null != (_sf_taskId1 = sf.taskId) ? _sf_taskId1 : taskId;
240
106
  }
107
+ return taskId;
108
+ }
109
+ function formatTime(frame, fps) {
110
+ const totalSeconds = Math.floor(frame / fps);
111
+ const m = Math.floor(totalSeconds / 60);
112
+ const s = totalSeconds % 60;
113
+ return `${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
241
114
  }
242
115
  function Player(props) {
243
- var _scripts_;
244
- const [titleText, setTitleText] = (0, external_react_namespaceObject.useState)('');
245
- const [subTitleText, setSubTitleText] = (0, external_react_namespaceObject.useState)('');
246
- const { autoZoom, setAutoZoom, playbackSpeed, setPlaybackSpeed } = (0, store_js_namespaceObject.useGlobalPreference)();
116
+ const { autoZoom, setAutoZoom, playbackSpeed, setPlaybackSpeed, subtitleEnabled, setSubtitleEnabled } = (0, store_js_namespaceObject.useGlobalPreference)();
247
117
  (0, external_react_namespaceObject.useEffect)(()=>{
248
118
  if ((null == props ? void 0 : props.autoZoom) !== void 0) setAutoZoom(props.autoZoom);
249
119
  }, [
@@ -251,628 +121,330 @@ function Player(props) {
251
121
  setAutoZoom
252
122
  ]);
253
123
  const scripts = null == props ? void 0 : props.replayScripts;
254
- const imageWidth = (null == props ? void 0 : props.imageWidth) || 1920;
255
- const imageHeight = (null == props ? void 0 : props.imageHeight) || 1080;
256
- const fitMode = (null == props ? void 0 : props.fitMode) || 'height';
257
- const currentImg = (0, external_react_namespaceObject.useRef)((null == scripts ? void 0 : null == (_scripts_ = scripts[0]) ? void 0 : _scripts_.img) || null);
258
- const divContainerRef = (0, external_react_namespaceObject.useRef)(null);
259
- const app = (0, external_react_namespaceObject.useMemo)(()=>new external_pixi_js_namespaceObject.Application(), []);
260
- const pointerSprite = (0, external_react_namespaceObject.useRef)(null);
261
- const spinningPointerSprite = (0, external_react_namespaceObject.useRef)(null);
262
- const [replayMark, setReplayMark] = (0, external_react_namespaceObject.useState)(0);
263
- const triggerReplay = ()=>{
264
- setReplayMark(Date.now());
265
- };
266
- const windowContentContainer = (0, external_react_namespaceObject.useMemo)(()=>{
267
- const container = new external_pixi_js_namespaceObject.Container();
268
- return container;
269
- }, []);
270
- const insightMarkContainer = (0, external_react_namespaceObject.useMemo)(()=>{
271
- const container = new external_pixi_js_namespaceObject.Container();
272
- container.zIndex = LAYER_ORDER_INSIGHT;
273
- return container;
274
- }, []);
275
- const basicCameraState = {
276
- left: 0,
277
- top: 0,
278
- width: imageWidth,
279
- pointerLeft: Math.round(imageWidth / 2),
280
- pointerTop: Math.round(imageHeight / 2)
281
- };
282
- const [animationProgress, setAnimationProgress] = (0, external_react_namespaceObject.useState)(-1);
283
- const cancelFlag = (0, external_react_namespaceObject.useRef)(false);
284
- const appInitialized = (0, external_react_namespaceObject.useRef)(false);
124
+ const frameMap = (0, external_react_namespaceObject.useMemo)(()=>{
125
+ if (!scripts || 0 === scripts.length) return null;
126
+ return (0, frame_calculator_js_namespaceObject.calculateFrameMap)(scripts);
127
+ }, [
128
+ scripts
129
+ ]);
130
+ const wrapperRef = (0, external_react_namespaceObject.useRef)(null);
131
+ const renderLayerRef = (0, external_react_namespaceObject.useRef)(null);
132
+ const lastTaskIdRef = (0, external_react_namespaceObject.useRef)(null);
133
+ const [containerSize, setContainerSize] = (0, external_react_namespaceObject.useState)({
134
+ width: 0,
135
+ height: 0
136
+ });
285
137
  (0, external_react_namespaceObject.useEffect)(()=>{
286
- cancelFlag.current = false;
287
- return ()=>{
288
- cancelFlag.current = true;
289
- };
290
- }, []);
291
- const cameraState = (0, external_react_namespaceObject.useRef)(_object_spread({}, basicCameraState));
292
- const resizeCanvasIfNeeded = (newWidth, newHeight)=>_async_to_generator(function*() {
293
- if (!appInitialized.current || !app.screen) return;
294
- if (app.screen.width !== newWidth || app.screen.height !== newHeight) {
295
- app.renderer.resize(newWidth, newHeight);
296
- if (divContainerRef.current) {
297
- const aspectRatio = newWidth / newHeight;
298
- divContainerRef.current.style.setProperty('--canvas-aspect-ratio', aspectRatio.toString());
299
- }
300
- const newBasicCameraState = {
301
- left: 0,
302
- top: 0,
303
- width: newWidth,
304
- pointerLeft: Math.round(newWidth / 2),
305
- pointerTop: Math.round(newHeight / 2)
306
- };
307
- cameraState.current = newBasicCameraState;
308
- }
309
- })();
310
- const repaintImage = (scriptWidth, scriptHeight)=>_async_to_generator(function*() {
311
- const imgToUpdate = currentImg.current;
312
- if (!imgToUpdate) return void console.warn('no image to update');
313
- const targetWidth = scriptWidth || imageWidth;
314
- const targetHeight = scriptHeight || imageHeight;
315
- yield resizeCanvasIfNeeded(targetWidth, targetHeight);
316
- if (!(0, pixi_loader_js_namespaceObject.getTextureFromCache)(imgToUpdate)) {
317
- console.warn('image not loaded', imgToUpdate);
318
- yield (0, pixi_loader_js_namespaceObject.loadTexture)(imgToUpdate);
138
+ const el = renderLayerRef.current;
139
+ if (!el) return;
140
+ const ro = new ResizeObserver((entries)=>{
141
+ for (const entry of entries){
142
+ const { width, height } = entry.contentRect;
143
+ setContainerSize((prev)=>prev.width === width && prev.height === height ? prev : {
144
+ width,
145
+ height
146
+ });
319
147
  }
320
- const texture = (0, pixi_loader_js_namespaceObject.getTextureFromCache)(imgToUpdate);
321
- if (!texture) throw new Error('texture not found');
322
- const sprite = external_pixi_js_namespaceObject.Sprite.from(texture);
323
- if (!sprite) throw new Error('sprite not found');
324
- const mainImgLabel = 'main-img';
325
- const child = windowContentContainer.getChildByLabel(mainImgLabel);
326
- if (child) windowContentContainer.removeChild(child);
327
- sprite.label = mainImgLabel;
328
- sprite.zIndex = LAYER_ORDER_IMG;
329
- sprite.width = targetWidth;
330
- sprite.height = targetHeight;
331
- windowContentContainer.addChild(sprite);
332
- })();
333
- const spinningPointer = (frame)=>{
334
- var _pointerSprite_current, _pointerSprite_current1;
335
- if (!spinningPointerSprite.current) {
336
- spinningPointerSprite.current = external_pixi_js_namespaceObject.Sprite.from(index_js_namespaceObject.mouseLoading);
337
- spinningPointerSprite.current.zIndex = LAYER_ORDER_SPINNING_POINTER;
338
- spinningPointerSprite.current.anchor.set(0.5, 0.5);
339
- spinningPointerSprite.current.scale.set(0.5);
340
- spinningPointerSprite.current.label = 'spinning-pointer';
148
+ });
149
+ ro.observe(el);
150
+ return ()=>ro.disconnect();
151
+ }, []);
152
+ var _frameMap_totalDurationInFrames, _frameMap_fps;
153
+ const player = (0, external_use_frame_player_js_namespaceObject.useFramePlayer)({
154
+ durationInFrames: Math.max(null != (_frameMap_totalDurationInFrames = null == frameMap ? void 0 : frameMap.totalDurationInFrames) ? _frameMap_totalDurationInFrames : 1, 1),
155
+ fps: null != (_frameMap_fps = null == frameMap ? void 0 : frameMap.fps) ? _frameMap_fps : 30,
156
+ autoPlay: true,
157
+ loop: false,
158
+ playbackRate: playbackSpeed
159
+ });
160
+ (0, external_react_namespaceObject.useEffect)(()=>{
161
+ if (!frameMap || !(null == props ? void 0 : props.onTaskChange)) return;
162
+ const taskId = deriveTaskId(frameMap.scriptFrames, player.currentFrame);
163
+ if (taskId !== lastTaskIdRef.current) {
164
+ lastTaskIdRef.current = taskId;
165
+ props.onTaskChange(taskId);
341
166
  }
342
- spinningPointerSprite.current.x = (null == (_pointerSprite_current = pointerSprite.current) ? void 0 : _pointerSprite_current.x) || 0;
343
- spinningPointerSprite.current.y = (null == (_pointerSprite_current1 = pointerSprite.current) ? void 0 : _pointerSprite_current1.y) || 0;
344
- windowContentContainer.addChild(spinningPointerSprite.current);
345
- let startTime;
346
- let isCancelled = false;
347
- const animate = (currentTime)=>{
348
- if (isCancelled) return;
349
- if (!startTime) startTime = currentTime;
350
- const elapsedTime = currentTime - startTime;
351
- const progress = (Math.sin(elapsedTime / 500 - Math.PI / 2) + 1) / 2;
352
- const rotation = progress * Math.PI * 2;
353
- if (spinningPointerSprite.current) spinningPointerSprite.current.rotation = rotation;
354
- frame(animate);
355
- };
356
- frame(animate);
357
- const stopFn = ()=>{
358
- if (spinningPointerSprite.current) windowContentContainer.removeChild(spinningPointerSprite.current);
359
- isCancelled = true;
167
+ }, [
168
+ frameMap,
169
+ null == props ? void 0 : props.onTaskChange,
170
+ player.currentFrame
171
+ ]);
172
+ const subtitle = (0, external_react_namespaceObject.useMemo)(()=>{
173
+ if (!frameMap) return null;
174
+ const state = (0, derive_frame_state_js_namespaceObject.deriveFrameState)(frameMap.scriptFrames, player.currentFrame, frameMap.imageWidth, frameMap.imageHeight, frameMap.fps);
175
+ if (!state.title && !state.subTitle) return null;
176
+ return {
177
+ title: state.title,
178
+ subTitle: state.subTitle
360
179
  };
361
- return stopFn;
362
- };
363
- const updatePointer = (img, x, y)=>_async_to_generator(function*() {
364
- var _pointerSprite_current, _pointerSprite_current1;
365
- if (!(0, pixi_loader_js_namespaceObject.getTextureFromCache)(img)) {
366
- console.warn('image not loaded', img);
367
- yield (0, pixi_loader_js_namespaceObject.loadTexture)(img);
368
- }
369
- const texture = (0, pixi_loader_js_namespaceObject.getTextureFromCache)(img);
370
- if (!texture) throw new Error('texture not found');
371
- const sprite = external_pixi_js_namespaceObject.Sprite.from(texture);
372
- let targetX = null == (_pointerSprite_current = pointerSprite.current) ? void 0 : _pointerSprite_current.x;
373
- let targetY = null == (_pointerSprite_current1 = pointerSprite.current) ? void 0 : _pointerSprite_current1.y;
374
- if ('number' == typeof x) targetX = x;
375
- if ('number' == typeof y) targetY = y;
376
- if (void 0 === targetX || void 0 === targetY) return void console.warn('invalid pointer position', x, y);
377
- if (pointerSprite.current) {
378
- const pointer = windowContentContainer.getChildByLabel('pointer');
379
- if (pointer) windowContentContainer.removeChild(pointer);
380
- }
381
- pointerSprite.current = sprite;
382
- pointerSprite.current.x = targetX;
383
- pointerSprite.current.y = targetY;
384
- pointerSprite.current.label = 'pointer';
385
- pointerSprite.current.zIndex = LAYER_ORDER_POINTER;
386
- windowContentContainer.addChild(pointerSprite.current);
387
- })();
388
- const updateCamera = (state, currentWidth)=>{
389
- var _app_screen;
390
- cameraState.current = state;
391
- const effectiveWidth = currentWidth || (null == (_app_screen = app.screen) ? void 0 : _app_screen.width) || imageWidth;
392
- const newScale = autoZoom ? Math.max(1, effectiveWidth / state.width) : 1;
393
- windowContentContainer.scale.set(newScale);
394
- windowContentContainer.x = autoZoom ? Math.round(canvasPaddingLeft - state.left * newScale) : canvasPaddingLeft;
395
- windowContentContainer.y = autoZoom ? Math.round(canvasPaddingTop - state.top * newScale) : canvasPaddingTop;
396
- const pointer = windowContentContainer.getChildByLabel('pointer');
397
- if (pointer) {
398
- pointer.scale.set(1 / newScale);
399
- if ('number' == typeof state.pointerLeft && 'number' == typeof state.pointerTop) {
400
- pointer.x = state.pointerLeft;
401
- pointer.y = state.pointerTop;
402
- }
403
- }
404
- };
405
- const cameraAnimation = (targetState, duration, frame)=>_async_to_generator(function*() {
406
- var _app_screen, _app_screen1;
407
- const currentCanvasWidth = (null == (_app_screen = app.screen) ? void 0 : _app_screen.width) || imageWidth;
408
- const currentCanvasHeight = (null == (_app_screen1 = app.screen) ? void 0 : _app_screen1.height) || imageHeight;
409
- if (!autoZoom) {
410
- const currentState = _object_spread({}, cameraState.current);
411
- const startPointerLeft = currentState.pointerLeft;
412
- const startPointerTop = currentState.pointerTop;
413
- const startTime = performance.now();
414
- const shouldMovePointer = 'number' == typeof targetState.pointerLeft && 'number' == typeof targetState.pointerTop && (targetState.pointerLeft !== startPointerLeft || targetState.pointerTop !== startPointerTop);
415
- if (!shouldMovePointer) return;
416
- yield new Promise((resolve)=>{
417
- const animate = (currentTime)=>{
418
- const elapsedTime = currentTime - startTime;
419
- const rawProgress = Math.min(elapsedTime / duration, 1);
420
- const progress = cubicMouse(rawProgress);
421
- const nextState = _object_spread_props(_object_spread({}, currentState), {
422
- pointerLeft: startPointerLeft + (targetState.pointerLeft - startPointerLeft) * progress,
423
- pointerTop: startPointerTop + (targetState.pointerTop - startPointerTop) * progress
424
- });
425
- updateCamera(nextState, currentCanvasWidth);
426
- if (elapsedTime < duration) frame(animate);
427
- else resolve();
428
- };
429
- frame(animate);
430
- });
431
- return;
432
- }
433
- const currentState = _object_spread({}, cameraState.current);
434
- const startLeft = currentState.left;
435
- const startTop = currentState.top;
436
- const startPointerLeft = currentState.pointerLeft;
437
- const startPointerTop = currentState.pointerTop;
438
- const startScale = currentState.width / currentCanvasWidth;
439
- const startTime = performance.now();
440
- const shouldMovePointer = 'number' == typeof targetState.pointerLeft && 'number' == typeof targetState.pointerTop && (targetState.pointerLeft !== startPointerLeft || targetState.pointerTop !== startPointerTop);
441
- const pointerMoveDuration = shouldMovePointer ? 0.375 * duration : 0;
442
- const cameraMoveStart = pointerMoveDuration;
443
- const cameraMoveDuration = duration - pointerMoveDuration;
444
- yield new Promise((resolve)=>{
445
- const animate = (currentTime)=>{
446
- const nextState = _object_spread({}, cameraState.current);
447
- const elapsedTime = currentTime - startTime;
448
- if (shouldMovePointer) if (elapsedTime <= pointerMoveDuration) {
449
- const rawMouseProgress = Math.min(elapsedTime / pointerMoveDuration, 1);
450
- const mouseProgress = cubicMouse(rawMouseProgress);
451
- nextState.pointerLeft = startPointerLeft + (targetState.pointerLeft - startPointerLeft) * mouseProgress;
452
- nextState.pointerTop = startPointerTop + (targetState.pointerTop - startPointerTop) * mouseProgress;
453
- } else {
454
- nextState.pointerLeft = targetState.pointerLeft;
455
- nextState.pointerTop = targetState.pointerTop;
456
- }
457
- if (elapsedTime > cameraMoveStart) {
458
- const cameraElapsedTime = elapsedTime - cameraMoveStart;
459
- const rawCameraProgress = Math.min(cameraElapsedTime / cameraMoveDuration, 1);
460
- const cameraProgress = cubicImage(rawCameraProgress);
461
- const targetScale = targetState.width / currentCanvasWidth;
462
- const progressScale = startScale + (targetScale - startScale) * cameraProgress;
463
- const progressWidth = currentCanvasWidth * progressScale;
464
- const progressHeight = currentCanvasHeight * progressScale;
465
- nextState.width = progressWidth;
466
- const progressLeft = startLeft + (targetState.left - startLeft) * cameraProgress;
467
- const progressTop = startTop + (targetState.top - startTop) * cameraProgress;
468
- const horizontalExceed = progressLeft + progressWidth - currentCanvasWidth;
469
- const verticalExceed = progressTop + progressHeight - currentCanvasHeight;
470
- nextState.left = horizontalExceed > 0 ? progressLeft + horizontalExceed : progressLeft;
471
- nextState.top = verticalExceed > 0 ? progressTop + verticalExceed : progressTop;
472
- }
473
- updateCamera(nextState, currentCanvasWidth);
474
- if (elapsedTime < duration) frame(animate);
475
- else resolve();
476
- };
477
- frame(animate);
478
- });
479
- })();
480
- const fadeInGraphics = (graphics, duration, frame, targetAlpha = 1)=>new Promise((resolve)=>{
481
- const startTime = performance.now();
482
- const animate = (currentTime)=>{
483
- const elapsedTime = currentTime - startTime;
484
- const progress = Math.min(elapsedTime / duration, 1);
485
- graphics.alpha = 0 === targetAlpha ? 1 - linear(progress) : linear(progress);
486
- if (elapsedTime < duration) frame(animate);
487
- else resolve();
488
- };
489
- frame(animate);
490
- });
491
- const fadeOutItem = (graphics, duration, frame)=>_async_to_generator(function*() {
492
- return fadeInGraphics(graphics, duration, frame, 0);
493
- })();
494
- const insightElementsAnimation = (elements, highlightElements, searchArea, duration, frame)=>_async_to_generator(function*() {
495
- insightMarkContainer.removeChildren();
496
- const elementsToAdd = [
497
- ...elements
498
- ];
499
- const totalLength = elementsToAdd.length;
500
- let childrenCount = 0;
501
- yield new Promise((resolve)=>{
502
- const startTime = performance.now();
503
- const animate = (currentTime)=>{
504
- const elapsedTime = currentTime - startTime;
505
- const progress = cubicInsightElement(Math.min(elapsedTime / duration, 1));
506
- const elementsToAddNow = Math.floor(progress * totalLength);
507
- while(childrenCount < elementsToAddNow){
508
- const randomIndex = Math.floor(Math.random() * elementsToAdd.length);
509
- const element = elementsToAdd.splice(randomIndex, 1)[0];
510
- if (element) {
511
- const [insightMarkGraphic] = (0, external_blackboard_index_js_namespaceObject.rectMarkForItem)(element.rect, element.content, 'element');
512
- insightMarkGraphic.alpha = 0;
513
- insightMarkContainer.addChild(insightMarkGraphic);
514
- childrenCount++;
515
- fadeInGraphics(insightMarkGraphic, singleElementFadeInDuration, frame);
516
- }
517
- }
518
- if (elapsedTime < duration) frame(animate);
519
- else {
520
- while(elementsToAdd.length > 0){
521
- const randomIndex = Math.floor(Math.random() * elementsToAdd.length);
522
- const element = elementsToAdd.splice(randomIndex, 1)[0];
523
- const [insightMarkGraphic] = (0, external_blackboard_index_js_namespaceObject.rectMarkForItem)(element.rect, element.content, 'element');
524
- insightMarkGraphic.alpha = 1;
525
- insightMarkContainer.addChild(insightMarkGraphic);
526
- }
527
- if (searchArea) {
528
- const [searchAreaGraphic] = (0, external_blackboard_index_js_namespaceObject.rectMarkForItem)(searchArea, 'Search Area', 'searchArea');
529
- searchAreaGraphic.alpha = 1;
530
- insightMarkContainer.addChild(searchAreaGraphic);
531
- }
532
- highlightElements.map((element)=>{
533
- const [insightMarkGraphic] = (0, external_blackboard_index_js_namespaceObject.rectMarkForItem)(element.rect, element.content || '', 'highlight');
534
- insightMarkGraphic.alpha = 1;
535
- insightMarkContainer.addChild(insightMarkGraphic);
536
- });
537
- resolve();
538
- }
539
- };
540
- frame(animate);
541
- });
542
- })();
543
- const init = ()=>_async_to_generator(function*() {
544
- if (!divContainerRef.current || !scripts) return;
545
- yield app.init({
546
- width: imageWidth,
547
- height: imageHeight,
548
- background: 0xf4f4f4,
549
- autoDensity: true,
550
- antialias: true
551
- });
552
- appInitialized.current = true;
553
- if (!divContainerRef.current) return;
554
- divContainerRef.current.appendChild(app.canvas);
555
- windowContentContainer.x = 0;
556
- windowContentContainer.y = 0;
557
- app.stage.addChild(windowContentContainer);
558
- insightMarkContainer.x = 0;
559
- insightMarkContainer.y = 0;
560
- windowContentContainer.addChild(insightMarkContainer);
561
- })();
562
- const [isRecording, setIsRecording] = (0, external_react_namespaceObject.useState)(false);
563
- const recorderSessionRef = (0, external_react_namespaceObject.useRef)(null);
564
- const cancelAnimationRef = (0, external_react_namespaceObject.useRef)(null);
565
- const playbackSessionIdRef = (0, external_react_namespaceObject.useRef)(0);
566
- const handleExport = ()=>_async_to_generator(function*() {
567
- if (recorderSessionRef.current) return void console.warn('recorderSession exists');
568
- if (!app.canvas) return void console.warn('canvas is not initialized');
569
- if (cancelAnimationRef.current) {
570
- cancelAnimationRef.current();
571
- cancelAnimationRef.current = null;
572
- yield new Promise((resolve)=>setTimeout(resolve, 100));
573
- }
574
- recorderSessionRef.current = new RecordingSession(app.canvas);
575
- setIsRecording(true);
576
- triggerReplay();
577
- })();
578
- const play = ()=>{
579
- let cancelFn;
580
- const currentSessionId = ++playbackSessionIdRef.current;
581
- const safeOnTaskChange = (taskId)=>{
582
- if (playbackSessionIdRef.current === currentSessionId) {
583
- var _props_onTaskChange;
584
- null == props || null == (_props_onTaskChange = props.onTaskChange) || _props_onTaskChange.call(props, taskId);
585
- }
180
+ }, [
181
+ frameMap,
182
+ player.currentFrame
183
+ ]);
184
+ const [controlsVisible, setControlsVisible] = (0, external_react_namespaceObject.useState)(true);
185
+ const hideTimerRef = (0, external_react_namespaceObject.useRef)(null);
186
+ const showControls = (0, external_react_namespaceObject.useCallback)(()=>{
187
+ setControlsVisible(true);
188
+ if (hideTimerRef.current) clearTimeout(hideTimerRef.current);
189
+ hideTimerRef.current = setTimeout(()=>setControlsVisible(false), 3000);
190
+ }, []);
191
+ const onMouseEnter = (0, external_react_namespaceObject.useCallback)(()=>{
192
+ setControlsVisible(true);
193
+ if (hideTimerRef.current) clearTimeout(hideTimerRef.current);
194
+ }, []);
195
+ const onMouseLeave = (0, external_react_namespaceObject.useCallback)(()=>{
196
+ if (hideTimerRef.current) clearTimeout(hideTimerRef.current);
197
+ hideTimerRef.current = setTimeout(()=>setControlsVisible(false), 1000);
198
+ }, []);
199
+ const seekBarRef = (0, external_react_namespaceObject.useRef)(null);
200
+ const handleSeekPointerDown = (0, external_react_namespaceObject.useCallback)((e)=>{
201
+ if (!frameMap || !seekBarRef.current) return;
202
+ const bar = seekBarRef.current;
203
+ bar.setPointerCapture(e.pointerId);
204
+ const seek = (clientX)=>{
205
+ const rect = bar.getBoundingClientRect();
206
+ const ratio = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
207
+ player.seekTo(Math.round(ratio * (frameMap.totalDurationInFrames - 1)));
586
208
  };
587
- Promise.resolve((()=>_async_to_generator(function*() {
588
- var _scripts_;
589
- if (!app || !appInitialized.current) throw new Error('app is not initialized');
590
- if (!scripts) throw new Error("scripts is required");
591
- const { frame, cancel, timeout, sleep: baseSleep } = frameKit();
592
- const scaleByPlaybackSpeed = (duration)=>duration / playbackSpeed;
593
- const sleep = (ms)=>baseSleep(scaleByPlaybackSpeed(ms));
594
- cancelFn = cancel;
595
- cancelAnimationRef.current = cancel;
596
- const allImages = scripts.filter((item)=>!!item.img).map((item)=>item.img);
597
- yield Promise.all([
598
- ...allImages,
599
- index_js_namespaceObject.mouseLoading,
600
- index_js_namespaceObject.mousePointer
601
- ].map(pixi_loader_js_namespaceObject.loadTexture));
602
- insightMarkContainer.removeChildren();
603
- yield updatePointer(index_js_namespaceObject.mousePointer, imageWidth / 2, imageHeight / 2);
604
- yield repaintImage();
605
- yield updateCamera(_object_spread({}, basicCameraState));
606
- const totalDuration = scaleByPlaybackSpeed(scripts.reduce((acc, item)=>acc + item.duration + (item.camera && item.insightCameraDuration ? item.insightCameraDuration : 0), 0));
607
- const progressUpdateInterval = 200;
608
- const startTime = performance.now();
609
- setAnimationProgress(0);
610
- const updateProgress = ()=>{
611
- const progress = Math.min((performance.now() - startTime) / totalDuration, 1);
612
- setAnimationProgress(progress);
613
- if (progress < 1) return timeout(updateProgress, progressUpdateInterval);
614
- };
615
- frame(updateProgress);
616
- if (recorderSessionRef.current) recorderSessionRef.current.start();
617
- let currentTaskId = null;
618
- var _scripts__taskId;
619
- const firstTaskId = null != (_scripts__taskId = null == (_scripts_ = scripts[0]) ? void 0 : _scripts_.taskId) ? _scripts__taskId : null;
620
- if (firstTaskId) {
621
- currentTaskId = firstTaskId;
622
- safeOnTaskChange(currentTaskId);
623
- }
624
- for(const index in scripts){
625
- const item = scripts[index];
626
- setTitleText(item.title || '');
627
- setSubTitleText(item.subTitle || '');
628
- var _item_taskId;
629
- const newTaskId = null != (_item_taskId = item.taskId) ? _item_taskId : null;
630
- if (newTaskId !== currentTaskId) {
631
- currentTaskId = newTaskId;
632
- safeOnTaskChange(currentTaskId);
633
- }
634
- if ('sleep' === item.type) yield sleep(item.duration);
635
- else if ('insight' === item.type) {
636
- if (!item.img) throw new Error('img is required');
637
- currentImg.current = item.img;
638
- yield repaintImage(item.imageWidth, item.imageHeight);
639
- const highlightElements = item.highlightElement ? [
640
- item.highlightElement
641
- ] : [];
642
- yield insightElementsAnimation([], highlightElements, item.searchArea, scaleByPlaybackSpeed(item.duration), frame);
643
- if (item.camera) {
644
- if (!item.insightCameraDuration) throw new Error('insightCameraDuration is required');
645
- yield cameraAnimation(item.camera, scaleByPlaybackSpeed(item.insightCameraDuration), frame);
646
- }
647
- } else if ('clear-insight' === item.type) {
648
- yield fadeOutItem(insightMarkContainer, scaleByPlaybackSpeed(item.duration), frame);
649
- insightMarkContainer.removeChildren();
650
- insightMarkContainer.alpha = 1;
651
- } else if ('img' === item.type) {
652
- if (item.img && item.img !== currentImg.current) {
653
- currentImg.current = item.img;
654
- yield repaintImage(item.imageWidth, item.imageHeight);
655
- }
656
- if (item.camera) yield cameraAnimation(item.camera, scaleByPlaybackSpeed(item.duration), frame);
657
- else yield sleep(item.duration);
658
- } else if ('pointer' === item.type) {
659
- if (!item.img) throw new Error('pointer img is required');
660
- yield updatePointer(item.img);
661
- } else if ('spinning-pointer' === item.type) {
662
- const stop = spinningPointer(frame);
663
- yield sleep(item.duration);
664
- stop();
665
- }
666
- }
667
- safeOnTaskChange(null);
668
- if (recorderSessionRef.current) {
669
- yield sleep(1200);
670
- recorderSessionRef.current.stop();
671
- recorderSessionRef.current = null;
672
- setIsRecording(false);
673
- }
674
- })())().catch((e)=>{
675
- console.error('player error', e);
676
- if ((null == e ? void 0 : e.message) === ERROR_FRAME_CANCEL) {
677
- console.log('Animation cancelled (expected behavior)');
678
- safeOnTaskChange(null);
679
- return;
680
- }
681
- const wasRecording = !!recorderSessionRef.current;
682
- if (recorderSessionRef.current) {
683
- try {
684
- recorderSessionRef.current.stop();
685
- } catch (stopError) {
686
- console.error('Error stopping recorder:', stopError);
687
- }
688
- recorderSessionRef.current = null;
689
- }
690
- setIsRecording(false);
691
- safeOnTaskChange(null);
692
- if (wasRecording) external_antd_namespaceObject.message.error('Failed to export video. Please try again.');
693
- }));
694
- return ()=>{
695
- null == cancelFn || cancelFn();
696
- cancelAnimationRef.current = null;
209
+ seek(e.clientX);
210
+ const onMove = (ev)=>seek(ev.clientX);
211
+ const onUp = ()=>{
212
+ bar.removeEventListener('pointermove', onMove);
213
+ bar.removeEventListener('pointerup', onUp);
697
214
  };
698
- };
215
+ bar.addEventListener('pointermove', onMove);
216
+ bar.addEventListener('pointerup', onUp);
217
+ }, [
218
+ frameMap,
219
+ player
220
+ ]);
221
+ const [isFullscreen, setIsFullscreen] = (0, external_react_namespaceObject.useState)(false);
222
+ const toggleFullscreen = (0, external_react_namespaceObject.useCallback)(()=>{
223
+ const el = wrapperRef.current;
224
+ if (!el) return;
225
+ if (document.fullscreenElement) document.exitFullscreen().then(()=>setIsFullscreen(false));
226
+ else el.requestFullscreen().then(()=>setIsFullscreen(true));
227
+ }, []);
699
228
  (0, external_react_namespaceObject.useEffect)(()=>{
700
- Promise.resolve((()=>_async_to_generator(function*() {
701
- yield init();
702
- if (divContainerRef.current && imageWidth && imageHeight) {
703
- const aspectRatio = imageWidth / imageHeight;
704
- divContainerRef.current.style.setProperty('--canvas-aspect-ratio', aspectRatio.toString());
705
- divContainerRef.current.setAttribute('data-fit-mode', fitMode);
706
- const playerContainer = divContainerRef.current.closest('.player-container');
707
- if (playerContainer) playerContainer.setAttribute('data-fit-mode', fitMode);
708
- }
709
- triggerReplay();
710
- })())());
711
- return ()=>{
712
- appInitialized.current = false;
229
+ const handler = ()=>setIsFullscreen(!!document.fullscreenElement);
230
+ document.addEventListener('fullscreenchange', handler);
231
+ return ()=>document.removeEventListener('fullscreenchange', handler);
232
+ }, []);
233
+ const [isExporting, setIsExporting] = (0, external_react_namespaceObject.useState)(false);
234
+ const [exportProgress, setExportProgress] = (0, external_react_namespaceObject.useState)(0);
235
+ const handleExportVideo = (0, external_react_namespaceObject.useCallback)(()=>_async_to_generator(function*() {
236
+ if (!frameMap || isExporting) return;
237
+ setIsExporting(true);
238
+ setExportProgress(0);
713
239
  try {
714
- app.destroy(true, {
715
- children: true,
716
- texture: true
717
- });
240
+ yield (0, export_branded_video_js_namespaceObject.exportBrandedVideo)(frameMap, (pct)=>setExportProgress(Math.round(100 * pct)));
241
+ external_antd_namespaceObject.message.success('Video exported');
718
242
  } catch (e) {
719
- console.warn('destroy failed', e);
243
+ console.error('Export failed:', e);
244
+ external_antd_namespaceObject.message.error('Export failed');
245
+ } finally{
246
+ setIsExporting(false);
247
+ setExportProgress(0);
720
248
  }
721
- };
722
- }, [
723
- imageWidth,
724
- imageHeight,
725
- fitMode
249
+ })(), [
250
+ frameMap,
251
+ isExporting
726
252
  ]);
727
- (0, external_react_namespaceObject.useEffect)(()=>{
728
- if (replayMark) return play();
729
- }, [
730
- replayMark
731
- ]);
732
- const [mouseOverStatusIcon, setMouseOverStatusIcon] = (0, external_react_namespaceObject.useState)(false);
733
- const [mouseOverSettingsIcon, setMouseOverSettingsIcon] = (0, external_react_namespaceObject.useState)(false);
734
- const progressString = Math.round(100 * animationProgress);
735
- const transitionStyle = 0 === animationProgress ? 'none' : '0.3s';
736
- const canReplayNow = 1 === animationProgress;
737
- (0, external_react_namespaceObject.useEffect)(()=>{
738
- if (canReplayNow) {
739
- const listener = (event)=>{
740
- if (' ' === event.key) triggerReplay();
741
- };
742
- window.addEventListener('keydown', listener);
743
- return ()=>{
744
- window.removeEventListener('keydown', listener);
745
- };
253
+ const chapterMarkers = (0, external_react_namespaceObject.useMemo)(()=>{
254
+ if (!frameMap) return [];
255
+ const { scriptFrames, totalDurationInFrames } = frameMap;
256
+ if (0 === totalDurationInFrames) return [];
257
+ const markers = [];
258
+ for (const sf of scriptFrames){
259
+ if ('img' !== sf.type && 'insight' !== sf.type || 0 === sf.durationInFrames) continue;
260
+ const globalFrame = sf.startFrame;
261
+ const percent = globalFrame / totalDurationInFrames * 100;
262
+ if (percent > 1 && percent < 99) {
263
+ const parts = [
264
+ sf.title,
265
+ sf.subTitle
266
+ ].filter(Boolean);
267
+ markers.push({
268
+ percent,
269
+ title: parts.length > 0 ? parts.join(': ') : `Chapter ${markers.length + 1}`,
270
+ frame: globalFrame
271
+ });
272
+ }
746
273
  }
274
+ return markers;
747
275
  }, [
748
- canReplayNow
276
+ frameMap
749
277
  ]);
750
- let statusIconElement;
751
- let statusOnClick = ()=>{};
752
- if (animationProgress < 1) statusIconElement = /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_antd_namespaceObject.Spin, {
753
- indicator: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(icons_namespaceObject.LoadingOutlined, {
754
- spin: true,
755
- color: "#333"
756
- }),
757
- size: "default"
278
+ if (!scripts || 0 === scripts.length || !frameMap) return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
279
+ className: "player-container"
758
280
  });
759
- else {
760
- statusIconElement = /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_antd_namespaceObject.Spin, {
761
- indicator: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(icons_namespaceObject.CaretRightOutlined, {
762
- color: "#333"
763
- }),
764
- size: "default"
765
- });
766
- statusOnClick = ()=>triggerReplay();
767
- }
768
- return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("div", {
281
+ const imgW = frameMap.imageWidth;
282
+ const imgH = frameMap.imageHeight;
283
+ const compositionWidth = imgW;
284
+ const compositionHeight = imgH;
285
+ const isPortraitCanvas = imgH > imgW;
286
+ const totalFrames = frameMap.totalDurationInFrames;
287
+ const seekPercent = totalFrames > 1 ? player.currentFrame / (totalFrames - 1) * 100 : 0;
288
+ return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
769
289
  className: "player-container",
770
- children: [
771
- /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
772
- className: "canvas-container",
773
- ref: divContainerRef
774
- }),
775
- /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
776
- className: "player-timeline-wrapper",
777
- children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
778
- className: "player-timeline",
779
- children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
780
- className: "player-timeline-progress",
290
+ "data-fit-mode": null == props ? void 0 : props.fitMode,
291
+ children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
292
+ className: "canvas-container",
293
+ children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("div", {
294
+ className: "player-wrapper",
295
+ ref: wrapperRef,
296
+ "data-portrait": isPortraitCanvas ? '' : void 0,
297
+ style: {
298
+ aspectRatio: `${compositionWidth}/${compositionHeight}`
299
+ },
300
+ onMouseMove: showControls,
301
+ onMouseEnter: onMouseEnter,
302
+ onMouseLeave: onMouseLeave,
303
+ children: [
304
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
305
+ ref: renderLayerRef,
781
306
  style: {
782
- width: `${progressString}%`,
783
- transition: transitionStyle
784
- }
785
- })
786
- })
787
- }),
788
- /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
789
- className: "player-tools-wrapper",
790
- children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
791
- className: "player-tools",
792
- children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("div", {
793
- className: "player-control",
794
- children: [
795
- /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("div", {
796
- className: "status-text",
797
- children: [
798
- /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
799
- className: "title",
800
- children: titleText
801
- }),
802
- /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_antd_namespaceObject.Tooltip, {
803
- title: subTitleText,
804
- children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
805
- className: "subtitle",
806
- children: subTitleText
807
- })
808
- })
809
- ]
810
- }),
811
- isRecording ? null : /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
812
- className: "status-icon",
813
- onMouseEnter: ()=>setMouseOverStatusIcon(true),
814
- onMouseLeave: ()=>setMouseOverStatusIcon(false),
815
- onClick: statusOnClick,
816
- children: statusIconElement
817
- }),
818
- (null == props ? void 0 : props.reportFileContent) && (null == props ? void 0 : props.canDownloadReport) !== false ? /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_antd_namespaceObject.Tooltip, {
819
- title: "Download Report",
820
- children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
821
- className: "status-icon",
822
- onMouseEnter: ()=>setMouseOverStatusIcon(true),
823
- onMouseLeave: ()=>setMouseOverStatusIcon(false),
824
- onClick: ()=>downloadReport(props.reportFileContent),
825
- children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(icons_namespaceObject.DownloadOutlined, {
826
- color: "#333"
827
- })
828
- })
829
- }) : null,
830
- /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_antd_namespaceObject.Tooltip, {
831
- title: isRecording ? 'Generating...' : 'Export Video',
307
+ display: 'flex',
308
+ justifyContent: 'center',
309
+ alignItems: 'center',
310
+ width: '100%',
311
+ height: '100%',
312
+ overflow: 'hidden'
313
+ },
314
+ onClick: player.toggle,
315
+ children: (()=>{
316
+ const scale = containerSize.width > 0 && containerSize.height > 0 ? Math.min(containerSize.width / compositionWidth, containerSize.height / compositionHeight) : 1;
317
+ return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
318
+ style: {
319
+ width: compositionWidth * scale,
320
+ height: compositionHeight * scale,
321
+ flexShrink: 0,
322
+ position: 'relative',
323
+ overflow: 'hidden'
324
+ },
832
325
  children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
833
- className: "status-icon",
834
- onClick: isRecording ? void 0 : handleExport,
835
326
  style: {
836
- opacity: isRecording ? 0.5 : 1,
837
- cursor: isRecording ? 'not-allowed' : 'pointer'
327
+ width: compositionWidth,
328
+ height: compositionHeight,
329
+ transformOrigin: '0 0',
330
+ transform: `scale(${scale})`
838
331
  },
839
- children: isRecording ? /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_antd_namespaceObject.Spin, {
840
- size: "default",
841
- percent: progressString
842
- }) : /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(icons_namespaceObject.ExportOutlined, {})
332
+ children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(StepScene_js_namespaceObject.StepsTimeline, {
333
+ frameMap: frameMap,
334
+ autoZoom: autoZoom,
335
+ frame: player.currentFrame,
336
+ width: compositionWidth,
337
+ height: compositionHeight,
338
+ fps: frameMap.fps
339
+ })
843
340
  })
341
+ });
342
+ })()
343
+ }),
344
+ subtitleEnabled && subtitle && /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("div", {
345
+ className: "player-subtitle",
346
+ children: [
347
+ subtitle.title && /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("span", {
348
+ className: "player-subtitle-badge",
349
+ children: subtitle.title
844
350
  }),
845
- /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_antd_namespaceObject.Dropdown, {
846
- trigger: [
847
- 'hover',
848
- 'click'
849
- ],
850
- placement: "bottomRight",
851
- overlayStyle: {
852
- minWidth: '148px'
853
- },
854
- dropdownRender: ()=>/*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("div", {
855
- className: "player-settings-dropdown",
856
- children: [
857
- /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("div", {
858
- className: "player-settings-item",
351
+ subtitle.subTitle && /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("span", {
352
+ className: "player-subtitle-text",
353
+ children: subtitle.subTitle
354
+ })
355
+ ]
356
+ }),
357
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("div", {
358
+ className: `control-bar ${controlsVisible ? '' : 'hidden'}`,
359
+ onClick: (e)=>e.stopPropagation(),
360
+ children: [
361
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
362
+ className: "status-icon",
363
+ onClick: player.toggle,
364
+ children: player.playing ? /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(icons_namespaceObject.PauseOutlined, {}) : /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(icons_namespaceObject.CaretRightOutlined, {})
365
+ }),
366
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("span", {
367
+ className: "time-display",
368
+ children: [
369
+ formatTime(player.currentFrame, frameMap.fps),
370
+ " /",
371
+ ' ',
372
+ formatTime(totalFrames, frameMap.fps)
373
+ ]
374
+ }),
375
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("div", {
376
+ className: "seek-bar-track",
377
+ ref: seekBarRef,
378
+ onPointerDown: handleSeekPointerDown,
379
+ children: [
380
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
381
+ className: "seek-bar-fill",
382
+ style: {
383
+ width: `${seekPercent}%`
384
+ }
385
+ }),
386
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
387
+ className: "seek-bar-knob",
388
+ style: {
389
+ left: `${seekPercent}%`
390
+ }
391
+ }),
392
+ chapterMarkers.map((marker)=>/*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_antd_namespaceObject.Tooltip, {
393
+ title: marker.title,
394
+ overlayClassName: "chapter-tooltip",
395
+ children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
396
+ className: "chapter-marker",
859
397
  style: {
860
- display: 'flex',
861
- alignItems: 'center',
862
- justifyContent: 'space-between',
863
- height: '32px',
864
- padding: '0 8px',
865
- borderRadius: '4px'
398
+ left: `${marker.percent}%`
866
399
  },
400
+ onClick: (e)=>{
401
+ e.stopPropagation();
402
+ player.seekTo(marker.frame);
403
+ }
404
+ })
405
+ }, marker.percent))
406
+ ]
407
+ }),
408
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("div", {
409
+ className: "player-custom-controls",
410
+ children: [
411
+ (null == props ? void 0 : props.reportFileContent) && (null == props ? void 0 : props.canDownloadReport) !== false ? /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_antd_namespaceObject.Tooltip, {
412
+ title: "Download Report",
413
+ children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
414
+ className: "status-icon",
415
+ onClick: ()=>downloadReport(props.reportFileContent),
416
+ children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(icons_namespaceObject.DownloadOutlined, {})
417
+ })
418
+ }) : null,
419
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_antd_namespaceObject.Dropdown, {
420
+ trigger: [
421
+ 'hover',
422
+ 'click'
423
+ ],
424
+ placement: "topRight",
425
+ overlayStyle: {
426
+ minWidth: '148px'
427
+ },
428
+ dropdownRender: ()=>/*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("div", {
429
+ className: "player-settings-dropdown",
867
430
  children: [
868
431
  /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("div", {
432
+ className: "player-settings-item",
869
433
  style: {
870
434
  display: 'flex',
871
435
  alignItems: 'center',
872
- gap: '4px'
436
+ gap: '4px',
437
+ height: '32px',
438
+ padding: '0 8px',
439
+ borderRadius: '4px',
440
+ cursor: isExporting ? 'not-allowed' : 'pointer',
441
+ opacity: isExporting ? 0.5 : 1
873
442
  },
443
+ onClick: isExporting ? void 0 : handleExportVideo,
874
444
  children: [
875
- /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(global_perspective_js_default(), {
445
+ isExporting ? /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_antd_namespaceObject.Spin, {
446
+ size: "small"
447
+ }) : /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(icons_namespaceObject.ExportOutlined, {
876
448
  style: {
877
449
  width: '16px',
878
450
  height: '16px'
@@ -880,103 +452,169 @@ function Player(props) {
880
452
  }),
881
453
  /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("span", {
882
454
  style: {
883
- fontSize: '12px',
884
- marginRight: '16px'
455
+ fontSize: '12px'
885
456
  },
886
- children: "Focus on cursor"
457
+ children: isExporting ? `Exporting ${exportProgress}%` : 'Export video'
887
458
  })
888
459
  ]
889
460
  }),
890
- /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_antd_namespaceObject.Switch, {
891
- size: "small",
892
- checked: autoZoom,
893
- onChange: (checked)=>{
894
- setAutoZoom(checked);
895
- triggerReplay();
896
- }
897
- })
898
- ]
899
- }),
900
- /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
901
- className: "player-settings-divider"
902
- }),
903
- /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("div", {
904
- style: {
905
- display: 'flex',
906
- alignItems: 'center',
907
- gap: '4px',
908
- height: '32px',
909
- padding: '0 8px'
910
- },
911
- children: [
912
- /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(icons_namespaceObject.ThunderboltOutlined, {
461
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
462
+ className: "player-settings-divider"
463
+ }),
464
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("div", {
465
+ className: "player-settings-item",
913
466
  style: {
914
- width: '16px',
915
- height: '16px'
916
- }
467
+ display: 'flex',
468
+ alignItems: 'center',
469
+ justifyContent: 'space-between',
470
+ height: '32px',
471
+ padding: '0 8px',
472
+ borderRadius: '4px'
473
+ },
474
+ children: [
475
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("div", {
476
+ style: {
477
+ display: 'flex',
478
+ alignItems: 'center',
479
+ gap: '4px'
480
+ },
481
+ children: [
482
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(global_perspective_js_default(), {
483
+ style: {
484
+ width: '16px',
485
+ height: '16px'
486
+ }
487
+ }),
488
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("span", {
489
+ style: {
490
+ fontSize: '12px',
491
+ marginRight: '16px'
492
+ },
493
+ children: "Focus on cursor"
494
+ })
495
+ ]
496
+ }),
497
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_antd_namespaceObject.Switch, {
498
+ size: "small",
499
+ checked: autoZoom,
500
+ onChange: (checked)=>setAutoZoom(checked)
501
+ })
502
+ ]
503
+ }),
504
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("div", {
505
+ className: "player-settings-item",
506
+ style: {
507
+ display: 'flex',
508
+ alignItems: 'center',
509
+ justifyContent: 'space-between',
510
+ height: '32px',
511
+ padding: '0 8px',
512
+ borderRadius: '4px'
513
+ },
514
+ children: [
515
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("div", {
516
+ style: {
517
+ display: 'flex',
518
+ alignItems: 'center',
519
+ gap: '4px'
520
+ },
521
+ children: [
522
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(icons_namespaceObject.FontSizeOutlined, {
523
+ style: {
524
+ width: '16px',
525
+ height: '16px'
526
+ }
527
+ }),
528
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("span", {
529
+ style: {
530
+ fontSize: '12px',
531
+ marginRight: '16px'
532
+ },
533
+ children: "Subtitle"
534
+ })
535
+ ]
536
+ }),
537
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_antd_namespaceObject.Switch, {
538
+ size: "small",
539
+ checked: subtitleEnabled,
540
+ onChange: (checked)=>setSubtitleEnabled(checked)
541
+ })
542
+ ]
543
+ }),
544
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
545
+ className: "player-settings-divider"
917
546
  }),
918
- /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("span", {
547
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("div", {
919
548
  style: {
920
- fontSize: '12px'
549
+ display: 'flex',
550
+ alignItems: 'center',
551
+ gap: '4px',
552
+ height: '32px',
553
+ padding: '0 8px'
921
554
  },
922
- children: "Playback speed"
923
- })
555
+ children: [
556
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(icons_namespaceObject.ThunderboltOutlined, {
557
+ style: {
558
+ width: '16px',
559
+ height: '16px'
560
+ }
561
+ }),
562
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("span", {
563
+ style: {
564
+ fontSize: '12px'
565
+ },
566
+ children: "Playback speed"
567
+ })
568
+ ]
569
+ }),
570
+ [
571
+ 0.5,
572
+ 1,
573
+ 1.5,
574
+ 2
575
+ ].map((speed)=>/*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("div", {
576
+ onClick: ()=>setPlaybackSpeed(speed),
577
+ style: {
578
+ height: '32px',
579
+ lineHeight: '32px',
580
+ padding: '0 8px 0 24px',
581
+ fontSize: '12px',
582
+ cursor: 'pointer',
583
+ borderRadius: '4px'
584
+ },
585
+ className: `player-speed-option${playbackSpeed === speed ? ' active' : ''}`,
586
+ children: [
587
+ speed,
588
+ "x"
589
+ ]
590
+ }, speed))
924
591
  ]
925
592
  }),
926
- [
927
- 0.5,
928
- 1,
929
- 1.5,
930
- 2
931
- ].map((speed)=>/*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("div", {
932
- onClick: ()=>{
933
- setPlaybackSpeed(speed);
934
- triggerReplay();
935
- },
936
- style: {
937
- height: '32px',
938
- lineHeight: '32px',
939
- padding: '0 8px 0 24px',
940
- fontSize: '12px',
941
- cursor: 'pointer',
942
- borderRadius: '4px'
943
- },
944
- className: `player-speed-option${playbackSpeed === speed ? ' active' : ''}`,
945
- children: [
946
- speed,
947
- "x"
948
- ]
949
- }, speed))
950
- ]
593
+ menu: {
594
+ items: []
595
+ },
596
+ children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
597
+ className: "status-icon",
598
+ children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(player_setting_js_default(), {
599
+ style: {
600
+ width: '16px',
601
+ height: '16px'
602
+ }
603
+ })
604
+ })
951
605
  }),
952
- menu: {
953
- items: []
954
- },
955
- children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
956
- className: "status-icon",
957
- onMouseEnter: ()=>setMouseOverSettingsIcon(true),
958
- onMouseLeave: ()=>setMouseOverSettingsIcon(false),
959
- style: {
960
- cursor: 'pointer',
961
- display: 'flex',
962
- alignItems: 'center',
963
- justifyContent: 'center',
964
- opacity: mouseOverSettingsIcon ? 1 : 0.7,
965
- transition: 'opacity 0.2s'
966
- },
967
- children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(player_setting_js_default(), {
968
- style: {
969
- width: '16px',
970
- height: '16px'
971
- }
606
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
607
+ className: "status-icon",
608
+ onClick: toggleFullscreen,
609
+ children: isFullscreen ? /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(icons_namespaceObject.CompressOutlined, {}) : /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(icons_namespaceObject.ExpandOutlined, {})
972
610
  })
973
- })
611
+ ]
974
612
  })
975
613
  ]
976
614
  })
977
- })
615
+ ]
978
616
  })
979
- ]
617
+ })
980
618
  });
981
619
  }
982
620
  exports.Player = __webpack_exports__.Player;