feat: 添加对 Ace 编辑器的支持和样式优化

This commit is contained in:
2026-04-11 19:43:30 +08:00
parent 7bae6d487d
commit 5a39432029
3 changed files with 198 additions and 116 deletions

View File

@@ -13,6 +13,7 @@
var DEFAULT_CDN = global.HUSTOJ_VDITOR_CDN || getLocalCdnBase(); var DEFAULT_CDN = global.HUSTOJ_VDITOR_CDN || getLocalCdnBase();
global.HUSTOJ_VDITOR_CDN = DEFAULT_CDN; global.HUSTOJ_VDITOR_CDN = DEFAULT_CDN;
var assetsPromise = null; var assetsPromise = null;
var aceAssetsPromise = null;
var styleInjected = false; var styleInjected = false;
function getLocalCdnBase() { function getLocalCdnBase() {
@@ -25,6 +26,16 @@
return scriptSrc.replace(/\/vditor-adapter\.js(?:[?#].*)?$/, '/vditor'); return scriptSrc.replace(/\/vditor-adapter\.js(?:[?#].*)?$/, '/vditor');
} }
function getLocalAceBase() {
var scriptSrc = currentScriptRef && currentScriptRef.src;
if (!scriptSrc) {
return 'ace';
}
return scriptSrc.replace(/\/include\/vditor-adapter\.js(?:[?#].*)?$/, '/ace');
}
function injectAdapterStyle() { function injectAdapterStyle() {
var style; var style;
@@ -39,6 +50,14 @@
'.hustoj-vditor-hidden{display:none !important;}', '.hustoj-vditor-hidden{display:none !important;}',
'.hustoj-vditor-wrapper{width:100%;margin:0 0 12px;}', '.hustoj-vditor-wrapper{width:100%;margin:0 0 12px;}',
'.hustoj-vditor-code{--hustoj-vditor-font-size:18px;text-align:left;}', '.hustoj-vditor-code{--hustoj-vditor-font-size:18px;text-align:left;}',
'.hustoj-vditor-code.ace_editor{position:relative;border:1px solid #d4d4d5;border-radius:6px;line-height:1.5;}',
'.hustoj-vditor-code .ace_scroller,',
'.hustoj-vditor-code .ace_gutter,',
'.hustoj-vditor-code .ace_content{font-family:Consolas,Monaco,"Courier New",monospace !important;font-size:var(--hustoj-vditor-font-size) !important;line-height:1.5 !important;}',
'.hustoj-vditor-code .ace_gutter{background:#f8f8f8;color:#8a8a8a;}',
'.hustoj-vditor-dark.ace_editor{background:#272822;border-color:#4a4a4a;}',
'.hustoj-vditor-dark .ace_gutter{background:#2f3129;color:#bcbcbc;}',
'.hustoj-vditor-dark .ace_print-margin{background:#3a3d34;}',
'.hustoj-vditor-preview{--hustoj-diagram-font-family:"Microsoft YaHei","PingFang SC","Hiragino Sans GB","Noto Sans CJK SC","Source Han Sans SC","WenQuanYi Micro Hei",Arial,sans-serif;--hustoj-diagram-font-size:14px;}', '.hustoj-vditor-preview{--hustoj-diagram-font-family:"Microsoft YaHei","PingFang SC","Hiragino Sans GB","Noto Sans CJK SC","Source Han Sans SC","WenQuanYi Micro Hei",Arial,sans-serif;--hustoj-diagram-font-size:14px;}',
'.hustoj-vditor-code .vditor-toolbar{display:none !important;}', '.hustoj-vditor-code .vditor-toolbar{display:none !important;}',
'.hustoj-vditor-code .vditor-reset,', '.hustoj-vditor-code .vditor-reset,',
@@ -147,6 +166,56 @@
return assetsPromise; return assetsPromise;
} }
function ensureAceAssets(options) {
var aceBase = (options && options.aceBase) || getLocalAceBase();
injectAdapterStyle();
if (global.ace) {
if (global.ace.config && typeof global.ace.config.set === 'function') {
global.ace.config.set('basePath', aceBase);
}
return Promise.resolve(global.ace);
}
if (aceAssetsPromise) {
return aceAssetsPromise;
}
aceAssetsPromise = new Promise(function (resolve, reject) {
var aceScriptSrc = aceBase + '/ace.js';
var existing = documentRef.querySelector('script[data-hustoj-ace-js="' + aceScriptSrc + '"]');
var finish = function () {
if (global.ace && global.ace.config && typeof global.ace.config.set === 'function') {
global.ace.config.set('basePath', aceBase);
}
resolve(global.ace);
};
if (existing) {
if (global.ace) {
finish();
} else {
existing.addEventListener('load', finish, { once: true });
existing.addEventListener('error', reject, { once: true });
}
return;
}
var script = documentRef.createElement('script');
script.src = aceScriptSrc;
script.async = true;
script.setAttribute('data-hustoj-ace-js', aceScriptSrc);
script.onload = finish;
script.onerror = function () {
reject(new Error('Failed to load ' + aceScriptSrc));
};
documentRef.head.appendChild(script);
});
return aceAssetsPromise;
}
function resolveElement(target) { function resolveElement(target) {
if (!target) { if (!target) {
return null; return null;
@@ -621,15 +690,16 @@
} }
function createCodeEditor(options) { function createCodeEditor(options) {
return ensureAssets(options).then(function () { return ensureAceAssets(options).then(function () {
var container = resolveElement(options.container); var container = resolveElement(options.container);
var hiddenField = resolveElement(options.hiddenField); var hiddenField = resolveElement(options.hiddenField);
var sourceField = resolveElement(options.sourceField); var sourceField = resolveElement(options.sourceField);
var initialValue = options.initialValue; var initialValue = options.initialValue;
var adapter = null; var adapter = null;
var vditor = null; var aceEditor = null;
var session = null; var session = null;
var state; var state;
var aceBase = (options && options.aceBase) || getLocalAceBase();
if (!container) { if (!container) {
throw new Error('Vditor code editor container not found'); throw new Error('Vditor code editor container not found');
@@ -649,25 +719,33 @@
container.classList.add('hustoj-vditor-code'); container.classList.add('hustoj-vditor-code');
container.style.setProperty('--hustoj-vditor-font-size', state.fontSize + 'px'); container.style.setProperty('--hustoj-vditor-font-size', state.fontSize + 'px');
container.classList.toggle('hustoj-vditor-dark', aceThemeToVditorTheme(state.theme) === 'dark'); container.classList.toggle('hustoj-vditor-dark', aceThemeToVditorTheme(state.theme) === 'dark');
container.style.width = options.width || '100%';
if (typeof options.height === 'string') {
container.style.height = options.height;
} else {
container.style.height = (options.height || computeHeight(container, 420)) + 'px';
}
if (sourceField && sourceField !== container) { if (sourceField && sourceField !== container) {
sourceField.classList.add('hustoj-vditor-hidden'); sourceField.classList.add('hustoj-vditor-hidden');
} }
container.textContent = ''; container.textContent = initialValue || '';
function applyTheme() { function applyTheme() {
var editorTheme = aceThemeToVditorTheme(state.theme); var editorTheme = aceThemeToVditorTheme(state.theme);
var contentTheme = aceThemeToContentTheme(state.theme);
container.classList.toggle('hustoj-vditor-dark', editorTheme === 'dark'); container.classList.toggle('hustoj-vditor-dark', editorTheme === 'dark');
if (vditor && typeof vditor.setTheme === 'function') { if (aceEditor) {
vditor.setTheme(editorTheme, contentTheme, 'github'); aceEditor.setTheme(state.theme);
} }
} }
function applyFontSize() { function applyFontSize() {
container.style.setProperty('--hustoj-vditor-font-size', state.fontSize + 'px'); container.style.setProperty('--hustoj-vditor-font-size', state.fontSize + 'px');
if (aceEditor) {
aceEditor.setFontSize(state.fontSize);
}
} }
function syncSourceValue(value) { function syncSourceValue(value) {
@@ -685,6 +763,9 @@
session = { session = {
setMode: function (modeName) { setMode: function (modeName) {
state.mode = normalizeMode(modeName); state.mode = normalizeMode(modeName);
if (aceEditor) {
aceEditor.session.setMode(modeName || 'text');
}
}, },
getValue: function () { getValue: function () {
return adapter.getValue(); return adapter.getValue();
@@ -695,55 +776,51 @@
}; };
return new Promise(function (resolve) { return new Promise(function (resolve) {
vditor = new global.Vditor(container, { if (global.ace && global.ace.config && typeof global.ace.config.set === 'function') {
cdn: options.cdn || DEFAULT_CDN, global.ace.config.set('basePath', aceBase);
cache: { enable: false },
mode: options.editorMode || 'sv',
lang: options.lang || 'zh_CN',
value: initialValue || '',
icon: options.icon || 'ant',
theme: aceThemeToVditorTheme(state.theme),
width: options.width || '100%',
minHeight: options.minHeight || computeHeight(container, 420),
height: options.height || computeHeight(container, 420),
toolbar: options.toolbar || [],
toolbarConfig: options.toolbarConfig || { hide: true, pin: false },
resize: options.resize || { enable: false },
outline: options.outline || { enable: false },
counter: options.counter || { enable: false },
preview: options.preview || {
mode: 'editor',
delay: 0,
actions: [],
markdown: {
sanitize: false,
autoSpace: false,
codeBlockPreview: false,
mathBlockPreview: false,
fixTermTypo: false
} }
},
input: function (value) { aceEditor = global.ace.edit(container);
syncSourceValue(value); aceEditor.$blockScrolling = Infinity;
}, aceEditor.setValue(initialValue || '', -1);
blur: function (value) { aceEditor.session.setUseWorker(false);
syncSourceValue(value); aceEditor.session.setUseWrapMode(true);
}, aceEditor.renderer.setShowPrintMargin(false);
after: function () { aceEditor.setHighlightActiveLine(true);
aceEditor.setOptions({
showFoldWidgets: false,
tabSize: 4,
useSoftTabs: true,
behavioursEnabled: false,
wrapBehavioursEnabled: false,
fontSize: state.fontSize
});
if (state.mode) {
aceEditor.session.setMode('ace/mode/' + state.mode);
}
aceEditor.session.on('change', function () {
syncSourceValue(aceEditor.getValue());
});
aceEditor.on('blur', function () {
syncSourceValue(aceEditor.getValue());
});
adapter = { adapter = {
getValue: function () { getValue: function () {
return vditor.getValue(); return aceEditor.getValue();
}, },
setValue: function (value) { setValue: function (value) {
var nextValue = typeof value === 'undefined' ? '' : String(value); var nextValue = typeof value === 'undefined' ? '' : String(value);
vditor.setValue(nextValue, true); aceEditor.setValue(nextValue, -1);
syncSourceValue(nextValue); syncSourceValue(nextValue);
return nextValue; return nextValue;
}, },
getSession: function () { getSession: function () {
return session; return aceEditor.getSession();
}, },
session: session, session: aceEditor.getSession(),
setTheme: function (themeName) { setTheme: function (themeName) {
state.theme = themeName || state.theme; state.theme = themeName || state.theme;
applyTheme(); applyTheme();
@@ -761,13 +838,18 @@
if (typeof editorOptions.readOnly !== 'undefined') { if (typeof editorOptions.readOnly !== 'undefined') {
this.setReadOnly(editorOptions.readOnly); this.setReadOnly(editorOptions.readOnly);
} }
aceEditor.setOptions(editorOptions);
}, },
setOption: function (name, value) { setOption: function (name, value) {
if (name === 'wrap') { if (name === 'wrap') {
state.wrap = value !== 'off'; state.wrap = value !== 'off';
aceEditor.session.setUseWrapMode(state.wrap);
return;
} }
aceEditor.setOption(name, value);
}, },
resize: function () { resize: function () {
aceEditor.resize();
applyFontSize(); applyFontSize();
}, },
getFontSize: function () { getFontSize: function () {
@@ -778,16 +860,12 @@
applyFontSize(); applyFontSize();
}, },
setReadOnly: function (readOnly) { setReadOnly: function (readOnly) {
if (readOnly) { aceEditor.setReadOnly(!!readOnly);
vditor.disabled();
} else {
vditor.enable();
}
}, },
focus: function () { focus: function () {
vditor.focus(); aceEditor.focus();
}, },
_vditor: vditor _ace: aceEditor
}; };
applyTheme(); applyTheme();
@@ -795,10 +873,8 @@
if (options.readOnly) { if (options.readOnly) {
adapter.setReadOnly(true); adapter.setReadOnly(true);
} }
syncSourceValue(vditor.getValue()); syncSourceValue(aceEditor.getValue());
resolve(adapter); resolve(adapter);
}
});
}); });
}); });
} }

View File

@@ -2818,13 +2818,6 @@ var plantumlRenderAdapter = {
/* harmony import */ var _util_function__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(446); /* harmony import */ var _util_function__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(446);
var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
var FLOWCHART_DIAGRAM_FONT_FAMILY = '"Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", "Noto Sans CJK SC", "Source Han Sans SC", "WenQuanYi Micro Hei", Arial, sans-serif';
var FLOWCHART_DIAGRAM_OPTIONS = {
"font-family": FLOWCHART_DIAGRAM_FONT_FAMILY,
"font-size": 14,
"font-weight": "normal"
};
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) { return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
@@ -2994,6 +2987,11 @@ var codeRender = function (element, option) {
var flowchartRender = function (element, cdn) { var flowchartRender = function (element, cdn) {
if (cdn === void 0) { cdn = _constants__WEBPACK_IMPORTED_MODULE_0__/* .Constants.CDN */ .g.CDN; } if (cdn === void 0) { cdn = _constants__WEBPACK_IMPORTED_MODULE_0__/* .Constants.CDN */ .g.CDN; }
var flowchartDiagramOptions = {
"font-family": '"Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", "Noto Sans CJK SC", "Source Han Sans SC", "WenQuanYi Micro Hei", Arial, sans-serif',
"font-size": 14,
"font-weight": "normal"
};
var flowchartElements = _adapterRender__WEBPACK_IMPORTED_MODULE_1__.flowchartRenderAdapter.getElements(element); var flowchartElements = _adapterRender__WEBPACK_IMPORTED_MODULE_1__.flowchartRenderAdapter.getElements(element);
if (flowchartElements.length === 0) { if (flowchartElements.length === 0) {
return; return;
@@ -3005,7 +3003,7 @@ var flowchartRender = function (element, cdn) {
} }
var flowchartObj = flowchart.parse(_adapterRender__WEBPACK_IMPORTED_MODULE_1__.flowchartRenderAdapter.getCode(item)); var flowchartObj = flowchart.parse(_adapterRender__WEBPACK_IMPORTED_MODULE_1__.flowchartRenderAdapter.getCode(item));
item.innerHTML = ""; item.innerHTML = "";
flowchartObj.drawSVG(item, FLOWCHART_DIAGRAM_OPTIONS); flowchartObj.drawSVG(item, flowchartDiagramOptions);
item.setAttribute("data-processed", "true"); item.setAttribute("data-processed", "true");
}); });
}); });
@@ -3548,6 +3546,10 @@ var mermaidRender = function (element, cdn, theme) {
fontFamily: mermaidFontFamily, fontFamily: mermaidFontFamily,
fontSize: "14px", fontSize: "14px",
startOnLoad: false, startOnLoad: false,
themeVariables: {
fontFamily: mermaidFontFamily,
fontSize: "14px"
},
flowchart: { flowchart: {
htmlLabels: false, htmlLabels: false,
useMaxWidth: !0 useMaxWidth: !0

View File

@@ -23,6 +23,10 @@ export const mermaidRender = (element: HTMLElement, cdn = Constants.CDN, theme:
fontFamily: MERMAID_FONT_FAMILY, fontFamily: MERMAID_FONT_FAMILY,
fontSize: MERMAID_FONT_SIZE, fontSize: MERMAID_FONT_SIZE,
startOnLoad: false, startOnLoad: false,
themeVariables: {
fontFamily: MERMAID_FONT_FAMILY,
fontSize: MERMAID_FONT_SIZE,
},
flowchart: { flowchart: {
htmlLabels: false, htmlLabels: false,
useMaxWidth: !0 useMaxWidth: !0