style(ui): 统一组件样式,优化视觉设计和交互体验
- 更新颜色系统,使用新的主题变量 - 调整圆角、边框、阴影等视觉样式 - 优化字体大小、间距和排版细节 - 改进表单组件的交互状态 - 增强暗色模式下的显示效果
This commit is contained in:
parent
51ee5a0da9
commit
8ee98a20eb
|
|
@ -1,5 +1,6 @@
|
|||
import { StrictMode } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { ThemeProvider } from "next-themes";
|
||||
import "@/assets/styles/globals.css";
|
||||
import App from "./App.tsx";
|
||||
import { AppWrapper } from "@/components/layout/PageMeta";
|
||||
|
|
@ -9,9 +10,16 @@ import "@/shared/utils/fetchWrapper"; // 初始化fetch拦截器
|
|||
createRoot(document.getElementById("root")!).render(
|
||||
<StrictMode>
|
||||
<ErrorBoundary>
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="dark"
|
||||
enableSystem
|
||||
disableTransitionOnChange={false}
|
||||
>
|
||||
<AppWrapper>
|
||||
<App />
|
||||
</AppWrapper>
|
||||
</ThemeProvider>
|
||||
</ErrorBoundary>
|
||||
</StrictMode>
|
||||
);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -23,7 +23,7 @@ export default function AgentModeSelector({
|
|||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Shield className="w-4 h-4 text-violet-400" />
|
||||
<span className="font-mono text-xs font-bold text-gray-400 uppercase tracking-wider">
|
||||
<span className="font-mono text-xs font-bold text-muted-foreground uppercase tracking-wider">
|
||||
审计模式
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -35,7 +35,7 @@ export default function AgentModeSelector({
|
|||
"relative flex flex-col p-4 border cursor-pointer transition-all rounded",
|
||||
value === "fast"
|
||||
? "border-amber-500/50 bg-amber-950/30"
|
||||
: "border-gray-700 hover:border-gray-600 bg-gray-900/30",
|
||||
: "border-border hover:border-border bg-muted/50",
|
||||
disabled && "opacity-50 cursor-not-allowed"
|
||||
)}
|
||||
>
|
||||
|
|
@ -54,16 +54,16 @@ export default function AgentModeSelector({
|
|||
"p-1.5 rounded border",
|
||||
value === "fast"
|
||||
? "bg-amber-500/20 border-amber-500/50"
|
||||
: "bg-gray-800 border-gray-700"
|
||||
: "bg-muted border-border"
|
||||
)}>
|
||||
<Zap className={cn(
|
||||
"w-4 h-4",
|
||||
value === "fast" ? "text-amber-400" : "text-gray-500"
|
||||
value === "fast" ? "text-amber-400" : "text-muted-foreground"
|
||||
)} />
|
||||
</div>
|
||||
<span className={cn(
|
||||
"font-bold text-sm font-mono uppercase",
|
||||
value === "fast" ? "text-amber-300" : "text-gray-400"
|
||||
value === "fast" ? "text-amber-300" : "text-muted-foreground"
|
||||
)}>
|
||||
快速审计
|
||||
</span>
|
||||
|
|
@ -72,7 +72,7 @@ export default function AgentModeSelector({
|
|||
)}
|
||||
</div>
|
||||
|
||||
<ul className="text-xs text-gray-500 space-y-1 mb-3 font-mono">
|
||||
<ul className="text-xs text-muted-foreground space-y-1 mb-3 font-mono">
|
||||
<li className="flex items-center gap-1">
|
||||
<Clock className="w-3 h-3" />
|
||||
速度快(分钟级)
|
||||
|
|
@ -81,14 +81,14 @@ export default function AgentModeSelector({
|
|||
<Code className="w-3 h-3" />
|
||||
逐文件 LLM 分析
|
||||
</li>
|
||||
<li className="flex items-center gap-1 text-gray-600">
|
||||
<li className="flex items-center gap-1 text-muted-foreground">
|
||||
<Shield className="w-3 h-3" />
|
||||
无漏洞验证
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div className="mt-auto pt-2 border-t border-gray-800">
|
||||
<span className="text-[10px] uppercase tracking-wider text-gray-600 font-bold font-mono">
|
||||
<div className="mt-auto pt-2 border-t border-border">
|
||||
<span className="text-xs uppercase tracking-wider text-muted-foreground font-bold font-mono">
|
||||
适合: CI/CD 集成、日常检查
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -100,7 +100,7 @@ export default function AgentModeSelector({
|
|||
"relative flex flex-col p-4 border cursor-pointer transition-all rounded",
|
||||
value === "agent"
|
||||
? "border-violet-500/50 bg-violet-950/30"
|
||||
: "border-gray-700 hover:border-gray-600 bg-gray-900/30",
|
||||
: "border-border hover:border-border bg-muted/50",
|
||||
disabled && "opacity-50 cursor-not-allowed"
|
||||
)}
|
||||
>
|
||||
|
|
@ -115,7 +115,7 @@ export default function AgentModeSelector({
|
|||
/>
|
||||
|
||||
{/* 推荐标签 */}
|
||||
<div className="absolute -top-2 -right-2 px-2 py-0.5 bg-violet-600 text-white text-[10px] font-bold uppercase font-mono rounded shadow-[0_0_10px_rgba(139,92,246,0.5)]">
|
||||
<div className="absolute -top-2 -right-2 px-2 py-0.5 bg-violet-600 text-foreground text-xs font-bold uppercase font-mono rounded shadow-[0_0_10px_rgba(139,92,246,0.5)]">
|
||||
推荐
|
||||
</div>
|
||||
|
||||
|
|
@ -124,16 +124,16 @@ export default function AgentModeSelector({
|
|||
"p-1.5 rounded border",
|
||||
value === "agent"
|
||||
? "bg-violet-500/20 border-violet-500/50"
|
||||
: "bg-gray-800 border-gray-700"
|
||||
: "bg-muted border-border"
|
||||
)}>
|
||||
<Bot className={cn(
|
||||
"w-4 h-4",
|
||||
value === "agent" ? "text-violet-400" : "text-gray-500"
|
||||
value === "agent" ? "text-violet-400" : "text-muted-foreground"
|
||||
)} />
|
||||
</div>
|
||||
<span className={cn(
|
||||
"font-bold text-sm font-mono uppercase",
|
||||
value === "agent" ? "text-violet-300" : "text-gray-400"
|
||||
value === "agent" ? "text-violet-300" : "text-muted-foreground"
|
||||
)}>
|
||||
Agent 审计
|
||||
</span>
|
||||
|
|
@ -142,7 +142,7 @@ export default function AgentModeSelector({
|
|||
)}
|
||||
</div>
|
||||
|
||||
<ul className="text-xs text-gray-500 space-y-1 mb-3 font-mono">
|
||||
<ul className="text-xs text-muted-foreground space-y-1 mb-3 font-mono">
|
||||
<li className="flex items-center gap-1">
|
||||
<Bot className="w-3 h-3" />
|
||||
AI Agent 自主分析
|
||||
|
|
@ -153,15 +153,15 @@ export default function AgentModeSelector({
|
|||
</li>
|
||||
<li className={cn(
|
||||
"flex items-center gap-1",
|
||||
value === "agent" ? "text-violet-400 font-medium" : "text-gray-500"
|
||||
value === "agent" ? "text-violet-400 font-medium" : "text-muted-foreground"
|
||||
)}>
|
||||
<Shield className="w-3 h-3" />
|
||||
沙箱漏洞验证
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div className="mt-auto pt-2 border-t border-gray-800">
|
||||
<span className="text-[10px] uppercase tracking-wider text-gray-600 font-bold font-mono">
|
||||
<div className="mt-auto pt-2 border-t border-border">
|
||||
<span className="text-xs uppercase tracking-wider text-muted-foreground font-bold font-mono">
|
||||
适合: 发版前审计、深度安全评估
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -232,16 +232,16 @@ export default function CreateAgentTaskDialog({
|
|||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="!w-[min(90vw,520px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 bg-[#0c0c12] border border-gray-800 rounded-lg">
|
||||
<DialogContent className="!w-[min(90vw,520px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 cyber-dialog border border-border rounded-lg">
|
||||
{/* Header */}
|
||||
<DialogHeader className="px-5 py-4 border-b border-gray-800 flex-shrink-0 bg-gray-900/50">
|
||||
<DialogTitle className="flex items-center gap-3 font-mono text-white">
|
||||
<DialogHeader className="px-5 py-4 border-b border-border flex-shrink-0 bg-muted">
|
||||
<DialogTitle className="flex items-center gap-3 font-mono text-foreground">
|
||||
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
||||
<Bot className="w-5 h-5 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-base font-bold uppercase tracking-wider">New Agent Audit</span>
|
||||
<p className="text-xs text-gray-500 font-normal mt-0.5">
|
||||
<p className="text-xs text-muted-foreground font-normal mt-0.5">
|
||||
AI-Powered Security Analysis
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -252,17 +252,17 @@ export default function CreateAgentTaskDialog({
|
|||
{/* 项目选择 */}
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs font-mono font-bold uppercase text-gray-400">
|
||||
<span className="text-xs font-mono font-bold uppercase text-muted-foreground">
|
||||
Select Project
|
||||
</span>
|
||||
<Badge className="cyber-badge-muted font-mono text-[10px]">
|
||||
<Badge className="cyber-badge-muted font-mono text-xs">
|
||||
{filteredProjects.length} available
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
{/* 搜索框 */}
|
||||
<div className="relative">
|
||||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-600" />
|
||||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
|
||||
<Input
|
||||
placeholder="Search projects..."
|
||||
value={searchTerm}
|
||||
|
|
@ -272,13 +272,13 @@ export default function CreateAgentTaskDialog({
|
|||
</div>
|
||||
|
||||
{/* 项目列表 */}
|
||||
<ScrollArea className="h-[200px] border border-gray-800 rounded bg-gray-900/30">
|
||||
<ScrollArea className="h-[200px] border border-border rounded bg-muted/50">
|
||||
{loadingProjects ? (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<Loader2 className="w-5 h-5 animate-spin text-primary" />
|
||||
</div>
|
||||
) : filteredProjects.length === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center h-full text-gray-600 font-mono">
|
||||
<div className="flex flex-col items-center justify-center h-full text-muted-foreground font-mono">
|
||||
<Package className="w-8 h-8 mb-2 opacity-50" />
|
||||
<span className="text-sm">{searchTerm ? "No matches" : "No projects"}</span>
|
||||
</div>
|
||||
|
|
@ -302,9 +302,9 @@ export default function CreateAgentTaskDialog({
|
|||
<div className="space-y-4">
|
||||
{/* 仓库项目:分支选择 */}
|
||||
{isRepositoryProject(selectedProject) && (
|
||||
<div className="flex items-center gap-3 p-3 border border-gray-800 rounded bg-blue-950/20">
|
||||
<div className="flex items-center gap-3 p-3 border border-border rounded bg-blue-950/20">
|
||||
<GitBranch className="w-5 h-5 text-blue-400" />
|
||||
<span className="font-mono text-sm text-gray-400 w-16">Branch</span>
|
||||
<span className="font-mono text-sm text-muted-foreground w-16">Branch</span>
|
||||
{loadingBranches ? (
|
||||
<div className="flex items-center gap-2 flex-1">
|
||||
<Loader2 className="w-4 h-4 animate-spin text-blue-400" />
|
||||
|
|
@ -315,9 +315,9 @@ export default function CreateAgentTaskDialog({
|
|||
<SelectTrigger className="flex-1 h-9 cyber-input">
|
||||
<SelectValue placeholder="Select branch" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
||||
<SelectContent className="cyber-dialog border-border">
|
||||
{branches.map((b) => (
|
||||
<SelectItem key={b} value={b} className="font-mono text-white">
|
||||
<SelectItem key={b} value={b} className="font-mono text-foreground">
|
||||
{b}
|
||||
</SelectItem>
|
||||
))}
|
||||
|
|
@ -329,27 +329,27 @@ export default function CreateAgentTaskDialog({
|
|||
|
||||
{/* ZIP 项目:文件选择 */}
|
||||
{isZipProject(selectedProject) && (
|
||||
<div className="p-3 border border-gray-800 rounded bg-amber-950/20 space-y-3">
|
||||
<div className="p-3 border border-border rounded bg-amber-950/20 space-y-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<Package className="w-5 h-5 text-amber-400" />
|
||||
<span className="font-mono text-sm text-gray-400 uppercase font-bold">ZIP File</span>
|
||||
<span className="font-mono text-sm text-muted-foreground uppercase font-bold">ZIP File</span>
|
||||
</div>
|
||||
|
||||
{storedZipInfo?.has_file && (
|
||||
<div
|
||||
className={`p-2 rounded border cursor-pointer transition-colors ${useStoredZip
|
||||
? 'border-emerald-500/50 bg-emerald-950/30'
|
||||
: 'border-gray-700 hover:border-gray-600 bg-gray-900/30'
|
||||
: 'border-border hover:border-border bg-muted/50'
|
||||
}`}
|
||||
onClick={() => setUseStoredZip(true)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className={`w-3 h-3 rounded-full border-2 ${useStoredZip ? 'border-emerald-500 bg-emerald-500' : 'border-gray-600'
|
||||
<div className={`w-3 h-3 rounded-full border-2 ${useStoredZip ? 'border-emerald-500 bg-emerald-500' : 'border-border'
|
||||
}`} />
|
||||
<span className="text-sm text-white font-mono">
|
||||
<span className="text-sm text-foreground font-mono">
|
||||
{storedZipInfo.original_filename}
|
||||
</span>
|
||||
<Badge className="cyber-badge-success text-[10px]">
|
||||
<Badge className="cyber-badge-success text-xs">
|
||||
Stored
|
||||
</Badge>
|
||||
</div>
|
||||
|
|
@ -359,14 +359,14 @@ export default function CreateAgentTaskDialog({
|
|||
<div
|
||||
className={`p-2 rounded border cursor-pointer transition-colors ${!useStoredZip && zipFile
|
||||
? 'border-amber-500/50 bg-amber-950/30'
|
||||
: 'border-gray-700 hover:border-gray-600 bg-gray-900/30'
|
||||
: 'border-border hover:border-border bg-muted/50'
|
||||
}`}
|
||||
>
|
||||
<label className="flex items-center gap-2 cursor-pointer">
|
||||
<div className={`w-3 h-3 rounded-full border-2 ${!useStoredZip && zipFile ? 'border-amber-500 bg-amber-500' : 'border-gray-600'
|
||||
<div className={`w-3 h-3 rounded-full border-2 ${!useStoredZip && zipFile ? 'border-amber-500 bg-amber-500' : 'border-border'
|
||||
}`} />
|
||||
<Upload className="w-4 h-4 text-gray-500" />
|
||||
<span className="text-sm text-gray-400 font-mono">
|
||||
<Upload className="w-4 h-4 text-muted-foreground" />
|
||||
<span className="text-sm text-muted-foreground font-mono">
|
||||
{zipFile ? zipFile.name : "Upload new file..."}
|
||||
</span>
|
||||
<input
|
||||
|
|
@ -382,7 +382,7 @@ export default function CreateAgentTaskDialog({
|
|||
|
||||
{/* 高级选项 */}
|
||||
<Collapsible open={showAdvanced} onOpenChange={setShowAdvanced}>
|
||||
<CollapsibleTrigger className="flex items-center gap-2 text-xs font-mono text-gray-500 hover:text-gray-300 transition-colors">
|
||||
<CollapsibleTrigger className="flex items-center gap-2 text-xs font-mono text-muted-foreground hover:text-foreground transition-colors">
|
||||
<ChevronRight className={`w-4 h-4 transition-transform ${showAdvanced ? "rotate-90" : ""}`} />
|
||||
<Settings2 className="w-4 h-4" />
|
||||
<span className="uppercase font-bold">Advanced Options</span>
|
||||
|
|
@ -396,12 +396,12 @@ export default function CreateAgentTaskDialog({
|
|||
const canSelectFiles = isRepo || (isZip && useStoredZip && hasStoredZip);
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between p-3 border border-dashed border-gray-700 rounded bg-gray-900/30">
|
||||
<div className="flex items-center justify-between p-3 border border-dashed border-border rounded bg-muted/50">
|
||||
<div>
|
||||
<p className="font-mono text-xs uppercase font-bold text-gray-500">
|
||||
<p className="font-mono text-xs uppercase font-bold text-muted-foreground">
|
||||
Scan Scope
|
||||
</p>
|
||||
<p className="text-sm text-white font-mono font-bold mt-1">
|
||||
<p className="text-sm text-foreground font-mono font-bold mt-1">
|
||||
{selectedFiles
|
||||
? `${selectedFiles.length} files selected`
|
||||
: "All files"}
|
||||
|
|
@ -434,9 +434,9 @@ export default function CreateAgentTaskDialog({
|
|||
})()}
|
||||
|
||||
{/* 排除模式 */}
|
||||
<div className="p-3 border border-dashed border-gray-700 rounded bg-gray-900/30 space-y-3">
|
||||
<div className="p-3 border border-dashed border-border rounded bg-muted/50 space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-mono text-xs uppercase font-bold text-gray-500">
|
||||
<span className="font-mono text-xs uppercase font-bold text-muted-foreground">
|
||||
Exclude Patterns
|
||||
</span>
|
||||
<button
|
||||
|
|
@ -452,7 +452,7 @@ export default function CreateAgentTaskDialog({
|
|||
{excludePatterns.map((p) => (
|
||||
<Badge
|
||||
key={p}
|
||||
className="bg-gray-800 text-gray-300 border-0 font-mono text-xs cursor-pointer hover:bg-rose-900/50 hover:text-rose-400"
|
||||
className="bg-muted text-foreground border-0 font-mono text-xs cursor-pointer hover:bg-rose-900/50 hover:text-rose-400"
|
||||
onClick={() => setExcludePatterns((prev) => prev.filter((x) => x !== p))}
|
||||
>
|
||||
{p} ×
|
||||
|
|
@ -481,12 +481,12 @@ export default function CreateAgentTaskDialog({
|
|||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="flex-shrink-0 flex justify-end gap-3 px-5 py-4 bg-gray-900/50 border-t border-gray-800">
|
||||
<div className="flex-shrink-0 flex justify-end gap-3 px-5 py-4 bg-muted border-t border-border">
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => onOpenChange(false)}
|
||||
disabled={creating}
|
||||
className="px-4 h-10 font-mono text-gray-400 hover:text-white hover:bg-gray-800"
|
||||
className="px-4 h-10 font-mono text-muted-foreground hover:text-foreground hover:bg-muted"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
|
|
@ -539,7 +539,7 @@ function ProjectItem({
|
|||
<div
|
||||
className={`flex items-center gap-3 p-3 cursor-pointer rounded transition-all ${selected
|
||||
? "bg-primary/10 border border-primary/50"
|
||||
: "hover:bg-gray-800/50 border border-transparent"
|
||||
: "hover:bg-muted border border-transparent"
|
||||
}`}
|
||||
onClick={onSelect}
|
||||
>
|
||||
|
|
@ -553,11 +553,11 @@ function ProjectItem({
|
|||
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className={`font-mono text-sm truncate ${selected ? 'text-white font-bold' : 'text-gray-300'}`}>
|
||||
<span className={`font-mono text-sm truncate ${selected ? 'text-foreground font-bold' : 'text-foreground'}`}>
|
||||
{project.name}
|
||||
</span>
|
||||
<Badge
|
||||
className={`text-[10px] px-1 py-0 font-mono ${isRepo
|
||||
className={`text-xs px-1 py-0 font-mono ${isRepo
|
||||
? "bg-blue-500/20 text-blue-400 border-blue-500/30"
|
||||
: "bg-amber-500/20 text-amber-400 border-amber-500/30"
|
||||
}`}
|
||||
|
|
@ -566,7 +566,7 @@ function ProjectItem({
|
|||
</Badge>
|
||||
</div>
|
||||
{project.description && (
|
||||
<p className="text-xs text-gray-600 mt-0.5 font-mono truncate">
|
||||
<p className="text-xs text-muted-foreground mt-0.5 font-mono truncate">
|
||||
{project.description}
|
||||
</p>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ export default function EmbeddingConfigPanel() {
|
|||
<div className="flex items-center justify-center min-h-[300px]">
|
||||
<div className="text-center space-y-4">
|
||||
<div className="loading-spinner mx-auto" />
|
||||
<p className="text-gray-500 font-mono text-sm uppercase tracking-wider">加载配置中...</p>
|
||||
<p className="text-muted-foreground font-mono text-sm uppercase tracking-wider">加载配置中...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -202,26 +202,26 @@ export default function EmbeddingConfigPanel() {
|
|||
<div className="cyber-card p-4 border-primary/30">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<Server className="w-4 h-4 text-primary" />
|
||||
<span className="font-mono font-bold text-sm uppercase text-gray-300">当前配置</span>
|
||||
<span className="font-mono font-bold text-sm uppercase text-foreground">当前配置</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<div className="bg-gray-900/50 p-3 rounded-lg border border-gray-800">
|
||||
<p className="text-xs text-gray-500 uppercase mb-1">提供商</p>
|
||||
<div className="bg-muted p-3 rounded-lg border border-border">
|
||||
<p className="text-xs text-muted-foreground uppercase mb-1">提供商</p>
|
||||
<Badge className="bg-primary/20 text-primary border-primary/50 font-mono">
|
||||
{currentConfig.provider}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="bg-gray-900/50 p-3 rounded-lg border border-gray-800">
|
||||
<p className="text-xs text-gray-500 uppercase mb-1">模型</p>
|
||||
<p className="font-mono text-sm text-gray-300 truncate">{currentConfig.model}</p>
|
||||
<div className="bg-muted p-3 rounded-lg border border-border">
|
||||
<p className="text-xs text-muted-foreground uppercase mb-1">模型</p>
|
||||
<p className="font-mono text-sm text-foreground truncate">{currentConfig.model}</p>
|
||||
</div>
|
||||
<div className="bg-gray-900/50 p-3 rounded-lg border border-gray-800">
|
||||
<p className="text-xs text-gray-500 uppercase mb-1">向量维度</p>
|
||||
<p className="font-mono text-sm text-gray-300">{currentConfig.dimensions}</p>
|
||||
<div className="bg-muted p-3 rounded-lg border border-border">
|
||||
<p className="text-xs text-muted-foreground uppercase mb-1">向量维度</p>
|
||||
<p className="font-mono text-sm text-foreground">{currentConfig.dimensions}</p>
|
||||
</div>
|
||||
<div className="bg-gray-900/50 p-3 rounded-lg border border-gray-800">
|
||||
<p className="text-xs text-gray-500 uppercase mb-1">批处理大小</p>
|
||||
<p className="font-mono text-sm text-gray-300">{currentConfig.batch_size}</p>
|
||||
<div className="bg-muted p-3 rounded-lg border border-border">
|
||||
<p className="text-xs text-muted-foreground uppercase mb-1">批处理大小</p>
|
||||
<p className="font-mono text-sm text-foreground">{currentConfig.batch_size}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -231,12 +231,12 @@ export default function EmbeddingConfigPanel() {
|
|||
<div className="cyber-card p-6 space-y-6">
|
||||
{/* 提供商选择 */}
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">嵌入模型提供商</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">嵌入模型提供商</Label>
|
||||
<Select value={selectedProvider} onValueChange={handleProviderChange}>
|
||||
<SelectTrigger className="h-12 cyber-input">
|
||||
<SelectValue placeholder="选择提供商" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
||||
<SelectContent className="cyber-dialog border-border">
|
||||
{providers.map((provider) => (
|
||||
<SelectItem key={provider.id} value={provider.id} className="font-mono">
|
||||
<div className="flex items-center gap-2">
|
||||
|
|
@ -253,7 +253,7 @@ export default function EmbeddingConfigPanel() {
|
|||
</Select>
|
||||
|
||||
{selectedProviderInfo && (
|
||||
<p className="text-xs text-gray-500 flex items-center gap-1">
|
||||
<p className="text-xs text-muted-foreground flex items-center gap-1">
|
||||
<Info className="w-3 h-3 text-sky-400" />
|
||||
{selectedProviderInfo.description}
|
||||
</p>
|
||||
|
|
@ -263,7 +263,7 @@ export default function EmbeddingConfigPanel() {
|
|||
{/* 模型选择/输入 */}
|
||||
{selectedProviderInfo && (
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">模型</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">模型</Label>
|
||||
<Input
|
||||
type="text"
|
||||
value={selectedModel}
|
||||
|
|
@ -273,7 +273,7 @@ export default function EmbeddingConfigPanel() {
|
|||
/>
|
||||
{selectedProviderInfo.models.length > 0 && (
|
||||
<div className="flex flex-wrap gap-2 mt-2">
|
||||
<span className="text-xs text-gray-500">预设模型:</span>
|
||||
<span className="text-xs text-muted-foreground">预设模型:</span>
|
||||
{selectedProviderInfo.models.map((model) => (
|
||||
<button
|
||||
key={model}
|
||||
|
|
@ -282,7 +282,7 @@ export default function EmbeddingConfigPanel() {
|
|||
className={`px-2 py-1 text-xs font-mono rounded border transition-colors ${
|
||||
selectedModel === model
|
||||
? "bg-primary/20 border-primary/50 text-primary"
|
||||
: "bg-gray-800/50 border-gray-700 text-gray-400 hover:border-gray-600 hover:text-gray-300"
|
||||
: "bg-muted border-border text-muted-foreground hover:border-border hover:text-foreground"
|
||||
}`}
|
||||
>
|
||||
{model}
|
||||
|
|
@ -296,7 +296,7 @@ export default function EmbeddingConfigPanel() {
|
|||
{/* API Key */}
|
||||
{selectedProviderInfo?.requires_api_key && (
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">
|
||||
API Key
|
||||
<span className="text-rose-400 ml-1">*</span>
|
||||
</Label>
|
||||
|
|
@ -307,7 +307,7 @@ export default function EmbeddingConfigPanel() {
|
|||
placeholder="输入 API Key"
|
||||
className="h-10 cyber-input"
|
||||
/>
|
||||
<p className="text-xs text-gray-600">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
API Key 将安全存储,不会显示在页面上
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -315,8 +315,8 @@ export default function EmbeddingConfigPanel() {
|
|||
|
||||
{/* 自定义端点 */}
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">
|
||||
自定义 API 端点 <span className="text-gray-600">(可选)</span>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">
|
||||
自定义 API 端点 <span className="text-muted-foreground">(可选)</span>
|
||||
</Label>
|
||||
<Input
|
||||
type="url"
|
||||
|
|
@ -335,14 +335,14 @@ export default function EmbeddingConfigPanel() {
|
|||
}
|
||||
className="h-10 cyber-input"
|
||||
/>
|
||||
<p className="text-xs text-gray-600">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
用于 API 代理或自托管服务
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 批处理大小 */}
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">批处理大小</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">批处理大小</Label>
|
||||
<Input
|
||||
type="number"
|
||||
value={batchSize}
|
||||
|
|
@ -351,7 +351,7 @@ export default function EmbeddingConfigPanel() {
|
|||
max={500}
|
||||
className="h-10 cyber-input w-32"
|
||||
/>
|
||||
<p className="text-xs text-gray-600">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
每批嵌入的文本数量,建议 50-100
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -379,14 +379,14 @@ export default function EmbeddingConfigPanel() {
|
|||
{testResult.success ? "测试成功" : "测试失败"}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-gray-400">{testResult.message}</p>
|
||||
<p className="text-sm text-muted-foreground">{testResult.message}</p>
|
||||
{testResult.success && (
|
||||
<div className="mt-3 pt-3 border-t border-gray-800 text-xs text-gray-500 space-y-1 font-mono">
|
||||
<div>向量维度: <span className="text-gray-300">{testResult.dimensions}</span></div>
|
||||
<div>延迟: <span className="text-gray-300">{testResult.latency_ms}ms</span></div>
|
||||
<div className="mt-3 pt-3 border-t border-border text-xs text-muted-foreground space-y-1 font-mono">
|
||||
<div>向量维度: <span className="text-foreground">{testResult.dimensions}</span></div>
|
||||
<div>延迟: <span className="text-foreground">{testResult.latency_ms}ms</span></div>
|
||||
{testResult.sample_embedding && (
|
||||
<div className="truncate">
|
||||
示例向量: <span className="text-gray-400">[{testResult.sample_embedding.slice(0, 5).map((v) => v.toFixed(4)).join(", ")}...]</span>
|
||||
示例向量: <span className="text-muted-foreground">[{testResult.sample_embedding.slice(0, 5).map((v) => v.toFixed(4)).join(", ")}...]</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -395,7 +395,7 @@ export default function EmbeddingConfigPanel() {
|
|||
)}
|
||||
|
||||
{/* 操作按钮 */}
|
||||
<div className="flex items-center gap-3 pt-4 border-t border-gray-800 border-dashed">
|
||||
<div className="flex items-center gap-3 pt-4 border-t border-border border-dashed">
|
||||
<Button
|
||||
onClick={handleTest}
|
||||
disabled={testing || !selectedProvider || !selectedModel}
|
||||
|
|
@ -434,15 +434,15 @@ export default function EmbeddingConfigPanel() {
|
|||
</div>
|
||||
|
||||
{/* 说明 */}
|
||||
<div className="bg-gray-900/50 border border-gray-800 p-4 rounded-lg text-xs space-y-2">
|
||||
<p className="font-bold uppercase text-gray-400 flex items-center gap-2">
|
||||
<div className="bg-muted border border-border p-4 rounded-lg text-xs space-y-2">
|
||||
<p className="font-bold uppercase text-muted-foreground flex items-center gap-2">
|
||||
<Info className="w-4 h-4 text-sky-400" />
|
||||
关于嵌入模型
|
||||
</p>
|
||||
<ul className="text-gray-500 space-y-1 ml-6">
|
||||
<ul className="text-muted-foreground space-y-1 ml-6">
|
||||
<li>• 嵌入模型用于 Agent 审计的代码语义搜索 (RAG)</li>
|
||||
<li>• 与分析使用的 LLM 独立配置,互不影响</li>
|
||||
<li>• 推荐使用 <span className="text-gray-300">OpenAI text-embedding-3-small</span> 或本地 <span className="text-gray-300">Ollama</span></li>
|
||||
<li>• 推荐使用 <span className="text-foreground">OpenAI text-embedding-3-small</span> 或本地 <span className="text-foreground">Ollama</span></li>
|
||||
<li>• 向量维度影响存储空间和检索精度</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -305,16 +305,16 @@ export default function CreateTaskDialog({
|
|||
return (
|
||||
<>
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="!w-[min(90vw,520px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 bg-[#0c0c12] border border-gray-800 rounded-lg">
|
||||
<DialogContent className="!w-[min(90vw,520px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 cyber-dialog border border-border rounded-lg">
|
||||
{/* Header */}
|
||||
<DialogHeader className="px-5 py-4 border-b border-gray-800 flex-shrink-0 bg-gray-900/50">
|
||||
<DialogTitle className="flex items-center gap-3 font-mono text-white">
|
||||
<DialogHeader className="px-5 py-4 border-b border-border flex-shrink-0 bg-muted">
|
||||
<DialogTitle className="flex items-center gap-3 font-mono text-foreground">
|
||||
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
||||
<Shield className="w-5 h-5 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-base font-bold uppercase tracking-wider">开始代码审计</span>
|
||||
<p className="text-xs text-gray-500 font-normal mt-0.5">
|
||||
<p className="text-xs text-muted-foreground font-normal mt-0.5">
|
||||
Code Security Analysis
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -325,17 +325,17 @@ export default function CreateTaskDialog({
|
|||
{/* 项目选择 */}
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs font-mono font-bold uppercase text-gray-400">
|
||||
<span className="text-sm font-mono font-bold uppercase text-muted-foreground">
|
||||
选择项目
|
||||
</span>
|
||||
<Badge className="cyber-badge-muted font-mono text-[10px]">
|
||||
<Badge className="cyber-badge-muted font-mono text-xs">
|
||||
{filteredProjects.length} 个
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
{/* 搜索框 */}
|
||||
<div className="relative">
|
||||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-600" />
|
||||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
|
||||
<Input
|
||||
placeholder="搜索项目..."
|
||||
value={searchTerm}
|
||||
|
|
@ -345,13 +345,13 @@ export default function CreateTaskDialog({
|
|||
</div>
|
||||
|
||||
{/* 项目列表 */}
|
||||
<ScrollArea className="h-[180px] border border-gray-800 rounded bg-gray-900/30">
|
||||
<ScrollArea className="h-[180px] border border-border rounded bg-muted/50">
|
||||
{loading ? (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<Loader2 className="w-5 h-5 animate-spin text-primary" />
|
||||
</div>
|
||||
) : filteredProjects.length === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center h-full text-gray-600 font-mono">
|
||||
<div className="flex flex-col items-center justify-center h-full text-muted-foreground font-mono">
|
||||
<Package className="w-8 h-8 mb-2 opacity-50" />
|
||||
<span className="text-sm">
|
||||
{searchTerm ? "未找到" : "暂无项目"}
|
||||
|
|
@ -384,14 +384,14 @@ export default function CreateTaskDialog({
|
|||
{/* 配置区域 */}
|
||||
{selectedProject && (
|
||||
<div className="space-y-4">
|
||||
<span className="text-xs font-mono font-bold uppercase text-gray-400">
|
||||
<span className="text-sm font-mono font-bold uppercase text-muted-foreground">
|
||||
配置
|
||||
</span>
|
||||
|
||||
{isRepositoryProject(selectedProject) ? (
|
||||
<div className="flex items-center gap-3 p-3 border border-gray-800 rounded bg-blue-950/20">
|
||||
<div className="flex items-center gap-3 p-3 border border-border rounded bg-blue-950/20">
|
||||
<GitBranch className="w-5 h-5 text-blue-400" />
|
||||
<span className="font-mono text-sm text-gray-400 w-12">
|
||||
<span className="font-mono text-base text-muted-foreground w-12">
|
||||
分支
|
||||
</span>
|
||||
{loadingBranches ? (
|
||||
|
|
@ -404,7 +404,7 @@ export default function CreateTaskDialog({
|
|||
<SelectTrigger className="h-9 flex-1 cyber-input">
|
||||
<SelectValue placeholder="选择分支" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
||||
<SelectContent className="cyber-dialog border-border">
|
||||
{branches.map((b) => (
|
||||
<SelectItem key={b} value={b} className="font-mono">
|
||||
{b}
|
||||
|
|
@ -438,19 +438,19 @@ export default function CreateTaskDialog({
|
|||
|
||||
{/* 规则集和提示词选择 - 仅快速扫描模式显示 */}
|
||||
{auditMode !== "agent" && (
|
||||
<div className="p-3 border border-gray-800 rounded bg-violet-950/20 space-y-3">
|
||||
<div className="p-3 border border-border rounded bg-violet-950/20 space-y-3">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Zap className="w-4 h-4 text-violet-400" />
|
||||
<span className="font-mono text-sm font-bold text-violet-300 uppercase">审计配置</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label className="block text-xs font-mono font-bold text-gray-500 mb-1 uppercase">规则集</label>
|
||||
<label className="block text-xs font-mono font-bold text-muted-foreground mb-1 uppercase">规则集</label>
|
||||
<Select value={selectedRuleSetId} onValueChange={setSelectedRuleSetId}>
|
||||
<SelectTrigger className="h-9 cyber-input text-xs">
|
||||
<SelectValue placeholder="选择规则集" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
||||
<SelectContent className="cyber-dialog border-border">
|
||||
{ruleSets.map((rs) => (
|
||||
<SelectItem key={rs.id} value={rs.id} className="font-mono text-xs">
|
||||
{rs.name} {rs.is_default && '(默认)'} ({rs.enabled_rules_count})
|
||||
|
|
@ -460,12 +460,12 @@ export default function CreateTaskDialog({
|
|||
</Select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-xs font-mono font-bold text-gray-500 mb-1 uppercase">提示词模板</label>
|
||||
<label className="block text-xs font-mono font-bold text-muted-foreground mb-1 uppercase">提示词模板</label>
|
||||
<Select value={selectedPromptTemplateId} onValueChange={setSelectedPromptTemplateId}>
|
||||
<SelectTrigger className="h-9 cyber-input text-xs">
|
||||
<SelectValue placeholder="选择提示词模板" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
||||
<SelectContent className="cyber-dialog border-border">
|
||||
{promptTemplates.map((pt) => (
|
||||
<SelectItem key={pt.id} value={pt.id} className="font-mono text-xs">
|
||||
{pt.name} {pt.is_default && '(默认)'}
|
||||
|
|
@ -480,7 +480,7 @@ export default function CreateTaskDialog({
|
|||
|
||||
{/* 高级选项 */}
|
||||
<Collapsible open={showAdvanced} onOpenChange={setShowAdvanced}>
|
||||
<CollapsibleTrigger className="flex items-center gap-2 text-xs font-mono text-gray-500 hover:text-gray-300 transition-colors">
|
||||
<CollapsibleTrigger className="flex items-center gap-2 text-xs font-mono text-muted-foreground hover:text-foreground transition-colors">
|
||||
<ChevronRight
|
||||
className={`w-4 h-4 transition-transform ${showAdvanced ? "rotate-90" : ""}`}
|
||||
/>
|
||||
|
|
@ -489,9 +489,9 @@ export default function CreateTaskDialog({
|
|||
</CollapsibleTrigger>
|
||||
<CollapsibleContent className="mt-3 space-y-3">
|
||||
{/* 排除模式 */}
|
||||
<div className="p-3 border border-dashed border-gray-700 rounded bg-gray-900/30 space-y-3">
|
||||
<div className="p-3 border border-dashed border-border rounded bg-muted/50 space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-mono text-xs uppercase font-bold text-gray-500">
|
||||
<span className="font-mono text-xs uppercase font-bold text-muted-foreground">
|
||||
排除模式
|
||||
</span>
|
||||
<button
|
||||
|
|
@ -507,7 +507,7 @@ export default function CreateTaskDialog({
|
|||
{excludePatterns.map((p) => (
|
||||
<Badge
|
||||
key={p}
|
||||
className="bg-gray-800 text-gray-300 border-0 font-mono text-xs cursor-pointer hover:bg-rose-900/50 hover:text-rose-400"
|
||||
className="bg-muted text-foreground border-0 font-mono text-xs cursor-pointer hover:bg-rose-900/50 hover:text-rose-400"
|
||||
onClick={() =>
|
||||
setExcludePatterns((prev) =>
|
||||
prev.filter((x) => x !== p)
|
||||
|
|
@ -518,12 +518,12 @@ export default function CreateTaskDialog({
|
|||
</Badge>
|
||||
))}
|
||||
{excludePatterns.length === 0 && (
|
||||
<span className="text-xs text-gray-600 font-mono">无排除模式</span>
|
||||
<span className="text-xs text-muted-foreground font-mono">无排除模式</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-1">
|
||||
<span className="text-xs text-gray-600 font-mono mr-1">快捷添加:</span>
|
||||
<span className="text-xs text-muted-foreground font-mono mr-1">快捷添加:</span>
|
||||
{[".test.", ".spec.", ".min.", "coverage/", "docs/", ".md"].map((pattern) => (
|
||||
<button
|
||||
key={pattern}
|
||||
|
|
@ -534,7 +534,7 @@ export default function CreateTaskDialog({
|
|||
setExcludePatterns((prev) => [...prev, pattern]);
|
||||
}
|
||||
}}
|
||||
className="text-xs font-mono px-1.5 py-0.5 border border-gray-700 bg-gray-800 hover:bg-gray-700 text-gray-400 hover:text-white disabled:opacity-40 disabled:cursor-not-allowed rounded"
|
||||
className="text-xs font-mono px-1.5 py-0.5 border border-border bg-muted hover:bg-muted text-muted-foreground hover:text-foreground disabled:opacity-40 disabled:cursor-not-allowed rounded"
|
||||
>
|
||||
+{pattern}
|
||||
</button>
|
||||
|
|
@ -565,12 +565,12 @@ export default function CreateTaskDialog({
|
|||
const canSelectFiles = isRepo || (isZip && useStored && hasStoredZip);
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between p-3 border border-dashed border-gray-700 rounded bg-gray-900/30">
|
||||
<div className="flex items-center justify-between p-3 border border-dashed border-border rounded bg-muted/50">
|
||||
<div>
|
||||
<p className="font-mono text-xs uppercase font-bold text-gray-500">
|
||||
<p className="font-mono text-xs uppercase font-bold text-muted-foreground">
|
||||
扫描范围
|
||||
</p>
|
||||
<p className="text-sm font-bold text-white mt-1">
|
||||
<p className="text-sm font-bold text-foreground mt-1">
|
||||
{selectedFiles
|
||||
? `已选 ${selectedFiles.length} 个文件`
|
||||
: "全部文件"}
|
||||
|
|
@ -608,12 +608,12 @@ export default function CreateTaskDialog({
|
|||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="flex-shrink-0 flex justify-end gap-3 px-5 py-4 bg-gray-900/50 border-t border-gray-800">
|
||||
<div className="flex-shrink-0 flex justify-end gap-3 px-5 py-4 bg-muted border-t border-border">
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => onOpenChange(false)}
|
||||
disabled={creating}
|
||||
className="px-4 h-10 font-mono text-gray-400 hover:text-white hover:bg-gray-800"
|
||||
className="px-4 h-10 font-mono text-muted-foreground hover:text-foreground hover:bg-muted"
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
|
|
@ -670,13 +670,13 @@ function ProjectCard({
|
|||
<div
|
||||
className={`flex items-center gap-3 p-3 cursor-pointer rounded transition-all ${selected
|
||||
? "bg-primary/10 border border-primary/50"
|
||||
: "hover:bg-gray-800/50 border border-transparent"
|
||||
: "hover:bg-muted border border-transparent"
|
||||
}`}
|
||||
onClick={onSelect}
|
||||
>
|
||||
<Checkbox
|
||||
checked={selected}
|
||||
className="border-gray-600 data-[state=checked]:bg-primary data-[state=checked]:border-primary"
|
||||
className="border-border data-[state=checked]:bg-primary data-[state=checked]:border-primary"
|
||||
/>
|
||||
|
||||
<div className={`p-1.5 rounded ${isRepo ? "bg-blue-500/20" : "bg-amber-500/20"}`}>
|
||||
|
|
@ -689,11 +689,11 @@ function ProjectCard({
|
|||
|
||||
<div className="flex-1 min-w-0 overflow-hidden">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className={`font-mono text-sm truncate ${selected ? 'text-white font-bold' : 'text-gray-300'}`}>
|
||||
<span className={`font-mono text-base truncate ${selected ? 'text-foreground font-bold' : 'text-foreground'}`}>
|
||||
{project.name}
|
||||
</span>
|
||||
<Badge
|
||||
className={`text-[10px] px-1 py-0 font-mono ${isRepo
|
||||
className={`text-xs px-1 py-0 font-mono ${isRepo
|
||||
? "bg-blue-500/20 text-blue-400 border-blue-500/30"
|
||||
: "bg-amber-500/20 text-amber-400 border-amber-500/30"
|
||||
}`}
|
||||
|
|
@ -702,7 +702,7 @@ function ProjectCard({
|
|||
</Badge>
|
||||
</div>
|
||||
{project.description && (
|
||||
<p className="text-xs text-gray-600 mt-0.5 font-mono line-clamp-2" title={project.description}>
|
||||
<p className="text-sm text-muted-foreground mt-0.5 font-mono line-clamp-2" title={project.description}>
|
||||
{project.description}
|
||||
</p>
|
||||
)}
|
||||
|
|
@ -722,7 +722,7 @@ function ZipUploadCard({
|
|||
}) {
|
||||
if (zipState.loading) {
|
||||
return (
|
||||
<div className="flex items-center gap-3 p-3 border border-gray-800 rounded bg-blue-950/20">
|
||||
<div className="flex items-center gap-3 p-3 border border-border rounded bg-blue-950/20">
|
||||
<Loader2 className="w-5 h-5 animate-spin text-blue-400" />
|
||||
<span className="text-sm font-mono text-blue-400">
|
||||
检查文件中...
|
||||
|
|
@ -733,7 +733,7 @@ function ZipUploadCard({
|
|||
|
||||
if (zipState.storedZipInfo?.has_file) {
|
||||
return (
|
||||
<div className="p-3 border border-gray-800 rounded bg-emerald-950/20 space-y-3">
|
||||
<div className="p-3 border border-border rounded bg-emerald-950/20 space-y-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-1.5 bg-emerald-500/20 rounded">
|
||||
<Package className="w-4 h-4 text-emerald-400" />
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ const getFileIcon = (path: string) => {
|
|||
if (configExts.includes(ext)) {
|
||||
return <FileJson className="w-4 h-4 text-amber-400" />;
|
||||
}
|
||||
return <File className="w-4 h-4 text-gray-500" />;
|
||||
return <File className="w-4 h-4 text-muted-foreground" />;
|
||||
};
|
||||
|
||||
// 获取文件扩展名
|
||||
|
|
@ -326,7 +326,7 @@ export default function FileSelectionDialog({
|
|||
items.push(
|
||||
<div key={`folder-${folder.path}`}>
|
||||
<div
|
||||
className="flex items-center space-x-2 p-2 hover:bg-gray-800/50 border border-transparent hover:border-gray-700 cursor-pointer transition-colors rounded"
|
||||
className="flex items-center space-x-2 p-2 hover:bg-muted border border-transparent hover:border-border cursor-pointer transition-colors rounded"
|
||||
style={{ paddingLeft: `${depth * 16 + 8}px` }}
|
||||
>
|
||||
<button
|
||||
|
|
@ -334,12 +334,12 @@ export default function FileSelectionDialog({
|
|||
e.stopPropagation();
|
||||
handleExpandFolder(folder.path);
|
||||
}}
|
||||
className="p-0.5 hover:bg-gray-700 rounded"
|
||||
className="p-0.5 hover:bg-muted rounded"
|
||||
>
|
||||
{isExpanded ? (
|
||||
<ChevronDown className="w-4 h-4 text-gray-500" />
|
||||
<ChevronDown className="w-4 h-4 text-muted-foreground" />
|
||||
) : (
|
||||
<ChevronRight className="w-4 h-4 text-gray-500" />
|
||||
<ChevronRight className="w-4 h-4 text-muted-foreground" />
|
||||
)}
|
||||
</button>
|
||||
<div onClick={(e) => e.stopPropagation()}>
|
||||
|
|
@ -352,7 +352,7 @@ export default function FileSelectionDialog({
|
|||
}
|
||||
}}
|
||||
onCheckedChange={() => handleToggleFolder(folder.path)}
|
||||
className="border-gray-600 data-[state=checked]:bg-primary data-[state=checked]:border-primary data-[state=indeterminate]:bg-gray-500"
|
||||
className="border-border data-[state=checked]:bg-primary data-[state=checked]:border-primary data-[state=indeterminate]:bg-background0"
|
||||
/>
|
||||
</div>
|
||||
{isExpanded ? (
|
||||
|
|
@ -361,12 +361,12 @@ export default function FileSelectionDialog({
|
|||
<Folder className="w-4 h-4 text-amber-400" />
|
||||
)}
|
||||
<span
|
||||
className="text-sm font-mono font-medium flex-1 text-gray-300"
|
||||
className="text-sm font-mono font-medium flex-1 text-foreground"
|
||||
onClick={() => handleExpandFolder(folder.path)}
|
||||
>
|
||||
{folder.name}
|
||||
</span>
|
||||
<Badge className="cyber-badge-muted font-mono text-[10px]">
|
||||
<Badge className="cyber-badge-muted font-mono text-xs">
|
||||
{
|
||||
filteredFiles.filter((f) =>
|
||||
f.path.startsWith(folder.path + "/")
|
||||
|
|
@ -387,7 +387,7 @@ export default function FileSelectionDialog({
|
|||
items.push(
|
||||
<div
|
||||
key={`file-${file.path}`}
|
||||
className="flex items-center space-x-3 p-2 hover:bg-gray-800/50 border border-transparent hover:border-gray-700 cursor-pointer transition-colors rounded"
|
||||
className="flex items-center space-x-3 p-2 hover:bg-muted border border-transparent hover:border-border cursor-pointer transition-colors rounded"
|
||||
style={{ paddingLeft: `${depth * 16 + 32}px` }}
|
||||
onClick={() => handleToggleFile(file.path)}
|
||||
>
|
||||
|
|
@ -395,18 +395,18 @@ export default function FileSelectionDialog({
|
|||
<Checkbox
|
||||
checked={selectedFiles.has(file.path)}
|
||||
onCheckedChange={() => handleToggleFile(file.path)}
|
||||
className="border-gray-600 data-[state=checked]:bg-primary data-[state=checked]:border-primary"
|
||||
className="border-border data-[state=checked]:bg-primary data-[state=checked]:border-primary"
|
||||
/>
|
||||
</div>
|
||||
{getFileIcon(file.path)}
|
||||
<span
|
||||
className="text-sm font-mono flex-1 min-w-0 truncate text-gray-300"
|
||||
className="text-sm font-mono flex-1 min-w-0 truncate text-foreground"
|
||||
title={file.path}
|
||||
>
|
||||
{fileName}
|
||||
</span>
|
||||
{file.size > 0 && (
|
||||
<Badge className="cyber-badge-muted font-mono text-[10px] flex-shrink-0">
|
||||
<Badge className="cyber-badge-muted font-mono text-xs flex-shrink-0">
|
||||
{formatSize(file.size)}
|
||||
</Badge>
|
||||
)}
|
||||
|
|
@ -422,24 +422,24 @@ export default function FileSelectionDialog({
|
|||
return filteredFiles.map((file) => (
|
||||
<div
|
||||
key={file.path}
|
||||
className="flex items-center space-x-3 p-2 hover:bg-gray-800/50 border border-transparent hover:border-gray-700 cursor-pointer transition-colors rounded"
|
||||
className="flex items-center space-x-3 p-2 hover:bg-muted border border-transparent hover:border-border cursor-pointer transition-colors rounded"
|
||||
onClick={() => handleToggleFile(file.path)}
|
||||
>
|
||||
<div onClick={(e) => e.stopPropagation()}>
|
||||
<Checkbox
|
||||
checked={selectedFiles.has(file.path)}
|
||||
onCheckedChange={() => handleToggleFile(file.path)}
|
||||
className="border-gray-600 data-[state=checked]:bg-primary data-[state=checked]:border-primary"
|
||||
className="border-border data-[state=checked]:bg-primary data-[state=checked]:border-primary"
|
||||
/>
|
||||
</div>
|
||||
{getFileIcon(file.path)}
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-mono truncate text-gray-300" title={file.path}>
|
||||
<p className="text-sm font-mono truncate text-foreground" title={file.path}>
|
||||
{file.path}
|
||||
</p>
|
||||
</div>
|
||||
{file.size > 0 && (
|
||||
<Badge className="cyber-badge-muted font-mono text-[10px] flex-shrink-0">
|
||||
<Badge className="cyber-badge-muted font-mono text-xs flex-shrink-0">
|
||||
{formatSize(file.size)}
|
||||
</Badge>
|
||||
)}
|
||||
|
|
@ -449,11 +449,11 @@ export default function FileSelectionDialog({
|
|||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="!max-w-[1000px] !w-[95vw] max-h-[85vh] flex flex-col cyber-card p-0 bg-[#0c0c12] !fixed">
|
||||
<DialogContent className="!max-w-[1000px] !w-[95vw] max-h-[85vh] flex flex-col cyber-card p-0 cyber-dialog !fixed">
|
||||
<DialogHeader className="cyber-card-header flex-shrink-0">
|
||||
<div className="flex items-center gap-3">
|
||||
<FolderOpen className="w-5 h-5 text-primary" />
|
||||
<DialogTitle className="text-lg font-bold uppercase tracking-wider text-white">
|
||||
<DialogTitle className="text-lg font-bold uppercase tracking-wider text-foreground">
|
||||
选择要审计的文件
|
||||
</DialogTitle>
|
||||
</div>
|
||||
|
|
@ -469,7 +469,7 @@ export default function FileSelectionDialog({
|
|||
<div className="flex items-center gap-2 flex-wrap">
|
||||
{/* 搜索框 */}
|
||||
<div className="relative flex-1 min-w-[200px]">
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500 w-4 h-4" />
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground w-4 h-4" />
|
||||
<Input
|
||||
placeholder="搜索文件..."
|
||||
value={searchTerm}
|
||||
|
|
@ -481,11 +481,11 @@ export default function FileSelectionDialog({
|
|||
{/* 文件类型筛选 */}
|
||||
{fileTypes.length > 0 && (
|
||||
<div className="flex items-center gap-1">
|
||||
<Filter className="w-4 h-4 text-gray-500" />
|
||||
<Filter className="w-4 h-4 text-muted-foreground" />
|
||||
<select
|
||||
value={filterType}
|
||||
onChange={(e) => setFilterType(e.target.value)}
|
||||
className="h-9 px-2 cyber-input font-mono text-sm bg-[#0a0a0f]"
|
||||
className="h-9 px-2 cyber-input font-mono text-sm cyber-bg-elevated"
|
||||
>
|
||||
<option value="">全部类型</option>
|
||||
{fileTypes.slice(0, 10).map(([ext, count]) => (
|
||||
|
|
@ -498,16 +498,16 @@ export default function FileSelectionDialog({
|
|||
)}
|
||||
|
||||
{/* 视图切换 */}
|
||||
<div className="flex border border-gray-700 rounded overflow-hidden">
|
||||
<div className="flex border border-border rounded overflow-hidden">
|
||||
<button
|
||||
onClick={() => setViewMode("tree")}
|
||||
className={`px-3 py-1.5 text-xs font-mono uppercase ${viewMode === "tree" ? "bg-primary text-white" : "bg-gray-800 text-gray-400 hover:bg-gray-700"}`}
|
||||
className={`px-3 py-1.5 text-xs font-mono uppercase ${viewMode === "tree" ? "bg-primary text-foreground" : "bg-muted text-muted-foreground hover:bg-muted"}`}
|
||||
>
|
||||
树形
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setViewMode("flat")}
|
||||
className={`px-3 py-1.5 text-xs font-mono uppercase border-l border-gray-700 ${viewMode === "flat" ? "bg-primary text-white" : "bg-gray-800 text-gray-400 hover:bg-gray-700"}`}
|
||||
className={`px-3 py-1.5 text-xs font-mono uppercase border-l border-border ${viewMode === "flat" ? "bg-primary text-foreground" : "bg-muted text-muted-foreground hover:bg-muted"}`}
|
||||
>
|
||||
列表
|
||||
</button>
|
||||
|
|
@ -552,14 +552,14 @@ export default function FileSelectionDialog({
|
|||
setSearchTerm("");
|
||||
setFilterType("");
|
||||
}}
|
||||
className="h-8 px-3 cyber-btn-outline font-mono text-xs text-gray-400"
|
||||
className="h-8 px-3 cyber-btn-outline font-mono text-xs text-muted-foreground"
|
||||
>
|
||||
<RotateCcw className="w-3 h-3 mr-1" />
|
||||
重置筛选
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div className="text-sm font-mono text-gray-500">
|
||||
<div className="text-sm font-mono text-muted-foreground">
|
||||
{searchTerm || filterType ? (
|
||||
<span>
|
||||
筛选: {filteredFiles.length}/{files.length} 个文件,
|
||||
|
|
@ -574,7 +574,7 @@ export default function FileSelectionDialog({
|
|||
</div>
|
||||
|
||||
{/* 文件列表 */}
|
||||
<div className="border border-gray-800 bg-[#0a0a0f] relative h-[450px] overflow-hidden rounded">
|
||||
<div className="border border-border cyber-bg-elevated relative h-[450px] overflow-hidden rounded">
|
||||
{loading ? (
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<div className="loading-spinner" />
|
||||
|
|
@ -588,7 +588,7 @@ export default function FileSelectionDialog({
|
|||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="absolute inset-0 flex flex-col items-center justify-center text-gray-500">
|
||||
<div className="absolute inset-0 flex flex-col items-center justify-center text-muted-foreground">
|
||||
<FileText className="w-12 h-12 mb-2 opacity-20" />
|
||||
<p className="font-mono text-sm">
|
||||
{searchTerm || filterType
|
||||
|
|
@ -600,8 +600,8 @@ export default function FileSelectionDialog({
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter className="p-5 border-t border-gray-800 bg-gray-900/50 flex-shrink-0 flex justify-between">
|
||||
<div className="text-xs font-mono text-gray-600 flex items-center gap-2">
|
||||
<DialogFooter className="p-5 border-t border-border bg-muted flex-shrink-0 flex justify-between">
|
||||
<div className="text-xs font-mono text-muted-foreground flex items-center gap-2">
|
||||
<Terminal className="w-3 h-3" />
|
||||
提示:点击文件夹可展开/折叠,点击文件夹复选框可批量选择
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -425,7 +425,7 @@ export default function TerminalProgressDialog({
|
|||
case "warning":
|
||||
return "text-amber-400";
|
||||
default:
|
||||
return "text-gray-400";
|
||||
return "text-muted-foreground";
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -462,13 +462,13 @@ export default function TerminalProgressDialog({
|
|||
/>
|
||||
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between px-4 py-3 bg-[#0a0c10] border-b border-[#1a2535]"
|
||||
<div className="flex items-center justify-between px-4 py-3 cyber-bg-elevated border-b border-[#1a2535]"
|
||||
style={{ backgroundImage: "linear-gradient(90deg, rgba(255, 95, 31, 0.05) 0%, transparent 50%, rgba(14, 181, 196, 0.05) 100%)" }}>
|
||||
<div className="flex items-center gap-3">
|
||||
<Terminal className="w-5 h-5 text-primary" style={{ filter: "drop-shadow(0 0 8px rgba(255, 95, 31, 0.5))" }} />
|
||||
<div>
|
||||
<span className="text-lg font-bold uppercase tracking-[0.15em] text-[#f0e6d3]" style={{ textShadow: "0 0 20px rgba(255, 95, 31, 0.3)" }}>AUDIT_TERMINAL</span>
|
||||
<span className="text-[10px] text-[#5a6577] ml-2 tracking-wider">v3.0</span>
|
||||
<span className="text-xs text-[#5a6577] ml-2 tracking-wider">v3.0</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -501,16 +501,16 @@ export default function TerminalProgressDialog({
|
|||
{/* Left Sidebar - Task Info */}
|
||||
<div className="w-48 p-4 border-r border-[#1a2535] bg-[#060810] flex flex-col gap-4">
|
||||
<div className="space-y-1.5">
|
||||
<div className="text-[9px] font-bold text-[#5a6577] uppercase tracking-[0.15em]">Task ID</div>
|
||||
<div className="text-xs font-mono text-primary truncate bg-[#0a0c10] p-2.5 rounded border border-[#1a2535]"
|
||||
<div className="text-xs font-bold text-[#5a6577] uppercase tracking-[0.15em]">Task ID</div>
|
||||
<div className="text-xs font-mono text-primary truncate cyber-bg-elevated p-2.5 rounded border border-[#1a2535]"
|
||||
style={{ textShadow: "0 0 10px rgba(255, 95, 31, 0.3)" }}>
|
||||
{taskId?.slice(0, 8)}...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<div className="text-[9px] font-bold text-[#5a6577] uppercase tracking-[0.15em]">Type</div>
|
||||
<div className="flex items-center gap-2 bg-[#0a0c10] p-2.5 rounded border border-[#1a2535]">
|
||||
<div className="text-xs font-bold text-[#5a6577] uppercase tracking-[0.15em]">Type</div>
|
||||
<div className="flex items-center gap-2 cyber-bg-elevated p-2.5 rounded border border-[#1a2535]">
|
||||
{taskType === 'repository'
|
||||
? <Cpu className="w-3.5 h-3.5 text-[#22d3ee]" style={{ filter: "drop-shadow(0 0 6px rgba(34, 211, 238, 0.5))" }} />
|
||||
: <HardDrive className="w-3.5 h-3.5 text-[#fbbf24]" style={{ filter: "drop-shadow(0 0 6px rgba(251, 191, 36, 0.5))" }} />}
|
||||
|
|
@ -522,7 +522,7 @@ export default function TerminalProgressDialog({
|
|||
|
||||
{/* Status Badge */}
|
||||
<div className="space-y-2">
|
||||
<div className="text-[9px] font-bold text-[#5a6577] uppercase tracking-[0.15em]">Status</div>
|
||||
<div className="text-xs font-bold text-[#5a6577] uppercase tracking-[0.15em]">Status</div>
|
||||
{isCancelled ? (
|
||||
<Badge className="w-full justify-center cyber-badge-warning">CANCELLED</Badge>
|
||||
) : isCompleted ? (
|
||||
|
|
@ -566,7 +566,7 @@ export default function TerminalProgressDialog({
|
|||
</div>
|
||||
|
||||
{/* Bottom Controls */}
|
||||
<div className="h-14 px-4 border-t border-[#1a2535] bg-[#0a0c10]/90 flex items-center justify-between">
|
||||
<div className="h-14 px-4 border-t border-[#1a2535] cyber-bg-elevated/90 flex items-center justify-between">
|
||||
<div className="flex items-center gap-2 text-xs text-[#6a7587] font-mono tracking-wide">
|
||||
<Activity className="w-3.5 h-3.5" />
|
||||
<span>
|
||||
|
|
@ -580,7 +580,7 @@ export default function TerminalProgressDialog({
|
|||
size="sm"
|
||||
variant="outline"
|
||||
onClick={handleCancel}
|
||||
className="h-8 bg-transparent border-[#fbbf24]/40 text-[#fbbf24] hover:bg-[#fbbf24]/10 hover:border-[#fbbf24]/60 font-mono uppercase tracking-wider text-[10px]"
|
||||
className="h-8 bg-transparent border-[#fbbf24]/40 text-[#fbbf24] hover:bg-[#fbbf24]/10 hover:border-[#fbbf24]/60 font-mono uppercase tracking-wider text-xs"
|
||||
>
|
||||
<AlertTriangle className="w-3 h-3 mr-1.5" />
|
||||
取消任务
|
||||
|
|
@ -592,7 +592,7 @@ export default function TerminalProgressDialog({
|
|||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => window.open('/logs', '_blank')}
|
||||
className="h-8 bg-transparent border-[#6a7587]/40 text-[#a8b0c0] hover:bg-[#1a2030]/50 hover:border-[#6a7587]/60 font-mono uppercase tracking-wider text-[10px]"
|
||||
className="h-8 bg-transparent border-[#6a7587]/40 text-[#a8b0c0] hover:bg-[#1a2030]/50 hover:border-[#6a7587]/60 font-mono uppercase tracking-wider text-xs"
|
||||
>
|
||||
<Activity className="w-3 h-3 mr-1.5" />
|
||||
查看日志
|
||||
|
|
@ -603,7 +603,7 @@ export default function TerminalProgressDialog({
|
|||
<Button
|
||||
size="sm"
|
||||
onClick={() => onOpenChange(false)}
|
||||
className="h-8 cyber-btn-primary font-mono uppercase tracking-wider text-[10px]"
|
||||
className="h-8 cyber-btn-primary font-mono uppercase tracking-wider text-xs"
|
||||
>
|
||||
<CheckCircle2 className="w-3 h-3 mr-1.5" />
|
||||
确认
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ export default function AdvancedOptions({
|
|||
<div className="space-y-6">
|
||||
<div>
|
||||
<Label className="text-base font-bold uppercase">扫描配置</Label>
|
||||
<p className="text-sm text-gray-500 mt-1 font-bold">
|
||||
<p className="text-sm text-muted-foreground mt-1 font-bold">
|
||||
配置代码扫描的详细参数
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -83,11 +83,11 @@ export default function AdvancedOptions({
|
|||
>
|
||||
<SelectTrigger
|
||||
id="analysis_depth"
|
||||
className="retro-input h-10 rounded-none border-2 border-black shadow-none focus:ring-0"
|
||||
className="retro-input h-10 rounded-none border-2 border-border shadow-none focus:ring-0"
|
||||
>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="rounded-none border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)]">
|
||||
<SelectContent className="rounded-none border-2 border-border shadow-[4px_4px_0px_0px_rgba(0,0,0,1)]">
|
||||
<SelectItem value="basic" className="font-mono">
|
||||
基础 (快速)
|
||||
</SelectItem>
|
||||
|
|
@ -104,16 +104,16 @@ export default function AdvancedOptions({
|
|||
</div>
|
||||
|
||||
{/* 分析范围 */}
|
||||
<div className="space-y-2 border-t-2 border-dashed border-gray-300 pt-4">
|
||||
<div className="space-y-2 border-t-2 border-dashed border-border pt-4">
|
||||
<Label className="font-bold uppercase">分析范围</Label>
|
||||
<div className="flex items-center justify-between p-3 border-2 border-black bg-white">
|
||||
<div className="flex items-center justify-between p-3 border-2 border-border bg-background">
|
||||
<div>
|
||||
<p className="text-sm font-bold uppercase">
|
||||
{hasSelectedFiles
|
||||
? `已选择 ${scanConfig.file_paths!.length} 个文件`
|
||||
: "全量扫描 (所有文件)"}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 font-bold">
|
||||
<p className="text-xs text-muted-foreground font-bold">
|
||||
{hasSelectedFiles
|
||||
? "仅分析选中的文件"
|
||||
: "分析项目中的所有代码文件"}
|
||||
|
|
@ -126,7 +126,7 @@ export default function AdvancedOptions({
|
|||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => onUpdate({ file_paths: undefined })}
|
||||
className="retro-btn bg-white text-red-600 hover:bg-red-50 h-8"
|
||||
className="retro-btn bg-background text-red-600 hover:bg-red-50 h-8"
|
||||
>
|
||||
重置
|
||||
</Button>
|
||||
|
|
@ -136,7 +136,7 @@ export default function AdvancedOptions({
|
|||
variant="outline"
|
||||
size="sm"
|
||||
onClick={onOpenFileSelection}
|
||||
className="retro-btn bg-white text-black hover:bg-gray-50 h-8"
|
||||
className="retro-btn bg-background text-foreground hover:bg-background h-8"
|
||||
>
|
||||
{hasSelectedFiles ? "修改选择" : "选择文件"}
|
||||
</Button>
|
||||
|
|
@ -162,15 +162,15 @@ function CheckboxOption({
|
|||
description: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="flex items-center space-x-3 p-3 border-2 border-black bg-white">
|
||||
<div className="flex items-center space-x-3 p-3 border-2 border-border bg-background">
|
||||
<Checkbox
|
||||
checked={checked}
|
||||
onCheckedChange={(c) => onChange(!!c)}
|
||||
className="rounded-none border-2 border-black data-[state=checked]:bg-primary data-[state=checked]:text-white"
|
||||
className="rounded-none border-2 border-border data-[state=checked]:bg-primary data-[state=checked]:text-foreground"
|
||||
/>
|
||||
<div>
|
||||
<p className="text-sm font-bold uppercase">{label}</p>
|
||||
<p className="text-xs text-gray-500 font-bold">{description}</p>
|
||||
<p className="text-xs text-muted-foreground font-bold">{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -178,7 +178,7 @@ function CheckboxOption({
|
|||
|
||||
function DepthExplanation() {
|
||||
return (
|
||||
<div className="bg-amber-50 border-2 border-black p-4 shadow-[4px_4px_0px_0px_rgba(0,0,0,1)]">
|
||||
<div className="bg-amber-50 border-2 border-border p-4 shadow-[4px_4px_0px_0px_rgba(0,0,0,1)]">
|
||||
<div className="flex items-start space-x-3">
|
||||
<AlertCircle className="w-5 h-5 text-amber-600 mt-0.5" />
|
||||
<div className="text-sm font-mono">
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export default function ExcludePatterns({
|
|||
<div className="space-y-4">
|
||||
<div>
|
||||
<Label className="text-base font-bold uppercase">排除模式</Label>
|
||||
<p className="text-sm text-gray-500 mt-1 font-bold">
|
||||
<p className="text-sm text-muted-foreground mt-1 font-bold">
|
||||
选择要从审计中排除的文件和目录模式
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -46,16 +46,16 @@ export default function ExcludePatterns({
|
|||
{COMMON_EXCLUDE_PATTERNS.map((pattern) => (
|
||||
<div
|
||||
key={pattern.value}
|
||||
className="flex items-center space-x-3 p-3 border-2 border-black bg-white hover:bg-gray-50 transition-all"
|
||||
className="flex items-center space-x-3 p-3 border-2 border-border bg-background hover:bg-background transition-all"
|
||||
>
|
||||
<Checkbox
|
||||
checked={patterns.includes(pattern.value)}
|
||||
onCheckedChange={() => onToggle(pattern.value)}
|
||||
className="rounded-none border-2 border-black data-[state=checked]:bg-primary data-[state=checked]:text-white"
|
||||
className="rounded-none border-2 border-border data-[state=checked]:bg-primary data-[state=checked]:text-foreground"
|
||||
/>
|
||||
<div className="flex-1">
|
||||
<p className="text-sm font-bold uppercase">{pattern.label}</p>
|
||||
<p className="text-xs text-gray-500 font-bold">
|
||||
<p className="text-xs text-muted-foreground font-bold">
|
||||
{pattern.description}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -103,7 +103,7 @@ function CustomPatternInput({ onAdd }: { onAdd: (pattern: string) => void }) {
|
|||
.previousElementSibling as HTMLInputElement;
|
||||
handleAdd(input);
|
||||
}}
|
||||
className="retro-btn bg-white text-black h-10"
|
||||
className="retro-btn bg-background text-foreground h-10"
|
||||
>
|
||||
添加
|
||||
</Button>
|
||||
|
|
@ -127,7 +127,7 @@ function SelectedPatterns({
|
|||
<Badge
|
||||
key={pattern}
|
||||
variant="secondary"
|
||||
className="cursor-pointer hover:bg-red-100 hover:text-red-800 rounded-none border-2 border-black bg-gray-100 text-black font-mono font-bold"
|
||||
className="cursor-pointer hover:bg-red-100 hover:text-red-800 rounded-none border-2 border-border bg-muted text-foreground font-mono font-bold"
|
||||
onClick={() => onRemove(pattern)}
|
||||
>
|
||||
{pattern} ×
|
||||
|
|
|
|||
|
|
@ -39,14 +39,14 @@ export default function ProjectSelector({
|
|||
</Label>
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="text-xs rounded-none border-black font-mono"
|
||||
className="text-xs rounded-none border-border font-mono"
|
||||
>
|
||||
{filteredProjects.length} 个可用项目
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-black w-4 h-4" />
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-foreground w-4 h-4" />
|
||||
<Input
|
||||
placeholder="搜索项目名称..."
|
||||
value={searchTerm}
|
||||
|
|
@ -91,7 +91,7 @@ function ProjectCard({
|
|||
className={`cursor-pointer transition-all border-2 p-4 relative ${
|
||||
isSelected
|
||||
? "border-primary bg-blue-50 shadow-[4px_4px_0px_0px_rgba(37,99,235,1)] translate-x-[-2px] translate-y-[-2px]"
|
||||
: "border-black bg-white hover:bg-gray-50 hover:shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] hover:translate-x-[-2px] hover:translate-y-[-2px]"
|
||||
: "border-border bg-background hover:bg-background hover:shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] hover:translate-x-[-2px] hover:translate-y-[-2px]"
|
||||
}`}
|
||||
onClick={onSelect}
|
||||
>
|
||||
|
|
@ -101,11 +101,11 @@ function ProjectCard({
|
|||
{project.name}
|
||||
</h4>
|
||||
{project.description && (
|
||||
<p className="text-xs text-gray-600 mt-1 line-clamp-2 font-mono">
|
||||
<p className="text-xs text-muted-foreground mt-1 line-clamp-2 font-mono">
|
||||
{project.description}
|
||||
</p>
|
||||
)}
|
||||
<div className="flex items-center space-x-4 mt-2 text-xs text-gray-500 font-mono font-bold">
|
||||
<div className="flex items-center space-x-4 mt-2 text-xs text-muted-foreground font-mono font-bold">
|
||||
<span
|
||||
className={`px-1.5 py-0.5 ${isRepo ? "bg-blue-100 text-blue-700" : "bg-amber-100 text-amber-700"}`}
|
||||
>
|
||||
|
|
@ -122,8 +122,8 @@ function ProjectCard({
|
|||
</div>
|
||||
</div>
|
||||
{isSelected && (
|
||||
<div className="w-5 h-5 bg-primary border-2 border-black flex items-center justify-center">
|
||||
<div className="w-2 h-2 bg-white" />
|
||||
<div className="w-5 h-5 bg-primary border-2 border-border flex items-center justify-center">
|
||||
<div className="w-2 h-2 bg-background" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -141,7 +141,7 @@ function LoadingSpinner() {
|
|||
|
||||
function EmptyState({ hasSearch }: { hasSearch: boolean }) {
|
||||
return (
|
||||
<div className="col-span-2 text-center py-8 text-gray-500 font-mono">
|
||||
<div className="col-span-2 text-center py-8 text-muted-foreground font-mono">
|
||||
<FileText className="w-8 h-8 mx-auto mb-2 opacity-50" />
|
||||
<p className="text-sm">
|
||||
{hasSearch ? "未找到匹配的项目" : "暂无可用项目"}
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ export function DatabaseManager() {
|
|||
<div className="cyber-card p-0">
|
||||
<div className="cyber-card-header">
|
||||
<Activity className="w-5 h-5 text-emerald-400" />
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-white">数据库健康检查</h3>
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-foreground">数据库健康检查</h3>
|
||||
<div className="ml-auto">
|
||||
<Button
|
||||
variant="outline"
|
||||
|
|
@ -238,16 +238,16 @@ export function DatabaseManager() {
|
|||
<AlertCircle className="h-5 w-5 text-rose-400" />
|
||||
)}
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-bold uppercase text-sm text-gray-400">状态:</span>
|
||||
<span className="font-bold uppercase text-sm text-muted-foreground">状态:</span>
|
||||
{getHealthStatusBadge(health.status)}
|
||||
</div>
|
||||
<span className="text-sm text-gray-400">
|
||||
<span className="text-sm text-muted-foreground">
|
||||
数据库连接:
|
||||
<span className={health.database_connected ? 'text-emerald-400' : 'text-rose-400'}>
|
||||
{health.database_connected ? '正常' : '异常'}
|
||||
</span>
|
||||
<span className="mx-2">|</span>
|
||||
总记录数:<span className="text-white">{health.total_records.toLocaleString()}</span>
|
||||
总记录数:<span className="text-foreground">{health.total_records.toLocaleString()}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
|
@ -292,7 +292,7 @@ export function DatabaseManager() {
|
|||
<div className="cyber-card p-0">
|
||||
<div className="cyber-card-header">
|
||||
<Database className="w-5 h-5 text-violet-400" />
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-white">详细数据统计</h3>
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-foreground">详细数据统计</h3>
|
||||
<div className="ml-auto">
|
||||
<Button
|
||||
variant="outline"
|
||||
|
|
@ -321,17 +321,17 @@ export function DatabaseManager() {
|
|||
<div className="cyber-card p-4">
|
||||
<p className="stat-label">任务</p>
|
||||
<p className="stat-value text-emerald-400">{stats.total_tasks}</p>
|
||||
<p className="text-xs text-gray-500 mt-1">完成: {stats.completed_tasks} | 进行中: {stats.running_tasks}</p>
|
||||
<p className="text-xs text-muted-foreground mt-1">完成: {stats.completed_tasks} | 进行中: {stats.running_tasks}</p>
|
||||
</div>
|
||||
<div className="cyber-card p-4">
|
||||
<p className="stat-label">问题</p>
|
||||
<p className="stat-value text-amber-400">{stats.total_issues}</p>
|
||||
<p className="text-xs text-gray-500 mt-1">未解决: {stats.open_issues} | 已解决: {stats.resolved_issues}</p>
|
||||
<p className="text-xs text-muted-foreground mt-1">未解决: {stats.open_issues} | 已解决: {stats.resolved_issues}</p>
|
||||
</div>
|
||||
<div className="cyber-card p-4">
|
||||
<p className="stat-label">分析记录</p>
|
||||
<p className="stat-value text-violet-400">{stats.total_analyses}</p>
|
||||
<p className="text-xs text-gray-500 mt-1">即时分析</p>
|
||||
<p className="text-xs text-muted-foreground mt-1">即时分析</p>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
|
|
@ -347,7 +347,7 @@ export function DatabaseManager() {
|
|||
<div className="cyber-card p-0">
|
||||
<div className="cyber-card-header">
|
||||
<Database className="w-5 h-5 text-primary" />
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-white">数据操作</h3>
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-foreground">数据操作</h3>
|
||||
</div>
|
||||
<div className="p-6 space-y-6">
|
||||
{message && (
|
||||
|
|
@ -369,11 +369,11 @@ export function DatabaseManager() {
|
|||
|
||||
<div className="grid gap-6 md:grid-cols-3">
|
||||
<div className="space-y-3">
|
||||
<h4 className="text-sm font-bold uppercase text-gray-300 flex items-center gap-2">
|
||||
<h4 className="text-sm font-bold uppercase text-foreground flex items-center gap-2">
|
||||
<Download className="h-4 w-4 text-sky-400" />
|
||||
导出数据
|
||||
</h4>
|
||||
<p className="text-xs text-gray-500">将数据导出为 JSON 文件,用于备份或迁移</p>
|
||||
<p className="text-xs text-muted-foreground">将数据导出为 JSON 文件,用于备份或迁移</p>
|
||||
<Button
|
||||
onClick={handleExport}
|
||||
disabled={loading}
|
||||
|
|
@ -385,11 +385,11 @@ export function DatabaseManager() {
|
|||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<h4 className="text-sm font-bold uppercase text-gray-300 flex items-center gap-2">
|
||||
<h4 className="text-sm font-bold uppercase text-foreground flex items-center gap-2">
|
||||
<Upload className="h-4 w-4 text-emerald-400" />
|
||||
导入数据
|
||||
</h4>
|
||||
<p className="text-xs text-gray-500">从 JSON 文件恢复数据(最大 50MB)</p>
|
||||
<p className="text-xs text-muted-foreground">从 JSON 文件恢复数据(最大 50MB)</p>
|
||||
<Button
|
||||
onClick={() => document.getElementById('import-file')?.click()}
|
||||
disabled={loading}
|
||||
|
|
@ -412,7 +412,7 @@ export function DatabaseManager() {
|
|||
<Trash2 className="h-4 w-4" />
|
||||
清空数据
|
||||
</h4>
|
||||
<p className="text-xs text-gray-500">删除所有数据(不可恢复)</p>
|
||||
<p className="text-xs text-muted-foreground">删除所有数据(不可恢复)</p>
|
||||
<Button
|
||||
onClick={handleClear}
|
||||
disabled={loading}
|
||||
|
|
@ -424,7 +424,7 @@ export function DatabaseManager() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="pt-6 border-t border-gray-800 border-dashed">
|
||||
<div className="pt-6 border-t border-border border-dashed">
|
||||
<div className="bg-sky-500/10 border border-sky-500/30 p-4 flex items-start gap-3 rounded-lg">
|
||||
<Info className="h-5 w-5 text-sky-400 mt-0.5 flex-shrink-0" />
|
||||
<p className="text-sm text-sky-300/80">
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ export default function DatabaseTest() {
|
|||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-sm text-gray-600">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
测试数据库连接状态和基本功能
|
||||
</p>
|
||||
<Button
|
||||
|
|
@ -188,7 +188,7 @@ export default function DatabaseTest() {
|
|||
{getStatusIcon(result.status)}
|
||||
<div>
|
||||
<p className="font-medium text-sm">{result.name}</p>
|
||||
<p className="text-xs text-gray-500">{result.message}</p>
|
||||
<p className="text-xs text-muted-foreground">{result.message}</p>
|
||||
</div>
|
||||
</div>
|
||||
{getStatusBadge(result.status)}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
import { useState } from "react";
|
||||
import { Link, useLocation } from "react-router-dom";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ThemeToggle } from "@/components/ui/theme-toggle";
|
||||
import {
|
||||
Menu,
|
||||
X,
|
||||
|
|
@ -22,22 +23,21 @@ import {
|
|||
Shield,
|
||||
MessageSquare,
|
||||
Bot,
|
||||
Terminal
|
||||
} from "lucide-react";
|
||||
import routes from "@/app/routes";
|
||||
import { version } from "../../../package.json";
|
||||
|
||||
// Icon mapping for routes
|
||||
const routeIcons: Record<string, React.ReactNode> = {
|
||||
"/": <Bot className="w-5 h-5" />,
|
||||
"/dashboard": <LayoutDashboard className="w-5 h-5" />,
|
||||
"/projects": <FolderGit2 className="w-5 h-5" />,
|
||||
"/instant-analysis": <Zap className="w-5 h-5" />,
|
||||
"/audit-tasks": <ListTodo className="w-5 h-5" />,
|
||||
"/audit-rules": <Shield className="w-5 h-5" />,
|
||||
"/prompts": <MessageSquare className="w-5 h-5" />,
|
||||
"/admin": <Settings className="w-5 h-5" />,
|
||||
"/recycle-bin": <Trash2 className="w-5 h-5" />,
|
||||
"/": <Bot className="w-6 h-6" />,
|
||||
"/dashboard": <LayoutDashboard className="w-6 h-6" />,
|
||||
"/projects": <FolderGit2 className="w-6 h-6" />,
|
||||
"/instant-analysis": <Zap className="w-6 h-6" />,
|
||||
"/audit-tasks": <ListTodo className="w-6 h-6" />,
|
||||
"/audit-rules": <Shield className="w-6 h-6" />,
|
||||
"/prompts": <MessageSquare className="w-6 h-6" />,
|
||||
"/admin": <Settings className="w-6 h-6" />,
|
||||
"/recycle-bin": <Trash2 className="w-6 h-6" />,
|
||||
};
|
||||
|
||||
interface SidebarProps {
|
||||
|
|
@ -57,7 +57,12 @@ export default function Sidebar({ collapsed, setCollapsed }: SidebarProps) {
|
|||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="fixed top-4 left-4 z-50 md:hidden bg-[#0c0c12] border border-gray-800 text-gray-300 hover:bg-gray-800 hover:text-white"
|
||||
className="fixed top-4 left-4 z-50 md:hidden"
|
||||
style={{
|
||||
background: 'var(--cyber-bg)',
|
||||
border: '1px solid var(--cyber-border)',
|
||||
color: 'var(--cyber-text-muted)'
|
||||
}}
|
||||
onClick={() => setMobileOpen(!mobileOpen)}
|
||||
>
|
||||
{mobileOpen ? <X className="w-5 h-5" /> : <Menu className="w-5 h-5" />}
|
||||
|
|
@ -74,12 +79,14 @@ export default function Sidebar({ collapsed, setCollapsed }: SidebarProps) {
|
|||
{/* Sidebar */}
|
||||
<aside
|
||||
className={`
|
||||
fixed top-0 left-0 h-screen
|
||||
bg-[#0a0a0f] border-r border-gray-800/60
|
||||
z-40 transition-all duration-300 ease-in-out
|
||||
fixed top-0 left-0 h-screen z-40 transition-all duration-300 ease-in-out
|
||||
${collapsed ? "w-20" : "w-64"}
|
||||
${mobileOpen ? "translate-x-0" : "-translate-x-full md:translate-x-0"}
|
||||
`}
|
||||
style={{
|
||||
background: 'var(--cyber-bg)',
|
||||
borderRight: '1px solid var(--cyber-border)'
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-col h-full relative">
|
||||
{/* Subtle grid background */}
|
||||
|
|
@ -87,30 +94,35 @@ export default function Sidebar({ collapsed, setCollapsed }: SidebarProps) {
|
|||
className="absolute inset-0 opacity-30 pointer-events-none"
|
||||
style={{
|
||||
backgroundImage: `
|
||||
linear-gradient(rgba(255,107,44,0.03) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(255,107,44,0.03) 1px, transparent 1px)
|
||||
linear-gradient(var(--cyber-border-accent) 1px, transparent 1px),
|
||||
linear-gradient(90deg, var(--cyber-border-accent) 1px, transparent 1px)
|
||||
`,
|
||||
backgroundSize: '24px 24px',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Logo Section */}
|
||||
<div className={`
|
||||
relative flex items-center h-[72px]
|
||||
border-b border-gray-800/60 bg-[#0c0c12]
|
||||
${collapsed ? 'px-3 justify-center' : 'px-4 pr-6'}
|
||||
`}>
|
||||
<div
|
||||
className={`relative flex items-center h-[72px] ${collapsed ? 'px-3 justify-center' : 'px-4 pr-6'}`}
|
||||
style={{
|
||||
background: 'var(--cyber-bg-elevated)',
|
||||
borderBottom: '1px solid var(--cyber-border)'
|
||||
}}
|
||||
>
|
||||
<Link
|
||||
to="/"
|
||||
className={`
|
||||
flex items-center gap-3 group transition-all duration-300
|
||||
${collapsed ? 'justify-center' : 'flex-1 min-w-0'}
|
||||
`}
|
||||
className={`flex items-center gap-3 group transition-all duration-300 ${collapsed ? 'justify-center' : 'flex-1 min-w-0'}`}
|
||||
onClick={() => setMobileOpen(false)}
|
||||
>
|
||||
{/* Logo Icon */}
|
||||
<div className="relative flex-shrink-0">
|
||||
<div className="w-10 h-10 bg-[#0a0a0f] border border-primary/30 rounded-lg flex items-center justify-center overflow-hidden group-hover:border-primary/60 transition-colors">
|
||||
<div
|
||||
className="w-10 h-10 rounded-lg flex items-center justify-center overflow-hidden group-hover:border-primary/60 transition-colors"
|
||||
style={{
|
||||
background: 'var(--cyber-bg)',
|
||||
border: '1px solid hsl(var(--primary) / 0.3)'
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src="/logo_deepaudit.png"
|
||||
alt="DeepAudit"
|
||||
|
|
@ -122,31 +134,27 @@ export default function Sidebar({ collapsed, setCollapsed }: SidebarProps) {
|
|||
</div>
|
||||
|
||||
{/* Logo Text */}
|
||||
<div className={`
|
||||
transition-all duration-300
|
||||
${collapsed ? 'w-0 opacity-0 overflow-hidden' : 'flex-1 min-w-0 opacity-100'}
|
||||
`}>
|
||||
<div className={`transition-all duration-300 ${collapsed ? 'w-0 opacity-0 overflow-hidden' : 'flex-1 min-w-0 opacity-100'}`}>
|
||||
<div
|
||||
className="text-xl font-bold tracking-wider font-mono"
|
||||
style={{ textShadow: '0 0 20px rgba(255,107,44,0.3)' }}
|
||||
>
|
||||
<span className="text-primary">DEEP</span>
|
||||
<span className="text-white">AUDIT</span>
|
||||
<span style={{ color: 'var(--cyber-text)' }}>AUDIT</span>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
{/* Collapse button */}
|
||||
<button
|
||||
className={`
|
||||
hidden md:flex absolute -right-3 top-1/2 -translate-y-1/2
|
||||
w-6 h-6 bg-[#0c0c12] border border-gray-700 rounded
|
||||
items-center justify-center text-gray-500
|
||||
hover:bg-primary hover:border-primary hover:text-white
|
||||
transition-all duration-200
|
||||
`}
|
||||
className="hidden md:flex absolute -right-3 top-1/2 -translate-y-1/2 w-6 h-6 rounded items-center justify-center hover:bg-primary hover:border-primary hover:text-foreground transition-all duration-200"
|
||||
style={{
|
||||
background: 'var(--cyber-bg)',
|
||||
border: '1px solid var(--cyber-border)',
|
||||
color: 'var(--cyber-text-muted)',
|
||||
zIndex: 100
|
||||
}}
|
||||
onClick={() => setCollapsed(!collapsed)}
|
||||
style={{ zIndex: 100 }}
|
||||
>
|
||||
{collapsed ? (
|
||||
<ChevronRight className="w-3 h-3" />
|
||||
|
|
@ -165,16 +173,26 @@ export default function Sidebar({ collapsed, setCollapsed }: SidebarProps) {
|
|||
<Link
|
||||
key={route.path}
|
||||
to={route.path}
|
||||
className={`
|
||||
flex items-center gap-3 px-3 py-2.5
|
||||
transition-all duration-200 group relative rounded-lg
|
||||
${isActive
|
||||
? "bg-primary/15 text-primary border border-primary/30"
|
||||
: "text-gray-400 hover:text-gray-200 hover:bg-gray-800/50 border border-transparent"
|
||||
}
|
||||
`}
|
||||
className="flex items-center gap-3 px-3 py-2.5 transition-all duration-200 group relative rounded-lg"
|
||||
style={{
|
||||
background: isActive ? 'hsl(var(--primary) / 0.15)' : 'transparent',
|
||||
border: isActive ? '1px solid hsl(var(--primary) / 0.3)' : '1px solid transparent',
|
||||
color: isActive ? 'hsl(var(--primary))' : 'var(--cyber-text-muted)'
|
||||
}}
|
||||
onClick={() => setMobileOpen(false)}
|
||||
title={collapsed ? route.name : undefined}
|
||||
onMouseEnter={(e) => {
|
||||
if (!isActive) {
|
||||
e.currentTarget.style.background = 'var(--cyber-hover-bg)';
|
||||
e.currentTarget.style.color = 'var(--cyber-text)';
|
||||
}
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
if (!isActive) {
|
||||
e.currentTarget.style.background = 'transparent';
|
||||
e.currentTarget.style.color = 'var(--cyber-text-muted)';
|
||||
}
|
||||
}}
|
||||
>
|
||||
{/* Active indicator */}
|
||||
{isActive && (
|
||||
|
|
@ -182,19 +200,13 @@ export default function Sidebar({ collapsed, setCollapsed }: SidebarProps) {
|
|||
)}
|
||||
|
||||
{/* Icon */}
|
||||
<span className={`
|
||||
flex-shrink-0 transition-colors duration-200
|
||||
${isActive ? "text-primary" : "text-gray-500 group-hover:text-gray-300"}
|
||||
`}>
|
||||
{routeIcons[route.path] || <LayoutDashboard className="w-5 h-5" />}
|
||||
<span className="flex-shrink-0 transition-colors duration-200">
|
||||
{routeIcons[route.path] || <LayoutDashboard className="w-6 h-6" />}
|
||||
</span>
|
||||
|
||||
{/* Label */}
|
||||
{!collapsed && (
|
||||
<span className={`
|
||||
font-mono text-sm tracking-wide
|
||||
${isActive ? 'font-semibold' : 'font-medium'}
|
||||
`}>
|
||||
<span className={`font-mono text-base tracking-wide ${isActive ? 'font-semibold' : 'font-medium'}`}>
|
||||
{route.name}
|
||||
</span>
|
||||
)}
|
||||
|
|
@ -212,26 +224,31 @@ export default function Sidebar({ collapsed, setCollapsed }: SidebarProps) {
|
|||
</nav>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="p-3 border-t border-gray-800/60 bg-[#0c0c12] space-y-1">
|
||||
<div
|
||||
className="p-3 space-y-1"
|
||||
style={{
|
||||
background: 'var(--cyber-bg-elevated)',
|
||||
borderTop: '1px solid var(--cyber-border)'
|
||||
}}
|
||||
>
|
||||
{/* Theme Toggle */}
|
||||
<ThemeToggle collapsed={collapsed} />
|
||||
|
||||
{/* Account Link */}
|
||||
<Link
|
||||
to="/account"
|
||||
className={`
|
||||
flex items-center gap-3 px-3 py-2.5 rounded-lg
|
||||
transition-all duration-200 group
|
||||
${location.pathname === '/account'
|
||||
? "bg-primary/15 text-primary border border-primary/30"
|
||||
: "text-gray-400 hover:text-gray-200 hover:bg-gray-800/50 border border-transparent"
|
||||
}
|
||||
`}
|
||||
className="flex items-center gap-3 px-3 py-2.5 rounded-lg transition-all duration-200 group"
|
||||
style={{
|
||||
background: location.pathname === '/account' ? 'hsl(var(--primary) / 0.15)' : 'transparent',
|
||||
border: location.pathname === '/account' ? '1px solid hsl(var(--primary) / 0.3)' : '1px solid transparent',
|
||||
color: location.pathname === '/account' ? 'hsl(var(--primary))' : 'var(--cyber-text-muted)'
|
||||
}}
|
||||
onClick={() => setMobileOpen(false)}
|
||||
title={collapsed ? "账号管理" : undefined}
|
||||
>
|
||||
<UserCircle className={`w-5 h-5 flex-shrink-0 ${
|
||||
location.pathname === '/account' ? 'text-primary' : 'text-gray-500 group-hover:text-gray-300'
|
||||
}`} />
|
||||
<UserCircle className="w-6 h-6 flex-shrink-0" />
|
||||
{!collapsed && (
|
||||
<span className="font-mono text-sm">账号管理</span>
|
||||
<span className="font-mono text-base">账号管理</span>
|
||||
)}
|
||||
</Link>
|
||||
|
||||
|
|
@ -240,29 +257,31 @@ export default function Sidebar({ collapsed, setCollapsed }: SidebarProps) {
|
|||
href="https://github.com/lintsinghua/DeepAudit"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={`
|
||||
flex items-center gap-3 px-3 py-2.5 rounded-lg
|
||||
text-gray-400 hover:text-gray-200 hover:bg-gray-800/50
|
||||
transition-all duration-200 group border border-transparent
|
||||
`}
|
||||
className="flex items-center gap-3 px-3 py-2.5 rounded-lg transition-all duration-200 group"
|
||||
style={{ color: 'var(--cyber-text-muted)' }}
|
||||
title={collapsed ? "GitHub" : undefined}
|
||||
>
|
||||
<Github className="w-5 h-5 flex-shrink-0 text-gray-500 group-hover:text-gray-300" />
|
||||
<Github className="w-6 h-6 flex-shrink-0" />
|
||||
{!collapsed && (
|
||||
<div className="flex flex-col">
|
||||
<span className="font-mono text-sm">GitHub</span>
|
||||
<span className="text-[10px] text-gray-600 font-mono">v{version}</span>
|
||||
<span className="font-mono text-base">GitHub</span>
|
||||
<span className="text-sm font-mono" style={{ color: 'var(--cyber-text-muted)' }}>v{version}</span>
|
||||
</div>
|
||||
)}
|
||||
</a>
|
||||
|
||||
{/* System Status */}
|
||||
{!collapsed && (
|
||||
<div className="mt-3 pt-3 border-t border-gray-800/50">
|
||||
<div className="mt-3 pt-3" style={{ borderTop: '1px solid var(--cyber-border)' }}>
|
||||
<div className="flex items-center gap-2 px-3 py-2">
|
||||
<div className="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"
|
||||
style={{ boxShadow: '0 0 8px rgba(52, 211, 153, 0.5)' }} />
|
||||
<span className="text-[10px] text-gray-600 font-mono uppercase tracking-wider">
|
||||
<div
|
||||
className="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"
|
||||
style={{ boxShadow: '0 0 8px rgba(52, 211, 153, 0.5)' }}
|
||||
/>
|
||||
<span
|
||||
className="text-sm font-mono uppercase tracking-wider"
|
||||
style={{ color: 'var(--cyber-text-muted)' }}
|
||||
>
|
||||
System Online
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -62,13 +62,13 @@ export default function ExportReportDialog({
|
|||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="sm:max-w-[600px] bg-[#0c0c12] border-gray-700">
|
||||
<DialogContent className="sm:max-w-[600px] cyber-dialog border-border">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-3 text-lg font-bold uppercase tracking-wider text-white">
|
||||
<DialogTitle className="flex items-center gap-3 text-lg font-bold uppercase tracking-wider text-foreground">
|
||||
<Download className="w-5 h-5 text-primary" />
|
||||
导出审计报告
|
||||
</DialogTitle>
|
||||
<DialogDescription className="text-gray-400 font-mono text-xs">
|
||||
<DialogDescription className="text-muted-foreground font-mono text-xs">
|
||||
选择报告格式并导出完整的代码审计结果
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
|
@ -79,57 +79,57 @@ export default function ExportReportDialog({
|
|||
onValueChange={(value) => setSelectedFormat(value as ExportFormat)}
|
||||
className="space-y-4"
|
||||
>
|
||||
<div className="flex items-center space-x-3 p-4 border border-gray-700 rounded bg-gray-900/30 cursor-pointer hover:bg-gray-800/50">
|
||||
<div className="flex items-center space-x-3 p-4 border border-border rounded bg-muted/50 cursor-pointer hover:bg-muted">
|
||||
<RadioGroupItem value="json" id="json" />
|
||||
<Label htmlFor="json" className="flex items-center gap-3 cursor-pointer flex-1">
|
||||
<FileJson className="w-5 h-5 text-amber-400" />
|
||||
<div>
|
||||
<div className="font-bold text-gray-200">JSON 格式</div>
|
||||
<div className="text-xs text-gray-500">结构化数据,适合程序处理和集成</div>
|
||||
<div className="font-bold text-foreground">JSON 格式</div>
|
||||
<div className="text-xs text-muted-foreground">结构化数据,适合程序处理和集成</div>
|
||||
</div>
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3 p-4 border border-gray-700 rounded bg-gray-900/30 cursor-pointer hover:bg-gray-800/50">
|
||||
<div className="flex items-center space-x-3 p-4 border border-border rounded bg-muted/50 cursor-pointer hover:bg-muted">
|
||||
<RadioGroupItem value="pdf" id="pdf" />
|
||||
<Label htmlFor="pdf" className="flex items-center gap-3 cursor-pointer flex-1">
|
||||
<FileText className="w-5 h-5 text-rose-400" />
|
||||
<div>
|
||||
<div className="font-bold text-gray-200">PDF 格式</div>
|
||||
<div className="text-xs text-gray-500">专业报告,适合打印和分享</div>
|
||||
<div className="font-bold text-foreground">PDF 格式</div>
|
||||
<div className="text-xs text-muted-foreground">专业报告,适合打印和分享</div>
|
||||
</div>
|
||||
</Label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
|
||||
{/* 报告预览信息 */}
|
||||
<div className="mt-6 border border-gray-700 rounded bg-gray-900/30">
|
||||
<div className="px-4 py-2 border-b border-gray-800 bg-gray-900/50 flex items-center gap-2">
|
||||
<div className="mt-6 border border-border rounded bg-muted/50">
|
||||
<div className="px-4 py-2 border-b border-border bg-muted flex items-center gap-2">
|
||||
<Terminal className="w-3 h-3 text-primary" />
|
||||
<h4 className="font-bold text-gray-300 uppercase text-xs">报告内容预览</h4>
|
||||
<h4 className="font-bold text-foreground uppercase text-xs">报告内容预览</h4>
|
||||
</div>
|
||||
<div className="p-4 grid grid-cols-2 gap-3 text-xs font-mono">
|
||||
<div className="flex items-center justify-between border-b border-gray-800 pb-2">
|
||||
<span className="text-gray-600">项目名称:</span>
|
||||
<span className="font-bold text-white">{task.project?.name || "未知"}</span>
|
||||
<div className="flex items-center justify-between border-b border-border pb-2">
|
||||
<span className="text-muted-foreground">项目名称:</span>
|
||||
<span className="font-bold text-foreground">{task.project?.name || "未知"}</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between border-b border-gray-800 pb-2">
|
||||
<span className="text-gray-600">质量评分:</span>
|
||||
<div className="flex items-center justify-between border-b border-border pb-2">
|
||||
<span className="text-muted-foreground">质量评分:</span>
|
||||
<span className="font-bold text-emerald-400">{task.quality_score.toFixed(1)}/100</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between border-b border-gray-800 pb-2">
|
||||
<span className="text-gray-600">扫描文件:</span>
|
||||
<span className="font-bold text-white">{task.scanned_files}/{task.total_files}</span>
|
||||
<div className="flex items-center justify-between border-b border-border pb-2">
|
||||
<span className="text-muted-foreground">扫描文件:</span>
|
||||
<span className="font-bold text-foreground">{task.scanned_files}/{task.total_files}</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between border-b border-gray-800 pb-2">
|
||||
<span className="text-gray-600">发现问题:</span>
|
||||
<div className="flex items-center justify-between border-b border-border pb-2">
|
||||
<span className="text-muted-foreground">发现问题:</span>
|
||||
<span className="font-bold text-amber-400">{issues.length}</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between border-b border-gray-800 pb-2">
|
||||
<span className="text-gray-600">代码行数:</span>
|
||||
<span className="font-bold text-white">{task.total_lines.toLocaleString()}</span>
|
||||
<div className="flex items-center justify-between border-b border-border pb-2">
|
||||
<span className="text-muted-foreground">代码行数:</span>
|
||||
<span className="font-bold text-foreground">{task.total_lines.toLocaleString()}</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between border-b border-gray-800 pb-2">
|
||||
<span className="text-gray-600">严重问题:</span>
|
||||
<div className="flex items-center justify-between border-b border-border pb-2">
|
||||
<span className="text-muted-foreground">严重问题:</span>
|
||||
<span className="font-bold text-rose-400">
|
||||
{issues.filter(i => i.severity === "critical").length}
|
||||
</span>
|
||||
|
|
@ -138,7 +138,7 @@ export default function ExportReportDialog({
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter className="border-t border-gray-800 pt-4">
|
||||
<DialogFooter className="border-t border-border pt-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => onOpenChange(false)}
|
||||
|
|
|
|||
|
|
@ -72,13 +72,13 @@ export default function InstantExportDialog({
|
|||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="sm:max-w-[600px] bg-[#0c0c12] border-gray-700">
|
||||
<DialogContent className="sm:max-w-[600px] cyber-dialog border-border">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-3 text-lg font-bold uppercase tracking-wider text-white">
|
||||
<DialogTitle className="flex items-center gap-3 text-lg font-bold uppercase tracking-wider text-foreground">
|
||||
<Download className="w-5 h-5 text-primary" />
|
||||
导出分析报告
|
||||
</DialogTitle>
|
||||
<DialogDescription className="text-gray-400 font-mono text-xs">
|
||||
<DialogDescription className="text-muted-foreground font-mono text-xs">
|
||||
选择报告格式并导出代码分析结果
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
|
@ -89,23 +89,23 @@ export default function InstantExportDialog({
|
|||
onValueChange={(value) => setSelectedFormat(value as ExportFormat)}
|
||||
className="space-y-4"
|
||||
>
|
||||
<div className="flex items-center space-x-3 p-4 border border-gray-700 rounded bg-gray-900/30 cursor-pointer hover:bg-gray-800/50">
|
||||
<div className="flex items-center space-x-3 p-4 border border-border rounded bg-muted/50 cursor-pointer hover:bg-muted">
|
||||
<RadioGroupItem value="json" id="json" />
|
||||
<Label htmlFor="json" className="flex items-center gap-3 cursor-pointer flex-1">
|
||||
<FileJson className="w-5 h-5 text-amber-400" />
|
||||
<div>
|
||||
<div className="font-bold text-gray-200">JSON 格式</div>
|
||||
<div className="text-xs text-gray-500">结构化数据,适合程序处理和集成</div>
|
||||
<div className="font-bold text-foreground">JSON 格式</div>
|
||||
<div className="text-xs text-muted-foreground">结构化数据,适合程序处理和集成</div>
|
||||
</div>
|
||||
</Label>
|
||||
</div>
|
||||
<div className={`flex items-center space-x-3 p-4 border border-gray-700 rounded bg-gray-900/30 ${isPdfDisabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer hover:bg-gray-800/50'}`}>
|
||||
<div className={`flex items-center space-x-3 p-4 border border-border rounded bg-muted/50 ${isPdfDisabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer hover:bg-muted'}`}>
|
||||
<RadioGroupItem value="pdf" id="pdf" disabled={isPdfDisabled} />
|
||||
<Label htmlFor="pdf" className={`flex items-center gap-3 flex-1 ${isPdfDisabled ? 'cursor-not-allowed' : 'cursor-pointer'}`}>
|
||||
<FileText className="w-5 h-5 text-rose-400" />
|
||||
<div>
|
||||
<div className="font-bold text-gray-200">PDF 格式</div>
|
||||
<div className="text-xs text-gray-500">
|
||||
<div className="font-bold text-foreground">PDF 格式</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{isPdfDisabled && <AlertTriangle className="w-3 h-3 inline mr-1 text-amber-500" />}
|
||||
{isPdfDisabled ? "需要先保存到历史记录" : "专业报告,适合打印和分享"}
|
||||
</div>
|
||||
|
|
@ -115,33 +115,33 @@ export default function InstantExportDialog({
|
|||
</RadioGroup>
|
||||
|
||||
{/* 报告预览信息 */}
|
||||
<div className="mt-6 border border-gray-700 rounded bg-gray-900/30">
|
||||
<div className="px-4 py-2 border-b border-gray-800 bg-gray-900/50 flex items-center gap-2">
|
||||
<div className="mt-6 border border-border rounded bg-muted/50">
|
||||
<div className="px-4 py-2 border-b border-border bg-muted flex items-center gap-2">
|
||||
<Terminal className="w-3 h-3 text-primary" />
|
||||
<h4 className="font-bold text-gray-300 uppercase text-xs">报告内容预览</h4>
|
||||
<h4 className="font-bold text-foreground uppercase text-xs">报告内容预览</h4>
|
||||
</div>
|
||||
<div className="p-4 grid grid-cols-2 gap-3 text-xs font-mono">
|
||||
<div className="flex items-center justify-between border-b border-gray-800 pb-2">
|
||||
<span className="text-gray-600">编程语言:</span>
|
||||
<div className="flex items-center justify-between border-b border-border pb-2">
|
||||
<span className="text-muted-foreground">编程语言:</span>
|
||||
<span className="font-bold text-sky-400">{language.toUpperCase()}</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between border-b border-gray-800 pb-2">
|
||||
<span className="text-gray-600">质量评分:</span>
|
||||
<div className="flex items-center justify-between border-b border-border pb-2">
|
||||
<span className="text-muted-foreground">质量评分:</span>
|
||||
<span className="font-bold text-emerald-400">{(analysisResult.quality_score ?? 0).toFixed(1)}/100</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between border-b border-gray-800 pb-2">
|
||||
<span className="text-gray-600">发现问题:</span>
|
||||
<div className="flex items-center justify-between border-b border-border pb-2">
|
||||
<span className="text-muted-foreground">发现问题:</span>
|
||||
<span className="font-bold text-amber-400">{analysisResult.issues?.length ?? 0}</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between border-b border-gray-800 pb-2">
|
||||
<span className="text-gray-600">分析耗时:</span>
|
||||
<span className="font-bold text-white">{(analysisTime ?? 0).toFixed(2)}s</span>
|
||||
<div className="flex items-center justify-between border-b border-border pb-2">
|
||||
<span className="text-muted-foreground">分析耗时:</span>
|
||||
<span className="font-bold text-foreground">{(analysisTime ?? 0).toFixed(2)}s</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter className="border-t border-gray-800 pt-4">
|
||||
<DialogFooter className="border-t border-border pt-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => onOpenChange(false)}
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ export function SystemConfig() {
|
|||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<div className="text-center space-y-4">
|
||||
<div className="loading-spinner mx-auto" />
|
||||
<p className="text-gray-500 font-mono text-sm uppercase tracking-wider">加载配置中...</p>
|
||||
<p className="text-muted-foreground font-mono text-sm uppercase tracking-wider">加载配置中...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -250,17 +250,17 @@ export function SystemConfig() {
|
|||
</div>
|
||||
|
||||
<Tabs defaultValue="llm" className="w-full">
|
||||
<TabsList className="grid w-full grid-cols-4 bg-gray-900/50 border border-gray-800 p-1 h-auto gap-1 rounded-lg mb-6">
|
||||
<TabsTrigger value="llm" className="data-[state=active]:bg-primary data-[state=active]:text-white font-mono font-bold uppercase py-2.5 text-gray-400 transition-all rounded text-xs flex items-center gap-2">
|
||||
<TabsList className="grid w-full grid-cols-4 bg-muted border border-border p-1 h-auto gap-1 rounded-lg mb-6">
|
||||
<TabsTrigger value="llm" className="data-[state=active]:bg-primary data-[state=active]:text-foreground font-mono font-bold uppercase py-2.5 text-muted-foreground transition-all rounded text-xs flex items-center gap-2">
|
||||
<Zap className="w-3 h-3" /> LLM 配置
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="embedding" className="data-[state=active]:bg-primary data-[state=active]:text-white font-mono font-bold uppercase py-2.5 text-gray-400 transition-all rounded text-xs flex items-center gap-2">
|
||||
<TabsTrigger value="embedding" className="data-[state=active]:bg-primary data-[state=active]:text-foreground font-mono font-bold uppercase py-2.5 text-muted-foreground transition-all rounded text-xs flex items-center gap-2">
|
||||
<Brain className="w-3 h-3" /> 嵌入模型
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="analysis" className="data-[state=active]:bg-primary data-[state=active]:text-white font-mono font-bold uppercase py-2.5 text-gray-400 transition-all rounded text-xs flex items-center gap-2">
|
||||
<TabsTrigger value="analysis" className="data-[state=active]:bg-primary data-[state=active]:text-foreground font-mono font-bold uppercase py-2.5 text-muted-foreground transition-all rounded text-xs flex items-center gap-2">
|
||||
<Settings className="w-3 h-3" /> 分析参数
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="git" className="data-[state=active]:bg-primary data-[state=active]:text-white font-mono font-bold uppercase py-2.5 text-gray-400 transition-all rounded text-xs flex items-center gap-2">
|
||||
<TabsTrigger value="git" className="data-[state=active]:bg-primary data-[state=active]:text-foreground font-mono font-bold uppercase py-2.5 text-muted-foreground transition-all rounded text-xs flex items-center gap-2">
|
||||
<Globe className="w-3 h-3" /> Git 集成
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
|
@ -270,29 +270,29 @@ export function SystemConfig() {
|
|||
<div className="cyber-card p-6 space-y-6">
|
||||
{/* Provider Selection */}
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">选择 LLM 提供商</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">选择 LLM 提供商</Label>
|
||||
<Select value={config.llmProvider} onValueChange={(v) => updateConfig('llmProvider', v)}>
|
||||
<SelectTrigger className="h-12 cyber-input">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
||||
<div className="px-2 py-1.5 text-xs font-bold text-gray-500 uppercase">LiteLLM 统一适配 (推荐)</div>
|
||||
<SelectContent className="cyber-dialog border-border">
|
||||
<div className="px-2 py-1.5 text-xs font-bold text-muted-foreground uppercase">LiteLLM 统一适配 (推荐)</div>
|
||||
{LLM_PROVIDERS.filter(p => p.category === 'litellm').map(p => (
|
||||
<SelectItem key={p.value} value={p.value} className="font-mono">
|
||||
<span className="flex items-center gap-2">
|
||||
<span>{p.icon}</span>
|
||||
<span>{p.label}</span>
|
||||
<span className="text-xs text-gray-500">- {p.hint}</span>
|
||||
<span className="text-xs text-muted-foreground">- {p.hint}</span>
|
||||
</span>
|
||||
</SelectItem>
|
||||
))}
|
||||
<div className="px-2 py-1.5 text-xs font-bold text-gray-500 uppercase mt-2">原生适配器</div>
|
||||
<div className="px-2 py-1.5 text-xs font-bold text-muted-foreground uppercase mt-2">原生适配器</div>
|
||||
{LLM_PROVIDERS.filter(p => p.category === 'native').map(p => (
|
||||
<SelectItem key={p.value} value={p.value} className="font-mono">
|
||||
<span className="flex items-center gap-2">
|
||||
<span>{p.icon}</span>
|
||||
<span>{p.label}</span>
|
||||
<span className="text-xs text-gray-500">- {p.hint}</span>
|
||||
<span className="text-xs text-muted-foreground">- {p.hint}</span>
|
||||
</span>
|
||||
</SelectItem>
|
||||
))}
|
||||
|
|
@ -303,7 +303,7 @@ export function SystemConfig() {
|
|||
{/* API Key */}
|
||||
{config.llmProvider !== 'ollama' && (
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">API Key</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">API Key</Label>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
type={showApiKey ? 'text' : 'password'}
|
||||
|
|
@ -327,7 +327,7 @@ export function SystemConfig() {
|
|||
{/* Model and Base URL */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">模型名称 (可选)</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">模型名称 (可选)</Label>
|
||||
<Input
|
||||
value={config.llmModel}
|
||||
onChange={(e) => updateConfig('llmModel', e.target.value)}
|
||||
|
|
@ -336,7 +336,7 @@ export function SystemConfig() {
|
|||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">API Base URL (可选)</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">API Base URL (可选)</Label>
|
||||
<Input
|
||||
value={config.llmBaseUrl}
|
||||
onChange={(e) => updateConfig('llmBaseUrl', e.target.value)}
|
||||
|
|
@ -347,10 +347,10 @@ export function SystemConfig() {
|
|||
</div>
|
||||
|
||||
{/* Test Connection */}
|
||||
<div className="pt-4 border-t border-gray-800 border-dashed flex items-center justify-between flex-wrap gap-4">
|
||||
<div className="pt-4 border-t border-border border-dashed flex items-center justify-between flex-wrap gap-4">
|
||||
<div className="text-sm">
|
||||
<span className="font-bold text-gray-300">测试连接</span>
|
||||
<span className="text-gray-500 ml-2">验证配置是否正确</span>
|
||||
<span className="font-bold text-foreground">测试连接</span>
|
||||
<span className="text-muted-foreground ml-2">验证配置是否正确</span>
|
||||
</div>
|
||||
<Button
|
||||
onClick={testLLMConnection}
|
||||
|
|
@ -386,11 +386,11 @@ export function SystemConfig() {
|
|||
)}
|
||||
|
||||
{/* Advanced Parameters */}
|
||||
<details className="pt-4 border-t border-gray-800 border-dashed">
|
||||
<summary className="font-bold uppercase cursor-pointer hover:text-primary text-gray-400 text-sm">高级参数</summary>
|
||||
<details className="pt-4 border-t border-border border-dashed">
|
||||
<summary className="font-bold uppercase cursor-pointer hover:text-primary text-muted-foreground text-sm">高级参数</summary>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mt-4">
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs text-gray-500 uppercase">超时 (毫秒)</Label>
|
||||
<Label className="text-xs text-muted-foreground uppercase">超时 (毫秒)</Label>
|
||||
<Input
|
||||
type="number"
|
||||
value={config.llmTimeout}
|
||||
|
|
@ -399,7 +399,7 @@ export function SystemConfig() {
|
|||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs text-gray-500 uppercase">温度 (0-2)</Label>
|
||||
<Label className="text-xs text-muted-foreground uppercase">温度 (0-2)</Label>
|
||||
<Input
|
||||
type="number"
|
||||
step="0.1"
|
||||
|
|
@ -411,7 +411,7 @@ export function SystemConfig() {
|
|||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs text-gray-500 uppercase">最大 Tokens</Label>
|
||||
<Label className="text-xs text-muted-foreground uppercase">最大 Tokens</Label>
|
||||
<Input
|
||||
type="number"
|
||||
value={config.llmMaxTokens}
|
||||
|
|
@ -424,14 +424,14 @@ export function SystemConfig() {
|
|||
</div>
|
||||
|
||||
{/* Usage Notes */}
|
||||
<div className="bg-gray-900/50 border border-gray-800 p-4 rounded-lg text-xs space-y-2">
|
||||
<p className="font-bold uppercase text-gray-400 flex items-center gap-2">
|
||||
<div className="bg-muted border border-border p-4 rounded-lg text-xs space-y-2">
|
||||
<p className="font-bold uppercase text-muted-foreground flex items-center gap-2">
|
||||
<Info className="w-4 h-4 text-sky-400" />
|
||||
配置说明
|
||||
</p>
|
||||
<p className="text-gray-500">• <strong className="text-gray-400">LiteLLM 统一适配</strong>: 大多数提供商通过 LiteLLM 统一处理,支持自动重试和负载均衡</p>
|
||||
<p className="text-gray-500">• <strong className="text-gray-400">原生适配器</strong>: 百度、MiniMax、豆包因 API 格式特殊,使用专用适配器</p>
|
||||
<p className="text-gray-500">• <strong className="text-gray-400">API 中转站</strong>: 在 Base URL 填入中转站地址即可,API Key 填中转站提供的 Key</p>
|
||||
<p className="text-muted-foreground">• <strong className="text-muted-foreground">LiteLLM 统一适配</strong>: 大多数提供商通过 LiteLLM 统一处理,支持自动重试和负载均衡</p>
|
||||
<p className="text-muted-foreground">• <strong className="text-muted-foreground">原生适配器</strong>: 百度、MiniMax、豆包因 API 格式特殊,使用专用适配器</p>
|
||||
<p className="text-muted-foreground">• <strong className="text-muted-foreground">API 中转站</strong>: 在 Base URL 填入中转站地址即可,API Key 填中转站提供的 Key</p>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
|
|
@ -445,47 +445,47 @@ export function SystemConfig() {
|
|||
<div className="cyber-card p-6 space-y-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">最大分析文件数</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">最大分析文件数</Label>
|
||||
<Input
|
||||
type="number"
|
||||
value={config.maxAnalyzeFiles}
|
||||
onChange={(e) => updateConfig('maxAnalyzeFiles', Number(e.target.value))}
|
||||
className="h-10 cyber-input"
|
||||
/>
|
||||
<p className="text-xs text-gray-600">单次任务最多处理的文件数量</p>
|
||||
<p className="text-xs text-muted-foreground">单次任务最多处理的文件数量</p>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">LLM 并发数</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">LLM 并发数</Label>
|
||||
<Input
|
||||
type="number"
|
||||
value={config.llmConcurrency}
|
||||
onChange={(e) => updateConfig('llmConcurrency', Number(e.target.value))}
|
||||
className="h-10 cyber-input"
|
||||
/>
|
||||
<p className="text-xs text-gray-600">同时发送的 LLM 请求数量</p>
|
||||
<p className="text-xs text-muted-foreground">同时发送的 LLM 请求数量</p>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">请求间隔 (毫秒)</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">请求间隔 (毫秒)</Label>
|
||||
<Input
|
||||
type="number"
|
||||
value={config.llmGapMs}
|
||||
onChange={(e) => updateConfig('llmGapMs', Number(e.target.value))}
|
||||
className="h-10 cyber-input"
|
||||
/>
|
||||
<p className="text-xs text-gray-600">每个请求之间的延迟时间</p>
|
||||
<p className="text-xs text-muted-foreground">每个请求之间的延迟时间</p>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">输出语言</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">输出语言</Label>
|
||||
<Select value={config.outputLanguage} onValueChange={(v) => updateConfig('outputLanguage', v)}>
|
||||
<SelectTrigger className="h-10 cyber-input">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
||||
<SelectContent className="cyber-dialog border-border">
|
||||
<SelectItem value="zh-CN" className="font-mono">🇨🇳 中文</SelectItem>
|
||||
<SelectItem value="en-US" className="font-mono">🇺🇸 English</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="text-xs text-gray-600">代码审查结果的输出语言</p>
|
||||
<p className="text-xs text-muted-foreground">代码审查结果的输出语言</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -495,7 +495,7 @@ export function SystemConfig() {
|
|||
<TabsContent value="git" className="space-y-6">
|
||||
<div className="cyber-card p-6 space-y-6">
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">GitHub Token (可选)</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">GitHub Token (可选)</Label>
|
||||
<Input
|
||||
type="password"
|
||||
value={config.githubToken}
|
||||
|
|
@ -503,7 +503,7 @@ export function SystemConfig() {
|
|||
placeholder="ghp_xxxxxxxxxxxx"
|
||||
className="h-10 cyber-input"
|
||||
/>
|
||||
<p className="text-xs text-gray-600">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
用于访问私有仓库。获取:{' '}
|
||||
<a href="https://github.com/settings/tokens" target="_blank" rel="noopener noreferrer" className="text-primary hover:underline">
|
||||
github.com/settings/tokens
|
||||
|
|
@ -511,7 +511,7 @@ export function SystemConfig() {
|
|||
</p>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">GitLab Token (可选)</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">GitLab Token (可选)</Label>
|
||||
<Input
|
||||
type="password"
|
||||
value={config.gitlabToken}
|
||||
|
|
@ -519,20 +519,20 @@ export function SystemConfig() {
|
|||
placeholder="glpat-xxxxxxxxxxxx"
|
||||
className="h-10 cyber-input"
|
||||
/>
|
||||
<p className="text-xs text-gray-600">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
用于访问私有仓库。获取:{' '}
|
||||
<a href="https://gitlab.com/-/profile/personal_access_tokens" target="_blank" rel="noopener noreferrer" className="text-primary hover:underline">
|
||||
gitlab.com/-/profile/personal_access_tokens
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<div className="bg-gray-900/50 border border-gray-800 p-4 rounded-lg text-xs">
|
||||
<p className="font-bold text-gray-400 flex items-center gap-2 mb-2">
|
||||
<div className="bg-muted border border-border p-4 rounded-lg text-xs">
|
||||
<p className="font-bold text-muted-foreground flex items-center gap-2 mb-2">
|
||||
<Info className="w-4 h-4 text-sky-400" />
|
||||
提示
|
||||
</p>
|
||||
<p className="text-gray-500">• 公开仓库无需配置 Token</p>
|
||||
<p className="text-gray-500">• 私有仓库需要配置对应平台的 Token</p>
|
||||
<p className="text-muted-foreground">• 公开仓库无需配置 Token</p>
|
||||
<p className="text-muted-foreground">• 私有仓库需要配置对应平台的 Token</p>
|
||||
</div>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
|
|
|||
|
|
@ -33,13 +33,13 @@ function AccordionTrigger({
|
|||
<AccordionPrimitive.Trigger
|
||||
data-slot="accordion-trigger"
|
||||
className={cn(
|
||||
"focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180",
|
||||
"focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-sm py-4 text-left text-base font-semibold transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronDownIcon className="text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200" />
|
||||
<ChevronDownIcon className="text-muted-foreground pointer-events-none size-5 shrink-0 translate-y-0.5 transition-transform duration-200" />
|
||||
</AccordionPrimitive.Trigger>
|
||||
</AccordionPrimitive.Header>
|
||||
);
|
||||
|
|
@ -53,7 +53,7 @@ function AccordionContent({
|
|||
return (
|
||||
<AccordionPrimitive.Content
|
||||
data-slot="accordion-content"
|
||||
className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm"
|
||||
className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-base"
|
||||
{...props}
|
||||
>
|
||||
<div className={cn("pt-0 pb-4", className)}>{children}</div>
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ function AlertDialogContent({
|
|||
<AlertDialogPrimitive.Content
|
||||
data-slot="alert-dialog-content"
|
||||
className={cn(
|
||||
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
|
||||
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-6 rounded-sm border border-border p-0 shadow-lg duration-200 sm:max-w-lg overflow-hidden",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
@ -68,7 +68,7 @@ function AlertDialogHeader({
|
|||
return (
|
||||
<div
|
||||
data-slot="alert-dialog-header"
|
||||
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
|
||||
className={cn("flex flex-col gap-2 px-6 pt-6 text-center sm:text-left", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
|
@ -82,7 +82,7 @@ function AlertDialogFooter({
|
|||
<div
|
||||
data-slot="alert-dialog-footer"
|
||||
className={cn(
|
||||
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
||||
"flex flex-col-reverse gap-3 sm:flex-row sm:justify-end px-6 pb-6 pt-4 border-t border-border bg-muted/30",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
@ -97,7 +97,7 @@ function AlertDialogTitle({
|
|||
return (
|
||||
<AlertDialogPrimitive.Title
|
||||
data-slot="alert-dialog-title"
|
||||
className={cn("text-lg font-semibold", className)}
|
||||
className={cn("text-xl font-mono font-bold uppercase tracking-wider text-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
|
@ -110,7 +110,7 @@ function AlertDialogDescription({
|
|||
return (
|
||||
<AlertDialogPrimitive.Description
|
||||
data-slot="alert-dialog-description"
|
||||
className={cn("text-muted-foreground text-sm", className)}
|
||||
className={cn("text-foreground/70 text-base font-mono", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { cva, type VariantProps } from "class-variance-authority"
|
|||
import { cn } from "@/shared/utils/utils"
|
||||
|
||||
const alertVariants = cva(
|
||||
"relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
|
||||
"relative w-full rounded-sm border px-5 py-4 text-base grid has-[>svg]:grid-cols-[calc(var(--spacing)*5)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-4 gap-y-1 items-start [&>svg]:size-5 [&>svg]:translate-y-0.5 [&>svg]:text-current",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
|
@ -39,7 +39,7 @@ function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|||
<div
|
||||
data-slot="alert-title"
|
||||
className={cn(
|
||||
"col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight",
|
||||
"col-start-2 line-clamp-1 min-h-5 font-semibold tracking-tight text-lg",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
@ -55,7 +55,7 @@ function AlertDescription({
|
|||
<div
|
||||
data-slot="alert-description"
|
||||
className={cn(
|
||||
"text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed",
|
||||
"text-foreground/70 col-start-2 grid justify-items-start gap-1 text-base [&_p]:leading-relaxed",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority";
|
|||
import { cn } from "@/shared/utils/utils";
|
||||
|
||||
const badgeVariants = cva(
|
||||
"inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
|
||||
"inline-flex items-center justify-center rounded-sm border px-3 py-1.5 text-sm font-mono font-semibold uppercase tracking-wider w-fit whitespace-nowrap shrink-0 [&>svg]:size-4 gap-1.5 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
|
@ -14,7 +14,7 @@ const badgeVariants = cva(
|
|||
secondary:
|
||||
"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
|
||||
destructive:
|
||||
"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||
"border-transparent bg-destructive text-foreground [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||
outline:
|
||||
"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority";
|
|||
import { cn } from "@/shared/utils/utils";
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-sm text-sm font-mono font-semibold transition-all duration-150 focus-visible:outline-none focus-visible:shadow-focus disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-sm text-base font-mono font-semibold transition-all duration-150 focus-visible:outline-none focus-visible:shadow-focus disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-5 [&_svg]:shrink-0",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
|
@ -21,10 +21,10 @@ const buttonVariants = cva(
|
|||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
default: "h-9 px-4 py-2",
|
||||
sm: "h-8 rounded-sm px-3 text-xs",
|
||||
lg: "h-10 rounded-sm px-8",
|
||||
icon: "h-9 w-9",
|
||||
default: "h-11 px-5 py-2.5",
|
||||
sm: "h-9 rounded-sm px-4 text-sm",
|
||||
lg: "h-13 rounded-sm px-8 text-lg",
|
||||
icon: "h-11 w-11",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ function Card({ className, ...props }: React.ComponentProps<"div">) {
|
|||
<div
|
||||
data-slot="card"
|
||||
className={cn(
|
||||
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6",
|
||||
"bg-card text-card-foreground flex flex-col gap-4 rounded-sm border border-border p-5 shadow-sm transition-all duration-200 hover:shadow-md relative overflow-hidden",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
@ -20,7 +20,7 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|||
<div
|
||||
data-slot="card-header"
|
||||
className={cn(
|
||||
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
|
||||
"flex flex-col gap-2 pb-4 border-b border-border",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
@ -32,7 +32,7 @@ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|||
return (
|
||||
<div
|
||||
data-slot="card-title"
|
||||
className={cn("leading-none font-semibold", className)}
|
||||
className={cn("text-lg font-mono font-bold uppercase tracking-wider text-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
|
@ -42,7 +42,7 @@ function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
|
|||
return (
|
||||
<div
|
||||
data-slot="card-description"
|
||||
className={cn("text-muted-foreground text-sm", className)}
|
||||
className={cn("text-base text-foreground/70 font-mono", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
|
@ -65,7 +65,7 @@ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
|
|||
return (
|
||||
<div
|
||||
data-slot="card-content"
|
||||
className={cn("px-6", className)}
|
||||
className={cn("py-2", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
|
@ -75,7 +75,7 @@ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|||
return (
|
||||
<div
|
||||
data-slot="card-footer"
|
||||
className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
|
||||
className={cn("flex items-center gap-3 pt-4 border-t border-border", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ function Checkbox({
|
|||
<CheckboxPrimitive.Root
|
||||
data-slot="checkbox"
|
||||
className={cn(
|
||||
"peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"peer border-border bg-background data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=checked]:border-primary focus-visible:border-primary focus-visible:shadow-focus aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-6 shrink-0 rounded-sm border shadow-none transition-all outline-none disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
@ -21,7 +21,7 @@ function Checkbox({
|
|||
data-slot="checkbox-indicator"
|
||||
className="flex items-center justify-center text-current transition-none"
|
||||
>
|
||||
<CheckIcon className="size-3.5" />
|
||||
<CheckIcon className="size-4" />
|
||||
</CheckboxPrimitive.Indicator>
|
||||
</CheckboxPrimitive.Root>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ function Command({
|
|||
<CommandPrimitive
|
||||
data-slot="command"
|
||||
className={cn(
|
||||
"bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md",
|
||||
"bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-sm",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
@ -64,7 +64,7 @@ function CommandInput({
|
|||
<CommandPrimitive.Input
|
||||
data-slot="command-input"
|
||||
className={cn(
|
||||
"placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"placeholder:text-muted-foreground flex h-10 w-full rounded-sm bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ const DialogContent = React.forwardRef<
|
|||
<DialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-6 border border-border bg-background p-0 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg overflow-hidden",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
@ -59,7 +59,7 @@ const DialogHeader = ({
|
|||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col space-y-1.5 text-center sm:text-left",
|
||||
"flex flex-col space-y-2 px-6 pt-6 text-center sm:text-left",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
@ -73,7 +73,7 @@ const DialogFooter = ({
|
|||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
||||
"flex flex-col-reverse sm:flex-row sm:justify-end gap-3 px-6 pb-6 pt-4 border-t border-border bg-muted/30",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
@ -88,7 +88,7 @@ const DialogTitle = React.forwardRef<
|
|||
<DialogPrimitive.Title
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"text-lg font-semibold leading-none tracking-tight",
|
||||
"text-xl font-mono font-bold uppercase tracking-wider text-foreground",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
@ -102,7 +102,7 @@ const DialogDescription = React.forwardRef<
|
|||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Description
|
||||
ref={ref}
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
className={cn("text-base text-foreground/70 font-mono", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ const DropdownMenuSubTrigger = React.forwardRef<
|
|||
<DropdownMenuPrimitive.SubTrigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
||||
"flex cursor-default select-none items-center gap-2 rounded-sm px-3 py-2 text-base outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-5 [&_svg]:shrink-0",
|
||||
inset && "pl-8",
|
||||
className
|
||||
)}
|
||||
|
|
@ -65,7 +65,7 @@ const DropdownMenuContent = React.forwardRef<
|
|||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
|
||||
"z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-sm border border-border bg-popover p-1 text-popover-foreground shadow-lg font-mono",
|
||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-dropdown-menu-content-transform-origin]",
|
||||
className
|
||||
)}
|
||||
|
|
@ -84,7 +84,7 @@ const DropdownMenuItem = React.forwardRef<
|
|||
<DropdownMenuPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
|
||||
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-4 py-2.5 text-base outline-none transition-colors focus:bg-muted focus:text-foreground hover:bg-muted data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-5 [&>svg]:shrink-0",
|
||||
inset && "pl-8",
|
||||
className
|
||||
)}
|
||||
|
|
@ -148,7 +148,7 @@ const DropdownMenuLabel = React.forwardRef<
|
|||
<DropdownMenuPrimitive.Label
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"px-2 py-1.5 text-sm font-semibold",
|
||||
"px-3 py-2 text-base font-semibold",
|
||||
inset && "pl-8",
|
||||
className
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
|||
type={type}
|
||||
data-slot="input"
|
||||
className={cn(
|
||||
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground font-mono flex h-9 w-full min-w-0 rounded-sm border border-input bg-background px-3 py-2 text-sm shadow-none transition-[border-color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground font-mono font-medium flex h-11 w-full min-w-0 rounded-sm border border-input bg-background px-4 py-2.5 text-base shadow-none transition-[border-color,box-shadow] outline-none file:inline-flex file:h-9 file:border-0 file:bg-transparent file:text-base file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"focus:border-primary focus:shadow-focus",
|
||||
"aria-invalid:border-secondary",
|
||||
className
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ function Label({
|
|||
<LabelPrimitive.Root
|
||||
data-slot="label"
|
||||
className={cn(
|
||||
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
||||
"flex items-center gap-2 text-base leading-none font-mono font-semibold text-foreground/80 uppercase tracking-wider select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ function Menubar({
|
|||
<MenubarPrimitive.Root
|
||||
data-slot="menubar"
|
||||
className={cn(
|
||||
"bg-background flex h-9 items-center gap-1 rounded-md border p-1 shadow-xs",
|
||||
"bg-background flex h-9 items-center gap-1 rounded-sm border p-1 shadow-xs",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
@ -77,7 +77,7 @@ function MenubarContent({
|
|||
alignOffset={alignOffset}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[12rem] origin-(--radix-menubar-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-md",
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[12rem] origin-(--radix-menubar-content-transform-origin) overflow-hidden rounded-sm border p-1 shadow-md",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
@ -246,7 +246,7 @@ function MenubarSubContent({
|
|||
<MenubarPrimitive.SubContent
|
||||
data-slot="menubar-sub-content"
|
||||
className={cn(
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-menubar-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-menubar-content-transform-origin) overflow-hidden rounded-sm border p-1 shadow-lg",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -89,13 +89,13 @@ const MultiSelect: React.FC<MultiSelectProps> = ({
|
|||
<div className="relative z-20 inline-block w-full" ref={containerRef}>
|
||||
<div className="relative flex flex-col items-center">
|
||||
<div onClick={toggleDropdown} className="w-full">
|
||||
<div className="mb-2 flex h-11 rounded-lg border border-gray-300 py-1.5 pl-3 pr-3 shadow-theme-xs outline-hidden transition focus:border-brand-300 focus:shadow-focus-ring dark:border-gray-700 dark:bg-gray-900 dark:focus:border-brand-300">
|
||||
<div className="mb-2 flex h-11 rounded-sm border border-border py-1.5 pl-3 pr-3 shadow-theme-xs outline-hidden transition focus:border-brand-300 focus:shadow-focus-ring dark:border-border dark:bg-card dark:focus:border-brand-300">
|
||||
<div className="flex flex-wrap flex-auto gap-2">
|
||||
{selectedValuesText.length > 0 ? (
|
||||
selectedValuesText.map((text, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="group flex items-center justify-center rounded-full border-[0.7px] border-transparent bg-gray-100 py-1 pl-2.5 pr-2 text-sm text-gray-800 hover:border-gray-200 dark:bg-gray-800 dark:text-white/90 dark:hover:border-gray-800"
|
||||
className="group flex items-center justify-center rounded-full border-[0.7px] border-transparent bg-muted py-1 pl-2.5 pr-2 text-sm text-foreground hover:border-border dark:bg-muted dark:text-foreground/90 dark:hover:border-border"
|
||||
>
|
||||
<span className="flex-initial max-w-full">{text}</span>
|
||||
<div className="flex flex-row-reverse flex-auto">
|
||||
|
|
@ -104,7 +104,7 @@ const MultiSelect: React.FC<MultiSelectProps> = ({
|
|||
e.stopPropagation();
|
||||
removeOption(selectedOptions[index]);
|
||||
}}
|
||||
className="pl-2 text-gray-500 cursor-pointer group-hover:text-gray-400 dark:text-gray-400"
|
||||
className="pl-2 text-muted-foreground cursor-pointer group-hover:text-muted-foreground dark:text-muted-foreground"
|
||||
>
|
||||
<svg
|
||||
className="fill-current"
|
||||
|
|
@ -127,7 +127,7 @@ const MultiSelect: React.FC<MultiSelectProps> = ({
|
|||
) : (
|
||||
<input
|
||||
placeholder="请选择选项..."
|
||||
className="w-full h-full p-1 pr-2 text-sm bg-transparent border-0 outline-hidden appearance-none placeholder:text-gray-800 focus:border-0 focus:outline-hidden focus:ring-0 dark:placeholder:text-white/90"
|
||||
className="w-full h-full p-1 pr-2 text-sm bg-transparent border-0 outline-hidden appearance-none placeholder:text-foreground focus:border-0 focus:outline-hidden focus:ring-0 dark:placeholder:text-foreground/90"
|
||||
readOnly
|
||||
value="请选择选项..."
|
||||
/>
|
||||
|
|
@ -137,7 +137,7 @@ const MultiSelect: React.FC<MultiSelectProps> = ({
|
|||
<button
|
||||
type="button"
|
||||
onClick={toggleDropdown}
|
||||
className="w-5 h-5 text-gray-700 outline-hidden cursor-pointer focus:outline-hidden dark:text-gray-400"
|
||||
className="w-5 h-5 text-muted-foreground outline-hidden cursor-pointer focus:outline-hidden dark:text-muted-foreground"
|
||||
>
|
||||
<svg
|
||||
className={`stroke-current ${isOpen ? "rotate-180" : ""}`}
|
||||
|
|
@ -162,14 +162,14 @@ const MultiSelect: React.FC<MultiSelectProps> = ({
|
|||
|
||||
{isOpen && (
|
||||
<div
|
||||
className="absolute left-0 z-40 w-full overflow-y-auto bg-white rounded-lg shadow-sm top-full max-h-select dark:bg-gray-900"
|
||||
className="absolute left-0 z-40 w-full overflow-y-auto bg-background rounded-sm shadow-sm top-full max-h-select dark:bg-card"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="flex flex-col">
|
||||
{options.map((option, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`hover:bg-primary/5 w-full cursor-pointer rounded-t border-b border-gray-200 dark:border-gray-800`}
|
||||
className={`hover:bg-primary/5 w-full cursor-pointer rounded-t border-b border-border dark:border-border`}
|
||||
onClick={() => handleSelect(option.value)}
|
||||
>
|
||||
<div
|
||||
|
|
@ -179,7 +179,7 @@ const MultiSelect: React.FC<MultiSelectProps> = ({
|
|||
: ""
|
||||
}`}
|
||||
>
|
||||
<div className="mx-2 leading-6 text-gray-800 dark:text-white/90">
|
||||
<div className="mx-2 leading-6 text-foreground dark:text-foreground/90">
|
||||
{option.label}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ function NavigationMenuItem({
|
|||
}
|
||||
|
||||
const navigationMenuTriggerStyle = cva(
|
||||
"group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=open]:hover:bg-accent data-[state=open]:text-accent-foreground data-[state=open]:focus:bg-accent data-[state=open]:bg-accent/50 focus-visible:ring-ring/50 outline-none transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1"
|
||||
"group inline-flex h-9 w-max items-center justify-center rounded-sm bg-background px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=open]:hover:bg-accent data-[state=open]:text-accent-foreground data-[state=open]:focus:bg-accent data-[state=open]:bg-accent/50 focus-visible:ring-ring/50 outline-none transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1"
|
||||
);
|
||||
|
||||
function NavigationMenuTrigger({
|
||||
|
|
@ -91,7 +91,7 @@ function NavigationMenuContent({
|
|||
data-slot="navigation-menu-content"
|
||||
className={cn(
|
||||
"data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 top-0 left-0 w-full p-2 pr-2.5 md:absolute md:w-auto",
|
||||
"group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:data-[state=open]:animate-in group-data-[viewport=false]/navigation-menu:data-[state=closed]:animate-out group-data-[viewport=false]/navigation-menu:data-[state=closed]:zoom-out-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:fade-in-0 group-data-[viewport=false]/navigation-menu:data-[state=closed]:fade-out-0 group-data-[viewport=false]/navigation-menu:top-full group-data-[viewport=false]/navigation-menu:mt-1.5 group-data-[viewport=false]/navigation-menu:overflow-hidden group-data-[viewport=false]/navigation-menu:rounded-md group-data-[viewport=false]/navigation-menu:border group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:duration-200 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none",
|
||||
"group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:data-[state=open]:animate-in group-data-[viewport=false]/navigation-menu:data-[state=closed]:animate-out group-data-[viewport=false]/navigation-menu:data-[state=closed]:zoom-out-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:fade-in-0 group-data-[viewport=false]/navigation-menu:data-[state=closed]:fade-out-0 group-data-[viewport=false]/navigation-menu:top-full group-data-[viewport=false]/navigation-menu:mt-1.5 group-data-[viewport=false]/navigation-menu:overflow-hidden group-data-[viewport=false]/navigation-menu:rounded-sm group-data-[viewport=false]/navigation-menu:border group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:duration-200 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
@ -112,7 +112,7 @@ function NavigationMenuViewport({
|
|||
<NavigationMenuPrimitive.Viewport
|
||||
data-slot="navigation-menu-viewport"
|
||||
className={cn(
|
||||
"origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border shadow md:w-[var(--radix-navigation-menu-viewport-width)]",
|
||||
"origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-sm border shadow md:w-[var(--radix-navigation-menu-viewport-width)]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ function PopoverContent({
|
|||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-sm border border-border p-5 shadow-lg outline-hidden font-mono text-base",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ function Progress({
|
|||
<ProgressPrimitive.Root
|
||||
data-slot="progress"
|
||||
className={cn(
|
||||
"bg-primary/20 relative h-2 w-full overflow-hidden rounded-full",
|
||||
"bg-muted relative h-3 w-full overflow-hidden rounded-full border border-border",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ const SelectTrigger = React.forwardRef<
|
|||
<SelectPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
||||
"flex h-11 w-full items-center justify-between whitespace-nowrap rounded-sm border border-input bg-background px-4 py-2.5 text-base font-mono font-medium shadow-none ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:border-primary focus:shadow-focus disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
@ -75,7 +75,7 @@ const SelectContent = React.forwardRef<
|
|||
<SelectPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative z-50 max-h-[--radix-select-content-available-height] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-select-content-transform-origin]",
|
||||
"relative z-50 max-h-[--radix-select-content-available-height] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-sm border border-border bg-popover text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-select-content-transform-origin]",
|
||||
position === "popper" &&
|
||||
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
||||
className
|
||||
|
|
@ -118,7 +118,7 @@ const SelectItem = React.forwardRef<
|
|||
<SelectPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
"relative flex w-full cursor-default select-none items-center rounded-sm py-2.5 pl-4 pr-9 text-base font-mono outline-none focus:bg-muted focus:text-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 hover:bg-muted transition-colors",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ const SheetOverlay = React.forwardRef<
|
|||
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
|
||||
|
||||
const sheetVariants = cva(
|
||||
"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out",
|
||||
"fixed z-50 gap-4 bg-background p-0 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out border-border",
|
||||
{
|
||||
variants: {
|
||||
side: {
|
||||
|
|
@ -108,7 +108,7 @@ const SheetTitle = React.forwardRef<
|
|||
>(({ className, ...props }, ref) => (
|
||||
<SheetPrimitive.Title
|
||||
ref={ref}
|
||||
className={cn("text-lg font-semibold text-foreground", className)}
|
||||
className={cn("text-xl font-mono font-bold uppercase tracking-wider text-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
|
|
@ -120,7 +120,7 @@ const SheetDescription = React.forwardRef<
|
|||
>(({ className, ...props }, ref) => (
|
||||
<SheetPrimitive.Description
|
||||
ref={ref}
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
className={cn("text-base text-foreground/70 font-mono", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
|
|||
return (
|
||||
<div
|
||||
data-slot="skeleton"
|
||||
className={cn("bg-accent animate-pulse rounded-md", className)}
|
||||
className={cn("bg-muted animate-pulse rounded-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ function Switch({
|
|||
<SwitchPrimitive.Root
|
||||
data-slot="switch"
|
||||
className={cn(
|
||||
"peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-muted focus-visible:border-primary focus-visible:shadow-focus inline-flex h-7 w-12 shrink-0 items-center rounded-full border border-border shadow-none transition-all outline-none disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
@ -19,7 +19,7 @@ function Switch({
|
|||
<SwitchPrimitive.Thumb
|
||||
data-slot="switch-thumb"
|
||||
className={cn(
|
||||
"bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0"
|
||||
"bg-background pointer-events-none block size-5.5 rounded-full ring-0 shadow-sm transition-transform data-[state=checked]:translate-x-5.5 data-[state=unchecked]:translate-x-0.5"
|
||||
)}
|
||||
/>
|
||||
</SwitchPrimitive.Root>
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ function Table({ className, ...props }: React.ComponentProps<"table">) {
|
|||
return (
|
||||
<div
|
||||
data-slot="table-container"
|
||||
className="relative w-full overflow-x-auto"
|
||||
className="relative w-full overflow-x-auto rounded-sm border border-border"
|
||||
>
|
||||
<table
|
||||
data-slot="table"
|
||||
className={cn("w-full caption-bottom text-sm", className)}
|
||||
className={cn("w-full caption-bottom text-base font-mono", className)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -21,7 +21,7 @@ function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
|
|||
return (
|
||||
<thead
|
||||
data-slot="table-header"
|
||||
className={cn("[&_tr]:border-b", className)}
|
||||
className={cn("bg-muted/50 [&_tr]:border-b border-border", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
|
@ -55,7 +55,7 @@ function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
|
|||
<tr
|
||||
data-slot="table-row"
|
||||
className={cn(
|
||||
"hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors",
|
||||
"hover:bg-muted/30 data-[state=selected]:bg-muted border-b border-border transition-colors",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
@ -68,7 +68,7 @@ function TableHead({ className, ...props }: React.ComponentProps<"th">) {
|
|||
<th
|
||||
data-slot="table-head"
|
||||
className={cn(
|
||||
"text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
||||
"text-foreground/70 h-12 px-4 text-left align-middle font-mono font-bold text-sm uppercase tracking-wider whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
@ -81,7 +81,7 @@ function TableCell({ className, ...props }: React.ComponentProps<"td">) {
|
|||
<td
|
||||
data-slot="table-cell"
|
||||
className={cn(
|
||||
"p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
||||
"px-4 py-3.5 align-middle text-foreground text-base [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ function TabsList({
|
|||
<TabsPrimitive.List
|
||||
data-slot="tabs-list"
|
||||
className={cn(
|
||||
"bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]",
|
||||
"bg-muted text-muted-foreground inline-flex h-12 w-fit items-center justify-center rounded-sm p-1.5 border border-border",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
@ -40,7 +40,7 @@ function TabsTrigger({
|
|||
<TabsPrimitive.Trigger
|
||||
data-slot="tabs-trigger"
|
||||
className={cn(
|
||||
"data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:border-primary/30 focus-visible:border-primary focus-visible:shadow-focus text-muted-foreground inline-flex h-10 flex-1 items-center justify-center gap-2 rounded-sm border border-transparent px-4 py-2 text-base font-mono font-semibold uppercase tracking-wider whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-5",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ export function Textarea({ className, ...props }: React.ComponentProps<"textarea
|
|||
<textarea
|
||||
data-slot="textarea"
|
||||
className={cn(
|
||||
"border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
"border-input placeholder:text-muted-foreground focus-visible:border-primary focus-visible:shadow-focus aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive flex field-sizing-content min-h-28 w-full rounded-sm border bg-background px-4 py-3 text-base font-mono font-medium leading-relaxed shadow-none transition-[color,box-shadow] outline-none disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,194 @@
|
|||
/**
|
||||
* Theme Toggle Component
|
||||
* Cyberpunk-styled theme switcher with smooth animations
|
||||
*/
|
||||
|
||||
import { useTheme } from "next-themes";
|
||||
import { useEffect, useState, useCallback } from "react";
|
||||
import { Sun, Moon, Monitor } from "lucide-react";
|
||||
import { cn } from "@/shared/utils/utils";
|
||||
|
||||
interface ThemeToggleProps {
|
||||
collapsed?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
// Enable smooth theme transition
|
||||
const enableTransition = () => {
|
||||
const html = document.documentElement;
|
||||
html.classList.add('theme-transition');
|
||||
|
||||
// Remove transition class after animation completes
|
||||
setTimeout(() => {
|
||||
html.classList.remove('theme-transition');
|
||||
}, 280);
|
||||
};
|
||||
|
||||
export function ThemeToggle({ collapsed = false, className }: ThemeToggleProps) {
|
||||
const { theme, setTheme, resolvedTheme } = useTheme();
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
// Prevent hydration mismatch
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
// Smooth theme change handler
|
||||
const handleThemeChange = useCallback((newTheme: string) => {
|
||||
enableTransition();
|
||||
setTheme(newTheme);
|
||||
}, [setTheme]);
|
||||
|
||||
if (!mounted) {
|
||||
return (
|
||||
<div className={cn("h-10 w-full animate-pulse rounded-lg bg-muted", className)} />
|
||||
);
|
||||
}
|
||||
|
||||
const themes = [
|
||||
{ value: "light", icon: Sun, label: "浅色" },
|
||||
{ value: "dark", icon: Moon, label: "深色" },
|
||||
{ value: "system", icon: Monitor, label: "系统" },
|
||||
];
|
||||
|
||||
const currentTheme = themes.find((t) => t.value === theme) || themes[2];
|
||||
const CurrentIcon = currentTheme.icon;
|
||||
|
||||
// Cycle through themes
|
||||
const cycleTheme = () => {
|
||||
const currentIndex = themes.findIndex((t) => t.value === theme);
|
||||
const nextIndex = (currentIndex + 1) % themes.length;
|
||||
handleThemeChange(themes[nextIndex].value);
|
||||
};
|
||||
|
||||
// Collapsed mode - single button
|
||||
if (collapsed) {
|
||||
return (
|
||||
<button
|
||||
onClick={cycleTheme}
|
||||
className={cn(
|
||||
"flex items-center justify-center w-full h-10 rounded-lg",
|
||||
"border border-transparent transition-colors duration-200",
|
||||
"dark:text-muted-foreground dark:hover:text-primary dark:hover:bg-primary/10 dark:hover:border-primary/30",
|
||||
"text-muted-foreground hover:text-primary hover:bg-primary/5 hover:border-primary/20",
|
||||
className
|
||||
)}
|
||||
title={`当前: ${currentTheme.label}模式`}
|
||||
>
|
||||
<CurrentIcon
|
||||
className={cn(
|
||||
"w-6 h-6 transition-transform duration-200",
|
||||
resolvedTheme === "dark" && "text-amber-400",
|
||||
resolvedTheme === "light" && "text-orange-500"
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
// Expanded mode - segmented control
|
||||
return (
|
||||
<div className={cn("w-full", className)}>
|
||||
<div className="flex items-center gap-2 px-3 py-2">
|
||||
<span className="text-xs text-muted-foreground dark:text-muted-foreground font-mono uppercase tracking-wider">
|
||||
主题
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-center gap-1 p-1 mx-3 mb-2 rounded-lg",
|
||||
"dark:cyber-bg-elevated dark:border dark:border-[#1a2535]",
|
||||
"bg-muted border border-border"
|
||||
)}
|
||||
>
|
||||
{themes.map(({ value, icon: Icon, label }) => {
|
||||
const isActive = theme === value;
|
||||
return (
|
||||
<button
|
||||
key={value}
|
||||
onClick={() => handleThemeChange(value)}
|
||||
className={cn(
|
||||
"flex-1 flex items-center justify-center gap-1.5 py-1.5 px-2 rounded-md transition-all duration-200",
|
||||
"text-xs font-mono uppercase tracking-wider",
|
||||
isActive
|
||||
? cn(
|
||||
"dark:bg-primary/20 dark:text-primary dark:border dark:border-primary/40",
|
||||
"bg-white text-primary border border-primary/30 shadow-sm"
|
||||
)
|
||||
: cn(
|
||||
"dark:text-muted-foreground dark:hover:text-foreground dark:hover:bg-[#151a22]",
|
||||
"text-muted-foreground hover:text-muted-foreground hover:bg-background"
|
||||
)
|
||||
)}
|
||||
title={`${label}模式`}
|
||||
>
|
||||
<Icon
|
||||
className={cn(
|
||||
"w-3.5 h-3.5 transition-all duration-200",
|
||||
isActive && value === "dark" && "text-amber-400",
|
||||
isActive && value === "light" && "text-orange-500",
|
||||
isActive && value === "system" && "text-cyan-400"
|
||||
)}
|
||||
/>
|
||||
<span className="hidden sm:inline">{label}</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Compact version for dropdown menus
|
||||
export function ThemeToggleCompact({ className }: { className?: string }) {
|
||||
const { setTheme, resolvedTheme } = useTheme();
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
const handleToggle = useCallback(() => {
|
||||
enableTransition();
|
||||
setTheme(resolvedTheme === "dark" ? "light" : "dark");
|
||||
}, [setTheme, resolvedTheme]);
|
||||
|
||||
if (!mounted) return null;
|
||||
|
||||
const isDark = resolvedTheme === "dark";
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={handleToggle}
|
||||
className={cn(
|
||||
"relative flex items-center justify-center w-10 h-10 rounded-lg",
|
||||
"dark:cyber-bg-elevated dark:border dark:border-[#1a2535] dark:hover:border-primary/50",
|
||||
"bg-muted border border-border hover:border-primary/50",
|
||||
"group transition-colors duration-200",
|
||||
className
|
||||
)}
|
||||
title={isDark ? "切换到浅色模式" : "切换到深色模式"}
|
||||
>
|
||||
{/* Sun icon */}
|
||||
<Sun
|
||||
className={cn(
|
||||
"absolute w-5 h-5 transition-all duration-250",
|
||||
isDark
|
||||
? "opacity-0 rotate-90 scale-0"
|
||||
: "opacity-100 rotate-0 scale-100 text-orange-500"
|
||||
)}
|
||||
/>
|
||||
{/* Moon icon */}
|
||||
<Moon
|
||||
className={cn(
|
||||
"absolute w-5 h-5 transition-all duration-250",
|
||||
isDark
|
||||
? "opacity-100 rotate-0 scale-100 text-amber-400"
|
||||
: "opacity-0 -rotate-90 scale-0"
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export default ThemeToggle;
|
||||
|
|
@ -26,7 +26,7 @@ function ToggleGroup({
|
|||
data-variant={variant}
|
||||
data-size={size}
|
||||
className={cn(
|
||||
"group/toggle-group flex w-fit items-center rounded-md data-[variant=outline]:shadow-xs",
|
||||
"group/toggle-group flex w-fit items-center rounded-sm data-[variant=outline]:shadow-xs",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority";
|
|||
import { cn } from "@/shared/utils/utils";
|
||||
|
||||
const toggleVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap",
|
||||
"inline-flex items-center justify-center gap-2 rounded-sm text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
|
|
|||
|
|
@ -46,13 +46,13 @@ function TooltipContent({
|
|||
data-slot="tooltip-content"
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
|
||||
"bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-sm px-4 py-2 text-sm font-mono text-balance shadow-lg",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<TooltipPrimitive.Arrow className="bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
|
||||
<TooltipPrimitive.Arrow className="bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
|
||||
</TooltipPrimitive.Content>
|
||||
</TooltipPrimitive.Portal>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -138,17 +138,17 @@ export default function Account() {
|
|||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-screen bg-[#0a0a0f]">
|
||||
<div className="flex items-center justify-center min-h-screen cyber-bg-elevated">
|
||||
<div className="text-center space-y-4">
|
||||
<div className="loading-spinner mx-auto" />
|
||||
<p className="text-gray-500 font-mono text-sm uppercase tracking-wider">加载中...</p>
|
||||
<p className="text-muted-foreground font-mono text-sm uppercase tracking-wider">加载中...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6 p-6 bg-[#0a0a0f] min-h-screen font-mono relative">
|
||||
<div className="space-y-6 p-6 cyber-bg-elevated min-h-screen font-mono relative">
|
||||
{/* Grid background */}
|
||||
<div className="absolute inset-0 cyber-grid-subtle pointer-events-none" />
|
||||
|
||||
|
|
@ -157,7 +157,7 @@ export default function Account() {
|
|||
<div className="cyber-card p-0">
|
||||
<div className="cyber-card-header">
|
||||
<User className="w-5 h-5 text-primary" />
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-white">用户信息</h3>
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-foreground">用户信息</h3>
|
||||
</div>
|
||||
<div className="p-6 text-center">
|
||||
<div className="relative inline-block mb-4">
|
||||
|
|
@ -167,31 +167,31 @@ export default function Account() {
|
|||
{getInitials(profile?.full_name, profile?.email)}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="absolute -bottom-1 -right-1 w-6 h-6 bg-emerald-500 rounded-full border-2 border-[#0a0a0f] flex items-center justify-center">
|
||||
<div className="w-2 h-2 bg-white rounded-full animate-pulse" />
|
||||
<div className="absolute -bottom-1 -right-1 w-6 h-6 bg-emerald-500 rounded-full border-2 border-background flex items-center justify-center">
|
||||
<div className="w-2 h-2 bg-foreground rounded-full animate-pulse" />
|
||||
</div>
|
||||
</div>
|
||||
<h4 className="text-lg font-bold text-white uppercase mb-1">
|
||||
<h4 className="text-lg font-bold text-foreground uppercase mb-1">
|
||||
{profile?.full_name || "未设置姓名"}
|
||||
</h4>
|
||||
<p className="text-gray-500 text-sm">{profile?.email}</p>
|
||||
<p className="text-muted-foreground text-sm">{profile?.email}</p>
|
||||
|
||||
<div className="mt-6 pt-6 border-t border-gray-800 space-y-3 text-left">
|
||||
<div className="mt-6 pt-6 border-t border-border space-y-3 text-left">
|
||||
<div className="flex items-center gap-3 text-sm">
|
||||
<Shield className="w-4 h-4 text-violet-400" />
|
||||
<span className="text-gray-500">角色:</span>
|
||||
<span className="text-muted-foreground">角色:</span>
|
||||
<span className="text-violet-400 font-bold uppercase">
|
||||
{profile?.role === 'admin' ? '管理员' : '成员'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 text-sm">
|
||||
<Calendar className="w-4 h-4 text-sky-400" />
|
||||
<span className="text-gray-500">注册时间:</span>
|
||||
<span className="text-white font-mono">{formatDate(profile?.created_at)}</span>
|
||||
<span className="text-muted-foreground">注册时间:</span>
|
||||
<span className="text-foreground font-mono">{formatDate(profile?.created_at)}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 pt-6 border-t border-gray-800 space-y-2">
|
||||
<div className="mt-6 pt-6 border-t border-border space-y-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={handleSwitchAccount}
|
||||
|
|
@ -216,24 +216,24 @@ export default function Account() {
|
|||
<div className="lg:col-span-2 cyber-card p-0">
|
||||
<div className="cyber-card-header">
|
||||
<Terminal className="w-5 h-5 text-primary" />
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-white">基本信息</h3>
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-foreground">基本信息</h3>
|
||||
</div>
|
||||
<div className="p-6 space-y-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email" className="text-xs font-bold text-gray-500 uppercase flex items-center gap-2">
|
||||
<Label htmlFor="email" className="text-xs font-bold text-muted-foreground uppercase flex items-center gap-2">
|
||||
<Mail className="w-3 h-3" /> 邮箱
|
||||
</Label>
|
||||
<Input
|
||||
id="email"
|
||||
value={profile?.email || ""}
|
||||
disabled
|
||||
className="cyber-input bg-gray-900/50 text-gray-500 cursor-not-allowed"
|
||||
className="cyber-input bg-muted text-muted-foreground cursor-not-allowed"
|
||||
/>
|
||||
<p className="text-xs text-gray-600">邮箱不可修改</p>
|
||||
<p className="text-xs text-muted-foreground">邮箱不可修改</p>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="full_name" className="text-xs font-bold text-gray-500 uppercase flex items-center gap-2">
|
||||
<Label htmlFor="full_name" className="text-xs font-bold text-muted-foreground uppercase flex items-center gap-2">
|
||||
<User className="w-3 h-3" /> 姓名
|
||||
</Label>
|
||||
<Input
|
||||
|
|
@ -245,7 +245,7 @@ export default function Account() {
|
|||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="phone" className="text-xs font-bold text-gray-500 uppercase flex items-center gap-2">
|
||||
<Label htmlFor="phone" className="text-xs font-bold text-muted-foreground uppercase flex items-center gap-2">
|
||||
<Phone className="w-3 h-3" /> 手机号
|
||||
</Label>
|
||||
<Input
|
||||
|
|
@ -258,14 +258,14 @@ export default function Account() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="pt-6 border-t border-gray-800">
|
||||
<div className="pt-6 border-t border-border">
|
||||
<h3 className="section-title text-sm mb-4 flex items-center gap-2">
|
||||
<GitBranch className="w-4 h-4" />
|
||||
代码托管账号
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="github" className="text-xs font-bold text-gray-500 uppercase flex items-center gap-2">
|
||||
<Label htmlFor="github" className="text-xs font-bold text-muted-foreground uppercase flex items-center gap-2">
|
||||
<GitBranch className="w-3 h-3" /> GitHub 用户名
|
||||
</Label>
|
||||
<Input
|
||||
|
|
@ -277,7 +277,7 @@ export default function Account() {
|
|||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="gitlab" className="text-xs font-bold text-gray-500 uppercase flex items-center gap-2">
|
||||
<Label htmlFor="gitlab" className="text-xs font-bold text-muted-foreground uppercase flex items-center gap-2">
|
||||
<GitBranch className="w-3 h-3" /> GitLab 用户名
|
||||
</Label>
|
||||
<Input
|
||||
|
|
@ -313,12 +313,12 @@ export default function Account() {
|
|||
<div className="lg:col-span-3 cyber-card p-0">
|
||||
<div className="cyber-card-header">
|
||||
<KeyRound className="w-5 h-5 text-amber-400" />
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-white">修改密码</h3>
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-foreground">修改密码</h3>
|
||||
</div>
|
||||
<div className="p-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="new_password" className="text-xs font-bold text-gray-500 uppercase">新密码</Label>
|
||||
<Label htmlFor="new_password" className="text-xs font-bold text-muted-foreground uppercase">新密码</Label>
|
||||
<Input
|
||||
id="new_password"
|
||||
type="password"
|
||||
|
|
@ -329,7 +329,7 @@ export default function Account() {
|
|||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="confirm_password" className="text-xs font-bold text-gray-500 uppercase">确认密码</Label>
|
||||
<Label htmlFor="confirm_password" className="text-xs font-bold text-muted-foreground uppercase">确认密码</Label>
|
||||
<Input
|
||||
id="confirm_password"
|
||||
type="password"
|
||||
|
|
@ -365,13 +365,13 @@ export default function Account() {
|
|||
|
||||
{/* Logout Confirmation Dialog */}
|
||||
<AlertDialog open={showLogoutDialog} onOpenChange={setShowLogoutDialog}>
|
||||
<AlertDialogContent className="cyber-card border-rose-500/30 bg-[#0c0c12]">
|
||||
<AlertDialogContent className="cyber-card border-rose-500/30 cyber-dialog">
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle className="text-lg font-bold uppercase text-white flex items-center gap-2">
|
||||
<AlertDialogTitle className="text-lg font-bold uppercase text-foreground flex items-center gap-2">
|
||||
<LogOut className="w-5 h-5 text-rose-400" />
|
||||
确认退出登录?
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription className="text-gray-400">
|
||||
<AlertDialogDescription className="text-muted-foreground">
|
||||
退出后需要重新登录才能访问系统。
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { Settings, Database, Terminal } from "lucide-react";
|
|||
|
||||
export default function AdminDashboard() {
|
||||
return (
|
||||
<div className="space-y-6 p-6 bg-[#0a0a0f] min-h-screen font-mono relative">
|
||||
<div className="space-y-6 p-6 cyber-bg-elevated min-h-screen font-mono relative">
|
||||
{/* Grid background */}
|
||||
<div className="absolute inset-0 cyber-grid-subtle pointer-events-none" />
|
||||
|
||||
|
|
@ -19,24 +19,24 @@ export default function AdminDashboard() {
|
|||
<div className="cyber-card p-0">
|
||||
<div className="cyber-card-header">
|
||||
<Terminal className="w-5 h-5 text-primary" />
|
||||
<h1 className="text-lg font-bold uppercase tracking-wider text-white">系统管理</h1>
|
||||
<h1 className="text-lg font-bold uppercase tracking-wider text-foreground">系统管理</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main Content Tabs */}
|
||||
<Tabs defaultValue="config" className="w-full relative z-10">
|
||||
<TabsList className="grid w-full grid-cols-2 bg-gray-900/50 border border-gray-800 p-1 h-auto gap-1 rounded-lg mb-6">
|
||||
<TabsList className="grid w-full grid-cols-2 bg-muted border border-border p-1 h-auto gap-1 rounded-lg mb-6">
|
||||
<TabsTrigger
|
||||
value="config"
|
||||
className="data-[state=active]:bg-primary data-[state=active]:text-white font-mono font-bold uppercase py-3 text-gray-400 transition-all rounded text-sm flex items-center gap-2"
|
||||
className="data-[state=active]:bg-primary data-[state=active]:text-foreground font-mono font-bold uppercase py-3 text-muted-foreground transition-all rounded text-sm flex items-center gap-2"
|
||||
>
|
||||
<Settings className="w-4 h-4" />
|
||||
系统配置
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="data"
|
||||
className="data-[state=active]:bg-primary data-[state=active]:text-white font-mono font-bold uppercase py-3 text-gray-400 transition-all rounded text-sm flex items-center gap-2"
|
||||
className="data-[state=active]:bg-primary data-[state=active]:text-foreground font-mono font-bold uppercase py-3 text-muted-foreground transition-all rounded text-sm flex items-center gap-2"
|
||||
>
|
||||
<Database className="w-4 h-4" />
|
||||
数据管理
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ export const AgentDetailPanel = memo(function AgentDetailPanel({ agentId, treeNo
|
|||
<div className="absolute top-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-primary/50 to-transparent" />
|
||||
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between p-3 border-b border-gray-800/50">
|
||||
<div className="flex items-center justify-between p-3 border-b border-border">
|
||||
<div className="flex items-center gap-2.5">
|
||||
{/* Agent type icon with color */}
|
||||
<div className={`text-${typeConfig.color}-400`}>
|
||||
|
|
@ -62,22 +62,22 @@ export const AgentDetailPanel = memo(function AgentDetailPanel({ agentId, treeNo
|
|||
|
||||
{/* Agent name */}
|
||||
<div>
|
||||
<span className="text-sm font-medium text-white block">{agent.agent_name}</span>
|
||||
<span className="text-[10px] text-gray-500 uppercase tracking-wider">{typeConfig.label}</span>
|
||||
<span className="text-sm font-medium text-foreground block">{agent.agent_name}</span>
|
||||
<span className="text-xs text-muted-foreground uppercase tracking-wider">{typeConfig.label}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Close button */}
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="w-6 h-6 flex items-center justify-center rounded hover:bg-white/10 transition-colors text-gray-500 hover:text-white"
|
||||
className="w-6 h-6 flex items-center justify-center rounded hover:bg-white/10 transition-colors text-muted-foreground hover:text-foreground"
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Status indicator */}
|
||||
<div className="px-3 py-2 border-b border-gray-800/50 bg-gray-900/20">
|
||||
<div className="px-3 py-2 border-b border-border bg-muted/30">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="relative">
|
||||
<div className={`
|
||||
|
|
@ -86,7 +86,7 @@ export const AgentDetailPanel = memo(function AgentDetailPanel({ agentId, treeNo
|
|||
${agent.status === 'completed' ? 'bg-green-500' : ''}
|
||||
${agent.status === 'failed' ? 'bg-red-400' : ''}
|
||||
${agent.status === 'waiting' ? 'bg-yellow-400' : ''}
|
||||
${agent.status === 'created' ? 'bg-gray-500' : ''}
|
||||
${agent.status === 'created' ? 'bg-background0' : ''}
|
||||
`} />
|
||||
{isRunning && (
|
||||
<div className="absolute inset-0 w-2.5 h-2.5 rounded-full bg-green-400 animate-ping opacity-30" />
|
||||
|
|
@ -101,30 +101,30 @@ export const AgentDetailPanel = memo(function AgentDetailPanel({ agentId, treeNo
|
|||
{/* Metrics grid */}
|
||||
<div className="p-3 grid grid-cols-2 gap-2">
|
||||
{/* Iterations */}
|
||||
<div className="flex items-center gap-2 p-2 rounded bg-gray-900/30 border border-gray-800/30">
|
||||
<div className="flex items-center gap-2 p-2 rounded bg-muted/50 border border-border">
|
||||
<Repeat className="w-3.5 h-3.5 text-cyan-400/70" />
|
||||
<div>
|
||||
<div className="text-[9px] text-gray-600 uppercase">Iterations</div>
|
||||
<div className="text-sm text-white font-mono">{agent.iterations || 0}</div>
|
||||
<div className="text-xs text-muted-foreground uppercase">Iterations</div>
|
||||
<div className="text-sm text-foreground font-mono">{agent.iterations || 0}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tool Calls */}
|
||||
<div className="flex items-center gap-2 p-2 rounded bg-gray-900/30 border border-gray-800/30">
|
||||
<div className="flex items-center gap-2 p-2 rounded bg-muted/50 border border-border">
|
||||
<Zap className="w-3.5 h-3.5 text-amber-400/70" />
|
||||
<div>
|
||||
<div className="text-[9px] text-gray-600 uppercase">Tool Calls</div>
|
||||
<div className="text-sm text-white font-mono">{agent.tool_calls || 0}</div>
|
||||
<div className="text-xs text-muted-foreground uppercase">Tool Calls</div>
|
||||
<div className="text-sm text-foreground font-mono">{agent.tool_calls || 0}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Findings - Only show for Orchestrator (root agent with no parent) */}
|
||||
{!agent.parent_agent_id && (
|
||||
<div className="flex items-center gap-2 p-2 rounded bg-gray-900/30 border border-gray-800/30">
|
||||
<Bug className={`w-3.5 h-3.5 ${agent.findings_count > 0 ? 'text-red-400/70' : 'text-gray-500/70'}`} />
|
||||
<div className="flex items-center gap-2 p-2 rounded bg-muted/50 border border-border">
|
||||
<Bug className={`w-3.5 h-3.5 ${agent.findings_count > 0 ? 'text-red-400/70' : 'text-muted-foreground/70'}`} />
|
||||
<div>
|
||||
<div className="text-[9px] text-gray-600 uppercase">Findings</div>
|
||||
<div className={`text-sm font-mono ${agent.findings_count > 0 ? 'text-red-400' : 'text-white'}`}>
|
||||
<div className="text-xs text-muted-foreground uppercase">Findings</div>
|
||||
<div className={`text-sm font-mono ${agent.findings_count > 0 ? 'text-red-400' : 'text-foreground'}`}>
|
||||
{agent.findings_count}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -133,13 +133,13 @@ export const AgentDetailPanel = memo(function AgentDetailPanel({ agentId, treeNo
|
|||
|
||||
{/* Duration/Status - Show for sub-agents instead of Findings */}
|
||||
{agent.parent_agent_id && (
|
||||
<div className="flex items-center gap-2 p-2 rounded bg-gray-900/30 border border-gray-800/30">
|
||||
<Clock className="w-3.5 h-3.5 text-slate-400/70" />
|
||||
<div className="flex items-center gap-2 p-2 rounded bg-muted/50 border border-border">
|
||||
<Clock className="w-3.5 h-3.5 text-muted-foreground/70" />
|
||||
<div>
|
||||
<div className="text-[9px] text-gray-600 uppercase">
|
||||
<div className="text-xs text-muted-foreground uppercase">
|
||||
{agent.duration_ms ? "Duration" : "Status"}
|
||||
</div>
|
||||
<div className="text-sm text-white font-mono">
|
||||
<div className="text-sm text-foreground font-mono">
|
||||
{agent.duration_ms
|
||||
? `${(agent.duration_ms / 1000).toFixed(1)}s`
|
||||
: (AGENT_STATUS_CONFIG[agent.status]?.text || agent.status)
|
||||
|
|
@ -150,11 +150,11 @@ export const AgentDetailPanel = memo(function AgentDetailPanel({ agentId, treeNo
|
|||
)}
|
||||
|
||||
{/* Tokens */}
|
||||
<div className="flex items-center gap-2 p-2 rounded bg-gray-900/30 border border-gray-800/30">
|
||||
<div className="flex items-center gap-2 p-2 rounded bg-muted/50 border border-border">
|
||||
<FileCode className="w-3.5 h-3.5 text-purple-400/70" />
|
||||
<div>
|
||||
<div className="text-[9px] text-gray-600 uppercase">Tokens</div>
|
||||
<div className="text-sm text-white font-mono">
|
||||
<div className="text-xs text-muted-foreground uppercase">Tokens</div>
|
||||
<div className="text-sm text-foreground font-mono">
|
||||
{((agent.tokens_used || 0) / 1000).toFixed(1)}k
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -164,12 +164,12 @@ export const AgentDetailPanel = memo(function AgentDetailPanel({ agentId, treeNo
|
|||
{/* Task description */}
|
||||
{agent.task_description && (
|
||||
<div className="px-3 pb-3">
|
||||
<div className="p-2.5 rounded bg-gray-900/30 border border-gray-800/30">
|
||||
<div className="p-2.5 rounded bg-muted/50 border border-border">
|
||||
<div className="flex items-center gap-1.5 mb-1.5">
|
||||
<Clock className="w-3 h-3 text-gray-500" />
|
||||
<span className="text-[9px] text-gray-500 uppercase tracking-wider">Current Task</span>
|
||||
<Clock className="w-3 h-3 text-muted-foreground" />
|
||||
<span className="text-xs text-muted-foreground uppercase tracking-wider">Current Task</span>
|
||||
</div>
|
||||
<p className="text-xs text-gray-400 leading-relaxed line-clamp-3">
|
||||
<p className="text-xs text-muted-foreground leading-relaxed line-clamp-3">
|
||||
{agent.task_description}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -179,7 +179,7 @@ export const AgentDetailPanel = memo(function AgentDetailPanel({ agentId, treeNo
|
|||
{/* Sub-agents indicator */}
|
||||
{agent.children && agent.children.length > 0 && (
|
||||
<div className="px-3 pb-3">
|
||||
<div className="flex items-center gap-2 text-[10px] text-gray-500">
|
||||
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
||||
<Network className="w-3 h-3" />
|
||||
<span className="uppercase tracking-wider">
|
||||
{agent.children.length} Sub-agent{agent.children.length > 1 ? 's' : ''}
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ export class AgentErrorBoundary extends Component<Props, State> {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="h-screen bg-[#0a0a0f] flex items-center justify-center p-4">
|
||||
<div className="h-screen cyber-bg-elevated flex items-center justify-center p-4">
|
||||
<div className="w-full max-w-lg space-y-6">
|
||||
{/* Error Header */}
|
||||
<div className="flex items-center gap-4">
|
||||
|
|
@ -179,16 +179,16 @@ export class AgentErrorBoundary extends Component<Props, State> {
|
|||
)} />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-white">Agent Error</h2>
|
||||
<p className="text-sm text-gray-400">{this.getRecoveryHint()}</p>
|
||||
<h2 className="text-xl font-bold text-foreground">Agent Error</h2>
|
||||
<p className="text-sm text-muted-foreground">{this.getRecoveryHint()}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Error Details */}
|
||||
<div className="bg-[#0d0d12] border border-gray-800 rounded-lg overflow-hidden">
|
||||
<div className="px-4 py-3 border-b border-gray-800 flex items-center gap-2">
|
||||
<Terminal className="w-4 h-4 text-gray-500" />
|
||||
<span className="text-xs text-gray-500 uppercase tracking-wider font-bold">
|
||||
<div className="cyber-dialog border border-border rounded-lg overflow-hidden">
|
||||
<div className="px-4 py-3 border-b border-border flex items-center gap-2">
|
||||
<Terminal className="w-4 h-4 text-muted-foreground" />
|
||||
<span className="text-xs text-muted-foreground uppercase tracking-wider font-bold">
|
||||
Error Details
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -199,20 +199,20 @@ export class AgentErrorBoundary extends Component<Props, State> {
|
|||
<Bug className="w-4 h-4 text-red-400 mt-0.5 flex-shrink-0" />
|
||||
<div>
|
||||
<p className="text-sm font-mono text-red-400">{error.name}</p>
|
||||
<p className="text-sm text-gray-300">{error.message}</p>
|
||||
<p className="text-sm text-foreground">{error.message}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{this.props.taskId && (
|
||||
<div className="text-xs text-gray-500">
|
||||
Task ID: <span className="font-mono text-gray-400">{this.props.taskId}</span>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
Task ID: <span className="font-mono text-muted-foreground">{this.props.taskId}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{retryCount > 0 && (
|
||||
<div className="text-xs text-gray-500">
|
||||
<div className="text-xs text-muted-foreground">
|
||||
Retry attempts: <span className="text-yellow-400">{retryCount}/{maxRetries}</span>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -220,10 +220,10 @@ export class AgentErrorBoundary extends Component<Props, State> {
|
|||
{/* Stack trace (dev only) */}
|
||||
{import.meta.env.DEV && error?.stack && (
|
||||
<details className="text-xs">
|
||||
<summary className="cursor-pointer text-gray-500 hover:text-gray-300 transition-colors">
|
||||
<summary className="cursor-pointer text-muted-foreground hover:text-foreground transition-colors">
|
||||
Stack Trace
|
||||
</summary>
|
||||
<pre className="mt-2 p-3 bg-black/50 rounded text-[10px] text-gray-400 overflow-auto max-h-40">
|
||||
<pre className="mt-2 p-3 bg-background/50 rounded text-xs text-muted-foreground overflow-auto max-h-40">
|
||||
{error.stack}
|
||||
</pre>
|
||||
</details>
|
||||
|
|
@ -231,10 +231,10 @@ export class AgentErrorBoundary extends Component<Props, State> {
|
|||
|
||||
{import.meta.env.DEV && errorInfo?.componentStack && (
|
||||
<details className="text-xs">
|
||||
<summary className="cursor-pointer text-gray-500 hover:text-gray-300 transition-colors">
|
||||
<summary className="cursor-pointer text-muted-foreground hover:text-foreground transition-colors">
|
||||
Component Stack
|
||||
</summary>
|
||||
<pre className="mt-2 p-3 bg-black/50 rounded text-[10px] text-gray-400 overflow-auto max-h-40">
|
||||
<pre className="mt-2 p-3 bg-background/50 rounded text-xs text-muted-foreground overflow-auto max-h-40">
|
||||
{errorInfo.componentStack}
|
||||
</pre>
|
||||
</details>
|
||||
|
|
@ -257,7 +257,7 @@ export class AgentErrorBoundary extends Component<Props, State> {
|
|||
<Button
|
||||
onClick={this.handleGoBack}
|
||||
variant="outline"
|
||||
className="flex-1 border-gray-700 hover:bg-gray-800"
|
||||
className="flex-1 border-border hover:bg-muted"
|
||||
>
|
||||
<ArrowLeft className="w-4 h-4 mr-2" />
|
||||
Go Back
|
||||
|
|
@ -265,7 +265,7 @@ export class AgentErrorBoundary extends Component<Props, State> {
|
|||
<Button
|
||||
onClick={this.handleReload}
|
||||
variant="ghost"
|
||||
className="flex-1 text-gray-400 hover:text-white"
|
||||
className="flex-1 text-muted-foreground hover:text-foreground"
|
||||
>
|
||||
Refresh Page
|
||||
</Button>
|
||||
|
|
@ -273,7 +273,7 @@ export class AgentErrorBoundary extends Component<Props, State> {
|
|||
|
||||
{/* Recovery suggestion */}
|
||||
{!canRetry && (
|
||||
<p className="text-center text-xs text-gray-500">
|
||||
<p className="text-center text-xs text-muted-foreground">
|
||||
Maximum retry attempts reached. Please refresh the page or contact support.
|
||||
</p>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@ import { Badge } from "@/components/ui/badge";
|
|||
import { AGENT_STATUS_CONFIG } from "../constants";
|
||||
import type { AgentTreeNodeItemProps } from "../types";
|
||||
|
||||
// Agent type icons with enhanced colors
|
||||
// Agent type icons with enhanced colors (light/dark mode compatible)
|
||||
const AGENT_TYPE_ICONS: Record<string, React.ReactNode> = {
|
||||
orchestrator: <Cpu className="w-3.5 h-3.5 text-violet-400" />,
|
||||
recon: <Scan className="w-3.5 h-3.5 text-teal-400" />,
|
||||
analysis: <FileSearch className="w-3.5 h-3.5 text-amber-400" />,
|
||||
verification: <ShieldCheck className="w-3.5 h-3.5 text-emerald-400" />,
|
||||
orchestrator: <Cpu className="w-4 h-4 text-violet-600 dark:text-violet-400" />,
|
||||
recon: <Scan className="w-4 h-4 text-teal-600 dark:text-teal-400" />,
|
||||
analysis: <FileSearch className="w-4 h-4 text-amber-600 dark:text-amber-400" />,
|
||||
verification: <ShieldCheck className="w-4 h-4 text-emerald-600 dark:text-emerald-400" />,
|
||||
};
|
||||
|
||||
// Status colors for the glow effect
|
||||
|
|
@ -40,14 +40,14 @@ export const AgentTreeNodeItem = memo(function AgentTreeNodeItem({
|
|||
const isRunning = node.status === 'running';
|
||||
|
||||
const statusConfig = AGENT_STATUS_CONFIG[node.status] || AGENT_STATUS_CONFIG.created;
|
||||
const typeIcon = AGENT_TYPE_ICONS[node.agent_type] || <Bot className="w-3.5 h-3.5 text-slate-400" />;
|
||||
const typeIcon = AGENT_TYPE_ICONS[node.agent_type] || <Bot className="w-3.5 h-3.5 text-muted-foreground" />;
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
{/* Connection line to parent - vertical line */}
|
||||
{depth > 0 && (
|
||||
<div
|
||||
className="absolute top-0 w-px bg-gradient-to-b from-slate-600 to-slate-700"
|
||||
className="absolute top-0 w-px bg-gradient-to-b from-border to-border"
|
||||
style={{
|
||||
left: `${depth * 16 - 8}px`,
|
||||
height: '20px',
|
||||
|
|
@ -58,7 +58,7 @@ export const AgentTreeNodeItem = memo(function AgentTreeNodeItem({
|
|||
{/* Horizontal connector line */}
|
||||
{depth > 0 && (
|
||||
<div
|
||||
className="absolute top-[20px] h-px bg-slate-600"
|
||||
className="absolute top-[20px] h-px bg-muted-foreground"
|
||||
style={{
|
||||
left: `${depth * 16 - 8}px`,
|
||||
width: '8px',
|
||||
|
|
@ -73,7 +73,7 @@ export const AgentTreeNodeItem = memo(function AgentTreeNodeItem({
|
|||
transition-all duration-200 ease-out
|
||||
${isSelected
|
||||
? 'bg-primary/15 border border-primary/40'
|
||||
: 'border border-transparent hover:bg-white/5 hover:border-slate-700/50'
|
||||
: 'border border-transparent hover:bg-white/5 hover:border-border/50'
|
||||
}
|
||||
${STATUS_GLOW_COLORS[node.status] || ''}
|
||||
`}
|
||||
|
|
@ -87,9 +87,9 @@ export const AgentTreeNodeItem = memo(function AgentTreeNodeItem({
|
|||
className="flex-shrink-0 w-5 h-5 flex items-center justify-center rounded hover:bg-white/10 transition-colors"
|
||||
>
|
||||
{expanded ? (
|
||||
<ChevronDown className="w-3.5 h-3.5 text-slate-400" />
|
||||
<ChevronDown className="w-3.5 h-3.5 text-muted-foreground" />
|
||||
) : (
|
||||
<ChevronRight className="w-3.5 h-3.5 text-slate-400" />
|
||||
<ChevronRight className="w-3.5 h-3.5 text-muted-foreground" />
|
||||
)}
|
||||
</button>
|
||||
) : (
|
||||
|
|
@ -104,7 +104,7 @@ export const AgentTreeNodeItem = memo(function AgentTreeNodeItem({
|
|||
${node.status === 'completed' ? 'bg-emerald-500' : ''}
|
||||
${node.status === 'failed' ? 'bg-rose-400' : ''}
|
||||
${node.status === 'waiting' ? 'bg-amber-400' : ''}
|
||||
${node.status === 'created' ? 'bg-slate-500' : ''}
|
||||
${node.status === 'created' ? 'bg-muted' : ''}
|
||||
`} />
|
||||
{isRunning && (
|
||||
<div className="absolute inset-0 w-2.5 h-2.5 rounded-full bg-emerald-400 animate-ping opacity-30" />
|
||||
|
|
@ -118,8 +118,8 @@ export const AgentTreeNodeItem = memo(function AgentTreeNodeItem({
|
|||
|
||||
{/* Agent name */}
|
||||
<span className={`
|
||||
text-xs font-mono truncate flex-1 transition-colors duration-200
|
||||
${isSelected ? 'text-white font-medium' : 'text-slate-300 group-hover:text-white'}
|
||||
text-sm font-mono truncate flex-1 transition-colors duration-200
|
||||
${isSelected ? 'text-foreground font-medium' : 'text-foreground group-hover:text-foreground'}
|
||||
`}>
|
||||
{node.agent_name}
|
||||
</span>
|
||||
|
|
@ -128,14 +128,14 @@ export const AgentTreeNodeItem = memo(function AgentTreeNodeItem({
|
|||
<div className="flex items-center gap-1.5 flex-shrink-0">
|
||||
{/* Iterations */}
|
||||
{(node.iterations ?? 0) > 0 && (
|
||||
<span className="text-[9px] text-slate-400 font-mono bg-slate-800/60 px-1.5 py-0.5 rounded">
|
||||
<span className="text-xs text-muted-foreground font-mono bg-muted px-1.5 py-0.5 rounded">
|
||||
{node.iterations}x
|
||||
</span>
|
||||
)}
|
||||
|
||||
{/* Findings count - Only show for Orchestrator (root agent) */}
|
||||
{!node.parent_agent_id && node.findings_count > 0 && (
|
||||
<Badge className="h-4 px-1.5 text-[9px] bg-rose-500/25 text-rose-300 border border-rose-500/40 font-mono font-semibold">
|
||||
<Badge className="h-5 px-2 text-sm bg-rose-500/25 text-rose-700 dark:text-rose-300 border border-rose-500/40 font-mono font-semibold">
|
||||
{node.findings_count}
|
||||
</Badge>
|
||||
)}
|
||||
|
|
@ -152,7 +152,7 @@ export const AgentTreeNodeItem = memo(function AgentTreeNodeItem({
|
|||
>
|
||||
{/* Vertical connection line for children */}
|
||||
<div
|
||||
className="absolute w-px bg-gradient-to-b from-slate-600 via-slate-700 to-transparent"
|
||||
className="absolute w-px bg-gradient-to-b from-border via-border to-transparent"
|
||||
style={{
|
||||
left: `${(depth + 1) * 16 - 8}px`,
|
||||
top: '0',
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ const STATUS_CONFIG: Record<ConnectionState, {
|
|||
disconnected: {
|
||||
icon: WifiOff,
|
||||
label: 'Disconnected',
|
||||
color: 'text-gray-400',
|
||||
bgColor: 'bg-gray-400/10',
|
||||
color: 'text-muted-foreground',
|
||||
bgColor: 'bg-muted/30',
|
||||
},
|
||||
connecting: {
|
||||
icon: RefreshCw,
|
||||
|
|
@ -67,7 +67,7 @@ export function ConnectionStatus({
|
|||
return (
|
||||
<div className={cn('flex items-center gap-1.5', className)}>
|
||||
<div className={cn(
|
||||
'flex items-center gap-1 px-2 py-0.5 rounded-full text-[10px] font-medium',
|
||||
'flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium',
|
||||
config.bgColor,
|
||||
config.color
|
||||
)}>
|
||||
|
|
|
|||
|
|
@ -18,21 +18,21 @@ export function Header({
|
|||
onNewAudit
|
||||
}: HeaderProps) {
|
||||
return (
|
||||
<header className="flex-shrink-0 h-14 border-b border-gray-800/80 flex items-center justify-between px-5 bg-gradient-to-r from-[#0d0d12] via-[#0e0e14] to-[#0d0d12] relative overflow-hidden">
|
||||
<header className="flex-shrink-0 h-14 border-b border-border flex items-center justify-between px-5 bg-card relative overflow-hidden">
|
||||
{/* Subtle animated line at top */}
|
||||
<div className="absolute top-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-primary/30 to-transparent" />
|
||||
|
||||
{/* Left side - Brand and task info */}
|
||||
<div className="flex items-center gap-4">
|
||||
{/* Logo section */}
|
||||
<div className="flex items-center gap-2.5 pr-4 border-r border-gray-800/50">
|
||||
<div className="flex items-center gap-2.5 pr-4 border-r border-border">
|
||||
<div className="relative">
|
||||
<Cpu className="w-5 h-5 text-primary" />
|
||||
{isRunning && (
|
||||
<span className="absolute -top-0.5 -right-0.5 w-2 h-2 bg-green-400 rounded-full animate-pulse" />
|
||||
)}
|
||||
</div>
|
||||
<span className="font-bold text-white tracking-wide text-sm">
|
||||
<span className="font-bold text-foreground tracking-wide text-sm">
|
||||
DEEP<span className="text-primary">AUDIT</span>
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -40,11 +40,11 @@ export function Header({
|
|||
{/* Task info */}
|
||||
{task && (
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex items-center gap-2 text-gray-400">
|
||||
<div className="flex items-center gap-2 text-muted-foreground">
|
||||
<Radio className="w-3 h-3" />
|
||||
<span className="text-xs font-mono uppercase tracking-wider">Task</span>
|
||||
</div>
|
||||
<span className="text-gray-300 text-sm font-mono truncate max-w-[180px]">
|
||||
<span className="text-foreground text-sm font-mono truncate max-w-[180px]">
|
||||
{task.name || task.id.slice(0, 8)}
|
||||
</span>
|
||||
<StatusBadge status={task.status} />
|
||||
|
|
@ -76,7 +76,7 @@ export function Header({
|
|||
</Button>
|
||||
)}
|
||||
|
||||
<div className="h-6 w-px bg-gray-800/50 mx-1" />
|
||||
<div className="h-6 w-px bg-muted mx-1" />
|
||||
|
||||
<Button
|
||||
variant="ghost"
|
||||
|
|
|
|||
|
|
@ -93,8 +93,8 @@ export const LogEntry = memo(function LogEntry({ item, isExpanded, onToggle }: L
|
|||
<div className={`
|
||||
relative rounded border-l-2 overflow-hidden
|
||||
${config.borderColor}
|
||||
${isExpanded ? 'bg-gray-900/60' : 'bg-gray-900/30'}
|
||||
${isCollapsible ? 'hover:bg-gray-900/50' : ''}
|
||||
${isExpanded ? 'bg-card/60' : 'bg-muted/50'}
|
||||
${isCollapsible ? 'hover:bg-muted' : ''}
|
||||
${isFinding ? 'border-r border-r-red-900/30' : ''}
|
||||
${isError ? 'border-r border-r-red-900/30' : ''}
|
||||
transition-all duration-200
|
||||
|
|
@ -120,35 +120,35 @@ export const LogEntry = memo(function LogEntry({ item, isExpanded, onToggle }: L
|
|||
|
||||
{/* Type label */}
|
||||
<span className={`
|
||||
text-[9px] font-mono font-bold uppercase tracking-wider px-1.5 py-0.5 rounded
|
||||
${isThinking ? 'bg-violet-500/25 text-violet-300' : ''}
|
||||
${isTool ? 'bg-amber-500/25 text-amber-300' : ''}
|
||||
${isFinding ? 'bg-rose-500/25 text-rose-300' : ''}
|
||||
${isError ? 'bg-red-500/25 text-red-300' : ''}
|
||||
${isInfo ? 'bg-slate-500/25 text-slate-300' : ''}
|
||||
${isProgress ? 'bg-cyan-500/25 text-cyan-300' : ''}
|
||||
${item.type === 'dispatch' ? 'bg-sky-500/25 text-sky-300' : ''}
|
||||
${item.type === 'phase' ? 'bg-teal-500/25 text-teal-300' : ''}
|
||||
${item.type === 'user' ? 'bg-indigo-500/25 text-indigo-300' : ''}
|
||||
text-sm font-mono font-bold uppercase tracking-wider px-2.5 py-1 rounded
|
||||
${isThinking ? 'bg-violet-500/30 text-violet-700 dark:text-violet-300' : ''}
|
||||
${isTool ? 'bg-amber-500/30 text-amber-700 dark:text-amber-300' : ''}
|
||||
${isFinding ? 'bg-rose-500/30 text-rose-700 dark:text-rose-300' : ''}
|
||||
${isError ? 'bg-red-500/30 text-red-700 dark:text-red-300' : ''}
|
||||
${isInfo ? 'bg-muted text-foreground' : ''}
|
||||
${isProgress ? 'bg-cyan-500/30 text-cyan-700 dark:text-cyan-300' : ''}
|
||||
${item.type === 'dispatch' ? 'bg-sky-500/30 text-sky-700 dark:text-sky-300' : ''}
|
||||
${item.type === 'phase' ? 'bg-teal-500/30 text-teal-700 dark:text-teal-300' : ''}
|
||||
${item.type === 'user' ? 'bg-indigo-500/30 text-indigo-700 dark:text-indigo-300' : ''}
|
||||
flex-shrink-0
|
||||
`}>
|
||||
{LOG_TYPE_LABELS[item.type] || 'LOG'}
|
||||
</span>
|
||||
|
||||
{/* Timestamp */}
|
||||
<span className="text-[10px] text-gray-600 font-mono flex-shrink-0">
|
||||
<span className="text-xs text-muted-foreground font-mono flex-shrink-0">
|
||||
{item.time}
|
||||
</span>
|
||||
|
||||
{/* Separator */}
|
||||
<ArrowRight className="w-3 h-3 text-gray-700 flex-shrink-0" />
|
||||
<ArrowRight className="w-3 h-3 text-muted-foreground flex-shrink-0" />
|
||||
|
||||
{/* Status icon for info messages */}
|
||||
{statusIcon && <span className="flex-shrink-0">{statusIcon}</span>}
|
||||
|
||||
{/* Title - for non-thinking types */}
|
||||
{!isThinking && (
|
||||
<span className="text-sm text-gray-300 truncate flex-1">
|
||||
<span className="text-base text-foreground font-medium truncate flex-1">
|
||||
{formattedTitle}
|
||||
</span>
|
||||
)}
|
||||
|
|
@ -162,7 +162,7 @@ export const LogEntry = memo(function LogEntry({ item, isExpanded, onToggle }: L
|
|||
{item.tool?.status === 'running' && (
|
||||
<div className="flex items-center gap-1.5 flex-shrink-0 bg-amber-500/10 px-2 py-0.5 rounded">
|
||||
<Loader2 className="w-3 h-3 animate-spin text-amber-400" />
|
||||
<span className="text-[9px] text-amber-400 font-mono uppercase">Running</span>
|
||||
<span className="text-xs text-amber-400 font-mono uppercase">Running</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
@ -176,7 +176,7 @@ export const LogEntry = memo(function LogEntry({ item, isExpanded, onToggle }: L
|
|||
{item.agentName && (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="h-5 px-2 text-[9px] uppercase tracking-wider border-primary/40 text-primary bg-primary/10 flex-shrink-0 font-semibold"
|
||||
className="h-5 px-2 text-xs uppercase tracking-wider border-primary/40 text-primary bg-primary/10 flex-shrink-0 font-semibold"
|
||||
>
|
||||
{item.agentName}
|
||||
</Badge>
|
||||
|
|
@ -186,7 +186,7 @@ export const LogEntry = memo(function LogEntry({ item, isExpanded, onToggle }: L
|
|||
<div className="flex items-center gap-2 flex-shrink-0 ml-auto">
|
||||
{/* Duration badge */}
|
||||
{item.tool?.duration !== undefined && (
|
||||
<span className="text-[10px] text-gray-500 font-mono bg-gray-800/50 px-1.5 py-0.5 rounded">
|
||||
<span className="text-xs text-muted-foreground font-mono bg-muted px-1.5 py-0.5 rounded">
|
||||
{item.tool.duration}ms
|
||||
</span>
|
||||
)}
|
||||
|
|
@ -195,7 +195,7 @@ export const LogEntry = memo(function LogEntry({ item, isExpanded, onToggle }: L
|
|||
{item.severity && (
|
||||
<Badge
|
||||
className={`
|
||||
text-[9px] uppercase tracking-wider font-bold px-1.5 py-0
|
||||
text-xs uppercase tracking-wider font-bold px-1.5 py-0
|
||||
${SEVERITY_COLORS[item.severity] || SEVERITY_COLORS.info}
|
||||
`}
|
||||
>
|
||||
|
|
@ -205,11 +205,11 @@ export const LogEntry = memo(function LogEntry({ item, isExpanded, onToggle }: L
|
|||
|
||||
{/* Expand indicator */}
|
||||
{isCollapsible && (
|
||||
<div className="w-5 h-5 flex items-center justify-center rounded bg-gray-800/30 group-hover:bg-gray-800/50 transition-colors">
|
||||
<div className="w-5 h-5 flex items-center justify-center rounded bg-muted/50 group-hover:bg-muted/50 transition-colors">
|
||||
{isExpanded ? (
|
||||
<ChevronUp className="w-3.5 h-3.5 text-gray-500" />
|
||||
<ChevronUp className="w-3.5 h-3.5 text-muted-foreground" />
|
||||
) : (
|
||||
<ChevronDown className="w-3.5 h-3.5 text-gray-500" />
|
||||
<ChevronDown className="w-3.5 h-3.5 text-muted-foreground" />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -220,7 +220,7 @@ export const LogEntry = memo(function LogEntry({ item, isExpanded, onToggle }: L
|
|||
{isThinking && item.content && (
|
||||
<div className="mt-2.5 relative">
|
||||
<div className="absolute left-0 top-0 bottom-0 w-px bg-gradient-to-b from-purple-500/50 via-purple-500/20 to-transparent" />
|
||||
<div className="pl-3 text-sm text-purple-200/90 leading-relaxed whitespace-pre-wrap break-words">
|
||||
<div className="pl-3 text-base text-foreground/90 leading-relaxed whitespace-pre-wrap break-words">
|
||||
{item.content}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -229,24 +229,24 @@ export const LogEntry = memo(function LogEntry({ item, isExpanded, onToggle }: L
|
|||
{/* Collapsible content */}
|
||||
{!isThinking && showContent && item.content && (
|
||||
<div className="mt-2.5 overflow-hidden">
|
||||
<div className="bg-[#08080c] rounded border border-gray-800/50 overflow-hidden">
|
||||
<div className="bg-card rounded border border-border overflow-hidden">
|
||||
{/* Mini header */}
|
||||
<div className="flex items-center justify-between px-2.5 py-1.5 border-b border-gray-800/50 bg-gray-900/50">
|
||||
<div className="flex items-center justify-between px-2.5 py-1.5 border-b border-border bg-muted">
|
||||
<div className="flex items-center gap-2">
|
||||
<Square className="w-2.5 h-2.5 text-gray-600" />
|
||||
<span className="text-[9px] text-gray-500 font-mono uppercase tracking-wider">
|
||||
<Square className="w-2.5 h-2.5 text-muted-foreground" />
|
||||
<span className="text-xs text-muted-foreground font-mono uppercase tracking-wider">
|
||||
{isTool ? 'Output' : 'Details'}
|
||||
</span>
|
||||
</div>
|
||||
{item.tool?.status === 'completed' && (
|
||||
<div className="flex items-center gap-1">
|
||||
<CheckCircle2 className="w-3 h-3 text-green-500/70" />
|
||||
<span className="text-[9px] text-green-500/70 font-mono">Complete</span>
|
||||
<span className="text-xs text-green-500/70 font-mono">Complete</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* Content */}
|
||||
<pre className="p-3 text-xs font-mono text-gray-400 max-h-56 overflow-y-auto custom-scrollbar whitespace-pre-wrap break-words leading-relaxed">
|
||||
<pre className="p-3 text-base font-mono text-foreground/80 max-h-56 overflow-y-auto custom-scrollbar whitespace-pre-wrap break-words leading-relaxed">
|
||||
{item.content}
|
||||
</pre>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ function getSeverityColor(severity: string): string {
|
|||
high: "text-orange-400",
|
||||
medium: "text-amber-400",
|
||||
low: "text-sky-400",
|
||||
info: "text-slate-400",
|
||||
info: "text-muted-foreground",
|
||||
};
|
||||
return colors[severity.toLowerCase()] || colors.info;
|
||||
}
|
||||
|
|
@ -181,7 +181,7 @@ const CircularProgress = memo(function CircularProgress({
|
|||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth={strokeWidth}
|
||||
className="text-slate-800/50"
|
||||
className="text-foreground/50"
|
||||
/>
|
||||
{/* Progress circle */}
|
||||
<circle
|
||||
|
|
@ -200,7 +200,7 @@ const CircularProgress = memo(function CircularProgress({
|
|||
<span className={`text-xl font-bold font-mono ${colors.text}`}>
|
||||
{value.toFixed(0)}
|
||||
</span>
|
||||
<span className="text-[8px] text-slate-500 uppercase tracking-wider">分</span>
|
||||
<span className="text-[8px] text-muted-foreground uppercase tracking-wider">分</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -223,7 +223,7 @@ const EnhancedStatsPanel = memo(function EnhancedStatsPanel({
|
|||
icon: <Bug className="w-4 h-4" />,
|
||||
label: "漏洞总数",
|
||||
value: totalFindings,
|
||||
color: "text-white",
|
||||
color: "text-foreground",
|
||||
iconColor: "text-rose-400",
|
||||
trend: totalFindings > 0 ? "up" : null,
|
||||
},
|
||||
|
|
@ -231,7 +231,7 @@ const EnhancedStatsPanel = memo(function EnhancedStatsPanel({
|
|||
icon: <AlertTriangle className="w-4 h-4" />,
|
||||
label: "高危问题",
|
||||
value: criticalAndHigh,
|
||||
color: criticalAndHigh > 0 ? "text-rose-400" : "text-slate-400",
|
||||
color: criticalAndHigh > 0 ? "text-rose-400" : "text-muted-foreground",
|
||||
iconColor: "text-orange-400",
|
||||
trend: criticalAndHigh > 0 ? "critical" : null,
|
||||
},
|
||||
|
|
@ -248,7 +248,7 @@ const EnhancedStatsPanel = memo(function EnhancedStatsPanel({
|
|||
return (
|
||||
<div className="flex items-stretch gap-4">
|
||||
{/* 环形安全评分 */}
|
||||
<div className="flex items-center justify-center p-3 rounded-xl bg-gradient-to-br from-slate-800/60 to-slate-900/60 border border-slate-700/40 backdrop-blur-sm">
|
||||
<div className="flex items-center justify-center p-3 rounded-xl bg-gradient-to-br from-muted to-background border border-border backdrop-blur-sm">
|
||||
<CircularProgress value={score} size={72} strokeWidth={5} />
|
||||
</div>
|
||||
|
||||
|
|
@ -257,13 +257,13 @@ const EnhancedStatsPanel = memo(function EnhancedStatsPanel({
|
|||
{stats.map((stat, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="relative p-3 rounded-xl bg-gradient-to-br from-slate-800/40 to-slate-900/40 border border-slate-700/30 backdrop-blur-sm group hover:border-slate-600/50 transition-all duration-300"
|
||||
className="relative p-3 rounded-xl bg-gradient-to-br from-muted/40 to-background/40 border border-border backdrop-blur-sm group hover:border-border transition-all duration-300"
|
||||
>
|
||||
<div className="flex items-center gap-2 mb-1.5">
|
||||
<div className={`${stat.iconColor} opacity-80`}>
|
||||
{stat.icon}
|
||||
</div>
|
||||
<span className="text-[10px] text-slate-500 uppercase tracking-wider font-medium">
|
||||
<span className="text-xs text-muted-foreground uppercase tracking-wider font-medium">
|
||||
{stat.label}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -307,25 +307,25 @@ const FormatSelector = memo(function FormatSelector({
|
|||
relative p-4 rounded-xl border transition-all duration-300 text-left group
|
||||
${isActive
|
||||
? `${config.bgColor} border-opacity-100 shadow-lg`
|
||||
: "bg-slate-800/30 border-slate-700/30 hover:border-slate-600/50 hover:bg-slate-800/50"
|
||||
: "bg-muted border-border hover:border-border hover:bg-muted"
|
||||
}
|
||||
`}
|
||||
>
|
||||
{/* 选中指示器 */}
|
||||
{isActive && (
|
||||
<div className="absolute -top-1 -right-1 w-5 h-5 rounded-full bg-primary flex items-center justify-center shadow-lg shadow-primary/30">
|
||||
<Check className="w-3 h-3 text-white" />
|
||||
<Check className="w-3 h-3 text-foreground" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={`mb-2 ${isActive ? config.color : "text-slate-400 group-hover:text-slate-300"}`}>
|
||||
<div className={`mb-2 ${isActive ? config.color : "text-muted-foreground group-hover:text-foreground"}`}>
|
||||
{config.icon}
|
||||
</div>
|
||||
|
||||
<div className={`text-sm font-semibold mb-0.5 ${isActive ? "text-white" : "text-slate-300"}`}>
|
||||
<div className={`text-sm font-semibold mb-0.5 ${isActive ? "text-foreground" : "text-foreground"}`}>
|
||||
{config.label}
|
||||
</div>
|
||||
<div className="text-[10px] text-slate-500">
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{config.description}
|
||||
</div>
|
||||
|
||||
|
|
@ -363,19 +363,19 @@ const ExportOptionsPanel = memo(function ExportOptionsPanel({
|
|||
];
|
||||
|
||||
return (
|
||||
<div className="rounded-xl border border-slate-700/40 bg-slate-800/20 overflow-hidden">
|
||||
<div className="rounded-xl border border-border bg-muted/50 overflow-hidden">
|
||||
<button
|
||||
onClick={onToggle}
|
||||
className="w-full flex items-center justify-between p-3 hover:bg-slate-700/20 transition-colors"
|
||||
className="w-full flex items-center justify-between p-3 hover:bg-muted/20 transition-colors"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<Settings2 className="w-4 h-4 text-slate-400" />
|
||||
<span className="text-sm font-medium text-slate-300">导出选项</span>
|
||||
<Settings2 className="w-4 h-4 text-muted-foreground" />
|
||||
<span className="text-sm font-medium text-foreground">导出选项</span>
|
||||
</div>
|
||||
{expanded ? (
|
||||
<ChevronUp className="w-4 h-4 text-slate-500" />
|
||||
<ChevronUp className="w-4 h-4 text-muted-foreground" />
|
||||
) : (
|
||||
<ChevronDown className="w-4 h-4 text-slate-500" />
|
||||
<ChevronDown className="w-4 h-4 text-muted-foreground" />
|
||||
)}
|
||||
</button>
|
||||
|
||||
|
|
@ -390,11 +390,11 @@ const ExportOptionsPanel = memo(function ExportOptionsPanel({
|
|||
{optionItems.map((item) => (
|
||||
<label
|
||||
key={item.key}
|
||||
className="flex items-center justify-between p-2 rounded-lg hover:bg-slate-700/20 cursor-pointer transition-colors"
|
||||
className="flex items-center justify-between p-2 rounded-lg hover:bg-muted/20 cursor-pointer transition-colors"
|
||||
>
|
||||
<div className="flex-1">
|
||||
<div className="text-xs font-medium text-slate-300">{item.label}</div>
|
||||
<div className="text-[10px] text-slate-500">{item.description}</div>
|
||||
<div className="text-xs font-medium text-foreground">{item.label}</div>
|
||||
<div className="text-xs text-muted-foreground">{item.description}</div>
|
||||
</div>
|
||||
<Switch
|
||||
checked={options[item.key as keyof ExportOptions]}
|
||||
|
|
@ -425,23 +425,23 @@ const PreviewSearchBar = memo(function PreviewSearchBar({
|
|||
onClear: () => void;
|
||||
}) {
|
||||
return (
|
||||
<div className="flex items-center gap-2 px-3 py-2 bg-slate-800/30 border-b border-slate-700/30">
|
||||
<Search className="w-3.5 h-3.5 text-slate-500" />
|
||||
<div className="flex items-center gap-2 px-3 py-2 bg-muted border-b border-border">
|
||||
<Search className="w-3.5 h-3.5 text-muted-foreground" />
|
||||
<input
|
||||
type="text"
|
||||
value={searchQuery}
|
||||
onChange={(e) => onSearchChange(e.target.value)}
|
||||
placeholder="搜索预览内容..."
|
||||
className="flex-1 bg-transparent text-xs text-slate-300 placeholder:text-slate-600 outline-none"
|
||||
className="flex-1 bg-transparent text-xs text-foreground placeholder:text-muted-foreground outline-none"
|
||||
/>
|
||||
{searchQuery && (
|
||||
<>
|
||||
<span className="text-[10px] text-slate-500 font-mono">
|
||||
<span className="text-xs text-muted-foreground font-mono">
|
||||
{matchCount} 匹配
|
||||
</span>
|
||||
<button
|
||||
onClick={onClear}
|
||||
className="p-1 rounded hover:bg-slate-700/50 text-slate-500 hover:text-slate-300 transition-colors"
|
||||
className="p-1 rounded hover:bg-muted/50 text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
<X className="w-3 h-3" />
|
||||
</button>
|
||||
|
|
@ -455,18 +455,18 @@ const PreviewSearchBar = memo(function PreviewSearchBar({
|
|||
const PreviewSkeleton = memo(function PreviewSkeleton() {
|
||||
return (
|
||||
<div className="space-y-4 animate-pulse">
|
||||
<div className="h-6 bg-slate-700/30 rounded w-3/4" />
|
||||
<div className="h-6 bg-muted/30 rounded w-3/4" />
|
||||
<div className="space-y-2">
|
||||
<div className="h-4 bg-slate-700/20 rounded w-full" />
|
||||
<div className="h-4 bg-slate-700/20 rounded w-5/6" />
|
||||
<div className="h-4 bg-slate-700/20 rounded w-4/6" />
|
||||
<div className="h-4 bg-muted/20 rounded w-full" />
|
||||
<div className="h-4 bg-muted/20 rounded w-5/6" />
|
||||
<div className="h-4 bg-muted/20 rounded w-4/6" />
|
||||
</div>
|
||||
<div className="h-20 bg-slate-700/20 rounded" />
|
||||
<div className="h-20 bg-muted/20 rounded" />
|
||||
<div className="space-y-2">
|
||||
<div className="h-4 bg-slate-700/20 rounded w-full" />
|
||||
<div className="h-4 bg-slate-700/20 rounded w-3/4" />
|
||||
<div className="h-4 bg-muted/20 rounded w-full" />
|
||||
<div className="h-4 bg-muted/20 rounded w-3/4" />
|
||||
</div>
|
||||
<div className="h-16 bg-slate-700/20 rounded" />
|
||||
<div className="h-16 bg-muted/20 rounded" />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
@ -486,7 +486,7 @@ const MarkdownPreview = memo(function MarkdownPreview({
|
|||
const parts = text.split(regex);
|
||||
return parts.map((part, i) =>
|
||||
regex.test(part) ? (
|
||||
<mark key={i} className="bg-primary/40 text-white px-0.5 rounded">{part}</mark>
|
||||
<mark key={i} className="bg-primary/40 text-foreground px-0.5 rounded">{part}</mark>
|
||||
) : (
|
||||
part
|
||||
)
|
||||
|
|
@ -506,30 +506,30 @@ const MarkdownPreview = memo(function MarkdownPreview({
|
|||
if (line.startsWith("```")) {
|
||||
if (inCodeBlock) {
|
||||
elements.push(
|
||||
<div key={`code-${index}`} className="my-4 rounded-xl bg-[#0d1117] border border-slate-700/50 overflow-hidden shadow-lg">
|
||||
<div className="flex items-center justify-between px-4 py-2 bg-gradient-to-r from-slate-800/80 to-slate-800/40 border-b border-slate-700/50">
|
||||
<div key={`code-${index}`} className="my-4 rounded-xl bg-card border border-border/50 overflow-hidden shadow-lg">
|
||||
<div className="flex items-center justify-between px-4 py-2 bg-gradient-to-r from-muted to-muted/40 border-b border-border/50">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex gap-1.5">
|
||||
<div className="w-2.5 h-2.5 rounded-full bg-rose-500/80" />
|
||||
<div className="w-2.5 h-2.5 rounded-full bg-amber-500/80" />
|
||||
<div className="w-2.5 h-2.5 rounded-full bg-emerald-500/80" />
|
||||
</div>
|
||||
<span className="text-[10px] text-slate-400 uppercase tracking-wider font-mono ml-2">
|
||||
<span className="text-xs text-muted-foreground uppercase tracking-wider font-mono ml-2">
|
||||
{codeLanguage || "code"}
|
||||
</span>
|
||||
</div>
|
||||
<Terminal className="w-3.5 h-3.5 text-slate-500" />
|
||||
<Terminal className="w-3.5 h-3.5 text-muted-foreground" />
|
||||
</div>
|
||||
<div className="relative">
|
||||
{/* 行号 */}
|
||||
<div className="absolute left-0 top-0 bottom-0 w-10 bg-slate-900/50 border-r border-slate-700/30 select-none">
|
||||
<div className="p-3 text-[10px] font-mono text-slate-600 leading-5">
|
||||
<div className="absolute left-0 top-0 bottom-0 w-10 bg-background border-r border-border select-none">
|
||||
<div className="p-3 text-xs font-mono text-muted-foreground leading-5">
|
||||
{codeContent.map((_, i) => (
|
||||
<div key={i}>{i + 1}</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<pre className="p-3 pl-14 text-xs font-mono text-slate-300 overflow-x-auto leading-5">
|
||||
<pre className="p-3 pl-14 text-xs font-mono text-foreground overflow-x-auto leading-5">
|
||||
{codeContent.map((codeLine, i) => (
|
||||
<div key={i}>{highlightText(codeLine, searchQuery) || " "}</div>
|
||||
))}
|
||||
|
|
@ -555,7 +555,7 @@ const MarkdownPreview = memo(function MarkdownPreview({
|
|||
// Headers with decorative elements
|
||||
if (line.startsWith("# ")) {
|
||||
elements.push(
|
||||
<h1 key={index} className="text-xl font-bold text-white mt-8 mb-4 pb-3 border-b border-slate-700/50 flex items-center gap-3">
|
||||
<h1 key={index} className="text-xl font-bold text-foreground mt-8 mb-4 pb-3 border-b border-border/50 flex items-center gap-3">
|
||||
<span className="w-1 h-6 bg-primary rounded-full" />
|
||||
{highlightText(line.slice(2), searchQuery)}
|
||||
</h1>
|
||||
|
|
@ -564,7 +564,7 @@ const MarkdownPreview = memo(function MarkdownPreview({
|
|||
}
|
||||
if (line.startsWith("## ")) {
|
||||
elements.push(
|
||||
<h2 key={index} className="text-lg font-bold text-white mt-6 mb-3 flex items-center gap-2">
|
||||
<h2 key={index} className="text-lg font-bold text-foreground mt-6 mb-3 flex items-center gap-2">
|
||||
<Sparkles className="w-4 h-4 text-primary/60" />
|
||||
{highlightText(line.slice(3), searchQuery)}
|
||||
</h2>
|
||||
|
|
@ -573,7 +573,7 @@ const MarkdownPreview = memo(function MarkdownPreview({
|
|||
}
|
||||
if (line.startsWith("### ")) {
|
||||
elements.push(
|
||||
<h3 key={index} className="text-base font-semibold text-slate-200 mt-5 mb-2 pl-2 border-l-2 border-slate-600">
|
||||
<h3 key={index} className="text-base font-semibold text-foreground mt-5 mb-2 pl-2 border-l-2 border-border">
|
||||
{highlightText(line.slice(4), searchQuery)}
|
||||
</h3>
|
||||
);
|
||||
|
|
@ -584,9 +584,9 @@ const MarkdownPreview = memo(function MarkdownPreview({
|
|||
if (line.match(/^---+$/)) {
|
||||
elements.push(
|
||||
<div key={index} className="my-6 flex items-center gap-3">
|
||||
<div className="flex-1 h-px bg-gradient-to-r from-transparent via-slate-700 to-transparent" />
|
||||
<div className="w-1.5 h-1.5 rounded-full bg-slate-700" />
|
||||
<div className="flex-1 h-px bg-gradient-to-r from-transparent via-slate-700 to-transparent" />
|
||||
<div className="flex-1 h-px bg-gradient-to-r from-transparent via-border to-transparent" />
|
||||
<div className="w-1.5 h-1.5 rounded-full bg-muted" />
|
||||
<div className="flex-1 h-px bg-gradient-to-r from-transparent via-border to-transparent" />
|
||||
</div>
|
||||
);
|
||||
return;
|
||||
|
|
@ -595,7 +595,7 @@ const MarkdownPreview = memo(function MarkdownPreview({
|
|||
// List items with better styling
|
||||
if (line.match(/^[-*]\s/)) {
|
||||
elements.push(
|
||||
<div key={index} className="flex gap-3 text-sm text-slate-300 ml-3 my-1 group">
|
||||
<div key={index} className="flex gap-3 text-sm text-foreground ml-3 my-1 group">
|
||||
<span className="text-primary mt-1.5 text-xs group-hover:scale-125 transition-transform">●</span>
|
||||
<span className="flex-1">{highlightText(line.slice(2), searchQuery)}</span>
|
||||
</div>
|
||||
|
|
@ -608,12 +608,12 @@ const MarkdownPreview = memo(function MarkdownPreview({
|
|||
const parts = line.split(/\*\*(.+?)\*\*/g);
|
||||
const lineElements = parts.map((part, i) => {
|
||||
if (i % 2 === 1) {
|
||||
return <strong key={i} className="text-white font-semibold">{highlightText(part, searchQuery)}</strong>;
|
||||
return <strong key={i} className="text-foreground font-semibold">{highlightText(part, searchQuery)}</strong>;
|
||||
}
|
||||
return highlightText(part, searchQuery);
|
||||
});
|
||||
elements.push(
|
||||
<p key={index} className="text-sm text-slate-300 my-1.5 leading-relaxed">
|
||||
<p key={index} className="text-sm text-foreground my-1.5 leading-relaxed">
|
||||
{lineElements}
|
||||
</p>
|
||||
);
|
||||
|
|
@ -628,7 +628,7 @@ const MarkdownPreview = memo(function MarkdownPreview({
|
|||
|
||||
// Regular paragraphs
|
||||
elements.push(
|
||||
<p key={index} className="text-sm text-slate-300 my-1.5 leading-relaxed">
|
||||
<p key={index} className="text-sm text-foreground my-1.5 leading-relaxed">
|
||||
{highlightText(line, searchQuery)}
|
||||
</p>
|
||||
);
|
||||
|
|
@ -663,11 +663,11 @@ const JsonPreview = memo(function JsonPreview({
|
|||
.replace(/: "([^"]+)"/g, ': <span class="text-emerald-400">"$1"</span>')
|
||||
.replace(/: (\d+\.?\d*)/g, ': <span class="text-amber-400">$1</span>')
|
||||
.replace(/: (true|false)/g, ': <span class="text-sky-400">$1</span>')
|
||||
.replace(/: (null)/g, ': <span class="text-slate-500">$1</span>');
|
||||
.replace(/: (null)/g, ': <span class="text-muted-foreground">$1</span>');
|
||||
|
||||
if (searchQuery) {
|
||||
const regex = new RegExp(`(${searchQuery.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
|
||||
result = result.replace(regex, '<mark class="bg-primary/40 text-white px-0.5 rounded">$1</mark>');
|
||||
result = result.replace(regex, '<mark class="bg-primary/40 text-foreground px-0.5 rounded">$1</mark>');
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
@ -681,15 +681,15 @@ const JsonPreview = memo(function JsonPreview({
|
|||
return (
|
||||
<div className="relative">
|
||||
{/* 行号区域 */}
|
||||
<div className="absolute left-0 top-0 bottom-0 w-10 bg-slate-900/30 border-r border-slate-700/30 select-none">
|
||||
<div className="py-3 text-[10px] font-mono text-slate-600 text-right pr-2 leading-5">
|
||||
<div className="absolute left-0 top-0 bottom-0 w-10 bg-background border-r border-border select-none">
|
||||
<div className="py-3 text-xs font-mono text-muted-foreground text-right pr-2 leading-5">
|
||||
{lines.map((_, i) => (
|
||||
<div key={i}>{i + 1}</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<pre
|
||||
className="text-xs font-mono text-slate-300 whitespace-pre-wrap pl-14 py-3 leading-5"
|
||||
className="text-xs font-mono text-foreground whitespace-pre-wrap pl-14 py-3 leading-5"
|
||||
dangerouslySetInnerHTML={{ __html: highlightJson(content) }}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -716,11 +716,11 @@ const HtmlPreview = memo(function HtmlPreview({
|
|||
.replace(/(<\/?[a-zA-Z][a-zA-Z0-9]*)/g, '<span class="text-rose-400">$1</span>')
|
||||
.replace(/(\s[a-zA-Z-]+)=/g, '<span class="text-amber-400">$1</span>=')
|
||||
.replace(/"([^"]*)"/g, '"<span class="text-emerald-400">$1</span>"')
|
||||
.replace(/(<!DOCTYPE[^&]*>)/gi, '<span class="text-slate-500">$1</span>');
|
||||
.replace(/(<!DOCTYPE[^&]*>)/gi, '<span class="text-muted-foreground">$1</span>');
|
||||
|
||||
if (searchQuery) {
|
||||
const regex = new RegExp(`(${searchQuery.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
|
||||
result = result.replace(regex, '<mark class="bg-primary/40 text-white px-0.5 rounded">$1</mark>');
|
||||
result = result.replace(regex, '<mark class="bg-primary/40 text-foreground px-0.5 rounded">$1</mark>');
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
@ -729,12 +729,12 @@ const HtmlPreview = memo(function HtmlPreview({
|
|||
return (
|
||||
<div className="relative">
|
||||
<pre
|
||||
className="text-xs font-mono text-slate-400 whitespace-pre-wrap leading-5"
|
||||
className="text-xs font-mono text-muted-foreground whitespace-pre-wrap leading-5"
|
||||
dangerouslySetInnerHTML={{ __html: highlightHtml(truncatedContent) }}
|
||||
/>
|
||||
{isTruncated && (
|
||||
<div className="mt-4 pt-4 border-t border-slate-700/30 text-center">
|
||||
<span className="text-xs text-slate-500 bg-slate-800/50 px-3 py-1.5 rounded-full">
|
||||
<div className="mt-4 pt-4 border-t border-border text-center">
|
||||
<span className="text-xs text-muted-foreground bg-muted px-3 py-1.5 rounded-full">
|
||||
已截断显示,完整内容请下载查看
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -1622,9 +1622,9 @@ export const ReportExportDialog = memo(function ReportExportDialog({
|
|||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="max-w-5xl h-[90vh] bg-gradient-to-b from-[#0a0a0f] to-[#0d0d14] border-slate-700/50 p-0 gap-0 overflow-hidden shadow-2xl shadow-black/50">
|
||||
<DialogContent className="max-w-5xl h-[90vh] bg-gradient-to-b from-[#0a0a0f] to-[#0d0d14] border-border/50 p-0 gap-0 overflow-hidden shadow-2xl shadow-black/50">
|
||||
{/* Header - 增强设计 */}
|
||||
<div className="relative px-6 py-5 border-b border-slate-700/50 bg-gradient-to-r from-[#0d0d12] via-[#0f0f16] to-[#0d0d12]">
|
||||
<div className="relative px-6 py-5 border-b border-border/50 bg-gradient-to-r from-[#0d0d12] via-[#0f0f16] to-[#0d0d12]">
|
||||
{/* 装饰性背景元素 */}
|
||||
<div className="absolute inset-0 overflow-hidden">
|
||||
<div className="absolute -top-20 -right-20 w-40 h-40 bg-primary/5 rounded-full blur-3xl" />
|
||||
|
|
@ -1636,14 +1636,14 @@ export const ReportExportDialog = memo(function ReportExportDialog({
|
|||
<div className="flex items-center gap-4">
|
||||
<div className="relative p-3 rounded-xl bg-gradient-to-br from-primary/20 to-primary/5 border border-primary/30 shadow-lg shadow-primary/10">
|
||||
<FileDown className="w-6 h-6 text-primary" />
|
||||
<div className="absolute -top-1 -right-1 w-3 h-3 rounded-full bg-emerald-500 border-2 border-[#0a0a0f] animate-pulse" />
|
||||
<div className="absolute -top-1 -right-1 w-3 h-3 rounded-full bg-emerald-500 border-2 border-background animate-pulse" />
|
||||
</div>
|
||||
<div>
|
||||
<DialogTitle className="text-xl font-bold text-white flex items-center gap-2">
|
||||
<DialogTitle className="text-xl font-bold text-foreground flex items-center gap-2">
|
||||
导出审计报告
|
||||
<Sparkles className="w-4 h-4 text-primary/60" />
|
||||
</DialogTitle>
|
||||
<p className="text-xs text-slate-500 mt-1 font-mono flex items-center gap-2">
|
||||
<p className="text-xs text-muted-foreground mt-1 font-mono flex items-center gap-2">
|
||||
<Clock className="w-3 h-3" />
|
||||
{task.name || `Task ${task.id.slice(0, 8)}`}
|
||||
</p>
|
||||
|
|
@ -1651,12 +1651,12 @@ export const ReportExportDialog = memo(function ReportExportDialog({
|
|||
</div>
|
||||
|
||||
{/* 快捷键提示 */}
|
||||
<div className="hidden md:flex items-center gap-2 text-[10px] text-slate-600">
|
||||
<div className="flex items-center gap-1 px-2 py-1 rounded bg-slate-800/50 border border-slate-700/30">
|
||||
<div className="hidden md:flex items-center gap-2 text-xs text-muted-foreground">
|
||||
<div className="flex items-center gap-1 px-2 py-1 rounded bg-muted border border-border">
|
||||
<Keyboard className="w-3 h-3" />
|
||||
<span>⌘S 下载</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1 px-2 py-1 rounded bg-slate-800/50 border border-slate-700/30">
|
||||
<div className="flex items-center gap-1 px-2 py-1 rounded bg-muted border border-border">
|
||||
<span>1-3 切换</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1667,17 +1667,17 @@ export const ReportExportDialog = memo(function ReportExportDialog({
|
|||
{/* 主体内容区域 */}
|
||||
<div className="flex-1 flex flex-col overflow-hidden">
|
||||
{/* Stats Summary - 增强统计卡片 */}
|
||||
<div className="px-6 py-4 border-b border-slate-700/30 bg-[#0b0b10]/80">
|
||||
<div className="px-6 py-4 border-b border-border bg-card/80">
|
||||
<EnhancedStatsPanel task={task} findings={findings} />
|
||||
</div>
|
||||
|
||||
{/* 两栏布局:左侧配置,右侧预览 */}
|
||||
<div className="flex-1 flex min-h-0">
|
||||
{/* 左侧:格式选择和配置 */}
|
||||
<div className="w-72 flex-shrink-0 border-r border-slate-700/30 bg-[#0a0a0e]/50 p-4 space-y-4 overflow-y-auto">
|
||||
<div className="w-72 flex-shrink-0 border-r border-border bg-card/50 p-4 space-y-4 overflow-y-auto">
|
||||
{/* 格式选择 */}
|
||||
<div>
|
||||
<h3 className="text-xs font-semibold text-slate-400 uppercase tracking-wider mb-3 flex items-center gap-2">
|
||||
<h3 className="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-3 flex items-center gap-2">
|
||||
<FileText className="w-3.5 h-3.5" />
|
||||
选择格式
|
||||
</h3>
|
||||
|
|
@ -1696,16 +1696,16 @@ export const ReportExportDialog = memo(function ReportExportDialog({
|
|||
/>
|
||||
|
||||
{/* 格式信息 */}
|
||||
<div className="p-3 rounded-xl bg-slate-800/30 border border-slate-700/30">
|
||||
<div className="p-3 rounded-xl bg-muted border border-border">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className={FORMAT_CONFIG[activeFormat].color}>
|
||||
{FORMAT_CONFIG[activeFormat].icon}
|
||||
</div>
|
||||
<span className="text-sm font-medium text-white">
|
||||
<span className="text-sm font-medium text-foreground">
|
||||
{FORMAT_CONFIG[activeFormat].label}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-[11px] text-slate-500 leading-relaxed">
|
||||
<p className="text-xs text-muted-foreground leading-relaxed">
|
||||
{activeFormat === "markdown" && "Markdown格式便于编辑和版本控制,可用任何文本编辑器打开。"}
|
||||
{activeFormat === "json" && "JSON格式包含完整的结构化数据,适合程序处理和数据分析。"}
|
||||
{activeFormat === "html" && "HTML格式可直接在浏览器中查看,包含完整样式和布局。"}
|
||||
|
|
@ -1714,32 +1714,32 @@ export const ReportExportDialog = memo(function ReportExportDialog({
|
|||
</div>
|
||||
|
||||
{/* 右侧:预览区域 */}
|
||||
<div className="flex-1 flex flex-col min-h-0 bg-[#0d0d12]">
|
||||
<div className="flex-1 flex flex-col min-h-0 cyber-dialog">
|
||||
{/* 预览工具栏 */}
|
||||
<div className="flex-shrink-0 flex items-center justify-between px-4 py-2.5 border-b border-slate-700/30 bg-slate-800/20">
|
||||
<div className="flex-shrink-0 flex items-center justify-between px-4 py-2.5 border-b border-border bg-muted/50">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<Eye className="w-4 h-4 text-slate-500" />
|
||||
<span className="text-xs text-slate-400 font-medium">预览</span>
|
||||
<Eye className="w-4 h-4 text-muted-foreground" />
|
||||
<span className="text-xs text-muted-foreground font-medium">预览</span>
|
||||
</div>
|
||||
<Badge className="text-[10px] bg-slate-700/50 text-slate-400 border-0 font-mono">
|
||||
<Badge className="text-xs bg-muted/50 text-muted-foreground border-0 font-mono">
|
||||
{formatBytes(preview.content.length)}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
{/* 搜索框 */}
|
||||
<div className="flex items-center gap-2 px-2.5 py-1.5 rounded-lg bg-slate-800/50 border border-slate-700/30">
|
||||
<Search className="w-3.5 h-3.5 text-slate-500" />
|
||||
<div className="flex items-center gap-2 px-2.5 py-1.5 rounded-lg bg-muted border border-border">
|
||||
<Search className="w-3.5 h-3.5 text-muted-foreground" />
|
||||
<input
|
||||
type="text"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
placeholder="搜索..."
|
||||
className="w-24 bg-transparent text-xs text-slate-300 placeholder:text-slate-600 outline-none"
|
||||
className="w-24 bg-transparent text-xs text-foreground placeholder:text-muted-foreground outline-none"
|
||||
/>
|
||||
{searchQuery && (
|
||||
<span className="text-[10px] text-slate-500 font-mono">
|
||||
<span className="text-xs text-muted-foreground font-mono">
|
||||
{searchMatchCount}
|
||||
</span>
|
||||
)}
|
||||
|
|
@ -1751,7 +1751,7 @@ export const ReportExportDialog = memo(function ReportExportDialog({
|
|||
size="sm"
|
||||
onClick={handleCopy}
|
||||
disabled={preview.loading || !preview.content}
|
||||
className="h-8 px-2.5 text-xs text-slate-400 hover:text-white hover:bg-slate-700/50"
|
||||
className="h-8 px-2.5 text-xs text-muted-foreground hover:text-foreground hover:bg-muted/50"
|
||||
>
|
||||
{copied ? (
|
||||
<Check className="w-3.5 h-3.5 mr-1.5 text-emerald-400" />
|
||||
|
|
@ -1766,7 +1766,7 @@ export const ReportExportDialog = memo(function ReportExportDialog({
|
|||
size="sm"
|
||||
onClick={() => fetchPreview(activeFormat, true)}
|
||||
disabled={preview.loading}
|
||||
className="h-8 px-2.5 text-xs text-slate-400 hover:text-white hover:bg-slate-700/50"
|
||||
className="h-8 px-2.5 text-xs text-muted-foreground hover:text-foreground hover:bg-muted/50"
|
||||
>
|
||||
<RefreshCw className={`w-3.5 h-3.5 mr-1.5 ${preview.loading ? 'animate-spin' : ''}`} />
|
||||
刷新
|
||||
|
|
@ -1787,8 +1787,8 @@ export const ReportExportDialog = memo(function ReportExportDialog({
|
|||
<AlertTriangle className="w-8 h-8 text-amber-400" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-slate-300 font-medium mb-1">加载失败</p>
|
||||
<p className="text-xs text-slate-500">{preview.error}</p>
|
||||
<p className="text-sm text-foreground font-medium mb-1">加载失败</p>
|
||||
<p className="text-xs text-muted-foreground">{preview.error}</p>
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
|
|
@ -1802,7 +1802,7 @@ export const ReportExportDialog = memo(function ReportExportDialog({
|
|||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="rounded-xl border border-slate-700/40 overflow-hidden bg-[#0a0a0e]">
|
||||
<div className="rounded-xl border border-border overflow-hidden bg-card">
|
||||
<div className="p-5 min-h-[300px]">
|
||||
{activeFormat === "markdown" && (
|
||||
<MarkdownPreview content={preview.content} searchQuery={searchQuery} />
|
||||
|
|
@ -1824,9 +1824,9 @@ export const ReportExportDialog = memo(function ReportExportDialog({
|
|||
</div>
|
||||
|
||||
{/* Footer - 增强设计 */}
|
||||
<div className="px-6 py-4 border-t border-slate-700/50 bg-gradient-to-r from-[#0d0d12] via-[#0f0f16] to-[#0d0d12]">
|
||||
<div className="px-6 py-4 border-t border-border/50 bg-gradient-to-r from-[#0d0d12] via-[#0f0f16] to-[#0d0d12]">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3 text-xs text-slate-500">
|
||||
<div className="flex items-center gap-3 text-xs text-muted-foreground">
|
||||
<div className={`flex items-center gap-2 px-3 py-1.5 rounded-lg border ${FORMAT_CONFIG[activeFormat].bgColor}`}>
|
||||
<span className={FORMAT_CONFIG[activeFormat].color}>
|
||||
{FORMAT_CONFIG[activeFormat].icon}
|
||||
|
|
@ -1841,7 +1841,7 @@ export const ReportExportDialog = memo(function ReportExportDialog({
|
|||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => onOpenChange(false)}
|
||||
className="h-10 px-5 text-sm text-slate-400 hover:text-white"
|
||||
className="h-10 px-5 text-sm text-muted-foreground hover:text-foreground"
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ export function SplashScreen({ onComplete }: SplashScreenProps) {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="h-screen bg-[#0a0a0f] flex flex-col overflow-hidden relative">
|
||||
<div className="h-screen cyber-bg-elevated flex flex-col overflow-hidden relative">
|
||||
{/* Scanline overlay */}
|
||||
<div className="absolute inset-0 pointer-events-none z-20">
|
||||
<div
|
||||
|
|
@ -192,26 +192,26 @@ export function SplashScreen({ onComplete }: SplashScreenProps) {
|
|||
style={{ textShadow: "0 0 30px rgba(255,107,44,0.5), 0 0 60px rgba(255,107,44,0.3)" }}
|
||||
>
|
||||
<span className="text-primary">DEEP</span>
|
||||
<span className="text-white">AUDIT</span>
|
||||
<span className="text-foreground">AUDIT</span>
|
||||
</div>
|
||||
<div className="text-gray-500 text-xs sm:text-sm tracking-[0.3em] uppercase">
|
||||
<div className="text-muted-foreground text-xs sm:text-sm tracking-[0.3em] uppercase">
|
||||
Autonomous Security Agent
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Terminal window */}
|
||||
<div
|
||||
className="bg-[#0c0c12] border border-gray-800/60 rounded-lg overflow-hidden shadow-2xl"
|
||||
className="cyber-dialog border border-border rounded-lg overflow-hidden shadow-2xl"
|
||||
onClick={handleTerminalClick}
|
||||
>
|
||||
{/* Terminal header */}
|
||||
<div className="flex items-center gap-2 px-4 py-2.5 bg-[#0a0a0f] border-b border-gray-800/50">
|
||||
<div className="flex items-center gap-2 px-4 py-2.5 cyber-bg-elevated border-b border-border">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<div className="w-3 h-3 rounded-full bg-red-500/80" />
|
||||
<div className="w-3 h-3 rounded-full bg-yellow-500/80" />
|
||||
<div className="w-3 h-3 rounded-full bg-green-500/80" />
|
||||
</div>
|
||||
<span className="text-[11px] text-gray-500 ml-3 font-mono tracking-wider">
|
||||
<span className="text-xs text-muted-foreground ml-3 font-mono tracking-wider">
|
||||
deepaudit@terminal
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -228,7 +228,7 @@ export function SplashScreen({ onComplete }: SplashScreenProps) {
|
|||
className={`mb-1 ${
|
||||
log.includes("[READY]") ? "text-green-400" :
|
||||
log.includes("[INIT]") ? "text-primary" :
|
||||
"text-gray-500"
|
||||
"text-muted-foreground"
|
||||
}`}
|
||||
style={{
|
||||
animation: "fadeSlideIn 0.2s ease-out",
|
||||
|
|
@ -236,17 +236,17 @@ export function SplashScreen({ onComplete }: SplashScreenProps) {
|
|||
animationDelay: `${i * 0.05}s`
|
||||
}}
|
||||
>
|
||||
<span className="text-gray-600 mr-2">></span>
|
||||
<span className="text-muted-foreground mr-2">></span>
|
||||
{log}
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Welcome message */}
|
||||
{bootComplete && (
|
||||
<div className="mt-4 mb-4 pt-3 border-t border-gray-800/50">
|
||||
<div className="mt-4 mb-4 pt-3 border-t border-border">
|
||||
<div className="text-primary mb-1">Welcome to DeepAudit Agent Terminal</div>
|
||||
<div className="text-gray-500 text-xs">
|
||||
Type <span className="text-emerald-400 font-semibold">'audit'</span> to start a new security audit, or <span className="text-gray-400">'help'</span> for commands.
|
||||
<div className="text-muted-foreground text-xs">
|
||||
Type <span className="text-emerald-400 font-semibold">'audit'</span> to start a new security audit, or <span className="text-muted-foreground">'help'</span> for commands.
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -254,13 +254,13 @@ export function SplashScreen({ onComplete }: SplashScreenProps) {
|
|||
{/* Command history */}
|
||||
{commandHistory.map((entry, i) => (
|
||||
<div key={`cmd-${i}`} className="mb-2">
|
||||
<div className="flex items-center gap-2 text-gray-300">
|
||||
<div className="flex items-center gap-2 text-foreground">
|
||||
<span className="text-emerald-500">$</span>
|
||||
<span>{entry.input}</span>
|
||||
</div>
|
||||
{entry.output && (
|
||||
<div className={`ml-4 mt-1 whitespace-pre-wrap text-xs ${
|
||||
entry.isError ? "text-red-400" : "text-gray-500"
|
||||
entry.isError ? "text-red-400" : "text-muted-foreground"
|
||||
}`}>
|
||||
{entry.output}
|
||||
</div>
|
||||
|
|
@ -270,7 +270,7 @@ export function SplashScreen({ onComplete }: SplashScreenProps) {
|
|||
|
||||
{/* Current input line */}
|
||||
{bootComplete && (
|
||||
<div className="flex items-center gap-2 text-gray-300">
|
||||
<div className="flex items-center gap-2 text-foreground">
|
||||
<span className="text-emerald-500">$</span>
|
||||
<div className="flex-1 relative">
|
||||
<input
|
||||
|
|
@ -285,7 +285,7 @@ export function SplashScreen({ onComplete }: SplashScreenProps) {
|
|||
autoComplete="off"
|
||||
autoFocus
|
||||
/>
|
||||
<span className="text-gray-200">{currentInput}</span>
|
||||
<span className="text-foreground">{currentInput}</span>
|
||||
<span
|
||||
className={`inline-block w-2 h-4 bg-emerald-400 ml-0.5 align-middle transition-opacity ${
|
||||
cursorBlink ? "opacity-100" : "opacity-0"
|
||||
|
|
@ -301,7 +301,7 @@ export function SplashScreen({ onComplete }: SplashScreenProps) {
|
|||
<div className={`mt-4 text-center transition-all duration-500 ${bootComplete ? "opacity-100" : "opacity-0"}`}>
|
||||
<div className="flex items-center justify-center gap-3">
|
||||
<div className="h-px w-8 bg-gradient-to-r from-transparent to-gray-700" />
|
||||
<span className="text-gray-600 text-[10px] font-mono tracking-wider">PRESS ENTER TO EXECUTE</span>
|
||||
<span className="text-muted-foreground text-xs font-mono tracking-wider">PRESS ENTER TO EXECUTE</span>
|
||||
<div className="h-px w-8 bg-gradient-to-l from-transparent to-gray-700" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -309,18 +309,18 @@ export function SplashScreen({ onComplete }: SplashScreenProps) {
|
|||
</div>
|
||||
|
||||
{/* Corner decorations */}
|
||||
<div className="absolute top-4 left-4 text-[10px] font-mono text-gray-700 z-30">
|
||||
<div className="absolute top-4 left-4 text-xs font-mono text-muted-foreground z-30">
|
||||
<div>SYS.VERSION: 3.0.0</div>
|
||||
<div>MODE: INTERACTIVE</div>
|
||||
</div>
|
||||
<div className="absolute top-4 right-4 text-[10px] font-mono text-gray-700 text-right z-30">
|
||||
<div className="absolute top-4 right-4 text-xs font-mono text-muted-foreground text-right z-30">
|
||||
<div>MEM: 16384MB</div>
|
||||
<div>STATUS: READY</div>
|
||||
</div>
|
||||
<div className="absolute bottom-4 left-4 text-[10px] font-mono text-gray-700 z-30">
|
||||
<div className="absolute bottom-4 left-4 text-xs font-mono text-muted-foreground z-30">
|
||||
DEEPAUDIT_AGENT_v3
|
||||
</div>
|
||||
<div className="absolute bottom-4 right-4 text-[10px] font-mono text-gray-700 z-30">
|
||||
<div className="absolute bottom-4 right-4 text-xs font-mono text-muted-foreground z-30">
|
||||
{new Date().toISOString().split("T")[0]}
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ function CircularProgress({ value, size = 48, strokeWidth = 3, color = "primary"
|
|||
}
|
||||
|
||||
// Metric card component with enhanced colors
|
||||
function MetricCard({ icon, label, value, suffix = "", colorClass = "text-slate-400" }: {
|
||||
function MetricCard({ icon, label, value, suffix = "", colorClass = "text-muted-foreground" }: {
|
||||
icon: React.ReactNode;
|
||||
label: string;
|
||||
value: string | number;
|
||||
|
|
@ -68,14 +68,14 @@ function MetricCard({ icon, label, value, suffix = "", colorClass = "text-slate-
|
|||
colorClass?: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="flex items-center gap-2.5 p-2.5 rounded bg-slate-900/40 border border-slate-700/30">
|
||||
<div className="flex items-center gap-2.5 p-3 rounded bg-card border border-border">
|
||||
<div className={colorClass}>
|
||||
{icon}
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="text-[9px] text-slate-500 uppercase tracking-wider truncate">{label}</div>
|
||||
<div className="text-sm text-white font-mono font-medium">
|
||||
{value}<span className="text-slate-500 text-xs">{suffix}</span>
|
||||
<div className="text-sm text-muted-foreground uppercase tracking-wider truncate font-medium">{label}</div>
|
||||
<div className="text-lg text-foreground font-mono font-bold">
|
||||
{value}<span className="text-muted-foreground text-base">{suffix}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -106,17 +106,17 @@ export const StatsPanel = memo(function StatsPanel({ task, findings }: StatsPane
|
|||
return (
|
||||
<div className="space-y-3">
|
||||
{/* Progress Section */}
|
||||
<div className="p-3 rounded border border-slate-700/40 bg-gradient-to-br from-slate-900/60 to-slate-900/30">
|
||||
<div className="p-4 rounded border border-border bg-card">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<Activity className="w-3.5 h-3.5 text-primary" />
|
||||
<span className="text-[10px] text-slate-400 uppercase tracking-wider font-medium">Progress</span>
|
||||
<span className="text-sm text-muted-foreground uppercase tracking-wider font-medium">Progress</span>
|
||||
</div>
|
||||
<span className="text-xs text-primary font-mono font-bold">{progressPercent.toFixed(0)}%</span>
|
||||
</div>
|
||||
|
||||
{/* Progress bar */}
|
||||
<div className="relative h-2 bg-slate-800 rounded-full overflow-hidden">
|
||||
<div className="relative h-2 bg-muted rounded-full overflow-hidden">
|
||||
<div
|
||||
className="absolute inset-y-0 left-0 bg-gradient-to-r from-primary to-primary/80 rounded-full transition-all duration-700 ease-out"
|
||||
style={{ width: `${progressPercent}%` }}
|
||||
|
|
@ -132,17 +132,17 @@ export const StatsPanel = memo(function StatsPanel({ task, findings }: StatsPane
|
|||
</div>
|
||||
|
||||
{/* File progress */}
|
||||
<div className="flex items-center justify-between mt-2 text-[10px]">
|
||||
<span className="text-slate-500">Files scanned</span>
|
||||
<span className="text-slate-300 font-mono">
|
||||
{task.analyzed_files}<span className="text-slate-500">/{task.total_files}</span>
|
||||
<div className="flex items-center justify-between mt-3 text-base">
|
||||
<span className="text-muted-foreground font-medium">Files scanned</span>
|
||||
<span className="text-foreground font-mono font-bold">
|
||||
{task.analyzed_files}<span className="text-muted-foreground font-normal">/{task.total_files}</span>
|
||||
</span>
|
||||
</div>
|
||||
{/* Files with findings */}
|
||||
{task.files_with_findings > 0 && (
|
||||
<div className="flex items-center justify-between mt-1 text-[10px]">
|
||||
<span className="text-slate-500">Files with findings</span>
|
||||
<span className="text-rose-400 font-mono font-medium">
|
||||
<div className="flex items-center justify-between mt-2 text-base">
|
||||
<span className="text-muted-foreground font-medium">Files with findings</span>
|
||||
<span className="text-rose-500 dark:text-rose-400 font-mono font-bold">
|
||||
{task.files_with_findings}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -152,58 +152,58 @@ export const StatsPanel = memo(function StatsPanel({ task, findings }: StatsPane
|
|||
{/* Metrics Grid */}
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<MetricCard
|
||||
icon={<Repeat className="w-3.5 h-3.5" />}
|
||||
icon={<Repeat className="w-4 h-4" />}
|
||||
label="Iterations"
|
||||
value={task.total_iterations || 0}
|
||||
colorClass="text-teal-400"
|
||||
colorClass="text-teal-600 dark:text-teal-400"
|
||||
/>
|
||||
<MetricCard
|
||||
icon={<Zap className="w-3.5 h-3.5" />}
|
||||
icon={<Zap className="w-4 h-4" />}
|
||||
label="Tool Calls"
|
||||
value={task.tool_calls_count || 0}
|
||||
colorClass="text-amber-400"
|
||||
colorClass="text-amber-600 dark:text-amber-400"
|
||||
/>
|
||||
<MetricCard
|
||||
icon={<FileCode className="w-3.5 h-3.5" />}
|
||||
icon={<FileCode className="w-4 h-4" />}
|
||||
label="Tokens"
|
||||
value={((task.tokens_used || 0) / 1000).toFixed(1)}
|
||||
suffix="k"
|
||||
colorClass="text-violet-400"
|
||||
colorClass="text-violet-600 dark:text-violet-400"
|
||||
/>
|
||||
<MetricCard
|
||||
icon={<Bug className="w-3.5 h-3.5" />}
|
||||
icon={<Bug className="w-4 h-4" />}
|
||||
label="Findings"
|
||||
value={totalFindings}
|
||||
colorClass={totalFindings > 0 ? "text-rose-400" : "text-slate-400"}
|
||||
colorClass={totalFindings > 0 ? "text-rose-600 dark:text-rose-400" : "text-muted-foreground"}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Findings breakdown */}
|
||||
{totalFindings > 0 && (
|
||||
<div className="p-3 rounded border border-slate-700/40 bg-slate-900/40">
|
||||
<div className="p-4 rounded border border-border bg-card">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<AlertTriangle className="w-3.5 h-3.5 text-rose-400" />
|
||||
<span className="text-[10px] text-slate-400 uppercase tracking-wider font-medium">Severity Breakdown</span>
|
||||
<span className="text-sm text-muted-foreground uppercase tracking-wider font-medium">Severity Breakdown</span>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{severityCounts.critical > 0 && (
|
||||
<Badge className="bg-rose-500/25 text-rose-300 border border-rose-500/40 text-[10px] font-mono font-semibold">
|
||||
<Badge className="bg-rose-500/25 text-rose-700 dark:text-rose-300 border border-rose-500/40 text-sm font-mono font-semibold">
|
||||
CRIT: {severityCounts.critical}
|
||||
</Badge>
|
||||
)}
|
||||
{severityCounts.high > 0 && (
|
||||
<Badge className="bg-orange-500/25 text-orange-300 border border-orange-500/40 text-[10px] font-mono font-semibold">
|
||||
<Badge className="bg-orange-500/25 text-orange-700 dark:text-orange-300 border border-orange-500/40 text-sm font-mono font-semibold">
|
||||
HIGH: {severityCounts.high}
|
||||
</Badge>
|
||||
)}
|
||||
{severityCounts.medium > 0 && (
|
||||
<Badge className="bg-amber-500/25 text-amber-300 border border-amber-500/40 text-[10px] font-mono font-semibold">
|
||||
<Badge className="bg-amber-500/25 text-amber-700 dark:text-amber-300 border border-amber-500/40 text-sm font-mono font-semibold">
|
||||
MED: {severityCounts.medium}
|
||||
</Badge>
|
||||
)}
|
||||
{severityCounts.low > 0 && (
|
||||
<Badge className="bg-sky-500/25 text-sky-300 border border-sky-500/40 text-[10px] font-mono font-semibold">
|
||||
<Badge className="bg-sky-500/25 text-sky-700 dark:text-sky-300 border border-sky-500/40 text-sm font-mono font-semibold">
|
||||
LOW: {severityCounts.low}
|
||||
</Badge>
|
||||
)}
|
||||
|
|
@ -213,11 +213,11 @@ export const StatsPanel = memo(function StatsPanel({ task, findings }: StatsPane
|
|||
|
||||
{/* Security Score */}
|
||||
{task.security_score !== null && task.security_score !== undefined && (
|
||||
<div className="p-3 rounded border border-slate-700/40 bg-gradient-to-br from-slate-900/60 to-slate-900/30">
|
||||
<div className="p-4 rounded border border-border bg-card">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Shield className="w-3.5 h-3.5 text-emerald-400" />
|
||||
<span className="text-[10px] text-slate-400 uppercase tracking-wider font-medium">Security Score</span>
|
||||
<span className="text-sm text-muted-foreground uppercase tracking-wider font-medium">Security Score</span>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<CircularProgress
|
||||
|
|
@ -227,9 +227,9 @@ export const StatsPanel = memo(function StatsPanel({ task, findings }: StatsPane
|
|||
color={getScoreColor(task.security_score)}
|
||||
/>
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<span className={`text-sm font-bold font-mono ${task.security_score >= 80 ? 'text-emerald-400' :
|
||||
task.security_score >= 60 ? 'text-amber-400' :
|
||||
'text-rose-400'
|
||||
<span className={`text-sm font-bold font-mono ${task.security_score >= 80 ? 'text-emerald-600 dark:text-emerald-400' :
|
||||
task.security_score >= 60 ? 'text-amber-600 dark:text-amber-400' :
|
||||
'text-rose-600 dark:text-rose-400'
|
||||
}`}>
|
||||
{task.security_score.toFixed(0)}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -24,48 +24,48 @@ const STATUS_CONFIG: Record<string, {
|
|||
pending: {
|
||||
icon: <Clock className="w-3.5 h-3.5" />,
|
||||
iconSm: <Clock className="w-3 h-3" />,
|
||||
bg: "bg-gray-800/80 border-gray-600/50",
|
||||
text: "text-gray-300",
|
||||
bg: "bg-muted border-border",
|
||||
text: "text-foreground",
|
||||
label: "PENDING",
|
||||
},
|
||||
running: {
|
||||
icon: <Loader2 className="w-3.5 h-3.5 animate-spin" />,
|
||||
iconSm: <Loader2 className="w-3 h-3 animate-spin" />,
|
||||
bg: "bg-green-950/80 border-green-500/50",
|
||||
text: "text-green-400",
|
||||
bg: "bg-green-100 dark:bg-green-950/80 border-green-500/50",
|
||||
text: "text-green-700 dark:text-green-400",
|
||||
label: "RUNNING",
|
||||
glow: "shadow-[0_0_8px_rgba(74,222,128,0.3)]",
|
||||
glow: "dark:shadow-[0_0_8px_rgba(74,222,128,0.3)]",
|
||||
animate: true,
|
||||
},
|
||||
completed: {
|
||||
icon: <CheckCircle2 className="w-3.5 h-3.5" />,
|
||||
iconSm: <CheckCircle2 className="w-3 h-3" />,
|
||||
bg: "bg-green-950/60 border-green-600/50",
|
||||
text: "text-green-400",
|
||||
bg: "bg-green-100 dark:bg-green-950/60 border-green-600/50",
|
||||
text: "text-green-700 dark:text-green-400",
|
||||
label: "COMPLETED",
|
||||
},
|
||||
failed: {
|
||||
icon: <XCircle className="w-3.5 h-3.5" />,
|
||||
iconSm: <XCircle className="w-3 h-3" />,
|
||||
bg: "bg-red-950/60 border-red-600/50",
|
||||
text: "text-red-400",
|
||||
bg: "bg-red-100 dark:bg-red-950/60 border-red-600/50",
|
||||
text: "text-red-700 dark:text-red-400",
|
||||
label: "FAILED",
|
||||
glow: "shadow-[0_0_8px_rgba(248,113,113,0.2)]",
|
||||
glow: "dark:shadow-[0_0_8px_rgba(248,113,113,0.2)]",
|
||||
},
|
||||
cancelled: {
|
||||
icon: <Square className="w-3.5 h-3.5" />,
|
||||
iconSm: <Square className="w-3 h-3" />,
|
||||
bg: "bg-yellow-950/60 border-yellow-600/50",
|
||||
text: "text-yellow-400",
|
||||
bg: "bg-yellow-100 dark:bg-yellow-950/60 border-yellow-600/50",
|
||||
text: "text-yellow-700 dark:text-yellow-400",
|
||||
label: "CANCELLED",
|
||||
},
|
||||
error: {
|
||||
icon: <AlertCircle className="w-3.5 h-3.5" />,
|
||||
iconSm: <AlertCircle className="w-3 h-3" />,
|
||||
bg: "bg-red-950/60 border-red-600/50",
|
||||
text: "text-red-400",
|
||||
bg: "bg-red-100 dark:bg-red-950/60 border-red-600/50",
|
||||
text: "text-red-700 dark:text-red-400",
|
||||
label: "ERROR",
|
||||
glow: "shadow-[0_0_8px_rgba(248,113,113,0.2)]",
|
||||
glow: "dark:shadow-[0_0_8px_rgba(248,113,113,0.2)]",
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -81,7 +81,7 @@ export const StatusBadge = memo(function StatusBadge({ status, size = "default"
|
|||
${config.bg}
|
||||
${config.text}
|
||||
${config.glow || ''}
|
||||
${isSmall ? 'px-1.5 py-0.5 text-[9px]' : 'px-2 py-1 text-[10px]'}
|
||||
${isSmall ? 'px-2 py-1 text-sm' : 'px-2.5 py-1.5 text-sm'}
|
||||
`}
|
||||
>
|
||||
{isSmall ? config.iconSm : config.icon}
|
||||
|
|
|
|||
|
|
@ -16,11 +16,11 @@ import {
|
|||
// ============ Severity Colors (Enhanced contrast) ============
|
||||
|
||||
export const SEVERITY_COLORS: Record<string, string> = {
|
||||
critical: "text-rose-300 bg-rose-500/20 border border-rose-500/40",
|
||||
high: "text-orange-300 bg-orange-500/20 border border-orange-500/40",
|
||||
medium: "text-amber-300 bg-amber-500/20 border border-amber-500/40",
|
||||
low: "text-sky-300 bg-sky-500/20 border border-sky-500/40",
|
||||
info: "text-slate-300 bg-slate-500/20 border border-slate-500/40",
|
||||
critical: "text-rose-700 dark:text-rose-300 bg-rose-500/20 border border-rose-500/40",
|
||||
high: "text-orange-700 dark:text-orange-300 bg-orange-500/20 border border-orange-500/40",
|
||||
medium: "text-amber-700 dark:text-amber-300 bg-amber-500/20 border border-amber-500/40",
|
||||
low: "text-sky-700 dark:text-sky-300 bg-sky-500/20 border border-sky-500/40",
|
||||
info: "text-foreground bg-muted/20 border border-border",
|
||||
};
|
||||
|
||||
// ============ Action Verbs for Animation ============
|
||||
|
|
@ -39,47 +39,47 @@ export const LOG_TYPE_CONFIG: Record<string, {
|
|||
bgColor: string;
|
||||
}> = {
|
||||
thinking: {
|
||||
icon: React.createElement(Brain, { className: "w-4 h-4 text-violet-400" }),
|
||||
icon: React.createElement(Brain, { className: "w-4 h-4 text-violet-600 dark:text-violet-400" }),
|
||||
borderColor: "border-l-violet-500",
|
||||
bgColor: "bg-violet-500/10"
|
||||
},
|
||||
tool: {
|
||||
icon: React.createElement(Wrench, { className: "w-4 h-4 text-amber-400" }),
|
||||
icon: React.createElement(Wrench, { className: "w-4 h-4 text-amber-600 dark:text-amber-400" }),
|
||||
borderColor: "border-l-amber-500",
|
||||
bgColor: "bg-amber-500/10"
|
||||
},
|
||||
phase: {
|
||||
icon: React.createElement(Target, { className: "w-4 h-4 text-teal-400" }),
|
||||
icon: React.createElement(Target, { className: "w-4 h-4 text-teal-600 dark:text-teal-400" }),
|
||||
borderColor: "border-l-teal-500",
|
||||
bgColor: "bg-teal-500/10"
|
||||
},
|
||||
finding: {
|
||||
icon: React.createElement(Bug, { className: "w-4 h-4 text-rose-400" }),
|
||||
icon: React.createElement(Bug, { className: "w-4 h-4 text-rose-600 dark:text-rose-400" }),
|
||||
borderColor: "border-l-rose-500",
|
||||
bgColor: "bg-rose-500/10"
|
||||
},
|
||||
dispatch: {
|
||||
icon: React.createElement(Zap, { className: "w-4 h-4 text-sky-400" }),
|
||||
icon: React.createElement(Zap, { className: "w-4 h-4 text-sky-600 dark:text-sky-400" }),
|
||||
borderColor: "border-l-sky-500",
|
||||
bgColor: "bg-sky-500/10"
|
||||
},
|
||||
info: {
|
||||
icon: React.createElement(Terminal, { className: "w-4 h-4 text-slate-400" }),
|
||||
borderColor: "border-l-slate-500",
|
||||
bgColor: "bg-slate-500/10"
|
||||
icon: React.createElement(Terminal, { className: "w-4 h-4 text-muted-foreground" }),
|
||||
borderColor: "border-l-muted-foreground",
|
||||
bgColor: "bg-muted/10"
|
||||
},
|
||||
error: {
|
||||
icon: React.createElement(AlertTriangle, { className: "w-4 h-4 text-red-400" }),
|
||||
icon: React.createElement(AlertTriangle, { className: "w-4 h-4 text-red-600 dark:text-red-400" }),
|
||||
borderColor: "border-l-red-500",
|
||||
bgColor: "bg-red-500/15"
|
||||
},
|
||||
user: {
|
||||
icon: React.createElement(Shield, { className: "w-4 h-4 text-indigo-400" }),
|
||||
icon: React.createElement(Shield, { className: "w-4 h-4 text-indigo-600 dark:text-indigo-400" }),
|
||||
borderColor: "border-l-indigo-500",
|
||||
bgColor: "bg-indigo-500/10"
|
||||
},
|
||||
progress: {
|
||||
icon: React.createElement(Loader2, { className: "w-4 h-4 text-cyan-400 animate-spin" }),
|
||||
icon: React.createElement(Loader2, { className: "w-4 h-4 text-cyan-600 dark:text-cyan-400 animate-spin" }),
|
||||
borderColor: "border-l-cyan-500",
|
||||
bgColor: "bg-cyan-500/10"
|
||||
},
|
||||
|
|
@ -94,29 +94,29 @@ export const AGENT_STATUS_CONFIG: Record<string, {
|
|||
animate?: boolean;
|
||||
}> = {
|
||||
running: {
|
||||
icon: React.createElement("div", { className: "w-2 h-2 rounded-full bg-emerald-400" }),
|
||||
color: "text-emerald-400",
|
||||
icon: React.createElement("div", { className: "w-2 h-2 rounded-full bg-emerald-500 dark:bg-emerald-400" }),
|
||||
color: "text-emerald-600 dark:text-emerald-400",
|
||||
text: "Running",
|
||||
animate: true
|
||||
},
|
||||
completed: {
|
||||
icon: React.createElement(CheckCircle2, { className: "w-3 h-3 text-emerald-400" }),
|
||||
color: "text-emerald-400",
|
||||
icon: React.createElement(CheckCircle2, { className: "w-3 h-3 text-emerald-600 dark:text-emerald-400" }),
|
||||
color: "text-emerald-600 dark:text-emerald-400",
|
||||
text: "Completed"
|
||||
},
|
||||
failed: {
|
||||
icon: React.createElement(XCircle, { className: "w-3 h-3 text-rose-400" }),
|
||||
color: "text-rose-400",
|
||||
icon: React.createElement(XCircle, { className: "w-3 h-3 text-rose-600 dark:text-rose-400" }),
|
||||
color: "text-rose-600 dark:text-rose-400",
|
||||
text: "Failed"
|
||||
},
|
||||
waiting: {
|
||||
icon: React.createElement(Clock, { className: "w-3 h-3 text-amber-400" }),
|
||||
color: "text-amber-400",
|
||||
icon: React.createElement(Clock, { className: "w-3 h-3 text-amber-600 dark:text-amber-400" }),
|
||||
color: "text-amber-600 dark:text-amber-400",
|
||||
text: "Waiting"
|
||||
},
|
||||
created: {
|
||||
icon: React.createElement("div", { className: "w-2 h-2 rounded-full bg-slate-500" }),
|
||||
color: "text-slate-400",
|
||||
icon: React.createElement("div", { className: "w-2 h-2 rounded-full bg-muted" }),
|
||||
color: "text-muted-foreground",
|
||||
text: "Created"
|
||||
},
|
||||
};
|
||||
|
|
@ -129,22 +129,22 @@ export const AGENT_TYPE_CONFIG: Record<string, {
|
|||
color: string;
|
||||
}> = {
|
||||
orchestrator: {
|
||||
icon: React.createElement(Cpu, { className: "w-3.5 h-3.5 text-violet-400" }),
|
||||
icon: React.createElement(Cpu, { className: "w-4 h-4 text-violet-600 dark:text-violet-400" }),
|
||||
label: "Orchestrator",
|
||||
color: "violet"
|
||||
},
|
||||
recon: {
|
||||
icon: React.createElement(Scan, { className: "w-3.5 h-3.5 text-teal-400" }),
|
||||
icon: React.createElement(Scan, { className: "w-4 h-4 text-teal-600 dark:text-teal-400" }),
|
||||
label: "Reconnaissance",
|
||||
color: "teal"
|
||||
},
|
||||
analysis: {
|
||||
icon: React.createElement(FileSearch, { className: "w-3.5 h-3.5 text-amber-400" }),
|
||||
icon: React.createElement(FileSearch, { className: "w-4 h-4 text-amber-600 dark:text-amber-400" }),
|
||||
label: "Analysis",
|
||||
color: "amber"
|
||||
},
|
||||
verification: {
|
||||
icon: React.createElement(ShieldCheck, { className: "w-3.5 h-3.5 text-emerald-400" }),
|
||||
icon: React.createElement(ShieldCheck, { className: "w-4 h-4 text-emerald-600 dark:text-emerald-400" }),
|
||||
label: "Verification",
|
||||
color: "emerald"
|
||||
},
|
||||
|
|
@ -158,7 +158,7 @@ export const TASK_STATUS_CONFIG: Record<string, {
|
|||
text: string;
|
||||
}> = {
|
||||
pending: {
|
||||
bg: "bg-slate-600",
|
||||
bg: "bg-muted",
|
||||
icon: React.createElement(Clock, { className: "w-3 h-3" }),
|
||||
text: "PENDING"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -757,12 +757,12 @@ function AgentAuditPageContent() {
|
|||
|
||||
if (isLoading && !task) {
|
||||
return (
|
||||
<div className="h-screen bg-[#08090d] flex items-center justify-center relative overflow-hidden">
|
||||
<div className="h-screen bg-background flex items-center justify-center relative overflow-hidden">
|
||||
{/* Grid background */}
|
||||
<div className="absolute inset-0 cyber-grid opacity-30" />
|
||||
{/* Vignette */}
|
||||
<div className="absolute inset-0 vignette pointer-events-none" />
|
||||
<div className="flex items-center gap-3 text-[#8a95a5] relative z-10">
|
||||
<div className="flex items-center gap-3 text-muted-foreground relative z-10">
|
||||
<Loader2 className="w-5 h-5 animate-spin text-primary" />
|
||||
<span className="font-mono text-sm tracking-wide">LOADING AUDIT TASK...</span>
|
||||
</div>
|
||||
|
|
@ -771,7 +771,7 @@ function AgentAuditPageContent() {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="h-screen bg-[#08090d] flex flex-col overflow-hidden relative">
|
||||
<div className="h-screen bg-background flex flex-col overflow-hidden relative">
|
||||
{/* Subtle grid background */}
|
||||
<div className="absolute inset-0 cyber-grid-subtle opacity-40 pointer-events-none" />
|
||||
{/* Scanline effect */}
|
||||
|
|
@ -790,24 +790,24 @@ function AgentAuditPageContent() {
|
|||
{/* Main content */}
|
||||
<div className="flex-1 flex overflow-hidden relative">
|
||||
{/* Left Panel - Activity Log */}
|
||||
<div className="w-3/4 flex flex-col border-r border-[#1a2535]">
|
||||
<div className="w-3/4 flex flex-col border-r border-border">
|
||||
{/* Log header */}
|
||||
<div className="flex-shrink-0 h-11 border-b border-[#1a2535] flex items-center justify-between px-4 bg-[#0a0c10]/90 backdrop-blur-sm">
|
||||
<div className="flex items-center gap-3 text-xs text-[#8a95a5]">
|
||||
<div className="flex-shrink-0 h-11 border-b border-border flex items-center justify-between px-4 cyber-bg-elevated/90 backdrop-blur-sm">
|
||||
<div className="flex items-center gap-3 text-xs text-muted-foreground">
|
||||
<div className="flex items-center gap-2">
|
||||
<Terminal className="w-4 h-4 text-[#5a6577]" />
|
||||
<span className="uppercase font-bold tracking-[0.15em] text-[#d0d8e8]">Activity Log</span>
|
||||
<Terminal className="w-4 h-4 text-muted-foreground" />
|
||||
<span className="uppercase font-bold tracking-[0.15em] text-foreground">Activity Log</span>
|
||||
</div>
|
||||
{isConnected && (
|
||||
<div className="flex items-center gap-1.5 text-[#3dd68c]">
|
||||
<div className="flex items-center gap-1.5 text-emerald-400">
|
||||
<span className="relative flex h-2 w-2">
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-[#3dd68c] opacity-75"></span>
|
||||
<span className="relative inline-flex rounded-full h-2 w-2 bg-[#3dd68c] shadow-[0_0_8px_rgba(61,214,140,0.5)]"></span>
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"></span>
|
||||
<span className="relative inline-flex rounded-full h-2 w-2 bg-emerald-400 shadow-[0_0_8px_rgba(61,214,140,0.5)]"></span>
|
||||
</span>
|
||||
<span className="text-[10px] font-mono uppercase tracking-wider">Live</span>
|
||||
<span className="text-xs font-mono uppercase tracking-wider">Live</span>
|
||||
</div>
|
||||
)}
|
||||
<Badge variant="outline" className="h-5 px-1.5 text-[10px] border-[#2a3545] text-[#6a7587] font-mono bg-[#0d1015]">
|
||||
<Badge variant="outline" className="h-5 px-1.5 text-xs border-border text-muted-foreground font-mono bg-muted">
|
||||
{filteredLogs.length}{!showAllLogs && logs.length !== filteredLogs.length ? ` / ${logs.length}` : ''}
|
||||
</Badge>
|
||||
</div>
|
||||
|
|
@ -815,11 +815,11 @@ function AgentAuditPageContent() {
|
|||
<button
|
||||
onClick={() => setAutoScroll(!isAutoScroll)}
|
||||
className={`
|
||||
flex items-center gap-1.5 text-[10px] px-2.5 py-1 rounded font-mono uppercase tracking-wider
|
||||
flex items-center gap-1.5 text-xs px-2.5 py-1 rounded font-mono uppercase tracking-wider
|
||||
transition-all duration-200
|
||||
${isAutoScroll
|
||||
? 'bg-primary/15 text-primary border border-primary/40 shadow-[0_0_10px_rgba(255,95,31,0.15)]'
|
||||
: 'text-[#6a7587] hover:text-[#a8b0c0] border border-transparent hover:border-[#2a3545] hover:bg-[#1a2030]/50'
|
||||
: 'text-muted-foreground hover:text-foreground border border-transparent hover:border-border hover:bg-muted/50'
|
||||
}
|
||||
`}
|
||||
>
|
||||
|
|
@ -829,7 +829,7 @@ function AgentAuditPageContent() {
|
|||
</div>
|
||||
|
||||
{/* Log content */}
|
||||
<div className="flex-1 overflow-y-auto p-4 custom-scrollbar bg-[#060810]/50">
|
||||
<div className="flex-1 overflow-y-auto p-4 custom-scrollbar bg-background/50">
|
||||
{/* Filter indicator */}
|
||||
{selectedAgentId && !showAllLogs && (
|
||||
<div className="mb-3 px-3 py-2 bg-primary/8 border border-primary/25 rounded flex items-center justify-between">
|
||||
|
|
@ -839,7 +839,7 @@ function AgentAuditPageContent() {
|
|||
</div>
|
||||
<button
|
||||
onClick={() => selectAgent(null)}
|
||||
className="text-[10px] text-[#6a7587] hover:text-[#d0d8e8] transition-colors font-mono uppercase tracking-wider"
|
||||
className="text-xs text-muted-foreground hover:text-foreground transition-colors font-mono uppercase tracking-wider"
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
|
|
@ -849,10 +849,10 @@ function AgentAuditPageContent() {
|
|||
{/* Logs */}
|
||||
{filteredLogs.length === 0 ? (
|
||||
<div className="h-full flex items-center justify-center">
|
||||
<div className="text-center text-[#4a5565]">
|
||||
<div className="text-center text-muted-foreground">
|
||||
{isRunning ? (
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
<Loader2 className="w-6 h-6 animate-spin text-[#5a6577]" />
|
||||
<Loader2 className="w-6 h-6 animate-spin text-muted-foreground" />
|
||||
<span className="text-sm font-mono tracking-wide">
|
||||
{selectedAgentId && !showAllLogs
|
||||
? 'WAITING FOR ACTIVITY FROM SELECTED AGENT...'
|
||||
|
|
@ -885,36 +885,36 @@ function AgentAuditPageContent() {
|
|||
|
||||
{/* Status bar */}
|
||||
{task && (
|
||||
<div className="flex-shrink-0 h-9 border-t border-[#1a2535] flex items-center justify-between px-4 text-xs bg-[#0a0c10]/90 backdrop-blur-sm">
|
||||
<div className="flex-shrink-0 h-9 border-t border-border flex items-center justify-between px-4 text-xs cyber-bg-elevated/90 backdrop-blur-sm">
|
||||
<span>
|
||||
{isRunning ? (
|
||||
<span className="flex items-center gap-2 text-[#3dd68c]">
|
||||
<span className="flex items-center gap-2 text-emerald-400">
|
||||
<span className="relative flex h-1.5 w-1.5">
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-[#3dd68c] opacity-75"></span>
|
||||
<span className="relative inline-flex rounded-full h-1.5 w-1.5 bg-[#3dd68c] shadow-[0_0_6px_rgba(61,214,140,0.5)]"></span>
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"></span>
|
||||
<span className="relative inline-flex rounded-full h-1.5 w-1.5 bg-emerald-400 shadow-[0_0_6px_rgba(61,214,140,0.5)]"></span>
|
||||
</span>
|
||||
<span className="font-mono tracking-wide">{statusVerb}{'.'.repeat(statusDots)}</span>
|
||||
</span>
|
||||
) : isComplete ? (
|
||||
<span className="text-[#6a7587] font-mono tracking-wide">AUDIT {task.status?.toUpperCase()}</span>
|
||||
<span className="text-muted-foreground font-mono tracking-wide">AUDIT {task.status?.toUpperCase()}</span>
|
||||
) : (
|
||||
<span className="text-[#4a5565] font-mono tracking-wide">READY</span>
|
||||
<span className="text-muted-foreground font-mono tracking-wide">READY</span>
|
||||
)}
|
||||
</span>
|
||||
<div className="flex items-center gap-4 font-mono text-[#6a7587]">
|
||||
<div className="flex items-center gap-4 font-mono text-muted-foreground">
|
||||
<span>
|
||||
<span className="text-primary text-glow-primary">{task.progress_percentage?.toFixed(0) || 0}</span>
|
||||
<span className="text-[#4a5565]">%</span>
|
||||
<span className="text-muted-foreground">%</span>
|
||||
</span>
|
||||
<span className="text-[#2a3545]">│</span>
|
||||
<span className="text-border">│</span>
|
||||
<span>
|
||||
<span className="text-[#a8b0c0]">{task.analyzed_files}</span>
|
||||
<span className="text-[#4a5565]">/{task.total_files} files</span>
|
||||
<span className="text-foreground">{task.analyzed_files}</span>
|
||||
<span className="text-muted-foreground">/{task.total_files} files</span>
|
||||
</span>
|
||||
<span className="text-[#2a3545]">│</span>
|
||||
<span className="text-border">│</span>
|
||||
<span>
|
||||
<span className="text-[#a8b0c0]">{task.tool_calls_count || 0}</span>
|
||||
<span className="text-[#4a5565]"> tools</span>
|
||||
<span className="text-foreground">{task.tool_calls_count || 0}</span>
|
||||
<span className="text-muted-foreground"> tools</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -922,16 +922,16 @@ function AgentAuditPageContent() {
|
|||
</div>
|
||||
|
||||
{/* Right Panel - Agent Tree + Stats */}
|
||||
<div className="w-1/4 flex flex-col bg-[#080a0e]">
|
||||
<div className="w-1/4 flex flex-col bg-background">
|
||||
{/* Agent Tree section */}
|
||||
<div className="flex-1 flex flex-col border-b border-[#1a2535] overflow-hidden">
|
||||
<div className="flex-1 flex flex-col border-b border-border overflow-hidden">
|
||||
{/* Tree header */}
|
||||
<div className="flex-shrink-0 h-11 border-b border-[#1a2535] flex items-center justify-between px-4 bg-[#0a0c10]/90">
|
||||
<div className="flex items-center gap-2 text-xs text-[#8a95a5]">
|
||||
<Bot className="w-4 h-4 text-[#5a6577]" />
|
||||
<span className="uppercase font-bold tracking-[0.15em] text-[#d0d8e8]">Agent Tree</span>
|
||||
<div className="flex-shrink-0 h-11 border-b border-border flex items-center justify-between px-4 cyber-bg-elevated/90">
|
||||
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
||||
<Bot className="w-4 h-4 text-muted-foreground" />
|
||||
<span className="uppercase font-bold tracking-[0.15em] text-foreground">Agent Tree</span>
|
||||
{agentTree && (
|
||||
<Badge variant="outline" className="h-5 px-1.5 text-[10px] border-[#2a3545] text-[#6a7587] font-mono bg-[#0d1015]">
|
||||
<Badge variant="outline" className="h-5 px-1.5 text-xs border-border text-muted-foreground font-mono bg-muted">
|
||||
{agentTree.total_agents}
|
||||
</Badge>
|
||||
)}
|
||||
|
|
@ -940,25 +940,25 @@ function AgentAuditPageContent() {
|
|||
{selectedAgentId && !showAllLogs && (
|
||||
<button
|
||||
onClick={() => selectAgent(null)}
|
||||
className="text-[10px] text-primary hover:text-primary/80 transition-colors font-mono uppercase tracking-wider"
|
||||
className="text-xs text-primary hover:text-primary/80 transition-colors font-mono uppercase tracking-wider"
|
||||
>
|
||||
Show All
|
||||
</button>
|
||||
)}
|
||||
{agentTree && agentTree.running_agents > 0 && (
|
||||
<div className="flex items-center gap-1.5 text-[#3dd68c]">
|
||||
<div className="flex items-center gap-1.5 text-emerald-400">
|
||||
<span className="relative flex h-1.5 w-1.5">
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-[#3dd68c] opacity-75"></span>
|
||||
<span className="relative inline-flex rounded-full h-1.5 w-1.5 bg-[#3dd68c] shadow-[0_0_6px_rgba(61,214,140,0.5)]"></span>
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"></span>
|
||||
<span className="relative inline-flex rounded-full h-1.5 w-1.5 bg-emerald-400 shadow-[0_0_6px_rgba(61,214,140,0.5)]"></span>
|
||||
</span>
|
||||
<span className="text-[10px] font-mono">{agentTree.running_agents}</span>
|
||||
<span className="text-xs font-mono">{agentTree.running_agents}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tree content */}
|
||||
<div className="flex-1 overflow-y-auto p-2 custom-scrollbar bg-[#060810]/50">
|
||||
<div className="flex-1 overflow-y-auto p-2 custom-scrollbar bg-background/50">
|
||||
{treeNodes.length > 0 ? (
|
||||
treeNodes.map(node => (
|
||||
<AgentTreeNodeItem
|
||||
|
|
@ -969,7 +969,7 @@ function AgentAuditPageContent() {
|
|||
/>
|
||||
))
|
||||
) : (
|
||||
<div className="h-full flex items-center justify-center text-[#4a5565] text-xs">
|
||||
<div className="h-full flex items-center justify-center text-muted-foreground text-xs">
|
||||
{isRunning ? (
|
||||
<div className="flex items-center gap-2">
|
||||
<Loader2 className="w-3 h-3 animate-spin" />
|
||||
|
|
@ -984,7 +984,7 @@ function AgentAuditPageContent() {
|
|||
</div>
|
||||
|
||||
{/* Bottom section - Details + Stats */}
|
||||
<div className="flex-shrink-0 p-3 space-y-3 max-h-[50%] overflow-y-auto custom-scrollbar">
|
||||
<div className="flex-shrink-0 p-3 space-y-3">
|
||||
{/* Agent detail panel */}
|
||||
{selectedAgentId && !showAllLogs && (
|
||||
<AgentDetailPanel
|
||||
|
|
|
|||
|
|
@ -235,17 +235,17 @@ export default function AuditRules() {
|
|||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-screen bg-[#0a0a0f]">
|
||||
<div className="flex items-center justify-center min-h-screen cyber-bg-elevated">
|
||||
<div className="text-center space-y-4">
|
||||
<div className="loading-spinner mx-auto" />
|
||||
<p className="text-gray-500 font-mono text-sm uppercase tracking-wider">加载中...</p>
|
||||
<p className="text-muted-foreground font-mono text-sm uppercase tracking-wider">加载中...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6 p-6 bg-[#0a0a0f] min-h-screen font-mono relative">
|
||||
<div className="space-y-6 p-6 cyber-bg-elevated min-h-screen font-mono relative">
|
||||
{/* Grid background */}
|
||||
<div className="absolute inset-0 cyber-grid-subtle pointer-events-none" />
|
||||
|
||||
|
|
@ -304,7 +304,7 @@ export default function AuditRules() {
|
|||
<div className="cyber-card p-0 relative z-10">
|
||||
<div className="cyber-card-header">
|
||||
<Terminal className="w-5 h-5 text-primary" />
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-white">审计规则管理</h3>
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-foreground">审计规则管理</h3>
|
||||
<div className="ml-auto flex gap-2">
|
||||
<Button variant="outline" onClick={() => setShowImportDialog(true)} className="cyber-btn-outline h-9">
|
||||
<Upload className="w-4 h-4 mr-2" />
|
||||
|
|
@ -336,26 +336,26 @@ export default function AuditRules() {
|
|||
ruleSets.map(ruleSet => (
|
||||
<div key={ruleSet.id} className={`cyber-card p-0 ${!ruleSet.is_active ? 'opacity-60' : ''}`}>
|
||||
{/* Rule Set Header */}
|
||||
<div className="p-6 border-b border-gray-800">
|
||||
<div className="p-6 border-b border-border">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-4 cursor-pointer" onClick={() => toggleExpand(ruleSet.id)}>
|
||||
<div className="w-10 h-10 bg-gray-800 border border-gray-700 flex items-center justify-center rounded">
|
||||
{expandedSets.has(ruleSet.id) ? <ChevronDown className="w-5 h-5 text-gray-400" /> : <ChevronRight className="w-5 h-5 text-gray-400" />}
|
||||
<div className="w-10 h-10 bg-muted border border-border flex items-center justify-center rounded">
|
||||
{expandedSets.has(ruleSet.id) ? <ChevronDown className="w-5 h-5 text-muted-foreground" /> : <ChevronRight className="w-5 h-5 text-muted-foreground" />}
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-bold text-lg text-white uppercase flex items-center gap-2">
|
||||
<h3 className="font-bold text-lg text-foreground uppercase flex items-center gap-2">
|
||||
{ruleSet.name}
|
||||
{ruleSet.is_system && <Badge className="cyber-badge-info">系统</Badge>}
|
||||
{ruleSet.is_default && <Badge className="cyber-badge-success">默认</Badge>}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-500">{ruleSet.description}</p>
|
||||
<p className="text-sm text-muted-foreground">{ruleSet.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<Badge className="cyber-badge-muted">{LANGUAGES.find(l => l.value === ruleSet.language)?.label}</Badge>
|
||||
<Badge className="cyber-badge-muted">{RULE_TYPES.find(t => t.value === ruleSet.rule_type)?.label}</Badge>
|
||||
<span className="text-sm font-mono text-gray-400 px-3 py-1 bg-gray-800 border border-gray-700 rounded">
|
||||
<span className="text-sm font-mono text-muted-foreground px-3 py-1 bg-muted border border-border rounded">
|
||||
{ruleSet.enabled_rules_count}/{ruleSet.rules_count} 启用
|
||||
</span>
|
||||
<Button variant="ghost" size="icon" onClick={() => handleExport(ruleSet)} className="cyber-btn-ghost h-9 w-9">
|
||||
|
|
@ -391,19 +391,19 @@ export default function AuditRules() {
|
|||
const severityInfo = getSeverityInfo(rule.severity);
|
||||
const CategoryIcon = categoryInfo.icon;
|
||||
return (
|
||||
<div key={rule.id} className={`cyber-card p-4 hover:border-gray-700 transition-all ${!rule.enabled ? 'opacity-50' : ''}`}>
|
||||
<div key={rule.id} className={`cyber-card p-4 hover:border-border transition-all ${!rule.enabled ? 'opacity-50' : ''}`}>
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className={`w-10 h-10 ${categoryInfo.bg} border border-gray-700 flex items-center justify-center rounded`}>
|
||||
<div className={`w-10 h-10 ${categoryInfo.bg} border border-border flex items-center justify-center rounded`}>
|
||||
<CategoryIcon className={`w-5 h-5 ${categoryInfo.color}`} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<span className="font-mono text-xs bg-gray-800 text-primary px-2 py-0.5 rounded">{rule.rule_code}</span>
|
||||
<span className="font-bold uppercase text-gray-200">{rule.name}</span>
|
||||
<span className="font-mono text-xs bg-muted text-primary px-2 py-0.5 rounded">{rule.rule_code}</span>
|
||||
<span className="font-bold uppercase text-foreground">{rule.name}</span>
|
||||
<Badge className={severityInfo.color}>{severityInfo.label}</Badge>
|
||||
</div>
|
||||
{rule.description && <p className="text-sm text-gray-500 mb-2">{rule.description}</p>}
|
||||
{rule.description && <p className="text-sm text-muted-foreground mb-2">{rule.description}</p>}
|
||||
{rule.reference_url && (
|
||||
<a href={rule.reference_url} target="_blank" rel="noopener noreferrer" className="text-sm text-primary hover:underline flex items-center gap-1">
|
||||
参考链接 <ExternalLink className="w-3 h-3" />
|
||||
|
|
@ -435,9 +435,9 @@ export default function AuditRules() {
|
|||
|
||||
{/* Create Rule Set Dialog */}
|
||||
<Dialog open={showCreateDialog} onOpenChange={setShowCreateDialog}>
|
||||
<DialogContent className="!w-[min(90vw,500px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 bg-[#0c0c12] border border-gray-800 rounded-lg">
|
||||
<DialogHeader className="px-6 py-4 border-b border-gray-800 flex-shrink-0 bg-gray-900/50">
|
||||
<DialogTitle className="flex items-center gap-3 font-mono text-white">
|
||||
<DialogContent className="!w-[min(90vw,500px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 cyber-dialog border border-border rounded-lg">
|
||||
<DialogHeader className="px-6 py-4 border-b border-border flex-shrink-0 bg-muted">
|
||||
<DialogTitle className="flex items-center gap-3 font-mono text-foreground">
|
||||
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
||||
<Terminal className="w-5 h-5 text-primary" />
|
||||
</div>
|
||||
|
|
@ -446,35 +446,35 @@ export default function AuditRules() {
|
|||
</DialogHeader>
|
||||
<div className="flex-1 overflow-y-auto p-6 space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">名称 *</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">名称 *</Label>
|
||||
<Input value={ruleSetForm.name} onChange={e => setRuleSetForm({ ...ruleSetForm, name: e.target.value })} placeholder="规则集名称" className="cyber-input" />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">描述</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">描述</Label>
|
||||
<Textarea value={ruleSetForm.description} onChange={e => setRuleSetForm({ ...ruleSetForm, description: e.target.value })} placeholder="规则集描述" className="cyber-input" />
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">适用语言</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">适用语言</Label>
|
||||
<Select value={ruleSetForm.language} onValueChange={v => setRuleSetForm({ ...ruleSetForm, language: v })}>
|
||||
<SelectTrigger className="cyber-input"><SelectValue /></SelectTrigger>
|
||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
||||
<SelectContent className="cyber-dialog border-border">
|
||||
{LANGUAGES.map(l => <SelectItem key={l.value} value={l.value}>{l.label}</SelectItem>)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">规则类型</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">规则类型</Label>
|
||||
<Select value={ruleSetForm.rule_type} onValueChange={v => setRuleSetForm({ ...ruleSetForm, rule_type: v })}>
|
||||
<SelectTrigger className="cyber-input"><SelectValue /></SelectTrigger>
|
||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
||||
<SelectContent className="cyber-dialog border-border">
|
||||
{RULE_TYPES.map(t => <SelectItem key={t.value} value={t.value}>{t.label}</SelectItem>)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter className="flex-shrink-0 flex justify-end gap-3 px-6 py-4 bg-gray-900/50 border-t border-gray-800">
|
||||
<DialogFooter className="flex-shrink-0 flex justify-end gap-3 px-6 py-4 bg-muted border-t border-border">
|
||||
<Button variant="outline" onClick={() => setShowCreateDialog(false)} className="cyber-btn-outline">取消</Button>
|
||||
<Button onClick={handleCreateRuleSet} className="cyber-btn-primary">创建</Button>
|
||||
</DialogFooter>
|
||||
|
|
@ -483,9 +483,9 @@ export default function AuditRules() {
|
|||
|
||||
{/* Edit Rule Set Dialog */}
|
||||
<Dialog open={showEditDialog} onOpenChange={setShowEditDialog}>
|
||||
<DialogContent className="!w-[min(90vw,500px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 bg-[#0c0c12] border border-gray-800 rounded-lg">
|
||||
<DialogHeader className="px-6 py-4 border-b border-gray-800 flex-shrink-0 bg-gray-900/50">
|
||||
<DialogTitle className="flex items-center gap-3 font-mono text-white">
|
||||
<DialogContent className="!w-[min(90vw,500px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 cyber-dialog border border-border rounded-lg">
|
||||
<DialogHeader className="px-6 py-4 border-b border-border flex-shrink-0 bg-muted">
|
||||
<DialogTitle className="flex items-center gap-3 font-mono text-foreground">
|
||||
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
||||
<Edit className="w-5 h-5 text-primary" />
|
||||
</div>
|
||||
|
|
@ -494,31 +494,31 @@ export default function AuditRules() {
|
|||
</DialogHeader>
|
||||
<div className="flex-1 overflow-y-auto p-6 space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">名称</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">名称</Label>
|
||||
<Input value={ruleSetForm.name} onChange={e => setRuleSetForm({ ...ruleSetForm, name: e.target.value })} className="cyber-input" />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">描述</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">描述</Label>
|
||||
<Textarea value={ruleSetForm.description} onChange={e => setRuleSetForm({ ...ruleSetForm, description: e.target.value })} className="cyber-input" />
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">适用语言</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">适用语言</Label>
|
||||
<Select value={ruleSetForm.language} onValueChange={v => setRuleSetForm({ ...ruleSetForm, language: v })}>
|
||||
<SelectTrigger className="cyber-input"><SelectValue /></SelectTrigger>
|
||||
<SelectContent className="bg-[#0c0c12] border-gray-700">{LANGUAGES.map(l => <SelectItem key={l.value} value={l.value}>{l.label}</SelectItem>)}</SelectContent>
|
||||
<SelectContent className="cyber-dialog border-border">{LANGUAGES.map(l => <SelectItem key={l.value} value={l.value}>{l.label}</SelectItem>)}</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">规则类型</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">规则类型</Label>
|
||||
<Select value={ruleSetForm.rule_type} onValueChange={v => setRuleSetForm({ ...ruleSetForm, rule_type: v })}>
|
||||
<SelectTrigger className="cyber-input"><SelectValue /></SelectTrigger>
|
||||
<SelectContent className="bg-[#0c0c12] border-gray-700">{RULE_TYPES.map(t => <SelectItem key={t.value} value={t.value}>{t.label}</SelectItem>)}</SelectContent>
|
||||
<SelectContent className="cyber-dialog border-border">{RULE_TYPES.map(t => <SelectItem key={t.value} value={t.value}>{t.label}</SelectItem>)}</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter className="flex-shrink-0 flex justify-end gap-3 px-6 py-4 bg-gray-900/50 border-t border-gray-800">
|
||||
<DialogFooter className="flex-shrink-0 flex justify-end gap-3 px-6 py-4 bg-muted border-t border-border">
|
||||
<Button variant="outline" onClick={() => setShowEditDialog(false)} className="cyber-btn-outline">取消</Button>
|
||||
<Button onClick={handleUpdateRuleSet} className="cyber-btn-primary">保存</Button>
|
||||
</DialogFooter>
|
||||
|
|
@ -527,9 +527,9 @@ export default function AuditRules() {
|
|||
|
||||
{/* Rule Edit Dialog */}
|
||||
<Dialog open={showRuleDialog} onOpenChange={setShowRuleDialog}>
|
||||
<DialogContent className="!w-[min(90vw,700px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 bg-[#0c0c12] border border-gray-800 rounded-lg">
|
||||
<DialogHeader className="px-6 py-4 border-b border-gray-800 flex-shrink-0 bg-gray-900/50">
|
||||
<DialogTitle className="flex items-center gap-3 font-mono text-white">
|
||||
<DialogContent className="!w-[min(90vw,700px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 cyber-dialog border border-border rounded-lg">
|
||||
<DialogHeader className="px-6 py-4 border-b border-border flex-shrink-0 bg-muted">
|
||||
<DialogTitle className="flex items-center gap-3 font-mono text-foreground">
|
||||
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
||||
<Code className="w-5 h-5 text-primary" />
|
||||
</div>
|
||||
|
|
@ -539,48 +539,48 @@ export default function AuditRules() {
|
|||
<div className="flex-1 overflow-y-auto p-6 space-y-4">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">规则代码 *</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">规则代码 *</Label>
|
||||
<Input value={ruleForm.rule_code} onChange={e => setRuleForm({ ...ruleForm, rule_code: e.target.value })} placeholder="如 SEC001" className="cyber-input" />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">规则名称 *</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">规则名称 *</Label>
|
||||
<Input value={ruleForm.name} onChange={e => setRuleForm({ ...ruleForm, name: e.target.value })} placeholder="规则名称" className="cyber-input" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">描述</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">描述</Label>
|
||||
<Textarea value={ruleForm.description} onChange={e => setRuleForm({ ...ruleForm, description: e.target.value })} placeholder="规则描述" className="cyber-input" />
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">类别</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">类别</Label>
|
||||
<Select value={ruleForm.category} onValueChange={v => setRuleForm({ ...ruleForm, category: v })}>
|
||||
<SelectTrigger className="cyber-input"><SelectValue /></SelectTrigger>
|
||||
<SelectContent className="bg-[#0c0c12] border-gray-700">{CATEGORIES.map(c => <SelectItem key={c.value} value={c.value}>{c.label}</SelectItem>)}</SelectContent>
|
||||
<SelectContent className="cyber-dialog border-border">{CATEGORIES.map(c => <SelectItem key={c.value} value={c.value}>{c.label}</SelectItem>)}</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">严重程度</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">严重程度</Label>
|
||||
<Select value={ruleForm.severity} onValueChange={v => setRuleForm({ ...ruleForm, severity: v })}>
|
||||
<SelectTrigger className="cyber-input"><SelectValue /></SelectTrigger>
|
||||
<SelectContent className="bg-[#0c0c12] border-gray-700">{SEVERITIES.map(s => <SelectItem key={s.value} value={s.value}>{s.label}</SelectItem>)}</SelectContent>
|
||||
<SelectContent className="cyber-dialog border-border">{SEVERITIES.map(s => <SelectItem key={s.value} value={s.value}>{s.label}</SelectItem>)}</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">自定义检测提示词</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">自定义检测提示词</Label>
|
||||
<Textarea value={ruleForm.custom_prompt} onChange={e => setRuleForm({ ...ruleForm, custom_prompt: e.target.value })} placeholder="用于增强LLM检测的自定义提示词" rows={3} className="cyber-input" />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">修复建议</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">修复建议</Label>
|
||||
<Textarea value={ruleForm.fix_suggestion} onChange={e => setRuleForm({ ...ruleForm, fix_suggestion: e.target.value })} placeholder="修复建议模板" rows={2} className="cyber-input" />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">参考链接</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">参考链接</Label>
|
||||
<Input value={ruleForm.reference_url} onChange={e => setRuleForm({ ...ruleForm, reference_url: e.target.value })} placeholder="如 https://owasp.org/..." className="cyber-input" />
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter className="flex-shrink-0 flex justify-end gap-3 px-6 py-4 bg-gray-900/50 border-t border-gray-800">
|
||||
<DialogFooter className="flex-shrink-0 flex justify-end gap-3 px-6 py-4 bg-muted border-t border-border">
|
||||
<Button variant="outline" onClick={() => setShowRuleDialog(false)} className="cyber-btn-outline">取消</Button>
|
||||
<Button onClick={selectedRule ? handleUpdateRule : handleAddRule} className="cyber-btn-primary">{selectedRule ? '保存' : '添加'}</Button>
|
||||
</DialogFooter>
|
||||
|
|
@ -589,22 +589,22 @@ export default function AuditRules() {
|
|||
|
||||
{/* Import Dialog */}
|
||||
<Dialog open={showImportDialog} onOpenChange={setShowImportDialog}>
|
||||
<DialogContent className="!w-[min(90vw,700px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 bg-[#0c0c12] border border-gray-800 rounded-lg">
|
||||
<DialogHeader className="px-6 py-4 border-b border-gray-800 flex-shrink-0 bg-gray-900/50">
|
||||
<DialogTitle className="flex items-center gap-3 font-mono text-white">
|
||||
<DialogContent className="!w-[min(90vw,700px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 cyber-dialog border border-border rounded-lg">
|
||||
<DialogHeader className="px-6 py-4 border-b border-border flex-shrink-0 bg-muted">
|
||||
<DialogTitle className="flex items-center gap-3 font-mono text-foreground">
|
||||
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
||||
<Upload className="w-5 h-5 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-base font-bold uppercase tracking-wider">导入规则集</span>
|
||||
<p className="text-xs text-gray-500 font-normal mt-0.5">粘贴导出的 JSON 内容</p>
|
||||
<p className="text-xs text-muted-foreground font-normal mt-0.5">粘贴导出的 JSON 内容</p>
|
||||
</div>
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="flex-1 overflow-y-auto p-6">
|
||||
<Textarea value={importJson} onChange={e => setImportJson(e.target.value)} placeholder='{"name": "...", "rules": [...]}' rows={15} className="cyber-input font-mono text-sm text-emerald-400" />
|
||||
</div>
|
||||
<DialogFooter className="flex-shrink-0 flex justify-end gap-3 px-6 py-4 bg-gray-900/50 border-t border-gray-800">
|
||||
<DialogFooter className="flex-shrink-0 flex justify-end gap-3 px-6 py-4 bg-muted border-t border-border">
|
||||
<Button variant="outline" onClick={() => setShowImportDialog(false)} className="cyber-btn-outline">取消</Button>
|
||||
<Button onClick={handleImport} className="cyber-btn-primary">导入</Button>
|
||||
</DialogFooter>
|
||||
|
|
|
|||
|
|
@ -239,8 +239,8 @@ export default function AuditTasks() {
|
|||
case 'completed': return <CheckCircle className="w-4 h-4 text-emerald-400" />;
|
||||
case 'running': return <Activity className="w-4 h-4 text-sky-400" />;
|
||||
case 'failed': return <AlertTriangle className="w-4 h-4 text-rose-400" />;
|
||||
case 'cancelled': return <XCircle className="w-4 h-4 text-gray-400" />;
|
||||
default: return <Clock className="w-4 h-4 text-gray-400" />;
|
||||
case 'cancelled': return <XCircle className="w-4 h-4 text-muted-foreground" />;
|
||||
default: return <Clock className="w-4 h-4 text-muted-foreground" />;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -290,14 +290,14 @@ export default function AuditTasks() {
|
|||
<div className="flex items-center justify-center min-h-[60vh]">
|
||||
<div className="text-center space-y-4">
|
||||
<div className="loading-spinner mx-auto" />
|
||||
<p className="text-gray-500 font-mono text-sm uppercase tracking-wider">加载任务数据...</p>
|
||||
<p className="text-muted-foreground font-mono text-sm uppercase tracking-wider">加载任务数据...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6 p-6 bg-[#0a0a0f] min-h-screen font-mono relative">
|
||||
<div className="space-y-6 p-6 cyber-bg-elevated min-h-screen font-mono relative">
|
||||
{/* Grid background */}
|
||||
<div className="absolute inset-0 cyber-grid-subtle pointer-events-none" />
|
||||
|
||||
|
|
@ -311,7 +311,7 @@ export default function AuditTasks() {
|
|||
transition-all duration-300 border-2 overflow-hidden
|
||||
${activeTab === "agent"
|
||||
? "bg-gradient-to-br from-primary/20 via-primary/10 to-transparent border-primary shadow-lg shadow-primary/20"
|
||||
: "bg-gray-900/50 border-gray-800 hover:border-primary/50 hover:bg-gray-900/80"
|
||||
: "bg-muted border-border hover:border-primary/50 hover:bg-card/80"
|
||||
}
|
||||
`}
|
||||
>
|
||||
|
|
@ -326,17 +326,17 @@ export default function AuditTasks() {
|
|||
transition-all duration-300
|
||||
${activeTab === "agent"
|
||||
? "bg-primary/30 shadow-lg shadow-primary/30"
|
||||
: "bg-gray-800/80 group-hover:bg-primary/20"
|
||||
: "bg-muted/80 group-hover:bg-primary/20"
|
||||
}
|
||||
`}>
|
||||
<Bot className={`w-7 h-7 transition-colors duration-300 ${activeTab === "agent" ? "text-primary" : "text-gray-400 group-hover:text-primary"
|
||||
<Bot className={`w-7 h-7 transition-colors duration-300 ${activeTab === "agent" ? "text-primary" : "text-muted-foreground group-hover:text-primary"
|
||||
}`} />
|
||||
</div>
|
||||
|
||||
{/* 内容区域 */}
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<h3 className={`text-lg font-mono font-bold uppercase tracking-[0.15em] transition-colors duration-300 ${activeTab === "agent" ? "text-primary text-glow-primary" : "text-gray-300 group-hover:text-primary"}`}>
|
||||
<h3 className={`text-lg font-mono font-bold uppercase tracking-[0.15em] transition-colors duration-300 ${activeTab === "agent" ? "text-primary text-glow-primary" : "text-foreground group-hover:text-primary"}`}>
|
||||
Agent 智能审计
|
||||
</h3>
|
||||
{agentStats.running > 0 && (
|
||||
|
|
@ -345,20 +345,20 @@ export default function AuditTasks() {
|
|||
</span>
|
||||
)}
|
||||
{activeTab === "agent" && (
|
||||
<span className="px-2 py-0.5 text-xs font-bold rounded-full bg-primary text-black">
|
||||
<span className="px-2 py-0.5 text-xs font-bold rounded-full bg-primary text-background">
|
||||
当前
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p className={`text-sm transition-colors duration-300 ${activeTab === "agent" ? "text-gray-300" : "text-gray-500 group-hover:text-gray-400"
|
||||
<p className={`text-sm transition-colors duration-300 ${activeTab === "agent" ? "text-foreground" : "text-muted-foreground group-hover:text-muted-foreground"
|
||||
}`}>
|
||||
LLM 驱动的多 Agent 协同深度审计,支持智能漏洞挖掘与验证
|
||||
</p>
|
||||
|
||||
{/* 统计数据 */}
|
||||
<div className="flex items-center gap-4 mt-3 text-xs">
|
||||
<span className={`transition-colors duration-300 ${activeTab === "agent" ? "text-gray-400" : "text-gray-600"}`}>
|
||||
共 <span className="font-bold text-white">{agentStats.total}</span> 个任务
|
||||
<span className={`transition-colors duration-300 ${activeTab === "agent" ? "text-muted-foreground" : "text-muted-foreground"}`}>
|
||||
共 <span className="font-bold text-foreground">{agentStats.total}</span> 个任务
|
||||
</span>
|
||||
<span className="text-emerald-400">
|
||||
<CheckCircle className="w-3 h-3 inline mr-1" />
|
||||
|
|
@ -388,7 +388,7 @@ export default function AuditTasks() {
|
|||
transition-all duration-300 border-2 overflow-hidden
|
||||
${activeTab === "regular"
|
||||
? "bg-gradient-to-br from-cyan-500/20 via-cyan-500/10 to-transparent border-cyan-500 shadow-lg shadow-cyan-500/20"
|
||||
: "bg-gray-900/50 border-gray-800 hover:border-cyan-500/50 hover:bg-gray-900/80"
|
||||
: "bg-muted border-border hover:border-cyan-500/50 hover:bg-card/80"
|
||||
}
|
||||
`}
|
||||
>
|
||||
|
|
@ -403,17 +403,17 @@ export default function AuditTasks() {
|
|||
transition-all duration-300
|
||||
${activeTab === "regular"
|
||||
? "bg-cyan-500/30 shadow-lg shadow-cyan-500/30"
|
||||
: "bg-gray-800/80 group-hover:bg-cyan-500/20"
|
||||
: "bg-muted/80 group-hover:bg-cyan-500/20"
|
||||
}
|
||||
`}>
|
||||
<Zap className={`w-7 h-7 transition-colors duration-300 ${activeTab === "regular" ? "text-cyan-400" : "text-gray-400 group-hover:text-cyan-400"
|
||||
<Zap className={`w-7 h-7 transition-colors duration-300 ${activeTab === "regular" ? "text-cyan-400" : "text-muted-foreground group-hover:text-cyan-400"
|
||||
}`} />
|
||||
</div>
|
||||
|
||||
{/* 内容区域 */}
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<h3 className={`text-lg font-mono font-bold uppercase tracking-[0.15em] transition-colors duration-300 ${activeTab === "regular" ? "text-cyan-400 text-glow-cyan" : "text-gray-300 group-hover:text-cyan-400"}`}>
|
||||
<h3 className={`text-lg font-mono font-bold uppercase tracking-[0.15em] transition-colors duration-300 ${activeTab === "regular" ? "text-cyan-400 text-glow-cyan" : "text-foreground group-hover:text-cyan-400"}`}>
|
||||
快速扫描任务
|
||||
</h3>
|
||||
{regularStats.running > 0 && (
|
||||
|
|
@ -422,20 +422,20 @@ export default function AuditTasks() {
|
|||
</span>
|
||||
)}
|
||||
{activeTab === "regular" && (
|
||||
<span className="px-2 py-0.5 text-xs font-bold rounded-full bg-cyan-500 text-black">
|
||||
<span className="px-2 py-0.5 text-xs font-bold rounded-full bg-cyan-500 text-background">
|
||||
当前
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p className={`text-sm transition-colors duration-300 ${activeTab === "regular" ? "text-gray-300" : "text-gray-500 group-hover:text-gray-400"
|
||||
<p className={`text-sm transition-colors duration-300 ${activeTab === "regular" ? "text-foreground" : "text-muted-foreground group-hover:text-muted-foreground"
|
||||
}`}>
|
||||
传统规则引擎驱动的快速代码扫描,适合大规模批量检测
|
||||
</p>
|
||||
|
||||
{/* 统计数据 */}
|
||||
<div className="flex items-center gap-4 mt-3 text-xs">
|
||||
<span className={`transition-colors duration-300 ${activeTab === "regular" ? "text-gray-400" : "text-gray-600"}`}>
|
||||
共 <span className="font-bold text-white">{regularStats.total}</span> 个任务
|
||||
<span className={`transition-colors duration-300 ${activeTab === "regular" ? "text-muted-foreground" : "text-muted-foreground"}`}>
|
||||
共 <span className="font-bold text-foreground">{regularStats.total}</span> 个任务
|
||||
</span>
|
||||
<span className="text-emerald-400">
|
||||
<CheckCircle className="w-3 h-3 inline mr-1" />
|
||||
|
|
@ -513,7 +513,7 @@ export default function AuditTasks() {
|
|||
<div className="cyber-card p-4 relative z-10">
|
||||
<div className="flex flex-col md:flex-row items-center gap-4">
|
||||
<div className="flex-1 relative w-full">
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500 w-4 h-4 z-10" />
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground w-4 h-4 z-10" />
|
||||
<Input
|
||||
placeholder={activeTab === "agent" ? "搜索Agent任务名称..." : "搜索项目名称或任务类型..."}
|
||||
value={searchTerm}
|
||||
|
|
@ -544,21 +544,21 @@ export default function AuditTasks() {
|
|||
<Button
|
||||
size="sm"
|
||||
onClick={() => setStatusFilter("running")}
|
||||
className={`h-10 ${statusFilter === "running" ? "bg-sky-500/90 border-sky-500/50 text-white hover:bg-sky-500" : "cyber-btn-outline"}`}
|
||||
className={`h-10 ${statusFilter === "running" ? "bg-sky-500/90 border-sky-500/50 text-foreground hover:bg-sky-500" : "cyber-btn-outline"}`}
|
||||
>
|
||||
运行中
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={() => setStatusFilter("completed")}
|
||||
className={`h-10 ${statusFilter === "completed" ? "bg-emerald-500/90 border-emerald-500/50 text-white hover:bg-emerald-500" : "cyber-btn-outline"}`}
|
||||
className={`h-10 ${statusFilter === "completed" ? "bg-emerald-500/90 border-emerald-500/50 text-foreground hover:bg-emerald-500" : "cyber-btn-outline"}`}
|
||||
>
|
||||
已完成
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={() => setStatusFilter("failed")}
|
||||
className={`h-10 ${statusFilter === "failed" ? "bg-rose-500/90 border-rose-500/50 text-white hover:bg-rose-500" : "cyber-btn-outline"}`}
|
||||
className={`h-10 ${statusFilter === "failed" ? "bg-rose-500/90 border-rose-500/50 text-foreground hover:bg-rose-500" : "cyber-btn-outline"}`}
|
||||
>
|
||||
失败
|
||||
</Button>
|
||||
|
|
@ -574,24 +574,24 @@ export default function AuditTasks() {
|
|||
{filteredAgentTasks.map((task) => (
|
||||
<div key={task.id} className="cyber-card p-6">
|
||||
{/* Task Header */}
|
||||
<div className="flex items-center justify-between mb-4 pb-4 border-b border-gray-800">
|
||||
<div className="flex items-center justify-between mb-4 pb-4 border-b border-border">
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className={`w-12 h-12 rounded-lg flex items-center justify-center ${task.status === 'completed' ? 'bg-emerald-500/20' :
|
||||
task.status === 'running' ? 'bg-sky-500/20' :
|
||||
task.status === 'failed' ? 'bg-rose-500/20' :
|
||||
'bg-gray-800/50'
|
||||
'bg-muted'
|
||||
}`}>
|
||||
<Bot className={`w-6 h-6 ${task.status === 'completed' ? 'text-emerald-400' :
|
||||
task.status === 'running' ? 'text-sky-400' :
|
||||
task.status === 'failed' ? 'text-rose-400' :
|
||||
'text-gray-400'
|
||||
'text-muted-foreground'
|
||||
}`} />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-bold text-xl text-white uppercase tracking-wide">
|
||||
<h3 className="font-bold text-xl text-foreground uppercase tracking-wide">
|
||||
{task.name || 'Agent审计任务'}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-500 font-mono">
|
||||
<p className="text-sm text-muted-foreground font-mono">
|
||||
{task.current_phase || task.task_type}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -611,25 +611,25 @@ export default function AuditTasks() {
|
|||
|
||||
{/* Stats Grid */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-5 gap-4 mb-4 font-mono">
|
||||
<div className="text-center p-3 bg-gray-900/50 rounded-lg border border-gray-800">
|
||||
<p className="text-2xl font-bold text-white">{task.total_files}</p>
|
||||
<p className="text-xs text-gray-500 uppercase">文件数</p>
|
||||
<div className="text-center p-3 bg-muted rounded-lg border border-border">
|
||||
<p className="text-2xl font-bold text-foreground">{task.total_files}</p>
|
||||
<p className="text-xs text-muted-foreground uppercase">文件数</p>
|
||||
</div>
|
||||
<div className="text-center p-3 bg-gray-900/50 rounded-lg border border-gray-800">
|
||||
<p className="text-2xl font-bold text-white">{task.analyzed_files}</p>
|
||||
<p className="text-xs text-gray-500 uppercase">已分析</p>
|
||||
<div className="text-center p-3 bg-muted rounded-lg border border-border">
|
||||
<p className="text-2xl font-bold text-foreground">{task.analyzed_files}</p>
|
||||
<p className="text-xs text-muted-foreground uppercase">已分析</p>
|
||||
</div>
|
||||
<div className="text-center p-3 bg-gray-900/50 rounded-lg border border-gray-800">
|
||||
<div className="text-center p-3 bg-muted rounded-lg border border-border">
|
||||
<p className="text-2xl font-bold text-amber-400">{task.findings_count}</p>
|
||||
<p className="text-xs text-gray-500 uppercase">发现问题</p>
|
||||
<p className="text-xs text-muted-foreground uppercase">发现问题</p>
|
||||
</div>
|
||||
<div className="text-center p-3 bg-gray-900/50 rounded-lg border border-gray-800">
|
||||
<div className="text-center p-3 bg-muted rounded-lg border border-border">
|
||||
<p className="text-2xl font-bold text-sky-400">{task.tool_calls_count || 0}</p>
|
||||
<p className="text-xs text-gray-500 uppercase">工具调用</p>
|
||||
<p className="text-xs text-muted-foreground uppercase">工具调用</p>
|
||||
</div>
|
||||
<div className="text-center p-3 bg-gray-900/50 rounded-lg border border-gray-800">
|
||||
<div className="text-center p-3 bg-muted rounded-lg border border-border">
|
||||
<p className="text-2xl font-bold text-primary">{task.security_score?.toFixed(1) || '-'}</p>
|
||||
<p className="text-xs text-gray-500 uppercase">安全评分</p>
|
||||
<p className="text-xs text-muted-foreground uppercase">安全评分</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -654,25 +654,25 @@ export default function AuditTasks() {
|
|||
{/* Progress Bar */}
|
||||
<div className="mb-4 font-mono">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-sm font-bold text-gray-400 uppercase">审计进度</span>
|
||||
<span className="text-sm text-gray-500">
|
||||
<span className="text-sm font-bold text-muted-foreground uppercase">审计进度</span>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{task.analyzed_files || 0} / {task.total_files || 0} 文件
|
||||
</span>
|
||||
</div>
|
||||
<Progress
|
||||
value={task.progress_percentage || 0}
|
||||
className="h-2 bg-gray-800 [&>div]:bg-primary"
|
||||
className="h-2 bg-muted [&>div]:bg-primary"
|
||||
/>
|
||||
<div className="text-right mt-1">
|
||||
<span className="text-xs text-gray-500">
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{(task.progress_percentage || 0).toFixed(0)}% 完成
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Task Footer */}
|
||||
<div className="flex items-center justify-between pt-4 border-t border-gray-800">
|
||||
<div className="flex items-center space-x-6 text-sm text-gray-500 font-mono">
|
||||
<div className="flex items-center justify-between pt-4 border-t border-border">
|
||||
<div className="flex items-center space-x-6 text-sm text-muted-foreground font-mono">
|
||||
<div className="flex items-center">
|
||||
<Calendar className="w-4 h-4 mr-2" />
|
||||
{formatDate(task.created_at)}
|
||||
|
|
@ -684,7 +684,7 @@ export default function AuditTasks() {
|
|||
</div>
|
||||
)}
|
||||
{task.tokens_used > 0 && (
|
||||
<div className="flex items-center text-gray-600">
|
||||
<div className="flex items-center text-muted-foreground">
|
||||
<span>{task.tokens_used.toLocaleString()} tokens</span>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -695,14 +695,14 @@ export default function AuditTasks() {
|
|||
<>
|
||||
{/* 🔥 查看终端实时流按钮 */}
|
||||
<Link to={`/agent-audit/${task.id}`}>
|
||||
<Button size="sm" className="cyber-btn bg-sky-500/90 border-sky-500/50 text-white hover:bg-sky-500 h-9">
|
||||
<Button size="sm" className="cyber-btn bg-sky-500/90 border-sky-500/50 text-foreground hover:bg-sky-500 h-9">
|
||||
<Terminal className="w-4 h-4 mr-2" />
|
||||
查看实时流
|
||||
</Button>
|
||||
</Link>
|
||||
<Button
|
||||
size="sm"
|
||||
className="cyber-btn bg-rose-500/90 border-rose-500/50 text-white hover:bg-rose-500 h-9"
|
||||
className="cyber-btn bg-rose-500/90 border-rose-500/50 text-foreground hover:bg-rose-500 h-9"
|
||||
onClick={() => handleCancelAgentTask(task.id)}
|
||||
disabled={cancellingAgentTaskId === task.id}
|
||||
>
|
||||
|
|
@ -725,11 +725,11 @@ export default function AuditTasks() {
|
|||
</div>
|
||||
) : (
|
||||
<div className="cyber-card p-16 text-center relative z-10 border-dashed">
|
||||
<Bot className="w-16 h-16 text-gray-600 mx-auto mb-4" />
|
||||
<h3 className="text-xl font-bold text-gray-300 mb-2 uppercase">
|
||||
<Bot className="w-16 h-16 text-muted-foreground mx-auto mb-4" />
|
||||
<h3 className="text-xl font-bold text-foreground mb-2 uppercase">
|
||||
{searchTerm || statusFilter !== "all" ? '未找到匹配的Agent任务' : '暂无Agent审计任务'}
|
||||
</h3>
|
||||
<p className="text-gray-500 mb-6 font-mono">
|
||||
<p className="text-muted-foreground mb-6 font-mono">
|
||||
{searchTerm || statusFilter !== "all" ? '尝试调整搜索条件或筛选器' : '创建第一个Agent审计任务开始智能安全审计'}
|
||||
</p>
|
||||
{!searchTerm && statusFilter === "all" && (
|
||||
|
|
@ -751,20 +751,20 @@ export default function AuditTasks() {
|
|||
{filteredTasks.map((task) => (
|
||||
<div key={task.id} className="cyber-card p-6">
|
||||
{/* Task Header */}
|
||||
<div className="flex items-center justify-between mb-4 pb-4 border-b border-gray-800">
|
||||
<div className="flex items-center justify-between mb-4 pb-4 border-b border-border">
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className={`w-12 h-12 rounded-lg flex items-center justify-center ${task.status === 'completed' ? 'bg-emerald-500/20' :
|
||||
task.status === 'running' ? 'bg-sky-500/20' :
|
||||
task.status === 'failed' ? 'bg-rose-500/20' :
|
||||
'bg-gray-800/50'
|
||||
'bg-muted'
|
||||
}`}>
|
||||
{getStatusIcon(task.status)}
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-bold text-xl text-white uppercase tracking-wide">
|
||||
<h3 className="font-bold text-xl text-foreground uppercase tracking-wide">
|
||||
{task.project?.name || '未知项目'}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-500 font-mono">
|
||||
<p className="text-sm text-muted-foreground font-mono">
|
||||
{task.task_type === 'repository' ? '仓库审计任务' : '即时分析任务'}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -774,46 +774,46 @@ export default function AuditTasks() {
|
|||
|
||||
{/* Stats Grid */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-4 font-mono">
|
||||
<div className="text-center p-3 bg-gray-900/50 rounded-lg border border-gray-800">
|
||||
<p className="text-2xl font-bold text-white">{task.total_files}</p>
|
||||
<p className="text-xs text-gray-500 uppercase">文件数</p>
|
||||
<div className="text-center p-3 bg-muted rounded-lg border border-border">
|
||||
<p className="text-2xl font-bold text-foreground">{task.total_files}</p>
|
||||
<p className="text-xs text-muted-foreground uppercase">文件数</p>
|
||||
</div>
|
||||
<div className="text-center p-3 bg-gray-900/50 rounded-lg border border-gray-800">
|
||||
<p className="text-2xl font-bold text-white">{task.total_lines.toLocaleString()}</p>
|
||||
<p className="text-xs text-gray-500 uppercase">代码行数</p>
|
||||
<div className="text-center p-3 bg-muted rounded-lg border border-border">
|
||||
<p className="text-2xl font-bold text-foreground">{task.total_lines.toLocaleString()}</p>
|
||||
<p className="text-xs text-muted-foreground uppercase">代码行数</p>
|
||||
</div>
|
||||
<div className="text-center p-3 bg-gray-900/50 rounded-lg border border-gray-800">
|
||||
<div className="text-center p-3 bg-muted rounded-lg border border-border">
|
||||
<p className="text-2xl font-bold text-amber-400">{task.issues_count}</p>
|
||||
<p className="text-xs text-gray-500 uppercase">发现问题</p>
|
||||
<p className="text-xs text-muted-foreground uppercase">发现问题</p>
|
||||
</div>
|
||||
<div className="text-center p-3 bg-gray-900/50 rounded-lg border border-gray-800">
|
||||
<div className="text-center p-3 bg-muted rounded-lg border border-border">
|
||||
<p className="text-2xl font-bold text-primary">{task.quality_score.toFixed(1)}</p>
|
||||
<p className="text-xs text-gray-500 uppercase">质量评分</p>
|
||||
<p className="text-xs text-muted-foreground uppercase">质量评分</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Progress Bar */}
|
||||
<div className="mb-4 font-mono">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-sm font-bold text-gray-400 uppercase">扫描进度</span>
|
||||
<span className="text-sm text-gray-500">
|
||||
<span className="text-sm font-bold text-muted-foreground uppercase">扫描进度</span>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{task.scanned_files || 0} / {task.total_files || 0} 文件
|
||||
</span>
|
||||
</div>
|
||||
<Progress
|
||||
value={calculateTaskProgress(task.scanned_files, task.total_files)}
|
||||
className="h-2 bg-gray-800 [&>div]:bg-primary"
|
||||
className="h-2 bg-muted [&>div]:bg-primary"
|
||||
/>
|
||||
<div className="text-right mt-1">
|
||||
<span className="text-xs text-gray-500">
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{calculateTaskProgress(task.scanned_files, task.total_files)}% 完成
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Task Footer */}
|
||||
<div className="flex items-center justify-between pt-4 border-t border-gray-800">
|
||||
<div className="flex items-center space-x-6 text-sm text-gray-500 font-mono">
|
||||
<div className="flex items-center justify-between pt-4 border-t border-border">
|
||||
<div className="flex items-center space-x-6 text-sm text-muted-foreground font-mono">
|
||||
<div className="flex items-center">
|
||||
<Calendar className="w-4 h-4 mr-2" />
|
||||
{formatDate(task.created_at)}
|
||||
|
|
@ -830,7 +830,7 @@ export default function AuditTasks() {
|
|||
{(task.status === 'running' || task.status === 'pending') && (
|
||||
<Button
|
||||
size="sm"
|
||||
className="cyber-btn bg-rose-500/90 border-rose-500/50 text-white hover:bg-rose-500 h-9"
|
||||
className="cyber-btn bg-rose-500/90 border-rose-500/50 text-foreground hover:bg-rose-500 h-9"
|
||||
onClick={() => handleCancelTask(task.id)}
|
||||
disabled={cancellingTaskId === task.id}
|
||||
>
|
||||
|
|
@ -859,11 +859,11 @@ export default function AuditTasks() {
|
|||
</div>
|
||||
) : (
|
||||
<div className="cyber-card p-16 text-center relative z-10 border-dashed">
|
||||
<Activity className="w-16 h-16 text-gray-600 mx-auto mb-4" />
|
||||
<h3 className="text-xl font-bold text-gray-300 mb-2 uppercase">
|
||||
<Activity className="w-16 h-16 text-muted-foreground mx-auto mb-4" />
|
||||
<h3 className="text-xl font-bold text-foreground mb-2 uppercase">
|
||||
{searchTerm || statusFilter !== "all" ? '未找到匹配的任务' : '暂无审计任务'}
|
||||
</h3>
|
||||
<p className="text-gray-500 mb-6 font-mono">
|
||||
<p className="text-muted-foreground mb-6 font-mono">
|
||||
{searchTerm || statusFilter !== "all" ? '尝试调整搜索条件或筛选器' : '创建第一个审计任务开始代码质量分析'}
|
||||
</p>
|
||||
{!searchTerm && statusFilter === "all" && (
|
||||
|
|
|
|||
|
|
@ -166,14 +166,14 @@ export default function Dashboard() {
|
|||
<div className="flex items-center justify-center min-h-[60vh]">
|
||||
<div className="text-center space-y-4">
|
||||
<div className="loading-spinner mx-auto" />
|
||||
<p className="text-gray-500 font-mono text-sm uppercase tracking-wider">加载数据中...</p>
|
||||
<p className="text-muted-foreground font-mono text-base uppercase tracking-wider">加载数据中...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6 p-6 bg-[#0a0a0f] min-h-screen font-mono relative">
|
||||
<div className="space-y-6 p-6 bg-background min-h-screen font-mono relative">
|
||||
{/* Grid background */}
|
||||
<div className="absolute inset-0 cyber-grid-subtle pointer-events-none" />
|
||||
|
||||
|
|
@ -182,7 +182,7 @@ export default function Dashboard() {
|
|||
<div className="relative z-10 cyber-card p-4 border-amber-500/30 bg-amber-500/5">
|
||||
<div className="flex items-start gap-3">
|
||||
<AlertTriangle className="w-5 h-5 text-amber-400 mt-0.5" />
|
||||
<div className="text-sm text-gray-300">
|
||||
<div className="text-sm text-foreground/80">
|
||||
当前使用<span className="text-amber-400 font-bold">演示模式</span>,显示的是模拟数据。
|
||||
<Link to="/admin" className="ml-2 text-primary font-bold hover:underline">
|
||||
前往配置 →
|
||||
|
|
@ -200,8 +200,8 @@ export default function Dashboard() {
|
|||
<div>
|
||||
<p className="stat-label">总项目数</p>
|
||||
<p className="stat-value">{stats?.total_projects || 0}</p>
|
||||
<p className="text-xs text-emerald-400 mt-1 flex items-center gap-1">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-emerald-400" />
|
||||
<p className="text-sm text-emerald-400 mt-1 flex items-center gap-1">
|
||||
<span className="w-2 h-2 rounded-full bg-emerald-400" />
|
||||
活跃: {stats?.active_projects || 0}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -217,8 +217,8 @@ export default function Dashboard() {
|
|||
<div>
|
||||
<p className="stat-label">审计任务</p>
|
||||
<p className="stat-value">{stats?.total_tasks || 0}</p>
|
||||
<p className="text-xs text-emerald-400 mt-1 flex items-center gap-1">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-emerald-400" />
|
||||
<p className="text-sm text-emerald-400 mt-1 flex items-center gap-1">
|
||||
<span className="w-2 h-2 rounded-full bg-emerald-400" />
|
||||
已完成: {stats?.completed_tasks || 0}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -234,8 +234,8 @@ export default function Dashboard() {
|
|||
<div>
|
||||
<p className="stat-label">发现问题</p>
|
||||
<p className="stat-value">{stats?.total_issues || 0}</p>
|
||||
<p className="text-xs text-amber-400 mt-1 flex items-center gap-1">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-amber-400" />
|
||||
<p className="text-sm text-amber-400 mt-1 flex items-center gap-1">
|
||||
<span className="w-2 h-2 rounded-full bg-amber-400" />
|
||||
已解决: {stats?.resolved_issues || 0}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -254,12 +254,12 @@ export default function Dashboard() {
|
|||
{stats?.avg_quality_score ? stats.avg_quality_score.toFixed(1) : '0.0'}
|
||||
</p>
|
||||
{stats?.avg_quality_score ? (
|
||||
<p className="text-xs text-emerald-400 mt-1 flex items-center gap-1">
|
||||
<TrendingUp className="w-3 h-3" />
|
||||
<p className="text-sm text-emerald-400 mt-1 flex items-center gap-1">
|
||||
<TrendingUp className="w-4 h-4" />
|
||||
持续改进
|
||||
</p>
|
||||
) : (
|
||||
<p className="text-xs text-gray-500 mt-1">暂无数据</p>
|
||||
<p className="text-sm text-muted-foreground mt-1">暂无数据</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="stat-icon text-violet-400">
|
||||
|
|
@ -284,25 +284,26 @@ export default function Dashboard() {
|
|||
{qualityTrendData.length > 0 ? (
|
||||
<ResponsiveContainer width="100%" height={220}>
|
||||
<LineChart data={qualityTrendData}>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="#1f1f2e" />
|
||||
<XAxis dataKey="date" stroke="#6b7280" fontSize={11} tick={{ fontFamily: 'monospace' }} />
|
||||
<YAxis stroke="#6b7280" fontSize={11} domain={[0, 100]} tick={{ fontFamily: 'monospace' }} />
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="var(--cyber-border)" />
|
||||
<XAxis dataKey="date" stroke="var(--cyber-text-muted)" fontSize={11} tick={{ fontFamily: 'monospace' }} />
|
||||
<YAxis stroke="var(--cyber-text-muted)" fontSize={11} domain={[0, 100]} tick={{ fontFamily: 'monospace' }} />
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
backgroundColor: '#0c0c12',
|
||||
border: '1px solid #2a2a35',
|
||||
backgroundColor: 'var(--cyber-bg-elevated)',
|
||||
border: '1px solid var(--cyber-border)',
|
||||
borderRadius: '4px',
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '12px'
|
||||
fontSize: '12px',
|
||||
color: 'var(--cyber-text)'
|
||||
}}
|
||||
/>
|
||||
<Line
|
||||
type="monotone"
|
||||
dataKey="score"
|
||||
stroke="#FF6B2C"
|
||||
stroke="hsl(var(--primary))"
|
||||
strokeWidth={2}
|
||||
dot={{ fill: '#FF6B2C', stroke: '#0c0c12', strokeWidth: 2, r: 4 }}
|
||||
activeDot={{ r: 6, fill: '#FF6B2C' }}
|
||||
dot={{ fill: 'hsl(var(--primary))', stroke: 'var(--cyber-bg)', strokeWidth: 2, r: 4 }}
|
||||
activeDot={{ r: 6, fill: 'hsl(var(--primary))' }}
|
||||
/>
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
|
|
@ -331,7 +332,7 @@ export default function Dashboard() {
|
|||
label={({ name, percent }) => `${name} ${(percent * 100).toFixed(0)}%`}
|
||||
outerRadius={70}
|
||||
dataKey="value"
|
||||
stroke="#0c0c12"
|
||||
stroke="var(--cyber-bg)"
|
||||
strokeWidth={2}
|
||||
>
|
||||
{issueTypeData.map((entry) => (
|
||||
|
|
@ -340,11 +341,12 @@ export default function Dashboard() {
|
|||
</Pie>
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
backgroundColor: '#0c0c12',
|
||||
border: '1px solid #2a2a35',
|
||||
backgroundColor: 'var(--cyber-bg-elevated)',
|
||||
border: '1px solid var(--cyber-border)',
|
||||
borderRadius: '4px',
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '12px'
|
||||
fontSize: '12px',
|
||||
color: 'var(--cyber-text)'
|
||||
}}
|
||||
/>
|
||||
</PieChart>
|
||||
|
|
@ -370,21 +372,33 @@ export default function Dashboard() {
|
|||
<Link
|
||||
key={project.id}
|
||||
to={`/projects/${project.id}`}
|
||||
className="block p-4 bg-gray-900/50 border border-gray-800/50 rounded-lg hover:bg-gray-800/50 hover:border-gray-700 transition-all group"
|
||||
className="block p-4 rounded-lg transition-all group"
|
||||
style={{
|
||||
background: 'var(--cyber-bg-elevated)',
|
||||
border: '1px solid var(--cyber-border)'
|
||||
}}
|
||||
onMouseOver={(e) => {
|
||||
e.currentTarget.style.background = 'var(--cyber-hover-bg)';
|
||||
e.currentTarget.style.borderColor = 'var(--cyber-border-accent)';
|
||||
}}
|
||||
onMouseOut={(e) => {
|
||||
e.currentTarget.style.background = 'var(--cyber-bg-elevated)';
|
||||
e.currentTarget.style.borderColor = 'var(--cyber-border)';
|
||||
}}
|
||||
>
|
||||
<div className="flex items-start justify-between mb-2">
|
||||
<h4 className="font-semibold text-gray-200 group-hover:text-primary transition-colors truncate">
|
||||
<h4 className="font-semibold text-foreground group-hover:text-primary transition-colors truncate">
|
||||
{project.name}
|
||||
</h4>
|
||||
<Badge className={`ml-2 flex-shrink-0 ${project.is_active ? 'cyber-badge-success' : 'cyber-badge-muted'}`}>
|
||||
{project.is_active ? '活跃' : '暂停'}
|
||||
</Badge>
|
||||
</div>
|
||||
<p className="text-xs text-gray-500 line-clamp-2 mb-3">
|
||||
<p className="text-sm text-muted-foreground line-clamp-2 mb-3">
|
||||
{project.description || '暂无描述'}
|
||||
</p>
|
||||
<div className="flex items-center text-xs text-gray-600">
|
||||
<Calendar className="w-3 h-3 mr-1" />
|
||||
<div className="flex items-center text-sm text-muted-foreground">
|
||||
<Calendar className="w-4 h-4 mr-1" />
|
||||
{new Date(project.created_at).toLocaleDateString('zh-CN')}
|
||||
</div>
|
||||
</Link>
|
||||
|
|
@ -405,7 +419,7 @@ export default function Dashboard() {
|
|||
<Clock className="w-5 h-5 text-emerald-400" />
|
||||
<h3 className="section-title">最近任务</h3>
|
||||
<Link to="/audit-tasks" className="ml-auto">
|
||||
<Button variant="ghost" size="sm" className="text-gray-400 hover:text-white">
|
||||
<Button variant="ghost" size="sm" className="text-muted-foreground hover:text-foreground">
|
||||
查看全部 <ArrowUpRight className="w-3 h-3 ml-1" />
|
||||
</Button>
|
||||
</Link>
|
||||
|
|
@ -416,7 +430,16 @@ export default function Dashboard() {
|
|||
<Link
|
||||
key={task.id}
|
||||
to={`/tasks/${task.id}`}
|
||||
className="flex items-center justify-between p-3 bg-gray-900/30 rounded-lg hover:bg-gray-800/50 transition-all group"
|
||||
className="flex items-center justify-between p-3 rounded-lg transition-all group"
|
||||
style={{
|
||||
background: 'var(--cyber-bg-elevated)',
|
||||
}}
|
||||
onMouseOver={(e) => {
|
||||
e.currentTarget.style.background = 'var(--cyber-hover-bg)';
|
||||
}}
|
||||
onMouseOut={(e) => {
|
||||
e.currentTarget.style.background = 'var(--cyber-bg-elevated)';
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={`w-8 h-8 rounded-lg flex items-center justify-center ${
|
||||
|
|
@ -429,11 +452,11 @@ export default function Dashboard() {
|
|||
<AlertTriangle className="w-4 h-4" />}
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-200 group-hover:text-primary transition-colors">
|
||||
<p className="text-base font-medium text-foreground group-hover:text-primary transition-colors">
|
||||
{task.project?.name || '未知项目'}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500">
|
||||
质量分: <span className="text-white">{task.quality_score?.toFixed(1) || '0.0'}</span>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
质量分: <span className="text-foreground">{task.quality_score?.toFixed(1) || '0.0'}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -494,7 +517,7 @@ export default function Dashboard() {
|
|||
</div>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-gray-400">数据库模式</span>
|
||||
<span className="text-base text-muted-foreground">数据库模式</span>
|
||||
<Badge className={`
|
||||
${dbMode === 'api' ? 'cyber-badge-primary' :
|
||||
dbMode === 'local' ? 'cyber-badge-info' :
|
||||
|
|
@ -505,36 +528,36 @@ export default function Dashboard() {
|
|||
</Badge>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-gray-400">活跃项目</span>
|
||||
<span className="text-sm font-bold text-white">{stats?.active_projects || 0}</span>
|
||||
<span className="text-base text-muted-foreground">活跃项目</span>
|
||||
<span className="text-base font-bold text-foreground">{stats?.active_projects || 0}</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-gray-400">运行中任务</span>
|
||||
<span className="text-sm font-bold text-sky-400">
|
||||
<span className="text-base text-muted-foreground">运行中任务</span>
|
||||
<span className="text-base font-bold text-sky-400">
|
||||
{recentTasks.filter(t => t.status === 'running').length}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-gray-400">待解决问题</span>
|
||||
<span className="text-sm font-bold text-amber-400">
|
||||
<span className="text-base text-muted-foreground">待解决问题</span>
|
||||
<span className="text-base font-bold text-amber-400">
|
||||
{stats ? stats.total_issues - stats.resolved_issues : 0}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-gray-400 flex items-center gap-1">
|
||||
<Shield className="w-3 h-3" />
|
||||
<span className="text-base text-muted-foreground flex items-center gap-1">
|
||||
<Shield className="w-4 h-4" />
|
||||
审计规则
|
||||
</span>
|
||||
<span className="text-sm font-bold text-violet-400">
|
||||
<span className="text-base font-bold text-violet-400">
|
||||
{ruleStats.enabled}/{ruleStats.total}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-gray-400 flex items-center gap-1">
|
||||
<MessageSquare className="w-3 h-3" />
|
||||
<span className="text-base text-muted-foreground flex items-center gap-1">
|
||||
<MessageSquare className="w-4 h-4" />
|
||||
提示词模板
|
||||
</span>
|
||||
<span className="text-sm font-bold text-emerald-400">
|
||||
<span className="text-base font-bold text-emerald-400">
|
||||
{templateStats.active}/{templateStats.total}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -572,28 +595,28 @@ export default function Dashboard() {
|
|||
<Link
|
||||
key={task.id}
|
||||
to={`/tasks/${task.id}`}
|
||||
className={`block p-3 rounded-lg border transition-all hover:border-gray-700 ${
|
||||
task.status === 'completed' ? 'bg-emerald-500/5 border-emerald-500/20' :
|
||||
task.status === 'running' ? 'bg-sky-500/5 border-sky-500/20' :
|
||||
task.status === 'failed' ? 'bg-rose-500/5 border-rose-500/20' :
|
||||
'bg-gray-800/30 border-gray-800/50'
|
||||
className={`block p-3 rounded-lg border transition-all ${
|
||||
task.status === 'completed' ? 'bg-emerald-500/5 border-emerald-500/20 hover:border-emerald-500/40' :
|
||||
task.status === 'running' ? 'bg-sky-500/5 border-sky-500/20 hover:border-sky-500/40' :
|
||||
task.status === 'failed' ? 'bg-rose-500/5 border-rose-500/20 hover:border-rose-500/40' :
|
||||
'bg-muted/30 border-border hover:border-border'
|
||||
}`}
|
||||
>
|
||||
<p className="text-sm font-medium text-gray-200">{statusText}</p>
|
||||
<p className="text-xs text-gray-500 mt-1 line-clamp-1">
|
||||
<p className="text-base font-medium text-foreground">{statusText}</p>
|
||||
<p className="text-sm text-muted-foreground mt-1 line-clamp-1">
|
||||
项目 "{task.project?.name || '未知项目'}"
|
||||
{task.status === 'completed' && task.issues_count > 0 &&
|
||||
` - 发现 ${task.issues_count} 个问题`
|
||||
}
|
||||
</p>
|
||||
<p className="text-[10px] text-gray-600 mt-1">{timeAgo}</p>
|
||||
<p className="text-sm text-muted-foreground/70 mt-1">{timeAgo}</p>
|
||||
</Link>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<div className="empty-state py-6">
|
||||
<Clock className="w-8 h-8 text-gray-600 mb-2" />
|
||||
<p className="text-sm text-gray-500">暂无活动记录</p>
|
||||
<Clock className="w-10 h-10 text-muted-foreground mb-2" />
|
||||
<p className="text-base text-muted-foreground">暂无活动记录</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -346,8 +346,8 @@ public class Example {
|
|||
|
||||
// Render issue with cyberpunk style
|
||||
const renderIssue = (issue: any, index: number) => (
|
||||
<div key={index} className="cyber-card p-4 mb-4 hover:border-gray-700 transition-all group">
|
||||
<div className="flex items-start justify-between mb-3 pb-3 border-b border-gray-800">
|
||||
<div key={index} className="cyber-card p-4 mb-4 hover:border-border transition-all group">
|
||||
<div className="flex items-start justify-between mb-3 pb-3 border-b border-border">
|
||||
<div className="flex items-start space-x-3">
|
||||
<div className={`w-10 h-10 rounded-lg flex items-center justify-center ${
|
||||
issue.severity === 'critical' ? 'bg-rose-500/20 text-rose-400' :
|
||||
|
|
@ -358,15 +358,15 @@ public class Example {
|
|||
{getTypeIcon(issue.type)}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h4 className="font-bold text-base text-gray-200 mb-1 group-hover:text-primary transition-colors uppercase">{issue.title}</h4>
|
||||
<div className="flex items-center space-x-1 text-xs text-gray-500 font-mono">
|
||||
<h4 className="font-bold text-base text-foreground mb-1 group-hover:text-primary transition-colors uppercase">{issue.title}</h4>
|
||||
<div className="flex items-center space-x-1 text-xs text-muted-foreground font-mono">
|
||||
<span className="text-primary">></span>
|
||||
<span>LINE: {issue.line}</span>
|
||||
{issue.column && <span>, COL: {issue.column}</span>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Badge className={`${getSeverityClasses(issue.severity)} font-bold uppercase px-2 py-1 rounded text-[10px]`}>
|
||||
<Badge className={`${getSeverityClasses(issue.severity)} font-bold uppercase px-2 py-1 rounded text-xs`}>
|
||||
{issue.severity === 'critical' ? '严重' :
|
||||
issue.severity === 'high' ? '高' :
|
||||
issue.severity === 'medium' ? '中等' : '低'}
|
||||
|
|
@ -374,27 +374,27 @@ public class Example {
|
|||
</div>
|
||||
|
||||
{issue.description && (
|
||||
<div className="bg-gray-900/50 border border-gray-800 p-3 mb-3 rounded font-mono">
|
||||
<div className="flex items-center mb-1 border-b border-gray-800 pb-1">
|
||||
<Info className="w-3 h-3 text-gray-500 mr-1" />
|
||||
<span className="font-bold text-gray-400 text-xs uppercase">问题详情</span>
|
||||
<div className="bg-muted border border-border p-3 mb-3 rounded font-mono">
|
||||
<div className="flex items-center mb-1 border-b border-border pb-1">
|
||||
<Info className="w-3 h-3 text-muted-foreground mr-1" />
|
||||
<span className="font-bold text-muted-foreground text-xs uppercase">问题详情</span>
|
||||
</div>
|
||||
<p className="text-gray-300 text-xs leading-relaxed mt-1">{issue.description}</p>
|
||||
<p className="text-foreground text-xs leading-relaxed mt-1">{issue.description}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{issue.code_snippet && (
|
||||
<div className="bg-[#0a0a0f] p-3 mb-3 border border-gray-800 rounded">
|
||||
<div className="flex items-center justify-between mb-2 border-b border-gray-800 pb-1">
|
||||
<div className="cyber-bg-elevated p-3 mb-3 border border-border rounded">
|
||||
<div className="flex items-center justify-between mb-2 border-b border-border pb-1">
|
||||
<div className="flex items-center space-x-1">
|
||||
<div className="w-4 h-4 bg-primary rounded flex items-center justify-center">
|
||||
<Code className="w-2 h-2 text-white" />
|
||||
<Code className="w-2 h-2 text-foreground" />
|
||||
</div>
|
||||
<span className="text-emerald-400 text-xs font-bold font-mono uppercase">CODE_SNIPPET</span>
|
||||
</div>
|
||||
<span className="text-gray-500 text-xs font-mono">LINE: {issue.line}</span>
|
||||
<span className="text-muted-foreground text-xs font-mono">LINE: {issue.line}</span>
|
||||
</div>
|
||||
<div className="bg-black/40 p-2 border border-gray-800 rounded">
|
||||
<div className="bg-black/40 p-2 border border-border rounded">
|
||||
<pre className="text-xs text-emerald-400 font-mono overflow-x-auto">
|
||||
<code>{issue.code_snippet}</code>
|
||||
</pre>
|
||||
|
|
@ -431,19 +431,19 @@ public class Example {
|
|||
{parsedExplanation.what && (
|
||||
<div className="border-l-2 border-rose-500 pl-2">
|
||||
<span className="font-bold text-rose-400 uppercase">问题:</span>
|
||||
<span className="text-gray-300 ml-1">{parsedExplanation.what}</span>
|
||||
<span className="text-foreground ml-1">{parsedExplanation.what}</span>
|
||||
</div>
|
||||
)}
|
||||
{parsedExplanation.why && (
|
||||
<div className="border-l-2 border-amber-500 pl-2">
|
||||
<span className="font-bold text-amber-400 uppercase">原因:</span>
|
||||
<span className="text-gray-300 ml-1">{parsedExplanation.why}</span>
|
||||
<span className="text-foreground ml-1">{parsedExplanation.why}</span>
|
||||
</div>
|
||||
)}
|
||||
{parsedExplanation.how && (
|
||||
<div className="border-l-2 border-emerald-500 pl-2">
|
||||
<span className="font-bold text-emerald-400 uppercase">方案:</span>
|
||||
<span className="text-gray-300 ml-1">{parsedExplanation.how}</span>
|
||||
<span className="text-foreground ml-1">{parsedExplanation.how}</span>
|
||||
</div>
|
||||
)}
|
||||
{parsedExplanation.learn_more && (
|
||||
|
|
@ -469,7 +469,7 @@ public class Example {
|
|||
<Zap className="w-4 h-4 text-violet-400 mr-2" />
|
||||
<span className="font-bold text-violet-300 text-sm uppercase">AI 解释</span>
|
||||
</div>
|
||||
<p className="text-gray-300 text-xs leading-relaxed font-mono">{issue.ai_explanation}</p>
|
||||
<p className="text-foreground text-xs leading-relaxed font-mono">{issue.ai_explanation}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -479,7 +479,7 @@ public class Example {
|
|||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-6 p-6 bg-[#0a0a0f] min-h-screen font-mono relative">
|
||||
<div className="space-y-6 p-6 cyber-bg-elevated min-h-screen font-mono relative">
|
||||
{/* Grid background */}
|
||||
<div className="absolute inset-0 cyber-grid-subtle pointer-events-none" />
|
||||
|
||||
|
|
@ -488,7 +488,7 @@ public class Example {
|
|||
<div className="cyber-card p-0 relative z-10">
|
||||
<div className="cyber-card-header">
|
||||
<History className="w-5 h-5 text-primary" />
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-white">分析历史记录</h3>
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-foreground">分析历史记录</h3>
|
||||
<div className="ml-auto flex items-center gap-2">
|
||||
{historyRecords.length > 0 && (
|
||||
<Button
|
||||
|
|
@ -514,7 +514,7 @@ public class Example {
|
|||
{loadingHistory ? (
|
||||
<div className="text-center py-8">
|
||||
<div className="loading-spinner mx-auto mb-4"></div>
|
||||
<p className="text-gray-500 font-mono">加载中...</p>
|
||||
<p className="text-muted-foreground font-mono">加载中...</p>
|
||||
</div>
|
||||
) : historyRecords.length === 0 ? (
|
||||
<div className="empty-state">
|
||||
|
|
@ -531,14 +531,14 @@ public class Example {
|
|||
className={`p-4 rounded-lg border transition-colors cursor-pointer ${
|
||||
selectedHistoryId === record.id
|
||||
? 'bg-primary/10 border-primary/30'
|
||||
: 'bg-gray-900/30 border-gray-800 hover:bg-gray-800/50 hover:border-gray-700'
|
||||
: 'bg-muted/50 border-border hover:bg-muted hover:border-border'
|
||||
}`}
|
||||
onClick={() => viewHistoryRecord(record)}
|
||||
>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge className="cyber-badge-muted">{record.language}</Badge>
|
||||
<span className="text-sm font-mono text-gray-500">{formatDate(record.created_at)}</span>
|
||||
<span className="text-sm font-mono text-muted-foreground">{formatDate(record.created_at)}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge className={`font-mono ${
|
||||
|
|
@ -556,10 +556,10 @@ public class Example {
|
|||
>
|
||||
<X className="w-3 h-3" />
|
||||
</Button>
|
||||
<ChevronRight className="w-4 h-4 text-gray-500" />
|
||||
<ChevronRight className="w-4 h-4 text-muted-foreground" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-4 text-xs font-mono text-gray-500">
|
||||
<div className="flex items-center gap-4 text-xs font-mono text-muted-foreground">
|
||||
<span className="flex items-center gap-1">
|
||||
<AlertTriangle className="w-3 h-3" />
|
||||
{record.issues_count} 个问题
|
||||
|
|
@ -582,7 +582,7 @@ public class Example {
|
|||
<div className="cyber-card p-0 relative z-10">
|
||||
<div className="cyber-card-header">
|
||||
<Terminal className="w-5 h-5 text-primary" />
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-white">代码分析</h3>
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-foreground">代码分析</h3>
|
||||
<div className="ml-auto flex items-center gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
|
|
@ -606,12 +606,12 @@ public class Example {
|
|||
{/* Toolbar */}
|
||||
<div className="flex flex-col sm:flex-row gap-3">
|
||||
<div className="flex-1 space-y-1">
|
||||
<label className="text-xs font-bold text-gray-500 uppercase">编程语言</label>
|
||||
<label className="text-xs font-bold text-muted-foreground uppercase">编程语言</label>
|
||||
<Select value={language} onValueChange={setLanguage}>
|
||||
<SelectTrigger className="cyber-input h-10">
|
||||
<SelectValue placeholder="选择编程语言" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
||||
<SelectContent className="cyber-dialog border-border">
|
||||
{supportedLanguages.map((lang) => (
|
||||
<SelectItem key={lang} value={lang}>
|
||||
{lang.charAt(0).toUpperCase() + lang.slice(1)}
|
||||
|
|
@ -621,7 +621,7 @@ public class Example {
|
|||
</Select>
|
||||
</div>
|
||||
<div className="flex-1 space-y-1">
|
||||
<label className="text-xs font-bold text-gray-500 uppercase">提示词模板</label>
|
||||
<label className="text-xs font-bold text-muted-foreground uppercase">提示词模板</label>
|
||||
<Select value={selectedPromptTemplateId} onValueChange={setSelectedPromptTemplateId}>
|
||||
<SelectTrigger className="cyber-input h-10">
|
||||
<div className="flex items-center gap-2">
|
||||
|
|
@ -629,7 +629,7 @@ public class Example {
|
|||
<SelectValue placeholder="选择提示词模板" />
|
||||
</div>
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
||||
<SelectContent className="cyber-dialog border-border">
|
||||
{promptTemplates.map((pt) => (
|
||||
<SelectItem key={pt.id} value={pt.id}>
|
||||
{pt.name} {pt.is_default && '(默认)'}
|
||||
|
|
@ -659,8 +659,8 @@ public class Example {
|
|||
</div>
|
||||
|
||||
{/* Quick Examples */}
|
||||
<div className="flex flex-wrap gap-2 items-center p-3 bg-gray-900/50 border border-gray-800 rounded">
|
||||
<span className="text-xs font-bold uppercase text-gray-500 mr-2">示例:</span>
|
||||
<div className="flex flex-wrap gap-2 items-center p-3 bg-muted border border-border rounded">
|
||||
<span className="text-xs font-bold uppercase text-muted-foreground mr-2">示例:</span>
|
||||
{['javascript', 'python', 'java'].map((lang) => (
|
||||
<Button
|
||||
key={lang}
|
||||
|
|
@ -677,17 +677,17 @@ public class Example {
|
|||
|
||||
{/* Code Editor */}
|
||||
<div className="relative">
|
||||
<div className="absolute top-0 right-0 bg-gray-800 text-gray-400 px-2 py-1 text-[10px] font-mono uppercase z-10 rounded-bl border-l border-b border-gray-700">
|
||||
<div className="absolute top-0 right-0 bg-muted text-muted-foreground px-2 py-1 text-xs font-mono uppercase z-10 rounded-bl border-l border-b border-border">
|
||||
Editor
|
||||
</div>
|
||||
<Textarea
|
||||
placeholder="// 粘贴代码或上传文件..."
|
||||
value={code}
|
||||
onChange={(e) => setCode(e.target.value)}
|
||||
className="min-h-[300px] font-mono text-sm bg-[#0a0a0f] text-emerald-400 border border-gray-800 p-4 focus:ring-0 focus:border-primary/50 placeholder:text-gray-600"
|
||||
className="min-h-[300px] font-mono text-sm cyber-bg-elevated text-emerald-400 border border-border p-4 focus:ring-0 focus:border-primary/50 placeholder:text-muted-foreground"
|
||||
disabled={analyzing}
|
||||
/>
|
||||
<div className="text-xs text-gray-600 mt-1 font-mono text-right">
|
||||
<div className="text-xs text-muted-foreground mt-1 font-mono text-right">
|
||||
{code.length} 字符,{code.split('\n').length} 行
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -720,7 +720,7 @@ public class Example {
|
|||
<div className="cyber-card p-0">
|
||||
<div className="cyber-card-header">
|
||||
<CheckCircle className="w-5 h-5 text-emerald-400" />
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-white">分析结果</h3>
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-foreground">分析结果</h3>
|
||||
<div className="ml-auto flex items-center gap-2">
|
||||
<Badge className="cyber-badge-muted">
|
||||
<Clock className="w-3 h-3 mr-1" />
|
||||
|
|
@ -748,7 +748,7 @@ public class Example {
|
|||
{(result.quality_score ?? 0).toFixed(1)}
|
||||
</div>
|
||||
<p className="stat-label mb-2">质量评分</p>
|
||||
<Progress value={result.quality_score ?? 0} className="h-2 bg-gray-800 [&>div]:bg-primary" />
|
||||
<Progress value={result.quality_score ?? 0} className="h-2 bg-muted [&>div]:bg-primary" />
|
||||
</div>
|
||||
|
||||
<div className="cyber-card p-4 text-center">
|
||||
|
|
@ -786,7 +786,7 @@ public class Example {
|
|||
</div>
|
||||
|
||||
{/* Detailed Metrics */}
|
||||
<div className="bg-gray-900/50 border border-gray-800 p-4 rounded-lg">
|
||||
<div className="bg-muted border border-border p-4 rounded-lg">
|
||||
<h3 className="section-title text-sm mb-4 flex items-center gap-2">
|
||||
<TrendingUp className="w-4 h-4" />
|
||||
详细指标
|
||||
|
|
@ -799,9 +799,9 @@ public class Example {
|
|||
{ label: '性能', value: result.metrics?.performance ?? 0 },
|
||||
].map((metric) => (
|
||||
<div key={metric.label} className="text-center">
|
||||
<div className="text-xl font-bold text-white mb-1">{metric.value}</div>
|
||||
<p className="text-xs text-gray-500 uppercase mb-2">{metric.label}</p>
|
||||
<Progress value={metric.value} className="h-2 bg-gray-800 [&>div]:bg-primary" />
|
||||
<div className="text-xl font-bold text-foreground mb-1">{metric.value}</div>
|
||||
<p className="text-xs text-muted-foreground uppercase mb-2">{metric.label}</p>
|
||||
<Progress value={metric.value} className="h-2 bg-muted [&>div]:bg-primary" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -813,22 +813,22 @@ public class Example {
|
|||
<div className="cyber-card p-0">
|
||||
<div className="cyber-card-header">
|
||||
<Shield className="w-5 h-5 text-amber-400" />
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-white">发现的问题 ({result.issues.length})</h3>
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-foreground">发现的问题 ({result.issues.length})</h3>
|
||||
</div>
|
||||
<div className="p-6">
|
||||
{result.issues.length > 0 ? (
|
||||
<Tabs defaultValue="all" className="w-full">
|
||||
<TabsList className="grid w-full grid-cols-4 bg-gray-900/50 border border-gray-800 p-1 h-auto gap-1 rounded mb-6">
|
||||
<TabsTrigger value="all" className="data-[state=active]:bg-primary data-[state=active]:text-white font-mono font-bold uppercase py-2 text-gray-400 transition-all rounded-sm text-xs">
|
||||
<TabsList className="grid w-full grid-cols-4 bg-muted border border-border p-1 h-auto gap-1 rounded mb-6">
|
||||
<TabsTrigger value="all" className="data-[state=active]:bg-primary data-[state=active]:text-foreground font-mono font-bold uppercase py-2 text-muted-foreground transition-all rounded-sm text-xs">
|
||||
全部 ({result.issues.length})
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="critical" className="data-[state=active]:bg-rose-500 data-[state=active]:text-white font-mono font-bold uppercase py-2 text-gray-400 transition-all rounded-sm text-xs">
|
||||
<TabsTrigger value="critical" className="data-[state=active]:bg-rose-500 data-[state=active]:text-foreground font-mono font-bold uppercase py-2 text-muted-foreground transition-all rounded-sm text-xs">
|
||||
严重 ({result.issues.filter(i => i.severity === 'critical').length})
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="high" className="data-[state=active]:bg-orange-500 data-[state=active]:text-white font-mono font-bold uppercase py-2 text-gray-400 transition-all rounded-sm text-xs">
|
||||
<TabsTrigger value="high" className="data-[state=active]:bg-orange-500 data-[state=active]:text-foreground font-mono font-bold uppercase py-2 text-muted-foreground transition-all rounded-sm text-xs">
|
||||
高 ({result.issues.filter(i => i.severity === 'high').length})
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="medium" className="data-[state=active]:bg-amber-500 data-[state=active]:text-black font-mono font-bold uppercase py-2 text-gray-400 transition-all rounded-sm text-xs">
|
||||
<TabsTrigger value="medium" className="data-[state=active]:bg-amber-500 data-[state=active]:text-background font-mono font-bold uppercase py-2 text-muted-foreground transition-all rounded-sm text-xs">
|
||||
中等 ({result.issues.filter(i => i.severity === 'medium').length})
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
|
@ -844,10 +844,10 @@ public class Example {
|
|||
) : (
|
||||
<div className="cyber-card p-12 text-center border-dashed">
|
||||
<CheckCircle className="w-16 h-16 text-emerald-400 mx-auto mb-4" />
|
||||
<h3 className="text-lg font-bold text-gray-300 uppercase mb-2">
|
||||
<h3 className="text-lg font-bold text-foreground uppercase mb-2">
|
||||
没有发现{severity === 'critical' ? '严重' : severity === 'high' ? '高优先级' : '中等优先级'}问题
|
||||
</h3>
|
||||
<p className="text-gray-500 font-mono">代码在此级别的检查中表现良好</p>
|
||||
<p className="text-muted-foreground font-mono">代码在此级别的检查中表现良好</p>
|
||||
</div>
|
||||
)}
|
||||
</TabsContent>
|
||||
|
|
@ -877,9 +877,9 @@ public class Example {
|
|||
<div className="w-20 h-20 bg-primary/20 border border-primary/40 rounded-lg flex items-center justify-center mx-auto mb-6">
|
||||
<div className="loading-spinner w-12 h-12"></div>
|
||||
</div>
|
||||
<h3 className="text-2xl font-bold text-white uppercase mb-3">AI正在分析您的代码</h3>
|
||||
<p className="text-gray-400 mb-6 font-mono">请稍候,这通常需要至少30秒钟...</p>
|
||||
<p className="text-gray-500 text-sm mb-6 font-mono">分析时长取决于您的网络环境、代码长度以及使用的模型等因素</p>
|
||||
<h3 className="text-2xl font-bold text-foreground uppercase mb-3">AI正在分析您的代码</h3>
|
||||
<p className="text-muted-foreground mb-6 font-mono">请稍候,这通常需要至少30秒钟...</p>
|
||||
<p className="text-muted-foreground text-sm mb-6 font-mono">分析时长取决于您的网络环境、代码长度以及使用的模型等因素</p>
|
||||
<div className="bg-primary/10 border border-primary/30 p-4 max-w-md mx-auto rounded">
|
||||
<p className="text-primary text-sm font-mono">
|
||||
正在进行安全检测、性能分析、代码风格检查等多维度评估<br />
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ export default function Login() {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-[#0a0a0f] relative overflow-hidden">
|
||||
<div className="min-h-screen flex items-center justify-center cyber-bg-elevated relative overflow-hidden">
|
||||
{/* Scanline overlay */}
|
||||
<div className="absolute inset-0 pointer-events-none z-20">
|
||||
<div
|
||||
|
|
@ -110,32 +110,32 @@ export default function Login() {
|
|||
/>
|
||||
|
||||
{/* Corner Decorations */}
|
||||
<div className="absolute top-4 left-4 text-[10px] font-mono text-gray-700 z-30 space-y-1">
|
||||
<div className="absolute top-4 left-4 text-sm font-mono text-muted-foreground z-30 space-y-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<Terminal className="w-3 h-3" />
|
||||
<Terminal className="w-4 h-4" />
|
||||
<span>SYS_ID: 0x84F2</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Shield className="w-3 h-3" />
|
||||
<Shield className="w-4 h-4" />
|
||||
<span>ENCRYPT: AES-256</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Fingerprint className="w-3 h-3" />
|
||||
<Fingerprint className="w-4 h-4" />
|
||||
<span>AUTH: READY</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="absolute top-4 right-4 text-[10px] font-mono text-gray-700 text-right z-30 space-y-1">
|
||||
<div className="absolute top-4 right-4 text-sm font-mono text-muted-foreground text-right z-30 space-y-1">
|
||||
<div>SECURE_CONN: TRUE</div>
|
||||
<div>PORT: 443</div>
|
||||
<div>TLS: 1.3</div>
|
||||
</div>
|
||||
|
||||
<div className="absolute bottom-4 left-4 text-[10px] font-mono text-gray-700 z-30">
|
||||
<div className="absolute bottom-4 left-4 text-sm font-mono text-muted-foreground z-30">
|
||||
DEEPAUDIT_AUTH_v3
|
||||
</div>
|
||||
|
||||
<div className="absolute bottom-4 right-4 text-[10px] font-mono text-gray-700 z-30">
|
||||
<div className="absolute bottom-4 right-4 text-sm font-mono text-muted-foreground z-30">
|
||||
{new Date().toISOString().split("T")[0]}
|
||||
</div>
|
||||
|
||||
|
|
@ -143,7 +143,7 @@ export default function Login() {
|
|||
<div className="w-full max-w-md relative z-30 px-4">
|
||||
{/* Logo & Title */}
|
||||
<div className="text-center mb-8">
|
||||
<div className="inline-flex items-center justify-center p-3 bg-[#0c0c12] border border-gray-800/60 rounded-lg mb-6"
|
||||
<div className="inline-flex items-center justify-center p-3 cyber-dialog border border-border/60 rounded-lg mb-6"
|
||||
style={{ boxShadow: '0 0 30px rgba(255,107,44,0.1)' }}>
|
||||
<img
|
||||
src="/logo_deepaudit.png"
|
||||
|
|
@ -156,24 +156,24 @@ export default function Login() {
|
|||
style={{ textShadow: "0 0 30px rgba(255,107,44,0.5), 0 0 60px rgba(255,107,44,0.3)" }}
|
||||
>
|
||||
<span className="text-primary">DEEP</span>
|
||||
<span className="text-white">AUDIT</span>
|
||||
<span className="text-foreground">AUDIT</span>
|
||||
</div>
|
||||
<p className="text-sm font-mono text-gray-500">
|
||||
<p className="text-base font-mono text-muted-foreground">
|
||||
// Autonomous Security Agent
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Login Form Card */}
|
||||
<div className="bg-[#0c0c12] border border-gray-800/60 rounded-lg overflow-hidden"
|
||||
<div className="cyber-dialog border border-border/60 rounded-lg overflow-hidden"
|
||||
style={{ boxShadow: '0 4px 30px rgba(0,0,0,0.5)' }}>
|
||||
{/* Card Header */}
|
||||
<div className="flex items-center gap-2 px-4 py-3 bg-[#0a0a0f] border-b border-gray-800/50">
|
||||
<div className="flex items-center gap-2 px-4 py-3 cyber-bg-elevated border-b border-border">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<div className="w-3 h-3 rounded-full bg-red-500/80" />
|
||||
<div className="w-3 h-3 rounded-full bg-yellow-500/80" />
|
||||
<div className="w-3 h-3 rounded-full bg-green-500/80" />
|
||||
</div>
|
||||
<span className="ml-2 font-mono text-[11px] text-gray-500 tracking-wider">
|
||||
<span className="ml-2 font-mono text-sm text-muted-foreground tracking-wider">
|
||||
authentication@deepaudit
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -183,7 +183,7 @@ export default function Login() {
|
|||
<div className="space-y-2">
|
||||
<Label
|
||||
htmlFor="email"
|
||||
className="font-mono text-xs text-gray-400 uppercase tracking-wider"
|
||||
className="font-mono text-sm text-muted-foreground uppercase tracking-wider"
|
||||
>
|
||||
邮箱地址
|
||||
</Label>
|
||||
|
|
@ -195,16 +195,16 @@ export default function Login() {
|
|||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
className="h-12 pl-11 font-mono bg-[#0a0a0f] border-gray-700/50 text-gray-200 placeholder:text-gray-600 focus:border-primary/50 focus:ring-0"
|
||||
className="h-12 pl-11 font-mono cyber-bg-elevated border-border/50 text-foreground placeholder:text-muted-foreground focus:border-primary/50 focus:ring-0"
|
||||
/>
|
||||
<Mail className="absolute left-4 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-600" />
|
||||
<Mail className="absolute left-4 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label
|
||||
htmlFor="password"
|
||||
className="font-mono text-xs text-gray-400 uppercase tracking-wider"
|
||||
className="font-mono text-sm text-muted-foreground uppercase tracking-wider"
|
||||
>
|
||||
密码
|
||||
</Label>
|
||||
|
|
@ -216,9 +216,9 @@ export default function Login() {
|
|||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
className="h-12 pl-11 font-mono bg-[#0a0a0f] border-gray-700/50 text-gray-200 placeholder:text-gray-600 focus:border-primary/50 focus:ring-0"
|
||||
className="h-12 pl-11 font-mono cyber-bg-elevated border-border/50 text-foreground placeholder:text-muted-foreground focus:border-primary/50 focus:ring-0"
|
||||
/>
|
||||
<Lock className="absolute left-4 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-600" />
|
||||
<Lock className="absolute left-4 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -230,11 +230,11 @@ export default function Login() {
|
|||
onCheckedChange={(checked) =>
|
||||
setRememberMe(checked as boolean)
|
||||
}
|
||||
className="border-gray-600 data-[state=checked]:bg-primary data-[state=checked]:border-primary"
|
||||
className="border-border data-[state=checked]:bg-primary data-[state=checked]:border-primary"
|
||||
/>
|
||||
<Label
|
||||
htmlFor="remember"
|
||||
className="text-sm font-mono text-gray-500 cursor-pointer"
|
||||
className="text-base font-mono text-muted-foreground cursor-pointer"
|
||||
>
|
||||
记住我
|
||||
</Label>
|
||||
|
|
@ -243,7 +243,7 @@ export default function Login() {
|
|||
|
||||
<Button
|
||||
type="submit"
|
||||
className="w-full h-12 text-base font-bold uppercase tracking-wider bg-primary hover:bg-primary/90 text-white border border-primary/50 transition-all"
|
||||
className="w-full h-12 text-base font-bold uppercase tracking-wider bg-primary hover:bg-primary/90 text-foreground border border-primary/50 transition-all"
|
||||
style={{ boxShadow: '0 0 20px rgba(255,107,44,0.3)' }}
|
||||
disabled={loading}
|
||||
>
|
||||
|
|
@ -259,8 +259,8 @@ export default function Login() {
|
|||
</form>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="mt-6 pt-5 border-t border-gray-800/50 text-center">
|
||||
<p className="text-sm font-mono text-gray-500">
|
||||
<div className="mt-6 pt-5 border-t border-border text-center">
|
||||
<p className="text-base font-mono text-muted-foreground">
|
||||
还没有账号?{" "}
|
||||
<span
|
||||
className="text-primary font-bold cursor-pointer hover:underline"
|
||||
|
|
@ -275,7 +275,7 @@ export default function Login() {
|
|||
|
||||
{/* Version Info */}
|
||||
<div className="mt-6 text-center">
|
||||
<p className="font-mono text-[10px] text-gray-600 uppercase">
|
||||
<p className="font-mono text-sm text-muted-foreground uppercase">
|
||||
Version {version} · Secure Connection
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export default function NotFound() {
|
|||
return (
|
||||
<>
|
||||
<PageMeta title="页面未找到" description="" />
|
||||
<div className="relative flex flex-col items-center justify-center min-h-screen p-6 bg-[#0a0a0f] font-mono overflow-hidden">
|
||||
<div className="relative flex flex-col items-center justify-center min-h-screen p-6 cyber-bg-elevated font-mono overflow-hidden">
|
||||
{/* Grid background */}
|
||||
<div className="absolute inset-0 cyber-grid-subtle pointer-events-none" />
|
||||
|
||||
|
|
@ -43,26 +43,26 @@ export default function NotFound() {
|
|||
<div className="cyber-card p-0 mb-8">
|
||||
<div className="cyber-card-header">
|
||||
<Terminal className="w-4 h-4 text-primary" />
|
||||
<span className="text-sm font-bold uppercase tracking-wider text-gray-300">ERROR_LOG</span>
|
||||
<span className="text-sm font-bold uppercase tracking-wider text-foreground">ERROR_LOG</span>
|
||||
</div>
|
||||
<div className="p-4 text-left">
|
||||
<div className="text-emerald-400 text-sm">
|
||||
<span className="text-gray-500">[{new Date().toISOString()}]</span>
|
||||
<span className="text-muted-foreground">[{new Date().toISOString()}]</span>
|
||||
</div>
|
||||
<div className="text-rose-400 text-sm mt-1">
|
||||
<span className="text-gray-500">ERROR:</span> PAGE_NOT_FOUND
|
||||
<span className="text-muted-foreground">ERROR:</span> PAGE_NOT_FOUND
|
||||
</div>
|
||||
<div className="text-amber-400 text-sm mt-1">
|
||||
<span className="text-gray-500">STATUS:</span> 404
|
||||
<span className="text-muted-foreground">STATUS:</span> 404
|
||||
</div>
|
||||
<div className="text-gray-400 text-sm mt-1">
|
||||
<span className="text-gray-500">MESSAGE:</span> 请求的页面不存在或已被移除
|
||||
<div className="text-muted-foreground text-sm mt-1">
|
||||
<span className="text-muted-foreground">MESSAGE:</span> 请求的页面不存在或已被移除
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Description */}
|
||||
<p className="text-gray-400 mb-8 text-sm">
|
||||
<p className="text-muted-foreground mb-8 text-sm">
|
||||
页面可能已被删除或不存在,请检查网址是否正确。
|
||||
</p>
|
||||
|
||||
|
|
@ -76,7 +76,7 @@ export default function NotFound() {
|
|||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<p className="absolute bottom-6 left-1/2 -translate-x-1/2 text-xs text-gray-600 font-mono uppercase">
|
||||
<p className="absolute bottom-6 left-1/2 -translate-x-1/2 text-xs text-muted-foreground font-mono uppercase">
|
||||
© {new Date().getFullYear()} DeepAudit
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -280,7 +280,7 @@ export default function ProjectDetail() {
|
|||
case 'completed': return <CheckCircle className="w-4 h-4 text-emerald-400" />;
|
||||
case 'running': return <Activity className="w-4 h-4 text-sky-400" />;
|
||||
case 'failed': return <AlertTriangle className="w-4 h-4 text-rose-400" />;
|
||||
default: return <Clock className="w-4 h-4 text-gray-400" />;
|
||||
default: return <Clock className="w-4 h-4 text-muted-foreground" />;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -316,7 +316,7 @@ export default function ProjectDetail() {
|
|||
<div className="flex items-center justify-center min-h-[60vh]">
|
||||
<div className="text-center space-y-4">
|
||||
<div className="loading-spinner mx-auto" />
|
||||
<p className="text-gray-500 font-mono text-sm uppercase tracking-wider">加载项目数据...</p>
|
||||
<p className="text-muted-foreground font-mono text-sm uppercase tracking-wider">加载项目数据...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -327,8 +327,8 @@ export default function ProjectDetail() {
|
|||
<div className="flex items-center justify-center min-h-[60vh]">
|
||||
<div className="cyber-card p-8 text-center">
|
||||
<AlertTriangle className="w-16 h-16 text-rose-400 mx-auto mb-4" />
|
||||
<h2 className="text-2xl font-bold text-white mb-2 uppercase">项目未找到</h2>
|
||||
<p className="text-gray-400 mb-4 font-mono">请检查项目ID是否正确</p>
|
||||
<h2 className="text-2xl font-bold text-foreground mb-2 uppercase">项目未找到</h2>
|
||||
<p className="text-muted-foreground mb-4 font-mono">请检查项目ID是否正确</p>
|
||||
<Link to="/projects">
|
||||
<Button className="cyber-btn-primary">
|
||||
<ArrowLeft className="w-4 h-4 mr-2" />
|
||||
|
|
@ -341,7 +341,7 @@ export default function ProjectDetail() {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6 p-6 bg-[#0a0a0f] min-h-screen font-mono relative">
|
||||
<div className="space-y-6 p-6 cyber-bg-elevated min-h-screen font-mono relative">
|
||||
{/* Grid background */}
|
||||
<div className="absolute inset-0 cyber-grid-subtle pointer-events-none" />
|
||||
|
||||
|
|
@ -354,7 +354,7 @@ export default function ProjectDetail() {
|
|||
</Button>
|
||||
</Link>
|
||||
<div className="flex items-center gap-3">
|
||||
<h1 className="text-2xl font-bold text-white uppercase tracking-wider">{project.name}</h1>
|
||||
<h1 className="text-2xl font-bold text-foreground uppercase tracking-wider">{project.name}</h1>
|
||||
<Badge className={`${project.is_active ? 'cyber-badge-success' : 'cyber-badge-muted'}`}>
|
||||
{project.is_active ? '活跃' : '暂停'}
|
||||
</Badge>
|
||||
|
|
@ -431,11 +431,11 @@ export default function ProjectDetail() {
|
|||
|
||||
{/* 主要内容 */}
|
||||
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full relative z-10">
|
||||
<TabsList className="grid w-full grid-cols-4 bg-gray-900/50 border border-gray-800 p-1 h-auto gap-1 rounded">
|
||||
<TabsTrigger value="overview" className="data-[state=active]:bg-primary data-[state=active]:text-white font-mono font-bold uppercase py-2 text-gray-400 transition-all rounded-sm">项目概览</TabsTrigger>
|
||||
<TabsTrigger value="tasks" className="data-[state=active]:bg-primary data-[state=active]:text-white font-mono font-bold uppercase py-2 text-gray-400 transition-all rounded-sm">审计任务</TabsTrigger>
|
||||
<TabsTrigger value="issues" className="data-[state=active]:bg-primary data-[state=active]:text-white font-mono font-bold uppercase py-2 text-gray-400 transition-all rounded-sm">问题管理</TabsTrigger>
|
||||
<TabsTrigger value="settings" className="data-[state=active]:bg-primary data-[state=active]:text-white font-mono font-bold uppercase py-2 text-gray-400 transition-all rounded-sm">项目设置</TabsTrigger>
|
||||
<TabsList className="grid w-full grid-cols-4 bg-muted border border-border p-1 h-auto gap-1 rounded">
|
||||
<TabsTrigger value="overview" className="data-[state=active]:bg-primary data-[state=active]:text-foreground font-mono font-bold uppercase py-2 text-muted-foreground transition-all rounded-sm">项目概览</TabsTrigger>
|
||||
<TabsTrigger value="tasks" className="data-[state=active]:bg-primary data-[state=active]:text-foreground font-mono font-bold uppercase py-2 text-muted-foreground transition-all rounded-sm">审计任务</TabsTrigger>
|
||||
<TabsTrigger value="issues" className="data-[state=active]:bg-primary data-[state=active]:text-foreground font-mono font-bold uppercase py-2 text-muted-foreground transition-all rounded-sm">问题管理</TabsTrigger>
|
||||
<TabsTrigger value="settings" className="data-[state=active]:bg-primary data-[state=active]:text-foreground font-mono font-bold uppercase py-2 text-muted-foreground transition-all rounded-sm">项目设置</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="overview" className="flex flex-col gap-6 mt-6">
|
||||
|
|
@ -450,7 +450,7 @@ export default function ProjectDetail() {
|
|||
<div className="space-y-3">
|
||||
{project.repository_url && (
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-gray-500 uppercase">仓库地址</span>
|
||||
<span className="text-sm text-muted-foreground uppercase">仓库地址</span>
|
||||
<a
|
||||
href={project.repository_url}
|
||||
target="_blank"
|
||||
|
|
@ -464,7 +464,7 @@ export default function ProjectDetail() {
|
|||
)}
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-gray-500 uppercase">项目类型</span>
|
||||
<span className="text-sm text-muted-foreground uppercase">项目类型</span>
|
||||
<Badge className={`${isRepositoryProject(project) ? 'cyber-badge-info' : 'cyber-badge-warning'}`}>
|
||||
{getSourceTypeLabel(project.source_type)}
|
||||
</Badge>
|
||||
|
|
@ -473,7 +473,7 @@ export default function ProjectDetail() {
|
|||
{isRepositoryProject(project) && (
|
||||
<>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-gray-500 uppercase">仓库平台</span>
|
||||
<span className="text-sm text-muted-foreground uppercase">仓库平台</span>
|
||||
<Badge className="cyber-badge-muted">
|
||||
{project.repository_type === 'github' ? 'GitHub' :
|
||||
project.repository_type === 'gitlab' ? 'GitLab' : '其他'}
|
||||
|
|
@ -481,26 +481,26 @@ export default function ProjectDetail() {
|
|||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-gray-500 uppercase">默认分支</span>
|
||||
<span className="text-sm font-bold text-gray-200 bg-gray-800 px-2 py-0.5 rounded border border-gray-700">{project.default_branch}</span>
|
||||
<span className="text-sm text-muted-foreground uppercase">默认分支</span>
|
||||
<span className="text-sm font-bold text-foreground bg-muted px-2 py-0.5 rounded border border-border">{project.default_branch}</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-gray-500 uppercase">创建时间</span>
|
||||
<span className="text-sm text-gray-300">{formatDate(project.created_at)}</span>
|
||||
<span className="text-sm text-muted-foreground uppercase">创建时间</span>
|
||||
<span className="text-sm text-foreground">{formatDate(project.created_at)}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-gray-500 uppercase">所有者</span>
|
||||
<span className="text-sm text-gray-300">{project.owner?.full_name || project.owner?.phone || '未知'}</span>
|
||||
<span className="text-sm text-muted-foreground uppercase">所有者</span>
|
||||
<span className="text-sm text-foreground">{project.owner?.full_name || project.owner?.phone || '未知'}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{project.programming_languages && (
|
||||
<div className="pt-4 border-t border-gray-800">
|
||||
<h4 className="text-sm font-bold mb-2 uppercase text-gray-500">支持的编程语言</h4>
|
||||
<div className="pt-4 border-t border-border">
|
||||
<h4 className="text-sm font-bold mb-2 uppercase text-muted-foreground">支持的编程语言</h4>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{JSON.parse(project.programming_languages).map((lang: string) => (
|
||||
<Badge key={lang} className="cyber-badge-primary">
|
||||
|
|
@ -526,22 +526,22 @@ export default function ProjectDetail() {
|
|||
<Link
|
||||
key={task.id}
|
||||
to={`/tasks/${task.id}`}
|
||||
className="flex items-center justify-between p-3 bg-gray-900/30 rounded-lg hover:bg-gray-800/50 transition-all group"
|
||||
className="flex items-center justify-between p-3 bg-muted/50 rounded-lg hover:bg-muted transition-all group"
|
||||
>
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className={`w-8 h-8 rounded-lg flex items-center justify-center ${
|
||||
task.status === 'completed' ? 'bg-emerald-500/20' :
|
||||
task.status === 'running' ? 'bg-sky-500/20' :
|
||||
task.status === 'failed' ? 'bg-rose-500/20' :
|
||||
'bg-gray-800/50'
|
||||
'bg-muted'
|
||||
}`}>
|
||||
{getStatusIcon(task.status)}
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-bold text-gray-200 group-hover:text-primary transition-colors uppercase">
|
||||
<p className="text-sm font-bold text-foreground group-hover:text-primary transition-colors uppercase">
|
||||
{task.task_type === 'repository' ? '仓库审计' : '即时分析'}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 font-mono">
|
||||
<p className="text-xs text-muted-foreground font-mono">
|
||||
{formatDate(task.created_at)}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -577,21 +577,21 @@ export default function ProjectDetail() {
|
|||
<div className="space-y-4">
|
||||
{tasks.map((task) => (
|
||||
<div key={task.id} className="cyber-card p-6">
|
||||
<div className="flex items-center justify-between mb-4 pb-4 border-b border-gray-800">
|
||||
<div className="flex items-center justify-between mb-4 pb-4 border-b border-border">
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className={`w-10 h-10 rounded-lg flex items-center justify-center ${
|
||||
task.status === 'completed' ? 'bg-emerald-500/20' :
|
||||
task.status === 'running' ? 'bg-sky-500/20' :
|
||||
task.status === 'failed' ? 'bg-rose-500/20' :
|
||||
'bg-gray-800/50'
|
||||
'bg-muted'
|
||||
}`}>
|
||||
{getStatusIcon(task.status)}
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-bold text-gray-200 uppercase">
|
||||
<h4 className="font-bold text-foreground uppercase">
|
||||
{task.task_type === 'repository' ? '仓库审计任务' : '即时分析任务'}
|
||||
</h4>
|
||||
<p className="text-sm text-gray-500 font-mono">
|
||||
<p className="text-sm text-muted-foreground font-mono">
|
||||
创建于 {formatDate(task.created_at)}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -600,35 +600,35 @@ export default function ProjectDetail() {
|
|||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-4 font-mono">
|
||||
<div className="text-center p-3 bg-gray-900/50 rounded-lg border border-gray-800">
|
||||
<p className="text-2xl font-bold text-white">{task.total_files}</p>
|
||||
<p className="text-xs text-gray-500 uppercase">总文件数</p>
|
||||
<div className="text-center p-3 bg-muted rounded-lg border border-border">
|
||||
<p className="text-2xl font-bold text-foreground">{task.total_files}</p>
|
||||
<p className="text-xs text-muted-foreground uppercase">总文件数</p>
|
||||
</div>
|
||||
<div className="text-center p-3 bg-gray-900/50 rounded-lg border border-gray-800">
|
||||
<p className="text-2xl font-bold text-white">{task.total_lines}</p>
|
||||
<p className="text-xs text-gray-500 uppercase">代码行数</p>
|
||||
<div className="text-center p-3 bg-muted rounded-lg border border-border">
|
||||
<p className="text-2xl font-bold text-foreground">{task.total_lines}</p>
|
||||
<p className="text-xs text-muted-foreground uppercase">代码行数</p>
|
||||
</div>
|
||||
<div className="text-center p-3 bg-gray-900/50 rounded-lg border border-gray-800">
|
||||
<div className="text-center p-3 bg-muted rounded-lg border border-border">
|
||||
<p className="text-2xl font-bold text-amber-400">{task.issues_count}</p>
|
||||
<p className="text-xs text-gray-500 uppercase">发现问题</p>
|
||||
<p className="text-xs text-muted-foreground uppercase">发现问题</p>
|
||||
</div>
|
||||
<div className="text-center p-3 bg-gray-900/50 rounded-lg border border-gray-800">
|
||||
<div className="text-center p-3 bg-muted rounded-lg border border-border">
|
||||
<p className="text-2xl font-bold text-primary">{task.quality_score.toFixed(1)}</p>
|
||||
<p className="text-xs text-gray-500 uppercase">质量评分</p>
|
||||
<p className="text-xs text-muted-foreground uppercase">质量评分</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{task.status === 'completed' && (
|
||||
<div className="space-y-2 mb-4">
|
||||
<div className="flex items-center justify-between text-sm font-mono">
|
||||
<span className="text-gray-400">质量评分</span>
|
||||
<span className="text-white font-bold">{task.quality_score.toFixed(1)}/100</span>
|
||||
<span className="text-muted-foreground">质量评分</span>
|
||||
<span className="text-foreground font-bold">{task.quality_score.toFixed(1)}/100</span>
|
||||
</div>
|
||||
<Progress value={task.quality_score} className="h-2 bg-gray-800 [&>div]:bg-primary" />
|
||||
<Progress value={task.quality_score} className="h-2 bg-muted [&>div]:bg-primary" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex justify-end space-x-2 pt-4 border-t border-gray-800">
|
||||
<div className="flex justify-end space-x-2 pt-4 border-t border-border">
|
||||
<Link to={`/tasks/${task.id}`}>
|
||||
<Button variant="outline" size="sm" className="cyber-btn-outline">
|
||||
<FileText className="w-4 h-4 mr-2" />
|
||||
|
|
@ -641,9 +641,9 @@ export default function ProjectDetail() {
|
|||
</div>
|
||||
) : (
|
||||
<div className="cyber-card p-12 text-center">
|
||||
<Activity className="w-16 h-16 text-gray-600 mx-auto mb-4" />
|
||||
<h3 className="text-lg font-bold text-gray-300 mb-2 uppercase">暂无审计任务</h3>
|
||||
<p className="text-sm text-gray-500 mb-6 font-mono">创建第一个审计任务开始代码质量分析</p>
|
||||
<Activity className="w-16 h-16 text-muted-foreground mx-auto mb-4" />
|
||||
<h3 className="text-lg font-bold text-foreground mb-2 uppercase">暂无审计任务</h3>
|
||||
<p className="text-sm text-muted-foreground mb-6 font-mono">创建第一个审计任务开始代码质量分析</p>
|
||||
<Button onClick={handleCreateTask} className="cyber-btn-primary">
|
||||
<Play className="w-4 h-4 mr-2" />
|
||||
创建任务
|
||||
|
|
@ -659,7 +659,7 @@ export default function ProjectDetail() {
|
|||
<h3 className="section-title">最新发现的问题</h3>
|
||||
</div>
|
||||
{tasks.length > 0 && (
|
||||
<p className="text-sm text-gray-500 font-mono">
|
||||
<p className="text-sm text-muted-foreground font-mono">
|
||||
来自最近一次审计 ({formatDate(tasks[0].created_at)})
|
||||
</p>
|
||||
)}
|
||||
|
|
@ -668,12 +668,12 @@ export default function ProjectDetail() {
|
|||
{loadingIssues ? (
|
||||
<div className="text-center py-12">
|
||||
<div className="loading-spinner mx-auto mb-4"></div>
|
||||
<p className="text-gray-500 font-mono">正在加载问题列表...</p>
|
||||
<p className="text-muted-foreground font-mono">正在加载问题列表...</p>
|
||||
</div>
|
||||
) : latestIssues.length > 0 ? (
|
||||
<div className="space-y-4">
|
||||
{latestIssues.map((issue, index) => (
|
||||
<div key={index} className="cyber-card p-4 hover:border-gray-700 transition-all">
|
||||
<div key={index} className="cyber-card p-4 hover:border-border transition-all">
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex items-start space-x-3">
|
||||
<div className={`w-8 h-8 rounded-lg flex items-center justify-center ${
|
||||
|
|
@ -685,9 +685,9 @@ export default function ProjectDetail() {
|
|||
<AlertTriangle className="w-4 h-4" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-bold text-base text-gray-200 mb-1 uppercase">{issue.title}</h4>
|
||||
<div className="flex items-center space-x-2 text-xs text-gray-500 font-mono">
|
||||
<span className="bg-gray-800 px-2 py-0.5 rounded border border-gray-700">{issue.file_path}:{issue.line_number}</span>
|
||||
<h4 className="font-bold text-base text-foreground mb-1 uppercase">{issue.title}</h4>
|
||||
<div className="flex items-center space-x-2 text-xs text-muted-foreground font-mono">
|
||||
<span className="bg-muted px-2 py-0.5 rounded border border-border">{issue.file_path}:{issue.line_number}</span>
|
||||
<span>{issue.category}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -697,14 +697,14 @@ export default function ProjectDetail() {
|
|||
issue.severity === 'high' ? 'severity-high' :
|
||||
issue.severity === 'medium' ? 'severity-medium' :
|
||||
'severity-low'}
|
||||
font-bold uppercase px-2 py-1 rounded text-[10px]
|
||||
font-bold uppercase px-2 py-1 rounded text-xs
|
||||
`}>
|
||||
{issue.severity === 'critical' ? '严重' :
|
||||
issue.severity === 'high' ? '高' :
|
||||
issue.severity === 'medium' ? '中等' : '低'}
|
||||
</Badge>
|
||||
</div>
|
||||
<p className="mt-3 text-sm text-gray-400 font-mono border-t border-gray-800 pt-3">
|
||||
<p className="mt-3 text-sm text-muted-foreground font-mono border-t border-border pt-3">
|
||||
{issue.description}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -713,8 +713,8 @@ export default function ProjectDetail() {
|
|||
) : (
|
||||
<div className="cyber-card p-12 text-center">
|
||||
<CheckCircle className="w-16 h-16 text-emerald-500 mx-auto mb-4" />
|
||||
<h3 className="text-lg font-bold text-gray-300 mb-2 uppercase">未发现问题</h3>
|
||||
<p className="text-sm text-gray-500 font-mono">最近一次审计未发现明显问题,或尚未进行审计。</p>
|
||||
<h3 className="text-lg font-bold text-foreground mb-2 uppercase">未发现问题</h3>
|
||||
<p className="text-sm text-muted-foreground font-mono">最近一次审计未发现明显问题,或尚未进行审计。</p>
|
||||
</div>
|
||||
)}
|
||||
</TabsContent>
|
||||
|
|
@ -730,7 +730,7 @@ export default function ProjectDetail() {
|
|||
{/* 基本信息 */}
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<Label htmlFor="edit-name" className="font-mono font-bold uppercase text-xs text-gray-400">项目名称 *</Label>
|
||||
<Label htmlFor="edit-name" className="font-mono font-bold uppercase text-xs text-muted-foreground">项目名称 *</Label>
|
||||
<Input
|
||||
id="edit-name"
|
||||
value={editForm.name}
|
||||
|
|
@ -741,7 +741,7 @@ export default function ProjectDetail() {
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="edit-description" className="font-mono font-bold uppercase text-xs text-gray-400">项目描述</Label>
|
||||
<Label htmlFor="edit-description" className="font-mono font-bold uppercase text-xs text-muted-foreground">项目描述</Label>
|
||||
<Textarea
|
||||
id="edit-description"
|
||||
value={editForm.description}
|
||||
|
|
@ -755,14 +755,14 @@ export default function ProjectDetail() {
|
|||
|
||||
{/* 仓库信息 - 仅远程仓库类型显示 */}
|
||||
{editForm.source_type === 'repository' && (
|
||||
<div className="space-y-4 border-t border-gray-800 pt-4">
|
||||
<h3 className="font-mono font-bold uppercase text-sm text-gray-400 flex items-center gap-2">
|
||||
<div className="space-y-4 border-t border-border pt-4">
|
||||
<h3 className="font-mono font-bold uppercase text-sm text-muted-foreground flex items-center gap-2">
|
||||
<GitBranch className="w-4 h-4" />
|
||||
仓库信息
|
||||
</h3>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="edit-repo-url" className="font-mono font-bold uppercase text-xs text-gray-400">仓库地址</Label>
|
||||
<Label htmlFor="edit-repo-url" className="font-mono font-bold uppercase text-xs text-muted-foreground">仓库地址</Label>
|
||||
<Input
|
||||
id="edit-repo-url"
|
||||
value={editForm.repository_url}
|
||||
|
|
@ -774,7 +774,7 @@ export default function ProjectDetail() {
|
|||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label htmlFor="edit-repo-type" className="font-mono font-bold uppercase text-xs text-gray-400">仓库平台</Label>
|
||||
<Label htmlFor="edit-repo-type" className="font-mono font-bold uppercase text-xs text-muted-foreground">仓库平台</Label>
|
||||
<Select
|
||||
value={editForm.repository_type}
|
||||
onValueChange={(value: any) => setEditForm({ ...editForm, repository_type: value })}
|
||||
|
|
@ -782,7 +782,7 @@ export default function ProjectDetail() {
|
|||
<SelectTrigger id="edit-repo-type" className="cyber-input mt-1">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
||||
<SelectContent className="cyber-dialog border-border">
|
||||
<SelectItem value="github">GitHub</SelectItem>
|
||||
<SelectItem value="gitlab">GitLab</SelectItem>
|
||||
<SelectItem value="other">其他</SelectItem>
|
||||
|
|
@ -791,7 +791,7 @@ export default function ProjectDetail() {
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="edit-branch" className="font-mono font-bold uppercase text-xs text-gray-400">默认分支</Label>
|
||||
<Label htmlFor="edit-branch" className="font-mono font-bold uppercase text-xs text-muted-foreground">默认分支</Label>
|
||||
<Input
|
||||
id="edit-branch"
|
||||
value={editForm.default_branch}
|
||||
|
|
@ -806,7 +806,7 @@ export default function ProjectDetail() {
|
|||
|
||||
{/* ZIP项目提示 */}
|
||||
{editForm.source_type === 'zip' && (
|
||||
<div className="border-t border-gray-800 pt-4">
|
||||
<div className="border-t border-border pt-4">
|
||||
<div className="bg-amber-500/10 border border-amber-500/30 p-4 rounded">
|
||||
<div className="flex items-start space-x-3">
|
||||
<Upload className="w-5 h-5 text-amber-400 mt-0.5" />
|
||||
|
|
@ -822,26 +822,26 @@ export default function ProjectDetail() {
|
|||
)}
|
||||
|
||||
{/* 编程语言 */}
|
||||
<div className="space-y-4 border-t border-gray-800 pt-4">
|
||||
<h3 className="font-mono font-bold uppercase text-sm text-gray-400">编程语言</h3>
|
||||
<div className="space-y-4 border-t border-border pt-4">
|
||||
<h3 className="font-mono font-bold uppercase text-sm text-muted-foreground">编程语言</h3>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3">
|
||||
{supportedLanguages.map((lang) => (
|
||||
<div
|
||||
key={lang}
|
||||
className={`flex items-center space-x-2 p-3 border cursor-pointer transition-all rounded ${editForm.programming_languages?.includes(lang)
|
||||
? 'border-primary bg-primary/10 text-primary'
|
||||
: 'border-gray-700 hover:border-gray-600 text-gray-400'
|
||||
: 'border-border hover:border-border text-muted-foreground'
|
||||
}`}
|
||||
onClick={() => handleToggleLanguage(lang)}
|
||||
>
|
||||
<div
|
||||
className={`w-4 h-4 border-2 rounded-sm flex items-center justify-center ${editForm.programming_languages?.includes(lang)
|
||||
? 'bg-primary border-primary'
|
||||
: 'border-gray-600'
|
||||
: 'border-border'
|
||||
}`}
|
||||
>
|
||||
{editForm.programming_languages?.includes(lang) && (
|
||||
<CheckCircle className="w-3 h-3 text-white" />
|
||||
<CheckCircle className="w-3 h-3 text-foreground" />
|
||||
)}
|
||||
</div>
|
||||
<span className="text-sm font-bold font-mono">{lang}</span>
|
||||
|
|
@ -850,7 +850,7 @@ export default function ProjectDetail() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end space-x-3 pt-6 border-t border-gray-800">
|
||||
<div className="flex justify-end space-x-3 pt-6 border-t border-border">
|
||||
<Button onClick={handleSaveSettings} className="cyber-btn-primary">
|
||||
<Edit className="w-4 h-4 mr-2" />
|
||||
保存修改
|
||||
|
|
@ -880,21 +880,21 @@ export default function ProjectDetail() {
|
|||
|
||||
{/* 审计选项对话框 */}
|
||||
<Dialog open={showAuditOptionsDialog} onOpenChange={setShowAuditOptionsDialog}>
|
||||
<DialogContent className="max-w-md cyber-card border-gray-700 bg-[#0c0c12] p-0">
|
||||
<DialogContent className="max-w-md cyber-card border-border cyber-dialog p-0">
|
||||
{/* Terminal Header */}
|
||||
<div className="flex items-center gap-2 px-4 py-3 bg-[#0a0a0f] border-b border-gray-800/50">
|
||||
<div className="flex items-center gap-2 px-4 py-3 cyber-bg-elevated border-b border-border">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<div className="w-3 h-3 rounded-full bg-red-500/80" />
|
||||
<div className="w-3 h-3 rounded-full bg-yellow-500/80" />
|
||||
<div className="w-3 h-3 rounded-full bg-green-500/80" />
|
||||
</div>
|
||||
<span className="ml-2 font-mono text-[11px] text-gray-500 tracking-wider">
|
||||
<span className="ml-2 font-mono text-xs text-muted-foreground tracking-wider">
|
||||
audit_options@deepaudit
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<DialogHeader className="px-6 pt-4">
|
||||
<DialogTitle className="font-mono text-lg uppercase tracking-wider flex items-center gap-2 text-white">
|
||||
<DialogTitle className="font-mono text-lg uppercase tracking-wider flex items-center gap-2 text-foreground">
|
||||
<Shield className="w-5 h-5 text-primary" />
|
||||
选择审计方式
|
||||
</DialogTitle>
|
||||
|
|
@ -903,28 +903,28 @@ export default function ProjectDetail() {
|
|||
<div className="p-6 space-y-4">
|
||||
<Button
|
||||
onClick={handleStartFullAudit}
|
||||
className="w-full h-auto py-4 flex flex-col items-center justify-center space-y-2 cyber-btn-outline hover:bg-gray-800/50"
|
||||
className="w-full h-auto py-4 flex flex-col items-center justify-center space-y-2 cyber-btn-outline hover:bg-muted"
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Activity className="w-5 h-5" />
|
||||
<span className="text-lg font-bold uppercase">全量审计</span>
|
||||
</div>
|
||||
<span className="text-xs text-gray-500 font-mono">扫描项目中的所有文件</span>
|
||||
<span className="text-xs text-muted-foreground font-mono">扫描项目中的所有文件</span>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
onClick={handleOpenCustomAudit}
|
||||
className="w-full h-auto py-4 flex flex-col items-center justify-center space-y-2 cyber-btn-outline hover:bg-gray-800/50"
|
||||
className="w-full h-auto py-4 flex flex-col items-center justify-center space-y-2 cyber-btn-outline hover:bg-muted"
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
<FileText className="w-5 h-5" />
|
||||
<span className="text-lg font-bold uppercase">自定义审计</span>
|
||||
</div>
|
||||
<span className="text-xs text-gray-500 font-mono">选择特定文件进行扫描</span>
|
||||
<span className="text-xs text-muted-foreground font-mono">选择特定文件进行扫描</span>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<DialogFooter className="p-4 border-t border-gray-800 bg-gray-900/30">
|
||||
<DialogFooter className="p-4 border-t border-border bg-muted/50">
|
||||
<Button variant="outline" onClick={() => setShowAuditOptionsDialog(false)} className="w-full cyber-btn-outline">
|
||||
取消
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -275,7 +275,7 @@ export default function Projects() {
|
|||
switch (type) {
|
||||
case 'github': return <Github className="w-5 h-5" />;
|
||||
case 'gitlab': return <GitBranch className="w-5 h-5 text-orange-500" />;
|
||||
default: return <Folder className="w-5 h-5 text-gray-600" />;
|
||||
default: return <Folder className="w-5 h-5 text-muted-foreground" />;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -404,14 +404,14 @@ export default function Projects() {
|
|||
<div className="flex items-center justify-center min-h-[60vh]">
|
||||
<div className="text-center space-y-4">
|
||||
<div className="loading-spinner mx-auto" />
|
||||
<p className="text-gray-500 font-mono text-sm uppercase tracking-wider">加载项目数据...</p>
|
||||
<p className="text-muted-foreground font-mono text-sm uppercase tracking-wider">加载项目数据...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6 p-6 bg-[#0a0a0f] min-h-screen font-mono relative">
|
||||
<div className="space-y-6 p-6 bg-background min-h-screen font-mono relative">
|
||||
{/* Grid background */}
|
||||
<div className="absolute inset-0 cyber-grid-subtle pointer-events-none" />
|
||||
|
||||
|
|
@ -423,21 +423,21 @@ export default function Projects() {
|
|||
初始化项目
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="!w-[min(90vw,700px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 bg-[#0c0c12] border border-gray-800 rounded-lg">
|
||||
<DialogContent className="!w-[min(90vw,700px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 cyber-dialog border border-border rounded-lg">
|
||||
{/* Terminal Header */}
|
||||
<div className="flex items-center gap-2 px-4 py-3 bg-[#0a0a0f] border-b border-gray-800/50 flex-shrink-0">
|
||||
<div className="flex items-center gap-2 px-4 py-3 cyber-bg-elevated border-b border-border flex-shrink-0">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<div className="w-3 h-3 rounded-full bg-red-500/80" />
|
||||
<div className="w-3 h-3 rounded-full bg-yellow-500/80" />
|
||||
<div className="w-3 h-3 rounded-full bg-green-500/80" />
|
||||
</div>
|
||||
<span className="ml-2 font-mono text-[11px] text-gray-500 tracking-wider">
|
||||
<span className="ml-2 font-mono text-xs text-muted-foreground tracking-wider">
|
||||
new_project@deepaudit
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<DialogHeader className="px-6 pt-4 flex-shrink-0">
|
||||
<DialogTitle className="font-mono text-lg uppercase tracking-wider flex items-center gap-2 text-white">
|
||||
<DialogTitle className="font-mono text-lg uppercase tracking-wider flex items-center gap-2 text-foreground">
|
||||
<Terminal className="w-5 h-5 text-primary" />
|
||||
初始化新项目
|
||||
</DialogTitle>
|
||||
|
|
@ -445,17 +445,17 @@ export default function Projects() {
|
|||
|
||||
<div className="flex-1 overflow-y-auto p-6">
|
||||
<Tabs defaultValue="repository" className="w-full">
|
||||
<TabsList className="flex w-full bg-gray-900/50 border border-gray-800 p-1 h-auto gap-1 rounded">
|
||||
<TabsList className="flex w-full bg-muted border border-border p-1 h-auto gap-1 rounded">
|
||||
<TabsTrigger
|
||||
value="repository"
|
||||
className="flex-1 data-[state=active]:bg-primary data-[state=active]:text-white font-mono font-bold uppercase py-2 text-gray-400 transition-all rounded-sm"
|
||||
className="flex-1 data-[state=active]:bg-primary data-[state=active]:text-foreground font-mono font-bold uppercase py-2 text-muted-foreground transition-all rounded-sm"
|
||||
>
|
||||
<GitBranch className="w-4 h-4 mr-2" />
|
||||
Git 仓库
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="upload"
|
||||
className="flex-1 data-[state=active]:bg-primary data-[state=active]:text-white font-mono font-bold uppercase py-2 text-gray-400 transition-all rounded-sm"
|
||||
className="flex-1 data-[state=active]:bg-primary data-[state=active]:text-foreground font-mono font-bold uppercase py-2 text-muted-foreground transition-all rounded-sm"
|
||||
>
|
||||
<Upload className="w-4 h-4 mr-2" />
|
||||
上传源码
|
||||
|
|
@ -465,7 +465,7 @@ export default function Projects() {
|
|||
<TabsContent value="repository" className="flex flex-col gap-5 mt-5">
|
||||
<div className="grid grid-cols-2 gap-5">
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="name" className="font-mono font-bold uppercase text-xs text-gray-400">项目名称 *</Label>
|
||||
<Label htmlFor="name" className="font-mono font-bold uppercase text-xs text-muted-foreground">项目名称 *</Label>
|
||||
<Input
|
||||
id="name"
|
||||
value={createForm.name}
|
||||
|
|
@ -475,7 +475,7 @@ export default function Projects() {
|
|||
/>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="repository_type" className="font-mono font-bold uppercase text-xs text-gray-400">仓库类型</Label>
|
||||
<Label htmlFor="repository_type" className="font-mono font-bold uppercase text-xs text-muted-foreground">仓库类型</Label>
|
||||
<Select
|
||||
value={createForm.repository_type}
|
||||
onValueChange={(value: any) => setCreateForm({ ...createForm, repository_type: value })}
|
||||
|
|
@ -483,7 +483,7 @@ export default function Projects() {
|
|||
<SelectTrigger className="cyber-input">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
||||
<SelectContent className="cyber-dialog border-border">
|
||||
<SelectItem value="github">GITHUB</SelectItem>
|
||||
<SelectItem value="gitlab">GITLAB</SelectItem>
|
||||
<SelectItem value="other">OTHER</SelectItem>
|
||||
|
|
@ -493,7 +493,7 @@ export default function Projects() {
|
|||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="description" className="font-mono font-bold uppercase text-xs text-gray-400">描述</Label>
|
||||
<Label htmlFor="description" className="font-mono font-bold uppercase text-xs text-muted-foreground">描述</Label>
|
||||
<Textarea
|
||||
id="description"
|
||||
value={createForm.description}
|
||||
|
|
@ -506,7 +506,7 @@ export default function Projects() {
|
|||
|
||||
<div className="grid grid-cols-2 gap-5">
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="repository_url" className="font-mono font-bold uppercase text-xs text-gray-400">仓库地址</Label>
|
||||
<Label htmlFor="repository_url" className="font-mono font-bold uppercase text-xs text-muted-foreground">仓库地址</Label>
|
||||
<Input
|
||||
id="repository_url"
|
||||
value={createForm.repository_url}
|
||||
|
|
@ -516,7 +516,7 @@ export default function Projects() {
|
|||
/>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="default_branch" className="font-mono font-bold uppercase text-xs text-gray-400">默认分支</Label>
|
||||
<Label htmlFor="default_branch" className="font-mono font-bold uppercase text-xs text-muted-foreground">默认分支</Label>
|
||||
<Input
|
||||
id="default_branch"
|
||||
value={createForm.default_branch}
|
||||
|
|
@ -528,12 +528,12 @@ export default function Projects() {
|
|||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="font-mono font-bold uppercase text-xs text-gray-400">技术栈</Label>
|
||||
<Label className="font-mono font-bold uppercase text-xs text-muted-foreground">技术栈</Label>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{supportedLanguages.map((lang) => (
|
||||
<label key={lang} className={`flex items-center space-x-2 px-3 py-1.5 border cursor-pointer transition-all rounded ${createForm.programming_languages.includes(lang)
|
||||
? 'border-primary bg-primary/10 text-primary'
|
||||
: 'border-gray-700 hover:border-gray-600 text-gray-400'
|
||||
: 'border-border hover:border-border text-muted-foreground'
|
||||
}`}>
|
||||
<input
|
||||
type="checkbox"
|
||||
|
|
@ -551,7 +551,7 @@ export default function Projects() {
|
|||
});
|
||||
}
|
||||
}}
|
||||
className="rounded border border-gray-600 w-3.5 h-3.5 text-primary focus:ring-0 bg-transparent"
|
||||
className="rounded border border-border w-3.5 h-3.5 text-primary focus:ring-0 bg-transparent"
|
||||
/>
|
||||
<span className="text-xs font-mono font-bold uppercase">{lang}</span>
|
||||
</label>
|
||||
|
|
@ -559,7 +559,7 @@ export default function Projects() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end space-x-4 pt-4 border-t border-gray-800">
|
||||
<div className="flex justify-end space-x-4 pt-4 border-t border-border">
|
||||
<Button variant="outline" onClick={() => setShowCreateDialog(false)} className="cyber-btn-outline">
|
||||
取消
|
||||
</Button>
|
||||
|
|
@ -571,7 +571,7 @@ export default function Projects() {
|
|||
|
||||
<TabsContent value="upload" className="flex flex-col gap-5 mt-5">
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="upload-name" className="font-mono font-bold uppercase text-xs text-gray-400">项目名称 *</Label>
|
||||
<Label htmlFor="upload-name" className="font-mono font-bold uppercase text-xs text-muted-foreground">项目名称 *</Label>
|
||||
<Input
|
||||
id="upload-name"
|
||||
value={createForm.name}
|
||||
|
|
@ -582,7 +582,7 @@ export default function Projects() {
|
|||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="upload-description" className="font-mono font-bold uppercase text-xs text-gray-400">描述</Label>
|
||||
<Label htmlFor="upload-description" className="font-mono font-bold uppercase text-xs text-muted-foreground">描述</Label>
|
||||
<Textarea
|
||||
id="upload-description"
|
||||
value={createForm.description}
|
||||
|
|
@ -594,12 +594,12 @@ export default function Projects() {
|
|||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="font-mono font-bold uppercase text-xs text-gray-400">技术栈</Label>
|
||||
<Label className="font-mono font-bold uppercase text-xs text-muted-foreground">技术栈</Label>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{supportedLanguages.map((lang) => (
|
||||
<label key={lang} className={`flex items-center space-x-2 px-3 py-1.5 border cursor-pointer transition-all rounded ${createForm.programming_languages.includes(lang)
|
||||
? 'border-primary bg-primary/10 text-primary'
|
||||
: 'border-gray-700 hover:border-gray-600 text-gray-400'
|
||||
: 'border-border hover:border-border text-muted-foreground'
|
||||
}`}>
|
||||
<input
|
||||
type="checkbox"
|
||||
|
|
@ -617,7 +617,7 @@ export default function Projects() {
|
|||
});
|
||||
}
|
||||
}}
|
||||
className="rounded border border-gray-600 w-3.5 h-3.5 text-primary focus:ring-0 bg-transparent"
|
||||
className="rounded border border-border w-3.5 h-3.5 text-primary focus:ring-0 bg-transparent"
|
||||
/>
|
||||
<span className="text-xs font-mono font-bold uppercase">{lang}</span>
|
||||
</label>
|
||||
|
|
@ -626,16 +626,16 @@ export default function Projects() {
|
|||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<Label className="font-mono font-bold uppercase text-xs text-gray-400">源代码</Label>
|
||||
<Label className="font-mono font-bold uppercase text-xs text-muted-foreground">源代码</Label>
|
||||
|
||||
{!selectedFile ? (
|
||||
<div
|
||||
className="border border-dashed border-gray-700 bg-gray-900/30 rounded p-6 text-center hover:bg-gray-900/50 hover:border-gray-600 transition-colors cursor-pointer group"
|
||||
className="border border-dashed border-border bg-muted/50 rounded p-6 text-center hover:bg-muted hover:border-border transition-colors cursor-pointer group"
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
>
|
||||
<Upload className="w-10 h-10 text-gray-500 mx-auto mb-3 group-hover:text-primary transition-colors" />
|
||||
<h3 className="text-base font-bold text-gray-300 uppercase mb-1">上传 ZIP 归档</h3>
|
||||
<p className="text-[10px] font-mono text-gray-500 mb-3">
|
||||
<Upload className="w-10 h-10 text-muted-foreground mx-auto mb-3 group-hover:text-primary transition-colors" />
|
||||
<h3 className="text-base font-bold text-foreground uppercase mb-1">上传 ZIP 归档</h3>
|
||||
<p className="text-xs font-mono text-muted-foreground mb-3">
|
||||
最大: 500MB // 格式: .ZIP
|
||||
</p>
|
||||
<input
|
||||
|
|
@ -661,14 +661,14 @@ export default function Projects() {
|
|||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="border border-gray-700 bg-gray-900/30 p-4 flex items-center justify-between rounded">
|
||||
<div className="border border-border bg-muted/50 p-4 flex items-center justify-between rounded">
|
||||
<div className="flex items-center space-x-3 overflow-hidden">
|
||||
<div className="w-10 h-10 bg-gray-800 border border-gray-700 rounded flex items-center justify-center flex-shrink-0">
|
||||
<div className="w-10 h-10 bg-muted border border-border rounded flex items-center justify-center flex-shrink-0">
|
||||
<FileText className="w-5 h-5 text-primary" />
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<p className="font-mono font-bold text-sm text-gray-200 truncate">{selectedFile.name}</p>
|
||||
<p className="font-mono text-xs text-gray-500">{(selectedFile.size / 1024 / 1024).toFixed(2)} MB</p>
|
||||
<p className="font-mono font-bold text-sm text-foreground truncate">{selectedFile.name}</p>
|
||||
<p className="font-mono text-xs text-muted-foreground">{(selectedFile.size / 1024 / 1024).toFixed(2)} MB</p>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
|
|
@ -685,18 +685,18 @@ export default function Projects() {
|
|||
|
||||
{uploading && (
|
||||
<div className="space-y-1.5">
|
||||
<div className="flex items-center justify-between text-xs font-mono text-gray-400">
|
||||
<div className="flex items-center justify-between text-xs font-mono text-muted-foreground">
|
||||
<span>上传并分析中...</span>
|
||||
<span className="text-primary">{uploadProgress}%</span>
|
||||
</div>
|
||||
<Progress value={uploadProgress} className="h-2 bg-gray-800 [&>div]:bg-primary" />
|
||||
<Progress value={uploadProgress} className="h-2 bg-muted [&>div]:bg-primary" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="bg-amber-500/10 border border-amber-500/30 p-3 rounded">
|
||||
<div className="flex items-start space-x-3">
|
||||
<AlertCircle className="w-4 h-4 text-amber-400 mt-0.5" />
|
||||
<div className="text-[10px] font-mono text-amber-300">
|
||||
<div className="text-xs font-mono text-amber-300">
|
||||
<p className="font-bold mb-1 uppercase">上传协议:</p>
|
||||
<ul className="space-y-0.5 list-disc list-inside text-amber-400/80">
|
||||
<li>确保完整的项目代码</li>
|
||||
|
|
@ -708,7 +708,7 @@ export default function Projects() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end space-x-4 pt-4 border-t border-gray-800 mt-auto">
|
||||
<div className="flex justify-end space-x-4 pt-4 border-t border-border mt-auto">
|
||||
<Button variant="outline" onClick={() => setShowCreateDialog(false)} disabled={uploading} className="cyber-btn-outline">
|
||||
取消
|
||||
</Button>
|
||||
|
|
@ -782,7 +782,7 @@ export default function Projects() {
|
|||
{/* Search and Filter */}
|
||||
<div className="cyber-card p-4 flex items-center gap-4 relative z-10">
|
||||
<div className="flex-1 relative">
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500 w-4 h-4 z-10" />
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground w-4 h-4 z-10" />
|
||||
<Input
|
||||
placeholder="搜索项目..."
|
||||
value={searchTerm}
|
||||
|
|
@ -802,13 +802,13 @@ export default function Projects() {
|
|||
filteredProjects.map((project) => (
|
||||
<div key={project.id} className="cyber-card flex flex-col h-full group">
|
||||
{/* Card Header */}
|
||||
<div className="p-4 border-b border-gray-800/50 bg-gray-900/30 flex justify-between items-start">
|
||||
<div className="p-4 border-b border-border bg-muted/50 flex justify-between items-start">
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="w-10 h-10 border border-gray-700 bg-gray-800/50 rounded flex items-center justify-center text-gray-400">
|
||||
<div className="w-10 h-10 border border-border bg-muted rounded flex items-center justify-center text-muted-foreground">
|
||||
{getRepositoryIcon(project.repository_type)}
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-bold text-base text-gray-200 group-hover:text-primary transition-colors">
|
||||
<h3 className="font-bold text-base text-foreground group-hover:text-primary transition-colors">
|
||||
<Link to={`/projects/${project.id}`}>
|
||||
{project.name}
|
||||
</Link>
|
||||
|
|
@ -828,15 +828,15 @@ export default function Projects() {
|
|||
{/* Card Body */}
|
||||
<div className="p-4 flex-1 space-y-3">
|
||||
{project.description && (
|
||||
<p className="text-sm text-gray-400 font-mono line-clamp-2 border-l-2 border-gray-700 pl-2">
|
||||
<p className="text-sm text-muted-foreground font-mono line-clamp-2 border-l-2 border-border pl-2">
|
||||
{project.description}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div className="space-y-2">
|
||||
{project.repository_url && (
|
||||
<div className="flex items-center text-xs font-mono text-gray-500 bg-gray-900/50 p-2 border border-gray-800 rounded">
|
||||
<GitBranch className="w-3 h-3 mr-2 flex-shrink-0 text-gray-600" />
|
||||
<div className="flex items-center text-xs font-mono text-muted-foreground bg-muted p-2 border border-border rounded">
|
||||
<GitBranch className="w-3 h-3 mr-2 flex-shrink-0 text-muted-foreground" />
|
||||
<a
|
||||
href={project.repository_url}
|
||||
target="_blank"
|
||||
|
|
@ -848,7 +848,7 @@ export default function Projects() {
|
|||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex justify-between items-center text-xs font-mono text-gray-500">
|
||||
<div className="flex justify-between items-center text-xs font-mono text-muted-foreground">
|
||||
<span className="flex items-center"><Calendar className="w-3 h-3 mr-1" /> {formatDate(project.created_at)}</span>
|
||||
<span className="flex items-center"><Users className="w-3 h-3 mr-1" /> {project.owner?.full_name || '未知'}</span>
|
||||
</div>
|
||||
|
|
@ -857,12 +857,12 @@ export default function Projects() {
|
|||
{project.programming_languages && (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{JSON.parse(project.programming_languages).slice(0, 4).map((lang: string) => (
|
||||
<span key={lang} className="text-[10px] font-mono font-bold border border-primary/30 px-1.5 py-0.5 bg-primary/10 text-primary rounded">
|
||||
<span key={lang} className="text-xs font-mono font-bold border border-primary/30 px-1.5 py-0.5 bg-primary/10 text-primary rounded">
|
||||
{lang.toUpperCase()}
|
||||
</span>
|
||||
))}
|
||||
{JSON.parse(project.programming_languages).length > 4 && (
|
||||
<span className="text-[10px] font-mono font-bold border border-gray-700 px-1.5 py-0.5 bg-gray-800/50 text-gray-400 rounded">
|
||||
<span className="text-xs font-mono font-bold border border-border px-1.5 py-0.5 bg-muted text-muted-foreground rounded">
|
||||
+{JSON.parse(project.programming_languages).length - 4}
|
||||
</span>
|
||||
)}
|
||||
|
|
@ -871,7 +871,7 @@ export default function Projects() {
|
|||
</div>
|
||||
|
||||
{/* Card Footer */}
|
||||
<div className="p-4 border-t border-gray-800/50 bg-gray-900/30 grid grid-cols-2 gap-2">
|
||||
<div className="p-4 border-t border-border bg-muted/50 grid grid-cols-2 gap-2">
|
||||
<Link to={`/projects/${project.id}`} className="col-span-2">
|
||||
<Button variant="outline" className="w-full cyber-btn-outline h-8 text-xs">
|
||||
<Code className="w-3 h-3 mr-2" />
|
||||
|
|
@ -897,11 +897,11 @@ export default function Projects() {
|
|||
) : (
|
||||
<div className="col-span-full">
|
||||
<div className="cyber-card p-16 text-center border-dashed">
|
||||
<Code className="w-16 h-16 text-gray-600 mx-auto mb-4" />
|
||||
<h3 className="text-xl font-bold text-gray-300 mb-2">
|
||||
<Code className="w-16 h-16 text-muted-foreground mx-auto mb-4" />
|
||||
<h3 className="text-xl font-bold text-foreground mb-2">
|
||||
{searchTerm ? '未找到匹配项' : '未初始化项目'}
|
||||
</h3>
|
||||
<p className="text-gray-500 font-mono mb-6">
|
||||
<p className="text-muted-foreground font-mono mb-6">
|
||||
{searchTerm ? '调整搜索参数' : '初始化第一个项目以开始'}
|
||||
</p>
|
||||
{!searchTerm && (
|
||||
|
|
@ -934,21 +934,21 @@ export default function Projects() {
|
|||
|
||||
{/* Edit Dialog */}
|
||||
<Dialog open={showEditDialog} onOpenChange={setShowEditDialog}>
|
||||
<DialogContent className="!w-[min(90vw,700px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 bg-[#0c0c12] border border-gray-800 rounded-lg">
|
||||
<DialogContent className="!w-[min(90vw,700px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 cyber-dialog border border-border rounded-lg">
|
||||
{/* Terminal Header */}
|
||||
<div className="flex items-center gap-2 px-4 py-3 bg-[#0a0a0f] border-b border-gray-800/50 flex-shrink-0">
|
||||
<div className="flex items-center gap-2 px-4 py-3 cyber-bg-elevated border-b border-border flex-shrink-0">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<div className="w-3 h-3 rounded-full bg-red-500/80" />
|
||||
<div className="w-3 h-3 rounded-full bg-yellow-500/80" />
|
||||
<div className="w-3 h-3 rounded-full bg-green-500/80" />
|
||||
</div>
|
||||
<span className="ml-2 font-mono text-[11px] text-gray-500 tracking-wider">
|
||||
<span className="ml-2 font-mono text-xs text-muted-foreground tracking-wider">
|
||||
edit_project@deepaudit
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<DialogHeader className="px-6 pt-4 flex-shrink-0">
|
||||
<DialogTitle className="font-mono text-lg uppercase tracking-wider flex items-center gap-2 text-white">
|
||||
<DialogTitle className="font-mono text-lg uppercase tracking-wider flex items-center gap-2 text-foreground">
|
||||
<Edit className="w-5 h-5 text-primary" />
|
||||
编辑项目配置
|
||||
{projectToEdit && (
|
||||
|
|
@ -962,9 +962,9 @@ export default function Projects() {
|
|||
<div className="flex-1 overflow-y-auto p-6 space-y-6">
|
||||
{/* 基本信息 */}
|
||||
<div className="space-y-4">
|
||||
<h3 className="font-mono font-bold uppercase text-sm text-gray-400 border-b border-gray-800 pb-2">基本信息</h3>
|
||||
<h3 className="font-mono font-bold uppercase text-sm text-muted-foreground border-b border-border pb-2">基本信息</h3>
|
||||
<div>
|
||||
<Label htmlFor="edit-name" className="font-mono font-bold uppercase text-xs text-gray-400">项目名称 *</Label>
|
||||
<Label htmlFor="edit-name" className="font-mono font-bold uppercase text-xs text-muted-foreground">项目名称 *</Label>
|
||||
<Input
|
||||
id="edit-name"
|
||||
value={editForm.name}
|
||||
|
|
@ -973,7 +973,7 @@ export default function Projects() {
|
|||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="edit-description" className="font-mono font-bold uppercase text-xs text-gray-400">描述</Label>
|
||||
<Label htmlFor="edit-description" className="font-mono font-bold uppercase text-xs text-muted-foreground">描述</Label>
|
||||
<Textarea
|
||||
id="edit-description"
|
||||
value={editForm.description}
|
||||
|
|
@ -987,13 +987,13 @@ export default function Projects() {
|
|||
{/* 仓库信息 - 仅远程仓库类型显示 */}
|
||||
{editForm.source_type === 'repository' && (
|
||||
<div className="space-y-4">
|
||||
<h3 className="font-mono font-bold uppercase text-sm text-gray-400 border-b border-gray-800 pb-2 flex items-center gap-2">
|
||||
<h3 className="font-mono font-bold uppercase text-sm text-muted-foreground border-b border-border pb-2 flex items-center gap-2">
|
||||
<GitBranch className="w-4 h-4" />
|
||||
仓库信息
|
||||
</h3>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="edit-repo-url" className="font-mono font-bold uppercase text-xs text-gray-400">仓库地址</Label>
|
||||
<Label htmlFor="edit-repo-url" className="font-mono font-bold uppercase text-xs text-muted-foreground">仓库地址</Label>
|
||||
<Input
|
||||
id="edit-repo-url"
|
||||
value={editForm.repository_url}
|
||||
|
|
@ -1005,7 +1005,7 @@ export default function Projects() {
|
|||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label htmlFor="edit-repo-type" className="font-mono font-bold uppercase text-xs text-gray-400">仓库平台</Label>
|
||||
<Label htmlFor="edit-repo-type" className="font-mono font-bold uppercase text-xs text-muted-foreground">仓库平台</Label>
|
||||
<Select
|
||||
value={editForm.repository_type}
|
||||
onValueChange={(value: any) => setEditForm({ ...editForm, repository_type: value })}
|
||||
|
|
@ -1013,7 +1013,7 @@ export default function Projects() {
|
|||
<SelectTrigger id="edit-repo-type" className="cyber-input mt-1">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
||||
<SelectContent className="cyber-dialog border-border">
|
||||
<SelectItem value="github">GITHUB</SelectItem>
|
||||
<SelectItem value="gitlab">GITLAB</SelectItem>
|
||||
<SelectItem value="other">OTHER</SelectItem>
|
||||
|
|
@ -1022,7 +1022,7 @@ export default function Projects() {
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="edit-default-branch" className="font-mono font-bold uppercase text-xs text-gray-400">默认分支</Label>
|
||||
<Label htmlFor="edit-default-branch" className="font-mono font-bold uppercase text-xs text-muted-foreground">默认分支</Label>
|
||||
<Input
|
||||
id="edit-default-branch"
|
||||
value={editForm.default_branch}
|
||||
|
|
@ -1038,7 +1038,7 @@ export default function Projects() {
|
|||
{/* ZIP项目文件管理 */}
|
||||
{editForm.source_type === 'zip' && (
|
||||
<div className="space-y-4">
|
||||
<h3 className="font-mono font-bold uppercase text-sm text-gray-400 border-b border-gray-800 pb-2 flex items-center gap-2">
|
||||
<h3 className="font-mono font-bold uppercase text-sm text-muted-foreground border-b border-border pb-2 flex items-center gap-2">
|
||||
<Upload className="w-4 h-4" />
|
||||
ZIP文件管理
|
||||
</h3>
|
||||
|
|
@ -1087,7 +1087,7 @@ export default function Projects() {
|
|||
|
||||
{/* 上传新文件 */}
|
||||
<div className="space-y-2">
|
||||
<Label className="font-mono font-bold uppercase text-xs text-gray-400">
|
||||
<Label className="font-mono font-bold uppercase text-xs text-muted-foreground">
|
||||
{editZipInfo?.has_file ? '更新ZIP文件' : '上传ZIP文件'}
|
||||
</Label>
|
||||
<input
|
||||
|
|
@ -1115,7 +1115,7 @@ export default function Projects() {
|
|||
<div className="flex items-center space-x-2">
|
||||
<FileText className="w-4 h-4 text-sky-400" />
|
||||
<span className="text-sm font-mono font-bold text-sky-300">{editZipFile.name}</span>
|
||||
<span className="text-xs text-gray-500">
|
||||
<span className="text-xs text-muted-foreground">
|
||||
({(editZipFile.size / 1024 / 1024).toFixed(2)} MB)
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -1144,25 +1144,25 @@ export default function Projects() {
|
|||
|
||||
{/* 技术栈 */}
|
||||
<div className="space-y-4">
|
||||
<h3 className="font-mono font-bold uppercase text-sm text-gray-400 border-b border-gray-800 pb-2">技术栈</h3>
|
||||
<h3 className="font-mono font-bold uppercase text-sm text-muted-foreground border-b border-border pb-2">技术栈</h3>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3">
|
||||
{supportedLanguages.map((lang) => (
|
||||
<div
|
||||
key={lang}
|
||||
className={`flex items-center space-x-2 p-2 border cursor-pointer transition-all rounded ${editForm.programming_languages?.includes(lang)
|
||||
? 'border-primary bg-primary/10 text-primary'
|
||||
: 'border-gray-700 hover:border-gray-600 text-gray-400'
|
||||
: 'border-border hover:border-border text-muted-foreground'
|
||||
}`}
|
||||
onClick={() => handleToggleLanguage(lang)}
|
||||
>
|
||||
<div
|
||||
className={`w-4 h-4 border-2 rounded-sm flex items-center justify-center ${editForm.programming_languages?.includes(lang)
|
||||
? 'bg-primary border-primary'
|
||||
: 'border-gray-600'
|
||||
: 'border-border'
|
||||
}`}
|
||||
>
|
||||
{editForm.programming_languages?.includes(lang) && (
|
||||
<CheckCircle className="w-3 h-3 text-white" />
|
||||
<CheckCircle className="w-3 h-3 text-foreground" />
|
||||
)}
|
||||
</div>
|
||||
<span className="text-sm font-mono font-bold uppercase">{lang}</span>
|
||||
|
|
@ -1172,7 +1172,7 @@ export default function Projects() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex-shrink-0 flex justify-end gap-3 px-6 py-4 bg-gray-900/50 border-t border-gray-800">
|
||||
<div className="flex-shrink-0 flex justify-end gap-3 px-6 py-4 bg-muted border-t border-border">
|
||||
<Button variant="outline" onClick={() => setShowEditDialog(false)} className="cyber-btn-outline">
|
||||
取消
|
||||
</Button>
|
||||
|
|
@ -1185,7 +1185,7 @@ export default function Projects() {
|
|||
|
||||
{/* Delete Dialog */}
|
||||
<AlertDialog open={showDeleteDialog} onOpenChange={setShowDeleteDialog}>
|
||||
<AlertDialogContent className="cyber-card border-gray-700 bg-[#0c0c12] p-0 !fixed">
|
||||
<AlertDialogContent className="cyber-card border-border cyber-dialog p-0 !fixed">
|
||||
{/* Terminal Header */}
|
||||
<div className="flex items-center gap-2 px-4 py-3 bg-rose-500/10 border-b border-rose-500/30">
|
||||
<div className="flex items-center gap-1.5">
|
||||
|
|
@ -1193,17 +1193,17 @@ export default function Projects() {
|
|||
<div className="w-3 h-3 rounded-full bg-yellow-500/80" />
|
||||
<div className="w-3 h-3 rounded-full bg-green-500/80" />
|
||||
</div>
|
||||
<span className="ml-2 font-mono text-[11px] text-rose-400 tracking-wider">
|
||||
<span className="ml-2 font-mono text-xs text-rose-400 tracking-wider">
|
||||
confirm_delete@deepaudit
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<AlertDialogHeader className="p-6">
|
||||
<AlertDialogTitle className="font-mono text-lg uppercase tracking-wider flex items-center gap-2 text-white">
|
||||
<AlertDialogTitle className="font-mono text-lg uppercase tracking-wider flex items-center gap-2 text-foreground">
|
||||
<Trash2 className="w-5 h-5 text-rose-400" />
|
||||
确认删除
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription className="text-gray-400 font-mono">
|
||||
<AlertDialogDescription className="text-muted-foreground font-mono">
|
||||
您确定要移动 <span className="font-bold text-rose-400">"{projectToDelete?.name}"</span> 到回收站吗?
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
|
|
@ -1220,11 +1220,11 @@ export default function Projects() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<AlertDialogFooter className="p-4 border-t border-gray-800 bg-gray-900/30">
|
||||
<AlertDialogFooter className="p-4 border-t border-border bg-muted/50">
|
||||
<AlertDialogCancel className="cyber-btn-outline">取消</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={handleConfirmDelete}
|
||||
className="cyber-btn bg-rose-500/90 border-rose-500/50 text-white hover:bg-rose-500"
|
||||
className="cyber-btn bg-rose-500/90 border-rose-500/50 text-foreground hover:bg-rose-500"
|
||||
>
|
||||
确认删除
|
||||
</AlertDialogAction>
|
||||
|
|
|
|||
|
|
@ -180,17 +180,17 @@ export default function PromptManager() {
|
|||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-screen bg-[#0a0a0f]">
|
||||
<div className="flex items-center justify-center min-h-screen cyber-bg-elevated">
|
||||
<div className="text-center space-y-4">
|
||||
<div className="loading-spinner mx-auto" />
|
||||
<p className="text-gray-500 font-mono text-sm uppercase tracking-wider">加载中...</p>
|
||||
<p className="text-muted-foreground font-mono text-sm uppercase tracking-wider">加载中...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6 p-6 bg-[#0a0a0f] min-h-screen font-mono relative">
|
||||
<div className="space-y-6 p-6 cyber-bg-elevated min-h-screen font-mono relative">
|
||||
{/* Grid background */}
|
||||
<div className="absolute inset-0 cyber-grid-subtle pointer-events-none" />
|
||||
|
||||
|
|
@ -249,7 +249,7 @@ export default function PromptManager() {
|
|||
<div className="cyber-card p-0 relative z-10">
|
||||
<div className="cyber-card-header">
|
||||
<Terminal className="w-5 h-5 text-primary" />
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-white">提示词模板管理</h3>
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-foreground">提示词模板管理</h3>
|
||||
<div className="ml-auto">
|
||||
<Button onClick={() => { resetForm(); setShowCreateDialog(true); }} className="cyber-btn-primary h-9">
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
|
|
@ -279,15 +279,15 @@ export default function PromptManager() {
|
|||
return (
|
||||
<div key={template.id} className={`cyber-card p-0 ${!template.is_active ? 'opacity-60' : ''}`}>
|
||||
{/* Template Header */}
|
||||
<div className="p-5 border-b border-gray-800">
|
||||
<div className="p-5 border-b border-border">
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 bg-gray-800 border border-gray-700 flex items-center justify-center rounded">
|
||||
<TemplateIcon className="w-5 h-5 text-gray-400" />
|
||||
<div className="w-10 h-10 bg-muted border border-border flex items-center justify-center rounded">
|
||||
<TemplateIcon className="w-5 h-5 text-muted-foreground" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-bold text-base text-white uppercase">{template.name}</h3>
|
||||
<p className="text-xs text-gray-500 line-clamp-1">{template.description}</p>
|
||||
<h3 className="font-bold text-base text-foreground uppercase">{template.name}</h3>
|
||||
<p className="text-xs text-muted-foreground line-clamp-1">{template.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -301,7 +301,7 @@ export default function PromptManager() {
|
|||
{/* Template Content Preview */}
|
||||
<div className="p-4">
|
||||
<div
|
||||
className="text-xs text-emerald-400 line-clamp-3 bg-[#0a0a0f] p-3 border border-gray-800 font-mono mb-4 cursor-pointer hover:border-gray-700 transition-colors rounded"
|
||||
className="text-xs text-emerald-400 line-clamp-3 cyber-bg-elevated p-3 border border-border font-mono mb-4 cursor-pointer hover:border-border transition-colors rounded"
|
||||
onClick={() => openViewDialog(template)}
|
||||
title="点击查看完整内容"
|
||||
>
|
||||
|
|
@ -344,9 +344,9 @@ export default function PromptManager() {
|
|||
|
||||
{/* Create/Edit Dialog */}
|
||||
<Dialog open={showCreateDialog || showEditDialog} onOpenChange={(open) => { if (!open) { setShowCreateDialog(false); setShowEditDialog(false); } }}>
|
||||
<DialogContent className="!w-[min(90vw,700px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 bg-[#0c0c12] border border-gray-800 rounded-lg">
|
||||
<DialogHeader className="px-6 py-4 border-b border-gray-800 flex-shrink-0 bg-gray-900/50">
|
||||
<DialogTitle className="flex items-center gap-3 font-mono text-white">
|
||||
<DialogContent className="!w-[min(90vw,700px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 cyber-dialog border border-border rounded-lg">
|
||||
<DialogHeader className="px-6 py-4 border-b border-border flex-shrink-0 bg-muted">
|
||||
<DialogTitle className="flex items-center gap-3 font-mono text-foreground">
|
||||
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
||||
<Terminal className="w-5 h-5 text-primary" />
|
||||
</div>
|
||||
|
|
@ -354,7 +354,7 @@ export default function PromptManager() {
|
|||
<span className="text-base font-bold uppercase tracking-wider">
|
||||
{showEditDialog ? '编辑模板' : '新建模板'}
|
||||
</span>
|
||||
<p className="text-xs text-gray-500 font-normal mt-0.5">
|
||||
<p className="text-xs text-muted-foreground font-normal mt-0.5">
|
||||
{showEditDialog ? 'Edit Template' : 'Create Template'}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -363,29 +363,29 @@ export default function PromptManager() {
|
|||
<div className="flex-1 overflow-y-auto p-6 space-y-4">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">模板名称 *</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">模板名称 *</Label>
|
||||
<Input value={form.name} onChange={e => setForm({ ...form, name: e.target.value })} placeholder="如:安全专项审计" className="cyber-input" />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">模板类型</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">模板类型</Label>
|
||||
<Select value={form.template_type} onValueChange={v => setForm({ ...form, template_type: v })}>
|
||||
<SelectTrigger className="cyber-input"><SelectValue /></SelectTrigger>
|
||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
||||
<SelectContent className="cyber-dialog border-border">
|
||||
{TEMPLATE_TYPES.map(t => <SelectItem key={t.value} value={t.value}>{t.label}</SelectItem>)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">描述</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">描述</Label>
|
||||
<Input value={form.description} onChange={e => setForm({ ...form, description: e.target.value })} placeholder="模板用途描述" className="cyber-input" />
|
||||
</div>
|
||||
<Tabs defaultValue="zh" className="w-full">
|
||||
<TabsList className="grid w-full grid-cols-2 bg-gray-900/50 border border-gray-800 p-1 h-auto gap-1 rounded">
|
||||
<TabsTrigger value="zh" className="data-[state=active]:bg-primary data-[state=active]:text-white font-mono font-bold uppercase py-2 text-gray-400 transition-all rounded-sm text-xs">
|
||||
<TabsList className="grid w-full grid-cols-2 bg-muted border border-border p-1 h-auto gap-1 rounded">
|
||||
<TabsTrigger value="zh" className="data-[state=active]:bg-primary data-[state=active]:text-foreground font-mono font-bold uppercase py-2 text-muted-foreground transition-all rounded-sm text-xs">
|
||||
中文提示词
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="en" className="data-[state=active]:bg-primary data-[state=active]:text-white font-mono font-bold uppercase py-2 text-gray-400 transition-all rounded-sm text-xs">
|
||||
<TabsTrigger value="en" className="data-[state=active]:bg-primary data-[state=active]:text-foreground font-mono font-bold uppercase py-2 text-muted-foreground transition-all rounded-sm text-xs">
|
||||
英文提示词
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
|
@ -398,10 +398,10 @@ export default function PromptManager() {
|
|||
</Tabs>
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch checked={form.is_active} onCheckedChange={v => setForm({ ...form, is_active: v })} />
|
||||
<Label className="text-xs font-bold text-gray-400 uppercase">启用此模板</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">启用此模板</Label>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter className="flex-shrink-0 flex justify-end gap-3 px-6 py-4 bg-gray-900/50 border-t border-gray-800">
|
||||
<DialogFooter className="flex-shrink-0 flex justify-end gap-3 px-6 py-4 bg-muted border-t border-border">
|
||||
<Button variant="outline" onClick={() => { setShowCreateDialog(false); setShowEditDialog(false); }} className="cyber-btn-outline">取消</Button>
|
||||
<Button onClick={showEditDialog ? handleUpdate : handleCreate} className="cyber-btn-primary">{showEditDialog ? '保存' : '创建'}</Button>
|
||||
</DialogFooter>
|
||||
|
|
@ -410,9 +410,9 @@ export default function PromptManager() {
|
|||
|
||||
{/* Test Dialog */}
|
||||
<Dialog open={showTestDialog} onOpenChange={setShowTestDialog}>
|
||||
<DialogContent className="!w-[min(95vw,1200px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 bg-[#0c0c12] border border-gray-800 rounded-lg">
|
||||
<DialogHeader className="px-6 py-4 border-b border-gray-800 flex-shrink-0 bg-gray-900/50">
|
||||
<DialogTitle className="flex items-center gap-3 font-mono text-white">
|
||||
<DialogContent className="!w-[min(95vw,1200px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 cyber-dialog border border-border rounded-lg">
|
||||
<DialogHeader className="px-6 py-4 border-b border-border flex-shrink-0 bg-muted">
|
||||
<DialogTitle className="flex items-center gap-3 font-mono text-foreground">
|
||||
<div className="p-2 bg-violet-500/20 rounded border border-violet-500/30">
|
||||
<Sparkles className="w-5 h-5 text-violet-400" />
|
||||
</div>
|
||||
|
|
@ -420,7 +420,7 @@ export default function PromptManager() {
|
|||
<span className="text-base font-bold uppercase tracking-wider">
|
||||
测试提示词: {selectedTemplate?.name}
|
||||
</span>
|
||||
<p className="text-xs text-gray-500 font-normal mt-0.5">使用示例代码测试提示词效果</p>
|
||||
<p className="text-xs text-muted-foreground font-normal mt-0.5">使用示例代码测试提示词效果</p>
|
||||
</div>
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
|
@ -429,14 +429,14 @@ export default function PromptManager() {
|
|||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">编程语言</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">编程语言</Label>
|
||||
<Select value={testForm.language} onValueChange={v => {
|
||||
const templateCodes = selectedTemplate ? TEMPLATE_TEST_CODES[selectedTemplate.name] : null;
|
||||
const code = templateCodes?.[v] || TEST_CODE_SAMPLES[v] || TEST_CODE_SAMPLES.python;
|
||||
setTestForm({ ...testForm, language: v, code });
|
||||
}}>
|
||||
<SelectTrigger className="cyber-input"><SelectValue /></SelectTrigger>
|
||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
||||
<SelectContent className="cyber-dialog border-border">
|
||||
<SelectItem value="python">Python</SelectItem>
|
||||
<SelectItem value="javascript">JavaScript</SelectItem>
|
||||
<SelectItem value="java">Java</SelectItem>
|
||||
|
|
@ -444,10 +444,10 @@ export default function PromptManager() {
|
|||
</Select>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">提示词语言</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">提示词语言</Label>
|
||||
<Select value={testForm.promptLang} onValueChange={(v: 'zh' | 'en') => setTestForm({ ...testForm, promptLang: v })}>
|
||||
<SelectTrigger className="cyber-input"><SelectValue /></SelectTrigger>
|
||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
||||
<SelectContent className="cyber-dialog border-border">
|
||||
<SelectItem value="zh">中文提示词</SelectItem>
|
||||
<SelectItem value="en">英文提示词</SelectItem>
|
||||
</SelectContent>
|
||||
|
|
@ -455,7 +455,7 @@ export default function PromptManager() {
|
|||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">测试代码</Label>
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">测试代码</Label>
|
||||
<Textarea value={testForm.code} onChange={e => setTestForm({ ...testForm, code: e.target.value })} rows={10} className="cyber-input font-mono text-sm text-emerald-400" />
|
||||
</div>
|
||||
<Button onClick={handleTest} disabled={testing} className="w-full cyber-btn-primary h-12">
|
||||
|
|
@ -464,8 +464,8 @@ export default function PromptManager() {
|
|||
</div>
|
||||
{/* Right: Results */}
|
||||
<div className="space-y-4">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">分析结果</Label>
|
||||
<div className="border border-gray-800 h-[400px] overflow-auto bg-[#0a0a0f] rounded">
|
||||
<Label className="text-xs font-bold text-muted-foreground uppercase">分析结果</Label>
|
||||
<div className="border border-border h-[400px] overflow-auto cyber-bg-elevated rounded">
|
||||
{testResult ? (
|
||||
testResult.success ? (
|
||||
<div className="flex flex-col h-full">
|
||||
|
|
@ -482,15 +482,15 @@ export default function PromptManager() {
|
|||
|
||||
{/* Quality Score */}
|
||||
{testResult.result?.quality_score !== undefined && (
|
||||
<div className="p-3 bg-gray-900/50 border-b border-gray-800 flex items-center justify-between">
|
||||
<span className="text-xs font-bold uppercase text-gray-500">质量评分</span>
|
||||
<div className="p-3 bg-muted border-b border-border flex items-center justify-between">
|
||||
<span className="text-xs font-bold uppercase text-muted-foreground">质量评分</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className={`text-2xl font-bold ${testResult.result.quality_score >= 80 ? 'text-emerald-400' :
|
||||
testResult.result.quality_score >= 60 ? 'text-amber-400' : 'text-rose-400'
|
||||
}`}>
|
||||
{testResult.result.quality_score}
|
||||
</div>
|
||||
<span className="text-xs text-gray-500">/ 100</span>
|
||||
<span className="text-xs text-muted-foreground">/ 100</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -500,14 +500,14 @@ export default function PromptManager() {
|
|||
{testResult.result?.issues?.length > 0 ? (
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-xs font-bold uppercase text-gray-500">发现问题</span>
|
||||
<span className="text-xs font-bold uppercase text-muted-foreground">发现问题</span>
|
||||
<Badge className="cyber-badge-danger">
|
||||
{testResult.result.issues.length} 个
|
||||
</Badge>
|
||||
</div>
|
||||
{testResult.result.issues.map((issue: any, idx: number) => (
|
||||
<div key={idx} className="cyber-card p-0 overflow-hidden">
|
||||
<div className={`px-3 py-2 border-b border-gray-800 flex items-center justify-between ${issue.severity === 'critical' ? 'bg-rose-500/20 text-rose-400' :
|
||||
<div className={`px-3 py-2 border-b border-border flex items-center justify-between ${issue.severity === 'critical' ? 'bg-rose-500/20 text-rose-400' :
|
||||
issue.severity === 'high' ? 'bg-orange-500/20 text-orange-400' :
|
||||
issue.severity === 'medium' ? 'bg-amber-500/20 text-amber-400' : 'bg-sky-500/20 text-sky-400'
|
||||
}`}>
|
||||
|
|
@ -515,9 +515,9 @@ export default function PromptManager() {
|
|||
{issue.line && <span className="text-xs opacity-80">行 {issue.line}</span>}
|
||||
</div>
|
||||
<div className="p-3">
|
||||
<h4 className="font-bold text-sm mb-1 text-gray-200">{issue.title}</h4>
|
||||
<h4 className="font-bold text-sm mb-1 text-foreground">{issue.title}</h4>
|
||||
{issue.description && (
|
||||
<p className="text-xs text-gray-500 leading-relaxed">{issue.description}</p>
|
||||
<p className="text-xs text-muted-foreground leading-relaxed">{issue.description}</p>
|
||||
)}
|
||||
{issue.suggestion && (
|
||||
<div className="mt-2 p-2 bg-sky-500/10 border-l-2 border-sky-500 rounded-r">
|
||||
|
|
@ -537,7 +537,7 @@ export default function PromptManager() {
|
|||
<Check className="w-6 h-6 text-emerald-400" />
|
||||
</div>
|
||||
<p className="font-bold text-emerald-400 uppercase text-sm">未发现问题</p>
|
||||
<p className="text-xs text-gray-500 mt-1">代码质量良好</p>
|
||||
<p className="text-xs text-muted-foreground mt-1">代码质量良好</p>
|
||||
</div>
|
||||
)}
|
||||
</ScrollArea>
|
||||
|
|
@ -567,8 +567,8 @@ export default function PromptManager() {
|
|||
</div>
|
||||
)
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center h-full text-gray-500">
|
||||
<div className="w-16 h-16 bg-gray-800 border border-gray-700 flex items-center justify-center mb-4 rounded">
|
||||
<div className="flex flex-col items-center justify-center h-full text-muted-foreground">
|
||||
<div className="w-16 h-16 bg-muted border border-border flex items-center justify-center mb-4 rounded">
|
||||
<Play className="w-8 h-8 opacity-50" />
|
||||
</div>
|
||||
<p className="font-mono uppercase text-sm">点击"运行测试"</p>
|
||||
|
|
@ -578,7 +578,7 @@ export default function PromptManager() {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter className="flex-shrink-0 flex justify-end gap-3 px-6 py-4 bg-gray-900/50 border-t border-gray-800">
|
||||
<DialogFooter className="flex-shrink-0 flex justify-end gap-3 px-6 py-4 bg-muted border-t border-border">
|
||||
<Button variant="outline" onClick={() => setShowTestDialog(false)} className="cyber-btn-outline">关闭</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
|
|
@ -586,9 +586,9 @@ export default function PromptManager() {
|
|||
|
||||
{/* View Dialog */}
|
||||
<Dialog open={showViewDialog} onOpenChange={setShowViewDialog}>
|
||||
<DialogContent className="!w-[min(90vw,800px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 bg-[#0c0c12] border border-gray-800 rounded-lg">
|
||||
<DialogHeader className="px-6 py-4 border-b border-gray-800 flex-shrink-0 bg-gray-900/50">
|
||||
<DialogTitle className="flex items-center gap-3 font-mono text-white">
|
||||
<DialogContent className="!w-[min(90vw,800px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 cyber-dialog border border-border rounded-lg">
|
||||
<DialogHeader className="px-6 py-4 border-b border-border flex-shrink-0 bg-muted">
|
||||
<DialogTitle className="flex items-center gap-3 font-mono text-foreground">
|
||||
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
||||
<FileText className="w-5 h-5 text-primary" />
|
||||
</div>
|
||||
|
|
@ -596,7 +596,7 @@ export default function PromptManager() {
|
|||
<span className="text-base font-bold uppercase tracking-wider">
|
||||
{viewTemplate?.name}
|
||||
</span>
|
||||
<p className="text-xs text-gray-500 font-normal mt-0.5">{viewTemplate?.description || 'View Template'}</p>
|
||||
<p className="text-xs text-muted-foreground font-normal mt-0.5">{viewTemplate?.description || 'View Template'}</p>
|
||||
</div>
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
|
@ -613,27 +613,27 @@ export default function PromptManager() {
|
|||
</div>
|
||||
|
||||
<Tabs defaultValue="zh" className="w-full">
|
||||
<TabsList className="grid w-full grid-cols-2 bg-gray-900/50 border border-gray-800 p-1 h-auto gap-1 rounded">
|
||||
<TabsTrigger value="zh" className="data-[state=active]:bg-primary data-[state=active]:text-white font-mono font-bold uppercase py-2 text-gray-400 transition-all rounded-sm text-xs">
|
||||
<TabsList className="grid w-full grid-cols-2 bg-muted border border-border p-1 h-auto gap-1 rounded">
|
||||
<TabsTrigger value="zh" className="data-[state=active]:bg-primary data-[state=active]:text-foreground font-mono font-bold uppercase py-2 text-muted-foreground transition-all rounded-sm text-xs">
|
||||
中文提示词
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="en" className="data-[state=active]:bg-primary data-[state=active]:text-white font-mono font-bold uppercase py-2 text-gray-400 transition-all rounded-sm text-xs">
|
||||
<TabsTrigger value="en" className="data-[state=active]:bg-primary data-[state=active]:text-foreground font-mono font-bold uppercase py-2 text-muted-foreground transition-all rounded-sm text-xs">
|
||||
英文提示词
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="zh" className="mt-4">
|
||||
<div className="bg-[#0a0a0f] text-emerald-400 p-4 border border-gray-800 font-mono text-sm whitespace-pre-wrap max-h-[500px] overflow-y-auto rounded">
|
||||
<div className="cyber-bg-elevated text-emerald-400 p-4 border border-border font-mono text-sm whitespace-pre-wrap max-h-[500px] overflow-y-auto rounded">
|
||||
{viewTemplate?.content_zh || '(无中文内容)'}
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="en" className="mt-4">
|
||||
<div className="bg-[#0a0a0f] text-emerald-400 p-4 border border-gray-800 font-mono text-sm whitespace-pre-wrap max-h-[500px] overflow-y-auto rounded">
|
||||
<div className="cyber-bg-elevated text-emerald-400 p-4 border border-border font-mono text-sm whitespace-pre-wrap max-h-[500px] overflow-y-auto rounded">
|
||||
{viewTemplate?.content_en || '(No English content)'}
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
<DialogFooter className="flex-shrink-0 flex justify-end gap-3 px-6 py-4 bg-gray-900/50 border-t border-gray-800">
|
||||
<DialogFooter className="flex-shrink-0 flex justify-end gap-3 px-6 py-4 bg-muted border-t border-border">
|
||||
<Button variant="outline" onClick={() => copyToClipboard(viewTemplate?.content_zh || viewTemplate?.content_en || '')} className="cyber-btn-outline">
|
||||
<Copy className="w-4 h-4 mr-2" />
|
||||
复制内容
|
||||
|
|
|
|||
|
|
@ -109,17 +109,17 @@ export default function RecycleBin() {
|
|||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-screen bg-[#0a0a0f]">
|
||||
<div className="flex items-center justify-center min-h-screen cyber-bg-elevated">
|
||||
<div className="text-center space-y-4">
|
||||
<div className="loading-spinner mx-auto" />
|
||||
<p className="text-gray-500 font-mono text-sm uppercase tracking-wider">加载中...</p>
|
||||
<p className="text-muted-foreground font-mono text-sm uppercase tracking-wider">加载中...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6 p-6 bg-[#0a0a0f] min-h-screen font-mono relative">
|
||||
<div className="space-y-6 p-6 cyber-bg-elevated min-h-screen font-mono relative">
|
||||
{/* Grid background */}
|
||||
<div className="absolute inset-0 cyber-grid-subtle pointer-events-none" />
|
||||
|
||||
|
|
@ -127,12 +127,12 @@ export default function RecycleBin() {
|
|||
<div className="cyber-card p-0 relative z-10">
|
||||
<div className="cyber-card-header">
|
||||
<Trash2 className="w-5 h-5 text-rose-400" />
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-white">回收站</h3>
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-foreground">回收站</h3>
|
||||
<Badge className="ml-2 cyber-badge-muted">{deletedProjects.length} 个项目</Badge>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<div className="relative">
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500 w-4 h-4" />
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground w-4 h-4" />
|
||||
<Input
|
||||
placeholder="搜索已删除的项目..."
|
||||
value={searchTerm}
|
||||
|
|
@ -147,20 +147,20 @@ export default function RecycleBin() {
|
|||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 relative z-10">
|
||||
{filteredProjects.length > 0 ? (
|
||||
filteredProjects.map((project) => (
|
||||
<div key={project.id} className="cyber-card p-0 hover:border-gray-700 transition-all group">
|
||||
<div key={project.id} className="cyber-card p-0 hover:border-border transition-all group">
|
||||
{/* Project Header */}
|
||||
<div className="p-4 border-b border-gray-800 bg-gray-900/30">
|
||||
<div className="p-4 border-b border-border bg-muted/50">
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="w-10 h-10 bg-gray-800 border border-gray-700 flex items-center justify-center text-lg rounded">
|
||||
<div className="w-10 h-10 bg-muted border border-border flex items-center justify-center text-lg rounded">
|
||||
{getRepositoryIcon(project.repository_type)}
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-base font-bold uppercase text-gray-200 truncate max-w-[150px] group-hover:text-primary transition-colors">
|
||||
<h3 className="text-base font-bold uppercase text-foreground truncate max-w-[150px] group-hover:text-primary transition-colors">
|
||||
{project.name}
|
||||
</h3>
|
||||
{project.description && (
|
||||
<p className="text-xs text-gray-500 mt-1 line-clamp-1">
|
||||
<p className="text-xs text-muted-foreground mt-1 line-clamp-1">
|
||||
{project.description}
|
||||
</p>
|
||||
)}
|
||||
|
|
@ -179,8 +179,8 @@ export default function RecycleBin() {
|
|||
{/* Project Info */}
|
||||
<div className="space-y-3">
|
||||
{isRepositoryProject(project) && project.repository_url && (
|
||||
<div className="flex items-center text-xs text-gray-500">
|
||||
<GitBranch className="w-4 h-4 mr-2 flex-shrink-0 text-gray-600" />
|
||||
<div className="flex items-center text-xs text-muted-foreground">
|
||||
<GitBranch className="w-4 h-4 mr-2 flex-shrink-0 text-muted-foreground" />
|
||||
<a
|
||||
href={project.repository_url}
|
||||
target="_blank"
|
||||
|
|
@ -193,13 +193,13 @@ export default function RecycleBin() {
|
|||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center justify-between text-xs text-gray-500">
|
||||
<div className="flex items-center justify-between text-xs text-muted-foreground">
|
||||
<div className="flex items-center">
|
||||
<Calendar className="w-4 h-4 mr-2 text-gray-600" />
|
||||
<Calendar className="w-4 h-4 mr-2 text-muted-foreground" />
|
||||
删除于 {formatDate(project.updated_at)}
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<Users className="w-4 h-4 mr-2 text-gray-600" />
|
||||
<Users className="w-4 h-4 mr-2 text-muted-foreground" />
|
||||
{project.owner?.full_name || '未知'}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -222,7 +222,7 @@ export default function RecycleBin() {
|
|||
)}
|
||||
|
||||
{/* Action Buttons */}
|
||||
<div className="flex gap-2 pt-3 border-t border-gray-800">
|
||||
<div className="flex gap-2 pt-3 border-t border-border">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
|
|
@ -262,19 +262,19 @@ export default function RecycleBin() {
|
|||
|
||||
{/* Restore Dialog */}
|
||||
<AlertDialog open={showRestoreDialog} onOpenChange={setShowRestoreDialog}>
|
||||
<AlertDialogContent className="cyber-card p-0 bg-[#0c0c12] max-w-md !fixed">
|
||||
<AlertDialogContent className="cyber-card p-0 cyber-dialog max-w-md !fixed">
|
||||
<AlertDialogHeader className="cyber-card-header">
|
||||
<RotateCcw className="w-5 h-5 text-emerald-400" />
|
||||
<AlertDialogTitle className="text-lg font-bold uppercase tracking-wider text-white">
|
||||
<AlertDialogTitle className="text-lg font-bold uppercase tracking-wider text-foreground">
|
||||
确认恢复项目
|
||||
</AlertDialogTitle>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogDescription className="p-6 text-gray-400">
|
||||
您确定要恢复项目 <span className="font-bold text-white">"{selectedProject?.name}"</span> 吗?
|
||||
<AlertDialogDescription className="p-6 text-muted-foreground">
|
||||
您确定要恢复项目 <span className="font-bold text-foreground">"{selectedProject?.name}"</span> 吗?
|
||||
<br /><br />
|
||||
恢复后,该项目将重新出现在项目列表中,您可以继续使用该项目的所有功能。
|
||||
</AlertDialogDescription>
|
||||
<AlertDialogFooter className="p-4 border-t border-gray-800 flex gap-3">
|
||||
<AlertDialogFooter className="p-4 border-t border-border flex gap-3">
|
||||
<AlertDialogCancel className="cyber-btn-outline">取消</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={handleConfirmRestore}
|
||||
|
|
@ -288,15 +288,15 @@ export default function RecycleBin() {
|
|||
|
||||
{/* Permanent Delete Dialog */}
|
||||
<AlertDialog open={showPermanentDeleteDialog} onOpenChange={setShowPermanentDeleteDialog}>
|
||||
<AlertDialogContent className="cyber-card p-0 bg-[#0c0c12] max-w-md !fixed">
|
||||
<AlertDialogContent className="cyber-card p-0 cyber-dialog max-w-md !fixed">
|
||||
<AlertDialogHeader className="p-4 border-b border-rose-500/30 bg-rose-500/10 flex flex-row items-center gap-2">
|
||||
<AlertTriangle className="w-5 h-5 text-rose-400" />
|
||||
<AlertDialogTitle className="text-lg font-bold uppercase tracking-wider text-rose-400">
|
||||
警告:永久删除项目
|
||||
</AlertDialogTitle>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogDescription className="p-6 text-gray-400">
|
||||
您确定要<span className="font-bold text-rose-400 uppercase">永久删除</span>项目 <span className="font-bold text-white">"{selectedProject?.name}"</span> 吗?
|
||||
<AlertDialogDescription className="p-6 text-muted-foreground">
|
||||
您确定要<span className="font-bold text-rose-400 uppercase">永久删除</span>项目 <span className="font-bold text-foreground">"{selectedProject?.name}"</span> 吗?
|
||||
<br /><br />
|
||||
<div className="bg-rose-500/10 border border-rose-500/30 p-4 rounded">
|
||||
<p className="text-rose-400 font-bold mb-2 uppercase flex items-center">
|
||||
|
|
@ -310,7 +310,7 @@ export default function RecycleBin() {
|
|||
</ul>
|
||||
</div>
|
||||
</AlertDialogDescription>
|
||||
<AlertDialogFooter className="p-4 border-t border-gray-800 flex gap-3">
|
||||
<AlertDialogFooter className="p-4 border-t border-border flex gap-3">
|
||||
<AlertDialogCancel className="cyber-btn-outline">取消</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={handleConfirmPermanentDelete}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ export default function Register() {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-[#0a0a0f] relative overflow-hidden">
|
||||
<div className="min-h-screen flex items-center justify-center cyber-bg-elevated relative overflow-hidden">
|
||||
{/* Scanline overlay */}
|
||||
<div className="absolute inset-0 pointer-events-none z-20">
|
||||
<div
|
||||
|
|
@ -79,7 +79,7 @@ export default function Register() {
|
|||
/>
|
||||
|
||||
{/* Corner Decorations */}
|
||||
<div className="absolute top-4 left-4 text-[10px] font-mono text-gray-700 z-30 space-y-1">
|
||||
<div className="absolute top-4 left-4 text-xs font-mono text-muted-foreground z-30 space-y-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<Terminal className="w-3 h-3" />
|
||||
<span>REG_MODULE: ACTIVE</span>
|
||||
|
|
@ -94,17 +94,17 @@ export default function Register() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="absolute top-4 right-4 text-[10px] font-mono text-gray-700 text-right z-30 space-y-1">
|
||||
<div className="absolute top-4 right-4 text-xs font-mono text-muted-foreground text-right z-30 space-y-1">
|
||||
<div>SECURE_CONN: TRUE</div>
|
||||
<div>VALIDATION: ENABLED</div>
|
||||
<div>HASH: SHA-256</div>
|
||||
</div>
|
||||
|
||||
<div className="absolute bottom-4 left-4 text-[10px] font-mono text-gray-700 z-30">
|
||||
<div className="absolute bottom-4 left-4 text-xs font-mono text-muted-foreground z-30">
|
||||
DEEPAUDIT_REG_v3
|
||||
</div>
|
||||
|
||||
<div className="absolute bottom-4 right-4 text-[10px] font-mono text-gray-700 z-30">
|
||||
<div className="absolute bottom-4 right-4 text-xs font-mono text-muted-foreground z-30">
|
||||
{new Date().toISOString().split("T")[0]}
|
||||
</div>
|
||||
|
||||
|
|
@ -112,7 +112,7 @@ export default function Register() {
|
|||
<div className="w-full max-w-md relative z-30 px-4">
|
||||
{/* Logo & Title */}
|
||||
<div className="text-center mb-8">
|
||||
<div className="inline-flex items-center justify-center p-3 bg-[#0c0c12] border border-gray-800/60 rounded-lg mb-6"
|
||||
<div className="inline-flex items-center justify-center p-3 cyber-dialog border border-border/60 rounded-lg mb-6"
|
||||
style={{ boxShadow: '0 0 30px rgba(255,107,44,0.1)' }}>
|
||||
<img
|
||||
src="/logo_deepaudit.png"
|
||||
|
|
@ -125,24 +125,24 @@ export default function Register() {
|
|||
style={{ textShadow: "0 0 30px rgba(255,107,44,0.5), 0 0 60px rgba(255,107,44,0.3)" }}
|
||||
>
|
||||
<span className="text-primary">DEEP</span>
|
||||
<span className="text-white">AUDIT</span>
|
||||
<span className="text-foreground">AUDIT</span>
|
||||
</div>
|
||||
<p className="text-sm font-mono text-gray-500">
|
||||
<p className="text-sm font-mono text-muted-foreground">
|
||||
// Create New Account
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Register Form Card */}
|
||||
<div className="bg-[#0c0c12] border border-gray-800/60 rounded-lg overflow-hidden"
|
||||
<div className="cyber-dialog border border-border/60 rounded-lg overflow-hidden"
|
||||
style={{ boxShadow: '0 4px 30px rgba(0,0,0,0.5)' }}>
|
||||
{/* Card Header */}
|
||||
<div className="flex items-center gap-2 px-4 py-3 bg-[#0a0a0f] border-b border-gray-800/50">
|
||||
<div className="flex items-center gap-2 px-4 py-3 cyber-bg-elevated border-b border-border">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<div className="w-3 h-3 rounded-full bg-red-500/80" />
|
||||
<div className="w-3 h-3 rounded-full bg-yellow-500/80" />
|
||||
<div className="w-3 h-3 rounded-full bg-green-500/80" />
|
||||
</div>
|
||||
<span className="ml-2 font-mono text-[11px] text-gray-500 tracking-wider">
|
||||
<span className="ml-2 font-mono text-xs text-muted-foreground tracking-wider">
|
||||
register@deepaudit
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -152,7 +152,7 @@ export default function Register() {
|
|||
<div className="space-y-2">
|
||||
<Label
|
||||
htmlFor="fullName"
|
||||
className="font-mono text-xs text-gray-400 uppercase tracking-wider"
|
||||
className="font-mono text-xs text-muted-foreground uppercase tracking-wider"
|
||||
>
|
||||
姓名
|
||||
</Label>
|
||||
|
|
@ -163,16 +163,16 @@ export default function Register() {
|
|||
value={fullName}
|
||||
onChange={(e) => setFullName(e.target.value)}
|
||||
required
|
||||
className="h-12 pl-11 font-mono bg-[#0a0a0f] border-gray-700/50 text-gray-200 placeholder:text-gray-600 focus:border-primary/50 focus:ring-0"
|
||||
className="h-12 pl-11 font-mono cyber-bg-elevated border-border/50 text-foreground placeholder:text-muted-foreground focus:border-primary/50 focus:ring-0"
|
||||
/>
|
||||
<User className="absolute left-4 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-600" />
|
||||
<User className="absolute left-4 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label
|
||||
htmlFor="email"
|
||||
className="font-mono text-xs text-gray-400 uppercase tracking-wider"
|
||||
className="font-mono text-xs text-muted-foreground uppercase tracking-wider"
|
||||
>
|
||||
邮箱地址
|
||||
</Label>
|
||||
|
|
@ -184,16 +184,16 @@ export default function Register() {
|
|||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
className="h-12 pl-11 font-mono bg-[#0a0a0f] border-gray-700/50 text-gray-200 placeholder:text-gray-600 focus:border-primary/50 focus:ring-0"
|
||||
className="h-12 pl-11 font-mono cyber-bg-elevated border-border/50 text-foreground placeholder:text-muted-foreground focus:border-primary/50 focus:ring-0"
|
||||
/>
|
||||
<Mail className="absolute left-4 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-600" />
|
||||
<Mail className="absolute left-4 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label
|
||||
htmlFor="password"
|
||||
className="font-mono text-xs text-gray-400 uppercase tracking-wider"
|
||||
className="font-mono text-xs text-muted-foreground uppercase tracking-wider"
|
||||
>
|
||||
密码
|
||||
</Label>
|
||||
|
|
@ -205,15 +205,15 @@ export default function Register() {
|
|||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
className="h-12 pl-11 font-mono bg-[#0a0a0f] border-gray-700/50 text-gray-200 placeholder:text-gray-600 focus:border-primary/50 focus:ring-0"
|
||||
className="h-12 pl-11 font-mono cyber-bg-elevated border-border/50 text-foreground placeholder:text-muted-foreground focus:border-primary/50 focus:ring-0"
|
||||
/>
|
||||
<Lock className="absolute left-4 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-600" />
|
||||
<Lock className="absolute left-4 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
className="w-full h-12 text-base font-bold uppercase tracking-wider bg-primary hover:bg-primary/90 text-white border border-primary/50 transition-all"
|
||||
className="w-full h-12 text-base font-bold uppercase tracking-wider bg-primary hover:bg-primary/90 text-foreground border border-primary/50 transition-all"
|
||||
style={{ boxShadow: '0 0 20px rgba(255,107,44,0.3)' }}
|
||||
disabled={loading}
|
||||
>
|
||||
|
|
@ -229,8 +229,8 @@ export default function Register() {
|
|||
</form>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="mt-6 pt-5 border-t border-gray-800/50 text-center">
|
||||
<p className="text-sm font-mono text-gray-500">
|
||||
<div className="mt-6 pt-5 border-t border-border text-center">
|
||||
<p className="text-sm font-mono text-muted-foreground">
|
||||
已有账号?{" "}
|
||||
<span
|
||||
className="text-primary font-bold cursor-pointer hover:underline"
|
||||
|
|
@ -245,7 +245,7 @@ export default function Register() {
|
|||
|
||||
{/* Version Info */}
|
||||
<div className="mt-6 text-center">
|
||||
<p className="font-mono text-[10px] text-gray-600 uppercase">
|
||||
<p className="font-mono text-xs text-muted-foreground uppercase">
|
||||
Version {version} · Secure Registration
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ function IssuesList({ issues }: { issues: AuditIssue[] }) {
|
|||
const lowIssues = issues.filter(issue => issue.severity === 'low');
|
||||
|
||||
const renderIssue = (issue: AuditIssue, index: number) => (
|
||||
<div key={issue.id || index} className="cyber-card p-4 hover:border-gray-700 transition-all group">
|
||||
<div key={issue.id || index} className="cyber-card p-4 hover:border-border transition-all group">
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<div className="flex items-start space-x-3">
|
||||
<div className={`w-10 h-10 rounded-lg flex items-center justify-center ${
|
||||
|
|
@ -93,13 +93,13 @@ function IssuesList({ issues }: { issues: AuditIssue[] }) {
|
|||
{getTypeIcon(issue.issue_type)}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h4 className="font-bold text-base text-gray-200 mb-1 group-hover:text-primary transition-colors uppercase">{issue.title}</h4>
|
||||
<div className="flex items-center space-x-1 text-xs text-gray-500 font-mono">
|
||||
<h4 className="font-bold text-base text-foreground mb-1 group-hover:text-primary transition-colors uppercase">{issue.title}</h4>
|
||||
<div className="flex items-center space-x-1 text-xs text-muted-foreground font-mono">
|
||||
<FileText className="w-3 h-3" />
|
||||
<span className="bg-gray-800 px-2 py-0.5 rounded border border-gray-700">{issue.file_path}</span>
|
||||
<span className="bg-muted px-2 py-0.5 rounded border border-border">{issue.file_path}</span>
|
||||
</div>
|
||||
{issue.line_number && (
|
||||
<div className="flex items-center space-x-1 text-xs text-gray-500 mt-1 font-mono">
|
||||
<div className="flex items-center space-x-1 text-xs text-muted-foreground mt-1 font-mono">
|
||||
<span className="text-primary">></span>
|
||||
<span>LINE: {issue.line_number}</span>
|
||||
{issue.column_number && <span>, COL: {issue.column_number}</span>}
|
||||
|
|
@ -107,7 +107,7 @@ function IssuesList({ issues }: { issues: AuditIssue[] }) {
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
<Badge className={`${getSeverityClasses(issue.severity)} font-bold uppercase px-2 py-1 rounded text-[10px]`}>
|
||||
<Badge className={`${getSeverityClasses(issue.severity)} font-bold uppercase px-2 py-1 rounded text-xs`}>
|
||||
{issue.severity === 'critical' ? '严重' :
|
||||
issue.severity === 'high' ? '高' :
|
||||
issue.severity === 'medium' ? '中等' : '低'}
|
||||
|
|
@ -115,31 +115,31 @@ function IssuesList({ issues }: { issues: AuditIssue[] }) {
|
|||
</div>
|
||||
|
||||
{issue.description && (
|
||||
<div className="bg-gray-900/50 border border-gray-800 p-3 mb-3 rounded font-mono">
|
||||
<div className="flex items-center mb-1 border-b border-gray-800 pb-1">
|
||||
<Info className="w-3 h-3 text-gray-500 mr-1" />
|
||||
<span className="font-bold text-gray-400 text-xs uppercase">问题详情</span>
|
||||
<div className="bg-muted border border-border p-3 mb-3 rounded font-mono">
|
||||
<div className="flex items-center mb-1 border-b border-border pb-1">
|
||||
<Info className="w-3 h-3 text-muted-foreground mr-1" />
|
||||
<span className="font-bold text-muted-foreground text-xs uppercase">问题详情</span>
|
||||
</div>
|
||||
<p className="text-gray-300 text-xs leading-relaxed mt-1">
|
||||
<p className="text-foreground text-xs leading-relaxed mt-1">
|
||||
{issue.description}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{issue.code_snippet && (
|
||||
<div className="bg-[#0a0a0f] p-3 mb-3 border border-gray-800 rounded">
|
||||
<div className="flex items-center justify-between mb-2 border-b border-gray-800 pb-1">
|
||||
<div className="cyber-bg-elevated p-3 mb-3 border border-border rounded">
|
||||
<div className="flex items-center justify-between mb-2 border-b border-border pb-1">
|
||||
<div className="flex items-center space-x-1">
|
||||
<div className="w-4 h-4 bg-primary rounded flex items-center justify-center">
|
||||
<Code className="w-2 h-2 text-white" />
|
||||
<Code className="w-2 h-2 text-foreground" />
|
||||
</div>
|
||||
<span className="text-emerald-400 text-xs font-bold font-mono uppercase">CODE_SNIPPET</span>
|
||||
</div>
|
||||
{issue.line_number && (
|
||||
<span className="text-gray-500 text-xs font-mono">LINE: {issue.line_number}</span>
|
||||
<span className="text-muted-foreground text-xs font-mono">LINE: {issue.line_number}</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="bg-black/40 p-2 border border-gray-800 rounded">
|
||||
<div className="bg-black/40 p-2 border border-border rounded">
|
||||
<pre className="text-xs text-emerald-400 font-mono overflow-x-auto">
|
||||
<code>{issue.code_snippet}</code>
|
||||
</pre>
|
||||
|
|
@ -177,21 +177,21 @@ function IssuesList({ issues }: { issues: AuditIssue[] }) {
|
|||
{parsedExplanation.what && (
|
||||
<div className="border-l-2 border-rose-500 pl-2">
|
||||
<span className="font-bold text-rose-400 uppercase">问题:</span>
|
||||
<span className="text-gray-300 ml-1">{parsedExplanation.what}</span>
|
||||
<span className="text-foreground ml-1">{parsedExplanation.what}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{parsedExplanation.why && (
|
||||
<div className="border-l-2 border-amber-500 pl-2">
|
||||
<span className="font-bold text-amber-400 uppercase">原因:</span>
|
||||
<span className="text-gray-300 ml-1">{parsedExplanation.why}</span>
|
||||
<span className="text-foreground ml-1">{parsedExplanation.why}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{parsedExplanation.how && (
|
||||
<div className="border-l-2 border-emerald-500 pl-2">
|
||||
<span className="font-bold text-emerald-400 uppercase">方案:</span>
|
||||
<span className="text-gray-300 ml-1">{parsedExplanation.how}</span>
|
||||
<span className="text-foreground ml-1">{parsedExplanation.how}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
@ -218,7 +218,7 @@ function IssuesList({ issues }: { issues: AuditIssue[] }) {
|
|||
<Zap className="w-4 h-4 text-violet-400 mr-2" />
|
||||
<span className="font-bold text-violet-300 text-sm uppercase">AI 解释</span>
|
||||
</div>
|
||||
<p className="text-gray-300 text-xs leading-relaxed font-mono">{issue.ai_explanation}</p>
|
||||
<p className="text-foreground text-xs leading-relaxed font-mono">{issue.ai_explanation}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -244,20 +244,20 @@ function IssuesList({ issues }: { issues: AuditIssue[] }) {
|
|||
|
||||
return (
|
||||
<Tabs defaultValue="all" className="w-full">
|
||||
<TabsList className="grid w-full grid-cols-5 bg-gray-900/50 border border-gray-800 p-1 h-auto gap-1 rounded">
|
||||
<TabsTrigger value="all" className="data-[state=active]:bg-primary data-[state=active]:text-white font-mono font-bold uppercase py-2 text-gray-400 transition-all rounded-sm text-xs">
|
||||
<TabsList className="grid w-full grid-cols-5 bg-muted border border-border p-1 h-auto gap-1 rounded">
|
||||
<TabsTrigger value="all" className="data-[state=active]:bg-primary data-[state=active]:text-foreground font-mono font-bold uppercase py-2 text-muted-foreground transition-all rounded-sm text-xs">
|
||||
全部 ({issues.length})
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="critical" className="data-[state=active]:bg-rose-500 data-[state=active]:text-white font-mono font-bold uppercase py-2 text-gray-400 transition-all rounded-sm text-xs">
|
||||
<TabsTrigger value="critical" className="data-[state=active]:bg-rose-500 data-[state=active]:text-foreground font-mono font-bold uppercase py-2 text-muted-foreground transition-all rounded-sm text-xs">
|
||||
严重 ({criticalIssues.length})
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="high" className="data-[state=active]:bg-orange-500 data-[state=active]:text-white font-mono font-bold uppercase py-2 text-gray-400 transition-all rounded-sm text-xs">
|
||||
<TabsTrigger value="high" className="data-[state=active]:bg-orange-500 data-[state=active]:text-foreground font-mono font-bold uppercase py-2 text-muted-foreground transition-all rounded-sm text-xs">
|
||||
高 ({highIssues.length})
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="medium" className="data-[state=active]:bg-amber-500 data-[state=active]:text-black font-mono font-bold uppercase py-2 text-gray-400 transition-all rounded-sm text-xs">
|
||||
<TabsTrigger value="medium" className="data-[state=active]:bg-amber-500 data-[state=active]:text-background font-mono font-bold uppercase py-2 text-muted-foreground transition-all rounded-sm text-xs">
|
||||
中等 ({mediumIssues.length})
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="low" className="data-[state=active]:bg-sky-500 data-[state=active]:text-white font-mono font-bold uppercase py-2 text-gray-400 transition-all rounded-sm text-xs">
|
||||
<TabsTrigger value="low" className="data-[state=active]:bg-sky-500 data-[state=active]:text-foreground font-mono font-bold uppercase py-2 text-muted-foreground transition-all rounded-sm text-xs">
|
||||
低 ({lowIssues.length})
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
|
@ -272,8 +272,8 @@ function IssuesList({ issues }: { issues: AuditIssue[] }) {
|
|||
) : (
|
||||
<div className="cyber-card p-12 text-center border-dashed">
|
||||
<CheckCircle className="w-16 h-16 text-emerald-400 mx-auto mb-4" />
|
||||
<h3 className="text-lg font-bold text-gray-300 uppercase mb-2">没有发现严重问题</h3>
|
||||
<p className="text-gray-500 font-mono">代码在严重级别的检查中表现良好</p>
|
||||
<h3 className="text-lg font-bold text-foreground uppercase mb-2">没有发现严重问题</h3>
|
||||
<p className="text-muted-foreground font-mono">代码在严重级别的检查中表现良好</p>
|
||||
</div>
|
||||
)}
|
||||
</TabsContent>
|
||||
|
|
@ -284,8 +284,8 @@ function IssuesList({ issues }: { issues: AuditIssue[] }) {
|
|||
) : (
|
||||
<div className="cyber-card p-12 text-center border-dashed">
|
||||
<CheckCircle className="w-16 h-16 text-emerald-400 mx-auto mb-4" />
|
||||
<h3 className="text-lg font-bold text-gray-300 uppercase mb-2">没有发现高优先级问题</h3>
|
||||
<p className="text-gray-500 font-mono">代码在高优先级检查中表现良好</p>
|
||||
<h3 className="text-lg font-bold text-foreground uppercase mb-2">没有发现高优先级问题</h3>
|
||||
<p className="text-muted-foreground font-mono">代码在高优先级检查中表现良好</p>
|
||||
</div>
|
||||
)}
|
||||
</TabsContent>
|
||||
|
|
@ -296,8 +296,8 @@ function IssuesList({ issues }: { issues: AuditIssue[] }) {
|
|||
) : (
|
||||
<div className="cyber-card p-12 text-center border-dashed">
|
||||
<CheckCircle className="w-16 h-16 text-emerald-400 mx-auto mb-4" />
|
||||
<h3 className="text-lg font-bold text-gray-300 uppercase mb-2">没有发现中等优先级问题</h3>
|
||||
<p className="text-gray-500 font-mono">代码在中等优先级检查中表现良好</p>
|
||||
<h3 className="text-lg font-bold text-foreground uppercase mb-2">没有发现中等优先级问题</h3>
|
||||
<p className="text-muted-foreground font-mono">代码在中等优先级检查中表现良好</p>
|
||||
</div>
|
||||
)}
|
||||
</TabsContent>
|
||||
|
|
@ -308,8 +308,8 @@ function IssuesList({ issues }: { issues: AuditIssue[] }) {
|
|||
) : (
|
||||
<div className="cyber-card p-12 text-center border-dashed">
|
||||
<CheckCircle className="w-16 h-16 text-emerald-400 mx-auto mb-4" />
|
||||
<h3 className="text-lg font-bold text-gray-300 uppercase mb-2">没有发现低优先级问题</h3>
|
||||
<p className="text-gray-500 font-mono">代码在低优先级检查中表现良好</p>
|
||||
<h3 className="text-lg font-bold text-foreground uppercase mb-2">没有发现低优先级问题</h3>
|
||||
<p className="text-muted-foreground font-mono">代码在低优先级检查中表现良好</p>
|
||||
</div>
|
||||
)}
|
||||
</TabsContent>
|
||||
|
|
@ -450,8 +450,8 @@ export default function TaskDetail() {
|
|||
case 'completed': return <CheckCircle className="w-4 h-4 text-emerald-400" />;
|
||||
case 'running': return <Activity className="w-4 h-4 text-sky-400" />;
|
||||
case 'failed': return <AlertTriangle className="w-4 h-4 text-rose-400" />;
|
||||
case 'cancelled': return <XCircle className="w-4 h-4 text-gray-400" />;
|
||||
default: return <Clock className="w-4 h-4 text-gray-400" />;
|
||||
case 'cancelled': return <XCircle className="w-4 h-4 text-muted-foreground" />;
|
||||
default: return <Clock className="w-4 h-4 text-muted-foreground" />;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -470,7 +470,7 @@ export default function TaskDetail() {
|
|||
<div className="flex items-center justify-center min-h-[60vh]">
|
||||
<div className="text-center space-y-4">
|
||||
<div className="loading-spinner mx-auto" />
|
||||
<p className="text-gray-500 font-mono text-sm uppercase tracking-wider">加载任务详情...</p>
|
||||
<p className="text-muted-foreground font-mono text-sm uppercase tracking-wider">加载任务详情...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -478,7 +478,7 @@ export default function TaskDetail() {
|
|||
|
||||
if (!task) {
|
||||
return (
|
||||
<div className="space-y-6 p-6 bg-[#0a0a0f] min-h-screen font-mono">
|
||||
<div className="space-y-6 p-6 cyber-bg-elevated min-h-screen font-mono">
|
||||
<div className="flex items-center space-x-4">
|
||||
<Link to="/audit-tasks">
|
||||
<Button variant="outline" size="sm" className="cyber-btn-ghost h-10 w-10 p-0">
|
||||
|
|
@ -488,8 +488,8 @@ export default function TaskDetail() {
|
|||
</div>
|
||||
<div className="cyber-card p-16 text-center">
|
||||
<AlertTriangle className="w-16 h-16 text-rose-400 mx-auto mb-4" />
|
||||
<h3 className="text-xl font-bold text-white uppercase mb-2">任务不存在</h3>
|
||||
<p className="text-gray-500 font-mono">请检查任务ID是否正确</p>
|
||||
<h3 className="text-xl font-bold text-foreground uppercase mb-2">任务不存在</h3>
|
||||
<p className="text-muted-foreground font-mono">请检查任务ID是否正确</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -498,7 +498,7 @@ export default function TaskDetail() {
|
|||
const progressPercentage = calculateTaskProgress(task.scanned_files, task.total_files);
|
||||
|
||||
return (
|
||||
<div className="space-y-6 p-6 bg-[#0a0a0f] min-h-screen font-mono relative">
|
||||
<div className="space-y-6 p-6 cyber-bg-elevated min-h-screen font-mono relative">
|
||||
{/* Grid background */}
|
||||
<div className="absolute inset-0 cyber-grid-subtle pointer-events-none" />
|
||||
|
||||
|
|
@ -516,7 +516,7 @@ export default function TaskDetail() {
|
|||
{(task.status === 'running' || task.status === 'pending') && (
|
||||
<Button
|
||||
size="sm"
|
||||
className="cyber-btn bg-rose-500/90 border-rose-500/50 text-white hover:bg-rose-500 h-10"
|
||||
className="cyber-btn bg-rose-500/90 border-rose-500/50 text-foreground hover:bg-rose-500 h-10"
|
||||
onClick={handleCancelTask}
|
||||
disabled={cancelling}
|
||||
>
|
||||
|
|
@ -545,7 +545,7 @@ export default function TaskDetail() {
|
|||
<div className="w-full">
|
||||
<p className="stat-label">扫描进度</p>
|
||||
<p className="stat-value mb-2">{progressPercentage}%</p>
|
||||
<Progress value={progressPercentage} className="h-2 bg-gray-800 [&>div]:bg-primary" />
|
||||
<Progress value={progressPercentage} className="h-2 bg-muted [&>div]:bg-primary" />
|
||||
</div>
|
||||
<div className="stat-icon text-primary ml-4">
|
||||
<Activity className="w-6 h-6" />
|
||||
|
|
@ -596,34 +596,34 @@ export default function TaskDetail() {
|
|||
<div className="cyber-card p-0">
|
||||
<div className="cyber-card-header">
|
||||
<Shield className="w-5 h-5 text-primary" />
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-white">任务信息</h3>
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-foreground">任务信息</h3>
|
||||
</div>
|
||||
<div className="p-6 space-y-4 font-mono">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<p className="text-xs font-bold text-gray-500 uppercase mb-1">任务类型</p>
|
||||
<p className="text-base font-bold text-gray-200">
|
||||
<p className="text-xs font-bold text-muted-foreground uppercase mb-1">任务类型</p>
|
||||
<p className="text-base font-bold text-foreground">
|
||||
{task.task_type === 'repository' ? '仓库审计任务' : '即时分析任务'}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs font-bold text-gray-500 uppercase mb-1">目标分支</p>
|
||||
<p className="text-base font-bold text-gray-200 flex items-center">
|
||||
<p className="text-xs font-bold text-muted-foreground uppercase mb-1">目标分支</p>
|
||||
<p className="text-base font-bold text-foreground flex items-center">
|
||||
<GitBranch className="w-4 h-4 mr-1" />
|
||||
{task.branch_name || '默认分支'}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs font-bold text-gray-500 uppercase mb-1">创建时间</p>
|
||||
<p className="text-base font-bold text-gray-200 flex items-center">
|
||||
<p className="text-xs font-bold text-muted-foreground uppercase mb-1">创建时间</p>
|
||||
<p className="text-base font-bold text-foreground flex items-center">
|
||||
<Calendar className="w-4 h-4 mr-1" />
|
||||
{formatDate(task.created_at)}
|
||||
</p>
|
||||
</div>
|
||||
{task.completed_at && (
|
||||
<div>
|
||||
<p className="text-xs font-bold text-gray-500 uppercase mb-1">完成时间</p>
|
||||
<p className="text-base font-bold text-gray-200 flex items-center">
|
||||
<p className="text-xs font-bold text-muted-foreground uppercase mb-1">完成时间</p>
|
||||
<p className="text-base font-bold text-foreground flex items-center">
|
||||
<CheckCircle className="w-4 h-4 mr-1" />
|
||||
{formatDate(task.completed_at)}
|
||||
</p>
|
||||
|
|
@ -633,7 +633,7 @@ export default function TaskDetail() {
|
|||
|
||||
{task.exclude_patterns && (
|
||||
<div>
|
||||
<p className="text-xs font-bold text-gray-500 uppercase mb-2">排除模式</p>
|
||||
<p className="text-xs font-bold text-muted-foreground uppercase mb-2">排除模式</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{JSON.parse(task.exclude_patterns).map((pattern: string) => (
|
||||
<Badge key={pattern} className="cyber-badge-muted">
|
||||
|
|
@ -646,8 +646,8 @@ export default function TaskDetail() {
|
|||
|
||||
{task.scan_config && (
|
||||
<div>
|
||||
<p className="text-xs font-bold text-gray-500 uppercase mb-2">扫描配置</p>
|
||||
<div className="bg-[#0a0a0f] border border-gray-800 p-3 rounded">
|
||||
<p className="text-xs font-bold text-muted-foreground uppercase mb-2">扫描配置</p>
|
||||
<div className="cyber-bg-elevated border border-border p-3 rounded">
|
||||
<pre className="text-xs text-emerald-400 font-mono overflow-x-auto">
|
||||
{JSON.stringify(JSON.parse(task.scan_config), null, 2)}
|
||||
</pre>
|
||||
|
|
@ -662,36 +662,36 @@ export default function TaskDetail() {
|
|||
<div className="cyber-card p-0">
|
||||
<div className="cyber-card-header">
|
||||
<FileText className="w-5 h-5 text-primary" />
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-white">项目信息</h3>
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-foreground">项目信息</h3>
|
||||
</div>
|
||||
<div className="p-6 space-y-4 font-mono">
|
||||
{task.project ? (
|
||||
<>
|
||||
<div>
|
||||
<p className="text-xs font-bold text-gray-500 uppercase mb-1">项目名称</p>
|
||||
<p className="text-xs font-bold text-muted-foreground uppercase mb-1">项目名称</p>
|
||||
<Link to={`/projects/${task.project.id}`} className="text-base font-bold text-primary hover:underline">
|
||||
{task.project.name}
|
||||
</Link>
|
||||
</div>
|
||||
{task.project.description && (
|
||||
<div>
|
||||
<p className="text-xs font-bold text-gray-500 uppercase mb-1">项目描述</p>
|
||||
<p className="text-sm text-gray-300">{task.project.description}</p>
|
||||
<p className="text-xs font-bold text-muted-foreground uppercase mb-1">项目描述</p>
|
||||
<p className="text-sm text-foreground">{task.project.description}</p>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<p className="text-xs font-bold text-gray-500 uppercase mb-1">项目类型</p>
|
||||
<p className="text-base font-bold text-gray-200">{getSourceTypeLabel(task.project.source_type)}</p>
|
||||
<p className="text-xs font-bold text-muted-foreground uppercase mb-1">项目类型</p>
|
||||
<p className="text-base font-bold text-foreground">{getSourceTypeLabel(task.project.source_type)}</p>
|
||||
</div>
|
||||
{isRepositoryProject(task.project) && (
|
||||
<div>
|
||||
<p className="text-xs font-bold text-gray-500 uppercase mb-1">仓库平台</p>
|
||||
<p className="text-base font-bold text-gray-200">{task.project.repository_type?.toUpperCase() || 'OTHER'}</p>
|
||||
<p className="text-xs font-bold text-muted-foreground uppercase mb-1">仓库平台</p>
|
||||
<p className="text-base font-bold text-foreground">{task.project.repository_type?.toUpperCase() || 'OTHER'}</p>
|
||||
</div>
|
||||
)}
|
||||
{task.project.programming_languages && (
|
||||
<div>
|
||||
<p className="text-xs font-bold text-gray-500 uppercase mb-2">编程语言</p>
|
||||
<p className="text-xs font-bold text-muted-foreground uppercase mb-2">编程语言</p>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{JSON.parse(task.project.programming_languages).map((lang: string) => (
|
||||
<Badge key={lang} className="cyber-badge-primary">
|
||||
|
|
@ -703,7 +703,7 @@ export default function TaskDetail() {
|
|||
)}
|
||||
</>
|
||||
) : (
|
||||
<p className="text-gray-500 font-bold">项目信息不可用</p>
|
||||
<p className="text-muted-foreground font-bold">项目信息不可用</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -715,7 +715,7 @@ export default function TaskDetail() {
|
|||
<div className="cyber-card p-0 relative z-10">
|
||||
<div className="cyber-card-header">
|
||||
<Bug className="w-5 h-5 text-amber-400" />
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-white">发现的问题 ({issues.length})</h3>
|
||||
<h3 className="text-lg font-bold uppercase tracking-wider text-foreground">发现的问题 ({issues.length})</h3>
|
||||
</div>
|
||||
<div className="p-6">
|
||||
<IssuesList issues={issues} />
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ export const PLATFORM_COLORS: Record<RepositoryPlatform, {
|
|||
bg: string;
|
||||
text: string;
|
||||
}> = {
|
||||
github: { bg: 'bg-gray-800', text: 'text-white' },
|
||||
github: { bg: 'bg-foreground', text: 'text-background' },
|
||||
gitlab: { bg: 'bg-orange-500', text: 'text-white' },
|
||||
other: { bg: 'bg-gray-500', text: 'text-white' }
|
||||
other: { bg: 'bg-muted-foreground', text: 'text-background' }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -25,13 +25,14 @@ export default {
|
|||
display: ['"Orbitron"', '"Rajdhani"', 'sans-serif'],
|
||||
},
|
||||
fontSize: {
|
||||
'xs': ['0.75rem', { lineHeight: '1rem', letterSpacing: '0.01em' }],
|
||||
'sm': ['0.875rem', { lineHeight: '1.25rem', letterSpacing: '0.01em' }],
|
||||
'base': ['1rem', { lineHeight: '1.5rem', letterSpacing: '0' }],
|
||||
'lg': ['1.125rem', { lineHeight: '1.75rem', letterSpacing: '-0.01em' }],
|
||||
'xl': ['1.25rem', { lineHeight: '1.75rem', letterSpacing: '-0.01em' }],
|
||||
'2xl': ['1.5rem', { lineHeight: '2rem', letterSpacing: '-0.02em' }],
|
||||
'3xl': ['1.875rem', { lineHeight: '2.25rem', letterSpacing: '-0.02em' }],
|
||||
'xs': ['0.8125rem', { lineHeight: '1.125rem', letterSpacing: '0.01em' }], // 13px
|
||||
'sm': ['0.9375rem', { lineHeight: '1.375rem', letterSpacing: '0.01em' }], // 15px
|
||||
'base': ['1rem', { lineHeight: '1.5rem', letterSpacing: '0' }], // 16px
|
||||
'lg': ['1.125rem', { lineHeight: '1.75rem', letterSpacing: '-0.01em' }], // 18px
|
||||
'xl': ['1.375rem', { lineHeight: '1.875rem', letterSpacing: '-0.01em' }], // 22px
|
||||
'2xl': ['1.625rem', { lineHeight: '2.125rem', letterSpacing: '-0.02em' }], // 26px
|
||||
'3xl': ['2rem', { lineHeight: '2.5rem', letterSpacing: '-0.02em' }], // 32px
|
||||
'4xl': ['2.5rem', { lineHeight: '3rem', letterSpacing: '-0.02em' }], // 40px
|
||||
},
|
||||
// Extended Color System
|
||||
colors: {
|
||||
|
|
|
|||
Loading…
Reference in New Issue