64 | {store.preferences.toolbarItems.map((item, index) => {
65 | let reactElement: ReactElement;
66 | if (typeof item === "string") {
67 | switch (item) {
68 | case "about": {
69 | reactElement = (
70 |

modals.about.open()}
74 | title="About"
75 | />
76 | );
77 | break;
78 | }
79 | case "|": {
80 | reactElement =
|;
81 | break;
82 | }
83 | case "bold": {
84 | reactElement = (
85 |
stylingClicked("**")}
89 | >
90 |
91 | );
92 | break;
93 | }
94 | case "italic": {
95 | reactElement = (
96 |
stylingClicked("*")}
100 | >
101 |
102 | );
103 | break;
104 | }
105 | case "strikethrough": {
106 | reactElement = (
107 |
stylingClicked("~~")}
111 | >
112 |
113 | );
114 | break;
115 | }
116 | case "underline": {
117 | reactElement = (
118 |
stylingClicked("++")}
122 | >
123 |
124 | );
125 | break;
126 | }
127 | case "mark": {
128 | reactElement = (
129 |
stylingClicked("==")}
133 | >
134 |
135 | );
136 | break;
137 | }
138 | case "emoji": {
139 | reactElement = (
140 |
modals.emoji.open()}
144 | >
145 |
146 | );
147 | break;
148 | }
149 | case "fontawesome": {
150 | reactElement = (
151 |
modals.fontAwesome.open()}
155 | >
156 |
157 | );
158 | break;
159 | }
160 | case "quote": {
161 | reactElement = (
162 |
listClicked("> ")}
166 | >
167 |
168 | );
169 | break;
170 | }
171 | case "unordered-list": {
172 | reactElement = (
173 |
listClicked("- ")}
177 | >
178 |
179 | );
180 | break;
181 | }
182 | case "ordered-list": {
183 | reactElement = (
184 |
listClicked("1. ")}
188 | >
189 |
190 | );
191 | break;
192 | }
193 | case "unchecked-list": {
194 | reactElement = (
195 |
listClicked("- [ ] ")}
199 | >
200 |
201 | );
202 | break;
203 | }
204 | case "checked-list": {
205 | reactElement = (
206 |
listClicked("- [x] ")}
210 | >
211 |
212 | );
213 | break;
214 | }
215 | case "link": {
216 | reactElement = (
217 |
{
221 | const editor = store.editor;
222 | const mainSelection = editor.state.selection.main;
223 | const text = editor.state.sliceDoc(
224 | mainSelection.from,
225 | mainSelection.to,
226 | ) || "link";
227 | editor.dispatch({
228 | changes: {
229 | from: mainSelection.from,
230 | to: mainSelection.to,
231 | insert:
232 | `[${text}](https://github.com/markpluslabs/react-markplus/)`,
233 | },
234 | });
235 | }}
236 | >
237 |
238 | );
239 | break;
240 | }
241 | case "image": {
242 | reactElement = (
243 |
{
247 | const editor = store.editor;
248 | const mainSelection = editor.state.selection.main;
249 | const text = editor.state.sliceDoc(
250 | mainSelection.from,
251 | mainSelection.to,
252 | ) || "image";
253 | editor.dispatch({
254 | changes: {
255 | from: mainSelection.from,
256 | to: mainSelection.to,
257 | insert:
258 | ``,
259 | },
260 | });
261 | }}
262 | >
263 |
264 | );
265 | break;
266 | }
267 | case "code": {
268 | reactElement = (
269 |
{
273 | const editor = store.editor;
274 | const mainSelection = editor.state.selection.main;
275 | const text = editor.state.sliceDoc(
276 | mainSelection.from,
277 | mainSelection.to,
278 | ) || "console.log('Hello, world!');";
279 | editor.dispatch({
280 | changes: {
281 | from: mainSelection.from,
282 | to: mainSelection.to,
283 | insert: `\n\`\`\`\n${text}\n\`\`\`\n`,
284 | },
285 | });
286 | }}
287 | >
288 |
289 | );
290 | break;
291 | }
292 | case "table": {
293 | reactElement = (
294 |
{
298 | const sample = `
299 | header 1 | header 2
300 | ---|---
301 | row 1 col 1 | row 1 col 2
302 | row 2 col 1 | row 2 col 2`.trim();
303 | const editor = store.editor;
304 | const cursorPos = editor.state.selection.main.head;
305 | const currentLine = editor.state.doc.lineAt(cursorPos);
306 | const isAtLineStart = cursorPos === currentLine.from;
307 | if (isAtLineStart) {
308 | editor.dispatch({
309 | changes: {
310 | from: currentLine.from,
311 | to: currentLine.from,
312 | insert: `\n${sample}\n\n`,
313 | },
314 | });
315 | } else {
316 | editor.dispatch({
317 | changes: {
318 | from: currentLine.to,
319 | to: currentLine.to,
320 | insert: `\n\n${sample}\n`,
321 | },
322 | });
323 | }
324 | }}
325 | >
326 |
327 | );
328 | break;
329 | }
330 | case "math": {
331 | reactElement = (
332 |
{
336 | const editor = store.editor;
337 | const mainSelection = editor.state.selection.main;
338 | const text = editor.state.sliceDoc(
339 | mainSelection.from,
340 | mainSelection.to,
341 | ) || "E = mc^2";
342 | editor.dispatch({
343 | changes: {
344 | from: mainSelection.from,
345 | to: mainSelection.to,
346 | insert: `\n\`\`\`math\n${text}\n\`\`\`\n`,
347 | },
348 | });
349 | }}
350 | >
351 |
352 | );
353 | break;
354 | }
355 | case "flowchart": {
356 | reactElement = (
357 |
insertFence("flowchart", "A -> B")}
361 | >
362 |
363 | );
364 | break;
365 | }
366 | default: {
367 | throw new Error(`Unknown toolbar item: ${item}`);
368 | }
369 | }
370 | } else {
371 | reactElement = item as ReactElement;
372 | }
373 | return cloneElement(reactElement, { key: `item-${index}` });
374 | })}
375 |
376 | );
377 | };
378 |
379 | export default auto(Toolbar);
380 |
--------------------------------------------------------------------------------
/src/css/github-markdown.scss:
--------------------------------------------------------------------------------
1 | .markplus {
2 | &[data-theme="dark"] {
3 | .markdown-body {
4 | /* dark */
5 | color-scheme: dark;
6 | --focus-outlineColor: #1f6feb;
7 | --fgColor-default: #f0f6fc;
8 | --fgColor-muted: #9198a1;
9 | --fgColor-accent: #4493f8;
10 | --fgColor-success: #3fb950;
11 | --fgColor-attention: #d29922;
12 | --fgColor-danger: #f85149;
13 | --fgColor-done: #ab7df8;
14 | --bgColor-default: #0d1117;
15 | --bgColor-muted: #151b23;
16 | --bgColor-neutral-muted: #656c7633;
17 | --bgColor-attention-muted: #bb800926;
18 | --borderColor-default: #3d444d;
19 | --borderColor-muted: #3d444db3;
20 | --borderColor-neutral-muted: #3d444db3;
21 | --borderColor-accent-emphasis: #1f6feb;
22 | --borderColor-success-emphasis: #238636;
23 | --borderColor-attention-emphasis: #9e6a03;
24 | --borderColor-danger-emphasis: #da3633;
25 | --borderColor-done-emphasis: #8957e5;
26 | --color-prettylights-syntax-comment: #9198a1;
27 | --color-prettylights-syntax-constant: #79c0ff;
28 | --color-prettylights-syntax-constant-other-reference-link: #a5d6ff;
29 | --color-prettylights-syntax-entity: #d2a8ff;
30 | --color-prettylights-syntax-storage-modifier-import: #f0f6fc;
31 | --color-prettylights-syntax-entity-tag: #7ee787;
32 | --color-prettylights-syntax-keyword: #ff7b72;
33 | --color-prettylights-syntax-string: #a5d6ff;
34 | --color-prettylights-syntax-variable: #ffa657;
35 | --color-prettylights-syntax-brackethighlighter-unmatched: #f85149;
36 | --color-prettylights-syntax-brackethighlighter-angle: #9198a1;
37 | --color-prettylights-syntax-invalid-illegal-text: #f0f6fc;
38 | --color-prettylights-syntax-invalid-illegal-bg: #8e1519;
39 | --color-prettylights-syntax-carriage-return-text: #f0f6fc;
40 | --color-prettylights-syntax-carriage-return-bg: #b62324;
41 | --color-prettylights-syntax-string-regexp: #7ee787;
42 | --color-prettylights-syntax-markup-list: #f2cc60;
43 | --color-prettylights-syntax-markup-heading: #1f6feb;
44 | --color-prettylights-syntax-markup-italic: #f0f6fc;
45 | --color-prettylights-syntax-markup-bold: #f0f6fc;
46 | --color-prettylights-syntax-markup-deleted-text: #ffdcd7;
47 | --color-prettylights-syntax-markup-deleted-bg: #67060c;
48 | --color-prettylights-syntax-markup-inserted-text: #aff5b4;
49 | --color-prettylights-syntax-markup-inserted-bg: #033a16;
50 | --color-prettylights-syntax-markup-changed-text: #ffdfb6;
51 | --color-prettylights-syntax-markup-changed-bg: #5a1e02;
52 | --color-prettylights-syntax-markup-ignored-text: #f0f6fc;
53 | --color-prettylights-syntax-markup-ignored-bg: #1158c7;
54 | --color-prettylights-syntax-meta-diff-range: #d2a8ff;
55 | --color-prettylights-syntax-sublimelinter-gutter-mark: #3d444d;
56 | }
57 | }
58 | &[data-theme="light"] {
59 | .markdown-body {
60 | /* light */
61 | color-scheme: light;
62 | --focus-outlineColor: #0969da;
63 | --fgColor-default: #1f2328;
64 | --fgColor-muted: #59636e;
65 | --fgColor-accent: #0969da;
66 | --fgColor-success: #1a7f37;
67 | --fgColor-attention: #9a6700;
68 | --fgColor-danger: #d1242f;
69 | --fgColor-done: #8250df;
70 | --bgColor-default: #ffffff;
71 | --bgColor-muted: #f6f8fa;
72 | --bgColor-neutral-muted: #818b981f;
73 | --bgColor-attention-muted: #fff8c5;
74 | --borderColor-default: #d1d9e0;
75 | --borderColor-muted: #d1d9e0b3;
76 | --borderColor-neutral-muted: #d1d9e0b3;
77 | --borderColor-accent-emphasis: #0969da;
78 | --borderColor-success-emphasis: #1a7f37;
79 | --borderColor-attention-emphasis: #9a6700;
80 | --borderColor-danger-emphasis: #cf222e;
81 | --borderColor-done-emphasis: #8250df;
82 | --color-prettylights-syntax-comment: #59636e;
83 | --color-prettylights-syntax-constant: #0550ae;
84 | --color-prettylights-syntax-constant-other-reference-link: #0a3069;
85 | --color-prettylights-syntax-entity: #6639ba;
86 | --color-prettylights-syntax-storage-modifier-import: #1f2328;
87 | --color-prettylights-syntax-entity-tag: #0550ae;
88 | --color-prettylights-syntax-keyword: #cf222e;
89 | --color-prettylights-syntax-string: #0a3069;
90 | --color-prettylights-syntax-variable: #953800;
91 | --color-prettylights-syntax-brackethighlighter-unmatched: #82071e;
92 | --color-prettylights-syntax-brackethighlighter-angle: #59636e;
93 | --color-prettylights-syntax-invalid-illegal-text: #f6f8fa;
94 | --color-prettylights-syntax-invalid-illegal-bg: #82071e;
95 | --color-prettylights-syntax-carriage-return-text: #f6f8fa;
96 | --color-prettylights-syntax-carriage-return-bg: #cf222e;
97 | --color-prettylights-syntax-string-regexp: #116329;
98 | --color-prettylights-syntax-markup-list: #3b2300;
99 | --color-prettylights-syntax-markup-heading: #0550ae;
100 | --color-prettylights-syntax-markup-italic: #1f2328;
101 | --color-prettylights-syntax-markup-bold: #1f2328;
102 | --color-prettylights-syntax-markup-deleted-text: #82071e;
103 | --color-prettylights-syntax-markup-deleted-bg: #ffebe9;
104 | --color-prettylights-syntax-markup-inserted-text: #116329;
105 | --color-prettylights-syntax-markup-inserted-bg: #dafbe1;
106 | --color-prettylights-syntax-markup-changed-text: #953800;
107 | --color-prettylights-syntax-markup-changed-bg: #ffd8b5;
108 | --color-prettylights-syntax-markup-ignored-text: #d1d9e0;
109 | --color-prettylights-syntax-markup-ignored-bg: #0550ae;
110 | --color-prettylights-syntax-meta-diff-range: #8250df;
111 | --color-prettylights-syntax-sublimelinter-gutter-mark: #818b98;
112 | }
113 | }
114 | .markdown-body {
115 | --base-size-4: 0.25rem;
116 | --base-size-8: 0.5rem;
117 | --base-size-16: 1rem;
118 | --base-size-24: 1.5rem;
119 | --base-size-40: 2.5rem;
120 | --base-text-weight-normal: 400;
121 | --base-text-weight-medium: 500;
122 | --base-text-weight-semibold: 600;
123 | --fontStack-monospace:
124 | ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono,
125 | monospace;
126 | --fgColor-accent: Highlight;
127 | }
128 |
129 | .markdown-body {
130 | -ms-text-size-adjust: 100%;
131 | -webkit-text-size-adjust: 100%;
132 | margin: 0;
133 | color: var(--fgColor-default);
134 | background-color: var(--bgColor-default);
135 | font-family:
136 | -apple-system,
137 | BlinkMacSystemFont,
138 | "Segoe UI",
139 | "Noto Sans",
140 | Helvetica,
141 | Arial,
142 | sans-serif,
143 | "Apple Color Emoji",
144 | "Segoe UI Emoji";
145 | font-size: 16px;
146 | line-height: 1.5;
147 | word-wrap: break-word;
148 | scroll-behavior: auto !important;
149 | }
150 |
151 | .markdown-body .octicon {
152 | display: inline-block;
153 | fill: currentColor;
154 | vertical-align: text-bottom;
155 | }
156 |
157 | .markdown-body h1:hover .anchor .octicon-link:before,
158 | .markdown-body h2:hover .anchor .octicon-link:before,
159 | .markdown-body h3:hover .anchor .octicon-link:before,
160 | .markdown-body h4:hover .anchor .octicon-link:before,
161 | .markdown-body h5:hover .anchor .octicon-link:before,
162 | .markdown-body h6:hover .anchor .octicon-link:before {
163 | width: 16px;
164 | height: 16px;
165 | content: " ";
166 | display: inline-block;
167 | background-color: currentColor;
168 | -webkit-mask-image: url("data:image/svg+xml,