${params.title}
-
- ?
-
-
-
- ${params.hint}
-
+
+
+ ?
+
+
+ ${params.hint}
+
${params.value}
${params.sub}
diff --git a/ui/src/styles/chat/tool-cards.css b/ui/src/styles/chat/tool-cards.css
index 5c9b5e098a03..2077e5200ed7 100644
--- a/ui/src/styles/chat/tool-cards.css
+++ b/ui/src/styles/chat/tool-cards.css
@@ -1689,20 +1689,8 @@
/* Hover preview on the status row: the latest tasks in a read-only card. The
tooltip body is content-sized (width: max-content up to --max-width), so a
single task stays one compact line instead of a fixed-size panel. */
-wa-tooltip.chat-tasks-status__preview {
- --max-width: min(360px, calc(100vw - 24px));
- font-family: var(--font-body);
-}
-
-wa-tooltip.chat-tasks-status__preview::part(body) {
- padding: 8px 10px;
- border: 1px solid color-mix(in srgb, var(--border-strong) 84%, transparent);
- border-radius: var(--radius-md);
- background: color-mix(in srgb, var(--card) 94%, black 6%);
- box-shadow: var(--shadow-md);
- color: var(--text);
- font-size: var(--control-ui-text-xs);
- line-height: 1.35;
+openclaw-tooltip.chat-tasks-status__preview {
+ --openclaw-tooltip-max-width: min(360px, calc(100vw - 24px));
}
.chat-tasks-preview {
diff --git a/ui/src/styles/layout.css b/ui/src/styles/layout.css
index 0e2e4bbe2066..f02c00d3c4fe 100644
--- a/ui/src/styles/layout.css
+++ b/ui/src/styles/layout.css
@@ -1933,6 +1933,11 @@ html.openclaw-native-macos
margin-left: -5px;
}
+.viewer-facepile > openclaw-viewer-avatar:not(:first-child) .viewer-avatar,
+.viewer-facepile > .viewer-avatar:not(:first-child) {
+ margin-left: -5px;
+}
+
.viewer-avatar > img,
.viewer-avatar > span {
width: 100%;
@@ -1996,7 +2001,7 @@ html.openclaw-native-macos
border: none;
border-radius: var(--radius-full);
background: none;
- cursor: pointer;
+ cursor: default;
}
.viewer-facepile-trigger:hover,
@@ -2004,114 +2009,6 @@ html.openclaw-native-macos
background: color-mix(in srgb, var(--fg) 8%, transparent);
}
-/* Long rosters scroll inside the popup instead of growing past the viewport.
- Chrome matches the sidebar's other fixed menus (customize/session menus). */
-.presence-roster-menu::part(menu) {
- min-width: 232px;
- max-width: 288px;
- max-height: min(340px, 60vh);
- overflow-y: auto;
- padding: 6px;
- border: 1px solid color-mix(in srgb, var(--border-strong) 78%, transparent);
- border-radius: var(--radius-lg);
- background: var(--bg-elevated);
- box-shadow: var(--shadow-lg);
-}
-
-.presence-roster-menu__title {
- padding: 6px 8px 4px;
- color: var(--muted);
- font-size: 11px;
- font-weight: 650;
- letter-spacing: 0.05em;
- text-transform: uppercase;
-}
-
-/* Rows are informational people entries, not checkbox menu items: drop Web
- Awesome's checkmark gutter and item metrics so title, avatars, and text
- share one left edge with the rest of the app's menus. */
-.presence-roster-menu__item {
- padding: 6px 8px;
- border-radius: var(--radius-md);
-}
-
-.presence-roster-menu__item:hover,
-.presence-roster-menu__item:focus-visible {
- outline: none;
- background: color-mix(in srgb, var(--bg-hover) 84%, transparent);
-}
-
-.presence-roster-menu__item::part(checkmark) {
- display: none;
-}
-
-.presence-roster-menu__item::part(label) {
- display: flex;
- align-items: center;
-}
-
-/* Roster avatars grow to two-line row scale. The circle clips via the child
- image/initials instead of the wrapper so the online badge can overflow.
- The avatar-to-text gap lives here: Web Awesome's icon-slot margin targets
- the slotted openclaw-viewer-avatar host, which is display:contents and
- therefore generates no box for the margin to apply to. */
-.presence-roster-menu__item .viewer-avatar {
- width: 26px;
- height: 26px;
- margin-inline-end: 10px;
- overflow: visible;
- border: none;
- font-size: 10px;
-}
-
-.presence-roster-menu__item .viewer-avatar > img,
-.presence-roster-menu__item .viewer-avatar > span {
- border-radius: var(--radius-full);
-}
-
-/* Everyone listed is online; the dot restates the header at a glance. */
-.presence-roster-menu__item .viewer-avatar::after {
- content: "";
- position: absolute;
- right: -1px;
- bottom: -1px;
- width: 8px;
- height: 8px;
- border: 2px solid var(--bg-elevated);
- border-radius: var(--radius-full);
- background: var(--ok);
-}
-
-.presence-roster-menu__text {
- display: flex;
- flex-direction: column;
- gap: 1px;
- min-width: 0;
- line-height: 1.3;
-}
-
-.presence-roster-menu__name {
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- color: var(--text);
- font-size: 13px;
- font-weight: 500;
-}
-
-.presence-roster-menu__you {
- color: var(--muted);
- font-weight: 400;
-}
-
-.presence-roster-menu__email {
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- color: var(--muted);
- font-size: 11px;
-}
-
.sidebar-recent-session__name {
flex: 1 1 auto;
min-width: 0;
@@ -3158,6 +3055,131 @@ wa-dropdown.sidebar-session-sort-menu::part(menu) {
margin-left: 1px;
}
+openclaw-tooltip.sidebar-hover-tooltip {
+ --openclaw-tooltip-max-width: min(340px, calc(100vw - 24px));
+}
+
+.sidebar-hover-card {
+ width: min(320px, calc(100vw - 44px));
+ max-width: 100%;
+ color: var(--text);
+ font-size: 12px;
+ line-height: 1.35;
+ text-align: left;
+}
+
+.sidebar-hover-card__region {
+ display: flex;
+ flex-direction: column;
+ gap: 7px;
+}
+
+.sidebar-hover-card__heading {
+ color: var(--muted);
+ font-size: 11px;
+ font-weight: 650;
+ letter-spacing: 0.04em;
+ text-transform: uppercase;
+}
+
+.sidebar-hover-card__people {
+ display: flex;
+ flex-direction: column;
+ gap: 7px;
+ max-height: min(240px, 45vh);
+ overflow-y: auto;
+}
+
+.sidebar-hover-card__person {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ min-width: 0;
+}
+
+.sidebar-hover-card__person .viewer-avatar {
+ width: 24px;
+ height: 24px;
+ flex: none;
+ border-color: var(--card);
+ font-size: 9px;
+}
+
+.sidebar-hover-card__person-text {
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ min-width: 0;
+}
+
+.sidebar-hover-card__person-name,
+.sidebar-hover-card__person-email {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.sidebar-hover-card__person-name {
+ color: var(--text);
+ font-weight: 550;
+}
+
+.sidebar-hover-card__you,
+.sidebar-hover-card__person-email {
+ color: var(--muted);
+ font-weight: 400;
+}
+
+.sidebar-hover-card__person-email {
+ font-size: 11px;
+}
+
+.sidebar-hover-card__divider {
+ height: 1px;
+ margin: 10px 0;
+ background: color-mix(in srgb, var(--border-strong) 68%, transparent);
+}
+
+.sidebar-hover-card__server-details {
+ display: flex;
+ flex-direction: column;
+ gap: 7px;
+}
+
+.sidebar-hover-card__summary {
+ color: var(--text);
+ font-weight: 600;
+}
+
+.sidebar-hover-card__metadata {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ margin: 0;
+}
+
+.sidebar-hover-card__metadata-row {
+ display: grid;
+ grid-template-columns: minmax(72px, auto) minmax(0, 1fr);
+ gap: 10px;
+}
+
+.sidebar-hover-card__metadata-row dt {
+ color: var(--muted);
+}
+
+.sidebar-hover-card__metadata-row dd {
+ min-width: 0;
+ margin: 0;
+ overflow-wrap: anywhere;
+ color: var(--text);
+}
+
+.sidebar-hover-card__metadata-value--mono {
+ font-family: var(--font-mono);
+ font-size: 11px;
+}
+
.sidebar-footer-bar__identity {
display: inline-flex;
align-items: center;
diff --git a/ui/src/styles/usage.css b/ui/src/styles/usage.css
index 6c0ca69bf5d8..08200bd66a2d 100644
--- a/ui/src/styles/usage.css
+++ b/ui/src/styles/usage.css
@@ -916,27 +916,6 @@ details.usage-filter-select summary::-webkit-details-marker,
outline-offset: 2px;
}
-wa-tooltip.usage-summary-tooltip {
- --max-width: min(280px, calc(100vw - 16px));
- font-family: var(--font-body);
- letter-spacing: normal;
- text-align: center;
- text-transform: none;
-}
-
-wa-tooltip.usage-summary-tooltip::part(body) {
- padding: 7px 9px;
- border: 1px solid color-mix(in srgb, var(--border-strong) 84%, transparent);
- border-radius: var(--radius-md);
- background: color-mix(in srgb, var(--card) 94%, black 6%);
- box-shadow: var(--shadow-md);
- color: var(--text);
- font-size: 12px;
- font-weight: 500;
- line-height: 1.35;
- overflow-wrap: anywhere;
-}
-
.usage-insights-grid {
grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
}
diff --git a/ui/src/test-helpers/app-sidebar-cases/basics.ts b/ui/src/test-helpers/app-sidebar-cases/basics.ts
index d0b93c46da26..faeb061ba36d 100644
--- a/ui/src/test-helpers/app-sidebar-cases/basics.ts
+++ b/ui/src/test-helpers/app-sidebar-cases/basics.ts
@@ -174,8 +174,19 @@ describe("AppSidebar viewer presence", () => {
expect(
footerFacepile?.querySelector(".viewer-facepile")?.getAttribute("data-viewer-count"),
).toBe("6");
- expect(footerFacepile?.querySelector('[data-viewer-id="00-self"]')).toBeNull();
+ expect(footerFacepile?.querySelector('.viewer-facepile [data-viewer-id="00-self"]')).toBeNull();
expect(footerFacepile?.querySelector(".viewer-avatar--overflow")?.textContent).toContain("+1");
+ expect(footerFacepile?.querySelector(".viewer-facepile openclaw-tooltip")).toBeNull();
+ expect(
+ [
+ ...(footerFacepile?.querySelectorAll(
+ ".sidebar-presence-hover-card .sidebar-hover-card__person",
+ ) ?? []),
+ ].map((row) => row.getAttribute("data-viewer-id")),
+ ).toEqual(["00-self", "alice", "bob", "carol", "dave", "erin", "frank"]);
+ expect(footerFacepile?.querySelector(".sidebar-presence-hover-card")?.textContent).toContain(
+ "Server",
+ );
const identityChip = sidebar.querySelector
(".sidebar-footer-bar__identity");
expect(identityChip?.querySelector(".sidebar-footer-bar__identity-name")?.textContent).toBe(