问题
我有一个obsidian仓库,私有的,因为里面有一些笔记涉及公司和个人隐私。但其余部分都是可公开的,有没有一个方案同时满足,
- 免费
- 可以控制哪些发布,哪些不发布
- 原始仓库是私有的
方案
工具名称 | 优点 | 缺点 |
---|---|---|
obsidian-digital-garden | 1. 一键部署到Vercel/Netlify 2. 通过Frontmatter控制单笔记发布 3. 无需维护GitHub CI | 1. 国内访问慢(Vercel已无法访问) 2. 导出网站存在异常,未深究 3. 部署GitHub Pages困难且定制性低 |
obsidian-webpage-export | 1. 支持仓库整体导出为HTML 2. 可部署到GitHub Pages | 1. 开发中功能不完善 2. 部分笔记导出报错且提示不清晰 3. 不支持单笔记发布控制 |
quartz | 1. 支持Glob匹配发布控制 2. 案例丰富参考性强 3. 文档齐全 4. UI高度可定制 | 1. 不支持Dataview语法 2. 不兼容Obsidian的Base文件 |
最后选用了quartz. 看看这个网站有多美!
Quartz需要一些内部文件来完成文件转网页的工作,通常是一些js、ts、json文件。这样以来便有两种选择,
- 将quartz集成到obsidian仓库(私有)中,将转换好的文件publish到另一个仓库(公开)
- 新建一个公开仓库放quartz,然后将obsidian仓库作为submodule引入,直接在本仓库执行构建,发布
这里我选用方案2,因为方案1会污染obsidian仓库,而它本应聚焦于内容创作!而这个新建的仓库就是guyueshui/quartz,参考其中的action文件。
# Copied from jackyzha0/quartz's ci.yaml
name: Build vault
on:
pull_request:
branches:
- master
push:
branches:
- master
paths:
- '.gitmodules'
- 'learn2live/**'
# receive event of submodule update
repository_dispatch:
types: [submodule_updated]
workflow_dispatch:
jobs:
build-and-test:
strategy:
matrix:
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
permissions:
contents: write
pages: write
id-token: write
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
token: ${{ secrets.VAULT_PAT }}
submodules: 'recursive'
- name: Update submodule to latest
run: |
git config --global user.email "actions@github.com"
git config --global user.name "GitHub Actions"
git submodule foreach --recursive 'git checkout main || git checkout master'
git submodule update --remote --merge
git add .
git commit -m "chore: sync submodules to HEAD" || exit 0
git push
- name: Setup Node
uses: actions/setup-node@v5
with:
node-version: 22
- name: Cache dependencies
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- run: npm ci
- name: Test
run: npm test
- name: Ensure Quartz builds, check bundle info
run: npx quartz build --bundleInfo -d learn2live
- name: Setup GitHub Pages
uses: actions/configure-pages@v4
- name: Upload GitHub Pages artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./public
retention-days: 1
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
而obsidian仓库(私有)仅作为submodule提供内容,同时也需要配置一个action,在仓库更新后,触发quartz仓库的构建。Actions文件如下,
name: Trigger deployment of obsidian vault
# see: https://github.com/guyueshui/quartz
on:
pull_request:
branches:
- master
push:
branches:
- master
# 允许手动触发
workflow_dispatch:
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Post "submodule_updated" event
# 向quartz仓库推送内容更新的事件,对应quartz仓库ci中的repository_dispatch
run: |
curl -X POST \
-H "Authorization: token ${{ secrets.VAULT_PAT }}" \
-H "Accept: application/vnd.github.everest-preview+json" \
https://api.github.com/repos/guyueshui/quartz/dispatches \
-d '{"event_type":"submodule_updated"}'
- run: |
echo "Onwer: ${{ github.repository_owner }}"
echo "Repo: ${{ github.event.repository.name }}"
另外配置还有几点要注意:
- 可以创建一个PAT(Personal Access Token)同时具备这两个仓库的读写权限,实际上obsidna仓库只需要读
- PAT创建入口:https://github.com/settings/personal-access-tokens
- PAT权限注意勾选workflow,因为要跨仓库触发quartz仓库的workflow
- quartz仓库设置中的Actions > Workflow permissions更改为”Read and write permissions”
- secrets.VAULT_PAT对应仓库中配置的Settings > Secrets and variables > Actions > Repository secrets
此时,就可以做到更新obsidian仓库,触发quartz仓库的构建操作,进而更新发布出去的网站。
Quartz定制
私有笔记不发布
参考:https://quartz.jzhao.xyz/features/private-pages
例如我的忽略配置如下,
ignorePatterns: [
"private", "templates", ".obsidian",
"__obex", ".+", "work",
"life/亲子",
"**/202305302345.md",
"**/2022-07-22.md",
"**/2022-09-06.md",
// "assets",
],
asset必须要发布,不然笔记中的图片就找不到了。
Accent color
直接修改quartz.config.ts
中的config.theme.colors
字段。改颜色是个审美的活,简单点就好。
Explorer过滤assets文件夹
参考:https://quartz.jzhao.xyz/features/explorer
diff --git a/quartz.layout.ts b/quartz.layout.ts
index bd16975..042210d 100644
--- a/quartz.layout.ts
+++ b/quartz.layout.ts
@@ -1,5 +1,27 @@
import { PageLayout, SharedLayout } from "./quartz/cfg"
import * as Component from "./quartz/components"
+import { Options as ExplOptions } from "./quartz/components/Explorer"
+
+// add explorer customizations, cf. https://quartz.jzhao.xyz/features/explorer
+export const explMapFn : ExplOptions["mapFn"] =
+(node) => {
+ if (node.isFolder) {
+ node.displayName = "📁 " + node.displayName
+ } else {
+ node.displayName = "📄 " + node.displayName
+ }
+}
+
+export const explFilterFn : ExplOptions["filterFn"] =
+(node) => {
+ // set containing names of everything you want to filter out
+ const omit = new Set(["assets", "tags", "advanced"])
+
+ // can also use node.slug or by anything on node.data
+ // note that node.data is only present for files that exist on disk
+ // (e.g. implicit folder nodes that have no associated index.md)
+ return !omit.has(node.displayName.toLowerCase())
+}
// components shared across all pages
export const sharedPageComponents: SharedLayout = {
@@ -38,7 +60,7 @@ export const defaultContentPageLayout: PageLayout = {
{ Component: Component.ReaderMode() },
],
}),
- Component.Explorer(),
+ Component.Explorer({'mapFn': explMapFn, 'filterFn': explFilterFn}),
],
right: [
Component.Graph(),
@@ -62,7 +84,7 @@ export const defaultListPageLayout: PageLayout = {
{ Component: Component.Darkmode() },
],
}),
- Component.Explorer(),
+ Component.Explorer({'mapFn': explMapFn, 'filterFn': explFilterFn}),
],
right: [],
}
Explorer显示文件名而非title
diff --git a/quartz/components/PageList.tsx b/quartz/components/PageList.tsx
index 7bf2382..5652b1a 100644
--- a/quartz/components/PageList.tsx
+++ b/quartz/components/PageList.tsx
@@ -67,7 +67,15 @@ export const PageList: QuartzComponent = ({ cfg, fileData, allFiles, limit, sort
return (
<ul class="section-ul">
{list.map((page) => {
- const title = page.frontmatter?.title
+ // use filename instead of page title, yychi@2025/10/04
+ const slugParts = page.slug!.split("/")
+ let title = null
+ for (let i = slugParts.length - 1; i >= 0; --i) {
+ if (slugParts[i] != "index") {
+ title = slugParts[i]
+ break
+ }
+ }
const tags = page.frontmatter?.tags ?? []
return (
diff --git a/quartz/util/fileTrie.ts b/quartz/util/fileTrie.ts
index 9e6706f..c36eb98 100644
--- a/quartz/util/fileTrie.ts
+++ b/quartz/util/fileTrie.ts
@@ -28,9 +28,9 @@ export class FileTrieNode<T extends FileTrieData = ContentDetails> {
}
get displayName(): string {
- const nonIndexTitle = this.data?.title === "index" ? undefined : this.data?.title
+ // use filename instead of page title, yychi@2025/10/04
return (
- this.displayNameOverride ?? nonIndexTitle ?? this.fileSegmentHint ?? this.slugSegment ?? ""
+ this.displayNameOverride ?? this.fileSegmentHint ?? this.slugSegment ?? ""
)
}
小结
可见,quartz的定制还是非常方便的。