Skip to content

Conversation

@TrevorBurgoyne
Copy link
Member

@TrevorBurgoyne TrevorBurgoyne commented Oct 15, 2025

Fly-to API

Description

  • Add fly-to functions, which sets the zoom and focus to a specific annotation
    • fly_to_next_annotation()
    • fly_to_annotation_id()
    • fly_to_annotation()
  • Add Tab and Tab+Shift default keybinds to fly-to the next/previous annotation, respectively
    • Keybinds are configurable:
      • fly_to_next_annotation_keybind
      • fly_to_previous_annotation_keybind
  • Closes Add flyTo API #122

PR Checklist

  • Merged latest main
  • Version number in package.json has been bumped since last release
  • Version numbers match between package package.json and src/version.js
  • Ran npm install and npm run build AFTER bumping the version number
  • Updated documentation if necessary (currently just in api_spec.md)
  • Added changes to changelog.md

Breaking API Changes

By default, prevents browser default for Tab.

@TrevorBurgoyne TrevorBurgoyne added the enhancement New feature or request label Oct 15, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR introduces a "fly-to" feature that allows users to quickly zoom and focus on specific annotations. The feature adds three new API methods and configurable keyboard shortcuts (defaulting to Tab and Shift+Tab) for navigating between annotations.

Key Changes:

  • New API methods (fly_to_next_annotation, fly_to_annotation_id, fly_to_annotation) for programmatic navigation to annotations
  • Keyboard shortcuts for cycling through annotations with smart handling of deprecated and non-spatial annotations
  • Configuration options for customizing keybindings

Reviewed Changes

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

Show a summary per file
File Description
src/version.js Bumped version to 0.20.0
package.json Updated package version to match version.js
src/subtask.ts Added fly_to_idx state field to track current annotation focus
src/configuration.ts Added configuration properties for fly-to keybindings
src/listeners.ts Implemented keyboard event handling for fly-to navigation
src/index.js Implemented core fly-to functionality with zoom calculation logic
api_spec.md Documented new API methods and keybindings
demo/resume-from.html Added example usage of custom keybind configuration
CHANGELOG.md Documented changes for 0.20.0 release

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@TrevorBurgoyne TrevorBurgoyne mentioned this pull request Oct 16, 2025
6 tasks
Copy link
Collaborator

@joshua-dean joshua-dean left a comment

Choose a reason for hiding this comment

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

Very nice, works well in the default case.
Just one quirk with the configuration, and some ideas.

api_spec.md Outdated
- Press `Escape` or `crtl+z` to cancel the start of a new region or hole.
- Press `Escape` to exit brush/erase mode.
- Press `Tab` to set the zoom to focus on the next annotation
- Press `Tab+Shift` to set the zoom to focus on the previous annotation
Copy link
Collaborator

Choose a reason for hiding this comment

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

Generally this is would be listed as Shift+Tab. See: https://ux.stackexchange.com/a/58188

Copy link
Member Author

Choose a reason for hiding this comment

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

src/listeners.ts Outdated
Comment on lines 239 to 255
case ulabel.config.fly_to_next_annotation_keybind:
// For 'Tab', prevent default
if (keydown_event.key === "Tab") {
keydown_event.preventDefault();
}

if (ulabel.config.fly_to_previous_annotation_keybind === null && shift) {
ulabel.fly_to_next_annotation(-1, ulabel.config.fly_to_max_zoom);
} else if (!shift) {
ulabel.fly_to_next_annotation(1, ulabel.config.fly_to_max_zoom);
}
break;
case ulabel.config.fly_to_previous_annotation_keybind:
if (ulabel.config.fly_to_previous_annotation_keybind !== null) {
ulabel.fly_to_next_annotation(-1, ulabel.config.fly_to_max_zoom);
}
break;
Copy link
Collaborator

Choose a reason for hiding this comment

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

If you set fly_to_next_annotation_keybind to a letter (e.g. "w"), this case won't trigger when holding "Shift" as keydown_event.key will be the capital version of that letter (e.g. "W").

The easy fix is just fold the case on everything for checks like these.

In general, it would be nice to fully support chords (I think that is the right term anyways), e.g. "Ctrl+Shift+Alt+Fn+~", but that is definitely out of scope here. I imagine there's probably a good library that can do that for us.

Copy link
Member Author

Choose a reason for hiding this comment

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

good catch, I really should've tested this. And yeah, handling chords is definitely a way better way to do this, and could be generalized for all the configurable keybinds. A full rework of how we configure keybinds is probably a good idea:

  • handle chords
  • handle duplicate keys
  • in-session configurability
  • a in-session list of current keybinds

Copy link
Member Author

Choose a reason for hiding this comment

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

for now i just check against lowercase: 880ddb5

Comment on lines 443 to 445
### `fly_to_max_zoom`
Maximum zoom factor used when flying-to an annotation. Default is `10`.
Copy link
Collaborator

Choose a reason for hiding this comment

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

My intuition told me that "1" would be no zoom (well, 100% of the original anyways), but upon testing this out, it seems that as you approach "0", that is the "no zoom" setting. However, actually setting it to 0 seems to totally break the view when flying to an annotation, requiring a zoom reset. Actually supporting 0 obviously isn't necessary, but we probably shouldn't break if the user sets it.

We might want to guard against any values <=0 and just do nothing in that case.

Copy link
Member Author

Choose a reason for hiding this comment

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

Added a setter for the zoom val and ensure it stays above 0: e84542b

Comment on lines +5621 to +5623
// Start with the full increment amount
let next_idx = (start_idx + increment + ordering.length) % ordering.length;
const first_checked_idx = next_idx;
Copy link
Collaborator

Choose a reason for hiding this comment

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

A nice QoL for this that I've seen for other "searching" mechanisms would be a toast or similar pop-up indicating once you have looped around. A display of "current index / total" might also be nice.

Thoughts? If it is out of scope, we can make a separate issue.

Copy link
Member Author

Choose a reason for hiding this comment

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

I do like this idea, it fits with a larger idea I have of having an annotation list you can interact with in the toolbox that will also leverage this flyto functionality. I created issue #234 to track

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add flyTo API

3 participants