Tôi tạo trang web này bằng cách nào

Tạo kho Obsidian Vault

Điều đầu tiên, do tôi dùng ghi chú của mình làm nơi lưu trữ chính cho bộ não thứ 2, đồng thời xuất bản luôn nó thành một dạng khu vườn kỹ thuật số. Do đó, tất nhiên là tôi sẽ phải tạo kho lưu trữ ghi chú của mình trước. Để đáp ứng nhu cầu đồng bộ, tôi dùng Git để quản lý mã nguồn tập tin ghi chú, và đồng bộ nó qua 2 công cụ:

Tôi lưu trữ kho ghi chú của mình trên Gitlab.com, có đồng bộ 1-1 (mirror) qua một số dịch vụ Git khác như GithubGitlab tự host của tôi để đảm bảo sao lưu dự phòng và tích hợp Continuous integration tôi sẽ nói ở phần sau.

cd ~/Document/Notes
git init
git remote add origin git@gitlab.com:dynamo.foss/notes.git
git push origin main

Thêm Quartz vào kho ghi chú

Qua quá trình kiểm nghiệm, tôi nhận thấy công cụ Quartz này là đủ nhu cầu của tôi để xuất bản ghi chú thành trang web tĩnh mà tôi có thể lưu trữ trên GitLab Pages.

Tôi muốn mỗi lần tôi đẩy các chỉnh sửa ghi chú lên kho ghi chú trên Gitlab, thì trang web này sẽ phải cập nhật theo, kể cả nếu Quartz có bất cứ thay đổi nào, đến từ nguồn của nó hoặc tôi muốn chỉnh sửa theo ý thích. Vì vậy, nhúng Quartz thành module con (submodule) của Git vào ghi chú là thích hợp hơn cả.

Vì vậy, tôi tạo một kho khác chứa mã nguồn của quartz và thêm nó vào kho ghi chú tai thư mục .publish như sau:

git submodule add .publish git@gitlab.com:dynamo.foss/quartz.git
git commit -m "feat: add Quartz submodule for publishing"
git push origin main

Tùy chỉnh Quartz theo cấu hình sẵn có

  • Cần chỉnh lại thư mục content của Quartz về kho ghi chú, ở đây là thư mục cha của Quartz theo cách sắp xếp của tôi.
cd .publish
npm i
rm -rf content
ln -sf .. content
  • Tùy chỉnh lại cấu hình và bố cục cho phù hợp với nhu cầu. Đây là các tùy chỉnh của tôi
--- quartz.config.ts
+++ quartz.config.ts
@@ -8,23 +8,23 @@ import * as Plugin from "./quartz/plugins"
  */
 const config: QuartzConfig = {
   configuration: {
-    pageTitle: "🪴 Quartz 4.0",
+    pageTitle: "🪴 Dynamo's Digital Garden",
     enableSPA: true,
     enablePopovers: true,
     analytics: {
       provider: "plausible",
     },
     locale: "en-US",
-    baseUrl: "quartz.jzhao.xyz",
-    ignorePatterns: ["private", "templates", ".obsidian"],
+    baseUrl: "notes.dynamotn.dev",
+    ignorePatterns: [".publish", "_assets", ".obsidian"],
     defaultDateType: "created",
     theme: {
       fontOrigin: "googleFonts",
       cdnCaching: true,
       typography: {
-        header: "Schibsted Grotesk",
-        body: "Source Sans Pro",
-        code: "IBM Plex Mono",
+        header: "Merriweather",
+        body: "Roboto",
+        code: "Inconsolata",
       },
       colors: {
         lightMode: {
-          light: "#faf8f8",
-          lightgray: "#e5e5e5",
-          gray: "#b8b8b8",
-          darkgray: "#4e4e4e",
-          dark: "#2b2b2b",
-          secondary: "#284b63",
-          tertiary: "#84a59d",
-          highlight: "rgba(143, 159, 169, 0.15)",
+          light: "#eff1f5",
+          lightgray: "#acb0be",
+          gray: "#7c7f93",
+          darkgray: "#5c5f77",
+          dark: "#4c4f69",
+          secondary: "#7287fd",
+          tertiary: "#04a5e5",
+          highlight: "rgba(114, 135, 253, 0.15)",
         },
         darkMode: {
-          light: "#161618",
-          lightgray: "#393639",
-          gray: "#646464",
-          darkgray: "#d4d4d4",
-          dark: "#ebebec",
-          secondary: "#7b97aa",
-          tertiary: "#84a59d",
-          highlight: "rgba(143, 159, 169, 0.15)",
+          light: "#24273a",
+          lightgray: "#5b6078",
+          gray: "#939ab7",
+          darkgray: "#b8c0e0",
+          dark: "#cad3f5",
+          secondary: "#7dc4e4",
+          tertiary: "#8bd5ca",
+          highlight: "rgba(125, 196, 228, 0.15)",
         },
       },
     },
--- quartz.layout.ts
+++ quartz.layout.ts
@@ -7,8 +7,9 @@ export const sharedPageComponents: SharedLayout = {
   header: [],
   footer: Component.Footer({
     links: {
-      GitHub: "https://github.com/jackyzha0/quartz",
-      "Discord Community": "https://discord.gg/cRFFHYye7t",
+      GitHub: "https://github.com/dynamotn",
+      GitLab: "https://gitlab.com/dynamo.foss",
+      "Contact Me": "mailto:me@dynamotn.dev",
     },
   }),
 }
--- quartz/i18n/locales/en-US.ts
+++ quartz/i18n/locales/en-US.ts
@@ -33,7 +33,7 @@ export default {
       title: "Explorer",
     },
     footer: {
-      createdWith: "Created with",
+      createdWith: "Created by Dynamo with",
     },
     graph: {
       title: "Graph View",
  • Xây dựng (building) thử
npx quartz build --serve

và truy cập http://localhost:8080/ để xem thử

Cấu hình CI trên kho ghi chú tại Gitlab

Do tôi đã chuyển Quartz thành module con (submoule) của kho ghi chú, vì vậy, cấu hình CI sẽ được đặt tại kho ghi chú (.gitlab-ci.yml) như sau:

stages:
  - build
  - deploy
 
variables:
  GIT_SUBMODULE_STRATEGY: recursive
  GIT_SUBMODULE_PATHS: .publish
 
build:
  stage: build
  image: node:18-alpine
  rules:
    - if: '$CI_COMMIT_REF_NAME == "main"'
  before_script:
    - cd .publish
    - npm ci
  script:
    - npx quartz build
  artifacts:
    paths:
      - .publish/public
  cache:
    paths:
      - ~/.npm/
    key: "${CI_COMMIT_REF_SLUG}-node-${CI_COMMIT_REF_NAME}"
 
pages:
  stage: deploy
  rules:
    - if: '$CI_COMMIT_REF_NAME == "main"'
  script:
    - echo "Deploying to GitLab Pages..."
  artifacts:
    paths:
      - .publish/public
  publish: .publish/public

Cấu hình tên miền với GitLab Pages

  • Trên menu Pages của kho ghi chú trên Gitlab (Deploy Pages), tôi thêm tên miền của tôi bằng cách nhấn New Domain và thêm tên miền notes.dynamotn.dev
  • Trên trang quản lý tên miền, tôi gán CNAME tên miền trên trỏ về domain trên Gitlab Pages, của tôi là dynamo.foss.gitlab.io
  • Chờ một thời gian để Gitlab Pages nhận dạng CNAME của domain, cấu hình SSL từ Let’s Encrypt miễn phí cho trang web

Tùy chỉnh mã nguồn Quartz theo nhu cầu

Đến đây, trang Digital Garden dạng web tĩnh được xuất bản từ ghi chú của tôi đã thành hình và có thể hoàn toàn bỏ qua bước này. Tuy nhiên, tôi muốn tùy chỉnh thêm (thực ra là tôi ngứa nghề code), nên tôi đã chỉnh một vài thứ sau:

  • Thêm Google Analytics để thống kê lượt truy cập (cái này khá quan trọng để tôi biết người theo dõi tôi họ quan tâm gì)
  • Tùy chỉnh nhỏ để hiện thời gian tạo và sửa ghi chú chính xác hơn
--- quartz.config.ts
+++ quartz.config.ts
@@ -18,6 +18,7 @@ const config: QuartzConfig = {
     baseUrl: "notes.dynamotn.dev",
     ignorePatterns: [".publish", "_assets", ".obsidian"],
     defaultDateType: "created",
+    defaultDateTypes: ["created", "modified"],
     theme: {
       fontOrigin: "googleFonts",
       cdnCaching: true,
--- quartz/components/ContentMeta.tsx
+++ quartz/components/ContentMeta.tsx
@@ -30,7 +30,9 @@ export default ((opts?: Partial<ContentMetaOptions>) => {
       const segments: (string | JSX.Element)[] = []
 
       if (fileData.dates) {
-        segments.push(formatDate(getDate(cfg, fileData)!, cfg.locale))
+        cfg.defaultDateTypes?.forEach((type) => {
+          segments.push(`${type}: ${formatDate(getDate({ defaultDateType: type }, fileData)!, cfg.locale)}`)
+        })
       }
 
       // Display reading time if enabled
--- quartz/plugins/transformers/lastmod.ts
+++ quartz/plugins/transformers/lastmod.ts
@@ -51,9 +51,11 @@ export const CreatedModifiedDate: QuartzTransformerPlugin<Partial<Options> | und
                 modified ||= st.mtimeMs                                                           
               } else if (source === "frontmatter" && file.data.frontmatter) {                     
                 created ||= file.data.frontmatter.date as MaybeDate                               
+                created ||= file.data.frontmatter.created as MaybeDate                            
                 modified ||= file.data.frontmatter.lastmod as MaybeDate                           
                 modified ||= file.data.frontmatter.updated as MaybeDate                           
                 modified ||= file.data.frontmatter["last-modified"] as MaybeDate                  
+                modified ||= file.data.frontmatter.modified as MaybeDate                          
                 published ||= file.data.frontmatter.publishDate as MaybeDate                      
               } else if (source === "git") {                                                      
                 if (!repo) {                                                                      
  • Tùy chỉnh bố cục hiển thị (Layout)
  • Thêm Disqus cho phép người đến thăm để lại bình luận
  • Thêm banner cho mỗi bài viết
  • Thêm tính năng mã hóa ghi chú bằng mật khẩu trên mỗi ghi chú, cho phép tôi chia sẻ ghi chú riêng tư với các cá nhân riêng biệt (Password-protect notes · Issue #637 · jackyzha0/quartz · GitHub)
  • Hiển thị file ảnh xuất từ file sơ đồ Excalidraw

4 tính năng cuối khá nhiều thay đổi và phức tạp hơn do là tính năng không có sẵn. Vì vậy, tôi sẽ tạo bài blog riêng về chúng.