diff --git a/plugin/lib/quickcomposer/browser/device.js b/plugin/lib/quickcomposer/browser/device.js new file mode 100644 index 0000000..fa8a650 --- /dev/null +++ b/plugin/lib/quickcomposer/browser/device.js @@ -0,0 +1,301 @@ +const { initCDP, cleanupCDP } = require("./cdp"); +const { searchTarget } = require("./tabs"); + +// 预定义的设备列表 +const devices = { + // iOS 设备 + "iPhone X": { + userAgent: + "Mozilla/5.0 (iPhone; CPU iPhone OS 17_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1", + viewport: { + width: 375, + height: 812, + deviceScaleFactor: 3, + mobile: true, + hasTouch: true, + isLandscape: false, + }, + }, + "iPhone 12 Pro": { + userAgent: + "Mozilla/5.0 (iPhone; CPU iPhone OS 17_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1", + viewport: { + width: 390, + height: 844, + deviceScaleFactor: 3, + mobile: true, + hasTouch: true, + isLandscape: false, + }, + }, + "iPhone 14 Pro Max": { + userAgent: + "Mozilla/5.0 (iPhone; CPU iPhone OS 17_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1", + viewport: { + width: 430, + height: 932, + deviceScaleFactor: 3, + mobile: true, + hasTouch: true, + isLandscape: false, + }, + }, + "iPad Pro": { + userAgent: + "Mozilla/5.0 (iPad; CPU OS 17_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1", + viewport: { + width: 1024, + height: 1366, + deviceScaleFactor: 2, + mobile: true, + hasTouch: true, + isLandscape: false, + }, + }, + "iPad Mini": { + userAgent: + "Mozilla/5.0 (iPad; CPU OS 17_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1", + viewport: { + width: 768, + height: 1024, + deviceScaleFactor: 2, + mobile: true, + hasTouch: true, + isLandscape: false, + }, + }, + + // Android 设备 + "Pixel 5": { + userAgent: + "Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36", + viewport: { + width: 393, + height: 851, + deviceScaleFactor: 2.75, + mobile: true, + hasTouch: true, + isLandscape: false, + }, + }, + "Pixel 7": { + userAgent: + "Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36", + viewport: { + width: 412, + height: 915, + deviceScaleFactor: 2.625, + mobile: true, + hasTouch: true, + isLandscape: false, + }, + }, + "Samsung Galaxy S20 Ultra": { + userAgent: + "Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36", + viewport: { + width: 412, + height: 915, + deviceScaleFactor: 3.5, + mobile: true, + hasTouch: true, + isLandscape: false, + }, + }, + "Samsung Galaxy Tab S7": { + userAgent: + "Mozilla/5.0 (Linux; Android 14; SM-X710) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36", + viewport: { + width: 1600, + height: 2560, + deviceScaleFactor: 2, + mobile: true, + hasTouch: true, + isLandscape: false, + }, + }, + "Xiaomi 12 Pro": { + userAgent: + "Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36", + viewport: { + width: 390, + height: 844, + deviceScaleFactor: 3, + mobile: true, + hasTouch: true, + isLandscape: false, + }, + }, + "HUAWEI Mate30 Pro": { + userAgent: + "Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36", + viewport: { + width: 392, + height: 835, + deviceScaleFactor: 3, + mobile: true, + hasTouch: true, + isLandscape: false, + }, + }, + + // 桌面设备 + Desktop: { + userAgent: + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36", + viewport: { + width: 1920, + height: 1080, + deviceScaleFactor: 1, + mobile: false, + hasTouch: false, + isLandscape: false, + }, + }, + "MacBook Pro 16": { + userAgent: + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36", + viewport: { + width: 1728, + height: 1117, + deviceScaleFactor: 2, + mobile: false, + hasTouch: false, + isLandscape: false, + }, + }, + "4K Display": { + userAgent: + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36", + viewport: { + width: 3840, + height: 2160, + deviceScaleFactor: 2, + mobile: false, + hasTouch: false, + isLandscape: false, + }, + }, +}; + +// 设置设备模拟 +const setDevice = async (tab, deviceName) => { + const target = await searchTarget(tab); + const { Network, Emulation } = await initCDP(target.id); + + try { + const device = devices[deviceName]; + if (!device) { + throw new Error(`未找到设备配置: ${deviceName}`); + } + + // 设置 User Agent + await Network.setUserAgentOverride({ + userAgent: device.userAgent, + }); + + // 设置视口 + await Emulation.setDeviceMetricsOverride({ + ...device.viewport, + screenWidth: device.viewport.width, + screenHeight: device.viewport.height, + }); + + // 设置触摸事件模拟 + if (device.viewport.hasTouch) { + await Emulation.setTouchEmulationEnabled({ + enabled: true, + maxTouchPoints: 5, + }); + } + } finally { + await cleanupCDP(target.id); + } +}; + +// 自定义设备模拟 +const setCustomDevice = async (tab, options) => { + const target = await searchTarget(tab); + const { Network, Emulation } = await initCDP(target.id); + + try { + const { + userAgent, + width = 1920, + height = 1080, + deviceScaleFactor = 1, + mobile = false, + hasTouch = false, + isLandscape = false, + } = options; + + // 设置 User Agent + if (userAgent) { + await Network.setUserAgentOverride({ + userAgent, + }); + } + + // 设置视口 + await Emulation.setDeviceMetricsOverride({ + width, + height, + deviceScaleFactor, + mobile, + isLandscape, + screenWidth: width, + screenHeight: height, + }); + + // 设置触摸事件模拟 + if (hasTouch) { + await Emulation.setTouchEmulationEnabled({ + enabled: true, + maxTouchPoints: 5, + }); + } + } finally { + await cleanupCDP(target.id); + } +}; + +// 清除设备模拟 +const clearDeviceEmulation = async (tab) => { + const target = await searchTarget(tab); + const { Network, Emulation } = await initCDP(target.id); + + try { + // 先禁用触摸事件模拟 + await Emulation.setTouchEmulationEnabled({ + enabled: false, + }); + + // 清除 User Agent 覆盖 + await Network.setUserAgentOverride({ + userAgent: "", + }); + + // 重置设备指标 + await Emulation.setDeviceMetricsOverride({ + width: 0, + height: 0, + deviceScaleFactor: 0, + mobile: false, + screenWidth: 0, + screenHeight: 0, + }); + + // 清除设备指标覆盖 + await Emulation.clearDeviceMetricsOverride(); + } catch (error) { + console.error("清除设备模拟失败:", error); + } finally { + await cleanupCDP(target.id); + } +}; + +module.exports = { + setDevice, + setCustomDevice, + clearDeviceEmulation, +}; diff --git a/plugin/lib/quickcomposer/browser/index.js b/plugin/lib/quickcomposer/browser/index.js index a2c74f0..2cf721d 100644 --- a/plugin/lib/quickcomposer/browser/index.js +++ b/plugin/lib/quickcomposer/browser/index.js @@ -5,6 +5,7 @@ const tabs = require("./tabs"); const url = require("./url"); const cookie = require("./cookie"); const screenshot = require("./screenshot"); +const device = require("./device"); module.exports = { ...url, @@ -14,4 +15,5 @@ module.exports = { ...browserManager, ...cookie, ...screenshot, + ...device, }; diff --git a/src/js/composer/commands/browserCommands.js b/src/js/composer/commands/browserCommands.js index b0d8bdf..6c049c8 100644 --- a/src/js/composer/commands/browserCommands.js +++ b/src/js/composer/commands/browserCommands.js @@ -1,4 +1,5 @@ import { newVarInputVal } from "js/composer/varInputValManager"; +import { deviceName, userAgent } from "js/options/httpOptions"; const tabConfig = { component: "OptionEditor", @@ -6,7 +7,6 @@ const tabConfig = { options: { by: { component: "QSelect", - label: "标签", width: 3, options: [ { label: "当前标签页", value: "active" }, @@ -19,7 +19,7 @@ const tabConfig = { component: "VariableInput", icon: "tab", width: 9, - placeholder: "当前标签页留空,其他支持模糊匹配", + placeholder: "选择当前标签页留空,URL/标题/ID支持模糊匹配", }, }, defaultValue: { @@ -625,5 +625,101 @@ export const browserCommands = { }, ], }, + { + value: "quickcomposer.browser.setDevice", + label: "设备模拟", + icon: "devices", + isAsync: true, + config: [tabConfig], + subCommands: [ + { + value: "quickcomposer.browser.setDevice", + label: "使用预设设备", + icon: "smartphone", + config: [ + { + label: "设备", + component: "QSelect", + icon: "devices", + width: 12, + options: [ + ...deviceName, + // 桌面设备 + { label: "Desktop", value: "Desktop" }, + { label: "MacBook Pro 16", value: "MacBook Pro 16" }, + { label: "4K Display", value: "4K Display" }, + ], + }, + ], + }, + { + value: "quickcomposer.browser.setCustomDevice", + label: "自定义设备", + icon: "build", + config: [ + { + label: "设备配置", + component: "OptionEditor", + icon: "settings", + width: 12, + options: { + width: { + label: "宽度", + component: "NumberInput", + width: 3, + defaultValue: 1920, + min: 0, + }, + height: { + label: "高度", + component: "NumberInput", + width: 3, + defaultValue: 1080, + min: 0, + }, + deviceScaleFactor: { + label: "设备像素比", + component: "NumberInput", + width: 3, + defaultValue: 1, + min: 1, + step: 0.1, + }, + mobile: { + label: "移动设备", + component: "CheckButton", + width: 3, + }, + hasTouch: { + label: "触摸屏", + component: "CheckButton", + width: 3, + }, + isLandscape: { + label: "横屏", + component: "CheckButton", + width: 3, + }, + userAgent: { + label: "User Agent", + component: "VariableInput", + icon: "code", + width: 6, + placeholder: "留空使用默认", + options: { + items: userAgent, + }, + }, + }, + }, + ], + }, + { + value: "quickcomposer.browser.clearDeviceEmulation", + label: "清除设备模拟", + icon: "clear", + }, + ], + }, ], }; diff --git a/src/js/options/httpOptions.js b/src/js/options/httpOptions.js index 6705be6..d9cf1a7 100644 --- a/src/js/options/httpOptions.js +++ b/src/js/options/httpOptions.js @@ -67,15 +67,22 @@ export const commonHeaders = [ ]; export const deviceName = [ - { label: "iPhone 11", value: "iPhone 11" }, { label: "iPhone X", value: "iPhone X" }, - { label: "iPad", value: "iPad" }, - { label: "iPhone 6/7/8 Plus", value: "iPhone 6/7/8 Plus" }, - { label: "iPhone 6/7/8", value: "iPhone 6/7/8" }, - { label: "iPhone 5/SE", value: "iPhone 5/SE" }, - { label: "HUAWEI Mate10", value: "HUAWEI Mate10" }, - { label: "HUAWEI Mate20", value: "HUAWEI Mate20" }, - { label: "HUAWEI Mate30", value: "HUAWEI Mate30" }, + { label: "iPhone 12 Pro", value: "iPhone 12 Pro" }, + { label: "iPhone 14 Pro Max", value: "iPhone 14 Pro Max" }, + { label: "iPad Pro", value: "iPad Pro" }, + { label: "iPad Mini", value: "iPad Mini" }, + { label: "Pixel 5", value: "Pixel 5" }, + { label: "Pixel 7", value: "Pixel 7" }, + { + label: "Samsung Galaxy S20 Ultra", + value: "Samsung Galaxy S20 Ultra", + }, + { + label: "Samsung Galaxy Tab S7", + value: "Samsung Galaxy Tab S7", + }, + { label: "Xiaomi 12 Pro", value: "Xiaomi 12 Pro" }, { label: "HUAWEI Mate30 Pro", value: "HUAWEI Mate30 Pro" }, ];