diff --git a/apps/tauri/src-tauri/Cargo.lock b/apps/tauri/src-tauri/Cargo.lock index 0d3447e..f5d07e2 100644 --- a/apps/tauri/src-tauri/Cargo.lock +++ b/apps/tauri/src-tauri/Cargo.lock @@ -138,6 +138,7 @@ dependencies = [ "chrono", "comrak", "cpal", + "fix-path-env", "futures-util", "indexmap 2.13.0", "notify", @@ -1380,6 +1381,16 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "fix-path-env" +version = "0.0.0" +source = "git+https://github.com/tauri-apps/fix-path-env-rs#c4c45d503ea115a839aae718d02f79e7c7f0f673" +dependencies = [ + "home", + "strip-ansi-escapes", + "thiserror 1.0.69", +] + [[package]] name = "fixedbitset" version = "0.5.7" @@ -2002,6 +2013,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "html5ever" version = "0.29.1" @@ -4679,6 +4699,15 @@ dependencies = [ "quote", ] +[[package]] +name = "strip-ansi-escapes" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a8f8038e7e7969abb3f1b7c2a811225e9296da208539e0f79c5251d6cac0025" +dependencies = [ + "vte", +] + [[package]] name = "strsim" version = "0.11.1" @@ -5853,6 +5882,15 @@ dependencies = [ "libc", ] +[[package]] +name = "vte" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "231fdcd7ef3037e8330d8e17e61011a2c244126acc0a982f4040ac3f9f0bc077" +dependencies = [ + "memchr", +] + [[package]] name = "walkdir" version = "2.5.0" diff --git a/apps/tauri/src-tauri/Cargo.toml b/apps/tauri/src-tauri/Cargo.toml index a1ee5dc..59b2447 100644 --- a/apps/tauri/src-tauri/Cargo.toml +++ b/apps/tauri/src-tauri/Cargo.toml @@ -35,3 +35,4 @@ rusqlite = { version = "0.31", features = ["bundled"] } uuid = { version = "1", features = ["v4"] } chrono = "0.4" indexmap = "2" +fix-path-env = { git = "https://github.com/tauri-apps/fix-path-env-rs" } diff --git a/apps/tauri/src-tauri/src/lib.rs b/apps/tauri/src-tauri/src/lib.rs index d669dfc..3d53e5e 100644 --- a/apps/tauri/src-tauri/src/lib.rs +++ b/apps/tauri/src-tauri/src/lib.rs @@ -873,6 +873,8 @@ fn setup_macos_menu(app: &tauri::App) -> Result<(), Box> #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { + let _ = fix_path_env::fix(); + let builder = tauri::Builder::default() .plugin(tauri_plugin_cli::init()) .plugin(tauri_plugin_dialog::init()) diff --git a/apps/tauri/src/components/ActiveSessionView.tsx b/apps/tauri/src/components/ActiveSessionView.tsx index d598c43..7f7fb19 100644 --- a/apps/tauri/src/components/ActiveSessionView.tsx +++ b/apps/tauri/src/components/ActiveSessionView.tsx @@ -118,12 +118,15 @@ export function ActiveSessionView({ // Core ACP actions using new per-session commands const sendPrompt = useCallback(async (text: string) => { try { + if (!text.startsWith("/")) { + sessionMessages.addOptimisticUserMessage(text); + } await invoke("acp_session_send_prompt", { sessionId: session.id, text }); } catch (e) { setErrors((prev) => [...prev, String(e)]); updateSessionEntry(session.id, { isStreaming: false }); } - }, [session.id]); + }, [session.id, sessionMessages.addOptimisticUserMessage]); const persistPreferences = useCallback((partial: Partial) => { try { diff --git a/apps/tauri/src/hooks/useSessionMessages.ts b/apps/tauri/src/hooks/useSessionMessages.ts index 40d9f38..7413917 100644 --- a/apps/tauri/src/hooks/useSessionMessages.ts +++ b/apps/tauri/src/hooks/useSessionMessages.ts @@ -32,6 +32,7 @@ function toAcpMessage(r: MessageRecord): AcpMessage { export interface UseSessionMessagesReturn { clearMessages: () => void; + addOptimisticUserMessage: (text: string) => void; messages: AcpMessage[]; isLoadingInitial: boolean; isLoadingMore: boolean; @@ -73,6 +74,16 @@ export function useSessionMessages(sessionId: string): UseSessionMessagesReturn void loadInitial(); }, [loadInitial]); + const addOptimisticUserMessage = useCallback((text: string) => { + const optimisticMsg: AcpMessage = { + id: `optimistic-${Date.now()}`, + role: "user", + content: text, + timestamp: new Date(), + }; + setMessages((prev) => [...prev, optimisticMsg]); + }, []); + useEffect(() => { const unlisten = window.__TAURI__.event.listen<{ sessionId: string; @@ -84,7 +95,16 @@ export function useSessionMessages(sessionId: string): UseSessionMessagesReturn if (msgSessionId !== sessionId) return; setMessages((prev) => { if (prev.some((m) => m.id === id)) return prev; - return [...prev, { id, role: "user", content, timestamp: new Date() }]; + const idx = prev.findIndex( + (m) => m.id.startsWith("optimistic-") && m.role === "user" && m.content === content + ); + const persisted: AcpMessage = { id, role: "user", content, timestamp: new Date() }; + if (idx >= 0) { + const updated = [...prev]; + updated[idx] = persisted; + return updated; + } + return [...prev, persisted]; }); }); return () => { void unlisten.then((fn) => fn()); }; @@ -151,5 +171,5 @@ export function useSessionMessages(sessionId: string): UseSessionMessagesReturn return [...messages, ...streamingMessages]; }, [messages, streamingMessages]); - return { messages: merged, isLoadingInitial, isLoadingMore, hasMore, loadMore, clearMessages }; + return { messages: merged, isLoadingInitial, isLoadingMore, hasMore, loadMore, clearMessages, addOptimisticUserMessage }; }