Skip to content

Commit fbbab9d

Browse files
jayairBrendonovich
andauthored
feat(app): hide desktop titlebar tools behind settings (#19029)
Co-authored-by: Brendan Allan <git@brendonovich.dev> Co-authored-by: Brendan Allan <brendonovich@outlook.com>
1 parent cccb907 commit fbbab9d

8 files changed

Lines changed: 290 additions & 141 deletions

File tree

packages/app/src/components/session/session-header.tsx

Lines changed: 61 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@ import { Spinner } from "@opencode-ai/ui/spinner"
88
import { showToast } from "@opencode-ai/ui/toast"
99
import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
1010
import { getFilename } from "@opencode-ai/shared/util/path"
11-
import { createEffect, createMemo, For, Show } from "solid-js"
11+
import { createEffect, createMemo, createSignal, For, onMount, Show } from "solid-js"
1212
import { createStore } from "solid-js/store"
1313
import { Portal } from "solid-js/web"
1414
import { useCommand } from "@/context/command"
1515
import { useLanguage } from "@/context/language"
1616
import { useLayout } from "@/context/layout"
1717
import { usePlatform } from "@/context/platform"
1818
import { useServer } from "@/context/server"
19+
import { useSettings } from "@/context/settings"
1920
import { useSync } from "@/context/sync"
2021
import { useTerminal } from "@/context/terminal"
2122
import { focusTerminalById } from "@/pages/session/helpers"
@@ -134,6 +135,7 @@ export function SessionHeader() {
134135
const server = useServer()
135136
const platform = usePlatform()
136137
const language = useLanguage()
138+
const settings = useSettings()
137139
const sync = useSync()
138140
const terminal = useTerminal()
139141
const { params, view } = useSessionLayout()
@@ -151,6 +153,11 @@ export function SessionHeader() {
151153
})
152154
const hotkey = createMemo(() => command.keybind("file.open"))
153155
const os = createMemo(() => detectOS(platform))
156+
const isDesktopBeta = platform.platform === "desktop" && import.meta.env.VITE_OPENCODE_CHANNEL === "beta"
157+
const search = createMemo(() => !isDesktopBeta || settings.general.showSearch())
158+
const tree = createMemo(() => !isDesktopBeta || settings.general.showFileTree())
159+
const term = createMemo(() => !isDesktopBeta || settings.general.showTerminal())
160+
const status = createMemo(() => !isDesktopBeta || settings.general.showStatus())
154161

155162
const [exists, setExists] = createStore<Partial<Record<OpenApp, boolean>>>({
156163
finder: true,
@@ -262,12 +269,16 @@ export function SessionHeader() {
262269
.catch((err: unknown) => showRequestError(language, err))
263270
}
264271

265-
const centerMount = createMemo(() => document.getElementById("opencode-titlebar-center"))
266-
const rightMount = createMemo(() => document.getElementById("opencode-titlebar-right"))
272+
const [centerMount, setCenterMount] = createSignal<HTMLElement | null>(null)
273+
const [rightMount, setRightMount] = createSignal<HTMLElement | null>(null)
274+
onMount(() => {
275+
setCenterMount(document.getElementById("opencode-titlebar-center"))
276+
setRightMount(document.getElementById("opencode-titlebar-right"))
277+
})
267278

268279
return (
269280
<>
270-
<Show when={centerMount()}>
281+
<Show when={search() && centerMount()}>
271282
{(mount) => (
272283
<Portal mount={mount()}>
273284
<Button
@@ -415,24 +426,28 @@ export function SessionHeader() {
415426
</div>
416427
</Show>
417428
<div class="flex items-center gap-1">
418-
<Tooltip placement="bottom" value={language.t("status.popover.trigger")}>
419-
<StatusPopover />
420-
</Tooltip>
421-
<TooltipKeybind
422-
title={language.t("command.terminal.toggle")}
423-
keybind={command.keybind("terminal.toggle")}
424-
>
425-
<Button
426-
variant="ghost"
427-
class="group/terminal-toggle titlebar-icon w-8 h-6 p-0 box-border shrink-0"
428-
onClick={toggleTerminal}
429-
aria-label={language.t("command.terminal.toggle")}
430-
aria-expanded={view().terminal.opened()}
431-
aria-controls="terminal-panel"
429+
<Show when={status()}>
430+
<Tooltip placement="bottom" value={language.t("status.popover.trigger")}>
431+
<StatusPopover />
432+
</Tooltip>
433+
</Show>
434+
<Show when={term()}>
435+
<TooltipKeybind
436+
title={language.t("command.terminal.toggle")}
437+
keybind={command.keybind("terminal.toggle")}
432438
>
433-
<Icon size="small" name={view().terminal.opened() ? "terminal-active" : "terminal"} />
434-
</Button>
435-
</TooltipKeybind>
439+
<Button
440+
variant="ghost"
441+
class="group/terminal-toggle titlebar-icon w-8 h-6 p-0 box-border shrink-0"
442+
onClick={toggleTerminal}
443+
aria-label={language.t("command.terminal.toggle")}
444+
aria-expanded={view().terminal.opened()}
445+
aria-controls="terminal-panel"
446+
>
447+
<Icon size="small" name={view().terminal.opened() ? "terminal-active" : "terminal"} />
448+
</Button>
449+
</TooltipKeybind>
450+
</Show>
436451

437452
<div class="hidden md:flex items-center gap-1 shrink-0">
438453
<TooltipKeybind
@@ -451,30 +466,32 @@ export function SessionHeader() {
451466
</Button>
452467
</TooltipKeybind>
453468

454-
<TooltipKeybind
455-
title={language.t("command.fileTree.toggle")}
456-
keybind={command.keybind("fileTree.toggle")}
457-
>
458-
<Button
459-
variant="ghost"
460-
class="titlebar-icon w-8 h-6 p-0 box-border"
461-
onClick={() => layout.fileTree.toggle()}
462-
aria-label={language.t("command.fileTree.toggle")}
463-
aria-expanded={layout.fileTree.opened()}
464-
aria-controls="file-tree-panel"
469+
<Show when={tree()}>
470+
<TooltipKeybind
471+
title={language.t("command.fileTree.toggle")}
472+
keybind={command.keybind("fileTree.toggle")}
465473
>
466-
<div class="relative flex items-center justify-center size-4">
467-
<Icon
468-
size="small"
469-
name={layout.fileTree.opened() ? "file-tree-active" : "file-tree"}
470-
classList={{
471-
"text-icon-strong": layout.fileTree.opened(),
472-
"text-icon-weak": !layout.fileTree.opened(),
473-
}}
474-
/>
475-
</div>
476-
</Button>
477-
</TooltipKeybind>
474+
<Button
475+
variant="ghost"
476+
class="titlebar-icon w-8 h-6 p-0 box-border"
477+
onClick={() => layout.fileTree.toggle()}
478+
aria-label={language.t("command.fileTree.toggle")}
479+
aria-expanded={layout.fileTree.opened()}
480+
aria-controls="file-tree-panel"
481+
>
482+
<div class="relative flex items-center justify-center size-4">
483+
<Icon
484+
size="small"
485+
name={layout.fileTree.opened() ? "file-tree-active" : "file-tree"}
486+
classList={{
487+
"text-icon-strong": layout.fileTree.opened(),
488+
"text-icon-weak": !layout.fileTree.opened(),
489+
}}
490+
/>
491+
</div>
492+
</Button>
493+
</TooltipKeybind>
494+
</Show>
478495
</div>
479496
</div>
480497
</div>

packages/app/src/components/settings-general.tsx

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ export const SettingsGeneral: Component = () => {
106106

107107
permission.disableAutoAccept(params.id, value)
108108
}
109+
const desktop = createMemo(() => platform.platform === "desktop")
109110

110111
const check = () => {
111112
if (!platform.checkUpdate) return
@@ -279,6 +280,74 @@ export const SettingsGeneral: Component = () => {
279280
</div>
280281
)
281282

283+
const AdvancedSection = () => (
284+
<div class="flex flex-col gap-1">
285+
<h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.general.section.advanced")}</h3>
286+
287+
<SettingsList>
288+
<SettingsRow
289+
title={language.t("settings.general.row.showFileTree.title")}
290+
description={language.t("settings.general.row.showFileTree.description")}
291+
>
292+
<div data-action="settings-show-file-tree">
293+
<Switch
294+
checked={settings.general.showFileTree()}
295+
onChange={(checked) => settings.general.setShowFileTree(checked)}
296+
/>
297+
</div>
298+
</SettingsRow>
299+
300+
<SettingsRow
301+
title={language.t("settings.general.row.showNavigation.title")}
302+
description={language.t("settings.general.row.showNavigation.description")}
303+
>
304+
<div data-action="settings-show-navigation">
305+
<Switch
306+
checked={settings.general.showNavigation()}
307+
onChange={(checked) => settings.general.setShowNavigation(checked)}
308+
/>
309+
</div>
310+
</SettingsRow>
311+
312+
<SettingsRow
313+
title={language.t("settings.general.row.showSearch.title")}
314+
description={language.t("settings.general.row.showSearch.description")}
315+
>
316+
<div data-action="settings-show-search">
317+
<Switch
318+
checked={settings.general.showSearch()}
319+
onChange={(checked) => settings.general.setShowSearch(checked)}
320+
/>
321+
</div>
322+
</SettingsRow>
323+
324+
<SettingsRow
325+
title={language.t("settings.general.row.showTerminal.title")}
326+
description={language.t("settings.general.row.showTerminal.description")}
327+
>
328+
<div data-action="settings-show-terminal">
329+
<Switch
330+
checked={settings.general.showTerminal()}
331+
onChange={(checked) => settings.general.setShowTerminal(checked)}
332+
/>
333+
</div>
334+
</SettingsRow>
335+
336+
<SettingsRow
337+
title={language.t("settings.general.row.showStatus.title")}
338+
description={language.t("settings.general.row.showStatus.description")}
339+
>
340+
<div data-action="settings-show-status">
341+
<Switch
342+
checked={settings.general.showStatus()}
343+
onChange={(checked) => settings.general.setShowStatus(checked)}
344+
/>
345+
</div>
346+
</SettingsRow>
347+
</SettingsList>
348+
</div>
349+
)
350+
282351
const AppearanceSection = () => (
283352
<div class="flex flex-col gap-1">
284353
<h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.general.section.appearance")}</h3>
@@ -527,6 +596,7 @@ export const SettingsGeneral: Component = () => {
527596
</div>
528597
)
529598

599+
console.log(import.meta.env)
530600
return (
531601
<div class="flex flex-col h-full overflow-y-auto no-scrollbar px-4 pb-10 sm:px-10 sm:pb-10">
532602
<div class="sticky top-0 z-10 bg-[linear-gradient(to_bottom,var(--surface-stronger-non-alpha)_calc(100%_-_24px),transparent)]">
@@ -609,6 +679,10 @@ export const SettingsGeneral: Component = () => {
609679
)
610680
}}
611681
</Show>
682+
683+
<Show when={desktop() && import.meta.env.VITE_OPENCODE_CHANNEL === "beta"}>
684+
<AdvancedSection />
685+
</Show>
612686
</div>
613687
</div>
614688
)

packages/app/src/components/titlebar.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { useLayout } from "@/context/layout"
1111
import { usePlatform } from "@/context/platform"
1212
import { useCommand } from "@/context/command"
1313
import { useLanguage } from "@/context/language"
14+
import { useSettings } from "@/context/settings"
1415
import { applyPath, backPath, forwardPath } from "./titlebar-history"
1516

1617
type TauriDesktopWindow = {
@@ -40,6 +41,7 @@ export function Titlebar() {
4041
const platform = usePlatform()
4142
const command = useCommand()
4243
const language = useLanguage()
44+
const settings = useSettings()
4345
const theme = useTheme()
4446
const navigate = useNavigate()
4547
const location = useLocation()
@@ -78,6 +80,7 @@ export function Titlebar() {
7880
const canBack = createMemo(() => history.index > 0)
7981
const canForward = createMemo(() => history.index < history.stack.length - 1)
8082
const hasProjects = createMemo(() => layout.projects.list().length > 0)
83+
const nav = createMemo(() => import.meta.env.VITE_OPENCODE_CHANNEL !== "beta" || settings.general.showNavigation())
8184

8285
const back = () => {
8386
const next = backPath(history)
@@ -255,13 +258,12 @@ export function Titlebar() {
255258
<div
256259
class="flex items-center shrink-0"
257260
classList={{
258-
"translate-x-0": !layout.sidebar.opened(),
259-
"-translate-x-[36px]": layout.sidebar.opened(),
261+
"-translate-x-[36px]": layout.sidebar.opened() && !!params.dir,
260262
"duration-180 ease-out": !layout.sidebar.opened(),
261263
"duration-180 ease-in": layout.sidebar.opened(),
262264
}}
263265
>
264-
<Show when={hasProjects()}>
266+
<Show when={hasProjects() && nav()}>
265267
<div class="flex items-center gap-0 transition-transform">
266268
<Tooltip placement="bottom" value={language.t("common.goBack")} openDelay={2000}>
267269
<Button

packages/app/src/context/settings.tsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ export interface Settings {
2323
autoSave: boolean
2424
releaseNotes: boolean
2525
followup: "queue" | "steer"
26+
showFileTree: boolean
27+
showNavigation: boolean
28+
showSearch: boolean
29+
showStatus: boolean
30+
showTerminal: boolean
2631
showReasoningSummaries: boolean
2732
shellToolPartsExpanded: boolean
2833
editToolPartsExpanded: boolean
@@ -89,6 +94,11 @@ const defaultSettings: Settings = {
8994
autoSave: true,
9095
releaseNotes: true,
9196
followup: "steer",
97+
showFileTree: false,
98+
showNavigation: false,
99+
showSearch: false,
100+
showStatus: false,
101+
showTerminal: false,
92102
showReasoningSummaries: false,
93103
shellToolPartsExpanded: false,
94104
editToolPartsExpanded: false,
@@ -162,6 +172,26 @@ export const { use: useSettings, provider: SettingsProvider } = createSimpleCont
162172
setFollowup(value: "queue" | "steer") {
163173
setStore("general", "followup", value === "queue" ? "steer" : value)
164174
},
175+
showFileTree: withFallback(() => store.general?.showFileTree, defaultSettings.general.showFileTree),
176+
setShowFileTree(value: boolean) {
177+
setStore("general", "showFileTree", value)
178+
},
179+
showNavigation: withFallback(() => store.general?.showNavigation, defaultSettings.general.showNavigation),
180+
setShowNavigation(value: boolean) {
181+
setStore("general", "showNavigation", value)
182+
},
183+
showSearch: withFallback(() => store.general?.showSearch, defaultSettings.general.showSearch),
184+
setShowSearch(value: boolean) {
185+
setStore("general", "showSearch", value)
186+
},
187+
showStatus: withFallback(() => store.general?.showStatus, defaultSettings.general.showStatus),
188+
setShowStatus(value: boolean) {
189+
setStore("general", "showStatus", value)
190+
},
191+
showTerminal: withFallback(() => store.general?.showTerminal, defaultSettings.general.showTerminal),
192+
setShowTerminal(value: boolean) {
193+
setStore("general", "showTerminal", value)
194+
},
165195
showReasoningSummaries: withFallback(
166196
() => store.general?.showReasoningSummaries,
167197
defaultSettings.general.showReasoningSummaries,

packages/app/src/env.d.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
1-
import "solid-js"
2-
31
interface ImportMetaEnv {
42
readonly VITE_OPENCODE_SERVER_HOST: string
53
readonly VITE_OPENCODE_SERVER_PORT: string
6-
readonly OPENCODE_CHANNEL?: "dev" | "beta" | "prod"
4+
readonly VITE_OPENCODE_CHANNEL?: "dev" | "beta" | "prod"
75
}
86

97
interface ImportMeta {
108
readonly env: ImportMetaEnv
119
}
1210

13-
declare module "solid-js" {
11+
export declare module "solid-js" {
1412
namespace JSX {
1513
interface Directives {
1614
sortable: true

packages/app/src/i18n/en.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,7 @@ export const dict = {
719719
"settings.desktop.wsl.description": "Run the OpenCode server inside WSL on Windows.",
720720

721721
"settings.general.section.appearance": "Appearance",
722+
"settings.general.section.advanced": "Advanced",
722723
"settings.general.section.notifications": "System notifications",
723724
"settings.general.section.updates": "Updates",
724725
"settings.general.section.sounds": "Sound effects",
@@ -741,6 +742,16 @@ export const dict = {
741742
"settings.general.row.followup.description": "Choose whether follow-up prompts steer immediately or wait in a queue",
742743
"settings.general.row.followup.option.queue": "Queue",
743744
"settings.general.row.followup.option.steer": "Steer",
745+
"settings.general.row.showFileTree.title": "File tree",
746+
"settings.general.row.showFileTree.description": "Show the file tree toggle and panel in desktop sessions",
747+
"settings.general.row.showNavigation.title": "Navigation controls",
748+
"settings.general.row.showNavigation.description": "Show the back and forward buttons in the desktop title bar",
749+
"settings.general.row.showSearch.title": "Command palette",
750+
"settings.general.row.showSearch.description": "Show the search and command palette button in the desktop title bar",
751+
"settings.general.row.showTerminal.title": "Terminal",
752+
"settings.general.row.showTerminal.description": "Show the terminal button in the desktop title bar",
753+
"settings.general.row.showStatus.title": "Server status",
754+
"settings.general.row.showStatus.description": "Show the server status button in the desktop title bar",
744755
"settings.general.row.reasoningSummaries.title": "Show reasoning summaries",
745756
"settings.general.row.reasoningSummaries.description": "Display model reasoning summaries in the timeline",
746757
"settings.general.row.shellToolPartsExpanded.title": "Expand shell tool parts",

0 commit comments

Comments
 (0)