+
diff --git a/packages/ui-default/components/editor/cmeditor.page.js b/packages/ui-default/components/editor/cmeditor.page.js
index c59e7aed3..a074c4a69 100644
--- a/packages/ui-default/components/editor/cmeditor.page.js
+++ b/packages/ui-default/components/editor/cmeditor.page.js
@@ -9,6 +9,9 @@ function runSubstitute($container) {
$container.find('textarea[data-yaml]').get().forEach((element) => {
CmEditor.getOrConstruct($(element), { language: 'yaml' });
});
+ $container.find('textarea[data-plain]').get().forEach((element) => {
+ CmEditor.getOrConstruct($(element), { language: 'plain' });
+ });
}
const cmEditorPage = new AutoloadPage('cmEditorPage', () => {
diff --git a/packages/ui-default/components/editor/index.js b/packages/ui-default/components/editor/index.js
index 4e6d6c3df..e2e90559c 100644
--- a/packages/ui-default/components/editor/index.js
+++ b/packages/ui-default/components/editor/index.js
@@ -37,20 +37,21 @@ export default class Editor extends DOMAttachedObject {
async initMonaco() {
const monaco = await import('monaco-editor/esm/vs/editor/editor.api');
+ const {
+ onChange, language = 'markdown',
+ theme = UserContext.monacoTheme || 'vs-light',
+ model = `file://model-${Math.random().toString(16)}`,
+ autoResize = true, autoLayout = true,
+ } = this.options;
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 = $dom.val();
- const {
- onChange, language = 'markdown',
- theme = UserContext.monacoTheme || 'vs-light',
- model = `file://model-${Math.random().toString(16)}`,
- autoResize = true,
- } = this.options;
+ const value = this.options.value || $dom.val();
this.model = typeof model === 'string' ? monaco.editor.createModel(value, language, monaco.Uri.parse(model)) : model;
const cfg = {
theme,
@@ -58,12 +59,25 @@ export default class Editor extends DOMAttachedObject {
glyphMargin: true,
lightbulb: { enabled: true },
model: this.model,
- automaticLayout: true,
minimap: { enabled: false },
hideCursorInOverviewRuler: true,
overviewRulerLanes: 0,
overviewRulerBorder: false,
};
+ if (autoLayout) cfg.automaticLayout = true;
+ let prevHeight = 0;
+ const updateEditorHeight = () => {
+ const editorElement = this.editor.getDomNode();
+ if (!editorElement) return;
+ const lineHeight = this.editor.getOption(monaco.editor.EditorOption.lineHeight);
+ const lineCount = this.editor.getModel()?.getLineCount() || 1;
+ const height = this.editor.getTopForLineNumber(lineCount + 1) + lineHeight;
+ if (prevHeight !== height) {
+ prevHeight = height;
+ editorElement.style.height = `${height}px`;
+ this.editor.layout();
+ }
+ };
if (autoResize) {
cfg.scrollbar = {
vertical: 'hidden',
@@ -84,23 +98,12 @@ export default class Editor extends DOMAttachedObject {
label: 'Use light theme',
run: () => monaco.editor.setTheme('vs-light'),
});
- let prevHeight = 0;
- const updateEditorHeight = () => {
- const editorElement = this.editor.getDomNode();
- if (!editorElement) return;
- const lineHeight = this.editor.getOption(monaco.editor.EditorOption.lineHeight);
- const lineCount = this.editor.getModel()?.getLineCount() || 1;
- const height = this.editor.getTopForLineNumber(lineCount + 1) + lineHeight;
- if (prevHeight !== height) {
- prevHeight = height;
- editorElement.style.height = `${height}px`;
- this.editor.layout();
- }
- };
- this.editor.onDidChangeModelDecorations(() => {
- updateEditorHeight(); // typing
- requestAnimationFrame(updateEditorHeight); // folding
- });
+ if (autoResize) {
+ this.editor.onDidChangeModelDecorations(() => {
+ updateEditorHeight(); // typing
+ requestAnimationFrame(updateEditorHeight); // folding
+ });
+ }
this._subscription = this.editor.onDidChangeModelContent(() => {
const val = this.editor.getValue();
$dom.val(val);
@@ -109,7 +112,7 @@ export default class Editor extends DOMAttachedObject {
});
this.isValid = true;
if (hasFocus) this.focus();
- updateEditorHeight();
+ if (autoResize) updateEditorHeight();
}
async initVditor() {
@@ -141,10 +144,20 @@ export default class Editor extends DOMAttachedObject {
if (hasFocus) this.focus();
}
+ destory() {
+ this.detach();
+ if (this.editor?.destory) this.editor.destory();
+ else if (this.editor?.dispose) this.editor.dispose();
+ }
+
ensureValid() {
if (!this.isValid) throw new Error('Editor is not loaded');
}
+ /**
+ * @param {string?} val
+ * @returns {string}
+ */
value(val) {
this.ensureValid();
if (typeof val === 'string') return this.editor.setValue(val);
diff --git a/packages/ui-default/package.json b/packages/ui-default/package.json
index fbfc81de7..76465fea4 100644
--- a/packages/ui-default/package.json
+++ b/packages/ui-default/package.json
@@ -1,6 +1,6 @@
{
"name": "@hydrooj/ui-default",
- "version": "4.8.10",
+ "version": "4.8.11",
"author": "undefined
",
"license": "AGPL-3.0",
"main": "hydro.js",
diff --git a/packages/ui-default/pages/problem_files.page.js b/packages/ui-default/pages/problem_files.page.js
index b37135856..43547dc0e 100644
--- a/packages/ui-default/pages/problem_files.page.js
+++ b/packages/ui-default/pages/problem_files.page.js
@@ -4,7 +4,7 @@ import _ from 'lodash';
import { createZipStream } from 'vj/utils/zip';
import { NamedPage } from 'vj/misc/Page';
import Notification from 'vj/components/notification';
-import { ConfirmDialog } from 'vj/components/dialog';
+import { ConfirmDialog, ActionDialog } from 'vj/components/dialog/index';
import request from 'vj/utils/request';
import pipeStream from 'vj/utils/pipeStream';
import tpl from 'vj/utils/tpl';
@@ -166,6 +166,54 @@ const page = new NamedPage('problem_files', () => {
handleClickUpload(type, files);
}
+ async function startEdit(filename, value) {
+ console.log(filename, value);
+ const { default: Editor } = await import('vj/components/editor/index');
+ const promise = new ActionDialog({
+ $body: tpl`
+
+
+
`,
+ width: `${window.innerWidth - 200}px`,
+ height: `${window.innerHeight - 100}px`,
+ }).open();
+ const editor = new Editor($('[name="fileContent"]'), { value, autoResize: false, autoLayout: false });
+ const action = await promise;
+ value = editor.value();
+ editor.destory();
+ if (action !== 'ok') return null;
+ return value;
+ }
+ /**
+ * @param {string} type
+ * @param {JQuery.ClickEvent} ev
+ */
+ async function handleEdit(type, ev) {
+ ev.preventDefault();
+ const filename = ev.currentTarget.closest('[data-filename]').getAttribute('data-filename');
+ const filesize = +ev.currentTarget.closest('[data-size]').getAttribute('data-size');
+ const link = $(ev.currentTarget).find('a').attr('href');
+ if (filesize > 8 * 1024 * 1024) Notification.error(i18n('file too large'));
+ Notification.info(i18n('Loading file...'));
+ const res = await request.get(link);
+ const content = await request.get(res.url, undefined, { dataType: 'text' });
+ const val = await startEdit(filename, content);
+ console.log(val);
+ if (typeof val !== 'string') return;
+ Notification.info(i18n('Saving file...'));
+ const data = new FormData();
+ data.append('filename', filename);
+ data.append('file', new Blob([val], { type: 'text/plain' }));
+ data.append('type', type);
+ data.append('operation', 'upload_file');
+ await request.postFile('', data);
+ Notification.success(i18n('File saved.'));
+ await delay(2000);
+ window.location.reload();
+ }
+
+ $('.problem-files-testdata .col--name').on('click', (ev) => handleEdit('testdata', ev));
+ $('.problem-files-additional_file .col--name').on('click', (ev) => handleEdit('additional_file', ev));
$('.problem-files-testdata').on('dragover', (ev) => handleDragOver('testdata', ev));
$('.problem-files-additional_file').on('dragover', (ev) => handleDragOver('additional_file', ev));
$('.problem-files-testdata').on('drop', (ev) => handleDrop('testdata', ev));
diff --git a/packages/ui-default/templates/problem_files.html b/packages/ui-default/templates/problem_files.html
index 00224f0da..62270f9dc 100644
--- a/packages/ui-default/templates/problem_files.html
+++ b/packages/ui-default/templates/problem_files.html
@@ -33,7 +33,7 @@
{%- for file in testdata -%}
-
+
|
|