Files
xianyan/docs/design-preview/chat-flow-design.html
Developer 182735df3b release: 发布v6.6.2版本,优化多项功能与体验
本次更新包含:
1.  更新应用标语与隐私政策文案,调整品牌宣传语
2.  重构Feed ID解析、HTML清理工具类,提取重复逻辑
3.  新增全屏图片查看器、通用动画操作按钮组件
4.  修复电池监听空指针、快捷操作异常捕获问题
5.  优化搜索、会话列表、RSS阅读器等页面体验
6.  完善多语言支持,新增多个翻译模块
7.  移除冗余代码,统一数字格式化逻辑
8.  调整登录页面布局与交互逻辑
2026-06-01 08:16:01 +08:00

1147 lines
61 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>闲言APP — 聊天会话流设计方案 v2</title>
<style>
:root {
--primary: #6C63FF;
--primary-light: #8B83FF;
--accent: #6C63FF;
--accent-light: #8B83FF;
--bg: #F2F2F7;
--bg-card: #FFFFFF;
--bg-secondary: #E5E5EA;
--bg-elevated: #FFFFFF;
--text: #1C1C1E;
--text-secondary: #8E8E93;
--text-hint: #AEAEB2;
--border: rgba(60,60,67,0.08);
--radius-sm: 8px;
--radius-md: 12px;
--radius-lg: 16px;
--radius-xl: 22px;
--shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
--shadow-md: 0 4px 6px -1px rgba(0,0,0,0.1);
--shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.1);
--safe-bottom: 34px;
--font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text', 'Helvetica Neue', sans-serif;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: var(--font-family);
background: #1a1a2e;
display: flex; justify-content: center; align-items: flex-start;
min-height: 100vh; padding: 20px; gap: 40px; flex-wrap: wrap;
}
.page-header { width: 100%; text-align: center; padding: 20px 0 10px; }
.page-header h1 { font-size: 28px; color: #fff; font-weight: 700; }
.page-header p { font-size: 14px; color: rgba(255,255,255,0.5); margin-top: 6px; }
.tab-bar { width: 100%; display: flex; justify-content: center; gap: 8px; margin-bottom: 10px; flex-wrap: wrap; }
.tab-btn {
padding: 8px 18px; border-radius: 20px; border: 1px solid rgba(255,255,255,0.15);
background: rgba(255,255,255,0.06); color: rgba(255,255,255,0.6); font-size: 13px;
cursor: pointer; transition: all 0.25s; font-family: var(--font-family);
}
.tab-btn:hover { background: rgba(255,255,255,0.1); }
.tab-btn.active { background: var(--primary); color: #fff; border-color: var(--primary); box-shadow: 0 2px 12px rgba(108,99,255,0.4); }
.phone-frame {
width: 390px; height: 844px; border-radius: 44px; overflow: hidden;
position: relative; background: var(--bg);
box-shadow: 0 0 0 8px #2a2a4a, 0 20px 60px rgba(0,0,0,0.5);
display: none; flex-direction: column;
}
.phone-frame.active { display: flex; flex-direction: column; }
.phone-notch {
position: absolute; top: 0; left: 50%; transform: translateX(-50%);
width: 126px; height: 34px; background: #000; border-radius: 0 0 20px 20px; z-index: 100;
}
.phone-status-bar {
height: 54px; display: flex; align-items: flex-end; justify-content: space-between;
padding: 0 28px 6px; font-size: 14px; font-weight: 600; color: var(--text);
background: transparent; z-index: 50; flex-shrink: 0;
}
.nav-bar {
height: 52px; display: flex; align-items: center; padding: 0 16px;
background: rgba(242,242,247,0.85); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
border-bottom: 0.5px solid var(--border); flex-shrink: 0; z-index: 40;
}
.nav-bar .back { font-size: 20px; color: var(--primary); cursor: pointer; padding: 4px; }
.nav-bar .title { flex: 1; text-align: center; font-size: 17px; font-weight: 600; color: var(--text); }
.nav-bar .action { font-size: 20px; color: var(--primary); cursor: pointer; padding: 4px; }
.chat-area { flex: 1; overflow-y: auto; padding: 8px 14px; -webkit-overflow-scrolling: touch; }
.chat-area::-webkit-scrollbar { display: none; }
.time-divider { text-align: center; padding: 12px 0 8px; font-size: 12px; color: var(--text-hint); }
.time-divider span { background: rgba(120,120,128,0.08); padding: 3px 10px; border-radius: 10px; font-size: 11px; }
.msg-row { display: flex; margin-bottom: 2px; gap: 8px; }
.msg-row.right { justify-content: flex-end; }
.msg-row.left { justify-content: flex-start; }
.msg-avatar {
width: 36px; height: 36px; border-radius: 10px; flex-shrink: 0;
display: flex; align-items: center; justify-content: center; font-size: 18px;
}
.msg-avatar.ai { background: linear-gradient(135deg, #6C63FF, #4ECDC4); }
.msg-avatar.user { background: rgba(108,99,255,0.12); }
.msg-avatar.imported { background: rgba(255,149,0,0.12); }
.msg-time-top { font-size: 11px; color: var(--text-hint); margin-bottom: 3px; padding: 0 4px; }
.msg-row.right .msg-time-top { text-align: right; }
.msg-row.left .msg-time-top { text-align: left; }
.msg-bubble { max-width: 72%; padding: 10px 14px; font-size: 15px; line-height: 1.55; word-break: break-word; position: relative; }
.msg-bubble.user-bubble {
background: linear-gradient(135deg, var(--primary), var(--primary-light));
color: #fff; border-radius: 18px 18px 4px 18px;
}
.msg-bubble.ai-bubble {
background: var(--bg-card); color: var(--text); border-radius: 18px 18px 18px 4px; box-shadow: var(--shadow-sm);
}
.msg-category-tag { display: inline-block; font-size: 10px; padding: 1px 6px; border-radius: 6px; margin-top: 4px; }
.msg-category-tag.love { background: rgba(240,147,251,0.15); color: #f093fb; }
.msg-category-tag.nature { background: rgba(67,233,123,0.15); color: #2bc766; }
.msg-category-tag.motivate { background: rgba(255,107,107,0.15); color: #FF6B6B; }
.msg-category-tag.hot { background: rgba(108,99,255,0.15); color: #6C63FF; }
.msg-category-tag.literature { background: rgba(102,126,234,0.15); color: #667eea; }
.msg-category-tag.movie { background: rgba(250,112,154,0.15); color: #fa709a; }
.msg-meta-outer {
font-size: 10px; color: var(--text-hint); line-height: 1.6; padding: 2px 4px 4px;
margin-bottom: 8px;
}
.msg-row.right .msg-meta-outer { text-align: right; }
.msg-row.left .msg-meta-outer { text-align: left; }
.msg-meta-outer .meta-item { display: inline-flex; align-items: center; gap: 2px; margin-right: 6px; }
.msg-meta-outer .meta-link { color: var(--primary); cursor: pointer; text-decoration: underline; }
.msg-meta-outer .meta-reserved { color: var(--text-hint); opacity: 0.5; font-style: italic; }
.msg-meta-outer .meta-read { color: #34C759; font-weight: 600; }
.category-bar {
display: flex; gap: 6px; padding: 6px 14px; overflow-x: auto;
flex-shrink: 0; -webkit-overflow-scrolling: touch; align-items: center;
}
.category-bar::-webkit-scrollbar { display: none; }
.cat-chip {
white-space: nowrap; padding: 6px 14px; border-radius: 20px; font-size: 13px;
background: var(--bg-card); color: var(--text-secondary); cursor: pointer;
border: 1px solid transparent; transition: all 0.2s; flex-shrink: 0;
}
.cat-chip.active { background: rgba(108,99,255,0.12); color: var(--primary); border-color: rgba(108,99,255,0.25); font-weight: 600; }
.cat-expand-btn {
white-space: nowrap; padding: 6px 12px; border-radius: 20px; font-size: 13px;
background: var(--bg-card); color: var(--text-secondary); cursor: pointer;
border: 1px solid var(--border); transition: all 0.2s; flex-shrink: 0;
display: flex; align-items: center; gap: 2px; font-weight: 500;
}
.cat-expand-btn.expanded { background: rgba(108,99,255,0.08); color: var(--primary); border-color: rgba(108,99,255,0.2); }
.cat-hidden { display: none; }
.cat-hidden.show { display: inline-block; }
.input-area { flex-shrink: 0; background: var(--bg-card); border-top: 0.5px solid var(--border); padding: 8px 12px 6px; }
.input-row { display: flex; align-items: flex-end; gap: 8px; }
.input-box {
flex: 1; background: var(--bg-secondary); border-radius: 20px; padding: 8px 14px;
font-size: 15px; color: var(--text); outline: none; border: none; resize: none;
min-height: 36px; max-height: 100px; line-height: 1.4; font-family: var(--font-family);
}
.input-box::placeholder { color: var(--text-hint); }
.send-btn {
width: 36px; height: 36px; border-radius: 50%; border: none;
display: flex; align-items: center; justify-content: center;
cursor: pointer; transition: all 0.2s; flex-shrink: 0;
}
.send-btn.empty { background: var(--bg-secondary); color: var(--text-hint); }
.send-btn.has-content { background: linear-gradient(135deg, var(--primary), var(--primary-light)); color: #fff; box-shadow: 0 2px 8px rgba(108,99,255,0.35); }
.bottom-actions { display: flex; gap: 4px; padding: 6px 4px 2px; justify-content: space-around; }
.bottom-action {
display: flex; flex-direction: column; align-items: center; gap: 2px;
cursor: pointer; padding: 4px 8px; border-radius: 10px; transition: all 0.2s;
border: none; background: transparent; font-family: var(--font-family);
}
.bottom-action:hover { background: rgba(108,99,255,0.06); }
.bottom-action .icon { font-size: 20px; }
.bottom-action .label { font-size: 10px; color: var(--text-secondary); }
.bottom-action.active .label { color: var(--primary); font-weight: 600; }
.draft-bar {
display: flex; align-items: center; gap: 6px; padding: 4px 12px;
background: rgba(108,99,255,0.06); border-radius: 8px; margin: 0 12px 4px;
}
.draft-bar .draft-icon { font-size: 14px; }
.draft-bar .draft-text { flex: 1; font-size: 12px; color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.draft-bar .draft-close { font-size: 14px; color: var(--text-hint); cursor: pointer; }
.safe-area-bottom { height: var(--safe-bottom); background: var(--bg-card); flex-shrink: 0; }
.session-list { flex: 1; overflow-y: auto; padding: 0 0 100px; }
.session-list::-webkit-scrollbar { display: none; }
.session-item {
display: flex; align-items: center; padding: 12px 16px; gap: 12px;
cursor: pointer; transition: background 0.15s;
}
.session-item:hover { background: rgba(120,120,128,0.04); }
.session-item:active { background: rgba(120,120,128,0.08); }
.session-avatar {
width: 50px; height: 50px; border-radius: 12px;
display: flex; align-items: center; justify-content: center;
font-size: 24px; flex-shrink: 0; position: relative;
}
.session-avatar .badge {
position: absolute; right: -4px; top: -4px; min-width: 18px; height: 18px;
background: #FF3B30; color: #fff; font-size: 11px; font-weight: 700;
border-radius: 9px; display: flex; align-items: center; justify-content: center; padding: 0 4px;
}
.session-info { flex: 1; min-width: 0; }
.session-name { font-size: 16px; font-weight: 600; color: var(--text); display: flex; align-items: center; gap: 4px; }
.session-name .pin-icon { font-size: 11px; color: var(--primary); }
.session-name .tag { font-size: 10px; padding: 1px 5px; border-radius: 4px; background: rgba(255,59,48,0.1); color: #FF3B30; font-weight: 700; }
.session-desc { font-size: 13px; color: var(--text-hint); margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.session-right { text-align: right; flex-shrink: 0; }
.session-time { font-size: 12px; color: var(--text-hint); }
.session-arrow { font-size: 12px; color: rgba(174,174,178,0.4); margin-top: 4px; }
.draft-tag {
display: inline-flex; align-items: center; gap: 3px; font-size: 10px;
padding: 1px 6px; border-radius: 4px; background: rgba(108,99,255,0.1);
color: var(--primary); margin-top: 3px;
}
.search-bar {
margin: 0 16px 8px; padding: 8px 12px; background: var(--bg-secondary);
border-radius: 10px; display: flex; align-items: center; gap: 6px;
}
.search-bar .search-icon { font-size: 14px; color: var(--text-hint); }
.search-bar .search-text { font-size: 15px; color: var(--text-hint); }
.section-header {
padding: 12px 16px 4px; font-size: 13px; font-weight: 600;
color: var(--text-secondary); display: flex; align-items: center; gap: 4px;
}
.section-header .count { font-size: 12px; color: var(--text-hint); font-weight: 400; }
.settings-scroll { flex: 1; overflow-y: auto; }
.settings-scroll::-webkit-scrollbar { display: none; }
.settings-section { margin: 16px 16px 0; background: var(--bg-card); border-radius: var(--radius-lg); overflow: hidden; }
.settings-section-title { padding: 16px 16px 6px; font-size: 13px; font-weight: 600; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.5px; }
.settings-item {
display: flex; align-items: center; padding: 12px 16px; gap: 12px;
cursor: pointer; transition: background 0.15s; border-bottom: 0.5px solid var(--border);
}
.settings-item:last-child { border-bottom: none; }
.settings-item:hover { background: rgba(120,120,128,0.04); }
.settings-item .item-icon { font-size: 20px; width: 30px; text-align: center; }
.settings-item .item-label { flex: 1; font-size: 15px; color: var(--text); }
.settings-item .item-value { font-size: 14px; color: var(--text-hint); }
.settings-item .item-arrow { font-size: 13px; color: rgba(174,174,178,0.5); }
.toggle-switch {
width: 51px; height: 31px; border-radius: 16px; background: var(--bg-secondary);
position: relative; cursor: pointer; transition: background 0.25s;
}
.toggle-switch.on { background: #34C759; }
.toggle-switch .knob {
width: 27px; height: 27px; border-radius: 50%; background: #fff;
position: absolute; top: 2px; left: 2px; transition: transform 0.25s;
box-shadow: 0 1px 3px rgba(0,0,0,0.15);
}
.toggle-switch.on .knob { transform: translateX(20px); }
.ip-modal {
display: none; position: absolute; top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0,0,0,0.4); z-index: 200; align-items: center; justify-content: center;
}
.ip-modal.show { display: flex; }
.ip-card { width: 320px; background: var(--bg-card); border-radius: var(--radius-xl); padding: 20px; box-shadow: var(--shadow-lg); }
.ip-card h3 { font-size: 17px; font-weight: 600; color: var(--text); margin-bottom: 12px; }
.ip-row { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 0.5px solid var(--border); font-size: 14px; }
.ip-row:last-child { border-bottom: none; }
.ip-row .ip-label { color: var(--text-secondary); }
.ip-row .ip-value { color: var(--text); font-weight: 500; }
.ip-close {
margin-top: 16px; width: 100%; padding: 10px; border-radius: 12px;
background: var(--bg-secondary); border: none; font-size: 16px;
color: var(--primary); font-weight: 600; cursor: pointer; font-family: var(--font-family);
}
.edit-modal {
display: none; position: absolute; top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0,0,0,0.4); z-index: 200; align-items: flex-end; justify-content: center;
}
.edit-modal.show { display: flex; }
.edit-sheet {
width: 100%; background: var(--bg-card); border-radius: 16px 16px 0 0;
padding: 16px 16px calc(16px + var(--safe-bottom));
}
.edit-sheet .handle { width: 36px; height: 4px; background: var(--text-hint); border-radius: 2px; margin: 0 auto 16px; opacity: 0.3; }
.edit-sheet h3 { font-size: 17px; font-weight: 600; color: var(--text); margin-bottom: 12px; }
.edit-textarea {
width: 100%; min-height: 80px; padding: 12px; border-radius: 12px;
background: var(--bg-secondary); border: none; font-size: 15px;
color: var(--text); resize: none; outline: none; font-family: var(--font-family);
}
.edit-actions { display: flex; gap: 10px; margin-top: 12px; }
.edit-actions button { flex: 1; padding: 12px; border-radius: 12px; font-size: 16px; font-weight: 600; cursor: pointer; border: none; font-family: var(--font-family); }
.edit-actions .cancel-btn { background: var(--bg-secondary); color: var(--primary); }
.edit-actions .save-btn { background: var(--primary); color: #fff; }
.emoji-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 8px; margin-bottom: 14px; }
.emoji-option {
width: 44px; height: 44px; border-radius: 10px; background: var(--bg-secondary);
display: flex; align-items: center; justify-content: center; font-size: 22px;
cursor: pointer; border: 2px solid transparent; transition: all 0.15s;
}
.emoji-option:hover { background: rgba(108,99,255,0.1); }
.emoji-option.selected { border-color: var(--primary); background: rgba(108,99,255,0.1); }
.add-input {
width: 100%; padding: 10px 14px; border-radius: 10px; background: var(--bg-secondary);
border: none; font-size: 15px; color: var(--text); outline: none; margin-bottom: 10px; font-family: var(--font-family);
}
.add-input::placeholder { color: var(--text-hint); }
.add-actions { display: flex; gap: 10px; }
.add-actions button { flex: 1; padding: 12px; border-radius: 12px; font-size: 16px; font-weight: 600; cursor: pointer; border: none; font-family: var(--font-family); }
.add-actions .cancel-btn { background: var(--bg-secondary); color: var(--primary); }
.add-actions .confirm-btn { background: var(--primary); color: #fff; }
.bg-preview {
width: 100%; height: 120px; border-radius: 12px;
background: linear-gradient(135deg, #667eea, #764ba2);
display: flex; align-items: center; justify-content: center;
color: rgba(255,255,255,0.7); font-size: 14px; margin-bottom: 10px;
position: relative; overflow: hidden;
}
.bg-preview::after {
content: '点击更换背景图'; position: absolute; bottom: 8px; right: 10px;
font-size: 11px; background: rgba(0,0,0,0.3); padding: 2px 8px;
border-radius: 6px; color: #fff;
}
.category-manage { display: flex; flex-wrap: wrap; gap: 6px; padding: 8px 0; }
.category-manage .cat-item {
display: flex; align-items: center; gap: 4px; padding: 6px 10px;
border-radius: 16px; background: var(--bg-secondary); font-size: 13px; color: var(--text);
cursor: pointer; transition: background 0.15s;
}
.category-manage .cat-item:hover { background: rgba(108,99,255,0.08); }
.category-manage .cat-item .cat-edit { font-size: 11px; color: var(--primary); cursor: pointer; margin-left: 2px; }
.category-manage .cat-item .cat-remove { font-size: 12px; color: var(--text-hint); cursor: pointer; }
.category-manage .cat-add {
padding: 6px 10px; border-radius: 16px; background: rgba(108,99,255,0.08);
color: var(--primary); font-size: 13px; cursor: pointer; border: 1px dashed rgba(108,99,255,0.3);
}
.cat-edit-input {
width: 60px; padding: 2px 6px; border-radius: 6px; border: 1px solid var(--primary);
background: var(--bg-card); font-size: 12px; color: var(--text); outline: none;
font-family: var(--font-family);
}
.context-menu {
display: none; position: absolute; background: var(--bg-card); border-radius: 14px;
box-shadow: 0 8px 30px rgba(0,0,0,0.2); padding: 4px 0; z-index: 150; min-width: 180px;
}
.context-menu.show { display: block; }
.context-menu-item {
padding: 10px 16px; font-size: 15px; color: var(--text); cursor: pointer;
display: flex; align-items: center; gap: 8px;
}
.context-menu-item:hover { background: rgba(120,120,128,0.08); }
.context-menu-item.danger { color: #FF3B30; }
.context-menu-divider { height: 0.5px; background: var(--border); margin: 4px 0; }
.theme-toggle {
position: fixed; bottom: 20px; right: 20px; width: 48px; height: 48px;
border-radius: 50%; background: var(--primary); color: #fff; border: none;
font-size: 22px; cursor: pointer; box-shadow: 0 4px 12px rgba(108,99,255,0.4);
z-index: 999; display: flex; align-items: center; justify-content: center;
}
.design-notes {
width: 390px; background: rgba(255,255,255,0.04); border-radius: 16px;
padding: 20px; color: rgba(255,255,255,0.8); font-size: 13px; line-height: 1.7;
max-height: 844px; overflow-y: auto;
}
.design-notes::-webkit-scrollbar { width: 4px; }
.design-notes::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.15); border-radius: 2px; }
.design-notes h2 { font-size: 18px; color: #fff; margin-bottom: 12px; }
.design-notes h3 { font-size: 15px; color: #8B83FF; margin: 16px 0 8px; }
.design-notes ul { padding-left: 16px; }
.design-notes li { margin-bottom: 4px; }
.design-notes .highlight { color: #8B83FF; font-weight: 600; }
.design-notes .tag {
display: inline-block; padding: 1px 6px; border-radius: 4px;
background: rgba(108,99,255,0.2); color: #8B83FF; font-size: 11px; margin: 0 2px;
}
body.dark-mode { --bg: #000000; --bg-card: #1C1C1E; --bg-secondary: #2C2C2E; --bg-elevated: #2C2C2E; --text: #FFFFFF; --text-secondary: #98989D; --text-hint: #636366; --border: rgba(84,84,88,0.34); }
body.dark-mode .phone-frame { background: #000; }
body.dark-mode .nav-bar { background: rgba(0,0,0,0.85); }
body.dark-mode .msg-bubble.ai-bubble { background: #1C1C1E; }
body.dark-mode .msg-avatar.user { background: rgba(108,99,255,0.2); }
</style>
</head>
<body>
<div class="page-header">
<h1>💬 聊天会话流 · 设计方案 v2</h1>
<p>闲言APP v4.21.0 · 2026-05-08 · 参考QQ对话框 · 支持动态主题</p>
</div>
<div class="tab-bar">
<button class="tab-btn active" onclick="switchView('chat')">💬 聊天页面</button>
<button class="tab-btn" onclick="switchView('inspiration')">✨ 灵感页面</button>
<button class="tab-btn" onclick="switchView('settings')">⚙️ 聊天设置</button>
<button class="tab-btn" onclick="switchView('add-session')"> 新增会话</button>
</div>
<!-- ============ 聊天页面 ============ -->
<div class="phone-frame active" id="view-chat">
<div class="phone-notch"></div>
<div class="phone-status-bar"><span>9:41</span><span>●●● ●● ◗</span></div>
<div class="nav-bar">
<span class="back" onclick="switchView('inspiration')"></span>
<span class="title">💬 会话流</span>
<span class="action" onclick="switchView('settings')">⚙️</span>
</div>
<div class="category-bar" id="categoryBar">
<span class="cat-expand-btn" id="catExpandBtn" onclick="toggleCategoryExpand()">📋 全部 ▸</span>
<span class="cat-chip active cat-hidden" onclick="selectCat(this)">🔥 热门</span>
<span class="cat-chip cat-hidden" onclick="selectCat(this)">爱情</span>
<span class="cat-chip cat-hidden" onclick="selectCat(this)">🌿 自然</span>
<span class="cat-chip cat-hidden" onclick="selectCat(this)">💪 励志</span>
<span class="cat-chip cat-hidden" onclick="selectCat(this)">📖 文学</span>
<span class="cat-chip cat-hidden" onclick="selectCat(this)">🎬 影视</span>
</div>
<div class="chat-area" id="chatArea">
<div class="time-divider"><span>今天 上午 9:30</span></div>
<div class="msg-row right" oncontextmenu="showContextMenu(event, this)">
<div>
<div class="msg-time-top">上午 9:30</div>
<div class="msg-bubble user-bubble">推荐一句励志的句子</div>
<div class="msg-meta-outer">
<span class="meta-item">📱 iPhone 17 Pro</span>
<span class="meta-item">📍 <span class="meta-link" onclick="showIpModal(event)">上海</span></span>
<span class="meta-item">🏷️ 励志</span>
<span class="meta-item">💾 本地</span>
<span class="meta-item">🕐 09:30</span>
<span class="meta-item meta-reserved">[ext_1] [ext_2]</span>
</div>
</div>
<div class="msg-avatar user">😊</div>
</div>
<div class="msg-row left" oncontextmenu="showContextMenu(event, this)">
<div class="msg-avatar ai">💪</div>
<div>
<div class="msg-time-top">上午 10:15</div>
<div class="msg-bubble ai-bubble" style="background:linear-gradient(135deg,#FF6B6B,#FFB74D);color:#fff;">
不管前方的路有多苦,只要走的方向正确,都比站在原地更接近幸福。
<div style="margin-top:6px;font-size:13px;color:rgba(255,255,255,0.75);">—— 宫崎骏</div>
<span class="msg-category-tag motivate">💪 励志</span>
</div>
<div class="msg-meta-outer">
<span class="meta-item">📱 Server</span>
<span class="meta-item">📍 <span class="meta-link" onclick="showIpModal(event)">深圳</span></span>
<span class="meta-item">🏷️ 励志</span>
<span class="meta-item">☁️ 云端</span>
<span class="meta-item">🕐 10:15</span>
<span class="meta-item meta-read">👁 已阅 3</span>
<span class="meta-item meta-reserved">[ext_1]</span>
</div>
</div>
</div>
<div class="time-divider"><span>下午 2:30</span></div>
<div class="msg-row right" oncontextmenu="showContextMenu(event, this)">
<div>
<div class="msg-time-top">下午 2:30</div>
<div class="msg-bubble user-bubble">今天天气真好,来一句关于自然的</div>
<div class="msg-meta-outer">
<span class="meta-item">📱 iPhone 17 Pro</span>
<span class="meta-item">📍 <span class="meta-link" onclick="showIpModal(event)">上海</span></span>
<span class="meta-item">🏷️ 自然</span>
<span class="meta-item">💾 本地</span>
<span class="meta-item">🕐 14:30</span>
<span class="meta-item meta-reserved">[ext_1] [ext_2]</span>
</div>
</div>
<div class="msg-avatar user">😊</div>
</div>
<div class="msg-row left" oncontextmenu="showContextMenu(event, this)">
<div class="msg-avatar ai">🌿</div>
<div>
<div class="msg-time-top">下午 2:30</div>
<div class="msg-bubble ai-bubble" style="background:linear-gradient(135deg,#43e97b,#38f9d7);color:#fff;">
采菊东篱下,悠然见南山。
<div style="margin-top:6px;font-size:13px;color:rgba(255,255,255,0.75);">—— 陶渊明</div>
<span class="msg-category-tag nature">🌿 自然</span>
</div>
<div class="msg-meta-outer">
<span class="meta-item">📱 Server</span>
<span class="meta-item">📍 <span class="meta-link" onclick="showIpModal(event)">深圳</span></span>
<span class="meta-item">🏷️ 自然</span>
<span class="meta-item">☁️ 云端</span>
<span class="meta-item">🕐 14:30</span>
<span class="meta-item meta-read">👁 已阅 1</span>
</div>
</div>
</div>
<div class="time-divider"><span>下午 5:42</span></div>
<div class="msg-row left" oncontextmenu="showContextMenu(event, this)">
<div class="msg-avatar imported">📱</div>
<div>
<div class="msg-time-top">下午 5:42 · 来自iPad</div>
<div class="msg-bubble ai-bubble">
人生如逆旅,我亦是行人。
<div style="margin-top:6px;font-size:13px;color:var(--text-secondary);">—— 苏轼《临江仙》</div>
<span class="msg-category-tag literature">📖 文学</span>
</div>
<div class="msg-meta-outer">
<span class="meta-item">📱 iPad Pro</span>
<span class="meta-item">📍 <span class="meta-link" onclick="showIpModal(event)">广州</span></span>
<span class="meta-item">🏷️ 文学</span>
<span class="meta-item">📥 导入</span>
<span class="meta-item">🕐 17:42</span>
<span class="meta-item meta-read">👁 已阅 5</span>
<span class="meta-item meta-reserved">[ext_1] [ext_2] [ext_3]</span>
</div>
</div>
</div>
<div class="msg-row right" oncontextmenu="showContextMenu(event, this)">
<div>
<div class="msg-time-top">下午 6:10</div>
<div class="msg-bubble user-bubble">收藏了!这句太美了 ✨</div>
<div class="msg-meta-outer">
<span class="meta-item">📱 iPhone 17 Pro</span>
<span class="meta-item">📍 <span class="meta-link" onclick="showIpModal(event)">上海</span></span>
<span class="meta-item">🏷️ 文学</span>
<span class="meta-item">💾 本地</span>
<span class="meta-item">🕐 18:10</span>
<span class="meta-item meta-reserved">[ext_1]</span>
</div>
</div>
<div class="msg-avatar user">😊</div>
</div>
</div>
<div class="draft-bar" id="draftBar" style="display:none;">
<span class="draft-icon">📝</span>
<span class="draft-text" id="draftText"></span>
<span class="draft-close" onclick="clearDraft()"></span>
</div>
<div class="input-area">
<div class="input-row">
<textarea class="input-box" id="inputBox" placeholder="说点什么..." rows="1" oninput="onInputChange()" onkeydown="onInputKeydown(event)"></textarea>
<button class="send-btn empty" id="sendBtn" onclick="sendMessage()">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="19" x2="12" y2="5"/><polyline points="5 12 12 5 19 12"/></svg>
</button>
</div>
<div class="bottom-actions">
<button class="bottom-action" onclick="selectBottomCat(this, '🔥')"><span class="icon">🔥</span><span class="label">热门</span></button>
<button class="bottom-action" onclick="selectBottomCat(this, '💕')"><span class="icon">💕</span><span class="label">爱情</span></button>
<button class="bottom-action" onclick="selectBottomCat(this, '🌿')"><span class="icon">🌿</span><span class="label">自然</span></button>
<button class="bottom-action" onclick="selectBottomCat(this, '💪')"><span class="icon">💪</span><span class="label">励志</span></button>
<button class="bottom-action" onclick="selectBottomCat(this, '📖')"><span class="icon">📖</span><span class="label">文学</span></button>
<button class="bottom-action" onclick="selectBottomCat(this, '🎬')"><span class="icon">🎬</span><span class="label">影视</span></button>
</div>
</div>
<div class="safe-area-bottom"></div>
<div class="ip-modal" id="ipModal" onclick="hideIpModal(event)">
<div class="ip-card" onclick="event.stopPropagation()">
<h3>🌐 IP地址查询</h3>
<div class="ip-row"><span class="ip-label">IP地址</span><span class="ip-value">223.104.xx.xx</span></div>
<div class="ip-row"><span class="ip-label">归属地</span><span class="ip-value">上海市</span></div>
<div class="ip-row"><span class="ip-label">运营商</span><span class="ip-value">中国移动</span></div>
<div class="ip-row"><span class="ip-label">经纬度</span><span class="ip-value">31.23, 121.47</span></div>
<div class="ip-row"><span class="ip-label">查询时间</span><span class="ip-value">2026-05-08 18:10</span></div>
<div class="ip-row"><span class="ip-label">缓存状态</span><span class="ip-value" style="color:#34C759;">✓ 已缓存本地</span></div>
<button class="ip-close" onclick="hideIpModal(event)">关闭</button>
</div>
</div>
<div class="edit-modal" id="editModal" onclick="hideEditModal(event)">
<div class="edit-sheet" onclick="event.stopPropagation()">
<div class="handle"></div>
<h3>✏️ 编辑消息</h3>
<textarea class="edit-textarea" id="editTextarea">推荐一句励志的句子</textarea>
<div class="edit-actions">
<button class="cancel-btn" onclick="hideEditModal(event)">取消</button>
<button class="save-btn" onclick="saveEdit()">保存</button>
</div>
</div>
</div>
<div class="context-menu" id="contextMenu">
<div class="context-menu-item" onclick="editMessage()">✏️ 编辑</div>
<div class="context-menu-item" onclick="copyMessage()">📋 复制</div>
<div class="context-menu-item" onclick="forwardMessage()">↗️ 转发</div>
<div class="context-menu-item" onclick="markAsRead()">👁 已阅 <span id="readCount" style="color:#34C759;font-weight:600;margin-left:4px;">0</span></div>
<div class="context-menu-divider"></div>
<div class="context-menu-item danger" onclick="deleteMessage()">🗑️ 删除</div>
</div>
</div>
<!-- ============ 灵感页面 ============ -->
<div class="phone-frame" id="view-inspiration">
<div class="phone-notch"></div>
<div class="phone-status-bar"><span>9:41</span><span>●●● ●● ◗</span></div>
<div style="padding:12px 16px 0;display:flex;align-items:center;">
<span style="font-size:28px;font-weight:700;color:var(--text);">✨ 灵感</span>
<span style="flex:1;"></span>
<span style="width:36px;height:36px;border-radius:10px;background:var(--bg-secondary);display:flex;align-items:center;justify-content:center;font-size:16px;">🔧</span>
</div>
<div class="search-bar"><span class="search-icon">🔍</span><span class="search-text">搜索会话</span></div>
<div class="session-list">
<div class="section-header">📌 置顶 <span class="count">1</span></div>
<div class="session-item" onclick="switchView('chat')">
<div class="session-avatar" style="background:rgba(108,99,255,0.12);">💬<div class="badge">3</div></div>
<div class="session-info">
<div class="session-name">会话流 <span class="pin-icon">📌</span></div>
<div class="session-desc">收藏了!这句太美了 ✨</div>
<div class="draft-tag">📝 草稿: 今天心情不太好,想看一些...</div>
</div>
<div class="session-right"><div class="session-time" style="color:var(--primary);font-weight:600;">18:10</div><div class="session-arrow"></div></div>
</div>
<div style="margin:4px 16px;height:0.5px;background:var(--border);"></div>
<div class="section-header">💬 对话 <span class="count">5</span></div>
<div class="session-item">
<div class="session-avatar" style="background:rgba(255,149,0,0.12);">🔍</div>
<div class="session-info"><div class="session-name">发现 <span class="tag">HOT</span></div><div class="session-desc">热门句子 · 分类浏览 · 每日推荐</div></div>
<div class="session-right"><div class="session-time">昨天</div><div class="session-arrow"></div></div>
</div>
<div class="session-item">
<div class="session-avatar" style="background:rgba(52,199,89,0.12);">👣</div>
<div class="session-info"><div class="session-name">足迹</div><div class="session-desc">浏览 · 点赞 · 收藏 · 稍后读 · 笔记</div></div>
<div class="session-right"><div class="session-time">3天前</div><div class="session-arrow"></div></div>
</div>
<div class="session-item">
<div class="session-avatar" style="background:rgba(255,59,48,0.12);">💕</div>
<div class="session-info"><div class="session-name">爱情语录</div><div class="session-desc">山有木兮木有枝,心悦君兮君不知</div><div class="draft-tag">📝 草稿: 求一句浪漫的...</div></div>
<div class="session-right"><div class="session-time">2小时前</div><div class="session-arrow"></div></div>
</div>
<div class="session-item">
<div class="session-avatar" style="background:rgba(102,126,234,0.12);">📖</div>
<div class="session-info"><div class="session-name">读书笔记</div><div class="session-desc">今天读了《人间失格》第3章...</div></div>
<div class="session-right"><div class="session-time">5小时前</div><div class="session-arrow"></div></div>
</div>
<div class="session-item">
<div class="session-avatar" style="background:rgba(250,112,154,0.12);">🎬</div>
<div class="session-info"><div class="session-name">影视台词</div><div class="session-desc">希望是件美好的事 —— 肖申克的救赎</div></div>
<div class="session-right"><div class="session-time">昨天</div><div class="session-arrow"></div></div>
</div>
<div style="text-align:center;padding:20px;font-size:12px;color:var(--text-hint);">⬆️ 下拉打开工具中心</div>
</div>
</div>
<!-- ============ 聊天设置页面 ============ -->
<div class="phone-frame" id="view-settings">
<div class="phone-notch"></div>
<div class="phone-status-bar"><span>9:41</span><span>●●● ●● ◗</span></div>
<div class="nav-bar">
<span class="back" onclick="switchView('chat')"></span>
<span class="title">⚙️ 聊天设置</span>
<span class="action"></span>
</div>
<div class="settings-scroll">
<div class="settings-section">
<div class="settings-item">
<div class="item-icon">💬</div>
<div class="item-label"><div style="font-weight:600;">会话流</div><div style="font-size:12px;color:var(--text-hint);margin-top:2px;">当前对话 · 7条消息</div></div>
<span class="item-arrow"></span>
</div>
</div>
<div class="settings-section">
<div class="settings-section-title">🎨 外观</div>
<div class="settings-item" style="flex-direction:column;align-items:stretch;">
<div style="font-size:15px;color:var(--text);margin-bottom:8px;">聊天背景</div>
<div class="bg-preview">当前背景预览</div>
<div style="display:flex;gap:8px;">
<div style="flex:1;padding:8px;border-radius:10px;background:var(--bg-secondary);text-align:center;font-size:13px;color:var(--text-secondary);cursor:pointer;">📷 相册</div>
<div style="flex:1;padding:8px;border-radius:10px;background:var(--bg-secondary);text-align:center;font-size:13px;color:var(--text-secondary);cursor:pointer;">🎨 纯色</div>
<div style="flex:1;padding:8px;border-radius:10px;background:var(--bg-secondary);text-align:center;font-size:13px;color:var(--text-secondary);cursor:pointer;">🖼️ 预设</div>
</div>
</div>
</div>
<div class="settings-section">
<div class="settings-section-title">💬 消息</div>
<div class="settings-item"><div class="item-icon">📝</div><div class="item-label">消息备注</div><span class="item-value">已开启</span><span class="item-arrow"></span></div>
<div class="settings-item"><div class="item-icon">📍</div><div class="item-label">显示地址信息</div><div class="toggle-switch on" onclick="toggleSwitch(this)"><div class="knob"></div></div></div>
<div class="settings-item"><div class="item-icon">📱</div><div class="item-label">显示设备型号</div><div class="toggle-switch on" onclick="toggleSwitch(this)"><div class="knob"></div></div></div>
<div class="settings-item"><div class="item-icon">🏷️</div><div class="item-label">显示分类标签</div><div class="toggle-switch on" onclick="toggleSwitch(this)"><div class="knob"></div></div></div>
<div class="settings-item"><div class="item-icon">🕐</div><div class="item-label">显示时间戳</div><div class="toggle-switch on" onclick="toggleSwitch(this)"><div class="knob"></div></div></div>
<div class="settings-item"><div class="item-icon">☁️</div><div class="item-label">显示本地/云端</div><div class="toggle-switch" onclick="toggleSwitch(this)"><div class="knob"></div></div></div>
<div class="settings-item"><div class="item-icon">👁</div><div class="item-label">显示已阅次数</div><div class="toggle-switch on" onclick="toggleSwitch(this)"><div class="knob"></div></div></div>
<div class="settings-item"><div class="item-icon">🔧</div><div class="item-label">显示保留扩展字段</div><div class="toggle-switch" onclick="toggleSwitch(this)"><div class="knob"></div></div></div>
</div>
<div class="settings-section">
<div class="settings-section-title">🏷️ 分类管理</div>
<div class="settings-item" style="flex-direction:column;align-items:stretch;">
<div style="font-size:12px;color:var(--text-hint);margin-bottom:8px;">点击 ✎ 编辑分类名称</div>
<div class="category-manage">
<span class="cat-item" onclick="editCatName(this)">🔥 <span class="cat-name">热门</span> <span class="cat-edit"></span> <span class="cat-remove"></span></span>
<span class="cat-item" onclick="editCatName(this)"><span class="cat-name">爱情</span> <span class="cat-edit"></span> <span class="cat-remove"></span></span>
<span class="cat-item" onclick="editCatName(this)">🌿 <span class="cat-name">自然</span> <span class="cat-edit"></span> <span class="cat-remove"></span></span>
<span class="cat-item" onclick="editCatName(this)">💪 <span class="cat-name">励志</span> <span class="cat-edit"></span> <span class="cat-remove"></span></span>
<span class="cat-item" onclick="editCatName(this)">📖 <span class="cat-name">文学</span> <span class="cat-edit"></span> <span class="cat-remove"></span></span>
<span class="cat-item" onclick="editCatName(this)">🎬 <span class="cat-name">影视</span> <span class="cat-edit"></span> <span class="cat-remove"></span></span>
<span class="cat-add">+ 添加分类</span>
</div>
</div>
</div>
<div class="settings-section">
<div class="settings-section-title">💾 数据</div>
<div class="settings-item"><div class="item-icon">📤</div><div class="item-label">导出聊天记录</div><span class="item-arrow"></span></div>
<div class="settings-item"><div class="item-icon">📥</div><div class="item-label">导入聊天记录</div><span class="item-arrow"></span></div>
<div class="settings-item"><div class="item-icon">↗️</div><div class="item-label">分享聊天记录</div><span class="item-arrow"></span></div>
<div class="settings-item"><div class="item-icon">🔄</div><div class="item-label">同步设置</div><span class="item-value">仅本地</span><span class="item-arrow"></span></div>
</div>
<div class="settings-section">
<div class="settings-section-title">📋 会话管理</div>
<div class="settings-item" onclick="switchView('add-session')"><div class="item-icon"></div><div class="item-label" style="color:var(--primary);">新增会话流</div><span class="item-arrow"></span></div>
<div class="settings-item"><div class="item-icon">📌</div><div class="item-label">置顶当前会话</div><div class="toggle-switch" onclick="toggleSwitch(this)"><div class="knob"></div></div></div>
<div class="settings-item"><div class="item-icon">🔕</div><div class="item-label">消息免打扰</div><div class="toggle-switch" onclick="toggleSwitch(this)"><div class="knob"></div></div></div>
<div class="settings-item"><div class="item-icon">🗑️</div><div class="item-label" style="color:#FF3B30;">清空聊天记录</div><span class="item-arrow"></span></div>
<div class="settings-item"><div class="item-icon">♻️</div><div class="item-label">回收站</div><span class="item-value">2条</span><span class="item-arrow"></span></div>
</div>
<div class="settings-section" style="margin-bottom:20px;">
<div class="settings-section-title">🔧 扩展字段(保留)</div>
<div class="settings-item" style="flex-direction:column;align-items:stretch;gap:6px;">
<div style="font-size:12px;color:var(--text-hint);line-height:1.6;">
以下字段仅对当前对话框生效,后续扩展到其他对话框:<br>
<span style="color:var(--primary);">session_id</span> — 会话唯一标识<br>
<span style="color:var(--primary);">bg_image</span> — 背景图路径<br>
<span style="color:var(--primary);">show_meta</span> — 元信息显示配置<br>
<span style="color:var(--primary);">categories</span> — 自定义分类列表<br>
<span style="color:var(--primary);">sync_mode</span> — 同步模式(local/cloud/both)<br>
<span style="color:var(--primary);">read_count</span> — 已阅次数<br>
<span style="color:var(--primary);">ext_1~ext_5</span> — 消息扩展字段(保留查询)<br>
<span style="color:var(--primary);">reserved_1~5</span> — 预留扩展字段
</div>
</div>
</div>
</div>
</div>
<!-- ============ 新增会话页面 ============ -->
<div class="phone-frame" id="view-add-session">
<div class="phone-notch"></div>
<div class="phone-status-bar"><span>9:41</span><span>●●● ●● ◗</span></div>
<div class="nav-bar">
<span class="back" onclick="switchView('settings')"></span>
<span class="title"> 新增会话流</span>
<span class="action"></span>
</div>
<div class="settings-scroll" style="padding:16px;">
<div style="background:var(--bg-card);border-radius:var(--radius-lg);padding:16px;margin-bottom:16px;">
<div style="font-size:15px;font-weight:600;color:var(--text);margin-bottom:12px;">选择图标</div>
<div class="emoji-grid">
<div class="emoji-option selected" onclick="selectEmoji(this)">💬</div>
<div class="emoji-option" onclick="selectEmoji(this)">💕</div>
<div class="emoji-option" onclick="selectEmoji(this)">🌿</div>
<div class="emoji-option" onclick="selectEmoji(this)">💪</div>
<div class="emoji-option" onclick="selectEmoji(this)">📖</div>
<div class="emoji-option" onclick="selectEmoji(this)">🎬</div>
<div class="emoji-option" onclick="selectEmoji(this)">🎵</div>
<div class="emoji-option" onclick="selectEmoji(this)">🎨</div>
<div class="emoji-option" onclick="selectEmoji(this)">📝</div>
<div class="emoji-option" onclick="selectEmoji(this)">🧘</div>
<div class="emoji-option" onclick="selectEmoji(this)">🌟</div>
<div class="emoji-option" onclick="selectEmoji(this)">🎯</div>
<div class="emoji-option" onclick="selectEmoji(this)">📚</div>
<div class="emoji-option" onclick="selectEmoji(this)">🏠</div>
<div class="emoji-option" onclick="selectEmoji(this)">💡</div>
<div class="emoji-option" onclick="selectEmoji(this)">🔮</div>
<div class="emoji-option" onclick="selectEmoji(this)"></div>
<div class="emoji-option" onclick="selectEmoji(this)">🌈</div>
</div>
</div>
<div style="background:var(--bg-card);border-radius:var(--radius-lg);padding:16px;margin-bottom:16px;">
<div style="font-size:15px;font-weight:600;color:var(--text);margin-bottom:10px;">会话名称</div>
<input class="add-input" placeholder="输入会话名称,如:爱情语录">
</div>
<div style="background:var(--bg-card);border-radius:var(--radius-lg);padding:16px;margin-bottom:16px;">
<div style="font-size:15px;font-weight:600;color:var(--text);margin-bottom:10px;">初始分类</div>
<div class="category-manage">
<span class="cat-item" onclick="editCatName(this)">🔥 <span class="cat-name">热门</span> <span class="cat-edit"></span> <span class="cat-remove"></span></span>
<span class="cat-item" onclick="editCatName(this)"><span class="cat-name">爱情</span> <span class="cat-edit"></span> <span class="cat-remove"></span></span>
<span class="cat-add">+ 添加</span>
</div>
</div>
<div style="background:var(--bg-card);border-radius:var(--radius-lg);padding:16px;margin-bottom:16px;">
<div style="font-size:15px;font-weight:600;color:var(--text);margin-bottom:10px;">会话描述</div>
<input class="add-input" placeholder="简短描述,如:收集浪漫爱情句子">
</div>
<div style="background:var(--bg-card);border-radius:var(--radius-lg);padding:16px;margin-bottom:24px;">
<div style="font-size:15px;font-weight:600;color:var(--text);margin-bottom:10px;">高级选项</div>
<div class="settings-item" style="padding:8px 0;"><div class="item-label" style="font-size:14px;">允许AI推送</div><div class="toggle-switch on" onclick="toggleSwitch(this)"><div class="knob"></div></div></div>
<div class="settings-item" style="padding:8px 0;border-bottom:none;"><div class="item-label" style="font-size:14px;">同步到云端</div><div class="toggle-switch" onclick="toggleSwitch(this)"><div class="knob"></div></div></div>
</div>
<div class="add-actions" style="margin:0;">
<button class="cancel-btn" onclick="switchView('settings')">取消</button>
<button class="confirm-btn" onclick="createSession()">创建会话</button>
</div>
</div>
</div>
<!-- ============ 设计说明 ============ -->
<div class="design-notes">
<h2>📋 设计说明 v2</h2>
<h3>1. 聊天页面布局</h3>
<ul>
<li>参考 <span class="highlight">QQ对话框</span> 风格,底部输入框+发送按钮</li>
<li>最底部是 <span class="highlight">分类快捷按钮</span>,点击后作为消息分类标签</li>
<li>右上角 <span class="highlight">⚙️ 设置</span> 进入聊天设置页面</li>
<li>发送按钮 <span class="tag">双色</span>:无内容=灰色,有内容=主题色渐变</li>
<li>📋 全部按钮 <span class="highlight">默认收起</span>,点击展开分类列表</li>
</ul>
<h3>2. 消息显示规则</h3>
<ul>
<li>用户消息在 <span class="highlight">右边</span>AI/导入消息在 <span class="highlight">左边</span></li>
<li>每条消息上方显示 <span class="highlight">时间</span>(上午/下午 + 时间)</li>
<li>时间间隔 >5分钟 显示 <span class="tag">时间分割线</span></li>
<li>消息下方小字 <span class="highlight">在气泡外下方</span> 显示</li>
<li>小字字段:<span class="tag">设备型号</span> <span class="tag">地址</span> <span class="tag">分类</span> <span class="tag">本地/云端</span> <span class="tag">时间</span> <span class="tag">已阅次数</span></li>
<li><span class="tag">ext_1~ext_5</span> 保留扩展字段,后续扩展开发</li>
<li>地址可点击 → <span class="highlight">IP查询弹窗</span>调API后缓存本地</li>
<li>消息 <span class="highlight">长按</span> → 弹出菜单(编辑/复制/转发/已阅/删除)</li>
<li><span class="highlight">已阅</span>:点一次已阅+1显示已阅次数</li>
</ul>
<h3>3. 导入消息识别</h3>
<ul>
<li>从其他设备导入/发送/接收的消息 → <span class="highlight">左边显示</span></li>
<li>头像显示来源设备图标(📱 iPad等</li>
<li>meta标记为 <span class="tag">📥 导入</span></li>
</ul>
<h3>4. 聊天设置</h3>
<ul>
<li><span class="highlight">公开设置</span>,仅对当前对话框生效</li>
<li>可设置:背景图、导出/导入/分享、消息备注、同步、分类管理</li>
<li>新增 <span class="tag">♻️ 回收站</span>:已删除消息可恢复</li>
<li>分类管理支持 <span class="highlight">编辑分类名称</span>(点击✎编辑)</li>
<li><span class="tag">保留字段</span>session_id, bg_image, show_meta, categories, sync_mode, read_count, ext_1~5, reserved_1~5</li>
<li>新增会话流 → 在灵感页面显示</li>
</ul>
<h3>5. 草稿功能</h3>
<ul>
<li>输入框内容未发送 → 自动保存为 <span class="highlight">草稿</span></li>
<li>灵感页面会话行显示 <span class="tag">📝 草稿</span> 标签+内容预览</li>
<li>返回聊天页时自动恢复草稿</li>
</ul>
<h3>6. 动态主题</h3>
<ul>
<li>所有颜色使用 <span class="highlight">CSS变量</span>,支持一键切换深色模式</li>
<li>消息气泡渐变色跟随分类主题</li>
<li>点击右下角 🌙 按钮切换深色模式</li>
</ul>
<h3>7. 异步处理</h3>
<ul>
<li>发送消息后,<span class="highlight">IP查询、设备信息、分类标记</span> 异步进行</li>
<li>先显示消息meta信息逐步填充</li>
<li>IP查询结果 <span class="tag">缓存本地</span>,避免重复请求</li>
</ul>
<h3>8. 需要的库</h3>
<ul>
<li><span class="tag">已有</span> dio, hive, shared_preferences, uuid, intl, device_info_plus</li>
<li><span class="tag">已有</span> flutter_slidable, share_plus, gal, archive, crypto</li>
<li><span class="tag">已有</span> connectivity_plus, path_provider, json_annotation, pull_down_button</li>
<li><span class="tag">无需新增</span> 现有库可覆盖所有功能</li>
</ul>
</div>
<button class="theme-toggle" onclick="toggleTheme()" title="切换深色模式">🌙</button>
<script>
let selectedBottomCat = null;
let isDark = false;
let isCategoryExpanded = false;
let readCounter = 0;
function switchView(view) {
document.querySelectorAll('.phone-frame').forEach(f => f.classList.remove('active'));
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
const el = document.getElementById('view-' + view);
if (el) el.classList.add('active');
const btns = document.querySelectorAll('.tab-btn');
const map = { chat: 0, inspiration: 1, settings: 2, 'add-session': 3 };
if (map[view] !== undefined && btns[map[view]]) btns[map[view]].classList.add('active');
hideContextMenu();
}
function toggleCategoryExpand() {
isCategoryExpanded = !isCategoryExpanded;
const btn = document.getElementById('catExpandBtn');
const hiddenChips = document.querySelectorAll('.cat-hidden');
if (isCategoryExpanded) {
btn.innerHTML = '📋 全部 <span style="font-size:10px;">▾</span>';
btn.classList.add('expanded');
hiddenChips.forEach(c => c.classList.add('show'));
} else {
btn.innerHTML = '📋 全部 <span style="font-size:10px;">▸</span>';
btn.classList.remove('expanded');
hiddenChips.forEach(c => c.classList.remove('show'));
}
}
function selectCat(el) {
document.querySelectorAll('.cat-chip').forEach(c => c.classList.remove('active'));
el.classList.add('active');
}
function selectBottomCat(el, emoji) {
document.querySelectorAll('.bottom-action').forEach(b => b.classList.remove('active'));
el.classList.add('active');
selectedBottomCat = emoji;
}
function onInputChange() {
const box = document.getElementById('inputBox');
const btn = document.getElementById('sendBtn');
const hasContent = box.value.trim().length > 0;
btn.className = 'send-btn ' + (hasContent ? 'has-content' : 'empty');
box.style.height = 'auto';
box.style.height = Math.min(box.scrollHeight, 100) + 'px';
const draftBar = document.getElementById('draftBar');
const draftText = document.getElementById('draftText');
if (hasContent) {
draftBar.style.display = 'flex';
draftText.textContent = box.value.trim().substring(0, 30) + (box.value.trim().length > 30 ? '...' : '');
} else {
draftBar.style.display = 'none';
}
}
function onInputKeydown(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
}
function sendMessage() {
const box = document.getElementById('inputBox');
const text = box.value.trim();
if (!text) return;
const chatArea = document.getElementById('chatArea');
const catLabel = selectedBottomCat ? selectedBottomCat + ' ' + getCatName(selectedBottomCat) : '未分类';
const now = new Date();
const timeStr = now.getHours() < 12 ? '上午' : '下午';
const h = now.getHours().toString().padStart(2, '0');
const m = now.getMinutes().toString().padStart(2, '0');
const msgHtml = `
<div class="msg-row right" oncontextmenu="showContextMenu(event, this)">
<div>
<div class="msg-time-top">${timeStr} ${h}:${m}</div>
<div class="msg-bubble user-bubble">${escapeHtml(text)}</div>
<div class="msg-meta-outer">
<span class="meta-item">📱 iPhone 17 Pro</span>
<span class="meta-item">📍 <span class="meta-link" onclick="showIpModal(event)">上海</span></span>
<span class="meta-item">🏷️ ${catLabel}</span>
<span class="meta-item">💾 本地</span>
<span class="meta-item">🕐 ${h}:${m}</span>
<span class="meta-item meta-reserved">[ext_1] [ext_2]</span>
</div>
</div>
<div class="msg-avatar user">😊</div>
</div>`;
chatArea.insertAdjacentHTML('beforeend', msgHtml);
box.value = '';
box.style.height = 'auto';
onInputChange();
chatArea.scrollTop = chatArea.scrollHeight;
setTimeout(() => {
const aiReplies = [
{ text: '生活不止眼前的苟且,还有诗和远方。', author: '高晓松', cat: '📖 文学', gradient: 'linear-gradient(135deg,#667eea,#764ba2)' },
{ text: '世界上只有一种英雄主义,就是在认清生活的真相后依然热爱生活。', author: '罗曼·罗兰', cat: '💪 励志', gradient: 'linear-gradient(135deg,#FF6B6B,#FFB74D)' },
{ text: '人生若只如初见,何事秋风悲画扇。', author: '纳兰性德', cat: '爱情', gradient: 'linear-gradient(135deg,#f093fb,#f5576c)' },
{ text: '采得百花成蜜后,为谁辛苦为谁甜。', author: '罗隐', cat: '🌿 自然', gradient: 'linear-gradient(135deg,#43e97b,#38f9d7)' },
];
const reply = aiReplies[Math.floor(Math.random() * aiReplies.length)];
const replyTime = new Date();
const rh = replyTime.getHours().toString().padStart(2, '0');
const rm = replyTime.getMinutes().toString().padStart(2, '0');
const rtStr = replyTime.getHours() < 12 ? '上午' : '下午';
const aiHtml = `
<div class="msg-row left" oncontextmenu="showContextMenu(event, this)">
<div class="msg-avatar ai">✨</div>
<div>
<div class="msg-time-top">${rtStr} ${rh}:${rm}</div>
<div class="msg-bubble ai-bubble" style="background:${reply.gradient};color:#fff;">
${reply.text}
<div style="margin-top:6px;font-size:13px;color:rgba(255,255,255,0.75);">—— ${reply.author}</div>
<span class="msg-category-tag">${reply.cat}</span>
</div>
<div class="msg-meta-outer">
<span class="meta-item">📱 Server</span>
<span class="meta-item">📍 <span class="meta-link" onclick="showIpModal(event)">深圳</span></span>
<span class="meta-item">🏷️ ${reply.cat}</span>
<span class="meta-item">☁️ 云端</span>
<span class="meta-item">🕐 ${rh}:${rm}</span>
<span class="meta-item meta-read">👁 已阅 0</span>
<span class="meta-item meta-reserved">[ext_1]</span>
</div>
</div>
</div>`;
chatArea.insertAdjacentHTML('beforeend', aiHtml);
chatArea.scrollTop = chatArea.scrollHeight;
}, 800);
}
function getCatName(emoji) {
const map = { '🔥': '热门', '💕': '爱情', '🌿': '自然', '💪': '励志', '📖': '文学', '🎬': '影视' };
return map[emoji] || '未分类';
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function showContextMenu(e, row) {
e.preventDefault();
const menu = document.getElementById('contextMenu');
const msgBubble = row.querySelector('.msg-bubble');
if (msgBubble) {
readCounter = parseInt(msgBubble.closest('.msg-row').querySelector('.meta-read')?.textContent?.replace(/[^0-9]/g, '') || '0');
document.getElementById('readCount').textContent = readCounter;
}
menu.style.left = Math.min(e.offsetX || 100, 200) + 'px';
menu.style.top = (e.offsetY || 50) + 'px';
menu.classList.add('show');
menu._targetRow = row;
}
function hideContextMenu() {
const menu = document.getElementById('contextMenu');
if (menu) menu.classList.remove('show');
}
function editMessage() {
const menu = document.getElementById('contextMenu');
const row = menu._targetRow;
if (!row) return;
const bubble = row.querySelector('.msg-bubble');
if (!bubble) return;
const editModal = document.getElementById('editModal');
const editTextarea = document.getElementById('editTextarea');
editTextarea.value = bubble.textContent.trim();
editModal.classList.add('show');
editModal._targetBubble = bubble;
hideContextMenu();
}
function copyMessage() {
const menu = document.getElementById('contextMenu');
const row = menu._targetRow;
if (!row) return;
const bubble = row.querySelector('.msg-bubble');
if (!bubble) return;
navigator.clipboard?.writeText(bubble.textContent.trim());
hideContextMenu();
}
function forwardMessage() {
hideContextMenu();
alert('↗️ 转发功能 — 选择目标会话');
}
function markAsRead() {
const menu = document.getElementById('contextMenu');
const row = menu._targetRow;
if (!row) return;
readCounter++;
document.getElementById('readCount').textContent = readCounter;
const metaRead = row.querySelector('.meta-read');
if (metaRead) {
metaRead.textContent = '👁 已阅 ' + readCounter;
} else {
const metaOuter = row.querySelector('.msg-meta-outer');
if (metaOuter) {
const span = document.createElement('span');
span.className = 'meta-item meta-read';
span.textContent = '👁 已阅 ' + readCounter;
metaOuter.insertBefore(span, metaOuter.querySelector('.meta-reserved'));
}
}
hideContextMenu();
}
function deleteMessage() {
const menu = document.getElementById('contextMenu');
const row = menu._targetRow;
if (!row) return;
row.style.transition = 'all 0.3s';
row.style.opacity = '0';
row.style.transform = 'translateX(100px)';
setTimeout(() => row.remove(), 300);
hideContextMenu();
}
function saveEdit() {
const editModal = document.getElementById('editModal');
const bubble = editModal._targetBubble;
const editTextarea = document.getElementById('editTextarea');
if (bubble && editTextarea.value.trim()) {
bubble.textContent = editTextarea.value.trim();
}
editModal.classList.remove('show');
}
function hideEditModal(e) {
if (e.target === document.getElementById('editModal') || e.target.classList.contains('cancel-btn')) {
document.getElementById('editModal').classList.remove('show');
}
}
function showIpModal(e) {
e.stopPropagation();
document.getElementById('ipModal').classList.add('show');
}
function hideIpModal(e) {
if (e.target === document.getElementById('ipModal') || e.target.classList.contains('ip-close')) {
document.getElementById('ipModal').classList.remove('show');
}
}
function toggleSwitch(el) {
el.classList.toggle('on');
}
function clearDraft() {
const box = document.getElementById('inputBox');
box.value = '';
onInputChange();
}
function toggleTheme() {
isDark = !isDark;
document.body.classList.toggle('dark-mode', isDark);
document.querySelector('.theme-toggle').textContent = isDark ? '☀️' : '🌙';
}
function editCatName(el) {
const nameEl = el.querySelector('.cat-name');
if (!nameEl) return;
if (el.querySelector('.cat-edit-input')) return;
const currentName = nameEl.textContent;
const input = document.createElement('input');
input.className = 'cat-edit-input';
input.value = currentName;
nameEl.replaceWith(input);
input.focus();
input.select();
const finish = () => {
const newName = input.value.trim() || currentName;
const newNameEl = document.createElement('span');
newNameEl.className = 'cat-name';
newNameEl.textContent = newName;
input.replaceWith(newNameEl);
};
input.addEventListener('blur', finish);
input.addEventListener('keydown', (e) => {
if (e.key === 'Enter') { e.preventDefault(); finish(); }
if (e.key === 'Escape') { input.value = currentName; finish(); }
});
}
function selectEmoji(el) {
document.querySelectorAll('.emoji-option').forEach(e => e.classList.remove('selected'));
el.classList.add('selected');
}
function createSession() {
alert('✅ 会话创建成功!将在灵感页面显示新会话');
switchView('inspiration');
}
document.addEventListener('click', (e) => {
const menu = document.getElementById('contextMenu');
if (menu && !menu.contains(e.target)) hideContextMenu();
});
</script>
</body>
</html>