feat: auto LLM feedback runner + problem link + 5xx retry

- Add SubmissionFeedbackRunner: async background queue for auto LLM feedback
- Enqueue feedback generation after each submission in submitProblem()
- Register runner in main.cc with CSP_FEEDBACK_AUTO_RUN env var
- Add problem_title to GET /api/v1/submissions/{id} response
- Frontend: clickable problem link on submission detail page
- Enhance LLM prompt with richer analysis dimensions
- Add 5xx/connection error retry (max 5 attempts) in Python LLM script

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
这个提交包含在:
cryptocommuniums-afk
2026-02-16 15:13:35 +08:00
父节点 bc2e085c70
当前提交 7860414ae5
修改 37 个文件,包含 312 行新增5343 行删除

查看文件

@@ -28,6 +28,7 @@ add_library(csp_core
src/services/kb_import_runner.cc src/services/kb_import_runner.cc
src/services/problem_gen_runner.cc src/services/problem_gen_runner.cc
src/services/submission_feedback_service.cc src/services/submission_feedback_service.cc
src/services/submission_feedback_runner.cc
src/services/import_service.cc src/services/import_service.cc
src/services/import_runner.cc src/services/import_runner.cc
src/domain/enum_strings.cc src/domain/enum_strings.cc

查看文件

@@ -0,0 +1,41 @@
#pragma once
#include "csp/db/sqlite_db.h"
#include <cstddef>
#include <cstdint>
#include <deque>
#include <mutex>
#include <string>
namespace csp::services {
class SubmissionFeedbackRunner {
public:
static SubmissionFeedbackRunner& Instance();
void Configure(std::string db_path);
/// Enqueue a submission for async feedback generation.
bool Enqueue(int64_t submission_id);
/// Scan DB for submissions without feedback and enqueue them.
void AutoStartIfEnabled(db::SqliteDb& db);
size_t PendingCount() const;
private:
SubmissionFeedbackRunner() = default;
void StartWorkerIfNeededLocked();
void WorkerLoop();
void RecoverPendingLocked();
std::string db_path_;
mutable std::mutex mu_;
std::deque<int64_t> queue_;
size_t pending_jobs_ = 0;
bool worker_running_ = false;
bool recovered_ = false;
};
} // namespace csp::services

查看文件

@@ -6,6 +6,7 @@
#include "csp/services/contest_service.h" #include "csp/services/contest_service.h"
#include "csp/services/problem_service.h" #include "csp/services/problem_service.h"
#include "csp/services/solution_access_service.h" #include "csp/services/solution_access_service.h"
#include "csp/services/submission_feedback_runner.h"
#include "csp/services/submission_feedback_service.h" #include "csp/services/submission_feedback_service.h"
#include "csp/services/submission_service.h" #include "csp/services/submission_service.h"
#include "http_auth.h" #include "http_auth.h"
@@ -130,6 +131,10 @@ void SubmissionController::submitProblem(
services::SubmissionService svc(csp::AppState::Instance().db()); services::SubmissionService svc(csp::AppState::Instance().db());
auto s = svc.CreateAndJudge(create); auto s = svc.CreateAndJudge(create);
// Auto-enqueue LLM feedback generation in background.
services::SubmissionFeedbackRunner::Instance().Enqueue(s.id);
cb(JsonOk(domain::ToJson(s))); cb(JsonOk(domain::ToJson(s)));
} catch (const std::invalid_argument&) { } catch (const std::invalid_argument&) {
cb(JsonError(drogon::k400BadRequest, "invalid numeric field")); cb(JsonError(drogon::k400BadRequest, "invalid numeric field"));
@@ -187,6 +192,14 @@ void SubmissionController::getSubmission(
Json::Value payload = domain::ToJson(*s); Json::Value payload = domain::ToJson(*s);
payload["code"] = s->code; payload["code"] = s->code;
// Attach problem title for frontend linking.
{
services::ProblemService psvc(csp::AppState::Instance().db());
if (const auto p = psvc.GetById(s->problem_id); p.has_value()) {
payload["problem_title"] = p->title;
}
}
services::SolutionAccessService access_svc(csp::AppState::Instance().db()); services::SolutionAccessService access_svc(csp::AppState::Instance().db());
const auto stats = const auto stats =
access_svc.QueryUserProblemViewStats(s->user_id, s->problem_id); access_svc.QueryUserProblemViewStats(s->user_id, s->problem_id);

查看文件

@@ -6,6 +6,7 @@
#include "csp/services/kb_import_runner.h" #include "csp/services/kb_import_runner.h"
#include "csp/services/problem_gen_runner.h" #include "csp/services/problem_gen_runner.h"
#include "csp/services/problem_solution_runner.h" #include "csp/services/problem_solution_runner.h"
#include "csp/services/submission_feedback_runner.h"
#include <cstdlib> #include <cstdlib>
#include <filesystem> #include <filesystem>
@@ -20,6 +21,7 @@ int main(int argc, char** argv) {
csp::services::KbImportRunner::Instance().Configure(db_path); csp::services::KbImportRunner::Instance().Configure(db_path);
csp::services::ProblemSolutionRunner::Instance().Configure(db_path); csp::services::ProblemSolutionRunner::Instance().Configure(db_path);
csp::services::ProblemGenRunner::Instance().Configure(db_path); csp::services::ProblemGenRunner::Instance().Configure(db_path);
csp::services::SubmissionFeedbackRunner::Instance().Configure(db_path);
// Optional seed admin user for dev/test. // Optional seed admin user for dev/test.
{ {
@@ -48,6 +50,9 @@ int main(int argc, char** argv) {
csp::AppState::Instance().db()); csp::AppState::Instance().db());
// Auto-generate one CSP-J new problem (RAG + dedupe) on startup by default. // Auto-generate one CSP-J new problem (RAG + dedupe) on startup by default.
csp::services::ProblemGenRunner::Instance().AutoStartIfEnabled(); csp::services::ProblemGenRunner::Instance().AutoStartIfEnabled();
// Auto-queue submission feedback generation for submissions without feedback.
csp::services::SubmissionFeedbackRunner::Instance().AutoStartIfEnabled(
csp::AppState::Instance().db());
// CORS (dev-friendly). In production, prefer reverse proxy same-origin. // CORS (dev-friendly). In production, prefer reverse proxy same-origin.
drogon::app().registerPreRoutingAdvice([](const drogon::HttpRequestPtr& req, drogon::app().registerPreRoutingAdvice([](const drogon::HttpRequestPtr& req,

查看文件

@@ -0,0 +1,180 @@
#include "csp/services/submission_feedback_runner.h"
#include "csp/services/problem_service.h"
#include "csp/services/submission_feedback_service.h"
#include "csp/services/submission_service.h"
#include <drogon/drogon.h>
#include <sqlite3.h>
#include <algorithm>
#include <cctype>
#include <chrono>
#include <cstdlib>
#include <thread>
#include <vector>
namespace csp::services {
namespace {
bool EnvBool(const char* key, bool default_value) {
const char* raw = std::getenv(key);
if (!raw) return default_value;
std::string val(raw);
for (auto& c : val)
c = static_cast<char>(::tolower(static_cast<unsigned char>(c)));
if (val == "1" || val == "true" || val == "yes" || val == "on") return true;
if (val == "0" || val == "false" || val == "no" || val == "off") return false;
return default_value;
}
int EnvInt(const char* key, int default_value) {
const char* raw = std::getenv(key);
if (!raw) return default_value;
try {
return std::stoi(raw);
} catch (...) {
return default_value;
}
}
} // namespace
SubmissionFeedbackRunner& SubmissionFeedbackRunner::Instance() {
static SubmissionFeedbackRunner inst;
return inst;
}
void SubmissionFeedbackRunner::Configure(std::string db_path) {
std::lock_guard<std::mutex> lock(mu_);
db_path_ = std::move(db_path);
RecoverPendingLocked();
StartWorkerIfNeededLocked();
}
bool SubmissionFeedbackRunner::Enqueue(int64_t submission_id) {
std::lock_guard<std::mutex> lock(mu_);
if (db_path_.empty()) return false;
queue_.push_back(submission_id);
++pending_jobs_;
StartWorkerIfNeededLocked();
return true;
}
void SubmissionFeedbackRunner::AutoStartIfEnabled(db::SqliteDb& db) {
if (!EnvBool("CSP_FEEDBACK_AUTO_RUN", false)) {
LOG_INFO << "submission feedback auto-run disabled";
return;
}
const int limit =
std::max(1, std::min(10000, EnvInt("CSP_FEEDBACK_AUTO_LIMIT", 500)));
// Find submissions without feedback.
sqlite3_stmt* stmt = nullptr;
const char* sql =
"SELECT s.id FROM submissions s "
"LEFT JOIN submission_feedback f ON f.submission_id=s.id "
"WHERE f.id IS NULL "
"ORDER BY s.id DESC LIMIT ?";
if (sqlite3_prepare_v2(db.raw(), sql, -1, &stmt, nullptr) != SQLITE_OK) {
LOG_ERROR << "feedback runner: failed to query pending submissions";
return;
}
sqlite3_bind_int(stmt, 1, limit);
int queued = 0;
while (sqlite3_step(stmt) == SQLITE_ROW) {
const int64_t sid = sqlite3_column_int64(stmt, 0);
Enqueue(sid);
++queued;
}
sqlite3_finalize(stmt);
LOG_INFO << "submission feedback auto-run queued=" << queued
<< ", pending=" << PendingCount();
}
size_t SubmissionFeedbackRunner::PendingCount() const {
std::lock_guard<std::mutex> lock(mu_);
return pending_jobs_;
}
void SubmissionFeedbackRunner::StartWorkerIfNeededLocked() {
if (worker_running_ || queue_.empty()) return;
worker_running_ = true;
std::thread([this]() { WorkerLoop(); }).detach();
}
void SubmissionFeedbackRunner::WorkerLoop() {
while (true) {
int64_t submission_id = 0;
std::string db_path;
{
std::lock_guard<std::mutex> lock(mu_);
if (queue_.empty()) {
worker_running_ = false;
return;
}
submission_id = queue_.front();
queue_.pop_front();
db_path = db_path_;
}
try {
db::SqliteDb db = db::SqliteDb::OpenFile(db_path);
SubmissionFeedbackService feedback_svc(db);
// Skip if feedback already exists.
if (feedback_svc.GetBySubmissionId(submission_id).has_value()) {
std::lock_guard<std::mutex> lock(mu_);
if (pending_jobs_ > 0) --pending_jobs_;
continue;
}
SubmissionService sub_svc(db);
const auto submission = sub_svc.GetById(submission_id);
if (!submission.has_value()) {
LOG_WARN << "feedback runner: submission " << submission_id
<< " not found, skipping";
std::lock_guard<std::mutex> lock(mu_);
if (pending_jobs_ > 0) --pending_jobs_;
continue;
}
ProblemService prob_svc(db);
const auto problem = prob_svc.GetById(submission->problem_id);
if (!problem.has_value()) {
LOG_WARN << "feedback runner: problem " << submission->problem_id
<< " not found for submission " << submission_id;
std::lock_guard<std::mutex> lock(mu_);
if (pending_jobs_ > 0) --pending_jobs_;
continue;
}
feedback_svc.GenerateAndSave(*submission, *problem, false);
LOG_INFO << "feedback runner: generated feedback for submission "
<< submission_id;
} catch (const std::exception& e) {
LOG_ERROR << "feedback runner: submission " << submission_id
<< " failed: " << e.what();
}
{
std::lock_guard<std::mutex> lock(mu_);
if (pending_jobs_ > 0) --pending_jobs_;
}
// Small delay between jobs to avoid overwhelming LLM API / SQLite.
std::this_thread::sleep_for(std::chrono::seconds(2));
}
}
void SubmissionFeedbackRunner::RecoverPendingLocked() {
if (recovered_ || db_path_.empty()) return;
recovered_ = true;
// Recovery is handled by AutoStartIfEnabled; no separate DB state to recover.
}
} // namespace csp::services

查看文件

@@ -22,6 +22,9 @@ services:
- CSP_SOLUTION_AUTO_RUN_MISSING=true - CSP_SOLUTION_AUTO_RUN_MISSING=true
- CSP_SOLUTION_AUTO_LIMIT=50000 - CSP_SOLUTION_AUTO_LIMIT=50000
- CSP_SOLUTION_AUTO_MAX_SOLUTIONS=3 - CSP_SOLUTION_AUTO_MAX_SOLUTIONS=3
- CSP_FEEDBACK_AUTO_RUN=true
- CSP_FEEDBACK_AUTO_LIMIT=500
- CSP_FEEDBACK_SCRIPT_PATH=/app/scripts/analyze_submission_feedback.py
build: build:
context: . context: .
dockerfile: Dockerfile.backend dockerfile: Dockerfile.backend

查看文件

@@ -1,5 +1,6 @@
"use client"; "use client";
import Link from "next/link";
import { useParams } from "next/navigation"; import { useParams } from "next/navigation";
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
@@ -21,6 +22,7 @@ type Submission = {
id: number; id: number;
user_id: number; user_id: number;
problem_id: number; problem_id: number;
problem_title?: string;
contest_id: number | null; contest_id: number | null;
language: string; language: string;
code: string; code: string;
@@ -117,7 +119,17 @@ export default function SubmissionDetailPage() {
<section className="rounded-xl border bg-white p-4 text-sm"> <section className="rounded-xl border bg-white p-4 text-sm">
<div className="grid gap-1 sm:grid-cols-2"> <div className="grid gap-1 sm:grid-cols-2">
<p>{tx("用户", "User")}: {data.user_id}</p> <p>{tx("用户", "User")}: {data.user_id}</p>
<p>{tx("题目", "Problem")}: {data.problem_id}</p> <p>
{tx("题目", "Problem")}:{" "}
<Link
href={`/problems/${data.problem_id}`}
className="text-blue-400 hover:text-blue-300 underline"
>
{data.problem_title
? `P${data.problem_id} - ${data.problem_title}`
: `P${data.problem_id}`}
</Link>
</p>
<p>{tx("比赛", "Contest")}: {data.contest_id ?? "-"}</p> <p>{tx("比赛", "Contest")}: {data.contest_id ?? "-"}</p>
<p>{tx("语言", "Language")}: {data.language}</p> <p>{tx("语言", "Language")}: {data.language}</p>
<p>{tx("状态", "Status")}: {data.status}</p> <p>{tx("状态", "Status")}: {data.status}</p>

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 19 MiB

之后

宽度:  |  高度:  |  大小: 0 B

查看文件

@@ -1,804 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=1920, initial-scale=1.0">
<title>CSP Learning Platform - Minecraft UI Kit Design Delivery</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/remixicon/4.6.0/remixicon.min.css" rel="stylesheet">
<style>
@font-face {
font-family: 'DelaGothicOne';
src: url('https://assets-persist.lovart.ai/agent-static-assets/DelaGothicOne-Regular.ttf');
}
@font-face {
font-family: 'MiSans';
src: url('https://assets-persist.lovart.ai/agent-static-assets/MiSans-Regular.ttf');
}
@font-face {
font-family: 'MiSansBold';
src: url('https://assets-persist.lovart.ai/agent-static-assets/MiSans-Bold.ttf');
}
:root {
--mc-grass-top: #5cb85c;
--mc-grass-side: #4cae4c;
--mc-dirt: #795548;
--mc-stone: #9e9e9e;
--mc-stone-dark: #616161;
--mc-obsidian: #212121;
--mc-wood: #8d6e63;
--mc-wood-dark: #5d4037;
--mc-gold: #ffd700;
--mc-diamond: #40e0d0;
--mc-redstone: #f44336;
--bg-dark: #1a1a1a;
--text-light: #f5f5f5;
--text-gray: #bdbdbd;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
width: 1920px;
background-color: var(--bg-dark);
background-image:
linear-gradient(rgba(26, 26, 26, 0.9), rgba(26, 26, 26, 0.9)),
url('https://a.lovart.ai/artifacts/agent/W1iXxVdg3xIm5fP9.png'); /* Using texture as bg pattern */
background-size: cover;
color: var(--text-light);
font-family: 'MiSans', sans-serif;
overflow-x: hidden;
display: flex;
}
/* Scrollbar */
::-webkit-scrollbar {
width: 12px;
background: var(--mc-obsidian);
}
::-webkit-scrollbar-thumb {
background: var(--mc-stone);
border: 2px solid var(--mc-obsidian);
}
/* Typography */
h1, h2, h3, h4 {
font-family: 'DelaGothicOne', sans-serif;
text-transform: uppercase;
letter-spacing: 1px;
}
p {
line-height: 1.6;
color: var(--text-gray);
font-size: 16px;
}
/* Sidebar Navigation */
.sidebar {
width: 320px;
height: 100vh;
background-color: rgba(33, 33, 33, 0.95);
border-right: 4px solid var(--mc-stone-dark);
position: fixed;
left: 0;
top: 0;
padding: 40px 0;
overflow-y: auto;
z-index: 100;
display: flex;
flex-direction: column;
}
.brand {
padding: 0 30px 40px;
border-bottom: 2px dashed var(--mc-stone-dark);
margin-bottom: 30px;
text-align: center;
}
.brand h1 {
font-size: 24px;
color: var(--mc-grass-top);
text-shadow: 2px 2px 0 #000;
margin-bottom: 10px;
}
.brand span {
font-size: 12px;
background: var(--mc-gold);
color: #000;
padding: 4px 8px;
border: 2px solid #000;
font-weight: bold;
box-shadow: 2px 2px 0 rgba(0,0,0,0.5);
}
.nav-menu {
list-style: none;
padding: 0 20px;
}
.nav-item {
margin-bottom: 15px;
}
.nav-link {
display: flex;
align-items: center;
text-decoration: none;
color: var(--text-light);
padding: 12px 20px;
border: 2px solid transparent;
transition: all 0.2s;
font-family: 'DelaGothicOne', sans-serif;
font-size: 14px;
}
.nav-link i {
margin-right: 15px;
font-size: 18px;
color: var(--mc-stone);
}
.nav-link:hover, .nav-link.active {
background-color: var(--mc-stone-dark);
border: 2px solid var(--text-light);
box-shadow: 4px 4px 0 #000;
transform: translate(-2px, -2px);
}
.nav-link:hover i, .nav-link.active i {
color: var(--mc-grass-top);
}
/* Main Content */
.main-content {
margin-left: 320px;
width: 1600px;
padding: 60px 80px;
}
/* Common Components */
.section-block {
margin-bottom: 100px;
scroll-margin-top: 40px;
}
.section-header {
display: flex;
align-items: center;
margin-bottom: 40px;
padding-bottom: 20px;
border-bottom: 4px solid var(--mc-stone-dark);
position: relative;
}
.section-header::after {
content: '';
position: absolute;
bottom: -4px;
left: 0;
width: 100px;
height: 4px;
background: var(--mc-grass-top);
}
.section-title {
font-size: 36px;
color: var(--text-light);
margin-right: 20px;
text-shadow: 3px 3px 0 #000;
}
.section-subtitle {
font-family: 'MiSans', sans-serif;
color: var(--mc-stone);
font-size: 18px;
margin-top: 10px;
}
.card {
background: #2d2d2d;
border: 4px solid #000;
box-shadow: 8px 8px 0 rgba(0,0,0,0.5);
padding: 20px;
margin-bottom: 30px;
position: relative;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 2px dashed var(--mc-stone-dark);
}
.card-title {
font-size: 20px;
color: var(--mc-gold);
}
.pixel-img {
width: 100%;
height: auto;
image-rendering: pixelated; /* Crucial for Minecraft style */
border: 2px solid #000;
display: block;
}
.grid-2 {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
}
.grid-3 {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 30px;
}
/* Cover Section */
.hero {
background: linear-gradient(rgba(0,0,0,0.7), rgba(0,0,0,0.7)), url('https://a.lovart.ai/artifacts/agent/BKErUBAJ8x52GYyY.png') center/cover;
height: 400px;
display: flex;
align-items: center;
justify-content: center;
border: 4px solid #fff;
box-shadow: 12px 12px 0 #000;
margin-bottom: 80px;
text-align: center;
position: relative;
image-rendering: pixelated;
}
.hero-content h1 {
font-size: 64px;
color: #fff;
text-shadow: 4px 4px 0 #000, -2px -2px 0 var(--mc-grass-side);
margin-bottom: 20px;
}
.hero-content p {
font-size: 24px;
color: var(--mc-gold);
font-family: 'DelaGothicOne', sans-serif;
text-shadow: 2px 2px 0 #000;
}
.meta-tag {
background: rgba(0,0,0,0.6);
padding: 10px 20px;
border: 2px solid var(--mc-stone);
display: inline-block;
margin-top: 30px;
font-family: 'MiSans', monospace;
}
/* Code Block */
.code-block {
background: #1e1e1e;
padding: 20px;
border: 2px solid var(--mc-stone-dark);
font-family: 'Consolas', monospace;
color: #a9b7c6;
overflow-x: auto;
margin-top: 20px;
}
.code-keyword { color: #cc7832; }
.code-string { color: #6a8759; }
.code-attr { color: #9876aa; }
.code-comment { color: #808080; }
/* Color Swatches */
.color-grid {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 20px;
}
.swatch {
border: 4px solid #000;
padding: 10px;
background: #333;
text-align: center;
}
.swatch-color {
height: 100px;
border: 2px solid rgba(255,255,255,0.2);
margin-bottom: 10px;
box-shadow: inset 2px 2px 0 rgba(255,255,255,0.2), inset -2px -2px 0 rgba(0,0,0,0.2);
}
.swatch-info {
font-family: 'MiSans', monospace;
font-size: 14px;
}
.swatch-name {
font-weight: bold;
display: block;
margin-bottom: 4px;
color: #fff;
}
/* Download Section */
.download-list {
background: #2d2d2d;
border: 4px solid #000;
padding: 30px;
}
.download-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20px;
border-bottom: 2px solid var(--mc-stone-dark);
}
.download-item:last-child {
border-bottom: none;
}
.file-info {
display: flex;
align-items: center;
}
.file-icon {
font-size: 32px;
margin-right: 20px;
color: var(--mc-stone);
}
.file-name {
font-family: 'MiSansBold';
font-size: 18px;
color: #fff;
}
.file-meta {
font-size: 14px;
color: var(--text-gray);
}
.btn-download {
background: var(--mc-grass-top);
color: #fff;
padding: 12px 30px;
text-decoration: none;
font-family: 'DelaGothicOne', sans-serif;
border: 4px solid #000;
border-bottom-width: 8px; /* 3D effect */
transition: all 0.1s;
text-transform: uppercase;
}
.btn-download:hover {
transform: translateY(2px);
border-bottom-width: 6px;
background: var(--mc-grass-side);
}
.btn-download:active {
transform: translateY(6px);
border-bottom-width: 2px;
}
/* Footer */
.footer {
margin-top: 100px;
padding-top: 40px;
border-top: 4px solid var(--mc-stone-dark);
text-align: center;
color: var(--mc-stone);
font-family: 'MiSans', sans-serif;
padding-bottom: 40px;
}
/* Custom Badges */
.status-badge {
display: inline-block;
padding: 4px 10px;
font-size: 12px;
font-weight: bold;
font-family: 'MiSansBold';
color: #000;
border: 2px solid #000;
}
.status-new { background: var(--mc-diamond); }
.status-update { background: var(--mc-gold); }
/* Animation */
@keyframes float {
0% { transform: translateY(0px); }
50% { transform: translateY(-10px); }
100% { transform: translateY(0px); }
}
.floating {
animation: float 4s ease-in-out infinite;
}
</style>
</head>
<body>
<!-- Sidebar Navigation -->
<nav class="sidebar">
<div class="brand">
<h1>CSP LEARN</h1>
<span>MINECRAFT EDITION</span>
</div>
<ul class="nav-menu">
<li class="nav-item"><a href="#overview" class="nav-link active"><i class="ri-compass-3-fill"></i> Design Overview</a></li>
<li class="nav-item"><a href="#ui-kit" class="nav-link"><i class="ri-layout-grid-fill"></i> UI Kit Library</a></li>
<li class="nav-item"><a href="#pages" class="nav-link"><i class="ri-pages-fill"></i> Page Designs</a></li>
<li class="nav-item"><a href="#responsive" class="nav-link"><i class="ri-smartphone-fill"></i> Responsive</a></li>
<li class="nav-item"><a href="#development" class="nav-link"><i class="ri-code-s-slash-fill"></i> Dev Guidelines</a></li>
<li class="nav-item"><a href="#assets" class="nav-link"><i class="ri-image-2-fill"></i> Image Assets</a></li>
<li class="nav-item"><a href="#downloads" class="nav-link"><i class="ri-download-cloud-2-fill"></i> Downloads</a></li>
<li class="nav-item"><a href="#appendix" class="nav-link"><i class="ri-palette-fill"></i> Appendix</a></li>
</ul>
<div style="margin-top: auto; padding: 20px; text-align: center; color: #666; font-size: 12px;">
v1.0.2 Build 20231024
</div>
</nav>
<!-- Main Content -->
<main class="main-content">
<!-- Hero Section -->
<section class="hero" id="overview">
<div class="hero-content">
<h1>CSP Learning Platform</h1>
<p>Minecraft Pixel Art Style UI Kit</p>
<div class="meta-tag">
DESIGN DELIVERY DOCUMENT • V1.0 • OCTOBER 2023
</div>
</div>
</section>
<!-- Design Overview -->
<section class="section-block">
<div class="section-header">
<h2 class="section-title">Design Concept</h2>
<span class="status-badge status-new">NEW</span>
</div>
<div class="card">
<div class="grid-2">
<div>
<h3 class="card-title" style="margin-bottom: 20px;">The Voxel World Metaphor</h3>
<p style="margin-bottom: 20px;">The CSP Learning Platform's design is deeply rooted in the "Minecraft" aesthetic, utilizing a voxel/pixel art style to create an immersive and engaging learning environment for programming students. This choice is strategic:</p>
<ul style="list-style-position: inside; color: var(--text-gray); line-height: 2;">
<li><strong>Gamification:</strong> Leveraging the familiarity of sandbox games to make coding challenges feel like quests.</li>
<li><strong>Modularity:</strong> The block-based nature of Minecraft mirrors the modular nature of code and algorithms.</li>
<li><strong>Creativity:</strong> Encouraging students to "build" their knowledge just as they build structures in-game.</li>
</ul>
</div>
<div>
<h3 class="card-title" style="margin-bottom: 20px;">Visual Language</h3>
<p>Our visual language relies on distinct 8-bit textures, bold black borders, and high-contrast colors. The interface uses skeuomorphic elements (wooden signs, stone buttons) combined with flat pixel art to ensure usability while maintaining the theme.</p>
<br>
<div style="background: #000; padding: 15px; border: 2px solid var(--mc-stone);">
<p style="color: var(--mc-grass-top); font-family: 'Consolas', monospace;">> System Status: ONLINE</p>
<p style="color: var(--mc-gold); font-family: 'Consolas', monospace;">> Style Loaded: PIXEL_ART_V2</p>
<p style="color: var(--mc-diamond); font-family: 'Consolas', monospace;">> Assets: READY</p>
</div>
</div>
</div>
</div>
</section>
<!-- UI Kit Components -->
<section id="ui-kit" class="section-block">
<div class="section-header">
<h2 class="section-title">UI Kit Components</h2>
</div>
<div class="card">
<div class="card-header">
<span class="card-title">Basic Components System</span>
<i class="ri-hammer-fill" style="color: var(--mc-stone);"></i>
</div>
<img src="https://a.lovart.ai/artifacts/agent/JBNC9ZCu38GlvB5k.png" alt="Basic UI Components" class="pixel-img">
<p style="margin-top: 15px; font-size: 14px;">Comprehensive system including color palette, typography hierarchy, input fields, and standard buttons with 4 states (Normal, Hover, Pressed, Disabled).</p>
</div>
<div class="grid-2">
<div class="card">
<div class="card-header">
<span class="card-title">Button States & Feedback</span>
</div>
<img src="https://a.lovart.ai/artifacts/agent/iGOQyJelwI6y5cUG.png" alt="Button States" class="pixel-img">
</div>
<div class="card">
<div class="card-header">
<span class="card-title">Gamification & Progress</span>
</div>
<img src="https://a.lovart.ai/artifacts/agent/PN1u4ENvUmKjZLbI.png" alt="Gamification Components" class="pixel-img">
</div>
</div>
<div class="card">
<div class="card-header">
<span class="card-title">Level System & XP Visualization</span>
<span class="status-badge status-update">ANIMATED</span>
</div>
<img src="https://a.lovart.ai/artifacts/agent/nJ3gAEmRoJyLLcY8.png" alt="Level System" class="pixel-img">
</div>
</section>
<!-- Page Designs -->
<section id="pages" class="section-block">
<div class="section-header">
<h2 class="section-title">Page Designs</h2>
</div>
<!-- Login -->
<div class="card">
<div class="card-header">
<span class="card-title">01. Login & Authentication</span>
</div>
<img src="https://a.lovart.ai/artifacts/agent/BKErUBAJ8x52GYyY.png" alt="Login Page" class="pixel-img">
</div>
<!-- Problem Library & Editor -->
<div class="grid-2">
<div class="card">
<div class="card-header">
<span class="card-title">02. Problem Library</span>
</div>
<img src="https://a.lovart.ai/artifacts/agent/mA9QlaxiEfFrYUdK.png" alt="Problem Library Page" class="pixel-img">
</div>
<div class="card">
<div class="card-header">
<span class="card-title">03. Code Editor IDE</span>
</div>
<img src="https://a.lovart.ai/artifacts/agent/IcCJDu9xkFALNuUS.png" alt="Code Editor Page" class="pixel-img">
</div>
</div>
<!-- Profile & Contest -->
<div class="grid-2">
<div class="card">
<div class="card-header">
<span class="card-title">04. User Profile Center</span>
</div>
<img src="https://a.lovart.ai/artifacts/agent/bkXI4WiboPJwwppy.png" alt="Profile Page" class="pixel-img">
</div>
<div class="card">
<div class="card-header">
<span class="card-title">05. Contest Arena</span>
</div>
<img src="https://a.lovart.ai/artifacts/agent/PtblvMgRI6EWZykt.png" alt="Contest Page" class="pixel-img">
</div>
</div>
</section>
<!-- Responsive Design -->
<section id="responsive" class="section-block">
<div class="section-header">
<h2 class="section-title">Responsive Behavior</h2>
</div>
<div class="card">
<img src="https://a.lovart.ai/artifacts/agent/nFDbXNrV4vIb0TAf.png" alt="Responsive Design Showcase" class="pixel-img">
<div style="margin-top: 20px; display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; text-align: center;">
<div><i class="ri-macbook-line"></i><br>Desktop (>1200px)</div>
<div><i class="ri-tablet-line"></i><br>Tablet Landscape (1024px)</div>
<div><i class="ri-tablet-line" style="transform: rotate(90deg);"></i><br>Tablet Portrait (768px)</div>
<div><i class="ri-smartphone-line"></i><br>Mobile (< 480px)</div>
</div>
</div>
</section>
<!-- Development Guidelines -->
<section id="development" class="section-block">
<div class="section-header">
<h2 class="section-title">Development Guidelines</h2>
</div>
<div class="card">
<img src="https://a.lovart.ai/artifacts/agent/pwGnBU44IlBbAked.png" alt="CSS Naming" class="pixel-img">
<h3 style="margin: 30px 0 15px; color: var(--mc-gold);">Core CSS Implementation (SCSS)</h3>
<div class="code-block">
<pre><code><span class="code-comment">// Variables & Mixins</span>
$mc-grass-top: #5cb85c;
$mc-dirt: #795548;
$pixel-scale: 4px;
<span class="code-keyword">@mixin</span> pixel-border($color: #000) {
box-shadow:
-4px 0 0 0 $color,
4px 0 0 0 $color,
0 -4px 0 0 $color,
0 4px 0 0 $color;
margin: 4px;
}
<span class="code-keyword">.mc-btn</span> {
<span class="code-attr">background-color</span>: $mc-stone;
<span class="code-attr">border</span>: none;
<span class="code-attr">color</span>: #fff;
<span class="code-attr">padding</span>: 12px 24px;
<span class="code-attr">font-family</span>: 'Minecraft', monospace;
<span class="code-keyword">@include</span> pixel-border(#212121);
<span class="code-keyword">&:hover</span> {
<span class="code-attr">background-color</span>: lighten($mc-stone, 10%);
<span class="code-attr">transform</span>: translateY(-2px);
}
}</code></pre>
</div>
</div>
</section>
<!-- Image Resources -->
<section id="assets" class="section-block">
<div class="section-header">
<h2 class="section-title">Asset Library</h2>
</div>
<div class="grid-2">
<div class="card">
<div class="card-header"><span class="card-title">Navigation Icons (32px)</span></div>
<img src="https://a.lovart.ai/artifacts/agent/QcxFQeeckVCAJQqf.png" alt="Navigation Icons" class="pixel-img">
</div>
<div class="card">
<div class="card-header"><span class="card-title">Difficulty & Prog. Icons</span></div>
<img src="https://a.lovart.ai/artifacts/agent/hJcFuIpo4xGOKOaI.png" alt="Difficulty Icons" class="pixel-img">
</div>
</div>
<div class="grid-2">
<div class="card">
<div class="card-header"><span class="card-title">Tiled Textures (256px)</span></div>
<img src="https://a.lovart.ai/artifacts/agent/W1iXxVdg3xIm5fP9.png" alt="Textures" class="pixel-img">
</div>
<div class="card">
<div class="card-header"><span class="card-title">Achievement Badges</span></div>
<img src="https://a.lovart.ai/artifacts/agent/vwdnD6uAy43JFude.png" alt="Badges" class="pixel-img">
</div>
</div>
<div class="card">
<div class="card-header"><span class="card-title">Blocks, Particles & Effects</span></div>
<img src="https://a.lovart.ai/artifacts/agent/SucsD8o76CGC5BbY.png" alt="Blocks and Particles" class="pixel-img">
</div>
</section>
<!-- Download Resources -->
<section id="downloads" class="section-block">
<div class="section-header">
<h2 class="section-title">Download Resources</h2>
</div>
<div class="download-list">
<div class="download-item">
<div class="file-info">
<i class="ri-file-zip-line file-icon"></i>
<div>
<div class="file-name">Complete UI Kit - Figma Source</div>
<div class="file-meta">.fig | 145 MB | Updated Oct 24, 2023</div>
</div>
</div>
<a href="#" class="btn-download">Download</a>
</div>
<div class="download-item">
<div class="file-info">
<i class="ri-file-code-line file-icon"></i>
<div>
<div class="file-name">Icon Set (SVG + PNG)</div>
<div class="file-meta">.zip | 24 MB | 120 Icons</div>
</div>
</div>
<a href="#" class="btn-download">Download</a>
</div>
<div class="download-item">
<div class="file-info">
<i class="ri-image-line file-icon"></i>
<div>
<div class="file-name">Texture Pack High-Res</div>
<div class="file-meta">.zip | 312 MB | Seamless Patterns</div>
</div>
</div>
<a href="#" class="btn-download">Download</a>
</div>
<div class="download-item">
<div class="file-info">
<i class="ri-font-size file-icon"></i>
<div>
<div class="file-name">Pixel Fonts Bundle</div>
<div class="file-meta">.ttf | 2 MB | 4 Font Families</div>
</div>
</div>
<a href="#" class="btn-download">Download</a>
</div>
</div>
</section>
<!-- Appendix -->
<section id="appendix" class="section-block">
<div class="section-header">
<h2 class="section-title">Appendix: Color Tokens</h2>
</div>
<div class="card">
<div class="color-grid">
<div class="swatch">
<div class="swatch-color" style="background: #5cb85c;"></div>
<div class="swatch-info">
<span class="swatch-name">Grass Top</span>
#5CB85C
</div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: #795548;"></div>
<div class="swatch-info">
<span class="swatch-name">Dirt</span>
#795548
</div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: #9e9e9e;"></div>
<div class="swatch-info">
<span class="swatch-name">Stone</span>
#9E9E9E
</div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: #8d6e63;"></div>
<div class="swatch-info">
<span class="swatch-name">Wood</span>
#8D6E63
</div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: #ffd700;"></div>
<div class="swatch-info">
<span class="swatch-name">Gold</span>
#FFD700
</div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: #40e0d0;"></div>
<div class="swatch-info">
<span class="swatch-name">Diamond</span>
#40E0D0
</div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: #f44336;"></div>
<div class="swatch-info">
<span class="swatch-name">Redstone</span>
#F44336
</div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: #212121;"></div>
<div class="swatch-info">
<span class="swatch-name">Obsidian</span>
#212121
</div>
</div>
</div>
</div>
</section>
<footer class="footer">
<p>CSP Learning Platform Design System © 2023</p>
<p>Designed with ❤️ for future coders.</p>
</footer>
</main>
</body>
</html>

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 9.5 MiB

之后

宽度:  |  高度:  |  大小: 0 B

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 5.5 MiB

之后

宽度:  |  高度:  |  大小: 0 B

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 5.6 MiB

之后

宽度:  |  高度:  |  大小: 0 B

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 5.2 MiB

之后

宽度:  |  高度:  |  大小: 0 B

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 6.0 MiB

之后

宽度:  |  高度:  |  大小: 0 B

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 5.7 MiB

之后

宽度:  |  高度:  |  大小: 0 B

查看文件

@@ -1,651 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=1920, initial-scale=1.0">
<title>CSP Learning Platform - Minecraft Edition</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/remixicon/4.6.0/remixicon.min.css" rel="stylesheet">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&family=VT323&display=swap" rel="stylesheet">
<style>
:root {
--grass-top: #7CB342;
--dirt-side: #795548;
--wood-frame: #5D4037;
--wood-bg: #A1887F;
--plank-light: #D7CCC8;
--gold-btn: #FFB300;
--gold-shadow: #BFA002;
--btn-shadow: #33691E;
--text-main: #3E2723;
--diamond-blue: #00B0D6;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
width: 1920px;
margin: 0 auto;
background-color: #1a1a1a;
font-family: 'VT323', monospace;
color: white;
overflow-x: hidden;
background-image:
linear-gradient(45deg, #1f1f1f 25%, transparent 25%),
linear-gradient(-45deg, #1f1f1f 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, #1f1f1f 75%),
linear-gradient(-45deg, transparent 75%, #1f1f1f 75%);
background-size: 40px 40px;
background-position: 0 0, 0 20px, 20px -20px, -20px 0px;
}
/* Utility Classes */
.pixel-text {
font-family: 'Press Start 2P', cursive;
line-height: 1.5;
}
.container {
width: 1400px;
margin: 0 auto;
position: relative;
z-index: 2;
}
/* Hero Section */
.hero {
height: 400px;
background-color: var(--grass-top);
position: relative;
display: flex;
align-items: center;
justify-content: center;
border-bottom: 20px solid var(--dirt-side);
box-shadow: inset 0 -10px 0 rgba(0,0,0,0.2);
overflow: hidden;
}
/* Minecraft Grass Pattern via CSS */
.hero::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image:
linear-gradient(rgba(255,255,255,0.1) 2px, transparent 2px),
linear-gradient(90deg, rgba(255,255,255,0.1) 2px, transparent 2px);
background-size: 32px 32px;
opacity: 0.3;
pointer-events: none;
}
.hero-content {
text-align: center;
text-shadow: 4px 4px 0px rgba(0,0,0,0.5);
z-index: 3;
}
.hero-title {
font-size: 48px;
margin-bottom: 16px;
color: white;
letter-spacing: 2px;
text-transform: uppercase;
}
.hero-subtitle {
font-size: 24px;
color: #FFEB3B;
margin-bottom: 24px;
}
.hero-desc {
font-size: 28px; /* VT323 is smaller visually */
margin-bottom: 30px;
color: #E0E0E0;
}
.features-list {
display: flex;
justify-content: center;
gap: 24px;
margin-bottom: 40px;
font-size: 20px;
}
.feature-item {
display: flex;
align-items: center;
gap: 8px;
}
.feature-item i {
color: #FFEB3B;
}
/* 3D Pixel Button */
.btn-pixel {
display: inline-block;
padding: 20px 40px;
font-family: 'Press Start 2P', cursive;
font-size: 16px;
text-decoration: none;
color: white;
text-transform: uppercase;
position: relative;
cursor: pointer;
transition: transform 0.1s;
border: 4px solid rgba(0,0,0,0.5);
}
.btn-green {
background-color: #558B2F;
box-shadow:
inset 4px 4px 0 rgba(255,255,255,0.3),
inset -4px -4px 0 rgba(0,0,0,0.3),
4px 4px 0 #1B5E20;
}
.btn-green:active {
transform: translate(4px, 4px);
box-shadow: none;
}
.btn-gold {
background-color: var(--gold-btn);
color: #3E2723;
box-shadow:
inset 4px 4px 0 rgba(255,255,255,0.4),
inset -4px -4px 0 rgba(0,0,0,0.2),
4px 4px 0 var(--gold-shadow);
}
.btn-gold:active {
transform: translate(4px, 4px);
box-shadow: none;
}
/* Showcase Section */
.showcase {
padding: 60px 0;
}
.grid-container {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 40px;
margin-bottom: 60px;
}
.card {
background-color: #C69C6D; /* Plank color */
border: 4px solid #3E2723;
padding: 8px;
position: relative;
transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.27);
cursor: pointer;
box-shadow:
8px 8px 0 rgba(0,0,0,0.5),
inset 4px 4px 0 rgba(255,255,255,0.2);
}
.card:hover {
transform: translateY(-10px) rotateX(5deg);
}
.card:last-child {
grid-column: span 2;
width: 60%;
margin: 0 auto;
}
.card-inner {
border: 2px solid #795548;
background: #EFEBE9;
padding: 20px;
height: 100%;
display: flex;
flex-direction: column;
}
.card-img-wrapper {
width: 100%;
height: 300px;
overflow: hidden;
border: 4px solid #3E2723;
margin-bottom: 20px;
position: relative;
}
.card-img {
width: 100%;
height: 100%;
object-fit: cover;
image-rendering: pixelated;
}
.card-content {
color: var(--text-main);
flex: 1;
}
.card-title {
font-family: 'Press Start 2P', cursive;
font-size: 20px;
margin-bottom: 12px;
color: #3E2723;
}
.card-desc {
font-size: 22px;
line-height: 1.4;
margin-bottom: 16px;
}
.card-features {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 20px;
}
.tag {
background: #8D6E63;
color: white;
padding: 4px 8px;
font-size: 18px;
border: 2px solid #5D4037;
}
.card-actions {
margin-top: auto;
text-align: right;
}
.btn-view {
background: #795548;
color: white;
border: none;
padding: 8px 16px;
font-family: 'VT323', monospace;
font-size: 22px;
cursor: pointer;
border: 2px solid #3E2723;
box-shadow: 2px 2px 0 #3E2723;
}
.btn-view:hover {
background: #8D6E63;
}
/* Download Section */
.download-section {
background: rgba(0,0,0,0.8);
border: 4px solid #795548;
padding: 40px;
text-align: center;
margin-bottom: 60px;
position: relative;
box-shadow: 0 0 20px rgba(0,0,0,0.5);
}
.file-list {
margin: 20px auto;
display: inline-block;
text-align: left;
font-size: 20px;
color: #ccc;
background: rgba(255,255,255,0.05);
padding: 20px;
border: 2px dashed #795548;
}
.file-list div {
margin-bottom: 8px;
}
/* Floating Orbs Animation */
.orb {
position: absolute;
width: 10px;
height: 10px;
background: #99ff33;
border: 2px solid #66cc00;
box-shadow: 0 0 5px #99ff33;
animation: float 4s infinite ease-in-out;
pointer-events: none;
}
@keyframes float {
0% { transform: translateY(0px) rotate(0deg); opacity: 0; }
50% { opacity: 1; }
100% { transform: translateY(-100px) rotate(360deg); opacity: 0; }
}
/* Footer */
.footer {
background: #000;
padding: 20px 0;
text-align: center;
border-top: 4px solid #795548;
font-size: 18px;
color: #888;
}
/* Modal */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.9);
z-index: 1000;
justify-content: center;
align-items: center;
}
.modal-content {
background: #C69C6D;
border: 4px solid white;
padding: 40px;
width: 600px;
text-align: center;
color: #3E2723;
box-shadow: 0 0 0 4px #000;
position: relative;
}
.modal-title {
font-family: 'Press Start 2P', cursive;
margin-bottom: 20px;
font-size: 24px;
}
.modal-close {
position: absolute;
top: 10px;
right: 10px;
background: #d32f2f;
color: white;
border: 2px solid #000;
width: 30px;
height: 30px;
cursor: pointer;
font-family: sans-serif;
font-weight: bold;
line-height: 26px;
}
/* Particle decoration on hero */
.hero-particle {
position: absolute;
width: 40px;
height: 40px;
background: rgba(255,255,255,0.1);
animation: pixelFloat 10s infinite linear;
}
@keyframes pixelFloat {
0% { transform: translateY(100vh); }
100% { transform: translateY(-100px); }
}
</style>
</head>
<body>
<!-- Hero Section -->
<header class="hero">
<!-- Floating particles -->
<div class="hero-particle" style="left: 10%; animation-delay: 0s; width: 20px; height: 20px;"></div>
<div class="hero-particle" style="left: 80%; animation-delay: 2s; width: 30px; height: 30px;"></div>
<div class="hero-particle" style="left: 40%; animation-delay: 5s; width: 15px; height: 15px;"></div>
<div class="hero-content">
<h1 class="hero-title">
<i class="ri-box-3-fill" style="vertical-align: middle; color: #5D4037;"></i>
CSP Learning Platform
</h1>
<div class="hero-subtitle">Minecraft Pixel Art Style Edition</div>
<p class="hero-desc">Complete HTML package with 5 fully functional pages</p>
<div class="features-list">
<div class="feature-item"><i class="ri-check-line"></i> Pure HTML/CSS/JS</div>
<div class="feature-item"><i class="ri-wifi-off-line"></i> Offline Ready</div>
<div class="feature-item"><i class="ri-device-line"></i> Responsive Design</div>
<div class="feature-item"><i class="ri-grid-fill"></i> Pixel Perfect</div>
</div>
<a href="#showcase" class="btn-pixel btn-green" id="startBtn">Start Exploring</a>
</div>
</header>
<!-- Main Content -->
<div class="container" id="showcase">
<div class="showcase">
<div class="grid-container">
<!-- Card 1: Login -->
<div class="card" onclick="window.location.href='#'">
<div class="card-inner">
<div class="card-img-wrapper">
<img src="https://a.lovart.ai/artifacts/agent/4jJnMhBDygFtkHFr.png" alt="Login Page" class="card-img">
</div>
<div class="card-content">
<h3 class="card-title">Login Page</h3>
<p class="card-desc">Minecraft-themed authentication with form validation.</p>
<div class="card-features">
<span class="tag">Tab Switching</span>
<span class="tag">Password Strength</span>
<span class="tag">Mock Login</span>
</div>
<div class="card-actions">
<button class="btn-view">View Page <i class="ri-arrow-right-line"></i></button>
</div>
</div>
</div>
</div>
<!-- Card 2: Problem Library -->
<div class="card" onclick="window.location.href='#'">
<div class="card-inner">
<div class="card-img-wrapper">
<img src="https://a.lovart.ai/artifacts/agent/j6E654EFXn3NxJb4.png" alt="Problem Library" class="card-img">
</div>
<div class="card-content">
<h3 class="card-title">Problem Library</h3>
<p class="card-desc">Browse 1,556 problems with advanced filtering and search.</p>
<div class="card-features">
<span class="tag">Sidebar Menu</span>
<span class="tag">Search & Filter</span>
<span class="tag">Pagination</span>
</div>
<div class="card-actions">
<button class="btn-view">View Page <i class="ri-arrow-right-line"></i></button>
</div>
</div>
</div>
</div>
<!-- Card 3: Code Editor -->
<div class="card" onclick="window.location.href='#'">
<div class="card-inner">
<div class="card-img-wrapper">
<img src="https://a.lovart.ai/artifacts/agent/OWMy8aA6hD9v2JYA.png" alt="Code Editor" class="card-img">
</div>
<div class="card-content">
<h3 class="card-title">Code Editor</h3>
<p class="card-desc">Split-panel editor with syntax highlighting and output.</p>
<div class="card-features">
<span class="tag">Multi-language</span>
<span class="tag">Theme Toggle</span>
<span class="tag">Test Runner</span>
</div>
<div class="card-actions">
<button class="btn-view">View Page <i class="ri-arrow-right-line"></i></button>
</div>
</div>
</div>
</div>
<!-- Card 4: Profile Center -->
<div class="card" onclick="window.location.href='#'">
<div class="card-inner">
<div class="card-img-wrapper">
<img src="https://a.lovart.ai/artifacts/agent/zDDM5HzNib6if6te.png" alt="Profile Center" class="card-img">
</div>
<div class="card-content">
<h3 class="card-title">Profile Center</h3>
<p class="card-desc">User dashboard with achievements, stats, and history.</p>
<div class="card-features">
<span class="tag">XP System</span>
<span class="tag">Badges</span>
<span class="tag">Leaderboard</span>
</div>
<div class="card-actions">
<button class="btn-view">View Page <i class="ri-arrow-right-line"></i></button>
</div>
</div>
</div>
</div>
<!-- Card 5: Contest Page (Wide) -->
<div class="card" onclick="window.location.href='#'">
<div class="card-inner" style="flex-direction: row; gap: 20px;">
<div class="card-img-wrapper" style="width: 50%; height: 250px; margin-bottom: 0;">
<img src="https://a.lovart.ai/artifacts/agent/KTP6hqvX7SCjbsuf.png" alt="Contest Page" class="card-img">
</div>
<div class="card-content" style="width: 50%;">
<h3 class="card-title">Contest Page</h3>
<p class="card-desc">Live contests with real-time leaderboard and arena mode.</p>
<div class="card-features">
<span class="tag">Countdown Timer</span>
<span class="tag">Live Updates</span>
<span class="tag">Rankings</span>
</div>
<div class="card-actions" style="margin-top: 20px;">
<button class="btn-view">View Page <i class="ri-arrow-right-line"></i></button>
</div>
</div>
</div>
</div>
</div>
<!-- Download Section -->
<div class="download-section">
<h2 class="pixel-text" style="color: white; margin-bottom: 20px; font-size: 28px;">Ready to Craft Your Code?</h2>
<div class="file-list">
<div><i class="ri-file-zip-line"></i> csp-platform-v1.0.zip (12.5 MB)</div>
<div><i class="ri-file-list-line"></i> README.txt (2 KB)</div>
<div><i class="ri-folder-line"></i> /assets (images, css, js)</div>
</div>
<div style="margin-top: 20px;">
<button class="btn-pixel btn-gold" id="downloadBtn">
<i class="ri-download-cloud-2-line"></i> Download Package
</button>
</div>
<p style="margin-top: 20px; color: #aaa; font-size: 18px;">Installation: Simply unzip and open index.html in any browser.</p>
</div>
</div>
</div>
<!-- Footer -->
<footer class="footer">
<p>Crafted with <i class="ri-heart-fill" style="color: #d32f2f;"></i> by HTML Generator | Version 1.19.2</p>
<p style="margin-top: 10px; font-size: 14px; opacity: 0.6;">Mock GitHub Link: github.com/user/csp-minecraft</p>
</footer>
<!-- Download Modal -->
<div class="modal" id="downloadModal">
<div class="modal-content">
<button class="modal-close" onclick="closeModal()">X</button>
<h3 class="modal-title">Generating World...</h3>
<p style="font-size: 20px; margin-bottom: 20px;">Your download is being prepared!</p>
<div style="background: #3E2723; padding: 10px; margin-top: 20px; position: relative; height: 30px;">
<div style="background: #76FF03; width: 0%; height: 100%; transition: width 2s;" id="progressBar"></div>
</div>
<p id="progressText" style="margin-top: 10px;">0%</p>
</div>
</div>
<script>
// Smooth scroll
document.getElementById('startBtn').addEventListener('click', function(e) {
e.preventDefault();
document.querySelector(this.getAttribute('href')).scrollIntoView({
behavior: 'smooth'
});
});
// Download Modal Logic
const modal = document.getElementById('downloadModal');
const downloadBtn = document.getElementById('downloadBtn');
const progressBar = document.getElementById('progressBar');
const progressText = document.getElementById('progressText');
downloadBtn.addEventListener('click', () => {
modal.style.display = 'flex';
progressBar.style.width = '0%';
progressText.innerText = '0%';
setTimeout(() => {
progressBar.style.width = '100%';
let progress = 0;
const interval = setInterval(() => {
progress += 5;
progressText.innerText = progress + '%';
if (progress >= 100) {
clearInterval(interval);
setTimeout(() => {
progressText.innerText = 'Download Complete!';
// Normally trigger download here
setTimeout(closeModal, 1500);
}, 500);
}
}, 100);
}, 500);
});
function closeModal() {
modal.style.display = 'none';
}
// XP Orbs Generation
function createOrb() {
const orb = document.createElement('div');
orb.className = 'orb';
// Random position
const x = Math.random() * window.innerWidth;
orb.style.left = x + 'px';
orb.style.bottom = '-20px'; // Start from bottom
// Random size variation
const size = 5 + Math.random() * 10;
orb.style.width = size + 'px';
orb.style.height = size + 'px';
document.body.appendChild(orb);
// Remove after animation
setTimeout(() => {
orb.remove();
}, 4000);
}
setInterval(createOrb, 800);
</script>
</body>
</html>

查看文件

@@ -1,619 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Minecraft Code Editor</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/remixicon/4.6.0/remixicon.min.css" rel="stylesheet">
<style>
@font-face {
font-family: 'DelaGothicOne';
src: url('https://assets-persist.lovart.ai/agent-static-assets/DelaGothicOne-Regular.ttf');
}
@font-face {
font-family: 'MiSans';
src: url('https://assets-persist.lovart.ai/agent-static-assets/MiSans-Regular.ttf');
}
@font-face {
font-family: 'PixelCode';
src: local('Courier New'), monospace; /* Fallback for code */
}
:root {
--mc-wood-light: #A07F53;
--mc-wood-dark: #765637;
--mc-stone: #757575;
--mc-stone-dark: #3b3b3b;
--mc-obsidian: #18181F;
--mc-grass: #7CB342;
--mc-grass-dark: #558B2F;
--mc-gold: #FFB300;
--mc-gold-dark: #FF8F00;
--mc-diamond: #00BCD4;
--mc-redstone: #F44336;
--mc-text-shadow: 2px 2px 0px #000;
--mc-border-light: rgba(255, 255, 255, 0.4);
--mc-border-dark: rgba(0, 0, 0, 0.4);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
width: 1920px;
height: 1080px; /* Fixed height for preview, though body usually auto */
background-color: #121212;
font-family: 'MiSans', sans-serif;
color: white;
overflow: hidden;
display: flex;
flex-direction: column;
}
/* Minecraft Texture Classes */
.bg-wood {
background-color: var(--mc-wood-dark);
background-image:
linear-gradient(90deg, rgba(0,0,0,0.1) 50%, transparent 50%),
linear-gradient(rgba(0,0,0,0.1) 50%, transparent 50%);
background-size: 4px 4px;
box-shadow: inset 0 0 40px rgba(0,0,0,0.5);
}
.bg-stone {
background-color: var(--mc-stone);
background-image:
radial-gradient(circle at 2px 2px, rgba(255,255,255,0.1) 1px, transparent 1px),
radial-gradient(circle at 10px 10px, rgba(0,0,0,0.1) 2px, transparent 2px);
background-size: 20px 20px;
}
.bg-obsidian {
background-color: #1a1a24;
background-image:
repeating-linear-gradient(45deg, rgba(255,255,255,0.02) 0px, rgba(255,255,255,0.02) 1px, transparent 1px, transparent 10px),
repeating-linear-gradient(-45deg, rgba(255,255,255,0.02) 0px, rgba(255,255,255,0.02) 1px, transparent 1px, transparent 10px);
}
/* Typography */
h1, h2, h3, .mc-font {
font-family: 'DelaGothicOne', cursive;
letter-spacing: 1px;
text-shadow: 2px 2px 0 rgba(0,0,0,0.5);
}
/* 3D Button Style */
.mc-btn {
border: 4px solid #000;
border-top-color: rgba(255,255,255,0.5);
border-left-color: rgba(255,255,255,0.5);
border-bottom-color: rgba(0,0,0,0.5);
border-right-color: rgba(0,0,0,0.5);
padding: 8px 16px;
font-family: 'DelaGothicOne', cursive;
cursor: pointer;
text-transform: uppercase;
font-size: 14px;
display: inline-flex;
align-items: center;
gap: 8px;
transition: transform 0.1s;
image-rendering: pixelated;
}
.mc-btn:active {
border-top-color: rgba(0,0,0,0.5);
border-left-color: rgba(0,0,0,0.5);
border-bottom-color: rgba(255,255,255,0.5);
border-right-color: rgba(255,255,255,0.5);
transform: translateY(2px);
}
.btn-green { background-color: var(--mc-grass); color: white; text-shadow: 1px 1px 0 #000; }
.btn-gold { background-color: var(--mc-gold); color: #3e2723; text-shadow: none; }
.btn-stone { background-color: var(--mc-stone); color: white; }
.btn-icon { padding: 8px; }
/* Scrollbar */
::-webkit-scrollbar { width: 12px; height: 12px; }
::-webkit-scrollbar-track { background: #2b2b2b; }
::-webkit-scrollbar-thumb {
background: #555;
border: 2px solid #2b2b2b;
border-top-color: #777;
border-left-color: #777;
}
/* Navigation */
.navbar {
height: 60px;
background-color: #212121;
border-bottom: 4px solid #000;
display: flex;
align-items: center;
padding: 0 24px;
gap: 20px;
position: relative;
z-index: 10;
}
.nav-logo { font-size: 24px; color: var(--mc-grass); }
.nav-item { color: #aaa; text-decoration: none; font-family: 'DelaGothicOne'; font-size: 14px; }
.nav-item:hover { color: white; text-decoration: underline; }
/* Main Layout */
.main-container {
display: flex;
height: calc(100vh - 60px);
position: relative;
}
/* Left Panel */
.left-panel {
width: 40%;
height: 100%;
display: flex;
flex-direction: column;
border-right: 4px solid #000;
position: relative;
}
.problem-content {
flex: 1;
overflow-y: auto;
padding: 32px;
}
.paper-card {
background-color: #FDF5E6; /* Old Lace */
color: #333;
padding: 24px;
border: 4px solid #333;
box-shadow: 8px 8px 0 rgba(0,0,0,0.3);
margin-bottom: 24px;
position: relative;
}
.paper-card::before {
content: '';
position: absolute;
top: -4px; left: -4px; right: -4px; bottom: -4px;
border: 2px solid #5d4037;
pointer-events: none;
}
.difficulty-badge {
background-color: var(--mc-grass);
color: white;
padding: 4px 8px;
font-size: 12px;
border: 2px solid #33691E;
display: inline-block;
margin-left: 10px;
font-family: 'DelaGothicOne';
vertical-align: middle;
}
.code-block-display {
background-color: #263238;
color: #eceff1;
padding: 12px;
border-left: 4px solid var(--mc-grass);
font-family: 'PixelCode', monospace;
margin: 10px 0;
white-space: pre-wrap;
}
.panel-footer {
height: 70px;
background-color: rgba(0,0,0,0.3);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
border-top: 4px solid #3e2723;
}
/* Right Panel */
.right-panel {
width: 60%;
display: flex;
flex-direction: column;
background-color: #1e1e1e;
}
.toolbar {
height: 56px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16px;
border-bottom: 4px solid #000;
}
.toolbar-group {
display: flex;
gap: 12px;
align-items: center;
}
.code-editor-wrapper {
flex: 1;
display: flex;
overflow: hidden;
position: relative;
}
.line-numbers {
width: 48px;
background-color: #121217;
color: #546e7a;
text-align: right;
padding: 16px 8px;
font-family: 'PixelCode', monospace;
font-size: 16px;
line-height: 1.5;
user-select: none;
border-right: 2px solid #333;
}
.code-area {
flex: 1;
background-color: transparent;
color: #d4d4d4;
padding: 16px;
font-family: 'PixelCode', monospace;
font-size: 16px;
line-height: 1.5;
overflow: auto;
white-space: pre;
outline: none;
}
/* Syntax Highlighting Colors */
.token-keyword { color: #cc7832; font-weight: bold; }
.token-type { color: #e0c46c; }
.token-string { color: #6a8759; }
.token-comment { color: #808080; font-style: italic; }
.token-number { color: #6897bb; }
.status-bar {
height: 32px;
background-color: #2d2d2d;
border-top: 2px solid #444;
display: flex;
align-items: center;
padding: 0 16px;
font-size: 12px;
gap: 20px;
color: #aaa;
font-family: 'PixelCode', monospace;
}
.combo-counter {
color: var(--mc-gold);
font-weight: bold;
display: flex;
align-items: center;
gap: 4px;
}
/* Test Panel */
.test-panel {
height: 220px;
border-top: 4px solid #000;
display: flex;
flex-direction: column;
transition: height 0.3s;
}
.test-panel.collapsed { height: 40px; }
.test-header {
height: 40px;
display: flex;
align-items: center;
padding: 0 16px;
background-color: rgba(0,0,0,0.2);
border-bottom: 2px solid #444;
cursor: pointer;
}
.tab-btn {
padding: 6px 16px;
background: none;
border: none;
color: #aaa;
cursor: pointer;
font-family: 'DelaGothicOne';
font-size: 12px;
}
.tab-btn.active { color: white; border-bottom: 2px solid var(--mc-grass); }
.test-content {
flex: 1;
padding: 16px;
display: flex;
gap: 16px;
overflow: hidden;
}
.test-case-item {
flex: 1;
background-color: rgba(0,0,0,0.3);
border: 2px solid #444;
padding: 12px;
font-family: 'PixelCode', monospace;
font-size: 14px;
}
.test-case-status { display: flex; justify-content: space-between; margin-bottom: 8px; font-weight: bold; }
.status-pass { color: var(--mc-grass); }
.status-fail { color: var(--mc-redstone); }
/* XP Overlay */
.xp-overlay {
position: absolute;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
font-size: 64px;
color: #B9F6CA;
text-shadow: 4px 4px 0 #1B5E20;
font-family: 'DelaGothicOne';
opacity: 0;
pointer-events: none;
z-index: 100;
}
@keyframes xpFloat {
0% { opacity: 0; transform: translate(-50%, -20%); }
20% { opacity: 1; transform: translate(-50%, -50%); }
80% { opacity: 1; transform: translate(-50%, -60%); }
100% { opacity: 0; transform: translate(-50%, -100%); }
}
.animate-xp { animation: xpFloat 2s ease-out forwards; }
/* Confetti */
.confetti {
position: absolute;
width: 8px; height: 8px;
background: var(--mc-diamond);
animation: fall 3s linear forwards;
z-index: 99;
}
@keyframes fall {
to { transform: translateY(100vh) rotate(720deg); }
}
/* Select styling */
.mc-select {
background: var(--mc-stone-dark);
border: 2px solid #000;
color: white;
padding: 4px 8px;
font-family: 'PixelCode', monospace;
cursor: pointer;
outline: none;
}
</style>
</head>
<body>
<!-- Top Navigation -->
<nav class="navbar bg-stone">
<div class="nav-logo mc-font"><i class="ri-box-3-fill"></i> MINECODE</div>
<a href="#" class="nav-item">Problems</a>
<a href="#" class="nav-item">Contest</a>
<a href="#" class="nav-item">Discuss</a>
<div style="flex:1"></div>
<div class="nav-item" style="color:var(--mc-gold)">LVL 24</div>
<img src="https://api.dicebear.com/7.x/pixel-art/svg?seed=Steve" alt="User" style="width: 32px; height: 32px; border: 2px solid white;">
</nav>
<div class="main-container">
<!-- Left Panel: Problem -->
<aside class="left-panel bg-wood">
<div class="problem-content">
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom: 20px;">
<h1 style="font-size: 28px; color: #3E2723;">1. Two Sum</h1>
<span class="difficulty-badge">EASY</span>
</div>
<div class="paper-card">
<p style="margin-bottom: 16px; line-height: 1.6;">
Given an array of integers <code>nums</code> and an integer <code>target</code>, return <i>indices</i> of the two numbers such that they add up to <code>target</code>.
</p>
<p style="line-height: 1.6;">
You may assume that each input would have <strong>exactly one solution</strong>, and you may not use the same element twice.
</p>
</div>
<div style="margin-bottom: 24px;">
<h3 style="color:#3E2723; margin-bottom:10px;">Example 1:</h3>
<div class="code-block-display">Input: nums = [2,7,11,15], target = 9
Output: [0,1]
Explanation: Because nums[0] + nums[1] == 9, we return [0, 1].</div>
</div>
<div style="margin-bottom: 24px;">
<h3 style="color:#3E2723; margin-bottom:10px;">Example 2:</h3>
<div class="code-block-display">Input: nums = [3,2,4], target = 6
Output: [1,2]</div>
</div>
<div style="margin-bottom: 24px;">
<h3 style="color:#3E2723; margin-bottom:10px; display:flex; align-items:center;">
<i class="ri-book-mark-fill" style="margin-right:8px;"></i> Hints
</h3>
<div style="background: rgba(0,0,0,0.1); padding: 10px; border: 2px dashed #5D4037; color: #3E2723;">
Try using a Hash Map to store visited numbers.
</div>
</div>
</div>
<div class="panel-footer">
<button class="mc-btn btn-stone"><i class="ri-star-fill"></i> Bookmark</button>
<div style="display: flex; gap: 10px;">
<button class="mc-btn btn-stone btn-icon"><i class="ri-share-forward-fill"></i></button>
<button class="mc-btn btn-stone btn-icon"><i class="ri-file-list-3-fill"></i></button>
</div>
</div>
</aside>
<!-- Right Panel: Editor -->
<main class="right-panel">
<!-- Toolbar -->
<div class="toolbar bg-stone">
<div class="toolbar-group">
<select class="mc-select">
<option>C++</option>
<option>Java</option>
<option>Python</option>
</select>
<button class="mc-btn btn-stone btn-icon" style="padding:4px 8px;"><i class="ri-settings-3-fill"></i></button>
<div style="color:#bbb; font-size:12px;">Size: 14px</div>
</div>
<div class="toolbar-group">
<button class="mc-btn btn-green" onclick="runCode()"><i class="ri-play-fill"></i> Run</button>
<button class="mc-btn btn-gold" onclick="submitCode()"><i class="ri-upload-cloud-2-fill"></i> Submit</button>
</div>
</div>
<!-- Code Editor -->
<div class="code-editor-wrapper bg-obsidian">
<div class="line-numbers">
1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>10<br>11
</div>
<div class="code-area" contenteditable="true" spellcheck="false">
<span class="token-keyword">class</span> Solution {
<span class="token-keyword">public</span>:
<span class="token-type">vector</span>&lt;<span class="token-type">int</span>&gt; twoSum(<span class="token-type">vector</span>&lt;<span class="token-type">int</span>&gt;& nums, <span class="token-type">int</span> target) {
<span class="token-type">unordered_map</span>&lt;<span class="token-type">int</span>, <span class="token-type">int</span>&gt; hash;
<span class="token-keyword">for</span> (<span class="token-type">int</span> i = <span class="token-number">0</span>; i < nums.size(); i++) {
<span class="token-type">int</span> complement = target - nums[i];
<span class="token-keyword">if</span> (hash.find(complement) != hash.end()) {
<span class="token-keyword">return</span> {hash[complement], i};
}
hash[nums[i]] = i;
}
<span class="token-keyword">return</span> {};
}
};</div>
</div>
<!-- Status Bar -->
<div class="status-bar">
<span>Ready</span>
<span style="border-left: 1px solid #555; height: 16px;"></span>
<span><i class="ri-time-fill"></i> 0ms</span>
<span><i class="ri-sd-card-mini-fill"></i> 0KB</span>
<div style="flex:1"></div>
<div class="combo-counter"><i class="ri-fire-fill"></i> COMBO x5</div>
</div>
<!-- Test Panel -->
<div class="test-panel bg-stone" id="testPanel">
<div class="test-header" onclick="toggleTestPanel()">
<i class="ri-terminal-box-fill" style="margin-right: 10px; color: #ccc;"></i>
<span class="mc-font" style="font-size: 14px; color: #eee; flex:1">Test Console</span>
<i class="ri-arrow-up-s-line" id="toggleIcon"></i>
</div>
<div style="background: rgba(0,0,0,0.4); padding: 0 16px; display: flex; border-bottom: 2px solid #444;">
<button class="tab-btn active">Test Cases</button>
<button class="tab-btn">Result</button>
</div>
<div class="test-content">
<div class="test-case-item">
<div class="test-case-status">Case 1 <i class="ri-checkbox-circle-fill status-pass"></i></div>
<div style="color: #888; font-size: 12px; margin-bottom: 4px;">Input</div>
<div style="color: #fff; margin-bottom: 8px;">nums = [2,7,11,15], target = 9</div>
<div style="color: #888; font-size: 12px; margin-bottom: 4px;">Expected</div>
<div style="color: #fff;">[0,1]</div>
</div>
<div class="test-case-item">
<div class="test-case-status">Case 2 <i class="ri-checkbox-circle-fill status-pass"></i></div>
<div style="color: #888; font-size: 12px; margin-bottom: 4px;">Input</div>
<div style="color: #fff; margin-bottom: 8px;">nums = [3,2,4], target = 6</div>
<div style="color: #888; font-size: 12px; margin-bottom: 4px;">Expected</div>
<div style="color: #fff;">[1,2]</div>
</div>
<div class="test-case-item" style="border-color: #F44336; opacity: 0.8;">
<div class="test-case-status">Case 3 <i class="ri-close-circle-fill status-fail"></i></div>
<div style="color: #888; font-size: 12px; margin-bottom: 4px;">Input</div>
<div style="color: #fff; margin-bottom: 8px;">nums = [3,3], target = 6</div>
<div style="color: #888; font-size: 12px; margin-bottom: 4px;">Expected</div>
<div style="color: #fff;">[0,1]</div>
</div>
</div>
</div>
</main>
</div>
<!-- XP Animation Overlay -->
<div id="xpDisplay" class="xp-overlay">+50 XP</div>
<script>
function toggleTestPanel() {
const panel = document.getElementById('testPanel');
const icon = document.getElementById('toggleIcon');
panel.classList.toggle('collapsed');
if(panel.classList.contains('collapsed')) {
icon.classList.remove('ri-arrow-down-s-line');
icon.classList.add('ri-arrow-up-s-line');
} else {
icon.classList.remove('ri-arrow-up-s-line');
icon.classList.add('ri-arrow-down-s-line');
}
}
function runCode() {
// Simulate running code
const btn = document.querySelector('.btn-green');
btn.innerHTML = '<i class="ri-loader-4-line ri-spin"></i> Running...';
setTimeout(() => {
btn.innerHTML = '<i class="ri-play-fill"></i> Run';
document.getElementById('testPanel').classList.remove('collapsed');
}, 1000);
}
function submitCode() {
const btn = document.querySelector('.btn-gold');
const originalContent = btn.innerHTML;
btn.innerHTML = '<i class="ri-loader-4-line ri-spin"></i> ...';
setTimeout(() => {
btn.innerHTML = originalContent;
showXP();
createConfetti();
}, 1500);
}
function showXP() {
const xp = document.getElementById('xpDisplay');
xp.classList.remove('animate-xp');
void xp.offsetWidth; // trigger reflow
xp.classList.add('animate-xp');
}
function createConfetti() {
const colors = ['#F44336', '#2196F3', '#FFEB3B', '#4CAF50', '#FF9800'];
for(let i=0; i<50; i++) {
const conf = document.createElement('div');
conf.className = 'confetti';
conf.style.left = Math.random() * 100 + 'vw';
conf.style.top = '-10px';
conf.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
conf.style.animationDuration = (Math.random() * 2 + 2) + 's';
document.body.appendChild(conf);
setTimeout(() => conf.remove(), 4000);
}
}
</script>
</body>
</html>

查看文件

@@ -1,905 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=1920, initial-scale=1.0">
<title>Minecraft Contest Page</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/remixicon/4.6.0/remixicon.min.css" rel="stylesheet">
<style>
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
:root {
--mc-bg-dark: #1D1D1D;
--mc-obsidian: #141019;
--mc-stone: #757575;
--mc-wood: #8B6914; /* Oak wood plank approximation */
--mc-wood-dark: #5C4033;
--mc-grass: #7CB342;
--mc-grass-dark: #558B2F;
--mc-gold: #FFB300;
--mc-redstone: #E53935;
--mc-diamond: #40C4FF;
--mc-white: #FFFFFF;
--mc-text-shadow: 2px 2px 0px #000;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
user-select: none;
}
body {
font-family: 'Press Start 2P', cursive;
background-color: #121212;
background-image:
linear-gradient(45deg, #1a1a1a 25%, transparent 25%, transparent 75%, #1a1a1a 75%, #1a1a1a),
linear-gradient(45deg, #1a1a1a 25%, transparent 25%, transparent 75%, #1a1a1a 75%, #1a1a1a);
background-size: 40px 40px;
background-position: 0 0, 20px 20px;
color: var(--mc-white);
width: 1920px;
margin: 0 auto;
overflow-x: hidden;
font-size: 14px;
line-height: 1.5;
}
/* --- Utilities --- */
.pixel-border {
box-shadow:
-4px 0 0 0 black,
4px 0 0 0 black,
0 -4px 0 0 black,
0 4px 0 0 black,
inset -4px -4px 0 0 rgba(0,0,0,0.5),
inset 4px 4px 0 0 rgba(255,255,255,0.2);
border: 4px solid transparent;
}
.text-shadow {
text-shadow: var(--mc-text-shadow);
}
.flex-center {
display: flex;
align-items: center;
justify-content: center;
}
/* --- Navigation --- */
.navbar {
height: 80px;
background-color: #333;
border-bottom: 4px solid #000;
display: flex;
align-items: center;
padding: 0 40px;
justify-content: space-between;
}
.logo {
font-size: 24px;
color: var(--mc-grass);
text-transform: uppercase;
}
.nav-links a {
color: #ccc;
text-decoration: none;
margin-left: 30px;
padding: 10px;
transition: color 0.2s;
}
.nav-links a.active {
color: var(--mc-gold);
text-shadow: 2px 2px 0 #5C4033;
}
/* --- Banner --- */
.banner {
height: 350px;
width: 100%;
position: relative;
background: linear-gradient(180deg, #2a0e36 0%, #46142e 100%); /* Nether-ish */
background-image: url('https://images.unsplash.com/photo-1628151015968-3a4429e9ef04?q=80&w=2072&auto=format&fit=crop'); /* Pixel art background placeholder */
background-size: cover;
background-position: center;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border-bottom: 6px solid #000;
overflow: hidden;
}
.banner-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.6);
z-index: 1;
}
.banner-content {
position: relative;
z-index: 2;
text-align: center;
width: 100%;
max-width: 1200px;
}
.live-badge {
background-color: var(--mc-redstone);
color: white;
padding: 8px 16px;
display: inline-block;
font-size: 16px;
margin-bottom: 20px;
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(229, 57, 53, 0.7); }
70% { transform: scale(1.05); box-shadow: 0 0 0 10px rgba(229, 57, 53, 0); }
100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(229, 57, 53, 0); }
}
.contest-title {
font-size: 42px;
color: var(--mc-gold);
margin-bottom: 20px;
-webkit-text-stroke: 2px #5C4033;
text-shadow: 4px 4px 0 #000;
}
.countdown {
font-size: 36px;
color: #fff;
margin-bottom: 30px;
letter-spacing: 4px;
text-shadow: 3px 3px 0 #000;
}
.join-btn {
background-color: var(--mc-grass);
color: white;
border: none;
padding: 20px 40px;
font-size: 20px;
cursor: pointer;
font-family: 'Press Start 2P', cursive;
transition: transform 0.1s, background-color 0.2s;
position: relative;
box-shadow: 0 6px 0 #558B2F, 0 10px 10px rgba(0,0,0,0.3);
text-transform: uppercase;
}
.join-btn:hover {
background-color: #8BC34A;
transform: translateY(-2px);
box-shadow: 0 8px 0 #558B2F, 0 12px 12px rgba(0,0,0,0.3);
}
.join-btn:active {
transform: translateY(4px);
box-shadow: 0 2px 0 #558B2F, 0 4px 4px rgba(0,0,0,0.3);
}
.participants-count {
margin-top: 20px;
font-size: 14px;
color: #ccc;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
}
.player-head {
width: 24px;
height: 24px;
background: #555;
display: inline-block;
image-rendering: pixelated;
}
/* --- Main Layout --- */
.container {
display: flex;
width: 100%;
padding: 40px;
gap: 40px;
max-width: 1800px;
margin: 0 auto;
}
.left-col {
flex: 0 0 70%;
}
.right-col {
flex: 0 0 30%;
}
/* --- Tab Nav --- */
.tabs {
display: flex;
margin-bottom: 20px;
gap: 10px;
}
.tab {
background-color: #555;
padding: 15px 30px;
color: #aaa;
cursor: pointer;
border-bottom: none;
position: relative;
top: 4px;
transition: all 0.2s;
}
.tab.active {
background-color: var(--mc-wood);
color: #fff;
top: 0;
padding-bottom: 19px;
text-shadow: 2px 2px 0 #000;
box-shadow:
-4px 0 0 0 black,
4px 0 0 0 black,
0 -4px 0 0 black;
}
/* --- Wood Panel Style --- */
.wood-panel {
background-color: var(--mc-wood);
border: 4px solid #000;
padding: 20px;
margin-bottom: 30px;
box-shadow: inset 0 0 0 4px rgba(255,255,255,0.1), 8px 8px 0 rgba(0,0,0,0.5);
position: relative;
}
.wood-texture {
/* Simulating wood grain with linear gradients */
background-image:
linear-gradient(90deg, rgba(0,0,0,0.05) 1px, transparent 1px),
linear-gradient(rgba(0,0,0,0.05) 1px, transparent 1px);
background-size: 20px 20px;
}
/* --- Contest Cards --- */
.contest-card {
background-color: #6D4C41; /* Darker wood */
margin-bottom: 20px;
padding: 20px;
display: flex;
align-items: center;
justify-content: space-between;
transition: transform 0.2s;
position: relative;
border: 4px solid #3E2723;
}
.contest-card:hover {
transform: translateX(10px);
background-color: #795548;
}
.card-icon {
width: 60px;
height: 60px;
background-color: #3E2723;
display: flex;
align-items: center;
justify-content: center;
font-size: 30px;
color: var(--mc-gold);
border: 4px solid #000;
margin-right: 20px;
}
.card-info h3 {
color: #fff;
margin-bottom: 10px;
font-size: 18px;
text-shadow: 2px 2px 0 #000;
}
.card-details {
font-size: 12px;
color: #D7CCC8;
display: flex;
gap: 15px;
}
.stars { color: var(--mc-gold); }
.card-action .btn-small {
background-color: var(--mc-stone);
border: 2px solid #000;
padding: 10px 20px;
color: white;
cursor: pointer;
font-family: inherit;
font-size: 12px;
box-shadow: 0 4px 0 #424242;
}
.card-action .btn-small:hover {
background-color: #9E9E9E;
margin-top: -2px;
box-shadow: 0 6px 0 #424242;
}
/* --- Leaderboard --- */
.leaderboard-title {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
}
.live-dot {
width: 12px;
height: 12px;
background-color: var(--mc-grass);
border-radius: 50%; /* Minecraft has no circles, but for indicator */
display: inline-block;
margin-right: 8px;
box-shadow: 0 0 10px var(--mc-grass);
animation: blink 1s infinite;
}
@keyframes blink { 50% { opacity: 0.5; } }
.leaderboard-table {
width: 100%;
border-collapse: separate;
border-spacing: 0 8px;
}
.leaderboard-table th {
text-align: left;
padding: 15px;
color: #3E2723;
font-size: 14px;
border-bottom: 4px solid #3E2723;
}
.leaderboard-table td {
background-color: #5D4037;
padding: 15px;
color: #fff;
border-top: 4px solid #3E2723;
border-bottom: 4px solid #3E2723;
}
.leaderboard-table tr td:first-child { border-left: 4px solid #3E2723; }
.leaderboard-table tr td:last-child { border-right: 4px solid #3E2723; }
.rank-1 td { background-color: #FFECB3; color: #5D4037; border-color: #FFB300 !important; }
.rank-2 td { background-color: #F5F5F5; color: #5D4037; border-color: #BDBDBD !important; }
.rank-3 td { background-color: #D7CCC8; color: #5D4037; border-color: #8D6E63 !important; }
.current-user td {
background-color: #DCEDC8;
color: #33691E;
border-color: #7CB342 !important;
}
.medal-icon { margin-right: 5px; }
/* --- Right Column --- */
.info-card {
background-color: #424242;
padding: 20px;
margin-bottom: 20px;
border: 4px solid #000;
box-shadow: 8px 8px 0 rgba(0,0,0,0.5);
}
.info-header {
font-size: 18px;
margin-bottom: 20px;
color: var(--mc-gold);
text-shadow: 2px 2px 0 #000;
border-bottom: 4px solid #000;
padding-bottom: 10px;
display: flex;
justify-content: space-between;
align-items: center;
}
.stat-row {
display: flex;
justify-content: space-between;
margin-bottom: 12px;
font-size: 13px;
}
.stat-val { color: var(--mc-diamond); }
.medals-display {
display: flex;
gap: 15px;
margin-top: 15px;
justify-content: center;
background: #212121;
padding: 10px;
border: 2px solid #000;
}
.timeline-item {
display: flex;
margin-bottom: 15px;
position: relative;
}
.timeline-line {
position: absolute;
left: 7px;
top: 20px;
bottom: -20px;
width: 2px;
background: #666;
z-index: 0;
}
.timeline-item:last-child .timeline-line { display: none; }
.timeline-dot {
width: 16px;
height: 16px;
background: var(--mc-gold);
border: 2px solid #000;
z-index: 1;
margin-right: 15px;
margin-top: 4px;
}
.timeline-content {
font-size: 12px;
}
.timeline-date { color: #888; font-size: 10px; margin-bottom: 4px; }
.timeline-rank { color: #fff; }
.collapsible-content {
max-height: 500px; /* arbitrary large */
overflow: hidden;
transition: max-height 0.3s ease-out;
}
.collapsed .collapsible-content {
max-height: 0;
}
.toggle-btn {
cursor: pointer;
font-size: 20px;
}
/* --- Animations --- */
@keyframes shine {
0% { background-position: -100px; }
40%, 100% { background-position: 140px; }
}
.rank-1 {
position: relative;
overflow: hidden;
}
/* Light ray effect using psuedo elements not easy on tr, applied to td */
.rank-1 td {
background: linear-gradient(120deg, #FFECB3 0%, #FFECB3 40%, #FFF9C4 50%, #FFECB3 60%, #FFECB3 100%);
background-size: 200% 100%;
animation: shine-gold 3s infinite linear;
}
@keyframes shine-gold {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
/* Responsive */
@media (max-width: 768px) {
.container { flex-direction: column; }
.left-col, .right-col { flex: 1 1 100%; }
}
</style>
</head>
<body>
<nav class="navbar pixel-border">
<div class="logo text-shadow"><i class="ri-sword-fill"></i> ALGO CRAFT</div>
<div class="nav-links">
<a href="#" class="active">CONTESTS</a>
<a href="#">PROBLEMS</a>
<a href="#">DISCUSS</a>
<a href="#">STORE</a>
</div>
<div style="display:flex; align-items:center; gap:10px;">
<div style="width:32px; height:32px; background:#ddd; border:2px solid #000;">
<img src="https://api.dicebear.com/7.x/pixel-art/svg?seed=Felix" alt="User" style="width:100%; height:100%;">
</div>
<span>STEVE_DEV</span>
</div>
</nav>
<div class="banner">
<div class="banner-overlay"></div>
<div class="banner-content">
<div class="live-badge pixel-border">LIVE NOW</div>
<h1 class="contest-title">WEEKLY ALGO CHALLENGE #42</h1>
<div class="countdown" id="countdown">02:45:30</div>
<button class="join-btn pixel-border">
JOIN CONTEST
</button>
<div class="participants-count">
<div class="player-head"></div>
<div class="player-head"></div>
<div class="player-head"></div>
<span>1,234 Crafters Online</span>
</div>
</div>
</div>
<div class="container">
<!-- Left Column -->
<div class="left-col">
<div class="tabs">
<div class="tab active pixel-border" onclick="switchTab('ongoing')">ONGOING</div>
<div class="tab pixel-border" onclick="switchTab('upcoming')">UPCOMING</div>
<div class="tab pixel-border" onclick="switchTab('finished')">FINISHED</div>
</div>
<div class="wood-panel pixel-border wood-texture" id="contest-list">
<!-- Contest Cards -->
<div class="contest-card pixel-border">
<div class="flex-center">
<div class="card-icon pixel-border"><i class="ri-trophy-fill"></i></div>
<div class="card-info">
<h3>Weekly Challenge #42</h3>
<div class="card-details">
<span><i class="ri-time-line"></i> Ends in 2h</span>
<span><i class="ri-star-fill stars"></i><i class="ri-star-fill stars"></i><i class="ri-star-fill stars"></i></span>
<span style="color:#4DB6AC">500 XP</span>
</div>
</div>
</div>
<div class="card-action">
<button class="btn-small">ENTER</button>
</div>
</div>
<div class="contest-card pixel-border">
<div class="flex-center">
<div class="card-icon pixel-border"><i class="ri-sword-fill" style="color:#CFD8DC"></i></div>
<div class="card-info">
<h3>Bi-Weekly Rumble #15</h3>
<div class="card-details">
<span><i class="ri-calendar-line"></i> Sat, 14:00</span>
<span><i class="ri-star-fill stars"></i><i class="ri-star-fill stars"></i><i class="ri-star-line"></i></span>
<span style="color:#4DB6AC">350 XP</span>
</div>
</div>
</div>
<div class="card-action">
<button class="btn-small">REGISTER</button>
</div>
</div>
<div class="contest-card pixel-border">
<div class="flex-center">
<div class="card-icon pixel-border"><i class="ri-vip-diamond-fill" style="color:var(--mc-diamond)"></i></div>
<div class="card-info">
<h3>Diamond League Qualifiers</h3>
<div class="card-details">
<span><i class="ri-calendar-line"></i> Sun, 10:00</span>
<span><i class="ri-star-fill stars"></i><i class="ri-star-fill stars"></i><i class="ri-star-fill stars"></i><i class="ri-star-fill stars"></i></span>
<span style="color:#4DB6AC">1000 XP + BADGE</span>
</div>
</div>
</div>
<div class="card-action">
<button class="btn-small">REGISTER</button>
</div>
</div>
</div>
<!-- Leaderboard -->
<div class="wood-panel pixel-border wood-texture">
<div class="leaderboard-title">
<h2 class="text-shadow" style="color:#3E2723">LEADERBOARD</h2>
<div style="font-size: 12px; color: #3E2723; display: flex; align-items: center;">
<span class="live-dot"></span> UPDATING LIVE
</div>
</div>
<table class="leaderboard-table">
<thead>
<tr>
<th>#</th>
<th>PLAYER</th>
<th>SCORE</th>
<th>TIME</th>
<th>STATUS</th>
</tr>
</thead>
<tbody id="leaderboard-body">
<!-- JS will populate -->
<tr class="rank-1 pixel-border">
<td><i class="ri-vip-crown-fill" style="color:#F57F17"></i> 1</td>
<td>Notch_Real</td>
<td>400</td>
<td>00:45:12</td>
<td><span style="color:green">AC</span></td>
</tr>
<tr class="rank-2">
<td><i class="ri-medal-fill" style="color:#757575"></i> 2</td>
<td>Alex_Pro</td>
<td>380</td>
<td>00:52:30</td>
<td><span style="color:green">AC</span></td>
</tr>
<tr class="rank-3">
<td><i class="ri-medal-fill" style="color:#8D6E63"></i> 3</td>
<td>CreeperAwMan</td>
<td>350</td>
<td>01:05:00</td>
<td><span style="color:green">AC</span></td>
</tr>
<tr>
<td>4</td>
<td>Enderman_tp</td>
<td>320</td>
<td>01:10:22</td>
<td><span style="color:green">AC</span></td>
</tr>
<tr>
<td>5</td>
<td>RedstoneEng</td>
<td>300</td>
<td>01:15:45</td>
<td><span style="color:green">AC</span></td>
</tr>
<tr>
<td>6</td>
<td>Miner64</td>
<td>280</td>
<td>01:20:10</td>
<td><span style="color:green">AC</span></td>
</tr>
<tr class="current-user">
<td>7</td>
<td>STEVE_DEV (YOU)</td>
<td>250</td>
<td>01:30:00</td>
<td><span style="color:orange">WA (1)</span></td>
</tr>
<tr>
<td>8</td>
<td>ZombieBoi</td>
<td>200</td>
<td>01:35:12</td>
<td><span style="color:green">AC</span></td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Right Column -->
<div class="right-col">
<!-- Stats Card -->
<div class="info-card pixel-border">
<div class="info-header">
<span>MY STATS</span>
<i class="ri-bar-chart-fill"></i>
</div>
<div class="stat-row">
<span>Participated:</span>
<span class="stat-val">24</span>
</div>
<div class="stat-row">
<span>Best Rank:</span>
<span class="stat-val">#3 🥉</span>
</div>
<div class="stat-row">
<span>Total Points:</span>
<span class="stat-val">1,250</span>
</div>
<div class="stat-row">
<span>Rating:</span>
<span class="stat-val" style="color:var(--mc-gold)">1650 (Diamond II)</span>
</div>
<div class="medals-display pixel-border">
<div style="text-align:center">
<i class="ri-medal-fill" style="color:var(--mc-gold); font-size:20px"></i>
<div style="font-size:10px">2</div>
</div>
<div style="text-align:center">
<i class="ri-medal-fill" style="color:#BDBDBD; font-size:20px"></i>
<div style="font-size:10px">5</div>
</div>
<div style="text-align:center">
<i class="ri-medal-fill" style="color:#8D6E63; font-size:20px"></i>
<div style="font-size:10px">8</div>
</div>
</div>
</div>
<!-- Rules Card -->
<div class="info-card pixel-border" id="rules-card">
<div class="info-header">
<span>RULES</span>
<i class="ri-book-open-fill toggle-btn" onclick="toggleRules()"></i>
</div>
<div class="collapsible-content" id="rules-content">
<ul style="padding-left: 20px; font-size: 12px; line-height: 1.8; color:#ccc;">
<li>Duration: 3 Hours</li>
<li>Penalty: +5 mins per WA</li>
<li>Languages: Java, C++, Python</li>
<li>Plagiarism check is ACTIVE</li>
<li>Do not break obsidian blocks</li>
</ul>
</div>
</div>
<!-- Past Results -->
<div class="info-card pixel-border">
<div class="info-header">
<span>HISTORY</span>
<i class="ri-history-line"></i>
</div>
<div class="timeline">
<div class="timeline-item">
<div class="timeline-line"></div>
<div class="timeline-dot"></div>
<div class="timeline-content">
<div class="timeline-date">2023-10-15</div>
<div class="timeline-rank">Weekly #41 - Rank #15</div>
</div>
</div>
<div class="timeline-item">
<div class="timeline-line"></div>
<div class="timeline-dot" style="background:#BDBDBD"></div>
<div class="timeline-content">
<div class="timeline-date">2023-10-08</div>
<div class="timeline-rank">Weekly #40 - Rank #2 🥈</div>
</div>
</div>
<div class="timeline-item">
<div class="timeline-line"></div>
<div class="timeline-dot" style="background:#8D6E63"></div>
<div class="timeline-content">
<div class="timeline-date">2023-10-01</div>
<div class="timeline-rank">Weekly #39 - Rank #3 🥉</div>
</div>
</div>
<div class="timeline-item">
<div class="timeline-line"></div>
<div class="timeline-dot" style="background:#555"></div>
<div class="timeline-content">
<div class="timeline-date">2023-09-24</div>
<div class="timeline-rank">Weekly #38 - Rank #45</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
// --- Countdown Timer ---
let totalSeconds = 2 * 3600 + 45 * 60 + 30; // 2h 45m 30s
const countdownEl = document.getElementById('countdown');
function updateCountdown() {
const h = Math.floor(totalSeconds / 3600);
const m = Math.floor((totalSeconds % 3600) / 60);
const s = totalSeconds % 60;
countdownEl.textContent =
`${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
if (totalSeconds > 0) {
totalSeconds--;
}
}
setInterval(updateCountdown, 1000);
updateCountdown();
// --- Tabs Functionality ---
function switchTab(tabName) {
const tabs = document.querySelectorAll('.tab');
tabs.forEach(t => t.classList.remove('active'));
// Find the clicked tab (simple logic for this demo)
event.target.classList.add('active');
// In a real app, this would filter the card list
const list = document.getElementById('contest-list');
list.style.opacity = '0.5';
setTimeout(() => {
list.style.opacity = '1';
// Mock content change
if(tabName === 'finished') {
// Just a visual cue that something changed
list.innerHTML = `<div style="text-align:center; padding:40px; color:#aaa">Loading Archive...</div>`;
setTimeout(() => {
list.innerHTML = `
<div class="contest-card pixel-border" style="opacity:0.7">
<div class="flex-center">
<div class="card-icon pixel-border" style="background:#555; color:#aaa"><i class="ri-trophy-line"></i></div>
<div class="card-info">
<h3 style="color:#aaa">Weekly Challenge #41</h3>
<div class="card-details">
<span>Ended: 2 days ago</span>
<span>Winner: Herobrine</span>
</div>
</div>
</div>
<div class="card-action">
<button class="btn-small">VIEW</button>
</div>
</div>
`;
}, 500);
} else if(tabName === 'ongoing') {
// Reset to initial HTML (simplified)
location.reload();
}
}, 200);
}
// --- Toggle Rules ---
function toggleRules() {
const card = document.getElementById('rules-card');
card.classList.toggle('collapsed');
}
// --- Real-time Leaderboard Simulation ---
function simulateLeaderboard() {
const rows = document.querySelectorAll('#leaderboard-body tr:not(.current-user)');
// Randomly swap two rows to simulate rank changes
const idx1 = Math.floor(Math.random() * 3); // Only top 3 change mostly
const idx2 = Math.floor(Math.random() * 3);
if (idx1 !== idx2) {
// Flash effect
rows[idx1].style.backgroundColor = '#fff';
setTimeout(() => {
rows[idx1].style.backgroundColor = ''; // revert to CSS class style
}, 200);
}
}
setInterval(simulateLeaderboard, 10000);
// --- LocalStorage Logic ---
// Save visit timestamp
localStorage.setItem('last_visit_mc_contest', new Date().toISOString());
// --- Firework/Hover Effects for Top 3 ---
const topRanks = document.querySelectorAll('.rank-1, .rank-2, .rank-3');
topRanks.forEach(row => {
row.addEventListener('mouseenter', () => {
// Add a subtle scale effect
row.style.transform = "scale(1.02)";
row.style.transition = "transform 0.2s";
row.style.zIndex = "10";
row.style.boxShadow = "0 0 15px rgba(255, 215, 0, 0.5)";
});
row.addEventListener('mouseleave', () => {
row.style.transform = "scale(1)";
row.style.boxShadow = "none";
row.style.zIndex = "1";
});
});
</script>
</body>
</html>

查看文件

@@ -1,605 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSP Learning Platform - Minecraft Login</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/remixicon/4.6.0/remixicon.min.css" rel="stylesheet">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" rel="stylesheet">
<style>
:root {
--mc-dirt-side: #79553a;
--mc-dirt-top: #5c3f2b;
--mc-grass-top: #70b348;
--mc-grass-side: #5a9139;
--mc-stone: #7d7d7d;
--mc-wood: #a0744b;
--mc-wood-dark: #6e4e34;
--mc-wood-light: #bca07e;
--mc-diamond: #29B6F6;
--mc-redstone: #E53935;
--mc-gold: #FFD700;
--mc-sky: #cceeff;
--bg-sky-top: #87CEEB;
--bg-sky-bottom: #E0F7FA;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Press Start 2P', cursive;
}
body {
width: 1920px;
min-height: 1080px;
background: linear-gradient(to bottom, var(--bg-sky-top), var(--bg-sky-bottom));
overflow-x: hidden;
position: relative;
color: white;
display: flex;
flex-direction: column;
align-items: center;
}
/* Pixel Cloud Background Decoration */
.clouds {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
pointer-events: none;
background-image:
radial-gradient(circle at 15% 20%, white 20px, transparent 21px),
radial-gradient(circle at 85% 30%, white 30px, transparent 31px),
radial-gradient(circle at 50% 10%, rgba(255,255,255,0.8) 40px, transparent 41px);
background-size: 800px 400px;
opacity: 0.6;
}
/* Top Nav */
.navbar {
width: 100%;
height: 80px;
background-color: rgba(0, 0, 0, 0.4);
display: flex;
align-items: center;
padding: 0 40px;
border-bottom: 4px solid rgba(0, 0, 0, 0.2);
backdrop-filter: blur(4px);
position: fixed;
top: 0;
z-index: 100;
}
.logo {
font-size: 20px;
color: #fff;
text-shadow: 4px 4px 0px #000;
display: flex;
align-items: center;
gap: 16px;
}
.logo i {
color: var(--mc-grass-top);
font-size: 28px;
}
/* Main Container */
.main-container {
margin-top: 140px;
position: relative;
z-index: 10;
}
/* Wooden Card */
.login-card {
width: 480px;
background-color: var(--mc-wood);
border: 4px solid #000;
box-shadow:
8px 8px 0px rgba(0,0,0,0.5),
inset 4px 4px 0px var(--mc-wood-light),
inset -4px -4px 0px var(--mc-wood-dark);
padding: 32px;
position: relative;
}
/* Wood Grain Texture CSS Pattern */
.login-card::before {
content: "";
position: absolute;
top: 4px;
left: 4px;
right: 4px;
bottom: 4px;
background-image:
repeating-linear-gradient(45deg,
rgba(0,0,0,0.05) 0px,
rgba(0,0,0,0.05) 2px,
transparent 2px,
transparent 8px
);
pointer-events: none;
z-index: 0;
}
.content-layer {
position: relative;
z-index: 1;
}
/* Tabs */
.tabs {
display: flex;
margin-bottom: 24px;
border-bottom: 4px solid var(--mc-wood-dark);
}
.tab-btn {
flex: 1;
padding: 12px 0;
text-align: center;
cursor: pointer;
color: rgba(255, 255, 255, 0.6);
background: none;
border: none;
font-size: 14px;
transition: all 0.2s;
text-shadow: 2px 2px 0 #000;
}
.tab-btn.active {
color: #fff;
background-color: rgba(0, 0, 0, 0.1);
border-top: 4px solid var(--mc-wood-light);
border-left: 4px solid var(--mc-wood-light);
border-right: 4px solid var(--mc-wood-dark);
}
/* Form Elements */
.form-group {
margin-bottom: 24px;
position: relative;
}
.input-wrapper {
position: relative;
display: flex;
align-items: center;
}
.input-icon {
position: absolute;
left: 16px;
color: #fff;
font-size: 16px;
z-index: 2;
text-shadow: 2px 2px 0 #000;
}
input[type="text"],
input[type="password"] {
width: 100%;
padding: 16px 16px 16px 48px;
background-color: #333;
border: 4px solid #000;
border-right-color: #555;
border-bottom-color: #555;
color: #fff;
font-size: 12px;
outline: none;
box-shadow: inset 4px 4px 0px #000;
}
input::placeholder {
color: #888;
}
input:focus {
background-color: #444;
}
.toggle-password {
position: absolute;
right: 16px;
cursor: pointer;
color: #aaa;
}
/* Checkbox */
.checkbox-group {
display: flex;
align-items: center;
margin-bottom: 24px;
font-size: 10px;
cursor: pointer;
}
.custom-checkbox {
width: 20px;
height: 20px;
background-color: #333;
border: 2px solid #000;
margin-right: 12px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: inset 2px 2px 0 #000;
}
.custom-checkbox i {
display: none;
font-size: 14px;
color: var(--mc-grass-top);
}
.checkbox-group.checked .custom-checkbox i {
display: block;
}
/* Password Strength */
.strength-meter {
height: 8px;
background-color: #000;
margin-top: 8px;
display: flex;
padding: 2px;
}
.strength-bar {
height: 100%;
width: 0%;
transition: width 0.3s, background-color 0.3s;
}
.strength-text {
font-size: 8px;
margin-top: 4px;
text-align: right;
color: #ddd;
}
/* Button */
.btn-minecraft {
width: 100%;
padding: 16px;
background-color: var(--mc-grass-top);
border: 4px solid #000;
color: #fff;
font-size: 16px;
cursor: pointer;
text-transform: uppercase;
text-shadow: 2px 2px 0px #000;
box-shadow:
inset 4px 4px 0px rgba(255,255,255,0.3),
inset -4px -4px 0px rgba(0,0,0,0.3),
4px 4px 0px #000;
transition: transform 0.1s, box-shadow 0.1s;
}
.btn-minecraft:active {
transform: translate(4px, 4px);
box-shadow: none;
}
.btn-minecraft:hover {
background-color: #7bc453;
}
/* Links */
.links {
margin-top: 24px;
text-align: center;
font-size: 10px;
}
.link-diamond {
color: var(--mc-diamond);
text-decoration: none;
text-shadow: 1px 1px 0 #000;
border-bottom: 2px solid transparent;
}
.link-diamond:hover {
border-bottom-color: var(--mc-diamond);
}
/* Error Message */
.error-msg {
color: var(--mc-redstone);
font-size: 10px;
margin-top: 8px;
text-shadow: 1px 1px 0 #000;
display: none;
}
/* Floating XP Orbs */
.xp-orb {
position: absolute;
width: 24px;
height: 24px;
background-color: #bcfc03;
border: 2px solid #6c8f02;
transform: rotate(45deg);
box-shadow: 0 0 10px #bcfc03;
animation: float 3s ease-in-out infinite;
}
.orb-1 { top: -40px; left: -40px; animation-delay: 0s; background-color: #bcfc03; }
.orb-2 { bottom: -30px; right: -30px; animation-delay: 1.5s; background-color: #64ffda; border-color: #009688; }
.orb-3 { top: 50%; right: -60px; animation-delay: 0.8s; width: 16px; height: 16px; }
@keyframes float {
0%, 100% { transform: rotate(45deg) translateY(0); }
50% { transform: rotate(45deg) translateY(-20px); }
}
/* Footer */
.footer {
position: fixed;
bottom: 0;
width: 100%;
height: 60px;
background-color: rgba(0,0,0,0.6);
backdrop-filter: blur(4px);
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-size: 12px;
border-top: 4px solid rgba(0,0,0,0.4);
}
.online-players {
display: flex;
align-items: center;
gap: 12px;
}
.player-head {
width: 24px;
height: 24px;
background-color: #333;
border: 2px solid #fff;
image-rendering: pixelated;
}
.player-head:nth-child(2) { background-color: #AA0000; }
.player-head:nth-child(3) { background-color: #00AA00; }
.player-head:nth-child(4) { background-color: #0000AA; }
/* Grass Block Floor Decoration */
.grass-floor {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 40px;
background-image:
linear-gradient(to right, var(--mc-grass-side) 50%, var(--mc-grass-top) 50%),
linear-gradient(to bottom, transparent 50%, var(--mc-dirt-side) 50%);
background-size: 40px 40px, 100% 40px;
z-index: 5;
display: none; /* Hidden to keep clean look, or show for immersion */
}
</style>
</head>
<body>
<nav class="navbar">
<div class="logo">
<i class="ri-box-3-fill"></i>
CSP Learning Platform
</div>
</nav>
<div class="clouds"></div>
<div class="main-container">
<div class="login-card">
<!-- Decorative Orbs -->
<div class="xp-orb orb-1"></div>
<div class="xp-orb orb-2"></div>
<div class="xp-orb orb-3"></div>
<div class="content-layer">
<div class="tabs">
<button class="tab-btn active" onclick="switchTab('login')">Login</button>
<button class="tab-btn" onclick="switchTab('register')">Register</button>
</div>
<form id="authForm" onsubmit="handleLogin(event)">
<div class="form-group">
<div class="input-wrapper">
<i class="ri-user-smile-fill input-icon"></i>
<input type="text" id="username" placeholder="Username" autocomplete="off">
</div>
<div id="username-error" class="error-msg">Username must be at least 3 chars!</div>
</div>
<div class="form-group">
<div class="input-wrapper">
<i class="ri-lock-fill input-icon"></i>
<input type="password" id="password" placeholder="Password" oninput="checkStrength()">
<i class="ri-eye-off-fill toggle-password" onclick="togglePassword()"></i>
</div>
<div class="strength-meter">
<div class="strength-bar" id="strength-bar"></div>
</div>
<div class="strength-text" id="strength-text"></div>
<div id="password-error" class="error-msg">Password must be at least 6 chars!</div>
</div>
<div class="checkbox-group" onclick="toggleRemember(this)">
<div class="custom-checkbox">
<i class="ri-check-fill"></i>
</div>
<span>Remember me</span>
</div>
<button type="submit" class="btn-minecraft" id="submit-btn">Login</button>
</form>
<div class="links">
<a href="#" class="link-diamond">Forgot password?</a>
</div>
</div>
</div>
</div>
<footer class="footer">
<div class="online-players">
<span>Online Players: <span style="color: var(--mc-gold);">12,002</span></span>
<div class="player-head"></div>
<div class="player-head"></div>
<div class="player-head"></div>
</div>
</footer>
<script>
let isLogin = true;
let rememberMe = false;
function switchTab(tab) {
const btns = document.querySelectorAll('.tab-btn');
const submitBtn = document.getElementById('submit-btn');
const form = document.getElementById('authForm');
// Clear errors
document.querySelectorAll('.error-msg').forEach(el => el.style.display = 'none');
if (tab === 'login') {
isLogin = true;
btns[0].classList.add('active');
btns[1].classList.remove('active');
submitBtn.innerText = 'Login';
} else {
isLogin = false;
btns[0].classList.remove('active');
btns[1].classList.add('active');
submitBtn.innerText = 'Register';
}
}
function togglePassword() {
const pwdInput = document.getElementById('password');
const icon = document.querySelector('.toggle-password');
if (pwdInput.type === 'password') {
pwdInput.type = 'text';
icon.classList.remove('ri-eye-off-fill');
icon.classList.add('ri-eye-fill');
} else {
pwdInput.type = 'password';
icon.classList.remove('ri-eye-fill');
icon.classList.add('ri-eye-off-fill');
}
}
function toggleRemember(el) {
el.classList.toggle('checked');
rememberMe = !rememberMe;
}
function checkStrength() {
const pwd = document.getElementById('password').value;
const bar = document.getElementById('strength-bar');
const text = document.getElementById('strength-text');
if (pwd.length === 0) {
bar.style.width = '0%';
bar.style.backgroundColor = 'transparent';
text.innerText = '';
return;
}
let strength = 0;
if (pwd.length >= 6) strength++;
if (pwd.match(/[a-z]/) && pwd.match(/[0-9]/)) strength++;
if (pwd.length > 8 && pwd.match(/[^a-zA-Z0-9]/)) strength++;
if (strength === 1) {
bar.style.width = '33%';
bar.style.backgroundColor = '#E53935'; // Weak
text.innerText = 'Weak';
text.style.color = '#E53935';
} else if (strength === 2) {
bar.style.width = '66%';
bar.style.backgroundColor = '#FFD700'; // Medium
text.innerText = 'Medium';
text.style.color = '#FFD700';
} else {
bar.style.width = '100%';
bar.style.backgroundColor = '#7CB342'; // Strong
text.innerText = 'Strong';
text.style.color = '#7CB342';
}
}
function handleLogin(e) {
e.preventDefault();
const user = document.getElementById('username').value;
const pass = document.getElementById('password').value;
const userErr = document.getElementById('username-error');
const passErr = document.getElementById('password-error');
let isValid = true;
// Reset errors
userErr.style.display = 'none';
passErr.style.display = 'none';
if (user.length < 3) {
userErr.style.display = 'block';
isValid = false;
}
if (pass.length < 6) {
passErr.style.display = 'block';
isValid = false;
}
if (!isValid) return;
// Mock login check
if (isLogin) {
if (user === 'test' && pass === 'whoami139') {
// Success
const token = 'mock_token_' + Date.now();
localStorage.setItem('authToken', token);
if(rememberMe) localStorage.setItem('savedUser', user);
// Simulate redirect with a nice alert or console log since we are in a static file context
// In a real app: window.location.href = 'problem-library.html';
const btn = document.getElementById('submit-btn');
btn.innerText = 'Connecting...';
btn.style.backgroundColor = '#FFD700'; // Gold color loading
setTimeout(() => {
// We just reload or show success for this demo
btn.innerText = 'Success!';
btn.style.backgroundColor = '#7CB342';
// Simulating redirect
// window.location.href = 'problem-library.html';
console.log("Redirecting to problem-library.html");
}, 1000);
} else {
// Wrong credentials
// Create a generic error message for demo
passErr.innerText = 'Invalid username or password!';
passErr.style.display = 'block';
}
} else {
// Register logic mock
alert(`Registered user: ${user}`);
}
}
</script>
</body>
</html>

查看文件

@@ -1,849 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Minecraft Problem Library</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/remixicon/4.6.0/remixicon.min.css" rel="stylesheet">
<style>
/* Font Loading */
@font-face {
font-family: 'Press Start 2P';
src: url('https://fonts.gstatic.com/s/pressstart2p/v15/e3t4euO8T-267oIAQAu6jDQyK3nVivM.woff2') format('woff2');
font-display: swap;
}
@font-face {
font-family: 'MiSans-Regular';
src: url('https://assets-persist.lovart.ai/agent-static-assets/MiSans-Regular.ttf');
}
:root {
--mc-dirt-dark: #5e4032;
--mc-dirt-light: #866043;
--mc-grass-side: #7CB342;
--mc-grass-top: #558B2F;
--mc-stone: #757575;
--mc-stone-light: #9E9E9E;
--mc-wood-dark: #4E342E;
--mc-wood-light: #795548;
--mc-plank: #A1887F;
--mc-text-shadow: 2px 2px 0 #000;
--mc-bg: #1e1e1e;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'MiSans-Regular', sans-serif;
background-color: #121212;
color: white;
width: 1920px;
min-height: 100vh;
overflow-x: hidden;
background-image:
linear-gradient(rgba(0,0,0,0.7), rgba(0,0,0,0.7)),
url('https://images.unsplash.com/photo-1587573088697-b4f9d17102dd?q=80&w=2669&auto=format&fit=crop'); /* Minecraft-like texture bg */
background-size: cover;
background-attachment: fixed;
display: flex;
flex-direction: column;
}
/* Minecraft Button Utility */
.mc-btn {
font-family: 'Press Start 2P', cursive;
font-size: 12px;
padding: 12px 20px;
border: 4px solid #000;
background: #9E9E9E;
color: #fff;
text-shadow: 2px 2px #333;
cursor: pointer;
box-shadow: inset -4px -4px 0px #555, inset 4px 4px 0px #DDD;
text-decoration: none;
display: inline-block;
transition: transform 0.1s;
text-transform: uppercase;
}
.mc-btn:active {
box-shadow: inset -4px -4px 0px #DDD, inset 4px 4px 0px #555;
transform: translateY(2px);
}
.mc-btn.green {
background: #7CB342;
box-shadow: inset -4px -4px 0px #33691E, inset 4px 4px 0px #AED581;
}
.mc-btn.green:active {
box-shadow: inset -4px -4px 0px #AED581, inset 4px 4px 0px #33691E;
}
/* Top Navigation */
.top-nav {
height: 70px;
background-color: rgba(0,0,0,0.8);
border-bottom: 4px solid #3e3e3e;
display: flex;
align-items: center;
padding: 0 40px;
justify-content: space-between;
position: sticky;
top: 0;
z-index: 100;
}
.nav-brand {
font-family: 'Press Start 2P', cursive;
font-size: 20px;
color: #FFEB3B;
text-shadow: var(--mc-text-shadow);
margin-right: 60px;
display: flex;
align-items: center;
gap: 10px;
}
.nav-links {
display: flex;
gap: 10px;
}
.nav-item {
font-family: 'Press Start 2P', cursive;
font-size: 12px;
color: #ccc;
text-decoration: none;
padding: 10px 20px;
border: 2px solid transparent;
transition: all 0.2s;
}
.nav-item:hover, .nav-item.active {
color: #fff;
background: rgba(255,255,255,0.1);
border: 2px solid #fff;
text-shadow: 2px 2px 0 #000;
}
.user-info {
display: flex;
align-items: center;
gap: 15px;
}
.user-avatar {
width: 40px;
height: 40px;
border: 2px solid #fff;
image-rendering: pixelated;
}
/* Layout Container */
.container {
display: flex;
flex: 1;
max-width: 1920px;
}
/* Sidebar */
.sidebar {
width: 280px;
background-color: #5D4037; /* Dark Wood */
border-right: 4px solid #3E2723;
display: flex;
flex-direction: column;
padding: 20px;
background-image: repeating-linear-gradient(
45deg,
rgba(255,255,255,0.05) 0px,
rgba(255,255,255,0.05) 2px,
transparent 2px,
transparent 10px
);
}
.sidebar-menu {
list-style: none;
margin-bottom: auto;
}
.menu-item {
margin-bottom: 12px;
}
.menu-link {
display: block;
padding: 15px;
background: #8D6E63; /* Plank color */
color: #fff;
text-decoration: none;
font-family: 'Press Start 2P', cursive;
font-size: 10px;
border: 4px solid #3E2723;
box-shadow: inset 2px 2px 0 rgba(255,255,255,0.2);
transition: transform 0.1s;
}
.menu-link:hover {
transform: scale(1.02);
background: #A1887F;
}
.menu-link.active {
background: #7CB342;
border-color: #33691E;
}
.daily-quest-panel {
background: #263238;
border: 4px solid #000;
padding: 15px;
margin-top: 20px;
}
.quest-title {
font-family: 'Press Start 2P', cursive;
font-size: 12px;
color: #FFEB3B;
margin-bottom: 10px;
text-shadow: 1px 1px #000;
}
.quest-progress {
height: 20px;
background: #37474F;
border: 2px solid #000;
position: relative;
}
.quest-bar {
height: 100%;
width: 60%;
background: #29B6F6;
box-shadow: inset 0 -2px 0 rgba(0,0,0,0.2);
}
.quest-text {
font-family: 'Press Start 2P', cursive;
font-size: 10px;
margin-top: 8px;
text-align: right;
color: #B0BEC5;
}
/* Main Content */
.main-content {
flex: 1;
padding: 40px;
background: rgba(0,0,0,0.4);
backdrop-filter: blur(5px);
}
.page-header {
display: flex;
align-items: center;
margin-bottom: 30px;
gap: 15px;
}
.page-title {
font-family: 'Press Start 2P', cursive;
font-size: 32px;
color: #fff;
text-shadow: 4px 4px 0 #000;
}
/* Stats Cards */
.stats-row {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: #546E7A; /* Stone Blue */
border: 4px solid #263238;
padding: 20px;
position: relative;
box-shadow: 6px 6px 0 rgba(0,0,0,0.5);
}
.stat-card::after {
content: '';
position: absolute;
top: 4px;
left: 4px;
right: 4px;
bottom: 4px;
border: 2px dashed rgba(255,255,255,0.1);
pointer-events: none;
}
.stat-label {
font-family: 'Press Start 2P', cursive;
font-size: 10px;
color: #CFD8DC;
margin-bottom: 10px;
}
.stat-value {
font-family: 'Press Start 2P', cursive;
font-size: 24px;
color: #fff;
text-shadow: 2px 2px 0 #000;
}
/* Filters */
.filter-section {
background: #3E2723;
border: 4px solid #000;
padding: 15px;
margin-bottom: 20px;
display: flex;
flex-wrap: wrap;
gap: 15px;
align-items: center;
}
.category-tabs {
display: flex;
gap: 8px;
margin-right: auto;
}
.tab-btn {
font-family: 'Press Start 2P', cursive;
font-size: 10px;
padding: 10px 15px;
background: #5D4037;
border: 2px solid #8D6E63;
color: #D7CCC8;
cursor: pointer;
text-transform: uppercase;
}
.tab-btn.active {
background: #7CB342;
color: white;
border-color: #AED581;
box-shadow: 0 4px 0 #33691E;
transform: translateY(-2px);
}
.filter-controls {
display: flex;
gap: 10px;
}
.mc-select {
font-family: 'MiSans-Regular', sans-serif; /* Readable font for dropdown */
font-size: 14px;
padding: 8px;
background: #D7CCC8;
border: 3px solid #5D4037;
color: #3E2723;
outline: none;
cursor: pointer;
min-width: 120px;
}
.mc-input {
font-family: 'MiSans-Regular', sans-serif;
font-size: 14px;
padding: 8px 12px;
background: #212121;
border: 3px solid #616161;
color: #fff;
width: 200px;
}
.mc-search-btn {
background: #FFB74D;
border: 3px solid #E65100;
color: #3E2723;
padding: 0 15px;
cursor: pointer;
font-family: 'Press Start 2P', cursive;
font-size: 10px;
display: flex;
align-items: center;
justify-content: center;
}
/* Problem Table */
.problem-table-container {
background: #8D6E63; /* Plank */
border: 4px solid #3E2723;
padding: 10px;
box-shadow: 8px 8px 0 rgba(0,0,0,0.5);
}
.problem-table {
width: 100%;
border-collapse: collapse;
}
.problem-table th {
font-family: 'Press Start 2P', cursive;
font-size: 10px;
text-align: left;
padding: 15px;
background: #5D4037;
color: #FFCC80;
border-bottom: 4px solid #3E2723;
}
.problem-table td {
padding: 12px 15px;
border-bottom: 2px solid #5D4037;
color: #3E2723;
font-size: 14px;
font-weight: 500;
}
.problem-table tr:hover td {
background-color: rgba(255,255,255,0.1);
}
.problem-table tr:last-child td {
border-bottom: none;
}
.status-icon {
font-size: 18px;
}
.status-solved { color: #2E7D32; }
.status-locked { color: #616161; }
.status-attempted { color: #F57F17; }
.difficulty-stars {
display: flex;
gap: 2px;
}
.star {
font-size: 12px;
}
.tag-badge {
background: #3E2723;
color: #D7CCC8;
padding: 2px 6px;
font-size: 12px;
border-radius: 2px;
display: inline-block;
margin-right: 4px;
}
.table-btn {
font-family: 'Press Start 2P', cursive;
font-size: 8px;
padding: 6px 10px;
border: 2px solid #000;
background: #FFB74D;
color: #000;
text-decoration: none;
display: inline-block;
box-shadow: inset -2px -2px 0 #E65100, inset 2px 2px 0 #FFE0B2;
}
.table-btn:hover {
transform: scale(1.05);
}
/* Pagination */
.pagination {
display: flex;
justify-content: center;
align-items: center;
margin-top: 20px;
gap: 15px;
}
.page-info {
font-family: 'Press Start 2P', cursive;
font-size: 10px;
color: #fff;
}
/* Right Floating Panel */
.right-panel {
width: 260px;
padding: 20px;
margin-right: 20px;
}
.task-card {
background: #F5F5F5; /* Paper */
border: 4px solid #424242;
padding: 15px;
box-shadow: 4px 4px 0 rgba(0,0,0,0.5);
position: sticky;
top: 100px;
}
.task-card::before {
content: '';
display: block;
height: 10px;
background: #B71C1C; /* Red tape */
width: 40px;
margin: -25px auto 15px;
box-shadow: 1px 1px 2px rgba(0,0,0,0.3);
}
.task-header {
font-family: 'Press Start 2P', cursive;
font-size: 12px;
color: #212121;
margin-bottom: 15px;
text-align: center;
border-bottom: 2px dashed #9E9E9E;
padding-bottom: 10px;
}
.task-list {
list-style: none;
}
.task-item {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 12px;
font-size: 13px;
color: #424242;
}
.mc-checkbox {
appearance: none;
width: 16px;
height: 16px;
border: 2px solid #000;
background: #fff;
cursor: pointer;
position: relative;
}
.mc-checkbox:checked {
background: #7CB342;
}
.mc-checkbox:checked::after {
content: '✔';
position: absolute;
top: -3px;
left: 1px;
font-size: 12px;
color: #fff;
}
.xp-reward {
font-family: 'Press Start 2P', cursive;
font-size: 8px;
color: #F57F17;
margin-left: auto;
}
/* Responsive */
@media (max-width: 1200px) {
.stats-row {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<nav class="top-nav">
<div class="nav-brand">
<i class="ri-book-3-fill" style="color: #8D6E63; font-size: 24px;"></i>
CRAFT CODE
</div>
<div class="nav-links">
<a href="#" class="nav-item">HOME</a>
<a href="#" class="nav-item active">PROBLEMS</a>
<a href="#" class="nav-item">CONTEST</a>
<a href="#" class="nav-item">LEADERBOARD</a>
</div>
<div class="user-info">
<img src="https://api.dicebear.com/7.x/pixel-art/svg?seed=Felix" alt="User" class="user-avatar">
<div style="font-family: 'Press Start 2P'; font-size: 10px; color: #fff;">Steve</div>
</div>
</nav>
<div class="container">
<!-- Sidebar -->
<aside class="sidebar">
<ul class="sidebar-menu">
<li class="menu-item"><a href="#" class="menu-link active">ALL PROBLEMS</a></li>
<li class="menu-item"><a href="#" class="menu-link">ALGORITHMS</a></li>
<li class="menu-item"><a href="#" class="menu-link">DATA STRUCTURES</a></li>
<li class="menu-item"><a href="#" class="menu-link">MATH</a></li>
<li class="menu-item"><a href="#" class="menu-link">STRINGS</a></li>
<li class="menu-item"><a href="#" class="menu-link">DYNAMIC PROG</a></li>
</ul>
<div class="daily-quest-panel">
<div class="quest-title">DAILY QUEST</div>
<div class="quest-progress">
<div class="quest-bar"></div>
</div>
<div class="quest-text">3/5 COMPLETED</div>
</div>
</aside>
<!-- Main Content -->
<main class="main-content">
<div class="page-header">
<i class="ri-book-read-line" style="font-size: 32px; color: #FFD54F;"></i>
<h1 class="page-title">PROBLEM LIBRARY</h1>
</div>
<div class="stats-row">
<div class="stat-card">
<div class="stat-label">TOTAL PROBLEMS</div>
<div class="stat-value">1,556</div>
</div>
<div class="stat-card">
<div class="stat-label">COMPLETED</div>
<div class="stat-value" style="color: #7CB342;">128</div>
</div>
<div class="stat-card">
<div class="stat-label">PASS RATE</div>
<div class="stat-value" style="color: #FFB74D;">85%</div>
</div>
</div>
<div class="filter-section">
<div class="category-tabs">
<button class="tab-btn active" onclick="switchTab(this)">CSP-J</button>
<button class="tab-btn" onclick="switchTab(this)">CSP-S</button>
<button class="tab-btn" onclick="switchTab(this)">NOIP</button>
</div>
<div class="filter-controls">
<select class="mc-select">
<option>All Difficulty</option>
<option>1 Star</option>
<option>2 Stars</option>
<option>3 Stars</option>
<option>4 Stars</option>
<option>5 Stars</option>
</select>
<select class="mc-select">
<option>All Tags</option>
<option>Array</option>
<option>Stack</option>
<option>Queue</option>
</select>
<input type="text" class="mc-input" id="searchInput" placeholder="Search problems...">
<button class="mc-search-btn" onclick="filterTable()">
<i class="ri-search-2-line"></i>
</button>
</div>
</div>
<div class="problem-table-container">
<table class="problem-table" id="problemTable">
<thead>
<tr>
<th width="50">STS</th>
<th width="60">ID</th>
<th>TITLE</th>
<th width="100">RATE</th>
<th width="100">DIFF</th>
<th width="150">TAGS</th>
<th width="80">ACT</th>
</tr>
</thead>
<tbody>
<!-- Data rows will be populated by JS -->
<tr>
<td><i class="ri-checkbox-circle-fill status-solved status-icon"></i></td>
<td>1001</td>
<td>A+B Problem</td>
<td>95%</td>
<td>
<div class="difficulty-stars">
<i class="ri-star-fill star" style="color:#7CB342"></i>
<i class="ri-star-line star" style="color:#757575"></i>
<i class="ri-star-line star" style="color:#757575"></i>
</div>
</td>
<td><span class="tag-badge">Math</span></td>
<td><a href="#" class="table-btn">SOLVE</a></td>
</tr>
<tr>
<td><i class="ri-lock-fill status-locked status-icon"></i></td>
<td>1002</td>
<td>Fibonacci Sequence</td>
<td>45%</td>
<td>
<div class="difficulty-stars">
<i class="ri-star-fill star" style="color:#FFB74D"></i>
<i class="ri-star-fill star" style="color:#FFB74D"></i>
<i class="ri-star-line star" style="color:#757575"></i>
</div>
</td>
<td><span class="tag-badge">DP</span></td>
<td><a href="#" class="table-btn">SOLVE</a></td>
</tr>
</tbody>
</table>
</div>
<div class="pagination">
<button class="mc-btn">PREV</button>
<div class="page-info">PAGE 1 / 32</div>
<button class="mc-btn green">NEXT</button>
</div>
</main>
<!-- Right Panel -->
<aside class="right-panel">
<div class="task-card">
<div class="task-header">DAILY TASKS</div>
<ul class="task-list">
<li class="task-item">
<input type="checkbox" class="mc-checkbox" checked>
<span>Login Daily</span>
<span class="xp-reward">+10XP</span>
</li>
<li class="task-item">
<input type="checkbox" class="mc-checkbox" checked>
<span>Solve 1 Easy</span>
<span class="xp-reward">+20XP</span>
</li>
<li class="task-item">
<input type="checkbox" class="mc-checkbox">
<span>Solve 1 Medium</span>
<span class="xp-reward">+50XP</span>
</li>
<li class="task-item">
<input type="checkbox" class="mc-checkbox">
<span>Submit 5 times</span>
<span class="xp-reward">+15XP</span>
</li>
<li class="task-item">
<input type="checkbox" class="mc-checkbox">
<span>Review Code</span>
<span class="xp-reward">+30XP</span>
</li>
</ul>
</div>
</aside>
</div>
<script>
// Sample Data Generation
const problems = [
{ id: '1001', title: 'A+B Problem', rate: '98%', diff: 1, tags: ['Math'], status: 'solved' },
{ id: '1002', title: 'Knapsack Problem', rate: '45%', diff: 3, tags: ['DP', 'Math'], status: 'attempted' },
{ id: '1003', title: 'Binary Tree Traversal', rate: '60%', diff: 2, tags: ['Tree', 'DFS'], status: 'locked' },
{ id: '1004', title: 'Shortest Path (Dijkstra)', rate: '32%', diff: 4, tags: ['Graph'], status: 'locked' },
{ id: '1005', title: 'String Matching (KMP)', rate: '28%', diff: 5, tags: ['String'], status: 'locked' },
{ id: '1006', title: 'Counting Sort', rate: '75%', diff: 2, tags: ['Sort'], status: 'solved' },
{ id: '1007', title: 'N-Queens', rate: '40%', diff: 3, tags: ['Backtrack'], status: 'locked' },
{ id: '1008', title: 'Union Find', rate: '55%', diff: 3, tags: ['DS'], status: 'attempted' },
{ id: '1009', title: 'Segment Tree Range Sum', rate: '20%', diff: 5, tags: ['Tree'], status: 'locked' },
{ id: '1010', title: 'Prim MST', rate: '35%', diff: 4, tags: ['Graph'], status: 'locked' }
];
function getStatusIcon(status) {
if (status === 'solved') return '<i class="ri-checkbox-circle-fill status-solved status-icon"></i>';
if (status === 'attempted') return '<i class="ri-record-circle-fill status-attempted status-icon"></i>';
return '<i class="ri-lock-fill status-locked status-icon"></i>';
}
function getStars(count) {
let html = '<div class="difficulty-stars">';
const colors = ['#7CB342', '#29B6F6', '#FFB74D', '#FF7043', '#D32F2F']; // Color coding
const color = colors[count - 1] || '#757575';
for(let i=0; i<5; i++) {
if(i < count) {
html += `<i class="ri-star-fill star" style="color:${color}"></i>`;
} else {
html += `<i class="ri-star-line star" style="color:#757575"></i>`;
}
}
html += '</div>';
return html;
}
function renderTable(data) {
const tbody = document.querySelector('#problemTable tbody');
tbody.innerHTML = '';
data.forEach(p => {
const tr = document.createElement('tr');
tr.innerHTML = `
<td>${getStatusIcon(p.status)}</td>
<td>${p.id}</td>
<td style="cursor:pointer;" onclick="window.location.href='code-editor.html?id=${p.id}'">${p.title}</td>
<td>${p.rate}</td>
<td>${getStars(p.diff)}</td>
<td>${p.tags.map(t => `<span class="tag-badge">${t}</span>`).join('')}</td>
<td><a href="code-editor.html?id=${p.id}" class="table-btn">SOLVE</a></td>
`;
tbody.appendChild(tr);
});
}
// Tab Switching Logic
function switchTab(element) {
document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
element.classList.add('active');
// Mock data change
const shuffled = [...problems].sort(() => 0.5 - Math.random());
renderTable(shuffled);
}
// Filter Logic
function filterTable() {
const term = document.getElementById('searchInput').value.toLowerCase();
const filtered = problems.filter(p =>
p.title.toLowerCase().includes(term) ||
p.id.includes(term) ||
p.tags.some(t => t.toLowerCase().includes(term))
);
renderTable(filtered);
}
// Real-time search
document.getElementById('searchInput').addEventListener('input', filterTable);
// Sidebar Active State
document.querySelectorAll('.menu-link').forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
document.querySelectorAll('.menu-link').forEach(l => l.classList.remove('active'));
link.classList.add('active');
});
});
// Initialize
renderTable(problems);
// Save checkbox state to localStorage
document.querySelectorAll('.mc-checkbox').forEach((box, index) => {
const saved = localStorage.getItem(`task-${index}`);
if(saved !== null) {
box.checked = saved === 'true';
}
box.addEventListener('change', () => {
localStorage.setItem(`task-${index}`, box.checked);
});
});
</script>
</body>
</html>

查看文件

@@ -1,896 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Minecraft Profile Center</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/remixicon/4.6.0/remixicon.min.css" rel="stylesheet">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" rel="stylesheet">
<style>
:root {
--bg-color: #1a1a1a;
--text-color: #ffffff;
--minecraft-green: #59a228;
--minecraft-dark-green: #376318;
--wood-light: #a07449;
--wood-dark: #634329;
--wood-border: #3d2919;
--gold: #fcc201;
--purple: #aa00aa;
--iron: #c6c6c6;
--diamond: #3fd2ea;
--red: #aa0000;
}
* {
box-sizing: border-box;
image-rendering: pixelated;
}
body {
margin: 0;
padding: 0;
background-color: #2c2c2c;
background-image:
linear-gradient(45deg, #252525 25%, transparent 25%),
linear-gradient(-45deg, #252525 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, #252525 75%),
linear-gradient(-45deg, transparent 75%, #252525 75%);
background-size: 40px 40px;
font-family: 'Press Start 2P', cursive;
color: var(--text-color);
width: 1920px;
overflow-x: hidden;
min-height: 100vh;
}
/* Utility Classes */
.flex { display: flex; }
.flex-col { flex-direction: column; }
.items-center { align-items: center; }
.justify-center { justify-content: center; }
.justify-between { justify-content: space-between; }
.gap-2 { gap: 8px; }
.gap-4 { gap: 16px; }
.w-full { width: 100%; }
.h-full { height: 100%; }
/* Minecraft UI Components */
.mc-btn {
background-color: #7d7d7d;
border: 4px solid #000;
border-top-color: #dedede;
border-left-color: #dedede;
border-right-color: #555;
border-bottom-color: #555;
padding: 10px 20px;
cursor: pointer;
text-transform: uppercase;
font-size: 14px;
color: white;
text-decoration: none;
position: relative;
}
.mc-btn:active, .mc-btn.active {
background-color: #555;
border-top-color: #333;
border-left-color: #333;
border-right-color: #dedede;
border-bottom-color: #dedede;
color: #aaa;
}
.mc-card {
background-color: #c6c6c6;
border: 4px solid #000;
border-top-color: #fff;
border-left-color: #fff;
border-right-color: #555;
border-bottom-color: #555;
padding: 4px;
position: relative;
}
.mc-card-inner {
background-color: #8b8b8b;
border: 4px solid #373737;
border-right-color: #fff;
border-bottom-color: #fff;
padding: 16px;
height: 100%;
}
.mc-wood-panel {
background-color: var(--wood-light);
background-image:
linear-gradient(90deg, rgba(0,0,0,0.1) 50%, transparent 50%),
linear-gradient(rgba(0,0,0,0.1) 50%, transparent 50%);
background-size: 8px 8px;
border: 4px solid var(--wood-border);
box-shadow: inset 4px 4px 0 var(--wood-dark), inset -4px -4px 0 var(--wood-dark);
padding: 20px;
}
/* Top Nav */
nav {
background: rgba(0,0,0,0.8);
padding: 20px 40px;
position: sticky;
top: 0;
z-index: 100;
border-bottom: 4px solid #555;
}
.nav-brand { font-size: 24px; color: var(--diamond); text-shadow: 2px 2px #000; }
.nav-links { gap: 20px; }
/* Main Container */
.container {
padding: 40px;
width: 100%;
}
/* User Info Card */
.user-hero {
display: grid;
grid-template-columns: 200px 1fr 400px;
gap: 40px;
margin-bottom: 40px;
position: relative;
}
.avatar-container {
width: 160px;
height: 160px;
background: #000;
border: 4px solid #fff;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
.avatar-img {
width: 100%;
height: 100%;
object-fit: cover;
image-rendering: pixelated;
}
.user-details h1 {
font-size: 32px;
margin: 0 0 10px 0;
text-shadow: 3px 3px 0 #000;
}
.badge-lv {
display: inline-block;
background: var(--gold);
color: #000;
padding: 8px 12px;
border: 2px solid #fff;
margin-right: 10px;
box-shadow: 4px 4px 0 rgba(0,0,0,0.5);
}
.title-banner {
display: inline-block;
background: var(--purple);
padding: 8px 16px;
border: 2px solid #d369d3;
text-shadow: 2px 2px #000;
margin-top: 10px;
}
.xp-bar-container {
width: 100%;
height: 24px;
background: #333;
border: 2px solid #fff;
margin-top: 15px;
position: relative;
}
.xp-bar-fill {
height: 100%;
background: var(--minecraft-green);
width: 0%; /* JS will animate */
transition: width 1.5s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
}
.xp-bar-fill::after {
content: "";
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background-image: linear-gradient(
45deg,
rgba(255, 255, 255, 0.2) 25%,
transparent 25%,
transparent 50%,
rgba(255, 255, 255, 0.2) 50%,
rgba(255, 255, 255, 0.2) 75%,
transparent 75%,
transparent
);
background-size: 20px 20px;
}
.xp-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 10px;
color: #fff;
text-shadow: 1px 1px #000;
z-index: 2;
}
.stats-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.stat-card {
background: rgba(0,0,0,0.3);
border: 2px solid rgba(255,255,255,0.2);
padding: 10px;
text-align: center;
}
.stat-val { font-size: 20px; color: var(--gold); margin-bottom: 5px; }
.stat-label { font-size: 10px; color: #ccc; }
/* 3 Columns Layout */
.main-content {
display: grid;
grid-template-columns: 25% 50% 25%;
gap: 20px;
}
/* Column Styles */
.col-card {
background: #c6c6c6;
border: 4px solid #555;
padding: 4px;
margin-bottom: 20px;
}
.col-card-inner {
background: #222;
border: 2px solid #000;
padding: 15px;
min-height: 100px;
}
.card-header {
font-size: 14px;
color: #fff;
border-bottom: 2px solid #555;
padding-bottom: 10px;
margin-bottom: 15px;
text-transform: uppercase;
}
/* Pie Chart Simulation */
.pie-chart-wrap {
position: relative;
width: 140px;
height: 140px;
margin: 20px auto;
border-radius: 50%;
background: conic-gradient(
var(--minecraft-green) 0% 50%,
var(--gold) 50% 80%,
var(--red) 80% 100%
);
border: 4px solid #000;
box-shadow: 4px 4px 0 rgba(0,0,0,0.5);
}
/* Activity Grid */
.activity-grid {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 4px;
margin-top: 15px;
}
.activity-cell {
width: 100%;
padding-top: 100%;
background: #333;
position: relative;
}
.activity-cell[data-level="1"] { background: #1a4d1a; }
.activity-cell[data-level="2"] { background: #2b7a2b; }
.activity-cell[data-level="3"] { background: #40b340; }
.activity-cell[data-level="4"] { background: #5cd65c; }
/* Achievement Wall */
.achievement-grid {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 15px;
padding: 20px;
background: url('https://www.transparenttextures.com/patterns/wood-pattern.png') #634329;
border: 8px solid #3d2919;
box-shadow: inset 0 0 20px #000;
}
.achievement-slot {
aspect-ratio: 1;
background: rgba(0,0,0,0.3);
border: 2px solid #3d2919;
display: flex;
justify-content: center;
align-items: center;
position: relative;
transition: transform 0.2s;
cursor: pointer;
}
.achievement-slot:hover {
transform: scale(1.1);
background: rgba(255,255,255,0.1);
z-index: 10;
}
.achievement-icon {
font-size: 32px;
filter: drop-shadow(2px 2px 0 #000);
}
.locked { filter: grayscale(1) brightness(0.5); }
.tooltip {
position: absolute;
bottom: 120%;
left: 50%;
transform: translateX(-50%);
background: #111;
border: 2px solid #fff;
padding: 8px;
font-size: 10px;
width: 150px;
text-align: center;
opacity: 0;
pointer-events: none;
transition: opacity 0.2s;
z-index: 20;
}
.achievement-slot:hover .tooltip { opacity: 1; }
/* Learning Path */
.path-row { margin-bottom: 15px; }
.path-label { font-size: 12px; margin-bottom: 5px; display: flex; justify-content: space-between; }
.path-track {
height: 16px;
background: #000;
border: 2px solid #555;
position: relative;
}
.path-fill {
height: 100%;
background: var(--diamond);
width: 0;
transition: width 1s ease-out;
position: relative;
}
.path-fill::after {
content: '';
position: absolute;
top: 0; right: 0; bottom: 0;
width: 4px;
background: #fff;
opacity: 0.5;
}
/* Leaderboard */
.leaderboard-item {
display: flex;
align-items: center;
padding: 8px;
border-bottom: 2px solid #333;
font-size: 12px;
}
.leaderboard-item.highlight {
background: rgba(255, 215, 0, 0.2);
border: 2px solid var(--gold);
}
.rank { width: 30px; text-align: center; font-weight: bold; }
.user { flex-grow: 1; padding-left: 10px; }
.score { color: var(--gold); }
/* Daily Tasks */
.task-item {
display: flex;
align-items: center;
padding: 8px 0;
border-bottom: 1px dashed #444;
font-size: 11px;
cursor: pointer;
}
.checkbox-custom {
width: 20px;
height: 20px;
border: 2px solid #777;
background: #000;
margin-right: 10px;
display: flex;
align-items: center;
justify-content: center;
}
.checked .checkbox-custom::after {
content: '✓';
color: var(--minecraft-green);
font-size: 14px;
}
.task-xp { margin-left: auto; color: #888; font-size: 10px; }
/* Treasure Chest */
.treasure-box {
text-align: center;
padding: 20px;
cursor: pointer;
position: relative;
}
.chest-icon {
font-size: 64px;
color: var(--gold);
filter: drop-shadow(0 0 10px var(--gold));
animation: bounce 2s infinite;
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
.chest-glow {
position: absolute;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
width: 100px; height: 100px;
background: radial-gradient(circle, rgba(255,215,0,0.4) 0%, transparent 70%);
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { transform: translate(-50%, -50%) scale(1); opacity: 0.5; }
50% { transform: translate(-50%, -50%) scale(1.5); opacity: 0.8; }
100% { transform: translate(-50%, -50%) scale(1); opacity: 0.5; }
}
/* Modal */
.modal {
display: none;
position: fixed;
top: 0; left: 0; w-full; h-full;
width: 100%; height: 100%;
background: rgba(0,0,0,0.8);
z-index: 1000;
justify-content: center;
align-items: center;
}
.modal-content {
background: #c6c6c6;
border: 4px solid #fff;
padding: 4px;
width: 400px;
text-align: center;
}
.modal-inner {
background: #222;
border: 4px solid #555;
padding: 30px;
color: #fff;
}
.reward-icon { font-size: 48px; margin: 20px 0; display: block; }
.close-btn {
background: var(--red);
border: 2px solid #fff;
color: #fff;
padding: 10px 20px;
margin-top: 20px;
cursor: pointer;
font-family: inherit;
}
</style>
</head>
<body>
<nav class="flex justify-between items-center">
<div class="nav-brand"><i class="ri-code-box-line"></i> CRAFTCODE</div>
<div class="nav-links flex">
<a href="#" class="mc-btn">Home</a>
<a href="#" class="mc-btn">Problems</a>
<a href="#" class="mc-btn active">Profile</a>
<a href="#" class="mc-btn">Shop</a>
</div>
</nav>
<div class="container">
<!-- User Hero Section -->
<div class="mc-wood-panel user-hero">
<div class="avatar-container">
<!-- Using a pixelated avatar placeholder -->
<img src="https://api.dicebear.com/7.x/pixel-art/svg?seed=Steve&backgroundColor=b6e3f4" alt="Avatar" class="avatar-img">
</div>
<div class="user-details flex flex-col justify-center">
<div class="flex items-center">
<h1>CodeMaster2024</h1>
</div>
<div style="color: #ddd; font-size: 14px; margin-bottom: 10px;">Passionate problem solver</div>
<div class="flex items-center">
<div class="badge-lv"><i class="ri-shield-star-fill"></i> Lv.25</div>
<div class="title-banner">Algorithm Expert</div>
</div>
<div class="xp-bar-container">
<div class="xp-bar-fill" id="mainXP" data-width="84.5%"></div>
<div class="xp-text">8,450 / 10,000 XP</div>
</div>
</div>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-val">128</div>
<div class="stat-label">Problems</div>
</div>
<div class="stat-card">
<div class="stat-val">456</div>
<div class="stat-label">Submits</div>
</div>
<div class="stat-card">
<div class="stat-val">85%</div>
<div class="stat-label">Acceptance</div>
</div>
<div class="stat-card">
<div class="stat-val">15🔥</div>
<div class="stat-label">Streak</div>
</div>
</div>
</div>
<!-- Three Column Content -->
<div class="main-content">
<!-- Left Column -->
<div class="left-col">
<div class="col-card">
<div class="col-card-inner">
<div class="card-header">Difficulty Stats</div>
<div class="pie-chart-wrap"></div>
<div class="flex justify-center gap-4" style="font-size: 10px; margin-top: 10px;">
<span style="color:var(--minecraft-green)">Easy</span>
<span style="color:var(--gold)">Med</span>
<span style="color:var(--red)">Hard</span>
</div>
</div>
</div>
<div class="col-card">
<div class="col-card-inner">
<div class="card-header">Activity Log</div>
<div style="font-size: 10px; color: #aaa; text-align: center; margin-bottom: 5px;">Last 30 Days</div>
<div class="activity-grid" id="activityGrid">
<!-- Generated by JS -->
</div>
</div>
</div>
<div class="col-card">
<div class="col-card-inner">
<div class="card-header">Timeline</div>
<div class="flex flex-col gap-2" style="font-size: 10px; color: #ccc;">
<div style="border-left: 2px solid #555; padding-left: 8px;">
<div style="color: var(--minecraft-green)">Solved "Two Sum"</div>
<div style="font-size: 8px; color: #888;">2 hours ago</div>
</div>
<div style="border-left: 2px solid #555; padding-left: 8px;">
<div style="color: var(--red)">Failed "Dijkstra"</div>
<div style="font-size: 8px; color: #888;">5 hours ago</div>
</div>
<div style="border-left: 2px solid #555; padding-left: 8px;">
<div style="color: var(--gold)">Badge Unlocked</div>
<div style="font-size: 8px; color: #888;">1 day ago</div>
</div>
</div>
</div>
</div>
</div>
<!-- Center Column -->
<div class="center-col">
<div class="col-card">
<div class="col-card-inner">
<div class="card-header">Achievement Wall</div>
<div class="achievement-grid">
<!-- 15 Slots: 5x3 -->
<!-- Row 1 -->
<div class="achievement-slot">
<i class="ri-sword-fill achievement-icon" style="color: #cd7f32;"></i>
<div class="tooltip">
<div style="color: var(--gold)">Novice Slayer</div>
<div style="margin-top:4px; color:#aaa">Solve 10 Easy</div>
<div style="color: var(--minecraft-green)">+50 XP</div>
</div>
</div>
<div class="achievement-slot">
<i class="ri-sword-fill achievement-icon" style="color: #eee;"></i>
<div class="tooltip">
<div style="color: var(--gold)">Iron Will</div>
<div style="margin-top:4px; color:#aaa">Solve 50 Medium</div>
<div style="color: var(--minecraft-green)">+200 XP</div>
</div>
</div>
<div class="achievement-slot locked">
<i class="ri-sword-fill achievement-icon" style="color: var(--diamond);"></i>
<div class="tooltip">LOCKED: Diamond Blade</div>
</div>
<div class="achievement-slot">
<i class="ri-trophy-fill achievement-icon" style="color: var(--gold);"></i>
<div class="tooltip">
<div style="color: var(--gold)">Champion</div>
<div style="margin-top:4px; color:#aaa">Win a Contest</div>
<div style="color: var(--minecraft-green)">+1000 XP</div>
</div>
</div>
<div class="achievement-slot">
<i class="ri-book-3-fill achievement-icon" style="color: #a0522d;"></i>
<div class="tooltip">
<div style="color: var(--gold)">Scholar</div>
<div style="margin-top:4px; color:#aaa">Read 100 Articles</div>
<div style="color: var(--minecraft-green)">+100 XP</div>
</div>
</div>
<!-- Row 2 -->
<div class="achievement-slot">
<i class="ri-flask-fill achievement-icon" style="color: #ff69b4;"></i>
<div class="tooltip">
<div style="color: var(--gold)">Alchemist</div>
<div style="margin-top:4px; color:#aaa">Optimize 10 times</div>
<div style="color: var(--minecraft-green)">+150 XP</div>
</div>
</div>
<div class="achievement-slot locked">
<i class="ri-fire-fill achievement-icon" style="color: orange;"></i>
<div class="tooltip">LOCKED: Inferno</div>
</div>
<div class="achievement-slot">
<i class="ri-flashlight-fill achievement-icon" style="color: yellow;"></i>
<div class="tooltip">
<div style="color: var(--gold)">Speedster</div>
<div style="margin-top:4px; color:#aaa">Solve in 5 mins</div>
<div style="color: var(--minecraft-green)">+50 XP</div>
</div>
</div>
<div class="achievement-slot locked">
<i class="ri-star-fill achievement-icon" style="color: cyan;"></i>
<div class="tooltip">LOCKED: Star Player</div>
</div>
<div class="achievement-slot locked">
<i class="ri-map-pin-user-fill achievement-icon" style="color: var(--minecraft-green);"></i>
<div class="tooltip">LOCKED: Explorer</div>
</div>
<!-- Row 3 -->
<div class="achievement-slot locked">
<i class="ri-shield-fill achievement-icon" style="color: gray;"></i>
<div class="tooltip">LOCKED: Guardian</div>
</div>
<div class="achievement-slot locked">
<i class="ri-hammer-fill achievement-icon" style="color: #ddd;"></i>
<div class="tooltip">LOCKED: Builder</div>
</div>
<div class="achievement-slot locked">
<i class="ri-bug-fill achievement-icon" style="color: var(--red);"></i>
<div class="tooltip">LOCKED: Bug Hunter</div>
</div>
<div class="achievement-slot locked">
<i class="ri-compass-3-fill achievement-icon" style="color: gold;"></i>
<div class="tooltip">LOCKED: Navigator</div>
</div>
<div class="achievement-slot locked">
<i class="ri-vip-crown-fill achievement-icon" style="color: var(--purple);"></i>
<div class="tooltip">LOCKED: King</div>
</div>
</div>
</div>
</div>
<div class="col-card">
<div class="col-card-inner">
<div class="card-header">Learning Path</div>
<div class="path-row">
<div class="path-label">
<span>Basics</span>
<span>100%</span>
</div>
<div class="path-track"><div class="path-fill" data-width="100%"></div></div>
</div>
<div class="path-row">
<div class="path-label">
<span>Algorithms</span>
<span>75%</span>
</div>
<div class="path-track"><div class="path-fill" data-width="75%"></div></div>
</div>
<div class="path-row">
<div class="path-label">
<span>Data Structures</span>
<span>60%</span>
</div>
<div class="path-track"><div class="path-fill" data-width="60%"></div></div>
</div>
<div class="path-row">
<div class="path-label">
<span>Advanced DP</span>
<span>40%</span>
</div>
<div class="path-track"><div class="path-fill" data-width="40%"></div></div>
</div>
<div class="path-row">
<div class="path-label">
<span>Contest Prep</span>
<span>20%</span>
</div>
<div class="path-track"><div class="path-fill" data-width="20%"></div></div>
</div>
</div>
</div>
</div>
<!-- Right Column -->
<div class="right-col">
<div class="col-card">
<div class="col-card-inner">
<div class="card-header">Leaderboard</div>
<div class="leaderboard-item">
<div class="rank">1</div>
<div class="user">Notch <i class="ri-vip-crown-fill" style="color:gold"></i></div>
<div class="score">99k</div>
</div>
<div class="leaderboard-item">
<div class="rank">2</div>
<div class="user">Jeb_ <i class="ri-medal-fill" style="color:silver"></i></div>
<div class="score">85k</div>
</div>
<div class="leaderboard-item">
<div class="rank">3</div>
<div class="user">Alex <i class="ri-medal-fill" style="color:#cd7f32"></i></div>
<div class="score">72k</div>
</div>
<div class="leaderboard-item">
<div class="rank">4</div>
<div class="user">Herobrine</div>
<div class="score">66k</div>
</div>
<div class="leaderboard-item highlight">
<div class="rank">5</div>
<div class="user">CodeMaster</div>
<div class="score">50k</div>
</div>
</div>
</div>
<div class="col-card">
<div class="col-card-inner">
<div class="card-header">Daily Quests</div>
<div class="task-item checked">
<div class="checkbox-custom"></div>
<div>Login</div>
<div class="task-xp">10XP</div>
</div>
<div class="task-item checked">
<div class="checkbox-custom"></div>
<div>Solve 1 Easy</div>
<div class="task-xp">20XP</div>
</div>
<div class="task-item checked">
<div class="checkbox-custom"></div>
<div>Review Code</div>
<div class="task-xp">15XP</div>
</div>
<div class="task-item">
<div class="checkbox-custom"></div>
<div>Solve 1 Hard</div>
<div class="task-xp">100XP</div>
</div>
<div class="task-item">
<div class="checkbox-custom"></div>
<div>Forum Post</div>
<div class="task-xp">30XP</div>
</div>
</div>
</div>
<div class="col-card">
<div class="col-card-inner" style="display:flex; justify-content:center; align-items:center;">
<div class="treasure-box" id="chestBtn">
<div class="chest-glow"></div>
<i class="ri-treasure-map-line chest-icon"></i>
<div style="margin-top:10px; font-size: 10px; color: var(--gold);">Daily Reward</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Reward Modal -->
<div class="modal" id="rewardModal">
<div class="modal-content">
<div class="modal-inner">
<h2>CHEST OPENED!</h2>
<i class="ri-diamond-fill reward-icon" style="color: var(--diamond);"></i>
<p>You found <span style="color:var(--gold); font-size: 20px;">500 XP</span></p>
<p style="font-size: 10px; color: #aaa; margin-top: 10px;">Rare Item Found: Diamond Boots</p>
<button class="close-btn" id="closeModal">CLAIM</button>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
// Animate XP Bar
const xpBar = document.getElementById('mainXP');
setTimeout(() => {
xpBar.style.width = xpBar.getAttribute('data-width');
}, 500);
// Animate Learning Paths
const paths = document.querySelectorAll('.path-fill');
paths.forEach(path => {
setTimeout(() => {
path.style.width = path.getAttribute('data-width');
}, 800);
});
// Activity Grid Generator
const activityGrid = document.getElementById('activityGrid');
for(let i=0; i<30; i++) {
const cell = document.createElement('div');
cell.className = 'activity-cell';
// Random activity level 0-4
const level = Math.random() > 0.3 ? Math.floor(Math.random() * 5) : 0;
if(level > 0) cell.setAttribute('data-level', level);
// Add tooltip on hover
cell.title = `Day ${30-i}: ${level*2} submissions`;
activityGrid.appendChild(cell);
}
// Task Checkbox Logic
const tasks = document.querySelectorAll('.task-item');
tasks.forEach(task => {
task.addEventListener('click', () => {
task.classList.toggle('checked');
// Add simple sound effect logic here if audio was allowed
});
});
// Treasure Chest Logic
const chestBtn = document.getElementById('chestBtn');
const modal = document.getElementById('rewardModal');
const closeBtn = document.getElementById('closeModal');
chestBtn.addEventListener('click', () => {
modal.style.display = 'flex';
});
closeBtn.addEventListener('click', () => {
modal.style.display = 'none';
});
// Close modal on outside click
modal.addEventListener('click', (e) => {
if(e.target === modal) {
modal.style.display = 'none';
}
});
});
</script>
</body>
</html>

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 17 MiB

之后

宽度:  |  高度:  |  大小: 0 B

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 14 MiB

之后

宽度:  |  高度:  |  大小: 0 B

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 15 MiB

之后

宽度:  |  高度:  |  大小: 0 B

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 18 MiB

之后

宽度:  |  高度:  |  大小: 0 B

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 16 MiB

之后

宽度:  |  高度:  |  大小: 0 B

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 16 MiB

之后

宽度:  |  高度:  |  大小: 0 B

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 17 MiB

之后

宽度:  |  高度:  |  大小: 0 B

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 5.8 MiB

之后

宽度:  |  高度:  |  大小: 0 B

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 1.6 MiB

之后

宽度:  |  高度:  |  大小: 0 B

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 1.3 MiB

之后

宽度:  |  高度:  |  大小: 0 B

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 1.4 MiB

之后

宽度:  |  高度:  |  大小: 0 B

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 6.2 MiB

之后

宽度:  |  高度:  |  大小: 0 B

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 16 MiB

之后

宽度:  |  高度:  |  大小: 0 B

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 16 MiB

之后

宽度:  |  高度:  |  大小: 0 B

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 17 MiB

之后

宽度:  |  高度:  |  大小: 0 B

查看文件

@@ -8,6 +8,7 @@ import json
import os import os
import re import re
import sys import sys
import time
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any, Dict, List, Optional from typing import Any, Dict, List, Optional
@@ -88,9 +89,17 @@ def build_fallback_feedback(payload: Dict[str, Any], llm_error: str = "") -> Llm
) )
lines: List[str] = [] lines: List[str] = []
lines.append("### 评测结论") lines.append("### 总体评语")
lines.append(f"- 本次状态:**{status}**,分数:**{score}**。") lines.append(f"- 本次状态:**{status}**,分数:**{score}**。")
lines.append(f"- 思路评价:{thought}") lines.append(f"- {thought}")
lines.append("")
lines.append("### 代码逐段讲解")
lines.append("- 由于 LLM 分析不可用,暂时无法提供代码逐段讲解。请仔细检查代码逻辑,确保输入输出格式正确。")
lines.append("")
lines.append("### 知识点提示")
lines.append("- 强项:基础实现与调试流程。")
lines.append("- 待加强:边界构造、类型一致性、赛场环境兼容性。")
lines.append("- 请对照 CSP-J/S 大纲,确认所涉及的算法知识点是否已掌握。")
lines.append("") lines.append("")
lines.append("### 福建 CSP-J/S 规范检查C++14") lines.append("### 福建 CSP-J/S 规范检查C++14")
for tip in risk_tips: for tip in risk_tips:
@@ -100,16 +109,12 @@ def build_fallback_feedback(payload: Dict[str, Any], llm_error: str = "") -> Llm
if runtime_log.strip(): if runtime_log.strip():
lines.append("- 运行日志有输出,建议重点检查边界输入与数组越界风险。") lines.append("- 运行日志有输出,建议重点检查边界输入与数组越界风险。")
lines.append("") lines.append("")
lines.append("### 修改建议(可执行)") lines.append("### 改进建议")
lines.append("- 按“先编译通过→再保证正确→最后做优化”的顺序迭代。") lines.append("- 按“先编译通过→再保证正确→最后做优化”的顺序迭代。")
lines.append("- `long long` 读写统一 `%lld`;不要使用 `%I64d`。") lines.append("- `long long` 读写统一 `%lld`;不要使用 `%I64d`。")
lines.append("- 清理 signed/unsigned 警告,降低不同编译器行为差异。") lines.append("- 清理 signed/unsigned 警告,降低不同编译器行为差异。")
lines.append("- 确保 `int main()` 且 `return 0;`。") lines.append("- 确保 `int main()` 且 `return 0;`。")
lines.append("") lines.append("")
lines.append("### 知识点评测")
lines.append("- 强项:基础实现与调试流程。")
lines.append("- 待加强:边界构造、类型一致性、赛场环境兼容性。")
lines.append("")
lines.append("### 推荐外链资料") lines.append("### 推荐外链资料")
for item in DEFAULT_LINKS: for item in DEFAULT_LINKS:
lines.append(f"- [{item['title']}]({item['url']})") lines.append(f"- [{item['title']}]({item['url']})")
@@ -162,17 +167,25 @@ def call_llm(payload: Dict[str, Any]) -> LlmResult:
raise RuntimeError("missing OI_LLM_API_URL") raise RuntimeError("missing OI_LLM_API_URL")
system_prompt = ( system_prompt = (
"你是福建省 CSP-J/S 代码规范与评测老师。" "你是福建省 CSP-J/S 代码规范与评测老师,也是一位经验丰富的算法竞赛教练"
"请严格按 C++14 旧 GCC 环境给建议,重点指出会导致 CE/RE/爆零的风险。" "请严格按 C++14 旧 GCC 环境给建议,重点指出会导致 CE/RE/爆零的风险。"
"你的评测需要覆盖以下维度:\n"
"1. 总体评语对代码质量和解题思路的综合评价2-3句话\n"
"2. 代码逐段讲解:解释代码的关键逻辑和实现思路\n"
"3. 知识点提示:涉及的算法和数据结构知识点,与 CSP-J/S 大纲的对应关系\n"
"4. 福建 CSP-J/S 规范检查C++14指出不符合规范的地方\n"
"5. 改进建议:具体可操作的优化方向\n"
"6. 推荐外链资料:相关学习资源\n"
"输出 JSON,不要输出其他文字。" "输出 JSON,不要输出其他文字。"
) )
user_prompt = { user_prompt = {
"task": "分析这份提交并给出改进建议", "task": "分析这份提交并给出详细点评",
"required_sections": [ "required_sections": [
"评测结论", "总体评语",
"代码逐段讲解",
"知识点提示",
"福建 CSP-J/S 规范检查C++14", "福建 CSP-J/S 规范检查C++14",
"改建议", "建议",
"知识点评测",
"推荐外链资料", "推荐外链资料",
], ],
"submission": payload, "submission": payload,
@@ -197,7 +210,37 @@ def call_llm(payload: Dict[str, Any]) -> LlmResult:
], ],
} }
max_retries = 5
last_exc: Optional[Exception] = None
resp = None
for attempt in range(1, max_retries + 1):
try:
resp = requests.post(api_url, headers=headers, json=body, timeout=50) resp = requests.post(api_url, headers=headers, json=body, timeout=50)
if resp.status_code < 500:
resp.raise_for_status()
break
# 5xx — retry
last_exc = requests.exceptions.HTTPError(
f"HTTP {resp.status_code}", response=resp
)
print(
f"[feedback] LLM returned {resp.status_code}, "
f"retry {attempt}/{max_retries}",
file=sys.stderr,
)
except (requests.exceptions.ConnectionError,
requests.exceptions.Timeout) as exc:
last_exc = exc
print(
f"[feedback] LLM request failed ({exc}), "
f"retry {attempt}/{max_retries}",
file=sys.stderr,
)
if attempt < max_retries:
time.sleep(min(2 ** attempt, 16))
else:
raise last_exc or RuntimeError("LLM request failed after retries")
resp.raise_for_status() resp.raise_for_status()
data = resp.json() data = resp.json()