Skip to content

Commit 7866bf8

Browse files
authored
Merge branch 'dev' into feat/free-model-resolution
2 parents b59c2c9 + efb6cc2 commit 7866bf8

70 files changed

Lines changed: 4837 additions & 1252 deletions

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: 1 addition & 0 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-F1luclnqCPQk9yxfmeSYGaM/nScf28yBu9K3Fv+Xd24=",
4-
"aarch64-linux": "sha256-XW0XZnsCRkU3MFJH9TjMRYZHffzVy3cQyiNCkec2gl4=",
5-
"aarch64-darwin": "sha256-bf8kvORs3Fs2UYLp3PekF+AJR7NKOcHb+fIQA79RtMk=",
6-
"x86_64-darwin": "sha256-sBdQPkzd7JXNW6Lbi9JHiAsfHwdLwTKWY+uPeXAv2Nw="
3+
"x86_64-linux": "sha256-XWoSYsV18a5uvjM3KuyGQWiFAtcCV0ij8brOLuFOW+g=",
4+
"aarch64-linux": "sha256-50cI0CuzzMRO8vznPxTHRhgHirE7siOg6AmyKLhZ2oo=",
5+
"aarch64-darwin": "sha256-5JpSBNyKqiB+gOrkxMNWyWGWLdNlbEiirA56GDhAS4E=",
6+
"x86_64-darwin": "sha256-X1r9aOSEdMUqWtqa4a/tBK2nRSwIWfKOwqJfpFknhII="
77
}
88
}

packages/app/e2e/regression/prompt-thinking-level.spec.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,18 +54,15 @@ test("shows the V2 thinking level control while relevant", async ({ page }) => {
5454
})
5555

5656
await page.goto(`/${base64Encode(directory)}/session/${sessionID}`)
57-
const composer = page.locator('[data-component="session-composer"]')
57+
const composer = page.locator('[data-component="prompt-input-v2"]')
5858
const input = composer.locator('[data-component="prompt-input"]')
59-
const control = composer.locator('[data-component="prompt-variant-control"]')
59+
const control = composer.locator('button[title="Choose model variant"]')
6060
await expectAppVisible(composer)
6161

6262
await idleComposer(page)
63-
await expect(control).toBeHidden()
64-
65-
await composer.hover()
6663
await expect(control).toBeVisible()
6764

68-
await control.locator('[data-action="prompt-model-variant"]').click()
65+
await control.click()
6966
const high = page.getByRole("menuitemradio", { name: "high" })
7067
await expect(high).toBeVisible()
7168
await page.mouse.move(0, 0)

packages/app/e2e/smoke/session-timeline.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -736,5 +736,5 @@ async function switchTitlebarSession(page: Page, sessionID: string, title: strin
736736
}
737737

738738
async function expectSessionReady(page: Page) {
739-
await expectAppVisible(page.getByRole("textbox", { name: /Ask anything/i }))
739+
await expectAppVisible(page.getByRole("textbox", { name: "Prompt" }))
740740
}

packages/app/src/app.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,16 @@ import { Font } from "@opencode-ai/ui/font"
99
import { Splash } from "@opencode-ai/ui/logo"
1010
import { ThemeProvider } from "@opencode-ai/ui/theme/context"
1111
import { MetaProvider } from "@solidjs/meta"
12-
import { type BaseRouterProps, Navigate, Route, Router, useNavigate, useParams, useSearchParams } from "@solidjs/router"
12+
import {
13+
type BaseRouterProps,
14+
Navigate,
15+
Route,
16+
Router,
17+
useLocation,
18+
useNavigate,
19+
useParams,
20+
useSearchParams,
21+
} from "@solidjs/router"
1322
import { QueryClient, QueryClientProvider } from "@tanstack/solid-query"
1423
import { Effect } from "effect"
1524
import { base64Encode } from "@opencode-ai/core/util/encode"
@@ -29,6 +38,7 @@ import {
2938
Show,
3039
} from "solid-js"
3140
import { Dynamic } from "solid-js/web"
41+
import { makeEventListener } from "@solid-primitives/event-listener"
3242
import { CommandProvider, useCommand, type CommandOption } from "@/context/command"
3343
import { CommentsProvider } from "@/context/comments"
3444
import { FileProvider } from "@/context/file"

packages/app/src/components/debug-bar.tsx

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { makeEventListener } from "@solid-primitives/event-listener"
55
import { Tooltip } from "@opencode-ai/ui/tooltip"
66
import { TooltipV2 } from "@opencode-ai/ui/v2/tooltip-v2"
77
import { useLanguage } from "@/context/language"
8+
import { usePlatform } from "@/context/platform"
89

910
type Mem = Performance & {
1011
memory?: {
@@ -107,15 +108,53 @@ function Cell(props: {
107108
)
108109
}
109110

111+
function FocusCell(props: { active: boolean; inline?: boolean; onClick: () => void }) {
112+
const content = () => (
113+
<button
114+
type="button"
115+
aria-label="Force focus styles on all interactive elements"
116+
aria-pressed={props.active}
117+
classList={{
118+
"flex min-w-0 items-center font-mono uppercase hover:bg-surface-raised-base focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-border-focus": true,
119+
"min-h-[20px] w-fit flex-row justify-start gap-1.5 rounded px-1.5 py-0.5 text-left": !!props.inline,
120+
"min-h-[42px] w-full flex-col justify-center rounded-[8px] px-0.5 py-1 text-center": !props.inline,
121+
"bg-surface-raised-base text-text-strong": props.active,
122+
}}
123+
onClick={props.onClick}
124+
>
125+
<span class="text-[10px] leading-none font-black tracking-[0.04em] opacity-70">FOCUS</span>
126+
<span classList={{ "leading-none font-bold": true, "text-[11px]": !!props.inline, "text-[13px]": !props.inline }}>
127+
{props.active ? "ON" : "OFF"}
128+
</span>
129+
</button>
130+
)
131+
132+
if (props.inline) {
133+
return (
134+
<TooltipV2 value="Force focus styles on all interactive elements" placement="top">
135+
{content()}
136+
</TooltipV2>
137+
)
138+
}
139+
140+
return (
141+
<Tooltip value="Force focus styles on all interactive elements" placement="top">
142+
{content()}
143+
</Tooltip>
144+
)
145+
}
146+
110147
export function DebugBar(props: { inline?: boolean } = {}) {
111148
const language = useLanguage()
149+
const platform = usePlatform()
112150
const location = useLocation()
113151
const routing = useIsRouting()
114152
const [state, setState] = createStore({
115153
cls: undefined as number | undefined,
116154
delay: undefined as number | undefined,
117155
fps: undefined as number | undefined,
118156
gap: undefined as number | undefined,
157+
focus: false,
119158
heap: {
120159
limit: undefined as number | undefined,
121160
used: undefined as number | undefined,
@@ -142,6 +181,16 @@ export function DebugBar(props: { inline?: boolean } = {}) {
142181
}
143182
const longv = () => (state.long.count === undefined ? na() : `${time(state.long.block) ?? na()}/${state.long.count}`)
144183
const navv = () => (state.nav.pending ? "..." : (time(state.nav.dur) ?? na()))
184+
const toggleFocus = async () => {
185+
if (!platform.setForceFocus) return
186+
const enabled = !state.focus
187+
await platform.setForceFocus(enabled)
188+
setState("focus", enabled)
189+
}
190+
191+
onCleanup(() => {
192+
if (state.focus) void platform.setForceFocus?.(false).catch(() => undefined)
193+
})
145194

146195
let prev = ""
147196
let start = 0
@@ -490,8 +539,11 @@ export function DebugBar(props: { inline?: boolean } = {}) {
490539
bad={bad(heap(), 0.8)}
491540
dim={state.heap.used === undefined}
492541
inline={props.inline}
493-
wide
542+
wide={!platform.setForceFocus}
494543
/>
544+
{platform.setForceFocus && (
545+
<FocusCell active={state.focus} inline={props.inline} onClick={() => void toggleFocus()} />
546+
)}
495547
</div>
496548
</aside>
497549
)
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// @ts-nocheck
2+
import { Button } from "@opencode-ai/ui/button"
3+
import { useDialog } from "@opencode-ai/ui/context/dialog"
4+
import { QueryClient, QueryClientProvider } from "@tanstack/solid-query"
5+
import { mockProviderAuth } from "@/context/server-sync"
6+
import { onCleanup, onMount } from "solid-js"
7+
import { DialogConnectProvider, useProviderConnectController } from "./dialog-connect-provider"
8+
9+
function ConnectProviderDialogStory() {
10+
const dialog = useDialog()
11+
const open = () => dialog.show(() => <DialogConnectProvider />)
12+
13+
onMount(open)
14+
15+
return (
16+
<Button variant="secondary" onClick={open}>
17+
Open connect provider dialog
18+
</Button>
19+
)
20+
}
21+
22+
function ProviderConnectionDialogStory(props) {
23+
onCleanup(mockProviderAuth(props.provider, props.methods))
24+
const dialog = useDialog()
25+
const controller = useProviderConnectController()
26+
controller.select(props.provider)
27+
const open = () => dialog.show(() => <DialogConnectProvider controller={controller} />)
28+
29+
onMount(open)
30+
31+
return (
32+
<Button variant="secondary" onClick={open}>
33+
Open {props.provider} connection dialog
34+
</Button>
35+
)
36+
}
37+
38+
function renderConnection(provider, methods) {
39+
return () => (
40+
<QueryClientProvider client={new QueryClient()}>
41+
<ProviderConnectionDialogStory provider={provider} methods={methods} />
42+
</QueryClientProvider>
43+
)
44+
}
45+
46+
export default {
47+
title: "App/Dialogs/Connect Provider",
48+
id: "app-dialog-connect-provider",
49+
}
50+
51+
export const V2 = {
52+
render: () => (
53+
<QueryClientProvider client={new QueryClient()}>
54+
<ConnectProviderDialogStory />
55+
</QueryClientProvider>
56+
),
57+
}
58+
59+
export const ApiKey = {
60+
render: renderConnection("openrouter", [{ type: "api", label: "API key" }]),
61+
}
62+
63+
export const OpenCodeZen = {
64+
render: renderConnection("opencode", [{ type: "api", label: "API key" }]),
65+
}
66+
67+
export const LoginMethods = {
68+
render: renderConnection("openai", [
69+
{ type: "oauth", label: "ChatGPT Pro/Plus (browser)" },
70+
{ type: "oauth", label: "ChatGPT Pro/Plus (headless)" },
71+
{ type: "api", label: "API key" },
72+
]),
73+
}

0 commit comments

Comments
 (0)