forked from
leaflet.pub/leaflet
a tool for shared writing and social publishing
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});