{"version":3,"file":"actions/keyboard.mjs","sources":["webpack://@agent-infra/browser/./src/actions/keyboard.ts"],"sourcesContent":["/*\n * Copyright (c) 2025 Bytedance, Inc. and its affiliates.\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport delay from 'delay';\n\nimport {\n  MAC_SYSTEM_HOTKEY_MAP,\n  KEY_ABBR_TO_STANDARD_MAP,\n  KEY_LOW_TO_STANDARD_MAP,\n} from './key-map';\n\nimport type { KeyInput, Page } from 'puppeteer-core';\nimport type {\n  EnvInfo,\n  DialogMetaInfo,\n  KeyboardOptions,\n  KeyOrHotKeyInput,\n  ActionResult,\n} from '../types';\nimport type { TabDialog } from '../tabs/dialog';\n\nexport class Keyboard {\n  #page: Page;\n  #env: EnvInfo;\n  #dialog: TabDialog;\n\n  constructor(page: Page, dialog: TabDialog, env: EnvInfo) {\n    this.#page = page;\n    this.#dialog = dialog;\n    this.#env = env;\n  }\n\n  /**\n   * Enhance puppeteer's [keyboard.press()](https://pptr.dev/api/puppeteer.keyboard.press) to support combination hotkeys.\n   */\n  async press(\n    key: KeyOrHotKeyInput,\n    options: KeyboardOptions = {},\n  ): Promise<ActionResult> {\n    if (this.#dialog.isOpen) {\n      return this.#buildDialogResponse('press');\n    }\n\n    const formattedHotkey = this.#formatHotkey(key);\n\n    if (this.#env.osName === 'macOS' && this.#env.browserName === 'Chrome') {\n      const success = await this.#macOSCDPHotKey(\n        formattedHotkey,\n        options,\n        true,\n      );\n      if (success) {\n        return { success: true };\n      }\n    }\n\n    // default behavior: press keys one by one\n    for (const key of formattedHotkey) {\n      await this.#page.keyboard.down(key);\n    }\n    await delay(options.delay ?? 0);\n    for (const key of formattedHotkey.reverse()) {\n      await this.#page.keyboard.up(key);\n    }\n\n    return { success: true };\n  }\n\n  /**\n   * Enhance puppeteer's [keyboard.down()](https://pptr.dev/api/puppeteer.keyboard.down) to support combination hotkeys.\n   */\n  async down(\n    key: KeyOrHotKeyInput,\n    options: KeyboardOptions = {},\n  ): Promise<ActionResult> {\n    if (this.#dialog.isOpen) {\n      return this.#buildDialogResponse('down');\n    }\n\n    const formattedHotkey = this.#formatHotkey(key);\n\n    if (this.#env.osName === 'macOS' && this.#env.browserName === 'Chrome') {\n      const success = await this.#macOSCDPHotKey(\n        formattedHotkey,\n        options,\n        false,\n      );\n      if (success) {\n        return { success: true };\n      }\n    }\n\n    for (const key of formattedHotkey) {\n      await this.#page.keyboard.down(key);\n    }\n\n    return { success: true };\n  }\n\n  /**\n   * Enhance puppeteer's [keyboard.up()](https://pptr.dev/api/puppeteer.keyboard.up) to support combination hotkeys.\n   */\n  async up(key: KeyOrHotKeyInput): Promise<ActionResult> {\n    if (this.#dialog.isOpen) {\n      return this.#buildDialogResponse('up');\n    }\n\n    const formattedHotkey = this.#formatHotkey(key);\n\n    for (const key of formattedHotkey.reverse()) {\n      await this.#page.keyboard.up(key);\n    }\n\n    return { success: true };\n  }\n\n  async type(\n    text: string,\n    options: KeyboardOptions = {},\n  ): Promise<ActionResult> {\n    if (this.#dialog.isOpen) {\n      return this.#buildDialogResponse('type');\n    }\n\n    if (text.length < 15 && options.delay) {\n      await this.#page.keyboard.type(text, options);\n    } else {\n      await this.#page.keyboard.sendCharacter(text);\n    }\n\n    return { success: true };\n  }\n\n  /**\n   * Format the hotkey string into an array of KeyInput.\n   *\n   * - example1: 'ctrl+c' -> ['Control', 'C']\n   * - example2: 'control+c' -> ['Control', 'C']\n   * - example3: 'Control+C' -> ['Control', 'C']\n   */\n  #formatHotkey(hotkey: string): KeyInput[] {\n    const lowerCaseHotkey = hotkey.toLowerCase();\n    const keys = lowerCaseHotkey.split(/[\\s+]+/);\n\n    const formattedKeys = keys.map((key) => {\n      if (KEY_ABBR_TO_STANDARD_MAP[key]) {\n        return KEY_ABBR_TO_STANDARD_MAP[key];\n      }\n      // Lowercase key to standard key\n      else if (KEY_LOW_TO_STANDARD_MAP[key]) {\n        return KEY_LOW_TO_STANDARD_MAP[key];\n      }\n      // Unsupported key\n      else {\n        throw new Error('Unsupported key: ' + key);\n      }\n    });\n\n    return formattedKeys;\n  }\n\n  /**\n   * adapt for common macOS system and chrome hotkeys\n   *\n   * see issues: https://github.com/bytedance/UI-TARS-desktop/pull/560\n   *\n   * example: 'Ctrl+C' -> CDP: { key: 'KeyC', commands: 'Copy' }\n   */\n  async #macOSCDPHotKey(\n    keys: KeyInput[],\n    options: Readonly<KeyboardOptions>,\n    isPress: boolean,\n  ): Promise<boolean> {\n    const hotkey = keys\n      .map((key) => {\n        if (key === 'Control' || key === 'Meta') {\n          return 'CorM';\n        }\n        return key;\n      })\n      .join('+');\n\n    const command = MAC_SYSTEM_HOTKEY_MAP.get(hotkey);\n\n    if (command) {\n      await this.#page.keyboard.down(command.key, {\n        commands: [command.commands],\n      });\n      await delay(options.delay ?? 0);\n\n      if (isPress) {\n        await this.#page.keyboard.up(command.key);\n      }\n\n      return true;\n    }\n\n    return false;\n  }\n\n  #buildDialogResponse(type: string): ActionResult {\n    return {\n      success: false,\n      message: `Cannot perform keyboard.${type}() operation because there is a dialog on the current page.`,\n      detail: this.#dialog.meta as DialogMetaInfo,\n    };\n  }\n}\n"],"names":["_page","_env","_dialog","formatHotkey","_macOSCDPHotKey","buildDialogResponse","Keyboard","key","options","formattedHotkey","success","macOSCDPHotKey","delay","text","page","dialog","env","hotkey","lowerCaseHotkey","keys","formattedKeys","KEY_ABBR_TO_STANDARD_MAP","KEY_LOW_TO_STANDARD_MAP","Error","isPress","command","MAC_SYSTEM_HOTKEY_MAP","type"],"mappings":"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"}