Skip to content

feat: arrow-key pan for zoomed images with remappable hotkeys (#9811)#9814

Open
sbatchelder wants to merge 1 commit into
HumanSignal:developfrom
sbatchelder:feat/arrowkey-zoompan-develop
Open

feat: arrow-key pan for zoomed images with remappable hotkeys (#9811)#9814
sbatchelder wants to merge 1 commit into
HumanSignal:developfrom
sbatchelder:feat/arrowkey-zoompan-develop

Conversation

@sbatchelder

Copy link
Copy Markdown
Contributor

This pull request adds configurable keyboard shortcuts for panning a zoomed-in image, so annotators can move around a magnified image with the arrow keys instead of reaching for the mouse. The four directions are registered through the editor Hotkey system, default to the arrow keys, and appear in Account Settings / Hotkeys under a new "Zoomed Image Controls" section where they can be rebound. Making the arrow keys rebindable required a fix to the hotkey recorder, which previously saved arrow presses under the wrong key name. That fix is included and is a prerequisite for the feature to work properly.

User story: As an annotator working on a zoomed-in image, I want to pan with the arrow keys so that I can move around the image without switching between the keyboard and the mouse on every pan.

Closes #9811.

Feature:

  • Added four named editor actions: tool:pan-on-zoom-left / -right / -up / -down — that pan the targeted zoomed image by 10% of the visible canvas per press. Defaults (left / right / up / down) live in keymap.json.
  • Registered them through the editor Hotkey system (hotkeys.addNamed) in ImageView.jsx, and exposed them in Account Settings / Hotkeys under a new "Zoomed Image Controls" section (defaults.js) so users can rebind them.

Prerequisite fix (hotkey recorder):

  • Item.tsx: recording an arrow key now normalizes the browser key name to the editor keymap name (ArrowLeftleft, etc.) so a recorded arrow-key shortcut actually matches the keymap and fires. Without this, rebinding the pan actions to arrow keys silently would not work.
  • useHotkeys.ts: extended the custom-hotkey prefix filter to recognize the new zoomed_image section so its shortcuts are passed through to the editor.

Reason for change

Problem. When an image is zoomed in, there is no keyboard way to move around it. Every pan method (middle-click-drag, shift-click-drag) requires a pointer device. On large images where you zoom in to label small objects, constantly switching between the keyboard (labels/tools) and the mouse (panning) is slow and breaks the labeling flow. This is also an accessibility barrier: annotators who avoid mice/trackpads (repetitive-strain or other reasons) can drive nearly everything else in Label Studio from the keyboard, but not zoomed-image panning. The arrow keys, the natural way to nudge the viewport, do nothing for a zoomed image. See #9811.

Solution. Add four configurable pan actions registered through the editor Hotkey system, so they behave like every other shortcut (visible in settings, rebindable) rather than being a bare key listener. Each pans the current zoomed image by PAN_STEP (10% of the visible canvas). Key behaviors:

  • Conditional registration. syncImagePanHotkey() only registers the pan hotkeys while a valid pan target exists (a mounted, zoomed image), and removes them otherwise, so the arrow keys keep their default behavior (page scroll, etc.) when there is nothing to pan.
  • Target resolution. The last-interacted image (mousedown/wheel) is the pan target; if there is no clear last-interacted image but exactly one mounted image is zoomed, that one is used.
  • Non-interference. Modifier combinations (ctrl / meta / alt / shift) are left alone so they remain available to other actions, and keypresses while an editable/focused input is active are ignored.

Why the recorder fix is included. The pan actions default to the arrow keys, and we want users to be able to rebind them. The Account Settings hotkey recorder captured the browser KeyboardEvent.key name (ArrowLeft) instead of the editor/keymaster name (left) that the keymap uses, so any arrow-key shortcut a user recorded silently never matched at runtime. Normalizing Arrow* to the short names in the recorder is therefore a prerequisite for this feature. Without it, the "rebind the pan keys to arrow keys" flow wouldn't work. The regex change in useHotkeys.ts is the matching plumbing to route the new zoomed_image section's custom hotkeys to the editor.

Screenshots / recording

The recording below shows:

  • The new "Zoomed Image Controls" section in Account Settings / Hotkeys
  • The standard conflict warning against Time Series Controls' "Extend Left" and correct application of "Left" instead of "ArrowLeft"
  • Remapping up-left-down-right from Arrowkey navigation to WASD keys.
  • Zooming and Panning around an image using only the keyboard.
  • One pan key-press corresponds to the visible window in the expected direction, ~10% per press.
2026-07-08.21-36-47.mp4

_

Prior to this feature, trying to bind a hotkey to an arrowkey looked like this.

2026-07-08.21-13-35.mp4

Rollout strategy

No feature flag or environment variable. The pan hotkeys are only registered while a zoomed image is present, so the change is inert for every other configuration and for un-zoomed images; arrow keys behave exactly as before. It ships on by default. It'd be great if this could be included in v1.24.0.

Testing

Acceptance criteria (QA):

  • When a user zooms into an image and presses an arrow key, then the visible window pans 10% or the viewsceen in that direction.
  • When a user is not zoomed in (or no image is present), then the arrow keys retain their default behavior (no pan, no preventDefault).
  • When a user holds a modifier (ctrl/meta/alt/shift) with an arrow key, or is typing in an input, then no pan occurs.
  • When a user opens Account Settings / Hotkeys, then a "Zoomed Image Controls" section shows Pan Left/Right/Up/Down and each can be rebound.
  • When a user records an arrow key for any shortcut, then it is saved as left/right/up/down and fires at runtime.

Automated tests. Extended web/libs/editor/src/components/ImageView/__tests__/ImageView.test.jsx to cover the pan behavior (direction handling, the zoomed-only gate, modifier/editable-target guards, and pan-target resolution).

Manual verification. Verified panning on a zoomed image with single and multiple <Image> tags, confirmed arrow keys fall through to default behavior when not zoomed, and confirmed that recording an arrow key in Account Settings now saves the short name and the rebound shortcut fires. Rebinding Zoomed Image Pan commands to WASD allows for the panning of a zoomed image with WASD, as seen in the video above.

Risks

  • Correctness: low. Pan hotkeys are only active while a zoomed image is mounted, and the handler bails on modifier combos and editable targets, so it won't hijack arrow keys from other contexts.
  • Interference with other tools: the handler yields (no preventDefault) whenever there is no pan target, and the registration is torn down when the target goes away, so TimeSeries and other arrow-key consumers keep the keys when appropriate.
  • Recorder change scope: the normalizeKey change affects all recorded shortcuts, not just pan — but it only maps Arrow* to the names the keymap already uses and lowercases others (previously key.toLowerCase()), so it strictly improves correctness for arrow keys and is a no-op for keys that were already correct.
  • Security: none.

Reviewer notes

  • The feature and the recorder fix are shipped together deliberately: the recorder fix is a prerequisite for rebinding the pan actions to arrow keys (the default), so splitting them would leave the headline flow broken on its own branch. Both are small and scoped.
  • Registration is centralized in syncImagePanHotkey() and driven by mount/interaction, so import order and re-mounts are safe; hotkeys are added and removed as the pan target appears/disappears.
  • Default bindings live in keymap.json; the settings surface (section + per-action labels) lives in defaults.js under the zoomed_image section.

General notes

  • PAN_STEP = 0.1 is the fraction of the visible canvas moved per key press; the direction map matches the existing trackpad-scroll convention in handleZoom (right/down reveal content in that direction).
  • The recorder normalization (ArrowLeft to left) which mirrors the names used throughout the editor keymap, so recorded and default arrowkey bindings now share one vocabulary.

@netlify

netlify Bot commented Jul 9, 2026

Copy link
Copy Markdown

👷 Deploy request for heartex-docs pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit b42f4d8

@netlify

netlify Bot commented Jul 9, 2026

Copy link
Copy Markdown

👷 Deploy request for label-studio-docs-new-theme pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit b42f4d8

@netlify

netlify Bot commented Jul 9, 2026

Copy link
Copy Markdown

Deploy Preview for label-studio-storybook ready!

Name Link
🔨 Latest commit b42f4d8
🔍 Latest deploy log https://app.netlify.com/projects/label-studio-storybook/deploys/6a4f1013da39cf0008e46387
😎 Deploy Preview https://deploy-preview-9814--label-studio-storybook.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Jul 9, 2026

Copy link
Copy Markdown

Deploy Preview for label-studio-playground ready!

Name Link
🔨 Latest commit b42f4d8
🔍 Latest deploy log https://app.netlify.com/projects/label-studio-playground/deploys/6a4f1013bfbfbe00083ba14a
😎 Deploy Preview https://deploy-preview-9814--label-studio-playground.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions github-actions Bot added the feat label Jul 9, 2026
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.

[feature] [accessibility] arrowkey keyboard shortcuts to pan a zoomed-in image

1 participant