summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-01-15 07:00:53 -0600
committerAdam <[email protected]>2026-01-15 07:29:13 -0600
commit564d3edfac6e3cc872d35d7b2d1e8bc1ea4b84bd (patch)
tree1b012bf5ba4ce00b943c234034d1a4025d7eb0f5
parent679270d9e0731c2b3e2c059d83907cb4086d90e2 (diff)
downloadopencode-564d3edfac6e3cc872d35d7b2d1e8bc1ea4b84bd.tar.gz
opencode-564d3edfac6e3cc872d35d7b2d1e8bc1ea4b84bd.zip
fix(app): new layout issues
-rw-r--r--packages/app/src/context/layout.tsx38
-rw-r--r--packages/app/src/pages/layout.tsx98
-rw-r--r--packages/desktop/src-tauri/src/lib.rs5
-rw-r--r--packages/desktop/src/index.tsx3
4 files changed, 113 insertions, 31 deletions
diff --git a/packages/app/src/context/layout.tsx b/packages/app/src/context/layout.tsx
index ba332be7b..a49e891bf 100644
--- a/packages/app/src/context/layout.tsx
+++ b/packages/app/src/context/layout.tsx
@@ -47,13 +47,34 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
const globalSdk = useGlobalSDK()
const globalSync = useGlobalSync()
const server = useServer()
+
+ const isRecord = (value: unknown): value is Record<string, unknown> =>
+ typeof value === "object" && value !== null && !Array.isArray(value)
+
+ const migrate = (value: unknown) => {
+ if (!isRecord(value)) return value
+ const sidebar = value.sidebar
+ if (!isRecord(sidebar)) return value
+ if (typeof sidebar.workspaces !== "boolean") return value
+ return {
+ ...value,
+ sidebar: {
+ ...sidebar,
+ workspaces: {},
+ workspacesDefault: sidebar.workspaces,
+ },
+ }
+ }
+
+ const target = Persist.global("layout", ["layout.v6"])
const [store, setStore, _, ready] = persisted(
- Persist.global("layout", ["layout.v6"]),
+ { ...target, migrate },
createStore({
sidebar: {
opened: false,
width: 280,
- workspaces: false,
+ workspaces: {} as Record<string, boolean>,
+ workspacesDefault: false,
},
terminal: {
height: 280,
@@ -305,12 +326,15 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
resize(width: number) {
setStore("sidebar", "width", width)
},
- workspaces: createMemo(() => store.sidebar.workspaces ?? false),
- setWorkspaces(value: boolean) {
- setStore("sidebar", "workspaces", value)
+ workspaces(directory: string) {
+ return createMemo(() => store.sidebar.workspaces[directory] ?? store.sidebar.workspacesDefault ?? false)
+ },
+ setWorkspaces(directory: string, value: boolean) {
+ setStore("sidebar", "workspaces", directory, value)
},
- toggleWorkspaces() {
- setStore("sidebar", "workspaces", (x) => !x)
+ toggleWorkspaces(directory: string) {
+ const current = store.sidebar.workspaces[directory] ?? store.sidebar.workspacesDefault ?? false
+ setStore("sidebar", "workspaces", directory, !current)
},
},
terminal: {
diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx
index cc5396656..b2c617bfa 100644
--- a/packages/app/src/pages/layout.tsx
+++ b/packages/app/src/pages/layout.tsx
@@ -271,6 +271,12 @@ export default function Layout(props: ParentProps) {
return layout.projects.list().find((p) => p.worktree === directory || p.sandboxes?.includes(directory))
})
+ const workspaceSetting = createMemo(() => {
+ const project = currentProject()
+ if (!project) return false
+ return layout.sidebar.workspaces(project.worktree)()
+ })
+
createEffect(() => {
const project = currentProject()
if (!project) return
@@ -306,7 +312,16 @@ export default function Layout(props: ParentProps) {
return sessions.filter((s) => !s.parentID)
}
- const currentSessions = createMemo(() => projectSessions(currentProject()))
+ const currentSessions = createMemo(() => {
+ const project = currentProject()
+ if (!project) return [] as Session[]
+ if (workspaceSetting()) return projectSessions(project)
+ const [projectStore] = globalSync.child(project.worktree)
+ return projectStore.session
+ .filter((session) => session.directory === projectStore.path.directory)
+ .filter((session) => !session.parentID)
+ .toSorted(sortSessions)
+ })
type PrefetchQueue = {
inflight: Set<string>
@@ -730,6 +745,21 @@ export default function Layout(props: ParentProps) {
})
createEffect(() => {
+ const project = currentProject()
+ if (!project) return
+
+ if (workspaceSetting()) {
+ const dirs = [project.worktree, ...(project.sandboxes ?? [])]
+ for (const directory of dirs) {
+ globalSync.project.loadSessions(directory)
+ }
+ return
+ }
+
+ globalSync.project.loadSessions(project.worktree)
+ })
+
+ createEffect(() => {
if (isLargeViewport()) {
const sidebarWidth = layout.sidebar.opened() ? layout.sidebar.width() : 64
document.documentElement.style.setProperty("--dialog-left-margin", `${sidebarWidth}px`)
@@ -943,6 +973,7 @@ export default function Layout(props: ParentProps) {
})
const workspaces = createMemo(() => workspaceIds(props.project).slice(0, 2))
+ const workspaceEnabled = createMemo(() => layout.sidebar.workspaces(props.project.worktree)())
const label = (directory: string) => {
const [data] = globalSync.child(directory)
const kind = directory === props.project.worktree ? "local" : "sandbox"
@@ -959,6 +990,15 @@ export default function Layout(props: ParentProps) {
.slice(0, 2)
}
+ const projectSessions = () => {
+ const [data] = globalSync.child(props.project.worktree)
+ return data.session
+ .filter((session) => session.directory === data.path.directory)
+ .filter((session) => !session.parentID)
+ .toSorted(sortSessions)
+ .slice(0, 2)
+ }
+
const trigger = (
<button
type="button"
@@ -980,23 +1020,39 @@ export default function Layout(props: ParentProps) {
<div class="-m-3 flex flex-col w-72">
<div class="px-3 py-2 text-12-medium text-text-strong">Recent sessions</div>
<div class="px-2 pb-2 flex flex-col gap-2">
- <For each={workspaces()}>
- {(directory) => (
- <div class="flex flex-col gap-1">
- <div class="px-2 py-0.5 flex items-center gap-1 min-w-0">
- <div class="shrink-0 size-6 flex items-center justify-center">
- <Icon name="branch" size="small" class="text-icon-base" />
+ <Show
+ when={workspaceEnabled()}
+ fallback={
+ <For each={projectSessions()}>
+ {(session) => (
+ <SessionItem
+ session={session}
+ slug={base64Encode(props.project.worktree)}
+ dense
+ mobile={props.mobile}
+ />
+ )}
+ </For>
+ }
+ >
+ <For each={workspaces()}>
+ {(directory) => (
+ <div class="flex flex-col gap-1">
+ <div class="px-2 py-0.5 flex items-center gap-1 min-w-0">
+ <div class="shrink-0 size-6 flex items-center justify-center">
+ <Icon name="branch" size="small" class="text-icon-base" />
+ </div>
+ <span class="truncate text-14-medium text-text-strong">{label(directory)}</span>
</div>
- <span class="truncate text-14-medium text-text-strong">{label(directory)}</span>
+ <For each={sessions(directory)}>
+ {(session) => (
+ <SessionItem session={session} slug={base64Encode(directory)} dense mobile={props.mobile} />
+ )}
+ </For>
</div>
- <For each={sessions(directory)}>
- {(session) => (
- <SessionItem session={session} slug={base64Encode(directory)} dense mobile={props.mobile} />
- )}
- </For>
- </div>
- )}
- </For>
+ )}
+ </For>
+ </Show>
</div>
<div class="px-2 py-2 border-t border-border-weak-base">
<Button
@@ -1068,7 +1124,7 @@ export default function Layout(props: ParentProps) {
return `${kind} : ${name}`
})
const open = createMemo(() => store.workspaceExpanded[props.directory] ?? true)
- const hasMore = createMemo(() => local() && workspaceStore.session.length >= workspaceStore.limit)
+ const hasMore = createMemo(() => local() && workspaceStore.sessionTotal > workspaceStore.session.length)
const loadMore = async () => {
if (!local()) return
setWorkspaceStore("limit", (limit) => limit + 5)
@@ -1157,7 +1213,7 @@ export default function Layout(props: ParentProps) {
.filter((session) => !session.parentID)
.toSorted(sortSessions),
)
- const hasMore = createMemo(() => workspaceStore.session.length >= workspaceStore.limit)
+ const hasMore = createMemo(() => workspaceStore.sessionTotal > workspaceStore.session.length)
const loadMore = async () => {
setWorkspaceStore("limit", (limit) => limit + 5)
await globalSync.project.loadSessions(props.project.worktree)
@@ -1324,9 +1380,9 @@ export default function Layout(props: ParentProps) {
<DropdownMenu.ItemLabel>Close project</DropdownMenu.ItemLabel>
</DropdownMenu.Item>
<DropdownMenu.Separator />
- <DropdownMenu.Item onSelect={() => layout.sidebar.toggleWorkspaces()}>
+ <DropdownMenu.Item onSelect={() => layout.sidebar.toggleWorkspaces(p().worktree)}>
<DropdownMenu.ItemLabel>
- {layout.sidebar.workspaces() ? "Disable workspaces" : "Enable workspaces"}
+ {layout.sidebar.workspaces(p().worktree)() ? "Disable workspaces" : "Enable workspaces"}
</DropdownMenu.ItemLabel>
</DropdownMenu.Item>
</DropdownMenu.Content>
@@ -1336,7 +1392,7 @@ export default function Layout(props: ParentProps) {
</div>
<Show
- when={layout.sidebar.workspaces()}
+ when={layout.sidebar.workspaces(p().worktree)()}
fallback={
<>
<div class="py-4 px-3">
diff --git a/packages/desktop/src-tauri/src/lib.rs b/packages/desktop/src-tauri/src/lib.rs
index 15033fd56..484c4a866 100644
--- a/packages/desktop/src-tauri/src/lib.rs
+++ b/packages/desktop/src-tauri/src/lib.rs
@@ -293,6 +293,11 @@ pub fn run() {
"#
));
+ #[cfg(target_os = "macos")]
+ let window_builder = window_builder
+ .title_bar_style(tauri::TitleBarStyle::Overlay)
+ .hidden_title(true);
+
let _window = window_builder.build().expect("Failed to create window");
let (tx, rx) = oneshot::channel();
diff --git a/packages/desktop/src/index.tsx b/packages/desktop/src/index.tsx
index e554f8da0..7a46ba8cd 100644
--- a/packages/desktop/src/index.tsx
+++ b/packages/desktop/src/index.tsx
@@ -319,9 +319,6 @@ render(() => {
return (
<PlatformProvider value={platform}>
<AppBaseProviders>
- {ostype() === "macos" && (
- <div class="mx-px bg-background-base border-b border-border-weak-base h-8" data-tauri-drag-region />
- )}
<ServerGate>
{(data) => {
setServerPassword(data().password)