Get link extension working better
This commit is contained in:
@@ -16,7 +16,7 @@
|
|||||||
let editor: Readable<Editor>
|
let editor: Readable<Editor>
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
editor = createEditor(getViewOptions(root.content))
|
editor = createEditor(getViewOptions(root))
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
import {Node, nodePasteRule, type PasteRuleMatch} from "@tiptap/core"
|
import {last} from '@welshman/lib'
|
||||||
|
import {Node, InputRule, nodePasteRule, type PasteRuleMatch} from "@tiptap/core"
|
||||||
import type {Node as ProsemirrorNode} from "@tiptap/pm/model"
|
import type {Node as ProsemirrorNode} from "@tiptap/pm/model"
|
||||||
import type {MarkdownSerializerState} from "prosemirror-markdown"
|
import type {MarkdownSerializerState} from "prosemirror-markdown"
|
||||||
|
import {createPasteRuleMatch, createInputRuleMatch} from './util'
|
||||||
|
|
||||||
export const LINK_REGEX =
|
export const LINK_REGEX =
|
||||||
/^([a-z\+:]{2,30}:\/\/)?[^<>\(\)\s]+\.[a-z]{2,6}[^\s]*[^<>"'\.!?,:\s\)\(]*/gi
|
/([a-z\+:]{2,30}:\/\/)?[^<>\(\)\s]+\.[a-z]{2,6}[^\s]*[^<>"'\.!?,:\s\)\(]*/gi
|
||||||
|
|
||||||
export const createPasteRuleMatch = <T extends Record<string, unknown>>(
|
|
||||||
match: RegExpMatchArray,
|
|
||||||
data: T,
|
|
||||||
): PasteRuleMatch => ({index: match.index!, replaceWith: match[2], text: match[0], match, data})
|
|
||||||
|
|
||||||
export interface LinkAttributes {
|
export interface LinkAttributes {
|
||||||
url: string
|
url: string
|
||||||
@@ -65,6 +62,42 @@ export const LinkExtension = Node.create({
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
addInputRules() {
|
||||||
|
return [
|
||||||
|
new InputRule({
|
||||||
|
find: text => {
|
||||||
|
const match = last(Array.from(text.matchAll(LINK_REGEX)))
|
||||||
|
|
||||||
|
if (match && text.length === match.index + match[0].length + 1) {
|
||||||
|
return {
|
||||||
|
index: match.index!,
|
||||||
|
text: match[0],
|
||||||
|
data: {
|
||||||
|
url: match[0],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
handler: ({state, range, match}) => {
|
||||||
|
const {tr} = state
|
||||||
|
|
||||||
|
if (match[0]) {
|
||||||
|
try {
|
||||||
|
tr.insert(range.from - 1, this.type.create(match.data))
|
||||||
|
.delete(tr.mapping.map(range.from - 1), tr.mapping.map(range.to))
|
||||||
|
.insert(tr.mapping.map(range.to), this.editor.schema.text(last(Array.from(match.input!))))
|
||||||
|
} catch (e) {
|
||||||
|
// If the node was already linkified, the above code breaks for whatever reason
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.scrollIntoView()
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
},
|
||||||
addPasteRules() {
|
addPasteRules() {
|
||||||
return [
|
return [
|
||||||
nodePasteRule({
|
nodePasteRule({
|
||||||
|
|||||||
+61
-59
@@ -78,68 +78,70 @@ export const getEditorOptions = ({
|
|||||||
loading,
|
loading,
|
||||||
getPubkeyHints,
|
getPubkeyHints,
|
||||||
submitOnEnter,
|
submitOnEnter,
|
||||||
}: EditorOptions) => {
|
}: EditorOptions) => ({
|
||||||
return {
|
content: "",
|
||||||
content: "",
|
autofocus: true,
|
||||||
autofocus: true,
|
extensions: [
|
||||||
extensions: [
|
Code,
|
||||||
Code,
|
CodeBlock,
|
||||||
CodeBlock,
|
Document,
|
||||||
Document,
|
Dropcursor,
|
||||||
Dropcursor,
|
Gapcursor,
|
||||||
Gapcursor,
|
History,
|
||||||
History,
|
Paragraph,
|
||||||
Paragraph,
|
Text,
|
||||||
Text,
|
submitOnEnter ? getModifiedHardBreakExtension() : HardBreakExtension,
|
||||||
submitOnEnter ? getModifiedHardBreakExtension() : HardBreakExtension,
|
LinkExtension.extend({
|
||||||
LinkExtension.extend({
|
addNodeView: () => SvelteNodeViewRenderer(EditLink),
|
||||||
addNodeView: () => SvelteNodeViewRenderer(EditLink),
|
}),
|
||||||
}),
|
Bolt11Extension.extend(asInline({addNodeView: () => SvelteNodeViewRenderer(EditBolt11)})),
|
||||||
Bolt11Extension.extend(asInline({addNodeView: () => SvelteNodeViewRenderer(EditBolt11)})),
|
NProfileExtension.extend({
|
||||||
NProfileExtension.extend({
|
addNodeView: () => SvelteNodeViewRenderer(EditMention),
|
||||||
addNodeView: () => SvelteNodeViewRenderer(EditMention),
|
addProseMirrorPlugins() {
|
||||||
addProseMirrorPlugins() {
|
return [
|
||||||
return [
|
createSuggestions({
|
||||||
createSuggestions({
|
char: "@",
|
||||||
char: "@",
|
name: "nprofile",
|
||||||
name: "nprofile",
|
editor: this.editor,
|
||||||
editor: this.editor,
|
search: profileSearch,
|
||||||
search: profileSearch,
|
select: (pubkey: string, props: any) => {
|
||||||
select: (pubkey: string, props: any) => {
|
const relays = getPubkeyHints(pubkey)
|
||||||
const relays = getPubkeyHints(pubkey)
|
const nprofile = nprofileEncode({pubkey, relays})
|
||||||
const nprofile = nprofileEncode({pubkey, relays})
|
|
||||||
|
|
||||||
return props.command({pubkey, nprofile, relays})
|
return props.command({pubkey, nprofile, relays})
|
||||||
},
|
},
|
||||||
suggestionComponent: SuggestionProfile,
|
suggestionComponent: SuggestionProfile,
|
||||||
suggestionsComponent: Suggestions,
|
suggestionsComponent: Suggestions,
|
||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
NEventExtension.extend(asInline({addNodeView: () => SvelteNodeViewRenderer(EditEvent)})),
|
NEventExtension.extend(asInline({addNodeView: () => SvelteNodeViewRenderer(EditEvent)})),
|
||||||
NAddrExtension.extend(asInline({addNodeView: () => SvelteNodeViewRenderer(EditEvent)})),
|
NAddrExtension.extend(asInline({addNodeView: () => SvelteNodeViewRenderer(EditEvent)})),
|
||||||
ImageExtension.extend(
|
ImageExtension.extend(
|
||||||
asInline({addNodeView: () => SvelteNodeViewRenderer(EditImage)}),
|
asInline({addNodeView: () => SvelteNodeViewRenderer(EditImage)}),
|
||||||
).configure({defaultUploadUrl: "https://nostr.build", defaultUploadType: "nip96"}),
|
).configure({defaultUploadUrl: "https://nostr.build", defaultUploadType: "nip96"}),
|
||||||
VideoExtension.extend(
|
VideoExtension.extend(
|
||||||
asInline({addNodeView: () => SvelteNodeViewRenderer(EditVideo)}),
|
asInline({addNodeView: () => SvelteNodeViewRenderer(EditVideo)}),
|
||||||
).configure({defaultUploadUrl: "https://nostr.build", defaultUploadType: "nip96"}),
|
).configure({defaultUploadUrl: "https://nostr.build", defaultUploadType: "nip96"}),
|
||||||
FileUploadExtension.configure({
|
FileUploadExtension.configure({
|
||||||
immediateUpload: false,
|
immediateUpload: false,
|
||||||
sign: (event: StampedEvent) => {
|
sign: (event: StampedEvent) => {
|
||||||
loading.set(true)
|
loading.set(true)
|
||||||
|
|
||||||
return signer.get()!.sign(event)
|
return signer.get()!.sign(event)
|
||||||
},
|
},
|
||||||
onComplete: () => {
|
onComplete: () => {
|
||||||
loading.set(false)
|
loading.set(false)
|
||||||
submit()
|
submit()
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
onTransaction() {
|
||||||
|
// @ts-ignore
|
||||||
|
console.log(this.getJSON())
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
type ViewOptions = {
|
type ViewOptions = {
|
||||||
content: string
|
content: string
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type {JSONContent, PasteRuleMatch} from "@tiptap/core"
|
import type {JSONContent, PasteRuleMatch, InputRuleMatch} from "@tiptap/core"
|
||||||
import {Editor} from "@tiptap/core"
|
import {Editor} from "@tiptap/core"
|
||||||
import {choice} from "@welshman/lib"
|
import {choice} from "@welshman/lib"
|
||||||
|
|
||||||
@@ -8,10 +8,15 @@ export const asInline = (extend: Record<string, any>) => ({
|
|||||||
...extend,
|
...extend,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const createInputRuleMatch = <T extends Record<string, unknown>>(
|
||||||
|
match: RegExpMatchArray,
|
||||||
|
data: T,
|
||||||
|
): InputRuleMatch => ({index: match.index!, text: match[0], match, data})
|
||||||
|
|
||||||
export const createPasteRuleMatch = <T extends Record<string, unknown>>(
|
export const createPasteRuleMatch = <T extends Record<string, unknown>>(
|
||||||
match: RegExpMatchArray,
|
match: RegExpMatchArray,
|
||||||
data: T,
|
data: T,
|
||||||
): PasteRuleMatch => ({index: match.index!, replaceWith: match[2], text: match[0], match, data})
|
): PasteRuleMatch => ({index: match.index!, text: match[0], match, data})
|
||||||
|
|
||||||
export const findNodes = (type: string, json: JSONContent) => {
|
export const findNodes = (type: string, json: JSONContent) => {
|
||||||
const results: JSONContent[] = []
|
const results: JSONContent[] = []
|
||||||
|
|||||||
Reference in New Issue
Block a user