diff --git a/web/package-lock.json b/web/package-lock.json
index e6f47b219..9cabacb2f 100644
--- a/web/package-lock.json
+++ b/web/package-lock.json
@@ -18,6 +18,7 @@
"preact-router": "^4.1.0",
"react": "npm:@preact/compat@^17.1.2",
"react-dom": "npm:@preact/compat@^17.1.2",
+ "react-editor-js": "^2.1.0",
"swr": "^1.3.0",
"video.js": "^7.20.3",
"videojs-playlist": "^5.0.0",
@@ -512,11 +513,39 @@
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
"dev": true
},
+ "node_modules/@codexteam/icons": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/@codexteam/icons/-/icons-0.0.4.tgz",
+ "integrity": "sha512-V8N/TY2TGyas4wLrPIFq7bcow68b3gu8DfDt1+rrHPtXxcexadKauRJL6eQgfG7Z0LCrN4boLRawR4S9gjIh/Q==",
+ "peer": true
+ },
"node_modules/@cycjimmy/jsmpeg-player": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/@cycjimmy/jsmpeg-player/-/jsmpeg-player-6.0.5.tgz",
"integrity": "sha512-bVNHQ7VN9ecKT5AI/6RC7zpW/y4ca68a9txeR5Wiin+jKpUn/7buMe+5NPub89A8NNeNnKPQfrD2+c76ch36mA=="
},
+ "node_modules/@editorjs/editorjs": {
+ "version": "2.26.1",
+ "resolved": "https://registry.npmjs.org/@editorjs/editorjs/-/editorjs-2.26.1.tgz",
+ "integrity": "sha512-sm1GQJ/B50CcF5xAO2MLgNPflR5b2B+qzazAvBZTot/+7dcKbtm8Az2zUiOyBOOcXHCgg1XFdpU5plBL58CYPQ==",
+ "peer": true,
+ "dependencies": {
+ "@codexteam/icons": "^0.0.4",
+ "codex-notifier": "^1.1.2",
+ "codex-tooltip": "^1.0.5",
+ "html-janitor": "^2.0.4",
+ "nanoid": "^3.1.22"
+ }
+ },
+ "node_modules/@editorjs/paragraph": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/@editorjs/paragraph/-/paragraph-2.9.0.tgz",
+ "integrity": "sha512-lI6x1eiqFx2X3KmMak6gBlimFqXhG35fQpXMxzrjIphNLr4uPsXhVbyMPimPoLUnS9rM6Q00vptXmP2QNDd0zg==",
+ "peer": true,
+ "dependencies": {
+ "@codexteam/icons": "^0.0.4"
+ }
+ },
"node_modules/@esbuild/android-arm": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.13.tgz",
@@ -982,6 +1011,41 @@
"vite": ">=2.0.0-beta.3"
}
},
+ "node_modules/@react-editor-js/client": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@react-editor-js/client/-/client-2.1.0.tgz",
+ "integrity": "sha512-X/OwqY2KKUUVIy24zvubHTI4qhiVIWMxGlU/Bc6tkf9gcsWgQjPvn75DwrQ5SIGBOYpSBcHY01JX0RRtJHRumw==",
+ "dependencies": {
+ "@react-editor-js/core": "2.1.0"
+ },
+ "peerDependencies": {
+ "@editorjs/editorjs": "*",
+ "@editorjs/paragraph": "*",
+ "react": "*"
+ }
+ },
+ "node_modules/@react-editor-js/core": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@react-editor-js/core/-/core-2.1.0.tgz",
+ "integrity": "sha512-6h98D5g2RatdCDQRW/+N+eiMtjoq0n1n02m8oA7FrlEzEaOUyV+J+rmtmRS8OexLVOiNeWaSPDpVJ3MNiVLq8Q==",
+ "peerDependencies": {
+ "@editorjs/editorjs": "*",
+ "react": "*"
+ }
+ },
+ "node_modules/@react-editor-js/server": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@react-editor-js/server/-/server-2.1.0.tgz",
+ "integrity": "sha512-RnTbCmzCHbjcjPNKhr9FaehItHgAZyd47Lq+iIGynFqWSsrwGy46LgOXSvb50DYbwDyT0GtxaXBIk6aiFki1Dg==",
+ "dependencies": {
+ "@react-editor-js/core": "2.1.0"
+ },
+ "peerDependencies": {
+ "@editorjs/editorjs": "*",
+ "@editorjs/paragraph": "*",
+ "react": "*"
+ }
+ },
"node_modules/@rollup/pluginutils": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz",
@@ -2916,6 +2980,18 @@
"node": ">=0.8"
}
},
+ "node_modules/codex-notifier": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/codex-notifier/-/codex-notifier-1.1.2.tgz",
+ "integrity": "sha512-DCp6xe/LGueJ1N5sXEwcBc3r3PyVkEEDNWCVigfvywAkeXcZMk9K41a31tkEFBW0Ptlwji6/JlAb49E3Yrxbtg==",
+ "peer": true
+ },
+ "node_modules/codex-tooltip": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/codex-tooltip/-/codex-tooltip-1.0.5.tgz",
+ "integrity": "sha512-IuA8LeyLU5p1B+HyhOsqR6oxyFQ11k3i9e9aXw40CrHFTRO2Y1npNBVU3W1SvhKAbUU7R/YikUBdcYFP0RcJag==",
+ "peer": true
+ },
"node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -4943,6 +5019,12 @@
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
"dev": true
},
+ "node_modules/html-janitor": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/html-janitor/-/html-janitor-2.0.4.tgz",
+ "integrity": "sha512-92J5h9jNZRk30PMHapjHEJfkrBWKCOy0bq3oW2pBungky6lzYSoboBGPMvxl1XRKB2q+kniQmsLsPbdpY7RM2g==",
+ "peer": true
+ },
"node_modules/http-basic": {
"version": "8.1.3",
"resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz",
@@ -6769,7 +6851,6 @@
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
- "dev": true,
"bin": {
"nanoid": "bin/nanoid.cjs"
},
@@ -7612,6 +7693,16 @@
"preact": "*"
}
},
+ "node_modules/react-editor-js": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/react-editor-js/-/react-editor-js-2.1.0.tgz",
+ "integrity": "sha512-unI9D2pTH/2gBenc6LgCXJm8iqnrzB71CHgfjQmaB+lGR0Njx+ZXydgUQm1VofMmvF6vcCNVDE1Eb47zQbm14g==",
+ "dependencies": {
+ "@react-editor-js/client": "2.1.0",
+ "@react-editor-js/core": "2.1.0",
+ "@react-editor-js/server": "2.1.0"
+ }
+ },
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
@@ -9496,11 +9587,39 @@
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
"dev": true
},
+ "@codexteam/icons": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/@codexteam/icons/-/icons-0.0.4.tgz",
+ "integrity": "sha512-V8N/TY2TGyas4wLrPIFq7bcow68b3gu8DfDt1+rrHPtXxcexadKauRJL6eQgfG7Z0LCrN4boLRawR4S9gjIh/Q==",
+ "peer": true
+ },
"@cycjimmy/jsmpeg-player": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/@cycjimmy/jsmpeg-player/-/jsmpeg-player-6.0.5.tgz",
"integrity": "sha512-bVNHQ7VN9ecKT5AI/6RC7zpW/y4ca68a9txeR5Wiin+jKpUn/7buMe+5NPub89A8NNeNnKPQfrD2+c76ch36mA=="
},
+ "@editorjs/editorjs": {
+ "version": "2.26.1",
+ "resolved": "https://registry.npmjs.org/@editorjs/editorjs/-/editorjs-2.26.1.tgz",
+ "integrity": "sha512-sm1GQJ/B50CcF5xAO2MLgNPflR5b2B+qzazAvBZTot/+7dcKbtm8Az2zUiOyBOOcXHCgg1XFdpU5plBL58CYPQ==",
+ "peer": true,
+ "requires": {
+ "@codexteam/icons": "^0.0.4",
+ "codex-notifier": "^1.1.2",
+ "codex-tooltip": "^1.0.5",
+ "html-janitor": "^2.0.4",
+ "nanoid": "^3.1.22"
+ }
+ },
+ "@editorjs/paragraph": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/@editorjs/paragraph/-/paragraph-2.9.0.tgz",
+ "integrity": "sha512-lI6x1eiqFx2X3KmMak6gBlimFqXhG35fQpXMxzrjIphNLr4uPsXhVbyMPimPoLUnS9rM6Q00vptXmP2QNDd0zg==",
+ "peer": true,
+ "requires": {
+ "@codexteam/icons": "^0.0.4"
+ }
+ },
"@esbuild/android-arm": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.13.tgz",
@@ -9852,6 +9971,28 @@
"@rollup/pluginutils": "^4.1.0"
}
},
+ "@react-editor-js/client": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@react-editor-js/client/-/client-2.1.0.tgz",
+ "integrity": "sha512-X/OwqY2KKUUVIy24zvubHTI4qhiVIWMxGlU/Bc6tkf9gcsWgQjPvn75DwrQ5SIGBOYpSBcHY01JX0RRtJHRumw==",
+ "requires": {
+ "@react-editor-js/core": "2.1.0"
+ }
+ },
+ "@react-editor-js/core": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@react-editor-js/core/-/core-2.1.0.tgz",
+ "integrity": "sha512-6h98D5g2RatdCDQRW/+N+eiMtjoq0n1n02m8oA7FrlEzEaOUyV+J+rmtmRS8OexLVOiNeWaSPDpVJ3MNiVLq8Q==",
+ "requires": {}
+ },
+ "@react-editor-js/server": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@react-editor-js/server/-/server-2.1.0.tgz",
+ "integrity": "sha512-RnTbCmzCHbjcjPNKhr9FaehItHgAZyd47Lq+iIGynFqWSsrwGy46LgOXSvb50DYbwDyT0GtxaXBIk6aiFki1Dg==",
+ "requires": {
+ "@react-editor-js/core": "2.1.0"
+ }
+ },
"@rollup/pluginutils": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz",
@@ -11232,6 +11373,18 @@
"integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
"dev": true
},
+ "codex-notifier": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/codex-notifier/-/codex-notifier-1.1.2.tgz",
+ "integrity": "sha512-DCp6xe/LGueJ1N5sXEwcBc3r3PyVkEEDNWCVigfvywAkeXcZMk9K41a31tkEFBW0Ptlwji6/JlAb49E3Yrxbtg==",
+ "peer": true
+ },
+ "codex-tooltip": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/codex-tooltip/-/codex-tooltip-1.0.5.tgz",
+ "integrity": "sha512-IuA8LeyLU5p1B+HyhOsqR6oxyFQ11k3i9e9aXw40CrHFTRO2Y1npNBVU3W1SvhKAbUU7R/YikUBdcYFP0RcJag==",
+ "peer": true
+ },
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -12654,6 +12807,12 @@
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
"dev": true
},
+ "html-janitor": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/html-janitor/-/html-janitor-2.0.4.tgz",
+ "integrity": "sha512-92J5h9jNZRk30PMHapjHEJfkrBWKCOy0bq3oW2pBungky6lzYSoboBGPMvxl1XRKB2q+kniQmsLsPbdpY7RM2g==",
+ "peer": true
+ },
"http-basic": {
"version": "8.1.3",
"resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz",
@@ -13994,8 +14153,7 @@
"nanoid": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
- "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
- "dev": true
+ "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="
},
"natural-compare": {
"version": "1.4.0",
@@ -14567,6 +14725,16 @@
"integrity": "sha512-7pOZN9lMDDRQ+6aWvjwTp483KR8/zOpfS83wmOo3zfuLKdngS8/5RLbsFWzFZMGdYlotAhX980hJ75bjOHTwWg==",
"requires": {}
},
+ "react-editor-js": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/react-editor-js/-/react-editor-js-2.1.0.tgz",
+ "integrity": "sha512-unI9D2pTH/2gBenc6LgCXJm8iqnrzB71CHgfjQmaB+lGR0Njx+ZXydgUQm1VofMmvF6vcCNVDE1Eb47zQbm14g==",
+ "requires": {
+ "@react-editor-js/client": "2.1.0",
+ "@react-editor-js/core": "2.1.0",
+ "@react-editor-js/server": "2.1.0"
+ }
+ },
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
diff --git a/web/package.json b/web/package.json
index b81d2fc96..78da6e3b3 100644
--- a/web/package.json
+++ b/web/package.json
@@ -22,6 +22,7 @@
"preact-router": "^4.1.0",
"react": "npm:@preact/compat@^17.1.2",
"react-dom": "npm:@preact/compat@^17.1.2",
+ "react-editor-js": "^2.1.0",
"swr": "^1.3.0",
"video.js": "^7.20.3",
"videojs-playlist": "^5.0.0",
diff --git a/web/src/routes/Config.jsx b/web/src/routes/Config.jsx
new file mode 100644
index 000000000..9e373ccfa
--- /dev/null
+++ b/web/src/routes/Config.jsx
@@ -0,0 +1,22 @@
+import { h, Fragment } from 'preact';
+import useSWR from 'swr';
+import { createReactEditorJS } from 'react-editor-js';
+import ActivityIndicator from '../components/ActivityIndicator';
+import Heading from '../components/Heading';
+
+export default function Config() {
+ const { data: config } = useSWR('config/raw');
+ const ReactEditorJS = createReactEditorJS();
+
+ if (!config) {
+ return