fix(editor): реактивное чтение editor.isEditable в DictationGroup — иконка диктовки больше не залипает серой (#311) #316
Reference in New Issue
Block a user
Delete Branch "fix/311-reactive-editable"
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
На редактируемой странице микрофон диктовки в байлайне залипал серым/
disabledпосле досинка коллаборации — до тех пор, пока что-то постороннее (переключение view↔edit, навигация) случайно не перерисует байлайн.DictationGroupчитал НЕреактивное полеeditor.isEditableпрямо в рендере;editorприходит изpageEditorAtom(стабильная ссылка на объект), поэтомуeditor.setEditable(true)после синка (гейт #218) меняет внутреннее состояние TipTap, не меняя ссылку атома → байлайн не перерендеривается,disabled=trueзастревает.Фикс — читать editable РЕАКТИВНО через
useEditorState(тот же приём, что уже использует тело редактора): микрофон оживает, когда тело становится editable, и гаснет обратно при потере editable. Исходное намерение #218 (нет диктовки до досинка) сохранено — просто стало реактивным.closes #311
How verified
vitest run dictation-group.test.tsx→ 1 passed. Тест флипаетisEditablefalse→true и ассертит переход микрофона disabled→enabled; против дореформенного кода (raw-field read безuseEditorState) — падает (микрофон залипает).tsc --noEmit— чисто поdictation-group; eslint — чисто (компонент + тест).Checklist
Ревью — #316 (editor: реактивное чтение editor.isEditable в DictationGroup, #311), round 1, head
0210faab, base developScope: реальная дельта — ТОЛЬКО
0210faabповерх смердженного #305 (родитель36b35395); 2 файла, 87 строк (dictation-group.tsx +12, НОВЫЙ .test +75).Вердикт: PASS — точечный фикс реактивности верен, объективка зелёная, регрессий нет. Готово к мержу.
Веер (stability, regressions, test-coverage, coherence, conventions). Объективка запущена мной (детач
0210faab): clienttsc --noEmit→ 0;vitest dictation-group→ 1 passed.Подтверждено по коду + прогоны
DictationGroupчитал НЕреактивное мутабельноеeditor.isEditableнапрямую (disabled={!editor.isEditable}) → мик застревал disabled на editable-странице, пока не случался несвязанный ре-рендер. Стало:useEditorState({ editor, selector: (ctx)=>ctx.editor?.isEditable ?? false })— подписка на editor-события → ре-рендер при флипе после collab-sync. Зеркалит реактивное чтение самого body СИМВОЛ-В-СИМВОЛ (page-editor.tsx:362-367, тот же selector +?? false-guard). Инлайн-selector — без stale-closure/лишних ре-рендеров (tiptap сравнивает ВЫХОД selector'а; boolean стабилен);?? falsenull/destroyed-safe.type {Editor}→{Editor, useEditorState}— Editor всё ещё как тип, паттерн уже есть в соседях, линтов нет. Единственный вызыватель (full-editor.tsx:255) подeditor && …-гвардом.dictation-group— ЕДИНСТВЕННАЯ группа fixed-toolbar с прямым.isEditable-чтением; остальные ~25 прямых чтений — ProseMirror NodeViews (другая render-модель через transaction-lifecycle, не тот баг). Фикс полон в своём scope, вне-scope хвостов нет.useEditorStatefaithful (ре-ран ТОЛЬКО черезeditor.on("update")-подписку, не безусловно); против pre-fix (!editor.isEditable, нет подписки) emit не ре-рендерит → финальныйtoBe(false)падает. Fake-editor surface адекватен (следует sibling-harnesscode-block-view.test). Reverse/null-пути ниже DROP-floor для one-liner-фикса.(служебное: коррекция маркера — в прошлом маркере round-1 PASS я опечатался в укороченном sha; ниже полный. Код не менялся, вердикт прежний: PASS.)