Update app icons, fix sidebar path overflow, and remove terminal URL accumulator
Replace placeholder icons with the Triple-C branded logo at all required Tauri sizes. Remove the host_path display from sidebar folder listings to prevent text overflow. Remove the URL accumulator that injected clickable login URL text into the terminal — the native WebLinksAddon still handles URLs when the window is wide enough. Add explicit logging on container removal confirming named volumes are preserved. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Before Width: | Height: | Size: 372 B After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 914 B After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 100 B After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 108 B After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 41 KiB |
@@ -392,6 +392,10 @@ pub async fn stop_container(container_id: &str) -> Result<(), String> {
|
|||||||
|
|
||||||
pub async fn remove_container(container_id: &str) -> Result<(), String> {
|
pub async fn remove_container(container_id: &str) -> Result<(), String> {
|
||||||
let docker = get_docker()?;
|
let docker = get_docker()?;
|
||||||
|
log::info!(
|
||||||
|
"Removing container {} (v=false: named volumes such as claude config are preserved)",
|
||||||
|
container_id
|
||||||
|
);
|
||||||
docker
|
docker
|
||||||
.remove_container(
|
.remove_container(
|
||||||
container_id,
|
container_id,
|
||||||
|
|||||||
@@ -269,8 +269,6 @@ export default function ProjectCard({ project }: Props) {
|
|||||||
{project.paths.map((pp, i) => (
|
{project.paths.map((pp, i) => (
|
||||||
<div key={i} className="text-xs text-[var(--text-secondary)] truncate">
|
<div key={i} className="text-xs text-[var(--text-secondary)] truncate">
|
||||||
<span className="font-mono">/workspace/{pp.mount_name}</span>
|
<span className="font-mono">/workspace/{pp.mount_name}</span>
|
||||||
<span className="mx-1">←</span>
|
|
||||||
<span>{pp.host_path}</span>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,11 +7,6 @@ import { openUrl } from "@tauri-apps/plugin-opener";
|
|||||||
import "@xterm/xterm/css/xterm.css";
|
import "@xterm/xterm/css/xterm.css";
|
||||||
import { useTerminal } from "../../hooks/useTerminal";
|
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 {
|
interface Props {
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
active: boolean;
|
active: boolean;
|
||||||
@@ -84,50 +79,12 @@ export default function TerminalView({ sessionId, active }: Props) {
|
|||||||
sendInput(sessionId, data);
|
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<typeof setTimeout> | 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
|
// Handle backend output -> terminal
|
||||||
let aborted = false;
|
let aborted = false;
|
||||||
|
|
||||||
const outputPromise = onOutput(sessionId, (data) => {
|
const outputPromise = onOutput(sessionId, (data) => {
|
||||||
if (aborted) return;
|
if (aborted) return;
|
||||||
term.write(data);
|
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) => {
|
}).then((unlisten) => {
|
||||||
if (aborted) unlisten();
|
if (aborted) unlisten();
|
||||||
return unlisten;
|
return unlisten;
|
||||||
@@ -159,7 +116,6 @@ export default function TerminalView({ sessionId, active }: Props) {
|
|||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
aborted = true;
|
aborted = true;
|
||||||
if (debounceTimer) clearTimeout(debounceTimer);
|
|
||||||
inputDisposable.dispose();
|
inputDisposable.dispose();
|
||||||
outputPromise.then((fn) => fn?.());
|
outputPromise.then((fn) => fn?.());
|
||||||
exitPromise.then((fn) => fn?.());
|
exitPromise.then((fn) => fn?.());
|
||||||
|
|||||||