- 更新 docs/ 与 app-ios/docs/ - 更新 app-ios/demo、assets/demo、app-ios 代码与计划 Co-authored-by: Cursor <cursoragent@cursor.com>
1805 lines
51 KiB
HTML
1805 lines
51 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>岁月时书 - Demo</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;
|
||
|
||
--safe-top: env(safe-area-inset-top, 47px);
|
||
--safe-bottom: env(safe-area-inset-bottom, 34px);
|
||
}
|
||
|
||
* {
|
||
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 Content */
|
||
.screen {
|
||
width: 100%;
|
||
height: 100%;
|
||
overflow: hidden;
|
||
position: relative;
|
||
}
|
||
|
||
/* Pages Container */
|
||
.pages-container {
|
||
width: 300%;
|
||
height: calc(100% - 90px);
|
||
display: flex;
|
||
transition: transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||
padding-top: 60px;
|
||
}
|
||
|
||
.page {
|
||
width: 33.333%;
|
||
height: 100%;
|
||
overflow-y: auto;
|
||
overflow-x: hidden;
|
||
-webkit-overflow-scrolling: touch;
|
||
}
|
||
|
||
.page::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
|
||
/* Navigation Bar */
|
||
.nav-bar {
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
height: 90px;
|
||
background: var(--white);
|
||
display: flex;
|
||
justify-content: space-around;
|
||
align-items: flex-start;
|
||
padding-top: 12px;
|
||
border-top: 1px solid rgba(32, 0, 40, 0.08);
|
||
z-index: 100;
|
||
}
|
||
|
||
.nav-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 4px;
|
||
cursor: pointer;
|
||
opacity: 0.5;
|
||
transition: all 0.3s ease;
|
||
padding: 4px 20px;
|
||
}
|
||
|
||
.nav-item.active {
|
||
opacity: 1;
|
||
}
|
||
|
||
.nav-item svg {
|
||
width: 26px;
|
||
height: 26px;
|
||
stroke: var(--deep-purple);
|
||
fill: none;
|
||
stroke-width: 1.8;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.nav-item.active svg {
|
||
stroke: var(--medium-purple);
|
||
}
|
||
|
||
.nav-item span {
|
||
font-size: 11px;
|
||
color: var(--deep-purple);
|
||
font-weight: 500;
|
||
letter-spacing: 0.3px;
|
||
}
|
||
|
||
.nav-item.active span {
|
||
color: var(--medium-purple);
|
||
}
|
||
|
||
/* ========== Page 1: Create Memoir ========== */
|
||
.page-create {
|
||
background: var(--cream);
|
||
padding: 0 24px 24px;
|
||
}
|
||
|
||
.header-stats {
|
||
text-align: center;
|
||
padding: 20px 0 30px;
|
||
}
|
||
|
||
.book-title-wrapper {
|
||
display: flex;
|
||
justify-content: center;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.book-title {
|
||
font-family: var(--font-serif);
|
||
font-size: 28px;
|
||
font-weight: 600;
|
||
color: var(--deep-purple);
|
||
letter-spacing: 2px;
|
||
cursor: pointer;
|
||
position: relative;
|
||
display: inline-block;
|
||
}
|
||
|
||
.book-title .edit-icon {
|
||
position: absolute;
|
||
right: -26px;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
width: 18px;
|
||
height: 18px;
|
||
stroke: var(--slate-purple);
|
||
fill: none;
|
||
opacity: 0;
|
||
transition: opacity 0.2s ease;
|
||
}
|
||
|
||
.book-title:hover .edit-icon {
|
||
opacity: 1;
|
||
}
|
||
|
||
/* Edit Title Modal */
|
||
.edit-modal {
|
||
position: fixed;
|
||
inset: 0;
|
||
background: rgba(32, 0, 40, 0.6);
|
||
display: none;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 300;
|
||
padding: 24px;
|
||
}
|
||
|
||
.edit-modal.visible {
|
||
display: flex;
|
||
}
|
||
|
||
.edit-modal-content {
|
||
background: var(--white);
|
||
border-radius: 20px;
|
||
padding: 24px;
|
||
width: 100%;
|
||
max-width: 320px;
|
||
animation: modalIn 0.3s ease;
|
||
}
|
||
|
||
@keyframes modalIn {
|
||
from { opacity: 0; transform: scale(0.95); }
|
||
to { opacity: 1; transform: scale(1); }
|
||
}
|
||
|
||
.edit-modal-title {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: var(--deep-purple);
|
||
margin-bottom: 16px;
|
||
text-align: center;
|
||
}
|
||
|
||
.edit-modal-input {
|
||
width: 100%;
|
||
padding: 14px 16px;
|
||
border: 2px solid var(--lavender);
|
||
border-radius: 12px;
|
||
font-size: 18px;
|
||
font-family: var(--font-serif);
|
||
color: var(--deep-purple);
|
||
text-align: center;
|
||
outline: none;
|
||
transition: border-color 0.2s ease;
|
||
}
|
||
|
||
.edit-modal-input:focus {
|
||
border-color: var(--medium-purple);
|
||
}
|
||
|
||
.edit-modal-hint {
|
||
font-size: 12px;
|
||
color: var(--slate-purple);
|
||
text-align: center;
|
||
margin-top: 8px;
|
||
}
|
||
|
||
.edit-modal-btns {
|
||
display: flex;
|
||
gap: 12px;
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.edit-modal-btn {
|
||
flex: 1;
|
||
padding: 12px;
|
||
border-radius: 12px;
|
||
font-size: 15px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
border: none;
|
||
}
|
||
|
||
.edit-modal-btn.cancel {
|
||
background: var(--cream);
|
||
color: var(--slate-purple);
|
||
}
|
||
|
||
.edit-modal-btn.confirm {
|
||
background: var(--medium-purple);
|
||
color: var(--white);
|
||
}
|
||
|
||
.edit-modal-btn:active {
|
||
transform: scale(0.96);
|
||
}
|
||
|
||
.stats-row {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 32px;
|
||
}
|
||
|
||
.stat-item {
|
||
text-align: center;
|
||
}
|
||
|
||
.stat-value {
|
||
font-size: 22px;
|
||
font-weight: 600;
|
||
color: var(--deep-purple);
|
||
}
|
||
|
||
.stat-label {
|
||
font-size: 12px;
|
||
color: var(--slate-purple);
|
||
margin-top: 2px;
|
||
}
|
||
|
||
/* Main Action Button */
|
||
.main-action {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
margin: 20px 0 40px;
|
||
}
|
||
|
||
.record-btn {
|
||
width: 140px;
|
||
height: 140px;
|
||
border-radius: 50%;
|
||
border: none;
|
||
background: var(--medium-purple);
|
||
color: var(--white);
|
||
cursor: pointer;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 8px;
|
||
box-shadow: 0 8px 24px rgba(32, 0, 40, 0.2);
|
||
transition: all 0.3s ease;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.record-btn:active {
|
||
transform: scale(0.95);
|
||
}
|
||
|
||
.record-btn.recording {
|
||
background: #e05555;
|
||
animation: pulse 1.5s infinite;
|
||
}
|
||
|
||
@keyframes pulse {
|
||
0%, 100% { box-shadow: 0 10px 40px rgba(224, 85, 85, 0.3), 0 0 0 8px rgba(224, 85, 85, 0.2); }
|
||
50% { box-shadow: 0 10px 40px rgba(224, 85, 85, 0.5), 0 0 0 16px rgba(224, 85, 85, 0.1); }
|
||
}
|
||
|
||
.record-btn svg {
|
||
width: 36px;
|
||
height: 36px;
|
||
fill: var(--white);
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.record-btn span {
|
||
font-size: 15px;
|
||
font-weight: 500;
|
||
letter-spacing: 1px;
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.record-hint {
|
||
margin-top: 16px;
|
||
font-size: 13px;
|
||
color: var(--slate-purple);
|
||
}
|
||
|
||
/* Timer Display */
|
||
.timer-display {
|
||
display: none;
|
||
margin-top: 16px;
|
||
font-size: 32px;
|
||
font-weight: 300;
|
||
color: var(--deep-purple);
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
|
||
.timer-display.visible {
|
||
display: block;
|
||
}
|
||
|
||
/* Progress Section */
|
||
.progress-section {
|
||
background: var(--white);
|
||
border-radius: 20px;
|
||
padding: 20px;
|
||
margin-bottom: 16px;
|
||
box-shadow: 0 2px 12px rgba(32, 0, 40, 0.06);
|
||
}
|
||
|
||
.progress-title {
|
||
font-size: 13px;
|
||
color: var(--slate-purple);
|
||
margin-bottom: 12px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.progress-items {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
}
|
||
|
||
.progress-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.progress-item-label {
|
||
font-size: 14px;
|
||
color: var(--deep-purple);
|
||
}
|
||
|
||
.progress-item-value {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: var(--medium-purple);
|
||
}
|
||
|
||
/* Session Summary */
|
||
.session-summary {
|
||
background: var(--white);
|
||
border-radius: 20px;
|
||
padding: 20px;
|
||
margin-bottom: 16px;
|
||
box-shadow: 0 2px 12px rgba(32, 0, 40, 0.06);
|
||
display: none;
|
||
}
|
||
|
||
.session-summary.visible {
|
||
display: block;
|
||
animation: slideUp 0.4s ease;
|
||
}
|
||
|
||
@keyframes slideUp {
|
||
from { opacity: 0; transform: translateY(20px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
|
||
.summary-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.summary-header svg {
|
||
width: 20px;
|
||
height: 20px;
|
||
stroke: var(--medium-purple);
|
||
fill: none;
|
||
}
|
||
|
||
.summary-header span {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: var(--deep-purple);
|
||
}
|
||
|
||
.summary-content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 10px;
|
||
}
|
||
|
||
.summary-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.summary-row .label {
|
||
color: var(--slate-purple);
|
||
}
|
||
|
||
.summary-row .value {
|
||
color: var(--deep-purple);
|
||
font-weight: 500;
|
||
}
|
||
|
||
.next-topic {
|
||
margin-top: 12px;
|
||
padding: 12px;
|
||
background: var(--lavender);
|
||
border-radius: 12px;
|
||
}
|
||
|
||
.next-topic-label {
|
||
font-size: 11px;
|
||
color: var(--slate-purple);
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.next-topic-value {
|
||
font-size: 14px;
|
||
color: var(--deep-purple);
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* Go to Book Button */
|
||
.goto-book {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 8px;
|
||
padding: 16px;
|
||
background: var(--white);
|
||
border-radius: 16px;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
box-shadow: 0 2px 12px rgba(32, 0, 40, 0.06);
|
||
}
|
||
|
||
.goto-book:active {
|
||
transform: scale(0.98);
|
||
background: var(--cream);
|
||
}
|
||
|
||
.goto-book svg {
|
||
width: 22px;
|
||
height: 22px;
|
||
stroke: var(--medium-purple);
|
||
fill: none;
|
||
}
|
||
|
||
.goto-book span {
|
||
font-size: 15px;
|
||
color: var(--deep-purple);
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* ========== Page 2: My Memoir ========== */
|
||
.page-memoir {
|
||
background: var(--cream);
|
||
padding: 0;
|
||
}
|
||
|
||
/* Book View Tabs */
|
||
.memoir-tabs {
|
||
display: flex;
|
||
padding: 0 20px;
|
||
gap: 20px;
|
||
border-bottom: 1px solid rgba(32, 0, 40, 0.08);
|
||
background: var(--white);
|
||
}
|
||
|
||
.memoir-tab {
|
||
padding: 16px 0;
|
||
font-size: 15px;
|
||
color: var(--slate-purple);
|
||
cursor: pointer;
|
||
border-bottom: 2px solid transparent;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.memoir-tab.active {
|
||
color: var(--deep-purple);
|
||
border-bottom-color: var(--medium-purple);
|
||
}
|
||
|
||
/* Table of Contents */
|
||
.toc-view {
|
||
padding: 24px 20px;
|
||
}
|
||
|
||
.toc-header {
|
||
text-align: center;
|
||
margin-bottom: 32px;
|
||
}
|
||
|
||
.toc-book-title {
|
||
font-family: var(--font-serif);
|
||
font-size: 32px;
|
||
font-weight: 600;
|
||
color: var(--deep-purple);
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.toc-subtitle {
|
||
font-size: 14px;
|
||
color: var(--slate-purple);
|
||
}
|
||
|
||
.toc-update {
|
||
margin-top: 8px;
|
||
font-size: 12px;
|
||
color: var(--medium-purple);
|
||
}
|
||
|
||
.toc-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
}
|
||
|
||
.toc-item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 16px;
|
||
background: var(--white);
|
||
border-radius: 14px;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
box-shadow: 0 2px 8px rgba(32, 0, 40, 0.04);
|
||
}
|
||
|
||
.toc-item:active {
|
||
transform: scale(0.98);
|
||
background: var(--lavender);
|
||
}
|
||
|
||
.toc-number {
|
||
width: 32px;
|
||
height: 32px;
|
||
border-radius: 10px;
|
||
background: var(--lavender);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: var(--deep-purple);
|
||
margin-right: 14px;
|
||
}
|
||
|
||
.toc-info {
|
||
flex: 1;
|
||
}
|
||
|
||
.toc-chapter-title {
|
||
font-size: 15px;
|
||
color: var(--deep-purple);
|
||
font-weight: 500;
|
||
margin-bottom: 2px;
|
||
}
|
||
|
||
.toc-status {
|
||
font-size: 12px;
|
||
color: var(--slate-purple);
|
||
}
|
||
|
||
.toc-status.complete {
|
||
color: var(--medium-purple);
|
||
}
|
||
|
||
.toc-arrow {
|
||
width: 20px;
|
||
height: 20px;
|
||
stroke: var(--slate-purple);
|
||
fill: none;
|
||
}
|
||
|
||
/* Chapter Reading View */
|
||
.reading-view {
|
||
display: none;
|
||
padding: 24px 20px;
|
||
}
|
||
|
||
.reading-view.active {
|
||
display: block;
|
||
}
|
||
|
||
.reading-back {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
margin-bottom: 24px;
|
||
cursor: pointer;
|
||
color: var(--slate-purple);
|
||
font-size: 14px;
|
||
}
|
||
|
||
.reading-back svg {
|
||
width: 18px;
|
||
height: 18px;
|
||
stroke: currentColor;
|
||
fill: none;
|
||
}
|
||
|
||
.chapter-header {
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.chapter-number {
|
||
font-size: 13px;
|
||
color: var(--medium-purple);
|
||
font-weight: 500;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.chapter-title {
|
||
font-family: var(--font-serif);
|
||
font-size: 26px;
|
||
font-weight: 600;
|
||
color: var(--deep-purple);
|
||
}
|
||
|
||
.chapter-content {
|
||
font-family: var(--font-serif);
|
||
font-size: 16px;
|
||
line-height: 1.9;
|
||
color: var(--deep-purple);
|
||
}
|
||
|
||
.chapter-content p {
|
||
text-indent: 2em;
|
||
margin-bottom: 1.2em;
|
||
}
|
||
|
||
.chapter-image {
|
||
margin: 24px -20px;
|
||
padding: 0 20px;
|
||
}
|
||
|
||
.chapter-image img {
|
||
width: 100%;
|
||
height: 180px;
|
||
object-fit: cover;
|
||
border-radius: 12px;
|
||
background: var(--lavender);
|
||
}
|
||
|
||
.chapter-image-caption {
|
||
text-align: center;
|
||
font-size: 12px;
|
||
color: var(--slate-purple);
|
||
margin-top: 8px;
|
||
font-style: italic;
|
||
}
|
||
|
||
.chapter-quote {
|
||
margin: 24px 0;
|
||
padding: 16px 20px;
|
||
background: var(--lavender);
|
||
border-left: 3px solid var(--medium-purple);
|
||
border-radius: 0 12px 12px 0;
|
||
font-size: 15px;
|
||
font-style: italic;
|
||
color: var(--deep-purple);
|
||
}
|
||
|
||
/* Export Actions */
|
||
.memoir-actions {
|
||
position: fixed;
|
||
bottom: 100px;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
display: flex;
|
||
gap: 12px;
|
||
padding: 8px;
|
||
background: var(--white);
|
||
border-radius: 16px;
|
||
box-shadow: 0 4px 20px rgba(32, 0, 40, 0.15);
|
||
z-index: 50;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.memoir-actions.visible {
|
||
opacity: 1;
|
||
pointer-events: auto;
|
||
}
|
||
|
||
.action-btn {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
padding: 10px 16px;
|
||
border-radius: 10px;
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.action-btn svg {
|
||
width: 18px;
|
||
height: 18px;
|
||
}
|
||
|
||
.action-btn.primary {
|
||
background: var(--medium-purple);
|
||
color: var(--white);
|
||
}
|
||
|
||
.action-btn.primary svg {
|
||
stroke: var(--white);
|
||
fill: none;
|
||
}
|
||
|
||
.action-btn.secondary {
|
||
background: var(--cream);
|
||
color: var(--deep-purple);
|
||
}
|
||
|
||
.action-btn.secondary svg {
|
||
stroke: var(--deep-purple);
|
||
fill: none;
|
||
}
|
||
|
||
/* ========== Page 3: Profile ========== */
|
||
.page-profile {
|
||
background: var(--cream);
|
||
padding: 0 20px 24px;
|
||
}
|
||
|
||
.profile-header {
|
||
padding: 20px 0 24px;
|
||
text-align: center;
|
||
}
|
||
|
||
.profile-avatar {
|
||
width: 72px;
|
||
height: 72px;
|
||
border-radius: 50%;
|
||
background: var(--lavender);
|
||
margin: 0 auto 12px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.profile-avatar svg {
|
||
width: 36px;
|
||
height: 36px;
|
||
stroke: var(--deep-purple);
|
||
fill: none;
|
||
}
|
||
|
||
.profile-name {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: var(--deep-purple);
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.profile-plan {
|
||
font-size: 13px;
|
||
color: var(--medium-purple);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 4px;
|
||
}
|
||
|
||
.profile-plan-dot {
|
||
width: 6px;
|
||
height: 6px;
|
||
border-radius: 50%;
|
||
background: var(--medium-purple);
|
||
}
|
||
|
||
/* Profile Sections */
|
||
.profile-section {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 12px;
|
||
color: var(--slate-purple);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.5px;
|
||
padding: 0 4px;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.section-card {
|
||
background: var(--white);
|
||
border-radius: 16px;
|
||
overflow: hidden;
|
||
box-shadow: 0 2px 8px rgba(32, 0, 40, 0.04);
|
||
}
|
||
|
||
.section-item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 16px;
|
||
cursor: pointer;
|
||
transition: background 0.2s ease;
|
||
border-bottom: 1px solid rgba(32, 0, 40, 0.06);
|
||
}
|
||
|
||
.section-item:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.section-item:active {
|
||
background: var(--cream);
|
||
}
|
||
|
||
.section-item-icon {
|
||
width: 36px;
|
||
height: 36px;
|
||
border-radius: 10px;
|
||
background: var(--lavender);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-right: 12px;
|
||
}
|
||
|
||
.section-item-icon svg {
|
||
width: 20px;
|
||
height: 20px;
|
||
stroke: var(--deep-purple);
|
||
fill: none;
|
||
}
|
||
|
||
.section-item-content {
|
||
flex: 1;
|
||
}
|
||
|
||
.section-item-label {
|
||
font-size: 15px;
|
||
color: var(--deep-purple);
|
||
}
|
||
|
||
.section-item-desc {
|
||
font-size: 12px;
|
||
color: var(--slate-purple);
|
||
margin-top: 2px;
|
||
}
|
||
|
||
.section-item-arrow {
|
||
width: 18px;
|
||
height: 18px;
|
||
stroke: var(--slate-purple);
|
||
fill: none;
|
||
}
|
||
|
||
.section-item-toggle {
|
||
width: 48px;
|
||
height: 28px;
|
||
border-radius: 14px;
|
||
background: var(--slate-purple);
|
||
position: relative;
|
||
transition: background 0.3s ease;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.section-item-toggle.active {
|
||
background: var(--medium-purple);
|
||
}
|
||
|
||
.section-item-toggle::after {
|
||
content: '';
|
||
position: absolute;
|
||
top: 3px;
|
||
left: 3px;
|
||
width: 22px;
|
||
height: 22px;
|
||
border-radius: 50%;
|
||
background: var(--white);
|
||
transition: transform 0.3s ease;
|
||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||
}
|
||
|
||
.section-item-toggle.active::after {
|
||
transform: translateX(20px);
|
||
}
|
||
|
||
/* Toast */
|
||
.toast {
|
||
position: fixed;
|
||
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;
|
||
}
|
||
|
||
/* Real-time Transcript */
|
||
.transcript-overlay {
|
||
position: absolute;
|
||
inset: 0;
|
||
background: rgba(32, 0, 40, 0.95);
|
||
z-index: 60;
|
||
display: none;
|
||
flex-direction: column;
|
||
padding: 80px 24px 120px;
|
||
}
|
||
|
||
.transcript-overlay.visible {
|
||
display: flex;
|
||
}
|
||
|
||
.transcript-close {
|
||
position: absolute;
|
||
top: 70px;
|
||
right: 20px;
|
||
width: 36px;
|
||
height: 36px;
|
||
border-radius: 50%;
|
||
background: rgba(255,255,255,0.1);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.transcript-close svg {
|
||
width: 20px;
|
||
height: 20px;
|
||
stroke: var(--white);
|
||
fill: none;
|
||
}
|
||
|
||
.transcript-header {
|
||
text-align: center;
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
.transcript-timer {
|
||
font-size: 48px;
|
||
font-weight: 300;
|
||
color: var(--white);
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
|
||
.transcript-status {
|
||
font-size: 14px;
|
||
color: var(--lavender);
|
||
margin-top: 8px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.recording-dot {
|
||
width: 8px;
|
||
height: 8px;
|
||
border-radius: 50%;
|
||
background: #e05555;
|
||
animation: blink 1s infinite;
|
||
}
|
||
|
||
@keyframes blink {
|
||
0%, 100% { opacity: 1; }
|
||
50% { opacity: 0.3; }
|
||
}
|
||
|
||
.transcript-content {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
-webkit-overflow-scrolling: touch;
|
||
}
|
||
|
||
.transcript-content::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
|
||
.transcript-message {
|
||
margin-bottom: 16px;
|
||
animation: fadeIn 0.3s ease;
|
||
}
|
||
|
||
@keyframes fadeIn {
|
||
from { opacity: 0; transform: translateY(10px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
|
||
.transcript-message.user {
|
||
text-align: right;
|
||
}
|
||
|
||
.transcript-message.ai {
|
||
text-align: left;
|
||
}
|
||
|
||
.transcript-bubble {
|
||
display: inline-block;
|
||
max-width: 85%;
|
||
padding: 12px 16px;
|
||
border-radius: 16px;
|
||
font-size: 15px;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.transcript-message.user .transcript-bubble {
|
||
background: var(--medium-purple);
|
||
color: var(--white);
|
||
border-bottom-right-radius: 4px;
|
||
}
|
||
|
||
.transcript-message.ai .transcript-bubble {
|
||
background: rgba(255,255,255,0.1);
|
||
color: var(--white);
|
||
border-bottom-left-radius: 4px;
|
||
}
|
||
|
||
.transcript-end-btn {
|
||
position: absolute;
|
||
bottom: 40px;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
padding: 16px 48px;
|
||
background: #e05555;
|
||
color: var(--white);
|
||
border: none;
|
||
border-radius: 30px;
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.transcript-end-btn svg {
|
||
width: 20px;
|
||
height: 20px;
|
||
fill: var(--white);
|
||
}
|
||
|
||
/* Waveform Animation */
|
||
.waveform {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 3px;
|
||
height: 30px;
|
||
margin-top: 16px;
|
||
}
|
||
|
||
.waveform-bar {
|
||
width: 4px;
|
||
background: var(--lavender);
|
||
border-radius: 2px;
|
||
animation: wave 0.8s ease-in-out infinite;
|
||
}
|
||
|
||
.waveform-bar:nth-child(1) { animation-delay: 0s; height: 10px; }
|
||
.waveform-bar:nth-child(2) { animation-delay: 0.1s; height: 20px; }
|
||
.waveform-bar:nth-child(3) { animation-delay: 0.2s; height: 15px; }
|
||
.waveform-bar:nth-child(4) { animation-delay: 0.3s; height: 25px; }
|
||
.waveform-bar:nth-child(5) { animation-delay: 0.4s; height: 18px; }
|
||
.waveform-bar:nth-child(6) { animation-delay: 0.5s; height: 22px; }
|
||
.waveform-bar:nth-child(7) { animation-delay: 0.6s; height: 12px; }
|
||
|
||
@keyframes wave {
|
||
0%, 100% { transform: scaleY(1); }
|
||
50% { transform: scaleY(1.8); }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="phone-frame">
|
||
<div class="dynamic-island"></div>
|
||
<div class="screen">
|
||
<div class="pages-container" id="pagesContainer">
|
||
<!-- Page 1: Create Memoir -->
|
||
<div class="page page-create">
|
||
<div class="header-stats">
|
||
<div class="book-title-wrapper">
|
||
<h1 class="book-title" id="bookTitle">
|
||
<span id="bookTitleText">《这一生》</span>
|
||
<svg class="edit-icon" viewBox="0 0 24 24"><path d="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z" stroke-width="2"/></svg>
|
||
</h1>
|
||
</div>
|
||
<div class="stats-row">
|
||
<div class="stat-item">
|
||
<div class="stat-value" id="todayMinutes">0</div>
|
||
<div class="stat-label">今日已聊(分钟)</div>
|
||
</div>
|
||
<div class="stat-item">
|
||
<div class="stat-value" id="totalHours">2:18</div>
|
||
<div class="stat-label">累计时长</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="main-action">
|
||
<button class="record-btn" id="recordBtn">
|
||
<svg viewBox="0 0 24 24" id="micIcon">
|
||
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/>
|
||
<path d="M19 10v2a7 7 0 0 1-14 0v-2M12 19v4M8 23h8" stroke="currentColor" stroke-width="2" fill="none"/>
|
||
</svg>
|
||
<svg viewBox="0 0 24 24" id="stopIcon" style="display:none;">
|
||
<rect x="6" y="6" width="12" height="12" rx="2"/>
|
||
</svg>
|
||
<span id="recordBtnText">开始聊天</span>
|
||
</button>
|
||
<p class="record-hint" id="recordHint">你可以一直说,我会认真听</p>
|
||
<div class="timer-display" id="timerDisplay">00:00</div>
|
||
</div>
|
||
|
||
<div class="progress-section">
|
||
<div class="progress-title">回忆录进度</div>
|
||
<div class="progress-items">
|
||
<div class="progress-item">
|
||
<span class="progress-item-label">预估页数</span>
|
||
<span class="progress-item-value">约 18 页</span>
|
||
</div>
|
||
<div class="progress-item">
|
||
<span class="progress-item-label">已整理章节</span>
|
||
<span class="progress-item-value">3 章</span>
|
||
</div>
|
||
<div class="progress-item">
|
||
<span class="progress-item-label">最近更新</span>
|
||
<span class="progress-item-value">刚刚</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="session-summary" id="sessionSummary">
|
||
<div class="summary-header">
|
||
<svg viewBox="0 0 24 24"><path d="M9 11l3 3L22 4M21 12v7a2 2 0 01-2 2H5a2 2 0 01-2-2V5a2 2 0 012-2h11" stroke-width="2"/></svg>
|
||
<span>本次聊天已保存</span>
|
||
</div>
|
||
<div class="summary-content">
|
||
<div class="summary-row">
|
||
<span class="label">聊天时长</span>
|
||
<span class="value" id="summaryDuration">8分42秒</span>
|
||
</div>
|
||
<div class="summary-row">
|
||
<span class="label">新增内容</span>
|
||
<span class="value">童年与家庭(2段)</span>
|
||
</div>
|
||
</div>
|
||
<div class="next-topic">
|
||
<div class="next-topic-label">推荐下次聊</div>
|
||
<div class="next-topic-value">上学的日子</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="goto-book" id="gotoBook">
|
||
<svg viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 016.5 17H20M4 19.5A2.5 2.5 0 004 14.5V5a2 2 0 012-2h14v16.5" stroke-width="2"/></svg>
|
||
<span>阅读我的回忆录</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Page 2: My Memoir -->
|
||
<div class="page page-memoir">
|
||
<div class="memoir-tabs">
|
||
<div class="memoir-tab active" data-tab="toc">目录</div>
|
||
<div class="memoir-tab" data-tab="reading">正在阅读</div>
|
||
</div>
|
||
|
||
<div class="toc-view" id="tocView">
|
||
<div class="toc-header">
|
||
<h1 class="toc-book-title">这一生</h1>
|
||
<p class="toc-subtitle">我的回忆录</p>
|
||
<p class="toc-update">更新于 2 分钟前</p>
|
||
</div>
|
||
<div class="toc-list">
|
||
<div class="toc-item" data-chapter="1">
|
||
<div class="toc-number">01</div>
|
||
<div class="toc-info">
|
||
<div class="toc-chapter-title">童年与家庭</div>
|
||
<div class="toc-status complete">已整理 · 约3页</div>
|
||
</div>
|
||
<svg class="toc-arrow" viewBox="0 0 24 24"><path d="M9 18l6-6-6-6" stroke-width="2"/></svg>
|
||
</div>
|
||
<div class="toc-item" data-chapter="2">
|
||
<div class="toc-number">02</div>
|
||
<div class="toc-info">
|
||
<div class="toc-chapter-title">上学的日子</div>
|
||
<div class="toc-status">部分整理 · 约2页</div>
|
||
</div>
|
||
<svg class="toc-arrow" viewBox="0 0 24 24"><path d="M9 18l6-6-6-6" stroke-width="2"/></svg>
|
||
</div>
|
||
<div class="toc-item" data-chapter="3">
|
||
<div class="toc-number">03</div>
|
||
<div class="toc-info">
|
||
<div class="toc-chapter-title">工作与事业</div>
|
||
<div class="toc-status">待补充</div>
|
||
</div>
|
||
<svg class="toc-arrow" viewBox="0 0 24 24"><path d="M9 18l6-6-6-6" stroke-width="2"/></svg>
|
||
</div>
|
||
<div class="toc-item" data-chapter="4">
|
||
<div class="toc-number">04</div>
|
||
<div class="toc-info">
|
||
<div class="toc-chapter-title">爱情与婚姻</div>
|
||
<div class="toc-status">待补充</div>
|
||
</div>
|
||
<svg class="toc-arrow" viewBox="0 0 24 24"><path d="M9 18l6-6-6-6" stroke-width="2"/></svg>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="reading-view" id="readingView">
|
||
<div class="reading-back" id="backToToc">
|
||
<svg viewBox="0 0 24 24"><path d="M15 18l-6-6 6-6" stroke-width="2"/></svg>
|
||
<span>返回目录</span>
|
||
</div>
|
||
<div class="chapter-header">
|
||
<div class="chapter-number">第一章</div>
|
||
<h2 class="chapter-title">童年与家庭</h2>
|
||
</div>
|
||
<div class="chapter-content">
|
||
<p>我出生在一个普通的农村家庭,那是1955年的深秋。母亲常说,我出生的那天,院子里的老槐树落了一地金黄的叶子,风一吹,像是漫天飞舞的蝴蝶。</p>
|
||
|
||
<div class="chapter-quote">
|
||
"日子虽然清苦,但那时候的快乐是最纯粹的。"
|
||
</div>
|
||
|
||
<p>父亲是村里的木匠,手艺在十里八乡都有名气。他的手上满是老茧,却能用那双手雕出最精美的花纹。每到农闲时节,总有人家请他去做嫁妆,一张八仙桌,一对太师椅,都是他的拿手活计。</p>
|
||
|
||
<div class="chapter-image">
|
||
<div style="width:100%;height:180px;background:var(--lavender);border-radius:12px;display:flex;align-items:center;justify-content:center;color:var(--deep-purple);font-size:14px;">老家门口的那条路</div>
|
||
<div class="chapter-image-caption">图:老家门口的那条路(AI 生成)</div>
|
||
</div>
|
||
|
||
<p>母亲是个勤快人,总是天不亮就起床,喂鸡、扫院子、生火做饭,一刻也闲不住。她不识字,但会唱很多老歌,夜里纳鞋底的时候,常常哼着小调,那旋律到现在我还记得。</p>
|
||
|
||
<p>我们家有三个孩子,我排行老二。大姐比我大三岁,小弟比我小五岁。那时候日子虽然清苦,但一家人在一起,总有说不完的话,笑不完的乐子。</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Page 3: Profile -->
|
||
<div class="page page-profile">
|
||
<div class="profile-header">
|
||
<div class="profile-avatar">
|
||
<svg viewBox="0 0 24 24"><path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2M12 11a4 4 0 100-8 4 4 0 000 8z" stroke-width="2"/></svg>
|
||
</div>
|
||
<div class="profile-name">李明华</div>
|
||
<div class="profile-plan">
|
||
<span class="profile-plan-dot"></span>
|
||
<span>免费体验版</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="profile-section">
|
||
<div class="section-title">套餐与付费</div>
|
||
<div class="section-card">
|
||
<div class="section-item" id="upgradePlan">
|
||
<div class="section-item-icon">
|
||
<svg viewBox="0 0 24 24"><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" stroke-width="2"/></svg>
|
||
</div>
|
||
<div class="section-item-content">
|
||
<div class="section-item-label">升级套餐</div>
|
||
<div class="section-item-desc">解锁完整导出与更多功能</div>
|
||
</div>
|
||
<svg class="section-item-arrow" viewBox="0 0 24 24"><path d="M9 18l6-6-6-6" stroke-width="2"/></svg>
|
||
</div>
|
||
<div class="section-item">
|
||
<div class="section-item-icon">
|
||
<svg viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3" stroke-width="2"/></svg>
|
||
</div>
|
||
<div class="section-item-content">
|
||
<div class="section-item-label">我的订单</div>
|
||
</div>
|
||
<svg class="section-item-arrow" viewBox="0 0 24 24"><path d="M9 18l6-6-6-6" stroke-width="2"/></svg>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="profile-section">
|
||
<div class="section-title">数据与隐私</div>
|
||
<div class="section-card">
|
||
<div class="section-item" id="exportData">
|
||
<div class="section-item-icon">
|
||
<svg viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M17 8l-5-5-5 5M12 3v12" stroke-width="2"/></svg>
|
||
</div>
|
||
<div class="section-item-content">
|
||
<div class="section-item-label">导出所有数据</div>
|
||
</div>
|
||
<svg class="section-item-arrow" viewBox="0 0 24 24"><path d="M9 18l6-6-6-6" stroke-width="2"/></svg>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="profile-section">
|
||
<div class="section-title">设置</div>
|
||
<div class="section-card">
|
||
<div class="section-item">
|
||
<div class="section-item-icon">
|
||
<svg viewBox="0 0 24 24"><path d="M12 6v6l4 2" stroke-width="2"/><circle cx="12" cy="12" r="10" stroke-width="2" fill="none"/></svg>
|
||
</div>
|
||
<div class="section-item-content">
|
||
<div class="section-item-label">语速</div>
|
||
<div class="section-item-desc">标准</div>
|
||
</div>
|
||
<svg class="section-item-arrow" viewBox="0 0 24 24"><path d="M9 18l6-6-6-6" stroke-width="2"/></svg>
|
||
</div>
|
||
<div class="section-item">
|
||
<div class="section-item-icon">
|
||
<svg viewBox="0 0 24 24"><path d="M4 7V4h16v3M9 20h6M12 4v16" stroke-width="2"/></svg>
|
||
</div>
|
||
<div class="section-item-content">
|
||
<div class="section-item-label">大字模式</div>
|
||
</div>
|
||
<div class="section-item-toggle" id="largeFontToggle"></div>
|
||
</div>
|
||
<div class="section-item">
|
||
<div class="section-item-icon">
|
||
<svg viewBox="0 0 24 24"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z" stroke-width="2"/></svg>
|
||
</div>
|
||
<div class="section-item-content">
|
||
<div class="section-item-label">夜间模式</div>
|
||
</div>
|
||
<div class="section-item-toggle" id="darkModeToggle"></div>
|
||
</div>
|
||
<div class="section-item">
|
||
<div class="section-item-icon">
|
||
<svg viewBox="0 0 24 24"><path d="M18 8A6 6 0 006 8c0 7-3 9-3 9h18s-3-2-3-9M13.73 21a2 2 0 01-3.46 0" stroke-width="2"/></svg>
|
||
</div>
|
||
<div class="section-item-content">
|
||
<div class="section-item-label">每日提醒</div>
|
||
<div class="section-item-desc">每天提醒聊5分钟</div>
|
||
</div>
|
||
<div class="section-item-toggle active" id="reminderToggle"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="profile-section">
|
||
<div class="section-title">帮助</div>
|
||
<div class="section-card">
|
||
<div class="section-item">
|
||
<div class="section-item-icon">
|
||
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" stroke-width="2" fill="none"/><path d="M9.09 9a3 3 0 015.83 1c0 2-3 3-3 3M12 17h.01" stroke-width="2"/></svg>
|
||
</div>
|
||
<div class="section-item-content">
|
||
<div class="section-item-label">常见问题</div>
|
||
</div>
|
||
<svg class="section-item-arrow" viewBox="0 0 24 24"><path d="M9 18l6-6-6-6" stroke-width="2"/></svg>
|
||
</div>
|
||
<div class="section-item">
|
||
<div class="section-item-icon">
|
||
<svg viewBox="0 0 24 24"><path d="M21 11.5a8.38 8.38 0 01-.9 3.8 8.5 8.5 0 01-7.6 4.7 8.38 8.38 0 01-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 01-.9-3.8 8.5 8.5 0 014.7-7.6 8.38 8.38 0 013.8-.9h.5a8.48 8.48 0 018 8v.5z" stroke-width="2"/></svg>
|
||
</div>
|
||
<div class="section-item-content">
|
||
<div class="section-item-label">反馈与客服</div>
|
||
</div>
|
||
<svg class="section-item-arrow" viewBox="0 0 24 24"><path d="M9 18l6-6-6-6" stroke-width="2"/></svg>
|
||
</div>
|
||
<div class="section-item">
|
||
<div class="section-item-icon">
|
||
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" stroke-width="2" fill="none"/><path d="M12 16v-4M12 8h.01" stroke-width="2"/></svg>
|
||
</div>
|
||
<div class="section-item-content">
|
||
<div class="section-item-label">关于我们</div>
|
||
</div>
|
||
<svg class="section-item-arrow" viewBox="0 0 24 24"><path d="M9 18l6-6-6-6" stroke-width="2"/></svg>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Navigation Bar -->
|
||
<nav class="nav-bar">
|
||
<div class="nav-item active" data-page="0">
|
||
<svg viewBox="0 0 24 24"><path d="M12 1a3 3 0 00-3 3v8a3 3 0 006 0V4a3 3 0 00-3-3z" fill="currentColor"/><path d="M19 10v2a7 7 0 01-14 0v-2M12 19v4M8 23h8"/></svg>
|
||
<span>聊天</span>
|
||
</div>
|
||
<div class="nav-item" data-page="1">
|
||
<svg viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 016.5 17H20M4 19.5A2.5 2.5 0 004 14.5V5a2 2 0 012-2h14v16.5"/></svg>
|
||
<span>回忆录</span>
|
||
</div>
|
||
<div class="nav-item" data-page="2">
|
||
<svg viewBox="0 0 24 24"><path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2M12 11a4 4 0 100-8 4 4 0 000 8z"/></svg>
|
||
<span>我的</span>
|
||
</div>
|
||
</nav>
|
||
|
||
<!-- Memoir Actions (floating) -->
|
||
<div class="memoir-actions" id="memoirActions">
|
||
<div class="action-btn primary" id="exportPdf">
|
||
<svg viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3" stroke-width="2"/></svg>
|
||
<span>导出 PDF</span>
|
||
</div>
|
||
<div class="action-btn secondary" id="shareLink">
|
||
<svg viewBox="0 0 24 24"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><path d="M8.59 13.51l6.83 3.98M15.41 6.51l-6.82 3.98" stroke-width="2"/></svg>
|
||
<span>分享</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Transcript Overlay -->
|
||
<div class="transcript-overlay" id="transcriptOverlay">
|
||
<div class="transcript-close" id="transcriptClose">
|
||
<svg viewBox="0 0 24 24"><path d="M18 6L6 18M6 6l12 12" stroke-width="2"/></svg>
|
||
</div>
|
||
<div class="transcript-header">
|
||
<div class="transcript-timer" id="transcriptTimer">00:00</div>
|
||
<div class="transcript-status">
|
||
<span class="recording-dot"></span>
|
||
<span>正在记录...</span>
|
||
</div>
|
||
<div class="waveform">
|
||
<div class="waveform-bar"></div>
|
||
<div class="waveform-bar"></div>
|
||
<div class="waveform-bar"></div>
|
||
<div class="waveform-bar"></div>
|
||
<div class="waveform-bar"></div>
|
||
<div class="waveform-bar"></div>
|
||
<div class="waveform-bar"></div>
|
||
</div>
|
||
</div>
|
||
<div class="transcript-content" id="transcriptContent">
|
||
<!-- Messages will be added here -->
|
||
</div>
|
||
<button class="transcript-end-btn" id="endRecording">
|
||
<svg viewBox="0 0 24 24"><rect x="6" y="6" width="12" height="12" rx="2"/></svg>
|
||
<span>结束聊天</span>
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Toast -->
|
||
<div class="toast" id="toast"></div>
|
||
|
||
<!-- Edit Title Modal -->
|
||
<div class="edit-modal" id="editModal">
|
||
<div class="edit-modal-content">
|
||
<div class="edit-modal-title">编辑回忆录名称</div>
|
||
<input type="text" class="edit-modal-input" id="editTitleInput" value="这一生" maxlength="20">
|
||
<div class="edit-modal-hint">建议 2-10 个字</div>
|
||
<div class="edit-modal-btns">
|
||
<button class="edit-modal-btn cancel" id="editCancel">取消</button>
|
||
<button class="edit-modal-btn confirm" id="editConfirm">确定</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// State
|
||
let currentPage = 0;
|
||
let isRecording = false;
|
||
let recordingSeconds = 0;
|
||
let recordingInterval = null;
|
||
let sessionDuration = 0;
|
||
|
||
// Elements
|
||
const pagesContainer = document.getElementById('pagesContainer');
|
||
const navItems = document.querySelectorAll('.nav-item');
|
||
const recordBtn = document.getElementById('recordBtn');
|
||
const recordBtnText = document.getElementById('recordBtnText');
|
||
const micIcon = document.getElementById('micIcon');
|
||
const stopIcon = document.getElementById('stopIcon');
|
||
const recordHint = document.getElementById('recordHint');
|
||
const timerDisplay = document.getElementById('timerDisplay');
|
||
const sessionSummary = document.getElementById('sessionSummary');
|
||
const transcriptOverlay = document.getElementById('transcriptOverlay');
|
||
const transcriptTimer = document.getElementById('transcriptTimer');
|
||
const transcriptContent = document.getElementById('transcriptContent');
|
||
const transcriptClose = document.getElementById('transcriptClose');
|
||
const endRecordingBtn = document.getElementById('endRecording');
|
||
const gotoBook = document.getElementById('gotoBook');
|
||
const memoirActions = document.getElementById('memoirActions');
|
||
const tocView = document.getElementById('tocView');
|
||
const readingView = document.getElementById('readingView');
|
||
const backToToc = document.getElementById('backToToc');
|
||
const memoirTabs = document.querySelectorAll('.memoir-tab');
|
||
const tocItems = document.querySelectorAll('.toc-item');
|
||
const toast = document.getElementById('toast');
|
||
const toggles = document.querySelectorAll('.section-item-toggle');
|
||
const bookTitle = document.getElementById('bookTitle');
|
||
const bookTitleText = document.getElementById('bookTitleText');
|
||
const tocBookTitle = document.querySelector('.toc-book-title');
|
||
const editModal = document.getElementById('editModal');
|
||
const editTitleInput = document.getElementById('editTitleInput');
|
||
const editCancel = document.getElementById('editCancel');
|
||
const editConfirm = document.getElementById('editConfirm');
|
||
|
||
// Demo conversation
|
||
const demoConversation = [
|
||
{ type: 'ai', text: '您好!今天想聊些什么呢?可以从童年开始,或者聊聊最近想起的往事。' },
|
||
{ type: 'user', text: '我想聊聊我小时候的事,那时候我们住在农村...' },
|
||
{ type: 'ai', text: '好的,农村的童年一定有很多有趣的回忆。您还记得那时候住的房子是什么样的吗?' },
|
||
{ type: 'user', text: '是一个土坯房,院子里有一棵很大的槐树。每到夏天,树上开满了白色的花,香得很。' },
|
||
{ type: 'ai', text: '槐花的香味,确实令人难忘。您有没有和小伙伴们一起在槐树下玩耍的经历呢?' }
|
||
];
|
||
|
||
// Navigate to page
|
||
function navigateTo(pageIndex) {
|
||
currentPage = pageIndex;
|
||
pagesContainer.style.transform = `translateX(-${pageIndex * 33.333}%)`;
|
||
|
||
navItems.forEach((item, index) => {
|
||
item.classList.toggle('active', index === pageIndex);
|
||
});
|
||
|
||
// Show/hide memoir actions
|
||
memoirActions.classList.toggle('visible', pageIndex === 1);
|
||
}
|
||
|
||
// Format time
|
||
function formatTime(seconds) {
|
||
const mins = Math.floor(seconds / 60);
|
||
const secs = seconds % 60;
|
||
return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
|
||
}
|
||
|
||
// Start recording
|
||
function startRecording() {
|
||
isRecording = true;
|
||
recordingSeconds = 0;
|
||
|
||
recordBtn.classList.add('recording');
|
||
recordBtnText.textContent = '录音中...';
|
||
micIcon.style.display = 'none';
|
||
stopIcon.style.display = 'block';
|
||
recordHint.style.display = 'none';
|
||
timerDisplay.classList.add('visible');
|
||
|
||
// Show transcript overlay and hide nav bar
|
||
transcriptOverlay.classList.add('visible');
|
||
document.querySelector('.nav-bar').style.display = 'none';
|
||
transcriptContent.innerHTML = '';
|
||
|
||
// Start timer
|
||
recordingInterval = setInterval(() => {
|
||
recordingSeconds++;
|
||
const timeStr = formatTime(recordingSeconds);
|
||
timerDisplay.textContent = timeStr;
|
||
transcriptTimer.textContent = timeStr;
|
||
|
||
// Add demo messages
|
||
if (recordingSeconds <= demoConversation.length * 3) {
|
||
const msgIndex = Math.floor((recordingSeconds - 1) / 3);
|
||
if (recordingSeconds % 3 === 1 && msgIndex < demoConversation.length) {
|
||
addTranscriptMessage(demoConversation[msgIndex]);
|
||
}
|
||
}
|
||
}, 1000);
|
||
}
|
||
|
||
// Stop recording
|
||
function stopRecording() {
|
||
isRecording = false;
|
||
sessionDuration = recordingSeconds;
|
||
|
||
clearInterval(recordingInterval);
|
||
|
||
recordBtn.classList.remove('recording');
|
||
recordBtnText.textContent = '开始聊天';
|
||
micIcon.style.display = 'block';
|
||
stopIcon.style.display = 'none';
|
||
recordHint.style.display = 'block';
|
||
timerDisplay.classList.remove('visible');
|
||
|
||
// Hide transcript overlay and show nav bar
|
||
transcriptOverlay.classList.remove('visible');
|
||
document.querySelector('.nav-bar').style.display = 'flex';
|
||
|
||
// Show session summary
|
||
document.getElementById('summaryDuration').textContent = formatTime(sessionDuration).replace(':', '分') + '秒';
|
||
sessionSummary.classList.add('visible');
|
||
|
||
// Update today's minutes
|
||
const todayMins = parseInt(document.getElementById('todayMinutes').textContent) + Math.ceil(sessionDuration / 60);
|
||
document.getElementById('todayMinutes').textContent = todayMins;
|
||
|
||
showToast('聊天已保存,正在整理...');
|
||
}
|
||
|
||
// Add transcript message
|
||
function addTranscriptMessage(message) {
|
||
const msgEl = document.createElement('div');
|
||
msgEl.className = `transcript-message ${message.type}`;
|
||
msgEl.innerHTML = `<div class="transcript-bubble">${message.text}</div>`;
|
||
transcriptContent.appendChild(msgEl);
|
||
transcriptContent.scrollTop = transcriptContent.scrollHeight;
|
||
}
|
||
|
||
// Show reading view
|
||
function showReadingView() {
|
||
tocView.style.display = 'none';
|
||
readingView.classList.add('active');
|
||
memoirTabs[0].classList.remove('active');
|
||
memoirTabs[1].classList.add('active');
|
||
}
|
||
|
||
// Show TOC view
|
||
function showTocView() {
|
||
tocView.style.display = 'block';
|
||
readingView.classList.remove('active');
|
||
memoirTabs[0].classList.add('active');
|
||
memoirTabs[1].classList.remove('active');
|
||
}
|
||
|
||
// Show toast
|
||
function showToast(message) {
|
||
toast.textContent = message;
|
||
toast.classList.add('visible');
|
||
setTimeout(() => {
|
||
toast.classList.remove('visible');
|
||
}, 2000);
|
||
}
|
||
|
||
// Event listeners
|
||
navItems.forEach((item, index) => {
|
||
item.addEventListener('click', () => navigateTo(index));
|
||
});
|
||
|
||
recordBtn.addEventListener('click', () => {
|
||
if (isRecording) {
|
||
stopRecording();
|
||
} else {
|
||
startRecording();
|
||
}
|
||
});
|
||
|
||
transcriptClose.addEventListener('click', stopRecording);
|
||
endRecordingBtn.addEventListener('click', stopRecording);
|
||
|
||
gotoBook.addEventListener('click', () => navigateTo(1));
|
||
|
||
tocItems.forEach(item => {
|
||
item.addEventListener('click', showReadingView);
|
||
});
|
||
|
||
backToToc.addEventListener('click', showTocView);
|
||
|
||
memoirTabs.forEach(tab => {
|
||
tab.addEventListener('click', () => {
|
||
if (tab.dataset.tab === 'toc') {
|
||
showTocView();
|
||
} else {
|
||
showReadingView();
|
||
}
|
||
});
|
||
});
|
||
|
||
document.getElementById('exportPdf').addEventListener('click', () => {
|
||
showToast('PDF 生成中...');
|
||
});
|
||
|
||
document.getElementById('shareLink').addEventListener('click', () => {
|
||
showToast('链接已复制');
|
||
});
|
||
|
||
toggles.forEach(toggle => {
|
||
toggle.addEventListener('click', () => {
|
||
toggle.classList.toggle('active');
|
||
});
|
||
});
|
||
|
||
document.getElementById('upgradePlan').addEventListener('click', () => {
|
||
showToast('套餐页面开发中...');
|
||
});
|
||
|
||
document.getElementById('exportData').addEventListener('click', () => {
|
||
showToast('数据导出中...');
|
||
});
|
||
|
||
// Edit title functionality
|
||
bookTitle.addEventListener('click', () => {
|
||
const currentTitle = bookTitleText.textContent.replace(/[《》]/g, '');
|
||
editTitleInput.value = currentTitle;
|
||
editModal.classList.add('visible');
|
||
editTitleInput.focus();
|
||
editTitleInput.select();
|
||
});
|
||
|
||
editCancel.addEventListener('click', () => {
|
||
editModal.classList.remove('visible');
|
||
});
|
||
|
||
editConfirm.addEventListener('click', () => {
|
||
const newTitle = editTitleInput.value.trim();
|
||
if (newTitle) {
|
||
bookTitleText.textContent = `《${newTitle}》`;
|
||
tocBookTitle.textContent = newTitle;
|
||
editModal.classList.remove('visible');
|
||
showToast('已保存');
|
||
}
|
||
});
|
||
|
||
editTitleInput.addEventListener('keydown', (e) => {
|
||
if (e.key === 'Enter') {
|
||
editConfirm.click();
|
||
} else if (e.key === 'Escape') {
|
||
editCancel.click();
|
||
}
|
||
});
|
||
|
||
editModal.addEventListener('click', (e) => {
|
||
if (e.target === editModal) {
|
||
editModal.classList.remove('visible');
|
||
}
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|
||
|