¶ TipTap Content Standard — Roadmap (LAN-87)
Реализовано в EditorTiptap:
| Компонент |
Файл |
Описание |
| Ядро редактора |
EditorTiptap.js |
React-обёртка useEditor из @tiptap/react |
| Форматирование |
StarterKit |
bold, italic, strike, inline-code, heading, blockquote, codeBlock, списки (ul/ol), hr |
| Автоссылки |
@tiptap/extension-link |
autolink, linkOnPaste, openOnClick:false |
| Изображения |
ImageWithView.js + ImageNodeView.js |
NodeView с кнопкой удаления, сериализация  |
| Видео |
Video.js + VideoNodeView.js |
Кастомный atomic-узел, iframe (YouTube/Rutube/Vimeo), сериализация голым embed-URL |
| Inline-тулбар |
InlineToolbar.js |
BubbleMenu над выделением: bold/italic/strike, списки, переключение ссылки |
| Hover-tooltip ссылок |
LinkHoverTooltip.js |
tippy.js delegate: «перейти по ссылке» / «написать письмо» |
| Markdown round-trip |
markdown.js |
Кастомный сериализатор markdownToDoc / docToMarkdown в диалекте проекта |
| Read-only |
readOnly prop |
editor.setEditable(false) |
| Disabled |
disabled prop |
То же, алиас |
| Singleline |
singleline prop |
Enter не создаёт новый блок |
| Only-linkify |
onlyLinkify prop |
Только текст + автоссылки, всё остальное форматирование отключено |
| Placeholder |
@tiptap/extension-placeholder |
Уже настроен |
| Медиа-кнопки |
FilesUploader + ImageButton + VideoButton |
Загрузка картинок, вставка видео |
| Паритет-тесты |
markdown.test.js |
243 строки, cross-check со старым draftRawFromMarkdown/markdownFromDraftRaw |
Установленные @tiptap-пакеты (^3.22.4): core, react, pm, starter-kit, extensions, extension-image, extension-link, extension-placeholder, extension-mention, suggestion, extension-document, extension-paragraph, extension-text, extension-history.
Отдельный компонент (PromptRichEditor/), не EditorTiptap. Свой набор extensions: Document, Paragraph, Text, History, Placeholder, Mention с тремя suggestion-триггерами (@, {, (). Сериализация — raw-строка (promptSerializer.js). Сюда не входит задача LAN-87.
| Компонент |
Файл (src/js/components/) |
Фичи |
TaskCardContent |
taskcard/TaskCardContent.js |
full-featured, description, error/info, disabled |
NoteCardContent |
notecard/NoteCardContent.js |
full-featured, description, error/info, disabled |
ServiceNoteCardContent |
servicenotecard/ServiceNoteCardContent.js |
full-featured, description, error/info, disabled |
GoalCard |
goalcard/GoalCard/GoalCard.js |
full-featured, description, error/info, disabled |
MarkInfoBlock |
MarkInfoBlock/MarkInfoBlock.js |
full-featured, description, error/info, disabled |
Все используют редактор для поля description. Нужны: форматирование, картинки, видео, лимиты символов.
| Компонент |
Файл |
Фичи |
EmployeeProfileContent |
EmployeeProfile/EmployeeProfileContent.js |
email (onlyLinkify), website (onlyLinkify), description (full-featured) |
PersonalMainInfo |
PersonalProfileContent/PersonalMainInfo.js |
email (readOnly), description (editable) |
Редактор для email/website в режиме onlyLinkify (автоссылки без rich-форматирования). Description — full-featured.
| Компонент |
Файл (CardFields/) |
Фичи |
EditorField |
EditorField/EditorField.js |
Обёртка: error/info через LimitError/LimitInfo |
LinkField |
LinkField/LinkField.js |
onlyLinkify + error/info |
EmailField |
EmailField/EmailField.js |
Замена Input, предупреждение дубликатов, onlyLinkify |
CardTemplateField |
CardTemplateField/CardTemplateField.js |
Через EditorField, кастомные поля |
| Компонент |
Файл (print/) |
Фичи |
TaskCardPrintSection |
TaskCardPrintSection/TaskCardPrintSection.js |
Через EditorField, без onChange (de facto read-only) |
NoteCardPrintSection |
NoteCardPrintSection/NoteCardPrintSection.js |
То же |
ServiceNoteCardPrintSection |
ServiceNoteCardPrintSection/ServiceNoteCardPrintSection.js |
То же |
RequisitionCardPrintSection |
RequisitionCardPrintSection/RequisitionCardPrintSection.js |
То же |
ProjectCardPrintSection |
ProjectCardPrintSection/ProjectCardPrintSection.js |
То же |
GoalCardPrintSection |
GoalCardPrintSection/GoalCardPrintSection.js |
То же |
AssignmentCardPrintSection |
AssignmentCardPrintSection/AssignmentCardPrintSection.js |
То же |
Проблема: print-секции используют EditorField без readOnly-пропа; редактор создаётся редактируемым, но onChange не передан — значение не сохраняется, но UI позволяет вносить изменения в DOM. Нужно унифицировать: либо явный readOnly, либо специализированный режим display-only.
| Компонент |
Файл (modals/) |
Фичи |
ConnectTelephonyModal |
ConnectTelephonyModal/ConnectTelephonyModal.js |
email (onlyLinkify), 2 экземпляра |
| Компонент |
Файл |
Фичи |
StageEditorField |
settings/StagesContent/fields/StageEditorField.js |
full-featured, через EditorField |
| # |
Фича |
Нужна для |
Приоритет |
Пакет / реализация |
Текущий статус |
| 1 |
Таблицы |
Задачи, заметки, база знаний — структурированные данные (сметы, сравнения, ТТХ) |
🟡 P1 |
@tiptap/extension-table (не установлен) |
Отсутствует |
| 2 |
Файловые вложения |
Задачи, карточки — прикрепление docx/pdf/xlsx к описанию |
🟡 P1 |
Custom extension + FileNodeView + upload API |
Отсутствует. Есть FilesUploader для картинок, но не для файлов |
| 3 |
Аудио в тексте |
Заметки, чаты — голосовые сообщения внутри rich-контента |
🟡 P2 |
Custom NodeView (<audio> / игрок) |
Отсутствует |
| 4 |
Упоминания сотрудников |
Задачи, заметки — @username в описании с уведомлением |
🟡 P1 |
@tiptap/extension-mention + @tiptap/suggestion (уже установлены) |
Не подключены в EditorTiptap. Реализованы только в PromptRichEditor/ (другой диалект raw-строк) |
| 5 |
Markdown clipboard import |
Импорт контента из внешних редакторов (Notion, Obsidian) |
🟢 P3 |
Кастомный transformPastedText-обработчик + markdownToDoc |
Отсутствует. Текущий сериализатор — только value↔doc (внутренний обмен). Вставка markdown извне не поддерживается |
| 6 |
Read-only mode |
Print-секции, просмотр карточек без прав редактирования |
🟢 P2 |
Уже реализован (readOnly prop) |
Есть. Нужно применить явно в print-секциях (LAN-87.2) |
| 7 |
Подсчёт символов |
Валидация лимитов на клиенте «на лету» (до отправки) |
🟢 P3 |
@tiptap/extension-character-count (не установлен) |
Сейчас лимиты только через error/info-функции (post-hoc), нет счётчика в реальном времени |
| 8 |
Code syntax highlight |
Техническая документация, вставка кода в базу знаний |
🟢 P3 |
@tiptap/extension-code-block-lowlight + lowlight (не установлены) |
Базовый codeBlock из StarterKit есть (без подсветки) |
| 9 |
Копирование/вставка HTML |
Вставка из Word/Google Docs, email-клиентов |
🟡 P2 |
Встроенная поддержка StarterKit (parseHTML) работает частично; диалект ссылок ломает [text](url) |
Частично. Требует адаптации под диалект (голые URL вместо [text](url)) |
| 10 |
Emoji suggestion |
Быстрая вставка emoji через : |
🟢 P3 |
@tiptap/extension-emoji (community, не установлен) |
Отсутствует |
Лимиты задаются через LIMIT_VALUES_FOR_RATES(typeField, rate) в utils/limits.js. Типы полей, актуальные для редактора:
| Тип поля |
Где используется |
Приблизительный лимит |
description |
Задачи, заметки, цели, служебки (основной контент) |
Определяется тарифом |
default |
Названия, мелкие поля |
Определяется тарифом |
email |
Email-поля (onlyLinkify) |
Определяется тарифом |
additionalWebSites |
Веб-сайты (onlyLinkify) |
Определяется тарифом |
textarea |
Авто-сообщения этапов |
Определяется тарифом |
message |
Сообщения чата |
Определяется тарифом |
| Параметр |
Предлагаемое значение |
Обоснование |
| Макс. размер файла (картинка) |
10 MB |
Текущий FilesUploader — ограничение бэкенда |
| Макс. размер файла (документ) |
25 MB |
Для docx/pdf — разумно для бизнес-документов |
| Макс. размер аудио |
50 MB |
Голосовые сообщения до ~30 мин |
| Макс. вложений на документ |
20 |
Чтобы не перегружать DOM/рендеринг |
- Символы:
@tiptap/extension-character-count + сравнение с лимитом в error/info-функциях в реальном времени.
- Файлы: валидация размера на клиенте (до upload) + серверная проверка. Показывать прогресс-бар загрузки через
FileNodeView.
- Кол-во вложений: подсчёт узлов типа
file/image/audio в doc-JSON, блокировка кнопок при достижении лимита.
EditorTiptap.propTypes = {
className: PropTypes.string, // CSS-класс контейнера
value: PropTypes.string, // markdown-строка (диалект проекта)
label: PropTypes.string, // подпись над редактором
error: PropTypes.func, // (value)=>message|falsy — ошибка валидации
warning: PropTypes.func, // (value)=>message|falsy — предупреждение
info: PropTypes.func, // (value)=>message|falsy — инфо-подсказка
readOnly: PropTypes.bool, // только просмотр
disabled: PropTypes.bool, // синоним readOnly (backward-compat)
singleline: PropTypes.bool, // Enter не создаёт новый блок
onlyLinkify: PropTypes.bool, // только текст + автоссылки
placeholder: PropTypes.string, // placeholder текста
onChange: PropTypes.func, // (markdown) => void
onFocus: PropTypes.func, // (event) => void
onBlur: PropTypes.func, // (event) => void
};
// ── Упоминания (LAN-87.1) ──
mentionSuggestions: PropTypes.shape({
employees: PropTypes.array, // [{_id, fullName, ...}] — список для @
groups: PropTypes.array, // [{_id, title, ...}] — для групп
}),
// ── Лимиты (LAN-87.3) ──
maxChars: PropTypes.number, // макс. символов (вкл. character-count)
// ── Файлы (LAN-87.4) ──
onFilesUpload: PropTypes.func, // (files: FileList) => Promise<{url, name, size}[]>
maxFileCount: PropTypes.number, // макс. кол-во вложений (по умолч. 20)
maxFileSize: PropTypes.number, // макс. размер файла в байтах (по умолч. 25 MB)
// ── Аудио (LAN-87.5) ──
showAudioButton: PropTypes.bool, // показывать кнопку вставки аудио
audioUploadFn: PropTypes.func, // (file: File) => Promise<{url}>
// ── Таблицы (LAN-87.6) ──
showTableButton: PropTypes.bool, // показывать кнопку вставки таблицы
// ── Markdown import (LAN-87.7) ──
allowMarkdownPaste: PropTypes.bool, // разрешить вставку markdown извне
- Все новые пропсы — опциональны, default:
null/false/undefined.
- Существующие потребители (17+ файлов) работают без изменений.
readOnly и disabled остаются синонимами.
noTooltip — не объявлен в propTypes (как и в EditorNext), игнорируется.
| ID |
Фича |
Effort |
Зависимости |
Фаза |
| LAN-87.1 |
Подключить @tiptap/extension-mention + @tiptap/suggestion в EditorTiptap: popup-подсказки при @ для сотрудников (данные через пропс mentionSuggestions). Сериализация в диалекте: @{id|fullName}. |
🟡 |
— |
8 |
| LAN-87.2 |
Явный readOnly для print-секций: пройти 7 файлов в print/, добавить readOnly проп в EditorField/EditorTiptap. Проверить что при печати редактор не реагирует на клики. |
🟢 |
— |
8 |
| LAN-87.3 |
@tiptap/extension-character-count + real-time лимит: установить пакет, добавить проп maxChars, показывать счётчик N/M в info-блоке. Интегрировать с существующими error/info-функциями. |
🟢 |
— |
8 |
| LAN-87.4 |
Файловые вложения: custom extension file (Node + NodeView) с иконкой типа файла и кнопкой скачивания. Пропсы onFilesUpload, maxFileCount, maxFileSize. Сериализация в диалекте: [fileName](url). |
🟡 |
LAN-87.3 (валидация размера) |
8 |
| LAN-87.5 |
Аудио-узел: custom extension audio (Node + NodeView c HTMLMediaElement). Кнопка в тулбаре через showAudioButton. Загрузка через audioUploadFn. Сериализация в диалекте: ~audio(url). |
🟡 |
LAN-87.4 (паттерн NodeView-загрузки) |
8 |
| LAN-87.6 |
Таблицы: установить @tiptap/extension-table + @tiptap/extension-table-row + @tiptap/extension-table-cell + @tiptap/extension-table-header. Кнопка в тулбаре. Сериализация: GFM-таблицы (markdown-it table-расширение). |
🟡 |
— |
8 |
| LAN-87.7 |
Markdown clipboard import: добавить обработчик transformPastedText в editorProps. Распознавать markdown-синтаксис (bold/italic/lists/links) и преобразовывать через markdownToDoc. Флажок allowMarkdownPaste. |
🟡 |
— |
8 |
| LAN-87.8 |
Интеграционное тестирование: сквозные тесты для всех новых фич в рамках EditorTiptap. Расширить markdown.test.js parity-тестами на таблицы, вложения, аудио. |
🟡 |
LAN-87.1–7 |
8 |
Расшифровка effort: 🟢 <4h · 🟡 4–16h · 🔴 >16h
- MIGRATION_AUDIT.md — Фаза 5 (Frontend modernisation)