Правила написания и форматирования текста комментариев¶
С 2026-05-13 (MR'ы spa!903 + spa!904, задачи #2038789 + #2075762) парсер SPA понимает стандартный CommonMark/GFM. iOS и Android — пока на старом подмножестве (отдельная задача).
1. Поддерживаемое форматирование¶
| Синтаксис | Результат | SPA лента | SPA чат | iOS | Android |
|---|---|---|---|---|---|
**текст** |
Жирный | + | + | + | + |
__текст__ |
Курсив (нестандарт: в CommonMark __ = жирный) |
+ | + | + | + |
~~текст~~ |
Зачёркнутый | + | + | + | + |
((текст)) |
Подчёркнутый (нестандарт) | + | + | + | + |
`текст` |
Inline-код, моноширинный | + | + | + | + |
[текст](url) |
Именованная ссылка | + | + | + | + |
# текст / ## текст / ### текст |
Заголовок → <strong> (без увеличения размера, как акцент) |
+ | + | — | — |
```...``` |
Fenced code блок (моноширинный, с padding'ом) | + | + | — | — |
\| a \| b \|\| --- \| --- \|\| 1 \| 2 \| |
GFM-таблица → <table> с border, header row. Ширина: в ленте/МТФ растягивается по ширине контейнера ленты (на узком/мобильном экране — горизонтальный скролл при сжатии); в баблах чата — горизонтальный скролл внутри сообщения, поскольку ширина бабла ограничена. См. § 3 SPA. |
+ | + | — | — |
#12345 |
Автоссылка на задачу | + | + | + | + |
@user{userId} (@user28) |
Упоминание пользователя | + | + | + | + |
URL (https://...) |
Автоматическая ссылка | + | + | + | + |
URL Диска (https://.../spa/disk/...) |
Открывает папку/файл Диска в превью | + | + | — | — |
\n (реальный перевод строки) |
Перенос строки | + | + | + | + |
Главное отличие от стандарта: __текст__ = курсив (в GitHub-flavored Markdown это жирный). Для жирного используй только **текст**.
2. Что НЕ поддерживается ни на одном клиенте¶
- HTML-теги (
<br>,<b>,<p>,<div>, любые другие) - iOS: отображает теги как текст
- SPA: интерпретирует в браузере (побочный эффект — нельзя обсуждать HTML-код в комментариях).
<>, не входящие в валидный HTML-тег (математические знаки, шаблонные строки, SQL-логи), экранируются на бэкенде до сохранения и отображаются как обычный текст; валидные теги с атрибутомmd-render(генерируемые markdown-процессором фронта) не затрагиваются - Маркированные / нумерованные списки (
-,*,1.) — план #2038789 их упоминал, но в работу не взяли. Используй тире-эм-даш с новой строки - Горизонтальные разделители (
---) — план #2038789 их упоминал, не реализовано - Изображения в тексте (
) - Стандартный markdown-italic через одинарный
*(*текст*) — поддерживается только__текст__
3. Рендеринг по клиентам¶
SPA (Angular, лента + чат)¶
Shared service: apps/spa/src/app/common/components/feed-comments/services/comment-markdown.service.ts (один на оба пайпа с 2026-05-13, MR spa!904).
Пайплайн обработки (упрощённо):
1. markdownProcess:
- mdFencedCode → <pre class="md-code-block"> (placeholder)
- mdTable → <div class="md-table-wrapper"><table class="md-table"> (placeholder, GFM с поддержкой alignment :--- / ---: / :---:)
- mdCode → <code> (placeholder, защита от ** внутри)
- mdLink → <a> (placeholder)
- mdHeader → <strong> (без placeholder — внутри заголовка работают taskLink / userLink / commonLinks)
- mdBold / mdEm / mdDel / mdUnderlined → <strong> / <em> / <del> / <u>
2. newLineBr → \n → <br> (только в основном тексте, внутри placeholder'ов newline сохраняется для <pre>)
3. commonLinks → URL → кликабельные <a>
4. taskLink → #12345 → ссылка на задачу
5. expandableText → длинные комменты сворачиваются (но см. § 5 — known issue с placeholder'ами)
6. replaceSigAccept → стилизация подписей событий 51/52/70/76
7. userLink → @user28 → ссылка на пользователя
8. restoreMarkdownTokens → подстановка HTML для всех placeholder'ов, multi-pass для вложенных ([code](url))
9. getSafeHtml → whitelist-санитизация (см. § 5 — known issue с onclick= внутри fenced)
Стилизация code по типу события (только inline):
- Events 51, 70, 80, 124 (позитивные): синий #2c88e0
- Events 52, 76 (негативные): красный #f44336
Стили таблиц (apps/spa/src/app/common/components/feed-comments/feed-comments.theme.scss:72-99):
- .md-table-wrapper — display: block; overflow-x: auto; max-width: 100% (обеспечивает горизонтальный скролл при переполнении в обоих режимах).
- .md-table — width: 100%; border-collapse: collapse; font-size: 0.95em (растягивается на всю ширину обёртки; колонки делят ширину пропорционально содержимому).
- .md-table th — background: var(--surface-zero); font-weight: 600 (фон шапки из темы).
- Разница лента vs чат не в стилях обёртки, а в ширине родительского контейнера: в ленте контейнер широкий — таблица растягивается, в бабле чата контейнер уже — таблица упирается в overflow-x: auto. Дополнительная адаптация под бабл чата при необходимости — в feature-chat.theme.scss.
Чат-пайп (apps/spa/src/app/components/chat/ui/pipes/comment-text-to-html.pipe.ts) использует тот же CommentMarkdownService. До MR spa!904 markdown в чате вообще не работал.
iOS¶
Файл: 1fchat-ios/Categories/String+Extension.swift:84-234
Рендеринг через NSAttributedString:
- **text** → bold font trait
- __text__ → italic font trait
- ~~text~~ → strikethrough
- ((text)) → underline
- `text` → monospace
- [text](url) → NSLinkAttributeName
- #12345 → кликабельный
- @user123 → кликабельный badge
Не поддерживается на iOS: #/##/### заголовки, ``` fenced code блоки, | GFM | таблицы. Покажутся как plain text с символами разметки. Отдельная задача.
Android¶
Аналогично iOS. Те же ограничения.
4. Правила для автоматических комментариев (Анфиса, боты, API)¶
- Пиши обычный markdown (CommonMark/GFM). Не нужно «кастомного синтаксиса 1Формы» — поддерживается стандарт.
- 1Форма-специфика:
__текст__это курсив, не жирный — для жирного используй**текст**((текст))— подчёркнутый (опционально)#12345— автоссылка на задачу,@user28— упоминание пользователя- Переносы строк — настоящий перевод строки (LF). Не
\nкак двусимвольный escape. - Списки
-/1.пока не рендерятся — используй тире-эм-даш с новой строки. - Заголовки/fenced/таблицы в SPA работают, на iOS/Android — пока plain text. Учитывай аудиторию — если комментарий важен для мобильных пользователей, по возможности используй inline-разметку (
**bold**,`code`) вместо блочной.
5. Known issues (follow-up, не блокирует use)¶
См. ServiceFlow/spa-feed-comments-md-followup-2038789.md:
- expandableText видит placeholder MD:N вместо реального fenced code блока → длинные блоки кода не сворачиваются.
- htmlHasPotentialRiskHtml ловит substring onclick= / <img / <script внутри escaped содержимого fenced code → весь HTML экранируется, блок рендерится как plain text.
Обе требуют рефакторинга processedValue$ pipeline (восстановление placeholder'ов после getSafeHtml).
6. Ссылки на сущности — URL-паттерны¶
В комментариях URL автоматически кликабельны (SPA, iOS, Android). Используй правильные паттерны:
Shorthand (парсится системой)¶
| Синтаксис | Результат | Пример |
|---|---|---|
#TaskId |
Ссылка на задачу | #12345 |
@user{userId} |
Упоминание пользователя | @user28 |
Полные URL (для сущностей без shorthand)¶
Задачи и гриды:
| Сущность | URL | Пример |
|---|---|---|
| Задача | https://{host}/spa/tasks/{taskId} |
/spa/tasks/12345 |
| Грид категории | https://{host}/spa/tasks/subcat/{subcatId}/grid |
/spa/tasks/subcat/5574/grid |
| Канбан категории | https://{host}/spa/tasks/subcat/{subcatId}/kanban |
/spa/tasks/subcat/5574/kanban |
| Портал категории | https://{host}/spa/tasks/subcat/{subcatId}/portal |
/spa/tasks/subcat/5574/portal |
Администрирование:
| Сущность | URL | Пример |
|---|---|---|
| Настройки категории | https://{host}/spa/administration/subcategory/{subcatId} |
/spa/administration/subcategory/63540 |
| ДП категории (список) | https://{host}/spa/administration/ext-params-settings/subcat/{subcatId} |
/spa/administration/ext-params-settings/subcat/13759 |
| Конкретный ДП | https://{host}/spa/administration/ext-params-settings/{subcatId}/{extParamId} |
/spa/administration/ext-params-settings/13759/45012 |
| SmartScripts | https://{host}/spa/administration/smart-scripts/{scriptId} |
/spa/administration/smart-scripts/492 |
| Пользователь (профиль) | https://{host}/spa/users/{userId}/info |
/spa/users/28/info |
Правила:
- Задачи — ВСЕГДА shorthand #TaskId, не полный URL. Система автоматически рендерит кликабельную ссылку.
- Для остальных сущностей — полный URL. Подставляй реальные ID из контекста.
- На клиентских площадках домен другой (например client.1forma.ru), но пути те же.
7. Ключевые файлы¶
| Клиент | Файл | Назначение |
|---|---|---|
| SPA | common/components/feed-comments/services/comment-markdown.service.ts |
Shared markdown service — единая логика для ленты и чата (с 2026-05-13) |
| SPA | common/components/feed-comments/services/comment-markdown.service.spec.ts |
Тесты shared сервиса (~49 кейсов, покрывают все форматы + edge cases) |
| SPA | common/components/feed-comments/pipes/comment-text-to-html.pipe.ts |
Pipe для ленты (оркестрация: markdown service + taskLink/userLink/expandableText/getSafeHtml) |
| SPA | components/chat/ui/pipes/comment-text-to-html.pipe.ts |
Pipe для чата (оркестрация для баблов) |
| SPA | common/components/feed-comments/feed-comments.theme.scss |
Стили .md-code-block, .md-table-wrapper, .md-table |
| SPA | common/components/feed-comments/pipes/decode-html-entities.pipe.ts |
Декодирование HTML-entities |
| iOS | String+Extension.swift:84-234 |
markdownAttributes() (старый подмножество, без headers/fenced/tables) |
| iOS | NSMutableAttributedString+Analyze.m:32-44 |
highlightTasksInText |
| iOS | LinksInteractionTextView.m |
UITextView с обработкой кликов |