Skip to content

Commit d1d3e9e

Browse files
jlongsteroleksii-honchar
authored andcommitted
fix(tui): filter only connected workspaces in dialog; add warp synthetic message (anomalyco#25915)
1 parent 2b36e06 commit d1d3e9e

2 files changed

Lines changed: 98 additions & 18 deletions

File tree

packages/opencode/src/cli/cmd/tui/component/dialog-workspace-create.tsx

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,28 @@ export type WorkspaceSelection =
3333
type WorkspaceSelectValue = WorkspaceSelection | { type: "existing-list" }
3434
type ExistingWorkspaceSelectValue = { workspace: Workspace }
3535

36+
export function recentConnectedWorkspaces<WorkspaceInfo extends { id: string }>(input: {
37+
sessions: readonly { workspaceID?: string; time: { updated: number } }[]
38+
get: (workspaceID: string) => WorkspaceInfo | undefined
39+
status: (workspaceID: string) => string | undefined
40+
limit?: number
41+
}) {
42+
const workspaces = input.sessions
43+
.toSorted((a, b) => b.time.updated - a.time.updated)
44+
.flatMap((session) => {
45+
const workspace = session.workspaceID ? input.get(session.workspaceID) : undefined
46+
return workspace && input.status(workspace.id) === "connected" ? [workspace] : []
47+
})
48+
.filter((workspace, index, list) => list.findIndex((item) => item.id === workspace.id) === index)
49+
const recent = workspaces.slice(0, input.limit ?? 3)
50+
51+
return { recent, hasMore: recent.length < workspaces.length }
52+
}
53+
54+
export function warpReminderText(dir: string) {
55+
return `<system-reminder>The user has changed the current working directory to "${dir}". This is still the same project but at a possibly new location; take this into account when working with any files from now on.</system-reminder>`
56+
}
57+
3658
async function loadWorkspaceAdapters(input: {
3759
sdk: ReturnType<typeof useSDK>
3860
sync: ReturnType<typeof useSync>
@@ -77,7 +99,7 @@ export async function warpWorkspaceSession(input: {
7799
}): Promise<boolean> {
78100
const result = await input.sdk.client.experimental.workspace
79101
.warp({
80-
id: input.workspaceID ?? undefined,
102+
id: input.workspaceID,
81103
sessionID: input.sessionID,
82104
})
83105
.catch(() => undefined)
@@ -93,10 +115,30 @@ export async function warpWorkspaceSession(input: {
93115

94116
await input.sync.bootstrap({ fatal: false }).catch(() => undefined)
95117

118+
const dir = input.project.instance.directory() || input.sync.path.directory
119+
if (dir) {
120+
await input.sdk.client.session
121+
.promptAsync({
122+
sessionID: input.sessionID,
123+
workspace: input.workspaceID ?? undefined,
124+
noReply: true,
125+
parts: [
126+
{
127+
type: "text",
128+
text: warpReminderText(dir),
129+
synthetic: true,
130+
},
131+
],
132+
})
133+
.catch(() => undefined)
134+
}
135+
96136
await Promise.all([input.project.workspace.sync(), input.sync.session.refresh()])
97137

98-
input.done?.()
99-
if (input.done) return true
138+
if (input.done) {
139+
input.done()
140+
return true
141+
}
100142
input.dialog.clear()
101143
return true
102144
}
@@ -125,15 +167,11 @@ export function DialogWorkspaceSelect(props: {
125167
const options = createMemo<DialogSelectOption<WorkspaceSelectValue>[]>(() => {
126168
const list = adapters()
127169
if (!list) return []
128-
const recent = sync.data.session
129-
.toSorted((a, b) => b.time.updated - a.time.updated)
130-
.flatMap((session) => (session.workspaceID ? [session.workspaceID] : []))
131-
.filter((workspaceID, index, list) => list.indexOf(workspaceID) === index)
132-
.flatMap((workspaceID) => {
133-
const workspace = project.workspace.get(workspaceID)
134-
return workspace && project.workspace.status(workspace.id) === "connected" ? [workspace] : []
135-
})
136-
.slice(0, 3)
170+
const { recent, hasMore } = recentConnectedWorkspaces({
171+
sessions: sync.data.session,
172+
get: project.workspace.get,
173+
status: project.workspace.status,
174+
})
137175
return [
138176
...list.map((adapter) => ({
139177
title: adapter.name,
@@ -158,12 +196,16 @@ export function DialogWorkspaceSelect(props: {
158196
},
159197
category: "Choose workspace",
160198
})),
161-
{
162-
title: "View all workspaces",
163-
value: { type: "existing-list" as const },
164-
description: "Choose from all workspaces",
165-
category: "Choose workspace",
166-
},
199+
...(hasMore
200+
? [
201+
{
202+
title: "View all workspaces",
203+
value: { type: "existing-list" as const },
204+
description: "Choose from all workspaces",
205+
category: "Choose workspace",
206+
},
207+
]
208+
: []),
167209
]
168210
})
169211

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { describe, expect, test } from "bun:test"
2+
import { recentConnectedWorkspaces } from "../../../../src/cli/cmd/tui/component/dialog-workspace-create"
3+
4+
describe("recentConnectedWorkspaces", () => {
5+
test("returns unique connected workspaces after filtering missing and inactive entries", () => {
6+
const workspaces = [
7+
{ id: "wrk_a", name: "alpha" },
8+
{ id: "wrk_b", name: "beta" },
9+
{ id: "wrk_c", name: "gamma" },
10+
{ id: "wrk_d", name: "delta" },
11+
{ id: "wrk_e", name: "epsilon" },
12+
]
13+
const status = {
14+
wrk_a: "connected",
15+
wrk_b: "disconnected",
16+
wrk_c: "error",
17+
wrk_d: "connected",
18+
wrk_e: "connected",
19+
} as const
20+
21+
const { recent } = recentConnectedWorkspaces({
22+
sessions: [
23+
{ time: { updated: 900 } },
24+
{ workspaceID: "wrk_b", time: { updated: 800 } },
25+
{ workspaceID: "wrk_a", time: { updated: 700 } },
26+
{ workspaceID: "wrk_a", time: { updated: 600 } },
27+
{ workspaceID: "wrk_missing", time: { updated: 500 } },
28+
{ workspaceID: "wrk_c", time: { updated: 400 } },
29+
{ workspaceID: "wrk_d", time: { updated: 300 } },
30+
{ workspaceID: "wrk_e", time: { updated: 200 } },
31+
],
32+
get: (workspaceID) => workspaces.find((workspace) => workspace.id === workspaceID),
33+
status: (workspaceID) => status[workspaceID as keyof typeof status],
34+
})
35+
36+
expect(recent.map((workspace) => workspace.id)).toEqual(["wrk_a", "wrk_d", "wrk_e"])
37+
})
38+
})

0 commit comments

Comments
 (0)