diff --git a/.gitignore b/.gitignore index 4e66d8e..169718c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ node_modules/ *.code-workspace .vscode/ + + +assets/themes/liora diff --git a/assets/scripts/index.js b/assets/scripts/index.js index 5f4502e..46440c6 100644 --- a/assets/scripts/index.js +++ b/assets/scripts/index.js @@ -1,8 +1,4 @@ -/* global Alpine, getWebsiteConfig, Typed, swal, renderMarkdown */ - -// 获取网站配置 -const config = getWebsiteConfig(); -config.init(); +/* global config, Alpine, getWebsiteConfig, Typed, swal, renderMarkdown */ // 初始化 Alpine document.addEventListener("alpine:init", () => { diff --git a/assets/scripts/theme-loader.js b/assets/scripts/theme-loader.js new file mode 100644 index 0000000..dd0ebf4 --- /dev/null +++ b/assets/scripts/theme-loader.js @@ -0,0 +1,263 @@ +/* global config */ + +var themePath; +var metaData; + +class ThemeManager { + constructor() { + this.parse(); + } + + // 解析主题 + parse() { + // 构建主题目录 + themePath = window.location.origin + "/assets/themes/" + config.content.theme.theme; + console.log("%c[I]%c " + `Theme Path: ${themePath}`, "background-color: #00896c;", ""); + + // 使用 XML 获取主题的元数据 + try { + const xhr = new XMLHttpRequest(); + xhr.open("GET", themePath + "/theme.json", false); // 使用同步请求 + xhr.send(); + + if (xhr.status >= 200 && xhr.status < 300) { + metaData = JSON.parse(xhr.responseText); + } else { + throw new Error("无法获取主题元数据"); + } + } catch (error) { + console.error("%c[E]%c " + `获取主题元数据失败: ${error}`, "background-color: #cb1b45;", ""); + throw new Error("获取主题元数据失败,无法继续执行操作"); + } + + console.log("%c[I]%c " + `主题元数据: ${JSON.stringify(metaData)}`, "background-color: #00896c;", ""); + + // 检查元数据是否合法 + if (metaData.id && metaData.name && metaData.version && metaData.files.styles && metaData.files.scripts && metaData.colors) { + // 输出欢迎语 + console.group("%c主题解析成功!%c" + `${metaData.name} (${metaData.id})`, "padding: 5px; border-radius: 6px 0 0 6px; background-color: #00896c; color: #ffffff;", "padding: 5px; border-radius: 0 6px 6px 0; background-color: #986db2; color: #ffffff;"); + console.log("%cID:%c" + `${metaData.id}`, "padding: 5px; border-radius: 6px 0 0 6px; background-color: #986db2; color: #ffffff;", "padding: 5px; border-radius: 0 6px 6px 0; background-color: #b5495b; color: #ffffff;"); + console.log("%cName:%c" + `${metaData.name}`, "padding: 5px; border-radius: 6px 0 0 6px; background-color: #986db2; color: #ffffff;", "padding: 5px; border-radius: 0 6px 6px 0; background-color: #b5495b; color: #ffffff;"); + console.log("%cVersion:%c" + `${metaData.version}`, "padding: 5px; border-radius: 6px 0 0 6px; background-color: #986db2; color: #ffffff;", "padding: 5px; border-radius: 0 6px 6px 0; background-color: #b5495b; color: #ffffff;"); + console.log("%cRepo:%c" + `${metaData.repo}`, "padding: 5px; border-radius: 6px 0 0 6px; background-color: #010101; color: #ffffff;", "padding: 5px; border-radius: 0 6px 6px 0; background-color: #ff9901; color: #ffffff;"); + console.groupEnd(); + + return metaData; + } else { + console.error("%c[E]%c " + `主题解析失败,元数据存在问题`, "background-color: #cb1b45;", ""); + throw new Error("主题解析失败,无法继续执行操作"); + } + } + + // 加载主题 + load() { + // 创建一个数组,用来存放生成的 Style 外部资源链接 HTML + var styleLinks; + + // 解析基本样式 URL 并赋值给数组 + styleLinks = metaData.files.styles + .map((key) => { + if (key) { + // 创建 标签 + return ``; + } + console.error("%c[E]%c " + `主题 ${key} 样式 Tag 生成失败,元数据可能存在问题`, "background-color: #cb1b45;", ""); + throw new Error("主题样式 Tag 生成失败,无法继续执行操作"); + }) + .filter(Boolean); // 过滤掉无效的值 + + // 解析配色方案样式 URL 并插入数组 + metaData.colors.index + .map((key) => { + let targetColor = localStorage.getItem("theme.color"); // 存储目标配色方案的变量 + + // 如果目标配色方案为保留关键字“!autoSwitch”(自动切换配色方案),将其改为实际需要加载的配色方案 + if (targetColor === "!autoSwitch") { + console.log("%c[I]%c " + `当前配色方案为 !autoSwitch 自动切换,用户的浏览器深色模式启用状态为: ${window.matchMedia("(prefers-color-scheme: dark)").matches}`, "background-color: #00896c;", ""); + if (window.matchMedia("(prefers-color-scheme: dark)").matches) { + targetColor = config.content.theme.colors.autoSwitch.dark; + } else { + targetColor = config.content.theme.colors.autoSwitch.light; + } + } + + const styles = metaData.colors.list[key].files.styles; // 获取对应的 styles + + // 根据目标配色方案决定是否生成标签 + if (styles && targetColor === key) { + // 如果 styles 是数组,生成多个 link 标签 + return styles.map((file) => ``).join(""); // 将生成的所有 link 标签拼接成字符串 + } else if (localStorage.getItem("theme.color") !== key) { + console.log("%c[I]%c " + `跳过了生成 ${key} 配色方案样式标签的步骤,因为 key 的值不符合用户设置`, "background-color: #00896c;", ""); + return; + } + console.error("%c[E]%c " + `配色方案 ${key} 样式 Tag 生成失败,元数据可能存在问题`, "background-color: #cb1b45;", ""); + throw new Error("配色方案样式 Tag 生成失败,无法继续执行操作"); + }) + .filter(Boolean) // 过滤掉无效的值 + .forEach((linkTags) => { + styleLinks.push(linkTags); // 将生成的 link 标签插入到数组中 + }); + + console.log("%c[I]%c " + `待插入的 Style 外部资源链接: ${styleLinks}`, "background-color: #00896c;", ""); + + // 创建一个数组,用来存放生成的 Script 外部资源链接 HTML + var scriptLinks; + + // 解析基本脚本 URL 并赋值给数组 + scriptLinks = metaData.files.scripts + .map((key) => { + if (key) { + // 创建 `; + } + console.error("%c[E]%c " + `主题 ${key} 脚本 Tag 生成失败,元数据可能存在问题`, "background-color: #cb1b45;", ""); + throw new Error("主题脚本 Tag 生成失败,无法继续执行操作"); + }) + .filter(Boolean); // 过滤掉无效的值 + + // 解析配色方案脚本 URL 并插入数组 + metaData.colors.index + .map((key) => { + let targetColor = localStorage.getItem("theme.color"); // 存储目标配色方案的变量 + + // 如果目标配色方案为保留关键字“!autoSwitch”(自动切换配色方案),将其改为实际需要加载的配色方案 + if (targetColor === "!autoSwitch") { + console.log("%c[I]%c " + `当前配色方案为 !autoSwitch 自动切换,用户的浏览器深色模式启用状态为: ${window.matchMedia("(prefers-color-scheme: dark)").matches}`, "background-color: #00896c;", ""); + if (window.matchMedia("(prefers-color-scheme: dark)").matches) { + targetColor = config.content.theme.colors.autoSwitch.dark; + } else { + targetColor = config.content.theme.colors.autoSwitch.light; + } + } + + const scripts = metaData.colors.list[key].files.scripts; // 获取对应的 scripts + + // 根据目标配色方案决定是否生成标签 + if (scripts && targetColor === key) { + // 如果 scripts 是数组,生成多个 script 标签 + return scripts.map((file) => ``).join(""); // 将生成的所有 link 标签拼接成字符串 + } else if (localStorage.getItem("theme.color") !== key) { + console.log("%c[I]%c " + `跳过了生成 ${key} 配色方案脚本标签的步骤,因为 key 的值不符合用户设置`, "background-color: #00896c;", ""); + return; + } + console.error("%c[E]%c " + `配色方案 ${key} 脚本 Tag 生成失败,元数据可能存在问题`, "background-color: #cb1b45;", ""); + throw new Error("配色方案脚本 Tag 生成失败,无法继续执行操作"); + }) + .filter(Boolean) // 过滤掉无效的值 + .forEach((linkTags) => { + scriptLinks.push(linkTags); // 将生成的 link 标签插入到数组中 + }); + + console.log("%c[I]%c " + `待插入的 Script 外部资源链接: ${scriptLinks}`, "background-color: #00896c;", ""); + + // 拼接 styleLinks 和 scriptLinks + const resTag = [...styleLinks, ...scriptLinks]; + + // 将生成的外部资源链接插入到 theme 元素中 + document.querySelector("theme").innerHTML = resTag.join(""); + } + + // 设置配色方案 + setColor(colorId) { + if (colorId === localStorage.getItem("theme.color")) { + console.warn("%c[W]%c " + `当前配色方案已是 ${colorId},与其白白重载一次,不如我现在就中断更改`, "background-color: #e98b2a;", ""); + } else { + // 检查索引中是否存在主题 + // 保留关键字 !autoSwitch 可以不需要在索引中存在 + if (metaData.colors.index.includes(colorId) || colorId === "!autoSwitch") { + try { + localStorage.setItem("theme.color", colorId); + + // 重新加载主题 + themeManager.load(); + + console.log("%c[I]%c " + `配色方案已更改为: ${colorId}`, "background-color: #00896c;", ""); + } catch (error) { + console.error("%c[E]%c " + `无法将配色方案更改为 ${colorId}: ${error}`, "background-color: #cb1b45;", ""); + throw new Error("配色方案更改失败: ", error); + } + } else { + console.error("%c[E]%c " + `无法将配色方案更改为 ${colorId},因为未在主题配色方案索引中匹配到传入的值`, "background-color: #cb1b45;", ""); + throw new Error("配色方案更改失败,未在主题配色方案索引中匹配到传入的值"); + } + + // 加载配色方案设置的选中效果 + loadThemeSelEff(); + } + } +} + +// 加载配色方案设置的选中效果 +function loadThemeSelEff() { + // 如果存在已加载的选中效果则清除它 + if (document.querySelector(".theme-item.enable")) { + document.querySelector(".theme-item.enable").setAttribute("class", "theme-item"); + } + + // 插入新的选中效果类 + document.getElementById(`theme-item-${localStorage.getItem("theme.color")}`).setAttribute("class", "theme-item enable"); +} + +// 创建 ThemeManager 实例 +const themeManager = new ThemeManager(); + +document.addEventListener("DOMContentLoaded", () => { + // 如果第一次访问,将配色方案设置为默认值 + if (localStorage.getItem("theme.color") === null) { + themeManager.setColor(config.content.theme.colors.default); + } else { + // 否则正常加载主题 + themeManager.load(); + } + + /* 根据可用配色方案生成设置按钮 */ + + // 获取 .themes 元素 + const themesElement = document.querySelector(".primary-container > .left-area > .cards > .card-item > .content > .settings-item > .themes"); + + // 创建一个数组,用来存放生成的按钮 HTML + const themeButtons = config.content.theme.colors.enable + .map((key) => { + let displayName; + let icon; + let color; + let background; + + if (key === "!autoSwitch") { + console.log("%c[I]%c " + `Website config enabled !autoSwitch`, "background-color: #00896c;", ""); + + displayName = config.content.theme.colors.autoSwitch.displayName; // 获取对应的 displayName + icon = config.content.theme.colors.autoSwitch.icon.icon; // 获取对应的 icon + color = config.content.theme.colors.autoSwitch.icon.color; // 获取对应的 color + background = config.content.theme.colors.autoSwitch.icon.background; // 获取对应的 background + } else { + displayName = metaData.colors.list[key].displayName; // 获取对应的 displayName + icon = metaData.colors.list[key].icon.icon; // 获取对应的 icon + color = metaData.colors.list[key].icon.color; // 获取对应的 color + background = metaData.colors.list[key].icon.background; // 获取对应的 background + } + + if (displayName && icon && color && background) { + // 创建
标签 + return ` +
+ + ${displayName} +
+ `; + } else { + console.error("%c[E]%c " + `配色方案 ${key} 的设置按钮生成失败,主题元数据的配色方案信息 (${displayName}, ${icon}, ${color}, ${background}) 不满足条件,元数据可能存在问题`, "background-color: #cb1b45;", ""); + } + return ""; + }) + .filter(Boolean); // 过滤掉无效的值 + + // 将生成的按钮插入到 .social-icons 元素中 + themesElement.innerHTML = themeButtons.join(""); + + // 加载配色方案设置的选中效果 + loadThemeSelEff(); + + /* 根据可用配色方案生成设置按钮 End */ +}); diff --git a/assets/scripts/utils.js b/assets/scripts/utils.js index 9dd1904..64c75cc 100644 --- a/assets/scripts/utils.js +++ b/assets/scripts/utils.js @@ -22,6 +22,10 @@ function getWebsiteConfig() { }; } +// 获取网站配置 +const config = getWebsiteConfig(); +config.init(); + // Markdown 渲染器 function renderMarkdown() { // 获取页面中的所有 .markdown-content 元素 diff --git a/assets/styles/index.css b/assets/styles/index.css index 9bd9311..d731bac 100644 --- a/assets/styles/index.css +++ b/assets/styles/index.css @@ -44,7 +44,7 @@ body { .page-head { display: flex; justify-content: center; - align-items: center; + /* align-items: center; */ /* [^1] 开这玩意儿实现打字标题居中会导致页面跳动,我也不知道这是为甚么 TAT */ width: 100%; height: 320px; background-color: #49b1f5; @@ -56,7 +56,7 @@ body { .page-head > .title, .page-head > .typed-cursor { - margin-top: 32px; + margin-top: 160px; /* [^1] 所以此处使用上边距实现居中,反正能跑就行( */ font-size: 32px; font-weight: bold; color: #ffffff; @@ -160,6 +160,64 @@ body { .primary-container > .left-area > .cards > .card-item#hitokoto > .content a#hitokoto-text { color: unset; } + +/* - 浏览设置 */ +.primary-container > .left-area > .cards > .card-item > .content > .settings-item { + margin-top: 8px; + padding: 6px 10px; + border-radius: 12px; + box-sizing: border-box; + background-color: #c0e5ff; +} + +/* 标题 */ +.primary-container > .left-area > .cards > .card-item > .content > .settings-item > .title { + display: inline-block; + margin-bottom: 8px; + font-size: 14px; + font-weight: bold; + + i { + margin-right: 10px; + } +} + +/* - 主题设置 */ +.primary-container > .left-area > .cards > .card-item > .content > .settings-item > .themes { + display: flex; + justify-content: center; + gap: 8px; +} + +.primary-container > .left-area > .cards > .card-item > .content > .settings-item > .themes > .theme-item { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 100%; + height: 42px; + border-radius: 12px; + font-size: 10px; + font-weight: bold; + box-sizing: border-box; + + i { + margin-bottom: 2px; + } +} + +.primary-container > .left-area > .cards > .card-item > .content > .settings-item > .themes > .theme-item, +.primary-container > .left-area > .cards > .card-item > .content > .settings-item > .themes > .theme-item * { + cursor: url(https://cdn.jsdelivr.net/gh/honjun/cdn@1.6/img/cursor/ayuda.cur), pointer; +} + +.primary-container > .left-area > .cards > .card-item > .content > .settings-item > .themes > .theme-item.enable { + /* border: 2px solid #e87a90; */ + box-shadow: 0px 0px 2px 2px #2a92d8; +} +/* - 主题设置 End */ +/* - 浏览设置 End */ + /* - 左侧卡片 End */ /* - 页脚 */ diff --git a/assets/themes/defdark/colors/dark/styles/elements.css b/assets/themes/defdark/colors/dark/styles/elements.css new file mode 100644 index 0000000..205b355 --- /dev/null +++ b/assets/themes/defdark/colors/dark/styles/elements.css @@ -0,0 +1,9 @@ +/* - 标题 */ +h1, +h2, +h3, +h4, +h5, +h6 { + color: #77c6ff; +} diff --git a/assets/themes/defdark/colors/dark/styles/index.css b/assets/themes/defdark/colors/dark/styles/index.css new file mode 100644 index 0000000..45f1738 --- /dev/null +++ b/assets/themes/defdark/colors/dark/styles/index.css @@ -0,0 +1,45 @@ +/* 全局样式 */ +body { + background-color: #1c1c1c; + color: #ffffff; +} + +/* 内容版块 */ +.primary-container > .right-area > .content-page { + background-color: #282828; +} + +/* - 左侧卡片 */ +.primary-container > .left-area > .cards > .card-item { + background-color: #282828; +} +/* - 浏览设置 */ +.primary-container > .left-area > .cards > .card-item > .content > .settings-item { + background-color: #206fa0; + color: #ffffff; +} + +/* - 主题设置 */ +.primary-container > .left-area > .cards > .card-item > .content > .settings-item > .themes { +} + +.primary-container > .left-area > .cards > .card-item > .content > .settings-item > .themes > .theme-item { +} +/* - 主题设置 End */ +/* - 浏览设置 End */ + +/* - 左侧卡片 End */ + +/* - 页脚 */ +.footer { + background-color: #171717; +} + +.footer > .line-break { + color: #aaaaaa; +} + +.footer .icp-link { + color: #ffffff; +} +/* - 页脚 End */ diff --git a/assets/themes/defdark/styles/elements.css b/assets/themes/defdark/styles/elements.css deleted file mode 100644 index e69de29..0000000 diff --git a/assets/themes/defdark/styles/index.css b/assets/themes/defdark/styles/index.css deleted file mode 100644 index e69de29..0000000 diff --git a/assets/themes/defdark/theme.json b/assets/themes/defdark/theme.json index 253fd5c..8631ab2 100644 --- a/assets/themes/defdark/theme.json +++ b/assets/themes/defdark/theme.json @@ -1,10 +1,40 @@ { - "id": "defdark.themes.acghome.hic.top", + "id": "defdark", "name": "DefDark", "version": "1.0.0", "author": "成成0v0 (hic.top, chengcheng@miao.ms)", + "repo": "https://github.com/ChengCheng0v0/ACG-Home", "files": { - "styles": ["elements.css", "index.css"], - "scripts": {} + "styles": [], + "scripts": [] + }, + "colors": { + "list": { + "light": { + "displayName": "Light", + "files": { + "styles": [], + "scripts": [] + }, + "icon": { + "icon": "fa-solid fa-sun", + "color": "#000000", + "background": "#eeeeee" + } + }, + "dark": { + "displayName": "Dark", + "files": { + "styles": ["elements.css", "index.css"], + "scripts": [] + }, + "icon": { + "icon": "fa-solid fa-moon", + "color": "#ffffff", + "background": "#1c1c1c" + } + } + }, + "index": ["light", "dark"] } } diff --git a/config.json b/config.json index f25413a..b0951f6 100644 --- a/config.json +++ b/config.json @@ -1,5 +1,23 @@ { "title": "成成0v0 の 个人网站", + "theme": { + "theme": "defdark", + "displayName": "Default", + "colors": { + "autoSwitch": { + "displayName": "Auto", + "light": "light", + "dark": "dark", + "icon": { + "icon": "fa-solid fa-circle-half-stroke", + "color": "#808080", + "background": "linear-gradient(45deg, #eeeeee 50%, #1c1c1c 50%)" + } + }, + "enable": ["light", "!autoSwitch", "dark"], + "default": "!autoSwitch" + } + }, "masterInfo": { "name": "成成0v0", "avatar": "./assets/images/avatar.png", diff --git a/index.html b/index.html index b06af2f..373d87a 100644 --- a/index.html +++ b/index.html @@ -19,7 +19,10 @@ + + +
@@ -32,11 +35,7 @@
Loading... - +
公告 @@ -50,10 +49,13 @@ Loading...
-
- 站点信息 +
+ 浏览设置
- 这是一条测试文本! +
+ 配色方案 · +
+