Skip to content

Commit 5d5e581

Browse files
committed
Merge remote-tracking branch 'upstream/dev' into dev
# Conflicts: # packages/app/src/pages/session.tsx
2 parents 02da307 + 6b31188 commit 5d5e581

211 files changed

Lines changed: 13427 additions & 4035 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

bun.lock

Lines changed: 195 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

nix/hashes.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"nodeModules": {
3-
"x86_64-linux": "sha256-3hfy6nfEnGq4J6inH0pXANw05oas+81iuayn7J0pj9c=",
4-
"aarch64-linux": "sha256-dxWaLtzSeI5NfHwB6u0K10yxoA0ESz/r+zTEQ3FdKFY=",
5-
"aarch64-darwin": "sha256-kkK4rj4g0j2jJFXVmVH7CJcXlI8Dj/KmL/VC3iE4Z+8=",
6-
"x86_64-darwin": "sha256-jt51irxZd48kb0BItd8InP7lfsELUh0unVYO2es+a98="
3+
"x86_64-linux": "sha256-dZoLhWe4smBsOF7WczMySLXSAB1YRO1vfhiOCL1rBf0=",
4+
"aarch64-linux": "sha256-J7nIz1xuVZEHun5WRZkYRySz29B0A8g5g0RRxnIWTYU=",
5+
"aarch64-darwin": "sha256-R2PuhX+EjUBuLE8MF0G0fcUwNaU+5n6V6uVeK89ulzw=",
6+
"x86_64-darwin": "sha256-Bvzfz9TsTpYriZNLSLgpNcNb+BgtkgpjoWqdOtF2IBg="
77
}
88
}

packages/app/e2e/files/file-tree.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ test("file tree can expand folders and open a file", async ({ page, gotoSession
4343
await tab.click()
4444
await expect(tab).toHaveAttribute("aria-selected", "true")
4545

46-
const code = page.locator('[data-component="code"]').first()
47-
await expect(code).toBeVisible()
48-
await expect(code).toContainText("export default function FileTree")
46+
const viewer = page.locator('[data-component="file"][data-mode="text"]').first()
47+
await expect(viewer).toBeVisible()
48+
await expect(viewer).toContainText("export default function FileTree")
4949
})

packages/app/e2e/files/file-viewer.spec.ts

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { test, expect } from "../fixtures"
22
import { promptSelector } from "../selectors"
3+
import { modKey } from "../utils"
34

45
test("smoke file viewer renders real file content", async ({ page, gotoSession }) => {
56
await gotoSession()
@@ -43,7 +44,60 @@ test("smoke file viewer renders real file content", async ({ page, gotoSession }
4344
await expect(tab).toBeVisible()
4445
await tab.click()
4546

46-
const code = page.locator('[data-component="code"]').first()
47-
await expect(code).toBeVisible()
48-
await expect(code.getByText(/"name"\s*:\s*"@opencode-ai\/app"/)).toBeVisible()
47+
const viewer = page.locator('[data-component="file"][data-mode="text"]').first()
48+
await expect(viewer).toBeVisible()
49+
await expect(viewer.getByText(/"name"\s*:\s*"@opencode-ai\/app"/)).toBeVisible()
50+
})
51+
52+
test("cmd+f opens text viewer search while prompt is focused", async ({ page, gotoSession }) => {
53+
await gotoSession()
54+
55+
await page.locator(promptSelector).click()
56+
await page.keyboard.type("/open")
57+
58+
const command = page.locator('[data-slash-id="file.open"]').first()
59+
await expect(command).toBeVisible()
60+
await page.keyboard.press("Enter")
61+
62+
const dialog = page
63+
.getByRole("dialog")
64+
.filter({ has: page.getByPlaceholder(/search files/i) })
65+
.first()
66+
await expect(dialog).toBeVisible()
67+
68+
const input = dialog.getByRole("textbox").first()
69+
await input.fill("package.json")
70+
71+
const items = dialog.locator('[data-slot="list-item"][data-key^="file:"]')
72+
let index = -1
73+
await expect
74+
.poll(
75+
async () => {
76+
const keys = await items.evaluateAll((nodes) => nodes.map((node) => node.getAttribute("data-key") ?? ""))
77+
index = keys.findIndex((key) => /packages[\\/]+app[\\/]+package\.json$/i.test(key.replace(/^file:/, "")))
78+
return index >= 0
79+
},
80+
{ timeout: 30_000 },
81+
)
82+
.toBe(true)
83+
84+
const item = items.nth(index)
85+
await expect(item).toBeVisible()
86+
await item.click()
87+
88+
await expect(dialog).toHaveCount(0)
89+
90+
const tab = page.getByRole("tab", { name: "package.json" })
91+
await expect(tab).toBeVisible()
92+
await tab.click()
93+
94+
const viewer = page.locator('[data-component="file"][data-mode="text"]').first()
95+
await expect(viewer).toBeVisible()
96+
97+
await page.locator(promptSelector).click()
98+
await page.keyboard.press(`${modKey}+f`)
99+
100+
const findInput = page.getByPlaceholder("Find")
101+
await expect(findInput).toBeVisible()
102+
await expect(findInput).toBeFocused()
49103
})

packages/app/e2e/projects/projects-switch.spec.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
sessionIDFromUrl,
1010
} from "../actions"
1111
import { projectSwitchSelector, promptSelector, workspaceItemSelector, workspaceNewSessionSelector } from "../selectors"
12-
import { createSdk, dirSlug } from "../utils"
12+
import { createSdk, dirSlug, sessionPath } from "../utils"
1313

1414
function slugFromUrl(url: string) {
1515
return /\/([^/]+)\/session(?:\/|$)/.exec(url)?.[1] ?? ""
@@ -51,7 +51,6 @@ test("switching back to a project opens the latest workspace session", async ({
5151

5252
const other = await createTestProject()
5353
const otherSlug = dirSlug(other)
54-
const stamp = Date.now()
5554
let rootDir: string | undefined
5655
let workspaceDir: string | undefined
5756
let sessionID: string | undefined
@@ -80,6 +79,7 @@ test("switching back to a project opens the latest workspace session", async ({
8079

8180
const workspaceSlug = slugFromUrl(page.url())
8281
workspaceDir = base64Decode(workspaceSlug)
82+
if (!workspaceDir) throw new Error(`Failed to decode workspace slug: ${workspaceSlug}`)
8383
await openSidebar(page)
8484

8585
const workspace = page.locator(workspaceItemSelector(workspaceSlug)).first()
@@ -92,15 +92,14 @@ test("switching back to a project opens the latest workspace session", async ({
9292

9393
await expect(page).toHaveURL(new RegExp(`/${workspaceSlug}/session(?:[/?#]|$)`))
9494

95-
const prompt = page.locator(promptSelector)
96-
await expect(prompt).toBeVisible()
97-
await prompt.fill(`project switch remembers workspace ${stamp}`)
98-
await prompt.press("Enter")
99-
100-
await expect.poll(() => sessionIDFromUrl(page.url()) ?? "", { timeout: 30_000 }).not.toBe("")
101-
const created = sessionIDFromUrl(page.url())
102-
if (!created) throw new Error(`Failed to parse session id from URL: ${page.url()}`)
95+
const created = await createSdk(workspaceDir)
96+
.session.create()
97+
.then((x) => x.data?.id)
98+
if (!created) throw new Error(`Failed to create session for workspace: ${workspaceDir}`)
10399
sessionID = created
100+
101+
await page.goto(sessionPath(workspaceDir, created))
102+
await expect(page.locator(promptSelector)).toBeVisible()
104103
await expect(page).toHaveURL(new RegExp(`/${workspaceSlug}/session/${created}(?:[/?#]|$)`))
105104

106105
await openSidebar(page)
@@ -114,7 +113,8 @@ test("switching back to a project opens the latest workspace session", async ({
114113
await expect(rootButton).toBeVisible()
115114
await rootButton.click()
116115

117-
await expect(page).toHaveURL(new RegExp(`/${workspaceSlug}/session/${created}(?:[/?#]|$)`))
116+
await expect.poll(() => sessionIDFromUrl(page.url()) ?? "").toBe(created)
117+
await expect(page).toHaveURL(new RegExp(`/session/${created}(?:[/?#]|$)`))
118118
},
119119
{ extra: [other] },
120120
)

packages/app/src/app.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
import "@/index.css"
2-
import { Code } from "@opencode-ai/ui/code"
2+
import { File } from "@opencode-ai/ui/file"
33
import { I18nProvider } from "@opencode-ai/ui/context"
4-
import { CodeComponentProvider } from "@opencode-ai/ui/context/code"
54
import { DialogProvider } from "@opencode-ai/ui/context/dialog"
6-
import { DiffComponentProvider } from "@opencode-ai/ui/context/diff"
5+
import { FileComponentProvider } from "@opencode-ai/ui/context/file"
76
import { MarkedProvider } from "@opencode-ai/ui/context/marked"
8-
import { Diff } from "@opencode-ai/ui/diff"
97
import { Font } from "@opencode-ai/ui/font"
108
import { ThemeProvider } from "@opencode-ai/ui/theme"
119
import { MetaProvider } from "@solidjs/meta"
@@ -122,9 +120,7 @@ export function AppBaseProviders(props: ParentProps) {
122120
<ErrorBoundary fallback={(error) => <ErrorPage error={error} />}>
123121
<DialogProvider>
124122
<MarkedProviderWithNativeParser>
125-
<DiffComponentProvider component={Diff}>
126-
<CodeComponentProvider component={Code}>{props.children}</CodeComponentProvider>
127-
</DiffComponentProvider>
123+
<FileComponentProvider component={File}>{props.children}</FileComponentProvider>
128124
</MarkedProviderWithNativeParser>
129125
</DialogProvider>
130126
</ErrorBoundary>

packages/app/src/components/dialog-select-model-unpaid.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,20 @@ export const DialogSelectModelUnpaid: Component = () => {
9797
<div class="w-full flex items-center gap-x-3">
9898
<ProviderIcon data-slot="list-item-extra-icon" id={i.id as IconName} />
9999
<span>{i.name}</span>
100+
<Show when={i.id === "opencode"}>
101+
<div class="text-14-regular text-text-weak">{language.t("dialog.provider.opencode.tagline")}</div>
102+
</Show>
100103
<Show when={i.id === "opencode"}>
101104
<Tag>{language.t("dialog.provider.tag.recommended")}</Tag>
102105
</Show>
106+
<Show when={i.id === "opencode-go"}>
107+
<>
108+
<div class="text-14-regular text-text-weak">
109+
{language.t("dialog.provider.opencodeGo.tagline")}
110+
</div>
111+
<Tag>{language.t("dialog.provider.tag.recommended")}</Tag>
112+
</>
113+
</Show>
103114
<Show when={i.id === "anthropic"}>
104115
<div class="text-14-regular text-text-weak">{language.t("dialog.provider.anthropic.note")}</div>
105116
</Show>

packages/app/src/components/dialog-select-provider.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export const DialogSelectProvider: Component = () => {
2929
if (id === "anthropic") return language.t("dialog.provider.anthropic.note")
3030
if (id === "openai") return language.t("dialog.provider.openai.note")
3131
if (id.startsWith("github-copilot")) return language.t("dialog.provider.copilot.note")
32+
if (id === "opencode-go") return language.t("dialog.provider.opencodeGo.tagline")
3233
}
3334

3435
return (
@@ -70,13 +71,19 @@ export const DialogSelectProvider: Component = () => {
7071
<div class="px-1.25 w-full flex items-center gap-x-3">
7172
<ProviderIcon data-slot="list-item-extra-icon" id={icon(i.id)} />
7273
<span>{i.name}</span>
74+
<Show when={i.id === "opencode"}>
75+
<div class="text-14-regular text-text-weak">{language.t("dialog.provider.opencode.tagline")}</div>
76+
</Show>
7377
<Show when={i.id === CUSTOM_ID}>
7478
<Tag>{language.t("settings.providers.tag.custom")}</Tag>
7579
</Show>
7680
<Show when={i.id === "opencode"}>
7781
<Tag>{language.t("dialog.provider.tag.recommended")}</Tag>
7882
</Show>
7983
<Show when={note(i.id)}>{(value) => <div class="text-14-regular text-text-weak">{value()}</div>}</Show>
84+
<Show when={i.id === "opencode-go"}>
85+
<Tag>{language.t("dialog.provider.tag.recommended")}</Tag>
86+
</Show>
8087
</div>
8188
)}
8289
</List>

0 commit comments

Comments
 (0)