create-opendocs 0.1.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.
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "__PROJECT_NAME__",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "next dev",
7
+ "build": "next build",
8
+ "start": "next start",
9
+ "lint": "next lint"
10
+ },
11
+ "dependencies": {
12
+ "@tailwindcss/typography": "^0.5.19",
13
+ "clsx": "^2.1.1",
14
+ "gray-matter": "^4.0.3",
15
+ "lucide-react": "^0.575.0",
16
+ "next": "14.2.35",
17
+ "next-mdx-remote": "^6.0.0",
18
+ "react": "^18",
19
+ "react-dom": "^18"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^20",
23
+ "@types/react": "^18",
24
+ "@types/react-dom": "^18",
25
+ "eslint": "^8",
26
+ "eslint-config-next": "14.2.35",
27
+ "postcss": "^8",
28
+ "tailwindcss": "^3.4.1",
29
+ "typescript": "^5"
30
+ }
31
+ }
@@ -0,0 +1,8 @@
1
+ /** @type {import('postcss-load-config').Config} */
2
+ const config = {
3
+ plugins: {
4
+ tailwindcss: {},
5
+ },
6
+ };
7
+
8
+ export default config;
@@ -0,0 +1,198 @@
1
+ import { getDocBySlug, getAllDocsSlugs, getConfig, getFlatDocLinks } from "@/lib/docs";
2
+ import { notFound } from "next/navigation";
3
+ import { MDXRemote } from "next-mdx-remote/rsc";
4
+ import { Metadata } from "next";
5
+ import { ChevronRight, Home, ArrowRight } from "lucide-react";
6
+ import Link from "next/link";
7
+ import React from "react";
8
+
9
+ export async function generateStaticParams() {
10
+ const slugs = getAllDocsSlugs();
11
+ return slugs.map((slug) => ({
12
+ slug: slug.length > 0 ? slug : undefined,
13
+ }));
14
+ }
15
+
16
+ export async function generateMetadata({ params }: { params: { slug?: string[] } }): Promise<Metadata> {
17
+ const doc = getDocBySlug(params.slug);
18
+ if (!doc) {
19
+ return {
20
+ title: params.slug && params.slug.length > 0 ? "Not Found" : "Setup Required",
21
+ description: "__SITE_DESCRIPTION__"
22
+ };
23
+ }
24
+ return {
25
+ title: `${doc.title} - __SITE_NAME__`,
26
+ description: `Read documentation for ${doc.title}.`,
27
+ };
28
+ }
29
+
30
+ export default async function DocPage({ params }: { params: { slug?: string[] } }) {
31
+ const doc = getDocBySlug(params.slug);
32
+ const config = getConfig();
33
+
34
+ const isRoot = !params.slug || params.slug.length === 0;
35
+
36
+ if (!doc) {
37
+ if (isRoot) {
38
+ return <SetupGuide />;
39
+ }
40
+ notFound();
41
+ }
42
+
43
+ const currSlugStr = isRoot ? '/' : '/' + (params.slug || []).join('/');
44
+
45
+ const flatPages = getFlatDocLinks();
46
+ const currentIndex = flatPages.findIndex(p => p.slug === currSlugStr);
47
+ const nextDoc = currentIndex !== -1 && currentIndex < flatPages.length - 1 ? flatPages[currentIndex + 1] : null;
48
+
49
+ // Premium Custom MDX Styling setup matching Fixoria's brand
50
+ const components = {
51
+ h1: (props: React.HTMLAttributes<HTMLHeadingElement>) => <h1 className="text-4xl md:text-5xl font-extrabold tracking-tight bg-clip-text text-transparent bg-gradient-to-r from-white to-zinc-400 mb-8 pb-4 border-b border-zinc-800/60" {...props} />,
52
+ h2: (props: React.HTMLAttributes<HTMLHeadingElement>) => <h2 className="text-2xl md:text-3xl font-semibold tracking-tight mt-12 mb-6 pb-2 border-b border-zinc-800/40 text-zinc-100" {...props} />,
53
+ h3: (props: React.HTMLAttributes<HTMLHeadingElement>) => <h3 className="text-xl font-medium tracking-tight mt-8 mb-4 text-zinc-200" {...props} />,
54
+ p: (props: React.HTMLAttributes<HTMLParagraphElement>) => <p className="leading-7 text-zinc-400 mb-6" {...props} />,
55
+ ul: (props: React.HTMLAttributes<HTMLUListElement>) => <ul className="list-disc leading-7 text-zinc-400 mb-6 pl-6 space-y-2 marker:text-zinc-600" {...props} />,
56
+ ol: (props: React.HTMLAttributes<HTMLOListElement>) => <ol className="list-decimal leading-7 text-zinc-400 mb-6 pl-6 space-y-2 marker:text-zinc-600" {...props} />,
57
+ li: (props: React.HTMLAttributes<HTMLLIElement>) => <li className="pl-1" {...props} />,
58
+ a: (props: React.AnchorHTMLAttributes<HTMLAnchorElement>) => <a className="text-white font-medium hover:text-blue-400 underline decoration-zinc-700 hover:decoration-blue-400/50 transition-all underline-offset-4" {...props} />,
59
+ blockquote: (props: React.QuoteHTMLAttributes<HTMLQuoteElement>) => <blockquote className="border-l-4 border-white/20 pl-6 italic text-zinc-300 my-8 bg-zinc-900/40 py-4 pr-6 rounded-r-2xl shadow-sm" {...props} />,
60
+ code: (props: React.HTMLAttributes<HTMLElement>) => <code className="bg-zinc-900 border border-zinc-800/80 rounded-md px-1.5 py-0.5 font-mono text-[0.85em] text-zinc-200 shadow-sm" {...props} />,
61
+ pre: (props: React.HTMLAttributes<HTMLPreElement>) => <pre className="bg-[#09090b] border border-zinc-800/80 rounded-2xl p-5 overflow-x-auto my-8 shadow-2xl custom-scrollbar" {...props} />,
62
+ table: (props: React.TableHTMLAttributes<HTMLTableElement>) => <div className="overflow-x-auto my-8 border border-zinc-800/80 rounded-2xl shadow-sm"><table className="w-full text-left text-sm" {...props} /></div>,
63
+ th: (props: React.ThHTMLAttributes<HTMLTableCellElement>) => <th className="border-b border-zinc-800 bg-zinc-900/60 p-4 font-medium text-zinc-200 text-sm tracking-wide" {...props} />,
64
+ td: (props: React.TdHTMLAttributes<HTMLTableCellElement>) => <td className="p-4 border-b border-zinc-800/40 text-zinc-400" {...props} />,
65
+ hr: (props: React.HTMLAttributes<HTMLHRElement>) => <hr className="my-10 border-zinc-800" {...props} />,
66
+ };
67
+
68
+ const breadcrumbs = doc.slug && doc.slug.length > 0 ? doc.slug : [];
69
+
70
+ return (
71
+ <article className="max-w-[750px] w-full animate-in fade-in slide-in-from-bottom-4 duration-700 ease-out">
72
+
73
+ {/* Breadcrumb Navigation */}
74
+ {breadcrumbs.length > 0 && (
75
+ <nav className="flex flex-wrap items-center gap-2 text-sm text-zinc-500 font-medium mb-12">
76
+ <Link href="/" className="hover:text-white transition-colors flex items-center gap-1.5">
77
+ <Home className="w-3.5 h-3.5" />
78
+ </Link>
79
+ {breadcrumbs.map((crumb, idx) => (
80
+ <div key={idx} className="flex items-center gap-2">
81
+ <ChevronRight className="w-3.5 h-3.5 text-zinc-700" />
82
+ <span className={idx === breadcrumbs.length - 1 ? 'text-zinc-200' : 'text-zinc-500 capitalize'}>
83
+ {crumb.replace(/-/g, ' ')}
84
+ </span>
85
+ </div>
86
+ ))}
87
+ </nav>
88
+ )}
89
+
90
+ {/* Main Content Render */}
91
+ <div className="prose-custom max-w-none text-base">
92
+ <MDXRemote source={doc.content} components={components} />
93
+ </div>
94
+
95
+ {/* Up Next Section */}
96
+ {nextDoc && (
97
+ <div className="mt-16">
98
+ <p className="text-sm font-medium text-zinc-500 mb-4 uppercase tracking-wider">Up next</p>
99
+ <Link
100
+ href={nextDoc.slug}
101
+ className="group relative flex flex-col p-6 rounded-2xl border border-zinc-800/60 bg-zinc-900/20 hover:bg-zinc-800/40 transition-all overflow-hidden"
102
+ >
103
+ {/* Subtle gradient background on hover */}
104
+ <div className="absolute inset-0 bg-gradient-to-br from-zinc-800/0 via-zinc-800/0 to-zinc-800/20 opacity-0 group-hover:opacity-100 transition-opacity" />
105
+
106
+ <div className="relative flex items-center justify-between">
107
+ <div>
108
+ <h4 className="text-lg font-semibold text-zinc-200 group-hover:text-white transition-colors mb-1">
109
+ {nextDoc.title}
110
+ </h4>
111
+ {nextDoc.categoryTitle && (
112
+ <p className="text-sm text-zinc-500 group-hover:text-zinc-400 transition-colors">
113
+ {nextDoc.categoryTitle}
114
+ </p>
115
+ )}
116
+ </div>
117
+ <div className="w-10 h-10 rounded-full bg-zinc-800/50 flex items-center justify-center group-hover:bg-white group-hover:text-black transition-colors">
118
+ <ArrowRight className="w-5 h-5 text-zinc-400 group-hover:text-black transition-colors" />
119
+ </div>
120
+ </div>
121
+ </Link>
122
+ </div>
123
+ )}
124
+
125
+ {/* Bottom Engagement Footer */}
126
+ {config.contactSupport && (config.contactSupport.title || config.contactSupport.description || config.contactSupport.buttonText) && (
127
+ <div className="mt-20 pt-10 border-t border-zinc-800/60 flex flex-col md:flex-row justify-between items-start md:items-center gap-6 text-sm text-zinc-500 bg-zinc-900/10 rounded-2xl p-6">
128
+ <div>
129
+ {config.contactSupport.title && <p className="text-zinc-300 font-medium mb-1">{config.contactSupport.title}</p>}
130
+ {config.contactSupport.description && <p>{config.contactSupport.description}</p>}
131
+ </div>
132
+ {config.contactSupport.buttonText && config.contactSupport.buttonLink && (
133
+ <a href={config.contactSupport.buttonLink} target="_blank" rel="noopener noreferrer" className="flex items-center gap-2 px-4 py-2 bg-white text-black font-semibold rounded-lg hover:bg-zinc-200 transition-colors shadow-lg shadow-white/10 group">
134
+ {config.contactSupport.buttonText}
135
+ <ArrowRight className="w-4 h-4 group-hover:translate-x-0.5 transition-transform" />
136
+ </a>
137
+ )}
138
+ </div>
139
+ )}
140
+ </article>
141
+ );
142
+ }
143
+
144
+ function SetupGuide() {
145
+ return (
146
+ <div className="max-w-[750px] w-full animate-in fade-in slide-in-from-bottom-4 duration-700 ease-out py-12">
147
+ <div className="p-8 rounded-2xl border border-zinc-800 bg-zinc-900/20 backdrop-blur-sm shadow-2xl">
148
+ <div className="w-12 h-12 rounded-xl bg-white flex items-center justify-center mb-8 shadow-lg shadow-white/5">
149
+ <Home className="w-6 h-6 text-black" />
150
+ </div>
151
+
152
+ <h1 className="text-4xl font-extrabold tracking-tight text-white mb-4">
153
+ Setup Your Homepage
154
+ </h1>
155
+
156
+ <p className="text-zinc-400 text-lg mb-8 leading-relaxed">
157
+ It looks like you haven&apos;t configured a homepage yet. Follow these simple steps to get your landing page live.
158
+ </p>
159
+
160
+ <div className="space-y-6">
161
+ <div className="flex gap-4 group">
162
+ <div className="flex-shrink-0 w-8 h-8 rounded-full bg-zinc-800 flex items-center justify-center text-sm font-bold text-zinc-300 group-hover:bg-zinc-700 transition-colors">1</div>
163
+ <div>
164
+ <h3 className="text-zinc-100 font-semibold mb-1">Create your content</h3>
165
+ <p className="text-zinc-500 text-sm">Create a <code className="text-zinc-300">index.md</code> or <code className="text-zinc-300">home.md</code> file in your <code className="text-zinc-300">content/</code> directory.</p>
166
+ </div>
167
+ </div>
168
+
169
+ <div className="flex gap-4 group">
170
+ <div className="flex-shrink-0 w-8 h-8 rounded-full bg-zinc-800 flex items-center justify-center text-sm font-bold text-zinc-300 group-hover:bg-zinc-700 transition-colors">2</div>
171
+ <div>
172
+ <h3 className="text-zinc-100 font-semibold mb-1">Update your config</h3>
173
+ <p className="text-zinc-500 text-sm">In <code className="text-zinc-300">content/config.json</code>, set the <code className="text-zinc-300">&quot;homepage&quot;</code> property to point to your new file.</p>
174
+ </div>
175
+ </div>
176
+
177
+ <div className="flex gap-4 group">
178
+ <div className="flex-shrink-0 w-8 h-8 rounded-full bg-zinc-800 flex items-center justify-center text-sm font-bold text-zinc-300 group-hover:bg-zinc-700 transition-colors">3</div>
179
+ <div>
180
+ <h3 className="text-zinc-100 font-semibold mb-1">Refresh</h3>
181
+ <p className="text-zinc-500 text-sm">Once saved, your homepage will instantly appear here with the site styling applied.</p>
182
+ </div>
183
+ </div>
184
+ </div>
185
+
186
+ <div className="mt-12 p-5 rounded-xl bg-zinc-500/5 border border-zinc-800/50">
187
+ <p className="text-xs text-zinc-500 font-medium uppercase tracking-widest mb-3">Example config.json</p>
188
+ <pre className="text-zinc-300 font-mono text-xs overflow-x-auto">
189
+ {`{
190
+ "homepage": "index.md",
191
+ "sidebar": [ ... ]
192
+ }`}
193
+ </pre>
194
+ </div>
195
+ </div>
196
+ </div>
197
+ );
198
+ }
Binary file
@@ -0,0 +1,92 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ :root {
6
+ --background: #000000;
7
+ --foreground: #ffffff;
8
+ --border: #27272a;
9
+
10
+ --brand: #ffffff;
11
+ --brand-foreground: #000000;
12
+
13
+ --sidebar: #09090b;
14
+
15
+ --content-muted: #a1a1aa;
16
+ }
17
+
18
+ body {
19
+ color: var(--foreground);
20
+ background: var(--background);
21
+ font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
22
+ -webkit-font-smoothing: antialiased;
23
+ }
24
+
25
+ /* Custom scrollbar for webkit */
26
+ ::-webkit-scrollbar {
27
+ width: 8px;
28
+ height: 8px;
29
+ }
30
+
31
+ ::-webkit-scrollbar-track {
32
+ background: transparent;
33
+ }
34
+
35
+ ::-webkit-scrollbar-thumb {
36
+ background: #3f3f46;
37
+ border-radius: 4px;
38
+ }
39
+
40
+ ::-webkit-scrollbar-thumb:hover {
41
+ background: #52525b;
42
+ }
43
+
44
+ /* Prose overrides for super premium dark mode look */
45
+ .prose {
46
+ max-width: 85ch;
47
+ --tw-prose-body: #d4d4d8;
48
+ --tw-prose-headings: #ffffff;
49
+ --tw-prose-lead: #a1a1aa;
50
+ --tw-prose-links: #ffffff;
51
+ --tw-prose-bold: #ffffff;
52
+ --tw-prose-counters: #a1a1aa;
53
+ --tw-prose-bullets: #52525b;
54
+ --tw-prose-hr: #27272a;
55
+ --tw-prose-quotes: #e4e4e7;
56
+ --tw-prose-quote-borders: #3f3f46;
57
+ --tw-prose-captions: #a1a1aa;
58
+ --tw-prose-code: #ffffff;
59
+ --tw-prose-pre-code: #e4e4e7;
60
+ --tw-prose-pre-bg: #18181b;
61
+ --tw-prose-th-borders: #3f3f46;
62
+ --tw-prose-td-borders: #27272a;
63
+ }
64
+
65
+ .prose a {
66
+ text-decoration-color: #52525b;
67
+ text-underline-offset: 4px;
68
+ transition: all 0.2s;
69
+ }
70
+
71
+ .prose a:hover {
72
+ text-decoration-color: #ffffff;
73
+ }
74
+
75
+ .prose pre {
76
+ border: 1px solid var(--border);
77
+ border-radius: 0.75rem;
78
+ }
79
+
80
+ .prose code {
81
+ font-size: 0.875em;
82
+ font-weight: 500;
83
+ background-color: #18181b;
84
+ padding: 0.2em 0.4em;
85
+ border-radius: 0.25rem;
86
+ border: 1px solid var(--border);
87
+ }
88
+
89
+ .prose code::before,
90
+ .prose code::after {
91
+ content: none !important;
92
+ }
@@ -0,0 +1,32 @@
1
+ import "@/app/globals.css";
2
+ import { Inter } from "next/font/google";
3
+ import Sidebar from "@/components/Sidebar";
4
+ import { buildSidebarTree } from "@/lib/docs";
5
+
6
+ const inter = Inter({ subsets: ["latin"] });
7
+
8
+ export const metadata = {
9
+ title: "__SITE_NAME__",
10
+ description: "__SITE_DESCRIPTION__",
11
+ };
12
+
13
+ export default function RootLayout({
14
+ children,
15
+ }: {
16
+ children: React.ReactNode;
17
+ }) {
18
+ const tree = buildSidebarTree();
19
+
20
+ return (
21
+ <html lang="en" className="dark">
22
+ <body className={`${inter.className} bg-black text-white antialiased selection:bg-zinc-800 selection:text-white`}>
23
+ <div className="flex min-h-screen pt-14 md:pt-0">
24
+ <Sidebar tree={tree} />
25
+ <main className="flex-1 w-full max-w-5xl mx-auto py-12 px-6 md:px-12 lg:px-24">
26
+ {children}
27
+ </main>
28
+ </div>
29
+ </body>
30
+ </html>
31
+ );
32
+ }
@@ -0,0 +1,17 @@
1
+ import { MetadataRoute } from 'next';
2
+ import { getAllDocsSlugs } from '@/lib/docs';
3
+
4
+ export default function sitemap(): MetadataRoute.Sitemap {
5
+ const slugs = getAllDocsSlugs();
6
+ const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || '__SITE_URL__';
7
+
8
+ return slugs.map((slug) => {
9
+ const urlPath = slug.length > 0 ? `/${slug.join('/')}` : '';
10
+ return {
11
+ url: `${baseUrl}${urlPath}`,
12
+ lastModified: new Date(),
13
+ changeFrequency: 'weekly',
14
+ priority: slug.length === 0 ? 1 : 0.8,
15
+ };
16
+ });
17
+ }
@@ -0,0 +1,101 @@
1
+ 'use client';
2
+
3
+ import Link from 'next/link';
4
+ import { usePathname } from 'next/navigation';
5
+ import clsx from 'clsx';
6
+ import { Book, Menu, X } from 'lucide-react';
7
+ import { useState, useEffect } from 'react';
8
+
9
+ type SidebarNode = {
10
+ title: string;
11
+ slug?: string;
12
+ children: SidebarNode[];
13
+ };
14
+
15
+ export default function Sidebar({ tree }: { tree: SidebarNode }) {
16
+ const pathname = usePathname();
17
+ const [isOpen, setIsOpen] = useState(false);
18
+
19
+ // Close sidebar on mobile when navigating
20
+ useEffect(() => {
21
+ setIsOpen(false);
22
+ }, [pathname]);
23
+
24
+ const renderTree = (node: SidebarNode, level: number = 0) => {
25
+ return (
26
+ <ul className={clsx("flex flex-col gap-1", level > 0 && "pl-4 border-l border-zinc-800/80 ml-2 mt-1")}>
27
+ {node.children.map((child, index) => {
28
+ const isActive = pathname === child.slug || (pathname === '/' && child.slug === '/');
29
+
30
+ return (
31
+ <li key={child.slug || index}>
32
+ {child.slug ? (
33
+ <Link
34
+ href={child.slug}
35
+ className={clsx(
36
+ "flex items-center text-sm px-3 py-1.5 rounded-lg transition-colors duration-200",
37
+ isActive
38
+ ? "bg-white/10 text-white font-medium"
39
+ : "text-zinc-400 hover:text-white hover:bg-white/5"
40
+ )}
41
+ >
42
+ {child.title}
43
+ </Link>
44
+ ) : (
45
+ <div className="flex items-center text-sm px-3 py-2 text-zinc-300 font-semibold mt-2">
46
+ {child.title}
47
+ </div>
48
+ )}
49
+ {child.children?.length > 0 && renderTree(child, level + 1)}
50
+ </li>
51
+ );
52
+ })}
53
+ </ul>
54
+ );
55
+ };
56
+
57
+ return (
58
+ <>
59
+ {/* Mobile Toggle */}
60
+ <div className="md:hidden fixed top-0 left-0 w-full bg-zinc-950 border-b border-zinc-800 z-50 flex items-center justify-between px-4 py-3">
61
+ <div className="flex items-center gap-2">
62
+ <div className="w-6 h-6 bg-white rounded-md flex items-center justify-center">
63
+ <Book className="w-4 h-4 text-black" />
64
+ </div>
65
+ <span className="text-white font-bold text-lg tracking-tight">__SITE_NAME__</span>
66
+ </div>
67
+ <button onClick={() => setIsOpen(!isOpen)} className="text-zinc-400 hover:text-white">
68
+ {isOpen ? <X className="w-6 h-6" /> : <Menu className="w-6 h-6" />}
69
+ </button>
70
+ </div>
71
+
72
+ {/* Sidebar Overlay */}
73
+ {isOpen && (
74
+ <div
75
+ className="fixed inset-0 bg-black/50 z-40 md:hidden"
76
+ onClick={() => setIsOpen(false)}
77
+ />
78
+ )}
79
+
80
+ {/* Sidebar Content */}
81
+ <aside className={clsx(
82
+ "fixed md:sticky top-0 left-0 h-[100dvh] w-64 bg-zinc-950/80 backdrop-blur-xl border-r border-zinc-800 z-50 transform transition-transform duration-300 ease-in-out md:translate-x-0 overflow-y-auto custom-scrollbar flex flex-col",
83
+ isOpen ? "translate-x-0" : "-translate-x-full"
84
+ )}>
85
+ <div className="p-6">
86
+ <Link href="/" className="mb-8 flex items-center gap-3">
87
+ <div className="w-8 h-8 flex-shrink-0 bg-white rounded-lg flex items-center justify-center shadow-[0_0_15px_rgba(255,255,255,0.3)]">
88
+ <Book className="w-5 h-5 text-black" />
89
+ </div>
90
+ <span className="text-xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-white to-zinc-400">
91
+ __SITE_NAME__
92
+ </span>
93
+ </Link>
94
+ <div className="mt-8">
95
+ {renderTree(tree)}
96
+ </div>
97
+ </div>
98
+ </aside>
99
+ </>
100
+ );
101
+ }