feat: add download toast, e2e permission tests, and update favicons

- Add download toast component with progress indicator and auto-dismiss
- Add download store for managing file download state
- Add e2e tests for permission prompts (allow/deny flows)
- Add global setup for e2e tests with isolated daemon instance
- Update favicon assets with new design
- Refactor agent-stream-view to use shared tool-call-details component
- Simplify file-explorer-pane component
- Clean up unused screenshots and PRODUCTION.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit is contained in:
Mohamed Boudra
2026-01-16 20:23:07 +07:00
parent f733f2852d
commit 420f25f1e9
39 changed files with 877 additions and 931 deletions
+31 -6
View File
@@ -2174,6 +2174,23 @@ export class Session {
{ cwd: agent.cwd }
);
// Get file statuses (A=added, D=deleted, M=modified) to detect deleted files
const { stdout: nameStatusOutput } = await execAsync(
"git diff --name-status HEAD",
{ cwd: agent.cwd }
);
const deletedFiles = new Set<string>();
const addedFiles = new Set<string>();
for (const line of nameStatusOutput.trim().split("\n").filter(Boolean)) {
const [status, ...pathParts] = line.split("\t");
const path = pathParts.join("\t");
if (status === "D") {
deletedFiles.add(path);
} else if (status === "A") {
addedFiles.add(path);
}
}
// Parse numstat output: "additions\tdeletions\tfilepath" or "-\t-\tfilepath" for binary
interface FileStats {
path: string;
@@ -2181,6 +2198,8 @@ export class Session {
deletions: number;
isBinary: boolean;
isTracked: boolean;
isDeleted: boolean;
isNew: boolean;
}
const fileStats: FileStats[] = [];
@@ -2196,6 +2215,8 @@ export class Session {
deletions: isBinary ? 0 : parseInt(delStr, 10),
isBinary,
isTracked: true,
isDeleted: deletedFiles.has(path),
isNew: addedFiles.has(path),
});
}
}
@@ -2224,6 +2245,8 @@ export class Session {
deletions: 0,
isBinary,
isTracked: false,
isDeleted: false,
isNew: true,
});
} catch {
// If we can't determine, assume text and try to get it
@@ -2233,6 +2256,8 @@ export class Session {
deletions: 0,
isBinary: false,
isTracked: false,
isDeleted: false,
isNew: true,
});
}
}
@@ -2250,8 +2275,8 @@ export class Session {
if (stats.isBinary) {
allFiles.push({
path: stats.path,
isNew: !stats.isTracked,
isDeleted: false,
isNew: stats.isNew,
isDeleted: stats.isDeleted,
additions: 0,
deletions: 0,
hunks: [],
@@ -2264,8 +2289,8 @@ export class Session {
if (totalLines > MAX_DIFF_LINES) {
allFiles.push({
path: stats.path,
isNew: !stats.isTracked,
isDeleted: false,
isNew: stats.isNew,
isDeleted: stats.isDeleted,
additions: stats.additions,
deletions: stats.deletions,
hunks: [],
@@ -2301,8 +2326,8 @@ export class Session {
// If diff fails for this file, add it with empty hunks
allFiles.push({
path: stats.path,
isNew: !stats.isTracked,
isDeleted: false,
isNew: stats.isNew,
isDeleted: stats.isDeleted,
additions: stats.additions,
deletions: stats.deletions,
hunks: [],
@@ -9,6 +9,8 @@ import { createPaseoDaemon, type PaseoDaemonConfig } from "../bootstrap.js";
type TestPaseoDaemonOptions = {
basicUsers?: Record<string, string>;
downloadTokenTtlMs?: number;
corsAllowedOrigins?: string[];
listen?: string;
};
export type TestPaseoDaemon = {
@@ -59,12 +61,13 @@ export async function createTestPaseoDaemon(
const staticDir = await mkdtemp(path.join(os.tmpdir(), "paseo-static-"));
const port = await getAvailablePort();
const listenHost = options.listen ?? '127.0.0.1';
const config: PaseoDaemonConfig = {
listen: `127.0.0.1:${port}`,
listen: `${listenHost}:${port}`,
paseoHome,
corsAllowedOrigins: [],
corsAllowedOrigins: options.corsAllowedOrigins ?? [],
agentMcpRoute: "/mcp/agents",
agentMcpAllowedHosts: [`127.0.0.1:${port}`, `localhost:${port}`],
agentMcpAllowedHosts: [`127.0.0.1:${port}`, `localhost:${port}`, `${listenHost}:${port}`],
auth: {
basicUsers,
agentMcpAuthHeader,