112 lines
4.2 KiB
Markdown
112 lines
4.2 KiB
Markdown
---
|
|
created: 2026-08-27
|
|
status: done
|
|
requirements:
|
|
- REQ-UI-RESPONSIVE-PLAN-FORMATTING-001
|
|
system_design:
|
|
- ../../specs/ui/system-design/responsive-plan-formatting.md
|
|
legacy_specs: []
|
|
---
|
|
|
|
# Implementation Plan: Stop the Plan Bubble Menu Transaction Loop
|
|
|
|
## Overview
|
|
|
|
Stop the desktop Plan editor from running a continuous React and Tiptap
|
|
transaction loop. Implement one focused TDD work order because the fault and
|
|
its regression boundary are in one component.
|
|
|
|
The trace shows approximately 44,800 React scheduler callbacks in 3.56
|
|
seconds. The DOM stays at 4,862 elements. The source-mapped stack shows a
|
|
Tiptap BubbleMenu option update followed by an editor transaction and another
|
|
PlanBubbleMenu render.
|
|
|
|
## Scope
|
|
|
|
### In scope
|
|
|
|
- Keep the desktop BubbleMenu option identity stable across unchanged renders.
|
|
- Publish a new editor snapshot only when its derived values change.
|
|
- Add a focused regression that models the real BubbleMenu option-update
|
|
transaction.
|
|
- Preserve the desktop selection bubble and the mobile docked formatting
|
|
strip.
|
|
|
|
### Out of scope
|
|
|
|
- Change Plan formatting commands, layout, touch geometry, or viewport
|
|
behavior.
|
|
- Change Tiptap, ProseMirror, task persistence, or WebSocket code.
|
|
- Add performance telemetry or a general React render monitor.
|
|
- Change the public plugin editor contract.
|
|
|
|
## Technical approach
|
|
|
|
### Stable BubbleMenu inputs
|
|
|
|
Define the placement options once in
|
|
`apps/web/components/editors/tiptap/plan-bubble-menu.tsx`. Pass that stable
|
|
object to the desktop BubbleMenu.
|
|
|
|
### Deduplicated editor snapshots
|
|
|
|
Compare the next `EditorSnapshot` with the current snapshot. Return the
|
|
current state when all derived values are equal. Publish the next snapshot
|
|
only after focus, selection, context, or active marks change.
|
|
|
|
### Regression boundary
|
|
|
|
Extend `plan-bubble-menu.test.tsx` with a controlled BubbleMenu fake. The fake
|
|
models Tiptap's option dependency and its metadata transaction. The test fails
|
|
on the current code because the transaction repeats after each render.
|
|
|
|
## Tests
|
|
|
|
| Acceptance criterion | Evidence |
|
|
| --- | --- |
|
|
| `AC-UI-RESPONSIVE-PLAN-FORMATTING-001.1` | The focused Vitest regression proves that the desktop BubbleMenu settles after one option update. Focus, selection, active-mark, and desktop/mobile code-block transitions remain reactive. |
|
|
| `AC-UI-RESPONSIVE-PLAN-FORMATTING-001.2` through `.8` | The existing mobile component tests and `mobile-plan-formatting-toolbar.spec.ts` prove that the docked presentation keeps its current behavior. |
|
|
|
|
## E2E tests
|
|
|
|
This fix changes render-state normalization only. It does not change layout,
|
|
touch behavior, scrolling, navigation, or viewport-dependent interaction.
|
|
|
|
The existing `mobile-plan-formatting-toolbar.spec.ts` remains the mobile
|
|
parity evidence. Run its `mobile-chrome` scenario after the focused unit test.
|
|
|
|
## Work orders
|
|
|
|
- [x] [Task 01: Stop the BubbleMenu transaction loop](task-01-stop-bubble-menu-transaction-loop.md) (done)
|
|
|
|
## Verification results
|
|
|
|
- Red regression: the controlled BubbleMenu fake observed four option-update
|
|
transactions before the fix and failed the settlement assertion.
|
|
- `cd apps/web && pnpm exec vitest run components/editors/tiptap/plan-bubble-menu.test.tsx`
|
|
passed, 13 tests, including active-mark and code-block transaction changes.
|
|
- `cd apps/web && pnpm run typecheck` passed.
|
|
- `cd apps/web && pnpm exec eslint components/editors/tiptap/plan-bubble-menu.tsx
|
|
components/editors/tiptap/plan-bubble-menu.test.tsx` passed.
|
|
- `cd apps/web && pnpm e2e:run --project mobile-chrome
|
|
tests/task/mobile-plan-formatting-toolbar.spec.ts` passed, 1 test.
|
|
- Fresh desktop and mobile PR screenshots were captured, inspected, validated,
|
|
and compressed. The disposable desktop capture spec was removed.
|
|
|
|
## Risks
|
|
|
|
- An incomplete snapshot comparison can leave pressed formatting state stale.
|
|
- An unstable BubbleMenu prop can reintroduce metadata transactions on each
|
|
render.
|
|
- A weak menu mock can hide the Tiptap effect that caused the fault.
|
|
|
|
## Public documentation
|
|
|
|
None. This fix changes no public command, configuration, API, workflow, or
|
|
plugin contract.
|
|
|
|
## Decisions
|
|
|
|
No ADR is required. The fix completes the current component design without a
|
|
new ownership or public contract boundary.
|