@docs.plus/extension-hyperlink 1.5.1 → 2.0.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/dist/index.js CHANGED
@@ -1,707 +1,22 @@
1
- import { combineTransactionSteps, getChangedRanges, getMarksBetween, findChildrenInRange, posToDOMRect, getAttributes, Mark, mergeAttributes, markPasteRule } from '@tiptap/core';
2
- import { test, find, registerCustomProtocol, reset } from 'linkifyjs';
3
- import { Plugin, PluginKey } from '@tiptap/pm/state';
4
- import tippy, { roundArrow } from 'tippy.js';
5
-
6
- function autoHyperlink(options) {
7
- return new Plugin({
8
- key: new PluginKey("autoHyperlink"),
9
- appendTransaction: (transactions, oldState, newState) => {
10
- const docChanges = transactions.some((transaction) => transaction.docChanged) &&
11
- !oldState.doc.eq(newState.doc);
12
- const preventAutoHyperlink = transactions.some((transaction) => transaction.getMeta("preventAutoHyperlink"));
13
- if (!docChanges || preventAutoHyperlink) {
14
- return;
15
- }
16
- const { tr } = newState;
17
- const transform = combineTransactionSteps(oldState.doc, [
18
- ...transactions,
19
- ]);
20
- const { mapping } = transform;
21
- const changes = getChangedRanges(transform);
22
- changes.forEach(({ oldRange, newRange }) => {
23
- // at first we check if we have to remove links
24
- getMarksBetween(oldRange.from, oldRange.to, oldState.doc)
25
- .filter((item) => item.mark.type === options.type)
26
- .forEach((oldMark) => {
27
- const newFrom = mapping.map(oldMark.from);
28
- const newTo = mapping.map(oldMark.to);
29
- const newMarks = getMarksBetween(newFrom, newTo, newState.doc).filter((item) => item.mark.type === options.type);
30
- if (!newMarks.length) {
31
- return;
32
- }
33
- const newMark = newMarks[0];
34
- const oldLinkText = oldState.doc.textBetween(oldMark.from, oldMark.to, undefined, " ");
35
- const newLinkText = newState.doc.textBetween(newMark.from, newMark.to, undefined, " ");
36
- const wasLink = test(oldLinkText);
37
- const isLink = test(newLinkText);
38
- // remove only the link, if it was a link before too
39
- // because we don’t want to remove links that were set manually
40
- if (wasLink && !isLink) {
41
- tr.removeMark(newMark.from, newMark.to, options.type);
42
- }
43
- });
44
- // now let’s see if we can add new links
45
- const nodesInChangedRanges = findChildrenInRange(newState.doc, newRange, (node) => node.isTextblock);
46
- let textBlock;
47
- let textBeforeWhitespace;
48
- if (nodesInChangedRanges.length > 1) {
49
- // Grab the first node within the changed ranges (ex. the first of two paragraphs when hitting enter)
50
- textBlock = nodesInChangedRanges[0];
51
- textBeforeWhitespace = newState.doc.textBetween(textBlock.pos, textBlock.pos + textBlock.node.nodeSize, undefined, " ");
52
- }
53
- else if (nodesInChangedRanges.length &&
54
- // We want to make sure to include the block seperator argument to treat hard breaks like spaces
55
- newState.doc
56
- .textBetween(newRange.from, newRange.to, " ", " ")
57
- .endsWith(" ")) {
58
- textBlock = nodesInChangedRanges[0];
59
- textBeforeWhitespace = newState.doc.textBetween(textBlock.pos, newRange.to, undefined, " ");
60
- }
61
- if (textBlock && textBeforeWhitespace) {
62
- const wordsBeforeWhitespace = textBeforeWhitespace
63
- .split(" ")
64
- .filter((s) => s !== "");
65
- if (wordsBeforeWhitespace.length <= 0) {
66
- return false;
67
- }
68
- const lastWordBeforeSpace = wordsBeforeWhitespace[wordsBeforeWhitespace.length - 1];
69
- const lastWordAndBlockOffset = textBlock.pos +
70
- textBeforeWhitespace.lastIndexOf(lastWordBeforeSpace);
71
- if (!lastWordBeforeSpace) {
72
- return false;
73
- }
74
- find(lastWordBeforeSpace)
75
- .filter((link) => link.isLink)
76
- .filter((link) => {
77
- if (options.validate) {
78
- return options.validate(link.value);
79
- }
80
- return true;
81
- })
82
- // calculate link position
83
- .map((link) => ({
84
- ...link,
85
- from: lastWordAndBlockOffset + link.start + 1,
86
- to: lastWordAndBlockOffset + link.end + 1,
87
- }))
88
- // add link mark
89
- .forEach((link) => {
90
- tr.addMark(link.from, link.to, options.type.create({
91
- href: link.href,
92
- }));
93
- });
94
- }
95
- });
96
- if (!tr.steps.length) {
97
- return;
98
- }
99
- return tr;
100
- },
101
- });
102
- }
103
-
104
- class Tooltip {
105
- constructor(options) {
106
- this.preventHide = false;
107
- this.mousedownHandler = () => {
108
- this.preventHide = true;
109
- };
110
- this.dragstartHandler = () => {
111
- this.hide();
112
- };
113
- this.blurHandler = ({ event }) => {
114
- var _a;
115
- if (this.preventHide) {
116
- this.preventHide = false;
117
- return;
118
- }
119
- if ((event === null || event === void 0 ? void 0 : event.relatedTarget) &&
120
- ((_a = this.tippyWrapper.parentNode) === null || _a === void 0 ? void 0 : _a.contains(event.relatedTarget))) {
121
- return;
122
- }
123
- this.hide();
124
- };
125
- this.tippyBlurHandler = (event) => {
126
- this.blurHandler({ event });
127
- };
128
- this.editor = options.editor;
129
- this.view = this.editor.view;
130
- this.tippyWrapper = document.createElement("div");
131
- this.tippyWrapper.addEventListener("mousedown", this.mousedownHandler, {
132
- capture: true,
133
- });
134
- this.view.dom.addEventListener("dragstart", this.dragstartHandler);
135
- this.editor.on("blur", this.blurHandler);
136
- }
137
- init() {
138
- this.tippyWrapper.innerHTML = "";
139
- return { tippyModal: this.tippyWrapper, tippyInstance: this.tippyInstance };
140
- }
141
- show() {
142
- var _a;
143
- (_a = this.tippyInstance) === null || _a === void 0 ? void 0 : _a.show();
144
- return true;
145
- }
146
- hide() {
147
- setTimeout(() => { var _a; return (_a = this.tippyInstance) === null || _a === void 0 ? void 0 : _a.hide(); });
148
- return false;
149
- }
150
- createTooltip() {
151
- if (!this.editor || !this.editor.options)
152
- return;
153
- const { element: editorElement } = this.editor.options;
154
- const editorIsAttached = !!editorElement.parentElement;
155
- if (this.tippyInstance || !editorIsAttached) {
156
- return;
157
- }
158
- this.tippyInstance = tippy(editorElement, {
159
- duration: 0,
160
- getReferenceClientRect: null,
161
- content: this.tippyWrapper,
162
- interactive: true,
163
- trigger: "manual",
164
- placement: "bottom",
165
- hideOnClick: true,
166
- onClickOutside: (instance, event) => {
167
- this.hide();
168
- },
169
- onAfterUpdate: (instance, partialProps) => {
170
- this.show();
171
- },
172
- });
173
- if (this.tippyInstance.popper.firstChild) {
174
- this.tippyInstance.popper.firstChild.addEventListener("blur", this.tippyBlurHandler);
175
- }
176
- }
177
- update(view, option = {}) {
178
- var _a;
179
- this.createTooltip();
180
- option.arrow = (_a = option === null || option === void 0 ? void 0 : option.arrow) !== null && _a !== void 0 ? _a : false;
181
- if (this.tippyInstance) {
182
- this.tippyInstance.setProps({
183
- ...option,
184
- getReferenceClientRect: () => {
185
- const pos = view.state.selection.from;
186
- // width: 0 is a hack to prevent tippy display in the wrong position
187
- return { ...posToDOMRect(view, pos, pos), width: 0 };
188
- },
189
- });
190
- }
191
- return {};
192
- }
193
- destroyTooltip() {
194
- if (this.tippyInstance) {
195
- this.tippyInstance.destroy();
196
- this.tippyInstance = undefined;
197
- this.tippyWrapper.removeEventListener("mousedown", this.mousedownHandler, {
198
- capture: true,
199
- });
200
- this.view.dom.removeEventListener("dragstart", this.dragstartHandler);
201
- this.editor.off("blur", this.blurHandler);
202
- }
203
- }
204
- }
205
-
206
- function clickHandler(options) {
207
- // Create the tooltip instance
208
- let tooltip = new Tooltip(options);
209
- // Initialize the tooltip
210
- let { tippyModal, tippyInstance } = tooltip.init();
211
- return new Plugin({
212
- key: new PluginKey("handleClickHyperlink"),
213
- props: {
214
- handleClick: (view, pos, event) => {
215
- var _a, _b;
216
- if (event.button !== 0)
217
- return false;
218
- // Get the target HTML element and its position
219
- const nodeTarget = event.target;
220
- const nodePos = view.posAtDOM(nodeTarget, 0);
221
- // Find the closest link element to the target element
222
- const link = nodeTarget === null || nodeTarget === void 0 ? void 0 : nodeTarget.closest("a");
223
- // Extract attributes from the state
224
- const attrs = getAttributes(view.state, options.type.name);
225
- // Extract href and target attributes from the link element or the state
226
- const href = (_a = link === null || link === void 0 ? void 0 : link.href) !== null && _a !== void 0 ? _a : attrs.href;
227
- const target = (_b = link === null || link === void 0 ? void 0 : link.target) !== null && _b !== void 0 ? _b : attrs.target;
228
- // If there is no previewHyperlink modal provided, then open the link in new window
229
- if (!options.modal) {
230
- if (link && href) {
231
- window.open(href, target);
232
- }
233
- return true;
234
- }
235
- // if the link does not contain href attribute, hide the tooltip
236
- if (!(link === null || link === void 0 ? void 0 : link.href))
237
- return tooltip.hide();
238
- // Create a preview of the hyperlink
239
- const hyperlinkPreview = options.modal({
240
- link,
241
- nodePos,
242
- tippy: tooltip,
243
- ...options,
244
- });
245
- // If there is no hyperlink preview, hide the modal
246
- if (!hyperlinkPreview)
247
- return tooltip.hide();
248
- // Empty the modal and append the hyperlink preview box
249
- while (tippyModal.firstChild) {
250
- tippyModal.removeChild(tippyModal.firstChild);
251
- }
252
- tippyModal.append(hyperlinkPreview);
253
- // Update the modal position
254
- tooltip.update(options.editor.view);
255
- return false;
256
- },
257
- },
258
- });
259
- }
260
-
261
- function pasteHandler(options) {
262
- return new Plugin({
263
- key: new PluginKey("handlePasteHyperlink"),
264
- props: {
265
- handlePaste: (view, event, slice) => {
266
- const { state } = view;
267
- const { selection } = state;
268
- const { empty } = selection;
269
- if (empty) {
270
- return false;
271
- }
272
- let textContent = "";
273
- slice.content.forEach((node) => {
274
- textContent += node.textContent;
275
- });
276
- const link = find(textContent).find((item) => item.isLink && item.value === textContent);
277
- if (!textContent || !link) {
278
- return false;
279
- }
280
- options.editor.commands.setMark(options.type, {
281
- href: link.href,
282
- });
283
- return true;
284
- },
285
- },
286
- });
287
- }
288
-
289
- function editHyperlink(options) {
290
- var _a;
291
- const { state, dispatch } = options.editor.view;
292
- const { from, to } = state.selection;
293
- let link = null;
294
- const selectedNode = options.editor.view.domAtPos(from - 1).node;
295
- const nodeName = selectedNode === null || selectedNode === void 0 ? void 0 : selectedNode.nodeName;
296
- if (nodeName === "#text") {
297
- link = (_a = selectedNode.parentNode) === null || _a === void 0 ? void 0 : _a.closest("a");
298
- }
299
- else {
300
- link = selectedNode === null || selectedNode === void 0 ? void 0 : selectedNode.closest("a");
301
- }
302
- // If no link found and node is paragraph, try finding link in sibling/parent
303
- if (!link && options.editor.view.domAtPos(from).node.nodeName === "P" && selectedNode.nextSibling)
304
- link =
305
- selectedNode.nextSibling ||
306
- selectedNode.parentElement;
307
- if (!link)
308
- return true;
309
- const text = options.newText || (link === null || link === void 0 ? void 0 : link.innerText);
310
- // Get the position of the link in the editor view
311
- const nodePos = options.editor.view.posAtDOM(link, 0);
312
- const sanitizeURL = find(options.newURL || link.href)
313
- .filter((link) => link.isLink)
314
- .filter((link) => (options.validate ? options.validate(link.value) : true))
315
- .at(0);
316
- return options.editor
317
- .chain()
318
- .focus()
319
- .command(({ tr }) => {
320
- var _a;
321
- tr.deleteRange(nodePos, nodePos + ((_a = ((link === null || link === void 0 ? void 0 : link.innerText) || "")) === null || _a === void 0 ? void 0 : _a.length)).replaceWith(nodePos, nodePos, options.editor.schema.text(text, [
322
- options.editor.schema.marks.hyperlink.create({
323
- href: sanitizeURL === null || sanitizeURL === void 0 ? void 0 : sanitizeURL.href,
324
- }),
325
- ]));
326
- return true;
327
- })
328
- .run();
329
- }
330
-
331
- const Hyperlink = Mark.create({
332
- name: "hyperlink",
333
- priority: 1000,
334
- keepOnSplit: false,
335
- onCreate() {
336
- this.options.protocols.forEach((protocol) => {
337
- if (typeof protocol === "string") {
338
- registerCustomProtocol(protocol);
339
- return;
340
- }
341
- registerCustomProtocol(protocol.scheme, protocol.optionalSlashes);
342
- });
343
- },
344
- onDestroy() {
345
- reset();
346
- },
347
- inclusive() {
348
- return this.options.autoHyperlink;
349
- },
350
- addOptions() {
351
- return {
352
- openOnClick: true,
353
- hyperlinkOnPaste: true,
354
- autoHyperlink: true,
355
- protocols: [],
356
- HTMLAttributes: {
357
- target: "_blank",
358
- rel: "noopener noreferrer nofollow",
359
- class: null,
360
- },
361
- modals: {
362
- previewHyperlink: null,
363
- setHyperlink: null,
364
- },
365
- validate: undefined,
366
- };
367
- },
368
- addAttributes() {
369
- return {
370
- href: {
371
- default: null,
372
- },
373
- target: {
374
- default: this.options.HTMLAttributes.target,
375
- },
376
- class: {
377
- default: this.options.HTMLAttributes.class,
378
- },
379
- };
380
- },
381
- parseHTML() {
382
- return [{ tag: 'a[href]:not([href *= "javascript:" i])' }];
383
- },
384
- renderHTML({ HTMLAttributes }) {
385
- return ["a", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
386
- },
387
- addCommands() {
388
- return {
389
- setHyperlink: (attributes) => ({ editor, chain }) => {
390
- if (!this.options.modals.setHyperlink) {
391
- return chain()
392
- .setMark(this.name, attributes)
393
- .setMeta("preventAutohyperlink", true)
394
- .run();
395
- }
396
- else {
397
- this.options.modals.setHyperlink({
398
- editor,
399
- validate: this.options.validate,
400
- extentionName: this.name,
401
- attributes,
402
- Tooltip,
403
- roundArrow,
404
- });
405
- return true;
406
- }
407
- },
408
- editHyperlink: (attributes) => ({ editor }) => {
409
- return editHyperlink({
410
- ...attributes,
411
- editor,
412
- validate: this.options.validate,
413
- });
414
- },
415
- editHyperLinkText: (newText) => ({ editor }) => {
416
- return editHyperlink({
417
- editor,
418
- newText,
419
- validate: this.options.validate,
420
- });
421
- },
422
- editHyperLinkHref: (newURL) => ({ editor }) => {
423
- return editHyperlink({
424
- editor,
425
- validate: this.options.validate,
426
- newURL,
427
- });
428
- },
429
- unsetHyperlink: () => ({ chain }) => {
430
- return chain()
431
- .unsetMark(this.name, { extendEmptyMarkRange: true })
432
- .setMeta("preventAutohyperlink", true)
433
- .run();
434
- },
435
- };
436
- },
437
- addPasteRules() {
438
- return [
439
- markPasteRule({
440
- find: (text) => find(text)
441
- .filter((link) => {
442
- if (this.options.validate) {
443
- return this.options.validate(link.value);
444
- }
445
- return true;
446
- })
447
- .filter((link) => link.isLink)
448
- .map((link) => ({
449
- text: link.value,
450
- index: link.start,
451
- data: link,
452
- })),
453
- type: this.type,
454
- getAttributes: (match) => {
455
- var _a;
456
- return ({
457
- href: (_a = match.data) === null || _a === void 0 ? void 0 : _a.href,
458
- });
459
- },
460
- }),
461
- ];
462
- },
463
- addProseMirrorPlugins() {
464
- const plugins = [];
465
- if (this.options.autoHyperlink) {
466
- plugins.push(autoHyperlink({
467
- type: this.type,
468
- validate: this.options.validate,
469
- }));
470
- }
471
- if (this.options.openOnClick) {
472
- plugins.push(clickHandler({
473
- type: this.type,
474
- editor: this.editor,
475
- validate: this.options.validate,
476
- modal: this.options.modals.previewHyperlink,
477
- }));
478
- }
479
- if (this.options.hyperlinkOnPaste) {
480
- plugins.push(pasteHandler({
481
- editor: this.editor,
482
- type: this.type,
483
- }));
484
- }
485
- return plugins;
486
- },
487
- });
488
-
489
- const editHyperlinkHandler = (options) => {
490
- var _a;
491
- // Create the form
492
- const form = document.createElement("form");
493
- const buttonsWrapper = document.createElement("div");
494
- const inputsWrapper = document.createElement("div");
495
- const linkTextInput = document.createElement("input");
496
- const hrefInput = document.createElement("input");
497
- const applyButton = document.createElement("button");
498
- buttonsWrapper.classList.add("hyperlink-edit-modal__buttons-wrapper");
499
- inputsWrapper.classList.add("hyperlink-edit-modal__inputs-wrapper");
500
- linkTextInput.type = "text";
501
- linkTextInput.value = ((_a = options.link) === null || _a === void 0 ? void 0 : _a.innerText) || "";
502
- linkTextInput.placeholder = "Enter link text";
503
- hrefInput.type = "text";
504
- hrefInput.value = options.link.href;
505
- hrefInput.placeholder = "Enter href";
506
- applyButton.type = "submit";
507
- applyButton.classList.add("hyperlink-edit-modal__apply-button");
508
- applyButton.innerText = "Apply";
509
- buttonsWrapper.append(applyButton);
510
- inputsWrapper.append(linkTextInput, hrefInput);
511
- // Append to form
512
- form.append(inputsWrapper, buttonsWrapper);
513
- options.hyperlinkModal.innerHTML = "";
514
- options.hyperlinkModal.appendChild(form);
515
- hrefInput.addEventListener("keydown", () => {
516
- hrefInput.style.outlineColor = " #dadce0";
517
- });
518
- hrefInput.addEventListener("keydown", () => {
519
- linkTextInput.style.outlineColor = " #dadce0";
520
- });
521
- // Handle form submission
522
- form.addEventListener("submit", (e) => {
523
- e.preventDefault();
524
- const newLinkText = linkTextInput.value;
525
- const newHref = hrefInput.value;
526
- if (!newHref || !newLinkText) {
527
- if (!newHref)
528
- hrefInput.style.outlineColor = "red";
529
- if (!newLinkText)
530
- linkTextInput.style.outlineColor = "red";
531
- return;
532
- }
533
- const sanitizeURL = find(newHref)
534
- .filter((link) => link.isLink)
535
- .filter((link) => {
536
- if (options.validate) {
537
- return options.validate(link.value);
538
- }
539
- return true;
540
- })
541
- .at(0);
542
- if (!(sanitizeURL === null || sanitizeURL === void 0 ? void 0 : sanitizeURL.href)) {
543
- hrefInput.style.outlineColor = "red";
544
- return;
545
- }
546
- options.editor.chain().focus().extendMarkRange("hyperlink").editHyperlink({
547
- newURL: newHref,
548
- newText: newLinkText,
549
- });
550
- options.tippy.hide();
551
- });
552
- // Show tooltip
553
- options.tippy.update(options.editor.view, {
554
- arrow: roundArrow,
555
- });
556
- setTimeout(() => linkTextInput.focus());
557
- };
558
-
559
- // prettier-ignore
560
- const Copy = ({ size = 24, fill = 'rgb(104, 81, 255)' } = {}) => {
561
- return `
562
- <svg fill=${fill} viewBox="0 0 24 24" width=${size} xmlns="http://www.w3.org/2000/svg">
563
- <path d="m13 20a5.006 5.006 0 0 0 5-5v-8.757a3.972 3.972 0 0 0 -1.172-2.829l-2.242-2.242a3.972 3.972 0 0 0 -2.829-1.172h-4.757a5.006 5.006 0 0 0 -5 5v10a5.006 5.006 0 0 0 5 5zm-9-5v-10a3 3 0 0 1 3-3s4.919.014 5 .024v1.976a2 2 0 0 0 2 2h1.976c.01.081.024 9 .024 9a3 3 0 0 1 -3 3h-6a3 3 0 0 1 -3-3zm18-7v11a5.006 5.006 0 0 1 -5 5h-9a1 1 0 0 1 0-2h9a3 3 0 0 0 3-3v-11a1 1 0 0 1 2 0z"/>
564
- </svg>
565
- `;
566
- };
567
- const LinkSlash = ({ size = 24, fill = "rgb(104, 81, 255)", } = {}) => {
568
- return `
569
- <svg fill=${fill} viewBox="0 0 24 24" width=${size} xmlns="http://www.w3.org/2000/svg">
570
- <path d="M11.953,17.993c-1.557-.074-3.021-.722-4.121-1.823-.23-.23-.446-.482-.641-.748-.325-.446-.227-1.072,.22-1.397,.446-.325,1.071-.227,1.397,.219,.129,.178,.274,.349,.437,.511,.749,.75,1.744,1.19,2.802,1.24,.552,.026,.979,.495,.952,1.046-.025,.536-.468,.953-.998,.953h-.048ZM1.831,13.331C.65,14.511,0,16.081,0,17.75c0,1.669,.65,3.239,1.831,4.419,1.18,1.181,2.749,1.831,4.419,1.831s3.239-.65,4.419-1.831l1.058-1.058c.391-.391,.391-1.023,0-1.414s-1.023-.391-1.414,0l-1.058,1.058c-.803,.803-1.87,1.245-3.005,1.245s-2.202-.442-3.005-1.245-1.245-1.87-1.245-3.005,.442-2.203,1.245-3.005l3.037-3.038c.391-.391,.391-1.024,0-1.414-.391-.391-1.023-.391-1.414,0l-3.037,3.038ZM.293,1.707L22.293,23.707c.195,.195,.451,.293,.707,.293s.512-.098,.707-.293c.391-.391,.391-1.023,0-1.414l-6.581-6.581,5.043-5.043c1.181-1.18,1.831-2.75,1.831-4.419,0-1.669-.65-3.239-1.831-4.419C20.989,.65,19.42,0,17.75,0s-3.239,.65-4.419,1.831l-1.058,1.058c-.391,.391-.391,1.023,0,1.414s1.023,.391,1.414,0l1.058-1.058c.803-.803,1.87-1.245,3.005-1.245s2.202,.442,3.005,1.245,1.245,1.87,1.245,3.005-.442,2.203-1.245,3.005l-5.043,5.043-5.852-5.852c1.612-.808,3.575-.52,4.894,.798,.162,.162,.308,.333,.437,.511,.196,.269,.501,.411,.81,.411,.204,0,.41-.063,.588-.192,.446-.325,.545-.951,.22-1.397-.194-.266-.41-.518-.641-.748-2.095-2.095-5.297-2.408-7.76-.835L1.707,.293c-.391-.391-1.023-.391-1.414,0s-.391,1.023,0,1.414Z"/>
571
- </svg>
572
- `;
573
- };
574
- const Pencil = ({ size = 24, fill = "rgb(104, 81, 255)", } = {}) => {
575
- return `
576
- <svg fill=${fill} viewBox="0 0 24 24" width=${size} xmlns="http://www.w3.org/2000/svg">
577
- <path d="M22.853,1.148a3.626,3.626,0,0,0-5.124,0L1.465,17.412A4.968,4.968,0,0,0,0,20.947V23a1,1,0,0,0,1,1H3.053a4.966,4.966,0,0,0,3.535-1.464L22.853,6.271A3.626,3.626,0,0,0,22.853,1.148ZM5.174,21.122A3.022,3.022,0,0,1,3.053,22H2V20.947a2.98,2.98,0,0,1,.879-2.121L15.222,6.483l2.3,2.3ZM21.438,4.857,18.932,7.364l-2.3-2.295,2.507-2.507a1.623,1.623,0,1,1,2.295,2.3Z"/>
578
- </svg>
579
- `;
580
- };
581
-
582
- function previewHyperlinkModal(options) {
583
- const href = options.link.href;
584
- const hyperlinkModal = document.createElement("div");
585
- const removeButton = document.createElement("button");
586
- const copyButton = document.createElement("button");
587
- const editButton = document.createElement("button");
588
- const newBubble = document.createElement("div");
589
- newBubble.classList.add("hyperlink-preview-modal__metadata");
590
- const hrefTitle = document.createElement("a");
591
- hrefTitle.setAttribute("target", "_blank");
592
- hrefTitle.setAttribute("rel", "noreferrer");
593
- hrefTitle.setAttribute("href", href);
594
- hrefTitle.innerText = href;
595
- newBubble.append(hrefTitle);
596
- fetch("/api/metadata", {
597
- method: "POST",
598
- headers: { "Content-Type": "application/json" },
599
- body: JSON.stringify({ url: href }),
600
- })
601
- .then((response) => response.json())
602
- .then((data) => {
603
- // Create a new bubble with the title
604
- hrefTitle.setAttribute("href", href);
605
- hrefTitle.innerText = data.title || data["og:title"] || href;
606
- newBubble.replaceChildren(hrefTitle);
607
- // Create an image element if image exists in metadata
608
- if (data.icon || data.image || data["og:image"]) {
609
- const img = document.createElement("img");
610
- img.src = data.icon || data.image || data["og:image"];
611
- img.alt = data.title || data["og:title"] || "hyperlink image";
612
- newBubble.appendChild(img);
613
- }
614
- })
615
- .catch((error) => {
616
- console.error("Error fetching metadata:", error);
617
- });
618
- hyperlinkModal.classList.add("hyperlink-preview-modal");
619
- removeButton.classList.add("hyperlink-preview-modal__remove-button");
620
- removeButton.innerHTML = LinkSlash();
621
- editButton.classList.add("hyperlink-preview-modal__edit-button");
622
- editButton.innerHTML = Pencil();
623
- copyButton.classList.add("hyperlink-preview-modal__copy-button");
624
- copyButton.innerHTML = Copy();
625
- removeButton.addEventListener("click", () => {
626
- options.tippy.hide();
627
- return options.editor.chain().focus().unsetHyperlink().run();
628
- });
629
- editButton.addEventListener("click", () => editHyperlinkHandler({ ...options, hyperlinkModal }));
630
- copyButton.addEventListener("click", () => {
631
- options.tippy.hide();
632
- navigator.clipboard.writeText(href);
633
- });
634
- hyperlinkModal.append(newBubble, copyButton, editButton, removeButton);
635
- return hyperlinkModal;
636
- }
637
-
638
- let tooltip = undefined;
639
- function setHyperlinkModal(options) {
640
- // Create the tooltip instance
641
- if (!tooltip)
642
- tooltip = new options.Tooltip(options);
643
- // Initialize the tooltip
644
- let { tippyModal } = tooltip.init();
645
- const hyperlinkModal = document.createElement("div");
646
- const buttonsWrapper = document.createElement("div");
647
- const inputsWrapper = document.createElement("div");
648
- hyperlinkModal.classList.add("hyperlink-set-modal");
649
- buttonsWrapper.classList.add("hyperlink-set-modal__buttons-wrapper");
650
- inputsWrapper.classList.add("hyperlink-set-modal__inputs-wrapper");
651
- // create a form that contain url input and a button for submit
652
- const form = document.createElement("form");
653
- const input = document.createElement("input");
654
- const button = document.createElement("button");
655
- input.setAttribute("type", "text");
656
- input.setAttribute("placeholder", "https://example.com");
657
- button.setAttribute("type", "submit");
658
- button.innerText = "Submit";
659
- inputsWrapper.append(input);
660
- buttonsWrapper.append(button);
661
- form.append(inputsWrapper, buttonsWrapper);
662
- hyperlinkModal.append(form);
663
- tippyModal.innerHTML = "";
664
- tippyModal.append(hyperlinkModal);
665
- tooltip.update(options.editor.view, {
666
- arrow: options.roundArrow,
667
- });
668
- // make sure
669
- setTimeout(() => {
670
- input.focus();
671
- input.style.outlineColor = " #dadce0";
672
- }, 100);
673
- input.addEventListener("keydown", () => {
674
- input.style.outlineColor = " #dadce0";
675
- });
676
- // event listenr for submit button
677
- form.addEventListener("submit", (event) => {
678
- event.preventDefault();
679
- const url = input.value;
680
- if (!url) {
681
- input.style.outlineColor = "red";
682
- return;
683
- }
684
- const sanitizeURL = find(url)
685
- .filter((link) => link.isLink)
686
- .filter((link) => {
687
- if (options.validate) {
688
- return options.validate(link.value);
689
- }
690
- return true;
691
- })
692
- .at(0);
693
- if (!(sanitizeURL === null || sanitizeURL === void 0 ? void 0 : sanitizeURL.href)) {
694
- input.style.outlineColor = "red";
695
- return;
696
- }
697
- tooltip === null || tooltip === void 0 ? void 0 : tooltip.hide();
698
- return options.editor
699
- .chain()
700
- .setMark(options.extentionName, { href: sanitizeURL.href })
701
- .setMeta("preventautohyperlink", true)
702
- .run();
703
- });
704
- }
705
-
706
- export { Hyperlink, Tooltip, previewHyperlinkModal, setHyperlinkModal };
707
- //# sourceMappingURL=index.js.map
1
+ import{Mark as Cr,mergeAttributes as Pr}from"@tiptap/core";import{registerCustomProtocol as Tr}from"linkifyjs";var R="hyperlink",ee="preventAutolink";var B="noopener,noreferrer";function Ue({markName:e,urls:t}){let r=(n,{commands:i,tr:s,dispatch:a})=>{if(!n.href)return!1;let[l]=t.forWrite({kind:"href",href:n.href});if(!l)return!1;let c=i.setMark(e,{...n,href:l.href});return c&&a&&s.setMeta(ee,!0),c},o=({commands:n,tr:i,dispatch:s})=>{let a=n.unsetMark(e,{extendEmptyMarkRange:!0});return a&&s&&i.setMeta(ee,!0),a};return{set:n=>i=>r(n,i),unset:()=>n=>o(n),toggle:n=>i=>i.editor.isActive(e)?o(i):r(n,i)}}import{computePosition as ct,flip as ut,offset as mt,shift as dt}from"@floating-ui/dom";var ft=400,Se="visible",N=null,te=null;function yt(){return N||(N=document.createElement("div"),N.className="floating-tooltip",N.setAttribute("role","tooltip"),document.body.append(N)),N}function Ne(){te!==null&&(clearTimeout(te),te=null)}async function gt(e,t){if(!e.isConnected)return;let r=yt();r.textContent=t;let{x:o,y:n}=await ct(e,r,{strategy:"fixed",placement:"top",middleware:[mt(6),ut(),dt({padding:8})]});r.style.left=`${o}px`,r.style.top=`${n}px`,r.classList.add(Se)}function H(){Ne(),N?.classList.remove(Se)}function Le(e,t){let r=()=>{Ne(),te=setTimeout(()=>{te=null,gt(e,t)},ft)},o=()=>{e.matches(":focus-visible")&&r()};return e.addEventListener("mouseenter",r),e.addEventListener("focus",o),e.addEventListener("mouseleave",H),e.addEventListener("blur",H),e.addEventListener("pointerdown",H),e.addEventListener("click",H),()=>{e.removeEventListener("mouseenter",r),e.removeEventListener("focus",o),e.removeEventListener("mouseleave",H),e.removeEventListener("blur",H),e.removeEventListener("pointerdown",H),e.removeEventListener("click",H),H()}}import{arrow as vt,autoUpdate as _e,computePosition as Et,flip as Lt,hide as xt,offset as Ht,shift as bt}from"@floating-ui/dom";function ht(){let e=null,t=new Set,r=()=>e===null?{kind:"idle"}:{kind:"mounted",popoverKind:e.kind,element:e.metadata.element,referenceElement:e.metadata.referenceElement},o=()=>{let n=r();t.forEach(i=>i(n))};return{adopt(n,i,s){let a=e;return e={popover:n,kind:i,metadata:s},a&&a.popover!==n&&a.popover.destroy(),o(),()=>{e?.popover===n&&(e=null,o())}},close(){if(e===null)return;let{popover:n}=e;e=null,n.hide(),o()},reposition(n,i){e?.popover.updateReference(n,i)},getState:r,subscribe(n){return t.add(n),()=>{t.delete(n)}}}}var kt=ht();function h(){return kt}var We=8,wt=9999,At=8,Ct="-5px",Pt=50,Tt=150,De={top:"bottom",bottom:"top",left:"right",right:"left"};function Fe(e){return{getBoundingClientRect:()=>{let{x:t,y:r,width:o,height:n}=e.getBoundingClientRect();return{width:o,height:n,top:r,right:t+o,bottom:r+n,left:t,x:t,y:r}},contextElement:e.contextElement||document.body}}function Rt(e){let t=e.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');if(!t.length)return[];let r=[];return t.forEach((o,n)=>{let i=s=>{if(s.key!=="Tab")return;s.preventDefault();let a=s.shiftKey?(n-1+t.length)%t.length:(n+1)%t.length;t[a].focus()};o.addEventListener("keydown",i),r.push(()=>o.removeEventListener("keydown",i))}),r}function _(e){let t=h(),{referenceElement:r,coordinates:o,content:n,placement:i="bottom-start",offset:s=We,showArrow:a=!1,className:l="",zIndex:c=wt,role:p,ariaLabel:m,ignoreOutsideClickOn:v,crossAxisShift:f=!0,onShow:x,onHide:M}=e;if(!r&&!o)throw new Error("Either referenceElement or coordinates must be provided");let L=r||Fe(o),A=v?Array.isArray(v)?[...v]:[v]:r?[r]:[],C=[],g=!1,k=!1,y=null,b=E=>E?u.contains(E)?!1:!A.some(T=>T===E||T.contains(E)):!0,u=document.createElement("div");u.className=`floating-popover ${l}`.trim(),p&&u.setAttribute("role",p),m&&u.setAttribute("aria-label",m),u.setAttribute("tabindex","-1"),u.style.zIndex=String(c),u.style.position="fixed";let P=null;a&&(P=document.createElement("div"),P.className="floating-popover-arrow",u.appendChild(P));let O=document.createElement("div");O.className="floating-popover-content",u.appendChild(O);let G=[],ae=()=>{for(;G.length;)G.pop()()},le=E=>{ae(),O.innerHTML="",O.appendChild(E),G.push(...Rt(u))};le(n);let Y=async()=>{if(!g)return;let E=[Ht(s),Lt(),bt({padding:At,crossAxis:f})];P&&E.push(vt({element:P})),E.push(xt({strategy:"referenceHidden"}));let{x:T,y:ce,placement:S,middlewareData:Q}=await Et(L,u,{placement:i,strategy:"fixed",middleware:E});if(!g)return;Object.assign(u.style,{left:`${T}px`,top:`${ce}px`}),u.style.visibility=Q.hide?.referenceHidden?"hidden":"visible";let Ee=S.split("-")[0];if(u.style.transformOrigin=De[Ee]??"center",P&&Q.arrow){let{x:Oe,y:Ie}=Q.arrow,pt=De[Ee];P.className=`floating-popover-arrow floating-popover-arrow-${Ee}`,Object.assign(P.style,{left:Oe!=null?`${Oe}px`:"",top:Ie!=null?`${Ie}px`:"",right:"",bottom:"",[pt]:Ct})}},X=null,pe=null,j=null,lt=()=>{pe&&(clearTimeout(pe),pe=null),j&&(u.removeEventListener("transitionend",j),j=null)},ve=()=>{lt(),u.remove()},J=()=>{g&&(g=!1,k=!0,y?.(),y=null,ae(),C.forEach(E=>E()),C.length=0,X?.(),X=null,u.classList.remove("visible"),j=E=>{E.target===u&&ve()},u.addEventListener("transitionend",j),pe=setTimeout(ve,Tt),M?.())},Me={element:u,show:()=>{if(g||k)return;document.body.appendChild(u),g=!0,y=_e(L,u,Y),requestAnimationFrame(()=>u.classList.add("visible")),setTimeout(()=>{if(!g)return;let T=S=>{b(S.target)&&J()},ce=S=>{if(!S.touches.length)return;let Q=document.elementFromPoint(S.touches[0].clientX,S.touches[0].clientY);b(Q)&&J()};document.addEventListener("mousedown",T,{passive:!0}),document.addEventListener("touchstart",ce,{passive:!0}),C.push(()=>document.removeEventListener("mousedown",T),()=>document.removeEventListener("touchstart",ce))},Pt);let E=T=>{T.key==="Escape"&&(T.preventDefault(),J())};u.addEventListener("keydown",E),C.push(()=>u.removeEventListener("keydown",E)),x?.()},hide:J,destroy:()=>{J(),k=!0,ve()},isVisible:()=>g,setContent:le,updateReference:(E,T)=>{E?L=E:T&&(L=Fe(T)),g?(y?.(),y=_e(L,u,Y)):Y()}};return X=t.adopt(Me,"unknown",{element:u,referenceElement:r??null}),Me}var Be="[extension-hyperlink]",D={warn:(...e)=>console.warn(Be,...e),error:(...e)=>console.error(Be,...e)};var xe=async(e,t)=>{try{if(navigator.clipboard?.writeText)await navigator.clipboard.writeText(e);else{let r=document.createElement("textarea");r.value=e,document.body.appendChild(r);let o=!1;try{r.select(),o=document.execCommand("copy")}finally{document.body.removeChild(r)}if(!o)return D.warn("copyToClipboard failed","execCommand returned false"),t?.(!1),!1}return t?.(!0),!0}catch(r){return D.warn("copyToClipboard failed",r),t?.(!1),!1}};var d=(e,t)=>{let r=document.createElement(e);return t&&Object.assign(r,t),r};var He=({size:e=24,fill:t="currentColor"}={})=>`
2
+ <svg xmlns="http://www.w3.org/2000/svg" height="${e}" viewBox="0 -960 960 960" width="${e}" fill="${t}" aria-hidden="true">
3
+ <path d="M360-240q-33 0-56.5-23.5T280-320v-480q0-33 23.5-56.5T360-880h360q33 0 56.5 23.5T800-800v480q0 33-23.5 56.5T720-240H360Zm0-80h360v-480H360v480ZM200-80q-33 0-56.5-23.5T120-160v-560h80v560h440v80H200Zm160-240v-480 480Z"/>
4
+ </svg>
5
+ `,be=({size:e=24,fill:t="currentColor"}={})=>`
6
+ <svg xmlns="http://www.w3.org/2000/svg" height="${e}" viewBox="0 -960 960 960" width="${e}" fill="${t}" aria-hidden="true">
7
+ <path d="m770-302-60-62q40-11 65-42.5t25-73.5q0-50-35-85t-85-35H520v-80h160q83 0 141.5 58.5T880-480q0 57-29.5 105T770-302ZM634-440l-80-80h86v80h-6ZM792-56 56-792l56-56 736 736-56 56ZM440-280H280q-83 0-141.5-58.5T80-480q0-69 42-123t108-71l74 74h-24q-50 0-85 35t-35 85q0 50 35 85t85 35h160v80ZM320-440v-80h65l79 80H320Z"/>
8
+ </svg>
9
+ `,ue=({size:e=24,fill:t="currentColor"}={})=>`
10
+ <svg xmlns="http://www.w3.org/2000/svg" height="${e}" viewBox="0 -960 960 960" width="${e}" fill="${t}" aria-hidden="true">
11
+ <path d="M440-280H280q-83 0-141.5-58.5T80-480q0-83 58.5-141.5T280-680h160v80H280q-50 0-85 35t-35 85q0 50 35 85t85 35h160v80ZM320-440v-80h320v80H320Zm200 160v-80h160q50 0 85-35t35-85q0-50-35-85t-85-35H520v-80h160q83 0 141.5 58.5T880-480q0 83-58.5 141.5T680-280H520Z"/>
12
+ </svg>
13
+ `,we=({size:e=24,fill:t="currentColor"}={})=>`
14
+ <svg xmlns="http://www.w3.org/2000/svg" height="${e}" viewBox="0 -960 960 960" width="${e}" fill="${t}" aria-hidden="true">
15
+ <path d="M200-200h57l391-391-57-57-391 391v57Zm-80 80v-170l528-527q12-11 26.5-17t30.5-6q16 0 31 6t26 18l55 56q12 11 17.5 26t5.5 30q0 16-5.5 30.5T817-647L290-120H120Zm640-584-56-56 56 56Zm-141 85-28-29 57 57-29-28Z"/>
16
+ </svg>
17
+ `,$e=({size:e=24,fill:t="currentColor"}={})=>`
18
+ <svg xmlns="http://www.w3.org/2000/svg" height="${e}" viewBox="0 -960 960 960" width="${e}" fill="${t}" aria-hidden="true">
19
+ <path d="M160-560v-80h360v80H160Zm0 160v-80h640v80H160Z"/>
20
+ </svg>
21
+ `;import{find as It}from"linkifyjs";var Mt=/^\+\d[\d\s.\-()]{4,18}\d$/,Ot=/\D/g,$=e=>{if(!Mt.test(e))return{ok:!1};let t=e.replace(Ot,"");return t.length<8||t.length>15?{ok:!1}:{ok:!0,href:`tel:+${t}`}};var Ut=/^([a-z][a-z0-9+.-]*):/i,St=/^([#?/]|\.\.?\/)/,Nt=e=>{if(e.startsWith("//"))return!0;let t=e.match(Ut);if(!t)return!1;if(e.includes("://"))return!0;let r=t[1].toLowerCase();return!(r==="localhost"||r.includes("."))},_t=e=>{if(!e.includes("@"))return{ok:!1};let t=It(e);if(t.length!==1)return{ok:!1};let r=t[0];return r.type!=="email"||r.value!==e?{ok:!1}:{ok:!0,href:r.href}},F="https",q=(e,t=F)=>{let r=e.trim();if(!r)return"";let o=$(r);if(o.ok)return o.href;let n=_t(r);return n.ok?n.href:Nt(r)||St.test(r)?r:`${t}://${r}`},Ae=(e,t=F)=>e.type==="url"?q(e.value,t):e.href;var Dt={"mailto:":{type:"email",title:"Email",category:"communication"},"tel:":{type:"phone",title:"Phone",category:"communication"},"telprompt:":{type:"phone",title:"Phone",category:"communication"},"sms:":{type:"sms",title:"SMS",category:"communication"},"facetime:":{type:"facetime",title:"FaceTime",category:"communication"},"facetime-audio:":{type:"facetime-audio",title:"FaceTime Audio",category:"communication"}},Ft={"whatsapp:":{type:"whatsapp",title:"WhatsApp",category:"social"},"tg:":{type:"telegram",title:"Telegram",category:"social"},"discord:":{type:"discord",title:"Discord",category:"social"},"skype:":{type:"skype",title:"Skype",category:"social"},"slack:":{type:"slack",title:"Slack",category:"social"},"twitter:":{type:"twitter",title:"Twitter",category:"social"},"fb:":{type:"facebook",title:"Facebook",category:"social"},"instagram:":{type:"instagram",title:"Instagram",category:"social"},"linkedin:":{type:"linkedin",title:"LinkedIn",category:"social"},"snapchat:":{type:"snapchat",title:"Snapchat",category:"social"},"reddit:":{type:"reddit",title:"Reddit",category:"social"},"tiktok:":{type:"tiktok",title:"TikTok",category:"social"}},Wt={"zoommtg:":{type:"zoom",title:"Zoom Meeting",category:"communication"},"zoomus:":{type:"zoom",title:"Zoom",category:"communication"},"msteams:":{type:"teams",title:"Microsoft Teams",category:"communication"},"webex:":{type:"webex",title:"Cisco Webex",category:"communication"}},Bt={"calshow:":{type:"calendar",title:"Calendar",category:"apple"},"x-apple-calevent:":{type:"calendar",title:"Calendar Event",category:"apple"},"x-apple-reminder:":{type:"reminders",title:"Reminders",category:"apple"},"contacts:":{type:"contacts",title:"Contacts",category:"apple"},"maps:":{type:"maps",title:"Maps",category:"apple"},"map:":{type:"maps",title:"Maps",category:"apple"},"music:":{type:"music",title:"Apple Music",category:"apple"},"videos:":{type:"apple-tv",title:"Apple TV",category:"apple"},"mobilenotes:":{type:"notes",title:"Notes",category:"apple"},"photos-redirect:":{type:"photos",title:"Photos",category:"apple"},"shortcuts:":{type:"shortcuts",title:"Shortcuts",category:"apple"},"itms-apps:":{type:"app-store",title:"App Store",category:"apple"}},$t={"github:":{type:"github",title:"GitHub",category:"development"},"gitlab:":{type:"gitlab",title:"GitLab",category:"development"},"vscode:":{type:"vscode",title:"VS Code",category:"development"},"notion:":{type:"notion",title:"Notion",category:"productivity"},"obsidian:":{type:"obsidian",title:"Obsidian",category:"productivity"},"figma:":{type:"figma",title:"Figma",category:"development"}},qt={"youtube:":{type:"youtube",title:"YouTube",category:"entertainment"},"spotify:":{type:"spotify",title:"Spotify",category:"entertainment"},"netflix:":{type:"netflix",title:"Netflix",category:"entertainment"},"twitch:":{type:"twitch",title:"Twitch",category:"entertainment"}},Kt={"amazon:":{type:"amazon",title:"Amazon",category:"shopping"},"uber:":{type:"uber",title:"Uber",category:"other"},"lyft:":{type:"lyft",title:"Lyft",category:"other"}},zt={...Dt,...Ft,...Wt,...Bt,...$t,...qt,...Kt},Vt={"wa.me":{type:"whatsapp",title:"WhatsApp",category:"social"},"t.me":{type:"telegram",title:"Telegram",category:"social"},"discord.gg":{type:"discord",title:"Discord Invite",category:"social"},"zoom.us":{type:"zoom",title:"Zoom Meeting",category:"communication"},"meet.google.com":{type:"meet",title:"Google Meet",category:"communication"},"teams.microsoft.com":{type:"teams",title:"Microsoft Teams",category:"communication"},"github.com":{type:"github",title:"GitHub",category:"development"},"gitlab.com":{type:"gitlab",title:"GitLab",category:"development"},"figma.com":{type:"figma",title:"Figma",category:"development"},"notion.so":{type:"notion",title:"Notion",category:"productivity"},"twitter.com":{type:"twitter",title:"Twitter",category:"social"},"x.com":{type:"twitter",title:"X (Twitter)",category:"social"},"instagram.com":{type:"instagram",title:"Instagram",category:"social"},"linkedin.com":{type:"linkedin",title:"LinkedIn",category:"social"},"youtube.com":{type:"youtube",title:"YouTube",category:"entertainment"},"spotify.com":{type:"spotify",title:"Spotify",category:"entertainment"}},Ce=e=>{let t=e.toLowerCase();for(let[r,o]of Object.entries(zt))if(t.startsWith(r))return o;try{let o=new URL(e).hostname.toLowerCase(),n=o.startsWith("www.")?o.slice(4):o;for(let[i,s]of Object.entries(Vt))if(n===i||n.endsWith(`.${i}`))return s}catch{}return null};import{find as Zt}from"linkifyjs";var qe=/^\s*(javascript|data|vbscript|file|blob):/i,w=e=>typeof e!="string"||e.length===0?!1:!qe.test(e.replace(/[\u0000-\u0020]+/g,"")),Gt=new Set(["http","https","ftp","ftps"]),Yt=/^\d{1,3}(\.\d{1,3}){3}$/,Xt=e=>Ce(e)!==null,jt=e=>{let t;try{t=new URL(e)}catch{return!1}let r=t.hostname;return r?r==="localhost"||Yt.test(r)||r.includes(":")?!0:r.includes("."):!1},I=(e,t)=>{let r=e.trim();if(!r||!w(r))return!1;try{if($(r).ok||Xt(e))return t?.customValidator?t.customValidator(e):!0;let o=Zt(e).find(n=>n.isLink&&(t?.customValidator?t.customValidator(n.value):!0));return o?.href?Qt(o.href)?jt(o.href):!0:!1}catch(o){return D.error("validateURL threw",o),!1}},Jt=e=>{if(!e.trim())return null;let t=e.indexOf(":");return t===-1?null:e.substring(0,t).toLowerCase()},Qt=e=>{let t=Jt(e);return t!==null&&Gt.has(t)};var er=e=>{let t=d("div",{className:"hyperlink-create-popover"}),r=d("div",{className:"buttons-wrapper"}),o=d("div",{className:"inputs-wrapper"}),n=d("form",{}),i=d("input",{type:"text",inputMode:"url",name:"hyperlink-url",placeholder:"https://example.com",autocomplete:"new-password",spellcheck:!1,value:e}),s=d("button",{type:"submit",textContent:"Apply",disabled:e.trim().length===0}),a=d("div",{className:"error-message",textContent:"Please enter a valid URL"}),l=d("div",{className:"search-icon",innerHTML:ue({size:24})});return o.append(l,i,a),r.append(s),n.append(o,r),t.append(n),{root:t,form:n,input:i,button:s,inputsWrapper:o,errorMessage:a}},tr=(e,t)=>{let{form:r,input:o,button:n,inputsWrapper:i,errorMessage:s}=e,{editor:a}=t,l=()=>{i.classList.add("error"),s.classList.add("show")},c=()=>{i.classList.remove("error"),s.classList.remove("show")},p=()=>{let m=o.value.trim().length>0;n.disabled=!m,m&&c()};o.addEventListener("input",p),o.addEventListener("paste",()=>setTimeout(p,0)),i.addEventListener("click",()=>o.focus()),o.addEventListener("keydown",m=>{m.key==="Escape"&&(m.preventDefault(),h().close(),a.commands.focus())}),r.addEventListener("submit",m=>{if(m.preventDefault(),n.disabled)return;let v=o.value.trim();if(!I(v,{customValidator:t.validate})){l();return}if(!a.can().setHyperlink({href:v})){l();return}let f=a.chain().setHyperlink({href:v});if(a.state.selection.empty&&f.insertContent({type:"text",text:v}),!f.run()){l();return}h().close(),a.commands.focus()})};function re(e){let t=er(e.attributes?.href??"");return tr(t,e),t.root}function K(e,t){let r=e.extensionManager.extensions.find(o=>o.name===R);if(!r)throw new Error(`${t}: Hyperlink extension not registered on editor`);return r.options}var U=null,z=null;function V(e,t){U={editor:e,popover:t},rr()}function rr(){z||(z=h().subscribe(e=>{U&&e.kind==="mounted"&&e.element===U.popover.element||(U=null,z?.(),z=null)}))}function Ke(e){if(!U||U.editor!==e)return;let{popover:t}=U;U=null,z?.(),z=null;let r=h(),o=r.getState();o.kind==="mounted"&&o.element===t.element&&r.close()}var or=100;function de(e,t={}){let r=K(e,"openCreateHyperlink"),o=r.popovers.createHyperlink??re,n={editor:e,validate:r.validate,extensionName:R,attributes:t},i=o(n);if(!i)return!1;let{from:s,to:a}=e.state.selection,l=null,c=_({coordinates:{getBoundingClientRect:()=>{try{let p=e.view.coordsAtPos(s),m=e.view.coordsAtPos(a);return{x:Math.min(p.left,m.left),y:p.top,width:Math.abs(m.left-p.left),height:m.bottom-p.top}}catch{return queueMicrotask(()=>c.hide()),{x:-9999,y:-9999,width:0,height:0}}},contextElement:e.view.dom},content:i,placement:"bottom",showArrow:!0,role:"dialog",ariaLabel:"Add link",onShow:()=>{let p=i.querySelector("input");p&&(l=setTimeout(()=>p.focus(),or))},onHide:()=>{l!=null&&(clearTimeout(l),l=null),H()}});return h().adopt(c,"create",{element:c.element,referenceElement:null}),V(e,c),c.show(),!0}import{getMarkRange as lr}from"@tiptap/core";import{find as nr}from"linkifyjs";var ir=/\b[a-zA-Z][a-zA-Z0-9+.-]*:[^\s]+/g,sr=/[.,;:!?)\]}]+$/,ar=e=>{let t=e.value.match(sr);if(!t)return e;let r=t[0].length;return{...e,value:e.value.slice(0,-r),href:e.href.endsWith(t[0])?e.href.slice(0,-r):e.href,end:e.end-r}},Pe=e=>{let t=[],r=nr(e).filter(n=>n.isLink);if(t.push(...r),e.includes(":"))for(let n of e.matchAll(ir)){let i=n[0],s=n.index,a=s+i.length;!r.some(c=>s>=c.start&&a<=c.end)&&I(i)&&t.push({type:"url",href:i,value:i,start:s,end:a,isLink:!0})}let o=$(e);return o.ok&&t.push({type:"phone",href:o.href,value:e,start:0,end:e.length,isLink:!0}),t.map(ar)};function ze(e={}){let t=e.defaultProtocol??F,r=e.protocols??[],o=e.isAllowedUri;return n=>w(n)?o?o(n,{defaultValidate:w,protocols:r,defaultProtocol:t}):!0:!1}function fe(e={}){let t=e.defaultProtocol??F,r=e.gate??w,o=(n,i)=>{if(!r(n))return!1;let s=i?.validate??e.validate;if(s&&!s(n))return!1;let a=e.shouldAutoLink;return!(a&&!a(n))};return{forWrite(n,i){if(n.kind==="href"){let s=n.href.trim();if(!s)return[];let a=q(s,t);if(!r(a))return[];let l=i?.validate??e.validate;return l&&!l(a)?[]:[{href:a,start:0,end:n.href.length}]}if(n.kind==="match"){let s=Ae(n.match,t);return o(s,i)?[{href:s,start:0,end:n.match.value.length}]:[]}return Pe(n.text).filter(s=>s.isLink).map(s=>({href:Ae(s,t),start:s.start,end:s.end})).filter(s=>o(s.href,i))},forRead(n){let i=typeof n=="string"?n:"";return{navigable:w(i)&&r(i)}},detect(n){return Pe(n).some(i=>i.isLink)}}}var ye=(e={})=>({tr:t,dispatch:r})=>{let{newURL:o,newText:n,title:i,image:s,validate:a,markName:l=R,urls:c=fe()}=e;if(o&&!I(o,{customValidator:a}))return!1;let p=t.doc.type.schema.marks[l];if(!p)return!1;let{from:m}=t.selection,v=t.doc.resolve(m),f=lr(v,p);if(!f)return!1;let x;try{x=t.doc.nodeAt(f.from)?.marks.find(k=>k.type===p)}catch(k){return D.warn("editHyperlink: failed to read mark at position",k),!1}if(!x)return!1;let M=x.attrs.href;if(o){let[k]=c.forWrite({kind:"href",href:o},{validate:a});if(!k)return!1;M=k.href}if(!r)return!0;let L=t.doc.textBetween(f.from,f.to),A=n||L,C=p.create({...x.attrs,href:M,...i!==void 0&&{title:i},...s!==void 0&&{image:s}});if(A===L)return t.removeMark(f.from,f.to,p),t.addMark(f.from,f.to,C),!0;let g=t.doc.nodeAt(f.from)?.marks.filter(k=>k.type!==p)??[];return t.replaceWith(f.from,f.to,t.doc.type.schema.text(A,[...g,C])),!0};function Ve(e){return{setHyperlink:e.set,unsetHyperlink:e.unset,toggleHyperlink:e.toggle,setLink:e.set,unsetLink:e.unset,toggleLink:e.toggle}}function Ze(e){let t={markName:e.markName,urls:e.urls,validate:e.validate};return{editHyperlink:r=>ye({...r,...t}),editHyperlinkText:r=>ye({newText:r,...t}),editHyperlinkHref:r=>ye({newURL:r,...t})}}function Ge(){return{openCreateHyperlinkPopover:e=>({editor:t,dispatch:r})=>r?de(t,e??{}):!0}}function Ye(e){let t=Ue({markName:e.markName,urls:e.urls});return{...Ve(t),...Ze({markName:e.markName,urls:e.urls,validate:e.options.validate}),...Ge()}}import{combineTransactionSteps as pr,findChildrenInRange as cr,getChangedRanges as ur,getMarksBetween as Xe}from"@tiptap/core";import{Plugin as mr,PluginKey as dr}from"@tiptap/pm/state";var fr=/\s$/,yr=15,gr=e=>e.split(/\s+/).filter(t=>t!=="");function je(e){let{urls:t,type:r}=e;return new mr({key:new dr("hyperlinkAutolink"),appendTransaction:(o,n,i)=>{let s=o.some(f=>f.docChanged)&&!n.doc.eq(i.doc),a=o.some(f=>f.getMeta(ee));if(!s||a)return;let{tr:l}=i,c=pr(n.doc,[...o]),{mapping:p}=c,m=ur(c),v=i.schema.marks.code??null;for(let{oldRange:f,newRange:x}of m){for(let y of Xe(f.from,f.to,n.doc).filter(b=>b.mark.type===r)){let b=p.map(y.from),u=p.map(y.to),P=Xe(b,u,i.doc).filter(X=>X.mark.type===r);if(!P.length)continue;let O=P[0],G=n.doc.textBetween(y.from,y.to,void 0," "),ae=i.doc.textBetween(O.from,O.to,void 0," "),le=t.detect(G),Y=t.detect(ae);le&&!Y&&l.removeMark(O.from,O.to,r)}let M=cr(i.doc,x,y=>y.isTextblock),L,A;if(M.length>1?(L=M[0],A=i.doc.textBetween(L.pos,L.pos+L.node.nodeSize,void 0," ")):M.length&&fr.test(i.doc.textBetween(x.from,x.to," "," "))&&(L=M[0],A=i.doc.textBetween(L.pos,x.to,void 0," ")),!L||!A)continue;let C=gr(A);if(!C.length)continue;let g=C[C.length-1],k=L.pos+A.lastIndexOf(g);t.forWrite({kind:"text",text:g}).map(y=>({href:y.href,from:k+y.start+1,to:k+y.end+1})).filter(({from:y})=>{let b=Math.max(0,y-yr),u=i.doc.textBetween(b,y," "," ");return!(u.includes("![")&&u.endsWith("]("))}).filter(({from:y})=>v?!i.doc.nodeAt(y)?.marks.some(u=>u.type===v):!0).forEach(({from:y,to:b,href:u})=>{l.addMark(y,b,r.create({href:u}))})}if(l.steps.length)return l}})}import{getMarkRange as Je}from"@tiptap/core";import{Plugin as hr,PluginKey as kr}from"@tiptap/pm/state";function oe(e,t,r){if(t.isConnected)return t;if(typeof r=="number")try{let{node:s}=e.view.domAtPos(r),l=("closest"in s?s:s.parentElement)?.closest("a");if(l&&e.view.dom.contains(l))return l}catch{}let o=t.getAttribute("href"),n=t.textContent;return Array.from(e.view.dom.querySelectorAll("a")).find(s=>s.getAttribute("href")===o&&s.textContent===n)??null}function W({editor:e,link:t,nodePos:r,validate:o,isAllowedUri:n,markName:i=R}){let s=e.view,a=oe(e,t,r)??t,l=r??-1,c=null;try{l=s.posAtDOM(a,0),l>=0&&(c=s.state.doc.nodeAt(l))}catch{c=null}let m=c?.marks.find(v=>v.type.name===i)?.attrs??{href:a.getAttribute("href"),target:null,rel:null,class:null,title:null,image:null};return{editor:e,link:a,nodePos:l,attrs:m,validate:o,isAllowedUri:n}}function ne(e){let{editor:t,link:r,validate:o}=e,n=d("form",{className:"hyperlink-edit-popover"}),i=d("div",{className:"inputs-wrapper"}),s=d("div",{className:"buttons-wrapper"}),a=d("div",{className:"text-wrapper"}),l=d("div",{className:"search-icon",innerHTML:$e({size:24})}),c=d("input",{type:"text",value:r.innerText||"",placeholder:"Enter link text"}),p=d("div",{className:"error-message",textContent:"Please enter link text"}),m=d("div",{className:"href-wrapper"}),v=d("div",{className:"search-icon",innerHTML:ue({size:24})}),f=d("input",{type:"text",inputMode:"url",value:r.getAttribute("href")??"",placeholder:"Enter URL",autocomplete:"new-password",spellcheck:!1}),x=d("div",{className:"error-message",textContent:"Please enter a valid URL"}),M=d("button",{type:"button",textContent:"Back",className:"back-button"}),L=d("button",{type:"submit",textContent:"Apply",className:"apply-button"}),A=(g,k)=>{g.classList.add("error"),k.classList.add("show")},C=(g,k)=>{g.classList.remove("error"),k.classList.remove("show")};return c.addEventListener("input",()=>C(a,p)),f.addEventListener("input",()=>C(m,x)),n.addEventListener("keydown",g=>{g.key==="Escape"&&(g.preventDefault(),h().close(),t.commands.focus())}),M.addEventListener("click",()=>{if(e.onBack){e.onBack();return}Z(W({editor:t,link:r,validate:o,isAllowedUri:e.isAllowedUri,nodePos:e.nodePos,markName:e.markName}))}),n.addEventListener("submit",g=>{g.preventDefault();let k=c.value.trim(),y=f.value.trim(),b=!1;if(k||(A(a,p),b=!0),I(y,{customValidator:o})||(A(m,x),b=!0),b)return;let u=e.markName??R;if(!t.chain().focus().extendMarkRange(u).editHyperlink({newURL:y,newText:k}).run()){if(!t.isActive(u)){h().close();return}A(m,x);return}h().close()}),a.append(l,c,p),m.append(v,f,x),i.append(a,m),s.append(M,L),n.append(i,s),queueMicrotask(()=>c.focus()),n}function ge(e){let{editor:t}=e,{popovers:r}=K(t,"openEditHyperlink"),n=(r.editHyperlink??ne)(e);if(!n)return;let i=oe(t,e.link,e.nodePos),s=!1,a=_({coordinates:{getBoundingClientRect:()=>{if(i=i?oe(t,i,e.nodePos):null,!i)return s||(s=!0,queueMicrotask(()=>a.hide())),{x:-9999,y:-9999,width:0,height:0};let l=i.getBoundingClientRect();return{x:l.left,y:l.top,width:l.width,height:l.height}},contextElement:t.view.dom},content:n,placement:"bottom",showArrow:!0,role:"dialog",ariaLabel:"Edit link",onHide:H});h().adopt(a,"edit",{element:a.element,referenceElement:null}),V(t,a),a.show()}var Te=(e,t,r)=>{let o=d("button",{type:"button",className:e,innerHTML:r});return o.setAttribute("aria-label",t),Le(o,t),o};function ie(e){let{link:t,editor:r}=e,o=e.attrs.href??"",n=w(o)?o:"",i=e.isAllowedUri??w,s=d("div",{className:"hyperlink-preview-popover"}),a=Te("remove","Remove link",be()),l=Te("copy","Copy link",He()),c=Te("edit","Edit link",we()),p=d("a",{target:"_blank",rel:"noopener noreferrer",href:n,innerText:o});return p.addEventListener("click",m=>{m.preventDefault(),n&&i(n)&&window.open(n,"_blank",B)}),a.addEventListener("click",()=>{h().close(),r.chain().focus().unsetHyperlink().run()}),c.addEventListener("click",()=>{ge({editor:r,link:t,nodePos:e.nodePos,validate:e.validate,isAllowedUri:e.isAllowedUri})}),l.addEventListener("click",()=>{xe(o,m=>{m&&h().close()})}),s.append(p,l,c,a),s}function Z(e){let{editor:t}=e,{popovers:r}=K(t,"openPreviewHyperlink"),n=(r.previewHyperlink??ie)(e);if(!n)return!1;let i=_({referenceElement:e.link,content:n,placement:"bottom",showArrow:!0,role:"toolbar",onHide:H});return h().adopt(i,"preview",{element:i.element,referenceElement:e.link}),V(t,i),i.show(),!0}function se(e,t){let r=e.target;if(!r)return null;let o=r.closest("a");return!o||!t.contains(o)?null:o}function et(e,t){return t.urls.forRead(e).navigable}function Qe(e,t,r,o){let n=W({editor:r.editor,link:t,validate:r.validate,isAllowedUri:l=>r.urls.forRead(l).navigable}),i=n.attrs.href??t.getAttribute("href"),s=t.target||n.attrs.target||"_blank";if(!r.previewPopover)return!e.editable&&et(i,r)&&window.open(i,s,B),!e.editable;if(!i)return h().close(),!0;if(!Z(n))return!0;if(o!==void 0){let{from:l,to:c}=e.state.selection;if(r.options.enableClickSelection&&e.editable&&l===c){let p=Je(e.state.doc.resolve(o),r.type);p?r.editor.chain().focus(o).setTextSelection(p).run():r.editor.chain().focus(o===0?"start":o).run()}else{let p=Je(e.state.doc.resolve(o),r.type),m=!!p&&l<p.to&&c>p.from,v=l===c||!m?o:{from:l,to:c};r.editor.chain().focus(o===0?"start":o).setTextSelection(v).run()}}return!0}function tt(e){return new hr({key:new kr("hyperlinkClickHandler"),view(t){let r=i=>{i.button!==0||!se(i,t.dom)||(i.preventDefault(),i.stopPropagation())},o=i=>{if(i.button!==0)return;se(i,t.dom)&&i.preventDefault()},n=i=>{if(i.button!==1)return;let s=se(i,t.dom);if(!s)return;i.preventDefault(),i.stopPropagation();let{attrs:a}=W({editor:e.editor,link:s}),l=a.href??s.getAttribute("href");et(l,e)&&window.open(l,"_blank",B)};return t.dom.addEventListener("mousedown",r,!0),t.dom.addEventListener("click",o,!0),t.dom.addEventListener("auxclick",n,!0),{destroy(){t.dom.removeEventListener("mousedown",r,!0),t.dom.removeEventListener("click",o,!0),t.dom.removeEventListener("auxclick",n,!0)}}},props:{handleDOMEvents:{touchend:(t,r)=>{let o=se(r,t.dom);if(!o)return!1;r.preventDefault();let{clientX:n,clientY:i}=r.changedTouches[0],s=t.posAtCoords({left:n,top:i});return Qe(t,o,e,s?.pos)},click:(t,r)=>{if(r.button!==0)return!1;let o=se(r,t.dom);if(!o)return!1;r.preventDefault();let n=t.posAtCoords({left:r.clientX,top:r.clientY});return Qe(t,o,e,n?.pos)}}}})}import{InputRule as vr}from"@tiptap/core";var Er=/(\[([^\]]+)\]\(([^)]+)\))$/;function rt(e){return new vr({find:Er,handler:({state:t,range:r,match:o})=>{let[,,n,i]=o,{tr:s}=t,a=r.from,l=r.to,[c]=e.urls.forWrite({kind:"href",href:i});if(!c)return;let p=t.doc.nodeAt(a)?.marks.filter(m=>m.type!==e.type)??[];s.replaceWith(a,l,t.schema.text(n,p)),s.addMark(a,a+n.length,e.type.create({href:c.href}))}})}import{Plugin as Lr,PluginKey as xr}from"@tiptap/pm/state";import{find as Hr}from"linkifyjs";function ot(e){return new Lr({key:new xr("hyperlinkPasteHandler"),props:{handlePaste:(t,r,o)=>{let{selection:n}=t.state;if(n.empty)return!1;let i="";o.content.forEach(l=>{i+=l.textContent});let s=Hr(i).find(l=>l.isLink&&l.value===i);if(!i||!s)return!1;let[a]=e.urls.forWrite({kind:"match",match:s});return a?e.editor.chain().setHyperlink({href:a.href}).run():!1}}})}import{markPasteRule as br}from"@tiptap/core";import{find as wr}from"linkifyjs";function nt(e){return br({find:t=>wr(t).filter(r=>r.isLink).flatMap(r=>{let[o]=e.urls.forWrite({kind:"match",match:r});return o?[{text:r.value,index:r.start,data:{href:o.href}}]:[]}),type:e.type,getAttributes:t=>({href:t.data.href})})}function Ar(e){return fe({defaultProtocol:e.defaultProtocol,gate:ze({isAllowedUri:e.isAllowedUri,protocols:e.protocols,defaultProtocol:e.defaultProtocol}),validate:e.validate,shouldAutoLink:e.shouldAutoLink})}function Re({editor:e,type:t,options:r}){return{type:t,editor:e,urls:Ar(r),options:r,validate:r.validate,previewPopover:r.popovers.previewHyperlink??null}}function he(e){let t=[];return e.options.autolink&&t.push(je(e)),e.options.openOnClick&&t.push(tt(e)),e.options.linkOnPaste&&t.push(ot(e)),{inputRules:[rt(e)],pasteRules:[nt(e)],plugins:t}}var it=new Set;function st(e,t){it.has(e)||(it.add(e),Tr(e,t))}var at=Cr.create({name:R,priority:1e3,keepOnSplit:!1,markdownTokenName:"link",parseMarkdown:(e,t)=>{let r=q(e.href||"");return t.applyMark(R,t.parseInline(e.tokens??[]),{href:w(r)?r:""})},renderMarkdown:(e,t,r)=>{let o=t.renderChildren(e),n=e.attrs?.href||"",i=(w(n)?n:"").replace(/\)/g,"%29").replace(/\s/g,"%20");return`[${o}](${i})`},addStorage(){return{context:null}},onCreate(){this.options.protocols.forEach(e=>{typeof e=="string"?st(e):st(e.scheme,e.optionalSlashes)})},onDestroy(){Ke(this.editor)},inclusive(){return this.options.autolink},addOptions(){return{openOnClick:!0,linkOnPaste:!0,autolink:!0,protocols:[],HTMLAttributes:{target:null,rel:"noopener noreferrer nofollow",class:null},popovers:{previewHyperlink:null,editHyperlink:null,createHyperlink:null},validate:void 0,defaultProtocol:F,isAllowedUri:void 0,shouldAutoLink:void 0,enableClickSelection:!1,exitable:!1}},addAttributes(){return{href:{default:null},target:{default:this.options.HTMLAttributes.target,rendered:!1},rel:{default:this.options.HTMLAttributes.rel},class:{default:this.options.HTMLAttributes.class},title:{default:null},image:{default:null,rendered:!1}}},parseHTML(){return[{tag:"a[href]",getAttrs:e=>{if(typeof e=="string")return!1;let t=e.getAttribute("href")??"";return w(t)?null:!1}}]},renderHTML({HTMLAttributes:e}){let t=w(e.href)?e:{...e,href:""};return["a",Pr(this.options.HTMLAttributes,t),0]},addCommands(){return Ye({markName:this.name,options:this.options,urls:ke(this).urls})},addKeyboardShortcuts(){let e={"Mod-k":()=>this.editor.commands.openCreateHyperlinkPopover()};return this.options.exitable&&(e.ArrowRight=()=>Rr(this.editor,this.type)),e},addInputRules(){return he(ke(this)).inputRules},addPasteRules(){return he(ke(this)).pasteRules},addProseMirrorPlugins(){return he(ke(this)).plugins}});function ke(e){if(e.storage.context)return e.storage.context;let t=Re({editor:e.editor,type:e.type,options:e.options});return e.storage.context=t,t}function Rr(e,t){let{state:r}=e,{selection:o,tr:n}=r;if(!o.empty)return!1;let i=o.$from,s=i.nodeAfter;return(i.nodeBefore?.marks.some(p=>p.type===t)??!1)&&(!s||!s.marks.some(p=>p.type===t))&&(n.setStoredMarks((r.storedMarks??i.marks()).filter(p=>p.type!==t)),e.view.dispatch(n)),!1}import{registerCustomProtocol as bi}from"linkifyjs";var fi=at;export{He as Copy,qe as DANGEROUS_SCHEME_RE,We as DEFAULT_OFFSET,at as Hyperlink,be as LinkOff,we as Pencil,B as SAFE_WINDOW_FEATURES,Le as attachTooltip,W as buildPreviewOptionsFromAnchor,xe as copyToClipboard,d as createHTMLElement,re as createHyperlinkPopover,_ as createPopover,fi as default,ne as editHyperlinkPopover,h as getDefaultController,Ce as getSpecialUrlInfo,H as hideTooltip,w as isSafeHref,q as normalizeHref,de as openCreateHyperlink,ge as openEditHyperlink,Z as openPreviewHyperlink,ie as previewHyperlinkPopover,bi as registerCustomProtocol,I as validateURL};
22
+ //# sourceMappingURL=index.js.map