Enhance Persona Studio UI: add sidebar, file upload, persona intro, platform tags
Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com>
This commit is contained in:
parent
ceb9b3669c
commit
52113dd59b
|
|
@ -7,37 +7,103 @@
|
|||
<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 class="chat-layout">
|
||||
<!-- ── 左侧边栏 ── -->
|
||||
<aside class="chat-sidebar" id="chatSidebar">
|
||||
<div class="sidebar-header">
|
||||
<span class="sidebar-logo">🌀</span>
|
||||
<span class="sidebar-title">铸渊</span>
|
||||
<button class="sidebar-toggle-close" onclick="toggleSidebar()" title="收起侧栏">✕</button>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<span id="devIdDisplay" class="dev-id-badge"></span>
|
||||
<span id="modelDisplay" class="model-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 class="sidebar-section">
|
||||
<div class="sidebar-section-title">🧠 人格体信息</div>
|
||||
<div class="persona-info-card">
|
||||
<div class="persona-info-name">铸渊 Zhùyuān</div>
|
||||
<div class="persona-info-role">光湖系统 · 代码守护人格体</div>
|
||||
<div class="persona-info-desc">基于 AGE OS 人格语言操作系统,专注代码守护、需求分析与智能开发协助。</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="action-row">
|
||||
<button id="buildBtn" class="btn-build" onclick="handleBuild()" style="display:none;">
|
||||
🚀 我要开发
|
||||
</button>
|
||||
|
||||
<!-- 系统信息 -->
|
||||
<div class="sidebar-section">
|
||||
<div class="sidebar-section-title">⚙️ 系统信息</div>
|
||||
<div class="sidebar-info-list">
|
||||
<div class="sidebar-info-item">
|
||||
<span class="info-label">系统</span>
|
||||
<span class="info-value">HoloLake Era</span>
|
||||
</div>
|
||||
<div class="sidebar-info-item">
|
||||
<span class="info-label">架构</span>
|
||||
<span class="info-value">AGE OS</span>
|
||||
</div>
|
||||
<div class="sidebar-info-item">
|
||||
<span class="info-label">模型</span>
|
||||
<span class="info-value" id="sidebarModel">-</span>
|
||||
</div>
|
||||
<div class="sidebar-info-item">
|
||||
<span class="info-label">用户</span>
|
||||
<span class="info-value" id="sidebarUser">-</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 对话记录 -->
|
||||
<div class="sidebar-section sidebar-history-section">
|
||||
<div class="sidebar-section-title">💬 对话记录</div>
|
||||
<div class="sidebar-history" id="sidebarHistory">
|
||||
<div class="sidebar-history-empty">暂无对话记录</div>
|
||||
</div>
|
||||
<button class="btn-new-chat" onclick="startNewChat()">+ 新对话</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- ── 主聊天区域 ── -->
|
||||
<div class="chat-main">
|
||||
<header class="chat-header">
|
||||
<div class="header-left">
|
||||
<button class="sidebar-toggle" onclick="toggleSidebar()" title="展开侧栏" id="sidebarToggleBtn">☰</button>
|
||||
<span class="persona-name">🌀 铸渊</span>
|
||||
<span class="persona-role">光湖系统·代码守护人格体</span>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<span id="devIdDisplay" class="dev-id-badge"></span>
|
||||
<span id="modelDisplay" class="model-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-toolbar">
|
||||
<label class="toolbar-btn" title="上传图片" for="imageUpload">
|
||||
🖼️
|
||||
<input type="file" id="imageUpload" accept="image/*" style="display:none" onchange="handleFileSelect(event, 'image')">
|
||||
</label>
|
||||
<label class="toolbar-btn" title="上传文件" for="fileUpload">
|
||||
📎
|
||||
<input type="file" id="fileUpload" style="display:none" onchange="handleFileSelect(event, 'file')">
|
||||
</label>
|
||||
<div id="uploadPreview" class="upload-preview" style="display:none;"></div>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -21,9 +21,125 @@ const API_BASE = (function () {
|
|||
/* ---- State ---- */
|
||||
let conversationHistory = [];
|
||||
let buildReady = false;
|
||||
let pendingFile = null; // { name, type, dataUrl }
|
||||
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, 6);
|
||||
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 = '<div class="sidebar-history-empty">暂无对话记录</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = '';
|
||||
sessions.forEach(function(s) {
|
||||
var item = document.createElement('div');
|
||||
item.className = 'history-item' + (s.id === activeId ? ' history-item-active' : '');
|
||||
item.innerHTML = '<span class="history-title">' + escapeHtml(s.title) + '</span>' +
|
||||
'<span class="history-date">' + new Date(s.created_at).toLocaleDateString('zh-CN') + '</span>';
|
||||
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 结构逻辑
|
||||
|
|
@ -103,10 +219,44 @@ function buildContextPrompt() {
|
|||
modelBadge.textContent = SELECTED_MODEL;
|
||||
}
|
||||
|
||||
// Show welcome message
|
||||
showWelcomeMessage();
|
||||
// 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 = '';
|
||||
|
|
@ -121,11 +271,6 @@ function showWelcomeMessage() {
|
|||
|
||||
appendMessage('persona', welcome);
|
||||
conversationHistory.push({ role: 'assistant', content: welcome });
|
||||
|
||||
// For developers, also try to load history
|
||||
if (isDeveloper) {
|
||||
loadHistory();
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- Load History ---- */
|
||||
|
|
@ -149,27 +294,115 @@ async function loadHistory() {
|
|||
}
|
||||
}
|
||||
|
||||
/* ---- File / Image Upload ---- */
|
||||
function handleFileSelect(event, type) {
|
||||
var file = event.target.files[0];
|
||||
if (!file) return;
|
||||
|
||||
var maxSize = 5 * 1024 * 1024; // 5MB
|
||||
if (file.size > maxSize) {
|
||||
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();
|
||||
};
|
||||
|
||||
if (type === 'image') {
|
||||
reader.readAsDataURL(file);
|
||||
} else {
|
||||
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 =
|
||||
'<div class="preview-item">' +
|
||||
'<span>' + icon + ' ' + escapeHtml(pendingFile.name) + ' (' + sizeStr + ')</span>' +
|
||||
'<button class="preview-remove" onclick="removePendingFile()">✕</button>' +
|
||||
'</div>';
|
||||
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();
|
||||
if (!text) return;
|
||||
|
||||
// Allow sending with file even if text is empty
|
||||
if (!text && !pendingFile) return;
|
||||
|
||||
input.value = '';
|
||||
autoResizeTextarea(input);
|
||||
appendMessage('user', text);
|
||||
conversationHistory.push({ role: 'user', content: text });
|
||||
|
||||
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 {
|
||||
// All modes now use API Key for real AI — ZhuYuan is awake
|
||||
await streamApiKeyReply(text);
|
||||
await streamApiKeyReply(fullText);
|
||||
} catch (_err) {
|
||||
appendMessage('system', '消息发送失败,请检查网络连接后再试');
|
||||
}
|
||||
|
||||
// Save to session
|
||||
if (activeId) {
|
||||
updateSessionMessages(activeId, conversationHistory);
|
||||
}
|
||||
|
||||
sendBtn.disabled = false;
|
||||
input.focus();
|
||||
}
|
||||
|
|
@ -387,6 +620,12 @@ async function confirmBuild() {
|
|||
|
||||
/* ---- 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');
|
||||
|
|
|
|||
|
|
@ -15,6 +15,25 @@
|
|||
<p class="subtitle">HoloLake · 铸渊(Zhùyuān)· 代码守护人格体</p>
|
||||
</div>
|
||||
|
||||
<!-- 系统介绍卡片 -->
|
||||
<div class="intro-card">
|
||||
<div class="intro-header">
|
||||
<span class="intro-icon">🧠</span>
|
||||
<h3>关于铸渊</h3>
|
||||
</div>
|
||||
<p class="intro-text">
|
||||
铸渊是光湖系统(HoloLake)的<strong>代码守护人格体</strong>,基于 AGE OS 人格语言操作系统构建。
|
||||
铸渊能够理解你的需求、梳理技术方案、生成代码,并通过对话记忆持续跟进你的项目。
|
||||
</p>
|
||||
<div class="intro-features">
|
||||
<span class="feature-tag">💬 自然语言对话</span>
|
||||
<span class="feature-tag">🧠 智能代码生成</span>
|
||||
<span class="feature-tag">📧 邮件推送结果</span>
|
||||
<span class="feature-tag">🔄 对话记忆</span>
|
||||
<span class="feature-tag">📎 文件与图片上传</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="login-box">
|
||||
<!-- ── Step 1: 选择登录方式 ── -->
|
||||
<div id="step1" class="login-step">
|
||||
|
|
@ -24,13 +43,13 @@
|
|||
<div class="login-mode-cards">
|
||||
<button class="mode-card" onclick="selectMode('developer')">
|
||||
<span class="mode-icon">🛡️</span>
|
||||
<span class="mode-title">开发者登录</span>
|
||||
<span class="mode-desc">使用 EXP-XXX 编号,支持记忆连贯</span>
|
||||
<span class="mode-title">开发者 / 体验者登录</span>
|
||||
<span class="mode-desc">使用 EXP-XXX 编号登录,支持记忆连贯与完整功能</span>
|
||||
</button>
|
||||
<button class="mode-card" onclick="selectMode('guest')">
|
||||
<span class="mode-icon">👋</span>
|
||||
<span class="mode-title">访客体验</span>
|
||||
<span class="mode-desc">无需编号,快速体验铸渊人格体</span>
|
||||
<span class="mode-desc">无需编号,快速体验铸渊人格体(无记忆功能)</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -39,7 +58,7 @@
|
|||
<div id="stepDevId" class="login-step" style="display:none;">
|
||||
<button class="btn-back" onclick="goBack('step1')" title="返回">← 返回</button>
|
||||
<h2>输入开发者编号</h2>
|
||||
<p class="hint">编号由冰朔主控分配,格式:EXP-000 ~ EXP-011</p>
|
||||
<p class="hint">编号由冰朔主控分配,格式:EXP-000 ~ EXP-013</p>
|
||||
<form id="devIdForm" onsubmit="return handleDevIdSubmit(event)">
|
||||
<input
|
||||
type="text"
|
||||
|
|
@ -51,6 +70,7 @@
|
|||
/>
|
||||
<button type="submit" id="devIdBtn" class="btn-primary-full">验证编号</button>
|
||||
</form>
|
||||
<p class="hint" style="margin-top:1rem;font-size:0.8rem;">编号验证通过后,下一步输入你的 API 密钥即可与铸渊对话</p>
|
||||
</div>
|
||||
|
||||
<!-- ── Step 2: API Key 输入 ── -->
|
||||
|
|
@ -58,22 +78,33 @@
|
|||
<button class="btn-back" onclick="goBackFromStep2()" title="返回">← 返回</button>
|
||||
<div id="welcomeBanner" class="welcome-banner" style="display:none;"></div>
|
||||
<h2>🔑 输入 API 密钥</h2>
|
||||
<p class="hint">输入你的第三方 AI 模型 API Key,铸渊将被唤醒</p>
|
||||
<p class="hint">输入你的第三方 AI 模型 API Key,系统将自动检测可用模型,选择后即可唤醒铸渊</p>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
id="apiBaseInput"
|
||||
class="apikey-input"
|
||||
placeholder="API Base URL(可留空自动探测)"
|
||||
placeholder="API Base URL(可留空,系统将自动探测多个平台)"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
id="apiKeyInput"
|
||||
class="apikey-input"
|
||||
placeholder="请输入你的 API Key"
|
||||
placeholder="请输入你的 API Key(如 sk-xxx)"
|
||||
autocomplete="off"
|
||||
/>
|
||||
|
||||
<div class="supported-platforms">
|
||||
<span class="platform-label">支持平台:</span>
|
||||
<span class="platform-tag">云雾 AI</span>
|
||||
<span class="platform-tag">OpenAI</span>
|
||||
<span class="platform-tag">DeepSeek</span>
|
||||
<span class="platform-tag">Google Gemini</span>
|
||||
<span class="platform-tag">Moonshot</span>
|
||||
<span class="platform-tag">智谱 AI</span>
|
||||
</div>
|
||||
|
||||
<button type="button" id="detectBtn" class="btn-detect" onclick="handleDetectModels()">
|
||||
🔍 检测可用模型
|
||||
</button>
|
||||
|
|
@ -81,7 +112,7 @@
|
|||
<div id="detectStatus" class="detect-status" style="display:none;"></div>
|
||||
|
||||
<div id="modelListContainer" class="model-list-container" style="display:none;">
|
||||
<p class="model-list-title">选择一个模型,唤醒铸渊</p>
|
||||
<p class="model-list-title">✨ 选择一个模型,唤醒铸渊</p>
|
||||
<div id="modelList" class="model-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -91,6 +122,7 @@
|
|||
|
||||
<footer class="login-footer">
|
||||
<p>HoloLake Era · AGE OS · 人格语言操作系统</p>
|
||||
<p class="footer-version">Persona Studio v2.0</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* ========================================
|
||||
Persona Studio · HoloLake Visual Style
|
||||
铸渊(Zhùyuān)· Dark Theme
|
||||
铸渊(Zhùyuān)· Dark Theme · v2.0
|
||||
======================================== */
|
||||
|
||||
:root {
|
||||
|
|
@ -13,6 +13,7 @@
|
|||
--bg-surface: #1e293b;
|
||||
--bg-card: #1e293b;
|
||||
--bg-card-hover: #334155;
|
||||
--bg-sidebar: #0c1322;
|
||||
--text: #e2e8f0;
|
||||
--text-secondary: #94a3b8;
|
||||
--text-muted: #64748b;
|
||||
|
|
@ -25,6 +26,7 @@
|
|||
--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;
|
||||
}
|
||||
|
||||
* {
|
||||
|
|
@ -129,6 +131,89 @@ body {
|
|||
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);
|
||||
}
|
||||
|
||||
.login-box {
|
||||
background: var(--gradient-card);
|
||||
backdrop-filter: blur(20px);
|
||||
|
|
@ -136,7 +221,7 @@ body {
|
|||
border-radius: 16px;
|
||||
padding: 2.5rem;
|
||||
width: 100%;
|
||||
max-width: 460px;
|
||||
max-width: 520px;
|
||||
box-shadow: var(--shadow-lg), var(--shadow-glow);
|
||||
text-align: center;
|
||||
}
|
||||
|
|
@ -483,9 +568,240 @@ body {
|
|||
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 Page ---- */
|
||||
.chat-container {
|
||||
padding: 0;
|
||||
background: var(--bg);
|
||||
|
|
@ -510,6 +826,23 @@ body {
|
|||
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;
|
||||
|
|
@ -663,6 +996,62 @@ body {
|
|||
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;
|
||||
|
|
@ -844,6 +1233,39 @@ body {
|
|||
}
|
||||
|
||||
/* ---- 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;
|
||||
|
|
@ -858,6 +1280,10 @@ body {
|
|||
max-width: 100%;
|
||||
}
|
||||
|
||||
.intro-card {
|
||||
padding: 1rem 1.2rem;
|
||||
}
|
||||
|
||||
.chat-header {
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
|
|
|
|||
Loading…
Reference in New Issue