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