diff --git a/README.md b/README.md
index dbec7c25..92b2c1b6 100644
--- a/README.md
+++ b/README.md
@@ -50,12 +50,9 @@
|--|--|--|--|
|
[预览NEXT](https://preview.tangly1024.com/?theme=next) |
[预览MEDIUM](https://preview.tangly1024.com/?theme=medium) |
[预览HEXO](https://preview.tangly1024.com/?theme=hexo) |
[预览FUKASAWA](https://preview.tangly1024.com/?theme=fukasawa) |
+*只需修改`blog.config.js`文件的`THEME`即可实现主题切换。* 没找到喜欢的主题?[贡献](/CONTRIBUTING.md)一个吧~
-*只需修改`blog.config.js`文件的`THEME`即可实现主题切换。*
-
-## 更新日志
-请移步 [更新文档](https://docs.tangly1024.com/zh/changelog)查看
## 快速起步
@@ -80,11 +77,15 @@ yarn run start # 本地启动NextJS服务
## 引用技术
-- **框架**: Next.js
+- **框架**: [Next.js](https://nextjs.org)
- **样式**: [Tailwind CSS](https://www.tailwindcss.cn/) 和 `@tailwindcss/jit` compiler
- **渲染**: [React-notion-x](https://github.com/NotionX/react-notion-x)
-- **评论**: Gitalk, Cusdis, Utterances
-- **图标**:[fontawesome](https://fontawesome.com/v5.15/icons?d=gallery)
+- **评论**: [Giscus](https://giscus.app/zh-CN), [Gitalk](https://gitalk.github.io), [Cusdis](https://gitalk.github.io), [Utterances](https://utteranc.es)
+- **图标**:[fontawesome v5.15](https://fontawesome.com/v5.15/icons?d=gallery)
+
+## 更新日志
+请移步 [更新文档](https://docs.tangly1024.com/zh/changelog)查看
+
## 致谢
感谢Craig Hart发起的Nobelium项目
@@ -92,13 +93,16 @@ yarn run start # 本地启动NextJS服务
 Craig Hart |
-## Contributors
+## 贡献者
+十分期待你的[贡献](/CONTRIBUTING.md),一起来完善这个项目~
+
+
## License
The MIT License.
diff --git a/components/DebugPanel.js b/components/DebugPanel.js
index b1623e50..50add94a 100644
--- a/components/DebugPanel.js
+++ b/components/DebugPanel.js
@@ -2,7 +2,7 @@ import BLOG from '@/blog.config'
import * as ThemeMap from '@/themes'
import { useState } from 'react'
import Select from './Select'
-import { ALL_THEME } from '@/lib/theme'
+import { ALL_THEME } from '@/themes'
import { useGlobal } from '@/lib/global'
/**
*
diff --git a/lib/global.js b/lib/global.js
index 8ca8deed..e1692217 100644
--- a/lib/global.js
+++ b/lib/global.js
@@ -2,7 +2,8 @@ import { generateLocaleDict, initLocale } from './lang'
import { createContext, useContext, useEffect, useState } from 'react'
import Router from 'next/router'
import BLOG from '@/blog.config'
-import { ALL_THEME, initDarkMode, initTheme, saveThemeToCookies } from '@/lib/theme'
+import { initDarkMode, initTheme, saveThemeToCookies } from '@/lib/theme'
+import { ALL_THEME } from '@/themes'
const GlobalContext = createContext()
let hasInit = false
diff --git a/lib/theme.js b/lib/theme.js
index 55d47aad..1cecad78 100644
--- a/lib/theme.js
+++ b/lib/theme.js
@@ -1,7 +1,6 @@
import cookie from 'react-cookies'
import BLOG from '@/blog.config'
-export const ALL_THEME = ['hexo', 'next', 'medium', 'fukasawa', 'empty']
/**
* 初始化主题
* @param isDarkMode
diff --git a/themes/empty/Layout404.js b/themes/empty/Layout404.js
deleted file mode 100644
index cd28a607..00000000
--- a/themes/empty/Layout404.js
+++ /dev/null
@@ -1,6 +0,0 @@
-
-export const Layout404 = () => {
- return
- 404 Not found.
-
-}
diff --git a/themes/empty/LayoutArchive.js b/themes/empty/LayoutArchive.js
deleted file mode 100644
index 04a6d647..00000000
--- a/themes/empty/LayoutArchive.js
+++ /dev/null
@@ -1,69 +0,0 @@
-import BLOG from '@/blog.config'
-import { useGlobal } from '@/lib/global'
-import Link from 'next/link'
-import LayoutBase from './LayoutBase'
-
-export const LayoutArchive = props => {
- const { posts } = props
- const { locale } = useGlobal()
- const postsSortByDate = Object.create(posts)
-
- postsSortByDate.sort((a, b) => {
- const dateA = new Date(a?.date.start_date || a.createdTime)
- const dateB = new Date(b?.date.start_date || b.createdTime)
- return dateB - dateA
- })
-
- const meta = {
- title: `${locale.NAV.ARCHIVE} | ${BLOG.TITLE}`,
- description: BLOG.DESCRIPTION,
- type: 'website'
- }
-
- const archivePosts = {}
-
- postsSortByDate.forEach(post => {
- const date = post.date.start_date.slice(0, 7)
- if (archivePosts[date]) {
- archivePosts[date].push(post)
- } else {
- archivePosts[date] = [post]
- }
- })
- return (
-
-
- {Object.keys(archivePosts).map(archiveTitle => (
-
-
- {archiveTitle}
-
-
- {archivePosts[archiveTitle].map(post => (
- -
-
-
- {post.date.start_date}
- {' '}
-
-
-
- {post.title}
-
-
-
-
- ))}
-
-
- ))}
-
-
- )
-}
diff --git a/themes/empty/LayoutBase.js b/themes/empty/LayoutBase.js
deleted file mode 100644
index 7898c47d..00000000
--- a/themes/empty/LayoutBase.js
+++ /dev/null
@@ -1,135 +0,0 @@
-import CommonHead from '@/components/CommonHead'
-import Live2D from '@/components/Live2D'
-import Link from 'next/link'
-import React from 'react'
-import BLOG from '@/blog.config'
-import { useGlobal } from '@/lib/global'
-/**
- * 基础布局 采用左右两侧布局,移动端使用顶部导航栏
-
- * @returns {JSX.Element}
- * @constructor
- */
-const LayoutBase = props => {
- const { children, meta, customNav } = props
- const { locale } = useGlobal()
- const d = new Date()
- const currentYear = d.getFullYear()
- const startYear = BLOG.SINCE && BLOG.SINCE !== currentYear && BLOG.SINCE + '-'
-
- let links = [
- { icon: 'fas fa-search', name: locale.NAV.SEARCH, to: '/search' },
- { icon: 'fas fa-archive', name: locale.NAV.ARCHIVE, to: '/archive' },
- { icon: 'fas fa-folder', name: locale.COMMON.CATEGORY, to: '/category' },
- { icon: 'fas fa-tag', name: locale.COMMON.TAGS, to: '/tag' }
- ]
-
- if (customNav) {
- links = links.concat(customNav)
- }
-
- return (
-
-
- {/* 导航菜单 */}
-
-
-
-
-
-
-
- {/* 内容主体 */}
-
- {children}
-
-
-
- {/* 页脚 */}
-
-
- )
-}
-
-export default LayoutBase
diff --git a/themes/empty/LayoutCategory.js b/themes/empty/LayoutCategory.js
deleted file mode 100644
index 85d399f6..00000000
--- a/themes/empty/LayoutCategory.js
+++ /dev/null
@@ -1,48 +0,0 @@
-import BLOG from '@/blog.config'
-import { useGlobal } from '@/lib/global'
-import Link from 'next/link'
-import { useState } from 'react'
-import LayoutBase from './LayoutBase'
-
-export const LayoutCategory = props => {
- const { category, posts } = props
- const { locale } = useGlobal()
-
- const [page, updatePage] = useState(1)
- let hasMore = false
- const postsToShow = posts
- ? Object.assign(posts).slice(0, BLOG.POSTS_PER_PAGE * page)
- : []
-
- if (posts) {
- const totalCount = posts.length
- hasMore = page * BLOG.POSTS_PER_PAGE < totalCount
- }
- const handleGetMore = () => {
- if (!hasMore) return
- updatePage(page + 1)
- }
-
- return (
-
- Category - {category}
- {postsToShow.map(p => (
-
- ))}
-
-
- {' '}
- {hasMore ? locale.COMMON.MORE : `${locale.COMMON.NO_MORE} 😰`}{' '}
-
-
-
- )
-}
diff --git a/themes/empty/LayoutCategoryIndex.js b/themes/empty/LayoutCategoryIndex.js
deleted file mode 100644
index b748b140..00000000
--- a/themes/empty/LayoutCategoryIndex.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import { useGlobal } from '@/lib/global'
-import Link from 'next/link'
-import LayoutBase from './LayoutBase'
-
-export const LayoutCategoryIndex = (props) => {
- const { categories } = props
- const { locale } = useGlobal()
- return
-
-
- {locale.COMMON.CATEGORY}:
-
-
- {categories && categories.map(category => {
- return
-
- {category.name}({category.count})
-
-
- })}
-
-
-
-}
diff --git a/themes/empty/LayoutIndex.js b/themes/empty/LayoutIndex.js
deleted file mode 100644
index 817bcdfb..00000000
--- a/themes/empty/LayoutIndex.js
+++ /dev/null
@@ -1,52 +0,0 @@
-import BLOG from '@/blog.config'
-import { useGlobal } from '@/lib/global'
-import Link from 'next/link'
-import { useRouter } from 'next/router'
-import LayoutBase from './LayoutBase'
-
-export const LayoutIndex = props => {
- const { posts, postCount } = props
-
- const { locale } = useGlobal()
- const router = useRouter()
- const totalPage = Math.ceil(postCount / BLOG.POSTS_PER_PAGE)
-
- const page = 1
- const showNext = page < totalPage && posts.length === BLOG.POSTS_PER_PAGE && posts.length < postCount
-
- const currentPage = +page
- return (
-
- {posts.map(p => (
-
- ))}
-
-
-
- )
-}
diff --git a/themes/empty/LayoutPage.js b/themes/empty/LayoutPage.js
deleted file mode 100644
index 2f5f795a..00000000
--- a/themes/empty/LayoutPage.js
+++ /dev/null
@@ -1,52 +0,0 @@
-import BLOG from '@/blog.config'
-import { useGlobal } from '@/lib/global'
-import Link from 'next/link'
-import { useRouter } from 'next/router'
-import LayoutBase from './LayoutBase'
-
-export const LayoutPage = (props) => {
- const { page } = props
- const { posts, postCount } = props
-
- const { locale } = useGlobal()
- const router = useRouter()
- const totalPage = Math.ceil(postCount / BLOG.POSTS_PER_PAGE)
-
- const showNext = page < totalPage && posts.length === BLOG.POSTS_PER_PAGE && posts.length < postCount
-
- const currentPage = +page
- return (
-
- {posts.map(p => (
-
- ))}
-
-
-
- )
-}
diff --git a/themes/empty/LayoutSearch.js b/themes/empty/LayoutSearch.js
deleted file mode 100644
index d24024a5..00000000
--- a/themes/empty/LayoutSearch.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import BLOG from '@/blog.config'
-import { useGlobal } from '@/lib/global'
-import Link from 'next/link'
-import { useEffect, useState } from 'react'
-import SearchInput from './components/SearchInput'
-import LayoutBase from './LayoutBase'
-
-export const LayoutSearch = props => {
- const { keyword, posts } = props
- useEffect(() => {
- setTimeout(() => {
- const container = document.getElementById('container')
- if (container && container.innerHTML) {
- const re = new RegExp(`${keyword}`, 'gim')
- container.innerHTML = container.innerHTML.replace(re, `${keyword}`)
- }
- }, 100)
- })
-
- const { locale } = useGlobal()
-
- const [page, updatePage] = useState(1)
- let hasMore = false
- const postsToShow = posts
- ? Object.assign(posts).slice(0, BLOG.POSTS_PER_PAGE * page)
- : []
-
- if (posts) {
- const totalCount = posts.length
- hasMore = page * BLOG.POSTS_PER_PAGE < totalCount
- }
- const handleGetMore = () => {
- if (!hasMore) return
- updatePage(page + 1)
- }
-
- return (
-
- Search - {keyword}
-
- {postsToShow.map(p => (
-
- ))}
-
-
- {' '}
- {hasMore ? locale.COMMON.MORE : `${locale.COMMON.NO_MORE} 😰`}{' '}
-
-
-
- )
-}
diff --git a/themes/empty/LayoutSlug.js b/themes/empty/LayoutSlug.js
deleted file mode 100644
index 7ff9b624..00000000
--- a/themes/empty/LayoutSlug.js
+++ /dev/null
@@ -1,100 +0,0 @@
-import BLOG from '@/blog.config'
-import { getPageTableOfContents } from 'notion-utils'
-import 'prismjs'
-import 'prismjs/components/prism-bash'
-import 'prismjs/components/prism-javascript'
-import 'prismjs/components/prism-markup'
-import 'prismjs/components/prism-python'
-import 'prismjs/components/prism-typescript'
-import {
- Code,
- Collection,
- CollectionRow,
- Equation,
- NotionRenderer
-} from 'react-notion-x'
-import LayoutBase from './LayoutBase'
-import { useRef, useEffect } from 'react'
-import { ArticleLock } from './components/ArticleLock'
-import mediumZoom from 'medium-zoom'
-
-const mapPageUrl = id => {
- return 'https://www.notion.so/' + id.replace(/-/g, '')
-}
-
-export const LayoutSlug = props => {
- const { post, lock, validPassword } = props
- const meta = {
- title: `${post.title} | ${BLOG.TITLE}`,
- description: post.summary,
- type: 'article',
- tags: post.tags
- }
-
- if (!lock && post?.blockMap?.block) {
- post.content = Object.keys(post.blockMap.block)
- post.toc = getPageTableOfContents(post, post.blockMap)
- }
-
- const zoom =
- typeof window !== 'undefined' &&
- mediumZoom({
- container: '.notion-viewport',
- background: 'rgba(0, 0, 0, 0.2)',
- margin: getMediumZoomMargin()
- })
- const zoomRef = useRef(zoom ? zoom.clone() : null)
- useEffect(() => {
- // 将所有container下的所有图片添加medium-zoom
- const container = document.getElementById('notion-article')
- const imgList = container?.getElementsByTagName('img')
- if (imgList && zoomRef.current) {
- for (let i = 0; i < imgList.length; i++) {
- zoomRef.current.attach(imgList[i])
- }
- }
- })
- return (
-
-
-
{post?.title}
-
- {lock &&
}
-
- {!lock &&
- {post.blockMap && (
-
- )}
- }
-
-
-
- )
-}
-
-function getMediumZoomMargin () {
- const width = window.innerWidth
-
- if (width < 500) {
- return 8
- } else if (width < 800) {
- return 20
- } else if (width < 1280) {
- return 30
- } else if (width < 1600) {
- return 40
- } else if (width < 1920) {
- return 48
- } else {
- return 72
- }
-}
diff --git a/themes/empty/LayoutTag.js b/themes/empty/LayoutTag.js
deleted file mode 100644
index e1a549f4..00000000
--- a/themes/empty/LayoutTag.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import BLOG from '@/blog.config'
-import { useGlobal } from '@/lib/global'
-import Link from 'next/link'
-import { useState } from 'react'
-import LayoutBase from './LayoutBase'
-
-export const LayoutTag = props => {
- const { tag, posts } = props
- const { locale } = useGlobal()
-
- const [page, updatePage] = useState(1)
-
- let hasMore = false
- const postsToShow = posts
- ? Object.assign(posts).slice(0, BLOG.POSTS_PER_PAGE * page)
- : []
-
- if (posts) {
- const totalCount = posts.length
- hasMore = page * BLOG.POSTS_PER_PAGE < totalCount
- }
- const handleGetMore = () => {
- if (!hasMore) return
- updatePage(page + 1)
- }
-
- return (
-
- Tag - {tag}
- {postsToShow.map(p => (
-
- ))}
-
-
- {' '}
- {hasMore ? locale.COMMON.MORE : `${locale.COMMON.NO_MORE} 😰`}{' '}
-
-
-
- )
-}
diff --git a/themes/empty/LayoutTagIndex.js b/themes/empty/LayoutTagIndex.js
deleted file mode 100644
index 3fb41c9f..00000000
--- a/themes/empty/LayoutTagIndex.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import { useGlobal } from '@/lib/global'
-import Link from 'next/link'
-import LayoutBase from './LayoutBase'
-
-export const LayoutTagIndex = (props) => {
- const { tags } = props
- const { locale } = useGlobal()
- return
-
-
{locale.COMMON.TAGS}:
-
-
-}
diff --git a/themes/empty/components/ArticleLock.js b/themes/empty/components/ArticleLock.js
deleted file mode 100644
index bfdd00ce..00000000
--- a/themes/empty/components/ArticleLock.js
+++ /dev/null
@@ -1,40 +0,0 @@
-import { useGlobal } from '@/lib/global'
-
-/**
- * 加密文章校验组件
- * @param {password, validPassword} props
- * @param password 正确的密码
- * @param validPassword(bool) 回调函数,校验正确回调入参为true
- * @returns
- */
-export const ArticleLock = props => {
- const { password, validPassword } = props
- const { locale } = useGlobal()
-
- const submitPassword = () => {
- const p = document.getElementById('password')
- if (p && p.value && p.value === password) {
- validPassword(true)
- } else {
- const tips = document.getElementById('tips')
- if (tips) {
- tips.innerHTML = ''
- tips.innerHTML = `${locale.COMMON.PASSWORD_ERROR}
`
- }
- }
- }
-
- return
-
-
{locale.COMMON.ARTICLE_LOCK_TIPS}
-
-
-
- {locale.COMMON.SUBMIT}
-
-
-
-
-
-
-}
diff --git a/themes/empty/components/SearchInput.js b/themes/empty/components/SearchInput.js
deleted file mode 100644
index 6a95ba01..00000000
--- a/themes/empty/components/SearchInput.js
+++ /dev/null
@@ -1,87 +0,0 @@
-import { useRouter } from 'next/router'
-import { useGlobal } from '@/lib/global'
-import { useImperativeHandle, useRef, useState } from 'react'
-
-let lock = false
-
-const SearchInput = ({ currentTag, currentSearch, cRef }) => {
- const { locale } = useGlobal()
- const router = useRouter()
- const searchInputRef = useRef(null)
- useImperativeHandle(cRef, () => {
- return {
- focus: () => {
- searchInputRef?.current?.focus()
- }
- }
- })
- const handleSearch = () => {
- const key = searchInputRef.current.value
- if (key && key !== '') {
- router.push({ pathname: '/search/' + key }).then(r => {
- console.log('搜索', key)
- })
- } else {
- router.push({ pathname: '/' }).then(r => {
- })
- }
- }
- const handleKeyUp = (e) => {
- if (e.keyCode === 13) { // 回车
- handleSearch(searchInputRef.current.value)
- } else if (e.keyCode === 27) { // ESC
- cleanSearch()
- }
- }
- const cleanSearch = () => {
- searchInputRef.current.value = ''
- setShowClean(false)
- }
- function lockSearchInput () {
- lock = true
- }
-
- function unLockSearchInput () {
- lock = false
- }
- const [showClean, setShowClean] = useState(false)
- const updateSearchKey = (val) => {
- if (lock) {
- return
- }
- searchInputRef.current.value = val
- if (val) {
- setShowClean(true)
- } else {
- setShowClean(false)
- }
- }
-
- return
-}
-
-export default SearchInput
diff --git a/themes/empty/config_empty.js b/themes/empty/config_empty.js
deleted file mode 100644
index 9f1ac567..00000000
--- a/themes/empty/config_empty.js
+++ /dev/null
@@ -1,4 +0,0 @@
-const CONFIG_EMPTY = {
- TEST_CONFIG: 'TESET'
-}
-export default CONFIG_EMPTY
diff --git a/themes/empty/index.js b/themes/empty/index.js
deleted file mode 100644
index 95b0a6b2..00000000
--- a/themes/empty/index.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import CONFIG_EMPTY from './config_empty'
-import { LayoutIndex } from './LayoutIndex'
-import { LayoutSearch } from './LayoutSearch'
-import { LayoutArchive } from './LayoutArchive'
-import { LayoutSlug } from './LayoutSlug'
-import { Layout404 } from './Layout404'
-import { LayoutCategory } from './LayoutCategory'
-import { LayoutCategoryIndex } from './LayoutCategoryIndex'
-import { LayoutPage } from './LayoutPage'
-import { LayoutTag } from './LayoutTag'
-import { LayoutTagIndex } from './LayoutTagIndex'
-
-export {
- CONFIG_EMPTY as THEME_CONFIG,
- LayoutIndex,
- LayoutSearch,
- LayoutArchive,
- LayoutSlug,
- Layout404,
- LayoutCategory,
- LayoutCategoryIndex,
- LayoutPage,
- LayoutTag,
- LayoutTagIndex
-}
diff --git a/themes/index.js b/themes/index.js
index 286446ca..e13cd4b3 100644
--- a/themes/index.js
+++ b/themes/index.js
@@ -5,11 +5,7 @@ import * as next from './next'
import * as fukasawa from './fukasawa'
import * as hexo from './hexo'
import * as medium from './medium'
-import * as empty from './empty'
-export {
- next,
- fukasawa,
- hexo,
- medium,
- empty
-}
+import * as example from './example'
+
+export const ALL_THEME = ['hexo', 'next', 'medium', 'fukasawa', 'example']
+export { hexo, next, medium, fukasawa, example }