summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src/pages/layout
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-03-12 07:26:43 -0500
committerGitHub <[email protected]>2026-03-12 12:26:43 +0000
commitd4107d51f163b74c96895b50d653828c698d937e (patch)
tree070ae9ad38ebd230d75517d485fc0c86f2263c6b /packages/app/src/pages/layout
parentd8fbe0af0151c662008d8a2dbbb7c76ff5db88d4 (diff)
downloadopencode-d4107d51f163b74c96895b50d653828c698d937e.tar.gz
opencode-d4107d51f163b74c96895b50d653828c698d937e.zip
chore: cleanup (#17115)
Diffstat (limited to 'packages/app/src/pages/layout')
-rw-r--r--packages/app/src/pages/layout/helpers.test.ts16
-rw-r--r--packages/app/src/pages/layout/helpers.ts8
-rw-r--r--packages/app/src/pages/layout/sidebar-project-helpers.test.ts63
-rw-r--r--packages/app/src/pages/layout/sidebar-project-helpers.ts11
-rw-r--r--packages/app/src/pages/layout/sidebar-project.tsx18
-rw-r--r--packages/app/src/pages/layout/sidebar-shell-helpers.ts1
-rw-r--r--packages/app/src/pages/layout/sidebar-shell.test.ts13
-rw-r--r--packages/app/src/pages/layout/sidebar-shell.tsx3
-rw-r--r--packages/app/src/pages/layout/sidebar-workspace-helpers.ts2
-rw-r--r--packages/app/src/pages/layout/sidebar-workspace.test.ts13
-rw-r--r--packages/app/src/pages/layout/sidebar-workspace.tsx100
11 files changed, 45 insertions, 203 deletions
diff --git a/packages/app/src/pages/layout/helpers.test.ts b/packages/app/src/pages/layout/helpers.test.ts
index d1569dbd9..916b80214 100644
--- a/packages/app/src/pages/layout/helpers.test.ts
+++ b/packages/app/src/pages/layout/helpers.test.ts
@@ -6,9 +6,15 @@ import {
parseDeepLink,
parseNewSessionDeepLink,
} from "./deep-links"
-import { displayName, errorMessage, getDraggableId, syncWorkspaceOrder, workspaceKey } from "./helpers"
import { type Session } from "@opencode-ai/sdk/v2/client"
-import { hasProjectPermissions, latestRootSession } from "./helpers"
+import {
+ displayName,
+ errorMessage,
+ hasProjectPermissions,
+ latestRootSession,
+ syncWorkspaceOrder,
+ workspaceKey,
+} from "./helpers"
const session = (input: Partial<Session> & Pick<Session, "id" | "directory">) =>
({
@@ -192,12 +198,6 @@ describe("layout workspace helpers", () => {
expect(result?.id).toBe("root")
})
- test("extracts draggable id safely", () => {
- expect(getDraggableId({ draggable: { id: "x" } })).toBe("x")
- expect(getDraggableId({ draggable: { id: 42 } })).toBeUndefined()
- expect(getDraggableId(null)).toBeUndefined()
- })
-
test("formats fallback project display name", () => {
expect(displayName({ worktree: "/tmp/app" })).toBe("app")
expect(displayName({ worktree: "/tmp/app", name: "My App" })).toBe("My App")
diff --git a/packages/app/src/pages/layout/helpers.ts b/packages/app/src/pages/layout/helpers.ts
index 42315e589..8881b8a48 100644
--- a/packages/app/src/pages/layout/helpers.ts
+++ b/packages/app/src/pages/layout/helpers.ts
@@ -54,14 +54,6 @@ export const childMapByParent = (sessions: Session[]) => {
return map
}
-export function getDraggableId(event: unknown): string | undefined {
- if (typeof event !== "object" || event === null) return undefined
- if (!("draggable" in event)) return undefined
- const draggable = (event as { draggable?: { id?: unknown } }).draggable
- if (!draggable) return undefined
- return typeof draggable.id === "string" ? draggable.id : undefined
-}
-
export const displayName = (project: { name?: string; worktree: string }) =>
project.name || getFilename(project.worktree)
diff --git a/packages/app/src/pages/layout/sidebar-project-helpers.test.ts b/packages/app/src/pages/layout/sidebar-project-helpers.test.ts
deleted file mode 100644
index 75958d49e..000000000
--- a/packages/app/src/pages/layout/sidebar-project-helpers.test.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-import { describe, expect, test } from "bun:test"
-import { projectSelected, projectTileActive } from "./sidebar-project-helpers"
-
-describe("projectSelected", () => {
- test("matches direct worktree", () => {
- expect(projectSelected("/tmp/root", "/tmp/root")).toBe(true)
- })
-
- test("matches sandbox worktree", () => {
- expect(projectSelected("/tmp/branch", "/tmp/root", ["/tmp/branch"])).toBe(true)
- expect(projectSelected("/tmp/other", "/tmp/root", ["/tmp/branch"])).toBe(false)
- })
-})
-
-describe("projectTileActive", () => {
- test("menu state always wins", () => {
- expect(
- projectTileActive({
- menu: true,
- preview: false,
- open: false,
- overlay: false,
- worktree: "/tmp/root",
- }),
- ).toBe(true)
- })
-
- test("preview mode uses open state", () => {
- expect(
- projectTileActive({
- menu: false,
- preview: true,
- open: true,
- overlay: true,
- hoverProject: "/tmp/other",
- worktree: "/tmp/root",
- }),
- ).toBe(true)
- })
-
- test("overlay mode uses hovered project", () => {
- expect(
- projectTileActive({
- menu: false,
- preview: false,
- open: false,
- overlay: true,
- hoverProject: "/tmp/root",
- worktree: "/tmp/root",
- }),
- ).toBe(true)
- expect(
- projectTileActive({
- menu: false,
- preview: false,
- open: false,
- overlay: true,
- hoverProject: "/tmp/other",
- worktree: "/tmp/root",
- }),
- ).toBe(false)
- })
-})
diff --git a/packages/app/src/pages/layout/sidebar-project-helpers.ts b/packages/app/src/pages/layout/sidebar-project-helpers.ts
deleted file mode 100644
index 06d38a3cd..000000000
--- a/packages/app/src/pages/layout/sidebar-project-helpers.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-export const projectSelected = (currentDir: string, worktree: string, sandboxes?: string[]) =>
- worktree === currentDir || sandboxes?.includes(currentDir) === true
-
-export const projectTileActive = (args: {
- menu: boolean
- preview: boolean
- open: boolean
- overlay: boolean
- hoverProject?: string
- worktree: string
-}) => args.menu || (args.preview ? args.open : args.overlay && args.hoverProject === args.worktree)
diff --git a/packages/app/src/pages/layout/sidebar-project.tsx b/packages/app/src/pages/layout/sidebar-project.tsx
index 187cd2f33..551090fd5 100644
--- a/packages/app/src/pages/layout/sidebar-project.tsx
+++ b/packages/app/src/pages/layout/sidebar-project.tsx
@@ -12,7 +12,6 @@ import { useLanguage } from "@/context/language"
import { useNotification } from "@/context/notification"
import { ProjectIcon, SessionItem, type SessionItemProps } from "./sidebar-items"
import { childMapByParent, displayName, sortedRootSessions } from "./helpers"
-import { projectSelected, projectTileActive } from "./sidebar-project-helpers"
export type ProjectSidebarContext = {
currentDir: Accessor<string>
@@ -277,8 +276,10 @@ export const SortableProject = (props: {
const globalSync = useGlobalSync()
const language = useLanguage()
const sortable = createSortable(props.project.worktree)
- const selected = createMemo(() =>
- projectSelected(props.ctx.currentDir(), props.project.worktree, props.project.sandboxes),
+ const selected = createMemo(
+ () =>
+ props.project.worktree === props.ctx.currentDir() ||
+ props.project.sandboxes?.includes(props.ctx.currentDir()) === true,
)
const workspaces = createMemo(() => props.ctx.workspaceIds(props.project).slice(0, 2))
const workspaceEnabled = createMemo(() => props.ctx.workspacesEnabled(props.project))
@@ -291,15 +292,8 @@ export const SortableProject = (props: {
const preview = createMemo(() => !props.mobile && props.ctx.sidebarOpened())
const overlay = createMemo(() => !props.mobile && !props.ctx.sidebarOpened())
- const active = createMemo(() =>
- projectTileActive({
- menu: state.menu,
- preview: preview(),
- open: state.open,
- overlay: overlay(),
- hoverProject: props.ctx.hoverProject(),
- worktree: props.project.worktree,
- }),
+ const active = createMemo(
+ () => state.menu || (preview() ? state.open : overlay() && props.ctx.hoverProject() === props.project.worktree),
)
createEffect(() => {
diff --git a/packages/app/src/pages/layout/sidebar-shell-helpers.ts b/packages/app/src/pages/layout/sidebar-shell-helpers.ts
deleted file mode 100644
index 93c286c15..000000000
--- a/packages/app/src/pages/layout/sidebar-shell-helpers.ts
+++ /dev/null
@@ -1 +0,0 @@
-export const sidebarExpanded = (mobile: boolean | undefined, opened: boolean) => !!mobile || opened
diff --git a/packages/app/src/pages/layout/sidebar-shell.test.ts b/packages/app/src/pages/layout/sidebar-shell.test.ts
deleted file mode 100644
index 694025a65..000000000
--- a/packages/app/src/pages/layout/sidebar-shell.test.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { describe, expect, test } from "bun:test"
-import { sidebarExpanded } from "./sidebar-shell-helpers"
-
-describe("sidebarExpanded", () => {
- test("expands on mobile regardless of desktop open state", () => {
- expect(sidebarExpanded(true, false)).toBe(true)
- })
-
- test("follows desktop open state when not mobile", () => {
- expect(sidebarExpanded(false, true)).toBe(true)
- expect(sidebarExpanded(false, false)).toBe(false)
- })
-})
diff --git a/packages/app/src/pages/layout/sidebar-shell.tsx b/packages/app/src/pages/layout/sidebar-shell.tsx
index d3070e374..82be4f024 100644
--- a/packages/app/src/pages/layout/sidebar-shell.tsx
+++ b/packages/app/src/pages/layout/sidebar-shell.tsx
@@ -11,7 +11,6 @@ import { ConstrainDragXAxis } from "@/utils/solid-dnd"
import { IconButton } from "@opencode-ai/ui/icon-button"
import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
import { type LocalProject } from "@/context/layout"
-import { sidebarExpanded } from "./sidebar-shell-helpers"
export const SidebarContent = (props: {
mobile?: boolean
@@ -33,7 +32,7 @@ export const SidebarContent = (props: {
onOpenHelp: () => void
renderPanel: () => JSX.Element
}): JSX.Element => {
- const expanded = createMemo(() => sidebarExpanded(props.mobile, props.opened()))
+ const expanded = createMemo(() => !!props.mobile || props.opened())
const placement = () => (props.mobile ? "bottom" : "right")
let panel: HTMLDivElement | undefined
diff --git a/packages/app/src/pages/layout/sidebar-workspace-helpers.ts b/packages/app/src/pages/layout/sidebar-workspace-helpers.ts
deleted file mode 100644
index aa7cb480e..000000000
--- a/packages/app/src/pages/layout/sidebar-workspace-helpers.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export const workspaceOpenState = (expanded: Record<string, boolean>, directory: string, local: boolean) =>
- expanded[directory] ?? local
diff --git a/packages/app/src/pages/layout/sidebar-workspace.test.ts b/packages/app/src/pages/layout/sidebar-workspace.test.ts
deleted file mode 100644
index d71c39fc8..000000000
--- a/packages/app/src/pages/layout/sidebar-workspace.test.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { describe, expect, test } from "bun:test"
-import { workspaceOpenState } from "./sidebar-workspace-helpers"
-
-describe("workspaceOpenState", () => {
- test("defaults to local workspace open", () => {
- expect(workspaceOpenState({}, "/tmp/root", true)).toBe(true)
- })
-
- test("uses persisted expansion state when present", () => {
- expect(workspaceOpenState({ "/tmp/root": false }, "/tmp/root", true)).toBe(false)
- expect(workspaceOpenState({ "/tmp/branch": true }, "/tmp/branch", false)).toBe(true)
- })
-})
diff --git a/packages/app/src/pages/layout/sidebar-workspace.tsx b/packages/app/src/pages/layout/sidebar-workspace.tsx
index c317b9c5e..1a54fdd8a 100644
--- a/packages/app/src/pages/layout/sidebar-workspace.tsx
+++ b/packages/app/src/pages/layout/sidebar-workspace.tsx
@@ -144,8 +144,6 @@ const WorkspaceActions = (props: {
setMenuOpen: (open: boolean) => void
setPendingRename: (value: boolean) => void
sidebarHovering: Accessor<boolean>
- mobile?: boolean
- nav: Accessor<HTMLElement | undefined>
touch: Accessor<boolean>
language: ReturnType<typeof useLanguage>
workspaceValue: Accessor<string>
@@ -340,6 +338,22 @@ export const SortableWorkspace = (props: {
}
const workspaceEditActive = createMemo(() => props.ctx.editorOpen(`workspace:${props.directory}`))
+ const header = () => (
+ <WorkspaceHeader
+ local={local}
+ busy={busy}
+ open={open}
+ directory={props.directory}
+ language={language}
+ branch={() => workspaceStore.vcs?.branch}
+ workspaceValue={workspaceValue}
+ workspaceEditActive={workspaceEditActive}
+ InlineEditor={props.ctx.InlineEditor}
+ renameWorkspace={props.ctx.renameWorkspace}
+ setEditor={props.ctx.setEditor}
+ projectId={props.project.id}
+ />
+ )
const openWrapper = (value: boolean) => {
props.ctx.setWorkspaceExpanded(props.directory, value)
@@ -379,20 +393,7 @@ export const SortableWorkspace = (props: {
data-action="workspace-toggle"
data-workspace={base64Encode(props.directory)}
>
- <WorkspaceHeader
- local={local}
- busy={busy}
- open={open}
- directory={props.directory}
- language={language}
- branch={() => workspaceStore.vcs?.branch}
- workspaceValue={workspaceValue}
- workspaceEditActive={workspaceEditActive}
- InlineEditor={props.ctx.InlineEditor}
- renameWorkspace={props.ctx.renameWorkspace}
- setEditor={props.ctx.setEditor}
- projectId={props.project.id}
- />
+ {header()}
</Collapsible.Trigger>
}
>
@@ -401,20 +402,7 @@ export const SortableWorkspace = (props: {
menu.open ? "pr-16" : "pr-2"
} group-hover/workspace:pr-16 group-focus-within/workspace:pr-16`}
>
- <WorkspaceHeader
- local={local}
- busy={busy}
- open={open}
- directory={props.directory}
- language={language}
- branch={() => workspaceStore.vcs?.branch}
- workspaceValue={workspaceValue}
- workspaceEditActive={workspaceEditActive}
- InlineEditor={props.ctx.InlineEditor}
- renameWorkspace={props.ctx.renameWorkspace}
- setEditor={props.ctx.setEditor}
- projectId={props.project.id}
- />
+ {header()}
</div>
</Show>
<WorkspaceActions
@@ -426,8 +414,6 @@ export const SortableWorkspace = (props: {
setMenuOpen={(open) => setMenu("open", open)}
setPendingRename={(value) => setMenu("pendingRename", value)}
sidebarHovering={props.ctx.sidebarHovering}
- mobile={props.mobile}
- nav={props.ctx.nav}
touch={touch}
language={language}
workspaceValue={workspaceValue}
@@ -490,44 +476,18 @@ export const LocalWorkspace = (props: {
ref={(el) => props.ctx.setScrollContainerRef(el, props.mobile)}
class="size-full flex flex-col py-2 overflow-y-auto no-scrollbar [overflow-anchor:none]"
>
- <nav class="flex flex-col gap-1 px-3">
- <Show when={loading()}>
- <SessionSkeleton />
- </Show>
- <For each={sessions()}>
- {(session) => (
- <SessionItem
- session={session}
- slug={slug()}
- mobile={props.mobile}
- children={children()}
- sidebarExpanded={props.ctx.sidebarExpanded}
- sidebarHovering={props.ctx.sidebarHovering}
- nav={props.ctx.nav}
- hoverSession={props.ctx.hoverSession}
- setHoverSession={props.ctx.setHoverSession}
- clearHoverProjectSoon={props.ctx.clearHoverProjectSoon}
- prefetchSession={props.ctx.prefetchSession}
- archiveSession={props.ctx.archiveSession}
- />
- )}
- </For>
- <Show when={hasMore()}>
- <div class="relative w-full py-1">
- <Button
- variant="ghost"
- class="flex w-full text-left justify-start text-14-regular text-text-weak pl-9 pr-10"
- size="large"
- onClick={(e: MouseEvent) => {
- loadMore()
- ;(e.currentTarget as HTMLButtonElement).blur()
- }}
- >
- {language.t("common.loadMore")}
- </Button>
- </div>
- </Show>
- </nav>
+ <WorkspaceSessionList
+ slug={slug}
+ mobile={props.mobile}
+ ctx={props.ctx}
+ showNew={() => false}
+ loading={loading}
+ sessions={sessions}
+ children={children}
+ hasMore={hasMore}
+ loadMore={loadMore}
+ language={language}
+ />
</div>
)
}