Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ascii tags #496

Merged
merged 2 commits into from
Dec 2, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/app/editor/Hashtag.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<script lang="ts">
export let value
</script>

<span>
{value}
</span>
208 changes: 208 additions & 0 deletions src/app/editor/Hashtag.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
import {Node, mergeAttributes} from "@tiptap/core"
import Suggestion, {type SuggestionOptions} from "@tiptap/suggestion"
import {topicSearch} from "@welshman/app"
import {PluginKey} from "prosemirror-state"
import type {SvelteComponent} from "svelte"
import tippy, {type Instance} from "tippy.js"
import Suggestions from "src/app/editor/Suggestions.svelte"
import HashtagComponent from "src/app/editor/Hashtag.svelte"

export type HashtagOptions = {
HTMLAttributes: Record<string, any>
suggestion: Omit<SuggestionOptions, "editor">
}

export const HashtagPluginKey = new PluginKey("hashtag")

export const Hashtag = Node.create<HashtagOptions>({
name: "tag",

addOptions: () => {
return {
HTMLAttributes: {"data-type": "tag"},
suggestion: {
char: "#",
pluginKey: HashtagPluginKey,
command: ({editor, range, props}) => {
// increase range.to by one when the next node is of type "text"
// and starts with a space character
const nodeAfter = editor.view.state.selection.$to.nodeAfter
const overrideSpace = nodeAfter?.text?.startsWith(" ")

if (overrideSpace) {
range.to += 1
}

editor
.chain()
.focus()
.insertContentAt(range, [
{
type: "tag",
attrs: props,
},
{
type: "text",
text: " ",
},
])
.run()
},
allow: ({editor, range}) => {
return true
},
render() {
let popover: Instance[]
let suggestions: SvelteComponent
const mapProps = (props: any) => ({
term: props.query,
search: topicSearch,
component: HashtagComponent,
allowCreate: true,
select: (value: string) => {
props.command({label: value})
},
})

return {
onStart: props => {
const target = document.createElement("div")
popover = tippy("body", {
getReferenceClientRect: props.clientRect as any,
appendTo: document.querySelector("dialog[open]") || document.body,
content: target,
showOnCreate: true,
interactive: true,
trigger: "manual",
placement: "bottom-start",
})
if (!props.query) popover[0].hide()
suggestions = new Suggestions({target, props: mapProps(props)})
},
onUpdate: props => {
if (props.query) {
popover[0].show()
} else {
popover[0].hide()
}
suggestions.$set(mapProps(props))

if (props.clientRect) {
popover[0].setProps({
getReferenceClientRect: props.clientRect as any,
})
}
},
onKeyDown: props => {
if (props.event.key === "Escape") {
popover[0].hide()

return true
}
return Boolean(suggestions.onKeyDown?.(props.event))
},
onExit: () => {
popover[0].destroy()
suggestions.$destroy()
},
}
},
},
}
},

group: "inline",

inline: true,

selectable: false,

atom: true,

addAttributes() {
return {
id: {
default: null,
parseHTML: element => element.getAttribute("data-id"),
renderHTML: attributes => {
if (!attributes.id) {
return {}
}

return {
"data-id": attributes.id,
}
},
},

label: {
default: null,
parseHTML: element => element.getAttribute("data-label"),
renderHTML: attributes => {
if (!attributes.label) {
return {}
}

return {
"data-label": attributes.label,
}
},
},
}
},

renderHTML({node, HTMLAttributes}) {
return [
"a",
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
"#" + node.attrs.label,
]
},

renderText({node}) {
return "#" + node.attrs.label
},

parseHTML() {
return [
{
tag: `a[data-type="${this.name}"]`,
},
]
},

addKeyboardShortcuts() {
return {
Backspace: () =>
this.editor.commands.command(({tr, state}) => {
let isMention = false
const {selection} = state
const {empty, anchor} = selection

if (!empty) {
return false
}

state.doc.nodesBetween(anchor - 1, anchor, (node, pos) => {
if (node.type.name === this.name) {
isMention = true
tr.insertText(this.options.suggestion.char || "", pos, pos + node.nodeSize)

return false
}
})

return isMention
}),
}
},

addProseMirrorPlugins() {
return [
Suggestion({
editor: this.editor,
...this.options.suggestion,
}),
]
},
})
79 changes: 0 additions & 79 deletions src/app/editor/TagExtension.ts

This file was deleted.

22 changes: 11 additions & 11 deletions src/app/editor/index.ts
Original file line number Diff line number Diff line change
@@ -22,17 +22,17 @@ import {ctx} from "@welshman/lib"
import type {StampedEvent} from "@welshman/util"
import {signer, profileSearch, RelayMode} from "@welshman/app"
import {createSuggestions} from "./Suggestions"
import EditMention from "./EditMention.svelte"
import EditEvent from "./EditEvent.svelte"
import EditBolt11 from "./EditBolt11.svelte"
import EditMedia from "./EditMedia.svelte"
import Suggestions from "./Suggestions.svelte"
import SuggestionProfile from "./SuggestionProfile.svelte"
import {asInline} from "./util"
import {WordCount} from "./wordcounts"
import {FileUploadExtension} from "./FileUpload"
import EditMention from "src/app/editor/EditMention.svelte"
import EditEvent from "src/app/editor/EditEvent.svelte"
import EditBolt11 from "src/app/editor/EditBolt11.svelte"
import EditMedia from "src/app/editor/EditMedia.svelte"
import Suggestions from "src/app/editor/Suggestions.svelte"
import SuggestionProfile from "src/app/editor/SuggestionProfile.svelte"
import {asInline} from "src/app/editor/util"
import {WordCount} from "src/app/editor/wordcounts"
import {FileUploadExtension} from "src/app/editor/FileUpload"
import {getSetting} from "src/engine"
import {TagExtension} from "./TagExtension"
import {Hashtag} from "src/app/editor/Hashtag"

export {createSuggestions, EditMention, EditEvent, EditBolt11, EditMedia, Suggestions}
export * from "./util"
@@ -81,7 +81,7 @@ export const getEditorOptions = ({
History,
Paragraph,
Text,
TagExtension,
Hashtag,
WordCount,
Placeholder.configure({placeholder}),
HardBreakExtension.extend({