Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions packages/app/src/context/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { batch, createEffect, createMemo, on, onCleanup, onMount, type Accessor
import { createSimpleContext } from "@opencode-ai/ui/context"
import { useGlobalSync } from "./global-sync"
import { useGlobalSDK } from "./global-sdk"
import { useServer } from "./server"
import { useServer, normalizePath } from "./server"
import { Project } from "@opencode-ai/sdk/v2"
import { Persist, persisted, removePersisted } from "@/utils/persist"
import { same } from "@/utils/same"
Expand Down Expand Up @@ -415,7 +415,8 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
list,
open(directory: string) {
const root = rootFor(directory)
if (server.projects.list().find((x) => x.worktree === root)) return
const normalized = normalizePath(root)
if (server.projects.list().find((x) => normalizePath(x.worktree) === normalized)) return
globalSync.project.loadSessions(root)
server.projects.open(root)
},
Expand Down
24 changes: 17 additions & 7 deletions packages/app/src/context/server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import { Persist, persisted } from "@/utils/persist"

type StoredProject = { worktree: string; expanded: boolean }

export function normalizePath(input: string) {
return input.replaceAll("\\", "/")
}

export function normalizeServerUrl(input: string) {
const trimmed = input.trim()
if (!trimmed) return
Expand Down Expand Up @@ -163,39 +167,44 @@ export const { use: useServer, provider: ServerProvider } = createSimpleContext(
open(directory: string) {
const key = origin()
if (!key) return
const normalized = normalizePath(directory)
const current = store.projects[key] ?? []
if (current.find((x) => x.worktree === directory)) return
setStore("projects", key, [{ worktree: directory, expanded: true }, ...current])
if (current.find((x) => normalizePath(x.worktree) === normalized)) return
setStore("projects", key, [{ worktree: normalized, expanded: true }, ...current])
},
close(directory: string) {
const key = origin()
if (!key) return
const normalized = normalizePath(directory)
const current = store.projects[key] ?? []
setStore(
"projects",
key,
current.filter((x) => x.worktree !== directory),
current.filter((x) => normalizePath(x.worktree) !== normalized),
)
},
expand(directory: string) {
const key = origin()
if (!key) return
const normalized = normalizePath(directory)
const current = store.projects[key] ?? []
const index = current.findIndex((x) => x.worktree === directory)
const index = current.findIndex((x) => normalizePath(x.worktree) === normalized)
if (index !== -1) setStore("projects", key, index, "expanded", true)
},
collapse(directory: string) {
const key = origin()
if (!key) return
const normalized = normalizePath(directory)
const current = store.projects[key] ?? []
const index = current.findIndex((x) => x.worktree === directory)
const index = current.findIndex((x) => normalizePath(x.worktree) === normalized)
if (index !== -1) setStore("projects", key, index, "expanded", false)
},
move(directory: string, toIndex: number) {
const key = origin()
if (!key) return
const normalized = normalizePath(directory)
const current = store.projects[key] ?? []
const fromIndex = current.findIndex((x) => x.worktree === directory)
const fromIndex = current.findIndex((x) => normalizePath(x.worktree) === normalized)
if (fromIndex === -1 || fromIndex === toIndex) return
const result = [...current]
const [item] = result.splice(fromIndex, 1)
Expand All @@ -210,7 +219,8 @@ export const { use: useServer, provider: ServerProvider } = createSimpleContext(
touch(directory: string) {
const key = origin()
if (!key) return
setStore("lastProject", key, directory)
const normalized = normalizePath(directory)
setStore("lastProject", key, normalized)
},
},
}
Expand Down
14 changes: 12 additions & 2 deletions packages/app/src/pages/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { DateTime } from "luxon"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { DialogSelectDirectory } from "@/components/dialog-select-directory"
import { DialogSelectServer } from "@/components/dialog-select-server"
import { useServer } from "@/context/server"
import { useServer, normalizePath } from "@/context/server"
import { useGlobalSync } from "@/context/global-sync"
import { useLanguage } from "@/context/language"

Expand All @@ -23,6 +23,16 @@ export default function Home() {
const server = useServer()
const language = useLanguage()
const homedir = createMemo(() => sync.data.path.home)
const displayPath = (path: string) => {
const home = homedir()
if (!home) return path
const normalizedPath = normalizePath(path)
const normalizedHome = normalizePath(home)
if (normalizedPath.startsWith(normalizedHome)) {
return "~" + normalizedPath.slice(normalizedHome.length)
}
return path
}
const recent = createMemo(() => {
return sync.data.project
.toSorted((a, b) => (b.time.updated ?? b.time.created) - (a.time.updated ?? a.time.created))
Expand Down Expand Up @@ -97,7 +107,7 @@ export default function Home() {
class="text-14-mono text-left justify-between px-3"
onClick={() => openProject(project.worktree)}
>
{project.worktree.replace(homedir(), "~")}
{displayPath(project.worktree)}
<div class="text-14-regular text-text-weak">
{DateTime.fromMillis(project.time.updated ?? project.time.created).toRelative()}
</div>
Expand Down
28 changes: 19 additions & 9 deletions packages/app/src/pages/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ import { navStart } from "@/utils/perf"
import { DialogSelectDirectory } from "@/components/dialog-select-directory"
import { DialogEditProject } from "@/components/dialog-edit-project"
import { Titlebar } from "@/components/titlebar"
import { useServer } from "@/context/server"
import { useServer, normalizePath } from "@/context/server"
import { useLanguage, type Locale } from "@/context/language"

export default function Layout(props: ParentProps) {
Expand Down Expand Up @@ -1111,13 +1111,14 @@ export default function Layout(props: ParentProps) {

function navigateToProject(directory: string | undefined) {
if (!directory) return
const normalized = normalizePath(directory)
if (!layout.sidebar.opened()) {
setState("hoverSession", undefined)
setState("hoverProject", undefined)
}
server.projects.touch(directory)
const lastSession = store.lastSession[directory]
navigate(`/${base64Encode(directory)}${lastSession ? `/session/${lastSession}` : ""}`)
server.projects.touch(normalized)
const lastSession = store.lastSession[normalized]
navigate(`/${base64Encode(normalized)}${lastSession ? `/session/${lastSession}` : ""}`)
layout.mobileSidebar.hide()
}

Expand All @@ -1132,8 +1133,9 @@ export default function Layout(props: ParentProps) {
}

function openProject(directory: string, navigate = true) {
layout.projects.open(directory)
if (navigate) navigateToProject(directory)
const normalized = normalizePath(directory)
layout.projects.open(normalized)
if (navigate) navigateToProject(normalized)
}

const deepLinkEvent = "opencode:deep-link"
Expand Down Expand Up @@ -2536,6 +2538,16 @@ export default function Layout(props: ParentProps) {
return layout.sidebar.workspaces(project.worktree)()
})
const homedir = createMemo(() => globalSync.data.path.home)
const displayPath = (path: string) => {
const home = homedir()
if (!home) return path
const normalizedPath = normalizePath(path)
const normalizedHome = normalizePath(home)
if (normalizedPath.startsWith(normalizedHome)) {
return "~" + normalizedPath.slice(normalizedHome.length)
}
return path
}

return (
<div
Expand Down Expand Up @@ -2570,9 +2582,7 @@ export default function Layout(props: ParentProps) {
transform: "translate3d(52px, 0, 0)",
}}
>
<span class="text-12-regular text-text-base truncate select-text">
{p.worktree.replace(homedir(), "~")}
</span>
<span class="text-12-regular text-text-base truncate select-text">{displayPath(p.worktree)}</span>
</Tooltip>
</div>

Expand Down
Loading