initial commit

This commit is contained in:
2025-11-11 12:09:49 -05:00
commit a2dca57fd2
23 changed files with 6424 additions and 0 deletions

156
src/App.tsx Executable file
View File

@@ -0,0 +1,156 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import { useState, useEffect } from 'react';
import { Box, VStack, Button, Text, Badge, Image } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query';
import { ConnectionControls } from './components/ConnectControls';
import { DarkMode } from '@chakra-ui/system';
// @ts-expect-error
import shield from './icons/icon.png';
function App() {
const [configName, setConfigName] = useState<string>('');
const [isLoading, setIsLoading] = useState<boolean>(false);
// Poll connection status every 3 seconds
const { data: connectionStatus } = useQuery({
queryKey: ['connectionStatus'],
queryFn: async () => {
const result = await window.electronAPI.getConnectionStatus();
return result.success && result.connected ? result.connected : false;
},
refetchInterval: 3000,
initialData: false,
});
const checkCurrentConfig = async () => {
const result = await window.electronAPI.getCurrentConfig();
if (result.success && result.hasConfig && result.configName) {
setConfigName(result.configName);
}
};
// Check for existing config on mount
useEffect(() => {
// eslint-disable-next-line
checkCurrentConfig();
}, []);
const handleLoadConfig = async () => {
setIsLoading(true);
const selectResult = await window.electronAPI.selectConfigFile();
if (!selectResult.success || !selectResult.filePath) {
setIsLoading(false);
return;
}
const loadResult = await window.electronAPI.loadConfig(
selectResult.filePath
);
if (loadResult.success && loadResult.configName) {
setTimeout(() => {
setIsLoading(false);
}, 2000);
} else {
setIsLoading(false);
}
};
return (
<DarkMode>
<Box
minH="100vh"
bg="gray.600"
color="gray.600"
p={6}
borderTop="none"
borderWidth="1px"
borderColor="gray.600"
>
<VStack gap={4} align="stretch">
<Image h={75} w={75} margin="auto" src={shield} />
<Text
color="gray.300"
fontSize="3xl"
fontWeight="bold"
textAlign="center"
>
Dark Shield
</Text>
<Box
p={4}
bg="gray.300"
borderRadius="md"
borderWidth="1px"
borderColor="gray.600"
shadow="sm"
>
<VStack gap={3} align="stretch">
<Text fontSize="sm" color="gray.800" fontWeight="semibold">
Connection Status
</Text>
<Badge
bg={connectionStatus ? 'green.50' : 'red.50'}
borderRadius="md"
borderWidth="1px"
borderColor={connectionStatus ? 'green.300' : 'red.300'}
color={connectionStatus ? 'green.700' : 'red.700'}
fontSize="md"
p={2}
textAlign="center"
>
{connectionStatus ? 'Connected' : 'Disconnected'}
</Badge>
</VStack>
</Box>
<Box
p={4}
bg="gray.300"
borderRadius="md"
borderWidth="1px"
borderColor="gray.600"
shadow="sm"
>
<VStack gap={3} align="stretch">
<Text fontSize="sm" color="gray.800" fontWeight="semibold">
Configuration
</Text>
{configName ? (
<Text fontSize="md" color="gray.800" fontWeight="medium">
{configName}
</Text>
) : (
<Text fontSize="sm" color="gray.800">
No config loaded
</Text>
)}
<Button
bg="gray.800"
color="white"
_hover={{ bg: 'gray.700' }}
onClick={handleLoadConfig}
loading={isLoading}
loadingText="Loading..."
>
Load Config
</Button>
</VStack>
</Box>
<ConnectionControls
isConnected={connectionStatus}
hasConfig={!!configName}
onConnectionChange={() => {}}
/>
</VStack>
</Box>
</DarkMode>
);
}
export default App;

View File

@@ -0,0 +1,94 @@
import { useState } from 'react';
import { Box, VStack, Button, Text } from '@chakra-ui/react';
interface ConnectionControlsProps {
isConnected: boolean;
hasConfig: boolean;
onConnectionChange: (connected: boolean) => void;
}
export function ConnectionControls({
isConnected,
hasConfig,
onConnectionChange,
}: ConnectionControlsProps) {
const [isLoading, setIsLoading] = useState(false);
const handleConnect = async () => {
setIsLoading(true);
const result = await window.electronAPI.connectWireguard();
if (result.success) {
onConnectionChange(true);
setTimeout(() => {
setIsLoading(false);
}, 2000);
} else {
setIsLoading(false);
}
};
const handleDisconnect = async () => {
setIsLoading(true);
const result = await window.electronAPI.disconnectWireguard();
if (result.success) {
onConnectionChange(true);
setTimeout(() => {
setIsLoading(false);
}, 2000);
} else {
setIsLoading(false);
}
};
return (
<Box
p={4}
bg="gray.300"
borderRadius="md"
borderWidth="1px"
borderColor="gray.600"
shadow="sm"
>
<VStack gap={3} align="stretch">
<Text fontSize="sm" color="gray.800" fontWeight="semibold">
Connection Controls
</Text>
{!hasConfig && (
<Text fontSize="sm" color="orange.600">
Please load a config file first
</Text>
)}
{hasConfig && isConnected ? (
<Button
bg="red.600"
color="white"
_hover={{ bg: 'red.700' }}
onClick={handleDisconnect}
disabled={isLoading}
loading={isLoading}
>
Disconnect
</Button>
) : (
<Button
bg="green.600"
color="white"
_hover={{ bg: 'green.700' }}
onClick={handleConnect}
disabled={isLoading}
loading={isLoading}
>
Connect
</Button>
)}
</VStack>
</Box>
);
}

BIN
src/icons/icon.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

21
src/main.tsx Executable file
View File

@@ -0,0 +1,21 @@
import { createRoot } from 'react-dom/client';
import { ChakraProvider, defaultSystem } from '@chakra-ui/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import App from './App';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
retry: 2,
},
},
});
createRoot(document.getElementById('root')!).render(
<ChakraProvider value={defaultSystem}>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</ChakraProvider>
);

52
src/vite.env.d.ts vendored Executable file
View File

@@ -0,0 +1,52 @@
declare module '*.png' {
const content: string;
export default content;
}
declare module '*.jpg' {
const content: string;
export default content;
}
declare module '*.svg' {
const content: string;
export default content;
}
export interface ElectronAPI {
getConnectionStatus: () => Promise<{
success: boolean;
connected?: boolean;
}>;
getCurrentConfig: () => Promise<{
success: boolean;
hasConfig?: boolean;
configName?: string;
}>;
selectConfigFile: () => Promise<{
success: boolean;
filePath?: string;
}>;
loadConfig: (filePath: string) => Promise<{
success: boolean;
configName?: string;
}>;
connectWireguard: () => Promise<{
success: boolean;
message?: string;
error?: string;
}>;
disconnectWireguard: () => Promise<{
success: boolean;
message?: string;
error?: string;
}>;
}
declare global {
interface Window {
electronAPI: ElectronAPI;
}
}
export {};