Skip to content

feat(textbox): add rotation support for textbox rendering - #862

Draft
zachelnet wants to merge 6 commits into
mayocream:mainfrom
zachelnet:feat/textbox-rotation
Draft

feat(textbox): add rotation support for textbox rendering#862
zachelnet wants to merge 6 commits into
mayocream:mainfrom
zachelnet:feat/textbox-rotation

Conversation

@zachelnet

@zachelnet zachelnet commented Jul 12, 2026

Copy link
Copy Markdown

Summary

This PR adds rotation support for textboxes in the rendering pipeline.

What changed

  • Added rotation handling in textbox data/model.
  • Applied rotation transforms during textbox rendering.
  • Adjusted positioning/alignment behavior for rotated textboxes.
  • Preserved existing rendering behavior when rotation is not set.

User-visible behavior

  • Textboxes can now be rendered with rotation.
  • Textboxes without a rotation value render the same as before (no behavior change).

Validation

  • Verified textboxes without rotation still render as before.
  • Tested rotated textboxes across different angles.
  • Checked positioning/alignment behavior for rotated textboxes in representative cases.

Demo

Bildschirmaufzeichnung.vom.2026-07-12.14-37-07.mp4

AI Disclosure

AI-assisted (DeepSeek V4).
The final patch was reviewed and validated by the contributor before submission.

Copilot AI review requested due to automatic review settings July 12, 2026 12:40
@github-actions

Copy link
Copy Markdown

Thanks for your first PR to Koharu.

Please review our contribution guide before review:
https://koharu.rs/contribute/introduction/

In the PR description, include:

  • what changed
  • any user-visible behavior differences
  • how you verified the change

If AI helped produce the patch, a human still needs to review and understand it before submission.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds end-to-end rotation support for textboxes so the editor UI, canvas overlays, and Rust render pipeline can all represent and render rotated text consistently.

Changes:

  • Adds a rotation control to the render controls panel (with localized label strings).
  • Updates canvas text block overlays to rotate the selection box, keep resize behavior intuitive under rotation, and rotate resize cursors.
  • Applies rotation during final sprite composition in the Rust renderer via a custom rotate+resample path.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
ui/public/locales/zh-TW/translation.json Adds render.rotationLabel localization.
ui/public/locales/zh-CN/translation.json Adds render.rotationLabel localization.
ui/public/locales/tr-TR/translation.json Adds render.rotationLabel localization.
ui/public/locales/ru-RU/translation.json Adds render.rotationLabel localization.
ui/public/locales/pt-BR/translation.json Adds render.rotationLabel localization.
ui/public/locales/ko-KR/translation.json Adds render.rotationLabel localization.
ui/public/locales/ja-JP/translation.json Adds render.rotationLabel localization.
ui/public/locales/es-ES/translation.json Adds render.rotationLabel localization.
ui/public/locales/en-US/translation.json Adds render.rotationLabel localization.
ui/components/panels/RenderControlsPanel.tsx Introduces rotation UI and writes transform.rotationDeg updates to the selected text node.
ui/components/Panels.tsx Adjusts tab panel sizing/scroll behavior (likely to accommodate additional controls).
ui/components/canvas/TextBlockLayer.tsx Rotates selection overlay/sprites, updates resize math for rotated boxes, and rotates resize cursors.
crates/koharu-app/src/renderer.rs Rotates text sprites during composition using a custom rotate+bilinear resample implementation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/koharu-app/src/renderer.rs Outdated
Comment thread crates/koharu-app/src/renderer.rs Outdated
Comment thread ui/components/panels/RenderControlsPanel.tsx Outdated
Comment thread ui/components/panels/RenderControlsPanel.tsx Outdated
Copilot AI review requested due to automatic review settings July 12, 2026 12:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.

Comment thread crates/koharu-app/src/renderer.rs
Comment thread crates/koharu-app/src/renderer.rs Outdated
Comment thread ui/components/panels/RenderControlsPanel.tsx
Copilot AI review requested due to automatic review settings July 12, 2026 13:00
@zachelnet
zachelnet force-pushed the feat/textbox-rotation branch from 4f3a747 to ed2ba7e Compare July 12, 2026 13:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.

Comment thread ui/components/canvas/TextBlockLayer.tsx Outdated
Comment thread ui/components/panels/RenderControlsPanel.tsx
Comment thread crates/koharu-app/src/renderer.rs
Copilot AI review requested due to automatic review settings July 12, 2026 13:09
@zachelnet
zachelnet force-pushed the feat/textbox-rotation branch from ed2ba7e to d01b760 Compare July 12, 2026 13:09

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 5 comments.

Comment thread ui/components/canvas/TextBlockLayer.tsx
Comment thread ui/components/panels/RenderControlsPanel.tsx
Comment thread ui/components/panels/RenderControlsPanel.tsx Outdated
Comment thread crates/koharu-app/src/renderer.rs
Comment thread crates/koharu-app/src/renderer.rs
@zachelnet
zachelnet force-pushed the feat/textbox-rotation branch from d01b760 to 6119ea2 Compare July 12, 2026 13:28
Copilot AI review requested due to automatic review settings July 12, 2026 13:42
@zachelnet
zachelnet force-pushed the feat/textbox-rotation branch from 6119ea2 to 6145919 Compare July 12, 2026 13:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 5 comments.

Comment thread ui/components/panels/RenderControlsPanel.tsx Outdated
Comment thread ui/components/Panels.tsx
Comment thread ui/components/canvas/TextBlockLayer.tsx Outdated
Comment thread crates/koharu-app/src/renderer.rs
Comment thread crates/koharu-app/src/renderer.rs
Copilot AI review requested due to automatic review settings July 15, 2026 21:46
@zachelnet
zachelnet force-pushed the feat/textbox-rotation branch from 6145919 to da92bd6 Compare July 15, 2026 21:46
Copilot AI review requested due to automatic review settings July 15, 2026 22:40
@zachelnet
zachelnet marked this pull request as draft July 15, 2026 22:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.

@zachelnet
zachelnet force-pushed the feat/textbox-rotation branch from 7dc3e92 to d726ab7 Compare July 15, 2026 22:52
@zachelnet
zachelnet marked this pull request as ready for review July 15, 2026 22:54
Copilot AI review requested due to automatic review settings July 15, 2026 22:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.

Comment thread ui/components/panels/RenderControlsPanel.tsx
Comment thread crates/koharu-app/src/renderer.rs
Copilot AI review requested due to automatic review settings July 16, 2026 05:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 4 comments.

Comment thread ui/components/canvas/TextBlockLayer.tsx
Comment thread crates/koharu-app/src/renderer.rs
Comment thread crates/koharu-app/src/renderer.rs
Comment thread crates/koharu-app/src/renderer.rs Outdated
Copilot AI review requested due to automatic review settings July 16, 2026 05:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment thread ui/components/canvas/TextBlockLayer.tsx Outdated
Copilot AI review requested due to automatic review settings July 16, 2026 05:54
@zachelnet
zachelnet force-pushed the feat/textbox-rotation branch from b62714a to de4dc6d Compare July 16, 2026 05:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment thread ui/components/canvas/TextBlockLayer.tsx
Add rotation controls and rendering for text blocks across the full
stack — Rust renderer, React canvas overlay, and settings panel.

Renderer (koharu-app):
- overlay_sprite_with_rotation: CSS top-left rotation model with
  bilinear resampling; 90/180/270° integer fast paths
- sample_bilinear_rgba: premultiplied-alpha interpolation with float
  precision to avoid edge halos
- sprite_collides_with_bubble_mask: rotation-aware collision check
  using rotated sprite matching render overlay
- centred_sprite_transform: keeps rotated text visually centered

UI (TextBlockLayer):
- Rotated resize with local-coordinate projection
- Rotated resize cursors via ROTATE_CURSOR_MAP (45° steps)
- BlockSprite overlay matches renderer (center rotation origin)

UI (RenderControlsPanel):
- Range slider + number input + ± buttons
- Draft-state pattern for smooth keyboard/slider interaction
- Batch rotation across multi-selected nodes via ops.batch
- Escape clears draft; commits on pointerUp/blur/keyUp

Panels: max-h-60 constraint on layout tab.
i18n: rotationLabel added to all 9 locales.
Tests: 5 Rust rotation tests + 1 UI rotation test.

Co-authored-by: DeepSeek V4 <deepseek@v4.ai>
@zachelnet
zachelnet force-pushed the feat/textbox-rotation branch from de4dc6d to 2d0a7f4 Compare July 16, 2026 06:59
Copilot AI review requested due to automatic review settings July 16, 2026 06:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.

Comment thread ui/components/panels/RenderControlsPanel.tsx
Comment thread crates/koharu-app/src/renderer.rs Outdated
zachelnet and others added 2 commits July 16, 2026 10:36
- updateSelectedRotation now sets lockLayoutBox:true so manual
  rotation survives subsequent layout passes (matches drag/resize).
- rotate_sprite_expand_top_left: use rem_euclid(4) for quadrant
  selection to avoid panic when normalized is near 360°
  (e.g. 359.9996° rounds to 360, hitting unreachable!()).

Co-authored-by: DeepSeek V4 <deepseek@v4.ai>
The center-based BlockSprite positioning (transformOrigin: center /
translate(-50%, -50%)) caused the sprite overlay to drift relative to
the text block when zooming. The translate(-50%, -50%) depends on the
element's rendered dimensions which can briefly change during re-render.

Revert to the original top-left model with rotate() as an addition.
The renderer still centers rotated sprites correctly; the UI overlay
is a preview where minor rotation offset is acceptable.

Co-authored-by: DeepSeek V4 <deepseek@v4.ai>
@zachelnet
zachelnet marked this pull request as draft July 16, 2026 19:52
zachelnet and others added 3 commits July 17, 2026 17:58
Co-authored-by: DeepSeek V4 <deepseek@v4.ai>
When a user manually rotates a text block via the UI, the rotation is
stored in Transform.rotation_deg, not TextData.rotation_deg. The PSD
export previously only read TextData.rotation_deg, falling back to the
AI-predicted angle, which meant user rotations were silently lost on
PSD export.

Now falls back to Transform.rotation_deg when TextData.rotation_deg is
None.

Co-authored-by: DeepSeek V4 <deepseek@v4.ai>
- Remove excessive doc comments (normalize_rotation, ROTATE_CURSOR_MAP,
  sample_bilinear_rgba, etc.) — code is self-documenting.
- Rename rotate_point_top_left → rotate_point (rotates around origin,
  not specifically a corner).
- Fix dead clamp(0.0, 255.0) in sample_bilinear_rgba (alpha already in
  0..1 range after /255.0 normalization).
- Improve 180° overlay test from weak any(alpha != 0) to precise pixel
  position assertion.

Co-authored-by: DeepSeek V4 <deepseek@v4.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants