feat(core): initialize project skeleton with Electron + React + TypeScript

Set up the complete project foundation for ClawX, a graphical AI assistant:

- Electron main process with IPC handlers, menu, tray, and gateway management
- React renderer with routing, layout components, and page scaffolding
- Zustand state management for gateway, settings, channels, skills, chat, and cron
- shadcn/ui components with Tailwind CSS and CSS variable theming
- Build tooling with Vite, electron-builder, and TypeScript configuration
- Testing setup with Vitest and Playwright
- Development configurations (ESLint, Prettier, gitignore, env example)
This commit is contained in:
Haze
2026-02-05 23:09:17 +08:00
Unverified
parent 9442e5f77a
commit b8ab0208d0
71 changed files with 14086 additions and 3 deletions

View File

@@ -0,0 +1,282 @@
/**
* Settings Page
* Application configuration
*/
import { useState, useEffect } from 'react';
import {
Sun,
Moon,
Monitor,
RefreshCw,
Loader2,
Terminal,
ExternalLink,
Info,
} from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Label } from '@/components/ui/label';
import { Switch } from '@/components/ui/switch';
import { Separator } from '@/components/ui/separator';
import { Badge } from '@/components/ui/badge';
import { useSettingsStore } from '@/stores/settings';
import { useGatewayStore } from '@/stores/gateway';
export function Settings() {
const {
theme,
setTheme,
gatewayAutoStart,
setGatewayAutoStart,
autoCheckUpdate,
setAutoCheckUpdate,
autoDownloadUpdate,
setAutoDownloadUpdate,
devModeUnlocked,
} = useSettingsStore();
const { status: gatewayStatus, restart: restartGateway } = useGatewayStore();
const [appVersion, setAppVersion] = useState('0.1.0');
const [checkingUpdate, setCheckingUpdate] = useState(false);
// Get app version
useEffect(() => {
window.electron.ipcRenderer.invoke('app:version').then((version) => {
setAppVersion(version as string);
});
}, []);
// Check for updates
const handleCheckUpdate = async () => {
setCheckingUpdate(true);
try {
await window.electron.ipcRenderer.invoke('update:check');
} finally {
setCheckingUpdate(false);
}
};
// Open developer console
const openDevConsole = () => {
window.electron.openExternal('http://localhost:18789');
};
return (
<div className="space-y-6 max-w-2xl">
<div>
<h1 className="text-2xl font-bold">Settings</h1>
<p className="text-muted-foreground">
Configure your ClawX experience
</p>
</div>
{/* Appearance */}
<Card>
<CardHeader>
<CardTitle>Appearance</CardTitle>
<CardDescription>Customize the look and feel</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label>Theme</Label>
<div className="flex gap-2">
<Button
variant={theme === 'light' ? 'default' : 'outline'}
size="sm"
onClick={() => setTheme('light')}
>
<Sun className="h-4 w-4 mr-2" />
Light
</Button>
<Button
variant={theme === 'dark' ? 'default' : 'outline'}
size="sm"
onClick={() => setTheme('dark')}
>
<Moon className="h-4 w-4 mr-2" />
Dark
</Button>
<Button
variant={theme === 'system' ? 'default' : 'outline'}
size="sm"
onClick={() => setTheme('system')}
>
<Monitor className="h-4 w-4 mr-2" />
System
</Button>
</div>
</div>
</CardContent>
</Card>
{/* Gateway */}
<Card>
<CardHeader>
<CardTitle>Gateway</CardTitle>
<CardDescription>OpenClaw Gateway settings</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center justify-between">
<div>
<Label>Status</Label>
<p className="text-sm text-muted-foreground">
Port: {gatewayStatus.port}
</p>
</div>
<div className="flex items-center gap-2">
<Badge
variant={
gatewayStatus.state === 'running'
? 'success'
: gatewayStatus.state === 'error'
? 'destructive'
: 'secondary'
}
>
{gatewayStatus.state}
</Badge>
<Button variant="outline" size="sm" onClick={restartGateway}>
<RefreshCw className="h-4 w-4 mr-2" />
Restart
</Button>
</div>
</div>
<Separator />
<div className="flex items-center justify-between">
<div>
<Label>Auto-start Gateway</Label>
<p className="text-sm text-muted-foreground">
Start Gateway when ClawX launches
</p>
</div>
<Switch
checked={gatewayAutoStart}
onCheckedChange={setGatewayAutoStart}
/>
</div>
</CardContent>
</Card>
{/* Updates */}
<Card>
<CardHeader>
<CardTitle>Updates</CardTitle>
<CardDescription>Keep ClawX up to date</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center justify-between p-4 rounded-lg border">
<div>
<p className="font-medium">ClawX</p>
<p className="text-sm text-muted-foreground">
Version {appVersion}
</p>
</div>
<Button
variant="outline"
onClick={handleCheckUpdate}
disabled={checkingUpdate}
>
{checkingUpdate ? (
<>
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
Checking...
</>
) : (
'Check for Updates'
)}
</Button>
</div>
<Separator />
<div className="flex items-center justify-between">
<div>
<Label>Auto-check for updates</Label>
<p className="text-sm text-muted-foreground">
Check for updates on startup
</p>
</div>
<Switch
checked={autoCheckUpdate}
onCheckedChange={setAutoCheckUpdate}
/>
</div>
<div className="flex items-center justify-between">
<div>
<Label>Auto-download updates</Label>
<p className="text-sm text-muted-foreground">
Download updates in the background
</p>
</div>
<Switch
checked={autoDownloadUpdate}
onCheckedChange={setAutoDownloadUpdate}
/>
</div>
</CardContent>
</Card>
{/* Developer */}
{devModeUnlocked && (
<Card>
<CardHeader>
<CardTitle>Developer</CardTitle>
<CardDescription>Advanced options for developers</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label>OpenClaw Console</Label>
<p className="text-sm text-muted-foreground">
Access the native OpenClaw management interface
</p>
<Button variant="outline" onClick={openDevConsole}>
<Terminal className="h-4 w-4 mr-2" />
Open Developer Console
<ExternalLink className="h-3 w-3 ml-2" />
</Button>
<p className="text-xs text-muted-foreground">
Opens http://localhost:18789 in your browser
</p>
</div>
</CardContent>
</Card>
)}
{/* About */}
<Card>
<CardHeader>
<CardTitle>About</CardTitle>
</CardHeader>
<CardContent className="space-y-2 text-sm text-muted-foreground">
<p>
<strong>ClawX</strong> - Graphical AI Assistant
</p>
<p>Based on OpenClaw</p>
<p>Version {appVersion}</p>
<div className="flex gap-4 pt-2">
<Button
variant="link"
className="h-auto p-0"
onClick={() => window.electron.openExternal('https://docs.clawx.app')}
>
Documentation
</Button>
<Button
variant="link"
className="h-auto p-0"
onClick={() => window.electron.openExternal('https://github.com/clawx/clawx')}
>
GitHub
</Button>
</div>
</CardContent>
</Card>
</div>
);
}
export default Settings;