Skip to content

Nav enhancements#54

Open
clozach wants to merge 4 commits into
roam-unofficial:masterfrom
clozach:nav-enhancements
Open

Nav enhancements#54
clozach wants to merge 4 commits into
roam-unofficial:masterfrom
clozach:nav-enhancements

Conversation

@clozach

@clozach clozach commented May 14, 2020

Copy link
Copy Markdown

As mentioned here, I decided to roll feedback from @Stvad into a new branch with additional navigation improvements in the form of arrow key navigation.

These small tweaks make my own experience in Roam feel much less stilted, and I hope you feel the same after trying them out!

Feedback welcome, esp. regarding…

  • Do you see/experience any bugs here?
  • Is there anything about the code structure, naming, and/or organization you'd like to see changed before considering a merge?

clozach added 4 commits May 13, 2020 16:53
…no regular nodes are currently active OR
…the title is active

Problem: When no nodes are selected, or the title is currently being edited, hitting `Enter` is a no-op.

Solution: Hitting `Enter` in these two cases now puts the cursor in a new node at the top of the page.
Problem: The arrow keys stop doing anything useful when either no nodes are active or the cursor is in the title or top node

Solution:
- When no nodes are active, Up Arrow (↑) activates the title
- From the top node, Up Arrow (↑) activates the title
- From the title, Down Arrow (↓) activates the top node
- When no nodes are active, Up Arrow (↓) activates the top node
When the top node starts with a tag, `activateTopBlock` places the cursor _after_ the tag by default. This tweak corrects that strange behavior.

# Steps to reproduce
I have a page where the first node looks like this:

    #character #father #grandfather #[[Book(0)]]

Before this commit, hitting Enter from within the title would place the cursor here…

    #character↓ #father #grandfather #[[Book(0)]]

…instead of here…

    ↓#character #father #grandfather #[[Book(0)]]
if (isInSearch) return;

switch (event.key) {
case 'Enter':

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

as discussed in previous PR, the Enter is probably redundant

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Not sure what you mean here. In the other PR, I had const enter = 'Enter'. Here there's no intermediary storage, just the raw string. ¯_(ツ)_/¯

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

no, I meant this whole shortcut overall, as the standard cmd-enter exists


browser.runtime.onMessage.addListener(command => dispatchMap.get(command)?.())

const enhanceNavigation = (event: KeyboardEvent) => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this deserves to be it's own "feature" now with the ability to enable/disable it

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fair enough. I'll put it behind a feature toggle.


const enhanceNavigation = (event: KeyboardEvent) => {
const isEditingTitle = event.target?.parentElement instanceof HTMLHeadingElement
const isNoBlockActive = !Roam.getActiveRoamNode()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

notInRoamBlock?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I started out as an iOS dev, where there's a standard naming convention of phrasing boolean names with "is", as in, "Is no block active?" However, I can see that it's just causing confusion here, so I'll avoid it in future, and find something better in this case, should this PR remain open. 🙃

const isEditingTitle = event.target?.parentElement instanceof HTMLHeadingElement
const isNoBlockActive = !Roam.getActiveRoamNode()
const isTopBlockActive = getActiveEditElement() === getFirstTopLevelBlock()
const isInSearch = event.target instanceof HTMLInputElement

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this seems like an unreliable way to check for search. But I suppose you probably don't want to mess with any input element in general. So probably rename variable to reflect that

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good point.

break
case 'ArrowUp':
if (isNoBlockActive || isTopBlockActive) {
if (event.getModifierState("Shift")) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this seems indicative of potentially larger surface area of impact (e.g. this is gonna break alt+up or cmd+up/etc in top block, maybe other things).
Makes me hesitate about overriding things that are so fundamental. How about just adding a shortcut to edit the title?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I take your point, and am considering extending the modifier check to include the full set of modifiers listed here so that it's impossible for there to be a conflict with existing shortcuts.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm still not entirely sure. seems like a rather intimate set of things to override and things can go wrong... that said, I think it can still be ok to include it. assuming we have a way to disable it and do careful testing that we don't introduce unexpected behaviors.
What about just scrolling the page with the keyboard? (I don't use it as I use vim mappings, but seems plausibly like something people use?)

@clozach clozach left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Thanks for the feedback, @Stvad! I hope to have your issues completely addressed in the next day or two.


browser.runtime.onMessage.addListener(command => dispatchMap.get(command)?.())

const enhanceNavigation = (event: KeyboardEvent) => {

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fair enough. I'll put it behind a feature toggle.


const enhanceNavigation = (event: KeyboardEvent) => {
const isEditingTitle = event.target?.parentElement instanceof HTMLHeadingElement
const isNoBlockActive = !Roam.getActiveRoamNode()

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I started out as an iOS dev, where there's a standard naming convention of phrasing boolean names with "is", as in, "Is no block active?" However, I can see that it's just causing confusion here, so I'll avoid it in future, and find something better in this case, should this PR remain open. 🙃

const isEditingTitle = event.target?.parentElement instanceof HTMLHeadingElement
const isNoBlockActive = !Roam.getActiveRoamNode()
const isTopBlockActive = getActiveEditElement() === getFirstTopLevelBlock()
const isInSearch = event.target instanceof HTMLInputElement

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good point.

if (isInSearch) return;

switch (event.key) {
case 'Enter':

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Not sure what you mean here. In the other PR, I had const enter = 'Enter'. Here there's no intermediary storage, just the raw string. ¯_(ツ)_/¯

break
case 'ArrowUp':
if (isNoBlockActive || isTopBlockActive) {
if (event.getModifierState("Shift")) {

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I take your point, and am considering extending the modifier check to include the full set of modifiers listed here so that it's impossible for there to be a conflict with existing shortcuts.

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.

2 participants