diff --git a/packages/ui-default/build/config/webpack.js b/packages/ui-default/build/config/webpack.js index 6bd105436..44712341f 100644 --- a/packages/ui-default/build/config/webpack.js +++ b/packages/ui-default/build/config/webpack.js @@ -204,7 +204,6 @@ export default function (env = {}) { new CopyWebpackPlugin({ patterns: [ { from: root('static') }, - { from: root(`${dirname(require.resolve('vditor/package.json'))}/dist`), to: 'vditor/dist' }, { from: `${dirname(require.resolve('monaco-themes/package.json'))}/themes`, to: 'monaco/themes/' }, { from: root('.build/sharedworker.js'), to: 'sharedworker.js' }, ], diff --git a/packages/ui-default/build/main.ts b/packages/ui-default/build/main.ts index e97125b83..916ce631a 100644 --- a/packages/ui-default/build/main.ts +++ b/packages/ui-default/build/main.ts @@ -98,13 +98,6 @@ async function main() { if (fs.existsSync('public/default.theme.css')) { fs.copyFileSync('public/default.theme.css', `public/default-${pkg.version}.theme.css`); } - if (argv.options.production) { - fs.removeSync('public/vditor/dist/js/mathjax'); - fs.removeSync('public/vditor/dist/js/echarts'); - fs.removeSync('public/vditor/dist/js/graphviz'); - fs.removeSync('public/vditor/dist/js/mermaid'); - fs.removeSync('public/vditor/dist/js/abcjs'); - } } process.chdir(dir); } diff --git a/packages/ui-default/components/editor/cmeditor.styl b/packages/ui-default/components/editor/cmeditor.styl index c5237498b..5474f21b7 100644 --- a/packages/ui-default/components/editor/cmeditor.styl +++ b/packages/ui-default/components/editor/cmeditor.styl @@ -1,12 +1,7 @@ -.vditor - margin-bottom: rem(10px) - -.vditor-preview__action - display: none - -.vditor ol - list-style: decimal - .decoration-hide color: grey !important background-color: grey !important + +.code-fence_selector + height: 2.2em !important + width: 10em !important diff --git a/packages/ui-default/components/editor/index.ts b/packages/ui-default/components/editor/index.ts index 001da120b..ef16cec97 100644 --- a/packages/ui-default/components/editor/index.ts +++ b/packages/ui-default/components/editor/index.ts @@ -1,32 +1,11 @@ import _ from 'lodash'; import Notification from 'vj/components/notification'; import DOMAttachedObject from 'vj/components/DOMAttachedObject'; +import type { Node } from 'prosemirror-model'; import { nanoid } from 'nanoid'; import i18n from 'vj/utils/i18n'; import request from 'vj/utils/request'; - -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, - }, - }, -}; +import tpl from 'vj/utils/tpl'; interface MonacoOptions { language?: string; @@ -38,26 +17,35 @@ interface MonacoOptions { value?: string; hide?: string[]; } -interface VditorOptions { - theme?: 'classic' | 'dark' -} -type Options = MonacoOptions & VditorOptions; +type Options = MonacoOptions; -export default class Editor extends DOMAttachedObject { +export default class VjEditor extends DOMAttachedObject { static DOMAttachKey = 'vjEditorInstance'; + isValid: boolean; + private lock: boolean; model: import('../monaco').default.editor.IModel; editor: import('../monaco').default.editor.IStandaloneCodeEditor; - vditor: import('vditor').default; - isValid: boolean; + milkdown: import('@milkdown/core').Editor & { + setValue?: (val: string) => void; + getValue?: () => string; + dispose?: () => void; + }; + + height = 0; constructor($dom, public options: Options = {}) { super($dom); - if (UserContext.preferredEditorType === 'monaco') this.initMonaco(); - else if (options.language && options.language !== 'markdown') this.initMonaco(); - else this.initVditor(); + $dom.hide(); + const root = $('
'); + const left = $('
'); + const right = $('
'); + root.append(left, right); + $($dom).parent().append(root); + this.initMonaco(left.get(0)); + if ((options?.language || 'markdown') === 'markdown') this.initMilkdown(right.get(0)); } - async initMonaco() { + async initMonaco(ele) { const { load } = await import('vj/components/monaco/loader'); const { onChange, language = 'markdown', @@ -67,16 +55,9 @@ export default class Editor extends DOMAttachedObject { hide = [], } = this.options; const { monaco, registerAction } = await load([language]); - const { $dom } = this; - const hasFocus = $dom.is(':focus') || $dom.hasClass('autofocus'); - const origin = $dom.get(0); - const ele = document.createElement('div'); - $(ele).width('100%').addClass('textbox'); - if (!autoResize && $dom.height()) $(ele).height($dom.height()); - $dom.hide(); - origin.parentElement.appendChild(ele); - const value = this.options.value || $dom.val(); - // eslint-disable-next-line no-nested-ternary + const hasFocus = this.$dom.is(':focus') || this.$dom.hasClass('autofocus'); + if (!autoResize && this.$dom.height()) $(ele).height(this.$dom.height()); + const value = this.options.value || this.$dom.val(); this.model = typeof model === 'string' ? monaco.editor.getModel(monaco.Uri.parse(model)) || monaco.editor.createModel(value, language === 'auto' ? undefined : language, monaco.Uri.parse(model)) @@ -100,7 +81,7 @@ export default class Editor extends DOMAttachedObject { if (!editorElement) return; const lineHeight = this.editor.getOption(monaco.editor.EditorOption.lineHeight); const lineCount = this.editor.getModel()?.getLineCount() || 1; - let height = this.editor.getTopForLineNumber(lineCount + 1) + lineHeight; + let height = Math.max(this.editor.getTopForLineNumber(lineCount + 1) + lineHeight, this.height); if (prevHeight !== height) { if (window.innerHeight * 1.5 < height) { height = window.innerHeight; @@ -147,9 +128,13 @@ export default class Editor extends DOMAttachedObject { } this.editor.onDidChangeModelContent(() => { const val = this.editor.getValue({ lineEnding: '\n', preserveBOM: false }); - $dom.val(val); - $dom.text(val); - if (onChange) onChange(val); + this.$dom.val(val); + this.$dom.text(val); + if (this.lock) return; + this.lock = true; + this.milkdown?.setValue(val); + onChange?.(val); + this.lock = false; }); this.isValid = true; if (hasFocus) this.focus(); @@ -160,97 +145,111 @@ export default class Editor extends DOMAttachedObject { window.editor = this.editor; } - async initVditor() { + async initMilkdown(ele) { const pagename = document.documentElement.getAttribute('data-page'); const isProblemPage = ['problem_create', 'problem_edit'].includes(pagename); const isProblemEdit = pagename === 'problem_edit'; - const { default: Vditor } = await import('vditor'); - const { $dom } = this; - const hasFocus = $dom.is(':focus') || $dom.hasClass('autofocus'); - const origin = $dom.get(0); - const ele = document.createElement('div'); - const value = $dom.val(); - const { onChange } = this.options; - await new Promise((resolve) => { - this.vditor = new Vditor(ele, { - ...config, - upload: { - multiple: false, - handler: (files) => { - let wrapper = ['', '']; - let ext: string; - const matches = files[0].type.match(/^image\/(png|jpg|jpeg|gif)$/i); - if (matches) { - wrapper = ['![image](', ')']; - [, ext] = matches; - } else if (files[0].type === 'application/x-zip-compressed') { - wrapper = ['[file](', ')']; - ext = 'zip'; - } - if (!ext) return i18n('No Supported file type.'); - const filename = `${nanoid()}.${ext}`; - const data = new FormData(); - data.append('filename', filename); - data.append('file', files[0]); - data.append('operation', 'upload_file'); - if (isProblemEdit) data.append('type', 'additional_file'); - let progress = 0; - request.postFile(isProblemEdit ? './files' : '/file', data, { - xhr: () => { - const xhr = new XMLHttpRequest(); - xhr.upload.addEventListener('loadstart', () => this.vditor.vditor.tip.show(i18n('Uploading...'), 0)); - xhr.upload.addEventListener('progress', (e) => { - if (!e.lengthComputable) return; - const percentComplete = Math.round((e.loaded / e.total) * 100); - if (percentComplete === progress) return; - progress = percentComplete; - this.vditor.vditor.tip.show(`${i18n('Uploading...')} ${percentComplete}%`, 0); - }, false); - return xhr; - }, - }) - .then(() => { - this.vditor.insertValue(`${wrapper.join(`${isProblemPage ? 'file://' : `/file/${UserContext._id}/`}${filename}`)} `); - this.vditor.vditor.tip.hide(); - }) - .catch((e: { message: string; }) => { - console.error(e); - return `${i18n('Upload Failed')}: ${e.message}`; - }); - return null; - }, - }, - ...this.options, - after: () => { - const pos = $(ele).find('button[data-mode="sv"]').get(); - const button = $(''); - button.on('click', (e) => { - Notification.info('You can select this in PreferenceSettings'); - e.preventDefault(); - }); - button.insertAfter(pos); - resolve(null); - }, - input(v) { - $dom.val(v); - $dom.text(v); - if (onChange) onChange(v); - }, - value, - cache: { id: Math.random().toString() }, + const [ + { + Editor, rootCtx, defaultValueCtx, editorViewCtx, parserCtx, serializerCtx, + }, + { nord }, { Slice }, { gfm }, { listener, listenerCtx }, + { math }, { history }, { clipboard }, { tooltip }, { slash }, + { emoji }, { upload, uploadPlugin }, { diagram }, { prism }, { cursor }, + { menu }, + ] = await Promise.all([ + import('@milkdown/core'), import('./theme'), import('@milkdown/prose'), + import('@milkdown/preset-gfm'), import('@milkdown/plugin-listener'), + import('@milkdown/plugin-math'), + import('@milkdown/plugin-history'), + import('@milkdown/plugin-clipboard'), + import('@milkdown/plugin-tooltip'), + import('@milkdown/plugin-slash'), + import('@milkdown/plugin-emoji'), + import('@milkdown/plugin-upload'), + import('@milkdown/plugin-diagram'), + import('@milkdown/plugin-prism'), + import('@milkdown/plugin-cursor'), + import('@milkdown/plugin-menu'), + ]); + + const uploader: import('@milkdown/plugin-upload').Uploader = async (files, schema) => { + const images: File[] = []; + for (let i = 0; i < files.length; i++) { + const file = files.item(i); + if (!file) continue; + if (!file.type.includes('image')) continue; + images.push(file); + } + const nodes: Node[] = await Promise.all( + images.map(async (image) => { + console.log(image); + // TODO + return null; + const src = await YourUploadAPI(image); + const alt = image.name; + return schema.nodes.image.createAndFill({ + src, + alt, + }) as Node; + }), + ); + return nodes; + }; + + this.milkdown = Editor.make() + .use(nord).use(listener).use(gfm) + .use(math).use(history).use(clipboard).use(tooltip).use(slash) + .use(emoji).use(diagram).use(prism).use(cursor).use(menu) + .use(upload.configure(uploadPlugin, { uploader })) + .config((ctx) => { + ctx.set(defaultValueCtx, this.$dom.val()); + ctx.set(rootCtx, ele); + ctx.get(listenerCtx).markdownUpdated((c, markdown) => { + this.$dom.val(markdown); + this.$dom.text(markdown); + if (this.lock) return; + this.lock = true; + if (this.model) { + const range = this.model.getFullModelRange(); + this.model.pushEditOperations([], [{ range, text: markdown }], undefined); + } else this.editor?.setValue?.(markdown); + this.options?.onChange?.(markdown); + this.lock = false; + }); + ctx.get(listenerCtx).updated((c) => { + this.height = $(ele).height(); + }); }); + this.milkdown.setValue = (val) => { + this.milkdown.action((ctx) => { + const view = ctx.get(editorViewCtx); + const doc = ctx.get(parserCtx)(val); + if (!doc) return; + view.dispatch(view.state.tr.replace(0, view.state.doc.content.size, new Slice(doc.content, 0, 0))); + }); + }; + this.milkdown.getValue = () => this.milkdown.action((ctx) => { + const editorView = ctx.get(editorViewCtx); + const serializer = ctx.get(serializerCtx); + return serializer(editorView.state.doc); }); - $(ele).addClass('textbox'); - $dom.hide(); - origin.parentElement.appendChild(ele); + this.milkdown.dispose = () => { + const view = this.milkdown.action((ctx) => ctx.get(editorViewCtx)); + const root = this.milkdown.action((ctx) => ctx.get(rootCtx)) as HTMLElement; + root?.firstChild?.remove(); + view?.destroy(); + }; + await this.milkdown.create(); + $('.editor-root').prepend($('
')); + $('.editor-menu').prepend($('.milkdown-menu')); this.isValid = true; - if (hasFocus) this.focus(); } destory() { this.detach(); - if (this.vditor?.destroy) this.vditor.destroy(); - else if (this.editor?.dispose) this.editor.dispose(); + this.editor?.dispose(); + this.milkdown?.dispose(); } ensureValid() { @@ -259,8 +258,12 @@ export default class Editor extends DOMAttachedObject { value(val?: string) { this.ensureValid(); - if (typeof val === 'string') return (this.editor || this.vditor).setValue(val); - return (this.editor || this.vditor).getValue({ lineEnding: '\n', preserveBOM: false }); + if (typeof val === 'string') { + this.editor?.setValue(val); + this.milkdown?.setValue(val); + return val; + } + return (this.editor || this.milkdown)?.getValue({ lineEnding: '\n', preserveBOM: false }); } focus() { @@ -271,4 +274,4 @@ export default class Editor extends DOMAttachedObject { } } -_.assign(Editor, DOMAttachedObject); +_.assign(VjEditor, DOMAttachedObject); diff --git a/packages/ui-default/components/editor/theme.ts b/packages/ui-default/components/editor/theme.ts new file mode 100644 index 000000000..e5ba8d6e0 --- /dev/null +++ b/packages/ui-default/components/editor/theme.ts @@ -0,0 +1,380 @@ +/* Copyright 2021, Milkdown by Mirone. */ +/* eslint-disable consistent-return */ +import { + Emotion, ThemeBorder, ThemeFont, ThemeManager, ThemeScrollbar, ThemeShadow, ThemeSize, + hex2rgb, ThemeColor, themeFactory, ThemeGlobal, ThemeIcon, +} from '@milkdown/core'; +import { getPalette } from '@milkdown/design-system'; +import { injectProsemirrorView, useAllPresetRenderer } from '@milkdown/theme-pack-helper'; +import { code, typography } from '@milkdown/theme-nord/src/font'; +import { getIcon } from '@milkdown/theme-nord/src/icon'; +import { darkColor, lightColor } from '@milkdown/theme-nord/src/nord'; + +export const font = { + typography, + code, +}; + +export const size = { + radius: '4px', + lineWidth: '1px', +}; + +export const getStyle = (manager: ThemeManager, emotion: Emotion) => { + const { injectGlobal, css } = emotion; + const palette = getPalette(manager); + const radius = manager.get(ThemeSize, 'radius'); + const neutral = palette('neutral', 0.87); + const surface = palette('surface'); + const line = palette('line'); + const highlight = palette('secondary', 0.38); + + const selection = css` + .ProseMirror-selectednode { + outline: ${manager.get(ThemeSize, 'lineWidth')} solid ${line}; + } + li.ProseMirror-selectednode { + outline: none; + } + li.ProseMirror-selectednode::after { + ${manager.get(ThemeBorder, undefined)}; + } + & ::selection { + background: ${highlight}; + } + `; + + const editorLayout = css` + padding: 1.25em; + outline: none; + `; + + const blockquote = css` + blockquote { + padding-left: 1.875em; + line-height: 1.75em; + border-left: 4px solid ${palette('primary')}; + margin-left: 0; + margin-right: 0; + * { + font-size: 1em; + line-height: 1.5em; + } + } + `; + + const hr = css` + hr { + height: ${manager.get(ThemeSize, 'lineWidth')}; + background-color: ${line}; + border-width: 0; + } + `; + + const list = css` + .list-item, + .list-item > * { + margin: 0.5em 0; + } + li { + &::marker { + color: ${palette('primary')}; + } + } + .task-list-item { + display: flex; + flex-direction: row; + align-items: flex-start; + &_checkbox { + margin: 0.5em 0.5em 0.5em 0; + height: 1em; + } + } + `; + + // eslint-disable-next-line no-shadow + const code = css` + .code-fence { + pre { + font-family: ${manager.get(ThemeFont, 'code')}; + margin: 0 1.2em !important; + white-space: pre; + overflow: auto; + ${manager.get(ThemeScrollbar, ['x'])} + background-color: ${palette('background')}; + color: ${palette('neutral')}; + font-size: 0.875em; + border-radius: ${radius}; + code { + line-height: 1.5; + font-family: ${manager.get(ThemeFont, 'code')}; + } + } + } + `; + + const img = css` + .image { + display: inline-block; + margin: 0 auto; + object-fit: contain; + width: 100%; + position: relative; + height: auto; + text-align: center; + } + `; + + const inline = css` + .code-inline { + background-color: ${palette('neutral')}; + color: ${palette('background')}; + border-radius: ${radius}; + font-weight: 500; + font-family: ${code}; + padding: 0 0.2em; + font-size: 1.2em; + } + .strong { + font-weight: 600; + } + .link, + a { + color: ${palette('secondary')}; + cursor: pointer; + transition: all 0.4s ease-in-out; + font-weight: 500; + &:hover { + background-color: ${palette('line')}; + box-shadow: 0 0.2em ${palette('line')}, 0 -0.2em ${palette('line')}; + } + } + .strike-through { + text-decoration-color: ${palette('secondary')}; + } + `; + + const footnote = css` + .footnote-definition { + ${manager.get(ThemeBorder, undefined)}; + border-radius: ${manager.get(ThemeSize, 'radius')}; + background-color: ${palette('background')}; + padding: 1em; + display: flex; + flex-direction: row; + & > .footnote-definition_content { + flex: 1; + width: calc(100% - 1em); + & > dd { + margin-inline-start: 1em; + } + & > dt { + color: ${palette('secondary')}; + font-weight: 500; + } + } + & > .footnote-definition_anchor { + width: 1em; + } + } + `; + + const table = css` + .tableWrapper { + overflow-x: auto; + margin: 0; + ${manager.get(ThemeScrollbar, ['x'])} + width: 100%; + * { + margin: 0; + box-sizing: border-box; + font-size: 1em; + } + } + table { + border-collapse: collapse; + table-layout: fixed; + width: 100%; + overflow: auto; + border-radius: ${manager.get(ThemeSize, 'radius')}; + p { + line-height: unset; + } + } + tr { + ${manager.get(ThemeBorder, 'bottom')}; + } + td, + th { + padding: 0 1em; + vertical-align: top; + box-sizing: border-box; + position: relative; + min-width: 100px; + ${manager.get(ThemeBorder, undefined)}; + text-align: left; + line-height: 3; + height: 3em; + } + th { + background: ${palette('background', 0.5)}; + font-weight: 400; + } + .column-resize-handle { + position: absolute; + right: -2px; + top: 0; + bottom: 0; + z-index: 20; + pointer-events: none; + background: ${palette('secondary')}; + width: ${manager.get(ThemeSize, 'lineWidth')}; + } + .resize-cursor { + cursor: ew-resize; + cursor: col-resize; + } + .selectedCell { + &::after { + z-index: 2; + position: absolute; + content: ''; + left: 0; + right: 0; + top: 0; + bottom: 0; + background: ${palette('secondary', 0.38)}; + pointer-events: none; + } + & ::selection { + background: transparent; + } + } + `; + + injectProsemirrorView(emotion); + + // eslint-disable-next-line no-unused-expressions + injectGlobal` + .milkdown { + .material-icons-outlined { + font-size: 1.5em; + } + position: relative; + margin-left: auto; + margin-right: auto; + box-sizing: border-box; + color: ${neutral}; + background: ${surface}; + font-family: ${manager.get(ThemeFont, 'typography')}; + ${manager.get(ThemeShadow, undefined)} + ${manager.get(ThemeScrollbar, undefined)} + ${selection}; + .editor { + ${editorLayout}; + ${blockquote}; + ${hr}; + ${list}; + ${code}; + ${img}; + ${table}; + ${footnote}; + ${inline}; + } + } + `; +}; + +export const createTheme = (isDarkMode: boolean) => (emotion: Emotion, manager: ThemeManager) => { + const { css } = emotion; + const colorSet = isDarkMode ? darkColor : lightColor; + + manager.set(ThemeColor, (options) => { + if (!options) return; + const [key, opacity] = options; + const hex = colorSet[key]; + const rgb = hex2rgb(hex); + if (!rgb) return; + + return `rgba(${rgb?.join(', ')}, ${opacity || 1})`; + }); + + manager.set(ThemeSize, (key) => { + if (!key) return; + return size[key]; + }); + + manager.set(ThemeFont, (key) => { + if (!key) return; + return font[key].join(', '); + }); + + manager.set(ThemeScrollbar, ([direction = 'y', type = 'normal'] = ['y', 'normal'] as never) => { + const main = manager.get(ThemeColor, ['secondary', 0.38]); + const bg = manager.get(ThemeColor, ['secondary', 0.12]); + const hover = manager.get(ThemeColor, ['secondary']); + return css` + scrollbar-width: thin; + scrollbar-color: ${main} ${bg}; + -webkit-overflow-scrolling: touch; + &::-webkit-scrollbar { + ${direction === 'y' ? 'width' : 'height'}: ${type === 'thin' ? 2 : 12}px; + background-color: transparent; + } + &::-webkit-scrollbar-track { + border-radius: 999px; + background: transparent; + border: 4px solid transparent; + } + &::-webkit-scrollbar-thumb { + border-radius: 999px; + background-color: ${main}; + border: ${type === 'thin' ? 0 : 4}px solid transparent; + background-clip: content-box; + } + &::-webkit-scrollbar-thumb:hover { + background-color: ${hover}; + } + `; + }); + + manager.set(ThemeShadow, () => { + const lineWidth = manager.get(ThemeSize, 'lineWidth'); + const getShadow = (opacity: number) => manager.get(ThemeColor, ['shadow', opacity]); + return css` + box-shadow: 0 ${lineWidth} ${lineWidth} ${getShadow(0.14)}, 0 2px ${lineWidth} ${getShadow(0.12)}, + 0 ${lineWidth} 3px ${getShadow(0.2)}; + `; + }); + + manager.set(ThemeBorder, (direction) => { + const lineWidth = manager.get(ThemeSize, 'lineWidth'); + const line = manager.get(ThemeColor, ['line']); + if (!direction) { + return css`border: ${lineWidth} solid ${line};`; + } + return css`${`border-${direction}`}: ${lineWidth} solid ${line};`; + }); + + manager.set(ThemeIcon, (icon) => { + if (!icon) return; + + return getIcon(icon); + }); + + manager.set(ThemeGlobal, () => { + getStyle(manager, emotion); + }); + + useAllPresetRenderer(manager, emotion); +}; + +export const getNord = (isDarkMode = false) => themeFactory((emotion, manager) => createTheme(isDarkMode)(emotion, manager)); + +export const nordDark = getNord(true); +export const nordLight = getNord(false); + +const darkMode = Boolean(window.matchMedia?.('(prefers-color-scheme: dark)').matches); +export const nord = getNord(darkMode); + +export { color, darkColor, lightColor } from '@milkdown/theme-nord/src/nord'; diff --git a/packages/ui-default/components/form/button.page.default.styl b/packages/ui-default/components/form/button.page.default.styl index ba1f1f731..6e4d5a1ee 100644 --- a/packages/ui-default/components/form/button.page.default.styl +++ b/packages/ui-default/components/form/button.page.default.styl @@ -1,7 +1,7 @@ @import './var.inc.styl' .button - &:not(.codicon) + &:not(.codicon):not(.milkdown-nutoqn) background-color: lighten($default-color, 50%) border: 1px solid $default-color transition: background-color .2s, box-shadow .5s diff --git a/packages/ui-default/components/form/button.page.styl b/packages/ui-default/components/form/button.page.styl index af6ddd822..494225a50 100644 --- a/packages/ui-default/components/form/button.page.styl +++ b/packages/ui-default/components/form/button.page.styl @@ -1,7 +1,7 @@ @import './var.inc.styl' .button - &:not(.codicon) + &:not(.codicon):not(.milkdown-nutoqn) font-size: rem($font-size-secondary) line-height: rem($form-control-height - 2) height: rem($form-control-height) diff --git a/packages/ui-default/misc/icons/template/webicon.styl b/packages/ui-default/misc/icons/template/webicon.styl index 556dead8d..d2f8a04ed 100644 --- a/packages/ui-default/misc/icons/template/webicon.styl +++ b/packages/ui-default/misc/icons/template/webicon.styl @@ -8,7 +8,7 @@ font-weight: normal font-style: normal -.icon +.icon:not(.material-icons-outlined) font-family: $icon-font-name, $primary-font-family !important speak: none font-style: normal diff --git a/packages/ui-default/package.json b/packages/ui-default/package.json index 70504d0e6..255ab91a7 100644 --- a/packages/ui-default/package.json +++ b/packages/ui-default/package.json @@ -20,6 +20,23 @@ "@fontsource/source-code-pro": "^4.5.6", "@fontsource/ubuntu-mono": "^4.5.6", "@hydrooj/utils": "workspace:*", + "@milkdown/core": "^6.0.1", + "@milkdown/plugin-clipboard": "^6.0.1", + "@milkdown/plugin-cursor": "^6.0.1", + "@milkdown/plugin-diagram": "^6.0.1", + "@milkdown/plugin-emoji": "^6.0.1", + "@milkdown/plugin-history": "^6.0.1", + "@milkdown/plugin-listener": "^6.0.1", + "@milkdown/plugin-math": "^6.0.1", + "@milkdown/plugin-menu": "^6.0.1", + "@milkdown/plugin-prism": "^6.0.1", + "@milkdown/plugin-slash": "^6.0.1", + "@milkdown/plugin-tooltip": "^6.0.1", + "@milkdown/plugin-upload": "^6.0.1", + "@milkdown/preset-commonmark": "^6.0.1", + "@milkdown/preset-gfm": "^6.0.1", + "@milkdown/prose": "^6.0.1", + "@milkdown/theme-nord": "^6.0.1", "@types/gulp-if": "^0.0.34", "@types/jquery": "^3.5.14", "@types/json-schema": "^7.0.11", @@ -57,6 +74,7 @@ "jquery-scroll-lock": "^3.1.3", "jquery.easing": "^1.4.1", "jquery.transit": "^0.9.12", + "material-icons": "^1.10.9", "mini-css-extract-plugin": "^1.6.2", "moment": "^2.29.1", "monaco-editor": "^0.31.1", @@ -99,7 +117,6 @@ "through2": "^4.0.2", "timeago-react": "^3.0.4", "timeago.js": "^4.0.2", - "vditor": "^3.8.13", "vinyl-buffer": "^1.0.1", "web-streams-polyfill": "^3.2.0", "webpack": "^4.46.0", diff --git a/packages/ui-default/theme/dark.styl b/packages/ui-default/theme/dark.styl index 93f58cdf1..0c217668e 100644 --- a/packages/ui-default/theme/dark.styl +++ b/packages/ui-default/theme/dark.styl @@ -196,7 +196,7 @@ $hover-background-color = #424242 background-color: #171717 color: #999 - pre:not(.vditor-sv), code + pre, code background-color: #171717 .scratchpad__toolbar diff --git a/packages/ui-default/theme/default.js b/packages/ui-default/theme/default.js index c53a9f24f..d63fbe39b 100644 --- a/packages/ui-default/theme/default.js +++ b/packages/ui-default/theme/default.js @@ -1,10 +1,10 @@ import 'normalize.css/normalize.css'; -import 'vditor/dist/index.css'; import 'pickadate/lib/themes/classic.css'; import 'pickadate/lib/themes/classic.date.css'; import 'pickadate/lib/themes/classic.time.css'; import 'katex/dist/katex.min.css'; import '@blueprintjs/core/lib/css/blueprint.css'; +import 'material-icons/iconfont/material-icons.css'; import '@fontsource/fira-code'; import '@fontsource/source-code-pro';