feat: add about dialog with custom information
This commit is contained in:
@@ -18,12 +18,24 @@ import { opencodeClient } from '@/lib/opencode/client';
|
||||
import { useFontPreferences } from '@/hooks/useFontPreferences';
|
||||
import { CODE_FONT_OPTION_MAP, DEFAULT_MONO_FONT, DEFAULT_UI_FONT, UI_FONT_OPTION_MAP } from '@/lib/fontOptions';
|
||||
import { ConfigUpdateOverlay } from '@/components/ui/ConfigUpdateOverlay';
|
||||
import { AboutDialog } from '@/components/ui/AboutDialog';
|
||||
import { RuntimeAPIProvider } from '@/contexts/RuntimeAPIProvider';
|
||||
import { registerRuntimeAPIs } from '@/contexts/runtimeAPIRegistry';
|
||||
import { OnboardingScreen } from '@/components/onboarding/OnboardingScreen';
|
||||
import { isCliAvailable } from '@/lib/desktop';
|
||||
import { useUIStore } from '@/stores/useUIStore';
|
||||
import type { RuntimeAPIs } from '@/lib/api/types';
|
||||
|
||||
const AboutDialogWrapper: React.FC = () => {
|
||||
const { isAboutDialogOpen, setAboutDialogOpen } = useUIStore();
|
||||
return (
|
||||
<AboutDialog
|
||||
open={isAboutDialogOpen}
|
||||
onOpenChange={setAboutDialogOpen}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
type AppProps = {
|
||||
apis: RuntimeAPIs;
|
||||
};
|
||||
@@ -200,6 +212,7 @@ function App({ apis }: AppProps) {
|
||||
<MainLayout />
|
||||
<Toaster />
|
||||
<ConfigUpdateOverlay />
|
||||
<AboutDialogWrapper />
|
||||
{showMemoryDebug && (
|
||||
<MemoryDebugPanel onClose={() => setShowMemoryDebug(false)} />
|
||||
)}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import React from 'react';
|
||||
import { RiDownloadLine, RiSettings3Line } from '@remixicon/react';
|
||||
import { RiDownloadLine, RiInformationLine, RiSettings3Line } from '@remixicon/react';
|
||||
import { toast } from 'sonner';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { ErrorBoundary } from '../ui/ErrorBoundary';
|
||||
import { useUIStore } from '@/stores/useUIStore';
|
||||
import { useUpdateCheck } from '@/hooks/useUpdateCheck';
|
||||
import { UpdateDialog } from '../ui/UpdateDialog';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip';
|
||||
|
||||
export const SIDEBAR_CONTENT_WIDTH = 264;
|
||||
const SIDEBAR_MIN_WIDTH = 200;
|
||||
@@ -20,7 +21,7 @@ interface SidebarProps {
|
||||
}
|
||||
|
||||
export const Sidebar: React.FC<SidebarProps> = ({ isOpen, isMobile, children }) => {
|
||||
const { sidebarWidth, setSidebarWidth, setSettingsDialogOpen } = useUIStore();
|
||||
const { sidebarWidth, setSidebarWidth, setSettingsDialogOpen, setAboutDialogOpen } = useUIStore();
|
||||
const [isResizing, setIsResizing] = React.useState(false);
|
||||
const startXRef = React.useRef(0);
|
||||
const startWidthRef = React.useRef(sidebarWidth || SIDEBAR_CONTENT_WIDTH);
|
||||
@@ -229,7 +230,7 @@ export const Sidebar: React.FC<SidebarProps> = ({ isOpen, isMobile, children })
|
||||
<RiSettings3Line className="h-4 w-4" />
|
||||
<span>Settings</span>
|
||||
</button>
|
||||
{(available || downloaded) && (
|
||||
{(available || downloaded) ? (
|
||||
<button
|
||||
onClick={() => setUpdateDialogOpen(true)}
|
||||
className={cn(
|
||||
@@ -243,6 +244,23 @@ export const Sidebar: React.FC<SidebarProps> = ({ isOpen, isMobile, children })
|
||||
<RiDownloadLine className="h-3.5 w-3.5" />
|
||||
<span>Update</span>
|
||||
</button>
|
||||
) : !isDesktopApp && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<button
|
||||
onClick={() => setAboutDialogOpen(true)}
|
||||
className={cn(
|
||||
'flex items-center justify-center rounded-md p-1.5',
|
||||
'text-muted-foreground',
|
||||
'hover:text-foreground hover:bg-muted/50',
|
||||
'transition-colors'
|
||||
)}
|
||||
>
|
||||
<RiInformationLine className="h-4 w-4" />
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top">About OpenChamber</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
} from '@/components/ui/dialog';
|
||||
import { OpenChamberLogo } from '@/components/ui/OpenChamberLogo';
|
||||
import { RiGithubFill, RiTwitterXFill } from '@remixicon/react';
|
||||
|
||||
declare const __APP_VERSION__: string | undefined;
|
||||
|
||||
interface AboutDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
}
|
||||
|
||||
export const AboutDialog: React.FC<AboutDialogProps> = ({
|
||||
open,
|
||||
onOpenChange,
|
||||
}) => {
|
||||
const [version, setVersion] = React.useState<string | null>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!open) return;
|
||||
|
||||
const isDesktop = typeof window !== 'undefined' && !!window.opencodeDesktop;
|
||||
|
||||
if (isDesktop) {
|
||||
const fetchVersion = async () => {
|
||||
try {
|
||||
const { getVersion } = await import('@tauri-apps/api/app');
|
||||
const v = await getVersion();
|
||||
setVersion(v);
|
||||
} catch {
|
||||
setVersion(typeof __APP_VERSION__ !== 'undefined' ? __APP_VERSION__ : null);
|
||||
}
|
||||
};
|
||||
fetchVersion();
|
||||
} else {
|
||||
setVersion(typeof __APP_VERSION__ !== 'undefined' ? __APP_VERSION__ : null);
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
const displayVersion = version;
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="max-w-xs p-6">
|
||||
<div className="flex flex-col items-center text-center space-y-4">
|
||||
<OpenChamberLogo width={64} height={64} />
|
||||
|
||||
<div className="space-y-1">
|
||||
<h2 className="text-lg font-semibold">OpenChamber</h2>
|
||||
{displayVersion && (
|
||||
<p className="typography-meta text-muted-foreground">
|
||||
Version {displayVersion}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p className="typography-meta text-muted-foreground">
|
||||
A beautiful interface for OpenCode AI coding agent
|
||||
</p>
|
||||
|
||||
<div className="flex items-center gap-4 pt-2">
|
||||
<a
|
||||
href="https://github.com/btriapitsyn/openchamber"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-1.5 typography-meta text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
<RiGithubFill className="h-4 w-4" />
|
||||
<span>GitHub</span>
|
||||
</a>
|
||||
<a
|
||||
href="https://x.com/btriapitsyn"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-1.5 typography-meta text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
<RiTwitterXFill className="h-4 w-4" />
|
||||
<span>@btriapitsyn</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p className="typography-meta text-muted-foreground/60 pt-2">
|
||||
Made with care for developers
|
||||
</p>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -12,6 +12,7 @@ import { isDesktopRuntime } from '@/lib/desktop';
|
||||
const MENU_ACTION_EVENT = 'openchamber:menu-action';
|
||||
|
||||
type MenuAction =
|
||||
| 'about'
|
||||
| 'settings'
|
||||
| 'command-palette'
|
||||
| 'new-session'
|
||||
@@ -39,6 +40,7 @@ export const useMenuActions = (
|
||||
setSessionCreateDialogOpen,
|
||||
setActiveMainTab,
|
||||
setSettingsDialogOpen,
|
||||
setAboutDialogOpen,
|
||||
} = useUIStore();
|
||||
const { agents } = useConfigStore();
|
||||
const { setDirectory } = useDirectoryStore();
|
||||
@@ -72,6 +74,10 @@ export const useMenuActions = (
|
||||
const action = (event as CustomEvent<MenuAction>).detail;
|
||||
|
||||
switch (action) {
|
||||
case 'about':
|
||||
setAboutDialogOpen(true);
|
||||
break;
|
||||
|
||||
case 'settings':
|
||||
setSettingsDialogOpen(true);
|
||||
break;
|
||||
@@ -185,6 +191,7 @@ export const useMenuActions = (
|
||||
setSessionCreateDialogOpen,
|
||||
setActiveMainTab,
|
||||
setSettingsDialogOpen,
|
||||
setAboutDialogOpen,
|
||||
setThemeMode,
|
||||
agents,
|
||||
onToggleMemoryDebug,
|
||||
|
||||
@@ -26,6 +26,7 @@ interface UIStore {
|
||||
isMobile: boolean;
|
||||
isCommandPaletteOpen: boolean;
|
||||
isHelpDialogOpen: boolean;
|
||||
isAboutDialogOpen: boolean;
|
||||
isSessionCreateDialogOpen: boolean;
|
||||
isSettingsDialogOpen: boolean;
|
||||
sidebarSection: SidebarSection;
|
||||
@@ -58,6 +59,7 @@ interface UIStore {
|
||||
setCommandPaletteOpen: (open: boolean) => void;
|
||||
toggleHelpDialog: () => void;
|
||||
setHelpDialogOpen: (open: boolean) => void;
|
||||
setAboutDialogOpen: (open: boolean) => void;
|
||||
setSessionCreateDialogOpen: (open: boolean) => void;
|
||||
setSettingsDialogOpen: (open: boolean) => void;
|
||||
applyTheme: () => void;
|
||||
@@ -93,6 +95,7 @@ export const useUIStore = create<UIStore>()(
|
||||
isMobile: false,
|
||||
isCommandPaletteOpen: false,
|
||||
isHelpDialogOpen: false,
|
||||
isAboutDialogOpen: false,
|
||||
isSessionCreateDialogOpen: false,
|
||||
isSettingsDialogOpen: false,
|
||||
sidebarSection: 'sessions',
|
||||
@@ -192,6 +195,10 @@ export const useUIStore = create<UIStore>()(
|
||||
set({ isHelpDialogOpen: open });
|
||||
},
|
||||
|
||||
setAboutDialogOpen: (open) => {
|
||||
set({ isAboutDialogOpen: open });
|
||||
},
|
||||
|
||||
setSessionCreateDialogOpen: (open) => {
|
||||
set({ isSessionCreateDialogOpen: open });
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user