碎碎念

陪她去流浪 新建

Go 语言多少有点儿大病,以至于这种东西需要我自己实现,以至于我需要用我三脚猫功夫的模板/泛型才能实现。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// https://en.wikipedia.org/wiki/IIf
// https://blog.twofei.com/716/#没有条件运算符
//
// 用法:
//      a := IIF(b, c, d)
// 类似于:
//      a := d; if b { a = c }
// 其它语言写法:
//      let a = b ? c : d;
func IIF[Condition ~bool, Any any](cond Condition, first, second Any) Any {
	if cond {
		return first
	}
	return second
}

是谁当初说不需要三元/条件运算符的?我打断他的 🐶 腿。

我真是个天才。

1条评论 页首

如果评论中带列表(list),那么当前的列表会顺序递进样式:比如第一层用实心圆点儿,第二层用空心圆点儿,第三层用方块儿。这个行为是浏览器默认的。

但是我的评论列表也是用的 <ul>/<ol> 来描述,这种嵌套关系会导致评论内容中的列表表现不一样,所以需要重置列表项的样式(list-style-type)。做法也非常简单:

1
2
3
ul, ol {
    list-style-type: initial;
}

解决完这个问题后,我突发奇想:Markdown 是支持多种 Markers 符号官方的(就是用哪个符号表示要开始写列表了,比如:- + * . )),那么我为啥不保留原始的这个符号呢,这样更所见即所得?于是我就打开了 list-style-type 一看,竟然示例中第一个就是自定义样式🤣,这下不得不学了……

如我想像中的那样简单,常用的都预置了。但是 1) 2) 这种没有。我用 @at-rule 自己写了一个 marker 生成器:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// https://developer.mozilla.org/en-US/docs/Web/CSS/@counter-style
@counter-style parenthesis {
	system: numeric;
	symbols: "0" "1" "2" "3" "4" "5" "6" "7" "8" "9";
	suffix: ") ";
}
ul, ol {
	&.marker-minus          { list-style-type: "- ";        }
	&.marker-plus           { list-style-type: "+ ";        }
	&.marker-asterisk       { list-style-type: "* ";        }
	&.marker-period         { list-style-type: decimal;     }
	&.marker-parenthesis    { list-style-type: parenthesis; }
}

然后就是在文档渲染时解析 Markdown 的过程中判断一下 Marker 的类型并增加类名即可:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
var knownListItemMarkers = map[byte]string{
	'-': `minus`,
	'+': `plus`,
	'*': `asterisk`,
	'.': `period`,
	')': `parenthesis`,
}

// 保留列表样式。
//
// 只是增加类名,前端通过类名自行决定怎么展示。
func (*_ReserveListItemMarkerStyle) WalkEntering(n ast.Node) (ast.WalkStatus, error) {
	switch typed := n.(type) {
	case *ast.List:
		if class, ok := knownListItemMarkers[typed.Marker]; ok {
			appendClass(typed, `marker-`+class)
		}
	}
	return ast.WalkContinue, nil
}

然后就是一些例子🌰:

如愿

  • 演唱:王菲
  • 作词:唐恬
  • 作曲:钱雷
  • 你是 遥遥的路
  • 山野大雾里的灯
  • 我是孩童啊 走在你的眼眸
  • 你是 明月清风
  • 我是你照拂的梦
  • 见与不见都一生与你相拥
  1. 而我将爱你所爱的人间
  2. 愿你所愿的笑颜
  3. 你的手我蹒跚在牵
  4. 请带我去明天
  5. 如果说你曾苦过我的甜
  6. 我愿活成你的愿
  7. 愿不枉啊 愿勇往啊
  8. 这盛世每一天
  1. 你是岁月长河 星火燃起的天空
  2. 我是仰望者 就把你唱成歌
  3. 你是我之所来 也是我心之所归
  4. 世间所有路都将与你相逢
3条评论 页首

不知道是哪天,为了显得行文更正式一点儿,我特地给“妳”添加到了自定义短语里面,输入vq即可代替原本不是二级简码的vqiy。这样就和输入“你(wq)”一样只需要两笔。

然而,我几乎从来没有打过这个字。因为,没有一个她。

妳

没有评论 页首

在我正在每天使用的电脑的一个很浅的备份目录内找到了7️⃣年前备份的“说说”(就是本碎念功能的前身)。看了下时间点,正好结束在隐藏说说功能前夕。

都是我的过去,为了不丢失,还是给它们“安个身”吧?毕竟也没有别的人这么多年陪着我,只有我的数据。

1
2
3
4
5
6
7
-- MySQL dump 10.13  Distrib 5.5.43, for debian-linux-gnu (x86_64)
--
-- Host: localhost    Database: taoblog
-- ------------------------------------------------------
-- Server version	5.5.43-0ubuntu0.14.04.1

-- Dump completed on 2017-10-08  3:38:08

看起来有些年迈了。

哈哈哈,那时候的“说说”还记录了地点信息的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
DROP TABLE IF EXISTS `shuoshuo`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `shuoshuo` (
  `id` int(20) unsigned NOT NULL AUTO_INCREMENT,
  `content` text CHARACTER SET utf8 NOT NULL,
  `source` text CHARACTER SET utf8,
  `date` datetime NOT NULL DEFAULT '1970-01-01 00:00:00',
  `geo_lat` float(10,6) NOT NULL DEFAULT '0.000000',
  `geo_lng` float(10,6) NOT NULL DEFAULT '0.000000',
  `geo_addr` varchar(256) CHARACTER SET utf8 NOT NULL,
  `comments` int(20) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=208 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

那还是我数据库时间用 datetime 的年代,想想当年好像是写了一整个的 PHP 文件来处理日期/时间📅。

评论就显得比较简单,竟然连作者邮箱都不用留的,随意留言,多么简单而又美好的年代:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
DROP TABLE IF EXISTS `shuoshuo_comments`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `shuoshuo_comments` (
  `id` int(20) unsigned NOT NULL AUTO_INCREMENT,
  `sid` int(20) unsigned NOT NULL,
  `author` tinytext CHARACTER SET utf8 NOT NULL,
  `date` datetime NOT NULL DEFAULT '1970-01-01 00:00:00',
  `content` text CHARACTER SET utf8 NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=36 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
1条评论 页首

2024-05-09.md

待办事项

  • 用节流的方式更改博客的备份时机。现在一直是一天定点备份。有滞后,风险较高。
  • 把代码渲染改成可选项,RSS 不需要渲染。
  • 接入 plantuml
  • 动态更新时间?页面时间 1 分钟前 显示不对不会更新,除非强刷。。
  • 把 taorm 成文
  • api获取文章时删除敏感字段(geo)
  • 我应该复用评论编辑框为文章编辑框,这样就使合并文章和评论的进度又提前了一步。
  • 管理员允许编辑评论者的信息。
  • 预览文章编辑信息。
  • 实现一个前端的编辑距离算法以自动定位没加 name 的 # hashtag。并且生成 toc。
  • 把三元运算符成文。
  • 把 Youtube 加载变懒。
  • 我甚至可以开放编辑✍️功能……
  • 写个debounce,延缓文件刷新。
  • 支持 HEAD 请求,不要返回 404
  • 用 api 更新动态可变数据(文章评论数)
没有评论 页首

这篇关于在二进制位中定位包含 1 的位置的论文1 PDF 《Using de Bruijn Sequences to Index a 1 in a Computer Word》好奇怪,选择的时候一团糊(别说,还挺好看🤩)。导致我 MacOS 的单词快速查看(QuickLookup)功能也彻底废了。

1.jpg

没有评论 页首

为了在前端发一张带图片的碎碎念,写了一天的代码支持在前端上传⏫文件🥵,兜兜转转又回来了。

看了很多上传文件相关的文章,但是都没有采用(fetch + XMLHttpRequest)。
取而代之,用的是我自己的文件上传方案:通过 WebSocket 把 GRPC Stream 代理(双向拷贝)到前端。
这样的话接口就全部统一了。也可以很方便地增加上传进度展示(目前没有加,因为文件小)。

这好像是一个网上很少被提起的方案。

没有评论 页首

Go 的内嵌接口不会自动包含被内嵌的对象实现的接口,所以像下面这样的代码不会如预期的方式工作,不知道为什么,踩坑几次了😫:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main

import (
	"io"
)

type A struct{}

func (a *A) Close() error               { return nil }
func (a *A) Read(p []byte) (int, error) { return len(p), nil }

// A 实现了 ReadCloser
var _ io.ReadCloser = (*A)(nil)

// 无论以何种方式嵌入 A,B/N 都无法完全断言出 A 实现的全部接口。
type B struct{ io.Closer }
type N struct{ any }

func main() {
	var b any

	b = &B{&A{}}
	b.(io.Closer).Close()
	b.(io.Reader).Read(nil)

	b = &N{&A{}}
	b.(io.Closer).Close()
	b.(io.Reader).Read(nil)
}

现实代码是,我包裹了 http.ResponseWriter 通过重写 WriteHeader 的方式以实现捕捉到状态码:

1
2
3
4
type _ResponseWriter struct {
	http.ResponseWriter
	code int
}

然后就发现,我自己的 _ResponseWriter 没有实现 Hijacker 了,导致我的 WebSocket 升级失败。

现实是,很多代码都这样写,并且以为不会出问题。比如:

一直没明白为什么不支持。

2024-6-1 18:05:41

发现了一个 Go1.20 新加入的功能:https://pkg.go.dev/net/http#NewResponseController 可以完成我要的功能。

2024-6-4 07:00:46
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// https://blog.twofei.com/909/
type _ResponseWriter struct {
	http.ResponseWriter
	*http.ResponseController
	code int
}

func (w *_ResponseWriter) WriteHeader(statusCode int) {
	w.code = statusCode
	w.ResponseWriter.WriteHeader(statusCode)
}
2条评论 页首

使用 Rollup 给 js 打包的时候发现它有一个“奇怪”的格式化参数 -f iife。查了一下才知道,像下面这样的代码写法:

1
2
3
(function () {
    /* ... */
})();

有一个专门的名字:Immediately invoked function expression / 立即调用函数表达式

这个概念已经存在很久很久了,只是我今天才知道它有正式的名字,被这样缩写。

我好像很久没看书📖了。有点儿恐怖。

2条评论 页首

2024-05-06.md

待办事项

  • admin/script.js 为什么没有缓存?
没有评论 页首

2024-05-05.md

待办事项

  • 编写统一的重未登录定向处理器。
  • 成文: Grpc stream interceptor 的 wrap context WrapServerStream
  • 把评论列表静态化成一个 json文件。
没有评论 页首

W3C 总是能给我整出恶心的东西,为什么把一个 Date 赋值给 <input type="datetime-local" /> 这么难?这tm是给人用的吗?🤔

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// https://dev.to/kevinluo201/set-value-of-datetime-local-input-field-3435
// getFullYear, getMonth, getDate, getHours, getMinutes all return values of local time.
const convertToDateTimeLocalString = (date) => {
  const year = date.getFullYear();
  const month = (date.getMonth() + 1).toString().padStart(2, "0");
  const day = date.getDate().toString().padStart(2, "0");
  const hours = date.getHours().toString().padStart(2, "0");
  const minutes = date.getMinutes().toString().padStart(2, "0");

  return `${year}-${month}-${day}T${hours}:${minutes}`;
}
const currentTime = new Date()
document.getElementById('start-time').value = convertToDateTimeLocalString(currentTime)

而如果是 Go 的话,只需要下面这样一句就可以了:

1
time.Now().Format("2006-01-02T15:04:05")
4条评论 页首

五一本来是不打算出去玩儿的,节假日都不喜欢,太堵了,堵人堵车。但这次是个例外,是准备去董的家乡茂名旅游一圈。董的相亲对象竟然非常巧合地在高铁临近出发前错过了,于是一行四人,晚上十点,从深圳出发。

从未在节假日高峰开车出游过,没想到到虎门50公里开了4️⃣个小时。而全程不到500公里的路程居然次日早上11点才到。总共驾驶13个小时,除两次充电分别休息差不多半小时外,全程是我开车,是真的累,堵得累。有史以来连续开车最长的一次,不是炫耀。

没有评论 页首

早在 2015 年刚创建本博客程序的时候就有了一个简单的“说说”功能,相对于复杂的文章来说,就是用来发表日常吐槽。

可是本来是我技术起步阶段,就做得非常难用。后来就隐藏了这个功能,再后来就删除了这个功能。不知道以前的数据还在不在,如果在的话,抽空同步上来。

现在你看到的这个“碎碎念”,不再是另外一种单独的载体,而就是一篇普通的、没有标题的文章。之所以这样做,有很多好处:

  • 可以完全复用文章的发表、更新、数据管理工具;
  • 可以完全复用文章的评论功能;
  • 和文章统一链接、统一渲染、统一……

当然,“碎碎念”默认不显示在首页、也不显示其首页评论、也不显示在 RSS 订阅中。

碎碎念不乏有一些比较隐私的内容,我该想一个较好的方案来隐私这部分细节?🤔

5条评论 页首

2024-04-29.md

待办事项

  • 编辑评论时应该带上时间以防止多次更新冲突;
  • p={id} 查询失效了。curl 中看到的。
  • 不要使用 h2、h3 作为大字体显示用。
  • 评论时检查是否仍属于此文章。
  • 规范化用户及ip 的获取过程
  • 把页面重定向到 /page 形式。

技术

这篇 MDN 的 onsise 处理里面提到了一篇 throuttle 和 debounce 的文章,很好。

在这里讨论了博客评论分页如何永久有效的问题:https://github.com/movsb/taoblog/commit/366c66d2841377c2ddf155af7b31e703a6c39ee2

没有评论 页首

2024-04-28.md

待办事项

  • 博客请求记录器;
  • 写一篇关于是否需要 HTML 转义的文章;
  • 文章的更新时间应该是最后评论的时间。
  • 更新评论的评论链接;
  • 更新首页的评论链接;

技术

关于 HTML 在特殊符号是否需要转码的问题。这篇文章写得比如清楚,但是,个人觉得只适合手写的情况。


准备把博客的评论改在后端生成,改了半天发现,我直接把所有评论用 js 返回给前端,前端用 js 渲染出来不就行了吗?妈的,写了半天,浪费时间……🙄

没有评论 页首

2024-04-27.md

待办事项

  • 把通行密钥的使用成文;

技术

终于把拖了几年的通行密钥给上线到博客了,太难了!

没有评论 页首

2024-04-26.md

技术

发现 iPad 上的浏览器的 user agent 是桌面电脑的,因为设置里面“请求桌面网站”一直是打开的。但是,桌面版不魂 touch event,iPad 支持。那我要如何区分?

通过 alert('ontouchstart' in window) 可以判断出来。

没有评论 页首

2024-04-25.md

技术

一句错误的代码:

1
(true ? document.body.prepend : document.body.append)(document.createElement('hr'))

正确的写法:

1
(true ? document.body.prepend : document.body.append).call(document.body,document.createElement('hr'))

火狐的提示比谷歌详细很多。

简写:

1
2
let a = document.append;
a('<hr>');
没有评论 页首

2024-04-24.md

技术

第一次学习了 JS 的 get/set 属性方法

待办事项

  • 写个 age 加密的教程。
  • 发现 age 可以直接上 ssh 的 key,曝爽!
没有评论 页首

连发音都是一样的,不能说毫无关系🤔。

1782445642662695037-GLyEuBubEAAljo9.jpg

没有评论 页首

2024-04-22.md

技术

不知道啥时候把 blog@twofei.com 这个邮箱发件人的昵称命名成了 “damn” 🥵,找了好一会儿设置才将其改掉了。


给博客加上了自动重新部署的逻辑,通过 GitHub 的 Webhooks。

待办事项

  • 把博客美化评论内容成文;
没有评论 页首

2024-04-21.md

技术

了解了一下新的 Go 1.22 版本的 ServeMux,已经支持 带 METHOD 的 Handler 了。可以废掉我在 TaoBlog 中写的带 Method 的 ServeMux 了。

没有评论 页首

2024-04-20.md

待办事项

生活

每次点击 Obsidian 的 “open today's daily note“ 发现是个新页面时,就恐惧又是新的一天,好像昨天什么也没做一样。

没有评论 页首

苹果要完,这应该怎么破? #bugOS

没有评论 页首

2024-04-12.md

前端

发现 node.propnode.getAttribute('prop') 还是不一样。比如当 <img src="照片.jpg" /> 时,前者拿到的是完整路径,后者拿到的是原始值。见:https://stackoverflow.com/a/78312419/3628322

没有评论 页首

终于去了一次甘坑古镇,花花非常漂亮

非常漂亮三角梅!

没有评论 页首

2024-04-09.md

待办事项

  • 把以前的 mind 项目(从 yaml 生成 table)改变成 markdown 扩展。再怎么说描述能力也比 markdown 强吧?🤔

生活

去了甘坑古镇和东西涌。甘坑非常漂亮,容易出图!适合带妹子去!我的妹子在哪里呢!

没有评论 页首

2024-04-08.md

生活

多次注意到 OpenStreetMap,今天终于打开看了一下。是由全世界的人共同绘制的,感觉很不错。

简单调研了一下几款地图显示老家地址的区别:谷歌 百度 高德,发现只有谷歌卫星地图最最清晰。

没有评论 页首