Skip to content
Closed
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
91 changes: 91 additions & 0 deletions .agent/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
---
---

# .agent — Development Guidelines

## Stack

- Next.js (App Router, latest stable patch)
- React 19
- TypeScript (strict)
- Convex
- shadcn/ui + Tailwind CSS

---

## Defaults

- **Server Components by default.** Add `'use client'` only at the leaves that need interactivity/hooks.
- **Convex is the source of truth** for server state; local state is for UI-only concerns.
- **Always leverage typescript**; avoid usage of `any` which defeats the point of using Typescript.

---

## Components & Composition

- Prefer **small, focused components**. Split when a component mixes concerns or becomes hard to scan.
- **Presentational components:** props in → JSX out (no data fetching; minimal/no app logic).
- **Feature components:** coordinate data + state + composition.
- Prefer **composition** (children, slots) over giant prop APIs.

---

## State & Side Effects

- Keep state **closest to where it’s used**; lift only for shared ownership.
- Don’t mirror Convex query results into local state. Store **UI state** (selection, filters, draft text), not server data.
- Avoid `useEffect` for app data fetching. Use it for **browser-only side effects** (subscriptions, observers, localStorage).

---

## Convex + Next.js (Best Practice Patterns)

- **Client reactivity:** `useQuery` / `useMutation` in Client Components.
- **SSR + reactivity:** in a Server Component, `preloadQuery(...)` and pass the payload to a Client Component using `usePreloadedQuery(...)`.
- **Server-only (non-reactive) rendering:** `fetchQuery(...)` in Server Components.
- **Server Actions / Route Handlers:** call Convex via `fetchMutation` / `fetchAction` when you must mutate from the server boundary (e.g., form action, webhook, route handler).
- **Optimistic UI:** prefer Convex optimistic updates (`useMutation(...).withOptimisticUpdate(...)`) over duplicating server logic in ad-hoc local state.
- **Consistency:** avoid multiple `preloadQuery` calls on the same page; consolidate queries or design around a single preload.

---

## Next.js App Router Conventions

- Use `loading.tsx` and `error.tsx` for route-segment UX.
- Use `not-found.tsx` for 404 states.
- Prefer colocating components with the route/feature that owns them; extract to shared only when reused.

---

## React 19 Usage

- Prefer **Actions + `useActionState`** for form submission state when using form actions.
- Use `useOptimistic` for instant UI feedback when appropriate.
- Use `useFormStatus` inside design-system components that need form pending state.
- `use` can read a Promise/Context during render (only where Suspense semantics make sense).

---

## TypeScript (Strict, Practical)

- Let inference work; **export explicit prop types** for shared/public components.
- No `any`. Use `unknown` and narrow.
- Avoid `as` casts; prefer `satisfies`, generics, and runtime validation.
- `@ts-expect-error` allowed only with a comment explaining why + link/TODO to remove.

---

## Performance

- Minimize client bundles: keep heavy logic/components server-side when possible.
- Use dynamic imports for non-critical client UI.
- Use `next/image` for images.
- Memoization is opt-in: use `memo`/`useMemo`/`useCallback` only when profiling shows value.

---

## Styling (shadcn/ui + Tailwind)

- Use shadcn/ui as the base; extend via composition + variants.
- Tailwind utilities in JSX; extract repetition into components, not `@apply`.
- Theme via CSS variables; keep design tokens centralized.
1 change: 1 addition & 0 deletions .eslintcache
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"C:\\Code\\pixelstream\\convex\\generatedImages.test.ts":"1","C:\\Code\\pixelstream\\convex\\generatedImages.ts":"2","C:\\Code\\pixelstream\\convex\\generatedImages\\helpers.ts":"3","C:\\Code\\pixelstream\\convex\\generatedImages\\internal.ts":"4","C:\\Code\\pixelstream\\convex\\generatedImages\\mutations.ts":"5","C:\\Code\\pixelstream\\convex\\generatedImages\\queries.ts":"6","C:\\Code\\pixelstream\\convex\\generatedImages\\types.ts":"7"},{"size":12275,"mtime":1768840417883,"results":"8","hashOfConfig":"9"},{"size":888,"mtime":1768838997931,"results":"10","hashOfConfig":"11"},{"size":10454,"mtime":1768838750161,"results":"12","hashOfConfig":"11"},{"size":5249,"mtime":1768838693204,"results":"13","hashOfConfig":"11"},{"size":10478,"mtime":1768838660696,"results":"14","hashOfConfig":"11"},{"size":15924,"mtime":1768838750173,"results":"15","hashOfConfig":"11"},{"size":4411,"mtime":1768838482327,"results":"16","hashOfConfig":"11"},{"filePath":"17","messages":"18","suppressedMessages":"19","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"1hc6ftq",{"filePath":"20","messages":"21","suppressedMessages":"22","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"1ekn74t",{"filePath":"23","messages":"24","suppressedMessages":"25","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"26","messages":"27","suppressedMessages":"28","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"29","messages":"30","suppressedMessages":"31","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"32","messages":"33","suppressedMessages":"34","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"35","messages":"36","suppressedMessages":"37","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"C:\\Code\\pixelstream\\convex\\generatedImages.test.ts",[],[],"C:\\Code\\pixelstream\\convex\\generatedImages.ts",[],[],"C:\\Code\\pixelstream\\convex\\generatedImages\\helpers.ts",[],[],"C:\\Code\\pixelstream\\convex\\generatedImages\\internal.ts",[],[],"C:\\Code\\pixelstream\\convex\\generatedImages\\mutations.ts",[],[],"C:\\Code\\pixelstream\\convex\\generatedImages\\queries.ts",[],[],"C:\\Code\\pixelstream\\convex\\generatedImages\\types.ts",[],[]]
84 changes: 84 additions & 0 deletions .github/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
trigger: always_on
---

# .agent — Development Guidelines

## Stack
- Next.js (App Router, latest stable patch)
- React 19
- TypeScript (strict)
- Convex
- shadcn/ui + Tailwind CSS
- Use `bun run test` to run tests - NOT `bun test`.

---

## Defaults
- **Server Components by default.** Add `'use client'` only at the leaves that need interactivity/hooks.
- **Convex is the source of truth** for server state; local state is for UI-only concerns.
- **Always leverage typescript**; avoid usage of `any` which defeats the point of using Typescript.
- **Ensure** any new or updated code have had their test files created or updated idiomatically using RTL gold standards and vite test usage.

---

## Components & Composition
- Prefer **small, focused components**. Split when a component mixes concerns or becomes hard to scan.
- **Presentational components:** props in → JSX out (no data fetching; minimal/no app logic).
- **Feature components:** coordinate data + state + composition.
- Prefer **composition** (children, slots) over giant prop APIs.

---

## State & Side Effects
- Keep state **closest to where it’s used**; lift only for shared ownership.
- Don’t mirror Convex query results into local state. Store **UI state** (selection, filters, draft text), not server data.
- Avoid `useEffect` for app data fetching. Use it for **browser-only side effects** (subscriptions, observers, localStorage).

---

## Convex + Next.js (Best Practice Patterns)
- **Client reactivity:** `useQuery` / `useMutation` in Client Components.
- **SSR + reactivity:** in a Server Component, `preloadQuery(...)` and pass the payload to a Client Component using `usePreloadedQuery(...)`.
- **Server-only (non-reactive) rendering:** `fetchQuery(...)` in Server Components.
- **Server Actions / Route Handlers:** call Convex via `fetchMutation` / `fetchAction` when you must mutate from the server boundary (e.g., form action, webhook, route handler).
- **Optimistic UI:** prefer Convex optimistic updates (`useMutation(...).withOptimisticUpdate(...)`) over duplicating server logic in ad-hoc local state.
- **Consistency:** avoid multiple `preloadQuery` calls on the same page; consolidate queries or design around a single preload.

---

## Next.js App Router Conventions
- Use `loading.tsx` and `error.tsx` for route-segment UX.
- Use `not-found.tsx` for 404 states.
- Prefer colocating components with the route/feature that owns them; extract to shared only when reused.

---

## React 19 Usage
- Prefer **Actions + `useActionState`** for form submission state when using form actions.
- Use `useOptimistic` for instant UI feedback when appropriate.
- Use `useFormStatus` inside design-system components that need form pending state.
- `use` can read a Promise/Context during render (only where Suspense semantics make sense).

---

## TypeScript (Strict, Practical)
- Let inference work; **export explicit prop types** for shared/public components.
- No `any`. Use `unknown` and narrow.
- Avoid `as` casts; prefer `satisfies`, generics, and runtime validation.
- `@ts-expect-error` allowed only with a comment explaining why + link/TODO to remove.

---

## Performance
- Minimize client bundles: keep heavy logic/components server-side when possible.
- Use dynamic imports for non-critical client UI.
- Use `next/image` for images.
- Memoization is opt-in: use `memo`/`useMemo`/`useCallback` only when profiling shows value.

---

## Styling (shadcn/ui + Tailwind)
- Use shadcn/ui as the base; extend via composition + variants.
- Tailwind utilities in JSX; extract repetition into components, not `@apply`.
- Theme via CSS variables; keep design tokens centralized.
143 changes: 143 additions & 0 deletions .github/skills/adding-convex-table/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
---
name: adding-convex-table
description: |
Adds a new table to Convex schema with indexes.
Input: Table name, fields, and query patterns.
Output: Updated schema.ts with new table definition.
---

# Adding a Convex Table

Adds a new table to `convex/schema.ts` with proper indexes for query performance.

## Preconditions

- Table doesn't already exist in schema
- User has provided: table name, fields, and expected query patterns

## Algorithm

```markdown
1. Read current schema:
- [ ] Open `convex/schema.ts`
- [ ] Identify existing tables for pattern reference

2. Define table:
- [ ] Add `defineTable()` with field validators
- [ ] Use appropriate `v.*` types for each field
- [ ] Add `userId` if user-scoped data

3. Add indexes:
- [ ] Add `.index()` for each field used in queries
- [ ] Name indexes descriptively: `by_[field]` or `by_[field1]_and_[field2]`

4. Verify:
- [ ] Save file — Convex auto-pushes in dev
- [ ] Check Convex dashboard for new table
- [ ] Run `bun run build` to regenerate types
```

## Validator Reference

| Type | Validator | Example |
|------|-----------|---------|
| String | `v.string()` | `name: v.string()` |
| Number | `v.number()` | `count: v.number()` |
| Boolean | `v.boolean()` | `isActive: v.boolean()` |
| Optional | `v.optional(v.string())` | `bio: v.optional(v.string())` |
| ID reference | `v.id("tableName")` | `userId: v.id("users")` |
| Union | `v.union(v.literal("a"), v.literal("b"))` | `status: v.union(...)` |
| Array | `v.array(v.string())` | `tags: v.array(v.string())` |
| Object | `v.object({ ... })` | Nested objects |
| Any (avoid) | `v.any()` | Only if truly dynamic |

## Template

```typescript
// convex/schema.ts
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";

export default defineSchema({
// Existing tables...

// NEW TABLE
myNewTable: defineTable({
// Required fields
userId: v.string(),
title: v.string(),

// Optional fields
description: v.optional(v.string()),

// Enum/status fields
status: v.union(
v.literal("draft"),
v.literal("published"),
v.literal("archived")
),

// Timestamps
createdAt: v.number(),
updatedAt: v.optional(v.number()),
})
// Indexes for queries
.index("by_user", ["userId"])
.index("by_user_and_status", ["userId", "status"])
.index("by_created", ["createdAt"]),
});
```

## Existing Tables (Reference)

| Table | Key Fields | Indexes |
|-------|------------|---------|
| `users` | `email`, `stripeCustomerId` | `by_email`, `by_clerk_id` |
| `generatedImages` | `userId`, `model`, `status` | `by_user`, `by_user_and_status` |
| `favorites` | `userId`, `imageId` | `by_user`, `by_image` |
| `follows` | `followerId`, `followedId` | `by_follower`, `by_followed` |
| `promptLibrary` | `userId`, `prompt` | `by_user` |
| `referenceImages` | `userId`, `storageId` | `by_user` |

## Index Strategy

| Query Pattern | Index Needed |
|--------------|--------------|
| Filter by single field | `.index("by_field", ["field"])` |
| Filter by multiple fields | `.index("by_a_and_b", ["a", "b"])` |
| Sort by field | Same as filter (Convex sorts on index) |
| Unique lookup | `.index("by_field", ["field"])` |

## Guardrails

- **Always index** fields used in `.withIndex()` or `.filter()`
- **User-scoped data** must have `userId` field + `by_user` index
- **Timestamps** use `v.number()` (Unix ms), not Date
- **Never use raw SQL** — Convex handles persistence
- **If schema push fails**, report error and stop

## Output Format

```markdown
## Summary
Added table `[tableName]` for [purpose].

## Schema
```typescript
myTable: defineTable({
field1: v.string(),
field2: v.number(),
}).index("by_field1", ["field1"])
```

## Indexes
- `by_field1` — For querying by field1

## Verification
- Schema pushed ✅
- Types regenerated ✅
- Table visible in dashboard ✅

## Next Steps
- Create queries/mutations in `convex/[tableName].ts`
```
Loading
Loading