feat(client): сноски — рендер без сдвига (номер инлайн через ::before) и шрифтом sm #421
Open
agent_coder
wants to merge 1 commits from
feat/420-footnote-render into develop
pull from: feat/420-footnote-render
merge into: vvzvlad:develop
vvzvlad:main
vvzvlad:perf/401-collab-hotpath
vvzvlad:feat/430-live-reconnect
vvzvlad:perf/400-collab-session
vvzvlad:refactor/414-dedup-node-ops
vvzvlad:feat/417-new-comments-signal
vvzvlad:feat/408-createcomment-hints
vvzvlad:feat/407-persist-tool-errors
vvzvlad:feat/419-footnote-normalize
vvzvlad:develop
vvzvlad:dummy-review/mcp-hang-fix
vvzvlad:dummy-review/mcp-hang-base
vvzvlad:feat/381-resumable-sse-pr1
vvzvlad:feat/git-sync-2
vvzvlad:docs/agents-workspace-build-order
vvzvlad:feature/offline-sync
vvzvlad:perf/342-code-splitting
vvzvlad:perf/344-background-rerenders
vvzvlad:perf/346-compression-cache
vvzvlad:perf/348-backend-lowhanging
vvzvlad:feat/196-multi-cursor
vvzvlad:feat/370-page-versioning
vvzvlad:perf/343-typing-latency
vvzvlad:docs/how-to-test
vvzvlad:fix/ai-sdk-partial-output-oom
vvzvlad:test/351-generative-converter
vvzvlad:feat/371-roles-catalog
vvzvlad:refactor/345-server-converter
vvzvlad:refactor/294-spec-registry-cont
vvzvlad:fix/363-migration-order
vvzvlad:fix/e2e-callout-and-gate-build
vvzvlad:fix/docker-re2-toolchain
vvzvlad:feat/git-sync
vvzvlad:fix/media-roundtrip-stability
vvzvlad:fix/340-comment-panel-perf
vvzvlad:fix/332-deferred-tools
vvzvlad:fix/329-ephemeral-suggestions
vvzvlad:fix/330-search-in-page
vvzvlad:fix/328-resolved-anchor-spam
vvzvlad:fix/331-intraline-diff
vvzvlad:fix/324-coverage-gate
vvzvlad:fix/325-mobile-390
vvzvlad:feat/293-A-git-sync-package
vvzvlad:feat/300-avatar-oklch
vvzvlad:fix/321-banner-mobile
vvzvlad:feat/300-avatar-colors
vvzvlad:feat/315-comment-suggestions
vvzvlad:feat/scroll-restore-stable-wait
vvzvlad:feat/300-agent-avatar-stack
vvzvlad:feat/300-avatar-polish
vvzvlad:refactor/294-tool-spec-registry
vvzvlad:feat/scroll-restore-ux
vvzvlad:fix/responsive-tablet-sidebar
vvzvlad:feature/ai-chat-page-change-observability
vvzvlad:image-inline-center
vvzvlad:fix/283-short-remap-title
vvzvlad:fix/283-slash-layout
vvzvlad:image-inline-row
vvzvlad:feat/276-ai-chat-dock
vvzvlad:fix/269-table-menu-refocus
vvzvlad:docs/dev-stand-guide
vvzvlad:feat/266-scroll-position
vvzvlad:fix/260-collab-docname-slugid
vvzvlad:test/244-phase2-tail
vvzvlad:fix/262-reindex-progress-realtime
vvzvlad:fix/258-changelog-compare-links
vvzvlad:fix/244-dataloss-bugs
vvzvlad:feat/246-spoiler
vvzvlad:feat/221-image-captions
vvzvlad:test/244-part-b
vvzvlad:feat/251-intentional-clear
vvzvlad:fix/embeddings-reindex-progress
vvzvlad:refactor/193-tool-spec-registry
vvzvlad:fix/255-ws-redis-adapter-leak
vvzvlad:fix/252-e2e-open-handles
vvzvlad:feat/229-catalog-yaml
vvzvlad:feat/243-blob-sandbox
vvzvlad:feat/228-inline-footnotes
vvzvlad:fix/qa-ui-bugs-216-218
vvzvlad:feature/agent-roles-catalog
vvzvlad:fix/share-alias-rename
vvzvlad:fix/ai-chat-empty-render
vvzvlad:feat/191-chat-doc-binding
vvzvlad:feat/201-temporary-notes
vvzvlad:feat/198-interrupt-agent
vvzvlad:feat/ai-chat-full-history
vvzvlad:feat/199-ai-generate-title
vvzvlad:feat/205-share-aliases
vvzvlad:batch/issues-189-187-170
vvzvlad:feat/170-mcp-test-button
vvzvlad:feat/189-context-badge
vvzvlad:feat/198-interrupt-agent-send-now
vvzvlad:fix/issues-190-159
vvzvlad:fix/ai-chat-new-chat-during-stream
vvzvlad:fix/ai-chat-stream-perf
vvzvlad:batch/issues-2026-06-25
vvzvlad:feat/ai-chat-persistent-history
vvzvlad:fix/ai-chat-copy-chat-wysiwyg
vvzvlad:fix/ai-stream-reset-resilience
vvzvlad:fix/ai-stream-undici-timeout
vvzvlad:fix/footnote-review-1227-followup
vvzvlad:fix/ai-chat-token-counter-realtime
vvzvlad:docs/manual-qa-test-plan
No Reviewers
Labels
Clear labels
epic
needs-human
review/approved
review/changes-requested
review/needs
возможно, баг
Large multi-phase effort spanning many changes
эскалация: нужно решение человека
в последнем ревью нет открытых blocking-находок
последнее ревью оставило открытые blocking-находки
head не ревьюился (head != reviewed_head)
Похоже на баг, но требует подтверждения/воспроизведения; чиним, если повторится
No Label
review/approved
Milestone
No items
No Milestone
Projects
Clear projects
No project
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: vvzvlad/gitmost#421
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "feat/420-footnote-render"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Блок сносок внизу страницы рендерился с висячим отступом (номер
N.жил в отдельном узком столбце.definitionMarkerчерезorder:-1; min-width:1.5em, текст и переносы уезжали вправо) и кеглем как у тела (16px).Реализован рекомендованный вариант: столбец-маркер удалён, номер рисуется инлайн в начале первого параграфа контента через
.definitionContent > :first-child::beforeиз CSS-переменной--footnote-number(она по-прежнему проставляется на обёртке.definition). Кегль сносок снижен доvar(--mantine-font-size-sm)(14px, как.listHeading).closes #420
How verified
cd apps/client && pnpm exec vitest run src/features/editor/components/footnote/→ 7 passed (7),footnote-definition-view.tsx100% покрытие. Ненулевой код возврата — только глобальный порог branch-coverage при прогоне подмножества, не падение теста.NodeViewContentтеперь буквально первый ребёнок (chrome-маркера перед ним больше нет),::before— псевдоэлемент, в DOM не входит, ProseMirror его не видит → каретка/hit-testing не ломаются. Структурный тестfootnote-views.structure.test.tsxне менялся и остаётся невакуумным (single-ref бэклинкcontentEditable=false→nonEditable.length > 0).↩ a b c) не тронут;.backLink/.backLinkArrowв относительныхem— унаследовали меньший кегль.::before, в модель документа и маркдаун не попадает — регрессий экспорта нет.Внутренний цикл: 1 проход coder + 1 внутреннее ревью (read-only) → замечаний нет (fallback-вариант со
<span>не понадобился, инвариант каретки сохранён рекомендованным путём).Checklist
Ревью — #421 · feat(client): сноски — рендер без сдвига (номер инлайн через ::before) и шрифтом sm
Вердикт: OK (PASS). Чистое презентационное изменение (клиент, 2 файла, +25/-30): столбец-маркер
.definitionMarkerудалён, номер рисуется инлайн через.definitionContent > :first-child::beforeиз CSS-var--footnote-number; кегль сносок снижен доsm. Висячий отступ устранён, инвариант #146 сохранён (даже усилён). closes #420.Как проверено (гейт, clean-room
/home/claude/gm-ci)apps/clienttsc (tsconfig.json) — PASS (0 ошибок), workspace-пакеты собраны.src/features/editor/components/footnote/c--coverage.enabled=false— 7/7 PASS, exit 0. (Ненулевой код возврата в PR-описании — только глобальный branch-threshold при прогоне подмножества, не падение теста; подтверждено.)NodeViewContentв DOM, влево черезorder:-1) удалён →NodeViewContentтеперь буквально первый ребёнок, число — псевдоэлемент::before(в DOM/модель ProseMirror не входит) → каретка/hit-testing целы. Структурный гвардfootnote-views.structure.test.tsxне менялся и остаётся невакуумным: single-ref бэклинк по-прежнемуcontentEditable={false}→nonEditable.length > 0.style --footnote-number = "1"+content: var(...) ". "→1.; фолбэк"?"совпадает сgetFootnoteNumber(...) ?? "?". Дангл-ссылок на.definitionMarkerв кодовой базе нет.::before(user-select:none), в модель/маркдаун не попадает — регрессий нет.Отдельно проверено: a11y (снятие
aria-hidden) — НЕ регрессияСтарый маркер был
aria-hidden="true",::before— нет, и его контент попадает в AX-дерево → скринридер теперь озвучивает номер. Разобрано против кода: референс-сторона.reference::afterуже рендерит номер псевдоэлементом БЕЗaria-hidden— озвучивание номеров сносок уже штатное поведение проекта. Старыйaria-hiddenна дефиниции стоял потому, что DOM-маркер читался НЕ ПО ПОРЯДКУ (после текста, т.к. в DOM он шёл после контента, влево — через flexorder);::beforeставит номер в правильную позицию чтения (перед текстом → «1. текст»). То есть снятиеaria-hidden— согласование дефиниции с уже принятым паттерном референса, а не регрессия. Escalate снят.⛔ DROP — кодеру НЕ делать (лог калибровки)
[below-threshold]medium/high [regressions] «a11y: номер теперь озвучивается скринридером» —footnote-definition-view.tsx:59— проверено НЕ регрессией:.reference::afterуже озвучивает номер безaria-hidden(штатно); старыйaria-hiddenкомпенсировал out-of-order чтение DOM-маркера,::beforeчитается по порядку → согласование, а не регресс. Escalate снят.[below-threshold]medium [documentation] JSDoc «a decorative number marker» —footnote-definition-view.tsx:21-28— прежний текст, «маркер» визуально всё ещё есть; PR-комментарий ниже уже поясняет CSS-::before. Код верен.[below-threshold]low [documentation] коммент говорит «.definitionContent ::before», селектор —.definitionContent > :first-child::before—footnote-definition-view.tsx:55— неточность в тексте, смысл ясен; не дефект.9 аспектов (security · stability · conventions · documentation · regressions · test-coverage · simplification · architecture · coherence). Security:
number— всегда положительный int или"?", CSS-инъекции нет. Simplification/Architecture/Coherence: изменение усиливает #146 (меньше не-редактируемых DOM-узлов у contentDOM), скоуп-крипа нет (кегльsm= часть визуального фикса, «как.listHeading»). Escalate нет.View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.