ui: drop vditor

This commit is contained in:
undefined committed 2024-06-30 00:24:41 +08:00
1 parent 979b8ce5af
commit 909c927986
16 files changed
+399 -153

No files matched your search

@@ -0,0 +1,235 @@
// Process ![test]( x =100x200)
// ^^^^^^^^ this size specification
function parseNextNumber(str, pos, max) {
const start = pos;
const result = {
ok: false,
pos,
value: '',
};
let code = str.charCodeAt(pos);
while (pos < max && (code >= 0x30 /* 0 */ && code <= 0x39 /* 9 */) || code === 0x25 /* % */) {
code = str.charCodeAt(++pos);
}
result.ok = true;
result.pos = pos;
result.value = str.slice(start, pos);
return result;
}
function parseImageSize(str, pos, max) {
const result = {
ok: false,
pos: 0,
width: '',
height: '',
};
if (pos >= max) { return result; }
let code = str.charCodeAt(pos);
if (code !== 0x3d /* = */) { return result; }
pos++;
code = str.charCodeAt(pos);
if (code !== 0x78 /* x */ && (code < 0x30 || code > 0x39) /* [0-9] */) {
return result;
}
// parse width
const resultW = parseNextNumber(str, pos, max);
pos = resultW.pos;
// next charactor must be 'x'
code = str.charCodeAt(pos);
if (code !== 0x78 /* x */) { return result; }
pos++;
// parse height
const resultH = parseNextNumber(str, pos, max);
pos = resultH.pos;
result.width = resultW.value;
result.height = resultH.value;
result.pos = pos;
result.ok = true;
return result;
}
export default function plugin(md) {
md.inline.ruler.before('emphasis', 'image', (state, silent) => {
let attrs;
let code;
let label;
let pos;
let ref;
let res;
let title;
let width = '';
let height = '';
let token;
let tokens;
let start;
let href = '';
const oldPos = state.pos;
const max = state.posMax;
if (state.src.charCodeAt(state.pos) !== 0x21/* ! */) { return false; }
if (state.src.charCodeAt(state.pos + 1) !== 0x5B/* [ */) { return false; }
const labelStart = state.pos + 2;
const labelEnd = md.helpers.parseLinkLabel(state, state.pos + 1, false);
// parser failed to find ']', so it's not a valid link
if (labelEnd < 0) { return false; }
pos = labelEnd + 1;
if (pos < max && state.src.charCodeAt(pos) === 0x28/* ( */) {
//
// Inline link
//
// [link]( <href> "title" )
// ^^ skipping these spaces
pos++;
for (; pos < max; pos++) {
code = state.src.charCodeAt(pos);
if (code !== 0x20 && code !== 0x0A) { break; }
}
if (pos >= max) { return false; }
// [link]( <href> "title" )
// ^^^^^^ parsing link destination
start = pos;
res = md.helpers.parseLinkDestination(state.src, pos, state.posMax);
if (res.ok) {
href = state.md.normalizeLink(res.str);
if (state.md.validateLink(href)) {
pos = res.pos;
} else {
href = '';
}
}
// [link]( <href> "title" )
// ^^ skipping these spaces
start = pos;
for (; pos < max; pos++) {
code = state.src.charCodeAt(pos);
if (code !== 0x20 && code !== 0x0A) { break; }
}
// [link]( <href> "title" )
// ^^^^^^^ parsing link title
res = md.helpers.parseLinkTitle(state.src, pos, state.posMax);
if (pos < max && start !== pos && res.ok) {
title = res.str;
pos = res.pos;
// [link]( <href> "title" )
// ^^ skipping these spaces
for (; pos < max; pos++) {
code = state.src.charCodeAt(pos);
if (code !== 0x20 && code !== 0x0A) { break; }
}
} else {
title = '';
}
// [link]( <href> "title" =WxH )
// ^^^^ parsing image size
if (pos - 1 >= 0) {
code = state.src.charCodeAt(pos - 1);
// there must be at least one white spaces
// between previous field and the size
if (code === 0x20) {
res = parseImageSize(state.src, pos, state.posMax);
if (res.ok) {
width = res.width;
height = res.height;
pos = res.pos;
// [link]( <href> "title" =WxH )
// ^^ skipping these spaces
for (; pos < max; pos++) {
code = state.src.charCodeAt(pos);
if (code !== 0x20 && code !== 0x0A) { break; }
}
}
}
}
if (pos >= max || state.src.charCodeAt(pos) !== 0x29/* ) */) {
state.pos = oldPos;
return false;
}
pos++;
} else {
//
// Link reference
//
if (typeof state.env.references === 'undefined') { return false; }
// [foo] [bar]
// ^^ optional whitespace (can include newlines)
for (; pos < max; pos++) {
code = state.src.charCodeAt(pos);
if (code !== 0x20 && code !== 0x0A) { break; }
}
if (pos < max && state.src.charCodeAt(pos) === 0x5B/* [ */) {
start = pos + 1;
pos = md.helpers.parseLinkLabel(state, pos);
if (pos >= 0) {
label = state.src.slice(start, pos++);
} else {
pos = labelEnd + 1;
}
} else {
pos = labelEnd + 1;
}
// covers label === '' and label === undefined
// (collapsed reference link and shortcut reference link respectively)
label ||= state.src.slice(labelStart, labelEnd);
ref = state.env.references[md.utils.normalizeReference(label)];
if (!ref) {
state.pos = oldPos;
return false;
}
href = ref.href;
title = ref.title;
}
//
// We found the end of the link, and know for a fact it's a valid link;
// so all that's left to do is to call tokenizer.
//
if (!silent) {
state.pos = labelStart;
state.posMax = labelEnd;
const newState = new state.md.inline.State(
state.src.slice(labelStart, labelEnd),
state.md,
state.env,
tokens = [],
);
newState.md.inline.tokenize(newState);
token = state.push('image', 'img', 0);
token.attrs = attrs = [['src', href],
['alt', '']];
token.children = tokens;
if (title) {
attrs.push(['title', title]);
}
if (width !== '') {
attrs.push(['width', width]);
}
if (height !== '') {
attrs.push(['height', height]);
}
}
state.pos = pos;
state.posMax = max;
return true;
});
};
@@ -1,7 +1,8 @@
// https://github.com/waylonflinn/markdown-it-katex/blob/master/index.js // https://github.com/waylonflinn/markdown-it-katex/blob/master/index.js
const katex = require('katex'); import { Logger } from 'hydrooj';
import katex from 'katex';
const logger = new global.Hydro.Logger('katex'); const logger = new Logger('katex');
function isValidDelim(state, pos) { function isValidDelim(state, pos) {
const max = state.posMax; const max = state.posMax;
@@ -109,8 +110,8 @@ function block(state, start, end, silent) {
return true; return true;
} }
module.exports = function plugin(md) { export default function plugin(md) {
const options = { throwOnError: false, strict: 'ignore' }; const options = { throwOnError: false, strict: 'ignore', displayMode: false };
const render = (latex: string, displayMode = false) => { const render = (latex: string, displayMode = false) => {
options.displayMode = displayMode; options.displayMode = displayMode;
try { try {
@@ -136,4 +137,4 @@ module.exports = function plugin(md) {
}); });
md.renderer.rules.math_inline = inlineRenderer; md.renderer.rules.math_inline = inlineRenderer;
md.renderer.rules.math_block = blockRenderer; md.renderer.rules.math_block = blockRenderer;
}; }
@@ -1,8 +1,8 @@
/* eslint-disable max-len */ /* eslint-disable max-len */
/* eslint-disable prefer-destructuring */ /* eslint-disable prefer-destructuring */
import { randomUUID } from 'crypto'; import type MarkdownIt from 'markdown-it';
import MarkdownIt from 'markdown-it'; import { v4 as uuid } from 'uuid';
const allowFullScreen = ' webkitallowfullscreen mozallowfullscreen allowfullscreen'; const allowFullScreen = ' webkitallowfullscreen mozallowfullscreen allowfullscreen';
@@ -110,7 +110,7 @@ export function Media(md: MarkdownIt) {
if (service === 'pdf') { if (service === 'pdf') {
if (src.startsWith('file://') || src.startsWith('./')) src += src.includes('?') ? '&noDisposition=1' : '?noDisposition=1'; if (src.startsWith('file://') || src.startsWith('./')) src += src.includes('?') ? '&noDisposition=1' : '?noDisposition=1';
return `\ return `\
<object classid="clsid:${randomUUID().toUpperCase()}"> <object classid="clsid:${uuid().toUpperCase()}">
<param name="SRC" value="${src}" > <param name="SRC" value="${src}" >
<embed width="100%" style="min-height: 100vh;border: none;" fullscreen="yes" src="${src}"> <embed width="100%" style="min-height: 100vh;border: none;" fullscreen="yes" src="${src}">
<noembed></noembed> <noembed></noembed>
+1 -1
View File
@@ -23,7 +23,7 @@ const MergeCells = require('markdown-it-merge-cells');
// @[youtube](https://youtube.com/watch?v=xxx) // @[youtube](https://youtube.com/watch?v=xxx)
// @[pdf](https://foo.com/bar.pdf) // @[pdf](https://foo.com/bar.pdf)
const { Media } = require('./markdown-it-media'); const { Media } = require('./markdown-it-media');
const Katex = require('./markdown-it-katex'); const { default: Katex } = require('./markdown-it-katex');
const { xssProtector } = require('./markdown-it-xss'); const { xssProtector } = require('./markdown-it-xss');
class Markdown extends MarkdownIt { class Markdown extends MarkdownIt {
+1 -2
View File
@@ -269,14 +269,13 @@ export default async function (env: { watch?: boolean, production?: boolean, mea
filename: '[name].css?[fullhash:6]', filename: '[name].css?[fullhash:6]',
}), }),
new WebpackManifestPlugin({}), new WebpackManifestPlugin({}),
new webpack.IgnorePlugin({ resourceRegExp: /(^\.\/locale$|mathjax|abcjs|vditor.+\.d\.ts)/ }), new webpack.IgnorePlugin({ resourceRegExp: /(^\.\/locale$)/ }),
new CopyWebpackPlugin({ new CopyWebpackPlugin({
patterns: [ patterns: [
{ from: root('static') }, { from: root('static') },
{ from: root('components/navigation/nav-logo-small_dark.png'), to: 'components/navigation/nav-logo-small_dark.png' }, { from: root('components/navigation/nav-logo-small_dark.png'), to: 'components/navigation/nav-logo-small_dark.png' },
{ from: root(`${dirname(require.resolve('streamsaver/package.json'))}/mitm.html`), to: 'streamsaver/mitm.html' }, { from: root(`${dirname(require.resolve('streamsaver/package.json'))}/mitm.html`), to: 'streamsaver/mitm.html' },
{ from: root(`${dirname(require.resolve('streamsaver/package.json'))}/sw.js`), to: 'streamsaver/sw.js' }, { from: root(`${dirname(require.resolve('streamsaver/package.json'))}/sw.js`), to: 'streamsaver/sw.js' },
{ from: root(`${dirname(require.resolve('vditor/package.json'))}/dist`), to: 'vditor/dist' },
{ from: root(`${dirname(require.resolve('graphiql/package.json'))}/graphiql.min.css`), to: 'graphiql.min.css' }, { from: root(`${dirname(require.resolve('graphiql/package.json'))}/graphiql.min.css`), to: 'graphiql.min.css' },
{ from: `${dirname(require.resolve('monaco-themes/package.json'))}/themes`, to: 'monaco/themes/' }, { from: `${dirname(require.resolve('monaco-themes/package.json'))}/themes`, to: 'monaco/themes/' },
], ],
-5
View File
@@ -127,11 +127,6 @@ async function main() {
if (fs.existsSync('public/theme.css')) { if (fs.existsSync('public/theme.css')) {
fs.copyFileSync('public/theme.css', `public/theme-${pkg.version}.css`); fs.copyFileSync('public/theme.css', `public/theme-${pkg.version}.css`);
} }
if (argv.options.production) {
for (const f of ['echarts', 'graphviz', 'mermaid', 'mathjax']) {
fs.removeSync(`public/vditor/dist/js/${f}`);
}
}
await Promise.all(globbySync('public/**/*.map').map((i) => fs.remove(i))); await Promise.all(globbySync('public/**/*.map').map((i) => fs.remove(i)));
} }
process.chdir(dir); process.chdir(dir);
@@ -1,12 +1,3 @@
.vditor
margin-bottom: rem(10px)
.vditor-preview__action
display: none
.vditor ol
list-style: decimal
.decoration-hide .decoration-hide
color: grey !important color: grey !important
background-color: grey !important background-color: grey !important
@@ -1,35 +1,10 @@
import $ from 'jquery'; import $ from 'jquery';
import { nanoid } from 'nanoid'; import { nanoid } from 'nanoid';
import React from 'react';
import ReactDOM from 'react-dom/client';
import DOMAttachedObject from 'vj/components/DOMAttachedObject'; import DOMAttachedObject from 'vj/components/DOMAttachedObject';
import Notification from 'vj/components/notification'; import { i18n } from 'vj/utils';
import { i18n, request } from 'vj/utils'; import uploadFiles from '../upload';
export const config = {
toolbar: [
'emoji', 'headings', 'bold', 'italic', 'strike', 'link', '|',
'list', 'ordered-list', 'check', 'outdent', 'indent', '|',
'quote', 'line', 'code', 'inline-code', 'table', '|',
'upload', 'edit-mode', 'fullscreen', 'export',
],
mode: UserContext.preferredEditorType || 'ir',
toolbarConfig: {
pin: true,
},
cdn: `${UiContext.cdn_prefix}vditor`,
counter: {
enable: true,
max: 65536,
},
preview: {
math: {
inlineDigit: true,
},
theme: {
path: `${UiContext.cdn_prefix}vditor/dist/css/content-theme`,
current: 'light',
},
},
};
interface MonacoOptions { interface MonacoOptions {
language?: string; language?: string;
@@ -42,23 +17,23 @@ interface MonacoOptions {
hide?: string[]; hide?: string[];
lineNumbers?: 'on' | 'off' | 'relative' | 'interval'; lineNumbers?: 'on' | 'off' | 'relative' | 'interval';
} }
interface VditorOptions { type Options = MonacoOptions;
theme?: 'classic' | 'dark'
}
type Options = MonacoOptions & VditorOptions;
export default class Editor extends DOMAttachedObject { export default class Editor extends DOMAttachedObject {
static DOMAttachKey = 'vjEditorInstance'; static DOMAttachKey = 'vjEditorInstance';
model: import('../monaco').default.editor.IModel; model: import('../monaco').default.editor.IModel;
editor: import('../monaco').default.editor.IStandaloneCodeEditor; editor: import('../monaco').default.editor.IStandaloneCodeEditor;
vditor: import('vditor').default; markdownEditor: any;
valueCache?: string;
setMarkdownEditorValue?: (v: string) => void;
reactRoot?: ReactDOM.Root;
isValid: boolean; isValid: boolean;
constructor($dom, public options: Options = {}) { constructor($dom, public options: Options = {}) {
super($dom); super($dom);
if (UserContext.preferredEditorType === 'monaco') this.initMonaco(); if (UserContext.preferredEditorType === 'monaco') this.initMonaco();
else if (options.language && options.language !== 'markdown') this.initMonaco(); else if (options.language && options.language !== 'markdown') this.initMonaco();
else this.initVditor(); else this.initMarkdownEditor();
} }
async initMonaco() { async initMonaco() {
@@ -168,87 +143,102 @@ export default class Editor extends DOMAttachedObject {
window.editor = this.editor; window.editor = this.editor;
} }
async initVditor() { async initMarkdownEditor() {
const pagename = document.documentElement.getAttribute('data-page'); const pagename = document.documentElement.getAttribute('data-page');
const isProblemPage = ['problem_create', 'problem_edit'].includes(pagename); const isProblemPage = ['problem_create', 'problem_edit'].includes(pagename);
const isProblemEdit = pagename === 'problem_edit'; const isProblemEdit = pagename === 'problem_edit';
const { default: Vditor } = await import('vditor'); const that = this;
const { $dom } = this; const { $dom } = this;
const hasFocus = $dom.is(':focus') || $dom.hasClass('autofocus'); const hasFocus = $dom.is(':focus') || $dom.hasClass('autofocus');
const origin = $dom.get(0); const origin = $dom.get(0);
const ele = document.createElement('div'); const ele = document.createElement('div');
const value = $dom.val(); const value = $dom.val();
const { onChange } = this.options; const { onChange } = this.options;
await new Promise((resolve) => { const { MdEditor } = await import('./mdeditor');
this.vditor = new Vditor(ele, {
...config, const renderCallback = (ref) => {
upload: { this.markdownEditor = ref;
multiple: false, };
handler: (files) => {
let wrapper = ['', '']; function EditorComponent() {
let ext: string; const [val, setVal] = React.useState(value);
const matches = files[0].type.match(/^image\/(png|jpg|jpeg|gif)$/i); that.setMarkdownEditorValue = setVal;
if (matches) { return <MdEditor
wrapper = ['![image](', ')']; className='textbox'
[, ext] = matches; autoFocus={hasFocus}
} else if (files[0].type === 'application/x-zip-compressed') { codeTheme='github'
wrapper = ['[file](', ')']; codeStyleReverse={false}
ext = 'zip'; ref={renderCallback}
} modelValue={val}
if (!ext) return i18n('No Supported file type.'); noMermaid
const filename = `${nanoid()}.${ext}`; noPrettier
const data = new FormData(); autoDetectCode
data.append('filename', filename); toolbarsExclude={[
data.append('file', files[0]); // 'bold',
data.append('operation', 'upload_file'); // 'underline',
if (isProblemEdit) data.append('type', 'additional_file'); // 'italic',
let progress = 0; // '-',
request.postFile(isProblemEdit ? './files' : '/file', data, { // 'strikeThrough',
xhr: () => { // 'title',
const xhr = new XMLHttpRequest(); // 'sub',
xhr.upload.addEventListener('loadstart', () => this.vditor.vditor.tip.show(i18n('Uploading...'), 0)); // 'sup',
xhr.upload.addEventListener('progress', (e) => { // 'quote',
if (!e.lengthComputable) return; // 'unorderedList',
const percentComplete = Math.round((e.loaded / e.total) * 100); // 'orderedList',
if (percentComplete === progress) return; // 'task',
progress = percentComplete; // '-',
this.vditor.vditor.tip.show(`${i18n('Uploading...')} ${percentComplete}%`, 0); // 'codeRow',
}, false); // 'code',
return xhr; // 'link',
}, // 'image',
}) // 'table',
.then(() => { 'mermaid',
this.vditor.insertValue(`${wrapper.join(`${isProblemPage ? 'file://' : `/file/${UserContext._id}/`}${filename}`)} `); // 'katex',
this.vditor.vditor.tip.hide(); // '-',
}) // 'revoke',
.catch((e: { message: string; }) => { // 'next',
console.error(e); 'save',
return `${i18n('Upload Failed')}: ${e.message}`; // '=',
}); 'pageFullscreen',
return null; 'fullscreen',
}, // 'preview',
}, 'previewOnly',
...this.options, 'htmlPreview',
after: () => { // 'catalog',
const pos = $(ele).find('button[data-mode="sv"]').get(); 'github',
const button = $('<button data-mode="monaco">Monaco Editor</button>'); ]}
button.on('click', (e) => { onChange={(v) => {
Notification.info('You can select this in PreferenceSettings'); that.valueCache = v;
e.preventDefault(); setVal(v);
});
button.insertAfter(pos);
resolve(null);
},
input(v) {
$dom.val(v); $dom.val(v);
$dom.text(v); $dom.text(v);
if (onChange) onChange(v); onChange?.(v);
}, }}
value, onUploadImg={async (files, callback) => {
cache: { id: Math.random().toString() }, let ext: string;
}); const matches = files[0].type.match(/^image\/(png|jpg|jpeg|gif)$/i);
}); if (matches) {
$(ele).addClass('textbox'); [, ext] = matches;
} else if (files[0].type === 'application/x-zip-compressed') {
ext = 'zip';
}
if (!ext) return i18n('No Supported file type.');
const filename = `${nanoid()}.${ext}`;
await uploadFiles(isProblemEdit ? './files' : '/file', [files[0]], {
type: isProblemEdit ? 'additional_file' : undefined,
filenameCallback: () => filename,
}).then(() => {
callback([`${isProblemPage ? 'file://' : `/file/${UserContext._id}/`}${filename}`]);
}).catch(() => {
callback([]);
});
return null;
}}
/>;
}
this.reactRoot = ReactDOM.createRoot(ele);
this.reactRoot.render(<EditorComponent />);
$dom.hide(); $dom.hide();
origin.parentElement.appendChild(ele); origin.parentElement.appendChild(ele);
this.isValid = true; this.isValid = true;
@@ -257,7 +247,7 @@ export default class Editor extends DOMAttachedObject {
destroy() { destroy() {
this.detach(); this.detach();
if (this.vditor?.destroy) this.vditor.destroy(); if (this.reactRoot) this.reactRoot.unmount();
else if (this.editor?.dispose) this.editor.dispose(); else if (this.editor?.dispose) this.editor.dispose();
} }
@@ -267,13 +257,17 @@ export default class Editor extends DOMAttachedObject {
value(val?: string) { value(val?: string) {
this.ensureValid(); this.ensureValid();
if (typeof val === 'string') return (this.editor || this.vditor).setValue(val); if (typeof val === 'string') {
return (this.editor || this.vditor).getValue({ lineEnding: '\n', preserveBOM: false }); if (this.editor) return this.editor.setValue(val);
this.setMarkdownEditorValue?.(val);
this.markdownEditor?.resetHistory?.();
}
if (this.editor) return this.editor.getValue({ lineEnding: '\n', preserveBOM: false });
return this.valueCache;
} }
focus() { focus() {
this.ensureValid(); this.ensureValid();
this.vditor?.focus();
if (!this.editor || !this.model) return; if (!this.editor || !this.model) return;
this.editor.focus(); this.editor.focus();
const range = this.model.getFullModelRange(); const range = this.model.getFullModelRange();
@@ -0,0 +1,39 @@
import 'md-editor-rt/lib/style.css';
import Anchor from 'markdown-it-anchor';
import Footnote from 'markdown-it-footnote';
import Mark from 'markdown-it-mark';
import MergeCells from 'markdown-it-merge-cells';
import TOC from 'markdown-it-table-of-contents';
import { config, MdEditor } from 'md-editor-rt';
import Imsize from '../../backendlib/markdown-it-imsize';
import { Media } from '../../backendlib/markdown-it-media';
import { xssProtector } from '../../backendlib/markdown-it-xss';
const pagename = document.documentElement.getAttribute('data-page');
const isProblemPage = ['problem_create', 'problem_edit'].includes(pagename);
config({
markdownItConfig(mdit) {
mdit.options.html = true;
mdit.options.linkify = true;
mdit.linkify.tlds('.py', false);
mdit.linkify.tlds('.zip', false);
mdit.linkify.tlds('.mov', false);
mdit.use(Media);
mdit.use(Footnote);
mdit.use(Mark);
mdit.use(Imsize);
mdit.use(Anchor);
mdit.use(TOC);
mdit.use(MergeCells);
mdit.use(xssProtector);
if (isProblemPage) {
mdit.core.ruler.before('normalize', 'xss', (state) => {
state.src = state.src.replace(/file:\/\//g, pagename === 'problem_create' ? `/file/${UserContext._id}/` : './file/');
});
}
},
});
export { MdEditor };
+2 -1
View File
@@ -13,6 +13,7 @@ interface UploadOptions {
pjax?: boolean; pjax?: boolean;
sidebar?: boolean; sidebar?: boolean;
singleFileUploadCallback?: (file: File) => any; singleFileUploadCallback?: (file: File) => any;
filenameCallback?: (file: File) => string;
} }
export default async function uploadFiles(endpoint = '', files: File[] | FileList = [], options: UploadOptions = {}) { export default async function uploadFiles(endpoint = '', files: File[] | FileList = [], options: UploadOptions = {}) {
const dialog = new Dialog({ const dialog = new Dialog({
@@ -38,7 +39,7 @@ export default async function uploadFiles(endpoint = '', files: File[] | FileLis
if (Number.isNaN(+i)) continue; if (Number.isNaN(+i)) continue;
const file = files[i]; const file = files[i];
const data = new FormData(); const data = new FormData();
data.append('filename', file.name); data.append('filename', options.filenameCallback?.(file) || file.name);
data.append('file', file); data.append('file', file);
if (options.type) data.append('type', options.type); if (options.type) data.append('type', options.type);
data.append('operation', 'upload_file'); data.append('operation', 'upload_file');
+6 -4
View File
@@ -25,7 +25,7 @@
"@fontsource/source-code-pro": "^5.0.18", "@fontsource/source-code-pro": "^5.0.18",
"@fontsource/ubuntu-mono": "^5.0.20", "@fontsource/ubuntu-mono": "^5.0.20",
"@hydrooj/utils": "workspace:^", "@hydrooj/utils": "workspace:^",
"@sentry/browser": "^8.12.0", "@sentry/browser": "^8.13.0",
"@sentry/webpack-plugin": "^2.20.1", "@sentry/webpack-plugin": "^2.20.1",
"@simplewebauthn/browser": "7.4.0", "@simplewebauthn/browser": "7.4.0",
"@svgr/webpack": "^8.1.0", "@svgr/webpack": "^8.1.0",
@@ -40,6 +40,7 @@
"@types/react": "^18.3.3", "@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.0",
"@types/redux-logger": "^3.0.13", "@types/redux-logger": "^3.0.13",
"@types/uuid": "^10",
"@types/webpack-env": "^1.18.5", "@types/webpack-env": "^1.18.5",
"@vscode/codicons": "^0.0.36", "@vscode/codicons": "^0.0.36",
"ajv": "^8.16.0", "ajv": "^8.16.0",
@@ -56,7 +57,7 @@
"css-loader": "^6.11.0", "css-loader": "^6.11.0",
"diff": "^5.2.0", "diff": "^5.2.0",
"diff-dom": "^5.1.3", "diff-dom": "^5.1.3",
"echarts": "^5.5.0", "echarts": "^5.5.1",
"emojis-keywords": "2.0.0", "emojis-keywords": "2.0.0",
"emojis-list": "2.1.0", "emojis-list": "2.1.0",
"esbuild-loader": "^4.2.0", "esbuild-loader": "^4.2.0",
@@ -73,9 +74,10 @@
"jquery.easing": "^1.4.1", "jquery.easing": "^1.4.1",
"jquery.transit": "^0.9.12", "jquery.transit": "^0.9.12",
"matchmedia-polyfill": "^0.3.2", "matchmedia-polyfill": "^0.3.2",
"md-editor-rt": "^4.17.0",
"mini-css-extract-plugin": "^2.9.0", "mini-css-extract-plugin": "^2.9.0",
"moment": "^2.30.1", "moment": "^2.30.1",
"monaco-editor": "0.46.0", "monaco-editor": "0.50.0",
"monaco-editor-nls": "^3.1.0", "monaco-editor-nls": "^3.1.0",
"monaco-editor-webpack-plugin": "^7.1.0", "monaco-editor-webpack-plugin": "^7.1.0",
"monaco-themes": "^0.4.4", "monaco-themes": "^0.4.4",
@@ -118,7 +120,6 @@
"timeago-react": "^3.0.6", "timeago-react": "^3.0.6",
"timeago.js": "^4.0.2", "timeago.js": "^4.0.2",
"ts-loader": "^9.5.1", "ts-loader": "^9.5.1",
"vditor": "^3.10.4",
"vinyl-buffer": "^1.0.1", "vinyl-buffer": "^1.0.1",
"web-streams-polyfill": "^4.0.0", "web-streams-polyfill": "^4.0.0",
"webpack": "^5.92.1", "webpack": "^5.92.1",
@@ -147,6 +148,7 @@
"nunjucks": "^3.2.4", "nunjucks": "^3.2.4",
"schemastery-jsonschema": "^1.1.0", "schemastery-jsonschema": "^1.1.0",
"streamsaver": "^2.0.6", "streamsaver": "^2.0.6",
"uuid": "^10.0.0",
"xss": "^1.0.15" "xss": "^1.0.15"
} }
} }
@@ -154,7 +154,7 @@ async function handleSection(ev, sidebar, type) {
$section.removeClass('animating'); $section.removeClass('animating');
} }
export default new NamedPage(['problem_create', 'problem_edit'], (pagename) => { export default new NamedPage(['problem_create', 'problem_edit'], () => {
let confirmed = false; let confirmed = false;
$(document).on('click', '[name="operation"]', (ev) => { $(document).on('click', '[name="operation"]', (ev) => {
ev.preventDefault(); ev.preventDefault();
@@ -240,14 +240,6 @@ export default new NamedPage(['problem_create', 'problem_edit'], (pagename) => {
await download(`${pdoc.docId} ${pdoc.title}.zip`, targets); await download(`${pdoc.docId} ${pdoc.title}.zip`, targets);
} }
setInterval(() => {
$('img').each(function () {
if (this.src.startsWith('file://')) {
$(this).attr('src', $(this).attr('src').replace('file://', (pagename === 'problem_create' ? `/file/${UserContext._id}/` : './file/')));
}
});
}, 500);
const $main = $('textarea[data-editor]'); const $main = $('textarea[data-editor]');
const $field = $('textarea[data-markdown-upload]'); const $field = $('textarea[data-markdown-upload]');
let content = $field.val(); let content = $field.val();
+1 -1
View File
@@ -89,7 +89,7 @@ self.addEventListener('notificationclick', (event) => {
}); });
const PRECACHE = `precache-${process.env.VERSION}`; const PRECACHE = `precache-${process.env.VERSION}`;
const DO_NOT_PRECACHE = ['vditor', '.worker.js', 'fonts', 'i.monaco']; const DO_NOT_PRECACHE = ['.worker.js', 'fonts', 'i.monaco'];
function shouldCachePath(path: string) { function shouldCachePath(path: string) {
if (!path.split('?')[0].split('/').pop()) return false; if (!path.split('?')[0].split('/').pop()) return false;
-2
View File
@@ -43,8 +43,6 @@ nav_logo_dark:
default: /components/navigation/nav-logo-small_dark.png default: /components/navigation/nav-logo-small_dark.png
preferredEditorType: preferredEditorType:
type: type:
wysiwyg: WYSIWYG
ir: Instant Rendering
sv: Split View sv: Split View
monaco: Monaco Editor monaco: Monaco Editor
family: markdown family: markdown
+1 -1
View File
@@ -196,7 +196,7 @@ $hover-background-color = #424242
background-color: #171717 background-color: #171717
color: #999 color: #999
pre:not(.vditor-sv), code pre, code
background-color: #171717 background-color: #171717
.scratchpad__toolbar .scratchpad__toolbar
-1
View File
@@ -1,5 +1,4 @@
import 'normalize.css/normalize.css'; import 'normalize.css/normalize.css';
import 'vditor/dist/index.css';
import 'allotment/dist/style.css'; import 'allotment/dist/style.css';
import 'pickadate/lib/themes/classic.css'; import 'pickadate/lib/themes/classic.css';
import 'pickadate/lib/themes/classic.date.css'; import 'pickadate/lib/themes/classic.date.css';