diff --git a/app.js b/app.js
index 8d67d91..ccdde6d 100644
--- a/app.js
+++ b/app.js
@@ -1,31 +1,351 @@
(() => {
'use strict';
- const landing = document.getElementById('landing');
- const creator = document.getElementById('creator');
- const auth = document.getElementById('auth');
- const distill = document.getElementById('distill');
+ // --- U9: Unified state management ---
+ const STORAGE_KEY = 'eternal_ai_state';
+
+ const defaultState = {
+ isLoggedIn: false,
+ isCreator: false,
+ account: null,
+ boundCreator: null,
+ libraryName: '我的 [XXX]',
+ creatorName: '',
+ roles: [],
+ income: { balance: 0, records: [] },
+ };
+
+ function loadState() {
+ try {
+ const saved = JSON.parse(localStorage.getItem(STORAGE_KEY));
+ return { ...defaultState, ...saved };
+ } catch {
+ return { ...defaultState };
+ }
+ }
+
+ function saveState() {
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
+ }
+
+ const state = loadState();
+
+ // --- Mock data for role library (U2) ---
+ const mockRoles = [
+ {
+ id: 'role_001',
+ name: '云朵',
+ avatar: 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=anime%20girl%20soft%20pastel%20portrait%20gentle%20smile&image_size=square',
+ desc: '温柔如云的女孩,总是轻声细语地陪伴你。',
+ price: 29.9,
+ status: 'running',
+ },
+ {
+ id: 'role_002',
+ name: '星河',
+ avatar: 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=anime%20boy%20starry%20eyes%20cool%20portrait&image_size=square',
+ desc: '嘴硬心软的少年,嘴上不饶人却总在关键时刻出现。',
+ price: 39.9,
+ status: 'running',
+ },
+ {
+ id: 'role_003',
+ name: '月见',
+ avatar: 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=anime%20girl%20moonlight%20mysterious%20elegant%20portrait&image_size=square',
+ desc: '神秘而优雅,像月光一样忽远忽近的存在。',
+ price: 19.9,
+ status: 'stopped',
+ },
+ ];
+
+ const mockIncome = {
+ balance: 1280.50,
+ records: [
+ { time: '2026-06-18 14:30', amount: 23.92, role: '云朵' },
+ { time: '2026-06-15 09:12', amount: 31.92, role: '星河' },
+ { time: '2026-06-10 20:05', amount: 15.92, role: '月见' },
+ ],
+ };
+
+ // --- DOM references ---
+ const views = {
+ landing: document.getElementById('landing'),
+ auth: document.getElementById('auth'),
+ 'role-library': document.getElementById('role-library'),
+ 'role-detail': document.getElementById('role-detail'),
+ distill: document.getElementById('distill'),
+ about: document.getElementById('about'),
+ onboarding: document.getElementById('onboarding'),
+ 'creator-center': document.getElementById('creator-center'),
+ creator: document.getElementById('creator'),
+ };
+
const form = document.getElementById('character-form');
const resultPanel = document.getElementById('result-panel');
const previewCode = document.querySelector('#preview-code code');
const systemPromptInput = document.getElementById('system-prompt');
-
- const views = { landing, creator, auth, distill };
const steps = Array.from(document.querySelectorAll('.form-step'));
- const dots = Array.from(document.querySelectorAll('.stepper__dot'));
+ const dots = Array.from(document.querySelectorAll('#creator .stepper__dot'));
let currentStep = 0;
let generatedSoul = '';
let generatedConfig = '';
let activePreview = 'soul';
let activeAuthTab = 'login';
+ let activeCenterTab = 'roles';
+ let currentRole = null;
+ let viewHistory = ['landing'];
- function showView(name) {
+ // --- U9: Unified showView with history and tab-bar sync ---
+ function showView(name, trackHistory = true) {
Object.entries(views).forEach(([key, el]) => {
if (el) el.classList.toggle('active', key === name);
});
+ if (trackHistory && viewHistory[viewHistory.length - 1] !== name) {
+ viewHistory.push(name);
+ }
window.scrollTo({ top: 0, behavior: 'smooth' });
+ updateTabBar(name);
}
+ function goBack() {
+ if (viewHistory.length > 1) {
+ viewHistory.pop();
+ const prev = viewHistory[viewHistory.length - 1];
+ showView(prev, false);
+ } else {
+ showView('landing', false);
+ }
+ }
+
+ // --- U8: Tab bar ---
+ function updateTabBar(viewName) {
+ const tabMap = {
+ landing: 'tab-home',
+ distill: 'tab-distill',
+ 'role-library': 'tab-mine',
+ 'creator-center': 'tab-mine',
+ };
+ const activeTab = tabMap[viewName] || 'tab-home';
+ document.querySelectorAll('.tab-bar__item').forEach((item) => {
+ item.classList.toggle('active', item.dataset.tabAction === activeTab);
+ });
+ }
+
+ function handleTabAction(action) {
+ if (action === 'tab-home') {
+ showView('landing');
+ } else if (action === 'tab-distill') {
+ showView('distill');
+ } else if (action === 'tab-mine') {
+ if (!state.isLoggedIn) {
+ switchAuthTab('login');
+ showView('auth');
+ } else if (state.isCreator) {
+ showView('creator-center');
+ renderCreatorCenter();
+ } else {
+ renderRoleLibrary();
+ showView('role-library');
+ }
+ }
+ }
+
+ // --- U1: Landing card state ---
+ function updateLandingCard() {
+ const nameEl = document.getElementById('library-name');
+ const descEl = document.getElementById('characters-desc');
+ const btnEl = document.getElementById('characters-btn');
+ const tabMineLabel = document.getElementById('tab-mine-label');
+
+ if (state.isLoggedIn) {
+ nameEl.textContent = state.libraryName || '我的角色库';
+ if (state.isCreator) {
+ descEl.textContent = '管理你的角色和收入';
+ btnEl.textContent = '进入管理中心';
+ tabMineLabel.textContent = '管理';
+ } else {
+ descEl.textContent = state.boundCreator ? '查看你的专属角色' : '寻找你的专属创作者';
+ btnEl.textContent = '进入角色库';
+ tabMineLabel.textContent = '我的';
+ }
+ } else {
+ nameEl.textContent = '我的 [XXX]';
+ descEl.textContent = '登录后管理你的角色';
+ btnEl.textContent = '登录 / 注册';
+ tabMineLabel.textContent = '我的';
+ }
+ }
+
+ // --- Auth ---
+ function switchAuthTab(tab) {
+ activeAuthTab = tab;
+ document.querySelectorAll('.auth-tab').forEach((t) => {
+ t.classList.toggle('active', t.dataset.tab === tab);
+ });
+ document.querySelectorAll('.auth-form').forEach((f) => {
+ f.classList.toggle('active', f.dataset.form === tab);
+ });
+ }
+
+ function validatePasswordMatch(formEl) {
+ const pwd = formEl.querySelector('[name="password"]');
+ const confirm = formEl.querySelector('[name="confirmPassword"]');
+ if (!pwd || !confirm) return true;
+ if (pwd.value !== confirm.value) {
+ confirm.setCustomValidity('两次输入的密码不一致');
+ confirm.reportValidity();
+ return false;
+ }
+ confirm.setCustomValidity('');
+ return true;
+ }
+
+ function login(account) {
+ state.isLoggedIn = true;
+ state.account = account;
+ state.boundCreator = state.boundCreator || { name: '云朵', roles: mockRoles };
+ saveState();
+ updateLandingCard();
+ }
+
+ function logout() {
+ state.isLoggedIn = false;
+ state.isCreator = false;
+ state.account = null;
+ saveState();
+ updateLandingCard();
+ showView('landing');
+ }
+
+ // --- U2: Role Library ---
+ function renderRoleLibrary() {
+ const listEl = document.getElementById('role-list');
+ const emptyEl = document.getElementById('library-empty');
+ const titleEl = document.getElementById('library-title');
+
+ titleEl.textContent = state.libraryName || '我的角色库';
+
+ if (!state.boundCreator) {
+ listEl.innerHTML = '';
+ emptyEl.hidden = false;
+ return;
+ }
+
+ emptyEl.hidden = true;
+ listEl.innerHTML = mockRoles
+ .map(
+ (role) => `
+
+
+
+
${role.name}
+
${role.desc}
+
¥${role.price}
+
+ `
+ )
+ .join('');
+ }
+
+ // --- U3: Role Detail ---
+ function renderRoleDetail(roleId) {
+ const role = mockRoles.find((r) => r.id === roleId);
+ if (!role) return;
+ currentRole = role;
+
+ document.getElementById('detail-name').textContent = role.name;
+ document.getElementById('detail-hero').style.backgroundImage = `url(${role.avatar})`;
+ document.getElementById('detail-role-name').textContent = role.name;
+ document.getElementById('detail-role-desc').textContent = role.desc;
+ document.getElementById('detail-price').innerHTML = `¥${role.price}/ 月`;
+
+ document.getElementById('detail-actions-pre').hidden = false;
+ document.getElementById('detail-paid').hidden = true;
+ }
+
+ function payRole() {
+ document.getElementById('detail-actions-pre').hidden = true;
+ const paidEl = document.getElementById('detail-paid');
+ paidEl.hidden = false;
+ document.getElementById('detail-qr').innerHTML = '
扫码连接
AI 角色
';
+ document.getElementById('detail-avatar').style.backgroundImage = `url(${currentRole.avatar})`;
+ }
+
+ // --- U4: About FAQ ---
+ function toggleFaq(button) {
+ const item = button.closest('.faq-item');
+ const answer = item.querySelector('.faq-a');
+ const icon = button.querySelector('.faq-icon');
+ const isOpen = answer.style.display === 'block';
+ answer.style.display = isOpen ? 'none' : 'block';
+ icon.textContent = isOpen ? '+' : '−';
+ }
+
+ // --- U7: Creator Center ---
+ function renderCreatorCenter() {
+ renderCreatorRoles();
+ renderIncome();
+ renderSettings();
+ }
+
+ function renderCreatorRoles() {
+ const listEl = document.getElementById('creator-role-list');
+ const roles = state.roles.length > 0 ? state.roles : mockRoles;
+ listEl.innerHTML = roles
+ .map(
+ (role) => `
+
+
+
+
${role.name}
+ ${role.status === 'running' ? '运行中' : '已停止'}
+
+
+ `
+ )
+ .join('');
+ }
+
+ function renderIncome() {
+ const income = state.income.balance > 0 ? state.income : mockIncome;
+ document.getElementById('income-balance').textContent = `¥ ${income.balance.toFixed(2)}`;
+ const listEl = document.getElementById('income-list');
+ if (income.records.length === 0) {
+ listEl.innerHTML = '暂无流水记录
';
+ return;
+ }
+ listEl.innerHTML = income.records
+ .map(
+ (r) => `
+
+
+ ${r.role}
+ ${r.time}
+
+
+¥${r.amount.toFixed(2)}
+
`
+ )
+ .join('');
+ }
+
+ function renderSettings() {
+ document.getElementById('settings-name').value = state.creatorName || '';
+ document.getElementById('settings-library').value = state.libraryName === '我的 [XXX]' ? '' : state.libraryName;
+ }
+
+ function switchCenterTab(tab) {
+ activeCenterTab = tab;
+ document.querySelectorAll('.center-tab').forEach((t) => {
+ t.classList.toggle('active', t.dataset.centerTab === tab);
+ });
+ document.querySelectorAll('.center-panel').forEach((p) => {
+ p.classList.toggle('active', p.id === `center-${tab}`);
+ });
+ const labels = { roles: '我的角色', income: '收入', settings: '我的' };
+ document.getElementById('center-tab-label').textContent = labels[tab] || '我的角色';
+ }
+
+ // --- Creator form (U6: preserved from original) ---
function updateStep(index) {
steps.forEach((step, i) => {
step.classList.toggle('active', i === index);
@@ -255,47 +575,59 @@ ${data.secrets || '无'}
});
}
- function switchAuthTab(tab) {
- activeAuthTab = tab;
- document.querySelectorAll('.auth-tab').forEach((t) => {
- t.classList.toggle('active', t.dataset.tab === tab);
- });
- document.querySelectorAll('.auth-form').forEach((f) => {
- f.classList.toggle('active', f.dataset.form === tab);
- });
- }
-
- function validatePasswordMatch(formEl) {
- const pwd = formEl.querySelector('[name="password"]');
- const confirm = formEl.querySelector('[name="confirmPassword"]');
- if (!pwd || !confirm) return true;
- if (pwd.value !== confirm.value) {
- confirm.setCustomValidity('两次输入的密码不一致');
- confirm.reportValidity();
- return false;
- }
- confirm.setCustomValidity('');
- return true;
- }
-
- // Event delegation
+ // --- Event delegation ---
document.addEventListener('click', (e) => {
- const target = e.target.closest('[data-action], [data-tab], [data-download]');
+ // FAQ toggle (U4)
+ const faqBtn = e.target.closest('.faq-q');
+ if (faqBtn) {
+ e.preventDefault();
+ toggleFaq(faqBtn);
+ return;
+ }
+
+ // Role card click (U2/U3)
+ const roleCard = e.target.closest('.role-card');
+ if (roleCard && !e.target.closest('[data-action]')) {
+ e.preventDefault();
+ const roleId = roleCard.dataset.roleId;
+ renderRoleDetail(roleId);
+ showView('role-detail');
+ return;
+ }
+
+ // Tab bar (U8)
+ const tabItem = e.target.closest('[data-tab-action]');
+ if (tabItem) {
+ e.preventDefault();
+ handleTabAction(tabItem.dataset.tabAction);
+ return;
+ }
+
+ const target = e.target.closest('[data-action], [data-tab], [data-download], [data-center-tab]');
if (!target) return;
const action = target.dataset.action;
- if (action === 'open-creator') {
+ // Center tab switching (U7)
+ if (target.dataset.centerTab) {
e.preventDefault();
- resetCreator();
- showView('creator');
+ switchCenterTab(target.dataset.centerTab);
return;
}
- if (action === 'open-auth') {
+ // Landing card: open characters (U1)
+ if (action === 'open-characters') {
e.preventDefault();
- switchAuthTab('login');
- showView('auth');
+ if (!state.isLoggedIn) {
+ switchAuthTab('login');
+ showView('auth');
+ } else if (state.isCreator) {
+ showView('creator-center');
+ renderCreatorCenter();
+ } else {
+ renderRoleLibrary();
+ showView('role-library');
+ }
return;
}
@@ -305,12 +637,86 @@ ${data.secrets || '无'}
return;
}
- if (action === 'back') {
+ if (action === 'open-about') {
e.preventDefault();
- showView('landing');
+ showView('about');
return;
}
+ if (action === 'open-onboarding') {
+ e.preventDefault();
+ showView('onboarding');
+ return;
+ }
+
+ if (action === 'back') {
+ e.preventDefault();
+ goBack();
+ return;
+ }
+
+ if (action === 'back-to-library') {
+ e.preventDefault();
+ renderRoleLibrary();
+ showView('role-library');
+ return;
+ }
+
+ if (action === 'back-to-center') {
+ e.preventDefault();
+ showView('creator-center');
+ renderCreatorCenter();
+ return;
+ }
+
+ if (action === 'pay') {
+ e.preventDefault();
+ payRole();
+ return;
+ }
+
+ if (action === 'pay-distill') {
+ e.preventDefault();
+ alert('下单成功!请添加客服微信完成后续流程。');
+ return;
+ }
+
+ if (action === 'contact-wechat') {
+ e.preventDefault();
+ alert('客服微信号:EternalAI_Service');
+ return;
+ }
+
+ if (action === 'new-role') {
+ e.preventDefault();
+ resetCreator();
+ showView('creator');
+ return;
+ }
+
+ if (action === 'edit-role') {
+ e.preventDefault();
+ resetCreator();
+ showView('creator');
+ return;
+ }
+
+ if (action === 'logout') {
+ e.preventDefault();
+ logout();
+ return;
+ }
+
+ if (action === 'download-avatar') {
+ e.preventDefault();
+ if (currentRole) {
+ download(currentRole.name + '_avatar.png', '');
+ window.open(currentRole.avatar, '_blank');
+ }
+ return;
+ }
+
+ // Creator form navigation
if (action === 'next') {
e.preventDefault();
if (validateStep(currentStep) && currentStep < steps.length - 1) {
@@ -339,6 +745,7 @@ ${data.secrets || '无'}
return;
}
+ // Tab switching (auth tabs and preview tabs)
if (target.dataset.tab) {
e.preventDefault();
if (target.closest('.auth-tabs')) {
@@ -351,6 +758,7 @@ ${data.secrets || '无'}
return;
}
+ // Download buttons
if (target.dataset.download) {
e.preventDefault();
if (target.dataset.download === 'soul') {
@@ -362,25 +770,60 @@ ${data.secrets || '无'}
}
});
- // Auth form submissions
+ // --- Auth form submissions ---
document.querySelectorAll('.auth-form').forEach((authForm) => {
authForm.addEventListener('submit', (e) => {
e.preventDefault();
if (!validatePasswordMatch(authForm)) return;
const formData = new FormData(authForm);
const data = Object.fromEntries(formData.entries());
- const action = authForm.dataset.form === 'login' ? '登录' : '注册';
- alert(`${action}成功:${data.account}`);
- showView('landing');
+ login(data.account);
+
+ // After login, go to role library or creator center
+ if (state.isCreator) {
+ showView('creator-center');
+ renderCreatorCenter();
+ } else {
+ renderRoleLibrary();
+ showView('role-library');
+ }
});
});
- // Update auto-generated system prompt as user types
+ // --- Settings form (U7) ---
+ document.getElementById('settings-form').addEventListener('submit', (e) => {
+ e.preventDefault();
+ const formData = new FormData(e.target);
+ const data = Object.fromEntries(formData.entries());
+ state.creatorName = data.creatorName || '';
+ state.libraryName = data.libraryName || '我的 [XXX]';
+ saveState();
+ updateLandingCard();
+ alert('设置已保存');
+ });
+
+ // --- Withdraw form (U7) ---
+ document.getElementById('withdraw-form').addEventListener('submit', (e) => {
+ e.preventDefault();
+ const formData = new FormData(e.target);
+ const data = Object.fromEntries(formData.entries());
+ const amount = parseFloat(data.amount);
+ if (amount > state.income.balance && amount > mockIncome.balance) {
+ alert('提现金额超过可提现余额');
+ return;
+ }
+ alert(`提现申请已提交:${data.method === 'wechat' ? '微信' : '支付宝'} ¥${amount.toFixed(2)}\n平台负责人将手动审核转账。`);
+ e.target.reset();
+ });
+
+ // --- Creator form input ---
form.addEventListener('input', () => {
updateSystemPromptPreview();
});
- // Initial state
+ // --- Initialize ---
updateStep(0);
updateSystemPromptPreview();
+ updateLandingCard();
+ updateTabBar('landing');
})();
diff --git a/index.html b/index.html
index b5456b1..0109848 100644
--- a/index.html
+++ b/index.html
@@ -7,19 +7,19 @@
-
+
-
+
-
+
-
我的 [XXX ]
-
登录后管理你的角色
-
+
我的 [XXX]
+
登录后管理你的角色
+
@@ -37,18 +37,368 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
你还没有绑定专属创作者
+
通过创作者的专属链接进入即可绑定
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫码添加后,请将下方头像保存并设置为该联系人的备注头像,获得更完整的体验
+
+
+
+
+
+
+
+
+
+
+
+
没耐心?直接加微信定制沟通 →
+
+
+
什么是蒸馏前任?
+
把你们曾经的聊天记录、语音习惯、相处细节交给我们,技术团队会将其蒸馏成一个可对话的 AI 前任。ta 会记得你们的暗号、说话节奏,甚至那些只有你们懂的小情绪。
+
+
+
+
适合谁?
+
+ - 想好好告别,却还没说完话的人
+ - 希望把一段关系以安全方式封存的人
+ - 想借 AI 完成自我疗愈、练习表达的人
+
+
+
+
+
服务流程
+
+ - ① 下单付款
+ - ② 客服指导导出聊天记录
+ - ③ 上传至指定云盘
+ - ④ 技术人员处理蒸馏
+ - ⑤ 完成后获得专属二维码和头像
+
+
+
+
+ 标准版
+ ¥ 199
+ / 次
+
+
+
+
+
+
+
+
付款后请添加客服微信,我们将一对一指导你完成聊天记录导出与上传。
+
+
+
+
创作者可推广蒸馏前任服务获得分润,具体比例请在创作者管理中心配置。
+
+
+
+
+
+
+
+
+
+
+
平台简介
+
Eternal AI 是一个 AI 陪伴平台,帮助创作者将人设设定部署为可对话的 AI 角色,让用户在微信中与 ta 交流。我们致力于在记忆与陪伴中,遇见更懂你的 AI。
+
+
+
+
连接方式说明
+
Eternal AI 基于微信 Claw 开放协议(iLink),非破解、非抓包,在微信官方授权范围内实现 AI 联系人接入。你的微信账号安全不受影响。
+
+
+
+
安全类
+
+
+
不能。我们只能看到你与 AI 角色的对话,无法访问你的其他聊天、朋友圈或通讯录。
+
+
+
+
不会。我们仅通过 Claw 协议与 AI 联系人通信,不具备读取你朋友圈或通讯录的能力。
+
+
+
+
+
隐私类
+
+
+
对话内容会保存在你的专属记忆库中,用于 AI 角色记住你们的对话上下文。仅你和你的 AI 角色可访问,我们不会人工查看。
+
+
+
+
上传的聊天记录仅用于训练你的专属 AI 前任,处理完成后原始数据将被删除。我们承诺不会将你的数据用于其他用途。
+
+
+
+
+
账号类
+
+
+
不会。Claw 协议在微信官方授权范围内运行,与正常添加联系人无异,不存在封号风险。
+
+
+
+
不能。AI 角色只是一个普通的微信联系人,只能与你收发消息,无法控制你的账号。
+
+
+
+
+
情感类
+
+
+
不会有人工查看你的对话内容。除非你主动联系客服反馈问题,我们才会根据你的授权查看相关记录。
+
+
+
+
不会。你的对话内容仅用于你自己的 AI 角色的记忆,绝不会用于训练其他模型或分享给第三方。
+
+
+
+
+
+
+
+
+
+
+
+
平台能为创作者提供什么
+
+ - 部署 AI 角色:提交人设设定,平台负责技术部署
+ - 资金中转:平台处理用户付款,按比例结算给创作者
+ - 私域变现:通过专属链接绑定粉丝,持续获得订阅收入
+
+
+
+
+
合作模式
+
创作者提交角色人设,平台部署上线。用户付款后,平台抽佣 20%,80% 转给创作者。结算周期为月结。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 可提现余额
+ ¥ 0.00
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
没耐心?直接加微信定制沟通 →
-
-
-
什么是蒸馏前任?
-
把你们曾经的聊天记录、语音习惯、相处细节交给我们,技术团队会将其蒸馏成一个可对话的 AI 前任。ta 会记得你们的暗号、说话节奏,甚至那些只有你们懂的小情绪。
-
-
-
-
适合谁?
-
- - 想好好告别,却还没说完话的人
- - 希望把一段关系以安全方式封存的人
- - 想借 AI 完成自我疗愈、练习表达的人
-
-
-
-
-
服务流程
-
- - ① 下单付款
- - ② 客服指导导出聊天记录
- - ③ 上传至指定云盘
- - ④ 技术人员处理蒸馏
- - ⑤ 完成后获得专属二维码和头像
-
-
-
-
- 标准版
- ¥ 199
- / 次
-
-
-
-
-
-
-
-
付款后请添加客服微信,我们将一对一指导你完成聊天记录导出与上传。
-
-
-
-
创作者可推广蒸馏前任服务获得分润,具体比例请在创作者管理中心配置。
+
+
+
+