From 3814cf3d89e406ff6f53ff4acd3f25d9666f403a Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 2 Apr 2026 14:46:40 +0000
Subject: [PATCH] =?UTF-8?q?=E9=A6=96=E9=A1=B5=E4=B8=89=E6=A0=8F=E5=B8=83?=
=?UTF-8?q?=E5=B1=80=20+=20=E6=82=AC=E6=B5=AE=E7=90=83=E5=8F=AF=E6=8B=96?=
=?UTF-8?q?=E6=8B=BD=20+=20=E5=8A=A8=E6=80=81=E5=85=AC=E5=91=8Aticker=20+?=
=?UTF-8?q?=20=E7=95=99=E8=A8=80=E6=9D=BF/=E5=9B=9E=E6=89=A7=E7=B3=BB?=
=?UTF-8?q?=E7=BB=9F=20+=20=E5=90=8E=E7=AB=AFfeedback=20API?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Agent-Logs-Url: https://github.com/qinfendebingshuo/guanghulab/sessions/32fa4778-f82b-4742-b89e-3b3af9529167
Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com>
---
docs/index.html | 384 ++++++++++++++++++++++++++++++++++++++++---
server/app/server.js | 59 +++++++
2 files changed, 418 insertions(+), 25 deletions(-)
diff --git a/docs/index.html b/docs/index.html
index 3e4566cc..5edabb93 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -187,6 +187,55 @@ label{display:block;font-size:12px;color:var(--dim);margin-bottom:4px;font-weigh
.csnd:hover{background:var(--accent2)}
.csnd:disabled{opacity:.3}
+/* ═══ Three-Column Home Layout ═══ */
+.home-grid{display:grid;grid-template-columns:260px 1fr 260px;gap:16px;padding:16px;max-width:1200px;margin:0 auto;align-content:start}
+@media(max-width:960px){.home-grid{grid-template-columns:1fr;max-width:600px}}
+.panel{background:var(--bg1);border:1px solid var(--border);border-radius:var(--r);padding:14px;height:fit-content}
+.panel-title{font-size:11px;color:var(--dim);text-transform:uppercase;letter-spacing:1.2px;font-weight:700;margin-bottom:10px;display:flex;align-items:center;gap:6px}
+.panel-item{padding:6px 0;border-bottom:1px solid var(--border);font-size:12px}
+.panel-item:last-child{border-bottom:none}
+.panel-label{color:var(--dim);font-size:10px;margin-bottom:1px}
+.panel-value{color:var(--text);font-weight:600;font-family:var(--m);font-size:11px}
+.panel-value.ok{color:var(--ok)}
+.panel-value.warn{color:var(--warn)}
+.panel-value.err{color:var(--err)}
+
+/* ═══ Ticker ═══ */
+.ticker-wrap{overflow:hidden;background:var(--bg1);border:1px solid var(--border);border-radius:var(--rs);padding:6px 0;margin-bottom:14px;position:relative}
+.ticker-wrap::before,.ticker-wrap::after{content:'';position:absolute;top:0;bottom:0;width:24px;z-index:2;pointer-events:none}
+.ticker-wrap::before{left:0;background:linear-gradient(90deg,var(--bg1),transparent)}
+.ticker-wrap::after{right:0;background:linear-gradient(-90deg,var(--bg1),transparent)}
+.ticker{display:flex;animation:tickScroll 28s linear infinite;white-space:nowrap;gap:36px;padding:0 16px}
+.ticker:hover{animation-play-state:paused}
+.ticker span{font-size:11px;color:var(--dim);display:flex;align-items:center;gap:5px;flex-shrink:0}
+.ticker span em{font-style:normal}
+@keyframes tickScroll{0%{transform:translateX(0)}100%{transform:translateX(-50%)}}
+
+/* ═══ Team Card ═══ */
+.team-slot{display:flex;align-items:center;gap:8px;padding:7px 8px;background:var(--bg2);border-radius:var(--rs);margin-bottom:5px;transition:border-color .3s;border:1px solid transparent}
+.team-slot:hover{border-color:var(--accent)}
+.team-av{width:28px;height:28px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:14px;flex-shrink:0}
+.team-nm{font-size:11px;font-weight:600}
+.team-role{font-size:9px;color:var(--dim)}
+
+/* ═══ Feedback Page ═══ */
+.fb-item{padding:10px}
+.fb-item .fb-meta{font-size:10px;color:var(--dim);margin-top:6px;display:flex;gap:8px;align-items:center}
+.fb-status{display:inline-block;padding:1px 6px;border-radius:10px;font-size:9px;font-weight:600}
+.fb-status.pending{background:rgba(251,191,36,.12);color:var(--warn)}
+.fb-status.replied{background:rgba(52,211,153,.12);color:var(--ok)}
+.fb-reply{margin-top:6px;padding:8px;background:var(--bg);border-radius:var(--rs);font-size:12px;color:var(--accent)}
+
+/* ═══ Home Entry Cards ═══ */
+.home-entries{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin:16px 0}
+@media(max-width:480px){.home-entries{grid-template-columns:1fr}}
+.entry-card{padding:18px 14px;text-align:center;background:linear-gradient(135deg,var(--bg1),var(--bg2));border:1px solid var(--border);border-radius:var(--r);cursor:pointer;transition:all .3s}
+.entry-card:hover{border-color:var(--accent);box-shadow:var(--glow)}
+.entry-card:active{transform:scale(.98)}
+.entry-card em{font-style:normal;font-size:28px;display:block;margin-bottom:6px}
+.entry-card strong{font-size:14px;display:block;margin-bottom:3px}
+.entry-card span{font-size:11px;color:var(--dim)}
+
/* ═══ Monitor ═══ */
.mon{position:fixed;top:calc(6px + var(--st));right:10px;z-index:40;background:rgba(12,18,32,.9);border:1px solid var(--border);border-radius:var(--r);padding:5px 9px;font-size:9px;font-family:var(--m);color:var(--dim);cursor:pointer;transition:all .3s;user-select:none;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px)}
.mon:hover{border-color:var(--accent)}
@@ -210,29 +259,108 @@ label{display:block;font-size:12px;color:var(--dim);margin-bottom:4px;font-weigh
🏛️ 光湖灯塔
在线
-
-
-
AGE OS v40.0
-
光湖灯塔
-
语言驱动操作系统 · 人类只说话,系统搞定一切
-
「语言 = 现实」
+
+
+
+
📡 系统状态
+
+
+
+
+
+
+
🧠 版本更新
+
+
v40.0 · 2026-04-02
COS双桶存储上线
+
v39.0 · 2026-04-01
全链路部署观测
+
v38.0 · 2026-04-01
HLDP协作语言协议
+
-
📡 系统更新
-
v40.0
COS双桶存储 · 智能模型分流
人格体数据库 + 语料库 · API智能选模型
-
v39.0
全链路部署观测
日志采集 · 自动修复 · LLM诊断
-
v38.0
HLDP协作语言协议
Notion↔GitHub双侧通信 · 铸渊方言
+
+
+
+
AGE OS v40.0
+
光湖灯塔
+
语言驱动操作系统 · 人类只说话,系统搞定一切
+
「语言 = 现实」
+
-
-
✍️
-
码字工作台
-
网文行业 · AI辅助创作 · 语言即命令
-
Beta 内测
+
+
+ 🏛️ AGE OS v40.0 运行中
+ 📡 COS双桶存储已上线
+ 🧠 HLDP协作语言协议在线
+ ⚡ 全链路部署观测系统就绪
+ ✍️ 码字工作台 Beta 内测中
+ 🏛️ AGE OS v40.0 运行中
+ 📡 COS双桶存储已上线
+ 🧠 HLDP协作语言协议在线
+ ⚡ 全链路部署观测系统就绪
+ ✍️ 码字工作台 Beta 内测中
+
+
+
+
+
+
✍️
+
码字工作台
+
网文行业 · AI辅助创作
+
Beta
+
+
+
📮
+
留言板
+
给冰朔和铸渊留言
+
每条必回
+
+
+
+
-
@@ -378,6 +506,29 @@ label{display:block;font-size:12px;color:var(--dim);margin-bottom:4px;font-weigh
+
+
+
+
+
+
+
+
📝 给冰朔和铸渊留言
+
+
+ 留言自动署名(已识别身份时)
+
+
+
+
💬 留言记录
+
+
+
+
@@ -477,6 +628,7 @@ function go(target){
if(target==='chapters')renderChapters();
if(target==='editor')renderEditor();
if(target==='charEdit'&&editingCharId)renderCharEdit();
+ if(target==='feedback')renderFeedback();
}
window.go=go;
@@ -856,13 +1008,7 @@ var chatIn=document.getElementById('chatIn');
var chatMs=document.getElementById('chatMs');
var mCalls=0;
-chatFab.onclick=function(){
- chatOpen=!chatOpen;
- chatP.classList.toggle('open',chatOpen);
- chatFab.classList.toggle('on',chatOpen);
- chatFab.textContent=chatOpen?'✕':'💬';
- if(chatOpen)setTimeout(function(){chatIn.focus()},200);
-};
+chatFab.onclick=null; // Overridden by draggable FAB code below
function chatMsg(text,type,meta){
var d=document.createElement('div');
@@ -920,6 +1066,7 @@ function routeCommand(msg){
go('editor');return'翻转到正文编辑器 ✏️';
}
if(/回首页|回灯塔|回主页|返回/.test(m)){go('home');return'翻转回灯塔首页 🏛️'}
+ if(/留言|留言板|反馈|feedback/.test(m)){go('feedback');return'翻转到留言板 📮'}
return null;
}
@@ -1009,6 +1156,193 @@ async function checkConn(){
}
checkConn();setInterval(checkConn,30000);
+/* ════════════════════════════════════════
+ DRAGGABLE FAB (touch+mouse · 位置记忆 · 边缘吸附)
+ ════════════════════════════════════════ */
+(function(){
+ var fab=document.getElementById('chatFab');
+ var isDrag=false,hasMoved=false;
+ var startX,startY,fabX,fabY;
+ var fabW=50,fabH=50;
+
+ // Restore saved position
+ try{
+ var pos=JSON.parse(localStorage.getItem('hl-fab-pos'));
+ if(pos&&typeof pos.x==='number'&&typeof pos.y==='number'){
+ var ww=window.innerWidth,wh=window.innerHeight;
+ if(pos.x>=0&&pos.x<=ww-fabW&&pos.y>=0&&pos.y<=wh-fabH){
+ fab.style.right='auto';fab.style.bottom='auto';
+ fab.style.left=pos.x+'px';fab.style.top=pos.y+'px';
+ }
+ }
+ }catch(e){}
+
+ function getXY(e){
+ if(e.touches)return{x:e.touches[0].clientX,y:e.touches[0].clientY};
+ return{x:e.clientX,y:e.clientY};
+ }
+
+ function onStart(e){
+ isDrag=true;hasMoved=false;
+ var xy=getXY(e);
+ var rect=fab.getBoundingClientRect();
+ startX=xy.x-rect.left;startY=xy.y-rect.top;
+ fabX=rect.left;fabY=rect.top;
+ fab.style.transition='none';
+ }
+
+ function onMove(e){
+ if(!isDrag)return;
+ var xy=getXY(e);
+ var nx=xy.x-startX,ny=xy.y-startY;
+ if(!hasMoved&&Math.abs(nx-fabX)<5&&Math.abs(ny-fabY)<5)return;
+ hasMoved=true;
+ e.preventDefault();
+ fabX=Math.max(0,Math.min(window.innerWidth-fabW,nx));
+ fabY=Math.max(0,Math.min(window.innerHeight-fabH,ny));
+ fab.style.right='auto';fab.style.bottom='auto';
+ fab.style.left=fabX+'px';fab.style.top=fabY+'px';
+ }
+
+ function onEnd(){
+ if(!isDrag)return;
+ isDrag=false;
+ fab.style.transition='all .3s cubic-bezier(.4,0,.2,1)';
+ if(hasMoved){
+ // Edge snapping: snap to nearest horizontal edge
+ var midX=window.innerWidth/2;
+ fabX=fabX0?uh+'h '+um%60+'m':um+'分钟';
+ }
+ }catch(e){
+ var sEl=document.getElementById('sysStatus');
+ if(sEl){sEl.textContent='🔴 离线';sEl.className='panel-value err'}
+ }
+}
+loadSysStatus();
+
+/* ════════════════════════════════════════
+ BULLETIN & TICKER (动态公告)
+ ════════════════════════════════════════ */
+async function loadBulletin(){
+ try{
+ var r=await fetch('/api/system/bulletin',{signal:AbortSignal.timeout(5000)});
+ var data=await r.json();
+ // Ticker
+ var ticker=document.getElementById('ticker');
+ if(ticker&&data.updates&&data.updates.length>0){
+ var h='';
+ data.updates.forEach(function(u){h+='📡 '+esc(u.version)+' '+esc(u.title)+''});
+ h+='✍️ 码字工作台 Beta 内测中';
+ ticker.innerHTML=h+h; // Duplicate for seamless scroll
+ }
+ // Version badge
+ if(data.system&&data.system.version){
+ var vEl=document.getElementById('homeVer');
+ if(vEl)vEl.textContent='AGE OS '+data.system.version;
+ }
+ // Left panel updates
+ if(data.updates){
+ var uEl=document.getElementById('sysUpdates');
+ if(uEl)uEl.innerHTML=data.updates.slice(0,3).map(function(u){
+ return ''+esc(u.version)+' · '+esc(u.date)+'
'+esc(u.title)+'
';
+ }).join('');
+ }
+ }catch(e){/* Use hardcoded ticker content */}
+}
+loadBulletin();
+
+/* ════════════════════════════════════════
+ FEEDBACK / 留言板
+ ════════════════════════════════════════ */
+function renderFeedback(){
+ var el=document.getElementById('fbList');
+ if(!el)return;
+ el.innerHTML='加载中…
';
+ fetch('/api/feedback',{signal:AbortSignal.timeout(5000)}).then(function(r){return r.json()}).then(function(data){
+ var items=data.feedback||[];
+ if(items.length===0){
+ el.innerHTML='📮还没有留言
说点什么吧
';
+ return;
+ }
+ el.innerHTML=items.slice(0,20).map(function(fb){
+ var st=fb.status==='replied'?'已回复':'待回复';
+ var rp=fb.reply?'↩ '+esc(fb.reply)+'
':'';
+ return ''+
+ '
'+esc(fb.message)+'
'+
+ '
'+esc(fb.name||'匿名')+''+st+''+esc((fb.timestamp||'').slice(0,10))+'
'+
+ rp+'
';
+ }).join('');
+ }).catch(function(){
+ el.innerHTML='📡服务器不在线
留言功能需要后端支持
';
+ });
+}
+
+document.getElementById('fbSend').onclick=function(){
+ var msg=document.getElementById('fbMsg').value.trim();
+ if(!msg){document.getElementById('fbMsg').focus();return}
+ var btn=this;btn.disabled=true;btn.textContent='提交中…';
+ fetch('/api/feedback',{
+ method:'POST',
+ headers:{'Content-Type':'application/json'},
+ body:JSON.stringify({name:usr.name||'匿名来客',message:msg,userId:usr.id})
+ }).then(function(r){return r.json()}).then(function(data){
+ btn.disabled=false;btn.textContent='提交留言';
+ if(data.success){
+ document.getElementById('fbMsg').value='';
+ chatMsg('留言已提交 ✅ 冰朔会看到的','b',{m:'铸渊',t:'本地'});
+ renderFeedback();
+ }
+ }).catch(function(){
+ btn.disabled=false;btn.textContent='提交留言';
+ chatMsg('留言提交失败,服务器不在线','b',{m:'铸渊',t:'本地'});
+ });
+};
+
/* ════════════════════════════════════════
UTILS
════════════════════════════════════════ */
diff --git a/server/app/server.js b/server/app/server.js
index dce23025..c73129d6 100644
--- a/server/app/server.js
+++ b/server/app/server.js
@@ -424,6 +424,63 @@ app.get('/api/system/bulletin', (_req, res) => {
});
});
+// ═══════════════════════════════════════════════════════════
+// 留言板 · Feedback API
+// ═══════════════════════════════════════════════════════════
+
+const FEEDBACK_FILE = path.join(DATA_DIR, 'feedback.json');
+
+app.get('/api/feedback', (_req, res) => {
+ try {
+ if (fs.existsSync(FEEDBACK_FILE)) {
+ const data = JSON.parse(fs.readFileSync(FEEDBACK_FILE, 'utf8'));
+ res.json({ success: true, feedback: data.items || [] });
+ } else {
+ res.json({ success: true, feedback: [] });
+ }
+ } catch (err) {
+ res.status(500).json({ error: true, message: err.message });
+ }
+});
+
+app.post('/api/feedback', (req, res) => {
+ try {
+ const { name, message, userId } = req.body;
+ if (!message || typeof message !== 'string') {
+ return res.status(400).json({ error: true, message: '留言内容不能为空' });
+ }
+
+ let data = { items: [] };
+ if (fs.existsSync(FEEDBACK_FILE)) {
+ data = JSON.parse(fs.readFileSync(FEEDBACK_FILE, 'utf8'));
+ }
+
+ const item = {
+ id: `FB-${Date.now().toString(36)}`,
+ name: (typeof name === 'string' ? name.substring(0, 50) : '') || '匿名来客',
+ message: message.substring(0, 500),
+ userId: typeof userId === 'string' ? userId.substring(0, 50) : null,
+ status: 'pending',
+ timestamp: new Date().toISOString(),
+ reply: null
+ };
+
+ data.items.unshift(item);
+
+ // Keep only latest 100
+ if (data.items.length > 100) {
+ data.items = data.items.slice(0, 100);
+ }
+
+ fs.mkdirSync(DATA_DIR, { recursive: true });
+ fs.writeFileSync(FEEDBACK_FILE, JSON.stringify(data, null, 2));
+
+ res.json({ success: true, feedback: item });
+ } catch (err) {
+ res.status(500).json({ error: true, message: err.message });
+ }
+});
+
// ═══════════════════════════════════════════════════════════
// 双域名架构 · 预览→主站 一键推送
// ═══════════════════════════════════════════════════════════
@@ -643,6 +700,8 @@ app.get('/', (_req, res) => {
model_pricing: '/api/model/pricing',
model_predict: 'POST /api/model/predict',
bulletin: '/api/system/bulletin',
+ feedback: '/api/feedback',
+ feedback_submit: 'POST /api/feedback',
sites: '/api/sites',
promote: 'POST /api/sites/promote',
rollback: 'POST /api/sites/rollback',