Compare commits

..

3 Commits

Author SHA1 Message Date
cc
5802cf36c6 年报S8临时修改 2026-04-21 23:44:20 +08:00
xuncha
e3174370bb Merge pull request #817 from xunchahaha:dev
修复双人年度报告[Bug]: 双人年度报告坏了
2026-04-21 20:21:01 +08:00
xuncha
0f8a9602bd 修复双人年度报告[Bug]: 双人年度报告坏了
Fixes #816
2026-04-21 20:20:11 +08:00
7 changed files with 1106 additions and 2193 deletions

8
package-lock.json generated
View File

@@ -21,7 +21,7 @@
"jszip": "^3.10.1", "jszip": "^3.10.1",
"koffi": "^2.9.0", "koffi": "^2.9.0",
"lucide-react": "^1.7.0", "lucide-react": "^1.7.0",
"react": "^19.2.5", "react": "^19.2.3",
"react-dom": "^19.2.3", "react-dom": "^19.2.3",
"react-markdown": "^10.1.0", "react-markdown": "^10.1.0",
"react-router-dom": "^7.14.0", "react-router-dom": "^7.14.0",
@@ -8464,9 +8464,9 @@
} }
}, },
"node_modules/react": { "node_modules/react": {
"version": "19.2.5", "version": "19.2.4",
"resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz", "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
"integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==", "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"

View File

@@ -35,7 +35,7 @@
"jszip": "^3.10.1", "jszip": "^3.10.1",
"koffi": "^2.9.0", "koffi": "^2.9.0",
"lucide-react": "^1.7.0", "lucide-react": "^1.7.0",
"react": "^19.2.5", "react": "^19.2.3",
"react-dom": "^19.2.3", "react-dom": "^19.2.3",
"react-markdown": "^10.1.0", "react-markdown": "^10.1.0",
"react-router-dom": "^7.14.0", "react-router-dom": "^7.14.0",

View File

@@ -81,6 +81,7 @@ function App() {
const isStandaloneChatWindow = location.pathname === '/chat-window' const isStandaloneChatWindow = location.pathname === '/chat-window'
const isNotificationWindow = location.pathname === '/notification-window' const isNotificationWindow = location.pathname === '/notification-window'
const isAnnualReportWindow = location.pathname === '/annual-report/view' const isAnnualReportWindow = location.pathname === '/annual-report/view'
const isDualReportWindow = location.pathname === '/dual-report/view'
const isSettingsRoute = location.pathname === '/settings' const isSettingsRoute = location.pathname === '/settings'
const settingsRouteState = location.state as { backgroundLocation?: Location; initialTab?: unknown } | null const settingsRouteState = location.state as { backgroundLocation?: Location; initialTab?: unknown } | null
const routeLocation = isSettingsRoute const routeLocation = isSettingsRoute
@@ -128,7 +129,7 @@ function App() {
const body = document.body const body = document.body
const appRoot = document.getElementById('app') const appRoot = document.getElementById('app')
if (isOnboardingWindow || isNotificationWindow || isAnnualReportWindow) { if (isOnboardingWindow || isNotificationWindow || isAnnualReportWindow || isDualReportWindow) {
root.style.background = 'transparent' root.style.background = 'transparent'
body.style.background = 'transparent' body.style.background = 'transparent'
body.style.overflow = 'hidden' body.style.overflow = 'hidden'
@@ -145,7 +146,7 @@ function App() {
appRoot.style.overflow = '' appRoot.style.overflow = ''
} }
} }
}, [isOnboardingWindow, isNotificationWindow, isAnnualReportWindow]) }, [isOnboardingWindow, isNotificationWindow, isAnnualReportWindow, isDualReportWindow])
// 应用主题 // 应用主题
useEffect(() => { useEffect(() => {
@@ -166,7 +167,7 @@ function App() {
} }
mq.addEventListener('change', handler) mq.addEventListener('change', handler)
return () => mq.removeEventListener('change', handler) return () => mq.removeEventListener('change', handler)
}, [currentTheme, themeMode, isOnboardingWindow, isNotificationWindow, isAnnualReportWindow]) }, [currentTheme, themeMode, isOnboardingWindow, isNotificationWindow, isAnnualReportWindow, isDualReportWindow])
// 读取已保存的主题设置 // 读取已保存的主题设置
useEffect(() => { useEffect(() => {
@@ -517,6 +518,11 @@ function App() {
return <AnnualReportWindow /> return <AnnualReportWindow />
} }
// 独立双人报告全屏窗口
if (isDualReportWindow) {
return <DualReportWindow />
}
// 主窗口 - 完整布局 // 主窗口 - 完整布局
const handleCloseSettings = () => { const handleCloseSettings = () => {
const backgroundLocation = settingsRouteState?.backgroundLocation ?? settingsBackgroundRef.current const backgroundLocation = settingsRouteState?.backgroundLocation ?? settingsBackgroundRef.current

View File

@@ -299,6 +299,12 @@
opacity: 0.05; opacity: 0.05;
box-shadow: none; box-shadow: none;
filter: blur(80px); filter: blur(80px);
animation: coreBreathing 6s ease-in-out infinite;
}
@keyframes coreBreathing {
0%, 100% { opacity: 0.03; transform: translate(-50%, -50%) scale(0.95); }
50% { opacity: 0.06; transform: translate(-50%, -50%) scale(1.05); }
} }
/* S9: LEXICON (大气) */ /* S9: LEXICON (大气) */
@@ -643,199 +649,160 @@
} }
#scene-8 { #scene-8 {
align-items: flex-start; align-items: center;
justify-content: flex-start; justify-content: center;
padding: 0 6vw; padding: 0;
overflow: hidden;
} }
#scene-8 .s8-layout { /* V2 Background: Cinematic Aura */
#scene-8 .s8-bg-layer {
position: absolute; position: absolute;
top: 18vh; inset: -10%;
left: 50%; z-index: 1;
transform: translateX(-50%); opacity: 0;
width: min(1240px, 86vw); transition: opacity 2s 0.2s var(--ease-out);
display: grid; filter: blur(120px) contrast(1.1) brightness(0.6);
grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr); pointer-events: none;
column-gap: clamp(34px, 4.8vw, 84px);
align-items: start; .bg-avatar {
width: 100%;
height: 100%;
object-fit: cover;
transform: scale(1.2);
}
} }
#scene-8 .s8-left { .scene.active #scene-8 .s8-bg-layer {
opacity: 0.18;
}
#scene-8 .s8-floating-layout {
position: relative;
width: 100vw;
height: 100vh;
z-index: 2;
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-template-rows: repeat(12, 1fr);
padding: 10vh 8vw;
}
/* The Central Pivot: Name & Meta */
#scene-8 .s8-hero-unit {
grid-column: 2 / 8;
grid-row: 4 / 7;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: clamp(2.5vh, 3.2vh, 4vh); justify-content: center;
padding-top: clamp(8vh, 9vh, 11vh);
}
#scene-8 .s8-name-wrap, .s8-name {
#scene-8 .s8-summary-wrap, font-size: clamp(4.5rem, 10vw, 8.5rem);
#scene-8 .s8-quote-wrap, font-weight: 700;
#scene-8 .s8-letter-wrap { color: var(--c-text-bright);
display: block;
width: 100%;
}
#scene-8 .s8-name {
font-size: clamp(3.2rem, 7.4vw, 5.6rem);
color: rgba(var(--c-gold-rgb), 0.88);
letter-spacing: 0.08em; letter-spacing: 0.08em;
line-height: 1.05; line-height: 1;
margin-bottom: 2vh;
background: linear-gradient(135deg, var(--c-gold-strong), var(--c-text-bright), var(--c-gold-strong));
background-size: 200% auto;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: shine 8s linear infinite;
text-shadow: 0 0 40px rgba(var(--c-gold-rgb), 0.2);
} }
#scene-8 .s8-summary { .s8-meta {
max-width: 34ch; font-family: 'SpaceMonoLocal';
font-size: clamp(1.06rem, 1.35vw, 1.35rem); font-size: clamp(0.7rem, 0.85vw, 0.9rem);
color: var(--c-text-soft);
line-height: 1.95;
letter-spacing: 0.02em;
}
#scene-8 .s8-summary-count {
margin: 0 8px;
font-size: clamp(1.35rem, 2vw, 1.75rem);
color: var(--c-gold-strong); color: var(--c-gold-strong);
white-space: nowrap; letter-spacing: 0.4em;
} text-transform: uppercase;
display: flex;
align-items: center;
gap: 1.5vw;
#scene-8 .s8-quote { &::after {
max-width: 32ch;
font-size: clamp(0.98rem, 1.12vw, 1.1rem);
color: var(--c-text-muted);
line-height: 1.9;
}
#scene-8 .s8-letter-wrap {
margin-top: clamp(3vh, 4vh, 5.5vh);
}
#scene-8 .s8-letter {
position: relative;
padding: clamp(24px, 3.2vh, 38px) clamp(20px, 2.6vw, 34px) clamp(24px, 3.2vh, 38px) clamp(30px, 3.2vw, 44px);
border-radius: 18px;
border: 1px solid rgba(var(--c-gold-rgb), 0.34);
background: linear-gradient(135deg, rgba(var(--c-gold-rgb), 0.16), rgba(var(--c-gold-rgb), 0.04));
font-size: clamp(0.95rem, 1.05vw, 1.08rem);
line-height: 2;
color: var(--c-text-soft);
text-align: left;
text-shadow: 0 4px 16px rgba(0, 0, 0, 0.22);
}
#scene-8 .s8-letter::before {
content: ''; content: '';
flex: 1;
height: 1px;
background: linear-gradient(to right, rgba(var(--c-gold-rgb), 0.6), transparent);
}
}
}
/* Fragmented Storytelling */
#scene-8 .s8-fragments {
position: absolute; position: absolute;
top: 20px; inset: 0;
left: 14px; pointer-events: none;
width: 2px; }
height: calc(100% - 40px);
border-radius: 2px; #scene-8 .fragment {
background: linear-gradient(to bottom, rgba(var(--c-gold-rgb), 0.7), rgba(var(--c-gold-rgb), 0.08)); position: absolute;
max-width: 24ch;
font-size: clamp(0.95rem, 1.1vw, 1.15rem);
line-height: 2.1;
color: var(--c-text-muted);
font-weight: 300;
&.f1 {
top: 25vh;
right: 12vw;
text-align: right;
color: var(--c-text-soft);
font-style: italic;
}
&.f2 {
bottom: 20vh;
left: 15vw;
max-width: 38ch;
}
&.f3 {
bottom: 12vh;
right: 10vw;
text-align: right;
opacity: 0.6;
font-size: 0.85rem;
letter-spacing: 0.05em;
}
}
@keyframes shine {
to { background-position: 200% center; }
} }
#scene-8 .s8-empty-wrap { #scene-8 .s8-empty-wrap {
display: block; grid-column: 4 / 10;
width: min(760px, 78vw); grid-row: 5 / 8;
margin-top: 24vh;
text-align: center; text-align: center;
} .s8-empty-text {
font-size: 1.6rem;
#scene-8 .s8-empty-text { line-height: 2.5;
color: var(--c-text); color: var(--c-text-soft);
line-height: 2; font-weight: 200;
}
@media (max-width: 1280px) {
#scene-8 .s8-layout {
width: min(1120px, 88vw);
grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.05fr);
column-gap: clamp(28px, 4vw, 56px);
}
#scene-8 .s8-left {
padding-top: clamp(6vh, 8vh, 9vh);
} }
} }
@media (max-width: 1024px) { @media (max-width: 1024px) {
#scene-8 .s8-layout { #scene-8 .s8-hero-unit {
top: 16vh; grid-column: 2 / 12;
width: min(900px, 90vw); grid-row: 2 / 5;
grid-template-columns: 1fr;
row-gap: clamp(3vh, 3.5vh, 4.5vh);
} }
#scene-8 .fragment {
#scene-8 .s8-left { position: relative;
padding-top: 0; inset: auto !important;
gap: clamp(1.6vh, 2.2vh, 2.8vh); max-width: 100%;
text-align: left !important;
margin-top: 4vh;
} }
#scene-8 .s8-fragments {
#scene-8 .s8-name { position: relative;
font-size: clamp(2.4rem, 8.4vw, 4.2rem); grid-column: 2 / 12;
letter-spacing: 0.06em; grid-row: 6 / 12;
} display: flex;
flex-direction: column;
#scene-8 .s8-summary,
#scene-8 .s8-quote {
max-width: none;
}
#scene-8 .s8-letter-wrap {
margin-top: 0;
}
#scene-8 .s8-letter {
font-size: clamp(0.9rem, 1.9vw, 1rem);
line-height: 1.95;
}
}
@media (max-width: 760px) {
#scene-8 .s8-layout {
top: 14.5vh;
width: 92vw;
row-gap: clamp(2.2vh, 3vh, 3.8vh);
}
#scene-8 .s8-name {
font-size: clamp(2rem, 10vw, 3rem);
}
#scene-8 .s8-summary {
font-size: clamp(0.92rem, 3.9vw, 1rem);
line-height: 1.85;
}
#scene-8 .s8-summary-count {
margin: 0 6px;
font-size: clamp(1.1rem, 4.8vw, 1.35rem);
}
#scene-8 .s8-quote {
font-size: clamp(0.86rem, 3.5vw, 0.95rem);
line-height: 1.8;
}
#scene-8 .s8-letter {
border-radius: 14px;
padding: 16px 16px 16px 24px;
font-size: clamp(0.82rem, 3.4vw, 0.9rem);
line-height: 1.82;
}
#scene-8 .s8-letter::before {
top: 16px;
left: 11px;
height: calc(100% - 32px);
}
#scene-8 .s8-empty-wrap {
width: 88vw;
margin-top: 23vh;
}
#scene-8 .s8-empty-text {
font-size: 1rem;
line-height: 1.9;
} }
} }

View File

@@ -872,45 +872,69 @@ function AnnualReportWindow() {
<div className="reveal-wrap en-tag"> <div className="reveal-wrap en-tag">
<div className="reveal-inner serif scene0-cn-tag"></div> <div className="reveal-inner serif scene0-cn-tag"></div>
</div> </div>
{reportData.lostFriend && (
<div className="s8-bg-layer">
<img src={reportData.lostFriend.avatarUrl} alt="" className="bg-avatar" />
</div>
)}
{reportData.lostFriend ? ( {reportData.lostFriend ? (
<div className="s8-layout"> <div className="s8-floating-layout">
<div className="s8-left"> <div className="s8-hero-unit">
<div className="reveal-wrap s8-name-wrap"> <div className="reveal-wrap">
<div className="reveal-inner serif delay-1 s8-name"> <div className="reveal-inner s8-name delay-1">
{reportData.lostFriend.displayName} {reportData.lostFriend.displayName}
</div> </div>
</div> </div>
<div className="reveal-wrap s8-summary-wrap"> <div className="reveal-wrap">
<div className="reveal-inner serif delay-2 s8-summary"> <div className="reveal-inner s8-meta delay-2">
{reportData.lostFriend.periodDesc} {reportData.lostFriend.periodDesc} /
<span className="num-display s8-summary-count"> <span className="num-display" style={{ margin: '0 10px', fontSize: '1.4em' }}>
<DecodeText value={reportData.lostFriend.lateCount.toLocaleString()} active={currentScene === 8} /> <DecodeText value={reportData.lostFriend.lateCount.toLocaleString()} active={currentScene === 8} />
</span> </span>
MESSAGES
</div>
</div>
<div className="reveal-wrap s8-quote-wrap">
<div className="reveal-inner serif delay-3 s8-quote">
</div> </div>
</div> </div>
</div> </div>
<div className="reveal-wrap s8-letter-wrap">
<div className="reveal-inner serif delay-4 s8-letter"> <div className="s8-fragments">
<div className="reveal-wrap fragment f1">
<div className="reveal-inner delay-3">
<br />
</div>
</div>
<div className="reveal-wrap fragment f2">
<div className="reveal-inner delay-4">
<br />
<br />
</div>
</div>
<div className="reveal-wrap fragment f3">
<div className="reveal-inner delay-5">
<br />
</div>
</div> </div>
</div> </div>
</div> </div>
) : ( ) : (
<div className="reveal-wrap desc-text s8-empty-wrap"> <div className="s8-floating-layout">
<div className="reveal-inner serif delay-1 s8-empty-text"> <div className="reveal-wrap s8-empty-wrap">
<div className="reveal-inner serif s8-empty-text delay-1">
<br /> <br />
<br/>
</div>
</div> </div>
</div> </div>
)} )}
</div> </div>
{/* S9: LEXICON & ARCHIVE */} {/* S9: LEXICON & ARCHIVE */}
<div className={getSceneClass(9)} id="scene-9"> <div className={getSceneClass(9)} id="scene-9">
<div className="reveal-wrap en-tag"> <div className="reveal-wrap en-tag">

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff