## AI Master Prompt for PixelLegacy MVP
**PROJECT OVERVIEW:**
The PixelLegacy application aims to be a comprehensive digital museum and discussion platform dedicated to the art and history of retro computer demo scene graphics. It addresses the problem of preserving, understanding, and contextualizing the unique artistic and technical achievements of the demo scene, particularly concerning graphic design. Many original artists and enthusiasts struggle with the nuances of copyright, inspiration, and originality in this niche, especially with the advent of AI art generation tools. PixelLegacy will serve as a repository for iconic retro graphics, a historical archive of techniques and artists, and a forum for discussing the ethics of art creation and appropriation, both historically and in the modern AI era.
**Core Value Proposition:** To be the definitive online resource for understanding, appreciating, and discussing the visual heritage of the demo scene, bridging the gap between historical pixel art and contemporary digital creation.
**TECH STACK:**
- **Frontend Framework:** Next.js (App Router)
- **UI Library:** shadcn/ui (built on Radix UI and Tailwind CSS)
- **Styling:** Tailwind CSS
- **State Management:** React Context API and Server Components for data fetching, potentially Zustand or Jotai for complex client-side state if needed.
- **Database:** PostgreSQL
- **ORM:** Drizzle ORM (with drizzle-kit for migrations)
- **Authentication:** NextAuth.js (e.g., using Credentials Provider and OAuth providers like Google/GitHub)
- **Image Optimization:** Next.js Image component
- **Deployment:** Vercel
- **Other:** React Hook Form for forms, Zod for schema validation, date-fns for date manipulation.
**DATABASE SCHEMA (Drizzle ORM - PostgreSQL):**
```typescript
// schema.ts
import { pgTable, uuid, varchar, text, timestamp, integer, boolean, pgEnum } from 'drizzle-orm/pg-core';
import { relations } from 'drizzle-orm';
// User Roles Enum
export const userRoleEnum = pgEnum('user_role', ['admin', 'editor', 'viewer']);
// Users Table
export const users = pgTable('users', {
id: uuid('id').primaryKey().defaultRandom(),
name: varchar('name'),
email: varchar('email', { length: 255 }).unique().notNull(),
emailVerified: timestamp('emailVerified', { mode: 'date' }),
image: varchar('image'),
role: userRoleEnum('role').default('viewer'),
createdAt: timestamp('createdAt').defaultNow(),
updatedAt: timestamp('updatedAt').defaultNow(),
});
// User Relations
export const usersRelations = relations(users, ({
posts,
comments,
favorites
}) => ({
posts: posts.map(el => el.author),
comments: comments.map(el => el.author),
favorites: favorites.map(el => el.user)
}));
// Graphics Table
export const graphics = pgTable('graphics', {
id: uuid('id').primaryKey().defaultRandom(),
title: varchar('title', { length: 255 }).notNull(),
description: text('description'),
imageUrl: varchar('imageUrl', { length: 1024 }).notNull(),
sourcePlatform: varchar('sourcePlatform', { length: 100 }), // e.g., 'Amiga', 'C64', 'PC Demo'
year: integer('year'),
artistId: uuid('artistId').references(() => artists.id, { onDelete: 'set null' }),
tags: text('tags').array(), // Array of strings for tags
isNsfw: boolean('isNsfw').default(false),
createdAt: timestamp('createdAt').defaultNow(),
updatedBy: uuid('updatedBy').references(() => users.id, { onDelete: 'set null' }),
});
// Graphics Relations
export const graphicsRelations = relations(graphics, ({
artist,
comments,
favorites
}) => ({
artist: artist.current,
comments: comments.map(el => el.graphic),
favorites: favorites.map(el => el.graphic)
}));
// Artists Table
export const artists = pgTable('artists', {
id: uuid('id').primaryKey().defaultRandom(),
name: varchar('name', { length: 255 }).notNull(),
bio: text('bio'),
country: varchar('country', { length: 100 }),
activeYears: varchar('activeYears', { length: 50 }), // e.g., '1988-1995'
slug: varchar('slug', { length: 255 }).unique().notNull(), // For friendly URLs
});
// Artist Relations
export const artistsRelations = relations(artists, ({
graphics,
posts
}) => ({
graphics: graphics.map(el => el.artist),
posts: posts.map(el => el.authorArtist)
}));
// Posts Table (for articles, discussions)
export const posts = pgTable('posts', {
id: uuid('id').primaryKey().defaultRandom(),
title: varchar('title', { length: 255 }).notNull(),
slug: varchar('slug', { length: 255 }).unique().notNull(),
content: text('content').notNull(),
authorId: uuid('authorId').references(() => users.id, { onDelete: 'set null' }),
authorArtistId: uuid('authorArtistId').references(() => artists.id, { onDelete: 'set null' }), // Optional: link to an artist profile
publishedAt: timestamp('publishedAt', { mode: 'date' }),
isPublished: boolean('isPublished').default(false),
createdAt: timestamp('createdAt').defaultNow(),
updatedAt: timestamp('updatedAt').defaultNow(),
});
// Post Relations
export const postsRelations = relations(posts, ({
author,
authorArtist,
comments,
tags
}) => ({
author: author.current,
authorArtist: authorArtist.current,
comments: comments.map(el => el.post),
tags: tags.map(el => el.post)
}));
// Comments Table
export const comments = pgTable('comments', {
id: uuid('id').primaryKey().defaultRandom(),
content: text('content').notNull(),
authorId: uuid('authorId').references(() => users.id, { onDelete: 'cascade' }).notNull(),
graphicId: uuid('graphicId').references(() => graphics.id, { onDelete: 'cascade' }), // Comment on a graphic
postId: uuid('postId').references(() => posts.id, { onDelete: 'cascade' }), // Comment on a post
createdAt: timestamp('createdAt').defaultNow(),
});
// Comment Relations
export const commentsRelations = relations(comments, ({
author,
graphic,
post
}) => ({
author: author.current,
graphic: graphic.current,
post: post.current
}));
// Favorites Table (for user's liked graphics/posts)
export const favorites = pgTable('favorites', {
userId: uuid('userId').references(() => users.id, { onDelete: 'cascade' }).notNull(),
graphicId: uuid('graphicId').references(() => graphics.id, { onDelete: 'cascade' }),
postId: uuid('postId').references(() => posts.id, { onDelete: 'cascade' }),
createdAt: timestamp('createdAt').defaultNow(),
// Ensure a user can only favorite a graphic OR a post, not both on the same row
// and that they favorite at least one.
constraint: 'UNIQUE(userId, graphicId, postId)'
});
// Favorite Relations
export const favoritesRelations = relations(favorites, ({
user,
graphic,
post
}) => ({
user: user.current,
graphic: graphic.current,
post: post.current
}));
// Post Tags Table (Many-to-Many relationship)
export const postTags = pgTable('post_tags', {
postId: uuid('postId').references(() => posts.id, { onDelete: 'cascade' }).notNull(),
tag: varchar('tag', { length: 100 }).notNull(),
});
// Post Tag Relations
export const postTagsRelations = relations(postTags, ({ post }) => ({
post: post.current,
}));
// Enum for AI Tool Usage (Example for future features)
export const aiToolUsageEnum = pgEnum('ai_tool_usage', ['inspiration', 'direct_generation', 'style_transfer', 'none']);
// --- Full Relations --- (Example for User)
export const userRelations = relations(users, ({ posts, comments, favorites }) => ({
posts: posts.map(el => el.author),
comments: comments.map(el => el.author),
favorites: favorites.map(el => el.user)
}));
// Add similar full relations for other tables if needed for complex queries.
// --- Example of a relation to itself (e.g., User following User) if needed in the future ---
// export const follows = pgTable('follows', {
// followerId: uuid('followerId').references(() => users.id, { onDelete: 'cascade' }).notNull(),
// followingId: uuid('followingId').references(() => users.id, { onDelete: 'cascade' }).notNull(),
// });
// export const followsRelations = relations(follows, ({ followerId, followingId }) => ({
// follower: followerId.fetch().map(u => u.following),
// following: followingId.fetch().map(u => u.follower)
// }));
```
**CORE FEATURES & USER FLOW:**
1. **User Authentication (NextAuth.js):**
* **Flow:** User lands on the homepage. Clicks 'Sign In'. Redirected to a sign-in page offering email/password (via Credentials Provider) and OAuth (Google, GitHub). Upon successful authentication, user is redirected to their dashboard or the homepage.
* **Protected Routes:** Access to posting comments, creating posts, and managing profile requires authentication. Some content might be free, but interactions are protected.
* **Session Management:** JWT-based session management handled by NextAuth.js.
2. **Retro Graphics Archive Browsing:**
* **Flow:** Homepage displays featured graphics and recent additions. Users can navigate to the 'Archive' page. Archive page shows a paginated grid of graphics. Users can filter by platform (Amiga, C64, etc.), year, artist, and search by keywords. Clicking a graphic opens a detailed view page.
* **Detail View:** Shows the graphic, title, description, artist (linked), platform, year, and comments section. Users can favorite the graphic.
* **Edge Case:** Empty archive state should display a friendly message and possibly a CTA to contribute.
3. **Artist Profiles:**
* **Flow:** Accessible via a dedicated 'Artists' page or by clicking an artist's name on a graphic or post. Artist profiles display bio, country, active years, associated graphics, and related posts. A unique slug (`/artists/[slug]`) will be used for each profile URL.
* **Edge Case:** Artists without much data should still have a basic profile page.
4. **Content Creation (Posts/Articles):**
* **Flow:** Authenticated users can navigate to 'Create Post'. A rich text editor (e.g., TipTap or a simple Markdown editor) allows content creation. Users can assign a title, tags, and optionally link to an artist profile. Posts can be saved as drafts or published. Published posts appear in the 'Blog/Discussions' section.
* **Admin/Editor Roles:** Specific roles might have elevated privileges for publishing or editing others' posts.
* **Validation:** Title, content are required. Slug generation should be automatic based on title, with manual override.
5. **Commenting System:**
* **Flow:** On graphic detail pages and post pages, authenticated users can see a comment input field. Users type their comment, submit. Comments are displayed chronologically below the content.
* **Validation:** Comment content is required.
* **Moderation:** Basic moderation flags or admin deletion capabilities will be implemented.
6. **Favoriting/Bookmarking:**
* **Flow:** Users can click a 'favorite' icon (e.g., a star or heart) on graphics and posts. The item is added to their personal 'Favorites' list, accessible from their user profile dropdown.
* **State:** The icon should visually indicate if the item is favorited by the current user.
**API & DATA FETCHING:**
- **Architecture:** Utilize Next.js App Router with Server Components for data fetching wherever possible to leverage caching and reduce client-side JS. API routes (within `app/api/`) will be used for mutations (POST, PUT, DELETE) and dynamic data fetching not suitable for Server Components.
- **Data Fetching:** Use `fetch` extended with caching options or directly query the database within Server Components for read operations. For mutations, use API routes that interact with Drizzle ORM.
- **Example API Route (POST /api/graphics/:id/comment):**
* **Request Body:** `{ content: string }`
* **Authentication:** Requires user session.
* **Logic:** Validates input (Zod), checks if user is authenticated, finds graphic, creates comment using Drizzle, returns success/error.
* **Response:** `{ success: true, comment: { ... } }` or `{ success: false, error: '...' }`
- **Example Server Component Data Fetching (`app/archive/page.tsx`):**
```tsx
import { db } from '@/lib/db'; // Your Drizzle DB instance
import { graphics } from '@/db/schema';
import { eq } from 'drizzle-orm';
import Image from 'next/image';
async function GraphicsGrid() {
const graphicsData = await db.query.graphics.findMany({
limit: 20,
orderBy: (graphics.createdAt, 'desc'),
with: { artist: true } // Join artist data
});
return (
<div className="grid grid-cols-3 gap-4">
{graphicsData.map(graphic => (
<div key={graphic.id}>
<Image src={graphic.imageUrl} alt={graphic.title} width={300} height={200} />
<h3>{graphic.title}</h3>
<p>{graphic.artist?.name || 'Unknown'}</p>
</div>
))}
</div>
);
}
```
**COMPONENT BREAKDOWN (Next.js App Router Structure):**
- **`app/`**
* **`layout.tsx`:** Root layout (html, body, providers, main navigation).
* **`page.tsx`:** Homepage (Featured graphics, recent posts, intro). Server Component.
* **`archive/`**
* **`page.tsx`:** Main archive page (filters, search, grid of graphics). Server Component primarily, with client-side filtering logic.
* **`[graphicId]/`**
* **`page.tsx`:** Individual graphic detail page (image, info, comments). Server Component with Client Components for comments/forms.
* **`artists/`**
* **`page.tsx`:** List of all artists. Server Component.
* **`[slug]/`**
* **`page.tsx`:** Individual artist profile page. Server Component.
* **`posts/`**
* **`page.tsx`:** Blog/Discussions index page. Server Component.
* **`[slug]/`**
* **`page.tsx`:** Individual post page. Server Component with Client Components for comments/forms.
* **`new/`** (Protected Route)
* **`page.tsx`:** New post creation form. Client Component.
* **`edit/[postId]/`** (Protected Route)
* **`page.tsx`:** Post editing form. Client Component.
* **`auth/`**
* **`signin/page.tsx`:** Sign-in page. Client Component.
* **`signup/page.tsx`:** Sign-up page (if using email/pass). Client Component.
* **`profile/`** (Protected Route)
* **`page.tsx`:** User profile page (view/edit details, view favorites). Server Component with Client Components for forms.
* **`api/`**
* **`auth/[...nextauth]/route.ts`:** NextAuth.js handler.
* **`graphics/[graphicId]/comment/route.ts`:** API route for posting comments on graphics.
* **`posts/[postId]/comment/route.ts`:** API route for posting comments on posts.
* **`favorites/route.ts`:** API route for adding/removing favorites.
- **`components/`**
* **`ui/`:** Re-exported shadcn/ui components (Button, Input, Card, Dialog, etc.).
* **`layout/`:** `Navbar`, `Footer`, `Sidebar` (if needed).
* **`graphics/`:** `GraphicCard`, `GraphicDetailImage`, `CommentSection`, `CommentForm`.
* **`artists/`:** `ArtistCard`, `ArtistProfileHeader`.
* **`posts/`:** `PostCard`, `PostContentRenderer`.
* **`auth/`:** `SignInForm`, `OAuthButtons`.
* **`common/`:** `Spinner`, `ErrorMessage`, `Pagination`.
**UI/UX DESIGN & VISUAL IDENTITY:**
- **Style:** "Retro Futurism Meets Minimalism". A blend of the chunky pixels and vibrant, often limited, color palettes of 8-bit and 16-bit eras, combined with modern, clean UI design principles. Think of a highly polished, functional interface that subtly references retro aesthetics.
- **Color Palette:**
* Primary (Dark Mode Focused): `#1A1A1A` (Deep Nearly Black)
* Secondary (Accent): `#00FF00` (Electric Green, reminiscent of old monitors/code) OR `#FF00FF` (Magenta)
* Tertiary (Muted Accents/Borders): `#333333` (Dark Gray)
* Background Highlights: `#2A2A2A`
* Text (Primary): `#E0E0E0` (Light Gray)
* Text (Secondary): `#A0A0A0` (Medium Gray)
* Call to Actions (Subtle): A slightly lighter shade of the accent, e.g., `#33FF33`.
- **Typography:**
* Headings: A slightly stylized, perhaps monospaced or pixel-inspired font (e.g., 'Press Start 2P' for major titles, or a clean sans-serif like Inter for UI consistency).
* Body Text: Inter (or a similar highly readable sans-serif).
- **Layout:** Clean, card-based layouts for grids. Generous whitespace. Clear visual hierarchy. Responsive design using Tailwind's breakpoints.
- **Visual Elements:** Subtle use of pixelated borders or background patterns where appropriate, but not overwhelming. Loading spinners could have a retro animation. Hover effects on cards should be smooth but noticeable.
- **Responsive Rules:** Mobile-first approach. Navigation collapses into a hamburger menu. Grids adjust column counts (e.g., 1 column on mobile, 2 on tablet, 3-4 on desktop). Ensure touch targets are sufficiently large.
**SAMPLE/MOCK DATA:**
**Graphics:**
1. `{ id: 'uuid-1', title: 'Cyber Dragon', description: 'A classic sci-fi dragon rendered in 32 colors.', imageUrl: '/images/mock/cyber-dragon.png', sourcePlatform: 'Amiga', year: 1991, artistId: 'uuid-artist-1', tags: ['fantasy', 'sci-fi', 'dragon'], isNsfw: false }`
2. `{ id: 'uuid-2', title: 'Cityscape at Night', description: 'Vibrant neon lights and towering buildings.', imageUrl: '/images/mock/cityscape.png', sourcePlatform: 'C64', year: 1987, artistId: 'uuid-artist-2', tags: ['scifi', 'city', 'landscape'], isNsfw: false }`
3. `{ id: 'uuid-3', title: 'Abstract Flow', description: 'An early abstract piece exploring color blending.', imageUrl: '/images/mock/abstract.png', sourcePlatform: 'Atari ST', year: 1989, artistId: 'uuid-artist-3', tags: ['abstract', 'color'], isNsfw: false }`
**Artists:**
1. `{ id: 'uuid-artist-1', name: 'PixelMaster', bio: 'Pioneering Amiga graphics artist known for detailed fantasy scenes.', country: 'Germany', activeYears: '1989-1994', slug: 'pixelmaster' }`
2. `{ id: 'uuid-artist-2', name: 'VectorVizion', bio: 'Early C64 artist experimenting with parallax scrolling and vibrant palettes.', country: 'USA', activeYears: '1986-1990', slug: 'vectorvizion' }`
3. `{ id: 'uuid-artist-3', name: 'ChromaFlow', bio: 'Known for experimental abstract pixel art.', country: 'UK', activeYears: '1990-1993', slug: 'chromaflow' }`
**Posts:**
1. `{ id: 'uuid-post-1', title: 'The Art of Hand-Pixelling', slug: 'art-of-hand-pixelling', content: 'Detailed breakdown of manual pixel creation techniques...', authorId: 'uuid-user-1', publishedAt: '2024-01-15T10:00:00Z', isPublished: true, tags: ['tutorial', 'pixel art', 'technique'] }`
2. `{ id: 'uuid-post-2', title: 'Copying vs. Inspiration: A Demo Scene Debate', slug: 'copying-vs-inspiration', content: 'Exploring the historical context of art reuse...', authorId: 'uuid-user-2', publishedAt: '2024-01-20T11:30:00Z', isPublished: true, tags: ['ethics', 'copyright', 'history'] }`
**Comments:**
1. `{ id: 'uuid-comment-1', content: 'Amazing detail in this piece!', authorId: 'uuid-user-3', graphicId: 'uuid-1', createdAt: '2024-01-22T09:00:00Z' }`
2. `{ id: 'uuid-comment-2', content: 'Reminds me of the early days!', authorId: 'uuid-user-1', postId: 'uuid-post-2', createdAt: '2024-01-23T14:00:00Z' }`
**Users:**
1. `{ id: 'uuid-user-1', name: 'RetroFan', email: 'retro@example.com', role: 'viewer' }`
2. `{ id: 'uuid-user-2', name: 'ArtHistorian', email: 'history@example.com', role: 'editor' }`
3. `{ id: 'uuid-user-3', name: 'PixelPusher', email: 'pusher@example.com', role: 'viewer' }`
**ANIMATIONS:**
- **Page Transitions:** Subtle fade-in/out or slide animations between pages using Next.js's built-in capabilities or a library like `Framer Motion` (if complexity warrants).
- **Component Transitions:** Smooth transitions for elements appearing/disappearing (e.g., filter results, comment loading). Use `react-transition-group` or Framer Motion.
- **Hover Effects:** Slight scale-up or shadow effect on `GraphicCard` and `PostCard` on hover.
- **Loading States:** Use shadcn/ui's `Skeleton` component or spinners (`lucide-react` icons) for data loading. Animate spinners with a retro feel (e.g., pulsing or blocky rotation).
- **Button Interactions:** Subtle click feedback (slight scale down or color change).
**EDGE CASES:**
- **Authentication:** Handle scenarios where session expires, user tries to access protected routes without login (redirect to signin), and OAuth callback errors.
- **Data Validation:** Use Zod for all incoming API request data and form inputs. Provide clear, user-friendly error messages.
- **Empty States:** Design informative and visually appealing empty states for archives, artist lists, user profiles, and comment sections (e.g., "No graphics found matching your criteria. Try broadening your search.").
- **Error Handling:** Global error boundaries and specific try/catch blocks in data fetching and mutations. Display user-friendly error messages via toasts or dedicated error components. Log errors to a service like Sentry.
- **Image Loading:** Implement proper lazy loading and placeholder/error handling for images using `next/image`.
- **Rate Limiting:** Consider basic rate limiting on API routes to prevent abuse.
- **Authorization:** Ensure users can only edit their own profile/posts, and admins have broader control. Implement role-based access control (RBAC) for features like post publishing.
- **Database Errors:** Gracefully handle potential database connection issues or query failures.