Skip to content

Commit 5b19b03

Browse files
committed
feat(desktop): add desktop config file
1 parent e6cdc54 commit 5b19b03

15 files changed

Lines changed: 485 additions & 25 deletions

File tree

packages/app/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"type": "module",
66
"exports": {
77
".": "./src/index.ts",
8+
"./desktop-config": "./src/desktop-config.ts",
89
"./desktop-menu": "./src/desktop-menu.ts",
910
"./updater": "./src/updater.ts",
1011
"./wsl/types": "./src/wsl/types.ts",

packages/app/src/context/permission.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { PermissionRequest } from "@opencode-ai/sdk/v2/client"
55
import { Persist, persisted } from "@/utils/persist"
66
import { useServerSDK } from "@/context/server-sdk"
77
import { useServerSync } from "./server-sync"
8+
import { usePlatform } from "./platform"
89
import { useParams } from "@solidjs/router"
910
import { decode64 } from "@/utils/base64"
1011
import {
@@ -49,8 +50,12 @@ export const { use: usePermission, provider: PermissionProvider } = createSimple
4950
gate: false,
5051
init: () => {
5152
const params = useParams()
53+
const platform = usePlatform()
5254
const serverSDK = useServerSDK()
5355
const serverSync = useServerSync()
56+
const autoApprove = createMemo(
57+
() => platform.platform === "desktop" && platform.desktopConfig?.()?.permissions?.autoApprove === true,
58+
)
5459

5560
const permissionsEnabled = createMemo(() => {
5661
const directory = decode64(params.dir)
@@ -142,15 +147,18 @@ export const { use: usePermission, provider: PermissionProvider } = createSimple
142147
}
143148

144149
function isAutoAccepting(sessionID: string, directory?: string) {
150+
if (autoApprove()) return true
145151
const session = directory ? serverSync().child(directory, { bootstrap: false })[0].session : []
146152
return autoRespondsPermission(store.autoAccept, session, { sessionID }, directory)
147153
}
148154

149155
function isAutoAcceptingDirectory(directory: string) {
156+
if (autoApprove()) return true
150157
return isDirectoryAutoAccepting(store.autoAccept, directory)
151158
}
152159

153160
function shouldAutoRespond(permission: PermissionRequest, directory?: string) {
161+
if (autoApprove()) return true
154162
const session = directory ? serverSync().child(directory, { bootstrap: false })[0].session : []
155163
return autoRespondsPermission(store.autoAccept, session, permission, directory)
156164
}

packages/app/src/context/platform.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { DesktopMenuAction } from "../desktop-menu"
55
import { ServerConnection } from "./server"
66
import type { WslServersPlatform } from "../wsl/types"
77
import type { UpdaterPlatform } from "../updater"
8+
import type { DesktopConfig } from "../desktop-config"
89

910
type PickerPaths = string | string[] | null
1011
type OpenDirectoryPickerOptions = { title?: string; multiple?: boolean }
@@ -111,6 +112,9 @@ type PlatformBase = {
111112

112113
/** Record a fatal renderer error in platform logs (desktop only) */
113114
recordFatalRendererError?(error: FatalRendererErrorLog): Promise<void>
115+
116+
/** Desktop-only machine config loaded from ~/.config/opencode/desktop.json */
117+
desktopConfig?: Accessor<DesktopConfig | undefined>
114118
}
115119

116120
export type Platform = PlatformBase &

packages/app/src/context/settings.tsx

Lines changed: 92 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { createStore, reconcile } from "solid-js/store"
22
import { createEffect, createMemo } from "solid-js"
33
import { createSimpleContext } from "@opencode-ai/ui/context"
44
import { persisted } from "@/utils/persist"
5+
import { usePlatform } from "./platform"
56

67
export interface NotificationSettings {
78
agent: boolean
@@ -148,19 +149,42 @@ function withFallback<T>(read: () => T | undefined, fallback: T) {
148149
return createMemo(() => read() ?? fallback)
149150
}
150151

152+
function desktopFallback<T>(read: () => T | undefined, config: () => T | undefined, fallback: T) {
153+
return createMemo(() => config() ?? read() ?? fallback)
154+
}
155+
151156
export const { use: useSettings, provider: SettingsProvider } = createSimpleContext({
152157
name: "Settings",
153158
gate: false,
154159
init: () => {
160+
const platform = usePlatform()
161+
const desktopConfig = () => (platform.platform === "desktop" ? platform.desktopConfig?.() : undefined)
155162
const [store, setStore, _, ready] = persisted("settings.v3", createStore<Settings>(defaultSettings))
156-
const showFileTree = withFallback(() => store.general?.showFileTree, defaultSettings.general.showFileTree)
157-
const showSearch = withFallback(() => store.general?.showSearch, defaultSettings.general.showSearch)
158-
const showStatus = withFallback(() => store.general?.showStatus, defaultSettings.general.showStatus)
159-
const showCustomAgents = withFallback(
163+
const showFileTree = desktopFallback(
164+
() => store.general?.showFileTree,
165+
() => desktopConfig()?.general?.showFileTree,
166+
defaultSettings.general.showFileTree,
167+
)
168+
const showSearch = desktopFallback(
169+
() => store.general?.showSearch,
170+
() => desktopConfig()?.general?.showSearch,
171+
defaultSettings.general.showSearch,
172+
)
173+
const showStatus = desktopFallback(
174+
() => store.general?.showStatus,
175+
() => desktopConfig()?.general?.showStatus,
176+
defaultSettings.general.showStatus,
177+
)
178+
const showCustomAgents = desktopFallback(
160179
() => store.general?.showCustomAgents,
180+
() => desktopConfig()?.general?.showCustomAgents,
161181
defaultSettings.general.showCustomAgents,
162182
)
163-
const newLayoutDesigns = withFallback(() => store.general?.newLayoutDesigns, newLayoutDesignsDefault)
183+
const newLayoutDesigns = desktopFallback(
184+
() => store.general?.newLayoutDesigns,
185+
() => desktopConfig()?.general?.newLayoutDesigns,
186+
newLayoutDesignsDefault,
187+
)
164188
const visible = (preference: () => boolean) => createMemo(() => !newLayoutDesigns() || preference())
165189

166190
createEffect(() => {
@@ -181,16 +205,26 @@ export const { use: useSettings, provider: SettingsProvider } = createSimpleCont
181205
return store
182206
},
183207
general: {
184-
autoSave: withFallback(() => store.general?.autoSave, defaultSettings.general.autoSave),
208+
autoSave: desktopFallback(
209+
() => store.general?.autoSave,
210+
() => desktopConfig()?.general?.autoSave,
211+
defaultSettings.general.autoSave,
212+
),
185213
setAutoSave(value: boolean) {
186214
setStore("general", "autoSave", value)
187215
},
188-
releaseNotes: withFallback(() => store.general?.releaseNotes, defaultSettings.general.releaseNotes),
216+
releaseNotes: desktopFallback(
217+
() => store.general?.releaseNotes,
218+
() => desktopConfig()?.general?.releaseNotes,
219+
defaultSettings.general.releaseNotes,
220+
),
189221
setReleaseNotes(value: boolean) {
190222
setStore("general", "releaseNotes", value)
191223
},
192-
followup: withFallback(
224+
followup: desktopFallback(
193225
() => (store.general?.followup === "queue" ? "steer" : store.general?.followup),
226+
() =>
227+
desktopConfig()?.general?.followup === "queue" ? "steer" : desktopConfig()?.general?.followup,
194228
defaultSettings.general.followup,
195229
),
196230
setFollowup(value: "queue" | "steer") {
@@ -200,7 +234,11 @@ export const { use: useSettings, provider: SettingsProvider } = createSimpleCont
200234
setShowFileTree(value: boolean) {
201235
setStore("general", "showFileTree", value)
202236
},
203-
showNavigation: withFallback(() => store.general?.showNavigation, defaultSettings.general.showNavigation),
237+
showNavigation: desktopFallback(
238+
() => store.general?.showNavigation,
239+
() => desktopConfig()?.general?.showNavigation,
240+
defaultSettings.general.showNavigation,
241+
),
204242
setShowNavigation(value: boolean) {
205243
setStore("general", "showNavigation", value)
206244
},
@@ -212,33 +250,41 @@ export const { use: useSettings, provider: SettingsProvider } = createSimpleCont
212250
setShowStatus(value: boolean) {
213251
setStore("general", "showStatus", value)
214252
},
215-
showTerminal: withFallback(() => store.general?.showTerminal, defaultSettings.general.showTerminal),
253+
showTerminal: desktopFallback(
254+
() => store.general?.showTerminal,
255+
() => desktopConfig()?.general?.showTerminal,
256+
defaultSettings.general.showTerminal,
257+
),
216258
setShowTerminal(value: boolean) {
217259
setStore("general", "showTerminal", value)
218260
},
219-
showReasoningSummaries: withFallback(
261+
showReasoningSummaries: desktopFallback(
220262
() => store.general?.showReasoningSummaries,
263+
() => desktopConfig()?.general?.showReasoningSummaries,
221264
defaultSettings.general.showReasoningSummaries,
222265
),
223266
setShowReasoningSummaries(value: boolean) {
224267
setStore("general", "showReasoningSummaries", value)
225268
},
226-
shellToolPartsExpanded: withFallback(
269+
shellToolPartsExpanded: desktopFallback(
227270
() => store.general?.shellToolPartsExpanded,
271+
() => desktopConfig()?.general?.shellToolPartsExpanded,
228272
defaultSettings.general.shellToolPartsExpanded,
229273
),
230274
setShellToolPartsExpanded(value: boolean) {
231275
setStore("general", "shellToolPartsExpanded", value)
232276
},
233-
editToolPartsExpanded: withFallback(
277+
editToolPartsExpanded: desktopFallback(
234278
() => store.general?.editToolPartsExpanded,
279+
() => desktopConfig()?.general?.editToolPartsExpanded,
235280
defaultSettings.general.editToolPartsExpanded,
236281
),
237282
setEditToolPartsExpanded(value: boolean) {
238283
setStore("general", "editToolPartsExpanded", value)
239284
},
240-
showSessionProgressBar: withFallback(
285+
showSessionProgressBar: desktopFallback(
241286
() => store.general?.showSessionProgressBar,
287+
() => desktopConfig()?.general?.showSessionProgressBar,
242288
defaultSettings.general.showSessionProgressBar,
243289
),
244290
setShowSessionProgressBar(value: boolean) {
@@ -295,7 +341,11 @@ export const { use: useSettings, provider: SettingsProvider } = createSimpleCont
295341
},
296342
},
297343
permissions: {
298-
autoApprove: withFallback(() => store.permissions?.autoApprove, defaultSettings.permissions.autoApprove),
344+
autoApprove: desktopFallback(
345+
() => store.permissions?.autoApprove,
346+
() => desktopConfig()?.permissions?.autoApprove,
347+
defaultSettings.permissions.autoApprove,
348+
),
299349
setAutoApprove(value: boolean) {
300350
setStore("permissions", "autoApprove", value)
301351
},
@@ -315,30 +365,51 @@ export const { use: useSettings, provider: SettingsProvider } = createSimpleCont
315365
},
316366
},
317367
sounds: {
318-
agentEnabled: withFallback(() => store.sounds?.agentEnabled, defaultSettings.sounds.agentEnabled),
368+
agentEnabled: desktopFallback(
369+
() => store.sounds?.agentEnabled,
370+
() => desktopConfig()?.sounds?.agentEnabled,
371+
defaultSettings.sounds.agentEnabled,
372+
),
319373
setAgentEnabled(value: boolean) {
320374
setStore("sounds", "agentEnabled", value)
321375
},
322-
agent: withFallback(() => store.sounds?.agent, defaultSettings.sounds.agent),
376+
agent: desktopFallback(
377+
() => store.sounds?.agent,
378+
() => desktopConfig()?.sounds?.agent,
379+
defaultSettings.sounds.agent,
380+
),
323381
setAgent(value: string) {
324382
setStore("sounds", "agent", value)
325383
},
326-
permissionsEnabled: withFallback(
384+
permissionsEnabled: desktopFallback(
327385
() => store.sounds?.permissionsEnabled,
386+
() => desktopConfig()?.sounds?.permissionsEnabled,
328387
defaultSettings.sounds.permissionsEnabled,
329388
),
330389
setPermissionsEnabled(value: boolean) {
331390
setStore("sounds", "permissionsEnabled", value)
332391
},
333-
permissions: withFallback(() => store.sounds?.permissions, defaultSettings.sounds.permissions),
392+
permissions: desktopFallback(
393+
() => store.sounds?.permissions,
394+
() => desktopConfig()?.sounds?.permissions,
395+
defaultSettings.sounds.permissions,
396+
),
334397
setPermissions(value: string) {
335398
setStore("sounds", "permissions", value)
336399
},
337-
errorsEnabled: withFallback(() => store.sounds?.errorsEnabled, defaultSettings.sounds.errorsEnabled),
400+
errorsEnabled: desktopFallback(
401+
() => store.sounds?.errorsEnabled,
402+
() => desktopConfig()?.sounds?.errorsEnabled,
403+
defaultSettings.sounds.errorsEnabled,
404+
),
338405
setErrorsEnabled(value: boolean) {
339406
setStore("sounds", "errorsEnabled", value)
340407
},
341-
errors: withFallback(() => store.sounds?.errors, defaultSettings.sounds.errors),
408+
errors: desktopFallback(
409+
() => store.sounds?.errors,
410+
() => desktopConfig()?.sounds?.errors,
411+
defaultSettings.sounds.errors,
412+
),
342413
setErrors(value: string) {
343414
setStore("sounds", "errors", value)
344415
},

0 commit comments

Comments
 (0)