**字段级合并策略配置**:
**冲突安全网**:
- 当 Merge Driver 无法自动解决(如正文同一行被修改):
- 生成
- TUI 启动时检测到冲突文件 → 进入"冲突解决向导"模式
- 用户可选择:保留本地 / 保留远程 / 手动合并
### 7.6 并发编辑检测
**场景**:TUI 打开时,用户用 VS Code 编辑同一文件。
**方案 1:Hash 检测**(基础)
1.
2. 外部编辑器关闭后:检查 hash
3. 如果 hash 变化且非当前进程修改:
- 提示冲突
- 显示 diff
- 用户选择:覆盖 / 合并 / 放弃
**方案 2:watchdog 实时监听**(增强)
使用
**线程安全注意**:
-
- Textual UI 更新必须在主线程
- 必须使用
**自激抑制(Suspension Context)**:
⚠️ **问题**:当 Synapse 执行
**行为**:
- TUI 打开时,自动启动 watchdog **仅监听当前 Namespace 目录**(性能优化)
- 切换 Namespace 时,watchdog 同步切换监听目标
- 检测到当前查看/编辑的文件被外部修改 → 实时弹出警告
- 用户可选择:重新加载 / 忽略 / 查看 diff
- **Git 操作期间自动暂停监听**,避免误报
---
## 8. 媒体管理(优先级低,预留设计)
> ⚠️ 本章节为预留设计,待后续迭代完善。初始版本仅支持外部链接引用媒体。
### 8.1 设计原则
- **仓库纯净**:禁止二进制文件直接入库
- **体验友好**:提供便捷的截图/贴图工作流
### 8.2
### 8.3 媒体存储目录结构
**建议使用资源 ID 作为目录名**,便于关联和管理:
### 8.4 媒体存储配置
# config.yaml
git:
merge_strategy:
# 默认策略:取 modified 最新的一方 (Last Write Wins)
default: "take_latest_modified"
# 特殊字段策略
fields:
# 标签:取并集。A端加了[urgent], B端加了[work] -> [urgent, work]
tags: "union"
# 关联:取并集
related: "union"
# 状态:取最新修改方(或自定义优先级:done > in-progress > todo)
status: "take_latest_modified"
# 正文内容:标准 Git 文本合并
content: "git_standard"
**冲突安全网**:
- 当 Merge Driver 无法自动解决(如正文同一行被修改):
- 生成
.conflict 备份文件- TUI 启动时检测到冲突文件 → 进入"冲突解决向导"模式
- 用户可选择:保留本地 / 保留远程 / 手动合并
### 7.6 并发编辑检测
**场景**:TUI 打开时,用户用 VS Code 编辑同一文件。
**方案 1:Hash 检测**(基础)
1.
syn edit 前:记录文件 hash2. 外部编辑器关闭后:检查 hash
3. 如果 hash 变化且非当前进程修改:
- 提示冲突
- 显示 diff
- 用户选择:覆盖 / 合并 / 放弃
**方案 2:watchdog 实时监听**(增强)
使用
watchdog 库监听文件系统事件,TUI 运行时实时感知外部修改:
# 伪代码示意 - 注意线程安全!
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
class SynapseFileWatcher(FileSystemEventHandler):
def __init__(self, app):
self.app = app # Textual App 实例
def on_modified(self, event):
if event.src_path == self.app.current_open_file:
# ⚠️ 关键:watchdog 在独立线程,必须用 call_from_thread
self.app.call_from_thread(
self.app.show_file_modified_warning,
event.src_path
)
**线程安全注意**:
-
watchdog 运行在独立线程- Textual UI 更新必须在主线程
- 必须使用
app.call_from_thread() 跨线程调用,否则 TUI 会崩溃**自激抑制(Suspension Context)**:
⚠️ **问题**:当 Synapse 执行
git pull 或 git checkout 时,文件系统变化会触发 watchdog,错误地向用户报告"外部修改警告"。
class FileWatcher:
def __init__(self):
self._suspended = False
def pause(self):
"""暂停监听(Git 操作前调用)"""
self._suspended = True
def resume(self):
"""恢复监听(Git 操作后调用)"""
self._suspended = False
def on_modified(self, event):
if self._suspended:
return # 忽略自身触发的变更
# ... 正常处理外部修改
class SynapseApp:
def perform_git_operation(self):
self.file_watcher.pause() # 暂停监听
try:
git.pull()
self.index.refresh() # 主动刷新索引
finally:
self.file_watcher.resume() # 恢复监听
**行为**:
- TUI 打开时,自动启动 watchdog **仅监听当前 Namespace 目录**(性能优化)
- 切换 Namespace 时,watchdog 同步切换监听目标
- 检测到当前查看/编辑的文件被外部修改 → 实时弹出警告
- 用户可选择:重新加载 / 忽略 / 查看 diff
- **Git 操作期间自动暂停监听**,避免误报
class SynapseFileWatcher:
def __init__(self, app):
self.app = app
self.observer = Observer()
self._current_path = None
self._started = False
def start(self):
"""启动观察者线程(TUI 启动时调用)"""
if not self._started:
self.observer.start()
self._started = True
def stop(self):
"""停止观察者线程(TUI 退出时调用)"""
if self._started:
self.observer.stop()
self.observer.join() # 等待线程结束
self._started = False
def watch_namespace(self, namespace_path: str):
"""切换监听目标到指定 Namespace"""
# 1. 取消之前的监听
if self._current_path:
self.observer.unschedule_all()
# 2. 调度新的监听目标
self._current_path = namespace_path
self.observer.schedule(
self,
namespace_path,
recursive=True # 监听 Namespace 下所有子目录
)
# 3. 确保观察者已启动
self.start()
---
## 8. 媒体管理(优先级低,预留设计)
> ⚠️ 本章节为预留设计,待后续迭代完善。初始版本仅支持外部链接引用媒体。
### 8.1 设计原则
- **仓库纯净**:禁止二进制文件直接入库
- **体验友好**:提供便捷的截图/贴图工作流
### 8.2
syn media 命令
# 从剪贴板上传图片
syn media paste
# → 自动上传到配置的存储 → 返回 Markdown 链接
# 上传指定文件
syn media upload /path/to/image.png
# 输出示例

### 8.3 媒体存储目录结构
**建议使用资源 ID 作为目录名**,便于关联和管理:
媒体存储根目录/
├── 10.001.0015/ # 资源 ID 为目录名
│ ├── screenshot-001.png
│ └── diagram.svg
├── 10.002.0003/
│ └── meeting-photo.jpg
└── _orphan/ # 未关联资源的媒体(待清理)
└── temp-upload.png
### 8.4 媒体存储配置