diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d232b44f..cbbe7e337 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## 0.1.52 - 2026-04-10 + +### Added +- Theme selector — choose from six themes including Midnight, Claude, and Ghostty dark variants. +- Branch switching — switch git branches directly from the workspace header, with automatic stash and restore for uncommitted changes. +- Auto-download updates — desktop updates download silently in the background so they're ready to install when you are. + +### Fixed +- Layout now responds correctly when resizing the window or rotating a tablet — previously the app could get stuck in mobile layout on a large screen. +- Terminal no longer causes massive memory spikes from snapshot thrashing during heavy output. +- Typing in the terminal works reliably — special keys, Ctrl combos, and paste are handled natively by the terminal emulator. +- Initializing agents no longer show a loading spinner as if they're running. +- Reconnecting to a running agent now works even when session persistence is unavailable. +- Error screens on desktop are now scrollable. +- Model list refreshes in the background when you open the model selector. +- Draft agent feature preferences (like thinking mode) are remembered across sessions. + ## 0.1.51 - 2026-04-09 ### Added diff --git a/nix/package.nix b/nix/package.nix index db9dd06ca..598e6e881 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -42,7 +42,7 @@ buildNpmPackage rec { # To update: run `nix build` with lib.fakeHash, copy the `got:` hash. # CI auto-updates this when package-lock.json changes (see .github/workflows/). - npmDepsHash = "sha256-gyCcVTlgLk8+G/OtwZT/GomN2lYe7uEcDoLp1ZQoc5M="; + npmDepsHash = "sha256-6NXZBZwIxCBYxaTe+lq6D8uNaJzL3+z/VUodVS0sS9o="; # Prevent onnxruntime-node's install script from running during automatic # npm rebuild (it tries to download from api.nuget.org, which fails in the sandbox). diff --git a/package-lock.json b/package-lock.json index bbf9ccefb..30869966c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "paseo", - "version": "0.1.51", + "version": "0.1.52", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "paseo", - "version": "0.1.51", + "version": "0.1.52", "hasInstallScript": true, "license": "AGPL-3.0-or-later", "workspaces": [ @@ -36169,16 +36169,16 @@ }, "packages/app": { "name": "@getpaseo/app", - "version": "0.1.51", + "version": "0.1.52", "dependencies": { "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", "@expo/vector-icons": "^15.0.2", "@floating-ui/react-native": "^0.10.7", - "@getpaseo/expo-two-way-audio": "0.1.51", - "@getpaseo/highlight": "0.1.51", - "@getpaseo/server": "0.1.51", + "@getpaseo/expo-two-way-audio": "0.1.52", + "@getpaseo/highlight": "0.1.52", + "@getpaseo/server": "0.1.52", "@gorhom/bottom-sheet": "^5.2.6", "@gorhom/portal": "^1.0.14", "@react-native-async-storage/async-storage": "2.2.0", @@ -36295,11 +36295,11 @@ }, "packages/cli": { "name": "@getpaseo/cli", - "version": "0.1.51", + "version": "0.1.52", "dependencies": { "@clack/prompts": "^1.0.0", - "@getpaseo/relay": "0.1.51", - "@getpaseo/server": "0.1.51", + "@getpaseo/relay": "0.1.52", + "@getpaseo/server": "0.1.52", "chalk": "^5.3.0", "commander": "^12.0.0", "mime-types": "^2.1.35", @@ -36340,11 +36340,11 @@ }, "packages/desktop": { "name": "@getpaseo/desktop", - "version": "0.1.51", + "version": "0.1.52", "license": "AGPL-3.0-or-later", "dependencies": { - "@getpaseo/cli": "0.1.51", - "@getpaseo/server": "0.1.51", + "@getpaseo/cli": "0.1.52", + "@getpaseo/server": "0.1.52", "electron-log": "^5.4.3", "electron-updater": "^6.6.2", "ws": "^8.14.2" @@ -36378,7 +36378,7 @@ }, "packages/expo-two-way-audio": { "name": "@getpaseo/expo-two-way-audio", - "version": "0.1.51", + "version": "0.1.52", "license": "MIT", "devDependencies": { "@biomejs/biome": "1.9.4", @@ -36579,7 +36579,7 @@ }, "packages/highlight": { "name": "@getpaseo/highlight", - "version": "0.1.51", + "version": "0.1.52", "dependencies": { "@lezer/common": "^1.5.0", "@lezer/cpp": "^1.1.5", @@ -36605,7 +36605,7 @@ }, "packages/relay": { "name": "@getpaseo/relay", - "version": "0.1.51", + "version": "0.1.52", "dependencies": { "base64-js": "^1.5.1", "tweetnacl": "^1.0.3", @@ -36621,14 +36621,14 @@ }, "packages/server": { "name": "@getpaseo/server", - "version": "0.1.51", + "version": "0.1.52", "dependencies": { "@agentclientprotocol/sdk": "^0.17.1", "@ai-sdk/openai": "2.0.52", "@anthropic-ai/claude-agent-sdk": "^0.2.11", "@deepgram/sdk": "^3.4.0", - "@getpaseo/highlight": "0.1.51", - "@getpaseo/relay": "0.1.51", + "@getpaseo/highlight": "0.1.52", + "@getpaseo/relay": "0.1.52", "@isaacs/ttlcache": "^2.1.4", "@modelcontextprotocol/sdk": "^1.20.1", "@opencode-ai/sdk": "1.2.6", @@ -37031,7 +37031,7 @@ }, "packages/website": { "name": "@getpaseo/website", - "version": "0.1.51", + "version": "0.1.52", "dependencies": { "@cloudflare/vite-plugin": "^1.20.3", "@cloudflare/workers-types": "^4.20260114.0", diff --git a/package.json b/package.json index 97c1537ec..587cceaa3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "paseo", - "version": "0.1.51", + "version": "0.1.52", "private": true, "workspaces": [ "packages/expo-two-way-audio", diff --git a/packages/app/package.json b/packages/app/package.json index e14fa095c..f2064b26e 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,7 +1,7 @@ { "name": "@getpaseo/app", "main": "index.ts", - "version": "0.1.51", + "version": "0.1.52", "private": true, "scripts": { "start": "expo start", @@ -31,9 +31,9 @@ "@dnd-kit/utilities": "^3.2.2", "@expo/vector-icons": "^15.0.2", "@floating-ui/react-native": "^0.10.7", - "@getpaseo/expo-two-way-audio": "0.1.51", - "@getpaseo/highlight": "0.1.51", - "@getpaseo/server": "0.1.51", + "@getpaseo/expo-two-way-audio": "0.1.52", + "@getpaseo/highlight": "0.1.52", + "@getpaseo/server": "0.1.52", "@gorhom/bottom-sheet": "^5.2.6", "@gorhom/portal": "^1.0.14", "@react-native-async-storage/async-storage": "2.2.0", diff --git a/packages/app/src/screens/workspace/workspace-desktop-tabs-row.tsx b/packages/app/src/screens/workspace/workspace-desktop-tabs-row.tsx index 9be66a853..03f140e40 100644 --- a/packages/app/src/screens/workspace/workspace-desktop-tabs-row.tsx +++ b/packages/app/src/screens/workspace/workspace-desktop-tabs-row.tsx @@ -1,4 +1,12 @@ -import { useCallback, useMemo, useState, type Dispatch, type SetStateAction } from "react"; +import { + useCallback, + useEffect, + useMemo, + useRef, + useState, + type Dispatch, + type SetStateAction, +} from "react"; import { ActivityIndicator, Platform, @@ -97,6 +105,27 @@ function getFallbackTabLabel(tab: WorkspaceTabDescriptor): string { return "Agent"; } +function useMiddleClickClose(onClose: () => void) { + const ref = useRef(null); + + useEffect(() => { + const node = ref.current as unknown as HTMLElement | null; + if (!node) return; + + function handleAuxClick(event: MouseEvent) { + if (event.button === 1) { + event.preventDefault(); + onClose(); + } + } + + node.addEventListener("auxclick", handleAuxClick); + return () => node.removeEventListener("auxclick", handleAuxClick); + }, [onClose]); + + return ref; +} + function TabChip({ tab, isActive, @@ -136,6 +165,9 @@ function TabChip({ }) { const { theme } = useUnistyles(); const { closeButtonTestId, contextMenuTestId, menuEntries } = resolvedTab; + const middleClickRef = useMiddleClickClose( + useCallback(() => void onCloseTab(tab.tabId), [onCloseTab, tab.tabId]), + ); const [hovered, setHovered] = useState(false); const isHighlighted = isActive || hovered || isCloseHovered; const closeButtonDragBlockers = @@ -151,6 +183,7 @@ function TabChip({ : undefined; return ( + @@ -318,6 +351,7 @@ function TabChip({ )} + ); } diff --git a/packages/cli/package.json b/packages/cli/package.json index 017f44bca..c0006121b 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@getpaseo/cli", - "version": "0.1.51", + "version": "0.1.52", "description": "Paseo CLI - control your AI coding agents from the command line", "type": "module", "files": [ @@ -24,8 +24,8 @@ }, "dependencies": { "@clack/prompts": "^1.0.0", - "@getpaseo/relay": "0.1.51", - "@getpaseo/server": "0.1.51", + "@getpaseo/relay": "0.1.52", + "@getpaseo/server": "0.1.52", "chalk": "^5.3.0", "commander": "^12.0.0", "mime-types": "^2.1.35", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 0704b3e01..dd39b1aad 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@getpaseo/desktop", - "version": "0.1.51", + "version": "0.1.52", "private": true, "description": "Paseo desktop app (Electron wrapper)", "main": "dist/main.js", @@ -12,8 +12,8 @@ "typecheck": "tsc --noEmit -p tsconfig.json" }, "dependencies": { - "@getpaseo/cli": "0.1.51", - "@getpaseo/server": "0.1.51", + "@getpaseo/cli": "0.1.52", + "@getpaseo/server": "0.1.52", "electron-log": "^5.4.3", "electron-updater": "^6.6.2", "ws": "^8.14.2" diff --git a/packages/expo-two-way-audio/package.json b/packages/expo-two-way-audio/package.json index 9b0a62de4..10b941306 100644 --- a/packages/expo-two-way-audio/package.json +++ b/packages/expo-two-way-audio/package.json @@ -1,6 +1,6 @@ { "name": "@getpaseo/expo-two-way-audio", - "version": "0.1.51", + "version": "0.1.52", "description": "Native module for two way audio streaming", "main": "build/index.js", "types": "build/index.d.ts", diff --git a/packages/highlight/package.json b/packages/highlight/package.json index c12a525a3..09a34fd48 100644 --- a/packages/highlight/package.json +++ b/packages/highlight/package.json @@ -1,6 +1,6 @@ { "name": "@getpaseo/highlight", - "version": "0.1.51", + "version": "0.1.52", "type": "module", "publishConfig": { "access": "public" diff --git a/packages/relay/package.json b/packages/relay/package.json index 06a6563e3..ce0583708 100644 --- a/packages/relay/package.json +++ b/packages/relay/package.json @@ -1,6 +1,6 @@ { "name": "@getpaseo/relay", - "version": "0.1.51", + "version": "0.1.52", "description": "Paseo relay for bridging daemon and client connections", "type": "module", "publishConfig": { diff --git a/packages/server/package.json b/packages/server/package.json index 2c00cfeba..d69d7941d 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,6 +1,6 @@ { "name": "@getpaseo/server", - "version": "0.1.51", + "version": "0.1.52", "description": "Paseo backend server", "type": "module", "publishConfig": { @@ -61,8 +61,8 @@ "@ai-sdk/openai": "2.0.52", "@anthropic-ai/claude-agent-sdk": "^0.2.11", "@deepgram/sdk": "^3.4.0", - "@getpaseo/highlight": "0.1.51", - "@getpaseo/relay": "0.1.51", + "@getpaseo/highlight": "0.1.52", + "@getpaseo/relay": "0.1.52", "@isaacs/ttlcache": "^2.1.4", "@modelcontextprotocol/sdk": "^1.20.1", "@opencode-ai/sdk": "1.2.6", diff --git a/packages/server/src/server/session.ts b/packages/server/src/server/session.ts index 64135eadb..445521796 100644 --- a/packages/server/src/server/session.ts +++ b/packages/server/src/server/session.ts @@ -408,7 +408,6 @@ export type SessionOptions = { appVersion?: string | null; onMessage: (msg: SessionOutboundMessage) => void; onBinaryMessage?: (frame: Uint8Array) => void; - getBinaryBufferedAmount?: () => number; onLifecycleIntent?: (intent: SessionLifecycleIntent) => void; logger: pino.Logger; downloadTokenStore: DownloadTokenStore; @@ -549,7 +548,6 @@ export class Session { private readonly sessionId: string; private readonly onMessage: (msg: SessionOutboundMessage) => void; private readonly onBinaryMessage: ((frame: Uint8Array) => void) | null; - private readonly getBinaryBufferedAmount: (() => number) | null; private readonly onLifecycleIntent: ((intent: SessionLifecycleIntent) => void) | null; private readonly sessionLogger: pino.Logger; private readonly paseoHome: string; @@ -661,7 +659,6 @@ export class Session { appVersion, onMessage, onBinaryMessage, - getBinaryBufferedAmount, onLifecycleIntent, logger, downloadTokenStore, @@ -697,7 +694,6 @@ export class Session { this.sessionId = uuidv4(); this.onMessage = onMessage; this.onBinaryMessage = onBinaryMessage ?? null; - this.getBinaryBufferedAmount = getBinaryBufferedAmount ?? null; this.onLifecycleIntent = onLifecycleIntent ?? null; this.downloadTokenStore = downloadTokenStore; this.pushTokenStore = pushTokenStore; @@ -8713,11 +8709,4 @@ export class Session { } } - private getCurrentBinaryBufferedAmount(): number { - const bufferedAmount = this.getBinaryBufferedAmount?.() ?? 0; - if (!Number.isFinite(bufferedAmount) || bufferedAmount < 0) { - return 0; - } - return Math.floor(bufferedAmount); - } } diff --git a/packages/server/src/server/websocket-server.ts b/packages/server/src/server/websocket-server.ts index 9e37a19f0..17c732ad4 100644 --- a/packages/server/src/server/websocket-server.ts +++ b/packages/server/src/server/websocket-server.ts @@ -673,16 +673,6 @@ export class VoiceAssistantWebSocketServer { } this.sendBinaryToConnection(connection, frame); }, - getBinaryBufferedAmount: () => { - if (!connection) { - return 0; - } - let bufferedAmount = 0; - for (const socket of connection.sockets) { - bufferedAmount = Math.max(bufferedAmount, socket.bufferedAmount ?? 0); - } - return bufferedAmount; - }, onLifecycleIntent: (intent) => { this.onLifecycleIntent?.(intent); }, diff --git a/packages/website/package.json b/packages/website/package.json index a42e6b38c..7b7582fba 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -1,6 +1,6 @@ { "name": "@getpaseo/website", - "version": "0.1.51", + "version": "0.1.52", "private": true, "type": "module", "scripts": {