feat(elemes): wire EmbedBridge, embedMode URL params, and auth/projects routers for LMS v3.0.4
This commit is contained in:
parent
af72e7cc3f
commit
bf457e17b8
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -113,6 +113,11 @@
|
|||
<changefreq>monthly</changefreq>
|
||||
<priority>0.6</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://velxio.dev/examples/esp32-micropython-external-library/</loc>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.6</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://velxio.dev/examples/esp32-idf-blink/</loc>
|
||||
<changefreq>monthly</changefreq>
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
||||
|
|
|
|||
|
|
@ -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<HTMLDivElement | null>(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 (
|
||||
<div className="app">
|
||||
<AppHeader
|
||||
editorMenu={!isMobile ? <EditorMenuBar /> : undefined}
|
||||
editorToolbar={unifiedToolbar}
|
||||
/>
|
||||
{!embedMode.enabled && (
|
||||
<AppHeader
|
||||
editorMenu={!isMobile ? <EditorMenuBar /> : undefined}
|
||||
editorToolbar={unifiedToolbar}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* ── Mobile tab bar (top, above panels) ── */}
|
||||
{isMobile && (
|
||||
{(isMobile || embedMode.desktopLayout) && !(embedMode.enabled && embedMode.hideEditor) && (
|
||||
<nav className="mobile-tab-bar">
|
||||
<button
|
||||
className={`mobile-tab-btn${mobileView === 'code' ? ' mobile-tab-btn--active' : ''}`}
|
||||
|
|
@ -607,7 +642,9 @@ export const EditorPage: React.FC = () => {
|
|||
<div
|
||||
className="editor-panel"
|
||||
style={{
|
||||
width: isMobile
|
||||
width: (embedMode.enabled && embedMode.hideEditor)
|
||||
? '0%'
|
||||
: isMobile
|
||||
? '100%'
|
||||
: viewMode === 'code'
|
||||
? '100%'
|
||||
|
|
@ -615,7 +652,9 @@ export const EditorPage: React.FC = () => {
|
|||
? '0%'
|
||||
: `${editorWidthPct}%`,
|
||||
display:
|
||||
(isMobile && mobileView !== 'code') || (!isMobile && viewMode === 'circuit')
|
||||
(embedMode.enabled && embedMode.hideEditor) ||
|
||||
(isMobile && mobileView !== 'code') ||
|
||||
(!isMobile && viewMode === 'circuit')
|
||||
? 'none'
|
||||
: 'flex',
|
||||
flexDirection: 'row',
|
||||
|
|
@ -728,7 +767,7 @@ export const EditorPage: React.FC = () => {
|
|||
</div>
|
||||
|
||||
{/* Resize handle (desktop only, and only when both panes are visible) */}
|
||||
{!isMobile && viewMode === 'both' && (
|
||||
{!isMobile && viewMode === 'both' && !(embedMode.enabled && embedMode.hideEditor) && (
|
||||
<div className="resize-handle" onMouseDown={handleResizeMouseDown}>
|
||||
<div className="resize-handle-grip" />
|
||||
</div>
|
||||
|
|
@ -738,17 +777,20 @@ export const EditorPage: React.FC = () => {
|
|||
<div
|
||||
className="simulator-panel"
|
||||
style={{
|
||||
width: isMobile
|
||||
width: (embedMode.enabled && embedMode.hideEditor)
|
||||
? '100%'
|
||||
: isMobile
|
||||
? '100%'
|
||||
: viewMode === 'circuit'
|
||||
? '100%'
|
||||
: viewMode === 'code'
|
||||
? '0%'
|
||||
: `${100 - editorWidthPct}%`,
|
||||
display:
|
||||
(isMobile && mobileView !== 'circuit') || (!isMobile && viewMode === 'code')
|
||||
? 'none'
|
||||
: 'flex',
|
||||
display: (embedMode.enabled && embedMode.hideEditor)
|
||||
? 'flex'
|
||||
: (isMobile && mobileView !== 'circuit') || (!isMobile && viewMode === 'code')
|
||||
? 'none'
|
||||
: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
|
|
@ -782,18 +824,20 @@ export const EditorPage: React.FC = () => {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{showStarBanner && (
|
||||
{!embedMode.enabled && showStarBanner && (
|
||||
<GitHubStarBanner
|
||||
onClose={handleDismissStarBanner}
|
||||
onStarClick={handleStarClick}
|
||||
round={starRound}
|
||||
/>
|
||||
)}
|
||||
<NewsAnnouncer />
|
||||
<NewProjectDialog
|
||||
isOpen={showNewProjectDialog}
|
||||
onClose={() => setShowNewProjectDialog(false)}
|
||||
/>
|
||||
{!embedMode.enabled && <NewsAnnouncer />}
|
||||
{!embedMode.enabled && (
|
||||
<NewProjectDialog
|
||||
isOpen={showNewProjectDialog}
|
||||
onClose={() => setShowNewProjectDialog(false)}
|
||||
/>
|
||||
)}
|
||||
{/* Slot reserved for the private pro overlay (e.g. agent chat panel).
|
||||
Self-hosted builds without an overlay see nothing here. */}
|
||||
<div data-velxio-slot="agent-chat" />
|
||||
|
|
|
|||
Loading…
Reference in New Issue