From 958999e78b26bd0a0fbca511155f648a735358dd Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Wed, 17 Jun 2026 11:06:01 +0800 Subject: [PATCH 01/53] =?UTF-8?q?feat(lang):=20=E6=96=B0=E5=A2=9E=20Dart?= =?UTF-8?q?=20=E8=AF=AD=E8=A8=80=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端插件 plugins/dart.rs:dart run $filename,扩展名 dart,console 输出 - 注册 plugins/mod.rs 与 manager.rs(前端语言下拉/扩展名探测自动生成) - CodeMirror 高亮复用 legacy-modes clike 的 dart 流式解析 - LSP:server_cmd/server_defs 接入 dart language-server(随 Dart SDK 提供) 及前端 lspExtension 的 LANGUAGE_ID/LANGUAGE_EXT - 新增图标 public/icons/dart.svg --- public/icons/dart.svg | 5 +++ src-tauri/src/lsp.rs | 2 + src-tauri/src/plugins/dart.rs | 54 ++++++++++++++++++++++++++ src-tauri/src/plugins/manager.rs | 2 + src-tauri/src/plugins/mod.rs | 1 + src/composables/useCodeMirrorEditor.ts | 4 +- src/editor/lspExtension.ts | 5 ++- 7 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 public/icons/dart.svg create mode 100644 src-tauri/src/plugins/dart.rs diff --git a/public/icons/dart.svg b/public/icons/dart.svg new file mode 100644 index 0000000..40ac534 --- /dev/null +++ b/public/icons/dart.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src-tauri/src/lsp.rs b/src-tauri/src/lsp.rs index 9cb1495..963293f 100644 --- a/src-tauri/src/lsp.rs +++ b/src-tauri/src/lsp.rs @@ -57,6 +57,7 @@ fn server_cmd(language: &str) -> Option<(&'static str, Vec<&'static str>)> { "yaml" => Some(("yaml-language-server", vec!["--stdio"])), "shell" => Some(("bash-language-server", vec!["start"])), "haskell" => Some(("haskell-language-server-wrapper", vec!["--lsp"])), + "dart" => Some(("dart", vec!["language-server"])), _ => None, } } @@ -151,6 +152,7 @@ fn server_defs() -> Vec<(&'static str, &'static str, &'static str, &'static str) "haskell-language-server-wrapper", "ghcup install hls", ), + ("dart", "Dart", "dart", "brew install dart"), ] } diff --git a/src-tauri/src/plugins/dart.rs b/src-tauri/src/plugins/dart.rs new file mode 100644 index 0000000..8b1c181 --- /dev/null +++ b/src-tauri/src/plugins/dart.rs @@ -0,0 +1,54 @@ +use super::{LanguagePlugin, PluginConfig}; +use std::vec; + +pub struct DartPlugin; + +impl LanguagePlugin for DartPlugin { + fn get_order(&self) -> i32 { + 29 + } + + fn get_language_name(&self) -> &'static str { + "Dart" + } + + fn get_language_key(&self) -> &'static str { + "dart" + } + + fn get_file_extension(&self) -> String { + self.get_config() + .map(|config| config.extension.clone()) + .unwrap_or_else(|| "dart".to_string()) + } + + fn get_version_args(&self) -> Vec<&'static str> { + vec!["--version"] + } + + fn get_path_command(&self) -> String { + "which dart".to_string() + } + + fn get_default_config(&self) -> PluginConfig { + PluginConfig { + enabled: true, + language: String::from("dart"), + before_compile: None, + extension: String::from("dart"), + execute_home: None, + run_command: Some(String::from("dart run $filename")), + after_compile: None, + template: Some(String::from("void main() {\n print('Hello, Dart!');\n}")), + timeout: Some(30), + console_type: Some(String::from("console")), + icon_path: None, + } + } + + fn get_default_command(&self) -> String { + self.get_config() + .and_then(|config| config.run_command.clone()) + .unwrap_or_else(|| "dart".to_string()) + } +} diff --git a/src-tauri/src/plugins/manager.rs b/src-tauri/src/plugins/manager.rs index c079b08..bfbf7ea 100644 --- a/src-tauri/src/plugins/manager.rs +++ b/src-tauri/src/plugins/manager.rs @@ -7,6 +7,7 @@ use crate::plugins::cpp::CppPlugin; use crate::plugins::css::CssPlugin; use crate::plugins::csv::CsvPlugin; use crate::plugins::custom::CustomPlugin; +use crate::plugins::dart::DartPlugin; use crate::plugins::go::GoPlugin; use crate::plugins::groovy::GroovyPlugin; use crate::plugins::haskell::HaskellPlugin; @@ -69,6 +70,7 @@ impl PluginManager { ("clojure".to_string(), Box::new(ClojurePlugin)), ("c".to_string(), Box::new(CPlugin)), ("ruby".to_string(), Box::new(RubyPlugin)), + ("dart".to_string(), Box::new(DartPlugin)), ("applescript".to_string(), Box::new(AppleScriptPlugin)), ("typescript".to_string(), Box::new(TypeScriptPlugin)), ("react".to_string(), Box::new(ReactPlugin)), diff --git a/src-tauri/src/plugins/mod.rs b/src-tauri/src/plugins/mod.rs index 66c0ff6..d61195b 100644 --- a/src-tauri/src/plugins/mod.rs +++ b/src-tauri/src/plugins/mod.rs @@ -406,6 +406,7 @@ pub mod cpp; pub mod css; pub mod csv; pub mod custom; +pub mod dart; pub mod go; pub mod groovy; pub mod haskell; diff --git a/src/composables/useCodeMirrorEditor.ts b/src/composables/useCodeMirrorEditor.ts index d9309cf..591f8b1 100644 --- a/src/composables/useCodeMirrorEditor.ts +++ b/src/composables/useCodeMirrorEditor.ts @@ -16,7 +16,7 @@ import {xml} from '@codemirror/lang-xml' import {php} from '@codemirror/lang-php' import {shell} from '@codemirror/legacy-modes/mode/shell' import {swift} from '@codemirror/legacy-modes/mode/swift' -import {kotlin, objectiveC, objectiveCpp, scala} from '@codemirror/legacy-modes/mode/clike' +import {dart, kotlin, objectiveC, objectiveCpp, scala} from '@codemirror/legacy-modes/mode/clike' import {clojure} from '@codemirror/legacy-modes/mode/clojure' import {ruby} from '@codemirror/legacy-modes/mode/ruby' import {groovy} from '@codemirror/legacy-modes/mode/groovy' @@ -348,6 +348,8 @@ export function useCodeMirrorEditor(props: Props) return StreamLanguage.define(scala) case 'kotlin': return StreamLanguage.define(kotlin) + case 'dart': + return StreamLanguage.define(dart) case 'clojure': return StreamLanguage.define(clojure) case 'ruby': diff --git a/src/editor/lspExtension.ts b/src/editor/lspExtension.ts index d419e8d..87bfa86 100644 --- a/src/editor/lspExtension.ts +++ b/src/editor/lspExtension.ts @@ -230,7 +230,8 @@ const LANGUAGE_ID: Record = { scala: 'scala', yaml: 'yaml', shell: 'shellscript', - haskell: 'haskell' + haskell: 'haskell', + dart: 'dart' } // 草稿(未保存)时用的文件扩展名,构造 untitled 文档 URI @@ -240,7 +241,7 @@ const LANGUAGE_EXT: Record = { lua: 'lua', php: 'php', ruby: 'rb', html: 'html', css: 'css', less: 'less', json: 'json', javascriptreact: 'jsx', java: 'java', kotlin: 'kt', swift: 'swift', scala: 'scala', yaml: 'yaml', - shellscript: 'sh', haskell: 'hs' + shellscript: 'sh', haskell: 'hs', dart: 'dart' } export const lspSupportsLanguage = (language?: string): boolean => From 6f98a64a7412d36139897bdb90c27574af923215 Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Wed, 17 Jun 2026 11:11:44 +0800 Subject: [PATCH 02/53] =?UTF-8?q?docs:=20=E5=9C=A8=20README=20=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E8=AF=AD=E8=A8=80=E4=B8=AD=E8=A1=A5=E5=85=85=20Dart?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1bff5f9..833a91d 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,7 @@ + @@ -122,7 +123,7 @@
-`Python` · `Node.js` · `TypeScript` · `JavaScript` · `React` · `Go` · `Rust` · `Java` · `Kotlin` · `Scala` · `Groovy` · `Clojure` · `C` · `C++` · `Objective-C/C++` · `Swift` · `Ruby` · `PHP` · `R` · `Lua` · `Haskell` · `Cangjie` · `Shell` · `AppleScript` · `SQL` · `HTML` · `CSS` · `Less` · `SVG` · `JSON` · `XML` · `YAML` · `Markdown` · `CSV` · `TSV` · `Excel` · `Text` +`Python` · `Node.js` · `TypeScript` · `JavaScript` · `React` · `Go` · `Rust` · `Java` · `Kotlin` · `Scala` · `Groovy` · `Clojure` · `C` · `C++` · `Objective-C/C++` · `Swift` · `Dart` · `Ruby` · `PHP` · `R` · `Lua` · `Haskell` · `Cangjie` · `Shell` · `AppleScript` · `SQL` · `HTML` · `CSS` · `Less` · `SVG` · `JSON` · `XML` · `YAML` · `Markdown` · `CSV` · `TSV` · `Excel` · `Text`
From d9c662d4074a5eb16cefeb50fcd921fcc0062d50 Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Wed, 17 Jun 2026 11:29:46 +0800 Subject: [PATCH 03/53] =?UTF-8?q?feat(lang):=20=E6=96=B0=E5=A2=9E=20Perl?= =?UTF-8?q?=20=E8=AF=AD=E8=A8=80=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端插件 plugins/perl.rs:perl $filename,扩展名 pl,console 输出 - 注册 plugins/mod.rs 与 manager.rs(语言下拉/扩展名探测自动生成) - CodeMirror 高亮复用 legacy-modes 的 perl 流式解析 - 新增图标 public/icons/perl.svg(社区洋葱符号),README 支持语言补充 Perl - 暂不接 LSP(无干净的单二进制 server,按约定自动回退高亮+AI 预测) --- README.md | 3 +- public/icons/perl.svg | 4 ++ src-tauri/src/plugins/manager.rs | 2 + src-tauri/src/plugins/mod.rs | 1 + src-tauri/src/plugins/perl.rs | 54 ++++++++++++++++++++++++++ src/composables/useCodeMirrorEditor.ts | 3 ++ 6 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 public/icons/perl.svg create mode 100644 src-tauri/src/plugins/perl.rs diff --git a/README.md b/README.md index 833a91d..871f73d 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,7 @@ + @@ -123,7 +124,7 @@
-`Python` · `Node.js` · `TypeScript` · `JavaScript` · `React` · `Go` · `Rust` · `Java` · `Kotlin` · `Scala` · `Groovy` · `Clojure` · `C` · `C++` · `Objective-C/C++` · `Swift` · `Dart` · `Ruby` · `PHP` · `R` · `Lua` · `Haskell` · `Cangjie` · `Shell` · `AppleScript` · `SQL` · `HTML` · `CSS` · `Less` · `SVG` · `JSON` · `XML` · `YAML` · `Markdown` · `CSV` · `TSV` · `Excel` · `Text` +`Python` · `Node.js` · `TypeScript` · `JavaScript` · `React` · `Go` · `Rust` · `Java` · `Kotlin` · `Scala` · `Groovy` · `Clojure` · `C` · `C++` · `Objective-C/C++` · `Swift` · `Dart` · `Ruby` · `Perl` · `PHP` · `R` · `Lua` · `Haskell` · `Cangjie` · `Shell` · `AppleScript` · `SQL` · `HTML` · `CSS` · `Less` · `SVG` · `JSON` · `XML` · `YAML` · `Markdown` · `CSV` · `TSV` · `Excel` · `Text`
diff --git a/public/icons/perl.svg b/public/icons/perl.svg new file mode 100644 index 0000000..aedf57e --- /dev/null +++ b/public/icons/perl.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src-tauri/src/plugins/manager.rs b/src-tauri/src/plugins/manager.rs index bfbf7ea..d578e4e 100644 --- a/src-tauri/src/plugins/manager.rs +++ b/src-tauri/src/plugins/manager.rs @@ -24,6 +24,7 @@ use crate::plugins::markdown::MarkdownPlugin; use crate::plugins::nodejs::NodeJSPlugin; use crate::plugins::objective_c::ObjectiveCPlugin; use crate::plugins::objective_cpp::ObjectiveCppPlugin; +use crate::plugins::perl::PerlPlugin; use crate::plugins::php::PHPPlugin; use crate::plugins::python2::Python2Plugin; use crate::plugins::python3::Python3Plugin; @@ -71,6 +72,7 @@ impl PluginManager { ("c".to_string(), Box::new(CPlugin)), ("ruby".to_string(), Box::new(RubyPlugin)), ("dart".to_string(), Box::new(DartPlugin)), + ("perl".to_string(), Box::new(PerlPlugin)), ("applescript".to_string(), Box::new(AppleScriptPlugin)), ("typescript".to_string(), Box::new(TypeScriptPlugin)), ("react".to_string(), Box::new(ReactPlugin)), diff --git a/src-tauri/src/plugins/mod.rs b/src-tauri/src/plugins/mod.rs index d61195b..85bf2dc 100644 --- a/src-tauri/src/plugins/mod.rs +++ b/src-tauri/src/plugins/mod.rs @@ -424,6 +424,7 @@ pub mod markdown; pub mod nodejs; pub mod objective_c; pub mod objective_cpp; +pub mod perl; pub mod php; pub mod python2; pub mod python3; diff --git a/src-tauri/src/plugins/perl.rs b/src-tauri/src/plugins/perl.rs new file mode 100644 index 0000000..7b96ab8 --- /dev/null +++ b/src-tauri/src/plugins/perl.rs @@ -0,0 +1,54 @@ +use super::{LanguagePlugin, PluginConfig}; +use std::vec; + +pub struct PerlPlugin; + +impl LanguagePlugin for PerlPlugin { + fn get_order(&self) -> i32 { + 30 + } + + fn get_language_name(&self) -> &'static str { + "Perl" + } + + fn get_language_key(&self) -> &'static str { + "perl" + } + + fn get_file_extension(&self) -> String { + self.get_config() + .map(|config| config.extension.clone()) + .unwrap_or_else(|| "pl".to_string()) + } + + fn get_version_args(&self) -> Vec<&'static str> { + vec!["--version"] + } + + fn get_path_command(&self) -> String { + "which perl".to_string() + } + + fn get_default_config(&self) -> PluginConfig { + PluginConfig { + enabled: true, + language: String::from("perl"), + before_compile: None, + extension: String::from("pl"), + execute_home: None, + run_command: Some(String::from("perl $filename")), + after_compile: None, + template: Some(String::from("print \"Hello, Perl!\\n\";")), + timeout: Some(30), + console_type: Some(String::from("console")), + icon_path: None, + } + } + + fn get_default_command(&self) -> String { + self.get_config() + .and_then(|config| config.run_command.clone()) + .unwrap_or_else(|| "perl".to_string()) + } +} diff --git a/src/composables/useCodeMirrorEditor.ts b/src/composables/useCodeMirrorEditor.ts index 591f8b1..51e23f1 100644 --- a/src/composables/useCodeMirrorEditor.ts +++ b/src/composables/useCodeMirrorEditor.ts @@ -19,6 +19,7 @@ import {swift} from '@codemirror/legacy-modes/mode/swift' import {dart, kotlin, objectiveC, objectiveCpp, scala} from '@codemirror/legacy-modes/mode/clike' import {clojure} from '@codemirror/legacy-modes/mode/clojure' import {ruby} from '@codemirror/legacy-modes/mode/ruby' +import {perl} from '@codemirror/legacy-modes/mode/perl' import {groovy} from '@codemirror/legacy-modes/mode/groovy' import {r} from "@codemirror/legacy-modes/mode/r" import {haskell} from "@codemirror/legacy-modes/mode/haskell" @@ -354,6 +355,8 @@ export function useCodeMirrorEditor(props: Props) return StreamLanguage.define(clojure) case 'ruby': return StreamLanguage.define(ruby) + case 'perl': + return StreamLanguage.define(perl) case 'typescript': case 'typescript-browser': case 'typescript-nodejs': From f222fe986bac38280b63126b2b0846121574b9e2 Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Wed, 17 Jun 2026 11:47:20 +0800 Subject: [PATCH 04/53] =?UTF-8?q?feat(lang):=20=E6=96=B0=E5=A2=9E=20Julia?= =?UTF-8?q?=20=E8=AF=AD=E8=A8=80=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端插件 plugins/julia.rs:julia $filename,扩展名 jl,console 输出 - 注册 plugins/mod.rs 与 manager.rs(语言下拉/扩展名探测自动生成) - CodeMirror 高亮复用 legacy-modes 的 julia 流式解析 - 新增图标 public/icons/julia.svg(三色圆点),README 支持语言补充 Julia - 暂不接 LSP(LanguageServer.jl 启动复杂,按约定自动回退高亮+AI 预测) --- README.md | 3 +- public/icons/julia.svg | 5 +++ src-tauri/src/plugins/julia.rs | 54 ++++++++++++++++++++++++++ src-tauri/src/plugins/manager.rs | 2 + src-tauri/src/plugins/mod.rs | 1 + src/composables/useCodeMirrorEditor.ts | 3 ++ 6 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 public/icons/julia.svg create mode 100644 src-tauri/src/plugins/julia.rs diff --git a/README.md b/README.md index 871f73d..a63e291 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,7 @@ + @@ -124,7 +125,7 @@
-`Python` · `Node.js` · `TypeScript` · `JavaScript` · `React` · `Go` · `Rust` · `Java` · `Kotlin` · `Scala` · `Groovy` · `Clojure` · `C` · `C++` · `Objective-C/C++` · `Swift` · `Dart` · `Ruby` · `Perl` · `PHP` · `R` · `Lua` · `Haskell` · `Cangjie` · `Shell` · `AppleScript` · `SQL` · `HTML` · `CSS` · `Less` · `SVG` · `JSON` · `XML` · `YAML` · `Markdown` · `CSV` · `TSV` · `Excel` · `Text` +`Python` · `Node.js` · `TypeScript` · `JavaScript` · `React` · `Go` · `Rust` · `Java` · `Kotlin` · `Scala` · `Groovy` · `Clojure` · `C` · `C++` · `Objective-C/C++` · `Swift` · `Dart` · `Ruby` · `Perl` · `PHP` · `R` · `Julia` · `Lua` · `Haskell` · `Cangjie` · `Shell` · `AppleScript` · `SQL` · `HTML` · `CSS` · `Less` · `SVG` · `JSON` · `XML` · `YAML` · `Markdown` · `CSV` · `TSV` · `Excel` · `Text`
diff --git a/public/icons/julia.svg b/public/icons/julia.svg new file mode 100644 index 0000000..953bdcc --- /dev/null +++ b/public/icons/julia.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src-tauri/src/plugins/julia.rs b/src-tauri/src/plugins/julia.rs new file mode 100644 index 0000000..706024b --- /dev/null +++ b/src-tauri/src/plugins/julia.rs @@ -0,0 +1,54 @@ +use super::{LanguagePlugin, PluginConfig}; +use std::vec; + +pub struct JuliaPlugin; + +impl LanguagePlugin for JuliaPlugin { + fn get_order(&self) -> i32 { + 31 + } + + fn get_language_name(&self) -> &'static str { + "Julia" + } + + fn get_language_key(&self) -> &'static str { + "julia" + } + + fn get_file_extension(&self) -> String { + self.get_config() + .map(|config| config.extension.clone()) + .unwrap_or_else(|| "jl".to_string()) + } + + fn get_version_args(&self) -> Vec<&'static str> { + vec!["--version"] + } + + fn get_path_command(&self) -> String { + "which julia".to_string() + } + + fn get_default_config(&self) -> PluginConfig { + PluginConfig { + enabled: true, + language: String::from("julia"), + before_compile: None, + extension: String::from("jl"), + execute_home: None, + run_command: Some(String::from("julia $filename")), + after_compile: None, + template: Some(String::from("println(\"Hello, Julia!\")")), + timeout: Some(30), + console_type: Some(String::from("console")), + icon_path: None, + } + } + + fn get_default_command(&self) -> String { + self.get_config() + .and_then(|config| config.run_command.clone()) + .unwrap_or_else(|| "julia".to_string()) + } +} diff --git a/src-tauri/src/plugins/manager.rs b/src-tauri/src/plugins/manager.rs index d578e4e..dd9cb23 100644 --- a/src-tauri/src/plugins/manager.rs +++ b/src-tauri/src/plugins/manager.rs @@ -17,6 +17,7 @@ use crate::plugins::javascript_browser::JavaScriptBrowserPlugin; use crate::plugins::javascript_jquery::JavaScriptJQueryPlugin; use crate::plugins::javascript_nodejs::JavaScriptNodeJsPlugin; use crate::plugins::json::JsonPlugin; +use crate::plugins::julia::JuliaPlugin; use crate::plugins::kotlin::KotlinPlugin; use crate::plugins::less::LessPlugin; use crate::plugins::lua::LuaPlugin; @@ -73,6 +74,7 @@ impl PluginManager { ("ruby".to_string(), Box::new(RubyPlugin)), ("dart".to_string(), Box::new(DartPlugin)), ("perl".to_string(), Box::new(PerlPlugin)), + ("julia".to_string(), Box::new(JuliaPlugin)), ("applescript".to_string(), Box::new(AppleScriptPlugin)), ("typescript".to_string(), Box::new(TypeScriptPlugin)), ("react".to_string(), Box::new(ReactPlugin)), diff --git a/src-tauri/src/plugins/mod.rs b/src-tauri/src/plugins/mod.rs index 85bf2dc..4ac7a51 100644 --- a/src-tauri/src/plugins/mod.rs +++ b/src-tauri/src/plugins/mod.rs @@ -416,6 +416,7 @@ pub mod javascript_browser; pub mod javascript_jquery; pub mod javascript_nodejs; pub mod json; +pub mod julia; pub mod kotlin; pub mod less; pub mod lua; diff --git a/src/composables/useCodeMirrorEditor.ts b/src/composables/useCodeMirrorEditor.ts index 51e23f1..c51b6bc 100644 --- a/src/composables/useCodeMirrorEditor.ts +++ b/src/composables/useCodeMirrorEditor.ts @@ -20,6 +20,7 @@ import {dart, kotlin, objectiveC, objectiveCpp, scala} from '@codemirror/legacy- import {clojure} from '@codemirror/legacy-modes/mode/clojure' import {ruby} from '@codemirror/legacy-modes/mode/ruby' import {perl} from '@codemirror/legacy-modes/mode/perl' +import {julia} from '@codemirror/legacy-modes/mode/julia' import {groovy} from '@codemirror/legacy-modes/mode/groovy' import {r} from "@codemirror/legacy-modes/mode/r" import {haskell} from "@codemirror/legacy-modes/mode/haskell" @@ -357,6 +358,8 @@ export function useCodeMirrorEditor(props: Props) return StreamLanguage.define(ruby) case 'perl': return StreamLanguage.define(perl) + case 'julia': + return StreamLanguage.define(julia) case 'typescript': case 'typescript-browser': case 'typescript-nodejs': From dde516c612ef9034669048d4537e33f429ee04f3 Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Wed, 17 Jun 2026 12:12:32 +0800 Subject: [PATCH 05/53] =?UTF-8?q?feat(lang):=20=E6=96=B0=E5=A2=9E=20Vue=20?= =?UTF-8?q?=E8=AF=AD=E8=A8=80=E6=94=AF=E6=8C=81=20(#85)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端插件 plugins/vue.rs:console_type web,CDN 引入 Vue 3 全局构建, 用户代码用 Vue.createApp(...).mount('#app') 在浏览器内运行时渲染 - 无本机依赖(get_path_command 用 "--",与 HTML 一致),始终可运行 - 注册 mod.rs/manager.rs,高亮复用 javascript(),新增图标 public/icons/vue.svg - README 支持语言补充 Vue - 不走 SFC 编译:WebOutput 内联 file:// 脚本会剥离 type 属性,故用纯 JS 全局构建最稳 --- README.md | 3 +- public/icons/vue.svg | 4 ++ src-tauri/src/plugins/manager.rs | 2 + src-tauri/src/plugins/mod.rs | 1 + src-tauri/src/plugins/vue.rs | 61 ++++++++++++++++++++++++++ src/composables/useCodeMirrorEditor.ts | 1 + 6 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 public/icons/vue.svg create mode 100644 src-tauri/src/plugins/vue.rs diff --git a/README.md b/README.md index a63e291..9f9bb60 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ + @@ -125,7 +126,7 @@
-`Python` · `Node.js` · `TypeScript` · `JavaScript` · `React` · `Go` · `Rust` · `Java` · `Kotlin` · `Scala` · `Groovy` · `Clojure` · `C` · `C++` · `Objective-C/C++` · `Swift` · `Dart` · `Ruby` · `Perl` · `PHP` · `R` · `Julia` · `Lua` · `Haskell` · `Cangjie` · `Shell` · `AppleScript` · `SQL` · `HTML` · `CSS` · `Less` · `SVG` · `JSON` · `XML` · `YAML` · `Markdown` · `CSV` · `TSV` · `Excel` · `Text` +`Python` · `Node.js` · `TypeScript` · `JavaScript` · `React` · `Vue` · `Go` · `Rust` · `Java` · `Kotlin` · `Scala` · `Groovy` · `Clojure` · `C` · `C++` · `Objective-C/C++` · `Swift` · `Dart` · `Ruby` · `Perl` · `PHP` · `R` · `Julia` · `Lua` · `Haskell` · `Cangjie` · `Shell` · `AppleScript` · `SQL` · `HTML` · `CSS` · `Less` · `SVG` · `JSON` · `XML` · `YAML` · `Markdown` · `CSV` · `TSV` · `Excel` · `Text`
diff --git a/public/icons/vue.svg b/public/icons/vue.svg new file mode 100644 index 0000000..b1893c2 --- /dev/null +++ b/public/icons/vue.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src-tauri/src/plugins/manager.rs b/src-tauri/src/plugins/manager.rs index dd9cb23..a7f8349 100644 --- a/src-tauri/src/plugins/manager.rs +++ b/src-tauri/src/plugins/manager.rs @@ -43,6 +43,7 @@ use crate::plugins::tsv::TsvPlugin; use crate::plugins::typescript::TypeScriptPlugin; use crate::plugins::typescript_browser::TypeScriptBrowserPlugin; use crate::plugins::typescript_nodejs::TypeScriptNodeJsPlugin; +use crate::plugins::vue::VuePlugin; use crate::plugins::xlsx::XlsxPlugin; use crate::plugins::xml::XmlPlugin; use crate::plugins::yaml::YamlPlugin; @@ -78,6 +79,7 @@ impl PluginManager { ("applescript".to_string(), Box::new(AppleScriptPlugin)), ("typescript".to_string(), Box::new(TypeScriptPlugin)), ("react".to_string(), Box::new(ReactPlugin)), + ("vue".to_string(), Box::new(VuePlugin)), ("cpp".to_string(), Box::new(CppPlugin)), ("groovy".to_string(), Box::new(GroovyPlugin)), ("html".to_string(), Box::new(HtmlPlugin)), diff --git a/src-tauri/src/plugins/mod.rs b/src-tauri/src/plugins/mod.rs index 4ac7a51..dc68982 100644 --- a/src-tauri/src/plugins/mod.rs +++ b/src-tauri/src/plugins/mod.rs @@ -443,6 +443,7 @@ pub mod tsv; pub mod typescript; pub mod typescript_browser; pub mod typescript_nodejs; +pub mod vue; pub mod xlsx; pub mod xml; pub mod yaml; diff --git a/src-tauri/src/plugins/vue.rs b/src-tauri/src/plugins/vue.rs new file mode 100644 index 0000000..7abafec --- /dev/null +++ b/src-tauri/src/plugins/vue.rs @@ -0,0 +1,61 @@ +use super::{LanguagePlugin, PluginConfig}; +use std::vec; + +pub struct VuePlugin; + +impl LanguagePlugin for VuePlugin { + fn get_order(&self) -> i32 { + 15 + } + + fn get_language_name(&self) -> &'static str { + "Vue" + } + + fn get_language_key(&self) -> &'static str { + "vue" + } + + fn get_file_extension(&self) -> String { + self.get_config() + .map(|config| config.extension.clone()) + .unwrap_or_else(|| "vue".to_string()) + } + + fn get_version_args(&self) -> Vec<&'static str> { + vec!["--"] + } + + fn get_path_command(&self) -> String { + // 浏览器内运行时预览,无需本机依赖(与 HTML 一致) + "--".to_string() + } + + fn get_default_config(&self) -> PluginConfig { + PluginConfig { + enabled: true, + language: String::from("vue"), + before_compile: None, + extension: String::from("vue"), + execute_home: None, + // 浏览器预览:CDN 引入 Vue 3 全局构建(含运行时模板编译器), + // 用户代码用 Vue.createApp(...).mount('#app') 直接挂载。 + run_command: Some(String::from( + "echo
\n\n", + )), + after_compile: None, + template: Some(String::from( + "const { createApp, ref } = Vue\n\ncreateApp({\n setup() {\n const msg = ref('Hello, Vue!')\n return { msg }\n },\n template: `

{{ msg }}

`\n}).mount('#app')", + )), + timeout: Some(30), + console_type: Some(String::from("web")), + icon_path: None, + } + } + + fn get_default_command(&self) -> String { + self.get_config() + .and_then(|config| config.run_command.clone()) + .unwrap_or_else(|| "echo".to_string()) + } +} diff --git a/src/composables/useCodeMirrorEditor.ts b/src/composables/useCodeMirrorEditor.ts index c51b6bc..13f61c2 100644 --- a/src/composables/useCodeMirrorEditor.ts +++ b/src/composables/useCodeMirrorEditor.ts @@ -328,6 +328,7 @@ export function useCodeMirrorEditor(props: Props) case 'javascript-jquery': case 'javascript-browser': case 'javascript-nodejs': + case 'vue': return javascript() case 'react': return javascript({jsx: true}) From 3ed40dfe7e380b5577d602f86f11cd5acd8a7af2 Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Wed, 17 Jun 2026 13:04:04 +0800 Subject: [PATCH 06/53] =?UTF-8?q?feat(chart):=20=E4=B8=AD=E5=9B=BD?= =?UTF-8?q?=E5=9C=B0=E5=9B=BE=E6=94=AF=E6=8C=81=E7=82=B9=E5=87=BB=E4=B8=8B?= =?UTF-8?q?=E9=92=BB=E5=88=B0=E7=9C=81/=E5=B8=82=20(#96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 点击带下级(childrenNum>0)的区域,按 adcode 加载下一级边界并切换地图 - 基础 china/world 仍走本地离线 json;省/市级按 adcode 联网取 DataV GeoAtlas, 会话内缓存已注册地图,离线或失败时 toast 提示并停留当前级 - 左上角「返回上级」按钮按面包屑逐级回退;切换 china↔world 自动重置层级 - 仅中国地图体系下钻(world 不下钻);新增 chart.mapBack/mapLoadFailed 文案 --- src/components/charts/MapChart.vue | 107 ++++++++++++++++++++++++----- src/i18n/locales/en.json | 2 + src/i18n/locales/zh-CN.json | 2 + 3 files changed, 93 insertions(+), 18 deletions(-) diff --git a/src/components/charts/MapChart.vue b/src/components/charts/MapChart.vue index 0f668fd..c4a390d 100644 --- a/src/components/charts/MapChart.vue +++ b/src/components/charts/MapChart.vue @@ -1,5 +1,12 @@ diff --git a/src/components/GitPanel.vue b/src/components/GitPanel.vue index 6f5b523..8759fc6 100644 --- a/src/components/GitPanel.vue +++ b/src/components/GitPanel.vue @@ -17,6 +17,9 @@
+ @@ -100,6 +103,9 @@
+ + + import {computed, h, onMounted, ref} from 'vue' import {invoke} from '@tauri-apps/api/core' -import {DownloadCloud, GitBranch, GitCompare, RefreshCw, Sparkles, Undo2, X} from 'lucide-vue-next' +import {DownloadCloud, GitBranch, GitCompare, History, RefreshCw, Sparkles, Undo2, X} from 'lucide-vue-next' import Button from '../ui/Button.vue' import Modal from '../ui/Modal.vue' import DiffView from './DiffView.vue' +import GitLog from './GitLog.vue' import {useToast} from '../plugins/toast' import {useI18n} from 'vue-i18n' import {useAiConfig} from '../composables/useAiConfig' @@ -144,6 +151,8 @@ const diffFile = ref<{ name: string; original: string; modified: string } | null // 丢弃改动确认 const showDiscard = ref(false) const discardTarget = ref(null) +// 提交历史 +const showLog = ref(false) // 文件视为已暂存:index 列非空且非未跟踪 const isStaged = (f: GitFile) => f.index !== ' ' && f.index !== '?' diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 153c014..0c72895 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -339,6 +339,12 @@ "discardFailed": "Discard failed", "cancel": "Cancel", "confirm": "Confirm", + "history": "Commit history", + "logEmpty": "No commits", + "loadMore": "Load more", + "allLoaded": "All commits loaded", + "logFailed": "Failed to load commit history", + "showFailed": "Failed to load commit details", "statusFailed": "Failed to read Git status", "stageFailed": "Stage failed", "unstageFailed": "Unstage failed", diff --git a/src/i18n/locales/zh-CN.json b/src/i18n/locales/zh-CN.json index a9b9695..da229d5 100644 --- a/src/i18n/locales/zh-CN.json +++ b/src/i18n/locales/zh-CN.json @@ -339,6 +339,12 @@ "discardFailed": "丢弃失败", "cancel": "取消", "confirm": "确定", + "history": "提交历史", + "logEmpty": "暂无提交", + "loadMore": "加载更多", + "allLoaded": "已加载全部提交", + "logFailed": "加载提交历史失败", + "showFailed": "加载提交详情失败", "statusFailed": "读取 Git 状态失败", "stageFailed": "暂存失败", "unstageFailed": "取消暂存失败", From 7e656fbba1d468db72addf57e13f5a4b6a4603d8 Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Wed, 17 Jun 2026 18:15:01 +0800 Subject: [PATCH 21/53] =?UTF-8?q?feat(git):=20=E6=96=B0=E5=A2=9E=20Stash?= =?UTF-8?q?=20=E5=82=A8=E8=97=8F=E7=AE=A1=E7=90=86=20(list/push/pop/drop)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端 git_stash_list/push(--include-untracked)/pop/drop 命令并注册 - 新增 GitStash.vue:储藏当前改动 + 列表(弹出/删除),pop 后通知 GitPanel 刷新 - GitPanel 头部加「储藏」按钮;新增 git.stash* 双语文案 --- src-tauri/src/filesystem.rs | 59 ++++++++++++++++++ src-tauri/src/main.rs | 10 ++- src/components/GitPanel.vue | 12 +++- src/components/GitStash.vue | 119 ++++++++++++++++++++++++++++++++++++ src/i18n/locales/en.json | 11 ++++ src/i18n/locales/zh-CN.json | 11 ++++ 6 files changed, 217 insertions(+), 5 deletions(-) create mode 100644 src/components/GitStash.vue diff --git a/src-tauri/src/filesystem.rs b/src-tauri/src/filesystem.rs index 227240f..04c62d8 100644 --- a/src-tauri/src/filesystem.rs +++ b/src-tauri/src/filesystem.rs @@ -647,6 +647,65 @@ pub async fn git_fetch(root: String) -> Result { .map_err(|e| format!("git 任务失败: {}", e))? } +#[derive(Serialize)] +pub struct GitStashEntry { + /// 形如 stash@{0} + reference: String, + message: String, +} + +/// 列出 stash 列表。 +#[tauri::command] +pub async fn git_stash_list(root: String) -> Result, String> { + tokio::task::spawn_blocking(move || { + let out = run_git(&root, &["stash", "list", "--pretty=format:%gd\x1f%s"])?; + let mut list = Vec::new(); + for line in out.lines() { + let p: Vec<&str> = line.split('\u{1f}').collect(); + if p.len() >= 2 { + list.push(GitStashEntry { + reference: p[0].to_string(), + message: p[1].to_string(), + }); + } + } + Ok(list) + }) + .await + .map_err(|e| format!("git 任务失败: {}", e))? +} + +/// 暂存当前改动(含未跟踪文件)。message 为空则用默认信息。 +#[tauri::command] +pub async fn git_stash_push(root: String, message: String) -> Result { + tokio::task::spawn_blocking(move || { + let mut args = vec!["stash", "push", "--include-untracked"]; + if !message.trim().is_empty() { + args.push("-m"); + args.push(message.as_str()); + } + run_git(&root, &args) + }) + .await + .map_err(|e| format!("git 任务失败: {}", e))? +} + +/// 应用并移除某个 stash(pop)。 +#[tauri::command] +pub async fn git_stash_pop(root: String, reference: String) -> Result { + tokio::task::spawn_blocking(move || run_git(&root, &["stash", "pop", &reference])) + .await + .map_err(|e| format!("git 任务失败: {}", e))? +} + +/// 丢弃某个 stash(drop)。 +#[tauri::command] +pub async fn git_stash_drop(root: String, reference: String) -> Result { + tokio::task::spawn_blocking(move || run_git(&root, &["stash", "drop", &reference])) + .await + .map_err(|e| format!("git 任务失败: {}", e))? +} + #[derive(Serialize)] pub struct GitBranches { current: String, diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index f9e3b19..673ea57 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -60,9 +60,9 @@ use crate::execution::{ use crate::filesystem::{ create_directory, create_file, delete_path, get_text_file_meta, git_branches, git_checkout, git_commit, git_diff, git_discard, git_fetch, git_file_head, git_log, git_pull, git_push, - git_show, git_stage, git_status, git_unstage, list_files, read_directory_tree, read_file_lines, - read_file_text, rename_path, replace_in_files, reveal_path, search_in_files, watch_directory, - write_file_text, + git_show, git_stage, git_stash_drop, git_stash_list, git_stash_pop, git_stash_push, git_status, + git_unstage, list_files, read_directory_tree, read_file_lines, read_file_text, rename_path, + replace_in_files, reveal_path, search_in_files, watch_directory, write_file_text, }; use crate::kv::{KvStore, kv_delete, kv_get_all, kv_set}; use crate::lsp::{ @@ -222,6 +222,10 @@ fn main() { git_file_head, git_log, git_show, + git_stash_list, + git_stash_push, + git_stash_pop, + git_stash_drop, // AI 助手 ai_chat, ai_chat_stream, diff --git a/src/components/GitPanel.vue b/src/components/GitPanel.vue index 8759fc6..97f65f8 100644 --- a/src/components/GitPanel.vue +++ b/src/components/GitPanel.vue @@ -17,6 +17,9 @@
+ @@ -103,6 +106,9 @@
+ + + @@ -119,11 +125,12 @@ diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 0c72895..4076561 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -345,6 +345,17 @@ "allLoaded": "All commits loaded", "logFailed": "Failed to load commit history", "showFailed": "Failed to load commit details", + "stash": "Stash", + "stashTitle": "Stash", + "stashPush": "Stash current changes", + "stashMessagePlaceholder": "Stash message (optional)", + "stashEmpty": "No stashes", + "stashPop": "Pop", + "stashDrop": "Drop", + "stashed": "Stashed", + "stashPopped": "Stash popped", + "stashDropped": "Stash dropped", + "stashFailed": "Stash operation failed", "statusFailed": "Failed to read Git status", "stageFailed": "Stage failed", "unstageFailed": "Unstage failed", diff --git a/src/i18n/locales/zh-CN.json b/src/i18n/locales/zh-CN.json index da229d5..b0ee7e3 100644 --- a/src/i18n/locales/zh-CN.json +++ b/src/i18n/locales/zh-CN.json @@ -345,6 +345,17 @@ "allLoaded": "已加载全部提交", "logFailed": "加载提交历史失败", "showFailed": "加载提交详情失败", + "stash": "储藏", + "stashTitle": "储藏 Stash", + "stashPush": "储藏当前改动", + "stashMessagePlaceholder": "储藏说明(可选)", + "stashEmpty": "暂无储藏", + "stashPop": "弹出", + "stashDrop": "删除", + "stashed": "已储藏", + "stashPopped": "已弹出储藏", + "stashDropped": "已删除储藏", + "stashFailed": "储藏操作失败", "statusFailed": "读取 Git 状态失败", "stageFailed": "暂存失败", "unstageFailed": "取消暂存失败", From 6b4ccae3c34524e58ebd370d561f0b5736577333 Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Wed, 17 Jun 2026 18:17:24 +0800 Subject: [PATCH 22/53] =?UTF-8?q?feat(git):=20=E5=88=86=E6=94=AF=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E6=94=AF=E6=8C=81=E6=96=B0=E5=BB=BA/=E5=88=A0?= =?UTF-8?q?=E9=99=A4/=E5=90=88=E5=B9=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端 git_branch_create(checkout -b)、git_branch_delete(branch -d)、git_merge(--no-edit) - GitPanel 头部加分支管理弹层:新建分支输入、逐分支「合并到当前/删除」、点击切换 - 新增 git.newBranch/mergeInto/deleteBranch 等双语文案 --- src-tauri/src/filesystem.rs | 24 ++++++++++ src-tauri/src/main.rs | 14 ++++-- src/components/GitPanel.vue | 96 ++++++++++++++++++++++++++++++++++++- src/i18n/locales/en.json | 10 ++++ src/i18n/locales/zh-CN.json | 10 ++++ 5 files changed, 148 insertions(+), 6 deletions(-) diff --git a/src-tauri/src/filesystem.rs b/src-tauri/src/filesystem.rs index 04c62d8..8a8e3da 100644 --- a/src-tauri/src/filesystem.rs +++ b/src-tauri/src/filesystem.rs @@ -739,6 +739,30 @@ pub async fn git_checkout(root: String, branch: String) -> Result Result { + tokio::task::spawn_blocking(move || run_git(&root, &["checkout", "-b", &name])) + .await + .map_err(|e| format!("git 任务失败: {}", e))? +} + +/// 删除分支(安全删除,未合并会失败)。 +#[tauri::command] +pub async fn git_branch_delete(root: String, name: String) -> Result { + tokio::task::spawn_blocking(move || run_git(&root, &["branch", "-d", &name])) + .await + .map_err(|e| format!("git 任务失败: {}", e))? +} + +/// 把指定分支合并到当前分支。 +#[tauri::command] +pub async fn git_merge(root: String, branch: String) -> Result { + tokio::task::spawn_blocking(move || run_git(&root, &["merge", "--no-edit", &branch])) + .await + .map_err(|e| format!("git 任务失败: {}", e))? +} + #[derive(Serialize)] pub struct GitCommit { hash: String, diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 673ea57..511d700 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -58,11 +58,12 @@ use crate::execution::{ stop_execution, }; use crate::filesystem::{ - create_directory, create_file, delete_path, get_text_file_meta, git_branches, git_checkout, - git_commit, git_diff, git_discard, git_fetch, git_file_head, git_log, git_pull, git_push, - git_show, git_stage, git_stash_drop, git_stash_list, git_stash_pop, git_stash_push, git_status, - git_unstage, list_files, read_directory_tree, read_file_lines, read_file_text, rename_path, - replace_in_files, reveal_path, search_in_files, watch_directory, write_file_text, + create_directory, create_file, delete_path, get_text_file_meta, git_branch_create, + git_branch_delete, git_branches, git_checkout, git_commit, git_diff, git_discard, git_fetch, + git_file_head, git_log, git_merge, git_pull, git_push, git_show, git_stage, git_stash_drop, + git_stash_list, git_stash_pop, git_stash_push, git_status, git_unstage, list_files, + read_directory_tree, read_file_lines, read_file_text, rename_path, replace_in_files, + reveal_path, search_in_files, watch_directory, write_file_text, }; use crate::kv::{KvStore, kv_delete, kv_get_all, kv_set}; use crate::lsp::{ @@ -219,6 +220,9 @@ fn main() { git_fetch, git_branches, git_checkout, + git_branch_create, + git_branch_delete, + git_merge, git_file_head, git_log, git_show, diff --git a/src/components/GitPanel.vue b/src/components/GitPanel.vue index 97f65f8..8db340a 100644 --- a/src/components/GitPanel.vue +++ b/src/components/GitPanel.vue @@ -15,7 +15,41 @@ ↑{{ status.ahead }} ↓{{ status.behind }} + + + +
+ +
{{ t('git.loadMore') }}…
{{ t('git.allLoaded') }}
@@ -41,6 +45,21 @@ + + + +
+

+ {{ resetTarget.short }} · {{ resetTarget.subject }} +

+

{{ t('git.resetHardWarn') }}

+
+ + + +
+
+
@@ -48,13 +67,15 @@ import {computed, onMounted, ref} from 'vue' import {invoke} from '@tauri-apps/api/core' import {History, X} from 'lucide-vue-next' +import Button from '../ui/Button.vue' +import Modal from '../ui/Modal.vue' import {useToast} from '../plugins/toast' import {useI18n} from 'vue-i18n' interface GitCommit { hash: string; short: string; author: string; date: string; subject: string } const props = defineProps<{ rootDir: string }>() -const emit = defineEmits<{ close: [] }>() +const emit = defineEmits<{ close: []; changed: [] }>() const toast = useToast() const {t} = useI18n() @@ -65,6 +86,7 @@ const loading = ref(false) const done = ref(false) const selected = ref(null) const patch = ref('') +const resetTarget = ref(null) const patchLines = computed(() => patch.value.split('\n')) @@ -116,6 +138,47 @@ const select = async (c: GitCommit) => { } } +// 操作后重新载入提交列表并选中首条 +const reloadAll = async () => { + commits.value = [] + done.value = false + patch.value = '' + selected.value = null + await loadMore() + if (commits.value.length) { + await select(commits.value[0]) + } +} + +const doRevert = async (c: GitCommit) => { + try { + await invoke('git_revert', {root: props.rootDir, hash: c.hash}) + toast.success(t('git.reverted')) + await reloadAll() + emit('changed') + } + catch (error) { + toast.error(t('git.revertFailed') + ': ' + error) + } +} + +const doReset = async (mode: 'soft' | 'mixed' | 'hard') => { + const c = resetTarget.value + resetTarget.value = null + if (!c) { + return + } + try { + await invoke('git_reset', {root: props.rootDir, hash: c.hash, mode}) + toast.success(t('git.resetDone')) + await reloadAll() + emit('changed') + } + catch (error) { + toast.error(t('git.resetFailed') + ': ' + error) + } +} + onMounted(async () => { await loadMore() if (commits.value.length) { diff --git a/src/components/GitPanel.vue b/src/components/GitPanel.vue index b3096a8..050e531 100644 --- a/src/components/GitPanel.vue +++ b/src/components/GitPanel.vue @@ -148,7 +148,7 @@ - + Date: Wed, 17 Jun 2026 18:28:49 +0800 Subject: [PATCH 26/53] =?UTF-8?q?feat(git):=20=E6=96=B0=E5=A2=9E=E6=A0=87?= =?UTF-8?q?=E7=AD=BE=E7=AE=A1=E7=90=86=20(=E5=88=9B=E5=BB=BA/=E5=88=97?= =?UTF-8?q?=E5=87=BA/=E5=88=A0=E9=99=A4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端 git_tags(按创建时间倒序)、git_tag_create(hash 为空打在 HEAD)、git_tag_delete - 新增 GitTags.vue:在 HEAD 打标签 + 列表删除;GitPanel 头部加「标签」按钮 - GitLog 每条提交加「标签」动作,弹窗输入名在该提交打标签 - 新增 git.tag/tagTitle/tagCreated 等双语文案 --- src-tauri/src/filesystem.rs | 37 ++++++++++++++ src-tauri/src/main.rs | 8 ++- src/components/GitLog.vue | 39 +++++++++++++++ src/components/GitPanel.vue | 12 ++++- src/components/GitTags.vue | 99 +++++++++++++++++++++++++++++++++++++ src/i18n/locales/en.json | 11 +++++ src/i18n/locales/zh-CN.json | 11 +++++ 7 files changed, 213 insertions(+), 4 deletions(-) create mode 100644 src/components/GitTags.vue diff --git a/src-tauri/src/filesystem.rs b/src-tauri/src/filesystem.rs index 86edcd4..a1e0ef5 100644 --- a/src-tauri/src/filesystem.rs +++ b/src-tauri/src/filesystem.rs @@ -817,6 +817,43 @@ pub async fn git_log(root: String, limit: u32, skip: u32) -> Result Result, String> { + tokio::task::spawn_blocking(move || { + let out = run_git(&root, &["tag", "--sort=-creatordate"])?; + Ok(out + .lines() + .map(|l| l.trim().to_string()) + .filter(|l| !l.is_empty()) + .collect()) + }) + .await + .map_err(|e| format!("git 任务失败: {}", e))? +} + +/// 创建标签。hash 为空则打在 HEAD。 +#[tauri::command] +pub async fn git_tag_create(root: String, name: String, hash: String) -> Result { + tokio::task::spawn_blocking(move || { + let mut args = vec!["tag", name.as_str()]; + if !hash.trim().is_empty() { + args.push(hash.as_str()); + } + run_git(&root, &args) + }) + .await + .map_err(|e| format!("git 任务失败: {}", e))? +} + +/// 删除标签。 +#[tauri::command] +pub async fn git_tag_delete(root: String, name: String) -> Result { + tokio::task::spawn_blocking(move || run_git(&root, &["tag", "-d", &name])) + .await + .map_err(|e| format!("git 任务失败: {}", e))? +} + /// 还原某次提交(生成一条反向提交,历史保留)。 #[tauri::command] pub async fn git_revert(root: String, hash: String) -> Result { diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index beb1981..23bdbea 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -62,8 +62,9 @@ use crate::filesystem::{ git_branch_delete, git_branches, git_checkout, git_commit, git_diff, git_discard, git_fetch, git_file_head, git_log, git_merge, git_pull, git_push, git_reset, git_revert, git_show, git_stage, git_stash_drop, git_stash_list, git_stash_pop, git_stash_push, git_status, - git_unstage, list_files, read_directory_tree, read_file_lines, read_file_text, rename_path, - replace_in_files, reveal_path, search_in_files, watch_directory, write_file_text, + git_tag_create, git_tag_delete, git_tags, git_unstage, list_files, read_directory_tree, + read_file_lines, read_file_text, rename_path, replace_in_files, reveal_path, search_in_files, + watch_directory, write_file_text, }; use crate::kv::{KvStore, kv_delete, kv_get_all, kv_set}; use crate::lsp::{ @@ -229,6 +230,9 @@ fn main() { git_show, git_revert, git_reset, + git_tags, + git_tag_create, + git_tag_delete, git_stash_list, git_stash_push, git_stash_pop, diff --git a/src/components/GitLog.vue b/src/components/GitLog.vue index 4cfd19a..2c926dd 100644 --- a/src/components/GitLog.vue +++ b/src/components/GitLog.vue @@ -29,6 +29,7 @@ @@ -46,6 +47,22 @@ + + +
+

+ {{ tagTarget.short }} · {{ tagTarget.subject }} +

+ +
+ +
+
+
+
@@ -87,6 +104,8 @@ const done = ref(false) const selected = ref(null) const patch = ref('') const resetTarget = ref(null) +const tagTarget = ref(null) +const tagName = ref('') const patchLines = computed(() => patch.value.split('\n')) @@ -179,6 +198,26 @@ const doReset = async (mode: 'soft' | 'mixed' | 'hard') => { } } +const openTag = (c: GitCommit) => { + tagTarget.value = c + tagName.value = '' +} + +const doTag = async () => { + const c = tagTarget.value + if (!c || !tagName.value.trim()) { + return + } + try { + await invoke('git_tag_create', {root: props.rootDir, name: tagName.value.trim(), hash: c.hash}) + toast.success(t('git.tagCreated')) + tagTarget.value = null + } + catch (error) { + toast.error(t('git.tagFailed') + ': ' + error) + } +} + onMounted(async () => { await loadMore() if (commits.value.length) { diff --git a/src/components/GitPanel.vue b/src/components/GitPanel.vue index 050e531..1cf5c47 100644 --- a/src/components/GitPanel.vue +++ b/src/components/GitPanel.vue @@ -51,6 +51,9 @@
+ @@ -147,6 +150,9 @@ + + + @@ -163,12 +169,13 @@ diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index d63b2b8..7cef380 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -381,6 +381,17 @@ "resetHardWarn": "Hard reset discards working tree and staged changes irreversibly.", "resetDone": "Reset done", "resetFailed": "Reset failed", + "tag": "Tags", + "tagTitle": "Tags", + "tagThis": "Tag this commit", + "tagHead": "Tag HEAD", + "tagNamePlaceholder": "Tag name (e.g. v1.0.0)", + "tagEmpty": "No tags", + "tagCreate": "Create", + "tagDelete": "Delete", + "tagCreated": "Tag created", + "tagDeleted": "Tag deleted", + "tagFailed": "Tag operation failed", "statusFailed": "Failed to read Git status", "stageFailed": "Stage failed", "unstageFailed": "Unstage failed", diff --git a/src/i18n/locales/zh-CN.json b/src/i18n/locales/zh-CN.json index deab9ed..988f666 100644 --- a/src/i18n/locales/zh-CN.json +++ b/src/i18n/locales/zh-CN.json @@ -381,6 +381,17 @@ "resetHardWarn": "硬重置会丢弃工作区与暂存区改动,不可恢复。", "resetDone": "已重置", "resetFailed": "重置失败", + "tag": "标签", + "tagTitle": "标签 Tag", + "tagThis": "在此提交打标签", + "tagHead": "在 HEAD 打标签", + "tagNamePlaceholder": "标签名(如 v1.0.0)", + "tagEmpty": "暂无标签", + "tagCreate": "创建", + "tagDelete": "删除", + "tagCreated": "已创建标签", + "tagDeleted": "已删除标签", + "tagFailed": "标签操作失败", "statusFailed": "读取 Git 状态失败", "stageFailed": "暂存失败", "unstageFailed": "取消暂存失败", From 756755c6f187a08cd6abf1356d67f6f7b5cb16f5 Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Wed, 17 Jun 2026 18:31:26 +0800 Subject: [PATCH 27/53] =?UTF-8?q?feat(git):=20=E6=96=B0=E5=A2=9E=E8=BF=9C?= =?UTF-8?q?=E7=A8=8B=E7=AE=A1=E7=90=86=E4=B8=8E=E8=AE=BE=E7=BD=AE=E4=B8=8A?= =?UTF-8?q?=E6=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端 git_remotes(解析 remote -v 的 fetch 行)、git_remote_add/remove、git_set_upstream - 新增 GitRemotes.vue:列表 + 添加/删除远程 + 为当前分支「设为上游」 - GitPanel 头部加「远程」按钮(传入当前分支);新增 git.remote* 双语文案 --- src-tauri/src/filesystem.rs | 61 +++++++++++++++++ src-tauri/src/main.rs | 14 ++-- src/components/GitPanel.vue | 10 ++- src/components/GitRemotes.vue | 120 ++++++++++++++++++++++++++++++++++ src/i18n/locales/en.json | 12 ++++ src/i18n/locales/zh-CN.json | 12 ++++ 6 files changed, 223 insertions(+), 6 deletions(-) create mode 100644 src/components/GitRemotes.vue diff --git a/src-tauri/src/filesystem.rs b/src-tauri/src/filesystem.rs index a1e0ef5..a04eea0 100644 --- a/src-tauri/src/filesystem.rs +++ b/src-tauri/src/filesystem.rs @@ -817,6 +817,67 @@ pub async fn git_log(root: String, limit: u32, skip: u32) -> Result Result, String> { + tokio::task::spawn_blocking(move || { + let out = run_git(&root, &["remote", "-v"])?; + let mut list: Vec = Vec::new(); + for line in out.lines() { + // 形如 "origin\tgit@...(fetch)",仅取 fetch 行 + if !line.contains("(fetch)") { + continue; + } + let mut it = line.split_whitespace(); + let name = it.next().unwrap_or("").to_string(); + let url = it.next().unwrap_or("").to_string(); + if !name.is_empty() { + list.push(GitRemote { name, url }); + } + } + Ok(list) + }) + .await + .map_err(|e| format!("git 任务失败: {}", e))? +} + +/// 添加远程。 +#[tauri::command] +pub async fn git_remote_add(root: String, name: String, url: String) -> Result { + tokio::task::spawn_blocking(move || run_git(&root, &["remote", "add", &name, &url])) + .await + .map_err(|e| format!("git 任务失败: {}", e))? +} + +/// 删除远程。 +#[tauri::command] +pub async fn git_remote_remove(root: String, name: String) -> Result { + tokio::task::spawn_blocking(move || run_git(&root, &["remote", "remove", &name])) + .await + .map_err(|e| format!("git 任务失败: {}", e))? +} + +/// 把当前分支的上游设为 /。 +#[tauri::command] +pub async fn git_set_upstream( + root: String, + remote: String, + branch: String, +) -> Result { + tokio::task::spawn_blocking(move || { + let target = format!("{}/{}", remote, branch); + run_git(&root, &["branch", &format!("--set-upstream-to={}", target)]) + }) + .await + .map_err(|e| format!("git 任务失败: {}", e))? +} + /// 列出标签(按创建时间倒序)。 #[tauri::command] pub async fn git_tags(root: String) -> Result, String> { diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 23bdbea..9e89c26 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -60,11 +60,11 @@ use crate::execution::{ use crate::filesystem::{ create_directory, create_file, delete_path, get_text_file_meta, git_blame, git_branch_create, git_branch_delete, git_branches, git_checkout, git_commit, git_diff, git_discard, git_fetch, - git_file_head, git_log, git_merge, git_pull, git_push, git_reset, git_revert, git_show, - git_stage, git_stash_drop, git_stash_list, git_stash_pop, git_stash_push, git_status, - git_tag_create, git_tag_delete, git_tags, git_unstage, list_files, read_directory_tree, - read_file_lines, read_file_text, rename_path, replace_in_files, reveal_path, search_in_files, - watch_directory, write_file_text, + git_file_head, git_log, git_merge, git_pull, git_push, git_remote_add, git_remote_remove, + git_remotes, git_reset, git_revert, git_set_upstream, git_show, git_stage, git_stash_drop, + git_stash_list, git_stash_pop, git_stash_push, git_status, git_tag_create, git_tag_delete, + git_tags, git_unstage, list_files, read_directory_tree, read_file_lines, read_file_text, + rename_path, replace_in_files, reveal_path, search_in_files, watch_directory, write_file_text, }; use crate::kv::{KvStore, kv_delete, kv_get_all, kv_set}; use crate::lsp::{ @@ -233,6 +233,10 @@ fn main() { git_tags, git_tag_create, git_tag_delete, + git_remotes, + git_remote_add, + git_remote_remove, + git_set_upstream, git_stash_list, git_stash_push, git_stash_pop, diff --git a/src/components/GitPanel.vue b/src/components/GitPanel.vue index 1cf5c47..2994b1d 100644 --- a/src/components/GitPanel.vue +++ b/src/components/GitPanel.vue @@ -51,6 +51,9 @@
+ @@ -153,6 +156,9 @@ + + + @@ -169,13 +175,14 @@ diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 7cef380..cb6bb04 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -392,6 +392,18 @@ "tagCreated": "Tag created", "tagDeleted": "Tag deleted", "tagFailed": "Tag operation failed", + "remote": "Remotes", + "remoteTitle": "Remotes", + "remoteEmpty": "No remotes", + "remoteNamePlaceholder": "Name (e.g. origin)", + "remoteUrlPlaceholder": "URL (git@… or https://…)", + "remoteAdd": "Add", + "remoteRemove": "Remove", + "setUpstream": "Set upstream", + "remoteAdded": "Remote added", + "remoteRemoved": "Remote removed", + "upstreamSet": "Upstream set", + "remoteFailed": "Remote operation failed", "statusFailed": "Failed to read Git status", "stageFailed": "Stage failed", "unstageFailed": "Unstage failed", diff --git a/src/i18n/locales/zh-CN.json b/src/i18n/locales/zh-CN.json index 988f666..41027d7 100644 --- a/src/i18n/locales/zh-CN.json +++ b/src/i18n/locales/zh-CN.json @@ -392,6 +392,18 @@ "tagCreated": "已创建标签", "tagDeleted": "已删除标签", "tagFailed": "标签操作失败", + "remote": "远程", + "remoteTitle": "远程仓库", + "remoteEmpty": "暂无远程", + "remoteNamePlaceholder": "名称(如 origin)", + "remoteUrlPlaceholder": "URL(git@… 或 https://…)", + "remoteAdd": "添加", + "remoteRemove": "删除", + "setUpstream": "设为上游", + "remoteAdded": "已添加远程", + "remoteRemoved": "已删除远程", + "upstreamSet": "已设置上游", + "remoteFailed": "远程操作失败", "statusFailed": "读取 Git 状态失败", "stageFailed": "暂存失败", "unstageFailed": "取消暂存失败", From c0880ff36a112efe41847a7a3349304750ea350c Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Wed, 17 Jun 2026 18:32:41 +0800 Subject: [PATCH 28/53] =?UTF-8?q?feat(git):=20=E8=AF=86=E5=88=AB=E5=86=B2?= =?UTF-8?q?=E7=AA=81=E6=96=87=E4=BB=B6=E5=B9=B6=E6=94=AF=E6=8C=81=E6=A0=87?= =?UTF-8?q?=E8=AE=B0=E5=B7=B2=E8=A7=A3=E5=86=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 识别未合并状态(任一侧 U,或 AA/DD),单列「冲突」分区,从暂存/更改区排除 - 每个冲突文件提供「标记已解决」(git add) 与打开;新增 git.conflicts/resolve/resolved 文案 --- src/components/GitPanel.vue | 30 +++++++++++++++++++++++++++--- src/i18n/locales/en.json | 3 +++ src/i18n/locales/zh-CN.json | 3 +++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/components/GitPanel.vue b/src/components/GitPanel.vue index 2994b1d..9f38e1b 100644 --- a/src/components/GitPanel.vue +++ b/src/components/GitPanel.vue @@ -78,6 +78,16 @@ + +
@@ -509,4 +514,31 @@ const gitFileHistory = () => { gitView.history = {rel, name: ctx.node.name} closeCtx() } + +const gitIgnore = async () => { + if (!ctx.node) { + return + } + try { + await invoke('git_ignore_add', {root: props.rootDir, pattern: relOf(ctx.node.path)}) + toast.success(t('git.ignored')) + emit('git-refresh') + } + catch (error) { + toast.error(t('git.ignoreFailed') + ': ' + error) + } + closeCtx() +} + +const gitInit = async () => { + try { + await invoke('git_init', {root: props.rootDir}) + toast.success(t('git.initDone')) + emit('git-refresh') + } + catch (error) { + toast.error(t('git.initFailed') + ': ' + error) + } + closeCtx() +} diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 26b2f22..58eb9b8 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -415,6 +415,12 @@ "unstaged": "Unstaged", "copyPath": "Copy path", "pathCopied": "Path copied", + "init": "Initialize Git repository", + "initDone": "Git repository initialized", + "initFailed": "Init failed", + "ignore": "Add to .gitignore", + "ignored": "Added to .gitignore", + "ignoreFailed": "Failed to add to .gitignore", "statusFailed": "Failed to read Git status", "stageFailed": "Stage failed", "unstageFailed": "Unstage failed", diff --git a/src/i18n/locales/zh-CN.json b/src/i18n/locales/zh-CN.json index b9c505c..ea3d922 100644 --- a/src/i18n/locales/zh-CN.json +++ b/src/i18n/locales/zh-CN.json @@ -415,6 +415,12 @@ "unstaged": "已取消暂存", "copyPath": "复制路径", "pathCopied": "已复制路径", + "init": "初始化 Git 仓库", + "initDone": "已初始化 Git 仓库", + "initFailed": "初始化失败", + "ignore": "加入 .gitignore", + "ignored": "已加入 .gitignore", + "ignoreFailed": "加入 .gitignore 失败", "statusFailed": "读取 Git 状态失败", "stageFailed": "暂存失败", "unstageFailed": "取消暂存失败", From 5ec709235037547a32c68bc12c887fa31cc46af4 Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Thu, 18 Jun 2026 09:25:45 +0800 Subject: [PATCH 33/53] =?UTF-8?q?feat(git):=20=E6=94=AF=E6=8C=81=E5=85=8B?= =?UTF-8?q?=E9=9A=86=E8=BF=9C=E7=A8=8B=E4=BB=93=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端 git_clone(克隆到所选目录,按 URL 推断仓库目录名并返回路径) - Sidebar 未打开文件夹时提供「克隆仓库」:输入 URL → 选目录 → 克隆 → 自动打开 - URL 占位符中的 @ 用 {'@'} 转义;新增 git.clone* 双语文案 --- src-tauri/src/filesystem.rs | 19 +++++++++++++++ src-tauri/src/main.rs | 15 ++++++------ src/components/Sidebar.vue | 46 ++++++++++++++++++++++++++++++++++++- src/i18n/locales/en.json | 7 ++++++ src/i18n/locales/zh-CN.json | 7 ++++++ 5 files changed, 86 insertions(+), 8 deletions(-) diff --git a/src-tauri/src/filesystem.rs b/src-tauri/src/filesystem.rs index 0d28a6f..8895074 100644 --- a/src-tauri/src/filesystem.rs +++ b/src-tauri/src/filesystem.rs @@ -471,6 +471,25 @@ pub async fn git_diff(root: String) -> Result { // ===== Git 源代码管理 ===== +/// 克隆远程仓库到 dir 下,返回克隆出的仓库目录路径。 +#[tauri::command] +pub async fn git_clone(url: String, dir: String) -> Result { + tokio::task::spawn_blocking(move || { + run_git(&dir, &["clone", &url])?; + // 由 URL 推断仓库目录名(去掉结尾 / 与 .git) + let name = url + .trim() + .trim_end_matches('/') + .rsplit('/') + .next() + .unwrap_or("repo") + .trim_end_matches(".git"); + Ok(format!("{}/{}", dir.trim_end_matches('/'), name)) + }) + .await + .map_err(|e| format!("git 任务失败: {}", e))? +} + /// 在目录初始化 Git 仓库。 #[tauri::command] pub async fn git_init(root: String) -> Result { diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 294912a..8ac34d5 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -59,13 +59,13 @@ use crate::execution::{ }; use crate::filesystem::{ create_directory, create_file, delete_path, get_text_file_meta, git_blame, git_branch_create, - git_branch_delete, git_branches, git_checkout, git_commit, git_diff, git_discard, git_fetch, - git_file_head, git_ignore_add, git_init, git_log, git_log_file, git_merge, git_pull, git_push, - git_remote_add, git_remote_remove, git_remotes, git_reset, git_revert, git_set_upstream, - git_show, git_stage, git_stash_drop, git_stash_list, git_stash_pop, git_stash_push, git_status, - git_tag_create, git_tag_delete, git_tags, git_unstage, list_files, read_directory_tree, - read_file_lines, read_file_text, rename_path, replace_in_files, reveal_path, search_in_files, - watch_directory, write_file_text, + git_branch_delete, git_branches, git_checkout, git_clone, git_commit, git_diff, git_discard, + git_fetch, git_file_head, git_ignore_add, git_init, git_log, git_log_file, git_merge, git_pull, + git_push, git_remote_add, git_remote_remove, git_remotes, git_reset, git_revert, + git_set_upstream, git_show, git_stage, git_stash_drop, git_stash_list, git_stash_pop, + git_stash_push, git_status, git_tag_create, git_tag_delete, git_tags, git_unstage, list_files, + read_directory_tree, read_file_lines, read_file_text, rename_path, replace_in_files, + reveal_path, search_in_files, watch_directory, write_file_text, }; use crate::kv::{KvStore, kv_delete, kv_get_all, kv_set}; use crate::lsp::{ @@ -241,6 +241,7 @@ fn main() { git_set_upstream, git_init, git_ignore_add, + git_clone, git_stash_list, git_stash_push, git_stash_pop, diff --git a/src/components/Sidebar.vue b/src/components/Sidebar.vue index 8a473fb..52174cf 100644 --- a/src/components/Sidebar.vue +++ b/src/components/Sidebar.vue @@ -21,7 +21,10 @@

{{ t('sidebar.noFolder') }}

- +
+ + +
@@ -127,6 +130,17 @@
+ + +
+ +
+ + +
+
+
+
@@ -145,6 +159,7 @@ diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 58eb9b8..f1bac9d 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -421,6 +421,13 @@ "ignore": "Add to .gitignore", "ignored": "Added to .gitignore", "ignoreFailed": "Failed to add to .gitignore", + "clone": "Clone repository", + "cloneTitle": "Clone Git repository", + "cloneUrlPlaceholder": "Repo URL (git{'@'}… or https://…)", + "clonePick": "Choose folder & clone", + "cloning": "Cloning…", + "cloned": "Cloned", + "cloneFailed": "Clone failed", "statusFailed": "Failed to read Git status", "stageFailed": "Stage failed", "unstageFailed": "Unstage failed", diff --git a/src/i18n/locales/zh-CN.json b/src/i18n/locales/zh-CN.json index ea3d922..c33880a 100644 --- a/src/i18n/locales/zh-CN.json +++ b/src/i18n/locales/zh-CN.json @@ -421,6 +421,13 @@ "ignore": "加入 .gitignore", "ignored": "已加入 .gitignore", "ignoreFailed": "加入 .gitignore 失败", + "clone": "克隆仓库", + "cloneTitle": "克隆 Git 仓库", + "cloneUrlPlaceholder": "仓库 URL(git{'@'}… 或 https://…)", + "clonePick": "选择目录并克隆", + "cloning": "克隆中…", + "cloned": "已克隆", + "cloneFailed": "克隆失败", "statusFailed": "读取 Git 状态失败", "stageFailed": "暂存失败", "unstageFailed": "取消暂存失败", From 102379305f94a4a2e4dafad65ae6812aa2cf6ec3 Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Thu, 18 Jun 2026 09:54:31 +0800 Subject: [PATCH 34/53] =?UTF-8?q?fix(chart):=20=E4=BF=AE=E5=A4=8D=E5=9C=B0?= =?UTF-8?q?=E5=9B=BE=E7=82=B9=E5=87=BB=E5=A4=84=E7=90=86=E5=99=A8=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E5=AF=BC=E8=87=B4=20vue-tsc=20=E6=9E=84=E5=BB=BA?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit echarts .on('click') 类型要求处理器返回 boolean|void,但下钻用的是 async(返回 Promise)。 包一层 (params)=>{ void onMapClick(params) } 触发并忽略 Promise,修复 pnpm build 的 TS2345。 --- src/components/charts/MapChart.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/charts/MapChart.vue b/src/components/charts/MapChart.vue index 9f559ee..cb843cf 100644 --- a/src/components/charts/MapChart.vue +++ b/src/components/charts/MapChart.vue @@ -145,7 +145,8 @@ onMounted(() => { return } chart = echarts.init(el.value, undefined, {renderer: 'canvas'}) - chart.on('click', onMapClick) + // 包一层避免把 async 处理器(返回 Promise)直接传给 echarts(其类型要求返回 boolean|void) + chart.on('click', (params: any) => { void onMapClick(params) }) render() ro = new ResizeObserver(() => chart?.resize()) ro.observe(el.value) From 91f03564392ec23080a3fe80897c14f6c17b5b3e Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Thu, 18 Jun 2026 10:00:17 +0800 Subject: [PATCH 35/53] =?UTF-8?q?feat(git):=20=E6=94=AF=E6=8C=81=E9=87=8D?= =?UTF-8?q?=E5=91=BD=E5=90=8D=E5=88=86=E6=94=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端 git_branch_rename(branch -m old new) - GitPanel 分支弹层每个分支加重命名按钮 + 输入弹窗,含当前分支 - 新增 git.renameBranch/branchRenamed 双语文案 --- src-tauri/src/filesystem.rs | 8 ++++++++ src-tauri/src/main.rs | 15 +++++++------- src/components/GitPanel.vue | 41 ++++++++++++++++++++++++++++++++++++- src/i18n/locales/en.json | 3 +++ src/i18n/locales/zh-CN.json | 3 +++ 5 files changed, 62 insertions(+), 8 deletions(-) diff --git a/src-tauri/src/filesystem.rs b/src-tauri/src/filesystem.rs index 8895074..ab22dbf 100644 --- a/src-tauri/src/filesystem.rs +++ b/src-tauri/src/filesystem.rs @@ -817,6 +817,14 @@ pub async fn git_branch_delete(root: String, name: String) -> Result Result { + tokio::task::spawn_blocking(move || run_git(&root, &["branch", "-m", &old, &new])) + .await + .map_err(|e| format!("git 任务失败: {}", e))? +} + /// 把指定分支合并到当前分支。 #[tauri::command] pub async fn git_merge(root: String, branch: String) -> Result { diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 8ac34d5..b5aba0b 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -59,13 +59,13 @@ use crate::execution::{ }; use crate::filesystem::{ create_directory, create_file, delete_path, get_text_file_meta, git_blame, git_branch_create, - git_branch_delete, git_branches, git_checkout, git_clone, git_commit, git_diff, git_discard, - git_fetch, git_file_head, git_ignore_add, git_init, git_log, git_log_file, git_merge, git_pull, - git_push, git_remote_add, git_remote_remove, git_remotes, git_reset, git_revert, - git_set_upstream, git_show, git_stage, git_stash_drop, git_stash_list, git_stash_pop, - git_stash_push, git_status, git_tag_create, git_tag_delete, git_tags, git_unstage, list_files, - read_directory_tree, read_file_lines, read_file_text, rename_path, replace_in_files, - reveal_path, search_in_files, watch_directory, write_file_text, + git_branch_delete, git_branch_rename, git_branches, git_checkout, git_clone, git_commit, + git_diff, git_discard, git_fetch, git_file_head, git_ignore_add, git_init, git_log, + git_log_file, git_merge, git_pull, git_push, git_remote_add, git_remote_remove, git_remotes, + git_reset, git_revert, git_set_upstream, git_show, git_stage, git_stash_drop, git_stash_list, + git_stash_pop, git_stash_push, git_status, git_tag_create, git_tag_delete, git_tags, + git_unstage, list_files, read_directory_tree, read_file_lines, read_file_text, rename_path, + replace_in_files, reveal_path, search_in_files, watch_directory, write_file_text, }; use crate::kv::{KvStore, kv_delete, kv_get_all, kv_set}; use crate::lsp::{ @@ -224,6 +224,7 @@ fn main() { git_checkout, git_branch_create, git_branch_delete, + git_branch_rename, git_merge, git_blame, git_file_head, diff --git a/src/components/GitPanel.vue b/src/components/GitPanel.vue index 9f38e1b..2d10682 100644 --- a/src/components/GitPanel.vue +++ b/src/components/GitPanel.vue @@ -38,6 +38,9 @@ {{ b }} +