feat: add branding, community links, GitHub star badge, and About settings tab

This commit is contained in:
simosmik
2026-04-10 13:06:16 +00:00
parent a8dab0edcf
commit 2207d05c1c
52 changed files with 654 additions and 86 deletions

View File

@@ -12,7 +12,7 @@ export default function AuthLoadingScreen() {
</div>
</div>
<h1 className="mb-2 text-2xl font-bold text-foreground">Claude Code UI</h1>
<h1 className="mb-2 text-2xl font-bold text-foreground">CloudCLI</h1>
<div className="flex items-center justify-center space-x-2">
{loadingDotAnimationDelays.map((delay) => (

View File

@@ -1,5 +1,6 @@
import type { ReactNode } from 'react';
import { MessageSquare } from 'lucide-react';
import { IS_PLATFORM } from '../../../constants/config';
type AuthScreenLayoutProps = {
title: string;
@@ -37,6 +38,22 @@ export default function AuthScreenLayout({
<div className="text-center">
<p className="text-sm text-muted-foreground">{footerText}</p>
</div>
{!IS_PLATFORM && (
<div className="flex items-center justify-center gap-1.5 pt-2">
<svg className="h-3.5 w-3.5 text-muted-foreground/50" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" />
</svg>
<a
href="https://github.com/siteboon/claudecodeui"
target="_blank"
rel="noopener noreferrer"
className="text-xs text-muted-foreground/50 transition-colors hover:text-muted-foreground"
>
CloudCLI is open source
</a>
</div>
)}
</div>
</div>
</div>

View File

@@ -58,7 +58,7 @@ export default function LoginForm() {
<AuthScreenLayout
title={t('login.title')}
description={t('login.description')}
footerText="Enter your credentials to access Claude Code UI"
footerText="Enter your credentials to access CloudCLI"
>
<form onSubmit={handleSubmit} className="space-y-4">
<AuthInputField

View File

@@ -82,7 +82,7 @@ export default function SetupForm() {
return (
<AuthScreenLayout
title="Welcome to Claude Code UI"
title="Welcome to CloudCLI"
description="Set up your account to get started"
footerText="This is a single-user system. Only one account can be created."
logo={<img src="/logo.svg" alt="CloudCLI" className="h-16 w-16" />}

View File

@@ -1,6 +1,6 @@
import type { Dispatch, SetStateAction } from 'react';
export type SettingsMainTab = 'agents' | 'appearance' | 'git' | 'api' | 'tasks' | 'notifications' | 'plugins';
export type SettingsMainTab = 'agents' | 'appearance' | 'git' | 'api' | 'tasks' | 'notifications' | 'plugins' | 'about';
export type AgentProvider = 'claude' | 'cursor' | 'codex' | 'gemini';
export type AgentCategory = 'account' | 'permissions' | 'mcp';
export type ProjectSortOrder = 'name' | 'date';

View File

@@ -0,0 +1,46 @@
import { ExternalLink, Lock } from 'lucide-react';
import type { ReactNode } from 'react';
const CLOUDCLI_URL = 'https://cloudcli.ai';
type PremiumFeatureCardProps = {
icon: ReactNode;
title: string;
description: string;
ctaText?: string;
};
export default function PremiumFeatureCard({
icon,
title,
description,
ctaText = 'Available with CloudCLI Pro',
}: PremiumFeatureCardProps) {
return (
<div className="rounded-xl border border-dashed border-border/60 bg-muted/20 p-5">
<div className="flex items-start gap-3">
<div className="flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg bg-muted/60 text-muted-foreground">
{icon}
</div>
<div className="min-w-0 flex-1">
<div className="flex items-center gap-2">
<h4 className="text-sm font-medium text-foreground">{title}</h4>
<Lock className="h-3 w-3 text-muted-foreground/60" />
</div>
<p className="mt-1 text-xs leading-relaxed text-muted-foreground">
{description}
</p>
<a
href={CLOUDCLI_URL}
target="_blank"
rel="noopener noreferrer"
className="mt-3 inline-flex items-center gap-1 text-xs font-medium text-primary transition-colors hover:underline"
>
{ctaText}
<ExternalLink className="h-3 w-3" />
</a>
</div>
</div>
</div>
);
}

View File

@@ -12,6 +12,7 @@ import GitSettingsTab from '../view/tabs/git-settings/GitSettingsTab';
import NotificationsSettingsTab from '../view/tabs/NotificationsSettingsTab';
import TasksSettingsTab from '../view/tabs/tasks-settings/TasksSettingsTab';
import PluginSettingsTab from '../../plugins/view/PluginSettingsTab';
import AboutTab from '../view/tabs/AboutTab';
import { useSettingsController } from '../hooks/useSettingsController';
import { useWebPush } from '../../../hooks/useWebPush';
import type { SettingsProps } from '../types/types';
@@ -206,6 +207,8 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'agents' }: Set
{activeTab === 'api' && <CredentialsSettingsTab />}
{activeTab === 'plugins' && <PluginSettingsTab />}
{activeTab === 'about' && <AboutTab />}
</div>
</main>
</div>

View File

@@ -1,4 +1,4 @@
import { GitBranch, Key, Puzzle } from 'lucide-react';
import { GitBranch, Info, Key, Puzzle } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import type { SettingsMainTab } from '../types/types';
@@ -22,6 +22,7 @@ const TAB_CONFIG: MainTabConfig[] = [
{ id: 'tasks', labelKey: 'mainTabs.tasks' },
{ id: 'notifications', labelKey: 'mainTabs.notifications' },
{ id: 'plugins', labelKey: 'mainTabs.plugins', icon: Puzzle },
{ id: 'about', labelKey: 'mainTabs.about', icon: Info },
];
export default function SettingsMainTabs({ activeTab, onChange }: SettingsMainTabsProps) {

View File

@@ -1,4 +1,4 @@
import { Bell, Bot, GitBranch, Key, ListChecks, Palette, Puzzle } from 'lucide-react';
import { Bell, Bot, GitBranch, Info, Key, ListChecks, Palette, Puzzle } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { cn } from '../../../lib/utils';
import { PillBar, Pill } from '../../../shared/view/ui';
@@ -23,6 +23,7 @@ const NAV_ITEMS: NavItem[] = [
{ id: 'tasks', labelKey: 'mainTabs.tasks', icon: ListChecks },
{ id: 'plugins', labelKey: 'mainTabs.plugins', icon: Puzzle },
{ id: 'notifications', labelKey: 'mainTabs.notifications', icon: Bell },
{ id: 'about', labelKey: 'mainTabs.about', icon: Info },
];
export default function SettingsSidebar({ activeTab, onChange }: SettingsSidebarProps) {

View File

@@ -0,0 +1,166 @@
import { ExternalLink, MessageSquare, Star } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { IS_PLATFORM } from '../../../../constants/config';
import { useVersionCheck } from '../../../../hooks/useVersionCheck';
import PremiumFeatureCard from '../PremiumFeatureCard';
import { Cloud, Users } from 'lucide-react';
const GITHUB_REPO_URL = 'https://github.com/siteboon/claudecodeui';
const DISCORD_URL = 'https://discord.gg/buxwujPNRE';
const DOCS_URL = 'https://cloudcli.ai/docs/plugin-overview';
const CLOUDCLI_URL = 'https://cloudcli.ai';
function GitHubIcon({ className }: { className?: string }) {
return (
<svg className={className} fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" />
</svg>
);
}
function DiscordIcon({ className }: { className?: string }) {
return (
<svg className={className} fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.095 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.095 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z" />
</svg>
);
}
export default function AboutTab() {
const { t } = useTranslation('settings');
const { updateAvailable, latestVersion, currentVersion, releaseInfo } = useVersionCheck('siteboon', 'claudecodeui');
const releasesUrl = releaseInfo?.htmlUrl || `${GITHUB_REPO_URL}/releases`;
return (
<div className="space-y-6">
{/* Logo + name + version */}
<div className="flex items-center gap-3">
<div className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-xl bg-primary/90 shadow-sm">
<MessageSquare className="h-5 w-5 text-primary-foreground" />
</div>
<div>
<div className="flex items-center gap-2">
<span className="text-base font-semibold text-foreground">CloudCLI</span>
<a
href={releasesUrl}
target="_blank"
rel="noopener noreferrer"
className="rounded-full bg-muted px-2 py-0.5 text-[11px] font-medium text-muted-foreground transition-colors hover:text-foreground"
>
v{currentVersion}
</a>
{updateAvailable && latestVersion && (
<a
href={releasesUrl}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-1 rounded-full bg-green-500/10 px-2 py-0.5 text-[10px] font-medium text-green-600 transition-colors hover:bg-green-500/20 dark:text-green-400"
>
{t('apiKeys.version.updateAvailable', { version: latestVersion })}
<ExternalLink className="h-2.5 w-2.5" />
</a>
)}
</div>
<p className="mt-0.5 text-sm text-muted-foreground">
Open-source AI coding assistant interface
</p>
</div>
</div>
{/* Star on GitHub button */}
<a
href={GITHUB_REPO_URL}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-2 rounded-lg border border-border/60 bg-background px-3.5 py-2 text-sm font-medium text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground"
>
<GitHubIcon className="h-4 w-4" />
<Star className="h-3.5 w-3.5" />
<span>Star on GitHub</span>
</a>
{/* Links */}
<div className="flex flex-wrap gap-4 text-sm">
<a
href={GITHUB_REPO_URL}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-1.5 text-muted-foreground transition-colors hover:text-foreground"
>
<GitHubIcon className="h-4 w-4" />
GitHub
</a>
<a
href={DISCORD_URL}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-1.5 text-muted-foreground transition-colors hover:text-foreground"
>
<DiscordIcon className="h-4 w-4" />
Discord
</a>
<a
href={DOCS_URL}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-1.5 text-muted-foreground transition-colors hover:text-foreground"
>
<ExternalLink className="h-3.5 w-3.5" />
Docs
</a>
<a
href={CLOUDCLI_URL}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-1.5 text-muted-foreground transition-colors hover:text-foreground"
>
<ExternalLink className="h-3.5 w-3.5" />
cloudcli.ai
</a>
</div>
{/* Hosted CTA (OSS mode only) */}
{!IS_PLATFORM && (
<div className="rounded-xl border border-primary/10 bg-primary/5 p-4">
<h4 className="text-sm font-medium text-foreground">Try CloudCLI Hosted</h4>
<p className="mt-1 text-xs text-muted-foreground">
Team collaboration, shared MCP configs, settings sync across environments, and managed infrastructure.
</p>
<a
href={CLOUDCLI_URL}
target="_blank"
rel="noopener noreferrer"
className="mt-2 inline-flex items-center gap-1 text-xs font-medium text-primary transition-colors hover:underline"
>
Learn more
<ExternalLink className="h-3 w-3" />
</a>
</div>
)}
{/* Premium feature placeholders (OSS mode only) */}
{!IS_PLATFORM && (
<div className="space-y-4 border-t border-border/50 pt-6">
<h3 className="text-sm font-medium text-foreground">CloudCLI Pro Features</h3>
<PremiumFeatureCard
icon={<Cloud className="h-5 w-5" />}
title="Sync Settings"
description="Keep your preferences, MCP configs, and theme in sync across all your environments."
/>
<PremiumFeatureCard
icon={<Users className="h-5 w-5" />}
title="Team Management"
description="Multiple users, role-based access, and shared projects for your team."
/>
</div>
)}
{/* License */}
<div className="border-t border-border/50 pt-4">
<p className="text-xs text-muted-foreground/60">
Licensed under AGPL-3.0
</p>
</div>
</div>
);
}

View File

@@ -1,6 +1,8 @@
import { Edit3, Globe, Plus, Server, Terminal, Trash2, Zap } from 'lucide-react';
import { Edit3, Globe, Plus, Server, Terminal, Trash2, Users, Zap } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { Badge, Button } from '../../../../../../../shared/view/ui';
import { IS_PLATFORM } from '../../../../../../../constants/config';
import PremiumFeatureCard from '../../../../PremiumFeatureCard';
import type { McpServer, McpToolsResult, McpTestResult } from '../../../../../types/types';
const getTransportIcon = (type: string | undefined) => {
@@ -179,6 +181,14 @@ function ClaudeMcpServers({
<div className="py-8 text-center text-muted-foreground">{t('mcpServers.empty')}</div>
)}
</div>
{!IS_PLATFORM && (
<PremiumFeatureCard
icon={<Users className="h-5 w-5" />}
title="Team MCP Configs"
description="Share MCP server configurations across your team. Everyone stays in sync automatically."
/>
)}
</div>
);
}

View File

@@ -1,14 +1,11 @@
import { useTranslation } from 'react-i18next';
import { useVersionCheck } from '../../../../../hooks/useVersionCheck';
import { useCredentialsSettings } from '../../../hooks/useCredentialsSettings';
import ApiKeysSection from './sections/ApiKeysSection';
import GithubCredentialsSection from './sections/GithubCredentialsSection';
import NewApiKeyAlert from './sections/NewApiKeyAlert';
import VersionInfoSection from './sections/VersionInfoSection';
export default function CredentialsSettingsTab() {
const { t } = useTranslation('settings');
const { updateAvailable, latestVersion, currentVersion, releaseInfo } = useVersionCheck('siteboon', 'claudecodeui');
const {
apiKeys,
githubCredentials,
@@ -89,12 +86,6 @@ export default function CredentialsSettingsTab() {
onDeleteGithubCredential={deleteGithubCredential}
/>
<VersionInfoSection
currentVersion={currentVersion}
updateAvailable={updateAvailable}
latestVersion={latestVersion}
releaseInfo={releaseInfo}
/>
</div>
);
}

View File

@@ -1,7 +1,29 @@
import { ExternalLink } from 'lucide-react';
import { ExternalLink, Star, MessageSquare } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { IS_PLATFORM } from '../../../../../../constants/config';
import type { ReleaseInfo } from '../../../../../../types/sharedTypes';
const GITHUB_REPO_URL = 'https://github.com/siteboon/claudecodeui';
const DISCORD_URL = 'https://discord.gg/buxwujPNRE';
const DOCS_URL = 'https://cloudcli.ai/docs/plugin-overview';
const CLOUDCLI_URL = 'https://cloudcli.ai';
function GitHubIcon({ className }: { className?: string }) {
return (
<svg className={className} fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" />
</svg>
);
}
function DiscordIcon({ className }: { className?: string }) {
return (
<svg className={className} fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.095 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.095 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z" />
</svg>
);
}
type VersionInfoSectionProps = {
currentVersion: string;
updateAvailable: boolean;
@@ -16,29 +38,115 @@ export default function VersionInfoSection({
releaseInfo,
}: VersionInfoSectionProps) {
const { t } = useTranslation('settings');
const releasesUrl = releaseInfo?.htmlUrl || 'https://github.com/siteboon/claudecodeui/releases';
const releasesUrl = releaseInfo?.htmlUrl || `${GITHUB_REPO_URL}/releases`;
return (
<div className="border-t border-border/50 pt-6">
<div className="flex items-center justify-between text-xs italic text-muted-foreground/60">
{/* About CloudCLI */}
<div className="space-y-4">
{/* Logo + name + version */}
<div className="flex items-center gap-3">
<div className="flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg bg-primary/90 shadow-sm">
<MessageSquare className="h-4.5 w-4.5 text-primary-foreground" />
</div>
<div>
<div className="flex items-center gap-2">
<span className="text-sm font-semibold text-foreground">CloudCLI</span>
<a
href={releasesUrl}
target="_blank"
rel="noopener noreferrer"
className="rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground transition-colors hover:text-foreground"
>
v{currentVersion}
</a>
{updateAvailable && latestVersion && (
<a
href={releasesUrl}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-1 rounded-full bg-green-500/10 px-2 py-0.5 text-[10px] font-medium text-green-600 transition-colors hover:bg-green-500/20 dark:text-green-400"
>
{t('apiKeys.version.updateAvailable', { version: latestVersion })}
<ExternalLink className="h-2.5 w-2.5" />
</a>
)}
</div>
<p className="mt-0.5 text-xs text-muted-foreground">
Open-source AI coding assistant interface
</p>
</div>
</div>
{/* Star on GitHub button */}
<a
href={releasesUrl}
href={GITHUB_REPO_URL}
target="_blank"
rel="noopener noreferrer"
className="transition-colors hover:text-muted-foreground"
className="inline-flex items-center gap-2 rounded-lg border border-border/60 bg-background px-3 py-1.5 text-xs font-medium text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground"
>
v{currentVersion}
<GitHubIcon className="h-4 w-4" />
<Star className="h-3.5 w-3.5" />
<span>Star on GitHub</span>
</a>
{updateAvailable && latestVersion && (
{/* Links */}
<div className="flex flex-wrap gap-3 text-xs">
<a
href={releasesUrl}
href={GITHUB_REPO_URL}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-1.5 rounded-full bg-green-500/10 px-2 py-0.5 font-medium not-italic text-green-600 transition-colors hover:bg-green-500/20 dark:text-green-400"
className="flex items-center gap-1 text-muted-foreground transition-colors hover:text-foreground"
>
<span className="text-[10px]">{t('apiKeys.version.updateAvailable', { version: latestVersion })}</span>
<ExternalLink className="h-2.5 w-2.5" />
<GitHubIcon className="h-3.5 w-3.5" />
GitHub
</a>
<a
href={DISCORD_URL}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-1 text-muted-foreground transition-colors hover:text-foreground"
>
<DiscordIcon className="h-3.5 w-3.5" />
Discord
</a>
<a
href={DOCS_URL}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-1 text-muted-foreground transition-colors hover:text-foreground"
>
<ExternalLink className="h-3 w-3" />
Docs
</a>
<a
href={CLOUDCLI_URL}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-1 text-muted-foreground transition-colors hover:text-foreground"
>
<ExternalLink className="h-3 w-3" />
cloudcli.ai
</a>
</div>
{/* Hosted CTA (OSS mode only) */}
{!IS_PLATFORM && (
<div className="rounded-xl border border-primary/10 bg-primary/5 p-4">
<h4 className="text-sm font-medium text-foreground">Try CloudCLI Hosted</h4>
<p className="mt-1 text-xs text-muted-foreground">
Team collaboration, shared MCP configs, settings sync across environments, and managed infrastructure.
</p>
<a
href={CLOUDCLI_URL}
target="_blank"
rel="noopener noreferrer"
className="mt-2 inline-flex items-center gap-1 text-xs font-medium text-primary transition-colors hover:underline"
>
Learn more
<ExternalLink className="h-3 w-3" />
</a>
</div>
)}
</div>
</div>

View File

@@ -266,6 +266,7 @@ function Sidebar({
updateAvailable={updateAvailable}
releaseInfo={releaseInfo}
latestVersion={latestVersion}
currentVersion={currentVersion}
onShowVersionModal={() => setShowVersionModal(true)}
onShowSettings={onShowSettings}
projectListProps={projectListProps}

View File

@@ -0,0 +1,48 @@
import { Star, X } from 'lucide-react';
import { useGitHubStars } from '../../../../hooks/useGitHubStars';
import { IS_PLATFORM } from '../../../../constants/config';
const GITHUB_REPO_URL = 'https://github.com/siteboon/claudecodeui';
function GitHubIcon({ className }: { className?: string }) {
return (
<svg className={className} fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" />
</svg>
);
}
export default function GitHubStarBadge() {
const { formattedCount, isDismissed, dismiss } = useGitHubStars('siteboon', 'claudecodeui');
if (IS_PLATFORM || isDismissed) return null;
return (
<div className="group/star relative hidden md:block">
<a
href={GITHUB_REPO_URL}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-1.5 rounded-lg border border-border/50 bg-muted/30 px-2.5 py-1 text-xs text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground"
>
<GitHubIcon className="h-3.5 w-3.5" />
<Star className="h-3 w-3" />
<span className="font-medium">Star</span>
{formattedCount && (
<span className="border-l border-border/50 pl-1.5 tabular-nums">{formattedCount}</span>
)}
</a>
<button
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
dismiss();
}}
className="absolute -right-1.5 -top-1.5 hidden h-4 w-4 items-center justify-center rounded-full border border-border/50 bg-muted text-muted-foreground transition-colors hover:text-foreground group-hover/star:flex"
aria-label="Dismiss"
>
<X className="h-2.5 w-2.5" />
</button>
</div>
);
}

View File

@@ -1,7 +1,8 @@
import { Settings, Sparkles, PanelLeftOpen } from 'lucide-react';
import { Settings, Sparkles, PanelLeftOpen, Bug } from 'lucide-react';
import type { TFunction } from 'i18next';
const DISCORD_INVITE_URL = 'https://discord.gg/buxwujPNRE';
const GITHUB_ISSUES_URL = 'https://github.com/siteboon/claudecodeui/issues/new';
function DiscordIcon({ className }: { className?: string }) {
return (
@@ -50,6 +51,18 @@ export default function SidebarCollapsed({
<Settings className="h-4 w-4 text-muted-foreground transition-colors group-hover:text-foreground" />
</button>
{/* Report Issue */}
<a
href={GITHUB_ISSUES_URL}
target="_blank"
rel="noopener noreferrer"
className="group flex h-8 w-8 items-center justify-center rounded-lg transition-colors hover:bg-accent/80"
aria-label={t('actions.reportIssue')}
title={t('actions.reportIssue')}
>
<Bug className="h-4 w-4 text-muted-foreground transition-colors group-hover:text-foreground" />
</a>
{/* Discord */}
<a
href={DISCORD_INVITE_URL}

View File

@@ -56,6 +56,7 @@ type SidebarContentProps = {
updateAvailable: boolean;
releaseInfo: ReleaseInfo | null;
latestVersion: string | null;
currentVersion: string;
onShowVersionModal: () => void;
onShowSettings: () => void;
projectListProps: SidebarProjectListProps;
@@ -83,6 +84,7 @@ export default function SidebarContent({
updateAvailable,
releaseInfo,
latestVersion,
currentVersion,
onShowVersionModal,
onShowSettings,
projectListProps,
@@ -217,6 +219,7 @@ export default function SidebarContent({
updateAvailable={updateAvailable}
releaseInfo={releaseInfo}
latestVersion={latestVersion}
currentVersion={currentVersion}
onShowVersionModal={onShowVersionModal}
onShowSettings={onShowSettings}
t={t}

View File

@@ -1,7 +1,11 @@
import { Settings, ArrowUpCircle } from 'lucide-react';
import { Settings, ArrowUpCircle, Bug } from 'lucide-react';
import type { TFunction } from 'i18next';
import { IS_PLATFORM } from '../../../../constants/config';
import type { ReleaseInfo } from '../../../../types/sharedTypes';
const GITHUB_ISSUES_URL = 'https://github.com/siteboon/claudecodeui/issues/new';
const GITHUB_REPO_URL = 'https://github.com/siteboon/claudecodeui';
const DISCORD_INVITE_URL = 'https://discord.gg/buxwujPNRE';
function DiscordIcon({ className }: { className?: string }) {
@@ -16,6 +20,7 @@ type SidebarFooterProps = {
updateAvailable: boolean;
releaseInfo: ReleaseInfo | null;
latestVersion: string | null;
currentVersion: string;
onShowVersionModal: () => void;
onShowSettings: () => void;
t: TFunction;
@@ -25,6 +30,7 @@ export default function SidebarFooter({
updateAvailable,
releaseInfo,
latestVersion,
currentVersion,
onShowVersionModal,
onShowSettings,
t,
@@ -79,11 +85,24 @@ export default function SidebarFooter({
</>
)}
{/* Discord + Settings */}
{/* Community + Settings */}
<div className="nav-divider" />
{/* Desktop Discord */}
{/* Desktop Report Issue */}
<div className="hidden px-2 pt-1.5 md:block">
<a
href={GITHUB_ISSUES_URL}
target="_blank"
rel="noopener noreferrer"
className="flex w-full items-center gap-2 rounded-lg px-2.5 py-1.5 text-muted-foreground transition-colors hover:bg-accent/60 hover:text-foreground"
>
<Bug className="h-3.5 w-3.5" />
<span className="text-sm">{t('actions.reportIssue')}</span>
</a>
</div>
{/* Desktop Discord */}
<div className="hidden px-2 md:block">
<a
href={DISCORD_INVITE_URL}
target="_blank"
@@ -106,8 +125,37 @@ export default function SidebarFooter({
</button>
</div>
{/* Mobile Discord */}
{/* Desktop version brand line (OSS mode only) */}
{!IS_PLATFORM && (
<div className="hidden px-3 py-2 text-center md:block">
<a
href={GITHUB_REPO_URL}
target="_blank"
rel="noopener noreferrer"
className="text-[10px] text-muted-foreground/40 transition-colors hover:text-muted-foreground"
>
CloudCLI v{currentVersion} {t('branding.openSource')}
</a>
</div>
)}
{/* Mobile Report Issue */}
<div className="px-3 pt-3 md:hidden">
<a
href={GITHUB_ISSUES_URL}
target="_blank"
rel="noopener noreferrer"
className="flex h-12 w-full items-center gap-3.5 rounded-xl bg-muted/40 px-4 transition-all hover:bg-muted/60 active:scale-[0.98]"
>
<div className="flex h-8 w-8 items-center justify-center rounded-xl bg-background/80">
<Bug className="w-4.5 h-4.5 text-muted-foreground" />
</div>
<span className="text-base font-medium text-foreground">{t('actions.reportIssue')}</span>
</a>
</div>
{/* Mobile Discord */}
<div className="px-3 pt-2 md:hidden">
<a
href={DISCORD_INVITE_URL}
target="_blank"

View File

@@ -3,6 +3,7 @@ import type { TFunction } from 'i18next';
import { Button, Input } from '../../../../shared/view/ui';
import { IS_PLATFORM } from '../../../../constants/config';
import { cn } from '../../../../lib/utils';
import GitHubStarBadge from './GitHubStarBadge';
type SearchMode = 'projects' | 'conversations';
@@ -106,6 +107,8 @@ export default function SidebarHeader({
</div>
</div>
<GitHubStarBadge />
{/* Search bar */}
{projectsCount > 0 && !isLoading && (
<div className="mt-2.5 space-y-2">

View File

@@ -0,0 +1,77 @@
import { useState, useEffect, useCallback } from 'react';
const CACHE_KEY = 'CLOUDCLI_GITHUB_STARS';
const DISMISS_KEY = 'CLOUDCLI_HIDE_GITHUB_STAR';
const CACHE_TTL = 60 * 60 * 1000; // 1 hour
type CachedStars = {
count: number;
timestamp: number;
};
export const useGitHubStars = (owner: string, repo: string) => {
const [starCount, setStarCount] = useState<number | null>(null);
const [isDismissed, setIsDismissed] = useState(() => {
try {
return localStorage.getItem(DISMISS_KEY) === 'true';
} catch {
return false;
}
});
useEffect(() => {
if (isDismissed) return;
// Check cache first
try {
const cached = localStorage.getItem(CACHE_KEY);
if (cached) {
const parsed: CachedStars = JSON.parse(cached);
if (Date.now() - parsed.timestamp < CACHE_TTL) {
setStarCount(parsed.count);
return;
}
}
} catch {
// ignore
}
const fetchStars = async () => {
try {
const response = await fetch(`https://api.github.com/repos/${owner}/${repo}`);
if (!response.ok) return;
const data = await response.json();
const count = data.stargazers_count;
if (typeof count === 'number') {
setStarCount(count);
try {
localStorage.setItem(CACHE_KEY, JSON.stringify({ count, timestamp: Date.now() }));
} catch {
// ignore
}
}
} catch {
// silent fail
}
};
void fetchStars();
}, [owner, repo, isDismissed]);
const dismiss = useCallback(() => {
setIsDismissed(true);
try {
localStorage.setItem(DISMISS_KEY, 'true');
} catch {
// ignore
}
}, []);
const formattedCount = starCount !== null
? starCount >= 1000
? `${(starCount / 1000).toFixed(1)}k`
: `${starCount}`
: null;
return { starCount, formattedCount, isDismissed, dismiss };
};

View File

@@ -1,7 +1,7 @@
{
"login": {
"title": "Willkommen zurück",
"description": "Meld dich bei deinem Claude Code UI-Konto an",
"description": "Meld dich bei deinem CloudCLI-Konto an",
"username": "Benutzername",
"password": "Passwort",
"submit": "Anmelden",

View File

@@ -84,7 +84,7 @@
"openInEditor": "Im Editor öffnen"
},
"mainContent": {
"loading": "Claude Code UI wird geladen",
"loading": "CloudCLI wird geladen",
"settingUpWorkspace": "Arbeitsbereich wird eingerichtet...",
"chooseProject": "Projekt auswählen",
"selectProjectDescription": "Wähl ein Projekt aus der Seitenleiste, um mit Claude zu programmieren. Jedes Projekt enthält deine Chat-Sitzungen und den Dateiverlauf.",

View File

@@ -105,7 +105,8 @@
"git": "Git",
"apiTokens": "API & Token",
"tasks": "Aufgaben",
"plugins": "Plugins"
"plugins": "Plugins",
"about": "Info"
},
"appearanceSettings": {
"darkMode": {

View File

@@ -20,7 +20,7 @@
"runClaudeCli": "Führ Claude CLI in einem Projektverzeichnis aus, um zu beginnen"
},
"app": {
"title": "Claude Code UI",
"title": "CloudCLI",
"subtitle": "KI-Programmierassistent-Oberfläche"
},
"sessions": {
@@ -65,7 +65,12 @@
"save": "Speichern",
"delete": "Löschen",
"rename": "Umbenennen",
"joinCommunity": "Community beitreten"
"joinCommunity": "Community beitreten",
"reportIssue": "Problem melden",
"starOnGithub": "Stern auf GitHub"
},
"branding": {
"openSource": "Open Source"
},
"status": {
"active": "Aktiv",

View File

@@ -1,7 +1,7 @@
{
"login": {
"title": "Welcome Back",
"description": "Sign in to your Claude Code UI account",
"description": "Sign in to your CloudCLI self-hosted account",
"username": "Username",
"password": "Password",
"submit": "Sign In",

View File

@@ -84,7 +84,7 @@
"openInEditor": "Open in Editor"
},
"mainContent": {
"loading": "Loading Claude Code UI",
"loading": "Loading CloudCLI",
"settingUpWorkspace": "Setting up your workspace...",
"chooseProject": "Choose Your Project",
"selectProjectDescription": "Select a project from the sidebar to start coding with Claude. Each project contains your chat sessions and file history.",

View File

@@ -106,8 +106,8 @@
"apiTokens": "API & Tokens",
"tasks": "Tasks",
"notifications": "Notifications",
"plugins": "Plugins"
"plugins": "Plugins",
"about": "About"
},
"notifications": {
"title": "Notifications",

View File

@@ -20,7 +20,7 @@
"runClaudeCli": "Run Claude CLI in a project directory to get started"
},
"app": {
"title": "Claude Code UI",
"title": "CloudCLI",
"subtitle": "AI coding assistant interface"
},
"sessions": {
@@ -65,7 +65,12 @@
"save": "Save",
"delete": "Delete",
"rename": "Rename",
"joinCommunity": "Join Community"
"joinCommunity": "Join Community",
"reportIssue": "Report Issue",
"starOnGithub": "Star on GitHub"
},
"branding": {
"openSource": "Open Source"
},
"status": {
"active": "Active",

View File

@@ -1,7 +1,7 @@
{
"login": {
"title": "おかえりなさい",
"description": "Claude Code UIアカウントにサインイン",
"description": "CloudCLIアカウントにサインイン",
"username": "ユーザー名",
"password": "パスワード",
"submit": "サインイン",

View File

@@ -84,7 +84,7 @@
"openInEditor": "エディタで開く"
},
"mainContent": {
"loading": "Claude Code UI を読み込んでいます",
"loading": "CloudCLI を読み込んでいます",
"settingUpWorkspace": "ワークスペースを準備しています...",
"chooseProject": "プロジェクトを選択",
"selectProjectDescription": "サイドバーからプロジェクトを選択して、Claudeとコーディングを始めましょう。各プロジェクトにはチャットセッションとファイル履歴が含まれています。",

View File

@@ -106,8 +106,8 @@
"apiTokens": "API & トークン",
"tasks": "タスク",
"notifications": "通知",
"plugins": "プラグイン"
"plugins": "プラグイン",
"about": "概要"
},
"notifications": {
"title": "通知",

View File

@@ -20,7 +20,7 @@
"runClaudeCli": "プロジェクトディレクトリでClaude CLIを実行して始めましょう"
},
"app": {
"title": "Claude Code UI",
"title": "CloudCLI",
"subtitle": "AIコーディングアシスタント"
},
"sessions": {
@@ -64,7 +64,12 @@
"save": "保存",
"delete": "削除",
"rename": "名前の変更",
"joinCommunity": "コミュニティに参加"
"joinCommunity": "コミュニティに参加",
"reportIssue": "問題を報告",
"starOnGithub": "GitHubでスター"
},
"branding": {
"openSource": "オープンソース"
},
"status": {
"active": "アクティブ",

View File

@@ -1,7 +1,7 @@
{
"login": {
"title": "다시 오신 것을 환영합니다",
"description": "Claude Code UI 계정에 로그인하세요",
"description": "CloudCLI 계정에 로그인하세요",
"username": "사용자명",
"password": "비밀번호",
"submit": "로그인",

View File

@@ -84,7 +84,7 @@
"openInEditor": "에디터에서 열기"
},
"mainContent": {
"loading": "Claude Code UI 로딩 중",
"loading": "CloudCLI 로딩 중",
"settingUpWorkspace": "워크스페이스 설정 중...",
"chooseProject": "프로젝트 선택",
"selectProjectDescription": "사이드바에서 프로젝트를 선택하여 Claude와 코딩을 시작하세요. 각 프로젝트에는 채팅 세션과 파일 히스토리가 포함됩니다.",

View File

@@ -106,8 +106,8 @@
"apiTokens": "API & 토큰",
"tasks": "작업",
"notifications": "알림",
"plugins": "플러그인"
"plugins": "플러그인",
"about": "정보"
},
"notifications": {
"title": "알림",

View File

@@ -20,7 +20,7 @@
"runClaudeCli": "프로젝트 디렉토리에서 Claude CLI를 실행하여 시작하세요"
},
"app": {
"title": "Claude Code UI",
"title": "CloudCLI",
"subtitle": "AI 코딩 어시스턴트 UI"
},
"sessions": {
@@ -64,7 +64,12 @@
"save": "저장",
"delete": "삭제",
"rename": "이름 변경",
"joinCommunity": "커뮤니티 참여"
"joinCommunity": "커뮤니티 참여",
"reportIssue": "문제 신고",
"starOnGithub": "GitHub에서 스타"
},
"branding": {
"openSource": "오픈 소스"
},
"status": {
"active": "활성",

View File

@@ -1,7 +1,7 @@
{
"login": {
"title": "Добро пожаловать",
"description": "Войдите в свой аккаунт Claude Code UI",
"description": "Войдите в свой аккаунт CloudCLI",
"username": "Имя пользователя",
"password": "Пароль",
"submit": "Войти",

View File

@@ -84,7 +84,7 @@
"openInEditor": "Открыть в редакторе"
},
"mainContent": {
"loading": "Загрузка Claude Code UI",
"loading": "Загрузка CloudCLI",
"settingUpWorkspace": "Настройка рабочего пространства...",
"chooseProject": "Выберите проект",
"selectProjectDescription": "Выберите проект на боковой панели, чтобы начать работу с Claude. Каждый проект содержит ваши сеансы чата и историю файлов.",

View File

@@ -105,7 +105,8 @@
"git": "Git",
"apiTokens": "API и токены",
"tasks": "Задачи",
"plugins": "Плагины"
"plugins": "Плагины",
"about": "О программе"
},
"appearanceSettings": {
"darkMode": {

View File

@@ -20,7 +20,7 @@
"runClaudeCli": "Запустите Claude CLI в каталоге проекта для начала работы"
},
"app": {
"title": "Claude Code UI",
"title": "CloudCLI",
"subtitle": "Интерфейс AI помощника для программирования"
},
"sessions": {
@@ -65,7 +65,12 @@
"save": "Сохранить",
"delete": "Удалить",
"rename": "Переименовать",
"joinCommunity": "Присоединиться к сообществу"
"joinCommunity": "Присоединиться к сообществу",
"reportIssue": "Сообщить о проблеме",
"starOnGithub": "Звезда на GitHub"
},
"branding": {
"openSource": "Открытый исходный код"
},
"status": {
"active": "Активен",

View File

@@ -1,7 +1,7 @@
{
"login": {
"title": "欢迎回来",
"description": "登录您的 Claude Code UI 账户",
"description": "登录您的 CloudCLI 账户",
"username": "用户名",
"password": "密码",
"submit": "登录",

View File

@@ -84,7 +84,7 @@
"openInEditor": "在编辑器中打开"
},
"mainContent": {
"loading": "正在加载 Claude Code UI",
"loading": "正在加载 CloudCLI",
"settingUpWorkspace": "正在设置您的工作空间...",
"chooseProject": "选择您的项目",
"selectProjectDescription": "从侧边栏选择一个项目以开始使用 Claude 进行编程。每个项目包含您的聊天会话和文件历史。",

View File

@@ -106,8 +106,8 @@
"apiTokens": "API 和令牌",
"tasks": "任务",
"notifications": "通知",
"plugins": "插件"
"plugins": "插件",
"about": "关于"
},
"notifications": {
"title": "通知",

View File

@@ -20,7 +20,7 @@
"runClaudeCli": "在项目目录中运行 Claude CLI 以开始使用"
},
"app": {
"title": "Claude Code UI",
"title": "CloudCLI",
"subtitle": "AI 编程助手"
},
"sessions": {
@@ -65,7 +65,12 @@
"save": "保存",
"delete": "删除",
"rename": "重命名",
"joinCommunity": "加入社区"
"joinCommunity": "加入社区",
"reportIssue": "报告问题",
"starOnGithub": "在GitHub上加星"
},
"branding": {
"openSource": "开源"
},
"status": {
"active": "活动",