import { AtUri } from "@atproto/api"; import { Schema, Node, MarkSpec, NodeSpec } from "prosemirror-model"; import { marks } from "prosemirror-schema-basic"; import { theme } from "tailwind.config"; let baseSchema = { marks: { strong: marks.strong, em: marks.em, code: { parseDOM: [ { tag: "code", }, ], toDOM() { return ["code", { class: "inline-code" }, 0]; }, } as MarkSpec, underline: { parseDOM: [ { tag: "u" }, { style: "text-decoration=underline", }, { style: "text-decoration=none", clearMark: (m) => m.type.name == "underline", }, ], toDOM() { return ["u", { class: "underline" }, 0]; }, } as MarkSpec, strikethrough: { parseDOM: [ { tag: "s" }, { tag: "del" }, { style: `text-decoration=line-through`, }, { style: "text-decoration=none", clearMark: (m) => m.type.name == "strikethrough", }, ], toDOM() { return [ "s", { style: `text-decoration-color: ${theme.colors.tertiary};`, }, 0, ]; }, } as MarkSpec, highlight: { attrs: { color: { default: "1", }, }, parseDOM: [ { tag: "span.highlight", getAttrs(dom: HTMLElement) { return { color: dom.getAttribute("data-color"), }; }, }, ], toDOM(node) { let { color } = node.attrs; return [ "span", { class: "highlight", "data-color": color, style: `background-color: ${color === "1" ? theme.colors["highlight-1"] : color === "2" ? theme.colors["highlight-2"] : theme.colors["highlight-3"]}`, }, 0, ]; }, } as MarkSpec, link: { attrs: { href: {}, }, inclusive: false, parseDOM: [ { tag: "a[href]", getAttrs(dom: HTMLElement) { return { href: dom.getAttribute("href"), }; }, }, ], toDOM(node) { let { href } = node.attrs; return ["a", { href, target: "_blank" }, 0]; }, } as MarkSpec, }, nodes: { doc: { content: "block" }, paragraph: { content: "inline*", group: "block", parseDOM: [{ tag: "p" }], toDOM: () => ["p", 0] as const, }, text: { group: "inline", }, hard_break: { group: "inline", inline: true, selectable: false, parseDOM: [{ tag: "br" }], toDOM: () => ["br"] as const, }, atMention: { attrs: { atURI: {}, text: { default: "" }, }, group: "inline", inline: true, atom: true, selectable: true, draggable: true, parseDOM: [ { tag: "span.atMention", getAttrs(dom: HTMLElement) { return { atURI: dom.getAttribute("data-at-uri"), text: dom.textContent || "", }; }, }, ], toDOM(node) { // NOTE: This rendering should match the AtMentionLink component in // components/AtMentionLink.tsx. If you update one, update the other. let className = "atMention text-accent-contrast"; let aturi = new AtUri(node.attrs.atURI); if (aturi.collection === "pub.leaflet.publication") className += " font-bold"; if (aturi.collection === "pub.leaflet.document") className += " italic"; // For publications and documents, show icon if ( aturi.collection === "pub.leaflet.publication" || aturi.collection === "pub.leaflet.document" ) { return [ "span", { class: className, "data-at-uri": node.attrs.atURI, }, [ "img", { src: `/api/pub_icon?at_uri=${encodeURIComponent(node.attrs.atURI)}`, class: "inline-block w-5 h-5 rounded-full mr-1 align-text-top", alt: "", width: "16", height: "16", loading: "lazy", }, ], node.attrs.text, ]; } return [ "span", { class: className, "data-at-uri": node.attrs.atURI, }, node.attrs.text, ]; }, } as NodeSpec, didMention: { attrs: { did: {}, text: { default: "" }, }, group: "inline", inline: true, atom: true, selectable: true, draggable: true, parseDOM: [ { tag: "span.didMention", getAttrs(dom: HTMLElement) { return { did: dom.getAttribute("data-did"), text: dom.textContent || "", }; }, }, ], toDOM(node) { return [ "span", { class: "didMention text-accent-contrast", "data-did": node.attrs.did, }, node.attrs.text, ]; }, } as NodeSpec, }, }; export const schema = new Schema(baseSchema); export const multiBlockSchema = new Schema({ marks: baseSchema.marks, nodes: { ...baseSchema.nodes, doc: { content: "block+" } }, });