diff --git a/frontend/src/App.css b/frontend/src/App.css index 7812bbfe..fd549460 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -633,7 +633,19 @@ body { background: #171a21; } -.explorer-account-footer:has([data-auth-user]) .corner-lang, -.editor-corner-box:has([data-auth-user]) .corner-lang { +.explorer-account-footer { + container-type: inline-size; +} + +/* Tight footer (narrow explorer): the avatar alone identifies the account; + the name is a luxury that costs exactly the width we do not have. */ +@container (max-width: 150px) { + .explorer-account-footer .header-username-text { + display: none; + } +} + +/* Collapsed explorer: the floating fallback box shows the circle only. */ +.editor-corner-box .header-username-text { display: none; } diff --git a/frontend/src/components/layout/LanguageSwitcher.css b/frontend/src/components/layout/LanguageSwitcher.css index 4fc3cc7e..e7d28cd5 100644 --- a/frontend/src/components/layout/LanguageSwitcher.css +++ b/frontend/src/components/layout/LanguageSwitcher.css @@ -76,18 +76,4 @@ letter-spacing: 0.4px; } -/* Inside the editor's bottom-left corner box the switcher sits at the - bottom edge of the screen — the menu must drop UP or it opens straight - into the void below the viewport. */ -.editor-corner-box .velxio-language-menu { - top: auto; - bottom: 110%; - margin: 0 0 6px; -} -/* Same drop-up need at the explorer footer. */ -.explorer-account-footer .velxio-language-menu { - top: auto; - bottom: 110%; - margin: 0 0 6px; -} diff --git a/frontend/src/i18n/LocaleSync.tsx b/frontend/src/i18n/LocaleSync.tsx index 296f7cd8..a714e3f1 100644 --- a/frontend/src/i18n/LocaleSync.tsx +++ b/frontend/src/i18n/LocaleSync.tsx @@ -11,18 +11,35 @@ */ import { useEffect, type ReactNode } from "react"; -import { useLocation } from "react-router-dom"; +import { useLocation, useNavigate } from "react-router-dom"; import { i18n, loadLocale } from "./index"; -import { LOCALE_META, DEFAULT_LOCALE } from "./config"; -import { getLocaleFromPath } from "./path"; +import { LOCALE_META, DEFAULT_LOCALE, type Locale } from "./config"; +import { getLocaleFromPath, switchLocale } from "./path"; import { writeLocaleCookie } from "./cookie"; type Props = { children: ReactNode }; export function LocaleSync({ children }: Props) { const { pathname } = useLocation(); + const navigate = useNavigate(); const target = getLocaleFromPath(pathname); + // Locale switching for UI that lives OUTSIDE the Router: the pro account + // menu is injected into its own React root, so it cannot call + // useNavigate() — its language rows dispatch this event instead, and the + // switch happens here as a normal SPA navigation (no reload, workspace + // intact). Same path the header globe takes. + useEffect(() => { + const onSwitch = (e: Event): void => { + const locale = (e as CustomEvent<{ locale?: string }>).detail?.locale; + if (!locale) return; + const next = switchLocale(window.location.pathname, locale as Locale); + navigate(next + window.location.search + window.location.hash); + }; + window.addEventListener('velxio-locale-switch', onSwitch); + return () => window.removeEventListener('velxio-locale-switch', onSwitch); + }, [navigate]); + useEffect(() => { let cancelled = false; (async () => { diff --git a/frontend/src/pages/EditorPage.tsx b/frontend/src/pages/EditorPage.tsx index e9ebdf96..468fd250 100644 --- a/frontend/src/pages/EditorPage.tsx +++ b/frontend/src/pages/EditorPage.tsx @@ -29,7 +29,6 @@ import { showConfirmDialog } from '../store/useMessageDialogStore'; import { useAutoSaveProject } from '../hooks/useAutoSaveProject'; import { registerEditorCommand } from '../lib/editorCommands'; import { EditorMenuBar } from '../components/editor/EditorMenuBar'; -import { LanguageSwitcher } from '../components/layout/LanguageSwitcher'; import type { CompilationLog } from '../utils/compilationLogger'; import '../App.css'; @@ -390,20 +389,15 @@ export const EditorPage: React.FC = () => { there used to be 44+38. On narrow widths the strip wraps internally and the header grows; the docked AI chat is avoided by the same padding-right the strip always had. */ - /* Account + language block. Fused as the explorer panel's footer so a - long file tree scrolls ABOVE it instead of disappearing underneath a - floating box; when the explorer is collapsed it falls back to the - small fixed corner box. The standalone language switcher only shows - while logged OUT — signed-in users change language inside the account - menu (see pro HeaderAuth), and a CSS :has() rule hides the globe when - the auth slot renders a logged-in marker. */ + /* Account block. Fused as the explorer panel's footer so a long file + tree scrolls ABOVE it instead of disappearing underneath a floating + box; when the explorer is collapsed it falls back to the small fixed + corner box (avatar only there — no room for a name). */ const accountBlock = !isMobile ? ( - <> - - - -
- + // Just the account button. Language lives in the menubar's Language + // menu (for everyone) and inside the account menu (for signed-in + // users) — the standalone globe crowded the footer for no gain. +
) : undefined; const unifiedToolbar = !isMobile ? (