diff --git a/packages/app/src/components/session/session-header.tsx b/packages/app/src/components/session/session-header.tsx index 495b323405..1c533dfd28 100644 --- a/packages/app/src/components/session/session-header.tsx +++ b/packages/app/src/components/session/session-header.tsx @@ -16,6 +16,7 @@ import { useLanguage } from "@/context/language" import { useLayout } from "@/context/layout" import { usePlatform } from "@/context/platform" import { useServer } from "@/context/server" +import { useSettings } from "@/context/settings" import { useSync } from "@/context/sync" import { useTerminal } from "@/context/terminal" import { focusTerminalById } from "@/pages/session/helpers" @@ -134,6 +135,7 @@ export function SessionHeader() { const server = useServer() const platform = usePlatform() const language = useLanguage() + const settings = useSettings() const sync = useSync() const terminal = useTerminal() const { params, view } = useSessionLayout() @@ -151,6 +153,9 @@ export function SessionHeader() { }) const hotkey = createMemo(() => command.keybind("file.open")) const os = createMemo(() => detectOS(platform)) + const tree = createMemo(() => platform.platform !== "desktop" || settings.general.showFileTree()) + const term = createMemo(() => platform.platform !== "desktop" || settings.general.showTerminal()) + const status = createMemo(() => platform.platform !== "desktop" || settings.general.showStatus()) const [exists, setExists] = createStore>>({ finder: true, @@ -415,24 +420,28 @@ export function SessionHeader() {
- - - - - - + + +
diff --git a/packages/app/src/components/settings-general.tsx b/packages/app/src/components/settings-general.tsx index f4b8198e7e..b9e2629608 100644 --- a/packages/app/src/components/settings-general.tsx +++ b/packages/app/src/components/settings-general.tsx @@ -74,6 +74,7 @@ export const SettingsGeneral: Component = () => { }) const linux = createMemo(() => platform.platform === "desktop" && platform.os === "linux") + const desktop = createMemo(() => platform.platform === "desktop") const check = () => { if (!platform.checkUpdate) return @@ -276,6 +277,50 @@ export const SettingsGeneral: Component = () => { ) + const AdvancedSection = () => ( +
+

{language.t("settings.general.section.advanced")}

+ + + +
+ settings.general.setShowFileTree(checked)} + /> +
+
+ + +
+ settings.general.setShowTerminal(checked)} + /> +
+
+ + +
+ settings.general.setShowStatus(checked)} + /> +
+
+
+
+ ) + const AppearanceSection = () => (

{language.t("settings.general.section.appearance")}

@@ -587,6 +632,10 @@ export const SettingsGeneral: Component = () => { ) }} + + + +
) diff --git a/packages/app/src/context/settings.tsx b/packages/app/src/context/settings.tsx index eddd752eb4..8f5ba101ff 100644 --- a/packages/app/src/context/settings.tsx +++ b/packages/app/src/context/settings.tsx @@ -23,6 +23,9 @@ export interface Settings { autoSave: boolean releaseNotes: boolean followup: "queue" | "steer" + showFileTree: boolean + showStatus: boolean + showTerminal: boolean showReasoningSummaries: boolean shellToolPartsExpanded: boolean editToolPartsExpanded: boolean @@ -47,6 +50,9 @@ const defaultSettings: Settings = { autoSave: true, releaseNotes: true, followup: "steer", + showFileTree: false, + showStatus: false, + showTerminal: false, showReasoningSummaries: false, shellToolPartsExpanded: true, editToolPartsExpanded: false, @@ -143,6 +149,18 @@ export const { use: useSettings, provider: SettingsProvider } = createSimpleCont setFollowup(value: "queue" | "steer") { setStore("general", "followup", value) }, + showFileTree: withFallback(() => store.general?.showFileTree, defaultSettings.general.showFileTree), + setShowFileTree(value: boolean) { + setStore("general", "showFileTree", value) + }, + showStatus: withFallback(() => store.general?.showStatus, defaultSettings.general.showStatus), + setShowStatus(value: boolean) { + setStore("general", "showStatus", value) + }, + showTerminal: withFallback(() => store.general?.showTerminal, defaultSettings.general.showTerminal), + setShowTerminal(value: boolean) { + setStore("general", "showTerminal", value) + }, showReasoningSummaries: withFallback( () => store.general?.showReasoningSummaries, defaultSettings.general.showReasoningSummaries, diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index 579b740d3a..00872a6ae4 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -715,6 +715,7 @@ export const dict = { "settings.desktop.wsl.description": "Run the OpenCode server inside WSL on Windows.", "settings.general.section.appearance": "Appearance", + "settings.general.section.advanced": "Advanced", "settings.general.section.notifications": "System notifications", "settings.general.section.updates": "Updates", "settings.general.section.sounds": "Sound effects", @@ -735,6 +736,12 @@ export const dict = { "settings.general.row.followup.description": "Choose whether follow-up prompts steer immediately or wait in a queue", "settings.general.row.followup.option.queue": "Queue", "settings.general.row.followup.option.steer": "Steer", + "settings.general.row.showFileTree.title": "File tree", + "settings.general.row.showFileTree.description": "Show the file tree toggle and panel in desktop sessions", + "settings.general.row.showTerminal.title": "Terminal", + "settings.general.row.showTerminal.description": "Show the terminal button in the desktop title bar", + "settings.general.row.showStatus.title": "Server status", + "settings.general.row.showStatus.description": "Show the server status button in the desktop title bar", "settings.general.row.reasoningSummaries.title": "Show reasoning summaries", "settings.general.row.reasoningSummaries.description": "Display model reasoning summaries in the timeline", "settings.general.row.shellToolPartsExpanded.title": "Expand shell tool parts", diff --git a/packages/app/src/pages/session/session-side-panel.tsx b/packages/app/src/pages/session/session-side-panel.tsx index 58c650fcd1..6ae32ac1ef 100644 --- a/packages/app/src/pages/session/session-side-panel.tsx +++ b/packages/app/src/pages/session/session-side-panel.tsx @@ -19,6 +19,8 @@ import { useCommand } from "@/context/command" import { useFile, type SelectedLineRange } from "@/context/file" import { useLanguage } from "@/context/language" import { useLayout } from "@/context/layout" +import { usePlatform } from "@/context/platform" +import { useSettings } from "@/context/settings" import { useSync } from "@/context/sync" import { createFileTabListSync } from "@/pages/session/file-tab-scroll" import { FileTabContent } from "@/pages/session/file-tabs" @@ -34,6 +36,8 @@ export function SessionSidePanel(props: { size: Sizing }) { const layout = useLayout() + const platform = usePlatform() + const settings = useSettings() const sync = useSync() const file = useFile() const language = useLanguage() @@ -42,9 +46,10 @@ export function SessionSidePanel(props: { const { params, sessionKey, tabs, view } = useSessionLayout() const isDesktop = createMediaQuery("(min-width: 768px)") + const shown = createMemo(() => platform.platform !== "desktop" || settings.general.showFileTree()) const reviewOpen = createMemo(() => isDesktop() && view().reviewPanel.opened()) - const fileOpen = createMemo(() => isDesktop() && layout.fileTree.opened()) + const fileOpen = createMemo(() => isDesktop() && shown() && layout.fileTree.opened()) const open = createMemo(() => reviewOpen() || fileOpen()) const reviewTab = createMemo(() => isDesktop()) const panelWidth = createMemo(() => { @@ -352,100 +357,102 @@ export function SessionSidePanel(props: { -
+
- - - - {reviewCount()}{" "} - {language.t(reviewCount() === 1 ? "session.review.change.one" : "session.review.change.other")} - - - {language.t("session.files.all")} - - - - - - - {language.t("common.loading")} - {language.t("common.loading.ellipsis")} -
- } - > + + + + {reviewCount()}{" "} + {language.t(reviewCount() === 1 ? "session.review.change.one" : "session.review.change.other")} + + + {language.t("session.files.all")} + + + + + + + {language.t("common.loading")} + {language.t("common.loading.ellipsis")} +
+ } + > + props.focusReviewDiff(node.path)} + /> + + + + {empty( + language.t(sync.project && !sync.project.vcs ? "session.review.noChanges" : reviewEmptyKey()), + )} + + + + + + {empty(language.t("session.files.empty"))} + props.focusReviewDiff(node.path)} + onFileClick={(node) => openTab(file.tab(node.path))} /> - - - - {empty( - language.t(sync.project && !sync.project.vcs ? "session.review.noChanges" : reviewEmptyKey()), - )} - - - - - - {empty(language.t("session.files.empty"))} - - openTab(file.tab(node.path))} - /> - - - - - - -
props.size.start()}> - { - props.size.touch() - layout.fileTree.resize(width) - }} - /> + + + +
-
- + +
props.size.start()}> + { + props.size.touch() + layout.fileTree.resize(width) + }} + /> +
+
+ + diff --git a/packages/app/src/pages/session/use-session-commands.tsx b/packages/app/src/pages/session/use-session-commands.tsx index f17e3f7a1f..11b833978f 100644 --- a/packages/app/src/pages/session/use-session-commands.tsx +++ b/packages/app/src/pages/session/use-session-commands.tsx @@ -7,8 +7,10 @@ import { useLanguage } from "@/context/language" import { useLayout } from "@/context/layout" import { useLocal } from "@/context/local" import { usePermission } from "@/context/permission" +import { usePlatform } from "@/context/platform" import { usePrompt } from "@/context/prompt" import { useSDK } from "@/context/sdk" +import { useSettings } from "@/context/settings" import { useSync } from "@/context/sync" import { useTerminal } from "@/context/terminal" import { DialogSelectFile } from "@/components/dialog-select-file" @@ -43,8 +45,10 @@ export const useSessionCommands = (actions: SessionCommandContext) => { const language = useLanguage() const local = useLocal() const permission = usePermission() + const platform = usePlatform() const prompt = usePrompt() const sdk = useSDK() + const settings = useSettings() const sync = useSync() const terminal = useTerminal() const layout = useLayout() @@ -74,6 +78,7 @@ export const useSessionCommands = (actions: SessionCommandContext) => { }) const activeFileTab = tabState.activeFileTab const closableTab = tabState.closableTab + const shown = () => platform.platform !== "desktop" || settings.general.showFileTree() const idle = { type: "idle" as const } const status = () => sync.data.session_status[params.id ?? ""] ?? idle @@ -307,12 +312,16 @@ export const useSessionCommands = (actions: SessionCommandContext) => { keybind: "mod+shift+r", onSelect: () => view().reviewPanel.toggle(), }), - viewCommand({ - id: "fileTree.toggle", - title: language.t("command.fileTree.toggle"), - keybind: "mod+\\", - onSelect: () => layout.fileTree.toggle(), - }), + ...(shown() + ? [ + viewCommand({ + id: "fileTree.toggle", + title: language.t("command.fileTree.toggle"), + keybind: "mod+\\", + onSelect: () => layout.fileTree.toggle(), + }), + ] + : []), viewCommand({ id: "input.focus", title: language.t("command.input.focus"),