Перейти к основному содержимому

Добавление новой функции

В этом руководстве описан сквозной процесс реализации полноценной функции в Elftia. Оно дополняет Добавление IPC-маршрутов и охватывает полный жизненный цикл: планирование, тестирование и обновление документации.

Обзор: процесс разработки функции

Планирование → Бэкенд → Фронтенд → Проверка качества → Тестирование → Обновление документации
flowchart LR
A[Планирование] --> B[Бэкенд]
B --> C[Фронтенд]
C --> D[Проверка качества]
D --> E[Тестирование]
E --> F[Обновление документации]
F --> G[Отправить PR]

Фаза 1: Планирование

Прежде чем писать код, чётко ответьте на следующие вопросы:

Контрольный список анализа требований

ВопросЦель
Какие общие типы необходимы?Определяет файлы типов в @shared/contracts/
Затрагиваются ли операции с таблицами базы данных?Определяет, нужна ли новая миграция
Какая логика Service нужна?Определяет модули бэкенда
Какие IPC-каналы нужны?Определяет Router и Preload API
Какие UI-компоненты нужны?Определяет структуру файлов фронтенда
Добавляется ли новая страница с маршрутом?Определяет, нужна ли ленивая загрузка и регистрация маршрута
Видит ли пользователь эту функцию?Определяет, нужно ли обновлять документацию базы знаний

Шаблон планирования файлов

На примере функции «Заметки»:

packages/
├── desktop/app/
│ ├── shared/contracts/
│ │ └── note-types.ts # Общие типы
│ └── main/
│ ├── services/content/workspace/notes/
│ │ ├── NoteFileService.ts # Сервис файловых CRUD-операций
│ │ └── NoteWatcher.ts # Сервис наблюдения за файлами
│ └── services/routers/
│ └── NoteRouter.ts # IPC Router

├── renderer/src/
│ ├── features/notes/
│ │ ├── hooks/
│ │ │ └── useNotes.ts # Hook для получения данных
│ │ └── components/
│ │ ├── NotesList.tsx # Компонент списка
│ │ ├── NoteEditor.tsx # Компонент редактора
│ │ └── index.ts # Barrel-экспорт
│ └── locales/
│ ├── en/notes.json
│ ├── zh/notes.json
│ └── ja/notes.json

Фаза 2: Реализация бэкенда

Создавайте код бэкенда в следующем порядке:

2.1 Общие типы

{/* packages/desktop/app/shared/contracts/note-types.ts */}

export interface Note {
id: string;
title: string;
content: string;
tags: string[];
createdAt: string;
updatedAt: string;
}

export interface CreateNoteInput {
title: string;
content?: string;
tags?: string[];
}

2.2 Service

Service инкапсулируют всю бизнес-логику. Следуйте принципу единственной ответственности — чтение/запись файлов и наблюдение за файлами должны быть двумя отдельными Service.

services/content/workspace/notes/
├── NoteFileService.ts # Файловые CRUD-операции
└── NoteWatcher.ts # Наблюдение за изменениями файловой системы

2.3 Router

Регистрируйте IPC-обработчики с помощью паттерна secureHandle. Все параметры должны проходить валидацию через Zod.

Подробные шаги см. в Добавление IPC-маршрутов.

2.4 Регистрация + Preload

  1. Создайте экземпляры Service и Router в registerAllRouters() внутри routers/index.ts
  2. Вызовите router.register()
  3. Откройте методы в объекте api в preload/index.ts

Фаза 3: Реализация фронтенда

3.1 Создание Hook

Инкапсулируйте логику получения данных и управления состоянием в пользовательском Hook:

{/* packages/renderer/src/features/notes/hooks/useNotes.ts */}

export function useNotes() {
const [notes, setNotes] = useState<Note[]>([]);
const [loading, setLoading] = useState(true);

// Получение данных, CRUD-операции...
return { notes, loading, create, update, remove };
}

3.2 Создание компонентов

Компоненты отвечают только за рендеринг UI; бизнес-логику делегируйте Hook:

components/notes/
├── index.ts # Barrel-экспорт
├── NotesList.tsx # Представление списка (< 400 строк)
├── NoteEditor.tsx # Редактор (< 400 строк)
└── NoteCard.tsx # Карточка одной заметки

Стандарты компонентов:

  • Используйте семантические токены (bg-surface-1, text-foreground), а не захардкоженные цвета
  • Используйте UI-компоненты проекта (Button, Input, Select), а не нативные HTML-элементы управления
  • Используйте useTranslation() для интернационализации текстов

3.3 Регистрация маршрута (для новых страниц)

Если функция требует отдельной страницы:

{/* packages/renderer/src/app/lazy/pages.tsx */}

export const LazyNotesPage = lazy(() => import('../../features/notes/NotesPage'));
export const SuspenseNotesPage = withSuspense(LazyNotesPage, PageSkeleton);
{/* packages/renderer/src/app/App.tsx — старые пути импорта остаются действительными через barrel re-export в lazy/index.ts */}

import { SuspenseNotesPage as NotesPage } from './lazy';

<Route path="/notes" element={<NotesPage />} />

Чтобы добавить новый тип workspace (не новую страницу), следуйте другому пути: отредактируйте features/chat/components/content/workspaces/registry.ts, добавьте definitions/<Name>Workspace.tsx и соответствующий lazy-импорт в app/lazy/workspaces.tsx. См. раздел «Добавление нового типа Workspace» в packages/renderer/CLAUDE.md.

3.4 i18n

Создайте файлы переводов для всех трёх языков и зарегистрируйте пространство имён в i18n/index.ts:

locales/
├── en/notes.json
├── zh/notes.json
└── ja/notes.json

Фаза 4: Проверка качества

ESLint

# Проверить изменённые файлы
npx eslint packages/renderer/src/features/notes/components/ packages/renderer/src/features/notes/hooks/

# Автоматически исправить порядок импортов и т. д.
npx eslint packages/renderer/src/features/notes/components/ --fix

Ограничения размера файлов

Тип файлаРекомендуетсяПредупреждениеЖёсткий лимит
React-компонент400 строк600 строк800 строк
Пользовательский Hook300 строк400 строк600 строк
Вспомогательная функция150 строк200 строк300 строк
Определение типов100 строк150 строк200 строк
Service300 строк400 строк600 строк

Файлы, превысившие предупредительный порог, необходимо разбить в рамках текущей задачи — не откладывайте.

TypeScript

npm run typecheck

Фаза 5: Тестирование

Команды тестирования

КомандаОписание
npm run testПолный прогон тестов Vitest
npm run test -- --filter notesФильтрация тестов по ключевому слову

Контрольный список ручного тестирования

  • Основной сценарий использования функции работает корректно
  • Ошибочные ситуации отображают правильные сообщения (сетевые ошибки, ошибки базы данных и т. д.)
  • UI корректно отображается в тёмной и светлой теме
  • UI корректно отображается при включённой прозрачности обоев
  • Макет не ломается при уменьшении окна до минимальной ширины

Фаза 6: Обновление документации

Обязательные обновления документации

УсловиеДокументация для обновления
Добавлен Service / компонент / Hook / Context.claude/skills/architecture-index/SKILL.md
Добавлена видимая пользователю функция страницыДокументация базы знаний docs/elfi-kb/
Добавлен маршрут страницыКарта маршрутов docs/elfi-kb/INDEX.md
Изменён IPC-интерфейсСписок IPC-каналов на этом сайте

Справочник типовых паттернов

Паттерн 1: CRUD Service

Подходит для функций управления сущностями (заметки, избранное, шаблоны и т. д.).

Общие типы → Service(CRUD) → Router(валидация Zod) → Preload → Hook(useState+CRUD) → Компоненты списка и редактирования

Паттерн 2: Потоковые события

Подходит для функций с push-уведомлениями в реальном времени (чат, задачи генерации и т. д.).

Service бэкенда генерирует событие → Main-процесс webContents.send(channel, data) → Preload onXxx-слушатель → Hook подписка/отписка
{/* Сторона Preload — паттерн подписки на события */}

onEvent: (cb: (data: any) => void) => {
const channel = 'myFeature:event';
const handler = (_event: IpcRendererEvent, data: any) => cb(data);
ipcRenderer.on(channel, handler);
return () => ipcRenderer.removeListener(channel, handler);
},
{/* Сторона Hook — управление подпиской */}

useEffect(() => {
const unsubscribe = window.api.myFeature.onEvent((data) => {
setMessages((prev) => [...prev, data]);
});
return unsubscribe;
}, []);

Паттерн 3: Файловые операции

Подходит для функций, требующих чтения или записи пользовательских файлов (управление проектами, экспорт и т. д.).

Фронтенд передаёт путь к файлу/конфигурацию → Service бэкенда работает с файловой системой → Возвращает URL результата или содержимое

Ключевой принцип: все операции с файловой системой выполняются на бэкенде. Фронтенд передаёт только пути и параметры — файлами напрямую не управляет.


Полный контрольный список

Бэкенд

  • Общие типы определены в @shared/contracts/
  • Service инкапсулирует всю бизнес-логику
  • Router использует secureHandle + валидацию Zod
  • Router зарегистрирован в registerAllRouters()
  • Сигнатуры методов Preload API корректны
  • Тип-интерфейс DesktopApi обновлён

Фронтенд

  • Пользовательский Hook инкапсулирует логику получения данных
  • Компоненты используют семантические токены и UI-компоненты проекта
  • Новые страницы используют ленивую загрузку React.lazy
  • Файлы i18n созданы для всех трёх языков

Качество

  • npm run lint выполняется без ошибок
  • npm run typecheck проходит
  • Размеры файлов в пределах лимитов
  • Тесты тёмной/светлой темы пройдены
  • Тесты режима прозрачности обоев пройдены

Документация

  • Skill architecture-index обновлён
  • База знаний elfi-kb обновлена (если добавлена видимая пользователю функция)
  • Описание PR чётко объясняет внесённые изменения