Skip to content

Richer internal-link chips, and a kind icon beside the pad link #174

Description

@HMarzban

Goal

Make same-document hyperlinks easier to read and more useful than a generic URL.

Two surfaces, one classifier (classifyInternalDocumentLink):

  1. Preview chip — show the real destination, not a count. A filter link that says “Filtered view · 1 term” must name that term.
  2. In the pad — a small leading icon on the linked text so a filter, heading, chat, history, or “this document” link is visible before anyone opens the popover. Same idea as GitHub’s leading mark on a special link, not a second toolbar.

Facts (2026-08-21)

  • Five internal kinds already exist: document, heading, chat, filter, history. Labels come from describeInternalDocumentLink. Filter copy is Filtered view · N term(s) plus Match any / Match all. The terms themselves never appear in the chip.
  • Desktop preview: imperative chip is the Go button. Mobile sheet and composer: React chip is display-only; Go is a separate row. Copy always yields the raw href.
  • In the document the mark still renders as a plain <a href>. No kind icon. No extra schema attr.
  • FilterBar term chips on the pad are a different species (info / --info-ink). Do not merge them with this hyperlink chip.
  • Hyperlink renderHTML is ['a', attrs, 0]. Icons must not be written into Yjs text. Do not add toDOM on the storage hyperlink stub.
  • Do not stamp persistent attributes onto nodes inside .ProseMirror from JS (DOMObserver recreates media node-views). Widget decorations and CSS on the existing <a> are the safe paths. Live regions inside the editor stay forbidden.
  • Webapp icon catalog is Lucide via Icons. Do not put emoji, Fa*, or Si* into pad text or the chip.

What to build

Preview chip — name the destination

  • Filter: list the terms in the chip (and keep Match any/all). One term → show it. Several terms → show the first few and a +N; the full set stays in the popover.
  • Heading / chat: keep the quoted heading title when the pad editor can resolve it (already does).
  • History: keep Version N / History.
  • Desktop, mobile sheet, and composer all read the same descriptor. Do not fork copy per surface.
  • Filter term pills inside the preview may reuse FilterBar’s info / --info-ink language so they look like the same filters. The icon tile on the chip stays the internal-link primary wash.

Pad text — leading kind icon

  • When classify returns an internal kind, show a leading Lucide icon beside that hyperlink in the pad (filter, heading, chat, history, document). External and other-document links stay unchanged.
  • The icon is decoration. The existing click path stays: editable tap opens the preview popover; the chip/Go still runs the destination. Do not add a second clickable control inside the link (nested button).
  • Same treatment in the chat composer preview of an internal href is welcome if cheap; pad is the required surface.
  • Print / copy HTML must not serialize a fake character into the document. The stored mark stays href + text.

Interaction

Internal links may do more in the preview than an external unfurl (named destination, Go in place, terms). They must not do more in the document click than today’s preview/open split. Keyboard and screen readers still meet one link, not “icon + link”.

Out of scope

  • Changing classify precedence or treating another docs.plus slug as internal.
  • Special-URL / favicon icons on external https://github.com/… links (later, separate).
  • Writing icons or emoji into the hyperlink mark or the visible text.
  • Merging this chip with FilterBar, TOC, or heading-action chips.
  • Changing chat Bookmark tables or My Documents Favorites (Favorites in My Documents, then a later list UX pass #173).

Acceptance criteria

  • A one-term filter link’s preview names that term. A many-term filter names terms or +N, not only a count.
  • Desktop popover, mobile sheet, and composer preview agree on that copy.
  • Pad hyperlinks for all five internal kinds show a leading Lucide icon; external links do not.
  • Click, copy, and Go behaviour match today (preview when editing; in-place run from the chip/Go; copy is the raw href).
  • Stored Yjs / HTML round-trip has no extra character or mark attr for the icon.
  • Light + dark, desktop + mobile, print: walked. Media embeds on the same page do not reload when the popover opens.

Blocked by

None. Visual density of many-term chips (how many names before +N) is HITL on this issue if the first walk feels crowded.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions