Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates the Connectors/MCP UI and shared UI components to improve connect/disconnect affordances, status indicators, and loading/hydration behavior (reducing “Connect → Disconnect” flashing). It also adjusts toast layering so notifications appear above dialogs/modals, and aligns i18n strings for connect/disconnect across locales.
Changes:
- Add “hydration” flags for installed-state derivation and disable actions until data is loaded (MCP market, tool selection, integration management).
- Introduce pill/rounded-full buttons and status-dot UI across MCP/integration lists and several dialogs.
- Move Sonner’s Toaster rendering to a portal + raise toaster z-index; add/align connect/disconnect i18n keys across locales.
Reviewed changes
Copilot reviewed 40 out of 40 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/style/index.css | Ensures Sonner toaster is stacked above modals/dialogs via z-index. |
| src/pages/Connectors/components/MCPMarket.tsx | Adds userInstallsHydrated gating + pill connect/disconnect button behavior. |
| src/pages/Connectors/components/MCPListItem.tsx | Replaces hardcoded green dot with status-derived dot classes. |
| src/pages/Connectors/components/MCPEnvDialog.tsx | Uses new DialogFooter rounded props for pill footer actions. |
| src/pages/Connectors/components/MCPAddDialog.tsx | Makes Monaco editor area flex-fill + enables word wrapping for long JSON. |
| src/pages/Browser/CDP.tsx | Updates buttons to pill style and refactors some layout class ordering. |
| src/i18n/locales/zh-Hant/setting.json | Adds disconnect translation. |
| src/i18n/locales/zh-Hant/layout.json | Reorders/adds connect/disconnect keys near install-related strings. |
| src/i18n/locales/zh-Hans/setting.json | Adds disconnect translation. |
| src/i18n/locales/zh-Hans/layout.json | Reorders/adds connect/disconnect keys near install-related strings. |
| src/i18n/locales/ru/setting.json | Adds disconnect translation. |
| src/i18n/locales/ru/layout.json | Reorders/adds connect/disconnect keys near install-related strings. |
| src/i18n/locales/ko/setting.json | Adds disconnect translation. |
| src/i18n/locales/ko/layout.json | Reorders/adds connect/disconnect keys near install-related strings. |
| src/i18n/locales/ja/setting.json | Adds disconnect translation. |
| src/i18n/locales/ja/layout.json | Reorders/adds connect/disconnect keys near install-related strings. |
| src/i18n/locales/it/setting.json | Adds disconnect translation. |
| src/i18n/locales/it/layout.json | Reorders/adds connect/disconnect keys near install-related strings. |
| src/i18n/locales/fr/setting.json | Updates connect phrasing and adds disconnect. |
| src/i18n/locales/fr/layout.json | Reorders/adds connect/disconnect keys near install-related strings. |
| src/i18n/locales/es/setting.json | Adds disconnect translation. |
| src/i18n/locales/es/layout.json | Reorders/adds connect/disconnect keys near install-related strings. |
| src/i18n/locales/en-us/setting.json | Adds connect/disconnect near install strings; removes some duplicate connect entries. |
| src/i18n/locales/en-us/layout.json | Reorders/adds connect/disconnect keys near install-related strings. |
| src/i18n/locales/de/setting.json | Adds disconnect translation. |
| src/i18n/locales/de/layout.json | Reorders/adds connect/disconnect keys near install-related strings. |
| src/i18n/locales/ar/setting.json | Adds disconnect translation (in both duplicated blocks). |
| src/i18n/locales/ar/layout.json | Reorders/adds connect/disconnect keys near install-related strings. |
| src/hooks/useIntegrationManagement.ts | Introduces configsHydrated to avoid install-state flashing before configs load. |
| src/components/ui/sonner.tsx | Renders Toaster via portal into document.body. |
| src/components/ui/input.tsx | Improves layout/overflow handling for narrow containers and long notes. |
| src/components/ui/input-select.tsx | Refactors component formatting/behavior + improves note wrapping/scroll handling. |
| src/components/ui/dialog.tsx | Adds optional rounded props for footer confirm/cancel buttons. |
| src/components/ui/button.tsx | Adds rounded variant (default/full) to support pill buttons. |
| src/components/ui/button.stories.tsx | Adds Storybook control + example for rounded="full". |
| src/components/IntegrationList/index.tsx | Replaces ellipse icon with status dots; adds hydration gating and new connect/disconnect button behavior. |
| src/components/AddWorker/index.tsx | Applies rounded footer actions and layout class reordering. |
| src/components/AddWorker/ToolSelect.tsx | Adds userMcpsHydrated gating to prevent premature installed-state derivation and disables install until ready. |
| src/App.tsx | Switches to app-local Toaster component and removes inline z-index style. |
| .storybook/preview.tsx | Switches to app-local Toaster component in Storybook. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Per-item error state for status dot | ||
| const [errorKeys, setErrorKeys] = useState<Record<string, boolean>>({}); | ||
| const errorTimers = useRef<Record<string, ReturnType<typeof setTimeout>>>({}); | ||
|
|
||
| const markError = useCallback((key: string) => { | ||
| setErrorKeys((prev) => ({ ...prev, [key]: true })); | ||
| if (errorTimers.current[key]) clearTimeout(errorTimers.current[key]); | ||
| errorTimers.current[key] = setTimeout(() => { | ||
| setErrorKeys((prev) => ({ ...prev, [key]: false })); | ||
| }, 4000); | ||
| }, []); |
There was a problem hiding this comment.
markError schedules per-key setTimeout callbacks but there’s no cleanup on unmount, so the timeout can fire after the component is gone (state update on unmounted component) and the ref will retain timers. Add a cleanup effect that clears all pending timeouts (and optionally deletes entries from errorTimers.current) when the component unmounts.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 40 out of 40 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/components/ui/input-select.tsx
Outdated
| const handleInputBlur = () => { | ||
| // Use setTimeout to allow option clicks to process first | ||
| // If user clicks an option, the option handler will update the value | ||
| // If user clicks outside, we commit the current input value | ||
| setTimeout(() => { |
There was a problem hiding this comment.
handleInputBlur schedules a setTimeout that calls setIsOpen(false) and commitValue(), but the timer isn’t tracked/cleared on unmount. If the component unmounts within 150ms (e.g., dialog closes), this can trigger state updates after unmount. Store the timeout id in a ref and clear it in an effect cleanup (and/or when a new blur occurs).
| import { useTheme } from 'next-themes'; | ||
| import { createPortal } from 'react-dom'; | ||
| import { Toaster as Sonner } from 'sonner'; | ||
|
|
||
| type ToasterProps = React.ComponentProps<typeof Sonner>; | ||
|
|
||
| const Toaster = ({ ...props }: ToasterProps) => { | ||
| const { theme = 'system' } = useTheme(); |
There was a problem hiding this comment.
This file uses the React namespace for types (React.ComponentProps) but doesn’t import React. Most UI components in this codebase import React when referencing React.* types (e.g., src/components/ui/input.tsx:15). Add an explicit import type * as React from 'react' (or equivalent) to avoid relying on global UMD typings and to match local conventions.
There was a problem hiding this comment.
@copilot apply changes based on this feedback
There was a problem hiding this comment.
Added import * as React from 'react' to sonner.tsx in commit 4705f4b, matching the convention used in other UI components like input.tsx.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Agent-Logs-Url: https://github.com/eigent-ai/eigent/sessions/d559fe91-0d43-4708-a771-ece8d840cc5c Co-authored-by: Douglasymlai <115660088+Douglasymlai@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 40 out of 40 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/i18n/locales/en-us/setting.json
Outdated
| @@ -289,6 +290,8 @@ | |||
| "coming-soon": "Coming Soon", | |||
| "uninstall": "Uninstall", | |||
| "install": "Install", | |||
| "connect": "Connect", | |||
| "disconnect": "Disconnect", | |||
| "add-your-agent": "Add Your Agent", | |||
There was a problem hiding this comment.
connect/disconnect are defined twice in this JSON object (e.g. again at lines 293–294). With duplicate JSON keys, only the last occurrence wins, so the earlier values are effectively dead and it’s easy for translations to diverge silently. Please remove the duplicate entries (or split these repeated sections into separate namespaces/files) so each key is defined once.
src/i18n/locales/ar/setting.json
Outdated
| "google-cloud-console": "Google Cloud وحدة تحكم", | ||
| "connect": "اتصال", | ||
| "disconnect": "قطع الاتصال", | ||
| "setting": "إعداد", |
There was a problem hiding this comment.
This locale file contains duplicate JSON keys for connect/disconnect (they appear in two places; only the later entries will be used at runtime). Please deduplicate these keys (or split the repeated blocks into separate namespaces) to avoid silent overrides and translation drift.
| if (!ignore) { | ||
| setConfigs([]); | ||
| setConfigsHydrated(true); | ||
| } |
There was a problem hiding this comment.
The ignore flag here won’t reliably prevent state updates after unmount: it’s passed as a boolean value to fetchInstalled, so when the effect cleanup flips ignore = true it doesn’t affect the already-running async call. Consider switching to a ref/AbortController (or checking an isMounted ref) inside fetchInstalled before calling setConfigs/setConfigsHydrated.
Related Issue
Fixes various edge-case UI bugs, race conditions, and polish issues across the frontend and server.
Description
UI Race Condition Fixes
useIntegrationManagement.ts): AddedconfigsHydratedflag and sequence-counter (configsFetchSeqRef) to prevent stale fetch responses from clobbering state. Buttons now show "Loading..." until the first configs fetch completes, preventing a flash of incorrect "Connect" state.ToolSelect.tsx): AddeduserMcpsHydratedflag so the MCP install list waits for the first fetch before rendering install/installed state. Buttons and click handlers are disabled until hydrated.input-select.tsx): Track thehandleInputBlursetTimeoutviablurTimerRefand clear it on unmount to prevent setState-after-unmount warnings. AddedisMountedRefguard.Toaster Fix
sonner.tsx): Wrap<Toaster>increatePortal(…, document.body)so toasts render above all stacking contexts (dialogs, drawers). Added SSR guard (typeof document === 'undefined'). Updated all import sites (App.tsx,.storybook/preview.tsx) to use the custom wrapper instead of baresonner.Integration List UI Polish
IntegrationList/index.tsx): Replaced<img>with CSS filter hacks with a simple<div>using Tailwindbg-icon-success/bg-icon-secondary/bg-icon-warningclasses. Added per-item error flash (4s timeout) on install failure.InstallButtonwith animated hover icons (Cable for connect, X for disconnect) usingframer-motionAnimatePresence. Added hover-state reset on install state change to prevent flicker.cursor-wait opacity-70and clicks are blocked untilconfigsHydratedis true.Button Component Enhancement
button.tsx): Addedroundedvariant (default|full) tobuttonVariantsCVA config. Used in Connect/Disconnect buttons and dialog footers for pill-shaped actions. Added Storybook story.Dialog Footer Enhancement
dialog.tsx): AddedconfirmButtonRoundedandcancelButtonRoundedprops toDialogFooter, passed through to<Button rounded={…}>.Input Component Fixes
input.tsx,input-select.tsx): Addedmin-w-0 max-w-full [overflow-wrap:anywhere]to note/error text to prevent long validation messages from overflowing containers. Addedmin-w-0to input wrapper.Stale Local Server Detection
server/main.py): Read the latest git commit hash forserver/at startup (viagit logwith 5s timeout, falling back to Docker-baked.image_env). Returnserver_hashandversionin/healthresponse.server/Dockerfile): BakeEIGENT_SERVER_GIT_COMMITinto.image_envat build time using--mount=type=bindfor.git.src/api/http.ts):checkLocalServerStale()fires once after backend health check passes. ComparesVITE_SERVER_CODE_HASH(injected at build time) against the running server's hash. Shows a persistent toast if mismatched.vite.config.ts): InjectVITE_SERVER_CODE_HASHenv var at build time viagit log.Logger Fix
server/main.py): Replacedlogger.warning/logger.errorwithloguru_loggerfor public dir warnings to match the rest of the codebase.i18n Updates
connect,disconnect,back,task-stopped-successfully,backend-startup-failedkeys across all locales.Testing Evidence (REQUIRED)
What is the purpose of this pull request?
Contribution Guidelines Acknowledgement