update
This commit is contained in:
70
src/components/MdPreview.tsx
Normal file
70
src/components/MdPreview.tsx
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { Marked } from 'marked';
|
||||||
|
import hljs from 'highlight.js';
|
||||||
|
import { markedHighlight } from 'marked-highlight';
|
||||||
|
|
||||||
|
const markedAndHighlight = new Marked(
|
||||||
|
markedHighlight({
|
||||||
|
emptyLangClass: 'hljs',
|
||||||
|
langPrefix: 'hljs language-',
|
||||||
|
highlight(code, lang, info) {
|
||||||
|
const language = hljs.getLanguage(lang) ? lang : 'plaintext';
|
||||||
|
return hljs.highlight(code, { language }).value;
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const md2html = async (md: string) => {
|
||||||
|
const html = markedAndHighlight.parse(md);
|
||||||
|
return html;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const clearMeta = (markdown?: string) => {
|
||||||
|
if (!markdown) return '';
|
||||||
|
// Remove YAML front matter if present
|
||||||
|
const yamlRegex = /^---\n[\s\S]*?\n---\n/;
|
||||||
|
return markdown.replace(yamlRegex, '');
|
||||||
|
};
|
||||||
|
type Props = {
|
||||||
|
children?: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
style?: React.CSSProperties;
|
||||||
|
content?: string; // Optional content prop for markdown text
|
||||||
|
[key: string]: any; // Allow any additional props
|
||||||
|
};
|
||||||
|
export const MarkdownPreview = (props: Props) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'markdown-body scrollbar h-full overflow-auto w-full px-6 py-2 max-w-[800px] border my-4 flex flex-col justify-self-center rounded-md shadow-md',
|
||||||
|
props.className,
|
||||||
|
)}
|
||||||
|
style={props.style}>
|
||||||
|
{props.children ? <WrapperText>{props.children}</WrapperText> : <MarkdownPreviewWrapper content={clearMeta(props.content)} />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WrapperText = (props: { children?: React.ReactNode; html?: string }) => {
|
||||||
|
if (props.html) {
|
||||||
|
return <div className='w-full' dangerouslySetInnerHTML={{ __html: props.html }} />;
|
||||||
|
}
|
||||||
|
return <div className='w-full h-full'>{props.children}</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MarkdownPreviewWrapper = (props: Props) => {
|
||||||
|
const [html, setHtml] = useState<string>('');
|
||||||
|
useEffect(() => {
|
||||||
|
init();
|
||||||
|
}, [props.content]);
|
||||||
|
const init = async () => {
|
||||||
|
if (props.content) {
|
||||||
|
const htmlContent = await md2html(props.content);
|
||||||
|
setHtml(htmlContent);
|
||||||
|
} else {
|
||||||
|
setHtml('');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return <WrapperText html={html} />;
|
||||||
|
};
|
||||||
17
src/content.config.ts
Normal file
17
src/content.config.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
// @ts-ignore
|
||||||
|
import { defineCollection, z } from 'astro:content';
|
||||||
|
import { glob, file } from 'astro/loaders'; // 不适用于旧版 API
|
||||||
|
|
||||||
|
const docs = defineCollection({
|
||||||
|
// loader: glob({ pattern: '**/*.md', base: './src/data/blogs' }),
|
||||||
|
loader: glob({ pattern: '**/[^_]*.md', base: './src/data/docs' }),
|
||||||
|
schema: z.object({
|
||||||
|
title: z.string().optional(),
|
||||||
|
description: z.string().optional(),
|
||||||
|
// pubDate: z.coerce.date(),
|
||||||
|
// updatedDate: z.coerce.date().optional(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export const collections = { docs };
|
||||||
24
src/layouts/blank.astro
Normal file
24
src/layouts/blank.astro
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
import '../styles/global.css';
|
||||||
|
import '../styles/theme.css';
|
||||||
|
---
|
||||||
|
|
||||||
|
<html lang='zh-CN'>
|
||||||
|
<head>
|
||||||
|
<meta charset='UTF-8' />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>AI Pages</title>
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<slot />
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
60
src/layouts/mdx.astro
Normal file
60
src/layouts/mdx.astro
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
---
|
||||||
|
export interface Props {
|
||||||
|
children: any;
|
||||||
|
}
|
||||||
|
import '../styles/global.css';
|
||||||
|
import '../styles/theme.css';
|
||||||
|
export interface Props {
|
||||||
|
title?: string;
|
||||||
|
description?: string;
|
||||||
|
lang?: string;
|
||||||
|
charset?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { title = 'Light Code', description = 'A lightweight code editor', lang = 'zh-CN', charset = 'UTF-8' } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<html lang='zh-CN'>
|
||||||
|
<head>
|
||||||
|
<meta charset='UTF-8' />
|
||||||
|
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
|
||||||
|
<title>Docs</title>
|
||||||
|
<link
|
||||||
|
rel='stylesheet'
|
||||||
|
href='https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.8.1/github-markdown-light.min.css'
|
||||||
|
integrity='sha512-X175XRJAO6PHAUi8AA7GP8uUF5Wiv+w9bOi64i02CHKDQBsO1yy0jLSKaUKg/NhRCDYBmOLQCfKaTaXiyZlLrw=='
|
||||||
|
crossorigin='anonymous'
|
||||||
|
referrerpolicy='no-referrer'
|
||||||
|
/>
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<slot name='header' />
|
||||||
|
</div>
|
||||||
|
<main class='p-2 flex-1 overflow-hidden'>
|
||||||
|
<div class='markdown-body h-full scrollbar border-gray-200 overflow-auto w-full px-6 py-2 max-w-[800px] border my-4 rounded-md shadow-md'>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<footer>
|
||||||
|
<slot name='footer'>
|
||||||
|
<p>Copyrignt © 2025</p>
|
||||||
|
</slot>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
4
src/modules/basename.ts
Normal file
4
src/modules/basename.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
// @ts-ignore
|
||||||
|
export const basename = BASE_NAME;
|
||||||
|
|
||||||
|
console.log(basename);
|
||||||
23
src/pages/docs/[...id].astro
Normal file
23
src/pages/docs/[...id].astro
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
import { getCollection, render } from 'astro:content';
|
||||||
|
import Main from '@/layouts/mdx.astro';
|
||||||
|
// 1. 为每个集合条目生成一个新路径
|
||||||
|
export async function getStaticPaths() {
|
||||||
|
const posts = await getCollection('docs');
|
||||||
|
return posts.map((post) => ({
|
||||||
|
params: { id: post.id },
|
||||||
|
props: { post },
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
type Post = {
|
||||||
|
data: { title: string };
|
||||||
|
};
|
||||||
|
// 2. 对于你的模板,你可以直接从 prop 获取条目
|
||||||
|
const { post } = Astro.props as { post: Post };
|
||||||
|
const { Content } = await render(post);
|
||||||
|
---
|
||||||
|
|
||||||
|
<Main>
|
||||||
|
<!-- <h1 slot={'header'}>{post.data.title}</h1> -->
|
||||||
|
<Content />
|
||||||
|
</Main>
|
||||||
23
src/pages/docs/index.astro
Normal file
23
src/pages/docs/index.astro
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
import { getCollection } from 'astro:content';
|
||||||
|
const posts = await getCollection('docs');
|
||||||
|
console.log('post', posts);
|
||||||
|
import { basename } from '@/modules/basename';
|
||||||
|
import Blank from '@/layouts/blank.astro';
|
||||||
|
---
|
||||||
|
|
||||||
|
<Blank>
|
||||||
|
<main class='max-w-3xl mx-auto'>
|
||||||
|
<h1>My posts</h1>
|
||||||
|
<ul class='p-2 m-2'>
|
||||||
|
{
|
||||||
|
posts.map((post) => (
|
||||||
|
<li>
|
||||||
|
{/* <a href={`${basename}/demo/${post.id}`}>{post.data.title}</a> */}
|
||||||
|
<a href={`/docs/${post.id}/`}>{post.data.title}</a>
|
||||||
|
</li>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</main>
|
||||||
|
</Blank>
|
||||||
79
src/styles/theme.css
Normal file
79
src/styles/theme.css
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
@import 'tailwindcss';
|
||||||
|
|
||||||
|
@theme {
|
||||||
|
/* --color-primary: #ffc107;
|
||||||
|
--color-secondary: #ffa000;
|
||||||
|
--color-text-primary: #000000;
|
||||||
|
--color-text-secondary: #000000;
|
||||||
|
--color-success: #28a745; */
|
||||||
|
|
||||||
|
--color-scrollbar-thumb: #999999;
|
||||||
|
--color-scrollbar-track: rgba(0, 0, 0, 0.1);
|
||||||
|
--color-scrollbar-thumb-hover: #666666;
|
||||||
|
--scrollbar-color: #ffc107;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
font-size: 16px;
|
||||||
|
font-family: 'Montserrat', sans-serif;
|
||||||
|
}
|
||||||
|
/* font-family */
|
||||||
|
@utility font-family-mon {
|
||||||
|
font-family: 'Montserrat', sans-serif;
|
||||||
|
}
|
||||||
|
@utility font-family-rob {
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
}
|
||||||
|
@utility font-family-int {
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
}
|
||||||
|
@utility font-family-orb {
|
||||||
|
font-family: 'Orbitron', sans-serif;
|
||||||
|
}
|
||||||
|
@utility font-family-din {
|
||||||
|
font-family: 'DIN', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
@utility flex-row-center {
|
||||||
|
@apply flex flex-row items-center justify-center;
|
||||||
|
}
|
||||||
|
@utility flex-col-center {
|
||||||
|
@apply flex flex-col items-center justify-center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@utility scrollbar {
|
||||||
|
overflow: auto;
|
||||||
|
/* 整个滚动条 */
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 3px;
|
||||||
|
height: 3px;
|
||||||
|
}
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
background-color: var(--color-scrollbar-track);
|
||||||
|
}
|
||||||
|
/* 滚动条有滑块的轨道部分 */
|
||||||
|
&::-webkit-scrollbar-track-piece {
|
||||||
|
background-color: transparent;
|
||||||
|
border-radius: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 滚动条滑块(竖向:vertical 横向:horizontal) */
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: var(--color-scrollbar-thumb);
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 滚动条滑块hover */
|
||||||
|
&::-webkit-scrollbar-thumb:hover {
|
||||||
|
background-color: var(--color-scrollbar-thumb-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 同时有垂直和水平滚动条时交汇的部分 */
|
||||||
|
&::-webkit-scrollbar-corner {
|
||||||
|
display: block; /* 修复交汇时出现的白块 */
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user