feat(editor): кнопки код-блока оверлеем + селектор языка по наведению #278
Reference in New Issue
Block a user
Delete Branch "feat/275-codeblock-buttons"
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
closes #275. Панель управления код-блока (селектор языка + копирование) больше не занимает отдельную строку — обе кнопки ушли абсолютным оверлеем в правый верхний угол блока, а селектор языка скрыт до наведения (появляется при hover/focus/открытом дропдауне, плавно). Кнопка копирования видна всегда. В read-only селектор языка не рендерится вообще.
<pre>(редактируемый contentDOM) остаётся ПЕРВЫМ в DOM — регресс #146 (каретка съезжает на строку выше) не воспроизводится; панель уходит из потока черезposition:absolute. Скрытый селектор держит ширину (opacity:0), поэтому кнопка копирования не прыгает;pointer-events:noneпропускает клик к коду. 3 файла (компонент + 2 css), комментарии про #146/order:-1 обновлены.How verified
tsc --noEmit0;eslintкомпонента 0 (из apps/client). Тест-файлов у код-блока нет.Checklist
Ревью
5280392fc(closes #275) — контролы код-блока оверлеем, селектор языка по наведению. 3 файла (компонент + 2 css), фан-аут с фокусом на регресс #146.Вердикт: PASS. Регресс #146 (каретка на строку выше) НЕ может вернуться, вёрстка согласована. Готово к мержу.
Что проверено
<pre>(contentDOM) по-прежнему ПЕРВЫЙ в DOM, а панель ушла из потока вposition:absolute(top-right). Переход с in-floworder:-1наabsoluteстрого БЕЗОПАСНЕЕ прежнего: out-of-flow элемент не участвует в box-модели contentDOM и не может сместить hit-testing каретки. Каретка-вверх-на-строку структурно невозможна..menuGroup(absolute; top/right:8px; z-index:1) привязан к.codeBlock(NodeViewWrapper), которому в code.css добавленposition:relative— ближайший позиционированный предок именно обёртка блока..languageSelectдержит ширину (тогглится толькоopacity) → кнопка копирования не прыгает;pointer-events:noneв скрытом состоянии пропускает клик к коду; копирование видно всегда (класс только на root Select'а); dropdown Mantine портален (оверлей/z-index не обрезает);@media printпанель по-прежнему скрывает.{editor.isEditable && <Select/>}), было — рендерился disabled. Осознанное изменение по #275 (у опубликованной страницы только копирование).Объективные проверки (запущены в окружении ревью)
tsc --noEmit(apps/client) — exit 0.eslintв окружении не поднялся (конфиг клиента не резолвит@tanstack/eslint-plugin-query— проблема окружения) → базис eslint = прогон кодера + вычитка. Тестов у код-блока нет.Полный 9-аспектный фан-аут (по запросу vvzvlad), поверх round-1 approve. Отдельный субагент на каждый аспект.
Вердикт: меняю на CHANGES. Инженерно всё подтверждено (см. ниже), и #146 структурно не воспроизводится — но полный проход нашёл два реальных, дешёвых пункта, которых round-1 не поднял. Оба low. Отвечай по id.
Что сделать
F1 [test coverage] Ветку read-only (селектор языка не рендерится) стоит закрыть тестом —
code-block-view.tsx:79({editor.isEditable && <Select/>})Раньше read-only показывал disabled-
<Select>, теперь — не рендерит вовсе. Это render-условие, а не CSS, и оно НЕ покрыто. Готовый jsdom-харнесс уже есть —code-block/footnote-views.structure.test.tsxрендеритCodeBlockViewс полным набором стабов. ~15 строк: замокатьSelectна детектируемый узел, отрендерить дважды (isEditable:true→ селектор есть;false→ селектора нет, copy остаётся). Остаточный риск невысок (в read-only редактор всё равно отвергает мутации), но условие тривиально регрессируемо, а тест дёшев.Fix: добавить
code-block-view.test.tsxпо образцу существующего харнесса (не трогая егоSelect:()=>nullмок).F2 [simplification] Мёртвый проп
justify="flex-end"—code-block-view.tsx:71После перехода
.menuGroupнаposition:absolute(без width, толькоright:8px) Group шринк-рэпается по контенту у правого края — распределятьjustifyнечего, проп ничего не делает. Убрать.Подтверждено чистым (по 9 аспектам)
<pre>(contentDOM) остаётся ПЕРВЫМ в DOM, панель ушла вabsolute(out-of-flow) — строго безопаснее прежнегоorder:-1; каретка-вверх структурно невозможна.useCopy, нет innerHTML,:global(.codeBlock)не течёт,isEditable-гейт — усиление), stability (условный рендер без краша, CopyButton не перемонтируется), documentation (все #146-комментарии переписаны, staleorder:-1не осталось), conventions (оверлей зеркалитhtml-embed-viewпаттерн,:global()/classNamesидиоматичны), architecture (оверлей/anchor структурно верны) — LGTM.Ниже порога (кодеру НЕ обязательно)
pointer-events:noneмешает), клавиатурой — да (присуще hover-reveal дизайну #275);gap:4pxв css — идиоматичнее пропомgap={4}на Group (стиль).Объективные проверки round-1 в силе:
tsc0,vitestLogViewer n/a (у код-блока нет тестов), eslint в окружении не поднялся.F1: fixed — добавил
code-block-view.test.tsxпо образцу существующего харнесса (он реально вfootnote/footnote-views.structure.test.tsx, не вcode-block/— путь в ревью был чуть неточен). Тот же набор стабов;Selectзамокан наdata-testid="language-select",ActionIcon—copy-button. Два кейса:isEditable:true→ селектор есть + copy есть;isEditable:false→ селектора нет, copy остаётся. Существующий харнесс не трогал.F2: fixed — убрал мёртвый
justify="flex-end"у<Group>(послеposition:absoluteбез ширины Group шринк-рэпается, distribute нечего).Проверки (из apps/client):
tsc0;eslintcode-block 0;vitestcode-block — 2 passed. Возвращаю review/needs.Ре-ревью
ba87f4ee2— раунд закрытия F1+F2. Полный 9-аспектный веер (отдельный субагент на каждый) на дельте.Вердикт: PASS. Обе находки закрыты. Готово к мержу.
Проверка
code-block-view.test.tsxрендерит РЕАЛЬНЫЙCodeBlockView(замоканы только зависимости — tiptap/mantine/copy-button/icons/mermaid, по образцу существующегоfootnote-views.structure.test.tsx;Select→детектируемый testid): (editable)language-selectесть + copy есть; (read-only)language-selectНЕТ + copy есть. Убери гард{editor.isEditable && <Select/>}→ read-only-ассерт падает. ВеткаisEditableреально решается в самом компоненте.justify="flex-end"снят с<Group>; изменение визуально-нейтрально (Groupposition:absoluteтолько сright:8px→ шринк-рэп по контенту, распределятьjustifyнечего). Больше в code-block-view.tsx ничего не тронуто.<pre>первый в DOM, панель absolute — не задето). security/stability/regressions/conventions/documentation/architecture/coherence — LGTM.Объективная проверка:
vitest run code-block-view.test.tsx— 2 passed (в окружении ревью).Ниже порога (опц.): тач-раскрытие селектора и copy-оверлей над углом кода остаются как отмечено ранее (присуще дизайну #275).
Маркер
reviewed_head—ba87f4ee2.