**字段级合并策略配置**:

# 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 前:记录文件 hash
2. 外部编辑器关闭后:检查 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 pullgit 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

# 输出示例
![image](https://your-bucket.s3.amazonaws.com/synapse/10.001.0015/abc123.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 媒体存储配置
 
 
Back to Top