diff options
| author | adamelmore <[email protected]> | 2026-01-12 08:38:29 -0600 |
|---|---|---|
| committer | Adam <[email protected]> | 2026-01-15 07:29:13 -0600 |
| commit | 9f66a45970d1edf12ae9b3e7a22d77711b5e51c3 (patch) | |
| tree | a34f43cddd3fc53a731af7c2fb631f3ec05c012c /packages/app/src/components | |
| parent | 779610d668e239c07014c9d350d47e2213c07b26 (diff) | |
| download | opencode-9f66a45970d1edf12ae9b3e7a22d77711b5e51c3.tar.gz opencode-9f66a45970d1edf12ae9b3e7a22d77711b5e51c3.zip | |
feat(app): new layout
Diffstat (limited to 'packages/app/src/components')
| -rw-r--r-- | packages/app/src/components/session/session-header.tsx | 402 |
1 files changed, 205 insertions, 197 deletions
diff --git a/packages/app/src/components/session/session-header.tsx b/packages/app/src/components/session/session-header.tsx index cfc6eb438..b2e7fafeb 100644 --- a/packages/app/src/components/session/session-header.tsx +++ b/packages/app/src/components/session/session-header.tsx @@ -1,4 +1,5 @@ import { createMemo, createResource, Show } from "solid-js" +import { Portal } from "solid-js/web" import { A, useNavigate, useParams } from "@solidjs/router" import { useLayout } from "@/context/layout" import { useCommand } from "@/context/command" @@ -57,211 +58,218 @@ export function SessionHeader() { navigate(`/${params.dir}/session/${session.id}`) } + const leftMount = createMemo(() => document.getElementById("opencode-titlebar-left")) + const rightMount = createMemo(() => document.getElementById("opencode-titlebar-right")) + return ( - <header class="h-12 shrink-0 bg-background-base border-b border-border-weak-base flex"> - <button - type="button" - class="xl:hidden w-12 shrink-0 flex items-center justify-center border-r border-border-weak-base hover:bg-surface-raised-base-hover active:bg-surface-raised-base-active transition-colors" - onClick={layout.mobileSidebar.toggle} - > - <Icon name="menu" size="small" /> - </button> - <div class="px-4 flex items-center justify-between gap-4 w-full"> - <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={ - <> + <> + <Show when={leftMount()}> + {(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={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" + 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" - /> - </> - } - > - <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) => { - // Only navigate if selecting a different session than current parent - 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> - <div class="flex items-center gap-1.5 min-w-0"> - <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> + > + {/* @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> - </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> - </Show> - </div> - <div class="flex items-center gap-3"> - <div class="hidden md:flex items-center gap-1"> - <Button - size="small" - variant="ghost" - onClick={() => { - dialog.show(() => <DialogSelectServer />) - }} - > - <div - classList={{ - "size-1.5 rounded-full": true, - "bg-icon-success-base": server.healthy() === true, - "bg-icon-critical-base": server.healthy() === false, - "bg-border-weak-base": server.healthy() === undefined, - }} - /> - <Icon name="server" size="small" class="text-icon-weak" /> - <span class="text-12-regular text-text-weak truncate max-w-[200px]">{server.name}</span> - </Button> - <SessionLspIndicator /> - <SessionMcpIndicator /> - </div> - <div class="flex items-center gap-1"> - <Show when={currentSession()?.summary?.files}> - <TooltipKeybind - class="hidden md:block shrink-0" - title="Toggle review" - keybind={command.keybind("review.toggle")} - > + <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> + </Show> + </div> + </Portal> + )} + </Show> + <Show when={rightMount()}> + {(mount) => ( + <Portal mount={mount()}> + <div class="flex items-center gap-3"> + <div class="hidden md:flex items-center gap-1"> <Button + size="small" variant="ghost" - class="group/review-toggle size-6 p-0" - onClick={() => view().reviewPanel.toggle()} + onClick={() => { + dialog.show(() => <DialogSelectServer />) + }} > - <div class="relative flex items-center justify-center size-4 [&>*]:absolute [&>*]:inset-0"> - <Icon - name={view().reviewPanel.opened() ? "layout-right" : "layout-left"} - size="small" - class="group-hover/review-toggle:hidden" - /> - <Icon - name={view().reviewPanel.opened() ? "layout-right-partial" : "layout-left-partial"} - size="small" - class="hidden group-hover/review-toggle:inline-block" - /> - <Icon - name={view().reviewPanel.opened() ? "layout-right-full" : "layout-left-full"} - size="small" - class="hidden group-active/review-toggle:inline-block" - /> - </div> - </Button> - </TooltipKeybind> - </Show> - <TooltipKeybind - class="hidden md:block shrink-0" - title="Toggle terminal" - keybind={command.keybind("terminal.toggle")} - > - <Button variant="ghost" class="group/terminal-toggle size-6 p-0" onClick={() => view().terminal.toggle()}> - <div class="relative flex items-center justify-center size-4 [&>*]:absolute [&>*]:inset-0"> - <Icon - size="small" - name={view().terminal.opened() ? "layout-bottom-full" : "layout-bottom"} - class="group-hover/terminal-toggle:hidden" + <div + classList={{ + "size-1.5 rounded-full": true, + "bg-icon-success-base": server.healthy() === true, + "bg-icon-critical-base": server.healthy() === false, + "bg-border-weak-base": server.healthy() === undefined, + }} /> - <Icon - size="small" - name="layout-bottom-partial" - class="hidden group-hover/terminal-toggle:inline-block" - /> - <Icon - size="small" - name={view().terminal.opened() ? "layout-bottom" : "layout-bottom-full"} - class="hidden group-active/terminal-toggle:inline-block" - /> - </div> - </Button> - </TooltipKeybind> - </div> - <Show when={shareEnabled() && currentSession()}> - <Popover - title="Share session" - trigger={ - <Tooltip class="shrink-0" value="Share session"> - <IconButton icon="share" variant="ghost" class="" /> - </Tooltip> - } - > - {iife(() => { - const [url] = createResource( - () => currentSession(), - async (session) => { - if (!session) return - let shareURL = session.share?.url - if (!shareURL) { - shareURL = await globalSDK.client.session - .share({ sessionID: session.id, directory: projectDirectory() }) - .then((r) => r.data?.share?.url) - .catch((e) => { - console.error("Failed to share session", e) - return undefined - }) - } - return shareURL - }, - { initialValue: "" }, - ) - return ( - <Show when={url.latest}> - {(shareUrl) => <TextField value={shareUrl()} readOnly copyable class="w-72" />} - </Show> - ) - })} - </Popover> - </Show> - </div> - </div> - </header> + <Icon name="server" size="small" class="text-icon-weak" /> + <span class="text-12-regular text-text-weak truncate max-w-[200px]">{server.name}</span> + </Button> + <SessionLspIndicator /> + <SessionMcpIndicator /> + </div> + <div class="flex items-center gap-1"> + <Show when={currentSession()?.summary?.files}> + <TooltipKeybind + class="hidden md:block shrink-0" + title="Toggle review" + keybind={command.keybind("review.toggle")} + > + <Button + variant="ghost" + class="group/review-toggle size-6 p-0" + onClick={() => view().reviewPanel.toggle()} + > + <div class="relative flex items-center justify-center size-4 [&>*]:absolute [&>*]:inset-0"> + <Icon + name={view().reviewPanel.opened() ? "layout-right" : "layout-left"} + size="small" + class="group-hover/review-toggle:hidden" + /> + <Icon + name={view().reviewPanel.opened() ? "layout-right-partial" : "layout-left-partial"} + size="small" + class="hidden group-hover/review-toggle:inline-block" + /> + <Icon + name={view().reviewPanel.opened() ? "layout-right-full" : "layout-left-full"} + size="small" + class="hidden group-active/review-toggle:inline-block" + /> + </div> + </Button> + </TooltipKeybind> + </Show> + <TooltipKeybind + class="hidden md:block shrink-0" + title="Toggle terminal" + keybind={command.keybind("terminal.toggle")} + > + <Button + variant="ghost" + class="group/terminal-toggle size-6 p-0" + onClick={() => view().terminal.toggle()} + > + <div class="relative flex items-center justify-center size-4 [&>*]:absolute [&>*]:inset-0"> + <Icon + size="small" + name={view().terminal.opened() ? "layout-bottom-full" : "layout-bottom"} + class="group-hover/terminal-toggle:hidden" + /> + <Icon + size="small" + name="layout-bottom-partial" + class="hidden group-hover/terminal-toggle:inline-block" + /> + <Icon + size="small" + name={view().terminal.opened() ? "layout-bottom" : "layout-bottom-full"} + class="hidden group-active/terminal-toggle:inline-block" + /> + </div> + </Button> + </TooltipKeybind> + </div> + <Show when={shareEnabled() && currentSession()}> + <Popover + title="Share session" + trigger={ + <Tooltip class="shrink-0" value="Share session"> + <IconButton icon="share" variant="ghost" class="" /> + </Tooltip> + } + > + {iife(() => { + const [url] = createResource( + () => currentSession(), + async (session) => { + if (!session) return + let shareURL = session.share?.url + if (!shareURL) { + shareURL = await globalSDK.client.session + .share({ sessionID: session.id, directory: projectDirectory() }) + .then((r) => r.data?.share?.url) + .catch((e) => { + console.error("Failed to share session", e) + return undefined + }) + } + return shareURL + }, + { initialValue: "" }, + ) + return ( + <Show when={url.latest}> + {(shareUrl) => <TextField value={shareUrl()} readOnly copyable class="w-72" />} + </Show> + ) + })} + </Popover> + </Show> + </div> + </Portal> + )} + </Show> + </> ) } |
