diff --git a/frontend/public/fonts/OFL.txt b/frontend/public/fonts/OFL.txt new file mode 100644 index 0000000..0145130 --- /dev/null +++ b/frontend/public/fonts/OFL.txt @@ -0,0 +1,94 @@ +Copyright (c) 2021, TakWolf (https://takwolf.com), +with Reserved Font Name "Ark Pixel". + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/frontend/public/fonts/ark-pixel-12px-monospaced-ja.ttf.woff2 b/frontend/public/fonts/ark-pixel-12px-monospaced-ja.ttf.woff2 new file mode 100644 index 0000000..2eea7a2 Binary files /dev/null and b/frontend/public/fonts/ark-pixel-12px-monospaced-ja.ttf.woff2 differ diff --git a/frontend/public/fonts/ark-pixel-12px-monospaced-ko.ttf.woff2 b/frontend/public/fonts/ark-pixel-12px-monospaced-ko.ttf.woff2 new file mode 100644 index 0000000..60e8c1a Binary files /dev/null and b/frontend/public/fonts/ark-pixel-12px-monospaced-ko.ttf.woff2 differ diff --git a/frontend/public/fonts/ark-pixel-12px-monospaced-latin.ttf.woff2 b/frontend/public/fonts/ark-pixel-12px-monospaced-latin.ttf.woff2 new file mode 100644 index 0000000..81e0c3d Binary files /dev/null and b/frontend/public/fonts/ark-pixel-12px-monospaced-latin.ttf.woff2 differ diff --git a/frontend/public/fonts/ark-pixel-12px-monospaced-zh_cn.ttf.woff2 b/frontend/public/fonts/ark-pixel-12px-monospaced-zh_cn.ttf.woff2 new file mode 100644 index 0000000..02f4570 Binary files /dev/null and b/frontend/public/fonts/ark-pixel-12px-monospaced-zh_cn.ttf.woff2 differ diff --git a/frontend/public/fonts/ark-pixel-12px-monospaced-zh_hk.ttf.woff2 b/frontend/public/fonts/ark-pixel-12px-monospaced-zh_hk.ttf.woff2 new file mode 100644 index 0000000..cc39c7a Binary files /dev/null and b/frontend/public/fonts/ark-pixel-12px-monospaced-zh_hk.ttf.woff2 differ diff --git a/frontend/public/fonts/ark-pixel-12px-monospaced-zh_tr.ttf.woff2 b/frontend/public/fonts/ark-pixel-12px-monospaced-zh_tr.ttf.woff2 new file mode 100644 index 0000000..6ffbc4e Binary files /dev/null and b/frontend/public/fonts/ark-pixel-12px-monospaced-zh_tr.ttf.woff2 differ diff --git a/frontend/public/fonts/ark-pixel-12px-monospaced-zh_tw.ttf.woff2 b/frontend/public/fonts/ark-pixel-12px-monospaced-zh_tw.ttf.woff2 new file mode 100644 index 0000000..96411e5 Binary files /dev/null and b/frontend/public/fonts/ark-pixel-12px-monospaced-zh_tw.ttf.woff2 differ diff --git a/frontend/public/images/example1.png b/frontend/public/images/example1.png index ffda1ee..245dfbc 100644 Binary files a/frontend/public/images/example1.png and b/frontend/public/images/example1.png differ diff --git a/frontend/public/images/example2.png b/frontend/public/images/example2.png index b5464b6..702693f 100644 Binary files a/frontend/public/images/example2.png and b/frontend/public/images/example2.png differ diff --git a/frontend/public/images/example3.png b/frontend/public/images/example3.png index 1ee09fd..e58259e 100644 Binary files a/frontend/public/images/example3.png and b/frontend/public/images/example3.png differ diff --git a/frontend/src/assets/styles/globals.css b/frontend/src/assets/styles/globals.css index 4f888cc..9f2b3a7 100644 --- a/frontend/src/assets/styles/globals.css +++ b/frontend/src/assets/styles/globals.css @@ -2,6 +2,14 @@ @tailwind components; @tailwind utilities; +@font-face { + font-family: 'ArkPixel'; + src: url('/fonts/ark-pixel-12px-monospaced-zh_cn.ttf.woff2') format('woff2'); + font-weight: normal; + font-style: normal; + font-display: swap; +} + /* XCodeReviewer Design System Aesthetic: Terminal Retro + Pixel Art + Mechanical + Cassette Futurism @@ -357,8 +365,30 @@ box-shadow: var(--shadow-sm); } + /* Smooth transitions */ /* Smooth transitions */ .terminal-transition { transition: all 0.15s ease; } + + /* Text Glow Effect */ + .text-shadow-glow { + text-shadow: 0 0 2px currentColor; + } + + /* Custom Scrollbar for Terminal */ + .custom-scrollbar::-webkit-scrollbar { + width: 10px; + background-color: #1a1a1a; + } + + .custom-scrollbar::-webkit-scrollbar-thumb { + background-color: #4a4a4a; + border: 2px solid #1a1a1a; + border-radius: 0; + } + + .custom-scrollbar::-webkit-scrollbar-thumb:hover { + background-color: #666; + } } \ No newline at end of file diff --git a/frontend/src/components/audit/TerminalProgressDialog.tsx b/frontend/src/components/audit/TerminalProgressDialog.tsx index 91f0c49..32ea471 100644 --- a/frontend/src/components/audit/TerminalProgressDialog.tsx +++ b/frontend/src/components/audit/TerminalProgressDialog.tsx @@ -1,7 +1,7 @@ -import { useEffect, useRef, useState } from "react"; +import { useEffect, useRef, useState, useCallback } from "react"; import { Dialog, DialogOverlay, DialogPortal } from "@/components/ui/dialog"; import * as DialogPrimitive from "@radix-ui/react-dialog"; -import { Terminal, X as XIcon } from "lucide-react"; +import { Terminal, X as XIcon, Activity, Cpu, HardDrive, AlertTriangle, CheckCircle2 } from "lucide-react"; import { cn, calculateTaskProgress } from "@/shared/utils/utils"; import * as VisuallyHidden from "@radix-ui/react-visually-hidden"; import { taskControl } from "@/shared/services/taskControl"; @@ -16,6 +16,7 @@ interface TerminalProgressDialogProps { } interface LogEntry { + id: string; timestamp: string; message: string; type: "info" | "success" | "error" | "warning"; @@ -36,15 +37,39 @@ export default function TerminalProgressDialog({ const pollIntervalRef = useRef(null); const hasInitializedLogsRef = useRef(false); + // Refs for state accessed in intervals/effects to avoid dependency cycles + const logsRef = useRef([]); + const isCompletedRef = useRef(false); + const isFailedRef = useRef(false); + const isCancelledRef = useRef(false); + + // Sync refs with state + useEffect(() => { + logsRef.current = logs; + }, [logs]); + + useEffect(() => { + isCompletedRef.current = isCompleted; + }, [isCompleted]); + + useEffect(() => { + isFailedRef.current = isFailed; + }, [isFailed]); + + useEffect(() => { + isCancelledRef.current = isCancelled; + }, [isCancelled]); + // 添加日志条目 - const addLog = (message: string, type: LogEntry["type"] = "info") => { + const addLog = useCallback((message: string, type: LogEntry["type"] = "info") => { const timestamp = new Date().toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit", second: "2-digit" }); - setLogs(prev => [...prev, { timestamp, message, type }]); - }; + const newLog = { id: Math.random().toString(36).substr(2, 9), timestamp, message, type }; + setLogs(prev => [...prev, newLog]); + }, []); // 取消任务处理 const handleCancel = async () => { @@ -57,13 +82,14 @@ export default function TerminalProgressDialog({ // 1. 标记任务为取消状态 taskControl.cancelTask(taskId); setIsCancelled(true); - addLog("🛑 用户取消任务,正在停止...", "error"); + addLog("[ERR] 用户取消任务,正在停止...", "error"); // 2. 立即更新数据库状态 try { const { api } = await import("@/shared/config/database"); + // biome-ignore lint/suspicious/noExplicitAny: API type mismatch workaround await api.updateAuditTask(taskId, { status: 'cancelled' } as any); - addLog("✓ 任务状态已更新为已取消", "warning"); + addLog("[WARN] 任务状态已更新为已取消", "warning"); toast.success("任务已取消"); } catch (error) { console.error('更新取消状态失败:', error); @@ -72,6 +98,7 @@ export default function TerminalProgressDialog({ }; // 自动滚动到底部 + // biome-ignore lint/correctness/useExhaustiveDependencies: We want to scroll when logs change useEffect(() => { logsEndRef.current?.scrollIntoView({ behavior: "smooth" }); }, [logs]); @@ -89,15 +116,17 @@ export default function TerminalProgressDialog({ return () => { clearInterval(timeInterval); }; - }, [open, isCompleted, isFailed]); + }, [open, isCompleted, isFailed, isCancelled]); // 轮询任务状态 useEffect(() => { if (!open || !taskId) { // 清理状态 setLogs([]); + logsRef.current = []; setIsCompleted(false); setIsFailed(false); + setIsCancelled(false); hasInitializedLogsRef.current = false; if (pollIntervalRef.current) { clearInterval(pollIntervalRef.current); @@ -111,24 +140,24 @@ export default function TerminalProgressDialog({ hasInitializedLogsRef.current = true; // 初始化日志 - addLog("🚀 审计任务已启动", "info"); - addLog(`任务ID: ${taskId}`, "info"); - addLog(`任务类型: ${taskType === "repository" ? "仓库审计" : "ZIP文件审计"}`, "info"); - addLog("⏳ 正在初始化审计环境...", "info"); + addLog("[INFO] 审计任务已启动", "info"); + addLog(`TASK_ID: ${taskId}`, "info"); + addLog(`TYPE: ${taskType === "repository" ? "REPO_AUDIT" : "ZIP_AUDIT"}`, "info"); + addLog("[WAIT] 正在初始化审计环境...", "info"); } let lastScannedFiles = 0; let lastIssuesCount = 0; let lastTotalLines = 0; let lastStatus = ""; - let pollCount = 0; + let _pollCount = 0; let hasDataChange = false; let isFirstPoll = true; // 开始轮询 const pollTask = async () => { // 如果任务已完成或失败,停止轮询 - if (isCompleted || isFailed) { + if (isCompletedRef.current || isFailedRef.current) { if (pollIntervalRef.current) { clearInterval(pollIntervalRef.current); pollIntervalRef.current = null; @@ -137,7 +166,7 @@ export default function TerminalProgressDialog({ } try { - pollCount++; + _pollCount++; hasDataChange = false; const requestStartTime = Date.now(); @@ -149,7 +178,7 @@ export default function TerminalProgressDialog({ const requestDuration = Date.now() - requestStartTime; if (!task) { - addLog(`❌ 任务不存在 (${requestDuration}ms)`, "error"); + addLog(`[ERR] 任务不存在 (${requestDuration}ms)`, "error"); throw new Error("任务不存在"); } @@ -168,9 +197,9 @@ export default function TerminalProgressDialog({ // 只在有变化时显示请求/响应信息(跳过 pending 状态) if (hasDataChange && task.status !== "pending") { - addLog(`🔄 正在获取任务状态...`, "info"); + addLog(`[NET] 正在获取任务状态...`, "info"); addLog( - `✓ 状态: ${task.status} | 文件: ${task.scanned_files}/${task.total_files} | 问题: ${task.issues_count} (${requestDuration}ms)`, + `[OK] 状态: ${task.status} | 文件: ${task.scanned_files}/${task.total_files} | 问题: ${task.issues_count} (${requestDuration}ms)`, "success" ); } @@ -185,12 +214,12 @@ export default function TerminalProgressDialog({ // 静默跳过 pending 状态,不显示任何日志 } else if (task.status === "running") { // 首次进入运行状态 - if (statusChanged && logs.filter(l => l.message.includes("开始扫描")).length === 0) { - addLog("🔍 开始扫描代码文件...", "info"); + if (statusChanged && logsRef.current.filter(l => l.message.includes("开始扫描")).length === 0) { + addLog("[SCAN] 开始扫描代码文件...", "info"); if (task.project) { - addLog(`📁 项目: ${task.project.name}`, "info"); + addLog(`[PROJ] 项目: ${task.project.name}`, "info"); if (task.branch_name) { - addLog(`🌿 分支: ${task.branch_name}`, "info"); + addLog(`[BRCH] 分支: ${task.branch_name}`, "info"); } } } @@ -200,7 +229,7 @@ export default function TerminalProgressDialog({ const progress = calculateTaskProgress(task.scanned_files, task.total_files); const filesProcessed = task.scanned_files - lastScannedFiles; addLog( - `📊 扫描进度: ${task.scanned_files || 0}/${task.total_files || 0} 文件 (${progress}%) [+${filesProcessed}]`, + `[PROG] 扫描进度: ${task.scanned_files || 0}/${task.total_files || 0} 文件 (${progress}%) [+${filesProcessed}]`, "info" ); lastScannedFiles = task.scanned_files; @@ -209,25 +238,25 @@ export default function TerminalProgressDialog({ // 显示问题发现(仅在有变化时) if (issuesChanged && task.issues_count > lastIssuesCount) { const newIssues = task.issues_count - lastIssuesCount; - addLog(`⚠️ 发现 ${newIssues} 个新问题 (总计: ${task.issues_count})`, "warning"); + addLog(`[WARN] 发现 ${newIssues} 个新问题 (总计: ${task.issues_count})`, "warning"); lastIssuesCount = task.issues_count; } // 显示代码行数(仅在有变化时) if (linesChanged && task.total_lines > lastTotalLines) { const newLines = task.total_lines - lastTotalLines; - addLog(`📝 已分析 ${task.total_lines.toLocaleString()} 行代码 [+${newLines.toLocaleString()}]`, "info"); + addLog(`[STAT] 已分析 ${task.total_lines.toLocaleString()} 行代码 [+${newLines.toLocaleString()}]`, "info"); lastTotalLines = task.total_lines; } } else if (task.status === "completed") { // 任务完成 - if (!isCompleted) { + if (!isCompletedRef.current) { addLog("", "info"); // 空行分隔 - addLog("✅ 代码扫描完成", "success"); - addLog("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━", "info"); - addLog(`📊 总计扫描: ${task.total_files} 个文件`, "success"); - addLog(`📝 总计分析: ${task.total_lines.toLocaleString()} 行代码`, "success"); - addLog(`⚠️ 发现问题: ${task.issues_count} 个`, task.issues_count > 0 ? "warning" : "success"); + addLog("[DONE] 代码扫描完成", "success"); + addLog("----------------------------------", "info"); + addLog(`[STAT] 总计扫描: ${task.total_files} 个文件`, "success"); + addLog(`[STAT] 总计分析: ${task.total_lines.toLocaleString()} 行代码`, "success"); + addLog(`[RSLT] 发现问题: ${task.issues_count} 个`, task.issues_count > 0 ? "warning" : "success"); // 解析问题类型分布 if (task.issues_count > 0) { @@ -243,31 +272,31 @@ export default function TerminalProgressDialog({ }; if (severityCounts.critical > 0) { - addLog(` 🔴 严重: ${severityCounts.critical} 个`, "error"); + addLog(` [CRIT] 严重: ${severityCounts.critical} 个`, "error"); } if (severityCounts.high > 0) { - addLog(` 🟠 高: ${severityCounts.high} 个`, "warning"); + addLog(` [HIGH] 高: ${severityCounts.high} 个`, "warning"); } if (severityCounts.medium > 0) { - addLog(` 🟡 中等: ${severityCounts.medium} 个`, "warning"); + addLog(` [MED] 中等: ${severityCounts.medium} 个`, "warning"); } if (severityCounts.low > 0) { - addLog(` 🟢 低: ${severityCounts.low} 个`, "info"); + addLog(` [LOW] 低: ${severityCounts.low} 个`, "info"); } - } catch (e) { + } catch (_e) { // 静默处理错误 } } - addLog(`⭐ 质量评分: ${task.quality_score.toFixed(1)}/100`, "success"); - addLog("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━", "info"); - addLog("🎉 审计任务已完成!", "success"); + addLog(`[SCOR] 质量评分: ${task.quality_score.toFixed(1)}/100`, "success"); + addLog("----------------------------------", "info"); + addLog("[FIN] 审计任务已完成!", "success"); if (task.completed_at) { const startTime = new Date(task.created_at).getTime(); const endTime = new Date(task.completed_at).getTime(); const duration = Math.round((endTime - startTime) / 1000); - addLog(`⏱️ 总耗时: ${duration} 秒`, "info"); + addLog(`[TIME] 总耗时: ${duration} 秒`, "info"); } setIsCompleted(true); @@ -278,16 +307,16 @@ export default function TerminalProgressDialog({ } } else if (task.status === "cancelled") { // 任务被取消 - if (!isCancelled) { + if (!isCancelledRef.current) { addLog("", "info"); // 空行分隔 - addLog("🛑 任务已被用户取消", "warning"); - addLog("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━", "warning"); - addLog(`📊 完成统计:`, "info"); + addLog("[STOP] 任务已被用户取消", "warning"); + addLog("----------------------------------", "warning"); + addLog(`[STAT] 完成统计:`, "info"); addLog(` • 已分析文件: ${task.scanned_files}/${task.total_files}`, "info"); addLog(` • 发现问题: ${task.issues_count} 个`, "info"); addLog(` • 代码行数: ${task.total_lines.toLocaleString()} 行`, "info"); - addLog("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━", "warning"); - addLog("✓ 已分析的结果已保存到数据库", "success"); + addLog("----------------------------------", "warning"); + addLog("[SAVE] 已分析的结果已保存到数据库", "success"); setIsCancelled(true); if (pollIntervalRef.current) { @@ -297,10 +326,10 @@ export default function TerminalProgressDialog({ } } else if (task.status === "failed") { // 任务失败 - if (!isFailed) { + if (!isFailedRef.current) { addLog("", "info"); // 空行分隔 - addLog("❌ 审计任务执行失败", "error"); - addLog("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━", "error"); + addLog("[FAIL] 审计任务执行失败", "error"); + addLog("----------------------------------", "error"); // 尝试从日志系统获取具体错误信息 try { @@ -338,7 +367,7 @@ export default function TerminalProgressDialog({ addLog(" • GitHub/GitLab API 限流", "error"); addLog(" • LLM API 配置错误或额度不足", "error"); } - } catch (e) { + } catch (_e) { // 如果获取日志失败,显示常见原因 addLog("可能的原因:", "error"); addLog(" • 网络连接问题", "error"); @@ -347,9 +376,9 @@ export default function TerminalProgressDialog({ addLog(" • LLM API 配置错误或额度不足", "error"); } - addLog("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━", "error"); - addLog("💡 建议: 检查系统配置和网络连接后重试", "warning"); - addLog("📋 查看完整日志: 导航栏 -> 系统日志", "warning"); + addLog("----------------------------------", "error"); + addLog("[HINT] 建议: 检查系统配置和网络连接后重试", "warning"); + addLog("[LOGS] 查看完整日志: 导航栏 -> 系统日志", "warning"); setIsFailed(true); if (pollIntervalRef.current) { @@ -358,8 +387,9 @@ export default function TerminalProgressDialog({ } } } - } catch (error: any) { - addLog(`❌ ${error.message || "未知错误"}`, "error"); + } catch (error: unknown) { + const errorMessage = error instanceof Error ? error.message : "未知错误"; + addLog(`[ERR] ${errorMessage}`, "error"); // 不中断轮询,继续尝试 } }; @@ -377,42 +407,41 @@ export default function TerminalProgressDialog({ pollIntervalRef.current = null; } }; - }, [open, taskId, taskType]); + }, [open, taskId, taskType, addLog]); - // 获取日志颜色 - 使用优雅的深红色主题 + // 获取日志颜色 - 简化配色,减少颜色数量 const getLogColor = (type: LogEntry["type"]) => { switch (type) { case "success": - return "text-green-500"; + return "text-[#00ff41]"; // 纯绿色 case "error": - return "text-red-500"; + return "text-[#ff3333]"; // 纯红色 case "warning": - return "text-yellow-500"; + return "text-[#ffb900]"; // 琥珀色 default: - return "text-gray-300"; + return "text-[#cccccc]"; // 浅灰色 (原为青色) } }; return ( - + e.preventDefault()} onInteractOutside={(e) => e.preventDefault()} > - {/* 无障碍访问标题 */} 审计进度监控 @@ -420,111 +449,191 @@ export default function TerminalProgressDialog({ - {/* 终端头部 */} -
-
- - TERMINAL // 审计进度监控 + {/* 机械外壳装饰 - 螺丝 */} +
+
+
+
+
+
+
+
+
+
+
+
+ + {/* 顶部控制面板 */} +
+ {/* 装饰条纹 */} +
+ +
+
+ +
+
+ System Monitor + AUDIT_TERMINAL_V2.0 +
-
- {/* 模拟窗口控制按钮 */} -
-
-
-
-
+ +
+ {/* 状态指示灯组 */} +
+
+
+
+
- {/* 终端内容 */} -
- {/* 扫描线效果 */} -
+ {/* 主体内容区 - 包含侧边栏和屏幕 */} +
+ {/* 左侧数据面板 */} +
+ {/* 装饰背景 */} +
-
- {logs.map((log, index) => ( -
- - [{log.timestamp}] - - - {log.type === 'info' && '> '} - {log.type === 'success' && '✓ '} - {log.type === 'error' && '✗ '} - {log.type === 'warning' && '! '} - {log.message} - +
+
Task ID
+
{taskId?.slice(0, 8)}...
+
+ +
+
Type
+
+ {taskType === 'repository' ? : } + {taskType}
- ))} +
- {/* 光标旋转闪烁效果 */} - {!isCompleted && !isFailed && ( -
- [{currentTime}] - _ +
+ + {/* 装饰性条形码/数据块 */} +
+
+
+
+
+
+
- )} - -
-
-
- - {/* 底部控制和提示 */} -
-
-
- - {isCancelled ? "STATUS: CANCELLED // 任务已取消" : - isCompleted ? "STATUS: COMPLETED // 任务已完成" : - isFailed ? "STATUS: FAILED // 任务失败" : - "STATUS: RUNNING // 审计进行中..."} - +
+ MEM: 64K OK
+ CPU: ACTIVE
+ NET: LINKED +
+
-
- {/* 运行中显示取消按钮 */} - {!isCompleted && !isFailed && !isCancelled && ( - - )} + {/* 中央屏幕区域 */} +
+ {/* 屏幕边框 */} +
+ {/* 屏幕内边框 */} +
- {/* 失败时显示查看日志按钮 */} - {isFailed && ( - - )} + {/* 屏幕内容 */} +
+ {/* CRT 效果层 */} +
+
- {/* 已完成/失败/取消显示关闭按钮 */} - {(isCompleted || isFailed || isCancelled) && ( - - )} + {/* 像素网格 */} +
+ +
+ {logs.map((log) => ( +
+ + {log.timestamp} + + + {log.message} + +
+ ))} + + {!isCompleted && !isFailed && ( +
+ {currentTime} + _ +
+ )} +
+
+
+
+ + {/* 屏幕下方控制区 */} +
+
+
+ Status +
+ {isCancelled ? ( + CANCELLED + ) : isCompleted ? ( + COMPLETED + ) : isFailed ? ( + FAILED + ) : ( + RUNNING... + )} +
+
+
+ +
+ {!isCompleted && !isFailed && !isCancelled && ( + + )} + + {isFailed && ( + + )} + + {(isCompleted || isFailed || isCancelled) && ( + + )} +
+
diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index 42ebfdd..02a6796 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -20,7 +20,7 @@ export default { extend: { // Typography - Pixel-perfect monospace for terminal aesthetic fontFamily: { - mono: ['"JetBrains Mono"', '"Roboto Mono"', '"Courier New"', 'monospace'], + mono: ['"ArkPixel"', '"JetBrains Mono"', '"Roboto Mono"', '"Courier New"', 'monospace'], sans: ['"Inter"', 'system-ui', 'sans-serif'], display: ['"Orbitron"', '"Rajdhani"', 'sans-serif'], },