feat: add basic webui

This commit is contained in:
2024-12-30 06:39:09 +08:00
parent 608a7fb9c6
commit a4c549e7c3
462 changed files with 35900 additions and 2491 deletions

View File

@@ -0,0 +1,89 @@
import { defineCollection, defineConfig } from '@content-collections/core';
import { compileMDX } from '@content-collections/mdx';
import {
type RehypeCodeOptions,
rehypeCode,
remarkGfm,
remarkHeading,
} from 'fumadocs-core/mdx-plugins';
import readingTime from 'reading-time';
import { sqip } from 'sqip';
const rehypeCodeOptions: RehypeCodeOptions = {
themes: {
light: 'catppuccin-mocha',
dark: 'catppuccin-mocha',
},
};
const posts = defineCollection({
name: 'posts',
directory: 'content/blog',
include: '**/*.mdx',
schema: (z) => ({
title: z.string(),
description: z.string(),
date: z.string(),
image: z.string(),
authors: z.array(z.string()),
tags: z.array(z.string()),
}),
transform: async ({ title, ...page }, context) => {
const body = await context.cache(page.content, async () =>
compileMDX(context, page, {
remarkPlugins: [remarkGfm, remarkHeading],
rehypePlugins: [[rehypeCode, rehypeCodeOptions]],
})
);
const blur = await context.cache(page._meta.path, async () =>
sqip({
input: `./public/${page.image}`,
plugins: [
'sqip-plugin-primitive',
'sqip-plugin-svgo',
'sqip-plugin-data-uri',
],
})
);
const blurResult = Array.isArray(blur) ? blur[0] : blur;
return {
...page,
_title: title,
_slug: page._meta.path,
body,
image: page.image,
imageBlur: blurResult.metadata.dataURIBase64 as string,
readingTime: readingTime(page.content).text,
};
},
});
const legals = defineCollection({
name: 'legal',
directory: 'content/legal',
include: '**/*.mdx',
schema: (z) => ({
title: z.string(),
description: z.string(),
date: z.string(),
}),
transform: async ({ title, ...page }, context) => {
const body = await context.cache(page.content, async () =>
compileMDX(context, page)
);
return {
...page,
_title: title,
_slug: page._meta.path,
body,
};
},
});
export default defineConfig({
collections: [posts, legals],
});

View File

@@ -0,0 +1,10 @@
import { MDXContent } from '@content-collections/mdx/react';
import type { ComponentProps } from 'react';
type BodyProperties = Omit<ComponentProps<typeof MDXContent>, 'code'> & {
content: ComponentProps<typeof MDXContent>['code'];
};
export const Body = ({ content, ...props }: BodyProperties) => (
<MDXContent {...props} code={content} />
);

View File

@@ -0,0 +1 @@
export { default as Image } from 'next/image';

View File

@@ -0,0 +1,31 @@
import { getTableOfContents } from 'fumadocs-core/server';
type TableOfContentsProperties = {
data: string;
};
export const TableOfContents = async ({
data,
}: TableOfContentsProperties) => {
const toc = await getTableOfContents(data);
return (
<ul className="flex list-none flex-col gap-2 text-sm">
{toc.map((item) => (
<li
key={item.url}
style={{
paddingLeft: `${item.depth - 2}rem`,
}}
>
<a
href={item.url}
className="line-clamp-3 flex rounded-sm text-foreground text-sm underline decoration-foreground/0 transition-colors hover:decoration-foreground/50"
>
{item.title}
</a>
</li>
))}
</ul>
);
};

View File

@@ -0,0 +1 @@
export const Toolbar = () => null;

23
packages/cms/index.ts Normal file
View File

@@ -0,0 +1,23 @@
import { allPosts, allLegals } from 'content-collections';
export const blog = {
postsQuery: null,
latestPostQuery: null,
postQuery: (slug: string) => null,
getPosts: async () => allPosts,
getLatestPost: async () =>
allPosts.sort((a, b) => a.date.getTime() - b.date.getTime()).at(0),
getPost: async (slug: string) =>
allPosts.find(({ _meta }) => _meta.path === slug),
};
export const legal = {
postsQuery: null,
latestPostQuery: null,
postQuery: (slug: string) => null,
getPosts: async () => allLegals,
getLatestPost: async () =>
allLegals.sort((a, b) => a.date.getTime() - b.date.getTime()).at(0),
getPost: async (slug: string) =>
allLegals.find(({ _meta }) => _meta.path === slug),
};

View File

@@ -0,0 +1 @@
export { withContentCollections as withCMS } from '@content-collections/next';

30
packages/cms/package.json Normal file
View File

@@ -0,0 +1,30 @@
{
"name": "@konobangu/cms",
"version": "0.0.0",
"private": true,
"scripts": {
"build": "content-collections build",
"analyze": "content-collections build",
"clean": "git clean -xdf .cache .turbo dist node_modules",
"typecheck": "tsc --noEmit --emitDeclarationOnly false"
},
"dependencies": {
"@content-collections/mdx": "^0.2.0",
"@content-collections/core": "^0.8.0",
"@content-collections/next": "^0.2.4",
"@konobangu/env": "workspace:*",
"acorn": "^8.14.0",
"fumadocs-core": "^14.6.0",
"react": "^19.0.0",
"reading-time": "^1.5.0",
"sqip": "1.0.0-alpha.51"
},
"devDependencies": {
"@content-collections/cli": "^0.1.6",
"@konobangu/typescript-config": "workspace:*",
"@types/node": "22.10.1",
"@types/react": "19.0.1",
"@types/react-dom": "^19.0.2",
"next": "^15.1.3"
}
}

View File

@@ -0,0 +1,8 @@
{
"extends": "@konobangu/typescript-config/nextjs.json",
"compilerOptions": {
"baseUrl": "."
},
"include": ["**/*.ts", "**/*.tsx", "../../apps/web/content-collections.ts"],
"exclude": ["node_modules"]
}

View File

@@ -0,0 +1,7 @@
{
"compilerOptions": {
"paths": {
"content-collections": ["./.content-collections/generated"]
}
}
}