diff --git a/README.md b/README.md index 421ff4f..88af7eb 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ - **页脚可关闭的主题署名**:默认「Theme by WhiteSpace」并链接到主题仓库,可在设置中修改名称 / 链接或整体关闭 - **页脚全站数据统计**:文章 / 分类 / 标签 / 评论 / 访问量 / 字数,可按项开关,整体可关闭 - **文章卡片字数 + 阅读时长**:可在设置中关闭(需 `halo-plugin-extra-api` 提供数据) +- **全站 lightgallery.js 灯箱**:文章、页面、瞬间、图库等页面的图片点击放大预览(按需懒加载,可在设置中整体关闭) - 多语言:`i18n/default.properties`(回退)+ `i18n/zh_CN.properties` - 提供 `layout.html` 布局契约,供使用布局契约的插件页面复用主题导航 / 页脚 / 暗色样式 @@ -25,9 +26,10 @@ 主题已为以下插件提供适配页面(在 `src/` 下以同名模板提供,构建后进入 `templates/`): -- **plugin-moments**:`/moments` 瞬间页 -- **plugin-photos**:`/photos` 图库页 -- **plugin-links**:`/links` 友链页;友链申请表单已**直接内嵌**在友链页底部(点击「申请友链」展开,调用插件公共 REST API,含图形验证码);并额外提供 +- **plugin-moments**:`/moments` 瞬间列表页 + `/moments/{name}` 瞬间详情页(完整内容、媒体、标签、作者信息与评论区);列表页发布人与发布时间同行对齐 +- **plugin-photos**:`/photos` 图库页 —— 首屏按相册分组展示(封面默认自动取相册内第一张图,可在主题设置「图库」中为每个相册手动配置封面);点击相册进入 `?group=photo-group-*` 瀑布流视图(一行三张、页面加宽),支持**预览模式**(lightgallery 点击放大,默认值可在设置中配置,访客可在前台自行切换) +- **plugin-important-dates**:`/important-dates` 重要日子页(即将到来提醒、全部日期倒计时、重要标记、人员生日) +- **plugin-links**:`/links` 友链页 —— 「全部」视图按分组展示并显示分组标题,卡片简介最多两行、卡片高度统一;友链申请表单已**直接内嵌**在友链页底部(点击「申请友链」展开,调用插件公共 REST API,含图形验证码);并额外提供 - `links-subscribe.html`:RSS 订阅动态聚合页(使用 `linkFeedFinder`);每条仅显示作者与发布时间,作者名链接到来源博客,摘要默认折叠为 5 行 - **halo-weread-plugin**:`weread.html` 微信读书书架页(前端拉取插件 `/shelf-html` 并注入) - **plugin-douban**:`douban.html` 豆瓣收藏页(按「读过 / 在读 / 看过 / 在追」分栏展示书影音,使用 `doubanFinder.getMovies(...)`;未安装插件时显示提示)。在控制台新建单页并选择「豆瓣页面」模板即可启用 @@ -88,8 +90,10 @@ pnpm check # 格式化 + Lint 修复 | basic | color_scheme | 默认配色:跟随系统 / 浅色 / 深色 | | basic | footer_copyright | 页脚自定义版权(支持 HTML,留空不显示) | | basic | show_theme_credit | 页脚是否显示「Theme by」署名(默认开) | -| basic | theme_credit_text | 署名名称(默认「暖心向阳335」) | +| basic | theme_credit_text | 署名名称(默认「WhiteSpace」,可改) | +| basic | theme_credit_url | 署名跳转链接(默认主题仓库地址) | | basic | show_word_count | 文章卡片是否显示字数与阅读时长(需 extra-api)| +| basic | enable_lightbox | 全站 lightgallery 图片灯箱(默认开) | | featured| enable | 首页是否展示置顶文章 | | about | avatar / bio / github_url / twitter_url / contact_email | 关于页(自定义模板 `page_about.html`)内容 | | seo | enable_metadata | 输出 canonical / Open Graph / Twitter Card | @@ -99,6 +103,8 @@ pnpm check # 格式化 + Lint 修复 | stats | enable | 页脚是否显示全站数据统计(默认关) | | stats | show_posts / show_categories / show_tags / show_comments / show_visits | 各项统计的显隐开关 | | stats | show_words | 全站总字数(需 extra-api 插件,默认开) | +| photos | preview_default | 相册瀑布流是否默认开启预览模式(默认开) | +| photos | album_covers | 各相册手动封面(「相册名称 + 封面图」列表,留空自动取第一张) | ## 约定与说明 diff --git a/i18n/default.properties b/i18n/default.properties index b34a90d..7f79e16 100644 --- a/i18n/default.properties +++ b/i18n/default.properties @@ -47,3 +47,6 @@ stats.comments={0} comments stats.visits={0} visits stats.words={0} words douban.plugin_required=Douban plugin (plugin-douban) not detected. Please install and enable it, then set your Douban ID in the plugin settings. +photos.photo_count={0} photos +moment.back_to_list=Back to moments +moment.tags=Tags diff --git a/i18n/zh_CN.properties b/i18n/zh_CN.properties index bd501c1..a53ecad 100644 --- a/i18n/zh_CN.properties +++ b/i18n/zh_CN.properties @@ -47,3 +47,6 @@ stats.comments={0} 条评论 stats.visits={0} 次访问 stats.words={0} 字 douban.plugin_required=未检测到豆瓣插件(plugin-douban),请安装并启用后在插件设置中填写豆瓣 ID。 +photos.photo_count={0} 张照片 +moment.back_to_list=返回瞬间列表 +moment.tags=标签 diff --git a/package.json b/package.json index c49fde8..cf8919e 100644 --- a/package.json +++ b/package.json @@ -25,5 +25,8 @@ "vite": "npm:@voidzero-dev/vite-plus-core@latest", "vitest": "npm:@voidzero-dev/vite-plus-test@latest" } + }, + "dependencies": { + "lightgallery": "^2.9.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1ff075a..d47a8ea 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,10 @@ overrides: importers: .: + dependencies: + lightgallery: + specifier: ^2.9.0 + version: 2.9.0 devDependencies: '@halo-dev/theme-package-cli': specifier: ^1.0.1 @@ -761,6 +765,10 @@ packages: resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} engines: {node: '>= 0.6.3'} + lightgallery@2.9.0: + resolution: {integrity: sha512-58Ud1DyhD2ao58t+kPEqSZrjFxg23tGd5ZKr75erm7q31g5xhUtWUJH3sTUkhHzlyJAKHj5eTrJ37HQRXG4Wbg==} + engines: {node: '>=6.0.0'} + lightningcss-android-arm64@1.32.0: resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} engines: {node: '>= 12.0.0'} @@ -1629,6 +1637,8 @@ snapshots: dependencies: readable-stream: 2.3.8 + lightgallery@2.9.0: {} + lightningcss-android-arm64@1.32.0: optional: true diff --git a/settings.yaml b/settings.yaml index b70c06b..f3b38ac 100644 --- a/settings.yaml +++ b/settings.yaml @@ -47,6 +47,11 @@ spec: label: 文章卡片显示字数 value: true help: 在文章卡片与置顶文章上展示字数及阅读时长(需 extra-api 插件提供数据) + - $formkit: checkbox + name: enable_lightbox + label: 全站图片灯箱 + value: true + help: 使用 lightgallery 为文章、瞬间、图库等页面的图片提供点击放大预览 - group: featured label: 置顶文章 formSchema: @@ -153,3 +158,25 @@ spec: name: show_words label: 全站总字数(需 extra-api 插件) value: true + - group: photos + label: 图库 + formSchema: + - $formkit: checkbox + name: preview_default + label: 相册默认开启预览模式 + value: true + help: 进入相册瀑布流时默认开启点击放大预览,访客可在前台自行切换 + - $formkit: repeater + name: album_covers + label: 相册封面 + value: [] + help: 为相册手动指定封面;不配置时自动使用相册内第一张图片。「相册名称」填写图库页面显示的相册名 + children: + - $formkit: text + name: group + label: 相册名称 + validation: required + - $formkit: attachment + name: cover + label: 封面图 + validation: required diff --git a/src/douban.html b/src/douban.html index 31f9a62..18385de 100644 --- a/src/douban.html +++ b/src/douban.html @@ -80,52 +80,164 @@ - - - + + + + + + + + + + diff --git a/src/important-dates.html b/src/important-dates.html new file mode 100644 index 0000000..59c2426 --- /dev/null +++ b/src/important-dates.html @@ -0,0 +1,237 @@ + + + +
+
+

记得

+

重要的日子,值得被记住。

+ + + +
+

即将到来

+
+
+

名称

+

+ + +

+
+
+
+
+ + +
+

全部日期

+
    +
  • + 名称 + 重要 + + + + + + 今天 + 还有 N 天 +
  • +
+

暂无重要日期

+
+ + + +
+

人员

+
    +
  • + + + 姓名 +

    + + + + +

    +
    +
  • +
+
+
+
+
+
diff --git a/src/js/lightbox.ts b/src/js/lightbox.ts new file mode 100644 index 0000000..d7691dd --- /dev/null +++ b/src/js/lightbox.ts @@ -0,0 +1,46 @@ +/** + * whitespace 留白主题 - lightgallery 灯箱 + * 动态模式:扫描带 data-lightbox 属性的容器内的图片,点击放大预览。 + */ +import lightGallery from "lightgallery"; +import lgZoom from "lightgallery/plugins/zoom"; +import "lightgallery/css/lightgallery.css"; +import "lightgallery/css/lg-zoom.css"; + +export function initLightbox(): void { + const containers = document.querySelectorAll("[data-lightbox]"); + if (!containers.length) return; + + containers.forEach((container) => { + const images = Array.from(container.querySelectorAll("img")); + if (!images.length) return; + + const items = images + .map((img) => { + const src = img.getAttribute("data-full") || img.currentSrc || img.src; + if (!src) return null; + return { src, thumb: src, alt: img.alt || "" }; + }) + .filter((item): item is { src: string; thumb: string; alt: string } => + Boolean(item), + ); + if (!items.length) return; + + // 挂载到 body,避免父级 stacking context 遮挡灯箱 + const gallery = lightGallery(document.body, { + dynamic: true, + dynamicEl: items, + plugins: [lgZoom], + speed: 400, + }); + + images.forEach((img, index) => { + img.style.cursor = "zoom-in"; + img.addEventListener("click", (e) => { + e.preventDefault(); + e.stopPropagation(); + gallery.openGallery(index); + }); + }); + }); +} diff --git a/src/js/main.ts b/src/js/main.ts index d69f077..534fedd 100644 --- a/src/js/main.ts +++ b/src/js/main.ts @@ -252,6 +252,20 @@ function initKeyboardShortcuts(): void { }); } +// ======================================== +// lightgallery 灯箱(按需加载) +// ======================================== + +async function initLightbox(): Promise { + if (!document.querySelector("[data-lightbox]")) return; + try { + const { initLightbox } = await import("./lightbox"); + initLightbox(); + } catch { + /* 加载失败时静默降级为普通图片展示 */ + } +} + // ======================================== // 初始化 // ======================================== @@ -270,6 +284,7 @@ function init(): void { initSmoothScroll(); initExternalLinks(); initKeyboardShortcuts(); + void initLightbox(); } if (document.readyState === "loading") { diff --git a/src/links.html b/src/links.html index 8803920..9344c9b 100644 --- a/src/links.html +++ b/src/links.html @@ -26,12 +26,15 @@ } .links-list li { margin: 0; + display: flex; } .links-list a { display: flex; - align-items: center; + align-items: flex-start; gap: 0.75rem; padding: 1rem; + width: 100%; + box-sizing: border-box; border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-bg-secondary); @@ -48,6 +51,11 @@ object-fit: cover; flex-shrink: 0; } + .links-list .links-body { + display: flex; + flex-direction: column; + min-width: 0; + } .links-list .links-name { font-weight: 600; } @@ -55,6 +63,19 @@ margin: 0.25rem 0 0; font-size: 0.85rem; color: var(--color-text-secondary); + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + } + .links-group-title { + font-size: 1.1rem; + margin: 2rem 0 1rem; + padding-bottom: 0.5rem; + border-bottom: 1px solid var(--color-border); + } + .links-section:first-of-type .links-group-title { + margin-top: 0; } .links-apply { margin-top: 2rem; @@ -138,29 +159,103 @@ > - + + +
+ + +

暂无链接

+