Skip to content

Commit a97ed5c

Browse files
authored
fix(app): make the session details popover match menus (#60494)
1 parent 2339bda commit a97ed5c

5 files changed

Lines changed: 117 additions & 103 deletions

File tree

‎packages/app/e2e/regression/session-summary.spec.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -870,9 +870,10 @@ test("long MCP lists stay in the viewport in a real RTL locale", async ({ page }
870870
await summary.getByRole("button", { name: "MCP", exact: true }).click()
871871
const menu = page.getByRole("group", { name: "MCP", exact: true })
872872
await expect(menu.locator(".session-mcp-row")).toHaveCount(30)
873-
await expect.poll(() => menu.evaluate((element) => element.scrollHeight > element.clientHeight)).toBe(true)
873+
const list = menu.locator("[data-scrollable]")
874+
await expect.poll(() => list.evaluate((element) => element.scrollHeight > element.clientHeight)).toBe(true)
874875
// Long names truncate; the list only scrolls vertically.
875-
expect(await menu.evaluate((element) => element.scrollWidth - element.clientWidth)).toBe(0)
876+
expect(await list.evaluate((element) => element.scrollWidth - element.clientWidth)).toBe(0)
876877
// The submenu shows and moves without motion unless its Knobs toggle is on.
877878
expect(
878879
await page.locator(".session-service-flyout").evaluate((element) => getComputedStyle(element).transitionDuration),

‎packages/gui-plugins/src/details/details.css‎

Lines changed: 57 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
.session-summary-popover {
2-
transform-origin: var(--kb-popover-content-transform-origin);
3-
animation: menu-v2-in 120ms ease-out;
1+
/* Only the card scrolls, so neither its shadow nor the Extensions flyout beside it is clipped. */
2+
.session-summary-popover .session-summary-card {
3+
max-height: calc(100dvh - 96px);
4+
overflow-y: auto;
5+
background: var(--v2-background-bg-layer-01);
6+
box-shadow: var(--v2-elevation-floating);
47
}
58

69
[data-component="session-summary-panel"] {
@@ -44,13 +47,13 @@
4447
align-items: center;
4548
gap: 8px;
4649
width: 100%;
47-
min-height: 32px;
50+
min-height: 28px;
4851
padding: 4px 12px;
4952
border-radius: 4px;
5053
color: var(--v2-text-text-base);
5154
font-size: 13px;
5255
font-weight: 440;
53-
line-height: var(--line-height-base);
56+
line-height: var(--line-height-compact);
5457
text-align: start;
5558
}
5659

@@ -92,12 +95,13 @@ button.session-service-row:focus-visible,
9295
}
9396

9497
.session-summary-row[data-expanded] {
95-
background: var(--v2-overlay-simple-overlay-pressed);
98+
background: var(--v2-overlay-simple-overlay-hover);
9699
}
97100

98101
.session-summary-heading {
99102
color: var(--v2-text-text-faint);
100-
font-weight: 530;
103+
font-weight: 440;
104+
user-select: none;
101105
}
102106

103107
.session-summary-label {
@@ -154,9 +158,6 @@ button.session-service-row:focus-visible,
154158
}
155159

156160
.session-service-flyout-body {
157-
/* The shadow follows the clipped surface, so it is a filter rather than a box shadow. */
158-
filter: drop-shadow(0 8px 8px var(--v2-alpha-dark-4)) drop-shadow(0 4px 4px var(--v2-alpha-dark-8))
159-
drop-shadow(0 0 0.5px var(--v2-alpha-dark-12));
160161
opacity: 0;
161162
scale: 0.96;
162163
translate: 4px 0;
@@ -174,21 +175,18 @@ button.session-service-row:focus-visible,
174175
}
175176
}
176177

177-
[data-color-scheme="dark"] .session-service-flyout-body {
178-
filter: drop-shadow(0 8px 8px var(--v2-alpha-dark-30)) drop-shadow(0 4px 4px var(--v2-alpha-dark-30))
179-
drop-shadow(0 0 0.5px var(--v2-alpha-light-16));
180-
}
181-
182178
.session-service-flyout-surface {
183179
display: grid;
184-
padding: 2px;
180+
height: var(--flyout-height);
181+
overflow: hidden;
182+
padding: 2px 0;
185183
border-radius: 6px;
186184
background: var(--v2-background-bg-layer-01);
185+
box-shadow: var(--v2-elevation-floating);
187186
color: var(--v2-text-text-base);
188187
font-size: 13px;
189188
font-weight: 440;
190189
line-height: var(--line-height-compact);
191-
clip-path: inset(0 0 calc(100% - var(--flyout-height)) 0 round 6px);
192190
}
193191

194192
/* Motion is off unless the "Extensions submenu motion" knob sets data-motion. */
@@ -218,20 +216,20 @@ button.session-service-row:focus-visible,
218216
}
219217

220218
.session-service-flyout-surface {
221-
transition: clip-path 0.3s var(--spring-snap);
219+
transition: height 0.3s var(--spring-snap);
222220
}
223221
}
224222

225223
.session-service-pane {
226224
grid-area: 1 / 1;
227225
align-self: start;
226+
/* The list scrolls above its actions, which stay in view. */
227+
display: flex;
228+
flex-direction: column;
228229
max-height: min(480px, calc(100dvh - 36px));
229-
/* Only vertical: long names truncate rather than scroll sideways. */
230-
overflow-x: hidden;
231-
overflow-y: auto;
232-
overscroll-behavior: contain;
230+
/* Long names truncate inside the list instead of widening it. */
231+
min-width: 0;
233232
outline: none;
234-
--session-service-scrollbar: 0px;
235233

236234
/* Lists swap at once; only the box glides and resizes, so two lists never show together. */
237235
&:not([data-active]) {
@@ -240,8 +238,25 @@ button.session-service-row:focus-visible,
240238
}
241239
}
242240

243-
/* A long list keeps the menu's 2px inset at both ends, and fades only at an edge with rows scrolled past it. The
244-
scrollbar's strip stays unmasked, so the thumb never fades. */
241+
.session-service-scroll {
242+
min-height: 0;
243+
/* Only vertical: long names truncate rather than scroll sideways. */
244+
overflow-x: hidden;
245+
overflow-y: auto;
246+
overscroll-behavior: contain;
247+
}
248+
249+
/* The list and its actions own the menu's inline inset, so a separator can run edge to edge. */
250+
.session-service-pane-content,
251+
.session-service-pane > .session-service-footer {
252+
padding-inline: 2px;
253+
}
254+
255+
.session-service-pane > .session-service-footer {
256+
flex-shrink: 0;
257+
}
258+
259+
/* A long list keeps the menu's 2px inset at both ends, and fades only at an edge with rows scrolled past it. */
245260
@property --session-service-fade-start {
246261
syntax: "<length>";
247262
inherits: false;
@@ -273,57 +288,21 @@ button.session-service-row:focus-visible,
273288
}
274289

275290
@supports (animation-timeline: scroll()) {
276-
.session-service-pane[data-overflow] {
277-
mask-image:
278-
linear-gradient(#000, #000),
279-
linear-gradient(
280-
to bottom,
281-
transparent,
282-
#000 var(--session-service-fade-start),
283-
#000 calc(100% - var(--session-service-fade-end)),
284-
transparent
285-
);
286-
mask-size:
287-
var(--session-service-scrollbar) 100%,
288-
100% 100%;
289-
mask-position:
290-
right top,
291-
left top;
292-
mask-repeat: no-repeat;
291+
.session-service-pane[data-overflow] .session-service-scroll {
292+
mask-image: linear-gradient(
293+
to bottom,
294+
transparent,
295+
#000 var(--session-service-fade-start),
296+
#000 calc(100% - var(--session-service-fade-end)),
297+
transparent
298+
);
293299
animation:
294300
session-service-fade-start linear both,
295301
session-service-fade-end linear both;
296302
animation-timeline: scroll(self), scroll(self);
297303
animation-range:
298304
0 16px,
299305
calc(100% - 16px) 100%;
300-
301-
&:dir(rtl) {
302-
mask-position:
303-
left top,
304-
left top;
305-
}
306-
}
307-
}
308-
309-
/* The thumb sits 6px inside the menu's edge and clear of its corners, rather than against the border. */
310-
@media (hover: hover) and (pointer: fine) {
311-
@supports selector(::-webkit-scrollbar) {
312-
.session-service-pane {
313-
--session-service-scrollbar: 12px;
314-
}
315-
316-
.session-service-pane::-webkit-scrollbar {
317-
width: 12px;
318-
}
319-
320-
.session-service-pane::-webkit-scrollbar-track:vertical {
321-
margin: 6px 0;
322-
}
323-
324-
.session-service-pane::-webkit-scrollbar-thumb {
325-
border-width: 2px 4px;
326-
}
327306
}
328307
}
329308

@@ -381,7 +360,8 @@ button.session-service-row:focus-visible,
381360

382361
strong {
383362
padding: 8px 12px;
384-
font-weight: 530;
363+
font-weight: 440;
364+
user-select: none;
385365
}
386366
}
387367

@@ -390,7 +370,7 @@ button.session-service-row:focus-visible,
390370
color: var(--v2-text-text-faint);
391371
overflow-wrap: anywhere;
392372
}
393-
:is(.session-service-pane-content, .session-service-drawer) > .session-service-footer {
373+
:is(.session-service-pane, .session-service-drawer) > .session-service-footer {
394374
padding-block-start: 6px;
395375
}
396376
.session-service-config-separator {
@@ -401,11 +381,6 @@ button.session-service-row:focus-visible,
401381
background: var(--v2-border-border-muted);
402382
}
403383

404-
/* Inside a submenu's scroll box, a full-bleed separator would overflow it sideways. */
405-
.session-service-pane .session-service-config-separator {
406-
width: auto;
407-
margin-inline: 0;
408-
}
409384
.session-service-config {
410385
display: inline-flex;
411386
align-items: center;
@@ -492,6 +467,11 @@ button.session-service-dot {
492467
height: 6px;
493468
}
494469

470+
/* The 16px switch sits 6px from the row's top, bottom and end. */
471+
.session-mcp-row {
472+
padding-inline-end: 6px;
473+
}
474+
495475
.session-mcp-trailing {
496476
display: flex;
497477
flex-shrink: 0;
@@ -544,7 +524,7 @@ button.session-service-dot {
544524
.session-summary-dismiss {
545525
position: absolute;
546526
inset-inline-end: 8px;
547-
bottom: 8px;
527+
bottom: 6px;
548528
width: 20px;
549529
height: 20px;
550530
display: flex;
@@ -569,7 +549,6 @@ button.session-service-dot {
569549
}
570550

571551
:root[data-motion="reduce"] {
572-
.session-summary-popover,
573552
.session-service-dot::before {
574553
animation: none;
575554
}

‎packages/gui-plugins/src/details/popover.tsx‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,7 @@ function DetailsPopover(
100100
/>
101101
</Tooltip>
102102
<Popover.Portal>
103-
{/* Kobalte positions the content relatively, so it places the Extensions flyout. Only the inner box scrolls,
104-
so the flyout beside it is not clipped. */}
103+
{/* Kobalte positions the content relatively, so it places the Extensions flyout. */}
105104
<Popover.Content
106105
class="session-summary-popover z-50 border-0 bg-transparent outline-none"
107106
aria-label={ctx.t("title")}
@@ -117,7 +116,7 @@ function DetailsPopover(
117116
if (content instanceof HTMLElement) setTimeout(() => content.focus({ preventScroll: true }))
118117
}}
119118
>
120-
<div class="max-h-[calc(100dvh-96px)] overflow-y-auto p-1">{props.children}</div>
119+
{props.children}
121120
</Popover.Content>
122121
</Popover.Portal>
123122
</Popover>

0 commit comments

Comments
 (0)