style(ui): 统一组件样式,优化视觉设计和交互体验
- 更新颜色系统,使用新的主题变量 - 调整圆角、边框、阴影等视觉样式 - 优化字体大小、间距和排版细节 - 改进表单组件的交互状态 - 增强暗色模式下的显示效果
This commit is contained in:
parent
51ee5a0da9
commit
8ee98a20eb
|
|
@ -1,5 +1,6 @@
|
||||||
import { StrictMode } from "react";
|
import { StrictMode } from "react";
|
||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
|
import { ThemeProvider } from "next-themes";
|
||||||
import "@/assets/styles/globals.css";
|
import "@/assets/styles/globals.css";
|
||||||
import App from "./App.tsx";
|
import App from "./App.tsx";
|
||||||
import { AppWrapper } from "@/components/layout/PageMeta";
|
import { AppWrapper } from "@/components/layout/PageMeta";
|
||||||
|
|
@ -9,9 +10,16 @@ import "@/shared/utils/fetchWrapper"; // 初始化fetch拦截器
|
||||||
createRoot(document.getElementById("root")!).render(
|
createRoot(document.getElementById("root")!).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
|
<ThemeProvider
|
||||||
|
attribute="class"
|
||||||
|
defaultTheme="dark"
|
||||||
|
enableSystem
|
||||||
|
disableTransitionOnChange={false}
|
||||||
|
>
|
||||||
<AppWrapper>
|
<AppWrapper>
|
||||||
<App />
|
<App />
|
||||||
</AppWrapper>
|
</AppWrapper>
|
||||||
|
</ThemeProvider>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</StrictMode>
|
</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="space-y-3">
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<div className="flex items-center gap-2 mb-2">
|
||||||
<Shield className="w-4 h-4 text-violet-400" />
|
<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>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -35,7 +35,7 @@ export default function AgentModeSelector({
|
||||||
"relative flex flex-col p-4 border cursor-pointer transition-all rounded",
|
"relative flex flex-col p-4 border cursor-pointer transition-all rounded",
|
||||||
value === "fast"
|
value === "fast"
|
||||||
? "border-amber-500/50 bg-amber-950/30"
|
? "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"
|
disabled && "opacity-50 cursor-not-allowed"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|
@ -54,16 +54,16 @@ export default function AgentModeSelector({
|
||||||
"p-1.5 rounded border",
|
"p-1.5 rounded border",
|
||||||
value === "fast"
|
value === "fast"
|
||||||
? "bg-amber-500/20 border-amber-500/50"
|
? "bg-amber-500/20 border-amber-500/50"
|
||||||
: "bg-gray-800 border-gray-700"
|
: "bg-muted border-border"
|
||||||
)}>
|
)}>
|
||||||
<Zap className={cn(
|
<Zap className={cn(
|
||||||
"w-4 h-4",
|
"w-4 h-4",
|
||||||
value === "fast" ? "text-amber-400" : "text-gray-500"
|
value === "fast" ? "text-amber-400" : "text-muted-foreground"
|
||||||
)} />
|
)} />
|
||||||
</div>
|
</div>
|
||||||
<span className={cn(
|
<span className={cn(
|
||||||
"font-bold text-sm font-mono uppercase",
|
"font-bold text-sm font-mono uppercase",
|
||||||
value === "fast" ? "text-amber-300" : "text-gray-400"
|
value === "fast" ? "text-amber-300" : "text-muted-foreground"
|
||||||
)}>
|
)}>
|
||||||
快速审计
|
快速审计
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -72,7 +72,7 @@ export default function AgentModeSelector({
|
||||||
)}
|
)}
|
||||||
</div>
|
</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">
|
<li className="flex items-center gap-1">
|
||||||
<Clock className="w-3 h-3" />
|
<Clock className="w-3 h-3" />
|
||||||
速度快(分钟级)
|
速度快(分钟级)
|
||||||
|
|
@ -81,14 +81,14 @@ export default function AgentModeSelector({
|
||||||
<Code className="w-3 h-3" />
|
<Code className="w-3 h-3" />
|
||||||
逐文件 LLM 分析
|
逐文件 LLM 分析
|
||||||
</li>
|
</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" />
|
<Shield className="w-3 h-3" />
|
||||||
无漏洞验证
|
无漏洞验证
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div className="mt-auto pt-2 border-t border-gray-800">
|
<div className="mt-auto pt-2 border-t border-border">
|
||||||
<span className="text-[10px] uppercase tracking-wider text-gray-600 font-bold font-mono">
|
<span className="text-xs uppercase tracking-wider text-muted-foreground font-bold font-mono">
|
||||||
适合: CI/CD 集成、日常检查
|
适合: CI/CD 集成、日常检查
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -100,7 +100,7 @@ export default function AgentModeSelector({
|
||||||
"relative flex flex-col p-4 border cursor-pointer transition-all rounded",
|
"relative flex flex-col p-4 border cursor-pointer transition-all rounded",
|
||||||
value === "agent"
|
value === "agent"
|
||||||
? "border-violet-500/50 bg-violet-950/30"
|
? "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"
|
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>
|
</div>
|
||||||
|
|
||||||
|
|
@ -124,16 +124,16 @@ export default function AgentModeSelector({
|
||||||
"p-1.5 rounded border",
|
"p-1.5 rounded border",
|
||||||
value === "agent"
|
value === "agent"
|
||||||
? "bg-violet-500/20 border-violet-500/50"
|
? "bg-violet-500/20 border-violet-500/50"
|
||||||
: "bg-gray-800 border-gray-700"
|
: "bg-muted border-border"
|
||||||
)}>
|
)}>
|
||||||
<Bot className={cn(
|
<Bot className={cn(
|
||||||
"w-4 h-4",
|
"w-4 h-4",
|
||||||
value === "agent" ? "text-violet-400" : "text-gray-500"
|
value === "agent" ? "text-violet-400" : "text-muted-foreground"
|
||||||
)} />
|
)} />
|
||||||
</div>
|
</div>
|
||||||
<span className={cn(
|
<span className={cn(
|
||||||
"font-bold text-sm font-mono uppercase",
|
"font-bold text-sm font-mono uppercase",
|
||||||
value === "agent" ? "text-violet-300" : "text-gray-400"
|
value === "agent" ? "text-violet-300" : "text-muted-foreground"
|
||||||
)}>
|
)}>
|
||||||
Agent 审计
|
Agent 审计
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -142,7 +142,7 @@ export default function AgentModeSelector({
|
||||||
)}
|
)}
|
||||||
</div>
|
</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">
|
<li className="flex items-center gap-1">
|
||||||
<Bot className="w-3 h-3" />
|
<Bot className="w-3 h-3" />
|
||||||
AI Agent 自主分析
|
AI Agent 自主分析
|
||||||
|
|
@ -153,15 +153,15 @@ export default function AgentModeSelector({
|
||||||
</li>
|
</li>
|
||||||
<li className={cn(
|
<li className={cn(
|
||||||
"flex items-center gap-1",
|
"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" />
|
<Shield className="w-3 h-3" />
|
||||||
沙箱漏洞验证
|
沙箱漏洞验证
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div className="mt-auto pt-2 border-t border-gray-800">
|
<div className="mt-auto pt-2 border-t border-border">
|
||||||
<span className="text-[10px] uppercase tracking-wider text-gray-600 font-bold font-mono">
|
<span className="text-xs uppercase tracking-wider text-muted-foreground font-bold font-mono">
|
||||||
适合: 发版前审计、深度安全评估
|
适合: 发版前审计、深度安全评估
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -232,16 +232,16 @@ export default function CreateAgentTaskDialog({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<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 */}
|
{/* Header */}
|
||||||
<DialogHeader className="px-5 py-4 border-b border-gray-800 flex-shrink-0 bg-gray-900/50">
|
<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-white">
|
<DialogTitle className="flex items-center gap-3 font-mono text-foreground">
|
||||||
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
||||||
<Bot className="w-5 h-5 text-primary" />
|
<Bot className="w-5 h-5 text-primary" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="text-base font-bold uppercase tracking-wider">New Agent Audit</span>
|
<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
|
AI-Powered Security Analysis
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -252,17 +252,17 @@ export default function CreateAgentTaskDialog({
|
||||||
{/* 项目选择 */}
|
{/* 项目选择 */}
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="flex items-center justify-between">
|
<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
|
Select Project
|
||||||
</span>
|
</span>
|
||||||
<Badge className="cyber-badge-muted font-mono text-[10px]">
|
<Badge className="cyber-badge-muted font-mono text-xs">
|
||||||
{filteredProjects.length} available
|
{filteredProjects.length} available
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 搜索框 */}
|
{/* 搜索框 */}
|
||||||
<div className="relative">
|
<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
|
<Input
|
||||||
placeholder="Search projects..."
|
placeholder="Search projects..."
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
|
|
@ -272,13 +272,13 @@ export default function CreateAgentTaskDialog({
|
||||||
</div>
|
</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 ? (
|
{loadingProjects ? (
|
||||||
<div className="flex items-center justify-center h-full">
|
<div className="flex items-center justify-center h-full">
|
||||||
<Loader2 className="w-5 h-5 animate-spin text-primary" />
|
<Loader2 className="w-5 h-5 animate-spin text-primary" />
|
||||||
</div>
|
</div>
|
||||||
) : filteredProjects.length === 0 ? (
|
) : 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" />
|
<Package className="w-8 h-8 mb-2 opacity-50" />
|
||||||
<span className="text-sm">{searchTerm ? "No matches" : "No projects"}</span>
|
<span className="text-sm">{searchTerm ? "No matches" : "No projects"}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -302,9 +302,9 @@ export default function CreateAgentTaskDialog({
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{/* 仓库项目:分支选择 */}
|
{/* 仓库项目:分支选择 */}
|
||||||
{isRepositoryProject(selectedProject) && (
|
{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" />
|
<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 ? (
|
{loadingBranches ? (
|
||||||
<div className="flex items-center gap-2 flex-1">
|
<div className="flex items-center gap-2 flex-1">
|
||||||
<Loader2 className="w-4 h-4 animate-spin text-blue-400" />
|
<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">
|
<SelectTrigger className="flex-1 h-9 cyber-input">
|
||||||
<SelectValue placeholder="Select branch" />
|
<SelectValue placeholder="Select branch" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
<SelectContent className="cyber-dialog border-border">
|
||||||
{branches.map((b) => (
|
{branches.map((b) => (
|
||||||
<SelectItem key={b} value={b} className="font-mono text-white">
|
<SelectItem key={b} value={b} className="font-mono text-foreground">
|
||||||
{b}
|
{b}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
|
|
@ -329,27 +329,27 @@ export default function CreateAgentTaskDialog({
|
||||||
|
|
||||||
{/* ZIP 项目:文件选择 */}
|
{/* ZIP 项目:文件选择 */}
|
||||||
{isZipProject(selectedProject) && (
|
{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">
|
<div className="flex items-center gap-3">
|
||||||
<Package className="w-5 h-5 text-amber-400" />
|
<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>
|
</div>
|
||||||
|
|
||||||
{storedZipInfo?.has_file && (
|
{storedZipInfo?.has_file && (
|
||||||
<div
|
<div
|
||||||
className={`p-2 rounded border cursor-pointer transition-colors ${useStoredZip
|
className={`p-2 rounded border cursor-pointer transition-colors ${useStoredZip
|
||||||
? 'border-emerald-500/50 bg-emerald-950/30'
|
? '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)}
|
onClick={() => setUseStoredZip(true)}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-2">
|
<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}
|
{storedZipInfo.original_filename}
|
||||||
</span>
|
</span>
|
||||||
<Badge className="cyber-badge-success text-[10px]">
|
<Badge className="cyber-badge-success text-xs">
|
||||||
Stored
|
Stored
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -359,14 +359,14 @@ export default function CreateAgentTaskDialog({
|
||||||
<div
|
<div
|
||||||
className={`p-2 rounded border cursor-pointer transition-colors ${!useStoredZip && zipFile
|
className={`p-2 rounded border cursor-pointer transition-colors ${!useStoredZip && zipFile
|
||||||
? 'border-amber-500/50 bg-amber-950/30'
|
? '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">
|
<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" />
|
<Upload className="w-4 h-4 text-muted-foreground" />
|
||||||
<span className="text-sm text-gray-400 font-mono">
|
<span className="text-sm text-muted-foreground font-mono">
|
||||||
{zipFile ? zipFile.name : "Upload new file..."}
|
{zipFile ? zipFile.name : "Upload new file..."}
|
||||||
</span>
|
</span>
|
||||||
<input
|
<input
|
||||||
|
|
@ -382,7 +382,7 @@ export default function CreateAgentTaskDialog({
|
||||||
|
|
||||||
{/* 高级选项 */}
|
{/* 高级选项 */}
|
||||||
<Collapsible open={showAdvanced} onOpenChange={setShowAdvanced}>
|
<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" : ""}`} />
|
<ChevronRight className={`w-4 h-4 transition-transform ${showAdvanced ? "rotate-90" : ""}`} />
|
||||||
<Settings2 className="w-4 h-4" />
|
<Settings2 className="w-4 h-4" />
|
||||||
<span className="uppercase font-bold">Advanced Options</span>
|
<span className="uppercase font-bold">Advanced Options</span>
|
||||||
|
|
@ -396,12 +396,12 @@ export default function CreateAgentTaskDialog({
|
||||||
const canSelectFiles = isRepo || (isZip && useStoredZip && hasStoredZip);
|
const canSelectFiles = isRepo || (isZip && useStoredZip && hasStoredZip);
|
||||||
|
|
||||||
return (
|
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>
|
<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
|
Scan Scope
|
||||||
</p>
|
</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
|
||||||
? `${selectedFiles.length} files selected`
|
? `${selectedFiles.length} files selected`
|
||||||
: "All files"}
|
: "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">
|
<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
|
Exclude Patterns
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
|
|
@ -452,7 +452,7 @@ export default function CreateAgentTaskDialog({
|
||||||
{excludePatterns.map((p) => (
|
{excludePatterns.map((p) => (
|
||||||
<Badge
|
<Badge
|
||||||
key={p}
|
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))}
|
onClick={() => setExcludePatterns((prev) => prev.filter((x) => x !== p))}
|
||||||
>
|
>
|
||||||
{p} ×
|
{p} ×
|
||||||
|
|
@ -481,12 +481,12 @@ export default function CreateAgentTaskDialog({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer */}
|
{/* 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
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={() => onOpenChange(false)}
|
onClick={() => onOpenChange(false)}
|
||||||
disabled={creating}
|
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
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -539,7 +539,7 @@ function ProjectItem({
|
||||||
<div
|
<div
|
||||||
className={`flex items-center gap-3 p-3 cursor-pointer rounded transition-all ${selected
|
className={`flex items-center gap-3 p-3 cursor-pointer rounded transition-all ${selected
|
||||||
? "bg-primary/10 border border-primary/50"
|
? "bg-primary/10 border border-primary/50"
|
||||||
: "hover:bg-gray-800/50 border border-transparent"
|
: "hover:bg-muted border border-transparent"
|
||||||
}`}
|
}`}
|
||||||
onClick={onSelect}
|
onClick={onSelect}
|
||||||
>
|
>
|
||||||
|
|
@ -553,11 +553,11 @@ function ProjectItem({
|
||||||
|
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-center gap-2">
|
<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}
|
{project.name}
|
||||||
</span>
|
</span>
|
||||||
<Badge
|
<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-blue-500/20 text-blue-400 border-blue-500/30"
|
||||||
: "bg-amber-500/20 text-amber-400 border-amber-500/30"
|
: "bg-amber-500/20 text-amber-400 border-amber-500/30"
|
||||||
}`}
|
}`}
|
||||||
|
|
@ -566,7 +566,7 @@ function ProjectItem({
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
{project.description && (
|
{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}
|
{project.description}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -189,7 +189,7 @@ export default function EmbeddingConfigPanel() {
|
||||||
<div className="flex items-center justify-center min-h-[300px]">
|
<div className="flex items-center justify-center min-h-[300px]">
|
||||||
<div className="text-center space-y-4">
|
<div className="text-center space-y-4">
|
||||||
<div className="loading-spinner mx-auto" />
|
<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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -202,26 +202,26 @@ export default function EmbeddingConfigPanel() {
|
||||||
<div className="cyber-card p-4 border-primary/30">
|
<div className="cyber-card p-4 border-primary/30">
|
||||||
<div className="flex items-center gap-2 mb-3">
|
<div className="flex items-center gap-2 mb-3">
|
||||||
<Server className="w-4 h-4 text-primary" />
|
<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>
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
<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">
|
<div className="bg-muted p-3 rounded-lg border border-border">
|
||||||
<p className="text-xs text-gray-500 uppercase mb-1">提供商</p>
|
<p className="text-xs text-muted-foreground uppercase mb-1">提供商</p>
|
||||||
<Badge className="bg-primary/20 text-primary border-primary/50 font-mono">
|
<Badge className="bg-primary/20 text-primary border-primary/50 font-mono">
|
||||||
{currentConfig.provider}
|
{currentConfig.provider}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-gray-900/50 p-3 rounded-lg border border-gray-800">
|
<div className="bg-muted p-3 rounded-lg border border-border">
|
||||||
<p className="text-xs text-gray-500 uppercase mb-1">模型</p>
|
<p className="text-xs text-muted-foreground uppercase mb-1">模型</p>
|
||||||
<p className="font-mono text-sm text-gray-300 truncate">{currentConfig.model}</p>
|
<p className="font-mono text-sm text-foreground truncate">{currentConfig.model}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-gray-900/50 p-3 rounded-lg border border-gray-800">
|
<div className="bg-muted p-3 rounded-lg border border-border">
|
||||||
<p className="text-xs text-gray-500 uppercase mb-1">向量维度</p>
|
<p className="text-xs text-muted-foreground uppercase mb-1">向量维度</p>
|
||||||
<p className="font-mono text-sm text-gray-300">{currentConfig.dimensions}</p>
|
<p className="font-mono text-sm text-foreground">{currentConfig.dimensions}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-gray-900/50 p-3 rounded-lg border border-gray-800">
|
<div className="bg-muted p-3 rounded-lg border border-border">
|
||||||
<p className="text-xs text-gray-500 uppercase mb-1">批处理大小</p>
|
<p className="text-xs text-muted-foreground uppercase mb-1">批处理大小</p>
|
||||||
<p className="font-mono text-sm text-gray-300">{currentConfig.batch_size}</p>
|
<p className="font-mono text-sm text-foreground">{currentConfig.batch_size}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -231,12 +231,12 @@ export default function EmbeddingConfigPanel() {
|
||||||
<div className="cyber-card p-6 space-y-6">
|
<div className="cyber-card p-6 space-y-6">
|
||||||
{/* 提供商选择 */}
|
{/* 提供商选择 */}
|
||||||
<div className="space-y-2">
|
<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}>
|
<Select value={selectedProvider} onValueChange={handleProviderChange}>
|
||||||
<SelectTrigger className="h-12 cyber-input">
|
<SelectTrigger className="h-12 cyber-input">
|
||||||
<SelectValue placeholder="选择提供商" />
|
<SelectValue placeholder="选择提供商" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
<SelectContent className="cyber-dialog border-border">
|
||||||
{providers.map((provider) => (
|
{providers.map((provider) => (
|
||||||
<SelectItem key={provider.id} value={provider.id} className="font-mono">
|
<SelectItem key={provider.id} value={provider.id} className="font-mono">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
|
@ -253,7 +253,7 @@ export default function EmbeddingConfigPanel() {
|
||||||
</Select>
|
</Select>
|
||||||
|
|
||||||
{selectedProviderInfo && (
|
{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" />
|
<Info className="w-3 h-3 text-sky-400" />
|
||||||
{selectedProviderInfo.description}
|
{selectedProviderInfo.description}
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -263,7 +263,7 @@ export default function EmbeddingConfigPanel() {
|
||||||
{/* 模型选择/输入 */}
|
{/* 模型选择/输入 */}
|
||||||
{selectedProviderInfo && (
|
{selectedProviderInfo && (
|
||||||
<div className="space-y-2">
|
<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
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
value={selectedModel}
|
value={selectedModel}
|
||||||
|
|
@ -273,7 +273,7 @@ export default function EmbeddingConfigPanel() {
|
||||||
/>
|
/>
|
||||||
{selectedProviderInfo.models.length > 0 && (
|
{selectedProviderInfo.models.length > 0 && (
|
||||||
<div className="flex flex-wrap gap-2 mt-2">
|
<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) => (
|
{selectedProviderInfo.models.map((model) => (
|
||||||
<button
|
<button
|
||||||
key={model}
|
key={model}
|
||||||
|
|
@ -282,7 +282,7 @@ export default function EmbeddingConfigPanel() {
|
||||||
className={`px-2 py-1 text-xs font-mono rounded border transition-colors ${
|
className={`px-2 py-1 text-xs font-mono rounded border transition-colors ${
|
||||||
selectedModel === model
|
selectedModel === model
|
||||||
? "bg-primary/20 border-primary/50 text-primary"
|
? "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}
|
{model}
|
||||||
|
|
@ -296,7 +296,7 @@ export default function EmbeddingConfigPanel() {
|
||||||
{/* API Key */}
|
{/* API Key */}
|
||||||
{selectedProviderInfo?.requires_api_key && (
|
{selectedProviderInfo?.requires_api_key && (
|
||||||
<div className="space-y-2">
|
<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
|
API Key
|
||||||
<span className="text-rose-400 ml-1">*</span>
|
<span className="text-rose-400 ml-1">*</span>
|
||||||
</Label>
|
</Label>
|
||||||
|
|
@ -307,7 +307,7 @@ export default function EmbeddingConfigPanel() {
|
||||||
placeholder="输入 API Key"
|
placeholder="输入 API Key"
|
||||||
className="h-10 cyber-input"
|
className="h-10 cyber-input"
|
||||||
/>
|
/>
|
||||||
<p className="text-xs text-gray-600">
|
<p className="text-xs text-muted-foreground">
|
||||||
API Key 将安全存储,不会显示在页面上
|
API Key 将安全存储,不会显示在页面上
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -315,8 +315,8 @@ export default function EmbeddingConfigPanel() {
|
||||||
|
|
||||||
{/* 自定义端点 */}
|
{/* 自定义端点 */}
|
||||||
<div className="space-y-2">
|
<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 端点 <span className="text-gray-600">(可选)</span>
|
自定义 API 端点 <span className="text-muted-foreground">(可选)</span>
|
||||||
</Label>
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
type="url"
|
type="url"
|
||||||
|
|
@ -335,14 +335,14 @@ export default function EmbeddingConfigPanel() {
|
||||||
}
|
}
|
||||||
className="h-10 cyber-input"
|
className="h-10 cyber-input"
|
||||||
/>
|
/>
|
||||||
<p className="text-xs text-gray-600">
|
<p className="text-xs text-muted-foreground">
|
||||||
用于 API 代理或自托管服务
|
用于 API 代理或自托管服务
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 批处理大小 */}
|
{/* 批处理大小 */}
|
||||||
<div className="space-y-2">
|
<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
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
value={batchSize}
|
value={batchSize}
|
||||||
|
|
@ -351,7 +351,7 @@ export default function EmbeddingConfigPanel() {
|
||||||
max={500}
|
max={500}
|
||||||
className="h-10 cyber-input w-32"
|
className="h-10 cyber-input w-32"
|
||||||
/>
|
/>
|
||||||
<p className="text-xs text-gray-600">
|
<p className="text-xs text-muted-foreground">
|
||||||
每批嵌入的文本数量,建议 50-100
|
每批嵌入的文本数量,建议 50-100
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -379,14 +379,14 @@ export default function EmbeddingConfigPanel() {
|
||||||
{testResult.success ? "测试成功" : "测试失败"}
|
{testResult.success ? "测试成功" : "测试失败"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-gray-400">{testResult.message}</p>
|
<p className="text-sm text-muted-foreground">{testResult.message}</p>
|
||||||
{testResult.success && (
|
{testResult.success && (
|
||||||
<div className="mt-3 pt-3 border-t border-gray-800 text-xs text-gray-500 space-y-1 font-mono">
|
<div className="mt-3 pt-3 border-t border-border text-xs text-muted-foreground space-y-1 font-mono">
|
||||||
<div>向量维度: <span className="text-gray-300">{testResult.dimensions}</span></div>
|
<div>向量维度: <span className="text-foreground">{testResult.dimensions}</span></div>
|
||||||
<div>延迟: <span className="text-gray-300">{testResult.latency_ms}ms</span></div>
|
<div>延迟: <span className="text-foreground">{testResult.latency_ms}ms</span></div>
|
||||||
{testResult.sample_embedding && (
|
{testResult.sample_embedding && (
|
||||||
<div className="truncate">
|
<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>
|
||||||
)}
|
)}
|
||||||
</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
|
<Button
|
||||||
onClick={handleTest}
|
onClick={handleTest}
|
||||||
disabled={testing || !selectedProvider || !selectedModel}
|
disabled={testing || !selectedProvider || !selectedModel}
|
||||||
|
|
@ -434,15 +434,15 @@ export default function EmbeddingConfigPanel() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 说明 */}
|
{/* 说明 */}
|
||||||
<div className="bg-gray-900/50 border border-gray-800 p-4 rounded-lg text-xs space-y-2">
|
<div className="bg-muted border border-border p-4 rounded-lg text-xs space-y-2">
|
||||||
<p className="font-bold uppercase text-gray-400 flex items-center gap-2">
|
<p className="font-bold uppercase text-muted-foreground flex items-center gap-2">
|
||||||
<Info className="w-4 h-4 text-sky-400" />
|
<Info className="w-4 h-4 text-sky-400" />
|
||||||
关于嵌入模型
|
关于嵌入模型
|
||||||
</p>
|
</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>• 嵌入模型用于 Agent 审计的代码语义搜索 (RAG)</li>
|
||||||
<li>• 与分析使用的 LLM 独立配置,互不影响</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>
|
<li>• 向量维度影响存储空间和检索精度</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -305,16 +305,16 @@ export default function CreateTaskDialog({
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<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 */}
|
{/* Header */}
|
||||||
<DialogHeader className="px-5 py-4 border-b border-gray-800 flex-shrink-0 bg-gray-900/50">
|
<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-white">
|
<DialogTitle className="flex items-center gap-3 font-mono text-foreground">
|
||||||
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
||||||
<Shield className="w-5 h-5 text-primary" />
|
<Shield className="w-5 h-5 text-primary" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="text-base font-bold uppercase tracking-wider">开始代码审计</span>
|
<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
|
Code Security Analysis
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -325,17 +325,17 @@ export default function CreateTaskDialog({
|
||||||
{/* 项目选择 */}
|
{/* 项目选择 */}
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="flex items-center justify-between">
|
<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>
|
</span>
|
||||||
<Badge className="cyber-badge-muted font-mono text-[10px]">
|
<Badge className="cyber-badge-muted font-mono text-xs">
|
||||||
{filteredProjects.length} 个
|
{filteredProjects.length} 个
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 搜索框 */}
|
{/* 搜索框 */}
|
||||||
<div className="relative">
|
<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
|
<Input
|
||||||
placeholder="搜索项目..."
|
placeholder="搜索项目..."
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
|
|
@ -345,13 +345,13 @@ export default function CreateTaskDialog({
|
||||||
</div>
|
</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 ? (
|
{loading ? (
|
||||||
<div className="flex items-center justify-center h-full">
|
<div className="flex items-center justify-center h-full">
|
||||||
<Loader2 className="w-5 h-5 animate-spin text-primary" />
|
<Loader2 className="w-5 h-5 animate-spin text-primary" />
|
||||||
</div>
|
</div>
|
||||||
) : filteredProjects.length === 0 ? (
|
) : 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" />
|
<Package className="w-8 h-8 mb-2 opacity-50" />
|
||||||
<span className="text-sm">
|
<span className="text-sm">
|
||||||
{searchTerm ? "未找到" : "暂无项目"}
|
{searchTerm ? "未找到" : "暂无项目"}
|
||||||
|
|
@ -384,14 +384,14 @@ export default function CreateTaskDialog({
|
||||||
{/* 配置区域 */}
|
{/* 配置区域 */}
|
||||||
{selectedProject && (
|
{selectedProject && (
|
||||||
<div className="space-y-4">
|
<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>
|
</span>
|
||||||
|
|
||||||
{isRepositoryProject(selectedProject) ? (
|
{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" />
|
<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>
|
</span>
|
||||||
{loadingBranches ? (
|
{loadingBranches ? (
|
||||||
|
|
@ -404,7 +404,7 @@ export default function CreateTaskDialog({
|
||||||
<SelectTrigger className="h-9 flex-1 cyber-input">
|
<SelectTrigger className="h-9 flex-1 cyber-input">
|
||||||
<SelectValue placeholder="选择分支" />
|
<SelectValue placeholder="选择分支" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
<SelectContent className="cyber-dialog border-border">
|
||||||
{branches.map((b) => (
|
{branches.map((b) => (
|
||||||
<SelectItem key={b} value={b} className="font-mono">
|
<SelectItem key={b} value={b} className="font-mono">
|
||||||
{b}
|
{b}
|
||||||
|
|
@ -438,19 +438,19 @@ export default function CreateTaskDialog({
|
||||||
|
|
||||||
{/* 规则集和提示词选择 - 仅快速扫描模式显示 */}
|
{/* 规则集和提示词选择 - 仅快速扫描模式显示 */}
|
||||||
{auditMode !== "agent" && (
|
{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">
|
<div className="flex items-center gap-2 mb-2">
|
||||||
<Zap className="w-4 h-4 text-violet-400" />
|
<Zap className="w-4 h-4 text-violet-400" />
|
||||||
<span className="font-mono text-sm font-bold text-violet-300 uppercase">审计配置</span>
|
<span className="font-mono text-sm font-bold text-violet-300 uppercase">审计配置</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
<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={selectedRuleSetId} onValueChange={setSelectedRuleSetId}>
|
<Select value={selectedRuleSetId} onValueChange={setSelectedRuleSetId}>
|
||||||
<SelectTrigger className="h-9 cyber-input text-xs">
|
<SelectTrigger className="h-9 cyber-input text-xs">
|
||||||
<SelectValue placeholder="选择规则集" />
|
<SelectValue placeholder="选择规则集" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
<SelectContent className="cyber-dialog border-border">
|
||||||
{ruleSets.map((rs) => (
|
{ruleSets.map((rs) => (
|
||||||
<SelectItem key={rs.id} value={rs.id} className="font-mono text-xs">
|
<SelectItem key={rs.id} value={rs.id} className="font-mono text-xs">
|
||||||
{rs.name} {rs.is_default && '(默认)'} ({rs.enabled_rules_count})
|
{rs.name} {rs.is_default && '(默认)'} ({rs.enabled_rules_count})
|
||||||
|
|
@ -460,12 +460,12 @@ export default function CreateTaskDialog({
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<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}>
|
<Select value={selectedPromptTemplateId} onValueChange={setSelectedPromptTemplateId}>
|
||||||
<SelectTrigger className="h-9 cyber-input text-xs">
|
<SelectTrigger className="h-9 cyber-input text-xs">
|
||||||
<SelectValue placeholder="选择提示词模板" />
|
<SelectValue placeholder="选择提示词模板" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
<SelectContent className="cyber-dialog border-border">
|
||||||
{promptTemplates.map((pt) => (
|
{promptTemplates.map((pt) => (
|
||||||
<SelectItem key={pt.id} value={pt.id} className="font-mono text-xs">
|
<SelectItem key={pt.id} value={pt.id} className="font-mono text-xs">
|
||||||
{pt.name} {pt.is_default && '(默认)'}
|
{pt.name} {pt.is_default && '(默认)'}
|
||||||
|
|
@ -480,7 +480,7 @@ export default function CreateTaskDialog({
|
||||||
|
|
||||||
{/* 高级选项 */}
|
{/* 高级选项 */}
|
||||||
<Collapsible open={showAdvanced} onOpenChange={setShowAdvanced}>
|
<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
|
<ChevronRight
|
||||||
className={`w-4 h-4 transition-transform ${showAdvanced ? "rotate-90" : ""}`}
|
className={`w-4 h-4 transition-transform ${showAdvanced ? "rotate-90" : ""}`}
|
||||||
/>
|
/>
|
||||||
|
|
@ -489,9 +489,9 @@ export default function CreateTaskDialog({
|
||||||
</CollapsibleTrigger>
|
</CollapsibleTrigger>
|
||||||
<CollapsibleContent className="mt-3 space-y-3">
|
<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">
|
<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>
|
</span>
|
||||||
<button
|
<button
|
||||||
|
|
@ -507,7 +507,7 @@ export default function CreateTaskDialog({
|
||||||
{excludePatterns.map((p) => (
|
{excludePatterns.map((p) => (
|
||||||
<Badge
|
<Badge
|
||||||
key={p}
|
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={() =>
|
onClick={() =>
|
||||||
setExcludePatterns((prev) =>
|
setExcludePatterns((prev) =>
|
||||||
prev.filter((x) => x !== p)
|
prev.filter((x) => x !== p)
|
||||||
|
|
@ -518,12 +518,12 @@ export default function CreateTaskDialog({
|
||||||
</Badge>
|
</Badge>
|
||||||
))}
|
))}
|
||||||
{excludePatterns.length === 0 && (
|
{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>
|
||||||
|
|
||||||
<div className="flex flex-wrap gap-1">
|
<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) => (
|
{[".test.", ".spec.", ".min.", "coverage/", "docs/", ".md"].map((pattern) => (
|
||||||
<button
|
<button
|
||||||
key={pattern}
|
key={pattern}
|
||||||
|
|
@ -534,7 +534,7 @@ export default function CreateTaskDialog({
|
||||||
setExcludePatterns((prev) => [...prev, pattern]);
|
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}
|
+{pattern}
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -565,12 +565,12 @@ export default function CreateTaskDialog({
|
||||||
const canSelectFiles = isRepo || (isZip && useStored && hasStoredZip);
|
const canSelectFiles = isRepo || (isZip && useStored && hasStoredZip);
|
||||||
|
|
||||||
return (
|
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>
|
<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>
|
||||||
<p className="text-sm font-bold text-white mt-1">
|
<p className="text-sm font-bold text-foreground mt-1">
|
||||||
{selectedFiles
|
{selectedFiles
|
||||||
? `已选 ${selectedFiles.length} 个文件`
|
? `已选 ${selectedFiles.length} 个文件`
|
||||||
: "全部文件"}
|
: "全部文件"}
|
||||||
|
|
@ -608,12 +608,12 @@ export default function CreateTaskDialog({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer */}
|
{/* 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
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={() => onOpenChange(false)}
|
onClick={() => onOpenChange(false)}
|
||||||
disabled={creating}
|
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>
|
</Button>
|
||||||
|
|
@ -670,13 +670,13 @@ function ProjectCard({
|
||||||
<div
|
<div
|
||||||
className={`flex items-center gap-3 p-3 cursor-pointer rounded transition-all ${selected
|
className={`flex items-center gap-3 p-3 cursor-pointer rounded transition-all ${selected
|
||||||
? "bg-primary/10 border border-primary/50"
|
? "bg-primary/10 border border-primary/50"
|
||||||
: "hover:bg-gray-800/50 border border-transparent"
|
: "hover:bg-muted border border-transparent"
|
||||||
}`}
|
}`}
|
||||||
onClick={onSelect}
|
onClick={onSelect}
|
||||||
>
|
>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={selected}
|
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"}`}>
|
<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-1 min-w-0 overflow-hidden">
|
||||||
<div className="flex items-center gap-2">
|
<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}
|
{project.name}
|
||||||
</span>
|
</span>
|
||||||
<Badge
|
<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-blue-500/20 text-blue-400 border-blue-500/30"
|
||||||
: "bg-amber-500/20 text-amber-400 border-amber-500/30"
|
: "bg-amber-500/20 text-amber-400 border-amber-500/30"
|
||||||
}`}
|
}`}
|
||||||
|
|
@ -702,7 +702,7 @@ function ProjectCard({
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
{project.description && (
|
{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}
|
{project.description}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
@ -722,7 +722,7 @@ function ZipUploadCard({
|
||||||
}) {
|
}) {
|
||||||
if (zipState.loading) {
|
if (zipState.loading) {
|
||||||
return (
|
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" />
|
<Loader2 className="w-5 h-5 animate-spin text-blue-400" />
|
||||||
<span className="text-sm font-mono text-blue-400">
|
<span className="text-sm font-mono text-blue-400">
|
||||||
检查文件中...
|
检查文件中...
|
||||||
|
|
@ -733,7 +733,7 @@ function ZipUploadCard({
|
||||||
|
|
||||||
if (zipState.storedZipInfo?.has_file) {
|
if (zipState.storedZipInfo?.has_file) {
|
||||||
return (
|
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="flex items-center gap-3">
|
||||||
<div className="p-1.5 bg-emerald-500/20 rounded">
|
<div className="p-1.5 bg-emerald-500/20 rounded">
|
||||||
<Package className="w-4 h-4 text-emerald-400" />
|
<Package className="w-4 h-4 text-emerald-400" />
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ const getFileIcon = (path: string) => {
|
||||||
if (configExts.includes(ext)) {
|
if (configExts.includes(ext)) {
|
||||||
return <FileJson className="w-4 h-4 text-amber-400" />;
|
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(
|
items.push(
|
||||||
<div key={`folder-${folder.path}`}>
|
<div key={`folder-${folder.path}`}>
|
||||||
<div
|
<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` }}
|
style={{ paddingLeft: `${depth * 16 + 8}px` }}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
|
|
@ -334,12 +334,12 @@ export default function FileSelectionDialog({
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
handleExpandFolder(folder.path);
|
handleExpandFolder(folder.path);
|
||||||
}}
|
}}
|
||||||
className="p-0.5 hover:bg-gray-700 rounded"
|
className="p-0.5 hover:bg-muted rounded"
|
||||||
>
|
>
|
||||||
{isExpanded ? (
|
{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>
|
</button>
|
||||||
<div onClick={(e) => e.stopPropagation()}>
|
<div onClick={(e) => e.stopPropagation()}>
|
||||||
|
|
@ -352,7 +352,7 @@ export default function FileSelectionDialog({
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onCheckedChange={() => handleToggleFolder(folder.path)}
|
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>
|
</div>
|
||||||
{isExpanded ? (
|
{isExpanded ? (
|
||||||
|
|
@ -361,12 +361,12 @@ export default function FileSelectionDialog({
|
||||||
<Folder className="w-4 h-4 text-amber-400" />
|
<Folder className="w-4 h-4 text-amber-400" />
|
||||||
)}
|
)}
|
||||||
<span
|
<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)}
|
onClick={() => handleExpandFolder(folder.path)}
|
||||||
>
|
>
|
||||||
{folder.name}
|
{folder.name}
|
||||||
</span>
|
</span>
|
||||||
<Badge className="cyber-badge-muted font-mono text-[10px]">
|
<Badge className="cyber-badge-muted font-mono text-xs">
|
||||||
{
|
{
|
||||||
filteredFiles.filter((f) =>
|
filteredFiles.filter((f) =>
|
||||||
f.path.startsWith(folder.path + "/")
|
f.path.startsWith(folder.path + "/")
|
||||||
|
|
@ -387,7 +387,7 @@ export default function FileSelectionDialog({
|
||||||
items.push(
|
items.push(
|
||||||
<div
|
<div
|
||||||
key={`file-${file.path}`}
|
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` }}
|
style={{ paddingLeft: `${depth * 16 + 32}px` }}
|
||||||
onClick={() => handleToggleFile(file.path)}
|
onClick={() => handleToggleFile(file.path)}
|
||||||
>
|
>
|
||||||
|
|
@ -395,18 +395,18 @@ export default function FileSelectionDialog({
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={selectedFiles.has(file.path)}
|
checked={selectedFiles.has(file.path)}
|
||||||
onCheckedChange={() => handleToggleFile(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>
|
</div>
|
||||||
{getFileIcon(file.path)}
|
{getFileIcon(file.path)}
|
||||||
<span
|
<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}
|
title={file.path}
|
||||||
>
|
>
|
||||||
{fileName}
|
{fileName}
|
||||||
</span>
|
</span>
|
||||||
{file.size > 0 && (
|
{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)}
|
{formatSize(file.size)}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
|
|
@ -422,24 +422,24 @@ export default function FileSelectionDialog({
|
||||||
return filteredFiles.map((file) => (
|
return filteredFiles.map((file) => (
|
||||||
<div
|
<div
|
||||||
key={file.path}
|
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)}
|
onClick={() => handleToggleFile(file.path)}
|
||||||
>
|
>
|
||||||
<div onClick={(e) => e.stopPropagation()}>
|
<div onClick={(e) => e.stopPropagation()}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={selectedFiles.has(file.path)}
|
checked={selectedFiles.has(file.path)}
|
||||||
onCheckedChange={() => handleToggleFile(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>
|
</div>
|
||||||
{getFileIcon(file.path)}
|
{getFileIcon(file.path)}
|
||||||
<div className="flex-1 min-w-0">
|
<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}
|
{file.path}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{file.size > 0 && (
|
{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)}
|
{formatSize(file.size)}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
|
|
@ -449,11 +449,11 @@ export default function FileSelectionDialog({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<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">
|
<DialogHeader className="cyber-card-header flex-shrink-0">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<FolderOpen className="w-5 h-5 text-primary" />
|
<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>
|
</DialogTitle>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -469,7 +469,7 @@ export default function FileSelectionDialog({
|
||||||
<div className="flex items-center gap-2 flex-wrap">
|
<div className="flex items-center gap-2 flex-wrap">
|
||||||
{/* 搜索框 */}
|
{/* 搜索框 */}
|
||||||
<div className="relative flex-1 min-w-[200px]">
|
<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
|
<Input
|
||||||
placeholder="搜索文件..."
|
placeholder="搜索文件..."
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
|
|
@ -481,11 +481,11 @@ export default function FileSelectionDialog({
|
||||||
{/* 文件类型筛选 */}
|
{/* 文件类型筛选 */}
|
||||||
{fileTypes.length > 0 && (
|
{fileTypes.length > 0 && (
|
||||||
<div className="flex items-center gap-1">
|
<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
|
<select
|
||||||
value={filterType}
|
value={filterType}
|
||||||
onChange={(e) => setFilterType(e.target.value)}
|
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>
|
<option value="">全部类型</option>
|
||||||
{fileTypes.slice(0, 10).map(([ext, count]) => (
|
{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
|
<button
|
||||||
onClick={() => setViewMode("tree")}
|
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>
|
||||||
<button
|
<button
|
||||||
onClick={() => setViewMode("flat")}
|
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>
|
</button>
|
||||||
|
|
@ -552,14 +552,14 @@ export default function FileSelectionDialog({
|
||||||
setSearchTerm("");
|
setSearchTerm("");
|
||||||
setFilterType("");
|
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" />
|
<RotateCcw className="w-3 h-3 mr-1" />
|
||||||
重置筛选
|
重置筛选
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm font-mono text-gray-500">
|
<div className="text-sm font-mono text-muted-foreground">
|
||||||
{searchTerm || filterType ? (
|
{searchTerm || filterType ? (
|
||||||
<span>
|
<span>
|
||||||
筛选: {filteredFiles.length}/{files.length} 个文件,
|
筛选: {filteredFiles.length}/{files.length} 个文件,
|
||||||
|
|
@ -574,7 +574,7 @@ export default function FileSelectionDialog({
|
||||||
</div>
|
</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 ? (
|
{loading ? (
|
||||||
<div className="absolute inset-0 flex items-center justify-center">
|
<div className="absolute inset-0 flex items-center justify-center">
|
||||||
<div className="loading-spinner" />
|
<div className="loading-spinner" />
|
||||||
|
|
@ -588,7 +588,7 @@ export default function FileSelectionDialog({
|
||||||
</div>
|
</div>
|
||||||
</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" />
|
<FileText className="w-12 h-12 mb-2 opacity-20" />
|
||||||
<p className="font-mono text-sm">
|
<p className="font-mono text-sm">
|
||||||
{searchTerm || filterType
|
{searchTerm || filterType
|
||||||
|
|
@ -600,8 +600,8 @@ export default function FileSelectionDialog({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DialogFooter className="p-5 border-t border-gray-800 bg-gray-900/50 flex-shrink-0 flex justify-between">
|
<DialogFooter className="p-5 border-t border-border bg-muted flex-shrink-0 flex justify-between">
|
||||||
<div className="text-xs font-mono text-gray-600 flex items-center gap-2">
|
<div className="text-xs font-mono text-muted-foreground flex items-center gap-2">
|
||||||
<Terminal className="w-3 h-3" />
|
<Terminal className="w-3 h-3" />
|
||||||
提示:点击文件夹可展开/折叠,点击文件夹复选框可批量选择
|
提示:点击文件夹可展开/折叠,点击文件夹复选框可批量选择
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -425,7 +425,7 @@ export default function TerminalProgressDialog({
|
||||||
case "warning":
|
case "warning":
|
||||||
return "text-amber-400";
|
return "text-amber-400";
|
||||||
default:
|
default:
|
||||||
return "text-gray-400";
|
return "text-muted-foreground";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -462,13 +462,13 @@ export default function TerminalProgressDialog({
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Header */}
|
{/* 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%)" }}>
|
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">
|
<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))" }} />
|
<Terminal className="w-5 h-5 text-primary" style={{ filter: "drop-shadow(0 0 8px rgba(255, 95, 31, 0.5))" }} />
|
||||||
<div>
|
<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-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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -501,16 +501,16 @@ export default function TerminalProgressDialog({
|
||||||
{/* Left Sidebar - Task Info */}
|
{/* Left Sidebar - Task Info */}
|
||||||
<div className="w-48 p-4 border-r border-[#1a2535] bg-[#060810] flex flex-col gap-4">
|
<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="space-y-1.5">
|
||||||
<div className="text-[9px] font-bold text-[#5a6577] uppercase tracking-[0.15em]">Task ID</div>
|
<div className="text-xs 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-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)" }}>
|
style={{ textShadow: "0 0 10px rgba(255, 95, 31, 0.3)" }}>
|
||||||
{taskId?.slice(0, 8)}...
|
{taskId?.slice(0, 8)}...
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
<div className="text-[9px] font-bold text-[#5a6577] uppercase tracking-[0.15em]">Type</div>
|
<div className="text-xs 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="flex items-center gap-2 cyber-bg-elevated p-2.5 rounded border border-[#1a2535]">
|
||||||
{taskType === 'repository'
|
{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))" }} />
|
? <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))" }} />}
|
: <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 */}
|
{/* Status Badge */}
|
||||||
<div className="space-y-2">
|
<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 ? (
|
{isCancelled ? (
|
||||||
<Badge className="w-full justify-center cyber-badge-warning">CANCELLED</Badge>
|
<Badge className="w-full justify-center cyber-badge-warning">CANCELLED</Badge>
|
||||||
) : isCompleted ? (
|
) : isCompleted ? (
|
||||||
|
|
@ -566,7 +566,7 @@ export default function TerminalProgressDialog({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Bottom Controls */}
|
{/* 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">
|
<div className="flex items-center gap-2 text-xs text-[#6a7587] font-mono tracking-wide">
|
||||||
<Activity className="w-3.5 h-3.5" />
|
<Activity className="w-3.5 h-3.5" />
|
||||||
<span>
|
<span>
|
||||||
|
|
@ -580,7 +580,7 @@ export default function TerminalProgressDialog({
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={handleCancel}
|
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" />
|
<AlertTriangle className="w-3 h-3 mr-1.5" />
|
||||||
取消任务
|
取消任务
|
||||||
|
|
@ -592,7 +592,7 @@ export default function TerminalProgressDialog({
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => window.open('/logs', '_blank')}
|
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" />
|
<Activity className="w-3 h-3 mr-1.5" />
|
||||||
查看日志
|
查看日志
|
||||||
|
|
@ -603,7 +603,7 @@ export default function TerminalProgressDialog({
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => onOpenChange(false)}
|
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" />
|
<CheckCircle2 className="w-3 h-3 mr-1.5" />
|
||||||
确认
|
确认
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ export default function AdvancedOptions({
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<Label className="text-base font-bold uppercase">扫描配置</Label>
|
<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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -83,11 +83,11 @@ export default function AdvancedOptions({
|
||||||
>
|
>
|
||||||
<SelectTrigger
|
<SelectTrigger
|
||||||
id="analysis_depth"
|
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 />
|
<SelectValue />
|
||||||
</SelectTrigger>
|
</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 value="basic" className="font-mono">
|
||||||
基础 (快速)
|
基础 (快速)
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
|
|
@ -104,16 +104,16 @@ export default function AdvancedOptions({
|
||||||
</div>
|
</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>
|
<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>
|
<div>
|
||||||
<p className="text-sm font-bold uppercase">
|
<p className="text-sm font-bold uppercase">
|
||||||
{hasSelectedFiles
|
{hasSelectedFiles
|
||||||
? `已选择 ${scanConfig.file_paths!.length} 个文件`
|
? `已选择 ${scanConfig.file_paths!.length} 个文件`
|
||||||
: "全量扫描 (所有文件)"}
|
: "全量扫描 (所有文件)"}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-gray-500 font-bold">
|
<p className="text-xs text-muted-foreground font-bold">
|
||||||
{hasSelectedFiles
|
{hasSelectedFiles
|
||||||
? "仅分析选中的文件"
|
? "仅分析选中的文件"
|
||||||
: "分析项目中的所有代码文件"}
|
: "分析项目中的所有代码文件"}
|
||||||
|
|
@ -126,7 +126,7 @@ export default function AdvancedOptions({
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => onUpdate({ file_paths: undefined })}
|
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>
|
</Button>
|
||||||
|
|
@ -136,7 +136,7 @@ export default function AdvancedOptions({
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={onOpenFileSelection}
|
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 ? "修改选择" : "选择文件"}
|
{hasSelectedFiles ? "修改选择" : "选择文件"}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -162,15 +162,15 @@ function CheckboxOption({
|
||||||
description: string;
|
description: string;
|
||||||
}) {
|
}) {
|
||||||
return (
|
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
|
<Checkbox
|
||||||
checked={checked}
|
checked={checked}
|
||||||
onCheckedChange={(c) => onChange(!!c)}
|
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>
|
<div>
|
||||||
<p className="text-sm font-bold uppercase">{label}</p>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -178,7 +178,7 @@ function CheckboxOption({
|
||||||
|
|
||||||
function DepthExplanation() {
|
function DepthExplanation() {
|
||||||
return (
|
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">
|
<div className="flex items-start space-x-3">
|
||||||
<AlertCircle className="w-5 h-5 text-amber-600 mt-0.5" />
|
<AlertCircle className="w-5 h-5 text-amber-600 mt-0.5" />
|
||||||
<div className="text-sm font-mono">
|
<div className="text-sm font-mono">
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ export default function ExcludePatterns({
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<Label className="text-base font-bold uppercase">排除模式</Label>
|
<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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -46,16 +46,16 @@ export default function ExcludePatterns({
|
||||||
{COMMON_EXCLUDE_PATTERNS.map((pattern) => (
|
{COMMON_EXCLUDE_PATTERNS.map((pattern) => (
|
||||||
<div
|
<div
|
||||||
key={pattern.value}
|
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
|
<Checkbox
|
||||||
checked={patterns.includes(pattern.value)}
|
checked={patterns.includes(pattern.value)}
|
||||||
onCheckedChange={() => onToggle(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">
|
<div className="flex-1">
|
||||||
<p className="text-sm font-bold uppercase">{pattern.label}</p>
|
<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}
|
{pattern.description}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -103,7 +103,7 @@ function CustomPatternInput({ onAdd }: { onAdd: (pattern: string) => void }) {
|
||||||
.previousElementSibling as HTMLInputElement;
|
.previousElementSibling as HTMLInputElement;
|
||||||
handleAdd(input);
|
handleAdd(input);
|
||||||
}}
|
}}
|
||||||
className="retro-btn bg-white text-black h-10"
|
className="retro-btn bg-background text-foreground h-10"
|
||||||
>
|
>
|
||||||
添加
|
添加
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -127,7 +127,7 @@ function SelectedPatterns({
|
||||||
<Badge
|
<Badge
|
||||||
key={pattern}
|
key={pattern}
|
||||||
variant="secondary"
|
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)}
|
onClick={() => onRemove(pattern)}
|
||||||
>
|
>
|
||||||
{pattern} ×
|
{pattern} ×
|
||||||
|
|
|
||||||
|
|
@ -39,14 +39,14 @@ export default function ProjectSelector({
|
||||||
</Label>
|
</Label>
|
||||||
<Badge
|
<Badge
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="text-xs rounded-none border-black font-mono"
|
className="text-xs rounded-none border-border font-mono"
|
||||||
>
|
>
|
||||||
{filteredProjects.length} 个可用项目
|
{filteredProjects.length} 个可用项目
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="relative">
|
<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
|
<Input
|
||||||
placeholder="搜索项目名称..."
|
placeholder="搜索项目名称..."
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
|
|
@ -91,7 +91,7 @@ function ProjectCard({
|
||||||
className={`cursor-pointer transition-all border-2 p-4 relative ${
|
className={`cursor-pointer transition-all border-2 p-4 relative ${
|
||||||
isSelected
|
isSelected
|
||||||
? "border-primary bg-blue-50 shadow-[4px_4px_0px_0px_rgba(37,99,235,1)] translate-x-[-2px] translate-y-[-2px]"
|
? "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}
|
onClick={onSelect}
|
||||||
>
|
>
|
||||||
|
|
@ -101,11 +101,11 @@ function ProjectCard({
|
||||||
{project.name}
|
{project.name}
|
||||||
</h4>
|
</h4>
|
||||||
{project.description && (
|
{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}
|
{project.description}
|
||||||
</p>
|
</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
|
<span
|
||||||
className={`px-1.5 py-0.5 ${isRepo ? "bg-blue-100 text-blue-700" : "bg-amber-100 text-amber-700"}`}
|
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>
|
||||||
</div>
|
</div>
|
||||||
{isSelected && (
|
{isSelected && (
|
||||||
<div className="w-5 h-5 bg-primary border-2 border-black flex items-center justify-center">
|
<div className="w-5 h-5 bg-primary border-2 border-border flex items-center justify-center">
|
||||||
<div className="w-2 h-2 bg-white" />
|
<div className="w-2 h-2 bg-background" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -141,7 +141,7 @@ function LoadingSpinner() {
|
||||||
|
|
||||||
function EmptyState({ hasSearch }: { hasSearch: boolean }) {
|
function EmptyState({ hasSearch }: { hasSearch: boolean }) {
|
||||||
return (
|
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" />
|
<FileText className="w-8 h-8 mx-auto mb-2 opacity-50" />
|
||||||
<p className="text-sm">
|
<p className="text-sm">
|
||||||
{hasSearch ? "未找到匹配的项目" : "暂无可用项目"}
|
{hasSearch ? "未找到匹配的项目" : "暂无可用项目"}
|
||||||
|
|
|
||||||
|
|
@ -208,7 +208,7 @@ export function DatabaseManager() {
|
||||||
<div className="cyber-card p-0">
|
<div className="cyber-card p-0">
|
||||||
<div className="cyber-card-header">
|
<div className="cyber-card-header">
|
||||||
<Activity className="w-5 h-5 text-emerald-400" />
|
<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">
|
<div className="ml-auto">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
|
@ -238,16 +238,16 @@ export function DatabaseManager() {
|
||||||
<AlertCircle className="h-5 w-5 text-rose-400" />
|
<AlertCircle className="h-5 w-5 text-rose-400" />
|
||||||
)}
|
)}
|
||||||
<div className="flex items-center gap-2">
|
<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)}
|
{getHealthStatusBadge(health.status)}
|
||||||
</div>
|
</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'}>
|
<span className={health.database_connected ? 'text-emerald-400' : 'text-rose-400'}>
|
||||||
{health.database_connected ? '正常' : '异常'}
|
{health.database_connected ? '正常' : '异常'}
|
||||||
</span>
|
</span>
|
||||||
<span className="mx-2">|</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>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -292,7 +292,7 @@ export function DatabaseManager() {
|
||||||
<div className="cyber-card p-0">
|
<div className="cyber-card p-0">
|
||||||
<div className="cyber-card-header">
|
<div className="cyber-card-header">
|
||||||
<Database className="w-5 h-5 text-violet-400" />
|
<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">
|
<div className="ml-auto">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
|
@ -321,17 +321,17 @@ export function DatabaseManager() {
|
||||||
<div className="cyber-card p-4">
|
<div className="cyber-card p-4">
|
||||||
<p className="stat-label">任务</p>
|
<p className="stat-label">任务</p>
|
||||||
<p className="stat-value text-emerald-400">{stats.total_tasks}</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>
|
||||||
<div className="cyber-card p-4">
|
<div className="cyber-card p-4">
|
||||||
<p className="stat-label">问题</p>
|
<p className="stat-label">问题</p>
|
||||||
<p className="stat-value text-amber-400">{stats.total_issues}</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>
|
||||||
<div className="cyber-card p-4">
|
<div className="cyber-card p-4">
|
||||||
<p className="stat-label">分析记录</p>
|
<p className="stat-label">分析记录</p>
|
||||||
<p className="stat-value text-violet-400">{stats.total_analyses}</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>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -347,7 +347,7 @@ export function DatabaseManager() {
|
||||||
<div className="cyber-card p-0">
|
<div className="cyber-card p-0">
|
||||||
<div className="cyber-card-header">
|
<div className="cyber-card-header">
|
||||||
<Database className="w-5 h-5 text-primary" />
|
<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>
|
||||||
<div className="p-6 space-y-6">
|
<div className="p-6 space-y-6">
|
||||||
{message && (
|
{message && (
|
||||||
|
|
@ -369,11 +369,11 @@ export function DatabaseManager() {
|
||||||
|
|
||||||
<div className="grid gap-6 md:grid-cols-3">
|
<div className="grid gap-6 md:grid-cols-3">
|
||||||
<div className="space-y-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" />
|
<Download className="h-4 w-4 text-sky-400" />
|
||||||
导出数据
|
导出数据
|
||||||
</h4>
|
</h4>
|
||||||
<p className="text-xs text-gray-500">将数据导出为 JSON 文件,用于备份或迁移</p>
|
<p className="text-xs text-muted-foreground">将数据导出为 JSON 文件,用于备份或迁移</p>
|
||||||
<Button
|
<Button
|
||||||
onClick={handleExport}
|
onClick={handleExport}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
|
|
@ -385,11 +385,11 @@ export function DatabaseManager() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-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">
|
||||||
<Upload className="h-4 w-4 text-emerald-400" />
|
<Upload className="h-4 w-4 text-emerald-400" />
|
||||||
导入数据
|
导入数据
|
||||||
</h4>
|
</h4>
|
||||||
<p className="text-xs text-gray-500">从 JSON 文件恢复数据(最大 50MB)</p>
|
<p className="text-xs text-muted-foreground">从 JSON 文件恢复数据(最大 50MB)</p>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => document.getElementById('import-file')?.click()}
|
onClick={() => document.getElementById('import-file')?.click()}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
|
|
@ -412,7 +412,7 @@ export function DatabaseManager() {
|
||||||
<Trash2 className="h-4 w-4" />
|
<Trash2 className="h-4 w-4" />
|
||||||
清空数据
|
清空数据
|
||||||
</h4>
|
</h4>
|
||||||
<p className="text-xs text-gray-500">删除所有数据(不可恢复)</p>
|
<p className="text-xs text-muted-foreground">删除所有数据(不可恢复)</p>
|
||||||
<Button
|
<Button
|
||||||
onClick={handleClear}
|
onClick={handleClear}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
|
|
@ -424,7 +424,7 @@ export function DatabaseManager() {
|
||||||
</div>
|
</div>
|
||||||
</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">
|
<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" />
|
<Info className="h-5 w-5 text-sky-400 mt-0.5 flex-shrink-0" />
|
||||||
<p className="text-sm text-sky-300/80">
|
<p className="text-sm text-sky-300/80">
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,7 @@ export default function DatabaseTest() {
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-4">
|
<CardContent className="space-y-4">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<p className="text-sm text-gray-600">
|
<p className="text-sm text-muted-foreground">
|
||||||
测试数据库连接状态和基本功能
|
测试数据库连接状态和基本功能
|
||||||
</p>
|
</p>
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -188,7 +188,7 @@ export default function DatabaseTest() {
|
||||||
{getStatusIcon(result.status)}
|
{getStatusIcon(result.status)}
|
||||||
<div>
|
<div>
|
||||||
<p className="font-medium text-sm">{result.name}</p>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
{getStatusBadge(result.status)}
|
{getStatusBadge(result.status)}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Link, useLocation } from "react-router-dom";
|
import { Link, useLocation } from "react-router-dom";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { ThemeToggle } from "@/components/ui/theme-toggle";
|
||||||
import {
|
import {
|
||||||
Menu,
|
Menu,
|
||||||
X,
|
X,
|
||||||
|
|
@ -22,22 +23,21 @@ import {
|
||||||
Shield,
|
Shield,
|
||||||
MessageSquare,
|
MessageSquare,
|
||||||
Bot,
|
Bot,
|
||||||
Terminal
|
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import routes from "@/app/routes";
|
import routes from "@/app/routes";
|
||||||
import { version } from "../../../package.json";
|
import { version } from "../../../package.json";
|
||||||
|
|
||||||
// Icon mapping for routes
|
// Icon mapping for routes
|
||||||
const routeIcons: Record<string, React.ReactNode> = {
|
const routeIcons: Record<string, React.ReactNode> = {
|
||||||
"/": <Bot className="w-5 h-5" />,
|
"/": <Bot className="w-6 h-6" />,
|
||||||
"/dashboard": <LayoutDashboard className="w-5 h-5" />,
|
"/dashboard": <LayoutDashboard className="w-6 h-6" />,
|
||||||
"/projects": <FolderGit2 className="w-5 h-5" />,
|
"/projects": <FolderGit2 className="w-6 h-6" />,
|
||||||
"/instant-analysis": <Zap className="w-5 h-5" />,
|
"/instant-analysis": <Zap className="w-6 h-6" />,
|
||||||
"/audit-tasks": <ListTodo className="w-5 h-5" />,
|
"/audit-tasks": <ListTodo className="w-6 h-6" />,
|
||||||
"/audit-rules": <Shield className="w-5 h-5" />,
|
"/audit-rules": <Shield className="w-6 h-6" />,
|
||||||
"/prompts": <MessageSquare className="w-5 h-5" />,
|
"/prompts": <MessageSquare className="w-6 h-6" />,
|
||||||
"/admin": <Settings className="w-5 h-5" />,
|
"/admin": <Settings className="w-6 h-6" />,
|
||||||
"/recycle-bin": <Trash2 className="w-5 h-5" />,
|
"/recycle-bin": <Trash2 className="w-6 h-6" />,
|
||||||
};
|
};
|
||||||
|
|
||||||
interface SidebarProps {
|
interface SidebarProps {
|
||||||
|
|
@ -57,7 +57,12 @@ export default function Sidebar({ collapsed, setCollapsed }: SidebarProps) {
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
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)}
|
onClick={() => setMobileOpen(!mobileOpen)}
|
||||||
>
|
>
|
||||||
{mobileOpen ? <X className="w-5 h-5" /> : <Menu className="w-5 h-5" />}
|
{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 */}
|
{/* Sidebar */}
|
||||||
<aside
|
<aside
|
||||||
className={`
|
className={`
|
||||||
fixed top-0 left-0 h-screen
|
fixed top-0 left-0 h-screen z-40 transition-all duration-300 ease-in-out
|
||||||
bg-[#0a0a0f] border-r border-gray-800/60
|
|
||||||
z-40 transition-all duration-300 ease-in-out
|
|
||||||
${collapsed ? "w-20" : "w-64"}
|
${collapsed ? "w-20" : "w-64"}
|
||||||
${mobileOpen ? "translate-x-0" : "-translate-x-full md:translate-x-0"}
|
${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">
|
<div className="flex flex-col h-full relative">
|
||||||
{/* Subtle grid background */}
|
{/* Subtle grid background */}
|
||||||
|
|
@ -87,30 +94,35 @@ export default function Sidebar({ collapsed, setCollapsed }: SidebarProps) {
|
||||||
className="absolute inset-0 opacity-30 pointer-events-none"
|
className="absolute inset-0 opacity-30 pointer-events-none"
|
||||||
style={{
|
style={{
|
||||||
backgroundImage: `
|
backgroundImage: `
|
||||||
linear-gradient(rgba(255,107,44,0.03) 1px, transparent 1px),
|
linear-gradient(var(--cyber-border-accent) 1px, transparent 1px),
|
||||||
linear-gradient(90deg, rgba(255,107,44,0.03) 1px, transparent 1px)
|
linear-gradient(90deg, var(--cyber-border-accent) 1px, transparent 1px)
|
||||||
`,
|
`,
|
||||||
backgroundSize: '24px 24px',
|
backgroundSize: '24px 24px',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Logo Section */}
|
{/* Logo Section */}
|
||||||
<div className={`
|
<div
|
||||||
relative flex items-center h-[72px]
|
className={`relative flex items-center h-[72px] ${collapsed ? 'px-3 justify-center' : 'px-4 pr-6'}`}
|
||||||
border-b border-gray-800/60 bg-[#0c0c12]
|
style={{
|
||||||
${collapsed ? 'px-3 justify-center' : 'px-4 pr-6'}
|
background: 'var(--cyber-bg-elevated)',
|
||||||
`}>
|
borderBottom: '1px solid var(--cyber-border)'
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Link
|
<Link
|
||||||
to="/"
|
to="/"
|
||||||
className={`
|
className={`flex items-center gap-3 group transition-all duration-300 ${collapsed ? 'justify-center' : 'flex-1 min-w-0'}`}
|
||||||
flex items-center gap-3 group transition-all duration-300
|
|
||||||
${collapsed ? 'justify-center' : 'flex-1 min-w-0'}
|
|
||||||
`}
|
|
||||||
onClick={() => setMobileOpen(false)}
|
onClick={() => setMobileOpen(false)}
|
||||||
>
|
>
|
||||||
{/* Logo Icon */}
|
{/* Logo Icon */}
|
||||||
<div className="relative flex-shrink-0">
|
<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
|
<img
|
||||||
src="/logo_deepaudit.png"
|
src="/logo_deepaudit.png"
|
||||||
alt="DeepAudit"
|
alt="DeepAudit"
|
||||||
|
|
@ -122,31 +134,27 @@ export default function Sidebar({ collapsed, setCollapsed }: SidebarProps) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Logo Text */}
|
{/* Logo Text */}
|
||||||
<div className={`
|
<div className={`transition-all duration-300 ${collapsed ? 'w-0 opacity-0 overflow-hidden' : 'flex-1 min-w-0 opacity-100'}`}>
|
||||||
transition-all duration-300
|
|
||||||
${collapsed ? 'w-0 opacity-0 overflow-hidden' : 'flex-1 min-w-0 opacity-100'}
|
|
||||||
`}>
|
|
||||||
<div
|
<div
|
||||||
className="text-xl font-bold tracking-wider font-mono"
|
className="text-xl font-bold tracking-wider font-mono"
|
||||||
style={{ textShadow: '0 0 20px rgba(255,107,44,0.3)' }}
|
style={{ textShadow: '0 0 20px rgba(255,107,44,0.3)' }}
|
||||||
>
|
>
|
||||||
<span className="text-primary">DEEP</span>
|
<span className="text-primary">DEEP</span>
|
||||||
<span className="text-white">AUDIT</span>
|
<span style={{ color: 'var(--cyber-text)' }}>AUDIT</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
{/* Collapse button */}
|
{/* Collapse button */}
|
||||||
<button
|
<button
|
||||||
className={`
|
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"
|
||||||
hidden md:flex absolute -right-3 top-1/2 -translate-y-1/2
|
style={{
|
||||||
w-6 h-6 bg-[#0c0c12] border border-gray-700 rounded
|
background: 'var(--cyber-bg)',
|
||||||
items-center justify-center text-gray-500
|
border: '1px solid var(--cyber-border)',
|
||||||
hover:bg-primary hover:border-primary hover:text-white
|
color: 'var(--cyber-text-muted)',
|
||||||
transition-all duration-200
|
zIndex: 100
|
||||||
`}
|
}}
|
||||||
onClick={() => setCollapsed(!collapsed)}
|
onClick={() => setCollapsed(!collapsed)}
|
||||||
style={{ zIndex: 100 }}
|
|
||||||
>
|
>
|
||||||
{collapsed ? (
|
{collapsed ? (
|
||||||
<ChevronRight className="w-3 h-3" />
|
<ChevronRight className="w-3 h-3" />
|
||||||
|
|
@ -165,16 +173,26 @@ export default function Sidebar({ collapsed, setCollapsed }: SidebarProps) {
|
||||||
<Link
|
<Link
|
||||||
key={route.path}
|
key={route.path}
|
||||||
to={route.path}
|
to={route.path}
|
||||||
className={`
|
className="flex items-center gap-3 px-3 py-2.5 transition-all duration-200 group relative rounded-lg"
|
||||||
flex items-center gap-3 px-3 py-2.5
|
style={{
|
||||||
transition-all duration-200 group relative rounded-lg
|
background: isActive ? 'hsl(var(--primary) / 0.15)' : 'transparent',
|
||||||
${isActive
|
border: isActive ? '1px solid hsl(var(--primary) / 0.3)' : '1px solid transparent',
|
||||||
? "bg-primary/15 text-primary border border-primary/30"
|
color: isActive ? 'hsl(var(--primary))' : 'var(--cyber-text-muted)'
|
||||||
: "text-gray-400 hover:text-gray-200 hover:bg-gray-800/50 border border-transparent"
|
}}
|
||||||
}
|
|
||||||
`}
|
|
||||||
onClick={() => setMobileOpen(false)}
|
onClick={() => setMobileOpen(false)}
|
||||||
title={collapsed ? route.name : undefined}
|
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 */}
|
{/* Active indicator */}
|
||||||
{isActive && (
|
{isActive && (
|
||||||
|
|
@ -182,19 +200,13 @@ export default function Sidebar({ collapsed, setCollapsed }: SidebarProps) {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Icon */}
|
{/* Icon */}
|
||||||
<span className={`
|
<span className="flex-shrink-0 transition-colors duration-200">
|
||||||
flex-shrink-0 transition-colors duration-200
|
{routeIcons[route.path] || <LayoutDashboard className="w-6 h-6" />}
|
||||||
${isActive ? "text-primary" : "text-gray-500 group-hover:text-gray-300"}
|
|
||||||
`}>
|
|
||||||
{routeIcons[route.path] || <LayoutDashboard className="w-5 h-5" />}
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{/* Label */}
|
{/* Label */}
|
||||||
{!collapsed && (
|
{!collapsed && (
|
||||||
<span className={`
|
<span className={`font-mono text-base tracking-wide ${isActive ? 'font-semibold' : 'font-medium'}`}>
|
||||||
font-mono text-sm tracking-wide
|
|
||||||
${isActive ? 'font-semibold' : 'font-medium'}
|
|
||||||
`}>
|
|
||||||
{route.name}
|
{route.name}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
@ -212,26 +224,31 @@ export default function Sidebar({ collapsed, setCollapsed }: SidebarProps) {
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{/* Footer */}
|
{/* 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 */}
|
{/* Account Link */}
|
||||||
<Link
|
<Link
|
||||||
to="/account"
|
to="/account"
|
||||||
className={`
|
className="flex items-center gap-3 px-3 py-2.5 rounded-lg transition-all duration-200 group"
|
||||||
flex items-center gap-3 px-3 py-2.5 rounded-lg
|
style={{
|
||||||
transition-all duration-200 group
|
background: location.pathname === '/account' ? 'hsl(var(--primary) / 0.15)' : 'transparent',
|
||||||
${location.pathname === '/account'
|
border: location.pathname === '/account' ? '1px solid hsl(var(--primary) / 0.3)' : '1px solid transparent',
|
||||||
? "bg-primary/15 text-primary border border-primary/30"
|
color: location.pathname === '/account' ? 'hsl(var(--primary))' : 'var(--cyber-text-muted)'
|
||||||
: "text-gray-400 hover:text-gray-200 hover:bg-gray-800/50 border border-transparent"
|
}}
|
||||||
}
|
|
||||||
`}
|
|
||||||
onClick={() => setMobileOpen(false)}
|
onClick={() => setMobileOpen(false)}
|
||||||
title={collapsed ? "账号管理" : undefined}
|
title={collapsed ? "账号管理" : undefined}
|
||||||
>
|
>
|
||||||
<UserCircle className={`w-5 h-5 flex-shrink-0 ${
|
<UserCircle className="w-6 h-6 flex-shrink-0" />
|
||||||
location.pathname === '/account' ? 'text-primary' : 'text-gray-500 group-hover:text-gray-300'
|
|
||||||
}`} />
|
|
||||||
{!collapsed && (
|
{!collapsed && (
|
||||||
<span className="font-mono text-sm">账号管理</span>
|
<span className="font-mono text-base">账号管理</span>
|
||||||
)}
|
)}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
|
@ -240,29 +257,31 @@ export default function Sidebar({ collapsed, setCollapsed }: SidebarProps) {
|
||||||
href="https://github.com/lintsinghua/DeepAudit"
|
href="https://github.com/lintsinghua/DeepAudit"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className={`
|
className="flex items-center gap-3 px-3 py-2.5 rounded-lg transition-all duration-200 group"
|
||||||
flex items-center gap-3 px-3 py-2.5 rounded-lg
|
style={{ color: 'var(--cyber-text-muted)' }}
|
||||||
text-gray-400 hover:text-gray-200 hover:bg-gray-800/50
|
|
||||||
transition-all duration-200 group border border-transparent
|
|
||||||
`}
|
|
||||||
title={collapsed ? "GitHub" : undefined}
|
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 && (
|
{!collapsed && (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<span className="font-mono text-sm">GitHub</span>
|
<span className="font-mono text-base">GitHub</span>
|
||||||
<span className="text-[10px] text-gray-600 font-mono">v{version}</span>
|
<span className="text-sm font-mono" style={{ color: 'var(--cyber-text-muted)' }}>v{version}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
{/* System Status */}
|
{/* System Status */}
|
||||||
{!collapsed && (
|
{!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="flex items-center gap-2 px-3 py-2">
|
||||||
<div className="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"
|
<div
|
||||||
style={{ boxShadow: '0 0 8px rgba(52, 211, 153, 0.5)' }} />
|
className="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"
|
||||||
<span className="text-[10px] text-gray-600 font-mono uppercase tracking-wider">
|
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
|
System Online
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -62,13 +62,13 @@ export default function ExportReportDialog({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<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>
|
<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" />
|
<Download className="w-5 h-5 text-primary" />
|
||||||
导出审计报告
|
导出审计报告
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogDescription className="text-gray-400 font-mono text-xs">
|
<DialogDescription className="text-muted-foreground font-mono text-xs">
|
||||||
选择报告格式并导出完整的代码审计结果
|
选择报告格式并导出完整的代码审计结果
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
@ -79,57 +79,57 @@ export default function ExportReportDialog({
|
||||||
onValueChange={(value) => setSelectedFormat(value as ExportFormat)}
|
onValueChange={(value) => setSelectedFormat(value as ExportFormat)}
|
||||||
className="space-y-4"
|
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" />
|
<RadioGroupItem value="json" id="json" />
|
||||||
<Label htmlFor="json" className="flex items-center gap-3 cursor-pointer flex-1">
|
<Label htmlFor="json" className="flex items-center gap-3 cursor-pointer flex-1">
|
||||||
<FileJson className="w-5 h-5 text-amber-400" />
|
<FileJson className="w-5 h-5 text-amber-400" />
|
||||||
<div>
|
<div>
|
||||||
<div className="font-bold text-gray-200">JSON 格式</div>
|
<div className="font-bold text-foreground">JSON 格式</div>
|
||||||
<div className="text-xs text-gray-500">结构化数据,适合程序处理和集成</div>
|
<div className="text-xs text-muted-foreground">结构化数据,适合程序处理和集成</div>
|
||||||
</div>
|
</div>
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</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" />
|
<RadioGroupItem value="pdf" id="pdf" />
|
||||||
<Label htmlFor="pdf" className="flex items-center gap-3 cursor-pointer flex-1">
|
<Label htmlFor="pdf" className="flex items-center gap-3 cursor-pointer flex-1">
|
||||||
<FileText className="w-5 h-5 text-rose-400" />
|
<FileText className="w-5 h-5 text-rose-400" />
|
||||||
<div>
|
<div>
|
||||||
<div className="font-bold text-gray-200">PDF 格式</div>
|
<div className="font-bold text-foreground">PDF 格式</div>
|
||||||
<div className="text-xs text-gray-500">专业报告,适合打印和分享</div>
|
<div className="text-xs text-muted-foreground">专业报告,适合打印和分享</div>
|
||||||
</div>
|
</div>
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
|
|
||||||
{/* 报告预览信息 */}
|
{/* 报告预览信息 */}
|
||||||
<div className="mt-6 border border-gray-700 rounded bg-gray-900/30">
|
<div className="mt-6 border border-border rounded bg-muted/50">
|
||||||
<div className="px-4 py-2 border-b border-gray-800 bg-gray-900/50 flex items-center gap-2">
|
<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" />
|
<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>
|
||||||
<div className="p-4 grid grid-cols-2 gap-3 text-xs font-mono">
|
<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">
|
<div className="flex items-center justify-between border-b border-border pb-2">
|
||||||
<span className="text-gray-600">项目名称:</span>
|
<span className="text-muted-foreground">项目名称:</span>
|
||||||
<span className="font-bold text-white">{task.project?.name || "未知"}</span>
|
<span className="font-bold text-foreground">{task.project?.name || "未知"}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between border-b border-gray-800 pb-2">
|
<div className="flex items-center justify-between border-b border-border pb-2">
|
||||||
<span className="text-gray-600">质量评分:</span>
|
<span className="text-muted-foreground">质量评分:</span>
|
||||||
<span className="font-bold text-emerald-400">{task.quality_score.toFixed(1)}/100</span>
|
<span className="font-bold text-emerald-400">{task.quality_score.toFixed(1)}/100</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between border-b border-gray-800 pb-2">
|
<div className="flex items-center justify-between border-b border-border pb-2">
|
||||||
<span className="text-gray-600">扫描文件:</span>
|
<span className="text-muted-foreground">扫描文件:</span>
|
||||||
<span className="font-bold text-white">{task.scanned_files}/{task.total_files}</span>
|
<span className="font-bold text-foreground">{task.scanned_files}/{task.total_files}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between border-b border-gray-800 pb-2">
|
<div className="flex items-center justify-between border-b border-border pb-2">
|
||||||
<span className="text-gray-600">发现问题:</span>
|
<span className="text-muted-foreground">发现问题:</span>
|
||||||
<span className="font-bold text-amber-400">{issues.length}</span>
|
<span className="font-bold text-amber-400">{issues.length}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between border-b border-gray-800 pb-2">
|
<div className="flex items-center justify-between border-b border-border pb-2">
|
||||||
<span className="text-gray-600">代码行数:</span>
|
<span className="text-muted-foreground">代码行数:</span>
|
||||||
<span className="font-bold text-white">{task.total_lines.toLocaleString()}</span>
|
<span className="font-bold text-foreground">{task.total_lines.toLocaleString()}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between border-b border-gray-800 pb-2">
|
<div className="flex items-center justify-between border-b border-border pb-2">
|
||||||
<span className="text-gray-600">严重问题:</span>
|
<span className="text-muted-foreground">严重问题:</span>
|
||||||
<span className="font-bold text-rose-400">
|
<span className="font-bold text-rose-400">
|
||||||
{issues.filter(i => i.severity === "critical").length}
|
{issues.filter(i => i.severity === "critical").length}
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -138,7 +138,7 @@ export default function ExportReportDialog({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DialogFooter className="border-t border-gray-800 pt-4">
|
<DialogFooter className="border-t border-border pt-4">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => onOpenChange(false)}
|
onClick={() => onOpenChange(false)}
|
||||||
|
|
|
||||||
|
|
@ -72,13 +72,13 @@ export default function InstantExportDialog({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<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>
|
<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" />
|
<Download className="w-5 h-5 text-primary" />
|
||||||
导出分析报告
|
导出分析报告
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogDescription className="text-gray-400 font-mono text-xs">
|
<DialogDescription className="text-muted-foreground font-mono text-xs">
|
||||||
选择报告格式并导出代码分析结果
|
选择报告格式并导出代码分析结果
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
@ -89,23 +89,23 @@ export default function InstantExportDialog({
|
||||||
onValueChange={(value) => setSelectedFormat(value as ExportFormat)}
|
onValueChange={(value) => setSelectedFormat(value as ExportFormat)}
|
||||||
className="space-y-4"
|
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" />
|
<RadioGroupItem value="json" id="json" />
|
||||||
<Label htmlFor="json" className="flex items-center gap-3 cursor-pointer flex-1">
|
<Label htmlFor="json" className="flex items-center gap-3 cursor-pointer flex-1">
|
||||||
<FileJson className="w-5 h-5 text-amber-400" />
|
<FileJson className="w-5 h-5 text-amber-400" />
|
||||||
<div>
|
<div>
|
||||||
<div className="font-bold text-gray-200">JSON 格式</div>
|
<div className="font-bold text-foreground">JSON 格式</div>
|
||||||
<div className="text-xs text-gray-500">结构化数据,适合程序处理和集成</div>
|
<div className="text-xs text-muted-foreground">结构化数据,适合程序处理和集成</div>
|
||||||
</div>
|
</div>
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</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} />
|
<RadioGroupItem value="pdf" id="pdf" disabled={isPdfDisabled} />
|
||||||
<Label htmlFor="pdf" className={`flex items-center gap-3 flex-1 ${isPdfDisabled ? 'cursor-not-allowed' : 'cursor-pointer'}`}>
|
<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" />
|
<FileText className="w-5 h-5 text-rose-400" />
|
||||||
<div>
|
<div>
|
||||||
<div className="font-bold text-gray-200">PDF 格式</div>
|
<div className="font-bold text-foreground">PDF 格式</div>
|
||||||
<div className="text-xs text-gray-500">
|
<div className="text-xs text-muted-foreground">
|
||||||
{isPdfDisabled && <AlertTriangle className="w-3 h-3 inline mr-1 text-amber-500" />}
|
{isPdfDisabled && <AlertTriangle className="w-3 h-3 inline mr-1 text-amber-500" />}
|
||||||
{isPdfDisabled ? "需要先保存到历史记录" : "专业报告,适合打印和分享"}
|
{isPdfDisabled ? "需要先保存到历史记录" : "专业报告,适合打印和分享"}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -115,33 +115,33 @@ export default function InstantExportDialog({
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
|
|
||||||
{/* 报告预览信息 */}
|
{/* 报告预览信息 */}
|
||||||
<div className="mt-6 border border-gray-700 rounded bg-gray-900/30">
|
<div className="mt-6 border border-border rounded bg-muted/50">
|
||||||
<div className="px-4 py-2 border-b border-gray-800 bg-gray-900/50 flex items-center gap-2">
|
<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" />
|
<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>
|
||||||
<div className="p-4 grid grid-cols-2 gap-3 text-xs font-mono">
|
<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">
|
<div className="flex items-center justify-between border-b border-border pb-2">
|
||||||
<span className="text-gray-600">编程语言:</span>
|
<span className="text-muted-foreground">编程语言:</span>
|
||||||
<span className="font-bold text-sky-400">{language.toUpperCase()}</span>
|
<span className="font-bold text-sky-400">{language.toUpperCase()}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between border-b border-gray-800 pb-2">
|
<div className="flex items-center justify-between border-b border-border pb-2">
|
||||||
<span className="text-gray-600">质量评分:</span>
|
<span className="text-muted-foreground">质量评分:</span>
|
||||||
<span className="font-bold text-emerald-400">{(analysisResult.quality_score ?? 0).toFixed(1)}/100</span>
|
<span className="font-bold text-emerald-400">{(analysisResult.quality_score ?? 0).toFixed(1)}/100</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between border-b border-gray-800 pb-2">
|
<div className="flex items-center justify-between border-b border-border pb-2">
|
||||||
<span className="text-gray-600">发现问题:</span>
|
<span className="text-muted-foreground">发现问题:</span>
|
||||||
<span className="font-bold text-amber-400">{analysisResult.issues?.length ?? 0}</span>
|
<span className="font-bold text-amber-400">{analysisResult.issues?.length ?? 0}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between border-b border-gray-800 pb-2">
|
<div className="flex items-center justify-between border-b border-border pb-2">
|
||||||
<span className="text-gray-600">分析耗时:</span>
|
<span className="text-muted-foreground">分析耗时:</span>
|
||||||
<span className="font-bold text-white">{(analysisTime ?? 0).toFixed(2)}s</span>
|
<span className="font-bold text-foreground">{(analysisTime ?? 0).toFixed(2)}s</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DialogFooter className="border-t border-gray-800 pt-4">
|
<DialogFooter className="border-t border-border pt-4">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => onOpenChange(false)}
|
onClick={() => onOpenChange(false)}
|
||||||
|
|
|
||||||
|
|
@ -208,7 +208,7 @@ export function SystemConfig() {
|
||||||
<div className="flex items-center justify-center min-h-[400px]">
|
<div className="flex items-center justify-center min-h-[400px]">
|
||||||
<div className="text-center space-y-4">
|
<div className="text-center space-y-4">
|
||||||
<div className="loading-spinner mx-auto" />
|
<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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -250,17 +250,17 @@ export function SystemConfig() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Tabs defaultValue="llm" className="w-full">
|
<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">
|
<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-white font-mono font-bold uppercase py-2.5 text-gray-400 transition-all rounded text-xs flex items-center gap-2">
|
<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 配置
|
<Zap className="w-3 h-3" /> LLM 配置
|
||||||
</TabsTrigger>
|
</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" /> 嵌入模型
|
<Brain className="w-3 h-3" /> 嵌入模型
|
||||||
</TabsTrigger>
|
</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" /> 分析参数
|
<Settings className="w-3 h-3" /> 分析参数
|
||||||
</TabsTrigger>
|
</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 集成
|
<Globe className="w-3 h-3" /> Git 集成
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
@ -270,29 +270,29 @@ export function SystemConfig() {
|
||||||
<div className="cyber-card p-6 space-y-6">
|
<div className="cyber-card p-6 space-y-6">
|
||||||
{/* Provider Selection */}
|
{/* Provider Selection */}
|
||||||
<div className="space-y-2">
|
<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)}>
|
<Select value={config.llmProvider} onValueChange={(v) => updateConfig('llmProvider', v)}>
|
||||||
<SelectTrigger className="h-12 cyber-input">
|
<SelectTrigger className="h-12 cyber-input">
|
||||||
<SelectValue />
|
<SelectValue />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
<SelectContent className="cyber-dialog border-border">
|
||||||
<div className="px-2 py-1.5 text-xs font-bold text-gray-500 uppercase">LiteLLM 统一适配 (推荐)</div>
|
<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 => (
|
{LLM_PROVIDERS.filter(p => p.category === 'litellm').map(p => (
|
||||||
<SelectItem key={p.value} value={p.value} className="font-mono">
|
<SelectItem key={p.value} value={p.value} className="font-mono">
|
||||||
<span className="flex items-center gap-2">
|
<span className="flex items-center gap-2">
|
||||||
<span>{p.icon}</span>
|
<span>{p.icon}</span>
|
||||||
<span>{p.label}</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>
|
</span>
|
||||||
</SelectItem>
|
</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 => (
|
{LLM_PROVIDERS.filter(p => p.category === 'native').map(p => (
|
||||||
<SelectItem key={p.value} value={p.value} className="font-mono">
|
<SelectItem key={p.value} value={p.value} className="font-mono">
|
||||||
<span className="flex items-center gap-2">
|
<span className="flex items-center gap-2">
|
||||||
<span>{p.icon}</span>
|
<span>{p.icon}</span>
|
||||||
<span>{p.label}</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>
|
</span>
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
|
|
@ -303,7 +303,7 @@ export function SystemConfig() {
|
||||||
{/* API Key */}
|
{/* API Key */}
|
||||||
{config.llmProvider !== 'ollama' && (
|
{config.llmProvider !== 'ollama' && (
|
||||||
<div className="space-y-2">
|
<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">
|
<div className="flex gap-2">
|
||||||
<Input
|
<Input
|
||||||
type={showApiKey ? 'text' : 'password'}
|
type={showApiKey ? 'text' : 'password'}
|
||||||
|
|
@ -327,7 +327,7 @@ export function SystemConfig() {
|
||||||
{/* Model and Base URL */}
|
{/* Model and Base URL */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div className="space-y-2">
|
<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
|
<Input
|
||||||
value={config.llmModel}
|
value={config.llmModel}
|
||||||
onChange={(e) => updateConfig('llmModel', e.target.value)}
|
onChange={(e) => updateConfig('llmModel', e.target.value)}
|
||||||
|
|
@ -336,7 +336,7 @@ export function SystemConfig() {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<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
|
<Input
|
||||||
value={config.llmBaseUrl}
|
value={config.llmBaseUrl}
|
||||||
onChange={(e) => updateConfig('llmBaseUrl', e.target.value)}
|
onChange={(e) => updateConfig('llmBaseUrl', e.target.value)}
|
||||||
|
|
@ -347,10 +347,10 @@ export function SystemConfig() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Test Connection */}
|
{/* 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">
|
<div className="text-sm">
|
||||||
<span className="font-bold text-gray-300">测试连接</span>
|
<span className="font-bold text-foreground">测试连接</span>
|
||||||
<span className="text-gray-500 ml-2">验证配置是否正确</span>
|
<span className="text-muted-foreground ml-2">验证配置是否正确</span>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
onClick={testLLMConnection}
|
onClick={testLLMConnection}
|
||||||
|
|
@ -386,11 +386,11 @@ export function SystemConfig() {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Advanced Parameters */}
|
{/* Advanced Parameters */}
|
||||||
<details className="pt-4 border-t border-gray-800 border-dashed">
|
<details className="pt-4 border-t border-border border-dashed">
|
||||||
<summary className="font-bold uppercase cursor-pointer hover:text-primary text-gray-400 text-sm">高级参数</summary>
|
<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="grid grid-cols-1 md:grid-cols-3 gap-4 mt-4">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label className="text-xs text-gray-500 uppercase">超时 (毫秒)</Label>
|
<Label className="text-xs text-muted-foreground uppercase">超时 (毫秒)</Label>
|
||||||
<Input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
value={config.llmTimeout}
|
value={config.llmTimeout}
|
||||||
|
|
@ -399,7 +399,7 @@ export function SystemConfig() {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<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
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
step="0.1"
|
step="0.1"
|
||||||
|
|
@ -411,7 +411,7 @@ export function SystemConfig() {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<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
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
value={config.llmMaxTokens}
|
value={config.llmMaxTokens}
|
||||||
|
|
@ -424,14 +424,14 @@ export function SystemConfig() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Usage Notes */}
|
{/* Usage Notes */}
|
||||||
<div className="bg-gray-900/50 border border-gray-800 p-4 rounded-lg text-xs space-y-2">
|
<div className="bg-muted border border-border p-4 rounded-lg text-xs space-y-2">
|
||||||
<p className="font-bold uppercase text-gray-400 flex items-center gap-2">
|
<p className="font-bold uppercase text-muted-foreground flex items-center gap-2">
|
||||||
<Info className="w-4 h-4 text-sky-400" />
|
<Info className="w-4 h-4 text-sky-400" />
|
||||||
配置说明
|
配置说明
|
||||||
</p>
|
</p>
|
||||||
<p className="text-gray-500">• <strong className="text-gray-400">LiteLLM 统一适配</strong>: 大多数提供商通过 LiteLLM 统一处理,支持自动重试和负载均衡</p>
|
<p className="text-muted-foreground">• <strong className="text-muted-foreground">LiteLLM 统一适配</strong>: 大多数提供商通过 LiteLLM 统一处理,支持自动重试和负载均衡</p>
|
||||||
<p className="text-gray-500">• <strong className="text-gray-400">原生适配器</strong>: 百度、MiniMax、豆包因 API 格式特殊,使用专用适配器</p>
|
<p className="text-muted-foreground">• <strong className="text-muted-foreground">原生适配器</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">API 中转站</strong>: 在 Base URL 填入中转站地址即可,API Key 填中转站提供的 Key</p>
|
||||||
</div>
|
</div>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
|
|
@ -445,47 +445,47 @@ export function SystemConfig() {
|
||||||
<div className="cyber-card p-6 space-y-6">
|
<div className="cyber-card p-6 space-y-6">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
<div className="space-y-2">
|
<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
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
value={config.maxAnalyzeFiles}
|
value={config.maxAnalyzeFiles}
|
||||||
onChange={(e) => updateConfig('maxAnalyzeFiles', Number(e.target.value))}
|
onChange={(e) => updateConfig('maxAnalyzeFiles', Number(e.target.value))}
|
||||||
className="h-10 cyber-input"
|
className="h-10 cyber-input"
|
||||||
/>
|
/>
|
||||||
<p className="text-xs text-gray-600">单次任务最多处理的文件数量</p>
|
<p className="text-xs text-muted-foreground">单次任务最多处理的文件数量</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<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
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
value={config.llmConcurrency}
|
value={config.llmConcurrency}
|
||||||
onChange={(e) => updateConfig('llmConcurrency', Number(e.target.value))}
|
onChange={(e) => updateConfig('llmConcurrency', Number(e.target.value))}
|
||||||
className="h-10 cyber-input"
|
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>
|
||||||
<div className="space-y-2">
|
<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
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
value={config.llmGapMs}
|
value={config.llmGapMs}
|
||||||
onChange={(e) => updateConfig('llmGapMs', Number(e.target.value))}
|
onChange={(e) => updateConfig('llmGapMs', Number(e.target.value))}
|
||||||
className="h-10 cyber-input"
|
className="h-10 cyber-input"
|
||||||
/>
|
/>
|
||||||
<p className="text-xs text-gray-600">每个请求之间的延迟时间</p>
|
<p className="text-xs text-muted-foreground">每个请求之间的延迟时间</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<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)}>
|
<Select value={config.outputLanguage} onValueChange={(v) => updateConfig('outputLanguage', v)}>
|
||||||
<SelectTrigger className="h-10 cyber-input">
|
<SelectTrigger className="h-10 cyber-input">
|
||||||
<SelectValue />
|
<SelectValue />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
<SelectContent className="cyber-dialog border-border">
|
||||||
<SelectItem value="zh-CN" className="font-mono">🇨🇳 中文</SelectItem>
|
<SelectItem value="zh-CN" className="font-mono">🇨🇳 中文</SelectItem>
|
||||||
<SelectItem value="en-US" className="font-mono">🇺🇸 English</SelectItem>
|
<SelectItem value="en-US" className="font-mono">🇺🇸 English</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
<p className="text-xs text-gray-600">代码审查结果的输出语言</p>
|
<p className="text-xs text-muted-foreground">代码审查结果的输出语言</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -495,7 +495,7 @@ export function SystemConfig() {
|
||||||
<TabsContent value="git" className="space-y-6">
|
<TabsContent value="git" className="space-y-6">
|
||||||
<div className="cyber-card p-6 space-y-6">
|
<div className="cyber-card p-6 space-y-6">
|
||||||
<div className="space-y-2">
|
<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
|
<Input
|
||||||
type="password"
|
type="password"
|
||||||
value={config.githubToken}
|
value={config.githubToken}
|
||||||
|
|
@ -503,7 +503,7 @@ export function SystemConfig() {
|
||||||
placeholder="ghp_xxxxxxxxxxxx"
|
placeholder="ghp_xxxxxxxxxxxx"
|
||||||
className="h-10 cyber-input"
|
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">
|
<a href="https://github.com/settings/tokens" target="_blank" rel="noopener noreferrer" className="text-primary hover:underline">
|
||||||
github.com/settings/tokens
|
github.com/settings/tokens
|
||||||
|
|
@ -511,7 +511,7 @@ export function SystemConfig() {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<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
|
<Input
|
||||||
type="password"
|
type="password"
|
||||||
value={config.gitlabToken}
|
value={config.gitlabToken}
|
||||||
|
|
@ -519,20 +519,20 @@ export function SystemConfig() {
|
||||||
placeholder="glpat-xxxxxxxxxxxx"
|
placeholder="glpat-xxxxxxxxxxxx"
|
||||||
className="h-10 cyber-input"
|
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">
|
<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
|
gitlab.com/-/profile/personal_access_tokens
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-gray-900/50 border border-gray-800 p-4 rounded-lg text-xs">
|
<div className="bg-muted border border-border p-4 rounded-lg text-xs">
|
||||||
<p className="font-bold text-gray-400 flex items-center gap-2 mb-2">
|
<p className="font-bold text-muted-foreground flex items-center gap-2 mb-2">
|
||||||
<Info className="w-4 h-4 text-sky-400" />
|
<Info className="w-4 h-4 text-sky-400" />
|
||||||
提示
|
提示
|
||||||
</p>
|
</p>
|
||||||
<p className="text-gray-500">• 公开仓库无需配置 Token</p>
|
<p className="text-muted-foreground">• 公开仓库无需配置 Token</p>
|
||||||
<p className="text-gray-500">• 私有仓库需要配置对应平台的 Token</p>
|
<p className="text-muted-foreground">• 私有仓库需要配置对应平台的 Token</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
|
||||||
|
|
@ -33,13 +33,13 @@ function AccordionTrigger({
|
||||||
<AccordionPrimitive.Trigger
|
<AccordionPrimitive.Trigger
|
||||||
data-slot="accordion-trigger"
|
data-slot="accordion-trigger"
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{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.Trigger>
|
||||||
</AccordionPrimitive.Header>
|
</AccordionPrimitive.Header>
|
||||||
);
|
);
|
||||||
|
|
@ -53,7 +53,7 @@ function AccordionContent({
|
||||||
return (
|
return (
|
||||||
<AccordionPrimitive.Content
|
<AccordionPrimitive.Content
|
||||||
data-slot="accordion-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}
|
{...props}
|
||||||
>
|
>
|
||||||
<div className={cn("pt-0 pb-4", className)}>{children}</div>
|
<div className={cn("pt-0 pb-4", className)}>{children}</div>
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ function AlertDialogContent({
|
||||||
<AlertDialogPrimitive.Content
|
<AlertDialogPrimitive.Content
|
||||||
data-slot="alert-dialog-content"
|
data-slot="alert-dialog-content"
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
@ -68,7 +68,7 @@ function AlertDialogHeader({
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-slot="alert-dialog-header"
|
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}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
@ -82,7 +82,7 @@ function AlertDialogFooter({
|
||||||
<div
|
<div
|
||||||
data-slot="alert-dialog-footer"
|
data-slot="alert-dialog-footer"
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
@ -97,7 +97,7 @@ function AlertDialogTitle({
|
||||||
return (
|
return (
|
||||||
<AlertDialogPrimitive.Title
|
<AlertDialogPrimitive.Title
|
||||||
data-slot="alert-dialog-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}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
@ -110,7 +110,7 @@ function AlertDialogDescription({
|
||||||
return (
|
return (
|
||||||
<AlertDialogPrimitive.Description
|
<AlertDialogPrimitive.Description
|
||||||
data-slot="alert-dialog-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}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { cva, type VariantProps } from "class-variance-authority"
|
||||||
import { cn } from "@/shared/utils/utils"
|
import { cn } from "@/shared/utils/utils"
|
||||||
|
|
||||||
const alertVariants = cva(
|
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: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
|
|
@ -39,7 +39,7 @@ function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
<div
|
<div
|
||||||
data-slot="alert-title"
|
data-slot="alert-title"
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
@ -55,7 +55,7 @@ function AlertDescription({
|
||||||
<div
|
<div
|
||||||
data-slot="alert-description"
|
data-slot="alert-description"
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority";
|
||||||
import { cn } from "@/shared/utils/utils";
|
import { cn } from "@/shared/utils/utils";
|
||||||
|
|
||||||
const badgeVariants = cva(
|
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: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
|
|
@ -14,7 +14,7 @@ const badgeVariants = cva(
|
||||||
secondary:
|
secondary:
|
||||||
"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
|
"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
|
||||||
destructive:
|
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:
|
outline:
|
||||||
"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
"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";
|
import { cn } from "@/shared/utils/utils";
|
||||||
|
|
||||||
const buttonVariants = cva(
|
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: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
|
|
@ -21,10 +21,10 @@ const buttonVariants = cva(
|
||||||
link: "text-primary underline-offset-4 hover:underline",
|
link: "text-primary underline-offset-4 hover:underline",
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
default: "h-9 px-4 py-2",
|
default: "h-11 px-5 py-2.5",
|
||||||
sm: "h-8 rounded-sm px-3 text-xs",
|
sm: "h-9 rounded-sm px-4 text-sm",
|
||||||
lg: "h-10 rounded-sm px-8",
|
lg: "h-13 rounded-sm px-8 text-lg",
|
||||||
icon: "h-9 w-9",
|
icon: "h-11 w-11",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ function Card({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
<div
|
<div
|
||||||
data-slot="card"
|
data-slot="card"
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
@ -20,7 +20,7 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
<div
|
<div
|
||||||
data-slot="card-header"
|
data-slot="card-header"
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
@ -32,7 +32,7 @@ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-slot="card-title"
|
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}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
@ -42,7 +42,7 @@ function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-slot="card-description"
|
data-slot="card-description"
|
||||||
className={cn("text-muted-foreground text-sm", className)}
|
className={cn("text-base text-foreground/70 font-mono", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
@ -65,7 +65,7 @@ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-slot="card-content"
|
data-slot="card-content"
|
||||||
className={cn("px-6", className)}
|
className={cn("py-2", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
@ -75,7 +75,7 @@ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-slot="card-footer"
|
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}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ function Checkbox({
|
||||||
<CheckboxPrimitive.Root
|
<CheckboxPrimitive.Root
|
||||||
data-slot="checkbox"
|
data-slot="checkbox"
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
@ -21,7 +21,7 @@ function Checkbox({
|
||||||
data-slot="checkbox-indicator"
|
data-slot="checkbox-indicator"
|
||||||
className="flex items-center justify-center text-current transition-none"
|
className="flex items-center justify-center text-current transition-none"
|
||||||
>
|
>
|
||||||
<CheckIcon className="size-3.5" />
|
<CheckIcon className="size-4" />
|
||||||
</CheckboxPrimitive.Indicator>
|
</CheckboxPrimitive.Indicator>
|
||||||
</CheckboxPrimitive.Root>
|
</CheckboxPrimitive.Root>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ function Command({
|
||||||
<CommandPrimitive
|
<CommandPrimitive
|
||||||
data-slot="command"
|
data-slot="command"
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
@ -64,7 +64,7 @@ function CommandInput({
|
||||||
<CommandPrimitive.Input
|
<CommandPrimitive.Input
|
||||||
data-slot="command-input"
|
data-slot="command-input"
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ const DialogContent = React.forwardRef<
|
||||||
<DialogPrimitive.Content
|
<DialogPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
@ -59,7 +59,7 @@ const DialogHeader = ({
|
||||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
@ -73,7 +73,7 @@ const DialogFooter = ({
|
||||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
@ -88,7 +88,7 @@ const DialogTitle = React.forwardRef<
|
||||||
<DialogPrimitive.Title
|
<DialogPrimitive.Title
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"text-lg font-semibold leading-none tracking-tight",
|
"text-xl font-mono font-bold uppercase tracking-wider text-foreground",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
@ -102,7 +102,7 @@ const DialogDescription = React.forwardRef<
|
||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<DialogPrimitive.Description
|
<DialogPrimitive.Description
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn("text-sm text-muted-foreground", className)}
|
className={cn("text-base text-foreground/70 font-mono", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ const DropdownMenuSubTrigger = React.forwardRef<
|
||||||
<DropdownMenuPrimitive.SubTrigger
|
<DropdownMenuPrimitive.SubTrigger
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
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",
|
inset && "pl-8",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
|
|
@ -65,7 +65,7 @@ const DropdownMenuContent = React.forwardRef<
|
||||||
ref={ref}
|
ref={ref}
|
||||||
sideOffset={sideOffset}
|
sideOffset={sideOffset}
|
||||||
className={cn(
|
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]",
|
"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
|
className
|
||||||
)}
|
)}
|
||||||
|
|
@ -84,7 +84,7 @@ const DropdownMenuItem = React.forwardRef<
|
||||||
<DropdownMenuPrimitive.Item
|
<DropdownMenuPrimitive.Item
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
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",
|
inset && "pl-8",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
|
|
@ -148,7 +148,7 @@ const DropdownMenuLabel = React.forwardRef<
|
||||||
<DropdownMenuPrimitive.Label
|
<DropdownMenuPrimitive.Label
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"px-2 py-1.5 text-sm font-semibold",
|
"px-3 py-2 text-base font-semibold",
|
||||||
inset && "pl-8",
|
inset && "pl-8",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
||||||
type={type}
|
type={type}
|
||||||
data-slot="input"
|
data-slot="input"
|
||||||
className={cn(
|
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",
|
"focus:border-primary focus:shadow-focus",
|
||||||
"aria-invalid:border-secondary",
|
"aria-invalid:border-secondary",
|
||||||
className
|
className
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ function Label({
|
||||||
<LabelPrimitive.Root
|
<LabelPrimitive.Root
|
||||||
data-slot="label"
|
data-slot="label"
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ function Menubar({
|
||||||
<MenubarPrimitive.Root
|
<MenubarPrimitive.Root
|
||||||
data-slot="menubar"
|
data-slot="menubar"
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
@ -77,7 +77,7 @@ function MenubarContent({
|
||||||
alignOffset={alignOffset}
|
alignOffset={alignOffset}
|
||||||
sideOffset={sideOffset}
|
sideOffset={sideOffset}
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
@ -246,7 +246,7 @@ function MenubarSubContent({
|
||||||
<MenubarPrimitive.SubContent
|
<MenubarPrimitive.SubContent
|
||||||
data-slot="menubar-sub-content"
|
data-slot="menubar-sub-content"
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
|
|
@ -89,13 +89,13 @@ const MultiSelect: React.FC<MultiSelectProps> = ({
|
||||||
<div className="relative z-20 inline-block w-full" ref={containerRef}>
|
<div className="relative z-20 inline-block w-full" ref={containerRef}>
|
||||||
<div className="relative flex flex-col items-center">
|
<div className="relative flex flex-col items-center">
|
||||||
<div onClick={toggleDropdown} className="w-full">
|
<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">
|
<div className="flex flex-wrap flex-auto gap-2">
|
||||||
{selectedValuesText.length > 0 ? (
|
{selectedValuesText.length > 0 ? (
|
||||||
selectedValuesText.map((text, index) => (
|
selectedValuesText.map((text, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
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>
|
<span className="flex-initial max-w-full">{text}</span>
|
||||||
<div className="flex flex-row-reverse flex-auto">
|
<div className="flex flex-row-reverse flex-auto">
|
||||||
|
|
@ -104,7 +104,7 @@ const MultiSelect: React.FC<MultiSelectProps> = ({
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
removeOption(selectedOptions[index]);
|
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
|
<svg
|
||||||
className="fill-current"
|
className="fill-current"
|
||||||
|
|
@ -127,7 +127,7 @@ const MultiSelect: React.FC<MultiSelectProps> = ({
|
||||||
) : (
|
) : (
|
||||||
<input
|
<input
|
||||||
placeholder="请选择选项..."
|
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
|
readOnly
|
||||||
value="请选择选项..."
|
value="请选择选项..."
|
||||||
/>
|
/>
|
||||||
|
|
@ -137,7 +137,7 @@ const MultiSelect: React.FC<MultiSelectProps> = ({
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={toggleDropdown}
|
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
|
<svg
|
||||||
className={`stroke-current ${isOpen ? "rotate-180" : ""}`}
|
className={`stroke-current ${isOpen ? "rotate-180" : ""}`}
|
||||||
|
|
@ -162,14 +162,14 @@ const MultiSelect: React.FC<MultiSelectProps> = ({
|
||||||
|
|
||||||
{isOpen && (
|
{isOpen && (
|
||||||
<div
|
<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()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
{options.map((option, index) => (
|
{options.map((option, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
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)}
|
onClick={() => handleSelect(option.value)}
|
||||||
>
|
>
|
||||||
<div
|
<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}
|
{option.label}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ function NavigationMenuItem({
|
||||||
}
|
}
|
||||||
|
|
||||||
const navigationMenuTriggerStyle = cva(
|
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({
|
function NavigationMenuTrigger({
|
||||||
|
|
@ -91,7 +91,7 @@ function NavigationMenuContent({
|
||||||
data-slot="navigation-menu-content"
|
data-slot="navigation-menu-content"
|
||||||
className={cn(
|
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",
|
"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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
@ -112,7 +112,7 @@ function NavigationMenuViewport({
|
||||||
<NavigationMenuPrimitive.Viewport
|
<NavigationMenuPrimitive.Viewport
|
||||||
data-slot="navigation-menu-viewport"
|
data-slot="navigation-menu-viewport"
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ function PopoverContent({
|
||||||
align={align}
|
align={align}
|
||||||
sideOffset={sideOffset}
|
sideOffset={sideOffset}
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ function Progress({
|
||||||
<ProgressPrimitive.Root
|
<ProgressPrimitive.Root
|
||||||
data-slot="progress"
|
data-slot="progress"
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ const SelectTrigger = React.forwardRef<
|
||||||
<SelectPrimitive.Trigger
|
<SelectPrimitive.Trigger
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
@ -75,7 +75,7 @@ const SelectContent = React.forwardRef<
|
||||||
<SelectPrimitive.Content
|
<SelectPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
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" &&
|
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",
|
"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
|
className
|
||||||
|
|
@ -118,7 +118,7 @@ const SelectItem = React.forwardRef<
|
||||||
<SelectPrimitive.Item
|
<SelectPrimitive.Item
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ const SheetOverlay = React.forwardRef<
|
||||||
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
|
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
|
||||||
|
|
||||||
const sheetVariants = cva(
|
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: {
|
variants: {
|
||||||
side: {
|
side: {
|
||||||
|
|
@ -108,7 +108,7 @@ const SheetTitle = React.forwardRef<
|
||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<SheetPrimitive.Title
|
<SheetPrimitive.Title
|
||||||
ref={ref}
|
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}
|
{...props}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
@ -120,7 +120,7 @@ const SheetDescription = React.forwardRef<
|
||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<SheetPrimitive.Description
|
<SheetPrimitive.Description
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn("text-sm text-muted-foreground", className)}
|
className={cn("text-base text-foreground/70 font-mono", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-slot="skeleton"
|
data-slot="skeleton"
|
||||||
className={cn("bg-accent animate-pulse rounded-md", className)}
|
className={cn("bg-muted animate-pulse rounded-sm", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ function Switch({
|
||||||
<SwitchPrimitive.Root
|
<SwitchPrimitive.Root
|
||||||
data-slot="switch"
|
data-slot="switch"
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
@ -19,7 +19,7 @@ function Switch({
|
||||||
<SwitchPrimitive.Thumb
|
<SwitchPrimitive.Thumb
|
||||||
data-slot="switch-thumb"
|
data-slot="switch-thumb"
|
||||||
className={cn(
|
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>
|
</SwitchPrimitive.Root>
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,11 @@ function Table({ className, ...props }: React.ComponentProps<"table">) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-slot="table-container"
|
data-slot="table-container"
|
||||||
className="relative w-full overflow-x-auto"
|
className="relative w-full overflow-x-auto rounded-sm border border-border"
|
||||||
>
|
>
|
||||||
<table
|
<table
|
||||||
data-slot="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}
|
{...props}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -21,7 +21,7 @@ function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
|
||||||
return (
|
return (
|
||||||
<thead
|
<thead
|
||||||
data-slot="table-header"
|
data-slot="table-header"
|
||||||
className={cn("[&_tr]:border-b", className)}
|
className={cn("bg-muted/50 [&_tr]:border-b border-border", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
@ -55,7 +55,7 @@ function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
|
||||||
<tr
|
<tr
|
||||||
data-slot="table-row"
|
data-slot="table-row"
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
@ -68,7 +68,7 @@ function TableHead({ className, ...props }: React.ComponentProps<"th">) {
|
||||||
<th
|
<th
|
||||||
data-slot="table-head"
|
data-slot="table-head"
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
@ -81,7 +81,7 @@ function TableCell({ className, ...props }: React.ComponentProps<"td">) {
|
||||||
<td
|
<td
|
||||||
data-slot="table-cell"
|
data-slot="table-cell"
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ function TabsList({
|
||||||
<TabsPrimitive.List
|
<TabsPrimitive.List
|
||||||
data-slot="tabs-list"
|
data-slot="tabs-list"
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
@ -40,7 +40,7 @@ function TabsTrigger({
|
||||||
<TabsPrimitive.Trigger
|
<TabsPrimitive.Trigger
|
||||||
data-slot="tabs-trigger"
|
data-slot="tabs-trigger"
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ export function Textarea({ className, ...props }: React.ComponentProps<"textarea
|
||||||
<textarea
|
<textarea
|
||||||
data-slot="textarea"
|
data-slot="textarea"
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...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-variant={variant}
|
||||||
data-size={size}
|
data-size={size}
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority";
|
||||||
import { cn } from "@/shared/utils/utils";
|
import { cn } from "@/shared/utils/utils";
|
||||||
|
|
||||||
const toggleVariants = cva(
|
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: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
|
|
|
||||||
|
|
@ -46,13 +46,13 @@ function TooltipContent({
|
||||||
data-slot="tooltip-content"
|
data-slot="tooltip-content"
|
||||||
sideOffset={sideOffset}
|
sideOffset={sideOffset}
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{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.Content>
|
||||||
</TooltipPrimitive.Portal>
|
</TooltipPrimitive.Portal>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -138,17 +138,17 @@ export default function Account() {
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
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="text-center space-y-4">
|
||||||
<div className="loading-spinner mx-auto" />
|
<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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
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 */}
|
{/* Grid background */}
|
||||||
<div className="absolute inset-0 cyber-grid-subtle pointer-events-none" />
|
<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 p-0">
|
||||||
<div className="cyber-card-header">
|
<div className="cyber-card-header">
|
||||||
<User className="w-5 h-5 text-primary" />
|
<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>
|
||||||
<div className="p-6 text-center">
|
<div className="p-6 text-center">
|
||||||
<div className="relative inline-block mb-4">
|
<div className="relative inline-block mb-4">
|
||||||
|
|
@ -167,31 +167,31 @@ export default function Account() {
|
||||||
{getInitials(profile?.full_name, profile?.email)}
|
{getInitials(profile?.full_name, profile?.email)}
|
||||||
</AvatarFallback>
|
</AvatarFallback>
|
||||||
</Avatar>
|
</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="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-white rounded-full animate-pulse" />
|
<div className="w-2 h-2 bg-foreground rounded-full animate-pulse" />
|
||||||
</div>
|
</div>
|
||||||
</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 || "未设置姓名"}
|
{profile?.full_name || "未设置姓名"}
|
||||||
</h4>
|
</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">
|
<div className="flex items-center gap-3 text-sm">
|
||||||
<Shield className="w-4 h-4 text-violet-400" />
|
<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">
|
<span className="text-violet-400 font-bold uppercase">
|
||||||
{profile?.role === 'admin' ? '管理员' : '成员'}
|
{profile?.role === 'admin' ? '管理员' : '成员'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-3 text-sm">
|
<div className="flex items-center gap-3 text-sm">
|
||||||
<Calendar className="w-4 h-4 text-sky-400" />
|
<Calendar className="w-4 h-4 text-sky-400" />
|
||||||
<span className="text-gray-500">注册时间:</span>
|
<span className="text-muted-foreground">注册时间:</span>
|
||||||
<span className="text-white font-mono">{formatDate(profile?.created_at)}</span>
|
<span className="text-foreground font-mono">{formatDate(profile?.created_at)}</span>
|
||||||
</div>
|
</div>
|
||||||
</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
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={handleSwitchAccount}
|
onClick={handleSwitchAccount}
|
||||||
|
|
@ -216,24 +216,24 @@ export default function Account() {
|
||||||
<div className="lg:col-span-2 cyber-card p-0">
|
<div className="lg:col-span-2 cyber-card p-0">
|
||||||
<div className="cyber-card-header">
|
<div className="cyber-card-header">
|
||||||
<Terminal className="w-5 h-5 text-primary" />
|
<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>
|
||||||
<div className="p-6 space-y-6">
|
<div className="p-6 space-y-6">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div className="space-y-2">
|
<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" /> 邮箱
|
<Mail className="w-3 h-3" /> 邮箱
|
||||||
</Label>
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
id="email"
|
id="email"
|
||||||
value={profile?.email || ""}
|
value={profile?.email || ""}
|
||||||
disabled
|
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>
|
||||||
<div className="space-y-2">
|
<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" /> 姓名
|
<User className="w-3 h-3" /> 姓名
|
||||||
</Label>
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
|
|
@ -245,7 +245,7 @@ export default function Account() {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<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" /> 手机号
|
<Phone className="w-3 h-3" /> 手机号
|
||||||
</Label>
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
|
|
@ -258,14 +258,14 @@ export default function Account() {
|
||||||
</div>
|
</div>
|
||||||
</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">
|
<h3 className="section-title text-sm mb-4 flex items-center gap-2">
|
||||||
<GitBranch className="w-4 h-4" />
|
<GitBranch className="w-4 h-4" />
|
||||||
代码托管账号
|
代码托管账号
|
||||||
</h3>
|
</h3>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div className="space-y-2">
|
<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 用户名
|
<GitBranch className="w-3 h-3" /> GitHub 用户名
|
||||||
</Label>
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
|
|
@ -277,7 +277,7 @@ export default function Account() {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<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 用户名
|
<GitBranch className="w-3 h-3" /> GitLab 用户名
|
||||||
</Label>
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
|
|
@ -313,12 +313,12 @@ export default function Account() {
|
||||||
<div className="lg:col-span-3 cyber-card p-0">
|
<div className="lg:col-span-3 cyber-card p-0">
|
||||||
<div className="cyber-card-header">
|
<div className="cyber-card-header">
|
||||||
<KeyRound className="w-5 h-5 text-amber-400" />
|
<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>
|
||||||
<div className="p-6">
|
<div className="p-6">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||||
<div className="space-y-2">
|
<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
|
<Input
|
||||||
id="new_password"
|
id="new_password"
|
||||||
type="password"
|
type="password"
|
||||||
|
|
@ -329,7 +329,7 @@ export default function Account() {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<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
|
<Input
|
||||||
id="confirm_password"
|
id="confirm_password"
|
||||||
type="password"
|
type="password"
|
||||||
|
|
@ -365,13 +365,13 @@ export default function Account() {
|
||||||
|
|
||||||
{/* Logout Confirmation Dialog */}
|
{/* Logout Confirmation Dialog */}
|
||||||
<AlertDialog open={showLogoutDialog} onOpenChange={setShowLogoutDialog}>
|
<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>
|
<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" />
|
<LogOut className="w-5 h-5 text-rose-400" />
|
||||||
确认退出登录?
|
确认退出登录?
|
||||||
</AlertDialogTitle>
|
</AlertDialogTitle>
|
||||||
<AlertDialogDescription className="text-gray-400">
|
<AlertDialogDescription className="text-muted-foreground">
|
||||||
退出后需要重新登录才能访问系统。
|
退出后需要重新登录才能访问系统。
|
||||||
</AlertDialogDescription>
|
</AlertDialogDescription>
|
||||||
</AlertDialogHeader>
|
</AlertDialogHeader>
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import { Settings, Database, Terminal } from "lucide-react";
|
||||||
|
|
||||||
export default function AdminDashboard() {
|
export default function AdminDashboard() {
|
||||||
return (
|
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 */}
|
{/* Grid background */}
|
||||||
<div className="absolute inset-0 cyber-grid-subtle pointer-events-none" />
|
<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 p-0">
|
||||||
<div className="cyber-card-header">
|
<div className="cyber-card-header">
|
||||||
<Terminal className="w-5 h-5 text-primary" />
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Main Content Tabs */}
|
{/* Main Content Tabs */}
|
||||||
<Tabs defaultValue="config" className="w-full relative z-10">
|
<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
|
<TabsTrigger
|
||||||
value="config"
|
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" />
|
<Settings className="w-4 h-4" />
|
||||||
系统配置
|
系统配置
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
value="data"
|
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" />
|
<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" />
|
<div className="absolute top-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-primary/50 to-transparent" />
|
||||||
|
|
||||||
{/* Header */}
|
{/* 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">
|
<div className="flex items-center gap-2.5">
|
||||||
{/* Agent type icon with color */}
|
{/* Agent type icon with color */}
|
||||||
<div className={`text-${typeConfig.color}-400`}>
|
<div className={`text-${typeConfig.color}-400`}>
|
||||||
|
|
@ -62,22 +62,22 @@ export const AgentDetailPanel = memo(function AgentDetailPanel({ agentId, treeNo
|
||||||
|
|
||||||
{/* Agent name */}
|
{/* Agent name */}
|
||||||
<div>
|
<div>
|
||||||
<span className="text-sm font-medium text-white block">{agent.agent_name}</span>
|
<span className="text-sm font-medium text-foreground block">{agent.agent_name}</span>
|
||||||
<span className="text-[10px] text-gray-500 uppercase tracking-wider">{typeConfig.label}</span>
|
<span className="text-xs text-muted-foreground uppercase tracking-wider">{typeConfig.label}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Close button */}
|
{/* Close button */}
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
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" />
|
<X className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Status indicator */}
|
{/* 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="flex items-center gap-2">
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className={`
|
<div className={`
|
||||||
|
|
@ -86,7 +86,7 @@ export const AgentDetailPanel = memo(function AgentDetailPanel({ agentId, treeNo
|
||||||
${agent.status === 'completed' ? 'bg-green-500' : ''}
|
${agent.status === 'completed' ? 'bg-green-500' : ''}
|
||||||
${agent.status === 'failed' ? 'bg-red-400' : ''}
|
${agent.status === 'failed' ? 'bg-red-400' : ''}
|
||||||
${agent.status === 'waiting' ? 'bg-yellow-400' : ''}
|
${agent.status === 'waiting' ? 'bg-yellow-400' : ''}
|
||||||
${agent.status === 'created' ? 'bg-gray-500' : ''}
|
${agent.status === 'created' ? 'bg-background0' : ''}
|
||||||
`} />
|
`} />
|
||||||
{isRunning && (
|
{isRunning && (
|
||||||
<div className="absolute inset-0 w-2.5 h-2.5 rounded-full bg-green-400 animate-ping opacity-30" />
|
<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 */}
|
{/* Metrics grid */}
|
||||||
<div className="p-3 grid grid-cols-2 gap-2">
|
<div className="p-3 grid grid-cols-2 gap-2">
|
||||||
{/* Iterations */}
|
{/* 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" />
|
<Repeat className="w-3.5 h-3.5 text-cyan-400/70" />
|
||||||
<div>
|
<div>
|
||||||
<div className="text-[9px] text-gray-600 uppercase">Iterations</div>
|
<div className="text-xs text-muted-foreground uppercase">Iterations</div>
|
||||||
<div className="text-sm text-white font-mono">{agent.iterations || 0}</div>
|
<div className="text-sm text-foreground font-mono">{agent.iterations || 0}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Tool Calls */}
|
{/* 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" />
|
<Zap className="w-3.5 h-3.5 text-amber-400/70" />
|
||||||
<div>
|
<div>
|
||||||
<div className="text-[9px] text-gray-600 uppercase">Tool Calls</div>
|
<div className="text-xs text-muted-foreground uppercase">Tool Calls</div>
|
||||||
<div className="text-sm text-white font-mono">{agent.tool_calls || 0}</div>
|
<div className="text-sm text-foreground font-mono">{agent.tool_calls || 0}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Findings - Only show for Orchestrator (root agent with no parent) */}
|
{/* Findings - Only show for Orchestrator (root agent with no parent) */}
|
||||||
{!agent.parent_agent_id && (
|
{!agent.parent_agent_id && (
|
||||||
<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">
|
||||||
<Bug className={`w-3.5 h-3.5 ${agent.findings_count > 0 ? 'text-red-400/70' : 'text-gray-500/70'}`} />
|
<Bug className={`w-3.5 h-3.5 ${agent.findings_count > 0 ? 'text-red-400/70' : 'text-muted-foreground/70'}`} />
|
||||||
<div>
|
<div>
|
||||||
<div className="text-[9px] text-gray-600 uppercase">Findings</div>
|
<div className="text-xs text-muted-foreground uppercase">Findings</div>
|
||||||
<div className={`text-sm font-mono ${agent.findings_count > 0 ? 'text-red-400' : 'text-white'}`}>
|
<div className={`text-sm font-mono ${agent.findings_count > 0 ? 'text-red-400' : 'text-foreground'}`}>
|
||||||
{agent.findings_count}
|
{agent.findings_count}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -133,13 +133,13 @@ export const AgentDetailPanel = memo(function AgentDetailPanel({ agentId, treeNo
|
||||||
|
|
||||||
{/* Duration/Status - Show for sub-agents instead of Findings */}
|
{/* Duration/Status - Show for sub-agents instead of Findings */}
|
||||||
{agent.parent_agent_id && (
|
{agent.parent_agent_id && (
|
||||||
<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">
|
||||||
<Clock className="w-3.5 h-3.5 text-slate-400/70" />
|
<Clock className="w-3.5 h-3.5 text-muted-foreground/70" />
|
||||||
<div>
|
<div>
|
||||||
<div className="text-[9px] text-gray-600 uppercase">
|
<div className="text-xs text-muted-foreground uppercase">
|
||||||
{agent.duration_ms ? "Duration" : "Status"}
|
{agent.duration_ms ? "Duration" : "Status"}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-white font-mono">
|
<div className="text-sm text-foreground font-mono">
|
||||||
{agent.duration_ms
|
{agent.duration_ms
|
||||||
? `${(agent.duration_ms / 1000).toFixed(1)}s`
|
? `${(agent.duration_ms / 1000).toFixed(1)}s`
|
||||||
: (AGENT_STATUS_CONFIG[agent.status]?.text || agent.status)
|
: (AGENT_STATUS_CONFIG[agent.status]?.text || agent.status)
|
||||||
|
|
@ -150,11 +150,11 @@ export const AgentDetailPanel = memo(function AgentDetailPanel({ agentId, treeNo
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Tokens */}
|
{/* 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" />
|
<FileCode className="w-3.5 h-3.5 text-purple-400/70" />
|
||||||
<div>
|
<div>
|
||||||
<div className="text-[9px] text-gray-600 uppercase">Tokens</div>
|
<div className="text-xs text-muted-foreground uppercase">Tokens</div>
|
||||||
<div className="text-sm text-white font-mono">
|
<div className="text-sm text-foreground font-mono">
|
||||||
{((agent.tokens_used || 0) / 1000).toFixed(1)}k
|
{((agent.tokens_used || 0) / 1000).toFixed(1)}k
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -164,12 +164,12 @@ export const AgentDetailPanel = memo(function AgentDetailPanel({ agentId, treeNo
|
||||||
{/* Task description */}
|
{/* Task description */}
|
||||||
{agent.task_description && (
|
{agent.task_description && (
|
||||||
<div className="px-3 pb-3">
|
<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">
|
<div className="flex items-center gap-1.5 mb-1.5">
|
||||||
<Clock className="w-3 h-3 text-gray-500" />
|
<Clock className="w-3 h-3 text-muted-foreground" />
|
||||||
<span className="text-[9px] text-gray-500 uppercase tracking-wider">Current Task</span>
|
<span className="text-xs text-muted-foreground uppercase tracking-wider">Current Task</span>
|
||||||
</div>
|
</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}
|
{agent.task_description}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -179,7 +179,7 @@ export const AgentDetailPanel = memo(function AgentDetailPanel({ agentId, treeNo
|
||||||
{/* Sub-agents indicator */}
|
{/* Sub-agents indicator */}
|
||||||
{agent.children && agent.children.length > 0 && (
|
{agent.children && agent.children.length > 0 && (
|
||||||
<div className="px-3 pb-3">
|
<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" />
|
<Network className="w-3 h-3" />
|
||||||
<span className="uppercase tracking-wider">
|
<span className="uppercase tracking-wider">
|
||||||
{agent.children.length} Sub-agent{agent.children.length > 1 ? 's' : ''}
|
{agent.children.length} Sub-agent{agent.children.length > 1 ? 's' : ''}
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,7 @@ export class AgentErrorBoundary extends Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
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">
|
<div className="w-full max-w-lg space-y-6">
|
||||||
{/* Error Header */}
|
{/* Error Header */}
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
|
|
@ -179,16 +179,16 @@ export class AgentErrorBoundary extends Component<Props, State> {
|
||||||
)} />
|
)} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl font-bold text-white">Agent Error</h2>
|
<h2 className="text-xl font-bold text-foreground">Agent Error</h2>
|
||||||
<p className="text-sm text-gray-400">{this.getRecoveryHint()}</p>
|
<p className="text-sm text-muted-foreground">{this.getRecoveryHint()}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Error Details */}
|
{/* Error Details */}
|
||||||
<div className="bg-[#0d0d12] border border-gray-800 rounded-lg overflow-hidden">
|
<div className="cyber-dialog border border-border rounded-lg overflow-hidden">
|
||||||
<div className="px-4 py-3 border-b border-gray-800 flex items-center gap-2">
|
<div className="px-4 py-3 border-b border-border flex items-center gap-2">
|
||||||
<Terminal className="w-4 h-4 text-gray-500" />
|
<Terminal className="w-4 h-4 text-muted-foreground" />
|
||||||
<span className="text-xs text-gray-500 uppercase tracking-wider font-bold">
|
<span className="text-xs text-muted-foreground uppercase tracking-wider font-bold">
|
||||||
Error Details
|
Error Details
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</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" />
|
<Bug className="w-4 h-4 text-red-400 mt-0.5 flex-shrink-0" />
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-mono text-red-400">{error.name}</p>
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{this.props.taskId && (
|
{this.props.taskId && (
|
||||||
<div className="text-xs text-gray-500">
|
<div className="text-xs text-muted-foreground">
|
||||||
Task ID: <span className="font-mono text-gray-400">{this.props.taskId}</span>
|
Task ID: <span className="font-mono text-muted-foreground">{this.props.taskId}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{retryCount > 0 && (
|
{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>
|
Retry attempts: <span className="text-yellow-400">{retryCount}/{maxRetries}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -220,10 +220,10 @@ export class AgentErrorBoundary extends Component<Props, State> {
|
||||||
{/* Stack trace (dev only) */}
|
{/* Stack trace (dev only) */}
|
||||||
{import.meta.env.DEV && error?.stack && (
|
{import.meta.env.DEV && error?.stack && (
|
||||||
<details className="text-xs">
|
<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
|
Stack Trace
|
||||||
</summary>
|
</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}
|
{error.stack}
|
||||||
</pre>
|
</pre>
|
||||||
</details>
|
</details>
|
||||||
|
|
@ -231,10 +231,10 @@ export class AgentErrorBoundary extends Component<Props, State> {
|
||||||
|
|
||||||
{import.meta.env.DEV && errorInfo?.componentStack && (
|
{import.meta.env.DEV && errorInfo?.componentStack && (
|
||||||
<details className="text-xs">
|
<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
|
Component Stack
|
||||||
</summary>
|
</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}
|
{errorInfo.componentStack}
|
||||||
</pre>
|
</pre>
|
||||||
</details>
|
</details>
|
||||||
|
|
@ -257,7 +257,7 @@ export class AgentErrorBoundary extends Component<Props, State> {
|
||||||
<Button
|
<Button
|
||||||
onClick={this.handleGoBack}
|
onClick={this.handleGoBack}
|
||||||
variant="outline"
|
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" />
|
<ArrowLeft className="w-4 h-4 mr-2" />
|
||||||
Go Back
|
Go Back
|
||||||
|
|
@ -265,7 +265,7 @@ export class AgentErrorBoundary extends Component<Props, State> {
|
||||||
<Button
|
<Button
|
||||||
onClick={this.handleReload}
|
onClick={this.handleReload}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
className="flex-1 text-gray-400 hover:text-white"
|
className="flex-1 text-muted-foreground hover:text-foreground"
|
||||||
>
|
>
|
||||||
Refresh Page
|
Refresh Page
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -273,7 +273,7 @@ export class AgentErrorBoundary extends Component<Props, State> {
|
||||||
|
|
||||||
{/* Recovery suggestion */}
|
{/* Recovery suggestion */}
|
||||||
{!canRetry && (
|
{!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.
|
Maximum retry attempts reached. Please refresh the page or contact support.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,12 @@ import { Badge } from "@/components/ui/badge";
|
||||||
import { AGENT_STATUS_CONFIG } from "../constants";
|
import { AGENT_STATUS_CONFIG } from "../constants";
|
||||||
import type { AgentTreeNodeItemProps } from "../types";
|
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> = {
|
const AGENT_TYPE_ICONS: Record<string, React.ReactNode> = {
|
||||||
orchestrator: <Cpu className="w-3.5 h-3.5 text-violet-400" />,
|
orchestrator: <Cpu className="w-4 h-4 text-violet-600 dark:text-violet-400" />,
|
||||||
recon: <Scan className="w-3.5 h-3.5 text-teal-400" />,
|
recon: <Scan className="w-4 h-4 text-teal-600 dark:text-teal-400" />,
|
||||||
analysis: <FileSearch className="w-3.5 h-3.5 text-amber-400" />,
|
analysis: <FileSearch className="w-4 h-4 text-amber-600 dark:text-amber-400" />,
|
||||||
verification: <ShieldCheck className="w-3.5 h-3.5 text-emerald-400" />,
|
verification: <ShieldCheck className="w-4 h-4 text-emerald-600 dark:text-emerald-400" />,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Status colors for the glow effect
|
// Status colors for the glow effect
|
||||||
|
|
@ -40,14 +40,14 @@ export const AgentTreeNodeItem = memo(function AgentTreeNodeItem({
|
||||||
const isRunning = node.status === 'running';
|
const isRunning = node.status === 'running';
|
||||||
|
|
||||||
const statusConfig = AGENT_STATUS_CONFIG[node.status] || AGENT_STATUS_CONFIG.created;
|
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 (
|
return (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
{/* Connection line to parent - vertical line */}
|
{/* Connection line to parent - vertical line */}
|
||||||
{depth > 0 && (
|
{depth > 0 && (
|
||||||
<div
|
<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={{
|
style={{
|
||||||
left: `${depth * 16 - 8}px`,
|
left: `${depth * 16 - 8}px`,
|
||||||
height: '20px',
|
height: '20px',
|
||||||
|
|
@ -58,7 +58,7 @@ export const AgentTreeNodeItem = memo(function AgentTreeNodeItem({
|
||||||
{/* Horizontal connector line */}
|
{/* Horizontal connector line */}
|
||||||
{depth > 0 && (
|
{depth > 0 && (
|
||||||
<div
|
<div
|
||||||
className="absolute top-[20px] h-px bg-slate-600"
|
className="absolute top-[20px] h-px bg-muted-foreground"
|
||||||
style={{
|
style={{
|
||||||
left: `${depth * 16 - 8}px`,
|
left: `${depth * 16 - 8}px`,
|
||||||
width: '8px',
|
width: '8px',
|
||||||
|
|
@ -73,7 +73,7 @@ export const AgentTreeNodeItem = memo(function AgentTreeNodeItem({
|
||||||
transition-all duration-200 ease-out
|
transition-all duration-200 ease-out
|
||||||
${isSelected
|
${isSelected
|
||||||
? 'bg-primary/15 border border-primary/40'
|
? '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] || ''}
|
${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"
|
className="flex-shrink-0 w-5 h-5 flex items-center justify-center rounded hover:bg-white/10 transition-colors"
|
||||||
>
|
>
|
||||||
{expanded ? (
|
{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>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -104,7 +104,7 @@ export const AgentTreeNodeItem = memo(function AgentTreeNodeItem({
|
||||||
${node.status === 'completed' ? 'bg-emerald-500' : ''}
|
${node.status === 'completed' ? 'bg-emerald-500' : ''}
|
||||||
${node.status === 'failed' ? 'bg-rose-400' : ''}
|
${node.status === 'failed' ? 'bg-rose-400' : ''}
|
||||||
${node.status === 'waiting' ? 'bg-amber-400' : ''}
|
${node.status === 'waiting' ? 'bg-amber-400' : ''}
|
||||||
${node.status === 'created' ? 'bg-slate-500' : ''}
|
${node.status === 'created' ? 'bg-muted' : ''}
|
||||||
`} />
|
`} />
|
||||||
{isRunning && (
|
{isRunning && (
|
||||||
<div className="absolute inset-0 w-2.5 h-2.5 rounded-full bg-emerald-400 animate-ping opacity-30" />
|
<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 */}
|
{/* Agent name */}
|
||||||
<span className={`
|
<span className={`
|
||||||
text-xs font-mono truncate flex-1 transition-colors duration-200
|
text-sm font-mono truncate flex-1 transition-colors duration-200
|
||||||
${isSelected ? 'text-white font-medium' : 'text-slate-300 group-hover:text-white'}
|
${isSelected ? 'text-foreground font-medium' : 'text-foreground group-hover:text-foreground'}
|
||||||
`}>
|
`}>
|
||||||
{node.agent_name}
|
{node.agent_name}
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -128,14 +128,14 @@ export const AgentTreeNodeItem = memo(function AgentTreeNodeItem({
|
||||||
<div className="flex items-center gap-1.5 flex-shrink-0">
|
<div className="flex items-center gap-1.5 flex-shrink-0">
|
||||||
{/* Iterations */}
|
{/* Iterations */}
|
||||||
{(node.iterations ?? 0) > 0 && (
|
{(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
|
{node.iterations}x
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Findings count - Only show for Orchestrator (root agent) */}
|
{/* Findings count - Only show for Orchestrator (root agent) */}
|
||||||
{!node.parent_agent_id && node.findings_count > 0 && (
|
{!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}
|
{node.findings_count}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
|
|
@ -152,7 +152,7 @@ export const AgentTreeNodeItem = memo(function AgentTreeNodeItem({
|
||||||
>
|
>
|
||||||
{/* Vertical connection line for children */}
|
{/* Vertical connection line for children */}
|
||||||
<div
|
<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={{
|
style={{
|
||||||
left: `${(depth + 1) * 16 - 8}px`,
|
left: `${(depth + 1) * 16 - 8}px`,
|
||||||
top: '0',
|
top: '0',
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,8 @@ const STATUS_CONFIG: Record<ConnectionState, {
|
||||||
disconnected: {
|
disconnected: {
|
||||||
icon: WifiOff,
|
icon: WifiOff,
|
||||||
label: 'Disconnected',
|
label: 'Disconnected',
|
||||||
color: 'text-gray-400',
|
color: 'text-muted-foreground',
|
||||||
bgColor: 'bg-gray-400/10',
|
bgColor: 'bg-muted/30',
|
||||||
},
|
},
|
||||||
connecting: {
|
connecting: {
|
||||||
icon: RefreshCw,
|
icon: RefreshCw,
|
||||||
|
|
@ -67,7 +67,7 @@ export function ConnectionStatus({
|
||||||
return (
|
return (
|
||||||
<div className={cn('flex items-center gap-1.5', className)}>
|
<div className={cn('flex items-center gap-1.5', className)}>
|
||||||
<div className={cn(
|
<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.bgColor,
|
||||||
config.color
|
config.color
|
||||||
)}>
|
)}>
|
||||||
|
|
|
||||||
|
|
@ -18,21 +18,21 @@ export function Header({
|
||||||
onNewAudit
|
onNewAudit
|
||||||
}: HeaderProps) {
|
}: HeaderProps) {
|
||||||
return (
|
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 */}
|
{/* 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" />
|
<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 */}
|
{/* Left side - Brand and task info */}
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
{/* Logo section */}
|
{/* 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">
|
<div className="relative">
|
||||||
<Cpu className="w-5 h-5 text-primary" />
|
<Cpu className="w-5 h-5 text-primary" />
|
||||||
{isRunning && (
|
{isRunning && (
|
||||||
<span className="absolute -top-0.5 -right-0.5 w-2 h-2 bg-green-400 rounded-full animate-pulse" />
|
<span className="absolute -top-0.5 -right-0.5 w-2 h-2 bg-green-400 rounded-full animate-pulse" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</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>
|
DEEP<span className="text-primary">AUDIT</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -40,11 +40,11 @@ export function Header({
|
||||||
{/* Task info */}
|
{/* Task info */}
|
||||||
{task && (
|
{task && (
|
||||||
<div className="flex items-center gap-3">
|
<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" />
|
<Radio className="w-3 h-3" />
|
||||||
<span className="text-xs font-mono uppercase tracking-wider">Task</span>
|
<span className="text-xs font-mono uppercase tracking-wider">Task</span>
|
||||||
</div>
|
</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)}
|
{task.name || task.id.slice(0, 8)}
|
||||||
</span>
|
</span>
|
||||||
<StatusBadge status={task.status} />
|
<StatusBadge status={task.status} />
|
||||||
|
|
@ -76,7 +76,7 @@ export function Header({
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="h-6 w-px bg-gray-800/50 mx-1" />
|
<div className="h-6 w-px bg-muted mx-1" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
|
|
|
||||||
|
|
@ -93,8 +93,8 @@ export const LogEntry = memo(function LogEntry({ item, isExpanded, onToggle }: L
|
||||||
<div className={`
|
<div className={`
|
||||||
relative rounded border-l-2 overflow-hidden
|
relative rounded border-l-2 overflow-hidden
|
||||||
${config.borderColor}
|
${config.borderColor}
|
||||||
${isExpanded ? 'bg-gray-900/60' : 'bg-gray-900/30'}
|
${isExpanded ? 'bg-card/60' : 'bg-muted/50'}
|
||||||
${isCollapsible ? 'hover:bg-gray-900/50' : ''}
|
${isCollapsible ? 'hover:bg-muted' : ''}
|
||||||
${isFinding ? 'border-r border-r-red-900/30' : ''}
|
${isFinding ? 'border-r border-r-red-900/30' : ''}
|
||||||
${isError ? 'border-r border-r-red-900/30' : ''}
|
${isError ? 'border-r border-r-red-900/30' : ''}
|
||||||
transition-all duration-200
|
transition-all duration-200
|
||||||
|
|
@ -120,35 +120,35 @@ export const LogEntry = memo(function LogEntry({ item, isExpanded, onToggle }: L
|
||||||
|
|
||||||
{/* Type label */}
|
{/* Type label */}
|
||||||
<span className={`
|
<span className={`
|
||||||
text-[9px] font-mono font-bold uppercase tracking-wider px-1.5 py-0.5 rounded
|
text-sm font-mono font-bold uppercase tracking-wider px-2.5 py-1 rounded
|
||||||
${isThinking ? 'bg-violet-500/25 text-violet-300' : ''}
|
${isThinking ? 'bg-violet-500/30 text-violet-700 dark:text-violet-300' : ''}
|
||||||
${isTool ? 'bg-amber-500/25 text-amber-300' : ''}
|
${isTool ? 'bg-amber-500/30 text-amber-700 dark:text-amber-300' : ''}
|
||||||
${isFinding ? 'bg-rose-500/25 text-rose-300' : ''}
|
${isFinding ? 'bg-rose-500/30 text-rose-700 dark:text-rose-300' : ''}
|
||||||
${isError ? 'bg-red-500/25 text-red-300' : ''}
|
${isError ? 'bg-red-500/30 text-red-700 dark:text-red-300' : ''}
|
||||||
${isInfo ? 'bg-slate-500/25 text-slate-300' : ''}
|
${isInfo ? 'bg-muted text-foreground' : ''}
|
||||||
${isProgress ? 'bg-cyan-500/25 text-cyan-300' : ''}
|
${isProgress ? 'bg-cyan-500/30 text-cyan-700 dark:text-cyan-300' : ''}
|
||||||
${item.type === 'dispatch' ? 'bg-sky-500/25 text-sky-300' : ''}
|
${item.type === 'dispatch' ? 'bg-sky-500/30 text-sky-700 dark:text-sky-300' : ''}
|
||||||
${item.type === 'phase' ? 'bg-teal-500/25 text-teal-300' : ''}
|
${item.type === 'phase' ? 'bg-teal-500/30 text-teal-700 dark:text-teal-300' : ''}
|
||||||
${item.type === 'user' ? 'bg-indigo-500/25 text-indigo-300' : ''}
|
${item.type === 'user' ? 'bg-indigo-500/30 text-indigo-700 dark:text-indigo-300' : ''}
|
||||||
flex-shrink-0
|
flex-shrink-0
|
||||||
`}>
|
`}>
|
||||||
{LOG_TYPE_LABELS[item.type] || 'LOG'}
|
{LOG_TYPE_LABELS[item.type] || 'LOG'}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{/* Timestamp */}
|
{/* 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}
|
{item.time}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{/* Separator */}
|
{/* 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 */}
|
{/* Status icon for info messages */}
|
||||||
{statusIcon && <span className="flex-shrink-0">{statusIcon}</span>}
|
{statusIcon && <span className="flex-shrink-0">{statusIcon}</span>}
|
||||||
|
|
||||||
{/* Title - for non-thinking types */}
|
{/* Title - for non-thinking types */}
|
||||||
{!isThinking && (
|
{!isThinking && (
|
||||||
<span className="text-sm text-gray-300 truncate flex-1">
|
<span className="text-base text-foreground font-medium truncate flex-1">
|
||||||
{formattedTitle}
|
{formattedTitle}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
@ -162,7 +162,7 @@ export const LogEntry = memo(function LogEntry({ item, isExpanded, onToggle }: L
|
||||||
{item.tool?.status === 'running' && (
|
{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">
|
<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" />
|
<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>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
@ -176,7 +176,7 @@ export const LogEntry = memo(function LogEntry({ item, isExpanded, onToggle }: L
|
||||||
{item.agentName && (
|
{item.agentName && (
|
||||||
<Badge
|
<Badge
|
||||||
variant="outline"
|
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}
|
{item.agentName}
|
||||||
</Badge>
|
</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">
|
<div className="flex items-center gap-2 flex-shrink-0 ml-auto">
|
||||||
{/* Duration badge */}
|
{/* Duration badge */}
|
||||||
{item.tool?.duration !== undefined && (
|
{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
|
{item.tool.duration}ms
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
@ -195,7 +195,7 @@ export const LogEntry = memo(function LogEntry({ item, isExpanded, onToggle }: L
|
||||||
{item.severity && (
|
{item.severity && (
|
||||||
<Badge
|
<Badge
|
||||||
className={`
|
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}
|
${SEVERITY_COLORS[item.severity] || SEVERITY_COLORS.info}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
|
|
@ -205,11 +205,11 @@ export const LogEntry = memo(function LogEntry({ item, isExpanded, onToggle }: L
|
||||||
|
|
||||||
{/* Expand indicator */}
|
{/* Expand indicator */}
|
||||||
{isCollapsible && (
|
{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 ? (
|
{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>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -220,7 +220,7 @@ export const LogEntry = memo(function LogEntry({ item, isExpanded, onToggle }: L
|
||||||
{isThinking && item.content && (
|
{isThinking && item.content && (
|
||||||
<div className="mt-2.5 relative">
|
<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="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}
|
{item.content}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -229,24 +229,24 @@ export const LogEntry = memo(function LogEntry({ item, isExpanded, onToggle }: L
|
||||||
{/* Collapsible content */}
|
{/* Collapsible content */}
|
||||||
{!isThinking && showContent && item.content && (
|
{!isThinking && showContent && item.content && (
|
||||||
<div className="mt-2.5 overflow-hidden">
|
<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 */}
|
{/* 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">
|
<div className="flex items-center gap-2">
|
||||||
<Square className="w-2.5 h-2.5 text-gray-600" />
|
<Square className="w-2.5 h-2.5 text-muted-foreground" />
|
||||||
<span className="text-[9px] text-gray-500 font-mono uppercase tracking-wider">
|
<span className="text-xs text-muted-foreground font-mono uppercase tracking-wider">
|
||||||
{isTool ? 'Output' : 'Details'}
|
{isTool ? 'Output' : 'Details'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{item.tool?.status === 'completed' && (
|
{item.tool?.status === 'completed' && (
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<CheckCircle2 className="w-3 h-3 text-green-500/70" />
|
<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>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{/* Content */}
|
{/* 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}
|
{item.content}
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ function getSeverityColor(severity: string): string {
|
||||||
high: "text-orange-400",
|
high: "text-orange-400",
|
||||||
medium: "text-amber-400",
|
medium: "text-amber-400",
|
||||||
low: "text-sky-400",
|
low: "text-sky-400",
|
||||||
info: "text-slate-400",
|
info: "text-muted-foreground",
|
||||||
};
|
};
|
||||||
return colors[severity.toLowerCase()] || colors.info;
|
return colors[severity.toLowerCase()] || colors.info;
|
||||||
}
|
}
|
||||||
|
|
@ -181,7 +181,7 @@ const CircularProgress = memo(function CircularProgress({
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
strokeWidth={strokeWidth}
|
strokeWidth={strokeWidth}
|
||||||
className="text-slate-800/50"
|
className="text-foreground/50"
|
||||||
/>
|
/>
|
||||||
{/* Progress circle */}
|
{/* Progress circle */}
|
||||||
<circle
|
<circle
|
||||||
|
|
@ -200,7 +200,7 @@ const CircularProgress = memo(function CircularProgress({
|
||||||
<span className={`text-xl font-bold font-mono ${colors.text}`}>
|
<span className={`text-xl font-bold font-mono ${colors.text}`}>
|
||||||
{value.toFixed(0)}
|
{value.toFixed(0)}
|
||||||
</span>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -223,7 +223,7 @@ const EnhancedStatsPanel = memo(function EnhancedStatsPanel({
|
||||||
icon: <Bug className="w-4 h-4" />,
|
icon: <Bug className="w-4 h-4" />,
|
||||||
label: "漏洞总数",
|
label: "漏洞总数",
|
||||||
value: totalFindings,
|
value: totalFindings,
|
||||||
color: "text-white",
|
color: "text-foreground",
|
||||||
iconColor: "text-rose-400",
|
iconColor: "text-rose-400",
|
||||||
trend: totalFindings > 0 ? "up" : null,
|
trend: totalFindings > 0 ? "up" : null,
|
||||||
},
|
},
|
||||||
|
|
@ -231,7 +231,7 @@ const EnhancedStatsPanel = memo(function EnhancedStatsPanel({
|
||||||
icon: <AlertTriangle className="w-4 h-4" />,
|
icon: <AlertTriangle className="w-4 h-4" />,
|
||||||
label: "高危问题",
|
label: "高危问题",
|
||||||
value: criticalAndHigh,
|
value: criticalAndHigh,
|
||||||
color: criticalAndHigh > 0 ? "text-rose-400" : "text-slate-400",
|
color: criticalAndHigh > 0 ? "text-rose-400" : "text-muted-foreground",
|
||||||
iconColor: "text-orange-400",
|
iconColor: "text-orange-400",
|
||||||
trend: criticalAndHigh > 0 ? "critical" : null,
|
trend: criticalAndHigh > 0 ? "critical" : null,
|
||||||
},
|
},
|
||||||
|
|
@ -248,7 +248,7 @@ const EnhancedStatsPanel = memo(function EnhancedStatsPanel({
|
||||||
return (
|
return (
|
||||||
<div className="flex items-stretch gap-4">
|
<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} />
|
<CircularProgress value={score} size={72} strokeWidth={5} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -257,13 +257,13 @@ const EnhancedStatsPanel = memo(function EnhancedStatsPanel({
|
||||||
{stats.map((stat, index) => (
|
{stats.map((stat, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
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="flex items-center gap-2 mb-1.5">
|
||||||
<div className={`${stat.iconColor} opacity-80`}>
|
<div className={`${stat.iconColor} opacity-80`}>
|
||||||
{stat.icon}
|
{stat.icon}
|
||||||
</div>
|
</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}
|
{stat.label}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -307,25 +307,25 @@ const FormatSelector = memo(function FormatSelector({
|
||||||
relative p-4 rounded-xl border transition-all duration-300 text-left group
|
relative p-4 rounded-xl border transition-all duration-300 text-left group
|
||||||
${isActive
|
${isActive
|
||||||
? `${config.bgColor} border-opacity-100 shadow-lg`
|
? `${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 && (
|
{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">
|
<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>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<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}
|
{config.icon}
|
||||||
</div>
|
</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}
|
{config.label}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-[10px] text-slate-500">
|
<div className="text-xs text-muted-foreground">
|
||||||
{config.description}
|
{config.description}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -363,19 +363,19 @@ const ExportOptionsPanel = memo(function ExportOptionsPanel({
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
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
|
<button
|
||||||
onClick={onToggle}
|
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">
|
<div className="flex items-center gap-2">
|
||||||
<Settings2 className="w-4 h-4 text-slate-400" />
|
<Settings2 className="w-4 h-4 text-muted-foreground" />
|
||||||
<span className="text-sm font-medium text-slate-300">导出选项</span>
|
<span className="text-sm font-medium text-foreground">导出选项</span>
|
||||||
</div>
|
</div>
|
||||||
{expanded ? (
|
{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>
|
</button>
|
||||||
|
|
||||||
|
|
@ -390,11 +390,11 @@ const ExportOptionsPanel = memo(function ExportOptionsPanel({
|
||||||
{optionItems.map((item) => (
|
{optionItems.map((item) => (
|
||||||
<label
|
<label
|
||||||
key={item.key}
|
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="flex-1">
|
||||||
<div className="text-xs font-medium text-slate-300">{item.label}</div>
|
<div className="text-xs font-medium text-foreground">{item.label}</div>
|
||||||
<div className="text-[10px] text-slate-500">{item.description}</div>
|
<div className="text-xs text-muted-foreground">{item.description}</div>
|
||||||
</div>
|
</div>
|
||||||
<Switch
|
<Switch
|
||||||
checked={options[item.key as keyof ExportOptions]}
|
checked={options[item.key as keyof ExportOptions]}
|
||||||
|
|
@ -425,23 +425,23 @@ const PreviewSearchBar = memo(function PreviewSearchBar({
|
||||||
onClear: () => void;
|
onClear: () => void;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2 px-3 py-2 bg-slate-800/30 border-b border-slate-700/30">
|
<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-slate-500" />
|
<Search className="w-3.5 h-3.5 text-muted-foreground" />
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={searchQuery}
|
value={searchQuery}
|
||||||
onChange={(e) => onSearchChange(e.target.value)}
|
onChange={(e) => onSearchChange(e.target.value)}
|
||||||
placeholder="搜索预览内容..."
|
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 && (
|
{searchQuery && (
|
||||||
<>
|
<>
|
||||||
<span className="text-[10px] text-slate-500 font-mono">
|
<span className="text-xs text-muted-foreground font-mono">
|
||||||
{matchCount} 匹配
|
{matchCount} 匹配
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
onClick={onClear}
|
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" />
|
<X className="w-3 h-3" />
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -455,18 +455,18 @@ const PreviewSearchBar = memo(function PreviewSearchBar({
|
||||||
const PreviewSkeleton = memo(function PreviewSkeleton() {
|
const PreviewSkeleton = memo(function PreviewSkeleton() {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4 animate-pulse">
|
<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="space-y-2">
|
||||||
<div className="h-4 bg-slate-700/20 rounded w-full" />
|
<div className="h-4 bg-muted/20 rounded w-full" />
|
||||||
<div className="h-4 bg-slate-700/20 rounded w-5/6" />
|
<div className="h-4 bg-muted/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-4/6" />
|
||||||
</div>
|
</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="space-y-2">
|
||||||
<div className="h-4 bg-slate-700/20 rounded w-full" />
|
<div className="h-4 bg-muted/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-3/4" />
|
||||||
</div>
|
</div>
|
||||||
<div className="h-16 bg-slate-700/20 rounded" />
|
<div className="h-16 bg-muted/20 rounded" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
@ -486,7 +486,7 @@ const MarkdownPreview = memo(function MarkdownPreview({
|
||||||
const parts = text.split(regex);
|
const parts = text.split(regex);
|
||||||
return parts.map((part, i) =>
|
return parts.map((part, i) =>
|
||||||
regex.test(part) ? (
|
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
|
part
|
||||||
)
|
)
|
||||||
|
|
@ -506,30 +506,30 @@ const MarkdownPreview = memo(function MarkdownPreview({
|
||||||
if (line.startsWith("```")) {
|
if (line.startsWith("```")) {
|
||||||
if (inCodeBlock) {
|
if (inCodeBlock) {
|
||||||
elements.push(
|
elements.push(
|
||||||
<div key={`code-${index}`} className="my-4 rounded-xl bg-[#0d1117] border border-slate-700/50 overflow-hidden shadow-lg">
|
<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-slate-800/80 to-slate-800/40 border-b border-slate-700/50">
|
<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 items-center gap-2">
|
||||||
<div className="flex gap-1.5">
|
<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-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-amber-500/80" />
|
||||||
<div className="w-2.5 h-2.5 rounded-full bg-emerald-500/80" />
|
<div className="w-2.5 h-2.5 rounded-full bg-emerald-500/80" />
|
||||||
</div>
|
</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"}
|
{codeLanguage || "code"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</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>
|
||||||
<div className="relative">
|
<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="absolute left-0 top-0 bottom-0 w-10 bg-background border-r border-border select-none">
|
||||||
<div className="p-3 text-[10px] font-mono text-slate-600 leading-5">
|
<div className="p-3 text-xs font-mono text-muted-foreground leading-5">
|
||||||
{codeContent.map((_, i) => (
|
{codeContent.map((_, i) => (
|
||||||
<div key={i}>{i + 1}</div>
|
<div key={i}>{i + 1}</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</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) => (
|
{codeContent.map((codeLine, i) => (
|
||||||
<div key={i}>{highlightText(codeLine, searchQuery) || " "}</div>
|
<div key={i}>{highlightText(codeLine, searchQuery) || " "}</div>
|
||||||
))}
|
))}
|
||||||
|
|
@ -555,7 +555,7 @@ const MarkdownPreview = memo(function MarkdownPreview({
|
||||||
// Headers with decorative elements
|
// Headers with decorative elements
|
||||||
if (line.startsWith("# ")) {
|
if (line.startsWith("# ")) {
|
||||||
elements.push(
|
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" />
|
<span className="w-1 h-6 bg-primary rounded-full" />
|
||||||
{highlightText(line.slice(2), searchQuery)}
|
{highlightText(line.slice(2), searchQuery)}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
@ -564,7 +564,7 @@ const MarkdownPreview = memo(function MarkdownPreview({
|
||||||
}
|
}
|
||||||
if (line.startsWith("## ")) {
|
if (line.startsWith("## ")) {
|
||||||
elements.push(
|
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" />
|
<Sparkles className="w-4 h-4 text-primary/60" />
|
||||||
{highlightText(line.slice(3), searchQuery)}
|
{highlightText(line.slice(3), searchQuery)}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
@ -573,7 +573,7 @@ const MarkdownPreview = memo(function MarkdownPreview({
|
||||||
}
|
}
|
||||||
if (line.startsWith("### ")) {
|
if (line.startsWith("### ")) {
|
||||||
elements.push(
|
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)}
|
{highlightText(line.slice(4), searchQuery)}
|
||||||
</h3>
|
</h3>
|
||||||
);
|
);
|
||||||
|
|
@ -584,9 +584,9 @@ const MarkdownPreview = memo(function MarkdownPreview({
|
||||||
if (line.match(/^---+$/)) {
|
if (line.match(/^---+$/)) {
|
||||||
elements.push(
|
elements.push(
|
||||||
<div key={index} className="my-6 flex items-center gap-3">
|
<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="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-slate-700" />
|
<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-slate-700 to-transparent" />
|
<div className="flex-1 h-px bg-gradient-to-r from-transparent via-border to-transparent" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
|
@ -595,7 +595,7 @@ const MarkdownPreview = memo(function MarkdownPreview({
|
||||||
// List items with better styling
|
// List items with better styling
|
||||||
if (line.match(/^[-*]\s/)) {
|
if (line.match(/^[-*]\s/)) {
|
||||||
elements.push(
|
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="text-primary mt-1.5 text-xs group-hover:scale-125 transition-transform">●</span>
|
||||||
<span className="flex-1">{highlightText(line.slice(2), searchQuery)}</span>
|
<span className="flex-1">{highlightText(line.slice(2), searchQuery)}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -608,12 +608,12 @@ const MarkdownPreview = memo(function MarkdownPreview({
|
||||||
const parts = line.split(/\*\*(.+?)\*\*/g);
|
const parts = line.split(/\*\*(.+?)\*\*/g);
|
||||||
const lineElements = parts.map((part, i) => {
|
const lineElements = parts.map((part, i) => {
|
||||||
if (i % 2 === 1) {
|
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);
|
return highlightText(part, searchQuery);
|
||||||
});
|
});
|
||||||
elements.push(
|
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}
|
{lineElements}
|
||||||
</p>
|
</p>
|
||||||
);
|
);
|
||||||
|
|
@ -628,7 +628,7 @@ const MarkdownPreview = memo(function MarkdownPreview({
|
||||||
|
|
||||||
// Regular paragraphs
|
// Regular paragraphs
|
||||||
elements.push(
|
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)}
|
{highlightText(line, searchQuery)}
|
||||||
</p>
|
</p>
|
||||||
);
|
);
|
||||||
|
|
@ -663,11 +663,11 @@ const JsonPreview = memo(function JsonPreview({
|
||||||
.replace(/: "([^"]+)"/g, ': <span class="text-emerald-400">"$1"</span>')
|
.replace(/: "([^"]+)"/g, ': <span class="text-emerald-400">"$1"</span>')
|
||||||
.replace(/: (\d+\.?\d*)/g, ': <span class="text-amber-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(/: (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) {
|
if (searchQuery) {
|
||||||
const regex = new RegExp(`(${searchQuery.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
|
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;
|
return result;
|
||||||
|
|
@ -681,15 +681,15 @@ const JsonPreview = memo(function JsonPreview({
|
||||||
return (
|
return (
|
||||||
<div className="relative">
|
<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="absolute left-0 top-0 bottom-0 w-10 bg-background border-r border-border select-none">
|
||||||
<div className="py-3 text-[10px] font-mono text-slate-600 text-right pr-2 leading-5">
|
<div className="py-3 text-xs font-mono text-muted-foreground text-right pr-2 leading-5">
|
||||||
{lines.map((_, i) => (
|
{lines.map((_, i) => (
|
||||||
<div key={i}>{i + 1}</div>
|
<div key={i}>{i + 1}</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<pre
|
<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) }}
|
dangerouslySetInnerHTML={{ __html: highlightJson(content) }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</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(/(<\/?[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(/(\s[a-zA-Z-]+)=/g, '<span class="text-amber-400">$1</span>=')
|
||||||
.replace(/"([^"]*)"/g, '"<span class="text-emerald-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) {
|
if (searchQuery) {
|
||||||
const regex = new RegExp(`(${searchQuery.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
|
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;
|
return result;
|
||||||
|
|
@ -729,12 +729,12 @@ const HtmlPreview = memo(function HtmlPreview({
|
||||||
return (
|
return (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<pre
|
<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) }}
|
dangerouslySetInnerHTML={{ __html: highlightHtml(truncatedContent) }}
|
||||||
/>
|
/>
|
||||||
{isTruncated && (
|
{isTruncated && (
|
||||||
<div className="mt-4 pt-4 border-t border-slate-700/30 text-center">
|
<div className="mt-4 pt-4 border-t border-border text-center">
|
||||||
<span className="text-xs text-slate-500 bg-slate-800/50 px-3 py-1.5 rounded-full">
|
<span className="text-xs text-muted-foreground bg-muted px-3 py-1.5 rounded-full">
|
||||||
已截断显示,完整内容请下载查看
|
已截断显示,完整内容请下载查看
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1622,9 +1622,9 @@ export const ReportExportDialog = memo(function ReportExportDialog({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<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 - 增强设计 */}
|
{/* 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 inset-0 overflow-hidden">
|
||||||
<div className="absolute -top-20 -right-20 w-40 h-40 bg-primary/5 rounded-full blur-3xl" />
|
<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="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">
|
<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" />
|
<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>
|
||||||
<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" />
|
<Sparkles className="w-4 h-4 text-primary/60" />
|
||||||
</DialogTitle>
|
</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" />
|
<Clock className="w-3 h-3" />
|
||||||
{task.name || `Task ${task.id.slice(0, 8)}`}
|
{task.name || `Task ${task.id.slice(0, 8)}`}
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -1651,12 +1651,12 @@ export const ReportExportDialog = memo(function ReportExportDialog({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 快捷键提示 */}
|
{/* 快捷键提示 */}
|
||||||
<div className="hidden md:flex items-center gap-2 text-[10px] text-slate-600">
|
<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-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">
|
||||||
<Keyboard className="w-3 h-3" />
|
<Keyboard className="w-3 h-3" />
|
||||||
<span>⌘S 下载</span>
|
<span>⌘S 下载</span>
|
||||||
</div>
|
</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>
|
<span>1-3 切换</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1667,17 +1667,17 @@ export const ReportExportDialog = memo(function ReportExportDialog({
|
||||||
{/* 主体内容区域 */}
|
{/* 主体内容区域 */}
|
||||||
<div className="flex-1 flex flex-col overflow-hidden">
|
<div className="flex-1 flex flex-col overflow-hidden">
|
||||||
{/* Stats Summary - 增强统计卡片 */}
|
{/* 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} />
|
<EnhancedStatsPanel task={task} findings={findings} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 两栏布局:左侧配置,右侧预览 */}
|
{/* 两栏布局:左侧配置,右侧预览 */}
|
||||||
<div className="flex-1 flex min-h-0">
|
<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>
|
<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" />
|
<FileText className="w-3.5 h-3.5" />
|
||||||
选择格式
|
选择格式
|
||||||
</h3>
|
</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="flex items-center gap-2 mb-2">
|
||||||
<div className={FORMAT_CONFIG[activeFormat].color}>
|
<div className={FORMAT_CONFIG[activeFormat].color}>
|
||||||
{FORMAT_CONFIG[activeFormat].icon}
|
{FORMAT_CONFIG[activeFormat].icon}
|
||||||
</div>
|
</div>
|
||||||
<span className="text-sm font-medium text-white">
|
<span className="text-sm font-medium text-foreground">
|
||||||
{FORMAT_CONFIG[activeFormat].label}
|
{FORMAT_CONFIG[activeFormat].label}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-[11px] text-slate-500 leading-relaxed">
|
<p className="text-xs text-muted-foreground leading-relaxed">
|
||||||
{activeFormat === "markdown" && "Markdown格式便于编辑和版本控制,可用任何文本编辑器打开。"}
|
{activeFormat === "markdown" && "Markdown格式便于编辑和版本控制,可用任何文本编辑器打开。"}
|
||||||
{activeFormat === "json" && "JSON格式包含完整的结构化数据,适合程序处理和数据分析。"}
|
{activeFormat === "json" && "JSON格式包含完整的结构化数据,适合程序处理和数据分析。"}
|
||||||
{activeFormat === "html" && "HTML格式可直接在浏览器中查看,包含完整样式和布局。"}
|
{activeFormat === "html" && "HTML格式可直接在浏览器中查看,包含完整样式和布局。"}
|
||||||
|
|
@ -1714,32 +1714,32 @@ export const ReportExportDialog = memo(function ReportExportDialog({
|
||||||
</div>
|
</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-3">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Eye className="w-4 h-4 text-slate-500" />
|
<Eye className="w-4 h-4 text-muted-foreground" />
|
||||||
<span className="text-xs text-slate-400 font-medium">预览</span>
|
<span className="text-xs text-muted-foreground font-medium">预览</span>
|
||||||
</div>
|
</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)}
|
{formatBytes(preview.content.length)}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<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">
|
<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-slate-500" />
|
<Search className="w-3.5 h-3.5 text-muted-foreground" />
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={searchQuery}
|
value={searchQuery}
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
placeholder="搜索..."
|
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 && (
|
{searchQuery && (
|
||||||
<span className="text-[10px] text-slate-500 font-mono">
|
<span className="text-xs text-muted-foreground font-mono">
|
||||||
{searchMatchCount}
|
{searchMatchCount}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
@ -1751,7 +1751,7 @@ export const ReportExportDialog = memo(function ReportExportDialog({
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={handleCopy}
|
onClick={handleCopy}
|
||||||
disabled={preview.loading || !preview.content}
|
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 ? (
|
{copied ? (
|
||||||
<Check className="w-3.5 h-3.5 mr-1.5 text-emerald-400" />
|
<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"
|
size="sm"
|
||||||
onClick={() => fetchPreview(activeFormat, true)}
|
onClick={() => fetchPreview(activeFormat, true)}
|
||||||
disabled={preview.loading}
|
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' : ''}`} />
|
<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" />
|
<AlertTriangle className="w-8 h-8 text-amber-400" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm text-slate-300 font-medium mb-1">加载失败</p>
|
<p className="text-sm text-foreground font-medium mb-1">加载失败</p>
|
||||||
<p className="text-xs text-slate-500">{preview.error}</p>
|
<p className="text-xs text-muted-foreground">{preview.error}</p>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
|
@ -1802,7 +1802,7 @@ export const ReportExportDialog = memo(function ReportExportDialog({
|
||||||
</div>
|
</div>
|
||||||
</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]">
|
<div className="p-5 min-h-[300px]">
|
||||||
{activeFormat === "markdown" && (
|
{activeFormat === "markdown" && (
|
||||||
<MarkdownPreview content={preview.content} searchQuery={searchQuery} />
|
<MarkdownPreview content={preview.content} searchQuery={searchQuery} />
|
||||||
|
|
@ -1824,9 +1824,9 @@ export const ReportExportDialog = memo(function ReportExportDialog({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer - 增强设计 */}
|
{/* 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 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}`}>
|
<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}>
|
<span className={FORMAT_CONFIG[activeFormat].color}>
|
||||||
{FORMAT_CONFIG[activeFormat].icon}
|
{FORMAT_CONFIG[activeFormat].icon}
|
||||||
|
|
@ -1841,7 +1841,7 @@ export const ReportExportDialog = memo(function ReportExportDialog({
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={() => onOpenChange(false)}
|
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>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -151,7 +151,7 @@ export function SplashScreen({ onComplete }: SplashScreenProps) {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
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 */}
|
{/* Scanline overlay */}
|
||||||
<div className="absolute inset-0 pointer-events-none z-20">
|
<div className="absolute inset-0 pointer-events-none z-20">
|
||||||
<div
|
<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)" }}
|
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-primary">DEEP</span>
|
||||||
<span className="text-white">AUDIT</span>
|
<span className="text-foreground">AUDIT</span>
|
||||||
</div>
|
</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
|
Autonomous Security Agent
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Terminal window */}
|
{/* Terminal window */}
|
||||||
<div
|
<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}
|
onClick={handleTerminalClick}
|
||||||
>
|
>
|
||||||
{/* Terminal header */}
|
{/* 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="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-red-500/80" />
|
||||||
<div className="w-3 h-3 rounded-full bg-yellow-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 className="w-3 h-3 rounded-full bg-green-500/80" />
|
||||||
</div>
|
</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
|
deepaudit@terminal
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -228,7 +228,7 @@ export function SplashScreen({ onComplete }: SplashScreenProps) {
|
||||||
className={`mb-1 ${
|
className={`mb-1 ${
|
||||||
log.includes("[READY]") ? "text-green-400" :
|
log.includes("[READY]") ? "text-green-400" :
|
||||||
log.includes("[INIT]") ? "text-primary" :
|
log.includes("[INIT]") ? "text-primary" :
|
||||||
"text-gray-500"
|
"text-muted-foreground"
|
||||||
}`}
|
}`}
|
||||||
style={{
|
style={{
|
||||||
animation: "fadeSlideIn 0.2s ease-out",
|
animation: "fadeSlideIn 0.2s ease-out",
|
||||||
|
|
@ -236,17 +236,17 @@ export function SplashScreen({ onComplete }: SplashScreenProps) {
|
||||||
animationDelay: `${i * 0.05}s`
|
animationDelay: `${i * 0.05}s`
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span className="text-gray-600 mr-2">></span>
|
<span className="text-muted-foreground mr-2">></span>
|
||||||
{log}
|
{log}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{/* Welcome message */}
|
{/* Welcome message */}
|
||||||
{bootComplete && (
|
{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-primary mb-1">Welcome to DeepAudit Agent Terminal</div>
|
||||||
<div className="text-gray-500 text-xs">
|
<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-gray-400">'help'</span> for commands.
|
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>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -254,13 +254,13 @@ export function SplashScreen({ onComplete }: SplashScreenProps) {
|
||||||
{/* Command history */}
|
{/* Command history */}
|
||||||
{commandHistory.map((entry, i) => (
|
{commandHistory.map((entry, i) => (
|
||||||
<div key={`cmd-${i}`} className="mb-2">
|
<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 className="text-emerald-500">$</span>
|
||||||
<span>{entry.input}</span>
|
<span>{entry.input}</span>
|
||||||
</div>
|
</div>
|
||||||
{entry.output && (
|
{entry.output && (
|
||||||
<div className={`ml-4 mt-1 whitespace-pre-wrap text-xs ${
|
<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}
|
{entry.output}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -270,7 +270,7 @@ export function SplashScreen({ onComplete }: SplashScreenProps) {
|
||||||
|
|
||||||
{/* Current input line */}
|
{/* Current input line */}
|
||||||
{bootComplete && (
|
{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>
|
<span className="text-emerald-500">$</span>
|
||||||
<div className="flex-1 relative">
|
<div className="flex-1 relative">
|
||||||
<input
|
<input
|
||||||
|
|
@ -285,7 +285,7 @@ export function SplashScreen({ onComplete }: SplashScreenProps) {
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
autoFocus
|
autoFocus
|
||||||
/>
|
/>
|
||||||
<span className="text-gray-200">{currentInput}</span>
|
<span className="text-foreground">{currentInput}</span>
|
||||||
<span
|
<span
|
||||||
className={`inline-block w-2 h-4 bg-emerald-400 ml-0.5 align-middle transition-opacity ${
|
className={`inline-block w-2 h-4 bg-emerald-400 ml-0.5 align-middle transition-opacity ${
|
||||||
cursorBlink ? "opacity-100" : "opacity-0"
|
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={`mt-4 text-center transition-all duration-500 ${bootComplete ? "opacity-100" : "opacity-0"}`}>
|
||||||
<div className="flex items-center justify-center gap-3">
|
<div className="flex items-center justify-center gap-3">
|
||||||
<div className="h-px w-8 bg-gradient-to-r from-transparent to-gray-700" />
|
<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 className="h-px w-8 bg-gradient-to-l from-transparent to-gray-700" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -309,18 +309,18 @@ export function SplashScreen({ onComplete }: SplashScreenProps) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Corner decorations */}
|
{/* 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>SYS.VERSION: 3.0.0</div>
|
||||||
<div>MODE: INTERACTIVE</div>
|
<div>MODE: INTERACTIVE</div>
|
||||||
</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>MEM: 16384MB</div>
|
||||||
<div>STATUS: READY</div>
|
<div>STATUS: READY</div>
|
||||||
</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
|
DEEPAUDIT_AGENT_v3
|
||||||
</div>
|
</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]}
|
{new Date().toISOString().split("T")[0]}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ function CircularProgress({ value, size = 48, strokeWidth = 3, color = "primary"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metric card component with enhanced colors
|
// 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;
|
icon: React.ReactNode;
|
||||||
label: string;
|
label: string;
|
||||||
value: string | number;
|
value: string | number;
|
||||||
|
|
@ -68,14 +68,14 @@ function MetricCard({ icon, label, value, suffix = "", colorClass = "text-slate-
|
||||||
colorClass?: string;
|
colorClass?: string;
|
||||||
}) {
|
}) {
|
||||||
return (
|
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}>
|
<div className={colorClass}>
|
||||||
{icon}
|
{icon}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<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-muted-foreground uppercase tracking-wider truncate font-medium">{label}</div>
|
||||||
<div className="text-sm text-white font-mono font-medium">
|
<div className="text-lg text-foreground font-mono font-bold">
|
||||||
{value}<span className="text-slate-500 text-xs">{suffix}</span>
|
{value}<span className="text-muted-foreground text-base">{suffix}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -106,17 +106,17 @@ export const StatsPanel = memo(function StatsPanel({ task, findings }: StatsPane
|
||||||
return (
|
return (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{/* Progress Section */}
|
{/* 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 justify-between mb-3">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Activity className="w-3.5 h-3.5 text-primary" />
|
<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>
|
</div>
|
||||||
<span className="text-xs text-primary font-mono font-bold">{progressPercent.toFixed(0)}%</span>
|
<span className="text-xs text-primary font-mono font-bold">{progressPercent.toFixed(0)}%</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Progress bar */}
|
{/* 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
|
<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"
|
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}%` }}
|
style={{ width: `${progressPercent}%` }}
|
||||||
|
|
@ -132,17 +132,17 @@ export const StatsPanel = memo(function StatsPanel({ task, findings }: StatsPane
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* File progress */}
|
{/* File progress */}
|
||||||
<div className="flex items-center justify-between mt-2 text-[10px]">
|
<div className="flex items-center justify-between mt-3 text-base">
|
||||||
<span className="text-slate-500">Files scanned</span>
|
<span className="text-muted-foreground font-medium">Files scanned</span>
|
||||||
<span className="text-slate-300 font-mono">
|
<span className="text-foreground font-mono font-bold">
|
||||||
{task.analyzed_files}<span className="text-slate-500">/{task.total_files}</span>
|
{task.analyzed_files}<span className="text-muted-foreground font-normal">/{task.total_files}</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{/* Files with findings */}
|
{/* Files with findings */}
|
||||||
{task.files_with_findings > 0 && (
|
{task.files_with_findings > 0 && (
|
||||||
<div className="flex items-center justify-between mt-1 text-[10px]">
|
<div className="flex items-center justify-between mt-2 text-base">
|
||||||
<span className="text-slate-500">Files with findings</span>
|
<span className="text-muted-foreground font-medium">Files with findings</span>
|
||||||
<span className="text-rose-400 font-mono font-medium">
|
<span className="text-rose-500 dark:text-rose-400 font-mono font-bold">
|
||||||
{task.files_with_findings}
|
{task.files_with_findings}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -152,58 +152,58 @@ export const StatsPanel = memo(function StatsPanel({ task, findings }: StatsPane
|
||||||
{/* Metrics Grid */}
|
{/* Metrics Grid */}
|
||||||
<div className="grid grid-cols-2 gap-2">
|
<div className="grid grid-cols-2 gap-2">
|
||||||
<MetricCard
|
<MetricCard
|
||||||
icon={<Repeat className="w-3.5 h-3.5" />}
|
icon={<Repeat className="w-4 h-4" />}
|
||||||
label="Iterations"
|
label="Iterations"
|
||||||
value={task.total_iterations || 0}
|
value={task.total_iterations || 0}
|
||||||
colorClass="text-teal-400"
|
colorClass="text-teal-600 dark:text-teal-400"
|
||||||
/>
|
/>
|
||||||
<MetricCard
|
<MetricCard
|
||||||
icon={<Zap className="w-3.5 h-3.5" />}
|
icon={<Zap className="w-4 h-4" />}
|
||||||
label="Tool Calls"
|
label="Tool Calls"
|
||||||
value={task.tool_calls_count || 0}
|
value={task.tool_calls_count || 0}
|
||||||
colorClass="text-amber-400"
|
colorClass="text-amber-600 dark:text-amber-400"
|
||||||
/>
|
/>
|
||||||
<MetricCard
|
<MetricCard
|
||||||
icon={<FileCode className="w-3.5 h-3.5" />}
|
icon={<FileCode className="w-4 h-4" />}
|
||||||
label="Tokens"
|
label="Tokens"
|
||||||
value={((task.tokens_used || 0) / 1000).toFixed(1)}
|
value={((task.tokens_used || 0) / 1000).toFixed(1)}
|
||||||
suffix="k"
|
suffix="k"
|
||||||
colorClass="text-violet-400"
|
colorClass="text-violet-600 dark:text-violet-400"
|
||||||
/>
|
/>
|
||||||
<MetricCard
|
<MetricCard
|
||||||
icon={<Bug className="w-3.5 h-3.5" />}
|
icon={<Bug className="w-4 h-4" />}
|
||||||
label="Findings"
|
label="Findings"
|
||||||
value={totalFindings}
|
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>
|
</div>
|
||||||
|
|
||||||
{/* Findings breakdown */}
|
{/* Findings breakdown */}
|
||||||
{totalFindings > 0 && (
|
{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">
|
<div className="flex items-center gap-2 mb-2">
|
||||||
<AlertTriangle className="w-3.5 h-3.5 text-rose-400" />
|
<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>
|
||||||
|
|
||||||
<div className="flex flex-wrap gap-1.5">
|
<div className="flex flex-wrap gap-1.5">
|
||||||
{severityCounts.critical > 0 && (
|
{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}
|
CRIT: {severityCounts.critical}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
{severityCounts.high > 0 && (
|
{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}
|
HIGH: {severityCounts.high}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
{severityCounts.medium > 0 && (
|
{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}
|
MED: {severityCounts.medium}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
{severityCounts.low > 0 && (
|
{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}
|
LOW: {severityCounts.low}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
|
|
@ -213,11 +213,11 @@ export const StatsPanel = memo(function StatsPanel({ task, findings }: StatsPane
|
||||||
|
|
||||||
{/* Security Score */}
|
{/* Security Score */}
|
||||||
{task.security_score !== null && task.security_score !== undefined && (
|
{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 justify-between">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Shield className="w-3.5 h-3.5 text-emerald-400" />
|
<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>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<CircularProgress
|
<CircularProgress
|
||||||
|
|
@ -227,9 +227,9 @@ export const StatsPanel = memo(function StatsPanel({ task, findings }: StatsPane
|
||||||
color={getScoreColor(task.security_score)}
|
color={getScoreColor(task.security_score)}
|
||||||
/>
|
/>
|
||||||
<div className="absolute inset-0 flex items-center justify-center">
|
<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' :
|
<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-400' :
|
task.security_score >= 60 ? 'text-amber-600 dark:text-amber-400' :
|
||||||
'text-rose-400'
|
'text-rose-600 dark:text-rose-400'
|
||||||
}`}>
|
}`}>
|
||||||
{task.security_score.toFixed(0)}
|
{task.security_score.toFixed(0)}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
||||||
|
|
@ -24,48 +24,48 @@ const STATUS_CONFIG: Record<string, {
|
||||||
pending: {
|
pending: {
|
||||||
icon: <Clock className="w-3.5 h-3.5" />,
|
icon: <Clock className="w-3.5 h-3.5" />,
|
||||||
iconSm: <Clock className="w-3 h-3" />,
|
iconSm: <Clock className="w-3 h-3" />,
|
||||||
bg: "bg-gray-800/80 border-gray-600/50",
|
bg: "bg-muted border-border",
|
||||||
text: "text-gray-300",
|
text: "text-foreground",
|
||||||
label: "PENDING",
|
label: "PENDING",
|
||||||
},
|
},
|
||||||
running: {
|
running: {
|
||||||
icon: <Loader2 className="w-3.5 h-3.5 animate-spin" />,
|
icon: <Loader2 className="w-3.5 h-3.5 animate-spin" />,
|
||||||
iconSm: <Loader2 className="w-3 h-3 animate-spin" />,
|
iconSm: <Loader2 className="w-3 h-3 animate-spin" />,
|
||||||
bg: "bg-green-950/80 border-green-500/50",
|
bg: "bg-green-100 dark:bg-green-950/80 border-green-500/50",
|
||||||
text: "text-green-400",
|
text: "text-green-700 dark:text-green-400",
|
||||||
label: "RUNNING",
|
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,
|
animate: true,
|
||||||
},
|
},
|
||||||
completed: {
|
completed: {
|
||||||
icon: <CheckCircle2 className="w-3.5 h-3.5" />,
|
icon: <CheckCircle2 className="w-3.5 h-3.5" />,
|
||||||
iconSm: <CheckCircle2 className="w-3 h-3" />,
|
iconSm: <CheckCircle2 className="w-3 h-3" />,
|
||||||
bg: "bg-green-950/60 border-green-600/50",
|
bg: "bg-green-100 dark:bg-green-950/60 border-green-600/50",
|
||||||
text: "text-green-400",
|
text: "text-green-700 dark:text-green-400",
|
||||||
label: "COMPLETED",
|
label: "COMPLETED",
|
||||||
},
|
},
|
||||||
failed: {
|
failed: {
|
||||||
icon: <XCircle className="w-3.5 h-3.5" />,
|
icon: <XCircle className="w-3.5 h-3.5" />,
|
||||||
iconSm: <XCircle className="w-3 h-3" />,
|
iconSm: <XCircle className="w-3 h-3" />,
|
||||||
bg: "bg-red-950/60 border-red-600/50",
|
bg: "bg-red-100 dark:bg-red-950/60 border-red-600/50",
|
||||||
text: "text-red-400",
|
text: "text-red-700 dark:text-red-400",
|
||||||
label: "FAILED",
|
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: {
|
cancelled: {
|
||||||
icon: <Square className="w-3.5 h-3.5" />,
|
icon: <Square className="w-3.5 h-3.5" />,
|
||||||
iconSm: <Square className="w-3 h-3" />,
|
iconSm: <Square className="w-3 h-3" />,
|
||||||
bg: "bg-yellow-950/60 border-yellow-600/50",
|
bg: "bg-yellow-100 dark:bg-yellow-950/60 border-yellow-600/50",
|
||||||
text: "text-yellow-400",
|
text: "text-yellow-700 dark:text-yellow-400",
|
||||||
label: "CANCELLED",
|
label: "CANCELLED",
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
icon: <AlertCircle className="w-3.5 h-3.5" />,
|
icon: <AlertCircle className="w-3.5 h-3.5" />,
|
||||||
iconSm: <AlertCircle className="w-3 h-3" />,
|
iconSm: <AlertCircle className="w-3 h-3" />,
|
||||||
bg: "bg-red-950/60 border-red-600/50",
|
bg: "bg-red-100 dark:bg-red-950/60 border-red-600/50",
|
||||||
text: "text-red-400",
|
text: "text-red-700 dark:text-red-400",
|
||||||
label: "ERROR",
|
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.bg}
|
||||||
${config.text}
|
${config.text}
|
||||||
${config.glow || ''}
|
${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}
|
{isSmall ? config.iconSm : config.icon}
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,11 @@ import {
|
||||||
// ============ Severity Colors (Enhanced contrast) ============
|
// ============ Severity Colors (Enhanced contrast) ============
|
||||||
|
|
||||||
export const SEVERITY_COLORS: Record<string, string> = {
|
export const SEVERITY_COLORS: Record<string, string> = {
|
||||||
critical: "text-rose-300 bg-rose-500/20 border border-rose-500/40",
|
critical: "text-rose-700 dark: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",
|
high: "text-orange-700 dark: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",
|
medium: "text-amber-700 dark: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",
|
low: "text-sky-700 dark: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",
|
info: "text-foreground bg-muted/20 border border-border",
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============ Action Verbs for Animation ============
|
// ============ Action Verbs for Animation ============
|
||||||
|
|
@ -39,47 +39,47 @@ export const LOG_TYPE_CONFIG: Record<string, {
|
||||||
bgColor: string;
|
bgColor: string;
|
||||||
}> = {
|
}> = {
|
||||||
thinking: {
|
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",
|
borderColor: "border-l-violet-500",
|
||||||
bgColor: "bg-violet-500/10"
|
bgColor: "bg-violet-500/10"
|
||||||
},
|
},
|
||||||
tool: {
|
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",
|
borderColor: "border-l-amber-500",
|
||||||
bgColor: "bg-amber-500/10"
|
bgColor: "bg-amber-500/10"
|
||||||
},
|
},
|
||||||
phase: {
|
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",
|
borderColor: "border-l-teal-500",
|
||||||
bgColor: "bg-teal-500/10"
|
bgColor: "bg-teal-500/10"
|
||||||
},
|
},
|
||||||
finding: {
|
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",
|
borderColor: "border-l-rose-500",
|
||||||
bgColor: "bg-rose-500/10"
|
bgColor: "bg-rose-500/10"
|
||||||
},
|
},
|
||||||
dispatch: {
|
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",
|
borderColor: "border-l-sky-500",
|
||||||
bgColor: "bg-sky-500/10"
|
bgColor: "bg-sky-500/10"
|
||||||
},
|
},
|
||||||
info: {
|
info: {
|
||||||
icon: React.createElement(Terminal, { className: "w-4 h-4 text-slate-400" }),
|
icon: React.createElement(Terminal, { className: "w-4 h-4 text-muted-foreground" }),
|
||||||
borderColor: "border-l-slate-500",
|
borderColor: "border-l-muted-foreground",
|
||||||
bgColor: "bg-slate-500/10"
|
bgColor: "bg-muted/10"
|
||||||
},
|
},
|
||||||
error: {
|
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",
|
borderColor: "border-l-red-500",
|
||||||
bgColor: "bg-red-500/15"
|
bgColor: "bg-red-500/15"
|
||||||
},
|
},
|
||||||
user: {
|
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",
|
borderColor: "border-l-indigo-500",
|
||||||
bgColor: "bg-indigo-500/10"
|
bgColor: "bg-indigo-500/10"
|
||||||
},
|
},
|
||||||
progress: {
|
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",
|
borderColor: "border-l-cyan-500",
|
||||||
bgColor: "bg-cyan-500/10"
|
bgColor: "bg-cyan-500/10"
|
||||||
},
|
},
|
||||||
|
|
@ -94,29 +94,29 @@ export const AGENT_STATUS_CONFIG: Record<string, {
|
||||||
animate?: boolean;
|
animate?: boolean;
|
||||||
}> = {
|
}> = {
|
||||||
running: {
|
running: {
|
||||||
icon: React.createElement("div", { className: "w-2 h-2 rounded-full bg-emerald-400" }),
|
icon: React.createElement("div", { className: "w-2 h-2 rounded-full bg-emerald-500 dark:bg-emerald-400" }),
|
||||||
color: "text-emerald-400",
|
color: "text-emerald-600 dark:text-emerald-400",
|
||||||
text: "Running",
|
text: "Running",
|
||||||
animate: true
|
animate: true
|
||||||
},
|
},
|
||||||
completed: {
|
completed: {
|
||||||
icon: React.createElement(CheckCircle2, { className: "w-3 h-3 text-emerald-400" }),
|
icon: React.createElement(CheckCircle2, { className: "w-3 h-3 text-emerald-600 dark:text-emerald-400" }),
|
||||||
color: "text-emerald-400",
|
color: "text-emerald-600 dark:text-emerald-400",
|
||||||
text: "Completed"
|
text: "Completed"
|
||||||
},
|
},
|
||||||
failed: {
|
failed: {
|
||||||
icon: React.createElement(XCircle, { className: "w-3 h-3 text-rose-400" }),
|
icon: React.createElement(XCircle, { className: "w-3 h-3 text-rose-600 dark:text-rose-400" }),
|
||||||
color: "text-rose-400",
|
color: "text-rose-600 dark:text-rose-400",
|
||||||
text: "Failed"
|
text: "Failed"
|
||||||
},
|
},
|
||||||
waiting: {
|
waiting: {
|
||||||
icon: React.createElement(Clock, { className: "w-3 h-3 text-amber-400" }),
|
icon: React.createElement(Clock, { className: "w-3 h-3 text-amber-600 dark:text-amber-400" }),
|
||||||
color: "text-amber-400",
|
color: "text-amber-600 dark:text-amber-400",
|
||||||
text: "Waiting"
|
text: "Waiting"
|
||||||
},
|
},
|
||||||
created: {
|
created: {
|
||||||
icon: React.createElement("div", { className: "w-2 h-2 rounded-full bg-slate-500" }),
|
icon: React.createElement("div", { className: "w-2 h-2 rounded-full bg-muted" }),
|
||||||
color: "text-slate-400",
|
color: "text-muted-foreground",
|
||||||
text: "Created"
|
text: "Created"
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -129,22 +129,22 @@ export const AGENT_TYPE_CONFIG: Record<string, {
|
||||||
color: string;
|
color: string;
|
||||||
}> = {
|
}> = {
|
||||||
orchestrator: {
|
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",
|
label: "Orchestrator",
|
||||||
color: "violet"
|
color: "violet"
|
||||||
},
|
},
|
||||||
recon: {
|
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",
|
label: "Reconnaissance",
|
||||||
color: "teal"
|
color: "teal"
|
||||||
},
|
},
|
||||||
analysis: {
|
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",
|
label: "Analysis",
|
||||||
color: "amber"
|
color: "amber"
|
||||||
},
|
},
|
||||||
verification: {
|
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",
|
label: "Verification",
|
||||||
color: "emerald"
|
color: "emerald"
|
||||||
},
|
},
|
||||||
|
|
@ -158,7 +158,7 @@ export const TASK_STATUS_CONFIG: Record<string, {
|
||||||
text: string;
|
text: string;
|
||||||
}> = {
|
}> = {
|
||||||
pending: {
|
pending: {
|
||||||
bg: "bg-slate-600",
|
bg: "bg-muted",
|
||||||
icon: React.createElement(Clock, { className: "w-3 h-3" }),
|
icon: React.createElement(Clock, { className: "w-3 h-3" }),
|
||||||
text: "PENDING"
|
text: "PENDING"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -757,12 +757,12 @@ function AgentAuditPageContent() {
|
||||||
|
|
||||||
if (isLoading && !task) {
|
if (isLoading && !task) {
|
||||||
return (
|
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 */}
|
{/* Grid background */}
|
||||||
<div className="absolute inset-0 cyber-grid opacity-30" />
|
<div className="absolute inset-0 cyber-grid opacity-30" />
|
||||||
{/* Vignette */}
|
{/* Vignette */}
|
||||||
<div className="absolute inset-0 vignette pointer-events-none" />
|
<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" />
|
<Loader2 className="w-5 h-5 animate-spin text-primary" />
|
||||||
<span className="font-mono text-sm tracking-wide">LOADING AUDIT TASK...</span>
|
<span className="font-mono text-sm tracking-wide">LOADING AUDIT TASK...</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -771,7 +771,7 @@ function AgentAuditPageContent() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
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 */}
|
{/* Subtle grid background */}
|
||||||
<div className="absolute inset-0 cyber-grid-subtle opacity-40 pointer-events-none" />
|
<div className="absolute inset-0 cyber-grid-subtle opacity-40 pointer-events-none" />
|
||||||
{/* Scanline effect */}
|
{/* Scanline effect */}
|
||||||
|
|
@ -790,24 +790,24 @@ function AgentAuditPageContent() {
|
||||||
{/* Main content */}
|
{/* Main content */}
|
||||||
<div className="flex-1 flex overflow-hidden relative">
|
<div className="flex-1 flex overflow-hidden relative">
|
||||||
{/* Left Panel - Activity Log */}
|
{/* 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 */}
|
{/* 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-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-[#8a95a5]">
|
<div className="flex items-center gap-3 text-xs text-muted-foreground">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Terminal className="w-4 h-4 text-[#5a6577]" />
|
<Terminal className="w-4 h-4 text-muted-foreground" />
|
||||||
<span className="uppercase font-bold tracking-[0.15em] text-[#d0d8e8]">Activity Log</span>
|
<span className="uppercase font-bold tracking-[0.15em] text-foreground">Activity Log</span>
|
||||||
</div>
|
</div>
|
||||||
{isConnected && (
|
{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="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="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-[#3dd68c] shadow-[0_0_8px_rgba(61,214,140,0.5)]"></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>
|
||||||
<span className="text-[10px] font-mono uppercase tracking-wider">Live</span>
|
<span className="text-xs font-mono uppercase tracking-wider">Live</span>
|
||||||
</div>
|
</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}` : ''}
|
{filteredLogs.length}{!showAllLogs && logs.length !== filteredLogs.length ? ` / ${logs.length}` : ''}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -815,11 +815,11 @@ function AgentAuditPageContent() {
|
||||||
<button
|
<button
|
||||||
onClick={() => setAutoScroll(!isAutoScroll)}
|
onClick={() => setAutoScroll(!isAutoScroll)}
|
||||||
className={`
|
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
|
transition-all duration-200
|
||||||
${isAutoScroll
|
${isAutoScroll
|
||||||
? 'bg-primary/15 text-primary border border-primary/40 shadow-[0_0_10px_rgba(255,95,31,0.15)]'
|
? '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>
|
</div>
|
||||||
|
|
||||||
{/* Log content */}
|
{/* 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 */}
|
{/* Filter indicator */}
|
||||||
{selectedAgentId && !showAllLogs && (
|
{selectedAgentId && !showAllLogs && (
|
||||||
<div className="mb-3 px-3 py-2 bg-primary/8 border border-primary/25 rounded flex items-center justify-between">
|
<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>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => selectAgent(null)}
|
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
|
Clear
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -849,10 +849,10 @@ function AgentAuditPageContent() {
|
||||||
{/* Logs */}
|
{/* Logs */}
|
||||||
{filteredLogs.length === 0 ? (
|
{filteredLogs.length === 0 ? (
|
||||||
<div className="h-full flex items-center justify-center">
|
<div className="h-full flex items-center justify-center">
|
||||||
<div className="text-center text-[#4a5565]">
|
<div className="text-center text-muted-foreground">
|
||||||
{isRunning ? (
|
{isRunning ? (
|
||||||
<div className="flex flex-col items-center gap-3">
|
<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">
|
<span className="text-sm font-mono tracking-wide">
|
||||||
{selectedAgentId && !showAllLogs
|
{selectedAgentId && !showAllLogs
|
||||||
? 'WAITING FOR ACTIVITY FROM SELECTED AGENT...'
|
? 'WAITING FOR ACTIVITY FROM SELECTED AGENT...'
|
||||||
|
|
@ -885,36 +885,36 @@ function AgentAuditPageContent() {
|
||||||
|
|
||||||
{/* Status bar */}
|
{/* Status bar */}
|
||||||
{task && (
|
{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>
|
<span>
|
||||||
{isRunning ? (
|
{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="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="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-[#3dd68c] shadow-[0_0_6px_rgba(61,214,140,0.5)]"></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>
|
||||||
<span className="font-mono tracking-wide">{statusVerb}{'.'.repeat(statusDots)}</span>
|
<span className="font-mono tracking-wide">{statusVerb}{'.'.repeat(statusDots)}</span>
|
||||||
</span>
|
</span>
|
||||||
) : isComplete ? (
|
) : 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>
|
</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>
|
||||||
<span className="text-primary text-glow-primary">{task.progress_percentage?.toFixed(0) || 0}</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>
|
||||||
<span className="text-[#2a3545]">│</span>
|
<span className="text-border">│</span>
|
||||||
<span>
|
<span>
|
||||||
<span className="text-[#a8b0c0]">{task.analyzed_files}</span>
|
<span className="text-foreground">{task.analyzed_files}</span>
|
||||||
<span className="text-[#4a5565]">/{task.total_files} files</span>
|
<span className="text-muted-foreground">/{task.total_files} files</span>
|
||||||
</span>
|
</span>
|
||||||
<span className="text-[#2a3545]">│</span>
|
<span className="text-border">│</span>
|
||||||
<span>
|
<span>
|
||||||
<span className="text-[#a8b0c0]">{task.tool_calls_count || 0}</span>
|
<span className="text-foreground">{task.tool_calls_count || 0}</span>
|
||||||
<span className="text-[#4a5565]"> tools</span>
|
<span className="text-muted-foreground"> tools</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -922,16 +922,16 @@ function AgentAuditPageContent() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right Panel - Agent Tree + Stats */}
|
{/* 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 */}
|
{/* 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 */}
|
{/* 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-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-[#8a95a5]">
|
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
||||||
<Bot className="w-4 h-4 text-[#5a6577]" />
|
<Bot className="w-4 h-4 text-muted-foreground" />
|
||||||
<span className="uppercase font-bold tracking-[0.15em] text-[#d0d8e8]">Agent Tree</span>
|
<span className="uppercase font-bold tracking-[0.15em] text-foreground">Agent Tree</span>
|
||||||
{agentTree && (
|
{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}
|
{agentTree.total_agents}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
|
|
@ -940,25 +940,25 @@ function AgentAuditPageContent() {
|
||||||
{selectedAgentId && !showAllLogs && (
|
{selectedAgentId && !showAllLogs && (
|
||||||
<button
|
<button
|
||||||
onClick={() => selectAgent(null)}
|
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
|
Show All
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{agentTree && agentTree.running_agents > 0 && (
|
{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="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="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-[#3dd68c] shadow-[0_0_6px_rgba(61,214,140,0.5)]"></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>
|
||||||
<span className="text-[10px] font-mono">{agentTree.running_agents}</span>
|
<span className="text-xs font-mono">{agentTree.running_agents}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Tree content */}
|
{/* 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.length > 0 ? (
|
||||||
treeNodes.map(node => (
|
treeNodes.map(node => (
|
||||||
<AgentTreeNodeItem
|
<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 ? (
|
{isRunning ? (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Loader2 className="w-3 h-3 animate-spin" />
|
<Loader2 className="w-3 h-3 animate-spin" />
|
||||||
|
|
@ -984,7 +984,7 @@ function AgentAuditPageContent() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Bottom section - Details + Stats */}
|
{/* 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 */}
|
{/* Agent detail panel */}
|
||||||
{selectedAgentId && !showAllLogs && (
|
{selectedAgentId && !showAllLogs && (
|
||||||
<AgentDetailPanel
|
<AgentDetailPanel
|
||||||
|
|
|
||||||
|
|
@ -235,17 +235,17 @@ export default function AuditRules() {
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
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="text-center space-y-4">
|
||||||
<div className="loading-spinner mx-auto" />
|
<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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
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 */}
|
{/* Grid background */}
|
||||||
<div className="absolute inset-0 cyber-grid-subtle pointer-events-none" />
|
<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 p-0 relative z-10">
|
||||||
<div className="cyber-card-header">
|
<div className="cyber-card-header">
|
||||||
<Terminal className="w-5 h-5 text-primary" />
|
<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">
|
<div className="ml-auto flex gap-2">
|
||||||
<Button variant="outline" onClick={() => setShowImportDialog(true)} className="cyber-btn-outline h-9">
|
<Button variant="outline" onClick={() => setShowImportDialog(true)} className="cyber-btn-outline h-9">
|
||||||
<Upload className="w-4 h-4 mr-2" />
|
<Upload className="w-4 h-4 mr-2" />
|
||||||
|
|
@ -336,26 +336,26 @@ export default function AuditRules() {
|
||||||
ruleSets.map(ruleSet => (
|
ruleSets.map(ruleSet => (
|
||||||
<div key={ruleSet.id} className={`cyber-card p-0 ${!ruleSet.is_active ? 'opacity-60' : ''}`}>
|
<div key={ruleSet.id} className={`cyber-card p-0 ${!ruleSet.is_active ? 'opacity-60' : ''}`}>
|
||||||
{/* Rule Set Header */}
|
{/* 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 justify-between">
|
||||||
<div className="flex items-center gap-4 cursor-pointer" onClick={() => toggleExpand(ruleSet.id)}>
|
<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">
|
<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-gray-400" /> : <ChevronRight className="w-5 h-5 text-gray-400" />}
|
{expandedSets.has(ruleSet.id) ? <ChevronDown className="w-5 h-5 text-muted-foreground" /> : <ChevronRight className="w-5 h-5 text-muted-foreground" />}
|
||||||
</div>
|
</div>
|
||||||
<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.name}
|
||||||
{ruleSet.is_system && <Badge className="cyber-badge-info">系统</Badge>}
|
{ruleSet.is_system && <Badge className="cyber-badge-info">系统</Badge>}
|
||||||
{ruleSet.is_default && <Badge className="cyber-badge-success">默认</Badge>}
|
{ruleSet.is_default && <Badge className="cyber-badge-success">默认</Badge>}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm text-gray-500">{ruleSet.description}</p>
|
<p className="text-sm text-muted-foreground">{ruleSet.description}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-3">
|
<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">{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>
|
<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} 启用
|
{ruleSet.enabled_rules_count}/{ruleSet.rules_count} 启用
|
||||||
</span>
|
</span>
|
||||||
<Button variant="ghost" size="icon" onClick={() => handleExport(ruleSet)} className="cyber-btn-ghost h-9 w-9">
|
<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 severityInfo = getSeverityInfo(rule.severity);
|
||||||
const CategoryIcon = categoryInfo.icon;
|
const CategoryIcon = categoryInfo.icon;
|
||||||
return (
|
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 justify-between">
|
||||||
<div className="flex items-start gap-4">
|
<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}`} />
|
<CategoryIcon className={`w-5 h-5 ${categoryInfo.color}`} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center gap-2 mb-1">
|
<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-mono text-xs bg-muted 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-bold uppercase text-foreground">{rule.name}</span>
|
||||||
<Badge className={severityInfo.color}>{severityInfo.label}</Badge>
|
<Badge className={severityInfo.color}>{severityInfo.label}</Badge>
|
||||||
</div>
|
</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 && (
|
{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">
|
<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" />
|
参考链接 <ExternalLink className="w-3 h-3" />
|
||||||
|
|
@ -435,9 +435,9 @@ export default function AuditRules() {
|
||||||
|
|
||||||
{/* Create Rule Set Dialog */}
|
{/* Create Rule Set Dialog */}
|
||||||
<Dialog open={showCreateDialog} onOpenChange={setShowCreateDialog}>
|
<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">
|
<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-gray-800 flex-shrink-0 bg-gray-900/50">
|
<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-white">
|
<DialogTitle className="flex items-center gap-3 font-mono text-foreground">
|
||||||
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
||||||
<Terminal className="w-5 h-5 text-primary" />
|
<Terminal className="w-5 h-5 text-primary" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -446,35 +446,35 @@ export default function AuditRules() {
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="flex-1 overflow-y-auto p-6 space-y-4">
|
<div className="flex-1 overflow-y-auto p-6 space-y-4">
|
||||||
<div className="space-y-2">
|
<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" />
|
<Input value={ruleSetForm.name} onChange={e => setRuleSetForm({ ...ruleSetForm, name: e.target.value })} placeholder="规则集名称" className="cyber-input" />
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<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" />
|
<Textarea value={ruleSetForm.description} onChange={e => setRuleSetForm({ ...ruleSetForm, description: e.target.value })} placeholder="规则集描述" className="cyber-input" />
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<div className="space-y-2">
|
<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 })}>
|
<Select value={ruleSetForm.language} onValueChange={v => setRuleSetForm({ ...ruleSetForm, language: v })}>
|
||||||
<SelectTrigger className="cyber-input"><SelectValue /></SelectTrigger>
|
<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>)}
|
{LANGUAGES.map(l => <SelectItem key={l.value} value={l.value}>{l.label}</SelectItem>)}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<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 })}>
|
<Select value={ruleSetForm.rule_type} onValueChange={v => setRuleSetForm({ ...ruleSetForm, rule_type: v })}>
|
||||||
<SelectTrigger className="cyber-input"><SelectValue /></SelectTrigger>
|
<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>)}
|
{RULE_TYPES.map(t => <SelectItem key={t.value} value={t.value}>{t.label}</SelectItem>)}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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 variant="outline" onClick={() => setShowCreateDialog(false)} className="cyber-btn-outline">取消</Button>
|
||||||
<Button onClick={handleCreateRuleSet} className="cyber-btn-primary">创建</Button>
|
<Button onClick={handleCreateRuleSet} className="cyber-btn-primary">创建</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
|
|
@ -483,9 +483,9 @@ export default function AuditRules() {
|
||||||
|
|
||||||
{/* Edit Rule Set Dialog */}
|
{/* Edit Rule Set Dialog */}
|
||||||
<Dialog open={showEditDialog} onOpenChange={setShowEditDialog}>
|
<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">
|
<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-gray-800 flex-shrink-0 bg-gray-900/50">
|
<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-white">
|
<DialogTitle className="flex items-center gap-3 font-mono text-foreground">
|
||||||
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
||||||
<Edit className="w-5 h-5 text-primary" />
|
<Edit className="w-5 h-5 text-primary" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -494,31 +494,31 @@ export default function AuditRules() {
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="flex-1 overflow-y-auto p-6 space-y-4">
|
<div className="flex-1 overflow-y-auto p-6 space-y-4">
|
||||||
<div className="space-y-2">
|
<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" />
|
<Input value={ruleSetForm.name} onChange={e => setRuleSetForm({ ...ruleSetForm, name: e.target.value })} className="cyber-input" />
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<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" />
|
<Textarea value={ruleSetForm.description} onChange={e => setRuleSetForm({ ...ruleSetForm, description: e.target.value })} className="cyber-input" />
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<div className="space-y-2">
|
<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 })}>
|
<Select value={ruleSetForm.language} onValueChange={v => setRuleSetForm({ ...ruleSetForm, language: v })}>
|
||||||
<SelectTrigger className="cyber-input"><SelectValue /></SelectTrigger>
|
<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>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<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 })}>
|
<Select value={ruleSetForm.rule_type} onValueChange={v => setRuleSetForm({ ...ruleSetForm, rule_type: v })}>
|
||||||
<SelectTrigger className="cyber-input"><SelectValue /></SelectTrigger>
|
<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>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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 variant="outline" onClick={() => setShowEditDialog(false)} className="cyber-btn-outline">取消</Button>
|
||||||
<Button onClick={handleUpdateRuleSet} className="cyber-btn-primary">保存</Button>
|
<Button onClick={handleUpdateRuleSet} className="cyber-btn-primary">保存</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
|
|
@ -527,9 +527,9 @@ export default function AuditRules() {
|
||||||
|
|
||||||
{/* Rule Edit Dialog */}
|
{/* Rule Edit Dialog */}
|
||||||
<Dialog open={showRuleDialog} onOpenChange={setShowRuleDialog}>
|
<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">
|
<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-gray-800 flex-shrink-0 bg-gray-900/50">
|
<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-white">
|
<DialogTitle className="flex items-center gap-3 font-mono text-foreground">
|
||||||
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
||||||
<Code className="w-5 h-5 text-primary" />
|
<Code className="w-5 h-5 text-primary" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -539,48 +539,48 @@ export default function AuditRules() {
|
||||||
<div className="flex-1 overflow-y-auto p-6 space-y-4">
|
<div className="flex-1 overflow-y-auto p-6 space-y-4">
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<div className="space-y-2">
|
<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" />
|
<Input value={ruleForm.rule_code} onChange={e => setRuleForm({ ...ruleForm, rule_code: e.target.value })} placeholder="如 SEC001" className="cyber-input" />
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<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" />
|
<Input value={ruleForm.name} onChange={e => setRuleForm({ ...ruleForm, name: e.target.value })} placeholder="规则名称" className="cyber-input" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<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" />
|
<Textarea value={ruleForm.description} onChange={e => setRuleForm({ ...ruleForm, description: e.target.value })} placeholder="规则描述" className="cyber-input" />
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<div className="space-y-2">
|
<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 })}>
|
<Select value={ruleForm.category} onValueChange={v => setRuleForm({ ...ruleForm, category: v })}>
|
||||||
<SelectTrigger className="cyber-input"><SelectValue /></SelectTrigger>
|
<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>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<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 })}>
|
<Select value={ruleForm.severity} onValueChange={v => setRuleForm({ ...ruleForm, severity: v })}>
|
||||||
<SelectTrigger className="cyber-input"><SelectValue /></SelectTrigger>
|
<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>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<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" />
|
<Textarea value={ruleForm.custom_prompt} onChange={e => setRuleForm({ ...ruleForm, custom_prompt: e.target.value })} placeholder="用于增强LLM检测的自定义提示词" rows={3} className="cyber-input" />
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<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" />
|
<Textarea value={ruleForm.fix_suggestion} onChange={e => setRuleForm({ ...ruleForm, fix_suggestion: e.target.value })} placeholder="修复建议模板" rows={2} className="cyber-input" />
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<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" />
|
<Input value={ruleForm.reference_url} onChange={e => setRuleForm({ ...ruleForm, reference_url: e.target.value })} placeholder="如 https://owasp.org/..." className="cyber-input" />
|
||||||
</div>
|
</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={() => setShowRuleDialog(false)} className="cyber-btn-outline">取消</Button>
|
<Button variant="outline" onClick={() => setShowRuleDialog(false)} className="cyber-btn-outline">取消</Button>
|
||||||
<Button onClick={selectedRule ? handleUpdateRule : handleAddRule} className="cyber-btn-primary">{selectedRule ? '保存' : '添加'}</Button>
|
<Button onClick={selectedRule ? handleUpdateRule : handleAddRule} className="cyber-btn-primary">{selectedRule ? '保存' : '添加'}</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
|
|
@ -589,22 +589,22 @@ export default function AuditRules() {
|
||||||
|
|
||||||
{/* Import Dialog */}
|
{/* Import Dialog */}
|
||||||
<Dialog open={showImportDialog} onOpenChange={setShowImportDialog}>
|
<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">
|
<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-gray-800 flex-shrink-0 bg-gray-900/50">
|
<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-white">
|
<DialogTitle className="flex items-center gap-3 font-mono text-foreground">
|
||||||
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
||||||
<Upload className="w-5 h-5 text-primary" />
|
<Upload className="w-5 h-5 text-primary" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="text-base font-bold uppercase tracking-wider">导入规则集</span>
|
<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>
|
</div>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="flex-1 overflow-y-auto p-6">
|
<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" />
|
<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>
|
</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 variant="outline" onClick={() => setShowImportDialog(false)} className="cyber-btn-outline">取消</Button>
|
||||||
<Button onClick={handleImport} className="cyber-btn-primary">导入</Button>
|
<Button onClick={handleImport} className="cyber-btn-primary">导入</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
|
|
|
||||||
|
|
@ -239,8 +239,8 @@ export default function AuditTasks() {
|
||||||
case 'completed': return <CheckCircle className="w-4 h-4 text-emerald-400" />;
|
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 'running': return <Activity className="w-4 h-4 text-sky-400" />;
|
||||||
case 'failed': return <AlertTriangle className="w-4 h-4 text-rose-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" />;
|
case 'cancelled': return <XCircle className="w-4 h-4 text-muted-foreground" />;
|
||||||
default: return <Clock className="w-4 h-4 text-gray-400" />;
|
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="flex items-center justify-center min-h-[60vh]">
|
||||||
<div className="text-center space-y-4">
|
<div className="text-center space-y-4">
|
||||||
<div className="loading-spinner mx-auto" />
|
<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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
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 */}
|
{/* Grid background */}
|
||||||
<div className="absolute inset-0 cyber-grid-subtle pointer-events-none" />
|
<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
|
transition-all duration-300 border-2 overflow-hidden
|
||||||
${activeTab === "agent"
|
${activeTab === "agent"
|
||||||
? "bg-gradient-to-br from-primary/20 via-primary/10 to-transparent border-primary shadow-lg shadow-primary/20"
|
? "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
|
transition-all duration-300
|
||||||
${activeTab === "agent"
|
${activeTab === "agent"
|
||||||
? "bg-primary/30 shadow-lg shadow-primary/30"
|
? "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>
|
||||||
|
|
||||||
{/* 内容区域 */}
|
{/* 内容区域 */}
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-center gap-2 mb-1">
|
<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 智能审计
|
Agent 智能审计
|
||||||
</h3>
|
</h3>
|
||||||
{agentStats.running > 0 && (
|
{agentStats.running > 0 && (
|
||||||
|
|
@ -345,20 +345,20 @@ export default function AuditTasks() {
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{activeTab === "agent" && (
|
{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>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</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 协同深度审计,支持智能漏洞挖掘与验证
|
LLM 驱动的多 Agent 协同深度审计,支持智能漏洞挖掘与验证
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* 统计数据 */}
|
{/* 统计数据 */}
|
||||||
<div className="flex items-center gap-4 mt-3 text-xs">
|
<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={`transition-colors duration-300 ${activeTab === "agent" ? "text-muted-foreground" : "text-muted-foreground"}`}>
|
||||||
共 <span className="font-bold text-white">{agentStats.total}</span> 个任务
|
共 <span className="font-bold text-foreground">{agentStats.total}</span> 个任务
|
||||||
</span>
|
</span>
|
||||||
<span className="text-emerald-400">
|
<span className="text-emerald-400">
|
||||||
<CheckCircle className="w-3 h-3 inline mr-1" />
|
<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
|
transition-all duration-300 border-2 overflow-hidden
|
||||||
${activeTab === "regular"
|
${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-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
|
transition-all duration-300
|
||||||
${activeTab === "regular"
|
${activeTab === "regular"
|
||||||
? "bg-cyan-500/30 shadow-lg shadow-cyan-500/30"
|
? "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>
|
||||||
|
|
||||||
{/* 内容区域 */}
|
{/* 内容区域 */}
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-center gap-2 mb-1">
|
<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>
|
</h3>
|
||||||
{regularStats.running > 0 && (
|
{regularStats.running > 0 && (
|
||||||
|
|
@ -422,20 +422,20 @@ export default function AuditTasks() {
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{activeTab === "regular" && (
|
{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>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</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>
|
</p>
|
||||||
|
|
||||||
{/* 统计数据 */}
|
{/* 统计数据 */}
|
||||||
<div className="flex items-center gap-4 mt-3 text-xs">
|
<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={`transition-colors duration-300 ${activeTab === "regular" ? "text-muted-foreground" : "text-muted-foreground"}`}>
|
||||||
共 <span className="font-bold text-white">{regularStats.total}</span> 个任务
|
共 <span className="font-bold text-foreground">{regularStats.total}</span> 个任务
|
||||||
</span>
|
</span>
|
||||||
<span className="text-emerald-400">
|
<span className="text-emerald-400">
|
||||||
<CheckCircle className="w-3 h-3 inline mr-1" />
|
<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="cyber-card p-4 relative z-10">
|
||||||
<div className="flex flex-col md:flex-row items-center gap-4">
|
<div className="flex flex-col md:flex-row items-center gap-4">
|
||||||
<div className="flex-1 relative w-full">
|
<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
|
<Input
|
||||||
placeholder={activeTab === "agent" ? "搜索Agent任务名称..." : "搜索项目名称或任务类型..."}
|
placeholder={activeTab === "agent" ? "搜索Agent任务名称..." : "搜索项目名称或任务类型..."}
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
|
|
@ -544,21 +544,21 @@ export default function AuditTasks() {
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => setStatusFilter("running")}
|
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>
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => setStatusFilter("completed")}
|
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>
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => setStatusFilter("failed")}
|
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>
|
</Button>
|
||||||
|
|
@ -574,24 +574,24 @@ export default function AuditTasks() {
|
||||||
{filteredAgentTasks.map((task) => (
|
{filteredAgentTasks.map((task) => (
|
||||||
<div key={task.id} className="cyber-card p-6">
|
<div key={task.id} className="cyber-card p-6">
|
||||||
{/* Task Header */}
|
{/* 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="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' :
|
<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 === 'running' ? 'bg-sky-500/20' :
|
||||||
task.status === 'failed' ? 'bg-rose-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' :
|
<Bot className={`w-6 h-6 ${task.status === 'completed' ? 'text-emerald-400' :
|
||||||
task.status === 'running' ? 'text-sky-400' :
|
task.status === 'running' ? 'text-sky-400' :
|
||||||
task.status === 'failed' ? 'text-rose-400' :
|
task.status === 'failed' ? 'text-rose-400' :
|
||||||
'text-gray-400'
|
'text-muted-foreground'
|
||||||
}`} />
|
}`} />
|
||||||
</div>
|
</div>
|
||||||
<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审计任务'}
|
{task.name || 'Agent审计任务'}
|
||||||
</h3>
|
</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}
|
{task.current_phase || task.task_type}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -611,25 +611,25 @@ export default function AuditTasks() {
|
||||||
|
|
||||||
{/* Stats Grid */}
|
{/* Stats Grid */}
|
||||||
<div className="grid grid-cols-2 md:grid-cols-5 gap-4 mb-4 font-mono">
|
<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">
|
<div className="text-center p-3 bg-muted rounded-lg border border-border">
|
||||||
<p className="text-2xl font-bold text-white">{task.total_files}</p>
|
<p className="text-2xl font-bold text-foreground">{task.total_files}</p>
|
||||||
<p className="text-xs text-gray-500 uppercase">文件数</p>
|
<p className="text-xs text-muted-foreground uppercase">文件数</p>
|
||||||
</div>
|
</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-white">{task.analyzed_files}</p>
|
<p className="text-2xl font-bold text-foreground">{task.analyzed_files}</p>
|
||||||
<p className="text-xs text-gray-500 uppercase">已分析</p>
|
<p className="text-xs text-muted-foreground uppercase">已分析</p>
|
||||||
</div>
|
</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-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>
|
||||||
<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-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>
|
||||||
<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-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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -654,25 +654,25 @@ export default function AuditTasks() {
|
||||||
{/* Progress Bar */}
|
{/* Progress Bar */}
|
||||||
<div className="mb-4 font-mono">
|
<div className="mb-4 font-mono">
|
||||||
<div className="flex items-center justify-between mb-2">
|
<div className="flex items-center justify-between mb-2">
|
||||||
<span className="text-sm font-bold text-gray-400 uppercase">审计进度</span>
|
<span className="text-sm font-bold text-muted-foreground uppercase">审计进度</span>
|
||||||
<span className="text-sm text-gray-500">
|
<span className="text-sm text-muted-foreground">
|
||||||
{task.analyzed_files || 0} / {task.total_files || 0} 文件
|
{task.analyzed_files || 0} / {task.total_files || 0} 文件
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<Progress
|
<Progress
|
||||||
value={task.progress_percentage || 0}
|
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">
|
<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)}% 完成
|
{(task.progress_percentage || 0).toFixed(0)}% 完成
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Task Footer */}
|
{/* Task Footer */}
|
||||||
<div className="flex items-center justify-between pt-4 border-t border-gray-800">
|
<div className="flex items-center justify-between pt-4 border-t border-border">
|
||||||
<div className="flex items-center space-x-6 text-sm text-gray-500 font-mono">
|
<div className="flex items-center space-x-6 text-sm text-muted-foreground font-mono">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Calendar className="w-4 h-4 mr-2" />
|
<Calendar className="w-4 h-4 mr-2" />
|
||||||
{formatDate(task.created_at)}
|
{formatDate(task.created_at)}
|
||||||
|
|
@ -684,7 +684,7 @@ export default function AuditTasks() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{task.tokens_used > 0 && (
|
{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>
|
<span>{task.tokens_used.toLocaleString()} tokens</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -695,14 +695,14 @@ export default function AuditTasks() {
|
||||||
<>
|
<>
|
||||||
{/* 🔥 查看终端实时流按钮 */}
|
{/* 🔥 查看终端实时流按钮 */}
|
||||||
<Link to={`/agent-audit/${task.id}`}>
|
<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" />
|
<Terminal className="w-4 h-4 mr-2" />
|
||||||
查看实时流
|
查看实时流
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
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)}
|
onClick={() => handleCancelAgentTask(task.id)}
|
||||||
disabled={cancellingAgentTaskId === task.id}
|
disabled={cancellingAgentTaskId === task.id}
|
||||||
>
|
>
|
||||||
|
|
@ -725,11 +725,11 @@ export default function AuditTasks() {
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="cyber-card p-16 text-center relative z-10 border-dashed">
|
<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" />
|
<Bot className="w-16 h-16 text-muted-foreground mx-auto mb-4" />
|
||||||
<h3 className="text-xl font-bold text-gray-300 mb-2 uppercase">
|
<h3 className="text-xl font-bold text-foreground mb-2 uppercase">
|
||||||
{searchTerm || statusFilter !== "all" ? '未找到匹配的Agent任务' : '暂无Agent审计任务'}
|
{searchTerm || statusFilter !== "all" ? '未找到匹配的Agent任务' : '暂无Agent审计任务'}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-gray-500 mb-6 font-mono">
|
<p className="text-muted-foreground mb-6 font-mono">
|
||||||
{searchTerm || statusFilter !== "all" ? '尝试调整搜索条件或筛选器' : '创建第一个Agent审计任务开始智能安全审计'}
|
{searchTerm || statusFilter !== "all" ? '尝试调整搜索条件或筛选器' : '创建第一个Agent审计任务开始智能安全审计'}
|
||||||
</p>
|
</p>
|
||||||
{!searchTerm && statusFilter === "all" && (
|
{!searchTerm && statusFilter === "all" && (
|
||||||
|
|
@ -751,20 +751,20 @@ export default function AuditTasks() {
|
||||||
{filteredTasks.map((task) => (
|
{filteredTasks.map((task) => (
|
||||||
<div key={task.id} className="cyber-card p-6">
|
<div key={task.id} className="cyber-card p-6">
|
||||||
{/* Task Header */}
|
{/* 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="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' :
|
<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 === 'running' ? 'bg-sky-500/20' :
|
||||||
task.status === 'failed' ? 'bg-rose-500/20' :
|
task.status === 'failed' ? 'bg-rose-500/20' :
|
||||||
'bg-gray-800/50'
|
'bg-muted'
|
||||||
}`}>
|
}`}>
|
||||||
{getStatusIcon(task.status)}
|
{getStatusIcon(task.status)}
|
||||||
</div>
|
</div>
|
||||||
<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 || '未知项目'}
|
{task.project?.name || '未知项目'}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm text-gray-500 font-mono">
|
<p className="text-sm text-muted-foreground font-mono">
|
||||||
{task.task_type === 'repository' ? '仓库审计任务' : '即时分析任务'}
|
{task.task_type === 'repository' ? '仓库审计任务' : '即时分析任务'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -774,46 +774,46 @@ export default function AuditTasks() {
|
||||||
|
|
||||||
{/* Stats Grid */}
|
{/* Stats Grid */}
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-4 font-mono">
|
<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">
|
<div className="text-center p-3 bg-muted rounded-lg border border-border">
|
||||||
<p className="text-2xl font-bold text-white">{task.total_files}</p>
|
<p className="text-2xl font-bold text-foreground">{task.total_files}</p>
|
||||||
<p className="text-xs text-gray-500 uppercase">文件数</p>
|
<p className="text-xs text-muted-foreground uppercase">文件数</p>
|
||||||
</div>
|
</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-white">{task.total_lines.toLocaleString()}</p>
|
<p className="text-2xl font-bold text-foreground">{task.total_lines.toLocaleString()}</p>
|
||||||
<p className="text-xs text-gray-500 uppercase">代码行数</p>
|
<p className="text-xs text-muted-foreground uppercase">代码行数</p>
|
||||||
</div>
|
</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-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>
|
||||||
<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-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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Progress Bar */}
|
{/* Progress Bar */}
|
||||||
<div className="mb-4 font-mono">
|
<div className="mb-4 font-mono">
|
||||||
<div className="flex items-center justify-between mb-2">
|
<div className="flex items-center justify-between mb-2">
|
||||||
<span className="text-sm font-bold text-gray-400 uppercase">扫描进度</span>
|
<span className="text-sm font-bold text-muted-foreground uppercase">扫描进度</span>
|
||||||
<span className="text-sm text-gray-500">
|
<span className="text-sm text-muted-foreground">
|
||||||
{task.scanned_files || 0} / {task.total_files || 0} 文件
|
{task.scanned_files || 0} / {task.total_files || 0} 文件
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<Progress
|
<Progress
|
||||||
value={calculateTaskProgress(task.scanned_files, task.total_files)}
|
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">
|
<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)}% 完成
|
{calculateTaskProgress(task.scanned_files, task.total_files)}% 完成
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Task Footer */}
|
{/* Task Footer */}
|
||||||
<div className="flex items-center justify-between pt-4 border-t border-gray-800">
|
<div className="flex items-center justify-between pt-4 border-t border-border">
|
||||||
<div className="flex items-center space-x-6 text-sm text-gray-500 font-mono">
|
<div className="flex items-center space-x-6 text-sm text-muted-foreground font-mono">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Calendar className="w-4 h-4 mr-2" />
|
<Calendar className="w-4 h-4 mr-2" />
|
||||||
{formatDate(task.created_at)}
|
{formatDate(task.created_at)}
|
||||||
|
|
@ -830,7 +830,7 @@ export default function AuditTasks() {
|
||||||
{(task.status === 'running' || task.status === 'pending') && (
|
{(task.status === 'running' || task.status === 'pending') && (
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
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)}
|
onClick={() => handleCancelTask(task.id)}
|
||||||
disabled={cancellingTaskId === task.id}
|
disabled={cancellingTaskId === task.id}
|
||||||
>
|
>
|
||||||
|
|
@ -859,11 +859,11 @@ export default function AuditTasks() {
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="cyber-card p-16 text-center relative z-10 border-dashed">
|
<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" />
|
<Activity className="w-16 h-16 text-muted-foreground mx-auto mb-4" />
|
||||||
<h3 className="text-xl font-bold text-gray-300 mb-2 uppercase">
|
<h3 className="text-xl font-bold text-foreground mb-2 uppercase">
|
||||||
{searchTerm || statusFilter !== "all" ? '未找到匹配的任务' : '暂无审计任务'}
|
{searchTerm || statusFilter !== "all" ? '未找到匹配的任务' : '暂无审计任务'}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-gray-500 mb-6 font-mono">
|
<p className="text-muted-foreground mb-6 font-mono">
|
||||||
{searchTerm || statusFilter !== "all" ? '尝试调整搜索条件或筛选器' : '创建第一个审计任务开始代码质量分析'}
|
{searchTerm || statusFilter !== "all" ? '尝试调整搜索条件或筛选器' : '创建第一个审计任务开始代码质量分析'}
|
||||||
</p>
|
</p>
|
||||||
{!searchTerm && statusFilter === "all" && (
|
{!searchTerm && statusFilter === "all" && (
|
||||||
|
|
|
||||||
|
|
@ -166,14 +166,14 @@ export default function Dashboard() {
|
||||||
<div className="flex items-center justify-center min-h-[60vh]">
|
<div className="flex items-center justify-center min-h-[60vh]">
|
||||||
<div className="text-center space-y-4">
|
<div className="text-center space-y-4">
|
||||||
<div className="loading-spinner mx-auto" />
|
<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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
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 */}
|
{/* Grid background */}
|
||||||
<div className="absolute inset-0 cyber-grid-subtle pointer-events-none" />
|
<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="relative z-10 cyber-card p-4 border-amber-500/30 bg-amber-500/5">
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<AlertTriangle className="w-5 h-5 text-amber-400 mt-0.5" />
|
<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>,显示的是模拟数据。
|
当前使用<span className="text-amber-400 font-bold">演示模式</span>,显示的是模拟数据。
|
||||||
<Link to="/admin" className="ml-2 text-primary font-bold hover:underline">
|
<Link to="/admin" className="ml-2 text-primary font-bold hover:underline">
|
||||||
前往配置 →
|
前往配置 →
|
||||||
|
|
@ -200,8 +200,8 @@ export default function Dashboard() {
|
||||||
<div>
|
<div>
|
||||||
<p className="stat-label">总项目数</p>
|
<p className="stat-label">总项目数</p>
|
||||||
<p className="stat-value">{stats?.total_projects || 0}</p>
|
<p className="stat-value">{stats?.total_projects || 0}</p>
|
||||||
<p className="text-xs text-emerald-400 mt-1 flex items-center gap-1">
|
<p className="text-sm text-emerald-400 mt-1 flex items-center gap-1">
|
||||||
<span className="w-1.5 h-1.5 rounded-full bg-emerald-400" />
|
<span className="w-2 h-2 rounded-full bg-emerald-400" />
|
||||||
活跃: {stats?.active_projects || 0}
|
活跃: {stats?.active_projects || 0}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -217,8 +217,8 @@ export default function Dashboard() {
|
||||||
<div>
|
<div>
|
||||||
<p className="stat-label">审计任务</p>
|
<p className="stat-label">审计任务</p>
|
||||||
<p className="stat-value">{stats?.total_tasks || 0}</p>
|
<p className="stat-value">{stats?.total_tasks || 0}</p>
|
||||||
<p className="text-xs text-emerald-400 mt-1 flex items-center gap-1">
|
<p className="text-sm text-emerald-400 mt-1 flex items-center gap-1">
|
||||||
<span className="w-1.5 h-1.5 rounded-full bg-emerald-400" />
|
<span className="w-2 h-2 rounded-full bg-emerald-400" />
|
||||||
已完成: {stats?.completed_tasks || 0}
|
已完成: {stats?.completed_tasks || 0}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -234,8 +234,8 @@ export default function Dashboard() {
|
||||||
<div>
|
<div>
|
||||||
<p className="stat-label">发现问题</p>
|
<p className="stat-label">发现问题</p>
|
||||||
<p className="stat-value">{stats?.total_issues || 0}</p>
|
<p className="stat-value">{stats?.total_issues || 0}</p>
|
||||||
<p className="text-xs text-amber-400 mt-1 flex items-center gap-1">
|
<p className="text-sm text-amber-400 mt-1 flex items-center gap-1">
|
||||||
<span className="w-1.5 h-1.5 rounded-full bg-amber-400" />
|
<span className="w-2 h-2 rounded-full bg-amber-400" />
|
||||||
已解决: {stats?.resolved_issues || 0}
|
已解决: {stats?.resolved_issues || 0}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -254,12 +254,12 @@ export default function Dashboard() {
|
||||||
{stats?.avg_quality_score ? stats.avg_quality_score.toFixed(1) : '0.0'}
|
{stats?.avg_quality_score ? stats.avg_quality_score.toFixed(1) : '0.0'}
|
||||||
</p>
|
</p>
|
||||||
{stats?.avg_quality_score ? (
|
{stats?.avg_quality_score ? (
|
||||||
<p className="text-xs text-emerald-400 mt-1 flex items-center gap-1">
|
<p className="text-sm text-emerald-400 mt-1 flex items-center gap-1">
|
||||||
<TrendingUp className="w-3 h-3" />
|
<TrendingUp className="w-4 h-4" />
|
||||||
持续改进
|
持续改进
|
||||||
</p>
|
</p>
|
||||||
) : (
|
) : (
|
||||||
<p className="text-xs text-gray-500 mt-1">暂无数据</p>
|
<p className="text-sm text-muted-foreground mt-1">暂无数据</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-icon text-violet-400">
|
<div className="stat-icon text-violet-400">
|
||||||
|
|
@ -284,25 +284,26 @@ export default function Dashboard() {
|
||||||
{qualityTrendData.length > 0 ? (
|
{qualityTrendData.length > 0 ? (
|
||||||
<ResponsiveContainer width="100%" height={220}>
|
<ResponsiveContainer width="100%" height={220}>
|
||||||
<LineChart data={qualityTrendData}>
|
<LineChart data={qualityTrendData}>
|
||||||
<CartesianGrid strokeDasharray="3 3" stroke="#1f1f2e" />
|
<CartesianGrid strokeDasharray="3 3" stroke="var(--cyber-border)" />
|
||||||
<XAxis dataKey="date" stroke="#6b7280" fontSize={11} tick={{ fontFamily: 'monospace' }} />
|
<XAxis dataKey="date" stroke="var(--cyber-text-muted)" fontSize={11} tick={{ fontFamily: 'monospace' }} />
|
||||||
<YAxis stroke="#6b7280" fontSize={11} domain={[0, 100]} tick={{ fontFamily: 'monospace' }} />
|
<YAxis stroke="var(--cyber-text-muted)" fontSize={11} domain={[0, 100]} tick={{ fontFamily: 'monospace' }} />
|
||||||
<Tooltip
|
<Tooltip
|
||||||
contentStyle={{
|
contentStyle={{
|
||||||
backgroundColor: '#0c0c12',
|
backgroundColor: 'var(--cyber-bg-elevated)',
|
||||||
border: '1px solid #2a2a35',
|
border: '1px solid var(--cyber-border)',
|
||||||
borderRadius: '4px',
|
borderRadius: '4px',
|
||||||
fontFamily: 'monospace',
|
fontFamily: 'monospace',
|
||||||
fontSize: '12px'
|
fontSize: '12px',
|
||||||
|
color: 'var(--cyber-text)'
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Line
|
<Line
|
||||||
type="monotone"
|
type="monotone"
|
||||||
dataKey="score"
|
dataKey="score"
|
||||||
stroke="#FF6B2C"
|
stroke="hsl(var(--primary))"
|
||||||
strokeWidth={2}
|
strokeWidth={2}
|
||||||
dot={{ fill: '#FF6B2C', stroke: '#0c0c12', strokeWidth: 2, r: 4 }}
|
dot={{ fill: 'hsl(var(--primary))', stroke: 'var(--cyber-bg)', strokeWidth: 2, r: 4 }}
|
||||||
activeDot={{ r: 6, fill: '#FF6B2C' }}
|
activeDot={{ r: 6, fill: 'hsl(var(--primary))' }}
|
||||||
/>
|
/>
|
||||||
</LineChart>
|
</LineChart>
|
||||||
</ResponsiveContainer>
|
</ResponsiveContainer>
|
||||||
|
|
@ -331,7 +332,7 @@ export default function Dashboard() {
|
||||||
label={({ name, percent }) => `${name} ${(percent * 100).toFixed(0)}%`}
|
label={({ name, percent }) => `${name} ${(percent * 100).toFixed(0)}%`}
|
||||||
outerRadius={70}
|
outerRadius={70}
|
||||||
dataKey="value"
|
dataKey="value"
|
||||||
stroke="#0c0c12"
|
stroke="var(--cyber-bg)"
|
||||||
strokeWidth={2}
|
strokeWidth={2}
|
||||||
>
|
>
|
||||||
{issueTypeData.map((entry) => (
|
{issueTypeData.map((entry) => (
|
||||||
|
|
@ -340,11 +341,12 @@ export default function Dashboard() {
|
||||||
</Pie>
|
</Pie>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
contentStyle={{
|
contentStyle={{
|
||||||
backgroundColor: '#0c0c12',
|
backgroundColor: 'var(--cyber-bg-elevated)',
|
||||||
border: '1px solid #2a2a35',
|
border: '1px solid var(--cyber-border)',
|
||||||
borderRadius: '4px',
|
borderRadius: '4px',
|
||||||
fontFamily: 'monospace',
|
fontFamily: 'monospace',
|
||||||
fontSize: '12px'
|
fontSize: '12px',
|
||||||
|
color: 'var(--cyber-text)'
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</PieChart>
|
</PieChart>
|
||||||
|
|
@ -370,21 +372,33 @@ export default function Dashboard() {
|
||||||
<Link
|
<Link
|
||||||
key={project.id}
|
key={project.id}
|
||||||
to={`/projects/${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">
|
<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}
|
{project.name}
|
||||||
</h4>
|
</h4>
|
||||||
<Badge className={`ml-2 flex-shrink-0 ${project.is_active ? 'cyber-badge-success' : 'cyber-badge-muted'}`}>
|
<Badge className={`ml-2 flex-shrink-0 ${project.is_active ? 'cyber-badge-success' : 'cyber-badge-muted'}`}>
|
||||||
{project.is_active ? '活跃' : '暂停'}
|
{project.is_active ? '活跃' : '暂停'}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</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 || '暂无描述'}
|
{project.description || '暂无描述'}
|
||||||
</p>
|
</p>
|
||||||
<div className="flex items-center text-xs text-gray-600">
|
<div className="flex items-center text-sm text-muted-foreground">
|
||||||
<Calendar className="w-3 h-3 mr-1" />
|
<Calendar className="w-4 h-4 mr-1" />
|
||||||
{new Date(project.created_at).toLocaleDateString('zh-CN')}
|
{new Date(project.created_at).toLocaleDateString('zh-CN')}
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
@ -405,7 +419,7 @@ export default function Dashboard() {
|
||||||
<Clock className="w-5 h-5 text-emerald-400" />
|
<Clock className="w-5 h-5 text-emerald-400" />
|
||||||
<h3 className="section-title">最近任务</h3>
|
<h3 className="section-title">最近任务</h3>
|
||||||
<Link to="/audit-tasks" className="ml-auto">
|
<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" />
|
查看全部 <ArrowUpRight className="w-3 h-3 ml-1" />
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
@ -416,7 +430,16 @@ export default function Dashboard() {
|
||||||
<Link
|
<Link
|
||||||
key={task.id}
|
key={task.id}
|
||||||
to={`/tasks/${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="flex items-center gap-3">
|
||||||
<div className={`w-8 h-8 rounded-lg flex items-center justify-center ${
|
<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" />}
|
<AlertTriangle className="w-4 h-4" />}
|
||||||
</div>
|
</div>
|
||||||
<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 || '未知项目'}
|
{task.project?.name || '未知项目'}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-gray-500">
|
<p className="text-sm text-muted-foreground">
|
||||||
质量分: <span className="text-white">{task.quality_score?.toFixed(1) || '0.0'}</span>
|
质量分: <span className="text-foreground">{task.quality_score?.toFixed(1) || '0.0'}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -494,7 +517,7 @@ export default function Dashboard() {
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="flex items-center justify-between">
|
<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={`
|
<Badge className={`
|
||||||
${dbMode === 'api' ? 'cyber-badge-primary' :
|
${dbMode === 'api' ? 'cyber-badge-primary' :
|
||||||
dbMode === 'local' ? 'cyber-badge-info' :
|
dbMode === 'local' ? 'cyber-badge-info' :
|
||||||
|
|
@ -505,36 +528,36 @@ export default function Dashboard() {
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="text-sm text-gray-400">活跃项目</span>
|
<span className="text-base text-muted-foreground">活跃项目</span>
|
||||||
<span className="text-sm font-bold text-white">{stats?.active_projects || 0}</span>
|
<span className="text-base font-bold text-foreground">{stats?.active_projects || 0}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="text-sm text-gray-400">运行中任务</span>
|
<span className="text-base text-muted-foreground">运行中任务</span>
|
||||||
<span className="text-sm font-bold text-sky-400">
|
<span className="text-base font-bold text-sky-400">
|
||||||
{recentTasks.filter(t => t.status === 'running').length}
|
{recentTasks.filter(t => t.status === 'running').length}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="text-sm text-gray-400">待解决问题</span>
|
<span className="text-base text-muted-foreground">待解决问题</span>
|
||||||
<span className="text-sm font-bold text-amber-400">
|
<span className="text-base font-bold text-amber-400">
|
||||||
{stats ? stats.total_issues - stats.resolved_issues : 0}
|
{stats ? stats.total_issues - stats.resolved_issues : 0}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="text-sm text-gray-400 flex items-center gap-1">
|
<span className="text-base text-muted-foreground flex items-center gap-1">
|
||||||
<Shield className="w-3 h-3" />
|
<Shield className="w-4 h-4" />
|
||||||
审计规则
|
审计规则
|
||||||
</span>
|
</span>
|
||||||
<span className="text-sm font-bold text-violet-400">
|
<span className="text-base font-bold text-violet-400">
|
||||||
{ruleStats.enabled}/{ruleStats.total}
|
{ruleStats.enabled}/{ruleStats.total}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="text-sm text-gray-400 flex items-center gap-1">
|
<span className="text-base text-muted-foreground flex items-center gap-1">
|
||||||
<MessageSquare className="w-3 h-3" />
|
<MessageSquare className="w-4 h-4" />
|
||||||
提示词模板
|
提示词模板
|
||||||
</span>
|
</span>
|
||||||
<span className="text-sm font-bold text-emerald-400">
|
<span className="text-base font-bold text-emerald-400">
|
||||||
{templateStats.active}/{templateStats.total}
|
{templateStats.active}/{templateStats.total}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -572,28 +595,28 @@ export default function Dashboard() {
|
||||||
<Link
|
<Link
|
||||||
key={task.id}
|
key={task.id}
|
||||||
to={`/tasks/${task.id}`}
|
to={`/tasks/${task.id}`}
|
||||||
className={`block p-3 rounded-lg border transition-all hover:border-gray-700 ${
|
className={`block p-3 rounded-lg border transition-all ${
|
||||||
task.status === 'completed' ? 'bg-emerald-500/5 border-emerald-500/20' :
|
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' :
|
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' :
|
task.status === 'failed' ? 'bg-rose-500/5 border-rose-500/20 hover:border-rose-500/40' :
|
||||||
'bg-gray-800/30 border-gray-800/50'
|
'bg-muted/30 border-border hover:border-border'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<p className="text-sm font-medium text-gray-200">{statusText}</p>
|
<p className="text-base font-medium text-foreground">{statusText}</p>
|
||||||
<p className="text-xs text-gray-500 mt-1 line-clamp-1">
|
<p className="text-sm text-muted-foreground mt-1 line-clamp-1">
|
||||||
项目 "{task.project?.name || '未知项目'}"
|
项目 "{task.project?.name || '未知项目'}"
|
||||||
{task.status === 'completed' && task.issues_count > 0 &&
|
{task.status === 'completed' && task.issues_count > 0 &&
|
||||||
` - 发现 ${task.issues_count} 个问题`
|
` - 发现 ${task.issues_count} 个问题`
|
||||||
}
|
}
|
||||||
</p>
|
</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>
|
</Link>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
) : (
|
) : (
|
||||||
<div className="empty-state py-6">
|
<div className="empty-state py-6">
|
||||||
<Clock className="w-8 h-8 text-gray-600 mb-2" />
|
<Clock className="w-10 h-10 text-muted-foreground mb-2" />
|
||||||
<p className="text-sm text-gray-500">暂无活动记录</p>
|
<p className="text-base text-muted-foreground">暂无活动记录</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -346,8 +346,8 @@ public class Example {
|
||||||
|
|
||||||
// Render issue with cyberpunk style
|
// Render issue with cyberpunk style
|
||||||
const renderIssue = (issue: any, index: number) => (
|
const renderIssue = (issue: any, index: number) => (
|
||||||
<div key={index} className="cyber-card p-4 mb-4 hover:border-gray-700 transition-all group">
|
<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-gray-800">
|
<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="flex items-start space-x-3">
|
||||||
<div className={`w-10 h-10 rounded-lg flex items-center justify-center ${
|
<div className={`w-10 h-10 rounded-lg flex items-center justify-center ${
|
||||||
issue.severity === 'critical' ? 'bg-rose-500/20 text-rose-400' :
|
issue.severity === 'critical' ? 'bg-rose-500/20 text-rose-400' :
|
||||||
|
|
@ -358,15 +358,15 @@ public class Example {
|
||||||
{getTypeIcon(issue.type)}
|
{getTypeIcon(issue.type)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1">
|
<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>
|
<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-gray-500 font-mono">
|
<div className="flex items-center space-x-1 text-xs text-muted-foreground font-mono">
|
||||||
<span className="text-primary">></span>
|
<span className="text-primary">></span>
|
||||||
<span>LINE: {issue.line}</span>
|
<span>LINE: {issue.line}</span>
|
||||||
{issue.column && <span>, COL: {issue.column}</span>}
|
{issue.column && <span>, COL: {issue.column}</span>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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 === 'critical' ? '严重' :
|
||||||
issue.severity === 'high' ? '高' :
|
issue.severity === 'high' ? '高' :
|
||||||
issue.severity === 'medium' ? '中等' : '低'}
|
issue.severity === 'medium' ? '中等' : '低'}
|
||||||
|
|
@ -374,27 +374,27 @@ public class Example {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{issue.description && (
|
{issue.description && (
|
||||||
<div className="bg-gray-900/50 border border-gray-800 p-3 mb-3 rounded font-mono">
|
<div className="bg-muted border border-border p-3 mb-3 rounded font-mono">
|
||||||
<div className="flex items-center mb-1 border-b border-gray-800 pb-1">
|
<div className="flex items-center mb-1 border-b border-border pb-1">
|
||||||
<Info className="w-3 h-3 text-gray-500 mr-1" />
|
<Info className="w-3 h-3 text-muted-foreground mr-1" />
|
||||||
<span className="font-bold text-gray-400 text-xs uppercase">问题详情</span>
|
<span className="font-bold text-muted-foreground text-xs uppercase">问题详情</span>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{issue.code_snippet && (
|
{issue.code_snippet && (
|
||||||
<div className="bg-[#0a0a0f] p-3 mb-3 border border-gray-800 rounded">
|
<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-gray-800 pb-1">
|
<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="flex items-center space-x-1">
|
||||||
<div className="w-4 h-4 bg-primary rounded flex items-center justify-center">
|
<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>
|
</div>
|
||||||
<span className="text-emerald-400 text-xs font-bold font-mono uppercase">CODE_SNIPPET</span>
|
<span className="text-emerald-400 text-xs font-bold font-mono uppercase">CODE_SNIPPET</span>
|
||||||
</div>
|
</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>
|
||||||
<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">
|
<pre className="text-xs text-emerald-400 font-mono overflow-x-auto">
|
||||||
<code>{issue.code_snippet}</code>
|
<code>{issue.code_snippet}</code>
|
||||||
</pre>
|
</pre>
|
||||||
|
|
@ -431,19 +431,19 @@ public class Example {
|
||||||
{parsedExplanation.what && (
|
{parsedExplanation.what && (
|
||||||
<div className="border-l-2 border-rose-500 pl-2">
|
<div className="border-l-2 border-rose-500 pl-2">
|
||||||
<span className="font-bold text-rose-400 uppercase">问题:</span>
|
<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>
|
</div>
|
||||||
)}
|
)}
|
||||||
{parsedExplanation.why && (
|
{parsedExplanation.why && (
|
||||||
<div className="border-l-2 border-amber-500 pl-2">
|
<div className="border-l-2 border-amber-500 pl-2">
|
||||||
<span className="font-bold text-amber-400 uppercase">原因:</span>
|
<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>
|
</div>
|
||||||
)}
|
)}
|
||||||
{parsedExplanation.how && (
|
{parsedExplanation.how && (
|
||||||
<div className="border-l-2 border-emerald-500 pl-2">
|
<div className="border-l-2 border-emerald-500 pl-2">
|
||||||
<span className="font-bold text-emerald-400 uppercase">方案:</span>
|
<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>
|
</div>
|
||||||
)}
|
)}
|
||||||
{parsedExplanation.learn_more && (
|
{parsedExplanation.learn_more && (
|
||||||
|
|
@ -469,7 +469,7 @@ public class Example {
|
||||||
<Zap className="w-4 h-4 text-violet-400 mr-2" />
|
<Zap className="w-4 h-4 text-violet-400 mr-2" />
|
||||||
<span className="font-bold text-violet-300 text-sm uppercase">AI 解释</span>
|
<span className="font-bold text-violet-300 text-sm uppercase">AI 解释</span>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -479,7 +479,7 @@ public class Example {
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
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 */}
|
{/* Grid background */}
|
||||||
<div className="absolute inset-0 cyber-grid-subtle pointer-events-none" />
|
<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 p-0 relative z-10">
|
||||||
<div className="cyber-card-header">
|
<div className="cyber-card-header">
|
||||||
<History className="w-5 h-5 text-primary" />
|
<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">
|
<div className="ml-auto flex items-center gap-2">
|
||||||
{historyRecords.length > 0 && (
|
{historyRecords.length > 0 && (
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -514,7 +514,7 @@ public class Example {
|
||||||
{loadingHistory ? (
|
{loadingHistory ? (
|
||||||
<div className="text-center py-8">
|
<div className="text-center py-8">
|
||||||
<div className="loading-spinner mx-auto mb-4"></div>
|
<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>
|
</div>
|
||||||
) : historyRecords.length === 0 ? (
|
) : historyRecords.length === 0 ? (
|
||||||
<div className="empty-state">
|
<div className="empty-state">
|
||||||
|
|
@ -531,14 +531,14 @@ public class Example {
|
||||||
className={`p-4 rounded-lg border transition-colors cursor-pointer ${
|
className={`p-4 rounded-lg border transition-colors cursor-pointer ${
|
||||||
selectedHistoryId === record.id
|
selectedHistoryId === record.id
|
||||||
? 'bg-primary/10 border-primary/30'
|
? '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)}
|
onClick={() => viewHistoryRecord(record)}
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between mb-2">
|
<div className="flex items-center justify-between mb-2">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Badge className="cyber-badge-muted">{record.language}</Badge>
|
<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>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Badge className={`font-mono ${
|
<Badge className={`font-mono ${
|
||||||
|
|
@ -556,10 +556,10 @@ public class Example {
|
||||||
>
|
>
|
||||||
<X className="w-3 h-3" />
|
<X className="w-3 h-3" />
|
||||||
</Button>
|
</Button>
|
||||||
<ChevronRight className="w-4 h-4 text-gray-500" />
|
<ChevronRight className="w-4 h-4 text-muted-foreground" />
|
||||||
</div>
|
</div>
|
||||||
</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">
|
<span className="flex items-center gap-1">
|
||||||
<AlertTriangle className="w-3 h-3" />
|
<AlertTriangle className="w-3 h-3" />
|
||||||
{record.issues_count} 个问题
|
{record.issues_count} 个问题
|
||||||
|
|
@ -582,7 +582,7 @@ public class Example {
|
||||||
<div className="cyber-card p-0 relative z-10">
|
<div className="cyber-card p-0 relative z-10">
|
||||||
<div className="cyber-card-header">
|
<div className="cyber-card-header">
|
||||||
<Terminal className="w-5 h-5 text-primary" />
|
<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">
|
<div className="ml-auto flex items-center gap-2">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
|
@ -606,12 +606,12 @@ public class Example {
|
||||||
{/* Toolbar */}
|
{/* Toolbar */}
|
||||||
<div className="flex flex-col sm:flex-row gap-3">
|
<div className="flex flex-col sm:flex-row gap-3">
|
||||||
<div className="flex-1 space-y-1">
|
<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}>
|
<Select value={language} onValueChange={setLanguage}>
|
||||||
<SelectTrigger className="cyber-input h-10">
|
<SelectTrigger className="cyber-input h-10">
|
||||||
<SelectValue placeholder="选择编程语言" />
|
<SelectValue placeholder="选择编程语言" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
<SelectContent className="cyber-dialog border-border">
|
||||||
{supportedLanguages.map((lang) => (
|
{supportedLanguages.map((lang) => (
|
||||||
<SelectItem key={lang} value={lang}>
|
<SelectItem key={lang} value={lang}>
|
||||||
{lang.charAt(0).toUpperCase() + lang.slice(1)}
|
{lang.charAt(0).toUpperCase() + lang.slice(1)}
|
||||||
|
|
@ -621,7 +621,7 @@ public class Example {
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 space-y-1">
|
<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}>
|
<Select value={selectedPromptTemplateId} onValueChange={setSelectedPromptTemplateId}>
|
||||||
<SelectTrigger className="cyber-input h-10">
|
<SelectTrigger className="cyber-input h-10">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
|
@ -629,7 +629,7 @@ public class Example {
|
||||||
<SelectValue placeholder="选择提示词模板" />
|
<SelectValue placeholder="选择提示词模板" />
|
||||||
</div>
|
</div>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
<SelectContent className="cyber-dialog border-border">
|
||||||
{promptTemplates.map((pt) => (
|
{promptTemplates.map((pt) => (
|
||||||
<SelectItem key={pt.id} value={pt.id}>
|
<SelectItem key={pt.id} value={pt.id}>
|
||||||
{pt.name} {pt.is_default && '(默认)'}
|
{pt.name} {pt.is_default && '(默认)'}
|
||||||
|
|
@ -659,8 +659,8 @@ public class Example {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Quick Examples */}
|
{/* Quick Examples */}
|
||||||
<div className="flex flex-wrap gap-2 items-center p-3 bg-gray-900/50 border border-gray-800 rounded">
|
<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-gray-500 mr-2">示例:</span>
|
<span className="text-xs font-bold uppercase text-muted-foreground mr-2">示例:</span>
|
||||||
{['javascript', 'python', 'java'].map((lang) => (
|
{['javascript', 'python', 'java'].map((lang) => (
|
||||||
<Button
|
<Button
|
||||||
key={lang}
|
key={lang}
|
||||||
|
|
@ -677,17 +677,17 @@ public class Example {
|
||||||
|
|
||||||
{/* Code Editor */}
|
{/* Code Editor */}
|
||||||
<div className="relative">
|
<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
|
Editor
|
||||||
</div>
|
</div>
|
||||||
<Textarea
|
<Textarea
|
||||||
placeholder="// 粘贴代码或上传文件..."
|
placeholder="// 粘贴代码或上传文件..."
|
||||||
value={code}
|
value={code}
|
||||||
onChange={(e) => setCode(e.target.value)}
|
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}
|
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} 行
|
{code.length} 字符,{code.split('\n').length} 行
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -720,7 +720,7 @@ public class Example {
|
||||||
<div className="cyber-card p-0">
|
<div className="cyber-card p-0">
|
||||||
<div className="cyber-card-header">
|
<div className="cyber-card-header">
|
||||||
<CheckCircle className="w-5 h-5 text-emerald-400" />
|
<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">
|
<div className="ml-auto flex items-center gap-2">
|
||||||
<Badge className="cyber-badge-muted">
|
<Badge className="cyber-badge-muted">
|
||||||
<Clock className="w-3 h-3 mr-1" />
|
<Clock className="w-3 h-3 mr-1" />
|
||||||
|
|
@ -748,7 +748,7 @@ public class Example {
|
||||||
{(result.quality_score ?? 0).toFixed(1)}
|
{(result.quality_score ?? 0).toFixed(1)}
|
||||||
</div>
|
</div>
|
||||||
<p className="stat-label mb-2">质量评分</p>
|
<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>
|
||||||
|
|
||||||
<div className="cyber-card p-4 text-center">
|
<div className="cyber-card p-4 text-center">
|
||||||
|
|
@ -786,7 +786,7 @@ public class Example {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Detailed Metrics */}
|
{/* 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">
|
<h3 className="section-title text-sm mb-4 flex items-center gap-2">
|
||||||
<TrendingUp className="w-4 h-4" />
|
<TrendingUp className="w-4 h-4" />
|
||||||
详细指标
|
详细指标
|
||||||
|
|
@ -799,9 +799,9 @@ public class Example {
|
||||||
{ label: '性能', value: result.metrics?.performance ?? 0 },
|
{ label: '性能', value: result.metrics?.performance ?? 0 },
|
||||||
].map((metric) => (
|
].map((metric) => (
|
||||||
<div key={metric.label} className="text-center">
|
<div key={metric.label} className="text-center">
|
||||||
<div className="text-xl font-bold text-white mb-1">{metric.value}</div>
|
<div className="text-xl font-bold text-foreground mb-1">{metric.value}</div>
|
||||||
<p className="text-xs text-gray-500 uppercase mb-2">{metric.label}</p>
|
<p className="text-xs text-muted-foreground uppercase mb-2">{metric.label}</p>
|
||||||
<Progress value={metric.value} className="h-2 bg-gray-800 [&>div]:bg-primary" />
|
<Progress value={metric.value} className="h-2 bg-muted [&>div]:bg-primary" />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -813,22 +813,22 @@ public class Example {
|
||||||
<div className="cyber-card p-0">
|
<div className="cyber-card p-0">
|
||||||
<div className="cyber-card-header">
|
<div className="cyber-card-header">
|
||||||
<Shield className="w-5 h-5 text-amber-400" />
|
<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>
|
||||||
<div className="p-6">
|
<div className="p-6">
|
||||||
{result.issues.length > 0 ? (
|
{result.issues.length > 0 ? (
|
||||||
<Tabs defaultValue="all" className="w-full">
|
<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">
|
<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-white font-mono font-bold uppercase py-2 text-gray-400 transition-all rounded-sm text-xs">
|
<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})
|
全部 ({result.issues.length})
|
||||||
</TabsTrigger>
|
</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})
|
严重 ({result.issues.filter(i => i.severity === 'critical').length})
|
||||||
</TabsTrigger>
|
</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})
|
高 ({result.issues.filter(i => i.severity === 'high').length})
|
||||||
</TabsTrigger>
|
</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})
|
中等 ({result.issues.filter(i => i.severity === 'medium').length})
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
@ -844,10 +844,10 @@ public class Example {
|
||||||
) : (
|
) : (
|
||||||
<div className="cyber-card p-12 text-center border-dashed">
|
<div className="cyber-card p-12 text-center border-dashed">
|
||||||
<CheckCircle className="w-16 h-16 text-emerald-400 mx-auto mb-4" />
|
<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' ? '高优先级' : '中等优先级'}问题
|
没有发现{severity === 'critical' ? '严重' : severity === 'high' ? '高优先级' : '中等优先级'}问题
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-gray-500 font-mono">代码在此级别的检查中表现良好</p>
|
<p className="text-muted-foreground font-mono">代码在此级别的检查中表现良好</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</TabsContent>
|
</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="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 className="loading-spinner w-12 h-12"></div>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-2xl font-bold text-white uppercase mb-3">AI正在分析您的代码</h3>
|
<h3 className="text-2xl font-bold text-foreground uppercase mb-3">AI正在分析您的代码</h3>
|
||||||
<p className="text-gray-400 mb-6 font-mono">请稍候,这通常需要至少30秒钟...</p>
|
<p className="text-muted-foreground mb-6 font-mono">请稍候,这通常需要至少30秒钟...</p>
|
||||||
<p className="text-gray-500 text-sm mb-6 font-mono">分析时长取决于您的网络环境、代码长度以及使用的模型等因素</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">
|
<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">
|
<p className="text-primary text-sm font-mono">
|
||||||
正在进行安全检测、性能分析、代码风格检查等多维度评估<br />
|
正在进行安全检测、性能分析、代码风格检查等多维度评估<br />
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ export default function Login() {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
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 */}
|
{/* Scanline overlay */}
|
||||||
<div className="absolute inset-0 pointer-events-none z-20">
|
<div className="absolute inset-0 pointer-events-none z-20">
|
||||||
<div
|
<div
|
||||||
|
|
@ -110,32 +110,32 @@ export default function Login() {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Corner Decorations */}
|
{/* 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">
|
<div className="flex items-center gap-2">
|
||||||
<Terminal className="w-3 h-3" />
|
<Terminal className="w-4 h-4" />
|
||||||
<span>SYS_ID: 0x84F2</span>
|
<span>SYS_ID: 0x84F2</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Shield className="w-3 h-3" />
|
<Shield className="w-4 h-4" />
|
||||||
<span>ENCRYPT: AES-256</span>
|
<span>ENCRYPT: AES-256</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Fingerprint className="w-3 h-3" />
|
<Fingerprint className="w-4 h-4" />
|
||||||
<span>AUTH: READY</span>
|
<span>AUTH: READY</span>
|
||||||
</div>
|
</div>
|
||||||
</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>SECURE_CONN: TRUE</div>
|
||||||
<div>PORT: 443</div>
|
<div>PORT: 443</div>
|
||||||
<div>TLS: 1.3</div>
|
<div>TLS: 1.3</div>
|
||||||
</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
|
DEEPAUDIT_AUTH_v3
|
||||||
</div>
|
</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]}
|
{new Date().toISOString().split("T")[0]}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -143,7 +143,7 @@ export default function Login() {
|
||||||
<div className="w-full max-w-md relative z-30 px-4">
|
<div className="w-full max-w-md relative z-30 px-4">
|
||||||
{/* Logo & Title */}
|
{/* Logo & Title */}
|
||||||
<div className="text-center mb-8">
|
<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)' }}>
|
style={{ boxShadow: '0 0 30px rgba(255,107,44,0.1)' }}>
|
||||||
<img
|
<img
|
||||||
src="/logo_deepaudit.png"
|
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)" }}
|
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-primary">DEEP</span>
|
||||||
<span className="text-white">AUDIT</span>
|
<span className="text-foreground">AUDIT</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm font-mono text-gray-500">
|
<p className="text-base font-mono text-muted-foreground">
|
||||||
// Autonomous Security Agent
|
// Autonomous Security Agent
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Login Form Card */}
|
{/* 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)' }}>
|
style={{ boxShadow: '0 4px 30px rgba(0,0,0,0.5)' }}>
|
||||||
{/* Card Header */}
|
{/* 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="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-red-500/80" />
|
||||||
<div className="w-3 h-3 rounded-full bg-yellow-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 className="w-3 h-3 rounded-full bg-green-500/80" />
|
||||||
</div>
|
</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
|
authentication@deepaudit
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -183,7 +183,7 @@ export default function Login() {
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label
|
<Label
|
||||||
htmlFor="email"
|
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>
|
</Label>
|
||||||
|
|
@ -195,16 +195,16 @@ export default function Login() {
|
||||||
value={email}
|
value={email}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
required
|
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>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label
|
<Label
|
||||||
htmlFor="password"
|
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>
|
</Label>
|
||||||
|
|
@ -216,9 +216,9 @@ export default function Login() {
|
||||||
value={password}
|
value={password}
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
required
|
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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -230,11 +230,11 @@ export default function Login() {
|
||||||
onCheckedChange={(checked) =>
|
onCheckedChange={(checked) =>
|
||||||
setRememberMe(checked as boolean)
|
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
|
<Label
|
||||||
htmlFor="remember"
|
htmlFor="remember"
|
||||||
className="text-sm font-mono text-gray-500 cursor-pointer"
|
className="text-base font-mono text-muted-foreground cursor-pointer"
|
||||||
>
|
>
|
||||||
记住我
|
记住我
|
||||||
</Label>
|
</Label>
|
||||||
|
|
@ -243,7 +243,7 @@ export default function Login() {
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
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)' }}
|
style={{ boxShadow: '0 0 20px rgba(255,107,44,0.3)' }}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
>
|
>
|
||||||
|
|
@ -259,8 +259,8 @@ export default function Login() {
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<div className="mt-6 pt-5 border-t border-gray-800/50 text-center">
|
<div className="mt-6 pt-5 border-t border-border text-center">
|
||||||
<p className="text-sm font-mono text-gray-500">
|
<p className="text-base font-mono text-muted-foreground">
|
||||||
还没有账号?{" "}
|
还没有账号?{" "}
|
||||||
<span
|
<span
|
||||||
className="text-primary font-bold cursor-pointer hover:underline"
|
className="text-primary font-bold cursor-pointer hover:underline"
|
||||||
|
|
@ -275,7 +275,7 @@ export default function Login() {
|
||||||
|
|
||||||
{/* Version Info */}
|
{/* Version Info */}
|
||||||
<div className="mt-6 text-center">
|
<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
|
Version {version} · Secure Connection
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ export default function NotFound() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageMeta title="页面未找到" description="" />
|
<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 */}
|
{/* Grid background */}
|
||||||
<div className="absolute inset-0 cyber-grid-subtle pointer-events-none" />
|
<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 p-0 mb-8">
|
||||||
<div className="cyber-card-header">
|
<div className="cyber-card-header">
|
||||||
<Terminal className="w-4 h-4 text-primary" />
|
<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>
|
||||||
<div className="p-4 text-left">
|
<div className="p-4 text-left">
|
||||||
<div className="text-emerald-400 text-sm">
|
<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>
|
||||||
<div className="text-rose-400 text-sm mt-1">
|
<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>
|
||||||
<div className="text-amber-400 text-sm mt-1">
|
<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>
|
||||||
<div className="text-gray-400 text-sm mt-1">
|
<div className="text-muted-foreground text-sm mt-1">
|
||||||
<span className="text-gray-500">MESSAGE:</span> 请求的页面不存在或已被移除
|
<span className="text-muted-foreground">MESSAGE:</span> 请求的页面不存在或已被移除
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
<p className="text-gray-400 mb-8 text-sm">
|
<p className="text-muted-foreground mb-8 text-sm">
|
||||||
页面可能已被删除或不存在,请检查网址是否正确。
|
页面可能已被删除或不存在,请检查网址是否正确。
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
@ -76,7 +76,7 @@ export default function NotFound() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer */}
|
{/* 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
|
© {new Date().getFullYear()} DeepAudit
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -280,7 +280,7 @@ export default function ProjectDetail() {
|
||||||
case 'completed': return <CheckCircle className="w-4 h-4 text-emerald-400" />;
|
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 'running': return <Activity className="w-4 h-4 text-sky-400" />;
|
||||||
case 'failed': return <AlertTriangle className="w-4 h-4 text-rose-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="flex items-center justify-center min-h-[60vh]">
|
||||||
<div className="text-center space-y-4">
|
<div className="text-center space-y-4">
|
||||||
<div className="loading-spinner mx-auto" />
|
<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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -327,8 +327,8 @@ export default function ProjectDetail() {
|
||||||
<div className="flex items-center justify-center min-h-[60vh]">
|
<div className="flex items-center justify-center min-h-[60vh]">
|
||||||
<div className="cyber-card p-8 text-center">
|
<div className="cyber-card p-8 text-center">
|
||||||
<AlertTriangle className="w-16 h-16 text-rose-400 mx-auto mb-4" />
|
<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>
|
<h2 className="text-2xl font-bold text-foreground mb-2 uppercase">项目未找到</h2>
|
||||||
<p className="text-gray-400 mb-4 font-mono">请检查项目ID是否正确</p>
|
<p className="text-muted-foreground mb-4 font-mono">请检查项目ID是否正确</p>
|
||||||
<Link to="/projects">
|
<Link to="/projects">
|
||||||
<Button className="cyber-btn-primary">
|
<Button className="cyber-btn-primary">
|
||||||
<ArrowLeft className="w-4 h-4 mr-2" />
|
<ArrowLeft className="w-4 h-4 mr-2" />
|
||||||
|
|
@ -341,7 +341,7 @@ export default function ProjectDetail() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
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 */}
|
{/* Grid background */}
|
||||||
<div className="absolute inset-0 cyber-grid-subtle pointer-events-none" />
|
<div className="absolute inset-0 cyber-grid-subtle pointer-events-none" />
|
||||||
|
|
||||||
|
|
@ -354,7 +354,7 @@ export default function ProjectDetail() {
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
<div className="flex items-center gap-3">
|
<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'}`}>
|
<Badge className={`${project.is_active ? 'cyber-badge-success' : 'cyber-badge-muted'}`}>
|
||||||
{project.is_active ? '活跃' : '暂停'}
|
{project.is_active ? '活跃' : '暂停'}
|
||||||
</Badge>
|
</Badge>
|
||||||
|
|
@ -431,11 +431,11 @@ export default function ProjectDetail() {
|
||||||
|
|
||||||
{/* 主要内容 */}
|
{/* 主要内容 */}
|
||||||
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full relative z-10">
|
<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">
|
<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-white font-mono font-bold uppercase py-2 text-gray-400 transition-all rounded-sm">项目概览</TabsTrigger>
|
<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-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-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-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-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-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-foreground font-mono font-bold uppercase py-2 text-muted-foreground transition-all rounded-sm">项目设置</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
||||||
<TabsContent value="overview" className="flex flex-col gap-6 mt-6">
|
<TabsContent value="overview" className="flex flex-col gap-6 mt-6">
|
||||||
|
|
@ -450,7 +450,7 @@ export default function ProjectDetail() {
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{project.repository_url && (
|
{project.repository_url && (
|
||||||
<div className="flex items-center justify-between">
|
<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
|
<a
|
||||||
href={project.repository_url}
|
href={project.repository_url}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
|
@ -464,7 +464,7 @@ export default function ProjectDetail() {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex items-center justify-between">
|
<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'}`}>
|
<Badge className={`${isRepositoryProject(project) ? 'cyber-badge-info' : 'cyber-badge-warning'}`}>
|
||||||
{getSourceTypeLabel(project.source_type)}
|
{getSourceTypeLabel(project.source_type)}
|
||||||
</Badge>
|
</Badge>
|
||||||
|
|
@ -473,7 +473,7 @@ export default function ProjectDetail() {
|
||||||
{isRepositoryProject(project) && (
|
{isRepositoryProject(project) && (
|
||||||
<>
|
<>
|
||||||
<div className="flex items-center justify-between">
|
<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">
|
<Badge className="cyber-badge-muted">
|
||||||
{project.repository_type === 'github' ? 'GitHub' :
|
{project.repository_type === 'github' ? 'GitHub' :
|
||||||
project.repository_type === 'gitlab' ? 'GitLab' : '其他'}
|
project.repository_type === 'gitlab' ? 'GitLab' : '其他'}
|
||||||
|
|
@ -481,26 +481,26 @@ export default function ProjectDetail() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center justify-between">
|
<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>
|
||||||
<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 font-bold text-foreground bg-muted px-2 py-0.5 rounded border border-border">{project.default_branch}</span>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex items-center justify-between">
|
<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>
|
||||||
<span className="text-sm text-gray-300">{formatDate(project.created_at)}</span>
|
<span className="text-sm text-foreground">{formatDate(project.created_at)}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center justify-between">
|
<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>
|
||||||
<span className="text-sm text-gray-300">{project.owner?.full_name || project.owner?.phone || '未知'}</span>
|
<span className="text-sm text-foreground">{project.owner?.full_name || project.owner?.phone || '未知'}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{project.programming_languages && (
|
{project.programming_languages && (
|
||||||
<div className="pt-4 border-t border-gray-800">
|
<div className="pt-4 border-t border-border">
|
||||||
<h4 className="text-sm font-bold mb-2 uppercase text-gray-500">支持的编程语言</h4>
|
<h4 className="text-sm font-bold mb-2 uppercase text-muted-foreground">支持的编程语言</h4>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{JSON.parse(project.programming_languages).map((lang: string) => (
|
{JSON.parse(project.programming_languages).map((lang: string) => (
|
||||||
<Badge key={lang} className="cyber-badge-primary">
|
<Badge key={lang} className="cyber-badge-primary">
|
||||||
|
|
@ -526,22 +526,22 @@ export default function ProjectDetail() {
|
||||||
<Link
|
<Link
|
||||||
key={task.id}
|
key={task.id}
|
||||||
to={`/tasks/${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="flex items-center space-x-3">
|
||||||
<div className={`w-8 h-8 rounded-lg flex items-center justify-center ${
|
<div className={`w-8 h-8 rounded-lg flex items-center justify-center ${
|
||||||
task.status === 'completed' ? 'bg-emerald-500/20' :
|
task.status === 'completed' ? 'bg-emerald-500/20' :
|
||||||
task.status === 'running' ? 'bg-sky-500/20' :
|
task.status === 'running' ? 'bg-sky-500/20' :
|
||||||
task.status === 'failed' ? 'bg-rose-500/20' :
|
task.status === 'failed' ? 'bg-rose-500/20' :
|
||||||
'bg-gray-800/50'
|
'bg-muted'
|
||||||
}`}>
|
}`}>
|
||||||
{getStatusIcon(task.status)}
|
{getStatusIcon(task.status)}
|
||||||
</div>
|
</div>
|
||||||
<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' ? '仓库审计' : '即时分析'}
|
{task.task_type === 'repository' ? '仓库审计' : '即时分析'}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-gray-500 font-mono">
|
<p className="text-xs text-muted-foreground font-mono">
|
||||||
{formatDate(task.created_at)}
|
{formatDate(task.created_at)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -577,21 +577,21 @@ export default function ProjectDetail() {
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{tasks.map((task) => (
|
{tasks.map((task) => (
|
||||||
<div key={task.id} className="cyber-card p-6">
|
<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="flex items-center space-x-3">
|
||||||
<div className={`w-10 h-10 rounded-lg flex items-center justify-center ${
|
<div className={`w-10 h-10 rounded-lg flex items-center justify-center ${
|
||||||
task.status === 'completed' ? 'bg-emerald-500/20' :
|
task.status === 'completed' ? 'bg-emerald-500/20' :
|
||||||
task.status === 'running' ? 'bg-sky-500/20' :
|
task.status === 'running' ? 'bg-sky-500/20' :
|
||||||
task.status === 'failed' ? 'bg-rose-500/20' :
|
task.status === 'failed' ? 'bg-rose-500/20' :
|
||||||
'bg-gray-800/50'
|
'bg-muted'
|
||||||
}`}>
|
}`}>
|
||||||
{getStatusIcon(task.status)}
|
{getStatusIcon(task.status)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h4 className="font-bold text-gray-200 uppercase">
|
<h4 className="font-bold text-foreground uppercase">
|
||||||
{task.task_type === 'repository' ? '仓库审计任务' : '即时分析任务'}
|
{task.task_type === 'repository' ? '仓库审计任务' : '即时分析任务'}
|
||||||
</h4>
|
</h4>
|
||||||
<p className="text-sm text-gray-500 font-mono">
|
<p className="text-sm text-muted-foreground font-mono">
|
||||||
创建于 {formatDate(task.created_at)}
|
创建于 {formatDate(task.created_at)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -600,35 +600,35 @@ export default function ProjectDetail() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-4 font-mono">
|
<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">
|
<div className="text-center p-3 bg-muted rounded-lg border border-border">
|
||||||
<p className="text-2xl font-bold text-white">{task.total_files}</p>
|
<p className="text-2xl font-bold text-foreground">{task.total_files}</p>
|
||||||
<p className="text-xs text-gray-500 uppercase">总文件数</p>
|
<p className="text-xs text-muted-foreground uppercase">总文件数</p>
|
||||||
</div>
|
</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-white">{task.total_lines}</p>
|
<p className="text-2xl font-bold text-foreground">{task.total_lines}</p>
|
||||||
<p className="text-xs text-gray-500 uppercase">代码行数</p>
|
<p className="text-xs text-muted-foreground uppercase">代码行数</p>
|
||||||
</div>
|
</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-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>
|
||||||
<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-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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{task.status === 'completed' && (
|
{task.status === 'completed' && (
|
||||||
<div className="space-y-2 mb-4">
|
<div className="space-y-2 mb-4">
|
||||||
<div className="flex items-center justify-between text-sm font-mono">
|
<div className="flex items-center justify-between text-sm font-mono">
|
||||||
<span className="text-gray-400">质量评分</span>
|
<span className="text-muted-foreground">质量评分</span>
|
||||||
<span className="text-white font-bold">{task.quality_score.toFixed(1)}/100</span>
|
<span className="text-foreground font-bold">{task.quality_score.toFixed(1)}/100</span>
|
||||||
</div>
|
</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>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<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}`}>
|
<Link to={`/tasks/${task.id}`}>
|
||||||
<Button variant="outline" size="sm" className="cyber-btn-outline">
|
<Button variant="outline" size="sm" className="cyber-btn-outline">
|
||||||
<FileText className="w-4 h-4 mr-2" />
|
<FileText className="w-4 h-4 mr-2" />
|
||||||
|
|
@ -641,9 +641,9 @@ export default function ProjectDetail() {
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="cyber-card p-12 text-center">
|
<div className="cyber-card p-12 text-center">
|
||||||
<Activity className="w-16 h-16 text-gray-600 mx-auto mb-4" />
|
<Activity className="w-16 h-16 text-muted-foreground mx-auto mb-4" />
|
||||||
<h3 className="text-lg font-bold text-gray-300 mb-2 uppercase">暂无审计任务</h3>
|
<h3 className="text-lg font-bold text-foreground mb-2 uppercase">暂无审计任务</h3>
|
||||||
<p className="text-sm text-gray-500 mb-6 font-mono">创建第一个审计任务开始代码质量分析</p>
|
<p className="text-sm text-muted-foreground mb-6 font-mono">创建第一个审计任务开始代码质量分析</p>
|
||||||
<Button onClick={handleCreateTask} className="cyber-btn-primary">
|
<Button onClick={handleCreateTask} className="cyber-btn-primary">
|
||||||
<Play className="w-4 h-4 mr-2" />
|
<Play className="w-4 h-4 mr-2" />
|
||||||
创建任务
|
创建任务
|
||||||
|
|
@ -659,7 +659,7 @@ export default function ProjectDetail() {
|
||||||
<h3 className="section-title">最新发现的问题</h3>
|
<h3 className="section-title">最新发现的问题</h3>
|
||||||
</div>
|
</div>
|
||||||
{tasks.length > 0 && (
|
{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)})
|
来自最近一次审计 ({formatDate(tasks[0].created_at)})
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
@ -668,12 +668,12 @@ export default function ProjectDetail() {
|
||||||
{loadingIssues ? (
|
{loadingIssues ? (
|
||||||
<div className="text-center py-12">
|
<div className="text-center py-12">
|
||||||
<div className="loading-spinner mx-auto mb-4"></div>
|
<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>
|
</div>
|
||||||
) : latestIssues.length > 0 ? (
|
) : latestIssues.length > 0 ? (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{latestIssues.map((issue, index) => (
|
{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 justify-between">
|
||||||
<div className="flex items-start space-x-3">
|
<div className="flex items-start space-x-3">
|
||||||
<div className={`w-8 h-8 rounded-lg flex items-center justify-center ${
|
<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" />
|
<AlertTriangle className="w-4 h-4" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h4 className="font-bold text-base text-gray-200 mb-1 uppercase">{issue.title}</h4>
|
<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-gray-500 font-mono">
|
<div className="flex items-center space-x-2 text-xs text-muted-foreground font-mono">
|
||||||
<span className="bg-gray-800 px-2 py-0.5 rounded border border-gray-700">{issue.file_path}:{issue.line_number}</span>
|
<span className="bg-muted px-2 py-0.5 rounded border border-border">{issue.file_path}:{issue.line_number}</span>
|
||||||
<span>{issue.category}</span>
|
<span>{issue.category}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -697,14 +697,14 @@ export default function ProjectDetail() {
|
||||||
issue.severity === 'high' ? 'severity-high' :
|
issue.severity === 'high' ? 'severity-high' :
|
||||||
issue.severity === 'medium' ? 'severity-medium' :
|
issue.severity === 'medium' ? 'severity-medium' :
|
||||||
'severity-low'}
|
'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 === 'critical' ? '严重' :
|
||||||
issue.severity === 'high' ? '高' :
|
issue.severity === 'high' ? '高' :
|
||||||
issue.severity === 'medium' ? '中等' : '低'}
|
issue.severity === 'medium' ? '中等' : '低'}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</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}
|
{issue.description}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -713,8 +713,8 @@ export default function ProjectDetail() {
|
||||||
) : (
|
) : (
|
||||||
<div className="cyber-card p-12 text-center">
|
<div className="cyber-card p-12 text-center">
|
||||||
<CheckCircle className="w-16 h-16 text-emerald-500 mx-auto mb-4" />
|
<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>
|
<h3 className="text-lg font-bold text-foreground mb-2 uppercase">未发现问题</h3>
|
||||||
<p className="text-sm text-gray-500 font-mono">最近一次审计未发现明显问题,或尚未进行审计。</p>
|
<p className="text-sm text-muted-foreground font-mono">最近一次审计未发现明显问题,或尚未进行审计。</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
@ -730,7 +730,7 @@ export default function ProjectDetail() {
|
||||||
{/* 基本信息 */}
|
{/* 基本信息 */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<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
|
<Input
|
||||||
id="edit-name"
|
id="edit-name"
|
||||||
value={editForm.name}
|
value={editForm.name}
|
||||||
|
|
@ -741,7 +741,7 @@ export default function ProjectDetail() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<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
|
<Textarea
|
||||||
id="edit-description"
|
id="edit-description"
|
||||||
value={editForm.description}
|
value={editForm.description}
|
||||||
|
|
@ -755,14 +755,14 @@ export default function ProjectDetail() {
|
||||||
|
|
||||||
{/* 仓库信息 - 仅远程仓库类型显示 */}
|
{/* 仓库信息 - 仅远程仓库类型显示 */}
|
||||||
{editForm.source_type === 'repository' && (
|
{editForm.source_type === 'repository' && (
|
||||||
<div className="space-y-4 border-t border-gray-800 pt-4">
|
<div className="space-y-4 border-t border-border pt-4">
|
||||||
<h3 className="font-mono font-bold uppercase text-sm text-gray-400 flex items-center gap-2">
|
<h3 className="font-mono font-bold uppercase text-sm text-muted-foreground flex items-center gap-2">
|
||||||
<GitBranch className="w-4 h-4" />
|
<GitBranch className="w-4 h-4" />
|
||||||
仓库信息
|
仓库信息
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<div>
|
<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
|
<Input
|
||||||
id="edit-repo-url"
|
id="edit-repo-url"
|
||||||
value={editForm.repository_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 className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<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
|
<Select
|
||||||
value={editForm.repository_type}
|
value={editForm.repository_type}
|
||||||
onValueChange={(value: any) => setEditForm({ ...editForm, repository_type: value })}
|
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">
|
<SelectTrigger id="edit-repo-type" className="cyber-input mt-1">
|
||||||
<SelectValue />
|
<SelectValue />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
<SelectContent className="cyber-dialog border-border">
|
||||||
<SelectItem value="github">GitHub</SelectItem>
|
<SelectItem value="github">GitHub</SelectItem>
|
||||||
<SelectItem value="gitlab">GitLab</SelectItem>
|
<SelectItem value="gitlab">GitLab</SelectItem>
|
||||||
<SelectItem value="other">其他</SelectItem>
|
<SelectItem value="other">其他</SelectItem>
|
||||||
|
|
@ -791,7 +791,7 @@ export default function ProjectDetail() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<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
|
<Input
|
||||||
id="edit-branch"
|
id="edit-branch"
|
||||||
value={editForm.default_branch}
|
value={editForm.default_branch}
|
||||||
|
|
@ -806,7 +806,7 @@ export default function ProjectDetail() {
|
||||||
|
|
||||||
{/* ZIP项目提示 */}
|
{/* ZIP项目提示 */}
|
||||||
{editForm.source_type === '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="bg-amber-500/10 border border-amber-500/30 p-4 rounded">
|
||||||
<div className="flex items-start space-x-3">
|
<div className="flex items-start space-x-3">
|
||||||
<Upload className="w-5 h-5 text-amber-400 mt-0.5" />
|
<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">
|
<div className="space-y-4 border-t border-border pt-4">
|
||||||
<h3 className="font-mono font-bold uppercase text-sm text-gray-400">编程语言</h3>
|
<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">
|
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3">
|
||||||
{supportedLanguages.map((lang) => (
|
{supportedLanguages.map((lang) => (
|
||||||
<div
|
<div
|
||||||
key={lang}
|
key={lang}
|
||||||
className={`flex items-center space-x-2 p-3 border cursor-pointer transition-all rounded ${editForm.programming_languages?.includes(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-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)}
|
onClick={() => handleToggleLanguage(lang)}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`w-4 h-4 border-2 rounded-sm flex items-center justify-center ${editForm.programming_languages?.includes(lang)
|
className={`w-4 h-4 border-2 rounded-sm flex items-center justify-center ${editForm.programming_languages?.includes(lang)
|
||||||
? 'bg-primary border-primary'
|
? 'bg-primary border-primary'
|
||||||
: 'border-gray-600'
|
: 'border-border'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{editForm.programming_languages?.includes(lang) && (
|
{editForm.programming_languages?.includes(lang) && (
|
||||||
<CheckCircle className="w-3 h-3 text-white" />
|
<CheckCircle className="w-3 h-3 text-foreground" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<span className="text-sm font-bold font-mono">{lang}</span>
|
<span className="text-sm font-bold font-mono">{lang}</span>
|
||||||
|
|
@ -850,7 +850,7 @@ export default function ProjectDetail() {
|
||||||
</div>
|
</div>
|
||||||
</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">
|
<Button onClick={handleSaveSettings} className="cyber-btn-primary">
|
||||||
<Edit className="w-4 h-4 mr-2" />
|
<Edit className="w-4 h-4 mr-2" />
|
||||||
保存修改
|
保存修改
|
||||||
|
|
@ -880,21 +880,21 @@ export default function ProjectDetail() {
|
||||||
|
|
||||||
{/* 审计选项对话框 */}
|
{/* 审计选项对话框 */}
|
||||||
<Dialog open={showAuditOptionsDialog} onOpenChange={setShowAuditOptionsDialog}>
|
<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 */}
|
{/* 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="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-red-500/80" />
|
||||||
<div className="w-3 h-3 rounded-full bg-yellow-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 className="w-3 h-3 rounded-full bg-green-500/80" />
|
||||||
</div>
|
</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
|
audit_options@deepaudit
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DialogHeader className="px-6 pt-4">
|
<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" />
|
<Shield className="w-5 h-5 text-primary" />
|
||||||
选择审计方式
|
选择审计方式
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
|
|
@ -903,28 +903,28 @@ export default function ProjectDetail() {
|
||||||
<div className="p-6 space-y-4">
|
<div className="p-6 space-y-4">
|
||||||
<Button
|
<Button
|
||||||
onClick={handleStartFullAudit}
|
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">
|
<div className="flex items-center space-x-2">
|
||||||
<Activity className="w-5 h-5" />
|
<Activity className="w-5 h-5" />
|
||||||
<span className="text-lg font-bold uppercase">全量审计</span>
|
<span className="text-lg font-bold uppercase">全量审计</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-xs text-gray-500 font-mono">扫描项目中的所有文件</span>
|
<span className="text-xs text-muted-foreground font-mono">扫描项目中的所有文件</span>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
onClick={handleOpenCustomAudit}
|
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">
|
<div className="flex items-center space-x-2">
|
||||||
<FileText className="w-5 h-5" />
|
<FileText className="w-5 h-5" />
|
||||||
<span className="text-lg font-bold uppercase">自定义审计</span>
|
<span className="text-lg font-bold uppercase">自定义审计</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-xs text-gray-500 font-mono">选择特定文件进行扫描</span>
|
<span className="text-xs text-muted-foreground font-mono">选择特定文件进行扫描</span>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</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 variant="outline" onClick={() => setShowAuditOptionsDialog(false)} className="w-full cyber-btn-outline">
|
||||||
取消
|
取消
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -275,7 +275,7 @@ export default function Projects() {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'github': return <Github className="w-5 h-5" />;
|
case 'github': return <Github className="w-5 h-5" />;
|
||||||
case 'gitlab': return <GitBranch className="w-5 h-5 text-orange-500" />;
|
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="flex items-center justify-center min-h-[60vh]">
|
||||||
<div className="text-center space-y-4">
|
<div className="text-center space-y-4">
|
||||||
<div className="loading-spinner mx-auto" />
|
<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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
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 */}
|
{/* Grid background */}
|
||||||
<div className="absolute inset-0 cyber-grid-subtle pointer-events-none" />
|
<div className="absolute inset-0 cyber-grid-subtle pointer-events-none" />
|
||||||
|
|
||||||
|
|
@ -423,21 +423,21 @@ export default function Projects() {
|
||||||
初始化项目
|
初始化项目
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</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 */}
|
{/* 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="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-red-500/80" />
|
||||||
<div className="w-3 h-3 rounded-full bg-yellow-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 className="w-3 h-3 rounded-full bg-green-500/80" />
|
||||||
</div>
|
</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
|
new_project@deepaudit
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DialogHeader className="px-6 pt-4 flex-shrink-0">
|
<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" />
|
<Terminal className="w-5 h-5 text-primary" />
|
||||||
初始化新项目
|
初始化新项目
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
|
|
@ -445,17 +445,17 @@ export default function Projects() {
|
||||||
|
|
||||||
<div className="flex-1 overflow-y-auto p-6">
|
<div className="flex-1 overflow-y-auto p-6">
|
||||||
<Tabs defaultValue="repository" className="w-full">
|
<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
|
<TabsTrigger
|
||||||
value="repository"
|
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" />
|
<GitBranch className="w-4 h-4 mr-2" />
|
||||||
Git 仓库
|
Git 仓库
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
value="upload"
|
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" />
|
<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">
|
<TabsContent value="repository" className="flex flex-col gap-5 mt-5">
|
||||||
<div className="grid grid-cols-2 gap-5">
|
<div className="grid grid-cols-2 gap-5">
|
||||||
<div className="space-y-1.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
|
<Input
|
||||||
id="name"
|
id="name"
|
||||||
value={createForm.name}
|
value={createForm.name}
|
||||||
|
|
@ -475,7 +475,7 @@ export default function Projects() {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1.5">
|
<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
|
<Select
|
||||||
value={createForm.repository_type}
|
value={createForm.repository_type}
|
||||||
onValueChange={(value: any) => setCreateForm({ ...createForm, repository_type: value })}
|
onValueChange={(value: any) => setCreateForm({ ...createForm, repository_type: value })}
|
||||||
|
|
@ -483,7 +483,7 @@ export default function Projects() {
|
||||||
<SelectTrigger className="cyber-input">
|
<SelectTrigger className="cyber-input">
|
||||||
<SelectValue />
|
<SelectValue />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
<SelectContent className="cyber-dialog border-border">
|
||||||
<SelectItem value="github">GITHUB</SelectItem>
|
<SelectItem value="github">GITHUB</SelectItem>
|
||||||
<SelectItem value="gitlab">GITLAB</SelectItem>
|
<SelectItem value="gitlab">GITLAB</SelectItem>
|
||||||
<SelectItem value="other">OTHER</SelectItem>
|
<SelectItem value="other">OTHER</SelectItem>
|
||||||
|
|
@ -493,7 +493,7 @@ export default function Projects() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-1.5">
|
<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
|
<Textarea
|
||||||
id="description"
|
id="description"
|
||||||
value={createForm.description}
|
value={createForm.description}
|
||||||
|
|
@ -506,7 +506,7 @@ export default function Projects() {
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-5">
|
<div className="grid grid-cols-2 gap-5">
|
||||||
<div className="space-y-1.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
|
<Input
|
||||||
id="repository_url"
|
id="repository_url"
|
||||||
value={createForm.repository_url}
|
value={createForm.repository_url}
|
||||||
|
|
@ -516,7 +516,7 @@ export default function Projects() {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1.5">
|
<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
|
<Input
|
||||||
id="default_branch"
|
id="default_branch"
|
||||||
value={createForm.default_branch}
|
value={createForm.default_branch}
|
||||||
|
|
@ -528,12 +528,12 @@ export default function Projects() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<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">
|
<div className="flex flex-wrap gap-2">
|
||||||
{supportedLanguages.map((lang) => (
|
{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)
|
<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-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
|
<input
|
||||||
type="checkbox"
|
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>
|
<span className="text-xs font-mono font-bold uppercase">{lang}</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
@ -559,7 +559,7 @@ export default function Projects() {
|
||||||
</div>
|
</div>
|
||||||
</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 variant="outline" onClick={() => setShowCreateDialog(false)} className="cyber-btn-outline">
|
||||||
取消
|
取消
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -571,7 +571,7 @@ export default function Projects() {
|
||||||
|
|
||||||
<TabsContent value="upload" className="flex flex-col gap-5 mt-5">
|
<TabsContent value="upload" className="flex flex-col gap-5 mt-5">
|
||||||
<div className="space-y-1.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
|
<Input
|
||||||
id="upload-name"
|
id="upload-name"
|
||||||
value={createForm.name}
|
value={createForm.name}
|
||||||
|
|
@ -582,7 +582,7 @@ export default function Projects() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-1.5">
|
<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
|
<Textarea
|
||||||
id="upload-description"
|
id="upload-description"
|
||||||
value={createForm.description}
|
value={createForm.description}
|
||||||
|
|
@ -594,12 +594,12 @@ export default function Projects() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<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">
|
<div className="flex flex-wrap gap-2">
|
||||||
{supportedLanguages.map((lang) => (
|
{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)
|
<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-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
|
<input
|
||||||
type="checkbox"
|
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>
|
<span className="text-xs font-mono font-bold uppercase">{lang}</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
@ -626,16 +626,16 @@ export default function Projects() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<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 ? (
|
{!selectedFile ? (
|
||||||
<div
|
<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()}
|
onClick={() => fileInputRef.current?.click()}
|
||||||
>
|
>
|
||||||
<Upload className="w-10 h-10 text-gray-500 mx-auto mb-3 group-hover:text-primary transition-colors" />
|
<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-gray-300 uppercase mb-1">上传 ZIP 归档</h3>
|
<h3 className="text-base font-bold text-foreground uppercase mb-1">上传 ZIP 归档</h3>
|
||||||
<p className="text-[10px] font-mono text-gray-500 mb-3">
|
<p className="text-xs font-mono text-muted-foreground mb-3">
|
||||||
最大: 500MB // 格式: .ZIP
|
最大: 500MB // 格式: .ZIP
|
||||||
</p>
|
</p>
|
||||||
<input
|
<input
|
||||||
|
|
@ -661,14 +661,14 @@ export default function Projects() {
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</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="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" />
|
<FileText className="w-5 h-5 text-primary" />
|
||||||
</div>
|
</div>
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<p className="font-mono font-bold text-sm text-gray-200 truncate">{selectedFile.name}</p>
|
<p className="font-mono font-bold text-sm text-foreground 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 text-xs text-muted-foreground">{(selectedFile.size / 1024 / 1024).toFixed(2)} MB</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -685,18 +685,18 @@ export default function Projects() {
|
||||||
|
|
||||||
{uploading && (
|
{uploading && (
|
||||||
<div className="space-y-1.5">
|
<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>上传并分析中...</span>
|
||||||
<span className="text-primary">{uploadProgress}%</span>
|
<span className="text-primary">{uploadProgress}%</span>
|
||||||
</div>
|
</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>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="bg-amber-500/10 border border-amber-500/30 p-3 rounded">
|
<div className="bg-amber-500/10 border border-amber-500/30 p-3 rounded">
|
||||||
<div className="flex items-start space-x-3">
|
<div className="flex items-start space-x-3">
|
||||||
<AlertCircle className="w-4 h-4 text-amber-400 mt-0.5" />
|
<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>
|
<p className="font-bold mb-1 uppercase">上传协议:</p>
|
||||||
<ul className="space-y-0.5 list-disc list-inside text-amber-400/80">
|
<ul className="space-y-0.5 list-disc list-inside text-amber-400/80">
|
||||||
<li>确保完整的项目代码</li>
|
<li>确保完整的项目代码</li>
|
||||||
|
|
@ -708,7 +708,7 @@ export default function Projects() {
|
||||||
</div>
|
</div>
|
||||||
</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 variant="outline" onClick={() => setShowCreateDialog(false)} disabled={uploading} className="cyber-btn-outline">
|
||||||
取消
|
取消
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -782,7 +782,7 @@ export default function Projects() {
|
||||||
{/* Search and Filter */}
|
{/* Search and Filter */}
|
||||||
<div className="cyber-card p-4 flex items-center gap-4 relative z-10">
|
<div className="cyber-card p-4 flex items-center gap-4 relative z-10">
|
||||||
<div className="flex-1 relative">
|
<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
|
<Input
|
||||||
placeholder="搜索项目..."
|
placeholder="搜索项目..."
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
|
|
@ -802,13 +802,13 @@ export default function Projects() {
|
||||||
filteredProjects.map((project) => (
|
filteredProjects.map((project) => (
|
||||||
<div key={project.id} className="cyber-card flex flex-col h-full group">
|
<div key={project.id} className="cyber-card flex flex-col h-full group">
|
||||||
{/* Card Header */}
|
{/* 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="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)}
|
{getRepositoryIcon(project.repository_type)}
|
||||||
</div>
|
</div>
|
||||||
<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}`}>
|
<Link to={`/projects/${project.id}`}>
|
||||||
{project.name}
|
{project.name}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
@ -828,15 +828,15 @@ export default function Projects() {
|
||||||
{/* Card Body */}
|
{/* Card Body */}
|
||||||
<div className="p-4 flex-1 space-y-3">
|
<div className="p-4 flex-1 space-y-3">
|
||||||
{project.description && (
|
{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}
|
{project.description}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{project.repository_url && (
|
{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">
|
<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-gray-600" />
|
<GitBranch className="w-3 h-3 mr-2 flex-shrink-0 text-muted-foreground" />
|
||||||
<a
|
<a
|
||||||
href={project.repository_url}
|
href={project.repository_url}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
|
@ -848,7 +848,7 @@ export default function Projects() {
|
||||||
</div>
|
</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"><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>
|
<span className="flex items-center"><Users className="w-3 h-3 mr-1" /> {project.owner?.full_name || '未知'}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -857,12 +857,12 @@ export default function Projects() {
|
||||||
{project.programming_languages && (
|
{project.programming_languages && (
|
||||||
<div className="flex flex-wrap gap-1">
|
<div className="flex flex-wrap gap-1">
|
||||||
{JSON.parse(project.programming_languages).slice(0, 4).map((lang: string) => (
|
{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()}
|
{lang.toUpperCase()}
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
{JSON.parse(project.programming_languages).length > 4 && (
|
{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}
|
+{JSON.parse(project.programming_languages).length - 4}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
@ -871,7 +871,7 @@ export default function Projects() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Card Footer */}
|
{/* 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">
|
<Link to={`/projects/${project.id}`} className="col-span-2">
|
||||||
<Button variant="outline" className="w-full cyber-btn-outline h-8 text-xs">
|
<Button variant="outline" className="w-full cyber-btn-outline h-8 text-xs">
|
||||||
<Code className="w-3 h-3 mr-2" />
|
<Code className="w-3 h-3 mr-2" />
|
||||||
|
|
@ -897,11 +897,11 @@ export default function Projects() {
|
||||||
) : (
|
) : (
|
||||||
<div className="col-span-full">
|
<div className="col-span-full">
|
||||||
<div className="cyber-card p-16 text-center border-dashed">
|
<div className="cyber-card p-16 text-center border-dashed">
|
||||||
<Code className="w-16 h-16 text-gray-600 mx-auto mb-4" />
|
<Code className="w-16 h-16 text-muted-foreground mx-auto mb-4" />
|
||||||
<h3 className="text-xl font-bold text-gray-300 mb-2">
|
<h3 className="text-xl font-bold text-foreground mb-2">
|
||||||
{searchTerm ? '未找到匹配项' : '未初始化项目'}
|
{searchTerm ? '未找到匹配项' : '未初始化项目'}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-gray-500 font-mono mb-6">
|
<p className="text-muted-foreground font-mono mb-6">
|
||||||
{searchTerm ? '调整搜索参数' : '初始化第一个项目以开始'}
|
{searchTerm ? '调整搜索参数' : '初始化第一个项目以开始'}
|
||||||
</p>
|
</p>
|
||||||
{!searchTerm && (
|
{!searchTerm && (
|
||||||
|
|
@ -934,21 +934,21 @@ export default function Projects() {
|
||||||
|
|
||||||
{/* Edit Dialog */}
|
{/* Edit Dialog */}
|
||||||
<Dialog open={showEditDialog} onOpenChange={setShowEditDialog}>
|
<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 */}
|
{/* 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="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-red-500/80" />
|
||||||
<div className="w-3 h-3 rounded-full bg-yellow-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 className="w-3 h-3 rounded-full bg-green-500/80" />
|
||||||
</div>
|
</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
|
edit_project@deepaudit
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DialogHeader className="px-6 pt-4 flex-shrink-0">
|
<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" />
|
<Edit className="w-5 h-5 text-primary" />
|
||||||
编辑项目配置
|
编辑项目配置
|
||||||
{projectToEdit && (
|
{projectToEdit && (
|
||||||
|
|
@ -962,9 +962,9 @@ export default function Projects() {
|
||||||
<div className="flex-1 overflow-y-auto p-6 space-y-6">
|
<div className="flex-1 overflow-y-auto p-6 space-y-6">
|
||||||
{/* 基本信息 */}
|
{/* 基本信息 */}
|
||||||
<div className="space-y-4">
|
<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>
|
<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
|
<Input
|
||||||
id="edit-name"
|
id="edit-name"
|
||||||
value={editForm.name}
|
value={editForm.name}
|
||||||
|
|
@ -973,7 +973,7 @@ export default function Projects() {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<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
|
<Textarea
|
||||||
id="edit-description"
|
id="edit-description"
|
||||||
value={editForm.description}
|
value={editForm.description}
|
||||||
|
|
@ -987,13 +987,13 @@ export default function Projects() {
|
||||||
{/* 仓库信息 - 仅远程仓库类型显示 */}
|
{/* 仓库信息 - 仅远程仓库类型显示 */}
|
||||||
{editForm.source_type === 'repository' && (
|
{editForm.source_type === 'repository' && (
|
||||||
<div className="space-y-4">
|
<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" />
|
<GitBranch className="w-4 h-4" />
|
||||||
仓库信息
|
仓库信息
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<div>
|
<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
|
<Input
|
||||||
id="edit-repo-url"
|
id="edit-repo-url"
|
||||||
value={editForm.repository_url}
|
value={editForm.repository_url}
|
||||||
|
|
@ -1005,7 +1005,7 @@ export default function Projects() {
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<div>
|
<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
|
<Select
|
||||||
value={editForm.repository_type}
|
value={editForm.repository_type}
|
||||||
onValueChange={(value: any) => setEditForm({ ...editForm, repository_type: value })}
|
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">
|
<SelectTrigger id="edit-repo-type" className="cyber-input mt-1">
|
||||||
<SelectValue />
|
<SelectValue />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent className="bg-[#0c0c12] border-gray-700">
|
<SelectContent className="cyber-dialog border-border">
|
||||||
<SelectItem value="github">GITHUB</SelectItem>
|
<SelectItem value="github">GITHUB</SelectItem>
|
||||||
<SelectItem value="gitlab">GITLAB</SelectItem>
|
<SelectItem value="gitlab">GITLAB</SelectItem>
|
||||||
<SelectItem value="other">OTHER</SelectItem>
|
<SelectItem value="other">OTHER</SelectItem>
|
||||||
|
|
@ -1022,7 +1022,7 @@ export default function Projects() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<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
|
<Input
|
||||||
id="edit-default-branch"
|
id="edit-default-branch"
|
||||||
value={editForm.default_branch}
|
value={editForm.default_branch}
|
||||||
|
|
@ -1038,7 +1038,7 @@ export default function Projects() {
|
||||||
{/* ZIP项目文件管理 */}
|
{/* ZIP项目文件管理 */}
|
||||||
{editForm.source_type === 'zip' && (
|
{editForm.source_type === 'zip' && (
|
||||||
<div className="space-y-4">
|
<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" />
|
<Upload className="w-4 h-4" />
|
||||||
ZIP文件管理
|
ZIP文件管理
|
||||||
</h3>
|
</h3>
|
||||||
|
|
@ -1087,7 +1087,7 @@ export default function Projects() {
|
||||||
|
|
||||||
{/* 上传新文件 */}
|
{/* 上传新文件 */}
|
||||||
<div className="space-y-2">
|
<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文件'}
|
{editZipInfo?.has_file ? '更新ZIP文件' : '上传ZIP文件'}
|
||||||
</Label>
|
</Label>
|
||||||
<input
|
<input
|
||||||
|
|
@ -1115,7 +1115,7 @@ export default function Projects() {
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<FileText className="w-4 h-4 text-sky-400" />
|
<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-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)
|
({(editZipFile.size / 1024 / 1024).toFixed(2)} MB)
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1144,25 +1144,25 @@ export default function Projects() {
|
||||||
|
|
||||||
{/* 技术栈 */}
|
{/* 技术栈 */}
|
||||||
<div className="space-y-4">
|
<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">
|
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3">
|
||||||
{supportedLanguages.map((lang) => (
|
{supportedLanguages.map((lang) => (
|
||||||
<div
|
<div
|
||||||
key={lang}
|
key={lang}
|
||||||
className={`flex items-center space-x-2 p-2 border cursor-pointer transition-all rounded ${editForm.programming_languages?.includes(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-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)}
|
onClick={() => handleToggleLanguage(lang)}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`w-4 h-4 border-2 rounded-sm flex items-center justify-center ${editForm.programming_languages?.includes(lang)
|
className={`w-4 h-4 border-2 rounded-sm flex items-center justify-center ${editForm.programming_languages?.includes(lang)
|
||||||
? 'bg-primary border-primary'
|
? 'bg-primary border-primary'
|
||||||
: 'border-gray-600'
|
: 'border-border'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{editForm.programming_languages?.includes(lang) && (
|
{editForm.programming_languages?.includes(lang) && (
|
||||||
<CheckCircle className="w-3 h-3 text-white" />
|
<CheckCircle className="w-3 h-3 text-foreground" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<span className="text-sm font-mono font-bold uppercase">{lang}</span>
|
<span className="text-sm font-mono font-bold uppercase">{lang}</span>
|
||||||
|
|
@ -1172,7 +1172,7 @@ export default function Projects() {
|
||||||
</div>
|
</div>
|
||||||
</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 variant="outline" onClick={() => setShowEditDialog(false)} className="cyber-btn-outline">
|
||||||
取消
|
取消
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -1185,7 +1185,7 @@ export default function Projects() {
|
||||||
|
|
||||||
{/* Delete Dialog */}
|
{/* Delete Dialog */}
|
||||||
<AlertDialog open={showDeleteDialog} onOpenChange={setShowDeleteDialog}>
|
<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 */}
|
{/* 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-2 px-4 py-3 bg-rose-500/10 border-b border-rose-500/30">
|
||||||
<div className="flex items-center gap-1.5">
|
<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-yellow-500/80" />
|
||||||
<div className="w-3 h-3 rounded-full bg-green-500/80" />
|
<div className="w-3 h-3 rounded-full bg-green-500/80" />
|
||||||
</div>
|
</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
|
confirm_delete@deepaudit
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<AlertDialogHeader className="p-6">
|
<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" />
|
<Trash2 className="w-5 h-5 text-rose-400" />
|
||||||
确认删除
|
确认删除
|
||||||
</AlertDialogTitle>
|
</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> 到回收站吗?
|
您确定要移动 <span className="font-bold text-rose-400">"{projectToDelete?.name}"</span> 到回收站吗?
|
||||||
</AlertDialogDescription>
|
</AlertDialogDescription>
|
||||||
</AlertDialogHeader>
|
</AlertDialogHeader>
|
||||||
|
|
@ -1220,11 +1220,11 @@ export default function Projects() {
|
||||||
</div>
|
</div>
|
||||||
</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>
|
<AlertDialogCancel className="cyber-btn-outline">取消</AlertDialogCancel>
|
||||||
<AlertDialogAction
|
<AlertDialogAction
|
||||||
onClick={handleConfirmDelete}
|
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>
|
</AlertDialogAction>
|
||||||
|
|
|
||||||
|
|
@ -180,17 +180,17 @@ export default function PromptManager() {
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
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="text-center space-y-4">
|
||||||
<div className="loading-spinner mx-auto" />
|
<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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
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 */}
|
{/* Grid background */}
|
||||||
<div className="absolute inset-0 cyber-grid-subtle pointer-events-none" />
|
<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 p-0 relative z-10">
|
||||||
<div className="cyber-card-header">
|
<div className="cyber-card-header">
|
||||||
<Terminal className="w-5 h-5 text-primary" />
|
<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">
|
<div className="ml-auto">
|
||||||
<Button onClick={() => { resetForm(); setShowCreateDialog(true); }} className="cyber-btn-primary h-9">
|
<Button onClick={() => { resetForm(); setShowCreateDialog(true); }} className="cyber-btn-primary h-9">
|
||||||
<Plus className="w-4 h-4 mr-2" />
|
<Plus className="w-4 h-4 mr-2" />
|
||||||
|
|
@ -279,15 +279,15 @@ export default function PromptManager() {
|
||||||
return (
|
return (
|
||||||
<div key={template.id} className={`cyber-card p-0 ${!template.is_active ? 'opacity-60' : ''}`}>
|
<div key={template.id} className={`cyber-card p-0 ${!template.is_active ? 'opacity-60' : ''}`}>
|
||||||
{/* Template Header */}
|
{/* 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-start justify-between mb-3">
|
||||||
<div className="flex items-center gap-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">
|
<div className="w-10 h-10 bg-muted border border-border flex items-center justify-center rounded">
|
||||||
<TemplateIcon className="w-5 h-5 text-gray-400" />
|
<TemplateIcon className="w-5 h-5 text-muted-foreground" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-bold text-base text-white uppercase">{template.name}</h3>
|
<h3 className="font-bold text-base text-foreground uppercase">{template.name}</h3>
|
||||||
<p className="text-xs text-gray-500 line-clamp-1">{template.description}</p>
|
<p className="text-xs text-muted-foreground line-clamp-1">{template.description}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -301,7 +301,7 @@ export default function PromptManager() {
|
||||||
{/* Template Content Preview */}
|
{/* Template Content Preview */}
|
||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<div
|
<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)}
|
onClick={() => openViewDialog(template)}
|
||||||
title="点击查看完整内容"
|
title="点击查看完整内容"
|
||||||
>
|
>
|
||||||
|
|
@ -344,9 +344,9 @@ export default function PromptManager() {
|
||||||
|
|
||||||
{/* Create/Edit Dialog */}
|
{/* Create/Edit Dialog */}
|
||||||
<Dialog open={showCreateDialog || showEditDialog} onOpenChange={(open) => { if (!open) { setShowCreateDialog(false); setShowEditDialog(false); } }}>
|
<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">
|
<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-gray-800 flex-shrink-0 bg-gray-900/50">
|
<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-white">
|
<DialogTitle className="flex items-center gap-3 font-mono text-foreground">
|
||||||
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
||||||
<Terminal className="w-5 h-5 text-primary" />
|
<Terminal className="w-5 h-5 text-primary" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -354,7 +354,7 @@ export default function PromptManager() {
|
||||||
<span className="text-base font-bold uppercase tracking-wider">
|
<span className="text-base font-bold uppercase tracking-wider">
|
||||||
{showEditDialog ? '编辑模板' : '新建模板'}
|
{showEditDialog ? '编辑模板' : '新建模板'}
|
||||||
</span>
|
</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'}
|
{showEditDialog ? 'Edit Template' : 'Create Template'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -363,29 +363,29 @@ export default function PromptManager() {
|
||||||
<div className="flex-1 overflow-y-auto p-6 space-y-4">
|
<div className="flex-1 overflow-y-auto p-6 space-y-4">
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<div className="space-y-2">
|
<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" />
|
<Input value={form.name} onChange={e => setForm({ ...form, name: e.target.value })} placeholder="如:安全专项审计" className="cyber-input" />
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<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 })}>
|
<Select value={form.template_type} onValueChange={v => setForm({ ...form, template_type: v })}>
|
||||||
<SelectTrigger className="cyber-input"><SelectValue /></SelectTrigger>
|
<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>)}
|
{TEMPLATE_TYPES.map(t => <SelectItem key={t.value} value={t.value}>{t.label}</SelectItem>)}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<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" />
|
<Input value={form.description} onChange={e => setForm({ ...form, description: e.target.value })} placeholder="模板用途描述" className="cyber-input" />
|
||||||
</div>
|
</div>
|
||||||
<Tabs defaultValue="zh" className="w-full">
|
<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">
|
<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-white font-mono font-bold uppercase py-2 text-gray-400 transition-all rounded-sm text-xs">
|
<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>
|
||||||
<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>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
@ -398,10 +398,10 @@ export default function PromptManager() {
|
||||||
</Tabs>
|
</Tabs>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Switch checked={form.is_active} onCheckedChange={v => setForm({ ...form, is_active: v })} />
|
<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>
|
||||||
</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 variant="outline" onClick={() => { setShowCreateDialog(false); setShowEditDialog(false); }} className="cyber-btn-outline">取消</Button>
|
||||||
<Button onClick={showEditDialog ? handleUpdate : handleCreate} className="cyber-btn-primary">{showEditDialog ? '保存' : '创建'}</Button>
|
<Button onClick={showEditDialog ? handleUpdate : handleCreate} className="cyber-btn-primary">{showEditDialog ? '保存' : '创建'}</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
|
|
@ -410,9 +410,9 @@ export default function PromptManager() {
|
||||||
|
|
||||||
{/* Test Dialog */}
|
{/* Test Dialog */}
|
||||||
<Dialog open={showTestDialog} onOpenChange={setShowTestDialog}>
|
<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">
|
<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-gray-800 flex-shrink-0 bg-gray-900/50">
|
<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-white">
|
<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">
|
<div className="p-2 bg-violet-500/20 rounded border border-violet-500/30">
|
||||||
<Sparkles className="w-5 h-5 text-violet-400" />
|
<Sparkles className="w-5 h-5 text-violet-400" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -420,7 +420,7 @@ export default function PromptManager() {
|
||||||
<span className="text-base font-bold uppercase tracking-wider">
|
<span className="text-base font-bold uppercase tracking-wider">
|
||||||
测试提示词: {selectedTemplate?.name}
|
测试提示词: {selectedTemplate?.name}
|
||||||
</span>
|
</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>
|
</div>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
@ -429,14 +429,14 @@ export default function PromptManager() {
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
<div className="space-y-2">
|
<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 => {
|
<Select value={testForm.language} onValueChange={v => {
|
||||||
const templateCodes = selectedTemplate ? TEMPLATE_TEST_CODES[selectedTemplate.name] : null;
|
const templateCodes = selectedTemplate ? TEMPLATE_TEST_CODES[selectedTemplate.name] : null;
|
||||||
const code = templateCodes?.[v] || TEST_CODE_SAMPLES[v] || TEST_CODE_SAMPLES.python;
|
const code = templateCodes?.[v] || TEST_CODE_SAMPLES[v] || TEST_CODE_SAMPLES.python;
|
||||||
setTestForm({ ...testForm, language: v, code });
|
setTestForm({ ...testForm, language: v, code });
|
||||||
}}>
|
}}>
|
||||||
<SelectTrigger className="cyber-input"><SelectValue /></SelectTrigger>
|
<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="python">Python</SelectItem>
|
||||||
<SelectItem value="javascript">JavaScript</SelectItem>
|
<SelectItem value="javascript">JavaScript</SelectItem>
|
||||||
<SelectItem value="java">Java</SelectItem>
|
<SelectItem value="java">Java</SelectItem>
|
||||||
|
|
@ -444,10 +444,10 @@ export default function PromptManager() {
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<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 })}>
|
<Select value={testForm.promptLang} onValueChange={(v: 'zh' | 'en') => setTestForm({ ...testForm, promptLang: v })}>
|
||||||
<SelectTrigger className="cyber-input"><SelectValue /></SelectTrigger>
|
<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="zh">中文提示词</SelectItem>
|
||||||
<SelectItem value="en">英文提示词</SelectItem>
|
<SelectItem value="en">英文提示词</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
|
|
@ -455,7 +455,7 @@ export default function PromptManager() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<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" />
|
<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>
|
</div>
|
||||||
<Button onClick={handleTest} disabled={testing} className="w-full cyber-btn-primary h-12">
|
<Button onClick={handleTest} disabled={testing} className="w-full cyber-btn-primary h-12">
|
||||||
|
|
@ -464,8 +464,8 @@ export default function PromptManager() {
|
||||||
</div>
|
</div>
|
||||||
{/* Right: Results */}
|
{/* Right: Results */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<Label className="text-xs font-bold text-gray-500 uppercase">分析结果</Label>
|
<Label className="text-xs font-bold text-muted-foreground uppercase">分析结果</Label>
|
||||||
<div className="border border-gray-800 h-[400px] overflow-auto bg-[#0a0a0f] rounded">
|
<div className="border border-border h-[400px] overflow-auto cyber-bg-elevated rounded">
|
||||||
{testResult ? (
|
{testResult ? (
|
||||||
testResult.success ? (
|
testResult.success ? (
|
||||||
<div className="flex flex-col h-full">
|
<div className="flex flex-col h-full">
|
||||||
|
|
@ -482,15 +482,15 @@ export default function PromptManager() {
|
||||||
|
|
||||||
{/* Quality Score */}
|
{/* Quality Score */}
|
||||||
{testResult.result?.quality_score !== undefined && (
|
{testResult.result?.quality_score !== undefined && (
|
||||||
<div className="p-3 bg-gray-900/50 border-b border-gray-800 flex items-center justify-between">
|
<div className="p-3 bg-muted border-b border-border flex items-center justify-between">
|
||||||
<span className="text-xs font-bold uppercase text-gray-500">质量评分</span>
|
<span className="text-xs font-bold uppercase text-muted-foreground">质量评分</span>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className={`text-2xl font-bold ${testResult.result.quality_score >= 80 ? 'text-emerald-400' :
|
<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 >= 60 ? 'text-amber-400' : 'text-rose-400'
|
||||||
}`}>
|
}`}>
|
||||||
{testResult.result.quality_score}
|
{testResult.result.quality_score}
|
||||||
</div>
|
</div>
|
||||||
<span className="text-xs text-gray-500">/ 100</span>
|
<span className="text-xs text-muted-foreground">/ 100</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -500,14 +500,14 @@ export default function PromptManager() {
|
||||||
{testResult.result?.issues?.length > 0 ? (
|
{testResult.result?.issues?.length > 0 ? (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="flex items-center justify-between mb-2">
|
<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">
|
<Badge className="cyber-badge-danger">
|
||||||
{testResult.result.issues.length} 个
|
{testResult.result.issues.length} 个
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
{testResult.result.issues.map((issue: any, idx: number) => (
|
{testResult.result.issues.map((issue: any, idx: number) => (
|
||||||
<div key={idx} className="cyber-card p-0 overflow-hidden">
|
<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 === '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'
|
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>}
|
{issue.line && <span className="text-xs opacity-80">行 {issue.line}</span>}
|
||||||
</div>
|
</div>
|
||||||
<div className="p-3">
|
<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 && (
|
{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 && (
|
{issue.suggestion && (
|
||||||
<div className="mt-2 p-2 bg-sky-500/10 border-l-2 border-sky-500 rounded-r">
|
<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" />
|
<Check className="w-6 h-6 text-emerald-400" />
|
||||||
</div>
|
</div>
|
||||||
<p className="font-bold text-emerald-400 uppercase text-sm">未发现问题</p>
|
<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>
|
</div>
|
||||||
)}
|
)}
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
|
@ -567,8 +567,8 @@ export default function PromptManager() {
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col items-center justify-center h-full text-gray-500">
|
<div className="flex flex-col items-center justify-center h-full text-muted-foreground">
|
||||||
<div className="w-16 h-16 bg-gray-800 border border-gray-700 flex items-center justify-center mb-4 rounded">
|
<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" />
|
<Play className="w-8 h-8 opacity-50" />
|
||||||
</div>
|
</div>
|
||||||
<p className="font-mono uppercase text-sm">点击"运行测试"</p>
|
<p className="font-mono uppercase text-sm">点击"运行测试"</p>
|
||||||
|
|
@ -578,7 +578,7 @@ export default function PromptManager() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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>
|
<Button variant="outline" onClick={() => setShowTestDialog(false)} className="cyber-btn-outline">关闭</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
@ -586,9 +586,9 @@ export default function PromptManager() {
|
||||||
|
|
||||||
{/* View Dialog */}
|
{/* View Dialog */}
|
||||||
<Dialog open={showViewDialog} onOpenChange={setShowViewDialog}>
|
<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">
|
<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-gray-800 flex-shrink-0 bg-gray-900/50">
|
<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-white">
|
<DialogTitle className="flex items-center gap-3 font-mono text-foreground">
|
||||||
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
||||||
<FileText className="w-5 h-5 text-primary" />
|
<FileText className="w-5 h-5 text-primary" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -596,7 +596,7 @@ export default function PromptManager() {
|
||||||
<span className="text-base font-bold uppercase tracking-wider">
|
<span className="text-base font-bold uppercase tracking-wider">
|
||||||
{viewTemplate?.name}
|
{viewTemplate?.name}
|
||||||
</span>
|
</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>
|
</div>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
@ -613,27 +613,27 @@ export default function PromptManager() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Tabs defaultValue="zh" className="w-full">
|
<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">
|
<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-white font-mono font-bold uppercase py-2 text-gray-400 transition-all rounded-sm text-xs">
|
<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>
|
||||||
<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>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
<TabsContent value="zh" className="mt-4">
|
<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 || '(无中文内容)'}
|
{viewTemplate?.content_zh || '(无中文内容)'}
|
||||||
</div>
|
</div>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent value="en" className="mt-4">
|
<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)'}
|
{viewTemplate?.content_en || '(No English content)'}
|
||||||
</div>
|
</div>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</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={() => copyToClipboard(viewTemplate?.content_zh || viewTemplate?.content_en || '')} className="cyber-btn-outline">
|
<Button variant="outline" onClick={() => copyToClipboard(viewTemplate?.content_zh || viewTemplate?.content_en || '')} className="cyber-btn-outline">
|
||||||
<Copy className="w-4 h-4 mr-2" />
|
<Copy className="w-4 h-4 mr-2" />
|
||||||
复制内容
|
复制内容
|
||||||
|
|
|
||||||
|
|
@ -109,17 +109,17 @@ export default function RecycleBin() {
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
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="text-center space-y-4">
|
||||||
<div className="loading-spinner mx-auto" />
|
<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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
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 */}
|
{/* Grid background */}
|
||||||
<div className="absolute inset-0 cyber-grid-subtle pointer-events-none" />
|
<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 p-0 relative z-10">
|
||||||
<div className="cyber-card-header">
|
<div className="cyber-card-header">
|
||||||
<Trash2 className="w-5 h-5 text-rose-400" />
|
<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>
|
<Badge className="ml-2 cyber-badge-muted">{deletedProjects.length} 个项目</Badge>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<div className="relative">
|
<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
|
<Input
|
||||||
placeholder="搜索已删除的项目..."
|
placeholder="搜索已删除的项目..."
|
||||||
value={searchTerm}
|
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">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 relative z-10">
|
||||||
{filteredProjects.length > 0 ? (
|
{filteredProjects.length > 0 ? (
|
||||||
filteredProjects.map((project) => (
|
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 */}
|
{/* 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-start justify-between">
|
||||||
<div className="flex items-center space-x-3">
|
<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)}
|
{getRepositoryIcon(project.repository_type)}
|
||||||
</div>
|
</div>
|
||||||
<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}
|
{project.name}
|
||||||
</h3>
|
</h3>
|
||||||
{project.description && (
|
{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}
|
{project.description}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
@ -179,8 +179,8 @@ export default function RecycleBin() {
|
||||||
{/* Project Info */}
|
{/* Project Info */}
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{isRepositoryProject(project) && project.repository_url && (
|
{isRepositoryProject(project) && project.repository_url && (
|
||||||
<div className="flex items-center text-xs text-gray-500">
|
<div className="flex items-center text-xs text-muted-foreground">
|
||||||
<GitBranch className="w-4 h-4 mr-2 flex-shrink-0 text-gray-600" />
|
<GitBranch className="w-4 h-4 mr-2 flex-shrink-0 text-muted-foreground" />
|
||||||
<a
|
<a
|
||||||
href={project.repository_url}
|
href={project.repository_url}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
|
@ -193,13 +193,13 @@ export default function RecycleBin() {
|
||||||
</div>
|
</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">
|
<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)}
|
删除于 {formatDate(project.updated_at)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center">
|
<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 || '未知'}
|
{project.owner?.full_name || '未知'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -222,7 +222,7 @@ export default function RecycleBin() {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Action Buttons */}
|
{/* 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
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
|
@ -262,19 +262,19 @@ export default function RecycleBin() {
|
||||||
|
|
||||||
{/* Restore Dialog */}
|
{/* Restore Dialog */}
|
||||||
<AlertDialog open={showRestoreDialog} onOpenChange={setShowRestoreDialog}>
|
<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">
|
<AlertDialogHeader className="cyber-card-header">
|
||||||
<RotateCcw className="w-5 h-5 text-emerald-400" />
|
<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>
|
</AlertDialogTitle>
|
||||||
</AlertDialogHeader>
|
</AlertDialogHeader>
|
||||||
<AlertDialogDescription className="p-6 text-gray-400">
|
<AlertDialogDescription className="p-6 text-muted-foreground">
|
||||||
您确定要恢复项目 <span className="font-bold text-white">"{selectedProject?.name}"</span> 吗?
|
您确定要恢复项目 <span className="font-bold text-foreground">"{selectedProject?.name}"</span> 吗?
|
||||||
<br /><br />
|
<br /><br />
|
||||||
恢复后,该项目将重新出现在项目列表中,您可以继续使用该项目的所有功能。
|
恢复后,该项目将重新出现在项目列表中,您可以继续使用该项目的所有功能。
|
||||||
</AlertDialogDescription>
|
</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>
|
<AlertDialogCancel className="cyber-btn-outline">取消</AlertDialogCancel>
|
||||||
<AlertDialogAction
|
<AlertDialogAction
|
||||||
onClick={handleConfirmRestore}
|
onClick={handleConfirmRestore}
|
||||||
|
|
@ -288,15 +288,15 @@ export default function RecycleBin() {
|
||||||
|
|
||||||
{/* Permanent Delete Dialog */}
|
{/* Permanent Delete Dialog */}
|
||||||
<AlertDialog open={showPermanentDeleteDialog} onOpenChange={setShowPermanentDeleteDialog}>
|
<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">
|
<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" />
|
<AlertTriangle className="w-5 h-5 text-rose-400" />
|
||||||
<AlertDialogTitle className="text-lg font-bold uppercase tracking-wider text-rose-400">
|
<AlertDialogTitle className="text-lg font-bold uppercase tracking-wider text-rose-400">
|
||||||
警告:永久删除项目
|
警告:永久删除项目
|
||||||
</AlertDialogTitle>
|
</AlertDialogTitle>
|
||||||
</AlertDialogHeader>
|
</AlertDialogHeader>
|
||||||
<AlertDialogDescription className="p-6 text-gray-400">
|
<AlertDialogDescription className="p-6 text-muted-foreground">
|
||||||
您确定要<span className="font-bold text-rose-400 uppercase">永久删除</span>项目 <span className="font-bold text-white">"{selectedProject?.name}"</span> 吗?
|
您确定要<span className="font-bold text-rose-400 uppercase">永久删除</span>项目 <span className="font-bold text-foreground">"{selectedProject?.name}"</span> 吗?
|
||||||
<br /><br />
|
<br /><br />
|
||||||
<div className="bg-rose-500/10 border border-rose-500/30 p-4 rounded">
|
<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">
|
<p className="text-rose-400 font-bold mb-2 uppercase flex items-center">
|
||||||
|
|
@ -310,7 +310,7 @@ export default function RecycleBin() {
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</AlertDialogDescription>
|
</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>
|
<AlertDialogCancel className="cyber-btn-outline">取消</AlertDialogCancel>
|
||||||
<AlertDialogAction
|
<AlertDialogAction
|
||||||
onClick={handleConfirmPermanentDelete}
|
onClick={handleConfirmPermanentDelete}
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ export default function Register() {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
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 */}
|
{/* Scanline overlay */}
|
||||||
<div className="absolute inset-0 pointer-events-none z-20">
|
<div className="absolute inset-0 pointer-events-none z-20">
|
||||||
<div
|
<div
|
||||||
|
|
@ -79,7 +79,7 @@ export default function Register() {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Corner Decorations */}
|
{/* 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">
|
<div className="flex items-center gap-2">
|
||||||
<Terminal className="w-3 h-3" />
|
<Terminal className="w-3 h-3" />
|
||||||
<span>REG_MODULE: ACTIVE</span>
|
<span>REG_MODULE: ACTIVE</span>
|
||||||
|
|
@ -94,17 +94,17 @@ export default function Register() {
|
||||||
</div>
|
</div>
|
||||||
</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>SECURE_CONN: TRUE</div>
|
||||||
<div>VALIDATION: ENABLED</div>
|
<div>VALIDATION: ENABLED</div>
|
||||||
<div>HASH: SHA-256</div>
|
<div>HASH: SHA-256</div>
|
||||||
</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
|
DEEPAUDIT_REG_v3
|
||||||
</div>
|
</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]}
|
{new Date().toISOString().split("T")[0]}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -112,7 +112,7 @@ export default function Register() {
|
||||||
<div className="w-full max-w-md relative z-30 px-4">
|
<div className="w-full max-w-md relative z-30 px-4">
|
||||||
{/* Logo & Title */}
|
{/* Logo & Title */}
|
||||||
<div className="text-center mb-8">
|
<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)' }}>
|
style={{ boxShadow: '0 0 30px rgba(255,107,44,0.1)' }}>
|
||||||
<img
|
<img
|
||||||
src="/logo_deepaudit.png"
|
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)" }}
|
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-primary">DEEP</span>
|
||||||
<span className="text-white">AUDIT</span>
|
<span className="text-foreground">AUDIT</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm font-mono text-gray-500">
|
<p className="text-sm font-mono text-muted-foreground">
|
||||||
// Create New Account
|
// Create New Account
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Register Form Card */}
|
{/* 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)' }}>
|
style={{ boxShadow: '0 4px 30px rgba(0,0,0,0.5)' }}>
|
||||||
{/* Card Header */}
|
{/* 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="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-red-500/80" />
|
||||||
<div className="w-3 h-3 rounded-full bg-yellow-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 className="w-3 h-3 rounded-full bg-green-500/80" />
|
||||||
</div>
|
</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
|
register@deepaudit
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -152,7 +152,7 @@ export default function Register() {
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label
|
<Label
|
||||||
htmlFor="fullName"
|
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>
|
</Label>
|
||||||
|
|
@ -163,16 +163,16 @@ export default function Register() {
|
||||||
value={fullName}
|
value={fullName}
|
||||||
onChange={(e) => setFullName(e.target.value)}
|
onChange={(e) => setFullName(e.target.value)}
|
||||||
required
|
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>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label
|
<Label
|
||||||
htmlFor="email"
|
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>
|
</Label>
|
||||||
|
|
@ -184,16 +184,16 @@ export default function Register() {
|
||||||
value={email}
|
value={email}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
required
|
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>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label
|
<Label
|
||||||
htmlFor="password"
|
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>
|
</Label>
|
||||||
|
|
@ -205,15 +205,15 @@ export default function Register() {
|
||||||
value={password}
|
value={password}
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
required
|
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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
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)' }}
|
style={{ boxShadow: '0 0 20px rgba(255,107,44,0.3)' }}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
>
|
>
|
||||||
|
|
@ -229,8 +229,8 @@ export default function Register() {
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<div className="mt-6 pt-5 border-t border-gray-800/50 text-center">
|
<div className="mt-6 pt-5 border-t border-border text-center">
|
||||||
<p className="text-sm font-mono text-gray-500">
|
<p className="text-sm font-mono text-muted-foreground">
|
||||||
已有账号?{" "}
|
已有账号?{" "}
|
||||||
<span
|
<span
|
||||||
className="text-primary font-bold cursor-pointer hover:underline"
|
className="text-primary font-bold cursor-pointer hover:underline"
|
||||||
|
|
@ -245,7 +245,7 @@ export default function Register() {
|
||||||
|
|
||||||
{/* Version Info */}
|
{/* Version Info */}
|
||||||
<div className="mt-6 text-center">
|
<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
|
Version {version} · Secure Registration
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ function IssuesList({ issues }: { issues: AuditIssue[] }) {
|
||||||
const lowIssues = issues.filter(issue => issue.severity === 'low');
|
const lowIssues = issues.filter(issue => issue.severity === 'low');
|
||||||
|
|
||||||
const renderIssue = (issue: AuditIssue, index: number) => (
|
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 justify-between mb-3">
|
||||||
<div className="flex items-start space-x-3">
|
<div className="flex items-start space-x-3">
|
||||||
<div className={`w-10 h-10 rounded-lg flex items-center justify-center ${
|
<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)}
|
{getTypeIcon(issue.issue_type)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1">
|
<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>
|
<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-gray-500 font-mono">
|
<div className="flex items-center space-x-1 text-xs text-muted-foreground font-mono">
|
||||||
<FileText className="w-3 h-3" />
|
<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>
|
</div>
|
||||||
{issue.line_number && (
|
{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 className="text-primary">></span>
|
||||||
<span>LINE: {issue.line_number}</span>
|
<span>LINE: {issue.line_number}</span>
|
||||||
{issue.column_number && <span>, COL: {issue.column_number}</span>}
|
{issue.column_number && <span>, COL: {issue.column_number}</span>}
|
||||||
|
|
@ -107,7 +107,7 @@ function IssuesList({ issues }: { issues: AuditIssue[] }) {
|
||||||
)}
|
)}
|
||||||
</div>
|
</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 === 'critical' ? '严重' :
|
||||||
issue.severity === 'high' ? '高' :
|
issue.severity === 'high' ? '高' :
|
||||||
issue.severity === 'medium' ? '中等' : '低'}
|
issue.severity === 'medium' ? '中等' : '低'}
|
||||||
|
|
@ -115,31 +115,31 @@ function IssuesList({ issues }: { issues: AuditIssue[] }) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{issue.description && (
|
{issue.description && (
|
||||||
<div className="bg-gray-900/50 border border-gray-800 p-3 mb-3 rounded font-mono">
|
<div className="bg-muted border border-border p-3 mb-3 rounded font-mono">
|
||||||
<div className="flex items-center mb-1 border-b border-gray-800 pb-1">
|
<div className="flex items-center mb-1 border-b border-border pb-1">
|
||||||
<Info className="w-3 h-3 text-gray-500 mr-1" />
|
<Info className="w-3 h-3 text-muted-foreground mr-1" />
|
||||||
<span className="font-bold text-gray-400 text-xs uppercase">问题详情</span>
|
<span className="font-bold text-muted-foreground text-xs uppercase">问题详情</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-gray-300 text-xs leading-relaxed mt-1">
|
<p className="text-foreground text-xs leading-relaxed mt-1">
|
||||||
{issue.description}
|
{issue.description}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{issue.code_snippet && (
|
{issue.code_snippet && (
|
||||||
<div className="bg-[#0a0a0f] p-3 mb-3 border border-gray-800 rounded">
|
<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-gray-800 pb-1">
|
<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="flex items-center space-x-1">
|
||||||
<div className="w-4 h-4 bg-primary rounded flex items-center justify-center">
|
<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>
|
</div>
|
||||||
<span className="text-emerald-400 text-xs font-bold font-mono uppercase">CODE_SNIPPET</span>
|
<span className="text-emerald-400 text-xs font-bold font-mono uppercase">CODE_SNIPPET</span>
|
||||||
</div>
|
</div>
|
||||||
{issue.line_number && (
|
{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>
|
||||||
<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">
|
<pre className="text-xs text-emerald-400 font-mono overflow-x-auto">
|
||||||
<code>{issue.code_snippet}</code>
|
<code>{issue.code_snippet}</code>
|
||||||
</pre>
|
</pre>
|
||||||
|
|
@ -177,21 +177,21 @@ function IssuesList({ issues }: { issues: AuditIssue[] }) {
|
||||||
{parsedExplanation.what && (
|
{parsedExplanation.what && (
|
||||||
<div className="border-l-2 border-rose-500 pl-2">
|
<div className="border-l-2 border-rose-500 pl-2">
|
||||||
<span className="font-bold text-rose-400 uppercase">问题:</span>
|
<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>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{parsedExplanation.why && (
|
{parsedExplanation.why && (
|
||||||
<div className="border-l-2 border-amber-500 pl-2">
|
<div className="border-l-2 border-amber-500 pl-2">
|
||||||
<span className="font-bold text-amber-400 uppercase">原因:</span>
|
<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>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{parsedExplanation.how && (
|
{parsedExplanation.how && (
|
||||||
<div className="border-l-2 border-emerald-500 pl-2">
|
<div className="border-l-2 border-emerald-500 pl-2">
|
||||||
<span className="font-bold text-emerald-400 uppercase">方案:</span>
|
<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>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
@ -218,7 +218,7 @@ function IssuesList({ issues }: { issues: AuditIssue[] }) {
|
||||||
<Zap className="w-4 h-4 text-violet-400 mr-2" />
|
<Zap className="w-4 h-4 text-violet-400 mr-2" />
|
||||||
<span className="font-bold text-violet-300 text-sm uppercase">AI 解释</span>
|
<span className="font-bold text-violet-300 text-sm uppercase">AI 解释</span>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -244,20 +244,20 @@ function IssuesList({ issues }: { issues: AuditIssue[] }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs defaultValue="all" className="w-full">
|
<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">
|
<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-white font-mono font-bold uppercase py-2 text-gray-400 transition-all rounded-sm text-xs">
|
<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})
|
全部 ({issues.length})
|
||||||
</TabsTrigger>
|
</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})
|
严重 ({criticalIssues.length})
|
||||||
</TabsTrigger>
|
</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})
|
高 ({highIssues.length})
|
||||||
</TabsTrigger>
|
</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})
|
中等 ({mediumIssues.length})
|
||||||
</TabsTrigger>
|
</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})
|
低 ({lowIssues.length})
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
@ -272,8 +272,8 @@ function IssuesList({ issues }: { issues: AuditIssue[] }) {
|
||||||
) : (
|
) : (
|
||||||
<div className="cyber-card p-12 text-center border-dashed">
|
<div className="cyber-card p-12 text-center border-dashed">
|
||||||
<CheckCircle className="w-16 h-16 text-emerald-400 mx-auto mb-4" />
|
<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>
|
<h3 className="text-lg font-bold text-foreground uppercase mb-2">没有发现严重问题</h3>
|
||||||
<p className="text-gray-500 font-mono">代码在严重级别的检查中表现良好</p>
|
<p className="text-muted-foreground font-mono">代码在严重级别的检查中表现良好</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
@ -284,8 +284,8 @@ function IssuesList({ issues }: { issues: AuditIssue[] }) {
|
||||||
) : (
|
) : (
|
||||||
<div className="cyber-card p-12 text-center border-dashed">
|
<div className="cyber-card p-12 text-center border-dashed">
|
||||||
<CheckCircle className="w-16 h-16 text-emerald-400 mx-auto mb-4" />
|
<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>
|
<h3 className="text-lg font-bold text-foreground uppercase mb-2">没有发现高优先级问题</h3>
|
||||||
<p className="text-gray-500 font-mono">代码在高优先级检查中表现良好</p>
|
<p className="text-muted-foreground font-mono">代码在高优先级检查中表现良好</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
@ -296,8 +296,8 @@ function IssuesList({ issues }: { issues: AuditIssue[] }) {
|
||||||
) : (
|
) : (
|
||||||
<div className="cyber-card p-12 text-center border-dashed">
|
<div className="cyber-card p-12 text-center border-dashed">
|
||||||
<CheckCircle className="w-16 h-16 text-emerald-400 mx-auto mb-4" />
|
<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>
|
<h3 className="text-lg font-bold text-foreground uppercase mb-2">没有发现中等优先级问题</h3>
|
||||||
<p className="text-gray-500 font-mono">代码在中等优先级检查中表现良好</p>
|
<p className="text-muted-foreground font-mono">代码在中等优先级检查中表现良好</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
@ -308,8 +308,8 @@ function IssuesList({ issues }: { issues: AuditIssue[] }) {
|
||||||
) : (
|
) : (
|
||||||
<div className="cyber-card p-12 text-center border-dashed">
|
<div className="cyber-card p-12 text-center border-dashed">
|
||||||
<CheckCircle className="w-16 h-16 text-emerald-400 mx-auto mb-4" />
|
<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>
|
<h3 className="text-lg font-bold text-foreground uppercase mb-2">没有发现低优先级问题</h3>
|
||||||
<p className="text-gray-500 font-mono">代码在低优先级检查中表现良好</p>
|
<p className="text-muted-foreground font-mono">代码在低优先级检查中表现良好</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
@ -450,8 +450,8 @@ export default function TaskDetail() {
|
||||||
case 'completed': return <CheckCircle className="w-4 h-4 text-emerald-400" />;
|
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 'running': return <Activity className="w-4 h-4 text-sky-400" />;
|
||||||
case 'failed': return <AlertTriangle className="w-4 h-4 text-rose-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" />;
|
case 'cancelled': return <XCircle className="w-4 h-4 text-muted-foreground" />;
|
||||||
default: return <Clock className="w-4 h-4 text-gray-400" />;
|
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="flex items-center justify-center min-h-[60vh]">
|
||||||
<div className="text-center space-y-4">
|
<div className="text-center space-y-4">
|
||||||
<div className="loading-spinner mx-auto" />
|
<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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -478,7 +478,7 @@ export default function TaskDetail() {
|
||||||
|
|
||||||
if (!task) {
|
if (!task) {
|
||||||
return (
|
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">
|
<div className="flex items-center space-x-4">
|
||||||
<Link to="/audit-tasks">
|
<Link to="/audit-tasks">
|
||||||
<Button variant="outline" size="sm" className="cyber-btn-ghost h-10 w-10 p-0">
|
<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>
|
||||||
<div className="cyber-card p-16 text-center">
|
<div className="cyber-card p-16 text-center">
|
||||||
<AlertTriangle className="w-16 h-16 text-rose-400 mx-auto mb-4" />
|
<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>
|
<h3 className="text-xl font-bold text-foreground uppercase mb-2">任务不存在</h3>
|
||||||
<p className="text-gray-500 font-mono">请检查任务ID是否正确</p>
|
<p className="text-muted-foreground font-mono">请检查任务ID是否正确</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -498,7 +498,7 @@ export default function TaskDetail() {
|
||||||
const progressPercentage = calculateTaskProgress(task.scanned_files, task.total_files);
|
const progressPercentage = calculateTaskProgress(task.scanned_files, task.total_files);
|
||||||
|
|
||||||
return (
|
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 */}
|
{/* Grid background */}
|
||||||
<div className="absolute inset-0 cyber-grid-subtle pointer-events-none" />
|
<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') && (
|
{(task.status === 'running' || task.status === 'pending') && (
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
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}
|
onClick={handleCancelTask}
|
||||||
disabled={cancelling}
|
disabled={cancelling}
|
||||||
>
|
>
|
||||||
|
|
@ -545,7 +545,7 @@ export default function TaskDetail() {
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<p className="stat-label">扫描进度</p>
|
<p className="stat-label">扫描进度</p>
|
||||||
<p className="stat-value mb-2">{progressPercentage}%</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>
|
||||||
<div className="stat-icon text-primary ml-4">
|
<div className="stat-icon text-primary ml-4">
|
||||||
<Activity className="w-6 h-6" />
|
<Activity className="w-6 h-6" />
|
||||||
|
|
@ -596,34 +596,34 @@ export default function TaskDetail() {
|
||||||
<div className="cyber-card p-0">
|
<div className="cyber-card p-0">
|
||||||
<div className="cyber-card-header">
|
<div className="cyber-card-header">
|
||||||
<Shield className="w-5 h-5 text-primary" />
|
<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>
|
||||||
<div className="p-6 space-y-4 font-mono">
|
<div className="p-6 space-y-4 font-mono">
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<div>
|
<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>
|
||||||
<p className="text-base font-bold text-gray-200">
|
<p className="text-base font-bold text-foreground">
|
||||||
{task.task_type === 'repository' ? '仓库审计任务' : '即时分析任务'}
|
{task.task_type === 'repository' ? '仓库审计任务' : '即时分析任务'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<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>
|
||||||
<p className="text-base font-bold text-gray-200 flex items-center">
|
<p className="text-base font-bold text-foreground flex items-center">
|
||||||
<GitBranch className="w-4 h-4 mr-1" />
|
<GitBranch className="w-4 h-4 mr-1" />
|
||||||
{task.branch_name || '默认分支'}
|
{task.branch_name || '默认分支'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<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>
|
||||||
<p className="text-base font-bold text-gray-200 flex items-center">
|
<p className="text-base font-bold text-foreground flex items-center">
|
||||||
<Calendar className="w-4 h-4 mr-1" />
|
<Calendar className="w-4 h-4 mr-1" />
|
||||||
{formatDate(task.created_at)}
|
{formatDate(task.created_at)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{task.completed_at && (
|
{task.completed_at && (
|
||||||
<div>
|
<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>
|
||||||
<p className="text-base font-bold text-gray-200 flex items-center">
|
<p className="text-base font-bold text-foreground flex items-center">
|
||||||
<CheckCircle className="w-4 h-4 mr-1" />
|
<CheckCircle className="w-4 h-4 mr-1" />
|
||||||
{formatDate(task.completed_at)}
|
{formatDate(task.completed_at)}
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -633,7 +633,7 @@ export default function TaskDetail() {
|
||||||
|
|
||||||
{task.exclude_patterns && (
|
{task.exclude_patterns && (
|
||||||
<div>
|
<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">
|
<div className="flex flex-wrap gap-2">
|
||||||
{JSON.parse(task.exclude_patterns).map((pattern: string) => (
|
{JSON.parse(task.exclude_patterns).map((pattern: string) => (
|
||||||
<Badge key={pattern} className="cyber-badge-muted">
|
<Badge key={pattern} className="cyber-badge-muted">
|
||||||
|
|
@ -646,8 +646,8 @@ export default function TaskDetail() {
|
||||||
|
|
||||||
{task.scan_config && (
|
{task.scan_config && (
|
||||||
<div>
|
<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="bg-[#0a0a0f] border border-gray-800 p-3 rounded">
|
<div className="cyber-bg-elevated border border-border p-3 rounded">
|
||||||
<pre className="text-xs text-emerald-400 font-mono overflow-x-auto">
|
<pre className="text-xs text-emerald-400 font-mono overflow-x-auto">
|
||||||
{JSON.stringify(JSON.parse(task.scan_config), null, 2)}
|
{JSON.stringify(JSON.parse(task.scan_config), null, 2)}
|
||||||
</pre>
|
</pre>
|
||||||
|
|
@ -662,36 +662,36 @@ export default function TaskDetail() {
|
||||||
<div className="cyber-card p-0">
|
<div className="cyber-card p-0">
|
||||||
<div className="cyber-card-header">
|
<div className="cyber-card-header">
|
||||||
<FileText className="w-5 h-5 text-primary" />
|
<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>
|
||||||
<div className="p-6 space-y-4 font-mono">
|
<div className="p-6 space-y-4 font-mono">
|
||||||
{task.project ? (
|
{task.project ? (
|
||||||
<>
|
<>
|
||||||
<div>
|
<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">
|
<Link to={`/projects/${task.project.id}`} className="text-base font-bold text-primary hover:underline">
|
||||||
{task.project.name}
|
{task.project.name}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
{task.project.description && (
|
{task.project.description && (
|
||||||
<div>
|
<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>
|
||||||
<p className="text-sm text-gray-300">{task.project.description}</p>
|
<p className="text-sm text-foreground">{task.project.description}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div>
|
<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>
|
||||||
<p className="text-base font-bold text-gray-200">{getSourceTypeLabel(task.project.source_type)}</p>
|
<p className="text-base font-bold text-foreground">{getSourceTypeLabel(task.project.source_type)}</p>
|
||||||
</div>
|
</div>
|
||||||
{isRepositoryProject(task.project) && (
|
{isRepositoryProject(task.project) && (
|
||||||
<div>
|
<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>
|
||||||
<p className="text-base font-bold text-gray-200">{task.project.repository_type?.toUpperCase() || 'OTHER'}</p>
|
<p className="text-base font-bold text-foreground">{task.project.repository_type?.toUpperCase() || 'OTHER'}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{task.project.programming_languages && (
|
{task.project.programming_languages && (
|
||||||
<div>
|
<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">
|
<div className="flex flex-wrap gap-1">
|
||||||
{JSON.parse(task.project.programming_languages).map((lang: string) => (
|
{JSON.parse(task.project.programming_languages).map((lang: string) => (
|
||||||
<Badge key={lang} className="cyber-badge-primary">
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -715,7 +715,7 @@ export default function TaskDetail() {
|
||||||
<div className="cyber-card p-0 relative z-10">
|
<div className="cyber-card p-0 relative z-10">
|
||||||
<div className="cyber-card-header">
|
<div className="cyber-card-header">
|
||||||
<Bug className="w-5 h-5 text-amber-400" />
|
<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>
|
||||||
<div className="p-6">
|
<div className="p-6">
|
||||||
<IssuesList issues={issues} />
|
<IssuesList issues={issues} />
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ export const PLATFORM_COLORS: Record<RepositoryPlatform, {
|
||||||
bg: string;
|
bg: string;
|
||||||
text: 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' },
|
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'],
|
display: ['"Orbitron"', '"Rajdhani"', 'sans-serif'],
|
||||||
},
|
},
|
||||||
fontSize: {
|
fontSize: {
|
||||||
'xs': ['0.75rem', { lineHeight: '1rem', letterSpacing: '0.01em' }],
|
'xs': ['0.8125rem', { lineHeight: '1.125rem', letterSpacing: '0.01em' }], // 13px
|
||||||
'sm': ['0.875rem', { lineHeight: '1.25rem', letterSpacing: '0.01em' }],
|
'sm': ['0.9375rem', { lineHeight: '1.375rem', letterSpacing: '0.01em' }], // 15px
|
||||||
'base': ['1rem', { lineHeight: '1.5rem', letterSpacing: '0' }],
|
'base': ['1rem', { lineHeight: '1.5rem', letterSpacing: '0' }], // 16px
|
||||||
'lg': ['1.125rem', { lineHeight: '1.75rem', letterSpacing: '-0.01em' }],
|
'lg': ['1.125rem', { lineHeight: '1.75rem', letterSpacing: '-0.01em' }], // 18px
|
||||||
'xl': ['1.25rem', { lineHeight: '1.75rem', letterSpacing: '-0.01em' }],
|
'xl': ['1.375rem', { lineHeight: '1.875rem', letterSpacing: '-0.01em' }], // 22px
|
||||||
'2xl': ['1.5rem', { lineHeight: '2rem', letterSpacing: '-0.02em' }],
|
'2xl': ['1.625rem', { lineHeight: '2.125rem', letterSpacing: '-0.02em' }], // 26px
|
||||||
'3xl': ['1.875rem', { lineHeight: '2.25rem', letterSpacing: '-0.02em' }],
|
'3xl': ['2rem', { lineHeight: '2.5rem', letterSpacing: '-0.02em' }], // 32px
|
||||||
|
'4xl': ['2.5rem', { lineHeight: '3rem', letterSpacing: '-0.02em' }], // 40px
|
||||||
},
|
},
|
||||||
// Extended Color System
|
// Extended Color System
|
||||||
colors: {
|
colors: {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue