@midscene/ios 0.30.3-beta-20251011080128.0 → 0.30.3-beta-20251014030035.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/es/bin.mjs CHANGED
@@ -31,7 +31,7 @@ import { Agent } from "@midscene/core/agent";
31
31
  import { getDebug } from "@midscene/shared/logger";
32
32
  import node_assert from "node:assert";
33
33
  import { getMidsceneLocationSchema, z } from "@midscene/core";
34
- import { defineAction, defineActionDoubleClick, defineActionDragAndDrop, defineActionKeyboardPress, defineActionScroll, defineActionTap } from "@midscene/core/device";
34
+ import { defineAction, defineActionClearInput, defineActionDoubleClick, defineActionDragAndDrop, defineActionKeyboardPress, defineActionScroll, defineActionTap } from "@midscene/core/device";
35
35
  import { createImgBase64ByFormat } from "@midscene/shared/img";
36
36
  import { WDAManager, WebDriverClient } from "@midscene/webdriver";
37
37
  import "node:os";
@@ -12033,6 +12033,16 @@ class IOSWebDriverClient extends WebDriverClient {
12033
12033
  });
12034
12034
  debugIOS(`Double tapped at coordinates (${x}, ${y})`);
12035
12035
  }
12036
+ async tripleTap(x, y) {
12037
+ this.ensureSession();
12038
+ await this.makeRequest('POST', `/session/${this.sessionId}/wda/tapWithNumberOfTaps`, {
12039
+ x,
12040
+ y,
12041
+ numberOfTaps: 3,
12042
+ numberOfTouches: 1
12043
+ });
12044
+ debugIOS(`Triple tapped at coordinates (${x}, ${y})`);
12045
+ }
12036
12046
  async getScreenScale() {
12037
12047
  var _screenResponse_value;
12038
12048
  const screenResponse = await this.makeRequest('GET', '/wda/screen');
@@ -12079,6 +12089,7 @@ function device_define_property(obj, key, value) {
12079
12089
  return obj;
12080
12090
  }
12081
12091
  const debugDevice = getDebug('ios:device');
12092
+ const BackspaceChar = '\u0008';
12082
12093
  class device_IOSDevice {
12083
12094
  actionSpace() {
12084
12095
  const defaultActions = [
@@ -12097,17 +12108,23 @@ class device_IOSDevice {
12097
12108
  description: 'Input text into the input field',
12098
12109
  interfaceAlias: 'aiInput',
12099
12110
  paramSchema: z.object({
12100
- value: z.string().describe('The final that should be filled in the input box. No matter what modifications are required, just provide the final value to replace the existing input value. Giving a blank string means clear the input field.'),
12111
+ value: z.string().describe('The text to input. Provide the final content for replace/append modes, or an empty string when using clear mode to remove existing text.'),
12101
12112
  autoDismissKeyboard: z.boolean().optional().describe('If true, the keyboard will be dismissed after the input is completed. Do not set it unless the user asks you to do so.'),
12113
+ mode: z["enum"]([
12114
+ 'replace',
12115
+ 'clear',
12116
+ 'append'
12117
+ ]).default('replace').optional().describe('Input mode: "replace" (default) - clear the field and input the value; "append" - append the value to existing content; "clear" - clear the field without inputting new text.'),
12102
12118
  locate: getMidsceneLocationSchema().describe('The input field to be filled').optional()
12103
12119
  }),
12104
12120
  call: async (param)=>{
12105
12121
  var _this_options;
12106
12122
  const element = param.locate;
12107
12123
  if (element) {
12108
- await this.clearInput(element);
12109
- if (!param || !param.value) return;
12124
+ if ('append' !== param.mode) await this.clearInput(element);
12110
12125
  }
12126
+ if ('clear' === param.mode) return;
12127
+ if (!param || !param.value) return;
12111
12128
  const autoDismissKeyboard = param.autoDismissKeyboard ?? (null == (_this_options = this.options) ? void 0 : _this_options.autoDismissKeyboard);
12112
12129
  await this.typeText(param.value, {
12113
12130
  autoDismissKeyboard
@@ -12175,6 +12192,11 @@ class device_IOSDevice {
12175
12192
  const [x, y] = element.center;
12176
12193
  await this.longPress(x, y, null == param ? void 0 : param.duration);
12177
12194
  }
12195
+ }),
12196
+ defineActionClearInput(async (param)=>{
12197
+ const element = param.locate;
12198
+ node_assert(element, 'Element not found, cannot clear input');
12199
+ await this.clearInput(element);
12178
12200
  })
12179
12201
  ];
12180
12202
  const customActions = this.customActions || [];
@@ -12278,13 +12300,13 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
12278
12300
  await this.tap(element.center[0], element.center[1]);
12279
12301
  await sleep(100);
12280
12302
  try {
12281
- await this.longPress(element.center[0], element.center[1], 800);
12303
+ await this.tripleTap(element.center[0], element.center[1]);
12282
12304
  await sleep(200);
12283
- await this.wdaBackend.typeText('');
12305
+ await this.wdaBackend.typeText(BackspaceChar);
12284
12306
  } catch (error2) {
12285
12307
  debugDevice(`Method 1 failed, trying method 2: ${error2}`);
12286
12308
  try {
12287
- const backspaces = Array(30).fill('\u0008').join('');
12309
+ const backspaces = Array(100).fill(BackspaceChar).join('');
12288
12310
  await this.wdaBackend.typeText(backspaces);
12289
12311
  } catch (error3) {
12290
12312
  debugDevice(`All clear methods failed: ${error3}`);
@@ -12304,6 +12326,9 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
12304
12326
  async doubleTap(x, y) {
12305
12327
  await this.wdaBackend.doubleTap(x, y);
12306
12328
  }
12329
+ async tripleTap(x, y) {
12330
+ await this.wdaBackend.tripleTap(x, y);
12331
+ }
12307
12332
  async longPress(x, y, duration = 1000) {
12308
12333
  await this.wdaBackend.longPress(x, y, duration);
12309
12334
  }
package/dist/es/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import node_assert from "node:assert";
2
2
  import { getMidsceneLocationSchema, z } from "@midscene/core";
3
- import { defineAction, defineActionDoubleClick, defineActionDragAndDrop, defineActionKeyboardPress, defineActionScroll, defineActionTap } from "@midscene/core/device";
3
+ import { defineAction, defineActionClearInput, defineActionDoubleClick, defineActionDragAndDrop, defineActionKeyboardPress, defineActionScroll, defineActionTap } from "@midscene/core/device";
4
4
  import { sleep } from "@midscene/core/utils";
5
5
  import { DEFAULT_WDA_PORT } from "@midscene/shared/constants";
6
6
  import { createImgBase64ByFormat } from "@midscene/shared/img";
@@ -227,6 +227,16 @@ class IOSWebDriverClient extends WebDriverClient {
227
227
  });
228
228
  debugIOS(`Double tapped at coordinates (${x}, ${y})`);
229
229
  }
230
+ async tripleTap(x, y) {
231
+ this.ensureSession();
232
+ await this.makeRequest('POST', `/session/${this.sessionId}/wda/tapWithNumberOfTaps`, {
233
+ x,
234
+ y,
235
+ numberOfTaps: 3,
236
+ numberOfTouches: 1
237
+ });
238
+ debugIOS(`Triple tapped at coordinates (${x}, ${y})`);
239
+ }
230
240
  async getScreenScale() {
231
241
  var _screenResponse_value;
232
242
  const screenResponse = await this.makeRequest('GET', '/wda/screen');
@@ -273,6 +283,7 @@ function _define_property(obj, key, value) {
273
283
  return obj;
274
284
  }
275
285
  const debugDevice = getDebug('ios:device');
286
+ const BackspaceChar = '\u0008';
276
287
  class IOSDevice {
277
288
  actionSpace() {
278
289
  const defaultActions = [
@@ -291,17 +302,23 @@ class IOSDevice {
291
302
  description: 'Input text into the input field',
292
303
  interfaceAlias: 'aiInput',
293
304
  paramSchema: z.object({
294
- value: z.string().describe('The final that should be filled in the input box. No matter what modifications are required, just provide the final value to replace the existing input value. Giving a blank string means clear the input field.'),
305
+ value: z.string().describe('The text to input. Provide the final content for replace/append modes, or an empty string when using clear mode to remove existing text.'),
295
306
  autoDismissKeyboard: z.boolean().optional().describe('If true, the keyboard will be dismissed after the input is completed. Do not set it unless the user asks you to do so.'),
307
+ mode: z["enum"]([
308
+ 'replace',
309
+ 'clear',
310
+ 'append'
311
+ ]).default('replace').optional().describe('Input mode: "replace" (default) - clear the field and input the value; "append" - append the value to existing content; "clear" - clear the field without inputting new text.'),
296
312
  locate: getMidsceneLocationSchema().describe('The input field to be filled').optional()
297
313
  }),
298
314
  call: async (param)=>{
299
315
  var _this_options;
300
316
  const element = param.locate;
301
317
  if (element) {
302
- await this.clearInput(element);
303
- if (!param || !param.value) return;
318
+ if ('append' !== param.mode) await this.clearInput(element);
304
319
  }
320
+ if ('clear' === param.mode) return;
321
+ if (!param || !param.value) return;
305
322
  const autoDismissKeyboard = param.autoDismissKeyboard ?? (null == (_this_options = this.options) ? void 0 : _this_options.autoDismissKeyboard);
306
323
  await this.typeText(param.value, {
307
324
  autoDismissKeyboard
@@ -369,6 +386,11 @@ class IOSDevice {
369
386
  const [x, y] = element.center;
370
387
  await this.longPress(x, y, null == param ? void 0 : param.duration);
371
388
  }
389
+ }),
390
+ defineActionClearInput(async (param)=>{
391
+ const element = param.locate;
392
+ node_assert(element, 'Element not found, cannot clear input');
393
+ await this.clearInput(element);
372
394
  })
373
395
  ];
374
396
  const customActions = this.customActions || [];
@@ -472,13 +494,13 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
472
494
  await this.tap(element.center[0], element.center[1]);
473
495
  await sleep(100);
474
496
  try {
475
- await this.longPress(element.center[0], element.center[1], 800);
497
+ await this.tripleTap(element.center[0], element.center[1]);
476
498
  await sleep(200);
477
- await this.wdaBackend.typeText('');
499
+ await this.wdaBackend.typeText(BackspaceChar);
478
500
  } catch (error2) {
479
501
  debugDevice(`Method 1 failed, trying method 2: ${error2}`);
480
502
  try {
481
- const backspaces = Array(30).fill('\u0008').join('');
503
+ const backspaces = Array(100).fill(BackspaceChar).join('');
482
504
  await this.wdaBackend.typeText(backspaces);
483
505
  } catch (error3) {
484
506
  debugDevice(`All clear methods failed: ${error3}`);
@@ -498,6 +520,9 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
498
520
  async doubleTap(x, y) {
499
521
  await this.wdaBackend.doubleTap(x, y);
500
522
  }
523
+ async tripleTap(x, y) {
524
+ await this.wdaBackend.tripleTap(x, y);
525
+ }
501
526
  async longPress(x, y, duration = 1000) {
502
527
  await this.wdaBackend.longPress(x, y, duration);
503
528
  }
package/dist/lib/bin.js CHANGED
@@ -12171,6 +12171,16 @@ var __webpack_exports__ = {};
12171
12171
  });
12172
12172
  debugIOS(`Double tapped at coordinates (${x}, ${y})`);
12173
12173
  }
12174
+ async tripleTap(x, y) {
12175
+ this.ensureSession();
12176
+ await this.makeRequest('POST', `/session/${this.sessionId}/wda/tapWithNumberOfTaps`, {
12177
+ x,
12178
+ y,
12179
+ numberOfTaps: 3,
12180
+ numberOfTouches: 1
12181
+ });
12182
+ debugIOS(`Triple tapped at coordinates (${x}, ${y})`);
12183
+ }
12174
12184
  async getScreenScale() {
12175
12185
  var _screenResponse_value;
12176
12186
  const screenResponse = await this.makeRequest('GET', '/wda/screen');
@@ -12217,6 +12227,7 @@ var __webpack_exports__ = {};
12217
12227
  return obj;
12218
12228
  }
12219
12229
  const debugDevice = (0, logger_namespaceObject.getDebug)('ios:device');
12230
+ const BackspaceChar = '\u0008';
12220
12231
  class device_IOSDevice {
12221
12232
  actionSpace() {
12222
12233
  const defaultActions = [
@@ -12235,17 +12246,23 @@ var __webpack_exports__ = {};
12235
12246
  description: 'Input text into the input field',
12236
12247
  interfaceAlias: 'aiInput',
12237
12248
  paramSchema: core_namespaceObject.z.object({
12238
- value: core_namespaceObject.z.string().describe('The final that should be filled in the input box. No matter what modifications are required, just provide the final value to replace the existing input value. Giving a blank string means clear the input field.'),
12249
+ value: core_namespaceObject.z.string().describe('The text to input. Provide the final content for replace/append modes, or an empty string when using clear mode to remove existing text.'),
12239
12250
  autoDismissKeyboard: core_namespaceObject.z.boolean().optional().describe('If true, the keyboard will be dismissed after the input is completed. Do not set it unless the user asks you to do so.'),
12251
+ mode: core_namespaceObject.z["enum"]([
12252
+ 'replace',
12253
+ 'clear',
12254
+ 'append'
12255
+ ]).default('replace').optional().describe('Input mode: "replace" (default) - clear the field and input the value; "append" - append the value to existing content; "clear" - clear the field without inputting new text.'),
12240
12256
  locate: (0, core_namespaceObject.getMidsceneLocationSchema)().describe('The input field to be filled').optional()
12241
12257
  }),
12242
12258
  call: async (param)=>{
12243
12259
  var _this_options;
12244
12260
  const element = param.locate;
12245
12261
  if (element) {
12246
- await this.clearInput(element);
12247
- if (!param || !param.value) return;
12262
+ if ('append' !== param.mode) await this.clearInput(element);
12248
12263
  }
12264
+ if ('clear' === param.mode) return;
12265
+ if (!param || !param.value) return;
12249
12266
  const autoDismissKeyboard = param.autoDismissKeyboard ?? (null == (_this_options = this.options) ? void 0 : _this_options.autoDismissKeyboard);
12250
12267
  await this.typeText(param.value, {
12251
12268
  autoDismissKeyboard
@@ -12313,6 +12330,11 @@ var __webpack_exports__ = {};
12313
12330
  const [x, y] = element.center;
12314
12331
  await this.longPress(x, y, null == param ? void 0 : param.duration);
12315
12332
  }
12333
+ }),
12334
+ (0, device_namespaceObject.defineActionClearInput)(async (param)=>{
12335
+ const element = param.locate;
12336
+ external_node_assert_default()(element, 'Element not found, cannot clear input');
12337
+ await this.clearInput(element);
12316
12338
  })
12317
12339
  ];
12318
12340
  const customActions = this.customActions || [];
@@ -12416,13 +12438,13 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
12416
12438
  await this.tap(element.center[0], element.center[1]);
12417
12439
  await (0, utils_namespaceObject.sleep)(100);
12418
12440
  try {
12419
- await this.longPress(element.center[0], element.center[1], 800);
12441
+ await this.tripleTap(element.center[0], element.center[1]);
12420
12442
  await (0, utils_namespaceObject.sleep)(200);
12421
- await this.wdaBackend.typeText('');
12443
+ await this.wdaBackend.typeText(BackspaceChar);
12422
12444
  } catch (error2) {
12423
12445
  debugDevice(`Method 1 failed, trying method 2: ${error2}`);
12424
12446
  try {
12425
- const backspaces = Array(30).fill('\u0008').join('');
12447
+ const backspaces = Array(100).fill(BackspaceChar).join('');
12426
12448
  await this.wdaBackend.typeText(backspaces);
12427
12449
  } catch (error3) {
12428
12450
  debugDevice(`All clear methods failed: ${error3}`);
@@ -12442,6 +12464,9 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
12442
12464
  async doubleTap(x, y) {
12443
12465
  await this.wdaBackend.doubleTap(x, y);
12444
12466
  }
12467
+ async tripleTap(x, y) {
12468
+ await this.wdaBackend.tripleTap(x, y);
12469
+ }
12445
12470
  async longPress(x, y, duration = 1000) {
12446
12471
  await this.wdaBackend.longPress(x, y, duration);
12447
12472
  }
package/dist/lib/index.js CHANGED
@@ -265,6 +265,16 @@ class IOSWebDriverClient extends webdriver_namespaceObject.WebDriverClient {
265
265
  });
266
266
  debugIOS(`Double tapped at coordinates (${x}, ${y})`);
267
267
  }
268
+ async tripleTap(x, y) {
269
+ this.ensureSession();
270
+ await this.makeRequest('POST', `/session/${this.sessionId}/wda/tapWithNumberOfTaps`, {
271
+ x,
272
+ y,
273
+ numberOfTaps: 3,
274
+ numberOfTouches: 1
275
+ });
276
+ debugIOS(`Triple tapped at coordinates (${x}, ${y})`);
277
+ }
268
278
  async getScreenScale() {
269
279
  var _screenResponse_value;
270
280
  const screenResponse = await this.makeRequest('GET', '/wda/screen');
@@ -311,6 +321,7 @@ function _define_property(obj, key, value) {
311
321
  return obj;
312
322
  }
313
323
  const debugDevice = (0, logger_namespaceObject.getDebug)('ios:device');
324
+ const BackspaceChar = '\u0008';
314
325
  class IOSDevice {
315
326
  actionSpace() {
316
327
  const defaultActions = [
@@ -329,17 +340,23 @@ class IOSDevice {
329
340
  description: 'Input text into the input field',
330
341
  interfaceAlias: 'aiInput',
331
342
  paramSchema: core_namespaceObject.z.object({
332
- value: core_namespaceObject.z.string().describe('The final that should be filled in the input box. No matter what modifications are required, just provide the final value to replace the existing input value. Giving a blank string means clear the input field.'),
343
+ value: core_namespaceObject.z.string().describe('The text to input. Provide the final content for replace/append modes, or an empty string when using clear mode to remove existing text.'),
333
344
  autoDismissKeyboard: core_namespaceObject.z.boolean().optional().describe('If true, the keyboard will be dismissed after the input is completed. Do not set it unless the user asks you to do so.'),
345
+ mode: core_namespaceObject.z["enum"]([
346
+ 'replace',
347
+ 'clear',
348
+ 'append'
349
+ ]).default('replace').optional().describe('Input mode: "replace" (default) - clear the field and input the value; "append" - append the value to existing content; "clear" - clear the field without inputting new text.'),
334
350
  locate: (0, core_namespaceObject.getMidsceneLocationSchema)().describe('The input field to be filled').optional()
335
351
  }),
336
352
  call: async (param)=>{
337
353
  var _this_options;
338
354
  const element = param.locate;
339
355
  if (element) {
340
- await this.clearInput(element);
341
- if (!param || !param.value) return;
356
+ if ('append' !== param.mode) await this.clearInput(element);
342
357
  }
358
+ if ('clear' === param.mode) return;
359
+ if (!param || !param.value) return;
343
360
  const autoDismissKeyboard = param.autoDismissKeyboard ?? (null == (_this_options = this.options) ? void 0 : _this_options.autoDismissKeyboard);
344
361
  await this.typeText(param.value, {
345
362
  autoDismissKeyboard
@@ -407,6 +424,11 @@ class IOSDevice {
407
424
  const [x, y] = element.center;
408
425
  await this.longPress(x, y, null == param ? void 0 : param.duration);
409
426
  }
427
+ }),
428
+ (0, device_namespaceObject.defineActionClearInput)(async (param)=>{
429
+ const element = param.locate;
430
+ external_node_assert_default()(element, 'Element not found, cannot clear input');
431
+ await this.clearInput(element);
410
432
  })
411
433
  ];
412
434
  const customActions = this.customActions || [];
@@ -510,13 +532,13 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
510
532
  await this.tap(element.center[0], element.center[1]);
511
533
  await (0, utils_namespaceObject.sleep)(100);
512
534
  try {
513
- await this.longPress(element.center[0], element.center[1], 800);
535
+ await this.tripleTap(element.center[0], element.center[1]);
514
536
  await (0, utils_namespaceObject.sleep)(200);
515
- await this.wdaBackend.typeText('');
537
+ await this.wdaBackend.typeText(BackspaceChar);
516
538
  } catch (error2) {
517
539
  debugDevice(`Method 1 failed, trying method 2: ${error2}`);
518
540
  try {
519
- const backspaces = Array(30).fill('\u0008').join('');
541
+ const backspaces = Array(100).fill(BackspaceChar).join('');
520
542
  await this.wdaBackend.typeText(backspaces);
521
543
  } catch (error3) {
522
544
  debugDevice(`All clear methods failed: ${error3}`);
@@ -536,6 +558,9 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
536
558
  async doubleTap(x, y) {
537
559
  await this.wdaBackend.doubleTap(x, y);
538
560
  }
561
+ async tripleTap(x, y) {
562
+ await this.wdaBackend.tripleTap(x, y);
563
+ }
539
564
  async longPress(x, y, duration = 1000) {
540
565
  await this.wdaBackend.longPress(x, y, duration);
541
566
  }
@@ -59,6 +59,7 @@ export declare class IOSDevice implements AbstractInterface {
59
59
  tap(x: number, y: number): Promise<void>;
60
60
  mouseClick(x: number, y: number): Promise<void>;
61
61
  doubleTap(x: number, y: number): Promise<void>;
62
+ tripleTap(x: number, y: number): Promise<void>;
62
63
  longPress(x: number, y: number, duration?: number): Promise<void>;
63
64
  swipe(fromX: number, fromY: number, toX: number, toY: number, duration?: number): Promise<void>;
64
65
  typeText(text: string, options?: IOSDeviceInputOpt): Promise<void>;
@@ -120,6 +121,7 @@ export declare class IOSWebDriverClient extends WebDriverClient {
120
121
  swipe(fromX: number, fromY: number, toX: number, toY: number, duration?: number): Promise<void>;
121
122
  longPress(x: number, y: number, duration?: number): Promise<void>;
122
123
  doubleTap(x: number, y: number): Promise<void>;
124
+ tripleTap(x: number, y: number): Promise<void>;
123
125
  getScreenScale(): Promise<number | null>;
124
126
  createSession(capabilities?: any): Promise<any>;
125
127
  private setupIOSSession;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@midscene/ios",
3
- "version": "0.30.3-beta-20251011080128.0",
3
+ "version": "0.30.3-beta-20251014030035.0",
4
4
  "description": "iOS automation library for Midscene",
5
5
  "keywords": [
6
6
  "iOS UI automation",
@@ -38,9 +38,9 @@
38
38
  "dependencies": {
39
39
  "@inquirer/prompts": "^7.8.6",
40
40
  "open": "10.1.0",
41
- "@midscene/core": "0.30.3-beta-20251011080128.0",
42
- "@midscene/shared": "0.30.3-beta-20251011080128.0",
43
- "@midscene/webdriver": "0.30.3-beta-20251011080128.0"
41
+ "@midscene/core": "0.30.3-beta-20251014030035.0",
42
+ "@midscene/shared": "0.30.3-beta-20251014030035.0",
43
+ "@midscene/webdriver": "0.30.3-beta-20251014030035.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@rslib/core": "^0.11.2",
@@ -49,7 +49,7 @@
49
49
  "typescript": "^5.8.3",
50
50
  "tsx": "^4.19.2",
51
51
  "vitest": "3.0.5",
52
- "@midscene/playground": "0.30.3-beta-20251011080128.0"
52
+ "@midscene/playground": "0.30.3-beta-20251014030035.0"
53
53
  },
54
54
  "license": "MIT",
55
55
  "scripts": {
package/static/index.html CHANGED
@@ -1 +1 @@
1
- <!doctype html><html><head><link rel="icon" href="/favicon.ico"><title>Midscene Playground</title><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><script defer src="/static/js/lib-react.f566a9ed.js"></script><script defer src="/static/js/657.5a5fe47b.js"></script><script defer src="/static/js/index.8d6e2c5c.js"></script><link href="/static/css/index.44466eb4.css" rel="stylesheet"></head><body><div id="root"></div></body></html>
1
+ <!doctype html><html><head><link rel="icon" href="/favicon.ico"><title>Midscene Playground</title><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><script defer src="/static/js/lib-react.f566a9ed.js"></script><script defer src="/static/js/657.5a5fe47b.js"></script><script defer src="/static/js/index.ab51350a.js"></script><link href="/static/css/index.44466eb4.css" rel="stylesheet"></head><body><div id="root"></div></body></html>