Skip to content
Merged
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
40 changes: 38 additions & 2 deletions packages/app/e2e/app/composer-workspace-chip.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { expect, test } from "../fixtures"
import { withSession } from "../actions"
import { createTestProject, withSession } from "../actions"
import { promptSelector } from "../selectors"
import { dirSlug } from "../utils"
import path from "node:path"

test("workspace chip popover opens on click", async ({ page, project }) => {
await project.open()
Expand All @@ -19,10 +22,43 @@ test("active workspace has a check icon", async ({ page, project }) => {
await chip.click()

const popover = page.getByRole("menu")
const active = popover.locator("button").filter({ has: page.locator('[data-icon="check"]') })
const active = popover.getByRole("menuitemradio", { checked: true })
await expect(active).toHaveCount(1)
})

test("homepage draft stays visible while workspace chip changes the send target", async ({ page, project, backend, assistant }) => {
const other = await createTestProject({ serverUrl: backend.url })
await project.open({ extra: [other] })
project.trackDirectory(other)

const draft = `https://x.com/paulg/status/${Date.now()}`
const prompt = page.locator(promptSelector).first()
await prompt.click()
await page.keyboard.type(draft)
await expect.poll(async () => (await prompt.textContent())?.replace(/\u200B/g, "").trim()).toBe(draft)

await page.locator('[data-action="prompt-workspace"]').click()
await page.getByRole("menuitemradio", { name: path.basename(other) }).click()
await expect(page).toHaveURL(new RegExp(`/${dirSlug(other)}/session`))
await expect.poll(async () => (await prompt.textContent())?.replace(/\u200B/g, "").trim()).toBe(draft)

await assistant.reply("ok")
await page.getByRole("button", { name: "Send" }).first().click()
await expect
.poll(
async () =>
page.evaluate(() => {
const sent = (window as any).__opencode_e2e?.prompt?.sent
return sent?.directory
}),
{ timeout: 90_000 },
)
.toBe(other)

const sessionID = await page.evaluate(() => (window as any).__opencode_e2e?.prompt?.sent?.sessionID)
if (sessionID) project.trackSession(sessionID, other)
})

test("workspace chip hidden in session", async ({ page, sdk, gotoSession }) => {
await withSession(sdk, `e2e ws-chip hidden ${Date.now()}`, async (session) => {
await gotoSession(session.id)
Expand Down
5 changes: 3 additions & 2 deletions packages/app/src/components/prompt-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -365,11 +365,12 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
items={contextItems()}
active={(item) => {
const active = comments.active()
return !!item.commentID && item.commentID === active?.id && item.path === active?.file
const commentPath = item.commentPath ?? item.path
return !!item.commentID && item.commentID === active?.id && commentPath === active?.file
}}
openComment={openComment}
remove={(item) => {
if (item.commentID) comments.remove(item.path, item.commentID)
if (item.commentID) comments.remove(item.commentPath ?? item.path, item.commentID)
prompt.context.remove(item.key)
}}
t={(key) => language.t(key as Parameters<typeof language.t>[0])}
Expand Down
13 changes: 7 additions & 6 deletions packages/app/src/components/prompt-input/comment-routing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export interface CommentRoutingDeps {

export interface CommentRouting {
recent: () => string[]
openComment: (item: { path: string; commentID?: string; commentOrigin?: "review" | "file" }) => void
openComment: (item: { path: string; commentPath?: string; commentID?: string; commentOrigin?: "review" | "file" }) => void
}

export function createCommentRouting(deps: CommentRoutingDeps): CommentRouting {
Expand All @@ -43,14 +43,15 @@ export function createCommentRouting(deps: CommentRoutingDeps): CommentRouting {
return aggregate.files.some((file) => file.restoreState === "applied" && (file.openPath ?? file.path) === path)
}

const openComment = (item: { path: string; commentID?: string; commentOrigin?: "review" | "file" }) => {
const openComment = (item: { path: string; commentPath?: string; commentID?: string; commentOrigin?: "review" | "file" }) => {
// Belt-and-suspenders: reject external absolute paths so we never try to
// open a same-named file that happens to live inside the current workspace.
if (isAbsoluteLike(item.path) && !isUnderDirectory(item.path, sdk.directory)) return

if (!item.commentID) return

const focus = { file: item.path, id: item.commentID }
const commentPath = item.commentPath ?? item.path
const focus = { file: commentPath, id: item.commentID }
comments.setActive(focus)

const queueCommentFocus = (attempts = 6) => {
Expand All @@ -70,7 +71,7 @@ export function createCommentRouting(deps: CommentRoutingDeps): CommentRouting {
schedule(attempts)
}

const wantsReview = item.commentOrigin === "review" || (item.commentOrigin !== "file" && commentInReview(item.path))
const wantsReview = item.commentOrigin === "review" || (item.commentOrigin !== "file" && commentInReview(commentPath))
view().sidePanel.openTab("review")
if (wantsReview) {
view().sidePanel.explorer.setTab("changes")
Expand All @@ -80,10 +81,10 @@ export function createCommentRouting(deps: CommentRoutingDeps): CommentRouting {
}

view().sidePanel.explorer.setTab("all")
const tab = files.tab(item.path)
const tab = files.tab(commentPath)
tabs().open(tab)
tabs().setActive(tab)
Promise.resolve(files.load(item.path)).finally(() => queueCommentFocus())
Promise.resolve(files.load(commentPath)).finally(() => queueCommentFocus())
}

const recent = createMemo(() => {
Expand Down
137 changes: 0 additions & 137 deletions packages/app/src/components/prompt-input/draft-carryover.test.ts

This file was deleted.

5 changes: 0 additions & 5 deletions packages/app/src/components/prompt-input/draft-carryover.ts

This file was deleted.

Loading
Loading