Skip to content

Commit 9f46837

Browse files
committed
feat(wall): layered wallpapers
1 parent ebc29fb commit 9f46837

File tree

10 files changed

+136
-59
lines changed

10 files changed

+136
-59
lines changed

changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
### features
66

77
- implement wallpaper downloads.
8+
- add new wallpaper type "layered" it allows create unique wallpaper via css.
9+
- new css variables added related to the date (hour, minute, day, month, year).
810

911
## [2.4.8]
1012

libs/core/src/state/theme/mod.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { newFromInvoke, newOnEvent } from "../../utils/State.ts";
1212
import { Widget } from "../widget/mod.ts";
1313
import { Settings } from "../settings/mod.ts";
1414
import { UIColors } from "../../system_state/ui_colors.ts";
15+
import { startDateCssVariables } from "./theming.ts";
1516

1617
export class ThemeList extends List<ITheme> {
1718
static getAsync(): Promise<ThemeList> {
@@ -139,4 +140,5 @@ export async function startThemingTool(): Promise<void> {
139140
await UIColors.onChange((colors) => colors.setAsCssVariables());
140141

141142
themes.applyToDocument(settings.activeThemes, settings.byTheme);
143+
startDateCssVariables();
142144
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
export function startDateCssVariables(): void {
2+
// Set initial values immediately
3+
updateDateCssVariables();
4+
5+
// Update every minute (60000ms) to avoid overhead from seconds
6+
setInterval(updateDateCssVariables, 60000);
7+
}
8+
9+
function updateDateCssVariables(): void {
10+
const now = new Date();
11+
const locale = navigator.language;
12+
13+
// Time values
14+
const hour = now.getHours(); // 0-23
15+
const minute = now.getMinutes(); // 0-59
16+
17+
// Date name values using Intl API
18+
const dayName = new Intl.DateTimeFormat(locale, { weekday: "long" }).format(now);
19+
const monthName = new Intl.DateTimeFormat(locale, { month: "long" }).format(now);
20+
21+
// Date numeric values
22+
const dayOfMonth = now.getDate(); // 1-31
23+
const monthNumber = now.getMonth() + 1; // 1-12
24+
const year = now.getFullYear(); // 2025, etc.
25+
26+
insertIntoStyleSheet({
27+
// Time variables
28+
"--date-hour": String(hour),
29+
"--date-minute": String(minute),
30+
// Date name variables (localized)
31+
"--date-day-name": dayName,
32+
"--date-month-name": monthName,
33+
// Date numeric variables
34+
"--date-day": String(dayOfMonth),
35+
"--date-month": String(monthNumber),
36+
"--date-year": String(year),
37+
});
38+
}
39+
40+
function getRuntimeStyleSheet(): HTMLStyleElement {
41+
const styleId = "slu-lib-date-variables";
42+
let styleElement = document.getElementById(styleId) as HTMLStyleElement;
43+
if (!styleElement) {
44+
styleElement = document.createElement("style");
45+
styleElement.id = styleId;
46+
styleElement.textContent = ":root {\n}";
47+
document.head.appendChild(styleElement);
48+
}
49+
return styleElement;
50+
}
51+
52+
function insertIntoStyleSheet(obj: Record<string, string>): void {
53+
const sheet = getRuntimeStyleSheet();
54+
const lines = sheet.textContent!.split("\n");
55+
lines.pop(); // remove the closing brace
56+
57+
for (const [key, value] of Object.entries(obj)) {
58+
const old = lines.findIndex((line) => line.startsWith(key));
59+
if (old !== -1) {
60+
lines[old] = `${key}: ${value};`;
61+
} else {
62+
lines.push(`${key}: ${value};`);
63+
}
64+
}
65+
66+
lines.push("}");
67+
sheet.textContent = lines.join("\n");
68+
}

libs/core/src/state/wallpaper/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ pub struct Wallpaper {
2222
pub filename: Option<String>,
2323
#[serde(alias = "thumbnail_filename")]
2424
pub thumbnail_filename: Option<String>,
25+
/// Only used if the wallpaper type is `Layered`.\
26+
/// Custom css that will be applied only on this wallpaper.
27+
pub css: Option<String>,
2528
}
2629

2730
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]
@@ -31,6 +34,8 @@ pub enum WallpaperKind {
3134
Image,
3235
#[serde(alias = "video")]
3336
Video,
37+
#[serde(alias = "layered")]
38+
Layered,
3439
/// used for wallpapers created before v2.4.9, will be changed on sanitization
3540
#[default]
3641
Unsupported,
@@ -136,6 +141,7 @@ impl Wallpaper {
136141
} else {
137142
None
138143
},
144+
css: None,
139145
};
140146
wallpaper.save()?;
141147

libs/widgets-shared/components/Wallpaper/index.module.css

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,28 @@
1818
height: 100%;
1919
}
2020
}
21+
22+
.defaultWallpaper {
23+
> :global(.bg-layers) {
24+
> :global(.bg-layer-1) {
25+
background: linear-gradient(
26+
to bottom,
27+
var(--config-accent-darkest-color),
28+
var(--config-accent-color),
29+
var(--config-accent-darkest-color)
30+
);
31+
}
32+
33+
> :global(.bg-layer-2) {
34+
background: linear-gradient(
35+
to bottom,
36+
var(--config-accent-dark-color),
37+
var(--config-accent-lightest-color)
38+
);
39+
mask-image: url("data:image/svg+xml,%3Csvg width='100%25' height='100%25' id='svg' viewBox='0 0 1440 790' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M 0,800 C 0,800 0,160 0,160 C 104.43062200956939,139.54066985645932 208.86124401913878,119.08133971291866 297,128 C 385.1387559808612,136.91866028708134 456.9856459330142,175.2153110047847 540,178 C 623.0143540669858,180.7846889952153 717.1961722488038,148.05741626794256 816,150 C 914.8038277511962,151.94258373205744 1018.2296650717703,188.555023923445 1123,196 C 1227.7703349282297,203.444976076555 1333.8851674641148,181.7224880382775 1440,160 C 1440,160 1440,800 1440,800 Z' stroke='none' stroke-width='0' fill='%23FFFFFF' fill-opacity='0.265' class='path-0' style='transition-property: all; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); transition-duration: 300ms; transition-delay: 150ms;'%3E%3C/path%3E%3Cpath d='M 0,800 C 0,800 0,320 0,320 C 120.88995215311004,343.9138755980861 241.7799043062201,367.82775119617224 335,373 C 428.2200956937799,378.17224880382776 493.77033492822966,364.60287081339715 576,343 C 658.2296650717703,321.39712918660285 757.1387559808612,291.76076555023917 869,287 C 980.8612440191388,282.23923444976083 1105.6746411483252,302.354066985646 1203,312 C 1300.3253588516748,321.645933014354 1370.1626794258373,320.822966507177 1440,320 C 1440,320 1440,800 1440,800 Z' stroke='none' stroke-width='0' fill='%23FFFFFF' fill-opacity='0.4' class='path-1' style='transition-property: all; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); transition-duration: 300ms; transition-delay: 150ms;'%3E%3C/path%3E%3Cpath d='M 0,800 C 0,800 0,480 0,480 C 116.71770334928229,477.07177033492826 233.43540669856458,474.14354066985646 330,462 C 426.5645933014354,449.85645933014354 502.97607655502395,428.49760765550246 593,421 C 683.023923444976,413.50239234449754 786.6602870813399,419.8660287081339 891,432 C 995.3397129186601,444.1339712918661 1100.382775119617,462.0382775119617 1192,471 C 1283.617224880383,479.9617224880383 1361.8086124401916,479.98086124401914 1440,480 C 1440,480 1440,800 1440,800 Z' stroke='none' stroke-width='0' fill='%23FFFFFF' fill-opacity='0.53' class='path-2' style='transition-property: all; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); transition-duration: 300ms; transition-delay: 150ms;'%3E%3C/path%3E%3Cpath d='M 0,800 C 0,800 0,640 0,640 C 117.33014354066987,611.6746411483253 234.66028708133973,583.3492822966507 339,587 C 443.33971291866027,590.6507177033493 534.688995215311,626.2775119617224 621,629 C 707.311004784689,631.7224880382776 788.5837320574162,601.5406698564593 883,614 C 977.4162679425838,626.4593301435407 1084.9760765550238,681.5598086124402 1180,693 C 1275.0239234449762,704.4401913875598 1357.5119617224882,672.22009569378 1440,640 C 1440,640 1440,800 1440,800 Z' stroke='none' stroke-width='0' fill='%23FFFFFF' fill-opacity='1' class='path-3' style='transition-property: all; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); transition-duration: 300ms; transition-delay: 150ms;'%3E%3C/path%3E%3C/svg%3E");
40+
mask-repeat: no-repeat;
41+
mask-position: bottom;
42+
mask-size: cover;
43+
}
44+
}
45+
}

libs/widgets-shared/components/Wallpaper/index.tsx

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
import { useSignal } from "@preact/signals";
2-
import {
3-
SUPPORTED_IMAGE_WALLPAPER_EXTENSIONS,
4-
SUPPORTED_VIDEO_WALLPAPER_EXTENSIONS,
5-
WallpaperConfiguration,
6-
} from "@seelen-ui/lib";
7-
import type { Wallpaper, WallpaperInstanceSettings } from "@seelen-ui/lib/types";
2+
import { WallpaperConfiguration } from "@seelen-ui/lib";
3+
import { type Wallpaper, type WallpaperInstanceSettings, WallpaperKind } from "@seelen-ui/lib/types";
84
import { cx } from "@shared/styles";
95
import { convertFileSrc } from "@tauri-apps/api/core";
106
import type { ComponentChildren } from "preact";
@@ -37,22 +33,20 @@ export function Wallpaper(props: BaseProps) {
3733
}
3834

3935
let element: ComponentChildren = null;
40-
if (
41-
definition &&
42-
SUPPORTED_IMAGE_WALLPAPER_EXTENSIONS.some((ext) => definition.filename?.toLowerCase()?.endsWith(ext))
43-
) {
44-
element = <ImageWallpaper {...props} onLoad={onLoad} definition={definition} config={config} />;
45-
}
46-
47-
if (
48-
definition &&
49-
SUPPORTED_VIDEO_WALLPAPER_EXTENSIONS.some((ext) => definition.filename?.toLowerCase()?.endsWith(ext))
50-
) {
51-
element = <VideoWallpaper {...props} onLoad={onLoad} definition={definition} config={config} />;
52-
}
5336

54-
if (!element) {
55-
element = <ThemedWallpaper {...props} onLoad={onLoad} config={config} />;
37+
switch (definition?.type) {
38+
case WallpaperKind.Image:
39+
element = <ImageWallpaper {...props} onLoad={onLoad} definition={definition} config={config} />;
40+
break;
41+
case WallpaperKind.Video:
42+
element = <VideoWallpaper {...props} onLoad={onLoad} definition={definition} config={config} />;
43+
break;
44+
case WallpaperKind.Layered:
45+
element = <ThemedWallpaper {...props} onLoad={onLoad} definition={definition} config={config} />;
46+
break;
47+
default:
48+
element = <ThemedWallpaper {...props} onLoad={onLoad} />;
49+
break;
5650
}
5751

5852
return (
@@ -76,13 +70,26 @@ export function Wallpaper(props: BaseProps) {
7670
);
7771
}
7872

79-
export function ThemedWallpaper({ config, onLoad }: Pick<DefinedWallProps, "config" | "onLoad">) {
73+
export function ThemedWallpaper({
74+
definition,
75+
config,
76+
onLoad,
77+
}: Pick<BaseProps, "definition" | "config" | "onLoad">) {
8078
useEffect(() => {
8179
onLoad?.();
8280
}, []);
8381

82+
if (!definition || !config) {
83+
return (
84+
<div className={cx(cs.wallpaper, cs.defaultWallpaper)}>
85+
<BackgroundByLayersV2 />
86+
</div>
87+
);
88+
}
89+
8490
return (
85-
<div className={cx(cs.wallpaper, "themed-wallpaper")} style={getWallpaperStyles(config)}>
91+
<div id={definition.id} className={cs.wallpaper} style={getWallpaperStyles(config)}>
92+
<style>{`@scope { ${definition.css || ""} }`}</style>
8693
<BackgroundByLayersV2 />
8794
</div>
8895
);

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/static/themes/default/styles/fancy-toolbar.scss

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -150,18 +150,6 @@
150150
// &[data-there-is-maximized-on-background="true"] {} (this attribute exist but is unused)
151151
// &[data-focused-is-maximized="true"] {} (this attribute exist but is unused)
152152
// &[data-focused-is-overlay="true"] {} (this attribute exist but is unused)
153-
154-
&[data-dynamic-color="true"] {
155-
// --color-focused-app-foreground (this var exist but is unused)
156-
color: var(--color-maximized-on-bg-foreground, var(--color-persist-gray-100));
157-
158-
> .bg-layers {
159-
> .bg-layer-2 {
160-
// --color-focused-app-background (this var exist but is unused)
161-
background-color: var(--color-maximized-on-bg-background, var(--color-persist-gray-900));
162-
}
163-
}
164-
}
165153
}
166154

167155
.ft-bar-item {

src/static/themes/default/styles/wallpaper-manager.css

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,3 @@
1-
.themed-wallpaper {
2-
background: linear-gradient(
3-
to bottom,
4-
var(--config-accent-darkest-color),
5-
var(--config-accent-color),
6-
var(--config-accent-darkest-color)
7-
);
8-
9-
> .bg-layers {
10-
> .bg-layer-1 {
11-
background: linear-gradient(
12-
to bottom,
13-
var(--config-accent-dark-color),
14-
var(--config-accent-lightest-color)
15-
);
16-
mask-image: url("data:image/svg+xml,%3Csvg width='100%25' height='100%25' id='svg' viewBox='0 0 1440 790' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M 0,800 C 0,800 0,160 0,160 C 104.43062200956939,139.54066985645932 208.86124401913878,119.08133971291866 297,128 C 385.1387559808612,136.91866028708134 456.9856459330142,175.2153110047847 540,178 C 623.0143540669858,180.7846889952153 717.1961722488038,148.05741626794256 816,150 C 914.8038277511962,151.94258373205744 1018.2296650717703,188.555023923445 1123,196 C 1227.7703349282297,203.444976076555 1333.8851674641148,181.7224880382775 1440,160 C 1440,160 1440,800 1440,800 Z' stroke='none' stroke-width='0' fill='%23FFFFFF' fill-opacity='0.265' class='path-0' style='transition-property: all; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); transition-duration: 300ms; transition-delay: 150ms;'%3E%3C/path%3E%3Cpath d='M 0,800 C 0,800 0,320 0,320 C 120.88995215311004,343.9138755980861 241.7799043062201,367.82775119617224 335,373 C 428.2200956937799,378.17224880382776 493.77033492822966,364.60287081339715 576,343 C 658.2296650717703,321.39712918660285 757.1387559808612,291.76076555023917 869,287 C 980.8612440191388,282.23923444976083 1105.6746411483252,302.354066985646 1203,312 C 1300.3253588516748,321.645933014354 1370.1626794258373,320.822966507177 1440,320 C 1440,320 1440,800 1440,800 Z' stroke='none' stroke-width='0' fill='%23FFFFFF' fill-opacity='0.4' class='path-1' style='transition-property: all; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); transition-duration: 300ms; transition-delay: 150ms;'%3E%3C/path%3E%3Cpath d='M 0,800 C 0,800 0,480 0,480 C 116.71770334928229,477.07177033492826 233.43540669856458,474.14354066985646 330,462 C 426.5645933014354,449.85645933014354 502.97607655502395,428.49760765550246 593,421 C 683.023923444976,413.50239234449754 786.6602870813399,419.8660287081339 891,432 C 995.3397129186601,444.1339712918661 1100.382775119617,462.0382775119617 1192,471 C 1283.617224880383,479.9617224880383 1361.8086124401916,479.98086124401914 1440,480 C 1440,480 1440,800 1440,800 Z' stroke='none' stroke-width='0' fill='%23FFFFFF' fill-opacity='0.53' class='path-2' style='transition-property: all; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); transition-duration: 300ms; transition-delay: 150ms;'%3E%3C/path%3E%3Cpath d='M 0,800 C 0,800 0,640 0,640 C 117.33014354066987,611.6746411483253 234.66028708133973,583.3492822966507 339,587 C 443.33971291866027,590.6507177033493 534.688995215311,626.2775119617224 621,629 C 707.311004784689,631.7224880382776 788.5837320574162,601.5406698564593 883,614 C 977.4162679425838,626.4593301435407 1084.9760765550238,681.5598086124402 1180,693 C 1275.0239234449762,704.4401913875598 1357.5119617224882,672.22009569378 1440,640 C 1440,640 1440,800 1440,800 Z' stroke='none' stroke-width='0' fill='%23FFFFFF' fill-opacity='1' class='path-3' style='transition-property: all; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); transition-duration: 300ms; transition-delay: 150ms;'%3E%3C/path%3E%3C/svg%3E");
17-
mask-repeat: no-repeat;
18-
mask-position: bottom;
19-
mask-size: cover;
20-
}
21-
}
22-
}
23-
241
.wallpaper-container {
252
display: none;
263
overflow: hidden;

src/ui/react/settings/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { createRoot } from "react-dom/client";
44
import { I18nextProvider } from "react-i18next";
55
import { Provider } from "react-redux";
66
import { HashRouter } from "react-router";
7+
import { startThemingTool } from "@seelen-ui/lib";
78

89
import { LoadSettingsToStore, registerStoreEvents, store } from "./modules/shared/store/infra.ts";
910

@@ -21,6 +22,7 @@ getCurrentWebviewWindow().show();
2122
await LoadSettingsToStore();
2223
await registerStoreEvents();
2324
await loadTranslations();
25+
await startThemingTool();
2426

2527
const container = getRootContainer();
2628
createRoot(container).render(

0 commit comments

Comments
 (0)