Color mode

Text Fragment

Copy a Text Fragment deep link (#:~:text=) or a Markdown quote for the text you select in an article.

日本語

Overview

Client-only plugin. On page load it installs one selection popover with two actions:

  • Copy link — builds a URL with a Text Fragment directive that highlights the selected text.
  • Copy quote — builds a Markdown block quote with a link back to the page.

Usage

ts
import { defineConfig } from "@riebeckite/core";
import { textFragmentPlugin } from "@riebeckite/plugin-text-fragment";
 
export default defineConfig({
  // ...
  plugins: [textFragmentPlugin()],
});

textFragmentPlugin() registers style.css and the initTextFragmentShare client entry, which the app calls during page initialization. Pass textFragmentPlugin({ labels }) to override individual UI labels.

Behavior

  • A non-empty selection inside the article body shows the popover near the selection. Selections inside pre, code, a[href], or [data-no-share] are ignored.
  • Both actions write to the clipboard through navigator.clipboard.writeText and fall back to a hidden textarea with document.execCommand("copy").
  • Failures are announced in a visible aria-live="polite" status region.
  • Escape or a click outside closes the popover. The buttons are real <button> elements, so they are reachable with the keyboard.
  • The popover is installed once per page and does nothing when there is no document (SSR-safe).

URL rules

The fragment follows #:~:text=[prefix-,]start[,end][,-suffix]:

  • ,, - and & are percent-encoded (%2C, %2D, %26).
  • Other characters are encoded per UTF-8 (newlines become %0A).
  • An existing hash on the page URL is dropped before the directive is appended.
  • Selections longer than ~200 characters or containing a newline are reduced to a start,end range built from the first and last token.
  • An empty or whitespace-only selection produces "".

API

  • textFragmentPlugin(options?) — plugin factory; options.labels overrides the UI labels
  • initTextFragmentShare(labels?) — client initializer (also via @riebeckite/plugin-text-fragment/client)
  • encodeTextFragment(text) — percent-encodes one text fragment term
  • buildTextFragmentUrl(pageUrl, selection, options?) — builds the deep link; options is { prefix?, suffix? }
  • buildQuoteMarkdown({ url, title, selection }) — builds the Markdown quote
  • DEFAULT_TEXT_FRAGMENT_LABELS — the default English UI labels
  • Types: TextFragmentOptions, TextFragmentLabels, TextFragmentPluginOptions

See also

History

1 changesCollapseExpand
1 + <!-- Generated from packages/plugins/text-fragment/README.md. Do not edit this page directly; edit the package README and run `pnpm docs:sync`. -->
2 +
3 + # Text Fragment
4 +
5 + Copy a Text Fragment deep link (`#:~:text=`) or a Markdown quote for the text
6 + you select in an article.
7 +
8 + [日本語](./text-fragment.md)
9 +
10 + ## Overview
11 +
12 + Client-only plugin. On page load it installs one selection popover with two
13 + actions:
14 +
15 + - **Copy link** — builds a URL with a
16 + [Text Fragment directive](https://wicg.github.io/scroll-to-text-fragment/)
17 + that highlights the selected text.
18 + - **Copy quote** — builds a Markdown block quote with a link back to the page.
19 +
20 + ## Usage
21 +
22 + ```ts
23 + import { defineConfig } from "@riebeckite/core";
24 + import { textFragmentPlugin } from "@riebeckite/plugin-text-fragment";
25 +
26 + export default defineConfig({
27 + // ...
28 + plugins: [textFragmentPlugin()],
29 + });
30 + ```
31 +
32 + `textFragmentPlugin()` registers `style.css` and the `initTextFragmentShare`
33 + client entry, which the app calls during page initialization. Pass
34 + `textFragmentPlugin({ labels })` to override individual UI labels.
35 +
36 + ## Behavior
37 +
38 + - A non-empty selection inside the article body shows the popover near the
39 + selection. Selections inside `pre`, `code`, `a[href]`, or `[data-no-share]`
40 + are ignored.
41 + - Both actions write to the clipboard through `navigator.clipboard.writeText`
42 + and fall back to a hidden textarea with `document.execCommand("copy")`.
43 + - Failures are announced in a visible `aria-live="polite"` status region.
44 + - `Escape` or a click outside closes the popover. The buttons are real
45 + `<button>` elements, so they are reachable with the keyboard.
46 + - The popover is installed once per page and does nothing when there is no
47 + `document` (SSR-safe).
48 +
49 + ### URL rules
50 +
51 + The fragment follows `#:~:text=[prefix-,]start[,end][,-suffix]`:
52 +
53 + - `,`, `-` and `&` are percent-encoded (`%2C`, `%2D`, `%26`).
54 + - Other characters are encoded per UTF-8 (newlines become `%0A`).
55 + - An existing hash on the page URL is dropped before the directive is appended.
56 + - Selections longer than ~200 characters or containing a newline are reduced to
57 + a `start,end` range built from the first and last token.
58 + - An empty or whitespace-only selection produces `""`.
59 +
60 + ## API
61 +
62 + - `textFragmentPlugin(options?)` — plugin factory; `options.labels` overrides
63 + the UI labels
64 + - `initTextFragmentShare(labels?)` — client initializer (also via
65 + `@riebeckite/plugin-text-fragment/client`)
66 + - `encodeTextFragment(text)` — percent-encodes one text fragment term
67 + - `buildTextFragmentUrl(pageUrl, selection, options?)` — builds the deep link;
68 + `options` is `{ prefix?, suffix? }`
69 + - `buildQuoteMarkdown({ url, title, selection })` — builds the Markdown quote
70 + - `DEFAULT_TEXT_FRAGMENT_LABELS` — the default English UI labels
71 + - Types: `TextFragmentOptions`, `TextFragmentLabels`,
72 + `TextFragmentPluginOptions`
73 +
74 + ## See also
75 +
76 + - [Plugin guide](../reference/plugin-api.en.md)
77 +