nnbb 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +1 -0
- package/.env.local +2 -0
- package/.eslintrc.json +15 -0
- package/.github/stale.yml +16 -0
- package/.github/workflows/codeql-analysis.yml +73 -0
- package/.github/workflows/docker-ghcr.yaml +59 -0
- package/.prettierrc +9 -0
- package/.vscode/launch.json +28 -0
- package/.vscode/settings.json +6 -0
- package/Dockerfile +38 -0
- package/LICENSE +21 -0
- package/README.md +16 -0
- package/blog.config.js +454 -0
- package/components/Ackee.js +83 -0
- package/components/AdBlockDetect.js +40 -0
- package/components/AlgoliaSearchModal.js +250 -0
- package/components/Artalk.js +30 -0
- package/components/Busuanzi.js +26 -0
- package/components/ChatBase.js +19 -0
- package/components/Collapse.tsx +134 -0
- package/components/Comment.js +161 -0
- package/components/CommonHead.tsx +101 -0
- package/components/CommonScript.js +125 -0
- package/components/CusdisComponent.js +35 -0
- package/components/CustomContextMenu.js +221 -0
- package/components/DebugPanel.js +134 -0
- package/components/DisableCopy.js +21 -0
- package/components/Draggable.js +167 -0
- package/components/Equation.js +31 -0
- package/components/ExternalPlugins.js +75 -0
- package/components/ExternalScript.js +29 -0
- package/components/FacebookMessenger.js +255 -0
- package/components/FacebookPage.js +34 -0
- package/components/Fireworks.js +210 -0
- package/components/FlipCard.js +56 -0
- package/components/FlutteringRibbon.js +322 -0
- package/components/FullScreenButton.js +48 -0
- package/components/Giscus.js +33 -0
- package/components/Gitalk.js +42 -0
- package/components/GoogleAdsense.js +111 -0
- package/components/Gtag.js +18 -0
- package/components/HeroIcons.tsx +321 -0
- package/components/KatexReact.js +53 -0
- package/components/LazyImage.js +95 -0
- package/components/Live2D.js +52 -0
- package/components/Loading.js +20 -0
- package/components/Mark.js +28 -0
- package/components/NProgress.ts +8 -0
- package/components/Nest.js +124 -0
- package/components/NotionIcon.js +20 -0
- package/components/NotionPage.tsx +206 -0
- package/components/Player.js +54 -0
- package/components/PrismMac.js +236 -0
- package/components/QrCode.js +34 -0
- package/components/Ribbon.js +98 -0
- package/components/Sakura.js +192 -0
- package/components/Select.js +40 -0
- package/components/ShareBar.js +29 -0
- package/components/ShareButtons.js +403 -0
- package/components/SideBarDrawer.js +50 -0
- package/components/StarrySky.js +130 -0
- package/components/Tabs.js +64 -0
- package/components/ThemeSwitch.js +67 -0
- package/components/Twikoo.js +27 -0
- package/components/TwikooCommentCount.js +22 -0
- package/components/TwikooCommentCounter.js +78 -0
- package/components/TwikooRecentComments.js +11 -0
- package/components/Utterances.js +35 -0
- package/components/VConsole.js +76 -0
- package/components/ValineComponent.js +59 -0
- package/components/ValineCount.js +6 -0
- package/components/ValinePanel.js +3 -0
- package/components/Vercel.tsx +54 -0
- package/components/WWAds.js +18 -0
- package/components/WalineComponent.js +83 -0
- package/components/WebMention.js +173 -0
- package/components/Webwhiz.js +17 -0
- package/components/WordCount.js +73 -0
- package/hooks/useToggleClickOutSide.ts +32 -0
- package/hooks/useWindowSize.ts +30 -0
- package/lib/algolia.js +108 -0
- package/lib/busuanzi.js +99 -0
- package/lib/cache/cacheManager.ts +49 -0
- package/lib/cache/localFileCache.ts +56 -0
- package/lib/cache/memoryMache.ts +20 -0
- package/lib/cache/mongoDbCache.ts +70 -0
- package/lib/cache/types.ts +5 -0
- package/lib/font.js +46 -0
- package/lib/global.tsx +129 -0
- package/lib/gtag.js +17 -0
- package/lib/mailchimp.js +49 -0
- package/lib/memorize.js +0 -0
- package/lib/mhchem.js +1696 -0
- package/lib/notion/getAllCategories.ts +51 -0
- package/lib/notion/getAllPageIds.ts +51 -0
- package/lib/notion/getAllPosts.js +68 -0
- package/lib/notion/getAllTags.ts +43 -0
- package/lib/notion/getNotionData.ts +340 -0
- package/lib/notion/getPageInfoOfPostPage.ts +58 -0
- package/lib/notion/getPageProperties.ts +203 -0
- package/lib/notion/getPageTableOfContents.ts +107 -0
- package/lib/notion/getPostBlocks.ts +147 -0
- package/lib/notion/mapImage.ts +130 -0
- package/lib/notion/types.ts +125 -0
- package/lib/notion.js +2 -0
- package/lib/robots.txt.js +25 -0
- package/lib/rss.js +63 -0
- package/lib/sitemap.xml.js +67 -0
- package/lib/utils.js +212 -0
- package/next-env.d.ts +5 -0
- package/next-i18next.config.js +7 -0
- package/next-sitemap.config.js +11 -0
- package/next.config.js +124 -0
- package/package.json +92 -0
- package/pages/404.tsx +40 -0
- package/pages/[prefix]/[slug].tsx +123 -0
- package/pages/[prefix]/index.tsx +223 -0
- package/pages/_app.js +59 -0
- package/pages/_document.js +42 -0
- package/pages/api/subscribe.js +22 -0
- package/pages/archive/index.tsx +79 -0
- package/pages/category/[category]/index.tsx +87 -0
- package/pages/category/[category]/page/[page].tsx +103 -0
- package/pages/category/index.tsx +43 -0
- package/pages/index.tsx +88 -0
- package/pages/page/[page].tsx +93 -0
- package/pages/search/[keyword]/index.tsx +162 -0
- package/pages/search/[keyword]/page/[page].tsx +166 -0
- package/pages/search/index.tsx +69 -0
- package/pages/sitemap.xml.js +70 -0
- package/pages/tag/[tag]/index.tsx +73 -0
- package/pages/tag/[tag]/page/[page].tsx +87 -0
- package/pages/tag/index.tsx +42 -0
- package/postcss.config.js +6 -0
- package/public/ads.txt +1 -0
- package/public/avatar.png +0 -0
- package/public/avatar.svg +11 -0
- package/public/bg_image.jpg +0 -0
- package/public/css/all.min.css +9 -0
- package/public/css/custom.css +8 -0
- package/public/css/img-shadow.css +5 -0
- package/public/css/prism-mac-style.css +58 -0
- package/public/favicon.ico +0 -0
- package/public/favicon.svg +9 -0
- package/public/js/cusdis.es.js +107 -0
- package/public/js/custom.js +1 -0
- package/public/locales/en/common.json +44 -0
- package/public/locales/en/menu.json +9 -0
- package/public/locales/en/nav.json +11 -0
- package/public/locales/zh-CN/common.json +44 -0
- package/public/locales/zh-CN/menu.json +9 -0
- package/public/locales/zh-CN/nav.json +9 -0
- package/public/webfonts/fa-brands-400.ttf +0 -0
- package/public/webfonts/fa-brands-400.woff2 +0 -0
- package/public/webfonts/fa-regular-400.ttf +0 -0
- package/public/webfonts/fa-regular-400.woff2 +0 -0
- package/public/webfonts/fa-solid-900.ttf +0 -0
- package/public/webfonts/fa-solid-900.woff2 +0 -0
- package/public/webfonts/fa-v4compatibility.ttf +0 -0
- package/public/webfonts/fa-v4compatibility.woff2 +0 -0
- package/styles/animate.css +503 -0
- package/styles/globals.css +183 -0
- package/styles/notion.css +2064 -0
- package/styles/nprogress.css +84 -0
- package/styles/prism-theme.css +119 -0
- package/styles/utility-patterns.css +79 -0
- package/tailwind.config.js +37 -0
- package/theme/index.ts +6 -0
- package/theme/types/@theme-components.d.ts +29 -0
- package/theme/useLayout.ts +41 -0
- package/theme/utils.ts +108 -0
- package/themes/innocent/package.json +7 -0
- package/themes/innocent/theme.config.js +1 -0
- package/themes/nobelium/components/Announcement.tsx +27 -0
- package/themes/nobelium/components/ArticleFooter.tsx +39 -0
- package/themes/nobelium/components/ArticleInfo.tsx +58 -0
- package/themes/nobelium/components/ArticleLock.tsx +86 -0
- package/themes/nobelium/components/BlogArchiveItem.js +41 -0
- package/themes/nobelium/components/BlogListBar.js +39 -0
- package/themes/nobelium/components/BlogListPage.tsx +67 -0
- package/themes/nobelium/components/BlogListScroll.tsx +96 -0
- package/themes/nobelium/components/BlogPost.tsx +33 -0
- package/themes/nobelium/components/DarkModeButton.tsx +50 -0
- package/themes/nobelium/components/ExampleRecentComments.js +35 -0
- package/themes/nobelium/components/Footer.tsx +35 -0
- package/themes/nobelium/components/JumpToTopButton.tsx +39 -0
- package/themes/nobelium/components/LanguageSwitchButton.tsx +58 -0
- package/themes/nobelium/components/MenuItemCollapse.tsx +92 -0
- package/themes/nobelium/components/MenuItemDrop.tsx +83 -0
- package/themes/nobelium/components/Nav/Nav.module.css +50 -0
- package/themes/nobelium/components/Nav/Nav.tsx +187 -0
- package/themes/nobelium/components/RandomPostButton.tsx +31 -0
- package/themes/nobelium/components/SearchButton.tsx +31 -0
- package/themes/nobelium/components/SearchInput.tsx +94 -0
- package/themes/nobelium/components/SearchNavBar.js +19 -0
- package/themes/nobelium/components/SideBar.js +83 -0
- package/themes/nobelium/components/SvgIcon.js +29 -0
- package/themes/nobelium/components/TagItem.js +13 -0
- package/themes/nobelium/components/Tags.tsx +44 -0
- package/themes/nobelium/components/Title.js +19 -0
- package/themes/nobelium/index.tsx +28 -0
- package/themes/nobelium/layout/LayoutBase.tsx +79 -0
- package/themes/nobelium/pages/Archive.tsx +30 -0
- package/themes/nobelium/pages/Category.tsx +43 -0
- package/themes/nobelium/pages/Home.tsx +22 -0
- package/themes/nobelium/pages/PageNotFound.tsx +15 -0
- package/themes/nobelium/pages/Post.tsx +34 -0
- package/themes/nobelium/pages/PostList.tsx +74 -0
- package/themes/nobelium/pages/Search.tsx +65 -0
- package/themes/nobelium/pages/Tag.tsx +42 -0
- package/themes/nobelium/providers/index.tsx +60 -0
- package/themes/nobelium/stores/index.tsx +42 -0
- package/themes/nobelium/theme.config.ts +17 -0
- package/themes/nobelium/types/index.ts +10 -0
- package/tsconfig.json +29 -0
- package/types/index.ts +1 -0
- package/types/page.ts +102 -0
- package/vercel.json +5 -0
@@ -0,0 +1,84 @@
|
|
1
|
+
/* Make clicks pass-through */
|
2
|
+
#nprogress {
|
3
|
+
pointer-events: none;
|
4
|
+
}
|
5
|
+
|
6
|
+
#nprogress .bar {
|
7
|
+
background: #29d;
|
8
|
+
|
9
|
+
position: fixed;
|
10
|
+
z-index: 1031;
|
11
|
+
top: 0;
|
12
|
+
left: 0;
|
13
|
+
|
14
|
+
width: 100%;
|
15
|
+
height: 2px;
|
16
|
+
}
|
17
|
+
|
18
|
+
/* Fancy blur effect */
|
19
|
+
#nprogress .peg {
|
20
|
+
display: block;
|
21
|
+
position: absolute;
|
22
|
+
right: 0px;
|
23
|
+
width: 100px;
|
24
|
+
height: 100%;
|
25
|
+
box-shadow: 0 0 10px #29d, 0 0 5px #29d;
|
26
|
+
opacity: 1;
|
27
|
+
|
28
|
+
-webkit-transform: rotate(3deg) translate(0px, -4px);
|
29
|
+
-ms-transform: rotate(3deg) translate(0px, -4px);
|
30
|
+
transform: rotate(3deg) translate(0px, -4px);
|
31
|
+
}
|
32
|
+
|
33
|
+
/* Remove these to get rid of the spinner */
|
34
|
+
#nprogress .spinner {
|
35
|
+
display: block;
|
36
|
+
position: fixed;
|
37
|
+
z-index: 1031;
|
38
|
+
top: 15px;
|
39
|
+
right: 15px;
|
40
|
+
}
|
41
|
+
|
42
|
+
#nprogress .spinner-icon {
|
43
|
+
width: 18px;
|
44
|
+
height: 18px;
|
45
|
+
box-sizing: border-box;
|
46
|
+
|
47
|
+
border: solid 2px transparent;
|
48
|
+
border-top-color: #29d;
|
49
|
+
border-left-color: #29d;
|
50
|
+
border-radius: 50%;
|
51
|
+
|
52
|
+
-webkit-animation: nprogress-spinner 400ms linear infinite;
|
53
|
+
animation: nprogress-spinner 400ms linear infinite;
|
54
|
+
}
|
55
|
+
|
56
|
+
.nprogress-custom-parent {
|
57
|
+
overflow: hidden;
|
58
|
+
position: relative;
|
59
|
+
}
|
60
|
+
|
61
|
+
.nprogress-custom-parent #nprogress .spinner,
|
62
|
+
.nprogress-custom-parent #nprogress .bar {
|
63
|
+
position: absolute;
|
64
|
+
}
|
65
|
+
|
66
|
+
@-webkit-keyframes nprogress-spinner {
|
67
|
+
0% {
|
68
|
+
-webkit-transform: rotate(0deg);
|
69
|
+
}
|
70
|
+
|
71
|
+
100% {
|
72
|
+
-webkit-transform: rotate(360deg);
|
73
|
+
}
|
74
|
+
}
|
75
|
+
|
76
|
+
@keyframes nprogress-spinner {
|
77
|
+
0% {
|
78
|
+
transform: rotate(0deg);
|
79
|
+
}
|
80
|
+
|
81
|
+
100% {
|
82
|
+
transform: rotate(360deg);
|
83
|
+
}
|
84
|
+
}
|
@@ -0,0 +1,119 @@
|
|
1
|
+
/* prism theme adjustments */
|
2
|
+
|
3
|
+
.notion-code {
|
4
|
+
background-color: rgba(249, 250, 251, 1);
|
5
|
+
border: 1px solid rgba(229, 231, 235, 1);
|
6
|
+
border-radius: 0.375rem;
|
7
|
+
padding: 1.5em !important;
|
8
|
+
}
|
9
|
+
|
10
|
+
@media (min-width: 1300px) {
|
11
|
+
.notion-code {
|
12
|
+
max-width: var(--notion-max-width);
|
13
|
+
}
|
14
|
+
}
|
15
|
+
|
16
|
+
@media (max-width: 640px) {
|
17
|
+
.notion-code {
|
18
|
+
max-width: 100vw;
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
.dark-mode .notion-code {
|
23
|
+
background-color: rgba(17, 24, 39, 1);
|
24
|
+
border-color: rgba(55, 65, 81, 1);
|
25
|
+
}
|
26
|
+
.notion code {
|
27
|
+
color: rgba(31, 41, 55, 1);
|
28
|
+
border: 0 none !important;
|
29
|
+
box-shadow: none !important;
|
30
|
+
background: none !important;
|
31
|
+
padding: 0 !important;
|
32
|
+
}
|
33
|
+
.dark-mode .notion code {
|
34
|
+
color: rgba(229, 231, 235, 1);
|
35
|
+
}
|
36
|
+
.token.cdata,
|
37
|
+
.token.doctype,
|
38
|
+
.token.prolog {
|
39
|
+
color: rgba(55, 65, 81, 1);
|
40
|
+
}
|
41
|
+
.token.comment {
|
42
|
+
color: #5b9b4c;
|
43
|
+
}
|
44
|
+
.dark-mode .token.cdata,
|
45
|
+
.dark-mode .token.doctype,
|
46
|
+
.dark-mode .token.prolog {
|
47
|
+
color: rgba(209, 213, 219, 1);
|
48
|
+
}
|
49
|
+
.token.punctuation {
|
50
|
+
color: rgba(55, 65, 81, 1);
|
51
|
+
}
|
52
|
+
.dark-mode .token.punctuation {
|
53
|
+
color: rgba(209, 213, 219, 1);
|
54
|
+
}
|
55
|
+
.token.boolean,
|
56
|
+
.token.constant,
|
57
|
+
.token.deleted,
|
58
|
+
.token.number,
|
59
|
+
.token.property,
|
60
|
+
.token.symbol,
|
61
|
+
.token.tag {
|
62
|
+
color: rgba(16, 185, 129, 1);
|
63
|
+
}
|
64
|
+
.token.attr-name,
|
65
|
+
.token.builtin,
|
66
|
+
.token.char,
|
67
|
+
.token.inserted,
|
68
|
+
.token.selector,
|
69
|
+
.token.string {
|
70
|
+
color: rgba(139, 92, 246, 1);
|
71
|
+
}
|
72
|
+
.language-css .token.string,
|
73
|
+
.style .token.string,
|
74
|
+
.token.entity,
|
75
|
+
.token.operator,
|
76
|
+
.token.url {
|
77
|
+
color: rgba(245, 158, 11, 1);
|
78
|
+
}
|
79
|
+
.token.atrule,
|
80
|
+
.token.attr-value,
|
81
|
+
.token.keyword {
|
82
|
+
color: rgba(59, 130, 246, 1);
|
83
|
+
}
|
84
|
+
.token.class-name,
|
85
|
+
.token.function {
|
86
|
+
color: rgba(236, 72, 153, 1);
|
87
|
+
}
|
88
|
+
.token.important,
|
89
|
+
.token.regex,
|
90
|
+
.token.variable {
|
91
|
+
color: rgba(245, 158, 11, 1);
|
92
|
+
}
|
93
|
+
code[class*='language-'],
|
94
|
+
pre[class*='language-'] {
|
95
|
+
color: rgba(31, 41, 55, 1);
|
96
|
+
}
|
97
|
+
.dark-mode code[class*='language-'],
|
98
|
+
.dark-mode pre[class*='language-'] {
|
99
|
+
color: rgba(249, 250, 251, 1);
|
100
|
+
}
|
101
|
+
pre::-webkit-scrollbar {
|
102
|
+
display: none;
|
103
|
+
}
|
104
|
+
pre {
|
105
|
+
-ms-overflow-style: none;
|
106
|
+
scrollbar-width: none;
|
107
|
+
}
|
108
|
+
.token.operator,
|
109
|
+
.token.entity,
|
110
|
+
.token.url,
|
111
|
+
.token.variable {
|
112
|
+
background: none;
|
113
|
+
}
|
114
|
+
|
115
|
+
pre[class*='language-'] > code {
|
116
|
+
border-left: 0 none !important;
|
117
|
+
box-shadow: none !important;
|
118
|
+
background: none !important;
|
119
|
+
}
|
@@ -0,0 +1,79 @@
|
|
1
|
+
/* Typography */
|
2
|
+
.h1 {
|
3
|
+
@apply text-4xl font-extrabold leading-tight tracking-tighter;
|
4
|
+
}
|
5
|
+
|
6
|
+
.h2 {
|
7
|
+
@apply text-3xl font-extrabold leading-tight tracking-tighter;
|
8
|
+
}
|
9
|
+
|
10
|
+
.h3 {
|
11
|
+
@apply text-3xl font-bold leading-tight;
|
12
|
+
}
|
13
|
+
|
14
|
+
.h4 {
|
15
|
+
@apply text-2xl font-bold leading-snug tracking-tight;
|
16
|
+
}
|
17
|
+
|
18
|
+
@screen md {
|
19
|
+
.h1 {
|
20
|
+
@apply text-5xl;
|
21
|
+
}
|
22
|
+
|
23
|
+
.h2 {
|
24
|
+
@apply text-4xl;
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
/* Buttons */
|
29
|
+
.btn,
|
30
|
+
.btn-sm {
|
31
|
+
@apply font-medium inline-flex items-center justify-center border border-transparent rounded leading-snug transition duration-150 ease-in-out;
|
32
|
+
}
|
33
|
+
|
34
|
+
.btn {
|
35
|
+
@apply px-8 py-3 shadow-lg;
|
36
|
+
}
|
37
|
+
|
38
|
+
.btn-sm {
|
39
|
+
@apply px-4 py-2 shadow;
|
40
|
+
}
|
41
|
+
|
42
|
+
/* Forms */
|
43
|
+
.form-input,
|
44
|
+
.form-textarea,
|
45
|
+
.form-multiselect,
|
46
|
+
.form-select,
|
47
|
+
.form-checkbox,
|
48
|
+
.form-radio {
|
49
|
+
@apply bg-white border border-gray-300 focus:border-gray-500;
|
50
|
+
}
|
51
|
+
|
52
|
+
.form-input,
|
53
|
+
.form-textarea,
|
54
|
+
.form-multiselect,
|
55
|
+
.form-select,
|
56
|
+
.form-checkbox {
|
57
|
+
@apply rounded;
|
58
|
+
}
|
59
|
+
|
60
|
+
.form-input,
|
61
|
+
.form-textarea,
|
62
|
+
.form-multiselect,
|
63
|
+
.form-select {
|
64
|
+
@apply py-3 px-4;
|
65
|
+
}
|
66
|
+
|
67
|
+
.form-input,
|
68
|
+
.form-textarea {
|
69
|
+
@apply placeholder-gray-500;
|
70
|
+
}
|
71
|
+
|
72
|
+
.form-select {
|
73
|
+
@apply pr-10;
|
74
|
+
}
|
75
|
+
|
76
|
+
.form-checkbox,
|
77
|
+
.form-radio {
|
78
|
+
@apply text-gray-800 rounded-sm;
|
79
|
+
}
|
@@ -0,0 +1,37 @@
|
|
1
|
+
const BLOG = require('./blog.config');
|
2
|
+
const { fontFamilies } = require('./lib/font');
|
3
|
+
|
4
|
+
module.exports = {
|
5
|
+
content: [
|
6
|
+
'./pages/**/*.{js,jsx,ts,tsx}',
|
7
|
+
'./components/**/*.{js,jsx,ts,tsx}',
|
8
|
+
'./layouts/**/*.{js,jsx,ts,tsx}',
|
9
|
+
'./themes/**/*.{js,jsx,ts,tsx}',
|
10
|
+
],
|
11
|
+
darkMode: BLOG.APPEARANCE === 'class' ? 'media' : 'class', // or 'media' or 'class'
|
12
|
+
theme: {
|
13
|
+
fontFamily: fontFamilies,
|
14
|
+
extend: {
|
15
|
+
colors: {
|
16
|
+
day: {
|
17
|
+
DEFAULT: BLOG.BACKGROUND_LIGHT || '#ffffff',
|
18
|
+
},
|
19
|
+
night: {
|
20
|
+
DEFAULT: BLOG.BACKGROUND_DARK || '#111827',
|
21
|
+
},
|
22
|
+
hexo: {
|
23
|
+
'background-gray': '#f5f5f5',
|
24
|
+
'black-gray': '#101414',
|
25
|
+
'light-gray': '#e5e5e5',
|
26
|
+
},
|
27
|
+
},
|
28
|
+
maxWidth: {
|
29
|
+
side: '14rem',
|
30
|
+
},
|
31
|
+
},
|
32
|
+
},
|
33
|
+
variants: {
|
34
|
+
extend: {},
|
35
|
+
},
|
36
|
+
plugins: [],
|
37
|
+
};
|
package/theme/index.ts
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
declare module '@theme-components' {
|
2
|
+
export const Home: React.FC<import('@/pages/types').ThemeHomeProps>;
|
3
|
+
export const Page: React.FC<import('@/pages/types').ThemePageProps>;
|
4
|
+
export const Archive: React.FC<import('@/pages/types').ThemeArchiveProps>;
|
5
|
+
export const Category: React.FC<import('@/pages/types').ThemeCategoryProps>;
|
6
|
+
export const CategoryDetail: React.FC<
|
7
|
+
import('@/pages/types').ThemeCategoryDetailProps
|
8
|
+
>;
|
9
|
+
export const CategoryPage: React.FC<
|
10
|
+
import('@/pages/types').ThemeCategoryPageCoProps
|
11
|
+
>;
|
12
|
+
export const Tag: React.FC<import('@/pages/types').ThemeTagProps>;
|
13
|
+
export const TagDetail: React.FC<import('@/pages/types').ThemeTagDetailProps>;
|
14
|
+
export const TagPage: React.FC<import('@/pages/types').ThemeTagPageProps>;
|
15
|
+
export const Search: React.FC<import('@/pages/types').ThemeSearchProps>;
|
16
|
+
export const SearchDetail: React.FC<
|
17
|
+
import('@/pages/types').ThemeSearchDetailProps
|
18
|
+
>;
|
19
|
+
export const SearchPage: React.FC<
|
20
|
+
import('@/pages/types').ThemeSearchPageProps
|
21
|
+
>;
|
22
|
+
export const Slug: React.FC<import('@/pages/types').ThemePrefixProps>;
|
23
|
+
export const PrefixSlug: React.FC<
|
24
|
+
import('@/pages/types').ThemePrefixSlugProps
|
25
|
+
>;
|
26
|
+
export const PageNotFound: React.FC<
|
27
|
+
import('@/pages/types').ThemePageNotFoundProps
|
28
|
+
>;
|
29
|
+
}
|
@@ -0,0 +1,41 @@
|
|
1
|
+
import BLOG from '@/blog.config';
|
2
|
+
import dynamic from 'next/dynamic';
|
3
|
+
import { useSearchParams } from 'next/navigation';
|
4
|
+
import * as ThemeComponents from '@theme-components';
|
5
|
+
import { useRouter } from 'next/router';
|
6
|
+
|
7
|
+
/**
|
8
|
+
* Route path-to-layout mapping
|
9
|
+
*/
|
10
|
+
const layoutNameMapping: Record<string, string> = {
|
11
|
+
'/': 'Home',
|
12
|
+
'/archive': 'Archive',
|
13
|
+
'/page/[page]': 'Page',
|
14
|
+
'/category': 'Category',
|
15
|
+
'/category/[category]': 'CategoryDetail',
|
16
|
+
'/category/[category]/page/[page]': 'CategoryPage',
|
17
|
+
'/tag': 'Tag',
|
18
|
+
'/tag/[tag]': 'TagDetail',
|
19
|
+
'/tag/[tag]/page/[page]': 'TagPage',
|
20
|
+
'/search': 'Search',
|
21
|
+
'/search/[keyword]': 'SearchDetail',
|
22
|
+
'/search/[keyword]/page/[page]': 'SearchPage',
|
23
|
+
'/[prefix]': 'Prefix',
|
24
|
+
'/[prefix]/[slug]': 'PrefixSlug',
|
25
|
+
'/404': 'PageNotFound',
|
26
|
+
};
|
27
|
+
|
28
|
+
export const useLayout = (): React.ComponentType<any> => {
|
29
|
+
const router = useRouter();
|
30
|
+
const searchParams = useSearchParams();
|
31
|
+
|
32
|
+
const theme = searchParams.get('theme') || BLOG.THEME;
|
33
|
+
const layoutName = (layoutNameMapping[router.pathname] ||
|
34
|
+
'PageNotFound') as keyof typeof ThemeComponents;
|
35
|
+
|
36
|
+
return theme !== BLOG.THEME
|
37
|
+
? dynamic(() => import(`@/themes/${theme}`).then((m) => m[layoutName]), {
|
38
|
+
ssr: true,
|
39
|
+
})
|
40
|
+
: ThemeComponents[layoutName];
|
41
|
+
};
|
package/theme/utils.ts
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
import store from 'store';
|
2
|
+
import { getQueryVariable, isBrowser } from '../lib/utils';
|
3
|
+
import BLOG from '@/blog.config';
|
4
|
+
|
5
|
+
/**
|
6
|
+
* 初始化主题 , 优先级 query > cookies > systemPrefer
|
7
|
+
* @param isDarkMode
|
8
|
+
* @description 读取cookie中存的用户主题
|
9
|
+
*/
|
10
|
+
export const initDarkMode = () => {
|
11
|
+
// 查看用户设备浏览器是否深色模型
|
12
|
+
let isDarkMode = isPreferDark();
|
13
|
+
|
14
|
+
// 查看cookie中是否用户强制设置深色模式
|
15
|
+
const storageDarkMode = loadDarkModeFromLocalStorage();
|
16
|
+
if (storageDarkMode) {
|
17
|
+
isDarkMode = JSON.parse(storageDarkMode);
|
18
|
+
}
|
19
|
+
|
20
|
+
// url查询条件中是否深色模式
|
21
|
+
const queryMode = getQueryVariable('mode');
|
22
|
+
if (queryMode) {
|
23
|
+
isDarkMode = queryMode === 'dark';
|
24
|
+
}
|
25
|
+
|
26
|
+
return isDarkMode;
|
27
|
+
};
|
28
|
+
|
29
|
+
export const operateDarkMode = (isDarkMode: boolean) => {
|
30
|
+
const htmlElement = document.getElementsByTagName('html')[0];
|
31
|
+
htmlElement.classList.toggle('light', !isDarkMode);
|
32
|
+
htmlElement.classList.toggle('dark', isDarkMode);
|
33
|
+
};
|
34
|
+
|
35
|
+
/**
|
36
|
+
* 是否优先深色模式, 根据系统深色模式以及当前时间判断
|
37
|
+
* @returns {*}
|
38
|
+
*/
|
39
|
+
export function isPreferDark() {
|
40
|
+
if (BLOG.APPEARANCE === 'dark') {
|
41
|
+
return true;
|
42
|
+
}
|
43
|
+
if (BLOG.APPEARANCE === 'auto') {
|
44
|
+
// 系统深色模式或时间是夜间时,强行置为夜间模式
|
45
|
+
const date = new Date();
|
46
|
+
const prefersDarkMode = window.matchMedia(
|
47
|
+
'(prefers-color-scheme: dark)',
|
48
|
+
).matches;
|
49
|
+
return (
|
50
|
+
prefersDarkMode ||
|
51
|
+
(BLOG.APPEARANCE_DARK_TIME &&
|
52
|
+
(date.getHours() >= BLOG.APPEARANCE_DARK_TIME[0] ||
|
53
|
+
date.getHours() < BLOG.APPEARANCE_DARK_TIME[1]))
|
54
|
+
);
|
55
|
+
}
|
56
|
+
return false;
|
57
|
+
}
|
58
|
+
|
59
|
+
/**
|
60
|
+
* 读取深色模式
|
61
|
+
* @returns {*}
|
62
|
+
*/
|
63
|
+
export const loadDarkModeFromLocalStorage = () => {
|
64
|
+
return store.get('isDarkMode');
|
65
|
+
};
|
66
|
+
|
67
|
+
/**
|
68
|
+
* 保存深色模式
|
69
|
+
* @param isDarkMode
|
70
|
+
*/
|
71
|
+
export const saveDarkModeToLocalStorage = (isDarkMode: boolean) => {
|
72
|
+
store.set('isDarkMode', isDarkMode);
|
73
|
+
};
|
74
|
+
|
75
|
+
/**
|
76
|
+
* 读取默认主题
|
77
|
+
* @returns {*}
|
78
|
+
*/
|
79
|
+
export const loadThemeFromLocalStorage = () => {
|
80
|
+
return store.get('theme');
|
81
|
+
};
|
82
|
+
|
83
|
+
/**
|
84
|
+
* 保存默认主题
|
85
|
+
* @param newTheme
|
86
|
+
*/
|
87
|
+
export const saveThemeToLocalStorage = (newTheme: string) => {
|
88
|
+
store.set('theme', newTheme);
|
89
|
+
};
|
90
|
+
|
91
|
+
/**
|
92
|
+
* 切换主题时的特殊处理
|
93
|
+
* @param {*} setTheme
|
94
|
+
*/
|
95
|
+
export const initTheme = () => {
|
96
|
+
if (isBrowser) {
|
97
|
+
setTimeout(() => {
|
98
|
+
const elements = document.querySelectorAll('[id^="theme-"]');
|
99
|
+
if (elements?.length > 1) {
|
100
|
+
elements[elements.length - 1].scrollIntoView();
|
101
|
+
// 删除前面的元素,只保留最后一个元素
|
102
|
+
for (let i = 0; i < elements.length - 1; i++) {
|
103
|
+
elements[i]?.parentNode?.removeChild(elements[i]);
|
104
|
+
}
|
105
|
+
}
|
106
|
+
}, 500);
|
107
|
+
}
|
108
|
+
};
|
@@ -0,0 +1 @@
|
|
1
|
+
export default {};
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import { PageInfo } from '@/lib/notion/types';
|
2
|
+
import dynamic from 'next/dynamic';
|
3
|
+
import type { FC } from 'react';
|
4
|
+
|
5
|
+
const NotionPage = dynamic(() => import('@/components/NotionPage'));
|
6
|
+
|
7
|
+
export interface AnnouncementProps {
|
8
|
+
notice: PageInfo | null;
|
9
|
+
}
|
10
|
+
|
11
|
+
const Announcement: FC<AnnouncementProps> = ({ notice }) => {
|
12
|
+
if (notice && notice?.blockMap) {
|
13
|
+
return (
|
14
|
+
<div>
|
15
|
+
<section id="announcement-wrapper" className="mb-10">
|
16
|
+
<div id="announcement-content">
|
17
|
+
<NotionPage post={notice} className="text-center " />
|
18
|
+
</div>
|
19
|
+
</section>
|
20
|
+
</div>
|
21
|
+
);
|
22
|
+
} else {
|
23
|
+
return null;
|
24
|
+
}
|
25
|
+
};
|
26
|
+
|
27
|
+
export default Announcement;
|
@@ -0,0 +1,39 @@
|
|
1
|
+
import { useRouter } from 'next/router';
|
2
|
+
import { useTranslation } from 'next-i18next';
|
3
|
+
|
4
|
+
/**
|
5
|
+
* 加密文章校验组件
|
6
|
+
* @param {password, validPassword} props
|
7
|
+
* @param password 正确的密码
|
8
|
+
* @param validPassword(bool) 回调函数,校验正确回调入参为true
|
9
|
+
* @returns
|
10
|
+
*/
|
11
|
+
export const ArticleFooter = () => {
|
12
|
+
const router = useRouter();
|
13
|
+
const { t } = useTranslation('common');
|
14
|
+
|
15
|
+
return (
|
16
|
+
<div className="flex justify-between font-medium text-gray-500 dark:text-gray-400">
|
17
|
+
<a>
|
18
|
+
<button
|
19
|
+
onClick={() => router.back()}
|
20
|
+
className="mt-2 cursor-pointer hover:text-black dark:hover:text-gray-100"
|
21
|
+
>
|
22
|
+
<i className="fas fa-angle-left mr-1" />
|
23
|
+
{t('back')}
|
24
|
+
</button>
|
25
|
+
</a>
|
26
|
+
<a>
|
27
|
+
<button
|
28
|
+
onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}
|
29
|
+
className="mt-2 cursor-pointer hover:text-black dark:hover:text-gray-100"
|
30
|
+
>
|
31
|
+
<i className="fas fa-angle-up mr-1" />
|
32
|
+
{t('top')}
|
33
|
+
</button>
|
34
|
+
</a>
|
35
|
+
</div>
|
36
|
+
);
|
37
|
+
};
|
38
|
+
|
39
|
+
export default ArticleFooter;
|
@@ -0,0 +1,58 @@
|
|
1
|
+
import Image from 'next/image';
|
2
|
+
import BLOG from '@/blog.config';
|
3
|
+
import TagItem from './TagItem';
|
4
|
+
import md5 from 'js-md5';
|
5
|
+
import dayjs from 'dayjs';
|
6
|
+
|
7
|
+
import type { FC } from 'react';
|
8
|
+
import type { PageInfo } from '@/lib/notion/types';
|
9
|
+
|
10
|
+
export interface ArticleInfoProps {
|
11
|
+
post: PageInfo;
|
12
|
+
}
|
13
|
+
|
14
|
+
export const ArticleInfo: FC<ArticleInfoProps> = ({ post }) => {
|
15
|
+
const emailHash = md5(BLOG.CONTACT_EMAIL);
|
16
|
+
|
17
|
+
return (
|
18
|
+
<section className="text-gray--600 mt-2 flex flex-wrap font-light leading-8 dark:text-gray-400">
|
19
|
+
<div>
|
20
|
+
<div className="text-3xl font-bold text-black dark:text-white">
|
21
|
+
{post?.title}
|
22
|
+
</div>
|
23
|
+
|
24
|
+
{post?.type !== 'Page' && (
|
25
|
+
<nav className="mt-7 flex items-start text-gray-500 dark:text-gray-400">
|
26
|
+
<div className="mb-4 flex">
|
27
|
+
<a href={BLOG.CONTACT_GITHUB || '#'} className="flex">
|
28
|
+
<Image
|
29
|
+
alt={BLOG.AUTHOR}
|
30
|
+
width={24}
|
31
|
+
height={24}
|
32
|
+
src={`https://gravatar.com/avatar/${emailHash}`}
|
33
|
+
className="rounded-full"
|
34
|
+
/>
|
35
|
+
<p className="ml-2 md:block">{BLOG.AUTHOR}</p>
|
36
|
+
</a>
|
37
|
+
<span className="mx-1 block">/</span>
|
38
|
+
</div>
|
39
|
+
<div className="mb-4 mr-2 md:ml-0">
|
40
|
+
{dayjs(post?.publishDate).format('YYYY-MM-DD')}
|
41
|
+
</div>
|
42
|
+
{post?.tags && (
|
43
|
+
<div className="article-tags mr-2 flex max-w-full flex-nowrap overflow-x-auto">
|
44
|
+
{post?.tags.map((tag) => <TagItem key={tag} tag={tag} />)}
|
45
|
+
</div>
|
46
|
+
)}
|
47
|
+
<span className="busuanzi_container_page_pv mr-2 hidden">
|
48
|
+
<i className="fas fa-eye mr-1" />
|
49
|
+
<span className="busuanzi_value_page_pv mr-2" />
|
50
|
+
</span>
|
51
|
+
</nav>
|
52
|
+
)}
|
53
|
+
</div>
|
54
|
+
</section>
|
55
|
+
);
|
56
|
+
};
|
57
|
+
|
58
|
+
export default ArticleInfo;
|