create-noxion 0.1.1 → 0.3.0
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/README.md +2 -2
- package/dist/add.d.ts +13 -0
- package/dist/add.d.ts.map +1 -0
- package/dist/add.js +141 -0
- package/dist/add.js.map +1 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +195 -5
- package/dist/index.js.map +1 -1
- package/dist/scaffold.d.ts +9 -1
- package/dist/scaffold.d.ts.map +1 -1
- package/dist/scaffold.js +20 -3
- package/dist/scaffold.js.map +1 -1
- package/dist/templates/docs/.env +8 -0
- package/dist/templates/docs/.env.example +9 -0
- package/dist/templates/docs/app/[slug]/page.tsx +49 -0
- package/dist/templates/docs/app/api/notion-webhook/route.ts +12 -0
- package/dist/templates/docs/app/api/revalidate/route.ts +12 -0
- package/dist/templates/docs/app/globals.css +31 -0
- package/dist/templates/docs/app/layout.tsx +36 -0
- package/dist/templates/docs/app/not-found.tsx +38 -0
- package/dist/templates/docs/app/page.tsx +45 -0
- package/dist/templates/docs/app/robots.ts +7 -0
- package/dist/templates/docs/app/site-layout.tsx +28 -0
- package/dist/templates/docs/app/sitemap.ts +9 -0
- package/dist/templates/docs/app/tailwind.css +3 -0
- package/dist/templates/docs/app/theme-script.tsx +16 -0
- package/dist/templates/docs/lib/config.ts +24 -0
- package/dist/templates/docs/lib/notion.ts +48 -0
- package/dist/templates/docs/next.config.ts +26 -0
- package/dist/templates/docs/noxion.config.ts +23 -0
- package/dist/templates/docs/package.json +25 -0
- package/dist/templates/docs/tsconfig.json +19 -0
- package/dist/templates/full/.env +10 -0
- package/dist/templates/full/.env.example +11 -0
- package/dist/templates/full/app/[slug]/page.tsx +55 -0
- package/dist/templates/full/app/api/notion-webhook/route.ts +12 -0
- package/dist/templates/full/app/api/revalidate/route.ts +12 -0
- package/dist/templates/full/app/globals.css +31 -0
- package/dist/templates/full/app/home-content.tsx +59 -0
- package/dist/templates/full/app/layout.tsx +36 -0
- package/dist/templates/full/app/not-found.tsx +38 -0
- package/dist/templates/full/app/page.tsx +50 -0
- package/dist/templates/full/app/robots.ts +7 -0
- package/dist/templates/full/app/site-layout.tsx +32 -0
- package/dist/templates/full/app/sitemap.ts +9 -0
- package/dist/templates/full/app/tag/[tag]/page.tsx +66 -0
- package/dist/templates/full/app/tailwind.css +3 -0
- package/dist/templates/full/app/theme-script.tsx +16 -0
- package/dist/templates/full/lib/config.ts +24 -0
- package/dist/templates/full/lib/notion.ts +66 -0
- package/dist/templates/full/next.config.ts +26 -0
- package/dist/templates/full/noxion.config.ts +40 -0
- package/dist/templates/full/package.json +25 -0
- package/dist/templates/full/tsconfig.json +19 -0
- package/dist/templates/nextjs/.env.example +1 -0
- package/dist/templates/nextjs/app/api/notion-webhook/route.ts +12 -0
- package/dist/templates/nextjs/app/home-content.tsx +17 -26
- package/dist/templates/nextjs/app/layout.tsx +4 -3
- package/dist/templates/nextjs/app/page.tsx +3 -3
- package/dist/templates/nextjs/app/site-layout.tsx +28 -0
- package/dist/templates/nextjs/app/tag/[tag]/page.tsx +4 -4
- package/dist/templates/nextjs/app/tailwind.css +3 -0
- package/dist/templates/nextjs/lib/config.ts +1 -0
- package/dist/templates/nextjs/lib/notion.ts +5 -5
- package/dist/templates/nextjs/package.json +3 -3
- package/dist/templates/plugin/noxion-plugin.json +10 -0
- package/dist/templates/plugin/package.json +41 -0
- package/dist/templates/plugin/src/__tests__/plugin.test.ts +39 -0
- package/dist/templates/plugin/src/index.ts +20 -0
- package/dist/templates/plugin/tsconfig.json +20 -0
- package/dist/templates/portfolio/.env +8 -0
- package/dist/templates/portfolio/.env.example +9 -0
- package/dist/templates/portfolio/app/[slug]/page.tsx +48 -0
- package/dist/templates/portfolio/app/api/notion-webhook/route.ts +12 -0
- package/dist/templates/portfolio/app/api/revalidate/route.ts +12 -0
- package/dist/templates/portfolio/app/globals.css +31 -0
- package/dist/templates/portfolio/app/layout.tsx +36 -0
- package/dist/templates/portfolio/app/not-found.tsx +38 -0
- package/dist/templates/portfolio/app/page.tsx +70 -0
- package/dist/templates/portfolio/app/robots.ts +7 -0
- package/dist/templates/portfolio/app/site-layout.tsx +31 -0
- package/dist/templates/portfolio/app/sitemap.ts +9 -0
- package/dist/templates/portfolio/app/tailwind.css +3 -0
- package/dist/templates/portfolio/app/theme-script.tsx +16 -0
- package/dist/templates/portfolio/lib/config.ts +24 -0
- package/dist/templates/portfolio/lib/notion.ts +48 -0
- package/dist/templates/portfolio/next.config.ts +26 -0
- package/dist/templates/portfolio/noxion.config.ts +23 -0
- package/dist/templates/portfolio/package.json +25 -0
- package/dist/templates/portfolio/tsconfig.json +19 -0
- package/dist/templates/theme/package.json +42 -0
- package/dist/templates/theme/src/index.ts +41 -0
- package/dist/templates/theme/styles/theme.css +23 -0
- package/dist/templates/theme/tsconfig.json +20 -0
- package/package.json +1 -1
- package/dist/templates/nextjs/app/providers.tsx +0 -45
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { revalidatePath } from "next/cache";
|
|
2
|
+
import { createNotionWebhookHandler } from "@noxion/adapter-nextjs";
|
|
3
|
+
import { siteConfig } from "../../../lib/config";
|
|
4
|
+
|
|
5
|
+
const handler = createNotionWebhookHandler({
|
|
6
|
+
config: siteConfig,
|
|
7
|
+
revalidatePath,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export async function POST(request: Request) {
|
|
11
|
+
return handler(request);
|
|
12
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { revalidatePath } from "next/cache";
|
|
2
|
+
import { createRevalidateHandler } from "@noxion/adapter-nextjs";
|
|
3
|
+
import { siteConfig } from "../../../lib/config";
|
|
4
|
+
|
|
5
|
+
const handler = createRevalidateHandler({
|
|
6
|
+
config: siteConfig,
|
|
7
|
+
revalidatePath,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export async function POST(request: Request) {
|
|
11
|
+
return handler(request as never);
|
|
12
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
@import "@noxion/notion-renderer/styles";
|
|
2
|
+
|
|
3
|
+
*,
|
|
4
|
+
*::before,
|
|
5
|
+
*::after {
|
|
6
|
+
box-sizing: border-box;
|
|
7
|
+
margin: 0;
|
|
8
|
+
padding: 0;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
html {
|
|
12
|
+
font-family: var(--noxion-font-sans, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif);
|
|
13
|
+
-webkit-font-smoothing: antialiased;
|
|
14
|
+
-moz-osx-font-smoothing: grayscale;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
body {
|
|
18
|
+
background-color: var(--noxion-background, #fff);
|
|
19
|
+
color: var(--noxion-foreground, #0a0a0a);
|
|
20
|
+
min-height: 100vh;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
[data-theme="dark"] body {
|
|
24
|
+
background-color: var(--noxion-background, #0a0a0a);
|
|
25
|
+
color: var(--noxion-foreground, #fafafa);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
a {
|
|
29
|
+
color: inherit;
|
|
30
|
+
text-decoration: none;
|
|
31
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Metadata } from "next";
|
|
2
|
+
import { generateNoxionListMetadata, generateWebSiteLD } from "@noxion/adapter-nextjs";
|
|
3
|
+
import { siteConfig } from "../lib/config";
|
|
4
|
+
import { ThemeScript } from "./theme-script";
|
|
5
|
+
import { SiteLayout } from "./site-layout";
|
|
6
|
+
import "./tailwind.css";
|
|
7
|
+
import "./globals.css";
|
|
8
|
+
|
|
9
|
+
export function generateMetadata(): Metadata {
|
|
10
|
+
return generateNoxionListMetadata(siteConfig);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default function RootLayout({
|
|
14
|
+
children,
|
|
15
|
+
}: {
|
|
16
|
+
children: React.ReactNode;
|
|
17
|
+
}) {
|
|
18
|
+
const jsonLd = generateWebSiteLD(siteConfig);
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<html lang={siteConfig.language} suppressHydrationWarning>
|
|
22
|
+
<head>
|
|
23
|
+
<ThemeScript />
|
|
24
|
+
<script
|
|
25
|
+
type="application/ld+json"
|
|
26
|
+
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
|
|
27
|
+
/>
|
|
28
|
+
</head>
|
|
29
|
+
<body>
|
|
30
|
+
<SiteLayout siteName={siteConfig.name} author={siteConfig.author}>
|
|
31
|
+
{children}
|
|
32
|
+
</SiteLayout>
|
|
33
|
+
</body>
|
|
34
|
+
</html>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import Link from "next/link";
|
|
2
|
+
|
|
3
|
+
export default function NotFound() {
|
|
4
|
+
return (
|
|
5
|
+
<div
|
|
6
|
+
style={{
|
|
7
|
+
display: "flex",
|
|
8
|
+
flexDirection: "column",
|
|
9
|
+
alignItems: "center",
|
|
10
|
+
justifyContent: "center",
|
|
11
|
+
minHeight: "50vh",
|
|
12
|
+
textAlign: "center",
|
|
13
|
+
gap: "1rem",
|
|
14
|
+
}}
|
|
15
|
+
>
|
|
16
|
+
<h1 style={{ fontSize: "4rem", fontWeight: 700, color: "var(--noxion-mutedForeground, #737373)" }}>
|
|
17
|
+
404
|
|
18
|
+
</h1>
|
|
19
|
+
<p style={{ fontSize: "1.125rem", color: "var(--noxion-mutedForeground, #737373)" }}>
|
|
20
|
+
This page could not be found.
|
|
21
|
+
</p>
|
|
22
|
+
<Link
|
|
23
|
+
href="/"
|
|
24
|
+
style={{
|
|
25
|
+
marginTop: "1rem",
|
|
26
|
+
padding: "0.5rem 1.5rem",
|
|
27
|
+
borderRadius: "var(--noxion-border-radius, 0.5rem)",
|
|
28
|
+
backgroundColor: "var(--noxion-primary, #2563eb)",
|
|
29
|
+
color: "var(--noxion-primaryForeground, #fff)",
|
|
30
|
+
fontSize: "0.875rem",
|
|
31
|
+
fontWeight: 500,
|
|
32
|
+
}}
|
|
33
|
+
>
|
|
34
|
+
Go Home
|
|
35
|
+
</Link>
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import Link from "next/link";
|
|
2
|
+
import { getAllPages } from "../lib/notion";
|
|
3
|
+
|
|
4
|
+
export const revalidate = 3600;
|
|
5
|
+
|
|
6
|
+
export default async function HomePage() {
|
|
7
|
+
const pages = await getAllPages();
|
|
8
|
+
|
|
9
|
+
const sections = new Map<string, typeof pages>();
|
|
10
|
+
for (const page of pages) {
|
|
11
|
+
const section = (page.metadata.section as string) ?? "General";
|
|
12
|
+
if (!sections.has(section)) sections.set(section, []);
|
|
13
|
+
sections.get(section)!.push(page);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div>
|
|
18
|
+
<h1 style={{ fontSize: "1.5rem", fontWeight: 700, marginBottom: "2rem" }}>
|
|
19
|
+
Documentation
|
|
20
|
+
</h1>
|
|
21
|
+
{[...sections.entries()].map(([section, sectionPages]) => (
|
|
22
|
+
<div key={section} style={{ marginBottom: "2rem" }}>
|
|
23
|
+
<h2 style={{ fontSize: "1.125rem", fontWeight: 600, marginBottom: "0.75rem" }}>
|
|
24
|
+
{section}
|
|
25
|
+
</h2>
|
|
26
|
+
<ul style={{ listStyle: "none", display: "flex", flexDirection: "column", gap: "0.5rem" }}>
|
|
27
|
+
{sectionPages.map((page) => (
|
|
28
|
+
<li key={page.id}>
|
|
29
|
+
<Link
|
|
30
|
+
href={`/${page.slug}`}
|
|
31
|
+
style={{
|
|
32
|
+
color: "var(--noxion-primary, #2563eb)",
|
|
33
|
+
fontSize: "0.95rem",
|
|
34
|
+
}}
|
|
35
|
+
>
|
|
36
|
+
{page.title}
|
|
37
|
+
</Link>
|
|
38
|
+
</li>
|
|
39
|
+
))}
|
|
40
|
+
</ul>
|
|
41
|
+
</div>
|
|
42
|
+
))}
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { DocsLayout, Header, Footer } from "@noxion/theme-default";
|
|
4
|
+
|
|
5
|
+
interface SiteLayoutProps {
|
|
6
|
+
siteName: string;
|
|
7
|
+
author: string;
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function SiteLayout({ siteName, author, children }: SiteLayoutProps) {
|
|
12
|
+
const SiteHeader = () => (
|
|
13
|
+
<Header
|
|
14
|
+
siteName={siteName}
|
|
15
|
+
navigation={[{ label: "Home", href: "/" }]}
|
|
16
|
+
/>
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
const SiteFooter = () => (
|
|
20
|
+
<Footer siteName={siteName} author={author} />
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<DocsLayout slots={{ header: SiteHeader, footer: SiteFooter }}>
|
|
25
|
+
{children}
|
|
26
|
+
</DocsLayout>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { MetadataRoute } from "next";
|
|
2
|
+
import { generateNoxionSitemap } from "@noxion/adapter-nextjs";
|
|
3
|
+
import { getAllPosts } from "../lib/notion";
|
|
4
|
+
import { siteConfig } from "../lib/config";
|
|
5
|
+
|
|
6
|
+
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
|
7
|
+
const posts = await getAllPosts();
|
|
8
|
+
return generateNoxionSitemap(posts, siteConfig);
|
|
9
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function ThemeScript() {
|
|
2
|
+
const script = `
|
|
3
|
+
(function() {
|
|
4
|
+
try {
|
|
5
|
+
var stored = localStorage.getItem('noxion-theme');
|
|
6
|
+
var theme = stored || 'system';
|
|
7
|
+
if (theme === 'system') {
|
|
8
|
+
theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
9
|
+
}
|
|
10
|
+
document.documentElement.dataset.theme = theme;
|
|
11
|
+
} catch (e) {}
|
|
12
|
+
})();
|
|
13
|
+
`;
|
|
14
|
+
|
|
15
|
+
return <script dangerouslySetInnerHTML={{ __html: script }} />;
|
|
16
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { loadConfig } from "@noxion/core";
|
|
2
|
+
import type { NoxionConfig } from "@noxion/core";
|
|
3
|
+
import noxionConfigInput from "../noxion.config";
|
|
4
|
+
|
|
5
|
+
function createConfig(): NoxionConfig {
|
|
6
|
+
try {
|
|
7
|
+
return loadConfig(noxionConfigInput);
|
|
8
|
+
} catch {
|
|
9
|
+
return {
|
|
10
|
+
name: noxionConfigInput.name ?? "{{SITE_NAME}}",
|
|
11
|
+
domain: noxionConfigInput.domain ?? "{{DOMAIN}}",
|
|
12
|
+
author: noxionConfigInput.author ?? "{{AUTHOR}}",
|
|
13
|
+
description: noxionConfigInput.description ?? "{{SITE_DESCRIPTION}}",
|
|
14
|
+
language: noxionConfigInput.language ?? "en",
|
|
15
|
+
defaultTheme: noxionConfigInput.defaultTheme ?? "system",
|
|
16
|
+
defaultPageType: "docs",
|
|
17
|
+
revalidate: noxionConfigInput.revalidate ?? 3600,
|
|
18
|
+
plugins: noxionConfigInput.plugins,
|
|
19
|
+
collections: noxionConfigInput.collections,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const siteConfig = createConfig();
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { createNotionClient, fetchCollection, fetchPage, downloadImages, mapImages } from "@noxion/core";
|
|
2
|
+
import type { NoxionPage, ExtendedRecordMap } from "@noxion/core";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { siteConfig } from "./config";
|
|
5
|
+
|
|
6
|
+
const notion = createNotionClient({
|
|
7
|
+
authToken: process.env.NOTION_TOKEN || undefined,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export async function getAllPages(): Promise<NoxionPage[]> {
|
|
11
|
+
const collections = siteConfig.collections ?? [];
|
|
12
|
+
if (collections.length === 0) return [];
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const results = await Promise.all(
|
|
16
|
+
collections.map((col) => fetchCollection(notion, col))
|
|
17
|
+
);
|
|
18
|
+
return results.flat();
|
|
19
|
+
} catch (error) {
|
|
20
|
+
console.error("Failed to fetch pages:", error);
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function getPageBySlug(slug: string): Promise<NoxionPage | undefined> {
|
|
26
|
+
const pages = await getAllPages();
|
|
27
|
+
return pages.find((p) => p.slug === slug);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function getPageRecordMap(pageId: string): Promise<ExtendedRecordMap> {
|
|
31
|
+
const recordMap = await fetchPage(notion, pageId);
|
|
32
|
+
|
|
33
|
+
if (process.env.NODE_ENV === "production") {
|
|
34
|
+
try {
|
|
35
|
+
const outputDir = join(process.cwd(), "public");
|
|
36
|
+
const urlMap = await downloadImages(recordMap, outputDir, { concurrency: 5 });
|
|
37
|
+
const localUrlMap: Record<string, string> = {};
|
|
38
|
+
for (const [originalUrl, localPath] of Object.entries(urlMap)) {
|
|
39
|
+
localUrlMap[originalUrl] = `/images/${localPath.split("/images/").pop()}`;
|
|
40
|
+
}
|
|
41
|
+
return mapImages(recordMap, localUrlMap);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.error("Image download failed, using original URLs:", error);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return recordMap;
|
|
48
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { NextConfig } from "next";
|
|
2
|
+
|
|
3
|
+
const nextConfig: NextConfig = {
|
|
4
|
+
output: "standalone",
|
|
5
|
+
transpilePackages: [
|
|
6
|
+
"@noxion/core",
|
|
7
|
+
"@noxion/renderer",
|
|
8
|
+
"@noxion/adapter-nextjs",
|
|
9
|
+
"@noxion/notion-renderer",
|
|
10
|
+
"notion-client",
|
|
11
|
+
"notion-types",
|
|
12
|
+
"notion-utils",
|
|
13
|
+
],
|
|
14
|
+
images: {
|
|
15
|
+
remotePatterns: [
|
|
16
|
+
{ protocol: "https", hostname: "www.notion.so" },
|
|
17
|
+
{ protocol: "https", hostname: "notion.so" },
|
|
18
|
+
{ protocol: "https", hostname: "images.unsplash.com" },
|
|
19
|
+
{ protocol: "https", hostname: "s3.us-west-2.amazonaws.com" },
|
|
20
|
+
{ protocol: "https", hostname: "prod-files-secure.s3.us-west-2.amazonaws.com" },
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
staticPageGenerationTimeout: 300,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default nextConfig;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { defineConfig } from "@noxion/core";
|
|
2
|
+
|
|
3
|
+
const config = defineConfig({
|
|
4
|
+
name: process.env.SITE_NAME ?? "{{SITE_NAME}}",
|
|
5
|
+
domain: process.env.SITE_DOMAIN ?? "{{DOMAIN}}",
|
|
6
|
+
author: process.env.SITE_AUTHOR ?? "{{AUTHOR}}",
|
|
7
|
+
description: process.env.SITE_DESCRIPTION ?? "{{SITE_DESCRIPTION}}",
|
|
8
|
+
language: "en",
|
|
9
|
+
defaultTheme: "system",
|
|
10
|
+
defaultPageType: "docs",
|
|
11
|
+
revalidate: 3600,
|
|
12
|
+
revalidateSecret: process.env.REVALIDATE_SECRET,
|
|
13
|
+
collections: [
|
|
14
|
+
{
|
|
15
|
+
name: "docs",
|
|
16
|
+
databaseId: process.env.NOTION_PAGE_ID!,
|
|
17
|
+
pageType: "docs",
|
|
18
|
+
pathPrefix: "/docs",
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export default config;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{PROJECT_NAME}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "next dev",
|
|
7
|
+
"build": "next build",
|
|
8
|
+
"start": "next start"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@noxion/core": "^0.3.0",
|
|
12
|
+
"@noxion/renderer": "^0.3.0",
|
|
13
|
+
"@noxion/adapter-nextjs": "^0.3.0",
|
|
14
|
+
"next": "^16.1.6",
|
|
15
|
+
"react": "^19.1.0",
|
|
16
|
+
"react-dom": "^19.1.0",
|
|
17
|
+
"notion-client": "^7.8.2"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/node": "^22.0.0",
|
|
21
|
+
"@types/react": "^19.2.14",
|
|
22
|
+
"@types/react-dom": "^19.2.3",
|
|
23
|
+
"typescript": "^5.7.0"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
7
|
+
"jsx": "react-jsx",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"plugins": [{ "name": "next" }],
|
|
15
|
+
"outDir": "./dist"
|
|
16
|
+
},
|
|
17
|
+
"include": ["**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
18
|
+
"exclude": ["node_modules", ".next", "out", "dist"]
|
|
19
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
NOTION_PAGE_ID={{NOTION_PAGE_ID}}
|
|
2
|
+
DOCS_NOTION_ID={{DOCS_NOTION_ID}}
|
|
3
|
+
PORTFOLIO_NOTION_ID={{PORTFOLIO_NOTION_ID}}
|
|
4
|
+
NOTION_TOKEN=
|
|
5
|
+
NOTION_SPACE_ID=
|
|
6
|
+
SITE_NAME={{SITE_NAME}}
|
|
7
|
+
SITE_DOMAIN={{DOMAIN}}
|
|
8
|
+
SITE_AUTHOR={{AUTHOR}}
|
|
9
|
+
SITE_DESCRIPTION={{SITE_DESCRIPTION}}
|
|
10
|
+
REVALIDATE_SECRET=
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
NOTION_PAGE_ID={{NOTION_PAGE_ID}}
|
|
2
|
+
DOCS_NOTION_ID={{DOCS_NOTION_ID}}
|
|
3
|
+
PORTFOLIO_NOTION_ID={{PORTFOLIO_NOTION_ID}}
|
|
4
|
+
NOTION_TOKEN=
|
|
5
|
+
NOTION_SPACE_ID=
|
|
6
|
+
SITE_NAME={{SITE_NAME}}
|
|
7
|
+
SITE_DOMAIN={{DOMAIN}}
|
|
8
|
+
SITE_AUTHOR={{AUTHOR}}
|
|
9
|
+
SITE_DESCRIPTION={{SITE_DESCRIPTION}}
|
|
10
|
+
REVALIDATE_SECRET=
|
|
11
|
+
NOTION_WEBHOOK_SECRET=
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { notFound } from "next/navigation";
|
|
2
|
+
import type { Metadata } from "next";
|
|
3
|
+
import { NotionPage } from "@noxion/renderer";
|
|
4
|
+
import { generateNoxionMetadata, generateBlogPostingLD } from "@noxion/adapter-nextjs";
|
|
5
|
+
import { getPageBySlug, getPageRecordMap, getAllPages } from "../../lib/notion";
|
|
6
|
+
import { siteConfig } from "../../lib/config";
|
|
7
|
+
|
|
8
|
+
export const revalidate = 3600;
|
|
9
|
+
|
|
10
|
+
export async function generateStaticParams() {
|
|
11
|
+
const pages = await getAllPages();
|
|
12
|
+
return pages.map((p) => ({ slug: p.slug }));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function generateMetadata({
|
|
16
|
+
params,
|
|
17
|
+
}: {
|
|
18
|
+
params: Promise<{ slug: string }>;
|
|
19
|
+
}): Promise<Metadata> {
|
|
20
|
+
const { slug } = await params;
|
|
21
|
+
const page = await getPageBySlug(slug);
|
|
22
|
+
if (!page) return { title: "Not Found" };
|
|
23
|
+
return generateNoxionMetadata(page, siteConfig);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default async function PageDetail({
|
|
27
|
+
params,
|
|
28
|
+
}: {
|
|
29
|
+
params: Promise<{ slug: string }>;
|
|
30
|
+
}) {
|
|
31
|
+
const { slug } = await params;
|
|
32
|
+
const page = await getPageBySlug(slug);
|
|
33
|
+
if (!page) notFound();
|
|
34
|
+
|
|
35
|
+
const recordMap = await getPageRecordMap(page.id);
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<article>
|
|
39
|
+
{page.pageType === "blog" && (
|
|
40
|
+
<script
|
|
41
|
+
type="application/ld+json"
|
|
42
|
+
dangerouslySetInnerHTML={{ __html: JSON.stringify(generateBlogPostingLD(page, siteConfig)) }}
|
|
43
|
+
/>
|
|
44
|
+
)}
|
|
45
|
+
<NotionPage
|
|
46
|
+
recordMap={recordMap}
|
|
47
|
+
rootPageId={page.id}
|
|
48
|
+
fullPage
|
|
49
|
+
previewImages
|
|
50
|
+
showTableOfContents={page.pageType !== "portfolio"}
|
|
51
|
+
mapPageUrl={(pageId: string) => `/${pageId}`}
|
|
52
|
+
/>
|
|
53
|
+
</article>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { revalidatePath } from "next/cache";
|
|
2
|
+
import { createNotionWebhookHandler } from "@noxion/adapter-nextjs";
|
|
3
|
+
import { siteConfig } from "../../../lib/config";
|
|
4
|
+
|
|
5
|
+
const handler = createNotionWebhookHandler({
|
|
6
|
+
config: siteConfig,
|
|
7
|
+
revalidatePath,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export async function POST(request: Request) {
|
|
11
|
+
return handler(request);
|
|
12
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { revalidatePath } from "next/cache";
|
|
2
|
+
import { createRevalidateHandler } from "@noxion/adapter-nextjs";
|
|
3
|
+
import { siteConfig } from "../../../lib/config";
|
|
4
|
+
|
|
5
|
+
const handler = createRevalidateHandler({
|
|
6
|
+
config: siteConfig,
|
|
7
|
+
revalidatePath,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export async function POST(request: Request) {
|
|
11
|
+
return handler(request as never);
|
|
12
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
@import "@noxion/notion-renderer/styles";
|
|
2
|
+
|
|
3
|
+
*,
|
|
4
|
+
*::before,
|
|
5
|
+
*::after {
|
|
6
|
+
box-sizing: border-box;
|
|
7
|
+
margin: 0;
|
|
8
|
+
padding: 0;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
html {
|
|
12
|
+
font-family: var(--noxion-font-sans, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif);
|
|
13
|
+
-webkit-font-smoothing: antialiased;
|
|
14
|
+
-moz-osx-font-smoothing: grayscale;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
body {
|
|
18
|
+
background-color: var(--noxion-background, #fff);
|
|
19
|
+
color: var(--noxion-foreground, #0a0a0a);
|
|
20
|
+
min-height: 100vh;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
[data-theme="dark"] body {
|
|
24
|
+
background-color: var(--noxion-background, #0a0a0a);
|
|
25
|
+
color: var(--noxion-foreground, #fafafa);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
a {
|
|
29
|
+
color: inherit;
|
|
30
|
+
text-decoration: none;
|
|
31
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { PostList, TagFilter, Search, ThemeToggle } from "@noxion/theme-default";
|
|
4
|
+
import { useSearch } from "@noxion/renderer";
|
|
5
|
+
import type { PostCardProps } from "@noxion/renderer";
|
|
6
|
+
import { useState, useCallback } from "react";
|
|
7
|
+
|
|
8
|
+
interface HomeContentProps {
|
|
9
|
+
posts: PostCardProps[];
|
|
10
|
+
allTags: string[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function HomeContent({ posts, allTags }: HomeContentProps) {
|
|
14
|
+
const [selectedTags, setSelectedTags] = useState<string[]>([]);
|
|
15
|
+
|
|
16
|
+
const tagFiltered = selectedTags.length > 0
|
|
17
|
+
? posts.filter((p) => p.tags.some((t) => selectedTags.includes(t)))
|
|
18
|
+
: posts;
|
|
19
|
+
|
|
20
|
+
const { setQuery, filtered } = useSearch(tagFiltered);
|
|
21
|
+
|
|
22
|
+
const handleToggleTag = useCallback((tag: string) => {
|
|
23
|
+
setSelectedTags((prev) =>
|
|
24
|
+
prev.includes(tag) ? prev.filter((t) => t !== tag) : [...prev, tag]
|
|
25
|
+
);
|
|
26
|
+
}, []);
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div>
|
|
30
|
+
<div
|
|
31
|
+
style={{
|
|
32
|
+
display: "flex",
|
|
33
|
+
alignItems: "center",
|
|
34
|
+
justifyContent: "space-between",
|
|
35
|
+
marginBottom: "1.5rem",
|
|
36
|
+
}}
|
|
37
|
+
>
|
|
38
|
+
<h1 style={{ fontSize: "1.5rem", fontWeight: 700 }}>Posts</h1>
|
|
39
|
+
<ThemeToggle />
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<div style={{ marginBottom: "1.5rem" }}>
|
|
43
|
+
<Search onSearch={setQuery} placeholder="Search posts..." />
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
{allTags.length > 0 && (
|
|
47
|
+
<div style={{ marginBottom: "2rem" }}>
|
|
48
|
+
<TagFilter
|
|
49
|
+
tags={allTags}
|
|
50
|
+
selectedTags={selectedTags}
|
|
51
|
+
onToggle={handleToggleTag}
|
|
52
|
+
/>
|
|
53
|
+
</div>
|
|
54
|
+
)}
|
|
55
|
+
|
|
56
|
+
<PostList posts={filtered} />
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Metadata } from "next";
|
|
2
|
+
import { generateNoxionListMetadata, generateWebSiteLD } from "@noxion/adapter-nextjs";
|
|
3
|
+
import { siteConfig } from "../lib/config";
|
|
4
|
+
import { ThemeScript } from "./theme-script";
|
|
5
|
+
import { SiteLayout } from "./site-layout";
|
|
6
|
+
import "./tailwind.css";
|
|
7
|
+
import "./globals.css";
|
|
8
|
+
|
|
9
|
+
export function generateMetadata(): Metadata {
|
|
10
|
+
return generateNoxionListMetadata(siteConfig);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default function RootLayout({
|
|
14
|
+
children,
|
|
15
|
+
}: {
|
|
16
|
+
children: React.ReactNode;
|
|
17
|
+
}) {
|
|
18
|
+
const jsonLd = generateWebSiteLD(siteConfig);
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<html lang={siteConfig.language} suppressHydrationWarning>
|
|
22
|
+
<head>
|
|
23
|
+
<ThemeScript />
|
|
24
|
+
<script
|
|
25
|
+
type="application/ld+json"
|
|
26
|
+
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
|
|
27
|
+
/>
|
|
28
|
+
</head>
|
|
29
|
+
<body>
|
|
30
|
+
<SiteLayout siteName={siteConfig.name} author={siteConfig.author}>
|
|
31
|
+
{children}
|
|
32
|
+
</SiteLayout>
|
|
33
|
+
</body>
|
|
34
|
+
</html>
|
|
35
|
+
);
|
|
36
|
+
}
|