Files
VFXdemo/admin.html
2026-04-02 11:15:21 +08:00

419 lines
11 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>管理后台 — Shadervault</title>
<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=Archivo:wght@600;700&family=DM+Sans:ital,opsz,wght@0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700&display=swap"
rel="stylesheet"
/>
<style>
:root {
color-scheme: dark;
--bg-deep: #05060c;
--border: rgba(118, 138, 215, 0.28);
--accent: #8fa8ff;
--accent-dim: rgba(143, 168, 255, 0.14);
--text: #eef1fb;
--text-secondary: #a4adc8;
--text-muted: #6f7a96;
--radius-md: 14px;
--radius-lg: 18px;
--font-ui: "DM Sans", ui-sans-serif, system-ui, sans-serif;
--font-display: "Archivo", var(--font-ui);
}
* {
box-sizing: border-box;
}
body {
margin: 0;
min-height: 100vh;
background-color: var(--bg-deep);
background-image:
radial-gradient(ellipse 100% 80% at 50% -20%, rgba(80, 100, 200, 0.18), transparent 50%),
radial-gradient(ellipse 60% 40% at 100% 50%, rgba(50, 70, 140, 0.08), transparent 45%);
color: var(--text);
font-family: var(--font-ui);
font-size: 15px;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
}
.wrap {
max-width: 920px;
margin: 0 auto;
padding: 28px 20px 48px;
}
.page-head {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
justify-content: space-between;
gap: 16px;
margin-bottom: 28px;
}
.page-head h1 {
margin: 0 0 6px;
font-family: var(--font-display);
font-size: 1.65rem;
font-weight: 700;
letter-spacing: -0.02em;
}
.page-head p {
margin: 0;
font-size: 0.875rem;
color: var(--text-muted);
max-width: 42ch;
}
.back-link {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 10px 16px;
border-radius: 999px;
border: 1px solid var(--border);
background: rgba(255, 255, 255, 0.05);
color: var(--text-secondary);
font-size: 0.8125rem;
font-weight: 600;
text-decoration: none;
transition: color 0.2s ease, border-color 0.2s ease, background 0.2s ease;
}
.back-link svg {
width: 16px;
height: 16px;
opacity: 0.8;
}
.back-link:hover {
color: var(--text);
border-color: rgba(148, 170, 255, 0.45);
background: var(--accent-dim);
}
.back-link:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
.card {
border: 1px solid var(--border);
background: linear-gradient(165deg, rgba(20, 24, 42, 0.75) 0%, rgba(10, 12, 22, 0.92) 100%);
border-radius: var(--radius-lg);
padding: 20px 22px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.35);
}
.card h2 {
margin: 0 0 16px;
font-family: var(--font-display);
font-size: 1rem;
font-weight: 600;
color: var(--text-secondary);
letter-spacing: 0.02em;
}
.field-label {
display: block;
font-size: 0.75rem;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--text-muted);
margin-bottom: 6px;
}
.grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
.field-full {
grid-column: 1 / -1;
}
input,
textarea {
width: 100%;
padding: 11px 14px;
border-radius: 12px;
border: 1px solid var(--border);
background: rgba(0, 0, 0, 0.25);
color: var(--text);
font-family: inherit;
font-size: 0.9375rem;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
input::placeholder,
textarea::placeholder {
color: var(--text-muted);
}
input:hover,
textarea:hover {
border-color: rgba(148, 170, 255, 0.35);
}
input:focus,
textarea:focus {
outline: none;
border-color: rgba(148, 170, 255, 0.55);
box-shadow: 0 0 0 3px var(--accent-dim);
}
textarea {
min-height: 260px;
resize: vertical;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Courier New", monospace;
font-size: 0.8125rem;
line-height: 1.5;
}
.actions {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 18px;
}
.btn {
font-family: inherit;
font-size: 0.8125rem;
font-weight: 600;
padding: 10px 20px;
border-radius: 12px;
cursor: pointer;
border: 1px solid transparent;
transition: background 0.2s ease, border-color 0.2s ease, transform 0.15s ease;
}
.btn:active {
transform: scale(0.98);
}
.btn:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
.btn-primary {
background: linear-gradient(180deg, rgba(120, 140, 220, 0.45) 0%, rgba(80, 100, 180, 0.35) 100%);
border-color: rgba(148, 170, 255, 0.45);
color: var(--text);
}
.btn-primary:hover {
background: linear-gradient(180deg, rgba(130, 150, 230, 0.55) 0%, rgba(90, 110, 190, 0.42) 100%);
border-color: rgba(180, 195, 255, 0.55);
}
.btn-secondary {
background: rgba(255, 255, 255, 0.06);
border-color: var(--border);
color: var(--text-secondary);
}
.btn-secondary:hover {
background: rgba(255, 255, 255, 0.1);
color: var(--text);
border-color: rgba(148, 170, 255, 0.35);
}
.list {
margin-top: 20px;
}
.list-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
padding: 0 2px;
}
.list-header h2 {
margin: 0;
font-family: var(--font-display);
font-size: 1rem;
font-weight: 600;
color: var(--text-secondary);
}
.list-count {
font-size: 0.75rem;
font-weight: 600;
color: var(--text-muted);
letter-spacing: 0.06em;
text-transform: uppercase;
}
.item-actions {
display: flex;
flex-shrink: 0;
align-items: center;
gap: 8px;
}
.btn-dl {
padding: 8px 14px;
font-size: 0.75rem;
text-decoration: none;
background: rgba(255, 255, 255, 0.06);
border: 1px solid var(--border);
border-radius: 10px;
color: var(--text-secondary);
font-weight: 600;
transition: color 0.2s ease, border-color 0.2s ease, background 0.2s ease;
}
.btn-dl:hover {
color: var(--text);
border-color: rgba(148, 170, 255, 0.4);
background: var(--accent-dim);
}
.btn-dl:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
.list .item {
display: flex;
align-items: center;
justify-content: space-between;
gap: 14px;
padding: 14px 16px;
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.06);
background: rgba(0, 0, 0, 0.2);
margin-bottom: 8px;
transition: border-color 0.2s ease, background 0.2s ease;
}
.list .item:hover {
border-color: rgba(148, 170, 255, 0.22);
background: rgba(255, 255, 255, 0.03);
}
.list .item:last-child {
margin-bottom: 0;
}
.item-main strong {
display: block;
font-family: var(--font-display);
font-size: 0.9375rem;
font-weight: 600;
color: var(--text);
margin-bottom: 4px;
}
.item-meta {
font-size: 0.75rem;
color: var(--text-muted);
font-family: ui-monospace, monospace;
word-break: break-all;
}
.btn-danger {
flex-shrink: 0;
padding: 8px 14px;
font-size: 0.75rem;
background: rgba(220, 80, 90, 0.12);
border-color: rgba(255, 120, 130, 0.35);
color: #ffb4b8;
}
.btn-danger:hover {
background: rgba(220, 80, 90, 0.22);
border-color: rgba(255, 140, 150, 0.5);
color: #ffe0e2;
}
.empty-state {
text-align: center;
padding: 40px 24px;
border-radius: var(--radius-md);
border: 1px dashed var(--border);
color: var(--text-muted);
font-size: 0.875rem;
}
@media (max-width: 640px) {
.grid {
grid-template-columns: 1fr;
}
.list .item {
flex-direction: column;
align-items: stretch;
}
.item-actions {
justify-content: flex-end;
}
}
</style>
</head>
<body>
<div class="wrap">
<header class="page-head">
<div>
<h1>Shader 管理</h1>
<p>保存后同步展示页并生成缩略图。</p>
</div>
<a class="back-link" href="./index.html">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path d="m15 18-6-6 6-6" />
</svg>
返回展示页
</a>
</header>
<section class="card" aria-labelledby="editor-heading">
<h2 id="editor-heading">新建 / 编辑</h2>
<div class="grid">
<div>
<label class="field-label" for="name-input">名称</label>
<input id="name-input" name="name" placeholder="例如 Aurora Noise" autocomplete="off" />
</div>
<div>
<label class="field-label" for="author-input">作者</label>
<input id="author-input" name="author" placeholder="可选" autocomplete="off" />
</div>
<div class="field-full">
<label class="field-label" for="code-input">GLSL 代码</label>
<textarea id="code-input" name="code" placeholder="粘贴 GLSL须包含 mainImage(out vec4 fragColor, in vec2 fragCoord)"></textarea>
</div>
</div>
<div class="actions">
<button type="button" class="btn btn-primary" id="save-btn">保存到列表</button>
<button type="button" class="btn btn-secondary" id="example-btn">填入示例</button>
</div>
</section>
<section class="list card" id="list-section" aria-labelledby="list-heading">
<div class="list-header">
<h2 id="list-heading">已保存</h2>
<span class="list-count" id="list-count" aria-live="polite"></span>
</div>
<div id="list" class="list-body"></div>
</section>
</div>
<script src="./thumb-renderer.js"></script>
<script src="./admin.js"></script>
</body>
</html>