diff --git a/gitbook/README.md b/gitbook/README.md
deleted file mode 100644
index 7b9b562..0000000
--- a/gitbook/README.md
+++ /dev/null
@@ -1,82 +0,0 @@
-# GitBook 主題架構說明
-
-GitBook 主題提供類似線上文件的閱讀體驗:左側為樹狀導覽、右側顯示文章資訊與公告,內頁則專注呈現 Notion 內容。本文件整理主題的檔案結構、各佈局元件的責任分工,以及常見的使用方式,方便日後整合或擴充。
-
-## 專案結構
-
-```
-.
-├── components/ # 主題專用的視覺元件與工具(導覽清單、目錄抽屜、底部工具列…)
-│ └── ui/dashboard/ # 儀表板頁面使用的額外組件
-├── config.js # 主題設定(首頁導向、導覽開關、Widget 等)
-├── index.js # 統一對外匯出的入口
-├── layouts/ # 依頁面職責拆分的佈局元件
-│ ├── ArchiveLayout.jsx # 歸檔頁
-│ ├── AuthLayouts.jsx # 登入/註冊頁
-│ ├── BaseLayout.jsx # 主架構:左右欄、行動裝置導覽、Loading 與廣告
-│ ├── ListLayouts.jsx # 首頁/文章列表/搜尋結果與儀表板
-│ ├── NotFoundLayout.jsx # 404 頁面
-│ ├── SlugLayout.jsx # 文章詳情頁
-│ └── TaxonomyLayouts.jsx # 分類與標籤索引
-├── style.js # 只對 GitBook 主題生效的全域樣式
-└── README.md # 本說明文件
-```
-
-## 主要佈局與職責
-
-- `LayoutBase`:包住所有內容的骨架,負責導覽抽屜、公告、目錄、回頂按鈕與 Loading 遮罩,並透過 `useGitBookGlobal` 共享狀態(搜尋、TOC、導覽列表)。
-- `LayoutIndex`:讀取 `config.js` 的 `GITBOOK_INDEX_PAGE`,在前端重新導向至指定文章,若找不到對應 slug 會注入錯誤提示。
-- `LayoutPostList` / `LayoutSearch`:此主題採左側導覽管理文章,頁面本身僅回傳空節點,確保路由存在。
-- `LayoutArchive`:使用 `BlogArchiveItem` 依年份渲染歸檔清單。
-- `LayoutSlug`:顯示單篇內容,處理加密文章、Meta 標題、分類/標籤、前後篇與留言,並在內容缺漏時自動導回 404。
-- `LayoutCategoryIndex` / `LayoutTagIndex`:輸出分類與標籤的索引列表,套用統一樣式與 `locale` 文案。
-- `LayoutSignIn` / `LayoutSignUp`:Clerk 驗證元件容器,會先顯示官方預設表單,再渲染 Notion 內容。
-- `LayoutDashboard`:搭配 `components/ui/dashboard/*` 顯示自訂儀表板。
-- `Layout404`:延遲檢查內容載入狀態,若仍無法抓到文章容器便導回首頁。
-
-## 使用說明
-
-1. **在 Next.js 匯入主題佈局**
- ```jsx
- import {
- LayoutBase,
- LayoutSlug,
- LayoutIndex,
- LayoutCategoryIndex
- } from '@/themes/gitbook'
-
- const Post = props => (
-
-
-
- )
-
- export default Post
- ```
- - `LayoutBase` 處理共同框架;內層依頁面需求替換為 `LayoutIndex`、`LayoutArchive`、`LayoutCategoryIndex`…等。
- - 需要使用 TOC 或導覽狀態時,可透過 `useGitBookGlobal()` 取得 `searchModal`、`tocVisible` 等共享資料。
-
-2. **調整主題設定**
- - 於 `config.js` 修改選項,或透過環境變數覆寫,例如 `NEXT_PUBLIC_GITBOOK_INDEX_PAGE`、`NEXT_PUBLIC_GITBOOK_AUTO_SORT`。
- - 常用開關:
- - `GITBOOK_AUTO_SORT`:自動依分類整理導覽。
- - `GITBOOK_EXCLUSIVE_COLLAPSE`:導覽是否一次只展開一組。
- - `GITBOOK_FOLDER_HOVER_EXPAND`:滑鼠懸停是否自動展開導覽資料夾。
- - `GITBOOK_WIDGET_REVOLVER_MAPS` / `GITBOOK_WIDGET_TO_TOP`:控制右側 Widget。
-
-3. **客製樣式與元件**
- - 全域樣式集中在 `style.js`,若需調整字體或底色可於此擴充。
- - 導覽列表、公告、底部工具列等都在 `components/`,可直接替換或新增對應元件。
- - 儀表板相關 UI 放在 `components/ui/dashboard/`,可拆分或擴充模組化功能。
-
-4. **新增頁面佈局**
- - 建議在 `layouts/` 下新增檔案並於 `index.js` 匯出,維持與現有架構一致。
- - 若需要共用狀態,可在新佈局內透過 `useGitBookGlobal` 共享資料,避免重複定義內容。
-
-## 開發提醒
-
-- 本主題內的註解與靜態文字皆已改為臺灣常用的繁體中文,若新增內容請維持相同用語風格。
-- 導覽列表仰賴 `allNavPages`,若調整資料格式請同步更新 `NavPostList` 相關邏輯。
-- 重新導向或動態載入元件時請注意瀏覽器環境判斷(`isBrowser`),避免在 SSR 階段觸發錯誤。
-
-如需額外備註,可持續更新本 README 讓後續維護者快速上手。
diff --git a/gitbook/components/Announcement.js b/gitbook/components/Announcement.js
deleted file mode 100755
index 62df1ec..0000000
--- a/gitbook/components/Announcement.js
+++ /dev/null
@@ -1,21 +0,0 @@
-// import { useGlobal } from '@/lib/global'
-import dynamic from 'next/dynamic'
-
-const NotionPage = dynamic(() => import('@/components/NotionPage'))
-
-const Announcement = ({ notice, className }) => {
-// const { locale } = useGlobal()
- if (notice?.blockMap) {
- return
-
- {/* {locale.COMMON.ANNOUNCEMENT}
*/}
- {notice && (
-
-
)}
-
-
- } else {
- return <>>
- }
-}
-export default Announcement
diff --git a/gitbook/components/ArticleAround.js b/gitbook/components/ArticleAround.js
deleted file mode 100644
index 02846ac..0000000
--- a/gitbook/components/ArticleAround.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import { useGlobal } from '@/lib/global'
-import SmartLink from '@/components/SmartLink'
-
-/**
- * 上一篇、下一篇文章
- * @param {prev,next} param0
- * @returns
- */
-export default function ArticleAround({ prev, next }) {
- const { locale } = useGlobal()
-
- if (!prev || !next) {
- return <>>
- }
-
- return (
-
-
-
-
-
{locale.COMMON.PREV_POST}
-
{prev.title}
-
-
-
-
-
-
{locale.COMMON.NEXT_POST}
-
{next.title}
-
-
-
-
- )
-}
diff --git a/gitbook/components/ArticleInfo.js b/gitbook/components/ArticleInfo.js
deleted file mode 100644
index 27def82..0000000
--- a/gitbook/components/ArticleInfo.js
+++ /dev/null
@@ -1,17 +0,0 @@
-/**
- * 文章補充資訊
- * @param {*} param0
- * @returns
- */
-export default function ArticleInfo({ post }) {
- if (!post) {
- return null
- }
- return (
-
-
- Last update:{' '}
- {post.date?.start_date || post?.publishDay || post?.lastEditedDay}
-
- )
-}
diff --git a/gitbook/components/ArticleLock.js b/gitbook/components/ArticleLock.js
deleted file mode 100644
index 2770739..0000000
--- a/gitbook/components/ArticleLock.js
+++ /dev/null
@@ -1,67 +0,0 @@
-import { useGlobal } from '@/lib/global'
-import { useRouter } from 'next/router'
-import { useEffect, useRef } from 'react'
-
-/**
- * 加密文章驗證元件
- * @param {password, validPassword} props
- * @param password 正確的密碼
- * @param validPassword(bool) 回呼函式,驗證通過時傳回 true
- * @returns
- */
-export const ArticleLock = props => {
- const { validPassword } = props
- const { locale } = useGlobal()
- const router = useRouter()
- const passwordInputRef = useRef(null)
-
- /**
- * 輸入並送出密碼
- */
- const submitPassword = () => {
- const p = document.getElementById('password')
- // 驗證失敗提示
- if (!validPassword(p?.value)) {
- const tips = document.getElementById('tips')
- if (tips) {
- tips.innerHTML = ''
- tips.innerHTML = `${locale.COMMON.PASSWORD_ERROR}
`
- }
- }
- }
-
- useEffect(() => {
- // 選取密碼輸入框並聚焦
- passwordInputRef.current.focus()
- }, [router])
-
- return (
-
-
-
{locale.COMMON.ARTICLE_LOCK_TIPS}
-
-
{
- if (e.key === 'Enter') {
- submitPassword()
- }
- }}
- ref={passwordInputRef} // 綁定 ref 到 passwordInputRef 變數
- className='outline-none w-full text-sm pl-5 rounded-l transition focus:shadow-lg dark:text-gray-300 font-light leading-10 text-black bg-gray-100 dark:bg-gray-500'>
-
-
- {locale.COMMON.SUBMIT}
-
-
-
-
-
-
- )
-}
diff --git a/gitbook/components/BlogArchiveItem.js b/gitbook/components/BlogArchiveItem.js
deleted file mode 100644
index ab11c28..0000000
--- a/gitbook/components/BlogArchiveItem.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import SmartLink from '@/components/SmartLink'
-
-/**
- * 歸檔分組
- * @param {*} param0
- * @returns
- */
-export default function BlogArchiveItem({ archiveTitle, archivePosts }) {
- return (
-
- )
-}
diff --git a/gitbook/components/BlogPostCard.js b/gitbook/components/BlogPostCard.js
deleted file mode 100644
index 0726d22..0000000
--- a/gitbook/components/BlogPostCard.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import Badge from '@/components/Badge'
-import NotionIcon from '@/components/NotionIcon'
-import { siteConfig } from '@/lib/config'
-import SmartLink from '@/components/SmartLink'
-import { useRouter } from 'next/router'
-
-const BlogPostCard = ({ post, className }) => {
- const router = useRouter()
- const currentSelected =
- decodeURIComponent(router.asPath.split('?')[0]) === post?.href
-
- return (
-
-
-
- {siteConfig('POST_TITLE_ICON') && (
-
- )}{' '}
- {post.title}
-
- {/* 最新文章加個紅點 */}
- {post?.isLatest && siteConfig('GITBOOK_LATEST_POST_RED_BADGE') && (
-
- )}
-
-
- )
-}
-
-export default BlogPostCard
diff --git a/gitbook/components/BottomMenuBar.js b/gitbook/components/BottomMenuBar.js
deleted file mode 100644
index f264d55..0000000
--- a/gitbook/components/BottomMenuBar.js
+++ /dev/null
@@ -1,50 +0,0 @@
-import { useGlobal } from '@/lib/global'
-import { useGitBookGlobal } from '..'
-
-/**
- * 行動版底部導覽
- * @param {*} param0
- * @returns
- */
-export default function BottomMenuBar({ post, className }) {
- const showTocButton = post?.toc?.length > 1
- const { locale } = useGlobal()
- const { pageNavVisible, changePageNavVisible, tocVisible, changeTocVisible } =
- useGitBookGlobal()
- const togglePageNavVisible = () => {
- changePageNavVisible(!pageNavVisible)
- }
-
- const toggleToc = () => {
- changeTocVisible(!tocVisible)
- }
-
- return (
-
-
-
-
- {showTocButton && (
-
- )}
-
-
- )
-}
diff --git a/gitbook/components/Card.js b/gitbook/components/Card.js
deleted file mode 100644
index d24c046..0000000
--- a/gitbook/components/Card.js
+++ /dev/null
@@ -1,9 +0,0 @@
-const Card = ({ children, headerSlot, className }) => {
- return
- <>{headerSlot}>
-
-
-}
-export default Card
diff --git a/gitbook/components/Catalog.js b/gitbook/components/Catalog.js
deleted file mode 100644
index 06c9206..0000000
--- a/gitbook/components/Catalog.js
+++ /dev/null
@@ -1,104 +0,0 @@
-import { isBrowser } from '@/lib/utils'
-import throttle from 'lodash.throttle'
-import { uuidToId } from 'notion-utils'
-import { useCallback, useEffect, useState } from 'react'
-
-/**
- * 目錄導覽元件
- * @param toc
- * @returns {JSX.Element}
- * @constructor
- */
-const Catalog = ({ post }) => {
- const toc = post?.toc
- // 同步選取目錄事件
- const [activeSection, setActiveSection] = useState(null)
-
- // 監聽捲動事件
- useEffect(() => {
- window.addEventListener('scroll', actionSectionScrollSpy)
- actionSectionScrollSpy()
- return () => {
- window.removeEventListener('scroll', actionSectionScrollSpy)
- }
- }, [post])
-
- const throttleMs = 200
- const actionSectionScrollSpy = useCallback(
- throttle(() => {
- const sections = document.getElementsByClassName('notion-h')
- let prevBBox = null
- let currentSectionId = null
- for (let i = 0; i < sections.length; ++i) {
- const section = sections[i]
- if (!section || !(section instanceof Element)) continue
- if (!currentSectionId) {
- currentSectionId = section.getAttribute('data-id')
- }
- const bbox = section.getBoundingClientRect()
- const prevHeight = prevBBox ? bbox.top - prevBBox.bottom : 0
- const offset = Math.max(150, prevHeight / 4)
- // GetBoundingClientRect returns values relative to viewport
- if (bbox.top - offset < 0) {
- currentSectionId = section.getAttribute('data-id')
- prevBBox = bbox
- continue
- }
- // No need to continue loop, if last element has been detected
- break
- }
- setActiveSection(currentSectionId)
- const tocIds = post?.toc?.map(t => uuidToId(t.id)) || []
- const index = tocIds.indexOf(currentSectionId) || 0
- if (isBrowser && tocIds?.length > 0) {
- for (const tocWrapper of document?.getElementsByClassName(
- 'toc-wrapper'
- )) {
- tocWrapper?.scrollTo({ top: 28 * index, behavior: 'smooth' })
- }
- }
- }, throttleMs)
- )
-
- // 無目錄則直接返回空
- if (!toc || toc?.length < 1) {
- return <>>
- }
-
- return (
- <>
- {/*
- {locale.COMMON.TABLE_OF_CONTENTS}
-
*/}
-
-
-
-
- >
- )
-}
-
-export default Catalog
diff --git a/gitbook/components/CatalogDrawerWrapper.js b/gitbook/components/CatalogDrawerWrapper.js
deleted file mode 100644
index 838d000..0000000
--- a/gitbook/components/CatalogDrawerWrapper.js
+++ /dev/null
@@ -1,66 +0,0 @@
-import { useGlobal } from '@/lib/global'
-import { useGitBookGlobal } from '@/themes/gitbook'
-import { useRouter } from 'next/router'
-import { useEffect } from 'react'
-import Catalog from './Catalog'
-
-/**
- * 懸浮抽屜目錄
- * @param toc
- * @param post
- * @returns {JSX.Element}
- * @constructor
- */
-const CatalogDrawerWrapper = ({ post, cRef }) => {
- const { tocVisible, changeTocVisible } = useGitBookGlobal()
- const { locale } = useGlobal()
- const router = useRouter()
- const switchVisible = () => {
- changeTocVisible(!tocVisible)
- }
- useEffect(() => {
- changeTocVisible(false)
- }, [router])
- return (
- <>
-
- {/* 側邊選單 */}
-
- {post && (
- <>
-
- {locale.COMMON.TABLE_OF_CONTENTS}
- {
- changeTocVisible(false)
- }}>
-
-
-
-
- >
- )}
-
-
- {/* 背景遮罩 */}
-
- >
- )
-}
-export default CatalogDrawerWrapper
diff --git a/gitbook/components/CategoryGroup.js b/gitbook/components/CategoryGroup.js
deleted file mode 100644
index ba43437..0000000
--- a/gitbook/components/CategoryGroup.js
+++ /dev/null
@@ -1,19 +0,0 @@
-
-import CategoryItem from './CategoryItem'
-
-const CategoryGroup = ({ currentCategory, categoryOptions }) => {
- if (!categoryOptions) {
- return <>>
- }
- return
-
分類
-
- {categoryOptions?.map(category => {
- const selected = currentCategory === category.name
- return
- })}
-
-
-}
-
-export default CategoryGroup
diff --git a/gitbook/components/CategoryItem.js b/gitbook/components/CategoryItem.js
deleted file mode 100644
index 8b7cbcf..0000000
--- a/gitbook/components/CategoryItem.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import SmartLink from '@/components/SmartLink'
-
-export default function CategoryItem ({ selected, category, categoryCount }) {
- return (
-
-
- {category} {categoryCount && `(${categoryCount})`}
-
-
-
- );
-}
diff --git a/gitbook/components/Footer.js b/gitbook/components/Footer.js
deleted file mode 100644
index d850840..0000000
--- a/gitbook/components/Footer.js
+++ /dev/null
@@ -1,66 +0,0 @@
-import { BeiAnGongAn } from '@/components/BeiAnGongAn'
-import { siteConfig } from '@/lib/config'
-import SocialButton from './SocialButton'
-/**
- * 站點也叫
- * @param {*} param0
- * @returns
- */
-const Footer = ({ siteInfo }) => {
- const d = new Date()
- const currentYear = d.getFullYear()
- const since = siteConfig('SINCE')
- const copyrightDate =
- parseInt(since) < currentYear ? since + '-' + currentYear : currentYear
-
- return (
-
- )
-}
-
-export default Footer
diff --git a/gitbook/components/Header.js b/gitbook/components/Header.js
deleted file mode 100644
index 2c93034..0000000
--- a/gitbook/components/Header.js
+++ /dev/null
@@ -1,134 +0,0 @@
-import Collapse from '@/components/Collapse'
-import DarkModeButton from '@/components/DarkModeButton'
-import { siteConfig } from '@/lib/config'
-import { useGlobal } from '@/lib/global'
-import { SignInButton, SignedOut, UserButton } from '@clerk/nextjs'
-import { useRef, useState } from 'react'
-import CONFIG from '../config'
-import LogoBar from './LogoBar'
-import { MenuBarMobile } from './MenuBarMobile'
-import { MenuItemDrop } from './MenuItemDrop'
-import SearchInput from './SearchInput'
-
-/**
- * 頁首:頂部導覽列 + 選單
- * @param {} param0
- * @returns
- */
-export default function Header(props) {
- const { className, customNav, customMenu } = props
- const [isOpen, changeShow] = useState(false)
- const collapseRef = useRef(null)
-
- const { locale } = useGlobal()
-
- const defaultLinks = [
- {
- icon: 'fas fa-th',
- name: locale.COMMON.CATEGORY,
- href: '/category',
- show: siteConfig('GITBOOK_MENU_CATEGORY', null, CONFIG)
- },
- {
- icon: 'fas fa-tag',
- name: locale.COMMON.TAGS,
- href: '/tag',
- show: siteConfig('GITBOOK_BOOK_MENU_TAG', null, CONFIG)
- },
- {
- icon: 'fas fa-archive',
- name: locale.NAV.ARCHIVE,
- href: '/archive',
- show: siteConfig('GITBOOK_MENU_ARCHIVE', null, CONFIG)
- },
- {
- icon: 'fas fa-search',
- name: locale.NAV.SEARCH,
- href: '/search',
- show: siteConfig('GITBOOK_MENU_SEARCH', null, CONFIG)
- }
- ]
-
- let links = defaultLinks.concat(customNav)
-
- const toggleMenuOpen = () => {
- changeShow(!isOpen)
- }
-
- // 若啟用自訂選單則覆寫頁面內建選單
- if (siteConfig('CUSTOM_MENU')) {
- links = customMenu
- }
-
- const enableClerk = process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
-
- return (
-
- {/* 桌面端選單 */}
-
-
- {/* 左側 */}
-
-
-
- {/* 桌面端頂部選單 */}
-
- {links &&
- links?.map((link, index) => (
-
- ))}
-
-
-
- {/* 右側 */}
-
- {/* 登入相關 */}
- {enableClerk && (
- <>
-
-
-
-
-
-
- >
- )}
-
-
- {/* 摺疊按鈕,僅行動版顯示 */}
-
-
-
- {isOpen ? (
-
- ) : (
-
- )}
-
-
-
-
-
-
- {/* 行動版摺疊選單 */}
-
-
-
- collapseRef.current?.updateCollapseHeight(param)
- }
- />
-
-
-
- )
-}
diff --git a/gitbook/components/InfoCard.js b/gitbook/components/InfoCard.js
deleted file mode 100644
index 4b6e5cf..0000000
--- a/gitbook/components/InfoCard.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import LazyImage from '@/components/LazyImage'
-import Router from 'next/router'
-import SocialButton from './SocialButton'
-import { siteConfig } from '@/lib/config'
-
-const InfoCard = (props) => {
- const { siteInfo } = props
- return
-
-
{ Router.push('/about') }}>
-
-
-
{siteConfig('AUTHOR')}
-
{siteConfig('BIO')}
-
-
-
-}
-
-export default InfoCard
diff --git a/gitbook/components/JumpToTopButton.js b/gitbook/components/JumpToTopButton.js
deleted file mode 100644
index 4041851..0000000
--- a/gitbook/components/JumpToTopButton.js
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * 跳轉至網頁頂端
- * 當畫面往下滑 500 像素後會顯示此元件
- * @param targetRef 關聯高度的目標 HTML 標籤
- * @param showPercent 是否顯示百分比
- * @returns {JSX.Element}
- * @constructor
- */
-const JumpToTopButton = ({ showPercent = false, percent, className }) => {
- return (
-
- {
- window.scrollTo({ top: 0, behavior: 'smooth' })
- }}
- />
-
- )
-}
-
-export default JumpToTopButton
diff --git a/gitbook/components/LeftMenuBar.js b/gitbook/components/LeftMenuBar.js
deleted file mode 100644
index 6f9da09..0000000
--- a/gitbook/components/LeftMenuBar.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import SmartLink from '@/components/SmartLink'
-
-export default function LeftMenuBar () {
- return (
-
- );
-}
diff --git a/gitbook/components/LogoBar.js b/gitbook/components/LogoBar.js
deleted file mode 100644
index 166ded5..0000000
--- a/gitbook/components/LogoBar.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import LazyImage from '@/components/LazyImage'
-import { siteConfig } from '@/lib/config'
-import SmartLink from '@/components/SmartLink'
-import CONFIG from '../config'
-
-/**
- * Logo 區域
- * @param {*} props
- * @returns
- */
-export default function LogoBar(props) {
- const { siteInfo } = props
- return (
-
-
-
- {siteInfo?.title || siteConfig('TITLE')}
-
-
- )
-}
diff --git a/gitbook/components/MenuBarMobile.js b/gitbook/components/MenuBarMobile.js
deleted file mode 100644
index e174fdb..0000000
--- a/gitbook/components/MenuBarMobile.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import { siteConfig } from '@/lib/config'
-import { useGlobal } from '@/lib/global'
-import CONFIG from '../config'
-import { MenuItemCollapse } from './MenuItemCollapse'
-
-export const MenuBarMobile = props => {
- const { customMenu, customNav } = props
- const { locale } = useGlobal()
-
- let links = [
- // { name: locale.NAV.INDEX, href: '/' || '/', show: true },
- {
- name: locale.COMMON.CATEGORY,
- href: '/category',
- show: siteConfig('GITBOOK_MENU_CATEGORY', null, CONFIG)
- },
- {
- name: locale.COMMON.TAGS,
- href: '/tag',
- show: siteConfig('GITBOOK_BOOK_MENU_TAG', null, CONFIG)
- },
- {
- name: locale.NAV.ARCHIVE,
- href: '/archive',
- show: siteConfig('GITBOOK_MENU_ARCHIVE', null, CONFIG)
- }
- // { name: locale.NAV.SEARCH, href: '/search', show: siteConfig('MENU_SEARCH', null, CONFIG) }
- ]
-
- if (customNav) {
- links = links.concat(customNav)
- }
-
- // 若啟用自訂選單,則不再使用 Page 生成的選單。
- if (siteConfig('CUSTOM_MENU')) {
- links = customMenu
- }
-
- if (!links || links.length === 0) {
- return null
- }
-
- return (
-
- )
-}
diff --git a/gitbook/components/MenuItemCollapse.js b/gitbook/components/MenuItemCollapse.js
deleted file mode 100644
index 378cdc5..0000000
--- a/gitbook/components/MenuItemCollapse.js
+++ /dev/null
@@ -1,97 +0,0 @@
-import Collapse from '@/components/Collapse'
-import SmartLink from '@/components/SmartLink'
-import { useRouter } from 'next/router'
-import { useState } from 'react'
-
-/**
- * 摺疊選單
- * @param {*} param0
- * @returns
- */
-export const MenuItemCollapse = props => {
- const { link } = props
- const [show, changeShow] = useState(false)
- const hasSubMenu = link?.subMenus?.length > 0
-
- const [isOpen, changeIsOpen] = useState(false)
-
- const router = useRouter()
-
- if (!link || !link.show) {
- return null
- }
-
- const selected = router.pathname === link.href || router.asPath === link.href
-
- const toggleShow = () => {
- changeShow(!show)
- }
-
- const toggleOpenSubMenu = () => {
- changeIsOpen(!isOpen)
- }
-
- return (
- <>
-
- {!hasSubMenu && (
-
-
-
- )}
-
- {hasSubMenu && (
-
- )}
-
-
- {/* 摺疊子選單 */}
- {hasSubMenu && (
-
- {link?.subMenus?.map((sLink, index) => {
- return (
-
- )
- })}
-
- )}
- >
- )
-}
diff --git a/gitbook/components/MenuItemDrop.js b/gitbook/components/MenuItemDrop.js
deleted file mode 100644
index ffbc90e..0000000
--- a/gitbook/components/MenuItemDrop.js
+++ /dev/null
@@ -1,73 +0,0 @@
-import SmartLink from '@/components/SmartLink'
-import { useRouter } from 'next/router'
-import { useState } from 'react'
-
-export const MenuItemDrop = ({ link }) => {
- const [show, changeShow] = useState(false)
- const router = useRouter()
-
- if (!link || !link.show) {
- return null
- }
- const hasSubMenu = link?.subMenus?.length > 0
- const selected = router.pathname === link.href || router.asPath === link.href
- return (
- changeShow(true)}
- onMouseOut={() => changeShow(false)}>
- {!hasSubMenu && (
-
-
- {link?.icon && } {link?.name}
-
-
- )}
-
- {/* 包含子選單 */}
- {hasSubMenu && (
- <>
-
-
- {link?.icon && } {link?.name}
- {hasSubMenu && (
-
- )}
-
-
- {/* 下拉選單內容 */}
-
- {link?.subMenus?.map((sLink, index) => {
- return (
- -
-
-
- {link?.icon && }
- {sLink.title}
-
-
-
- )
- })}
-
- >
- )}
-
- )
-}
diff --git a/gitbook/components/MenuItemMobileNormal.js b/gitbook/components/MenuItemMobileNormal.js
deleted file mode 100644
index d04ba79..0000000
--- a/gitbook/components/MenuItemMobileNormal.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import SmartLink from '@/components/SmartLink'
-import { useRouter } from 'next/router'
-
-export const NormalMenu = props => {
- const { link } = props
- const router = useRouter()
-
- if (!link || !link.show) {
- return null
- }
-
- const selected = router.pathname === link.href || router.asPath === link.href
-
- return (
-
-
- {link.slot}
-
- )
-}
diff --git a/gitbook/components/MenuItemPCNormal.js b/gitbook/components/MenuItemPCNormal.js
deleted file mode 100644
index 06bee8a..0000000
--- a/gitbook/components/MenuItemPCNormal.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import SmartLink from '@/components/SmartLink'
-import { useRouter } from 'next/router'
-
-export const MenuItemPCNormal = props => {
- const { link } = props
- const router = useRouter()
- const selected = router.pathname === link.href || router.asPath === link.href
- if (!link || !link.show) {
- return null
- }
-
- return (
-
-
- {link.slot}
-
- )
-}
diff --git a/gitbook/components/NavPostItem.js b/gitbook/components/NavPostItem.js
deleted file mode 100644
index a6102be..0000000
--- a/gitbook/components/NavPostItem.js
+++ /dev/null
@@ -1,90 +0,0 @@
-import Badge from '@/components/Badge'
-import Collapse from '@/components/Collapse'
-import { siteConfig } from '@/lib/config'
-import { useEffect, useState } from 'react'
-import BlogPostCard from './BlogPostCard'
-
-/**
- * 導覽列表
- * @param posts 所有文章
- * @param tags 所有標籤
- * @returns {JSX.Element}
- * @constructor
- */
-const NavPostItem = props => {
- const { group, expanded, toggleItem } = props // 接收傳入的展開狀態與切換函式
- const hoverExpand = siteConfig('GITBOOK_FOLDER_HOVER_EXPAND')
- const [isTouchDevice, setIsTouchDevice] = useState(false)
-
- // 偵測是否為觸控裝置
- useEffect(() => {
- const checkTouchDevice = () => {
- if (window.matchMedia('(pointer: coarse)').matches) {
- setIsTouchDevice(true)
- }
- }
- checkTouchDevice()
-
- // 選用:監聽視窗尺寸變化時重新檢測
- window.addEventListener('resize', checkTouchDevice)
- return () => {
- window.removeEventListener('resize', checkTouchDevice)
- }
- }, [])
-
- // 當展開狀態改變時觸發切換函式並同步內部狀態
- const toggleOpenSubMenu = () => {
- toggleItem() // 呼叫父元件傳入的切換函式
- }
- const onHoverToggle = () => {
- // 允許滑鼠懸停時自動展開,而非點擊
- if (!hoverExpand || isTouchDevice) {
- return
- }
- toggleOpenSubMenu()
- }
-
- const groupHasLatest = group?.items?.some(post => post.isLatest)
-
- if (group?.category) {
- return (
- <>
-
-
- {group?.category}
-
-
-
-
- {groupHasLatest &&
- siteConfig('GITBOOK_LATEST_POST_RED_BADGE') &&
- !expanded &&
}
-
-
- {group?.items?.map((post, index) => (
-
-
-
- ))}
-
- >
- )
- } else {
- return (
- <>
- {group?.items?.map((post, index) => (
-
-
-
- ))}
- >
- )
- }
-}
-
-export default NavPostItem
diff --git a/gitbook/components/NavPostList.js b/gitbook/components/NavPostList.js
deleted file mode 100644
index 2a5cdac..0000000
--- a/gitbook/components/NavPostList.js
+++ /dev/null
@@ -1,165 +0,0 @@
-import { siteConfig } from '@/lib/config'
-import { useGlobal } from '@/lib/global'
-import { useRouter } from 'next/router'
-import { useEffect, useState } from 'react'
-import CONFIG from '../config'
-import BlogPostCard from './BlogPostCard'
-import NavPostItem from './NavPostItem'
-
-/**
- * 部落格列表滾動分頁
- * @param posts 所有文章
- * @param tags 所有標籤
- * @returns {JSX.Element}
- * @constructor
- */
-const NavPostList = props => {
- const { filteredNavPages } = props
- const { locale, currentSearch } = useGlobal()
- const router = useRouter()
-
- // 依分類將文章歸成資料夾
- const categoryFolders = groupArticles(filteredNavPages)
-
- // 存放被展開的群組
- const [expandedGroups, setExpandedGroups] = useState([])
-
- // 是否採用排他折疊,一次只展開一個資料夾
- const GITBOOK_EXCLUSIVE_COLLAPSE = siteConfig(
- 'GITBOOK_EXCLUSIVE_COLLAPSE',
- null,
- CONFIG
- )
-
- useEffect(() => {
- // 展開資料夾
- setTimeout(() => {
- const currentPath = decodeURIComponent(router.asPath.split('?')[0])
- const defaultOpenIndex = getDefaultOpenIndexByPath(
- categoryFolders,
- currentPath
- )
- setExpandedGroups([defaultOpenIndex])
- }, 500)
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [router, filteredNavPages])
-
- // 切換折疊項目,當陣列狀態改變時觸發
- const toggleItem = index => {
- let newExpandedGroups = [...expandedGroups] // 建立新的展開群組陣列
-
- // 若 expandedGroups 中不存在則加入,存在則移除
- if (expandedGroups.includes(index)) {
- // 若 expandedGroups 中包含 index,則移除
- newExpandedGroups = newExpandedGroups.filter(
- expandedIndex => expandedIndex !== index
- )
- } else {
- // 若 expandedGroups 中不含 index,則加入
- newExpandedGroups.push(index)
- }
- // 是否排他
- if (GITBOOK_EXCLUSIVE_COLLAPSE) {
- // 若折疊選單為排他模式,僅保留目前群組其餘關閉
- newExpandedGroups = newExpandedGroups.filter(
- expandedIndex => expandedIndex === index
- )
- }
-
- // 更新展開群組陣列
- setExpandedGroups(newExpandedGroups)
- }
-
- // 無資料時
- if (!categoryFolders || categoryFolders.length === 0) {
- // 空白內容
- return (
-
-
- {locale.COMMON.NO_RESULTS_FOUND}{' '}
- {currentSearch &&
{currentSearch}
}
-
-
- )
- }
- // 文章首頁對應路徑
- const href = siteConfig('GITBOOK_INDEX_PAGE') + ''
-
- const homePost = {
- id: '-1',
- title: siteConfig('DESCRIPTION'),
- href: href.indexOf('/') !== 0 ? '/' + href : href
- }
-
- return (
-
- {/* 當前文章 */}
-
-
- {/* 文章列表 */}
- {categoryFolders?.map((group, index) => (
- toggleItem(index)} // 將切換函式傳給子元件
- />
- ))}
-
- )
-}
-
-// 依分類將文章歸成資料夾
-function groupArticles(filteredNavPages) {
- if (!filteredNavPages) {
- return []
- }
- const groups = []
- const AUTO_SORT = siteConfig('GITBOOK_AUTO_SORT', true, CONFIG)
-
- for (let i = 0; i < filteredNavPages.length; i++) {
- const item = filteredNavPages[i]
- const categoryName = item?.category ? item?.category : '' // 將 category 轉成字串
-
- let existingGroup = null
- // 啟用自動分組排序;會把相同分類歸到同一個資料夾,忽略 Notion 中的排序
- if (AUTO_SORT) {
- existingGroup = groups.find(group => group.category === categoryName) // 尋找同名的最後一個群組
- } else {
- existingGroup = groups[groups.length - 1] // 取得最後一個群組
- }
-
- // 新增資料
- if (existingGroup && existingGroup.category === categoryName) {
- existingGroup.items.push(item)
- } else {
- groups.push({ category: categoryName, items: [item] })
- }
- }
- return groups
-}
-
-/**
- * 查看當前路徑需要展開的選單索引
- * 若皆不符合則回傳 0,即預設展開第一個
- * @param {*} categoryFolders
- * @param {*} path
- * @returns {number} 需展開的選單索引
- */
-function getDefaultOpenIndexByPath(categoryFolders, path) {
- // 找出符合條件的第一個索引
- const index = categoryFolders.findIndex(group => {
- return group.items.some(post => path === post.href)
- })
-
- // 若找到符合條件的索引則回傳
- if (index !== -1) {
- return index
- }
-
- return 0
-}
-export default NavPostList
diff --git a/gitbook/components/PageNavDrawer.js b/gitbook/components/PageNavDrawer.js
deleted file mode 100644
index eada964..0000000
--- a/gitbook/components/PageNavDrawer.js
+++ /dev/null
@@ -1,61 +0,0 @@
-import { useGlobal } from '@/lib/global'
-import { useGitBookGlobal } from '@/themes/gitbook'
-import { useRouter } from 'next/router'
-import { useEffect } from 'react'
-import NavPostList from './NavPostList'
-
-/**
- * 懸浮抽屜 頁面內導覽
- * @param toc
- * @param post
- * @returns {JSX.Element}
- * @constructor
- */
-const PageNavDrawer = props => {
- const { pageNavVisible, changePageNavVisible } = useGitBookGlobal()
- const { filteredNavPages } = props
- const { locale } = useGlobal()
- const router = useRouter()
- const switchVisible = () => {
- changePageNavVisible(!pageNavVisible)
- }
-
- useEffect(() => {
- changePageNavVisible(false)
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [router])
-
- return (
- <>
-
- {/* 側邊選單 */}
-
-
- {locale.COMMON.ARTICLE_LIST}
- {
- changePageNavVisible(false)
- }}>
-
- {/* 所有文章列表 */}
-
-
-
-
-
-
- {/* 背景遮罩 */}
-
- >
- )
-}
-export default PageNavDrawer
diff --git a/gitbook/components/PaginationSimple.js b/gitbook/components/PaginationSimple.js
deleted file mode 100644
index 7e579df..0000000
--- a/gitbook/components/PaginationSimple.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import SmartLink from '@/components/SmartLink'
-import { useRouter } from 'next/router'
-import { useGlobal } from '@/lib/global'
-
-/**
- * 簡易翻頁外掛
- * @param page 當前頁碼
- * @param totalPage 是否有下一頁
- * @returns {JSX.Element}
- * @constructor
- */
-const PaginationSimple = ({ page, totalPage }) => {
- const { locale } = useGlobal()
- const router = useRouter()
- const currentPage = +page
- const showNext = currentPage < totalPage
- const pagePrefix = router.asPath.replace(/\/page\/[1-9]\d*/, '').replace(/\/$/, '')
-
- return (
-
-
- ←{locale.PAGINATION.PREV}
-
-
-
-
- {locale.PAGINATION.NEXT}→
-
-
- )
-}
-
-export default PaginationSimple
diff --git a/gitbook/components/Progress.js b/gitbook/components/Progress.js
deleted file mode 100644
index 3ae1dcc..0000000
--- a/gitbook/components/Progress.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import { useEffect, useState } from 'react'
-import { isBrowser } from '@/lib/utils'
-
-/**
- * 頂部頁面閱讀進度條
- * @returns {JSX.Element}
- * @constructor
- */
-const Progress = ({ targetRef, showPercent = true }) => {
- const currentRef = targetRef?.current || targetRef
- const [percent, changePercent] = useState(0)
- const scrollListener = () => {
- const target = currentRef || (isBrowser && document.getElementById('posts-wrapper'))
- if (target) {
- const clientHeight = target.clientHeight
- const scrollY = window.pageYOffset
- const fullHeight = clientHeight - window.outerHeight
- let per = parseFloat(((scrollY / fullHeight) * 100).toFixed(0))
- if (per > 100) per = 100
- if (per < 0) per = 0
- changePercent(per)
- }
- }
-
- useEffect(() => {
- document.addEventListener('scroll', scrollListener)
- return () => document.removeEventListener('scroll', scrollListener)
- }, [])
-
- return (
-
-
- {showPercent && (
-
{percent}%
- )}
-
-
- )
-}
-
-export default Progress
diff --git a/gitbook/components/RevolverMaps.js b/gitbook/components/RevolverMaps.js
deleted file mode 100644
index b994de5..0000000
--- a/gitbook/components/RevolverMaps.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import { useEffect, useState } from 'react'
-
-export default function RevolverMaps () {
- const [load, changeLoad] = useState(false)
- useEffect(() => {
- if (!load) {
- initRevolverMaps()
- changeLoad(true)
- }
- }, [])
- return
-}
-
-function initRevolverMaps () {
- if (screen.width >= 768) {
- Promise.all([
- loadExternalResource('https://rf.revolvermaps.com/0/0/8.js?i=5jnp1havmh9&m=0&c=ff0000&cr1=ffffff&f=arial&l=33')
- ]).then(() => {
- console.log('地圖載入完成')
- })
- }
-}
-
-// 封裝非同步載入資源的方法
-function loadExternalResource (url) {
- return new Promise((resolve, reject) => {
- const container = document.getElementById('revolvermaps')
- const tag = document.createElement('script')
- tag.src = url
- if (tag) {
- tag.onload = () => resolve(url)
- tag.onerror = () => reject(url)
- container.appendChild(tag)
- }
- })
-}
diff --git a/gitbook/components/SearchInput.js b/gitbook/components/SearchInput.js
deleted file mode 100644
index 5c2cfc9..0000000
--- a/gitbook/components/SearchInput.js
+++ /dev/null
@@ -1,159 +0,0 @@
-import { siteConfig } from '@/lib/config'
-import { deepClone } from '@/lib/utils'
-import { useGitBookGlobal } from '@/themes/gitbook'
-import { useImperativeHandle, useRef, useState } from 'react'
-import { useHotkeys } from 'react-hotkeys-hook'
-let lock = false
-
-/**
- * 搜尋列
- */
-const SearchInput = ({ currentSearch, cRef, className }) => {
- const searchInputRef = useRef()
- const { searchModal, setFilteredNavPages, allNavPages } = useGitBookGlobal()
-
- useImperativeHandle(cRef, () => {
- return {
- focus: () => {
- searchInputRef?.current?.focus()
- }
- }
- })
-
- /**
- * 快捷鍵設定
- */
- useHotkeys('ctrl+k', e => {
- searchInputRef?.current?.focus()
- e.preventDefault()
- handleSearch()
- })
-
- const handleSearch = () => {
- // 使用 Algolia
- if (siteConfig('ALGOLIA_APP_ID')) {
- searchModal?.current?.openSearch()
- }
- let keyword = searchInputRef.current.value
- if (keyword) {
- keyword = keyword.trim()
- } else {
- setFilteredNavPages(allNavPages)
- return
- }
- const filterAllNavPages = deepClone(allNavPages)
-
- for (let i = filterAllNavPages.length - 1; i >= 0; i--) {
- const post = filterAllNavPages[i]
- const articleInfo = post.title + ''
- const hit = articleInfo.toLowerCase().indexOf(keyword.toLowerCase()) > -1
- if (!hit) {
- // 刪除
- filterAllNavPages.splice(i, 1)
- }
- }
-
- // 更新完成
- setFilteredNavPages(filterAllNavPages)
- }
-
- /**
- * Enter 鍵
- * @param {*} e
- */
- const handleKeyUp = e => {
- // 使用 Algolia
- if (siteConfig('ALGOLIA_APP_ID')) {
- searchModal?.current?.openSearch()
- return
- }
-
- if (e.keyCode === 13) {
- // Enter
- handleSearch(searchInputRef.current.value)
- } else if (e.keyCode === 27) {
- // ESC
- cleanSearch()
- }
- }
-
- const handleFocus = () => {
- // 使用 Algolia
- if (siteConfig('ALGOLIA_APP_ID')) {
- searchModal?.current?.openSearch()
- }
- }
-
- /**
- * 清除搜尋
- */
- const cleanSearch = () => {
- searchInputRef.current.value = ''
- handleSearch()
- setShowClean(false)
- }
-
- const [showClean, setShowClean] = useState(false)
- const updateSearchKey = val => {
- if (lock) {
- return
- }
- searchInputRef.current.value = val
- if (val) {
- setShowClean(true)
- } else {
- setShowClean(false)
- }
- }
-
- function lockSearchInput() {
- lock = true
- }
-
- function unLockSearchInput() {
- lock = false
- }
-
- return (
-
-
-
-
-
updateSearchKey(e.target.value)}
- defaultValue={currentSearch}
- />
-
- Ctrl+K
-
-
- {showClean && (
-
-
-
- )}
-
- )
-}
-
-export default SearchInput
diff --git a/gitbook/components/SocialButton.js b/gitbook/components/SocialButton.js
deleted file mode 100644
index 5bf8688..0000000
--- a/gitbook/components/SocialButton.js
+++ /dev/null
@@ -1,188 +0,0 @@
-import QrCode from '@/components/QrCode'
-import { siteConfig } from '@/lib/config'
-import { useRef, useState } from 'react'
-import { handleEmailClick } from '@/lib/plugins/mailEncrypt'
-
-/**
- * 社群聯絡按鈕組
- * @returns {JSX.Element}
- * @constructor
- */
-const SocialButton = () => {
- const CONTACT_GITHUB = siteConfig('CONTACT_GITHUB')
- const CONTACT_TWITTER = siteConfig('CONTACT_TWITTER')
- const CONTACT_TELEGRAM = siteConfig('CONTACT_TELEGRAM')
-
- const CONTACT_LINKEDIN = siteConfig('CONTACT_LINKEDIN')
- const CONTACT_WEIBO = siteConfig('CONTACT_WEIBO')
- const CONTACT_INSTAGRAM = siteConfig('CONTACT_INSTAGRAM')
- const CONTACT_EMAIL = siteConfig('CONTACT_EMAIL')
- const ENABLE_RSS = siteConfig('ENABLE_RSS')
- const CONTACT_BILIBILI = siteConfig('CONTACT_BILIBILI')
- const CONTACT_YOUTUBE = siteConfig('CONTACT_YOUTUBE')
-
- const CONTACT_XIAOHONGSHU = siteConfig('CONTACT_XIAOHONGSHU')
- const CONTACT_ZHISHIXINGQIU = siteConfig('CONTACT_ZHISHIXINGQIU')
- const CONTACT_WEHCHAT_PUBLIC = siteConfig('CONTACT_WEHCHAT_PUBLIC')
- const [qrCodeShow, setQrCodeShow] = useState(false)
-
- const openPopover = () => {
- setQrCodeShow(true)
- }
- const closePopover = () => {
- setQrCodeShow(false)
- }
-
- const emailIcon = useRef(null)
-
-
- return (
-
- )
-}
-export default SocialButton
diff --git a/gitbook/components/TagGroups.js b/gitbook/components/TagGroups.js
deleted file mode 100644
index b02594e..0000000
--- a/gitbook/components/TagGroups.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import TagItemMini from './TagItemMini'
-
-/**
- * 標籤組
- * @param tags
- * @param currentTag
- * @returns {JSX.Element}
- * @constructor
- */
-const TagGroups = ({ tagOptions, currentTag }) => {
- if (!tagOptions) return <>>
- return (
-
- )
-}
-
-export default TagGroups
diff --git a/gitbook/components/TagItemMini.js b/gitbook/components/TagItemMini.js
deleted file mode 100644
index e8fde28..0000000
--- a/gitbook/components/TagItemMini.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import SmartLink from '@/components/SmartLink'
-
-const TagItemMini = ({ tag, selected = false }) => {
- return (
-
-
- {selected && } {tag.name + (tag.count ? `(${tag.count})` : '')}
-
-
- )
-}
-
-export default TagItemMini
diff --git a/gitbook/config.js b/gitbook/config.js
deleted file mode 100644
index dfef470..0000000
--- a/gitbook/config.js
+++ /dev/null
@@ -1,25 +0,0 @@
-const CONFIG = {
- GITBOOK_INDEX_PAGE: 'about', // 文件首頁顯示的文章,請確認此路徑包含在您的 Notion 資料庫中
-
- GITBOOK_AUTO_SORT: process.env.NEXT_PUBLIC_GITBOOK_AUTO_SORT || true, // 是否自動依分類名稱分組排序文章;自動分組可能會打亂您在 Notion 中的文章順序
-
- GITBOOK_LATEST_POST_RED_BADGE:
- process.env.NEXT_PUBLIC_GITBOOK_LATEST_POST_RED_BADGE || true, // 是否替最新文章顯示紅點
-
- // 選單
- GITBOOK_MENU_CATEGORY: true, // 顯示分類
- GITBOOK_BOOK_MENU_TAG: true, // 顯示標籤
- GITBOOK_MENU_ARCHIVE: true, // 顯示歸檔
- GITBOOK_MENU_SEARCH: true, // 顯示搜尋
-
- // 導覽文章自動排他折疊
- GITBOOK_EXCLUSIVE_COLLAPSE: true, // 一次只展開一個分類,其它資料夾自動關閉。
-
- GITBOOK_FOLDER_HOVER_EXPAND: false, // 左側導覽資料夾滑鼠懸停時自動展開;若為 false 則需點擊才會展開
-
- // Widget
- GITBOOK_WIDGET_REVOLVER_MAPS:
- process.env.NEXT_PUBLIC_WIDGET_REVOLVER_MAPS || 'false', // 地圖外掛
- GITBOOK_WIDGET_TO_TOP: true // 回到頂端按鈕
-}
-export default CONFIG
diff --git a/gitbook/index.js b/gitbook/index.js
deleted file mode 100644
index 02e9f69..0000000
--- a/gitbook/index.js
+++ /dev/null
@@ -1,14 +0,0 @@
-export { LayoutBase, useGitBookGlobal } from './layouts/BaseLayout'
-export {
- LayoutDashboard,
- LayoutIndex,
- LayoutPostList,
- LayoutSearch
-} from './layouts/ListLayouts'
-export { LayoutArchive } from './layouts/ArchiveLayout'
-export { LayoutSlug } from './layouts/SlugLayout'
-export { Layout404 } from './layouts/NotFoundLayout'
-export { LayoutCategoryIndex, LayoutTagIndex } from './layouts/TaxonomyLayouts'
-export { LayoutSignIn, LayoutSignUp } from './layouts/AuthLayouts'
-export { default as CONFIG } from './config'
-export { default as THEME_CONFIG } from './config'
diff --git a/gitbook/layouts/ArchiveLayout.jsx b/gitbook/layouts/ArchiveLayout.jsx
deleted file mode 100644
index 7b3cd46..0000000
--- a/gitbook/layouts/ArchiveLayout.jsx
+++ /dev/null
@@ -1,27 +0,0 @@
-'use client'
-
-import BlogArchiveItem from '../components/BlogArchiveItem'
-
-/**
- * 歸檔頁面
- * 主要依靠頁面導覽
- */
-const LayoutArchive = props => {
- const { archivePosts } = props
-
- return (
- <>
-
- {Object.keys(archivePosts)?.map(archiveTitle => (
-
- ))}
-
- >
- )
-}
-
-export { LayoutArchive }
diff --git a/gitbook/layouts/AuthLayouts.jsx b/gitbook/layouts/AuthLayouts.jsx
deleted file mode 100644
index b0da5b2..0000000
--- a/gitbook/layouts/AuthLayouts.jsx
+++ /dev/null
@@ -1,54 +0,0 @@
-'use client'
-
-import NotionPage from '@/components/NotionPage'
-import { SignIn, SignUp } from '@clerk/nextjs'
-
-/**
- * 登入頁面
- */
-const LayoutSignIn = props => {
- const { post } = props
- const enableClerk = process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
-
- return (
- <>
-
- {/* Clerk 預設表單 */}
- {enableClerk && (
-
-
-
- )}
-
-
-
-
- >
- )
-}
-
-/**
- * 註冊頁面
- */
-const LayoutSignUp = props => {
- const { post } = props
- const enableClerk = process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
-
- return (
- <>
-
- {/* Clerk 預設表單 */}
- {enableClerk && (
-
-
-
- )}
-
-
-
-
- >
- )
-}
-
-export { LayoutSignIn, LayoutSignUp }
diff --git a/gitbook/layouts/BaseLayout.jsx b/gitbook/layouts/BaseLayout.jsx
deleted file mode 100644
index 9733332..0000000
--- a/gitbook/layouts/BaseLayout.jsx
+++ /dev/null
@@ -1,223 +0,0 @@
-'use client'
-
-import { AdSlot } from '@/components/GoogleAdsense'
-import Live2D from '@/components/Live2D'
-import LoadingCover from '@/components/LoadingCover'
-import dynamic from 'next/dynamic'
-import { createContext, useContext, useEffect, useRef, useState } from 'react'
-import { siteConfig } from '@/lib/config'
-import { useGlobal } from '@/lib/global'
-import { useRouter } from 'next/router'
-import { getShortId } from '@/lib/utils/pageId'
-import Announcement from '../components/Announcement'
-import ArticleInfo from '../components/ArticleInfo'
-import BottomMenuBar from '../components/BottomMenuBar'
-import Catalog from '../components/Catalog'
-import Footer from '../components/Footer'
-import Header from '../components/Header'
-import InfoCard from '../components/InfoCard'
-import JumpToTopButton from '../components/JumpToTopButton'
-import NavPostList from '../components/NavPostList'
-import PageNavDrawer from '../components/PageNavDrawer'
-import RevolverMaps from '../components/RevolverMaps'
-import CONFIG from '../config'
-import { Style } from '../style'
-
-const AlgoliaSearchModal = dynamic(
- () => import('@/components/AlgoliaSearchModal'),
- { ssr: false }
-)
-const WWAds = dynamic(() => import('@/components/WWAds'), { ssr: false })
-
-// 主題全域變數
-const ThemeGlobalGitbook = createContext()
-const useGitBookGlobal = () => useContext(ThemeGlobalGitbook)
-
-/**
- * 為最新文章加入紅點標記
- */
-function getNavPagesWithLatest(allNavPages, latestPosts, post) {
- // localStorage 儲存 id 與上次閱讀時間戳: posts_read_time = {"${post.id}":"Date()"}
- const postReadTime = JSON.parse(
- localStorage.getItem('post_read_time') || '{}'
- )
- if (post) {
- postReadTime[getShortId(post.id)] = new Date().getTime()
- }
- // 更新記錄
- localStorage.setItem('post_read_time', JSON.stringify(postReadTime))
-
- return allNavPages?.map(item => {
- const res = {
- short_id: item.short_id,
- title: item.title || '',
- pageCoverThumbnail: item.pageCoverThumbnail || '',
- category: item.category || null,
- tags: item.tags || null,
- summary: item.summary || null,
- slug: item.slug,
- href: item.href,
- pageIcon: item.pageIcon || '',
- lastEditedDate: item.lastEditedDate
- }
- // 屬於最新的文章通常 6 篇 &&(無閱讀紀錄 || 最近更新時間大於上次閱讀時間)
- if (
- latestPosts.some(post => post?.id.indexOf(item?.short_id) === 14) &&
- (!postReadTime[item.short_id] ||
- postReadTime[item.short_id] < new Date(item.lastEditedDate).getTime())
- ) {
- return { ...res, isLatest: true }
- } else {
- return res
- }
- })
-}
-
-/**
- * 基礎佈局
- * 左右雙欄,手機版改為頂部導覽列
- */
-const LayoutBase = props => {
- const {
- children,
- post,
- allNavPages,
- latestPosts,
- slotLeft,
- slotRight,
- slotTop
- } = props
- const { fullWidth } = useGlobal()
- const router = useRouter()
- const [tocVisible, changeTocVisible] = useState(false)
- const [pageNavVisible, changePageNavVisible] = useState(false)
- const [filteredNavPages, setFilteredNavPages] = useState(allNavPages)
-
- const searchModal = useRef(null)
-
- useEffect(() => {
- setFilteredNavPages(getNavPagesWithLatest(allNavPages, latestPosts, post))
- }, [router])
-
- const GITBOOK_LOADING_COVER = siteConfig(
- 'GITBOOK_LOADING_COVER',
- true,
- CONFIG
- )
- return (
-
-
-
-
-
-
- {/* 頂部導覽列 */}
-
-
-
- {/* 左側抽屜 */}
- {fullWidth ? null : (
-
-
- {/* 導覽清單 */}
-
- {/* 嵌入區塊 */}
- {slotLeft}
-
- {/* 文章列表 */}
-
-
- {/* 頁尾 */}
-
-
-
- )}
-
- {/* 內容區域 */}
-
-
- {slotTop}
-
-
- {children}
-
- {/* Google 廣告 */}
-
-
-
-
- {/* 手機版頁尾 */}
-
-
-
-
-
- {/* 右側資訊欄 */}
- {fullWidth ? null : (
-
-
-
-
-
- {/* 桌面版目錄 */}
-
- {slotRight}
- {router.route === '/' && (
- <>
-
- {siteConfig(
- 'GITBOOK_WIDGET_REVOLVER_MAPS',
- null,
- CONFIG
- ) === 'true' &&
}
-
- >
- )}
- {/* 主題首頁僅顯示公告 */}
-
-
-
-
-
-
-
- )}
-
-
- {GITBOOK_LOADING_COVER &&
}
-
- {/* 回頂按鈕 */}
-
-
- {/* 手機版導覽抽屜 */}
-
-
- {/* 手機版底部導覽列 */}
-
-
-
- )
-}
-
-export { LayoutBase, useGitBookGlobal }
diff --git a/gitbook/layouts/ListLayouts.jsx b/gitbook/layouts/ListLayouts.jsx
deleted file mode 100644
index 407d95c..0000000
--- a/gitbook/layouts/ListLayouts.jsx
+++ /dev/null
@@ -1,101 +0,0 @@
-'use client'
-
-import NotionPage from '@/components/NotionPage'
-import { siteConfig } from '@/lib/config'
-import { useEffect, useState } from 'react'
-import { useRouter } from 'next/router'
-import DashboardBody from '../components/ui/dashboard/DashboardBody'
-import DashboardHeader from '../components/ui/dashboard/DashboardHeader'
-import CONFIG from '../config'
-
-/**
- * 首頁
- * 重新導向到指定的文章詳情頁
- */
-const LayoutIndex = props => {
- const router = useRouter()
- const index = siteConfig('GITBOOK_INDEX_PAGE', 'about', CONFIG)
- const [hasRedirected, setHasRedirected] = useState(false) // 用來追蹤是否已重新導向
-
- useEffect(() => {
- const tryRedirect = async () => {
- if (!hasRedirected) {
- // 僅在尚未導向時執行
- setHasRedirected(true)
-
- // 重新導向至指定文章
- await router.push(index)
-
- // 使用 setTimeout 檢查頁面載入狀態
- setTimeout(() => {
- const article = document.querySelector(
- '#article-wrapper #notion-article'
- )
- if (!article) {
- console.log('請檢查您的 Notion 資料庫中是否包含此 slug 頁面: ', index)
-
- // 顯示錯誤訊息
- const containerInner = document.querySelector(
- '#theme-gitbook #container-inner'
- )
- const newHTML = `設定錯誤
請在您的 Notion 中新增一篇 slug 為 ${index} 的文章
`
- containerInner?.insertAdjacentHTML('afterbegin', newHTML)
- }
- }, 2000)
- }
- }
-
- if (index) {
- console.log('重新導向', index)
- tryRedirect()
- } else {
- console.log('未重新導向', index)
- }
- }, [index, hasRedirected, router])
-
- return null
-}
-
-/**
- * 文章列表
- * 主要依靠頁面導覽
- */
-const LayoutPostList = () => {
- return <>>
-}
-
-/**
- * 文章搜尋頁
- * 主要依靠頁面導覽
- */
-const LayoutSearch = () => {
- return <>>
-}
-
-/**
- * 儀表板
- */
-const LayoutDashboard = props => {
- const { post } = props
-
- return (
- <>
-
-
-
- {post && (
-
-
-
- )}
-
-
-
- {/* 儀表板 */}
-
-
- >
- )
-}
-
-export { LayoutDashboard, LayoutIndex, LayoutPostList, LayoutSearch }
diff --git a/gitbook/layouts/NotFoundLayout.jsx b/gitbook/layouts/NotFoundLayout.jsx
deleted file mode 100644
index 613e499..0000000
--- a/gitbook/layouts/NotFoundLayout.jsx
+++ /dev/null
@@ -1,43 +0,0 @@
-'use client'
-
-import { useGlobal } from '@/lib/global'
-import { isBrowser } from '@/lib/utils'
-import { useEffect } from 'react'
-import { useRouter } from 'next/router'
-
-/**
- * 404 頁面
- */
-const Layout404 = () => {
- const router = useRouter()
- const { locale } = useGlobal()
- useEffect(() => {
- // 延遲 3 秒若載入失敗就返回首頁
- setTimeout(() => {
- const article = isBrowser && document.getElementById('article-wrapper')
- if (!article) {
- router.push('/').then(() => {
- // console.log('找不到頁面', router.asPath)
- })
- }
- }, 3000)
- }, [router])
-
- return (
- <>
-
-
-
-
- 404
-
-
-
{locale.NAV.PAGE_NOT_FOUND_REDIRECT}
-
-
-
- >
- )
-}
-
-export { Layout404 }
diff --git a/gitbook/layouts/SlugLayout.jsx b/gitbook/layouts/SlugLayout.jsx
deleted file mode 100644
index e4aab37..0000000
--- a/gitbook/layouts/SlugLayout.jsx
+++ /dev/null
@@ -1,111 +0,0 @@
-'use client'
-
-import Comment from '@/components/Comment'
-import NotionIcon from '@/components/NotionIcon'
-import NotionPage from '@/components/NotionPage'
-import ShareBar from '@/components/ShareBar'
-import { siteConfig } from '@/lib/config'
-import { isBrowser } from '@/lib/utils'
-import Head from 'next/head'
-import { useEffect } from 'react'
-import { useRouter } from 'next/router'
-import CategoryItem from '../components/CategoryItem'
-import { ArticleLock } from '../components/ArticleLock'
-import ArticleAround from '../components/ArticleAround'
-import CatalogDrawerWrapper from '../components/CatalogDrawerWrapper'
-import TagItemMini from '../components/TagItemMini'
-import CONFIG from '../config'
-
-/**
- * 文章詳情頁
- */
-const LayoutSlug = props => {
- const { post, prev, next, siteInfo, lock, validPassword } = props
- const router = useRouter()
- // 若為文件首頁文章則更新瀏覽器標題
- const index = siteConfig('GITBOOK_INDEX_PAGE', 'about', CONFIG)
- const basePath = router.asPath.split('?')[0]
- const title =
- basePath?.indexOf(index) > 0
- ? `${post?.title} | ${siteInfo?.description}`
- : `${post?.title} | ${siteInfo?.title}`
-
- const waiting404 = siteConfig('POST_WAITING_TIME_FOR_404') * 1000
- useEffect(() => {
- // 404 處理
- if (!post) {
- setTimeout(() => {
- if (isBrowser) {
- const article = document.querySelector(
- '#article-wrapper #notion-article'
- )
- if (!article) {
- router.push('/404').then(() => {
- console.warn('找不到頁面', router.asPath)
- })
- }
- }
- }, waiting404)
- }
- }, [post, router, waiting404])
- return (
- <>
-
- {title}
-
-
- {/* 文章加鎖 */}
- {lock && }
-
- {!lock && (
-
- {/* 標題 */}
-
- {siteConfig('POST_TITLE_ICON') && (
-
- )}
- {post?.title}
-
-
- {/* Notion 文章主體 */}
- {post && (
-
-
-
-
-
- {/* 分享 */}
-
- {/* 文章分類與標籤資訊 */}
-
- {siteConfig('POST_DETAIL_CATEGORY') && post?.category && (
-
- )}
-
- {siteConfig('POST_DETAIL_TAG') &&
- post?.tagItems?.map(tag => (
-
- ))}
-
-
-
- {post?.type === 'Post' && (
-
- )}
-
- {/*
- */}
-
-
-
- )}
-
- {/* 文章目錄 */}
-
-
- )}
- >
- )
-}
-
-export { LayoutSlug }
diff --git a/gitbook/layouts/TaxonomyLayouts.jsx b/gitbook/layouts/TaxonomyLayouts.jsx
deleted file mode 100644
index 92d6125..0000000
--- a/gitbook/layouts/TaxonomyLayouts.jsx
+++ /dev/null
@@ -1,72 +0,0 @@
-'use client'
-
-import SmartLink from '@/components/SmartLink'
-import { useGlobal } from '@/lib/global'
-import TagItemMini from '../components/TagItemMini'
-
-/**
- * 分類列表
- */
-const LayoutCategoryIndex = props => {
- const { categoryOptions } = props
- const { locale } = useGlobal()
- return (
- <>
-
-
-
- {locale.COMMON.CATEGORY}:
-
-
- {categoryOptions?.map(category => {
- return (
-
-
-
- {category.name}({category.count})
-
-
- )
- })}
-
-
- >
- )
-}
-
-/**
- * 標籤列表
- */
-const LayoutTagIndex = props => {
- const { tagOptions } = props
- const { locale } = useGlobal()
-
- return (
- <>
-
-
-
- {locale.COMMON.TAGS}:
-
-
-
- >
- )
-}
-
-export { LayoutCategoryIndex, LayoutTagIndex }
diff --git a/gitbook/style.js b/gitbook/style.js
deleted file mode 100644
index 96b26b3..0000000
--- a/gitbook/style.js
+++ /dev/null
@@ -1,22 +0,0 @@
-/* eslint-disable react/no-unknown-property */
-/**
- * 此處樣式僅對當前主題生效
- * 此處不支援 tailwindCSS 的 @apply 語法
- * @returns
- */
-const Style = () => {
- return (
-
- )
-}
-
-export { Style }
diff --git a/mhhung/README.md b/mhhung/README.md
deleted file mode 100644
index 9e43796..0000000
--- a/mhhung/README.md
+++ /dev/null
@@ -1,70 +0,0 @@
-# Typography 主題架構說明
-
-本專案是部落格佈景主題(Typography),主要透過 `index.js` 匯出多種佈局組件,方便在 Next.js / Notion-Blog 專案中依照情境載入。下方整理目前的檔案架構與各模組職責,以及常見的使用方式。
-
-## 專案結構
-
-```
-.
-├── components/ # 主題客製化的視覺組件(TopBar、Footer、BlogPostBar…)
-├── config.js # 主題對外設定(標題、選單顯示開關…)
-├── index.js # 封裝後的佈局出口,對外統一匯出
-├── layouts/ # 各種頁面佈局拆分檔案
-│ ├── ArchiveLayout.jsx # 文章歸檔頁
-│ ├── BaseLayout.jsx # 共同外殼(搜尋、導覽、跳頂按鈕…)
-│ ├── ListLayouts.jsx # 首頁、文章列表、搜尋列表
-│ ├── NotFoundLayout.jsx # 404 頁面
-│ ├── SlugLayout.jsx # 單篇文章頁
-│ └── TaxonomyLayouts.jsx # 分類與標籤頁
-├── style.js # 只針對此主題作用的全域樣式
-└── utils/
- └── groupArticlesByYear.js # 文章依年份分組的小工具
-```
-
-## 主要佈局與職責
-
-- `LayoutBase`:最外層骨架,負責載入樣式、導覽列、頁尾、搜尋與 Loading 狀態,其他頁面內容會以 children 注入。
-- `LayoutIndex` / `LayoutPostList`:顯示文章列表或首頁,會掛上 `BlogPostBar` 與 `BlogListPage`。
-- `LayoutSearch`:在文章列表基礎上,透過 `replaceSearchResult` 將關鍵字高亮。
-- `LayoutArchive`:使用 `groupArticlesByYearArray` 將文章依年份分組後渲染。
-- `LayoutSlug`:單篇文章頁面,負責文章資訊、廣告、前後文推薦與留言區。
-- `LayoutCategoryIndex` / `LayoutTagIndex`:顯示分類與標籤的索引列表。
-- `Layout404`:等待 Notion 內容載入後做導向,或直接顯示 404。
-
-## 使用說明
-
-1. **在 Next.js 主題系統中載入**
- ```jsx
- import { LayoutBase, LayoutIndex } from 'mhhung'
-
- const Home = props => (
-
-
-
- )
-
- export default Home
- ```
- - `LayoutBase` 會處理共同外殼;`LayoutIndex` 則是此頁的實際內容。
- - 其他頁面可依需求替換為 `LayoutArchive`、`LayoutSlug`…等對應佈局。
-
-2. **設定主題參數**
- - 編輯 `config.js`,可調整部落格名稱、是否展示分類/標籤/歸檔選單,以及文章推薦、封面等開關。
- - 也可以透過環境變數(如 `NEXT_PUBLIC_TYPOGRAPHY_BLOG_NAME`)覆寫預設值,方便在不同部署環境中使用。
-
-3. **擴充樣式或功能**
- - 共用樣式可以在 `style.js` 中調整或新增。
- - 若要新增新的頁面佈局,建議在 `layouts/` 下建立新的 JSX 檔案,並於 `index.js` 匯出,以維持統一入口。
- - 共用的邏輯或工具函式,可集中在 `utils/` 目錄內,減少重複程式碼。
-
-4. **整合既有元件**
- - 所有 `layouts/` 中引用的元件皆放於 `components/`。若要替換視覺元素,如導覽列或頁尾,直接在對應元件修改即可。
- - 動態載入 (`next/dynamic`) 會避免在 SSR 階段載入僅限瀏覽器的套件,若新增依賴,請依照既有範例設定 `ssr: false`。
-
-## 開發建議
-
-- 調整佈局時,先確認是否為共用邏輯(放在 `BaseLayout` 或工具函式),再決定是否要拆分。
-- 新增功能建議寫在對應的 `layouts/` 或 `components/` 中,並保持檔案職責單一,讓後續維護更容易。
-- 若有多語系或 Theme 變化需求,可以在 `config.js` 中增加新的設定值,並於各佈局透過 `siteConfig` 取得。
-
-有任何額外需求都可以在 README 中持續補充,讓後續維護人員快速上手。
diff --git a/mhhung/components/ArticleAround.js b/mhhung/components/ArticleAround.js
index c3d2653..1c5eb96 100644
--- a/mhhung/components/ArticleAround.js
+++ b/mhhung/components/ArticleAround.js
@@ -1,7 +1,7 @@
import SmartLink from '@/components/SmartLink'
/**
- * 上一則與下一則文章
+ * 上一篇、下一篇文章
* @param {prev,next} param0
* @returns
*/
diff --git a/mhhung/components/ArticleInfo.js b/mhhung/components/ArticleInfo.js
index 8175aa0..ade74da 100644
--- a/mhhung/components/ArticleInfo.js
+++ b/mhhung/components/ArticleInfo.js
@@ -6,7 +6,7 @@ import { formatDateFmt } from '@/lib/utils/formatDate'
import NotionIcon from '@/components/NotionIcon'
/**
- * 文章說明
+ * 文章描述
* @param {*} props
* @returns
*/
@@ -27,7 +27,7 @@ export default function ArticleInfo(props) {
- 發佈於
+ 發布於
diff --git a/mhhung/components/ArticleLock.js b/mhhung/components/ArticleLock.js
index 3a47720..1b8a1de 100644
--- a/mhhung/components/ArticleLock.js
+++ b/mhhung/components/ArticleLock.js
@@ -5,7 +5,7 @@ import { useEffect, useRef } from 'react'
* 加密文章驗證元件
* @param {password, validPassword} props
* @param password 正確的密碼
- * @param validPassword(bool) 回呼函式,驗證正確回呼參數為 true
+ * @param validPassword(bool) 回呼函式,驗證成功時回傳 true
* @returns
*/
export default function ArticleLock (props) {
@@ -24,7 +24,7 @@ export default function ArticleLock (props) {
}
const passwordInputRef = useRef(null)
useEffect(() => {
- // 選中密碼輸入框並將其聚焦
+ // 選取密碼輸入框並將它聚焦
passwordInputRef.current.focus()
}, [])
@@ -38,7 +38,7 @@ export default function ArticleLock (props) {
submitPassword()
}
}}
- ref={passwordInputRef} // 綁定 ref 到 passwordInputRef 變數
+ ref={passwordInputRef} // 將 ref 綁定到 passwordInputRef 變數
className='outline-none w-full text-sm pl-5 rounded-l transition focus:shadow-lg font-light leading-10 text-black dark:bg-gray-500 bg-gray-50'
>
diff --git a/mhhung/components/BlogArchiveItem.js b/mhhung/components/BlogArchiveItem.js
index b3d547d..9a4f046 100644
--- a/mhhung/components/BlogArchiveItem.js
+++ b/mhhung/components/BlogArchiveItem.js
@@ -1,7 +1,7 @@
import SmartLink from '@/components/SmartLink'
/**
- * 歸檔文章清單
+ * 歸檔分組文章
* @param {*} param0
* @returns
*/
diff --git a/mhhung/components/BlogItem.js b/mhhung/components/BlogItem.js
index b51a4ff..fa01f97 100644
--- a/mhhung/components/BlogItem.js
+++ b/mhhung/components/BlogItem.js
@@ -49,7 +49,7 @@ export const BlogItem = props => {
- 發佈於
+ 發布於
diff --git a/mhhung/components/BlogListPage.js b/mhhung/components/BlogListPage.js
index ecb92e4..6c095a1 100644
--- a/mhhung/components/BlogListPage.js
+++ b/mhhung/components/BlogListPage.js
@@ -7,7 +7,7 @@ import CONFIG from '../config'
import { BlogItem } from './BlogItem'
/**
- * 部落格文章列表
+ * 部落格列表
* @param {*} props
* @returns
*/
@@ -19,7 +19,7 @@ export default function BlogListPage(props) {
const totalPage = Math.ceil(postCount / POSTS_PER_PAGE)
const currentPage = +page
- // 部落格列表插入廣告
+ // 部落格列表嵌入廣告
const TYPOGRAPHY_POST_AD_ENABLE = siteConfig(
'TYPOGRAPHY_POST_AD_ENABLE',
false,
diff --git a/mhhung/components/BlogListScroll.js b/mhhung/components/BlogListScroll.js
index 710e68b..1cd232d 100644
--- a/mhhung/components/BlogListScroll.js
+++ b/mhhung/components/BlogListScroll.js
@@ -5,7 +5,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'
import { BlogItem } from './BlogItem'
/**
- * 捲動部落格列表
+ * 滾動部落格列表
* @param {*} props
* @returns
*/
diff --git a/mhhung/components/BlogPostBar.js b/mhhung/components/BlogPostBar.js
index 53ce417..7ac7b91 100644
--- a/mhhung/components/BlogPostBar.js
+++ b/mhhung/components/BlogPostBar.js
@@ -1,7 +1,7 @@
import { useGlobal } from '@/lib/global'
/**
- * 文章列表上方區塊
+ * 文章列表上方嵌入區塊
* @param {*} props
* @returns
*/
diff --git a/mhhung/components/Catalog.js b/mhhung/components/Catalog.js
index 5149bc4..dd389a5 100644
--- a/mhhung/components/Catalog.js
+++ b/mhhung/components/Catalog.js
@@ -22,8 +22,8 @@ const Catalog = ({ post }) => {
if (!post || !post?.toc || post?.toc?.length < 1) {
return
}
-
- const throttleMs = 100 // 降低節流時間提高響應速度
+
+ const throttleMs = 100 // 降低節流時間提升反應速度
const actionSectionScrollSpy = throttle(() => {
const sections = document.getElementsByClassName('notion-h')
@@ -32,15 +32,15 @@ const Catalog = ({ post }) => {
let prevBBox = null
let currentSectionId = null
- // 先檢查當前視窗中的所有標題
+ // 先檢查目前視窗中的所有標題
for (let i = 0; i < sections.length; ++i) {
const section = sections[i]
if (!section || !(section instanceof Element)) continue
-
+
const bbox = section.getBoundingClientRect()
- const offset = 100 // 固定位移量,避免計算不穩定
-
- // 如果標題在視窗上方或接近頂部,認為是當前標題
+ const offset = 100 // 固定偏移量,避免計算不穩定
+
+ // 如果標題在視窗上方或接近頂部,視為目前標題
if (bbox.top - offset < 0) {
currentSectionId = section.getAttribute('data-id')
prevBBox = bbox
@@ -49,16 +49,16 @@ const Catalog = ({ post }) => {
break
}
}
-
- // 如果沒找到任何標題在視窗上方,使用第一個標題
+
+ // 若沒有找到任何標題位於視窗上方,就使用第一個標題
if (!currentSectionId && sections.length > 0) {
currentSectionId = sections[0].getAttribute('data-id')
}
-
+
// 只有當 ID 變化時才更新狀態,減少不必要的渲染
if (currentSectionId !== activeSection) {
setActiveSection(currentSectionId)
-
+
// 查找目錄中對應的索引並滾動
const index = post?.toc?.findIndex(
obj => uuidToId(obj.id) === currentSectionId
@@ -71,22 +71,22 @@ const Catalog = ({ post }) => {
}, throttleMs)
const content = document.querySelector('#container-inner')
- if (!content) return // 防止 content 不存在
-
- // 添加滾動和內容變化的監聽
+ if (!content) return // 避免 content 不存在
+
+ // 加入滾動與內容變化的監聽
content.addEventListener('scroll', actionSectionScrollSpy)
-
+
// 初始執行一次
setTimeout(() => {
actionSectionScrollSpy()
- }, 300) // 延遲執行確保 DOM 已完全載入
+ }, 300) // 延遲執行以確保 DOM 已完全載入
return () => {
content?.removeEventListener('scroll', actionSectionScrollSpy)
}
}, [post])
- // 無目錄就直接返回空
+ // 沒有目錄就直接回傳空值
if (!post || !post?.toc || post?.toc?.length < 1) {
return <>>
}
diff --git a/mhhung/components/Footer.js b/mhhung/components/Footer.js
index ee404e6..c3836c9 100644
--- a/mhhung/components/Footer.js
+++ b/mhhung/components/Footer.js
@@ -3,7 +3,7 @@ import DarkModeButton from '@/components/DarkModeButton'
import { siteConfig } from '@/lib/config'
/**
- * 頁腳
+ * 頁尾
* @param {*} props
* @returns
*/
diff --git a/mhhung/components/JumpToTopButton.js b/mhhung/components/JumpToTopButton.js
index 00ec74b..bcefca5 100644
--- a/mhhung/components/JumpToTopButton.js
+++ b/mhhung/components/JumpToTopButton.js
@@ -3,8 +3,8 @@ import { useEffect, useState } from 'react'
/**
* 跳轉到網頁頂部
- * 當畫面下滑 500 像素後會出現此按鈕
- * @param targetRef 連結高度的目標 html 元素
+ * 當螢幕下滑 500 像素後會出現該控制項
+ * @param targetRef 關聯高度的目標 HTML 標籤
* @param showPercent 是否顯示百分比
* @returns {JSX.Element}
* @constructor
diff --git a/mhhung/components/MenuItemCollapse.js b/mhhung/components/MenuItemCollapse.js
index de470a0..a65cc6f 100644
--- a/mhhung/components/MenuItemCollapse.js
+++ b/mhhung/components/MenuItemCollapse.js
@@ -3,7 +3,7 @@ import SmartLink from '@/components/SmartLink'
import { useState } from 'react'
/**
- * 收合選單
+ * 摺疊選單
* @param {*} param0
* @returns
*/
@@ -64,7 +64,7 @@ export const MenuItemCollapse = props => {
)}
- {/* 收合子選單 */}
+ {/* 摺疊子選單 */}
{hasSubMenu && (
{link.subMenus.map((sLink, index) => {
diff --git a/mhhung/components/MenuItemDrop.js b/mhhung/components/MenuItemDrop.js
index 6aaca96..fa718f1 100644
--- a/mhhung/components/MenuItemDrop.js
+++ b/mhhung/components/MenuItemDrop.js
@@ -49,7 +49,7 @@ export const MenuItemDrop = ({ link }) => {
)}
- {/* 子選單 */}
+ {/* 子菜單 */}
{link?.subMenus?.map((sLink, index) => {
diff --git a/mhhung/components/MenuList.js b/mhhung/components/MenuList.js
index 0b04791..58c6aaa 100644
--- a/mhhung/components/MenuList.js
+++ b/mhhung/components/MenuList.js
@@ -53,7 +53,7 @@ export const MenuList = ({ customNav, customMenu }) => {
links = links.concat(customNav)
}
- // 如果 開啟自訂選單,則覆蓋 Page 生成的選單
+ // 若啟用自訂選單,就覆寫 Page 產生的選單
if (siteConfig('CUSTOM_MENU')) {
links = customMenu
}
@@ -70,7 +70,7 @@ export const MenuList = ({ customNav, customMenu }) => {
))}
- {/* 行動端小螢幕選單 - 水平排列 */}
+ {/* 行動版小螢幕選單 - 水平排列 */}