agent-ecosystem/docs/iterations/edit-project/iter-5-git-watching.md
iliya d7fc71a5e6 feat: enhance project editor with file management, Git integration, and UI improvements
- Introduced `EditorFileWatcher` for live file change detection and `GitStatusService` for displaying Git status in the file tree.
- Added context menu for file operations (create, delete) and implemented multi-tab support for the editor.
- Enhanced user experience with keyboard shortcuts, search functionality, and breadcrumb navigation.
- Updated IPC channels for file operations and integrated conflict detection during file saves.
- Improved performance with file watcher optimizations and virtualized file tree rendering.
2026-02-28 13:35:22 +02:00

96 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Итерация 5: Git status, file watching, расширенные возможности
> Зависит от: [Итерация 4](iter-4-search-shortcuts.md)
## Цель
Git status в дереве файлов. Live refresh при изменениях на диске. Conflict detection при сохранении. Line wrap toggle.
## IPC каналы
| Канал | Описание |
|-------|----------|
| `editor:gitStatus` | `git --no-optional-locks status --porcelain -z --untracked-files=normal --ignore-submodules`, кеш 5 сек |
| `editor:watchDir` | Запуск file watcher (opt-in, НЕ по умолчанию) |
| `editor:change` | Event: файл изменился на диске (main -> renderer) |
## Новые файлы
| # | Файл | Описание |
|---|------|----------|
| 1 | `src/main/services/editor/EditorFileWatcher.ts` | FileWatcher (~250-300 LOC, не ~60!). fs.watch + burst coalescing (200ms debounce + batch) + ENOSPC fallback to polling (Linux). Фильтрация node_modules/.git/dist |
| 2 | `src/main/services/editor/GitStatusService.ts` | `git --no-optional-locks status --porcelain -z` парсинг, кеш 5 сек. Переиспользовать `isGitRepo()` из `GitDiffFallback.ts` (~200-250 LOC) |
| 3 | `src/main/services/editor/conflictDetection.ts` | Утилита mtime check: сравнение mtime до/после save, conflict resolution (~40 LOC) |
| 4 | `src/renderer/components/team/editor/GitStatusBadge.tsx` | M/U/A бейджи в дереве |
## Изменения в существующих файлах
| # | Файл | Изменение |
|---|------|-----------|
| 1 | `src/shared/types/editor.ts` | `GitFileStatus`, `EditorFileChangeEvent` |
| 2 | `src/shared/types/api.ts` | `gitStatus`, `onEditorChange` в EditorAPI |
| 3 | `src/main/ipc/editor.ts` | Handlers для git status и file watcher |
| 4 | `src/preload/index.ts` | `editor.gitStatus`, `editor.onEditorChange` (НЕ `onFileChange` — конфликт с существующим `ElectronAPI.onFileChange`) |
| 5 | `src/preload/constants/ipcChannels.ts` | `EDITOR_GIT_STATUS`, `EDITOR_WATCH_DIR`, `EDITOR_CHANGE` |
| 6 | `src/renderer/components/team/editor/EditorFileTree.tsx` | Git status badges |
| 7 | `src/renderer/components/team/editor/CodeMirrorEditor.tsx` | Conflict detection (mtime check) при сохранении |
| 8 | `src/renderer/components/team/editor/ProjectEditorOverlay.tsx` | File watcher подписка, auto-refresh, conflict modal |
| 9 | `src/renderer/store/slices/editorSlice.ts` | Git status data, file watcher state |
| 10 | `src/renderer/store/index.ts` | В `initializeNotificationListeners()` добавить подписку `if (api.editor?.onEditorChange)` → обновление дерева/табов при внешних изменениях (guard обязателен — паттерн из всех существующих subscriptions) |
| 11 | `src/main/index.ts` | `mainWindow.on('closed')``cleanupEditorState()`. `shutdownServices()``cleanupEditorState()` |
## Security-требования
1. `editor:gitStatus`: `cwd = activeProjectRoot` (валидный). Не передавать full paths от git без валидации
2. `editor:change`: пути в events могут утечь через symlink -- валидировать перед передачей в renderer (SEC-2)
## Watcher lifecycle cleanup (macOS: window closed but app alive)
- `editor:open` — если `activeProjectRoot !== null`, сначала остановить предыдущий watcher и сбросить state (идемпотентный reset). Guard: `if (activeProjectRoot !== null) throw new Error('Another editor is already open')`
- `mainWindow.on('closed')` в `src/main/index.ts` — вызвать `cleanupEditorState()` (экспорт из `editor.ts`): сброс `activeProjectRoot`, остановка watcher. Аналог существующего cleanup для `notificationManager`, `ptyTerminalService`
- `shutdownServices()` — добавить `cleanupEditorState()` рядом с `removeIpcHandlers()`
## Performance-требования (R4/R5)
- File watcher opt-in: по умолчанию ВЫКЛЮЧЕН. Toggle "Watch for external changes". По умолчанию ручной refresh (F5)
- `fs.watch({ recursive: true })` + фильтрация (node_modules/.git/dist) + burst coalescing 200ms
- **macOS**: FSEvents reliable (надёжность 9/10)
- **Linux**: inotify (надёжность 6/10). При `ENOSPC` → fallback на polling (5-10 сек). НЕ падать, деградировать
- Git status кешировать на 5 секунд. Invalidate по file watcher event
- Git command: `git --no-optional-locks status --porcelain -z --untracked-files=normal --ignore-submodules`
- `--no-optional-locks` — предотвращает `.git/index.lock` конфликты (критично!)
- `-z` — NUL-separated вывод (безопасный парсинг путей с пробелами)
- `--ignore-submodules` — ускорение на монорепо
- Timeout: 10 секунд (паттерн из GitDiffFallback.ts), AbortSignal
- Graceful degradation:
- Нет git → скрыть git бейджи, "Git not available" в status bar
- Не git-repo → скрыть git бейджи
- Timeout → "Git status unavailable" + кнопка retry
## UX-требования
- File changed on disk while open: banner в табе "File changed on disk. [Reload] [Keep mine] [Show diff]" (НЕ перезаписывать молча)
- File deleted on disk while open: banner "File no longer exists on disk. [Close tab]"
- Conflict detection при save: mtime check. Если изменился -- dialog "Overwrite / Cancel / Show diff"
- Line wrap toggle в toolbar
## Тестирование
| # | Что тестировать | Файл |
|---|----------------|------|
| 1 | `GitStatusService` -- парсинг `git status --porcelain` вывода | `test/main/services/editor/GitStatusService.test.ts` |
| 2 | `EditorFileWatcher` -- debounce, event types | `test/main/services/editor/EditorFileWatcher.test.ts` |
| 3 | `conflictDetection` -- mtime check логика | `test/main/services/editor/conflictDetection.test.ts` |
| 4 | Manual: изменить файл в внешнем редакторе -> conflict banner | — |
## Критерии готовности
- [ ] Git status бейджи (M/U/A) в файловом дереве
- [ ] Auto-refresh при изменениях на диске (при включённом watcher)
- [ ] Conflict detection при сохранении
- [ ] Line wrap toggle
## Оценка
- **Надёжность решения: 7/10** (было 6/10) -- R4/R5 ресёрч закрыл ключевые пробелы: `--no-optional-locks`, ENOSPC fallback, burst coalescing. Race conditions остаются, но митигированы.
- **Уверенность: 8/10** (было 7/10) -- паттерны FileWatcher + GitDiffFallback изучены, переиспользование кода подтверждено.