velxio/frontend/src/lib/editorCommands.ts

81 lines
2.4 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
/**
* editorCommands — a tiny id → handler registry for the editor menu bar.
*
* The actions the File/Edit menus expose (save, open, export, reset the
* canvas view…) already exist, but they live inside the closures of four
* different components: EditorPage owns save/new, FileExplorer owns open,
* EditorToolbar owns import/export, SimulatorCanvas owns the view. A menu
* mounted in the app header can reach none of them by props without
* threading callbacks through the whole page tree.
*
* So the owners register their handler under a stable id on mount (and
* unregister on unmount), and the menu invokes by id — the same
* registration-seam pattern the codebase already uses for pro examples,
* guest setups and board builtins. A menu item whose command is not
* currently registered renders disabled, which is also the truth: with no
* canvas mounted there is nothing to reset.
*/
export type EditorCommandId =
| 'project.new'
| 'project.save'
| 'project.open'
| 'project.import'
| 'project.export'
| 'project.exportBom'
| 'project.exportScreenshot'
| 'file.new'
| 'project.share'
| 'project.githubSync'
| 'firmware.upload'
| 'sim.record'
| 'sim.compile'
| 'sim.run'
| 'sim.stop'
| 'sim.resetBoard'
| 'view.toggleExplorer'
| 'view.toggleConsole'
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
| 'view.reset'
| 'view.zoomIn'
| 'view.zoomOut';
const handlers = new Map<EditorCommandId, () => void>();
// Menus re-render on registration changes so items enable/disable live.
let version = 0;
const listeners = new Set<() => void>();
export function registerEditorCommand(id: EditorCommandId, run: () => void): () => void {
handlers.set(id, run);
version++;
for (const l of listeners) l();
return () => {
// Only clear if we are still the registered handler — a remount
// registers the new closure BEFORE the old effect's cleanup runs.
if (handlers.get(id) === run) {
handlers.delete(id);
version++;
for (const l of listeners) l();
}
};
}
export function hasEditorCommand(id: EditorCommandId): boolean {
return handlers.has(id);
}
export function runEditorCommand(id: EditorCommandId): void {
handlers.get(id)?.();
}
export function subscribeEditorCommands(cb: () => void): () => void {
listeners.add(cb);
return () => {
listeners.delete(cb);
};
}
export function getEditorCommandsVersion(): number {
return version;
}