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 <sim@local>
This commit is contained in:
Brennan Benson
2026-09-04 13:37:24 -07:00
committed by GitHub
co-authored by Merge Sim
parent b0253673c9
commit d9e44a6749
2 changed files with 28 additions and 4 deletions
+2 -2
View File
@@ -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'] {
@@ -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', () => {