v0.6.22 — useHistoryStack hook extracted from ComposeBoard.tsx
v0.6.22 — useHistoryStack hook extracted from ComposeBoard.tsx
The first slice of the long-deferred "ComposeBoard hooks/state 抽离"
backlog item. The undo/redo stack is the most self-contained piece
of ComposeBoard state — it only reads state (passed in), writes
to setState / setSelectedId / announce (all passed in), and
consumes the pure applyEntry / invertEntry functions that
already live in lib/compose-history.ts. So it's the lowest-risk
extraction: the behaviour is unchanged, the public surface is
mechanical, and any regression is caught by a new dedicated test
suite.
What got extracted
- The
{ past, future }state. - The
commitcallback (apply + push entry + announce label). - The
undo/redocallbacks (apply inverted / forward entry,
push onto the other stack, announce). - The "coalesce arrow-key moves" logic from
moveBlock— the
single thing the lib version didn't know about. - The "clear history on wholesale canvas replacement" path
(load board / import JSON / reset canvas).
New hook surface
const { history, commit, pushEntry, pushOrMergeMoveEntry,
clearHistory, undo, redo, canUndo, canRedo }
= useHistoryStack({ state, setState, setSelectedId, announce, t });The 5 entry-point methods map to 5 distinct use cases:
| Method | Used by | Why a separate method |
|---|---|---|
commit |
20+ callbacks (connection edits, addBlock, connect, disconnect, ...) | Standard "I have a before/after transition to record" path. |
pushEntry |
endBlockDrag |
State already mutated; recording only the final delta. |
pushOrMergeMoveEntry |
moveBlock (arrow-key handler) |
Holding an arrow key fires many moveBlock calls; merge into ONE undo step. |
clearHistory |
loadBoardFromServer, importJson, resetCanvas |
Wholesale canvas replacement. |
undo / redo |
keyboard handler, toolbar buttons | Apply inverted / forward entry, push onto the other stack, announce. |
Files touched (8)
| Area | Files |
|---|---|
| New hook | web/src/app/compose/use-history-stack.ts (209 lines, well-commented) |
| Refactored | web/src/app/compose/ComposeBoard.tsx (-40 lines net) |
| Tests | web/tests/use-history-stack.test.tsx (12 new tests, 516 lines) |
| Docs | CHANGELOG.md, AGENTS.md |
| Versions | package.json, web/package.json (both → 0.6.22) |
File size
ComposeBoard.tsx: 2184 → 2144 lines (-40). The drop is
smaller than the extracted code because theuseHistoryStack
call site + explanatory comments take ~30 lines. The
cognitive drop is bigger — the commit / undo / redo
triplet is now testable in isolation.
Backlog after v0.6.22
v0.6.23+— MoreComposeBoard.tsxextractions: drag/drop,
server persistence, keyboard shortcuts, view state. The
pattern established here (custom hook that owns a slice of
state and exposes a small public surface) should make the
next 4-5 extractions mechanical, but each is its own release.- block-center avoidance for orthogonal routes (real A* grid
router) — still the v0.6.20 followup, never started - per-direction palette — still the v0.6.19 followup, never started