diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 187a864..f9204c8 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -81,24 +81,13 @@ jobs:
- name: Create Release
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
- id: create_release
- uses: actions/create-release@v1
+ uses: softprops/action-gh-release@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- tag_name: ${{ github.ref }}
- release_name: Release ${{ github.ref_name }}
+ tag_name: ${{ github.ref_name }}
+ name: Release ${{ github.ref_name }}
body: ${{ env.CHANGELOG }}
draft: false
prerelease: false
-
- - name: Upload Release Asset
- if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
- uses: actions/upload-release-asset@v1
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- with:
- upload_url: ${{ steps.create_release.outputs.upload_url }}
- asset_path: ./${{ env.ZIP_NAME }}
- asset_name: ${{ env.ZIP_NAME }}
- asset_content_type: application/zip
+ files: ${{ env.ZIP_NAME }}
diff --git a/komari-theme.json b/komari-theme.json
index 672e0ec..a3b8720 100644
--- a/komari-theme.json
+++ b/komari-theme.json
@@ -1,9 +1,42 @@
{
- "name": "Komari Theme PurCarte",
+ "name": "Komari Theme PurCart",
"short": "PurCarte",
"description": "A frosted glass theme for Komari",
- "version": "0.1.2",
+ "version": "0.1.3",
"author": "Montia & Gemini",
"url": "https://github.com/Montia37/Komari-theme-purcarte",
- "preview": "preview.png"
+ "preview": "preview.png",
+ "configuration": {
+ "type": "managed",
+ "data": [
+ {
+ "name": "背景",
+ "type": "title"
+ },
+ {
+ "key": "backgroundImage",
+ "name": "背景图片链接",
+ "type": "string",
+ "required": false,
+ "default": "https://i.yon.li/w/682f73d97eade.png",
+ "help": "多张图片请以英文逗号分隔(eg:https://img.com/1.png,https://test.com/2.jpg)"
+ },
+ {
+ "key": "switchTime",
+ "name": "切换时间(秒)",
+ "type": "number",
+ "required": false,
+ "default": 10,
+ "help": "背景图片切换的时间间隔,单位为秒(仅设置多张图片时生效)"
+ },
+ {
+ "key": "transition",
+ "name": "背景切换过渡效果",
+ "type": "string",
+ "required": false,
+ "default": "background-image 0.8s ease-in-out",
+ "help": "CSS 过渡效果,用于背景图片切换时的动画效果(仅设置多张图片时生效)"
+ }
+ ]
+ }
}
\ No newline at end of file
diff --git a/src/components/sections/Background.ts b/src/components/sections/Background.ts
new file mode 100644
index 0000000..60f1d79
--- /dev/null
+++ b/src/components/sections/Background.ts
@@ -0,0 +1,99 @@
+import { useEffect, useState, useMemo } from "react";
+import { BACKGROUND } from "@/config/default";
+import type { PublicInfo } from "@/types/node.d";
+
+/**
+ * 动态背景组件
+ * 根据设定的时间间隔自动切换背景图片
+ * 并预加载所有图片以提高用户体验
+ * 支持自定义过渡效果和切换时间
+ */
+interface ThemeSettings {
+ backgroundImage?: string; // 逗号分隔的背景图片URL列表
+ switchTime?: number; // 背景切换时间间隔(秒)
+ transition?: string; // CSS过渡效果
+}
+
+interface BackgroundProps {
+ publicSettings: PublicInfo;
+}
+
+function DynamicPseudoBackground({ publicSettings }: BackgroundProps) {
+ const theme = (publicSettings?.theme_settings as ThemeSettings) || {};
+
+ // 使用 useMemo 缓存背景图片列表,避免每次渲染时重新计算
+ const imageList = useMemo(() => {
+ return theme.backgroundImage
+ ? theme.backgroundImage.split(",").map((url) => url.trim())
+ : [BACKGROUND.backgroundImage];
+ }, [theme.backgroundImage]);
+
+ // 将切换时间从秒转换为毫秒
+ const switchTime = useMemo(() => {
+ return (theme.switchTime || BACKGROUND.switchTime) * 1000;
+ }, [theme.switchTime]);
+
+ const transition = useMemo(() => {
+ return theme.transition || BACKGROUND.transition;
+ }, [theme.transition]);
+
+ const [currentImageIndex, setCurrentImageIndex] = useState(0);
+ const currentImageUrl = imageList[currentImageIndex];
+
+ // 预加载指定的图片
+ const preloadImage = (url: string) => {
+ if (!url) return;
+ const img = new Image();
+ img.src = url;
+ };
+
+ // 预加载所有图片,只在组件初始化或图片列表变化时执行一次
+ useEffect(() => {
+ // 只有当有多张图片时才设置过渡效果
+ if (imageList.length > 1) {
+ document.body.style.setProperty(
+ "--body-background-transition",
+ transition
+ );
+
+ // 预加载所有图片以提高用户体验
+ imageList.forEach((url) => {
+ preloadImage(url);
+ });
+ }
+
+ // 组件卸载时清理
+ return () => {
+ document.body.style.removeProperty("--body-background-transition");
+ };
+ }, [imageList, transition]);
+
+ // 背景切换逻辑
+ useEffect(() => {
+ // 当当前图片URL变化时,更新CSS变量
+ document.body.style.setProperty(
+ "--body-background-url",
+ `url(${currentImageUrl})`
+ );
+
+ // 只有当有多张图片时才设置定时器进行轮换
+ let intervalId: number | undefined;
+ if (imageList.length > 1) {
+ intervalId = window.setInterval(() => {
+ setCurrentImageIndex((prevIndex) => (prevIndex + 1) % imageList.length);
+ }, switchTime);
+ }
+
+ // 清理函数,组件卸载或依赖项变化时执行
+ return () => {
+ if (intervalId) {
+ clearInterval(intervalId);
+ }
+ };
+ }, [currentImageUrl, imageList, switchTime]);
+
+ // 此组件不渲染任何可见内容
+ return null;
+}
+
+export default DynamicPseudoBackground;
diff --git a/src/components/sections/Flag.tsx b/src/components/sections/Flag.tsx
index b6b7353..50265d9 100644
--- a/src/components/sections/Flag.tsx
+++ b/src/components/sections/Flag.tsx
@@ -93,6 +93,7 @@ const Flag = React.memo(({ flag, size }: FlagProps) => {
src={imgSrc}
alt={altText}
style={{ width: "100%", height: "100%", objectFit: "contain" }}
+ loading="lazy"
/>
);
diff --git a/src/config/default.ts b/src/config/default.ts
new file mode 100644
index 0000000..63f36f8
--- /dev/null
+++ b/src/config/default.ts
@@ -0,0 +1,5 @@
+export const BACKGROUND = {
+ backgroundImage: "https://i.yon.li/w/682f73d97eade.png",
+ switchTime: 10, // 10 seconds
+ transition: "background-image 0.8s ease-in-out", // CSS transition for background change
+};
diff --git a/src/index.css b/src/index.css
index 44c98d3..cad5fd8 100644
--- a/src/index.css
+++ b/src/index.css
@@ -79,6 +79,9 @@
/* Frosted Glass Variables */
--frosted-bg-light: rgba(255, 255, 255, 0.1);
--frosted-border-light: rgba(255, 255, 255, 0.2);
+
+ --body-background-url: url("");
+ --body-background-transition: background-image 0.8s ease-in-out;
}
.dark {
@@ -132,6 +135,19 @@
}
}
+/* 背景图片伪元素 */
+body::before {
+ content: "";
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: -1;
+ background: var(--body-background-url) center/cover no-repeat;
+ transition: var(--body-background-transition);
+}
+
.striped-bg-red-translucent-diagonal {
background-image: linear-gradient(
45deg,
diff --git a/src/main.tsx b/src/main.tsx
index 2e78076..597b1eb 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -5,6 +5,7 @@ import "./index.css";
import "@radix-ui/themes/styles.css";
import { Theme } from "@radix-ui/themes";
import { Header } from "@/components/sections/Header";
+import DynamicPseudoBackground from "@/components/sections/Background";
import { useTheme } from "@/hooks/useTheme";
import { NodeDataProvider } from "@/contexts/NodeDataContext";
import { LiveDataProvider } from "@/contexts/LiveDataContext";
@@ -45,6 +46,10 @@ const App = () => {
appearance="inherit"
scaling="110%"
style={{ backgroundColor: "transparent" }}>
+ {/* 使用动态背景组件 */}
+ {publicSettings && (
+