diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md index 4bb707e..4886b6b 100644 --- a/.github/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -10,19 +10,19 @@ We pledge to act and interact in ways that contribute to an open, welcoming, div Examples of behavior that contributes to a positive environment for our community include: -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience -* Focusing on what is best not just for us as individuals, but for the overall community +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience +- Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and unwelcome sexual attention or advances -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a professional setting +- The use of sexualized language or imagery, and unwelcome sexual attention or advances +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 842c368..4c8f54f 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,21 +1,26 @@ ## Description + Please include a summary of the change and which issue is fixed. ## Change Type + - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] Documentation update ## Testing Steps + Please describe the tests that you ran to verify your changes. ## Screenshots (if applicable) ## Related Issues + Fixes # ## Checklist + - [ ] `pnpm typecheck` passes - [ ] `pnpm lint` passes - [ ] `pnpm test` (Vitest) passes diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 41d311f..2abe248 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -4,17 +4,17 @@ on: workflow_dispatch: inputs: environment: - description: 'Environment to deploy to' + description: "Environment to deploy to" required: true - default: 'preview' + default: "preview" type: choice options: - production - preview provider: - description: 'Deployment provider' + description: "Deployment provider" required: true - default: 'vercel' + default: "vercel" type: choice options: - vercel diff --git a/.mise.toml b/.mise.toml index c450e3d..11e299d 100644 --- a/.mise.toml +++ b/.mise.toml @@ -6,6 +6,7 @@ pnpm = "latest" m = "mise run" i = "mise run install" l = "mise run lint" +f = "mise run format" t = "mise run test" b = "mise run build" d = "mise run dev" @@ -15,10 +16,10 @@ v = "mise run verify" g = "git" gs = "git status" gb = "git branch" -gt = "git checkout" +gco = "git checkout" gl = "git log" ga = "git add" -gc = "git commit -a -m" +gc = "git commit" gp = "git push" gr = "git rebase --merge" @@ -47,6 +48,11 @@ description = "Run linting" run = "pnpm run lint" alias = "l" +[tasks.format] +description = "Run formatting" +run = "pnpm run format" +alias = "f" + [tasks.typecheck] description = "Run typechecking" run = "pnpm run typecheck" diff --git a/AGENTS.md b/AGENTS.md index 8cde494..ef576a9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,9 +1,11 @@ # Agents.md ## 1. Project Overview + cur8d is a production-ready Next.js starter optimized for data integration and scalability. ## 2. Tech Stack + - Next.js 16.2.6: App Router, Server Components. - HeroUI v3.0.5: Compound components with dot notation. - Tailwind CSS v4: CSS-first configuration. @@ -11,57 +13,71 @@ cur8d is a production-ready Next.js starter optimized for data integration and s - Supabase & Vercel Blob: Data and storage stubs. ## 3. Directory Structure -- `src/app`: Routes and layouts. -- `src/components`: Reusable components. -- `src/lib`: Logic and data layer. + +- `app`: Routes, layouts, components (`app/components`), hooks (`app/hooks`), data (`app/data`), and types (`app/types`). +- `tests`: Unit tests (`tests/unit`) and E2E tests (`tests/e2e`). - `docs`: Nextra v4 documentation. ## 4. Toolchain Management + Managed via `mise`. Update `.mise.toml` to change Node.js or pnpm versions. ## 5. TypeScript Rules + - Strict mode enabled. - No `any` types allowed. - Explicit interfaces for all component props. ## 6. Coding Conventions + - Named imports for icons. - Barrel exports (`index.ts`) in component folders. - Server Components by default. ## 7. How to add a new page -Add directory to `src/app/` with `page.tsx`. + +Add directory to `app/` with `page.tsx`. ## 8. How to add a new component -Create folder in `src/components/` with `index.tsx`, types, and tests. + +Create folder in `app/components/` with `index.tsx` and types, and add test in `tests/unit/components/`. ## 9. How to add a keyboard shortcut -Register in `src/config/shortcuts.ts`, use `useShortcuts` hook, add UI hint. + +Register in `app/config/shortcuts.ts`, use `useShortcuts` hook, add UI hint. ## 10. Testing Guide + - Unit: `pnpm test` - E2E: `pnpm test:e2e` - 80% coverage required. ## 11. Environment Variables -Validated via Zod in `src/lib/env.ts`. + +Validate via Zod in `app/lib/env.ts` when required. ## 12. Local Development Commands + - `pnpm dev`: Start dev server. - `pnpm build`: Production build. - `pnpm lint`: Run linting. ## 13. Deployment + Vercel for SSR, GitHub Pages for static export. ## 14. HeroUI v3 usage + Use compound component pattern (e.g., ``). ## 15. Dark mode + Wiring via `next-themes` and Tailwind v4 variables. ## 16. Icon usage + `lucide-react` named imports with Tailwind `size-*` utilities. ## 17. Data layer -Mock data in `src/lib/data/mock.ts`, migration path to Supabase documented in `src/lib/data/README.md`. + +Mock data in `app/data/templates.ts`, migration path to Supabase documented in `app/data/README.md`. diff --git a/CHANGELOG.md b/CHANGELOG.md index 6be4020..a6634c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,5 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [v0.0.1] + ### Added + - Initial cur8d.dev implementation. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a0fd758..9b744bf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,18 +1,22 @@ # Contributing to cur8d ## Setup + 1. Fork the repository. 2. `mise install && pnpm install`. ## Branching + - `feat/`: New features. - `fix/`: Bug fixes. - `chore/`: Maintenance. ## Conventional Commits + We follow the Conventional Commits specification for all commit messages. ## Development + - Add tests for new components. - Update documentation in `docs/` if applicable. - Architecture changes require an ADR in `docs/content/adr/`. diff --git a/app/components/CodeBlock/CopyButton.tsx b/app/components/CodeBlock/CopyButton.tsx new file mode 100644 index 0000000..d2ed280 --- /dev/null +++ b/app/components/CodeBlock/CopyButton.tsx @@ -0,0 +1,60 @@ +"use client"; + +import { useState } from "react"; +import { Button, Tooltip } from "@heroui/react"; +import { Copy, Check } from "lucide-react"; + +export function CodeBlockCopyButton({ + code, + className, +}: { + code: string; + className?: string; +}) { + const [copied, setCopied] = useState(false); + + const copyToClipboard = async () => { + try { + await navigator.clipboard.writeText(code); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + } catch (err) { + console.error("Failed to copy: ", err); + } + }; + + return ( + + { + const buttonProps = triggerProps as unknown as React.ComponentProps< + typeof Button + >; + return ( + + ); + }} + /> + + {copied ? "Copied!" : "Copy to clipboard"} + + + ); +} diff --git a/src/components/CodeBlock/index.tsx b/app/components/CodeBlock/index.tsx similarity index 62% rename from src/components/CodeBlock/index.tsx rename to app/components/CodeBlock/index.tsx index e9dd1b8..da9d6e4 100644 --- a/src/components/CodeBlock/index.tsx +++ b/app/components/CodeBlock/index.tsx @@ -7,11 +7,7 @@ interface CodeBlockProps { } function CodeBlockRoot({ children, className }: CodeBlockProps) { - return ( -
- {children} -
- ); + return
{children}
; } interface CodeBlockHeaderProps { @@ -21,9 +17,7 @@ interface CodeBlockHeaderProps { function CodeBlockHeader({ children, className }: CodeBlockHeaderProps) { return ( -
- {children} -
+
{children}
); } @@ -31,12 +25,22 @@ interface CodeBlockCodeProps { code: string; language?: string; className?: string; + showPrompt?: boolean; + showCursor?: boolean; } -function CodeBlockCode({ code, language, className }: CodeBlockCodeProps) { +function CodeBlockCode({ + code, + language, + className, + showPrompt = false, + showCursor = false, +}: CodeBlockCodeProps) { return (
+      {showPrompt && $ }
       {code}
+      {showCursor && 
); } diff --git a/src/components/Footer/index.tsx b/app/components/Footer/index.tsx similarity index 100% rename from src/components/Footer/index.tsx rename to app/components/Footer/index.tsx diff --git a/src/components/Navbar/index.tsx b/app/components/Navbar/index.tsx similarity index 100% rename from src/components/Navbar/index.tsx rename to app/components/Navbar/index.tsx diff --git a/src/components/Providers/index.tsx b/app/components/Providers/index.tsx similarity index 85% rename from src/components/Providers/index.tsx rename to app/components/Providers/index.tsx index 670e86f..ea529c6 100644 --- a/src/components/Providers/index.tsx +++ b/app/components/Providers/index.tsx @@ -13,7 +13,7 @@ export function Providers({ children }: ProvidersProps) { return ( - + {children} diff --git a/app/components/TemplateCard/index.tsx b/app/components/TemplateCard/index.tsx new file mode 100644 index 0000000..11f216f --- /dev/null +++ b/app/components/TemplateCard/index.tsx @@ -0,0 +1,70 @@ +import { Card, Link, Chip } from "@heroui/react"; +import { FaGithub } from "react-icons/fa6"; +import { Template } from "@/types/template"; + +export interface TemplateCardProps extends Template {} + +export function TemplateCard({ + title, + description, + icon, + github, + docs, + preview, + status, +}: TemplateCardProps) { + return ( + + +
+ {`.${icon}`} +
+ {github && ( + + + + )} +
+ + + {title} {status && {status.toLowerCase()}} + +

{description}

+
+ + {((docs || preview) && ( +
+ {docs && ( + + Docs +
+ )) || Coming Soon} +
+
+ ); +} diff --git a/src/components/ThemeToggle/index.tsx b/app/components/ThemeToggle/index.tsx similarity index 84% rename from src/components/ThemeToggle/index.tsx rename to app/components/ThemeToggle/index.tsx index 2378cf6..8af21f9 100644 --- a/src/components/ThemeToggle/index.tsx +++ b/app/components/ThemeToggle/index.tsx @@ -24,7 +24,11 @@ export function ThemeToggle() { aria-label="Toggle theme" onClick={() => setTheme(theme === "dark" ? "light" : "dark")} > - {theme === "dark" ? : } + {theme === "dark" ? ( + + ) : ( + + )} ); } diff --git a/app/data/templates.ts b/app/data/templates.ts new file mode 100644 index 0000000..03b6b1c --- /dev/null +++ b/app/data/templates.ts @@ -0,0 +1,35 @@ +import { Template } from "@/types/template"; + +export const templates: Template[] = [ + { + title: "cur8d.aws", + description: + "Production-ready AWS Lambda templates in Python for different real-life scenarios", + icon: "aws", + docs: "https://cur8d.dev/lambda", + github: "https://github.com/cur8d/cur8d.aws", + }, + { + title: "cur8d.py", + description: "Python project template for quick start with best practices", + icon: "py", + docs: "https://cur8d.dev/python", + github: "https://github.com/cur8d/cur8d.py", + }, + { + title: "cur8d.tsx", + description: + "TypeScript web-application starter powered by Next.js, Tailwind CSS, and HeroUI", + icon: "tsx", + docs: "https://cur8d.dev/typescript", + preview: "https://typescript.cur8d.dev", + github: "https://github.com/cur8d/cur8d.tsx", + status: "beta", + }, + { + title: "cur8d.kts", + description: + "Kotlin Multiplatform (KMP) application skeleton for Android, iOS, and web", + icon: "kts", + }, +]; diff --git a/src/app/error.tsx b/app/error.tsx similarity index 97% rename from src/app/error.tsx rename to app/error.tsx index 7fa1520..bb6d4ec 100644 --- a/src/app/error.tsx +++ b/app/error.tsx @@ -24,7 +24,8 @@ export default function Error({

Something went wrong!

- An unexpected error occurred. We've been notified and are looking into it. + An unexpected error occurred. We've been notified and are looking into + it.

diff --git a/src/app/globals.css b/app/globals.css similarity index 92% rename from src/app/globals.css rename to app/globals.css index 3065800..d28d3ad 100644 --- a/src/app/globals.css +++ b/app/globals.css @@ -1,5 +1,5 @@ @import "tailwindcss"; -@import "@heroui/styles"; +@import "@heroui/styles"; @theme { --font-sans: var(--font-geist-sans); @@ -115,4 +115,12 @@ .code-block__copy-button { @apply hover:bg-secondary/80 transition-colors; } + + .code-block__prompt { + @apply text-muted-foreground mr-1.5 select-none font-mono; + } + + .code-block__cursor { + @apply inline-block w-2 h-4 bg-primary animate-pulse align-middle ml-1 rounded-xs; + } } diff --git a/src/hooks/use-search-state.ts b/app/hooks/use-search-state.ts similarity index 97% rename from src/hooks/use-search-state.ts rename to app/hooks/use-search-state.ts index 12e548c..fb8230b 100644 --- a/src/hooks/use-search-state.ts +++ b/app/hooks/use-search-state.ts @@ -34,6 +34,6 @@ export function useSearchState() { // Compatibility with HeroUI useOverlayState if needed open: onOpen, close: onClose, - setOpen: onOpenChange + setOpen: onOpenChange, }; } diff --git a/src/app/layout.tsx b/app/layout.tsx similarity index 85% rename from src/app/layout.tsx rename to app/layout.tsx index d0ae92d..21ead84 100644 --- a/src/app/layout.tsx +++ b/app/layout.tsx @@ -30,7 +30,9 @@ interface RootLayoutProps { export default function RootLayout({ children }: RootLayoutProps) { return ( - + -
- {children} -
+
{children}