Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
5e048b8
feat(workspace): 切换文件时自动在文件树中定位(可开关)
qianmoQ Jun 26, 2026
428e114
feat(tabs): 支持置顶标签 (Pin Tab)
qianmoQ Jun 26, 2026
636b9fd
feat(editor): 状态栏新增缩进指示,点击可快速切换空格/Tab 与缩进宽度
qianmoQ Jun 26, 2026
f78ebb0
feat(git): AI 生成提交信息改用暂存 diff,更贴合实际提交内容
qianmoQ Jun 26, 2026
15cd416
feat(git): 复制当前行的远程仓库永久链接
qianmoQ Jun 26, 2026
0495a2f
fix(editor): 右键菜单按真实尺寸夹取到视口内,修复贴底/贴右裁切
qianmoQ Jun 27, 2026
1a9a87a
feat(editor): 新增缩进参考线(可开关)
qianmoQ Jun 27, 2026
51e428c
feat(git): 远程永久链接支持在浏览器打开,并加入编辑器右键菜单
qianmoQ Jun 27, 2026
8ba7917
feat(git): 状态栏显示当前分支,点击打开 Git 面板
qianmoQ Jun 27, 2026
5a519bb
feat(editor): 新增文本变换命令——排序行(升/降)、转大写/小写
qianmoQ Jun 27, 2026
17a0ad3
feat(editor): 文本命令补充——删除重复行、去除行尾空白
qianmoQ Jun 28, 2026
9fb9db1
feat(editor): 新增专注模式,隐藏工具栏/输入区/侧栏/状态栏沉浸编辑
qianmoQ Jun 28, 2026
4dcc19d
style(rust): cargo fmt 统一格式(git_permalink 等)
qianmoQ Jun 28, 2026
acf7905
fix(ci): docs 锁文件重建为 pnpm 10 格式(lockfileVersion 9.0)
qianmoQ Jun 28, 2026
2cce136
refactor(app): 文本变换命令抽离为 useTextCommands composable
qianmoQ Jun 28, 2026
94eb8cd
refactor(app): 远程永久链接逻辑抽离为 useGitPermalink composable
qianmoQ Jun 28, 2026
2a674e2
refactor(app): 文件树定位逻辑抽离为 useRevealInTree composable
qianmoQ Jun 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,069 changes: 1,691 additions & 1,378 deletions docs/pnpm-lock.yaml

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@codemirror/state": "^6.5.2",
"@codemirror/view": "^6.38.1",
"@open-rpc/client-js": "^2.0.0",
"@replit/codemirror-indentation-markers": "^6.5.3",
"@replit/codemirror-minimap": "^0.5.2",
"@tauri-apps/api": "^2",
"@tauri-apps/plugin-dialog": "^2.3.2",
Expand Down
4 changes: 4 additions & 0 deletions src-tauri/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub struct EditorConfig {
pub show_minimap: Option<bool>, // 是否显示代码缩略图
pub show_sticky_scroll: Option<bool>, // 是否启用粘性滚动
pub word_wrap: Option<bool>, // 是否自动换行
pub show_indent_guides: Option<bool>, // 是否显示缩进参考线
pub layout: Option<String>, // 编辑器/控制台布局: horizontal | vertical | editor
pub last_direction: Option<String>, // 仅编辑器模式下控制台弹出方向: horizontal | vertical
pub max_open_file_size: Option<u32>, // 打开文件大小上限(MB),超过则拒绝打开
Expand Down Expand Up @@ -75,6 +76,7 @@ impl Default for AppConfig {
show_minimap: Some(false),
show_sticky_scroll: Some(false),
word_wrap: Some(false),
show_indent_guides: Some(false),
layout: Some("horizontal".to_string()),
last_direction: Some("horizontal".to_string()),
max_open_file_size: Some(5),
Expand Down Expand Up @@ -148,6 +150,7 @@ impl ConfigManager {
show_minimap: Some(false),
show_sticky_scroll: Some(false),
word_wrap: Some(false),
show_indent_guides: Some(false),
layout: Some("horizontal".to_string()),
last_direction: Some("horizontal".to_string()),
max_open_file_size: Some(5),
Expand Down Expand Up @@ -274,6 +277,7 @@ impl ConfigManager {
show_minimap: Some(false),
show_sticky_scroll: Some(false),
word_wrap: Some(false),
show_indent_guides: Some(false),
layout: Some("horizontal".to_string()),
last_direction: Some("horizontal".to_string()),
max_open_file_size: Some(5),
Expand Down
80 changes: 80 additions & 0 deletions src-tauri/src/filesystem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,86 @@ pub async fn git_diff(root: String) -> Result<String, String> {
.map_err(|e| format!("git 任务失败: {}", e))?
}

/// 已暂存改动的 diff(git diff --cached),用于 AI 生成提交信息
#[tauri::command]
pub async fn git_staged_diff(root: String) -> Result<String, String> {
tokio::task::spawn_blocking(move || {
let output = std::process::Command::new("git")
.args(["-C", &root, "diff", "--cached"])
.output()
.map_err(|e| format!("执行 git 失败: {}", e))?;
if !output.status.success() {
let err = String::from_utf8_lossy(&output.stderr);
return Err(format!("git diff --cached 失败:{}", err.trim()));
}
let mut diff = String::from_utf8_lossy(&output.stdout).to_string();
if diff.len() > MAX_DIFF_LEN {
diff.truncate(MAX_DIFF_LEN);
diff.push_str("\n…(diff 过长已截断)");
}
Ok(diff)
})
.await
.map_err(|e| format!("git 任务失败: {}", e))?
}

/// 生成当前文件指定行的远程仓库永久链接(基于 origin 远程与当前 HEAD 提交)。
/// 支持 GitHub / Gitee(/blob/)与 GitLab(/-/blob/),SSH 与 HTTPS 远程均可解析。
#[tauri::command]
pub async fn git_permalink(root: String, rel_path: String, line: u32) -> Result<String, String> {
tokio::task::spawn_blocking(move || {
let git = |args: &[&str]| -> Result<String, String> {
let mut a: Vec<&str> = vec!["-C", &root];
a.extend_from_slice(args);
let out = std::process::Command::new("git")
.args(&a)
.output()
.map_err(|e| format!("执行 git 失败: {}", e))?;
if !out.status.success() {
return Err(String::from_utf8_lossy(&out.stderr).trim().to_string());
}
Ok(String::from_utf8_lossy(&out.stdout).trim().to_string())
};

let remote =
git(&["remote", "get-url", "origin"]).map_err(|_| "未找到 origin 远程".to_string())?;
let sha = git(&["rev-parse", "HEAD"]).map_err(|_| "无法获取当前提交".to_string())?;

// 解析远程地址 → (host, owner/repo)
let raw = remote.trim();
let raw = raw.strip_suffix(".git").unwrap_or(raw);
let (host, path) = if let Some(rest) = raw.strip_prefix("git@") {
// scp 形式:git@host:owner/repo
rest.split_once(':')
.map(|(h, p)| (h.to_string(), p.trim_start_matches('/').to_string()))
.ok_or_else(|| "无法解析远程地址".to_string())?
} else {
let rest = raw
.strip_prefix("https://")
.or_else(|| raw.strip_prefix("http://"))
.or_else(|| raw.strip_prefix("ssh://"))
.ok_or_else(|| "无法解析远程地址".to_string())?;
let rest = rest.strip_prefix("git@").unwrap_or(rest);
rest.split_once('/')
.map(|(h, p)| (h.to_string(), p.trim_start_matches('/').to_string()))
.ok_or_else(|| "无法解析远程地址".to_string())?
};

let rel = rel_path.trim_start_matches(['/', '\\']).replace('\\', "/");
let blob = if host.contains("gitlab") {
"/-/blob/"
} else {
"/blob/"
};
Ok(format!(
"https://{}/{}{}{}/{}#L{}",
host, path, blob, sha, rel, line
))
})
.await
.map_err(|e| format!("git 任务失败: {}", e))?
}

// ===== Git 源代码管理 =====

/// 克隆远程仓库到 dir 下,返回克隆出的仓库目录路径。
Expand Down
20 changes: 11 additions & 9 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,15 @@ use crate::filesystem::{
git_file_diff, git_file_head, git_get_identity, git_get_signing, git_graph, git_hook_delete,
git_hook_read, git_hook_save, git_hooks, git_ignore_add, git_ignore_append_block, git_init,
git_log, git_log_file, git_merge, git_op_abort, git_op_continue, git_op_skip, git_op_state,
git_pull, git_pull_rebase, git_push, git_push_force, git_push_tags, git_rebase_interactive,
git_reflog, git_remote_add, git_remote_branches, git_remote_remove, git_remotes, git_reset,
git_restore_file, git_revert, git_set_identity, git_set_signing, git_set_upstream, git_show,
git_stage, git_stash_apply, git_stash_drop, git_stash_list, git_stash_pop, git_stash_push,
git_stash_show, git_status, git_submodule_sync, git_submodule_update, git_submodules,
git_tag_create, git_tag_delete, git_tags, git_unstage, git_worktree_add, git_worktree_prune,
git_worktree_remove, git_worktrees, list_files, read_directory_tree, read_file_lines,
read_file_text, rename_path, replace_in_files, resolve_editorconfig, reveal_path,
search_in_files, watch_directory, write_file_text,
git_permalink, git_pull, git_pull_rebase, git_push, git_push_force, git_push_tags,
git_rebase_interactive, git_reflog, git_remote_add, git_remote_branches, git_remote_remove,
git_remotes, git_reset, git_restore_file, git_revert, git_set_identity, git_set_signing,
git_set_upstream, git_show, git_stage, git_staged_diff, git_stash_apply, git_stash_drop,
git_stash_list, git_stash_pop, git_stash_push, git_stash_show, git_status, git_submodule_sync,
git_submodule_update, git_submodules, git_tag_create, git_tag_delete, git_tags, git_unstage,
git_worktree_add, git_worktree_prune, git_worktree_remove, git_worktrees, list_files,
read_directory_tree, read_file_lines, read_file_text, rename_path, replace_in_files,
resolve_editorconfig, reveal_path, search_in_files, watch_directory, write_file_text,
};
use crate::gitignore_templates::{
GitignoreStore, gitignore_template_delete, gitignore_template_save, gitignore_templates_list,
Expand Down Expand Up @@ -232,6 +232,8 @@ fn main() {
search_in_files,
replace_in_files,
git_diff,
git_staged_diff,
git_permalink,
git_file_diff,
git_apply_patch,
git_status,
Expand Down
Loading
Loading