a tool for shared writing and social publishing
at main 5.7 kB view raw
1import { AtUri } from "@atproto/api"; 2import { Schema, Node, MarkSpec, NodeSpec } from "prosemirror-model"; 3import { marks } from "prosemirror-schema-basic"; 4import { theme } from "tailwind.config"; 5import { 6 isDocumentCollection, 7 isPublicationCollection, 8} from "src/utils/collectionHelpers"; 9 10let baseSchema = { 11 marks: { 12 strong: marks.strong, 13 em: marks.em, 14 code: { 15 parseDOM: [ 16 { 17 tag: "code", 18 }, 19 ], 20 21 toDOM() { 22 return ["code", { class: "inline-code" }, 0]; 23 }, 24 } as MarkSpec, 25 underline: { 26 parseDOM: [ 27 { tag: "u" }, 28 { 29 style: "text-decoration=underline", 30 }, 31 { 32 style: "text-decoration=none", 33 clearMark: (m) => m.type.name == "underline", 34 }, 35 ], 36 toDOM() { 37 return ["u", { class: "underline" }, 0]; 38 }, 39 } as MarkSpec, 40 strikethrough: { 41 parseDOM: [ 42 { tag: "s" }, 43 { tag: "del" }, 44 { 45 style: `text-decoration=line-through`, 46 }, 47 { 48 style: "text-decoration=none", 49 clearMark: (m) => m.type.name == "strikethrough", 50 }, 51 ], 52 toDOM() { 53 return [ 54 "s", 55 { 56 style: `text-decoration-color: ${theme.colors.tertiary};`, 57 }, 58 0, 59 ]; 60 }, 61 } as MarkSpec, 62 highlight: { 63 attrs: { 64 color: { 65 default: "1", 66 }, 67 }, 68 parseDOM: [ 69 { 70 tag: "span.highlight", 71 getAttrs(dom: HTMLElement) { 72 return { 73 color: dom.getAttribute("data-color"), 74 }; 75 }, 76 }, 77 ], 78 toDOM(node) { 79 let { color } = node.attrs; 80 return [ 81 "span", 82 { 83 class: "highlight", 84 "data-color": color, 85 style: `background-color: ${color === "1" ? theme.colors["highlight-1"] : color === "2" ? theme.colors["highlight-2"] : theme.colors["highlight-3"]}`, 86 }, 87 0, 88 ]; 89 }, 90 } as MarkSpec, 91 link: { 92 attrs: { 93 href: {}, 94 }, 95 inclusive: false, 96 parseDOM: [ 97 { 98 tag: "a[href]", 99 getAttrs(dom: HTMLElement) { 100 return { 101 href: dom.getAttribute("href"), 102 }; 103 }, 104 }, 105 ], 106 toDOM(node) { 107 let { href } = node.attrs; 108 return ["a", { href, target: "_blank" }, 0]; 109 }, 110 } as MarkSpec, 111 }, 112 nodes: { 113 doc: { content: "block" }, 114 paragraph: { 115 content: "inline*", 116 group: "block", 117 parseDOM: [{ tag: "p" }], 118 toDOM: () => ["p", 0] as const, 119 }, 120 text: { 121 group: "inline", 122 }, 123 hard_break: { 124 group: "inline", 125 inline: true, 126 selectable: false, 127 parseDOM: [{ tag: "br" }], 128 toDOM: () => ["br"] as const, 129 }, 130 atMention: { 131 attrs: { 132 atURI: {}, 133 text: { default: "" }, 134 }, 135 group: "inline", 136 inline: true, 137 atom: true, 138 selectable: true, 139 draggable: true, 140 parseDOM: [ 141 { 142 tag: "span.atMention", 143 getAttrs(dom: HTMLElement) { 144 return { 145 atURI: dom.getAttribute("data-at-uri"), 146 text: dom.textContent || "", 147 }; 148 }, 149 }, 150 ], 151 toDOM(node) { 152 // NOTE: This rendering should match the AtMentionLink component in 153 // components/AtMentionLink.tsx. If you update one, update the other. 154 let className = "atMention mention"; 155 let aturi = new AtUri(node.attrs.atURI); 156 if (isPublicationCollection(aturi.collection)) 157 className += " font-bold"; 158 if (isDocumentCollection(aturi.collection)) className += " italic"; 159 160 // For publications and documents, show icon 161 if ( 162 isPublicationCollection(aturi.collection) || 163 isDocumentCollection(aturi.collection) 164 ) { 165 return [ 166 "span", 167 { 168 class: className, 169 "data-at-uri": node.attrs.atURI, 170 }, 171 [ 172 "img", 173 { 174 src: `/api/pub_icon?at_uri=${encodeURIComponent(node.attrs.atURI)}`, 175 class: 176 "inline-block w-4 h-4 rounded-full mt-[3px] mr-1 align-text-top", 177 alt: "", 178 width: "16", 179 height: "16", 180 loading: "lazy", 181 }, 182 ], 183 node.attrs.text, 184 ]; 185 } 186 187 return [ 188 "span", 189 { 190 class: className, 191 "data-at-uri": node.attrs.atURI, 192 }, 193 node.attrs.text, 194 ]; 195 }, 196 } as NodeSpec, 197 didMention: { 198 attrs: { 199 did: {}, 200 text: { default: "" }, 201 }, 202 group: "inline", 203 inline: true, 204 atom: true, 205 selectable: true, 206 draggable: true, 207 parseDOM: [ 208 { 209 tag: "span.didMention", 210 getAttrs(dom: HTMLElement) { 211 return { 212 did: dom.getAttribute("data-did"), 213 text: dom.textContent || "", 214 }; 215 }, 216 }, 217 ], 218 toDOM(node) { 219 return [ 220 "span", 221 { 222 class: "didMention mention", 223 "data-did": node.attrs.did, 224 }, 225 node.attrs.text, 226 ]; 227 }, 228 } as NodeSpec, 229 }, 230}; 231export const schema = new Schema(baseSchema); 232 233export const multiBlockSchema = new Schema({ 234 marks: baseSchema.marks, 235 nodes: { ...baseSchema.nodes, doc: { content: "block+" } }, 236});