From 622b29c6036b3d9a2de01b637bd5afbd144419fb Mon Sep 17 00:00:00 2001 From: youngbeom Date: Tue, 14 Jul 2026 16:29:04 +0800 Subject: [PATCH] fix(web): polish agent model configuration layout --- .../ProfileControls/ProfileControls.css | 23 +++++- web/app/src/components/ui/Select/Select.tsx | 6 +- .../AgentDetailPane/AgentDetailPane.css | 81 +++++++++++++++++-- .../AgentDetailPane/AgentDetailPane.tsx | 36 ++++++++- .../WorkspaceModals/AgentProfileModal.tsx | 5 ++ .../WorkspaceModals/WorkspaceModals.css | 46 ++++++++--- .../SidebarUserButton.module.css | 4 + .../WorkspaceSidebar/WorkspaceSidebar.tsx | 37 +++++---- 8 files changed, 199 insertions(+), 39 deletions(-) diff --git a/web/app/src/components/business/ProfileControls/ProfileControls.css b/web/app/src/components/business/ProfileControls/ProfileControls.css index c20fdc13..4ac8fbf1 100644 --- a/web/app/src/components/business/ProfileControls/ProfileControls.css +++ b/web/app/src/components/business/ProfileControls/ProfileControls.css @@ -177,16 +177,16 @@ } .model-option-label { - display: grid; - grid-template-columns: 24px minmax(0, 1fr); + display: inline-flex; align-items: center; gap: 9px; + width: 100%; min-width: 0; max-width: 100%; + overflow: hidden; } .model-option-label.no-avatar { - grid-template-columns: minmax(0, 1fr); gap: 0; } @@ -194,7 +194,19 @@ display: block; } +.csg-select-trigger .csg-select-value > .model-option-label.single-line { + display: flex; + flex-direction: row; + align-items: center; +} + +.csg-select-trigger .csg-select-value > .model-option-label.single-line .model-option-copy { + display: block; + min-width: 0; +} + .model-option-avatar { + flex: 0 0 24px; width: 24px; height: 24px; display: block; @@ -205,14 +217,19 @@ } .model-option-copy { + flex: 1 1 auto; min-width: 0; + max-width: 100%; display: grid; gap: 1px; + overflow: hidden; } .model-option-model, .model-option-provider { + display: block; min-width: 0; + max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; diff --git a/web/app/src/components/ui/Select/Select.tsx b/web/app/src/components/ui/Select/Select.tsx index 0323ea36..4029a606 100644 --- a/web/app/src/components/ui/Select/Select.tsx +++ b/web/app/src/components/ui/Select/Select.tsx @@ -170,6 +170,7 @@ export type SelectProps = Omit & { placeholder?: ReactNode; searchable?: boolean; searchPlaceholder?: string; + selectedLabel?: ReactNode; size?: SelectSize; triggerClassName?: string; triggerProps?: Omit; @@ -197,6 +198,7 @@ export function Select({ placeholder, searchable = false, searchPlaceholder = "Search", + selectedLabel, size = "md", triggerClassName, triggerProps, @@ -259,7 +261,9 @@ export function Select({ }} {...props} > - + + {selectedLabel} + {children ?? (options ? ( diff --git a/web/app/src/pages/AgentPage/components/AgentDetailPane/AgentDetailPane.css b/web/app/src/pages/AgentPage/components/AgentDetailPane/AgentDetailPane.css index d5debac6..8106f72e 100644 --- a/web/app/src/pages/AgentPage/components/AgentDetailPane/AgentDetailPane.css +++ b/web/app/src/pages/AgentPage/components/AgentDetailPane/AgentDetailPane.css @@ -43,6 +43,29 @@ overflow-y: auto; overscroll-behavior: contain; scrollbar-gutter: stable; + scrollbar-width: thin; + scrollbar-color: transparent transparent; +} + +.agent-profile-scroll-region::-webkit-scrollbar { + width: 8px; +} + +.agent-profile-scroll-region::-webkit-scrollbar-track { + background: transparent; +} + +.agent-profile-scroll-region::-webkit-scrollbar-thumb { + background-color: transparent; + border-radius: 6px; +} + +.agent-profile-scroll-region.is-scrolling { + scrollbar-color: var(--gray-400) transparent; +} + +.agent-profile-scroll-region.is-scrolling::-webkit-scrollbar-thumb { + background-color: var(--gray-400); } .agent-profile-scroll-region .profile-editor-shell.agent-page-editor { @@ -1636,20 +1659,22 @@ } .agent-detail-pane .agent-model-config-grid { - grid-template-columns: minmax(220px, 1.05fr) minmax(320px, 1.5fr) minmax(190px, 0.9fr) minmax(220px, 0.85fr); + grid-template-columns: repeat(auto-fit, minmax(min(100%, 170px), 1fr)); align-items: start; } .agent-detail-pane .agent-model-config-grid > .field { display: grid; - grid-template-rows: 24px 64px; gap: 8px; + min-width: 0; } .agent-detail-pane .agent-model-config-grid .csg-select-trigger, .agent-detail-pane .agent-model-config-grid .agent-fast-mode-toggle { - height: 64px; - min-height: 64px; + width: 100%; + min-width: 0; + max-width: 100%; + box-sizing: border-box; } .agent-detail-pane .agent-model-config-grid .csg-select-trigger { @@ -1662,6 +1687,13 @@ align-items: center; } +.agent-detail-pane .agent-model-config-grid .csg-select-value, +.agent-detail-pane .agent-model-config-grid .csg-select-value > span { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + .agent-detail-pane .field input, .agent-detail-pane .field textarea, .agent-detail-pane .csg-select-trigger { @@ -1720,6 +1752,11 @@ padding: 10px 14px; } +.agent-detail-pane .agent-model-config-grid .agent-fast-mode-field .compact-toggle-row { + padding: 10px 14px; + box-shadow: var(--input-shadow); +} + .agent-detail-pane .agent-fast-mode-toggle input[type="checkbox"] { appearance: none; display: grid; @@ -1763,11 +1800,13 @@ } .agent-detail-pane .agent-fast-mode-help { + display: block; min-width: 0; - color: var(--muted); - font-size: 14px; - font-weight: 620; - line-height: 20px; + overflow: hidden; + color: var(--input-text); + font: var(--text-md-normal); + text-overflow: ellipsis; + white-space: nowrap; } .agent-detail-pane .profile-section .field textarea.compact-textarea { @@ -1932,6 +1971,32 @@ color: var(--input-text); } +:root[data-theme="dark"] + .agent-detail-pane + .profile-section + .agent-model-config-grid + .selection-item.compact-toggle-row.agent-fast-mode-toggle, +:root[data-theme="dark"] + .agent-detail-pane + .profile-section + .agent-model-config-grid + .selection-item.compact-toggle-row.agent-fast-mode-toggle:hover, +:root[data-theme="dark"] + .agent-detail-pane + .profile-section + .agent-model-config-grid + .selection-item.compact-toggle-row.agent-fast-mode-toggle:focus-within, +:root[data-theme="dark"] + .agent-detail-pane + .profile-section + .agent-model-config-grid + .selection-item.compact-toggle-row.agent-fast-mode-toggle:has(input:checked) { + border-color: var(--input-border); + background: var(--input-background); + color: var(--input-text); + box-shadow: var(--input-shadow); +} + :root[data-theme="dark"] .agent-detail-pane .agent-fast-mode-help { color: var(--input-text); } diff --git a/web/app/src/pages/AgentPage/components/AgentDetailPane/AgentDetailPane.tsx b/web/app/src/pages/AgentPage/components/AgentDetailPane/AgentDetailPane.tsx index 818db9a5..c9069b3f 100644 --- a/web/app/src/pages/AgentPage/components/AgentDetailPane/AgentDetailPane.tsx +++ b/web/app/src/pages/AgentPage/components/AgentDetailPane/AgentDetailPane.tsx @@ -238,8 +238,10 @@ export function AgentDetailPane({ const [selectedMCPServerNames, setSelectedMCPServerNames] = useState([]); const [deleteMCPDialogOpen, setDeleteMCPDialogOpen] = useState(false); const [mcpPendingDelete, setMCPPendingDelete] = useState(null); + const [isProfileScrolling, setIsProfileScrolling] = useState(false); const descriptionInputRef = useRef(null); const nameInputRef = useRef(null); + const profileScrollTimerRef = useRef(null); const isManager = isManagerAgent(item); const canEditAgentName = Boolean(draft && !isManager); const running = isAgentRunning(item); @@ -351,6 +353,26 @@ export function AgentDetailPane({ } }, [showMCPServers]); + useEffect( + () => () => { + if (profileScrollTimerRef.current) { + window.clearTimeout(profileScrollTimerRef.current); + } + }, + [], + ); + + function onProfileScroll() { + setIsProfileScrolling(true); + if (profileScrollTimerRef.current) { + window.clearTimeout(profileScrollTimerRef.current); + } + profileScrollTimerRef.current = window.setTimeout(() => { + setIsProfileScrolling(false); + profileScrollTimerRef.current = null; + }, 700); + } + async function handleAddSkillsConfirm(): Promise { if (!selectedSkillNames.length) { return; @@ -599,7 +621,10 @@ export function AgentDetailPane({ ) : null} -
+
{!draft ? ( <>
@@ -1168,6 +1193,7 @@ function AgentModelPanel({ t, updateDraft, }: AgentModelPanelProps) { + const selectedProviderOption = providerOptions.find((option) => option.id === selectedProviderID); return (
@@ -1182,6 +1208,14 @@ function AgentModelPanel({ + ) : undefined + } onValueChange={(value) => { const nextProvider = providerOptions.find((option) => option.id === value); if (!nextProvider) { diff --git a/web/app/src/pages/WorkspacePage/components/WorkspaceModals/WorkspaceModals.css b/web/app/src/pages/WorkspacePage/components/WorkspaceModals/WorkspaceModals.css index 4522ffcb..492f24d5 100644 --- a/web/app/src/pages/WorkspacePage/components/WorkspaceModals/WorkspaceModals.css +++ b/web/app/src/pages/WorkspacePage/components/WorkspaceModals/WorkspaceModals.css @@ -1210,19 +1210,21 @@ } .profile-runtime-grid.agent-model-config-grid { - grid-template-columns: minmax(180px, 1.05fr) minmax(240px, 1.4fr) minmax(150px, 0.9fr) minmax(190px, 0.9fr); + grid-template-columns: repeat(auto-fit, minmax(min(100%, 170px), 1fr)); } .profile-runtime-grid.agent-model-config-grid > .field { display: grid; - grid-template-rows: 22px 60px; gap: 7px; + min-width: 0; } .profile-runtime-grid.agent-model-config-grid .csg-select-trigger, .profile-runtime-grid.agent-model-config-grid .agent-fast-mode-toggle { - height: 60px; - min-height: 60px; + width: 100%; + min-width: 0; + max-width: 100%; + box-sizing: border-box; } .profile-runtime-grid.agent-model-config-grid .csg-select-trigger { @@ -1647,8 +1649,8 @@ .profile-modal.agent-modal .agent-model-config-grid .agent-fast-mode-field .compact-toggle-row { align-items: center; - height: 60px; - min-height: 60px; + padding-block: 10px; + box-shadow: var(--input-shadow); } .profile-modal.agent-modal .agent-fast-mode-field input[type="checkbox"] { @@ -1700,10 +1702,8 @@ .profile-modal.agent-modal .agent-fast-mode-help { flex: 1 1 auto; min-width: 0; - color: var(--gray-600); - font-size: 14px; - font-weight: 400; - line-height: 20px; + color: var(--input-text); + font: var(--text-md-normal); overflow-wrap: anywhere; white-space: normal; } @@ -2214,6 +2214,32 @@ color: var(--input-text); } +:root[data-theme="dark"] + .profile-modal.agent-modal + .profile-section + .agent-model-config-grid + .selection-item.compact-toggle-row.agent-fast-mode-toggle, +:root[data-theme="dark"] + .profile-modal.agent-modal + .profile-section + .agent-model-config-grid + .selection-item.compact-toggle-row.agent-fast-mode-toggle:hover, +:root[data-theme="dark"] + .profile-modal.agent-modal + .profile-section + .agent-model-config-grid + .selection-item.compact-toggle-row.agent-fast-mode-toggle:focus-within, +:root[data-theme="dark"] + .profile-modal.agent-modal + .profile-section + .agent-model-config-grid + .selection-item.compact-toggle-row.agent-fast-mode-toggle:has(input:checked) { + border-color: var(--input-border); + background: var(--input-background); + color: var(--input-text); + box-shadow: var(--input-shadow); +} + :root[data-theme="dark"] .profile-modal.agent-modal .agent-sandbox-toggle.readonly, :root[data-theme="dark"] .profile-modal.agent-modal .agent-sandbox-toggle.readonly:hover, :root[data-theme="dark"] .profile-modal.agent-modal .agent-sandbox-toggle.readonly:focus-within, diff --git a/web/app/src/pages/WorkspacePage/components/WorkspaceSidebar/SidebarUserButton.module.css b/web/app/src/pages/WorkspacePage/components/WorkspaceSidebar/SidebarUserButton.module.css index 8aeadebc..39d6df55 100644 --- a/web/app/src/pages/WorkspacePage/components/WorkspaceSidebar/SidebarUserButton.module.css +++ b/web/app/src/pages/WorkspacePage/components/WorkspaceSidebar/SidebarUserButton.module.css @@ -667,6 +667,10 @@ color: var(--white); } +:global(:root[data-theme="dark"]) .active { + background: var(--gray-800); +} + :global(:root[data-theme="dark"]) .alertDot { box-shadow: 0 0 0 3px var(--surface); } diff --git a/web/app/src/pages/WorkspacePage/components/WorkspaceSidebar/WorkspaceSidebar.tsx b/web/app/src/pages/WorkspacePage/components/WorkspaceSidebar/WorkspaceSidebar.tsx index 090deb70..4e9427dc 100644 --- a/web/app/src/pages/WorkspacePage/components/WorkspaceSidebar/WorkspaceSidebar.tsx +++ b/web/app/src/pages/WorkspacePage/components/WorkspaceSidebar/WorkspaceSidebar.tsx @@ -1,4 +1,4 @@ -import { useEffect, useMemo, useRef, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { PanelLeftOpen, Plus, Search } from "lucide-react"; import { SidebarAlertTriangleIcon, @@ -132,6 +132,8 @@ export function WorkspaceSidebar({ const firstMCPServer = hub?.mcpServers?.[0] ?? null; const firstHubSkill = hub?.skills[0] ?? null; const firstModelProvider = modelProviders?.providers[0] ?? null; + const isSettingsPane = activePane.type === WorkspacePaneTypes.settings; + const isPrimaryNavigationActive = useCallback((active: boolean) => !isSettingsPane && active, [isSettingsPane]); const notificationAgentIds = useMemo( () => new Set(notificationAgentItems.map((item) => item.id).filter((id): id is string => Boolean(id))), [notificationAgentItems], @@ -162,9 +164,7 @@ export function WorkspaceSidebar({ label: t("messagesTab"), items: [ { - active: - activePane.type !== WorkspacePaneTypes.settings && - activeContextSectionId === WorkspaceContextSectionIds.messages, + active: isPrimaryNavigationActive(activeContextSectionId === WorkspaceContextSectionIds.messages), groupId: WorkspaceContextSectionIds.messages, icon: navigationIcon(WORKSPACE_NAVIGATION_ICONS.messages), id: "messages", @@ -178,7 +178,7 @@ export function WorkspaceSidebar({ label: t("agentsTab"), items: [ { - active: activeContextSectionId === WorkspaceContextSectionIds.agents, + active: isPrimaryNavigationActive(activeContextSectionId === WorkspaceContextSectionIds.agents), groupId: WorkspaceContextSectionIds.agents, icon: navigationIcon(WORKSPACE_NAVIGATION_ICONS.agents), id: "agents", @@ -190,7 +190,7 @@ export function WorkspaceSidebar({ }, }, { - active: activeContextSectionId === WorkspaceContextSectionIds.humans, + active: isPrimaryNavigationActive(activeContextSectionId === WorkspaceContextSectionIds.humans), groupId: WorkspaceContextSectionIds.humans, icon: navigationIcon(WORKSPACE_NAVIGATION_ICONS.humans), id: "humans", @@ -202,7 +202,7 @@ export function WorkspaceSidebar({ }, }, { - active: activeContextSectionId === WorkspaceContextSectionIds.computers, + active: isPrimaryNavigationActive(activeContextSectionId === WorkspaceContextSectionIds.computers), groupId: WorkspaceContextSectionIds.computers, icon: navigationIcon(WORKSPACE_NAVIGATION_ICONS.computers), id: "computers", @@ -210,7 +210,7 @@ export function WorkspaceSidebar({ onSelect: onSelectComputer, }, { - active: activeContextSectionId === WorkspaceContextSectionIds.notifications, + active: isPrimaryNavigationActive(activeContextSectionId === WorkspaceContextSectionIds.notifications), groupId: WorkspaceContextSectionIds.notifications, icon: navigationIcon(WORKSPACE_NAVIGATION_ICONS.notifications), id: "notifications", @@ -224,7 +224,7 @@ export function WorkspaceSidebar({ }, }, { - active: activeContextSectionId === WorkspaceContextSectionIds.teams, + active: isPrimaryNavigationActive(activeContextSectionId === WorkspaceContextSectionIds.teams), groupId: WorkspaceContextSectionIds.teams, icon: navigationIcon(WORKSPACE_NAVIGATION_ICONS.teams), id: "teams", @@ -244,7 +244,9 @@ export function WorkspaceSidebar({ label: t("tasksTab"), items: [ { - active: activePane.type === WorkspacePaneTypes.task && activeTaskBoardView !== "scheduled", + active: isPrimaryNavigationActive( + activePane.type === WorkspacePaneTypes.task && activeTaskBoardView !== "scheduled", + ), groupId: WorkspaceContextSectionIds.tasks, icon: navigationIcon(WORKSPACE_NAVIGATION_ICONS.tasks), id: "tasks", @@ -255,7 +257,9 @@ export function WorkspaceSidebar({ }, }, { - active: activePane.type === WorkspacePaneTypes.task && activeTaskBoardView === "scheduled", + active: isPrimaryNavigationActive( + activePane.type === WorkspacePaneTypes.task && activeTaskBoardView === "scheduled", + ), groupId: WorkspaceContextSectionIds.tasks, icon: navigationIcon(WORKSPACE_NAVIGATION_ICONS.scheduledTasks), id: "scheduled-tasks", @@ -272,7 +276,7 @@ export function WorkspaceSidebar({ label: t("resourcesTab"), items: [ { - active: activeContextSectionId === WorkspaceContextSectionIds.hubTemplates, + active: isPrimaryNavigationActive(activeContextSectionId === WorkspaceContextSectionIds.hubTemplates), groupId: WorkspaceContextSectionIds.hubTemplates, icon: navigationIcon(WORKSPACE_NAVIGATION_ICONS.templates), id: "templates", @@ -286,7 +290,7 @@ export function WorkspaceSidebar({ }, }, { - active: activeContextSectionId === WorkspaceContextSectionIds.hubSkills, + active: isPrimaryNavigationActive(activeContextSectionId === WorkspaceContextSectionIds.hubSkills), groupId: WorkspaceContextSectionIds.hubSkills, icon: navigationIcon(WORKSPACE_NAVIGATION_ICONS.skills), id: "skills", @@ -300,7 +304,7 @@ export function WorkspaceSidebar({ }, }, { - active: activeContextSectionId === WorkspaceContextSectionIds.mcpServers, + active: isPrimaryNavigationActive(activeContextSectionId === WorkspaceContextSectionIds.mcpServers), groupId: WorkspaceContextSectionIds.mcpServers, icon: navigationIcon(SidebarMcpIcon), id: "mcp-servers", @@ -314,7 +318,7 @@ export function WorkspaceSidebar({ }, }, { - active: activeContextSectionId === WorkspaceContextSectionIds.models, + active: isPrimaryNavigationActive(activeContextSectionId === WorkspaceContextSectionIds.models), groupId: WorkspaceContextSectionIds.models, icon: navigationIcon(WORKSPACE_NAVIGATION_ICONS.models), id: "models", @@ -356,6 +360,7 @@ export function WorkspaceSidebar({ onSelectTeam, onSelectTeamSection, onWorkspaceTabChange, + isPrimaryNavigationActive, t, ], ); @@ -440,7 +445,7 @@ export function WorkspaceSidebar({