Compare commits

..
10 Commits
Author SHA1 Message Date
Rami Bitar a6f0b33934 Update template page layout and copy 2026-09-05 20:25:20 -04:00
Rami BitarandClaude Opus 4.8 d5e463a665 Strip template artifacts and fix lint errors
Remove npm lockfile, unused create-next-app SVGs, project-specific
next.config workarounds, and the unused @remixicon/react and date-fns deps.

Replace three set-state-in-effect patterns with useSyncExternalStore in
use-mobile, DialogPortal, and Carousel; the carousel change also fixes a
leaked reInit listener. yarn lint and yarn build are clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K8PmydbW6A1S2Tw3SFc3Cg
2026-07-23 19:24:23 -04:00
Rami BitarandClaude Opus 4.8 02327188e2 Drop packageManager field and refresh yarn/tsconfig settings
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K8PmydbW6A1S2Tw3SFc3Cg
2026-07-23 19:17:59 -04:00
Rami Bitar 4c35335b64 Migrate to next 16.2.10 2026-07-17 15:05:00 -04:00
Rami BitarandClaude Fable 5 d919ec4788 Remove AGENTS.md and CLAUDE.md
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0184B4PAd3uXhcJ6Hfrptkdg
2026-07-02 22:07:20 -04:00
Rami BitarandClaude Opus 4.8 b37625007b Mount-gate DialogPortal for SSR safety
Defensive guard so the portal only mounts client-side. Radix's Portal is
already SSR-safe, so this is belt-and-suspenders.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RCfQKfr64YJbFRs3Ai9v2
2026-06-30 17:26:30 -04:00
Rami BitarandClaude Opus 4.8 1037183769 Update landing copy to template getting-started message
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 10:14:28 -04:00
Rami BitarandClaude Opus 4.8 8499841634 Update landing subtitle to highlight prompt, image, and link inputs
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 18:55:40 -04:00
Rami BitarandClaude Opus 4.8 839d9fdb55 Simplify landing hero and clarify sandbox guidance in agent docs
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 18:44:18 -04:00
Rami BitarandClaude Opus 4.8 7467cdce3f Rewrite agent guidance: sandbox notes, style guide, fonts, Vercel AI
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 16:20:56 -04:00
18 changed files with 8349 additions and 12560 deletions
+2 -5
View File
@@ -4,11 +4,8 @@
/node_modules /node_modules
/.pnp /.pnp
.pnp.* .pnp.*
.yarn/* .yarn/
!.yarn/patches .yarnrc.yml
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing # testing
/coverage /coverage
-5
View File
@@ -1,5 +0,0 @@
<!-- BEGIN:nextjs-agent-rules -->
# This is NOT the Next.js you know
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices.
<!-- END:nextjs-agent-rules -->
-1
View File
@@ -1 +0,0 @@
@AGENTS.md
+5 -20
View File
@@ -2,8 +2,8 @@ import Image from "next/image";
export default function Home() { export default function Home() {
return ( return (
<div className="flex min-h-screen flex-col items-center justify-center bg-zinc-50 font-sans dark:bg-black"> <div className="flex min-h-screen flex-col items-center justify-center bg-white font-sans dark:bg-black">
<main className="flex flex-1 w-full max-w-3xl flex-col items-center justify-between gap-20 py-32 px-16 bg-white dark:bg-black sm:items-start"> <main className="flex w-full max-w-6xl flex-1 flex-col items-center bg-white px-16 py-16 dark:bg-black sm:items-start">
<Image <Image
className="dark:invert" className="dark:invert"
src="/next.svg" src="/next.svg"
@@ -12,29 +12,14 @@ export default function Home() {
height={20} height={20}
priority priority
/> />
<div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left"> <div className="flex flex-1 flex-col items-center justify-center gap-6 text-center sm:items-start sm:text-left">
<h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50"> <h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
To get started, use the chat panel to make edits. Start prompting
</h1> </h1>
<p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400"> <p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
Describe what you want to build and watch it come to life. Just Next.js 16 template built with tailwind css and shadcn.
start typing in the chat to shape your app, one idea at a time.
</p> </p>
</div> </div>
<div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
<button
type="button"
className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]"
>
Primary Button
</button>
<button
type="button"
className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]"
>
Secondary Button
</button>
</div>
</main> </main>
</div> </div>
); );
+25 -18
View File
@@ -58,14 +58,32 @@ function Carousel({
}, },
plugins plugins
) )
const [canScrollPrev, setCanScrollPrev] = React.useState(false) // Embla is an external store: subscribe to its events rather than mirroring
const [canScrollNext, setCanScrollNext] = React.useState(false) // its scroll state into React state from an effect.
const subscribe = React.useCallback(
(onStoreChange: () => void) => {
if (!api) return () => {}
api.on("reInit", onStoreChange)
api.on("select", onStoreChange)
const onSelect = React.useCallback((api: CarouselApi) => { return () => {
if (!api) return api.off("reInit", onStoreChange)
setCanScrollPrev(api.canScrollPrev()) api.off("select", onStoreChange)
setCanScrollNext(api.canScrollNext()) }
}, []) },
[api]
)
const canScrollPrev = React.useSyncExternalStore(
subscribe,
() => api?.canScrollPrev() ?? false,
() => false
)
const canScrollNext = React.useSyncExternalStore(
subscribe,
() => api?.canScrollNext() ?? false,
() => false
)
const scrollPrev = React.useCallback(() => { const scrollPrev = React.useCallback(() => {
api?.scrollPrev() api?.scrollPrev()
@@ -93,17 +111,6 @@ function Carousel({
setApi(api) setApi(api)
}, [api, setApi]) }, [api, setApi])
React.useEffect(() => {
if (!api) return
onSelect(api)
api.on("reInit", onSelect)
api.on("select", onSelect)
return () => {
api?.off("select", onSelect)
}
}, [api, onSelect])
return ( return (
<CarouselContext.Provider <CarouselContext.Provider
value={{ value={{
+15
View File
@@ -19,9 +19,24 @@ function DialogTrigger({
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} /> return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
} }
const subscribeToNothing = () => () => {}
function DialogPortal({ function DialogPortal({
...props ...props
}: React.ComponentProps<typeof DialogPrimitive.Portal>) { }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
// Radix's Portal calls createPortal(children, document.body) as soon as it
// renders. During static prerendering there's no `document`, so gate the
// portal on the client snapshot, which is only read in the browser. Closed
// dialogs render nothing on the server anyway, so there's no visual/
// hydration change.
const mounted = React.useSyncExternalStore(
subscribeToNothing,
() => true,
() => false
)
if (!mounted) return null
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} /> return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
} }
+12 -13
View File
@@ -1,19 +1,18 @@
import * as React from "react" import * as React from "react"
const MOBILE_BREAKPOINT = 768 const MOBILE_BREAKPOINT = 768
const MOBILE_QUERY = `(max-width: ${MOBILE_BREAKPOINT - 1}px)`
function subscribe(onStoreChange: () => void) {
const mql = window.matchMedia(MOBILE_QUERY)
mql.addEventListener("change", onStoreChange)
return () => mql.removeEventListener("change", onStoreChange)
}
export function useIsMobile() { export function useIsMobile() {
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined) return React.useSyncExternalStore(
subscribe,
React.useEffect(() => { () => window.matchMedia(MOBILE_QUERY).matches,
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`) () => false
const onChange = () => { )
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
}
mql.addEventListener("change", onChange)
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
return () => mql.removeEventListener("change", onChange)
}, [])
return !!isMobile
} }
+1
View File
@@ -0,0 +1 @@
export function register() {}
+6
View File
@@ -0,0 +1,6 @@
module.exports = {
reactStrictMode: true,
images: {
unoptimized: true,
},
};
-7
View File
@@ -1,7 +0,0 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
};
export default nextConfig;
-6818
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -3,22 +3,22 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev --webpack",
"build": "next build", "build": "next build --webpack",
"start": "next start", "start": "next start",
"lint": "eslint" "lint": "eslint"
}, },
"dependencies": { "dependencies": {
"@base-ui/react": "^1.6.0", "@base-ui/react": "^1.6.0",
"@next/swc-wasm-web": "16.2.10",
"@shadcn/react": "^0.1.0", "@shadcn/react": "^0.1.0",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"cmdk": "^1.1.1", "cmdk": "^1.1.1",
"date-fns": "^4.4.0",
"embla-carousel-react": "^8.6.0", "embla-carousel-react": "^8.6.0",
"input-otp": "^1.4.2", "input-otp": "^1.4.2",
"lucide-react": "^1.22.0", "lucide-react": "^1.22.0",
"next": "16.2.9", "next": "16.2.10",
"next-themes": "^0.4.6", "next-themes": "^0.4.6",
"radix-ui": "^1.6.0", "radix-ui": "^1.6.0",
"react": "19.2.4", "react": "19.2.4",
-1
View File
@@ -1 +0,0 @@
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>

Before

Width:  |  Height:  |  Size: 391 B

-1
View File
@@ -1 +0,0 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

-1
View File
@@ -1 +0,0 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>

Before

Width:  |  Height:  |  Size: 128 B

-1
View File
@@ -1 +0,0 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>

Before

Width:  |  Height:  |  Size: 385 B

+12 -5
View File
@@ -1,7 +1,11 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ES2017", "target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"], "lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"strict": true, "strict": true,
@@ -19,7 +23,9 @@
} }
], ],
"paths": { "paths": {
"@/*": ["./*"] "@/*": [
"./*"
]
} }
}, },
"include": [ "include": [
@@ -27,8 +33,9 @@
"**/*.ts", "**/*.ts",
"**/*.tsx", "**/*.tsx",
".next/types/**/*.ts", ".next/types/**/*.ts",
".next/dev/types/**/*.ts", ".next/dev/types/**/*.ts"
"**/*.mts"
], ],
"exclude": ["node_modules"] "exclude": [
"node_modules"
]
} }
+8267 -5660
View File
File diff suppressed because it is too large Load Diff