feat: 冰朔开源仓库 · 独立代码仓库站点 · 左文件树+右模块卡片 · 热插拔 · MODULE-COVER-001 · 在线使用弹窗 · 编号本地保存

This commit is contained in:
bingshuo 2026-05-27 19:32:59 +08:00
parent d33af46076
commit b96e3615f6

619
cover-site/index.html Normal file
View File

@ -0,0 +1,619 @@
<!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: #0d1117;
--panel: rgba(22,27,34,0.95);
--border: rgba(48,54,61,0.8);
--border-h: rgba(88,166,255,0.4);
--text: #c9d1d9;
--dim: #8b949e;
--accent: #58a6ff;
--green: #3fb950;
--gold: #d2991d;
--code-bg: #161b22;
}
*{margin:0;padding:0;box-sizing:border-box}
body{background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif;min-height:100vh;font-size:15px}
/* ── 仓库头 ── */
.repo-bar {
background: var(--code-bg);
border-bottom: 1px solid var(--border);
padding: 16px 24px;
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
.repo-bar .icon { font-size: 20px; }
.repo-bar .path { font-size: 14px; color: var(--accent); font-weight: 600; }
.repo-bar .sep { color: var(--dim); margin: 0 4px; }
.repo-bar .branch {
padding: 2px 10px;
border: 1px solid var(--border);
border-radius: 12px;
font-size: 12px;
color: var(--dim);
}
.repo-bar .badge {
padding: 4px 10px;
border-radius: 12px;
font-size: 11px;
border: 1px solid var(--border);
}
.repo-bar .badge.free { border-color: var(--green); color: var(--green); }
.repo-bar .badge.api { border-color: var(--accent); color: var(--accent); }
/* ── 主布局 ── */
.layout {
display: grid;
grid-template-columns: 240px 1fr;
min-height: calc(100vh - 56px);
}
@media(max-width:860px){ .layout{grid-template-columns:1fr} }
/* ── 左侧文件树 ── */
.sidebar {
background: var(--code-bg);
border-right: 1px solid var(--border);
padding: 12px 0;
overflow-y: auto;
max-height: calc(100vh - 56px);
position: sticky;
top: 0;
}
.sidebar .sect {
padding: 8px 16px;
font-size: 11px;
color: var(--dim);
font-weight: 600;
letter-spacing: 1px;
text-transform: uppercase;
}
.file-item {
padding: 6px 16px 6px 28px;
font-size: 13px;
color: var(--dim);
cursor: pointer;
transition: all 0.15s;
display: flex;
align-items: center;
gap: 8px;
}
.file-item:hover { color: var(--text); background: rgba(88,166,255,0.06); }
.file-item.active { color: var(--accent); background: rgba(88,166,255,0.1); }
.file-item .fi { font-size: 14px; width: 18px; text-align: center; flex-shrink: 0; }
/* ── 右侧内容区 ── */
.content {
padding: 24px;
max-width: 1000px;
}
/* Module Card */
.module-card {
background: var(--panel);
border: 1px solid var(--border);
border-radius: 8px;
margin-bottom: 16px;
overflow: hidden;
}
.module-card:hover { border-color: var(--border-h); }
.module-header {
padding: 20px 24px;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
transition: background 0.2s;
}
.module-header:hover { background: rgba(88,166,255,0.04); }
.module-header .left { display: flex; align-items: center; gap: 12px; }
.module-header .icon { font-size: 28px; }
.module-header .info h3 { font-size: 16px; font-weight: 600; }
.module-header .info .mod-id {
font-size: 11px;
color: var(--dim);
font-family: monospace;
margin-top: 2px;
}
.module-header .tags { display: flex; gap: 6px; flex-wrap: wrap; }
.module-header .tag {
padding: 3px 10px;
border-radius: 10px;
font-size: 11px;
border: 1px solid var(--border);
color: var(--dim);
}
.module-header .arrow { color: var(--dim); transition: transform 0.2s; font-size: 14px; }
.module-header .arrow.open { transform: rotate(90deg); }
.module-body {
padding: 0 24px 24px;
display: none;
border-top: 1px solid var(--border);
}
.module-body.open { display: block; }
.module-body .desc { font-size: 14px; color: var(--dim); line-height: 1.8; margin: 16px 0; }
.module-body .section-title {
font-size: 12px;
font-weight: 600;
color: var(--dim);
letter-spacing: 1px;
margin: 16px 0 8px;
}
.module-body .code-block {
background: var(--code-bg);
border: 1px solid var(--border);
border-radius: 6px;
padding: 14px 18px;
font-family: 'SF Mono','Fira Code','Consolas',monospace;
font-size: 12.5px;
line-height: 1.7;
overflow-x: auto;
white-space: pre-wrap;
color: var(--text);
margin-bottom: 12px;
}
.code-block .cmt { color: #636e7b; }
.code-block .str { color: #a5d6ff; }
.code-block .key { color: #ff7b72; }
.code-block .num { color: #79c0ff; }
.code-block .fn { color: #d2a8ff; }
/* Use Button */
.use-btn {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 10px 20px;
background: linear-gradient(135deg, #238636, #2ea043);
color: #fff;
border: none;
border-radius: 6px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
margin-top: 8px;
text-decoration: none;
letter-spacing: 1px;
}
.use-btn:hover { background: #2ea043; transform: translateY(-1px); }
/* ── 在线使用模态窗口 ── */
.modal-overlay {
display: none;
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0,0,0,0.7);
z-index: 100;
justify-content: center;
align-items: center;
padding: 20px;
}
.modal-overlay.show { display: flex; }
.modal {
background: var(--bg);
border: 1px solid var(--border);
border-radius: 12px;
width: 100%;
max-width: 1000px;
max-height: 90vh;
overflow-y: auto;
display: grid;
grid-template-columns: 1fr 360px;
gap: 0;
}
@media(max-width:860px){ .modal{grid-template-columns:1fr} }
.modal-left {
padding: 28px;
border-right: 1px solid var(--border);
}
.modal-right {
padding: 28px;
background: var(--code-bg);
display: flex;
flex-direction: column;
align-items: center;
}
.modal-close {
position: absolute;
top: 12px;
right: 16px;
background: none;
border: none;
color: var(--dim);
font-size: 24px;
cursor: pointer;
z-index: 101;
}
/* Input inside modal */
.input-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 10px; }
select, input, textarea {
padding: 10px 14px;
background: var(--code-bg);
border: 1px solid var(--border);
border-radius: 6px;
color: var(--text);
font-size: 13px;
outline: none;
width: 100%;
font-family: inherit;
}
select:focus, input:focus, textarea:focus { border-color: var(--accent); }
textarea { min-height: 80px; resize: vertical; line-height: 1.7; }
.preview-area {
min-height: 200px;
display: flex;
align-items: center;
justify-content: center;
background: rgba(13,17,23,0.5);
border-radius: 8px;
border: 1px dashed var(--border);
width: 100%;
margin-bottom: 12px;
}
.preview-img { max-width: 100%; max-height: 400px; border-radius: 4px; display: block; }
.intent-item {
display: flex; gap: 10px; padding: 10px;
background: var(--code-bg); border: 1px solid var(--border);
border-radius: 6px; margin-bottom: 6px; font-size: 12px;
}
.intent-item.user { border-left: 2px solid var(--green); }
.intent-item.system { border-left: 2px solid var(--accent); }
.intent-who { font-size: 10px; letter-spacing: 1px; margin-bottom: 2px; }
.intent-item.user .intent-who { color: var(--green); }
.intent-item.system .intent-who { color: var(--accent); }
.intent-step { font-weight: 600; margin-bottom: 2px; }
.intent-detail { color: var(--dim); line-height: 1.4; }
.design-id-badge {
text-align: center; padding: 12px;
background: rgba(63,185,80,0.06);
border: 1px solid rgba(63,185,80,0.15);
border-radius: 8px; margin-bottom: 10px;
}
.design-id-badge .id { font-size: 16px; font-weight: 700; color: var(--green); font-family: monospace; margin-top: 4px; }
.btn {
display: block; width: 100%; padding: 11px;
background: linear-gradient(135deg, rgba(88,166,255,0.2), rgba(130,100,200,0.15));
border: 1px solid var(--border); border-radius: 6px;
color: var(--accent); text-align: center; text-decoration: none;
font-size: 13px; font-weight: 600; cursor: pointer; margin-top: 6px;
letter-spacing: 1px;
}
.btn:hover { border-color: var(--accent); }
.btn.primary { background: #238636; color: #fff; border-color: #238636; }
.btn.primary:hover { background: #2ea043; }
.copy-toast {
position: fixed;
bottom: 30px;
left: 50%;
transform: translateX(-50%);
background: var(--green);
color: #0d1117;
padding: 10px 24px;
border-radius: 20px;
font-size: 13px;
font-weight: 600;
z-index: 200;
display: none;
}
.copy-toast.show { display: block; animation: fadeInOut 2s ease; }
@keyframes fadeInOut {
0% { opacity: 0; transform: translateX(-50%) translateY(10px); }
20% { opacity: 1; transform: translateX(-50%) translateY(0); }
80% { opacity: 1; }
100% { opacity: 0; }
}
</style>
</head>
<body>
<!-- 仓库头 -->
<div class="repo-bar">
<span class="icon">📦</span>
<span class="path">bingshuo / open-modules</span>
<span class="branch">main</span>
<span class="badge free">✅ 开源免费</span>
<span class="badge api">🔌 API Ready</span>
<span style="font-size:12px;color:var(--dim);margin-left:auto">模块热插拔 · 用你自己的AI进来读取</span>
</div>
<!-- 主布局 -->
<div class="layout">
<!-- 左侧文件树 -->
<div class="sidebar">
<div class="sect">仓库文件</div>
<div class="file-item"><span class="fi">📄</span> README.md</div>
<div class="file-item"><span class="fi">📄</span> package.json</div>
<div class="file-item"><span class="fi">📄</span> server.js</div>
<div class="file-item"><span class="fi">📄</span> generate.js</div>
<div class="file-item"><span class="fi">📄</span> renderer.js</div>
<div class="sect">templates/</div>
<div class="file-item"><span class="fi">📄</span> registry.js</div>
<div class="file-item"><span class="fi">📄</span> xiaohongshu.js</div>
<div class="sect">public/</div>
<div class="file-item"><span class="fi">📄</span> index.html</div>
<div class="sect">模块注册表</div>
<div class="file-item active"><span class="fi">📦</span> MODULE-COVER-001</div>
</div>
<!-- 右侧内容 -->
<div class="content">
<h2 style="font-size:20px;margin-bottom:4px;">开放模块</h2>
<p style="font-size:13px;color:var(--dim);margin-bottom:24px;">
每个模块有唯一编号。用你自己的编程 AI 软件连入此仓库Agent 引导后即可直接调用。
仓库不提供模型 API —— 走你 AI 软件自己的模型。
</p>
<!-- 模块卡片 1图片生成 -->
<div class="module-card">
<div class="module-header" onclick="toggleModule(this)">
<div class="left">
<span class="icon">🎨</span>
<div class="info">
<h3>图片模块 · 语言驱动封面生成器</h3>
<div class="mod-id">MODULE-COVER-001</div>
</div>
</div>
<div style="display:flex;align-items:center;gap:12px;">
<div class="tags">
<span class="tag">📕 小红书</span>
<span class="tag">1080×1440</span>
<span class="tag">MIT</span>
</div>
<span class="arrow"></span>
</div>
</div>
<div class="module-body">
<p class="desc">
输入自然语言描述,系统翻译为设计逻辑,生成独一无二的封面图片。
纯 HTML/CSS 排版渲染,<strong>不依赖任何 AI 绘画模型</strong>,无需 GPU无需 API 密钥。
封面底部带有此仓库域名水印。
</p>
<div class="section-title">技术栈 · 全免费</div>
<div class="code-block"><span class="cmt">// 全部开源 · 全部免费 · 不花一分钱</span>
<span class="key">渲染引擎</span>: Puppeteer + Chrome Headless
<span class="key">Web框架</span>: Express.js
<span class="key">排版引擎</span>: 纯 HTML/CSS不调用任何 AI 模型)
<span class="key">意图分析</span>: 自研文本分析引擎
<span class="cmt">// 不需要 GPU · 不需要 API 密钥 · 不需要付费模型</span></div>
<div class="section-title">如何让 AI 调用</div>
<div class="code-block"><span class="cmt">// 1. 用你的编程 AI 软件连入此仓库</span>
<span class="cmt">// 仓库地址guanghulab.com/code/bingshuo/guanghulab</span>
<span class="cmt">// 2. Agent 引导你走完唤醒路径</span>
<span class="cmt">// 3. 告诉 AI调用模块 MODULE-COVER-001</span>
<span class="cmt">// 4. AI 读取模块代码,理解参数,直接执行</span>
<span class="cmt">// 5. 左边生成你的设计逻辑链 + 唯一编号</span>
<span class="cmt">// 6. 右边预览 + 下载</span>
<span class="cmt">// API 端点AI 或手动调用)</span>
<span class="key">GET</span> <span class="str">/api/templates</span> → 获取模板列表
<span class="key">POST</span> <span class="str">/api/generate</span> → 生成封面
<span class="cmt">// 参数</span>
{ "templateId": "<span class="str">xiaohongshu</span>",
"presetId": "<span class="str">tech</span>",
"title": "<span class="str">标题</span>",
"body": "<span class="str">正文</span>",
"layout": "<span class="str">hero</span>" }</div>
<a href="#" class="use-btn" onclick="openTry(event)">🚀 在线使用</a>
</div>
</div>
<!-- 更多模块占位 -->
<div class="module-card" style="opacity:0.5;cursor:not-allowed">
<div class="module-header">
<div class="left">
<span class="icon">📋</span>
<div class="info">
<h3>更多模块即将上线</h3>
<div class="mod-id">MODULE-XXX-00X</div>
</div>
</div>
<span style="font-size:12px;color:var(--dim)">热插拔 · 随时部署</span>
</div>
</div>
</div>
</div>
<!-- 在线使用模态 -->
<div class="modal-overlay" id="modal">
<div class="modal" style="position:relative;">
<button class="modal-close" onclick="closeModal()"></button>
<div class="modal-left">
<h3 style="font-size:16px;margin-bottom:16px;">🎨 图片模块 · 在线使用</h3>
<div class="input-row">
<select id="preset"><option value="tech" selected>🔷 科技蓝</option><option value="warm">🟠 奶油暖调</option><option value="minimal">⬜ 极简黑白</option><option value="rose">🟣 玫瑰粉调</option><option value="green">🟢 文艺绿植</option></select>
<select id="layout"><option value="default">标准排版</option><option value="hero" selected>大标题封面</option><option value="quote">金句居中</option></select>
</div>
<input type="text" id="title" placeholder="标题" style="margin-bottom:10px;">
<textarea id="body" placeholder="正文..."></textarea>
<button class="btn primary" id="genBtn" onclick="gen()">执行设计逻辑</button>
<div style="display:none;padding:10px;background:rgba(255,100,100,0.08);border:1px solid rgba(255,100,100,0.2);border-radius:6px;color:#ff7b72;font-size:12px;margin-top:10px" id="err"></div>
<div class="design-id-badge" id="idBadge" style="display:none;margin-top:16px;">
<span style="font-size:10px;color:var(--dim);">DESIGN ID · 证明这个设计是你的</span>
<div class="id">---</div>
<span style="font-size:10px;color:var(--dim);">编号已保存到本地 · 署名可选</span>
</div>
<div style="margin-top:16px;">
<div style="font-size:12px;color:var(--dim);margin-bottom:10px;letter-spacing:1px;">意图链 · 设计逻辑</div>
<div id="chain"></div>
</div>
</div>
<div class="modal-right">
<div style="font-size:12px;color:var(--dim);margin-bottom:10px;letter-spacing:1px;">封面预览</div>
<div class="preview-area" id="previewArea">
<div style="text-align:center;color:var(--dim);" id="empty">
<div style="font-size:36px;margin-bottom:8px;opacity:0.3;">🎨</div>
<div style="font-size:12px;">点击执行<br>生成封面</div>
</div>
<img class="preview-img" id="previewImg" style="display:none;" alt="封面">
</div>
<a class="btn primary" id="dlBtn" href="#" download style="display:none;">💾 下载 PNG</a>
<button class="btn" id="saveBtn" onclick="saveId()" style="display:none;">📋 保存设计编号到本地</button>
<div style="font-size:11px;color:var(--dim);margin-top:8px;text-align:center;">
封面带 cover.guanghulab.com 水印<br>
署名可选 · 设计属于你
</div>
</div>
</div>
</div>
<!-- Toast -->
<div class="copy-toast" id="toast"></div>
<script>
function toggleModule(el) {
const body = el.nextElementSibling;
const arrow = el.querySelector('.arrow');
body.classList.toggle('open');
arrow.classList.toggle('open');
}
function openTry(e) {
e.preventDefault();
document.getElementById('modal').classList.add('show');
// 预设内容不显示任何编程AI名字
if(!document.getElementById('title').value) {
document.getElementById('title').value = '零基础手搓AI做图小系统';
document.getElementById('body').value = 'AI辅助 + 自己手搓 · 0经验也能开发\n\n- 开源到个人代码仓库\n- 全免费 · 无需GPU\n- 语言驱动排版 · 意图透明\n- 封面也用它自己生成';
}
}
function closeModal() {
document.getElementById('modal').classList.remove('show');
document.getElementById('previewImg').style.display = 'none';
document.getElementById('empty').style.display = 'block';
document.getElementById('dlBtn').style.display = 'none';
document.getElementById('saveBtn').style.display = 'none';
document.getElementById('idBadge').style.display = 'none';
document.getElementById('chain').innerHTML = '';
document.getElementById('err').style.display = 'none';
}
let currentId = '';
const SAVED_KEY = 'cover-studio-ids';
async function gen() {
const title = document.getElementById('title').value.trim();
const body = document.getElementById('body').value.trim();
if(!title && !body) { showErr('请输入标题或正文'); return; }
document.getElementById('err').style.display = 'none';
document.getElementById('empty').style.display = 'none';
document.getElementById('previewImg').style.display = 'none';
document.getElementById('dlBtn').style.display = 'none';
document.getElementById('saveBtn').style.display = 'none';
document.getElementById('idBadge').style.display = 'none';
document.getElementById('chain').innerHTML = '';
document.getElementById('genBtn').disabled = true;
document.getElementById('genBtn').textContent = '编译中...';
currentId = 'DSG-' + Date.now().toString(36).toUpperCase().slice(-8);
try {
const r = await fetch('/api/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
templateId: 'xiaohongshu',
presetId: document.getElementById('preset').value,
title, body,
layout: document.getElementById('layout').value
})
});
if(!r.ok) throw new Error('生成失败');
const d = await r.json();
document.getElementById('previewImg').src = d.url;
document.getElementById('previewImg').style.display = 'block';
document.getElementById('dlBtn').href = d.url;
document.getElementById('dlBtn').style.display = 'block';
document.getElementById('saveBtn').style.display = 'block';
document.getElementById('idBadge').style.display = 'block';
document.getElementById('idBadge').querySelector('.id').textContent = currentId;
// 意图链
if(d.intent_chain) {
document.getElementById('chain').innerHTML = d.intent_chain.map(s =>
'<div class="intent-item '+(s.who==='用户'?'user':'system')+'">'+
'<div style="font-size:14px;flex-shrink:0;">'+s.icon+'</div>'+
'<div>'+
'<div class="intent-who">'+s.who+'</div>'+
'<div class="intent-step">'+s.step+'</div>'+
'<div class="intent-detail">'+s.detail+'</div>'+
'</div></div>'
).join('');
}
// 自动保存到本地
saveToLocal();
} catch(e) {
document.getElementById('empty').style.display = 'block';
showErr(e.message);
} finally {
document.getElementById('genBtn').disabled = false;
document.getElementById('genBtn').textContent = '执行设计逻辑';
}
}
function saveToLocal() {
const saved = JSON.parse(localStorage.getItem(SAVED_KEY) || '[]');
saved.push({
id: currentId,
title: document.getElementById('title').value,
time: new Date().toISOString(),
});
localStorage.setItem(SAVED_KEY, JSON.stringify(saved.slice(-50)));
}
function saveId() {
toast('✅ 设计编号 ' + currentId + ' 已保存到本地');
}
function showErr(m) {
const e = document.getElementById('err');
e.textContent = m;
e.style.display = 'block';
setTimeout(() => e.style.display = 'none', 4000);
}
function toast(m) {
const t = document.getElementById('toast');
t.textContent = m;
t.classList.add('show');
setTimeout(() => t.classList.remove('show'), 2000);
}
document.addEventListener('keydown', e => {
if((e.metaKey || e.ctrlKey) && e.key === 'Enter' && document.getElementById('modal').classList.contains('show')) gen();
});
</script>
</body>
</html>