Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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: 2 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ jobs:
- host: "windows-2025"
target: aarch64-pc-windows-msvc
platform_flag: --win --arm64
bun_install_flags: --os=win32 --cpu=arm64
- host: "blacksmith-4vcpu-windows-2025"
target: x86_64-pc-windows-msvc
platform_flag: --win
Expand Down Expand Up @@ -388,6 +389,7 @@ jobs:
working-directory: packages/desktop
env:
NODE_OPTIONS: --max-old-space-size=4096
RUST_TARGET: ${{ matrix.settings.target }}
OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ vars.SENTRY_ORG }}
Expand Down
53 changes: 28 additions & 25 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

80 changes: 77 additions & 3 deletions packages/app/src/components/session/session-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { useCommand } from "@/context/command"
import { useLanguage } from "@/context/language"
import { useLayout } from "@/context/layout"
import { usePlatform } from "@/context/platform"
import { useServer } from "@/context/server"
import { ServerConnection, useServer } from "@/context/server"
import { useSettings } from "@/context/settings"
import { useSync } from "@/context/sync"
import { useTerminal } from "@/context/terminal"
Expand Down Expand Up @@ -242,6 +242,17 @@ export function SessionHeader() {
reviewVisible: isDesktop(),
reviewOpened: view().reviewPanel.opened(),
onReviewToggle: () => view().reviewPanel.toggle(),
browserLabel: language.t("command.browser.toggle"),
browserKeybind: command.keybindParts("browser.toggle"),
browserVisible:
isDesktop() &&
!!params.id &&
!!platform.browserPane &&
settings.general.experimentalBrowser() &&
!!server.current &&
ServerConnection.builtin(server.current),
browserOpened: view().browserPanel.opened(),
onBrowserToggle: () => view().browserPanel.toggle(),
}))

const selectApp = (app: OpenApp) => {
Expand Down Expand Up @@ -478,6 +489,39 @@ export function SessionHeader() {
</Button>
</TooltipKeybind>

<Show
when={
platform.browserPane &&
params.id &&
settings.general.experimentalBrowser() &&
server.current &&
ServerConnection.builtin(server.current)
}
>
<TooltipKeybind
title={language.t("command.browser.toggle")}
keybind={command.keybind("browser.toggle")}
>
<Button
variant="ghost"
class="group/browser-toggle titlebar-icon w-8 h-6 p-0 box-border"
onClick={() => view().browserPanel.toggle()}
aria-label={language.t("command.browser.toggle")}
aria-expanded={view().browserPanel.opened()}
aria-controls="browser-panel"
>
<Icon
size="small"
name="window-cursor"
classList={{
"text-icon-strong": view().browserPanel.opened(),
"text-icon-weak": !view().browserPanel.opened(),
}}
/>
</Button>
</TooltipKeybind>
</Show>

<TooltipKeybind
title={language.t("command.fileTree.toggle")}
keybind={command.keybind("fileTree.toggle")}
Expand Down Expand Up @@ -524,11 +568,14 @@ type SessionHeaderV2ActionsState = {
reviewVisible: boolean
reviewOpened: boolean
onReviewToggle: () => void
browserLabel: string
browserKeybind: string[]
browserVisible: boolean
browserOpened: boolean
onBrowserToggle: () => void
}

function SessionHeaderV2Actions(props: { state: SessionHeaderV2ActionsState }) {
const language = useLanguage()

return (
<div class="flex items-center gap-2">
<Show when={props.state.statusVisible}>
Expand Down Expand Up @@ -563,6 +610,33 @@ function SessionHeaderV2Actions(props: { state: SessionHeaderV2ActionsState }) {
/>
</TooltipV2>
</Show>
<Show when={props.state.browserVisible}>
<TooltipV2
class="shrink-0"
placement="bottom"
value={
<>
{props.state.browserLabel}
<Show when={props.state.browserKeybind.length > 0}>
<KeybindV2 keys={props.state.browserKeybind} variant="neutral" />
</Show>
</>
}
>
<IconButtonV2
type="button"
variant="ghost-muted"
size="large"
class="!w-9 shrink-0"
state={props.state.browserOpened ? "pressed" : undefined}
onClick={props.state.onBrowserToggle}
aria-label={props.state.browserLabel}
aria-expanded={props.state.browserOpened}
aria-controls="browser-panel"
icon={<Icon name="window-cursor" size="small" />}
/>
</TooltipV2>
</Show>
</div>
)
}
14 changes: 14 additions & 0 deletions packages/app/src/components/settings-general.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,20 @@ export const SettingsGeneral: Component = () => {
<h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.general.section.advanced")}</h3>

<SettingsList>
<Show when={platform.browserPane}>
<SettingsRow
title={language.t("settings.general.row.experimentalBrowser.title")}
description={language.t("settings.general.row.experimentalBrowser.description")}
>
<div data-action="settings-experimental-browser">
<Switch
checked={settings.general.experimentalBrowser()}
onChange={(checked) => settings.general.setExperimentalBrowser(checked)}
/>
</div>
</SettingsRow>
</Show>

<SettingsRow
title={language.t("settings.general.row.showFileTree.title")}
description={language.t("settings.general.row.showFileTree.description")}
Expand Down
14 changes: 14 additions & 0 deletions packages/app/src/components/settings-v2/general.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,20 @@ export const SettingsGeneralV2: Component<{
<h3 class="settings-v2-section-title">{language.t("settings.general.section.advanced")}</h3>

<SettingsListV2>
<Show when={platform.browserPane}>
<SettingsRowV2
title={language.t("settings.general.row.experimentalBrowser.title")}
description={language.t("settings.general.row.experimentalBrowser.description")}
>
<div data-action="settings-experimental-browser">
<Switch
checked={settings.general.experimentalBrowser()}
onChange={(checked) => settings.general.setExperimentalBrowser(checked)}
/>
</div>
</SettingsRowV2>
</Show>

<SettingsRowV2
title={language.t("settings.general.row.showFileTree.title")}
description={language.t("settings.general.row.showFileTree.description")}
Expand Down
40 changes: 39 additions & 1 deletion packages/app/src/context/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,9 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
diffStyle: "split" as ReviewDiffStyle,
panelOpened: DEFAULT_REVIEW_PANEL_OPENED,
},
browser: {
panelOpened: false,
},
fileTree: {
opened: false,
width: DEFAULT_FILE_TREE_WIDTH,
Expand Down Expand Up @@ -700,6 +703,7 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
setStore("fileTree", "tab", tab)
},
open() {
if (store.browser?.panelOpened) setStore("browser", "panelOpened", false)
if (!store.fileTree) {
setStore("fileTree", { opened: true, width: DEFAULT_FILE_TREE_WIDTH, tab: "changes" })
return
Expand All @@ -714,11 +718,13 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
setStore("fileTree", "opened", false)
},
toggle() {
const next = !(store.fileTree?.opened ?? true)
if (next && store.browser?.panelOpened) setStore("browser", "panelOpened", false)
if (!store.fileTree) {
setStore("fileTree", { opened: true, width: DEFAULT_FILE_TREE_WIDTH, tab: "changes" })
return
}
setStore("fileTree", "opened", (x) => !x)
setStore("fileTree", "opened", next)
},
resize(width: number) {
if (!store.fileTree) {
Expand Down Expand Up @@ -806,6 +812,7 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
})
const terminalOpened = createMemo(() => store.terminal?.opened ?? false)
const reviewPanelOpened = createMemo(() => store.review?.panelOpened ?? DEFAULT_REVIEW_PANEL_OPENED)
const browserPanelOpened = createMemo(() => store.browser?.panelOpened ?? false)
const reviewPanelSource = createMemo(() => (reviewPanelOpened() ? ephemeral.reviewPanelSource : "other"))

function setTerminalOpened(next: boolean) {
Expand All @@ -821,6 +828,7 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
}

function setReviewPanelOpened(next: boolean, source: ReviewPanelSource) {
if (next && browserPanelOpened()) setStore("browser", "panelOpened", false)
const nextSource = next ? source : "other"
const current = store.review
if (!current) {
Expand All @@ -842,6 +850,24 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
})
}

function setBrowserPanelOpened(next: boolean) {
const current = store.browser
if (!current) {
batch(() => {
setStore("browser", { panelOpened: next })
if (next) setReviewPanelOpened(false, "other")
if (next && store.fileTree?.opened) setStore("fileTree", "opened", false)
})
return
}
if ((current.panelOpened ?? false) === next) return
batch(() => {
setStore("browser", "panelOpened", next)
if (next) setReviewPanelOpened(false, "other")
if (next && store.fileTree?.opened) setStore("fileTree", "opened", false)
})
}

return {
scroll(tab: string) {
return scroll.scroll(key(), tab)
Expand Down Expand Up @@ -874,6 +900,18 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
setReviewPanelOpened(!reviewPanelOpened(), "other")
},
},
browserPanel: {
opened: browserPanelOpened,
open() {
setBrowserPanelOpened(true)
},
close() {
setBrowserPanelOpened(false)
},
toggle() {
setBrowserPanelOpened(!browserPanelOpened())
},
},
review: {
mode: reviewMode,
setMode(mode: ReviewChangeMode) {
Expand Down
Loading
Loading