diff options
| author | adamelmore <[email protected]> | 2026-01-09 08:19:39 -0600 |
|---|---|---|
| committer | adamelmore <[email protected]> | 2026-01-09 08:20:00 -0600 |
| commit | 0433d4d064e38430980797a1be179397aec0febd (patch) | |
| tree | 46127e011bcd36cdc8a4676c6dd571462ff3330a /packages | |
| parent | d34fdac85450c26ae422b00bd151eab797ea7e2e (diff) | |
| download | opencode-0433d4d064e38430980797a1be179397aec0febd.tar.gz opencode-0433d4d064e38430980797a1be179397aec0febd.zip | |
fix(app): store terminal and review pane visibility per session
Diffstat (limited to 'packages')
| -rw-r--r-- | packages/app/src/components/dialog-select-file.tsx | 3 | ||||
| -rw-r--r-- | packages/app/src/components/session-context-usage.tsx | 3 | ||||
| -rw-r--r-- | packages/app/src/components/session/session-header.tsx | 20 | ||||
| -rw-r--r-- | packages/app/src/context/layout.tsx | 86 | ||||
| -rw-r--r-- | packages/app/src/pages/session.tsx | 16 |
5 files changed, 87 insertions, 41 deletions
diff --git a/packages/app/src/components/dialog-select-file.tsx b/packages/app/src/components/dialog-select-file.tsx index 9e3bbeddd..461f8a0c0 100644 --- a/packages/app/src/components/dialog-select-file.tsx +++ b/packages/app/src/components/dialog-select-file.tsx @@ -15,6 +15,7 @@ export function DialogSelectFile() { const params = useParams() const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`) const tabs = createMemo(() => layout.tabs(sessionKey())) + const view = createMemo(() => layout.view(sessionKey())) return ( <Dialog title="Select file"> <List @@ -27,7 +28,7 @@ export function DialogSelectFile() { const value = file.tab(path) tabs().open(value) file.load(path) - layout.review.open() + view().reviewPanel.open() } dialog.close() }} diff --git a/packages/app/src/components/session-context-usage.tsx b/packages/app/src/components/session-context-usage.tsx index 53e578214..680f32713 100644 --- a/packages/app/src/components/session-context-usage.tsx +++ b/packages/app/src/components/session-context-usage.tsx @@ -20,6 +20,7 @@ export function SessionContextUsage(props: SessionContextUsageProps) { const variant = createMemo(() => props.variant ?? "button") const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`) const tabs = createMemo(() => layout.tabs(sessionKey())) + const view = createMemo(() => layout.view(sessionKey())) const messages = createMemo(() => (params.id ? (sync.data.message[params.id] ?? []) : [])) const cost = createMemo(() => { @@ -48,7 +49,7 @@ export function SessionContextUsage(props: SessionContextUsageProps) { const openContext = () => { if (!params.id) return - layout.review.open() + view().reviewPanel.open() tabs().open("context") tabs().setActive("context") } diff --git a/packages/app/src/components/session/session-header.tsx b/packages/app/src/components/session/session-header.tsx index 4958ad2c3..cfc6eb438 100644 --- a/packages/app/src/components/session/session-header.tsx +++ b/packages/app/src/components/session/session-header.tsx @@ -43,6 +43,8 @@ export function SessionHeader() { }) const shareEnabled = createMemo(() => sync.data.config.share !== "disabled") const worktrees = createMemo(() => layout.projects.list().map((p) => p.worktree), [], { equals: same }) + const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`) + const view = createMemo(() => layout.view(sessionKey())) function navigateToProject(directory: string) { navigate(`/${base64Encode(directory)}`) @@ -171,20 +173,24 @@ export function SessionHeader() { title="Toggle review" keybind={command.keybind("review.toggle")} > - <Button variant="ghost" class="group/review-toggle size-6 p-0" onClick={layout.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={layout.review.opened() ? "layout-right" : "layout-left"} + name={view().reviewPanel.opened() ? "layout-right" : "layout-left"} size="small" class="group-hover/review-toggle:hidden" /> <Icon - name={layout.review.opened() ? "layout-right-partial" : "layout-left-partial"} + name={view().reviewPanel.opened() ? "layout-right-partial" : "layout-left-partial"} size="small" class="hidden group-hover/review-toggle:inline-block" /> <Icon - name={layout.review.opened() ? "layout-right-full" : "layout-left-full"} + name={view().reviewPanel.opened() ? "layout-right-full" : "layout-left-full"} size="small" class="hidden group-active/review-toggle:inline-block" /> @@ -197,11 +203,11 @@ export function SessionHeader() { title="Toggle terminal" keybind={command.keybind("terminal.toggle")} > - <Button variant="ghost" class="group/terminal-toggle size-6 p-0" onClick={layout.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={layout.terminal.opened() ? "layout-bottom-full" : "layout-bottom"} + name={view().terminal.opened() ? "layout-bottom-full" : "layout-bottom"} class="group-hover/terminal-toggle:hidden" /> <Icon @@ -211,7 +217,7 @@ export function SessionHeader() { /> <Icon size="small" - name={layout.terminal.opened() ? "layout-bottom" : "layout-bottom-full"} + name={view().terminal.opened() ? "layout-bottom" : "layout-bottom-full"} class="hidden group-active/terminal-toggle:inline-block" /> </div> diff --git a/packages/app/src/context/layout.tsx b/packages/app/src/context/layout.tsx index 2d92409f0..385f564fa 100644 --- a/packages/app/src/context/layout.tsx +++ b/packages/app/src/context/layout.tsx @@ -33,6 +33,8 @@ type SessionTabs = { type SessionView = { scroll: Record<string, SessionScroll> reviewOpen?: string[] + terminalOpened?: boolean + reviewPanelOpened?: boolean } export type LocalProject = Partial<Project> & { worktree: string; expanded: boolean } @@ -53,11 +55,9 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( width: 280, }, terminal: { - opened: false, height: 280, }, review: { - opened: true, diffStyle: "split" as ReviewDiffStyle, }, session: { @@ -150,7 +150,7 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( const current = store.sessionView[sessionKey] const keep = meta.active ?? sessionKey if (!current) { - setStore("sessionView", sessionKey, { scroll: next }) + setStore("sessionView", sessionKey, { scroll: next, terminalOpened: false, reviewPanelOpened: true }) prune(keep) return } @@ -306,40 +306,20 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( }, }, terminal: { - opened: createMemo(() => store.terminal.opened), - open() { - setStore("terminal", "opened", true) - }, - close() { - setStore("terminal", "opened", false) - }, - toggle() { - setStore("terminal", "opened", (x) => !x) - }, height: createMemo(() => store.terminal.height), resize(height: number) { setStore("terminal", "height", height) }, }, review: { - opened: createMemo(() => store.review?.opened ?? true), diffStyle: createMemo(() => store.review?.diffStyle ?? "split"), setDiffStyle(diffStyle: ReviewDiffStyle) { if (!store.review) { - setStore("review", { opened: true, diffStyle }) + setStore("review", { diffStyle }) return } setStore("review", "diffStyle", diffStyle) }, - open() { - setStore("review", "opened", true) - }, - close() { - setStore("review", "opened", false) - }, - toggle() { - setStore("review", "opened", (x) => !x) - }, }, session: { width: createMemo(() => store.session?.width ?? 600), @@ -367,6 +347,33 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( touch(sessionKey) scroll.seed(sessionKey) const s = createMemo(() => store.sessionView[sessionKey] ?? { scroll: {} }) + const terminalOpened = createMemo(() => s().terminalOpened ?? false) + const reviewPanelOpened = createMemo(() => s().reviewPanelOpened ?? true) + + function setTerminalOpened(next: boolean) { + const current = store.sessionView[sessionKey] + if (!current) { + setStore("sessionView", sessionKey, { scroll: {}, terminalOpened: next, reviewPanelOpened: true }) + return + } + + const value = current.terminalOpened ?? false + if (value === next) return + setStore("sessionView", sessionKey, "terminalOpened", next) + } + + function setReviewPanelOpened(next: boolean) { + const current = store.sessionView[sessionKey] + if (!current) { + setStore("sessionView", sessionKey, { scroll: {}, terminalOpened: false, reviewPanelOpened: next }) + return + } + + const value = current.reviewPanelOpened ?? true + if (value === next) return + setStore("sessionView", sessionKey, "reviewPanelOpened", next) + } + return { scroll(tab: string) { return scroll.scroll(sessionKey, tab) @@ -374,12 +381,41 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( setScroll(tab: string, pos: SessionScroll) { scroll.setScroll(sessionKey, tab, pos) }, + terminal: { + opened: terminalOpened, + open() { + setTerminalOpened(true) + }, + close() { + setTerminalOpened(false) + }, + toggle() { + setTerminalOpened(!terminalOpened()) + }, + }, + reviewPanel: { + opened: reviewPanelOpened, + open() { + setReviewPanelOpened(true) + }, + close() { + setReviewPanelOpened(false) + }, + toggle() { + setReviewPanelOpened(!reviewPanelOpened()) + }, + }, review: { open: createMemo(() => s().reviewOpen), setOpen(open: string[]) { const current = store.sessionView[sessionKey] if (!current) { - setStore("sessionView", sessionKey, { scroll: {}, reviewOpen: open }) + setStore("sessionView", sessionKey, { + scroll: {}, + terminalOpened: false, + reviewPanelOpened: true, + reviewOpen: open, + }) return } diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx index 58f1e0a64..ab6995d92 100644 --- a/packages/app/src/pages/session.tsx +++ b/packages/app/src/pages/session.tsx @@ -377,7 +377,7 @@ export default function Page() { }) createEffect(() => { - if (!layout.terminal.opened()) return + if (!view().terminal.opened()) return if (!terminal.ready()) return if (terminal.all().length !== 0) return terminal.new() @@ -440,7 +440,7 @@ export default function Page() { category: "View", keybind: "ctrl+`", slash: "terminal", - onSelect: () => layout.terminal.toggle(), + onSelect: () => view().terminal.toggle(), }, { id: "review.toggle", @@ -448,7 +448,7 @@ export default function Page() { description: "Show or hide the review panel", category: "View", keybind: "mod+shift+r", - onSelect: () => layout.review.toggle(), + onSelect: () => view().reviewPanel.toggle(), }, { id: "terminal.new", @@ -720,7 +720,9 @@ export default function Page() { const reviewTab = createMemo(() => hasReview() || tabs().active() === "review") const mobileReview = createMemo(() => !isDesktop() && hasReview() && store.mobileTab === "review") - const showTabs = createMemo(() => layout.review.opened() && (hasReview() || tabs().all().length > 0 || contextOpen())) + const showTabs = createMemo( + () => view().reviewPanel.opened() && (hasReview() || tabs().all().length > 0 || contextOpen()), + ) const activeTab = createMemo(() => { const active = tabs().active() @@ -745,7 +747,7 @@ export default function Page() { if (!id) return if (!hasReview()) return - const wants = isDesktop() ? layout.review.opened() && activeTab() === "review" : store.mobileTab === "review" + const wants = isDesktop() ? view().reviewPanel.opened() && activeTab() === "review" : store.mobileTab === "review" if (!wants) return if (diffsReady()) return @@ -1600,7 +1602,7 @@ export default function Page() { </Show> </div> - <Show when={isDesktop() && layout.terminal.opened()}> + <Show when={isDesktop() && view().terminal.opened()}> <div class="relative w-full flex-col shrink-0 border-t border-border-weak-base" style={{ height: `${layout.terminal.height()}px` }} @@ -1612,7 +1614,7 @@ export default function Page() { max={window.innerHeight * 0.6} collapseThreshold={50} onResize={layout.terminal.resize} - onCollapse={layout.terminal.close} + onCollapse={view().terminal.close} /> <Show when={terminal.ready()} |
