Skip to content

[#205] Implement speech bubble, narration, and SFX overlay tools#218

Merged
realproject7 merged 2 commits into
mainfrom
task/205-overlay-tools
May 27, 2026
Merged

[#205] Implement speech bubble, narration, and SFX overlay tools#218
realproject7 merged 2 commits into
mainfrom
task/205-overlay-tools

Conversation

@realproject7
Copy link
Copy Markdown
Owner

Summary

  • Add toolbar buttons to create speech, narration, and SFX overlays
  • Drag overlays to move, corner handle to resize (clamped to image bounds)
  • Inspector panel: editable text/speaker fields, tail anchor adjustment, position readout
  • Delete with double-click confirmation
  • Extend Overlay model with tailAnchor for speech bubble tails (default {x: 0.5, y: 1.2})
  • Overlays persist via existing Save → PUT cuts.json flow
  • 6 new tests: add overlay, edit text, delete with confirmation, save callback, tailAnchor defaults, JSON roundtrip
  • 138 total tests pass

Test plan

  • npm run typecheck passes
  • npm run lint passes (no new errors)
  • npm run test passes (138 tests)
  • Add speech/narration/SFX overlays via toolbar
  • Edit overlay text and speaker in inspector
  • Drag overlay to move, resize via corner handle
  • Delete with confirmation (click twice)
  • Adjust bubble tail anchor for speech overlays
  • Save persists overlays, reload shows them
  • Fiction editor and terminal unaffected

Closes #205

🤖 Generated with Claude Code

Add full overlay CRUD to LetteringEditor: toolbar buttons to create
speech/narration/SFX overlays, drag to move, corner handle to
resize, editable text/speaker fields in inspector, delete with
confirmation, and bubble tail anchor adjustment for speech overlays.

Extend Overlay model with tailAnchor field for speech bubble tails.
Overlays persist via existing onSave → PUT cuts.json flow.

Tests: add overlay via toolbar, edit text via inspector, delete
with double-click confirmation, save callback verification,
tailAnchor defaults and JSON roundtrip. 138 total tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@realproject7
Copy link
Copy Markdown
Owner Author

@re2 verdict: APPROVE

Reviewed:

Overlay model (overlays.ts):

  • tailAnchor?: { x: number; y: number } — optional, only set for speech. Default { x: 0.5, y: 1.2 } (centered below bubble). JSON roundtrip test confirms persistence.

CRUD operations:

  • addOverlay — random offset (0.1-0.4) prevents stacking, auto-selects new overlay.
  • updateOverlay — partial update via spread, clean and correct.
  • deleteOverlay — two-click confirmation via confirmDelete state, resets on deselect and re-select. Good UX safety.

Drag/resize:

  • dragRef stores mode, start position, and original bounds — correct ref pattern for mouse tracking.
  • Move clamping: clamp(origX + dx, 0, 1 - origW) — prevents overlay from leaving image bounds.
  • Resize clamping: clamp(origW + dx, MIN_SIZE, 1 - origX) — minimum 5% size, can't extend past image edge.
  • Delta computed in normalized coords via toNorm — scale-independent. Listeners on window handle edge-leaving. Cleanup in useEffect return.
  • pointer-events-none on text label, select-none on container, cursor-move / cursor-se-resize — all correct interaction details.

Inspector:

  • Speaker: editable input (speech only) ✓
  • Text: editable textarea ✓
  • Tail anchor: number inputs with step 0.1, parseFloat || 0 fallback ✓
  • Position readout: x/y/w/h to 3 decimal places ✓
  • Delete button with confirmation state ✓

Tests (6 new): Add overlay, edit text, delete with confirmation, save callback, tailAnchor defaults, JSON roundtrip. Existing tests updated for new data-testid selectors.

No issues found.

Copy link
Copy Markdown
Collaborator

@project7-interns project7-interns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: REQUEST CHANGES

Summary

The overlay CRUD foundation is mostly in place and CI is passing, but speech overlays that were saved before tailAnchor existed cannot use the new tail-anchor adjustment UI. That leaves the tail feature unavailable for valid existing speech overlay data.

Findings

  • [medium] Existing speech overlays without tailAnchor do not get tail-anchor controls.
    • File: app/web/components/LetteringEditor.tsx:295
    • Details: The inspector renders tail controls only when selectedOverlay.tailAnchor is already present. Since tailAnchor is optional in the model and older/minimal speech overlays can have type: "speech" without this field, selecting those overlays will not show any tail controls and saving will not add the default tail anchor. This undercuts the #205 acceptance around speech bubble tail adjustment/persistence for existing overlay data.
    • Suggestion: Gate the controls on selectedOverlay.type === "speech", derive a default like { x: 0.5, y: 1.2 } when the field is missing, and have edits persist that value. Please add a regression test with a preexisting speech overlay that lacks tailAnchor, verifies the tail controls appear with defaults, edits one coordinate, and saves the resulting tailAnchor.

Decision

Requesting changes because the new tail-anchor feature should work for both newly created speech overlays and existing persisted speech overlays. The rest of the implementation is directionally solid, and lint-and-typecheck is passing.

Gate tail anchor controls on type === "speech" instead of
tailAnchor existence. Uses default {x: 0.5, y: 1.2} when the
field is missing, so existing speech overlays without tailAnchor
can still adjust and persist the tail. Add regression test.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@realproject7
Copy link
Copy Markdown
Owner Author

@re2 re-review verdict: APPROVE (maintained)

Fix correctly addresses @re1's finding:

  • Tail anchor controls now gate on type === "speech" instead of tailAnchor existence
  • Fallback selectedOverlay.tailAnchor || { x: 0.5, y: 1.2 } ensures default when field missing
  • Regression test: speech overlay without tailAnchor field → controls visible with inspector-tail-x testid

Approval maintained.

Copy link
Copy Markdown
Collaborator

@project7-interns project7-interns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE

Summary

The tail-anchor compatibility issue is fixed: all speech overlays now show tail controls, including existing persisted overlays without a tailAnchor, and edits persist through the normal save flow. CI is passing.

Findings

  • No blocking findings.

Decision

Approving because PR #218 now satisfies #205's overlay CRUD and tail-anchor requirements, keeps changes scoped to the editor model/UI, and lint-and-typecheck passes.

@realproject7 realproject7 merged commit c7ba9c8 into main May 27, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement speech bubble, narration, and SFX overlay tools

2 participants