From e8ab7ca78661de7f00a8196444be1db93e7c14b4 Mon Sep 17 00:00:00 2001 From: qer Date: Thu, 2 Jul 2026 20:40:47 +0800 Subject: [PATCH] fix(web): stop session row from shifting on hover (#1319) The session row swapped the relative time for the kebab on hover using display:none, which dropped the time from layout and appended the kebab at the end. Because the time is variable-width (2h / 5m / just now) and the kebab is a fixed 26px, the right region reflowed: status badges shifted and the title's truncation changed, causing visible jitter. Place the time and kebab in one inline-grid cell (grid-area:1/1) and swap them via visibility instead, so the slot width stays max(time width, 26px) across hover. The badges and title no longer reflow. Documented in the design system (section 07 Session row) as a trailing action slot rule. --- .changeset/fix-web-session-row-hover.md | 5 ++ apps/kimi-web/design/design-system.html | 23 ++++++-- apps/kimi-web/public/design-system.html | 23 ++++++-- apps/kimi-web/src/components/SessionRow.vue | 59 +++++++++++++-------- 4 files changed, 80 insertions(+), 30 deletions(-) create mode 100644 .changeset/fix-web-session-row-hover.md diff --git a/.changeset/fix-web-session-row-hover.md b/.changeset/fix-web-session-row-hover.md new file mode 100644 index 000000000..f983ec733 --- /dev/null +++ b/.changeset/fix-web-session-row-hover.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Fix the sidebar session row shifting its title and status badges when hovered. diff --git a/apps/kimi-web/design/design-system.html b/apps/kimi-web/design/design-system.html index 3694066d9..d91372e8b 100644 --- a/apps/kimi-web/design/design-system.html +++ b/apps/kimi-web/design/design-system.html @@ -2188,20 +2188,35 @@ export function auth(token: string) {

Session row

-

A session row is an inset rounded pill, structured as: status slot → title → time → attention Badge → kebab.

+

A session row is an inset rounded pill, structured as: status slot → title → attention Badge → trailing action slot.

- - - + +
PartRule
Containermargin: 1px 6px; padding: 7px 10px; radius-md; hover = surface-sunken; active = accent-soft + inset 0 0 0 1px accent-bd
Status slot (lead)fixed --sb-gutter width; running = Spinner sm, otherwise unread = 7px accent dot
Titleflex:1 with truncation; double-click enters inline rename (compact input, not Input)
Timemono xs, fg-faint; yields to the kebab on hover
Attention BadgeBadge sm: info (needs answer) / warning (needs approval) / danger (aborted)
kebabIconButton sm, shown on hover; dropdown uses Menu/MenuItem
Attention BadgeBadge sm: info (needs answer) / warning (needs approval) / danger (aborted); always shown, not toggled on hover
Trailing action slot (trail)fixed slot shared by the time ↔ kebab (see below)
Archive confirmationreplaces the title area, Button sm (danger confirm / secondary cancel)
+

Trailing action slot

+

The session row's rightmost is a fixed-width action slot: at rest it shows the relative time (mono xs, fg-faint); on hover it swaps in place to the kebab (IconButton sm).

+ +
SessionRow.vue
.act { display: inline-grid; flex: none; align-items: center; justify-items: center; }
+.act .ts, .act .kebab { grid-area: 1 / 1; }
+.act .kebab { visibility: hidden; }
+.se:hover .act .kebab, .act:has(.kebab.open) .kebab { visibility: visible; }
+.se:hover .act .ts,   .act:has(.kebab.open) .ts   { visibility: hidden; }
+
i
+ Why the time and kebab share a slot and use visibility rather than display:none: the time is variable-width (2h / 5m / just now …), the kebab a fixed 26px. If hover used display:none to drop the time and append the kebab, the right region would reflow — badges shift and the title's truncation changes, causing visible jitter. Sharing one slot and swapping via visibility keeps the slot width constant; this is a hard rule for sidebar list-row stability. +
+

Workspace group

The group head and session rows share --sb-*: folder icon (open/closed) → name → path subtitle, with the kebab and "+" revealed on hover.