327 lines
15 KiB
Markdown
327 lines
15 KiB
Markdown
|
---
|
|||
|
title: 园子装修日志(2)
|
|||
|
slug: yuanzi-decorating-2
|
|||
|
date: 2024-08-21
|
|||
|
|
|||
|
summary: 今天食堂又做(酸菜)鱼了呢,小氯刚刚摸的。
|
|||
|
description: 本文记录了作者最近优化 Hugo 博客的过程,包括优化 GitHub 卡片、重构 CSS 和侧边栏等。优化包括样式、API 逻辑、错误处理等,重构 CSS 采用 SCSS 方法,导入特定页面样式。侧边栏优化了公告、目录、移动端显示和图标,使得代码结构更加整洁。
|
|||
|
categories: ["百草园"]
|
|||
|
tags:
|
|||
|
- 博客
|
|||
|
- 折腾
|
|||
|
|
|||
|
featuredImage: https://img.viento.cc/cover/yuanzi-decorating-2-cover.webp
|
|||
|
---
|
|||
|
各位老友们好,我是 Chlorine。继续水文,讲讲这几天摸鱼的经历。
|
|||
|
|
|||
|
准确来说应该是我能记得的经历,因为我记性一向不好,而且这几天的 Git commit 也异常多:
|
|||
|
|
|||
|

|
|||
|
|
|||
|
## 优化 GitHub 卡片
|
|||
|
|
|||
|
这几乎是我的每日任务了。由于 GitHub/GitLab/Codeberg 的卡片基本上是一样的,所以说一优化可以优化三次,三倍业绩,三倍快乐,~~改 bug 也是三倍快乐~~。
|
|||
|
|
|||
|
这次的优化很多,包括但是不限于:
|
|||
|
|
|||
|
- 样式打磨,我的目标就是 Vercel/iOS18 的风格~
|
|||
|
- API 逻辑改写,更多地使用 Hugo 模板
|
|||
|
- 错误处理完善
|
|||
|
- 使用 license 映射来改进显示
|
|||
|
- 自动截断过长的描述
|
|||
|
- 图标更改,采用 Simple Icons 的 GitLab/Codeberg 图标
|
|||
|
- 去掉父元素的 `<a>` 标签,改良整体样式和点击容错性
|
|||
|
|
|||
|
此外,我在之前的老朋友 Blowfish 主题那里闲逛的时候发现他们加了 Gitea 短代码,无论谁在写 Hugo shortcode,我小氯都要帮帮场子。直接加上。Gitea 和 Codeberg 的逻辑基本上一样,毕竟 Codeberg 就是基于 Gitea 的。
|
|||
|
|
|||
|
顺便说一句,我发现我犯了个非常执杖的错误,就是我已经用 `resources.GetRemote` 获取 API 的数据了,我居然用 JavaScript 又获取了一次。
|
|||
|
|
|||
|
## refactor CSS
|
|||
|
|
|||
|
闲着没事去主题原型 Hugo landscape 的仓库看了看,发现恐咖兵糖大佬把 CSS 重构了一下。正好我也一直被无法正确导入 CSS 困扰,于是就动手开始 refactor 了。
|
|||
|
|
|||
|
我原本的文件结构大概是:
|
|||
|
|
|||
|
```txt
|
|||
|
.
|
|||
|
├── css
|
|||
|
│ ├── addon.css
|
|||
|
│ ├── algolia(almost of no use)
|
|||
|
│ │ ├── algolia_dark.css
|
|||
|
│ │ └── algolia_light.css
|
|||
|
│ ├── base.css
|
|||
|
│ ├── normalize.css
|
|||
|
│ ├── tailwind.css
|
|||
|
│ └── whisper.css
|
|||
|
├── js ...
|
|||
|
├── json ...
|
|||
|
└── style.css
|
|||
|
```
|
|||
|
|
|||
|
其中 `style.css` 是 UnoCSS 自动构建生成的,而我的几乎所有魔改 CSS 都堆在 `addon.css` 中,看着很难受,维护起来也不方便。
|
|||
|
|
|||
|
重构代码时我选择了恐咖兵糖大佬使用的 SCSS 方法:在文件夹中创建 SCSS 索引文件,然后在 `css.html` 中直接用 Hugo 来解构 SCSS 得到 CSS 导入。一顿拆分重组后得到了:
|
|||
|
|
|||
|
```txt
|
|||
|
.
|
|||
|
├── addon
|
|||
|
│ ├── algolia.css
|
|||
|
│ ├── fonts.css
|
|||
|
│ ├── friend.css
|
|||
|
│ ├── index.scss
|
|||
|
│ ├── others.css
|
|||
|
│ ├── twikoo.css
|
|||
|
│ └── whisper.css
|
|||
|
├── base
|
|||
|
│ ├── base.css
|
|||
|
│ ├── index.scss
|
|||
|
│ ├── normalize.css
|
|||
|
│ └── tailwind.css
|
|||
|
├── main.scss
|
|||
|
├── test.txt
|
|||
|
└── uno.css
|
|||
|
```
|
|||
|
|
|||
|
此外,对于一些只在特定页面生效的 CSS 文件(例如 `whisper.css` 只在《碎语》页面生效),我们直接在页面进行导入,避免不必要的开销。
|
|||
|
|
|||
|
```html
|
|||
|
{{ with resources.Get "css/addon/whisper.css" }}
|
|||
|
<style>
|
|||
|
{{ .Content | safeCSS }}
|
|||
|
</style>
|
|||
|
{{ end }}
|
|||
|
```
|
|||
|
|
|||
|
## 侧边栏优化
|
|||
|
|
|||
|
在我看来,侧边栏是个相当重要的页面元素。我在侧边栏堆了公告(Announcement)、目录(TOC)、分类(Categories)和标签(Tags)。在移动端,它们会显示在主体卡片的下方。
|
|||
|
|
|||
|
### 目录
|
|||
|
|
|||
|
这个目录改得我几乎红温。我原本是直接采用 Hugo 的 `{{ .Page.TableOfContents }}` 模板,但是其样式令人一言难尽。而且可能是因为 Swup 的原因,目录是没办法随着文章同步更新的。于是,在深思熟虑后,我决定暂时将其去掉。
|
|||
|
|
|||
|
> 《fix bug by removing the feature》
|
|||
|
|
|||
|
### 公告
|
|||
|
|
|||
|
公告组件是我比较看重的东西。我采用的是在 Hugo 配置文件相应选项开启的情况下,直接读取 `content` 下的 `announcement.md` 进行展示。其功能比较完善,但是样式看起来并不好看。
|
|||
|
|
|||
|
我去查了些资料,找到了一个名为 prose 的布局,经过实验看起来还不错。所以就改成了这样:
|
|||
|
|
|||
|
```html
|
|||
|
<div id="announcement-content" class="collapse-wrapper px-4 overflow-hidden">
|
|||
|
{{ $announcement := .Site.GetPage "announcement" }}
|
|||
|
{{ with $announcement }}
|
|||
|
<div class="rounded-xl backdrop-blur-md p-2 mx-4 mb-2 relative md:mx-auto md:max-w-lg">
|
|||
|
<div class="prose dark:prose-invert max-w-none text-sm md:text-base">
|
|||
|
{{ .Content | markdownify }}
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
{{ end }}
|
|||
|
</div>
|
|||
|
```
|
|||
|
|
|||
|
### 移动端隐藏组件
|
|||
|
|
|||
|
我感觉,移动端很少有人会去专门翻最下面的 categories 和 tags,所以我干脆直接把它们 `hidden md:block` 了。这样最下面就是 profile 和 announcement 了。
|
|||
|
|
|||
|
### 图标
|
|||
|
|
|||
|
从页面的元素选择上,各位老友应该可以看出,我非常喜欢 UnoCSS 的矢量图标,尤其是 IBM 的 Carbon Icons 系列。
|
|||
|
|
|||
|
所以我打算给侧边栏的组件加图标。这个不算难,一个 `flex` 布局,再把图标和标题套在一起就可以了。
|
|||
|
|
|||
|
```html
|
|||
|
<div class="font-bold transition text-lg text-neutral-900 dark:text-neutral-100 relative ml-8 mt-4 mb-2 flex items-center
|
|||
|
before:content-['']
|
|||
|
before:w-1 before:h-4 before:rounded-md before:bg-[var(--primary)]
|
|||
|
before:absolute before:left-[-16px] before:top-[5.5px]">
|
|||
|
<i class="i-carbon-data-categorical mr-2"></i>
|
|||
|
Categories
|
|||
|
</div>
|
|||
|
```
|
|||
|
|
|||
|
加个边距看上去好看。
|
|||
|
|
|||
|
我用的图标分别是:
|
|||
|
|
|||
|
```html
|
|||
|
<i class="i-carbon-border-full mr-2"></i>
|
|||
|
<i class="i-carbon-data-categorical mr-2"></i>
|
|||
|
<i class="i-carbon-tag-group mr-2"></i>
|
|||
|
```
|
|||
|
|
|||
|
### partial 化
|
|||
|
|
|||
|
我原本的组件样式大部分是写在 `sidebar.html` 那里的。但是这样让 `sidebar.html` 看起来很不整洁,所以我把大部分的代码都移动到了相应的部分代码(partial)中。
|
|||
|
|
|||
|
```html
|
|||
|
{{/* sidebar.html */}}
|
|||
|
|
|||
|
<div id="sidebar"
|
|||
|
class="w-full row-start-3 row-end-4 col-span-2 lg:row-start-2 lg:row-end-3 lg:col-span-1 lg:max-w-[17.5rem] onload-animation">
|
|||
|
<div class="flex flex-col w-full gap-4 mb-4">
|
|||
|
{{ partial "sidebar/profile.html" . }}
|
|||
|
</div>
|
|||
|
<div class="flex flex-col w-full gap-4 top-4 sticky">
|
|||
|
{{ if .Site.Params.Basic.announcement }}
|
|||
|
<widget-layout id="announcement-widget" class="pb-4 card-base">
|
|||
|
{{ partial "sidebar/announcement.html" . }}
|
|||
|
</widget-layout>
|
|||
|
{{ end }}
|
|||
|
|
|||
|
<widget-layout id="categories-widget" class="pb-4 card-base hidden md:block">
|
|||
|
{{ partial "sidebar/categories.html" . }}
|
|||
|
</widget-layout>
|
|||
|
|
|||
|
<widget-layout id="tags-widget" class="pb-4 card-base hidden md:block">
|
|||
|
{{ partial "sidebar/tags.html" . }}
|
|||
|
</widget-layout>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
```
|
|||
|
|
|||
|
可以看到基本上是一个模子刻出来的,形成可复用打法了属于。
|
|||
|
|
|||
|
### 折叠展开
|
|||
|
|
|||
|
此外还写了个折叠展开的代码。但是感觉现在的 tags 和 categories 都不是很长,因此就没真正加上。
|
|||
|
|
|||
|
## Hello,109chan
|
|||
|
|
|||
|
> 《hello 宇宙最新力作》
|
|||
|
|
|||
|
### 前言
|
|||
|
|
|||
|
熟悉博客圈的老友应该知道一个著名的工具:[TianliGPT](https://postsummary.zhheo.com/)。这个工具来自洪哥([张洪 Heo](https://blog.zhheo.com/))和 Tianli,是个自动生成博客文章 AI 摘要的工具。对于希望在听故事之前简单了解其脉络的老友来说是个很好的补益。
|
|||
|
|
|||
|
问题是:这个工具收费。虽然不贵,但是以小氯的节俭程度,还是不太舍得花这个钱的。
|
|||
|
|
|||
|
~~那你买域名的时候怎么一点也不节俭啊喂!~~
|
|||
|
|
|||
|
于是我就开始想替代方法。很巧,在之前写 Java 大作业的时候,我们有一个 feature request 就是 AI summary(用大模型的 API 生成新闻摘要)。虽然小氯没用过 API,但这个部分的思路大概还是很清晰的。最后实现得非常好,除了学校给的 API SDK 居然不能用,必须 HTTP,害得我和热心的答疑大佬调了半个下午。
|
|||
|
|
|||
|
于是我想按照这个思路做一个 AI 摘要。不过,我不太熟悉怎么在 Hugo 里面用数据库,而且我的 API 只有几千万 token 的额度,我比较担心用完(~~你确定你能写这么多?~~)。
|
|||
|
|
|||
|
于是我又想到了薅 CloudFlare 的羊毛。用 Workers 做 JavaScript 运行时,D1 做数据持久化,Workers AI 做摘要。这个免费额度是绝对够的,唯一的问题是我不会。
|
|||
|
|
|||
|
穷途末路之下,我想到了我的好伙伴:Quail。
|
|||
|
|
|||
|
各位老友应该都知道,我的 newsletter 是通过 [Quail](https://quail.ink) 实现的,而 Quail 为每位创作者都免费提供 AI 摘要生成功能。这个摘要直接写在 Markdown 源文件的 YAML front matter 里面,是完全静态的,既不怕丢,也不用执行复杂的 API 请求和数据库调用。
|
|||
|
|
|||
|
那么,就是你了。
|
|||
|
|
|||
|
### 设计
|
|||
|
|
|||
|
由于只有 `content/posts` 中的页面需要加摘要,所以我们直接改动相应的 `single.html` 即可。位置我选择在封面图和正文之间。
|
|||
|
|
|||
|
先用 AI 搓个形:
|
|||
|
|
|||
|
```html
|
|||
|
<div class="my-4 p-4 bg-neutral-100 dark:bg-neutral-800 rounded-lg shadow-md cursor-pointer transition-transform transform hover:scale-105" id="ai-summary-toggle">
|
|||
|
<div class="flex items-center justify-between">
|
|||
|
<span class="text-neutral-900 dark:text-neutral-100 font-semibold">AI 摘要</span>
|
|||
|
<svg class="w-5 h-5 text-neutral-900 dark:text-neutral-100 transition-transform duration-200 transform" id="ai-summary-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
|||
|
</svg>
|
|||
|
</div>
|
|||
|
<div class="mt-2 text-neutral-700 dark:text-neutral-300 hidden" id="ai-summary-content">
|
|||
|
{{ .Description }}
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
```
|
|||
|
|
|||
|
然后我简单修了下,大概就是这样了:
|
|||
|
|
|||
|
```html
|
|||
|
<div class="my-4 p-4 border dark:border-color-neutral-600 border-color-neutral rounded-xl shadow-md cursor-pointer transition-transform transform-gpu md:hover:scale-105 shadow-md md:hover:shadow-lg"
|
|||
|
id="ai-summary-toggle">
|
|||
|
<div class="flex items-center justify-between pb-3">
|
|||
|
<div class="flex items-center">
|
|||
|
<span class="i-carbon-ai-generate text-neutral-900 dark:text-neutral-100 mr-4"></span>
|
|||
|
<span class="text-lg text-neutral-900 dark:text-neutral-100 font-semibold">AI 摘要</span>
|
|||
|
</div>
|
|||
|
<div class="flex items-center">
|
|||
|
<span
|
|||
|
class="px-2 py-1 bg-gradient-to-r from-blue-500 to-purple-500 bg-opacity-20 backdrop-blur-md text-neutral-300 rounded-md text-sm mr-2 ring-1 ring-offset-1 ring-indigo-200 dark:ring-indigo-700">109酱</span>
|
|||
|
<svg class="w-5 h-5 text-neutral-900 dark:text-neutral-100 transition-all duration-300 transform hover:scale-110"
|
|||
|
id="ai-summary-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
|||
|
stroke="currentColor">
|
|||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
|||
|
</svg>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<div class="border-t border-neutral-200 dark:border-neutral-700 my-3"></div>
|
|||
|
<div class="mt-3 text-neutral-700 dark:text-neutral-300 hidden" id="ai-summary-content">
|
|||
|
{{ .Description }}
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
```
|
|||
|
|
|||
|
移动端的点击放大我关掉了,因为看起来不太舒服。高斯模糊的手艺是跟 GPT 学的,本来想用 `var(--primary)` (就是我的博客那个一直在变化的主题色),但是不知道为什么没成功。
|
|||
|
|
|||
|
使用 `description` 字段可以保证其尽量都是 AI 摘要,`summary` 字段我比较喜欢用来扯淡。难绷的是 Quail 的标准 AI 摘要键是 `summary`,造成我需要手动改一下。已经和作者[歌词经理](https://quail.ink/lyric)反馈了,作者说会想办法优化一下。
|
|||
|
|
|||
|
嗯,你问为什么叫 109 酱啊?就不告诉你 (/ω\)
|
|||
|
|
|||
|
## 更新依赖
|
|||
|
|
|||
|
npm 有 `package-lock.json`,pnpm 有 `pnpm-lock.yaml`,yarn 有 `yarn.lock`, Bun 有 `bun.lockb`,这充分说明了对于 JavaScript 包,锁定版本有多么重要。我曾经试着直接对 `theme/efimero` 的依赖进行更新,结果自然是必遭严惩,样式全都乱了。
|
|||
|
|
|||
|
最近闲着没事去 UnoCSS 的[官网](https://unocss.dev)的官网看了下,发现已经到 `0.62` 了,而我的依赖还是 `0.60` 左右。由于我非常喜欢 UnoCSS,并且确信自己会一直使用,于是我想着更新一下。
|
|||
|
|
|||
|
直接删除 `node_modules` 和 `bun.lockb`,然后把 `package.json` 的版本全改成 `latest`,直接一手 `bun i` 更新。
|
|||
|
|
|||
|
然后构建,不出意料出了很多错误:
|
|||
|
|
|||
|
```txt
|
|||
|
Failed to load custom icon "copy" in "carbon": TypeError [ERR_IMPORT_ATTRIBUTE_MISSING]: Module "file:///Users/chlorine/Dev/Hugo/themes/efimero/node_modules/@iconify-json/carbon/icons.json" needs an import attribute of "type: json"
|
|||
|
at validateAttributes (node:internal/modules/esm/assert:88:15)
|
|||
|
at defaultLoad (node:internal/modules/esm/load:133:3)
|
|||
|
at async nextLoad (node:internal/modules/esm/hooks:746:22)
|
|||
|
at async nextLoad (node:internal/modules/esm/hooks:746:22)
|
|||
|
at async nextLoad (node:internal/modules/esm/hooks:746:22)
|
|||
|
at async Hooks.load (node:internal/modules/esm/hooks:383:20)
|
|||
|
at async handleMessage (node:internal/modules/esm/worker:199:18) {
|
|||
|
code: 'ERR_IMPORT_ATTRIBUTE_MISSING'
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
看来是缺少一个导入类型。在询问 GPT 后,得知改动 `uno.config.ts` 中的导入部分为动态导入即可:
|
|||
|
|
|||
|
```ts
|
|||
|
import { defineConfig } from "unocss";
|
|||
|
import { presetUno } from "unocss";
|
|||
|
import { presetIcons } from "unocss";
|
|||
|
import { presetAttributify, presetTypography } from "unocss";
|
|||
|
import presetLegacyCompat from '@unocss/preset-legacy-compat';
|
|||
|
|
|||
|
import carbonIcons from '@iconify-json/carbon/icons.json';
|
|||
|
import mdiIcons from '@iconify-json/mdi/icons.json';
|
|||
|
|
|||
|
export default defineConfig({
|
|||
|
presets: [
|
|||
|
presetAttributify(),
|
|||
|
presetUno(),
|
|||
|
presetTypography(),
|
|||
|
presetIcons({
|
|||
|
collections: {
|
|||
|
carbon: () => carbonIcons,
|
|||
|
mdi: () => mdiIcons,
|
|||
|
},
|
|||
|
scale: 1.2,
|
|||
|
warn: true,
|
|||
|
}),
|
|||
|
presetLegacyCompat({
|
|||
|
commaStyleColorFunction: true,
|
|||
|
})
|
|||
|
],
|
|||
|
// ...
|
|||
|
});
|
|||
|
```
|
|||
|
|
|||
|
## 结语
|
|||
|
|
|||
|
除此之外还有一堆优化,我们就不展开说了,像什么优化 Algolia Docsearch 样式之类的。静态博主都是装修爱好者,折腾就完了,把园子装修得漂漂亮亮的。
|
|||
|
|
|||
|
明天我考~~磕墓三~~科目三,考完之后我就要着手二次备案了。
|