diff --git a/packages/app/src/components/session/session-header.tsx b/packages/app/src/components/session/session-header.tsx
index 4947ad06a9b..2a1871003f8 100644
--- a/packages/app/src/components/session/session-header.tsx
+++ b/packages/app/src/components/session/session-header.tsx
@@ -145,9 +145,12 @@ export function SessionHeader() {
return layout.projects.list().find((p) => p.worktree === directory || p.sandboxes?.includes(directory))
})
const name = createMemo(() => {
+ const dir = projectDirectory()
const current = project()
+ // When in a workspace (sandbox), show the workspace name, not the project root
+ if (current && dir && dir !== current.worktree) return getFilename(dir)
if (current) return current.name || getFilename(current.worktree)
- return getFilename(projectDirectory())
+ return getFilename(dir)
})
const hotkey = createMemo(() => command.keybind("file.open"))
const os = createMemo(() => detectOS(platform))
diff --git a/packages/app/src/components/session/session-new-view.tsx b/packages/app/src/components/session/session-new-view.tsx
index e4ef3639362..99b0dca1ed3 100644
--- a/packages/app/src/components/session/session-new-view.tsx
+++ b/packages/app/src/components/session/session-new-view.tsx
@@ -27,7 +27,7 @@ export function NewSessionView(props: NewSessionViewProps) {
if (options().includes(selection)) return selection
return MAIN_WORKTREE
})
- const projectRoot = createMemo(() => sync.project?.worktree ?? sdk.directory)
+ const displayDir = createMemo(() => sdk.directory)
const isWorktree = createMemo(() => {
const project = sync.project
if (!project) return false
@@ -59,8 +59,8 @@ export function NewSessionView(props: NewSessionViewProps) {
- {getDirectory(projectRoot())}
- {getFilename(projectRoot())}
+ {getDirectory(displayDir())}
+ {getFilename(displayDir())}
diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx
index ab2687dcab9..1f834b0cdb9 100644
--- a/packages/app/src/pages/layout.tsx
+++ b/packages/app/src/pages/layout.tsx
@@ -72,6 +72,7 @@ import {
effectiveWorkspaceOrder,
errorMessage,
latestRootSession,
+ sortSessions,
sortedRootSessions,
workspaceKey,
} from "./layout/helpers"
@@ -572,6 +573,17 @@ export default function Layout(props: ParentProps) {
return projects.find((p) => p.worktree === root)
})
+ // Auto-enable workspaces when the current directory is a sandbox of a project
+ createEffect(() => {
+ const dir = currentDir()
+ const project = currentProject()
+ if (!dir || !project) return
+ if (dir === project.worktree) return
+ if (project.vcs !== "git") return
+ if (layout.sidebar.workspaces(project.worktree)()) return
+ layout.sidebar.setWorkspaces(project.worktree, true)
+ })
+
createEffect(
on(
() => ({ ready: pageReady(), layoutReady: layoutReady(), dir: params.dir, list: layout.projects.list() }),
@@ -620,8 +632,11 @@ export default function Layout(props: ParentProps) {
setStore("workspaceBranchName", projectId, branch, next)
}
- const workspaceLabel = (directory: string, branch?: string, projectId?: string) =>
- workspaceName(directory, projectId, branch) ?? branch ?? getFilename(directory)
+ const workspaceLabel = (directory: string, branch?: string, projectId?: string) => {
+ // For JJ co-located workspaces, branch is "HEAD" (detached) — fall through to directory name
+ const effectiveBranch = branch && branch !== "HEAD" ? branch : undefined
+ return workspaceName(directory, projectId, effectiveBranch) ?? effectiveBranch ?? getFilename(directory)
+ }
const workspaceSetting = createMemo(() => {
const project = currentProject()
@@ -1234,6 +1249,12 @@ export default function Layout(props: ParentProps) {
const root = projectRoot(directory)
server.projects.touch(root)
const project = layout.projects.list().find((item) => item.worktree === root)
+ // Auto-enable workspaces when navigating to a sandbox directory
+ if (project && directory !== root && project.vcs === "git") {
+ if (!layout.sidebar.workspaces(root)()) {
+ layout.sidebar.setWorkspaces(root, true)
+ }
+ }
let dirs = project
? effectiveWorkspaceOrder(root, [root, ...(project.sandboxes ?? [])], store.workspaceOrder[root])
: [root]
@@ -1269,6 +1290,37 @@ export default function Layout(props: ParentProps) {
return true
}
+ // When user explicitly opened a specific workspace (not the project root),
+ // navigate directly to that workspace instead of searching across all workspaces.
+ if (workspaceKey(directory) !== workspaceKey(root)) {
+ await refreshDirs(directory)
+ if (canOpen(directory)) {
+ const [dirStore] = globalSync.child(directory, { bootstrap: false })
+ const latest = sortedRootSessions(dirStore, Date.now())[0]
+ if (latest) {
+ setStore("lastProjectSession", root, { directory, id: latest.id, at: Date.now() })
+ navigateWithSidebarReset(`/${base64Encode(directory)}/session/${latest.id}`)
+ return
+ }
+ // Try fetching sessions from server for this specific workspace
+ const fetched = await globalSDK.client.session
+ .list({ directory })
+ .then((x) => x.data ?? [])
+ .catch(() => [] as Session[])
+ const visible = fetched
+ .filter((s) => !s.parentID && !s.time?.archived)
+ .sort(sortSessions(Date.now()))
+ if (visible[0]) {
+ setStore("lastProjectSession", root, { directory, id: visible[0].id, at: Date.now() })
+ navigateWithSidebarReset(`/${base64Encode(directory)}/session/${visible[0].id}`)
+ return
+ }
+ }
+ // No sessions in this workspace — navigate to it anyway (empty state)
+ navigateWithSidebarReset(`/${base64Encode(directory)}/session`)
+ return
+ }
+
const projectSession = store.lastProjectSession[root]
if (projectSession?.id) {
await refreshDirs(projectSession.directory)
diff --git a/packages/app/src/pages/layout/helpers.ts b/packages/app/src/pages/layout/helpers.ts
index be4ce9f5742..76f50372488 100644
--- a/packages/app/src/pages/layout/helpers.ts
+++ b/packages/app/src/pages/layout/helpers.ts
@@ -8,7 +8,7 @@ export const workspaceKey = (directory: string) => {
return directory.replace(/[\\/]+$/, "")
}
-function sortSessions(now: number) {
+export function sortSessions(now: number) {
const oneMinuteAgo = now - 60 * 1000
return (a: Session, b: Session) => {
const aUpdated = a.time.updated ?? a.time.created
diff --git a/packages/app/src/pages/layout/sidebar-workspace.tsx b/packages/app/src/pages/layout/sidebar-workspace.tsx
index 86ede774e63..1147de12b9e 100644
--- a/packages/app/src/pages/layout/sidebar-workspace.tsx
+++ b/packages/app/src/pages/layout/sidebar-workspace.tsx
@@ -110,7 +110,7 @@ const WorkspaceHeader = (props: {
when={!props.local()}
fallback={
- {props.branch() ?? getFilename(props.directory)}
+ {props.branch() && props.branch() !== "HEAD" ? props.branch() : getFilename(props.directory)}
}
>
@@ -326,8 +326,9 @@ export const SortableWorkspace = (props: {
const active = createMemo(() => props.ctx.currentDir() === props.directory)
const workspaceValue = createMemo(() => {
const branch = workspaceStore.vcs?.branch
- const name = branch ?? getFilename(props.directory)
- return props.ctx.workspaceName(props.directory, props.project.id, branch) ?? name
+ const effectiveBranch = branch && branch !== "HEAD" ? branch : undefined
+ const name = effectiveBranch ?? getFilename(props.directory)
+ return props.ctx.workspaceName(props.directory, props.project.id, effectiveBranch) ?? name
})
const open = createMemo(() => props.ctx.workspaceExpanded(props.directory, local()))
const boot = createMemo(() => open() || active())