From 30f3da24b13d8813a80fb4e08a60d17d2219df47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=AB=8B=E5=B8=AE?= <3294713004@qq.com> Date: Thu, 21 Aug 2025 21:40:18 +0800 Subject: [PATCH] =?UTF-8?q?feat(core):=20micropython=20`=E6=9D=BF=E5=8D=A1?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E7=AE=A1=E7=90=86`=20=E5=8F=B3=E9=94=AE?= =?UTF-8?q?=E8=8F=9C=E5=8D=95=E6=B7=BB=E5=8A=A0=20`=E4=B8=8A=E4=BC=A0`=20?= =?UTF-8?q?=E5=92=8C=20`=E4=B8=8B=E8=BD=BD`=20=E9=80=89=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/modules/mixly-modules/common/fs.js | 2 +- .../mixly-modules/common/statusbar-ampy.js | 143 +++++++++++++++++- common/modules/mixly-modules/deps.json | 1 + .../modules/mixly-modules/electron/ampy-fs.js | 11 +- common/modules/mixly-modules/electron/ampy.js | 4 +- common/modules/mixly-modules/web/ampy-fs.js | 4 +- common/modules/mixly-modules/web/ampy.js | 8 +- common/modules/mixly-modules/web/file.js | 3 +- common/msg/mixly/en.json | 3 + common/msg/mixly/zh-hans.json | 3 + common/msg/mixly/zh-hant.json | 3 + 11 files changed, 171 insertions(+), 14 deletions(-) diff --git a/common/modules/mixly-modules/common/fs.js b/common/modules/mixly-modules/common/fs.js index c4ad27a0..1108b8ec 100644 --- a/common/modules/mixly-modules/common/fs.js +++ b/common/modules/mixly-modules/common/fs.js @@ -11,7 +11,7 @@ class FS { async createFile(filePath) {} - async readFile(filePath) {} + async readFile(filePath, encoding = 'utf8') {} async writeFile(filePath, data) {} diff --git a/common/modules/mixly-modules/common/statusbar-ampy.js b/common/modules/mixly-modules/common/statusbar-ampy.js index 553f170c..d04be88a 100644 --- a/common/modules/mixly-modules/common/statusbar-ampy.js +++ b/common/modules/mixly-modules/common/statusbar-ampy.js @@ -5,6 +5,7 @@ goog.require('layui'); goog.require('Mixly.PageBase'); goog.require('Mixly.Env'); goog.require('Mixly.Msg'); +goog.require('Mixly.Debug'); goog.require('Mixly.HTMLTemplate'); goog.require('Mixly.DragV'); goog.require('Mixly.StatusBar'); @@ -17,6 +18,7 @@ const { PageBase, Env, Msg, + Debug, HTMLTemplate, DragV, StatusBar, @@ -142,6 +144,141 @@ class StatusBarAmpy extends PageBase { fileTreeMenu.add({ weight: 14, id: 'sep5', + data: '---------' + }); + + fileTreeMenu.add({ + weight: 15, + id: 'upload-folder', + preconditionFn: ($trigger) => { + let type = $trigger.attr('type'); + if (type === 'file') { + return false; + } + return true; + }, + data: { + isHtmlName: true, + name: Menu.getItem(Msg.Lang['statusbar.ampy.uploadFolder'], ''), + callback: async (_, { $trigger }) => { + this.#fileTree_.showProgress(); + try { + const fp = await window.showDirectoryPicker(); + if (!fp) { + this.#fileTree_.hideProgress(); + return; + } + const type = $trigger.attr('type'); + let folderPath = '/'; + if (type !== 'root') { + folderPath = $trigger.attr('id'); + } + const fs = this.#fileTree_.getFS(); + try { + for await (const [key, value] of fp.entries()) { + if (value.kind !== 'file') { + continue; + } + const file = await value.getFile(); + const arrayBuffer = await file.arrayBuffer(); + const [error,] = await fs.writeFile(path.join(folderPath, file.name), arrayBuffer); + if (error) { + Debug.error(error); + } + } + } catch (error) { + Debug.error(error); + } + this.#fileTree_.refreshFolder(folderPath); + } catch (_) { } + this.#fileTree_.hideProgress(); + } + } + }); + + fileTreeMenu.add({ + weight: 16, + id: 'upload-file', + preconditionFn: ($trigger) => { + let type = $trigger.attr('type'); + if (type === 'file') { + return false; + } + return true; + }, + data: { + isHtmlName: true, + name: Menu.getItem(Msg.Lang['statusbar.ampy.uploadFile'], ''), + callback: async (_, { $trigger }) => { + this.#fileTree_.showProgress(); + try { + const [ fp ] = await window.showOpenFilePicker({ + multiple: false + }); + if (!fp) { + this.#fileTree_.hideProgress(); + return; + } + const type = $trigger.attr('type'); + let folderPath = '/'; + if (type !== 'root') { + folderPath = $trigger.attr('id'); + } + const file = await fp.getFile(); + const arrayBuffer = await file.arrayBuffer(); + const fs = this.#fileTree_.getFS(); + const [error,] = await fs.writeFile(path.join(folderPath, file.name), arrayBuffer); + if (error) { + Debug.error(error); + } else { + this.#fileTree_.refreshFolder(folderPath); + } + } catch (_) { } + this.#fileTree_.hideProgress(); + } + } + }); + + fileTreeMenu.add({ + weight: 17, + id: 'download', + preconditionFn: ($trigger) => { + const type = $trigger.attr('type'); + if (type === 'file') { + return true; + } + return false; + }, + data: { + isHtmlName: true, + name: Menu.getItem(Msg.Lang['statusbar.ampy.download'], ''), + callback: async (_, { $trigger }) => { + const filePath = $trigger.attr('id'); + this.#fileTree_.showProgress(); + const fp = await window.showSaveFilePicker({ + suggestedName: path.basename(filePath) + }); + if (!fp) { + this.#fileTree_.hideProgress(); + return; + } + const fs = this.#fileTree_.getFS(); + const [error, result] = await fs.readFile(filePath, 'buffer'); + if (error) { + Debug.error(error); + } else { + const writer = await fp.createWritable(); + await writer.write(result); + await writer.close(); + } + this.#fileTree_.hideProgress(); + } + } + }); + + fileTreeMenu.add({ + weight: 18, + id: 'sep6', preconditionFn: ($trigger) => { const selectedNodeId = this.#fileTree_.getSelectedNodeId(); let type = $trigger.attr('type'); @@ -155,7 +292,7 @@ class StatusBarAmpy extends PageBase { }); fileTreeMenu.add({ - weight: 15, + weight: 19, id: 'refresh', preconditionFn: ($trigger) => { const selectedNodeId = this.#fileTree_.getSelectedNodeId(); @@ -187,7 +324,7 @@ class StatusBarAmpy extends PageBase { }); fileTreeMenu.add({ - weight: 16, + weight: 20, id: 'sep6', preconditionFn: ($trigger) => { let type = $trigger.attr('type'); @@ -197,7 +334,7 @@ class StatusBarAmpy extends PageBase { }); fileTreeMenu.add({ - weight: 17, + weight: 21, id: 'exit', preconditionFn: ($trigger) => { let type = $trigger.attr('type'); diff --git a/common/modules/mixly-modules/deps.json b/common/modules/mixly-modules/deps.json index 33562b15..ee5ffc1b 100644 --- a/common/modules/mixly-modules/deps.json +++ b/common/modules/mixly-modules/deps.json @@ -957,6 +957,7 @@ "Mixly.PageBase", "Mixly.Env", "Mixly.Msg", + "Mixly.Debug", "Mixly.HTMLTemplate", "Mixly.DragV", "Mixly.StatusBar", diff --git a/common/modules/mixly-modules/electron/ampy-fs.js b/common/modules/mixly-modules/electron/ampy-fs.js index 8563a0bb..4a5ad65a 100644 --- a/common/modules/mixly-modules/electron/ampy-fs.js +++ b/common/modules/mixly-modules/electron/ampy-fs.js @@ -54,12 +54,16 @@ class AmpyFS extends FS { return [error, stdout]; } - async readFile(filePath) { + async readFile(filePath, encoding = 'utf8') { let stdout = '', error = null; try { const output = await this.#ampy_.get(this.#port_, this.#baud_, filePath); stdout = output.stdout; - stdout = this.#decoder_.decode(this.#ampy_.unhexlify(stdout)); + if (encoding = 'utf8') { + stdout = this.#decoder_.decode(this.#ampy_.unhexlify(stdout)); + } else { + stdout = this.#ampy_.unhexlify(stdout); + } } catch (e) { error = e; Debug.error(error); @@ -71,6 +75,9 @@ class AmpyFS extends FS { let stdout = '', error = null; try { const startFilePath = path.join(Env.clientPath, 'temp/temp'); + if (data.constructor.name === 'ArrayBuffer') { + data = Buffer.from(data); + } await fs_extra.outputFile(startFilePath, data); const output = await this.#ampy_.put(this.#port_, this.#baud_, startFilePath, filePath); stdout = output.stdout; diff --git a/common/modules/mixly-modules/electron/ampy.js b/common/modules/mixly-modules/electron/ampy.js index e4b07a58..7f81a2b4 100644 --- a/common/modules/mixly-modules/electron/ampy.js +++ b/common/modules/mixly-modules/electron/ampy.js @@ -54,8 +54,8 @@ class AmpyExt extends Ampy { return this.exec(port, this.render('ls', { port, baud, folderPath })); } - async get(port, baud, filePath) { - return this.exec(port, this.render('get', { port, baud, filePath })); + async get(port, baud, filePath, encoding = 'utf8') { + return this.exec(port, this.render('get', { port, baud, filePath, encoding })); } async mkdir(port, baud, folderPath) { diff --git a/common/modules/mixly-modules/web/ampy-fs.js b/common/modules/mixly-modules/web/ampy-fs.js index 47339f02..1071496b 100644 --- a/common/modules/mixly-modules/web/ampy-fs.js +++ b/common/modules/mixly-modules/web/ampy-fs.js @@ -72,12 +72,12 @@ class AmpyFS extends FS { return [error, stdout]; } - async readFile(filePath) { + async readFile(filePath, encoding = 'utf8') { let stdout = '', error = null, ampy = null; try { ampy = await this.getAmpy(); await ampy.enter(); - stdout = await ampy.get(filePath); + stdout = await ampy.get(filePath, encoding); } catch (e) { error = e; Debug.error(error); diff --git a/common/modules/mixly-modules/web/ampy.js b/common/modules/mixly-modules/web/ampy.js index 909f1635..8ec9c875 100644 --- a/common/modules/mixly-modules/web/ampy.js +++ b/common/modules/mixly-modules/web/ampy.js @@ -211,7 +211,7 @@ class AmpyExt extends Ampy { this.#active_ = false; } - async get(filename, timeout = 5000) { + async get(filename, encoding = 'utf8', timeout = 5000) { if (!this.isActive()) { throw new Error(Msg.Lang['ampy.portIsNotOpen']); } @@ -222,7 +222,11 @@ class AmpyExt extends Ampy { if (dataError) { return ''; } - return this.#device_.decode(this.unhexlify(data)); + if (encoding === 'utf8') { + return this.#device_.decode(this.unhexlify(data)); + } else { + return this.unhexlify(data); + } } async put(filename, data, timeout = 5000) { diff --git a/common/modules/mixly-modules/web/file.js b/common/modules/mixly-modules/web/file.js index 0f07b560..95422220 100644 --- a/common/modules/mixly-modules/web/file.js +++ b/common/modules/mixly-modules/web/file.js @@ -57,8 +57,7 @@ File.open = async () => { const fileConfig = { multiple: false, types: File.getFileTypes(filters), - excludeAcceptAllOption: true, - multiple: false, + excludeAcceptAllOption: true }; try { const [ obj ] = await window.showOpenFilePicker(fileConfig); diff --git a/common/msg/mixly/en.json b/common/msg/mixly/en.json index 10f8f6d1..54e01378 100644 --- a/common/msg/mixly/en.json +++ b/common/msg/mixly/en.json @@ -38,6 +38,9 @@ "statusbar.ampy.refresh": "Refresh", "statusbar.ampy.exit": "Exit", "statusbar.ampy.cannotEdit": "This file type does not support editing", + "statusbar.ampy.uploadFile": "Upload file", + "statusbar.ampy.uploadFolder": "Upload folder", + "statusbar.ampy.download": "Download", "statusbar.dropdownMenu.noOptions": "No options", "statusbar.fs.newMapFolder": "Create a new mapping directory", "statusbar.fs.localFolderNotExist": "Local mapping directory does not exist", diff --git a/common/msg/mixly/zh-hans.json b/common/msg/mixly/zh-hans.json index c4e76ac1..1d049b27 100644 --- a/common/msg/mixly/zh-hans.json +++ b/common/msg/mixly/zh-hans.json @@ -38,6 +38,9 @@ "statusbar.ampy.refresh": "刷新", "statusbar.ampy.exit": "退出", "statusbar.ampy.cannotEdit": "该文件类型不支持编辑", + "statusbar.ampy.uploadFile": "上传文件", + "statusbar.ampy.uploadFolder": "上传文件夹", + "statusbar.ampy.download": "下载", "statusbar.dropdownMenu.noOptions": "无选项", "statusbar.fs.newMapFolder": "新建映射目录", "statusbar.fs.localFolderNotExist": "本地映射目录不存在", diff --git a/common/msg/mixly/zh-hant.json b/common/msg/mixly/zh-hant.json index 5a6cdaaf..98dc128f 100644 --- a/common/msg/mixly/zh-hant.json +++ b/common/msg/mixly/zh-hant.json @@ -39,6 +39,9 @@ "statusbar.ampy.refresh": "刷新", "statusbar.ampy.exit": "退出", "statusbar.ampy.cannotEdit": "該文件類型不支援編輯", + "statusbar.ampy.uploadFile": "上傳檔案", + "statusbar.ampy.uploadFolder": "上傳資料夾", + "statusbar.ampy.download": "下載", "statusbar.dropdownMenu.noOptions": "無選項", "statusbar.fs.newMapFolder": "新映射目錄", "statusbar.fs.localFolderNotExist": "本機映射目錄不存在",