diff --git a/packages/app/src/app/h/[serverId]/workspace/[workspaceId]/_layout.tsx b/packages/app/src/app/h/[serverId]/workspace/[workspaceId]/_layout.tsx
index 8b763cffb..d2c06c8c7 100644
--- a/packages/app/src/app/h/[serverId]/workspace/[workspaceId]/_layout.tsx
+++ b/packages/app/src/app/h/[serverId]/workspace/[workspaceId]/_layout.tsx
@@ -1,16 +1,22 @@
-import { useGlobalSearchParams, usePathname } from 'expo-router'
+import { useLocalSearchParams } from 'expo-router'
import { WorkspaceScreen } from '@/screens/workspace/workspace-screen'
import {
- parseHostWorkspaceRouteFromPathname,
+ decodeWorkspaceIdFromPathSegment,
parseWorkspaceOpenIntent,
} from '@/utils/host-routes'
export default function HostWorkspaceLayout() {
- const expoPathname = usePathname()
- const params = useGlobalSearchParams<{ open?: string | string[] }>()
- const activeRoute = parseHostWorkspaceRouteFromPathname(expoPathname)
- const serverId = activeRoute?.serverId ?? ''
- const workspaceId = activeRoute?.workspaceId ?? ''
+ const params = useLocalSearchParams<{
+ serverId?: string | string[]
+ workspaceId?: string | string[]
+ open?: string | string[]
+ }>()
+ const serverValue = Array.isArray(params.serverId) ? params.serverId[0] : params.serverId
+ const workspaceValue = Array.isArray(params.workspaceId)
+ ? params.workspaceId[0]
+ : params.workspaceId
+ const serverId = serverValue?.trim() ?? ''
+ const workspaceId = workspaceValue ? (decodeWorkspaceIdFromPathSegment(workspaceValue) ?? '') : ''
const openValue = Array.isArray(params.open) ? params.open[0] : params.open
const openIntent = parseWorkspaceOpenIntent(openValue)
diff --git a/packages/app/src/components/file-explorer-pane.tsx b/packages/app/src/components/file-explorer-pane.tsx
index 2f6b4f870..14d15282f 100644
--- a/packages/app/src/components/file-explorer-pane.tsx
+++ b/packages/app/src/components/file-explorer-pane.tsx
@@ -23,6 +23,7 @@ import Animated, {
withRepeat,
withTiming,
} from "react-native-reanimated";
+import { WORKSPACE_SECONDARY_HEADER_HEIGHT } from "@/constants/layout";
import { Fonts } from "@/constants/theme";
import * as Clipboard from "expo-clipboard";
import {
@@ -581,13 +582,12 @@ export function FileExplorerPane({
style={({ hovered, pressed }) => [
styles.iconButton,
(hovered || pressed) && styles.iconButtonHovered,
- pressed && styles.iconButtonPressed,
]}
accessibilityRole="button"
accessibilityLabel="Refresh files"
>
-
+
@@ -875,7 +875,7 @@ const styles = StyleSheet.create((theme) => ({
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
- height: 32 + theme.spacing[2] * 2,
+ height: WORKSPACE_SECONDARY_HEADER_HEIGHT,
paddingHorizontal: theme.spacing[3],
borderBottomWidth: 1,
borderBottomColor: theme.colors.border,
@@ -898,7 +898,7 @@ const styles = StyleSheet.create((theme) => ({
flexShrink: 0,
},
sortButton: {
- height: 32,
+ height: 28,
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
@@ -1025,8 +1025,8 @@ const styles = StyleSheet.create((theme) => ({
fontWeight: theme.fontWeight.normal,
},
iconButton: {
- width: 32,
- height: 32,
+ width: 22,
+ height: 22,
borderRadius: theme.borderRadius.md,
alignItems: "center",
justifyContent: "center",
@@ -1034,10 +1034,6 @@ const styles = StyleSheet.create((theme) => ({
iconButtonHovered: {
backgroundColor: theme.colors.surface2,
},
- iconButtonPressed: {
- opacity: 0.8,
- transform: [{ scale: 0.96 }],
- },
refreshIcon: {
width: 16,
height: 16,
diff --git a/packages/app/src/components/git-diff-pane.tsx b/packages/app/src/components/git-diff-pane.tsx
index e157ca86b..ecd1154cc 100644
--- a/packages/app/src/components/git-diff-pane.tsx
+++ b/packages/app/src/components/git-diff-pane.tsx
@@ -37,6 +37,7 @@ import { useCheckoutStatusQuery } from "@/hooks/use-checkout-status-query";
import { useCheckoutPrStatusQuery } from "@/hooks/use-checkout-pr-status-query";
import { useHorizontalScrollOptional } from "@/contexts/horizontal-scroll-context";
import { useExplorerSidebarAnimation } from "@/contexts/explorer-sidebar-animation-context";
+import { WORKSPACE_SECONDARY_HEADER_HEIGHT } from "@/constants/layout";
import { Fonts } from "@/constants/theme";
import { shouldAnchorHeaderBeforeCollapse } from "@/utils/git-diff-scroll";
import {
@@ -1086,11 +1087,12 @@ const styles = StyleSheet.create((theme) => ({
flexShrink: 1,
},
diffStatusContainer: {
- paddingVertical: 1.5,
+ height: WORKSPACE_SECONDARY_HEADER_HEIGHT,
borderBottomWidth: 1,
borderBottomColor: theme.colors.border,
},
diffStatusInner: {
+ flex: 1,
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
diff --git a/packages/app/src/components/sidebar-workspace-list.tsx b/packages/app/src/components/sidebar-workspace-list.tsx
index 4cefdf9f6..a128b0136 100644
--- a/packages/app/src/components/sidebar-workspace-list.tsx
+++ b/packages/app/src/components/sidebar-workspace-list.tsx
@@ -20,7 +20,7 @@ import {
type ReactElement,
type MutableRefObject,
} from 'react'
-import { router, usePathname } from 'expo-router'
+import { router, useGlobalSearchParams, usePathname } from 'expo-router'
import { navigateToWorkspace } from '@/hooks/use-workspace-navigation'
import { StyleSheet, UnistylesRuntime, useUnistyles } from 'react-native-unistyles'
import { type GestureType } from 'react-native-gesture-handler'
@@ -35,8 +35,10 @@ import { projectIconQueryKey } from '@/hooks/use-project-icon-query'
import {
buildHostNewAgentRoute,
buildHostWorkspaceRoute,
+ decodeWorkspaceIdFromPathSegment,
parseHostWorkspaceRouteFromPathname,
} from '@/utils/host-routes'
+import { normalizeWorkspaceIdentity } from '@/utils/workspace-identity'
import {
type SidebarProjectEntry,
type SidebarWorkspaceEntry,
@@ -1210,6 +1212,10 @@ export function SidebarWorkspaceList({
const isMobile = UnistylesRuntime.breakpoint === 'xs' || UnistylesRuntime.breakpoint === 'sm'
const isNative = Platform.OS !== 'web'
const pathname = usePathname()
+ const routeParams = useGlobalSearchParams<{
+ serverId?: string | string[]
+ workspaceId?: string | string[]
+ }>()
const isTauri = getIsTauri()
const altDown = useKeyboardShortcutsStore((state) => state.altDown)
const cmdOrCtrlDown = useKeyboardShortcutsStore((state) => state.cmdOrCtrlDown)
@@ -1224,15 +1230,36 @@ export function SidebarWorkspaceList({
if (!pathname) {
return null
}
- const parsed = parseHostWorkspaceRouteFromPathname(pathname)
- if (!parsed) {
+ if (!parseHostWorkspaceRouteFromPathname(pathname)) {
return null
}
- return {
- serverId: parsed.serverId,
- workspaceId: parsed.workspaceId,
+
+ const serverValue = Array.isArray(routeParams.serverId)
+ ? routeParams.serverId[0]
+ : routeParams.serverId
+ const workspaceValue = Array.isArray(routeParams.workspaceId)
+ ? routeParams.workspaceId[0]
+ : routeParams.workspaceId
+ const serverId = serverValue?.trim() ?? ''
+ const workspaceId = normalizeWorkspaceIdentity(
+ workspaceValue ? decodeWorkspaceIdFromPathSegment(workspaceValue) : null
+ )
+ if (!serverId || !workspaceId) {
+ const parsed = parseHostWorkspaceRouteFromPathname(pathname)
+ if (!parsed) {
+ return null
+ }
+ return {
+ serverId: parsed.serverId,
+ workspaceId: parsed.workspaceId,
+ }
}
- }, [pathname])
+
+ return {
+ serverId,
+ workspaceId,
+ }
+ }, [pathname, routeParams.serverId, routeParams.workspaceId])
const projectIconRequests = useMemo(() => {
if (!serverId) {
diff --git a/packages/app/src/constants/layout.ts b/packages/app/src/constants/layout.ts
index aca0be463..7a8ac7833 100644
--- a/packages/app/src/constants/layout.ts
+++ b/packages/app/src/constants/layout.ts
@@ -8,6 +8,7 @@ export const FOOTER_HEIGHT = 75;
// This ensures both headers have the same visual height
export const HEADER_INNER_HEIGHT = 48;
export const HEADER_INNER_HEIGHT_MOBILE = 56;
+export const WORKSPACE_SECONDARY_HEADER_HEIGHT = 36;
export const HEADER_TOP_PADDING_MOBILE = 8;
// Max width for chat content (stream view, input area, new agent form)