diff --git a/app/src-tauri/icons/128x128.png b/app/src-tauri/icons/128x128.png
index 843d237..0549ced 100644
Binary files a/app/src-tauri/icons/128x128.png and b/app/src-tauri/icons/128x128.png differ
diff --git a/app/src-tauri/icons/128x128@2x.png b/app/src-tauri/icons/128x128@2x.png
index dc5a4a2..3235a80 100644
Binary files a/app/src-tauri/icons/128x128@2x.png and b/app/src-tauri/icons/128x128@2x.png differ
diff --git a/app/src-tauri/icons/32x32.png b/app/src-tauri/icons/32x32.png
index c091288..286c604 100644
Binary files a/app/src-tauri/icons/32x32.png and b/app/src-tauri/icons/32x32.png differ
diff --git a/app/src-tauri/icons/icon.ico b/app/src-tauri/icons/icon.ico
index d4b604f..88e60c3 100644
Binary files a/app/src-tauri/icons/icon.ico and b/app/src-tauri/icons/icon.ico differ
diff --git a/app/src-tauri/icons/icon.png b/app/src-tauri/icons/icon.png
index 5afc55c..cf3f6b7 100644
Binary files a/app/src-tauri/icons/icon.png and b/app/src-tauri/icons/icon.png differ
diff --git a/app/src-tauri/src/docker/container.rs b/app/src-tauri/src/docker/container.rs
index f3c98f6..0ff5eda 100644
--- a/app/src-tauri/src/docker/container.rs
+++ b/app/src-tauri/src/docker/container.rs
@@ -392,6 +392,10 @@ pub async fn stop_container(container_id: &str) -> Result<(), String> {
pub async fn remove_container(container_id: &str) -> Result<(), String> {
let docker = get_docker()?;
+ log::info!(
+ "Removing container {} (v=false: named volumes such as claude config are preserved)",
+ container_id
+ );
docker
.remove_container(
container_id,
diff --git a/app/src/components/projects/ProjectCard.tsx b/app/src/components/projects/ProjectCard.tsx
index 6333e98..d606820 100644
--- a/app/src/components/projects/ProjectCard.tsx
+++ b/app/src/components/projects/ProjectCard.tsx
@@ -269,8 +269,6 @@ export default function ProjectCard({ project }: Props) {
{project.paths.map((pp, i) => (
/workspace/{pp.mount_name}
- ←
- {pp.host_path}
))}
diff --git a/app/src/components/terminal/TerminalView.tsx b/app/src/components/terminal/TerminalView.tsx
index 5610738..e36faab 100644
--- a/app/src/components/terminal/TerminalView.tsx
+++ b/app/src/components/terminal/TerminalView.tsx
@@ -7,11 +7,6 @@ import { openUrl } from "@tauri-apps/plugin-opener";
import "@xterm/xterm/css/xterm.css";
import { useTerminal } from "../../hooks/useTerminal";
-/** Strip ANSI escape sequences from a string. */
-function stripAnsi(s: string): string {
- return s.replace(/\x1b\[[0-9;]*[A-Za-z]|\x1b\][^\x07]*\x07/g, "");
-}
-
interface Props {
sessionId: string;
active: boolean;
@@ -84,50 +79,12 @@ export default function TerminalView({ sessionId, active }: Props) {
sendInput(sessionId, data);
});
- // ── URL accumulator ──────────────────────────────────────────────
- // Claude Code login emits a long OAuth URL that gets split across
- // hard newlines (\n / \r\n). The WebLinksAddon only joins
- // soft-wrapped lines (the `isWrapped` flag), so the URL match is
- // truncated and the link fails when clicked.
- //
- // Fix: buffer recent output, strip ANSI codes, and after a short
- // debounce check for a URL that spans multiple lines. When found,
- // write a single clean clickable copy to the terminal.
- const textDecoder = new TextDecoder();
- let outputBuffer = "";
- let debounceTimer: ReturnType | null = null;
-
- const flushUrlBuffer = () => {
- const plain = stripAnsi(outputBuffer);
- // Reassemble: strip hard newlines and carriage returns to join
- // fragments that were split across terminal lines.
- const joined = plain.replace(/[\r\n]+/g, "");
- // Look for a long OAuth/auth URL (Claude login URLs contain
- // "oauth" or "console.anthropic.com" or "/authorize").
- const match = joined.match(/https?:\/\/[^\s'"\x07]{80,}/);
- if (match) {
- const url = match[0];
- term.write("\r\n\x1b[36m🔗 Clickable login URL:\x1b[0m\r\n");
- term.write(`\x1b[4;34m${url}\x1b[0m\r\n`);
- }
- outputBuffer = "";
- };
-
// Handle backend output -> terminal
let aborted = false;
const outputPromise = onOutput(sessionId, (data) => {
if (aborted) return;
term.write(data);
-
- // Accumulate for URL detection (data is a Uint8Array, so decode it)
- outputBuffer += textDecoder.decode(data);
- // Cap buffer size to avoid memory growth
- if (outputBuffer.length > 8192) {
- outputBuffer = outputBuffer.slice(-4096);
- }
- if (debounceTimer) clearTimeout(debounceTimer);
- debounceTimer = setTimeout(flushUrlBuffer, 150);
}).then((unlisten) => {
if (aborted) unlisten();
return unlisten;
@@ -159,7 +116,6 @@ export default function TerminalView({ sessionId, active }: Props) {
return () => {
aborted = true;
- if (debounceTimer) clearTimeout(debounceTimer);
inputDisposable.dispose();
outputPromise.then((fn) => fn?.());
exitPromise.then((fn) => fn?.());