merge: 合并master到develop

This commit is contained in:
王立帮
2025-05-18 00:17:23 +08:00
89 changed files with 4695 additions and 860 deletions

View File

@@ -146,6 +146,11 @@
box-sizing: unset;
}
.classic-theme.thrasos-renderer .blocklyMenu > .blocklyMenuItem {
background-color: #fff;
color: #2f2f2f;
}
.classic-theme.geras-renderer .blocklyMenu > .blocklyMenuItem {
background-color: #fff;
color: #2f2f2f;
@@ -185,6 +190,12 @@
box-sizing: unset;
}
.dark-theme.thrasos-renderer .blocklyMenu > .blocklyMenuItem {
background-color: #252525;
color: #eee;
font-family: "Lato", "Noto Sans SC";
}
.dark-theme.geras-renderer .blocklyMenu > .blocklyMenuItem {
background-color: #252525;
color: #eee;
@@ -233,4 +244,8 @@
.geras-renderer.dark-theme .blocklyText {
color: #000;
}
.thrasos-renderer.dark-theme .blocklyText {
color: #000;
}

View File

@@ -1,7 +1,7 @@
.chrome-tabs {
box-sizing: border-box;
position: relative;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-family: "Lato", "Noto Sans SC";
font-size: 12px;
height: 46px;
padding: 5px 0px 0px 0px;

View File

@@ -193,9 +193,10 @@
}
.tippy-box > .tippy-content > .context-menu-list {
-webkit-box-shadow: unset;
box-shadow: unset;
border-radius: 0.25rem;
position: relative;
-webkit-box-shadow: unset;
box-shadow: unset;
border-radius: 0.25rem;
}
.context-menu-item {

View File

@@ -128,8 +128,8 @@
"provide": ["PouchDB"],
"require": []
}, {
"path": "modules/web-modules/avr-uploader.min.js",
"provide": ["AvrUploader"],
"path": "modules/web-modules/avrbro.min.js",
"provide": ["avrbro"],
"require": []
}, {
"path": "modules/web-modules/microbit/microbit-fs.umd.min.js",
@@ -233,7 +233,7 @@
"require": []
}, {
"path": "modules/web-modules/esptool.min.js",
"provide": ["ESPTool"],
"provide": ["esptooljs"],
"require": []
}, {
"path": "modules/web-modules/adafruit-esptool/index.js",

View File

@@ -6,7 +6,7 @@
window.VIEW = scriptUrl.searchParams.get('view') || 'board';
let config = {};
try {
config = JSON.parse(localStorage.getItem('mixly2.0') ?? '{}');
config = JSON.parse(localStorage.getItem('mixly3.0') ?? '{}');
} catch (error) {
console.log(error);
}

View File

@@ -41,10 +41,11 @@ class ContextMenu {
}
}
#selector_ = null;
#menus_ = new Registry();
#events_ = new Events(['getMenu']);
constructor(selector, config = {}) {
this.selector = selector;
this.#selector_ = selector;
this.menu = $.contextMenu({
selector,
build: ($trigger) => {
@@ -67,20 +68,20 @@ class ContextMenu {
return ContextMenu.generate(menu, $trigger, e);
}
dispose() {
$.contextMenu('destroy', this.selector);
this.#events_.reset();
this.#menus_.reset();
this.menu = null;
this.selector = null;
}
show() {
$(this.selector).contextMenu();
$(this.#selector_).contextMenu();
}
hide() {
$(this.selector).contextMenu('hide');
$(this.#selector_).contextMenu('hide');
}
dispose() {
$.contextMenu('destroy', this.#selector_);
this.#events_.reset();
this.#menus_.reset();
this.menu = null;
this.#selector_ = null;
}
bind(type, func) {

View File

@@ -1,35 +1,66 @@
goog.loadJs('common', () => {
goog.require('tippy');
goog.require('Mixly.IdGenerator');
goog.require('Mixly.ContextMenu');
goog.provide('Mixly.DropdownMenu');
const { ContextMenu } = Mixly;
const {
IdGenerator,
ContextMenu
} = Mixly;
class DropdownMenu extends ContextMenu {
#contextMenu_ = null;
#layer_ = null;
static {
this.$container = $('<div class="mixly-dropdown-menus"></div>');
$(document.body).append(this.$container);
}
#shown_ = false;
constructor(selector) {
#layer_ = null;
#contextMenuId_ = '';
#$contextMenuElem_ = null;
constructor(elem) {
const layer = tippy(elem, {
allowHTML: true,
content: '',
trigger: 'click',
interactive: true,
maxWidth: 'none',
offset: [0, 0],
appendTo: document.body,
arrow: false,
placement: 'bottom-start',
delay: 0,
duration: [0, 0],
onCreate: (instance) => {
$(instance.popper).addClass('mixly-drapdown-menu');
},
onMount: () => {
this.show();
},
onHide: () => {
this.#shown_ && this.hide();
}
});
const contextMenuId = IdGenerator.generate();
const selector = `body > .mixly-dropdown-menus > div[m-id="${contextMenuId}"]`;
super(selector, {
trigger: 'none',
appendTo: $(layer.popper).children().children(),
zIndex: 1001,
position: (opt) => {
opt.$menu.css({
top: 0,
left: 0,
position: 'relative',
margin: 0
});
opt.$menu.css('margin', 0);
},
events: {
show: (opt) => {
opt.$menu.detach();
$('body > .mixly-drapdown-menu > .tippy-box > .tippy-content').empty().append(opt.$menu);
this.#layer_.setProps({});
show: () => {
this.#shown_ = true;
this.#layer_.setProps({});
},
hide: (opt) => {
hide: () => {
this.#shown_ = false;
if (this.#layer_.state.isShown) {
this.#layer_.hide();
@@ -38,33 +69,26 @@ class DropdownMenu extends ContextMenu {
}
});
this.#layer_ = tippy($(selector)[0], {
allowHTML: true,
content: '',
trigger: 'click',
interactive: true,
maxWidth: 'none',
offset: [ 0, 0 ],
appendTo: document.body,
arrow: false,
placement: 'bottom-start',
delay: 0,
duration: [ 0, 0 ],
onCreate: (instance) => {
$(instance.popper).addClass('mixly-drapdown-menu');
},
onMount: (instance) => {
this.show();
},
onHide: () => {
this.#shown_ && this.hide();
}
});
this.#$contextMenuElem_ = $(`<div m-id="${contextMenuId}"><div>`);
DropdownMenu.$container.append(this.#$contextMenuElem_);
this.#contextMenuId_ = contextMenuId;
this.#layer_ = layer;
}
show() {
this.#$contextMenuElem_.contextMenu();
}
hide() {
this.#$contextMenuElem_.contextMenu('hide');
}
dispose() {
super.dispose();
this.#layer_.destroy();
this.#layer_ = null;
this.#$contextMenuElem_.remove();
this.#$contextMenuElem_ = null;
}
}

View File

@@ -60,7 +60,7 @@ class EditorBlockly extends EditorBase {
this.initBlockly = () => {
const DEFAULT_CATEGORIES = HTMLTemplate.get('xml/default-categories.xml').render();
const media = path.join(Env.srcDirPath, 'common/media/blockly');
const renderer = ['geras', 'zelos'].includes(USER.blockRenderer) ? USER.blockRenderer : 'geras';
const renderer = ['geras', 'zelos', 'thrasos'].includes(USER.blockRenderer) ? USER.blockRenderer : 'geras';
this.editor = Blockly.inject(this.$blockly[0], {
media,
toolbox: DEFAULT_CATEGORIES,

View File

@@ -22,7 +22,7 @@ Env.hasSocketServer = false;
Env.hasCompiler = false;
/**
* 获取当前mixly2.0的路径
* 获取当前mixly的路径
* @type {String}
*/
Env.clientPath = null;

View File

@@ -16,7 +16,8 @@ goog.require('Mixly.Debug');
goog.require('Mixly.API2');
goog.require('Mixly.Electron.LibManager');
goog.require('Mixly.Electron.File');
goog.require('Mixly.WebSocket.Socket');
goog.require('Mixly.WebCompiler.Loader');
goog.require('Mixly.WebSocket.Loader');
goog.provide('Mixly.Loader');
const {
@@ -35,16 +36,20 @@ const {
API2,
Electron = {},
Web = {},
WebCompiler = {},
WebSocket = {}
} = Mixly;
const { LibManager, File } = goog.isElectron? Electron : Web;
const { Socket } = WebSocket;
window.addEventListener('load', () => {
if (!goog.isElectron && Env.hasSocketServer) {
Socket.init();
if (!goog.isElectron) {
if (Env.hasSocketServer) {
WebSocket.Loader.init();
} else if (Env.hasCompiler) {
WebCompiler.Loader.init();
}
}
const app = new App($('body')[0]);
Mixly.app = app;

View File

@@ -7,9 +7,9 @@ goog.provide('Mixly.LocalStorage');
const { MArray, LocalStorage } = Mixly;
LocalStorage.PATH = {
USER: 'mixly2.0/user',
BOARD: 'mixly2.0/boards/{{d.boardType}}/user',
THIRD_PARTY: 'mixly2.0/boards/{{d.boardType}}/third_party/{{d.thirdPartyName}}'
USER: 'mixly3.0/user',
BOARD: 'mixly3.0/boards/{{d.boardType}}/user',
THIRD_PARTY: 'mixly3.0/boards/{{d.boardType}}/third_party/{{d.thirdPartyName}}'
};
LocalStorage.set = function (path, value) {

View File

@@ -0,0 +1,91 @@
goog.loadJs('common', () => {
goog.require('io');
goog.require('Mixly');
goog.provide('Mixly.Socket');
class Socket {
#socket_ = null;
constructor(path, option) {
this.#socket_ = io(path, option);
}
#detectStatus_(status, callback) {
window.setTimeout(() => {
if (status.finished) {
return;
}
if (this.isConnected()) {
this.#detectStatus_(status, callback);
} else {
callback({
error: 'socket is not connected'
});
status.finished = true;
}
}, 1000);
}
emit(eventName, ...args) {
const callback = args.pop();
if (this.isConnected()) {
let emitStatus = {
finished: false
};
let status = this.#socket_.emit(eventName, ...args, (...callbackArgs) => {
if (emitStatus.finished) {
return;
}
emitStatus.finished = true;
callback(...callbackArgs);
});
this.#detectStatus_(emitStatus, callback);
return status;
} else {
callback({
error: 'socket is not connected'
});
return false;
}
}
async emitAsync(eventName, ...args) {
return new Promise((resolve, reject) => {
if (this.isConnected()) {
const callback = (...callbackArgs) => {
if (callbackArgs[0].error) {
reject(callbackArgs[0].error);
return;
}
resolve(...callbackArgs);
}
let emitStatus = {
finished: false
};
let status = this.#socket_.emit(eventName, ...args, (...callbackArgs) => {
if (emitStatus.finished) {
return;
}
emitStatus.finished = true;
callback(...callbackArgs);
});
this.#detectStatus_(emitStatus, callback);
} else {
reject('socket is not connected');
}
})
}
getSocket() {
return this.#socket_;
}
isConnected() {
return this.#socket_?.connected;
}
}
Mixly.Socket = Socket;
});

View File

@@ -86,6 +86,7 @@ class StatusBarsManager extends PagesManager {
#shown_ = false;
#dropdownMenu_ = null;
#$dropdownBtn_ = null;
constructor(element) {
const managerHTMLTemplate = HTMLTemplate.get('html/statusbar/statusbars-manager.html');
@@ -103,6 +104,7 @@ class StatusBarsManager extends PagesManager {
this.tabId = tabHTMLTemplate.id;
this.id = IdGenerator.generate();
this.addEventsType(['show', 'hide', 'onSelectMenu', 'getMenu']);
this.#$dropdownBtn_ = $tab.find('.statusbar-dropdown-menu > button');
$tab.find('.statusbar-close > button').click(() => this.hide());
this.#addDropdownMenu_();
this.#addEventsListener_();
@@ -146,7 +148,6 @@ class StatusBarsManager extends PagesManager {
}
#addDropdownMenu_() {
const selector = `div[m-id="${this.tabId}"] > .statusbar-dropdown-menu > .layui-btn`;
let menu = new Menu();
let serialChildMenu = new Menu(true);
menu.add({
@@ -240,7 +241,7 @@ class StatusBarsManager extends PagesManager {
}
return result;
});
this.#dropdownMenu_ = new DropdownMenu(selector);
this.#dropdownMenu_ = new DropdownMenu(this.#$dropdownBtn_[0]);
this.#dropdownMenu_.register('menu', menu);
this.#dropdownMenu_.bind('getMenu', () => 'menu');
}
@@ -278,6 +279,9 @@ class StatusBarsManager extends PagesManager {
dispose() {
StatusBarsManager.remove(this);
this.#dropdownMenu_.dispose();
this.#$dropdownBtn_.remove();
this.#$dropdownBtn_ = null;
super.dispose();
}
}

View File

@@ -49,6 +49,7 @@
"Mixly.Web.FS",
"Mixly.Web.File",
"Mixly.Web.Serial",
"Mixly.WebCompiler.ArduShell",
"Mixly.WebSocket.File",
"Mixly.WebSocket.Serial",
"Mixly.WebSocket.ArduShell",
@@ -190,6 +191,7 @@
"path": "/common/dropdown-menu.js",
"require": [
"tippy",
"Mixly.IdGenerator",
"Mixly.ContextMenu"
],
"provide": [
@@ -652,7 +654,8 @@
"Mixly.API2",
"Mixly.Electron.LibManager",
"Mixly.Electron.File",
"Mixly.WebSocket.Socket"
"Mixly.WebCompiler.Loader",
"Mixly.WebSocket.Loader"
],
"provide": [
"Mixly.Loader"
@@ -918,6 +921,16 @@
"Mixly.RightSideBarsManager"
]
},
{
"path": "/common/socket.js",
"require": [
"io",
"Mixly"
],
"provide": [
"Mixly.Socket"
]
},
{
"path": "/common/statusbar-ampy.js",
"require": [
@@ -1428,6 +1441,7 @@
{
"path": "/electron/loader.js",
"require": [
"path",
"Mixly.Url",
"Mixly.Config",
"Mixly.Env",
@@ -1552,10 +1566,9 @@
"FSWrapper",
"DAPWrapper",
"PartialFlashing",
"ESPTool",
"esptooljs",
"AdafruitESPTool",
"CryptoJS",
"AvrUploader",
"Mixly.Env",
"Mixly.LayerExt",
"Mixly.Config",
@@ -1712,19 +1725,38 @@
]
},
{
"path": "/web-compiler/compiler.js",
"path": "/web-compiler/arduino-shell.js",
"require": [
"Mixly.Url",
"Mixly.Config",
"Mixly.LayerExt",
"layui",
"avrbro",
"esptooljs",
"AdafruitESPTool",
"CryptoJS",
"dayjs.duration",
"Mixly.Boards",
"Mixly.MFile",
"Mixly.Debug",
"Mixly.LayerExt",
"Mixly.Msg",
"Mixly.Web.BU",
"Mixly.Web.Serial"
"Mixly.Workspace",
"Mixly.LayerProgress",
"Mixly.Web.Serial",
"Mixly.WebCompiler"
],
"provide": [
"Mixly.WebCompiler.Compiler"
"Mixly.WebCompiler.ArduShell"
]
},
{
"path": "/web-compiler/loader.js",
"require": [
"Mixly.Debug",
"Mixly.Config",
"Mixly.StatusBarsManager",
"Mixly.Socket",
"Mixly.WebCompiler.ArduShell"
],
"provide": [
"Mixly.WebCompiler.Loader"
]
},
{
@@ -1810,6 +1842,22 @@
"Mixly.WebSocket.File"
]
},
{
"path": "/web-socket/loader.js",
"require": [
"Mixly.Debug",
"Mixly.Config",
"Mixly.StatusBarsManager",
"Mixly.Socket",
"Mixly.WebSocket.Serial",
"Mixly.WebSocket.ArduShell",
"Mixly.WebSocket.BU",
"Mixly.WebSocket.Ampy"
],
"provide": [
"Mixly.WebSocket.Loader"
]
},
{
"path": "/web-socket/serial.js",
"require": [
@@ -1824,25 +1872,9 @@
"Mixly.WebSocket.Serial"
]
},
{
"path": "/web-socket/socket.js",
"require": [
"Mixly.Debug",
"Mixly.StatusBarsManager",
"Mixly.WebSocket",
"Mixly.WebSocket.Serial",
"Mixly.WebSocket.ArduShell",
"Mixly.WebSocket.BU",
"Mixly.WebSocket.Ampy"
],
"provide": [
"Mixly.WebSocket.Socket"
]
},
{
"path": "/web-socket/web-socket.js",
"require": [
"io",
"Mixly"
],
"provide": [

View File

@@ -1,5 +1,6 @@
goog.loadJs('electron', () => {
goog.require('path');
goog.require('Mixly.Url');
goog.require('Mixly.Config');
goog.require('Mixly.Env');
@@ -32,7 +33,7 @@ Loader.onbeforeunload = function(reload = false) {
window.location.reload(true);
}
}
let href = Env.srcDirPath + '/index.html?' + Url.jsonToUrl({ boardType: BOARD.boardType ?? 'None' });
let href = path.join(Env.srcDirPath, 'index.html') + '?' + Url.jsonToUrl({ boardType: BOARD.boardType ?? 'None' });
let endPromise = [];
const { mainStatusBarTabs } = Mixly;
Serial.getCurrentPortsName().map((name) => {

View File

@@ -0,0 +1,399 @@
goog.loadJs('web', () => {
goog.require('layui');
goog.require('avrbro');
goog.require('esptooljs');
goog.require('AdafruitESPTool');
goog.require('CryptoJS');
goog.require('dayjs.duration');
goog.require('Mixly.Boards');
goog.require('Mixly.Debug');
goog.require('Mixly.LayerExt');
goog.require('Mixly.Msg');
goog.require('Mixly.Workspace');
goog.require('Mixly.LayerProgress');
goog.require('Mixly.Web.Serial');
goog.require('Mixly.WebCompiler');
goog.provide('Mixly.WebCompiler.ArduShell');
const {
Boards,
Debug,
LayerExt,
Msg,
Workspace,
LayerProgress,
Web,
WebCompiler
} = Mixly;
const { Serial } = Web;
const { layer } = layui;
const { ESPLoader, Transport } = esptooljs;
function hexToBinaryString (hex) {
let binaryString = '';
for (let i = 0; i < hex.length; i += 2) {
const byte = parseInt(hex.substr(i, 2), 16);
binaryString += String.fromCharCode(byte);
}
return binaryString;
}
class WebCompilerArduShell {
static {
this.mixlySocket = null;
this.socket = null;
this.shell = null;
this.getSocket = function () {
return this.socket;
}
this.getMixlySocket = function () {
return this.mixlySocket;
}
this.init = function (mixlySocket) {
this.mixlySocket = mixlySocket;
this.socket = mixlySocket.getSocket();
this.shell = new WebCompilerArduShell();
const socket = this.socket;
socket.on('arduino.dataEvent', (data) => {
if (data.length > 1000) {
return;
}
const { mainStatusBarTabs } = Mixly;
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
statusBarTerminal.addValue(data);
});
socket.on('arduino.errorEvent', (data) => {
const { mainStatusBarTabs } = Mixly;
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
try {
data = unescape(data.replace(/(_E[0-9A-F]{1}_[0-9A-F]{2}_[0-9A-F]{2})+/gm, '%$1'));
data = unescape(data.replace(/\\(u[0-9a-fA-F]{4})/gm, '%$1'));
} catch (error) {
Debug.error(error);
}
statusBarTerminal.addValue(data);
});
}
this.initCompile = function () {
if (!this.mixlySocket.isConnected()) {
layer.msg(Msg.Lang['websocket.offline'], { time: 1000 });
return;
}
const { mainStatusBarTabs } = Mixly;
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
mainStatusBarTabs.changeTo('output');
mainStatusBarTabs.show();
const mainWorkspace = Workspace.getMain();
const editor = mainWorkspace.getEditorsManager().getActive();
const code = editor.getCode();
statusBarTerminal.setValue(`${Msg.Lang['shell.compiling']}...\n`);
this.shell.compile(code)
.then((info) => {
this.endCallback(info.code, info.time);
})
.catch((error) => {
Debug.error(error);
statusBarTerminal.addValue(`\n==${Msg.Lang['shell.compileFailed']}==\n`);
});
}
this.initUpload = function () {
if (!this.mixlySocket.isConnected()) {
layer.msg(Msg.Lang['websocket.offline'], { time: 1000 });
return;
}
const port = Serial.getSelectedPortName();
if (!port) {
layer.msg(Msg.Lang['statusbar.serial.noDevice'], {
time: 1000
});
return;
}
const { mainStatusBarTabs } = Mixly;
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
mainStatusBarTabs.changeTo('output');
mainStatusBarTabs.show();
statusBarTerminal.setValue(`${Msg.Lang['shell.uploading']}...\n`);
const mainWorkspace = Workspace.getMain();
const editor = mainWorkspace.getEditorsManager().getActive();
const code = editor.getCode();
const statusBarSerial = mainStatusBarTabs.getStatusBarById(port);
const closePromise = statusBarSerial ? statusBarSerial.close() : Promise.resolve();
closePromise
.then(() => {
return this.shell.upload(port, code)
})
.then((info) => {
this.endCallback(info.code, info.time);
if (info.code || !Serial.portIsLegal(port)) {
return;
}
mainStatusBarTabs.add('serial', port);
mainStatusBarTabs.changeTo(port);
const statusBarSerial = mainStatusBarTabs.getStatusBarById(port);
statusBarSerial.open()
.then(() => {
const baudRates = code.match(/(?<=Serial.begin[\s]*\([\s]*)[0-9]*(?=[\s]*\))/g);
if (!baudRates?.length) {
return statusBarSerial.setBaudRate(9600);
} else {
return statusBarSerial.setBaudRate(baudRates[0] - 0);
}
})
.catch(Debug.error);
})
.catch((error) => {
Debug.error(error);
statusBarTerminal.addValue(`\n==${Msg.Lang['shell.uploadFailed']}==\n`);
});
}
this.endCallback = function (code, time) {
const { mainStatusBarTabs } = Mixly;
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
mainStatusBarTabs.changeTo('output');
let message = '';
if (code) {
message = (this.shell.isCompiling() ? Msg.Lang['shell.compileFailed'] : Msg.Lang['shell.uploadFailed']);
statusBarTerminal.addValue(`\n==${message}==\n`);
} else {
message = (this.shell.isCompiling() ? Msg.Lang['shell.compileSucc'] : Msg.Lang['shell.uploadSucc']);
statusBarTerminal.addValue(`\n==${message}(${Msg.Lang['shell.timeCost']} ${
dayjs.duration(time).format('HH:mm:ss.SSS')
})==\n`);
}
layer.msg(message, { time: 1000 });
}
}
#running_ = false;
#upload_ = false;
#killing_ = false;
#layer_ = null;
constructor() {
this.#layer_ = new LayerProgress({
width: 200,
cancelValue: Msg.Lang['nav.btn.stop'],
skin: 'layui-anim layui-anim-scale',
cancel: () => {
if (this.#killing_) {
return false;
}
this.#layer_.title(`${Msg.Lang['shell.aborting']}...`);
this.#killing_ = true;
this.kill().catch(Debug.error);
return false;
},
cancelDisplay: false
});
}
async compile(code) {
return new Promise(async (resolve, reject) => {
this.#running_ = true;
this.#upload_ = false;
this.#killing_ = false;
this.showProgress();
const key = Boards.getSelectedBoardCommandParam();
const config = { key, code };
const mixlySocket = WebCompilerArduShell.getMixlySocket();
mixlySocket.emit('arduino.compile', config, (response) => {
this.hideProgress();
if (response.error) {
reject(response.error);
return;
}
const [error, result] = response;
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
}
async upload(port, code) {
return new Promise(async (resolve, reject) => {
this.#running_ = true;
this.#upload_ = true;
this.#killing_ = false;
this.showProgress();
const key = Boards.getSelectedBoardCommandParam();
const config = { key, code };
const mixlySocket = WebCompilerArduShell.getMixlySocket();
mixlySocket.emit('arduino.upload', config, async (response) => {
if (response.error) {
this.hideProgress();
reject(response.error);
return;
}
const [error, result] = response;
if (error) {
this.hideProgress();
reject(error);
return;
}
if (result.code !== 0) {
this.hideProgress();
resolve(result);
return;
}
const { files } = result;
try {
const keys = Boards.getSelectedBoardKey().split(':');
if (`${keys[0]}:${keys[1]}` === 'arduino:avr') {
await this.uploadWithAvrbro(port, files);
} else {
await this.uploadWithEsptool(port, files);
}
} catch (error) {
this.hideProgress();
reject(error);
return;
}
this.hideProgress();
result.files = null;
resolve(result);
});
});
}
async uploadWithEsptool(port, files) {
const { mainStatusBarTabs } = Mixly;
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
let esploader = null;
let transport = null;
let baudrate = 115200;
let eraseAll = true;
try {
const keys = Boards.getSelectedBoardKey().split(':');
if (`${keys[0]}:${keys[1]}` === 'esp32:esp32') {
baudrate = Boards.getSelectedBoardConfigParam('UploadSpeed');
eraseAll = Boards.getSelectedBoardConfigParam('EraseFlash') === 'all';
} else {
baudrate = Boards.getSelectedBoardConfigParam('baud');
eraseAll = Boards.getSelectedBoardConfigParam('wipe') === 'all';
}
transport = new Transport(Serial.getPort(port), false);
esploader = new ESPLoader({
transport,
baudrate,
terminal: {
clean() {},
writeLine(data) {
statusBarTerminal.addValue(data + '\n');
},
write(data) {
statusBarTerminal.addValue(data);
}
}
});
let chip = await esploader.main();
} catch (error) {
await transport.disconnect();
throw new Error(error);
}
let data = [];
statusBarTerminal.addValue("\n");
for (let file of files) {
if (file.data && file.offset) {
data.push({
address: parseInt(file.offset, 16),
data: hexToBinaryString(file.data)
});
}
}
const flashOptions = {
fileArray: data,
flashSize: 'keep',
eraseAll,
compress: true,
calculateMD5Hash: (image) => CryptoJS.MD5(CryptoJS.enc.Latin1.parse(image))
};
try {
await esploader.writeFlash(flashOptions);
await transport.setDTR(false);
await new Promise((resolve) => setTimeout(resolve, 100));
await transport.setDTR(true);
await transport.disconnect();
} catch (error) {
await transport.disconnect();
throw new Error(error);
}
}
async uploadWithAvrbro(port, files) {
const key = Boards.getSelectedBoardKey();
const boardId = key.split(':')[2];
let boardName = '';
if (boardId === 'uno') {
boardName = 'uno';
} else if (boardId === 'nano') {
const cpu = Boards.getSelectedBoardConfigParam('cpu');
if (cpu === 'atmega328old') {
boardName = 'nano';
} else {
boardName = 'nano (new bootloader)';
}
} else if (boardId === 'pro') {
boardName = 'pro-mini';
} else if (boardId === 'mega') {
boardName = 'mega';
} else if (boardId === 'leonardo') {
boardName = 'leonardo';
}
const buffer = avrbro.parseHex(files[0].data);
await avrbro.flash(Serial.getPort(port), buffer, { boardName });
}
async kill() {
return new Promise(async (resolve, reject) => {
const mixlySocket = WebCompilerArduShell.getMixlySocket();
mixlySocket.emit('arduino.kill', (response) => {
if (response.error) {
reject(response.error);
return;
}
const [error, result] = response;
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
}
showProgress() {
const message = this.isCompiling() ? Msg.Lang['shell.compiling'] : Msg.Lang['shell.uploading'];
this.#layer_.title(`${message}...`);
this.#layer_.show();
}
hideProgress() {
this.#layer_.hide();
}
isUploading() {
return this.#running_ && this.#upload_;
}
isCompiling() {
return this.#running_ && !this.#upload_;
}
}
WebCompiler.ArduShell = WebCompilerArduShell;
});

View File

@@ -1,184 +0,0 @@
goog.loadJs('web', () => {
goog.require('Mixly.Url');
goog.require('Mixly.Config');
goog.require('Mixly.LayerExt');
goog.require('Mixly.Boards');
goog.require('Mixly.MFile');
goog.require('Mixly.Msg');
goog.require('Mixly.Web.BU');
goog.require('Mixly.Web.Serial');
goog.provide('Mixly.WebCompiler.Compiler');
const {
WebCompiler,
Url,
Boards,
MFile,
Config,
LayerExt,
Msg,
Web
} = Mixly;
const { SOFTWARE, BOARD } = Config;
const { Compiler } = WebCompiler;
const { BU, Serial } = Web;
const DEFAULT_CONFIG = {
"enabled": true,
"protocol": "http:",
"ip": "localhost",
"domain": null
};
Compiler.CONFIG = { ...DEFAULT_CONFIG, ...(SOFTWARE?.webCompiler ?? {}) };
const { CONFIG } = Compiler;
let { hostname, protocol, port } = window.location;
if (port) {
port = ':' + port;
}
Compiler.protocol = protocol;
Compiler.URL = Compiler.protocol + '//' + hostname + port + '/compile';
Compiler.compile = () => {
const { mainStatusBarTabs } = Mixly;
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
mainStatusBarTabs.show();
statusBarTerminal.setValue('');
Compiler.generateCommand('compile', (error, obj, layerNum) => {
layer.close(layerNum);
let message = Msg.Lang['shell.compileSucc'];
if (error) {
message = Msg.Lang['shell.compileFailed'];
}
layer.msg(message, { time: 1000 });
statusBarTerminal.addValue("==" + message + "(" + Msg.Lang['shell.timeCost'] + " " + obj.timeCost + ")==\n");
});
}
Compiler.upload = async () => {
const { mainStatusBarTabs } = Mixly;
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
mainStatusBarTabs.show();
statusBarTerminal.setValue('');
BU.burning = false;
BU.uploading = true;
const board = Boards.getSelectedBoardCommandParam();
const boardParam = board.split(':');
const portName = 'web-serial';
if (boardParam[1] === 'avr') {
let boardUpload;
switch (boardParam[2]) {
case 'uno':
boardUpload = 'uno';
break;
case 'nano':
if (boardParam.length > 3 && boardParam[3] === 'cpu=atmega328old') {
boardUpload = 'nanoOldBootloader';
} else {
boardUpload = 'nano';
}
break;
case 'pro':
boardUpload = 'proMini';
break;
}
Serial.portClose(portName, async () => {
mainStatusBarTabs.changeTo('output');
try {
await AvrUploader.connect(boardUpload, {});
Compiler.generateCommand('upload', BU.uploadWithAvrUploader);
} catch (error) {
statusBarTerminal.addValue(error.toString() + '\n');
}
});
} else {
Serial.connect(portName, 115200, async (port) => {
if (!port) {
layer.msg(Msg.Lang['已取消连接'], { time: 1000 });
return;
}
mainStatusBarTabs.changeTo('output');
Compiler.generateCommand('upload', BU.uploadWithEsptool);
});
}
}
Compiler.generateCommand = (operate, endFunc = (errorMessage, data, layerNum) => {}) => {
const code = MFile.getCode();
let type;
const boardType = Boards.getSelectedBoardCommandParam();
let command = {
board: encodeURIComponent(boardType),
code: encodeURIComponent(code),
visitorId: BOARD.visitorId.str32CRC32b,
operate
};
const { mainStatusBarTabs } = Mixly;
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
let commandStr = Compiler.URL + '?' + Url.jsonToUrl(command);
statusBarTerminal.setValue(Msg.Lang['shell.compiling'] + '...\n');
console.log('send -> ', commandStr);
const compileLayer = layer.open({
type: 1,
title: Msg.Lang['shell.compiling'] + "...",
content: $('#mixly-loader-div'),
shade: LayerExt.SHADE_NAV,
closeBtn: 0,
success: function () {
$(".layui-layer-page").css("z-index", "198910151");
$("#mixly-loader-btn").off("click").click(() => {
layer.close(compileLayer);
});
},
end: function () {
$('#mixly-loader-div').css('display', 'none');
$(".layui-layer-shade").remove();
}
});
Compiler.sendCommand(compileLayer, commandStr, endFunc);
}
Compiler.sendCommand = (layerType, command, endFunc = (errorMessage, data, layerNum) => {}) => {
/*
fetch(command).then(function(response) {
console.log(response);
if(response.ok) {
return response.blob();
}
throw new Error('Network response was not ok.');
}).then(function(myBlob) {
var objectURL = URL.createObjectURL(myBlob);
console.log(objectURL);
}).catch(function(error) {
console.log('There has been a problem with your fetch operation: ', error.message);
});
*/
const { mainStatusBarTabs } = Mixly;
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
let req = new Request(command);
fetch(req, {
credentials: 'omit', // 设置不传递cookie
mode: 'cors', // 设置请求不允许跨域
}).then(res => {
return res.text();
}).then((data) => {
const dataObj = JSON.parse(data);
console.log(dataObj);
if (dataObj.error) {
statusBarTerminal.addValue(decodeURIComponent(dataObj.error));
endFunc(true, null, layerType);
} else {
statusBarTerminal.addValue(decodeURIComponent(dataObj.compileMessage));
endFunc(false, {
data: dataObj.data,
timeCost: decodeURIComponent(dataObj.timeCost)
}, layerType);
}
})
.catch((error) => {
endFunc(true, error.toString(), layerType);
});
}
});

View File

@@ -0,0 +1,53 @@
goog.loadJs('web', () => {
goog.require('Mixly.Debug');
goog.require('Mixly.Config');
goog.require('Mixly.StatusBarsManager');
goog.require('Mixly.Socket');
goog.require('Mixly.WebCompiler.ArduShell');
goog.provide('Mixly.WebCompiler.Loader');
const {
Debug,
Config,
StatusBarsManager,
Socket,
WebCompiler
} = Mixly;
const {
Loader,
ArduShell
} = WebCompiler;
const { SOFTWARE } = Config;
Loader.init = function () {
let url = '';
if (SOFTWARE.webCompiler?.url) {
const info = new window.URL(SOFTWARE.webCompiler.url);
if (info.hostname === 'default') {
info.hostname = window.location.hostname;
url = info.origin;
} else {
url = SOFTWARE.webCompiler.url;
}
} else {
url = `wss://${window.location.host}`;
}
const mixlySocket = new Socket(`${url}/compile`, {
path: '/mixly-socket/',
reconnection: true,
reconnectionDelayMax: 10000,
transports: ['websocket'],
protocols: ['my-protocol-v1']
});
const socket = mixlySocket.getSocket();
socket.on('connect', () => {});
socket.on('disconnect', () => {});
ArduShell.init(mixlySocket);
}
});

View File

@@ -128,7 +128,7 @@ class WebSocketArduShell {
statusBarSerial.open()
.then(() => {
const baudRates = code.match(/(?<=Serial.begin[\s]*\([\s]*)[0-9]*(?=[\s]*\))/g);
if (!baudRates.length) {
if (!baudRates?.length) {
return statusBarSerial.setBaudRate(9600);
} else {
return statusBarSerial.setBaudRate(baudRates[0] - 0);

View File

@@ -1,31 +1,48 @@
goog.loadJs('web', () => {
goog.require('Mixly.Debug');
goog.require('Mixly.Config');
goog.require('Mixly.StatusBarsManager');
goog.require('Mixly.WebSocket');
goog.require('Mixly.Socket');
goog.require('Mixly.WebSocket.Serial');
goog.require('Mixly.WebSocket.ArduShell');
goog.require('Mixly.WebSocket.BU');
goog.require('Mixly.WebSocket.Ampy');
goog.provide('Mixly.WebSocket.Socket');
goog.provide('Mixly.WebSocket.Loader');
const {
Debug,
Config,
StatusBarsManager,
Socket,
WebSocket
} = Mixly;
const {
Socket,
Loader,
Serial,
ArduShell,
BU,
Ampy
} = WebSocket;
const { SOFTWARE } = Config;
Socket.init = function () {
const mixlySocket = new WebSocket('wss://127.0.0.1:4000', {
Loader.init = function () {
let url = '';
if (SOFTWARE.webSocket?.url) {
const info = new window.URL(SOFTWARE.webSocket.url);
if (info.hostname === 'default') {
info.hostname = window.location.hostname;
url = info.origin;
} else {
url = SOFTWARE.webSocket.url;
}
} else {
url = `wss://${window.location.host}`;
}
const mixlySocket = new Socket(`${url}/all`, {
path: '/mixly-socket/',
reconnection: true,
reconnectionDelayMax: 10000,

View File

@@ -1,91 +1,6 @@
goog.loadJs('web', () => {
goog.require('io');
goog.require('Mixly');
goog.provide('Mixly.WebSocket');
class WebSocket {
#socket_ = null;
constructor(path, option) {
this.#socket_ = io(path, option);
}
#detectStatus_(status, callback) {
window.setTimeout(() => {
if (status.finished) {
return;
}
if (this.isConnected()) {
this.#detectStatus_(status, callback);
} else {
callback({
error: 'socket is not connected'
});
status.finished = true;
}
}, 1000);
}
emit(eventName, ...args) {
const callback = args.pop();
if (this.isConnected()) {
let emitStatus = {
finished: false
};
let status = this.#socket_.emit(eventName, ...args, (...callbackArgs) => {
if (emitStatus.finished) {
return;
}
emitStatus.finished = true;
callback(...callbackArgs);
});
this.#detectStatus_(emitStatus, callback);
return status;
} else {
callback({
error: 'socket is not connected'
});
return false;
}
}
async emitAsync(eventName, ...args) {
return new Promise((resolve, reject) => {
if (this.isConnected()) {
const callback = (...callbackArgs) => {
if (callbackArgs[0].error) {
reject(callbackArgs[0].error);
return;
}
resolve(...callbackArgs);
}
let emitStatus = {
finished: false
};
let status = this.#socket_.emit(eventName, ...args, (...callbackArgs) => {
if (emitStatus.finished) {
return;
}
emitStatus.finished = true;
callback(...callbackArgs);
});
this.#detectStatus_(emitStatus, callback);
} else {
reject('socket is not connected');
}
})
}
getSocket() {
return this.#socket_;
}
isConnected() {
return this.#socket_?.connected;
}
}
Mixly.WebSocket = WebSocket;
});

View File

@@ -166,11 +166,13 @@ class AmpyExt extends Ampy {
if (data.length < 1) {
throw new Error(Msg.Lang['ampy.waitingFirstEOFTimeout']);
}
let start = data.toLowerCase().indexOf('>ok');
if (start === -1){
let start = data.toLowerCase().lastIndexOf('ok');
if (start === -1) {
start = 0;
} else {
start += 2;
}
data = data.substring(start + 3, data.length - 1);
data = data.substring(start, data.length - 1);
let dataError = await this.readUntil('\x04', true, timeout);
if (dataError.length < 1) {
throw new Error(Msg.Lang['ampy.secondEOFTimeout']);

View File

@@ -5,10 +5,9 @@ goog.require('BoardId');
goog.require('FSWrapper');
goog.require('DAPWrapper');
goog.require('PartialFlashing');
goog.require('ESPTool');
goog.require('esptooljs');
goog.require('AdafruitESPTool');
goog.require('CryptoJS');
goog.require('AvrUploader');
goog.require('Mixly.Env');
goog.require('Mixly.LayerExt');
goog.require('Mixly.Config');
@@ -52,7 +51,7 @@ const { BOARD, SELECTED_BOARD } = Config;
const {
ESPLoader,
Transport
} = ESPTool;
} = esptooljs;
BU.uploading = false;
BU.burning = false;
@@ -333,6 +332,9 @@ BU.burnWithEsptool = async (binFile, erase) => {
};
try {
await esploader.writeFlash(flashOptions);
await transport.setDTR(false);
await new Promise((resolve) => setTimeout(resolve, 100));
await transport.setDTR(true);
BU.progressLayer.hide();
layer.msg(Msg.Lang['shell.burnSucc'], { time: 1000 });
statusBarTerminal.addValue(`==${Msg.Lang['shell.burnSucc']}==\n`);
@@ -660,6 +662,9 @@ BU.uploadWithAmpy = async (portName) => {
for (let item of rootInfo) {
rootMap[item[0]] = item[1];
}
if (cwd === '/') {
cwd = '';
}
if (libraries && libraries instanceof Object) {
for (let key in libraries) {
if (rootMap[`${cwd}/${key}`] !== undefined && rootMap[`${cwd}/${key}`] === libraries[key].size) {
@@ -692,110 +697,6 @@ BU.uploadWithAmpy = async (portName) => {
}
}
function hexToBuf (hex) {
var typedArray = new Uint8Array(hex.match(/[\da-f]{2}/gi).map(function (h) {
return parseInt(h, 16)
}));
return typedArray.buffer;
}
BU.uploadWithEsptool = async (endType, obj, layerType) => {
const portName = 'web-serial';
const portObj = Serial.portsOperator[portName];
const { serialport, toolConfig } = portObj;
let prevBaud = toolConfig.baudRates;
if (prevBaud !== 115200) {
toolConfig.baudRates = 115200;
await serialport.setBaudRate(toolConfig.baudRates);
}
const { mainStatusBarTabs } = Mixly;
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
let firmwareData = obj.data;
if (endType || typeof firmwareData !== 'object') {
statusBarTerminal.addValue(Msg.Lang['shell.bin.readFailed'] + "\n");
layer.close(layerType);
return;
}
layer.title(Msg.Lang['shell.uploading'] + '...', layerType);
statusBarTerminal.addValue(Msg.Lang['shell.bin.reading'] + "... ");
let firmwareList = [];
for (let i of firmwareData) {
if (!i.offset || !i.data) {
continue;
}
const firmware = {
offset: i.offset,
binBuf: hexToBuf(i.data)
};
firmwareList.push(firmware);
}
statusBarTerminal.addValue("Done!\n");
BU.burning = true;
BU.uploading = false;
statusBarTerminal.addValue(Msg.Lang['shell.uploading'] + '...\n');
mainStatusBarTabs.show();
mainStatusBarTabs.changeTo('output');
try {
SerialPort.refreshOutputBuffer = false;
SerialPort.refreshInputBuffer = true;
await espTool.reset();
if (await clickSync()) {
// await clickErase();
for (let i of firmwareList) {
await clickProgram(i.offset, i.binBuf);
}
}
layer.close(layerType);
layer.msg(Msg.Lang['shell.uploadSucc'], { time: 1000 });
statusBarTerminal.addValue(`==${Msg.Lang['shell.uploadSucc']}==\n`);
Serial.reset(portName, 'upload');
mainStatusBarTabs.changeTo(portName);
} catch (error) {
Debug.error(error);
layer.close(layerType);
statusBarTerminal.addValue(`==${Msg.Lang['shell.uploadFailed']}==\n`);
} finally {
SerialPort.refreshOutputBuffer = true;
SerialPort.refreshInputBuffer = false;
const code = MFile.getCode();
const baudRateList = code.match(/(?<=Serial.begin[\s]*\([\s]*)[0-9]*(?=[\s]*\))/g);
if (baudRateList && Serial.BAUDRATES.includes(baudRateList[0]-0)) {
prevBaud = baudRateList[0]-0;
}
if (toolConfig.baudRates !== prevBaud) {
toolConfig.baudRates = prevBaud;
await serialport.setBaudRate(prevBaud);
}
}
}
BU.uploadWithAvrUploader = async (endType, obj, layerType) => {
let firmwareData = obj.data;
const { mainStatusBarTabs } = Mixly;
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
if (endType || typeof firmwareData !== 'object') {
statusBarTerminal.addValue(Msg.Lang['shell.bin.readFailed'] + "\n");
layer.close(layerType);
return;
}
statusBarTerminal.addValue(Msg.Lang['shell.uploading'] + '...\n');
layer.title(Msg.Lang['shell.uploading'] + '...', layerType);
let uploadSucMessageShow = true;
AvrUploader.upload(firmwareData[0].data, (progress) => {
if (progress >= 100 && uploadSucMessageShow) {
statusBarTerminal.addValue(`==${Msg.Lang['shell.uploadSucc']}==\n`);
layer.msg(Msg.Lang['shell.uploadSucc'], { time: 1000 });
layer.close(layerType);
uploadSucMessageShow = false;
}
}, true)
.catch((error) => {
layer.close(layerType);
statusBarTerminal.addValue(`==${Msg.Lang['shell.uploadFailed']}==\n`);
});
}
/**
* @function 特殊固件的烧录
* @return {void}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -3640,9 +3640,9 @@ En.MIXLY_SPECIAL_KEY = "special key";
En.MIXLY_GENERAL_KEY = "general key";
En.MIXLY_RELEASE = "automatic release";
En.MIXLY_SPECIAL_KEY0 = "no operation";
En.MIXLY_SPECIAL_KEY1 = "Left Control key";
En.MIXLY_SPECIAL_KEY1 = "Left Alt key";
En.MIXLY_SPECIAL_KEY2 = "Left Shift key";
En.MIXLY_SPECIAL_KEY4 = "Left Alt key";
En.MIXLY_SPECIAL_KEY4 = "Left Control key";
En.MIXLY_SPECIAL_KEY8 = "Left Windows key";
En.MIXLY_SPECIAL_KEY16 = "Right Control key";
En.MIXLY_SPECIAL_KEY32 = "Right Shift key";

View File

@@ -3789,9 +3789,9 @@ ZhHans.MIXLY_SPECIAL_KEY = "特殊按键";
ZhHans.MIXLY_GENERAL_KEY = "普通按键";
ZhHans.MIXLY_RELEASE = "自动释放";
ZhHans.MIXLY_SPECIAL_KEY0 = "无操作";
ZhHans.MIXLY_SPECIAL_KEY1 = "左Control键";
ZhHans.MIXLY_SPECIAL_KEY1 = "左Alt键";
ZhHans.MIXLY_SPECIAL_KEY2 = "左Shift键";
ZhHans.MIXLY_SPECIAL_KEY4 = "左Alt键";
ZhHans.MIXLY_SPECIAL_KEY4 = "左Control键";
ZhHans.MIXLY_SPECIAL_KEY8 = "左Windows键";
ZhHans.MIXLY_SPECIAL_KEY16 = "右Control键";
ZhHans.MIXLY_SPECIAL_KEY32 = "右Shift键";

View File

@@ -3789,9 +3789,9 @@ ZhHant.MIXLY_SPECIAL_KEY = "特殊按鍵";
ZhHant.MIXLY_GENERAL_KEY = "普通按鍵";
ZhHant.MIXLY_RELEASE = "自動釋放";
ZhHant.MIXLY_SPECIAL_KEY0 = "無操作";
ZhHant.MIXLY_SPECIAL_KEY1 = "左Control鍵";
ZhHant.MIXLY_SPECIAL_KEY1 = "左Alt鍵";
ZhHant.MIXLY_SPECIAL_KEY2 = "左Shift鍵";
ZhHant.MIXLY_SPECIAL_KEY4 = "左Alt鍵";
ZhHant.MIXLY_SPECIAL_KEY4 = "左Control鍵";
ZhHant.MIXLY_SPECIAL_KEY8 = "左Windows鍵";
ZhHant.MIXLY_SPECIAL_KEY16 = "右Control鍵";
ZhHant.MIXLY_SPECIAL_KEY32 = "右Shift鍵";

View File

@@ -1,7 +1,7 @@
<div class="menu-line">
<label>{{d.name}}</label>
<div class="sep"></div>
{{# if (d.hotKey) { }}
<div class="sep"></div>
<label>{{d.hotKey}}</label>
{{# } }}
</div>

View File

@@ -12,10 +12,12 @@
}
html[data-bs-theme=light] ul[m-id="{{d.mId}}"] {
background-color: var(--app-light-color) !important;
border-bottom: 1px solid #c9c9c9;
}
html[data-bs-theme=dark] ul[m-id="{{d.mId}}"] {
background-color: #3c3c3c !important;
border-bottom: 1px solid rgba(128, 128, 128, 0.35);
}
@@ -27,9 +29,6 @@
flex-direction: row;
align-items: center;
justify-content: center;
}
ul[m-id="{{d.mId}}"] button {
border-color: transparent;
color: #fff !important;
}
@@ -48,13 +47,11 @@
html[data-bs-theme=light] ul[m-id="{{d.mId}}"] button:hover {
box-shadow: 2px 0px 5px #7b7171;
/*box-shadow: none;*/
border-color: #19897f;
}
html[data-bs-theme=dark] ul[m-id="{{d.mId}}"] button:hover {
box-shadow: 2px 0px 5px #303030;
/*box-shadow: none;*/
border-color: #434242;
}
@@ -75,14 +72,8 @@
max-height: calc(100vh - var(--nav-height) - 12px);
line-height: 30px;
overflow-y: auto;
}
html[data-bs-theme=light] ul[m-id="{{d.mId}}"] {
background-color: var(--app-light-color) !important;
}
html[data-bs-theme=dark] ul[m-id="{{d.mId}}"] {
background-color: #3c3c3c !important;
top: var(--nav-height);
box-shadow: 0 2px 5px rgb(0 0 0 / 50%);
}
ul[m-id="{{d.mId}}"] .layui-nav-item {
@@ -91,10 +82,6 @@
cursor: pointer;
}
ul[m-id="{{d.mId}}"] .layui-nav-child {
top: var(--nav-height);
}
ul[m-id="{{d.mId}}"] dl {
z-index: 1001;
}
@@ -161,25 +148,20 @@
}
ul[m-id="{{d.mId}}"] .layui-nav-item dd > a {
font-size: 14px;
line-height: 14px;
font-family: "Lato", "Noto Sans SC";
font-size: 13px;
line-height: 13px;
border-radius: 5px;
box-sizing: content-box;
padding: 5px 7px 4px 7px !important;
padding: 5px 7px;
margin: 0px 5px;
border-radius: 5px;
min-width: 100px;
font-family: "Lato", "Noto Sans SC";
}
ul[m-id="{{d.mId}}"] .layui-nav-item dd > a {
transition: none;
-webkit-transition: none;
}
ul[m-id="{{d.mId}}"] .layui-nav-item dd > a:hover {
transition: none;
-webkit-transition: none;
color: #fff;
}
@@ -205,11 +187,11 @@
ul[m-id="{{d.mId}}"] > .editor-btn-container {
line-height: 22px;
font-size: 16px;
border-radius: 5px;
background-color: rgba(255, 255, 255, 0.05);
margin: 4px;
padding: 0 2px;
border: 1px solid rgba(204, 204, 204, 0.2);
border-radius: 5px;
}
ul[m-id="{{d.mId}}"] > .editor-btn-container > .copyright {
@@ -283,33 +265,33 @@
-webkit-transition: all .3s;
}
html[data-bs-theme=light] ul[m-id="{{d.mId}}"] select + .select2 > .selection > .select2-selection {
html[data-bs-theme=light] ul[m-id="{{d.mId}}"] .select2 > .selection > .select2-selection {
background-color: #009f90;
border-color: #009f90;
}
html[data-bs-theme=dark] ul[m-id="{{d.mId}}"] select + .select2 > .selection > .select2-selection {
html[data-bs-theme=dark] ul[m-id="{{d.mId}}"] .select2 > .selection > .select2-selection {
background-color: var(--lay-color-fill-2);
border-color: var(--lay-color-border-1);
}
html[data-bs-theme=light] ul[m-id="{{d.mId}}"] select + .select2 > .selection > .select2-selection:hover {
html[data-bs-theme=light] ul[m-id="{{d.mId}}"] .select2 > .selection > .select2-selection:hover {
border-color: #037a6f;
}
html[data-bs-theme=dark] ul[m-id="{{d.mId}}"] select + .select2 > .selection > .select2-selection:hover {
html[data-bs-theme=dark] ul[m-id="{{d.mId}}"] .select2 > .selection > .select2-selection:hover {
border-color: #1f1f1f;
}
ul[m-id="{{d.mId}}"] select + .select2 > .selection > .select2-selection > .select2-selection__rendered {
ul[m-id="{{d.mId}}"] .select2 > .selection > .select2-selection > .select2-selection__rendered {
color: #fff;
}
ul[m-id="{{d.mId}}"] select + .select2 > .selection > .select2-selection > .select2-selection__arrow b {
ul[m-id="{{d.mId}}"] .select2 > .selection > .select2-selection > .select2-selection__arrow b {
border-color: #fff transparent transparent transparent;
}
ul[m-id="{{d.mId}}"] select + .select2-container--open > .selection > .select2-selection > .select2-selection__arrow b {
ul[m-id="{{d.mId}}"] .select2-container--open > .selection > .select2-selection > .select2-selection__arrow b {
border-color: transparent transparent #fff transparent;
}
</style>