Skip to content

Commit 24deeba

Browse files
chore: 同步当前代码
1 parent 5a833d3 commit 24deeba

8 files changed

Lines changed: 1963 additions & 43 deletions

File tree

.github/workflows/build-macos.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: build-macos
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
tags:
7+
- "v*"
8+
9+
jobs:
10+
build:
11+
runs-on: macos-latest
12+
strategy:
13+
fail-fast: false
14+
matrix:
15+
arch: [x64, arm64]
16+
env:
17+
CSC_IDENTITY_AUTO_DISCOVERY: "false"
18+
steps:
19+
- uses: actions/checkout@v4
20+
21+
- uses: actions/setup-node@v4
22+
with:
23+
node-version: 18
24+
cache: npm
25+
26+
- run: npm ci
27+
28+
- run: npm run electron:build:mac -- --${{ matrix.arch }}
29+
30+
- uses: actions/upload-artifact@v4
31+
with:
32+
name: macos-${{ matrix.arch }}
33+
path: dist-electron

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
node_modules
22
dist
33
dist-electron
4+
release
45
.DS_Store
56
*.log
67
.vscode

openspec/changes/add-inline-semantic-editing/design.md

Lines changed: 244 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# 变更:默认渲染视图的语义就地编辑(Typora 风格)
2+
3+
## 为什么
4+
- 现在内容区偏“代码编辑器心智”:虽然强,但对写规范/需求的用户来说,阅读与轻量改字成本高。
5+
- 你提出的目标是“像 Typora 一样”:默认就是排版后的 view,双击即可改一小段内容,且**只有必要时**才暴露 Markdown 语法符号。
6+
- 我们还必须满足“尽量保留原文件字节风格”:BOM换行风格、缩进与空格不应被全局格式化重排。
7+
8+
## 变更内容
9+
- Markdown(含 tasks.md)默认展示为“渲染视图”,不再默认进入全屏编辑器。
10+
- 在渲染视图中支持“语义单元双击编辑”:
11+
- B:双击选中语义单元(词/链接文本/加粗范围/列表项文本等),进入就地编辑。
12+
- A:仅在必要时显示 Markdown 语法符号(光标所在行、结构被破坏风险区域、或用户显式进入源码编辑时)。
13+
- tasks.md:默认仍为任务看板;在看板/渲染视图中也支持语义就地编辑,并保证与源 Markdown 一致(同源编辑、同源保存)。
14+
- 提供“兜底源码编辑”入口(非主路径):遇到复杂结构或冲突时可进入源码块编辑,但不作为默认体验。
15+
16+
## 影响
17+
- 受影响规范:visualizer-core(内容渲染与任务文件交互)
18+
- 受影响代码(预期范围):内容区渲染链路、搜索与选区逻辑、任务看板与文件保存链路
19+
- 依赖与体积:将引入/替换为“文本为真相 + 装饰层”的编辑核心(详见 design.md)
20+
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
## 修改需求
2+
3+
### 需求:内容渲染与就地编辑
4+
系统必须对 Markdown 文件默认以渲染视图展示,并支持语义就地编辑。
5+
6+
#### 场景:默认以渲染视图打开 Markdown
7+
- **** 用户打开 `.md` 文件
8+
- **那么** 内容区默认展示为渲染视图,而不是直接进入全量编辑器
9+
10+
#### 场景:双击语义单元进行编辑
11+
- **** 用户在渲染视图中双击一段内容
12+
- **那么** 系统选中对应语义单元并进入就地编辑
13+
14+
#### 场景:必要时显示 Markdown 语法符号
15+
- **** 用户进行就地编辑且需要避免结构被破坏
16+
- **那么** 系统仅在必要范围内显示相关 Markdown 语法符号(例如光标所在行)
17+
18+
### 需求:语义双击选区
19+
系统必须将双击行为提升为“语义单元选区”,而不是仅按字符/单词选中。
20+
21+
#### 场景:双击选中链接文本
22+
- **** 用户双击一个链接的可见文本
23+
- **那么** 系统选中“链接文本”作为一个语义单元,而不是选中包含括号语法的整段源码
24+
25+
#### 场景:双击选中强调文本
26+
- **** 用户双击加粗/斜体等强调文本
27+
- **那么** 系统优先选中强调范围内的“可见文本”,并在必要时显示对应语法符号以允许修改
28+
29+
### 需求:必要时显示语法(最小暴露)
30+
系统必须默认弱化或隐藏 Markdown 语法符号,并在必要时显示,以支撑准确编辑。
31+
32+
#### 场景:光标进入行内编辑
33+
- **** 用户进入某一行进行就地编辑
34+
- **那么** 系统在必要范围显示该行相关语法符号以避免破坏结构
35+
36+
#### 场景:退出编辑后恢复渲染观感
37+
- **** 用户结束就地编辑并离开该行
38+
- **那么** 系统恢复渲染观感(语法符号不再占据可见视觉层)
39+
40+
### 需求:最小改动面保存与风格保持
41+
系统必须以“最小改动面”写回文件,并尽量保持原文件的字节风格(如 BOM、换行风格)。
42+
43+
#### 场景:只修改一个语义单元
44+
- **** 用户仅修改一小段语义单元内容
45+
- **那么** 系统只写回必要的文本改动,不对未触及区域做全局格式化重排
46+
47+
#### 场景:保存保持 BOM 与换行风格
48+
- **** 用户保存文件
49+
- **那么** 系统保持该文件的 BOM、换行风格与编码设置不被意外改变
50+
51+
### 需求:撤销与重做
52+
系统必须支持对就地编辑的撤销/重做,以匹配编辑器习惯。
53+
54+
#### 场景:撤销最近一次编辑
55+
- **** 用户撤销最近一次就地编辑
56+
- **那么** 内容回退到上一个状态,且渲染视图与实际文本保持一致
57+
58+
#### 场景:使用常见快捷键撤销与重做
59+
- **** 用户按下 `Ctrl+Z`
60+
- **那么** 系统执行撤销
61+
- **** 用户按下 `Ctrl+Y``Ctrl+Shift+Z`
62+
- **那么** 系统执行重做
63+
64+
### 需求:兜底源码编辑
65+
系统必须在语义定位失败或结构复杂时提供兜底的源码编辑路径,但不应成为默认体验。
66+
67+
#### 场景:语义定位失败
68+
- **** 系统无法将某次双击定位到可安全编辑的语义单元
69+
- **那么** 系统提供兜底方式编辑对应源码片段,并明确该方式为降级路径
70+
71+
### 需求:渲染视图行号
72+
系统必须在 Markdown 的渲染视图中提供可选的行号展示,以便用户能将屏幕内容与源文本行号对齐定位。
73+
74+
#### 场景:开启行号显示
75+
- **** 用户在渲染视图中开启“显示行号”
76+
- **那么** 内容区应展示与源文本一致的行号,并随滚动保持对齐
77+
78+
#### 场景:行号不影响编辑
79+
- **** 用户进行语义就地编辑或文本选择
80+
- **那么** 行号不应抢占焦点、不应影响选区与复制行为
81+
82+
#### 场景:行号与折行的对齐策略
83+
- **** 某一源文本行在渲染视图中发生折行(视觉上变成多行)
84+
- **那么** 系统按“源文本行”为单位显示行号,并确保该行号与该源行的首个视觉行对齐
85+
86+
#### 场景:行号只对渲染视图生效
87+
- **** 用户进入就地编辑或降级到行/块/源码编辑
88+
- **那么** 行号可以保持显示,但不得影响编辑器的布局与输入体验
89+
90+
### 需求:tasks.md 同源编辑
91+
系统必须将 tasks.md 的任务看板视图与源 Markdown 同源,并允许在看板/渲染视图中进行最小改动面的编辑。
92+
93+
#### 场景:打开 tasks.md
94+
- **** 用户打开 `tasks.md`
95+
- **那么** 默认展示任务看板,并支持勾选与编辑任务内容
96+
97+
#### 场景:看板操作写回源 Markdown
98+
- **** 用户在看板中勾选或编辑任务项
99+
- **那么** 系统以最小改动面写回 `tasks.md` 原文,并保持保存风格一致
100+
101+
#### 场景:看板与渲染视图保持一致
102+
- **** 用户在任务看板修改任务项
103+
- **那么** 渲染视图(或打开的任务内容)应反映同一份源文本结果,不出现分叉
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
## 0. 技术评审与纠偏结论(执行约束)
2+
3+
这部分不是“想法”,而是本变更的**强约束**。后续实现必须按这些结论落地,否则会走回头路。
4+
5+
### 0.1 技术选型结论(性能 / 可用 / 稳定 / 迭代 / 完美)
6+
- Markdown 编辑核心:**CodeMirror 6**(源文本为真相 + decorations),承载渲染观感、就地编辑、撤销栈与查找面板。
7+
- 不选:Milkdown/ProseMirror 系 WYSIWYG 作为主路径(存在 Markdown 解析与序列化回写链路,容易触发全局重排,与“最小改动面保存”方向冲突)。
8+
- Monaco:保留为“非 Markdown 的代码/纯文本编辑器兜底”,不承担 Typora 风格主路径。
9+
- 语义选区:用增量语法树驱动(Lezer Markdown 思路),双击选中语义单元;失败必须按 design.md 的降级策略退回行编辑/块编辑/全文源码编辑。
10+
- 行号(纠偏):**不做 DOM data-line 锚点映射**;统一用 CodeMirror 的 gutter 行号能力(lineNumbers)作为主方案。
11+
- 查找:Ctrl+F 必须打开内容区查找面板;滚动策略必须可控,避免横向滚动条被强行拉动(只有命中不可见时才允许调整)。
12+
- 撤销重做:必须支持 Ctrl+Z 撤销;Ctrl+Y 与 Ctrl+Shift+Z 重做(终端除外)。
13+
14+
### 0.2 接手入口(从哪段代码开始)
15+
- 默认内容模式:ContentPanel.vue:244(目前 Markdown 默认 editor,后续需改为 view)
16+
- 任务看板写回:ContentPanel.vue:844(当前正则替换存在误命中风险,后续必须改为“范围锚点 + 指纹副键 + 降级回退”)
17+
- 文件读取元信息注入:project.ts:597(encoding、bom、eol 已进入 FileContent)
18+
- 文件保存保持:main.js:633(BOM 与换行探测/写回),main.js:919(save-file IPC)
19+
- Monaco 兜底编辑器:FileEditor.vue:131(编辑器内 Ctrl+F 查找),FileEditor.vue:158(Ctrl+S 保存)
20+
21+
### 0.3 验收口径(写代码前必须对齐)
22+
- “最小改动面”不是格式化:任何序列化重排都视为失败(spec.md:40)
23+
- tasks 看板编辑必须同源、可撤销,且写回不得依赖“任务文本唯一”假设(design.md:133)
24+
- 行号必须与源文本行号一致,折行不重复(spec.md:83)
25+
- 查找高亮:当前命中蓝色、候选黄色;焦点不离开输入框;纵向滚动可跟随命中,横向滚动仅在命中不可见时才调整
26+
27+
## 1. 需求与设计
28+
- [x] 1.1 补齐 visualizer-core 增量规范(默认渲染、语义双击、必要时显语法、tasks 同源)
29+
- [x] 1.2 输出“语义单元定义表”(段落/标题/列表项/链接/强调/任务项/代码块/表格单元)与降级策略
30+
- [x] 1.3 定义“必要时显示语法”的规则(触发条件、显示范围、退出条件)
31+
- [x] 1.4 定义 tasks.md 看板与源文本的映射规则(最小 patch 方式 + 可撤销)
32+
- [x] 1.5 增加“渲染视图行号”需求与交互约束(可选开关 + 不影响编辑/选区)
33+
34+
## 2. 实施(P0:文本为真相)
35+
- [x] 2.1 引入 CodeMirror 6(仅 Markdown 通道),建立 MarkdownSurface 骨架
36+
- 诉求:Markdown 默认“渲染观感 + 就地编辑”,替代 v-html 与 Monaco 的 Markdown 主路径(spec.md:6)
37+
- 入口:ContentPanel.vue:244(默认模式),ContentPanel.vue:255(模式切换触发渲染)
38+
- 技术:EditorView + extensions(history、search、decorations、gutter)
39+
- 实现:新增 MarkdownSurface;在 view 模式用 decorations 隐藏/弱化语法并保持可编辑;需要时降级到源码编辑
40+
- [x] 2.2 接入 FilePersistence:读取保留 encoding/BOM/EOL;保存按原风格输出;只做最小改动面
41+
- 诉求:保存保持 BOM 与换行风格;不做全局格式化重排(spec.md:40)
42+
- 入口:project.ts:626(FileContent 元信息),preload.js:14(saveFile 桥接),main.js:919(save-file IPC)
43+
- 实现:MarkdownSurface 保存时传入 encoding、bom、eol;未提供时由主进程探测兜底;写回必须保持最小改动面
44+
- [x] 2.3 查找能力:Ctrl+F 打开内容搜索面板;支持 next/prev;不乱改横向滚动条
45+
- 诉求:Ctrl+F 只作用于内容区;当前命中蓝色、候选黄色;焦点不丢;横向滚动仅在命中不可见时才调整
46+
- 入口:ContentPanel.vue:16(现有 find-bar),ContentPanel.vue:414(openFind 入口),FileEditor.vue:131(Monaco openFind 兜底)
47+
- 技术:search 面板 + searchKeymap;用 scrollToMatch 控制滚动策略
48+
- 实现:MarkdownSurface 内统一实现查找与高亮(view 与编辑共用一套),移除重复实现与状态分叉
49+
- [x] 2.4 行号实现纠偏:MarkdownSurface 使用 gutter 行号(lineNumbers);支持开关与持久化;不影响选区与复制
50+
- 诉求:渲染视图可选显示行号;与源文本行号对齐;折行不重复(spec.md:83)
51+
- 入口:design.md:200(行号约束),main.js:21(偏好存储),main.js:1323(偏好读写 IPC)
52+
- 技术:lineNumbers gutter;通过扩展开关控制;CSS 固定 gutter 列宽
53+
- 实现:MarkdownSurface 增加“显示行号”开关;状态持久化到 preferences;不再推进 DOM data-line 映射
54+
## 3. 实施(P1:语义编辑)
55+
- [x] 3.1 双击语义选区:实现语义扩展算法(链接/强调/列表项文本优先级)
56+
- 诉求:双击选中语义单元(链接文本、强调文本优先),而不是简单选词(spec.md:18)
57+
- 依据:design.md:34(语义单元表),design.md:55(降级策略)
58+
- 实现:基于增量语法树定位 from-to 并生成 selection;无法安全定位时必须降级到行/块/全文编辑
59+
- [x] 3.2 就地编辑:编辑在同一视图完成;必要时显语法(光标行/风险区)
60+
- 诉求:默认好读;编辑时只暴露最小必要语法,避免结构被破坏(spec.md:29)
61+
- 依据:design.md:75(触发条件/显示范围/退出条件)
62+
- 实现:用 decorations 控制语法显隐;编辑结束恢复渲染观感;语义定位失败时走降级提示
63+
- [x] 3.3 渲染投影:用 decorations 渲染标题/列表/checkbox 的“Typora 观感”
64+
- 诉求:Typora 风格观感,但源文本仍为真相,不做全局重排(design.md:13)
65+
- 实现:标题/列表 marker/checkbox 用 replace 或 widget 呈现;必要时显语法时回退为可见源码片段
66+
67+
## 4. 实施(P2:tasks 同源)
68+
- [x] 4.1 TaskBoard 改造为结构视图:数据来自 MarkdownSurface 的解析结果
69+
- 诉求:tasks 看板只是结构视图;源文本唯一(design.md:19)
70+
- 入口:ContentPanel.vue:50(现有看板渲染),project.ts:689(parseTaskList)
71+
- 实现:看板只消费 MarkdownSurface 的结构化结果(章节、任务行、描述块、源范围),不再自行维护 content
72+
- [x] 4.2 看板操作映射为最小 patch(勾选/改标题/改描述),并接入统一撤销栈
73+
- 诉求:勾选/改标题/改描述都必须是可撤销的“文本事务”,写回最小范围(design.md:166)
74+
- 入口:ContentPanel.vue:844(toggleTask),ContentPanel.vue:887(saveTaskEdit)
75+
- 实现:由 MarkdownSurface 提供 applyPatch 接口(基于范围);看板事件只发起 patch,不直接正则替换全文
76+
- [x] 4.3 任务进度徽章与自动刷新保持一致,不引入闪烁/抖动
77+
- 诉求:进度统计与展示必须基于同一份源文本;刷新不闪(design.md:189)
78+
- 入口:project.ts:659(parseTaskProgress),ContentPanel.vue:806(taskProgress)
79+
- 实现:统一从 MarkdownSurface 文本状态派生进度;更新节流,避免“内容抖动”
80+
81+
## 4.4 快捷键
82+
- [x] 4.4.1 撤销/重做快捷键:Ctrl+Z 撤销;Ctrl+Y / Ctrl+Shift+Z 重做(终端除外)
83+
- 诉求:符合市面习惯(spec.md:58)
84+
- 实现:MarkdownSurface 使用 history + keymap;必要时提高优先级以覆盖默认绑定;终端保持独立逻辑
85+
86+
## 5. 验证
87+
- [x] 5.1 冒烟:Markdown 默认渲染;双击编辑;必要时显语法;保存不破坏 BOM/EOL
88+
- 覆盖点:语义双击、降级编辑、保存风格保持、行号开关不影响选区/复制
89+
- [x] 5.2 冒烟:tasks.md 默认看板;双击/看板改动都能保存且一致;撤销/重做可用
90+
- 覆盖点:勾选/改标题/改描述的最小 patch、撤销栈一致性、进度徽章刷新无闪烁
91+
- [x] 5.3 运行 openspec-cn validate add-inline-semantic-editing --strict
92+
- 注意:当前环境可能无法执行 openspec-cn.ps1;必要时用 node 直接执行 openspec.js 进行验证
93+
- [x] 5.4 更新 README.md:同步最新功能描述与使用路径(安装、运行、快捷键、行号开关、编辑方式)
94+
- 诉求:避免新功能落地但文档入口过期;README 必须可作为“新手第一天”使用指引
95+
96+
## 6. 补充修复(表格/行号/稳定性)
97+
- [x] 6.1 修复 Markdown 打开即崩溃:decorations 构建不满足 sorted 约束(RangeSetBuilder)
98+
- 现象:打开 .md 报 “Ranges must be added sorted by from position and startSide”
99+
- 实现:改为基于 ranges 构建并交给 CodeMirror 排序,避免手写排序与 startSide 偏差
100+
- [x] 6.2 表格渲染:不再“点击表格回到源码”,保持表格观感且源文本可编辑
101+
- 实现:表格行用 line + mark decorations 做 grid 投影;pipe 与边缘空白隐藏;分隔线用 divider 投影(非 block),避免点击回源码
102+
- 交互:点击/双击单元格可直接编辑 cell;空 cell 也保留可点击的占位区域
103+
- [x] 6.4 表格细节对齐(Typora 风格):列宽自适应、header/body 连贯、code span 内 `|` 不切列
104+
- 诉求:列宽跟内容一起“看起来合理”,不再固定 180px;表头与内容行不出现断层;`a|b` 这类行内代码不应被当作分隔符
105+
- 实现:每张表计算 templateColumns,并写到行级 style(统一 header/body/divider 的 grid-template-columns);divider 变成单线分隔;表格行扫描 split 基于 code span 规则跳过 `|`
106+
- [x] 6.3 行号 gutter 观感:贴左、不透明、横向滚动不覆盖内容
107+
- 实现:gutter 背景与内容区拉开层级;行号右对齐;保持 sticky 左侧定位

0 commit comments

Comments
 (0)