CodeNote
以题目为中心的编程学习工作区 VS Code 扩展:左边看题面、写笔记、留画板,右边用 VS Code 原生编辑器写代码。
当前版本:0.9.3
CodeNote (English)
Keep each piece of code together with the thinking that produced it.
CodeNote is a VS Code extension for competitive programming / algorithm practice. Instead of
scattering screenshots, notes and source files across folders, it keeps them in one place —
per problem:
|
|
| Statement |
Markdown, with one-keystroke paste from any online judge (HTML → Markdown, KaTeX math supported) |
| Notes |
Markdown per problem, plus multiple notes per code file |
| Whiteboard |
Flowcharts and derivations, exportable as PNG straight into the statement |
| Screenshots |
Paste into the statement or annotate an existing screenshot with boxes / arrows / text |
| Code |
Your own .c / .cpp / .py files, opened in VS Code's native editor |
Everything is stored as plain Markdown + JSON in a .codenote/ folder next to your code,
so it works with git, other editors and any tool that can read text. No account, no cloud, no telemetry.
The interface is currently Chinese only. The core workflow (statement / notes / whiteboard /
annotations / file association) is language-neutral, but UI strings are not translated yet —
issues and feedback in English are welcome.
Bulk import existing code: right-click the project → 批量把代码文件建成条目 to create one entry
per unassociated source file at once.
1. 它解决什么问题
刷题时代码都在,但过几天回头看,想不起这题问的是什么;题面截图、思路笔记和代码又散落在不同地方。
CodeNote 把「题面 + 笔记 + 画板」和「代码文件」组织到同一个题目下:
- 每个项目(工作区文件夹)可以有任意多道题;
- 每道题有稳定 ID,改名不影响身份;
- 一道题可以关联多个代码文件(C / C++ / Python …),代码文件仍留在原位置,CodeNote 只记录相对路径;
- 所有题目资料保存在项目内的
.codenote/ 目录,纯文本、可进 git。
2. 安装
从扩展商店安装(推荐):
在 VS Code 的扩展面板搜索 CodeNote,或直接执行:
code --install-extension codenote-dev.codenote-vscode
商店页:https://marketplace.visualstudio.com/items?itemName=codenote-dev.codenote-vscode
手动安装 vsix(离线 / 想用最新构建):
npm run package # 生成 codenote-vscode-<version>.vsix
code --install-extension codenote-vscode-<version>.vsix
扩展 ID 是 codenote-dev.codenote-vscode:codenote-dev 是发布者,
codenote-vscode 是内部名(codenote 已被另一个扩展占用)。
商店里显示的名字是 CodeNote。
3. 环境要求
| 项 |
要求 |
| 系统 |
Windows(当前开发与验证环境) |
| VS Code |
1.90 及以上(实测 1.145.0) |
| Node.js |
18+(实测 v24.15.0) |
| 依赖 |
仅 TypeScript 与类型定义,无运行时第三方依赖 |
4. 快速开始
cd D:\code-vscode-tools\problem-mate
npm install
npm run compile
3.1 在 VS Code 里调试
用 VS Code 打开 D:\code-vscode-tools\problem-mate,按 F5。
.vscode/launch.json 会自动:
- 编译 TypeScript;
- 启动 Extension Development Host;
- 把
samples/demo-workspace 作为演示项目打开(里面预置了 3 道题)。
启动后:
- 活动栏出现 CodeNote 图标 → 点击打开「项目与题目」侧边栏;
- 展开项目 → 点击题目 → 左侧打开题目工作区(题面 / 笔记 / 画板),右侧用原生编辑器打开该题关联的代码文件;
- 用工作区顶部的
‹ 1 / 3 › 切换上一题 / 下一题。
3.2 在自己的刷题仓库里使用
- 用 VS Code 打开你的刷题仓库文件夹(例如
D:\code-vscode);
- 命令面板执行 CodeNote: 初始化项目,会在仓库根目录创建
.codenote/;
- 在编辑器中打开某个
.c 文件,执行 CodeNote: 从当前文件新建题目;
- 或者执行 CodeNote: 新建题目,再在题目工作区底部点 + 关联 选择代码文件。
初始化只新增一个 .codenote/ 目录,不会改动任何已有代码文件。
5. 命令
| 命令 |
说明 |
CodeNote: 初始化项目 |
在工作区文件夹中创建 .codenote/ |
CodeNote: 添加项目文件夹… |
把另一个文件夹加入工作区并初始化(多项目) |
CodeNote: 新建题目 |
输入题目名称创建题目;默认同时生成代码文件并打开(0.5.12) |
CodeNote: 从当前文件新建题目 |
用当前编辑器文件作为关联代码创建题目 |
CodeNote: 打开题目工作区 |
选择题目并打开 |
CodeNote: 重命名题目 |
只改显示名,ID / 目录不变 |
CodeNote: 删除题目 |
删除题目数据目录到回收站,不动代码文件 |
CodeNote: 关联代码文件到题目 |
多选文件,路径必须在项目文件夹内 |
CodeNote: 取消关联代码文件 |
只删引用,不动文件 |
CodeNote: 重新定位代码文件 |
文件移动 / 重命名后,保留 codeFileId 重新关联 |
CodeNote: 检查代码文件关联 |
检查缺失路径与跨题目重复关联 |
CodeNote: 在资源管理器中显示题目数据 |
定位 .codenote/problems/<id> |
CodeNote: 导出题目为 Markdown |
把题面 / 全部笔记 / 代码文件与附件信息导出成一份 .md(0.5.6) |
CodeNote: 搜索题目 |
跨题目搜题目名 / 题面 / 笔记正文,结果显示行号与片段(0.5.8) |
CodeNote: 筛选题目 / 题目排序方式 |
全部 / 代码文件缺失 / 有笔记 / 有附件 / 有标签;排序按顺序 / 名称 / 最近更新(0.5.8) |
CodeNote: 题目上移 / 题目下移 |
改「手动顺序」的 order,间隔 10、只写变动的题目(0.6.2) |
CodeNote: 开始使用(一键上手) |
初始化项目 + 建第一个条目 + 直接打开,陌生人从这进(0.7.0) |
CodeNote: 立即给项目数据拍快照 |
把 .codenote 复制到 .codenote-backup/日期/(0.8.0) |
CodeNote: 批量把代码文件建成条目 |
仓库里一堆 .c 时,扫描未归条的文件、勾选批量建条目(0.7.0) |
CodeNote: 打开三分钟上手指南 |
只读 Markdown,讲清四个常用场景(0.7.0) |
CodeNote: 新建代码文件 |
按语言模板生成骨架并自动关联到题目(0.5.8) |
CodeNote: 为当前文件写一篇笔记 |
给当前代码文件建一个「文件笔记」条目(无题面)(0.5.16) |
CodeNote: 备份项目数据(.codenote) |
把项目数据整份复制到选定目录(0.5.8) |
CodeNote: 编辑题目标签 |
最多 5 个标签,用于筛选与展示(0.5.10) |
CodeNote: 从备份恢复项目 |
选备份目录还原成新项目并加入工作区(0.5.13) |
CodeNote: 导出 AI 上下文 |
单题自包含上下文(题面+代码+笔记)或项目索引 JSON(0.5.15) |
CodeNote: 上一题 / 下一题 |
与工作区按钮等效 |
CodeNote: 新建笔记 |
在当前题目里新建一条笔记(0.5.2) |
CodeNote: 重命名笔记 |
只改列表标题,不动正文(0.5.2) |
CodeNote: 删除笔记 |
进系统回收站;被引用或只剩一条时拒绝(0.5.2) |
CodeNote: 刷新 |
重新读取磁盘数据 |
侧边栏和题目工作区里都有对应的右键菜单 / 内联按钮。
6. 数据目录
<项目根>/
.codenote/
project.json 项目元数据
problems/
p_20261107-110000_demo92/ 目录名 = 题目 ID(永远不变)
problem.json 题目元数据 + 关联代码文件(含稳定 id)+ 附件引用
statement.md 题面(Markdown)
assets/
index.json 附件索引
images/ 粘贴 / 导入的图片
20261108-135324-040cb4ca.png
notes/
index.json 笔记索引(未来支持多条)
note_xxx.md 笔记正文
canvas/
canvas.json 画板数据(schemaVersion 2:元素 + 视口)
5.1 题目元数据示例
{
"schemaVersion": 1,
"id": "p_20261107-110000_demo92",
"name": "92. 反转链表 II",
"createdAt": "2026-11-07T03:00:00.000Z",
"updatedAt": "2026-11-07T03:00:00.000Z",
"order": 10,
"tags": ["链表", "中等"],
"codeFiles": [
{
"path": "链表题/反转部分链表(头插法).c",
"language": "c",
"addedAt": "2026-11-07T03:00:00.000Z"
}
],
"statement": { "file": "statement.md", "format": "markdown" },
"notes": { "dir": "notes", "index": "notes/index.json" },
"canvas": { "file": "canvas/canvas.json", "format": "json" },
"assets": { "dir": "assets" }
}
5.2 设计要点
- ID 稳定:
p_<时间戳>_<随机>,与名称、路径无关;目录名即 ID。
- 代码文件 ID:
cf_<16 位十六进制>。旧数据用「题目 ID + 规范化相对路径」的 sha1 前 16 位
确定性回填,下一次写操作时落盘;之后改名不影响身份。
- 路径修复:VS Code 内重命名 / 移动代码文件时,按旧相对路径自动更新关联,
保留
codeFileId;附件归属不需要迁移。删除文件只标失效,不自动取消关联。
- 重新定位:文件被移到项目外、由外部工具移动,或自动修复遇到冲突时,
可从树 / 失效横幅 / 打开提示里手动重新定位,仍保留原
codeFileId。
- 重复关联治理:新关联与重新定位都会拒绝已属于其他题目的文件;
「检查代码文件关联」列出缺失与跨题目重复路径,重复项不自动清理,由用户明确取消。
- 代码文件路径:相对项目根目录、统一
/ 分隔;文件移动/删除后,打开题目会把对应文件标为「路径失效」,侧边栏用警告图标提示。
- 路径边界:只接受项目文件夹内的文件,拒绝绝对路径与
..,保证项目可以整体移动、进 git。
- 资源引用显式化:题面 / 笔记 / 画布 / 资源目录都写在元数据里,未来可以扩展成多份题面、更多资源类型。
- 写入安全:JSON 与图片都采用「临时文件 + 重命名」写入,避免写一半损坏。
- 附件索引独立:图片元数据写在
assets/index.json,problem.json 只在首次导入附件时写一次,
之后截图不会反复改动元数据(git 噪声最小)。
- 附件归属:每条附件记录
codeFileId(粘贴时正在编辑的代码文件),文件改名后归属不丢;
无法确定时按题目级资料保存。
- 去重与校验:按 sha256 去重;MIME 由文件头魔数判定,扩展名只由魔数决定,不信任剪贴板给的文件名。
- 附件管理:附件标签提供缩略图、预览与删除;删除前先保存 Webview 草稿,
题面引用必须用户明确同意才移除;笔记 / 无法自动识别的引用阻断删除。
- 删除顺序:题面(去引用)→ 索引(去条目)→ 物理文件(系统回收站);
文件删除放在最后,失败只留下可对账的孤儿文件。
- 对账:索引缺失 / 损坏 / 有坏条目时禁止清理;缺失只删索引,
ORPHAN 只删文件;被题面 / 笔记引用的孤儿保护。
- 路径安全:删除目标由扩展从索引推导,只接受
assets/images/ 下的普通文件,
并做题目边界检查;不信任 Webview 传来的路径。
7. 代码结构
problem-mate/
package.json 扩展清单:视图容器、视图、命令、菜单、欢迎页
tsconfig.json TypeScript 配置(src → out)
media/
codenote-activity.svg 活动栏图标
problemWorkspace.html 题目工作区 Webview 模板(含 CSP / 外观 / nonce 占位符)
problemWorkspace.css 工作区样式(主题变量 + `--cn-*` 变量,强调色 / 字号可配)
problemWorkspace.js Webview 前端逻辑(标签页、自动保存、保存屏障、导航、
粘贴导入、附件管理、预览、删除、对账、外观热更新、消息)
src/
extension.ts 激活入口:输出通道、树视图、面板、命令、文件监听
core/
types.ts 数据结构定义(纯类型)
ids.ts ID 生成与校验
paths.ts 相对路径规范化、边界检查、语言推断
metadata.ts 元数据创建 / 解析 / 排序(纯函数,可单测)
attachments.ts 附件纯逻辑:魔数、文件名、索引解析、去重、代码文件 ID 回填、
规范图片路径
attachmentRefs.ts 引用分析:inline 图片、代码区遮罩、残留路径阻断
reconcile.ts 索引状态、缺失 / ORPHAN 对账分析
imageSize.ts 只读图片头取宽高(PNG / JPEG / GIF / WebP)
fs.ts workspace.fs 封装(UTF-8、JSON 状态、二进制原子写、删除)
projectStore.ts 项目发现、初始化、元数据更新
problemStore.ts 题目 CRUD、代码文件关联、题面 / 笔记读写、
附件导入 / 删除事务 / 对账动作
locators.ts 命令与界面之间传递的定位信息
appearanceDefaults.ts 外观默认值 / 取值范围 / 颜色校验(纯逻辑,可单测)
ui/
treeProvider.ts 侧边栏树:项目 → 题目 → 代码文件
problemPanel.ts WebviewPanel 生命周期、双栏布局、消息处理
webviewContent.ts Webview HTML 组装、CSP、外观首屏注入
appearance.ts 读外观配置 → 校验 → CSS 变量(热更新用同一份)
markdown.ts 调用内置 Markdown 渲染器 + 本地图片 URI 改写
messages.ts 扩展 → Webview 的数据结构
features/
codeFileIndexService.ts 代码文件反向索引(路径 → 题目)
codeFileRenameService.ts 重命名 / 移动 / 删除 / 新建文件监听与路径修复
autoRestore.ts 打开已关联代码文件时恢复题目上下文
commands/index.ts 所有命令的实现与错误处理
test/
runTests.ts 纯逻辑单元测试(Node 直接运行)
integration/run.ts Extension Host 集成测试
showcase.ts 开发用:打开工作区并保持窗口,便于截图
scripts/
build-preview.mjs 用演示数据生成 Webview 预览页(默认 + 非默认外观两份)
appearance-fixture.mjs 预览 / 检查共用的非默认外观样本与样式生成
check-webview.mjs headless Chrome 打开预览页做布局/交互断言
run-integration.ps1 集成测试 / 展示窗口启动脚本
preview/ 预览页与截图(开发用,不打包)
samples/demo-workspace/ 演示项目(内置 3 题 + 3 个 C 文件;可能还有手动创建的题目)
6.1 双栏是怎么做的
ProblemWorkspacePanel 把 Webview 创建在第一列编辑器组,再用
vscode.window.showTextDocument(uri, { viewColumn: ViewColumn.Beside }) 把代码文件开在右侧一列。
这是 VS Code 扩展 API 能稳定做到的「左 Webview / 右原生编辑器」布局,用户仍然可以拖动分隔条调整比例。
- 代码文件必须是 VS Code 原生编辑器 —— 已满足;
- Webview 与扩展宿主通过
postMessage 通信 —— 已满足;
- 切换题目只更新同一 Webview 的数据,不重建页面,标签页、草稿、滚动状态都保留。
6.2 自动恢复(P2)
src/features/codeFileIndexService.ts 在内存里维护「代码文件绝对路径 → 题目」的反向索引,
元数据变化后自动重建。src/features/autoRestore.ts 监听活动编辑器变化:
- 打开已关联文件:把题目面板创建 / 切换到对应题目;
- 打开未关联文件:完全静默,不创建空题目,不动现有面板;
- 不抢焦点:创建面板后把原编辑器重新显示到前台,恢复原选区;
- 不改布局:面板固定在第一列;单组时原文件重新成为活动标签,
多组时恢复第一列原有标签;编辑器组数量不变;
- 同一题目的不同代码文件:不重建 Webview、不切换面板、不丢状态;
- 用户手动关闭面板后:本次会话不再自动重开,直到主动打开题目或重载窗口;
- 事件重入 / 重复恢复:内部抑制标记 + 120ms 防抖 + 题目相同直接返回。
设置项:codenote.autoRestore.enabled(默认 true)。
设计取舍:为了不打断当前编辑器,自动恢复只在后台创建/更新面板标签,
不会把面板切到前台。要看题面请点一下 CodeNote 标签页。
6.3 图片附件(P3)
导入方式(三种,共用同一条管线):
- 粘贴:在题目工作区按
Ctrl+V。只拦截「剪贴板里确实有图片字节」的情况;
纯文字粘贴、笔记里的粘贴保持原样。
- 「粘贴截图」按钮:直接读取系统剪贴板里的图片(Snipaste、微信/QQ 截图等复制后一键导入),
不需要先保存文件。读取被系统拒绝时提示改用
Ctrl+V。
- 「+ 图片」按钮:打开文件选择器,可多选。
落盘与校验(全部在扩展侧完成):
- 先按
File.size 预检,解码后再按真实字节数校验上限(默认 10MB,可配 1–50);
- 用文件头魔数判定类型(PNG / JPEG / WebP / GIF),不信任剪贴板的文件名与声明 MIME;
- sha256 去重:同一题目同一张图只存一份,重复粘贴只提示、不重复插入引用;
- 文件名由扩展生成:
yyyyMMdd-HHmmss-<sha256 前 8 位>.<ext>;
- 图片写到
assets/images/,索引写到 assets/index.json,两者都是原子写;
- 索引写失败会清理刚写入的图片;清理也失败时只记录
ORPHAN 日志(输出通道),
不重试、不自动删除,留给后续版本对账。
引用插入:成功后把  插到题面光标处。
如果插入时你在预览模式,会自动切回预览,直接看到图片;
如果你在编辑模式,则保持在编辑区(引用插在光标处)。
多张图按剪贴板里的原始顺序逐张提交,单张失败不影响其余。
其它行为:
- 每条附件记录
codeFileId:粘贴时正在编辑的代码文件;无法确定时是题目级资料;
- 一次最多 10 张;
- 剪贴板里只有 HTML(网页图片 URL)时提示「网页图片没有原始字节,请保存后使用「+ 图片」」;
- 页脚显示「附件 N」;
- 切题期间到达的导入结果会暂存,切回该题时自动补插引用。
设置项:codenote.attachments.maxSizeMb(默认 10)。
6.4 附件展示、删除与对账(P4)
附件标签:题面 / 笔记 / 画板之后的第四个标签。
它是这道题的图片总账:题面 / 笔记里引用的图,加上导进来但还没用上的图,都在这里。
- 缩略图网格展示文件名、尺寸、体积、归属代码文件;
- 角标:题面引用、需手动处理、文件缺失;
- 页脚「附件 N · M 缺失」可点击跳转;
- 点击卡片预览大图,Esc / 遮罩 / 关闭按钮退出;预览里可以标注、删除;
- 「对账」检查索引与实际文件是否一致(缺失 / 孤儿),不会自动删你的文件。
删除:
- 未被引用:一个确认框,文件移入系统回收站;
- 题面引用:显示引用位置 +「删除图片并移除引用」;
- 笔记引用 / 引用式语法 / 代码块 / 裸路径等无法可靠处理的引用:阻断,
提示手动处理,不自动改写笔记;
- 删除前等待 Webview 草稿保存完成;保存失败则取消删除;
- 执行时扩展重新扫描引用,引用数量变化会要求重新确认。
对账:
- 识别「索引有记录、文件缺失」与「文件存在、索引没有(ORPHAN)」;
- 缺失只删索引,ORPHAN 只删物理文件(回收站);
- 被题面 / 笔记引用的 ORPHAN 保护,不允许删除;
- 索引缺失 / 损坏 / 有坏条目时禁止清理,只提示打开索引文件;
- 逐项确认,没有批量 / 自动清理。
限制:缩略图直接使用原图缩放,不生成磁盘缩略图缓存;
watcher 自写抑制仍为可选优化,P4 未实现。
6.5 代码文件路径修复与重新定位(P5)
自动修复:
- 在 VS Code 内重命名 / 移动代码文件时,监听
onDidRenameFiles;
- 按旧相对路径找到唯一关联,更新
problem.json 的路径与语言;
- 保留
CodeFileRef.id,附件里的 codeFileId 不变;
- 新路径冲突、旧路径属于多个题目、目标移出项目或进入
.codenote 时
不自动修改,提示原因。
重新定位:
- 文件被外部工具移动、删除后恢复,或自动修复冲突时使用;
- 可从代码文件树、Webview 失效横幅、打开失效文件时的提示进入;
- 仍保留原
codeFileId,所以附件归属不变;
- 只能选择当前项目内的普通文件。
重复关联治理:
- 新关联、重新定位都拒绝已属于其他题目的文件;
- 「检查代码文件关联」列出缺失路径与跨题目重复路径;
- 缺失项可直接进入重新定位;重复项只报告,不自动清理,
由用户明确取消其中一条关联。
不做:外部工具移动的自动监听、整目录重命名的关联迁移、
重复项自动清理。
6.11 画板(0.6.0)
第三个标签,用来画推导:流程图、状态转移、手推一遍样例。
工具:选择 · 画笔 · 橡皮 · 矩形 · 椭圆 · 菱形 · 箭头 · 直线 · 文字,可调颜色 / 粗细(5 档圆点)/ 字号。
操作:
| 做什么 |
怎么操作 |
| 画 |
选工具后在画布上拖 |
| 选 / 改已有元素 |
选择工具(V)点一下 → 改颜色或粗细会直接应用到它 |
| 移动元素 |
选择工具下按住元素直接拖;选中多个时一起移动 |
| 框选多个 |
选择工具下从空白处拖出选框;Shift 点选可增减 |
| Delete |
删掉当前选中的全部元素(可撤销) |
| 平移 |
空格拖拽 / 中键拖拽 / 滚轮 |
| 缩放 |
Ctrl + 滚轮(以光标为轴)· 工具栏 − / + |
| 复位 |
双击工具栏的百分比 |
| 撤销 / 重做 |
Ctrl+Z / Ctrl+Shift+Z(或 Ctrl+Y)· 也有按钮 |
| 微调 / 删除 |
方向键(Shift ×10)· Delete |
| 橡皮 |
E 后按住拖动:笔迹会被擦掉一部分(切成几段),形状 / 文字整块删除;可撤销 |
| 橡皮大小 |
橡皮选中时,工具栏那排 5 个圆点就是橡皮大小(6/10/16/24/34 px) |
| 写文字 |
T 后点一下画布,直接打字;回车 确认,Shift+回车 换行,Esc 取消 |
| 切工具 |
V 选择 · P 画笔 · E 橡皮 · R 矩形 · O 椭圆 · D 菱形 · A 箭头 · L 直线 · T 文字 |
橡皮对笔迹(手绘 / 直线 / 箭头)和形状的轮廓都是切成几段 ——
擦矩形上边会把轮廓断开,剩下的边留下;擦在矩形内部没有反应。
只有文字 / 图片是整块删除(它们没有「半块」的概念)。都能 Ctrl+Z 撤销。
注意:形状被擦过之后会变成折线元素(矩形没有「缺一角」这种表示)。
快捷键只在画板有焦点时生效,不会抢 VS Code 的 Ctrl+Z。
保存:改完 700ms 自动写入 canvas/canvas.json(切标签 / 切题目 / 上一题下一题前会先落盘),
右下角显示「未保存 / 保存中 / 已保存」。数据格式是 schemaVersion 2:
元素带稳定 id、坐标是画布坐标,缩放平移只存视口,不改元素本身。
导出:点工具栏的导出按钮 → 生成 2x 白底 PNG → 走附件管线写进 assets/images/
(sha256 去重)→ 自动把  插进题面。
(不再提供 1x / 透明选项:1x 只是文件小一半,透明在深色文档里反而看不清。)
坏数据保护:canvas.json 读不懂(JSON 坏了 / 版本更新 / 旧版却带元素)时,
画板切换为只读并显示提示条,任何保存都会被拒绝,不会覆盖你的文件。
数据长这样:
{
"schemaVersion": 2,
"type": "codenote.canvas",
"updatedAt": "2026-11-11T01:00:00.000Z",
"viewport": { "width": 900, "height": 600, "zoom": 1, "scrollX": 0, "scrollY": 0 },
"background": "theme",
"elements": [
{ "id": "el_1a2b3c4d", "type": "rect", "x": 60, "y": 60, "width": 160, "height": 80,
"stroke": "#2a8899", "strokeWidth": 2, "opacity": 100, "createdAt": "…" }
]
}
6.12 截图标注(0.6.1)
附件预览弹窗里点「标注」,直接在截图上画框 / 椭圆 / 箭头 / 文字,
完成时把「原图 + 标注」合成一张新 PNG,存成新附件并自动插进题面——原图不动。
- 工具:矩形框 · 椭圆 · 箭头 · 文字(点图上位置直接打字,回车确认)
- 颜色默认红色,三档线宽(细 / 中 / 粗),撤销 / 重做
- 取消 /
Esc / 点遮罩 / × 都只是退出标注,不落盘半成品
- 保存失败会留在标注里、重新点亮「完成」按钮,不丢内容
没有马赛克 / 涂抹 / 自由画笔——标注这类图,框和箭头就够了。
想改图上已标的字,直接用文字工具点一下那条字即可编辑。
6.6 外观与排版(0.5.1)
题目工作区的配色与排版可以在设置里调,改完立即生效,不需要重载窗口:
| 设置项 |
默认 |
范围 |
作用 |
codenote.appearance.accentDark |
#d3785a |
#rgb / #rrggbb |
暗色主题下的强调色 |
codenote.appearance.accentLight |
#b8563a |
#rgb / #rrggbb |
亮色主题下的强调色 |
codenote.appearance.uiFontSize |
13 |
11–18 |
界面字号(标签 / 按钮 / 提示) |
codenote.appearance.contentFontSize |
14 |
12–22 |
题面正文字号 |
codenote.appearance.codeFontSize |
13 |
11–20 |
题面代码块字号 |
codenote.appearance.contentLineHeight |
1.78 |
1.2–2.4 |
题面行高倍数 |
codenote.appearance.contentMaxWidth |
880 |
480–1400 |
题面最大阅读宽度(更宽时居中留白) |
实现方式:
- 默认值与范围在
src/core/appearanceDefaults.ts(纯逻辑),package.json 里声明同一套;
单测 U-35 校验两处一致,改一处漏改另一处会红。
ui/appearance.ts 读配置 → 校验(非法颜色回落默认、数值越界钳制)→ CSS 变量;
首屏由 webviewContent.ts 注入 <style>,运行期由 onDidChangeConfiguration
触发 appearance/update 消息热更新。
- 样式全部走
--cn-* 变量:--cn-accent-dark / --cn-accent-light /
--cn-ui-size / --cn-content-size / --cn-code-size / --cn-line-height /
--cn-content-width。
- 预览与检查脚本用
scripts/appearance-fixture.mjs 的固定样本生成
preview/webview-preview-custom.html,check:webview 据此断言注入链路(W-73…W-79)。
限制:不校验强调色与背景的对比度;只影响题目工作区,不影响侧边栏与 VS Code 原生界面;
外观的极端取值仍需人工在真实宿主里看一遍。
6.7 笔记多条目(0.5.2)
笔记标签页分成两部分:
- 左侧列表:这道题所有笔记,显示标题与「更新日期 时:分」,当前条目用强调色标出;
顶部是「N 条」计数与「+」新建按钮;条目可拖拽排序(拖到某一项上插到它前面);
- 右侧:预览 / 编辑分段按钮、笔记文件路径、保存状态、重命名、删除;
预览用内置 Markdown 渲染器,编辑是纯文本 textarea。
数据与写入:
- 索引仍是
notes/index.json(数组),每条的 file 是 <notesDir>/<noteId>.md;
ProblemStore 提供 listNotes / readNoteById / writeNoteById / createNote /
renameNote / deleteNote / reorderNotes,全部进「按题目串行队列」;
- 新建先写文件再写索引;删除是「引用检查 → 回收站 → 索引」,任一步失败都不改索引;
- 只剩一条时禁止删除;重命名只改索引标题,不改正文。
索引损坏时只读:notes/index.json 解析失败 / 结构不对 / 条目缺字段或 id 重复时,
classifyNotesIndex() 判为 corrupt;此时所有写操作都拒绝(不会把坏文件覆盖掉),
界面禁用新建 / 重命名 / 删除并显示只读说明,修好后点题目刷新即可恢复。
窄面板(≤720px)下的排版:笔记列表变成顶部一行横向滚动,只显示标题(隐藏日期),
「N 条 / +」压成一行细条;笔记正文顶部留白收到 8px,紧贴工具栏,不再出现
「列表 → 一大片空白 → 正文」的观感。宽面板是左侧 200px 竖排侧栏 + 卡片式笔记项
(1px 边框、8px 圆角、条目间距 6px;当前项有强调色边框与左侧 3px 竖条)。
界面状态:
- 每条笔记独立自动保存(700ms 防抖),切条目 / 切题目 / 切标签前先 flush;
- 保存失败会阻止切换并提示,避免「看起来切了其实没保存」;
- 按题目记住上次打开的笔记(
vscode.setState),重开面板后回到那条;
- 全部笔记正文随 payload 一次下发(笔记体积小),切条目不再往返。
限制:窄面板下列表是一行横向滚动而不是下拉;拖拽没有「拖到末尾之后」的落点;
删除确认 / 重命名输入是 VS Code 原生弹窗。
6.9 题面导入:网页内容粘贴即转 Markdown(0.5.7)
从力扣 / PTA / 学校 OJ 复制题面 → 在题面或笔记的编辑框里 Ctrl+V:
- 标题、段落、有序 / 无序列表(含嵌套)、代码块(保留缩进)、行内代码、
加粗 / 斜体 / 删除线、链接、图片、引用、单层表格、分割线、常见 HTML 实体
都会转成对应 Markdown;
- 图片保留为在线地址(不下载);纯图片复制仍提示「请保存后用+图片」;
- 插入到光标处(焦点不在编辑器时追加到末尾),自动切编辑模式、自动保存,
Ctrl+Z 可撤回整次导入;
- 转换在扩展侧完成(
core/htmlToMarkdown.ts,纯函数、可单测),
Webview 只负责读剪贴板。
限制:不做 CSS 还原;多层嵌套表格 / 站点私有结构只保证「尽量转对」;
在线图片离线时是断链;粘贴内容里的站内导航需要手动删几行。
6.10 检索、模板与备份(0.5.8)
- 搜索题目(侧边栏标题栏 🔍 或命令面板):输入关键词,跨所有题目搜题目名、题面、
笔记正文;结果显示「第 N 行:片段」,题目名命中优先;选中直接打开该题。
- 筛选 / 排序(侧边栏标题栏):筛选「全部 / 代码文件缺失 / 有笔记 / 有附件 / 有标签」,
排序「手动顺序 / 名称 / 最近更新」;选择按工作区保存,下次打开自动恢复。
- 题目上移 / 下移(题目右键,0.6.2):改的就是「手动顺序」的
order,
写回时间隔 10(10/20/30…)且只写变动的那几个 problem.json;
当前排序方式不是「手动顺序」时会自动切回来再移动;到两端给提示、不写盘。
注意顺序是在完整列表上移动的,即使开着筛选也不会打乱全局 order。
- 新建题目即建代码文件(默认开启,
codenote.newProblem.createCodeFileOnCreate):
输入题目名后按模板生成骨架、自动关联,打开题目时右侧直接是代码编辑器;
- 新建代码文件(题目右键):按设置
codenote.newProblem.language
(C / C++ / Python / Java / 纯文本)生成带题目注释的骨架,
自动关联到题目并在右侧打开;重名自动加 -2、-3;
目录由 codenote.newProblem.codeFileDir 控制(留空 = 项目根)。
- 备份项目数据(项目右键):把
.codenote 整份复制到选定目录,
目录名 <项目名>-codenote-YYYYMMDD-HHMMSS;只导出、不导入覆盖。
- 题目标签(题目右键):最多 5 个、每个 16 字,逗号 / 顿号 / 空格 / 分号分隔,
自动去重;筛选里可只看「有标签」的题,tooltip 显示
#标签。
- 笔记大纲(笔记工具栏「≡」):按渲染后的 h1–h3 生成,点击跳到对应标题;
标题少于 2 个时不出现按钮。
- 从备份恢复(命令面板):选备份目录 → 校验
project.json → 选目标位置与文件夹名 →
还原成 <目标>/<名字>/.codenote 并加入工作区;目标已有数据时先确认覆盖。
限制:搜索是全量扫描内存匹配(几百题毫秒级,上千题建议加索引);
备份不做 zip;恢复需要手工把目录拷回。
6.8 编辑工具条(0.5.3)
编辑模式下(题面 / 笔记都一样)标签栏下面会出现一条 Markdown 工具条,
不用记语法也能排版:
- 段落样式下拉:正文 / 大标题 / 小标题 / 小节标题 / 引用框(带竖线的方框)/ 代码块;
- 行内:B 加粗、I 斜体、S 删除线、代码(行内代码);
- 块:• 列表、1. 编号(逐行递增)、☑ 待办、❝ 引用、▦ 表格、链接、— 分割;
- 插入模板:题面骨架(## 示例 / ## 提示)、笔记骨架(关键点 / 易错点 / 复杂度)、示例表格;
- ?:语法速查浮层,列出每个按钮对应的 Markdown 写法与快捷键。
快捷键:Ctrl+B 粗体、Ctrl+I 斜体、Ctrl+K 链接、Ctrl+0 正文、Ctrl+1/2/3 标题、
Ctrl+Shift+7 编号、Ctrl+Shift+8 圆点;列表里 Tab 缩进、Shift+Tab 回退、
回车自动续行、空条目回车退出列表。
实现要点:
- 文件仍然是纯 Markdown(能 diff、能被别的编辑器打开、能在预览里渲染)——不做富文本;
- 改写走
document.execCommand('insertText'),保留浏览器原生撤销栈(Ctrl+Z 能撤);
- 工具条按钮
mousedown 时 preventDefault,textarea 的选区不会丢;
- 一条工具条给题面和笔记共用,只在「题面 / 笔记 + 编辑模式 + 可写」时出现。
限制:快捷键会覆盖 VS Code 同名组合(只在输入框聚焦时);
没有「粘贴 HTML 自动转 Markdown」;表格只有固定 3×2 模板;不显示当前选区格式。
8. 开发与验证
npm run compile # TypeScript 编译
npm test # 纯逻辑单元测试(78 项)
npm run check:webview # 全量:生成预览页 + headless Chrome 渲染并断言(125 项检查)
npm run check:canvas # 只跑画板那段(改画板时用,十几秒)
npm run check:annotate # 只跑截图标注那段(改标注时用,十几秒)
npm run test:integration # 在真实 VS Code 扩展宿主中跑集成测试(59 项检查)
npm run package # 打包 codenote-<version>.vsix(见 docs/P7-打包说明.txt)
npm run check:vsix # 打包并自检产物(manifest / 文件 / 语法 / 相对 require)
npm run icon # 重新生成扩展图标 media/icon.png
npm run bench # 规模基准:100/500/1000/2000 题(见 docs/性能基准.txt)
npm run showcase # 打开一个保持 3 分钟的演示窗口,方便截图
check:webview 会输出 preview/shot-*.png(题面、笔记、画板、空状态、失效状态、浅色主题、窄面板、
外观默认值 / 非默认值 / 亮色 / 窄面板四张)。
7.1 排查:bad option: --extensionDevelopmentPath
如果当前终端里存在 ELECTRON_RUN_AS_NODE=1(部分工具链会注入),Code.exe 会以 Node 模式运行并拒绝 VS Code 参数。
启动前清掉即可:
Remove-Item Env:ELECTRON_RUN_AS_NODE -ErrorAction SilentlyContinue
scripts/run-integration.ps1 和 scripts/start-devhost.ps1 已经处理了这个情况。
7.2 排查:按 F5 没有反应
先确认不是这几种情况:
- 打开的文件夹不对。必须打开
D:\code-vscode-tools\problem-mate 本身(窗口标题是 problem-mate - Visual Studio Code),
不能只打开它的父目录或某个文件。.vscode/launch.json 只对「打开的文件夹」生效。
- 笔记本 F 键是功能键。很多笔记本按
F5 实际触发的是音量/亮度,需要按 Fn + F5。
- 焦点不在编辑器区。焦点在集成终端或侧边栏时,F5 可能被别处消费。先点一下编辑器空白处。
- 调试面板没有选中配置。按
Ctrl + Shift + D 打开「运行和调试」,左上角下拉框应能看到
运行 CodeNote(开发宿主);没有的话点 创建 launch.json 文件,或执行
开发人员: 重新加载窗口(Ctrl + Shift + P 输入 reload)。
不依赖 F5 的三种启动方式(任选其一):
启动成功的标志:出现标题为 [Extension Development Host] demo-workspace - Visual Studio Code 的新窗口,
活动栏出现 CodeNote 图标。
注意:只打开 F5 调试会话里的断点才生效;npm run dev 只是运行扩展,不挂调试器。
如果 F5 仍无反应,请把「按 F5 时界面上出现的任何提示」或 Ctrl + Shift + P → Developer: Toggle Developer Tools 里的报错发出来。
7.3 排查:Code is currently being updated
如果启动扩展宿主时报这个错,说明 VS Code 的静默更新安装器(CodeSetup-*.exe)正在运行并持有全局互斥量,
与 CodeNote 无关。处理方式:
- 完全退出所有 VS Code 窗口,等更新完成后再启动;
- 或先检查任务管理器里是否有卡住的
CodeSetup-* 进程(本机曾出现一个卡住约一天的情况),
结束它之后 VS Code 下次启动会重新尝试更新。
9. 本轮已实现 / 未实现
已实现
- 标准 TypeScript 扩展工程:编译、调试配置、
.vscodeignore、演示工作区
- Activity Bar 入口 + 「项目与题目」侧边栏(VS Code 原生收起 / 展开)
- 项目 → 题目 → 关联代码文件三级树;路径失效有可视化提示
- 项目初始化、多项目(多根工作区)、添加项目文件夹
- 题目创建 / 从当前文件创建 / 重命名 / 删除 / 排序值
- 代码文件关联、取消关联、打开、缺失提示
- 题目工作区 Webview:题面(Markdown 预览 + 编辑)、笔记(Markdown 文本)、画板(能画 / 撤销 / 导出)
- 上一题 / 下一题导航,切换题目时联动右侧代码文件
- 打开已关联代码文件时自动恢复题目上下文(不抢焦点、不改选区 / 布局;同题切换不重建)
- 一个代码文件只能属于一个题目;关联到其他题目会被明确拒绝并提示
- 编辑自动保存(700ms 防抖)、保存状态提示、切换题目 / 标签不丢状态
- 题面相对路径图片可正常显示(CSP +
asWebviewUri)
- 项目 / 题目元数据文件监听,外部修改(git pull、手改 JSON)后侧边栏自动刷新
codenote.autoRestore.enabled 设置项(默认开启,可关闭自动恢复)
- 图片粘贴导入:
Ctrl+V 只拦截图片,魔数校验、sha256 去重、大小限制、原子写
- 「粘贴截图」按钮:直接读取系统剪贴板里的图片(Snipaste 等截图工具一键导入)
- 「+ 图片」文件选择导入(与粘贴共用同一条校验 / 去重 / 写入管线)
- 附件索引
assets/index.json、按题去重、图片宽高元数据、codeFileId 归属
- 题面自动插入
 引用(可撤销)、页脚「附件 N」计数
- HTML-only 剪贴板的明确提示;切题期间的引用补插
codenote.attachments.maxSizeMb 设置项(默认 10MB)
- 附件标签:缩略图网格、缺失 / 引用 / 需手动角标、页脚计数
- 附件预览:大图、元信息、Esc / 遮罩 / 关闭、焦点返回
- 附件删除:未引用 / 题面引用两种确认流程;题面引用需显式同意移除;
笔记 / 无法自动移除的引用阻断;文件进系统回收站
- 删除事务:题面 → 索引 → 回收站;失败明确反馈;
inconsistent 原生模态错误
- 索引与目录对账:缺失文件、ORPHAN、保护被引用孤儿、索引异常时禁止清理
- 删除 / 对账前的 Webview 保存屏障(requestId + 等待 / 超时 / 失败取消)
- 附件导入成功即时更新列表与计数(不依赖 watcher 重载)
- VS Code 内代码文件重命名 / 移动时自动更新相对路径,保留
codeFileId
- 文件删除后保留失效标记;支持从树 / 失效横幅 / 打开提示重新定位
- 重新定位保留附件
codeFileId 归属;拒绝关联到其他题目已占用的文件
- 「检查代码文件关联」:列出缺失路径与跨题目重复关联,不自动清理重复项
- 外观可配置(0.5.1):强调色、界面 / 正文 / 代码字号、行高、阅读宽度,改设置立即生效
- 题面首个与题名重复的一级标题自动去掉;页脚显示「更新于」
- 外观默认值与
package.json 声明一致由单测守住;非默认外观另有专用预览页与 7 项断言
- 笔记多条目(0.5.2):列表 + 预览 / 编辑、新建 / 重命名 / 删除 / 拖拽排序
- 每条笔记独立自动保存,切条目前先落盘;按题目记住上次打开的笔记
- 笔记被题面或其他笔记引用时拒绝删除;只剩一条时禁止删除
- 编辑工具条(0.5.3):标题 / 列表 / 待办 / 引用框 / 代码块 / 表格 / 链接一键插入,
快捷键与列表续行,附 Markdown 语法速查
- 导出题目为 Markdown(0.5.6):题面 + 全部笔记 + 代码文件 / 附件信息合成一份 .md
- 题面导入(0.5.7):从网页 / OJ 复制题面,Ctrl+V 自动转成 Markdown(题面与笔记都支持)
- 画板(0.6.0):9 个工具 + 撤销重做 + 缩放平移 + 局部擦除 + 拖动框选 + 导出 PNG 进题面
- 截图标注(0.6.1):预览弹窗内画框 / 箭头 / 文字,另存为新附件插进题面,原图不动
- 题目上移 / 下移(0.6.2):改 order,间隔 10、只写变动的 problem.json
- 自动快照(0.8.0):每天第一次打开时把
.codenote 复制到 .codenote-backup/,
保留最近 7 份、超 300 MB 跳过、快照不进 git
- 多窗口提示(0.8.0):另一个窗口也开着同一项目时提醒「后保存会覆盖先保存」
- 外部改动同步(0.8.0):在别的编辑器改了题面 / 笔记,面板自动同步(有草稿时会先提示)
- 解析缓存(0.7.0):2000 题从 459.7 ms 降到 2.0 ms;TTL 2s + 写操作 / watcher 即时失效
- 批量归条(0.7.0):扫出还没归条的代码文件,勾选后一次建多条(可建题目或文件笔记);
默认一个都不选,要全选点列表标题栏右侧的按钮(批量是不可逆操作,不预选)
- 一键上手 + 三分钟指南(0.7.0):陌生人从「开始使用」一条命令进入完整链路
- 新笔记模板只留标题,不预置任何小节(不给用户规定该写什么;要结构用工具条模板)
未实现(后续期次)
- 拖拽导入、复制引用、批量清理
- 画板图片元素(把附件放进画板)——评测物的「在图上标」已由截图标注覆盖,可继续不做
- 粘贴 HTML 自动转 Markdown(从网页复制的带格式文字)
- 表格行列增删 / 插入表格对话框
- 切换题目时恢复「上次使用的代码文件」(当前打开第一个存在的文件)
- 题目排序拖拽(命令式上移 / 下移已有;TreeView 无稳定拖放 API)
- 笔记的标签 / 搜索 / 模板选择
- 打包
.vsix 发布流程(0.5.1 起已能本地打包)
已知限制
- 双栏宽度不能编程精确设置。扩展 API 没有设置编辑器组宽度的接口,只能在首次打开时尝试
workbench.action.evenEditorWidths,其余交给用户拖动分隔条。
- 自动恢复把面板放在后台。为了不改变当前编辑器 / 布局,自动恢复只创建或更新 CodeNote 面板标签,不会把它切到前台;需要看题面时点一下标签页。
- 自动恢复的启动场景没有自动化覆盖。窗口启动时恢复的编辑器由 1.2s 启动检查处理,建议手动验证一次「重启 VS Code 后打开已关联文件」。
- Webview 内的
Alt + ←/→ 会与 VS Code 的「后退 / 前进」冲突。本轮导航只用按钮,不注册全局快捷键;后续如需 Alt 组合键,要加 when 条件并实际验证。
- Markdown 渲染借用内置
markdown-language-features 扩展 的 markdown.api.render 命令;若该扩展被禁用,题面会退化为 <pre> 纯文本(不影响数据)。
- 重命名笔记只改列表标题,不改正文开头的
# 标题(正文是用户内容,避免覆盖)。
- 窄面板下笔记列表是一行横向滚动(不是下拉),条目多时需要横向滑。
- 拖拽排序只能拖到某一项上(插到它前面),没有「拖到末尾之后」的落点。
- 笔记删除确认 / 重命名输入用原生弹窗,headless 只覆盖到前端发请求与列表刷新。
- 代码文件自动修复仅覆盖 VS Code 内发起的单个文件重命名 / 移动。外部工具移动、整目录重命名不会自动识别;
会显示“路径失效”,用「重新定位」或「检查代码文件关联」处理。
- 代码文件必须位于项目文件夹内,项目外文件会被拒绝(保证路径可移植)。
- 画板:多选只能框选;导出固定 2x 白底(没有 1x / 透明选项)。
canvas.json 是单行压缩 JSON,git diff 不友好(体积小,可接受)。
撤销栈只在内存里,关掉面板就没了(落盘的只有最终文档)。
橡皮能擦笔迹也能擦形状:笔迹和形状轮廓都切成几段,文字 / 图片整块删除(都可撤销)。
- 附件缩略图直接使用原图缩放:不生成磁盘缩略图缓存;大量大图时首屏解码开销较高。
- 网页图片复制取不到字节:剪贴板里通常只有
text/html + 图片 URL,Webview 的 CSP
也拿不回来。会提示保存后用「+ 图片」。
- 切换题目期间到达的导入结果只暂存在 Webview 内存里,切回该题会补插引用;
如果这时关闭窗口或重启 VS Code,暂存的引用不会补(图片本身已落盘)。
- 单次最多导入 10 张;单图上限默认 10MB,可配 1–50MB。
- 去重按「题目 + sha256」;跨题目同一张图会各存一份(题目目录自包含)。
- 旧代码文件 ID 由路径哈希回填,在第一次写操作落盘之前若改名,ID 会变;
P5 的重命名修复 / 重新定位会立即落盘稳定 ID,此后附件归属不再受路径影响。
- 附件删除 / 对账的原生模态错误、真实宿主交互未在 headless 覆盖:
inconsistent 原生弹窗、真实系统回收站、真实符号链接过滤需要人工验收。
- watcher 自写抑制未实现:自己写
assets/index.json 仍会触发一次面板重载;
依赖 dirty 保护避免覆盖草稿,属于可选优化,P4 未投入。
- 附件索引损坏 / 有坏条目时清理和导入会被拒绝:需要手工修复
assets/index.json 后才能继续。
- 自动修复遇到冲突会跳过:新路径已被其他题目 / 本题目占用、旧路径同时属于多个题目、
目标在项目外或
.codenote 内,都会保留旧关联并提示;需手动重新定位或清理重复。
- 重复关联只检测、不自动清理:「检查代码文件关联」会列出跨题目重复路径,
最终保留哪个题目必须由用户取消其余关联,避免静默改数据。
- VS Code 文件事件的实际触发需要真实 Explorer 操作验收:集成测试覆盖 Store 修复逻辑,
但
onDidRenameFiles / onDidDeleteFiles / onDidCreateFiles 的宿主事件未在自动化中完整驱动。
- 整目录提升 / 降级、跨项目移动未自动处理;跨项目只能重新定位到当前项目内文件,
或取消关联后重新创建。
- 强调色不做对比度校验:填得过于接近背景色时,题号和下划线会看不清。
- 外观设置只有设置页入口:没有「打开外观设置」命令,也没有面板内设置面板。
- 外观的自动化只覆盖注入链路:配置热更新、系统主题切换、极端字号下的滚动与焦点
需要人工在真实宿主里过一遍。
- 工具条快捷键会覆盖 VS Code 同名组合(例如 Ctrl+K 是插入链接):只在两个输入框
聚焦时生效,失焦后恢复。
- 没有「粘贴 HTML 自动转 Markdown」:从网页复制的带格式文字仍是纯文本。
- 工具条不显示当前选区格式(选中粗体时 B 不变亮),只回显段落样式。
- 网页导入不做样式还原:多层嵌套表格、站点私有结构不保证;粘贴内容里若混入导航,
需要手动删几行;在线图片离线时是断链。
10. 下一步
按 docs/竞品分析与差距清单.txt 的结论排序:
- 题面导入(P0-1):从网页 / OJ 复制题面 →
Ctrl+V → 自动转 Markdown。
- 检索与筛选(P0-2):树内筛选、标签筛选、跨题目搜题面 / 笔记正文。
- 代码模板(P0-3):新建题目时按语言生成骨架文件并自动关联。
- 备份 / 恢复 / 迁移(P0-4):
.codenote 打包导入导出、schema 迁移框架。
- 发布工程化(P0-5):icon、市场 README、干净机器安装验收。
- P6 阶段 4:题目排序 / 筛选 / 搜索(
docs/P6-计划.txt)。画板与截图标注已在 0.6.0 / 0.6.1 完成。
- 遗留的人工验收项:真实系统回收站、真实符号链接过滤、
inconsistent 原生弹窗、真实宿主 Webview 交互、Explorer 文件重命名 / 删除事件、
外观设置的热更新与极值、笔记的原生确认框与拖拽排序。
0.5.6:导出题目为 Markdown(题面 + 全部笔记 + 代码文件 / 附件信息)。
P6 阶段 1(0.5.2)已完成:笔记多条目(列表 / 增删改 / 拖拽排序 / 独立自动保存)。
P5.5(0.5.1)已完成:外观可配置(强调色 / 字号 / 行高 / 阅读宽度)、标题去重,
预览与检查脚本覆盖注入链路。
P5(0.5.0)已完成:代码文件重命名 / 移动自动修复、重新定位、
重复关联检查;codeFileId 保留,附件归属不丢。
P4(0.4.0)已完成:附件缩略图 / 预览 / 删除 / 对账。