style(usage): tighten filter menus and match the analysis toggle

Size usage filter menus to their content and use the home protocol selection style for the Token and request toggle.
This commit is contained in:
lzt404 committed 2026-10-06 15:36:29 +08:00
1 parent ed988f7998
commit 9e5ed4d2d3
4 files changed
+74 -24

No files matched your search

+8 -5
View File
@@ -53,8 +53,11 @@
position: fixed;
z-index: 45;
display: grid;
min-width: 180px;
padding: 6px;
box-sizing: border-box;
width: max-content;
min-width: 0;
max-width: min(320px, calc(100vw - 24px));
padding: 4px;
border: 1px solid var(--border-color);
border-radius: 10px;
background: var(--bg-card);
@@ -69,12 +72,12 @@
.select-menu-option {
display: grid;
grid-template-columns: minmax(0, 1fr) 16px;
grid-template-columns: minmax(0, max-content) 16px;
align-items: center;
gap: 8px;
width: 100%;
min-height: 36px;
padding: 6px 10px;
min-height: 32px;
padding: 4px 8px;
border: 0;
border-radius: 6px;
background: transparent;
+35 -5
View File
@@ -21,6 +21,31 @@ type MenuLayout = {
height: number;
};
const measureMenu = (menu: HTMLDivElement) => {
const menuStyle = menu.getAttribute('style');
const optionButtons = [...menu.querySelectorAll<HTMLButtonElement>('.select-menu-option')];
const optionStyles = optionButtons.map((option) => option.getAttribute('style'));
menu.style.width = 'max-content';
menu.style.maxWidth = 'none';
menu.style.height = 'auto';
menu.style.maxHeight = 'none';
menu.style.visibility = 'hidden';
optionButtons.forEach((option) => {
option.style.width = 'max-content';
option.style.maxWidth = 'none';
option.style.gridTemplateColumns = 'max-content 16px';
});
const rect = menu.getBoundingClientRect();
if (menuStyle === null) menu.removeAttribute('style');
else menu.setAttribute('style', menuStyle);
optionButtons.forEach((option, index) => {
const style = optionStyles[index];
if (style === null) option.removeAttribute('style');
else option.setAttribute('style', style);
});
return { width: Math.ceil(rect.width), height: Math.ceil(rect.height) };
};
export function SelectMenu({
value,
options,
@@ -44,18 +69,23 @@ export function SelectMenu({
const updateLayout = useCallback(() => {
const root = rootRef.current;
const menu = menuRef.current;
if (!root) return;
const rect = root.getBoundingClientRect();
const edgeGap = 12;
const triggerGap = 6;
const preferredHeight = Math.min(282, Math.max(options.length, 1) * 42 + 18);
const minimumHeight = 88;
const measured = menu ? measureMenu(menu) : null;
const contentWidth = measured && measured.width > 0 ? measured.width : rect.width;
const contentHeight = measured && measured.height > 0
? measured.height
: Math.max(options.length, 1) * 36 + 12;
const spaceBelow = Math.max(0, window.innerHeight - edgeGap - rect.bottom - triggerGap);
const spaceAbove = Math.max(0, rect.top - triggerGap - edgeGap);
const placeAbove = spaceBelow < minimumHeight && spaceAbove > spaceBelow;
const placeAbove = spaceBelow < contentHeight && spaceAbove > spaceBelow;
const available = placeAbove ? spaceAbove : spaceBelow;
const height = Math.min(preferredHeight, Math.max(Math.min(minimumHeight, available), available));
const width = Math.min(320, Math.max(rect.width, 180));
const maxWidth = Math.max(edgeGap, window.innerWidth - edgeGap * 2);
const width = Math.min(320, maxWidth, contentWidth);
const height = Math.min(contentHeight, Math.max(available, Math.min(contentHeight, 36)));
const maxLeft = Math.max(edgeGap, window.innerWidth - edgeGap - width);
const left = Math.min(Math.max(edgeGap, rect.left), maxLeft);
const desiredTop = placeAbove ? rect.top - triggerGap - height : rect.bottom + triggerGap;
+21 -11
View File
@@ -57,26 +57,36 @@
.usage-analysis-metric {
display: flex;
flex: none;
gap: 2px;
padding: 3px;
border-radius: 8px;
background: var(--bg-secondary);
gap: 8px;
padding: 0;
border: 0;
border-radius: 0;
background: transparent;
}
.usage-analysis-metric button {
padding: 5px 9px;
border: 0;
border-radius: 5px;
border: 1px solid transparent;
border-radius: 8px;
background: transparent;
color: var(--text-secondary);
color: var(--text-tertiary);
font-size: 12px;
font-weight: 500;
white-space: nowrap;
}
.usage-analysis-metric button[aria-pressed='true'] {
background: var(--bg-primary);
color: var(--text-primary);
box-shadow: 0 1px 3px rgb(0 0 0 / 9%);
.usage-analysis-metric button:hover {
color: var(--ui-accent);
background: color-mix(in srgb, var(--ui-accent) 5%, var(--bg-card));
}
.usage-analysis-metric button[aria-pressed='true'],
.usage-analysis-metric button[aria-pressed='true']:hover {
border-color: transparent;
background: color-mix(in srgb, var(--ui-accent) 10%, var(--bg-card));
color: var(--ui-accent);
font-weight: 600;
box-shadow: none;
}
.usage-model-comparison {
+10 -3
View File
@@ -43,7 +43,7 @@
:root:not([data-theme='dark']) .workspace :is(
.agent-model-section, .agent-auth-segmented, .routing-segmented,
.plugin-tabs, .usage-analysis-metric,
.plugin-tabs,
.credential-quota, .usage-collector-state
) {
background: #fff;
@@ -73,8 +73,7 @@
:root:not([data-theme='dark']) .workspace :is(
.agent-list-items button.active,
.routing-segmented button.active, .plugin-tabs button.active,
.usage-analysis-metric button[aria-pressed='true']
.routing-segmented button.active, .plugin-tabs button.active
) {
background: #fff;
border: 1px solid var(--ui-accent);
@@ -111,6 +110,14 @@
box-shadow: none;
}
:root:not([data-theme='dark']) .workspace .usage-analysis-metric button[aria-pressed='true'],
:root:not([data-theme='dark']) .workspace .usage-analysis-metric button[aria-pressed='true']:hover {
background: color-mix(in srgb, var(--ui-accent) 10%, var(--bg-card));
border-color: transparent;
color: var(--ui-accent);
box-shadow: none;
}
/* Do not let local subpage palettes bring the neutral fills back. */
:root:not([data-theme='dark']) .workspace .usage-tab-panel {
--usage-page-soft: #fff;