Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
a593ed4
fix: disable telemetry in the php lsp server (#7649)
madflow Jan 10, 2026
5818600
chore: generate
actions-user Jan 10, 2026
afb1cad
fix(desktop): Allow Selecting Card Errors (#7506)
dbpolito Jan 10, 2026
b6b0097
fix(ui): allow text editing shortcuts in search dialogs on macOS (#7419)
zerone0x Jan 10, 2026
1a642a7
fix(desktop): remove split operation in serverDisplayName function (#…
OpeOginni Jan 10, 2026
a8f23fb
chore: generate
actions-user Jan 10, 2026
02b7eb5
feat: support configuring default server URL for desktop (#7363)
athal7 Jan 10, 2026
d5738f5
fix(grep): follow symlinks by default in ripgrep searches (#7501)
kdcokenny Jan 10, 2026
794c598
fix: exclude 'none' variant for gpt-5.2-codex (#7682)
Alejandro-CSt Jan 10, 2026
b41fbda
wip: black
fwang Jan 10, 2026
2d2a044
fix(desktop): build failing - missing import (#7697)
Leka74 Jan 10, 2026
f882cca
fix(desktop): add missing StoreExt import and fix unused mut warning …
jerome-benoit Jan 10, 2026
50ed4c6
feat(deskop): Add Copy to Messages (#7658)
dbpolito Jan 10, 2026
a9275de
feat(desktop): Make mouse hover / click work on prompt input autocomp…
dbpolito Jan 10, 2026
76386f5
feat(desktop): Fork Session (#7673)
dbpolito Jan 10, 2026
f94ee5c
core: extract external directory validation to shared utility to redu…
thdxr Jan 10, 2026
4752c83
feat: pass sessionID to chat.system.transform (#7718)
spoons-and-mirrors Jan 11, 2026
a457828
fix(opencode): command palette mouse hover highlights wrong item (#7721)
kitlangton Jan 11, 2026
44fa3d5
feat(acp): track file modified (#7723)
wid4t Jan 11, 2026
ee8b38a
fix: ensure /connect works for openai business plans too (was missing…
rekram1-node Jan 11, 2026
0cc3c3b
tweak: ensure codex built in plugin is always available
rekram1-node Jan 11, 2026
9280db3
fix(tui): move props.ref to onMount (#7702)
itsrainingmani Jan 11, 2026
efbab08
release: v1.1.13
Jan 11, 2026
aca0b1e
sync: merge upstream v1.1.13 into shuvcode-dev
shuv1337 Jan 11, 2026
ea9eb10
sync: record last synced tag v1.1.13
shuv1337 Jan 11, 2026
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
2 changes: 1 addition & 1 deletion .github/last-synced-tag
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v1.1.12
v1.1.13
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ docker/workspace
opencode-dev
logs/
.loop*
*.bun-build
30 changes: 15 additions & 15 deletions bun.lock
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
},
"packages/app": {
"name": "@opencode-ai/app",
"version": "1.1.12",
"version": "1.1.13",
"dependencies": {
"@kobalte/core": "catalog:",
"@opencode-ai/sdk": "workspace:*",
Expand Down Expand Up @@ -71,7 +71,7 @@
},
"packages/console/app": {
"name": "@opencode-ai/console-app",
"version": "1.1.12",
"version": "1.1.13",
"dependencies": {
"@cloudflare/vite-plugin": "1.15.2",
"@ibm/plex": "6.4.1",
Expand Down Expand Up @@ -100,7 +100,7 @@
},
"packages/console/core": {
"name": "@opencode-ai/console-core",
"version": "1.1.12",
"version": "1.1.13",
"dependencies": {
"@aws-sdk/client-sts": "3.782.0",
"@jsx-email/render": "1.1.1",
Expand All @@ -127,7 +127,7 @@
},
"packages/console/function": {
"name": "@opencode-ai/console-function",
"version": "1.1.12",
"version": "1.1.13",
"dependencies": {
"@ai-sdk/anthropic": "2.0.0",
"@ai-sdk/openai": "2.0.2",
Expand All @@ -151,7 +151,7 @@
},
"packages/console/mail": {
"name": "@opencode-ai/console-mail",
"version": "1.1.12",
"version": "1.1.13",
"dependencies": {
"@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3",
Expand All @@ -175,7 +175,7 @@
},
"packages/desktop": {
"name": "@shuvcode/desktop",
"version": "1.1.12",
"version": "1.1.13",
"dependencies": {
"@opencode-ai/app": "workspace:*",
"@opencode-ai/ui": "workspace:*",
Expand Down Expand Up @@ -204,7 +204,7 @@
},
"packages/enterprise": {
"name": "@opencode-ai/enterprise",
"version": "1.1.12",
"version": "1.1.13",
"dependencies": {
"@opencode-ai/ui": "workspace:*",
"@opencode-ai/util": "workspace:*",
Expand Down Expand Up @@ -233,7 +233,7 @@
},
"packages/function": {
"name": "@opencode-ai/function",
"version": "1.1.12",
"version": "1.1.13",
"dependencies": {
"@octokit/auth-app": "8.0.1",
"@octokit/rest": "catalog:",
Expand All @@ -249,7 +249,7 @@
},
"packages/opencode": {
"name": "opencode",
"version": "1.1.12",
"version": "1.1.13",
"bin": {
"opencode": "./bin/opencode",
},
Expand Down Expand Up @@ -353,7 +353,7 @@
},
"packages/plugin": {
"name": "@opencode-ai/plugin",
"version": "1.1.12",
"version": "1.1.13",
"dependencies": {
"@opencode-ai/sdk": "workspace:*",
"zod": "catalog:",
Expand All @@ -373,7 +373,7 @@
},
"packages/sdk/js": {
"name": "@opencode-ai/sdk",
"version": "1.1.12",
"version": "1.1.13",
"devDependencies": {
"@hey-api/openapi-ts": "0.88.1",
"@tsconfig/node22": "catalog:",
Expand All @@ -384,7 +384,7 @@
},
"packages/slack": {
"name": "@opencode-ai/slack",
"version": "1.1.12",
"version": "1.1.13",
"dependencies": {
"@opencode-ai/sdk": "workspace:*",
"@slack/bolt": "^3.17.1",
Expand All @@ -397,7 +397,7 @@
},
"packages/ui": {
"name": "@opencode-ai/ui",
"version": "1.1.12",
"version": "1.1.13",
"dependencies": {
"@kobalte/core": "catalog:",
"@opencode-ai/sdk": "workspace:*",
Expand Down Expand Up @@ -436,7 +436,7 @@
},
"packages/util": {
"name": "@opencode-ai/util",
"version": "1.1.12",
"version": "1.1.13",
"dependencies": {
"zod": "catalog:",
},
Expand All @@ -447,7 +447,7 @@
},
"packages/web": {
"name": "@opencode-ai/web",
"version": "1.1.12",
"version": "1.1.13",
"dependencies": {
"@astrojs/cloudflare": "12.6.3",
"@astrojs/markdown-remark": "6.3.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/app",
"version": "1.1.12",
"version": "1.1.13",
"description": "",
"type": "module",
"exports": {
Expand Down
17 changes: 11 additions & 6 deletions packages/app/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const Loading = () => <div class="size-full flex items-center justify-center tex
declare global {
interface Window {
__SHUVCODE__?: { updaterEnabled?: boolean; port?: number; serverReady?: boolean }
__OPENCODE__?: { updaterEnabled?: boolean; port?: number; serverReady?: boolean }
__OPENCODE__?: { updaterEnabled?: boolean; port?: number; serverReady?: boolean; serverUrl?: string }
}
}

Expand All @@ -43,18 +43,23 @@ const defaultServerUrl = iife(() => {
const param = new URLSearchParams(document.location.search).get("url")
if (param) return param

// 2. Known production hosts -> localhost (same as upstream + shuv.ai)
// 2. Configured server URL (from desktop settings)
if (window.__OPENCODE__?.serverUrl) return window.__OPENCODE__.serverUrl

// 3. Known production hosts -> localhost (same as upstream + shuv.ai)
if (location.hostname.includes("opencode.ai") || location.hostname.includes("shuv.ai"))
return "http://localhost:4096"

// 3. Desktop app (Tauri) with injected port
// 4. Desktop app (Tauri) with injected port
if (window.__SHUVCODE__?.port) return `http://127.0.0.1:${window.__SHUVCODE__.port}`
if (window.__OPENCODE__?.port) return `http://127.0.0.1:${window.__OPENCODE__.port}`

// 4. Dev mode -> same-origin so Vite proxy handles LAN access + CORS
if (import.meta.env.DEV) return window.location.origin
// 5. Dev mode -> same-origin so Vite proxy handles LAN access + CORS
if (import.meta.env.DEV) {
return `http://${import.meta.env.VITE_OPENCODE_SERVER_HOST ?? "localhost"}:${import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096"}`
}

// 5. Default -> same origin (production web command)
// 6. Default -> same origin (production web command)
return window.location.origin
})

Expand Down
99 changes: 99 additions & 0 deletions packages/app/src/components/dialog-fork.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { Component, createMemo } from "solid-js"
import { useNavigate, useParams } from "@solidjs/router"
import { useSync } from "@/context/sync"
import { useSDK } from "@/context/sdk"
import { usePrompt } from "@/context/prompt"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { Dialog } from "@opencode-ai/ui/dialog"
import { List } from "@opencode-ai/ui/list"
import { extractPromptFromParts } from "@/utils/prompt"
import type { TextPart as SDKTextPart } from "@opencode-ai/sdk/v2/client"
import { base64Encode } from "@opencode-ai/util/encode"

interface ForkableMessage {
id: string
text: string
time: string
}

function formatTime(date: Date): string {
return date.toLocaleTimeString(undefined, { timeStyle: "short" })
}

export const DialogFork: Component = () => {
const params = useParams()
const navigate = useNavigate()
const sync = useSync()
const sdk = useSDK()
const prompt = usePrompt()
const dialog = useDialog()

const messages = createMemo((): ForkableMessage[] => {
const sessionID = params.id
if (!sessionID) return []

const msgs = sync.data.message[sessionID] ?? []
const result: ForkableMessage[] = []

for (const message of msgs) {
if (message.role !== "user") continue

const parts = sync.data.part[message.id] ?? []
const textPart = parts.find((x): x is SDKTextPart => x.type === "text" && !x.synthetic && !x.ignored)
if (!textPart) continue

result.push({
id: message.id,
text: textPart.text.replace(/\n/g, " ").slice(0, 200),
time: formatTime(new Date(message.time.created)),
})
}

return result.reverse()
})

const handleSelect = (item: ForkableMessage | undefined) => {
if (!item) return

const sessionID = params.id
if (!sessionID) return

const parts = sync.data.part[item.id] ?? []
const restored = extractPromptFromParts(parts, { directory: sdk.directory })

dialog.close()

sdk.client.session.fork({ sessionID, messageID: item.id }).then((forked) => {
if (!forked.data) return
navigate(`/${base64Encode(sdk.directory)}/session/${forked.data.id}`)
requestAnimationFrame(() => {
prompt.set(restored)
})
})
}

return (
<Dialog title="Fork from message">
<List
class="flex-1 min-h-0 [&_[data-slot=list-scroll]]:flex-1 [&_[data-slot=list-scroll]]:min-h-0"
search={{ placeholder: "Search", autofocus: true }}
emptyMessage="No messages to fork from"
key={(x) => x.id}
items={messages}
filterKeys={["text"]}
onSelect={handleSelect}
>
{(item) => (
<div class="w-full flex items-center gap-2">
<span class="truncate flex-1 min-w-0 text-left" style={{ "font-weight": "400" }}>
{item.text}
</span>
<span class="text-text-weak shrink-0" style={{ "font-weight": "400" }}>
{item.time}
</span>
</div>
)}
</List>
</Dialog>
)
}
51 changes: 50 additions & 1 deletion packages/app/src/components/dialog-select-server.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createEffect, createMemo, onCleanup } from "solid-js"
import { createResource, createEffect, createMemo, onCleanup, Show } from "solid-js"
import { createStore, reconcile } from "solid-js/store"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { Dialog } from "@opencode-ai/ui/dialog"
Expand Down Expand Up @@ -35,6 +35,8 @@ export function DialogSelectServer() {
error: "",
status: {} as Record<string, ServerStatus | undefined>,
})
const [defaultUrl, defaultUrlActions] = createResource(() => platform.getDefaultServerUrl?.())
const isDesktop = platform.platform === "desktop"

const items = createMemo(() => {
const current = server.url
Expand Down Expand Up @@ -173,6 +175,53 @@ export function DialogSelectServer() {
</div>
</form>
</div>

<Show when={isDesktop}>
<div class="mt-6 px-3 flex flex-col gap-1.5">
<div class="px-3">
<h3 class="text-14-regular text-text-weak">Default server</h3>
<p class="text-12-regular text-text-weak mt-1">
Connect to this server on app launch instead of starting a local server. Requires restart.
</p>
</div>
<div class="flex items-center gap-2 px-3 py-2">
<Show
when={defaultUrl()}
fallback={
<Show
when={server.url}
fallback={<span class="text-14-regular text-text-weak">No server selected</span>}
>
<Button
variant="secondary"
size="small"
onClick={async () => {
await platform.setDefaultServerUrl?.(server.url)
defaultUrlActions.refetch(server.url)
}}
>
Set current server as default
</Button>
</Show>
}
>
<div class="flex items-center gap-2 flex-1 min-w-0">
<span class="truncate text-14-regular">{serverDisplayName(defaultUrl()!)}</span>
</div>
<Button
variant="ghost"
size="small"
onClick={async () => {
await platform.setDefaultServerUrl?.(null)
defaultUrlActions.refetch()
}}
>
Clear
</Button>
</Show>
</div>
</div>
</Show>
</div>
</Dialog>
)
Expand Down
Loading
Loading