fix(tui): filter only connected workspaces in dialog; add warp synthetic message (#25915)

This commit is contained in:
James Long 2026-05-06 10:25:42 -04:00 committed by GitHub
parent aa3c99a3c0
commit 2abc4507b2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 98 additions and 18 deletions

View file

@ -33,6 +33,28 @@ 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 }
}
export function warpReminderText(dir: string) {
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>`
}
async function loadWorkspaceAdapters(input: {
sdk: ReturnType<typeof useSDK>
sync: ReturnType<typeof useSync>
@ -77,7 +99,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)
@ -93,10 +115,30 @@ export async function warpWorkspaceSession(input: {
await input.sync.bootstrap({ fatal: false }).catch(() => undefined)
const dir = input.project.instance.directory() || input.sync.path.directory
if (dir) {
await input.sdk.client.session
.promptAsync({
sessionID: input.sessionID,
workspace: input.workspaceID ?? undefined,
noReply: true,
parts: [
{
type: "text",
text: warpReminderText(dir),
synthetic: true,
},
],
})
.catch(() => undefined)
}
await Promise.all([input.project.workspace.sync(), input.sync.session.refresh()])
input.done?.()
if (input.done) return true
if (input.done) {
input.done()
return true
}
input.dialog.clear()
return true
}
@ -125,15 +167,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,
@ -158,12 +196,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",
},
]
: []),
]
})

View file

@ -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"])
})
})