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
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,24 @@ export type WorkspaceSelection =
type WorkspaceSelectValue = WorkspaceSelection | { type: "existing-list" }
type ExistingWorkspaceSelectValue = { workspace: Workspace }

export function recentConnectedWorkspaces<WorkspaceInfo extends { id: string }>(input: {
sessions: readonly { workspaceID?: string; time: { updated: number } }[]
get: (workspaceID: string) => WorkspaceInfo | undefined
status: (workspaceID: string) => string | undefined
limit?: number
}) {
const workspaces = input.sessions
.toSorted((a, b) => b.time.updated - a.time.updated)
.flatMap((session) => {
const workspace = session.workspaceID ? input.get(session.workspaceID) : undefined
return workspace && input.status(workspace.id) === "connected" ? [workspace] : []
})
.filter((workspace, index, list) => list.findIndex((item) => item.id === workspace.id) === index)
const recent = workspaces.slice(0, input.limit ?? 3)

return { recent, hasMore: recent.length < workspaces.length }
}

async function loadWorkspaceAdapters(input: {
sdk: ReturnType<typeof useSDK>
sync: ReturnType<typeof useSync>
Expand Down Expand Up @@ -77,7 +95,7 @@ export async function warpWorkspaceSession(input: {
}): Promise<boolean> {
const result = await input.sdk.client.experimental.workspace
.warp({
id: input.workspaceID ?? undefined,
id: input.workspaceID,
sessionID: input.sessionID,
})
.catch(() => undefined)
Expand Down Expand Up @@ -125,15 +143,11 @@ export function DialogWorkspaceSelect(props: {
const options = createMemo<DialogSelectOption<WorkspaceSelectValue>[]>(() => {
const list = adapters()
if (!list) return []
const recent = sync.data.session
.toSorted((a, b) => b.time.updated - a.time.updated)
.flatMap((session) => (session.workspaceID ? [session.workspaceID] : []))
.filter((workspaceID, index, list) => list.indexOf(workspaceID) === index)
.flatMap((workspaceID) => {
const workspace = project.workspace.get(workspaceID)
return workspace && project.workspace.status(workspace.id) === "connected" ? [workspace] : []
})
.slice(0, 3)
const { recent, hasMore } = recentConnectedWorkspaces({
sessions: sync.data.session,
get: project.workspace.get,
status: project.workspace.status,
})
return [
...list.map((adapter) => ({
title: adapter.name,
Expand All @@ -158,12 +172,16 @@ export function DialogWorkspaceSelect(props: {
},
category: "Choose workspace",
})),
{
title: "View all workspaces",
value: { type: "existing-list" as const },
description: "Choose from all workspaces",
category: "Choose workspace",
},
...(hasMore
? [
{
title: "View all workspaces",
value: { type: "existing-list" as const },
description: "Choose from all workspaces",
category: "Choose workspace",
},
]
: []),
]
})

Expand Down
38 changes: 38 additions & 0 deletions packages/opencode/test/cli/cmd/tui/dialog-workspace-create.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { describe, expect, test } from "bun:test"
import { recentConnectedWorkspaces } from "../../../../src/cli/cmd/tui/component/dialog-workspace-create"

describe("recentConnectedWorkspaces", () => {
test("returns unique connected workspaces after filtering missing and inactive entries", () => {
const workspaces = [
{ id: "wrk_a", name: "alpha" },
{ id: "wrk_b", name: "beta" },
{ id: "wrk_c", name: "gamma" },
{ id: "wrk_d", name: "delta" },
{ id: "wrk_e", name: "epsilon" },
]
const status = {
wrk_a: "connected",
wrk_b: "disconnected",
wrk_c: "error",
wrk_d: "connected",
wrk_e: "connected",
} as const

const { recent } = recentConnectedWorkspaces({
sessions: [
{ time: { updated: 900 } },
{ workspaceID: "wrk_b", time: { updated: 800 } },
{ workspaceID: "wrk_a", time: { updated: 700 } },
{ workspaceID: "wrk_a", time: { updated: 600 } },
{ workspaceID: "wrk_missing", time: { updated: 500 } },
{ workspaceID: "wrk_c", time: { updated: 400 } },
{ workspaceID: "wrk_d", time: { updated: 300 } },
{ workspaceID: "wrk_e", time: { updated: 200 } },
],
get: (workspaceID) => workspaces.find((workspace) => workspace.id === workspaceID),
status: (workspaceID) => status[workspaceID as keyof typeof status],
})

expect(recent.map((workspace) => workspace.id)).toEqual(["wrk_a", "wrk_d", "wrk_e"])
})
})
9 changes: 8 additions & 1 deletion packages/sdk/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -8405,7 +8405,14 @@
"type": "object",
"properties": {
"id": {
"type": "string"
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"sessionID": {
"type": "string"
Expand Down
Loading