feat(app): inset new layout session panels (#30342)

This commit is contained in:
Brendan Allan
2026-06-02 07:43:08 +00:00
committed by GitHub
parent 8af7371c13
commit 787f170682
5 changed files with 31 additions and 25 deletions
@@ -530,7 +530,7 @@ type SessionHeaderV2ActionsState = {
function SessionHeaderV2Actions(props: { state: SessionHeaderV2ActionsState }) { function SessionHeaderV2Actions(props: { state: SessionHeaderV2ActionsState }) {
return ( return (
<div class="flex items-center gap-0"> <div class="flex items-center gap-2">
<Show when={props.state.statusVisible}> <Show when={props.state.statusVisible}>
<Tooltip placement="bottom" value={props.state.statusLabel}> <Tooltip placement="bottom" value={props.state.statusLabel}>
<StatusPopoverV2 /> <StatusPopoverV2 />
+3 -13
View File
@@ -24,7 +24,6 @@ import { ProjectAvatar } from "@opencode-ai/ui/v2/project-avatar-v2"
import { displayName, getProjectAvatarSource, projectForSession } from "@/pages/layout/helpers" import { displayName, getProjectAvatarSource, projectForSession } from "@/pages/layout/helpers"
import { useSessionTabAvatarState } from "@/pages/layout/project-avatar-state" import { useSessionTabAvatarState } from "@/pages/layout/project-avatar-state"
import { makeEventListener } from "@solid-primitives/event-listener" import { makeEventListener } from "@solid-primitives/event-listener"
import { StatusPopoverV2 } from "@/components/status-popover"
import { import {
readSessionTabsRemovedDetail, readSessionTabsRemovedDetail,
SESSION_TABS_REMOVED_EVENT, SESSION_TABS_REMOVED_EVENT,
@@ -53,7 +52,7 @@ const tauriApi = () => (window as unknown as { __TAURI__?: TauriApi }).__TAURI__
const currentDesktopWindow = () => tauriApi()?.window?.getCurrentWindow?.() const currentDesktopWindow = () => tauriApi()?.window?.getCurrentWindow?.()
const currentThemeWindow = () => tauriApi()?.webviewWindow?.getCurrentWebviewWindow?.() const currentThemeWindow = () => tauriApi()?.webviewWindow?.getCurrentWebviewWindow?.()
const legacyTitlebarHeight = 40 const legacyTitlebarHeight = 40
const v2TitlebarHeight = 44 const v2TitlebarHeight = 36
const minTitlebarZoom = 0.25 const minTitlebarZoom = 0.25
const windowsControlsBaseWidth = 138 // 3 native Windows caption buttons at 46px each. const windowsControlsBaseWidth = 138 // 3 native Windows caption buttons at 46px each.
@@ -134,8 +133,6 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
}) })
const v2RightState = createMemo<TitlebarV2RightState>(() => ({ const v2RightState = createMemo<TitlebarV2RightState>(() => ({
update: updateState(), update: updateState(),
statusVisible: !params.dir && settings.general.showStatus(),
statusLabel: language.t("status.popover.trigger"),
})) }))
const back = () => { const back = () => {
@@ -223,7 +220,7 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
<header <header
classList={{ classList={{
"shrink-0 relative overflow-hidden flex flex-row": true, "shrink-0 relative overflow-hidden flex flex-row": true,
"h-11 bg-v2-background-bg-deep": useV2Titlebar(), "h-9 bg-v2-background-bg-deep": useV2Titlebar(),
"h-10 bg-background-base": !useV2Titlebar(), "h-10 bg-background-base": !useV2Titlebar(),
}} }}
style={{ style={{
@@ -463,7 +460,7 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
return ( return (
<div <div
class="h-full flex-1 flex flex-row items-center gap-1.5 pr-3 py-2" class="h-full flex-1 flex flex-row items-center gap-1.5 pr-3 pt-2"
classList={{ classList={{
"pl-2": mac(), "pl-2": mac(),
"pl-4": !mac(), "pl-4": !mac(),
@@ -704,19 +701,12 @@ type TitlebarUpdatePillState = {
type TitlebarV2RightState = { type TitlebarV2RightState = {
update: TitlebarUpdatePillState update: TitlebarUpdatePillState
statusVisible: boolean
statusLabel: string
} }
function TitlebarV2Right(props: { state: TitlebarV2RightState }) { function TitlebarV2Right(props: { state: TitlebarV2RightState }) {
return ( return (
<div class="flex shrink-0 items-center justify-end gap-0"> <div class="flex shrink-0 items-center justify-end gap-0">
<TitlebarUpdatePill state={props.state.update} /> <TitlebarUpdatePill state={props.state.update} />
<Show when={props.state.statusVisible}>
<Tooltip placement="bottom" value={props.state.statusLabel}>
<StatusPopoverV2 scope="server" />
</Tooltip>
</Show>
<div id="opencode-titlebar-right" class="flex shrink-0 items-center justify-end gap-0" /> <div id="opencode-titlebar-right" class="flex shrink-0 items-center justify-end gap-0" />
</div> </div>
) )
+1 -6
View File
@@ -2374,12 +2374,7 @@ export default function Layout(props: ParentProps) {
<div class="relative bg-v2-background-bg-deep flex-1 min-h-0 min-w-0 flex flex-col select-none [&_input]:select-text [&_textarea]:select-text [&_[contenteditable]]:select-text"> <div class="relative bg-v2-background-bg-deep flex-1 min-h-0 min-w-0 flex flex-col select-none [&_input]:select-text [&_textarea]:select-text [&_[contenteditable]]:select-text">
{autoselecting() ?? ""} {autoselecting() ?? ""}
<Titlebar update={titlebarUpdate} /> <Titlebar update={titlebarUpdate} />
<main <main class="flex-1 min-h-0 min-w-0 overflow-x-hidden flex flex-col items-start contain-strict">
class="flex-1 min-h-0 min-w-0 overflow-x-hidden flex flex-col items-start contain-strict bg-v2-background-bg-base"
classList={{
"m-2 mt-0 rounded-[10px] shadow-[var(--v2-elevation-raised)] overflow-hidden": !!params.id || !params.dir,
}}
>
<Show when={!autoselecting.loading} fallback={<div class="size-full" />}> <Show when={!autoselecting.loading} fallback={<div class="size-full" />}>
{props.children} {props.children}
</Show> </Show>
+17 -3
View File
@@ -1707,10 +1707,15 @@ export default function Page() {
) )
return ( return (
<div class="relative bg-background-base size-full overflow-hidden flex flex-col"> <div class="relative size-full overflow-hidden flex flex-col">
{sessionSync() ?? ""} {sessionSync() ?? ""}
<SessionHeader /> <SessionHeader />
<div class="flex-1 min-h-0 flex flex-col md:flex-row"> <div
class="flex-1 min-h-0 flex flex-col md:flex-row "
classList={{
"gap-2 p-2": settings.general.newLayoutDesigns(),
}}
>
<Show when={!isDesktop() && !!params.id}> <Show when={!isDesktop() && !!params.id}>
<Tabs value={store.mobileTab} class="h-auto"> <Tabs value={store.mobileTab} class="h-auto">
<Tabs.List> <Tabs.List>
@@ -1742,12 +1747,18 @@ export default function Page() {
"duration-[240ms] ease-[cubic-bezier(0.22,1,0.36,1)] will-change-[width] motion-reduce:transition-none": "duration-[240ms] ease-[cubic-bezier(0.22,1,0.36,1)] will-change-[width] motion-reduce:transition-none":
!size.active() && !ui.reviewSnap, !size.active() && !ui.reviewSnap,
"transition-[width]": !isV2NewSessionPage(), "transition-[width]": !isV2NewSessionPage(),
"rounded-[10px] shadow-[var(--v2-elevation-raised)]": settings.general.newLayoutDesigns(),
}} }}
style={{ style={{
width: sessionPanelWidth(), width: sessionPanelWidth(),
}} }}
> >
<div class="flex-1 min-h-0 overflow-hidden"> <div
class="flex-1 min-h-0 overflow-hidden"
classList={{
"rounded-[10px]": settings.general.newLayoutDesigns(),
}}
>
<Switch> <Switch>
<Match when={params.id && mobileChanges()}> <Match when={params.id && mobileChanges()}>
<div class="relative h-full overflow-hidden"> <div class="relative h-full overflow-hidden">
@@ -1810,6 +1821,9 @@ export default function Page() {
<Show when={desktopReviewOpen()}> <Show when={desktopReviewOpen()}>
<div onPointerDown={() => size.start()}> <div onPointerDown={() => size.start()}>
<ResizeHandle <ResizeHandle
classList={{
"-right-1": settings.general.newLayoutDesigns(),
}}
direction="horizontal" direction="horizontal"
size={layout.session.width()} size={layout.session.width()}
min={450} min={450}
@@ -67,7 +67,7 @@ export function SessionSidePanel(props: {
const reviewTab = createMemo(() => isDesktop()) const reviewTab = createMemo(() => isDesktop())
const panelWidth = createMemo(() => { const panelWidth = createMemo(() => {
if (!open()) return "0px" if (!open()) return "0px"
if (reviewOpen()) return `calc(100% - ${layout.session.width()}px)` if (reviewOpen()) return "auto"
return `${layout.fileTree.width()}px` return `${layout.fileTree.width()}px`
}) })
const treeWidth = createMemo(() => (fileOpen() ? `${layout.fileTree.width()}px` : "0px")) const treeWidth = createMemo(() => (fileOpen() ? `${layout.fileTree.width()}px` : "0px"))
@@ -214,11 +214,18 @@ export function SessionSidePanel(props: {
"pointer-events-none": !open(), "pointer-events-none": !open(),
"transition-[width] duration-[240ms] ease-[cubic-bezier(0.22,1,0.36,1)] will-change-[width] motion-reduce:transition-none": "transition-[width] duration-[240ms] ease-[cubic-bezier(0.22,1,0.36,1)] will-change-[width] motion-reduce:transition-none":
!props.size.active() && !props.reviewSnap, !props.size.active() && !props.reviewSnap,
"rounded-[10px] shadow-[var(--v2-elevation-raised)] overflow-hidden": settings.general.newLayoutDesigns(),
"flex-1": reviewOpen(),
}} }}
style={{ width: panelWidth() }} style={{ width: panelWidth() }}
> >
<Show when={open()}> <Show when={open()}>
<div class="size-full flex border-l border-border-weaker-base"> <div
class="size-full flex"
classList={{
"border-l border-border-weaker-base": !settings.general.newLayoutDesigns(),
}}
>
<div <div
aria-hidden={!reviewOpen()} aria-hidden={!reviewOpen()}
inert={!reviewOpen()} inert={!reviewOpen()}