597 lines
17 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>封面工作室 · 语言驱动设计系统</title>
<style>
:root {
--bg: #0a0e1a;
--panel: #11162a;
--panel-hover: #161d35;
--border: #1e2640;
--border-h: #2d3655;
--text: #c5cee0;
--text-dim: #6b7280;
--accent: #3b82f6;
--accent-glow: #60a5fa;
--green: #10b981;
--gold: #f59e0b;
--purple: #8b5cf6;
--code-bg: #0d1117;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: var(--bg);
color: var(--text);
font-family: -apple-system, 'SF Pro Display', 'PingFang SC', sans-serif;
min-height: 100vh;
line-height: 1.6;
}
/* Header */
header {
text-align: center;
padding: 40px 20px 30px;
border-bottom: 1px solid var(--border);
}
header h1 {
font-size: 32px;
font-weight: 700;
background: linear-gradient(90deg, var(--accent), var(--accent-glow));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin-bottom: 8px;
}
header p {
font-size: 14px;
color: var(--text-dim);
}
/* Main Layout */
.main {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0;
height: calc(100vh - 140px);
}
@media (max-width: 900px) {
.main { grid-template-columns: 1fr; height: auto; }
}
/* Left Panel - Design Logic */
.left {
background: var(--panel);
border-right: 1px solid var(--border);
display: flex;
flex-direction: column;
}
.left-header {
padding: 16px 20px;
border-bottom: 1px solid var(--border);
display: flex;
justify-content: space-between;
align-items: center;
}
.left-header .title {
font-size: 13px;
font-weight: 600;
letter-spacing: 1px;
color: var(--text-dim);
}
.left-header .status {
font-size: 12px;
color: var(--green);
display: flex;
align-items: center;
gap: 6px;
}
.left-header .status::before {
content: '';
width: 6px;
height: 6px;
background: var(--green);
border-radius: 50%;
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
}
/* Code Editor Style */
.code-area {
flex: 1;
overflow-y: auto;
padding: 20px;
font-family: 'SF Mono', 'Fira Code', monospace;
font-size: 13px;
}
.code-block {
margin-bottom: 20px;
}
.code-block .line-num {
color: var(--text-dim);
display: inline-block;
width: 30px;
user-select: none;
}
.code-block .comment {
color: #6b7280;
}
.code-block .keyword {
color: var(--purple);
}
.code-block .string {
color: var(--green);
}
.code-block .function {
color: var(--accent);
}
.code-block .number {
color: var(--gold);
}
/* Input Section */
.input-section {
padding: 20px;
border-top: 1px solid var(--border);
background: var(--code-bg);
}
.input-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
margin-bottom: 12px;
}
.input-row select, .input-row input {
padding: 10px 14px;
background: var(--panel);
border: 1px solid var(--border);
border-radius: 6px;
color: var(--text);
font-size: 13px;
outline: none;
}
.input-row select:focus, .input-row input:focus {
border-color: var(--accent);
}
textarea {
width: 100%;
padding: 14px;
background: var(--panel);
border: 1px solid var(--border);
border-radius: 6px;
color: var(--text);
font-size: 14px;
line-height: 1.7;
resize: vertical;
min-height: 120px;
outline: none;
font-family: inherit;
}
textarea:focus { border-color: var(--accent); }
.gen-btn {
width: 100%;
padding: 14px;
background: var(--accent);
color: white;
border: none;
border-radius: 6px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
margin-top: 12px;
transition: all 0.2s;
}
.gen-btn:hover {
background: var(--accent-glow);
transform: translateY(-1px);
}
.gen-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* Right Panel - Preview */
.right {
background: var(--bg);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px;
position: relative;
}
.preview-container {
position: relative;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 20px 60px rgba(0,0,0,0.5);
border: 1px solid var(--border);
}
.preview-img {
max-width: 100%;
max-height: 70vh;
display: block;
}
/* ID Watermark on Image */
.id-watermark {
position: absolute;
bottom: 16px;
right: 16px;
background: rgba(0,0,0,0.7);
padding: 8px 14px;
border-radius: 20px;
font-size: 12px;
font-family: 'SF Mono', monospace;
color: var(--accent-glow);
border: 1px solid var(--accent);
letter-spacing: 1px;
}
/* Design ID Badge */
.design-id {
position: absolute;
top: 20px;
left: 20px;
background: var(--panel);
padding: 10px 16px;
border-radius: 8px;
border: 1px solid var(--border);
}
.design-id .label {
font-size: 11px;
color: var(--text-dim);
letter-spacing: 1px;
}
.design-id .value {
font-size: 14px;
font-weight: 600;
color: var(--accent);
font-family: 'SF Mono', monospace;
}
/* Empty State */
.empty-state {
text-align: center;
color: var(--text-dim);
}
.empty-state .icon {
font-size: 64px;
margin-bottom: 20px;
opacity: 0.3;
}
.empty-state p {
font-size: 15px;
line-height: 2;
}
/* Download */
.download-bar {
margin-top: 24px;
display: none;
}
.download-bar.show {
display: flex;
gap: 12px;
}
.dl-btn {
padding: 12px 24px;
background: var(--panel);
border: 1px solid var(--border);
border-radius: 6px;
color: var(--text);
text-decoration: none;
font-size: 14px;
transition: all 0.2s;
}
.dl-btn:hover {
border-color: var(--accent);
color: var(--accent);
}
.dl-btn.primary {
background: var(--accent);
color: white;
border-color: var(--accent);
}
/* Intent Chain Display */
.intent-chain {
position: absolute;
bottom: 20px;
left: 20px;
right: 20px;
background: var(--panel);
border: 1px solid var(--border);
border-radius: 8px;
padding: 16px;
display: none;
}
.intent-chain.show {
display: block;
}
.intent-chain .title {
font-size: 12px;
color: var(--text-dim);
margin-bottom: 12px;
letter-spacing: 1px;
}
.chain-step {
display: flex;
gap: 12px;
padding: 8px 0;
font-size: 13px;
border-bottom: 1px solid var(--border);
}
.chain-step:last-child {
border-bottom: none;
}
.chain-step .num {
color: var(--text-dim);
font-family: 'SF Mono', monospace;
min-width: 30px;
}
.chain-step .action {
color: var(--accent);
}
.chain-step .detail {
color: var(--text-dim);
flex: 1;
}
.loading {
display: none;
text-align: center;
padding: 60px;
}
.loading.show {
display: block;
}
.loading-dots span {
display: inline-block;
width: 8px;
height: 8px;
background: var(--accent);
border-radius: 50%;
margin: 0 4px;
animation: bounce 1.4s infinite ease-in-out both;
}
.loading-dots span:nth-child(1) { animation-delay: -0.32s; }
.loading-dots span:nth-child(2) { animation-delay: -0.16s; }
@keyframes bounce {
0%, 80%, 100% { transform: scale(0); }
40% { transform: scale(1); }
}
</style>
</head>
<body>
<header>
<h1>封面工作室</h1>
<p>语言驱动设计系统 · 你的意图,系统只是执行者</p>
</header>
<div class="main">
<!-- Left: Design Logic -->
<div class="left">
<div class="left-header">
<span class="title">DESIGN LOGIC // 设计逻辑</span>
<span class="status">SYSTEM READY</span>
</div>
<div class="code-area" id="codeArea">
<div class="code-block">
<span class="line-num">1</span><span class="comment">// 等待用户输入自然语言意图...</span><br>
<span class="line-num">2</span><span class="keyword">await</span> <span class="function">userInput</span>();<br>
<span class="line-num">3</span><br>
<span class="line-num">4</span><span class="comment">// 系统将把语言翻译成设计逻辑</span><br>
<span class="line-num">5</span><span class="comment">// 每一步都可追溯、可验证、可署名</span>
</div>
</div>
<div class="input-section">
<div class="input-row">
<select id="template">
<option value="xiaohongshu">📕 小红书封面 (1080×1440)</option>
</select>
<select id="preset">
<option value="tech">🔷 科技蓝</option>
<option value="warm">🟠 奶油暖调</option>
<option value="minimal">⬜ 极简黑白</option>
<option value="rose">🟣 玫瑰粉调</option>
<option value="green">🟢 文艺绿植</option>
</select>
</div>
<div class="input-row">
<select id="layout">
<option value="default">标准排版</option>
<option value="hero">大标题封面</option>
<option value="quote">金句居中</option>
</select>
<input type="text" id="tag" placeholder="标签: 教程 · 干货">
</div>
<input type="text" id="title" placeholder="标题" style="width:100%;margin-bottom:12px;padding:12px 14px;background:var(--panel);border:1px solid var(--border);border-radius:6px;color:var(--text);">
<textarea id="content" placeholder="输入正文内容...
支持格式:
- 列表项用 - 开头
## 小标题
**加粗文字**"></textarea>
<button class="gen-btn" id="genBtn" onclick="generate()">执行设计逻辑 → 生成封面</button>
</div>
</div>
<!-- Right: Preview -->
<div class="right">
<div class="design-id" id="designId" style="display:none;">
<div class="label">DESIGN ID</div>
<div class="value" id="idValue">---</div>
</div>
<div class="empty-state" id="empty">
<div class="icon">🎨</div>
<p>在左侧输入设计意图<br>系统将翻译成可执行逻辑<br>生成带编号的封面</p>
</div>
<div class="loading" id="loading">
<div class="loading-dots">
<span></span><span></span><span></span>
</div>
<p style="margin-top:20px;color:var(--text-dim);">编译设计意图...</p>
</div>
<div class="preview-container" id="previewBox" style="display:none;">
<img class="preview-img" id="previewImg" alt="封面">
<div class="id-watermark" id="watermark">---</div>
</div>
<div class="download-bar" id="dlBar">
<a href="#" class="dl-btn primary" id="dlLink" download>下载 PNG</a>
<button class="dl-btn" onclick="copyId()">复制设计编号</button>
</div>
<div class="intent-chain" id="chain">
<div class="title">执行日志 // EXECUTION LOG</div>
<div id="chainContent"></div>
</div>
</div>
</div>
<script>
let currentId = '';
function generate() {
const title = document.getElementById('title').value.trim();
const content = document.getElementById('content').value.trim();
const preset = document.getElementById('preset').value;
const layout = document.getElementById('layout').value;
const tag = document.getElementById('tag').value.trim();
if (!title && !content) {
alert('请输入标题或内容');
return;
}
// Show loading
document.getElementById('empty').style.display = 'none';
document.getElementById('previewBox').style.display = 'none';
document.getElementById('dlBar').style.display = 'none';
document.getElementById('chain').style.display = 'none';
document.getElementById('designId').style.display = 'none';
document.getElementById('loading').classList.add('show');
document.getElementById('genBtn').disabled = true;
// Generate design ID
currentId = 'D-' + Date.now().toString(36).toUpperCase();
// Update code area with design logic
const codeArea = document.getElementById('codeArea');
codeArea.innerHTML = `
<div class="code-block">
<span class="line-num">1</span><span class="comment">// ========================================</span><br>
<span class="line-num">2</span><span class="comment">// DESIGN ID: ${currentId}</span><br>
<span class="line-num">3</span><span class="comment">// OWNER: 用户</span><br>
<span class="line-num">4</span><span class="comment">// ========================================</span><br>
<span class="line-num">5</span><br>
<span class="line-num">6</span><span class="keyword">const</span> <span class="function">userIntent</span> = {<br>
<span class="line-num">7</span> title: <span class="string">"${title || '未命名'}"</span>,<br>
<span class="line-num">8</span> content: <span class="string">"${content.substring(0, 50)}${content.length > 50 ? '...' : ''}"</span>,<br>
<span class="line-num">9</span> mood: <span class="string">"${preset === 'tech' ? '科技感' : preset === 'warm' ? '温暖' : preset === 'rose' ? '文艺' : '极简'}"</span><br>
<span class="line-num">10</span>};<br>
<span class="line-num">11</span><br>
<span class="line-num">12</span><span class="comment">// STEP 1: 分析意图</span><br>
<span class="line-num">13</span><span class="keyword">const</span> analysis = <span class="function">analyzeIntent</span>(userIntent);<br>
<span class="line-num">14</span><span class="comment">// → 内容类型: ${content.includes('-') ? '列表/教程' : '段落'}</span><br>
<span class="line-num">15</span><span class="comment">// → 推荐布局: ${layout === 'hero' ? '大标题封面' : layout === 'quote' ? '金句居中' : '标准排版'}</span><br>
<span class="line-num">16</span><br>
<span class="line-num">17</span><span class="comment">// STEP 2: 选择配色方案</span><br>
<span class="line-num">18</span><span class="keyword">const</span> colorScheme = <span class="string">"${preset}"</span>;<br>
<span class="line-num">19</span><span class="comment">// → 匹配情绪: ${preset === 'tech' ? '冷静、专业' : preset === 'warm' ? '温暖、亲和' : '简约、高级'}</span><br>
<span class="line-num">20</span><br>
<span class="line-num">21</span><span class="comment">// STEP 3: 渲染引擎执行</span><br>
<span class="line-num">22</span><span class="keyword">await</span> <span class="function">render</span>({<br>
<span class="line-num">23</span> template: <span class="string">"xiaohongshu"</span>,<br>
<span class="line-num">24</span> size: <span class="string">"1080x1440"</span>,<br>
<span class="line-num">25</span> style: colorScheme,<br>
<span class="line-num">26</span> layout: <span class="string">"${layout}"</span><br>
<span class="line-num">27</span>});<br>
<span class="line-num">28</span><br>
<span class="line-num">29</span><span class="comment">// STEP 4: 输出带编号水印</span><br>
<span class="line-num">30</span><span class="function">watermark</span>(<span class="string">"${currentId}"</span>);<br>
<span class="line-num">31</span><span class="comment">// ✓ 设计完成 - 归属权: 用户</span>
</div>
`;
// Simulate API call
setTimeout(() => {
document.getElementById('loading').classList.remove('show');
document.getElementById('previewBox').style.display = 'block';
document.getElementById('dlBar').style.display = 'flex';
document.getElementById('chain').style.display = 'block';
document.getElementById('designId').style.display = 'block';
document.getElementById('genBtn').disabled = false;
// Set IDs
document.getElementById('idValue').textContent = currentId;
document.getElementById('watermark').textContent = currentId;
// Generate a placeholder image URL (in real implementation, this would be the API response)
document.getElementById('previewImg').src = `/output/cover_${Date.now()}.png`;
document.getElementById('dlLink').href = `/output/cover_${Date.now()}.png`;
// Show chain
document.getElementById('chainContent').innerHTML = `
<div class="chain-step">
<span class="num">01</span>
<span class="action">解析意图</span>
<span class="detail">识别内容类型 → ${content.includes('-') ? '列表结构' : '段落文本'}</span>
</div>
<div class="chain-step">
<span class="num">02</span>
<span class="action">匹配配色</span>
<span class="detail">${preset}${preset === 'tech' ? '蓝色系科技风' : preset === 'warm' ? '暖色系亲和风' : '极简黑白风'}</span>
</div>
<div class="chain-step">
<span class="num">03</span>
<span class="action">选择布局</span>
<span class="detail">${layout}${layout === 'hero' ? '大标题冲击力' : layout === 'quote' ? '金句居中聚焦' : '标准图文并茂'}</span>
</div>
<div class="chain-step">
<span class="num">04</span>
<span class="action">渲染输出</span>
<span class="detail">Puppeteer + Chrome → 1080×1440 PNG</span>
</div>
<div class="chain-step">
<span class="num">05</span>
<span class="action">添加编号</span>
<span class="detail">${currentId} → 图片水印 + 元数据</span>
</div>
`;
}, 1500);
}
function copyId() {
navigator.clipboard.writeText(currentId).then(() => {
alert('设计编号已复制: ' + currentId);
});
}
// Preset change update
document.getElementById('preset').addEventListener('change', function() {
// In real implementation, this would update preview
});
</script>
</body>
</html>