mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-02 08:33:36 +00:00
Merge main into massive-terminal-upgrade
Resolves merge conflicts: - apps/server/src/routes/terminal/common.ts: Keep randomBytes import, use @automaker/utils for createLogger - apps/ui/eslint.config.mjs: Use main's explicit globals list with XMLHttpRequest and MediaQueryListEvent additions - apps/ui/src/components/views/terminal-view.tsx: Keep our terminal improvements (killAllSessions, beforeunload, better error handling) - apps/ui/src/config/terminal-themes.ts: Keep our search highlight colors for all themes - apps/ui/src/store/app-store.ts: Keep our terminal settings persistence improvements (merge function) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,12 +1,6 @@
|
||||
import { Label } from "@/components/ui/label";
|
||||
import {
|
||||
CheckCircle2,
|
||||
AlertCircle,
|
||||
Info,
|
||||
Terminal,
|
||||
Sparkles,
|
||||
} from "lucide-react";
|
||||
import type { ClaudeAuthStatus } from "@/store/setup-store";
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { CheckCircle2, AlertCircle, Info, Terminal } from 'lucide-react';
|
||||
import type { ClaudeAuthStatus } from '@/store/setup-store';
|
||||
|
||||
interface AuthenticationStatusDisplayProps {
|
||||
claudeAuthStatus: ClaudeAuthStatus | null;
|
||||
@@ -39,35 +33,29 @@ export function AuthenticationStatusDisplay({
|
||||
<div className="p-3 rounded-lg bg-card border border-border">
|
||||
<div className="flex items-center gap-2 mb-1.5">
|
||||
<Terminal className="w-4 h-4 text-brand-500" />
|
||||
<span className="text-sm font-medium text-foreground">
|
||||
Claude (Anthropic)
|
||||
</span>
|
||||
<span className="text-sm font-medium text-foreground">Claude (Anthropic)</span>
|
||||
</div>
|
||||
<div className="space-y-1.5 text-xs min-h-12">
|
||||
{claudeAuthStatus?.authenticated ? (
|
||||
<>
|
||||
<div className="flex items-center gap-2">
|
||||
<CheckCircle2 className="w-3 h-3 text-green-500 shrink-0" />
|
||||
<span className="text-green-400 font-medium">
|
||||
Authenticated
|
||||
</span>
|
||||
<span className="text-green-400 font-medium">Authenticated</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-muted-foreground">
|
||||
<Info className="w-3 h-3 shrink-0" />
|
||||
<span>
|
||||
{claudeAuthStatus.method === "oauth_token"
|
||||
? "Using stored OAuth token (subscription)"
|
||||
: claudeAuthStatus.method === "api_key_env"
|
||||
? "Using ANTHROPIC_API_KEY"
|
||||
: claudeAuthStatus.method === "api_key"
|
||||
? "Using stored API key"
|
||||
: claudeAuthStatus.method === "credentials_file"
|
||||
? "Using credentials file"
|
||||
: claudeAuthStatus.method === "cli_authenticated"
|
||||
? "Using Claude CLI authentication"
|
||||
: `Using ${
|
||||
claudeAuthStatus.method || "detected"
|
||||
} authentication`}
|
||||
{claudeAuthStatus.method === 'oauth_token'
|
||||
? 'Using stored OAuth token (subscription)'
|
||||
: claudeAuthStatus.method === 'api_key_env'
|
||||
? 'Using ANTHROPIC_API_KEY'
|
||||
: claudeAuthStatus.method === 'api_key'
|
||||
? 'Using stored API key'
|
||||
: claudeAuthStatus.method === 'credentials_file'
|
||||
? 'Using credentials file'
|
||||
: claudeAuthStatus.method === 'cli_authenticated'
|
||||
? 'Using Claude CLI authentication'
|
||||
: `Using ${claudeAuthStatus.method || 'detected'} authentication`}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export function ClaudeUsageSection() {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'rounded-2xl overflow-hidden',
|
||||
'border border-border/50',
|
||||
'bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl',
|
||||
'shadow-sm shadow-black/5'
|
||||
)}
|
||||
>
|
||||
<div className="p-6 border-b border-border/50 bg-gradient-to-r from-transparent via-accent/5 to-transparent">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<div className="w-9 h-9 rounded-xl bg-gradient-to-br from-green-500/20 to-green-600/10 flex items-center justify-center border border-green-500/20">
|
||||
<div className="w-5 h-5 rounded-full bg-green-500/50" />
|
||||
</div>
|
||||
<h2 className="text-lg font-semibold text-foreground tracking-tight">
|
||||
Claude Usage Tracking
|
||||
</h2>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground/80 ml-12">
|
||||
Track your Claude Code usage limits. Uses the Claude CLI for data.
|
||||
</p>
|
||||
</div>
|
||||
<div className="p-6 space-y-6">
|
||||
{/* Info about CLI requirement */}
|
||||
<div className="rounded-lg bg-secondary/30 p-3 text-xs text-muted-foreground space-y-2 border border-border/50">
|
||||
<p>Usage tracking requires Claude Code CLI to be installed and authenticated:</p>
|
||||
<ol className="list-decimal list-inside space-y-1 ml-1">
|
||||
<li>Install Claude Code CLI if not already installed</li>
|
||||
<li>
|
||||
Run <code className="font-mono bg-muted px-1 rounded">claude login</code> to
|
||||
authenticate
|
||||
</li>
|
||||
<li>Usage data will be fetched automatically every ~minute</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { useApiKeyManagement } from './use-api-key-management';
|
||||
@@ -0,0 +1,4 @@
|
||||
export { ApiKeyField } from './api-key-field';
|
||||
export { ApiKeysSection } from './api-keys-section';
|
||||
export { AuthenticationStatusDisplay } from './authentication-status-display';
|
||||
export { SecurityNotice } from './security-notice';
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Palette } from "lucide-react";
|
||||
import { themeOptions } from "@/config/theme-options";
|
||||
import { cn } from "@/lib/utils";
|
||||
import type { Theme, Project } from "../shared/types";
|
||||
import { useState } from 'react';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Palette, Moon, Sun } from 'lucide-react';
|
||||
import { darkThemes, lightThemes } from '@/config/theme-options';
|
||||
import { cn } from '@/lib/utils';
|
||||
import type { Theme, Project } from '../shared/types';
|
||||
|
||||
interface AppearanceSectionProps {
|
||||
effectiveTheme: Theme;
|
||||
@@ -15,13 +16,17 @@ export function AppearanceSection({
|
||||
currentProject,
|
||||
onThemeChange,
|
||||
}: AppearanceSectionProps) {
|
||||
const [activeTab, setActiveTab] = useState<'dark' | 'light'>('dark');
|
||||
|
||||
const themesToShow = activeTab === 'dark' ? darkThemes : lightThemes;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"rounded-2xl overflow-hidden",
|
||||
"border border-border/50",
|
||||
"bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl",
|
||||
"shadow-sm shadow-black/5"
|
||||
'rounded-2xl overflow-hidden',
|
||||
'border border-border/50',
|
||||
'bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl',
|
||||
'shadow-sm shadow-black/5'
|
||||
)}
|
||||
>
|
||||
<div className="p-6 border-b border-border/50 bg-gradient-to-r from-transparent via-accent/5 to-transparent">
|
||||
@@ -37,43 +42,69 @@ export function AppearanceSection({
|
||||
</div>
|
||||
<div className="p-6 space-y-4">
|
||||
<div className="space-y-4">
|
||||
<Label className="text-foreground font-medium">
|
||||
Theme{" "}
|
||||
<span className="text-muted-foreground font-normal">
|
||||
{currentProject ? `(for ${currentProject.name})` : "(Global)"}
|
||||
</span>
|
||||
</Label>
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="text-foreground font-medium">
|
||||
Theme{' '}
|
||||
<span className="text-muted-foreground font-normal">
|
||||
{currentProject ? `(for ${currentProject.name})` : '(Global)'}
|
||||
</span>
|
||||
</Label>
|
||||
{/* Dark/Light Tabs */}
|
||||
<div className="flex gap-1 p-1 rounded-lg bg-accent/30">
|
||||
<button
|
||||
onClick={() => setActiveTab('dark')}
|
||||
className={cn(
|
||||
'flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-all duration-200',
|
||||
activeTab === 'dark'
|
||||
? 'bg-brand-500 text-white shadow-sm'
|
||||
: 'text-muted-foreground hover:text-foreground'
|
||||
)}
|
||||
>
|
||||
<Moon className="w-3.5 h-3.5" />
|
||||
Dark
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('light')}
|
||||
className={cn(
|
||||
'flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-all duration-200',
|
||||
activeTab === 'light'
|
||||
? 'bg-brand-500 text-white shadow-sm'
|
||||
: 'text-muted-foreground hover:text-foreground'
|
||||
)}
|
||||
>
|
||||
<Sun className="w-3.5 h-3.5" />
|
||||
Light
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||
{themeOptions.map(({ value, label, Icon, testId }) => {
|
||||
{themesToShow.map(({ value, label, Icon, testId, color }) => {
|
||||
const isActive = effectiveTheme === value;
|
||||
return (
|
||||
<button
|
||||
key={value}
|
||||
onClick={() => onThemeChange(value)}
|
||||
className={cn(
|
||||
"group flex items-center justify-center gap-2.5 px-4 py-3.5 rounded-xl",
|
||||
"text-sm font-medium transition-all duration-200 ease-out",
|
||||
'group flex items-center justify-center gap-2.5 px-4 py-3.5 rounded-xl',
|
||||
'text-sm font-medium transition-all duration-200 ease-out',
|
||||
isActive
|
||||
? [
|
||||
"bg-gradient-to-br from-brand-500/15 to-brand-600/10",
|
||||
"border-2 border-brand-500/40",
|
||||
"text-foreground",
|
||||
"shadow-md shadow-brand-500/10",
|
||||
'bg-gradient-to-br from-brand-500/15 to-brand-600/10',
|
||||
'border-2 border-brand-500/40',
|
||||
'text-foreground',
|
||||
'shadow-md shadow-brand-500/10',
|
||||
]
|
||||
: [
|
||||
"bg-accent/30 hover:bg-accent/50",
|
||||
"border border-border/50 hover:border-border",
|
||||
"text-muted-foreground hover:text-foreground",
|
||||
"hover:shadow-sm",
|
||||
'bg-accent/30 hover:bg-accent/50',
|
||||
'border border-border/50 hover:border-border',
|
||||
'text-muted-foreground hover:text-foreground',
|
||||
'hover:shadow-sm',
|
||||
],
|
||||
"hover:scale-[1.02] active:scale-[0.98]"
|
||||
'hover:scale-[1.02] active:scale-[0.98]'
|
||||
)}
|
||||
data-testid={testId}
|
||||
>
|
||||
<Icon className={cn(
|
||||
"w-4 h-4 transition-all duration-200",
|
||||
isActive ? "text-brand-500" : "group-hover:text-brand-400"
|
||||
)} />
|
||||
<Icon className="w-4 h-4 transition-all duration-200" style={{ color }} />
|
||||
<span>{label}</span>
|
||||
</button>
|
||||
);
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { AppearanceSection } from './appearance-section';
|
||||
@@ -0,0 +1 @@
|
||||
export { AudioSection } from './audio-section';
|
||||
@@ -0,0 +1 @@
|
||||
export { ClaudeCliStatus } from './claude-cli-status';
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Folder, Trash2 } from "lucide-react";
|
||||
import { DeleteConfirmDialog } from "@/components/ui/delete-confirm-dialog";
|
||||
import type { Project } from "@/lib/electron";
|
||||
import { Folder } from 'lucide-react';
|
||||
import { DeleteConfirmDialog } from '@/components/ui/delete-confirm-dialog';
|
||||
import type { Project } from '@/lib/electron';
|
||||
|
||||
interface DeleteProjectDialogProps {
|
||||
open: boolean;
|
||||
@@ -39,18 +39,13 @@ export function DeleteProjectDialog({
|
||||
<Folder className="w-5 h-5 text-brand-500" />
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<p className="font-medium text-foreground truncate">
|
||||
{project.name}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground truncate">
|
||||
{project.path}
|
||||
</p>
|
||||
<p className="font-medium text-foreground truncate">{project.name}</p>
|
||||
<p className="text-xs text-muted-foreground truncate">{project.path}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-muted-foreground">
|
||||
The folder will remain on disk until you permanently delete it from
|
||||
Trash.
|
||||
The folder will remain on disk until you permanently delete it from Trash.
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export { DeleteProjectDialog } from './delete-project-dialog';
|
||||
export { KeyboardMapDialog } from './keyboard-map-dialog';
|
||||
export { SettingsHeader } from './settings-header';
|
||||
export { SettingsNavigation } from './settings-navigation';
|
||||
@@ -0,0 +1,2 @@
|
||||
export { NAV_ITEMS } from './navigation';
|
||||
export type { NavigationItem } from './navigation';
|
||||
@@ -0,0 +1 @@
|
||||
export { DangerZoneSection } from './danger-zone-section';
|
||||
@@ -0,0 +1 @@
|
||||
export { FeatureDefaultsSection } from './feature-defaults-section';
|
||||
@@ -0,0 +1 @@
|
||||
export { KeyboardShortcutsSection } from './keyboard-shortcuts-section';
|
||||
@@ -0,0 +1,2 @@
|
||||
export type { Theme } from '@/config/theme-options';
|
||||
export type { CliStatus, KanbanDetailLevel, Project, ApiKeys } from './types';
|
||||
@@ -1,4 +1,6 @@
|
||||
// Shared TypeScript types for settings view components
|
||||
// Theme type is now imported from the central theme-options config
|
||||
export { type Theme } from '@/config/theme-options';
|
||||
|
||||
export interface CliStatus {
|
||||
success: boolean;
|
||||
@@ -17,31 +19,13 @@ export interface CliStatus {
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export type Theme =
|
||||
| "dark"
|
||||
| "light"
|
||||
| "retro"
|
||||
| "dracula"
|
||||
| "nord"
|
||||
| "monokai"
|
||||
| "tokyonight"
|
||||
| "solarized"
|
||||
| "gruvbox"
|
||||
| "catppuccin"
|
||||
| "onedark"
|
||||
| "synthwave"
|
||||
| "red"
|
||||
| "cream"
|
||||
| "sunset"
|
||||
| "gray";
|
||||
|
||||
export type KanbanDetailLevel = "minimal" | "standard" | "detailed";
|
||||
export type KanbanDetailLevel = 'minimal' | 'standard' | 'detailed';
|
||||
|
||||
export interface Project {
|
||||
id: string;
|
||||
name: string;
|
||||
path: string;
|
||||
theme?: Theme;
|
||||
theme?: string;
|
||||
}
|
||||
|
||||
export interface ApiKeys {
|
||||
|
||||
Reference in New Issue
Block a user