agent-ecosystem/docs/iterations/edit-project/plan-iterations.md
Илия 945d5b237c
feat: project editor with drag & drop, search, Quick Open (#11)
* fix: add retry logic to sendInboxMessage for concurrent writes

On Windows, parallel writes to the same inbox file cause race conditions
where atomicWrite verification fails (another process overwrites between
write and verify). Added retry loop (8 attempts) matching the existing
pattern in addTaskComment. Bumps teamctl version to 11.

Fixes CI failure: test (windows-latest) "parallel messages to same inbox"

* fix: enhance CLI installer and session management

- Updated the postinstall script in package.json to handle rebuild failures gracefully.
- Added clearContext option in team launch requests to allow starting fresh sessions without resuming previous context.
- Improved CLI installer logging by integrating raw output chunks for better terminal rendering.
- Refactored components to utilize TerminalLogPanel for displaying installation logs, enhancing user experience during CLI installation.
- Updated various services and hooks to support the new clearContext feature and raw logging.

* fix: update MemberBadge and LaunchTeamDialog components for improved functionality

- Modified MemberBadge to display 'lead' for team leads instead of the full name.
- Refactored LaunchTeamDialog to simplify model selection logic and replace the Select component with a custom button-based interface for better user experience.
- Enhanced KanbanTaskCard to include meta actions for task management, improving the layout and functionality for manual review tasks.

* feat: auto-publish releases with stable download links

- Change releaseType from draft to release for auto-publishing
- Add upload-stable-links job to create version-agnostic asset copies
- Update README with direct download URLs per platform
- Add Requirements section to Installation
- Remove downloads/platform badges
- Add docs/RELEASE.md with versioning and release guide
- Move community docs to .github/

* improvemtns

* improvement

* fix: handle Windows spawn EINVAL on non-ASCII paths and add helper utilities

* improvements

* fix: enhance child process environment handling for Windows

- Added a helper function to build the child process environment with the correct HOME directory, addressing issues with non-ASCII usernames on Windows.
- Updated CLI installer methods to utilize the new environment setup for improved compatibility and error handling.

* refactor: replace execFile and spawn with execCli and spawnCli in CLI and TeamProvisioning services

- Updated CliInstallerService and TeamProvisioningService to use execCli and spawnCli for improved error handling and compatibility, particularly on Windows.
- Enhanced child process utility functions to better manage non-ASCII paths and provide consistent behavior across different platforms.
- Adjusted tests to mock new child process utilities and verify correct usage in service methods.

* fix

* fix windows

* feat: add download badges with direct links per platform

* refactor: move download buttons to Installation section

* refactor: move Docker files to docker/ and CHANGELOG to docs/

* refactor: move vite.standalone.config to docker/, remove .nvmrc

* refactor: merge tsconfig.test.json into tsconfig.json

* refactor: remove .editorconfig, .gitattributes, merge knip.json into package.json

* fix: adjust macOS download badge sizing

* feat: implement in-app project editor with CodeMirror integration

- Added architectural plan and iteration plan for the in-app project editor.
- Introduced new components for the editor, including CodeEditorOverlay, FileTreePanel, and EditorTabsPanel.
- Established state management using Zustand for editor state persistence.
- Implemented IPC channels for file operations and editor functionality.
- Enhanced TeamDetailView with a button to open the editor overlay.
- Conducted reuse analysis for existing components to optimize codebase integration.

* feat: enhance in-app project editor with architecture documentation and service updates

- Added detailed architecture and component hierarchy documentation for the in-app project editor.
- Introduced `ProjectFileService` to manage file operations with improved path validation.
- Updated `electron.vite.config.ts` to set `UV_THREADPOOL_SIZE` for better performance on Windows.
- Deferred non-critical startup tasks in `index.ts` to avoid thread pool contention.
- Enhanced `CliInstallerService` with timeout handling for status gathering to prevent UI hangs.
- Added tests for `CliInstallerService` to ensure proper timeout behavior.

* 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.

* feat: enhance project editor with autosave, improved file management, and performance optimizations

- Implemented draft autosave functionality to prevent data loss during crashes, with recovery options for unsaved changes.
- Updated file management services to support better path validation and conflict detection.
- Enhanced performance with optimized file watcher and caching strategies for project scanning.
- Improved user experience with confirmation dialogs for unsaved changes and refined keyboard shortcuts.
- Documented testing strategies and rollback plans for iterative development.

* feat: integrate simple-git for enhanced Git status tracking and improve file watcher performance

- Replaced direct Git command usage with `simple-git` for more reliable status tracking, including support for renamed files and conflict detection.
- Updated IPC channels to reflect changes in Git status retrieval method.
- Enhanced file watcher initialization on Windows to prevent UV thread pool saturation by starting watchers sequentially.
- Improved application startup by staggering context system initialization and notification listeners to optimize performance.

* feat: optimize IPC initialization and context management for improved app performance

- Deferred IPC-heavy initialization to occur after the first paint to prevent app freezing on Windows.
- Staggered notification listener setup to avoid saturating the UV thread pool during startup.
- Updated context system initialization to be lazy, ensuring local context is always ready without upfront costs.
- Enhanced data fetching sequence to reduce simultaneous IPC calls, improving overall responsiveness.

* feat: enhance project editor with new error boundary and file handling improvements

- Introduced `EditorErrorBoundary` component to catch runtime errors in CodeMirror, providing a fallback UI to prevent crashes.
- Updated file handling logic to utilize `isbinaryfile` for more reliable binary detection, replacing manual null-byte scans.
- Enhanced `openFile` method to prevent duplicate tabs for already opened files.
- Improved documentation for new components and updated file lists to reflect recent changes.
- Optimized file watcher and path validation processes for better performance and reliability.

* feat: enhance TeamConfigReader with improved file handling and concurrency

- Introduced `mapLimit` function to manage concurrent processing of team directories, optimizing performance.
- Added `readFileHead` function to read the beginning of large configuration files efficiently.
- Implemented `extractQuotedString` to safely extract values from JSON strings in configuration headers.
- Enhanced error handling and validation for team configuration files, ensuring robust processing of team data.
- Updated logic to handle large configuration files differently, improving overall reliability and performance.

* feat: enhance team management with improved session and project path history handling

- Introduced constants for maximum session and project path history limits to optimize memory usage.
- Updated `TeamConfigReader` and `TeamProvisioningService` to limit session and project path history to defined maximums.
- Enhanced `TeamMembersMetaStore` to handle large meta files more efficiently by checking file size before processing.
- Refactored state management in `teamSlice` to include optimized lookups for team summaries by name and session ID.

* feat: enhance GlobalTaskDetailDialog and TaskDetailDialog with loading state management

- Added a loading state to the TaskDetailDialog to display a loading indicator while fetching team data.
- Updated GlobalTaskDetailDialog to pass the loading state to TaskDetailDialog.
- Modified the selectTeam function in teamSlice to accept options for skipping project auto-selection, improving team data handling.

* feat: optimize team display name resolution and enhance file change handling

- Introduced a caching mechanism for team display names to reduce redundant API calls and improve performance.
- Updated file change event handling to debounce cache invalidation, preventing unnecessary rescans during rapid file changes.
- Enhanced GlobalTaskDetailDialog and TaskDetailDialog to manage loading states and improve team data fetching logic.
- Refactored teamSlice to streamline team selection and data loading processes.

* fix: improve team selection logic and prevent duplicate fetches

- Enhanced GlobalTaskDetailDialog to handle loading states more effectively, preventing unnecessary re-fetching of team data.
- Updated selectTeam function in teamSlice to guard against duplicate in-flight fetches for the same team, improving performance and user experience.
- Refactored dependencies in useEffect to ensure proper data loading behavior.

* feat: enhance team data fetching with performance logging and timeout handling

- Added performance logging to the handleGetData function, tracking the duration of team data retrieval and logging warnings for slow responses.
- Implemented a timeout mechanism in the selectTeam function to prevent long-running fetch operations, improving user experience.
- Enhanced the getTeamData method with detailed timing metrics for each data loading step, allowing for better performance analysis and debugging.

* feat: implement timeout handling and logging for team data fetching

- Added a timeout mechanism to the getBranch calls in TeamDataService to prevent hangs on Windows setups, improving reliability during team data retrieval.
- Introduced performance logging in TeamDetailView and teamSlice to track the start and completion of team selection processes, enhancing debugging capabilities.
- Updated error handling to provide clearer warnings during team provisioning and selection, improving user experience.

* feat: enhance MarkdownViewer and task dialogs with loading state management and performance logging

- Introduced character limits for Markdown content in MarkdownViewer to prevent UI freezes with large content.
- Added state management for raw content display in MarkdownViewer, allowing users to expand and view large markdown files.
- Implemented performance logging in GlobalTaskDetailDialog and TaskDetailDialog to track loading states and improve debugging.
- Updated loading state handling in TaskDetailDialog to ensure accurate representation of loading conditions.

* feat: enhance TaskCommentsSection with improved comment rendering and visibility management

- Added state management for visible comments, allowing users to see a limited number of comments for better performance.
- Implemented logic to cap the number of rendered comments, preventing UI freezes with large comment lists.
- Introduced sorting for comments based on creation date to display the most recent comments first.
- Updated the UI to inform users when only a subset of comments is being displayed, enhancing user experience.

* feat: enhance MarkdownViewer with improved character limits and syntax highlighting management

- Updated character limits for Markdown content to prevent UI freezes with large inputs.
- Introduced logic to disable syntax highlighting for medium/large content and show raw previews for very large content.
- Enhanced responsiveness of the MarkdownViewer by managing rendering based on content size.

* refactor: remove console warnings from team-related components for cleaner logging

- Eliminated console warnings in TeamDetailView, GlobalTaskDetailDialog, and TaskDetailDialog to streamline logging and reduce clutter during team data operations.
- Updated the selectTeam function in teamSlice to remove unnecessary logging, enhancing performance and readability.

* feat: add project editor with drag & drop file management

- Backend: ProjectFileService with file CRUD, search, git status, file watcher
- IPC: 12 editor channels with security validation and path containment
- Store: editorSlice with multi-tab management, draft persistence, conflict detection
- UI: CodeMirror 6 editor, file tree with DnD, search-in-files, context menus
- Move: fs.rename with EXDEV fallback, full path remapping across all caches
- Tests: comprehensive coverage for services, IPC handlers, store, and utilities

* fix: rename closeTab/setActiveTab to closeEditorTab/setActiveEditorTab

Resolve naming collision between editorSlice and tabSlice.
Both slices defined closeTab and setActiveTab, and since editorSlice
was spread last in the store composition, it silently overwrote the
tabSlice methods, breaking tab management.

* fix: editor improvements — isDir bug, scroll-to-line, Quick Open, a11y

- Fix isDir heuristic: use backend-provided isDirectory instead of
  filename-based guessing (breaks for Makefile, .github, etc.)
- Add scroll-to-line on search result click via editorPendingGoToLine
- Add Cmd+Shift+W shortcut for toggling line wrap
- Rewrite Quick Open to fetch all project files from backend API
  instead of flattening the loaded tree (limited to expanded dirs)
- Fix fd leak in atomicWrite: close file handle in finally block
- Add a11y: role=dialog/alert, aria-modal, aria-label on modals
- Add type=button on error state buttons

---------

Co-authored-by: Алексей <aleksei@example.com>
2026-03-01 07:56:59 +02:00

44 KiB
Raw Permalink Blame History

Plan: In-App Project Editor -- Iteration Plan

Контекст и предпосылки

На странице деталей команды (TeamDetailView.tsx) рядом с путём проекта (строки 761-769 в TeamDetailView.tsx, используется FolderOpen иконка и formatProjectPath()) добавляется кнопка "Open in Editor", которая открывает полноэкранный оверлей с файловым деревом, CodeMirror-редактором, вкладками и файловыми операциями.

Существующие паттерны, на которые опираемся

  1. Fullscreen overlay: ChangeReviewDialog.tsx -- полноэкранный fixed inset-0 z-50 компонент с хедером, левой панелью (ReviewFileTree) и правой панелью (ContinuousScrollView). Это точный архитектурный прототип.

  2. File tree: ReviewFileTree.tsx -- дерево файлов с buildTree(), collapse/expand, активный элемент. Будет адаптирован для файлового браузера (не review).

  3. CodeMirror: уже установлен в проекте (@codemirror/* ~20 пакетов), используется в CodeMirrorDiffView.tsx. Функция getSyncLanguageExtension() уже мапит расширения на языковые пакеты. Тема diffTheme использует CSS-переменные проекта.

  4. IPC-паттерн: module-level state + initialize/register/remove тройка + wrapHandler<T>() для IpcResult. Ближайший пример: review.ts.

  5. Preload bridge: invokeIpcWithResult<T>() для IpcResult, прямой ipcRenderer.invoke() для остальных. Группировка методов через sub-объект (как review: ReviewAPI).

  6. Path security: validateFilePath() из pathValidation.ts -- проверяет путь на sensitive patterns и sandbox.

  7. Store: Zustand slices с паттерном data/selectedId/loading/error.


Итерация 1: Walking Skeleton (файловое дерево + read-only просмотр)

Цель

Минимальный end-to-end вертикальный срез: кнопка "Open in Editor" на TeamDetailView открывает полноэкранный оверлей, где слева -- дерево файлов проекта, справа -- содержимое выбранного файла (read-only, с подсветкой синтаксиса через CodeMirror).

Зависимости (npm)

Никаких новых -- все CodeMirror-пакеты и lucide-react иконки уже установлены.

IPC каналы (новые)

Канал Направление Описание
editor:readDir renderer -> main Рекурсивное чтение директории (возвращает дерево)
editor:readFile renderer -> main Чтение содержимого файла по абсолютному пути

Новые файлы

Файл Описание
src/shared/types/editor.ts Типы: EditorTreeNode, EditorFileContent, запросы/ответы
src/main/services/editor/ProjectFileService.ts Сервис: чтение директорий (рекурсивно с лимитами) и файлов. Использует validateFilePath() для security
src/main/ipc/editor.ts IPC handlers: editor:readDir, editor:readFile. Паттерн: module-level state + wrapEditorHandler()
src/preload/constants/ipcChannels.ts Добавить EDITOR_READ_DIR, EDITOR_READ_FILE
src/renderer/components/team/editor/ProjectEditorOverlay.tsx Главный fullscreen overlay (по образцу ChangeReviewDialog.tsx)
src/renderer/components/team/editor/EditorFileTree.tsx Компонент дерева файлов (адаптация ReviewFileTree.tsx для filesystem -- без review-статусов)
src/renderer/components/team/editor/EditorCodeView.tsx Read-only CodeMirror view (адаптация CodeMirrorDiffView.tsx без merge mode)

Изменения в существующих файлах

Файл Изменение
src/shared/types/api.ts Добавить EditorAPI интерфейс + editor: EditorAPI в ElectronAPI
src/preload/index.ts Добавить editor: группу в electronAPI объект
src/main/ipc/handlers.ts Добавить initialize/register/removeEditorHandlers
src/renderer/components/team/TeamDetailView.tsx Кнопка "Open in Editor" рядом с projectPath (строка ~770), state для open/close оверлея

Важные решения

  • Security: ProjectFileService ОБЯЗАН использовать validateFilePath(filePath, projectRoot) для каждого запроса. Путь должен быть внутри projectRoot (sandbox). Нельзя читать файлы вне проекта.
  • Лимиты: readDir рекурсия ограничена глубиной (max 10 уровней) и количеством файлов (max 5000 nodes). Исключаются node_modules, .git, dist, build, __pycache__, .next.
  • Read-only: на этой итерации CodeMirror создаётся с EditorState.readOnly.of(true).
  • Lazy loading дерева: первый вызов readDir возвращает только верхний уровень. При раскрытии папки -- повторный вызов для поддиректории (ленивая загрузка). Или: полное дерево сразу, но с лимитом глубины и ignored patterns.

Тестирование

  • Unit: ProjectFileService -- чтение директории с mock fs, проверка security (reject paths outside projectRoot), проверка исключения node_modules.
  • Unit: EditorFileTree -- snapshot тесты рендеринга дерева.
  • Manual: открыть TeamDetailView, нажать "Open in Editor", убедиться что дерево загружается, клик по файлу показывает содержимое с подсветкой.

Критерии готовности

  • Кнопка видна на TeamDetailView рядом с путём проекта
  • Оверлей открывается по клику, закрывается по Escape или X
  • Дерево файлов загружается для projectPath команды
  • Клик по файлу показывает содержимое с синтаксической подсветкой
  • Попытка прочитать файл за пределами проекта -- отказ
  • pnpm typecheck проходит

Надёжность решения: 8/10

Уверенность: 9/10


Итерация 2: Editable CodeMirror + сохранение файлов

Цель

Переключить CodeMirror из read-only в редактируемый режим. Добавить Cmd+S для сохранения. Показывать индикатор unsaved changes.

IPC каналы (новые)

Канал Направление Описание
editor:writeFile renderer -> main Запись содержимого файла на диск

Новые файлы

Файл Описание
src/renderer/components/team/editor/EditorTabBar.tsx Панель вкладок (один файл пока, но подготовка к multi-tab)
src/renderer/components/team/editor/useEditorState.ts Хук для управления состоянием открытых файлов, dirty flags, save
src/renderer/store/slices/editorSlice.ts Zustand slice: openFiles, activeFilePath, dirtyFiles, loading/error

Изменения в существующих файлах

Файл Изменение
src/shared/types/editor.ts Добавить типы для write request/response
src/shared/types/api.ts Добавить writeFile в EditorAPI
src/main/services/editor/ProjectFileService.ts Метод writeFile(projectRoot, filePath, content) с validation
src/main/ipc/editor.ts Handler editor:writeFile
src/preload/index.ts Добавить editor.writeFile
src/preload/constants/ipcChannels.ts EDITOR_WRITE_FILE
src/renderer/components/team/editor/ProjectEditorOverlay.tsx Интеграция EditorTabBar, переключение read-only -> editable
src/renderer/components/team/editor/EditorCodeView.tsx Убрать readOnly, добавить onChange callback, Cmd+S keymap
src/renderer/store/index.ts Подключить editorSlice
src/renderer/store/types.ts Расширить AppState типом editorSlice

Важные решения

  • Cmd+S: перехватывается через CodeMirror keymap extension (не глобальный listener), чтобы не конфликтовать с другими горячими клавишами.
  • Dirty flag: отслеживается через сравнение текущего содержимого с оригинальным (при загрузке). Точка в названии вкладки для dirty файлов.
  • Confirm on close: если есть unsaved changes -- confirm() через существующий ConfirmDialog.
  • Backup: перед записью -- никакого backup на этой итерации (файл просто перезаписывается). В будущем можно добавить.
  • Concurrency: если файл изменился на диске пока был открыт -- пока не обрабатываем (это итерация 4-5).

Тестирование

  • Unit: ProjectFileService.writeFile -- запись с mock fs, reject для файлов вне проекта.
  • Unit: editorSlice -- открытие/закрытие файлов, dirty state, сохранение.
  • Unit: useEditorState -- хук тестирование с Zustand store.
  • Manual: открыть файл, отредактировать, Cmd+S, убедиться что файл записался, dirty индикатор сбрасывается.

Критерии готовности

  • Файл редактируется в CodeMirror (не read-only)
  • Cmd+S сохраняет файл
  • Dirty indicator (точка) на вкладке
  • При закрытии с unsaved changes -- confirmation dialog
  • Сохранение отказывает для файлов вне projectRoot

Надёжность решения: 7/10

Уверенность: 8/10


Итерация 3: Multi-tab + создание/удаление файлов

Цель

Поддержка нескольких открытых файлов во вкладках. Контекстное меню на файловом дереве: создать файл, создать папку, удалить файл. Переименование -- вне scope.

IPC каналы (новые)

Канал Направление Описание
editor:createFile renderer -> main Создать файл (с опциональным начальным содержимым)
editor:createDir renderer -> main Создать директорию
editor:deleteFile renderer -> main Удалить файл (в Trash через Electron shell.trashItem)

Новые файлы

Файл Описание
src/renderer/components/team/editor/EditorContextMenu.tsx Context menu для дерева файлов (New File, New Folder, Delete, Reveal in Finder)
src/renderer/components/team/editor/NewFileDialog.tsx Маленький inline-input для ввода имени нового файла/папки

Изменения в существующих файлах

Файл Изменение
src/shared/types/editor.ts Типы для create/delete запросов
src/shared/types/api.ts Расширить EditorAPI методами createFile, createDir, deleteFile
src/main/services/editor/ProjectFileService.ts Методы createFile, createDir, deleteFile. deleteFile использует shell.trashItem() (безопасное удаление)
src/main/ipc/editor.ts 3 новых handler
src/preload/index.ts 3 новых метода в editor
src/preload/constants/ipcChannels.ts EDITOR_CREATE_FILE, EDITOR_CREATE_DIR, EDITOR_DELETE_FILE
src/renderer/components/team/editor/EditorTabBar.tsx Multi-tab: массив вкладок, переключение, close (X), close other tabs, middle-click close
src/renderer/components/team/editor/EditorFileTree.tsx Right-click context menu, refresh после create/delete
src/renderer/components/team/editor/ProjectEditorOverlay.tsx Управление массивом открытых файлов, переключение активной вкладки
src/renderer/store/slices/editorSlice.ts Массив openTabs, activeTabId, actions: openFile, closeFile, switchTab, reorderTabs

Важные решения

  • Удаление через Trash: используем shell.trashItem() (Electron API) вместо fs.unlink(). Это безопасно -- пользователь может восстановить файл из корзины.
  • Confirm on delete: обязательный ConfirmDialog перед удалением.
  • Tab ordering: drag-and-drop для вкладок через @dnd-kit (уже установлен в проекте).
  • Имя нового файла: валидация -- запрет на ., .., / в начале, запрет на спецсимволы.
  • Refresh дерева: после create/delete автоматически перечитываем поддерево. Не нужен FileWatcher -- явный refresh.

Тестирование

  • Unit: ProjectFileService.createFile/deleteFile с mock fs.
  • Unit: editorSlice -- multi-tab actions (open, close, reorder).
  • Unit: EditorContextMenu -- рендеринг, клики.
  • Manual: открыть несколько файлов, переключаться между вкладками, создать файл, удалить файл.

Критерии готовности

  • Можно открыть несколько файлов одновременно
  • Вкладки переключаются, закрываются
  • Правый клик по дереву -- New File, New Folder, Delete
  • Создание файла добавляет его в дерево
  • Удаление -- через Trash с confirmation

Надёжность решения: 7/10

Уверенность: 8/10


Итерация 4: Горячие клавиши, поиск, UX polish

Цель

Клавиатурная навигация (Cmd+P quick open, Cmd+W close tab, Cmd+Shift+[ / ] switch tabs). Поиск по содержимому файлов через Cmd+Shift+F. Breadcrumb навигация. Иконки файлов по типу.

IPC каналы (новые)

Канал Направление Описание
editor:searchInFiles renderer -> main Поиск по содержимому файлов (grep-like)

Новые файлы

Файл Описание
src/renderer/components/team/editor/QuickOpenDialog.tsx Cmd+P dialog: fuzzy search по именам файлов (по образцу cmdk -- уже установлен)
src/renderer/components/team/editor/SearchInFilesPanel.tsx Панель результатов поиска (заменяет или дополняет file tree)
src/renderer/components/team/editor/EditorBreadcrumb.tsx Breadcrumb навигация по пути текущего файла
src/renderer/components/team/editor/fileIcons.ts Маппинг расширений на lucide-react иконки и цвета
src/renderer/hooks/useEditorKeyboardShortcuts.ts Хук для всех горячих клавиш редактора
src/main/services/editor/FileSearchService.ts Сервис: search in files с лимитами (grep-like, max 100 results)

Изменения в существующих файлах

Файл Изменение
src/shared/types/editor.ts Типы для search request/response
src/shared/types/api.ts searchInFiles в EditorAPI
src/main/ipc/editor.ts Handler editor:searchInFiles
src/preload/index.ts editor.searchInFiles
src/preload/constants/ipcChannels.ts EDITOR_SEARCH_IN_FILES
src/renderer/components/team/editor/ProjectEditorOverlay.tsx Интеграция QuickOpen, SearchInFiles, Breadcrumb, keyboard shortcuts
src/renderer/components/team/editor/EditorFileTree.tsx Иконки файлов по типу
src/renderer/components/team/editor/EditorTabBar.tsx Иконки файлов на вкладках

Важные решения

  • Quick Open: использовать cmdk (уже в зависимостях, v1.0.4) для fuzzy search по именам файлов. Список файлов загружается при открытии оверлея.
  • Search in Files: серверная сторона делает простой grep по файлам с Node.js (readline + regex). Не используем external tools типа ripgrep -- держим zero-dependency. Лимит: 100 результатов, max 10MB на файл.
  • Горячие клавиши: Cmd+P (quick open), Cmd+W (close tab), Cmd+S (save), Cmd+Shift+F (search), Cmd+Shift+[ / ] (switch tabs), Cmd+\ (toggle file tree).
  • Breadcrumb: кликабельный -- каждый сегмент пути открывает эту папку в дереве.

Тестирование

  • Unit: FileSearchService -- поиск по mock файлам, лимиты.
  • Unit: useEditorKeyboardShortcuts -- обработка горячих клавиш.
  • Unit: fileIcons.ts -- маппинг расширений.
  • Manual: Cmd+P, Cmd+Shift+F, навигация клавиатурой.

Критерии готовности

  • Cmd+P открывает quick open с fuzzy search
  • Cmd+Shift+F показывает результаты поиска по содержимому
  • Все основные горячие клавиши работают
  • Breadcrumb-навигация для текущего файла
  • Иконки файлов по типу в дереве и вкладках

Надёжность решения: 7/10

Уверенность: 7/10


Итерация 5: Git status, file watching, расширенные возможности

Цель

Показывать git status (modified/untracked/staged) в дереве файлов. Live refresh при изменениях на диске. Conflict detection при сохранении. Minimap. Line numbers toggle.

IPC каналы (новые)

Канал Направление Описание
editor:gitStatus renderer -> main Получить git status для директории (modified, staged, untracked)
editor:watchDir renderer -> main Запустить file watcher для проекта (возвращает cleanup)
editor:change main -> renderer Event: файл изменился на диске

Новые файлы

Файл Описание
src/main/services/editor/EditorFileWatcher.ts FileWatcher адаптация (~60 LOC) для отслеживания изменений в projectRoot
src/main/services/editor/GitStatusService.ts Сервис: вызывает git status --porcelain и парсит вывод
src/renderer/components/team/editor/GitStatusBadge.tsx Бейджи M/U/A рядом с файлами в дереве

Изменения в существующих файлах

Файл Изменение
src/shared/types/editor.ts GitFileStatus, EditorFileChangeEvent
src/shared/types/api.ts gitStatus, onEditorFileChange в EditorAPI
src/main/ipc/editor.ts Handlers для git status и file watcher events
src/preload/index.ts editor.gitStatus, editor.onFileChange
src/preload/constants/ipcChannels.ts EDITOR_GIT_STATUS, EDITOR_WATCH_DIR, EDITOR_CHANGE
src/renderer/components/team/editor/EditorFileTree.tsx Git status badges (M = modified, U = untracked, A = staged)
src/renderer/components/team/editor/EditorCodeView.tsx Line wrapping toggle, conflict detection при сохранении
src/renderer/components/team/editor/ProjectEditorOverlay.tsx File watcher подписка, auto-refresh дерева, conflict modal при concurrent edit
src/renderer/store/slices/editorSlice.ts Git status data, file watcher state

Важные решения

  • Git status: вызываем child_process с git status --porcelain -u в projectRoot. Парсим вывод. Кешируем на 5 секунд. Не используем libgit2 -- слишком тяжёлый.
  • File watcher: используем существующий chokidar-подобный паттерн (как FileWatcher в проекте). Debounce 200ms. При получении события -- refresh дерева и уведомление если открытый файл изменился.
  • Conflict detection: при сохранении -- проверить mtime файла. Если изменился после последнего чтения -- показать conflict dialog (перезаписать / отменить / diff).
  • Minimap: CodeMirror не имеет встроенного minimap. Можно использовать @replit/codemirror-minimap или пропустить. Решение: пропустить minimap (слишком специфичная dependency), вместо этого добавить line wrap toggle и go-to-line (Cmd+G).

Тестирование

  • Unit: GitStatusService -- парсинг git status --porcelain вывода.
  • Unit: EditorFileWatcher -- debounce, event types.
  • Unit: conflict detection логика.
  • Manual: изменить файл в внешнем редакторе, убедиться что отображается conflict.

Критерии готовности

  • Git status бейджи в файловом дереве
  • Auto-refresh при изменениях на диске
  • Conflict detection при сохранении файла, изменённого извне
  • Go-to-line (Cmd+G)
  • Line wrap toggle

Надёжность решения: 6/10

Уверенность: 7/10


Сводная таблица файлов по итерациям

Итерация 1 (7 новых, 4 изменения)

Новые: shared/types/editor.ts, main/services/editor/ProjectFileService.ts, main/ipc/editor.ts, renderer/components/team/editor/ProjectEditorOverlay.tsx, renderer/components/team/editor/EditorFileTree.tsx, renderer/components/team/editor/EditorCodeView.tsx Изменения: shared/types/api.ts, preload/index.ts, preload/constants/ipcChannels.ts, main/ipc/handlers.ts, renderer/components/team/TeamDetailView.tsx

Итерация 2 (3 новых, ~8 изменений)

Новые: renderer/components/team/editor/EditorTabBar.tsx, renderer/hooks/useEditorState.ts, renderer/store/slices/editorSlice.ts Изменения: shared/types/editor.ts, shared/types/api.ts, main/services/editor/ProjectFileService.ts, main/ipc/editor.ts, preload/index.ts, preload/constants/ipcChannels.ts, renderer/components/team/editor/*, renderer/store/index.ts, renderer/store/types.ts

Итерация 3 (2 новых, ~8 изменений)

Новые: renderer/components/team/editor/EditorContextMenu.tsx, renderer/components/team/editor/NewFileDialog.tsx Изменения: многие файлы из итерации 2

Итерация 4 (6 новых, ~8 изменений)

Новые: QuickOpenDialog.tsx, SearchInFilesPanel.tsx, EditorBreadcrumb.tsx, fileIcons.ts, useEditorKeyboardShortcuts.ts, main/services/editor/FileSearchService.ts

Итерация 5 (3 новых, ~7 изменений)

Новые: EditorFileWatcher.ts, GitStatusService.ts, GitStatusBadge.tsx


Риски и предупреждения

  1. Безопасность (критичный риск): каждый файловый IPC handler ОБЯЗАН валидировать что запрашиваемый путь находится внутри projectRoot. Path traversal (../../etc/passwd) -- главный вектор атаки. Используем существующий validateFilePath() из src/main/utils/pathValidation.ts (НЕ писать свой).

  2. Большие проекты: дерево файлов может содержать тысячи файлов. Обязательны excluded patterns (node_modules, .git) и лимиты. Для поиска по файлам -- лимит на размер файла.

  3. Race conditions при сохранении: если агент Claude параллельно редактирует тот же файл -- потеря данных. Итерация 5 добавляет mtime-проверку, но полноценный lock отсутствует.

  4. Memory: CodeMirror для очень больших файлов (10MB+) может потреблять много памяти. Лимит на размер читаемого файла: 2MB (не 5MB -- снижено после security review; IPC сериализация удваивает потребление памяти).

  5. ProseMirror vs CodeMirror: в requirements указан ProseMirror, но в проекте уже глубоко интегрирован CodeMirror (20+ пакетов, diff view, языковые пакеты). Рекомендация: использовать CodeMirror (не ProseMirror). ProseMirror ориентирован на rich-text, а CodeMirror -- на код. CodeMirror 6 = тот же автор (Marijn Haverbeke), уже в проекте, zero additional dependencies.


Архитектурные решения после ревизии

Добавлено после ревизии. Влияет на каждую итерацию.

Обязательные рефакторинги ДО или ВО ВРЕМЯ итерации 1

  1. Извлечь buildTree() в src/renderer/utils/fileTreeBuilder.ts (из ReviewFileTree.tsx). Иначе будет дублирование при создании EditorFileTree. Рефакторинг не ломает Review -- это extract-and-import.

  2. Извлечь getSyncLanguageExtension() + getAsyncLanguageDesc() в src/renderer/utils/codemirrorLanguages.ts (из CodeMirrorDiffView.tsx). Аналогично -- extract-and-import, CodeMirrorDiffView начинает импортировать из утилиты.

  3. Извлечь базовую тему CM в src/renderer/utils/codemirrorTheme.ts (из diffTheme в CodeMirrorDiffView.tsx). Общие стили (&, .cm-gutters, .cm-scroller, .cm-content, .cm-cursor, .cm-selectionBackground) -- в общую тему. Diff-специфичные (.cm-changedLine, .cm-deletedChunk и т.д.) -- остаются в CodeMirrorDiffView.tsx.

  4. Извлечь wrapHandler в src/main/ipc/ipcWrapper.ts (из review.ts). createIpcWrapper('IPC:editor') вместо копирования wrapReviewHandler.

  5. Имя сервиса: ProjectFileService (не FileEditorService). Stateless, без rootPath в конструкторе. Каждый метод принимает projectRoot как первый аргумент. Паттерн: TeamDataService.

Изменения в итерациях по результатам ревизии

Итерация 1:

  • EditorFileTree.tsx использует generic FileTree из fileTreeBuilder.ts + render-prop для иконок
  • EditorCodeView.tsx использует extracted codemirrorLanguages.ts и codemirrorTheme.ts
  • ProjectFileService -- stateless, readDir(projectRoot, dirPath), readFile(projectRoot, filePath)
  • Security: validateFilePath() из pathValidation.ts, НЕ свой assertInsideRoot()
  • НЕ создавать editorSlice на итерации 1 -- state для read-only просмотра можно держать в useState

Итерация 2:

  • editorSlice.ts создаётся с чёткими секциями-группами (tree / tabs / content-save)
  • buildEditorExtensions(options) -- фабрика extensions, компонент не знает о конкретных CM plugins
  • useEditorState.ts -> убрать. Логика целиком в slice. Хук useEditorState дублирует slice.

Итерация 3:

  • Tab management actions (openFile, closeTab, setActiveTab) уже в slice с итерации 2
  • EditorContextMenu.tsx -- ОК, отдельный компонент
  • NewFileDialog.tsx -- ОК, inline input

Итерация 4:

  • FileSearchService.ts -- отдельный сервис в main, ОК (SRP)
  • useEditorKeyboardShortcuts.ts -- ОК, отдельный хук
  • fileIcons.ts -- ОК, чистая утилита

Итерация 5:

  • GitStatusService.ts -- отдельный сервис, ОК
  • EditorFileWatcher.ts -- повторяет паттерн FileWatcher (~60 LOC), ОК
  • mtime conflict detection -- необходима проверка и в saveFile (slice), и в writeFile (service)

UX Review

Добавлено после UX-ревью. Дополнения и исправления по итерациям.

Дополнения к итерации 1 (Walking Skeleton)

  1. Focus management: При открытии overlay -- фокус на первый файл в дереве. При закрытии -- вернуть фокус на кнопку "Open in Editor" (паттерн returnFocusRef). Добавить inert на фоновый контент.

  2. ARIA: File tree сразу с role="tree", role="treeitem", aria-expanded, role="group". Не откладывать accessibility на потом.

  3. Пустой проект: Если readDir возвращает 0 видимых файлов -- показать "No files found" + "Create a new file?" (кнопка неактивна до итерации 3).

  4. Binary файлы: Уже на итерации 1 (read-only) нужна проверка бинарности. Добавить isBinary в ReadFileResult и EditorBinaryState.tsx -- "This file is binary. Open in system viewer?"

  5. Глубокая вложенность: Max визуальный indent = 12 уровней. Tooltip с полным путём на глубоких узлах.

Дополнения к итерации 2 (Editing + Save)

  1. Status bar: Добавить EditorStatusBar.tsx -- [Ln 42, Col 15] | [TypeScript] | [Spaces: 2]. Данные из CM6 state. CSS: bg-surface-sidebar border-t border-border text-text-muted text-xs h-6.

  2. Unsaved changes при закрытии overlay: Не только при закрытии tab, но и при Escape/X для overlay. Три кнопки: "Save All & Close" / "Discard & Close" / "Cancel". Добавить hasUnsavedChanges() в slice.

  3. Файл удалён извне: При saveFile с ENOENT -- inline-ошибка "File was deleted. Create new? / Close tab". Не падать.

Дополнения к итерации 3 (Multi-tab + file ops)

  1. Disambiguation tab labels: Два таба "index.ts" -- нужно показать "(main/utils)" и "(renderer/utils)". Утилита getDisambiguatedTabLabel() в src/renderer/utils/tabLabelDisambiguation.ts.

  2. Длинные имена файлов: Табы с max-width ~160px, truncate, tooltip. Modified dot ПЕРЕД текстом (не обрезается при truncate).

  3. ARIA для tab bar: role="tablist", role="tab", aria-selected, role="tabpanel".

Исправления к итерации 4 (Hotkeys, search, UX polish)

  1. Keyboard shortcuts -- конфликт: Cmd+[ / Cmd+] это indent/outdent в CM6 и VS Code! Переключение табов: Cmd+Shift+[ / Cmd+Shift+] (VS Code convention). Добавить Ctrl+Tab / Ctrl+Shift+Tab.

  2. Cmd+B toggle sidebar: Добавить в список горячих клавиш. Sidebar width persist в localStorage.

  3. Cmd+G go to line: Добавить. CM6 уже поддерживает через gotoLine command.

  4. Discoverability: Кнопка ? в header overlay (как в ChangeReviewDialog). EmptyState показывает шпаргалку shortcuts.

Дополнения к итерации 5 (Git, file watching)

  1. File changed on disk while open in tab: При обнаружении изменения -- banner в табе: "File changed on disk. [Reload] [Keep mine] [Show diff]". Не перезаписывать молча.

  2. File deleted on disk while open in tab: Banner: "File no longer exists on disk. [Close tab]". Не показывать ошибку при попытке сохранить -- показать предупреждение.


Security Review -- дополнения по итерациям

Полный аудит безопасности описан в plan-architecture.md секция 18. Здесь -- конкретные требования для каждой итерации.

Итерация 1: Security-critical

  1. ProjectFileService.readDir(): Валидировать КАЖДЫЙ entry через validateFilePath(). Для symlinks -- fs.realpath() + повторная проверка containment. Молча пропускать symlinks, ведущие за пределы projectRoot (см. SEC-2 в plan-architecture.md).

  2. ProjectFileService.readFile(): Проверить fs.lstat() -> isFile() ДО чтения. Проверить stats.size <= 2MB. Блокировать пути /dev/, /proc/, /sys/. После чтения -- post-read verify через fs.realpath() (TOCTOU mitigation).

  3. projectRoot: Хранить в module-level state editor.ts, НЕ принимать от renderer при каждом IPC вызове. Устанавливать через editor:open(projectPath) с валидацией.

  4. Sensitive файлы: validateFilePath() уже блокирует .env, .ssh, credentials.json и т.д. В readDir: показывать с пометкой "locked", при клике -- "Sensitive file, cannot open".

Итерация 2: Security-critical

  1. ProjectFileService.writeFile():

    • validateFilePath() ДО записи
    • Buffer.byteLength(content, 'utf8') <= 2MB ДО записи
    • Atomic write: tmp файл в той же директории + rename()
    • Запрет записи в .git/ директорию
    • Post-write verify не нужна (atomic rename -- одна операция)
  2. editor:writeFile IPC handler: Параметр filePath от renderer валидируется через validateFilePath(filePath, activeProjectRoot). activeProjectRoot из module-level state.

Итерация 3: Security-critical

  1. editor:createFile: Валидация имени файла через validateFileName():

    • Запрет . и .. как имени
    • Запрет control characters (\x00-\x1f)
    • Запрет path separators (/, \, :)
    • Запрет NUL bytes
    • Max длина 255 символов
    • Запрет sensitive паттернов (.env, *.key) при СОЗДАНИИ (опционально -- можно разрешить)
  2. editor:deleteFile: Использовать shell.trashItem(), НЕ fs.unlink(). Валидация пути через validateFilePath().

  3. Валидация parentDir: При createFile(parentDir, name) -- валидировать и parentDir, и path.join(parentDir, name).

Итерация 4: Security-critical

  1. editor:searchInFiles:
    • ТОЛЬКО literal string search, НЕ regex от пользователя
    • Max 1000 файлов для поиска, max 1MB на файл
    • Запустить в worker_thread или с AbortController timeout
    • Каждый файл для поиска валидировать через validateFilePath()

Итерация 5: Medium security

  1. editor:gitStatus: Выполняет child_process.exec('git status') -- убедиться что cwd установлен в projectRoot и что projectRoot валиден.

  2. editor:watchDir: FileWatcher на projectRoot -- ОК, но при получении событий не передавать полные пути файлов в renderer без валидации.

  3. editor:change events (main->renderer): Пути файлов в events -- потенциальная утечка информации если watcher случайно поймает файл за пределами проекта (через symlink).

ВНИМАНИЕ: Существующая уязвимость (не связана с editor)

review:saveEditedFile в src/main/ipc/review.ts записывает файл без валидации пути. См. SEC-11 в plan-architecture.md. Необходим отдельный hotfix НЕЗАВИСИМО от editor-фичи.


Performance Review -- дополнения по итерациям

Полный аудит в plan-architecture.md секция 19. Здесь -- конкретные performance-требования для каждой итерации.

Итерация 1: Performance-critical

  1. EditorView lifecycle (CRITICAL): НЕ использовать Map<tabId, EditorView> + CSS show/hide (как описано в plan-architecture секция 6.5). Использовать EditorState pooling: Map<tabId, EditorState> в useRef + один активный EditorView. При переключении таба: savedStates.set(oldId, view.state) -> view.destroy() -> new EditorView({ state: savedStates.get(newId) }). Паттерн initialState уже используется в CodeMirrorDiffView.tsx (строки 699-705).

  2. readDir лимиты: MAX_ENTRIES_PER_DIR = 500 (не 10,000). При превышении -- "N more files..." + кнопка "Show all". Только root level при открытии, expand = depth=1 для конкретной папки.

  3. readFile тиерная стратегия: <256KB мгновенно | 256KB-2MB с progress | 2MB-5MB preview only (100 строк) | >5MB external editor. Детектировать минификацию (строка >10,000 chars) и binary (null bytes в первых 8KB).

  4. Дедупликация IPC: Map<string, Promise<ReadFileResult>> для readFile. Если файл уже загружается -- ждать результат, не создавать новый запрос.

Итерация 2: Performance-critical

  1. НЕ хранить modified content в Zustand (CRITICAL): editorModifiedContents: Record<string, string> из секции 2.1 plan-architecture -- УБРАТЬ. Контент живёт только в EditorState CodeMirror. В Zustand: editorModifiedFiles: Set<string> (только dirty flags). Dirty flag обновляется debounced (300ms) через EditorView.updateListener (паттерн из CodeMirrorDiffView строки 517-527).

  2. Гранулярные Zustand селекторы (обязательно):

const tabList = useStore(s => s.editorOpenTabs, shallow);
const activeId = useStore(s => s.editorActiveTabId);
// FileTreePanel НЕ подписывается ни на content, ни на tabs
// TabBar НЕ подписывается на tree state
  1. LRU eviction EditorState: При >30 states в кеше -- вытеснять oldest, сохраняя { content: string, cursorPos: number } (без undo). При возврате к вытесненному табу -- восстановить через EditorState.create().

Итерация 3: Performance-medium

  1. Tab closing -- memory cleanup: При closeTab: stateCache.delete(tabId). При closeAllTabs: stateCache.clear(). Явно вызывать -- не полагаться на GC.

  2. Concurrent file operations: При createFile/deleteFile -- дебаунсить обновление дерева (500ms), не перечитывать после каждой операции.

Итерация 4: Performance-critical

  1. File tree виртуализация (HIGH): Перейти на @tanstack/react-virtual (уже в проекте -- DateGroupedSessions.tsx, ChatHistory.tsx, NotificationsView.tsx). flattenTree(tree, expandedDirs) -> FlatNode[] + useVirtualizer({ count, estimateSize: () => 28 }). Рендерить только видимые ноды.

  2. Search in files -- main process: Запускать в worker_thread или с AbortController (timeout 5s). Limit: 100 результатов, max 1MB на файл. НЕ читать binary файлы для поиска.

  3. Quick Open (Cmd+P): Кешировать flat file list при открытии editor. НЕ перечитывать на каждое открытие Cmd+P. Invalidate по F5 или file watcher event.

Итерация 5: Performance-medium

  1. File watcher -- opt-in: НЕ включать по умолчанию. Toggle "Watch for external changes". По умолчанию -- ручной refresh (F5). При включении: fs.watch({ recursive: true }) с фильтрацией (node_modules/.git/dist) и debounce 200ms.

  2. Git status -- кеширование: Результат git status --porcelain кешировать на 5 секунд (как в плане). При file watcher event -- invalidate и перечитать.

Benchmarks для CI/Manual

Benchmark 1: EditorView memory
  Открыть 25 файлов x 200KB
  Измерить: performance.memory.usedJSHeapSize
  Порог: < 150MB

Benchmark 2: Tab switch latency
  Переключить таб (500KB файл с syntax highlighting)
  Измерить: time from click to contentful paint
  Порог: < 50ms

Benchmark 3: File tree render
  5000+ файлов, все папки раскрыты (с виртуализацией)
  Измерить: FPS при скролле
  Порог: >= 55fps

Benchmark 4: readDir latency
  Директория с 5000 файлами
  Измерить: time from click to tree displayed
  Порог: < 200ms

Benchmark 5: Keystroke re-renders
  React DevTools Profiler при наборе текста
  Порог: FileTreePanel и TabBar рендерятся 0 раз