@docs.plus/extension-hyperlink 1.5.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +572 -0
- package/LICENSE +21 -0
- package/README.md +135 -385
- package/dist/index.cjs +22 -712
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +392 -0
- package/dist/index.d.ts +392 -9
- package/dist/index.js +22 -707
- package/dist/index.js.map +1 -1
- package/dist/styles.css +362 -0
- package/package.json +85 -33
- package/dist/helpers/autoHyperlink.d.ts +0 -8
- package/dist/helpers/clickHandler.d.ts +0 -11
- package/dist/helpers/editHyperlink.d.ts +0 -9
- package/dist/helpers/pasteHandler.d.ts +0 -9
- package/dist/helpers/tippyHelper.d.ts +0 -29
- package/dist/hyperlink.d.ts +0 -77
- package/dist/index.umd.js +0 -713
- package/dist/index.umd.js.map +0 -1
- package/dist/modals/editeHyperlink.d.ts +0 -11
- package/dist/modals/icons.d.ts +0 -9
- package/dist/modals/previewHyperlink.d.ts +0 -11
- package/dist/modals/setHyperlink.d.ts +0 -14
- package/src/helpers/autoHyperlink.ts +0 -167
- package/src/helpers/clickHandler.ts +0 -80
- package/src/helpers/editHyperlink.ts +0 -59
- package/src/helpers/pasteHandler.ts +0 -46
- package/src/helpers/tippyHelper.ts +0 -136
- package/src/hyperlink.ts +0 -277
- package/src/index.ts +0 -17
- package/src/modals/editeHyperlink.ts +0 -99
- package/src/modals/icons.ts +0 -46
- package/src/modals/previewHyperlink.ts +0 -85
- package/src/modals/setHyperlink.ts +0 -97
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023-2026 Hossein Marzban
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,454 +1,204 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @docs.plus/extension-hyperlink
|
|
2
2
|
|
|
3
|
+
<a href="https://docs.plus"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/docs-plus/docs.plus/main/apps/webapp/public/badges/badge-docsplus-dark.svg"><img alt="docs.plus" height="20" src="https://raw.githubusercontent.com/docs-plus/docs.plus/main/apps/webapp/public/badges/badge-docsplus.svg"></picture></a>
|
|
3
4
|
[](https://www.npmjs.com/package/@docs.plus/extension-hyperlink)
|
|
4
5
|
[](https://npmcharts.com/compare/@docs.plus/extension-hyperlink)
|
|
5
6
|
[](https://www.npmjs.com/package/@docs.plus/extension-hyperlink)
|
|
7
|
+
[](https://discord.gg/2EmAjmgZ8)
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
<p align="center">
|
|
10
|
+
<picture>
|
|
11
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/docs-plus/docs.plus/main/extensions/extension-hyperlink/assets/demo-dark.gif">
|
|
12
|
+
<img alt="Typing "docs.plus" autolinks it, clicking the link opens a preview with copy, edit and remove, and Mod-K on a selected word opens the create-link form" width="640" src="https://raw.githubusercontent.com/docs-plus/docs.plus/main/extensions/extension-hyperlink/assets/demo-light.gif">
|
|
13
|
+
</picture>
|
|
14
|
+
</p>
|
|
8
15
|
|
|
9
|
-
|
|
16
|
+
Tiptap hyperlink mark with optional prebuilt popovers for creating, previewing, and editing links.
|
|
10
17
|
|
|
11
|
-
|
|
18
|
+
It replaces the `@tiptap/extension-link` mark that StarterKit v3 bundles.
|
|
12
19
|
|
|
13
|
-
|
|
14
|
-
npm install @docs.plus/extension-hyperlink
|
|
15
|
-
````
|
|
20
|
+
[](https://stackblitz.com/github/docs-plus/docs.plus/tree/main/extensions/extension-hyperlink/examples/vanilla?file=main.ts)
|
|
16
21
|
|
|
17
|
-
##
|
|
22
|
+
## Why use it
|
|
18
23
|
|
|
19
|
-
|
|
24
|
+
- **Autolink covers more than web URLs.** As you type, bare domains get `https://`, emails become `mailto:`, and E.164 phones become `tel:`. App schemes from a catalog of 47, such as `whatsapp://`, autolink too.
|
|
25
|
+
- **The link UI is included.** Prebuilt create, preview, and edit popovers ship with the package. `Mod-k` opens the create-link form with no configuration. `@tiptap/extension-link` binds no keyboard shortcut.
|
|
26
|
+
- **Every boundary checks the scheme.** `isSafeHref` blocks `javascript:`, `data:`, `vbscript:`, `file:`, and `blob:` on parse, import, write, serialize, and navigation. An embedded tab or newline cannot hide a scheme from the gate.
|
|
27
|
+
- **Your `@tiptap/extension-link` configuration ports over.** `isAllowedUri` takes the same `(uri, ctx)` shape as `@tiptap/extension-link`, so an existing policy works unchanged. `setLink`, `unsetLink`, and `toggleLink` stay as aliases. `[label](url)` round-trips with `@tiptap/markdown`.
|
|
20
28
|
|
|
21
|
-
|
|
29
|
+
## Install
|
|
22
30
|
|
|
23
|
-
|
|
31
|
+
```sh
|
|
32
|
+
npm install @docs.plus/extension-hyperlink
|
|
33
|
+
```
|
|
24
34
|
|
|
25
|
-
|
|
26
|
-
Hyperlink.configure({
|
|
27
|
-
protocols: ['ftp', 'mailto'],
|
|
28
|
-
})
|
|
29
|
-
````
|
|
35
|
+
Or use `pnpm add @docs.plus/extension-hyperlink`, `yarn add @docs.plus/extension-hyperlink`, or `bun add @docs.plus/extension-hyperlink`.
|
|
30
36
|
|
|
31
|
-
|
|
37
|
+
Requires **`@tiptap/core` ^3.31.3** and **`@tiptap/pm` ^3.31.3** (Tiptap 3.x).
|
|
32
38
|
|
|
33
|
-
|
|
34
|
-
Hyperlink.configure({
|
|
35
|
-
protocols: [
|
|
36
|
-
{
|
|
37
|
-
scheme: 'tel',
|
|
38
|
-
optionalSlashes: true
|
|
39
|
-
}
|
|
40
|
-
]
|
|
41
|
-
})
|
|
42
|
-
````
|
|
39
|
+
This package imports no React, Vue, or Next.js code. Use it from a plain page, Vite, React, Vue, Svelte, Next.js, Nuxt, or SvelteKit. Create the editor in the browser.
|
|
43
40
|
|
|
44
|
-
|
|
41
|
+
React Native has no DOM. Load the editor in a web view.
|
|
45
42
|
|
|
46
|
-
|
|
43
|
+
Installs two runtime dependencies, `@floating-ui/dom` and `linkifyjs`. The popover engine and the tooltip ship inside `dist`, so they add no third package.
|
|
47
44
|
|
|
48
|
-
|
|
45
|
+
Upgrading from `1.x`? Option names, command names, and CSS class names all changed — see [Migrating from 1.x](https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hyperlink/docs/migration.md#migrating-from-1x).
|
|
49
46
|
|
|
50
|
-
|
|
51
|
-
Hyperlink.configure({
|
|
52
|
-
autolink: false,
|
|
53
|
-
})
|
|
54
|
-
````
|
|
47
|
+
## Quickstart
|
|
55
48
|
|
|
56
|
-
|
|
49
|
+
[](https://stackblitz.com/github/docs-plus/docs.plus/tree/main/extensions/extension-hyperlink/examples/vanilla?file=main.ts)
|
|
57
50
|
|
|
58
|
-
|
|
51
|
+
Run this Quickstart in your browser first, with nothing to install. The app lives in [`examples/vanilla`](https://github.com/docs-plus/docs.plus/tree/main/extensions/extension-hyperlink/examples/vanilla).
|
|
59
52
|
|
|
60
|
-
|
|
53
|
+
The host page needs one mount point: `<div id="editor"></div>`. The snippet also imports `@tiptap/starter-kit`. Add it with `npm install @tiptap/starter-kit` when your app has none yet.
|
|
61
54
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
55
|
+
```ts
|
|
56
|
+
import { Editor } from '@tiptap/core'
|
|
57
|
+
import StarterKit from '@tiptap/starter-kit'
|
|
58
|
+
import {
|
|
59
|
+
Hyperlink,
|
|
60
|
+
createHyperlinkPopover,
|
|
61
|
+
editHyperlinkPopover,
|
|
62
|
+
previewHyperlinkPopover
|
|
63
|
+
} from '@docs.plus/extension-hyperlink'
|
|
64
|
+
import '@docs.plus/extension-hyperlink/styles.css'
|
|
65
|
+
|
|
66
|
+
const editor = new Editor({
|
|
67
|
+
element: document.querySelector('#editor'),
|
|
68
|
+
content: '<p>Try <a href="https://example.com">this link</a>.</p>',
|
|
69
|
+
extensions: [
|
|
70
|
+
// Disable StarterKit's bundled link mark — see Caveats.
|
|
71
|
+
StarterKit.configure({ link: false }),
|
|
72
|
+
Hyperlink.configure({
|
|
73
|
+
popovers: {
|
|
74
|
+
previewHyperlink: previewHyperlinkPopover,
|
|
75
|
+
editHyperlink: editHyperlinkPopover,
|
|
76
|
+
createHyperlink: createHyperlinkPopover
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
]
|
|
65
80
|
})
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
### linkOnPaste
|
|
69
|
-
|
|
70
|
-
Adds a link to the current selection if the pasted content only contains an url.
|
|
81
|
+
```
|
|
71
82
|
|
|
72
|
-
|
|
83
|
+
The `styles.css` import is optional. The snippet also styles no document link — see [Popovers](https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hyperlink/docs/popovers.md) and [Style document links](#style-document-links).
|
|
73
84
|
|
|
74
|
-
|
|
75
|
-
Hyperlink.configure({
|
|
76
|
-
linkOnPaste: false,
|
|
77
|
-
})
|
|
78
|
-
````
|
|
85
|
+
You should see a paragraph with one link. Click the link to open the preview popover, with copy, edit, and remove actions. Type `example.org` and a space, and the text becomes a link. Select a word and press `Mod-k` (Cmd on macOS, Ctrl elsewhere) to open the create-link form.
|
|
79
86
|
|
|
80
|
-
|
|
87
|
+
## Caveats
|
|
81
88
|
|
|
82
|
-
|
|
89
|
+
- **`StarterKit.configure({ link: false })` is required, not stylistic.** StarterKit v3 bundles `@tiptap/extension-link` by default. The two marks collide on the `setLink` / `unsetLink` / `toggleLink` command names and on the `a[href]` parse rule. Your `extensions` array order then decides each contest, with no warning. If the upstream mark wins, this package's `isSafeHref` gate never runs on parsed or pasted HTML, and its popovers never attach.
|
|
90
|
+
- **A click on a link needs `popovers.previewHyperlink`.** The three openers fall back to the prebuilt factory when a slot is `null`. The click handler does not: leave `previewHyperlink` at `null` and a click on a link opens no popover.
|
|
91
|
+
- **`linkOnPaste: false` does not stop every paste from linking.** The linkify paste rule stays registered, so a pasted bare URL still becomes a link. To stop that path too, set `shouldAutoLink: () => false`, which every autolink path consults.
|
|
92
|
+
- **`HTMLAttributes.target` reaches the DOM.** `HTMLAttributes: { target: '_blank' }` does emit `<a target="_blank">`. A browser-driven `target="_blank"` navigation skips the click gate. Leave `target` at `null` and let the click handler open the link.
|
|
83
93
|
|
|
84
|
-
|
|
85
|
-
Hyperlink.configure({
|
|
86
|
-
HTMLAttributes: {
|
|
87
|
-
class: 'my-custom-class',
|
|
88
|
-
},
|
|
89
|
-
})
|
|
90
|
-
````
|
|
94
|
+
The full list, with the reason for each, is in [Caveats](https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hyperlink/docs/api.md#caveats).
|
|
91
95
|
|
|
92
|
-
|
|
96
|
+
## Common tasks
|
|
93
97
|
|
|
94
|
-
|
|
98
|
+
### Set a link from your own button
|
|
95
99
|
|
|
96
|
-
|
|
97
|
-
- [Demo](https://github.com/HMarzban/extension-hyperlink#test-drive-with-our-demo-)
|
|
98
|
-
|
|
99
|
-
<details>
|
|
100
|
-
<summary>The `previewHyperlinkModal` function</summary>
|
|
100
|
+
Every command lands on `editor.commands` and on `editor.chain()`. On `editor.commands`, each returns `boolean`, and `false` marks a no-op.
|
|
101
101
|
|
|
102
102
|
```ts
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const hyperlinkModal = document.createElement("div");
|
|
107
|
-
const removeButton = document.createElement("button");
|
|
108
|
-
const copyButton = document.createElement("button");
|
|
109
|
-
|
|
110
|
-
const newBubble = document.createElement("div");
|
|
111
|
-
newBubble.classList.add("hyperlink-preview-modal__metadata");
|
|
103
|
+
editor.chain().focus().setHyperlink({ href: 'https://example.com' }).run()
|
|
104
|
+
editor.getAttributes('hyperlink').href // read the current href
|
|
105
|
+
```
|
|
112
106
|
|
|
113
|
-
|
|
114
|
-
hrefTitle.setAttribute("target", "_blank");
|
|
115
|
-
hrefTitle.setAttribute("rel", "noreferrer");
|
|
116
|
-
hrefTitle.setAttribute("href", href);
|
|
117
|
-
hrefTitle.innerText = href;
|
|
107
|
+
`setHyperlink` returns `false` when `href` is empty, when the [composed gate](https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hyperlink/docs/api.md#validate-vs-isalloweduri) rejects it, or when the schema cannot apply the mark there. See [Commands](https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hyperlink/docs/api.md#commands) for the full list.
|
|
118
108
|
|
|
119
|
-
|
|
109
|
+
### Open the create-link form
|
|
120
110
|
|
|
121
|
-
|
|
111
|
+
`Mod-k` calls this command. Call it from your own toolbar button too:
|
|
122
112
|
|
|
123
|
-
|
|
124
|
-
|
|
113
|
+
```ts
|
|
114
|
+
editor.commands.openCreateHyperlinkPopover()
|
|
115
|
+
```
|
|
125
116
|
|
|
126
|
-
|
|
127
|
-
copyButton.innerHTML = "copy";
|
|
117
|
+
`openCreateHyperlinkPopover` opens UI, so `editor.can().openCreateHyperlinkPopover()` reports availability without mounting anything. With nothing selected, the prebuilt form inserts the typed URL as its own link text.
|
|
128
118
|
|
|
129
|
-
|
|
130
|
-
options.tippy.hide();
|
|
131
|
-
return options.editor.chain().focus().unsetHyperlink().run();
|
|
132
|
-
});
|
|
119
|
+
### Style document links
|
|
133
120
|
|
|
134
|
-
|
|
135
|
-
options.tippy.hide();
|
|
136
|
-
navigator.clipboard.writeText(href);
|
|
137
|
-
});
|
|
121
|
+
The `styles.css` stylesheet skins the popovers only. The mark renders a plain `<a>` with no class, so style your document links yourself:
|
|
138
122
|
|
|
139
|
-
|
|
123
|
+
```ts
|
|
124
|
+
Hyperlink.configure({ HTMLAttributes: { class: 'my-link' } })
|
|
125
|
+
```
|
|
140
126
|
|
|
141
|
-
|
|
127
|
+
```css
|
|
128
|
+
a.my-link {
|
|
129
|
+
color: #2563eb;
|
|
130
|
+
text-decoration: underline;
|
|
142
131
|
}
|
|
143
|
-
|
|
144
132
|
```
|
|
145
133
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
<details>
|
|
149
|
-
<summary>The `setHyperlinkModal` function</summary>
|
|
150
|
-
|
|
151
|
-
```ts
|
|
152
|
-
import { Editor } from "@tiptap/core";
|
|
153
|
-
import { find } from "linkifyjs";
|
|
154
|
-
|
|
155
|
-
let tooltip = undefined;
|
|
156
|
-
|
|
157
|
-
function setHyperlinkModal(options) {
|
|
158
|
-
// Create the tooltip instance
|
|
159
|
-
if (!tooltip) tooltip = new options.Tooltip(options);
|
|
160
|
-
|
|
161
|
-
// Initialize the tooltip
|
|
162
|
-
let { tippyModal } = tooltip.init();
|
|
163
|
-
|
|
164
|
-
const hyperlinkModal = document.createElement("div");
|
|
165
|
-
const buttonsWrapper = document.createElement("div");
|
|
166
|
-
const inputsWrapper = document.createElement("div");
|
|
167
|
-
|
|
168
|
-
hyperlinkModal.classList.add("hyperlink-set-modal");
|
|
169
|
-
|
|
170
|
-
buttonsWrapper.classList.add("hyperlink-set-modal__buttons-wrapper");
|
|
171
|
-
inputsWrapper.classList.add("hyperlink-set-modal__inputs-wrapper");
|
|
172
|
-
|
|
173
|
-
// create a form that contain url input and a button for submit
|
|
174
|
-
const form = document.createElement("form");
|
|
175
|
-
const input = document.createElement("input");
|
|
176
|
-
const button = document.createElement("button");
|
|
177
|
-
|
|
178
|
-
input.setAttribute("type", "text");
|
|
179
|
-
input.setAttribute("placeholder", "https://example.com");
|
|
180
|
-
button.setAttribute("type", "submit");
|
|
181
|
-
button.innerText = "Submit";
|
|
182
|
-
|
|
183
|
-
inputsWrapper.append(input);
|
|
184
|
-
buttonsWrapper.append(button);
|
|
185
|
-
form.append(inputsWrapper, buttonsWrapper);
|
|
186
|
-
|
|
187
|
-
hyperlinkModal.append(form);
|
|
188
|
-
|
|
189
|
-
tippyModal.innerHTML = "";
|
|
190
|
-
tippyModal.append(hyperlinkModal);
|
|
191
|
-
tooltip.update(options.editor.view, {
|
|
192
|
-
arrow: options.roundArrow,
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
// make sure
|
|
196
|
-
setTimeout(() => input.focus();, 100);
|
|
197
|
-
|
|
198
|
-
// event listenr for submit button
|
|
199
|
-
form.addEventListener("submit", (event) => {
|
|
200
|
-
event.preventDefault();
|
|
201
|
-
const url = input.value;
|
|
134
|
+
For theme tokens, dark mode, and class names, see [Styling](https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hyperlink/docs/styling.md).
|
|
202
135
|
|
|
203
|
-
|
|
136
|
+
## Gallery
|
|
204
137
|
|
|
205
|
-
|
|
206
|
-
.filter((link) => link.isLink)
|
|
207
|
-
.filter((link) => {
|
|
208
|
-
if (options.validate) {
|
|
209
|
-
return options.validate(link.value);
|
|
210
|
-
}
|
|
211
|
-
return true;
|
|
212
|
-
})
|
|
213
|
-
.at(0);
|
|
138
|
+
Prebuilt create, preview, and edit popovers (`styles.css` + `popovers` config). Each screenshot has a light and a dark version, and follows your system preference.
|
|
214
139
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
tooltip?.hide();
|
|
140
|
+
<details>
|
|
141
|
+
<summary><strong>Create</strong> — Mod+K</summary>
|
|
218
142
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
226
|
-
```
|
|
143
|
+
<p align="center">
|
|
144
|
+
<picture>
|
|
145
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/docs-plus/docs.plus/main/extensions/extension-hyperlink/assets/create-dark.png">
|
|
146
|
+
<img alt="Create-link popover with URL field and Apply button" width="640" src="https://raw.githubusercontent.com/docs-plus/docs.plus/main/extensions/extension-hyperlink/assets/create-light.png">
|
|
147
|
+
</picture>
|
|
148
|
+
</p>
|
|
227
149
|
|
|
228
150
|
</details>
|
|
229
151
|
|
|
230
152
|
<details>
|
|
231
|
-
<summary>
|
|
232
|
-
|
|
233
|
-
```scss
|
|
234
|
-
.tippy-box {
|
|
235
|
-
.hyperlink-preview-modal,
|
|
236
|
-
.hyperlink-set-modal,
|
|
237
|
-
.hyperlink-edit-modal {
|
|
238
|
-
background-color: #fff;
|
|
239
|
-
border-radius: 10px;
|
|
240
|
-
border: 1px solid #dadce0;
|
|
241
|
-
display: flex;
|
|
242
|
-
align-items: center;
|
|
243
|
-
justify-content: space-between;
|
|
244
|
-
padding: 6px 6px;
|
|
245
|
-
box-shadow: 0 1px 3px 1px rgba(60, 64, 67, 0.15);
|
|
246
|
-
margin-top: -6px;
|
|
247
|
-
|
|
248
|
-
&__metadata {
|
|
249
|
-
width: 200px;
|
|
250
|
-
display: flex;
|
|
251
|
-
align-items: center;
|
|
252
|
-
justify-content: flex-end;
|
|
253
|
-
flex-direction: row-reverse;
|
|
254
|
-
a {
|
|
255
|
-
font-size: 0.9rem;
|
|
256
|
-
margin-right: 6px;
|
|
257
|
-
white-space: nowrap;
|
|
258
|
-
overflow: hidden;
|
|
259
|
-
text-overflow: ellipsis;
|
|
260
|
-
}
|
|
261
|
-
img {
|
|
262
|
-
width: 18px;
|
|
263
|
-
height: 18px;
|
|
264
|
-
border-radius: 50%;
|
|
265
|
-
margin-right: 8px;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
&__remove-button,
|
|
270
|
-
&__edit-button,
|
|
271
|
-
&__copy-button,
|
|
272
|
-
&__apply-button {
|
|
273
|
-
width: 30px;
|
|
274
|
-
height: 30px;
|
|
275
|
-
border-radius: 50%;
|
|
276
|
-
margin: 0 0.25rem;
|
|
277
|
-
display: flex;
|
|
278
|
-
align-items: center;
|
|
279
|
-
justify-content: center;
|
|
280
|
-
transition: background-color 0.1s ease-in-out;
|
|
281
|
-
&:hover {
|
|
282
|
-
background-color: #eee;
|
|
283
|
-
}
|
|
284
|
-
> svg {
|
|
285
|
-
width: 16px;
|
|
286
|
-
height: 16px;
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
form {
|
|
291
|
-
display: flex;
|
|
292
|
-
align-items: flex-end;
|
|
293
|
-
width: 100%;
|
|
294
|
-
input {
|
|
295
|
-
border: 1px solid #dadce0;
|
|
296
|
-
border-radius: 6px;
|
|
297
|
-
padding: 0.4rem 0.8rem;
|
|
298
|
-
margin-bottom: 0.2rem;
|
|
299
|
-
width: 100%;
|
|
300
|
-
&:last-of-type {
|
|
301
|
-
margin-bottom: 0;
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
.hyperlink-set-modal__buttons-wrapper,
|
|
305
|
-
.hyperlink-edit-modal__buttons-wrapper {
|
|
306
|
-
margin-left: 8px;
|
|
307
|
-
button {
|
|
308
|
-
border-radius: 6px;
|
|
309
|
-
padding: 4px 14px;
|
|
310
|
-
width: 70px;
|
|
311
|
-
margin-bottom: 0.2rem;
|
|
312
|
-
color: #1a73e8;
|
|
313
|
-
&:hover {
|
|
314
|
-
background: rgba(26, 115, 232, 0.04);
|
|
315
|
-
color: #174ea6;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}
|
|
153
|
+
<summary><strong>Preview</strong> — click a link</summary>
|
|
321
154
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
155
|
+
<p align="center">
|
|
156
|
+
<picture>
|
|
157
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/docs-plus/docs.plus/main/extensions/extension-hyperlink/assets/preview-dark.png">
|
|
158
|
+
<img alt="Preview popover on a link — copy, edit, and remove actions" width="640" src="https://raw.githubusercontent.com/docs-plus/docs.plus/main/extensions/extension-hyperlink/assets/preview-light.png">
|
|
159
|
+
</picture>
|
|
160
|
+
</p>
|
|
327
161
|
|
|
328
162
|
</details>
|
|
329
163
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
Hyperlink,
|
|
333
|
-
previewHyperlinkModal,
|
|
334
|
-
setHyperlinkModal
|
|
335
|
-
} from "@docs.plus/extension-hyperlink";
|
|
336
|
-
|
|
337
|
-
Hyperlink.configure({
|
|
338
|
-
modals: {
|
|
339
|
-
previewHyperlink: (data) => {
|
|
340
|
-
return previewHyperlinkModal(data);
|
|
341
|
-
},
|
|
342
|
-
setHyperlink: (data) => {
|
|
343
|
-
return setHyperlinkModal(data);
|
|
344
|
-
},
|
|
345
|
-
},
|
|
346
|
-
})
|
|
347
|
-
````
|
|
348
|
-
|
|
349
|
-
> Note: The `previewHyperlinkModal` and `setHyperlinkModal` modals are prebuilt modal boxes. You can use them or create your own modal boxes. To learn how to create your own modal boxes, please check the [source code](https://github.com/HMarzban/extension-hyperlink/tree/main/packages/extension-hyperlink/src/modals)
|
|
350
|
-
|
|
351
|
-
<Su>
|
|
352
|
-
|
|
353
|
-
### Removing and overriding existing html attributes
|
|
354
|
-
|
|
355
|
-
You can add `rel: null` to HTMLAttributes to remove the default `rel="noopener noreferrer nofollow"`. You can also override the default by using `rel: "your-value"`.
|
|
356
|
-
|
|
357
|
-
This can also be used to change the `target` from the default value of `_blank`.
|
|
358
|
-
|
|
359
|
-
````js
|
|
360
|
-
Hyperlink.configure({
|
|
361
|
-
HTMLAttributes: {
|
|
362
|
-
// Change rel to different value
|
|
363
|
-
// Allow search engines to follow links(remove nofollow)
|
|
364
|
-
rel: 'noopener noreferrer',
|
|
365
|
-
// Remove target entirely so links open in current tab
|
|
366
|
-
target: null,
|
|
367
|
-
},
|
|
368
|
-
})
|
|
369
|
-
````
|
|
370
|
-
|
|
371
|
-
### validate
|
|
372
|
-
|
|
373
|
-
A function that validates every autolinked link. If it exists, it will be called with the link href as argument. If it returns `false`, the link will be removed.
|
|
374
|
-
|
|
375
|
-
Can be used to set rules for example excluding or including certain domains, tlds, etc.
|
|
376
|
-
|
|
377
|
-
````js
|
|
378
|
-
// only autolink urls with a protocol
|
|
379
|
-
Hyperlink.configure({
|
|
380
|
-
validate: href => /^https?:\/\//.test(href),
|
|
381
|
-
})
|
|
382
|
-
````
|
|
383
|
-
|
|
384
|
-
## Commands
|
|
385
|
-
|
|
386
|
-
### editHyperLinkText(), editHyperLinkHref(), editHyperlink()
|
|
387
|
-
|
|
388
|
-
These commands allow you to edit the text and href value of a hyperlink.
|
|
389
|
-
|
|
390
|
-
```js
|
|
391
|
-
this.editor.commands.editHyperLinkText('New Text');
|
|
392
|
-
this.editor.commands.editHyperLinkHref('https://new-url.com');
|
|
393
|
-
this.editor.commands.editHyperlink({
|
|
394
|
-
newText: 'New Text',
|
|
395
|
-
newURL: 'https://new-url.com'
|
|
396
|
-
});
|
|
397
|
-
```
|
|
398
|
-
|
|
399
|
-
### setHyperlink()
|
|
400
|
-
|
|
401
|
-
Links the selected text.
|
|
402
|
-
|
|
403
|
-
````js
|
|
404
|
-
this.editor.commands.setHyperlink({
|
|
405
|
-
href: '<https://example.com>'
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
this.editor.commands.setHyperlink({
|
|
409
|
-
href: '<https://example.com>',
|
|
410
|
-
target: '_blank'
|
|
411
|
-
});
|
|
412
|
-
|
|
413
|
-
this.editor.commands.unsetHyperlink();
|
|
414
|
-
````
|
|
415
|
-
|
|
416
|
-
### unsetHyperlink()
|
|
417
|
-
|
|
418
|
-
Removes a Hyperlink.
|
|
419
|
-
|
|
420
|
-
````js
|
|
421
|
-
this.editor.commands.unsetHyperlink();
|
|
422
|
-
````
|
|
423
|
-
|
|
424
|
-
## Keyboard shortcuts
|
|
164
|
+
<details>
|
|
165
|
+
<summary><strong>Edit</strong> — preview → Edit</summary>
|
|
425
166
|
|
|
426
|
-
|
|
427
|
-
|
|
167
|
+
<p align="center">
|
|
168
|
+
<picture>
|
|
169
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/docs-plus/docs.plus/main/extensions/extension-hyperlink/assets/edit-dark.png">
|
|
170
|
+
<img alt="Edit-link popover with URL and text fields and Apply button" width="640" src="https://raw.githubusercontent.com/docs-plus/docs.plus/main/extensions/extension-hyperlink/assets/edit-light.png">
|
|
171
|
+
</picture>
|
|
172
|
+
</p>
|
|
428
173
|
|
|
429
|
-
|
|
174
|
+
</details>
|
|
430
175
|
|
|
431
|
-
|
|
176
|
+
## Documentation
|
|
432
177
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
178
|
+
| Guide | What it covers |
|
|
179
|
+
| ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
|
180
|
+
| [API reference](https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hyperlink/docs/api.md) | Options, commands, keyboard shortcuts, the full caveats list, server bundles, and TypeScript exports |
|
|
181
|
+
| [Popovers](https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hyperlink/docs/popovers.md) | The three popover slots, factory option shapes, openers, and custom popovers |
|
|
182
|
+
| [Styling](https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hyperlink/docs/styling.md) | The stylesheet, `--hl-*` theme tokens, dark mode, and class names |
|
|
183
|
+
| [URL handling](https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hyperlink/docs/url-handling.md) | Href normalization, scheme classification, and Markdown round-trip |
|
|
184
|
+
| [Security](https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hyperlink/docs/security.md) | The dangerous-scheme gate at each boundary, and the exported safety helpers |
|
|
185
|
+
| [Advanced](https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hyperlink/docs/advanced.md) | The floating-popover primitive, the UI controller, and the tooltip primitive |
|
|
186
|
+
| [Migrating from 1.x](https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hyperlink/docs/migration.md) | Renamed options, commands, CSS classes, and popover APIs |
|
|
187
|
+
| [Changelog](https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hyperlink/CHANGELOG.md) | Every release and its breaking changes |
|
|
188
|
+
| [For AI coding agents](https://cdn.jsdelivr.net/npm/@docs.plus/extension-hyperlink/README.md) | This README as plain Markdown for your agent. The docs are also on [Context7](https://context7.com/docs-plus/docs.plus) |
|
|
436
189
|
|
|
437
|
-
##
|
|
190
|
+
## Part of docs.plus
|
|
438
191
|
|
|
439
|
-
-
|
|
440
|
-
[extension-hyperlink](https://github.com/HMarzban/extension-hyperlink#test-drive-with-our-demo-)
|
|
441
|
-
- Extension:
|
|
442
|
-
[packages/extension-hyperlink](https://github.com/HMarzban/extension-hyperlink/tree/main/packages/extension-hyperlink)
|
|
443
|
-
- Usage: [packages/nextjs/src/components/Tiptap.tsx](https://github.com/HMarzban/extension-hyperlink/blob/59f45eba1886202f4840eb2112c34574c16fe68a/packages/nextjs/src/components/Tiptap.tsx#L19-L29)
|
|
192
|
+
This extension is built for and maintained by [docs.plus](https://docs.plus). docs.plus is a free, real-time collaboration tool that lets communities organize knowledge hierarchically, with a chat thread on every heading. docs.plus runs these packages from source in production, so every release is exercised there before it reaches npm.
|
|
444
193
|
|
|
445
|
-
|
|
194
|
+
- Website: [docs.plus](https://docs.plus)
|
|
195
|
+
- Project README: [docs-plus/docs.plus](https://github.com/docs-plus/docs.plus#readme)
|
|
196
|
+
- Sibling extensions and recommended pairings: [extensions/README.md](https://github.com/docs-plus/docs.plus/blob/main/extensions/README.md)
|
|
446
197
|
|
|
447
|
-
|
|
198
|
+
## Contributing
|
|
448
199
|
|
|
449
|
-
|
|
450
|
-
> Please note: We're not affiliated with Tiptap, but we believe in recognizing foundational work.
|
|
200
|
+
Bug reports and PRs welcome. Setup, test commands, and the playground harness live in [CONTRIBUTING.md](https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hyperlink/CONTRIBUTING.md).
|
|
451
201
|
|
|
452
|
-
|
|
202
|
+
## License
|
|
453
203
|
|
|
454
|
-
|
|
204
|
+
MIT — see [LICENSE](https://github.com/docs-plus/docs.plus/blob/main/LICENSE).
|