- 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.
10 KiB
Требуемый ресёрч
Зафиксировано после 4-агентного ревью плана. Каждый пункт — пробел в знаниях, без закрытия которого реализация рискованна.
R1: Scope isolation горячих клавиш
Проблема: 6 из 12 шорткатов (Cmd+W, Cmd+B, Cmd+F, Cmd+Shift+[/], Ctrl+Tab) конфликтуют с глобальными в useKeyboardShortcuts.ts.
Нужно выяснить:
- Как CM6 keymaps (через
keymap.of()) взаимодействуют с глобальнымwindow.addEventListener('keydown')? - Останавливает ли CM6 propagation события?
- Какой паттерн используют VS Code / другие Electron-editors для scope isolation?
- Варианты: guard
isEditorOpenв глобальном хуке? KeyboardEvent stack? Priority system?
Статус: COMPLETED
Результат:
- Глобальный handler в
useKeyboardShortcuts.tsиспользуетwindow.addEventListener('keydown')в bubble phase (строка 278) - CM6 использует bubble phase на
.cm-content, вызываетpreventDefault()но НЕstopPropagation()по умолчанию - CM6 поддерживает
stopPropagation: trueопцию per keybinding ChangeReviewDialogуже использует capture-phase handler с guard (строки 379-408)- Рекомендация: Approach A — Guard в глобальном handler с флагом
editorOverlayOpenв store (~20-30 LOC, надёжность 8/10)- В
useKeyboardShortcuts.ts:const editorOpen = useStore(s => s.editorProjectPath !== null);→ early return для конфликтующих shortcuts - Плюс: добавить
stopPropagation: trueк CM6 keybindings как safety net
- В
- Конкретные конфликты:
Cmd+W(строка 155),Cmd+B(271),Cmd+F(241),Cmd+Shift+[/](177),Ctrl+Tab(81)
R2: CM6 Compartment + EditorState pooling
Проблема: План хранит Compartments в useRef (один экземпляр) и использует для 30+ EditorState в пуле. CM6 может не поддерживать sharing одного Compartment между разными EditorState.
Нужно выяснить:
- Документация CM6: привязан ли Compartment к конкретному EditorState?
- Что происходит при
compartment.of(X)в extensions для разных EditorState? - Что происходит при
dispatch({ effects: compartment.reconfigure(Y) })если другой state в кэше использует тот же Compartment? - Паттерн из CodeMirrorDiffView: один View + один State — работает, но не pooling.
- Альтернатива: Compartment-per-state в Map (рядом с EditorState).
Статус: COMPLETED
Результат:
- Compartment — opaque identity token без внутреннего state. Подтверждено Marijn Haverbeke (автор CM6): "Compartments can be shared without issue"
- Каждый EditorState имеет свой
Map<Compartment, Extension>в config reconfigure()на одном View не влияет на cached states в пуле- Рекомендация: Option A — Общие Compartment-инстансы для всех states (надёжность 9/10)
- useRef Compartments безопасны для sharing:
readOnlyCompartment.current.of(...)в extensions для каждого нового EditorState - При unmount+remount: кешированные states ссылаются на старые Compartments → при remount создать новые Compartments, заново создать EditorState для активного таба
- LRU eviction теряет undo history (ожидаемо), cursor сохраняется через EditorSelection
- useRef Compartments безопасны для sharing:
R3: Store ↔ Component ref bridge (closeEditor + saveAllFiles)
Проблема: closeEditor() и saveAllFiles() в Zustand action должны работать с stateCache и viewRef из useRef компонента CodeMirrorEditor. Zustand actions не имеют доступа к React refs.
Нужно выяснить:
- Как существующий код решает аналогичные проблемы (например, terminal cleanup)?
- Варианты: (a) global ref registry; (b) store хранит cleanup callback через
registerCleanup(fn); (c) компонент слушаетeditorProjectPath === nullв useEffect и делает cleanup сам; (d) zustand subscribe в компоненте. - Какой вариант минимально инвазивен и надёжен при unmount?
Статус: COMPLETED
Результат: Существующие паттерны в кодовой базе:
MembersJsonEditor— компонент владеет lifecycle полностьюCodeMirrorDiffView— внешний ref holderchangeReviewSlice— module-level state (строки 5-12)ConfirmDialog— singleton-регистрация с module-levelglobalSetStateChangeReviewDialog— компонент оркестрирует
Рекомендация: Hybrid C+D — editorBridge.ts module-level singleton (надёжность 9/10):
// src/renderer/utils/editorBridge.ts (module-level)
let stateCache: Map<string, EditorState> | null = null;
let scrollTopCache: Map<string, number> | null = null;
let viewRef: EditorView | null = null;
export const editorBridge = {
register(sc, stc, v) { stateCache = sc; scrollTopCache = stc; viewRef = v; },
unregister() { stateCache = null; scrollTopCache = null; viewRef = null; },
getContent(filePath) { return stateCache?.get(filePath)?.doc.toString() ?? null; },
getAllModifiedContent(modifiedFiles) { /* iterate stateCache */ },
destroy() { viewRef?.destroy(); stateCache?.clear(); scrollTopCache?.clear(); },
};
- Компонент вызывает
register()при mount,unregister()при unmount - Store actions (closeEditor, saveAllFiles) используют
editorBridge.getContent()/editorBridge.destroy() saveAllFiles: компонент итерирует и вызываетstore.saveFile()для каждого (паттерн ChangeReviewDialog)discardChanges: store делает IPC read, компонент применяет черезview.dispatch({ changes })
R4: fs.watch recursive на Linux + watcher reliability
Проблема: fs.watch({ recursive: true }) экспериментальный на Linux в Node.js 18. Может тихо не работать. Нет fallback.
Нужно выяснить:
- Какую версию Node.js использует Electron 40? Поддерживается ли
recursive: trueна Linux? - Существующий
FileWatcher.tsв проекте: использует ли онrecursive: true? Есть ли fallback? - Альтернативы: chokidar (но добавляет зависимость), polling, non-recursive watch + manual traversal.
max_user_watchesлимит inotify — как обойти?- macOS FSEvents: coalescing events — как существующий FileWatcher решает это?
Статус: COMPLETED
Результат:
- Electron 40 = Node.js 24 (LTS) —
recursive: trueработает на macOS (FSEvents, надёжность 9/10) и Linux (inotify, надёжность 6/10) - Существующий
FileWatcher.ts(1098 строк) — зрелый watcher с debounce (строки 1060-1074), recovery (424-457), catch-up scan (992-1051) - НЕ добавлять chokidar — использовать паттерны из FileWatcher.ts
- macOS: fs.watch recursive reliable (FSEvents), burst coalescing для git checkout сценариев
- Linux:
ENOSPC→ fallback на polling (5-10 секунд интервал) max_user_watchesлимит inotify: при ENOSPC не падать, а деградировать до polling- Рекомендация: EditorFileWatcher ~250-300 LOC (вместо первоначальных ~60 LOC), включая:
- Burst coalescing (200ms debounce + batch)
- ENOSPC fallback to polling
- Фильтрация: node_modules/.git/dist
- Graceful stop/restart
R5: Git CLI availability & performance
Проблема: git status --porcelain без проверки наличия git. На больших монорепо — десятки секунд. Non-git проекты не обработаны.
Нужно выяснить:
- Есть ли в проекте утилита проверки наличия git? (проверить существующие git-related сервисы)
git status --porcelainperformance:--untracked-files=noускоряет?--ignore-submodules?- Timeout стратегия: AbortSignal + child_process?
- Graceful degradation: что показывать если git недоступен / не git-repo?
.git/index.lock— как обрабатывать concurrent git operations?
Статус: COMPLETED
Результат:
isGitRepo()уже есть вGitDiffFallback.ts(строки 118-133) — переиспользовать- Оптимальная команда:
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
- Добавить
'conflict'статус вGitFileStatusдля merge conflicts (UU,AA,DDкоды) - Graceful degradation: проверить git available → проверить is repo → timeout handling
- Нет git: скрыть git бейджи, показать "Git not available" в status bar
- Не git-repo: скрыть git бейджи
- Timeout: показать "Git status unavailable" + кнопка retry
- GitStatusService ~200-250 LOC, EditorFileWatcher ~250-300 LOC