@midscene/playground-app 1.9.6 → 1.9.7

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.
@@ -114,20 +114,32 @@ function DeviceInteractionLayer({ enabled, deviceSize, onTap, onSwipe, scrollEna
114
114
  const activePointer = useRef(null);
115
115
  const composingRef = useRef(false);
116
116
  const keyboardArmedRef = useRef(false);
117
+ const keyboardFocusRequestIdRef = useRef(0);
117
118
  const lastKeyboardPointRef = useRef(null);
118
119
  const pendingWheelRef = useRef(null);
119
120
  const wheelTimerRef = useRef(null);
120
121
  const focusKeyboardSink = useCallback(()=>{
121
122
  if (keyboardEnabled) {
122
- var _keyboardSinkRef_current;
123
123
  keyboardArmedRef.current = true;
124
- null == (_keyboardSinkRef_current = keyboardSinkRef.current) || _keyboardSinkRef_current.focus({
124
+ const sink = keyboardSinkRef.current;
125
+ if (sink && document.activeElement !== sink) sink.focus({
125
126
  preventScroll: true
126
127
  });
127
128
  }
128
129
  }, [
129
130
  keyboardEnabled
130
131
  ]);
132
+ const refocusKeyboardSinkAfterTap = useCallback((focusRequestId, interaction)=>{
133
+ if (!keyboardEnabled) return;
134
+ Promise.resolve(interaction).catch(()=>void 0).then(()=>{
135
+ window.setTimeout(()=>{
136
+ if (keyboardArmedRef.current && keyboardFocusRequestIdRef.current === focusRequestId) focusKeyboardSink();
137
+ }, 0);
138
+ });
139
+ }, [
140
+ focusKeyboardSink,
141
+ keyboardEnabled
142
+ ]);
131
143
  const positionKeyboardSink = useCallback((clientX, clientY)=>{
132
144
  const overlay = overlayRef.current;
133
145
  const sink = keyboardSinkRef.current;
@@ -161,6 +173,8 @@ function DeviceInteractionLayer({ enabled, deviceSize, onTap, onSwipe, scrollEna
161
173
  return;
162
174
  }
163
175
  positionKeyboardSink(event.clientX, event.clientY);
176
+ const keyboardFocusRequestId = keyboardFocusRequestIdRef.current + 1;
177
+ keyboardFocusRequestIdRef.current = keyboardFocusRequestId;
164
178
  focusKeyboardSink();
165
179
  try {
166
180
  overlayRef.current.setPointerCapture(event.pointerId);
@@ -169,7 +183,8 @@ function DeviceInteractionLayer({ enabled, deviceSize, onTap, onSwipe, scrollEna
169
183
  startX: event.clientX,
170
184
  startY: event.clientY,
171
185
  startTime: performance.now(),
172
- contentRect
186
+ contentRect,
187
+ keyboardFocusRequestId
173
188
  };
174
189
  event.preventDefault();
175
190
  }, [
@@ -196,12 +211,13 @@ function DeviceInteractionLayer({ enabled, deviceSize, onTap, onSwipe, scrollEna
196
211
  const endPoint = projectToDevice(event.clientX, event.clientY, active.contentRect);
197
212
  if (!startPoint || !endPoint) return;
198
213
  lastKeyboardPointRef.current = endPoint;
199
- if (distance <= tapMaxDistance && duration <= tapMaxDurationMs) null == onTap || onTap(startPoint);
214
+ if (distance <= tapMaxDistance && duration <= tapMaxDurationMs) refocusKeyboardSinkAfterTap(active.keyboardFocusRequestId, null == onTap ? void 0 : onTap(startPoint));
200
215
  else null == onSwipe || onSwipe(startPoint, endPoint, Math.round(duration));
201
216
  }, [
202
217
  onTap,
203
218
  onSwipe,
204
219
  projectToDevice,
220
+ refocusKeyboardSinkAfterTap,
205
221
  tapMaxDistance,
206
222
  tapMaxDurationMs
207
223
  ]);
@@ -423,8 +439,11 @@ function DeviceInteractionLayer({ enabled, deviceSize, onTap, onSwipe, scrollEna
423
439
  top: 0,
424
440
  width: 32,
425
441
  height: 24,
426
- opacity: 0.01,
442
+ opacity: 0,
427
443
  pointerEvents: 'none',
444
+ appearance: 'none',
445
+ boxShadow: 'none',
446
+ overflow: 'hidden',
428
447
  resize: 'none',
429
448
  border: 0,
430
449
  padding: 0,
@@ -1,10 +1,13 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { globalThemeConfig } from "@midscene/visualizer";
3
- import { ConfigProvider } from "antd";
3
+ import { App, ConfigProvider } from "antd";
4
4
  function PlaygroundThemeProvider({ children }) {
5
5
  return /*#__PURE__*/ jsx(ConfigProvider, {
6
6
  theme: globalThemeConfig(),
7
- children: children
7
+ children: /*#__PURE__*/ jsx(App, {
8
+ component: false,
9
+ children: children
10
+ })
8
11
  });
9
12
  }
10
13
  export { PlaygroundThemeProvider };
@@ -2,7 +2,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { PREVIEW_TEXT_INPUT_BATCH_DELAY_MS } from "@midscene/shared/constants";
3
3
  import { ScreenshotViewer } from "@midscene/visualizer";
4
4
  import { WebCodecsVideoDecoder } from "@yume-chan/scrcpy-decoder-webcodecs";
5
- import { Alert, Popover, message } from "antd";
5
+ import { Alert, App, Popover } from "antd";
6
6
  import { useCallback, useEffect, useRef, useState } from "react";
7
7
  import { DeviceInteractionLayer } from "./DeviceInteractionLayer.mjs";
8
8
  import { ScrcpyPanel } from "./ScrcpyPanel.mjs";
@@ -34,6 +34,29 @@ function _async_to_generator(fn) {
34
34
  });
35
35
  };
36
36
  }
37
+ function _define_property(obj, key, value) {
38
+ if (key in obj) Object.defineProperty(obj, key, {
39
+ value: value,
40
+ enumerable: true,
41
+ configurable: true,
42
+ writable: true
43
+ });
44
+ else obj[key] = value;
45
+ return obj;
46
+ }
47
+ function _object_spread(target) {
48
+ for(var i = 1; i < arguments.length; i++){
49
+ var source = null != arguments[i] ? arguments[i] : {};
50
+ var ownKeys = Object.keys(source);
51
+ if ("function" == typeof Object.getOwnPropertySymbols) ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
52
+ return Object.getOwnPropertyDescriptor(source, sym).enumerable;
53
+ }));
54
+ ownKeys.forEach(function(key) {
55
+ _define_property(target, key, source[key]);
56
+ });
57
+ }
58
+ return target;
59
+ }
37
60
  function isNonLocalhostHttp() {
38
61
  try {
39
62
  const { protocol, hostname } = window.location;
@@ -44,6 +67,7 @@ function isNonLocalhostHttp() {
44
67
  }
45
68
  }
46
69
  function PreviewRenderer({ connectingOverlay, onDeviceSizeChange, onScrcpyStatusChange, renderErrorOverlay, scrcpyViewportStyle, screenshotViewerMode, playgroundSDK, runtimeInfo, serverUrl, serverOnline, isUserOperating }) {
70
+ const { message } = App.useApp();
47
71
  const previewConnection = resolvePreviewConnectionInfo(runtimeInfo, serverUrl);
48
72
  const [deviceSize, setDeviceSize] = useState(null);
49
73
  const [streamSize, setStreamSize] = useState(null);
@@ -51,6 +75,7 @@ function PreviewRenderer({ connectingOverlay, onDeviceSizeChange, onScrcpyStatus
51
75
  const [scrcpyStatus, setScrcpyStatus] = useState('connecting');
52
76
  const manualControlQueueRef = useRef(Promise.resolve());
53
77
  const pendingTextInputRef = useRef('');
78
+ const pendingTextInputPointRef = useRef(null);
54
79
  const textInputTimerRef = useRef(null);
55
80
  const textInputFlushPromiseRef = useRef(null);
56
81
  const previewContentRef = useRef(null);
@@ -65,7 +90,9 @@ function PreviewRenderer({ connectingOverlay, onDeviceSizeChange, onScrcpyStatus
65
90
  const nextTask = manualControlQueueRef.current.then(task, task);
66
91
  manualControlQueueRef.current = nextTask.catch(()=>void 0);
67
92
  return nextTask;
68
- }, []);
93
+ }, [
94
+ message
95
+ ]);
69
96
  useEffect(()=>{
70
97
  if (!serverOnline) {
71
98
  setDeviceSize(null);
@@ -136,19 +163,24 @@ function PreviewRenderer({ connectingOverlay, onDeviceSizeChange, onScrcpyStatus
136
163
  const flushPendingTextInput = useCallback(()=>{
137
164
  clearTextInputTimer();
138
165
  const text = pendingTextInputRef.current;
166
+ const point = pendingTextInputPointRef.current;
139
167
  if (!text) {
140
168
  var _textInputFlushPromiseRef_current;
141
169
  return null != (_textInputFlushPromiseRef_current = textInputFlushPromiseRef.current) ? _textInputFlushPromiseRef_current : Promise.resolve();
142
170
  }
143
171
  pendingTextInputRef.current = '';
172
+ pendingTextInputPointRef.current = null;
144
173
  var _textInputFlushPromiseRef_current1;
145
174
  const previousFlush = null != (_textInputFlushPromiseRef_current1 = textInputFlushPromiseRef.current) ? _textInputFlushPromiseRef_current1 : Promise.resolve();
146
175
  const flushPromise = previousFlush.catch(()=>void 0).then(()=>_async_to_generator(function*() {
147
- const res = yield enqueueManualControl(()=>playgroundSDK.interact({
176
+ const res = yield enqueueManualControl(()=>playgroundSDK.interact(_object_spread({
148
177
  actionType: 'Input',
149
178
  value: text,
150
179
  mode: 'typeOnly'
151
- }));
180
+ }, point ? {
181
+ x: point.x,
182
+ y: point.y
183
+ } : {})));
152
184
  if (!res.ok) showManualControlError('Input failed', res.error);
153
185
  })());
154
186
  const trackedFlushPromise = flushPromise.finally(()=>{
@@ -187,9 +219,10 @@ function PreviewRenderer({ connectingOverlay, onDeviceSizeChange, onScrcpyStatus
187
219
  playgroundSDK,
188
220
  showManualControlError
189
221
  ]);
190
- const handleTextInput = useCallback((text)=>{
222
+ const handleTextInput = useCallback((text, point)=>{
191
223
  if (!text) return;
192
224
  pendingTextInputRef.current += text;
225
+ if (point) pendingTextInputPointRef.current = point;
193
226
  clearTextInputTimer();
194
227
  textInputTimerRef.current = setTimeout(()=>{
195
228
  flushPendingTextInput();
@@ -202,6 +235,7 @@ function PreviewRenderer({ connectingOverlay, onDeviceSizeChange, onScrcpyStatus
202
235
  if (serverOnline && previewInteractionEnabled && manualKeyboardEnabled) return;
203
236
  clearTextInputTimer();
204
237
  pendingTextInputRef.current = '';
238
+ pendingTextInputPointRef.current = null;
205
239
  }, [
206
240
  clearTextInputTimer,
207
241
  manualKeyboardEnabled,
@@ -211,6 +245,7 @@ function PreviewRenderer({ connectingOverlay, onDeviceSizeChange, onScrcpyStatus
211
245
  useEffect(()=>()=>{
212
246
  if (serverOnline && previewInteractionEnabled && manualKeyboardEnabled && pendingTextInputRef.current) return void flushPendingTextInput();
213
247
  clearTextInputTimer();
248
+ pendingTextInputPointRef.current = null;
214
249
  }, [
215
250
  clearTextInputTimer,
216
251
  flushPendingTextInput,
@@ -1,6 +1,6 @@
1
1
  import { PlaygroundSDK } from "@midscene/playground";
2
2
  import { notifyError, useEnvConfig } from "@midscene/visualizer";
3
- import { Form, message } from "antd";
3
+ import { App, Form } from "antd";
4
4
  import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
5
5
  import { resolveAutoCreateSessionInput } from "../session-setup.mjs";
6
6
  import { buildSessionInitialValues, resolveSessionViewState } from "../session-state.mjs";
@@ -81,6 +81,7 @@ function getPlatformSelectorFieldKey(setup) {
81
81
  return null == setup ? void 0 : null == (_setup_platformSelector = setup.platformSelector) ? void 0 : _setup_platformSelector.fieldKey;
82
82
  }
83
83
  function usePlaygroundController({ serverUrl, defaultDeviceType = 'web', pollIntervalMs = 5000, countdownSeconds = 3, initialFormValues, onCountdownFinish }) {
84
+ const { message } = App.useApp();
84
85
  const [form] = Form.useForm();
85
86
  const initialFormValuesRef = useRef(initialFormValues);
86
87
  useLayoutEffect(()=>{
@@ -266,6 +267,7 @@ function usePlaygroundController({ serverUrl, defaultDeviceType = 'web', pollInt
266
267
  })(), [
267
268
  applyAiConfig,
268
269
  form,
270
+ message,
269
271
  playgroundSDK,
270
272
  refreshServerState
271
273
  ]);
@@ -288,6 +290,7 @@ function usePlaygroundController({ serverUrl, defaultDeviceType = 'web', pollInt
288
290
  }
289
291
  })(), [
290
292
  form,
293
+ message,
291
294
  playgroundSDK,
292
295
  refreshServerState,
293
296
  refreshSessionSetup,
@@ -155,20 +155,32 @@ function DeviceInteractionLayer({ enabled, deviceSize, onTap, onSwipe, scrollEna
155
155
  const activePointer = (0, external_react_namespaceObject.useRef)(null);
156
156
  const composingRef = (0, external_react_namespaceObject.useRef)(false);
157
157
  const keyboardArmedRef = (0, external_react_namespaceObject.useRef)(false);
158
+ const keyboardFocusRequestIdRef = (0, external_react_namespaceObject.useRef)(0);
158
159
  const lastKeyboardPointRef = (0, external_react_namespaceObject.useRef)(null);
159
160
  const pendingWheelRef = (0, external_react_namespaceObject.useRef)(null);
160
161
  const wheelTimerRef = (0, external_react_namespaceObject.useRef)(null);
161
162
  const focusKeyboardSink = (0, external_react_namespaceObject.useCallback)(()=>{
162
163
  if (keyboardEnabled) {
163
- var _keyboardSinkRef_current;
164
164
  keyboardArmedRef.current = true;
165
- null == (_keyboardSinkRef_current = keyboardSinkRef.current) || _keyboardSinkRef_current.focus({
165
+ const sink = keyboardSinkRef.current;
166
+ if (sink && document.activeElement !== sink) sink.focus({
166
167
  preventScroll: true
167
168
  });
168
169
  }
169
170
  }, [
170
171
  keyboardEnabled
171
172
  ]);
173
+ const refocusKeyboardSinkAfterTap = (0, external_react_namespaceObject.useCallback)((focusRequestId, interaction)=>{
174
+ if (!keyboardEnabled) return;
175
+ Promise.resolve(interaction).catch(()=>void 0).then(()=>{
176
+ window.setTimeout(()=>{
177
+ if (keyboardArmedRef.current && keyboardFocusRequestIdRef.current === focusRequestId) focusKeyboardSink();
178
+ }, 0);
179
+ });
180
+ }, [
181
+ focusKeyboardSink,
182
+ keyboardEnabled
183
+ ]);
172
184
  const positionKeyboardSink = (0, external_react_namespaceObject.useCallback)((clientX, clientY)=>{
173
185
  const overlay = overlayRef.current;
174
186
  const sink = keyboardSinkRef.current;
@@ -202,6 +214,8 @@ function DeviceInteractionLayer({ enabled, deviceSize, onTap, onSwipe, scrollEna
202
214
  return;
203
215
  }
204
216
  positionKeyboardSink(event.clientX, event.clientY);
217
+ const keyboardFocusRequestId = keyboardFocusRequestIdRef.current + 1;
218
+ keyboardFocusRequestIdRef.current = keyboardFocusRequestId;
205
219
  focusKeyboardSink();
206
220
  try {
207
221
  overlayRef.current.setPointerCapture(event.pointerId);
@@ -210,7 +224,8 @@ function DeviceInteractionLayer({ enabled, deviceSize, onTap, onSwipe, scrollEna
210
224
  startX: event.clientX,
211
225
  startY: event.clientY,
212
226
  startTime: performance.now(),
213
- contentRect
227
+ contentRect,
228
+ keyboardFocusRequestId
214
229
  };
215
230
  event.preventDefault();
216
231
  }, [
@@ -237,12 +252,13 @@ function DeviceInteractionLayer({ enabled, deviceSize, onTap, onSwipe, scrollEna
237
252
  const endPoint = projectToDevice(event.clientX, event.clientY, active.contentRect);
238
253
  if (!startPoint || !endPoint) return;
239
254
  lastKeyboardPointRef.current = endPoint;
240
- if (distance <= tapMaxDistance && duration <= tapMaxDurationMs) null == onTap || onTap(startPoint);
255
+ if (distance <= tapMaxDistance && duration <= tapMaxDurationMs) refocusKeyboardSinkAfterTap(active.keyboardFocusRequestId, null == onTap ? void 0 : onTap(startPoint));
241
256
  else null == onSwipe || onSwipe(startPoint, endPoint, Math.round(duration));
242
257
  }, [
243
258
  onTap,
244
259
  onSwipe,
245
260
  projectToDevice,
261
+ refocusKeyboardSinkAfterTap,
246
262
  tapMaxDistance,
247
263
  tapMaxDurationMs
248
264
  ]);
@@ -464,8 +480,11 @@ function DeviceInteractionLayer({ enabled, deviceSize, onTap, onSwipe, scrollEna
464
480
  top: 0,
465
481
  width: 32,
466
482
  height: 24,
467
- opacity: 0.01,
483
+ opacity: 0,
468
484
  pointerEvents: 'none',
485
+ appearance: 'none',
486
+ boxShadow: 'none',
487
+ overflow: 'hidden',
469
488
  resize: 'none',
470
489
  border: 0,
471
490
  padding: 0,
@@ -32,7 +32,10 @@ const external_antd_namespaceObject = require("antd");
32
32
  function PlaygroundThemeProvider({ children }) {
33
33
  return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_antd_namespaceObject.ConfigProvider, {
34
34
  theme: (0, visualizer_namespaceObject.globalThemeConfig)(),
35
- children: children
35
+ children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_antd_namespaceObject.App, {
36
+ component: false,
37
+ children: children
38
+ })
36
39
  });
37
40
  }
38
41
  exports.PlaygroundThemeProvider = __webpack_exports__.PlaygroundThemeProvider;
@@ -62,6 +62,29 @@ function _async_to_generator(fn) {
62
62
  });
63
63
  };
64
64
  }
65
+ function _define_property(obj, key, value) {
66
+ if (key in obj) Object.defineProperty(obj, key, {
67
+ value: value,
68
+ enumerable: true,
69
+ configurable: true,
70
+ writable: true
71
+ });
72
+ else obj[key] = value;
73
+ return obj;
74
+ }
75
+ function _object_spread(target) {
76
+ for(var i = 1; i < arguments.length; i++){
77
+ var source = null != arguments[i] ? arguments[i] : {};
78
+ var ownKeys = Object.keys(source);
79
+ if ("function" == typeof Object.getOwnPropertySymbols) ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
80
+ return Object.getOwnPropertyDescriptor(source, sym).enumerable;
81
+ }));
82
+ ownKeys.forEach(function(key) {
83
+ _define_property(target, key, source[key]);
84
+ });
85
+ }
86
+ return target;
87
+ }
65
88
  function isNonLocalhostHttp() {
66
89
  try {
67
90
  const { protocol, hostname } = window.location;
@@ -72,6 +95,7 @@ function isNonLocalhostHttp() {
72
95
  }
73
96
  }
74
97
  function PreviewRenderer({ connectingOverlay, onDeviceSizeChange, onScrcpyStatusChange, renderErrorOverlay, scrcpyViewportStyle, screenshotViewerMode, playgroundSDK, runtimeInfo, serverUrl, serverOnline, isUserOperating }) {
98
+ const { message } = external_antd_namespaceObject.App.useApp();
75
99
  const previewConnection = (0, external_runtime_info_js_namespaceObject.resolvePreviewConnectionInfo)(runtimeInfo, serverUrl);
76
100
  const [deviceSize, setDeviceSize] = (0, external_react_namespaceObject.useState)(null);
77
101
  const [streamSize, setStreamSize] = (0, external_react_namespaceObject.useState)(null);
@@ -79,6 +103,7 @@ function PreviewRenderer({ connectingOverlay, onDeviceSizeChange, onScrcpyStatus
79
103
  const [scrcpyStatus, setScrcpyStatus] = (0, external_react_namespaceObject.useState)('connecting');
80
104
  const manualControlQueueRef = (0, external_react_namespaceObject.useRef)(Promise.resolve());
81
105
  const pendingTextInputRef = (0, external_react_namespaceObject.useRef)('');
106
+ const pendingTextInputPointRef = (0, external_react_namespaceObject.useRef)(null);
82
107
  const textInputTimerRef = (0, external_react_namespaceObject.useRef)(null);
83
108
  const textInputFlushPromiseRef = (0, external_react_namespaceObject.useRef)(null);
84
109
  const previewContentRef = (0, external_react_namespaceObject.useRef)(null);
@@ -93,7 +118,9 @@ function PreviewRenderer({ connectingOverlay, onDeviceSizeChange, onScrcpyStatus
93
118
  const nextTask = manualControlQueueRef.current.then(task, task);
94
119
  manualControlQueueRef.current = nextTask.catch(()=>void 0);
95
120
  return nextTask;
96
- }, []);
121
+ }, [
122
+ message
123
+ ]);
97
124
  (0, external_react_namespaceObject.useEffect)(()=>{
98
125
  if (!serverOnline) {
99
126
  setDeviceSize(null);
@@ -149,7 +176,7 @@ function PreviewRenderer({ connectingOverlay, onDeviceSizeChange, onScrcpyStatus
149
176
  const showInsecureContextHint = 'scrcpy' === previewConnection.type && !scrcpy_decoder_webcodecs_namespaceObject.WebCodecsVideoDecoder.isSupported && isNonLocalhostHttp();
150
177
  const previewInteractionEnabled = 'none' !== previewConnection.type && (!scrcpyAvailable || 'connected' === scrcpyStatus);
151
178
  const showManualControlError = (0, external_react_namespaceObject.useCallback)((fallback, error)=>{
152
- external_antd_namespaceObject.message.open({
179
+ message.open({
153
180
  type: 'error',
154
181
  content: error || fallback,
155
182
  key: 'manual-control-error'
@@ -164,19 +191,24 @@ function PreviewRenderer({ connectingOverlay, onDeviceSizeChange, onScrcpyStatus
164
191
  const flushPendingTextInput = (0, external_react_namespaceObject.useCallback)(()=>{
165
192
  clearTextInputTimer();
166
193
  const text = pendingTextInputRef.current;
194
+ const point = pendingTextInputPointRef.current;
167
195
  if (!text) {
168
196
  var _textInputFlushPromiseRef_current;
169
197
  return null != (_textInputFlushPromiseRef_current = textInputFlushPromiseRef.current) ? _textInputFlushPromiseRef_current : Promise.resolve();
170
198
  }
171
199
  pendingTextInputRef.current = '';
200
+ pendingTextInputPointRef.current = null;
172
201
  var _textInputFlushPromiseRef_current1;
173
202
  const previousFlush = null != (_textInputFlushPromiseRef_current1 = textInputFlushPromiseRef.current) ? _textInputFlushPromiseRef_current1 : Promise.resolve();
174
203
  const flushPromise = previousFlush.catch(()=>void 0).then(()=>_async_to_generator(function*() {
175
- const res = yield enqueueManualControl(()=>playgroundSDK.interact({
204
+ const res = yield enqueueManualControl(()=>playgroundSDK.interact(_object_spread({
176
205
  actionType: 'Input',
177
206
  value: text,
178
207
  mode: 'typeOnly'
179
- }));
208
+ }, point ? {
209
+ x: point.x,
210
+ y: point.y
211
+ } : {})));
180
212
  if (!res.ok) showManualControlError('Input failed', res.error);
181
213
  })());
182
214
  const trackedFlushPromise = flushPromise.finally(()=>{
@@ -215,9 +247,10 @@ function PreviewRenderer({ connectingOverlay, onDeviceSizeChange, onScrcpyStatus
215
247
  playgroundSDK,
216
248
  showManualControlError
217
249
  ]);
218
- const handleTextInput = (0, external_react_namespaceObject.useCallback)((text)=>{
250
+ const handleTextInput = (0, external_react_namespaceObject.useCallback)((text, point)=>{
219
251
  if (!text) return;
220
252
  pendingTextInputRef.current += text;
253
+ if (point) pendingTextInputPointRef.current = point;
221
254
  clearTextInputTimer();
222
255
  textInputTimerRef.current = setTimeout(()=>{
223
256
  flushPendingTextInput();
@@ -230,6 +263,7 @@ function PreviewRenderer({ connectingOverlay, onDeviceSizeChange, onScrcpyStatus
230
263
  if (serverOnline && previewInteractionEnabled && manualKeyboardEnabled) return;
231
264
  clearTextInputTimer();
232
265
  pendingTextInputRef.current = '';
266
+ pendingTextInputPointRef.current = null;
233
267
  }, [
234
268
  clearTextInputTimer,
235
269
  manualKeyboardEnabled,
@@ -239,6 +273,7 @@ function PreviewRenderer({ connectingOverlay, onDeviceSizeChange, onScrcpyStatus
239
273
  (0, external_react_namespaceObject.useEffect)(()=>()=>{
240
274
  if (serverOnline && previewInteractionEnabled && manualKeyboardEnabled && pendingTextInputRef.current) return void flushPendingTextInput();
241
275
  clearTextInputTimer();
276
+ pendingTextInputPointRef.current = null;
242
277
  }, [
243
278
  clearTextInputTimer,
244
279
  flushPendingTextInput,
@@ -109,6 +109,7 @@ function getPlatformSelectorFieldKey(setup) {
109
109
  return null == setup ? void 0 : null == (_setup_platformSelector = setup.platformSelector) ? void 0 : _setup_platformSelector.fieldKey;
110
110
  }
111
111
  function usePlaygroundController({ serverUrl, defaultDeviceType = 'web', pollIntervalMs = 5000, countdownSeconds = 3, initialFormValues, onCountdownFinish }) {
112
+ const { message } = external_antd_namespaceObject.App.useApp();
112
113
  const [form] = external_antd_namespaceObject.Form.useForm();
113
114
  const initialFormValuesRef = (0, external_react_namespaceObject.useRef)(initialFormValues);
114
115
  (0, external_react_namespaceObject.useLayoutEffect)(()=>{
@@ -277,7 +278,7 @@ function usePlaygroundController({ serverUrl, defaultDeviceType = 'web', pollInt
277
278
  const values = null != input ? input : yield form.validateFields();
278
279
  yield playgroundSDK.createSession(values);
279
280
  if ((0, external_auto_create_js_namespaceObject.shouldResetAutoCreateBlock)(options)) autoCreateBlockedSignatureRef.current = null;
280
- if (!(null == options ? void 0 : options.silent)) external_antd_namespaceObject.message.success('Agent created');
281
+ if (!(null == options ? void 0 : options.silent)) message.success('Agent created');
281
282
  yield refreshServerState();
282
283
  return true;
283
284
  } catch (error) {
@@ -294,6 +295,7 @@ function usePlaygroundController({ serverUrl, defaultDeviceType = 'web', pollInt
294
295
  })(), [
295
296
  applyAiConfig,
296
297
  form,
298
+ message,
297
299
  playgroundSDK,
298
300
  refreshServerState
299
301
  ]);
@@ -303,7 +305,7 @@ function usePlaygroundController({ serverUrl, defaultDeviceType = 'web', pollInt
303
305
  sessionMutatingRef.current = true;
304
306
  setSessionMutating(true);
305
307
  yield playgroundSDK.destroySession();
306
- external_antd_namespaceObject.message.success('Session disconnected');
308
+ message.success('Session disconnected');
307
309
  yield refreshServerState();
308
310
  yield refreshSessionSetup();
309
311
  } catch (error) {
@@ -316,6 +318,7 @@ function usePlaygroundController({ serverUrl, defaultDeviceType = 'web', pollInt
316
318
  }
317
319
  })(), [
318
320
  form,
321
+ message,
319
322
  playgroundSDK,
320
323
  refreshServerState,
321
324
  refreshSessionSetup,
@@ -9,7 +9,7 @@ export interface DeviceInteractionLayerProps {
9
9
  onTap?: (point: {
10
10
  x: number;
11
11
  y: number;
12
- }) => void;
12
+ }) => void | Promise<void>;
13
13
  onSwipe?: (start: {
14
14
  x: number;
15
15
  y: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@midscene/playground-app",
3
- "version": "1.9.6",
3
+ "version": "1.9.7",
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.9.6",
32
- "@midscene/visualizer": "1.9.6",
33
- "@midscene/shared": "1.9.6"
31
+ "@midscene/shared": "1.9.7",
32
+ "@midscene/visualizer": "1.9.7",
33
+ "@midscene/playground": "1.9.7"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@rsbuild/plugin-less": "^1.5.0",