Skip to content

update error loading unwrapped ui bugs#1514

Open
Douglasymlai wants to merge 10 commits intomainfrom
edge-case-ui-fix
Open

update error loading unwrapped ui bugs#1514
Douglasymlai wants to merge 10 commits intomainfrom
edge-case-ui-fix

Conversation

@Douglasymlai
Copy link
Copy Markdown
Contributor

@Douglasymlai Douglasymlai commented Mar 24, 2026

Related Issue

Fixes various edge-case UI bugs, race conditions, and polish issues across the frontend and server.

Description

UI Race Condition Fixes

  • Integration install state hydration guard (useIntegrationManagement.ts): Added configsHydrated flag 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.
  • MCP hydration guard (ToolSelect.tsx): Added userMcpsHydrated flag 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 blur timer fix (input-select.tsx): Track the handleInputBlur setTimeout via blurTimerRef and clear it on unmount to prevent setState-after-unmount warnings. Added isMountedRef guard.

Toaster Fix

  • Sonner portal rendering (sonner.tsx): Wrap <Toaster> in createPortal(…, 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 bare sonner.

Integration List UI Polish

  • Status dot (IntegrationList/index.tsx): Replaced <img> with CSS filter hacks with a simple <div> using Tailwind bg-icon-success / bg-icon-secondary / bg-icon-warning classes. Added per-item error flash (4s timeout) on install failure.
  • InstallButton component: Extracted shared InstallButton with animated hover icons (Cable for connect, X for disconnect) using framer-motion AnimatePresence. Added hover-state reset on install state change to prevent flicker.
  • Install state ready UX: Items show cursor-wait opacity-70 and clicks are blocked until configsHydrated is true.

Button Component Enhancement

  • Rounded variant (button.tsx): Added rounded variant (default | full) to buttonVariants CVA config. Used in Connect/Disconnect buttons and dialog footers for pill-shaped actions. Added Storybook story.

Dialog Footer Enhancement

  • Rounded props (dialog.tsx): Added confirmButtonRounded and cancelButtonRounded props to DialogFooter, passed through to <Button rounded={…}>.

Input Component Fixes

  • Overflow handling (input.tsx, input-select.tsx): Added min-w-0 max-w-full [overflow-wrap:anywhere] to note/error text to prevent long validation messages from overflowing containers. Added min-w-0 to input wrapper.

Stale Local Server Detection

  • Server hash in /health (server/main.py): Read the latest git commit hash for server/ at startup (via git log with 5s timeout, falling back to Docker-baked .image_env). Return server_hash and version in /health response.
  • Dockerfile (server/Dockerfile): Bake EIGENT_SERVER_GIT_COMMIT into .image_env at build time using --mount=type=bind for .git.
  • Frontend detection (src/api/http.ts): checkLocalServerStale() fires once after backend health check passes. Compares VITE_SERVER_CODE_HASH (injected at build time) against the running server's hash. Shows a persistent toast if mismatched.
  • Vite config (vite.config.ts): Inject VITE_SERVER_CODE_HASH env var at build time via git log.

Logger Fix

  • Use loguru (server/main.py): Replaced logger.warning/logger.error with loguru_logger for public dir warnings to match the rest of the codebase.

i18n Updates

  • Added connect, disconnect, back, task-stopped-successfully, backend-startup-failed keys across all locales.
  • Added browser login and cookie manager keys to settings locales.
  • Removed duplicate key blocks in several locale files (ar, de, en-us, es, fr, it, ja, ko, ru, zh-Hans, zh-Hant).

Testing Evidence (REQUIRED)

  • I have included human-verified testing evidence in this PR.
  • This PR includes frontend/UI changes, and I attached screenshot(s) or screen recording(s).
  • No frontend/UI changes in this PR.
Screenshot 2026-04-06 at 13 00 18 Screenshot 2026-04-06 at 13 00 32

What is the purpose of this pull request?

  • Bug fix
  • New Feature
  • Documentation update
  • Other

Contribution Guidelines Acknowledgement

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +94 to +104
// 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);
}, []);
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Douglasymlai pls help check the issue.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines 276 to 280
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(() => {
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copilot uses AI. Check for mistakes.
Comment on lines 15 to 22
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();
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines 289 to 295
@@ -289,6 +290,8 @@
"coming-soon": "Coming Soon",
"uninstall": "Uninstall",
"install": "Install",
"connect": "Connect",
"disconnect": "Disconnect",
"add-your-agent": "Add Your Agent",
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines 279 to 285
"google-cloud-console": "Google Cloud وحدة تحكم",
"connect": "اتصال",
"disconnect": "قطع الاتصال",
"setting": "إعداد",
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +57 to +68
if (!ignore) {
setConfigs([]);
setConfigsHydrated(true);
}
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants