Skip to content

Commit dbb591d

Browse files
committed
Polish agentic sidebar status indicators
1 parent 26f7e54 commit dbb591d

7 files changed

Lines changed: 123 additions & 91 deletions

File tree

apps/ui/src/components/sidebar-header/index.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ export function SidebarHeader( { onToggleSidebar }: Props ) {
1616
const navigate = useNavigate();
1717
return (
1818
<div className={ `${ styles.root } ${ isFullscreen ? styles.fullscreen : '' }` }>
19-
<span className={ styles.title }>{ __( 'Studio' ) }</span>
2019
<div className={ styles.actions }>
2120
<Menu.Root modal={ false }>
2221
<Menu.Trigger

apps/ui/src/components/sidebar-header/style.module.css

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
.root {
2+
--sidebar-header-inline-end: calc(
3+
var(--wpds-dimension-padding-2xl) - var(--wpds-dimension-padding-xs)
4+
);
5+
26
display: flex;
37
align-items: center;
48
gap: var(--wpds-dimension-padding-sm);
59
/* Leave room for macOS traffic lights at {20, 20} plus a visible gap.
610
min-height is tuned so the flex-centered content lines up with the
711
vertical center of the traffic lights. */
8-
padding: var(--wpds-dimension-padding-sm) var(--wpds-dimension-padding-2xl)
12+
padding: var(--wpds-dimension-padding-sm) var(--sidebar-header-inline-end)
913
var(--wpds-dimension-padding-sm) 94px;
1014
min-height: 46px;
1115
-webkit-app-region: drag;
@@ -18,37 +22,22 @@
1822
}
1923

2024
/* In macOS fullscreen the traffic lights are hidden, so reclaim the
21-
padding that was reserved for them and align the title with the site
22-
list entries below. The site list outer padding is `2xl` and its rows
23-
now start flush with that edge (no row/button inline-start padding), so
24-
the site label text starts at `2xl` from the sidebar edge. */
25+
padding that was reserved for them. */
2526
.fullscreen {
26-
padding-left: var(--wpds-dimension-padding-2xl);
27-
}
28-
29-
.title {
30-
flex: 1;
31-
min-width: 0;
32-
font-size: var(--wpds-typography-font-size-md);
33-
font-weight: 600;
34-
color: var(--wpds-color-fg-content-neutral);
35-
overflow: hidden;
36-
text-overflow: ellipsis;
37-
white-space: nowrap;
27+
padding-left: var(--wpds-dimension-padding-lg);
3828
}
3929

4030
.actions {
4131
display: flex;
4232
align-items: center;
33+
margin-inline-start: auto;
4334
/* Match the site-row action buttons (`.siteActions` in site-list), which
4435
sit flush with no gap. */
4536
gap: 0;
46-
opacity: 0.65;
47-
}
48-
49-
.actions:hover,
50-
.actions:focus-within {
51-
opacity: 1;
37+
/* The site rows have their action rail tucked into the row padding. Pull
38+
the header buttons over by the row's inner padding so the create/toggle
39+
controls share those same trailing columns. */
40+
margin-inline-end: calc(-1 * var(--wpds-dimension-padding-sm));
5241
}
5342

5443
.popup {

apps/ui/src/components/site-icon/style.module.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
display: inline-grid;
33
box-sizing: border-box;
44
place-items: center;
5-
width: 16px;
6-
height: 16px;
5+
width: var(--site-icon-size, 16px);
6+
height: var(--site-icon-size, 16px);
77
border-radius: 4px;
88
background: radial-gradient(
99
circle at var(--site-icon-position-a),

apps/ui/src/components/site-list/index.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ function SessionItem( { session, isVisible }: { session: AiSessionSummary; isVis
167167
return (
168168
<li className={ styles.sessionItem }>
169169
<SidebarButton
170-
className={ clsx( styles.sessionLink, isRunning && styles.sessionLinkRunning ) }
170+
className={ styles.sessionLink }
171171
render={
172172
<Link
173173
to="/sessions/$sessionId"
@@ -188,9 +188,7 @@ function SessionItem( { session, isVisible }: { session: AiSessionSummary; isVis
188188
className={ styles.sessionQuestionIndicator }
189189
role="status"
190190
aria-label={ __( 'Studio needs an answer.' ) }
191-
>
192-
?
193-
</span>
191+
/>
194192
}
195193
/>
196194
<Tooltip.Popup positioner={ <Tooltip.Positioner side="top" /> }>
@@ -578,6 +576,7 @@ function SiteSection( {
578576
} ) {
579577
const isStarting = useIsSiteStarting( group.site?.id );
580578
const isStopping = useIsSiteStopping( group.site?.id );
579+
const isStopped = !! group.site && ! group.site.running && ! isStarting;
581580

582581
return (
583582
<section
@@ -597,6 +596,7 @@ function SiteSection( {
597596
{ group.site ? (
598597
<span className={ styles.siteIconSlot } aria-hidden="true">
599598
<SiteIcon
599+
className={ clsx( styles.siteIcon, isStopped && styles.siteIconStopped ) }
600600
seed={ `${ group.site.id }:${ group.site.name }:${ group.site.path }` }
601601
imageSrc={ group.site.siteIcon }
602602
/>

apps/ui/src/components/site-list/style.module.css

Lines changed: 84 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,27 @@
11
.root {
2-
--site-list-row-height: 32px;
2+
--site-list-row-height: 34px;
33
--site-list-row-background-hover: rgb(0 0 0 / 4%);
44
--site-list-row-background-active: rgb(0 0 0 / 7%);
5-
6-
/* Left padding matches the sidebar footer (user-menu) so
7-
the row highlight boxes share the same left edge as the rest of the
8-
sidebar chrome. The right side is smaller: rows carry `sm` of their
9-
own right padding, and `xl + sm` keeps the row icons (site status,
10-
actions) visually aligned with the header's drawer icon. */
11-
padding: var(--wpds-dimension-padding-xl) var(--wpds-dimension-padding-xl)
12-
var(--wpds-dimension-padding-xl) var(--wpds-dimension-padding-2xl);
5+
--site-list-row-leading-inset: var(--wpds-dimension-padding-xs);
6+
--site-list-row-outer-inset: calc(
7+
var(--wpds-dimension-padding-lg) - var(--site-list-row-leading-inset)
8+
);
9+
--site-list-icon-size: 18px;
10+
--site-list-icon-gap: calc(var(--wpds-dimension-gap-sm) + 2px);
11+
--site-list-chat-status-size: 14px;
12+
--site-list-chat-status-offset-adjust: 2px;
13+
--site-list-chat-status-offset: var(--site-list-row-leading-inset);
14+
--site-list-pending-indicator-bg: color-mix(
15+
in srgb,
16+
var(--wpds-color-bg-interactive-error-strong) 34%,
17+
var(--wpds-color-bg-surface-caution)
18+
);
19+
--site-list-pending-indicator-dot: var(--wpds-color-fg-content-error);
20+
21+
/* Pull the row shape slightly left, then add the inset back inside rows so
22+
icons and labels keep their column while selected rows have breathing room. */
23+
padding: 0 var(--site-list-row-outer-inset) var(--wpds-dimension-padding-xl)
24+
var(--site-list-row-outer-inset);
1325
display: flex;
1426
flex-direction: column;
1527
gap: var(--wpds-dimension-padding-sm);
@@ -20,13 +32,15 @@
2032
.root {
2133
--site-list-row-background-hover: rgb(255 255 255 / 8%);
2234
--site-list-row-background-active: rgb(255 255 255 / 12%);
35+
--site-list-pending-indicator-bg: #facc15;
36+
--site-list-pending-indicator-dot: #422006;
2337
}
2438
}
2539

2640
.sites {
2741
display: flex;
2842
flex-direction: column;
29-
gap: 2px;
43+
gap: 0;
3044
}
3145

3246
.site {
@@ -41,12 +55,10 @@
4155
min-width: 0;
4256
box-sizing: border-box;
4357
height: var(--site-list-row-height);
44-
/* No inline-start padding: the row's leading content (site icon / title)
45-
sits flush with the sidebar content edge (`.root`'s 2xl inline padding),
46-
lining the title up with the macOS window traffic lights. The button
47-
inside (`.siteToggle`) also drops its inline-start padding to match. */
58+
/* The row shape extends left of the content column; this padding keeps the
59+
icon/title column aligned while giving selected rows breathing room. */
4860
padding: var(--wpds-dimension-padding-xs) var(--wpds-dimension-padding-sm)
49-
var(--wpds-dimension-padding-xs) 0;
61+
var(--wpds-dimension-padding-xs) var(--site-list-row-leading-inset);
5062
border-radius: 6px;
5163
}
5264

@@ -72,30 +84,19 @@
7284
display: inline-grid;
7385
place-items: center;
7486
flex-shrink: 0;
75-
inline-size: 0;
76-
margin-inline-end: 0;
77-
opacity: 0;
78-
transform: translateX(-4px) scale(0.75);
79-
transform-origin: left center;
87+
inline-size: var(--site-list-icon-size);
88+
/* The footer gravatar is 20px wide while site icons are 18px, so the extra
89+
2px lives after the icon to keep titles aligned with the display name. */
90+
margin-inline-end: var(--site-list-icon-gap);
8091
overflow: hidden;
81-
transition: inline-size 120ms ease, margin-inline-end 120ms ease, opacity 120ms ease,
82-
transform 120ms ease;
8392
}
8493

85-
.siteActive .siteIconSlot {
86-
inline-size: 16px;
87-
/* Gap between the site icon and its title. Kept in sync with the chat-row
88-
indent below (`.sessionLink` padding-inline-start), which carries the same
89-
+4px so chat labels stay aligned under the site title. */
90-
margin-inline-end: calc(var(--wpds-dimension-padding-xs) + 4px);
91-
opacity: 1;
92-
transform: translateX(0) scale(1);
94+
.siteIcon {
95+
--site-icon-size: var(--site-list-icon-size);
9396
}
9497

95-
@media (prefers-reduced-motion: reduce) {
96-
.siteIconSlot {
97-
transition: none;
98-
}
98+
.siteIconStopped {
99+
opacity: 0.5;
99100
}
100101

101102
.siteName {
@@ -371,7 +372,7 @@
371372
margin: 0;
372373
display: flex;
373374
flex-direction: column;
374-
gap: 2px;
375+
gap: 0;
375376
}
376377

377378
.sessionItem {
@@ -388,15 +389,18 @@
388389
--wp-ui-button-border-color-active: transparent;
389390
--wp-ui-button-height: var(--site-list-row-height);
390391

392+
position: relative;
391393
width: 100%;
392394
box-sizing: border-box;
393395
height: var(--site-list-row-height);
394-
/* Chat labels align under the active site's title text. The site row now
395-
starts flush with the sidebar edge, and its title begins one icon width
396-
(16px) plus the icon→title gap past that edge — so this indent mirrors
397-
`.siteActive .siteIconSlot`'s inline-size + margin-inline-end. */
396+
/* Chat labels align under the parent site's title text and the footer
397+
display name while the row shape extends left for the status indicator. */
398398
padding: var(--wpds-dimension-padding-xs) var(--wpds-dimension-padding-sm)
399-
var(--wpds-dimension-padding-xs) calc(16px + var(--wpds-dimension-padding-xs) + 4px);
399+
var(--wpds-dimension-padding-xs)
400+
calc(
401+
var(--site-list-row-leading-inset) + var(--site-list-icon-size) +
402+
var(--site-list-icon-gap)
403+
);
400404
border-radius: 6px;
401405
background-color: var(--session-link-background);
402406
color: var(--wpds-color-fg-content-neutral-weak);
@@ -408,13 +412,10 @@
408412
transition: color 80ms ease, background-color 80ms ease;
409413
}
410414

411-
.sessionLinkRunning {
412-
position: relative;
413-
}
414-
415415
.sessionLabel {
416416
flex: 1;
417417
min-width: 0;
418+
font-weight: var(--wpds-typography-font-weight-regular);
418419
overflow: hidden;
419420
text-overflow: ellipsis;
420421
white-space: nowrap;
@@ -434,31 +435,57 @@
434435
transition: opacity 100ms ease;
435436
}
436437

437-
/* The running spinner and question indicator sit in the indent gutter to
438-
the left of the chat label. */
438+
/* The running spinner and question indicator sit inside the active row shape,
439+
inset from its edge while the chat label keeps its shared title column. */
439440
.sessionInlineSpinner {
441+
--spinner-size: var(--site-list-chat-status-size);
442+
443+
box-sizing: border-box;
440444
position: absolute;
441445
inset-block-start: 50%;
442-
inset-inline-start: calc(var(--wpds-dimension-padding-sm) + 6px);
443-
margin-block-start: -6px;
446+
inset-inline-start: calc(
447+
var(--site-list-chat-status-offset) + var(--site-list-chat-status-offset-adjust)
448+
);
449+
inline-size: var(--site-list-chat-status-size);
450+
block-size: var(--site-list-chat-status-size);
451+
margin-block-start: calc(var(--site-list-chat-status-size) / -2);
444452
}
445453

446454
.sessionQuestionIndicator {
447455
position: absolute;
448456
inset-block-start: 50%;
449-
inset-inline-start: calc(var(--wpds-dimension-padding-sm) + 4px);
457+
inset-inline-start: calc(
458+
var(--site-list-chat-status-offset) + var(--site-list-chat-status-offset-adjust)
459+
);
450460
display: inline-flex;
451461
align-items: center;
452462
justify-content: center;
453-
inline-size: 16px;
454-
block-size: 16px;
455-
border-radius: 50%;
456-
background-color: #facc15;
457-
color: #422006;
458-
font-size: 11px;
459-
font-weight: 700;
460-
line-height: 1;
463+
inline-size: var(--site-list-chat-status-size);
464+
block-size: var(--site-list-chat-status-size);
461465
transform: translateY(-50%);
466+
isolation: isolate;
467+
}
468+
469+
.sessionQuestionIndicator::before {
470+
content: '';
471+
position: absolute;
472+
inset: 0;
473+
z-index: -1;
474+
clip-path: path(
475+
'M 7 1.7 C 7.55 1.7 8.05 2 8.3 2.5 L 12.8 11.8 C 13.2 12.65 12.6 13.2 11.75 13.2 L 2.25 13.2 C 1.4 13.2 0.8 12.65 1.2 11.8 L 5.7 2.5 C 5.95 2 6.45 1.7 7 1.7 Z'
476+
);
477+
background-color: var(--site-list-pending-indicator-bg);
478+
}
479+
480+
.sessionQuestionIndicator::after {
481+
content: '';
482+
position: absolute;
483+
inset-block-start: calc(58% - 1.5px);
484+
inset-inline-start: calc(50% - 1.5px);
485+
inline-size: 3px;
486+
block-size: 3px;
487+
border-radius: 50%;
488+
background-color: var(--site-list-pending-indicator-dot);
462489
}
463490

464491
.sessionLink:hover,

apps/ui/src/components/spinner/style.module.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
.spinner {
22
display: inline-block;
3-
width: 12px;
4-
height: 12px;
3+
width: var(--spinner-size, 12px);
4+
height: var(--spinner-size, 12px);
55
border: 2px solid var(--wpds-color-stroke-surface-neutral);
66
border-top-color: var(--wpds-color-fg-interactive-brand, #2563eb);
77
border-radius: 50%;

apps/ui/src/components/user-menu/style.module.css

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
/* Pinning to the sidebar bottom is handled by the layout's
22
`sidebarFooter` wrapper. */
33
.root {
4-
padding: var(--wpds-dimension-padding-sm) var(--wpds-dimension-padding-2xl)
5-
var(--wpds-dimension-padding-xl) var(--wpds-dimension-padding-2xl);
4+
--user-menu-leading-offset: calc(
5+
var(--wpds-dimension-padding-lg) - var(--wpds-dimension-padding-sm)
6+
);
7+
--user-menu-trailing-offset: calc(
8+
var(--wpds-dimension-padding-2xl) - var(--wpds-dimension-padding-xs)
9+
);
10+
11+
/* SidebarButton adds `sm` inline padding; this offset puts the gravatar's
12+
left edge on the same column as the site icons. */
13+
padding: var(--wpds-dimension-padding-sm) var(--user-menu-trailing-offset)
14+
var(--wpds-dimension-padding-xl) var(--user-menu-leading-offset);
615
}
716

817
.row {
@@ -17,6 +26,10 @@
1726
color: var(--wpds-color-fg-content-neutral-weak);
1827
}
1928

29+
.themeToggle {
30+
margin-inline-end: calc(-1 * var(--wpds-dimension-padding-sm));
31+
}
32+
2033
.row:hover .userName,
2134
.row:hover .themeToggle,
2235
.row:hover .settingsButton,
@@ -27,8 +40,12 @@
2740
}
2841

2942
.userTrigger {
43+
--user-menu-avatar-offset: 2px;
44+
3045
flex: 1;
3146
font-weight: var(--wpds-typography-font-weight-regular);
47+
gap: calc(var(--wpds-dimension-gap-sm) + var(--user-menu-avatar-offset));
48+
padding-inline-start: calc(var(--wpds-dimension-padding-sm) - var(--user-menu-avatar-offset));
3249
}
3350

3451
.userName {

0 commit comments

Comments
 (0)