velxio/frontend/src/components/editor/EditorMenuBar.tsx

318 lines
13 KiB
TypeScript
Raw Normal View History

feat(editor): menus File/Edit en la cabecera y editor sin nav de marketing La queja: demasiados botones en una sola fila, y en pantallas pequenas se solapan — medido, no impresion: a 1024px Code/Both/Circuit pisaban a Compile/Run/Stop por 9-20px y Add pisaba el chat por 29px, con el idioma y el usuario cortados fuera del viewport. Tres piezas: 1. En el editor, el nav de marketing (Home/Docs/Pricing/...) sobra: ya estas dentro, y cuesta exactamente el ancho que le falta a la toolbar. AppHeader gana la variante editorMenu — mismo mecanismo que ya usa la build de escritorio (VITE_DESKTOP) — que oculta el nav y pinta un menu File/Edit junto al logo. El logo sigue llevando a inicio; idioma, autoguardado, Share y usuario se quedan. 2. File/Edit al estilo escritorio: lo que se usa cada minuto sigue siendo boton; lo que se usa unas veces por sesion va al menu. File: nuevo workspace, nuevo fichero, abrir, guardar, importar, exportar, BOM, imagen del esquema. Edit: undo/redo (con su estado real del historial), centrar vista, zoom. Las acciones viven en cierres de cuatro componentes distintos, asi que hay un registro id->handler (lib/editorCommands, mismo patron de seam que registerProExamples o registerBoardBuiltins): cada dueno registra al montar y el menu invoca por id; un item sin dueno montado se pinta deshabilitado, que ademas es la verdad. El registro sobrevive al remontaje StrictMode (el cleanup viejo no borra al sucesor) y tiene test de eso. 3. La toolbar adelgaza: fuera Undo/Redo (Ctrl+Z/Y siguen), fuera los botones inline de Import/Export y sus gemelos responsivos del More (~70px recuperados); el More queda solo con los extras pro (firmware, GitHub, share, grabacion). El CSS responsivo de los botones retirados se va con ellos. Suite en verde (2300) y build del overlay verificado desde este arbol.
2026-07-30 21:26:36 +07:00
/**
* EditorMenuBar — File / Edit menus for the editor header.
*
* The editor grew a single toolbar row where every action, frequent or
* rare, competed for the same pixels; on small screens the buttons
* measurably overlapped. The classic fix is the classic desktop split:
* things you do every minute stay as buttons (Run, Stop, board, Add),
* things you do a few times per session move into menus. This is those
* menus.
*
* Actions are invoked through the editorCommands registry — their real
* owners (EditorPage, FileExplorer, EditorToolbar, SimulatorCanvas)
* register handlers on mount, so nothing here duplicates logic and an
* item whose owner is not mounted renders disabled. Undo/redo read the
* canvas history from the store directly, mirroring the canvas buttons.
*
* Menubar behaviour follows the desktop convention: click opens, click
* again closes, hovering a sibling while open switches menus, Escape and
* outside clicks close.
*/
import React, { useEffect, useRef, useState, useSyncExternalStore } from 'react';
import { useTranslation } from 'react-i18next';
import { useLocation, useNavigate } from 'react-router-dom';
feat(editor): menus File/Edit en la cabecera y editor sin nav de marketing La queja: demasiados botones en una sola fila, y en pantallas pequenas se solapan — medido, no impresion: a 1024px Code/Both/Circuit pisaban a Compile/Run/Stop por 9-20px y Add pisaba el chat por 29px, con el idioma y el usuario cortados fuera del viewport. Tres piezas: 1. En el editor, el nav de marketing (Home/Docs/Pricing/...) sobra: ya estas dentro, y cuesta exactamente el ancho que le falta a la toolbar. AppHeader gana la variante editorMenu — mismo mecanismo que ya usa la build de escritorio (VITE_DESKTOP) — que oculta el nav y pinta un menu File/Edit junto al logo. El logo sigue llevando a inicio; idioma, autoguardado, Share y usuario se quedan. 2. File/Edit al estilo escritorio: lo que se usa cada minuto sigue siendo boton; lo que se usa unas veces por sesion va al menu. File: nuevo workspace, nuevo fichero, abrir, guardar, importar, exportar, BOM, imagen del esquema. Edit: undo/redo (con su estado real del historial), centrar vista, zoom. Las acciones viven en cierres de cuatro componentes distintos, asi que hay un registro id->handler (lib/editorCommands, mismo patron de seam que registerProExamples o registerBoardBuiltins): cada dueno registra al montar y el menu invoca por id; un item sin dueno montado se pinta deshabilitado, que ademas es la verdad. El registro sobrevive al remontaje StrictMode (el cleanup viejo no borra al sucesor) y tiene test de eso. 3. La toolbar adelgaza: fuera Undo/Redo (Ctrl+Z/Y siguen), fuera los botones inline de Import/Export y sus gemelos responsivos del More (~70px recuperados); el More queda solo con los extras pro (firmware, GitHub, share, grabacion). El CSS responsivo de los botones retirados se va con ellos. Suite en verde (2300) y build del overlay verificado desde este arbol.
2026-07-30 21:26:36 +07:00
import { useSimulatorStore } from '../../store/useSimulatorStore';
import { useOscilloscopeStore } from '../../store/useOscilloscopeStore';
import { LOCALES, LOCALE_META, type Locale } from '../../i18n/config';
import { getLocaleFromPath, switchLocale } from '../../i18n/path';
feat(editor): menus File/Edit en la cabecera y editor sin nav de marketing La queja: demasiados botones en una sola fila, y en pantallas pequenas se solapan — medido, no impresion: a 1024px Code/Both/Circuit pisaban a Compile/Run/Stop por 9-20px y Add pisaba el chat por 29px, con el idioma y el usuario cortados fuera del viewport. Tres piezas: 1. En el editor, el nav de marketing (Home/Docs/Pricing/...) sobra: ya estas dentro, y cuesta exactamente el ancho que le falta a la toolbar. AppHeader gana la variante editorMenu — mismo mecanismo que ya usa la build de escritorio (VITE_DESKTOP) — que oculta el nav y pinta un menu File/Edit junto al logo. El logo sigue llevando a inicio; idioma, autoguardado, Share y usuario se quedan. 2. File/Edit al estilo escritorio: lo que se usa cada minuto sigue siendo boton; lo que se usa unas veces por sesion va al menu. File: nuevo workspace, nuevo fichero, abrir, guardar, importar, exportar, BOM, imagen del esquema. Edit: undo/redo (con su estado real del historial), centrar vista, zoom. Las acciones viven en cierres de cuatro componentes distintos, asi que hay un registro id->handler (lib/editorCommands, mismo patron de seam que registerProExamples o registerBoardBuiltins): cada dueno registra al montar y el menu invoca por id; un item sin dueno montado se pinta deshabilitado, que ademas es la verdad. El registro sobrevive al remontaje StrictMode (el cleanup viejo no borra al sucesor) y tiene test de eso. 3. La toolbar adelgaza: fuera Undo/Redo (Ctrl+Z/Y siguen), fuera los botones inline de Import/Export y sus gemelos responsivos del More (~70px recuperados); el More queda solo con los extras pro (firmware, GitHub, share, grabacion). El CSS responsivo de los botones retirados se va con ellos. Suite en verde (2300) y build del overlay verificado desde este arbol.
2026-07-30 21:26:36 +07:00
import {
hasEditorCommand,
runEditorCommand,
subscribeEditorCommands,
getEditorCommandsVersion,
type EditorCommandId,
} from '../../lib/editorCommands';
import './EditorMenuBar.css';
type Item =
| { kind: 'command'; id: EditorCommandId; label: string; shortcut?: string; pro?: boolean }
| { kind: 'link'; href: string; label: string }
feat(editor): menus File/Edit en la cabecera y editor sin nav de marketing La queja: demasiados botones en una sola fila, y en pantallas pequenas se solapan — medido, no impresion: a 1024px Code/Both/Circuit pisaban a Compile/Run/Stop por 9-20px y Add pisaba el chat por 29px, con el idioma y el usuario cortados fuera del viewport. Tres piezas: 1. En el editor, el nav de marketing (Home/Docs/Pricing/...) sobra: ya estas dentro, y cuesta exactamente el ancho que le falta a la toolbar. AppHeader gana la variante editorMenu — mismo mecanismo que ya usa la build de escritorio (VITE_DESKTOP) — que oculta el nav y pinta un menu File/Edit junto al logo. El logo sigue llevando a inicio; idioma, autoguardado, Share y usuario se quedan. 2. File/Edit al estilo escritorio: lo que se usa cada minuto sigue siendo boton; lo que se usa unas veces por sesion va al menu. File: nuevo workspace, nuevo fichero, abrir, guardar, importar, exportar, BOM, imagen del esquema. Edit: undo/redo (con su estado real del historial), centrar vista, zoom. Las acciones viven en cierres de cuatro componentes distintos, asi que hay un registro id->handler (lib/editorCommands, mismo patron de seam que registerProExamples o registerBoardBuiltins): cada dueno registra al montar y el menu invoca por id; un item sin dueno montado se pinta deshabilitado, que ademas es la verdad. El registro sobrevive al remontaje StrictMode (el cleanup viejo no borra al sucesor) y tiene test de eso. 3. La toolbar adelgaza: fuera Undo/Redo (Ctrl+Z/Y siguen), fuera los botones inline de Import/Export y sus gemelos responsivos del More (~70px recuperados); el More queda solo con los extras pro (firmware, GitHub, share, grabacion). El CSS responsivo de los botones retirados se va con ellos. Suite en verde (2300) y build del overlay verificado desde este arbol.
2026-07-30 21:26:36 +07:00
| { kind: 'separator' };
// Same links the desktop app's Help menu opens (pro/desktop menu.rs) — the
// web editor mirrors that structure so both feel like one product. They
// replace the marketing nav this header no longer shows, opening in a new
// tab so the editor (and any unsaved work) stays put.
const GITHUB_URL = 'https://github.com/davidmonterocrespo24/velxio';
const DISCORD_URL = 'https://discord.gg/3mARjJrh4E';
refactor(routes): OSS is the editor; marketing moves to the overlay The open-source build now ships exactly its product surface: /editor, the examples gallery (/examples, /examples/:id) and the per-example editor (/example/:id). Landing, about, pricing, docs, the 14 keyword-targeted simulator landings and the v2/v2.5/v3 showcases move to the private overlay, registered through the same registerProRoutes seam that already carries login/admin/classroom. Root behaves per build: an overlay that registers an index route claims '/' (velxio.dev keeps its landing); otherwise '/' redirects to /editor. The redirect waits for the overlay import to settle — same contract as markProExamplesSettled — so a velxio.dev visitor is never bounced into the editor because the landing was 300ms away from registering. Prerender and sitemap follow the split: entry-server pulls the marketing page map from '@pro/pages/marketing' behind a VITE_PRO_BUILD-gated dynamic import (the proven main.tsx pattern, with an OSS stub for tsc), and generate-sitemap lists only served routes in OSS builds (2 URLs) while pro builds stay byte-identical — verified against a pre-migration baseline: sitemap (37 URLs) and prerendered /, /about, /docs, /arduino-simulator, /esp32-simulator, /v3 all identical modulo hashed asset names. OSS prerender drops exactly the 32 marketing pages (348→316). The OSS header slims down to match: Editor, Examples, GitHub, Discord — the marketing links only render in pro builds, where their routes exist. The editor Help menu links those pages absolutely (velxio.dev) in OSS, exactly like the desktop app's Help menu.
2026-07-30 22:50:14 +07:00
// In the OSS build the marketing pages live on velxio.dev, not in this app
// (the overlay registers them only in pro builds) — link absolute, exactly
// like the desktop app's Help menu does.
const SITE = import.meta.env.VITE_PRO_BUILD ? '' : 'https://velxio.dev';
feat(editor): menus File/Edit en la cabecera y editor sin nav de marketing La queja: demasiados botones en una sola fila, y en pantallas pequenas se solapan — medido, no impresion: a 1024px Code/Both/Circuit pisaban a Compile/Run/Stop por 9-20px y Add pisaba el chat por 29px, con el idioma y el usuario cortados fuera del viewport. Tres piezas: 1. En el editor, el nav de marketing (Home/Docs/Pricing/...) sobra: ya estas dentro, y cuesta exactamente el ancho que le falta a la toolbar. AppHeader gana la variante editorMenu — mismo mecanismo que ya usa la build de escritorio (VITE_DESKTOP) — que oculta el nav y pinta un menu File/Edit junto al logo. El logo sigue llevando a inicio; idioma, autoguardado, Share y usuario se quedan. 2. File/Edit al estilo escritorio: lo que se usa cada minuto sigue siendo boton; lo que se usa unas veces por sesion va al menu. File: nuevo workspace, nuevo fichero, abrir, guardar, importar, exportar, BOM, imagen del esquema. Edit: undo/redo (con su estado real del historial), centrar vista, zoom. Las acciones viven en cierres de cuatro componentes distintos, asi que hay un registro id->handler (lib/editorCommands, mismo patron de seam que registerProExamples o registerBoardBuiltins): cada dueno registra al montar y el menu invoca por id; un item sin dueno montado se pinta deshabilitado, que ademas es la verdad. El registro sobrevive al remontaje StrictMode (el cleanup viejo no borra al sucesor) y tiene test de eso. 3. La toolbar adelgaza: fuera Undo/Redo (Ctrl+Z/Y siguen), fuera los botones inline de Import/Export y sus gemelos responsivos del More (~70px recuperados); el More queda solo con los extras pro (firmware, GitHub, share, grabacion). El CSS responsivo de los botones retirados se va con ellos. Suite en verde (2300) y build del overlay verificado desde este arbol.
2026-07-30 21:26:36 +07:00
export const EditorMenuBar: React.FC = () => {
const { t } = useTranslation();
const [open, setOpen] = useState<'file' | 'edit' | 'view' | 'account' | 'help' | null>(null);
feat(editor): menus File/Edit en la cabecera y editor sin nav de marketing La queja: demasiados botones en una sola fila, y en pantallas pequenas se solapan — medido, no impresion: a 1024px Code/Both/Circuit pisaban a Compile/Run/Stop por 9-20px y Add pisaba el chat por 29px, con el idioma y el usuario cortados fuera del viewport. Tres piezas: 1. En el editor, el nav de marketing (Home/Docs/Pricing/...) sobra: ya estas dentro, y cuesta exactamente el ancho que le falta a la toolbar. AppHeader gana la variante editorMenu — mismo mecanismo que ya usa la build de escritorio (VITE_DESKTOP) — que oculta el nav y pinta un menu File/Edit junto al logo. El logo sigue llevando a inicio; idioma, autoguardado, Share y usuario se quedan. 2. File/Edit al estilo escritorio: lo que se usa cada minuto sigue siendo boton; lo que se usa unas veces por sesion va al menu. File: nuevo workspace, nuevo fichero, abrir, guardar, importar, exportar, BOM, imagen del esquema. Edit: undo/redo (con su estado real del historial), centrar vista, zoom. Las acciones viven en cierres de cuatro componentes distintos, asi que hay un registro id->handler (lib/editorCommands, mismo patron de seam que registerProExamples o registerBoardBuiltins): cada dueno registra al montar y el menu invoca por id; un item sin dueno montado se pinta deshabilitado, que ademas es la verdad. El registro sobrevive al remontaje StrictMode (el cleanup viejo no borra al sucesor) y tiene test de eso. 3. La toolbar adelgaza: fuera Undo/Redo (Ctrl+Z/Y siguen), fuera los botones inline de Import/Export y sus gemelos responsivos del More (~70px recuperados); el More queda solo con los extras pro (firmware, GitHub, share, grabacion). El CSS responsivo de los botones retirados se va con ellos. Suite en verde (2300) y build del overlay verificado desde este arbol.
2026-07-30 21:26:36 +07:00
const rootRef = useRef<HTMLDivElement>(null);
// Re-render when owners (un)register their commands.
useSyncExternalStore(subscribeEditorCommands, getEditorCommandsVersion);
const location = useLocation();
const navigate = useNavigate();
const currentLocale = getLocaleFromPath(location.pathname);
const serialOpen = useSimulatorStore((s) => s.serialMonitorOpen);
const toggleSerialMonitor = useSimulatorStore((s) => s.toggleSerialMonitor);
const scopeOpen = useOscilloscopeStore((s) => s.open);
const toggleOscilloscope = useOscilloscopeStore((s) => s.toggleOscilloscope);
feat(editor): menus File/Edit en la cabecera y editor sin nav de marketing La queja: demasiados botones en una sola fila, y en pantallas pequenas se solapan — medido, no impresion: a 1024px Code/Both/Circuit pisaban a Compile/Run/Stop por 9-20px y Add pisaba el chat por 29px, con el idioma y el usuario cortados fuera del viewport. Tres piezas: 1. En el editor, el nav de marketing (Home/Docs/Pricing/...) sobra: ya estas dentro, y cuesta exactamente el ancho que le falta a la toolbar. AppHeader gana la variante editorMenu — mismo mecanismo que ya usa la build de escritorio (VITE_DESKTOP) — que oculta el nav y pinta un menu File/Edit junto al logo. El logo sigue llevando a inicio; idioma, autoguardado, Share y usuario se quedan. 2. File/Edit al estilo escritorio: lo que se usa cada minuto sigue siendo boton; lo que se usa unas veces por sesion va al menu. File: nuevo workspace, nuevo fichero, abrir, guardar, importar, exportar, BOM, imagen del esquema. Edit: undo/redo (con su estado real del historial), centrar vista, zoom. Las acciones viven en cierres de cuatro componentes distintos, asi que hay un registro id->handler (lib/editorCommands, mismo patron de seam que registerProExamples o registerBoardBuiltins): cada dueno registra al montar y el menu invoca por id; un item sin dueno montado se pinta deshabilitado, que ademas es la verdad. El registro sobrevive al remontaje StrictMode (el cleanup viejo no borra al sucesor) y tiene test de eso. 3. La toolbar adelgaza: fuera Undo/Redo (Ctrl+Z/Y siguen), fuera los botones inline de Import/Export y sus gemelos responsivos del More (~70px recuperados); el More queda solo con los extras pro (firmware, GitHub, share, grabacion). El CSS responsivo de los botones retirados se va con ellos. Suite en verde (2300) y build del overlay verificado desde este arbol.
2026-07-30 21:26:36 +07:00
const undo = useSimulatorStore((s) => s.undo);
const redo = useSimulatorStore((s) => s.redo);
const history = useSimulatorStore((s) => s.history);
const historyIndex = useSimulatorStore((s) => s.historyIndex);
useEffect(() => {
if (!open) return;
const onDown = (e: MouseEvent): void => {
if (!rootRef.current?.contains(e.target as Node)) setOpen(null);
};
const onKey = (e: KeyboardEvent): void => {
if (e.key === 'Escape') setOpen(null);
};
document.addEventListener('mousedown', onDown);
document.addEventListener('keydown', onKey);
return () => {
document.removeEventListener('mousedown', onDown);
document.removeEventListener('keydown', onKey);
};
}, [open]);
const fileItems: Item[] = [
{ kind: 'command', id: 'project.new', label: t('editor.menu.newProject', 'New workspace') },
{ kind: 'command', id: 'file.new', label: t('editor.menu.newFile', 'New file') },
{ kind: 'separator' },
{ kind: 'command', id: 'project.open', label: t('editor.menu.open', 'Open project…') },
{
kind: 'command',
id: 'project.save',
label: t('editor.menu.save', 'Save project'),
shortcut: 'Ctrl+S',
},
{ kind: 'separator' },
{ kind: 'command', id: 'project.import', label: t('editor.toolbar.importLabel', 'Import project') },
{ kind: 'command', id: 'project.export', label: t('editor.toolbar.exportLabel', 'Export project (.zip)') },
{ kind: 'command', id: 'project.exportBom', label: t('editor.toolbar.exportBomLabel', 'Bill of Materials (CSV)'), pro: true },
feat(editor): menus File/Edit en la cabecera y editor sin nav de marketing La queja: demasiados botones en una sola fila, y en pantallas pequenas se solapan — medido, no impresion: a 1024px Code/Both/Circuit pisaban a Compile/Run/Stop por 9-20px y Add pisaba el chat por 29px, con el idioma y el usuario cortados fuera del viewport. Tres piezas: 1. En el editor, el nav de marketing (Home/Docs/Pricing/...) sobra: ya estas dentro, y cuesta exactamente el ancho que le falta a la toolbar. AppHeader gana la variante editorMenu — mismo mecanismo que ya usa la build de escritorio (VITE_DESKTOP) — que oculta el nav y pinta un menu File/Edit junto al logo. El logo sigue llevando a inicio; idioma, autoguardado, Share y usuario se quedan. 2. File/Edit al estilo escritorio: lo que se usa cada minuto sigue siendo boton; lo que se usa unas veces por sesion va al menu. File: nuevo workspace, nuevo fichero, abrir, guardar, importar, exportar, BOM, imagen del esquema. Edit: undo/redo (con su estado real del historial), centrar vista, zoom. Las acciones viven en cierres de cuatro componentes distintos, asi que hay un registro id->handler (lib/editorCommands, mismo patron de seam que registerProExamples o registerBoardBuiltins): cada dueno registra al montar y el menu invoca por id; un item sin dueno montado se pinta deshabilitado, que ademas es la verdad. El registro sobrevive al remontaje StrictMode (el cleanup viejo no borra al sucesor) y tiene test de eso. 3. La toolbar adelgaza: fuera Undo/Redo (Ctrl+Z/Y siguen), fuera los botones inline de Import/Export y sus gemelos responsivos del More (~70px recuperados); el More queda solo con los extras pro (firmware, GitHub, share, grabacion). El CSS responsivo de los botones retirados se va con ellos. Suite en verde (2300) y build del overlay verificado desde este arbol.
2026-07-30 21:26:36 +07:00
{
kind: 'command',
id: 'project.exportScreenshot',
label: t('editor.toolbar.exportScreenshotLabel', 'Schematic image (PNG)'),
pro: true,
feat(editor): menus File/Edit en la cabecera y editor sin nav de marketing La queja: demasiados botones en una sola fila, y en pantallas pequenas se solapan — medido, no impresion: a 1024px Code/Both/Circuit pisaban a Compile/Run/Stop por 9-20px y Add pisaba el chat por 29px, con el idioma y el usuario cortados fuera del viewport. Tres piezas: 1. En el editor, el nav de marketing (Home/Docs/Pricing/...) sobra: ya estas dentro, y cuesta exactamente el ancho que le falta a la toolbar. AppHeader gana la variante editorMenu — mismo mecanismo que ya usa la build de escritorio (VITE_DESKTOP) — que oculta el nav y pinta un menu File/Edit junto al logo. El logo sigue llevando a inicio; idioma, autoguardado, Share y usuario se quedan. 2. File/Edit al estilo escritorio: lo que se usa cada minuto sigue siendo boton; lo que se usa unas veces por sesion va al menu. File: nuevo workspace, nuevo fichero, abrir, guardar, importar, exportar, BOM, imagen del esquema. Edit: undo/redo (con su estado real del historial), centrar vista, zoom. Las acciones viven en cierres de cuatro componentes distintos, asi que hay un registro id->handler (lib/editorCommands, mismo patron de seam que registerProExamples o registerBoardBuiltins): cada dueno registra al montar y el menu invoca por id; un item sin dueno montado se pinta deshabilitado, que ademas es la verdad. El registro sobrevive al remontaje StrictMode (el cleanup viejo no borra al sucesor) y tiene test de eso. 3. La toolbar adelgaza: fuera Undo/Redo (Ctrl+Z/Y siguen), fuera los botones inline de Import/Export y sus gemelos responsivos del More (~70px recuperados); el More queda solo con los extras pro (firmware, GitHub, share, grabacion). El CSS responsivo de los botones retirados se va con ellos. Suite en verde (2300) y build del overlay verificado desde este arbol.
2026-07-30 21:26:36 +07:00
},
{ kind: 'separator' },
// The toolbar's "..." menu folded in here — same actions, same PRO
// pills, one button fewer in the strip.
{ kind: 'command', id: 'project.share', label: t('editor.toolbar.shareLabel', 'Share / Embed') },
{ kind: 'command', id: 'project.githubSync', label: t('editor.toolbar.githubSyncLabel', 'Sync to GitHub'), pro: true },
{ kind: 'command', id: 'firmware.upload', label: t('editor.toolbar.uploadFirmwareLabel', 'Upload firmware') },
{ kind: 'command', id: 'sim.record', label: t('editor.toolbar.recordLabel', 'Record simulation'), pro: true },
feat(editor): menus File/Edit en la cabecera y editor sin nav de marketing La queja: demasiados botones en una sola fila, y en pantallas pequenas se solapan — medido, no impresion: a 1024px Code/Both/Circuit pisaban a Compile/Run/Stop por 9-20px y Add pisaba el chat por 29px, con el idioma y el usuario cortados fuera del viewport. Tres piezas: 1. En el editor, el nav de marketing (Home/Docs/Pricing/...) sobra: ya estas dentro, y cuesta exactamente el ancho que le falta a la toolbar. AppHeader gana la variante editorMenu — mismo mecanismo que ya usa la build de escritorio (VITE_DESKTOP) — que oculta el nav y pinta un menu File/Edit junto al logo. El logo sigue llevando a inicio; idioma, autoguardado, Share y usuario se quedan. 2. File/Edit al estilo escritorio: lo que se usa cada minuto sigue siendo boton; lo que se usa unas veces por sesion va al menu. File: nuevo workspace, nuevo fichero, abrir, guardar, importar, exportar, BOM, imagen del esquema. Edit: undo/redo (con su estado real del historial), centrar vista, zoom. Las acciones viven en cierres de cuatro componentes distintos, asi que hay un registro id->handler (lib/editorCommands, mismo patron de seam que registerProExamples o registerBoardBuiltins): cada dueno registra al montar y el menu invoca por id; un item sin dueno montado se pinta deshabilitado, que ademas es la verdad. El registro sobrevive al remontaje StrictMode (el cleanup viejo no borra al sucesor) y tiene test de eso. 3. La toolbar adelgaza: fuera Undo/Redo (Ctrl+Z/Y siguen), fuera los botones inline de Import/Export y sus gemelos responsivos del More (~70px recuperados); el More queda solo con los extras pro (firmware, GitHub, share, grabacion). El CSS responsivo de los botones retirados se va con ellos. Suite en verde (2300) y build del overlay verificado desde este arbol.
2026-07-30 21:26:36 +07:00
];
const helpItems: Item[] = [
refactor(routes): OSS is the editor; marketing moves to the overlay The open-source build now ships exactly its product surface: /editor, the examples gallery (/examples, /examples/:id) and the per-example editor (/example/:id). Landing, about, pricing, docs, the 14 keyword-targeted simulator landings and the v2/v2.5/v3 showcases move to the private overlay, registered through the same registerProRoutes seam that already carries login/admin/classroom. Root behaves per build: an overlay that registers an index route claims '/' (velxio.dev keeps its landing); otherwise '/' redirects to /editor. The redirect waits for the overlay import to settle — same contract as markProExamplesSettled — so a velxio.dev visitor is never bounced into the editor because the landing was 300ms away from registering. Prerender and sitemap follow the split: entry-server pulls the marketing page map from '@pro/pages/marketing' behind a VITE_PRO_BUILD-gated dynamic import (the proven main.tsx pattern, with an OSS stub for tsc), and generate-sitemap lists only served routes in OSS builds (2 URLs) while pro builds stay byte-identical — verified against a pre-migration baseline: sitemap (37 URLs) and prerendered /, /about, /docs, /arduino-simulator, /esp32-simulator, /v3 all identical modulo hashed asset names. OSS prerender drops exactly the 32 marketing pages (348→316). The OSS header slims down to match: Editor, Examples, GitHub, Discord — the marketing links only render in pro builds, where their routes exist. The editor Help menu links those pages absolutely (velxio.dev) in OSS, exactly like the desktop app's Help menu.
2026-07-30 22:50:14 +07:00
{ kind: 'link', href: `${SITE}/docs`, label: t('header.nav.documentation', 'Documentation') },
{ kind: 'link', href: '/examples', label: t('header.nav.examples', 'Examples') },
refactor(routes): OSS is the editor; marketing moves to the overlay The open-source build now ships exactly its product surface: /editor, the examples gallery (/examples, /examples/:id) and the per-example editor (/example/:id). Landing, about, pricing, docs, the 14 keyword-targeted simulator landings and the v2/v2.5/v3 showcases move to the private overlay, registered through the same registerProRoutes seam that already carries login/admin/classroom. Root behaves per build: an overlay that registers an index route claims '/' (velxio.dev keeps its landing); otherwise '/' redirects to /editor. The redirect waits for the overlay import to settle — same contract as markProExamplesSettled — so a velxio.dev visitor is never bounced into the editor because the landing was 300ms away from registering. Prerender and sitemap follow the split: entry-server pulls the marketing page map from '@pro/pages/marketing' behind a VITE_PRO_BUILD-gated dynamic import (the proven main.tsx pattern, with an OSS stub for tsc), and generate-sitemap lists only served routes in OSS builds (2 URLs) while pro builds stay byte-identical — verified against a pre-migration baseline: sitemap (37 URLs) and prerendered /, /about, /docs, /arduino-simulator, /esp32-simulator, /v3 all identical modulo hashed asset names. OSS prerender drops exactly the 32 marketing pages (348→316). The OSS header slims down to match: Editor, Examples, GitHub, Discord — the marketing links only render in pro builds, where their routes exist. The editor Help menu links those pages absolutely (velxio.dev) in OSS, exactly like the desktop app's Help menu.
2026-07-30 22:50:14 +07:00
{ kind: 'link', href: `${SITE}/pricing`, label: t('header.nav.pricing', 'Pricing') },
{ kind: 'separator' },
refactor(routes): OSS is the editor; marketing moves to the overlay The open-source build now ships exactly its product surface: /editor, the examples gallery (/examples, /examples/:id) and the per-example editor (/example/:id). Landing, about, pricing, docs, the 14 keyword-targeted simulator landings and the v2/v2.5/v3 showcases move to the private overlay, registered through the same registerProRoutes seam that already carries login/admin/classroom. Root behaves per build: an overlay that registers an index route claims '/' (velxio.dev keeps its landing); otherwise '/' redirects to /editor. The redirect waits for the overlay import to settle — same contract as markProExamplesSettled — so a velxio.dev visitor is never bounced into the editor because the landing was 300ms away from registering. Prerender and sitemap follow the split: entry-server pulls the marketing page map from '@pro/pages/marketing' behind a VITE_PRO_BUILD-gated dynamic import (the proven main.tsx pattern, with an OSS stub for tsc), and generate-sitemap lists only served routes in OSS builds (2 URLs) while pro builds stay byte-identical — verified against a pre-migration baseline: sitemap (37 URLs) and prerendered /, /about, /docs, /arduino-simulator, /esp32-simulator, /v3 all identical modulo hashed asset names. OSS prerender drops exactly the 32 marketing pages (348→316). The OSS header slims down to match: Editor, Examples, GitHub, Discord — the marketing links only render in pro builds, where their routes exist. The editor Help menu links those pages absolutely (velxio.dev) in OSS, exactly like the desktop app's Help menu.
2026-07-30 22:50:14 +07:00
{ kind: 'link', href: SITE || '/', label: t('editor.menu.home', 'Velxio Home') },
{ kind: 'link', href: `${SITE}/blog/`, label: t('header.nav.blog', 'Blog') },
{ kind: 'link', href: `${SITE}/about`, label: t('editor.menu.about', 'About Velxio') },
{ kind: 'separator' },
{ kind: 'link', href: DISCORD_URL, label: t('editor.menu.discord', 'Discord Community') },
{ kind: 'link', href: GITHUB_URL, label: t('editor.menu.github', 'GitHub Repository') },
];
// Edit is undo/redo only (they render specially, with live history state);
// everything view-shaped lives in the View menu, like the desktop app.
const editItems: Item[] = [];
const viewItems: Item[] = [
{ kind: 'command', id: 'sim.compile', label: t('editor.menu.compile', 'Compile'), shortcut: 'Ctrl+B' },
{ kind: 'command', id: 'sim.run', label: t('editor.menu.run', 'Run') },
{ kind: 'command', id: 'sim.stop', label: t('editor.toolbar.stop', 'Stop') },
{ kind: 'command', id: 'sim.resetBoard', label: t('editor.toolbar.reset', 'Reset') },
{ kind: 'separator' },
{ kind: 'command', id: 'view.toggleExplorer', label: t('editor.menu.toggleExplorer', 'File Explorer') },
{ kind: 'command', id: 'view.toggleConsole', label: t('editor.menu.toggleConsole', 'Output Console') },
{ kind: 'separator' },
feat(editor): menus File/Edit en la cabecera y editor sin nav de marketing La queja: demasiados botones en una sola fila, y en pantallas pequenas se solapan — medido, no impresion: a 1024px Code/Both/Circuit pisaban a Compile/Run/Stop por 9-20px y Add pisaba el chat por 29px, con el idioma y el usuario cortados fuera del viewport. Tres piezas: 1. En el editor, el nav de marketing (Home/Docs/Pricing/...) sobra: ya estas dentro, y cuesta exactamente el ancho que le falta a la toolbar. AppHeader gana la variante editorMenu — mismo mecanismo que ya usa la build de escritorio (VITE_DESKTOP) — que oculta el nav y pinta un menu File/Edit junto al logo. El logo sigue llevando a inicio; idioma, autoguardado, Share y usuario se quedan. 2. File/Edit al estilo escritorio: lo que se usa cada minuto sigue siendo boton; lo que se usa unas veces por sesion va al menu. File: nuevo workspace, nuevo fichero, abrir, guardar, importar, exportar, BOM, imagen del esquema. Edit: undo/redo (con su estado real del historial), centrar vista, zoom. Las acciones viven en cierres de cuatro componentes distintos, asi que hay un registro id->handler (lib/editorCommands, mismo patron de seam que registerProExamples o registerBoardBuiltins): cada dueno registra al montar y el menu invoca por id; un item sin dueno montado se pinta deshabilitado, que ademas es la verdad. El registro sobrevive al remontaje StrictMode (el cleanup viejo no borra al sucesor) y tiene test de eso. 3. La toolbar adelgaza: fuera Undo/Redo (Ctrl+Z/Y siguen), fuera los botones inline de Import/Export y sus gemelos responsivos del More (~70px recuperados); el More queda solo con los extras pro (firmware, GitHub, share, grabacion). El CSS responsivo de los botones retirados se va con ellos. Suite en verde (2300) y build del overlay verificado desde este arbol.
2026-07-30 21:26:36 +07:00
{ kind: 'command', id: 'view.reset', label: t('editor.menu.centerView', 'Center canvas view') },
{ kind: 'command', id: 'view.zoomIn', label: t('editor.canvas.zoomIn', 'Zoom in') },
{ kind: 'command', id: 'view.zoomOut', label: t('editor.canvas.zoomOut', 'Zoom out') },
];
const renderLink = (item: Extract<Item, { kind: 'link' }>): React.ReactNode => (
<a
key={item.href}
role="menuitem"
className="emb-item"
href={item.href}
target="_blank"
rel="noopener noreferrer"
onClick={() => setOpen(null)}
>
<span>{item.label}</span>
</a>
);
feat(editor): menus File/Edit en la cabecera y editor sin nav de marketing La queja: demasiados botones en una sola fila, y en pantallas pequenas se solapan — medido, no impresion: a 1024px Code/Both/Circuit pisaban a Compile/Run/Stop por 9-20px y Add pisaba el chat por 29px, con el idioma y el usuario cortados fuera del viewport. Tres piezas: 1. En el editor, el nav de marketing (Home/Docs/Pricing/...) sobra: ya estas dentro, y cuesta exactamente el ancho que le falta a la toolbar. AppHeader gana la variante editorMenu — mismo mecanismo que ya usa la build de escritorio (VITE_DESKTOP) — que oculta el nav y pinta un menu File/Edit junto al logo. El logo sigue llevando a inicio; idioma, autoguardado, Share y usuario se quedan. 2. File/Edit al estilo escritorio: lo que se usa cada minuto sigue siendo boton; lo que se usa unas veces por sesion va al menu. File: nuevo workspace, nuevo fichero, abrir, guardar, importar, exportar, BOM, imagen del esquema. Edit: undo/redo (con su estado real del historial), centrar vista, zoom. Las acciones viven en cierres de cuatro componentes distintos, asi que hay un registro id->handler (lib/editorCommands, mismo patron de seam que registerProExamples o registerBoardBuiltins): cada dueno registra al montar y el menu invoca por id; un item sin dueno montado se pinta deshabilitado, que ademas es la verdad. El registro sobrevive al remontaje StrictMode (el cleanup viejo no borra al sucesor) y tiene test de eso. 3. La toolbar adelgaza: fuera Undo/Redo (Ctrl+Z/Y siguen), fuera los botones inline de Import/Export y sus gemelos responsivos del More (~70px recuperados); el More queda solo con los extras pro (firmware, GitHub, share, grabacion). El CSS responsivo de los botones retirados se va con ellos. Suite en verde (2300) y build del overlay verificado desde este arbol.
2026-07-30 21:26:36 +07:00
const renderCommand = (item: Extract<Item, { kind: 'command' }>): React.ReactNode => (
<button
key={item.id}
role="menuitem"
className="emb-item"
disabled={!hasEditorCommand(item.id)}
onClick={() => {
setOpen(null);
runEditorCommand(item.id);
}}
>
<span>
{item.label}
{item.pro && <span className="emb-pro">PRO</span>}
</span>
feat(editor): menus File/Edit en la cabecera y editor sin nav de marketing La queja: demasiados botones en una sola fila, y en pantallas pequenas se solapan — medido, no impresion: a 1024px Code/Both/Circuit pisaban a Compile/Run/Stop por 9-20px y Add pisaba el chat por 29px, con el idioma y el usuario cortados fuera del viewport. Tres piezas: 1. En el editor, el nav de marketing (Home/Docs/Pricing/...) sobra: ya estas dentro, y cuesta exactamente el ancho que le falta a la toolbar. AppHeader gana la variante editorMenu — mismo mecanismo que ya usa la build de escritorio (VITE_DESKTOP) — que oculta el nav y pinta un menu File/Edit junto al logo. El logo sigue llevando a inicio; idioma, autoguardado, Share y usuario se quedan. 2. File/Edit al estilo escritorio: lo que se usa cada minuto sigue siendo boton; lo que se usa unas veces por sesion va al menu. File: nuevo workspace, nuevo fichero, abrir, guardar, importar, exportar, BOM, imagen del esquema. Edit: undo/redo (con su estado real del historial), centrar vista, zoom. Las acciones viven en cierres de cuatro componentes distintos, asi que hay un registro id->handler (lib/editorCommands, mismo patron de seam que registerProExamples o registerBoardBuiltins): cada dueno registra al montar y el menu invoca por id; un item sin dueno montado se pinta deshabilitado, que ademas es la verdad. El registro sobrevive al remontaje StrictMode (el cleanup viejo no borra al sucesor) y tiene test de eso. 3. La toolbar adelgaza: fuera Undo/Redo (Ctrl+Z/Y siguen), fuera los botones inline de Import/Export y sus gemelos responsivos del More (~70px recuperados); el More queda solo con los extras pro (firmware, GitHub, share, grabacion). El CSS responsivo de los botones retirados se va con ellos. Suite en verde (2300) y build del overlay verificado desde este arbol.
2026-07-30 21:26:36 +07:00
{item.shortcut && <span className="emb-shortcut">{item.shortcut}</span>}
</button>
);
const menu = (which: 'file' | 'edit' | 'view' | 'account' | 'help', label: string, items: Item[]): React.ReactNode => (
feat(editor): menus File/Edit en la cabecera y editor sin nav de marketing La queja: demasiados botones en una sola fila, y en pantallas pequenas se solapan — medido, no impresion: a 1024px Code/Both/Circuit pisaban a Compile/Run/Stop por 9-20px y Add pisaba el chat por 29px, con el idioma y el usuario cortados fuera del viewport. Tres piezas: 1. En el editor, el nav de marketing (Home/Docs/Pricing/...) sobra: ya estas dentro, y cuesta exactamente el ancho que le falta a la toolbar. AppHeader gana la variante editorMenu — mismo mecanismo que ya usa la build de escritorio (VITE_DESKTOP) — que oculta el nav y pinta un menu File/Edit junto al logo. El logo sigue llevando a inicio; idioma, autoguardado, Share y usuario se quedan. 2. File/Edit al estilo escritorio: lo que se usa cada minuto sigue siendo boton; lo que se usa unas veces por sesion va al menu. File: nuevo workspace, nuevo fichero, abrir, guardar, importar, exportar, BOM, imagen del esquema. Edit: undo/redo (con su estado real del historial), centrar vista, zoom. Las acciones viven en cierres de cuatro componentes distintos, asi que hay un registro id->handler (lib/editorCommands, mismo patron de seam que registerProExamples o registerBoardBuiltins): cada dueno registra al montar y el menu invoca por id; un item sin dueno montado se pinta deshabilitado, que ademas es la verdad. El registro sobrevive al remontaje StrictMode (el cleanup viejo no borra al sucesor) y tiene test de eso. 3. La toolbar adelgaza: fuera Undo/Redo (Ctrl+Z/Y siguen), fuera los botones inline de Import/Export y sus gemelos responsivos del More (~70px recuperados); el More queda solo con los extras pro (firmware, GitHub, share, grabacion). El CSS responsivo de los botones retirados se va con ellos. Suite en verde (2300) y build del overlay verificado desde este arbol.
2026-07-30 21:26:36 +07:00
<div className="emb-root" key={which}>
<button
className={`emb-trigger${open === which ? ' emb-trigger-open' : ''}`}
aria-haspopup="menu"
aria-expanded={open === which}
onClick={() => setOpen((cur) => (cur === which ? null : which))}
onMouseEnter={() => setOpen((cur) => (cur && cur !== which ? which : cur))}
>
{label}
</button>
{open === which && (
<div className="emb-menu" role="menu">
{which === 'view' && (
<>
<button
role="menuitemcheckbox"
aria-checked={serialOpen}
className="emb-item"
onClick={() => {
setOpen(null);
toggleSerialMonitor();
}}
>
<span>{t('editor.canvas.toggleSerialMonitor', 'Serial Monitor')}</span>
<span className="emb-shortcut">{serialOpen ? '✓' : ''}</span>
</button>
<button
role="menuitemcheckbox"
aria-checked={scopeOpen}
className="emb-item"
onClick={() => {
setOpen(null);
toggleOscilloscope();
}}
>
<span>{t('editor.menu.toggleScope', 'Oscilloscope / Logic Analyzer')}</span>
<span className="emb-shortcut">{scopeOpen ? '✓' : ''}</span>
</button>
<div className="emb-separator" />
</>
)}
{which === 'account' && (
<>
{/* Pro account items (PRO badge, Subscribe / Manage
subscription, licenses, history, replays, Privacy) mount
here via the SAME user-menu slot the bottom-left account
dropdown uses — one overlay renderer, two hosts. Clicking
any of them closes this menu (they navigate or open their
own modal). Empty in OSS builds. */}
<div
data-velxio-slot="user-menu"
style={{ display: 'contents' }}
onClick={() => setOpen(null)}
/>
<div className="emb-separator" />
{/* Language moved in here from its own top-level menu — the
menubar was one trigger too wide once the AI chat docks. */}
<div className="emb-section-label">
{t('editor.menu.language', 'Language')}
</div>
{LOCALES.map((loc) => (
<button
key={loc}
role="menuitemradio"
aria-checked={currentLocale === loc}
className="emb-item"
onClick={() => {
setOpen(null);
if (loc === currentLocale) return;
navigate(
switchLocale(location.pathname, loc as Locale) +
location.search +
location.hash,
);
}}
>
<span>{LOCALE_META[loc].nativeName}</span>
<span className="emb-shortcut">{currentLocale === loc ? '✓' : ''}</span>
</button>
))}
</>
)}
feat(editor): menus File/Edit en la cabecera y editor sin nav de marketing La queja: demasiados botones en una sola fila, y en pantallas pequenas se solapan — medido, no impresion: a 1024px Code/Both/Circuit pisaban a Compile/Run/Stop por 9-20px y Add pisaba el chat por 29px, con el idioma y el usuario cortados fuera del viewport. Tres piezas: 1. En el editor, el nav de marketing (Home/Docs/Pricing/...) sobra: ya estas dentro, y cuesta exactamente el ancho que le falta a la toolbar. AppHeader gana la variante editorMenu — mismo mecanismo que ya usa la build de escritorio (VITE_DESKTOP) — que oculta el nav y pinta un menu File/Edit junto al logo. El logo sigue llevando a inicio; idioma, autoguardado, Share y usuario se quedan. 2. File/Edit al estilo escritorio: lo que se usa cada minuto sigue siendo boton; lo que se usa unas veces por sesion va al menu. File: nuevo workspace, nuevo fichero, abrir, guardar, importar, exportar, BOM, imagen del esquema. Edit: undo/redo (con su estado real del historial), centrar vista, zoom. Las acciones viven en cierres de cuatro componentes distintos, asi que hay un registro id->handler (lib/editorCommands, mismo patron de seam que registerProExamples o registerBoardBuiltins): cada dueno registra al montar y el menu invoca por id; un item sin dueno montado se pinta deshabilitado, que ademas es la verdad. El registro sobrevive al remontaje StrictMode (el cleanup viejo no borra al sucesor) y tiene test de eso. 3. La toolbar adelgaza: fuera Undo/Redo (Ctrl+Z/Y siguen), fuera los botones inline de Import/Export y sus gemelos responsivos del More (~70px recuperados); el More queda solo con los extras pro (firmware, GitHub, share, grabacion). El CSS responsivo de los botones retirados se va con ellos. Suite en verde (2300) y build del overlay verificado desde este arbol.
2026-07-30 21:26:36 +07:00
{which === 'edit' && (
<>
<button
role="menuitem"
className="emb-item"
disabled={historyIndex < 0}
onClick={() => {
setOpen(null);
undo();
}}
>
<span>{t('editor.menu.undo', 'Undo')}</span>
<span className="emb-shortcut">Ctrl+Z</span>
</button>
<button
role="menuitem"
className="emb-item"
disabled={historyIndex >= history.length - 1}
onClick={() => {
setOpen(null);
redo();
}}
>
<span>{t('editor.menu.redo', 'Redo')}</span>
<span className="emb-shortcut">Ctrl+Y</span>
</button>
</>
)}
{items.map((item, i) =>
item.kind === 'separator' ? (
<div key={`sep-${i}`} className="emb-separator" />
) : item.kind === 'link' ? (
renderLink(item)
feat(editor): menus File/Edit en la cabecera y editor sin nav de marketing La queja: demasiados botones en una sola fila, y en pantallas pequenas se solapan — medido, no impresion: a 1024px Code/Both/Circuit pisaban a Compile/Run/Stop por 9-20px y Add pisaba el chat por 29px, con el idioma y el usuario cortados fuera del viewport. Tres piezas: 1. En el editor, el nav de marketing (Home/Docs/Pricing/...) sobra: ya estas dentro, y cuesta exactamente el ancho que le falta a la toolbar. AppHeader gana la variante editorMenu — mismo mecanismo que ya usa la build de escritorio (VITE_DESKTOP) — que oculta el nav y pinta un menu File/Edit junto al logo. El logo sigue llevando a inicio; idioma, autoguardado, Share y usuario se quedan. 2. File/Edit al estilo escritorio: lo que se usa cada minuto sigue siendo boton; lo que se usa unas veces por sesion va al menu. File: nuevo workspace, nuevo fichero, abrir, guardar, importar, exportar, BOM, imagen del esquema. Edit: undo/redo (con su estado real del historial), centrar vista, zoom. Las acciones viven en cierres de cuatro componentes distintos, asi que hay un registro id->handler (lib/editorCommands, mismo patron de seam que registerProExamples o registerBoardBuiltins): cada dueno registra al montar y el menu invoca por id; un item sin dueno montado se pinta deshabilitado, que ademas es la verdad. El registro sobrevive al remontaje StrictMode (el cleanup viejo no borra al sucesor) y tiene test de eso. 3. La toolbar adelgaza: fuera Undo/Redo (Ctrl+Z/Y siguen), fuera los botones inline de Import/Export y sus gemelos responsivos del More (~70px recuperados); el More queda solo con los extras pro (firmware, GitHub, share, grabacion). El CSS responsivo de los botones retirados se va con ellos. Suite en verde (2300) y build del overlay verificado desde este arbol.
2026-07-30 21:26:36 +07:00
) : (
renderCommand(item)
),
)}
</div>
)}
</div>
);
return (
<div className="editor-menubar" ref={rootRef}>
{menu('file', t('editor.menu.file', 'File'), fileItems)}
{menu('edit', t('editor.menu.edit', 'Edit'), editItems)}
{menu('view', t('editor.menu.view', 'View'), viewItems)}
{menu('account', t('editor.menu.account', 'Account'), [])}
{menu('help', t('editor.menu.help', 'Help'), helpItems)}
feat(editor): menus File/Edit en la cabecera y editor sin nav de marketing La queja: demasiados botones en una sola fila, y en pantallas pequenas se solapan — medido, no impresion: a 1024px Code/Both/Circuit pisaban a Compile/Run/Stop por 9-20px y Add pisaba el chat por 29px, con el idioma y el usuario cortados fuera del viewport. Tres piezas: 1. En el editor, el nav de marketing (Home/Docs/Pricing/...) sobra: ya estas dentro, y cuesta exactamente el ancho que le falta a la toolbar. AppHeader gana la variante editorMenu — mismo mecanismo que ya usa la build de escritorio (VITE_DESKTOP) — que oculta el nav y pinta un menu File/Edit junto al logo. El logo sigue llevando a inicio; idioma, autoguardado, Share y usuario se quedan. 2. File/Edit al estilo escritorio: lo que se usa cada minuto sigue siendo boton; lo que se usa unas veces por sesion va al menu. File: nuevo workspace, nuevo fichero, abrir, guardar, importar, exportar, BOM, imagen del esquema. Edit: undo/redo (con su estado real del historial), centrar vista, zoom. Las acciones viven en cierres de cuatro componentes distintos, asi que hay un registro id->handler (lib/editorCommands, mismo patron de seam que registerProExamples o registerBoardBuiltins): cada dueno registra al montar y el menu invoca por id; un item sin dueno montado se pinta deshabilitado, que ademas es la verdad. El registro sobrevive al remontaje StrictMode (el cleanup viejo no borra al sucesor) y tiene test de eso. 3. La toolbar adelgaza: fuera Undo/Redo (Ctrl+Z/Y siguen), fuera los botones inline de Import/Export y sus gemelos responsivos del More (~70px recuperados); el More queda solo con los extras pro (firmware, GitHub, share, grabacion). El CSS responsivo de los botones retirados se va con ellos. Suite en verde (2300) y build del overlay verificado desde este arbol.
2026-07-30 21:26:36 +07:00
</div>
);
};