From d9e44a6749014c0b7da81e52f3259463c0ead76b Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Fri, 4 Sep 2026 13:37:24 -0700 Subject: [PATCH] Keep the floating workspace above a working native chat pane (#18692) * Keep the floating workspace above a working native chat pane While an agent is streaming, `data-native-chat-working='true'` promoted the native chat pane shell from z-10 to z-50, above the floating workspace panel's z-45. Both compete in the root stacking context, so the chat column painted over a summoned floating workspace and only the sliver past the chat's right edge stayed visible. Lower the working-state rule to z-44. It still clears the z-40 updater and onboarding chrome the rule was added for (#16729) and the 41-44 band was otherwise empty. Raising the panel instead would put it over the z-50 modal layer it deliberately sits under. The existing layering test pinned z-index 50 with no upper bound, which is what let this through; it now derives both bounds from source. * Stop the layering test reading a comment as the panel class The panel's z-index was read with a lazy match from the data attribute to the first `z-[NN]`. Nearby comments cite bare tiers (the toggle button's own note mentions z-[45]), so a reworded comment landing after the attribute would be read as the class. Strip comments and anchor to `className=`. --------- Co-authored-by: Merge Sim --- src/renderer/src/assets/main.css | 4 +-- .../native-chat-stop-layering.test.ts | 28 +++++++++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/renderer/src/assets/main.css b/src/renderer/src/assets/main.css index 8187fe496c7..e3d267cb353 100644 --- a/src/renderer/src/assets/main.css +++ b/src/renderer/src/assets/main.css @@ -392,9 +392,9 @@ z-index: 40 !important; } -/* Keep interruption controls above unrelated updater/onboarding chrome. */ +/* Above the z-40 updater/onboarding chrome, below the floating workspace panel's z-45. */ .native-chat-pane-shell:has([data-native-chat-working='true']) { - z-index: 50; + z-index: 44; } [data-sonner-toaster] [data-sonner-toast][data-styled='true'] { diff --git a/src/renderer/src/components/native-chat/native-chat-stop-layering.test.ts b/src/renderer/src/components/native-chat/native-chat-stop-layering.test.ts index 716f1dc3182..46bee5d6172 100644 --- a/src/renderer/src/components/native-chat/native-chat-stop-layering.test.ts +++ b/src/renderer/src/components/native-chat/native-chat-stop-layering.test.ts @@ -6,6 +6,15 @@ function source(path: string): string { return readFileSync(join(process.cwd(), path), 'utf8') } +function workingChatZIndex(css: string): number { + const match = + /\.native-chat-pane-shell:has\(\[data-native-chat-working='true'\]\)[^{]*\{[^}]*z-index:\s*(\d+);/s.exec( + css + ) + expect(match, 'working native-chat z-index rule not found in main.css').not.toBeNull() + return Number(match?.[1]) +} + describe('native chat Stop layering', () => { it('keeps a working chat pane above bottom-right product chrome', () => { const css = source('src/renderer/src/assets/main.css') @@ -15,9 +24,24 @@ describe('native chat Stop layering', () => { expect(terminalPane).toContain('native-chat-pane-shell absolute inset-0 z-10') expect(css).toMatch(/\[data-sonner-toaster\][^{]*\{[^}]*z-index:\s*40\s*!important;/s) - expect(css).toMatch( - /\.native-chat-pane-shell:has\(\[data-native-chat-working='true'\]\)[^{]*\{[^}]*z-index:\s*50;/s + expect(workingChatZIndex(css)).toBeGreaterThan(40) + }) + + // Why both bounds: raising the working pane over the panel hides a summoned + // floating workspace behind the chat column while an agent streams. + it('stays under the floating workspace panel while working', () => { + // Comments stripped first: the surrounding layering comment cites bare z-40/z-50 + // tiers, and a reworded one could otherwise be read as the panel's own class. + const panel = source( + 'src/renderer/src/components/floating-terminal/FloatingTerminalPanelSurface.tsx' + ).replace(/\/\*[\s\S]*?\*\/|\/\/[^\n]*/g, '') + const panelZIndex = Number( + /data-floating-terminal-panel[\s\S]*?className=[\s\S]*?z-\[(\d+)\]/.exec(panel)?.[1] ) + + // FloatingTerminalPanel.bounds.test.tsx pins this same 45 through a real render. + expect(panelZIndex).toBe(45) + expect(workingChatZIndex(source('src/renderer/src/assets/main.css'))).toBeLessThan(panelZIndex) }) it('publishes working state from both structured and bridge chat roots', () => {