OverviewFilesCommitsIssuesDocsReleasesPeers

keet-deobfuscated / @holepunchto / keet-editor / src / extensions / link-preview.js

70 lines · javascript ref:
import { Node as e, mergeAttributes as t } from "@tiptap/core"
import { ReactNodeViewRenderer as i } from "@tiptap/react"
import { getUrls as n, debounce as r } from "./helpers/index.js"
const s = "link-preview",
  a = 5
export const handleUpdate = r((e, t) => {
  const i = e.getJSON(),
    r = t.storage,
    s = n(i, (t) => {
      const i = e.chain().focus().extendMarkRange("link")
      t ? i.setLink({ href: t }).run() : i.unsetLink().run()
    })
  for (const t of [...s].slice(0, 5))
    r.active.has(t) || r.failed.has(t) || (e.commands.setLinkPreview({ url: t }), r.active.add(t))
  for (const t of r.active)
    (!r.failed.has(t) && s.has(t)) || (e.commands.removeLinkPreview({ url: t }), r.active.delete(t))
}, 600)
export const LinkPreview = e.create({
  name: s,
  group: "block",
  atom: !0,
  addStorage: () => ({ active: new Set(), failed: new Set() }),
  addAttributes: () => ({
    url: { default: null },
    title: { default: null },
    description: { default: null },
    image: { default: null },
  }),
  parseHTML: () => [{ tag: s }],
  renderHTML: ({ HTMLAttributes: e }) => [s, t(e)],
  addCommands() {
    return {
      saveFile: (e, t) => () => (t && this.options.store.set(e, t), !0),
      removeFile: (e) => () => (this.options.store.del(e), !0),
      setLinkPreview:
        ({ url: e }) =>
        ({ state: t, dispatch: i }) => {
          const { tr: n, schema: r, doc: a } = t,
            o = r.nodes[s].create({ url: e }),
            d = n.insert(a.content.size, o)
          return (i && d.docChanged && i(d), !0)
        },
      removeLinkPreview:
        ({ url: e }) =>
        ({ state: t, dispatch: i }) => {
          const { tr: n, doc: r } = t
          let a = n,
            o = !1
          return (
            r.descendants((t, i) => {
              t.type.name === s &&
                t.attrs.url === e &&
                ((a = a.delete(i, i + t.nodeSize)), (o = !0))
            }),
            i && o && i(a),
            !0
          )
        },
    }
  },
  addNodeView() {
    return i(this.options.Component, { className: "inline-block" })
  },
  onUpdate({ editor: e }) {
    this.options?.isFileSharingDisabled?.() ||
      this.options?.isLinkSharingDisabled?.() ||
      handleUpdate(e, this)
  },
})