From e3784fb3f339bba91650275dc59cb951720d0c0d Mon Sep 17 00:00:00 2001 From: xintaofei Date: Wed, 18 Mar 2026 22:59:12 +0800 Subject: [PATCH 1/8] =?UTF-8?q?=E4=BD=BF=E7=94=A8rust=20which=E5=8C=85?= =?UTF-8?q?=E6=9D=A5=E6=9F=A5=E6=89=BE=E5=91=BD=E4=BB=A4=E8=B7=AF=E5=BE=84?= =?UTF-8?q?=EF=BC=8C=E8=A7=A3=E5=86=B3=E9=83=A8=E5=88=86=E7=94=B5=E8=84=91?= =?UTF-8?q?=E7=8E=AF=E5=A2=83=E5=8F=98=E9=87=8F=E8=AF=86=E5=88=AB=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/Cargo.lock | 27 ++++++++++++++++++++++++++- src-tauri/Cargo.toml | 1 + src-tauri/src/acp/connection.rs | 10 +++++++--- src-tauri/src/acp/preflight.rs | 24 +++++++++++++++++------- src-tauri/src/commands/acp.rs | 20 ++++---------------- 5 files changed, 55 insertions(+), 27 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index fcb1c4b..8673cff 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -409,7 +409,7 @@ dependencies = [ "rustc-hash", "shlex", "syn 2.0.114", - "which", + "which 4.4.2", ] [[package]] @@ -827,6 +827,7 @@ dependencies = [ "urlencoding", "uuid", "walkdir", + "which 7.0.3", "windows-sys 0.59.0", "zip 2.4.2", ] @@ -1422,6 +1423,12 @@ dependencies = [ "syn 2.0.114", ] +[[package]] +name = "env_home" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" + [[package]] name = "equivalent" version = "1.0.2" @@ -7185,6 +7192,18 @@ dependencies = [ "rustix 0.38.44", ] +[[package]] +name = "which" +version = "7.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762" +dependencies = [ + "either", + "env_home", + "rustix 1.1.3", + "winsafe", +] + [[package]] name = "whoami" version = "1.6.1" @@ -7762,6 +7781,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + [[package]] name = "wit-bindgen" version = "0.51.0" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 5eda92d..ecd00bf 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -48,6 +48,7 @@ notify = "6" base64 = "0.22" agent-client-protocol-schema = { version = "0.10", features = ["unstable_session_usage", "unstable_session_fork"] } kill_tree = { version = "0.2", features = ["tokio"] } +which = "7" [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] tauri-plugin-window-state = "2" diff --git a/src-tauri/src/acp/connection.rs b/src-tauri/src/acp/connection.rs index b8c779c..9c91447 100644 --- a/src-tauri/src/acp/connection.rs +++ b/src-tauri/src/acp/connection.rs @@ -131,9 +131,13 @@ async fn build_agent( parts.push(format!("{k}={v}")); } parts.push( - crate::process::normalized_program(cmd) - .to_string_lossy() - .to_string(), + which::which(cmd) + .map(|p| p.to_string_lossy().to_string()) + .unwrap_or_else(|_| { + crate::process::normalized_program(cmd) + .to_string_lossy() + .to_string() + }), ); for a in args { parts.push((*a).into()); diff --git a/src-tauri/src/acp/preflight.rs b/src-tauri/src/acp/preflight.rs index 45ee429..4890978 100644 --- a/src-tauri/src/acp/preflight.rs +++ b/src-tauri/src/acp/preflight.rs @@ -93,14 +93,24 @@ async fn check_npm_environment(node_required: Option<&str>) -> Vec { return checks; } - // Run node and npm checks in parallel + // Resolve absolute paths via `which` crate to avoid GUI PATH issues, + // then run version checks in parallel. + let node_path = which::which("node").ok(); + let npm_path = which::which("npm").ok(); + let (node_result, npm_result) = tokio::join!( - crate::process::tokio_command("node") - .arg("--version") - .output(), - crate::process::tokio_command("npm") - .arg("--version") - .output(), + async { + match &node_path { + Some(p) => crate::process::tokio_command(p).arg("--version").output().await, + None => Err(std::io::Error::new(std::io::ErrorKind::NotFound, "node not found in PATH")), + } + }, + async { + match &npm_path { + Some(p) => crate::process::tokio_command(p).arg("--version").output().await, + None => Err(std::io::Error::new(std::io::ErrorKind::NotFound, "npm not found in PATH")), + } + }, ); // Track the raw node version string for reuse in the version check diff --git a/src-tauri/src/commands/acp.rs b/src-tauri/src/commands/acp.rs index e7afba2..ae6231e 100644 --- a/src-tauri/src/commands/acp.rs +++ b/src-tauri/src/commands/acp.rs @@ -81,27 +81,15 @@ fn package_name_from_spec(package: &str) -> String { /// Check whether a command is available on the system PATH. /// Uses `which` on unix and `where` on windows — lightweight and does not /// invoke the target binary itself, avoiding side-effects or slow startups. -async fn is_cmd_available(cmd: &str) -> bool { - #[cfg(unix)] - let check_cmd = "which"; - #[cfg(windows)] - let check_cmd = "where"; - - crate::process::tokio_command(check_cmd) - .arg(cmd) - .stdout(std::process::Stdio::null()) - .stderr(std::process::Stdio::null()) - .status() - .await - .map(|s| s.success()) - .unwrap_or(false) +fn is_cmd_available(cmd: &str) -> bool { + which::which(cmd).is_ok() } async fn detect_local_version(agent_type: AgentType) -> Option { let meta = registry::get_agent_meta(agent_type); match meta.distribution { registry::AgentDistribution::Npx { cmd, package, .. } => { - if is_cmd_available(cmd).await { + if is_cmd_available(cmd) { version_from_package_spec(package) } else { None @@ -1047,7 +1035,7 @@ pub async fn acp_connect( } if let registry::AgentDistribution::Npx { cmd, .. } = meta.distribution { - if !is_cmd_available(cmd).await { + if !is_cmd_available(cmd) { return Err(AcpError::protocol(format!( "{} SDK is not installed. Please install it in Agent Settings.", meta.name From f2919210864dc5c1b4bd2e9934c2c9f7f049b063 Mon Sep 17 00:00:00 2001 From: xintaofei Date: Wed, 18 Mar 2026 23:13:34 +0800 Subject: [PATCH 2/8] =?UTF-8?q?=E5=8D=87=E7=BA=A7claude=20code=E7=9A=84sdk?= =?UTF-8?q?=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/src/acp/registry.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src-tauri/src/acp/registry.rs b/src-tauri/src/acp/registry.rs index e5a87d7..2491d7c 100644 --- a/src-tauri/src/acp/registry.rs +++ b/src-tauri/src/acp/registry.rs @@ -113,8 +113,8 @@ pub fn get_agent_meta(agent_type: AgentType) -> AcpAgentMeta { name: "Claude Code", description: "ACP wrapper for Anthropic's Claude", distribution: AgentDistribution::Npx { - version: "0.22.1", - package: "@zed-industries/claude-agent-acp@0.22.1", + version: "0.22.2", + package: "@zed-industries/claude-agent-acp@0.22.2", cmd: "claude-agent-acp", args: &[], env: &[], From 00825f2a3998d647571fae33098d296557ecb970 Mon Sep 17 00:00:00 2001 From: xintaofei Date: Wed, 18 Mar 2026 23:13:58 +0800 Subject: [PATCH 3/8] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dnpx=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E7=9A=84agent=E7=9A=84=E6=9C=AC=E5=9C=B0=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E6=A3=80=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/src/commands/acp.rs | 38 +++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/src-tauri/src/commands/acp.rs b/src-tauri/src/commands/acp.rs index ae6231e..145b9d9 100644 --- a/src-tauri/src/commands/acp.rs +++ b/src-tauri/src/commands/acp.rs @@ -85,15 +85,45 @@ fn is_cmd_available(cmd: &str) -> bool { which::which(cmd).is_ok() } +/// Detect the actual installed version of an npm global package by running +/// `npm list -g --json` and parsing the JSON output. +async fn detect_npm_global_version(package_name: &str) -> Option { + let npm_path = which::which("npm").ok()?; + let output = crate::process::tokio_command(npm_path) + .arg("list") + .arg("-g") + .arg(package_name) + .arg("--json") + .arg("--depth=0") + .output() + .await + .ok()?; + // npm list --json may exit non-zero when package is missing, but still + // outputs valid JSON with an empty dependencies object. + let stdout = String::from_utf8_lossy(&output.stdout); + let json: serde_json::Value = serde_json::from_str(&stdout).ok()?; + let version = json + .get("dependencies")? + .get(package_name)? + .get("version")? + .as_str()?; + normalize_version_candidate(version) +} + async fn detect_local_version(agent_type: AgentType) -> Option { let meta = registry::get_agent_meta(agent_type); match meta.distribution { registry::AgentDistribution::Npx { cmd, package, .. } => { - if is_cmd_available(cmd) { - version_from_package_spec(package) - } else { - None + if !is_cmd_available(cmd) { + return None; } + // Try `npm list -g --json` to get the real installed version. + let pkg_name = package_name_from_spec(package); + if let Some(v) = detect_npm_global_version(&pkg_name).await { + return Some(v); + } + // Fallback: parse version from registry package spec + version_from_package_spec(package) } registry::AgentDistribution::Binary { cmd, .. } => { binary_cache::detect_installed_version(agent_type, cmd) From f3acb45cb0d5f3f7788c20816c4d31c2d19b430b Mon Sep 17 00:00:00 2001 From: xintaofei Date: Wed, 18 Mar 2026 23:21:19 +0800 Subject: [PATCH 4/8] Release version 0.2.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 使用rust which包来查找命令路径,解决部分电脑环境变量识别问题; 升级claude code的sdk版本; 修复npx类型的agent的本地版本检测。 --- package.json | 2 +- src-tauri/Cargo.lock | 2 +- src-tauri/Cargo.toml | 2 +- src-tauri/tauri.conf.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 71965f4..512b55d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "codeg", "private": true, - "version": "0.2.1", + "version": "0.2.2", "scripts": { "dev": "next dev --turbopack", "build": "next build", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 8673cff..f365ec4 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -792,7 +792,7 @@ checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" [[package]] name = "codeg" -version = "0.2.1" +version = "0.2.2" dependencies = [ "agent-client-protocol-schema", "base64 0.22.1", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index ecd00bf..815ff89 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "codeg" -version = "0.2.1" +version = "0.2.2" description = "Agent Code Generation App" authors = ["feitao"] edition = "2021" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 73861a7..41cb794 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "codeg", - "version": "0.2.1", + "version": "0.2.2", "identifier": "app.codeg", "build": { "beforeDevCommand": "pnpm dev", From 33d70b886629f78d746124c68368aca06dd1408a Mon Sep 17 00:00:00 2001 From: xintaofei Date: Thu, 19 Mar 2026 00:24:50 +0800 Subject: [PATCH 5/8] =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=92=8C=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../conversations/search-command-dialog.tsx | 362 +++++++++++++++--- .../layout/aux-panel-file-tree-tab.tsx | 21 +- src/components/layout/aux-panel.tsx | 10 +- src/components/ui/command.tsx | 11 +- src/contexts/aux-panel-context.tsx | 31 +- src/i18n/messages/ar.json | 6 +- src/i18n/messages/de.json | 6 +- src/i18n/messages/en.json | 6 +- src/i18n/messages/es.json | 6 +- src/i18n/messages/fr.json | 6 +- src/i18n/messages/ja.json | 6 +- src/i18n/messages/ko.json | 6 +- src/i18n/messages/pt.json | 6 +- src/i18n/messages/zh-CN.json | 6 +- src/i18n/messages/zh-TW.json | 6 +- 15 files changed, 432 insertions(+), 63 deletions(-) diff --git a/src/components/conversations/search-command-dialog.tsx b/src/components/conversations/search-command-dialog.tsx index 4e87f7b..8caa601 100644 --- a/src/components/conversations/search-command-dialog.tsx +++ b/src/components/conversations/search-command-dialog.tsx @@ -1,16 +1,25 @@ "use client" -import { useState, useEffect, useRef, useCallback } from "react" +import { useState, useEffect, useRef, useCallback, useMemo } from "react" import { formatDistanceToNow } from "date-fns" import { enUS, zhCN, zhTW } from "date-fns/locale" +import { File, Folder } from "lucide-react" +import ig from "ignore" import { useLocale, useTranslations } from "next-intl" +import { useAuxPanelContext } from "@/contexts/aux-panel-context" import { useFolderContext } from "@/contexts/folder-context" import { useTabContext } from "@/contexts/tab-context" -import { listFolderConversations } from "@/lib/tauri" +import { useWorkspaceContext } from "@/contexts/workspace-context" +import { + getFileTree, + listFolderConversations, + readFilePreview, +} from "@/lib/tauri" import type { AgentType, ConversationStatus, DbConversationSummary, + FileTreeNode, } from "@/lib/types" import { AGENT_LABELS, STATUS_COLORS, compareAgentType } from "@/lib/types" import { AgentIcon } from "@/components/agent-icon" @@ -24,6 +33,51 @@ import { } from "@/components/ui/command" import { cn } from "@/lib/utils" +type SearchTab = "conversations" | "files" + +interface FlatFileEntry { + name: string + /** Relative path from folder root (same as FileTreeNode.path) */ + relativePath: string + kind: "file" | "dir" + /** Pre-computed lowercase relativePath for filtering */ + lowerPath: string + /** Pre-computed lowercase name for filtering */ + lowerName: string +} + +function flattenTree(nodes: FileTreeNode[]): FlatFileEntry[] { + const entries: FlatFileEntry[] = [] + function walk(node: FileTreeNode) { + entries.push({ + name: node.name, + relativePath: node.path, + kind: node.kind, + lowerPath: node.path.toLowerCase(), + lowerName: node.name.toLowerCase(), + }) + if (node.kind === "dir" && node.children) { + for (const child of node.children) { + walk(child) + } + } + } + for (const node of nodes) { + walk(node) + } + return entries +} + +/** Check whether any ancestor directory of `path` is in `ignoredDirs`. */ +function hasIgnoredAncestor(path: string, ignoredDirs: Set): boolean { + let idx = path.indexOf("/") + while (idx !== -1) { + if (ignoredDirs.has(path.slice(0, idx))) return true + idx = path.indexOf("/", idx + 1) + } + return false +} + interface SearchCommandDialogProps { open: boolean onOpenChange: (open: boolean) => void @@ -37,20 +91,125 @@ export function SearchCommandDialog({ const locale = useLocale() const dateFnsLocale = locale === "zh-CN" ? zhCN : locale === "zh-TW" ? zhTW : enUS - const { folderId, conversations } = useFolderContext() + const { folderId, folder, conversations } = useFolderContext() const { openTab } = useTabContext() + const { openFilePreview } = useWorkspaceContext() + const { revealInFileTree } = useAuxPanelContext() + const [activeTab, setActiveTab] = useState("conversations") const [query, setQuery] = useState("") const [agentFilter, setAgentFilter] = useState(null) const [results, setResults] = useState([]) const [searching, setSearching] = useState(false) const debounceRef = useRef>(undefined) + // File search state + const [allFiles, setAllFiles] = useState([]) + const [filesLoading, setFilesLoading] = useState(false) + const filesLoadedRef = useRef(false) + + const folderPath = folder?.path ?? "" + // Compute which agent types exist in current folder const availableAgents = Array.from( new Set(conversations.map((c) => c.agent_type)) ).sort(compareAgentType) + // Load file tree when switching to files tab, filtering by .gitignore + useEffect(() => { + if (activeTab !== "files" || !folderPath || filesLoadedRef.current) return + let canceled = false + setFilesLoading(true) + + async function load() { + try { + const tree = await getFileTree(folderPath, 10) + const flat = flattenTree(tree) + + // Collect all .gitignore files from the tree + const gitignoreEntries = flat.filter( + (f) => f.kind === "file" && f.name === ".gitignore" + ) + + // Build matchers keyed by directory prefix + const matchers: { prefix: string; matcher: ReturnType }[] = + [] + await Promise.all( + gitignoreEntries.map(async (entry) => { + try { + const result = await readFilePreview( + folderPath, + entry.relativePath + ) + const lastSlash = entry.relativePath.lastIndexOf("/") + const dir = lastSlash === -1 ? "" : entry.relativePath.slice(0, lastSlash) + matchers.push({ + prefix: dir ? dir + "/" : "", + matcher: ig().add(result.content), + }) + } catch { + // skip unreadable .gitignore + } + }) + ) + + // Sort matchers by prefix length (shortest/root first) so that + // parent rules are evaluated before child rules. + matchers.sort((a, b) => a.prefix.length - b.prefix.length) + + // Filter: check each entry against all applicable .gitignore matchers + const ignoredDirs = new Set() + const filtered = flat.filter((f) => { + // Skip .gitignore files themselves from results + if (f.name === ".gitignore") return false + // If an ancestor directory is already ignored, skip — O(depth) lookup + if (hasIgnoredAncestor(f.relativePath, ignoredDirs)) return false + for (const { prefix, matcher } of matchers) { + if (!f.relativePath.startsWith(prefix)) continue + const relPath = f.relativePath.slice(prefix.length) + if (!relPath) continue + const testPath = + f.kind === "dir" ? `${relPath}/` : relPath + if (matcher.ignores(testPath)) { + if (f.kind === "dir") ignoredDirs.add(f.relativePath) + return false + } + } + return true + }) + + if (!canceled) { + setAllFiles(filtered) + filesLoadedRef.current = true + } + } catch { + if (!canceled) setAllFiles([]) + } finally { + if (!canceled) setFilesLoading(false) + } + } + + void load() + return () => { + canceled = true + } + }, [activeTab, folderPath]) + + // Filter files by query using pre-computed lowercase fields + const filteredFiles = useMemo(() => { + const trimmed = query.trim() + if (!trimmed) return allFiles.slice(0, 100) + const lower = trimmed.toLowerCase() + const matched: FlatFileEntry[] = [] + for (const f of allFiles) { + if (f.lowerName.includes(lower) || f.lowerPath.includes(lower)) { + matched.push(f) + if (matched.length >= 100) break + } + } + return matched + }, [allFiles, query]) + const doSearch = useCallback( async (q: string, agent: AgentType | null) => { if (!q.trim() && !agent) { @@ -75,8 +234,9 @@ export function SearchCommandDialog({ [folderId] ) - // Debounced search on query change + // Debounced search on query change (conversations tab only) useEffect(() => { + if (activeTab !== "conversations") return if (debounceRef.current) clearTimeout(debounceRef.current) debounceRef.current = setTimeout(() => { doSearch(query, agentFilter) @@ -84,7 +244,7 @@ export function SearchCommandDialog({ return () => { if (debounceRef.current) clearTimeout(debounceRef.current) } - }, [query, agentFilter, doSearch]) + }, [query, agentFilter, doSearch, activeTab]) // Reset state when dialog closes useEffect(() => { @@ -92,26 +252,89 @@ export function SearchCommandDialog({ setQuery("") setAgentFilter(null) setResults([]) + setActiveTab("conversations") + filesLoadedRef.current = false + setAllFiles([]) } }, [open]) - const handleSelect = (conv: DbConversationSummary) => { - openTab(conv.id, conv.agent_type, true) - onOpenChange(false) - } + const handleSelectConversation = useCallback( + (conv: DbConversationSummary) => { + openTab(conv.id, conv.agent_type, true) + onOpenChange(false) + }, + [openTab, onOpenChange] + ) + + const handleSelectFile = useCallback( + (entry: FlatFileEntry) => { + if (entry.kind === "dir") { + revealInFileTree(entry.relativePath) + } else { + // Reveal parent directory in file tree, then open the file + const lastSlash = entry.relativePath.lastIndexOf("/") + if (lastSlash > 0) { + revealInFileTree(entry.relativePath.slice(0, lastSlash)) + } + openFilePreview(entry.relativePath) + } + onOpenChange(false) + }, + [revealInFileTree, openFilePreview, onOpenChange] + ) + + const placeholder = + activeTab === "conversations" + ? t("placeholder") + : t("filePlaceholder") return ( + {/* Tabs */} +
+ + +
+ - {availableAgents.length > 1 && ( + + {/* Agent filter (conversations tab only) */} + {activeTab === "conversations" && availableAgents.length > 1 && (
)} + - - {searching - ? t("searching") - : !query.trim() && !agentFilter - ? t("typeToSearch") - : t("noResults")} - - {results.length > 0 && ( - - {results.map((conv) => ( - handleSelect(conv)} - > - - - {conv.title || t("untitledConversation")} - - - {AGENT_LABELS[conv.agent_type]} - - - {formatDistanceToNow(new Date(conv.created_at), { - addSuffix: true, - locale: dateFnsLocale, - })} - - - ))} - + {/* Conversations tab */} + {activeTab === "conversations" && ( + <> + + {searching + ? t("searching") + : !query.trim() && !agentFilter + ? t("typeToSearch") + : t("noResults")} + + {results.length > 0 && ( + + {results.map((conv) => ( + handleSelectConversation(conv)} + > + + + {conv.title || t("untitledConversation")} + + + {AGENT_LABELS[conv.agent_type]} + + + {formatDistanceToNow(new Date(conv.created_at), { + addSuffix: true, + locale: dateFnsLocale, + })} + + + ))} + + )} + + )} + + {/* Files tab */} + {activeTab === "files" && ( + <> + + {filesLoading + ? t("searching") + : !query.trim() + ? t("typeToSearchFiles") + : t("noResults")} + + {filteredFiles.length > 0 && ( + + {filteredFiles.map((entry) => ( + handleSelectFile(entry)} + > + {entry.kind === "dir" ? ( + + ) : ( + + )} + + {entry.name} + + + {entry.relativePath} + + + ))} + + )} + )}
diff --git a/src/components/layout/aux-panel-file-tree-tab.tsx b/src/components/layout/aux-panel-file-tree-tab.tsx index 2b14017..3f94295 100644 --- a/src/components/layout/aux-panel-file-tree-tab.tsx +++ b/src/components/layout/aux-panel-file-tree-tab.tsx @@ -744,7 +744,8 @@ function RenderNode({ export function FileTreeTab() { const t = useTranslations("Folder.fileTreeTab") const tCommon = useTranslations("Folder.common") - const { activeTab } = useAuxPanelContext() + const { activeTab, pendingRevealPath, consumePendingRevealPath } = + useAuxPanelContext() const { folder } = useFolderContext() const { tabs, activeTabId } = useTabContext() const { createTerminalInDirectory } = useTerminalContext() @@ -857,6 +858,24 @@ export function FileTreeTab() { externalConflictSignatureByPathRef.current.clear() }, [folder?.path]) + // Handle pending reveal path: expand all ancestor directories once tree is loaded + const hasNodes = nodes.length > 0 + useEffect(() => { + if (!pendingRevealPath || !hasNodes) return + consumePendingRevealPath() + setExpandedPaths((prev) => { + const next = new Set(prev) + next.add(FILE_TREE_ROOT_PATH) + let idx = pendingRevealPath.indexOf("/") + while (idx !== -1) { + next.add(pendingRevealPath.slice(0, idx)) + idx = pendingRevealPath.indexOf("/", idx + 1) + } + next.add(pendingRevealPath) + return next + }) + }, [pendingRevealPath, consumePendingRevealPath, hasNodes]) + useEffect(() => { if (!activeFileTab || activeFileTab.kind !== "file") return if (!activeFileTab.path) return diff --git a/src/components/layout/aux-panel.tsx b/src/components/layout/aux-panel.tsx index 21db2d8..7858410 100644 --- a/src/components/layout/aux-panel.tsx +++ b/src/components/layout/aux-panel.tsx @@ -1,6 +1,6 @@ "use client" -import { useCallback, useState } from "react" +import { useCallback, useEffect, useState } from "react" import { FileDiff, Folder, FolderPen, GitCommit } from "lucide-react" import { useTranslations } from "next-intl" import { @@ -26,6 +26,14 @@ export function AuxPanel() { activeTab === "git_log" ) + // Sync mount flags when activeTab changes programmatically (e.g. revealInFileTree) + useEffect(() => { + if (!isOpen) return + if (activeTab === "file_tree") setHasMountedFileTree(true) + else if (activeTab === "changes") setHasMountedChanges(true) + else if (activeTab === "git_log") setHasMountedGitLog(true) + }, [isOpen, activeTab]) + const handleTabValueChange = useCallback( (value: string) => { const nextTab = value as AuxPanelTab diff --git a/src/components/ui/command.tsx b/src/components/ui/command.tsx index 8239f36..b52b7d4 100644 --- a/src/components/ui/command.tsx +++ b/src/components/ui/command.tsx @@ -26,15 +26,22 @@ function Command({ function CommandDialog({ title = "Command", children, + shouldFilter, ...props -}: React.ComponentProps & { title?: string }) { +}: React.ComponentProps & { + title?: string + shouldFilter?: boolean +}) { return ( {title} - + {children} diff --git a/src/contexts/aux-panel-context.tsx b/src/contexts/aux-panel-context.tsx index cf2926d..74de057 100644 --- a/src/contexts/aux-panel-context.tsx +++ b/src/contexts/aux-panel-context.tsx @@ -31,6 +31,9 @@ interface AuxPanelContextValue { setWidth: (w: number) => void setActiveTab: (tab: AuxPanelTab) => void openTab: (tab: AuxPanelTab) => void + pendingRevealPath: string | null + revealInFileTree: (path: string) => void + consumePendingRevealPath: () => void } const AuxPanelContext = createContext(null) @@ -64,6 +67,9 @@ export function AuxPanelProvider({ const [width, setWidthState] = useState(DEFAULT_WIDTH) const [restored, setRestored] = useState(false) const [activeTab, setActiveTab] = useState("session_files") + const [pendingRevealPath, setPendingRevealPath] = useState( + null + ) const toggle = useCallback(() => setIsOpen((prev) => !prev), []) @@ -76,6 +82,16 @@ export function AuxPanelProvider({ setIsOpen(true) }, []) + const revealInFileTree = useCallback((path: string) => { + setPendingRevealPath(path) + setActiveTab("file_tree") + setIsOpen(true) + }, []) + + const consumePendingRevealPath = useCallback(() => { + setPendingRevealPath(null) + }, []) + useEffect(() => { const stored = loadPersistedPanelState(storageKey) // Hydrate from localStorage after mount to keep SSR/CSR markup consistent. @@ -101,8 +117,21 @@ export function AuxPanelProvider({ setWidth, setActiveTab, openTab, + pendingRevealPath, + revealInFileTree, + consumePendingRevealPath, }), - [isOpen, width, activeTab, toggle, setWidth, openTab] + [ + isOpen, + width, + activeTab, + toggle, + setWidth, + openTab, + pendingRevealPath, + revealInFileTree, + consumePendingRevealPath, + ] ) return ( diff --git a/src/i18n/messages/ar.json b/src/i18n/messages/ar.json index 7cbd224..cf5e02b 100644 --- a/src/i18n/messages/ar.json +++ b/src/i18n/messages/ar.json @@ -648,11 +648,15 @@ "save": "حفظ" }, "search": { - "dialogTitle": "البحث في المحادثات", + "dialogTitle": "بحث", + "tabConversations": "المحادثات", + "tabFiles": "الملفات", "placeholder": "البحث في المحادثات...", + "filePlaceholder": "البحث في الملفات أو المجلدات...", "allAgents": "الكل", "searching": "جارٍ البحث...", "typeToSearch": "اكتب للبحث في المحادثات", + "typeToSearchFiles": "اكتب للبحث في الملفات أو المجلدات", "noResults": "لم يتم العثور على نتائج.", "untitledConversation": "محادثة بدون عنوان" }, diff --git a/src/i18n/messages/de.json b/src/i18n/messages/de.json index 32d3553..b0ce5c2 100644 --- a/src/i18n/messages/de.json +++ b/src/i18n/messages/de.json @@ -648,11 +648,15 @@ "save": "Speichern" }, "search": { - "dialogTitle": "Konversationen suchen", + "dialogTitle": "Suchen", + "tabConversations": "Konversationen", + "tabFiles": "Dateien", "placeholder": "Konversationen suchen...", + "filePlaceholder": "Dateien oder Verzeichnisse suchen...", "allAgents": "Alle", "searching": "Suche...", "typeToSearch": "Tippen, um Konversationen zu suchen", + "typeToSearchFiles": "Tippen, um Dateien oder Verzeichnisse zu suchen", "noResults": "Keine Ergebnisse gefunden.", "untitledConversation": "Unbenannte Konversation" }, diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index 062cba2..f862749 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -648,11 +648,15 @@ "save": "Save" }, "search": { - "dialogTitle": "Search conversations", + "dialogTitle": "Search", + "tabConversations": "Conversations", + "tabFiles": "Files", "placeholder": "Search conversations...", + "filePlaceholder": "Search files or directories...", "allAgents": "All", "searching": "Searching...", "typeToSearch": "Type to search conversations", + "typeToSearchFiles": "Type to search files or directories", "noResults": "No results found.", "untitledConversation": "Untitled conversation" }, diff --git a/src/i18n/messages/es.json b/src/i18n/messages/es.json index 75af880..46ae9cc 100644 --- a/src/i18n/messages/es.json +++ b/src/i18n/messages/es.json @@ -648,11 +648,15 @@ "save": "Guardar" }, "search": { - "dialogTitle": "Buscar conversaciones", + "dialogTitle": "Buscar", + "tabConversations": "Conversaciones", + "tabFiles": "Archivos", "placeholder": "Buscar conversaciones...", + "filePlaceholder": "Buscar archivos o directorios...", "allAgents": "Todo", "searching": "Buscando...", "typeToSearch": "Escribe para buscar conversaciones", + "typeToSearchFiles": "Escribe para buscar archivos o directorios", "noResults": "No se encontraron resultados.", "untitledConversation": "Conversación sin título" }, diff --git a/src/i18n/messages/fr.json b/src/i18n/messages/fr.json index 433ca77..29588c1 100644 --- a/src/i18n/messages/fr.json +++ b/src/i18n/messages/fr.json @@ -648,11 +648,15 @@ "save": "Enregistrer" }, "search": { - "dialogTitle": "Rechercher des conversations", + "dialogTitle": "Rechercher", + "tabConversations": "Conversations", + "tabFiles": "Fichiers", "placeholder": "Rechercher des conversations...", + "filePlaceholder": "Rechercher des fichiers ou répertoires...", "allAgents": "Tout", "searching": "Recherche...", "typeToSearch": "Tapez pour rechercher des conversations", + "typeToSearchFiles": "Tapez pour rechercher des fichiers ou répertoires", "noResults": "Aucun résultat trouvé.", "untitledConversation": "Conversation sans titre" }, diff --git a/src/i18n/messages/ja.json b/src/i18n/messages/ja.json index fb3d574..9678d07 100644 --- a/src/i18n/messages/ja.json +++ b/src/i18n/messages/ja.json @@ -648,11 +648,15 @@ "save": "保存" }, "search": { - "dialogTitle": "会話を検索", + "dialogTitle": "検索", + "tabConversations": "会話", + "tabFiles": "ファイル", "placeholder": "会話を検索...", + "filePlaceholder": "ファイルまたはディレクトリを検索...", "allAgents": "すべて", "searching": "検索中...", "typeToSearch": "入力して会話を検索", + "typeToSearchFiles": "入力してファイルまたはディレクトリを検索", "noResults": "結果が見つかりません。", "untitledConversation": "無題の会話" }, diff --git a/src/i18n/messages/ko.json b/src/i18n/messages/ko.json index 562cc63..e56259a 100644 --- a/src/i18n/messages/ko.json +++ b/src/i18n/messages/ko.json @@ -648,11 +648,15 @@ "save": "저장" }, "search": { - "dialogTitle": "대화 검색", + "dialogTitle": "검색", + "tabConversations": "대화", + "tabFiles": "파일", "placeholder": "대화 검색...", + "filePlaceholder": "파일 또는 디렉토리 검색...", "allAgents": "전체", "searching": "검색 중...", "typeToSearch": "입력하여 대화를 검색하세요", + "typeToSearchFiles": "입력하여 파일 또는 디렉토리를 검색하세요", "noResults": "검색 결과가 없습니다.", "untitledConversation": "제목 없는 대화" }, diff --git a/src/i18n/messages/pt.json b/src/i18n/messages/pt.json index 4de8e41..ae6a578 100644 --- a/src/i18n/messages/pt.json +++ b/src/i18n/messages/pt.json @@ -648,11 +648,15 @@ "save": "Salvar" }, "search": { - "dialogTitle": "Buscar conversas", + "dialogTitle": "Buscar", + "tabConversations": "Conversas", + "tabFiles": "Arquivos", "placeholder": "Buscar conversas...", + "filePlaceholder": "Buscar arquivos ou diretórios...", "allAgents": "Todos", "searching": "Buscando...", "typeToSearch": "Digite para buscar conversas", + "typeToSearchFiles": "Digite para buscar arquivos ou diretórios", "noResults": "Nenhum resultado encontrado.", "untitledConversation": "Conversa sem título" }, diff --git a/src/i18n/messages/zh-CN.json b/src/i18n/messages/zh-CN.json index a52e923..2d5c288 100644 --- a/src/i18n/messages/zh-CN.json +++ b/src/i18n/messages/zh-CN.json @@ -648,11 +648,15 @@ "save": "保存" }, "search": { - "dialogTitle": "搜索会话", + "dialogTitle": "搜索", + "tabConversations": "会话", + "tabFiles": "文件", "placeholder": "搜索会话...", + "filePlaceholder": "搜索文件或目录...", "allAgents": "全部", "searching": "搜索中...", "typeToSearch": "输入关键词搜索会话", + "typeToSearchFiles": "输入关键词搜索文件或目录", "noResults": "未找到结果。", "untitledConversation": "未命名会话" }, diff --git a/src/i18n/messages/zh-TW.json b/src/i18n/messages/zh-TW.json index 226c5bd..3025fd0 100644 --- a/src/i18n/messages/zh-TW.json +++ b/src/i18n/messages/zh-TW.json @@ -648,11 +648,15 @@ "save": "儲存" }, "search": { - "dialogTitle": "搜尋會話", + "dialogTitle": "搜尋", + "tabConversations": "會話", + "tabFiles": "檔案", "placeholder": "搜尋會話...", + "filePlaceholder": "搜尋檔案或目錄...", "allAgents": "全部", "searching": "搜尋中...", "typeToSearch": "輸入關鍵字搜尋會話", + "typeToSearchFiles": "輸入關鍵字搜尋檔案或目錄", "noResults": "找不到結果。", "untitledConversation": "未命名會話" }, From dc461c1c18e7e74433924fdf9a1ca0f1c36d67fd Mon Sep 17 00:00:00 2001 From: xintaofei Date: Thu, 19 Mar 2026 00:35:36 +0800 Subject: [PATCH 6/8] =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E7=BB=93=E6=9E=9C=E9=87=8C=E5=AF=B9=E7=9B=AE=E5=BD=95=E5=9B=BE?= =?UTF-8?q?=E6=A0=87=E7=9D=80=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../conversations/search-command-dialog.tsx | 21 ++--- .../layout/aux-panel-file-tree-tab.tsx | 8 +- src/components/layout/aux-panel.tsx | 4 +- .../settings/acp-agent-settings.tsx | 85 +++++++++---------- 4 files changed, 59 insertions(+), 59 deletions(-) diff --git a/src/components/conversations/search-command-dialog.tsx b/src/components/conversations/search-command-dialog.tsx index 8caa601..e3a01e5 100644 --- a/src/components/conversations/search-command-dialog.tsx +++ b/src/components/conversations/search-command-dialog.tsx @@ -142,7 +142,8 @@ export function SearchCommandDialog({ entry.relativePath ) const lastSlash = entry.relativePath.lastIndexOf("/") - const dir = lastSlash === -1 ? "" : entry.relativePath.slice(0, lastSlash) + const dir = + lastSlash === -1 ? "" : entry.relativePath.slice(0, lastSlash) matchers.push({ prefix: dir ? dir + "/" : "", matcher: ig().add(result.content), @@ -168,8 +169,7 @@ export function SearchCommandDialog({ if (!f.relativePath.startsWith(prefix)) continue const relPath = f.relativePath.slice(prefix.length) if (!relPath) continue - const testPath = - f.kind === "dir" ? `${relPath}/` : relPath + const testPath = f.kind === "dir" ? `${relPath}/` : relPath if (matcher.ignores(testPath)) { if (f.kind === "dir") ignoredDirs.add(f.relativePath) return false @@ -284,9 +284,7 @@ export function SearchCommandDialog({ ) const placeholder = - activeTab === "conversations" - ? t("placeholder") - : t("filePlaceholder") + activeTab === "conversations" ? t("placeholder") : t("filePlaceholder") return ( @@ -430,13 +427,11 @@ export function SearchCommandDialog({ onSelect={() => handleSelectFile(entry)} > {entry.kind === "dir" ? ( - + ) : ( )} - - {entry.name} - + {entry.name} {entry.relativePath} diff --git a/src/components/layout/aux-panel-file-tree-tab.tsx b/src/components/layout/aux-panel-file-tree-tab.tsx index 3f94295..09de0f0 100644 --- a/src/components/layout/aux-panel-file-tree-tab.tsx +++ b/src/components/layout/aux-panel-file-tree-tab.tsx @@ -2321,7 +2321,9 @@ export function FileTreeTab() { { e.preventDefault() - const input = (e.currentTarget as HTMLElement | null)?.querySelector("input") + const input = ( + e.currentTarget as HTMLElement | null + )?.querySelector("input") if (input) requestAnimationFrame(() => input.focus()) }} > @@ -2388,7 +2390,9 @@ export function FileTreeTab() { { e.preventDefault() - const input = (e.currentTarget as HTMLElement | null)?.querySelector("input") + const input = ( + e.currentTarget as HTMLElement | null + )?.querySelector("input") if (input) requestAnimationFrame(() => input.focus()) }} > diff --git a/src/components/layout/aux-panel.tsx b/src/components/layout/aux-panel.tsx index 7858410..35c5d5c 100644 --- a/src/components/layout/aux-panel.tsx +++ b/src/components/layout/aux-panel.tsx @@ -26,13 +26,15 @@ export function AuxPanel() { activeTab === "git_log" ) - // Sync mount flags when activeTab changes programmatically (e.g. revealInFileTree) + // Sync mount flags when activeTab changes programmatically (e.g. revealInFileTree). + /* eslint-disable react-hooks/set-state-in-effect */ useEffect(() => { if (!isOpen) return if (activeTab === "file_tree") setHasMountedFileTree(true) else if (activeTab === "changes") setHasMountedChanges(true) else if (activeTab === "git_log") setHasMountedGitLog(true) }, [isOpen, activeTab]) + /* eslint-enable react-hooks/set-state-in-effect */ const handleTabValueChange = useCallback( (value: string) => { diff --git a/src/components/settings/acp-agent-settings.tsx b/src/components/settings/acp-agent-settings.tsx index 4f2a54d..7a4becd 100644 --- a/src/components/settings/acp-agent-settings.tsx +++ b/src/components/settings/acp-agent-settings.tsx @@ -2040,10 +2040,7 @@ function hasComparableVersion( } function buildVersionCheck(agent: AcpAgentInfo): UiCheckItem | null { - if ( - agent.distribution_type !== "binary" && - agent.distribution_type !== "npx" - ) + if (agent.distribution_type !== "binary" && agent.distribution_type !== "npx") return null const remoteVersion = agent.registry_version ?? "unknown" @@ -2390,46 +2387,51 @@ export function AcpAgentSettings() { const runPreflight = useCallback( async (agentType: AgentType, forceRefresh?: boolean) => { - setChecking((prev) => ({ ...prev, [agentType]: true })) - try { - const [resultState, versionState] = await Promise.allSettled([ - acpPreflight(agentType, forceRefresh), - acpDetectAgentLocalVersion(agentType), - ]) + setChecking((prev) => ({ ...prev, [agentType]: true })) + try { + const [resultState, versionState] = await Promise.allSettled([ + acpPreflight(agentType, forceRefresh), + acpDetectAgentLocalVersion(agentType), + ]) - if (versionState.status === "fulfilled") { - setAgents((prev) => { - if (versionState.value === null) return prev - let changed = false - const next = prev.map((agent) => { - if (agent.agent_type !== agentType) return agent - if (agent.installed_version === versionState.value) return agent - changed = true - return { ...agent, installed_version: versionState.value } + if (versionState.status === "fulfilled") { + setAgents((prev) => { + if (versionState.value === null) return prev + let changed = false + const next = prev.map((agent) => { + if (agent.agent_type !== agentType) return agent + if (agent.installed_version === versionState.value) return agent + changed = true + return { ...agent, installed_version: versionState.value } + }) + return changed ? next : prev }) - return changed ? next : prev - }) - } + } - if (resultState.status === "fulfilled") { - setCheckState((prev) => ({ - ...prev, - [agentType]: { result: resultState.value }, - })) - } else { - const message = - resultState.reason instanceof Error - ? resultState.reason.message - : String(resultState.reason) + if (resultState.status === "fulfilled") { + setCheckState((prev) => ({ + ...prev, + [agentType]: { result: resultState.value }, + })) + } else { + const message = + resultState.reason instanceof Error + ? resultState.reason.message + : String(resultState.reason) + setCheckState((prev) => ({ + ...prev, + [agentType]: { error: message }, + })) + } + } catch (err) { + const message = err instanceof Error ? err.message : String(err) setCheckState((prev) => ({ ...prev, [agentType]: { error: message } })) + } finally { + setChecking((prev) => ({ ...prev, [agentType]: false })) } - } catch (err) { - const message = err instanceof Error ? err.message : String(err) - setCheckState((prev) => ({ ...prev, [agentType]: { error: message } })) - } finally { - setChecking((prev) => ({ ...prev, [agentType]: false })) - } - }, []) + }, + [] + ) const runAllPreflight = useCallback( async (agentTypes: AgentType[]) => { @@ -2790,10 +2792,7 @@ export function AcpAgentSettings() { await runNpxAction(agent, "upgrade") return } - if ( - action.kind === "uninstall_binary" || - action.kind === "uninstall_npx" - ) { + if (action.kind === "uninstall_binary" || action.kind === "uninstall_npx") { setUninstallConfirmAgent(agent) return } From 03191b3ff2be813f25090f7ae0bda05f6bea283e Mon Sep 17 00:00:00 2001 From: xintaofei Date: Thu, 19 Mar 2026 00:43:29 +0800 Subject: [PATCH 7/8] =?UTF-8?q?aux-panel=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/layout/aux-panel.tsx | 45 +++++++++-------------------- 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/src/components/layout/aux-panel.tsx b/src/components/layout/aux-panel.tsx index 35c5d5c..0f3022e 100644 --- a/src/components/layout/aux-panel.tsx +++ b/src/components/layout/aux-panel.tsx @@ -1,6 +1,6 @@ "use client" -import { useCallback, useEffect, useState } from "react" +import { useCallback, useState } from "react" import { FileDiff, Folder, FolderPen, GitCommit } from "lucide-react" import { useTranslations } from "next-intl" import { @@ -13,42 +13,23 @@ import { GitChangesTab } from "./aux-panel-git-changes-tab" import { GitLogTab } from "./aux-panel-git-log-tab" import { SessionFilesTab } from "./aux-panel-session-files-tab" +const LAZY_TABS: AuxPanelTab[] = ["file_tree", "changes", "git_log"] + export function AuxPanel() { const t = useTranslations("Folder.auxPanel.tabs") const { isOpen, activeTab, setActiveTab } = useAuxPanelContext() - const [hasMountedFileTree, setHasMountedFileTree] = useState( - activeTab === "file_tree" - ) - const [hasMountedChanges, setHasMountedChanges] = useState( - activeTab === "changes" - ) - const [hasMountedGitLog, setHasMountedGitLog] = useState( - activeTab === "git_log" + const [mountedTabs, setMountedTabs] = useState>( + () => new Set(LAZY_TABS.filter((tab) => tab === activeTab)) ) - // Sync mount flags when activeTab changes programmatically (e.g. revealInFileTree). - /* eslint-disable react-hooks/set-state-in-effect */ - useEffect(() => { - if (!isOpen) return - if (activeTab === "file_tree") setHasMountedFileTree(true) - else if (activeTab === "changes") setHasMountedChanges(true) - else if (activeTab === "git_log") setHasMountedGitLog(true) - }, [isOpen, activeTab]) - /* eslint-enable react-hooks/set-state-in-effect */ + // Ensure the active tab is mounted (covers both user clicks and programmatic changes) + if (isOpen && LAZY_TABS.includes(activeTab) && !mountedTabs.has(activeTab)) { + setMountedTabs((prev) => new Set(prev).add(activeTab)) + } const handleTabValueChange = useCallback( (value: string) => { - const nextTab = value as AuxPanelTab - setActiveTab(nextTab) - if (nextTab === "file_tree") { - setHasMountedFileTree(true) - } - if (nextTab === "changes") { - setHasMountedChanges(true) - } - if (nextTab === "git_log") { - setHasMountedGitLog(true) - } + setActiveTab(value as AuxPanelTab) }, [setActiveTab] ) @@ -107,21 +88,21 @@ export function AuxPanel() { forceMount className="mt-0 flex-1 min-h-0 overflow-hidden" > - {hasMountedFileTree ? : null} + {mountedTabs.has("file_tree") ? : null} - {hasMountedChanges ? : null} + {mountedTabs.has("changes") ? : null} - {hasMountedGitLog ? : null} + {mountedTabs.has("git_log") ? : null} From 795f8e23a869595bc26e7994f890749668393438 Mon Sep 17 00:00:00 2001 From: xintaofei Date: Thu, 19 Mar 2026 01:18:35 +0800 Subject: [PATCH 8/8] Release version 0.2.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit folder头部的搜索支持文件/目录搜索并跳转 --- package.json | 2 +- src-tauri/Cargo.lock | 2 +- src-tauri/Cargo.toml | 2 +- src-tauri/tauri.conf.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 512b55d..9317a3f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "codeg", "private": true, - "version": "0.2.2", + "version": "0.2.3", "scripts": { "dev": "next dev --turbopack", "build": "next build", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index f365ec4..6399a58 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -792,7 +792,7 @@ checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" [[package]] name = "codeg" -version = "0.2.2" +version = "0.2.3" dependencies = [ "agent-client-protocol-schema", "base64 0.22.1", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 815ff89..29898b0 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "codeg" -version = "0.2.2" +version = "0.2.3" description = "Agent Code Generation App" authors = ["feitao"] edition = "2021" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 41cb794..08475f2 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "codeg", - "version": "0.2.2", + "version": "0.2.3", "identifier": "app.codeg", "build": { "beforeDevCommand": "pnpm dev",