Friday, November 22, 2024

ios – Definitive way to enable ESC-key-like functionality for Obsidian on iPhone

Has anyone provided a good solution to the lack of an escape key on iPhone yet, for vim mode? I feel like this needs a definitive solution, and I’d be willing to work on it if nobody has, yet.

Not an external keyboard. Just a plugin that gives a good way to enter the escape character, like a special button above the keyboard, or a special character sequence that gets remapped to the ESC key code, etc.

What strategy would be the best solution for implementing this, if writing an Obsidian plugin? Like, the most natural way to work within the confines of iOS, and not a kind of awkward workaround.


From Claude 3.5 Sonnet, to give ideas and try to get the ball rolling:

1. Special Character Sequence Remapping

Listening for a specific character sequence and remapping it to the ESC key.

  1. In your main plugin class, add an event listener for the 'keydown' event:
import { Plugin, EditorView } from 'obsidian';

export default class EscKeyPlugin extends Plugin {
  private lastKeys: string[] = [];
  private escSequence=";;"; // Configurable

  async onload() {
    this.registerDomEvent(document, 'keydown', (event: KeyboardEvent) => {
      this.handleKeyPress(event);
    });
  }

  private handleKeyPress(event: KeyboardEvent) {
    this.lastKeys.push(event.key);
    if (this.lastKeys.length > this.escSequence.length) {
      this.lastKeys.shift();
    }

    if (this.lastKeys.join('') === this.escSequence) {
      this.triggerEsc();
      event.preventDefault();
      this.lastKeys = [];
    }
  }

  private triggerEsc() {
    const activeLeaf = this.app.workspace.activeLeaf;
    if (activeLeaf && activeLeaf.view instanceof EditorView) {
      const cm = activeLeaf.view.editor.cm;
      cm.triggerOnKeyDown(new KeyboardEvent('keydown', { key: 'Escape' }));
    }
  }
}
  • Make the escape sequence configurable in the plugin settings.
  • Consider adding a small delay to distinguish between intentional sequence input and regular typing.

2. Custom Button Overlay

Add a floating button that triggers the ESC key functionality when tapped.

  1. Create the button element and add it to the DOM:
import { Plugin, EditorView } from 'obsidian';

export default class EscKeyPlugin extends Plugin {
  private escButton: HTMLElement;

  async onload() {
    this.escButton = this.createEscButton();
    document.body.appendChild(this.escButton);
  }

  onunload() {
    this.escButton.remove();
  }

  private createEscButton(): HTMLElement {
    const button = document.createElement('button');
    button.setText('ESC');
    button.addClass('esc-button');
    button.style.position = 'fixed';
    button.style.bottom = '20px';
    button.style.right="20px";
    button.style.zIndex = '1000';
    
    button.addEventListener('click', () => {
      this.triggerEsc();
    });

    return button;
  }

  private triggerEsc() {
    // Same implementation as in the previous example
  }
}
  1. Add CSS to style the button:
.esc-button {
  background-color: #202020;
  color: white;
  border: none;
  border-radius: 50%;
  width: 50px;
  height: 50px;
  font-size: 14px;
  box-shadow: 0 2px 5px rgba(0,0,0,0.2);
}
  • Make the button’s position and appearance customizable.
  • Consider adding an option to hide/show the button with a gesture.

3. Gesture Recognition

Implement a custom gesture to trigger the ESC key functionality.

  1. Add a touch event listener to recognize a specific gesture:
import { Plugin, EditorView } from 'obsidian';

export default class EscKeyPlugin extends Plugin {
  private touchStartX: number = 0;
  private touchStartY: number = 0;

  async onload() {
    this.registerDomEvent(document, 'touchstart', (e: TouchEvent) => {
      this.touchStartX = e.touches[0].clientX;
      this.touchStartY = e.touches[0].clientY;
    });

    this.registerDomEvent(document, 'touchend', (e: TouchEvent) => {
      const touchEndX = e.changedTouches[0].clientX;
      const touchEndY = e.changedTouches[0].clientY;
      
      const deltaX = touchEndX - this.touchStartX;
      const deltaY = touchEndY - this.touchStartY;
      
      if (Math.abs(deltaX) < 10 && deltaY < -50) {
        // Swipe up detected
        this.triggerEsc();
      }
    });
  }

  private triggerEsc() {
    // Same implementation as in the previous examples
  }
}
  • Make the gesture customizable (e.g., swipe direction, distance threshold).
  • Consider using a library like Hammer.js for more complex gesture recognition.

Notes:

  1. To “pass the escape key control code” to Obsidian, we’re using CodeMirror’s triggerOnKeyDown method with a simulated KeyboardEvent. This approach works because Obsidian uses CodeMirror as its text editor component.

  2. Always provide user settings to customize the behavior (e.g., escape sequence, button position, gesture type).

  3. Test thoroughly on different iOS versions and device sizes to ensure compatibility.

  4. Consider combining multiple methods (e.g., both a button and a gesture) to provide users with options.

  5. Implement proper cleanup in the onunload method to remove any added elements or event listeners.

Related Articles

Latest Articles