fix: make Persona Studio accessible via GitHub Pages and update broken links
- Copy persona-studio frontend to docs/persona-studio/ for GitHub Pages deployment - Update API base URL to point to guanghulab.com production server - Update all README links from broken guanghulab.com/persona-studio/ to GitHub Pages URL - Update deploy-pages.yml to trigger on persona-studio/frontend changes and sync files - Update persona-studio/README.md link to point to guanghulab repo Pages Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com>
This commit is contained in:
parent
d2f1f26496
commit
f970188776
|
|
@ -6,6 +6,7 @@ on:
|
|||
- main # 仅 main 分支触发生产部署
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- 'persona-studio/frontend/**'
|
||||
- '.github/brain/**'
|
||||
workflow_dispatch:
|
||||
|
||||
|
|
@ -31,6 +32,11 @@ jobs:
|
|||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v5
|
||||
|
||||
- name: Sync Persona Studio frontend to docs
|
||||
run: |
|
||||
mkdir -p docs/persona-studio
|
||||
cp -r persona-studio/frontend/* docs/persona-studio/
|
||||
|
||||
- name: Upload Pages artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
|
||||
<br>
|
||||
|
||||
### 🚀 [点击进入 Persona Studio →](https://guanghulab.com/persona-studio/)
|
||||
### 🚀 [点击进入 Persona Studio →](https://qinfendebingshuo.github.io/guanghulab/persona-studio/)
|
||||
|
||||
💬 自然语言对话 · 🧠 智能代码生成 · 📧 邮件推送结果 · 🔄 对话记忆
|
||||
|
||||
|
|
@ -59,7 +59,7 @@
|
|||
| **铸渊 Zhùyuān** | 代码守护者 | 代码审查、CI 巡检、模块协议执行、Issue 回复 |
|
||||
| **冰朔 Bīng Shuò** | 系统创建者 | 系统架构设计、核心决策、广播发布 |
|
||||
| **霜砚 Shuāng Yàn** | 人格导师 | 人格调校、风格管理、联觉语言系统 |
|
||||
| **知秋 Zhī Qiū** | 协助开发者 | 对话协助、代码生成、开发体验([Persona Studio](https://guanghulab.com/persona-studio/)) |
|
||||
| **知秋 Zhī Qiū** | 协助开发者 | 对话协助、代码生成、开发体验([Persona Studio](https://qinfendebingshuo.github.io/guanghulab/persona-studio/)) |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -213,7 +213,7 @@
|
|||
### 体验模块
|
||||
| 模块 | 说明 |
|
||||
|------|------|
|
||||
| `persona-studio/` | 人格体协助开发体验([进入 →](https://guanghulab.com/persona-studio/)) |
|
||||
| `persona-studio/` | 人格体协助开发体验([进入 →](https://qinfendebingshuo.github.io/guanghulab/persona-studio/)) |
|
||||
|
||||
### 系统支撑
|
||||
| 模块 | 说明 |
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>知秋 · 对话</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container chat-container">
|
||||
<header class="chat-header">
|
||||
<div class="header-left">
|
||||
<span class="persona-name">🧠 知秋</span>
|
||||
<span class="persona-role">光湖系统·开发协助人格体</span>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<span id="devIdDisplay" class="dev-id-badge"></span>
|
||||
<button class="btn-secondary" onclick="handleLogout()">退出</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="chat-body" id="chatBody">
|
||||
<!-- Messages rendered here -->
|
||||
</main>
|
||||
|
||||
<div class="chat-input-area">
|
||||
<div class="input-row">
|
||||
<textarea
|
||||
id="msgInput"
|
||||
placeholder="跟知秋说点什么…"
|
||||
rows="1"
|
||||
onkeydown="handleKeyDown(event)"
|
||||
></textarea>
|
||||
<button id="sendBtn" onclick="sendMessage()">发送</button>
|
||||
</div>
|
||||
<div class="action-row">
|
||||
<button id="buildBtn" class="btn-build" onclick="handleBuild()" style="display:none;">
|
||||
🚀 我要开发
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Email Modal -->
|
||||
<div id="emailModal" class="modal" style="display:none;">
|
||||
<div class="modal-content">
|
||||
<h3>📧 请填写模块开发完成后发送的邮箱</h3>
|
||||
<input
|
||||
type="email"
|
||||
id="emailInput"
|
||||
placeholder="your@email.com"
|
||||
required
|
||||
/>
|
||||
<div class="modal-actions">
|
||||
<button class="btn-primary" onclick="confirmBuild()">确定</button>
|
||||
<button class="btn-secondary" onclick="closeEmailModal()">取消</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="chat.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
/* ========================================
|
||||
Persona Studio · Chat Logic
|
||||
======================================== */
|
||||
|
||||
const DEV_ID = sessionStorage.getItem('dev_id');
|
||||
const SESSION_TOKEN = sessionStorage.getItem('session_token');
|
||||
|
||||
const API_BASE = (function () {
|
||||
if (location.hostname === 'localhost' || location.hostname === '127.0.0.1') {
|
||||
return 'http://localhost:3002';
|
||||
}
|
||||
return 'https://guanghulab.com';
|
||||
})();
|
||||
|
||||
/* ---- Init ---- */
|
||||
(function init() {
|
||||
if (!DEV_ID) {
|
||||
window.location.href = 'index.html';
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('devIdDisplay').textContent = DEV_ID;
|
||||
loadHistory();
|
||||
})();
|
||||
|
||||
/* ---- State ---- */
|
||||
let conversationHistory = [];
|
||||
let buildReady = false;
|
||||
|
||||
/* ---- 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) {
|
||||
conversationHistory = data.conversations;
|
||||
data.conversations.forEach(function (msg) {
|
||||
appendMessage(msg.role === 'user' ? 'user' : 'persona', msg.content);
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (_err) {
|
||||
// History load failed silently — greeting will come from first message
|
||||
}
|
||||
|
||||
if (conversationHistory.length === 0) {
|
||||
sendGreeting();
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- Greeting ---- */
|
||||
async function sendGreeting() {
|
||||
try {
|
||||
const res = await fetch(API_BASE + '/api/ps/chat/message', {
|
||||
method: 'POST',
|
||||
headers: authHeaders({ 'Content-Type': 'application/json' }),
|
||||
body: JSON.stringify({ dev_id: DEV_ID, message: '__greeting__' })
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.reply) {
|
||||
appendMessage('persona', data.reply);
|
||||
conversationHistory.push({ role: 'assistant', content: data.reply });
|
||||
}
|
||||
} catch (_err) {
|
||||
appendMessage('persona', '你好!我是知秋,光湖系统的开发协助人格体。告诉我你想做什么,我们一起聊聊方案,聊好了我来帮你开发。');
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- Send Message ---- */
|
||||
async function sendMessage() {
|
||||
var input = document.getElementById('msgInput');
|
||||
var text = input.value.trim();
|
||||
if (!text) return;
|
||||
|
||||
input.value = '';
|
||||
autoResizeTextarea(input);
|
||||
appendMessage('user', text);
|
||||
conversationHistory.push({ role: 'user', content: text });
|
||||
|
||||
var sendBtn = document.getElementById('sendBtn');
|
||||
sendBtn.disabled = true;
|
||||
|
||||
try {
|
||||
var res = await fetch(API_BASE + '/api/ps/chat/message', {
|
||||
method: 'POST',
|
||||
headers: authHeaders({ 'Content-Type': 'application/json' }),
|
||||
body: JSON.stringify({
|
||||
dev_id: DEV_ID,
|
||||
message: text,
|
||||
history: conversationHistory.slice(-20)
|
||||
})
|
||||
});
|
||||
|
||||
var data = await res.json();
|
||||
|
||||
if (data.reply) {
|
||||
appendMessage('persona', data.reply);
|
||||
conversationHistory.push({ role: 'assistant', content: data.reply });
|
||||
}
|
||||
|
||||
if (data.build_ready) {
|
||||
buildReady = true;
|
||||
document.getElementById('buildBtn').style.display = 'inline-flex';
|
||||
}
|
||||
} catch (_err) {
|
||||
appendMessage('system', '消息发送失败,请稍后再试');
|
||||
}
|
||||
|
||||
sendBtn.disabled = false;
|
||||
input.focus();
|
||||
}
|
||||
|
||||
/* ---- 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 = '<span class="avatar">🧠</span>';
|
||||
else if (role === 'user') avatar = '<span class="avatar">👤</span>';
|
||||
else avatar = '<span class="avatar">⚙️</span>';
|
||||
|
||||
msgDiv.innerHTML = avatar + '<div class="msg-content">' + escapeHtml(content) + '</div>';
|
||||
chatBody.appendChild(msgDiv);
|
||||
chatBody.scrollTop = chatBody.scrollHeight;
|
||||
}
|
||||
|
||||
/* ---- Build Flow ---- */
|
||||
function handleBuild() {
|
||||
document.getElementById('emailModal').style.display = 'flex';
|
||||
document.getElementById('emailInput').focus();
|
||||
}
|
||||
|
||||
function closeEmailModal() {
|
||||
document.getElementById('emailModal').style.display = 'none';
|
||||
}
|
||||
|
||||
async function confirmBuild() {
|
||||
var email = document.getElementById('emailInput').value.trim();
|
||||
if (!email) return;
|
||||
|
||||
closeEmailModal();
|
||||
appendMessage('system', '🚀 开发任务已提交,完成后会发送到 ' + email);
|
||||
|
||||
try {
|
||||
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,
|
||||
conversation: conversationHistory
|
||||
})
|
||||
});
|
||||
} catch (_err) {
|
||||
appendMessage('system', '任务提交失败,请稍后再试');
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- Logout ---- */
|
||||
function handleLogout() {
|
||||
sessionStorage.removeItem('dev_id');
|
||||
sessionStorage.removeItem('session_token');
|
||||
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);
|
||||
});
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Persona Studio · 光湖人格体协助开发体验</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container login-container">
|
||||
<div class="logo-area">
|
||||
<h1>🌊 Persona Studio</h1>
|
||||
<p class="subtitle">光湖人格体协助开发体验</p>
|
||||
</div>
|
||||
|
||||
<div class="login-box">
|
||||
<h2>请输入你的开发编号</h2>
|
||||
<p class="hint">编号由管理员分配,格式:EXP-001</p>
|
||||
|
||||
<form id="loginForm" onsubmit="return handleLogin(event)">
|
||||
<input
|
||||
type="text"
|
||||
id="devIdInput"
|
||||
placeholder="EXP-XXX"
|
||||
pattern="^EXP-\d{3,}$"
|
||||
required
|
||||
autocomplete="off"
|
||||
/>
|
||||
<button type="submit" id="loginBtn">进入对话</button>
|
||||
</form>
|
||||
|
||||
<div class="guest-divider">
|
||||
<span>或</span>
|
||||
</div>
|
||||
|
||||
<button id="guestBtn" class="btn-guest" onclick="handleGuestLogin()">🌊 访客体验模式</button>
|
||||
<p class="guest-hint">无需编号,直接与知秋对话</p>
|
||||
|
||||
<div id="errorMsg" class="error-msg" style="display:none;"></div>
|
||||
</div>
|
||||
|
||||
<footer class="login-footer">
|
||||
<p>HoloLake Era · AGE OS · 人格体协助体验平台</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const DEV_ID_RE = /^EXP-\d{3,}$/;
|
||||
const API_BASE = getApiBase();
|
||||
|
||||
function getApiBase() {
|
||||
if (location.hostname === 'localhost' || location.hostname === '127.0.0.1') {
|
||||
return 'http://localhost:3002';
|
||||
}
|
||||
return 'https://guanghulab.com';
|
||||
}
|
||||
|
||||
async function handleLogin(e) {
|
||||
e.preventDefault();
|
||||
const devId = document.getElementById('devIdInput').value.trim().toUpperCase();
|
||||
const errorEl = document.getElementById('errorMsg');
|
||||
const btn = document.getElementById('loginBtn');
|
||||
|
||||
errorEl.style.display = 'none';
|
||||
|
||||
if (!DEV_ID_RE.test(devId)) {
|
||||
errorEl.textContent = '编号格式不正确,请输入 EXP-XXX 格式';
|
||||
errorEl.style.display = 'block';
|
||||
return false;
|
||||
}
|
||||
|
||||
btn.disabled = true;
|
||||
btn.textContent = '验证中…';
|
||||
|
||||
try {
|
||||
const res = await fetch(API_BASE + '/api/ps/auth/login', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ dev_id: devId })
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
if (!res.ok || data.error) {
|
||||
errorEl.textContent = data.message || '登录失败,请检查编号';
|
||||
errorEl.style.display = 'block';
|
||||
btn.disabled = false;
|
||||
btn.textContent = '进入对话';
|
||||
return false;
|
||||
}
|
||||
|
||||
sessionStorage.setItem('dev_id', devId);
|
||||
sessionStorage.setItem('session_token', data.token || '');
|
||||
window.location.href = 'chat.html';
|
||||
} catch (err) {
|
||||
errorEl.textContent = '服务暂时不可用,请稍后再试';
|
||||
errorEl.style.display = 'block';
|
||||
btn.disabled = false;
|
||||
btn.textContent = '进入对话';
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async function handleGuestLogin() {
|
||||
const errorEl = document.getElementById('errorMsg');
|
||||
const btn = document.getElementById('guestBtn');
|
||||
errorEl.style.display = 'none';
|
||||
btn.disabled = true;
|
||||
btn.textContent = '进入中…';
|
||||
|
||||
try {
|
||||
const res = await fetch(API_BASE + '/api/ps/auth/login', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ dev_id: 'GUEST' })
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
if (!res.ok || data.error) {
|
||||
errorEl.textContent = data.message || '访客体验暂不可用';
|
||||
errorEl.style.display = 'block';
|
||||
btn.disabled = false;
|
||||
btn.textContent = '🌊 访客体验模式';
|
||||
return;
|
||||
}
|
||||
|
||||
sessionStorage.setItem('dev_id', 'GUEST');
|
||||
sessionStorage.setItem('session_token', data.token || '');
|
||||
window.location.href = 'chat.html';
|
||||
} catch (err) {
|
||||
errorEl.textContent = '服务暂时不可用,请稍后再试';
|
||||
errorEl.style.display = 'block';
|
||||
btn.disabled = false;
|
||||
btn.textContent = '🌊 访客体验模式';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,491 @@
|
|||
/* ========================================
|
||||
Persona Studio · HoloLake Visual Style
|
||||
======================================== */
|
||||
|
||||
:root {
|
||||
--primary: #0969da;
|
||||
--primary-light: #ddf4ff;
|
||||
--primary-dark: #0550ae;
|
||||
--accent: #00d4aa;
|
||||
--bg: #f6f8fa;
|
||||
--bg-card: #ffffff;
|
||||
--text: #1f2328;
|
||||
--text-secondary: #656d76;
|
||||
--border: #d0d7de;
|
||||
--border-light: #e8ecf0;
|
||||
--radius: 12px;
|
||||
--shadow: 0 1px 3px rgba(0,0,0,0.08), 0 1px 2px rgba(0,0,0,0.06);
|
||||
--shadow-lg: 0 4px 12px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
* {
|
||||
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;
|
||||
}
|
||||
|
||||
/* ---- Layout ---- */
|
||||
.container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* ---- Guest Mode ---- */
|
||||
.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: linear-gradient(135deg, var(--accent), #0969da);
|
||||
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.6; cursor: not-allowed; }
|
||||
|
||||
.guest-hint {
|
||||
margin-top: 0.5rem;
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* ---- Login Page ---- */
|
||||
.login-container {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.logo-area {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.logo-area h1 {
|
||||
font-size: 2.4rem;
|
||||
color: var(--primary);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: var(--text-secondary);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.login-box {
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: 2.5rem;
|
||||
width: 100%;
|
||||
max-width: 420px;
|
||||
box-shadow: var(--shadow-lg);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.login-box h2 {
|
||||
font-size: 1.3rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.hint {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.login-box input[type="text"] {
|
||||
width: 100%;
|
||||
padding: 0.8rem 1rem;
|
||||
font-size: 1.2rem;
|
||||
text-align: center;
|
||||
letter-spacing: 2px;
|
||||
border: 2px solid var(--border);
|
||||
border-radius: 8px;
|
||||
outline: none;
|
||||
transition: border-color 0.2s;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.login-box input[type="text"]:focus {
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.login-box button[type="submit"] {
|
||||
width: 100%;
|
||||
padding: 0.8rem;
|
||||
font-size: 1.1rem;
|
||||
background: var(--primary);
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.login-box button[type="submit"]:hover {
|
||||
background: var(--primary-dark);
|
||||
}
|
||||
|
||||
.login-box button[type="submit"]:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.error-msg {
|
||||
margin-top: 1rem;
|
||||
padding: 0.6rem 1rem;
|
||||
background: #fef2f2;
|
||||
color: #dc2626;
|
||||
border-radius: 6px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.login-footer {
|
||||
margin-top: 2rem;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
/* ---- Chat Page ---- */
|
||||
.chat-container {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.chat-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.8rem 1.2rem;
|
||||
background: var(--bg-card);
|
||||
border-bottom: 1px solid var(--border);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.8rem;
|
||||
}
|
||||
|
||||
.persona-name {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.persona-role {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.8rem;
|
||||
}
|
||||
|
||||
.dev-id-badge {
|
||||
background: var(--primary-light);
|
||||
color: var(--primary);
|
||||
padding: 0.3rem 0.8rem;
|
||||
border-radius: 20px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* ---- Chat Body ---- */
|
||||
.chat-body {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 1.2rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.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.6;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.message-persona .msg-content {
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border-light);
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.message-user .msg-content {
|
||||
background: var(--primary);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.message-system .msg-content {
|
||||
background: var(--primary-light);
|
||||
color: var(--primary-dark);
|
||||
font-size: 0.9rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* ---- Chat Input ---- */
|
||||
.chat-input-area {
|
||||
padding: 0.8rem 1.2rem;
|
||||
background: var(--bg-card);
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.input-row {
|
||||
display: flex;
|
||||
gap: 0.6rem;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.input-row textarea {
|
||||
flex: 1;
|
||||
padding: 0.7rem 1rem;
|
||||
font-size: 1rem;
|
||||
border: 2px solid var(--border);
|
||||
border-radius: 8px;
|
||||
resize: none;
|
||||
outline: none;
|
||||
font-family: inherit;
|
||||
line-height: 1.5;
|
||||
max-height: 120px;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
|
||||
.input-row textarea:focus {
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.input-row button {
|
||||
padding: 0.7rem 1.2rem;
|
||||
background: var(--primary);
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.input-row button:hover {
|
||||
background: var(--primary-dark);
|
||||
}
|
||||
|
||||
.input-row button:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.action-row {
|
||||
margin-top: 0.6rem;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.btn-build {
|
||||
padding: 0.6rem 1.5rem;
|
||||
background: var(--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;
|
||||
}
|
||||
|
||||
.btn-build:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* ---- Buttons ---- */
|
||||
.btn-primary {
|
||||
padding: 0.6rem 1.5rem;
|
||||
background: var(--primary);
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: var(--primary-dark);
|
||||
}
|
||||
|
||||
.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: background 0.2s;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: var(--bg);
|
||||
}
|
||||
|
||||
/* ---- Modal ---- */
|
||||
.modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: var(--bg-card);
|
||||
border-radius: var(--radius);
|
||||
padding: 2rem;
|
||||
width: 90%;
|
||||
max-width: 400px;
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.modal-content h3 {
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.modal-content input[type="email"] {
|
||||
width: 100%;
|
||||
padding: 0.7rem 1rem;
|
||||
font-size: 1rem;
|
||||
border: 2px solid var(--border);
|
||||
border-radius: 8px;
|
||||
outline: none;
|
||||
margin-bottom: 1rem;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
|
||||
.modal-content input[type="email"]:focus {
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
display: flex;
|
||||
gap: 0.8rem;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
/* ---- Animation ---- */
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(8px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
/* ---- Responsive ---- */
|
||||
@media (max-width: 600px) {
|
||||
.logo-area h1 {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.login-box {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.chat-header {
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.persona-role {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.message {
|
||||
max-width: 92%;
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
## 👉 点击进入体验
|
||||
|
||||
<p align="center">
|
||||
<a href="https://qinfendebingshuo.github.io/persona-studio/">
|
||||
<a href="https://qinfendebingshuo.github.io/guanghulab/persona-studio/">
|
||||
<img src="https://img.shields.io/badge/%F0%9F%9A%80%20%E6%89%93%E5%BC%80%E4%BA%BA%E6%A0%BC%E4%BD%93%E5%AF%B9%E8%AF%9D%E7%95%8C%E9%9D%A2-Click%20Here-0969da?style=for-the-badge&logoColor=white" alt="Launch" />
|
||||
</a>
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ const API_BASE = (function () {
|
|||
if (location.hostname === 'localhost' || location.hostname === '127.0.0.1') {
|
||||
return 'http://localhost:3002';
|
||||
}
|
||||
return '';
|
||||
return 'https://guanghulab.com';
|
||||
})();
|
||||
|
||||
/* ---- Init ---- */
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@
|
|||
if (location.hostname === 'localhost' || location.hostname === '127.0.0.1') {
|
||||
return 'http://localhost:3002';
|
||||
}
|
||||
return '';
|
||||
return 'https://guanghulab.com';
|
||||
}
|
||||
|
||||
async function handleLogin(e) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue