mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 17:36:08 +08:00
ui: theme changer
This commit is contained in:
1 parent
8e02eb5bf9
commit
7dd2d82eb8
9 files changed
+103
-65
No files matched your search
@@ -198,6 +198,7 @@ export default function (env = {}) {
|
||||
patterns: [
|
||||
{ from: root('static') },
|
||||
{ from: root(`${dirname(require.resolve('vditor/package.json'))}`), to: 'vditor/' },
|
||||
{ from: `${dirname(require.resolve('monaco-themes/package.json'))}/themes`, to: 'monaco/themes/' },
|
||||
],
|
||||
}),
|
||||
new webpack.DefinePlugin({
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
|
||||
import './monaco.css';
|
||||
|
||||
export default monaco;
|
||||
|
||||
/** @param {import('monaco-editor').editor.IStandaloneCodeEditor} editor */
|
||||
export function registerAction(editor, model, element) {
|
||||
editor.addAction({
|
||||
id: 'theme-dark',
|
||||
label: 'Use dark theme',
|
||||
run: () => monaco.editor.setTheme('vs-dark'),
|
||||
});
|
||||
editor.addAction({
|
||||
id: 'theme-light',
|
||||
label: 'Use light theme',
|
||||
run: () => monaco.editor.setTheme('vs-light'),
|
||||
});
|
||||
if (element) {
|
||||
editor.addAction({
|
||||
id: 'submit-form',
|
||||
label: 'Submit',
|
||||
keybindings: [
|
||||
monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter,
|
||||
monaco.KeyMod.WinCtrl | monaco.KeyCode.Enter,
|
||||
],
|
||||
run: () => $(element).closest('form').submit(),
|
||||
});
|
||||
}
|
||||
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KeyP, () => {
|
||||
editor.getAction('editor.action.quickCommand').run();
|
||||
});
|
||||
editor.addCommand(monaco.KeyMod.Alt | monaco.KeyMod.Shift | monaco.KeyCode.KeyF, () => {
|
||||
const action = editor.getAction('editor.action.format') || editor.getAction('editor.action.formatDocument');
|
||||
if (action) action.run();
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
|
||||
import { EditorAction, registerEditorAction } from 'monaco-editor/esm/vs/editor/browser/editorExtensions';
|
||||
import { IQuickInputService } from 'monaco-editor/esm/vs/platform/quickinput/common/quickInput';
|
||||
import list from 'monaco-themes/themes/themelist.json';
|
||||
import i18n from 'vj/utils/i18n';
|
||||
import './monaco.css';
|
||||
|
||||
export default monaco;
|
||||
export const customOptions: monaco.editor.IStandaloneDiffEditorConstructionOptions = JSON.parse(localStorage.getItem('editor.config') || '{}');
|
||||
|
||||
const loaded = {};
|
||||
async function fetchTheme(id: string, label: string) {
|
||||
if (loaded[id]) return;
|
||||
const res = await fetch(`${UiContext.cdn_prefix}monaco/themes/${label}.json`);
|
||||
const data = await res.json();
|
||||
monaco.editor.defineTheme(id, data);
|
||||
loaded[id] = true;
|
||||
}
|
||||
export const loadThemePromise = customOptions.theme
|
||||
? fetchTheme(customOptions.theme, list[customOptions.theme])
|
||||
: Promise.resolve();
|
||||
|
||||
// 这破烂 monaco 能不能给个 typings 啊
|
||||
// 我翻源码真的很累的好吧
|
||||
class ChangeThemeAction extends EditorAction {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'hydro.changeEditorTheme',
|
||||
label: i18n('Change Theme'),
|
||||
alias: i18n('Change Theme'),
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor, editor: monaco.editor.IStandaloneCodeEditor) {
|
||||
const items = Object.keys(list).map((key) => ({
|
||||
label: list[key],
|
||||
command: key,
|
||||
}));
|
||||
const quickInputService = accessor.get(IQuickInputService);
|
||||
const oldTheme = (editor as any)._themeService._theme;
|
||||
let focus = '';
|
||||
const selected = await quickInputService.pick(items, {
|
||||
canPickMany: false,
|
||||
async onDidFocus(item) {
|
||||
focus = item.command;
|
||||
await fetchTheme(item.command, item.label);
|
||||
if (focus === item.command) monaco.editor.setTheme(item.command);
|
||||
},
|
||||
});
|
||||
if (!selected) {
|
||||
const themeId = oldTheme.type ? `${oldTheme.base}-${oldTheme.type}` : oldTheme.id;
|
||||
return monaco.editor.setTheme(themeId);
|
||||
}
|
||||
await fetchTheme(selected.command, selected.label);
|
||||
customOptions.theme = selected.command;
|
||||
return monaco.editor.setTheme(selected.command);
|
||||
}
|
||||
}
|
||||
|
||||
registerEditorAction(ChangeThemeAction);
|
||||
|
||||
export function registerAction(
|
||||
editor: monaco.editor.IStandaloneCodeEditor,
|
||||
model: monaco.editor.IModel,
|
||||
element,
|
||||
) {
|
||||
if (element) {
|
||||
editor.addAction({
|
||||
id: 'hydro.submitForm',
|
||||
label: 'Submit',
|
||||
keybindings: [
|
||||
monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter,
|
||||
monaco.KeyMod.WinCtrl | monaco.KeyCode.Enter,
|
||||
],
|
||||
run: () => {
|
||||
$(element).closest('form').submit();
|
||||
},
|
||||
});
|
||||
}
|
||||
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KeyP, () => {
|
||||
editor.getAction('editor.action.quickCommand').run();
|
||||
});
|
||||
editor.addCommand(monaco.KeyMod.Alt | monaco.KeyMod.Shift | monaco.KeyCode.KeyF, () => {
|
||||
const action = editor.getAction('editor.action.format') || editor.getAction('editor.action.formatDocument');
|
||||
if (action) action.run();
|
||||
});
|
||||
editor.onDidChangeConfiguration(() => {
|
||||
const current = editor.getOption(monaco.editor.EditorOptions.fontSize.id);
|
||||
customOptions.fontSize = current;
|
||||
localStorage.setItem('editor.config', JSON.stringify(customOptions));
|
||||
});
|
||||
}
|
||||
@@ -49,6 +49,7 @@ export async function load(features = ['markdown']) {
|
||||
console.log('Loaded monaco feature:', feat, 'in', Date.now() - s, 'ms');
|
||||
loaded.push(feat);
|
||||
}
|
||||
await res.loadThemePromise;
|
||||
return { monaco: res.default, registerAction: res.registerAction };
|
||||
}
|
||||
export default load;
|
||||
@@ -4,7 +4,6 @@ import { connect } from 'react-redux';
|
||||
const mapStateToProps = (state) => ({
|
||||
value: state.editor.code,
|
||||
language: window.LANGS[state.editor.lang]?.monaco,
|
||||
theme: 'vs-light',
|
||||
mainSize: state.ui.main.size,
|
||||
pretestSize: state.ui.pretest.size,
|
||||
recordSize: state.ui.records.size,
|
||||
@@ -23,21 +22,20 @@ export default connect(mapStateToProps, mapDispatchToProps)(class MonacoEditor e
|
||||
|
||||
async componentDidMount() {
|
||||
const value = this.props.value || '';
|
||||
const { language, theme } = this.props;
|
||||
const { language } = this.props;
|
||||
const { load } = await import('vj/components/monaco/loader');
|
||||
const { monaco, registerAction } = await load([language]);
|
||||
const { monaco, registerAction, customOptions } = await load([language]);
|
||||
this.model = monaco.editor.createModel(value, language, monaco.Uri.parse(`file:///${UiContext.pdoc.pid || UiContext.pdoc.docId}.${language}`));
|
||||
if (this.containerElement) {
|
||||
/** @type {monaco.editor.IStandaloneEditorConstructionOptions} */
|
||||
const config = {
|
||||
theme,
|
||||
theme: 'vs-light',
|
||||
...customOptions,
|
||||
lineNumbers: true,
|
||||
glyphMargin: true,
|
||||
lightbulb: { enabled: true },
|
||||
model: this.model,
|
||||
};
|
||||
const fontSize = localStorage.getItem('scratchpad.editor.fontSize');
|
||||
if (fontSize && !Number.isNaN(+fontSize)) config.fontSize = +fontSize;
|
||||
this.editor = monaco.editor.create(this.containerElement, config);
|
||||
registerAction(this.editor, this.model);
|
||||
this.disposable.push(
|
||||
@@ -46,10 +44,6 @@ export default connect(mapStateToProps, mapDispatchToProps)(class MonacoEditor e
|
||||
this.props.handleUpdateCode(this.editor.getValue(), event);
|
||||
}
|
||||
}),
|
||||
this.editor.onDidChangeConfiguration(() => {
|
||||
const current = this.editor.getOptions()._values[40].fontSize;
|
||||
localStorage.setItem('scratchpad.editor.fontSize', current);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -80,7 +74,6 @@ export default connect(mapStateToProps, mapDispatchToProps)(class MonacoEditor e
|
||||
monaco.editor.setModelLanguage(model, language);
|
||||
editor.updateOptions({ mode: language });
|
||||
}
|
||||
if (prevProps.theme !== theme) monaco.editor.setTheme(theme);
|
||||
if (editor) {
|
||||
if (prevProps.mainSize !== mainSize
|
||||
|| prevProps.recordSize !== recordSize
|
||||
|
||||
@@ -64,9 +64,6 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
payload: request.get(UiContext.getSubmissionsUrl),
|
||||
});
|
||||
},
|
||||
handleClickRefresh() {
|
||||
if (this.props.recordsVisible) this.loadSubmissions();
|
||||
},
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(class ScratchpadToolbarContainer extends React.PureComponent {
|
||||
@@ -121,18 +118,6 @@ export default connect(mapStateToProps, mapDispatchToProps)(class ScratchpadTool
|
||||
{' '}
|
||||
(F10)
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
data-tooltip={i18n('Refresh Records')}
|
||||
className="scratchpad__toolbar__refresh"
|
||||
data-global-hotkey="alt+f"
|
||||
onClick={() => this.props.handleClickRefresh()}
|
||||
>
|
||||
<Icon name="refresh" />
|
||||
{' '}
|
||||
{i18n('Refresh')}
|
||||
{' '}
|
||||
(Alt+F)
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
data-global-hotkey="alt+q"
|
||||
data-tooltip={`${i18n('Quit Scratchpad')} (Alt+Q)`}
|
||||
|
||||
@@ -13,7 +13,7 @@ export default function reducer(state = {
|
||||
},
|
||||
records: {
|
||||
visible: UiContext.canViewRecord && localStorage.getItem('scratchpad/records') === 'true',
|
||||
size: 200,
|
||||
size: 100,
|
||||
isLoading: false,
|
||||
},
|
||||
isPosting: false,
|
||||
|
||||
@@ -33,12 +33,12 @@ __langname: 简体中文
|
||||
'Join {0}': '加入 {0}'
|
||||
'No': 否
|
||||
'Note: Problem title may not be hidden.': 注意:题目标题可能不会被隐藏。
|
||||
'Packages have new version: {0}': '以下组件有新版本: {0}'
|
||||
'Role {1} already exists in domain {0}.': '域 {0} 中已存在角色 {1}。'
|
||||
'Solved {0} problems, RP: {1} (No. {2})': '解决了 {0} 道题目,RP: {1} (No. {2})'
|
||||
'The value `{1}` of {0} already exists.': '{0} 的值 `{1}` 已经存在。'
|
||||
'Yes': 是
|
||||
"Effects only when Difficulty is not 'Use algorithm calculated'.": 仅当难度不为“使用算法计算”时才起效。
|
||||
'Packages have new version: {0}': '以下组件有新版本: {0}'
|
||||
"Split by ', '.": 由“, ”或“,”分隔。
|
||||
"What's file?": 什么是文件?
|
||||
(All Problems): (所有题目)
|
||||
@@ -110,6 +110,7 @@ Categories: 分类
|
||||
Category: 分类
|
||||
Change Email: 修改电子邮件
|
||||
Change Password: 修改密码
|
||||
Change Theme: 切换主题
|
||||
Check In: 签到
|
||||
Chinese: 中文
|
||||
Choose the background image in your profile page.: 选择您资料页面的背景图片。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@hydrooj/ui-default",
|
||||
"version": "4.29.9",
|
||||
"version": "4.30.0",
|
||||
"author": "undefined <i@undefined.moe>",
|
||||
"license": "AGPL-3.0",
|
||||
"main": "hydro.js",
|
||||
@@ -57,6 +57,7 @@
|
||||
"monaco-editor": "^0.31.1",
|
||||
"monaco-editor-nls": "^2.0.0",
|
||||
"monaco-editor-webpack-plugin": "^7.0.1",
|
||||
"monaco-themes": "^0.4.0",
|
||||
"nanoid": "^3.1.30",
|
||||
"normalize.css": "^8.0.1",
|
||||
"nprogress": "^0.2.0",
|
||||
|
||||
Reference in new issue
Block a user