Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,7 +55,7 @@
"input-otp": "^1.4.2",
"lucide-react": "^0.485.0",
"mermaid": "^11.16.0",
"next": "15.5.20",
"next": "15.5.21",
"next-themes": "^0.4.6",
"react": "^19.1.0",
"react-day-picker": "^8.10.1",
Expand Down
82 changes: 41 additions & 41 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 8 additions & 3 deletions src/app/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,6 +5,7 @@ import { AnimatedTerminal } from '@/components/animated-terminal';
import { AnnouncementBanner } from '@/components/announcement-banner';
import { ContributorsSection } from '@/components/contributors-section';
import { CopyButton } from '@/components/copy-button';
import { DiscordIcon } from '@/components/discord-icon';
import { EcosystemSection } from '@/components/ecosystem-section';
import { FeaturedTemplate } from '@/components/featured-template';
import { HeroSection } from '@/components/hero-section';
Expand All@@ -14,6 +15,7 @@ import { StatsBar } from '@/components/stats-bar';
import { TemplateCategories } from '@/components/template-categories';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
import { DISCORD_INVITE_URL } from '@/lib/community';
import { getTemplatesData } from '@/lib/data';

const PRIMARY_COMMAND = 'uvx create-awesome-python-app my-app';
Expand All@@ -31,11 +33,14 @@ export default async function Home() {
<div className="flex min-h-screen flex-col">
<main className="flex-1">
<AnnouncementBanner
icon={<DiscordIcon className="h-5 w-5 shrink-0 text-yellow-200" />}
label="NEW"
message={
<>New: FastAPI Starter — production-ready API with uv, Ruff, pytest, and composable Python extensions.</>
<>Join Create Awesome on Discord — chat, good first issues, and collaboration across Node / Python / V.</>
}
ctaHref="/templates/fastapi-starter"
ctaLabel="Explore template"
ctaHref={DISCORD_INVITE_URL}
ctaLabel="Join Discord"
ctaExternal
/>
<HeroSection
title={
Expand Down
58 changes: 35 additions & 23 deletions src/components/announcement-banner.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,7 +11,7 @@ const bannerVariants = cva(
{
variants: {
variant: {
gradient: 'bg-gradient-to-r from-blue-600 via-blue-500 to-green-600 text-white',
gradient: 'bg-gradient-to-r from-amber-600 via-amber-500 to-teal-600 text-white',
subtle: 'bg-[hsl(var(--background))] border-b border-border/60 text-foreground/90 dark:text-foreground/80',
accent: 'bg-[hsl(var(--primary)/0.15)] text-foreground',
},
Expand All@@ -31,6 +31,7 @@ export interface AnnouncementBannerProps extends VariantProps<typeof bannerVaria
message: ReactNode;
ctaHref?: string;
ctaLabel?: string;
ctaExternal?: boolean;
className?: string;
dismissible?: boolean; // future enhancement
}
Expand All@@ -41,17 +42,37 @@ export function AnnouncementBanner({
message,
ctaHref,
ctaLabel = 'Learn more',
ctaExternal = false,
className,
variant,
size,
}: AnnouncementBannerProps) {
const ctaClassName =
'inline-flex shrink-0 items-center gap-1 underline underline-offset-4 decoration-white/40 hover:decoration-white transition-colors group';
const ctaArrow = (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="h-4 w-4 group-hover:translate-x-1 transition-transform"
aria-hidden="true"
>
<path d="M5 12h14" />
<path d="m12 5 7 7-7 7" />
</svg>
);

return (
<div className={cn(bannerVariants({ variant, size }), className)}>
<div className="absolute inset-0 pointer-events-none select-none overflow-hidden" aria-hidden>
{variant === 'gradient' && (
<>
<div className="absolute inset-0 bg-[url('/grid.svg')] bg-center opacity-40 [mask-image:linear-gradient(180deg,white,rgba(255,255,255,0))]" />
<div className="absolute inset-0 bg-gradient-to-r from-blue-500/20 via-blue-400/15 to-green-500/20 animate-gradient-text" />
<div className="absolute inset-0 bg-gradient-to-r from-amber-500/20 via-amber-400/15 to-teal-500/20 animate-gradient-text" />
</>
)}
</div>
Expand All@@ -63,27 +84,18 @@ export function AnnouncementBanner({
</span>
)}
<span className="min-w-0 text-balance">{message}</span>
{ctaHref && (
<Link
href={ctaHref}
className="inline-flex shrink-0 items-center gap-1 underline underline-offset-4 decoration-white/40 hover:decoration-white transition-colors group"
>
{ctaLabel}
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="h-4 w-4 group-hover:translate-x-1 transition-transform"
>
<path d="M5 12h14" />
<path d="m12 5 7 7-7 7" />
</svg>
</Link>
)}
{ctaHref &&
(ctaExternal ? (
<a href={ctaHref} target="_blank" rel="noreferrer" className={ctaClassName}>
{ctaLabel}
{ctaArrow}
</a>
) : (
<Link href={ctaHref} className={ctaClassName}>
{ctaLabel}
{ctaArrow}
</Link>
))}
</div>
</div>
);
Expand Down
10 changes: 10 additions & 0 deletions src/components/discord-icon.tsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
import { cn } from '@/lib/utils';

/** Discord brand mark (simple-icons path). */
export function DiscordIcon({ className }: { className?: string }) {
return (
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" className={cn('h-5 w-5', className)}>
<path d="M20.317 4.37a19.8 19.8 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.3 18.3 0 0 0-5.487 0 12.6 12.6 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.7 19.7 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.08.08 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028 14 14 0 0 0 1.226-1.994.076.076 0 0 0-.041-.106 13.1 13.1 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.3 12.3 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.8 19.8 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.548-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z" />
</svg>
);
}
1 change: 1 addition & 0 deletions src/components/layout-shell.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -117,6 +117,7 @@ export function LayoutShell({ children }: { children: ReactNode }) {
<p className="text-xs font-semibold uppercase tracking-widest text-muted-foreground">Community</p>
<nav className="flex flex-col gap-2">
{[
{ href: 'https://discord.gg/bR5VyATgka', label: 'Discord Community', external: true },
{ href: 'https://github.com/Create-Python-App', label: 'GitHub Organization', external: true },
{
href: 'https://pypi.org/project/create-awesome-python-app/',
Expand Down
2 changes: 1 addition & 1 deletion src/components/saas-ai-banner.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -46,7 +46,7 @@ export function SaasAiBanner() {
{/* Badge */}
<div className="inline-flex items-center gap-2 rounded-full border border-blue-400/30 bg-blue-500/10 px-4 py-1.5 text-sm font-medium text-blue-200 backdrop-blur-sm">
<span>✨</span>
<span>Flagship Template</span>
<span>NEW · Flagship</span>
</div>

{/* Heading */}
Expand Down
7 changes: 7 additions & 0 deletions src/components/site-header.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,10 +4,12 @@ import { Gauge, Github, Menu, Package, Search } from 'lucide-react';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { useEffect, useState } from 'react';
import { DiscordIcon } from '@/components/discord-icon';
import { usePerformanceMode } from '@/components/performance-provider';
import { ThemeToggle } from '@/components/theme-toggle';
import { Button } from '@/components/ui/button';
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui/sheet';
import { DISCORD_INVITE_URL } from '@/lib/community';
import { cn } from '@/lib/utils';

interface NavItem {
Expand DownExpand Up@@ -139,6 +141,11 @@ export function SiteHeader({ onOpenCommand }: { onOpenCommand?: () => void }) {
<Package className="h-5 w-5" />
</Button>
</Link>
<Link href={DISCORD_INVITE_URL} target="_blank" rel="noreferrer" aria-label="Discord Community">
<Button variant="ghost" size="icon" className="hidden sm:inline-flex">
<DiscordIcon className="h-5 w-5" />
</Button>
</Link>
<Link href="https://github.com/Create-Python-App" target="_blank" aria-label="GitHub">
<Button variant="ghost" size="icon">
<Github className="h-5 w-5" />
Expand Down
2 changes: 2 additions & 0 deletions src/lib/community.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
/** Canonical Create Awesome Discord invite (shared across CNA / CPA / CVA). */
export const DISCORD_INVITE_URL = 'https://discord.gg/bR5VyATgka';
Loading