Научный редактор

Научный редактор Zemna.AI — редактор форматированного текста на базе Tiptap для академического письма. Он поддерживает формулы LaTeX, автоматическое сохранение, историю версий и сохранение публикаций через слой базы данных Ring Platform.

Статус: Фаза 2 (спринты 2.1–2.3) завершены. Поддержка рисунков/таблиц и экспорт запланированы в спринтах 2.4–2.5.


Обзор

ВозможностьОписание
Форматированный текстЗаголовки (H1–H6), жирный, курсив, подчёркивание, списки, цитаты, блоки кода, таблицы, ссылки, изображения
LaTeXИнлайн и блочные формулы через KaTeX; модальное окно формул с предпросмотром и шаблонами
АвтосохранениеСохранение с задержкой 30 с; первое сохранение создаёт публикацию, далее обновление через PUT
История версийСнимки и восстановление версий; список версий с датами и опциональными описаниями
ПубликацииСодержимое хранится как Tiptap JSON; загрузка по ?id=... или создание новой

Возможности редактора

Форматированный текст (Tiptap)

Редактор использует Tiptap с StarterKit и расширениями для научного письма:

  • Форматирование: Жирный, курсив, подчёркивание, зачёркивание, надстрочный, подстрочный, выделение
  • Структура: Заголовки H1–H6, нумерованные/маркированные списки, блочные цитаты
  • Блоки: Блоки кода (подсветка через lowlight), таблицы (строки, столбцы, заголовок)
  • Инлайн: Ссылки, изображения (поддержка placeholder)
  • Типографика: Умные кавычки и улучшения типографики

Содержимое хранится как Tiptap JSON в поле публикации content; редактор принимает начальное содержимое как строку HTML или JSON для загрузки сохранённых публикаций.

Формулы LaTeX

  • Блочные формулы: Вставка формул в режиме display через панель (иконка калькулятора) или модальное окно формул.
  • Отрисовка: KaTeX рендерит формулы в редакторе; клик для редактирования открывает модальное окно.
  • Модальное окно: Поле LaTeX, переключатель display/inline, предпросмотр, кнопки шаблонов (дробь, сумма, интеграл, матрица), Вставить/Отмена.

Формулы хранятся как собственные узлы Tiptap (mathBlock) с атрибутами latex и display.

Автосохранение и публикации

  • Новый документ: Нет ?id= в URL; первое «Сохранить» или первое автосохранение создаёт публикацию через POST /api/publications и устанавливает id публикации для последующих сохранений.
  • Существующий документ: Открыть с ?id=<publicationId>; страница загружает публикацию и устанавливает заголовок и содержимое (JSON). Автосохранение и ручное «Сохранить» используют PUT /api/publications/:id.
  • Задержка: Изменения заголовка или содержимого запускают сохранение с задержкой 30 с; ручное «Сохранить» выполняет сохранение сразу.
  • Индикатор сохранения: На панели отображается «Сохранено» с временем последнего успешного сохранения.

История версий

  • Панель: Кнопка «История версий» на панели открывает диалог со списком версий (номер версии, дата, опциональное описание).
  • Снимок: «Сохранить снимок» создаёт новую версию с текущим содержимым (опциональное описание изменений).
  • Восстановить: «Восстановить» для версии обновляет содержимое публикации до этой версии и перезагружает редактор; панель закрывается, редактор показывает восстановленное содержимое.

Архитектура

Схема публикаций

Публикации и версии используют абстракцию базы данных Ring Platform (PostgreSQL с JSONB):

  • publications: id, data (JSONB), created_at, updated_at.
    data содержит: user_id, title, content (Tiptap JSON), status, template_id.
  • publication_versions: id, data (JSONB), created_at, updated_at.
    data содержит: publication_id, version_number, content, change_summary, created_by.

Схема определена в data/schema.sql; адаптер PostgreSQL имеет fieldMappings для обеих коллекций.

Поток сохранения

sequenceDiagram
  participant User
  participant Editor
  participant useAutoSave
  participant API
  participant DB

  User->>Editor: Edit content / title
  Editor->>useAutoSave: touch(publicationId, payload)
  useAutoSave->>useAutoSave: Debounce 30s
  Note over useAutoSave: Timer fires
  useAutoSave->>API: POST /api/publications (new) or PUT /api/publications/:id
  API->>DB: create() or update()
  DB-->>API: publication
  API-->>useAutoSave: 200 + publication
  useAutoSave->>Editor: onFirstSave(id) if new
  useAutoSave->>Editor: lastSaved updated

Поток истории версий

sequenceDiagram
  participant User
  participant Panel
  participant API
  participant DB

  User->>Panel: Open Version History
  Panel->>API: GET /api/publications/:id/versions
  API->>DB: findByField(publication_versions, publication_id, id)
  DB-->>API: versions[]
  API-->>Panel: versions

  User->>Panel: Restore version V
  Panel->>API: POST /api/publications/:id/versions { action: 'restore', versionId: V }
  API->>DB: updatePublication(id, content from V)
  DB-->>API: publication
  API-->>Panel: 200
  Panel->>User: onRestore() → refetch publication, set editor content

API-маршруты

МетодПутьОписание
GET/api/publicationsСписок публикаций текущего пользователя
POST/api/publicationsСоздать публикацию (body: title, content, status, template_id)
GET/api/publications/[id]Получить публикацию (проверка владения)
PUT/api/publications/[id]Обновить публикацию (проверка владения)
DELETE/api/publications/[id]Удалить публикацию (проверка владения)
GET/api/publications/[id]/versionsСписок версий публикации
POST/api/publications/[id]/versionsСнимок (action: snapshot) или восстановление (action: restore, versionId)

Все маршруты требуют аутентификации; для get/update/delete и версий проверяется владение.


Ключевые файлы

ОбластьПуть
Страница редактораapp/(authenticated)/[locale]/editor/page.tsx
Научный редакторcomponents/editor/scientific-editor.tsx
Панель инструментовcomponents/editor/editor-toolbar.tsx
Расширение LaTeXcomponents/editor/extensions/latex-extension.tsx
Модальное окно формулcomponents/editor/equation-editor.tsx
Панель истории версийcomponents/editor/version-history-panel.tsx
Хук автосохраненияhooks/use-auto-save.ts
Типы публикацийfeatures/publications/types/index.ts
Сервис публикацийfeatures/publications/services/publication-service.ts
API-маршрутыapp/(public)/api/publications/
Схемаdata/schema.sql

Использование

Открытие редактора

  • Новая публикация: Перейдите на /editor (или /[locale]/editor). Редактор загружается с типовым научным шаблоном; id публикации устанавливается при первом сохранении.
  • Существующая публикация: Перейдите на /editor?id=<publicationId>. Страница получает публикацию и устанавливает заголовок и содержимое (Tiptap JSON) в редакторе.

Сохранение

  • Вручную: Нажмите Сохранить на панели. Для нового документа это создаёт публикацию и устанавливает id; для существующего — обновление через PUT.
  • Автосохранение: Через 30 секунд без изменений текущий заголовок и содержимое сохраняются автоматически (POST для нового, PUT для существующего).

История версий

  1. Нажмите История версий (иконка истории) на панели.
  2. Используйте Сохранить снимок, чтобы создать новую версию с текущим содержимым.
  3. Используйте Восстановить для версии, чтобы установить содержимое публикации на эту версию и перезагрузить редактор.
Совет

Восстановление не удаляет версии; оно обновляет содержимое публикации, история версий сохраняется.


Дорожная карта (остаток фазы 2)

  • Спринт 2.4: Компонент загрузки рисунков, улучшения редактора таблиц, расширение перекрёстных ссылок, интеграция FileService для изображений.
  • Спринт 2.5: Сервисы экспорта (PDF, Word, Markdown), диалог экспорта, кнопка и сочетание клавиш экспорта.

Подробный разбор спринтов: Фаза 2: Улучшение научного редактора.