GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Hyperlink: correctness, runtime and bundle work from the 2.3.0 review · Issue #769 · docs-plus/docs.plus

Hyperlink: correctness, runtime and bundle work from the 2.3.0 review #769

New issue
Copy link
New issue
Copy link
Open
Open
Hyperlink: correctness, runtime and bundle work from the 2.3.0 review#769
Copy link
Assignees
HMarzban
Labels
Extensiontiptap
Milestone
v2.3.8

Description

@HMarzban
HMarzban
opened on Nov 12, 2026
Issue body actions

Tracking issue for the 2026-11-12 review of @docs.plus/extension-hyperlink 2.3.0: bundle size, runtime cost and correctness.

Summary

Bundlephobia shows 91,188 B min / 35,890 B gzip. That number counts linkifyjs and Floating UI as new code. A StarterKit app really pays 70,854 / 25,213 B, and the docs.plus webapp pays 54,947 / 19,144 B, because both already ship most of the dependencies. The review found one live bug that every pad and chat message can hit, two more bugs, and four measured optimizations. It also rejected several ideas with measured reasons (below).

Order and status

# Issue Kind Depends on Ruling Status
1 #770 Keep the whole link when part of it is formatted bug — — committed c9cd2c98a (not pushed); chat message box check owed (needs sign-in); production check
2 #772 Keep a custom href when autolink runs bug #770 — committed c9cd2c98a (not pushed); Mod-k form flow and production check owed
3 #773 Faster autolink on big pastes and select-all formatting perf #770 — committed c9cd2c98a (not pushed); jsdom −51 % paste, −33 % Bold; foreground browser run, typing profile and production check owed
4 #776 Store the special-URL catalog as typed rows perf — — committed c9cd2c98a (not pushed); −96 B gzip; production glance owed
5 #771 A peer brings back a removed link bug #770 9 waits on the ruling
6 #774 PURE build and its preflight guard build — 4 waits on the ruling
7 #775 HyperlinkBase without the prebuilt popovers feat #774 — waits on #774
8 #777 Load link-icon images only near the screen perf — — measure first
9 #778 Enter inside a link decision #770 2 waits on the ruling

Findings from the work (not filed)

  • On a local 2,000-paragraph pad, select all then Bold is one long task of about 5.2 s (background pane, so inflated). Autolink is about 78 ms of it (1.5 %). The rest is other plugins, rendering and sync. Profile it in a foreground window before filing. Details on Hyperlink: cut autolink cost on big pastes and select-all formatting #773.
  • Text inserted in one chunk autolinks only its last word before the space. This is old behaviour (same on main before Hyperlink: cut autolink cost on big pastes and select-all formatting #773). Decide whether to file it.

Rulings the maintainer owes

Ruling Question Recommendation Issue
2 Enter inside a link: keep today, match upstream, or unlink both halves? Keep today #778
4 How the PURE build reaches two packages under the single shared tsup config One opt-in option in defineTiptapExtensionConfig #774
9 Should a client autolink text that a peer or the server wrote? No: skip remote non-undo transactions; link server writes on the server if wanted #771

Rules for every child issue

  • Bench before and after. extensions/extension-hyperlink/scripts/bench/ (added with Hyperlink: keep the whole link when part of it is formatted #770) compares the working tree with a git ref, plus a control copy that shows the noise. Run NODE_ENV=production bun scripts/bench/run.ts --ref <commit before the change> from the package, and scripts/bench/browser.ts for a real browser. Post the before as a Measured comment before the first commit, and the after on the commit.
  • UX first. A saving that changes what a user sees or does is a regression. Each child lists its UX contract: what must not change, and the check that proves it. Bug fixes compare against upstream @tiptap/extension-link 3.32.2, not against today's buggy code.
  • Prove tests by sabotage. Each new test fails with its fix reverted.
  • Real browser for speed claims. jsdom numbers compare variants only. Quote a speed number only from a foreground browser run, with its condition.
  • Gates. bun run test:unit, bun run typecheck, lint and Prettier on changed files, and the full package Cypress suite (env -u ELECTRON_RUN_AS_NODE bun run test:e2e).
  • Owner. Agent in the repo: code, tests, bench, CHANGELOG. Maintainer: rulings, commits review, release, production checks.

Not doing (measured reasons)

  • Replace linkifyjs: 0 bytes for StarterKit apps, which already ship it through @tiptap/extension-link.
  • Lazy import() of the hovercard engine through editor.registerPlugin: it rebuilds every plugin view and forces a full-doc re-render. A lazy built-in card only (plugin stays eager) is not measured; the webapp always draws its own card, so it gains nothing.
  • Lazy prebuilt popovers or openers: HyperlinkBase (Hyperlink: add HyperlinkBase without the prebuilt popovers #775) gives the webapp these bytes; CJS gains nothing; iOS keyboard focus inside the user gesture is a risk.
  • Subpath entries: 665 B more than the PURE build for helpers; in CJS each entry would get its own popover controller and tooltip bubble.
  • Move isSafeHref into a module without linkifyjs: costs every StarterKit app +52 / +125 B; the PURE build already gives helper-only users about 19 kB.
  • Feature factories for hovercard, pasteTitle and linkIcon: a major version; −5.9 kB gzip only for apps that use none of the three; the webapp gains 0. Revisit at the product 3.0 major.
  • Keystroke fast path: about 16 µs of a 95 µs key in a real browser, +199 B gzip, and it skips work next to links.
  • Remove the built-in hovercard card: ruled to stay (extensions/extension-hyperlink/AGENTS.md).
  • Drop sourcemaps: 555,127 B of install size, 0 B in an app bundle.
  • Trim Floating UI imports or minify the package CSS: every import is used; app pipelines minify CSS.

Settled rules this work keeps

@docs.plus/floating-popover and @docs.plus/floating-tooltip stay bundled; @floating-ui/dom stays external; CJS stays; linkifyjs stays; the hyperlink mark name and the Markdown hooks stay.

Activity

  1. HMarzban
    added this to the v2.3.8 milestone on Nov 12, 2026
  2. HMarzban
    self-assigned this
    on Nov 12, 2026
  3. HMarzban
    added
    tiptap
    Extension
    on Nov 12, 2026
  4. HMarzban commented on Nov 12, 2026

    @HMarzban
    HMarzban
    on Nov 12, 2026
    CollaboratorAuthor
    More actions

    Measured: commit c9cd2c98a against its parent

    The commit holds #770, #772, #773 and #776 together. Package bench from extensions/extension-hyperlink: NODE_ENV=production bun scripts/bench/run.ts --ref c9cd2c98a~1.

    Metric base head Change
    Bytes: npm dist/index.js (min / gz) 57,127 / 20,500 57,013 / 20,861 -114 / +361
    Bytes: a StarterKit app pays (min / gz) 70,854 / 25,213 70,776 / 25,582 -78 / +369
    Bytes: normalizeHref-only import (min / gz) 368,030 / 121,399 367,952 / 121,722 -78 / +323
    Behaviour: all cases 5/18 18/18 +13
    Behaviour: strip 0/10 10/10
    Behaviour: undo 1/2 2/2
    Behaviour: keep 4/4 4/4
    Behaviour: href 0/2 2/2
    Speed: pasteText2000 (ms) 479.661 228.795 -52% · control -1%
    Speed: boldAll2000 (ms) 350.266 227.547 -35% · control +3%
    Speed: typeEnd2000 (µs/key) 152.222 153.656 +1% · control +0%
    Speed: pasteOverSelection250k (ms) 22.605 12.216 -46% · control +3%
    Speed: boldPartOfLink (ms) 0.159 0.126 -21% · control -1% · output differs
    Speed: mount1000Links (ms) 26.997 27.02 +0% · control -2%

    jsdom on Bun 1.4.4, darwin arm64, 9 rotated rounds, 2026-11-12. output differs on boldPartOfLink is the #770 fix itself. A StarterKit app pays −78 B minified but +369 B gzip: #776 saves minified bytes, and the #773 pass adds gzip bytes. No browser number here: a foreground run is still owed on #773.

  5. HMarzban commented on Nov 12, 2026

    @HMarzban
    HMarzban
    on Nov 12, 2026
    CollaboratorAuthor
    More actions

    Workflow results, wf3 and wf4 (2026-11-12, local, read-only)

    Measured on c9cd2c98a against c9cd2c98a~1. Machine: Apple M5 Max, Bun 1.4.4, headless Chrome for Testing 158.0.8127.18. That is build 1254, because Playwright 1.64.1's build 1248 is not installed. Pad checks ran on the local webapp at :3001, a next dev build. A verifier re-ran each number. Status per issue:

    • Hyperlink: cut autolink cost on big pastes and select-all formatting #773: the bench before/after is done. Over 6 wf3 sessions, a paste of 2,000 lines with URLs took 43.58–48.79 % less time. Select-all Bold over 2,000 linked paragraphs took 17.1–24.0 % less. Four wf4 sessions fell inside both ranges. A fixed timer probe removes the false background flag. On a local 2,025-block pad, select-all Bold takes about 4.6–4.7 s, and autolink is 1.5 % of it (final profile below). See the Hyperlink: cut autolink cost on big pastes and select-all formatting #773 comment.
    • Hyperlink: keep the whole link when part of it is formatted #770: the in-app chat composer check passed in 8 of 8 sessions. The maintainer owns that contract tick.
    • Hyperlink: keep a custom href when autolink runs over an existing link #772: the in-app Mod-k check passed in 8 of 8 sessions.
    • Hyperlink: keep a removed link removed when a peer edits the pad #771 (ruling 9): the live key y-sync$1 is read, and all three triggers happen on a live pad.
    • Hyperlink: keep PURE comments so helper-only imports tree-shake #774 (ruling 4) and Hyperlink: decide what Enter inside a link does #778 (ruling 2): evidence is posted on each issue.
    • Hyperlink: add HyperlinkBase without the prebuilt popovers #775: worth doing only after Hyperlink: keep PURE comments so helper-only imports tree-shake #774. HyperlinkBase then saves 6,734 B min / 2,022 B gzip in a StarterKit app.
    • Hyperlink: load link-icon images only near the screen #777: the in-app "before" is measured on four scratch pads.

    Finding: chunked input links only the last word. When one transaction inserts several words, autolink checks only the last word. On fresh local pads, a one-chunk insertText of see example.com now left example.com unlinked in 0 of 10 runs. A CDP IME phrase commit also linked 0 of 10. Key-by-key typing linked 10 of 10. With the URL as the last word (go to example.com ), both chunk paths linked 5 of 5. A verifier re-run on 5 new pads matched. Key-typing one more word (more ) did not repair the miss (0 of 20). Enter, blur and reload were not tested. Upstream Link 3.32.2 has the same rule, so this is not a regression. A scratch prototype fixed it for +234 B gzip, but only on a scratch page, not on a pad. It should become a low-priority issue after ruling 9. Still owed: one real macOS dictation check.

    Raw data: local workflow folders wf3/ and wf4/, with one subfolder per task. Verifier re-runs are in the v-*, verify-* and p1-verify subfolders.

    Pad profile, final: on a 2,025-block pad, select-all Bold takes about 4.6–4.7 s (n = 7 per pad, A/A −1.13 %). Autolink is 1.5 % of it. The CPU profile puts 32–35 % in y-prosemirror / y-tiptap / Yjs and 30–34 % in prosemirror-transform; the hyperlink extension is 0.38 %. Details on #773.

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

Metadata

Metadata

Assignees

  • @HMarzban
    HMarzban

Labels

Extensiontiptap

Type

No type

Projects

No projects

    Milestone

    • v2.3.8No due date

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    You are visitor 01402520 — GeezerVison does not store the pages you look up.
    Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.