diff options
| author | Adam <[email protected]> | 2026-01-12 10:11:29 -0600 |
|---|---|---|
| committer | Adam <[email protected]> | 2026-01-15 07:29:13 -0600 |
| commit | 679270d9e0731c2b3e2c059d83907cb4086d90e2 (patch) | |
| tree | 4e8d70a281b92f86a4b916ed45a5410ecbba0289 /packages/app/src/components | |
| parent | 9f66a45970d1edf12ae9b3e7a22d77711b5e51c3 (diff) | |
| download | opencode-679270d9e0731c2b3e2c059d83907cb4086d90e2.tar.gz opencode-679270d9e0731c2b3e2c059d83907cb4086d90e2.zip | |
feat(app): new layout
Diffstat (limited to 'packages/app/src/components')
| -rw-r--r-- | packages/app/src/components/session/session-header.tsx | 101 | ||||
| -rw-r--r-- | packages/app/src/components/titlebar.tsx | 115 |
2 files changed, 142 insertions, 74 deletions
diff --git a/packages/app/src/components/session/session-header.tsx b/packages/app/src/components/session/session-header.tsx index b2e7fafeb..5ed721740 100644 --- a/packages/app/src/components/session/session-header.tsx +++ b/packages/app/src/components/session/session-header.tsx @@ -34,6 +34,17 @@ export function SessionHeader() { const sync = useSync() const projectDirectory = createMemo(() => base64Decode(params.dir ?? "")) + const project = createMemo(() => { + const directory = projectDirectory() + if (!directory) return + return layout.projects.list().find((p) => p.worktree === directory || p.sandboxes?.includes(directory)) + }) + const name = createMemo(() => { + const current = project() + if (current) return current.name || getFilename(current.worktree) + return getFilename(projectDirectory()) + }) + const hotkey = createMemo(() => command.keybind("file.open")) const sessions = createMemo(() => (sync.data.session ?? []).filter((s) => !s.parentID)) const currentSession = createMemo(() => sync.data.session.find((s) => s.id === params.id)) @@ -58,87 +69,29 @@ export function SessionHeader() { navigate(`/${params.dir}/session/${session.id}`) } - const leftMount = createMemo(() => document.getElementById("opencode-titlebar-left")) + const centerMount = createMemo(() => document.getElementById("opencode-titlebar-center")) const rightMount = createMemo(() => document.getElementById("opencode-titlebar-right")) return ( <> - <Show when={leftMount()}> + <Show when={centerMount()}> {(mount) => ( <Portal mount={mount()}> - <div class="flex items-center gap-3 min-w-0"> - <div class="flex items-center gap-2 min-w-0"> - <div class="hidden xl:flex items-center gap-2"> - <Select - options={worktrees()} - current={sync.project?.worktree ?? projectDirectory()} - label={(x) => getFilename(x)} - onSelect={(x) => (x ? navigateToProject(x) : undefined)} - class="text-14-regular text-text-base" - variant="ghost" - > - {/* @ts-ignore */} - {(i) => ( - <div class="flex items-center gap-2"> - <Icon name="folder" size="small" /> - <div class="text-text-strong">{getFilename(i)}</div> - </div> - )} - </Select> - <div class="text-text-weaker">/</div> - </div> - <Show - when={parentSession()} - fallback={ - <> - <Select - options={sessions()} - current={currentSession()} - placeholder="New session" - label={(x) => x.title} - value={(x) => x.id} - onSelect={navigateToSession} - class="text-14-regular text-text-base max-w-[calc(100vw-180px)] md:max-w-md" - variant="ghost" - /> - </> - } - > - <div class="flex items-center gap-2 min-w-0"> - <Select - options={sessions()} - current={parentSession()} - placeholder="Back to parent session" - label={(x) => x.title} - value={(x) => x.id} - onSelect={(session) => { - const currentParent = parentSession() - if (session && currentParent && session.id !== currentParent.id) { - navigateToSession(session) - } - }} - class="text-14-regular text-text-base max-w-[calc(100vw-180px)] md:max-w-md" - variant="ghost" - /> - <div class="text-text-weaker">/</div> - <Tooltip value="Back to parent session"> - <button - type="button" - class="flex items-center justify-center gap-1 p-1 rounded hover:bg-surface-raised-base-hover active:bg-surface-raised-base-active transition-colors flex-shrink-0" - onClick={() => navigateToSession(parentSession())} - > - <Icon name="arrow-left" size="small" class="text-icon-base" /> - </button> - </Tooltip> - </div> - </Show> - </div> - <Show when={currentSession() && !parentSession()}> - <TooltipKeybind class="hidden xl:block" title="New session" keybind={command.keybind("session.new")}> - <IconButton as={A} href={`/${params.dir}/session`} icon="edit-small-2" variant="ghost" /> - </TooltipKeybind> + <button + type="button" + class="hidden md:flex w-[320px] h-7 px-1.5 items-center gap-2 rounded-md border border-border-weak-base bg-surface-raised-base transition-colors cursor-default hover:bg-surface-raised-base-hover focus:bg-surface-raised-base-hover active:bg-surface-raised-base-active" + onClick={() => command.trigger("file.open")} + > + <Icon name="magnifying-glass" size="small" class="text-text-weak" /> + <span class="flex-1 min-w-0 text-14-regular text-text-weak truncate">Search {name()}</span> + <Show when={hotkey()}> + {(keybind) => ( + <span class="shrink-0 flex items-center justify-center h-5 px-2 rounded-md border border-border-weak-base bg-surface-base text-12-medium text-text-weak"> + {keybind()} + </span> + )} </Show> - </div> + </button> </Portal> )} </Show> diff --git a/packages/app/src/components/titlebar.tsx b/packages/app/src/components/titlebar.tsx new file mode 100644 index 000000000..5cf9f74bc --- /dev/null +++ b/packages/app/src/components/titlebar.tsx @@ -0,0 +1,115 @@ +import { createEffect, createMemo, Show } from "solid-js" +import { IconButton } from "@opencode-ai/ui/icon-button" +import { TooltipKeybind } from "@opencode-ai/ui/tooltip" +import { useTheme } from "@opencode-ai/ui/theme" + +import { useLayout } from "@/context/layout" +import { usePlatform } from "@/context/platform" +import { useCommand } from "@/context/command" + +export function Titlebar() { + const layout = useLayout() + const platform = usePlatform() + const command = useCommand() + const theme = useTheme() + + const mac = createMemo(() => platform.platform === "desktop" && platform.os === "macos") + const reserve = createMemo( + () => platform.platform === "desktop" && (platform.os === "windows" || platform.os === "linux"), + ) + + const getWin = () => { + if (platform.platform !== "desktop") return + + const tauri = ( + window as unknown as { + __TAURI__?: { window?: { getCurrentWindow?: () => { startDragging?: () => Promise<void> } } } + } + ).__TAURI__ + if (!tauri?.window?.getCurrentWindow) return + + return tauri.window.getCurrentWindow() + } + + createEffect(() => { + if (platform.platform !== "desktop") return + + const scheme = theme.colorScheme() + const value = scheme === "system" ? null : scheme + + const tauri = (window as unknown as { __TAURI__?: { webviewWindow?: { getCurrentWebviewWindow?: () => unknown } } }) + .__TAURI__ + const get = tauri?.webviewWindow?.getCurrentWebviewWindow + if (!get) return + + const win = get() as { setTheme?: (theme?: "light" | "dark" | null) => Promise<void> } + if (!win.setTheme) return + + void win.setTheme(value).catch(() => undefined) + }) + + const interactive = (target: EventTarget | null) => { + if (!(target instanceof Element)) return false + + const selector = + "button, a, input, textarea, select, option, [role='button'], [role='menuitem'], [contenteditable='true'], [contenteditable='']" + + return !!target.closest(selector) + } + + const drag = (e: MouseEvent) => { + if (platform.platform !== "desktop") return + if (e.buttons !== 1) return + if (interactive(e.target)) return + + const win = getWin() + if (!win?.startDragging) return + + e.preventDefault() + void win.startDragging().catch(() => undefined) + } + + return ( + <header class="h-10 shrink-0 bg-background-base flex items-center relative"> + <div + classList={{ + "flex items-center w-full min-w-0 pr-2": true, + "pl-2": !mac(), + }} + onMouseDown={drag} + > + <Show when={mac()}> + <div class="w-[72px] h-full shrink-0" data-tauri-drag-region /> + </Show> + <IconButton + icon="menu" + variant="ghost" + class="xl:hidden size-8 rounded-md" + onClick={layout.mobileSidebar.toggle} + /> + <TooltipKeybind + class="hidden xl:flex shrink-0" + placement="bottom" + title="Toggle sidebar" + keybind={command.keybind("sidebar.toggle")} + > + <IconButton + icon={layout.sidebar.opened() ? "layout-left" : "layout-right"} + variant="ghost" + class="size-8 rounded-md" + onClick={layout.sidebar.toggle} + /> + </TooltipKeybind> + <div id="opencode-titlebar-left" class="flex items-center gap-3 min-w-0 px-2" /> + <div class="flex-1 h-full" data-tauri-drag-region /> + <div id="opencode-titlebar-right" class="flex items-center gap-3 shrink-0" /> + <Show when={reserve()}> + <div class="w-[120px] h-full shrink-0" data-tauri-drag-region /> + </Show> + </div> + <div class="absolute inset-0 flex items-center justify-center pointer-events-none"> + <div id="opencode-titlebar-center" class="pointer-events-auto" /> + </div> + </header> + ) +} |
