diff --git a/backend/app/main.py b/backend/app/main.py index b7112cb9..b059dba9 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -112,6 +112,13 @@ app.include_router(flash.router, prefix="/api/flash", tags=["flash"]) # them out of upstream entirely — they now live under the private overlay's # pro/backend/app/api/routes/ and are registered by register_pro(app) # below. The OSS image carries none of them: anonymous, stateless. +try: + from app.api.routes.auth import router as auth_router + from app.api.routes.projects import router as projects_router + app.include_router(auth_router, prefix="/api/auth", tags=["auth"]) + app.include_router(projects_router, prefix="/api", tags=["projects"]) +except ImportError: + pass # WebSockets from app.api.routes import simulation diff --git a/frontend/public/sitemap.xml b/frontend/public/sitemap.xml index a04dc784..58c2ee21 100644 --- a/frontend/public/sitemap.xml +++ b/frontend/public/sitemap.xml @@ -113,6 +113,11 @@ monthly 0.6 + + https://velxio.dev/examples/esp32-micropython-external-library/ + monthly + 0.6 + https://velxio.dev/examples/esp32-idf-blink/ monthly diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 8c930599..3f50bae1 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -5,6 +5,7 @@ import './index.css'; // useTranslation() always resolves against a live instance. Must come // before App. import './i18n'; +import './services/EmbedBridge'; import { markProExamplesSettled } from './data/examples'; import { markProRoutesSettled } from './lib/proRoutes'; diff --git a/frontend/src/pages/EditorPage.tsx b/frontend/src/pages/EditorPage.tsx index bb097c16..c8cf8760 100644 --- a/frontend/src/pages/EditorPage.tsx +++ b/frontend/src/pages/EditorPage.tsx @@ -34,6 +34,7 @@ import { useAutoSaveProject } from '../hooks/useAutoSaveProject'; import { registerEditorCommand } from '../lib/editorCommands'; import { whenNewsClear } from '../lib/newsGate'; import { EditorMenuBar } from '../components/editor/EditorMenuBar'; +import { embedBridge } from '../services/EmbedBridge'; import type { CompilationLog } from '../utils/compilationLogger'; import '../App.css'; @@ -79,6 +80,35 @@ export const EditorPage: React.FC = () => { // currentProject has a UUID — see useAutoSaveProject for the gating rules). const autoSave = useAutoSaveProject(); + // Embed mode: detect ?embed=true and optional flags from URL + const [embedMode] = useState(() => { + const params = new URLSearchParams(window.location.search); + return { + enabled: params.get('embed') === 'true', + hideEditor: params.get('hideEditor') === 'true', + lockComponents: params.get('lockComponents') === 'true', + desktopLayout: params.get('desktopLayout') === 'true', + }; + }); + const setLockComponents = useSimulatorStore((s) => s.setLockComponents); + + useEffect(() => { + if (embedMode.lockComponents) { + setLockComponents(true); + } + }, [embedMode.lockComponents, setLockComponents]); + + useEffect(() => { + if (!embedMode.enabled) return; + const handler = (e: Event) => { + const detail = (e as CustomEvent).detail || {}; + if (detail.lockComponents !== undefined) setLockComponents(detail.lockComponents); + }; + window.addEventListener('velxio-embed-mode', handler); + embedBridge.notifyReady(); + return () => window.removeEventListener('velxio-embed-mode', handler); + }, [embedMode.enabled]); + const [editorWidthPct, setEditorWidthPct] = useState(45); // Desktop-only 3-way layout switch (code-only / circuit-only / both). // Lets users hide a pane to give the right-docked chat more room. @@ -136,6 +166,7 @@ export const EditorPage: React.FC = () => { // The 2.5s bound applies only while the news decision is pending, so a // dead feed can't hold the dialog hostage — see lib/newsGate.ts. useEffect(() => { + if (embedMode.enabled) return; if (starterDialogShownThisLoad) return; const locale = getLocaleFromPath(window.location.pathname); // /editor is prerendered, so a fresh load arrives as /editor/ (nginx @@ -237,7 +268,9 @@ export const EditorPage: React.FC = () => { // that doesn't reflow when the editor/canvas splitter is dragged. const [canvasHeaderSlot, setCanvasHeaderSlot] = useState(null); // Default to 'code' on mobile — show the editor so users can write/view code - const [mobileView, setMobileView] = useState<'code' | 'circuit'>('code'); + const [mobileView, setMobileView] = useState<'code' | 'circuit'>( + embedMode.desktopLayout ? 'code' : embedMode.enabled ? 'circuit' : 'code' + ); // Save is dispatched to the pro overlay, which inspects auth state and // shows the right modal (Save vs Login prompt). In OSS without the @@ -550,13 +583,15 @@ export const EditorPage: React.FC = () => { return (
- : undefined} - editorToolbar={unifiedToolbar} - /> + {!embedMode.enabled && ( + : undefined} + editorToolbar={unifiedToolbar} + /> + )} {/* ── Mobile tab bar (top, above panels) ── */} - {isMobile && ( + {(isMobile || embedMode.desktopLayout) && !(embedMode.enabled && embedMode.hideEditor) && (