docs(openspec): change wiki-render — рендер вики-разметки Jira в карточке (#53)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
86739cfcf8
commit
2fe9518d01
2
openspec/changes/wiki-render/.openspec.yaml
Normal file
2
openspec/changes/wiki-render/.openspec.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-10-17
|
||||
100
openspec/changes/wiki-render/design.md
Normal file
100
openspec/changes/wiki-render/design.md
Normal file
@ -0,0 +1,100 @@
|
||||
## Context
|
||||
|
||||
`main` 86739cf. Устройство, на которое опираются правки:
|
||||
|
||||
- **Данные.** `GET /issues/{key}` отдаёт `description: string | null` и `comments: [{comment_id, author, created,
|
||||
updated, body}]` — тексты Jira как есть (вики-разметка Jira Server, REST v2). Хаб и MCP не меняются.
|
||||
- **Карточка** — `web/src/issues/IssueCard.tsx`: `Description` выводит текст в `<pre class="description">` со свёрткой
|
||||
до 8 строк (`-webkit-line-clamp` на `.description.clamped`, признак «длинное» — больше 8 строк или
|
||||
`scrollHeight > clientHeight`, кнопка «развернуть» / «свернуть»); `Comments` выводит `<pre class="body">` в каждом
|
||||
`<li>`, над ним `.who` (автор, время); «Скрыть» / «Показать» раздела — `useCommentsHidden` (localStorage).
|
||||
Стили — `web/src/issues/issues.css`, токены цветов — `var(--panel)`, `var(--line)`, `var(--muted)` и др.
|
||||
- **Ссылка на карточку** — `IssueLink` (`web/src/issues/IssueLink.tsx`): `<Link to="/issues/{key}"
|
||||
state={openCardState(loc)}>`, открывает задачу в том же окне с сохранением страницы под ним.
|
||||
- **Реальные тексты AURORA** (выборка 17.10.2026): заголовки `h3.`; нумерованные `#` и маркированные `*` списки;
|
||||
` * пункт` с пробелом перед маркером и пустыми строками между пунктами (шаблон ошибок); `*Жирный заголовок.*` в начале
|
||||
комментария; ключи задач `AURORA-1445` в тексте; в описаниях, написанных через MCP, — Markdown-конструкции
|
||||
(`` `код` ``, `- пункт`), которые Jira показывает как есть (`-` — список Jira).
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:** описание и комментарии читаются так, как в Jira; исходник любого блока — одной кнопкой; рендер не ломает
|
||||
карточку ни на каком тексте и не открывает путь для вставки разметки или скриптов.
|
||||
|
||||
**Non-Goals:** см. proposal.md.
|
||||
|
||||
## Decisions
|
||||
|
||||
1. **Модуль `web/src/issues/wiki/`.** `parse.ts` — чистая функция `parseWiki(text: string): Block[]` (дерево блоков и
|
||||
строчных узлов, без React); `WikiText.tsx` — компонент `<WikiText text=… />`, превращающий дерево в React-элементы.
|
||||
Разделение даёт тесты разборщика без DOM и один компонент для описания и комментариев.
|
||||
2. **Дерево.** Блоки: `heading {level, inlines}`, `paragraph {inlines}` (одиночные переносы внутри — узел `br`),
|
||||
`list {ordered, items: [{inlines, children: list[]}]}`, `table {rows: [{cells: [{header, inlines}]}]}`,
|
||||
`pre {text}` (`{code}`, `{noformat}`), `quote {blocks}` (`{quote}`, `bq.`), `hr`. Строчные: `text`, `strong`, `em`,
|
||||
`del`, `ins`, `sup`, `sub`, `code` (`{{…}}`), `link {href, inlines}`, `issue {key, inlines?}`, `mention {login}`,
|
||||
`br`. Рендер: `h1`–`h6` (внутри карточки уровни сдвигаются стилем, не тегом — тег по разметке), `p`, `ul`/`ol`/`li`,
|
||||
`table`/`th`/`td`, `pre`, `blockquote`, `hr`, `strong`, `em`, `del`, `ins`, `sup`, `sub`, `code`,
|
||||
`<a target="_blank" rel="noopener noreferrer">`, `IssueLink`.
|
||||
3. **Разбор блоков — построчно.** Строка классифицируется по началу: `h[1-6]. `, маркеры списка `^\s*[*#]+ ` или
|
||||
`^\s*- ` (ровно один `-`; `--`, `*-`, `-#` — не список), `|`, `{code…}` / `{noformat}` / `{quote}` (открывающий тег в
|
||||
начале строки после необязательных пробелов; содержимое — всё до первого закрывающего тега, который может стоять
|
||||
в той же строке или в любой последующей; остаток строки после закрывающего тега разбирается как новая строка;
|
||||
тег не в начале строки — обычный текст), `bq. `, `----`, пустая строка; прочее — абзац. Соседние строки списка образуют один список; пустая строка между пунктами одного вида списка **не** рвёт
|
||||
список (так Jira показывает шаблон ошибок с пустыми строками между ` * `). Незакрытый `{code}` / `{noformat}` /
|
||||
`{quote}` — открывающая строка разбирается как обычный текст (видна как есть).
|
||||
4. **Строчный разбор — однопроходный по позиции** с проверкой границ из спеки: открывающий маркер — в начале текста
|
||||
или после пробела / знака препинания, за ним не пробел; за открывающим не тот же символ (`**x**`, написанное через MCP Markdown-ом,
|
||||
остаётся текстом как есть — читается лучше полуразобранного); закрывающий — тот же символ после не-пробела, за ним не
|
||||
буква и не цифра; закрывающий ищется до конца строки (выделение не переходит через перенос). Без пары — символ
|
||||
остаётся текстом. Вложенность (жирный курсив `*_x_*`) — рекурсивным разбором содержимого. `{{…}}` и содержимое
|
||||
`{code}` не разбираются. `\` экранирует следующий символ разметки.
|
||||
5. **Ссылки и безопасность.** Адрес ссылки проходит проверку схемы (`http:`, `https:`, `mailto:`, без учёта регистра,
|
||||
после обрезки пробелов); иначе вся конструкция `[…]` — текст. Голые адреса — `https?://` до пробела, хвостовые
|
||||
`.,;:!?)` не входят в адрес. Ключ задачи — `\b<project_key>-\d+\b`, где `project_key` — из настроек хаба
|
||||
(`useSettings()`, сейчас `AURORA`), не внутри адреса, `{{…}}`, `{code}`; `parseWiki(text, {projectKey})`, без
|
||||
`projectKey` (настройки ещё не загружены) ключи — текст. Так `UTF-8`, `SHA-256`, `DOC-221280` ссылками не становятся. Рендер
|
||||
только через React-элементы: текст экранирует React, `href` — после проверки схемы; `dangerouslySetInnerHTML` в
|
||||
модуле нет (проверка — тестом поиска по исходникам модуля).
|
||||
6. **Отказоустойчивость.** `WikiText` — собственная маленькая граница ошибок (класс с `getDerivedStateFromError`, по
|
||||
образцу `shell/ErrorBoundary.tsx`, но с фолбэком «исходник», а не «Страница сломалась»), внутри — разбор и рендер:
|
||||
при исключении блок показывается исходником (`<pre class="wiki-source">`), остальная карточка работает. Разборщик
|
||||
не бросает на нормальном пути; граница — страховка.
|
||||
7. **Переключатель.** Компонент `SourceToggle` — кнопка `.link` с текстом «исходник» (в режиме рендера) / «текст» (в
|
||||
режиме исходника), `aria-pressed`. Состояние — `useState(false)` внутри `Description` и внутри компонента одного
|
||||
комментария (`Comment`, ключ — `comment_id`); `IssueModal` монтирует карточку заново при смене задачи
|
||||
(`<ErrorBoundary key={issueKey}>`), поэтому выбор не переживает переход по связи и закрытие окна. Без localStorage.
|
||||
8. **Свёртка в режиме рендера.** `-webkit-line-clamp` не работает на контейнере с блочными детьми (списки, таблицы),
|
||||
поэтому свёрнутое описание ограничивается высотой: `max-height` = 8 строк (`calc(8 * 1.5em)` при `line-height:
|
||||
1.5`) и `overflow: hidden`, признак «длинное» в режиме рендера — только измерение `scrollHeight > clientHeight + 1`
|
||||
после рендера (без счёта строк исходника: шаблон ошибок с пустыми строками между пунктами иначе показал бы
|
||||
«развернуть» без нужды). В режиме исходника свёртка и признак прежние (больше 8 строк исходника или измерение). Переключение режима не сбрасывает «развёрнуто».
|
||||
9. **Вид.** Отрендеренный текст — в том же «листе», что и сейчас (`.description`: фон `var(--panel)`, рамка, отступ),
|
||||
шрифт интерфейса; заголовки — 15/14/13 px полужирным, отступы абзацев и списков компактные; таблица — рамки
|
||||
`var(--line)`, заголовочные ячейки — фоном; `{code}` / `{noformat}` / `{{…}}` — `var(--font-mono)`; цитата — левая полоса
|
||||
`var(--line)`; широкая таблица или `pre` — горизонтальная прокрутка внутри блока, окно не расширяется.
|
||||
10. **Тесты.** Разборщик — таблица случаев «текст → дерево» в `parse.test.ts` (все конструкции спеки, границы
|
||||
выделения, ложные срабатывания `data_type`, даты, `a*b*c`, числа с ` - `, незакрытые блоки, опасные схемы, ключи
|
||||
задач, тексты по образцу реальных AURORA). Компонент — `WikiText.test.tsx` (теги, ссылки, `IssueLink`, неизвестное
|
||||
текстом, фолбэк при ошибке). Карточка — `IssueCard.test.tsx` (кнопки у описания и у каждого комментария,
|
||||
независимость, сброс при новом монтировании, свёртка в обоих режимах). e2e — сценарий «Описание со структурой» и
|
||||
«Переключение на исходник» в `web/e2e/issues.spec.ts`. Фикстуры — синтетические тексты по образцу реальных, без
|
||||
данных заказчика (R-1).
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- Собственный разборщик расходится с Jira в краевых случаях (вложенные таблицы, смешанные списки, макросы). Смягчение:
|
||||
неразобранное — текстом, исходник — одной кнопкой; расхождение видно и чинится точечно.
|
||||
- Высота свёртки в режиме рендера — приблизительно 8 строк (заголовок или таблица выше строки текста).
|
||||
|
||||
## Decisions made without discussion
|
||||
|
||||
| Маркер | Дата | Решение | Обоснование | Цена ошибки |
|
||||
|---|---|---|---|---|
|
||||
| R-1 | 2026-10-17 | Тестовые тексты — синтетические по образцу реальных описаний AURORA, без копирования текстов задач в репозиторий | В задачах — названия организаций и цифры заказчика; конструкции разметки воспроизводятся без них | Пропущенная редкая конструкция реального текста; видна в карточке, чинится тестом |
|
||||
| R-2 | 2026-10-17 | Ссылками становятся только ключи проекта `project_key` из настроек хаба (`AURORA-123`), открывают карточку в окне хаба, а не в Jira | Jira делает ссылками ключи существующих проектов; хаб знает только свой проект, а общий шаблон `[A-Z]+-\d+` ловит `UTF-8`, `SHA-256`, `DOC-221280` (builder check); переход в Jira есть в шапке | Ключи других проектов Jira — текстом; задача AURORA вне зеркала — «нет в зеркале» |
|
||||
| R-3 | 2026-10-17 | `[~логин]` показывается как «@логин»: отображаемых имён пользователей в хабе нет | Jira показывает имя, но словаря логин → имя в зеркале нет | Логин вместо ФИО |
|
||||
| R-4 | 2026-10-17 | Пустая строка между пунктами списка одного вида не рвёт список | Так выглядит шаблон ошибок AURORA (` * пункт`, пустая строка, ` * пункт`); один список читается лучше трёх | Два задуманных отдельными списка сливаются в один |
|
||||
| R-5 | 2026-10-17 | Свёрнутое описание в режиме рендера ограничивается высотой ≈ 8 строк, а не числом строк | `-webkit-line-clamp` не работает на блочном содержимом | Свёрнутая часть немного выше или ниже 8 строк |
|
||||
| R-7 | 2026-10-17 | Открывающий маркер выделения, за которым тот же символ (`**`, `__`, `--`), не считается маркером | Markdown `**жирный**` из описаний, написанных через MCP, при буквальном применении правил Jira разобрался бы в «жирное `*x` и `*`» — хуже исходника; Jira-разметка двойных маркеров не использует | `**x**` видно со звёздочками (как и в Markdown-исходнике) |
|
||||
| R-8 | 2026-10-17 | Блочные макросы `{code}` / `{noformat}` / `{quote}` распознаются только с открывающим тегом в начале строки; закрывающий — где угодно дальше; остаток строки после него — обычный текст | Однострочный `{quote}` посреди фразы в задачах AURORA не встречался; построчный разбор проще и предсказуемее | Такой `{quote}` виден исходником |
|
||||
| R-6 | 2026-10-17 | Подсветка синтаксиса `{code}`, макросы `{panel}`, `{color}`, картинки, эмодзи, типографика `--` — вне change | Утверждённый объём; неразобранное видно текстом | Эти конструкции видны исходником |
|
||||
35
openspec/changes/wiki-render/proposal.md
Normal file
35
openspec/changes/wiki-render/proposal.md
Normal file
@ -0,0 +1,35 @@
|
||||
## Why
|
||||
|
||||
Описание и комментарии в карточке задачи веб-приложения показываются исходником вики-разметки Jira: `h3. Контекст`,
|
||||
`# пункт`, ` * пункт`, `*жирный*` видны как символы, длинные структурированные описания (контекст, шаги
|
||||
воспроизведения, критерии готовности) читаются плохо. Хаб хранит эти поля как есть (`description`, `body` — wiki,
|
||||
требование «Таблица issue» data-model), разбора разметки в приложении нет — так записано в требовании «Карточка задачи».
|
||||
|
||||
Решения владельца 17.10.2026 (brainstorming, jira-hub#53):
|
||||
- показывать описание и комментарии **так, как их показывает Jira** — рендер вики-разметки Jira, а не Markdown;
|
||||
Markdown-конструкции в наших описаниях (обратные кавычки, `- пункт`) выглядят так же, как в Jira;
|
||||
- исходник остаётся доступен: **кнопка у каждого блока** — у «Описания» и у каждого комментария отдельно.
|
||||
|
||||
## What Changes
|
||||
|
||||
- **Веб-приложение, карточка задачи:**
|
||||
- «Описание» и текст каждого комментария показываются отрендеренной вики-разметкой Jira: заголовки `h1.`–`h6.`,
|
||||
`*жирный*`, `_курсив_`, `-зачёркнутый-`, `+подчёркнутый+`, `^верхний^`, `~нижний~`, `{{моно}}`, списки `*` / `#` / `-` с вложенностью,
|
||||
таблицы `||заголовок||` / `|ячейка|`, блоки `{code}`, `{noformat}`, `{quote}`, `bq.`, ссылки `[текст|url]`, `[url]`
|
||||
и голые URL, упоминания `[~логин]`, ключи задач проекта (`AURORA-123`, открывают карточку в окне), `----`, разрыв строки `\\`;
|
||||
- всё, что разборщик не знает (`{panel}`, `{color}`, картинки `!файл!` и прочее), выводится обычным текстом как есть;
|
||||
- у «Описания» (в заголовке раздела) и у каждого комментария (в строке автора) — кнопка «исходник» / «текст»;
|
||||
исходник выглядит как сейчас — текст с сохранёнными переносами; при каждом открытии карточки всё показано
|
||||
отрендеренным, выбор не запоминается;
|
||||
- свёртка длинного описания («развернуть» / «свернуть») работает в обоих режимах.
|
||||
- Разметка разбирается в браузере собственным модулем на TypeScript в React-элементы; `innerHTML` /
|
||||
`dangerouslySetInnerHTML` не используются, новых зависимостей нет.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Хаб (API, данные, MCP) и агент не меняются; MCP `issue` по-прежнему отдаёт исходник.
|
||||
- Markdown не разбирается.
|
||||
- Полный набор макросов Jira (`{panel}`, `{color}`, `{anchor}`, `{toc}`, вложения-картинки, эмодзи `:)`, типографика
|
||||
`--` / `---`) — нет; неразобранное видно текстом.
|
||||
- Подсветка синтаксиса в `{code}` — нет.
|
||||
- Рендер разметки вне карточки (список «Задачи», доска, «Служебные заметки») — нет.
|
||||
131
openspec/changes/wiki-render/specs/web-app/spec.md
Normal file
131
openspec/changes/wiki-render/specs/web-app/spec.md
Normal file
@ -0,0 +1,131 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Карточка задачи
|
||||
Адрес `/app/issues/{key}` SHALL открывать карточку задачи из `GET /issues/{key}` модальным окном поверх страницы, с
|
||||
которой её открыли (список «Задачи» или доска); страница под окном SHALL оставаться смонтированной и сохранять фильтры,
|
||||
загруженные строки, прокрутку и раскладку. Перезагрузка страницы с открытым окном SHALL сохранять страницу под окном. Открытый напрямую
|
||||
адрес (новая вкладка, закладка) SHALL
|
||||
показывать окно поверх списка задач без фильтров. Окно SHALL закрываться кнопкой ✕, клавишей Esc и щелчком по фону (нажатие и отпускание — на фоне; выделение текста,
|
||||
отпущенное за краем окна, окно не закрывает) и
|
||||
возвращать на страницу, с которой его открыли, даже после переходов по связям внутри окна; «назад» браузера после
|
||||
закрытия MUST NOT открывать карточку снова. Переход по связанной задаче SHALL менять содержимое окна и адрес, «назад»
|
||||
браузера — возвращать к прежней карточке. Пока окно открыто, фокус SHALL оставаться в окне, страница под ним не
|
||||
прокручивается.
|
||||
|
||||
Содержимое окна:
|
||||
- шапка: ключ, статус Jira, статус по факту (цветом класса из `status_labels`), «Открыть в Jira», ✕, тема; строка
|
||||
«исполнитель · срок · спринт · эпик» без пустых частей, срок — `planned_end`, иначе `duedate`, с пометкой «просрочено»,
|
||||
если он раньше сегодняшней даты и класс статуса по факту не `fact-done` и не `fact-cancelled`; эпик — темой
|
||||
(`epic_summary`), иначе ключом;
|
||||
- «Связи» по группам в порядке: «Зависит от» (входящие связи логических типов `блокирует начало` и `блокирует
|
||||
завершение` из `link_types`, с меткой «начало» / «завершение»), «Блокирует» (исходящие тех же типов, с теми же метками),
|
||||
«Связана с» (тип `связанные` в обе стороны), «Прочие» (тип Jira вне `link_types`: имя типа и направление «→» / «←»);
|
||||
в строке — ключ (открывает связанную задачу в окне), тема и статус Jira, а если задачи нет в зеркале — ключ и «нет в
|
||||
зеркале»; пустые группы не показываются, без связей — «связей нет»;
|
||||
- «Описание» — текст, отрендеренный по требованию «Разметка Jira в карточке», с кнопкой «исходник» / «текст» в
|
||||
заголовке раздела; длиннее 8 строк — свёрнуто с кнопкой «развернуть» / «свернуть» в обоих режимах; без описания
|
||||
раздела нет;
|
||||
- «Комментарии (N)» — новые сверху, автор, время, кнопка «исходник» / «текст» у каждого комментария, текст,
|
||||
отрендеренный по требованию «Разметка Jira в карточке»; кнопка «Скрыть» / «Показать» сворачивает раздел, выбор SHALL
|
||||
запоминаться в браузере для всех карточек; без комментариев — «комментариев нет»;
|
||||
- «Служебные заметки» (требование «Служебные заметки»);
|
||||
- «Прочие поля» — непустые из: тип, приоритет, автор, метки, плановое начало, обновлена в Jira, сосед по связям;
|
||||
- «Команды моста (N)» — 10 последних команд по задаче (id, пакет, тип, статус, время завершения, ошибка), раздел свёрнут
|
||||
при открытии; если команд не было — текст «команд по задаче не было».
|
||||
На ширине от 900 px «Связи», «Описание», «Комментарии» — в левой колонке, заметки, прочие поля и команды — в правой;
|
||||
уже — одна колонка: заметки, связи, описание, комментарии, прочие поля, команды. Если задачи нет в зеркале (404), окно
|
||||
SHALL сообщать об этом с кнопкой «Закрыть»; хаб не отвечает — сообщение и «Повторить», ранее загруженные данные
|
||||
остаются.
|
||||
|
||||
#### Scenario: Связанная задача
|
||||
- **WHEN** у AURORA-1620 исходящая связь `ASUPR_Блокер_Начала` на AURORA-1621 «Расчёт свода» в статусе «В работе»
|
||||
- **THEN** в карточке в группе «Блокирует» строка «начало · AURORA-1621 · Расчёт свода · В работе»; щелчок по ключу
|
||||
меняет содержимое окна на AURORA-1621, адрес — `/app/issues/AURORA-1621`; «назад» браузера возвращает AURORA-1620
|
||||
|
||||
#### Scenario: Входящий блокер
|
||||
- **WHEN** у AURORA-1620 входящая связь `ASUPR_Блокер_Завершения` от AURORA-1500
|
||||
- **THEN** строка с AURORA-1500 и меткой «завершение» — в группе «Зависит от»
|
||||
|
||||
#### Scenario: Задача без команд
|
||||
- **WHEN** по AURORA-1620 не было команд
|
||||
- **THEN** в разделе «Команды моста» — текст «команд по задаче не было», таблицы нет
|
||||
|
||||
#### Scenario: Закрытие после переходов
|
||||
- **WHEN** со списка «Задачи» открыта AURORA-1620, в окне — переход по связи на AURORA-1621, нажата Esc
|
||||
- **THEN** окно закрыто, адрес — адрес списка с прежними фильтрами; «назад» браузера не открывает карточку
|
||||
|
||||
#### Scenario: Прямой адрес
|
||||
- **WHEN** в новой вкладке открыт `/app/issues/AURORA-1620`, затем нажат ✕
|
||||
- **THEN** под окном был список задач без фильтров; после закрытия адрес `/app/issues`, «назад» не открывает карточку
|
||||
|
||||
#### Scenario: Скрытые комментарии
|
||||
- **WHEN** в карточке нажато «Скрыть» у комментариев, затем страница перезагружена и открыта другая карточка
|
||||
- **THEN** комментарии в ней скрыты, видна кнопка «Показать» и число комментариев
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Разметка Jira в карточке
|
||||
Описание задачи и текст каждого комментария в карточке SHALL показываться отрендеренной вики-разметкой Jira (хаб
|
||||
хранит их как есть, в wiki) так, как их показывает Jira. Разбираются:
|
||||
- блоки: заголовки `h1.`–`h6.` в начале строки; списки — строки, начинающиеся (после необязательных пробелов) с
|
||||
маркеров `*`, `#` и пробела (уровень вложенности — числом маркеров, `*` — маркированный, `#` — нумерованный,
|
||||
смешанная вложенность `*#` — по последнему маркеру) или с одного `-` и пробела (маркированный, первый уровень);
|
||||
`--`, `*-` и прочие сочетания с `-` — не список; таблицы — строки, начинающиеся с
|
||||
`|`, ячейки `||заголовок||` и `|ячейка|`; `{code}` / `{code:язык}` … `{code}` и `{noformat}` … `{noformat}` —
|
||||
моноширинный блок без разбора разметки внутри; `{quote}` … `{quote}` и строка `bq. ` — цитата; открывающий тег
|
||||
блока — в начале строки, закрывающий — в любом месте той же или последующей строки, текст после закрывающего тега
|
||||
разбирается дальше как обычные строки; тег не в начале строки — текст как есть; строка `----` —
|
||||
горизонтальная черта; прочие строки — абзацы, разделённые пустыми строками, одиночный перенос внутри абзаца —
|
||||
перенос строки;
|
||||
- внутри строки: `*жирный*`, `_курсив_`, `-зачёркнутый-`, `+подчёркнутый+`, `^верхний^`, `~нижний~`, `{{моно}}`
|
||||
(`{{…}}` — без разбора внутри); маркер выделения действует, только если открывающий стоит в начале текста или после
|
||||
пробела либо знака препинания и за ним не пробел и не тот же символ (`**x**` — текст как есть), а закрывающий стоит после не-пробела и за ним не буква и не
|
||||
цифра (`data_type`, `2026-10-15`, `счёт-фактура`, `a*b*c` — обычный текст); `\\` — перенос строки; `\` перед
|
||||
символом разметки — сам символ;
|
||||
- ссылки: `[текст|адрес]`, `[адрес]` и голые адреса `http://`, `https://` — ссылка в новой вкладке, только для схем
|
||||
`http`, `https`, `mailto`, иначе текст как есть; `[ключ задачи]`, `[текст|ключ задачи]` и ключ задачи в тексте вида
|
||||
`ПРОЕКТ-123`, где `ПРОЕКТ` — `project_key` из настроек хаба, — ссылка, открывающая карточку этой задачи в окне
|
||||
(`UTF-8`, `SHA-256` и ключи других проектов — текст);
|
||||
`[~логин]` — «@логин».
|
||||
Всё, что разборщик не распознал (`{panel}`, `{color}`, `!картинка!`, незакрытые маркеры и блоки и прочее), SHALL
|
||||
показываться как текст исходника без потерь. Разметка MUST NOT превращаться в HTML строкой (`innerHTML`,
|
||||
`dangerouslySetInnerHTML`): текст задачи не может добавить на страницу элементы, атрибуты или скрипты. Если рендер
|
||||
блока завершился ошибкой, блок SHALL показываться исходником.
|
||||
|
||||
Кнопка «исходник» у описания и у каждого комментария SHALL переключать этот блок на исходник — текст с сохранёнными
|
||||
переносами строк, как без разбора разметки, — и обратно кнопкой «текст»; остальные блоки не переключаются. При каждом
|
||||
открытии карточки (в том числе при переходе по связи) все блоки показываются отрендеренными, выбор не запоминается.
|
||||
|
||||
#### Scenario: Описание со структурой
|
||||
- **WHEN** описание AURORA-1620 — `h3. Контекст`, строка `Шаги:`, затем `# Загрузить ОСВ` и `# Открыть документ`,
|
||||
затем пустая строка и `*Итог:* ошибка в {{data_type}}`
|
||||
- **THEN** в «Описании» заголовок «Контекст», абзац «Шаги:», нумерованный список из двух пунктов и абзац, где «Итог:» —
|
||||
жирным, «data_type» — моноширинным; символов `h3.`, `#`, `*`, `{{` не видно
|
||||
|
||||
#### Scenario: Переключение на исходник
|
||||
- **WHEN** в карточке AURORA-1620 у описания нажато «исходник»
|
||||
- **THEN** описание показано исходником с переносами строк (видны `h3. Контекст` и `# Загрузить ОСВ`), кнопка — «текст»;
|
||||
комментарии остаются отрендеренными; после «текст» описание снова отрендерено
|
||||
|
||||
#### Scenario: Исходник у одного комментария
|
||||
- **WHEN** у второго комментария нажато «исходник», затем открыта связанная задача и возврат «назад»
|
||||
- **THEN** сразу после нажатия исходником показан только второй комментарий; после возврата все комментарии
|
||||
отрендерены
|
||||
|
||||
#### Scenario: Ключ задачи в тексте
|
||||
- **WHEN** комментарий — `П. 1 ведётся в AURORA-1445; AURORA-1645 снята как дубль.`
|
||||
- **THEN** «AURORA-1445» и «AURORA-1645» — ссылки; щелчок по первой меняет содержимое окна на AURORA-1445
|
||||
|
||||
#### Scenario: Неизвестная разметка и опасный адрес
|
||||
- **WHEN** описание — `{color:red}важно{color}`, `!схема.png!` и `[нажми|javascript:alert(1)]`
|
||||
- **THEN** все три показаны текстом исходника как есть, ссылки и картинки нет, скрипт не выполняется
|
||||
|
||||
#### Scenario: Списки с отступом и пустыми строками
|
||||
- **WHEN** описание — `Шаги воспроизведения:`, ` * Загрузить ОСВ.`, пустая строка, ` * Дождаться обработки.`
|
||||
- **THEN** абзац «Шаги воспроизведения:» и маркированные пункты «Загрузить ОСВ.» и «Дождаться обработки.» без
|
||||
звёздочек
|
||||
|
||||
#### Scenario: Обычный текст не портится
|
||||
- **WHEN** описание — `Поле data_type = null, дата 2026-10-15, оборот 18 803 903,65 - 9 351 102,89, a*b*c, UTF-8,
|
||||
**важно**`
|
||||
- **THEN** текст показан как есть, без курсива, зачёркивания, жирного и ссылок
|
||||
Loading…
x
Reference in New Issue
Block a user