@midscene/visualizer 0.17.5 → 0.17.6-beta-20250607054355.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.
@@ -15,6 +15,10 @@ var __spreadValues = (a, b) => {
15
15
  }
16
16
  return a;
17
17
  };
18
+ var __publicField = (obj, key, value) => {
19
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
20
+ return value;
21
+ };
18
22
  var __async = (__this, __arguments, generator) => {
19
23
  return new Promise((resolve, reject) => {
20
24
  var fulfilled = (value) => {
@@ -50,6 +54,7 @@ import { mouseLoading, mousePointer } from "../utils";
50
54
  import {
51
55
  CaretRightOutlined,
52
56
  DownloadOutlined,
57
+ ExportOutlined,
53
58
  LoadingOutlined
54
59
  } from "@ant-design/icons";
55
60
  import { Spin, Tooltip } from "antd";
@@ -120,6 +125,49 @@ const downloadReport = (content) => {
120
125
  a.download = "midscene_report.html";
121
126
  a.click();
122
127
  };
128
+ class RecordingSession {
129
+ constructor(canvas) {
130
+ __publicField(this, "canvas");
131
+ __publicField(this, "mediaRecorder", null);
132
+ __publicField(this, "chunks");
133
+ __publicField(this, "recording", false);
134
+ this.canvas = canvas;
135
+ this.chunks = [];
136
+ }
137
+ start() {
138
+ const stream = this.canvas.captureStream(60);
139
+ const mediaRecorder = new MediaRecorder(stream, {
140
+ mimeType: "video/webm"
141
+ });
142
+ mediaRecorder.ondataavailable = (event) => {
143
+ if (event.data.size > 0) {
144
+ this.chunks.push(event.data);
145
+ }
146
+ };
147
+ this.mediaRecorder = mediaRecorder;
148
+ this.recording = true;
149
+ return this.mediaRecorder.start();
150
+ }
151
+ stop() {
152
+ var _a;
153
+ if (!this.recording || !this.mediaRecorder) {
154
+ console.warn("not recording");
155
+ return;
156
+ }
157
+ this.mediaRecorder.onstop = () => {
158
+ const blob = new Blob(this.chunks, { type: "video/webm" });
159
+ const url = URL.createObjectURL(blob);
160
+ const a = document.createElement("a");
161
+ a.href = url;
162
+ a.download = "midscene_replay.webm";
163
+ a.click();
164
+ URL.revokeObjectURL(url);
165
+ };
166
+ (_a = this.mediaRecorder) == null ? void 0 : _a.stop();
167
+ this.recording = false;
168
+ this.mediaRecorder = null;
169
+ }
170
+ }
123
171
  function Player(props) {
124
172
  var _a;
125
173
  const [titleText, setTitleText] = useState("");
@@ -133,6 +181,9 @@ function Player(props) {
133
181
  const pointerSprite = useRef(null);
134
182
  const spinningPointerSprite = useRef(null);
135
183
  const [replayMark, setReplayMark] = useState(0);
184
+ const triggerReplay = () => {
185
+ setReplayMark(Date.now());
186
+ };
136
187
  const windowContentContainer = useMemo(() => {
137
188
  const container = new PIXI.Container();
138
189
  return container;
@@ -448,6 +499,21 @@ function Player(props) {
448
499
  insightMarkContainer.y = 0;
449
500
  windowContentContainer.addChild(insightMarkContainer);
450
501
  });
502
+ const [isRecording, setIsRecording] = useState(false);
503
+ const recorderSessionRef = useRef(null);
504
+ const handleExport = () => {
505
+ if (recorderSessionRef.current) {
506
+ console.warn("recorderSession exists");
507
+ return;
508
+ }
509
+ if (!app.canvas) {
510
+ console.warn("canvas is not initialized");
511
+ return;
512
+ }
513
+ recorderSessionRef.current = new RecordingSession(app.canvas);
514
+ setIsRecording(true);
515
+ triggerReplay();
516
+ };
451
517
  const play = () => {
452
518
  let cancelFn;
453
519
  Promise.resolve(
@@ -470,7 +536,7 @@ function Player(props) {
470
536
  yield repaintImage();
471
537
  yield updateCamera(__spreadValues({}, basicCameraState));
472
538
  const totalDuration = scripts.reduce((acc, item) => {
473
- return acc + item.duration + (item.insightCameraDuration || 0);
539
+ return acc + item.duration + (item.camera && item.insightCameraDuration ? item.insightCameraDuration : 0);
474
540
  }, 0);
475
541
  const progressUpdateInterval = 200;
476
542
  const startTime = performance.now();
@@ -486,6 +552,9 @@ function Player(props) {
486
552
  }
487
553
  };
488
554
  frame(updateProgress);
555
+ if (recorderSessionRef.current) {
556
+ recorderSessionRef.current.start();
557
+ }
489
558
  for (const index in scripts) {
490
559
  const item = scripts[index];
491
560
  setTitleText(item.title || "");
@@ -542,6 +611,11 @@ function Player(props) {
542
611
  stop();
543
612
  }
544
613
  }
614
+ if (recorderSessionRef.current) {
615
+ recorderSessionRef.current.stop();
616
+ recorderSessionRef.current = null;
617
+ setIsRecording(false);
618
+ }
545
619
  }))().catch((e) => {
546
620
  console.error("player error", e);
547
621
  })
@@ -554,7 +628,7 @@ function Player(props) {
554
628
  Promise.resolve(
555
629
  (() => __async(this, null, function* () {
556
630
  yield init();
557
- setReplayMark(Date.now());
631
+ triggerReplay();
558
632
  }))()
559
633
  );
560
634
  return () => {
@@ -578,7 +652,7 @@ function Player(props) {
578
652
  if (canReplayNow) {
579
653
  const listener = (event) => {
580
654
  if (event.key === " ") {
581
- setReplayMark(Date.now());
655
+ triggerReplay();
582
656
  }
583
657
  };
584
658
  window.addEventListener("keydown", listener);
@@ -594,7 +668,7 @@ function Player(props) {
594
668
  statusIconElement = /* @__PURE__ */ jsx(Spin, { indicator: /* @__PURE__ */ jsx(LoadingOutlined, { spin: true, color: "#333" }), size: "default" });
595
669
  } else if (mouseOverStatusIcon) {
596
670
  statusIconElement = /* @__PURE__ */ jsx(Spin, { indicator: /* @__PURE__ */ jsx(CaretRightOutlined, { color: "#333" }), size: "default" });
597
- statusOnClick = () => setReplayMark(Date.now());
671
+ statusOnClick = () => triggerReplay();
598
672
  } else {
599
673
  statusIconElement = // <Spin indicator={<CheckCircleOutlined />} size="default" />
600
674
  /* @__PURE__ */ jsx(Spin, { indicator: /* @__PURE__ */ jsx(CaretRightOutlined, { color: "#333" }), size: "default" });
@@ -616,7 +690,7 @@ function Player(props) {
616
690
  /* @__PURE__ */ jsx("div", { className: "title", children: titleText }),
617
691
  /* @__PURE__ */ jsx(Tooltip, { title: subTitleText, children: /* @__PURE__ */ jsx("div", { className: "subtitle", children: subTitleText }) })
618
692
  ] }),
619
- /* @__PURE__ */ jsx(
693
+ isRecording ? null : /* @__PURE__ */ jsx(
620
694
  "div",
621
695
  {
622
696
  className: "status-icon",
@@ -626,7 +700,7 @@ function Player(props) {
626
700
  children: statusIconElement
627
701
  }
628
702
  ),
629
- (props == null ? void 0 : props.reportFileContent) ? /* @__PURE__ */ jsx(
703
+ (props == null ? void 0 : props.reportFileContent) ? /* @__PURE__ */ jsx(Tooltip, { title: "Download Report", children: /* @__PURE__ */ jsx(
630
704
  "div",
631
705
  {
632
706
  className: "status-icon",
@@ -635,7 +709,19 @@ function Player(props) {
635
709
  onClick: () => downloadReport(props.reportFileContent),
636
710
  children: /* @__PURE__ */ jsx(DownloadOutlined, { color: "#333" })
637
711
  }
638
- ) : null
712
+ ) }) : null,
713
+ /* @__PURE__ */ jsx(Tooltip, { title: isRecording ? "Generating..." : "Export Video", children: /* @__PURE__ */ jsx(
714
+ "div",
715
+ {
716
+ className: "status-icon",
717
+ onClick: isRecording ? void 0 : handleExport,
718
+ style: {
719
+ opacity: isRecording ? 0.5 : 1,
720
+ cursor: isRecording ? "not-allowed" : "pointer"
721
+ },
722
+ children: isRecording ? /* @__PURE__ */ jsx(Spin, { size: "default", percent: progressString }) : /* @__PURE__ */ jsx(ExportOutlined, {})
723
+ }
724
+ ) })
639
725
  ] }) }) })
640
726
  ] });
641
727
  }
package/dist/es/index.js CHANGED
@@ -39,6 +39,7 @@ import {
39
39
  getPlaceholderForType,
40
40
  blankResult
41
41
  } from "./component/playground/playground-utils";
42
+ import { timeStr, filterBase64Value } from "./utils";
42
43
  export {
43
44
  Blackboard,
44
45
  ContextPreview,
@@ -55,6 +56,7 @@ export {
55
56
  blankResult,
56
57
  checkServerStatus,
57
58
  colorForName,
59
+ filterBase64Value,
58
60
  formatErrorMessage,
59
61
  generateAnimationScripts,
60
62
  getPlaceholderForType,
@@ -66,6 +68,7 @@ export {
66
68
  requestPlaygroundServer,
67
69
  staticAgentFromContext,
68
70
  timeCostStrElement,
71
+ timeStr,
69
72
  useEnvConfig,
70
73
  useServerValid,
71
74
  useStaticPageAgent