- Custom scrollbar CSS using ::-webkit-scrollbar for Tauri's Chromium WebView - Portable storage: all data written next to exe in data/ folder instead of AppData - Rust get_portable_data_dir command with runtime FS scope for exe directory - Window size/position/maximized saved to settings on close, restored on startup
148 lines
4.7 KiB
TypeScript
148 lines
4.7 KiB
TypeScript
import { useState, useEffect, useCallback } from "react";
|
|
import { getCurrentWindow, LogicalSize, LogicalPosition } from "@tauri-apps/api/window";
|
|
import { AnimatePresence, motion } from "framer-motion";
|
|
import { springs, fadeSlideLeft, fadeSlideRight } from "@/lib/motion";
|
|
import { useAppStore } from "@/stores/app-store";
|
|
import { useBoardStore } from "@/stores/board-store";
|
|
import { saveSettings } from "@/lib/storage";
|
|
import { AppShell } from "@/components/layout/AppShell";
|
|
import { BoardList } from "@/components/boards/BoardList";
|
|
import { BoardView } from "@/components/board/BoardView";
|
|
import { CommandPalette } from "@/components/command-palette/CommandPalette";
|
|
import { SettingsDialog } from "@/components/settings/SettingsDialog";
|
|
import { ToastContainer } from "@/components/toast/ToastContainer";
|
|
import { ShortcutHelpModal } from "@/components/shortcuts/ShortcutHelpModal";
|
|
import { useKeyboardShortcuts } from "@/hooks/useKeyboardShortcuts";
|
|
|
|
export default function App() {
|
|
const initialized = useAppStore((s) => s.initialized);
|
|
const init = useAppStore((s) => s.init);
|
|
const view = useAppStore((s) => s.view);
|
|
|
|
const [settingsOpen, setSettingsOpen] = useState(false);
|
|
const [shortcutHelpOpen, setShortcutHelpOpen] = useState(false);
|
|
|
|
useEffect(() => {
|
|
init().then(() => {
|
|
// Restore window state after settings are loaded
|
|
const { settings } = useAppStore.getState();
|
|
const ws = settings.windowState;
|
|
if (ws) {
|
|
const appWindow = getCurrentWindow();
|
|
if (ws.maximized) {
|
|
appWindow.maximize();
|
|
} else {
|
|
appWindow.setSize(new LogicalSize(ws.width, ws.height));
|
|
appWindow.setPosition(new LogicalPosition(ws.x, ws.y));
|
|
}
|
|
}
|
|
});
|
|
}, [init]);
|
|
|
|
// Save window state + flush board saves before the app window closes
|
|
useEffect(() => {
|
|
function handleBeforeUnload() {
|
|
useBoardStore.getState().closeBoard();
|
|
|
|
// Save window state synchronously-ish (fire and forget)
|
|
const appWindow = getCurrentWindow();
|
|
Promise.all([
|
|
appWindow.outerSize(),
|
|
appWindow.outerPosition(),
|
|
appWindow.isMaximized(),
|
|
]).then(([size, position, maximized]) => {
|
|
const settings = useAppStore.getState().settings;
|
|
saveSettings({
|
|
...settings,
|
|
windowState: {
|
|
x: position.x,
|
|
y: position.y,
|
|
width: size.width,
|
|
height: size.height,
|
|
maximized,
|
|
},
|
|
});
|
|
});
|
|
}
|
|
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
return () => {
|
|
window.removeEventListener("beforeunload", handleBeforeUnload);
|
|
};
|
|
}, []);
|
|
|
|
// Listen for custom event to open settings from TopBar or command palette
|
|
useEffect(() => {
|
|
function handleOpenSettings() {
|
|
setSettingsOpen(true);
|
|
}
|
|
document.addEventListener("open-settings-dialog", handleOpenSettings);
|
|
return () => {
|
|
document.removeEventListener("open-settings-dialog", handleOpenSettings);
|
|
};
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
function handleOpenShortcutHelp() {
|
|
setShortcutHelpOpen(true);
|
|
}
|
|
document.addEventListener("open-shortcut-help", handleOpenShortcutHelp);
|
|
return () => {
|
|
document.removeEventListener("open-shortcut-help", handleOpenShortcutHelp);
|
|
};
|
|
}, []);
|
|
|
|
const handleOpenSettings = useCallback(() => {
|
|
setSettingsOpen(true);
|
|
}, []);
|
|
|
|
useKeyboardShortcuts();
|
|
|
|
if (!initialized) {
|
|
return (
|
|
<div className="flex h-screen items-center justify-center bg-pylon-bg">
|
|
<span className="font-heading text-lg text-pylon-text-secondary">
|
|
Loading...
|
|
</span>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<AppShell>
|
|
<AnimatePresence mode="wait">
|
|
{view.type === "board-list" ? (
|
|
<motion.div
|
|
key="board-list"
|
|
className="h-full"
|
|
variants={fadeSlideRight}
|
|
initial="hidden"
|
|
animate="visible"
|
|
exit="exit"
|
|
transition={springs.gentle}
|
|
>
|
|
<BoardList />
|
|
</motion.div>
|
|
) : (
|
|
<motion.div
|
|
key={`board-${view.boardId}`}
|
|
className="h-full"
|
|
variants={fadeSlideLeft}
|
|
initial="hidden"
|
|
animate="visible"
|
|
exit="exit"
|
|
transition={springs.gentle}
|
|
>
|
|
<BoardView />
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
</AppShell>
|
|
<CommandPalette onOpenSettings={handleOpenSettings} />
|
|
<SettingsDialog open={settingsOpen} onOpenChange={setSettingsOpen} />
|
|
<ToastContainer />
|
|
<ShortcutHelpModal open={shortcutHelpOpen} onOpenChange={setShortcutHelpOpen} />
|
|
</>
|
|
);
|
|
}
|