阅读视图

发现新文章,点击刷新页面。

24、冬至

一候蚯蚓结;二候麋角解;三候水泉动

四川自贡冬至的习俗为食服滋补药食。

冬季自古以来就是滋补季节,更有俗语「冬季进补,来年打虎」之说法,寒冷冬季食用大补药膳却不必担心过补上火的问题。

通过搭配不同滋补功效的药材,辅以猪脚、鸡鸭、猪肚等食材小火慢慢炖煮,晚餐十分,全家人齐聚餐桌前,一人一个小碗乘上热气腾腾的汤汁,真是好不惬意。

DIY明信片互换计划

前些天收到了 品味苏州 小伙伴发来的明信片,非常欣喜。

在现在网络发达、信息快餐化的时代,能有一封经过漫长里程、时间送到你手上的信件,会更能体会到时间的味道。

因为我刚好有一台彩色打印机,每次出去玩了以后会习惯性把出行的照片精选一些打印出来放到相册里。这次在打印时我就在想:「既然我有打印机,那我能不能每次出去玩了以后自己做一些明信片寄给这些好友呢?」

打印的相片

所以昨天试着找了九张此次出行的照片,并调色打印,设计了一版,附上了临时想到的标语「现在网络太快,所以跨越千山而来的信件更值得我们等待和珍惜...」。

感觉效果好像还不错?

明信片背面

从左至右,从上到下,分别为1至9

所以,我也准备开启一个明信片互换计划。

各位小伙伴可以可以直接留言你的收件信息,并说明你要的图片序号,我会在看到你的留言后给你寄出。

我的博客已经开启审核模式,你们发出的信息不会直接公开展示,不用担心自己的信息泄露,我会在删掉你们的收件信息后通过留言审核,我会认真根据我对你的了解和想法填写后面给你的留言,祝好。

我需要第6张照片。
收件信息:
- 姓名:1900
- 地址:四川自贡xxxxxxxx
- 邮编:643000

另外;

家里没有打印机,也想自己做这种明信片的朋友,可以试着在淘宝、PDD找找代打服务,价格非常便宜,有众多模板可以选择,只需要简单把照片发给商家即可。

不知道能否号召大家一起参与这个活动,把自己的快乐,所看到的风景,想说的话传达给远在千里之外的另一个人。

谢谢。

川西笔架山,冬季雪山登山实操

前言

因为经常有看到阿均哥在周末时间出去爬山,很是羡慕。

我其实也是非常喜欢户外运动的人,2014 年和朋友去过一次牛背山,2015 年去过一次阿合哈洛,其实这种集体户外出行有不少好处:

  • 一是每次出行都是十来号人,一车形形色色的人在一起,在旅途中互相帮助、认识、了解、解放自我,总是能擦出不一样的火花,也能将平时积累的压力在旅途过程中完全释放出去。
  • 二是因为人多,旅途中产生的各种费用都大大降低了。

所以也想着自己能不能也经常这样出去玩一玩。

2014年牛背山徒步

2015年啊合哈洛徒步

驴友群

所以向他询问了都是通过什么渠道找的出行队伍,后被告知是加入了当地的一些户外微信群,跟着群里的其他人一起出行的。

在那之后偶然在骑行群里看到其他骑友说周末要跟着户外群里的人去爬山,所以顺嘴说了一下,让对方拉我进群,然后一下就像是打开了潘多拉墨盒,东拉西拉的进了三四个户外群里,这才知道原来我们这个小地方也有这么多活跃分子,几乎每周都有组团出去玩的驴友。

这些驴友群的组员出行规则几乎都是统一的

  • 出车的司机不承担邮费、过路费
  • 提前买好户外保险,一般在 20w 保额的在 5 元一天左右
  • 其他公共费用大家 AA。
  • 各自承担自己的风险。

因为行程有长有短,蹲了一两天后才加入了标题中提到的「笔架山冬季登山」活动。

笔架山冬季登山

景点介绍

笔架山位于四川理县,木卡羌寨前行 10 来公里左右,总徒步距离来回 14.47 公里,预计花费时间 5-6 小时左右,总爬升 1343 米,最高海拔 3766 米。上山后积雪覆盖,几乎没有路,可以说是一条完全的野生线路,有一名当地向导带路。

笔架山徒步路线图

人员

本次出行原本是 10 人,后有两人临时有事放弃,最终 8 人出行,两对情侣,另外 2 男、2 女。平均年龄没超过 35 岁。

行程安排

  • 第一天:星期六中午一点左右出发,司机来接的我们,开车 4 个小时左右到达理县,下高速后走 8 公里盘山公路,到达第一晚露营地「坞瓦布露营地」,晚上就着日落吃烤鸡烤兔 + 铁板烧烤,然后围着篝火唱歌喝酒。
  • 第二天:早上 8 点起床,客栈安排了当地的手工面 + 煎鸡蛋,吃完后 9 点左右出发爬山,整个行程走完下午 6 点左右回到露营地,吃完牦牛火锅后返程回家,最后 11 点 30 到家。

费用

  • 油费 + 过路费 380
  • 住宿 + 餐饮 492 两人
  • 合计 380+492=872

装备

冬季登山危险系数不比平日 ,所以装备一定要带齐全。我这次爬山除了没带登山杖,其他装备基本上都准备没有问题。

  • 贴身穿速干衣物:上衣可以是跑步的速干衣,下面穿速干骑行裤或者瑜伽裤,因为在登山过程中会大量出汗,普通棉质保暖衣可能无法快速排汗,会导致失温。
  • 保暖内衣两套:一套穿在速干衣物上面,另外一套备用。
  • 冲锋衣三件套:没有的话穿耐磨的羽绒服之类的也行。但是一定要是防水面料,避免粘上积雪,溶化后把里衣打湿。
  • 毛线帽:主要是要护住耳朵,在穿林时最好把冲锋衣帽子带上,避免落下的积雪掉进衣领。
  • 手套:这个一定要准备一款好一点的防水手套,进雪水后的冰凉感你一定不会想体验的。
  • 袜子:厚一点的长筒羊毛袜,多备几双,方便意外替换。
  • 雪套:这个一定要备,山上积雪厚的话没有雪套你的鞋几分钟就湿透了,冰冷的脚会加速失温。
  • 冰爪:雪地必备,增加摩擦力,避免打滑,这次我是在半山出现落叶堆积腐烂打滑后开始装备上,瞬间就不打滑了,下山时也能避免脚滑跌倒,非常好用。
  • 登山鞋:高帮登山鞋最好,可以保护脚踝,更保暖,也不容易进雪水。
  • 墨镜:雪地里反光容易雪盲,必备,其实骑行那种防风镜要更好一些,不容易掉,而且保护的更周全。
  • 登山杖:最好两根,质量一定要好,折叠优先,实在没有也可以在山上砍截树枝替代,我这次没带,用的树枝,也没啥问题,下山的时候别用,容易打滑戳伤自己。
  • 防晒:山上紫外线强,做好皮肤保护。
  • 登山包:最好是有背负、透气系统的背包,耐磨系数要高。
  • 热水壶、水袋:在雪地里喝一口热水是真的舒服。
  • 零食:在登山途中补充热量,体力。
  • 保鲜袋、塑料袋:可以多带几个保鲜袋,一是装垃圾,二是万一鞋子湿了也能套脚,作用很多,我的每次出门旅行都会带上七八个这玩意儿。

旅行后记

这次的行程其实终点的风景、餐食、住宿都算不上很好。

但是雪林穿越的体验是非常棒的,总共 1300 多米的爬升,而且后半段全是雪路,因为积雪覆盖,基本上已经看不到原来的路线了,我们一路在雪林里穿梭,都是现场开辟的路线,很多地方都要手脚共用才能爬上去,非常有挑战和刺激感。

而且这次出行认识了不少谈的来的本地朋友,如我们车的司机林老师、本地大学里的一对情侣小罗和小刘,出发和返程在车上的时间大家唠嗑渡过,一点也不觉得无聊和犯困。

不过这次行程难度对于我老婆来说挺大的。

因为之前她是不准备爬山的,而且她也没有户外经验,算是个小白,不过出行前我还是把她的雪套、雪爪一起带上了,怕万一需要上山。哪知道还真用上了,出发后当时另外一个也说在营地休息的人放鸽子没来了,现在只有她一个人在营地,挺没意思,索性就跟着我们一起上山了。

不过毕竟是小白,而且以前也没搞过户外,所以装备准备的不是很充分,没有冲锋衣裤,裤子这次就穿的普通牛仔裤,爬山途中遇到那些容易滑倒的地方都害怕的要命,都是屁股划着雪地走的,后半段的时候裤子有些地方就已经湿透了。

所以最后登顶的那一刻忽然就绷不住了,哭的稀里哗啦,她说就我们两个人在后面吊车尾,前面的人都走的很快,看不到了人,害怕的很,而且身上又冷,经常滑倒会觉得万一我们一不小心就滑到山下去,没准就死在山上。

在我看来其实整条线路都是在树林里,基本上没有悬崖峭壁的情况,即便是滑倒了也能很快抓住旁边的树枝,而且坡度都不是很大,风险是有,但是在我看来并不危险。

不过她在下山后回味起来又觉得特别有成就感,感觉好像瘾一下就起来了,还问我下次准备去哪里,哈哈。

图记

途中和小伙伴们

营地和住宿

日出

营地中

烧烤小啤酒+篝火晚会

上山中

集体照

「工人们」

山腰+自拍

雪路

望雪
屁股蹲
登顶,绷不住了,hhh

缓缓后美美自拍

集体照
下山
山脚
已经筋疲力尽,头顶的雪和树枝暗暗体现了狼狈

我VPS上的Selfhost服务

以前刚玩博客的时候用的 Wordpress 作为博客系统,基本上都搭建在一些互联网的公益服务上,如今还记得就是 wpoak 这个平台,不过这些服务难抵时间漫长,大多因为成本问题关门大吉了。

所以之后开始自己买 VPS 进行博客程序部署,也开始慢慢重视起数据的重要性和自我保有。

刚开始 VPS 还是玩的非常简单,实例化完成后便用 lnmp 等程序一键装好 PHP 环境,然后下载上传 WordPress 安装程序便算完事了,后来接触的多了才知道 VPS 能做的事情元远不止于此。

后来我开始接触到 Docker,然后研究各种 Selfhost 的应用、服务。

昨天在夜未央的「浪费」一文中评论到:「对于很多普通用户来说基本上没有什么选择了,我们是喜欢折腾和了解,知道有什么渠道去找平替」。而后又在 Dayu 的「目前使用的自托管服务」一文中看到了他分享的 Selfhost 清单,所以这里索性也做一次分享,希望也能让更多的人用到这些开源、可控、好用的服务,利用起自己 VPS 上多余的算力资源。

我目前的 VPS 只有一个国内的腾讯轻量云。

配置为 2C2G4M,一年费用大概在 100 元左右,目前购买了 3 年,自托管的服务如下:

Ghost

这是我目前使用的博客后端,Ghost 在国内算是非常小众的博客程序了。

我大概在 16 年开始使用 Ghost 作为博客系统,当时服务部署在百度的 BAE 平台上,「在百度开放云BAE上部署GHOST博客并配置七牛云」,后因服务不能在继续免费白嫖几经搬家,后落到现在的 VPS 上。

可以使用 Docker 或者 Ghost 官方的 CLI 程序安装。在以前的老版本时,数据库支持 SQLite,后来几经更新,现在在正式环境中只能使用 MySQL。

Artalk-Go

在早期时 Ghost 并没有提供评论功能,虽然在后续的迭代中增加了评论功能,但是在我看来不太友好,而且因为评论需要用户登录,其他使用 Ghost 评论功能的小伙伴反应,在迁移过程中需要额外进行会员数据、评论数据处理,所以我一直没有使用。

所以我最开始使用的自部署评论程序是 Twikoo ,但是因为这个项目更新在很长一段时间内停滞,且管理面板、功能过于简陋,几经尝试后换到了 Artalk。

Artalk 是国内大学生大佬开发的一款 Disqus 的第三方评论平替程序,开源,自部署,且功能十分丰富,更新也非常勤快,个人认为是一款非常完善的评论程序。

可以用 Docker 部署。

MySQL

这个不用多说,声名远播的开源数据库,因为 Ghost 和后面其他的一些程序需要用到。

可 Docker 部署。

  • 推荐程度:🌟🌟🌟🌟
  • 部署方式:Docker
  • 是否开源:是
  • 项目地址:MySQL

Vaultwarden

密码管理器我以前使用的是 LastPass 这个免费服务,但是在使用过程中频繁出现网络问题无法使用,而且管理面板没有中文,且某一次还出现了安全漏洞的问题,自那后我就切换到了 Bitwarden,在 PC 上通过浏览器拓展可以快速进行当前网站的密码填充。

但是因为想用密码泄露检测等服务,不过这些功能在官方的 Bitwarden 上都是需要付费使用,所以我开始使用了国外大佬基于 Bitwarden API 重新实现的开源密码管理程序 Vaultwarden,Bitwarden 中一些需要付费使用的服务在 Vaultwarden 上都能免费使用,搭配第三方的安卓端 Keyguard ,可以说颜值与实力并存。

密码管理器我是非常推荐每一个没用过的人使用的。

因为一些第三方网站管理员自身水平良莠不齐,可能会因为服务漏洞导致数据库泄露,此时如果你将常用密码用在注册这些网站上,届时你的其他重要服务被撞库暴力破解的风险将大大提升。

但是如果你使用密码管理器的随机高强度密码,只要在主密码没有泄露的情况下密码的情况下,即便数据库泄露了也不用担心被破解的风险,且因为在注册时使用的随机密码,即便第三方服务的数据库被泄露也不用担心被撞库。

Alist

Alist 是一个非常好用的网盘管理程序,因为太好用、用的人太多了,导致国内一些网盘实在承受不住,甚至不顾脸面将以前承诺的一些服务重新二次收费——没错,说的就是你「阿里网盘」。

Alist 可以在一个程序内管理你的大部分网盘服务,将他们集成在一起统一管理,并且通过 Alist 的一些拓展实现诸如公开分享、WebDav 等等,但是因为用的人实在太多了,而且很多影视程序也跟着上来薅羊毛,把网盘做成资源服务开放给其他人使用,导致这些情况的发生。

但是,即便这样,Alist 也依旧是一款非常好用的网盘管理程序。

Memos

Memos 是个非常好的 Flomo 平替工具,可以方便的进行卡片笔记记录,甚至你可以拿他当作微博、微博客来用也没什么问题,而且作者更新也非常勤快。

但是开发者时常进行 Break Change,我碰到的就有不下三次,导致我实在不敢继续再升级了,在某一次回退后彻底停留在目前使用的 v0.18.1 版本,好在那时的功能已经非常完善,即便不升级也不影响软件的后续使用。

Umami

google 统计的平替服务,之前部署在 Raliway 上,但是因为平台收费政策调整,只给 5 刀的免费额度,导致有一次超出额度,多交了 30 多元的费用后转移到 VPS 上部署。

GoToSocial

一款用 Go 写的联邦宇宙实例工具,长毛象的超小型替代,但是我没有玩的太明白,使用过程中出现很多莫名奇妙的问题,暂时放弃。

miniFlux

一款开源的 RSS 阅读器,简单好用,没有多余的花哨功能。

lx-music-sync

洛雪音乐的同步服务,可以在手机、PC 端之间同步历史记录、收藏等等。

wewe-rss

国内大佬开发的一款微信公众号转 RSS 订阅工具。

Express

我用 Docker 部署了一个 Express 服务,用于博客上一些需要后端的小功能,如点赞、PC 状态同步等。

WatchTower

Docker 镜像更新服务,通过维护一个需要自动更新的 Docker 容器名称列表,实现对对应容器镜像的自动更新。

独立博客自省问卷15题

雅余哥发起的调查,回答一波 

1、你的博客更新频率是多少?

A.每周更新

B.一周数篇:基本上每周都有把。

C.一月1-2篇

D.几个月一篇

 

2、你的博客上次更新是什么时候?

A.本周:有节气这个系列在,基本上很稳定。

B.上周

C.上个月

D.上季度

 

3、你的博客文章是原创的吗?

A.坚持原创:都是自己写的,可能有部分引用的块。

B.部分借鉴

C.AI 帮我写的

D.搬运别人的,而且不署名

 

4、你觉得自己的文章对他人有帮助吗?

A.旨在对他人有启示

B.多少有点意义:技术文章偏多一点,多少应该会有点帮助把。

C.每日每周流水账

D.自我陶醉就好,管他呢

 

5、你上次换博客主题/程序是什么时候?

A.上周

B.上个月:最近才从11ty换到Astro

C.去年

D.凭良心说,我多年都是一个主题

 

6、你上一次捣腾博客主题代码是什么时候?

A.昨天,撸代码到凌晨

B.每周必捣腾:经常会折腾,就像狂热的钓鱼佬一样。

C.每月有那么一次

D.一年有那么一次

 

7、你会对博客主题进行二次开发?

A.直接配置使用,省心不折腾

B.时不时自己改改,搞点新花样,换图片,换字体,爽:自然是这个选项。

C.删除主题作者版权信息,改改样式,然后自我感觉良好

D.改得面目全非,但保留原作者版权信息或注明

 

8、你多久打开自己博客自我陶醉一次?

A.每天数次:基本上每天都会打开,看看评论之类

B.每周一次

C.看心情

D.一般都是照镜子,不看博客

 

9、你近期对自己博客域名什么感受?

A.想搞到一个 .COM 的域名

B.如果域名能再短几个字符就更好了

C.今年才换双拼域名了,明年再看看

D.目前挺好,没想法:没啥想法。

 

10、你每天都会看网站的流量统计吗?

A.每天看几次,今天又多了100PV

B.每周回顾,看看流量趋势

C.记得就看看:偶尔看看。

D.没有搞流量统计,都是浮云

 

11、你通过博客的广告赚到钱了吗?

A.有,能覆盖建站费用

B.有,但付出大于收入

C.没考虑通过博客流量赚钱:暂时没考虑

D.拒绝广告,保证阅读体验

 

12、你去浏览别人的博客/网站主要为什么?

A.学习别人分享的知识:友链多是生活型博客,看看别人活的咋样。

B.搬运别人的内容

C.看看别人怎么装修博客,自己也抄一下,感觉都比自己的好

D.不爱看别人博客,自己爱写啥写啥

 

13、看到别人分享了一篇文章,你打开第一反应是什么?

A.哇,这域名真不错,怎么我没想到

B.哇,这网站速度真快,图片延迟加载丝滑

C.哇,这程序/主题不错,我也要抄一抄/留言问问哪里搞的

D.看看文章内容:因为打开就会看到设计,所以一般都是先看到设计再看到内容。但是我认为内容更重要。

 

14、你觉得博客哪方面更重要?

A.域名

B.服务器

C.主题

D.内容:上一条回答了。

 

15、近期通过写博客有哪些新收获?

A.知识面有拓展

B.认识了新朋友

C.写作水平提升

D.通过知识变现

基本上都有。

我的miniFlux主题

最近Follow大火,虽然也在使用,不过我还是更喜欢miniFlux这种简单的阅读器。

之前从 yarr 切换到 miniflux 是因为 木木 大佬推荐的 miniflux 主题 Miniflux-Theme-Reeder,不过当时用了一段时间后发现各种不适,最后还是切换会默认主题,用了一段时间发现其实默认的miniflux已经非常好用了,不过需要做一点美化,所以简单做了一下调整,这里分享一下我的miniflux主题。

项目地址:https://github.com/rebron1900/mini-miniflux

主题就叫 mini-miniflux ,复制项目中 theme.css 文件中的样式或直接复制下面的css到miniflux中的 custom css 中即可。

:root {
    --font-family: "Noto Serif SC", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
    --body-color: #efefef;
    --body-background: #222;
    --hr-border-color: #555;
    --title-color: #aaa;
    --link-color: #aaa;
    --link-focus-color: #ddd;
    --link-hover-color: #ddd;
    --link-visited-color: #f083e4;
    --header-list-border-color: #333;
    --header-link-color: #ddd;
    --header-link-focus-color: rgba(82, 168, 236, 0.85);
    --header-link-hover-color: rgba(82, 168, 236, 0.85);
    --header-active-link-color: #9b9494;
    --page-header-title-color: #aaa;
    --page-header-title-border-color: #333;
    --logo-color: #bbb;
    --logo-hover-color-span: #bbb;
    --table-border-color: #555;
    --table-th-background: #333;
    --table-th-color: #aaa;
    --table-tr-hover-background-color: #333;
    --table-tr-hover-color: #aaa;
    --button-primary-border-color: #444;
    --button-primary-background: #333;
    --button-primary-color: #efefef;
    --button-primary-focus-border-color: #888;
    --button-primary-focus-background: #555;
    --input-border: 1px solid #555;
    --input-background: #333;
    --input-color: #ccc;
    --input-placeholder-color: #666;
    --input-focus-color: #efefef;
    --input-focus-border-color: rgba(82, 168, 236, 0.8);
    --input-focus-box-shadow: 0 0 8px rgba(82, 168, 236, 0.6);
    --alert-color: #efefef;
    --alert-background-color: #333;
    --alert-border-color: #444;
    --alert-success-color: #efefef;
    --alert-success-background-color: #333;
    --alert-success-border-color: #444;
    --alert-error-color: #efefef;
    --alert-error-background-color: #333;
    --alert-error-border-color: #444;
    --alert-info-color: #efefef;
    --alert-info-background-color: #333;
    --alert-info-border-color: #444;
    --panel-background: #333;
    --panel-border-color: #555;
    --panel-color: #9b9b9b;
    --modal-background: #333;
    --modal-color: #efefef;
    --modal-box-shadow: 0 0 10px rgba(82, 168, 236, 0.6);
    --pagination-link-color: #aaa;
    --pagination-border-color: #333;
    --category-color: #efefef;
    --category-background-color: #333;
    --category-border-color: #444;
    --category-link-color: #999;
    --category-link-hover-color: #aaa;
    --item-border-color: #666;
    --item-padding: 15px;
    --item-title-link-font-weight: 900;
    --item-status-read-title-link-color: #666;
    --item-status-read-title-focus-color: rgba(82, 168, 236, 0.6);
    --item-meta-focus-color: #aaa;
    --item-meta-li-color: #ddd;
    --current-item-border-width: 2px;
    --current-item-border-color: rgba(82, 168, 236, 0.8);
    --current-item-box-shadow: 0 0 8px rgba(82, 168, 236, 0.6);
    --entry-header-border-color: #333;
    --entry-header-title-link-color: #bbb;
    --entry-content-color: #999;
    --entry-content-code-color: #fff;
    --entry-content-code-background: #555;
    --entry-content-code-border-color: #888;
    --entry-content-quote-color: #777;
    --entry-content-abbr-border-color: #777;
    --entry-enclosure-border-color: #333;
    --parsing-error-color: #eee;
    --feed-parsing-error-background-color: #3a1515;
    --feed-parsing-error-border-style: solid;
    --feed-parsing-error-border-color: #562222;
    --feed-has-unread-background-color: #1b1a1a;
    --feed-has-unread-border-style: solid;
    --feed-has-unread-border-color: rgb(33 57 76);
    --category-has-unread-background-color: #1b1a1a;
    --category-has-unread-border-style: solid;
    --category-has-unread-border-color: rgb(33 57 76);
    --keyboard-shortcuts-li-color: #9b9b9b;
    --counter-color: #bbb --entry-content-font-weight: 300;
    --entry-content-font-family: "Noto Serif SC", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
    --entry-content-quote-font-family: var(--entry-content-font-family) --gray-100: rgba(196, 196, 196, 0.1);
    --gray-200: rgba(196, 196, 196, 0.2);
    --gray-500: rgba(196, 196, 196, 0.5);
    --gray-700: rgba(196, 196, 196, 0.7);

    --color-link: #84b2ff;
    --color-visited-link: #84b2ff;

    --body-font-color: #e9ecef;
    --color-hover-link: #589ff2;

    --btn-color: red;
    --btn-bg: blue;

    --icon-filter: brightness(0) invert(1);

    --box-radius: 0.25rem;

    --hint-color-info: #6bf;
    --hint-color-warning: #fd6;
    --hint-color-danger: #f66;
}

* {
    font-family: var(--entry-content-font-family);
    text-shadow: 0 0 .75px var(--gray-500);
}


main {
    .item {
        border-radius: var(--box-radius);
    }
}

@media (max-width: 630px) {
    body {
        padding: 0 1.25rem;
    }
    
    .page-header{
        margin-top: 1rem;
    }

    .header {
        position: fixed;
        bottom: 10px;
        right: 10px;
        #header-menu {
            background-color: var(--alert-border-color);
            padding: 10px;
            border-radius: var(--box-radius);
            list-style: none;
        }
    }
}
.entry-content {
    line-height: 1.8;


    > :first-child {
        margin-top: 0;
    }

    h1,
    h2,
    h3,
    h4,
    h5,
    h6 {
        font-weight: bolder;
        line-height: 1;
        margin-top: 1.5em;
        margin-bottom: 1rem;

        a.anchor {
            opacity: 0;
            font-size: 0.75em;
            vertical-align: middle;
            text-decoration: none;
        }

        &:hover a.anchor,
        a.anchor:focus {
            opacity: initial;
        }
    }

    h4,
    h5,
    h6 {
        font-weight: bolder;
    }

    h5 {
        font-size: 0.875em;
    }

    h6 {
        font-size: 0.75em;
    }

    b,
    optgroup,
    strong {
        font-weight: bolder;
    }

    a {
        color: var(--color-link);
        text-underline-offset: 0.3em;

        &:hover {
            color: var(--color-hover-link);
            text-shadow: 0 0 0.75px var(--color-hover-link);
        }
        &:visited {
            color: var(--color-link);
        }
    }

    img {
        max-width: 100%;
        height: auto;
        border-radius: 0.25rem;
    }

    code {
        padding: 0 0.25rem;
        background: var(--gray-200);
        border-radius: 0.25rem;
        font-size: 0.875em;
    }

    pre {
        padding: 1rem;
        background: var(--gray-200);
        border-radius: 0.25rem;
        overflow-x: auto;
        position: relative;

        code {
            padding: 0;
            background: none;
            color: white;
        }
    }

    p {
        word-wrap: break-word;
        margin-bottom: 1.3rem;
    }

    /** adapter ghost callout cards **/
    blockquote,
    .kg-callout-card {
        margin: 1rem 0;
        padding: 0.5rem 1rem 0.5rem (1rem - 0.25rem);

        border-inline-start: 0.25rem solid var(--gray-200);
        border-radius: 0.25rem;

        color: var(--gray-700);
        font-style: italic;

        word-break: break-all;

        :first-child {
            margin-top: 0;
        }
        :last-child {
            margin-bottom: 0;
        }
    }

    table {
        overflow: auto;
        display: block;
        border-spacing: 0;
        border-collapse: collapse;
        margin-top: 1rem;
        margin-bottom: 1rem;

        tr th,
        tr td {
            padding: 0.5rem 1rem;
            border: $padding-1 solid var(--gray-200);
        }

        tr:nth-child(2n) {
            background: var(--gray-100);
        }
    }

    hr {
        height: 1px;
        border: none;
        background: var(--gray-200);
    }

    ul,
    ol {
        padding-inline-start: 2rem;
        word-wrap: break-word;
    }

    dl {
        dt {
            font-weight: bolder;
            margin-top: 1rem;
        }

        dd {
            margin-inline-start: 0;
            margin-bottom: 1rem;
        }
    }

    .highlight table tr {
        td:nth-child(1) pre {
            margin: 0;
            padding-inline-end: 0;
        }
        td:nth-child(2) pre {
            margin: 0;
            padding-inline-start: 0;
        }
    }

    details {
        padding: 1rem;
        border: $padding-1 solid var(--gray-200);
        border-radius: 0.25rem;

        summary {
            line-height: 1;
            padding: 1rem;
            margin: -1rem;
            cursor: pointer;
        }

        &[open] summary {
            margin-bottom: 0;
        }
    }

    figure {
        margin: 1rem 0;
        figcaption p {
            margin-top: 0;
        }
    }
}

MacType正确注册表全局热替换、渲染尝试

前言

因为Windows系统字体渲染太垃圾,而我又没需求换4k,所以需要用到其他工具对字体进行渲染。而MacType自然是Windows平台的不二选择,我使用的时间估计已经有10年了把?

以前在XP、Win7上都能很完美的渲染,但是Windows10开始陆续出现了一些程序渲染不上的问题,

  • 比如大部分UWP程序
  • 而Chrome也因为安全策略更改也无法正常进行渲染,虽然百分浏览器有使用GDI++的版本,但是所有Emoji会变成黑白色
  • 微信、企业微信 也因为使用了WebView也无法正常渲染

期间在MacType的仓库里发了一个求助issue 为何微信中FontSubstitutes替换雅黑后字体会直接失去所有渲染呢,有回答的人说通过注册表模式可以正常渲染,但是我进行了尝试后死活没有效果,Maple-font的作者也遇到了和我同样的问题 Segoe UI Variable 似乎无法替换

不过昨天因为 Windows11 LTSC发布了,我重新安装系统后对MacType进行了尝试,发现似乎是因为火绒的问题导致无法进行渲染 火绒 6.0 导致 MacType Version 2024.8.15 无法渲染 ,在进行了很多次尝试后发现不单纯是因为火绒的问题,即便在开了注册表模式下有些配置文件依旧无法正常渲染、替换,尝试了很久终于总结了一套对于我来说可行的全局渲染、热替换方案。

方案

MacType安装到C盘下

根据Snow大佬的解释,因为Windows11系统安全的原因在其他目录可能无法加载dll,但是C盘的 Windows 目录和 Program Files 目录除外,所以我们要将程序装在 Program Files 中。

启用火绒或开启白名单

火绒系统加固里在「自动处理」中给下面几个程序开启「关键进程规则」下所有选项,「文件规则」下的「系统任务目录」

启用注册表模式

因为Windows11安全引导的问题,新版本的MacType取消了在设置向导中开启注册表模式的选项,但是可以通过手动导入、设置注册表的形式启用。

  1. 关闭BIOS中的安全引导(Secure boot)选项。
  2. 在MacType的设置向导中设置为「不使用自动加载」
  3. 导入注册表
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows]
"AppInit_DLLs"="MacType64.dll"
"LoadAppInit_DLLs"=dword:00000001
"RequireSignedAppInit_DLLs"=dword:00000000


[HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\Windows]
"AppInit_DLLs"="MacType.dll"
"LoadAppInit_DLLs"=dword:00000001
"RequireSignedAppInit_DLLs"=dword:00000000

配置文件

我之前一直用的自己改的一个配置文件,但是这个配置文件之前我用MacType服务模式时也有一些莫名奇妙的BUG,比如微信中输入框里的字体无法渲染。

因为喜欢比较厚重的字体,这次测试我首先使用MacType服务模式对所有的配置文件进行测试,找出一个渲染风格是我喜欢的配置文件,最后选中了 XMac.LCD.Default.ini 这个配置,然后开启MacType注册表模式不断的修改配置、重启进行测试,最终找到了一个能正常渲染的配置。

在这个配置文件中我只将 Segoe UI Variable 字体热替换成了 Maple UI ,这部分可以根据使用需求进行任意热替换。

; Only for MacType (NOT SUPPORT THE OLD VERSION OF GDI++/HE)
; maxchow@qq.com

[Preview]
Font=Tahoma
Color=$990088
Text=卍 XMac.LCD 默认设置
Size=12
Align=Center

[General]
Name=XMac.LCD.Default
Icon=XMac\XMac.ico,0

;【自动挂钩子进程】
;[0:Disable]  1:Enable
HookChildProcesses=1

;【字体微调】
;0:Normal 1:NoHinting 2:AutoHinting 3:Light+AutoHinting
HintingMode=1

;【抗锯齿方式】
;-1:Disable 0:Normal 1:Light 2:LCD(RGB) 3:LCD(GBR) 4:Light-LCD(RGB) 5:Light-LCD(GBR)
AntiAliasMode=2

;【常规体调整】
;-32:+32
NormalWeight=14

;【粗体字的调整】
;-16:+16
BoldWeight=2

;【斜体字的倾斜角度微调】
;-16:+16
ItalicSlant=0

;【只有在FontLoader=0(在下面)时此项设置才有效。建议选0,选1会占用大量内存,也没看出有什么好处。】
;0:Disable 1:Enable
UseMapping=0

;【Gamma模式开关】
;-1:关闭;0:使用设置的Gamma值(GammaValue);1:使用sRGB的Gamma值;2:不明
GammaMode=0

;【Gamma值】
;GammaMode=0的时候此项设置才有效
;sRGB ≒ 2.2 CT-Default = 1.4
;windows的cleartype的Gamma值是1.4
GammaValue=1.4

;【对比度】
;数字越大越锐利,数字越小越发虚
Contrast=1.0

;【字体轮廓】
;数字越大字体就越粗越黑
RenderWeight=1.0

;【文字边界设置】
;数字越大越强调背景(类似文字边界被侵蚀的感觉),强调背景会有中字体锐利的感觉。适当降低对比度,提高TextTuning,可以使文字周围的颜色看起来“淡一点”
;再加上RenderWeight,把这三个设置的微妙平衡调节好的话,能得到不输给Mac的效果
;0:12

;AntiAliasMode=1时,通过TextTuning分量来调节
TextTuning=0

;AntiAliasMode=2时,通过TextTuning的RGB分量来调节
TextTuningR=0
TextTuningG=0
TextTuningB=0

;【字体加粗模式】
;对NormalWeight和BoldWeight有效
;0:Weight值大时横向加粗,值小时双向加粗,主要是避免Weight值过大时,模式3中在小字号时加粗过度出现斩头的现象;
;1:总是横向加粗;
;2:总是双向加粗,也就是freetype本身的粗体渲染。
;注意:通过加大GammaValue、TextTuning的值,可以减少Weight值过大的造成的效果问题,即可以在BolderMode=2时也可以避免转接头
BolderMode=0

;【字体的载入方法】
;(注意是载入方法,不是渲染方法)
;0:用freetype载入字体
;1:用windows的绘图核心载入字体
;很多freetype的设置需要此项选0才能生效
FontLoader=0

;【字体链接】
;当FontLoader=0时
;0:什么都不做;
;1:使用注册表里的字体链接设置,通过FreeType的字符表进行查找
;2:使用注册表里的字体链接设置,使用Windows的转换函数直接查找
;当FontLoader=1时
;会保持启用的状态,但是所有字体链接的处理会交由WIN32API完成。
FontLink=2

;【字体替代】
;当FontLoader=0时
;0:什么都不做;
;1:安全替换方案(兼容性较好),根据以下FontSubstitutes的配置进行替换;
;2:完全替换方案(效果较全面,如替换后出现乱码,请尝试使用1),根据以下FontSubstitutes的配置进行替换;
;当FontLoader=1时
;会保持启用的状态,但是所有字体替代的处理会交由WIN32API完成
FontSubstitutes=2

;【让freetype处理的最大的字体尺寸,单位是像素,0的话就是全尺寸】
;交付 gdi++ 处理的最大尺寸(单位为像素)
;0-2147483647
MaxHeight=0

;【阴影设置】
;格式:水平偏移,垂直偏移,深色文字的阴影透明度(0-100),深色文字的阴影颜色,浅色文字的阴影透明度(0-100),浅色文字的阴影颜色
;颜色值格式:BBGGRR 
;Shadow=1,1,30,FFFFFF,20,000000

;【液晶显示器的优化配置】
;[0:None]  1:Default  2:Light  16:Legacy
LcdFilter=2

;【微调LcdFilter】
;LcdFilter>0,且AntiAliasMode>-1时,该微调才生效
;这5个值分别代表文字笔划中从左到右的笔划浓度,最小为0,最大为255
;注释掉这个参数则恢复使用
;LcdFilter=1,默认值为“16,64,112,64,16”
;LcdFilter=2,默认值为“0,85,86,85,0”
;LcdFilterWeight=20,80,120,80,20

;【字体缓存、内存的设置】
LoadOnDemand=1
CacheMaxFaces=256
CacheMaxSizes=33554432
CacheMaxBytes=67108864
EnableKerning=0

;【强制使用某一个字体】
;无视所有其他字体,全系统的字体都会被这里设置的字体代替
;ForceChangeFont=华文中宋

;【优先使用点阵】
;小于指定字号的文字将优先使用内嵌点阵
MaxBitmap=0

; DirectWrite 支持;DirectWrite support
DirectWrite=1

[DirectWrite]
;渲染模式,0=默认,1=锯齿,2=经典,3=经典自然,4=自然,5=自然对称,6=字体原型
RenderingMode=6
;Gamma值,不写则根据上面的GammaValue自动计算
GammaValue=1.5
;对比度,默认为1
Contrast=1.0
;抗锯齿程度,默认1
ClearTypeLevel=1

[Individual]
;【单独设置的字体】
;格式=Hinting, AAMode, NormalWeight, BoldWeight, ItalicSlant, Kerning
XSong=,,28,0,,
XSong SimSun=,,28,0,,
XSong NSimSun=,,28,0,,
XSong Sharp=,,28,0,,
XSong Sharp.SimSun=,,28,0,,
XSong Sharp.NSimSun=,,28,0,,
XSong Harmony=,,28,0,,
XSong Harmony.SimSun=,,28,0,,
XSong Harmony.NSimSun=,,28,0,,
XSong Classical=,,28,0,,
XSong Classical.SimSun=,,28,0,,
XSong Classical.NSimSun=,,28,0,,
XSong Tradition=,,28,0,,
XSong Tradition.SimSun=,,28,0,,
XSong Tradition.NSimSun=,,28,0,,

宋体=,,28,0,,
新宋体=,,28,0,,
SimSun-ExtB=,,28,0,,
仿宋_GB2312=,,28,0,,
仿宋=,,28,0,,
华文仿宋=,,28,0,,
华文宋体=,,28,0,,
华文中宋=,,28,0,,
Batang=,,28,0,,
MingLiU=,,28,0,,
MingLiU-ExtB=,,28,0,,
PMingLiU=,,28,0,,
PMingLiU-ExtB=,,28,0,,
PMingLiU_HKSCS=,,28,0,,
PMingLiU_HKSCS-ExtB=,,28,0,,
MS Mincho=,,28,0,,
MS PMincho=,,28,0,,

[Exclude]
;【除外的字体】

[ExcludeModule]
;【不渲染的程序,但仍会加载DLL】

[ExcludeSub]
;【排除不替换字体的程序】
;以下程序不会被进行字体热替换
;===== Office =====
;EXCEL.EXE
;POWERPNT.EXE
;WINWORD.EXE

[UnloadDll]
;【不渲染的程序,同时完全不加载DLL】
fontview.exe
MainType.exe
AxureRP.exe
FontCreator.exe
FontCreator.cn.exe
notecaselauncher.exe
notecase.exe
gnucash-bin.exe
bleachbit.exe
Fontforge_TC.exe
Fontforge_SC.exe
Fontforge_EN.exe
NexusFont.exe
dwm.exe

[FontSubstitutes@firefox.exe]
;【字体替代】
;系统在调用“=”前面的字体时会忽视“=”前面的字体,而去直接调用“=”后面的字体
;Fixedsys=XHei NSimSun
SimSun=XHei SimSun
@SimSun=@XHei SimSun
NSimSun=XHei NSimSun
@NSimSun=@XHei NSimSun

[FontSubstitutes]
;【字体替代】
;系统在调用“=”前面的字体时会忽视“=”前面的字体,而去直接调用“=”后面的字体
Segoe UI Variable=Maple UI
Segoe UI Variable Small Light=Maple UI
Segoe UI Variable Small Semilight=Maple UI
Segoe UI Variable Small=Maple UI
Segoe UI Variable Small Semibold=Maple UI
Segoe UI Variable Small Bold=Maple UI
Segoe UI Variable Text Light=Maple UI
Segoe UI Variable Text Semilight=Maple UI
Segoe UI Variable Text=Maple UI
Segoe UI Variable Text Semibold=Maple UI
Segoe UI Variable Text Bold=Maple UI
Segoe UI Variable Display Light=Maple UI
Segoe UI Variable Display Semilight=Maple UI
Segoe UI Variable Display=Maple UI
Segoe UI Variable Display Semibold=Maple UI
Segoe UI Variable Display Bold=Maple UI
Segoe UI=Maple UI
Segoe UI Semilight=Maple UI
Segoe UI Semibold=Maple UI
Segoe UI Black=Maple UI
Segoe UI Light=Maple UI

沉默的霸凌施暴者们

💡
本文包含部分关键剧情剧透,请谨慎阅读。
每个人都是施暴者

等了这么久,默杀终于上流媒体平台了。

恰逢假期,家里也来了客人,中午吃了饭便和家人们一起认认真真看看完了这部电影。

不似一些其他电影对内在观点输出的含蓄,默杀这部电影则是非常直给。而标题「沉默的霸凌施暴者们」是我总结的默杀这部片子导演想传达的观点。是呀,「默杀」这个名字和电影封面上每个人的状态不正是最好的阐述吗,同时电影中非常多的直给镜头都在赤裸裸展现。

施暴者

在影片的前半部分间断呈现的「霸凌」画面中,我们能看到所有相关人员的冷漠旁观,而他们都是这场悲剧的酿造者。

首先那几个施暴的女同学自是不必说,罄竹难书,说她们是未进化的动物动也不足为过,其次从校长,普通的同学,校园保安,方老师,房东,这些人在事件发生之后的选择及冷漠更是成了帮凶。

在看这部影片的时候我的姨妈恰好讲了一个这样的故事,

她在商场经营了几家服装店,这几家服装店都处于商场内,而门店旁边都是另外的老板开的娃娃机店,因为现在的娃娃机店都是自助式,没有营业员,所以平时需要有人打扫卫生,整理娃娃。我姨妈便和对方商量了,说承包下他们的这些工作,每天负责打扫的人给于50元补贴,其实我小姨的想法是看工作简单,想给员工捞些外快,她一分钱也没有赚。

因为工作其实非常简单,每天去一次,简单扫一下地,重新补充一下空掉的娃娃机就好了,其中一个门店有一个姐姐认为的确很简单,所以每天都积极的干这个工作,这也影响了同门店的其她同事,最终大家都抢着做这项工作。而另外一个门店有人则觉得工钱太少,工作很麻烦,导致没有一个人愿意去做,然后那个店的承包工作只能无奈放弃了。

这个小故事中两个门店所表现的选择不恰好贴合了默杀这部电影的故事内核吗?

如果故事中的这些人,能和第一个门店中的姐姐们那样,这些人中哪怕只有一个人能放下冷漠、事不关己的心态做出正确的选择,这个故事的结尾至少都不会这么悲剧。

因果

其次是中国儒释道三家观点中时常提到的「因果」。

在这个短短的故事中,所有人都在种下自己的因,也都纷纷吃下了由这个因得出的不同程度的「果」。

  • 施暴的女学生种下霸凌的因,得到了被在福报复杀害的「恶果」。
  • 校长、钟晓晴、李涵、警察戴国栋种下教育无方的因,得到了失去女儿、儿子变成偷拍狂的果。
  • 女房东种下事不关己的因,得到被挟持,绑架的果。
  • 方老师种下谅解、「她们还能被救赎的因」,得到被挟持、伤害的果。
  • 陈明章强奸9岁女儿、家暴老婆,得到被女杀死的「恶果」。
  • 在福杀复仇,得到付出性命的果。
另外小涵最后在监狱的画面我认为是「中国电影审核哲学」的影响下造成的,不纳入这个清单里,我更相信的是小涵已经逃离。

白鸽

电影中多次出现了白鸽,我个人认为这是影片对正义、正确的具象化。

最后在天台上,林在福完成了自己最后的使命,看着这起事件的所有相关人员围在一起,他们好似忽然将自己曾经的冷漠抛的无影无踪,背靠着无可匹敌的正义对着他口诛笔伐。

在福露出了嘲讽的微笑,转身没有丝毫犹豫的跳下了高楼,坠落的半空中,数十只白鸽飞起,好似托着林在福一般,而这在传统港片中只有在正义的英雄出场才会有白鸽飞舞的场景。

而这里,是为 英雄 来福送别。

司法体系的缺失让这一切发生,所以在福已经不相信法律,也认为自己的作为没有必要受到法律制裁,他所做一切都是发自内心,对于夺取那几个女孩子的性命心中自是也有愧疚,所有源自存从自己内心的价值观以死偿命,但是他不愿接受这已经畸形的司法体系的审判,和这些施暴者们的口诛笔伐,所以遵从自己真正的正确,自我了结。

最后

校园霸凌是全球所有学校都存在的普遍现象。

我可以肯定在所有学校,至少我的小、初、高、大的学业生涯中全都有。所有班级中都有那么一两个弱势者会或多或少的被班里的坏孩子、强势者欺凌,虽然大多数情况下并没有影片中表达的这么极端,但是对那些孩子所造成的伤害依旧是不可磨灭的。

我们作为这些旁观之人,如果都能伸出援手,这些悲剧可能就不会发生了,至少不会让一切都变得没有余地...。

想起电影「第二十条」中的一句台词:「所有正确的事情都是有代价的,但不能因为有代价就不去做。」 

愿世上处处都有白鸽飞舞...。

强心针

在写完 馄饨、饺子和抄手 一文后发现回忆是真的很美好。

人们总说要向前看,向前看,未来会更加美好!所以总是回忆过去是不是说明当下过的并不好呢?

虽然这个论点并不充分,但是至少我这段时间想把自己归在这类情况中去。

人们在越是在困苦、迷茫、无助的时刻越是想下意识通过这些美好滋润干涸的心河,引导我们行进的方向,人在不断的进化过程中演化出了这种能力,虽然这种做法在有些人看来确实有些「鸵鸟效应」、「掩耳盗铃」,不过对我来说却也不全是望梅止渴的行为。

中国有句俗语「人生不如意十之八九」这个说法,好在还是有这「一、二」可以追求,因为任何事情都没有绝对的说法,他就像没开盒的「猫」,我们得给自己留一条退路,很多极端的人正是因为把这最后一条路也断了,而做出后悔一辈子的决定。

当然,我们也不能一直沉静在这些记忆里不出来。

毕竟和强心针一样,回忆所带来的美好是有时效的,在那个激励后的短暂余韵里,也是我们该重新迈步向前的时候。

然后,努力生活。

为了下一个「可能」的止步不前,生产另一支强心针。

两款CMS系统体验

因为Ghost只提供文章的 HTML 代码,不提供 Markdown 的问题导致我曾经一度想更换CMS系统。但是将近800篇文章的转移工作十分麻烦,而且之前也尝试过用一些工具处理,无一都失败了,问题多是因为古早之前的很多文章存在格式问题,无法通过一些 HTML to Markdown 工具的转换规则,所以这个事也就一直没下定决定弄。

不过不影响我寻找下一位有动力让我这么做的CMS继任者。

最近发现了两款CMS工具,分别是:

  • KeyStatic: 之前有在周报 [[🤣JUSTFUN 周刊:第13期]] 中推荐过,一款基于Github的CMS系统。
  • PocketBase:一款用Go写的非常轻量、高速的CMS工具,使用简单,扩展性强。

KeyStatic

基于Github的CMS系统有很多,我所知道的就不下四五款,比如有: statamic/cmskeystonejs/keystone 等。

在周报里我也提到过,我是在Astro的CMS推荐列表中看到了KeyStatic,并在昨天花了一点时间体验了一下,整体体验还是不错的,总结如下。

  • 字段:可以进行丰富的字段配置和自定义,但是字段配置需要通过配置文件进行,而且可选项有点多,初上手会比较麻烦,好在官方提供了一些模板库,可以直接超。
  • 同步:可以基于本地也可以基于Github,基于Github模式的话每次增加文件文章后需要手动提交pull request,在页面上有个按钮,点击能跳转到Github,然后再进行后续操作,其实还是比较麻烦的。
  • 部署:官方提供了一些Astro、Next.js等集成,可以将网站、管理面板都部署在Vercel、Netlify、Clouflare上,对白嫖党来说是还是比较友好的。而且因为集成了SSG工具,网站也能同一时间进行更新发布。
  • 文件:
    • 文章:撰写的文章会在项目里生成对应的markdown文件
    • 图片:也会传到Github的内,官方目前提供了一个云服务,后续应该也是会支持S3、R2等服务的。
  • 定制化:后台的定制性非常强,你甚至可以在字段设置里使用你写的Astro组件,不过需要一定的开发知识。
  • API:很丰富,还提供了Nodejs的api库,操作非常方便。
💡
昨天在部署到Vercel后提示链接不安全,在文档里找了半天没发现有相关的说明,后来想起在初始化链接到GitHub时提示了需要设置一个发布网址,当时留空了,所以重新去Github中的App Setting里增加了Vercel链接的Rule后解决。

Poketbase

现在的CMS系统都设计都好强悍呀!昨天刷到蜗牛大佬Star了一个叫 pocketbase 的开源内容管理系统,程序是用Go写的,基于SQLite数据。

管理全在网页上进行,虽然结构简单,但是API、权限、功能拓展什么的都一应俱全,相当哇撒了。

  • 字段:官方提供的字段略少,而且Content内容目前只能保存解析好后的HTML字符串,而且没有自动生成slug。
  • 同步:因为是通过二进制程序直接运行,也有其他开发者提供了Docker版本,就是一个在线服务,所以没有数据同步问题,所以这部分会比较简单,好管理一些。
  • 部署:同上,直接起个Docker就行了,但是并没有集成前端框架,不过有完善的API、权限管理,在SSG中实现一下就好。
  • 文件: 文件可以存在本地,也支持上传到R2等服务上。
  • 定制化:这部分可能会比KeyStatic弱很多,目前文档里没怎么有这方面的介绍
  • API:很完善,接口权限划分的很细。

因为官方没有自带slug生成的功能,所以我跟着官方文档写了个转换脚本,很有意思。

onRecordBeforeUpdateRequest((e) => {
    const pinyin = require(`pinyin`);

    const title = e.record.get("title");
    const titleToSlug = (title) => {
        const pinyinTitle = pinyin(title, {
            style: pinyin.STYLE_NORMAL, // 普通风格
            heteronym: false, // 不使用多音字
        })
            .flat()
            .join("-"); // 将拼音数组转换为字符串并用短横线连接

        return pinyinTitle
            .trim() // 去除首尾空格
            .replace(/[\s]+/g, "-") // 将空格替换为短横线
            .replace(/[^\w\-]+/g, "") // 删除非字母数字和非短横线字符
            .replace(/--+/g, "-") // 替换多个短横线为一个短横线
            .replace(/^-+|-+$/g, ""); // 去除首尾的短横线
    };

    // 示例用法
    const slug = titleToSlug(title);
    e.record.set("slug", slug);
}, "posts");

官方提供的event hook也相当多,能玩出非常多的花样。

目前唯一的疑惑是为啥不提供markdown格式的fields呀?

至于换不换CMS,还是准备再蹲蹲。

如何快速解析HTML里的WikiLink?

前几天在 你好Astro! 中提到想把Obsidian的文章集成在博客中来,并实现CMS和Obsidian之间通过Wikilink的互相引用。

不过因为Ghost的API只提供了文章的HTML字符串,Remark衍生的WikiLink插件肯定都是无法使用的,而且目前Github上能找到的WikiLink相关的案例基本上都是整对于Markdown语法去解析的,对于我来说基本上没什么参考价值。

所以我只能走另外的路子了,不过方法无非就是以下几种

  • 正则过滤替换
  • 生成虚拟DOM处理

正则过滤

昨天晚上我倒是找到了 loveminimal 大佬写的一个姑且能用方案,但是这段代码有个不大不小的缺陷,就是正则会将所有标签内的 [[]] 内容都进行解析转换,所以 pre 代码块内的内容也会被解析成Wikilink,这意味着会无法控制的出现在任何你可能不想他出现的地方,所以目前这种方案暂时待选。

const wikilinks = (innerHtml) => {
    let _innerHtml = innerHtml;
    if (_innerHtml.indexOf('[[') > -1) {
        let _re = /!\[\[(([\/\-\.\*\$\&]|\w|\s|[^\x00-\xff])*\.\w+)\s*\|?\s*(\d*)\]\]/g;
        let _str = _innerHtml.replace(_re, '<img src="/$1" alt="$1" width="$3" />');


        // 2. 后匹配替换链接
        let _reLink = /\[\[(([\/\-\.\*\$\:\#]|\w|\s|[^\x00-\xff])*)\|?(([\/\-\.\*\$]|\w|\s|[^\x00-\xff])*)\]\]/g;
        // let _strLink = _str.match(_reLink);
        // let _strLink = _str.replace(_reLink, '<a href="$1">$3</a>');
        let _strLink = _str.replace(_reLink, (val) => {
            val = val.replace(/[\[\]]/g, '');
            let _arr = val.split(/\s*\|\s*/);
            let _relLink = _arr[0];
            let _desc = _arr[1] ? _arr[1] : _arr[0];

            // 检查链接描述是否包含 #锚点,形式有(我们假设当前文章名称为 test ,它有一个章节 ttt):
            // - 2.1. 孙子兵法#军争篇 - 此类可以正常识别
            // - 2.2. cpu-是如何制造出来的#18.-等级测试 - https://example.com/cpu-是如何制造出来的#18.-等级测试 ,
            //        此类锚点中包含特殊符号 `.` ,在新标签中打开,且无法正确定位到锚点
            // - 2.3 test#ttt - https://example.com/test#ttt 默认会在新标签页中打开,需要优化为在当前页面滚动
            // - 2.4  #ttt - 不能正常,会翻译为 https://example.com/#ttt ,丢失了当前页面路径
            let _idx = _desc.indexOf('#');
            if (_idx > -1) {
                // 2.4
                if (_idx == 0) {
                    _relLink = location.pathname.slice(1) + _desc;
                } else {
                    // 2.3
                    _relLink = _desc.replace('#', '/#');

                    // 2.2
                    _relLink = _relLink.replace(/[\.\、]/g, '');
                }
            }

            // console.log(_arr);
            // console.log(_desc);
            // return `<a href="${_arr[0]}">${_desc}</a>`
            return `<a href="/${_relLink.replace(/\s/g, '-').toLowerCase()}">${_desc}</a>`;
            // });
        });

        return _strLink;
    }
};

虚拟化DOM

这个方式就是在Nodejs里将HTML字符串虚拟化成DOM,再去对DOM进行操作,虽然理论上可行的,但是效率应该是极低的,这个我是不愿意使用的,所以直接排除掉了。

没有其他方案了吗?

真的没有其他方案了吗?

我今天这样想着,抱着试一试的心态换着各种关键字在Github上搜索,运气不错的让我找到了这个个库:html-parse-stringify ,他可以将html字符串快速抽象成一个AST语法树,我的想法是通过遍历这个语法树来找出文本内容,再反序列化成html字符串不就好了?

所以我试着按着我的思路写了个Demo,好像的确可行,而且还可以配置那些情况下包含的 [[]] 不进行渲染。


import HTML from 'html-parse-stringify';
var ast = HTML.parse(page?.html); // [!code highlight]
let fatherList = '';
function findTextNodes(node, text) {
    let result = [];
    fatherList += text; // 记录节点路径

    // 检查当前节点,并做wikilink的识别和路径排除,这里做排除名单应该很方便
    if (node.type === 'text' && node.content.startsWith('[[') && !fatherList.endsWith('precode'))   // [!code highlight]{
        node.content = node.content.replace('[[', '<a>test').replace(']]', '</a>');
        result.push(node);
    }

    // 如果有子节点,递归遍历
    if (node.children) {
        node.children.forEach((child) => {
            result = result.concat(findTextNodes(child, node.name));
        });
    }

    return result;
}

const result = [];

ast.forEach((node) => {
    const temp = findTextNodes(node, node.name);
    let fatherList = '';
});

const html = HTML.stringify(ast);

这是我目前能找到的最优方案了,不过这个方案应该也是偏向于解析成dom,不过这个库没有进行更多消耗资源的操作,应该是比传统的解析库性能更优秀的。

各种标签测试

WikiLink测试

[[note1]]

[[note2]]

[[note3]]

[[note4]]
  • [[note5]]

[[note6]]

[[note7]]

[[note8]]

@1900’Blog
All work and no play makes Jack a dull boy
@1900'Blog

https://cms.1900.live/ni-hao-astro/

测试一下文字包裹[[note9]]后的解析情况

[[note9]]

实际引用测试

已经初步完成联动,目前的考虑的策略顺序如下,

博客中

  1. 优先从CMS中去匹配文章 Title 一致的文章
  2. 如果博客内没有一直的则去Obsidian中匹配文件Title字段一致的文章
  3. 如果有多条数据只返回最前面一条
  4. 如果都没有则不进行转换

Obsidian中

  1. 优先匹配Obsidian中Title一致的文章
  2. 如果没有则去博客内匹配Title一致的文章
  3. 如果有多条数据只返回最前面一条
  4. 如果都没有则不进行转换

如Obsidian中的 About 这篇文章,在博客中以 [[About]] 写入,最终文章内就会解析成 [[About]] 。

因为Astro的本地路由都是基于网站根目录,所以我们只要将博客和Obsidian的文件分别进行静态生成,并在生成过程干涉 [[]] 的转换成 <a href=''><a/> 标签,然后给出正确的slug,便可以很方便的实现SPA形式的页面跳转。

完。

🤣JUSTFUN 周刊:第14期

一图

骑行路上

1. 种的辣椒 2. 骑行路上

流水账

  • 2024年9月2日:忽然对Astro的语法有了新的理解,也许我可以做到?
  • 2024年9月2日:如果地球是个学校,各个国家是上学的孩子,那么老鹰就是校园霸凌的恶霸。
  • 2024年9月2日:自由的含意是:我能自由的干你,你不能还手。
  • 2024年9月2日:我真是个傻逼,之前吐槽说Ghost文章丢失了,刚刚忽然发现特么是我写成Page了,压根不在Posts列表里。
  • 2024年9月4日:路永远在自己的脚下。
  • 2024年9月4日:正式靠别了Tai,改用ActivityWatch
  • 2024年9月4日:11ty-book有人点了Star,开心
  • 2024年9月4日:成年人变得更成熟时需要同时学会两件事情:第一,你想要的东西,懂得主动提出要求,不要让别人猜;第二,你提出来的要求,无论别人接受还是拒绝都要尊重别人的决定。这两者看起来不可调和的矛盾,其实是你自己对于被拒绝的恐惧,因为恐惧所以要么害怕提出要求,要么要求被拒绝后转化为厌恶情绪。
  • 2024年9月5日:开始移植11ty-book到astro上,新主题:astro-book
  • 2024年9月5日:我以后的孩子英文名可以叫 Dura ?是柔软、纯洁的意思,好像是乌克兰语?
  • 2024年9月11日:终于完成了Astro的所有功能移植。

读了、看了、听了

  • 2024年9月1日:看了 [[花束般的恋爱]] ,对两性相处有了新的看法,可以说是教科级别的两性相处案例。
  • 2024年9月2日:看了[[因果报应]] ,可惜是五分钟解说版本版本,后悔。
  • 2024年9月6日:在微信听书听王阳明——知行合一有声书
  • 2024年9月6日:开始看 [[心流]]
  • 2024年9月10日:看了 [[从21世纪安全撤离]],想起我小时候想的那些特异画面就是这部电影的风格,独一无二的味道,还是以前李献计一样的味道。

发现

一个动作改善脖子前倾

#知识 来自 x

在推上看到一个改善脖子前倾动作的视频,试了一下挺有效果,不过要长期坚持才行。

地址:x.com

极简主题参考

#设计 来自 ?

偶然发现的一个博客平台,他们默认提供的主题风格多是纯文字极简风格,简洁大气,找不到设计灵感的时候可以参考一下。

地址:Templates - Blot

足不出户看全球

#网站 来自 ?

偶然发现的一个全球范围热门景点的在线视频监控网站,不过国内的公开摄像头比较少。

网址:实况摄像头 马尔代夫-库雷杜 | SkylineWebcams

TV版Surfboard

#工具 来自 Github

优化TV操作的SurfBoard,懂的都懂。

项目地址:GitHub - getsurfboard/surfboard

GitHub - getsurfboard/surfboard

基于Bilibli音源的在线音乐播放器

#工具 来自 Github

一款基于bilibili的在线音乐播放器。

Eno Music 是一个基于 bilibili 的音乐播放器,完全改变了你在该平台上的体验。它重新构想了 bilibili 作为一个音乐平台的样子,并提供了独特而沉浸式的体验。

项目地址:GitHub - cloudflypeng/eno-music

3D打印桌面手机支架

#工具 来自 Youtube

一个可以通过3D打印制作的多功能桌面手机支架,emmm,应该说桌面时钟更贴切?

各种加载样式

#网站 来自 ?

无意间找到的一个拥有各种五花把门加载效果的CSS网站,在线预览,及拿及用。

网站地址:The Classic CSS Loaders Collection

你好Astro!

选型往事

在2024年3月份,我正式将Ghost博客作为无头CMS使用,并搭配SSG工具生成静态页面 。

其实在当时的技术选型时其实已经考虑过Astro,当时的Astro的版本号还是3.x,不过因为自己的技术不够成熟,编码思路还一直是Ghost时期的handlers这种模板结构,尝试多次无果后最终还是选择了11ty作为网站的生成工具,并撰文 新主题11ty-book 分享了当时的成果。

积累

不过这段时间我都有在不间断的重新查阅Astro的文档,思考该如何用Ghost搭配它,希望能用上这个热门的网站生成器。

事情的转折点是前几天发现 Astro-remote 这个插件,它提供的组件能加载HTML字符串或远程Markdown文件,渲染成网页内容,并在这个过程中还可以对HTML元素使用Astro的组件进行替换,这意味着可以不再用之前在11ty中那样,用 JSDOM 这个库通过通过虚拟DOM进行操作 eleventy/transforms/parseContent.js

之后我便根据自己的思路写了几个Demo,以前困扰的几个点似乎都能解决

  • 可以通过这个插件的容器替换功能,使用Shiki、Prism等工具的代码高亮
  • 可以自定义容器,之前 hugo-book中的Columns Expand 等功能都机会实
  • 甚至可以把之前Obsidian 数字花园整合进一个网站来。

新开始

所以没有犹豫,在9月2号就开始了移植工作。

这段时间,我边看文档边学习、实现、思考,得益于我在11ty-book上花费的时间和对Node.js逐渐了解,以前看不懂的Astro代码忽然像是顿悟般的或霍然开朗,明白了组件形式和之前11ty时传统模板形式的区别,和意义。

Astro的开发体验的确很棒,这也是我之前在选型11ty之后依旧还对Astro念念不忘的原因。

高速的迭代频率,优秀的设计语言和活跃的开发群体让这款工具一直充满着活力,得益于官方全站的中文文档,而且手册十分详尽,让我在实际开发过程中能少走了不少弯路。

终于在昨天,一共历时七天,我终于将11ty-book的功能移植了个80%作用,并提交了第一个正式版本到了Github。并在昨天晚上正式切换到了Astro版本的huog-book。

开源地址:https://github.com/rebron1900/astro-book/

不过目前以前还有一些问题还未来得及处理,现在还有一些构思等待实现:

  • 整合Obsidian
  • 实现Ghost CMS和Obsidian之间互相的WikiLink引用
  • 全站搜索
  • 影集功能
  • 文章内丰富的卡片引用,如NeoDB,CodePen,视屏,音乐等。
  • 试试用SSR实现文章的密码访问
  • ...以及其他无数种可能

结束语

发现自己这些年在折腾博客这条路上越走越远。

从以前的博客大巴只会转发文章,到后开始学习编程,开始SelfHost Wordpress,开始满世界找主题、找插件优化博客,到现在我已经能流畅使用JavaScript,Node.js、Python等工具进行代码开发,之前还自己实现了一个在博客显示博主状态服务 将博主PC上使用的应用信息实时显示到博客,体验了一把Socket和服务端的开发,那时候还狂妄的想着「自己是不是已经可以前后端一把梭了?」,哈哈。

虽然13年前毕业后我没有从事IT工作。

但是我热爱编程,我喜欢编程,我喜欢互联网蓬勃的生命力。并且不同于需要拿找饭吃,我更多是处于喜欢、兴趣使然,基于这个初心去学自己感兴趣的技术,并在折腾的路上学以致用。

我很满足,也很开心,希望我20年后依旧初心不改。

Thks。

❌