@midscene/playground-app 1.8.0 → 1.8.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.
@@ -212,7 +212,8 @@
212
212
 
213
213
  .platform-selector-group .ant-radio-button-wrapper {
214
214
  white-space: normal;
215
- background: #fff;
215
+ border-color: var(--midscene-border-strong, #e9ecf3);
216
+ background: var(--midscene-surface-elevated, #fff);
216
217
  border-radius: 14px;
217
218
  justify-content: flex-start;
218
219
  align-items: flex-start;
@@ -252,24 +253,24 @@
252
253
  }
253
254
 
254
255
  .platform-selector-group .ant-radio-button-wrapper:hover {
255
- border-color: #1677ff;
256
+ border-color: var(--midscene-brand, #1677ff);
256
257
  transform: translateY(-1px);
257
258
  }
258
259
 
259
260
  .platform-selector-group .ant-radio-button-wrapper-checked {
260
- border-color: #1677ff;
261
+ border-color: var(--midscene-brand, #1677ff);
261
262
  box-shadow: 0 10px 24px rgba(22, 119, 255, .12);
262
263
  }
263
264
 
264
265
  .platform-selector-card .platform-selector-title {
265
- color: rgba(0, 0, 0, .88);
266
+ color: var(--midscene-text-primary, rgba(0, 0, 0, .88));
266
267
  font-size: 15px;
267
268
  font-weight: 600;
268
269
  line-height: 1.4;
269
270
  }
270
271
 
271
272
  .platform-selector-card .platform-selector-description {
272
- color: rgba(0, 0, 0, .5);
273
+ color: var(--midscene-text-tertiary, rgba(0, 0, 0, .5));
273
274
  margin-top: 6px;
274
275
  font-size: 12px;
275
276
  line-height: 1.5;
@@ -283,11 +284,11 @@
283
284
  }
284
285
 
285
286
  .session-select-option-label {
286
- color: rgba(0, 0, 0, .88);
287
+ color: var(--midscene-text-primary, rgba(0, 0, 0, .88));
287
288
  }
288
289
 
289
290
  .session-select-option-description {
290
- color: rgba(0, 0, 0, .45);
291
+ color: var(--midscene-text-tertiary, rgba(0, 0, 0, .45));
291
292
  font-size: 12px;
292
293
  }
293
294
 
@@ -1,5 +1,5 @@
1
1
  import { PlaygroundSDK } from "@midscene/playground";
2
- import { useEnvConfig } from "@midscene/visualizer";
2
+ import { notifyError, useEnvConfig } from "@midscene/visualizer";
3
3
  import { Form, message } from "antd";
4
4
  import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
5
5
  import { resolveAutoCreateSessionInput } from "../session-setup.mjs";
@@ -80,7 +80,7 @@ function getPlatformSelectorFieldKey(setup) {
80
80
  var _setup_platformSelector;
81
81
  return null == setup ? void 0 : null == (_setup_platformSelector = setup.platformSelector) ? void 0 : _setup_platformSelector.fieldKey;
82
82
  }
83
- function usePlaygroundController({ serverUrl, defaultDeviceType = 'web', pollIntervalMs = 5000, countdownSeconds = 3, initialFormValues }) {
83
+ function usePlaygroundController({ serverUrl, defaultDeviceType = 'web', pollIntervalMs = 5000, countdownSeconds = 3, initialFormValues, onCountdownFinish }) {
84
84
  const [form] = Form.useForm();
85
85
  const initialFormValuesRef = useRef(initialFormValues);
86
86
  useLayoutEffect(()=>{
@@ -144,8 +144,9 @@ function usePlaygroundController({ serverUrl, defaultDeviceType = 'web', pollInt
144
144
  appliedAiConfigSignatureRef.current = aiConfigSignature;
145
145
  return true;
146
146
  } catch (error) {
147
- const errorMessage = error instanceof Error ? error.message : 'Failed to apply AI configuration';
148
- message.error(errorMessage);
147
+ notifyError(error, {
148
+ title: 'Failed to apply AI configuration'
149
+ });
149
150
  return false;
150
151
  } finally{
151
152
  if (pendingAiConfigApplicationRef.current === pendingApplicationState) pendingAiConfigApplicationRef.current = null;
@@ -160,6 +161,7 @@ function usePlaygroundController({ serverUrl, defaultDeviceType = 'web', pollInt
160
161
  playgroundSDK
161
162
  ]);
162
163
  const finishCountdown = useCallback(()=>{
164
+ const wasActive = null !== countdownTimerRef.current;
163
165
  if (null !== countdownTimerRef.current) {
164
166
  window.clearInterval(countdownTimerRef.current);
165
167
  countdownTimerRef.current = null;
@@ -168,7 +170,10 @@ function usePlaygroundController({ serverUrl, defaultDeviceType = 'web', pollInt
168
170
  countdownResolveRef.current = null;
169
171
  if (mountedRef.current) setCountdown(null);
170
172
  null == resolve || resolve();
171
- }, []);
173
+ if (wasActive && mountedRef.current) null == onCountdownFinish || onCountdownFinish();
174
+ }, [
175
+ onCountdownFinish
176
+ ]);
172
177
  const showCountdownModal = useCallback(()=>_async_to_generator(function*() {
173
178
  if (countdownSeconds <= 0) return;
174
179
  finishCountdown();
@@ -249,8 +254,9 @@ function usePlaygroundController({ serverUrl, defaultDeviceType = 'web', pollInt
249
254
  return true;
250
255
  } catch (error) {
251
256
  if (error.errorFields) return false;
252
- const errorMessage = error instanceof Error ? error.message : 'Failed to create Agent';
253
- message.error(errorMessage);
257
+ notifyError(error, {
258
+ title: 'Failed to create Agent'
259
+ });
254
260
  return false;
255
261
  } finally{
256
262
  sessionMutatingRef.current = false;
@@ -273,8 +279,9 @@ function usePlaygroundController({ serverUrl, defaultDeviceType = 'web', pollInt
273
279
  yield refreshServerState();
274
280
  yield refreshSessionSetup();
275
281
  } catch (error) {
276
- const errorMessage = error instanceof Error ? error.message : 'Failed to disconnect session';
277
- message.error(errorMessage);
282
+ notifyError(error, {
283
+ title: 'Failed to disconnect session'
284
+ });
278
285
  } finally{
279
286
  sessionMutatingRef.current = false;
280
287
  setSessionMutating(false);
@@ -53,9 +53,6 @@ function PlaygroundConversationPanel({ controller, appVersion, title = 'Playgrou
53
53
  onCancel: actions.finishCountdown,
54
54
  centered: true,
55
55
  width: 400,
56
- style: {
57
- top: '30%'
58
- },
59
56
  styles: {
60
57
  mask: {
61
58
  backgroundColor: 'rgba(0, 0, 0, 0.75)'
@@ -69,7 +66,7 @@ function PlaygroundConversationPanel({ controller, appVersion, title = 'Playgrou
69
66
  children: [
70
67
  /*#__PURE__*/ jsx("div", {
71
68
  style: {
72
- fontSize: '72px',
69
+ fontSize: '120px',
73
70
  fontWeight: 'bold',
74
71
  color: 'GO!' === state.countdown ? '#52c41a' : '#1890ff',
75
72
  marginBottom: '24px',
@@ -1,3 +1,8 @@
1
+ function toUint8Array(data) {
2
+ if (data instanceof Uint8Array) return data;
3
+ if (data instanceof ArrayBuffer) return new Uint8Array(data);
4
+ return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
5
+ }
1
6
  function createScrcpyVideoStream(socket) {
2
7
  let configurationPacketSent = false;
3
8
  let pendingDataPackets = [];
@@ -19,7 +24,7 @@ function createScrcpyVideoStream(socket) {
19
24
  start (controller) {
20
25
  const handleVideoData = (data)=>{
21
26
  try {
22
- const payload = new Uint8Array(data.data);
27
+ const payload = toUint8Array(data.data);
23
28
  if ('configuration' === data.type) return void controller.enqueue({
24
29
  type: 'configuration',
25
30
  data: payload
@@ -212,7 +212,8 @@
212
212
 
213
213
  .platform-selector-group .ant-radio-button-wrapper {
214
214
  white-space: normal;
215
- background: #fff;
215
+ border-color: var(--midscene-border-strong, #e9ecf3);
216
+ background: var(--midscene-surface-elevated, #fff);
216
217
  border-radius: 14px;
217
218
  justify-content: flex-start;
218
219
  align-items: flex-start;
@@ -252,24 +253,24 @@
252
253
  }
253
254
 
254
255
  .platform-selector-group .ant-radio-button-wrapper:hover {
255
- border-color: #1677ff;
256
+ border-color: var(--midscene-brand, #1677ff);
256
257
  transform: translateY(-1px);
257
258
  }
258
259
 
259
260
  .platform-selector-group .ant-radio-button-wrapper-checked {
260
- border-color: #1677ff;
261
+ border-color: var(--midscene-brand, #1677ff);
261
262
  box-shadow: 0 10px 24px rgba(22, 119, 255, .12);
262
263
  }
263
264
 
264
265
  .platform-selector-card .platform-selector-title {
265
- color: rgba(0, 0, 0, .88);
266
+ color: var(--midscene-text-primary, rgba(0, 0, 0, .88));
266
267
  font-size: 15px;
267
268
  font-weight: 600;
268
269
  line-height: 1.4;
269
270
  }
270
271
 
271
272
  .platform-selector-card .platform-selector-description {
272
- color: rgba(0, 0, 0, .5);
273
+ color: var(--midscene-text-tertiary, rgba(0, 0, 0, .5));
273
274
  margin-top: 6px;
274
275
  font-size: 12px;
275
276
  line-height: 1.5;
@@ -283,11 +284,11 @@
283
284
  }
284
285
 
285
286
  .session-select-option-label {
286
- color: rgba(0, 0, 0, .88);
287
+ color: var(--midscene-text-primary, rgba(0, 0, 0, .88));
287
288
  }
288
289
 
289
290
  .session-select-option-description {
290
- color: rgba(0, 0, 0, .45);
291
+ color: var(--midscene-text-tertiary, rgba(0, 0, 0, .45));
291
292
  font-size: 12px;
292
293
  }
293
294
 
@@ -108,7 +108,7 @@ function getPlatformSelectorFieldKey(setup) {
108
108
  var _setup_platformSelector;
109
109
  return null == setup ? void 0 : null == (_setup_platformSelector = setup.platformSelector) ? void 0 : _setup_platformSelector.fieldKey;
110
110
  }
111
- function usePlaygroundController({ serverUrl, defaultDeviceType = 'web', pollIntervalMs = 5000, countdownSeconds = 3, initialFormValues }) {
111
+ function usePlaygroundController({ serverUrl, defaultDeviceType = 'web', pollIntervalMs = 5000, countdownSeconds = 3, initialFormValues, onCountdownFinish }) {
112
112
  const [form] = external_antd_namespaceObject.Form.useForm();
113
113
  const initialFormValuesRef = (0, external_react_namespaceObject.useRef)(initialFormValues);
114
114
  (0, external_react_namespaceObject.useLayoutEffect)(()=>{
@@ -172,8 +172,9 @@ function usePlaygroundController({ serverUrl, defaultDeviceType = 'web', pollInt
172
172
  appliedAiConfigSignatureRef.current = aiConfigSignature;
173
173
  return true;
174
174
  } catch (error) {
175
- const errorMessage = error instanceof Error ? error.message : 'Failed to apply AI configuration';
176
- external_antd_namespaceObject.message.error(errorMessage);
175
+ (0, visualizer_namespaceObject.notifyError)(error, {
176
+ title: 'Failed to apply AI configuration'
177
+ });
177
178
  return false;
178
179
  } finally{
179
180
  if (pendingAiConfigApplicationRef.current === pendingApplicationState) pendingAiConfigApplicationRef.current = null;
@@ -188,6 +189,7 @@ function usePlaygroundController({ serverUrl, defaultDeviceType = 'web', pollInt
188
189
  playgroundSDK
189
190
  ]);
190
191
  const finishCountdown = (0, external_react_namespaceObject.useCallback)(()=>{
192
+ const wasActive = null !== countdownTimerRef.current;
191
193
  if (null !== countdownTimerRef.current) {
192
194
  window.clearInterval(countdownTimerRef.current);
193
195
  countdownTimerRef.current = null;
@@ -196,7 +198,10 @@ function usePlaygroundController({ serverUrl, defaultDeviceType = 'web', pollInt
196
198
  countdownResolveRef.current = null;
197
199
  if (mountedRef.current) setCountdown(null);
198
200
  null == resolve || resolve();
199
- }, []);
201
+ if (wasActive && mountedRef.current) null == onCountdownFinish || onCountdownFinish();
202
+ }, [
203
+ onCountdownFinish
204
+ ]);
200
205
  const showCountdownModal = (0, external_react_namespaceObject.useCallback)(()=>_async_to_generator(function*() {
201
206
  if (countdownSeconds <= 0) return;
202
207
  finishCountdown();
@@ -277,8 +282,9 @@ function usePlaygroundController({ serverUrl, defaultDeviceType = 'web', pollInt
277
282
  return true;
278
283
  } catch (error) {
279
284
  if (error.errorFields) return false;
280
- const errorMessage = error instanceof Error ? error.message : 'Failed to create Agent';
281
- external_antd_namespaceObject.message.error(errorMessage);
285
+ (0, visualizer_namespaceObject.notifyError)(error, {
286
+ title: 'Failed to create Agent'
287
+ });
282
288
  return false;
283
289
  } finally{
284
290
  sessionMutatingRef.current = false;
@@ -301,8 +307,9 @@ function usePlaygroundController({ serverUrl, defaultDeviceType = 'web', pollInt
301
307
  yield refreshServerState();
302
308
  yield refreshSessionSetup();
303
309
  } catch (error) {
304
- const errorMessage = error instanceof Error ? error.message : 'Failed to disconnect session';
305
- external_antd_namespaceObject.message.error(errorMessage);
310
+ (0, visualizer_namespaceObject.notifyError)(error, {
311
+ title: 'Failed to disconnect session'
312
+ });
306
313
  } finally{
307
314
  sessionMutatingRef.current = false;
308
315
  setSessionMutating(false);
@@ -81,9 +81,6 @@ function PlaygroundConversationPanel({ controller, appVersion, title = 'Playgrou
81
81
  onCancel: actions.finishCountdown,
82
82
  centered: true,
83
83
  width: 400,
84
- style: {
85
- top: '30%'
86
- },
87
84
  styles: {
88
85
  mask: {
89
86
  backgroundColor: 'rgba(0, 0, 0, 0.75)'
@@ -97,7 +94,7 @@ function PlaygroundConversationPanel({ controller, appVersion, title = 'Playgrou
97
94
  children: [
98
95
  /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
99
96
  style: {
100
- fontSize: '72px',
97
+ fontSize: '120px',
101
98
  fontWeight: 'bold',
102
99
  color: 'GO!' === state.countdown ? '#52c41a' : '#1890ff',
103
100
  marginBottom: '24px',
@@ -26,6 +26,11 @@ __webpack_require__.r(__webpack_exports__);
26
26
  __webpack_require__.d(__webpack_exports__, {
27
27
  createScrcpyVideoStream: ()=>createScrcpyVideoStream
28
28
  });
29
+ function toUint8Array(data) {
30
+ if (data instanceof Uint8Array) return data;
31
+ if (data instanceof ArrayBuffer) return new Uint8Array(data);
32
+ return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
33
+ }
29
34
  function createScrcpyVideoStream(socket) {
30
35
  let configurationPacketSent = false;
31
36
  let pendingDataPackets = [];
@@ -47,7 +52,7 @@ function createScrcpyVideoStream(socket) {
47
52
  start (controller) {
48
53
  const handleVideoData = (data)=>{
49
54
  try {
50
- const payload = new Uint8Array(data.data);
55
+ const payload = toUint8Array(data.data);
51
56
  if ('configuration' === data.type) return void controller.enqueue({
52
57
  type: 'configuration',
53
58
  data: payload
@@ -12,5 +12,12 @@ export interface UsePlaygroundControllerOptions {
12
12
  * returning a generic "Choose a platform" setup.
13
13
  */
14
14
  initialFormValues?: Record<string, unknown>;
15
+ /**
16
+ * Invoked when an active countdown dismisses — either reaching its natural
17
+ * end or being skipped by the user. Lets hosts step out of the way before
18
+ * automation begins (Studio minimises so the controlled desktop is in
19
+ * view). Not fired during unmount cleanup.
20
+ */
21
+ onCountdownFinish?: () => void;
15
22
  }
16
- export declare function usePlaygroundController({ serverUrl, defaultDeviceType, pollIntervalMs, countdownSeconds, initialFormValues, }: UsePlaygroundControllerOptions): PlaygroundControllerResult;
23
+ export declare function usePlaygroundController({ serverUrl, defaultDeviceType, pollIntervalMs, countdownSeconds, initialFormValues, onCountdownFinish, }: UsePlaygroundControllerOptions): PlaygroundControllerResult;
@@ -1,7 +1,8 @@
1
1
  import type { ScrcpyMediaStreamPacket } from '@yume-chan/scrcpy';
2
+ type RawScrcpyVideoData = ArrayBuffer | ArrayBufferView;
2
3
  interface RawScrcpyVideoPacket {
3
4
  type?: string;
4
- data: ArrayLike<number>;
5
+ data: RawScrcpyVideoData;
5
6
  keyFrame?: boolean;
6
7
  }
7
8
  interface ScrcpyVideoSocketLike {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@midscene/playground-app",
3
- "version": "1.8.0",
3
+ "version": "1.8.1",
4
4
  "description": "Reusable React shell for Midscene playground applications",
5
5
  "repository": "https://github.com/web-infra-dev/midscene",
6
6
  "homepage": "https://midscenejs.com/",
@@ -28,9 +28,9 @@
28
28
  "antd": "^5.21.6",
29
29
  "react-resizable-panels": "2.0.22",
30
30
  "socket.io-client": "4.8.1",
31
- "@midscene/playground": "1.8.0",
32
- "@midscene/shared": "1.8.0",
33
- "@midscene/visualizer": "1.8.0"
31
+ "@midscene/playground": "1.8.1",
32
+ "@midscene/shared": "1.8.1",
33
+ "@midscene/visualizer": "1.8.1"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@rsbuild/plugin-less": "^1.5.0",