fix(editor): account footer is the account alone; language rides elsewhere

The bottom-left block read wrong: a globe crowding the account button for
no gain — language already lives in the menubar's Language menu for
everyone, and (for signed-in users) inside the account menu. The footer
now holds only the account button.

Width-aware, as requested: when the explorer is dragged narrow (footer
under 150px, via container query) the username hides and the avatar
alone identifies the account; with the explorer collapsed, the floating
fallback box shows just the circle.

Adds the OSS half of the crash fix: LocaleSync listens for
'velxio-locale-switch' window events and performs the locale change as a
normal SPA navigation. The pro account menu is injected into its own
React root OUTSIDE the Router, so it cannot navigate itself — its
language rows dispatch this event instead.
This commit is contained in:
David Montero Crespo 2026-07-30 22:56:19 +02:00
parent e17f6fd6cf
commit 155513a493
4 changed files with 42 additions and 33 deletions

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 () => {

View File

@ -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 ? (
<>
<span className="corner-lang">
<LanguageSwitcher />
</span>
<div data-velxio-slot="header-auth" style={{ display: 'contents' }} />
</>
// 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.
<div data-velxio-slot="header-auth" style={{ display: 'contents' }} />
) : undefined;
const unifiedToolbar = !isMobile ? (