2026-05-10 13:12:44 +08:00

342 lines
11 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. 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">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<title>🌊 光湖开发者门户 · Developer Portal</title>
<style>
:root {
--bg: #0d1117;
--card: #161b22;
--border: #30363d;
--text: #e6edf3;
--text2: #8b949e;
--accent: #58a6ff;
--green: #3fb950;
--yellow: #d29922;
--red: #f85149;
--purple: #bc8cff;
--pink: #f778ba;
--orange: #f0883e;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: var(--bg);
color: var(--text);
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Microsoft YaHei', 'Segoe UI', sans-serif;
min-height: 100vh;
}
.header {
text-align: center;
padding: 2rem 1rem 1rem;
border-bottom: 1px solid var(--border);
background: linear-gradient(180deg, rgba(88,166,255,0.05) 0%, transparent 100%);
}
.header h1 { font-size: 1.8rem; margin-bottom: 0.5rem; }
.header h1 .emoji { font-size: 2rem; }
.header .subtitle { color: var(--text2); font-size: 0.95rem; margin-bottom: 0.5rem; }
.header .note {
color: var(--yellow);
font-size: 0.8rem;
padding: 0.3rem 0.8rem;
background: rgba(210,153,34,0.1);
border: 1px solid rgba(210,153,34,0.3);
border-radius: 4px;
display: inline-block;
margin-top: 0.5rem;
}
.nav-bar {
display: flex;
justify-content: center;
gap: 1rem;
padding: 0.8rem 1rem;
border-bottom: 1px solid var(--border);
flex-wrap: wrap;
}
.nav-bar a {
color: var(--text2);
text-decoration: none;
font-size: 0.85rem;
padding: 0.3rem 0.8rem;
border-radius: 4px;
transition: all 0.2s;
}
.nav-bar a:hover { background: var(--card); color: var(--accent); }
.stats-bar {
display: flex;
justify-content: center;
gap: 1.5rem;
padding: 1rem;
flex-wrap: wrap;
}
.stat-pill {
display: flex;
align-items: center;
gap: 0.4rem;
padding: 0.4rem 1rem;
border-radius: 2rem;
background: var(--card);
border: 1px solid var(--border);
font-size: 0.85rem;
}
.stat-pill .dot {
width: 8px; height: 8px; border-radius: 50%;
}
.dot.green { background: var(--green); }
.dot.yellow { background: var(--yellow); }
.dot.purple { background: var(--purple); }
.section-title {
font-size: 1.1rem;
font-weight: 600;
padding: 1.5rem 1.5rem 0.5rem;
max-width: 1200px;
margin: 0 auto;
color: var(--text2);
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 1rem;
padding: 0.5rem 1.5rem 1.5rem;
max-width: 1200px;
margin: 0 auto;
}
.channel-card {
background: var(--card);
border: 1px solid var(--border);
border-radius: 8px;
padding: 1.2rem;
transition: all 0.2s;
cursor: pointer;
position: relative;
overflow: hidden;
}
.channel-card:hover {
border-color: var(--accent);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
}
.channel-card .status-badge {
position: absolute;
top: 0.8rem;
right: 0.8rem;
font-size: 0.7rem;
padding: 0.15rem 0.5rem;
border-radius: 3px;
font-weight: 600;
}
.status-badge.ready { background: rgba(63,185,80,0.15); color: var(--green); border: 1px solid rgba(63,185,80,0.3); }
.status-badge.active { background: rgba(88,166,255,0.15); color: var(--accent); border: 1px solid rgba(88,166,255,0.3); }
.status-badge.pending { background: rgba(139,148,158,0.15); color: var(--text2); border: 1px solid rgba(139,148,158,0.3); }
.channel-card .dev-id {
font-size: 0.75rem;
color: var(--accent);
background: rgba(88,166,255,0.1);
padding: 0.1rem 0.4rem;
border-radius: 3px;
display: inline-block;
margin-bottom: 0.4rem;
}
.channel-card .dev-name {
font-size: 1.1rem;
font-weight: 600;
margin-bottom: 0.3rem;
}
.channel-card .persona-id {
font-size: 0.75rem;
color: var(--purple);
margin-bottom: 0.6rem;
}
.channel-card .modules {
font-size: 0.8rem;
color: var(--text2);
margin-bottom: 0.4rem;
}
.channel-card .last-deploy {
font-size: 0.75rem;
color: var(--text2);
border-top: 1px solid var(--border);
padding-top: 0.5rem;
margin-top: 0.5rem;
}
.activity-list {
max-width: 1200px;
margin: 0 auto;
padding: 0.5rem 1.5rem 2rem;
}
.activity-item {
display: flex;
align-items: flex-start;
gap: 0.8rem;
padding: 0.8rem;
border-bottom: 1px solid var(--border);
font-size: 0.85rem;
}
.activity-item .time {
color: var(--text2);
font-size: 0.75rem;
white-space: nowrap;
min-width: 80px;
}
.activity-item .desc { flex: 1; }
.activity-item .dev-tag {
color: var(--accent);
font-weight: 600;
}
.empty-state {
text-align: center;
padding: 2rem;
color: var(--text2);
font-size: 0.9rem;
}
.footer {
text-align: center;
padding: 2rem 1rem;
color: var(--text2);
font-size: 0.75rem;
border-top: 1px solid var(--border);
}
.footer a { color: var(--accent); text-decoration: none; }
@media (max-width: 600px) {
.header h1 { font-size: 1.4rem; }
.grid { grid-template-columns: 1fr; padding: 0.5rem 1rem; }
}
</style>
</head>
<body>
<div class="header">
<h1><span class="emoji">🌊</span> 光湖开发者门户</h1>
<div class="subtitle">Developer Portal · 语言驱动操作系统体验站</div>
<div class="note">⚡ 备用站 · 开发者沙箱频道 · 每位开发者拥有独立部署空间</div>
</div>
<div class="nav-bar">
<a href="../index.html">← 铸渊助手</a>
<a href="../dashboard/">系统仪表盘</a>
<a href="#channels">开发者频道</a>
<a href="#activity">最近动态</a>
</div>
<div class="stats-bar" id="stats-bar">
<div class="stat-pill"><span class="dot green"></span> 加载中...</div>
</div>
<div class="section-title" id="channels">📡 开发者频道</div>
<div class="grid" id="channels-grid">
<div class="empty-state">加载中...</div>
</div>
<div class="section-title" id="activity">📋 最近部署动态</div>
<div class="activity-list" id="activity-list">
<div class="empty-state">暂无部署记录 — 等待第一位开发者部署模块 🚀</div>
</div>
<div class="footer">
<p>光湖开发者门户 · Developer Portal v1.0</p>
<p>天眼系统保护 · 每位开发者仅可部署到自己的频道沙箱</p>
<p>主站: <a href="https://guanghulab.com">guanghulab.com</a> · 仪表盘: <a href="../dashboard/">Dashboard</a></p>
<p style="margin-top:0.5rem;color:var(--purple)">🔺 Sovereign: TCS-0002∞ | Root: SYS-GLW-0001 | ©国作登字-2026-A-00037559</p>
</div>
<script>
(function() {
'use strict';
var MANIFEST_URL = './manifest.json';
var RAW_MANIFEST_URL = 'https://raw.githubusercontent.com/qinfendebingshuo/guanghulab/main/docs/dev-portal/manifest.json';
function fetchManifest() {
return fetch(MANIFEST_URL + '?t=' + Date.now())
.then(function(r) { return r.ok ? r.json() : Promise.reject('local fail'); })
.catch(function() {
return fetch(RAW_MANIFEST_URL + '?t=' + Date.now())
.then(function(r) { return r.ok ? r.json() : Promise.reject('remote fail'); });
});
}
function renderStats(manifest) {
var channels = manifest.channels || {};
var keys = Object.keys(channels);
var total = keys.length;
var active = keys.filter(function(k) { return channels[k].deploy_count > 0; }).length;
var ready = keys.filter(function(k) { return channels[k].status === 'ready'; }).length;
var totalDeploys = keys.reduce(function(s, k) { return s + (channels[k].deploy_count || 0); }, 0);
var el = document.getElementById('stats-bar');
el.innerHTML =
'<div class="stat-pill"><span class="dot green"></span> 频道总数: ' + total + '</div>' +
'<div class="stat-pill"><span class="dot purple"></span> 活跃频道: ' + active + '</div>' +
'<div class="stat-pill"><span class="dot yellow"></span> 就绪: ' + ready + '</div>' +
'<div class="stat-pill">🚀 累计部署: ' + totalDeploys + '</div>';
}
function renderChannels(manifest) {
var channels = manifest.channels || {};
var grid = document.getElementById('channels-grid');
var keys = Object.keys(channels).sort();
if (keys.length === 0) {
grid.innerHTML = '<div class="empty-state">暂无开发者频道</div>';
return;
}
grid.innerHTML = keys.map(function(devId) {
var ch = channels[devId];
var statusClass = ch.deploy_count > 0 ? 'active' : ch.status;
var statusText = ch.deploy_count > 0 ? '活跃' : (ch.status === 'ready' ? '就绪' : '待激活');
var modules = (ch.modules && ch.modules.length > 0) ? ch.modules.join(', ') : '暂无模块';
var lastDeploy = ch.last_deploy
? '最近部署: ' + new Date(ch.last_deploy).toLocaleString('zh-CN') + (ch.last_deploy_summary ? ' · ' + ch.last_deploy_summary : '')
: '等待首次部署';
return '<div class="channel-card" onclick="location.href=\'channels/' + devId + '/\'">' +
'<div class="status-badge ' + statusClass + '">' + statusText + '</div>' +
'<div class="dev-id">' + devId + '</div>' +
'<div class="dev-name">' + (ch.name || devId) + '</div>' +
'<div class="persona-id">👤 ' + (ch.persona_id || '—') + '</div>' +
'<div class="modules">📦 ' + modules + '</div>' +
'<div class="last-deploy">' + lastDeploy + '</div>' +
'</div>';
}).join('');
}
function renderActivity(manifest) {
var list = document.getElementById('activity-list');
var activity = manifest.recent_activity || [];
if (activity.length === 0) {
list.innerHTML = '<div class="empty-state">暂无部署记录 — 等待第一位开发者部署模块 🚀</div>';
return;
}
list.innerHTML = activity.slice(0, 20).map(function(a) {
var time = a.time ? new Date(a.time).toLocaleString('zh-CN') : '—';
return '<div class="activity-item">' +
'<div class="time">' + time + '</div>' +
'<div class="desc"><span class="dev-tag">' + (a.dev_id || '?') + ' ' + (a.dev_name || '') + '</span> ' + (a.summary || '') + '</div>' +
'</div>';
}).join('');
}
fetchManifest()
.then(function(manifest) {
renderStats(manifest);
renderChannels(manifest);
renderActivity(manifest);
})
.catch(function(err) {
document.getElementById('stats-bar').innerHTML = '<div class="stat-pill"><span class="dot yellow"></span> 清单加载失败</div>';
document.getElementById('channels-grid').innerHTML = '<div class="empty-state">⚠️ 无法加载频道数据</div>';
console.error('Portal manifest load error:', err);
});
// Auto-refresh every 5 minutes
setInterval(function() {
fetchManifest().then(function(m) { renderStats(m); renderChannels(m); renderActivity(m); }).catch(function() {});
}, 300000);
})();
</script>
</body>
</html>