Skip to content

Focus the URL field when the Add link form opens on selected text #366

Description

@HMarzban

What happened

Edward Saperia reported this bug. Select text in a pad and click the Hyperlink button in the toolbar. The Add link form opens, but focus does not move to the URL field. The reader must click the field before typing.

What we expect

The caret sits in the URL field as soon as the form opens. The reader can type or paste a URL at once, with no extra click.

Steps to reproduce

  1. Open a pad you can edit.
  2. Select one word in the text.
  3. Click Hyperlink in the toolbar. Its tooltip reads "Hyperlink (⌘+K)".
  4. Type a letter. Expected: the letter appears in the URL field. Reported: it does not.

The report does not name the device. Check desktop first, then a phone.

Acceptance criteria

  • With text selected, a click on the Hyperlink toolbar button opens the Add link form and puts the caret in the URL field. A letter typed right away shows in the field.
  • The same holds with a collapsed caret and no selection.
  • ⌘K, the slash menu link item, and the phone toolbar link button give the same result.
  • On a phone, the result is written on this issue. If iOS or Android blocks the focus, the comment says so.
  • The Edit link form still focuses and selects its URL field.
  • A Cypress check fails without the fix. After the toolbar click, the field hyperlink-editor-url has focus.

Blocked by

None — can start now.

Agent brief

Type: AFK for desktop and the Cypress check. The phone check needs a person with a phone.

Category: bug

Current behavior: Reported by Edward Saperia. We have not reproduced it on a dev build yet. Two code paths try to focus the field. Check both.

  1. The extension opener arms a 100 ms timer in onShow. The timer focuses content.querySelector('input'). The webapp URL field is a <textarea>, so the query finds nothing.
  2. The webapp form focuses the field in a mount effect on desktop. createHyperlinkDesktop returns an empty host, and React portals the form into it. Check whether that effect runs before the popover is attached to the page. A focus() on a detached node does nothing.

The existing webapp create spec never asserts focus. The extension's own clean-room spec asserts it, but only for the extension's built-in form, which uses an <input>.

Desired behavior: Focus lands in the URL field on every open path, on desktop. Phone behavior is recorded, even if the OS blocks it.

Where to start: extensions/extension-hyperlink/src/openers/openCreateHyperlink.ts (onShow, INPUT_FOCUS_DELAY_MS). apps/webapp/src/components/TipTap/hyperlinkPopovers/hooks/useHyperlinkEditorForm.ts (the focus effect). apps/webapp/src/components/TipTap/hyperlinkPopovers/desktopPopoverEntries.ts (createHyperlinkDesktop). apps/webapp/src/components/TipTap/hyperlinkPopovers/HyperlinkPopoverPortal.tsx. apps/webapp/src/components/TipTap/toolbar/desktop/EditorToolbar.tsx (toolbar-hyperlink). apps/webapp/cypress/e2e/editor/hyperlink/hyperlink-create.cy.js. Search by symbol. File names are hints as of 2026-09-29.

Rules that apply: extensions/CLAUDE.md §Webapp-Owned Hyperlink Popovers. The extension stays host-agnostic: the webapp factory returns an HTMLElement and the extension never imports the webapp. extensions/extension-hyperlink/AGENTS.md §Floating Popover. A popover is a one-shot, so build a new one and never reopen a cached one. After any extension source change, run bunx tsup in that package, clear .next, and restart the dev server. AGENTS.md §Test Policy allows this Cypress check because it pins a reported bug. Run it and watch it pass.

Verify: Start make dev-local. Confirm the webapp port first, because it is not always 3000. Open a pad, select a word, click Hyperlink, and type a letter. The letter must appear in the URL field. Then run the create spec with bun run --filter @docs.plus/webapp cypress:run. Add --spec to limit it to hyperlink-create.cy.js. That spec runs on the /editor playground route. Also check a real pad route, because that route mounts the page-level portal.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    EditorTiptap & ProsemirrorUIbugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions