- 更新 docs/ 与 app-ios/docs/ - 更新 app-ios/demo、assets/demo、app-ios 代码与计划 Co-authored-by: Cursor <cursoragent@cursor.com>
1043 lines
30 KiB
HTML
1043 lines
30 KiB
HTML
<!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>岁月时书 - 聊天</title>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;500;600;700&family=Noto+Sans+SC:wght@300;400;500;600&display=swap" rel="stylesheet">
|
|
<style>
|
|
:root {
|
|
--deep-purple: #200028;
|
|
--slate-purple: #8C8EA3;
|
|
--medium-purple: #A177A6;
|
|
--lavender: #CEB0DA;
|
|
--blush: #DBBABA;
|
|
--cream: #FAF7F8;
|
|
--white: #FFFFFF;
|
|
|
|
--font-serif: 'Noto Serif SC', serif;
|
|
--font-sans: 'Noto Sans SC', sans-serif;
|
|
}
|
|
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
-webkit-tap-highlight-color: transparent;
|
|
}
|
|
|
|
body {
|
|
font-family: var(--font-sans);
|
|
background: #1a1a2e;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
min-height: 100vh;
|
|
padding: 20px;
|
|
}
|
|
|
|
/* Phone Frame */
|
|
.phone-frame {
|
|
width: 393px;
|
|
height: 852px;
|
|
background: var(--cream);
|
|
border-radius: 55px;
|
|
overflow: hidden;
|
|
position: relative;
|
|
box-shadow:
|
|
0 0 0 11px #1a1a1a,
|
|
0 0 0 13px #3a3a3a,
|
|
0 25px 50px -12px rgba(0, 0, 0, 0.5);
|
|
}
|
|
|
|
/* Dynamic Island */
|
|
.dynamic-island {
|
|
position: absolute;
|
|
top: 11px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
width: 126px;
|
|
height: 37px;
|
|
background: #000;
|
|
border-radius: 24px;
|
|
z-index: 1000;
|
|
}
|
|
|
|
/* Screen */
|
|
.screen {
|
|
width: 100%;
|
|
height: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
position: relative;
|
|
}
|
|
|
|
/* Chat Header */
|
|
.chat-header {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 54px 16px 14px;
|
|
background: var(--medium-purple);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.chat-back-btn {
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: 12px;
|
|
background: rgba(255,255,255,0.2);
|
|
border: none;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
flex-shrink: 0;
|
|
transition: background 0.2s ease;
|
|
}
|
|
|
|
.chat-back-btn:active {
|
|
background: rgba(255,255,255,0.3);
|
|
}
|
|
|
|
.chat-back-btn svg {
|
|
width: 24px;
|
|
height: 24px;
|
|
stroke: var(--white);
|
|
fill: none;
|
|
stroke-width: 2.5;
|
|
}
|
|
|
|
.chat-title-area {
|
|
flex: 1;
|
|
text-align: center;
|
|
padding-right: 40px;
|
|
}
|
|
|
|
.chat-name {
|
|
font-size: 18px;
|
|
font-weight: 600;
|
|
color: var(--white);
|
|
}
|
|
|
|
.chat-status {
|
|
font-size: 12px;
|
|
color: rgba(255,255,255,0.8);
|
|
margin-top: 2px;
|
|
}
|
|
|
|
/* Chat Messages */
|
|
.chat-messages {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
padding: 16px 14px 20px;
|
|
background: var(--cream);
|
|
-webkit-overflow-scrolling: touch;
|
|
}
|
|
|
|
.chat-messages::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
|
|
.chat-time {
|
|
text-align: center;
|
|
font-size: 12px;
|
|
color: var(--slate-purple);
|
|
margin: 16px 0;
|
|
padding: 4px 12px;
|
|
background: rgba(140, 142, 163, 0.1);
|
|
border-radius: 12px;
|
|
display: inline-block;
|
|
position: relative;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
}
|
|
|
|
.chat-message {
|
|
display: flex;
|
|
margin-bottom: 16px;
|
|
animation: messageIn 0.3s ease;
|
|
}
|
|
|
|
@keyframes messageIn {
|
|
from { opacity: 0; transform: translateY(10px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
|
|
.chat-message.ai {
|
|
flex-direction: row;
|
|
}
|
|
|
|
.chat-message.user {
|
|
flex-direction: row-reverse;
|
|
}
|
|
|
|
.chat-avatar {
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: 10px;
|
|
flex-shrink: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 18px;
|
|
}
|
|
|
|
.chat-message.ai .chat-avatar {
|
|
background: var(--lavender);
|
|
margin-right: 10px;
|
|
}
|
|
|
|
.chat-message.user .chat-avatar {
|
|
background: var(--blush);
|
|
margin-left: 10px;
|
|
}
|
|
|
|
.chat-bubble {
|
|
max-width: 70%;
|
|
padding: 12px 16px;
|
|
border-radius: 18px;
|
|
font-size: 15px;
|
|
line-height: 1.5;
|
|
position: relative;
|
|
word-break: break-word;
|
|
}
|
|
|
|
.chat-message.ai .chat-bubble {
|
|
background: var(--white);
|
|
color: var(--deep-purple);
|
|
border-top-left-radius: 4px;
|
|
box-shadow: 0 2px 8px rgba(32, 0, 40, 0.06);
|
|
}
|
|
|
|
.chat-message.user .chat-bubble {
|
|
background: var(--medium-purple);
|
|
color: var(--white);
|
|
border-top-right-radius: 4px;
|
|
box-shadow: 0 2px 8px rgba(161, 119, 166, 0.3);
|
|
}
|
|
|
|
.chat-bubble img {
|
|
max-width: 100%;
|
|
border-radius: 8px;
|
|
display: block;
|
|
}
|
|
|
|
.chat-bubble .voice-msg {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
min-width: 80px;
|
|
}
|
|
|
|
.chat-bubble .voice-msg svg {
|
|
width: 20px;
|
|
height: 20px;
|
|
}
|
|
|
|
.chat-message.ai .chat-bubble .voice-msg svg {
|
|
fill: var(--deep-purple);
|
|
}
|
|
|
|
.chat-message.user .chat-bubble .voice-msg svg {
|
|
fill: var(--white);
|
|
}
|
|
|
|
/* Typing indicator */
|
|
.typing-indicator {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
padding: 8px 0;
|
|
}
|
|
|
|
.typing-indicator span {
|
|
width: 8px;
|
|
height: 8px;
|
|
border-radius: 50%;
|
|
background: var(--slate-purple);
|
|
animation: typing 1.4s infinite;
|
|
}
|
|
|
|
.typing-indicator span:nth-child(2) { animation-delay: 0.2s; }
|
|
.typing-indicator span:nth-child(3) { animation-delay: 0.4s; }
|
|
|
|
@keyframes typing {
|
|
0%, 100% { opacity: 0.3; transform: scale(0.8); }
|
|
50% { opacity: 1; transform: scale(1); }
|
|
}
|
|
|
|
/* Chat Input Area */
|
|
.chat-input-area {
|
|
background: var(--white);
|
|
border-top: 1px solid rgba(32, 0, 40, 0.08);
|
|
padding: 10px 14px;
|
|
padding-bottom: calc(34px + 10px);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.chat-input-row {
|
|
display: flex;
|
|
align-items: flex-end;
|
|
gap: 8px;
|
|
}
|
|
|
|
.chat-input-btn {
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: 12px;
|
|
border: none;
|
|
background: transparent;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
flex-shrink: 0;
|
|
transition: background 0.2s ease;
|
|
}
|
|
|
|
.chat-input-btn svg {
|
|
width: 26px;
|
|
height: 26px;
|
|
stroke: var(--slate-purple);
|
|
fill: none;
|
|
stroke-width: 1.8;
|
|
}
|
|
|
|
.chat-input-btn:active {
|
|
background: var(--lavender);
|
|
}
|
|
|
|
.chat-text-input {
|
|
flex: 1;
|
|
min-height: 40px;
|
|
max-height: 100px;
|
|
padding: 10px 16px;
|
|
border: none;
|
|
border-radius: 20px;
|
|
background: var(--cream);
|
|
font-size: 16px;
|
|
line-height: 1.4;
|
|
resize: none;
|
|
outline: none;
|
|
font-family: var(--font-sans);
|
|
color: var(--deep-purple);
|
|
}
|
|
|
|
.chat-text-input::placeholder {
|
|
color: var(--slate-purple);
|
|
}
|
|
|
|
.chat-text-input:focus {
|
|
outline: none;
|
|
background: rgba(206, 176, 218, 0.2);
|
|
}
|
|
|
|
.chat-send-btn {
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: 12px;
|
|
border: none;
|
|
background: var(--medium-purple);
|
|
display: none;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
flex-shrink: 0;
|
|
box-shadow: 0 2px 8px rgba(161, 119, 166, 0.3);
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.chat-send-btn.visible {
|
|
display: flex;
|
|
}
|
|
|
|
.chat-send-btn:active {
|
|
transform: scale(0.95);
|
|
}
|
|
|
|
.chat-send-btn svg {
|
|
width: 20px;
|
|
height: 20px;
|
|
fill: var(--white);
|
|
}
|
|
|
|
/* Voice Input Button */
|
|
.voice-input-btn {
|
|
flex: 1;
|
|
height: 40px;
|
|
border: none;
|
|
border-radius: 20px;
|
|
background: var(--cream);
|
|
font-size: 15px;
|
|
color: var(--slate-purple);
|
|
cursor: pointer;
|
|
display: none;
|
|
align-items: center;
|
|
justify-content: center;
|
|
user-select: none;
|
|
-webkit-user-select: none;
|
|
transition: background 0.2s ease;
|
|
}
|
|
|
|
.voice-input-btn.visible {
|
|
display: flex;
|
|
}
|
|
|
|
.voice-input-btn:active {
|
|
background: var(--lavender);
|
|
}
|
|
|
|
.voice-input-btn.recording {
|
|
background: var(--lavender);
|
|
}
|
|
|
|
/* Emoji Picker */
|
|
.emoji-picker {
|
|
display: none;
|
|
background: var(--white);
|
|
border-top: 1px solid rgba(32, 0, 40, 0.08);
|
|
padding: 12px 14px;
|
|
padding-bottom: 34px;
|
|
max-height: 220px;
|
|
overflow-y: auto;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.emoji-picker.visible {
|
|
display: block;
|
|
}
|
|
|
|
.emoji-picker.visible + .chat-input-area {
|
|
padding-bottom: 10px;
|
|
}
|
|
|
|
.emoji-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(8, 1fr);
|
|
gap: 8px;
|
|
}
|
|
|
|
.emoji-item {
|
|
font-size: 24px;
|
|
text-align: center;
|
|
padding: 6px;
|
|
cursor: pointer;
|
|
border-radius: 8px;
|
|
transition: background 0.2s ease;
|
|
}
|
|
|
|
.emoji-item:active {
|
|
background: var(--lavender);
|
|
}
|
|
|
|
/* More Options Panel */
|
|
.more-options {
|
|
display: none;
|
|
background: var(--white);
|
|
border-top: 1px solid rgba(32, 0, 40, 0.08);
|
|
padding: 20px 14px;
|
|
padding-bottom: 34px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.more-options.visible {
|
|
display: block;
|
|
}
|
|
|
|
.more-options.visible + .emoji-picker + .chat-input-area,
|
|
.more-options.visible ~ .chat-input-area {
|
|
padding-bottom: 10px;
|
|
}
|
|
|
|
.more-options-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(4, 1fr);
|
|
gap: 16px;
|
|
}
|
|
|
|
.more-option-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 8px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.more-option-icon {
|
|
width: 56px;
|
|
height: 56px;
|
|
border-radius: 14px;
|
|
background: var(--cream);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
transition: background 0.2s ease;
|
|
}
|
|
|
|
.more-option-icon svg {
|
|
width: 28px;
|
|
height: 28px;
|
|
stroke: var(--medium-purple);
|
|
fill: none;
|
|
stroke-width: 1.5;
|
|
}
|
|
|
|
.more-option-label {
|
|
font-size: 12px;
|
|
color: var(--slate-purple);
|
|
}
|
|
|
|
.more-option-item:active .more-option-icon {
|
|
background: var(--lavender);
|
|
}
|
|
|
|
/* Voice Recording Overlay */
|
|
.voice-recording-overlay {
|
|
position: absolute;
|
|
inset: 0;
|
|
background: rgba(32, 0, 40, 0.7);
|
|
display: none;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 500;
|
|
border-radius: 55px;
|
|
}
|
|
|
|
.voice-recording-overlay.visible {
|
|
display: flex;
|
|
}
|
|
|
|
.voice-recording-box {
|
|
background: var(--deep-purple);
|
|
border-radius: 16px;
|
|
padding: 28px 36px;
|
|
text-align: center;
|
|
color: var(--white);
|
|
}
|
|
|
|
.voice-recording-waves {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 4px;
|
|
height: 40px;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.voice-recording-waves span {
|
|
width: 4px;
|
|
background: var(--medium-purple);
|
|
border-radius: 2px;
|
|
animation: voiceWave 0.5s ease-in-out infinite alternate;
|
|
}
|
|
|
|
.voice-recording-waves span:nth-child(1) { height: 15px; animation-delay: 0s; }
|
|
.voice-recording-waves span:nth-child(2) { height: 25px; animation-delay: 0.1s; }
|
|
.voice-recording-waves span:nth-child(3) { height: 20px; animation-delay: 0.2s; }
|
|
.voice-recording-waves span:nth-child(4) { height: 30px; animation-delay: 0.3s; }
|
|
.voice-recording-waves span:nth-child(5) { height: 20px; animation-delay: 0.4s; }
|
|
.voice-recording-waves span:nth-child(6) { height: 25px; animation-delay: 0.5s; }
|
|
.voice-recording-waves span:nth-child(7) { height: 15px; animation-delay: 0.6s; }
|
|
|
|
@keyframes voiceWave {
|
|
from { transform: scaleY(1); }
|
|
to { transform: scaleY(1.5); }
|
|
}
|
|
|
|
.voice-recording-text {
|
|
font-size: 14px;
|
|
color: var(--lavender);
|
|
}
|
|
|
|
.voice-recording-cancel {
|
|
color: #FF6B6B;
|
|
}
|
|
|
|
/* Hidden file input */
|
|
.hidden-file-input {
|
|
display: none;
|
|
}
|
|
|
|
/* Toast */
|
|
.toast {
|
|
position: absolute;
|
|
bottom: 120px;
|
|
left: 50%;
|
|
transform: translateX(-50%) translateY(100px);
|
|
background: var(--deep-purple);
|
|
color: var(--white);
|
|
padding: 12px 24px;
|
|
border-radius: 12px;
|
|
font-size: 14px;
|
|
opacity: 0;
|
|
transition: all 0.3s ease;
|
|
z-index: 200;
|
|
}
|
|
|
|
.toast.visible {
|
|
transform: translateX(-50%) translateY(0);
|
|
opacity: 1;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="phone-frame">
|
|
<div class="dynamic-island"></div>
|
|
<div class="screen">
|
|
<!-- Chat Header -->
|
|
<div class="chat-header">
|
|
<a href="chat-list.html" class="chat-back-btn">
|
|
<svg viewBox="0 0 24 24"><path d="M15 18l-6-6 6-6"/></svg>
|
|
</a>
|
|
<div class="chat-title-area">
|
|
<div class="chat-name">回忆录助手</div>
|
|
<div class="chat-status">在线</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Chat Messages -->
|
|
<div class="chat-messages" id="chatMessages">
|
|
<div class="chat-time">今天 14:30</div>
|
|
<div class="chat-message ai">
|
|
<div class="chat-avatar">📖</div>
|
|
<div class="chat-bubble">您好!我是您的回忆录助手,很高兴能陪您聊聊往事。😊<br><br>您想从哪里开始呢?可以聊聊童年、上学时光,或者任何您想分享的故事。</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- More Options Panel -->
|
|
<div class="more-options" id="moreOptions">
|
|
<div class="more-options-grid">
|
|
<div class="more-option-item" id="optionPhoto">
|
|
<div class="more-option-icon">
|
|
<svg viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>
|
|
</div>
|
|
<span class="more-option-label">照片</span>
|
|
</div>
|
|
<div class="more-option-item" id="optionCamera">
|
|
<div class="more-option-icon">
|
|
<svg viewBox="0 0 24 24"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>
|
|
</div>
|
|
<span class="more-option-label">拍摄</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Emoji Picker -->
|
|
<div class="emoji-picker" id="emojiPicker">
|
|
<div class="emoji-grid">
|
|
<span class="emoji-item">😊</span>
|
|
<span class="emoji-item">😂</span>
|
|
<span class="emoji-item">🥰</span>
|
|
<span class="emoji-item">😢</span>
|
|
<span class="emoji-item">😍</span>
|
|
<span class="emoji-item">🤔</span>
|
|
<span class="emoji-item">👍</span>
|
|
<span class="emoji-item">❤️</span>
|
|
<span class="emoji-item">😭</span>
|
|
<span class="emoji-item">😘</span>
|
|
<span class="emoji-item">🙏</span>
|
|
<span class="emoji-item">😅</span>
|
|
<span class="emoji-item">😆</span>
|
|
<span class="emoji-item">🥺</span>
|
|
<span class="emoji-item">😳</span>
|
|
<span class="emoji-item">😤</span>
|
|
<span class="emoji-item">🤗</span>
|
|
<span class="emoji-item">😎</span>
|
|
<span class="emoji-item">🥳</span>
|
|
<span class="emoji-item">😴</span>
|
|
<span class="emoji-item">🤩</span>
|
|
<span class="emoji-item">😇</span>
|
|
<span class="emoji-item">🤭</span>
|
|
<span class="emoji-item">😋</span>
|
|
<span class="emoji-item">🌸</span>
|
|
<span class="emoji-item">🌺</span>
|
|
<span class="emoji-item">🌻</span>
|
|
<span class="emoji-item">🌷</span>
|
|
<span class="emoji-item">🍀</span>
|
|
<span class="emoji-item">🌙</span>
|
|
<span class="emoji-item">⭐</span>
|
|
<span class="emoji-item">☀️</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Chat Input Area -->
|
|
<div class="chat-input-area">
|
|
<div class="chat-input-row">
|
|
<button class="chat-input-btn" id="voiceToggleBtn">
|
|
<svg viewBox="0 0 24 24" id="voiceIcon"><path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" fill="currentColor"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="8" y1="23" x2="16" y2="23"/></svg>
|
|
<svg viewBox="0 0 24 24" id="keyboardIcon" style="display:none;"><rect x="2" y="4" width="20" height="16" rx="2" ry="2"/><line x1="6" y1="8" x2="6" y2="8"/><line x1="10" y1="8" x2="10" y2="8"/><line x1="14" y1="8" x2="14" y2="8"/><line x1="18" y1="8" x2="18" y2="8"/><line x1="6" y1="12" x2="6" y2="12"/><line x1="10" y1="12" x2="10" y2="12"/><line x1="14" y1="12" x2="14" y2="12"/><line x1="18" y1="12" x2="18" y2="12"/><line x1="8" y1="16" x2="16" y2="16"/></svg>
|
|
</button>
|
|
<textarea class="chat-text-input" id="chatInput" placeholder="说点什么..." rows="1"></textarea>
|
|
<button class="voice-input-btn" id="voiceInputBtn">按住 说话</button>
|
|
<button class="chat-input-btn" id="emojiBtn">
|
|
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="M8 14s1.5 2 4 2 4-2 4-2"/><line x1="9" y1="9" x2="9.01" y2="9"/><line x1="15" y1="9" x2="15.01" y2="9"/></svg>
|
|
</button>
|
|
<button class="chat-input-btn" id="moreBtn">
|
|
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="16"/><line x1="8" y1="12" x2="16" y2="12"/></svg>
|
|
</button>
|
|
<button class="chat-send-btn" id="sendBtn">
|
|
<svg viewBox="0 0 24 24"><path d="M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z"/></svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Voice Recording Overlay -->
|
|
<div class="voice-recording-overlay" id="voiceRecordingOverlay">
|
|
<div class="voice-recording-box">
|
|
<div class="voice-recording-waves">
|
|
<span></span>
|
|
<span></span>
|
|
<span></span>
|
|
<span></span>
|
|
<span></span>
|
|
<span></span>
|
|
<span></span>
|
|
</div>
|
|
<div class="voice-recording-text" id="voiceRecordingText">松开 发送,上滑 取消</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Toast -->
|
|
<div class="toast" id="toast"></div>
|
|
</div>
|
|
<input type="file" accept="image/*" class="hidden-file-input" id="imageInput">
|
|
</div>
|
|
|
|
<script>
|
|
// State
|
|
let isVoiceMode = false;
|
|
let isVoiceRecording = false;
|
|
let voiceStartY = 0;
|
|
|
|
// Elements
|
|
const chatMessages = document.getElementById('chatMessages');
|
|
const chatInput = document.getElementById('chatInput');
|
|
const sendBtn = document.getElementById('sendBtn');
|
|
const voiceToggleBtn = document.getElementById('voiceToggleBtn');
|
|
const voiceIcon = document.getElementById('voiceIcon');
|
|
const keyboardIcon = document.getElementById('keyboardIcon');
|
|
const voiceInputBtn = document.getElementById('voiceInputBtn');
|
|
const emojiBtn = document.getElementById('emojiBtn');
|
|
const emojiPicker = document.getElementById('emojiPicker');
|
|
const moreBtn = document.getElementById('moreBtn');
|
|
const moreOptions = document.getElementById('moreOptions');
|
|
const imageInput = document.getElementById('imageInput');
|
|
const voiceRecordingOverlay = document.getElementById('voiceRecordingOverlay');
|
|
const voiceRecordingText = document.getElementById('voiceRecordingText');
|
|
const toast = document.getElementById('toast');
|
|
|
|
// AI Responses for memoir conversation
|
|
const aiResponses = [
|
|
"这个故事真让人感动!😊 能再详细说说当时的场景吗?比如那时候是什么季节?",
|
|
"听起来是一段珍贵的回忆呢!那个时候您多大年纪?身边有哪些人陪着您?",
|
|
"太有画面感了!这件事对您的人生有什么特别的影响吗?",
|
|
"我能感受到您讲述时的情感。这段经历中有没有什么特别难忘的细节?",
|
|
"真是有意思的往事!那个年代的生活和现在很不一样吧?能说说当时的日常生活是什么样的吗?",
|
|
"您的故事很生动!除了这件事,那个时期还有什么印象深刻的事情吗?",
|
|
"这段记忆很宝贵呢。您有没有保留那个时候的照片或者物件?",
|
|
"听您这么说,我仿佛也看到了当时的情景。后来这件事有什么后续吗?",
|
|
"这是您人生中很重要的一段经历呢。您觉得它塑造了现在的您吗?"
|
|
];
|
|
|
|
// Show toast
|
|
function showToast(message) {
|
|
toast.textContent = message;
|
|
toast.classList.add('visible');
|
|
setTimeout(() => {
|
|
toast.classList.remove('visible');
|
|
}, 2000);
|
|
}
|
|
|
|
// Add chat message
|
|
function addChatMessage(type, content, isImage = false) {
|
|
const msgEl = document.createElement('div');
|
|
msgEl.className = `chat-message ${type}`;
|
|
|
|
const avatar = type === 'ai' ? '📖' : '👤';
|
|
let bubbleContent = content;
|
|
|
|
if (isImage) {
|
|
bubbleContent = `<img src="${content}" alt="图片">`;
|
|
}
|
|
|
|
msgEl.innerHTML = `
|
|
<div class="chat-avatar">${avatar}</div>
|
|
<div class="chat-bubble">${bubbleContent}</div>
|
|
`;
|
|
|
|
chatMessages.appendChild(msgEl);
|
|
chatMessages.scrollTop = chatMessages.scrollHeight;
|
|
}
|
|
|
|
// Add typing indicator
|
|
function showTypingIndicator() {
|
|
const typingEl = document.createElement('div');
|
|
typingEl.className = 'chat-message ai';
|
|
typingEl.id = 'typingIndicator';
|
|
typingEl.innerHTML = `
|
|
<div class="chat-avatar">📖</div>
|
|
<div class="chat-bubble">
|
|
<div class="typing-indicator">
|
|
<span></span>
|
|
<span></span>
|
|
<span></span>
|
|
</div>
|
|
</div>
|
|
`;
|
|
chatMessages.appendChild(typingEl);
|
|
chatMessages.scrollTop = chatMessages.scrollHeight;
|
|
}
|
|
|
|
// Remove typing indicator
|
|
function removeTypingIndicator() {
|
|
const indicator = document.getElementById('typingIndicator');
|
|
if (indicator) {
|
|
indicator.remove();
|
|
}
|
|
}
|
|
|
|
// Get AI response
|
|
function getAIResponse() {
|
|
return aiResponses[Math.floor(Math.random() * aiResponses.length)];
|
|
}
|
|
|
|
// Send message
|
|
function sendMessage(text) {
|
|
if (!text.trim()) return;
|
|
|
|
// Add user message
|
|
addChatMessage('user', text);
|
|
|
|
// Clear input
|
|
chatInput.value = '';
|
|
chatInput.style.height = 'auto';
|
|
sendBtn.classList.remove('visible');
|
|
|
|
// Hide panels
|
|
emojiPicker.classList.remove('visible');
|
|
moreOptions.classList.remove('visible');
|
|
|
|
// Show typing indicator
|
|
setTimeout(() => {
|
|
showTypingIndicator();
|
|
}, 500);
|
|
|
|
// AI response after delay
|
|
setTimeout(() => {
|
|
removeTypingIndicator();
|
|
addChatMessage('ai', getAIResponse());
|
|
}, 1500 + Math.random() * 1000);
|
|
}
|
|
|
|
// Send image
|
|
function sendImage(imageUrl) {
|
|
addChatMessage('user', imageUrl, true);
|
|
|
|
// Show typing indicator
|
|
setTimeout(() => {
|
|
showTypingIndicator();
|
|
}, 500);
|
|
|
|
// AI response
|
|
setTimeout(() => {
|
|
removeTypingIndicator();
|
|
addChatMessage('ai', '这张照片很有意义!📷 能跟我说说这是什么时候拍的吗?照片里有什么特别的故事?');
|
|
}, 2000);
|
|
}
|
|
|
|
// Send voice message
|
|
function sendVoiceMessage(duration) {
|
|
const msgEl = document.createElement('div');
|
|
msgEl.className = 'chat-message user';
|
|
msgEl.innerHTML = `
|
|
<div class="chat-avatar">👤</div>
|
|
<div class="chat-bubble">
|
|
<div class="voice-msg">
|
|
<svg viewBox="0 0 24 24"><path d="M3 18v-6a9 9 0 0 1 18 0v6"/><path d="M21 19a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3v5z"/><path d="M3 19a2 2 0 0 0 2 2h1a2 2 0 0 0 2-2v-3a2 2 0 0 0-2-2H3v5z"/></svg>
|
|
<span>${duration}"</span>
|
|
</div>
|
|
</div>
|
|
`;
|
|
chatMessages.appendChild(msgEl);
|
|
chatMessages.scrollTop = chatMessages.scrollHeight;
|
|
|
|
// AI response
|
|
setTimeout(() => {
|
|
showTypingIndicator();
|
|
}, 500);
|
|
|
|
setTimeout(() => {
|
|
removeTypingIndicator();
|
|
addChatMessage('ai', getAIResponse());
|
|
}, 2000);
|
|
}
|
|
|
|
// Toggle voice mode
|
|
function toggleVoiceMode() {
|
|
isVoiceMode = !isVoiceMode;
|
|
|
|
if (isVoiceMode) {
|
|
voiceIcon.style.display = 'none';
|
|
keyboardIcon.style.display = 'block';
|
|
chatInput.style.display = 'none';
|
|
voiceInputBtn.classList.add('visible');
|
|
sendBtn.classList.remove('visible');
|
|
} else {
|
|
voiceIcon.style.display = 'block';
|
|
keyboardIcon.style.display = 'none';
|
|
chatInput.style.display = 'block';
|
|
voiceInputBtn.classList.remove('visible');
|
|
if (chatInput.value.trim()) {
|
|
sendBtn.classList.add('visible');
|
|
}
|
|
}
|
|
|
|
emojiPicker.classList.remove('visible');
|
|
moreOptions.classList.remove('visible');
|
|
}
|
|
|
|
// Voice recording handlers
|
|
function startVoiceRecording(e) {
|
|
e.preventDefault();
|
|
isVoiceRecording = true;
|
|
voiceStartY = e.touches ? e.touches[0].clientY : e.clientY;
|
|
voiceInputBtn.classList.add('recording');
|
|
voiceRecordingOverlay.classList.add('visible');
|
|
voiceRecordingText.textContent = '松开 发送,上滑 取消';
|
|
voiceRecordingText.classList.remove('voice-recording-cancel');
|
|
}
|
|
|
|
function moveVoiceRecording(e) {
|
|
if (!isVoiceRecording) return;
|
|
|
|
const currentY = e.touches ? e.touches[0].clientY : e.clientY;
|
|
const diff = voiceStartY - currentY;
|
|
|
|
if (diff > 50) {
|
|
voiceRecordingText.textContent = '松开手指,取消发送';
|
|
voiceRecordingText.classList.add('voice-recording-cancel');
|
|
} else {
|
|
voiceRecordingText.textContent = '松开 发送,上滑 取消';
|
|
voiceRecordingText.classList.remove('voice-recording-cancel');
|
|
}
|
|
}
|
|
|
|
function endVoiceRecording(e) {
|
|
if (!isVoiceRecording) return;
|
|
|
|
isVoiceRecording = false;
|
|
voiceInputBtn.classList.remove('recording');
|
|
voiceRecordingOverlay.classList.remove('visible');
|
|
|
|
const currentY = e.changedTouches ? e.changedTouches[0].clientY : e.clientY;
|
|
const diff = voiceStartY - currentY;
|
|
|
|
if (diff <= 50) {
|
|
// Send voice message (simulated duration)
|
|
const duration = Math.floor(Math.random() * 10) + 3;
|
|
sendVoiceMessage(duration);
|
|
} else {
|
|
showToast('已取消');
|
|
}
|
|
}
|
|
|
|
// Event listeners
|
|
chatInput.addEventListener('input', () => {
|
|
// Auto-resize textarea
|
|
chatInput.style.height = 'auto';
|
|
chatInput.style.height = Math.min(chatInput.scrollHeight, 100) + 'px';
|
|
|
|
// Show/hide send button
|
|
if (chatInput.value.trim()) {
|
|
sendBtn.classList.add('visible');
|
|
} else {
|
|
sendBtn.classList.remove('visible');
|
|
}
|
|
});
|
|
|
|
chatInput.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
e.preventDefault();
|
|
sendMessage(chatInput.value);
|
|
}
|
|
});
|
|
|
|
chatInput.addEventListener('focus', () => {
|
|
emojiPicker.classList.remove('visible');
|
|
moreOptions.classList.remove('visible');
|
|
});
|
|
|
|
sendBtn.addEventListener('click', () => {
|
|
sendMessage(chatInput.value);
|
|
});
|
|
|
|
voiceToggleBtn.addEventListener('click', toggleVoiceMode);
|
|
|
|
// Voice input events
|
|
voiceInputBtn.addEventListener('mousedown', startVoiceRecording);
|
|
voiceInputBtn.addEventListener('touchstart', startVoiceRecording);
|
|
document.addEventListener('mousemove', moveVoiceRecording);
|
|
document.addEventListener('touchmove', moveVoiceRecording);
|
|
document.addEventListener('mouseup', endVoiceRecording);
|
|
document.addEventListener('touchend', endVoiceRecording);
|
|
|
|
// Emoji picker
|
|
emojiBtn.addEventListener('click', () => {
|
|
emojiPicker.classList.toggle('visible');
|
|
moreOptions.classList.remove('visible');
|
|
chatInput.blur();
|
|
});
|
|
|
|
document.querySelectorAll('.emoji-item').forEach(item => {
|
|
item.addEventListener('click', () => {
|
|
chatInput.value += item.textContent;
|
|
chatInput.dispatchEvent(new Event('input'));
|
|
if (!isVoiceMode) {
|
|
sendBtn.classList.add('visible');
|
|
}
|
|
});
|
|
});
|
|
|
|
// More options
|
|
moreBtn.addEventListener('click', () => {
|
|
moreOptions.classList.toggle('visible');
|
|
emojiPicker.classList.remove('visible');
|
|
chatInput.blur();
|
|
});
|
|
|
|
// Photo option
|
|
document.getElementById('optionPhoto').addEventListener('click', () => {
|
|
imageInput.click();
|
|
});
|
|
|
|
document.getElementById('optionCamera').addEventListener('click', () => {
|
|
imageInput.click();
|
|
});
|
|
|
|
imageInput.addEventListener('change', (e) => {
|
|
const file = e.target.files[0];
|
|
if (file) {
|
|
const reader = new FileReader();
|
|
reader.onload = (e) => {
|
|
sendImage(e.target.result);
|
|
};
|
|
reader.readAsDataURL(file);
|
|
}
|
|
moreOptions.classList.remove('visible');
|
|
imageInput.value = '';
|
|
});
|
|
|
|
// Close panels when clicking messages
|
|
chatMessages.addEventListener('click', () => {
|
|
emojiPicker.classList.remove('visible');
|
|
moreOptions.classList.remove('visible');
|
|
});
|
|
|
|
// Scroll to bottom on load
|
|
chatMessages.scrollTop = chatMessages.scrollHeight;
|
|
</script>
|
|
</body>
|
|
</html>
|