Source code for abizareyhan.com: a Finder-inspired portfolio where projects are organized like folders/files, with a preview pane and built-in search.
- 3-pane Finder UI (Folders / Files / Preview) with responsive mobile flow
- Fuzzy search over project name + description, plus tag matching (Fuse.js)
- Deep links via URL hash (
#<folder>/<fileId>) and session restore viasessionStorage - Project previews with optional screenshots, tags, dates, and actions
- External-link confirmation dialog and image preview dialog
- SEO: OpenGraph/Twitter meta + sitemap and robots.txt generation
- Cloudflare Pages deployment via
@cloudflare/next-on-pages+ Wrangler
- Next.js 14 (App Router) + React 18 + TypeScript
- Tailwind CSS + Framer Motion
- Fuse.js (search)
- Cloudflare Pages (
@cloudflare/next-on-pages,wrangler) next-sitemap(sitemap + robots.txt)
src/app/: App Router entry points (layout.tsx,page.tsx,globals.css)src/components/: Finder UI components (FolderList,FileList,FilePreview) and dialogssrc/data/: Content sources (folders, projects, opportunity status messages)src/lib/: Context providers and helpers (SearchContext,DialogContext,getOpportunityStatus, types)public/: Icons, preview images, and generated SEO files (sitemap.xml,robots.txt)
Prerequisites:
- Node.js 18.17+ (recommended: Node 20+)
- npm
Commands:
npm install
npm run devOpen http://localhost:3000.
npm run dev: Next.js dev server (includes@cloudflare/next-on-pages/next-devsetup for local bindings)npm run build: production build (runsnext-sitemapviapostbuild)npm run start: run the Next.js Node server (not the Cloudflare Pages runtime)npm run lint: Next.js lintnpm run pages:build: build for Cloudflare Pages via@cloudflare/next-on-pages(outputs to.vercel/output/static)npm run preview: Pages preview via Wrangler (wrangler pages dev)npm run deploy: deploy to Cloudflare Pages via Wrangler (wrangler pages deploy)npm run cf-typegen: generateCloudflareEnvtypes intoenv.d.ts
npm run lintPrettier is configured in .prettierrc (includes prettier-plugin-tailwindcss).
npx prettier -w .- About dialog:
src/components/dialogs/AboutDialog.tsx - Opportunity status rotation logic:
src/lib/getOpportunityStatus.ts - Region/tone message sets:
src/data/opportunityStatuses.ts
Edit src/data/folders.ts. Folder ids are used by projects to decide which "folder" they appear in.
Edit src/data/projects.ts. Each entry follows src/lib/types/file.ts:
id: unique (used for hash deep links)name: display nameiconPath: icon inpublic/(example:/ic_file_kotlin.png)folder: array of folder ids (a file can be in multiple folders)metadata(optional):description,startDate,endDate,thumbnail,tags,url
Notes:
- Omit
metadata.endDateto showPresent. - If
metadata.urlis set, the preview adds anOpenaction that uses the confirmation dialog. - Thumbnails in
public/are referenced as/img_preview_*.avif. Remote thumbnails used withnext/imagemust be added toimages.remotePatternsinnext.config.mjs. - Deep links are hash-based:
/#<folderId>/<fileId>(example:/#all-projects/gravel-android).
Wrangler config lives in wrangler.toml. This project uses @cloudflare/next-on-pages and deploys the generated output under .vercel/output/static.
# Build for Cloudflare Pages
npm run pages:build
# Preview the Pages runtime locally
npm run preview
# Deploy (Wrangler) - Never run this locally, its triggered by GitHub Actions
npm run deployIf you add Cloudflare bindings (KV/D1/R2/Queues/etc), configure them in wrangler.toml and regenerate types:
npm run cf-typegen- Page metadata and Google Analytics live in
src/app/layout.tsx(see<GoogleAnalytics gaId="..."/>). next-sitemapruns afternpm run build(viapostbuild) and writespublic/sitemap.xmlandpublic/robots.txt.next-sitemap.config.jsalso injects selected blog post URLs fromhttps://blog.abizareyhan.com.
Optional environment variables:
SITE_URL: overridessiteUrlfor sitemap generation (default:https://abizareyhan.com)