- 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.
5.4 KiB
5.4 KiB
Итерация 2: Editable CodeMirror + сохранение файлов
Зависит от: Итерация 1
Цель
Переключить CodeMirror из read-only в редактируемый режим. Cmd+S для сохранения. Индикатор unsaved changes. Status bar.
IPC каналы
| Канал | Описание |
|---|---|
editor:writeFile |
Запись файла (atomic write через tmp + rename) |
Новые файлы
| # | Файл | Описание |
|---|---|---|
| 1 | src/main/utils/atomicWrite.ts |
Перемещение существующего atomicWriteAsync() из src/main/services/team/atomicWrite.ts (shared utility). H2: Blast radius — ~10 source файлов + ~4 тестовых файла (TeamTaskWriter, TeamDataService, TeamKanbanManager, TeamAgentToolsInstaller, и их тесты). Обновить все импорты |
| 2 | src/renderer/components/team/editor/EditorTabBar.tsx |
Панель вкладок (один файл пока, подготовка к multi-tab) |
| 3 | src/renderer/components/team/editor/EditorStatusBar.tsx |
Ln:Col, язык, отступы |
| 4 | src/renderer/components/team/editor/EditorToolbar.tsx |
Save, Undo, Redo |
| 5 | src/renderer/utils/editorBridge.ts |
Module-level singleton: Store ↔ CM6 refs bridge (R3). Компонент вызывает register() при mount, store actions используют getContent()/destroy() |
Изменения в существующих файлах
| # | Файл | Изменение |
|---|---|---|
| 1 | src/shared/types/editor.ts |
Типы для write request/response |
| 2 | src/shared/types/api.ts |
writeFile в EditorAPI |
| 3 | src/main/services/editor/ProjectFileService.ts |
Метод writeFile() с atomic write |
| 4 | src/main/ipc/editor.ts |
Handler editor:writeFile |
| 5 | src/preload/index.ts |
editor.writeFile |
| 6 | src/preload/constants/ipcChannels.ts |
EDITOR_WRITE_FILE |
| 7 | src/renderer/components/team/editor/ProjectEditorOverlay.tsx |
Интеграция TabBar, StatusBar |
| 8 | src/renderer/components/team/editor/CodeMirrorEditor.tsx |
Убрать readOnly, EditorState pooling (Map<tabId, EditorState>), Cmd+S keymap |
| 9 | src/renderer/store/slices/editorSlice.ts |
Расширить: +Группа 2 (tabs) + Группа 3 (dirty/save) |
| 10 | src/renderer/index.css |
+8 editor CSS-переменных (--editor-tab-active-bg, --editor-tab-modified-dot и др.) |
Security-требования
writeFile:validateFilePath()ДО записи. + SEC-14:isPathWithinRoot(normalizedPath, activeProjectRoot)для блокировки~/.claudewrites.Buffer.byteLength(content, 'utf8') <= 2MB. Atomic write. Запрет записи в.git/.activeProjectRootиз module-level state (SEC-9, SEC-12)- Файл удалён извне при save: ENOENT -> inline-ошибка "File was deleted. Create new? / Close tab" (не падать)
Performance-требования
- НЕ хранить modified content в Zustand. Контент только в EditorState CM. В store:
editorModifiedFiles: Record<string, boolean>(dirty flags — Record вместо Set, т.к. Zustand не отслеживает мутации Set) - Dirty flag через debounced
EditorView.updateListener(300ms) - Гранулярные Zustand-селекторы: FileTreePanel не подписывается на tabs/content
- EditorState pooling: один EditorView, Map<tabId, EditorState> в useRef
- LRU eviction при > 30 states
UX-требования
- Status bar:
[Ln 42, Col 15] | [TypeScript] | [UTF-8] | [Spaces: 2] - Unsaved changes при закрытии overlay: три кнопки ("Save All & Close" / "Discard & Close" / "Cancel")
- Dirty indicator (точка) на вкладке ПЕРЕД текстом
hasUnsavedChanges()в slice
Тестирование
| # | Что тестировать | Файл |
|---|---|---|
| 1 | ProjectFileService.writeFile -- запись с mock fs, reject для файлов вне проекта, atomic write |
test/main/services/editor/ProjectFileService.test.ts (расширение) |
| 2 | editorSlice -- open/close файлы, dirty state, save |
test/renderer/store/editorSlice.test.ts (расширение) |
| 3 | atomicWrite -- unit тесты |
test/main/utils/atomicWrite.test.ts |
| 4 | EditorState pooling -- save/restore state при switch tab | — |
| 5 | Manual: открыть файл -> отредактировать -> Cmd+S -> dirty indicator сбрасывается | — |
Критерии готовности
- Файл редактируется в CodeMirror (не read-only)
- Cmd+S сохраняет файл через atomic write
- Dirty indicator на вкладке
- Status bar показывает позицию курсора и язык
- При закрытии overlay с unsaved changes -- confirmation dialog
- Benchmark: 0 re-render FileTreePanel/TabBar при наборе текста
Оценка
- Надёжность решения: 7/10 -- atomic write и EditorState pooling добавляют сложность.
- Уверенность: 8/10 -- паттерны известны, но dirty tracking через CM6 updateListener требует тестирования.