Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions packages/app-bundle/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@
"packages/app/src/pages/new-session-landing.ts": "528d27cd5d441b521674328fb7812fb2acf77233e9e436d081c98ce611e8b268",
"packages/app/src/pages/new-session.tsx": "c060bbb15569b6da2f67146e6af0879ccb5444345df03dbc2f9b39cb98b9db78",
"packages/app/src/pages/overlay-content-hash.test.ts": "aa208098213522dcde42434408afe9c8641fad663f44a58c316922dbd0da2f5c",
"packages/app/src/pages/session.tsx": "e502d2274c351612aacbed4dbee49e8692da1fb6b3411fbd1491499737772a39",
"packages/app/src/pages/session.tsx": "a76358b41641c2568b0904b4260f9d0e0b5747424751859e48957a5b32c46d67",
"packages/app/src/utils/amicode-bridge.ts": "5104a04c7822f80dc0c564619e7dd2d850ba50035dbc86ea875604ecd97df05f",
"packages/app/src/utils/amicode-bug-report.test.ts": "b1bbf49230ff3aa6d1a43ffa9165ba5a84ca4ee18a6d49f52ee5cd887c3e6444",
"packages/app/src/utils/amicode-bug-report.ts": "d55d9cecfe17719c0d4a0ca498345c7eff6fca1de6b645e4bc3fecb40f2f4645",
Expand Down Expand Up @@ -692,7 +692,7 @@
"packages/app/src/pages/session/helpers.ts": "0311f52b7cc34c2c38950512c01397e9672c89a9e61fdfa0e4696192da114f34",
"packages/app/src/pages/session/review-tab.tsx": "3c90e6b11fad40ea19a258966c374ee12c05bd0e7891fcea561ba0b965ddc9f5",
"packages/app/src/pages/session/session-panel-width.test.ts": "9482afba7fbce254cbbd21ef885e9e9620b981616681635376f466478f611155",
"packages/app/src/pages/session/session-panel-width.ts": "02ee3a02ed78db2eac47c2528055cf78aef95f389641e929ddc56c17471dcbe2",
"packages/app/src/pages/session/session-panel-width.ts": "654f84b2b43d9e2b0d5f021a1f859d69fe9ee579c88039a492dd3c645d087bea",
"packages/app/src/pages/session/session-side-panel-structure.test.ts": "b2d1d007d9200d0ac4a0889e45c2dbe57a324a10cb26ae9ab8f420d3c890402b",
"packages/app/src/pages/session/session-side-panel.tsx": "b295340d9a39ba37c9e025d6360669d2a879284c4e60df795b7902c5a4cef3fc",
"packages/app/src/pages/session/terminal-panel-v2.tsx": "68dad9307f1d2abf3ff9248e451bd08005acf01ddc46b30a6d01b58f4a90dce0",
Expand Down Expand Up @@ -873,7 +873,7 @@
"packages/ui/src/v2/components/text-shimmer-v2.tsx"
],
"exceptions": [],
"extracted_at": "2026-09-24T04:46:38.771Z",
"extracted_at": "2026-09-24T20:34:57.925Z",
"per_package": {
"packages/app": {
"A": 186,
Expand Down
112 changes: 108 additions & 4 deletions packages/app-bundle/overlay/packages/app/src/pages/session.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ import { restorePromptModel, syncPromptModel, syncSessionModel } from "@/pages/s
import {
clampSessionPanelWidth,
clampWorkColumnWidth,
CHAT_COLLAPSE_THRESHOLD,
SESSION_PANEL_WIDTH_MIN,
sessionChatTakesRemainder,
sessionPanelWidthMax,
Expand Down Expand Up @@ -707,9 +708,54 @@ export default function Page() {
const chatTakesRemainder = createMemo(() =>
sessionChatTakesRemainder({ newDesign: newSessionDesign(), columnVisible: isDesktop() && desktopV2PanelLayout().visible }),
)
const workColumnWidth = createMemo(() =>
clampWorkColumnWidth({ width: layout.panelColumn.width(), available: sessionPanelAvailable() }),
)
const workColumnWidth = createMemo(() => {
// #1434: when Chat is collapsed, the Work Column fills the full row
if (chatCollapsed()) return sessionPanelAvailable() ?? 900
return clampWorkColumnWidth({ width: layout.panelColumn.width(), available: sessionPanelAvailable() })
})

// #1434: Work Column maximize — collapse the Chat panel to give Preview
// full width. The pre-maximize column width is remembered for restore.
const [chatCollapsed, setChatCollapsed] = createSignal(false)
const [preMaximizeWidth, setPreMaximizeWidth] = createSignal<number | undefined>(undefined)

const chatEffectivelyCollapsed = createMemo(() => {
if (chatCollapsed()) return true
// Also collapse when the user drags the column so wide the chat is squeezed
// below the threshold.
const available = sessionPanelAvailable()
if (available === undefined) return false
return available - workColumnWidth() < CHAT_COLLAPSE_THRESHOLD
})

function toggleMaximize() {
if (chatCollapsed()) {
// Restore: set column back to pre-maximize width
setChatCollapsed(false)
const restoreWidth = preMaximizeWidth() ?? WORK_COLUMN_WIDTH_MIN
layout.panelColumn.resize(restoreWidth)
} else {
// Maximize: remember current width, then expand column to fill
setPreMaximizeWidth(workColumnWidth())
setChatCollapsed(true)
const available = sessionPanelAvailable() ?? 900
layout.panelColumn.resize(available)
}
}

// Cmd+Shift+M (Ctrl+Shift+M on Linux/Windows) toggles maximize
createEffect(() => {
if (!newSessionDesign()) return
function onKeyDown(e: KeyboardEvent) {
if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key.toLowerCase() === "m") {
e.preventDefault()
toggleMaximize()
}
}
window.addEventListener("keydown", onKeyDown)
onCleanup(() => window.removeEventListener("keydown", onKeyDown))
})

const sessionPanelWidth = createMemo(() => {
if (chatTakesRemainder()) return undefined
if (!desktopSidePanelOpen()) return "100%"
Expand Down Expand Up @@ -2739,13 +2785,25 @@ export default function Page() {
>
<Show when={!isDesktop() && !!params.id && !settings.general.newLayoutDesigns()}>{mobileTabs()}</Show>

{/* #1434: restore chevron when Chat is collapsed */}
<Show when={newSessionDesign() && chatEffectivelyCollapsed()}>
<button
class="shrink-0 flex items-center justify-center w-6 h-full hover:bg-background-interactive-hover rounded transition-colors"
title={`Show Chat (${navigator.platform.includes("Mac") ? "⌘" : "Ctrl"}⇧M)`}
onClick={toggleMaximize}
>
<svg width="16" height="16" viewBox="0 0 20 20" fill="none" class="text-text-dimmed"><path d="M7.5 15L12.5 10L7.5 5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
</button>
</Show>

<div
classList={{
"@container relative shrink-0 flex flex-col min-h-0 h-full transition-[width]": true,
"flex-1 md:flex-none": !chatTakesRemainder(),
"flex-1": chatTakesRemainder(),
"duration-[240ms] ease-[cubic-bezier(0.22,1,0.36,1)] will-change-[width] motion-reduce:transition-none":
!size.active() && !ui.reviewSnap && !desktopInlineTerminalOnlyOpen(),
"!hidden": newSessionDesign() && chatEffectivelyCollapsed(),
}}
style={{
width: sessionPanelWidth(),
Expand Down Expand Up @@ -2816,7 +2874,30 @@ export default function Page() {
edge), sizing layout.panelColumn within the policy bounds —
the chat flexes around it, never below it. */}
<Show when={isDesktop()}>
<div onPointerDown={() => size.start()}>
<div onPointerDown={() => size.start()} onDblClick={(e) => {
e.preventDefault()
// #1434: double-click cycles: 50/50 → full Preview → default
const available = sessionPanelAvailable() ?? 900
const current = workColumnWidth()
const half = Math.floor(available * 0.5)
const full = available
const def = WORK_COLUMN_WIDTH_MIN
// Determine which snap to go to next
if (Math.abs(current - half) < 20) {
// Currently ~50/50 → go to full
setChatCollapsed(true)
setPreMaximizeWidth(half)
layout.panelColumn.resize(full)
} else if (current > available - CHAT_COLLAPSE_THRESHOLD) {
// Currently full → go to default
setChatCollapsed(false)
layout.panelColumn.resize(def)
} else {
// Currently at some other width → go to 50/50
setChatCollapsed(false)
layout.panelColumn.resize(half)
}
}}>
<ResizeHandle
direction="horizontal"
edge="start"
Expand All @@ -2826,9 +2907,32 @@ export default function Page() {
onResize={(width) => {
size.touch()
layout.panelColumn.resize(width)
// #1434: if dragging pushes chat below collapse threshold,
// auto-collapse it
const available = sessionPanelAvailable() ?? 900
if (available - width < CHAT_COLLAPSE_THRESHOLD && !chatCollapsed()) {
setChatCollapsed(true)
setPreMaximizeWidth(width)
} else if (available - width >= CHAT_COLLAPSE_THRESHOLD && chatCollapsed()) {
setChatCollapsed(false)
}
}}
/>
</div>
{/* #1434: Maximize/restore toggle button */}
<button
class="absolute top-1 right-1 z-10 flex items-center justify-center w-6 h-6 rounded hover:bg-background-interactive-hover transition-colors opacity-60 hover:opacity-100"
title={chatEffectivelyCollapsed()
? `Show Chat (${navigator.platform.includes("Mac") ? "⌘" : "Ctrl"}⇧M)`
: `Maximize Preview (${navigator.platform.includes("Mac") ? "⌘" : "Ctrl"}⇧M)`
}
onClick={toggleMaximize}
>
{chatEffectivelyCollapsed()
? <svg width="16" height="16" viewBox="0 0 20 20" fill="none" class="text-text-dimmed"><path d="M12.5 15L7.5 10L12.5 5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
: <svg width="16" height="16" viewBox="0 0 20 20" fill="none" class="text-text-dimmed"><path d="M7.5 15L12.5 10L7.5 5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
}
</button>
</Show>
<Show when={isDesktop() && (desktopV2ReviewOpen() || desktopFileTreeOpen())}>
<div class="min-h-0 flex-1">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,19 @@ export function clampSessionPanelWidth(input: { width: number; available: number
}

// amicode#105: the Work Column owns a bounded width of its own. The pre-fix
// philosophy (above) let the review pane take everything the chat left behind
// philosophy (above) let the review pane take whatever the chat left behind
// — a wide monitor squished the chat into the left margin. Now the column is
// fixed-width (default 320 — DEFAULT_PANEL_COLUMN_WIDTH — user-resizable
// within these bounds) and the CHAT is the flex remainder.
export const WORK_COLUMN_WIDTH_MIN = 330

/** The column may never take more than 60% of the measured row. */
/** Minimum chat width before it collapses entirely (#1434). */
export const CHAT_COLLAPSE_THRESHOLD = 200

/** The column may expand to fill the full row — the chat collapses when
* squeezed below CHAT_COLLAPSE_THRESHOLD (#1434). */
export function workColumnWidthMax(available: number) {
return Math.max(WORK_COLUMN_WIDTH_MIN, Math.floor(available * 0.6))
return Math.max(WORK_COLUMN_WIDTH_MIN, available)
}

/** `available` is undefined until the layout row is first measured; render the
Expand Down
Loading