初始提交:时之刃的小站

基于 VermilionVoid(朱墨留白)主题的个人博客。
- 站点配置集中在 src/data/site.ts 与 src/data/profile.ts
- 30 篇文章、8 条偶得、相册
- 评论使用自托管 twikoo
- 部署走 deploy.sh(rsync 到树莓派 + 云服务器)
This commit is contained in:
dsh
2026-09-14 20:44:12 +08:00
commit 6f4cc13a20
209 changed files with 36176 additions and 0 deletions
+133
View File
@@ -0,0 +1,133 @@
/* 创建一条新的「偶得」(thought)
*
* 用法:
* pnpm new-thought "今天把树莓派的散热换掉了,温度降了 15 度。"
* pnpm new-thought "标题" --title 换散热 --location 张掖 --weather "晴 27°C" \
* --images /gallery/GalleryFav/xxx.png --tags 生活,硬件 --pinned
*
* 只提供正文,其余字段都是可选的。
*/
import fs from "node:fs"
import path from "node:path"
const TARGET_DIR = "./src/content/thoughts"
/**
* 用带时区的 ISO 时间。若写成不带时区的 `2026-09-14 21:30`,
* YAML 会按 UTC 解析,显示时会偏移。
*/
function nowIso() {
return new Date().toISOString()
}
function fileStamp() {
const now = new Date()
const pad = (n) => String(n).padStart(2, "0")
return (
`${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}` +
`-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`
)
}
const args = process.argv.slice(2)
if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
console.log(`创建一条新的「偶得」
用法:
pnpm new-thought "正文内容" [选项]
选项:
--title <标题> 可选标题
--location <地点> 地点,显示在日期旁
--weather <天气> 天气,与地点一起显示
--images <路径,路径> 配图,public 下的绝对路径,逗号分隔
--tags <标签,标签> 标签,逗号分隔
--pinned 置顶
示例:
pnpm new-thought "换了散热,温度降了 15 度。" --location 张掖 --tags 硬件`)
process.exit(args.length === 0 ? 1 : 0)
}
const content = args[0]
const options = {
title: "",
location: "",
weather: "",
images: "",
tags: "",
pinned: false,
}
for (let i = 1; i < args.length; i++) {
const arg = args[i]
if (arg === "--pinned") {
options.pinned = true
continue
}
if (!arg.startsWith("--")) {
console.error(`错误:无法识别的参数 "${arg}"`)
process.exit(1)
}
const key = arg.slice(2)
if (!(key in options)) {
console.error(`错误:未知选项 --${key}`)
process.exit(1)
}
const value = args[i + 1]
if (value === undefined || value.startsWith("--")) {
console.error(`错误:--${key} 缺少值`)
process.exit(1)
}
options[key] = value
i++
}
const list = (value) =>
value
? value
.split(",")
.map((s) => s.trim())
.filter(Boolean)
: []
const images = list(options.images)
const tags = list(options.tags)
if (!fs.existsSync(TARGET_DIR)) {
fs.mkdirSync(TARGET_DIR, { recursive: true })
}
const fullPath = path.join(TARGET_DIR, `${fileStamp()}.md`)
if (fs.existsSync(fullPath)) {
console.error(`错误:${fullPath} 已存在,请稍后重试`)
process.exit(1)
}
// 字符串统一加引号,避免标题里的冒号、井号等破坏 YAML
const quote = (value) => `"${String(value).replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`
const frontmatter = [
`published: ${nowIso()}`,
options.title ? `title: ${quote(options.title)}` : "",
options.location ? `location: ${quote(options.location)}` : "",
options.weather ? `weather: ${quote(options.weather)}` : "",
images.length > 0 ? `images:\n${images.map((img) => ` - ${quote(img)}`).join("\n")}` : "",
tags.length > 0 ? `tags:\n${tags.map((tag) => ` - ${quote(tag)}`).join("\n")}` : "",
options.pinned ? "pinned: true" : "",
]
.filter(Boolean)
.join("\n")
fs.writeFileSync(fullPath, `---\n${frontmatter}\n---\n\n${content}\n`, "utf8")
console.log(`已创建偶得: ${fullPath}`)
console.log("写入正文后,dev server 会自动刷新。")