Skip to content

Commit 2c9fb5b

Browse files
committed
feat: 增加 .vbs 脚本支持,VBS 优先于 BAT
- 后端: 新增 parse_vbs_metadata 解析 VBS 单引号注释元数据 - 扫描: 收集 .bat/.vbs,同目录同时存在时只保留 .vbs - 执行: /api/run-bat 兼容 .vbs(通过 wscript.exe 运行) - 前端: scripts + type 字段,卡片徽章区分 BAT/VBS - 元数据注入: inject_bat_meta 自动跳过 .vbs 文件 - 文档: README/AGENTS/CLAUDE 同步更新
1 parent 79f7d83 commit 2c9fb5b

11 files changed

Lines changed: 566 additions & 68 deletions

File tree

‎AGENTS.md‎

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,27 @@
22

33
## 项目定位
44

5-
一个轻量级 Windows 脚本启动台。扫描指定目录下所有同时包含 `.bat` 和 `readme.md` 的子目录,在浏览器中提供现代化的查找、预览和一键启动体验。
5+
一个轻量级 Windows 脚本启动台。扫描指定目录下所有同时包含脚本文件(`.bat` 或 `.vbs`)和 `readme.md` 的子目录,在浏览器中提供现代化的查找、预览和一键启动体验。
66

7-
并非局限于 Python 脚本——任何能通过 `.bat` 启动的项目都能接入。
7+
并非局限于 Python 脚本——任何能通过 `.bat` / `.vbs` 启动的项目都能接入。
88

99
## 脚本接入规范
1010

1111
ScriptDeck 扫描目录时**只收集同时满足以下两点的子目录**:
1212

13-
1. 目录下至少有一个 `.bat` 文件
13+
1. 目录下至少有一个 `.bat` 或 `.vbs` 文件
1414
2. 目录下存在 `readme.md` 文件
1515

16-
`.bat` 文件建议使用绝对路径(含 Python 解释器路径),避免依赖系统环境变量。`readme.md` 第一行作为脚本标题、第二行作为简要描述。
16+
**VBS 优先规则**:当同一目录同时存在 `.vbs` 和 `.bat` 时,只提取 `.vbs` 文件。
17+
18+
`.bat` 文件建议使用绝对路径(含 Python 解释器路径),避免依赖系统环境变量。`.vbs` 文件使用 `WScript.Shell` 启动目标程序。`readme.md` 第一行作为脚本标题、第二行作为简要描述。
19+
20+
### 内嵌元数据(可选)
21+
22+
脚本文件头部支持内嵌元数据,优先级高于 readme 推导:
23+
24+
- `.bat`:`@echo off` 之后用 `:: title:` / `:: desc:`(或 `REM` 风格)注释
25+
- `.vbs`:用 `' title:` / `' desc:` 单引号注释(VBS 标准注释风格)
1726

1827
## 快速启动
1928

@@ -41,18 +50,20 @@ python main.py
4150
### 后端 (`main.py`)
4251

4352
单文件 Flask 应用。核心逻辑:
44-
- `scan_directory()` — 递归扫描 `scan_root`,收集每子目录下的 `.bat` 和 `readme.md`
45-
- 执行 bat 时用 `subprocess.Popen` + `cmd /k chcp 65001` 确保 UTF-8 不乱码
53+
- `scan_directory()` — 递归扫描 `scan_root`,收集每子目录下的 `.bat` / `.vbs` 和 `readme.md`;同目录同时存在时只保留 `.vbs`(VBS 优先)
54+
- `parse_bat_metadata()` — 解析 `.bat` 头部的 `:: title:` / `:: desc:` 元数据
55+
- `parse_vbs_metadata()` — 解析 `.vbs` 头部的 `' title:` / `' desc:` 元数据
56+
- 执行脚本:`.bat` 用 `subprocess.Popen` + `cmd /k chcp 65001` 确保 UTF-8 不乱码;`.vbs` 用 `wscript.exe` 执行
4657

4758
API 路由:
4859
| 路由 | 方法 | 说明 |
4960
|------|------|------|
5061
| `/` | GET | 渲染主页面 |
5162
| `/api/scan` | GET | 扫描并返回所有脚本数据 |
5263
| `/api/set-root` | POST | 修改扫描根目录 |
53-
| `/api/exclude-bats` | GET/POST | 查询/更新 bat 排除规则 |
64+
| `/api/exclude-bats` | GET/POST | 查询/更新脚本文件名排除规则 |
5465
| `/api/exclude-script` | POST | 添加/移除脚本排除 |
55-
| `/api/run-bat` | POST | 在新建 cmd 窗口中执行 .bat |
66+
| `/api/run-bat` | POST | 执行脚本(.bat 在新 cmd 窗口 / .vbs 通过 wscript.exe) |
5667
| `/api/open-folder` | POST | 在资源管理器中打开文件夹 |
5768

5869
### 前端
@@ -72,7 +83,7 @@ API 路由:
7283
"folder": "ai/bacth_ai",
7384
"folder_name": "bacth_ai",
7485
"parent": "ai",
75-
"bats": [{"name": "xxx.bat", "path": "C:\\..."}],
86+
"scripts": [{"name": "xxx.bat", "path": "C:\\...", "type": "bat", "meta": {"title": "...", "desc": "..."}}],
7687
"readme": {"name": "readme.md", "path": "...", "content": "..."}
7788
}
7889
],
@@ -84,7 +95,7 @@ API 路由:
8495

8596
- **Hero** — 居中渐变标题 + 环境光晕背景
8697
- **侧边栏** — 品牌区、搜索框、全部/收藏/最近标签、可折叠目录树
87-
- **脚本卡片** — 渐变 Lucide 图标(按关键词自动匹配)、标题、路径、README 摘要、BAT/README 徽章、运行/收藏按钮
98+
- **脚本卡片** — 渐变 Lucide 图标(按关键词自动匹配)、标题、路径、README 摘要、BAT/VBS/README 徽章、运行/收藏按钮
8899
- **详情面板** — 点击卡片右侧展示:完整路径、操作按钮、README 渲染、同目录脚本列表
89100
- **小火箭** — 滚动超过 10px 显示,点击触发升空动画后回到顶部
90101
- **收藏 / 最近运行** — `localStorage` 持久化
@@ -99,7 +110,7 @@ API 路由:
99110

100111
## 平台限制
101112

102-
- **仅 Windows**:依赖 `.bat`、`cmd`、`explorer.exe`
113+
- **仅 Windows**:依赖 `.bat`、`.vbs`(`wscript.exe`)、`cmd`、`explorer.exe`
103114
- 路径使用反斜杠,经 `os.path.normpath` 标准化
104115

105116
## 开发笔记
@@ -113,7 +124,8 @@ API 路由:
113124

114125
```
115126
ScriptDeck/
116-
├── main.py # Flask 后端(单文件)
127+
├── main.py # Flask 后端(单文件,含 bat/vbs 元数据解析)
128+
├── inject_bat_meta.py # 批量注入 bat 元数据(从 readme 读取)
117129
├── config.json # 运行时配置
118130
├── requirements.txt # 依赖(仅 Flask)
119131
├── templates/
@@ -123,7 +135,7 @@ ScriptDeck/
123135
├── screenshots/
124136
│ └── index.png # 界面截图
125137
├── css/
126-
│ └── app.css # 完整样式表
138+
│ └── app.css # 完整样式表(含 .badge.vbs)
127139
└── js/
128-
└── app.js # 前端逻辑
140+
└── app.js # 前端逻辑(scripts + type 字段)
129141
```

‎CLAUDE.md‎

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
ScriptDeck(脚本中台)— a lightweight Windows script launcher. It scans a directory tree for subfolders containing a script file (`.bat` or `.vbs`) and `readme.md`, then serves a browser UI to search, preview, and launch them. Not limited to Python — any project runnable via `.bat` / `.vbs` works. When a subfolder has both `.vbs` and `.bat`, only the `.vbs` is kept (VBS priority).
8+
9+
## Commands
10+
11+
```bash
12+
# Install dependency
13+
pip install flask
14+
15+
# Run the server (visit http://127.0.0.1:5000)
16+
python main.py
17+
18+
# Batch-inject bat metadata (:: title: / :: desc:) from readme.md files
19+
python inject_bat_meta.py
20+
```
21+
22+
No test suite, linter, or CI exists. Flask runs with `debug=False`.
23+
24+
## Architecture
25+
26+
### Backend — `main.py` (single file)
27+
28+
- `scan_directory()` — recursive `os.walk` over `scan_root`, collects `.bat`/`.vbs`/`readme.md` per subfolder, applies exclude rules. **VBS priority rule**: when a subfolder has both `.vbs` and `.bat`, only `.vbs` is kept. Returns a sorted list of folder-grouped items, each script carrying a `type` (`"bat"`/`"vbs"`) discriminator.
29+
- `parse_bat_metadata()` — reads the first 10 lines of a `.bat` and extracts `:: title:` / `:: desc:` (or `REM` style) comments immediately after `@echo off`.
30+
- `parse_vbs_metadata()` — reads the first 10 lines of a `.vbs` and extracts `' title:` / `' desc:` comments (VBS single-quote style), stopping at the first non-comment line.
31+
- `_read_file_with_encoding()` — tries `utf-8-sig`, `utf-8`, `gbk`, `gb2312`, `gb18030` to handle Chinese-encoded files; falls back to `utf-8` with `errors='replace'`.
32+
- All mutating APIs (`/api/set-root`, `/api/exclude-bats`, `/api/exclude-script`) write `config.json` then re-scan and return fresh data in one response.
33+
- Global `CFG` dict is reloaded from disk at the start of each stateless request (`/api/scan`) and re-saved after mutations; no in-memory cache drift.
34+
35+
### Frontend — vanilla JS, no framework
36+
37+
- `static/js/app.js` — all client logic: state (`appState`), rendering (`render*`), event delegation, `localStorage` persistence for favorites/recent/theme/view-mode.
38+
- `static/css/app.css` — full stylesheet (~1560 lines), CSS custom-property-driven glassmorphism design system.
39+
- `templates/index.html` — Jinja2 template; vendor libs (Lucide icons, Marked.js) loaded locally from `static/js/vendor/`.
40+
41+
### Title / description resolution priority
42+
43+
When displaying a script card, title and summary are resolved in this order (see `flattenScripts()`):
44+
- **Title**: script meta `title:` → `readme.md` first line → filename (sans `.bat`/`.vbs`)
45+
- **Summary**: script meta `desc:` → first 3 lines of readme joined with `·`
46+
47+
### API routes
48+
49+
| Route | Method | Purpose |
50+
|-------|--------|---------|
51+
| `/` | GET | Render main page |
52+
| `/api/scan` | GET | Reload config, re-scan, return all items |
53+
| `/api/set-root` | POST | Change `scan_root` |
54+
| `/api/exclude-bats` | GET/POST | Query/update script filename exclude patterns |
55+
| `/api/exclude-script` | POST | Add/remove single-script exclusion by full path |
56+
| `/api/run-bat` | POST | Run script — `.bat` in a new `cmd` window (`chcp 65001`), `.vbs` via `wscript.exe` |
57+
| `/api/open-folder` | POST | Open folder in `explorer.exe` |
58+
59+
## Platform Constraints
60+
61+
- **Windows-only**: depends on `.bat`, `.vbs` (`wscript.exe`), `cmd`, `explorer.exe`, and Windows registry (see `add_startup.reg` / `remove_startup.reg`).
62+
- Paths use backslashes, normalized via `os.path.normpath`.
63+
- `startup.vbs` and `add_startup.reg` register a run-on-boot entry using a hardcoded Anaconda Python path — these are local conveniences, not portable.
64+
65+
## Configuration — `config.json`
66+
67+
- `scan_root` — directory to walk.
68+
- `exclude_dirs` — folder names to skip (also skips any dot-prefixed folder in `scan_directory`).
69+
- `exclude_bats` — filename patterns (`*`, `?`, `%`) that filter out script files (applies to both `.bat` and `.vbs`).
70+
- `exclude_scripts` — full paths to exclude specific scripts.
71+
72+
## Dev Notes
73+
74+
- Template edits require a server restart; CSS/JS changes only need a browser refresh.
75+
- Lucide icon names per script are chosen by keyword matching in `iconFor()` (`app.js`); default is `zap`.
76+
- Card colors are a deterministic hash of the folder+filename into a fixed palette (`colorFor()`).
77+
- README content is truncated to 3000 chars server-side; both metadata parsers (bat / vbs) only read the first 10 lines.
78+
- `inject_bat_meta.py` only injects `:: title:`/`:: desc:` into `.bat` files; it skips `.vbs` files (identified by the `type` field).

‎README.md‎

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ScriptDeck(脚本中台)
22

3-
> 给你的 Python 脚本一个家 —— 自动扫描目录中的 `.bat` 和 README 文档,在漂亮的浏览器界面中一键查找、预览、启动。不只 Python,任何能用 .bat 跑起来的项目都行。
3+
> 给你的 Python 脚本一个家 —— 自动扫描目录中的 `.bat` / `.vbs` 和 README 文档,在漂亮的浏览器界面中一键查找、预览、启动。不只 Python,任何能用 .bat / .vbs 跑起来的项目都行。
44
55
![Python](https://img.shields.io/badge/Python-3.8+-blue?logo=python)
66
![Platform](https://img.shields.io/badge/Platform-Windows-lightgrey?logo=windows)
@@ -10,9 +10,11 @@
1010

1111
## 💡 工作原理
1212

13-
ScriptDeck 不关心你的脚本是什么语言。它只看一件事:**你指定的目录下,哪些子目录同时有 `.bat` 文件和 `readme.md` 文件。**
13+
ScriptDeck 不关心你的脚本是什么语言。它只看一件事:**你指定的目录下,哪些子目录同时有脚本文件(`.bat` 或 `.vbs`)和 `readme.md` 文件。**
1414

15-
找到后,自动整理成卡片界面 —— bat 负责启动,readme 负责说明。点一下"运行",脚本就在新窗口中跑起来了。
15+
找到后,自动整理成卡片界面 —— 脚本文件负责启动,readme 负责说明。点一下"运行",脚本就在新窗口中跑起来了。
16+
17+
> 💡 **VBS 优先**:当同一目录同时存在 `.vbs` 和 `.bat` 时,只提取 `.vbs` 文件。
1618
1719
### 接入一个新脚本,只需三步
1820

@@ -32,14 +34,24 @@ C:\Users\WIN11\.workbuddy\binaries\python\versions\3.13.12\python.exe C:\project
3234

3335
> ⚠️ **注意**:多 Python 环境下,bat 文件里**必须用绝对路径**指定 Python 解释器,避免跑到系统默认版本。
3436
37+
你也可以用 `.vbs` 来启动(适合需要隐藏命令窗口、只显示 GUI 的场景),比如 `run.vbs`:
38+
39+
```vbs
40+
' title: 时间戳生成工具
41+
' desc: 根据开始日期+天数自动计算结束日期,批量生成 UNIX 时间戳
42+
Set shell = CreateObject("WScript.Shell")
43+
scriptPath = Left(WScript.ScriptFullName, InStrRev(WScript.ScriptFullName, "\") - 1)
44+
shell.Run """C:/path/to/pythonw.exe"" -u """ & scriptPath & "\main.py""", 0, False
45+
```
46+
3547
`readme.md` 长这样:
3648

3749
```md
3850
数据报表分析
3951
读取 Excel 数据并生成周报图表
4052
```
4153

42-
ScriptDeck 会把第一行作为卡片标题,第二行作为描述摘要。后面的内容会完整渲染在详情面板里。
54+
ScriptDeck 会把第一行作为卡片标题、第二行作为描述摘要。脚本文件头部也支持内嵌元数据(`:: title:` / `' title:` 风格),优先级高于 readme。
4355

4456
---
4557

@@ -117,7 +129,7 @@ ScriptDeck 启动中...
117129
|------|------|
118130
| `scan_root` | 扫描根目录,改成你自己放脚本的目录 |
119131
| `exclude_dirs` | 跳过这些文件夹 |
120-
| `exclude_bats` | 跳过这些 bat 文件(支持 `*` `?` 通配符) |
132+
| `exclude_bats` | 跳过这些脚本文件(支持 `*` `?` 通配符,对 `.bat` / `.vbs` 均生效) |
121133
| `exclude_scripts` | 按完整路径跳过特定脚本 |
122134
| `host` | 默认 `127.0.0.1`,想局域网访问改成 `0.0.0.0` |
123135
| `port` | 默认 `5000` |
@@ -148,9 +160,9 @@ ScriptDeck 启动中...
148160
|------|------|------|
149161
| `/api/scan` | GET | 重新扫描目录 |
150162
| `/api/set-root` | POST | 修改扫描根目录 |
151-
| `/api/exclude-bats` | GET/POST | 查询或更新 bat 排除规则 |
163+
| `/api/exclude-bats` | GET/POST | 查询或更新脚本排除规则 |
152164
| `/api/exclude-script` | POST | 添加/移除脚本排除 |
153-
| `/api/run-bat` | POST | 运行指定 bat 脚本 |
165+
| `/api/run-bat` | POST | 运行脚本(.bat 在新 cmd 窗口 / .vbs 通过 wscript.exe) |
154166
| `/api/open-folder` | POST | 在资源管理器中打开文件夹 |
155167

156168
---
@@ -164,7 +176,7 @@ ScriptDeck 启动中...
164176
| 图标 | Lucide Icons(CDN) |
165177
| Markdown | Marked.js(CDN) |
166178
| 设计 | CSS 自定义属性 + Glassmorphism |
167-
| 运行环境 | 仅 Windows(依赖 .bat / cmd) |
179+
| 运行环境 | 仅 Windows(依赖 .bat / .vbs / cmd) |
168180

169181
---
170182

@@ -186,17 +198,19 @@ A:把文件夹名加到 `config.json` 的 `exclude_dirs` 里,重启。
186198
A:当前根据脚本名关键词自动匹配 Lucide 图标。改 `static/js/app.js` 里的 `iconFor()` 函数就行。
187199

188200
**Q:为什么我的脚本没出现在页面上?**
189-
A:检查两点:① 文件夹下是否同时有 `.bat` 和 `readme.md` ② 文件夹是否在 `scan_root` 下且没被 `exclude_dirs` 排除。
201+
A:检查两点:① 文件夹下是否同时有脚本文件(`.bat` 或 `.vbs`)和 `readme.md` ② 文件夹是否在 `scan_root` 下且没被 `exclude_dirs` 排除。
190202

191203
---
192204

193205
## 📁 文件结构
194206

195207
```
196208
ScriptDeck/
197-
├── main.py # Flask 后端
209+
├── main.py # Flask 后端(含 bat/vbs 元数据解析)
210+
├── inject_bat_meta.py # 批量注入 bat 元数据工具
198211
├── config.json # 运行时配置
199-
├── requirements.txt # 依赖
212+
├── requirements.txt # 依赖(仅 Flask)
213+
├── CLAUDE.md # Claude Code 项目指南
200214
├── AGENTS.md # AI 项目上下文
201215
├── README.md # 你正在看的这份文档
202216
├── templates/
@@ -206,9 +220,10 @@ ScriptDeck/
206220
├── screenshots/
207221
│ └── index.png # 界面截图
208222
├── css/
209-
│ └── app.css # 样式表
223+
│ └── app.css # 样式表(含 .badge.vbs)
210224
└── js/
211-
└── app.js # 前端逻辑
225+
├── vendor/ # 本地依赖(Lucide、Marked)
226+
└── app.js # 前端逻辑(scripts + type 字段)
212227
```
213228

214229
---

‎config.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"ceshi",
1616
"config",
1717
"neirong",
18+
"scripts",
1819
"wwwroot"
1920
],
2021
"exclude_bats": [

0 commit comments

Comments
 (0)