create-audora-next 0.1.7 → 2.0.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.
Files changed (61) hide show
  1. package/README.md +15 -5
  2. package/index.ts +9 -4
  3. package/package.json +6 -1
  4. package/templates/blog/README.md +164 -0
  5. package/templates/blog/bun.lock +1341 -0
  6. package/templates/blog/env.example.template +5 -0
  7. package/templates/blog/eslint.config.mjs +18 -0
  8. package/templates/blog/gitignore.template +41 -0
  9. package/templates/blog/husky.template/pre-commit +16 -0
  10. package/templates/blog/lint-staged.config.mjs +17 -0
  11. package/templates/blog/next.config.ts +38 -0
  12. package/templates/blog/package.json +59 -0
  13. package/templates/blog/postcss.config.mjs +7 -0
  14. package/templates/blog/public/favicon/apple-touch-icon.png +0 -0
  15. package/templates/blog/public/favicon/favicon-96x96.png +0 -0
  16. package/templates/blog/public/favicon/favicon.ico +0 -0
  17. package/templates/blog/public/favicon/favicon.svg +1 -0
  18. package/templates/blog/public/favicon/site.webmanifest +21 -0
  19. package/templates/blog/public/favicon/web-app-manifest-192x192.png +0 -0
  20. package/templates/blog/public/favicon/web-app-manifest-512x512.png +0 -0
  21. package/templates/blog/public/images/screenshot-desktop-dark.webp +0 -0
  22. package/templates/blog/public/images/screenshot-desktop-light.webp +0 -0
  23. package/templates/blog/public/images/screenshot-mobile-dark.webp +0 -0
  24. package/templates/blog/public/images/screenshot-mobile-light.webp +0 -0
  25. package/templates/blog/src/app/blogs/[slug]/page.tsx +171 -0
  26. package/templates/blog/src/app/blogs/page.tsx +108 -0
  27. package/templates/blog/src/app/layout.tsx +60 -0
  28. package/templates/blog/src/app/llms-full.txt/route.ts +97 -0
  29. package/templates/blog/src/app/llms.txt/route.ts +40 -0
  30. package/templates/blog/src/app/manifest.ts +61 -0
  31. package/templates/blog/src/app/page.tsx +57 -0
  32. package/templates/blog/src/app/robots.ts +16 -0
  33. package/templates/blog/src/app/sitemap.ts +52 -0
  34. package/templates/blog/src/blogs/components/animated-blog-list.tsx +33 -0
  35. package/templates/blog/src/blogs/components/blog-post-card.tsx +46 -0
  36. package/templates/blog/src/blogs/components/blog-section.tsx +34 -0
  37. package/templates/blog/src/blogs/components/blog-table-of-contents.tsx +369 -0
  38. package/templates/blog/src/blogs/components/copy-button.tsx +46 -0
  39. package/templates/blog/src/blogs/components/mdx.tsx +225 -0
  40. package/templates/blog/src/blogs/content/cosketch/cosketch-canvas-engine.mdx +186 -0
  41. package/templates/blog/src/blogs/content/cosketch/cosketch-docker-architecture.mdx +175 -0
  42. package/templates/blog/src/blogs/content/cosketch/cosketch-eraser-and-selection.mdx +207 -0
  43. package/templates/blog/src/blogs/content/hello-world.mdx +66 -0
  44. package/templates/blog/src/blogs/data/mdx.ts +68 -0
  45. package/templates/blog/src/blogs/utils/extract-headings.ts +38 -0
  46. package/templates/blog/src/components/copyable-code.tsx +41 -0
  47. package/templates/blog/src/components/footer.tsx +25 -0
  48. package/templates/blog/src/components/header.tsx +27 -0
  49. package/templates/blog/src/components/icons.tsx +84 -0
  50. package/templates/blog/src/components/section-heading.tsx +11 -0
  51. package/templates/blog/src/components/theme-provider.tsx +11 -0
  52. package/templates/blog/src/components/theme-toggle.tsx +20 -0
  53. package/templates/blog/src/components/view-all-link.tsx +56 -0
  54. package/templates/blog/src/config/site.ts +19 -0
  55. package/templates/blog/src/data/llms.ts +112 -0
  56. package/templates/blog/src/data/site.ts +52 -0
  57. package/templates/blog/src/lib/seo.ts +190 -0
  58. package/templates/blog/src/lib/utils.ts +83 -0
  59. package/templates/blog/src/styles/globals.css +99 -0
  60. package/templates/blog/src/utils/cn.ts +7 -0
  61. package/templates/blog/tsconfig.json +34 -0
@@ -0,0 +1,97 @@
1
+ import { SITE_CONFIG } from "@/config/site";
2
+ import LLMS_DATA from "@/data/llms";
3
+
4
+ export const dynamic = "force-static";
5
+
6
+ export function GET() {
7
+ const content = `# ${SITE_CONFIG.name}
8
+
9
+ > ${SITE_CONFIG.tagline}
10
+
11
+ ${SITE_CONFIG.description}
12
+
13
+ ## Quick Start
14
+
15
+ \`\`\`bash
16
+ ${LLMS_DATA.quickStart}
17
+ cd my-app
18
+ bun install
19
+ bun dev
20
+ \`\`\`
21
+
22
+ ## Resources
23
+
24
+ - Summary: ${SITE_CONFIG.url}/llms.txt
25
+ - Sitemap: ${SITE_CONFIG.url}/sitemap.xml
26
+ - Repository: ${LLMS_DATA.repository}
27
+
28
+ ## Tech Stack
29
+
30
+ ${LLMS_DATA.techStack.map((item) => `- ${item}`).join("\n")}
31
+
32
+ ## Features
33
+
34
+ ${LLMS_DATA.features.map((item) => `- ${item}`).join("\n")}
35
+
36
+ ## Folder Structure
37
+
38
+ \`\`\`
39
+ ${LLMS_DATA.folderStructure}
40
+ \`\`\`
41
+
42
+ ## Development Commands
43
+
44
+ | Command | Description |
45
+ |---------|-------------|
46
+ | \`${LLMS_DATA.commands.dev}\` | Start development server |
47
+ | \`${LLMS_DATA.commands.build}\` | Build for production |
48
+ | \`${LLMS_DATA.commands.start}\` | Start production server |
49
+ | \`${LLMS_DATA.commands.lint}\` | Lint code |
50
+ | \`${LLMS_DATA.commands.format}\` | Format code |
51
+
52
+ ${LLMS_DATA.seoDetails}
53
+
54
+ ${LLMS_DATA.conventions}
55
+
56
+ ## Environment Variables
57
+
58
+ | Variable | Description |
59
+ |----------|-------------|
60
+ | \`NEXT_PUBLIC_SITE_URL\` | Base URL for sitemap, robots.txt, and Open Graph |
61
+
62
+ ## Key Files
63
+
64
+ ### src/data/site.ts
65
+ Contains site configuration that powers SEO metadata:
66
+ - name, url, ogImage
67
+ - tagline, description, shortDescription
68
+ - twitterHandle, keywords
69
+
70
+ ### src/lib/seo.ts
71
+ SEO utility functions:
72
+ - getMetadata() - Root layout metadata
73
+ - getPageMetadata() - Per-page metadata
74
+ - getViewport() - Viewport with theme colors
75
+ - JSON-LD generators for structured data
76
+
77
+ ### src/app/layout.tsx
78
+ Root layout with:
79
+ - Metadata exports
80
+ - Theme provider
81
+ - Geist font configuration
82
+
83
+ ### src/components/theme-toggle.tsx
84
+ Dark/light mode toggle component using next-themes.
85
+
86
+ ## License
87
+
88
+ MIT
89
+ `;
90
+
91
+ return new Response(content, {
92
+ headers: {
93
+ "Content-Type": "text/plain; charset=utf-8",
94
+ "Cache-Control": "public, max-age=3600, s-maxage=86400",
95
+ },
96
+ });
97
+ }
@@ -0,0 +1,40 @@
1
+ import { SITE_CONFIG } from "@/config/site";
2
+ import LLMS_DATA from "@/data/llms";
3
+
4
+ export const dynamic = "force-static";
5
+
6
+ export function GET() {
7
+ const content = `# ${SITE_CONFIG.name}
8
+
9
+ > ${SITE_CONFIG.tagline}
10
+
11
+ ${SITE_CONFIG.description}
12
+
13
+ ## Quick Start
14
+
15
+ \`\`\`bash
16
+ ${LLMS_DATA.quickStart}
17
+ \`\`\`
18
+
19
+ ## Resources
20
+
21
+ - Full documentation: ${SITE_CONFIG.url}/llms-full.txt
22
+ - Sitemap: ${SITE_CONFIG.url}/sitemap.xml
23
+ - Repository: ${LLMS_DATA.repository}
24
+
25
+ ## Tech Stack
26
+
27
+ ${LLMS_DATA.techStack.map((item) => `- ${item}`).join("\n")}
28
+
29
+ ## Features
30
+
31
+ ${LLMS_DATA.features.map((item) => `- ${item}`).join("\n")}
32
+ `;
33
+
34
+ return new Response(content, {
35
+ headers: {
36
+ "Content-Type": "text/plain; charset=utf-8",
37
+ "Cache-Control": "public, max-age=3600, s-maxage=86400",
38
+ },
39
+ });
40
+ }
@@ -0,0 +1,61 @@
1
+ import type { MetadataRoute } from "next";
2
+
3
+ import { SITE_CONFIG } from "@/config/site";
4
+
5
+ export default function manifest(): MetadataRoute.Manifest {
6
+ return {
7
+ name: SITE_CONFIG.name,
8
+ short_name: SITE_CONFIG.name,
9
+ description: SITE_CONFIG.description,
10
+ icons: [
11
+ {
12
+ src: "/favicon/favicon.svg",
13
+ type: "image/svg+xml",
14
+ sizes: "any",
15
+ purpose: "any",
16
+ },
17
+ {
18
+ src: "/favicon/web-app-manifest-192x192.png",
19
+ type: "image/png",
20
+ sizes: "192x192",
21
+ purpose: "any",
22
+ },
23
+ {
24
+ src: "/favicon/web-app-manifest-512x512.png",
25
+ type: "image/png",
26
+ sizes: "512x512",
27
+ purpose: "maskable",
28
+ },
29
+ ],
30
+ id: "/?utm_source=pwa",
31
+ start_url: "/?utm_source=pwa",
32
+ display: "standalone",
33
+ scope: "/",
34
+ screenshots: [
35
+ {
36
+ src: "/images/screenshot-mobile-dark.webp",
37
+ type: "image/webp",
38
+ sizes: "440x956",
39
+ form_factor: "narrow",
40
+ },
41
+ {
42
+ src: "/images/screenshot-mobile-light.webp",
43
+ type: "image/webp",
44
+ sizes: "440x956",
45
+ form_factor: "narrow",
46
+ },
47
+ {
48
+ src: "/images/screenshot-desktop-dark.webp",
49
+ type: "image/webp",
50
+ sizes: "1920x1080",
51
+ form_factor: "wide",
52
+ },
53
+ {
54
+ src: "/images/screenshot-desktop-light.webp",
55
+ type: "image/webp",
56
+ sizes: "1920x1080",
57
+ form_factor: "wide",
58
+ },
59
+ ],
60
+ };
61
+ }
@@ -0,0 +1,57 @@
1
+ import Link from "next/link";
2
+ import { SITE_CONFIG } from "@/config/site";
3
+ import { getPageMetadata } from "@/lib/seo";
4
+ import { BlogSection } from "@/blogs/components/blog-section";
5
+ import { CopyableCode } from "@/components/copyable-code";
6
+ import { Github } from "lucide-react";
7
+
8
+ export const metadata = getPageMetadata({
9
+ title: `${SITE_CONFIG.name} - ${SITE_CONFIG.tagline}`,
10
+ path: "/",
11
+ });
12
+
13
+ export default function Home() {
14
+ return (
15
+ <>
16
+ <section className="container mx-auto flex max-w-5xl flex-col items-center justify-center px-4 py-10 text-center sm:py-16">
17
+ <div className="space-y-6">
18
+ <h1 className="text-3xl font-bold tracking-tight text-foreground sm:text-5xl sm:leading-tight">
19
+ {SITE_CONFIG.tagline}
20
+ </h1>
21
+
22
+ <p className="mx-auto max-w-2xl text-lg text-muted-foreground sm:text-xl">
23
+ {SITE_CONFIG.shortDescription}
24
+ </p>
25
+
26
+ <div className="flex flex-col items-center justify-center gap-4 sm:flex-row">
27
+ <Link
28
+ href="https://github.com/audoralabs/nextblog-starter"
29
+ target="_blank"
30
+ rel="noopener noreferrer"
31
+ className="inline-flex h-10 items-center justify-center gap-2 rounded-md bg-primary px-6 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
32
+ >
33
+ <Github className="h-4 w-4" suppressHydrationWarning />
34
+ Star on GitHub
35
+ </Link>
36
+ <Link
37
+ href="https://www.npmjs.com/package/create-audora-next"
38
+ target="_blank"
39
+ rel="noopener noreferrer"
40
+ className="inline-flex h-10 items-center justify-center rounded-md border border-input bg-background px-6 text-sm font-medium shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
41
+ >
42
+ View on npm
43
+ </Link>
44
+ </div>
45
+
46
+ <div>
47
+ <CopyableCode code="bunx create-audora-next@latest -blog my-blog" />
48
+ </div>
49
+ </div>
50
+ </section>
51
+
52
+ <section className="border-t border-border/40 p-2">
53
+ <BlogSection />
54
+ </section>
55
+ </>
56
+ );
57
+ }
@@ -0,0 +1,16 @@
1
+ import type { MetadataRoute } from "next";
2
+
3
+ import { SITE_CONFIG } from "@/config/site";
4
+
5
+ export default function robots(): MetadataRoute.Robots {
6
+ return {
7
+ rules: [
8
+ {
9
+ userAgent: "*",
10
+ allow: "/",
11
+ disallow: ["/api/", "/_next/", "/private/"],
12
+ },
13
+ ],
14
+ sitemap: `${SITE_CONFIG.url}/sitemap.xml`,
15
+ };
16
+ }
@@ -0,0 +1,52 @@
1
+ import type { MetadataRoute } from "next";
2
+ import { SITE_CONFIG } from "@/config/site";
3
+ import { getBlogPosts } from "@/blogs/data/mdx";
4
+
5
+ export const dynamic = "force-static";
6
+
7
+ /**
8
+ * Generates a comprehensive sitemap for the website
9
+ *
10
+ * Priority guidelines:
11
+ * - 1.0: Homepage (most important)
12
+ * - 0.8-0.9: Main pages (About, Blog listing, etc.)
13
+ * - 0.6-0.7: Blog posts, category pages
14
+ * - 0.4-0.5: Archive pages, tag pages
15
+ * - 0.3: Less important pages
16
+ *
17
+ * Change frequency guidelines:
18
+ * - always: Pages that change with every access
19
+ * - hourly: Pages that change hourly
20
+ * - daily: Pages that change daily
21
+ * - weekly: Pages that change weekly
22
+ * - monthly: Pages that change monthly
23
+ * - yearly: Pages that change yearly
24
+ * - never: Archived pages
25
+ */
26
+
27
+ export default function sitemap(): MetadataRoute.Sitemap {
28
+ const siteUrl = SITE_CONFIG.url;
29
+ const currentDate = new Date().toISOString();
30
+
31
+ // Static routes with priorities and change frequencies
32
+ const staticRoutes: MetadataRoute.Sitemap = [
33
+ {
34
+ url: siteUrl,
35
+ lastModified: currentDate,
36
+ changeFrequency: "weekly",
37
+ priority: 1.0,
38
+ },
39
+ ];
40
+
41
+ // Blog posts
42
+ const blogPosts: MetadataRoute.Sitemap = getBlogPosts().map((post) => ({
43
+ url: `${siteUrl}/blog/${post.slug}`,
44
+ lastModified: post.metadata?.publishedAt
45
+ ? new Date(post.metadata.publishedAt).toISOString()
46
+ : currentDate,
47
+ changeFrequency: "monthly" as const,
48
+ priority: 0.7,
49
+ }));
50
+
51
+ return [...staticRoutes, ...blogPosts];
52
+ }
@@ -0,0 +1,33 @@
1
+ import { BlogPostCard } from "./blog-post-card";
2
+ // import { AnimateIn } from "@/components/animate-in";
3
+
4
+ interface BlogPostDisplayProps {
5
+ slug: string;
6
+ metadata: {
7
+ title: string;
8
+ publishedAt: string;
9
+ summary: string;
10
+ };
11
+ }
12
+
13
+ interface AnimatedBlogListProps {
14
+ posts: BlogPostDisplayProps[];
15
+ }
16
+
17
+ export function AnimatedBlogList({ posts }: AnimatedBlogListProps) {
18
+ return (
19
+ <div className="flex flex-col gap-4">
20
+ {posts.map((post, index) => (
21
+ // <AnimateIn key={post.slug} delay={index * 0.05}>
22
+ <BlogPostCard
23
+ key={post.slug}
24
+ slug={post.slug}
25
+ title={post.metadata.title}
26
+ publishedAt={post.metadata.publishedAt}
27
+ summary={post.metadata.summary}
28
+ />
29
+ // </AnimateIn>
30
+ ))}
31
+ </div>
32
+ );
33
+ }
@@ -0,0 +1,46 @@
1
+ import Link from "next/link";
2
+ import { Calendar, ChevronRight } from "lucide-react";
3
+ import { formatDateDisplay } from "@/lib/utils";
4
+
5
+ type BlogPostCardProps = {
6
+ slug: string;
7
+ title: string;
8
+ publishedAt: string;
9
+ summary?: string;
10
+ };
11
+
12
+ export function BlogPostCard({
13
+ slug,
14
+ title,
15
+ publishedAt,
16
+ summary,
17
+ }: BlogPostCardProps) {
18
+ return (
19
+ <Link
20
+ className="group flex flex-col gap-1 rounded-lg border border-border/50 p-3 transition-all hover:border-border sm:p-4"
21
+ href={`/blogs/${slug}`}
22
+ >
23
+ <div className="flex w-full flex-col gap-1 sm:flex-row sm:items-start sm:justify-between">
24
+ <h2 className="text-lg font-medium tracking-tight text-foreground transition-colors group-hover:text-foreground/80">
25
+ {title}
26
+ </h2>
27
+ <time
28
+ dateTime={publishedAt}
29
+ className="flex shrink-0 items-center gap-1.5 text-sm text-muted-foreground tabular-nums"
30
+ >
31
+ <Calendar className="h-4 w-4 shrink-0" />
32
+ {formatDateDisplay(publishedAt)}
33
+ </time>
34
+ </div>
35
+ {summary && (
36
+ <p className="line-clamp-2 text-sm text-muted-foreground">{summary}</p>
37
+ )}
38
+ <span className="mt-1 inline-flex items-center gap-1 text-sm font-medium text-foreground/70 transition-colors group-hover:text-foreground">
39
+ Read article
40
+ <ChevronRight className="h-4 w-4 transition-transform group-hover:translate-x-0.5" />
41
+ </span>
42
+ </Link>
43
+ );
44
+ }
45
+
46
+ export default BlogPostCard;
@@ -0,0 +1,34 @@
1
+ import { getBlogPosts } from "@/blogs/data/mdx";
2
+ import { ViewAllLink } from "@/components/view-all-link";
3
+ import SectionHeading from "@/components/section-heading";
4
+ import { AnimatedBlogList } from "./animated-blog-list";
5
+
6
+ export function BlogSection() {
7
+ const allBlogs = getBlogPosts();
8
+
9
+ const sorted = [...allBlogs]
10
+ .sort(
11
+ (a, b) =>
12
+ new Date(b.metadata.publishedAt).getTime() -
13
+ new Date(a.metadata.publishedAt).getTime(),
14
+ )
15
+ .slice(0, 4);
16
+
17
+ return (
18
+ <section id="blog" className="pt-4 pb-2">
19
+ <div className="container mx-auto mt-8 flex max-w-(--content-max-width) flex-col gap-6 px-4">
20
+ <SectionHeading>Blogs</SectionHeading>
21
+ {sorted.length === 0 ? (
22
+ <p className="text-muted-foreground">No posts yet.</p>
23
+ ) : (
24
+ <AnimatedBlogList posts={sorted} />
25
+ )}
26
+ <div className="flex justify-center pt-2">
27
+ <ViewAllLink href="/blogs" />
28
+ </div>
29
+ </div>
30
+ </section>
31
+ );
32
+ }
33
+
34
+ export default BlogSection;