# dev_tools **Repository Path**: stdylan/dev_tools ## Basic Information - **Project Name**: dev_tools - **Description**: No description available - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2026-09-13 - **Last Updated**: 2026-09-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # dev_tools — Windows 开发小工具集(AutoHotkey v2) 一套**纯本地、零网络、轻量无边框**的 Windows 桌面效率小工具,用 [AutoHotkey v2](https://www.autohotkey.com/) 写成, 全部功能靠全局热键唤起,不占任务栏、不驻留托盘菜单、不联网。 目前包含 11 个功能:**工作记录台账**、**JSON 格式化悬浮**、**选中文本存文件**、**区域截图悬浮**、 **选中文本透明悬浮**、**项目资料本**、**选中多行加引号逗号**、**轻量链式计算器**、**POST 请求测试**、 **文本对比(`Alt+9`)**(左右分栏、行级差异着色、上一处/下一处定位),以及 **日历(`Alt+0`)** (公历 + 农历 + 二十四节气,支持翻月与日期跳转,离线数据)。 --- ## 目录 - [快速开始](#快速开始) - [快捷键总览](#快捷键总览) - [功能详解](#功能详解) - [目录与文件](#目录与文件) - [配置项](#配置项) - [依赖与编译](#依赖与编译) - [常见问题](#常见问题) - [开发说明](#开发说明) --- ## 快速开始 ### 方式一:直接运行编译版(推荐,免装环境) 双击 **`toolkit.exe`** 即可。它是用 Ahk2Exe 打包的独立可执行文件,**已内置 AutoHotkey 运行时和 ImagePut 依赖**, 目标机器无需安装 AutoHotkey。 ### 方式二:从源码运行(需要 AutoHotkey v2) 1. 安装 [AutoHotkey v2](https://www.autohotkey.com/)(本仓库基于 **v2.0.27** 开发验证)。 2. 确认 `toolkit.ahk` 同目录下存在 **`ImagePut.ahk`**(**仓库已自带**,见 [依赖与编译](#依赖与编译))。 3. 双击 `toolkit.ahk`。 运行后在任意窗口按 **`Alt+R`**,能看到"工作记录"悬浮窗即为启动成功。 > 首次运行会在脚本所在目录自动创建 `worklog.txt`、`screenshots\`、`projects\` 三个运行期产物(见 [目录与文件](#目录与文件))。 --- ## 快捷键总览 所有热键均为**全局**(任意前台窗口下均可触发)。 | 快捷键 | 功能 | 依据 | |---|---|---| | `Alt` + `R` | 工作记录台账:呼出 / 隐藏 | — | | `Alt` + `S` | 台账:保存并隐藏(**仅台账窗口激活时有效**)| 台账输入框内容 | | `Alt` + `O` | 台账:用默认程序打开 `worklog.txt` 并隐藏自身(**仅台账窗口激活时有效**)| `worklog.txt` | | `Alt` + `1` | 区域截图 → 原位置原尺寸悬浮 | 鼠标拖选区域 | | `Alt` + `2` | 选中文本 → 半透明可编辑悬浮窗 | 当前选中的文本 | | `Alt` + `3` | 选中 JSON → 格式化后悬浮 | 当前选中的 JSON | | `Alt` + `4` | 选中文本 → 直接存为文件并打开所在目录 | 当前选中的文本 | | `Alt` + `5` | 项目资料本:呼出 / 隐藏 | 左侧项目列表 | | `Alt` + `6` | 选中多行 → 每行加单引号与逗号(末行无逗号)→ 静默写回剪贴板 | 当前选中的多行文本 | | `Alt` + `7` | 轻量链式计算器:输入框回车即算、结果进历史、上一步结果可续算,记录持久化 | 输入的表达式 | | `Alt` + `8` | POST 请求测试:左列表+右(地址/请求体/返回),固定 POST,JSON 自动格式化,请求可保存 | 地址 + 请求体 | | `Alt` + `0` | 日历:呼出 / 隐藏(农历 + 二十四节气,翻月与日期跳转) | 当前日期 / 指定年月 | | `Alt` + `9` | 文本对比:呼出 / 隐藏(左右分栏、比对按钮、差异着色、上一处 / 下一处定位) | 左右两侧手动粘贴的文本 | | `Ctrl` + `Enter` | POST 测试窗口内:发送请求(等同「发送」按钮,**仅该窗口激活时有效**)| 地址 + 请求体 | | `Esc` | 关闭当前悬浮窗 / 隐藏当前工具窗 | — | > `Alt+S` / `Alt+O` 只在"工作记录"窗口处于激活状态时生效(`#HotIf WinActive(...)`), > 避免和其它程序的 `Alt+S`(保存)冲突。 --- ## 功能详解 ### 功能 0 · 工作记录台账(`Alt+R`) 一个无标题栏、可拖动的便签式输入窗,用于随手记工作流水,保存后自动追加到 `worklog.txt` 并隐藏。 - **保存逻辑**:内容做换行统一(`CRLF`/`CR` → `LF`)并去掉首尾空行;文件**首次创建时写入 UTF-8 BOM**, 之后每次追加 `\n\n\n` 作为分隔。内容为空时提示"请先输入内容"且不落盘。 - **打开日志**:`Alt+O` 用系统默认程序打开 `worklog.txt`,并等待目标窗口激活后再隐藏自身。 - **自适应滚动条**:输入行数超过 12 行时自动加纵向滚动条(`WS_VSCROLL=0x200000`),回到 12 行以内自动去掉, 保持"短内容无滚动条"的干净外观。 - 底部固定 `Esc` 隐藏;隐藏前不自动保存(需显式 `Alt+S`)。 ### 功能 1 · JSON 格式化悬浮(`Alt+3`) 1. 选中任意 JSON 文本 → 按 `Alt+3`。 2. 脚本清空剪贴板后 `Ctrl+C` 抓取选中内容(`ClipWait` 超时或内容为空则静默返回)。 3. 用内置的**自包含 JSON 解析器**(`JSONParser` / `SerializeJSON`,零依赖)格式化为带缩进的文本。 4. 弹出浅灰底、无标题栏、**可拉伸**、**可编辑**的悬浮窗,方便直接复制或二次修改。 - 解析失败时不报错弹窗,而是把**错误信息 + 原始内容**一起显示,便于排查问题 JSON。 - `Esc` 关闭。 ### 功能 2 · 选中文本存文件(`Alt+4`) 1. 选中文本 → `Alt+4`。 2. 抓取选中内容,保存为 `文本_yyyyMMdd_HHmmss.txt`(UTF-8)。 3. 自动调起资源管理器并**选中该文件**(`explorer.exe /select,`)。 4. 全程无弹窗,仅用 `ToolTip` 提示文件名(1.5 秒后消失),失败时提示原因(2.5 秒)。 保存目录默认为**系统"下载"目录**,取不到时回退到桌面(见 [配置项](#配置项))。 ### 功能 3 · 区域截图悬浮(`Alt+1`) 1. 按 `Alt+1`,全屏出现半透明(120/255)黑色遮罩。 2. **按住左键拖选**区域,四周实时显示亮绿色(`Green`)描边选框。 3. 松开鼠标完成截图: - 保存为 `screenshots\shot_yyyyMMdd_HHmmss.png`; - 在**截图的原始屏幕位置**弹出同尺寸悬浮窗,带 **2px 亮青色(`0x00E5FF`)描边**; - 悬浮窗可**点击图片任意位置拖动**,`Esc` 关闭。 4. 拖选范围小于 3×3 像素视为误触,直接取消。 实现要点:遮罩与描边条是同一个 Gui;鼠标事件通过全局 `OnMessage(0x201/0x200/0x202)` 捕获; 截图由 ImagePut 的 `ImagePutFile([x, y, w, h], path)` 完成。 ### 功能 4 · 选中文本透明悬浮(`Alt+2`) 1. 选中文本 → `Alt+2`。 2. 弹出**半透明(210/255)**、浅米色(`0xFFFFF0`)悬浮窗,顶部有一条浅蓝色**拖动条**(20px 高)。 3. 内容**可编辑**、可拉伸(带 `+Resize`,无固定尺寸),编辑区**无纵向滚动条**。 4. 窗口初始定位在**屏幕右上 1/4 象限的中央**(先 `Show("Hide")` 定位再显示,避免闪烁)。 5. 只有顶部拖动条能移动窗口,编辑区保持正常文本输入行为;`Esc` 关闭。 ### 功能 5 · 项目资料本(`Alt+5`) 按项目隔离的多个纯文本笔记,一个项目 = `projects\<项目名>.txt`。 布局: ``` ┌───────────────────────────────────────────────────────────┐ │ ┌─ 项目列表 ─────┐ ┌─ 笔记编辑区(扁平无 3D 框)─────┐ │ │ │ 1 │ │ │ │ │ │ 2 │ │ │ │ │ │ 测试 │ │ │ │ │ │ ... │ │ │ │ │ │ │ │ │ │ │ │[新增][修改][删除]│ │ │ │ │ └────────────────┘ └───────────────────────────────┘ │ └───────────────────────────────────────────────────────────┘ ``` - **左侧列表**:常驻列出 `projects\*.txt` 的**全部**项目(不带扩展名),**单击即打开**对应项目, 无需输入框、无需下拉。窗口缩放时列表与编辑区自适应尺寸。 - **操作按钮**:新增 / 修改 / 删除三个按钮**排在列表下方同一行**,各 46px 宽等宽排列, 行宽 150px 与左侧列表**左右边缘精确对齐**(x = 8 / 60 / 112,右边缘 158)。按钮字号 s10, 两个字不挤不断行;鼠标悬停有 ToolTip 补全语义。 - **新增**:点"新增" → 弹出**自建输入窗**(320×80 客户区,标题栏下方 10px 就是输入框)→ 标题栏给提示 `新增项目 — 输入项目名称` → 输入项目名 → 不存在则创建文件并立即打开, 文件内**自动预填项目信息模板**(见下)→ 输入框默认留空。 - **修改**:在左侧选中一个项目 → 点"修改" → 自建输入窗,标题栏提示 `修改项目名称 — 输入新名称`, 输入框默认填入当前名称并**全选**(直接输入即可整体覆盖)→ 改名文件并刷新列表; 若改的是当前项目,会同步内部路径,后续保存写入新文件;**目标名已存在时弹提示且不做任何改动**。 - **删除**:在左侧选中一个项目 → 点"删除" → **弹出"是/否"二次确认**(提示中列出将被删除的完整文件路径, 默认焦点落在"否",回车不会误删)→ 确认后删除该 `.txt` 并刷新列表。 若删的是当前打开的项目,会同时清空笔记区并解除当前项目绑定(避免后续保存又把它写回来)。 未选中任何项目时点删除/修改只弹提示、不弹确认框。 - **自动保存**:切换项目前、关闭窗口(`Esc` / 标题栏关闭按钮)时自动保存当前笔记。 - **系统标题栏**:窗口保留原生标题栏,因此**原生可拖动、可关闭**。 > 为什么不用自绘标题栏?脚本已经用 `OnMessage(0x201)` 处理截图选区了,而 AutoHotkey v2 中 > **对同一消息号重复注册 `OnMessage` 会覆盖前一个回调**,自绘拖动(`WM_NCLBUTTONDOWN`)会顶掉截图功能。 > 保留系统标题栏是唯一"零冲突"的方案。 #### 新建项目的默认模板 新建项目时会**自动预填一份项目信息模板**,省去每次从零敲字段。模板字段来自 `D:\Documents\归档\文档备份\项目文档` 下 14 份真实项目文档的共同结构(**只取字段骨架,不含任何账号密码**): ``` === 访问地址 === 测试环境 / 正式环境 / 异构(integrate)/ 数据库工具(DB) === 测试环境 === 【服务器】地址、端口 22、账号 root、密码 【数据库】地址、端口 1521、实例名、用户名、密码、管理员 SYS/SYSTEM === 正式环境 === 同上(服务器 + 数据库) === 开发环境(如有)=== 【数据库】+【代码】SVN 路径 === VPN === 接入地址、安全码、账号、密码 === 跳板机 === 【向日葵】识别码、验证码 /【ToDesk】设备代码、密码 === 文件服务器 === 地址、账号、密码、管理员 === ERP / 接口 === 接口地址、EntCode/OrgCode、应用ID/应用密钥、clientid/clientsecret === 其他 === WIFI、企业邮箱、常用链接 ``` - **纯文本排版,不用 Markdown**:项目文件是 `.txt`、编辑框是纯文本控件,`###`/`**`/`-` 之类不会被渲染, 只会变成碍眼的符号。故一级标题用 `=== 标题 ===` 包夹、子块用 `【】`,在纯文本下同样层次分明。 - **想改模板**:直接编辑仓库里的 **`项目模板.txt`**(随仓库提供,内容与内置默认模板一致), 改完即时生效,无需改代码或重新编译;改完提交即可把你的定制也留在仓库里。 (若该文件被删掉,下次新建项目会按内置默认模板重新生成一份。) - 模板只在**文件不存在时**套用;已经存在(含你手动清空过)的项目文件不会被覆盖。 - 已填好内容的字段随意删改,模板只是个起点。 > 关于"以 Markdown 方式浏览/编辑":AutoHotkey 的 `Edit` 控件是纯文本控件,要渲染 Markdown 只能 > 内嵌 WebView2 或自己做 `md → RTF` 转换 + 富文本控件——前者体积与复杂度都很高,后者会变成 > "编辑富文本"而不是"编辑 Markdown",与"轻量"的前提冲突,故**不引入**。 > 若只是想让外部编辑器(Typora / VS Code)能渲染,把项目文件扩展名从 `.txt` 换成 `.md` 即可, > 但那不会改变应用内的阅读体验。 --- ### 功能 6 · 选中多行加引号逗号(`Alt+6`) 把数据库查询结果中**某一列的多行值**快速拼成 SQL `IN (...)` 列表,写回剪贴板后直接 `Ctrl+V` 粘贴。 1. 在任意程序里选中多行文本(如查询结果某列)→ `Alt+6`。 2. 每行被包上**英文半角单引号** `'`,行尾加**英文半角逗号** `,`,**最后一行不加逗号**。 3. 结果**静默写回剪贴板**(不弹窗、不悬浮),可直接粘进 `WHERE col IN ('a','b','c')`。 示例(选中三行 `abc` / `def` / `ghi`): ``` 'abc', 'def', 'ghi' ``` 规则与边界: - 仅取**非空行**(自动跳过首尾及中间的空白行,避免选中末尾换行产生的空串变成 `''`)。 - 单行选中 → `'solo'`(既是首行也是末行,不加逗号)。 - 引号、逗号均为**英文半角**符号。 - 若未选中或复制为空,不改动剪贴板。 > 注意:数据本身含英文单引号(如 `O'Brien`)时不会转义,粘贴进 SQL 需自行处理;如需自动转义可再提。 ### 功能 7 · 轻量链式计算器(`Alt+7`) 以计算为主的小工具:常驻悬浮窗,上半部是历史记录(逐行 `表达式 = 结果`,大字号等宽显示),底部加高的单行输入框(更大字号),回车即算。 1. `Alt+7` 呼出窗口(再按一次或 `Esc` 隐藏),窗口激活、光标自动落在输入框末尾。 2. 输入框里输入算式 → `Enter`(或点「计算」按钮)即算,结果追加进历史;**结果会保留回输入框**、光标停在末尾,方便接着算。 3. **续算**:结果已在输入框里,直接接着输运算符即可(如结果 `3`,再输 `*2` → 输入框成为 `3*2` → 回车得 `6`)。若清空输入框后只输 `*2`,也会自动用上一步结果补上左值。 链式用法示例(正好对应 `1+2 回车=3 *2 回车=6`): ``` 输入 1+2 ↵ 历史: 1+2 = 3 输入框保留: 3 再输 *2 ↵ 输入框变 3*2 → 历史: 3*2 = 6 输入框保留: 6 ``` - 支持:`+ - * /`、`^`(幂)、`()` 括号、小数、负数,如 `1.5*4` → `6`、`2^3` → `8`、`-5+3` → `-2`、`10/4` → `2.5`。 - 历史里显示的是**完整表达式**(含续算时补上的左值),不会出现只有 `*2` 这类缺左值的怪记录。 - 表达式错误(如 `1+`、`*2`、未闭合括号)会在该行显示 `= ⚠ 表达式错误`,且**保留原输入**便于修改,不 crash。 - **记录持久化**:每条计算写入同目录 `calc_history.txt`,下次打开计算器仍在;打开时自动把上一步结果放回输入框,可跨会话续算。点「清空记录」可清空界面与文件。 > `calc_history.txt` 是运行期个人数据,已在 `.gitignore` 中忽略,不会入库。 ### 功能 8 · POST 请求测试(`Alt+8`) 轻量 Postman:固定 **POST**(不做类型选择),请求体与返回结果均为 **JSON 且自动格式化**。 窗口为**左右分区 + 右侧上中下**: ``` ┌──────────┬──────────────────────────────────────────────┐ │ 已保存 │ [地址栏................................] [发送][保存] │ ← 上:地址栏 │ 请求列表 ├──────────────────────────────────────────────┤ │ │ 请求体 · JSON(可编辑) [格式化] │ ← 中:请求体 │ (点选 ├──────────────────────────────────────────────┤ │ 载入) │ 返回结果 · HTTP 200 · 128 ms │ │ │ 返回 JSON(只读,等宽) │ ← 下:返回结果 │ [新建][删除] │ └──────────┴──────────────────────────────────────────────┘ ``` 1. `Alt+8` 呼出窗口(再按一次或 `Esc` 隐藏)。窗口初始尺寸为**工作区(不含任务栏)的 80%、居中**,可自由拉伸;窗口**不置顶**(不会一直盖在其它程序之上)。 2. 右侧填**地址** + **请求体(JSON)** → 「发送」(或 `Ctrl+Enter`)。发送前先**校验请求体是否为合法 JSON**(不合法会明确提示并**取消发送**),合法则自动格式化后发送;返回结果自动格式化为缩进 JSON,状态行显示 **HTTP 状态码 + 耗时**(2xx 绿 / 其它橙 / 失败红)。 3. **格式化**:请求体标签右侧的「格式化」小按钮,可随时把请求体按 JSON 缩进重排;**不是合法 JSON 时会弹框提示出错的「行、列」与原因**(如 `第 5 行,第 7 列:此处可能缺少逗号`)。 4. **保存**:点右侧「保存」→ 输入名称 → 把「地址 + 请求体」存到本地(`post_requests\<名称>.txt`)。左侧列表点选即回填地址与请求体(并格式化显示);「新建」清空右侧,「删除」删掉选中请求(二次确认)。 约定与边界: - 固定请求头:`Content-Type: application/json; charset=utf-8`、`Accept: application/json`、`Accept-Charset: utf-8`。 - 请求体以 **UTF-8 字节**发送(避免中文乱码);响应按 UTF-8 解码(自动去 BOM)。超时:连接 5s / 发送 10s / 接收 20s。 - 请求体非法 JSON → 状态行显示 `未发送 · 请求体不是合法 JSON`,返回区给出**出错行/列**、解析原因与原始请求体,**不发出请求**。 (JSON 解析报错均带位置:`第 N 行,第 M 列:<中文提示>(<原始信息>)`,功能1 `Alt+3` 亦同。) - 返回内容非 JSON 时原样显示,不报错;网络失败(地址错、连不上等)在返回区显示 `请求失败:<原因>`。 - 返回区**只读**,但可选中复制。 > `post_requests/` 是运行期个人数据,已在 `.gitignore` 中忽略,不会入库。 ### 功能 9 · 日历(`Alt+0`) 一块常驻悬浮的月历,把**公历、农历、二十四节气**叠在同一张网格上,可翻月、可跳到任意日期。 全部数据**内置离线**(不联网、不读文件),开箱即用。 窗体布局: ``` ┌ 上月 ── 2026年9月 丙午年·马 ── 今天 ── [跳转输入框] 跳转 ─────────────┐ │ 日 一 二 三 四 五 六 │ │ 1 2 3 初十 初十一 初十二 十三 │ │ ... (今天蓝底高亮) ... │ └──────────────────────────────────────────────────────────────────────┘ ``` - **呼出 / 隐藏**:`Alt+0` 切换;窗口**非置顶**,`Esc` 或标题栏关闭按钮隐藏。 - **每格内容**:右上角是公历日;下方小字依次是——节气(如「立秋」)> 农历初一显示月份(如「八月」)> 农历日(如「十五」);闰月在月份前加「闰」(如「闰六月」)。 - **高亮规则**: - 今天:浅蓝底,文字转蓝。 - **导航**: - **「上月」/「下月」**按钮翻上 / 下一个月; - **「今天」**按钮一键回到当前月并定位今天; - 顶部输入框支持两种格式:`YYYY-MM`(跳到该月)或 `YYYY-MM-DD`(跳到该日),回车或点「跳转」生效; 格式错误、年份超 `1900–2100` 或月份非 `1–12` 时弹提示且**不改变当前视图**(不崩溃)。 - **数据范围**:农历表覆盖 **1900–2100**;二十四节气用天文算法实时计算(约 **2000–2050** 精确,覆盖常用区间)。 - **不落盘**:日历是纯展示工具,不产生任何运行期文件。 > 农历 / 节气全部在脚本内算好,断网也能用。 --- ### 功能 10 · 文本对比(`Alt+9`) 把两段文本做**行级 diff**,左右分栏并排,差异行用颜色标出,可手动点「比对」或勾「自动」实时比对,再用「上一处 / 下一处」在差异之间跳转。 窗体布局(约工作区 90%、居中、**不置顶**、可自由拉伸): ``` ┌ 比对 [✓自动] 上一处 下一处 清空 复制结果 ─────────────────────────────────┐ │ 文本 A(左侧,粘贴到此) │ 文本 B(右侧,粘贴到此) │ │ ┌──────────────────────────────┐ │ ┌──────────────────────────────┐ │ │ │ 可粘贴 / 可编辑的多行输入框 │ │ │ 可粘贴 / 可编辑的多行输入框 │ │ │ └──────────────────────────────┘ │ └──────────────────────────────┘ │ │ ┌─ ListView:行 │ 内容 ───────┐ │ ┌─ ListView:行 │ 内容 ───────┐ │ │ │ 1 alpha │ │ │ 1 alpha │ │ │ │ 2 bravo (红底=删除)│ │ │ 2 XRAY (绿底=新增)│ │ │ │ 3 charlie │ │ │ 3 charlie │ │ │ │ 4 delta (橙底=修改)│ │ │ 4 echo (橙底=修改)│ │ │ └──────────────────────────────┘ │ └──────────────────────────────┘ │ └──────────────────────────────────────────────────────────────────────────┘ ``` 用法: 1. `Alt+9` 呼出窗口(再按一次或 `Esc` 隐藏);窗口**非置顶**,`Esc` 或标题栏关闭按钮隐藏。 2. 左侧输入框粘贴 / 输入「文本 A」,右侧粘贴「文本 B」。 3. 点「比对」生成差异;或勾上「自动」,两侧任一输入变动即时重算(无需手动点)。 4. 差异着色规则(ListView 行自绘,`NM_CUSTOMDRAW`): - **红底** = 仅左侧有(被删除 / 左独有); - **绿底** = 仅右侧有(被新增 / 右独有); - **橙底** = 两侧都有但内容不同(修改,按 LCS 把相邻的删/增配对成一处「修改」)。 5. 「上一处 / 下一处」在**所有差异行**之间循环跳转,左右两个 ListView 同步选中并滚动到该行;点任一 ListView 的行也会联动选中另一侧对应行。 6. 「清空」清掉两侧输入框与差异表;「复制结果」把比对结果按 `= 相同 / - 删除 / + 新增` 前缀写到剪贴板,方便粘进 diff 工具或聊天。 实现要点: - 差异算法是**行级 LCS**(动态规划求最长公共子序列),返回 `= / - / + / ~` 四种操作;相邻的一删一增自动合并为一处「修改(~)」,让「把 bravo 改成 XRAY」显示为单条橙底而非「先删后增」两条。 - 逐行着色**不走 `ListView.Modify("c...")`**(v2 的 ListView 不支持按行设置文字 / 背景色),而是用 `OnMessage(0x4F, DiffOnNotify)` 拦截 `NM_CUSTOMDRAW` 在自绘阶段改 `clrText` / `clrTextBk`。该回调对所有 ListView 共用,靠 `hwnd` 过滤只处理对比框的两个 ListView。 - 窗口尺寸走 `Size` 事件重排(输入框与 ListView 宽度按客户区一半计算),不写死绝对尺寸,缩放时自适应。 > 注意:「自动」模式在任一侧每次按键变更都会重算整份 diff,超大文本(上万行)下可能略有延迟;此时取消「自动」、改完再点「比对」更顺手。 --- ## 目录与文件 ``` dev_tools/ ├── toolkit.ahk 主脚本(全部功能,唯一源文件) ├── toolkit.exe Ahk2Exe 编译产物(独立可运行,已内置运行时与依赖) ├── README.md 本文档 ├── LICENSE MIT License ├── .gitignore 忽略运行期产物 │ ├── ImagePut.ahk 【第三方依赖,已随仓库提供】toolkit.ahk 第 7 行 #Include 它(功能3 截图) │ ├── worklog.txt 【运行期生成】工作记录台账的累积日志 ├── 项目模板.txt 新建项目时预填的模板(可自行编辑定制) ├── screenshots/ 【运行期生成】截图功能输出(shot_*.png) ├── projects/ 【运行期生成】项目资料本的各项目笔记(<项目名>.txt) ├── calc_history.txt 【运行期生成】计算器(功能7)的计算记录 └── post_requests/ 【运行期生成】POST 请求测试(功能8)已保存的请求(<名称>.txt) ``` | 文件 | 是否入库 | 说明 | |---|---|---| | `toolkit.ahk` | ✅ | 唯一源文件 | | `toolkit.exe` | ✅ | 编译产物,免环境运行 | | `README.md` | ✅ | 文档 | | `LICENSE` | ✅ | MIT License | | `.gitignore` | ✅ | 排除运行期产物 | | `ImagePut.ahk` | ✅ | 第三方依赖(v1.12,271 KB),功能 3 必需,随仓库提供 | | `项目模板.txt` | ✅ | 新建项目时预填的模板,可自行编辑定制 | | `worklog.txt` / `screenshots/` / `projects/` / `calc_history.txt` / `post_requests/` | ❌ | 运行期生成的个人数据,已加入 `.gitignore` | --- ## 配置项 全部配置集中在 `toolkit.ahk` 顶部的"配置"区,按需修改即可: | 变量 | 默认值 | 含义 | |---|---|---| | `ToolkitDir` | `A_ScriptDir` | 脚本所在目录,所有相对路径的基准 | | `LogPath` | `\worklog.txt` | 工作记录台账的日志文件 | | `SaveDir` | 系统"下载"目录 → 回退 `A_Desktop` | 功能 2 保存文本的目录 | | `ShotDir` | `\screenshots` | 功能 3 截图的存放目录 | | `ProjectsDir` | `\projects` | 功能 5 项目笔记的存放目录 | | `ProjTplPath` | `\项目模板.txt` | 功能 5 新建项目时预填的模板文件(可自行编辑)| `SaveDir` 通过读取注册表 `HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders` 下的 `{374DE290-123F-4565-9164-39C4925E467B}`(下载目录)获得,并会展开其中的环境变量(如 `%USERPROFILE%`); 读取失败或目录不存在时回退到桌面。 `ShotDir` 与 `ProjectsDir` 若不存在会在启动时自动创建。 --- ## 依赖与编译 ### 运行环境 - **Windows**(脚本使用了 Win32 消息、`explorer.exe /select`、注册表等 Windows 专有特性)。 - **AutoHotkey v2**(源文件基于 **v2.0.27** 开发与验证;v1 语法不兼容)。 ### 第三方依赖:ImagePut `toolkit.ahk` 第 7 行 `#Include ImagePut.ahk`,**功能 3(`Alt+1` 区域截图)** 依赖它的 `ImagePutFile()` 做屏幕区域抓取并编码为 PNG(第 649 行)。 - 上游项目:(本仓库随包提供 **v1.12**,见文件头 `; Version: 1.12`)。 - **已纳入本仓库**,与 `toolkit.ahk` 同目录,**clone 下来即可直接运行/编译源码**,无需额外下载。 - 缺失时的表现:脚本加载即报 `#Include` 找不到文件;或 `Alt+1` 截图时提示"截图失败"。 ### 编译为 exe 用 [Ahk2Exe](https://www.autohotkey.com/docs/v2/Scripts.htm#ahk2exe)(随 AutoHotkey 安装,含 GUI 与命令行版)编译。 本机安装路径为 `C:\Program Files\AutoHotkey\Compiler\Ahk2Exe.exe`: ```bash "C:\Program Files\AutoHotkey\Compiler\Ahk2Exe.exe" \ /in toolkit.ahk \ /out toolkit.exe \ /base "C:\Program Files\AutoHotkey\v2\AutoHotkey64.exe" ``` - `/base` 指定用 v2 的 64 位运行时作为底包(不指定时会用默认底包,可能编译出 v1 兼容的可执行文件)。 - 生成的 exe 是**独立可执行文件**,已打包 AutoHotkey 运行时。 - 打包时会一并内联 `#Include` 的 `ImagePut.ahk`,所以 **exe 不依赖外部 `ImagePut.ahk`**。 --- ## 常见问题 **Q:按热键没反应?** A:分两种情况。① 脚本没在运行——检查任务管理器里是否有 `AutoHotkey64.exe`(`toolkit.exe` 运行时进程名也是它)。 ② 当前前台窗口是**以管理员权限运行**的程序(例如管理员启动的终端、部分游戏/开发工具)——Windows 的 UIPI 会阻止 普通权限脚本向其发送按键/鼠标消息。此时请以管理员身份运行 `toolkit.exe`。 **Q:`Alt+S` / `Alt+O` 没反应?** A:这两个热键**只在"工作记录"窗口激活时**生效,请先 `Alt+R` 唤出台账。 **Q:`Alt+1` 截图提示"截图失败"?** A:`ImagePut.ahk` 缺失(仓库已自带,确认没被误删;被删了从仓库恢复或去 下载)。使用 `toolkit.exe` 时不会出现该问题。 **Q:`Alt+2` / `Alt+3` 悬浮窗没出来?** A:这两个功能依赖"当前选中的文本"。请先选中文本再按热键;脚本会先清空剪贴板再 `Ctrl+C`, 若 `ClipWait` 超时(1 秒内没抓到内容)会静默返回,这是设计行为。 **Q:项目资料本里"新增"/"修改"点了没弹窗?** A:该问题已在当前版本修复。历史原因是本机 AutoHotkey 2.0.27 的 `InputBox()` 返回的是**对象**(文本在 `.Value`) 而非字符串,`Trim(InputBox(...))` 会抛异常并被 `try/catch` 静默吞掉,表现为"点击无反应"。 现在的做法是走兼容层 `ProjInput()`(内部统一用 `ProjResultText()` 归一返回值): 对象取 `.Value`、字符串直接用,取消/异常统一返回空串。 > 实测结论:本机 **`MsgBox()` 正常返回字符串**(`"Yes"`/`"No"`),只有 `InputBox()` 返回对象,两者不要混为一谈。 **Q:点"删除"为什么没直接删掉?** A:删除是不可恢复操作,必须走二次确认("是/否"对话框,默认焦点在"否")。确认框里会列出将被删除的完整文件路径。 **Q:新增/修改的输入窗为什么不用系统 InputBox?** A:系统的 `InputBox` 会为"提示文字"预留一整行高度,提示置空也照样留白, 于是**标题栏与输入框之间出现一大段空白**。现在改用**自建输入窗**(`ProjAsk`): 提示语放标题栏、输入框紧贴标题栏下方 10px,客户区只有 320×80;窗口归属主窗口 (`+Owner`,不占任务栏、始终盖在主窗口之上),弹出期间主窗口整体禁用(简易模态)。 > 顺带记录:本机 2.0.27 的 `InputBox()` 返回的是**对象**(文本在 `.Value`)且不支持 `Owner` 选项, > 而 `MsgBox()` 正常返回字符串(`"Yes"`/`"No"`)—— 这也是不再使用 `InputBox` 的原因之一。 **Q:新建项目时默认带出来的那套字段能改吗?** A:能。编辑仓库里的 **`项目模板.txt`** 即可(**已随仓库提供**,内容与 README 里列的一致)。 改完即时生效;它是版本库里的文件,改完提交就会保留你的定制(不要了 `git checkout` 即可还原)。 若把它删掉,下次新建项目会按内置默认模板重新生成一份。 **Q:模板/笔记能用 Markdown 吗?应用内会渲染吗?** A:不会渲染。编辑框是 AutoHotkey 的纯文本 `Edit` 控件,Markdown 语法只会原样显示成符号, 所以内置模板刻意用了纯文本排版(`=== 标题 ===` + `【子块】`)。要用 Markdown 请到外部编辑器里写 (Typora / VS Code);若希望外部编辑器能渲染,可把项目文件扩展名改成 `.md`(应用内体验不变)。 **Q:悬浮窗关不掉?** A:所有悬浮窗都监听 `Esc`。若窗口未激活(例如焦点在别的程序),请先点一下悬浮窗再按 `Esc`。 **Q:`worklog.txt` 中文乱码?** A:文件以 UTF-8(首次写入带 BOM)编码。请用支持 UTF-8 的编辑器查看。 --- ## 开发说明 ### 代码组织 单文件 `toolkit.ahk`,按功能分段,段首有 `====` 标题注释: | 区段 | 内容 | |---|---| | 配置 | `ToolkitDir` / `LogPath` / `SaveDir` / `ShotDir` / `ProjectsDir` / `ProjTplPath` 与全局状态变量 | | JSON 引擎 | `JSONTrue` / `JSONFalse` / `JSONNull` / `JSONParser` / `SerializeJSON`(自包含,零依赖)| | 功能 0 | 工作记录台账:`ToggleLog` / `SaveLog` / `OpenLog` / `UpdateScroll` / `CaretToEnd` | | 功能 1 | `FormatJsonFloat` | | 功能 2 | `SaveTextFile` | | 功能 3 | `StartCapture` / `PositionBars` / `ShowShotFloat` | | 功能 4 | `TextFloat` | | 全局鼠标 | `OnLButtonDown` / `OnMouseMove` / `OnLButtonUp`(截图选区 + 浮窗拖动)| | 功能 5 | 项目资料本:`EnsureProjGui` / `RefreshList` / `SwitchProject` / `SaveProj` / `ProjDefaultTpl` / `ProjTpl` / `NewProject` / `RenameProject` / `RenameTo` / `DeleteProject` / `ProjAsk` / `EnsureProjAskGui` / `FinishProjAsk` / `ProjConfirm` / `ProjMsg` | | 功能 6 | `QuoteLines`(选中多行加英文单引号与逗号,末行无逗号 → 静默写回剪贴板)| | 功能 7 | `CalcToggle` / `CalcCompute` / `CalcClear`(轻量链式计算器:回车即算、结果续算、历史持久化)| | 功能 8 | `TogglePostTester` / `EnsurePostGui` / `PostDoSend` / `PostDelete`(POST 请求测试:地址 + 请求体 + 返回,JSON 自动格式化)| | 功能 9 | 日历:`EnsureCalGui` / `CalRender` / `CalStep` / `CalGoToday` / `CalJump`;农历与节气:`Solar2Lunar` / `GetSolarTerm` / `LunarMonthLabel` / `LunarDayLabel` | | 功能 10 | 文本对比:`EnsureDiffGui` / `DiffLines` / `DiffCompare` / `DiffGoto` / `DiffClear` / `DiffCopy` / `DiffSyncFromLeft` / `DiffSyncFromRight` / `DiffOnResize` / `DiffOnNotify`(行级 LCS 差异、ListView 自绘着色、上一处 / 下一处)| ### 关键实现约定 - **浮窗拖动**:浮窗把自身 hwnd(以及允许拖动的子控件 hwnd)登记到全局 `dragHwnds` 映射; `OnLButtonDown` 命中时发 `WM_NCLBUTTONDOWN(HTCAPTION)`(`PostMessage(0xA1, 2, ...)`)交给系统拖拽。 这样**不用重写鼠标逻辑、不破坏编辑区的正常选中行为**。 - **消息注册冲突**:`0x201 / 0x200 / 0x202` 已被截图功能占用。**新增任何 `OnMessage` 前先 grep 现有注册**, 同消息号重复注册会覆盖旧回调。 - **按钮事件一律 `SetTimer(handler, -1)` 延迟一拍**(项目资料本的三个按钮、输入窗的确定/取消/Esc/关闭都是如此)。 原因:处理函数里要 `Show()` 自有窗口,而 `Show()` 会泵消息 —— 如果处理函数还在栈上时有一个嵌套事件 (例如用户/探针在窗口刚出现时点了"取消")被派发进来,AutoHotkey 之后会**永久丢弃这个按钮的 Click 通知** (表现为该按钮从此点不动,其它按钮正常)。先返回、下一消息周期再干活即可彻底避开嵌套。 - **高 DPI / 尺寸自适应**:所有工具窗通过 `Size` 事件处理函数在窗口缩放时重新 `Move()` 子控件, 不要写死绝对坐标(仅初始坐标写死)。 - **`#Warn LocalSameAsGlobal, Off`**:`ImagePut.ahk` 内部存在 `global e` 与 `catch OSError as e` 的同名冲突, 会触发 `#Warn` 模态提示并阻塞截图流程。该冲突对库功能无影响,故仅关闭此告警类别,其余 `#Warn` 检查保持开启。 ### 修改后的自检 改完 `toolkit.ahk` 后至少做一次**加载校验**(能发现全部语法/解析错误): ```bash cd <脚本目录> timeout 6 "C:\Program Files\AutoHotkey\v2\AutoHotkey64.exe" /ErrorStdOut toolkit.ahk 2>err.txt echo "exit=$? (期望 124 = 常驻运行) stderr=$(wc -c < err.txt) 字节 (期望 0)" cat err.txt taskkill //F //IM AutoHotkey64.exe ``` `exit=124`(被 `timeout` 杀掉)+ stderr 空 = 脚本干净加载并常驻;非 0 且 stderr 有内容即报错位置。 由于工具集大量依赖 GUI 交互(热键、弹窗、控件事件),**建议改完后手工跑一遍相关功能**; 纯加载校验只能覆盖语法层。 --- ## 许可 本项目采用 **MIT License**,Copyright (c) 2026 Dylan,全文见仓库根目录的 [`LICENSE`](LICENSE)。 使用到的第三方库 `ImagePut.ahk` 版权归其作者所有(), 其授权条款以上游仓库为准,不在本项目的 MIT 许可覆盖范围内。