diff --git a/docs/important-notice.md b/docs/important-notice.md index 0caacb6..7e93fff 100644 --- a/docs/important-notice.md +++ b/docs/important-notice.md @@ -66,5 +66,6 @@ V2 当前是 **Preview 体验版,仅面向专业版用户**。功能仍在快 "2223-分享适用软件与浏览器通用", "2224-获取最后打开编辑器的动作", "2225-设置窗口改版与本机界面状态", - "2226-轮盘-f2-编辑指向的动作" + "2226-轮盘-f2-编辑指向的动作", + "230-脚本动作" ]} /> diff --git a/docs/release-notes/index.md b/docs/release-notes/index.md index edd3b29..659950e 100644 --- a/docs/release-notes/index.md +++ b/docs/release-notes/index.md @@ -28,8 +28,19 @@ comments: true 多设备使用、降级或回退前,请阅读[升级与回退](/v2/migration/upgrade-and-rollback)。各版本的具体风险也在下方对应条目中标出。 +官网版本记录目前写到 2.3.0,完整条目以那里为准。 + ## 近期文档补充 +### 2.3.0 · 脚本动作与截图/设置 AI + +- 新增 [脚本动作](/v2/features/script-actions/):用 C# 语法编写动作,通过内置 API 处理文本、文件、网络、窗口和键鼠,并可调用其他动作与公共子程序。编辑器支持 AI 辅助、代码检查、断点与单步调试、运行轨迹和变量变化查看;可最小化后延迟运行。安全与授权见 [脚本动作安全与授权](/v2/features/script-actions/security);回退注意见[升级与回退](/v2/migration/upgrade-and-rollback#230-脚本动作)。 +- [设置与场景窗口中的 AI 助手](/v2/features/ai-and-agent#设置与场景窗口中的-ai-助手):可用自然语言协助调整设置和触发规则。 +- [内嵌子程序](/v2/xaction/concepts/subprogram#检查内嵌子程序的来源更新)支持从来源检查和应用更新,并可撤销、重做更新。 +- [长截图自动滚动](/v2/features/screenshot/capture-pro#长截图自动滚动)与速度调整;选区外增加采集状态提示。 +- [二维码与条形码](/v2/features/screenshot/capture-pro#二维码与条形码)识别扩展为多种常见码制;自动预览优先快速显示,手动识别执行完整扫描。 +- 选择操作类型时支持搜索和分类筛选(完整说明见[官网版本记录](https://getquicker.net/V2/Versions))。 + ### 2.2.26 · 动作编辑 AI 分析、上下文菜单与入口整合 - [动作编辑窗口 AI](/v2/features/ai-and-agent#用-ai-编写和修改动作)支持「运行并分析」:可查看最近运行结果、定位失败步骤并分析运行问题。 diff --git a/docs/v2/features/actions.md b/docs/v2/features/actions.md index 84068cb..7794059 100644 --- a/docs/v2/features/actions.md +++ b/docs/v2/features/actions.md @@ -21,6 +21,11 @@ Quicker 2.0 将动作内容与使用位置分开。同一个动作可以出现 - 需要了解变量、表达式和子程序时,查看[基础与进阶概念](/v2/xaction/concepts)。 - 需要参考完整场景时,查看[教程与实践](/v2/xaction/guides)。 + +## 脚本动作 + +2.3.0 起,可新建 **Quicker脚本动作**,用 C# 语法编写动作逻辑,通过 `qk` 调用文本、文件、网络、窗口与键鼠等能力,并调用其他动作或公共子程序。入门、安全与 API 见 [脚本动作](/v2/features/script-actions/)。 + ## 动作面板(新面板) 日常如何启用、查找、整理分组与显示方式,见 [动作面板(新面板)](/v2/features/action-panel/)。相对 1.x / 迁移说明仍在 [新面板窗口(更新说明)](/v2/what's-new/new-main-win/usage)。 diff --git a/docs/v2/features/ai-and-agent.md b/docs/v2/features/ai-and-agent.md index 521fbfa..3b512c3 100644 --- a/docs/v2/features/ai-and-agent.md +++ b/docs/v2/features/ai-and-agent.md @@ -191,11 +191,9 @@ Agent 对话与外部助手设置已有用户入口,但不代表所有开发 新对话中尚未发送的文本和引用可在本机保留;同一工作区保留一个新对话草稿入口。重新打开后可继续编辑,发送前仍需检查引用和附件。输入草稿尚不是已发送的历史消息,也不会因为恢复输入框而自动发送。 -## 开发版:设置与场景窗口中的 AI 助手 +## 设置与场景窗口中的 AI 助手 -以下依据 2026-10-01 核对的开发代码,尚未包含在 2.2.26 中。 - -在「设置」或「场景、动作与触发管理」窗口中,点击标题栏的 AI 按钮,或按 `Ctrl+J`,可打开右侧助手。先配置 AI 服务与模型,再说明要查找的设置、目标场景和已有动作,例如「把主题改成深色」或「在 Chrome 场景为翻译动作设置快捷键」。场景窗口会提供当前场景、页签和部分选中项作为上下文;涉及继承规则时,仍要明确希望修改哪个场景。 +2.3.0 起,在「设置」或「场景、动作与触发管理」窗口中,点击标题栏的 AI 按钮,或按 `Ctrl+J`,可打开右侧助手。先配置 AI 服务与模型,再说明要查找的设置、目标场景和已有动作,例如「把主题改成深色」或「在 Chrome 场景为翻译动作设置快捷键」。场景窗口会提供当前场景、页签和部分选中项作为上下文;涉及继承规则时,仍要明确希望修改哪个场景。 助手可查找设置、修改已支持的设置项,并为已有动作配置支持的触发规则。回复中的设置或触发链接可打开相应页面,便于核对实际结果。部分列表或尚未支持直接修改的设置只提供页面入口,需要本人继续操作;手势绘制、轮盘布局以及动作程序编写也应到对应编辑器完成。 diff --git a/docs/v2/features/index.md b/docs/v2/features/index.md index 59cc226..fb3a446 100644 --- a/docs/v2/features/index.md +++ b/docs/v2/features/index.md @@ -24,6 +24,7 @@ hide_table_of_contents: true {href: '/v2/features/triggers/', label: '快捷键与鼠标触发', description: '快捷键、手势、轮盘、划词工具条和其他触发方式。'}, {href: '/v2/features/floating-actions', label: '悬浮动作与分组', description: '把动作留在桌面上,整理悬浮布局或跟随程序窗口。'}, {href: '/v2/features/actions', label: '动作的运行与管理', description: '了解动作、面板入口和引用关系,避免误删动作本体。'}, + {href: '/v2/features/script-actions/', label: '脚本动作', description: '用 C# 编写动作,调用 qk API 处理文本、文件、窗口与网络。'}, {href: '/v2/features/action-sharing', label: '分享动作与公共子程序', description: '发布、安装和更新动作,了解 V1 / V2 格式与版本历史。'}, ]} /> diff --git a/docs/v2/features/screenshot/capture-pro.md b/docs/v2/features/screenshot/capture-pro.md index 052a910..afef8d5 100644 --- a/docs/v2/features/screenshot/capture-pro.md +++ b/docs/v2/features/screenshot/capture-pro.md @@ -89,17 +89,17 @@ comments: true 2.2.8 起,选区侧栏支持**按住刷新**查看实时桌面,松开后重新捕获当前画面;也可按 `F5` 刷新截图,并保留当前选区、样式和标注。刷新后再用马赛克、模糊、填充、橡皮擦或撤销 / 重做时,不会带回旧画面;旧画面上的智能马赛克识别结果也会清除。 -### 选区二维码 +### 二维码与条形码 2.2.5 起,OCR 结果窗可以查看并复制识别到的二维码内容。2.2.6 起,截图选区阶段也会自动识别选区内的二维码:把光标悬停到码上即可看到内容,并可直接复制或打开链接,不必先打开结果窗。 选区内没有可识别的码、或码被遮挡、模糊时,不会出现悬停提示;仍可确认截图后,在 [结果窗](#结果窗) 中查看二维码结果。 -#### 开发版:识别更多条码 +#### 识别更多条码 -2026-10-01 核对的开发版扩展了截图 Pro 与贴图的本地条码识别,尚未包含在 2.2.26 中。除二维码外,还支持 Code 128、EAN / UPC、Data Matrix、Aztec、PDF417 等常见格式,并改进竖向、反色和同图多个码的识别。悬停结果可查看或复制内容;只有有效的 HTTP / HTTPS 地址才提供打开链接。 +2.3.0 起,截图 Pro 与贴图的本地识别扩展为**二维码 / 条形码**:除二维码外,还支持 Code 128、EAN / UPC、Data Matrix、Aztec、PDF417 等常见格式,并改进竖向、反色和同图多个码的识别。悬停结果可查看或复制内容;只有有效的 HTTP / HTTPS 地址才提供打开链接。 -选区内先显示快速预览;未出现提示并不一定代表图片中没有码。可缩小选区、提高图片清晰度,或先贴图,再从右键菜单执行「识别二维码 / 条形码」进行完整识别。完整扫描与快速预览的识别范围和耗时不同,结果仍需核对。本项说明针对截图与贴图界面,不改变「识别二维码」组合动作模块的参数定义。 +选区内先显示快速预览(自动预览优先快速显示结果);未出现提示并不一定代表图片中没有码。可缩小选区、提高图片清晰度,或先贴图,再从右键菜单执行「识别二维码 / 条形码」进行完整扫描。完整扫描与快速预览的识别范围和耗时不同,结果仍需核对。本项说明针对截图与贴图界面,不改变「识别二维码」组合动作模块的参数定义。 ### 自动吸附 @@ -164,11 +164,9 @@ comments: true 超长图贴图时会先适合当前屏幕并居中,必要时首次缩放可低于 10%,避免窗口超出工作区。 -#### 开发版:长截图自动滚动 +#### 长截图自动滚动 -以下依据 2026-10-01 核对的开发代码,尚未包含在 2.2.26 中。 - -选好范围并进入长截图后,点击工具栏的 **自动滚动**。纵向模式向下滚动,横向模式向右滚动;每次匹配完成后再继续下一次滚动。点击速度按钮可在 **0.5×~2×** 间调整,速度面板打开时暂停采集,关闭后继续。 +2.3.0 起,选好范围并进入长截图后,点击工具栏的 **自动滚动**。纵向模式向下滚动,横向模式向右滚动;每次匹配完成后再继续下一次滚动。点击速度按钮可在 **0.5×~2×** 间调整,速度面板打开时暂停采集,关闭后继续。 点击 **停止自动滚动** 可切回手动滚动;需要暂停采集或移动选区时,使用 **暂停采集**。暂停、移动选区或进入编辑、导出会停止自动滚动。它不会替你自动滚回页面起点。 diff --git a/docs/v2/features/script-actions/_category_.json b/docs/v2/features/script-actions/_category_.json new file mode 100644 index 0000000..77e9b97 --- /dev/null +++ b/docs/v2/features/script-actions/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "脚本动作", + "position": 11, + "link": { + "type": "doc", + "id": "v2/features/script-actions/index" + } +} diff --git a/docs/v2/features/script-actions/api.md b/docs/v2/features/script-actions/api.md new file mode 100644 index 0000000..5332749 --- /dev/null +++ b/docs/v2/features/script-actions/api.md @@ -0,0 +1,787 @@ +--- +title: 脚本动作 API 参考 +description: 脚本动作中 qk 对象各域成员、数据类型、运行限额与错误码说明。以编辑器补全与当前 Quicker 行为为准。 +sidebar_position: 3 +quickerDocKey: v2/features/script-actions/api +comments: true +--- + +# 脚本动作 API 参考 + +> 本文列出脚本中可用的 `qk` 成员,按功能域组织。签名与中文说明以当前 Quicker 编辑器补全为准;`qk` API 随版本演进时,编辑器的「检查」会指出旧写法并给出新写法。 +> +> 相关文档:[脚本动作入门](./)、[脚本动作安全与授权](./security)。 + +## 阅读说明 + +- **能力**:需要授权确认的能力(仅对导入/安装来源的动作确认,见 [安全与授权](./security))。写“无”表示不需要确认。 +- **写法**:请直接写 `qk.域.方法(...)`,不要把 `qk` 或 `qk.Files` 等赋给变量再调用,否则 Quicker 无法识别所需能力。 +- **通用约定**: + - 时长、超时一律为**毫秒**(参数名以 `Ms` 结尾);`timeoutMs = 0` 表示不单独限时,但任何调用都受动作总超时约束。 + - 屏幕坐标为**物理像素**,主显示器左上为 (0, 0),其他显示器可以是负坐标;图片内坐标以图片左上为原点;界面尺寸(如表单宽度)为逻辑像素。 + - **用户取消返回 `null`**(`Confirm` 返回 `false`);**查询无结果返回 `null` 或空数组**;失败抛 `ActionApiException`,按错误码判断(见[错误码表](#错误码表))。 + - 所有失败都可能出现 `INVALID_ARGUMENT`(参数不合法)、`CAPABILITY_DENIED`(未获授权或在停止后的清理阶段调用)、`LIMIT_EXCEEDED`(超出限额),下文不再逐条列出。 + - “持久”表示效果在脚本运行结束后仍然保留。 + +## 目录 + +- [入口:Main、ActionParameter、ActionMenu](#入口) +- [根成员:Log、Wait、Context](#根成员) +- [Selection](#qkselection选区)、[State](#qkstate动作状态)、[Actions](#qkactions动作)、[Ui](#qkui对话框与界面)、[Window](#qkwindow窗口)、[Keyboard](#qkkeyboard键盘)、[Mouse](#qkmouse鼠标)、[Clipboard](#qkclipboard剪贴板)、[Files](#qkfiles文件)、[Process](#qkprocess进程)、[Image 与 Img](#qkimage-与-img图片)、[Text](#qktext文本工具)、[Http](#qkhttp网络)、[Screen](#qkscreen截屏)、[Vision](#qkvision找图找字与-ocr)、[Browser](#qkbrowser浏览器)、[Apps](#qkapps外部程序)、[Ai](#qkaiai-与翻译)、[Uia](#qkuia界面自动化)、[Quicker](#qkquickerquicker-服务)、[Sys](#qksys系统)、[Steps](#qksteps组合动作步骤) +- [数据类型](#数据类型)、[每次运行的限额](#每次运行的限额)、[错误码表](#错误码表)、[字符串取值表](#字符串取值表) + +--- + +## 入口 + +| 项 | 说明 | +|---|---| +| `Main(...)` | 唯一入口,不能重载,不能带修饰符或泛型。参数即输入,返回值即输出(`void` 无输出)。参数类型:`string`、`string?`、`bool`、`int`、`long`、`double`、`decimal`、`int?`、`bool?`、`string[]`、`int[]`、`object`、`DateTimeOffset`、`TimeSpan`;最多 64 个 | +| 必填推断 | 非可空且无默认值 = 必填;可空(`?`)或有默认值 = 可选 | +| `[ActionParameter("标题", Description, Options, MultiLine, Required, Ask)]` | 修饰 `Main` 参数。`Options` 每行“标题\|值”,只用于 `string`;`Ask = true` 表示交互运行时总是弹出表单确认 | +| `string quicker_in_param = ""` | 接收动作的原始输入文本;始终可选,不触发表单,不能标 `Ask`;不声明时可用 `qk.Context.Input` 读取 | +| `[ActionMenu("组/项", Description, Icon)]` | 修饰无参、返回 `void` 的方法,生成固定右键菜单项;点击只运行该方法、不运行 `Main` | +| 返回值 | 单值最多 1 MiB、10,000 项、32 层;`Win`/`Img`/`El` 等句柄不能返回(`CODEC_UNSUPPORTED`) | +| 运行超时 | 默认 30 秒,可设 0.1 秒–24 小时或不限制;等待对话框和按键的时间也计入 | + +## 根成员 + +| 签名 | 说明 | 能力 | +|---|---|---| +| `void qk.Log(string message, string level = "info")` | 写运行日志;`level`:`debug`、`info`、`warn`、`error`;单条最多 4096 字符。停止后的 `finally` 中可用 | 无 | +| `void qk.Wait(int durationMs)` | 等待指定毫秒,可被停止打断 | 无 | +| `Ctx qk.Context { get; }` | 本次运行的只读上下文快照(动作信息、触发方式、触发时的窗口与鼠标、传入的文本/图片等;首次读取时生成,运行内不变);要保存的数据见 `qk.State` | 无 | + +`Ctx` 的字段: + +| 字段 | 说明 | +|---|---| +| `string ActionId`、`string ActionTitle` | 当前动作的 Id 与标题 | +| `Guid RunId` | 本次运行的 Id | +| `string Trigger` | 触发方式,如 `panel`、`hotkey`、`contextMenu`、`editor`,完整取值见[字符串取值表](#字符串取值表) | +| `DateTimeOffset StartedAt` | 运行开始时间 | +| `bool Debugging` | 是否为调试运行 | +| `string? Input` | 调用方传入的原始输入(与 `quicker_in_param` 同值);没有为 `null` | +| `string? Text` | 触发时附带的上下文文本(如文本工具栏);没有为 `null`;与 `Input` 互不代替 | +| `Pt Mouse` | 弹出面板前的鼠标位置;没有面板时为运行开始时的位置 | +| `Win? MouseWindow` | 弹出面板前鼠标下的顶层窗口;没有记录、已关闭或属于管理员程序时为 `null` | +| `Win? ActiveWindow` | 弹出面板前的前台窗口(`qk.Window.RestoreForeground()` 的目标) | +| `string? MouseProcess`、`int? MouseDpi` | `MouseWindow` 的进程名(不含 `.exe`)与所在屏幕 DPI | +| `Img? Image` | 触发时附带的图片(如从截图工具栏触发);首次读取时才复制像素 | + +--- + +## qk.Selection(选区) + +前台程序中用户当前选中的内容(选中文本、资源管理器/桌面中选中的文件);读写剪贴板见 `qk.Clipboard`,资源管理器当前文件夹见 `qk.Files.GetExplorerPath/SetExplorerPath`。 + +| 签名 | 说明 | 能力 / 常见错误码 | +|---|---|---| +| `string? GetText(int timeoutMs = 500, string format = "text")` | 模拟复制读取当前选中的内容;`format`:`text`、`html`、`rtf`、`csv`;无选中返回 `null`。会临时占用剪贴板,完成后恢复 | 读取选中文本 / `SELECTION_UNAVAILABLE`、`SELECTION_FAILED` | +| `string[] GetFiles()` | 资源管理器或桌面中选中的文件/文件夹完整路径;不在资源管理器中返回空数组 | 读取资源管理器选中路径 | +只支持 Windows 资源管理器与桌面。读取/切换资源管理器当前文件夹见 `qk.Files.GetExplorerPath/SetExplorerPath`。 + +## qk.State(动作状态) + +当前动作自己的状态存储(本机,与图形动作共用);跨设备存储见 `qk.Quicker.GetCloud`。 + +状态属于当前动作,与组合动作的“状态存储”互通;未保存的动作只在本次运行内保留。键为 1–256 个字符,单值最多 1 MiB。全部方法在停止后的 `finally` 中可用。能力:无。 + +| 签名 | 说明 | +|---|---| +| `T? Get(string key, T? defaultValue = default)` | 读取 JSON 状态;不存在返回 `defaultValue`。类型不符报 `CODEC_VALUE_INVALID` | +| `void Set(string key, object? value)` | 写入 JSON 状态(不能写 `Win`、`Img` 等句柄) | +| `string? GetText(string key)` | 读取原始文本状态(与组合动作的文本状态互通) | +| `void SetText(string key, string value)` | 写入原始文本状态;`"*NULL*"` 是保留值,不能写入 | +| `void Remove(string key)` | 删除;不存在时无操作 | +| `T? GetGlobal(string key, T? defaultValue = default)` / `void SetGlobal(string key, object? value)` | 跨动作共享的全局状态(JSON),慎用 | +| `string? GetGlobalText(string key)` / `void SetGlobalText(string key, string value)` | 全局状态的原始文本 | +| `void RemoveGlobal(string key)` | 删除全局状态键 | + +不要用 `Get` 读取 `SetText` 写入的普通文本。常见错误码:`STATE_UNAVAILABLE`、`CODEC_VALUE_INVALID`。 + +## qk.Actions(动作) + +调用和管理 Quicker 动作(同步调用本机动作或公共子程序、查询/停止运行中的动作、设置当前动作的角标与右键菜单);启动外部程序见 `qk.Process`。 + +| 签名 | 说明 | 能力 / 常见错误码 | +|---|---|---| +| `object? Call(string action, string? input = null)` | 同步调用本机动作(Id 或名称),`input` 为原始输入字符串;返回被调动作的结果 | 调用动作 / `CALL_FAILED`(被调方错误码在 `e.Detail`)、`CALL_DEPTH_LIMIT_EXCEEDED` | +| `ActionInfo? Info(string action)` | 读取动作信息,找不到返回 `null`;安装来源的脚本只能读自身 | 无 / `ACCESS_DENIED` | +| `int GetRunningCount(string? action = null)` | 动作正在运行的实例数(含本次);`null` 为当前动作 | 无 / `ACCESS_DENIED` | +| `IReadOnlyDictionary CallSubprogram(string name, IDictionary? inputs = null)` | 调用本机公共子程序,返回输出参数 | 调用动作 / `SUBPROGRAM_NOT_FOUND`、`CALL_FAILED` | +| `int Stop(string action)` | 停止指定动作正在运行的实例(不含本次),返回停止的实例数 | 调用动作 | +| `void StopOthers()` | 停止当前动作的其他运行实例 | 无 | +| `void SetBadge(string? text, string? color = null, string? textColor = null)` | 设置当前动作按钮的角标(持久),`null` 清除;停止后的 `finally` 中只能清除 | 无 | +| `void SetOverlay(string? icon, string? tooltip = null)` | 设置按钮角落的覆盖图标(`fa:` 图标,持久),`null` 清除 | 无 | +| `void SetContextMenu(string? menu)` | 设置当前动作的附加右键菜单(持久),每行“[fa:图标]标题\|值”,`"[+]组名"` 后接 `"[-]标题\|值"` 为子菜单,`"----"` 为分隔线,最多 100 行;点击后以该值为输入重新运行 `Main` | 无 | +| `void ShowContextMenu(string? action = null, bool customOnly = true)` | 在鼠标处弹出动作的右键菜单后立即返回 | 调用动作 / `ACCESS_DENIED` | + +被调用的动作或子程序被用户取消时,本脚本也随之停止。 + +## qk.Ui(对话框与界面) + +Quicker 自己弹出的对话框、通知与窗口;操作其他程序窗口里的界面元素见 `qk.Uia`。 + +所有方法能力均为“无”(`Notify` 带 `click` 时另需启动程序)。`options` 为共享界面选项(见 [UiOptions](#数据类型))。取消返回 `null`。列表最多 200 项。常见错误码:`DIALOG_LIMIT_EXCEEDED`、`NOTIFY_LIMIT_EXCEEDED`、`UI_UNAVAILABLE`。 + +| 签名 | 说明 | +|---|---| +| `SelectResult? Select(IEnumerable items, string? note = null, string? selected = null, string[]? operations = null, bool quick = false, bool? filter = null, string? filterText = null, UiOptions? options = null)` | 单选。`items` 为 `"[fa:图标]标题\|值"` 字符串或 `Item`;`operations` 为附加操作菜单“标题\|值”,选中后写入 `SelectResult.Operation`;`filter` 为 `null` 时超过 10 项自动显示筛选框 | +| `SelectManyResult? SelectMany(IEnumerable items, string? note = null, IEnumerable? selected = null, string[]? operations = null, bool allowEmpty = false, bool? filter = null, string? filterText = null, UiOptions? options = null)` | 多选 | +| `string? Prompt(string message, string value = "", bool multiline = false, bool required = false, string? pattern = null, string? tools = null, bool enterSubmit = true, UiOptions? options = null)` | 输入文本;`pattern` 为正则校验 | +| `double? PromptNumber(string message, double? value = null, string? pattern = null, UiOptions? options = null)` | 输入数字;`null` 只表示取消 | +| `DateTimeOffset? PromptDate(string message, DateTimeOffset? value = null, UiOptions? options = null)` | 选择日期时间(本地时区) | +| `void Alert(string message, string icon = "info", UiOptions? options = null)` | 消息框;`icon`:`none`、`info`、`question`、`warn`、`error` | +| `bool Confirm(string message, bool yesNo = false, string icon = "question", UiOptions? options = null)` | 确认返回 `true`,否认或关闭返回 `false` | +| `string? Ask(string message, IEnumerable buttons, string? defaultValue = null, string icon = "question", UiOptions? options = null)` | 自定义按钮“[图标]标题(_K)\|值”,返回所点按钮的值 | +| `FormResult? Form(IEnumerable fields, IDictionary? values = null, string? note = null, string[]? buttons = null, string? group = null, bool enterSubmit = true, int width = 0, int labelWidth = 0, UiOptions? options = null)` | 表单,返回 `Values`(字段键 → 值)、`Button`、`Group`。字段类型见 [Field](#数据类型) | +| `TextResult ShowText(string text, string[]? operations = null, string? language = null, bool wrap = true, bool lineNumbers = false, int caret = -1, string? saveKey = null, UiOptions? options = null)` | 显示文本并等待关闭,返回最终文本;**永不返回 `null`**。关闭后不恢复原前台窗口 | +| `void Notify(string message, string kind = "info", string? title = null, int durationMs = 0, string position = "bottomCenter", string? key = null, string duplicate = "replace", string? click = null)` | 通知,不等待。`kind`:`info`、`success`、`warn`、`error`、`toast`;`durationMs`:0 默认、-1 常驻或 2000–30000;`duplicate`:`replace`、`count`、`ignore`(需给 `key`) | +| `string? PickFile(string? filter = null, string? directory = null, UiOptions? options = null)` | 选择文件;`filter` 如 `"图片\|*.png;*.jpg"` | +| `string[]? PickFiles(string? filter = null, string? directory = null, UiOptions? options = null)` | 选择多个文件;取消为 `null`,确认空结果为空数组 | +| `string? PickSaveFile(string? filter = null, string? fileName = null, string? directory = null, UiOptions? options = null)` | 另存为对话框:选择保存路径(只返回路径,不写文件) | +| `string? PickFolder(string? directory = null, UiOptions? options = null)` | 选择文件夹 | +| `void Pin(object content, Pt? at = null, string kind = "auto")` | 贴图(持久,由用户关闭)。`content` 为 `Img` 或字符串;`kind`:`auto`、`image`、`text`、`html`、`latex`。HTML 在隔离环境中显示,不加载外部资源;置顶钉在屏幕上(看图窗口见 `ShowImage`) | +| `TextWin OpenText(string text, string[]? operations = null, string? language = null, UiOptions? options = null)` | 打开可编辑文本窗口(不等待),返回 `TextWin` 句柄;窗口在运行结束后保留 | +| `string? ShowMenu(IEnumerable items, bool focus = false, int fontSize = 0, int iconSize = 0)` | 在鼠标处弹出菜单,返回所选值;`Item.Children` 为子菜单,`"-"` 为分隔线 | +| `ProgressWin OpenProgress(string text, string? title = null, double? percent = null, string[]? operations = null, bool stopOnClose = true, bool bar = false, UiOptions? options = null)` | 进度窗口(不等待)。`stopOnClose` 默认 `true`:用户点 X 即**停止本次运行**;不希望停止时传 `false`。运行结束时自动关闭 | +| `void ShowImage(Img image, double scale = 1, bool wait = false, double opacity = 1, bool noActivate = false, UiOptions? options = null)` | 看图窗口:显示图片的普通窗口(要置顶钉在屏幕上见 `Pin`) | +| `Pt? PickPoint()` | 用户在屏幕上点选一个点 / `CAPTURE_BUSY` | +| `Rect? PickArea(bool detect = true)` | 用户框选区域(只返回坐标;需要截图用 `qk.Screen.PickCapture`) | +| `Win? PickWindow()` | 用户点选窗口 / `ELEVATED_TARGET_DENIED` | +| `string? PickColor(string? initialColor = null, bool fromScreen = false, bool alpha = false)` | 选色,返回 `#RRGGBB`(`alpha` 时 `#AARRGGBB`) | +| `List? EditList(IEnumerable items, string? note = null, bool allowAdd = true, bool allowEdit = true, bool allowDelete = true, int width = 0, int height = 0, UiOptions? options = null)` | 列表编辑窗,确认返回新列表 | +| `List>? EditTable(IEnumerable> rows, string[]? columns = null, bool readOnly = false, int width = 0, int height = 0, UiOptions? options = null)` | 表格查看/编辑窗;确认返回的值一律为字符串 | +| `object? React(string source, object? input = null, int width = 0, int height = 0, UiOptions? options = null)` | 显示单文件 TSX 界面并等待,返回页面 `close(result)` 的值;页面不能联网 / `UI_REACT_FAILED` | + +`TextWin` 句柄:`bool Closed`、`void Append(string text)`、`void SetText(string text)`、`void Activate()`、`TextResult? WaitForClose()`、`void Close()`。 + +`ProgressWin` 句柄:`bool Closed`、`string? Operation`(点击的附加按钮值)、`void Update(string? text = null, double? percent = null, string? title = null)`、`string? WaitForClose()`、`void Close()`。 + +## qk.Window(窗口) + +桌面上的顶层窗口本身(查找、激活、移动/缩放、置顶、关闭等);窗口里的按钮、输入框等界面元素见 `qk.Uia`。 + +`Win` 是本次运行内有效的窗口引用,只有 `Title`、`Process`(进程名,不含 `.exe`)两个只读属性,不能返回或写入状态。管理员权限的窗口会被拒绝(`ELEVATED_TARGET_DENIED`)。常见错误码:`WINDOW_REF_STALE`(窗口已关闭)、`WINDOW_LIMIT_EXCEEDED`、`WINDOW_UNAVAILABLE`、`WINDOW_CHANGED`。 + +匹配规则(`Find`/`FindAll`/`FindAllInfo`/`WaitFor`/`WaitForClose`):`title` 包含匹配、`process` 与 `className` 完整匹配,均不区分大小写;`regex: true` 时 `title` 为正则。 + +| 签名 | 说明 | 能力 / 常见错误码 | +|---|---|---| +| `Win? GetForeground()` | 当前前台窗口 | 读取窗口 | +| `Win? FromPoint(Pt? point = null, bool root = true)` | 点下的窗口;`null` 为鼠标位置;`root: false` 返回最深的子窗口 | 读取窗口 | +| `Win? Find(string title = "", string process = "", string className = "", bool regex = false, bool hidden = false)` | 第一个匹配的窗口,没有为 `null` | 读取窗口 | +| `Win[] FindAll(string title = "", string process = "", string className = "", bool regex = false, bool hidden = false, int limit = 64)` | 全部匹配(1–64 个) | 读取窗口 | +| `WinInfo[] FindAllInfo(string title = "", string process = "", string className = "", bool regex = false, bool hidden = false, int limit = 64)` | 同 `FindAll`,一次返回各窗口信息,整次只计 1 次观察 | 读取窗口 | +| `Win[] ListChildren(Win window, string title = "", string className = "")` | 子窗口,最多 64 个 | 读取窗口 | +| `WinInfo Info(Win window)` | 读取最新窗口信息(可返回) | 读取窗口 | +| `void Activate(Win window)` | 激活窗口(最小化时先还原) | 激活窗口 / `WINDOW_ACTIVATION_FAILED` | +| `Win? ActivateProcess(string process, string? path = null, string? title = null, string? className = null, bool regex = false, string? hotkey = null, bool launch = true)` | 程序已运行则激活其主窗口,否则按 `path` 启动;`hotkey` 为单个组合键 | 读取+激活窗口、启动程序(`hotkey` 另需键盘) | +| `void RestoreForeground()` | 回到弹出面板前的前台窗口 | 激活窗口 | +| `void SetBounds(Win window, Rect bounds)` | 设置位置大小 | 调整窗口 | +| `void SetState(Win window, string state)` | `normal`、`minimized`、`maximized` | 调整窗口 | +| `void SetVisible(Win window, bool visible)` | 隐藏或显示窗口 | 调整窗口 | +| `void SendToBack(Win window)` | 置于其他窗口之下 | 调整窗口 | +| `bool SetTopmost(Win window, bool topmost = true)` | 设置/取消置顶,返回调用后的实际状态 | 调整窗口 | +| `void SetOpacity(Win window, int alpha)` | 不透明度 0–255 | 调整窗口 | +| `void Close(Win window, bool kill = false)` | 请求关闭;`kill: true` 时未及时关闭则**强制结束程序** | 调整窗口(`kill` 另需强制结束程序) | +| `Win? WaitFor(string title = "", string process = "", string className = "", string state = "exists", int timeoutMs = 10000)` | 等待窗口出现;`state`:`exists`、`visible`、`foreground`;超时返回 `null` | 读取窗口 | +| `bool WaitForClose(string title = "", string process = "", string className = "", int timeoutMs = 10000)` | 等待匹配窗口全部关闭;超时返回 `false` | 读取窗口 | +| `Pt ToScreen(Win window, Pt offset, string anchor = "topLeft")` | 窗口内偏移转屏幕坐标;`anchor`:`topLeft`、`topRight`、`bottomLeft`、`bottomRight`、`center` | 读取窗口 | +| `long SendMessage(Win window, int message, long wParam = 0, object? lParam = null, bool post = false, int timeoutMs = 5000)` | 向窗口发送消息;字符串 `lParam` 只允许 `WM_SETTEXT`、`WM_COPYDATA` | 【高风险】窗口消息 / `WINDOW_MESSAGE_TIMEOUT`、`WINDOW_MESSAGE_FAILED` | +| `int CloseSimilar(Win window, bool keepCurrent = false)` | 关闭同一程序的顶层窗口,返回已发出的关闭请求数 | 调整窗口 / `WINDOW_ARRANGE_FAILED` | +| `int MinimizeSimilar(Win window)` / `int RestoreSimilar(Win window)` | 最小化 / 还原同一程序的窗口 | 调整窗口 | +| `Win? ActivateSimilar(Win window, bool previous = false)` | 激活同一程序的下一个(或上一个)窗口 | 激活窗口 | +| `bool SetEdgeHide(Win window, bool enabled = true, string edge = "auto")` | 贴边自动隐藏(持久,直到关闭或 Quicker 退出);`edge`:`auto`、`left`、`top`、`right`、`bottom` | 调整窗口 / `WINDOW_EDGE_HIDE_FAILED` | + +子窗口只能查询,不能激活、排列或关闭;Quicker 自身的窗口不能排列或关闭。 + +## qk.Keyboard(键盘) + +向前台窗口模拟键盘输入(按键、组合键、输入文本、粘贴);读写剪贴板见 `qk.Clipboard`。 + +能力:键盘(`Paste` 另需读写剪贴板;`WaitForKey` 为键盘监听)。键盘与鼠标共用每次运行的输入限额;运行结束或停止时自动松开本次按下的键。常见错误码:`INPUT_LIMIT_EXCEEDED`、`INPUT_USER_ACTIVE`、`INPUT_UNAVAILABLE`、`INPUT_FAILED`。 + +| 签名 | 说明 | +|---|---| +| `void Press(string keys, int repeat = 1, int holdMs = 0)` | 按键或组合键,如 `"Ctrl+Shift+S"`、`"Enter"`、`"Ctrl+/"`;键名见表下说明 | +| `void Down(string key)` / `void Up(string key)` | 按下保持 / 松开(最多同时 8 个键);`Up` 可在停止后的 `finally` 中调用 | +| `bool IsDown(string key, bool toggled = false)` | 读取按键状态;`toggled` 读 CapsLock 等切换状态 | +| `int Type(string text, int intervalMs = 0)` | 逐字输入,返回输入的字符数;每次运行累计最多 2000 字符 | +| `void Paste(string text, bool restore = true, int restoreDelayMs = 400)` | 经剪贴板粘贴,默认粘贴后恢复原剪贴板 | +| `void SendKeys(string keys)` | SendKeys 语法:`+` Shift、`^` Ctrl、`%` Alt、`~` Enter、`{KEY}` | +| `string? WaitForKey(string? keys = null, int timeoutMs = 0, bool swallow = false)` | 等待用户的物理按键,返回键名;超时 `null`;`swallow` 拦截该次按键 | +| `bool GetIme()` / `void SetIme(bool chinese)` | 读取 / 设置前台窗口输入法中英文状态 | + +键名(不区分大小写):字母、数字、`F1`–`F24`、`Enter`/`Tab`/`Space`/`Esc`/方向键等功能键、小键盘键(`Numpad0`、`NumpadAdd`…)、修饰键 `Ctrl`/`Shift`/`Alt`/`Win`;单个标点 `,` `.` `/` `;` `-` `=` `[` `]` `\` `'` 与反引号(按美式键位,`+` 请写 `"Shift+="`);媒体与音量键 `MediaPlayPause`、`MediaNextTrack`、`MediaPreviousTrack`、`MediaStop`、`VolumeUp`、`VolumeDown`、`VolumeMute`;浏览器键 `BrowserBack`、`BrowserForward`、`BrowserRefresh`、`BrowserHome`;其他 Windows 键名(如 `OemPeriod`)或 `0x` 虚拟键码。`SendKeys` 的 `{KEY}` 使用同一键名,修饰键也可作用于上述标点(如 `^/`)。键名写错报 `INVALID_ARGUMENT`。 + +## qk.Mouse(鼠标) + +模拟鼠标移动、点击、拖动与滚轮(坐标为屏幕物理像素);不靠坐标操作控件见 `qk.Uia.Act`。 + +能力:鼠标。坐标为屏幕物理像素。 + +| 签名 | 说明 | +|---|---| +| `Pt GetPosition()` | 当前鼠标位置 | +| `Pt MoveTo(Pt to, int durationMs = 0)` | 移动到指定点,返回移动后位置 | +| `Pt MoveBy(int dx, int dy)` | 相对移动 | +| `Pt Click(Pt? at = null, string button = "left", int count = 1)` | 点击;`at` 为 `null` 时点当前位置;`button`:`left`、`right`、`middle`;`count` 1–3 | +| `void Down(string button = "left")` / `void Up(string button = "left")` | 按下保持 / 松开;`Up` 可在停止后的 `finally` 中调用 | +| `Pt Scroll(int clicks, bool horizontal = false)` | 滚轮,正数向上(水平时向右) | +| `Pt DragTo(Pt to, int durationMs = 300)` | 从当前位置拖动到目标 | +| `Pt RestorePosition()` | 回到 `qk.Context.Mouse`(弹出面板前或运行开始时的位置);停止后的 `finally` 中可用 | +| `string GetCursor()` | 当前指针形状,如 `arrow`、`iBeam`、`hand`、`wait`;能力:无 | + +## qk.Clipboard(剪贴板) + +系统剪贴板的读写(文本、HTML、图片、文件列表);读取前台选中内容见 `qk.Selection`。 + +能力:读取剪贴板 / 写入剪贴板。读写方法(`WaitForChange` 除外)在停止后的 `finally` 中可用,便于恢复剪贴板。常见错误码:`CLIPBOARD_UNAVAILABLE`、`CLIPBOARD_LIMIT_EXCEEDED`。 + +| 签名 | 说明 | +|---|---| +| `string? GetText()` / `string? GetHtml()` | 文本 / HTML 片段;没有返回 `null`(有文本格式但为空返回 `""`) | +| `string? Get(string format)` | `format` 取 `rtf`、`csv` 或自定义格式名,按文本返回;`text`/`html` 不接受(报 `INVALID_ARGUMENT`),请用 `GetText`/`GetHtml` | +| `string[] GetFiles()` | 文件路径列表;没有返回空数组 | +| `Img? GetImage()` | 剪贴板图片;没有返回 `null` | +| `void SetText(string text, bool noHistory = false)` | 写入文本;`noHistory` 不进剪贴板历史;`SetText("")` 等于清空剪贴板 | +| `void SetHtml(string html, string? text = null)` | 写入 HTML,`text` 为纯文本备用格式 | +| `void SetFiles(IEnumerable paths, bool cut = false)` | 写入文件列表(路径须存在);`cut` 为剪切(另需修改文件能力) | +| `void Set(string format, object data)` | 写入指定格式;`format` 取 `text`、`html`、`rtf`、`csv` 或自定义格式名,自定义格式可写 `string` 或 `byte[]` | +| `void SetImage(Img image)` | 写入图片 | +| `void Clear(bool history = false)` | 清空;`history: true` 同时清除系统剪贴板历史 | +| `bool WaitForChange(int timeoutMs = 5000)` | 等待内容变化,超时返回 `false`;最长 1 小时 | + +## qk.Files(文件) + +本机文件与文件夹(读写、复制、压缩、搜索)以及资源管理器当前文件夹;用关联程序打开文件见 `qk.Process.Open`。 + +能力:读取文件 / 修改文件(写入隐含读取);`GetExplorerPath`/`SetExplorerPath` 例外,按“读取资源管理器选中路径”(`SetExplorerPath` 另需激活窗口)。文本默认**严格 UTF-8**;写入、复制、移动默认**不覆盖**;删除默认**不经过回收站**;单次读写最多 16 MiB。建议使用绝对路径。常见错误码:`FILE_NOT_FOUND`、`ACCESS_DENIED`、`FILE_FAILED`。 + +| 签名 | 说明 | +|---|---| +| `string GetFullPath(string path)` | 转为绝对路径 | +| `string GetRunTempDirectory()` | 本次运行专属的临时文件夹,运行结束后自动删除;取目录不需能力 | +| `bool Exists(string path)` | 文件或文件夹是否存在 | +| `PathInfo? Info(string path)` | 路径信息(是否文件夹、大小、修改时间),不存在为 `null` | +| `byte[] ReadBytes(string path)` | 读取字节 | +| `string ReadText(string path, string encoding = "utf-8")` | 读取文本;其他编码显式传入,如 `"gbk"` | +| `void WriteBytes(string path, byte[] bytes, bool overwrite = false)` | 写入字节 | +| `void WriteText(string path, string text, bool overwrite = false, string encoding = "utf-8")` | 写入文本(不写 BOM) | +| `void AppendText(string path, string text, string encoding = "utf-8")` | 追加文本(不存在则创建) | +| `string[] ListFiles(string directory, string pattern = "*", bool recursive = false)` | 列出文件 | +| `string[] ListDirectories(string directory, string pattern = "*", bool recursive = false)` | 列出子文件夹 | +| `string CreateDirectory(string path)` | 创建文件夹(含上级),已存在直接返回 | +| `void Copy(string source, string destination, bool overwrite = false)` | 复制文件;目标文件夹须已存在 | +| `void Move(string source, string destination, bool overwrite = false)` | 移动或重命名文件/文件夹 | +| `void Delete(string path, bool recursive = false, bool recycle = false)` | 删除;非空文件夹须 `recursive: true`;`recycle: true` 移到回收站(不能进回收站时报错且不删除) | +| `void Zip(string source, string zipPath, bool overwrite = false)` | 压缩文件或文件夹内容 / `ZIP_FAILED` | +| `void Unzip(string zipPath, string directory, bool overwrite = false)` | 解压(全有或全无,拒绝越出目标文件夹的条目) / `ZIP_FAILED` | +| `string GetKnownFolder(string name)` | 已知文件夹路径,如 `desktop`、`documents`、`downloads`、`temp` | +| `void Reveal(params string[] paths)` | 在资源管理器中打开并选中(同一文件夹内 1–100 个) | +| `string? GetExplorerPath()` | 当前资源管理器窗口的文件夹;不在资源管理器中返回 `null`(只支持 Windows 资源管理器与桌面) | +| `void SetExplorerPath(string directory, Win? window = null)` | 让资源管理器窗口转到指定文件夹;`window` 为 `null` 时作用于前台窗口;打开/另存为对话框见 `qk.Uia.SetDialogPath` / `FILE_NOT_FOUND`、`EXPLORER_NOT_FOUND` | +| `string Hash(string path, string algorithm = "sha256", string output = "hex")` | 文件哈希(流式,不受 16 MiB 限制) | +| `string[] Search(string query, int limit = 100, bool regex = false, string? sort = null, bool descending = false)` | 用 Everything 搜索(须已运行)/ `EVERYTHING_UNAVAILABLE`、`EVERYTHING_FAILED` | + +## qk.Process(进程) + +启动程序、用关联程序打开文件/网址、运行命令行并读取输出、列出进程;在已打开的程序内部执行代码见 `qk.Apps`。 + +以普通(非管理员)权限启动;参数以数组传入,无需自己拼引号。常见错误码:`PROCESS_FAILED`、`PROCESS_TIMEOUT`。 + +| 签名 | 说明 | 能力 | +|---|---|---| +| `int Start(string executable, string[]? arguments = null, string workingDirectory = "")` | 启动程序并返回进程 Id,不等待 | 启动程序 | +| `void Open(string target)` | 用系统关联程序打开文件、文件夹或网址 | 启动程序 | +| `ProcResult Run(string executable, string[]? arguments = null, string workingDirectory = "", string encoding = "utf-8", int timeoutMs = 0)` | 隐藏窗口运行并等待退出,返回退出码与输出;非零退出码不算失败;中文控制台程序通常要传 `"gbk"`。超时或停止时尽力结束进程树(含其子进程) | 启动程序 | +| `ProcInfo[] List(string? name = null)` | 当前会话的进程列表 | 读取窗口与进程 | +| `int Kill(int pid)` / `int Kill(string name)` | 结束进程(不含子进程),返回结束的进程数(没有在运行为 0);只能结束当前登录会话中的进程,按名称时结束当前会话中所有同名进程。不能结束 Quicker 自身、资源管理器与系统关键进程(`INVALID_ARGUMENT`);无权限报 `PROCESS_FAILED` | 强制结束程序 | + +## qk.Image 与 Img(图片) + +`Img` 图片的读取、生成二维码、处理与保存为文件;截屏见 `qk.Screen`,找图/OCR/识别二维码见 `qk.Vision`。 + +`Img` 是本次运行内有效的图片句柄:不能直接返回或写入状态,需要带出时用 `ToBase64()`、`ToBytes()` 或保存为文件。变换方法返回**新图**。常见错误码:`IMAGE_DECODE_FAILED`、`IMAGE_ENCODE_FAILED`、`IMAGE_REF_DISPOSED`、`IMAGE_REF_INVALID`。 + +| 签名 | 说明 | 能力 | +|---|---|---| +| `Img qk.Image.Load(object source)` | 从文件路径、`http(s)` 网址、`byte[]`、Base64 或 `data:` URI 加载;支持 png/jpg/gif/bmp/tif/ico(不支持 webp) | 文件需读取文件,网址需网络 | +| `string qk.Image.Save(Img image, string path, int quality = 90, bool overwrite = false)` | 按扩展名保存,返回完整路径 | 修改文件 | +| `Img qk.Image.CreateQr(string text, int size = 256, Img? icon = null, string darkColor = "#000000", string lightColor = "#FFFFFF", bool quietZone = true)` | 生成二维码(识别见 `qk.Vision.ReadQr`) | 无 | +| `int img.Width` / `int img.Height` | 像素尺寸 | | +| `Img img.Crop(Rect area)` | 裁剪(图内坐标) | | +| `Img img.Resize(int width, int height = 0)` / `Img img.Scale(double factor)` | 缩放(`height` 为 0 按宽等比) | | +| `Img img.Rotate(int degrees)` | 顺时针旋转 90 的倍数 | | +| `Img img.Grayscale()` / `Img img.Invert()` | 灰度 / 反色 | | +| `byte[] img.ToBytes(string format = "png", int quality = 90)` | 编码为字节;`format`:`png`、`jpg`、`bmp` | | +| `string img.ToBase64(bool dataUri = false, string format = "png", int quality = 90)` | 编码为 Base64 | | +| `void img.Dispose()` | 提前释放像素(循环中大量取图时使用),可重复调用 | | + +## qk.Text(文本工具) + +纯计算的文本工具(哈希、拼音、HTML 解析);显示文本见 `qk.Ui.ShowText`。 + +能力:无。 + +| 签名 | 说明 | +|---|---| +| `string Hash(object data, string algorithm = "sha256", string? hmacKey = null, string output = "hex")` | 字符串(UTF-8)或 `byte[]` 的哈希/HMAC;`algorithm`:`md5`、`sha1`、`sha256`、`sha384`、`sha512`;`output`:`hex`、`base64` | +| `string ToPinyin(string text, bool initials = false, bool allReadings = false, string? separator = null)` | 汉字转拼音(无声调、小写) | +| `bool MatchPinyin(string text, string query)` | 与 Quicker 搜索相同的拼音匹配规则 | +| `string HtmlToText(string html)` | HTML 转纯文本(不联网) | +| `string[] QueryHtml(string html, string xpath, string? attribute = null, string output = "text")` | 用 XPath 1.0 查询 HTML 片段;`output`:`text`、`innerHtml`、`outerHtml` | + +--- + +## qk.Http(网络) + +直接发送 HTTP 请求与下载文件(不经浏览器);操作网页见 `qk.Browser`。 + +能力:访问网络(`Download` 另需修改文件)。只支持 `http`/`https`,使用 Quicker 的代理设置,不自动重试。常见错误码:`HTTP_FAILED`(网络失败)、`HTTP_STATUS_FAILED`(非 2xx)、`HTTP_TIMEOUT`(请求已取消,但服务端可能已处理)。 + +| 签名 | 说明 | +|---|---| +| `HttpResult Send(string url, string method = "GET", string? body = null, string contentType = "application/json", IDictionary? headers = null, IDictionary? form = null, IDictionary? files = null, int timeoutMs = 60000)` | 通用请求;**非 2xx 仍返回结果**(看 `StatusCode`);不自动跟随重定向;`form`/`files` 用于 multipart 上传(`files` 另需读取文件) | +| `string GetText(string url, int timeoutMs = 60000)` | GET 并返回文本;非 2xx 抛 `HTTP_STATUS_FAILED` | +| `string PostJson(string url, string json, int timeoutMs = 60000)` | POST 已序列化的 JSON,返回响应文本 | +| `string Download(string url, string path, bool overwrite = false, IDictionary? headers = null, int timeoutMs = 0)` | 下载到文件并返回完整路径;最多 512 MiB;失败不留残缺文件 | + +## qk.Screen(截屏) + +获取屏幕图像:截屏、截窗口、框选截图、截图 Pro、取色与显示器信息;只要坐标不要图像见 `qk.Ui.PickArea`。 + +屏幕物理像素;UAC、锁屏等安全桌面报 `SECURE_DESKTOP`。 + +| 签名 | 说明 | 能力 / 常见错误码 | +|---|---|---| +| `Img Capture(Rect? area = null, string screen = "all")` | 截屏;`screen`:`all`、`primary`、`mouse` | 截屏 / `SCREEN_CAPTURE_FAILED` | +| `Img CaptureWindow(Win window, bool background = false)` | 截窗口;`background: true` 被遮挡也能截(部分程序为黑图) | 截屏 / `WINDOW_CAPTURE_FAILED` | +| `CaptureResult? PickCapture(bool detect = true, int delayMs = 0)` | 用户框选截图,取消返回 `null` | 截图 Pro / `CAPTURE_BUSY` | +| `CaptureResult? CapturePro(string mode = "capture")` | 打开截图 Pro;`mode` 见[字符串取值表](#字符串取值表);部分模式另需剪贴板、文件或网络能力 | 截图 Pro / `CAPTURE_PRO_FAILED`、`CAPTURE_BUSY` | +| `string GetPixel(Pt point)` | 屏幕点颜色 `#RRGGBB`(不计截图次数,适合轮询) | 截屏 | +| `ScreenInfo[] List()` | 全部显示器信息(主屏在前);`Id` 可传给 `qk.Sys.GetBrightness/SetBrightness` 的 `screen` | 无 | + +## qk.Vision(找图、找字与 OCR) + +在屏幕或图片中识别内容(找图、找色、找字、OCR、识别二维码);截图本身见 `qk.Screen`,生成二维码见 `qk.Image.CreateQr`。 + +结果为屏幕坐标(传入 `Img` 时为图内坐标);没找到返回空数组。找图/找色/找字的 `timeoutMs > 0` 时每 300 毫秒重试一轮(每轮计 1 次截图)。能力:截屏(找字与 OCR 另需本机 OCR)。 + +文字识别只用 **本机 OCR**(不联网、不自动下载模型)。`model` 为 `small`(默认,更准)或 `tiny`(更快),`detectOrientation` 为 `true` 时同时识别旋转/倒置的文字(较慢);两者只影响本次调用,不改 Quicker 的 OCR 设置。`Ocr`/`OcrTable` 的 `timeoutMs` 默认 30000、最大 90000(0 为不另设,只受动作超时约束);单次识别请求另有约 90 秒上限,超出报 `OCR_FAILED`;`timeoutMs` 只能缩短不能延长这一上限。返回全部识别结果,不限次数与行数;结果很大时(返回值最多 1 MiB)请在脚本中只取需要的部分。 + +| 签名 | 说明 | 常见错误码 | +|---|---|---| +| `Hit[] FindImage(Img template, Rect? area = null, Win? window = null, double similarity = 0.9, int limit = 1, int timeoutMs = 0)` | 找图,按相似度降序 | | +| `Pt[] FindColor(string color, Rect? area = null, Win? window = null, int tolerance = 0, int limit = 1)` | 找颜色 `#RRGGBB` | | +| `Hit[] FindText(string text, Rect? area = null, Win? window = null, int timeoutMs = 0, string model = "small", bool detectOrientation = false)` | 本机 OCR 找文字(包含、忽略大小写,每行取第一处);命中框按文字在行内的位置**估算** | `OCR_UNAVAILABLE` | +| `OcrResult Ocr(object? source = null, string model = "small", bool detectOrientation = false, int timeoutMs = 30000)` | 识别文字;`source` 为 `Img`、`Rect` 或 `null`(全部显示器);返回全文、各行(位置、四点框、置信度、行/段/栏序号)与智能排版文本 `LayoutText` | `OCR_UNAVAILABLE`、`OCR_TIMEOUT`、`OCR_FAILED` | +| `OcrTableResult OcrTable(object? source = null, bool detectOrientation = false, int timeoutMs = 30000)` | 本机表格识别,返回 TSV、HTML、行列数、单元格与表头行数 | 同上 | +| `string[] ReadQr(Img image)` | 识别二维码;没有返回空数组(生成见 `qk.Image.CreateQr`);能力:无 | | + +## qk.Browser(浏览器) + +浏览器中的网页(标签页、元素、表单、页面脚本),经 Quicker 浏览器扩展读取和操作;单纯请求网址或下载见 `qk.Http`。 + +除 `Open` 外需要 Quicker 浏览器扩展已连接。`timeoutMs` 不支持 0(范围 200–300000)。常见错误码:`BROWSER_UNAVAILABLE`(没有已连接的浏览器)、`BROWSER_FAILED`(扩展返回失败,扩展错误码在 `e.Detail`)、`BROWSER_TIMEOUT`(宿主停止等待,页面上的操作可能仍在进行)。 + +| 签名 | 说明 | 能力 | +|---|---|---| +| `void Open(string url, string browser = "default")` | 用浏览器打开网址(不经扩展);`browser` 见[字符串取值表](#字符串取值表) | 启动程序 | +| `string? GetUrl()` | 前台浏览器当前标签页网址;前台不是浏览器或未连接扩展时为 `null` | 操作浏览器 | +| `Tab[] ListTabs()` | 目标浏览器的全部标签页 | 操作浏览器 | +| `int OpenTab(string url, bool wait = true)` | 新标签页打开网址,返回标签页 Id | 操作浏览器 | +| `void ActivateTab(int tabId)` / `void CloseTab(int tabId)` | 激活 / 关闭标签页 | 操作浏览器 | +| `object? Eval(string script, int? tabId = null, int frameId = 0, int timeoutMs = 30000)` | 在页面中执行 JS 函数体并返回 JSON 结果 | 【高风险】外部脚本 | +| `void Act(string target, string action = "click", string? value = null, int? tabId = null, int timeoutMs = 10000)` | 操作页面元素;`target` 支持 `css=`、`text=`、`role=`、`xpath=` 前缀;`action` 见取值表 | 操作浏览器 | +| `bool WaitFor(string? target = null, string? urlPattern = null, int timeoutMs = 10000, int? tabId = null)` | 等待元素出现和/或网址匹配;超时返回 `false` | 操作浏览器 | +| `List> Extract(string template, int? tabId = null, int limit = 500, bool allowEmpty = false, int timeoutMs = 30000)` | 按扩展“列表提取”模板提取当前页的行 | 操作浏览器 | +| `FillResult Fill(string template, IDictionary data, int? tabId = null, bool dryRun = false, string failOn = "required", int timeoutMs = 30000)` | 按表单模板填写;`failOn`:`required`、`any`、`never` | 操作浏览器 | +| `void Upload(string target, IEnumerable files, int? tabId = null, string? urlPattern = null, int timeoutMs = 30000)` | 把本机文件放入页面的文件输入框;建议给出 `urlPattern` | 操作浏览器 + 读取文件 | +| `object? Command(string command, object? arguments = null, int? tabId = null, int timeoutMs = 30000)` | 执行浏览器扩展后台命令并返回结果(如 `Command("api_tabs_query", new { active = true })`);`arguments` 为匿名对象、字典或 JSON 对象文本,`tabId` 为 `page.*` 命令的目标标签页。读写本机文件、调试协议、删除浏览数据等命令被拒绝(`INVALID_ARGUMENT`);Cookie 命令须在 `arguments` 给出 `url`。命令名随扩展版本可能调整 | 【高风险】外部脚本(同 `Eval`) | + +## qk.Apps(外部程序) + +在 Office/WPS/Adobe/CAD 等程序内部执行代码或调用插件命令;启动程序见 `qk.Process`。 + +| 签名 | 说明 | 能力 / 常见错误码 | +|---|---|---| +| `string? Run(string app, string code, bool wait = true, int timeoutMs = 30000, bool launch = false)` | 在 Office/WPS(VBA)、Photoshop 等(ExtendScript)、AutoCAD、Rhino 中执行代码并返回结果文本;停止脚本后已发送的代码仍会执行完 | 【高风险】外部脚本 / `APP_UNAVAILABLE`、`APP_FAILED`、`APP_TIMEOUT` | +| `object? Bridge(string app, string command, object? arguments = null, int timeoutMs = 30000, string? target = null)` | 调用“软件连接”插件命令,返回解码后的数据;需在 设置 → 软件连接 中开启 | 【高风险】外部脚本 / `BRIDGE_*` 系列 | +| `BridgeTarget[] ListTargets(string app)` | 列出软件连接的在线实例 | 读取窗口 / `BRIDGE_DISABLED` | +| `void RunOfficeCommand(string app, string msoId, int timeoutMs = 10000)` | 在已运行的 Office/WPS 中执行功能区命令(如 `"Bold"`) | 【高风险】外部脚本 / `APP_UNAVAILABLE`、`APP_TIMEOUT` | + +`app` 取值见[字符串取值表](#字符串取值表)。`Bridge` 报 `BRIDGE_FAILED` 且 `e.Detail` 为 `RESULT_INVALID` 时,命令可能已在目标软件中执行,请勿盲目重试。 + +## qk.Ai(AI 与翻译) + +调用 AI 模型(问答、提取、分类、看图、对话)与机器翻译;纯计算的文本工具见 `qk.Text`。 + +能力:调用 AI 或翻译服务(会把内容发送到你配置的 AI 或 Quicker 服务器,消耗 AI 额度或 Quicker 点数)。使用你在 Quicker 中的 AI 配置。常见错误码:`AI_FAILED`、`AI_TIMEOUT`(本机已取消请求,本机已取消请求;服务端是否已计费以实际账单为准)。`timeoutMs` 默认 120000(2 分钟),0 为不单独限时;单次调用同样受动作总超时(默认 30 秒)约束,需要较长回答时请同时在编辑器中调大动作超时。 + +| 签名 | 说明 | +|---|---| +| `string Ask(string prompt, string? system = null, string? useCase = null, IEnumerable? history = null, double? temperature = null, int maxTokens = 0, int timeoutMs = 120000)` | 对话,返回回复文本 | +| `JsonNode? Extract(string text, string schema, string? instructions = null, string? useCase = null, int timeoutMs = 120000)` | 按 JSON Schema 从文本提取结构化数据(模型须支持结构化输出) | +| `ClassifyResult Classify(string text, IDictionary categories, string? defaultValue = null, string? instructions = null, int timeoutMs = 120000)` | 把文本归入一个类别 | +| `string AskImage(Img image, string prompt, string? system = null, string? useCase = null, int timeoutMs = 120000)` | 看图回答(模型须支持图片输入) | +| `string Translate(string text, string targetLanguage = "zh", string? sourceLanguage = null, string? vendor = null, int timeoutMs = 120000)` | 机器翻译(经 Quicker 服务器,可能消耗点数);语言只能是 `zh`、`en`、`ja`、`ko`(`sourceLanguage` 为 `null` 时自动检测),其他值报 `INVALID_ARGUMENT`;`TRANSLATE_FAILED`、`TRANSLATE_TIMEOUT` | +| `ChatResult? Chat(string prompt, string? conversationId = null, string? system = null, string? title = null, string? useCase = null, int maxRounds = 10, int maxTokens = 0)` | 打开交互式 AI 对话窗口,用户点“采用”返回结果,关闭返回 `null` | + +## qk.Uia(界面自动化) + +其他程序窗口里的界面元素(查找/读取/操作);Quicker 自己的对话框见 `qk.Ui`,窗口本身见 `qk.Window`。`window` 为 `null` 表示前台窗口。**Quicker 自身的窗口不能读取也不能操作**(`UIA_TARGET_DENIED`)。`El` 只在本次运行内有效(只读属性 `Name`、`ControlType`),要返回或保存时用 `Info`。常见错误码:`UIA_NOT_FOUND`、`UIA_REF_STALE`、`UIA_PATTERN_UNSUPPORTED`、`UIA_TIMEOUT`、`UIA_LIMIT_EXCEEDED`、`UIA_FAILED`、`ELEVATED_TARGET_DENIED`。 + +| 签名 | 说明 | 能力 | +|---|---|---| +| `El? Find(Win? window = null, string? name = null, string? controlType = null, string? automationId = null, string? className = null, string? xpath = null, int timeoutMs = 0)` | 第一个匹配元素,没有为 `null` | 读取界面元素 | +| `El[] FindAll(Win? window = null, string? name = null, string? controlType = null, string? automationId = null, string? className = null, int limit = 64)` | 全部匹配 | 读取界面元素 | +| `El? FromPoint(Pt? point = null)` / `El? GetFocused()` | 屏幕点下 / 拥有焦点的元素 | 读取界面元素 | +| `ElInfo Info(El element)` | 元素信息快照(可返回) | 读取界面元素 | +| `string GetTree(Win? window = null, int depth = 6, bool interactiveOnly = true)` | 元素树 JSON(编写时查定位用;密码框不含值) | 读取界面元素 | +| `void Act(El element, string action = "invoke")` | 操作元素;`action` 见取值表(`click` 另需鼠标) | 操作界面元素 | +| `void SetValue(El element, string value)` | 设置元素的值 | 操作界面元素 | +| `void ClickMenu(string path, Win? window = null)` | 按路径点击菜单,如 `"文件/另存为"` | 操作界面元素 | +| `void SetDialogPath(string path, bool createDirectory = false, bool pressEnter = false)` | 把路径填入打开/另存为对话框(资源管理器窗口见 `qk.Files.SetExplorerPath`);`pressEnter: true` 时**程序会立即保存或打开该文件** | 操作界面元素(`createDirectory` 另需修改文件) | + +## qk.Quicker(Quicker 服务) + +Quicker 自身与账号服务(信息、命令、云端数据、临时分享、账号绑定加解密);调用其他动作见 `qk.Actions`。 + +| 签名 | 说明 | 能力 / 常见错误码 | +|---|---|---| +| `QuickerInfo Info()` | Quicker 版本、专业版状态、用户标识(`UnionId`)、暂停状态、主题、运行时长 | 读取本机信息 | +| `void Command(string command, string? argument = null)` | 执行 Quicker 命令(见取值表),发出即返回 | `togglePause`/`stopAll`/`loadProfile`/`restart` 需控制 Quicker,`runLast` 需调用动作 / `QUICKER_COMMAND_FAILED` | +| `string? GetCloud(string key)` | 读取账号云端数据(所有动作、所有设备共享);不存在为 `null` | 网络 + 云端数据 / `CLOUD_FAILED` | +| `void SetCloud(string key, string value)` | 写入云端数据;多设备同时写以最后一次为准;超时或停止后请求可能已生效 | 同上 | +| `void RemoveCloud(string key)` | 删除云端数据(不存在不报错) | 同上 | +| `string ShareTemporary(object content)` | 上传文本或 `Img` 到临时分享并返回网址(**任何人可访问**) | 网络 + 临时分享 / `TEMP_SHARE_FAILED` | +| `string ShareTemporaryFile(string path, bool randomName = false)` | 上传本机文件(最多 10 MB)到临时分享 | 网络 + 临时分享 + 读取文件 | +| `string EncryptLocal(string text)` | “自用加密”,与“加密”步骤本机模式互通;**不能用来防范攻击者** | 无 / `ACCESS_DENIED`(未登录) | +| `string DecryptLocal(string cipherText)` | 解密“自用加密”数据 | 自用解密 / `ACCESS_DENIED` | + +## qk.Sys(系统) + +Windows 与本机(系统信息、环境变量与注册表读取、提示音、朗读、音量与音频设备、显示器亮度、深色模式、电源操作);Quicker 自身的信息见 `qk.Quicker`。 + +| 签名 | 说明 | 能力 / 常见错误码 | +|---|---|---| +| `SysInfo Info()` | 计算机名、用户名、系统版本、锁屏/全屏/深色模式/联网状态、局域网 IP、开机时长、电池(是否用电池、剩余电量) | 读取本机信息 | +| `void PlaySound(string sound, bool wait = false)` | 播放内置声音(见取值表)、音频文件或网址;`wait: false` 后台播放 | 无(文件需读取文件,网址需网络)/ `SOUND_FAILED` | +| `void Speak(string text, bool wait = false)` | 用 Windows 默认语音朗读 | 无 / `SOUND_FAILED` | +| `VolumeInfo GetVolume(string device = "output")` | 默认输出设备(`device: "input"` 为默认录音设备)的音量(0–100)与静音 | 无 | +| `VolumeInfo SetVolume(int? level = null, bool? muted = null, string device = "output")` | 设置音量/静音(`null` 保持不变),返回调用后状态;`device: "input"` 可让麦克风静音;停止后的 `finally` 中可用于恢复 | 无 | +| `AudioDevice[] ListAudioDevices(string device = "output")` | 已启用的播放(或录音)设备,标出当前默认设备 | 无 / `SOUND_FAILED` | +| `void SetDefaultAudioDevice(string idOrName, string device = "output")` | 切换默认音频设备;按设备 Id、完整名称或名称中的一段匹配 | 无 / `AUDIO_DEVICE_NOT_FOUND`、`SOUND_FAILED` | +| `int GetBrightness(string screen = "mouse")` | 显示器亮度 0–100;`screen`:`mouse`(鼠标所在屏)、`primary`、`all`(同 `primary`)或 `qk.Screen.List()` 的 `Id` | 无 / `BRIGHTNESS_UNSUPPORTED`、`BRIGHTNESS_FAILED` | +| `int SetBrightness(int? level = null, int delta = 0, string screen = "mouse", bool osd = false)` | 设置亮度:`level` 为目标值,或 `delta` 相对调整(二者给一个);返回设置后的亮度;`osd: true` 显示亮度提示 | 无 / 同上 | +| `void SetDarkMode(bool dark, string scope = "all")` | 切换深色/浅色模式;`scope`:`apps`、`system`、`all` | 无 | +| `void Power(string action)` | `lock` 锁屏、`screenOff` 关闭显示器;`sleep` 睡眠、`hibernate` 休眠、`signOut` 注销、`shutdown` 关机、`restart` 重启。`screenOff` 由鼠标或按键触发时,随后的鼠标移动或松键可能立即点亮屏幕;睡眠期间停止与超时不生效,唤醒后若已超过动作超时,后续代码不再执行 | `lock`/`screenOff` 无;其余为**电源/会话(后果严重)** / `POWER_FAILED` | +| `string? GetEnv(string name)` | 读取环境变量;不存在为 `null` | 读取本机信息 | +| `object? GetRegistry(string path, string? name = null)` | 读取注册表值(只读),如 `GetRegistry(@"HKCU\Software\…", "名称")`;不存在为 `null` | 读取本机信息 / `ACCESS_DENIED` | + +## qk.Steps(组合动作步骤) + +用于调用**还没有对应 `qk` 方法**的组合动作(XAction)步骤,如 Windows 服务/注册表、PDF、Excel 区域、部分软件控制。有对应 `qk` 方法时请优先用 `qk`(确认更清楚、检查更完善)。步骤键、字段键随步骤演进可能调整,届时旧脚本可能在运行时报错。 + +| 签名 | 说明 | 能力 / 常见错误码 | +|---|---|---| +| `IReadOnlyDictionary Run(string key, object? inputs = null, int timeoutMs = 0)` | 执行一个步骤并返回其输出(键为输出参数 key)。`key` 如 `"sys:winservice"`(可省略 `sys:`),须写成字符串字面量;`inputs` 为匿名对象或字典,键为输入参数 key,值按字面传入(`$=`、`{变量}` 不求值);`timeoutMs` 0 为只受动作超时约束 | 【高风险】调用组合动作步骤(按步骤逐个确认,可运行任意程序的步骤单独警示)/ `STEP_FAILED`(`e.Detail` 为步骤的错误码)、`STEP_TIMEOUT`;未经审计或不允许的步骤报 `INVALID_ARGUMENT` | + +--- + +## 数据类型 + +`qk` 提供的数据类型可以直接构造(推荐具名参数,如 `new Item("甲", Value: "1")`)和读取。返回或写入状态时字段名转为小驼峰(如 `Width` → `width`)。 + +| 类型 | 字段 | +|---|---| +| `Pt` | `(int X, int Y)` | +| `Rect` | `(int X, int Y, int Width, int Height)` | +| `Item` | `(string Title, string? Value = null, string? Icon = null, string? Description = null, Item[]? Children = null)`;`Title`/`Value` 原样使用,任意文本(路径、网址)请用 `Item` 而不是字符串简写 | +| `UiOptions` | `(string? Title, string? Help, string Position = "mouse", Rect? Bounds, bool Topmost = true, bool RestoreFocus = true, bool CloseOnBlur, bool NoFocus, int FontSize, string? Font, string? Ime, int AutoCloseMs = 0, string? Key)`;`Position`、`Ime` 取值见取值表 | +| `Field` | `(string Key, string Label, string Kind = "text", object? Value, string? Options, bool Required, string? Help, string? Group, string? Visible, string? Pattern, double? Min, double? Max, bool ReadOnly, int Width)`;`Kind` 见取值表;`Visible` 为简单条件,如 `"kind == 'md' \|\| vip"` | +| `FormResult` | `(IReadOnlyDictionary Values, string Button, string? Group)`;`number`/`slider` 为 `double`,`check` 为 `bool`,`multi` 为 `List`,其余多为 `string` | +| `SelectResult` | `(string? Value, int Index, string Title, object? Item, string? Operation, string Filter)` | +| `SelectManyResult` | `(string[] Values, int[] Indexes, string? Operation, string Filter)` | +| `TextResult` | `(string Text, string SelectedText, int Caret, string? Operation)` | +| `WinInfo` | `(string Title, string Process, string Path, int Pid, string ClassName, Rect Bounds, int Dpi, bool Visible, string State, bool Topmost, Win Window)`;`State` 为 `normal`/`minimized`/`maximized` | +| `PathInfo` | `(string Path, string Name, bool IsDirectory, long? Length, DateTimeOffset ModifiedAt)` | +| `ProcInfo` | `(int Pid, string Name, string Path, DateTimeOffset? StartedAt)` | +| `ProcResult` | `(int ExitCode, string StandardOutput, string StandardError)` | +| `ActionInfo` | `(string Id, string Title, string Icon, string Description, string? SharedId, int? SharedRevision)` | +| `Ctx` | 见[根成员](#根成员) | +| `Win`、`Img` | 句柄,见对应域 | +| `HttpResult` | `(int StatusCode, string Text, IReadOnlyDictionary Headers, string[] SetCookies)`,另有 `bool IsSuccess`(2xx) | +| `CaptureResult` | `(Img? Image, Rect Area, string? Text, string? Path)` | +| `ScreenInfo` | `(Rect Bounds, Rect WorkArea, int Dpi, bool Primary, string? Id, string? Name)`:`Id` 为显示器设备标识,`Name` 为显示器名称 | +| `Hit` | `(Rect Bounds, Pt Center, double Score)` | +| `OcrResult` / `OcrLine` | `(string Text, OcrLine[] Lines, string LayoutText)` / `(string Text, Rect Bounds, double Confidence, Pt[] Polygon, int LineIndex, int ParagraphIndex, int ColumnIndex)`:`Polygon` 为四点框(左上、右上、右下、左下),序号从 0 起,`ColumnIndex` 为 -1 表示跨栏 | +| `OcrTableResult` / `OcrCell` | `(string Tsv, string Html, int Rows, int Columns, OcrCell[] Cells, double Confidence, int HeaderRows)` / `(int Row, int Column, int RowSpan, int ColumnSpan, string Text, Rect Bounds, double Confidence, Pt[] Polygon)`:行列从 0 起 | +| `Tab` | `(int Id, int WindowId, string Url, string Title, bool Active)` | +| `FillResult` / `FillField` | `(bool AllRequiredOk, int FilledCount, int FailedCount, bool NeedsCheck, string[] Warnings, FillField[] Fields)` / `(string Key, string Label, bool Required, string Status, string? Code, string? Message)` | +| `BridgeTarget` | `(string Id, int? Pid, string Title, string Document, string HostVersion, string BridgeVersion, string? Kind, DateTimeOffset? ConnectedAt)` | +| `ChatMessage` | `(string Role, string Text)`;`Role` 为 `user`/`assistant` | +| `ChatResult` | `(string Answer, string ConversationId, int Rounds)` | +| `ClassifyResult` | `(string Key, string Reason, bool UsedDefault)` | +| `ElInfo` | `(string Name, string ControlType, string AutomationId, string ClassName, string? Value, Rect Bounds, bool Enabled, bool Visible, string XPath, bool? Toggled)` | +| `QuickerInfo` | `(string Version, bool IsPro, string? UnionId, bool Paused, string Theme, long UptimeMs)`;`UnionId` 是标识当前 Quicker 用户的不透明字符串,与组合动作“获取 Quicker 信息”的 UnionId 相同;不是账号 Id,不能用来登录或反查账号;未登录为 `null` | +| `SysInfo` | `(string MachineName, string UserName, string OsVersion, bool Locked, bool Fullscreen, bool DarkMode, bool Online, string? LanIp, long UptimeMs, bool? OnBattery, int? BatteryPercent)`:没有电池时后两项为 `null` | +| `VolumeInfo` | `(int Level, bool Muted)` | +| `AudioDevice` | `(string Id, string Name, bool IsDefault)` | +| `El`、`TextWin`、`ProgressWin` | 句柄,见对应域 | + +## 每次运行的限额 + +限额用于防止脚本失控,具体数值可能放宽。超出时一般报 `LIMIT_EXCEEDED` 或所在域的 `*_LIMIT_EXCEEDED`。 + +| 范围 | 限额 | +|---|---| +| 对话框 / 通知 | 对话框 100 个;通知 100 条,另限每秒 5 条 | +| 选区读取 | 16 次;`GetFiles` 最多 256 项 | +| 窗口 | 观察 64 次、激活 16 次、调整 32 次;`FindAll`/`ListChildren` 每次最多 64 个 | +| 键盘 | `Type` 累计 2000 字符、键盘调用累计 256 次;**超出后本次运行的键鼠输入全部失败** | +| 鼠标 | 点击累计 100 次,滚轮累计 120 格 | +| 剪贴板 | 64 次操作,其中 32 次写入;文本单次 1 MiB | +| 文件 | 单次读写 16 MiB;列表 10,000 项;`Reveal` 20 次 | +| 界面自动化 | 读取 400 次、操作 100 次、累计耗时 120 秒 | +| HTTP | 请求与响应正文各 4 MiB;`Download` 512 MiB | +| 进程 | `Run` 输出各 512K 字符 | +| 截图与识别 | 屏幕截图 1000 次(本机 OCR 不限次数);交互截图 64 次;图片同时存活 32 个 | +| 声音 | `PlaySound` + `Speak` 合计 100 次;整个 Quicker 同时最多 4 个后台播放 | +| 云端数据 / 临时分享 | 云端读写 200 次、临时分享 20 次 | +| 动作调用 | 调用链 16 层 | +| 返回值 / 状态 | 1 MiB、10,000 项、32 层 | + +## 错误码表 + +通过 `e.Code.Value`(字符串)或 `e.Code == ActionErrorCode.Xxx`(静态成员为错误码的帕斯卡写法,如 `FILE_NOT_FOUND` → `FileNotFound`)判断。`e.Detail` 为扩展码,可能为 `null`。 + +### 通用 + +| 错误码 | 含义 | 常见处理 | +|---|---|---| +| `INVALID_ARGUMENT` | 参数不合法(取值不在允许范围、格式错误、在不支持的场景调用等) | 按消息改正参数 | +| `CAPABILITY_DENIED` | 未获得该能力(用户拒绝授权、源码中无法识别该调用),或在停止后的清理阶段调用了不允许的方法 | 见[安全与授权](./security);直接写 `qk.域.方法(...)` | +| `ACCESS_DENIED` | 无权访问(文件权限、安装来源的脚本访问其他动作、未登录等) | 检查权限或登录状态 | +| `LIMIT_EXCEEDED` | 超出大小或次数限额(截图、找图找色、OCR、二维码与图片处理的限额也报此码) | 减少调用次数或数据量 | +| `HOST_UNAVAILABLE` | 当前运行环境没有提供该服务(不是授权问题) | 在正常的 Quicker 中运行 | +| `HOST_FAILED` | Quicker 内部失败且无法归类(原内部码在 `e.Detail`) | 正常不应出现,遇到请反馈 | +| `UI_THREAD_NOT_ALLOWED` | 在不允许的线程上同步等待 | 正常不应出现,遇到请反馈 | + +### 入口、返回值与状态 + +| 错误码 | 含义 | 常见处理 | +|---|---|---| +| `INPUT_MISSING` | 缺少必填的 `Main` 参数(OperationId 为 `input`) | 给参数加默认值,或从交互方式触发 | +| `CODEC_UNSUPPORTED` | 返回值或状态中含不能保存的数据(如 `Win`、`Img`) | 改为返回 `Info(...)`、Base64 等数据 | +| `CODEC_VALUE_INVALID` | 读回的数据与声明的类型不符 | 保持写入与读取类型一致 | +| `STATE_UNAVAILABLE` | 状态存储读写失败或不可用 | 重试;持续出现请反馈 | + +### 界面 + +| 错误码 | 含义 | 常见处理 | +|---|---|---| +| `UI_UNAVAILABLE` | 当前环境无法显示界面 | 在交互桌面中运行 | +| `DIALOG_LIMIT_EXCEEDED` | 对话框数量超出限额 | 减少弹窗 | +| `NOTIFY_LIMIT_EXCEEDED` | 通知数量或频率超限 | 用 `key` + `duplicate` 合并通知 | +| `NOTIFY_FAILED` | 通知发布失败(`qk.Ui.Notify` 调用系统通知/Quicker 通知模块时出错) | 重试;检查系统通知设置 | +| `UI_REACT_FAILED` | React 界面编译或运行失败 | 按消息中的页面错误修改 | + +### 动作调用 + +| 错误码 | 含义 | 常见处理 | +|---|---|---| +| `CALL_FAILED` | 被调用的动作或子程序失败;被调脚本的错误码在 `e.Detail` | 查看 `e.Detail` 与消息 | +| `SUBPROGRAM_NOT_FOUND` | 找不到公共子程序 | 检查名称或 Id | +| `CALL_DEPTH_LIMIT_EXCEEDED` | 调用链超过 16 层 | 检查是否互相递归调用 | + +### 剪贴板与选区 + +| 错误码 | 含义 | 常见处理 | +|---|---|---| +| `CLIPBOARD_UNAVAILABLE` | 剪贴板被其他程序占用,或图片数据无法读取 | 稍后重试 | +| `CLIPBOARD_LIMIT_EXCEEDED` | 剪贴板操作次数超限 | 减少读写次数 | +| `SELECTION_UNAVAILABLE` | 前台程序不支持读取选中内容 | 改用剪贴板或其他方式 | +| `SELECTION_FAILED` | 读取选中内容失败(复制期间剪贴板被改写等,内部码在 `e.Detail`) | 重试;在编辑器中测试请用“最小化后延迟运行” | +| `EXPLORER_NOT_FOUND` | `qk.Files.SetExplorerPath` 的目标不是资源管理器 | 改用 `qk.Process.Open(directory)` 新开窗口 | + +### 键盘与鼠标 + +| 错误码 | 含义 | 常见处理 | +|---|---|---| +| `INPUT_LIMIT_EXCEEDED` | 键鼠输入超出限额;之后本次运行的键鼠输入全部失败 | 事先计算总量;长文本用 `Paste` | +| `INPUT_USER_ACTIVE` | 用户正在操作键鼠,注入被中止 | 运行时不要同时操作键鼠 | +| `INPUT_UNAVAILABLE` | 键鼠输入当前不可用(如不在交互桌面) | — | +| `INPUT_FAILED` | 注入失败(内部码在 `e.Detail`) | 重试 | + +### 窗口 + +| 错误码 | 含义 | 常见处理 | +|---|---|---| +| `WINDOW_NOT_FOUND` | 找不到窗口 | 检查匹配条件 | +| `WINDOW_REF_STALE` | 窗口已关闭或句柄已被复用 | 重新查找窗口 | +| `WINDOW_REF_INVALID` | `Win` 不属于本次运行 | 不要跨运行保存 `Win` | +| `WINDOW_UNAVAILABLE` | 窗口隐藏、最小化或无法校验,不能用于该操作 | 先还原或激活窗口 | +| `WINDOW_CHANGED` | 操作期间窗口被移动、改变大小或遮挡 | 重试 | +| `WINDOW_LIMIT_EXCEEDED` | 窗口操作次数或枚举数量超限 | 用 `FindAllInfo` 批量读取 | +| `WINDOW_ACTIVATION_FAILED` | Windows 拒绝了前台切换 | 重试,或先 `RestoreForeground` | +| `ELEVATED_TARGET_DENIED` | 目标是管理员权限的程序 | 普通权限下无法操作 | +| `TARGET_PERMISSION_UNKNOWN` | 无法确定目标窗口权限,按拒绝处理 | — | +| `WINDOW_ARRANGE_FAILED` | 排列或同类窗口操作失败 | — | +| `WINDOW_EDGE_HIDE_FAILED` | 窗口不适合贴边隐藏(最小化、全屏、工具窗口) | — | +| `WINDOW_MESSAGE_TIMEOUT` | 发送窗口消息超时,目标无响应 | 调大 `timeoutMs` | +| `WINDOW_MESSAGE_FAILED` | 窗口消息发送失败(窗口已关闭或被拒绝) | — | +| `WINDOW_WAIT_FAILED` | 等待窗口时出错 | — | +| `WINDOW_FAILED` | 其他窗口操作失败 | — | + +### 文件 + +| 错误码 | 含义 | 常见处理 | +|---|---|---| +| `FILE_NOT_FOUND` | 文件或文件夹不存在 | 先 `Exists` 检查 | +| `FILE_FAILED` | 文件操作失败(目标已存在、被占用、不能进回收站等) | 需要覆盖时传 `overwrite: true` | +| `ZIP_FAILED` | 压缩/解压失败(损坏、加密、条目路径不安全) | — | +| `EVERYTHING_UNAVAILABLE` / `EVERYTHING_FAILED` | Everything 未运行 / 搜索失败 | 启动 Everything;检查查询语法 | + +### 网络与进程 + +| 错误码 | 含义 | 常见处理 | +|---|---|---| +| `HTTP_FAILED` | 网络失败、重定向过多 | 检查网络与代理 | +| `HTTP_STATUS_FAILED` | 服务器返回非 2xx | 需要读取错误响应时改用 `Send` | +| `HTTP_TIMEOUT` | 请求超时(已取消,但服务端可能已处理) | 非幂等请求不要盲目重试 | +| `PROCESS_FAILED` | 启动程序失败 | 检查路径与参数 | +| `PROCESS_TIMEOUT` | `Process.Run` 超时,已读输出不返回;是否结束进程目前不保证 | 调大 `timeoutMs` | + +### 图片、截屏与识别 + +| 错误码 | 含义 | 常见处理 | +|---|---|---| +| `IMAGE_DECODE_FAILED` / `IMAGE_ENCODE_FAILED` | 图片无法解码 / 编码失败 | 检查格式(不支持 webp) | +| `IMAGE_REF_DISPOSED` / `IMAGE_REF_INVALID` | 图片已释放 / 不属于本次运行 | — | +| `SECURE_DESKTOP` | 安全桌面(UAC、锁屏)无法截屏 | — | +| `SCREEN_CAPTURE_FAILED` / `SCREEN_CAPTURE_UNAVAILABLE` / `CAPTURE_UNAVAILABLE` | 屏幕截取失败 / 不可用 | 重试 | +| `WINDOW_CAPTURE_FAILED` | 截窗口失败 | 换用 `background` 另一种模式 | +| `CAPTURE_BUSY` | 另一截图或选择正在进行 | 稍后重试 | +| `CAPTURE_PRO_FAILED` | 截图 Pro 未能完成 | — | +| `OCR_UNAVAILABLE` / `OCR_TIMEOUT` / `OCR_FAILED` | 本机 OCR 未安装 / 超时(`timeoutMs`,默认 30 秒)/ 失败 | 按消息提示安装本机 OCR(截图后使用一次“文字识别”);调大 `timeoutMs` 或缩小区域 | +| `QR_UNAVAILABLE` / `QR_FAILED` | `qk.Vision.ReadQr`:本机二维码解码组件无法加载 / 解码出错 | — | + +### 界面自动化 + +| 错误码 | 含义 | +|---|---| +| `UIA_TARGET_DENIED` | 目标是 Quicker 自身窗口 | +| `UIA_NOT_FOUND` | 菜单项、文件对话框等目标未找到 | +| `UIA_REF_STALE` / `UIA_REF_INVALID` | 元素已消失 / 不属于本次运行 | +| `UIA_PATTERN_UNSUPPORTED` | 元素不支持该操作 | +| `UIA_TIMEOUT` | 目标程序无响应 | +| `UIA_LIMIT_EXCEEDED` | 界面自动化次数、元素数或耗时超限 | +| `UIA_FAILED` | 其他失败 | + +### 浏览器、外部程序与 AI + +| 错误码 | 含义 | +|---|---| +| `BROWSER_UNAVAILABLE` | 没有已连接扩展的浏览器 | +| `BROWSER_FAILED` | 扩展返回失败;扩展错误码在 `e.Detail`(如 `URL_PATTERN_MISMATCH`、`LIST_ROWS_NOT_FOUND`) | +| `BROWSER_TIMEOUT` | 扩展超时无响应(页面上的操作可能仍在进行) | +| `APP_UNAVAILABLE` / `APP_FAILED` / `APP_TIMEOUT` | 目标程序未安装或未运行 / 程序返回错误 / 超时(代码可能仍在目标程序中运行) | +| `BRIDGE_DISABLED` | 软件连接未开启 | +| `BRIDGE_TARGET_NOT_FOUND` / `BRIDGE_TARGET_AMBIGUOUS` | 指定实例不在线 / 有多个实例(用 `ListTargets` 指定 `target`) | +| `BRIDGE_TIMEOUT` / `BRIDGE_BUSY` | 超时 / 软件连接正在切换,稍后重试 | +| `BRIDGE_FAILED` | 插件返回失败;插件错误码在 `e.Detail` | +| `AI_FAILED` / `AI_TIMEOUT` | AI 未配置或模型错误 / 超时 | +| `TRANSLATE_FAILED` / `TRANSLATE_TIMEOUT` | 翻译失败 / 超时 | + +### Quicker 服务与声音 + +| 错误码 | 含义 | +|---|---| +| `QUICKER_COMMAND_FAILED` | Quicker 命令执行失败 | +| `CLOUD_FAILED` | 云端数据读写失败(未登录、网络、每日次数用尽等) | +| `TEMP_SHARE_FAILED` | 临时分享上传失败(网络、上传间隔限制等) | +| `SOUND_FAILED` | 播放或朗读失败(无法解码、没有音频设备);读写音量、列出或切换音频设备失败 | +| `AUDIO_DEVICE_NOT_FOUND` | `Sys.SetDefaultAudioDevice` 找不到匹配的音频设备(消息列出可用设备) | +| `BRIGHTNESS_UNSUPPORTED` | 显示器不支持调节亮度(外接显示器未开启 DDC/CI、远程桌面、虚拟显示器等) | +| `BRIGHTNESS_FAILED` | 读取或调节亮度失败(屏幕未连接等) | +| `POWER_FAILED` | `Sys.Power` 失败(如未启用休眠、系统拒绝) | + +### 组合动作步骤 + +| 错误码 | 含义 | +|---|---| +| `STEP_FAILED` / `STEP_TIMEOUT` | `qk.Steps.Run` 调用的组合动作步骤失败 / 超时(见 [qk.Steps](#qksteps组合动作步骤)) | + +## 字符串取值表 + +取值比较不区分大小写,文档与返回值使用下表写法。标“…”的集合还接受其他值(见说明)。 + +| 位置 | 取值 | +|---|---| +| `Log(level)` | `debug`、`info`、`warn`、`error` | +| `UiOptions.Position` | `mouse`、`mouse2`、`center`、`topLeft`、`topCenter`、`topRight`、`leftCenter`、`rightCenter`、`bottomLeft`、`bottomCenter`、`bottomRight`、`last` | +| `UiOptions.Ime` | `on`、`off` | +| `Ui.Notify(kind)` | `info`、`success`、`warn`、`error`、`toast` | +| `Ui.Notify(position)` | `bottomCenter`、`bottomLeft`、`bottomRight`、`topCenter`、`topLeft`、`topRight` | +| `Ui.Notify(duplicate)` | `replace`、`count`、`ignore` | +| `Ui.Alert/Confirm/Ask(icon)` | `none`、`info`、`question`、`warn`、`error` | +| `Field.Kind` | `text`、`multiline`、`number`、`slider`、`check`、`dropdown`、`combo`、`autocomplete`、`multi`、`radio`、`date`、`dateTime`、`color`、`password`、`font`、`dict`、`label`、`separator` | +| `Ui.Pin(kind)` | `auto`、`image`、`text`、`html`、`latex` | +| `Window.SetState(state)`、`WinInfo.State` | `normal`、`minimized`、`maximized` | +| `Window.WaitFor(state)` | `exists`、`visible`、`foreground` | +| `Window.ToScreen(anchor)` | `topLeft`、`topRight`、`bottomLeft`、`bottomRight`、`center` | +| `Window.SetEdgeHide(edge)` | `auto`、`left`、`top`、`right`、`bottom` | +| `Mouse.Click/Down/Up(button)` | `left`、`right`、`middle` | +| `Mouse.GetCursor()` 返回值 | `arrow`、`iBeam`、`hand`、`wait`、`appStarting`、`cross`、`no`、`help`、`sizeAll`、`sizeNs`、`sizeWe`、`sizeNwse`、`sizeNesw`、`upArrow`、`hidden`、`unknown` | +| `Selection.GetText(format)` | `text`、`html`、`rtf`、`csv` | +| `Clipboard.Get/Set(format)` | `rtf`、`csv`、`html`、`text`,或自定义格式名 | +| `Files.GetKnownFolder(name)` | `desktop`、`documents`、`pictures`、`music`、`videos`、`appData`、`localAppData`、`programData`、`userProfile`、`startup`、`downloads`、`temp`,或 .NET `Environment.SpecialFolder` 名称 | +| `Files.Search(sort)` | `name`、`path`、`size`、`extension`、`created`、`modified` | +| `Files.Hash`、`Text.Hash` 的 `algorithm` | `md5`、`sha1`、`sha256`、`sha384`、`sha512` | +| `Files.Hash`、`Text.Hash` 的 `output` | `hex`、`base64` | +| `Img.ToBytes/ToBase64(format)` | `png`、`jpg`、`bmp` | +| `Screen.Capture(screen)` | `all`、`primary`、`mouse` | +| `Screen.CapturePro(mode)` | `capture`、`captureNow`、`copy`、`pin`、`ocr`、`ocrCopy`、`table`、`formula`、`translate`、`imageTranslate`、`quickSave` | +| `Vision.Ocr(model)`、`Vision.FindText(model)` | `tiny`、`small` | +| `Browser.Open(browser)` | `default`、`edge`、`chrome`、`current`、`edgeApp`、`edgeIncognito`、`chromeApp`、`chromeIncognito`,或浏览器 exe 完整路径 | +| `Browser.Act(action)` | `click`、`fill`、`type`、`paste`、`clear`、`select`、`check`、`uncheck`、`hover`、`scroll` | +| `Browser.Fill(failOn)` | `required`、`any`、`never` | +| `FillField.Status` | `filled`、`unchanged`、`matched`、`failed`、`skipped` | +| `Apps.Run(app)` | `word`、`excel`、`ppt`、`wps`、`et`、`wpp`、`visio`、`wordOrWps`、`excelOrEt`、`pptOrWpp`、`photoshop`、`illustrator`、`indesign`、`aftereffects`、`autocad`、`rhino` 等 | +| `Apps.RunOfficeCommand(app)` | `word`、`excel`、`ppt`、`wps`、`et`、`wpp`、`visio`、`wordOrWps`、`excelOrEt`、`pptOrWpp` | +| `BridgeTarget.Kind` | `wps`、`et`、`wpp` | +| `Ai.Translate` 的语言 | `zh`、`en`、`ja`、`ko` 等 | +| `ChatMessage.Role` | `user`、`assistant` | +| `Uia.Act(action)` | `invoke`、`click`、`toggle`、`check`、`uncheck`、`expand`、`collapse`、`select`、`focus`、`scroll` | +| `Uia.Find(controlType)` | UIA 控件类型名,如 `Button`、`Edit`、`CheckBox`、`ComboBox`、`ListItem`、`MenuItem` | +| `Quicker.Command(command)` | `showPanel`、`showSearch`、`showCircleMenu`、`showToolbar`、`editAction`、`editSubprogram`、`runLast`、`togglePause`、`stopAll`、`loadProfile`、`restart` | +| `QuickerInfo.Theme` | `light`、`dark`、`autoLight`、`autoDark` | +| `Text.QueryHtml(output)` | `text`、`innerHtml`、`outerHtml` | +| `Sys.PlaySound(sound)` 内置音 | `info`、`snip`、`succeed`、`warning`、`wrong`、`dim`,或音频文件路径、网址 | +| `Sys.Power(action)` | `lock`、`screenOff`、`sleep`、`hibernate`、`signOut`、`shutdown`、`restart` | +| `Sys.GetBrightness/SetBrightness(screen)` | `mouse`、`primary`、`all`,或 `qk.Screen.List()` 的 `Id` | +| `Sys.GetVolume/SetVolume/ListAudioDevices/SetDefaultAudioDevice(device)` | `output`、`input` | +| `Sys.SetDarkMode(scope)` | `apps`、`system`、`all` | +| `Ctx.Trigger` | `panel`、`floatButton`、`floatPanel`、`dashboard`、`editor`、`circleMenu`、`search`、`searchInput`、`searchCallback`、`searchContextMenu`、`hotkey`、`hotkeyWatcher`、`mouse`、`leftButtonPlus`、`scrollOnButton`、`advancedMouseAction`、`mobileApp`、`external`、`androidRemote`、`event`、`screenshot`、`textToolbar`、`triggerKey`、`gesture`、`textCommand`、`autoRun`、`contextMenu`、`association`、`browserContextMenu`、`webpageButton`、`other` | + + +## 相关链接 + + diff --git a/docs/v2/features/script-actions/index.md b/docs/v2/features/script-actions/index.md new file mode 100644 index 0000000..a946617 --- /dev/null +++ b/docs/v2/features/script-actions/index.md @@ -0,0 +1,406 @@ +--- +title: 脚本动作 +description: 用 C# 语法编写 Quicker 动作,通过 qk 处理文本、文件、网络、窗口与键鼠,并调用其他动作与公共子程序。 +sidebar_position: 1 +quickerDocKey: v2/features/script-actions +comments: true +--- + +# 脚本动作 + +**2.3.0 或更高版本**可用。新建动作时选择 **Quicker脚本动作**,即可打开脚本编辑器。无需安装 Visual Studio 或 .NET SDK,也不必设置环境变量。 + +脚本动作用 **C# 语法**编写:`Main` 的参数是输入,`return` 的值是输出;读取选中文本、弹对话框、操作窗口、读写文件、访问网络等,通过名为 `qk` 的对象调用(例如 `qk.Selection.GetText()`、`qk.Ui.Notify("完成")`)。 + +相关说明:[安全与授权](./security)、[API 参考](./api)。编写时也可在编辑器里输入 `qk.` 查看补全与中文说明,或按 `Ctrl+J` 打开 AI 助手。 + +:::warning[真实执行、无回滚] +在编辑器中运行脚本就是真实执行。窗口、剪贴板、键鼠、文件、网络等操作都会真正发生,不会自动撤销。测试会删除或覆盖文件的脚本时请格外小心。 +::: + +## 什么是脚本动作 + +```csharp +string Main(string quicker_in_param = "") +{ + qk.Ui.Notify("你好,脚本动作!"); + return "完成"; +} +``` + +几个要点: + +- 脚本由 Quicker 内置解释器在后台执行,**不需要**安装 Visual Studio 或 .NET SDK,也不会生成 exe。 +- 支持大部分常用 C# 写法:变量、`if`/`switch`、循环、LINQ、字符串插值、模式匹配、`try/catch/finally`、局部函数、元组、匿名类型、`List`/`Dictionary`、`Regex`、`JsonNode` 等。 +- **不支持**:`async`/`await`、`yield`、`lock`、`goto`、声明 `class`/`record`/`struct`/`enum`/命名空间、泛型局部函数等。需要自定义数据结构时,用元组、匿名类型或 `Dictionary`。 +- **系统能力只能通过 `qk` 使用**:`File`、`Directory`、`HttpClient`、`Process`、`Thread`、`Console`、`MessageBox`、反射等不可用。对应写法见下表。 + +| 习惯写法 | 在脚本动作中改用 | +|---|---| +| `File.ReadAllText(path)` | `qk.Files.ReadText(path)` | +| `File.WriteAllText(path, text)` | `qk.Files.WriteText(path, text, overwrite: true)` | +| `new HttpClient()` | `qk.Http.GetText(url)` / `qk.Http.PostJson(url, json)` / `qk.Http.Send(...)` | +| `Process.Start(...)` | `qk.Process.Start(...)` / `qk.Process.Open(...)` / `qk.Process.Run(...)` | +| `Thread.Sleep(ms)` | `qk.Wait(ms)` | +| `MessageBox.Show(...)` | `qk.Ui.Alert(...)` / `qk.Ui.Confirm(...)` | +| `Console.WriteLine(...)` | `qk.Log(...)` | + +`qk` 的全部成员见 [API 参考](./api)。 + +### 适合做什么 + +- **文本处理**:读取选中文本或剪贴板,清洗、转换、统计后粘贴回去或复制出来。 +- **有条件分支、循环较多的逻辑**:用几行 C# 代替组合动作里层层嵌套的「如果 / 循环」步骤。 +- **数据处理**:JSON 解析、正则提取、表格 / 列表整理、哈希、拼音匹配等纯计算。 +- **窗口与键鼠自动化**:查找 / 激活 / 排列窗口,按键、输入、点击。 +- **文件与网络**:批量改名、压缩解压、下载文件、调用 Web API。 +- **与用户交互**:选择列表、输入框、表单、文本窗口、进度窗口、通知。 + +### 不适合做什么 + +- **需要大量现成步骤能力、但脚本还没有对应 `qk` 方法的场景**:优先用组合动作,或把相关步骤做成**公共子程序**,在脚本里用 `qk.Actions.CallSubprogram` 调用。 +- **需要自定义窗口 / 面板设计器、长期常驻监控**:组合动作的自定义窗口、文件监视等能力脚本暂不提供。脚本每次运行有运行时限和次数限额(见 [API 参考·每次运行的限额](./api#每次运行的限额))。 +- **想调用任意 .NET 库、执行任意系统命令而不经确认**:脚本运行在受限环境中,这是有意的设计(见 [安全与授权](./security))。 + +### 与组合动作的关系 + +| | 组合动作 | 脚本动作 | +|---|---|---| +| 编写方式 | 图形化拼接步骤 | 编写 C# 源码 | +| 输入 | 动作参数 / 变量 | `Main` 的参数(自动生成参数表单) | +| 输出 | 返回值变量 | `Main` 的 `return` | +| 右键菜单 | 自定义菜单 | `[ActionMenu]` 菜单方法 | +| 状态存储 | 「状态存储」步骤 | `qk.State`(文本状态与组合动作**互通**) | + +二者可以互相调用: + +- 组合动作用「运行动作」步骤调用脚本动作,传入的文本会进入脚本的 `quicker_in_param` 参数(以及 `qk.Context.Input`)。 +- 脚本用 `qk.Actions.Call("动作名或 Id", "输入")` 调用其他动作(包括组合动作),用 `qk.Actions.CallSubprogram("子程序名", inputs)` 调用本机公共子程序。 +- 调用链(脚本与组合动作混合)最多 16 层。 + +## 如何使用 + +新建动作时选择「Quicker脚本动作」即可。新建、编辑、复制、导出、导入以及 AI 助手中与脚本动作相关的能力都直接可用。 + +脚本动作可以发布到分享平台(包括内嵌了脚本动作的多操作动作);含无法识别的 `qk` 用法的脚本会被拒绝,规则见 [安全与授权·分享规则](./security#6-分享规则)。 + +`qk` API 随 Quicker 版本演进;个别成员调整时,编辑器的「检查」会指出旧写法并给出新写法。文档与实际行为不一致时,以编辑器补全说明和实际行为为准。 + +## 第一个脚本 + +新建脚本动作后,编辑器会预填一个模板: + +```csharp +void Main(string quicker_in_param = "") +{ + qk.Ui.Notify("你好"); +} +``` + +把它改成「把选中的文本转成大写并复制到剪贴板」: + +```csharp +string Main(string quicker_in_param = "") +{ + // 先读取当前窗口里选中的文字;没有选中时使用动作收到的输入文本。 + var text = qk.Selection.GetText() ?? quicker_in_param; + if (string.IsNullOrWhiteSpace(text)) + { + qk.Ui.Notify("没有选中文本", "warn"); + return null; + } + + var result = text.Trim().ToUpperInvariant(); + qk.Clipboard.SetText(result); + qk.Ui.Notify("已复制:" + result, "success"); + return result; +} +``` + +编辑器的基本用法: + +| 区域 / 按钮 | 作用 | +|---|---| +| 顶部信息栏 | 图标、标题、说明 | +| **运行**(F5) | 运行当前源码。右侧 ▾ 可选「直接运行」或「最小化后延迟运行」 | +| **检查** | 不执行代码,只检查语法、入口、已知错误用法,并给出修复建议 | +| 超时 | 本次动作最长运行时间,默认 30 秒;可设 0.1 秒–24 小时,或勾选「不限制」 | +| 底部测试面板(Ctrl+\`) | 「输入」「结果」「日志」「问题」等页签 | +| 保存 | 保存并关闭编辑器;按 Ctrl+S(或按住 Ctrl 点击「保存」)只保存,编辑器保持打开 | +| 历史版本 / 保存版本 | 查看并载入旧版本;把当前内容存成带备注的版本(见下文「备份与恢复」) | + +:::caution[测试读取选中文本或操作其他窗口] +请选择「最小化后延迟运行」。直接运行时前台窗口是编辑器本身,读到的选中文本会是空的。最小化后延迟运行会先最小化编辑器、回到你之前使用的窗口,等约 2 秒再运行,结束后自动还原编辑器。 +::: + +保存后,把动作放到面板上,点击即可运行。 + +## Main 参数与参数表单 + +### 参数就是输入 + +`Main` 的参数就是动作的输入。支持的参数类型:`string`、`string?`、`bool`、`int`、`long`、`double`、`decimal`、`int?`、`bool?`、`string[]`、`int[]`、`object`、`DateTimeOffset`、`TimeSpan`,最多 64 个。 + +**必填规则**: + +- 不带 `?`、也没有默认值的参数是**必填**的(如 `string text`、`int count`); +- 带 `?`(如 `string? note`、`int? limit`)或有默认值(如 `string mode = "upper"`)的参数是**可选**的; +- 必填参数传入空值或空白文本时报「不能为空」。 + +### 用 `[ActionParameter]` 美化表单 + +```csharp +string Main( + [ActionParameter("文本", Description = "要处理的内容", MultiLine = true)] string text, + [ActionParameter("模式", Options = "大写|upper\n小写|lower")] string mode = "upper", + [ActionParameter("最多字符数", Ask = true)] int limit = 100) +{ + var r = mode == "upper" ? text.ToUpperInvariant() : text.ToLowerInvariant(); + return r.Length > limit ? r[..limit] : r; +} +``` + +| 字段 | 含义 | +|---|---| +| 第一个参数(如 `"文本"`) | 表单中显示的标题 | +| `Description` | 说明文字 | +| `Options` | 下拉选项,每行「标题\|值」,多行用 `\n` 分隔(只用于 `string` 参数,值不能重复) | +| `MultiLine` | 多行输入框 | +| `Required` | 显式指定是否必填(优先于上面的自动推断) | +| `Ask` | 即使有默认值,也在每次交互运行时弹出表单让用户确认或修改 | + +所有字段的值都必须是字符串或布尔常量。 + +### 表单什么时候弹出 + +- 只在**交互触发**时弹出:从面板、悬浮按钮、悬浮面板、搜索窗口或编辑器运行; +- 并且存在「没有默认值又没有传入」的参数,或存在标了 `Ask = true` 的参数; +- **所有参数都有默认值且都没有 `Ask` 时不会弹出表单**,直接使用默认值。希望用户每次都能改的选项,请加 `Ask = true`,或在 `Main` 里用 `qk.Ui.Form` 询问。编辑器的「检查」会对这种情况给出提示; +- 热键、手势等非交互触发时缺少必填参数会直接报错,不弹表单; +- 用户取消表单时不会执行 `Main`。 + +### `quicker_in_param`:动作收到的原始输入 + +`string quicker_in_param = ""` 是一个特殊参数,用来接收动作的**原始输入文本**:组合动作「运行动作」步骤传入的值、`qk.Actions.Call` 的 `input`、右键附加菜单项的值、文本指令等。它: + +- 总是可选的,不会触发表单,也不能标 `Ask`; +- 不声明也可以,原始输入同样可以通过 `qk.Context.Input` 读取(没有输入时为 `null`)。 + +`qk.Context.Text` 是另一回事:它是触发时附带的上下文文本(如从文本工具栏触发时的文本),与 `Input` 互不代替。 + +### 在运行中询问:`qk.Ui.Form` + +需要在运行中途收集多个值时,用表单: + +```csharp +string Main() +{ + var r = qk.Ui.Form(new[] + { + new Field("name", "姓名", Required: true), + new Field("age", "年龄", Kind: "number", Value: 18), + new Field("vip", "会员", Kind: "check"), + }); + if (r == null) return null; // 用户取消 + + var name = (string)r.Values["name"]; + var age = Convert.ToInt32(r.Values["age"]); // number 字段为 double + var vip = (bool)r.Values["vip"]; + return $"{name},{age} 岁,{(vip ? "会员" : "非会员")}"; +} +``` + +## 返回值 + +- `return` 的值就是动作的输出;`void Main()` 表示没有输出。返回 `null` 不算失败。 +- 可以返回:字符串、数字、布尔、日期时间、数组、`List`、`Dictionary`、匿名对象、元组、`JsonNode`,以及 `qk` 提供的数据类型(如 `WinInfo`、`SelectResult`)。结构化的值会被转换成 JSON(字段名为小驼峰,如 `title`、`statusCode`)。 +- **不能返回**窗口引用 `Win`、图片 `Img`、界面元素 `El` 等「句柄」(报 `CODEC_UNSUPPORTED`)。需要窗口信息时返回 `qk.Window.Info(w)`;需要图片时返回 `img.ToBase64()` 或保存为文件后返回路径。 +- 单个返回值最多 1 MiB、10,000 项、嵌套 32 层。LINQ 查询先 `.ToList()` 再返回。 + +```csharp +object Main() +{ + var w = qk.Window.GetForeground(); + if (w == null) return null; + var info = qk.Window.Info(w); + return new { info.Title, info.Process, info.Bounds.Width, info.Bounds.Height }; +} +``` + +## 右键菜单:`[ActionMenu]` + +给动作按钮加固定的右键菜单项,只需在 `Main` 旁边写一个**无参数、返回 `void`** 的方法,并标上 `[ActionMenu]`: + +```csharp +void Main() +{ + var n = qk.State.Get("count", 0) + 1; + qk.State.Set("count", n); + qk.Ui.Notify($"第 {n} 次运行"); +} + +[ActionMenu("查看次数", Description = "显示已运行的次数", Icon = "fa:Light_Cog")] +void ShowCount() +{ + qk.Ui.Alert("已运行 " + qk.State.Get("count", 0) + " 次"); +} + +[ActionMenu("工具/重置计数", Icon = "fa:Light_Trash")] +void Reset() +{ + qk.State.Remove("count"); + qk.Ui.Notify("已重置"); +} +``` + +- 点击菜单项时**只运行该方法,不运行 `Main`**,也不弹参数表单;此时 `qk.Context.Trigger` 为 `"contextMenu"`。 +- 标题中的 `/` 生成子菜单(如 `"工具/重置计数"`);完整路径不能重复,某一路径不能既是菜单项又是父菜单。 +- `Description`(悬停提示)和 `Icon`(`fa:` 字体图标)可选;标题、说明、图标都必须是字符串常量。 +- 不能标在 `Main` 上;每个方法最多一个 `[ActionMenu]`;方法不能带修饰符、泛型或重载。 +- 菜单按**已保存的源码**生成。修改源码并保存后,请重新打开右键菜单;点击旧菜单会提示「源码已更新」。 +- 编辑器里可在运行入口下拉中选择某个菜单方法单独试运行。 +- 菜单项需要随数据动态变化(如「最近使用」列表)时,才改用 `qk.Actions.SetContextMenu`(点击后重新运行 `Main`,菜单项的值从 `qk.Context.Input` 读取)。 + +右键菜单中的顺序:`[ActionMenu]` 项在最前,其后是动作自身的自定义菜单,最后是 `SetContextMenu` 设置的附加项。 + +## 调试 + +### 编辑器里的调试手段 + +- **检查**:不执行代码,提示语法错误、不支持的写法(例如用了 `File.ReadAllText`)及修复建议。问题页双击或回车可跳到出错位置。检查通过**不代表**运行一定成功。 +- **断点与单步调试**:可在源码中设断点,并单步执行,便于逐步核对逻辑。 +- **运行轨迹和变量变化**:调试时可查看运行轨迹以及变量变化,配合日志定位问题。 +- **日志**:`qk.Log("消息")` 输出到测试面板「日志」页;可指定级别 `qk.Log("详情", "debug")`,级别为 `debug`、`info`、`warn`、`error`,单条最多 4096 字符。 +- **结果**:运行结束后在「结果」页显示返回值或错误信息。 +- **输入**:测试面板「输入」页可以填写原始输入文本,或按 `Main` 签名填写各参数。 +- **停止**:运行中可点停止(Shift+F5)。 +- **最小化后延迟运行**:适合测试读取选中文本、操作其他窗口的脚本(见上文提示)。 + +编辑器中的运行是**真实执行**,不会回滚(见文首注意)。 + +### 编辑器 AI 助手 + +脚本编辑器标题栏有 AI 按钮(`Ctrl+J` 开关),打开右侧助手面板: + +- 用自然语言描述需求,AI 会直接修改编辑器里的源码,也可以顺手填写标题与说明; +- 运行失败或检查有问题时,「结果」页、状态栏或「问题」页会出现「让 AI 修复」入口,AI 会结合运行记录和诊断信息分析; +- 可以选中一段代码「引用给 AI」,让它只改这部分; +- 每轮修改后会显示改动摘要,可「撤销本轮」,也可用 Ctrl+Z 撤销; +- **AI 助手只修改代码,不会替你运行脚本。** 运行与保存始终由你自己决定; +- 当一轮修改**新增了高风险能力**(如键盘监听、外部脚本)或出现无法识别的 `qk` 用法时,完成卡片会给出提醒。 + +AI 助手需要先保存动作(有动作 Id)才能使用。**AI 写的脚本保存后被视为「你自己编写的动作」,运行前不会弹出授权确认**,请在运行前读一遍代码,尤其是涉及文件删除、网络上传、键盘监听的部分。详见 [安全与授权](./security)。 + +### 备份与恢复 + +- **历史版本**:新建的动作需先保存一次才能使用。每次保存都会自动留一份本地版本(保留约 1 个月)。点信息栏「历史版本」可查看本地与服务器上的版本,选中后载入到编辑器——只是载入,**不会自动保存**;源码可按 Ctrl+Z 撤回,确认后再保存。载入只替换源码和超时,标题、图标、选项不变。 +- **保存版本**:点信息栏「保存版本」,填写备注即可把编辑器当前内容存成一个长期保留的版本(可选同时备份到服务器)。它不会改动已保存的动作;源码有错误时也能保存,备注里会注明「含未通过检查的源码」。也可以在动作右键菜单「导出或备份」中备份。 +- **未保存内容的恢复**:编辑时,编辑器会在后台保留一份未保存内容(停止输入约 3 秒后、连续编辑时至少每 15 秒、每次运行前、AI 每轮结束时更新)。如果 Quicker 意外退出,再次打开该动作(新建的动作则是再次新建脚本动作)时,编辑器顶部会提示「发现 HH:mm 未保存的编辑」,可选择「恢复」或「丢弃」。保存成功或关闭时选择「不保存」后,这份内容会被删除;超过 30 天的自动清理。 +- 历史版本与「保存版本」需要相应的会员权益;专业版开启「修改动作后,自动将其备份到服务器」后,每次保存还会自动备份到服务器。 + +## 常见错误与排查 + +### 读懂错误:错误码与 OperationId + +`qk` 调用失败时抛出 `ActionApiException`(不需要 `using`)。它有四个关键信息: + +| 属性 | 含义 | 示例 | +|---|---|---| +| `e.Code.Value` | **错误码**,稳定的英文大写字符串,用来判断失败原因 | `FILE_NOT_FOUND`、`CAPABILITY_DENIED` | +| `e.OperationId` | 出错的**调用名**,与源码中的写法对应 | `Files.ReadText`、`Img.Crop` | +| `e.Detail` | 扩展错误码(可能为 `null`),如浏览器扩展、被调动作返回的具体原因 | `URL_PATTERN_MISMATCH` | +| `e.Message` | 中文错误说明,只用于阅读 | 「文件不存在:…」 | + +`OperationId` 的取值: + +- `域.方法`:如 `Files.ReadText`、`Window.Activate`;根方法为 `Log`、`Wait`; +- `类型.成员`:图片、文本窗口、进度窗口等句柄上的方法,如 `Img.Crop`、`TextWin.Append`、`ProgressWin.Update`; +- `input`:绑定 `Main` 参数时出错(如缺少必填参数,错误码 `INPUT_MISSING`); +- `return`:转换返回值时出错(如返回了 `Win`); +- `run`:运行前的准备阶段出错(如用户拒绝了授权,错误码 `CAPABILITY_DENIED`)。 + +在脚本里按错误码处理: + +```csharp +string Main(string path = @"C:\temp\不存在.txt") +{ + try + { + return qk.Files.ReadText(path); + } + catch (ActionApiException e) when (e.Code == ActionErrorCode.FileNotFound) + { + return null; // 文件不存在就返回空 + } + catch (ActionApiException e) + { + // 其他失败:把错误码、调用名和扩展码写进日志,便于排查 + qk.Log($"{e.Code.Value} @ {e.OperationId},Detail={e.Detail ?? "无"}:{e.Message}", "error"); + return null; + } +} +``` + +- 每个错误码都有同名的静态成员(错误码的帕斯卡写法,如 `ActionErrorCode.FileNotFound`),也可以写 `e.Code.Value == "FILE_NOT_FOUND"`。 +- **不要解析 `e.Message`**:消息文字可能随版本调整,错误码用于程序判断,不随消息文字变化。 +- 没有捕获的错误会让动作运行失败,「结果」页只显示错误说明文字,不单独显示错误码与 OperationId;需要时按上面的写法记录到日志。 +- 全部错误码见 [API 参考·错误码表](./api#错误码表)。 + +### 用户取消与停止 + +- **用户取消对话框不是错误**:`qk.Ui.Select`、`Prompt`、`Form`、`PickFile` 等取消时返回 `null`,`Confirm` 返回 `false`。 +- **停止不是错误**:用户点停止、超过动作超时、被调用的动作被取消时,脚本会被停止,`catch` 捕获不到(包括 `catch (Exception)`),只会执行 `finally`。停止后的 `finally` 里只能做有限的清理(如写日志、恢复剪贴板、松开按键、清除角标),其他 `qk` 调用会报 `CAPABILITY_DENIED`。 + +### 常见问题速查 + +| 现象 / 错误 | 可能原因 | 处理 | +|---|---|---| +| 检查提示「不能声明类 / 命名空间」 | 写了 `class`、`record`、`namespace` | 改用元组、匿名类型或字典;只写方法 | +| 检查提示 `File`/`HttpClient`/`Thread.Sleep` 等不可用 | 使用了有副作用的 .NET 类型 | 按上文对照表改用 `qk` | +| 检查提示 `async`/`await` 不支持 | 脚本是同步执行的 | 去掉 `async`/`await`,`qk` 调用本身就是同步的 | +| 编辑器里运行时选中文本为空 | 直接运行时前台是编辑器 | 改用「最小化后延迟运行」 | +| 「脚本执行超时(30000 毫秒…)」 | 超过动作超时;等待对话框、按键的时间也计入 | 在编辑器调大超时,交互式脚本建议 ≥ 300 秒;计时 / 监视类可设为不限制 | +| `CAPABILITY_DENIED` | 未获授权;在停止后的 `finally` 中调用了不允许的方法;`qk` 写法无法识别 | 见 [安全与授权](./security);**直接写 `qk.域.方法(...)`**,不要把 `qk` 或 `qk.Files` 赋给变量、当参数传递或写 `qk?.` | +| 「请先保存动作;只有动作编辑器中的临时调试运行可以逐次确认权限。」 | 需要授权确认的动作(如导入的动作)还没有保存,且不是从编辑器运行 | 先保存动作再运行 | +| `INPUT_MISSING`(OperationId 为 `input`) | 非交互触发且缺少必填参数 | 给参数加默认值,或从面板等交互方式触发 | +| `CODEC_UNSUPPORTED`(OperationId 为 `return`) | 返回或写入状态的值里含 `Win`、`Img` 等句柄 | 返回 `qk.Window.Info(w)`、`img.ToBase64()` 等数据 | +| `CODEC_VALUE_INVALID` | `qk.State.Get` 读回的数据与类型不符 | 检查写入与读取的类型是否一致;文本状态用 `GetText` | +| `INPUT_LIMIT_EXCEEDED` | 键盘输入超出本次运行的限额(如 `Type` 累计超过 2000 字符) | 之后本次运行的键鼠输入全部失败;长文本改用 `qk.Keyboard.Paste` | +| `WINDOW_LIMIT_EXCEEDED` | 窗口查询 / 操作次数超限 | 用 `FindAllInfo` 一次取回多个窗口信息,避免循环里逐个 `Info` | +| `ELEVATED_TARGET_DENIED` | 目标是管理员权限运行的程序 | 普通权限下无法操作管理员窗口 | +| 右键菜单点击提示「源码已更新」 | 保存了新源码,菜单还是旧的 | 重新打开右键菜单 | +| 检查提示「`qk.X.Y` 在当前 Quicker 中不存在」 | 成员名写错,或当前 Quicker 版本没有它 | 按补全列表改正,或更新 Quicker | + +排查的一般顺序:先点「检查」→ 看「问题」页;再运行 → 看「结果」页和「日志」页;仍不明白时点「让 AI 修复」,或用 `try/catch` 把 `e.Code.Value`、`e.OperationId`、`e.Detail` 记到日志里。反馈时可附上脚本源码(去掉敏感信息)、结果 / 日志页内容,以及错误码与 OperationId,到社区或反馈入口说明。 + +## 相关链接 + + diff --git a/docs/v2/features/script-actions/security.md b/docs/v2/features/script-actions/security.md new file mode 100644 index 0000000..a0261b5 --- /dev/null +++ b/docs/v2/features/script-actions/security.md @@ -0,0 +1,201 @@ +--- +title: 脚本动作安全与授权 +description: 脚本动作的沙箱边界、按来源的能力授权、高风险提示与分享规则。 +sidebar_position: 2 +quickerDocKey: v2/features/script-actions/security +comments: true +--- + +# 脚本动作安全与授权 + +> 本文说明脚本动作能做什么、不能做什么,什么时候会弹出授权确认,以及哪些情况需要提高警惕。内容以当前版本的实际行为为准,**不代表脚本动作是绝对安全的**。 +> +> 相关文档:[脚本动作入门](./)、[脚本动作 API 参考](./api)。 + +## 1. 一句话概括 + +- 脚本运行在一个**受限环境(沙箱)**里:不能直接使用文件、网络、进程等 .NET 功能,**一切系统能力都必须通过 `qk` 调用**。 +- 运行前,Quicker 从源码中识别脚本会用到哪些能力;运行时,**只放行已识别且已授权的能力**。 +- **你自己编写的脚本(包括让 AI 帮你写并保存的)运行前不会确认**;从外部导入、从动作库安装的脚本,首次运行前会按能力逐项请你确认。 +- 沙箱是软件层面的限制,**脚本与 Quicker 运行在同一个进程中**,不是操作系统级别的隔离。请只运行你信任、并且看得懂的脚本。 + +## 2. 沙箱是什么 + +### 2.1 脚本能直接做的 + +- 纯计算:字符串、数字、日期、正则、LINQ、集合、JSON(`JsonNode`、只作用于数据的 `JsonSerializer`)、`Encoding`、`Math`、`Guid`、`Random`、`Uri` 等; +- `Path` 的纯字符串方法(`Path.Combine`、`GetFileName` 等,不访问磁盘); +- 调用 `qk` 提供的能力。 + +### 2.2 脚本不能直接做的 + +- 读写文件和目录(`File`、`Directory`、文件流); +- 访问网络(`HttpClient`、`WebClient`); +- 启动或结束进程(`Process`); +- 创建线程、定时器、并行任务; +- 访问环境变量、控制台、`MessageBox`/WPF/WinForms 窗口; +- 反射、加载程序集、调用系统 API。 + +这些能力只有对应的 `qk` 版本,例如读文件要用 `qk.Files.ReadText`,访问网络要用 `qk.Http.GetText`。`qk` 方法内部会做参数校验、次数与大小限额、权限检查(例如拒绝操作管理员权限的窗口、拒绝读取 Quicker 自身界面、以普通权限启动程序等)。 + +### 2.3 沙箱不能保证什么 + +请准确理解沙箱的边界: + +- **与 Quicker 同进程**:脚本由 Quicker 内置的解释器在 Quicker 进程内执行。沙箱依靠解释器拦截不允许的类型和成员来工作,不是虚拟机或独立进程。如果解释器存在尚未发现的漏洞,脚本理论上可能获得与 Quicker 相同的权限。 +- **`qk` 能力本身就很强**:一个获得了“读取文件”和“访问网络”能力的脚本,完全可以把你的文件上传出去;获得“键盘监听”的脚本可以记录你输入的密码。沙箱只保证脚本“按声明的能力做事”,**不判断它做的事是否对你有利**。 +- **能力确认只针对“安装来源”的动作**:自己编写的动作运行前没有任何确认(见第 3 节)。 +- **每次运行的限额**(例如键盘输入 2000 字符、窗口操作次数等)是为了防止失控,不是安全保证。 +- **部分外部操作无法撤回**:例如启动的程序、发出的网络请求、写入云端的数据、在 Office 中执行的宏,超时或停止脚本后可能仍在继续或已经生效。 + +## 3. 能力与授权确认 + +### 3.1 按来源决定是否确认 + +| 动作来源 | 运行前是否确认 | +|---|---| +| **本机自建**:你在编辑器中手写,或 AI 助手代写后由你保存;以及它们的副本;同一账号同步到其他设备后的同一动作 | **不确认**。作者就是你本人 | +| **从动作库安装或更新** | 首次运行前按能力**逐项确认** | +| **在安装来的动作上做了本地修改**,或安装来的动作的副本 | 仍按“安装来源”处理(改一个字不会变成“自建”) | +| **从文件导入**,或**从剪贴板粘贴了他人复制的动作**(例如从论坛、聊天中复制) | 按“外部导入”处理,与安装来源相同,逐项确认 | + +> “安装来源”的脚本动作来自**分享平台安装**、**文件导入**和**剪贴板粘贴**。本机“复制动作”后再粘贴仍视为自建;Quicker 重启后,之前复制在剪贴板里的动作会按外部来源处理(多问一次,不会漏问)。 + +### 3.2 确认时会发生什么 + +对需要确认的动作: + +1. 运行前,Quicker 列出脚本用到、但**尚未授权**的能力,请你确认; +2. 你确认后,这些能力在**本机**被记住,下次运行不再询问; +3. 你拒绝时,本次运行失败,错误码为 `CAPABILITY_DENIED`(OperationId 为 `run`),消息为“用户拒绝了 … 权限”;拒绝不会被记住,下次运行会再次询问; +4. 授权**只保存在本机**,不随账号同步、不随动作导出。换一台电脑需要重新确认; +5. 如果是在编辑器中临时运行一个**尚未保存**的此类动作,确认只对本次运行有效,不会保存。 + +以下能力不需要确认(任何来源都直接可用):写日志、等待、读取运行上下文、动作状态(`qk.State`)、大部分对话框与通知(`qk.Ui`,带 `click` 的通知除外)、纯计算的文本工具(哈希、拼音)、显示器信息、音量与播放内置声音/朗读、读取或装饰本动作自身(角标、覆盖图标、附加右键菜单)等。播放本机音频文件或网址、从文件或网址加载图片时,另需“读取文件”或“访问网络”能力。 + +### 3.3 能力扩大时再次确认 + +授权按“**动作 + 来源 + 能力**”逐项记录,**不绑定具体源码**: + +- 动作更新或你本地修改后,只要用到的能力没有超出已授权的范围,就不再询问; +- 一旦新版本用到了新的能力(例如原来只读剪贴板,新版本增加了“访问网络”),运行前**只询问新增的那几项**; +- 调用组合动作步骤的 `qk.Steps.Run`(见 [API 参考·qk.Steps](./api#qksteps组合动作步骤))按**步骤**逐个确认,新增步骤会再次询问。 + +### 3.4 确认对话框里的能力说明 + +| 确认中显示 | 对应的 `qk` 用法(举例) | +|---|---| +| 桌面鼠标自动化(全屏范围) | `qk.Mouse.*` | +| 桌面键盘自动化(前台窗口) | `qk.Keyboard.Press/Type/Paste/SendKeys`、`GetIme/SetIme` | +| 读取剪贴板内容 / 写入或清空剪贴板内容 | `qk.Clipboard.Get*` / `qk.Clipboard.Set*`、`Clear` | +| 读取桌面窗口与进程信息 | `qk.Window.Find/FindAll/Info/...`、`qk.Process.List`、`qk.Apps.ListTargets` | +| 激活桌面窗口 | `qk.Window.Activate/RestoreForeground` | +| 移动、缩放、隐藏或改变窗口状态 | `qk.Window.SetBounds/SetState/SetTopmost/Close/...` | +| 读取资源管理器选中路径 | `qk.Selection.GetFiles`、`qk.Files.GetExplorerPath/SetExplorerPath`(后者另需激活窗口) | +| 通过复制读取前台选中文本 | `qk.Selection.GetText` | +| 捕获屏幕或窗口图像 / 在本机识别图像文字 | `qk.Screen.Capture*`、`qk.Vision.*` | +| 打开截图 Pro 并读取用户确认的区域信息 | `qk.Screen.PickCapture/CapturePro` | +| 读取本机文件和目录 | `qk.Files` 的读取类方法、`qk.Files.Search` | +| 读取、创建、修改或删除本机文件(删除默认不经过回收站) | `qk.Files` 的写入/复制/移动/删除/压缩类方法、`qk.Image.Save`、`qk.Http.Download` | +| 访问网络 | `qk.Http.*`、云端数据、临时分享 | +| 启动其他程序或打开文件/网址。`qk.Process.Start/Run` 与指定浏览器程序路径的 `qk.Browser.Open` 以普通权限(降权)启动;`qk.Process.Open`、关联文件、网址与通知点击动作遵循 Windows/系统 Shell 的权限行为,Quicker 以管理员运行时不保证降权 | `qk.Process.*`、`qk.Browser.Open`、带 `click` 的 `qk.Ui.Notify` | +| 调用本机其他动作或子程序,或停止正在运行的动作 | `qk.Actions.Call/CallSubprogram/Stop/ShowContextMenu` | +| 强制结束程序(可能丢失未保存数据) | `qk.Window.Close(w, kill: true)`、`qk.Process.Kill` | +| 让电脑睡眠、休眠、注销、关机或重启(未保存的工作可能丢失) | `qk.Sys.Power` 的 `sleep`/`hibernate`/`signOut`/`shutdown`/`restart`(锁屏 `lock`、关闭显示器 `screenOff` 不需确认) | +| 读取和操作浏览器页面 | `qk.Browser` 的标签页与元素操作 | +| 调用 AI 或翻译服务(发送内容到你配置的 AI 或 Quicker 服务器,消耗 AI 额度/Quicker 点数) | `qk.Ai.*` | +| 读取其他程序窗口中的界面元素 / 操作其他程序的界面元素 | `qk.Uia` 的查找类 / 操作类方法 | +| 监听你按下的键:可记录你输入的所有按键(包括密码),并可拦截按键不传给当前程序 | `qk.Keyboard.WaitForKey` | +| 控制 Quicker 本身(暂停/恢复、停止全部动作、切换动作页、重启 Quicker) | `qk.Quicker.Command` 的部分命令 | +| 读写你账号下的 Quicker 云端数据(所有动作、所有设备共享) | `qk.Quicker.GetCloud/SetCloud/RemoveCloud` | +| 上传内容到 Quicker 临时分享(生成他人可访问的网址) | `qk.Quicker.ShareTemporary/ShareTemporaryFile` | +| 读取可识别本机与你的信息(计算机名、用户名、局域网 IP、Quicker 用户标识、环境变量与注册表,可能含程序保存的密钥/令牌) | `qk.Sys.Info`、`qk.Sys.GetEnv/GetRegistry`、`qk.Quicker.Info` | +| 解密“自用加密”的数据 | `qk.Quicker.DecryptLocal` | +| 【高风险】在其他程序中执行脚本或命令 | `qk.Browser.Eval/Command`、`qk.Apps.Run/Bridge/RunOfficeCommand` | +| 【高风险】向其他程序的窗口发送任意窗口消息 | `qk.Window.SendMessage` | +| 【高风险】调用组合动作步骤(按步骤列出) | `qk.Steps.Run` | + +少数参数会追加能力:`Window.Close` 的 `kill`、`Window.ActivateProcess` 的 `hotkey`(键盘)、`Clipboard.SetFiles` 的 `cut`(修改文件)、`Ui.Notify` 的 `click`(启动程序)写了非 `false`/`null`/空串的值时,会追加对应能力。 + +### 3.5 高风险提示的含义 + +确认对话框中标着 **【高风险】** 并单独成行的能力,意味着授权后脚本可以做到“几乎任何事”: + +- **在其他程序中执行脚本或命令**:Office 宏可以在该程序中以你的权限执行任意程序;浏览器页面脚本可以读写该页面及其登录状态。 +- **向其他程序的窗口发送任意窗口消息**:可以触发该程序的菜单命令、改写其中的内容或关闭窗口。 +- **调用组合动作步骤**:其中可以运行任意程序或命令的步骤会再单独一行警示。 + +此外,以下能力虽然不标【高风险】,但后果严重,AI 助手完成修改时也会专门提醒:**强制结束程序**、**键盘监听**、**自用解密**、**控制 Quicker**、**电源/会话**(睡眠、休眠、注销、关机、重启)。 + +### 3.6 源码无法识别时 + +Quicker 通过源码中的 `qk.域.方法(...)` 写法识别能力。如果脚本把 `qk` 或某个域赋给变量、当作参数传递,或写成 `qk?.`,Quicker 无法判断它会调用什么,会**按“需要全部能力”处理**:安装来源的动作会因此要求确认一长串能力。 + +**看到一个来路不明的脚本申请了几乎所有能力,应当拒绝**,并检查源码中是否有这类写法。编辑器“检查”会逐处提示位置与改写方式(直接写 `qk.域.方法(...)`)。 + +## 4. 如何撤销授权 + +- **删除动作**会同时清除它在本机的全部脚本授权; +- **导入并替换**一个已有动作时,原动作的脚本授权会被清除,新内容需要重新确认; +- 目前没有单独的授权管理界面;删除动作会同时清除它的授权。如果你想收回某个安装来源动作的授权,可以删除该动作后重新安装或导入,运行时会重新询问。 + +自己编写的动作没有授权记录可撤销,因为它们本来就不经确认。 + +## 5. 什么情况下要警惕 + +运行来自他人的脚本(文件导入、聊天/论坛里复制的动作)前,请留意确认对话框中的**能力组合**: + +| 能力组合 | 可能的风险 | +|---|---| +| **键盘监听 + 访问网络** | 记录你输入的内容(包括密码)并发送出去 | +| **读取文件 + 访问网络**(或临时分享) | 把本机文件上传到外部 | +| **读取剪贴板 / 选中文本 + 访问网络** | 把你复制的内容(可能含账号、验证码)发送出去 | +| **自用解密 + 访问网络** | 解开其他动作用“自用加密”保存的秘密并外发 | +| **读写云端数据** | 云端数据由你账号下的**所有动作、所有设备**共享,脚本可读取或覆盖其他动作保存的数据 | +| **写入/删除文件** | 删除默认**不经过回收站** | +| **强制结束程序** | 可能丢失未保存的工作 | +| **任何【高风险】能力** | 等同于允许脚本在其他程序中执行任意操作 | +| **申请了几乎全部能力** | 多半是源码中有无法识别的 `qk` 写法(第 3.6 节),或有意隐藏真实意图 | + +其他需要注意的点: + +- **AI 代写的脚本**:AI 助手写好并由你保存的脚本按“自建”处理,运行前**不会确认**。如果你让 AI 参考了网页或文件内容,这些内容可能夹带恶意指令,诱导 AI 写出“读取本机数据并发送到网络”的代码。请在运行前读一遍代码;AI 助手在一轮修改**新增高风险能力**或出现无法识别的 `qk` 用法时会给出一行提醒,请务必留意。 +- **`qk.Ui.React` 页面**:页面完全由脚本绘制,可以仿冒任意界面。不要在脚本弹出的界面里输入其他账号的密码。 +- **“自用加密”不是强加密**:`qk.Quicker.EncryptLocal` 的密钥只由你的 Quicker 账号决定,只适合避免明文落盘或与“加密”步骤互通,不能用来防范攻击者。 +- **临时分享的网址任何人都能打开**:不要用 `qk.Quicker.ShareTemporary` 上传敏感内容。 +- **在编辑器中运行就是真实执行**,没有“试运行”或自动回滚。 + +## 6. 分享规则 + +脚本动作(包括内嵌了脚本动作的多操作动作)可以发布到分享平台,按以下规则判定: + +- 用到哪些 `qk` 成员都不影响能否分享。成员以后若有调整,旧脚本可能需要按编辑器“检查”的提示改写,这由作者按提示修复; +- 源码中有无法识别的 `qk` 用法(`qk` 赋给变量、当参数传递、`qk?.`、只写了 `qk.域`、写了不存在的成员)的动作不能分享,因为 Quicker 无法确认它会用到哪些能力(对方安装时也就无法准确地逐项确认)。分享被拒时会列出这些位置和改法,改正后即可分享; +- 分享时会记录你使用的 Quicker 版本。比这个版本旧的 Quicker 打开该动作会提示“此动作需要 Quicker x.y 或更高版本”,更新后即可使用。 + +## 7. API 兼容 + +`qk` API 随 Quicker 版本演进;个别成员调整时,编辑器的“检查”会指出旧写法并给出新写法。错误码以英文大写字符串表示,判断失败原因时请用错误码,不要解析错误消息文字。 + + +## 相关链接 + + diff --git a/docs/v2/migration/upgrade-and-rollback.md b/docs/v2/migration/upgrade-and-rollback.md index a68cbe9..417e8ed 100644 --- a/docs/v2/migration/upgrade-and-rollback.md +++ b/docs/v2/migration/upgrade-and-rollback.md @@ -105,6 +105,12 @@ AI 助手正式版支持选择本地工作区。选择本地文件夹后,AI ## 动作与日常操作 +### 2.3.0 · 脚本动作 {/* #230-脚本动作 */} + +脚本动作需使用 **2.3.0 或更高版本**。来自分享、文件导入或外部粘贴的脚本会按能力请求授权,后续能力扩大时再次确认;编辑器中的运行会真实执行操作。说明见 [脚本动作](/v2/features/script-actions/)与 [安全与授权](/v2/features/script-actions/security)。 + +使用脚本动作前建议导出备份,并将同步设备统一升级。回退旧版后重新保存或同步动作,可能丢弃新版的「外部导入」来源标记,影响重新升级后的授权判定;请避免在旧版编辑这类动作,必要时重新导入备份。 + ### 2.2.20 · 显示器与虚拟桌面 {/* #2220-显示器与虚拟桌面步骤 */} 2.2.20 起新增的显示器、虚拟桌面步骤需要新版客户端。含这些步骤的动作在旧版客户端中可能无法识别或运行;多设备同步或分享给他人前,请确认目标设备已升级到 2.2.20 或更高版本。 diff --git a/docs/v2/what's-new/actions.md b/docs/v2/what's-new/actions.md index 7c9f4d1..69d213f 100644 --- a/docs/v2/what's-new/actions.md +++ b/docs/v2/what's-new/actions.md @@ -12,6 +12,8 @@ Quicker V2 重做了动作的底层模型。基础动作、面板中的快捷操 这项变化主要解决 1.x 中动作类型多、参数字段含义不一致、正文与位置耦合,以及不同入口重复保存快捷操作的问题。 +2.3.0 起新增独立的 [脚本动作](/v2/features/script-actions/)类型(C# 语法 + `qk` API),与组合动作并列;安全、调试与 API 见该专页,不要与组合动作模块混淆。 + ## 从字符串参数改为结构化参数 1.x 的动作经常根据动作类型解释 `Data`、`Data2`、`Data3` 等字符串。同一个字段在不同动作中的含义可能完全不同,复杂动作还会把一段 JSON 再塞进字符串字段。 diff --git a/docs/v2/xaction/concepts/subprogram.md b/docs/v2/xaction/concepts/subprogram.md index 45777a7..5071a7f 100644 --- a/docs/v2/xaction/concepts/subprogram.md +++ b/docs/v2/xaction/concepts/subprogram.md @@ -89,9 +89,9 @@ legacyContentUpdatedAt: "2025-06-06T03:33:32.000Z" 公共或网络子程序也可从步骤右键转成动作内,以便改内部定义。列表上还能重命名、复制、建副本;复制后可到别的动作的子程序列表空白处粘贴。 -### 开发版:检查内嵌子程序的来源更新 +### 检查内嵌子程序的来源更新 -以下依据 2026-10-01 核对的开发代码,尚未包含在 2.2.26 中。对于保留有效分享来源的动作内子程序,可在子程序列表中右键选择 **检查来源更新**。 +2.3.0 起,对于保留有效分享来源的动作内子程序,可在子程序列表中右键选择 **检查来源更新**。 1. 先保存正在编辑的子程序并返回主程序,结束调试或 AI 回合。 2. 检查来源更新。发现新版后,核对确认框中的来源、版本、更新说明、步骤和变量数量,以及输入输出参数变化摘要。