Skip to content

Commit a609d03

Browse files
committed
feat(shell): support live video wallpaper with toggle button
Replace static image wallpaper with a video wallpaper (mp4) as the default, and add a toggle button to switch between live video and static image. Enhance desktop icon and control button visibility with backdrop blur, darker backgrounds, and edge-gradient overlay for better contrast.
1 parent 4c87cf7 commit a609d03

2 files changed

Lines changed: 119 additions & 13 deletions

File tree

apps/webuiapps/src/components/Shell/index.module.scss

Lines changed: 82 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,17 @@
88
position: relative;
99
}
1010

11+
.videoBg {
12+
position: absolute;
13+
top: 0;
14+
left: 0;
15+
width: 100%;
16+
height: 100%;
17+
object-fit: cover;
18+
z-index: 0;
19+
pointer-events: none;
20+
}
21+
1122
.desktop {
1223
width: 100%;
1324
height: 100%;
@@ -17,7 +28,16 @@
1728
display: flex;
1829
align-items: flex-start;
1930
padding: 32px;
20-
background: rgba(0, 0, 0, 0.35);
31+
background: linear-gradient(
32+
to right,
33+
rgba(0, 0, 0, 0.7) 0%,
34+
rgba(0, 0, 0, 0.3) 14%,
35+
rgba(0, 0, 0, 0.05) 35%,
36+
rgba(0, 0, 0, 0.05) 70%,
37+
rgba(0, 0, 0, 0.3) 90%,
38+
rgba(0, 0, 0, 0.65) 100%
39+
);
40+
z-index: 1;
2141
}
2242

2343
.iconGrid {
@@ -61,6 +81,9 @@
6181
display: flex;
6282
align-items: center;
6383
justify-content: center;
84+
backdrop-filter: blur(16px);
85+
background: rgba(0, 0, 0, 0.45);
86+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
6487
}
6588

6689
.iconLabel {
@@ -71,6 +94,47 @@
7194
overflow: hidden;
7295
text-overflow: ellipsis;
7396
white-space: nowrap;
97+
color: #fff;
98+
text-shadow:
99+
0 1px 2px rgba(0, 0, 0, 0.9),
100+
0 0 8px rgba(0, 0, 0, 0.5);
101+
}
102+
103+
.liveWallpaperToggle {
104+
position: fixed;
105+
right: 16px;
106+
bottom: 160px;
107+
width: 36px;
108+
height: 36px;
109+
border-radius: 50%;
110+
cursor: pointer;
111+
display: flex;
112+
align-items: center;
113+
justify-content: center;
114+
z-index: 9999;
115+
transition: all 0.2s;
116+
backdrop-filter: blur(16px);
117+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.6);
118+
119+
&.liveOn {
120+
background: rgba(139, 92, 246, 0.3);
121+
color: #a78bfa;
122+
border: 1px solid rgba(139, 92, 246, 0.4);
123+
}
124+
125+
&.liveOff {
126+
background: rgba(255, 255, 255, 0.12);
127+
color: rgba(255, 255, 255, 0.5);
128+
border: 1px solid rgba(255, 255, 255, 0.15);
129+
}
130+
131+
&:hover {
132+
transform: scale(1.1);
133+
}
134+
135+
&.chatOpen {
136+
right: 336px;
137+
}
74138
}
75139

76140
.langToggle {
@@ -87,15 +151,18 @@
87151
justify-content: center;
88152
z-index: 9999;
89153
transition: all 0.2s;
90-
background: rgba(250, 234, 95, 0.15);
154+
background: rgba(250, 234, 95, 0.25);
155+
backdrop-filter: blur(16px);
91156
color: #faea5f;
92157
font-size: 12px;
93158
font-weight: 800;
94159
letter-spacing: -0.5px;
160+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.6);
161+
border: 1px solid rgba(250, 234, 95, 0.3);
95162

96163
&:hover {
97164
transform: scale(1.1);
98-
background: rgba(250, 234, 95, 0.25);
165+
background: rgba(250, 234, 95, 0.4);
99166
}
100167

101168
&.chatOpen {
@@ -117,20 +184,26 @@
117184
justify-content: center;
118185
z-index: 9999;
119186
transition: all 0.2s;
187+
backdrop-filter: blur(12px);
120188

121189
&.reportOn {
122-
background: rgba(46, 167, 255, 0.15);
190+
background: rgba(46, 167, 255, 0.3);
123191
color: #2ea7ff;
124-
box-shadow: 0 0 8px rgba(46, 167, 255, 0.3);
192+
box-shadow:
193+
0 2px 10px rgba(0, 0, 0, 0.6),
194+
0 0 8px rgba(46, 167, 255, 0.3);
195+
border: 1px solid rgba(46, 167, 255, 0.35);
125196

126197
svg {
127198
animation: pulse-radio 1.5s ease-in-out infinite;
128199
}
129200
}
130201

131202
&.reportOff {
132-
background: rgba(255, 255, 255, 0.06);
133-
color: rgba(255, 255, 255, 0.3);
203+
background: rgba(255, 255, 255, 0.12);
204+
color: rgba(255, 255, 255, 0.5);
205+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.6);
206+
border: 1px solid rgba(255, 255, 255, 0.15);
134207
}
135208

136209
&:hover {
@@ -161,14 +234,14 @@
161234
border-radius: 50%;
162235
background: #faea5f;
163236
color: #121214;
164-
border: none;
237+
border: 2px solid rgba(255, 255, 255, 0.3);
165238
cursor: pointer;
166239
display: flex;
167240
align-items: center;
168241
justify-content: center;
169242
font-size: 20px;
170243
z-index: 9999;
171-
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
244+
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.6);
172245

173246
&:hover {
174247
transform: scale(1.05);

apps/webuiapps/src/components/Shell/index.tsx

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import {
1212
Shield,
1313
Newspaper,
1414
Radio,
15+
Video,
16+
VideoOff,
1517
type LucideIcon,
1618
} from 'lucide-react';
1719
import ChatPanel from '../ChatPanel';
@@ -49,17 +51,37 @@ const DESKTOP_APPS = getDesktopApps().map((app) => ({
4951
IconComp: ICON_MAP[app.icon] || Circle,
5052
}));
5153

52-
const DEFAULT_WALLPAPER =
54+
const VIDEO_WALLPAPER =
55+
'https://cdn.openroom.ai/public-cdn-s3-us-west-2/talkie-op-img/1609284623_1772622757413_1.mp4';
56+
57+
const STATIC_WALLPAPER =
5358
'https://cdn.openroom.ai/public-cdn-s3-us-west-2/talkie-op-img/image/437110625_1772619481913_Aoi_default_Commander_Room.jpg';
5459

60+
function isVideoUrl(url: string): boolean {
61+
try {
62+
const pathname = new URL(url).pathname.toLowerCase();
63+
return /\.(mp4|webm|mov|ogg)$/.test(pathname);
64+
} catch {
65+
return false;
66+
}
67+
}
68+
5569
const Shell: React.FC = () => {
5670
const [chatOpen, setChatOpen] = useState(false);
5771
const [reportEnabled, setReportEnabled] = useState(true);
5872
const [lang, setLang] = useState<'en' | 'zh'>('en');
73+
const [liveWallpaper, setLiveWallpaper] = useState(true);
5974
// eslint-disable-next-line @typescript-eslint/no-unused-vars
60-
const [wallpaper, setWallpaper] = useState(DEFAULT_WALLPAPER);
75+
const [wallpaper, setWallpaper] = useState(VIDEO_WALLPAPER);
6176
const windows = useWindows();
6277

78+
const activeWallpaper = liveWallpaper
79+
? wallpaper
80+
: isVideoUrl(wallpaper)
81+
? STATIC_WALLPAPER
82+
: wallpaper;
83+
const showVideo = liveWallpaper && isVideoUrl(wallpaper);
84+
6385
const handleToggleReport = useCallback(() => {
6486
setReportEnabled((prev) => {
6587
const next = !prev;
@@ -84,15 +106,18 @@ const Shell: React.FC = () => {
84106
<div
85107
className={styles.shell}
86108
style={
87-
wallpaper
109+
activeWallpaper && !showVideo
88110
? {
89-
backgroundImage: `url(${wallpaper})`,
111+
backgroundImage: `url(${activeWallpaper})`,
90112
backgroundSize: 'cover',
91113
backgroundPosition: 'center',
92114
}
93115
: undefined
94116
}
95117
>
118+
{showVideo && (
119+
<video className={styles.videoBg} src={wallpaper} autoPlay loop muted playsInline />
120+
)}
96121
{/* Desktop with app icons */}
97122
<div className={styles.desktop}>
98123
<div className={styles.iconGrid}>
@@ -126,6 +151,14 @@ const Shell: React.FC = () => {
126151
{/* Chat Panel */}
127152
{chatOpen && <ChatPanel onClose={() => setChatOpen(false)} />}
128153

154+
<button
155+
className={`${styles.liveWallpaperToggle} ${chatOpen ? styles.chatOpen : ''} ${liveWallpaper ? styles.liveOn : styles.liveOff}`}
156+
onClick={() => setLiveWallpaper((prev) => !prev)}
157+
title={liveWallpaper ? 'Live wallpaper: ON' : 'Live wallpaper: OFF'}
158+
>
159+
{liveWallpaper ? <Video size={16} /> : <VideoOff size={16} />}
160+
</button>
161+
129162
<button
130163
className={`${styles.langToggle} ${chatOpen ? styles.chatOpen : ''}`}
131164
onClick={handleToggleLang}

0 commit comments

Comments
 (0)