mirror of
https://github.com/DefectingCat/DefectingCat.github.io
synced 2025-07-16 09:11:38 +00:00
Fix checklist in markdown
* Add strip-markdown * Add CopyButton component * Fix copy code * Add about page * Use async function with get all markdown content * Add shortcut icon * Remove useless code
This commit is contained in:
39
README.md
39
README.md
@ -1,38 +1,7 @@
|
||||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||
## 小破站
|
||||
|
||||
## Getting Started
|
||||
一个个人博客,Powered by [Next.js](https://nextjs.org/).
|
||||
|
||||
First, run the development server:
|
||||
## 施工中
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
|
||||
|
||||
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
|
||||
|
||||
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||
|
||||
## Todo
|
||||
|
||||
- [ ] Add types https://github1s.com/vercel/next.js/blob/canary/examples/blog-starter-typescript/
|
||||
- [x] 归档页面
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit';
|
||||
import { configureStore } from '@reduxjs/toolkit';
|
||||
import routerReducer from '../features/router/routerSlice';
|
||||
|
||||
export const store = configureStore({
|
||||
|
39
components/post/CopyButton.tsx
Normal file
39
components/post/CopyButton.tsx
Normal file
@ -0,0 +1,39 @@
|
||||
import { Button, useClipboard } from '@chakra-ui/react';
|
||||
import { FC, MouseEventHandler, useEffect, useState } from 'react';
|
||||
|
||||
const CopyButton: FC = ({ children }) => {
|
||||
// Copy code
|
||||
const [codeContent, setCodeContent] = useState('');
|
||||
const { hasCopied, onCopy } = useClipboard(codeContent);
|
||||
|
||||
const copyCode: MouseEventHandler<HTMLButtonElement> = (e) => {
|
||||
const target = e.target as HTMLButtonElement;
|
||||
// Button is sibling with Code tag
|
||||
const codeToCopy = target.nextElementSibling?.textContent;
|
||||
codeToCopy && setCodeContent(codeToCopy);
|
||||
};
|
||||
useEffect(() => {
|
||||
codeContent && onCopy();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [codeContent]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<pre>
|
||||
<Button
|
||||
size="xs"
|
||||
colorScheme="teal"
|
||||
position="absolute"
|
||||
top="5px"
|
||||
right="5px"
|
||||
onClick={copyCode}
|
||||
>
|
||||
{hasCopied ? 'COPYIED' : 'COPY'}
|
||||
</Button>
|
||||
{children}
|
||||
</pre>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default CopyButton;
|
35
lib/posts.ts
35
lib/posts.ts
@ -1,7 +1,8 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import matter from 'gray-matter';
|
||||
import markdownToTxt from 'markdown-to-txt';
|
||||
import { remark } from 'remark';
|
||||
import strip from 'strip-markdown';
|
||||
|
||||
const postsDirectory = path.join(process.cwd(), 'public/posts');
|
||||
|
||||
@ -23,10 +24,11 @@ export interface AllPostsData extends MyMatters {
|
||||
* Get all sorted posts
|
||||
* @returns
|
||||
*/
|
||||
export function getSortedPostsData() {
|
||||
export async function getSortedPostsData() {
|
||||
// Get file names under /posts
|
||||
const fileNames = fs.readdirSync(postsDirectory);
|
||||
const allPostsData = fileNames.map((fileName) => {
|
||||
const allPostsData = await Promise.all(
|
||||
fileNames.map(async (fileName) => {
|
||||
// Remove ".md" from file name to get id
|
||||
const id = fileName.replace(/\.md$/, '');
|
||||
|
||||
@ -38,19 +40,22 @@ export function getSortedPostsData() {
|
||||
const matterResult = matter(fileContents);
|
||||
|
||||
// Process markdown to plain text
|
||||
const contentText = markdownToTxt(matterResult.content);
|
||||
const contentText = await remark()
|
||||
.use(strip)
|
||||
.process(matterResult.content);
|
||||
|
||||
// Combine the data with the id
|
||||
return {
|
||||
id,
|
||||
// Add post description
|
||||
desc: `${contentText.slice(0, 100)}...`,
|
||||
desc: `${contentText.toString().slice(0, 100)}...`,
|
||||
...({
|
||||
...matterResult.data,
|
||||
date: matterResult.data.date.toISOString(),
|
||||
} as MyMatters),
|
||||
};
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
// Sort posts by date
|
||||
return allPostsData.sort(({ date: a }, { date: b }) => {
|
||||
@ -62,12 +67,6 @@ export function getSortedPostsData() {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
// Convert Date to locale string.
|
||||
// return allPostsData.map((post) => {
|
||||
// if (typeof post.date == 'object') post.date = post.date.toISOString();
|
||||
// return post;
|
||||
// });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -119,7 +118,7 @@ export function getPagingData(allPostsData: AllPostsData[], start?: string) {
|
||||
};
|
||||
}
|
||||
|
||||
export function getAllPostSlugs() {
|
||||
export async function getAllPostSlugs() {
|
||||
const fileNames = fs.readdirSync(postsDirectory);
|
||||
|
||||
// Returns an array that looks like this:
|
||||
@ -135,8 +134,9 @@ export function getAllPostSlugs() {
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
return fileNames.map((fileName) => {
|
||||
const allPostsData = getSortedPostsData();
|
||||
return await Promise.all(
|
||||
fileNames.map(async (fileName) => {
|
||||
const allPostsData = await getSortedPostsData();
|
||||
const slug = allPostsData.find(
|
||||
(item) => item.id === fileName.replace(/\.md$/, '')
|
||||
);
|
||||
@ -146,7 +146,8 @@ export function getAllPostSlugs() {
|
||||
slug: slug?.url,
|
||||
},
|
||||
};
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export interface MyPost extends AllPostsData {
|
||||
@ -154,7 +155,7 @@ export interface MyPost extends AllPostsData {
|
||||
}
|
||||
|
||||
export async function getPostData(slug: string) {
|
||||
const allPostsData = getSortedPostsData();
|
||||
const allPostsData = await getSortedPostsData();
|
||||
const post = allPostsData.find((item) => item.url === slug);
|
||||
|
||||
const fullPath = path.join(postsDirectory, `${post?.id}.md`);
|
||||
|
@ -25,7 +25,6 @@
|
||||
"framer-motion": "^5.0.2",
|
||||
"gray-matter": "^4.0.3",
|
||||
"highlight.js": "^11.3.1",
|
||||
"markdown-to-txt": "^1.0.1",
|
||||
"next": "11.1.2",
|
||||
"postcss": "^8.3.11",
|
||||
"react": "17.0.2",
|
||||
@ -44,6 +43,7 @@
|
||||
"remark-parse": "^10.0.0",
|
||||
"remark-rehype": "^10.0.1",
|
||||
"remark-toc": "^8.0.1",
|
||||
"strip-markdown": "^5.0.0",
|
||||
"unified": "^10.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -7,6 +7,7 @@ import { Provider } from 'react-redux';
|
||||
import { store } from '../app/store';
|
||||
import 'antd/dist/antd.css';
|
||||
import '../assets/css/rua.css';
|
||||
import Head from 'next/head';
|
||||
|
||||
const theme = extendTheme({
|
||||
colors: {
|
||||
@ -35,11 +36,17 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
||||
const getLayout = Component.getLayout ?? ((page) => page);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<link rel="shortcut icon" href="/images/img/favicon.webp"></link>
|
||||
</Head>
|
||||
|
||||
<Provider store={store}>
|
||||
<ChakraProvider theme={theme}>
|
||||
{getLayout(<Component {...pageProps} />)}
|
||||
</ChakraProvider>
|
||||
</Provider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
export default MyApp;
|
||||
|
31
pages/about.tsx
Normal file
31
pages/about.tsx
Normal file
@ -0,0 +1,31 @@
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import Head from 'next/head';
|
||||
import { ReactElement } from 'react';
|
||||
import HomeLayout from '../layouts/HomeLayout';
|
||||
|
||||
const about = () => {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>RUA - About</title>
|
||||
</Head>
|
||||
|
||||
<Box
|
||||
w={['full', 'full', '55rem']}
|
||||
borderRadius="10px"
|
||||
shadow="lg"
|
||||
bg="white"
|
||||
overflow="hidden"
|
||||
p={['1rem', '1rem', '1.5rem']}
|
||||
>
|
||||
施工中...
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
about.getLayout = function getLayout(page: ReactElement) {
|
||||
return <HomeLayout>{page}</HomeLayout>;
|
||||
};
|
||||
|
||||
export default about;
|
@ -7,7 +7,7 @@ import { getArchiveData, getSortedPostsData } from '../lib/posts';
|
||||
import ArchiveCard from '../components/ArchiveCard';
|
||||
|
||||
export const getStaticProps = async () => {
|
||||
const allPostsData = getSortedPostsData();
|
||||
const allPostsData = await getSortedPostsData();
|
||||
|
||||
return {
|
||||
props: {
|
||||
|
@ -7,7 +7,7 @@ import HomeLayout from '../layouts/HomeLayout';
|
||||
import PostCard from '../components/PostCard';
|
||||
|
||||
export const getStaticProps = async () => {
|
||||
const allPostsData = getSortedPostsData();
|
||||
const allPostsData = await getSortedPostsData();
|
||||
|
||||
return {
|
||||
props: {
|
||||
|
@ -10,13 +10,7 @@ import remarkParse from 'remark-parse';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import rehypeHighlight from 'rehype-highlight';
|
||||
import { unified } from 'unified';
|
||||
import {
|
||||
createElement,
|
||||
Fragment,
|
||||
MouseEventHandler,
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { createElement, Fragment } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Image,
|
||||
@ -25,7 +19,6 @@ import {
|
||||
Icon,
|
||||
Link,
|
||||
Button,
|
||||
useClipboard,
|
||||
Tag,
|
||||
} from '@chakra-ui/react';
|
||||
import 'highlight.js/styles/github.css';
|
||||
@ -41,9 +34,10 @@ import { Giscus } from '@giscus/react';
|
||||
import { RootState } from '../../app/store';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import { cleanFromPath } from '../../features/router/routerSlice';
|
||||
import CopyButton from '../../components/post/CopyButton';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const paths = getAllPostSlugs();
|
||||
const paths = await getAllPostSlugs();
|
||||
return {
|
||||
paths,
|
||||
fallback: false,
|
||||
@ -66,23 +60,9 @@ const Post = ({ postData }: InferGetStaticPropsType<typeof getStaticProps>) => {
|
||||
|
||||
const goBack = () => {
|
||||
dispatch(cleanFromPath());
|
||||
router.push(fromPath);
|
||||
router.push(fromPath || '/');
|
||||
};
|
||||
|
||||
// Copy code
|
||||
const [codeContent, setCodeContent] = useState('');
|
||||
const { hasCopied, onCopy } = useClipboard(codeContent);
|
||||
const copyCode: MouseEventHandler<HTMLButtonElement> = (e) => {
|
||||
const target = e.target as HTMLButtonElement;
|
||||
// Button is sibling with Code tag
|
||||
const codeToCopy = target.nextElementSibling?.textContent;
|
||||
codeToCopy && setCodeContent(codeToCopy);
|
||||
};
|
||||
useEffect(() => {
|
||||
codeContent && onCopy();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [codeContent]);
|
||||
|
||||
const processedContent = unified()
|
||||
.use(remarkParse)
|
||||
.use(remarkToc, {
|
||||
@ -116,23 +96,7 @@ const Post = ({ postData }: InferGetStaticPropsType<typeof getStaticProps>) => {
|
||||
</Box>
|
||||
);
|
||||
},
|
||||
pre: (props: any) => {
|
||||
return (
|
||||
<pre {...props}>
|
||||
<Button
|
||||
size="xs"
|
||||
colorScheme="teal"
|
||||
position="absolute"
|
||||
top="5px"
|
||||
right="5px"
|
||||
onClick={copyCode}
|
||||
>
|
||||
{hasCopied ? 'COPYIED' : 'COPY'}
|
||||
</Button>
|
||||
{props.children}
|
||||
</pre>
|
||||
);
|
||||
},
|
||||
pre: CopyButton,
|
||||
},
|
||||
Fragment,
|
||||
});
|
||||
@ -165,6 +129,7 @@ const Post = ({ postData }: InferGetStaticPropsType<typeof getStaticProps>) => {
|
||||
flexFlow={['column', 'column', 'row']}
|
||||
px="0.5rem"
|
||||
>
|
||||
{/* Back button */}
|
||||
<Button
|
||||
size="lg"
|
||||
mt={['1rem', '1rem', '3rem']}
|
||||
@ -175,6 +140,7 @@ const Post = ({ postData }: InferGetStaticPropsType<typeof getStaticProps>) => {
|
||||
BACK
|
||||
</Button>
|
||||
|
||||
{/* Main article area */}
|
||||
<Flex
|
||||
w={['full', 'full', '55rem', '68rem']}
|
||||
flexFlow="column"
|
||||
@ -188,6 +154,7 @@ const Post = ({ postData }: InferGetStaticPropsType<typeof getStaticProps>) => {
|
||||
overflow="hidden"
|
||||
flex="1"
|
||||
>
|
||||
{/* Post image */}
|
||||
{postData.index_img && (
|
||||
<Image
|
||||
src={postData.index_img}
|
||||
@ -197,12 +164,15 @@ const Post = ({ postData }: InferGetStaticPropsType<typeof getStaticProps>) => {
|
||||
alt="Head image"
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Post heading */}
|
||||
<Box as="article" p={['1rem', '1rem', '2rem']}>
|
||||
<Box as="header">
|
||||
<Heading as="h1" mb="1rem">
|
||||
{postData.title}
|
||||
</Heading>
|
||||
|
||||
{/* Post tags */}
|
||||
<Box mb="1rem">
|
||||
{Array.isArray(postData.tags)
|
||||
? // Mutil tags
|
||||
@ -222,12 +192,14 @@ const Post = ({ postData }: InferGetStaticPropsType<typeof getStaticProps>) => {
|
||||
</Flex>
|
||||
</Box>
|
||||
|
||||
{/* Post content */}
|
||||
<Box as="section" id="write" mt="2rem">
|
||||
{postContent}
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Comment */}
|
||||
<Box mt="2rem">
|
||||
<Giscus
|
||||
repo="DefectingCat/DefectingCat.github.io"
|
||||
@ -244,6 +216,7 @@ const Post = ({ postData }: InferGetStaticPropsType<typeof getStaticProps>) => {
|
||||
<Footer />
|
||||
</Flex>
|
||||
|
||||
{/* Table of content */}
|
||||
{toc && (
|
||||
<Box
|
||||
display={['none', 'none', 'none', 'block']}
|
||||
|
@ -6,23 +6,6 @@ categories: 踩坑
|
||||
url: markdown-test
|
||||
---
|
||||
|
||||
## Hello world
|
||||
|
||||
This is a test.
|
||||
|
||||
---
|
||||
__Advertisement :)__
|
||||
|
||||
- __[pica](https://nodeca.github.io/pica/demo/)__ - high quality and fast image
|
||||
resize in browser.
|
||||
- __[babelfish](https://github.com/nodeca/babelfish/)__ - developer friendly
|
||||
i18n with plurals support and easy syntax.
|
||||
|
||||
You will like those projects!
|
||||
|
||||
---
|
||||
|
||||
# h1 Heading
|
||||
## h2 Heading
|
||||
### h3 Heading
|
||||
#### h4 Heading
|
||||
@ -38,6 +21,38 @@ ___
|
||||
|
||||
***
|
||||
|
||||
## Lists
|
||||
|
||||
### Solar System Exploration, 1950s – 1960s
|
||||
|
||||
- [ ] Mercury
|
||||
- [x] Venus
|
||||
- [x] Earth (Orbit/Moon)
|
||||
- [x] Mars
|
||||
- [ ] Jupiter
|
||||
- [ ] Saturn
|
||||
- [ ] Uranus
|
||||
- [ ] Neptune
|
||||
- [ ] Comet Haley
|
||||
|
||||
### Numbers
|
||||
|
||||
1. One
|
||||
2. Two
|
||||
3. Three
|
||||
|
||||
### Solar System Exploration, 1950s – 1960s
|
||||
|
||||
- Mercury
|
||||
- Venus
|
||||
- Earth (Orbit/Moon)
|
||||
- Mars
|
||||
- Jupiter
|
||||
- Saturn
|
||||
- Uranus
|
||||
- Neptune
|
||||
- Comet Haley
|
||||
|
||||
|
||||
## Typographic replacements
|
||||
|
||||
|
23
yarn.lock
23
yarn.lock
@ -1208,7 +1208,7 @@
|
||||
resolved "https://registry.npmmirror.com/@types/sizzle/download/@types/sizzle-2.3.3.tgz?cache=0&sync_timestamp=1637272274009&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2F%40types%2Fsizzle%2Fdownload%2F%40types%2Fsizzle-2.3.3.tgz#ff5e2f1902969d305225a047c8a0fd5c915cebef"
|
||||
integrity sha1-/14vGQKWnTBSJaBHyKD9XJFc6+8=
|
||||
|
||||
"@types/unist@*", "@types/unist@^2.0.0":
|
||||
"@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.6":
|
||||
version "2.0.6"
|
||||
resolved "https://registry.nlark.com/@types/unist/download/@types/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d"
|
||||
integrity sha1-JQp7FsO5H2cqJFUuxkZ47rHToI0=
|
||||
@ -4144,18 +4144,6 @@ markdown-table@^3.0.0:
|
||||
resolved "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.1.tgz#88c48957aaf2a8014ccb2ba026776a1d736fe3dc"
|
||||
integrity sha512-CBbaYXKSGnE1uLRpKA1SWgIRb2PQrpkllNWpZtZe6VojOJ4ysqiq7/2glYcmKsOYN09QgH/HEBX5hIshAeiK6A==
|
||||
|
||||
markdown-to-txt@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmmirror.com/markdown-to-txt/download/markdown-to-txt-1.0.1.tgz#f02528acaf77366d6f29569546de61acd74ba2b7"
|
||||
integrity sha1-8CUorK93Nm1vKVaVRt5hrNdLorc=
|
||||
dependencies:
|
||||
marked "^0.6.2"
|
||||
|
||||
marked@^0.6.2:
|
||||
version "0.6.3"
|
||||
resolved "https://registry.npmmirror.com/marked/download/marked-0.6.3.tgz#79babad78af638ba4d522a9e715cdfdd2429e946"
|
||||
integrity sha1-ebq614r2OLpNUiqecVzf3SQp6UY=
|
||||
|
||||
md5.js@^1.3.4:
|
||||
version "1.3.5"
|
||||
resolved "https://registry.npm.taobao.org/md5.js/download/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
|
||||
@ -6512,6 +6500,15 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
|
||||
resolved "https://registry.nlark.com/strip-json-comments/download/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
|
||||
integrity sha1-MfEoGzgyYwQ0gxwxDAHMzajL4AY=
|
||||
|
||||
strip-markdown@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.npmjs.org/strip-markdown/-/strip-markdown-5.0.0.tgz#222b864ecce6cf2ef87b2bb1e6466464e8127081"
|
||||
integrity sha512-PXSts6Ta9A/TwGxVVSRlQs1ukJTAwwtbip2OheJEjPyfykaQ4sJSTnQWjLTI2vYWNts/R/91/csagp15W8n9gA==
|
||||
dependencies:
|
||||
"@types/mdast" "^3.0.0"
|
||||
"@types/unist" "^2.0.6"
|
||||
unified "^10.0.0"
|
||||
|
||||
style-to-object@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.npmjs.org/style-to-object/-/style-to-object-0.3.0.tgz#b1b790d205991cc783801967214979ee19a76e46"
|
||||
|
Reference in New Issue
Block a user