fix: apply theme variables to root app shell and overlays
Browse files- src/App.tsx +18 -32
src/App.tsx
CHANGED
|
@@ -12,21 +12,10 @@ import { listen } from '@tauri-apps/api/event';
|
|
| 12 |
const BrowserPanel = lazy(() => import('./components/BrowserPanel').then(m => ({ default: m.BrowserPanel })));
|
| 13 |
const LibraryPanel = lazy(() => import('./components/LibraryPanel').then(m => ({ default: m.LibraryPanel })));
|
| 14 |
const SettingsPanel = lazy(() => import('./components/SettingsPanel').then(m => ({ default: m.SettingsPanel })));
|
| 15 |
-
|
| 16 |
const appWindow = getCurrentWindow();
|
| 17 |
|
| 18 |
const MainApp = () => {
|
| 19 |
-
const {
|
| 20 |
-
currentScreen, setIsBrowserOpen, setIsLibraryOpen, isBrowserOpen, isLibraryOpen,
|
| 21 |
-
isSettingsOpen, setIsSettingsOpen, isAlwaysOnTop, setIsAlwaysOnTop, bgOpacity,
|
| 22 |
-
isAnnotationMode, setIsAnnotationMode, globalDesaturate, setGlobalDesaturate,
|
| 23 |
-
setZoom, setPan, pan, zoom, images, setImages, setTextNotes, selectedNodeIds, setSelectedNodeIds,
|
| 24 |
-
annotationColor, setAnnotationColor, annotationSize, setAnnotationSize,
|
| 25 |
-
isEraser, setIsEraser, isHighlighter, setIsHighlighter, undo, redo,
|
| 26 |
-
focusedImageId, setFocusedImageId, valueMirrorIds, setValueMirrorIds,
|
| 27 |
-
isZoomLensActive, setIsZoomLensActive, isWhisperBrowser, setIsWhisperBrowser,
|
| 28 |
-
showMinimap,
|
| 29 |
-
} = useAppStore();
|
| 30 |
const [toastMsg, setToastMsg] = useState<string | null>(null);
|
| 31 |
const [isCapturing, setIsCapturing] = useState(false);
|
| 32 |
const lastBPress = useRef(0);
|
|
@@ -39,8 +28,9 @@ const MainApp = () => {
|
|
| 39 |
const data = event.payload;
|
| 40 |
if (!data || !data.url) return;
|
| 41 |
const img = new Image();
|
| 42 |
-
|
| 43 |
-
img.
|
|
|
|
| 44 |
img.src = data.url;
|
| 45 |
});
|
| 46 |
return () => { unlisten.then(fn => fn()); };
|
|
@@ -70,7 +60,6 @@ const MainApp = () => {
|
|
| 70 |
if (key === 'l' && !ctrl && !e.shiftKey) { setIsLibraryOpen(p => !p); return; }
|
| 71 |
if (ctrl && key === ',') { e.preventDefault(); setIsSettingsOpen(p => !p); return; }
|
| 72 |
if (key === 'a' && !ctrl && !e.shiftKey) { setIsAnnotationMode(p => !p); return; }
|
| 73 |
-
if (key === 'g' && !ctrl && !e.shiftKey) { window.dispatchEvent(new CustomEvent('muse:toggle-grid', { detail: true })); return; }
|
| 74 |
if (key === 'd' && !ctrl) { if (e.shiftKey) setGlobalDesaturate(p => !p); else if (selectedNodeIds.length > 0) setImages(prev => prev.map(i => selectedNodeIds.includes(i.id) ? { ...i, isDesaturated: !i.isDesaturated } : i)); return; }
|
| 75 |
if (key === 't' && !ctrl && !e.shiftKey) { setIsAlwaysOnTop(p => !p); return; }
|
| 76 |
if (e.key === 'Escape') { setIsBrowserOpen(false); setIsLibraryOpen(false); setIsSettingsOpen(false); setSelectedNodeIds([]); setFocusedImageId(null); return; }
|
|
@@ -85,24 +74,21 @@ const MainApp = () => {
|
|
| 85 |
}, [selectedNodeIds, images, pan, zoom, focusedImageId, undo, redo]);
|
| 86 |
|
| 87 |
const handleCtxMenu = (e: React.MouseEvent) => { if (!(e.target instanceof HTMLInputElement) && !(e.target instanceof HTMLTextAreaElement) && !(e.target as HTMLElement).isContentEditable) e.preventDefault(); };
|
|
|
|
| 88 |
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
<
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
{focusedImageId && <div className="absolute top-4 left-1/2 -translate-x-1/2 z-50 pointer-events-none"><div className="bg-[#0A84FF]/20 border border-[#0A84FF] text-white px-3 py-1 rounded text-xs shadow-lg backdrop-blur">Focus Mode — ←→ cycle, F/Esc exit</div></div>}
|
| 103 |
-
{isCapturing && <div className="absolute inset-0 bg-black/50 flex items-center justify-center z-[100] pointer-events-none"><div className="bg-[#2A2A2E] border border-[#3A3A3E] rounded-xl px-6 py-4 shadow-2xl text-white text-sm">Capturing...</div></div>}
|
| 104 |
-
</div>
|
| 105 |
-
);
|
| 106 |
};
|
| 107 |
|
| 108 |
export default function App() { return <AppProvider><MainApp /></AppProvider>; }
|
|
|
|
| 12 |
const BrowserPanel = lazy(() => import('./components/BrowserPanel').then(m => ({ default: m.BrowserPanel })));
|
| 13 |
const LibraryPanel = lazy(() => import('./components/LibraryPanel').then(m => ({ default: m.LibraryPanel })));
|
| 14 |
const SettingsPanel = lazy(() => import('./components/SettingsPanel').then(m => ({ default: m.SettingsPanel })));
|
|
|
|
| 15 |
const appWindow = getCurrentWindow();
|
| 16 |
|
| 17 |
const MainApp = () => {
|
| 18 |
+
const { currentScreen, setIsBrowserOpen, setIsLibraryOpen, isBrowserOpen, isLibraryOpen, isSettingsOpen, setIsSettingsOpen, isAlwaysOnTop, setIsAlwaysOnTop, bgOpacity, isAnnotationMode, setIsAnnotationMode, globalDesaturate, setGlobalDesaturate, setZoom, setPan, pan, zoom, images, setImages, setTextNotes, selectedNodeIds, setSelectedNodeIds, annotationColor, setAnnotationColor, annotationSize, setAnnotationSize, isEraser, setIsEraser, isHighlighter, setIsHighlighter, undo, redo, focusedImageId, setFocusedImageId, valueMirrorIds, setValueMirrorIds, isZoomLensActive, setIsZoomLensActive, isWhisperBrowser, setIsWhisperBrowser, showMinimap } = useAppStore();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
const [toastMsg, setToastMsg] = useState<string | null>(null);
|
| 20 |
const [isCapturing, setIsCapturing] = useState(false);
|
| 21 |
const lastBPress = useRef(0);
|
|
|
|
| 28 |
const data = event.payload;
|
| 29 |
if (!data || !data.url) return;
|
| 30 |
const img = new Image();
|
| 31 |
+
const add = (w0: number, h0: number) => { const ratio = w0 / h0; const w = Math.min(400, w0); const h = w / ratio; setImages(prev => [...prev, { id: data.id || crypto.randomUUID(), url: data.url, x: (-pan.x + window.innerWidth / 3 + Math.random() * 80) / zoom, y: (-pan.y + window.innerHeight / 3 + Math.random() * 80) / zoom, width: w, height: h, aspectRatio: ratio }]); setToastMsg('✓ Image added'); };
|
| 32 |
+
img.onload = () => add(img.width, img.height);
|
| 33 |
+
img.onerror = () => add(data.width || 300, data.height || 200);
|
| 34 |
img.src = data.url;
|
| 35 |
});
|
| 36 |
return () => { unlisten.then(fn => fn()); };
|
|
|
|
| 60 |
if (key === 'l' && !ctrl && !e.shiftKey) { setIsLibraryOpen(p => !p); return; }
|
| 61 |
if (ctrl && key === ',') { e.preventDefault(); setIsSettingsOpen(p => !p); return; }
|
| 62 |
if (key === 'a' && !ctrl && !e.shiftKey) { setIsAnnotationMode(p => !p); return; }
|
|
|
|
| 63 |
if (key === 'd' && !ctrl) { if (e.shiftKey) setGlobalDesaturate(p => !p); else if (selectedNodeIds.length > 0) setImages(prev => prev.map(i => selectedNodeIds.includes(i.id) ? { ...i, isDesaturated: !i.isDesaturated } : i)); return; }
|
| 64 |
if (key === 't' && !ctrl && !e.shiftKey) { setIsAlwaysOnTop(p => !p); return; }
|
| 65 |
if (e.key === 'Escape') { setIsBrowserOpen(false); setIsLibraryOpen(false); setIsSettingsOpen(false); setSelectedNodeIds([]); setFocusedImageId(null); return; }
|
|
|
|
| 74 |
}, [selectedNodeIds, images, pan, zoom, focusedImageId, undo, redo]);
|
| 75 |
|
| 76 |
const handleCtxMenu = (e: React.MouseEvent) => { if (!(e.target instanceof HTMLInputElement) && !(e.target instanceof HTMLTextAreaElement) && !(e.target as HTMLElement).isContentEditable) e.preventDefault(); };
|
| 77 |
+
if (currentScreen === 'hub') return <div className="w-screen h-screen relative overflow-hidden bg-[var(--panel-bg)]" onContextMenu={handleCtxMenu}><StarterHub /><Suspense fallback={null}>{isSettingsOpen && <SettingsPanel />}</Suspense></div>;
|
| 78 |
|
| 79 |
+
return <div className="w-screen h-screen relative overflow-hidden bg-[var(--canvas-bg)]" style={{ opacity: isAlwaysOnTop ? bgOpacity / 100 : 1 }} onContextMenu={handleCtxMenu}>
|
| 80 |
+
<Canvas />
|
| 81 |
+
{(isBrowserOpen || isLibraryOpen || isSettingsOpen) && <div className="absolute inset-0 bg-black/40 pointer-events-none z-30 transition-opacity" />}
|
| 82 |
+
<Toolbar />
|
| 83 |
+
<Suspense fallback={null}>{isBrowserOpen && <BrowserPanel />}{isLibraryOpen && <LibraryPanel />}{isSettingsOpen && <SettingsPanel />}</Suspense>
|
| 84 |
+
{showMinimap && <Minimap />}
|
| 85 |
+
<ContextMenu />
|
| 86 |
+
<div className="absolute bottom-4 left-4 text-[11px] opacity-40 text-[var(--ui-primary)] pointer-events-none select-none z-10 mix-blend-difference">Space+Drag Pan · Scroll Zoom · B Browser · L Library · A Annotate · Shift+S Screenshot</div>
|
| 87 |
+
{toastMsg && <div className="absolute top-16 left-1/2 -translate-x-1/2 z-50 pointer-events-none"><div className="bg-[var(--panel-bg)]/90 border border-white/10 text-[var(--ui-primary)] font-medium px-4 py-2 rounded-full shadow-2xl backdrop-blur text-xs">{toastMsg}</div></div>}
|
| 88 |
+
{globalDesaturate && <div className="absolute top-16 left-1/2 -translate-x-1/2 z-50 pointer-events-none"><div className="bg-[var(--accent-amber)]/20 border border-[var(--accent-amber)] text-[var(--ui-primary)] px-3 py-1 rounded text-xs shadow-lg backdrop-blur">Desaturation Active (Shift+D)</div></div>}
|
| 89 |
+
{focusedImageId && <div className="absolute top-4 left-1/2 -translate-x-1/2 z-50 pointer-events-none"><div className="bg-[var(--accent)]/20 border border-[var(--accent)] text-[var(--ui-primary)] px-3 py-1 rounded text-xs shadow-lg backdrop-blur">Focus Mode — ←→ cycle, F/Esc exit</div></div>}
|
| 90 |
+
{isCapturing && <div className="absolute inset-0 bg-black/50 flex items-center justify-center z-[100] pointer-events-none"><div className="bg-[var(--panel-surface)] border border-[var(--panel-border)] rounded-xl px-6 py-4 shadow-2xl text-[var(--ui-primary)] text-sm">Capturing...</div></div>}
|
| 91 |
+
</div>;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
};
|
| 93 |
|
| 94 |
export default function App() { return <AppProvider><MainApp /></AppProvider>; }
|