Skip to content

Snippet {cursor-position} placeholder is removed but caret stays at end after paste #445

@allgemeiner-intellekt

Description

@allgemeiner-intellekt

Summary

The Snippet editor exposes a Cursor Position dynamic value ({cursor-position}), but using it does not move the caret after the snippet is pasted or expanded. The placeholder is removed from the inserted text, yet the caret remains at the end of the pasted snippet.

Reproduction

  1. Create a snippet containing {cursor-position} in the middle of the text, for example:

    Hello {cursor-position}world
    
  2. Paste or expand the snippet into a text field.

  3. Test targets observed:

    • Obsidian editor
    • Browser input fields
    • SuperCmd's own input fields

Expected behavior

The inserted text should be:

Hello world

and the caret should land between Hello and world, where {cursor-position} was placed.

Actual behavior

The inserted text becomes:

Hello world

but the caret remains at the end of the inserted text:

Hello world|

So the placeholder is removed correctly, but it does not control the final caret position.

Technical notes

From a quick code inspection, this appears to be a partial implementation rather than a target-app-specific failure.

  • The Snippet UI exposes {cursor-position} as a dynamic value in SnippetManager.tsx.
  • resolveSnippetPlaceholders() handles cursor-position by returning an empty string.
  • renderSnippetById() returns only the final text string, so the caret target index is lost during rendering.
  • snippet-paste / snippet-paste-resolved then pass only that text to pasteTextToActiveApp().
  • pasteTextToActiveApp() writes the text to the clipboard and simulates paste (Cmd+V / native CGEvent path), which naturally leaves the caret at the end of the pasted text.
  • The keyword auto-expansion path similarly renders the snippet to plain text, deletes the keyword, and pastes the resolved text without any post-paste caret adjustment.

In other words, {cursor-position} is currently treated as a zero-width text placeholder, not as metadata that survives rendering and drives caret placement after insertion.

Possible fix direction

The snippet renderer likely needs to return structured data, e.g.:

{
  text: string,
  cursorOffset?: number
}

Then paste/expand paths can:

  1. Paste the resolved text.
  2. If cursorOffset is present, move the caret to that offset.

Potential implementation options:

  • For SuperCmd/Electron-owned inputs: use DOM selection APIs such as setSelectionRange() or contenteditable selection handling.
  • For external macOS apps: attempt AXSelectedTextRange via Accessibility after paste.
  • As a fallback for plain text fields: send left-arrow key events for text.length - cursorOffset positions after paste, though this may be less reliable for multi-line text, IME state, or apps with custom editors.

Environment

Observed in local testing on macOS with current SuperCmd source. The issue appears independent of a specific target app because the current snippet rendering path discards the cursor position before paste.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions