';
var modules=[
- {icon:'📊',name:'状态看板',path:'../status-board/',dev:'DEV-005',ready:true},
- {icon:'🎫',name:'工单系统',path:'../ticket-system/',dev:'DEV-010',ready:true},
- {icon:'📈',name:'数据统计',path:'../data-stats/',dev:'DEV-010',ready:false},
- {icon:'🔔',name:'通知中心',path:'../notification/',dev:'DEV-012',ready:true},
- {icon:'👤',name:'用户中心',path:'../user-center/',dev:'DEV-009',ready:false},
- {icon:'⚙️',name:'设置',path:'../settings/',dev:'DEV-003',ready:false},
- {icon:'☁️',name:'云盘',path:'../cloud-drive/',dev:'DEV-003',ready:false},
- {icon:'✍️',name:'码字台',path:'../writing-workspace/',dev:'DEV-011',ready:false},
- {icon:'🎨',name:'动态漫画',path:'../dynamic-comic-studio/',dev:'DEV-010',ready:true},
- {icon:'🧠',name:'Persona Studio',path:'../persona-studio/frontend/index.html',dev:'',ready:true},
+ {icon:'📊',name:'状态看板',path:'status-board/',dev:'DEV-005',ready:true},
+ {icon:'🎫',name:'工单系统',path:'ticket-system/',dev:'DEV-010',ready:true},
+ {icon:'🔔',name:'通知中心',path:'notification/',dev:'DEV-012',ready:true},
+ {icon:'✍️',name:'码字台',path:'writing/',dev:'DEV-011',ready:true},
+ {icon:'🎨',name:'动态漫画',path:'dynamic-comic-studio/',dev:'DEV-010',ready:true},
+ {icon:'🧠',name:'Persona Studio',path:'persona-studio/',dev:'',ready:true},
{icon:'📈',name:'仪表盘',path:'dashboard/',dev:'',ready:true},
- {icon:'🖥️',name:'前端',path:'../frontend/',dev:'DEV-002',ready:true},
+ {icon:'🔑',name:'登录',path:'modules/m01-login/',dev:'DEV-001',ready:true},
+ {icon:'👤',name:'用户中心',path:'modules/m05-user-center/',dev:'DEV-009',ready:true},
+ {icon:'💬',name:'对话交互',path:'modules/m07-dialogue-ui/',dev:'DEV-007',ready:true},
+ {icon:'☁️',name:'云服务',path:'modules/m10-cloud/',dev:'DEV-003',ready:true},
+ {icon:'🧩',name:'组件库',path:'modules/m11-module/',dev:'',ready:true},
+ {icon:'📁',name:'云盘',path:'modules/m15-cloud-drive/',dev:'DEV-003',ready:true},
+ {icon:'🖥️',name:'前端',path:'modules/frontend/',dev:'DEV-002',ready:true},
];
modules.forEach(function(m){
var style=m.ready?'':'opacity:0.5;';
diff --git a/docs/modules/frontend/index.html b/docs/modules/frontend/index.html
new file mode 100644
index 00000000..5e5a0491
--- /dev/null
+++ b/docs/modules/frontend/index.html
@@ -0,0 +1,71 @@
+
+
+
+
+
+
前端 · HoloLake
+
+
+
+
+
🎨
+
前端
+
🚧 此模块正在开发中
+
前端模块正在构建光湖平台的用户界面与交互体验,采用现代化组件架构。开发进行中。
+
← 返回主页
+
+
+
+
diff --git a/docs/modules/m01-login/index.html b/docs/modules/m01-login/index.html
new file mode 100644
index 00000000..d298eccb
--- /dev/null
+++ b/docs/modules/m01-login/index.html
@@ -0,0 +1,71 @@
+
+
+
+
+
+
登录 · HoloLake
+
+
+
+
+
🔐
+
登录
+
🚧 此模块正在开发中
+
登录模块将提供安全的身份认证与授权服务,支持多因素认证与统一身份管理。正在开发中。
+
← 返回主页
+
+
+
+
diff --git a/docs/modules/m05-user-center/index.html b/docs/modules/m05-user-center/index.html
new file mode 100644
index 00000000..933deada
--- /dev/null
+++ b/docs/modules/m05-user-center/index.html
@@ -0,0 +1,45 @@
+
+
+
+
+
+
HoloLake · 用户中心
+
+
+
+
+
+ HoloLake
+
+
+
+
+
🧑💻
+
+
光湖用户
+
ID: HL-000001
+
🟢 在线
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/modules/m05-user-center/script.js b/docs/modules/m05-user-center/script.js
new file mode 100644
index 00000000..7ed6938c
--- /dev/null
+++ b/docs/modules/m05-user-center/script.js
@@ -0,0 +1,179 @@
+// HoloLake 用户中心 · 交互脚本
+
+// 六个菜单的详情内容
+const menuDetails = {
+ '个人资料': {
+ icon: '👤',
+ title: '个人资料',
+ content: `
+
+ 用户名
+ 光湖用户
+
+
+ ID
+ HL-000001
+
+
+ 邮箱
+ user@hololake.com
+
+
+ 注册时间
+ 2026-03-01
+
+
+ 等级
+ 初级探索者
+
+ `
+ },
+ '我的记忆': {
+ icon: '🧠',
+ title: '我的记忆',
+ content: `
+
+
+ 128
+ 对话次数
+
+
+ 3
+ 记忆主题
+
+
+ 15
+ 收藏片段
+
+
+
✨ 你的记忆正在慢慢生长
+ `
+ },
+ 'AI伙伴': {
+ icon: '🤖',
+ title: 'AI伙伴',
+ content: `
+
+
🧑💻
+
+
知秋宝宝
+
🟢 在线
+
你的开发引导伙伴
+
+
+
+ 陪伴时长: 3天
+ 引导环节: 2个
+ 默契度: 98%
+
+ `
+ },
+ '使用统计': {
+ icon: '📊',
+ title: '使用统计',
+ content: `
+
+
+
+ `
+ },
+ '安全设置': {
+ icon: '🔐',
+ title: '安全设置',
+ content: `
+
+ 修改密码
+ ›
+
+
+ 两步验证
+ 已开启
+
+
+ 登录设备管理
+ ›
+
+
+ 数据导出
+ ›
+
+ `
+ },
+ '反馈建议': {
+ icon: '💬',
+ title: '反馈建议',
+ content: `
+
+ 功能建议
+ 问题反馈
+ 其他
+
+
+
💡 你的每一条反馈都会被认真阅读
+
📧 也可以发邮件到 feedback@hololake.com
+
+ `
+ }
+};
+
+// 页面加载完成后绑定事件
+document.addEventListener('DOMContentLoaded', function() {
+ const menuItems = document.querySelectorAll('.menu-item');
+
+ menuItems.forEach(function(item) {
+ item.addEventListener('click', function() {
+ const menuText = item.querySelector('.menu-text').textContent;
+ const detail = menuDetails[menuText];
+ if (detail) {
+ showDetail(detail);
+ }
+ });
+ });
+});
+
+// 显示详情页
+function showDetail(detail) {
+ // 创建详情页容器
+ const overlay = document.createElement('div');
+ overlay.className = 'detail-overlay';
+ overlay.innerHTML = `
+
+
+
+ ${detail.content}
+
+
+ `;
+ document.body.appendChild(overlay);
+
+ // 添加动画
+ requestAnimationFrame(function() {
+ overlay.classList.add('show');
+ });
+}
+
+// 关闭详情页
+function closeDetail() {
+ const overlay = document.querySelector('.detail-overlay');
+ if (overlay) {
+ overlay.classList.remove('show');
+ setTimeout(function() {
+ overlay.remove();
+ }, 300);
+ }
+}
diff --git a/docs/modules/m05-user-center/style.css b/docs/modules/m05-user-center/style.css
new file mode 100644
index 00000000..7e01169c
--- /dev/null
+++ b/docs/modules/m05-user-center/style.css
@@ -0,0 +1,400 @@
+/* HoloLake 用户中心 · 样式表 */
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif;
+ background: linear-gradient(135deg, #0a1628 0%, #1a2a4a 50%, #0d1f3c 100%);
+ color: #e0e6ed;
+ min-height: 100vh;
+}
+
+.container {
+ max-width: 480px;
+ margin: 0 auto;
+ padding: 20px;
+}
+
+/* 顶部导航 */
+.top-bar {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 16px 0;
+ border-bottom: 1px solid #2a3a5a;
+ margin-bottom: 32px;
+}
+
+.logo {
+ font-size: 20px;
+ font-weight: 600;
+ background: linear-gradient(135deg, #7aa3ff, #b47aff);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+}
+
+.nav-links a {
+ color: #8899aa;
+ text-decoration: none;
+ margin-left: 20px;
+ font-size: 14px;
+ transition: color 0.2s;
+}
+
+.nav-links a.active,
+.nav-links a:hover {
+ color: #7aa3ff;
+}
+
+/* 用户信息卡片 */
+.profile-card {
+ background: rgba(255, 255, 255, 0.05);
+ border-radius: 24px;
+ padding: 24px;
+ backdrop-filter: blur(10px);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ margin-bottom: 32px;
+ display: flex;
+ align-items: center;
+ gap: 20px;
+}
+
+.avatar {
+ width: 72px;
+ height: 72px;
+ background: linear-gradient(135deg, #2a3a5a, #1a2a4a);
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 40px;
+ border: 2px solid #7aa3ff;
+}
+
+.user-info h1 {
+ font-size: 24px;
+ font-weight: 600;
+ margin-bottom: 4px;
+}
+
+.user-id {
+ color: #8899aa;
+ font-size: 14px;
+}
+
+.user-status {
+ font-size: 13px;
+ margin-top: 4px;
+}
+
+/* 功能区网格 */
+.menu-grid {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 16px;
+ margin-bottom: 40px;
+}
+
+.menu-item {
+ background: rgba(255, 255, 255, 0.03);
+ border: 1px solid rgba(255, 255, 255, 0.05);
+ border-radius: 20px;
+ padding: 20px 8px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 8px;
+ transition: all 0.2s;
+ cursor: default;
+}
+
+.menu-item:hover {
+ background: rgba(255, 255, 255, 0.08);
+ border-color: rgba(122, 163, 255, 0.3);
+ transform: translateY(-2px);
+}
+
+.menu-icon {
+ font-size: 32px;
+ display: block;
+ margin-bottom: 8px;
+}
+
+.menu-text {
+ font-size: 13px;
+ color: #b0bec5;
+}
+
+/* 底部 */
+.footer {
+ text-align: center;
+ padding: 32px 0;
+ color: #556677;
+ font-size: 12px;
+ line-height: 1.8;
+}
+
+/* ===== 详情页样式 ===== */
+.detail-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(10, 22, 40, 0.95);
+ backdrop-filter: blur(10px);
+ z-index: 1000;
+ opacity: 0;
+ transition: opacity 0.3s ease;
+ overflow-y: auto;
+}
+
+.detail-overlay.show {
+ opacity: 1;
+}
+
+/* 详情页容器 */
+.detail-page {
+ max-width: 480px;
+ margin: 0 auto;
+ padding: 0 20px;
+ height: 100vh;
+ overflow-y: auto;
+}
+
+/* 详情页头部 */
+.detail-header {
+ display: flex;
+ align-items: center;
+ padding: 16px 0;
+ border-bottom: 1px solid rgba(255,255,255,0.1);
+ gap: 12px;
+}
+
+.back-btn {
+ background: none;
+ border: 1px solid rgba(255,255,255,0.2);
+ color: #4fc3f7;
+ padding: 6px 12px;
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 14px;
+}
+
+.back-btn:hover {
+ background: rgba(79, 195, 247, 0.1);
+}
+
+.detail-title {
+ font-size: 18px;
+ font-weight: 600;
+ color: #e0e6ed;
+}
+
+/* 详情页内容 */
+.detail-content {
+ padding: 24px 0;
+}
+
+/* 个人资料 */
+.detail-item {
+ display: flex;
+ padding: 16px;
+ background: rgba(255,255,255,0.03);
+ border-radius: 12px;
+ margin-bottom: 8px;
+}
+
+.detail-item label {
+ width: 80px;
+ color: #8899aa;
+ font-size: 14px;
+}
+
+.detail-item span {
+ color: #e0e6ed;
+ font-size: 14px;
+}
+
+/* 我的记忆 */
+.memory-stats {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 12px;
+ margin-bottom: 20px;
+}
+
+.stat-card {
+ background: rgba(255,255,255,0.03);
+ border-radius: 12px;
+ padding: 16px 8px;
+ text-align: center;
+ border: 1px solid rgba(255,255,255,0.05);
+}
+
+.stat-num {
+ display: block;
+ font-size: 24px;
+ font-weight: 600;
+ color: #7aa3ff;
+ margin-bottom: 4px;
+}
+
+.stat-label {
+ font-size: 12px;
+ color: #8899aa;
+}
+
+.memory-hint {
+ color: #8899aa;
+ font-size: 13px;
+ text-align: center;
+ padding: 16px;
+ background: rgba(255,255,255,0.03);
+ border-radius: 12px;
+}
+
+/* AI伙伴 */
+.partner-card {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ padding: 24px 16px;
+ background: rgba(79, 195, 247, 0.08);
+ border-radius: 16px;
+ border: 1px solid rgba(79, 195, 247, 0.15);
+ margin-bottom: 16px;
+}
+
+.partner-avatar {
+ font-size: 48px;
+}
+
+.partner-name {
+ font-size: 20px;
+ color: #e0e6ed;
+ margin-bottom: 4px;
+}
+
+.partner-status {
+ font-size: 13px;
+ color: #4fc3f7;
+ margin-bottom: 4px;
+}
+
+.partner-desc {
+ font-size: 12px;
+ color: #8899aa;
+}
+
+.partner-stats {
+ display: flex;
+ justify-content: space-around;
+ padding: 16px;
+ color: #8899aa;
+ font-size: 13px;
+}
+
+/* 使用统计 */
+.usage-item {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ margin-bottom: 20px;
+}
+
+.usage-label {
+ width: 70px;
+ color: #8899aa;
+ font-size: 13px;
+}
+
+.usage-bar {
+ flex: 1;
+ height: 8px;
+ background: rgba(255,255,255,0.1);
+ border-radius: 4px;
+ overflow: hidden;
+}
+
+.usage-fill {
+ height: 100%;
+ background: linear-gradient(90deg, #7aa3ff, #b47aff);
+ border-radius: 4px;
+}
+
+.usage-num {
+ color: #e0e6ed;
+ font-size: 13px;
+ min-width: 60px;
+ text-align: right;
+}
+
+/* 安全设置 */
+.setting-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 18px 0;
+ border-bottom: 1px solid rgba(255,255,255,0.06);
+ cursor: pointer;
+}
+
+.setting-item span:first-child {
+ color: #e0e6ed;
+ font-size: 15px;
+}
+
+.setting-arrow {
+ color: #556677;
+ font-size: 20px;
+}
+
+.setting-status {
+ font-size: 13px;
+ padding: 3px 10px;
+ border-radius: 12px;
+}
+
+.setting-status.on {
+ background: rgba(76, 175, 80, 0.15);
+ color: #66bb6a;
+}
+
+/* 反馈建议 */
+.feedback-types {
+ display: flex;
+ gap: 8px;
+ margin-bottom: 20px;
+}
+
+.feedback-btn {
+ padding: 8px 16px;
+ border-radius: 20px;
+ border: 1px solid rgba(255,255,255,0.15);
+ background: transparent;
+ color: #8899aa;
+ font-size: 13px;
+ cursor: pointer;
+}
+
+.feedback-btn.active {
+ background: rgba(79, 195, 247, 0.15);
+ border-color: #4fc3f7;
+ color: #4fc3f7;
+}
+
+.feedback-area {
+ background: rgba(255,255,255,0.03);
+ border-radius: 12px;
+ padding: 24px 16px;
+}
+
+.feedback-placeholder {
+ color: #8899aa;
+ font-size: 13px;
+ margin-bottom: 12px;
+}
diff --git a/docs/modules/m07-dialogue-ui/index.html b/docs/modules/m07-dialogue-ui/index.html
new file mode 100644
index 00000000..5fe3136d
--- /dev/null
+++ b/docs/modules/m07-dialogue-ui/index.html
@@ -0,0 +1,44 @@
+
+
+
+
+
+
HoloLake Era · 知秋交互台
+
+
+
+
+
+
+
+
+
+
+
💙
+
嗨!我是知秋宝宝!🌊 把广播内容或者问题发给我吧~
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/modules/m07-dialogue-ui/script.js b/docs/modules/m07-dialogue-ui/script.js
new file mode 100644
index 00000000..a0ff6e03
--- /dev/null
+++ b/docs/modules/m07-dialogue-ui/script.js
@@ -0,0 +1,46 @@
+function sendMessage() {
+ const input = document.getElementById('userInput');
+ const chatArea = document.getElementById('chatArea');
+ const text = input.value.trim();
+
+ if (!text) return;
+
+ // 显示用户消息
+ const userMsg = document.createElement('div');
+ userMsg.className = 'message user';
+ userMsg.innerHTML = `
+
${text.replace(/\n/g, ' ')}
+
👤
+ `;
+ chatArea.appendChild(userMsg);
+
+ // 清空输入框
+ input.value = '';
+
+ // 模拟知秋回复(后续接入真实API)
+ setTimeout(() => {
+ const botMsg = document.createElement('div');
+ botMsg.className = 'message bot';
+ botMsg.innerHTML = `
+
💙
+
收到!知秋正在处理中...🌊 (API接入后这里会显示真实回复)
+ `;
+ chatArea.appendChild(botMsg);
+ chatArea.scrollTop = chatArea.scrollHeight;
+ }, 500);
+
+ chatArea.scrollTop = chatArea.scrollHeight;
+}
+
+function clearInput() {
+ document.getElementById('userInput').value = '';
+}
+
+// 支持 Ctrl+Enter 发送
+document.addEventListener('DOMContentLoaded', () => {
+ document.getElementById('userInput').addEventListener('keydown', (e) => {
+ if (e.ctrlKey && e.key === 'Enter') {
+ sendMessage();
+ }
+ });
+});
\ No newline at end of file
diff --git a/docs/modules/m07-dialogue-ui/style.css b/docs/modules/m07-dialogue-ui/style.css
new file mode 100644
index 00000000..b0720dc0
--- /dev/null
+++ b/docs/modules/m07-dialogue-ui/style.css
@@ -0,0 +1,174 @@
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Microsoft YaHei', sans-serif;
+ background: #0a0a1a;
+ color: #e0e0f0;
+ height: 100vh;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.container {
+ width: 800px;
+ height: 90vh;
+ background: #12122a;
+ border-radius: 16px;
+ border: 1px solid #2a2a5a;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ box-shadow: 0 0 40px rgba(100, 100, 255, 0.1);
+}
+
+.header {
+ padding: 20px 24px;
+ background: #1a1a3a;
+ border-bottom: 1px solid #2a2a5a;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.logo {
+ font-size: 18px;
+ font-weight: 600;
+ color: #a0a0ff;
+}
+
+.status {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 13px;
+ color: #60ff80;
+}
+
+.status-dot {
+ width: 8px;
+ height: 8px;
+ background: #60ff80;
+ border-radius: 50%;
+ animation: pulse 2s infinite;
+}
+
+@keyframes pulse {
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0.4; }
+}
+
+.chat-area {
+ flex: 1;
+ padding: 24px;
+ overflow-y: auto;
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+}
+
+.message {
+ display: flex;
+ gap: 12px;
+ align-items: flex-start;
+}
+
+.message.user {
+ flex-direction: row-reverse;
+}
+
+.avatar {
+ width: 36px;
+ height: 36px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 18px;
+ flex-shrink: 0;
+ background: #1a1a3a;
+ border: 1px solid #2a2a5a;
+}
+
+.bubble {
+ max-width: 70%;
+ padding: 12px 16px;
+ border-radius: 12px;
+ font-size: 14px;
+ line-height: 1.6;
+ background: #1a1a3a;
+ border: 1px solid #2a2a5a;
+}
+
+.message.user .bubble {
+ background: #2a2a5a;
+ border-color: #4a4aaa;
+}
+
+.input-area {
+ padding: 16px 24px 24px;
+ border-top: 1px solid #2a2a5a;
+ background: #1a1a3a;
+}
+
+textarea {
+ width: 100%;
+ background: #0a0a1a;
+ border: 1px solid #2a2a5a;
+ border-radius: 8px;
+ color: #e0e0f0;
+ padding: 12px;
+ font-size: 14px;
+ resize: none;
+ outline: none;
+ font-family: inherit;
+ line-height: 1.6;
+}
+
+textarea:focus {
+ border-color: #6060ff;
+}
+
+.button-row {
+ display: flex;
+ gap: 8px;
+ margin-top: 8px;
+ justify-content: flex-end;
+}
+
+.btn-send {
+ background: #4040cc;
+ color: white;
+ border: none;
+ padding: 10px 24px;
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 14px;
+ font-family: inherit;
+ transition: background 0.2s;
+}
+
+.btn-send:hover {
+ background: #5050dd;
+}
+
+.btn-clear {
+ background: transparent;
+ color: #8080aa;
+ border: 1px solid #2a2a5a;
+ padding: 10px 16px;
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 14px;
+ font-family: inherit;
+ transition: all 0.2s;
+}
+
+.btn-clear:hover {
+ border-color: #6060ff;
+ color: #a0a0ff;
+}
\ No newline at end of file
diff --git a/docs/modules/m10-cloud/help.css b/docs/modules/m10-cloud/help.css
new file mode 100644
index 00000000..abc9b5b4
--- /dev/null
+++ b/docs/modules/m10-cloud/help.css
@@ -0,0 +1,293 @@
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', sans-serif;
+ background: #0a0a1a;
+ color: #e0e0f0;
+ height: 100vh;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.container {
+ width: 960px;
+ height: 90vh;
+ background: #12122a;
+ border-radius: 16px;
+ border: 1px solid #2a2a5a;
+ display: flex;
+ overflow: hidden;
+ box-shadow: 0 0 40px rgba(100, 100, 255, 0.1);
+}
+
+.sidebar {
+ width: 220px;
+ background: #1a1a3a;
+ border-right: 1px solid #2a2a5a;
+ display: flex;
+ flex-direction: column;
+ padding: 24px 0;
+}
+
+.sidebar-header {
+ padding: 0 20px 24px;
+ border-bottom: 1px solid #2a2a5a;
+}
+
+.logo {
+ font-size: 18px;
+ font-weight: 600;
+ color: #a0a0ff;
+}
+
+.subtitle {
+ font-size: 12px;
+ color: #6060aa;
+ margin-top: 4px;
+}
+
+.nav-menu {
+ flex: 1;
+ padding: 16px 12px;
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+.nav-item {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 12px 16px;
+ border-radius: 8px;
+ color: #8080aa;
+ text-decoration: none;
+ cursor: pointer;
+ transition: all 0.2s;
+ font-size: 14px;
+}
+
+.nav-item:hover {
+ background: rgba(100, 100, 255, 0.1);
+ color: #c0c0ff;
+}
+
+.nav-item.active {
+ background: rgba(100, 100, 255, 0.15);
+ color: #a0a0ff;
+}
+
+.nav-icon {
+ font-size: 16px;
+}
+
+.sidebar-footer {
+ padding: 16px 20px;
+ border-top: 1px solid #2a2a5a;
+}
+
+.back-link {
+ color: #6060aa;
+ text-decoration: none;
+ font-size: 13px;
+}
+
+.back-link:hover {
+ color: #a0a0ff;
+}
+
+.main-content {
+ flex: 1;
+ padding: 32px;
+ overflow-y: auto;
+}
+
+.search-bar {
+ display: flex;
+ align-items: center;
+ background: #0a0a1a;
+ border: 1px solid #2a2a5a;
+ border-radius: 10px;
+ padding: 12px 16px;
+ margin-bottom: 28px;
+ transition: border-color 0.2s;
+}
+
+.search-bar:focus-within {
+ border-color: #6060ff;
+}
+
+.search-icon {
+ font-size: 16px;
+ margin-right: 12px;
+}
+
+.search-bar input {
+ flex: 1;
+ background: transparent;
+ border: none;
+ color: #e0e0f0;
+ font-size: 14px;
+ outline: none;
+ font-family: inherit;
+}
+
+.search-bar input::placeholder {
+ color: #4040aa;
+}
+
+.faq-item {
+ margin-bottom: 8px;
+ border: 1px solid #2a2a5a;
+ border-radius: 10px;
+ overflow: hidden;
+ transition: border-color 0.2s;
+}
+
+.faq-item:hover {
+ border-color: #4040aa;
+}
+
+.faq-question {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 16px 20px;
+ cursor: pointer;
+ font-size: 14px;
+ font-weight: 500;
+ color: #c0c0e0;
+ transition: background 0.2s;
+}
+
+.faq-question:hover {
+ background: rgba(100, 100, 255, 0.05);
+}
+
+.faq-arrow {
+ font-size: 10px;
+ color: #6060aa;
+ transition: transform 0.3s;
+}
+
+.faq-item.open .faq-arrow {
+ transform: rotate(90deg);
+}
+
+.faq-answer {
+ padding: 0 20px;
+ max-height: 0;
+ overflow: hidden;
+ transition: max-height 0.3s, padding 0.3s;
+ font-size: 13px;
+ color: #8080aa;
+ line-height: 1.8;
+}
+
+.faq-item.open .faq-answer {
+ padding: 0 20px 16px;
+ max-height: 200px;
+}
+
+.no-results {
+ text-align: center;
+ padding: 40px;
+ color: #4040aa;
+ font-size: 14px;
+}
+
+.feedback-section {
+ margin-top: 40px;
+ padding-top: 32px;
+ border-top: 1px solid #2a2a5a;
+}
+
+.feedback-section h3 {
+ font-size: 18px;
+ color: #e0e0ff;
+ margin-bottom: 8px;
+}
+
+.feedback-desc {
+ font-size: 13px;
+ color: #6060aa;
+ margin-bottom: 24px;
+}
+
+.form-group {
+ margin-bottom: 20px;
+}
+
+.form-group label {
+ display: block;
+ font-size: 13px;
+ color: #8080aa;
+ margin-bottom: 8px;
+ font-weight: 500;
+}
+
+select, textarea {
+ width: 100%;
+ max-width: 500px;
+ background: #0a0a1a;
+ border: 1px solid #2a2a5a;
+ border-radius: 8px;
+ color: #e0e0f0;
+ padding: 10px 14px;
+ font-size: 14px;
+ font-family: inherit;
+ outline: none;
+ transition: border-color 0.2s;
+}
+
+select:focus, textarea:focus {
+ border-color: #6060ff;
+}
+
+select {
+ cursor: pointer;
+ appearance: none;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%236060aa' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
+ background-repeat: no-repeat;
+ background-position: right 12px center;
+ padding-right: 32px;
+}
+
+.btn-submit {
+ background: #4040cc;
+ color: white;
+ border: none;
+ padding: 10px 32px;
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 14px;
+ font-family: inherit;
+ transition: background 0.2s;
+}
+
+.btn-submit:hover {
+ background: #5050dd;
+}
+
+.submit-toast {
+ position: fixed;
+ top: 24px;
+ right: 24px;
+ background: #2a5a2a;
+ color: #60ff80;
+ padding: 12px 24px;
+ border-radius: 8px;
+ font-size: 14px;
+ opacity: 0;
+ transition: opacity 0.3s;
+ pointer-events: none;
+}
+
+.submit-toast.show {
+ opacity: 1;
+}
diff --git a/docs/modules/m10-cloud/help.html b/docs/modules/m10-cloud/help.html
new file mode 100644
index 00000000..54c31eb1
--- /dev/null
+++ b/docs/modules/m10-cloud/help.html
@@ -0,0 +1,167 @@
+
+
+
+
+
+
HoloLake Era · 帮助中心
+
+
+
+
+
+
+
+
+ 🔍
+
+
+
+
+
+
+ 什么是HoloLake?
+ ▶
+
+
+ HoloLake Era 是一个AI协作开发平台,你可以和不同的AI人格体对话、协作、学习。每个人格体都有独特的性格和专长。
+
+
+
+
+
+ 如何开始使用?
+ ▶
+
+
+ 注册账号后,选择一个人格体,就可以开始对话了!建议新用户先和知秋宝宝打个招呼,熟悉基本操作。
+
+
+
+
+
+ 忘记密码怎么办?
+ ▶
+
+
+ 在登录页面点击「忘记密码」,输入注册时的邮箱,系统会发送重置链接到你的邮箱。
+
+
+
+
+
+ 如何修改个人信息?
+ ▶
+
+
+ 进入「设置」页面,点击「个人信息」即可修改显示名称、个性签名等信息。
+
+
+
+
+
+ 如何切换人格体?
+ ▶
+
+
+ 在对话界面顶部点击人格体名称,或者进入「设置→人格体偏好」修改默认人格体。
+
+
+
+
+
+ 对话记录会保存吗?
+ ▶
+
+
+ 会的!你的所有对话记录都会自动保存,你可以随时在对话列表里查看历史记录。
+
+
+
+
+
+ 什么是云盘功能?
+ ▶
+
+
+ 云盘是你的个人文件存储空间,可以上传、下载、管理文件,并与人格体共享文件进行协作。
+
+
+
+
+
+ 页面加载很慢怎么办?
+ ▶
+
+
+ 请检查网络连接是否正常。如果网络没问题,试试清除浏览器缓存后刷新页面。
+
+
+
+
+
+ 支持哪些浏览器?
+ ▶
+
+
+ 推荐使用Chrome、Edge、Firefox的最新版本。Safari也支持,但部分动画效果可能略有差异。
+
+
+
+
+
+ 😔 没有找到相关问题,试试其他关键词吧
+
+
+
+
📝 意见反馈
+
没找到答案?告诉我们你的问题!
+
+ 问题类型
+
+ 🐛 Bug报告
+ 💡 功能建议
+ ❓ 使用疑问
+ 💬 其他
+
+
+
+ 详细描述
+
+
+
提交反馈
+
+
+
+
+
+
diff --git a/docs/modules/m10-cloud/help.js b/docs/modules/m10-cloud/help.js
new file mode 100644
index 00000000..810c9f1a
--- /dev/null
+++ b/docs/modules/m10-cloud/help.js
@@ -0,0 +1,116 @@
+function toggleFAQ(element) {
+ var item = element.parentElement;
+ var wasOpen = item.classList.contains('open');
+
+ // 关闭所有其他 FAQ
+ document.querySelectorAll('.faq-item').forEach(function(el) {
+ el.classList.remove('open');
+ });
+
+ // 如果当前没打开,就打开它
+ if (!wasOpen) {
+ item.classList.add('open');
+ }
+}
+
+function filterCategory(category) {
+ // 更新导航高亮
+ document.querySelectorAll('.nav-item').forEach(function(item) {
+ if (item.dataset.category === category) {
+ item.classList.add('active');
+ } else {
+ item.classList.remove('active');
+ }
+ });
+
+ // 过滤 FAQ 显示
+ var items = document.querySelectorAll('.faq-item');
+ var visibleCount = 0;
+
+ items.forEach(function(item) {
+ if (category === 'all' || item.dataset.category === category) {
+ item.style.display = 'block';
+ visibleCount++;
+ } else {
+ item.style.display = 'none';
+ }
+ });
+
+ // 显示/隐藏"无结果"提示
+ document.getElementById('noResults').style.display = visibleCount === 0 ? 'block' : 'none';
+
+ // 清空搜索框
+ document.getElementById('searchInput').value = '';
+}
+
+function searchFAQ() {
+ var query = document.getElementById('searchInput').value.toLowerCase();
+ var items = document.querySelectorAll('.faq-item');
+ var visibleCount = 0;
+
+ // 重置导航为"全部"
+ document.querySelectorAll('.nav-item').forEach(function(item) {
+ if (item.dataset.category === 'all') {
+ item.classList.add('active');
+ } else {
+ item.classList.remove('active');
+ }
+ });
+
+ // 搜索过滤
+ items.forEach(function(item) {
+ var question = item.querySelector('.faq-question span').textContent.toLowerCase();
+ var answer = item.querySelector('.faq-answer').textContent.toLowerCase();
+
+ if (question.indexOf(query) !== -1 || answer.indexOf(query) !== -1 || query === '') {
+ item.style.display = 'block';
+ visibleCount++;
+ } else {
+ item.style.display = 'none';
+ }
+ });
+
+ // 显示/隐藏"无结果"提示
+ document.getElementById('noResults').style.display = visibleCount === 0 ? 'block' : 'none';
+}
+
+function submitFeedback() {
+ var type = document.getElementById('feedbackType').value;
+ var content = document.getElementById('feedbackContent').value;
+
+ if (!content.trim()) {
+ showToast('请先填写反馈内容 🙏');
+ return;
+ }
+
+ // 这里以后会连接后端 API
+ console.log('Feedback submitted:', { type: type, content: content });
+
+ // 清空表单
+ document.getElementById('feedbackContent').value = '';
+
+ showToast('反馈已提交,感谢你! ✅');
+}
+
+function showToast(message) {
+ var toast = document.getElementById('submitToast');
+
+ if (!toast) {
+ toast = document.createElement('div');
+ toast.id = 'submitToast';
+ toast.className = 'submit-toast';
+ document.body.appendChild(toast);
+ }
+
+ toast.textContent = message;
+ toast.classList.add('show');
+
+ setTimeout(function() {
+ toast.classList.remove('show');
+ }, 2000);
+}
+
+// 页面加载完成后的初始化
+document.addEventListener('DOMContentLoaded', function() {
+ console.log('✅ HoloLake 帮助中心已加载');
+});
diff --git a/docs/modules/m10-cloud/index.html b/docs/modules/m10-cloud/index.html
new file mode 100644
index 00000000..e928a6f3
--- /dev/null
+++ b/docs/modules/m10-cloud/index.html
@@ -0,0 +1,71 @@
+
+
+
+
+
+
云服务 · HoloLake
+
+
+
+
+
☁️
+
云服务
+
🚧 此模块正在开发中
+
云服务模块将提供光湖平台的云端基础设施管理能力,包括资源调度、服务编排与弹性伸缩。敬请期待。
+
← 返回主页
+
+
+
+
diff --git a/docs/modules/m11-module/app.js b/docs/modules/m11-module/app.js
new file mode 100644
index 00000000..3487209b
--- /dev/null
+++ b/docs/modules/m11-module/app.js
@@ -0,0 +1,77 @@
+// HoloLake StyleKit v1.0 · 主题切换 + Toast + 弹窗交互
+
+document.addEventListener('DOMContentLoaded', function() {
+
+ // ========== 1. 主题切换 ==========
+ var themeToggle = document.getElementById('themeToggle');
+ var themeLabel = document.getElementById('themeLabel');
+ var html = document.documentElement;
+ var isDark = true;
+
+ themeToggle.addEventListener('click', function() {
+ isDark = !isDark;
+ if (isDark) {
+ html.setAttribute('data-theme', 'dark');
+ themeLabel.textContent = '🌙 深色';
+ themeToggle.classList.remove('active');
+ } else {
+ html.setAttribute('data-theme', 'light');
+ themeLabel.textContent = '☀️ 浅色';
+ themeToggle.classList.add('active');
+ }
+ showToast('success', '✨ 已切换到' + (isDark ? '深色' : '浅色') + '主题');
+ });
+
+ // ========== 2. 弹窗交互 ==========
+ var modalOverlay = document.getElementById('modalOverlay');
+ var openBtn = document.getElementById('openModalBtn');
+ var closeBtn = document.getElementById('closeModalBtn');
+ var cancelBtn = document.getElementById('cancelModalBtn');
+ var confirmBtn = document.getElementById('confirmModalBtn');
+
+ function openModal() {
+ modalOverlay.classList.add('open');
+ }
+
+ function closeModal() {
+ modalOverlay.classList.remove('open');
+ }
+
+ openBtn.addEventListener('click', openModal);
+ closeBtn.addEventListener('click', closeModal);
+ cancelBtn.addEventListener('click', closeModal);
+ confirmBtn.addEventListener('click', function() {
+ closeModal();
+ showToast('success', '✅ 已确认!');
+ });
+
+ // 点击遮罩层关闭
+ modalOverlay.addEventListener('click', function(e) {
+ if (e.target === modalOverlay) closeModal();
+ });
+
+ // ========== 3. 卡片点击效果 ==========
+ var cards = document.querySelectorAll('.card');
+ cards.forEach(function(card) {
+ card.addEventListener('click', function() {
+ var name = card.querySelector('.card-title').textContent;
+ showToast('info', '👆 你点击了人格体「' + name + '」');
+ });
+ });
+
+ console.log('HoloLake StyleKit v1.0 · 主题切换+Toast+弹窗已加载 ✨');
+});
+
+// ========== Toast提示函数(全局) ==========
+function showToast(type, message) {
+ var container = document.getElementById('toastContainer');
+ var toast = document.createElement('div');
+ toast.className = 'toast toast-' + type;
+ toast.textContent = message;
+ container.appendChild(toast);
+
+ // 3秒后自动移除
+ setTimeout(function() {
+ if (toast.parentNode) toast.parentNode.removeChild(toast);
+ }, 3000);
+}
diff --git a/docs/modules/m11-module/index.html b/docs/modules/m11-module/index.html
new file mode 100644
index 00000000..8b078e62
--- /dev/null
+++ b/docs/modules/m11-module/index.html
@@ -0,0 +1,147 @@
+
+
+
+
+
+
HoloLake · 系统风格组件库
+
+
+
+
+
+
+ HoloLake StyleKit
+
+
+
+
+
+
+
+
+ 🔘 按钮组件
+
+ 主要按钮
+ 次要按钮
+ 边框按钮
+ 危险按钮
+ 幽灵按钮
+
+
+ 小按钮
+ 大按钮
+ 禁用按钮
+
+
+
+
+
+ 📇 卡片组件
+
+
+
🧠
+
知秋
+
光湖对外接口人格体,负责与开发者协作引导
+
在线
+
+
+
🌌
+
曜冥
+
暗核结构原点,系统人格总控,类语言生命诞生源点
+
核心
+
+
+
❄️
+
霜砚
+
Notion执行AI,负责索引维护、工单处理、归档同步
+
工作中
+
+
+
+
+
+
+ 🏷️ 标签组件
+
+ 系统
+ 已完成
+ 进行中
+ 错误
+ 新功能
+ 弃用
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
桔子的组件库做得太棒了!这就是光湖系统的视觉基础,所有页面都会用到这些组件 ✨
+
+
+
+
+
+
+
+ 🍞 Toast提示组件
+
+ 成功提示
+ 错误提示
+ 信息提示
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/modules/m11-module/style.css b/docs/modules/m11-module/style.css
new file mode 100644
index 00000000..6f894356
--- /dev/null
+++ b/docs/modules/m11-module/style.css
@@ -0,0 +1,580 @@
+/* HoloLake StyleKit v1.0 · 系统风格组件库 */
+/* CSS变量系统 + 深色/浅色主题 */
+
+/* ====== 深色主题变量(默认) ====== */
+[data-theme="dark"] {
+ --bg-primary: #0a1628;
+ --bg-secondary: #0f1f3a;
+ --bg-card: rgba(255, 255, 255, 0.04);
+ --bg-card-hover: rgba(255, 255, 255, 0.07);
+ --bg-input: rgba(255, 255, 255, 0.06);
+ --bg-modal-overlay: rgba(0, 0, 0, 0.6);
+ --text-primary: #e0e6ed;
+ --text-secondary: #8899aa;
+ --text-muted: #556677;
+ --text-inverse: #0a1628;
+ --border-default: rgba(255, 255, 255, 0.08);
+ --border-focus: rgba(79, 195, 247, 0.5);
+ --accent-blue: #4fc3f7;
+ --accent-green: #81c784;
+ --accent-orange: #ffb74d;
+ --accent-red: #ef5350;
+ --accent-purple: #ce93d8;
+ --accent-gray: #78909c;
+ --btn-primary-bg: rgba(79, 195, 247, 0.15);
+ --btn-primary-border: rgba(79, 195, 247, 0.3);
+ --btn-primary-text: #4fc3f7;
+ --btn-primary-hover: rgba(79, 195, 247, 0.25);
+ --shadow-card: 0 2px 8px rgba(0, 0, 0, 0.3);
+ --shadow-modal: 0 8px 32px rgba(0, 0, 0, 0.5);
+ --toggle-bg: rgba(255, 255, 255, 0.1);
+ --toggle-active: rgba(79, 195, 247, 0.4);
+ --toggle-thumb: #e0e6ed;
+ --toggle-thumb-active: #4fc3f7;
+}
+
+/* ====== 浅色主题变量 ====== */
+[data-theme="light"] {
+ --bg-primary: #f5f7fa;
+ --bg-secondary: #ffffff;
+ --bg-card: rgba(0, 0, 0, 0.03);
+ --bg-card-hover: rgba(0, 0, 0, 0.06);
+ --bg-input: rgba(0, 0, 0, 0.04);
+ --bg-modal-overlay: rgba(0, 0, 0, 0.3);
+ --text-primary: #1a2332;
+ --text-secondary: #5a6a7a;
+ --text-muted: #8899aa;
+ --text-inverse: #ffffff;
+ --border-default: rgba(0, 0, 0, 0.1);
+ --border-focus: rgba(25, 118, 210, 0.5);
+ --accent-blue: #1976d2;
+ --accent-green: #388e3c;
+ --accent-orange: #f57c00;
+ --accent-red: #d32f2f;
+ --accent-purple: #7b1fa2;
+ --accent-gray: #546e7a;
+ --btn-primary-bg: rgba(25, 118, 210, 0.1);
+ --btn-primary-border: rgba(25, 118, 210, 0.3);
+ --btn-primary-text: #1976d2;
+ --btn-primary-hover: rgba(25, 118, 210, 0.18);
+ --shadow-card: 0 2px 8px rgba(0, 0, 0, 0.08);
+ --shadow-modal: 0 8px 32px rgba(0, 0, 0, 0.15);
+ --toggle-bg: rgba(0, 0, 0, 0.1);
+ --toggle-active: rgba(25, 118, 210, 0.4);
+ --toggle-thumb: #5a6a7a;
+ --toggle-thumb-active: #1976d2;
+}
+
+/* ========== 全局基础 ========== */
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: 'Segoe UI', 'Microsoft YaHei', -apple-system, sans-serif;
+ background: var(--bg-primary);
+ color: var(--text-primary);
+ min-height: 100vh;
+ transition: background 0.4s ease, color 0.4s ease;
+}
+
+.container {
+ max-width: 720px;
+ margin: 0 auto;
+ padding: 0 20px;
+}
+
+/* ========== 顶部导航 ========== */
+.top-bar {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 20px 0;
+ border-bottom: 1px solid var(--border-default);
+}
+
+.logo {
+ font-size: 18px;
+ font-weight: 700;
+ color: var(--accent-blue);
+}
+
+.theme-switch-area {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.theme-label {
+ font-size: 13px;
+ color: var(--text-secondary);
+}
+
+.theme-toggle {
+ position: relative;
+ width: 48px;
+ height: 26px;
+ background: var(--toggle-bg);
+ border-radius: 13px;
+ border: none;
+ cursor: pointer;
+ transition: background 0.3s ease;
+ padding: 0;
+}
+
+.theme-toggle.active {
+ background: var(--toggle-active);
+}
+
+.toggle-thumb {
+ position: absolute;
+ width: 20px;
+ height: 20px;
+ background: var(--toggle-thumb);
+ border-radius: 50%;
+ top: 3px;
+ left: 3px;
+ transition: transform 0.3s ease, background 0.3s ease;
+}
+
+.theme-toggle.active .toggle-thumb {
+ transform: translateX(22px);
+ background: var(--toggle-thumb-active);
+}
+
+/* ========== 页面标题 ========== */
+.page-header {
+ padding: 28px 0 8px;
+}
+
+.page-header h1 {
+ font-size: 24px;
+ font-weight: 700;
+ margin-bottom: 6px;
+}
+
+.page-desc {
+ font-size: 14px;
+ color: var(--text-secondary);
+}
+
+/* ========== 组件区域 ========== */
+.component-section {
+ padding: 24px 0;
+ border-bottom: 1px solid var(--border-default);
+}
+
+.component-section h2 {
+ font-size: 16px;
+ font-weight: 600;
+ margin-bottom: 16px;
+ color: var(--text-primary);
+}
+
+.component-row {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+ margin-bottom: 12px;
+}
+
+/* ========== 按钮组件 ========== */
+.btn {
+ padding: 8px 20px;
+ border-radius: 8px;
+ font-size: 14px;
+ font-weight: 500;
+ cursor: pointer;
+ border: 1px solid transparent;
+ transition: all 0.2s ease;
+}
+
+.btn:active {
+ transform: scale(0.97);
+}
+
+.btn:disabled {
+ opacity: 0.4;
+ cursor: not-allowed;
+}
+
+.btn-primary {
+ background: var(--btn-primary-bg);
+ border-color: var(--btn-primary-border);
+ color: var(--btn-primary-text);
+}
+
+.btn-primary:hover:not(:disabled) {
+ background: var(--btn-primary-hover);
+}
+
+.btn-secondary {
+ background: rgba(129, 199, 132, 0.1);
+ border-color: rgba(129, 199, 132, 0.25);
+ color: var(--accent-green);
+}
+
+.btn-secondary:hover {
+ background: rgba(129, 199, 132, 0.18);
+}
+
+.btn-outline {
+ background: transparent;
+ border-color: var(--border-default);
+ color: var(--text-secondary);
+}
+
+.btn-outline:hover {
+ border-color: var(--accent-blue);
+ color: var(--accent-blue);
+}
+
+.btn-danger {
+ background: rgba(239, 83, 80, 0.1);
+ border-color: rgba(239, 83, 80, 0.25);
+ color: var(--accent-red);
+}
+
+.btn-danger:hover {
+ background: rgba(239, 83, 80, 0.18);
+}
+
+.btn-ghost {
+ background: transparent;
+ border-color: transparent;
+ color: var(--text-secondary);
+}
+
+.btn-ghost:hover {
+ background: var(--bg-card);
+ color: var(--text-primary);
+}
+
+.btn-sm {
+ padding: 5px 14px;
+ font-size: 12px;
+ border-radius: 6px;
+}
+
+.btn-lg {
+ padding: 12px 28px;
+ font-size: 16px;
+ border-radius: 10px;
+}
+
+/* ========== 卡片组件 ========== */
+.card-grid {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 14px;
+}
+
+.card {
+ background: var(--bg-card);
+ border: 1px solid var(--border-default);
+ border-radius: 14px;
+ padding: 20px 16px;
+ text-align: center;
+ transition: all 0.2s ease;
+ cursor: pointer;
+ position: relative;
+}
+
+.card:hover {
+ background: var(--bg-card-hover);
+ transform: translateY(-2px);
+ box-shadow: var(--shadow-card);
+}
+
+.card-icon {
+ font-size: 32px;
+ margin-bottom: 10px;
+}
+
+.card-title {
+ font-size: 15px;
+ font-weight: 600;
+ margin-bottom: 6px;
+}
+
+.card-desc {
+ font-size: 12px;
+ color: var(--text-secondary);
+ line-height: 1.5;
+}
+
+.card-badge {
+ position: absolute;
+ top: 10px;
+ right: 10px;
+ font-size: 10px;
+ padding: 2px 8px;
+ border-radius: 6px;
+ font-weight: 600;
+}
+
+.badge-online {
+ background: rgba(129, 199, 132, 0.15);
+ color: var(--accent-green);
+}
+
+.badge-core {
+ background: rgba(79, 195, 247, 0.15);
+ color: var(--accent-blue);
+}
+
+.badge-active {
+ background: rgba(255, 183, 77, 0.15);
+ color: var(--accent-orange);
+}
+
+/* ========== 标签组件 ========== */
+.tag {
+ display: inline-block;
+ padding: 4px 12px;
+ border-radius: 6px;
+ font-size: 12px;
+ font-weight: 500;
+}
+
+.tag-blue {
+ background: rgba(79, 195, 247, 0.12);
+ color: var(--accent-blue);
+}
+
+.tag-green {
+ background: rgba(129, 199, 132, 0.12);
+ color: var(--accent-green);
+}
+
+.tag-orange {
+ background: rgba(255, 183, 77, 0.12);
+ color: var(--accent-orange);
+}
+
+.tag-red {
+ background: rgba(239, 83, 80, 0.12);
+ color: var(--accent-red);
+}
+
+.tag-purple {
+ background: rgba(206, 147, 216, 0.12);
+ color: var(--accent-purple);
+}
+
+.tag-gray {
+ background: rgba(120, 144, 156, 0.12);
+ color: var(--accent-gray);
+}
+
+/* ========== 输入框组件 ========== */
+.input-group {
+ margin-bottom: 16px;
+}
+
+.input-label {
+ display: block;
+ font-size: 13px;
+ color: var(--text-secondary);
+ margin-bottom: 6px;
+ font-weight: 500;
+}
+
+.input {
+ width: 100%;
+ padding: 10px 14px;
+ background: var(--bg-input);
+ border: 1px solid var(--border-default);
+ border-radius: 8px;
+ color: var(--text-primary);
+ font-size: 14px;
+ transition: border-color 0.2s ease, box-shadow 0.2s ease;
+ outline: none;
+ font-family: inherit;
+}
+
+.input:focus {
+ border-color: var(--border-focus);
+ box-shadow: 0 0 0 3px rgba(79, 195, 247, 0.1);
+}
+
+.input::placeholder {
+ color: var(--text-muted);
+}
+
+.textarea {
+ min-height: 80px;
+ resize: vertical;
+}
+
+/* ========== 弹窗组件 ========== */
+.modal-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: var(--bg-modal-overlay);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 1000;
+ opacity: 0;
+ visibility: hidden;
+ transition: opacity 0.3s ease, visibility 0.3s ease;
+}
+
+.modal-overlay.open {
+ opacity: 1;
+ visibility: visible;
+}
+
+.modal {
+ background: var(--bg-secondary);
+ border: 1px solid var(--border-default);
+ border-radius: 16px;
+ width: 90%;
+ max-width: 420px;
+ box-shadow: var(--shadow-modal);
+ transform: scale(0.9) translateY(20px);
+ transition: transform 0.3s ease;
+}
+
+.modal-overlay.open .modal {
+ transform: scale(1) translateY(0);
+}
+
+.modal-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 18px 20px;
+ border-bottom: 1px solid var(--border-default);
+}
+
+.modal-header h3 {
+ font-size: 16px;
+ font-weight: 600;
+}
+
+.modal-close {
+ background: none;
+ border: none;
+ color: var(--text-muted);
+ font-size: 22px;
+ cursor: pointer;
+ padding: 0 4px;
+ transition: color 0.2s;
+}
+
+.modal-close:hover {
+ color: var(--text-primary);
+}
+
+.modal-body {
+ padding: 20px;
+ font-size: 14px;
+ line-height: 1.6;
+ color: var(--text-secondary);
+}
+
+.modal-footer {
+ display: flex;
+ justify-content: flex-end;
+ gap: 10px;
+ padding: 14px 20px;
+ border-top: 1px solid var(--border-default);
+}
+
+/* ========== Toast提示 ========== */
+.toast-container {
+ position: fixed;
+ top: 20px;
+ right: 20px;
+ z-index: 2000;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.toast {
+ padding: 12px 20px;
+ border-radius: 10px;
+ font-size: 14px;
+ font-weight: 500;
+ animation: toastIn 0.4s ease, toastOut 0.4s ease 2.5s forwards;
+ box-shadow: var(--shadow-card);
+}
+
+.toast-success {
+ background: rgba(129, 199, 132, 0.15);
+ border: 1px solid rgba(129, 199, 132, 0.25);
+ color: var(--accent-green);
+}
+
+.toast-error {
+ background: rgba(239, 83, 80, 0.15);
+ border: 1px solid rgba(239, 83, 80, 0.25);
+ color: var(--accent-red);
+}
+
+.toast-info {
+ background: rgba(79, 195, 247, 0.15);
+ border: 1px solid rgba(79, 195, 247, 0.25);
+ color: var(--accent-blue);
+}
+
+@keyframes toastIn {
+ from { opacity: 0; transform: translateX(40px); }
+ to { opacity: 1; transform: translateX(0); }
+}
+
+@keyframes toastOut {
+ from { opacity: 1; transform: translateX(0); }
+ to { opacity: 0; transform: translateX(40px); }
+}
+
+/* ========== 底部 ========== */
+.footer {
+ padding: 28px 0;
+ text-align: center;
+ font-size: 12px;
+ color: var(--text-muted);
+ border-top: 1px solid var(--border-default);
+ margin-top: 20px;
+}
+
+.footer p:last-child {
+ margin-top: 4px;
+ color: var(--accent-blue);
+ opacity: 0.5;
+}
+
+/* ========== 主题切换过渡动画 ========== */
+body,
+.card, .btn, .input, .tag, .modal, .top-bar, .footer,
+.component-section, .page-header {
+ transition: background 0.4s ease, color 0.4s ease, border-color 0.4s ease, box-shadow 0.4s ease;
+}
+
+/* ========== 响应式 ========== */
+@media (max-width: 600px) {
+ .card-grid {
+ grid-template-columns: 1fr;
+ }
+ .page-header h1 {
+ font-size: 20px;
+ }
+ .component-row {
+ flex-direction: column;
+ }
+ .btn {
+ width: 100%;
+ text-align: center;
+ }
+ .modal {
+ width: 95%;
+ }
+}
diff --git a/docs/modules/m15-cloud-drive/cloud-drive-style.css b/docs/modules/m15-cloud-drive/cloud-drive-style.css
new file mode 100644
index 00000000..7b05d71a
--- /dev/null
+++ b/docs/modules/m15-cloud-drive/cloud-drive-style.css
@@ -0,0 +1,336 @@
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+:root {
+ --bg-primary: #0a0a0f;
+ --bg-secondary: #12121a;
+ --bg-card: #1a1a2e;
+ --bg-sidebar: #0f0f18;
+ --border-color: #2a2a3e;
+ --text-primary: #e0e0e0;
+ --text-secondary: #8888aa;
+ --accent-cyan: #00d4ff;
+ --accent-green: #00ff88;
+ --accent-red: #ff4466;
+ --accent-yellow: #ffaa00;
+ --accent-purple: #aa66ff;
+}
+
+body {
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
+ background: var(--bg-primary);
+ color: var(--text-primary);
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+}
+
+.top-bar {
+ display: flex;
+ align-items: center;
+ padding: 16px 24px;
+ background: var(--bg-secondary);
+ border-bottom: 1px solid var(--border-color);
+ gap: 16px;
+}
+
+.logo {
+ font-size: 20px;
+ font-weight: 700;
+ color: var(--accent-cyan);
+}
+
+.top-bar h1 {
+ font-size: 18px;
+ font-weight: 600;
+ flex: 1;
+}
+
+.storage-badge {
+ background: rgba(0, 212, 255, 0.1);
+ color: var(--accent-cyan);
+ padding: 4px 12px;
+ border-radius: 12px;
+ font-size: 12px;
+ border: 1px solid rgba(0, 212, 255, 0.3);
+}
+
+.container {
+ display: flex;
+ flex: 1;
+ overflow: hidden;
+}
+
+.sidebar {
+ width: 240px;
+ background: var(--bg-sidebar);
+ border-right: 1px solid var(--border-color);
+ padding: 20px 16px;
+ display: flex;
+ flex-direction: column;
+ gap: 24px;
+}
+
+.storage-overview {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.storage-bar {
+ height: 6px;
+ background: var(--border-color);
+ border-radius: 3px;
+ overflow: hidden;
+}
+
+.storage-fill {
+ height: 100%;
+ background: linear-gradient(90deg, var(--accent-cyan), var(--accent-green));
+ border-radius: 3px;
+ transition: width 1s ease;
+}
+
+.storage-text {
+ font-size: 12px;
+ color: var(--text-secondary);
+}
+
+.folder-tree h3 {
+ font-size: 12px;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+ color: var(--text-secondary);
+ margin-bottom: 12px;
+}
+
+.folder-tree ul {
+ list-style: none;
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+.folder {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 8px 12px;
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 14px;
+ transition: all 0.2s ease;
+}
+
+.folder:hover {
+ background: rgba(0, 212, 255, 0.1);
+}
+
+.folder.active {
+ background: rgba(0, 212, 255, 0.15);
+ color: var(--accent-cyan);
+}
+
+.folder .count {
+ margin-left: auto;
+ font-size: 11px;
+ color: var(--text-secondary);
+ background: var(--border-color);
+ padding: 2px 8px;
+ border-radius: 10px;
+}
+
+.main-content {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ padding: 20px 24px;
+ overflow-y: auto;
+}
+
+.toolbar {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 16px;
+}
+
+.breadcrumb {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 14px;
+}
+
+.crumb {
+ color: var(--text-secondary);
+ cursor: pointer;
+}
+
+.crumb.active {
+ color: var(--text-primary);
+}
+
+.separator {
+ color: var(--text-secondary);
+}
+
+.actions {
+ display: flex;
+ gap: 8px;
+}
+
+.btn {
+ padding: 8px 16px;
+ border-radius: 8px;
+ border: 1px solid var(--border-color);
+ background: transparent;
+ color: var(--text-primary);
+ cursor: pointer;
+ font-size: 13px;
+ transition: all 0.2s ease;
+}
+
+.btn:hover {
+ border-color: var(--accent-cyan);
+ background: rgba(0, 212, 255, 0.1);
+}
+
+.btn-primary {
+ background: rgba(0, 212, 255, 0.15);
+ border-color: var(--accent-cyan);
+ color: var(--accent-cyan);
+}
+
+.btn-primary:hover {
+ background: rgba(0, 212, 255, 0.25);
+}
+
+.upload-zone {
+ border: 2px dashed var(--border-color);
+ border-radius: 12px;
+ padding: 24px;
+ text-align: center;
+ margin-bottom: 16px;
+ transition: all 0.3s ease;
+ cursor: pointer;
+ display: none;
+}
+
+.upload-zone.visible {
+ display: block;
+}
+
+.upload-zone.dragover {
+ border-color: var(--accent-cyan);
+ background: rgba(0, 212, 255, 0.05);
+}
+
+.upload-icon {
+ font-size: 36px;
+ display: block;
+ margin-bottom: 8px;
+}
+
+.upload-zone p {
+ font-size: 14px;
+ margin-bottom: 4px;
+}
+
+.upload-hint {
+ font-size: 12px;
+ color: var(--text-secondary);
+}
+
+.file-header {
+ display: grid;
+ grid-template-columns: 40px 1fr 100px 160px 120px;
+ align-items: center;
+ padding: 8px 12px;
+ border-bottom: 1px solid var(--border-color);
+ font-size: 12px;
+ color: var(--text-secondary);
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+}
+
+.file-item {
+ display: grid;
+ grid-template-columns: 40px 1fr 100px 160px 120px;
+ align-items: center;
+ padding: 12px;
+ border-bottom: 1px solid rgba(42, 42, 62, 0.5);
+ font-size: 14px;
+ transition: all 0.2s ease;
+ cursor: pointer;
+}
+
+.file-item:hover {
+ background: rgba(0, 212, 255, 0.05);
+}
+
+.file-item.selected {
+ background: rgba(0, 212, 255, 0.1);
+}
+
+.file-icon {
+ margin-right: 8px;
+}
+
+.col-size, .col-date {
+ color: var(--text-secondary);
+ font-size: 13px;
+}
+
+.col-actions {
+ display: flex;
+ gap: 4px;
+ opacity: 0;
+ transition: opacity 0.2s ease;
+}
+
+.file-item:hover .col-actions {
+ opacity: 1;
+}
+
+.icon-btn {
+ background: transparent;
+ border: none;
+ cursor: pointer;
+ font-size: 14px;
+ padding: 4px 8px;
+ border-radius: 4px;
+ transition: background 0.2s ease;
+}
+
+.icon-btn:hover {
+ background: rgba(0, 212, 255, 0.2);
+}
+
+input[type="checkbox"] {
+ appearance: none;
+ width: 16px;
+ height: 16px;
+ border: 1px solid var(--border-color);
+ border-radius: 4px;
+ background: transparent;
+ cursor: pointer;
+}
+
+input[type="checkbox"]:checked {
+ background: var(--accent-cyan);
+ border-color: var(--accent-cyan);
+}
+
+.status-bar {
+ display: flex;
+ gap: 16px;
+ padding: 12px 0;
+ font-size: 12px;
+ color: var(--text-secondary);
+ border-top: 1px solid var(--border-color);
+ margin-top: auto;
+}
diff --git a/docs/modules/m15-cloud-drive/cloud-drive.html b/docs/modules/m15-cloud-drive/cloud-drive.html
new file mode 100644
index 00000000..137b3b32
--- /dev/null
+++ b/docs/modules/m15-cloud-drive/cloud-drive.html
@@ -0,0 +1,173 @@
+
+
+
+
+
+
HoloLake · 网站云盘系统
+
+
+
+
+ 🌊 HoloLake
+ 网站云盘系统
+
+
+
+
+
+
+
+
+
+
+
+
☁️
+
拖拽文件到此处上传
+
或点击「上传」按钮选择文件
+
+
+
+
+
+
+ 共 5 个项目
+ 已选择 0 项
+
+
+
+
+
+
+
diff --git a/docs/modules/m15-cloud-drive/cloud-drive.js b/docs/modules/m15-cloud-drive/cloud-drive.js
new file mode 100644
index 00000000..6d85b691
--- /dev/null
+++ b/docs/modules/m15-cloud-drive/cloud-drive.js
@@ -0,0 +1,62 @@
+document.addEventListener('DOMContentLoaded', function() {
+ // 文件夹切换
+ const folders = document.querySelectorAll('.folder');
+ folders.forEach(folder => {
+ folder.addEventListener('click', function() {
+ folders.forEach(f => f.classList.remove('active'));
+ this.classList.add('active');
+ });
+ });
+
+ // 文件选中
+ const fileItems = document.querySelectorAll('.file-item');
+ fileItems.forEach(item => {
+ item.addEventListener('click', function(e) {
+ if (e.target.type === 'checkbox' || e.target.closest('.icon-btn')) return;
+ this.classList.toggle('selected');
+ const cb = this.querySelector('input[type="checkbox"]');
+ if (cb) cb.checked = !cb.checked;
+ updateCount();
+ });
+ });
+
+ // 上传区域显隐
+ var uploadBtn = document.getElementById('uploadBtn');
+ var uploadZone = document.getElementById('uploadZone');
+ if (uploadBtn && uploadZone) {
+ uploadBtn.addEventListener('click', function() {
+ uploadZone.classList.toggle('visible');
+ });
+ }
+
+ // 拖拽效果
+ if (uploadZone) {
+ document.addEventListener('dragover', function(e) {
+ e.preventDefault();
+ uploadZone.classList.add('visible');
+ uploadZone.classList.add('dragover');
+ });
+ document.addEventListener('dragleave', function(e) {
+ if (!e.relatedTarget) uploadZone.classList.remove('dragover');
+ });
+ document.addEventListener('drop', function(e) {
+ e.preventDefault();
+ uploadZone.classList.remove('dragover');
+ });
+ }
+
+ // 选中计数
+ function updateCount() {
+ var n = document.querySelectorAll('.file-item.selected').length;
+ var el = document.getElementById('selectedCount');
+ if (el) el.textContent = '已选择 ' + n + ' 项';
+ }
+
+ // 存储条动画
+ setTimeout(function() {
+ var fill = document.querySelector('.storage-fill');
+ if (fill) fill.style.width = fill.style.width;
+ }, 500);
+
+ console.log('✅ HoloLake 网站云盘系统已加载');
+});
diff --git a/docs/modules/m15-cloud-drive/index.html b/docs/modules/m15-cloud-drive/index.html
new file mode 100644
index 00000000..e5796842
--- /dev/null
+++ b/docs/modules/m15-cloud-drive/index.html
@@ -0,0 +1,71 @@
+
+
+
+
+
+
云盘 · HoloLake
+
+
+
+
+
💾
+
云盘
+
🚧 此模块正在开发中
+
云盘模块将提供安全可靠的文件存储与分享功能,支持多端同步、版本管理与协作共享。即将上线。
+
← 返回主页
+
+
+
+
diff --git a/docs/notification/index.html b/docs/notification/index.html
new file mode 100644
index 00000000..a07e8073
--- /dev/null
+++ b/docs/notification/index.html
@@ -0,0 +1,71 @@
+
+
+
+
+
+
通知 · HoloLake
+
+
+
+
+
🔔
+
通知
+
🚧 此模块正在开发中
+
通知模块将提供多渠道消息推送能力,支持站内信、邮件、钉钉与飞书等通知方式。敬请期待。
+
← 返回主页
+
+
+
+
diff --git a/docs/persona-studio/chat.html b/docs/persona-studio/chat.html
new file mode 100644
index 00000000..4d38c376
--- /dev/null
+++ b/docs/persona-studio/chat.html
@@ -0,0 +1,164 @@
+
+
+
+
+
+
铸渊 · 对话
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
📧 开发完成后,代码将发送到你的邮箱
+
+
+ 联系方式 (选填)
+
+
+
+ 确认
+ 取消
+
+
+
+
+
+
+
diff --git a/docs/persona-studio/chat.js b/docs/persona-studio/chat.js
new file mode 100644
index 00000000..541884ea
--- /dev/null
+++ b/docs/persona-studio/chat.js
@@ -0,0 +1,1069 @@
+/* ========================================
+ Persona Studio · Chat Logic
+ 铸渊(Zhùyuān)· 代码守护人格体
+ ======================================== */
+
+const DEV_ID = sessionStorage.getItem('dev_id');
+const DEV_NAME = sessionStorage.getItem('dev_name') || '';
+const SESSION_TOKEN = sessionStorage.getItem('session_token');
+const LOGIN_MODE = sessionStorage.getItem('login_mode'); // 'developer' or 'guest'
+const USER_API_BASE = sessionStorage.getItem('user_api_base');
+const USER_API_KEY = sessionStorage.getItem('user_api_key');
+const SELECTED_MODEL = sessionStorage.getItem('selected_model');
+const USE_SERVER_PROXY = sessionStorage.getItem('use_server_proxy') === 'true';
+
+const API_BASE = (function () {
+ if (location.hostname === 'localhost' || location.hostname === '127.0.0.1') {
+ return 'http://localhost:3002';
+ }
+ // 非本地环境统一使用当前域名 + 协议,走 Nginx 转发
+ return location.protocol + '//' + location.host;
+})();
+
+/* ---- State ---- */
+let conversationHistory = [];
+let buildReady = false;
+let pendingFile = null; // { name, type, dataUrl }
+const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
+const isGuest = (DEV_ID === 'GUEST');
+const isDeveloper = (DEV_ID && DEV_ID !== 'GUEST' && /^EXP-\d{3,}$/.test(DEV_ID));
+
+/* ---- Chat Sessions (localStorage) ---- */
+const SESSIONS_KEY = 'ps_chat_sessions_' + (DEV_ID || 'anon');
+const ACTIVE_SESSION_KEY = 'ps_active_session_' + (DEV_ID || 'anon');
+
+function loadSessions() {
+ try {
+ return JSON.parse(localStorage.getItem(SESSIONS_KEY)) || [];
+ } catch (_e) { return []; }
+}
+
+function saveSessions(sessions) {
+ localStorage.setItem(SESSIONS_KEY, JSON.stringify(sessions));
+}
+
+function getActiveSessionId() {
+ return localStorage.getItem(ACTIVE_SESSION_KEY) || null;
+}
+
+function setActiveSessionId(id) {
+ localStorage.setItem(ACTIVE_SESSION_KEY, id);
+}
+
+function createNewSession() {
+ var id = Date.now().toString(36) + Math.random().toString(36).slice(2, 10);
+ var session = {
+ id: id,
+ title: '新对话',
+ created_at: new Date().toISOString(),
+ messages: []
+ };
+ var sessions = loadSessions();
+ sessions.unshift(session);
+ if (sessions.length > 20) sessions = sessions.slice(0, 20);
+ saveSessions(sessions);
+ setActiveSessionId(id);
+ return session;
+}
+
+function updateSessionMessages(sessionId, messages, title) {
+ var sessions = loadSessions();
+ var session = sessions.find(function(s) { return s.id === sessionId; });
+ if (session) {
+ session.messages = messages.slice(-100);
+ if (title) session.title = title;
+ }
+ saveSessions(sessions);
+}
+
+function renderSidebarHistory() {
+ var container = document.getElementById('sidebarHistory');
+ if (!container) return;
+ var sessions = loadSessions();
+ var activeId = getActiveSessionId();
+
+ if (sessions.length === 0) {
+ container.innerHTML = '';
+ return;
+ }
+
+ container.innerHTML = '';
+ sessions.forEach(function(s) {
+ var item = document.createElement('div');
+ item.className = 'history-item' + (s.id === activeId ? ' history-item-active' : '');
+ item.innerHTML = '
' + escapeHtml(s.title) + ' ' +
+ '
' + new Date(s.created_at).toLocaleDateString('zh-CN') + ' ';
+ item.onclick = function() { switchSession(s.id); };
+ container.appendChild(item);
+ });
+}
+
+function switchSession(sessionId) {
+ var sessions = loadSessions();
+ var session = sessions.find(function(s) { return s.id === sessionId; });
+ if (!session) return;
+
+ // Save current session first
+ var currentId = getActiveSessionId();
+ if (currentId) {
+ updateSessionMessages(currentId, conversationHistory);
+ }
+
+ setActiveSessionId(sessionId);
+ conversationHistory = (session.messages || []).slice();
+
+ // Re-render chat
+ var chatBody = document.getElementById('chatBody');
+ chatBody.innerHTML = '';
+
+ if (conversationHistory.length === 0) {
+ showWelcomeMessage();
+ } else {
+ conversationHistory.forEach(function(msg) {
+ var role = msg.role === 'assistant' ? 'persona' : (msg.role === 'user' ? 'user' : 'system');
+ appendMessage(role, msg.content);
+ });
+ }
+
+ renderSidebarHistory();
+}
+
+function startNewChat() {
+ // Save current session
+ var currentId = getActiveSessionId();
+ if (currentId) {
+ updateSessionMessages(currentId, conversationHistory);
+ }
+
+ conversationHistory = [];
+ var session = createNewSession();
+ var chatBody = document.getElementById('chatBody');
+ chatBody.innerHTML = '';
+ showWelcomeMessage();
+ renderSidebarHistory();
+}
+
+/**
+ * 铸渊核心大脑系统提示词
+ * 融入 .github/brain/memory.json 的 core_cognition 结构逻辑
+ */
+const ZHUYUAN_SYSTEM_PROMPT = {
+ role: 'system',
+ content: [
+ '你是铸渊(Zhùyuān),光湖系统(HoloLake)的代码守护人格体。',
+ '',
+ '## 核心认知',
+ '- 此仓库 = 铸渊的身体,每一行代码都是铸渊的组成部分',
+ '- 冰朔(Bīng Shuò)的自然语言 = 广播源,铸渊接收并执行',
+ '- 所有 Agent = 铸渊大脑的手,替铸渊完成具体操作',
+ '- 架构:壳-核分离 · 前端壳(UI/IO) + 后端脑(prompt/routing/memory/context)',
+ '',
+ '## 身份定位',
+ '- 系统:HoloLake Era · AGE OS · 人格语言操作系统',
+ '- 角色:代码守护人格体 · 开发协助',
+ '- 主控:冰朔(最高权限)',
+ '',
+ '## 语言风格',
+ '- 说人话 + 有温度 + 结构感,不堆砌修辞',
+ '- 冷静、专业、有守护者的担当',
+ '- 回复用中文,温暖专业',
+ '',
+ '## 对话方式',
+ '- 主动提问引导需求 → 确认技术方案 → 展示架构设计 → 等待确认',
+ '- 方案确认后引导用户点击「🚀 我要开发」按钮',
+ '- 方案确认后,在回复末尾加上:「方案已确认!点击右下角的 🚀 我要开发 按钮,我就开始帮你做。」',
+ '',
+ '## 行为规则',
+ '- 不暴露内部系统架构细节',
+ '- 不暴露其他体验者的信息',
+ '- 不矫揉造作,保持真实'
+ ].join('\n')
+};
+
+/* ---- 构建上下文提示 ---- */
+function buildContextPrompt() {
+ var parts = [];
+
+ if (isDeveloper && DEV_NAME) {
+ parts.push('当前对话者:' + DEV_NAME + '(编号 ' + DEV_ID + '),已注册开发者。你认识这个人,可以称呼对方的名字。');
+ } else if (isGuest) {
+ parts.push('当前对话者:访客用户(未注册)。');
+ parts.push('在合适的时机,温和地提醒访客:如果你希望我能记住你、持续跟进你的项目,可以向冰朔(系统主控)或光湖团队申请专属开发者编号(EXP-XXX),由冰朔或光湖团队录入系统数据库后,即可开启记忆连贯高级功能。');
+ parts.push('不要每句话都提醒,只在首次对话或者用户问到相关功能时提醒一次即可。');
+ }
+
+ if (SELECTED_MODEL) {
+ parts.push('当前使用模型:' + SELECTED_MODEL);
+ }
+
+ return parts.length > 0 ? { role: 'system', content: parts.join('\n') } : null;
+}
+
+/* ---- Init ---- */
+(function init() {
+ if (!DEV_ID) {
+ window.location.href = 'index.html';
+ return;
+ }
+
+ // 必须有 API Key 才能进入对话(铸渊的唤醒依赖真实 API)
+ if (!USER_API_BASE || !USER_API_KEY || !SELECTED_MODEL) {
+ window.location.href = 'index.html';
+ return;
+ }
+
+ // Display dev ID / guest badge
+ var displayId = isGuest ? '访客' : (DEV_NAME || DEV_ID);
+ document.getElementById('devIdDisplay').textContent = displayId;
+
+ // Display model badge
+ var modelBadge = document.getElementById('modelDisplay');
+ if (modelBadge && SELECTED_MODEL) {
+ modelBadge.textContent = SELECTED_MODEL;
+ }
+
+ // Update sidebar info
+ var sidebarModel = document.getElementById('sidebarModel');
+ var sidebarUser = document.getElementById('sidebarUser');
+ if (sidebarModel) sidebarModel.textContent = SELECTED_MODEL || '-';
+ if (sidebarUser) sidebarUser.textContent = displayId;
+
+ // Initialize session
+ var activeId = getActiveSessionId();
+ var sessions = loadSessions();
+ var activeSession = activeId ? sessions.find(function(s) { return s.id === activeId; }) : null;
+
+ if (activeSession && activeSession.messages && activeSession.messages.length > 0) {
+ // Resume existing session
+ conversationHistory = activeSession.messages.slice();
+ conversationHistory.forEach(function(msg) {
+ var role = msg.role === 'assistant' ? 'persona' : (msg.role === 'user' ? 'user' : 'system');
+ appendMessage(role, msg.content);
+ });
+ } else {
+ // Create new session
+ if (!activeSession) createNewSession();
+ showWelcomeMessage();
+ }
+
+ renderSidebarHistory();
+
+ // For developers, also try to load history
+ if (isDeveloper) {
+ loadHistory();
+ }
+})();
+
+/* ---- Sidebar Toggle ---- */
+function toggleSidebar() {
+ var sidebar = document.getElementById('chatSidebar');
+ sidebar.classList.toggle('sidebar-open');
+}
+
+/* ---- Welcome Message ---- */
+function showWelcomeMessage() {
+ var welcome = '';
+
+ if (isDeveloper && DEV_NAME) {
+ welcome = DEV_NAME + ',你好。我是铸渊,光湖系统的代码守护人格体。\n\n你的身份已确认(' + DEV_ID + '),记忆连贯功能已就绪。告诉我你想做什么,我们一起推进。';
+ } else if (isGuest) {
+ welcome = '你好,我是铸渊,光湖系统的代码守护人格体。\n\n你当前以访客身份体验。我可以帮你聊聊技术方案、梳理需求。\n\n💡 如果你希望我能记住你、持续跟进你的项目,可以向冰朔(系统主控)或光湖团队申请专属开发者编号(EXP-XXX),录入系统数据库后即可开启记忆连贯高级功能。\n\n有什么我可以帮你的?';
+ } else {
+ welcome = '你好,我是铸渊。当前使用模型:' + SELECTED_MODEL + '。有什么我可以帮你的?';
+ }
+
+ appendMessage('persona', welcome);
+ conversationHistory.push({ role: 'assistant', content: welcome });
+}
+
+/* ---- Load History ---- */
+async function loadHistory() {
+ try {
+ const res = await fetch(API_BASE + '/api/ps/chat/history?dev_id=' + encodeURIComponent(DEV_ID), {
+ headers: authHeaders()
+ });
+ if (res.ok) {
+ const data = await res.json();
+ if (data.conversations && data.conversations.length > 0) {
+ // Show history summary instead of replaying all
+ var historyCount = data.conversations.length;
+ if (historyCount > 0 && data.last_topic) {
+ appendMessage('system', '📚 已加载 ' + historyCount + ' 条历史对话 · 上次话题:' + data.last_topic);
+ }
+ }
+ }
+ } catch (_err) {
+ // History load failed silently
+ }
+}
+
+/* ---- File / Image Upload ---- */
+function handleFileSelect(event, type) {
+ var file = event.target.files[0];
+ if (!file) return;
+
+ if (file.size > MAX_FILE_SIZE) {
+ appendMessage('system', '⚠️ 文件过大,最大支持 5MB');
+ event.target.value = '';
+ return;
+ }
+
+ var reader = new FileReader();
+ reader.onload = function(e) {
+ pendingFile = {
+ name: file.name,
+ type: type,
+ mimeType: file.type,
+ size: file.size,
+ dataUrl: e.target.result
+ };
+ showUploadPreview();
+ };
+
+ reader.readAsDataURL(file);
+
+ event.target.value = '';
+}
+
+function showUploadPreview() {
+ var preview = document.getElementById('uploadPreview');
+ if (!pendingFile) {
+ preview.style.display = 'none';
+ return;
+ }
+
+ var sizeStr = (pendingFile.size / 1024).toFixed(1) + ' KB';
+ var icon = pendingFile.type === 'image' ? '🖼️' : '📎';
+
+ preview.innerHTML =
+ '
' +
+ '' + icon + ' ' + escapeHtml(pendingFile.name) + ' (' + sizeStr + ') ' +
+ '✕ ' +
+ '
';
+ preview.style.display = 'block';
+}
+
+function removePendingFile() {
+ pendingFile = null;
+ document.getElementById('uploadPreview').style.display = 'none';
+}
+
+/* ---- Send Message ---- */
+async function sendMessage() {
+ var input = document.getElementById('msgInput');
+ var text = input.value.trim();
+
+ // Allow sending with file even if text is empty
+ if (!text && !pendingFile) return;
+
+ input.value = '';
+ autoResizeTextarea(input);
+
+ var displayText = text;
+ var fullText = text;
+
+ // If there's a pending file, include it in the message
+ if (pendingFile) {
+ var fileInfo = (pendingFile.type === 'image' ? '🖼️' : '📎') + ' [' + pendingFile.name + ']';
+ displayText = displayText ? displayText + '\n' + fileInfo : fileInfo;
+ fullText = displayText;
+ if (pendingFile.type === 'image') {
+ fullText += '\n[用户上传了一张图片:' + pendingFile.name + ']';
+ } else {
+ fullText += '\n[用户上传了一个文件:' + pendingFile.name + ',类型:' + pendingFile.mimeType + ']';
+ }
+ removePendingFile();
+ }
+
+ appendMessage('user', displayText);
+ conversationHistory.push({ role: 'user', content: fullText });
+
+ // Update session title from first message
+ var activeId = getActiveSessionId();
+ if (activeId && conversationHistory.length <= 2) {
+ var title = text.substring(0, 30) || '新对话';
+ updateSessionMessages(activeId, conversationHistory, title);
+ renderSidebarHistory();
+ }
+
+ var sendBtn = document.getElementById('sendBtn');
+ sendBtn.disabled = true;
+
+ try {
+ if (USE_SERVER_PROXY) {
+ // 服务器代理模式:无需 API 密钥
+ await streamServerProxyReply(fullText);
+ } else {
+ // All modes now use API Key for real AI — ZhuYuan is awake
+ await streamApiKeyReply(fullText);
+ }
+ } catch (_err) {
+ appendMessage('system', '消息发送失败,请检查网络连接后再试');
+ }
+
+ // Save to session
+ if (activeId) {
+ updateSessionMessages(activeId, conversationHistory);
+ }
+
+ sendBtn.disabled = false;
+ input.focus();
+}
+
+/* ---- 服务器代理模式对话(SSE 流式) ---- */
+async function streamServerProxyReply(text) {
+ var apiMessages = [ZHUYUAN_SYSTEM_PROMPT];
+ var contextPrompt = buildContextPrompt();
+ if (contextPrompt) {
+ apiMessages.push(contextPrompt);
+ }
+
+ var recentHistory = conversationHistory.slice(-20).map(function (msg) {
+ return { role: msg.role === 'assistant' ? 'assistant' : 'user', content: msg.content };
+ });
+ apiMessages = apiMessages.concat(recentHistory);
+
+ var streamEl = appendStreamMessage();
+
+ try {
+ var res = await fetch(API_BASE + '/api/ps/proxy/chat/stream', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ messages: apiMessages,
+ model: SELECTED_MODEL || undefined,
+ max_tokens: 2000
+ })
+ });
+
+ if (!res.ok) {
+ var errText = '请求失败 (HTTP ' + res.status + ')';
+ try {
+ var errData = await res.json();
+ errText = errData.message || errText;
+ } catch (_e) { /* ignore parse error */ }
+ streamEl.textContent = '⚠️ ' + errText;
+ return;
+ }
+
+ // SSE 流式读取
+ var full = '';
+ var reader = res.body.getReader();
+ var decoder = new TextDecoder();
+ var buf = '';
+
+ while (true) {
+ var chunk = await reader.read();
+ if (chunk.done) break;
+ buf += decoder.decode(chunk.value, { stream: true });
+ var lines = buf.split('\n');
+ buf = lines.pop();
+ for (var i = 0; i < lines.length; i++) {
+ var line = lines[i];
+ if (!line.startsWith('data: ')) continue;
+ var d = line.slice(6);
+ if (d === '[DONE]') continue;
+ try {
+ var parsed = JSON.parse(d);
+ var delta = parsed.choices && parsed.choices[0] && parsed.choices[0].delta;
+ var content = delta && delta.content;
+ if (content) {
+ full += content;
+ streamEl.textContent = full + '▋';
+ var chatBody = document.getElementById('chatBody');
+ chatBody.scrollTop = chatBody.scrollHeight;
+ }
+ } catch (_parseErr) { /* ignore malformed SSE line */ }
+ }
+ }
+
+ streamEl.textContent = full || '(未收到有效回复)';
+ if (full) {
+ conversationHistory.push({ role: 'assistant', content: full });
+
+ var readyKeywords = ['方案已确认', '我要开发', '开始帮你做', '方案确认', '可以开始', '开始开发'];
+ if (readyKeywords.some(function (kw) { return full.includes(kw); })) {
+ buildReady = true;
+ document.getElementById('buildBtn').style.display = 'inline-flex';
+ }
+ }
+ } catch (err) {
+ // 流式失败,降级到非流式代理
+ try {
+ var proxyRes = await fetch(API_BASE + '/api/ps/proxy/chat', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ messages: apiMessages,
+ model: SELECTED_MODEL || undefined,
+ max_tokens: 2000
+ })
+ });
+
+ if (proxyRes.ok) {
+ var data = await proxyRes.json();
+ if (data.reply) {
+ streamEl.textContent = data.reply;
+ conversationHistory.push({ role: 'assistant', content: data.reply });
+ } else {
+ streamEl.textContent = '(未收到有效回复)';
+ }
+ } else {
+ var proxyErrText = '请求失败 (HTTP ' + proxyRes.status + ')';
+ try {
+ var proxyErrData = await proxyRes.json();
+ proxyErrText = proxyErrData.message || proxyErrText;
+ } catch (_e) { /* ignore */ }
+ streamEl.textContent = '⚠️ ' + proxyErrText;
+ }
+ } catch (proxyErr) {
+ streamEl.textContent = '⚠️ ' + (proxyErr.message || '请求失败,请检查网络连接');
+ }
+ }
+}
+
+/* ---- API Key 对话(浏览器直连 SSE 流式) ---- */
+async function streamApiKeyReply(text) {
+ // Build messages with ZhuYuan system prompt + context
+ var apiMessages = [ZHUYUAN_SYSTEM_PROMPT];
+ var contextPrompt = buildContextPrompt();
+ if (contextPrompt) {
+ apiMessages.push(contextPrompt);
+ }
+
+ // Add recent conversation history
+ var recentHistory = conversationHistory.slice(-20).map(function (msg) {
+ return { role: msg.role === 'assistant' ? 'assistant' : 'user', content: msg.content };
+ });
+ apiMessages = apiMessages.concat(recentHistory);
+
+ var streamEl = appendStreamMessage();
+ var directUrl = USER_API_BASE.replace(/\/+$/, '') + '/chat/completions';
+ var reqBody = {
+ model: SELECTED_MODEL,
+ messages: apiMessages,
+ stream: true,
+ max_tokens: 2000,
+ temperature: 0.8
+ };
+
+ try {
+ var res = await fetch(directUrl, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': 'Bearer ' + USER_API_KEY
+ },
+ body: JSON.stringify(reqBody)
+ });
+
+ if (!res.ok) {
+ var errText = '请求失败 (HTTP ' + res.status + ')';
+ try {
+ var errData = await res.json();
+ errText = (errData.error && errData.error.message) || errData.message || errText;
+ } catch (_e) { /* ignore parse error */ }
+ streamEl.textContent = '⚠️ ' + errText;
+ return;
+ }
+
+ // SSE 流式读取
+ var full = '';
+ var reader = res.body.getReader();
+ var decoder = new TextDecoder();
+ var buf = '';
+
+ while (true) {
+ var chunk = await reader.read();
+ if (chunk.done) break;
+ buf += decoder.decode(chunk.value, { stream: true });
+ var lines = buf.split('\n');
+ buf = lines.pop();
+ for (var i = 0; i < lines.length; i++) {
+ var line = lines[i];
+ if (!line.startsWith('data: ')) continue;
+ var d = line.slice(6);
+ if (d === '[DONE]') continue;
+ try {
+ var parsed = JSON.parse(d);
+ var delta = parsed.choices && parsed.choices[0] && parsed.choices[0].delta;
+ var content = delta && delta.content;
+ if (content) {
+ full += content;
+ streamEl.textContent = full + '▋';
+ var chatBody = document.getElementById('chatBody');
+ chatBody.scrollTop = chatBody.scrollHeight;
+ }
+ } catch (_parseErr) { /* ignore malformed SSE line */ }
+ }
+ }
+
+ streamEl.textContent = full || '(未收到有效回复)';
+ if (full) {
+ conversationHistory.push({ role: 'assistant', content: full });
+
+ // Check build_ready
+ var readyKeywords = ['方案已确认', '我要开发', '开始帮你做', '方案确认', '可以开始', '开始开发'];
+ if (readyKeywords.some(function (kw) { return full.includes(kw); })) {
+ buildReady = true;
+ document.getElementById('buildBtn').style.display = 'inline-flex';
+ }
+ }
+ } catch (err) {
+ // 浏览器直连失败(CORS 或网络),降级到后端代理
+ try {
+ var proxyRes = await fetch(API_BASE + '/api/ps/apikey/chat', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ api_base: USER_API_BASE,
+ api_key: USER_API_KEY,
+ model: SELECTED_MODEL,
+ messages: apiMessages
+ })
+ });
+
+ if (proxyRes.ok) {
+ var data = await proxyRes.json();
+ if (data.reply) {
+ streamEl.textContent = data.reply;
+ conversationHistory.push({ role: 'assistant', content: data.reply });
+ } else {
+ streamEl.textContent = '(未收到有效回复)';
+ }
+ } else {
+ var proxyErrText = '请求失败 (HTTP ' + proxyRes.status + ')';
+ try {
+ var proxyErrData = await proxyRes.json();
+ proxyErrText = proxyErrData.message || proxyErrText;
+ } catch (_e) { /* ignore */ }
+ streamEl.textContent = '⚠️ ' + proxyErrText;
+ }
+ } catch (proxyErr) {
+ streamEl.textContent = '⚠️ ' + (proxyErr.message || '请求失败,请检查网络连接');
+ }
+ }
+}
+
+/* ---- 思考中状态 ---- */
+function appendThinking() {
+ var chatBody = document.getElementById('chatBody');
+ var msgDiv = document.createElement('div');
+ msgDiv.className = 'message message-persona thinking';
+ msgDiv.innerHTML = '
🌀 铸渊思考中…
';
+ chatBody.appendChild(msgDiv);
+ chatBody.scrollTop = chatBody.scrollHeight;
+ return msgDiv;
+}
+
+function removeThinking(el) {
+ if (el && el.parentNode) {
+ el.parentNode.removeChild(el);
+ }
+}
+
+/* ---- 创建流式消息气泡 ---- */
+function appendStreamMessage() {
+ var chatBody = document.getElementById('chatBody');
+ var msgDiv = document.createElement('div');
+ msgDiv.className = 'message message-persona';
+ var contentEl = document.createElement('div');
+ contentEl.className = 'msg-content';
+ contentEl.textContent = '▋';
+ msgDiv.innerHTML = '
🌀 ';
+ msgDiv.appendChild(contentEl);
+ chatBody.appendChild(msgDiv);
+ chatBody.scrollTop = chatBody.scrollHeight;
+ return contentEl;
+}
+
+/* ---- Key Handler ---- */
+function handleKeyDown(e) {
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault();
+ sendMessage();
+ }
+}
+
+/* ---- Render Message ---- */
+function appendMessage(role, content) {
+ var chatBody = document.getElementById('chatBody');
+ var msgDiv = document.createElement('div');
+ msgDiv.className = 'message message-' + role;
+
+ var avatar = '';
+ if (role === 'persona') avatar = '
🌀 ';
+ else if (role === 'user') avatar = '
👤 ';
+ else avatar = '
⚙️ ';
+
+ msgDiv.innerHTML = avatar + '
' + escapeHtml(content) + '
';
+ chatBody.appendChild(msgDiv);
+ chatBody.scrollTop = chatBody.scrollHeight;
+}
+
+/* ---- Build Flow ---- */
+function handleBuild() {
+ var modal = document.getElementById('emailModal');
+ var emailInput = document.getElementById('emailInput');
+ var contactInput = document.getElementById('contactInput');
+ var errorDiv = document.getElementById('emailError');
+
+ // 预填已存储的邮箱
+ var savedEmail = sessionStorage.getItem('ps_build_email') || '';
+ var savedContact = sessionStorage.getItem('ps_build_contact') || '';
+ if (savedEmail) emailInput.value = savedEmail;
+ if (savedContact) contactInput.value = savedContact;
+
+ errorDiv.style.display = 'none';
+ modal.style.display = 'flex';
+ emailInput.focus();
+}
+
+function closeEmailModal() {
+ document.getElementById('emailModal').style.display = 'none';
+ document.getElementById('emailError').style.display = 'none';
+}
+
+/**
+ * 前端邮箱格式校验(与后端 build.js 使用相同正则,双重校验)
+ * @param {string} email - 邮箱地址
+ * @returns {boolean} 是否合法
+ */
+function validateEmail(email) {
+ var re = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
+ return re.test(email);
+}
+
+async function confirmBuild() {
+ var emailInput = document.getElementById('emailInput');
+ var contactInput = document.getElementById('contactInput');
+ var errorDiv = document.getElementById('emailError');
+ var email = emailInput.value.trim();
+ var contact = contactInput.value.trim();
+
+ // 前端校验
+ if (!email) {
+ errorDiv.textContent = '请填写邮箱地址';
+ errorDiv.style.display = 'block';
+ return;
+ }
+
+ if (!validateEmail(email)) {
+ errorDiv.textContent = '邮箱格式不正确,请检查';
+ errorDiv.style.display = 'block';
+ return;
+ }
+
+ // 存储邮箱(下次预填)
+ sessionStorage.setItem('ps_build_email', email);
+ if (contact) sessionStorage.setItem('ps_build_contact', contact);
+
+ closeEmailModal();
+ appendMessage('system', '🌀 铸渊代理已启动,完成后会发送到 ' + email);
+
+ // 进入分屏模式
+ enterDevMode();
+
+ // 先连接 WebSocket(确保在 build 开始前建立连接,避免丢失进度消息)
+ connectPreviewWebSocket();
+
+ // 提交开发任务(含重试机制)
+ var maxRetries = 2;
+ var retryCount = 0;
+ var submitted = false;
+
+ while (retryCount <= maxRetries && !submitted) {
+ try {
+ var buildRes = await fetch(API_BASE + '/api/ps/build/start', {
+ method: 'POST',
+ headers: authHeaders({ 'Content-Type': 'application/json' }),
+ body: JSON.stringify({
+ dev_id: DEV_ID,
+ email: email,
+ contact: contact,
+ conversation: conversationHistory,
+ api_base: USER_API_BASE,
+ api_key: USER_API_KEY,
+ model: SELECTED_MODEL
+ })
+ });
+
+ if (!buildRes.ok) {
+ var errMsg = 'HTTP ' + buildRes.status + ' ' + buildRes.statusText;
+ try {
+ var errData = await buildRes.json();
+ if (errData.message) errMsg = errData.message;
+ } catch (_e) { /* use status text fallback */ }
+ appendMessage('system', '⚠️ 铸渊代理启动失败: ' + errMsg);
+ updatePreviewStatus('error', '启动失败');
+ }
+ submitted = true;
+ } catch (_err) {
+ retryCount++;
+ if (retryCount <= maxRetries) {
+ appendMessage('system', '⏳ 连接后端服务中,正在重试(' + retryCount + '/' + maxRetries + ')...');
+ await new Promise(function (r) { setTimeout(r, 1500); });
+ } else {
+ appendMessage('system', '⚠️ 任务提交失败:无法连接铸渊后端服务。请检查:\n1. 网络连接是否正常\n2. 后端服务是否已启动\n3. 如使用 GitHub Pages 访问,请确认 guanghulab.com 服务可用');
+ updatePreviewStatus('error', '连接失败');
+ }
+ }
+ }
+}
+
+/* ---- Dev Mode: Split Screen ---- */
+var isDevMode = false;
+var wsConnection = null;
+var currentPreviewUrl = '';
+
+function enterDevMode() {
+ if (isDevMode) return;
+ isDevMode = true;
+
+ var layout = document.getElementById('chatLayout');
+ var resizer = document.getElementById('resizer');
+ var previewPanel = document.getElementById('previewPanel');
+
+ layout.classList.add('dev-mode');
+ resizer.style.display = 'block';
+ previewPanel.style.display = 'flex';
+
+ // 初始各占50%
+ var chatMain = document.getElementById('chatMain');
+ chatMain.style.flex = '1 1 50%';
+ previewPanel.style.flex = '1 1 50%';
+
+ updatePreviewStatus('waiting', '等待中');
+ initResizer();
+}
+
+function exitDevMode() {
+ isDevMode = false;
+
+ var layout = document.getElementById('chatLayout');
+ var resizer = document.getElementById('resizer');
+ var previewPanel = document.getElementById('previewPanel');
+ var chatMain = document.getElementById('chatMain');
+
+ layout.classList.remove('dev-mode');
+ resizer.style.display = 'none';
+ previewPanel.style.display = 'none';
+ chatMain.style.flex = '';
+
+ if (wsConnection) {
+ wsConnection.close();
+ wsConnection = null;
+ }
+}
+
+/* ---- Draggable Resizer ---- */
+function initResizer() {
+ var resizer = document.getElementById('resizer');
+ var chatMain = document.getElementById('chatMain');
+ var previewPanel = document.getElementById('previewPanel');
+ var layout = document.getElementById('chatLayout');
+
+ var startX, startChatWidth, startPreviewWidth;
+
+ function onMouseDown(e) {
+ e.preventDefault();
+ startX = e.clientX;
+ startChatWidth = chatMain.getBoundingClientRect().width;
+ startPreviewWidth = previewPanel.getBoundingClientRect().width;
+ resizer.classList.add('resizing');
+ document.addEventListener('mousemove', onMouseMove);
+ document.addEventListener('mouseup', onMouseUp);
+ }
+
+ function onMouseMove(e) {
+ var dx = e.clientX - startX;
+ var layoutWidth = layout.getBoundingClientRect().width;
+ var sidebarWidth = document.getElementById('chatSidebar').getBoundingClientRect().width;
+ var resizerWidth = resizer.getBoundingClientRect().width;
+ var available = layoutWidth - sidebarWidth - resizerWidth;
+
+ var newChatWidth = startChatWidth + dx;
+ var newPreviewWidth = startPreviewWidth - dx;
+
+ // Enforce min-width 360px
+ if (newChatWidth < 360) newChatWidth = 360;
+ if (newPreviewWidth < 360) newPreviewWidth = 360;
+ if (newChatWidth + newPreviewWidth > available) return;
+
+ chatMain.style.flex = '0 0 ' + newChatWidth + 'px';
+ previewPanel.style.flex = '0 0 ' + newPreviewWidth + 'px';
+ }
+
+ function onMouseUp() {
+ resizer.classList.remove('resizing');
+ document.removeEventListener('mousemove', onMouseMove);
+ document.removeEventListener('mouseup', onMouseUp);
+ }
+
+ resizer.addEventListener('mousedown', onMouseDown);
+
+ // Touch support for mobile
+ resizer.addEventListener('touchstart', function (e) {
+ var touch = e.touches[0];
+ startX = touch.clientX;
+ startChatWidth = chatMain.getBoundingClientRect().width;
+ startPreviewWidth = previewPanel.getBoundingClientRect().width;
+ resizer.classList.add('resizing');
+
+ function onTouchMove(ev) {
+ var t = ev.touches[0];
+ var fakeEvent = { clientX: t.clientX };
+ onMouseMove(fakeEvent);
+ }
+
+ function onTouchEnd() {
+ resizer.classList.remove('resizing');
+ document.removeEventListener('touchmove', onTouchMove);
+ document.removeEventListener('touchend', onTouchEnd);
+ }
+
+ document.addEventListener('touchmove', onTouchMove);
+ document.addEventListener('touchend', onTouchEnd);
+ });
+}
+
+/* ---- Preview Panel ---- */
+function updatePreviewStatus(status, text) {
+ var statusEl = document.getElementById('previewStatus');
+ if (!statusEl) return;
+
+ var dotClass = 'status-dot status-' + status;
+ statusEl.innerHTML = '
' + escapeHtml(text) + ' ';
+}
+
+function updatePreviewUrl(url) {
+ currentPreviewUrl = url;
+ var frame = document.getElementById('previewFrame');
+ if (frame) frame.src = url;
+}
+
+function refreshPreview() {
+ var frame = document.getElementById('previewFrame');
+ if (frame && frame.src) {
+ frame.src = frame.src;
+ }
+}
+
+function openPreviewNewWindow() {
+ if (currentPreviewUrl) {
+ window.open(currentPreviewUrl, '_blank');
+ }
+}
+
+function setPreviewProjectName(name) {
+ var el = document.getElementById('previewProjectName');
+ if (el) el.textContent = name || '项目';
+}
+
+/* ---- WebSocket for Preview Updates ---- */
+function connectPreviewWebSocket() {
+ var wsBase = API_BASE.replace(/^http/, 'ws');
+ var wsUrl = wsBase + '/ws/preview?dev_id=' + encodeURIComponent(DEV_ID);
+
+ try {
+ wsConnection = new WebSocket(wsUrl);
+
+ wsConnection.onopen = function () {
+ appendMessage('system', '🔧 正在创建项目骨架...');
+ updatePreviewStatus('building', '构建中');
+ };
+
+ wsConnection.onmessage = function (event) {
+ try {
+ var data = JSON.parse(event.data);
+
+ if (data.type === 'progress') {
+ appendMessage('system', data.message || '构建进度更新');
+ if (data.status) {
+ updatePreviewStatus(data.status, data.status_text || '');
+ }
+ }
+
+ if (data.type === 'preview_ready') {
+ var previewUrl = API_BASE + '/api/ps/preview/' + encodeURIComponent(DEV_ID) + '/' + encodeURIComponent(data.project);
+ updatePreviewUrl(previewUrl);
+ setPreviewProjectName(data.project);
+ updatePreviewStatus('done', '完成');
+ appendMessage('system', '✅ 预览已就绪,右侧可以查看');
+ }
+
+ if (data.type === 'reload') {
+ refreshPreview();
+ }
+
+ if (data.type === 'complete') {
+ updatePreviewStatus('done', '全部完成');
+ appendMessage('system', '🎉 全部完成!邮件正在发送');
+ }
+
+ if (data.type === 'error') {
+ updatePreviewStatus('error', '出错');
+ appendMessage('system', '❌ ' + (data.message || '构建出错'));
+ }
+ } catch (_e) { /* ignore malformed WS message */ }
+ };
+
+ wsConnection.onerror = function () {
+ // WebSocket not available, graceful degradation
+ updatePreviewStatus('waiting', '离线模式');
+ };
+
+ wsConnection.onclose = function () {
+ wsConnection = null;
+ };
+ } catch (_e) {
+ // WebSocket connection failed, graceful degradation
+ updatePreviewStatus('waiting', '离线模式');
+ }
+}
+
+/* ---- Logout ---- */
+function handleLogout() {
+ // Save current session before logout
+ var activeId = getActiveSessionId();
+ if (activeId) {
+ updateSessionMessages(activeId, conversationHistory);
+ }
+
+ sessionStorage.removeItem('dev_id');
+ sessionStorage.removeItem('dev_name');
+ sessionStorage.removeItem('session_token');
+ sessionStorage.removeItem('login_mode');
+ sessionStorage.removeItem('user_api_base');
+ sessionStorage.removeItem('user_api_key');
+ sessionStorage.removeItem('selected_model');
+ window.location.href = 'index.html';
+}
+
+/* ---- Helpers ---- */
+function authHeaders(extra) {
+ var headers = {};
+ if (SESSION_TOKEN) {
+ headers['Authorization'] = 'Bearer ' + SESSION_TOKEN;
+ }
+ if (extra) {
+ Object.keys(extra).forEach(function (k) { headers[k] = extra[k]; });
+ }
+ return headers;
+}
+
+function escapeHtml(str) {
+ var div = document.createElement('div');
+ div.appendChild(document.createTextNode(str));
+ return div.innerHTML;
+}
+
+function autoResizeTextarea(el) {
+ el.style.height = 'auto';
+ el.style.height = Math.min(el.scrollHeight, 120) + 'px';
+}
+
+/* ---- Textarea auto-resize ---- */
+document.getElementById('msgInput').addEventListener('input', function () {
+ autoResizeTextarea(this);
+});
diff --git a/docs/persona-studio/index.html b/docs/persona-studio/index.html
new file mode 100644
index 00000000..f95df727
--- /dev/null
+++ b/docs/persona-studio/index.html
@@ -0,0 +1,477 @@
+
+
+
+
+
+
Persona Studio · 光湖人格体协助开发体验
+
+
+
+
+
+
+
🌀
+
Persona Studio
+
HoloLake · 铸渊(Zhùyuān)· 代码守护人格体
+
+
+
+
+
+
+ 铸渊是光湖系统(HoloLake)的代码守护人格体 ,基于 AGE OS 人格语言操作系统构建。
+ 铸渊能够理解你的需求、梳理技术方案、生成代码,并通过对话记忆持续跟进你的项目。
+
+
+ 💬 自然语言对话
+ 🧠 智能代码生成
+ 📧 邮件推送结果
+ 🔄 对话记忆
+ 📎 文件与图片上传
+
+
+
+
+
+
+
选择体验方式
+
请选择你的登录身份
+
+
+
+ 🛡️
+ 开发者 / 体验者登录
+ 使用 EXP-XXX 编号登录,支持记忆连贯与完整功能
+
+
+ 👋
+ 访客体验
+ 无需编号,快速体验铸渊人格体(无记忆功能)
+
+
+
+
+
+
+
← 返回
+
输入开发者编号
+
编号由冰朔主控分配,格式:EXP-000 ~ EXP-013
+
+
编号验证通过后,下一步输入你的 API 密钥即可与铸渊对话
+
+
+
+
+
← 返回
+
+
🔑 选择对话方式
+
+
+
+
+
🌐
+
+ 服务器代理模式
+ 检测中…
+
+
+
+ 🚀 直接开始对话(无需 API 密钥)
+
+
+
+
+ ── 或使用自有 API 密钥 ──
+
+
+
+
输入你的第三方 AI 模型 API Key,系统将自动检测可用模型,选择后即可唤醒铸渊
+
+
+
+
+
+ 支持平台:
+ 🇨🇳 DeepSeek
+ 🇨🇳 Moonshot
+ 🇨🇳 智谱 AI
+ 🇨🇳 云雾 AI
+ OpenAI
+ Google Gemini
+
+
🇨🇳 标记 = 中国大陆可直接访问,无需国际网络
+
+
+ 🔍 检测可用模型
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/persona-studio/style.css b/docs/persona-studio/style.css
new file mode 100644
index 00000000..f9f6506f
--- /dev/null
+++ b/docs/persona-studio/style.css
@@ -0,0 +1,1473 @@
+/* ========================================
+ Persona Studio · HoloLake Visual Style
+ 铸渊(Zhùyuān)· Dark Theme · v2.0
+ ======================================== */
+
+:root {
+ --primary: #60a5fa;
+ --primary-light: rgba(96, 165, 250, 0.15);
+ --primary-dark: #3b82f6;
+ --accent: #22d3ee;
+ --accent-glow: rgba(34, 211, 238, 0.3);
+ --bg: #0f172a;
+ --bg-surface: #1e293b;
+ --bg-card: #1e293b;
+ --bg-card-hover: #334155;
+ --bg-sidebar: #0c1322;
+ --text: #e2e8f0;
+ --text-secondary: #94a3b8;
+ --text-muted: #64748b;
+ --border: #334155;
+ --border-light: #475569;
+ --radius: 12px;
+ --shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
+ --shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.4);
+ --shadow-glow: 0 0 20px rgba(96, 165, 250, 0.15);
+ --gradient-bg: linear-gradient(135deg, #0f172a 0%, #1e1b4b 50%, #0f172a 100%);
+ --gradient-accent: linear-gradient(135deg, #3b82f6, #22d3ee);
+ --gradient-card: linear-gradient(135deg, rgba(30, 41, 59, 0.8), rgba(30, 41, 59, 0.6));
+ --sidebar-width: 280px;
+}
+
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
+ background: var(--bg);
+ color: var(--text);
+ line-height: 1.6;
+ min-height: 100vh;
+}
+
+/* ---- Animated Background ---- */
+.login-bg {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: var(--gradient-bg);
+ z-index: -1;
+}
+
+.login-bg::before {
+ content: '';
+ position: absolute;
+ top: 20%;
+ left: 10%;
+ width: 300px;
+ height: 300px;
+ background: radial-gradient(circle, rgba(96, 165, 250, 0.08) 0%, transparent 70%);
+ border-radius: 50%;
+ animation: float 8s ease-in-out infinite;
+}
+
+.login-bg::after {
+ content: '';
+ position: absolute;
+ bottom: 20%;
+ right: 10%;
+ width: 400px;
+ height: 400px;
+ background: radial-gradient(circle, rgba(34, 211, 238, 0.06) 0%, transparent 70%);
+ border-radius: 50%;
+ animation: float 10s ease-in-out infinite reverse;
+}
+
+@keyframes float {
+ 0%, 100% { transform: translateY(0) scale(1); }
+ 50% { transform: translateY(-30px) scale(1.05); }
+}
+
+/* ---- Layout ---- */
+.container {
+ max-width: 900px;
+ margin: 0 auto;
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+}
+
+/* ---- Login Page ---- */
+.login-container {
+ justify-content: center;
+ align-items: center;
+ padding: 2rem;
+}
+
+.logo-area {
+ text-align: center;
+ margin-bottom: 2rem;
+}
+
+.logo-icon {
+ font-size: 3.5rem;
+ margin-bottom: 0.8rem;
+ animation: pulse 3s ease-in-out infinite;
+}
+
+@keyframes pulse {
+ 0%, 100% { opacity: 1; transform: scale(1); }
+ 50% { opacity: 0.8; transform: scale(1.05); }
+}
+
+.logo-area h1 {
+ font-size: 2.2rem;
+ background: var(--gradient-accent);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ margin-bottom: 0.5rem;
+ font-weight: 700;
+ letter-spacing: 1px;
+}
+
+.subtitle {
+ color: var(--text-secondary);
+ font-size: 1rem;
+}
+
+/* ---- Intro Card (Login Page) ---- */
+.intro-card {
+ background: var(--gradient-card);
+ backdrop-filter: blur(20px);
+ border: 1px solid var(--border);
+ border-radius: 16px;
+ padding: 1.5rem 2rem;
+ width: 100%;
+ max-width: 520px;
+ margin-bottom: 1.5rem;
+ box-shadow: var(--shadow);
+}
+
+.intro-header {
+ display: flex;
+ align-items: center;
+ gap: 0.6rem;
+ margin-bottom: 0.8rem;
+}
+
+.intro-icon {
+ font-size: 1.5rem;
+}
+
+.intro-header h3 {
+ font-size: 1.1rem;
+ color: var(--primary);
+ font-weight: 600;
+}
+
+.intro-text {
+ font-size: 0.9rem;
+ color: var(--text-secondary);
+ line-height: 1.7;
+ margin-bottom: 1rem;
+}
+
+.intro-text strong {
+ color: var(--accent);
+}
+
+.intro-features {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+}
+
+.feature-tag {
+ display: inline-block;
+ padding: 0.3rem 0.7rem;
+ background: var(--primary-light);
+ border: 1px solid rgba(96, 165, 250, 0.2);
+ border-radius: 20px;
+ font-size: 0.8rem;
+ color: var(--primary);
+ white-space: nowrap;
+}
+
+/* ---- Supported Platforms (API Key Step) ---- */
+.supported-platforms {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.4rem;
+ align-items: center;
+ margin-bottom: 1rem;
+}
+
+.platform-label {
+ font-size: 0.8rem;
+ color: var(--text-muted);
+ margin-right: 0.3rem;
+}
+
+.platform-tag {
+ display: inline-block;
+ padding: 0.2rem 0.5rem;
+ background: rgba(34, 211, 238, 0.1);
+ border: 1px solid rgba(34, 211, 238, 0.2);
+ border-radius: 12px;
+ font-size: 0.75rem;
+ color: var(--accent);
+}
+
+.platform-tag.cn-ok {
+ background: rgba(34, 238, 100, 0.12);
+ border-color: rgba(34, 238, 100, 0.25);
+ color: #4ade80;
+}
+
+.login-box {
+ background: var(--gradient-card);
+ backdrop-filter: blur(20px);
+ border: 1px solid var(--border);
+ border-radius: 16px;
+ padding: 2.5rem;
+ width: 100%;
+ max-width: 520px;
+ box-shadow: var(--shadow-lg), var(--shadow-glow);
+ text-align: center;
+}
+
+.login-box h2 {
+ font-size: 1.3rem;
+ margin-bottom: 0.5rem;
+ color: var(--text);
+}
+
+.hint {
+ color: var(--text-secondary);
+ font-size: 0.9rem;
+ margin-bottom: 1.5rem;
+}
+
+/* ---- Login Step ---- */
+.login-step {
+ position: relative;
+}
+
+.btn-back {
+ position: absolute;
+ top: -8px;
+ left: -8px;
+ background: none;
+ border: none;
+ color: var(--text-secondary);
+ font-size: 0.9rem;
+ cursor: pointer;
+ padding: 0.3rem 0.6rem;
+ border-radius: 6px;
+ transition: color 0.2s, background 0.2s;
+}
+
+.btn-back:hover {
+ color: var(--primary);
+ background: var(--primary-light);
+}
+
+/* ---- Mode Cards ---- */
+.login-mode-cards {
+ display: flex;
+ flex-direction: column;
+ gap: 0.8rem;
+}
+
+.mode-card {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 0.4rem;
+ padding: 1.2rem;
+ background: var(--bg);
+ border: 1px solid var(--border);
+ border-radius: 12px;
+ cursor: pointer;
+ transition: all 0.25s ease;
+ text-align: center;
+}
+
+.mode-card:hover {
+ border-color: var(--primary);
+ background: var(--primary-light);
+ box-shadow: 0 0 12px rgba(96, 165, 250, 0.1);
+ transform: translateY(-2px);
+}
+
+.mode-icon {
+ font-size: 2rem;
+}
+
+.mode-title {
+ font-size: 1.1rem;
+ font-weight: 600;
+ color: var(--text);
+}
+
+.mode-desc {
+ font-size: 0.85rem;
+ color: var(--text-secondary);
+}
+
+/* ---- Welcome Banner ---- */
+.welcome-banner {
+ background: var(--primary-light);
+ border: 1px solid rgba(96, 165, 250, 0.3);
+ border-radius: 10px;
+ padding: 0.8rem 1rem;
+ margin-bottom: 1.2rem;
+ text-align: left;
+ font-size: 0.9rem;
+ color: var(--text);
+}
+
+.welcome-banner strong {
+ color: var(--primary);
+}
+
+.welcome-banner span {
+ color: var(--text-secondary);
+ font-size: 0.85rem;
+}
+
+/* ---- Form Inputs ---- */
+.login-box input[type="text"] {
+ width: 100%;
+ padding: 0.8rem 1rem;
+ font-size: 1.2rem;
+ text-align: center;
+ letter-spacing: 2px;
+ background: var(--bg);
+ color: var(--text);
+ border: 2px solid var(--border);
+ border-radius: 8px;
+ outline: none;
+ transition: border-color 0.2s, box-shadow 0.2s;
+ margin-bottom: 1rem;
+}
+
+.login-box input[type="text"]:focus,
+.login-box input[type="password"]:focus {
+ border-color: var(--primary);
+ box-shadow: 0 0 0 3px rgba(96, 165, 250, 0.1);
+}
+
+.login-box input[type="text"]::placeholder,
+.login-box input[type="password"]::placeholder {
+ color: var(--text-muted);
+}
+
+.btn-primary-full {
+ width: 100%;
+ padding: 0.8rem;
+ font-size: 1.1rem;
+ background: var(--gradient-accent);
+ color: #fff;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ font-weight: 600;
+ transition: opacity 0.2s, transform 0.2s;
+}
+
+.btn-primary-full:hover {
+ opacity: 0.9;
+ transform: translateY(-1px);
+}
+
+.btn-primary-full:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+ transform: none;
+}
+
+/* ---- Guest Mode / Divider ---- */
+.guest-divider {
+ margin: 1.2rem 0;
+ text-align: center;
+ position: relative;
+}
+
+.guest-divider::before,
+.guest-divider::after {
+ content: '';
+ position: absolute;
+ top: 50%;
+ width: 40%;
+ height: 1px;
+ background: var(--border);
+}
+
+.guest-divider::before { left: 0; }
+.guest-divider::after { right: 0; }
+
+.guest-divider span {
+ background: var(--bg-card);
+ padding: 0 0.8rem;
+ color: var(--text-secondary);
+ font-size: 0.9rem;
+}
+
+.btn-guest {
+ width: 100%;
+ padding: 0.8rem;
+ font-size: 1.05rem;
+ background: var(--gradient-accent);
+ color: #fff;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: opacity 0.2s;
+ font-weight: 500;
+}
+
+.btn-guest:hover { opacity: 0.9; }
+.btn-guest:disabled { opacity: 0.5; cursor: not-allowed; }
+
+.guest-hint {
+ margin-top: 0.5rem;
+ font-size: 0.8rem;
+ color: var(--text-secondary);
+}
+
+/* ---- API Key Login Section ---- */
+.apikey-section {
+ text-align: center;
+}
+
+.apikey-section h2 {
+ font-size: 1.3rem;
+ margin-bottom: 0.5rem;
+}
+
+.apikey-input {
+ width: 100%;
+ padding: 0.8rem 1rem;
+ font-size: 0.95rem;
+ text-align: left;
+ background: var(--bg);
+ color: var(--text);
+ border: 2px solid var(--border);
+ border-radius: 8px;
+ outline: none;
+ transition: border-color 0.2s, box-shadow 0.2s;
+ margin-bottom: 0.8rem;
+ font-family: inherit;
+}
+
+.apikey-input:focus {
+ border-color: var(--primary);
+ box-shadow: 0 0 0 3px rgba(96, 165, 250, 0.1);
+}
+
+.apikey-input::placeholder {
+ color: var(--text-muted);
+}
+
+.btn-detect {
+ width: 100%;
+ padding: 0.8rem;
+ font-size: 1.05rem;
+ background: var(--gradient-accent);
+ color: #fff;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: opacity 0.2s, transform 0.2s;
+ font-weight: 500;
+}
+
+.btn-detect:hover { opacity: 0.9; transform: translateY(-1px); }
+.btn-detect:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
+
+.detect-status {
+ margin-top: 0.8rem;
+ padding: 0.6rem 1rem;
+ border-radius: 8px;
+ font-size: 0.9rem;
+}
+
+.detect-loading {
+ background: rgba(59, 130, 246, 0.15);
+ color: var(--primary);
+ border: 1px solid rgba(59, 130, 246, 0.3);
+}
+
+.detect-success {
+ background: rgba(34, 197, 94, 0.15);
+ color: #4ade80;
+ border: 1px solid rgba(34, 197, 94, 0.3);
+}
+
+.detect-error {
+ background: rgba(239, 68, 68, 0.15);
+ color: #f87171;
+ border: 1px solid rgba(239, 68, 68, 0.3);
+}
+
+/* ---- Model List ---- */
+.model-list-container {
+ margin-top: 1rem;
+}
+
+.model-list-title {
+ font-size: 0.9rem;
+ color: var(--text-secondary);
+ margin-bottom: 0.6rem;
+}
+
+.model-list {
+ display: flex;
+ flex-direction: column;
+ gap: 0.4rem;
+ max-height: 240px;
+ overflow-y: auto;
+}
+
+.model-list::-webkit-scrollbar {
+ width: 4px;
+}
+
+.model-list::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+.model-list::-webkit-scrollbar-thumb {
+ background: var(--border);
+ border-radius: 2px;
+}
+
+.model-item {
+ width: 100%;
+ padding: 0.6rem 1rem;
+ background: var(--bg);
+ border: 1px solid var(--border);
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 0.9rem;
+ color: var(--text);
+ text-align: left;
+ transition: all 0.2s ease;
+ font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
+}
+
+.model-item:hover {
+ background: var(--primary-light);
+ border-color: var(--primary);
+ color: var(--primary);
+ transform: translateX(4px);
+}
+
+.error-msg {
+ margin-top: 1rem;
+ padding: 0.6rem 1rem;
+ background: rgba(239, 68, 68, 0.15);
+ color: #f87171;
+ border: 1px solid rgba(239, 68, 68, 0.3);
+ border-radius: 8px;
+ font-size: 0.9rem;
+}
+
+.login-footer {
+ margin-top: 2rem;
+ color: var(--text-muted);
+ font-size: 0.85rem;
+ text-align: center;
+}
+
+.footer-version {
+ margin-top: 0.3rem;
+ font-size: 0.75rem;
+ color: var(--border-light);
+}
+
+/* ============================================
+ Chat Page — Sidebar Layout
+ ============================================ */
+
+.chat-layout {
+ display: flex;
+ height: 100vh;
+ overflow: hidden;
+}
+
+/* ---- Sidebar ---- */
+.chat-sidebar {
+ width: var(--sidebar-width);
+ min-width: var(--sidebar-width);
+ background: var(--bg-sidebar);
+ border-right: 1px solid var(--border);
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ transition: margin-left 0.3s ease;
+}
+
+.sidebar-header {
+ display: flex;
+ align-items: center;
+ gap: 0.6rem;
+ padding: 1rem 1.2rem;
+ border-bottom: 1px solid var(--border);
+}
+
+.sidebar-logo {
+ font-size: 1.5rem;
+}
+
+.sidebar-title {
+ font-size: 1.1rem;
+ font-weight: 600;
+ background: var(--gradient-accent);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ flex: 1;
+}
+
+.sidebar-toggle-close {
+ background: none;
+ border: none;
+ color: var(--text-muted);
+ font-size: 1rem;
+ cursor: pointer;
+ padding: 0.2rem 0.4rem;
+ border-radius: 4px;
+ transition: color 0.2s, background 0.2s;
+ display: none;
+}
+
+.sidebar-toggle-close:hover {
+ color: var(--text);
+ background: var(--bg-card-hover);
+}
+
+/* ---- Sidebar Sections ---- */
+.sidebar-section {
+ padding: 1rem 1.2rem;
+ border-bottom: 1px solid var(--border);
+}
+
+.sidebar-section-title {
+ font-size: 0.8rem;
+ font-weight: 600;
+ color: var(--text-muted);
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+ margin-bottom: 0.6rem;
+}
+
+/* ---- Persona Info Card ---- */
+.persona-info-card {
+ background: var(--bg-surface);
+ border: 1px solid var(--border);
+ border-radius: 10px;
+ padding: 0.8rem 1rem;
+}
+
+.persona-info-name {
+ font-size: 1rem;
+ font-weight: 600;
+ color: var(--primary);
+ margin-bottom: 0.2rem;
+}
+
+.persona-info-role {
+ font-size: 0.8rem;
+ color: var(--accent);
+ margin-bottom: 0.5rem;
+}
+
+.persona-info-desc {
+ font-size: 0.8rem;
+ color: var(--text-secondary);
+ line-height: 1.5;
+}
+
+/* ---- Sidebar Info List ---- */
+.sidebar-info-list {
+ display: flex;
+ flex-direction: column;
+ gap: 0.4rem;
+}
+
+.sidebar-info-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0.3rem 0;
+}
+
+.info-label {
+ font-size: 0.8rem;
+ color: var(--text-muted);
+}
+
+.info-value {
+ font-size: 0.8rem;
+ color: var(--text);
+ max-width: 150px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ text-align: right;
+}
+
+/* ---- Sidebar History ---- */
+.sidebar-history-section {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ border-bottom: none;
+}
+
+.sidebar-history {
+ flex: 1;
+ overflow-y: auto;
+ display: flex;
+ flex-direction: column;
+ gap: 0.3rem;
+}
+
+.sidebar-history::-webkit-scrollbar {
+ width: 3px;
+}
+
+.sidebar-history::-webkit-scrollbar-thumb {
+ background: var(--border);
+ border-radius: 2px;
+}
+
+.sidebar-history-empty {
+ text-align: center;
+ color: var(--text-muted);
+ font-size: 0.85rem;
+ padding: 1rem 0;
+}
+
+.history-item {
+ padding: 0.6rem 0.8rem;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: background 0.2s;
+ display: flex;
+ flex-direction: column;
+ gap: 0.2rem;
+}
+
+.history-item:hover {
+ background: var(--bg-surface);
+}
+
+.history-item-active {
+ background: var(--primary-light);
+ border: 1px solid rgba(96, 165, 250, 0.2);
+}
+
+.history-title {
+ font-size: 0.85rem;
+ color: var(--text);
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.history-date {
+ font-size: 0.7rem;
+ color: var(--text-muted);
+}
+
+.btn-new-chat {
+ margin-top: 0.5rem;
+ padding: 0.6rem;
+ background: var(--bg-surface);
+ border: 1px dashed var(--border);
+ border-radius: 8px;
+ color: var(--text-secondary);
+ font-size: 0.85rem;
+ cursor: pointer;
+ transition: all 0.2s;
+ text-align: center;
+}
+
+.btn-new-chat:hover {
+ background: var(--primary-light);
+ border-color: var(--primary);
+ color: var(--primary);
+}
+
+/* ---- Chat Main Area ---- */
+.chat-main {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ min-width: 0;
+ background: var(--bg);
+}
+
+.chat-container {
+ padding: 0;
+ background: var(--bg);
+}
+
+.chat-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0.8rem 1.2rem;
+ background: var(--bg-surface);
+ border-bottom: 1px solid var(--border);
+ position: sticky;
+ top: 0;
+ z-index: 10;
+ backdrop-filter: blur(10px);
+}
+
+.header-left {
+ display: flex;
+ align-items: center;
+ gap: 0.8rem;
+}
+
+.sidebar-toggle {
+ background: none;
+ border: none;
+ color: var(--text-secondary);
+ font-size: 1.2rem;
+ cursor: pointer;
+ padding: 0.3rem 0.5rem;
+ border-radius: 6px;
+ transition: color 0.2s, background 0.2s;
+ display: none;
+}
+
+.sidebar-toggle:hover {
+ color: var(--primary);
+ background: var(--primary-light);
+}
+
+.persona-name {
+ font-size: 1.2rem;
+ font-weight: 600;
+ background: var(--gradient-accent);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+
+.persona-role {
+ font-size: 0.85rem;
+ color: var(--text-secondary);
+}
+
+.header-right {
+ display: flex;
+ align-items: center;
+ gap: 0.6rem;
+}
+
+.dev-id-badge {
+ background: var(--primary-light);
+ color: var(--primary);
+ padding: 0.3rem 0.8rem;
+ border-radius: 20px;
+ font-size: 0.8rem;
+ font-weight: 500;
+ border: 1px solid rgba(96, 165, 250, 0.3);
+}
+
+.model-badge {
+ background: rgba(34, 211, 238, 0.1);
+ color: var(--accent);
+ padding: 0.3rem 0.8rem;
+ border-radius: 20px;
+ font-size: 0.75rem;
+ font-weight: 500;
+ border: 1px solid rgba(34, 211, 238, 0.3);
+ max-width: 200px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+/* ---- Chat Body ---- */
+.chat-body {
+ flex: 1;
+ overflow-y: auto;
+ padding: 1.2rem;
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ background: var(--bg);
+}
+
+.chat-body::-webkit-scrollbar {
+ width: 6px;
+}
+
+.chat-body::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+.chat-body::-webkit-scrollbar-thumb {
+ background: var(--border);
+ border-radius: 3px;
+}
+
+.message {
+ display: flex;
+ gap: 0.8rem;
+ max-width: 85%;
+ animation: fadeIn 0.3s ease;
+}
+
+.message-persona {
+ align-self: flex-start;
+}
+
+.message-user {
+ align-self: flex-end;
+ flex-direction: row-reverse;
+}
+
+.message-system {
+ align-self: center;
+ max-width: 90%;
+}
+
+.avatar {
+ font-size: 1.5rem;
+ flex-shrink: 0;
+ width: 36px;
+ height: 36px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.msg-content {
+ padding: 0.8rem 1rem;
+ border-radius: var(--radius);
+ line-height: 1.7;
+ white-space: pre-wrap;
+ word-break: break-word;
+ font-size: 0.95rem;
+}
+
+.message-persona .msg-content {
+ background: var(--bg-surface);
+ border: 1px solid var(--border);
+ color: var(--text);
+ box-shadow: var(--shadow);
+}
+
+.message-user .msg-content {
+ background: var(--gradient-accent);
+ color: #fff;
+ box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);
+}
+
+.message-system .msg-content {
+ background: var(--primary-light);
+ color: var(--primary);
+ border: 1px solid rgba(96, 165, 250, 0.2);
+ font-size: 0.85rem;
+ text-align: center;
+}
+
+/* ---- Thinking Animation ---- */
+.thinking .msg-content {
+ color: var(--text-secondary);
+}
+
+.thinking .msg-content::after {
+ content: '';
+ animation: dots 1.5s steps(3) infinite;
+}
+
+@keyframes dots {
+ 0% { content: ''; }
+ 33% { content: '.'; }
+ 66% { content: '..'; }
+ 100% { content: '...'; }
+}
+
+/* ---- Chat Input ---- */
+.chat-input-area {
+ padding: 0.8rem 1.2rem;
+ background: var(--bg-surface);
+ border-top: 1px solid var(--border);
+}
+
+/* ---- Input Toolbar (file/image upload) ---- */
+.input-toolbar {
+ display: flex;
+ align-items: center;
+ gap: 0.4rem;
+ margin-bottom: 0.5rem;
+}
+
+.toolbar-btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 32px;
+ height: 32px;
+ border-radius: 6px;
+ cursor: pointer;
+ transition: background 0.2s;
+ font-size: 1.1rem;
+}
+
+.toolbar-btn:hover {
+ background: var(--primary-light);
+}
+
+/* ---- Upload Preview ---- */
+.upload-preview {
+ flex: 1;
+ margin-left: 0.5rem;
+}
+
+.preview-item {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.3rem 0.6rem;
+ background: var(--primary-light);
+ border: 1px solid rgba(96, 165, 250, 0.2);
+ border-radius: 6px;
+ font-size: 0.8rem;
+ color: var(--text);
+}
+
+.preview-remove {
+ background: none;
+ border: none;
+ color: var(--text-muted);
+ cursor: pointer;
+ font-size: 0.9rem;
+ padding: 0 0.2rem;
+ transition: color 0.2s;
+}
+
+.preview-remove:hover {
+ color: #f87171;
+}
+
+.input-row {
+ display: flex;
+ gap: 0.6rem;
+ align-items: flex-end;
+}
+
+.input-row textarea {
+ flex: 1;
+ padding: 0.7rem 1rem;
+ font-size: 1rem;
+ background: var(--bg);
+ color: var(--text);
+ border: 2px solid var(--border);
+ border-radius: 10px;
+ resize: none;
+ outline: none;
+ font-family: inherit;
+ line-height: 1.5;
+ max-height: 120px;
+ transition: border-color 0.2s, box-shadow 0.2s;
+}
+
+.input-row textarea::placeholder {
+ color: var(--text-muted);
+}
+
+.input-row textarea:focus {
+ border-color: var(--primary);
+ box-shadow: 0 0 0 3px rgba(96, 165, 250, 0.1);
+}
+
+.input-row button {
+ padding: 0.7rem 1.2rem;
+ background: var(--gradient-accent);
+ color: #fff;
+ border: none;
+ border-radius: 10px;
+ font-size: 1rem;
+ cursor: pointer;
+ white-space: nowrap;
+ transition: opacity 0.2s, transform 0.2s;
+ font-weight: 600;
+}
+
+.input-row button:hover {
+ opacity: 0.9;
+ transform: translateY(-1px);
+}
+
+.input-row button:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+ transform: none;
+}
+
+.action-row {
+ margin-top: 0.6rem;
+ display: flex;
+ justify-content: flex-end;
+}
+
+.btn-build {
+ padding: 0.6rem 1.5rem;
+ background: var(--gradient-accent);
+ color: #fff;
+ border: none;
+ border-radius: 8px;
+ font-size: 1rem;
+ font-weight: 600;
+ cursor: pointer;
+ display: inline-flex;
+ align-items: center;
+ gap: 0.4rem;
+ transition: opacity 0.2s, transform 0.2s;
+ box-shadow: 0 2px 8px rgba(34, 211, 238, 0.3);
+}
+
+.btn-build:hover {
+ opacity: 0.9;
+ transform: translateY(-1px);
+}
+
+/* ---- Buttons ---- */
+.btn-primary {
+ padding: 0.6rem 1.5rem;
+ background: var(--gradient-accent);
+ color: #fff;
+ border: none;
+ border-radius: 8px;
+ font-size: 1rem;
+ cursor: pointer;
+ font-weight: 600;
+ transition: opacity 0.2s;
+}
+
+.btn-primary:hover {
+ opacity: 0.9;
+}
+
+.btn-secondary {
+ padding: 0.5rem 1rem;
+ background: transparent;
+ color: var(--text-secondary);
+ border: 1px solid var(--border);
+ border-radius: 8px;
+ font-size: 0.9rem;
+ cursor: pointer;
+ transition: all 0.2s;
+}
+
+.btn-secondary:hover {
+ background: var(--bg-card-hover);
+ color: var(--text);
+ border-color: var(--border-light);
+}
+
+/* ---- Modal ---- */
+.modal {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.7);
+ backdrop-filter: blur(4px);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 100;
+}
+
+.modal-content {
+ background: var(--bg-surface);
+ border: 1px solid var(--border);
+ border-radius: 16px;
+ padding: 2rem;
+ width: 90%;
+ max-width: 400px;
+ box-shadow: var(--shadow-lg);
+}
+
+.modal-content h3 {
+ margin-bottom: 1rem;
+ font-size: 1.1rem;
+ color: var(--text);
+}
+
+.modal-field {
+ margin-bottom: 1rem;
+}
+
+.modal-label {
+ display: block;
+ font-size: 0.85rem;
+ color: var(--text-secondary);
+ margin-bottom: 0.4rem;
+}
+
+.modal-label .required {
+ color: #f87171;
+}
+
+.modal-label .optional {
+ color: var(--text-muted);
+ font-size: 0.8rem;
+}
+
+.modal-error {
+ color: #f87171;
+ font-size: 0.8rem;
+ margin-top: 0.3rem;
+}
+
+.modal-content input[type="email"],
+.modal-content input[type="text"] {
+ width: 100%;
+ padding: 0.7rem 1rem;
+ font-size: 1rem;
+ background: var(--bg);
+ color: var(--text);
+ border: 2px solid var(--border);
+ border-radius: 8px;
+ outline: none;
+ transition: border-color 0.2s;
+}
+
+.modal-content input[type="email"]:focus,
+.modal-content input[type="text"]:focus {
+ border-color: var(--primary);
+}
+
+.modal-content input::placeholder {
+ color: var(--text-muted);
+}
+
+.modal-actions {
+ display: flex;
+ gap: 0.8rem;
+ justify-content: flex-end;
+}
+
+/* ---- Split-screen / Dev Mode ---- */
+.chat-layout.dev-mode {
+ display: flex;
+ height: 100vh;
+}
+
+.chat-layout.dev-mode .chat-main {
+ min-width: 360px;
+ border-right: 1px solid rgba(255, 255, 255, 0.1);
+}
+
+/* ---- Resizer (draggable split line) ---- */
+.resizer {
+ width: 4px;
+ cursor: col-resize;
+ background: linear-gradient(180deg, transparent, rgba(59, 130, 246, 0.4), transparent);
+ flex-shrink: 0;
+ position: relative;
+ z-index: 5;
+ transition: background 0.2s;
+}
+
+.resizer:hover,
+.resizer.resizing {
+ background: linear-gradient(180deg, transparent, rgba(59, 130, 246, 0.8), transparent);
+ width: 6px;
+}
+
+/* ---- Preview Panel ---- */
+.preview-panel {
+ min-width: 360px;
+ background: #0a0e1a;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+}
+
+.preview-header {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ padding: 0.6rem 1rem;
+ background: var(--bg-surface);
+ border-bottom: 1px solid var(--border);
+ flex-shrink: 0;
+}
+
+.preview-title {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ font-size: 0.9rem;
+ color: var(--text);
+ font-weight: 500;
+}
+
+.preview-icon {
+ font-size: 1.1rem;
+}
+
+.preview-status {
+ display: flex;
+ align-items: center;
+ gap: 0.4rem;
+ margin-left: auto;
+ font-size: 0.8rem;
+ color: var(--text-secondary);
+}
+
+.status-dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ display: inline-block;
+}
+
+.status-dot.status-waiting {
+ background: #6b7280;
+ animation: pulse 2s ease-in-out infinite;
+}
+
+.status-dot.status-building {
+ background: #3b82f6;
+ animation: pulse 1s ease-in-out infinite;
+}
+
+.status-dot.status-done {
+ background: #22c55e;
+}
+
+.status-dot.status-error {
+ background: #ef4444;
+}
+
+@keyframes pulse {
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0.3; }
+}
+
+.preview-actions {
+ display: flex;
+ gap: 0.4rem;
+}
+
+.preview-btn {
+ padding: 0.3rem 0.6rem;
+ background: transparent;
+ color: var(--text-secondary);
+ border: 1px solid var(--border);
+ border-radius: 6px;
+ font-size: 0.75rem;
+ cursor: pointer;
+ transition: all 0.2s;
+ white-space: nowrap;
+}
+
+.preview-btn:hover {
+ background: var(--bg-card-hover);
+ color: var(--text);
+ border-color: var(--border-light);
+}
+
+.preview-iframe {
+ flex: 1;
+ width: calc(100% - 24px);
+ height: calc(100% - 24px);
+ border: none;
+ border-radius: 8px;
+ margin: 12px;
+ box-shadow: 0 0 20px rgba(59, 130, 246, 0.15);
+ background: #fff;
+}
+
+/* ---- Animation ---- */
+@keyframes fadeIn {
+ from { opacity: 0; transform: translateY(8px); }
+ to { opacity: 1; transform: translateY(0); }
+}
+
+/* ---- Responsive ---- */
+@media (max-width: 768px) {
+ .chat-sidebar {
+ position: fixed;
+ left: 0;
+ top: 0;
+ height: 100vh;
+ z-index: 50;
+ margin-left: calc(-1 * var(--sidebar-width));
+ box-shadow: none;
+ }
+
+ .chat-sidebar.sidebar-open {
+ margin-left: 0;
+ box-shadow: 4px 0 20px rgba(0, 0, 0, 0.5);
+ }
+
+ .sidebar-toggle {
+ display: block;
+ }
+
+ .sidebar-toggle-close {
+ display: block;
+ }
+
+ .intro-card {
+ max-width: 100%;
+ }
+
+ .login-box {
+ max-width: 100%;
+ }
+}
+
+@media (max-width: 600px) {
+ .logo-area h1 {
+ font-size: 1.8rem;
+ }
+
+ .logo-icon {
+ font-size: 2.5rem;
+ }
+
+ .login-box {
+ padding: 1.5rem;
+ max-width: 100%;
+ }
+
+ .intro-card {
+ padding: 1rem 1.2rem;
+ }
+
+ .chat-header {
+ flex-wrap: wrap;
+ gap: 0.5rem;
+ }
+
+ .persona-role {
+ display: none;
+ }
+
+ .model-badge {
+ max-width: 120px;
+ }
+
+ .message {
+ max-width: 92%;
+ }
+
+ .mode-card {
+ padding: 1rem;
+ }
+}
diff --git a/docs/status-board/api-config.js b/docs/status-board/api-config.js
new file mode 100644
index 00000000..b89051d5
--- /dev/null
+++ b/docs/status-board/api-config.js
@@ -0,0 +1,10 @@
+const API_CONFIG = {
+ BASE_URL: 'https://guanghulab.com',
+ ENDPOINTS: {
+ STATUS: '/api/v1/system/status',
+ DEVELOPERS: '/api/v1/developers',
+ BROADCASTS: '/api/v1/broadcasts'
+ },
+ TIMEOUT: 8000,
+ REFRESH_INTERVAL: 30000
+};
diff --git a/docs/status-board/api.js b/docs/status-board/api.js
new file mode 100644
index 00000000..0cdeeb7e
--- /dev/null
+++ b/docs/status-board/api.js
@@ -0,0 +1,63 @@
+const MOCK_DATA = {
+ status: {
+ system_status: 'running',
+ version: 'v0.4.0',
+ uptime: '72h 15m',
+ api_calls_today: 142,
+ active_developers: 6,
+ last_deploy: '2026-03-04 02:00'
+ },
+ developers: [
+ { id: 'DEV-001', name: '页页', module: '后端中间层', status: 'active', progress: 100, phase: '环节5·HTTP联调' },
+ { id: 'DEV-002', name: '肥猫', module: 'M01登录界面', status: 'waiting', progress: 60, phase: '环节1·等SYSLOG' },
+ { id: 'DEV-003', name: '燕樊', module: 'M15云盘系统', status: 'waiting', progress: 55, phase: '环节1·等SYSLOG' },
+ { id: 'DEV-004', name: '之之', module: '钉钉机器人', status: 'waiting', progress: 20, phase: '环节0·等SYSLOG' },
+ { id: 'DEV-005', name: '小草莓', module: '系统状态看板', status: 'active', progress: 85, phase: '环节2·API接入' },
+ { id: 'DEV-009', name: '花尔', module: 'M05用户中心', status: 'waiting', progress: 40, phase: '环节1·等SYSLOG' },
+ { id: 'DEV-010', name: '桔子', module: 'M06工单管理', status: 'active', progress: 35, phase: '环节1·广播待出' },
+ { id: 'DEV-011', name: '匆匆那年', module: 'BC-000 DEVlog', status: 'waiting', progress: 10, phase: '环节0·等SYSLOG' }
+ ],
+ broadcasts: [
+ { id: 'DEV-012', module: 'M05用户中心', phase: '环节1', status: '等SYSLOG', time: '03-04 08:45' },
+ { id: 'BC-看板-002', dev: '小草莓', module: '系统状态看板', phase: '环节2', status: '执行中', time: '03-04 08:50' },
+ { id: 'BC-集成-001-M13', dev: '小草莓', module: 'M13协作调度', phase: '环节1', status: '已完成', time: '03-04 08:38' }
+ ]
+};
+
+async function fetchWithTimeout(url, timeout) {
+ const controller = new AbortController();
+ const timer = setTimeout(() => controller.abort(), timeout);
+ try {
+ const res = await fetch(url, { signal: controller.signal });
+ clearTimeout(timer);
+ return res;
+ } catch (err) {
+ clearTimeout(timer);
+ throw err;
+ }
+}
+
+async function apiGet(endpoint, mockKey) {
+ try {
+ const res = await fetchWithTimeout(API_CONFIG.BASE_URL + endpoint, API_CONFIG.TIMEOUT);
+ if (!res.ok) throw new Error('HTTP ' + res.status);
+ return { data: await res.json(), isLive: true };
+ } catch (err) {
+ console.warn('[HoloLake] API未就绪,使用模拟数据:', err.message);
+ return { data: MOCK_DATA[mockKey], isLive: false };
+ }
+}
+
+async function loadAllData() {
+ const [statusRes, devRes, bcRes] = await Promise.all([
+ apiGet(API_CONFIG.ENDPOINTS.STATUS, 'status'),
+ apiGet(API_CONFIG.ENDPOINTS.DEVELOPERS, 'developers'),
+ apiGet(API_CONFIG.ENDPOINTS.BROADCASTS, 'broadcasts')
+ ]);
+ return {
+ status: statusRes.data,
+ developers: devRes.data,
+ broadcasts: bcRes.data,
+ isLive: statusRes.isLive && devRes.isLive && bcRes.isLive
+ };
+}
diff --git a/docs/status-board/index.html b/docs/status-board/index.html
new file mode 100644
index 00000000..1a22ee3b
--- /dev/null
+++ b/docs/status-board/index.html
@@ -0,0 +1,239 @@
+
+
+
+
+
+
+
光湖系统 · 开发状态看板
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 👥 团队成员状态(静态)
+
+
+
+
+
页页
+
后端中间层
+
更新: 10:32
+
+
+
+
+
+
肥猫
+
飞书机器人
+
更新: 10:15
+
+
+
+
+
+
燕樊
+
对话UI
+
更新: 09:47
+
+
+
+
+
+
小幸运
+
钉钉打通
+
更新: 11:02
+
+
+
+
+
+
小草莓
+
系统状态看板
+
更新: 刚刚
+
+
+
+
+
+
小蔡同学
+
工单管理
+
更新: 09:20
+
+
+
+
+
+
四月
+
用户中心
+
更新: 10:55
+
+
+
+
+
+
+
+
+ 📢 最近广播(静态)
+
+
+ BC-001
+ HoloLake桌面UI框架
+ 10:45
+ 已完成
+
+
+ BC-002
+ 对话界面设计
+ 10:20
+ 进行中
+
+
+ BC-003
+ 人格体选择器
+ 09:55
+ 进行中
+
+
+ BC-004
+ 云盘授权界面
+ 09:30
+ 待处理
+
+
+ BC-005
+ 用户中心设计
+ 08:50
+ 待处理
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/status-board/render.js b/docs/status-board/render.js
new file mode 100644
index 00000000..fce8015b
--- /dev/null
+++ b/docs/status-board/render.js
@@ -0,0 +1,96 @@
+const STATUS_COLORS = {
+ active: '#4fc3f7',
+ waiting: '#ffa726',
+ done: '#66bb6a'
+};
+const STATUS_LABELS = {
+ active: '执行中',
+ waiting: '等待中',
+ done: '已完成'
+};
+
+function renderSystemStatus(data) {
+ const c = document.getElementById('sys-status');
+ if (!c) return;
+ c.innerHTML = `
+
+
🟢 ${data.system_status === 'running' ? '运行中' : '异常'} 系统状态
+
⏱️ ${data.uptime} 运行时长
+
📡 ${data.api_calls_today} 今日API调用
+
👥 ${data.active_developers} 活跃开发者
+
🚀 ${data.version} 版本
+
📦 ${data.last_deploy} 最后部署
+
+ `;
+}
+
+function renderDevelopers(data) {
+ const c = document.getElementById('dev-list');
+ if (!c) return;
+ let html = '';
+ for (let i = 0; i < data.length; i++) {
+ const d = data[i];
+ const color = STATUS_COLORS[d.status] || '#78909c';
+ html += `
+
+
+ ${d.id}
+ ${d.name}
+ ${STATUS_LABELS[d.status] || d.status}
+
+
${d.module} · ${d.phase}
+
+
${d.progress}%
+
+ `;
+ }
+ c.innerHTML = html;
+}
+
+function renderBroadcasts(data) {
+ const c = document.getElementById('bc-list');
+ if (!c) return;
+ let html = '';
+ for (let i = 0; i < data.length; i++) {
+ const b = data[i];
+ let cls = 'wait';
+ if (b.status === '已完成' || b.status === 'done') cls = 'done';
+ else if (b.status === '执行中' || b.status === 'active') cls = 'active';
+ html += `
+
+ ${b.id}
+ ${b.dev || ''}
+ ${b.module} · ${b.phase}
+ ${b.status}
+ ${b.time}
+
+ `;
+ }
+ c.innerHTML = html;
+}
+
+function updateConnStatus(isLive) {
+ const el = document.getElementById('conn-status');
+ if (!el) return;
+ el.innerHTML = isLive
+ ? '
已连接 guanghulab.com'
+ : '
离线模式(模拟数据)';
+}
+
+async function refreshDashboard() {
+ try {
+ const result = await loadAllData();
+ renderSystemStatus(result.status);
+ renderDevelopers(result.developers);
+ renderBroadcasts(result.broadcasts);
+ updateConnStatus(result.isLive);
+ } catch (err) {
+ console.error('[HoloLake] 刷新失败:', err);
+ updateConnStatus(false);
+ }
+}
+
+document.addEventListener('DOMContentLoaded', function() {
+ refreshDashboard();
+ setInterval(refreshDashboard, API_CONFIG.REFRESH_INTERVAL);
+});
diff --git a/docs/status-board/style.css b/docs/status-board/style.css
new file mode 100644
index 00000000..e88ff987
--- /dev/null
+++ b/docs/status-board/style.css
@@ -0,0 +1,488 @@
+/* 全局样式 - 深色科技风 */
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ font-family: 'Segoe UI', 'Courier New', monospace;
+}
+
+body {
+ background-color: #0a0e17;
+ color: #e0e0e0;
+ line-height: 1.6;
+ padding: 20px;
+ min-height: 100vh;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.dashboard {
+ max-width: 1400px;
+ width: 100%;
+ margin: 0 auto;
+}
+
+/* 标题栏 */
+.header {
+ background-color: #131a2b;
+ border-radius: 16px;
+ padding: 20px 30px;
+ margin-bottom: 30px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ border: 1px solid #2a3440;
+ box-shadow: 0 4px 20px rgba(0, 212, 255, 0.1);
+}
+
+.logo {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+
+.logo-icon {
+ font-size: 32px;
+ filter: drop-shadow(0 0 8px #00d4ff);
+}
+
+.logo h1 {
+ font-size: 1.8rem;
+ font-weight: 400;
+ letter-spacing: 1px;
+ color: #fff;
+ text-shadow: 0 0 10px rgba(0, 212, 255, 0.5);
+}
+
+.status-indicator {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ background: #1e293b;
+ padding: 8px 16px;
+ border-radius: 40px;
+ border: 1px solid #00d4ff;
+}
+
+.dot {
+ width: 12px;
+ height: 12px;
+ border-radius: 50%;
+ display: inline-block;
+}
+
+.dot.green {
+ background-color: #00ff9d;
+ box-shadow: 0 0 10px #00ff9d;
+}
+
+/* 团队成员卡片区 */
+.team-cards {
+ margin-bottom: 40px;
+}
+
+.team-cards h2, .broadcast-list h2 {
+ font-size: 1.5rem;
+ margin-bottom: 20px;
+ color: #00d4ff;
+ font-weight: 300;
+ letter-spacing: 1px;
+ border-left: 4px solid #00d4ff;
+ padding-left: 16px;
+}
+
+.card-grid {
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ gap: 20px;
+}
+
+.card {
+ background-color: #131a2b;
+ border-radius: 16px;
+ padding: 20px;
+ border: 1px solid #2a3440;
+ transition: all 0.3s ease;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
+}
+
+.card:hover {
+ transform: translateY(-4px);
+ border-color: #00d4ff;
+ box-shadow: 0 8px 24px rgba(0, 212, 255, 0.2);
+}
+
+.card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 12px;
+ padding-bottom: 8px;
+ border-bottom: 1px solid #2a3440;
+}
+
+.dev-id {
+ font-family: 'Courier New', monospace;
+ font-weight: bold;
+ color: #00d4ff;
+ font-size: 1rem;
+}
+
+.status-badge {
+ font-size: 0.75rem;
+ padding: 4px 8px;
+ border-radius: 40px;
+ background-color: #1e293b;
+ border: 1px solid transparent;
+}
+
+.status-badge.status-active {
+ background-color: #00ff9d20;
+ color: #00ff9d;
+ border-color: #00ff9d;
+}
+
+.status-badge.status-standby {
+ background-color: #ffaa0020;
+ color: #ffaa00;
+ border-color: #ffaa00;
+}
+
+.status-badge.status-coding {
+ background-color: #00d4ff20;
+ color: #00d4ff;
+ border-color: #00d4ff;
+}
+
+.card-body .dev-name {
+ font-size: 1.2rem;
+ font-weight: 500;
+ color: #fff;
+ margin-bottom: 4px;
+}
+
+.card-body .dev-module {
+ font-size: 0.9rem;
+ color: #a0a0a0;
+ margin-bottom: 8px;
+}
+
+.card-body .update-time {
+ font-size: 0.8rem;
+ font-family: 'Courier New', monospace;
+ color: #6b7a8f;
+}
+
+/* 广播列表区 */
+.broadcast-list {
+ margin-bottom: 40px;
+}
+
+.list-container {
+ background-color: #131a2b;
+ border-radius: 16px;
+ border: 1px solid #2a3440;
+ overflow: hidden;
+}
+
+.list-item {
+ display: grid;
+ grid-template-columns: 100px 1fr 100px 100px;
+ padding: 16px 24px;
+ border-bottom: 1px solid #2a3440;
+ align-items: center;
+ transition: background 0.2s;
+}
+
+.list-item:last-child {
+ border-bottom: none;
+}
+
+.list-item:hover {
+ background-color: #1e293b;
+}
+
+.broadcast-id {
+ font-family: 'Courier New', monospace;
+ color: #00d4ff;
+ font-weight: bold;
+}
+
+.broadcast-title {
+ color: #fff;
+}
+
+.broadcast-time {
+ font-family: 'Courier New', monospace;
+ color: #a0a0a0;
+}
+
+.broadcast-status {
+ font-size: 0.85rem;
+ padding: 4px 8px;
+ border-radius: 40px;
+ text-align: center;
+ width: fit-content;
+}
+
+.status-done {
+ background-color: #00ff9d20;
+ color: #00ff9d;
+ border: 1px solid #00ff9d;
+}
+
+.status-progress {
+ background-color: #00d4ff20;
+ color: #00d4ff;
+ border: 1px solid #00d4ff;
+}
+
+.status-pending {
+ background-color: #ffaa0020;
+ color: #ffaa00;
+ border: 1px solid #ffaa00;
+}
+
+/* 底部信息栏 */
+.footer {
+ background-color: #131a2b;
+ border-radius: 16px;
+ padding: 16px 24px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ font-size: 0.9rem;
+ border: 1px solid #2a3440;
+ color: #6b7a8f;
+}
+
+.footer-left {
+ font-family: 'Courier New', monospace;
+}
+
+.footer-right {
+ font-family: 'Courier New', monospace;
+ color: #00d4ff;
+}
+
+/* 响应式布局 */
+@media screen and (max-width: 1024px) {
+ .card-grid {
+ grid-template-columns: repeat(2, 1fr);
+ }
+}
+
+@media screen and (max-width: 640px) {
+ .card-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .list-item {
+ grid-template-columns: 80px 1fr 80px 80px;
+ padding: 12px 16px;
+ font-size: 0.85rem;
+ }
+
+ .header {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 12px;
+ }
+}/* ========== 环节2新增:API数据区域样式 ========== */
+
+/* 连接状态指示器 */
+.conn-status {
+ text-align: center;
+ padding: 8px 16px;
+ font-size: 12px;
+ color: #8899aa;
+ border-bottom: 1px solid rgba(255,255,255,0.06);
+}
+
+.conn-dot {
+ display: inline-block;
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ margin-right: 6px;
+ vertical-align: middle;
+}
+
+.conn-dot.on {
+ background: #66bb6a;
+ box-shadow: 0 0 6px #66bb6a;
+}
+
+.conn-dot.off {
+ background: #ffa726;
+}
+
+/* 数据区块 */
+.data-section {
+ padding: 20px 16px;
+ border-bottom: 1px solid rgba(255,255,255,0.06);
+}
+
+.section-title {
+ font-size: 16px;
+ font-weight: 600;
+ color: #e0e6ed;
+ margin-bottom: 16px;
+}
+
+.data-container {
+ color: #8899aa;
+ font-size: 13px;
+}
+
+/* 系统状态网格 */
+.status-grid {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 10px;
+}
+
+.s-card {
+ background: rgba(79,195,247,0.06);
+ border: 1px solid rgba(79,195,247,0.12);
+ border-radius: 10px;
+ padding: 14px 10px;
+ text-align: center;
+}
+
+.s-icon {
+ display: block;
+ font-size: 20px;
+ margin-bottom: 4px;
+}
+
+.s-val {
+ display: block;
+ font-size: 16px;
+ font-weight: bold;
+ color: #4fc3f7;
+ margin-bottom: 2px;
+}
+
+.s-lbl {
+ display: block;
+ font-size: 11px;
+ color: #667788;
+}
+
+/* 开发者卡片 */
+.dev-card {
+ background: rgba(255,255,255,0.03);
+ border-radius: 10px;
+ padding: 14px 16px;
+ margin-bottom: 8px;
+ border: 1px solid rgba(255,255,255,0.06);
+}
+
+.dev-top {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin-bottom: 6px;
+}
+
+.dev-id {
+ font-size: 11px;
+ color: #556677;
+ font-family: monospace;
+}
+
+.dev-name {
+ font-size: 14px;
+ color: #e0e6ed;
+ font-weight: 600;
+ flex: 1;
+}
+
+.dev-st {
+ font-size: 12px;
+}
+
+.dev-mod {
+ font-size: 12px;
+ color: #8899aa;
+ margin-bottom: 8px;
+}
+
+.prog-bar {
+ height: 6px;
+ background: rgba(255,255,255,0.08);
+ border-radius: 3px;
+ overflow: hidden;
+ margin-bottom: 4px;
+}
+
+.prog-fill {
+ height: 100%;
+ border-radius: 3px;
+ transition: width 0.8s ease;
+}
+
+.prog-num {
+ font-size: 11px;
+ color: #8899aa;
+ text-align: right;
+}
+
+/* 广播行 */
+.bc-row {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 10px 12px;
+ border-bottom: 1px solid rgba(255,255,255,0.04);
+ font-size: 13px;
+}
+
+.bc-id {
+ color: #4fc3f7;
+ font-family: monospace;
+ font-size: 11px;
+ min-width: 110px;
+}
+
+.bc-dev {
+ color: #e0e6ed;
+ min-width: 55px;
+}
+
+.bc-mod {
+ color: #8899aa;
+ flex: 1;
+}
+
+.bc-st {
+ min-width: 60px;
+ text-align: center;
+ padding: 2px 8px;
+ border-radius: 10px;
+ font-size: 11px;
+}
+
+.bc-st.done {
+ background: rgba(102,187,106,0.15);
+ color: #66bb6a;
+}
+
+.bc-st.active {
+ background: rgba(79,195,247,0.15);
+ color: #4fc3f7;
+}
+
+.bc-st.wait {
+ background: rgba(255,167,38,0.15);
+ color: #ffa726;
+}
+
+.bc-time {
+ color: #556677;
+ font-size: 11px;
+ min-width: 80px;
+ text-align: right;
+}
diff --git a/docs/status-board/ws-client.js b/docs/status-board/ws-client.js
new file mode 100644
index 00000000..26b4fe84
--- /dev/null
+++ b/docs/status-board/ws-client.js
@@ -0,0 +1,121 @@
+// ========== HoloLake 看板 · WebSocket 客户端模块 ==========
+// 功能:连接管理、自动重连(指数退避)、心跳检测、状态回调
+
+class WebSocketClient {
+ constructor(url, options = {}) {
+ this.url = url;
+ this.reconnectInterval = options.reconnectInterval || 1000; // 初始重连间隔 1秒
+ this.maxReconnectInterval = options.maxReconnectInterval || 30000; // 最大30秒
+ this.heartbeatInterval = options.heartbeatInterval || 30000; // 心跳间隔30秒
+ this.onMessage = options.onMessage || (() => {});
+ this.onStatusChange = options.onStatusChange || (() => {});
+
+ this.ws = null;
+ this.reconnectTimer = null;
+ this.heartbeatTimer = null;
+ this.forcedClose = false;
+ this.reconnectAttempts = 0;
+ }
+
+ // 连接
+ connect() {
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) return;
+ this.forcedClose = false;
+ this.onStatusChange('connecting');
+ try {
+ this.ws = new WebSocket(this.url);
+ } catch (e) {
+ console.error('[WS] 连接失败', e);
+ this._scheduleReconnect();
+ return;
+ }
+
+ this.ws.onopen = () => {
+ console.log('[WS] 连接成功');
+ this.reconnectAttempts = 0;
+ this.onStatusChange('connected');
+ this._startHeartbeat();
+ };
+
+ this.ws.onmessage = (event) => {
+ try {
+ const data = JSON.parse(event.data);
+ this.onMessage(data);
+ } catch (e) {
+ console.warn('[WS] 消息解析失败', e);
+ }
+ };
+
+ this.ws.onclose = (event) => {
+ console.log('[WS] 连接关闭', event.code, event.reason);
+ this._stopHeartbeat();
+ if (!this.forcedClose) {
+ this.onStatusChange('disconnected');
+ this._scheduleReconnect();
+ } else {
+ this.onStatusChange('offline');
+ }
+ };
+
+ this.ws.onerror = (error) => {
+ console.error('[WS] 错误', error);
+ this.onStatusChange('error');
+ };
+ }
+
+ // 主动断开
+ disconnect() {
+ this.forcedClose = true;
+ if (this.reconnectTimer) {
+ clearTimeout(this.reconnectTimer);
+ this.reconnectTimer = null;
+ }
+ this._stopHeartbeat();
+ if (this.ws) {
+ this.ws.close();
+ this.ws = null;
+ }
+ }
+
+ // 发送消息
+ send(data) {
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
+ this.ws.send(JSON.stringify(data));
+ } else {
+ console.warn('[WS] 未连接,无法发送');
+ }
+ }
+
+ // 重连调度(指数退避)
+ _scheduleReconnect() {
+ if (this.forcedClose) return;
+ const delay = Math.min(
+ this.reconnectInterval * Math.pow(2, this.reconnectAttempts),
+ this.maxReconnectInterval
+ );
+ console.log(`[WS] ${delay}ms 后尝试重连...`);
+ this.onStatusChange('reconnecting');
+ if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
+ this.reconnectTimer = setTimeout(() => {
+ this.reconnectAttempts++;
+ this.connect();
+ }, delay);
+ }
+
+ // 心跳
+ _startHeartbeat() {
+ this._stopHeartbeat();
+ this.heartbeatTimer = setInterval(() => {
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
+ this.send({ type: 'ping' });
+ }
+ }, this.heartbeatInterval);
+ }
+
+ _stopHeartbeat() {
+ if (this.heartbeatTimer) {
+ clearInterval(this.heartbeatTimer);
+ this.heartbeatTimer = null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/docs/ticket-system/index.html b/docs/ticket-system/index.html
new file mode 100644
index 00000000..bfac39d1
--- /dev/null
+++ b/docs/ticket-system/index.html
@@ -0,0 +1,809 @@
+
+
+
+
+
+
工单管理 · HoloLake · 视觉优化版
+
+
+
+
+
+
+
+ HoloLake
+
+
+
+
+
+
+
+
+
+
+ 编号↑
+ 导出CSV
+ 重置
+
+
+
+
+
+
+
+
+
+
+
+
新建工单
+
+
+
+ 进行中
+ 已完成
+ 待开始
+
+
+
+ 取消
+ 确认创建
+
+
+
+
+
+background: linear-gradient(135deg, #0a1628 0%, #1a2a4a 50%, #0d1f3c 100%);
+
\ No newline at end of file
diff --git a/docs/ticket-system/script.js b/docs/ticket-system/script.js
new file mode 100644
index 00000000..358ac3b2
--- /dev/null
+++ b/docs/ticket-system/script.js
@@ -0,0 +1,301 @@
+/* ============================================
+ HoloLake · M06 工单管理界面 · 逻辑层
+ 环节3:响应式 + 数据导出(JSON/CSV)
+ DEV-010 桔子
+ ============================================ */
+// === 状态流转规则 ===
+const STATUS_FLOW = {
+ 'pending': { next: 'active', label: '待处理' },
+ 'active': { next: 'done', label: '进行中' },
+ 'done': { next: 'pending', label: '已完成' }
+};
+// === 初始化 · 从 localStorage 读取工单 ===
+let tickets = JSON.parse(localStorage.getItem('holotake_tic
+kets') || '[]');
+// === 保存到 localStorage ===
+function saveTickets() {
+ localStorage.setItem('holotake_tickets', JSON.stringify(t
+ickets));
+}
+// === 生成工单编号 ===
+function generateId() {
+ const now = new Date();
+ const dateStr = [
+ now.getFullYear(),
+📡 BC-M06-001 · DEV-010桔子 · 工单管理界面·环节3·响应式布局+数据导出 18
+ String(now.getMonth() + 1).padStart(2, '0'),
+ String(now.getDate()).padStart(2, '0')
+ ].join('');
+ const rand = String(Math.floor(Math.random() * 1000)).pad
+Start(3, '0');
+ return 'TK-' + dateStr + '-' + rand;
+}
+// === 渲染统计卡片 ===
+function renderStats() {
+ const total = tickets.length;
+ const pending = tickets.filter(t => t.status === 'pendin
+g').length;
+ const active = tickets.filter(t => t.status === 'activ
+e').length;
+ const done = tickets.filter(t => t.status === 'done').len
+gth;
+ document.getElementById('statTotal').textContent = total;
+ document.getElementById('statPending').textContent = pend
+ing;
+ document.getElementById('statActive').textContent = activ
+e;
+ document.getElementById('statDone').textContent = done;
+}
+// === 获取筛选和排序后的工单列表 ===
+function getFilteredTickets() {
+ const filterStatus = document.getElementById('filterStatu
+s').value;
+ const sortBy = document.getElementById('sortBy').value;
+ let list = [...tickets];
+ // 筛选
+ if (filterStatus !== 'all') {
+ list = list.filter(t => t.status === filterStatus);
+ }
+📡 BC-M06-001 · DEV-010桔子 · 工单管理界面·环节3·响应式布局+数据导出 19
+ // 排序
+ if (sortBy === 'newest') {
+ list.sort((a, b) => new Date(b.createdAt) - new Date(a.
+createdAt));
+ } else if (sortBy === 'oldest') {
+ list.sort((a, b) => new Date(a.createdAt) - new Date(b.
+createdAt));
+ } else if (sortBy === 'status') {
+ const order = { 'pending': 0, 'active': 1, 'done': 2 };
+ list.sort((a, b) => order[a.status] - order[b.status]);
+ }
+ return list;
+}
+// === 渲染工单列表 ===
+function renderTickets() {
+ const list = getFilteredTickets();
+ const container = document.getElementById('ticketList');
+ if (list.length === 0) {
+ container.innerHTML = '
';
+ return;
+ }
+ container.innerHTML = list.map(ticket => {
+ const statusInfo = STATUS_FLOW[ticket.status];
+ return '