Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 118 additions & 58 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useContext } from "react";
import { useState, useEffect } from "react";
import { motion, AnimatePresence } from "framer-motion";
import {
IconBattery2,
Expand All @@ -7,14 +7,13 @@ import {
IconKeyboard,
IconPointer,
IconSettings,
IconTerminal2,
} from "@tabler/icons-react";

import { SplashScreen } from "./components/SplashScreen";
import {
DeviceConnectionProvider,
ConnectionContext,
} from "./components/DeviceConnection";
import { ThemeProvider } from "./contexts/ThemeContext";
import { AppStateProvider } from "./contexts/AppStateContext";
import { useAppState } from "./contexts/AppStateContextDef";
import { TabNavigation } from "./components/TabNavigation";
import type { TabItem } from "./components/TabNavigation";
import { AppLayout } from "./layouts/AppLayout";
Expand All @@ -24,101 +23,162 @@ import { HealthCheckPage } from "./pages/HealthCheckPage";
import { KeymapPage } from "./pages/KeymapPage";
import { TrackballPage } from "./pages/TrackballPage";
import { SettingsPage } from "./pages/SettingsPage";

const tabs: TabItem[] = [
{
id: "battery",
label: "Battery",
icon: <IconBattery2 size={18} />,
content: <BatteryPage />,
},
{
id: "ble",
label: "BLE",
icon: <IconBluetooth size={18} />,
content: <BLEConnectionsPage />,
},
{
id: "health",
label: "Health",
icon: <IconHeartRateMonitor size={18} />,
content: <HealthCheckPage />,
},
{
id: "keymap",
label: "Keymap",
icon: <IconKeyboard size={18} />,
content: <KeymapPage />,
},
{
id: "trackball",
label: "Trackball",
icon: <IconPointer size={18} />,
content: <TrackballPage />,
},
{
id: "settings",
label: "Settings",
icon: <IconSettings size={18} />,
content: <SettingsPage />,
},
];
import { DebugConsolePage } from "./pages/DebugConsolePage";
import { DraggableWindow } from "./components/DraggableWindow";
import { SerialConsole } from "./components/SerialConsole";

function App() {
return (
<ThemeProvider>
<DeviceConnectionProvider>
<AppStateProvider>
<AppContent />
</DeviceConnectionProvider>
</AppStateProvider>
</ThemeProvider>
);
}

function AppContent() {
const connection = useContext(ConnectionContext);
const appState = useAppState();
const [activeTab, setActiveTab] = useState("battery");

// Base tabs - always available when ZMK connected
const baseTabs: TabItem[] = [
{
id: "battery",
label: "Battery",
icon: <IconBattery2 size={18} />,
content: <BatteryPage />,
},
{
id: "ble",
label: "BLE",
icon: <IconBluetooth size={18} />,
content: <BLEConnectionsPage />,
},
{
id: "health",
label: "Health",
icon: <IconHeartRateMonitor size={18} />,
content: <HealthCheckPage />,
},
{
id: "keymap",
label: "Keymap",
icon: <IconKeyboard size={18} />,
content: <KeymapPage />,
},
{
id: "trackball",
label: "Trackball",
icon: <IconPointer size={18} />,
content: <TrackballPage />,
},
{
id: "settings",
label: "Settings",
icon: <IconSettings size={18} />,
content: <SettingsPage />,
},
];

// Console tab only appears when serial console is connected
const tabs: TabItem[] = [
...baseTabs,
...(appState.serialConnected
? [
{
id: "console",
label: "Console",
icon: <IconTerminal2 size={18} />,
content: <DebugConsolePage />,
},
]
: []),
];

// Handle tab changes
const handleTabChange = (tabId: string) => {
setActiveTab(tabId);

if (tabId === "console") {
appState.onConsoleTabActivated();
} else if (activeTab === "console") {
appState.onOtherTabActivated();
}
};

// Auto-switch to console tab when entering state D
useEffect(() => {
if (appState.state === "D" && activeTab !== "console") {
setTimeout(() => setActiveTab("console"), 0);
}
}, [appState.state, activeTab]);

// Show splash screen in states A and C
const showSplashScreen = appState.state === "A" || appState.state === "C";
// Show main app in states B, D, E
const showMainApp = appState.state === "B" || appState.state === "D" || appState.state === "E";
// Show console window in states C and E
const showConsoleWindow = appState.state === "C" || appState.state === "E";

return (
<>
<AnimatePresence>
{!connection.isConnected && (
{showSplashScreen && (
<motion.div
key="splash"
initial={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.3 }}
>
<SplashScreen
onConnect={connection.onConnect}
isConnecting={connection.isLoading}
error={connection.error}
onConnect={appState.onConnect}
isConnecting={appState.isLoading}
error={appState.error}
/>
</motion.div>
)}
</AnimatePresence>

{connection.isConnected && (
{showMainApp && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.3 }}
className="h-screen"
>
<AppLayout
isConnected={connection.isConnected}
deviceName={connection.deviceName}
onConnect={connection.onConnect}
onDisconnect={connection.onDisconnect}
isConnecting={connection.isLoading}
isConnected={appState.zmkConnected}
deviceName={appState.deviceName}
onConnect={appState.onConnect}
onDisconnect={appState.onDisconnect}
isConnecting={appState.isLoading}
>
<TabNavigation
tabs={tabs}
activeTab={activeTab}
onTabChange={setActiveTab}
onTabChange={handleTabChange}
/>
</AppLayout>
</motion.div>
)}

{/* Serial Console Window - shown in states C and E */}
{showConsoleWindow && (
<DraggableWindow
open={true}
onClose={appState.onSerialDisconnect}
title="Serial Console"
>
<SerialConsole
onConnectionChange={(connected) => {
if (!connected) {
appState.onSerialDisconnect();
}
}}
/>
</DraggableWindow>
)}
</>
);
}
Expand Down
Loading