diff --git a/packages/app/src/components/session-context-usage.tsx b/packages/app/src/components/session-context-usage.tsx index 0d6b761c94..d08f2b4869 100644 --- a/packages/app/src/components/session-context-usage.tsx +++ b/packages/app/src/components/session-context-usage.tsx @@ -26,7 +26,7 @@ function openSessionContext(args: { layout: ReturnType tabs: ReturnType["tabs"]> }) { - if (!args.view.reviewPanel.opened()) args.view.reviewPanel.open() + args.view.reviewPanel.open(args.view.reviewPanel.opened() ? "other" : "context-button") if (args.layout.fileTree.opened() && args.layout.fileTree.tab() !== "all") args.layout.fileTree.setTab("all") void args.tabs.open("context") args.tabs.setActive("context") @@ -64,16 +64,21 @@ export function SessionContextUsage(props: SessionContextUsageProps) { const cost = createMemo(() => { return usd().format(info()?.cost ?? 0) }) + const contextVisible = createMemo(() => view().reviewPanel.opened() && tabState.activeTab() === "context") + const hasOtherTabs = createMemo(() => tabs().all().some((tab) => tab !== "context" && tab !== "review")) const openContext = () => { if (!params.id) return - if (tabState.activeTab() === "context") { + const sessionView = view() + if (contextVisible()) { tabs().close("context") + if (sessionView.reviewPanel.source() === "context-button" && !hasOtherTabs()) sessionView.reviewPanel.close() return } + openSessionContext({ - view: view(), + view: sessionView, layout, tabs: tabs(), }) @@ -81,7 +86,20 @@ export function SessionContextUsage(props: SessionContextUsageProps) { const circle = () => (
- +
) const circleV2 = () => ( @@ -119,10 +137,10 @@ export function SessionContextUsage(props: SessionContextUsageProps) { return ( - - - {circle()} - + + {circle()} + + - - + + + + - - - + + + ) } diff --git a/packages/app/src/context/layout.tsx b/packages/app/src/context/layout.tsx index 2e4684bbce..1487676e54 100644 --- a/packages/app/src/context/layout.tsx +++ b/packages/app/src/context/layout.tsx @@ -78,6 +78,7 @@ export type LocalProject = Partial & { worktree: string; expanded: bool export type HomeProjectSelection = { server: ServerConnection.Key; directory?: string } export type ReviewDiffStyle = "unified" | "split" +export type ReviewPanelSource = "context-button" | "other" export type LayoutRoute = | { type: "home" } @@ -304,6 +305,9 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( }, }), ) + const [ephemeral, setEphemeral] = createStore({ + reviewPanelSource: "other" as ReviewPanelSource, + }) const MAX_SESSION_KEYS = 50 const PENDING_MESSAGE_TTL_MS = 2 * 60 * 1000 @@ -780,6 +784,7 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( const s = createMemo(() => store.sessionView[key()] ?? { scroll: {} }) const terminalOpened = createMemo(() => store.terminal?.opened ?? false) const reviewPanelOpened = createMemo(() => store.review?.panelOpened ?? DEFAULT_REVIEW_PANEL_OPENED) + const reviewPanelSource = createMemo(() => (reviewPanelOpened() ? ephemeral.reviewPanelSource : "other")) function setTerminalOpened(next: boolean) { const current = store.terminal @@ -793,16 +798,26 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( setStore("terminal", "opened", next) } - function setReviewPanelOpened(next: boolean) { + function setReviewPanelOpened(next: boolean, source: ReviewPanelSource) { + const nextSource = next ? source : "other" const current = store.review if (!current) { - setStore("review", { diffStyle: "split" as ReviewDiffStyle, panelOpened: next }) + batch(() => { + setStore("review", { diffStyle: "split" as ReviewDiffStyle, panelOpened: next }) + setEphemeral("reviewPanelSource", nextSource) + }) return } const value = current.panelOpened ?? DEFAULT_REVIEW_PANEL_OPENED - if (value === next) return - setStore("review", "panelOpened", next) + if (value === next) { + if (ephemeral.reviewPanelSource !== nextSource) setEphemeral("reviewPanelSource", nextSource) + return + } + batch(() => { + setStore("review", "panelOpened", next) + setEphemeral("reviewPanelSource", nextSource) + }) } return { @@ -838,14 +853,15 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( }, reviewPanel: { opened: reviewPanelOpened, - open() { - setReviewPanelOpened(true) + source: reviewPanelSource, + open(source: ReviewPanelSource = "other") { + setReviewPanelOpened(true, source) }, close() { - setReviewPanelOpened(false) + setReviewPanelOpened(false, "other") }, toggle() { - setReviewPanelOpened(!reviewPanelOpened()) + setReviewPanelOpened(!reviewPanelOpened(), "other") }, }, review: { diff --git a/packages/ui/src/components/progress-circle.css b/packages/ui/src/components/progress-circle.css index afaf72af61..4d1e79cddc 100644 --- a/packages/ui/src/components/progress-circle.css +++ b/packages/ui/src/components/progress-circle.css @@ -2,11 +2,15 @@ transform: rotate(-90deg); [data-slot="progress-circle-background"] { - stroke: var(--border-weak-base); + stroke: var(--progress-circle-background, var(--border-weak-base)); + } + + [data-slot="progress-circle-background-overlay"] { + stroke: var(--progress-circle-background-overlay, transparent); } [data-slot="progress-circle-progress"] { - stroke: var(--border-active); + stroke: var(--progress-circle-progress, var(--border-active)); transition: stroke-dashoffset 0.35s cubic-bezier(0.65, 0, 0.35, 1); } } diff --git a/packages/ui/src/components/progress-circle.tsx b/packages/ui/src/components/progress-circle.tsx index 992fb62e83..7c764b5dd1 100644 --- a/packages/ui/src/components/progress-circle.tsx +++ b/packages/ui/src/components/progress-circle.tsx @@ -1,6 +1,6 @@ import { type ComponentProps, createMemo, splitProps } from "solid-js" -export interface ProgressCircleProps extends Pick, "class" | "classList"> { +export interface ProgressCircleProps extends Pick, "class" | "classList" | "style"> { percentage: number size?: number strokeWidth?: number @@ -43,6 +43,13 @@ export function ProgressCircle(props: ProgressCircleProps) { data-slot="progress-circle-background" stroke-width={strokeWidth()} /> +