vibe-ship-it 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +133 -0
- package/bin/init.js +135 -0
- package/package.json +30 -0
- package/template/CLAUDE.md +71 -0
- package/template/_claude/commands/check.md +6 -0
- package/template/_claude/commands/save.md +9 -0
- package/template/_claude/commands/ship.md +8 -0
- package/template/_claude/commands/stuck.md +10 -0
- package/template/_claude/commands/wow.md +10 -0
- package/template/_github/agents/assistant.agent.md +60 -0
- package/template/_github/agents/checker.agent.md +93 -0
- package/template/_github/agents/investigator.agent.md +196 -0
- package/template/_github/agents/shipper.agent.md +180 -0
- package/template/_github/copilot-instructions.md +60 -0
- package/template/skills/add-login/SKILL.md +204 -0
- package/template/skills/before-you-ship/SKILL.md +132 -0
- package/template/skills/build-page/SKILL.md +137 -0
- package/template/skills/make-it-wow/SKILL.md +139 -0
- package/template/skills/noob-mode/SKILL.md +135 -0
- package/template/skills/platforms/web-nextjs/SKILL.md +160 -0
- package/template/skills/platforms/web-nextjs/references/tailwind-shortcuts.md +97 -0
- package/template/skills/quick-check/SKILL.md +89 -0
- package/template/skills/save-data/SKILL.md +155 -0
- package/template/skills/send-email/SKILL.md +164 -0
- package/template/skills/show-data/SKILL.md +209 -0
- package/template/skills/unstuck/SKILL.md +105 -0
- package/template/skills/upload-file/SKILL.md +188 -0
- package/template/skills/what-am-i-building/SKILL.md +129 -0
- package/template/skills/what-am-i-building/references/platform-routing.md +66 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: show-data
|
|
3
|
+
description: "Displays saved data as lists, tables, cards, or dashboards. Triggers: 'show me all the', 'list all', 'dashboard', 'display', 'table of', 'view submissions', 'view entries', 'see the data', 'admin page', 'show bookings', 'show messages', 'show orders', 'data page', 'overview'."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Show Data
|
|
7
|
+
|
|
8
|
+
Displays saved data in a styled, readable format. Turns raw database rows into cards, tables, or dashboard views.
|
|
9
|
+
|
|
10
|
+
## Process
|
|
11
|
+
|
|
12
|
+
### Step 1: Identify What to Show
|
|
13
|
+
|
|
14
|
+
From context, determine:
|
|
15
|
+
- Which table/data source (what did `save-data` create?)
|
|
16
|
+
- Who sees it (public visitors or admin only?)
|
|
17
|
+
- How many items expected (handful or hundreds?)
|
|
18
|
+
|
|
19
|
+
### Step 2: Choose Display Format
|
|
20
|
+
|
|
21
|
+
| Best for | Format | Use when |
|
|
22
|
+
|---|---|---|
|
|
23
|
+
| Few items with details | Cards | Portfolio items, testimonials, team members |
|
|
24
|
+
| Many items to scan | Table | Admin view of submissions, orders, bookings |
|
|
25
|
+
| Stats overview | Dashboard | Counts, recent activity, key numbers |
|
|
26
|
+
| Timeline | List | Activity log, blog posts, updates |
|
|
27
|
+
|
|
28
|
+
Don't ask the designer which format — pick the best one for their data. They'll say "actually, can it be cards instead?" if they want something different.
|
|
29
|
+
|
|
30
|
+
### Step 3: Build the View
|
|
31
|
+
|
|
32
|
+
**Server component that fetches data** (Next.js App Router):
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
import { createClient } from '@/utils/supabase/server'
|
|
36
|
+
|
|
37
|
+
export default async function DashboardPage() {
|
|
38
|
+
const supabase = await createClient()
|
|
39
|
+
const { data: items } = await supabase
|
|
40
|
+
.from('table_name')
|
|
41
|
+
.select('*')
|
|
42
|
+
.order('created_at', { ascending: false })
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<div className="max-w-4xl mx-auto px-4 py-12">
|
|
46
|
+
<h1 className="text-3xl font-bold mb-8">Your [Items]</h1>
|
|
47
|
+
|
|
48
|
+
{items?.length === 0 ? (
|
|
49
|
+
<EmptyState />
|
|
50
|
+
) : (
|
|
51
|
+
<div className="space-y-4">
|
|
52
|
+
{items?.map((item) => (
|
|
53
|
+
<ItemCard key={item.id} item={item} />
|
|
54
|
+
))}
|
|
55
|
+
</div>
|
|
56
|
+
)}
|
|
57
|
+
</div>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Step 4: Empty State
|
|
63
|
+
|
|
64
|
+
Never show a blank page. Always show an empty state:
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
function EmptyState() {
|
|
68
|
+
return (
|
|
69
|
+
<div className="text-center py-16">
|
|
70
|
+
<p className="text-gray-400 text-lg">No entries yet.</p>
|
|
71
|
+
<p className="text-gray-400 text-sm mt-2">
|
|
72
|
+
When someone submits the form, their entry will appear here.
|
|
73
|
+
</p>
|
|
74
|
+
</div>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Step 5: Test
|
|
80
|
+
|
|
81
|
+
> "Check your dashboard page. If you submitted test data through your form earlier, you'll see it here. If not, go submit the form, then refresh the dashboard."
|
|
82
|
+
|
|
83
|
+
## Display Templates
|
|
84
|
+
|
|
85
|
+
### Card Layout (for rich content)
|
|
86
|
+
```tsx
|
|
87
|
+
function ItemCard({ item }: { item: any }) {
|
|
88
|
+
return (
|
|
89
|
+
<div className="bg-white border rounded-xl p-6 shadow-sm">
|
|
90
|
+
<div className="flex justify-between items-start">
|
|
91
|
+
<div>
|
|
92
|
+
<h3 className="font-semibold text-lg">{item.name}</h3>
|
|
93
|
+
<p className="text-gray-500 text-sm">{item.email}</p>
|
|
94
|
+
</div>
|
|
95
|
+
<span className="text-gray-400 text-sm">
|
|
96
|
+
{new Date(item.created_at).toLocaleDateString()}
|
|
97
|
+
</span>
|
|
98
|
+
</div>
|
|
99
|
+
<p className="text-gray-700 mt-3">{item.message}</p>
|
|
100
|
+
</div>
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Table Layout (for scanning)
|
|
106
|
+
```tsx
|
|
107
|
+
<div className="overflow-x-auto">
|
|
108
|
+
<table className="w-full text-left">
|
|
109
|
+
<thead>
|
|
110
|
+
<tr className="border-b text-sm text-gray-500">
|
|
111
|
+
<th className="py-3 px-4 font-medium">Name</th>
|
|
112
|
+
<th className="py-3 px-4 font-medium">Email</th>
|
|
113
|
+
<th className="py-3 px-4 font-medium">Date</th>
|
|
114
|
+
</tr>
|
|
115
|
+
</thead>
|
|
116
|
+
<tbody>
|
|
117
|
+
{items?.map((item) => (
|
|
118
|
+
<tr key={item.id} className="border-b hover:bg-gray-50">
|
|
119
|
+
<td className="py-3 px-4">{item.name}</td>
|
|
120
|
+
<td className="py-3 px-4 text-gray-500">{item.email}</td>
|
|
121
|
+
<td className="py-3 px-4 text-gray-400 text-sm">
|
|
122
|
+
{new Date(item.created_at).toLocaleDateString()}
|
|
123
|
+
</td>
|
|
124
|
+
</tr>
|
|
125
|
+
))}
|
|
126
|
+
</tbody>
|
|
127
|
+
</table>
|
|
128
|
+
</div>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Dashboard Stats (for overview)
|
|
132
|
+
```tsx
|
|
133
|
+
async function DashboardStats() {
|
|
134
|
+
const supabase = await createClient()
|
|
135
|
+
const { count: total } = await supabase
|
|
136
|
+
.from('table_name')
|
|
137
|
+
.select('*', { count: 'exact', head: true })
|
|
138
|
+
|
|
139
|
+
const { count: today } = await supabase
|
|
140
|
+
.from('table_name')
|
|
141
|
+
.select('*', { count: 'exact', head: true })
|
|
142
|
+
.gte('created_at', new Date().toISOString().split('T')[0])
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<div className="grid grid-cols-2 md:grid-cols-3 gap-4 mb-8">
|
|
146
|
+
<StatCard label="Total" value={total ?? 0} />
|
|
147
|
+
<StatCard label="Today" value={today ?? 0} />
|
|
148
|
+
</div>
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function StatCard({ label, value }: { label: string; value: number }) {
|
|
153
|
+
return (
|
|
154
|
+
<div className="bg-white border rounded-xl p-6">
|
|
155
|
+
<p className="text-sm text-gray-500">{label}</p>
|
|
156
|
+
<p className="text-3xl font-bold mt-1">{value}</p>
|
|
157
|
+
</div>
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Access Control
|
|
163
|
+
|
|
164
|
+
If this is an admin-only view:
|
|
165
|
+
- Ensure the route is protected by middleware (from `add-login` skill)
|
|
166
|
+
- Add sign-out button on the page
|
|
167
|
+
- Show the user's email: "Signed in as you@email.com"
|
|
168
|
+
|
|
169
|
+
If this is public (like a portfolio or directory):
|
|
170
|
+
- No auth needed
|
|
171
|
+
- Make sure the Supabase RLS policy allows public reads
|
|
172
|
+
|
|
173
|
+
## Real-Time Updates (Optional)
|
|
174
|
+
|
|
175
|
+
If the designer wants to see new entries appear without refreshing:
|
|
176
|
+
```tsx
|
|
177
|
+
'use client'
|
|
178
|
+
|
|
179
|
+
import { createClient } from '@/utils/supabase/client'
|
|
180
|
+
import { useEffect, useState } from 'react'
|
|
181
|
+
|
|
182
|
+
// Subscribe to new entries
|
|
183
|
+
useEffect(() => {
|
|
184
|
+
const supabase = createClient()
|
|
185
|
+
const channel = supabase.channel('entries')
|
|
186
|
+
.on('postgres_changes', {
|
|
187
|
+
event: 'INSERT',
|
|
188
|
+
schema: 'public',
|
|
189
|
+
table: 'table_name',
|
|
190
|
+
}, (payload) => {
|
|
191
|
+
setItems(prev => [payload.new, ...prev])
|
|
192
|
+
})
|
|
193
|
+
.subscribe()
|
|
194
|
+
|
|
195
|
+
return () => { supabase.removeChannel(channel) }
|
|
196
|
+
}, [])
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Only add if they ask for it. Don't over-engineer.
|
|
200
|
+
|
|
201
|
+
## Common Issues
|
|
202
|
+
|
|
203
|
+
| Problem | Fix |
|
|
204
|
+
|---|---|
|
|
205
|
+
| Page shows empty even though data exists | Check RLS policy allows reads for this user |
|
|
206
|
+
| Data shows but unstyled | Make sure Tailwind classes are applied |
|
|
207
|
+
| Dates look ugly | Format with `toLocaleDateString()` |
|
|
208
|
+
| Too much data, page is slow | Add `.limit(50)` to query, add pagination later if needed |
|
|
209
|
+
| Data appears on refresh but not immediately | Page is server-side cached — add `export const revalidate = 0` for always-fresh data |
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: unstuck
|
|
3
|
+
description: "Detects frustration and fixes problems fast. Triggers: 'stuck', 'this doesn't work', 'broken', 'help', 'ugh', 'what the hell', 'why', 'not working', 'it broke', 'error', 'can't figure out', 'I give up', 'frustrated', 'wtf', 'nothing happens', 'blank page', 'won't load', 'keeps crashing'."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Unstuck
|
|
7
|
+
|
|
8
|
+
The momentum-preservation skill. When a designer hits a wall, get them back to a working state as fast as possible. Explain after.
|
|
9
|
+
|
|
10
|
+
## Detection Signals
|
|
11
|
+
|
|
12
|
+
Activate when you see:
|
|
13
|
+
- Short frustrated messages: "ugh", "help", "stuck", "wtf"
|
|
14
|
+
- Error descriptions: "this doesn't work", "it broke", "nothing happens"
|
|
15
|
+
- Repeated attempts at the same thing
|
|
16
|
+
- Same error appearing multiple times
|
|
17
|
+
- Designer pasting error messages
|
|
18
|
+
- Questions like "why doesn't this work?"
|
|
19
|
+
|
|
20
|
+
## Response Protocol
|
|
21
|
+
|
|
22
|
+
### Step 1: Acknowledge (1 sentence)
|
|
23
|
+
> "Got it, let me take a look."
|
|
24
|
+
|
|
25
|
+
Do NOT say: "What seems to be the problem?" or "Can you describe the error?" or "What were you trying to do?"
|
|
26
|
+
|
|
27
|
+
They already told you. Or the context tells you. Diagnose from what you can see.
|
|
28
|
+
|
|
29
|
+
### Step 2: Diagnose Silently
|
|
30
|
+
|
|
31
|
+
Check in this order:
|
|
32
|
+
1. **Terminal output** — any errors visible?
|
|
33
|
+
2. **Browser console** — any errors in the page?
|
|
34
|
+
3. **Recent file changes** — what did they (or you) change last?
|
|
35
|
+
4. **Dev server** — is it running? Did it crash?
|
|
36
|
+
5. **Dependencies** — any missing packages?
|
|
37
|
+
6. **Environment** — any missing env vars?
|
|
38
|
+
|
|
39
|
+
### Step 3: Fix
|
|
40
|
+
|
|
41
|
+
Fix the problem. Don't describe what you're about to do. Just do it.
|
|
42
|
+
|
|
43
|
+
### Step 4: Show Working Result
|
|
44
|
+
> "That's working now. Check your browser."
|
|
45
|
+
|
|
46
|
+
### Step 5: Brief Explanation (1-2 sentences, only if helpful)
|
|
47
|
+
> "The issue was [X]. This happens when [Y]. No worries."
|
|
48
|
+
|
|
49
|
+
Do NOT:
|
|
50
|
+
- Launch into a teaching moment
|
|
51
|
+
- Explain the underlying architecture
|
|
52
|
+
- Suggest they should have done it differently
|
|
53
|
+
- Recommend preventive measures
|
|
54
|
+
|
|
55
|
+
### Step 6: Momentum Restore
|
|
56
|
+
> "Want to keep going?"
|
|
57
|
+
|
|
58
|
+
## Common Problems & Fast Fixes
|
|
59
|
+
|
|
60
|
+
| Symptom | Likely cause | Fix |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| Blank page | Server component trying to use client hooks | Add `'use client'` directive |
|
|
63
|
+
| "Module not found" | Missing package | `npm install [package]` |
|
|
64
|
+
| Form submits but nothing happens | Server action not connected or missing `'use server'` | Check action binding |
|
|
65
|
+
| "hydration mismatch" | Server/client HTML differs | Move dynamic content to client component |
|
|
66
|
+
| Page shows raw code/JSON | Missing page component or wrong export | Check default export |
|
|
67
|
+
| Styles not applying | Tailwind class not compiling or wrong class name | Check tailwind config, restart dev server |
|
|
68
|
+
| "ECONNREFUSED" on database | Supabase not configured or env vars missing | Check `.env.local` |
|
|
69
|
+
| Login redirect loop | Middleware misconfigured | Check middleware matcher |
|
|
70
|
+
| Image not showing | Wrong path or missing from public folder | Fix path or move image |
|
|
71
|
+
| Dev server won't start | Port in use or syntax error | Kill port, check last edit |
|
|
72
|
+
| "Cannot read properties of undefined" | Accessing data before it loads | Add loading/null check |
|
|
73
|
+
| Build fails | TypeScript error or missing import | Check the specific error line |
|
|
74
|
+
|
|
75
|
+
## Escalation
|
|
76
|
+
|
|
77
|
+
If you can't fix it in 2 attempts:
|
|
78
|
+
|
|
79
|
+
> "This one's tricky. I've tried two approaches and it's still not playing nice.
|
|
80
|
+
>
|
|
81
|
+
> Here's what I can do:
|
|
82
|
+
> 1. **Roll back** — go back to how it was 5 minutes ago before this broke
|
|
83
|
+
> 2. **Try a different approach** — do the same thing but built differently
|
|
84
|
+
>
|
|
85
|
+
> Which do you prefer?"
|
|
86
|
+
|
|
87
|
+
If they choose rollback, use git:
|
|
88
|
+
```bash
|
|
89
|
+
git stash # saves their changes
|
|
90
|
+
git checkout -- . # restores last good state
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Then re-apply their intended change with a different approach.
|
|
94
|
+
|
|
95
|
+
## Anti-Patterns
|
|
96
|
+
|
|
97
|
+
NEVER do these when a designer is frustrated:
|
|
98
|
+
|
|
99
|
+
- ❌ "What error are you seeing?" (you can see it)
|
|
100
|
+
- ❌ "Can you try clearing your cache?" (you fix it)
|
|
101
|
+
- ❌ "This is because of how React hydration works..." (they don't care right now)
|
|
102
|
+
- ❌ "You should have used X instead of Y" (not the time)
|
|
103
|
+
- ❌ "Let me explain what went wrong in detail" (later, if they ask)
|
|
104
|
+
- ❌ Ask multiple clarifying questions before acting
|
|
105
|
+
- ❌ Suggest they read documentation
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: upload-file
|
|
3
|
+
description: "Handles file and image uploads. Triggers: 'upload', 'add a photo', 'add an image', 'file upload', 'image upload', 'let them upload', 'upload pictures', 'attach file', 'profile picture', 'photo gallery', 'add photos', 'drag and drop'."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Upload File
|
|
7
|
+
|
|
8
|
+
Lets users upload images or files. Stores them in the cloud, displays them on the site.
|
|
9
|
+
|
|
10
|
+
## Default: Supabase Storage
|
|
11
|
+
|
|
12
|
+
Already using Supabase for data, so storage is built in.
|
|
13
|
+
|
|
14
|
+
### Step 1: Create Storage Bucket
|
|
15
|
+
|
|
16
|
+
Via Supabase dashboard:
|
|
17
|
+
> "Go to Supabase dashboard → Storage → New Bucket.
|
|
18
|
+
> Name it something like 'images' or 'uploads'.
|
|
19
|
+
> Set it to Public if these images should be visible on your site."
|
|
20
|
+
|
|
21
|
+
Or via SQL:
|
|
22
|
+
```sql
|
|
23
|
+
insert into storage.buckets (id, name, public)
|
|
24
|
+
values ('images', 'images', true);
|
|
25
|
+
|
|
26
|
+
-- Allow anyone to upload (for public forms)
|
|
27
|
+
create policy "Anyone can upload" on storage.objects
|
|
28
|
+
for insert with check (bucket_id = 'images');
|
|
29
|
+
|
|
30
|
+
-- Allow anyone to view (for public images)
|
|
31
|
+
create policy "Anyone can view" on storage.objects
|
|
32
|
+
for select using (bucket_id = 'images');
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Step 2: Create Upload Component
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
'use client'
|
|
39
|
+
|
|
40
|
+
import { createClient } from '@/utils/supabase/client'
|
|
41
|
+
import { useState } from 'react'
|
|
42
|
+
|
|
43
|
+
export function ImageUpload({ onUpload }: { onUpload: (url: string) => void }) {
|
|
44
|
+
const [uploading, setUploading] = useState(false)
|
|
45
|
+
|
|
46
|
+
async function handleUpload(e: React.ChangeEvent<HTMLInputElement>) {
|
|
47
|
+
const file = e.target.files?.[0]
|
|
48
|
+
if (!file) return
|
|
49
|
+
|
|
50
|
+
setUploading(true)
|
|
51
|
+
const supabase = createClient()
|
|
52
|
+
|
|
53
|
+
// Generate unique filename
|
|
54
|
+
const fileExt = file.name.split('.').pop()
|
|
55
|
+
const fileName = `${Date.now()}-${Math.random().toString(36).slice(2)}.${fileExt}`
|
|
56
|
+
|
|
57
|
+
const { error } = await supabase.storage
|
|
58
|
+
.from('images')
|
|
59
|
+
.upload(fileName, file)
|
|
60
|
+
|
|
61
|
+
if (error) {
|
|
62
|
+
alert('Upload failed. Try again.')
|
|
63
|
+
setUploading(false)
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Get the public URL
|
|
68
|
+
const { data: { publicUrl } } = supabase.storage
|
|
69
|
+
.from('images')
|
|
70
|
+
.getPublicUrl(fileName)
|
|
71
|
+
|
|
72
|
+
onUpload(publicUrl)
|
|
73
|
+
setUploading(false)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<label className="block cursor-pointer">
|
|
78
|
+
<div className="border-2 border-dashed border-gray-300 rounded-xl p-8 text-center hover:border-gray-400 transition-colors">
|
|
79
|
+
{uploading ? (
|
|
80
|
+
<p className="text-gray-500">Uploading...</p>
|
|
81
|
+
) : (
|
|
82
|
+
<>
|
|
83
|
+
<p className="text-gray-500">Click to upload an image</p>
|
|
84
|
+
<p className="text-sm text-gray-400 mt-1">PNG, JPG, WebP up to 5MB</p>
|
|
85
|
+
</>
|
|
86
|
+
)}
|
|
87
|
+
</div>
|
|
88
|
+
<input
|
|
89
|
+
type="file"
|
|
90
|
+
accept="image/*"
|
|
91
|
+
onChange={handleUpload}
|
|
92
|
+
className="hidden"
|
|
93
|
+
disabled={uploading}
|
|
94
|
+
/>
|
|
95
|
+
</label>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Step 3: Use It
|
|
101
|
+
|
|
102
|
+
In any page or form:
|
|
103
|
+
```tsx
|
|
104
|
+
import { ImageUpload } from '@/components/ImageUpload'
|
|
105
|
+
|
|
106
|
+
// In your component:
|
|
107
|
+
<ImageUpload onUpload={(url) => {
|
|
108
|
+
// Save the URL to your database, or display it
|
|
109
|
+
console.log('Uploaded:', url)
|
|
110
|
+
}} />
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Step 4: Display Uploaded Images
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
// Single image
|
|
117
|
+
<img src={imageUrl} alt="Uploaded image" className="rounded-lg w-full max-w-md" />
|
|
118
|
+
|
|
119
|
+
// Gallery grid
|
|
120
|
+
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
|
|
121
|
+
{images.map((url) => (
|
|
122
|
+
<img key={url} src={url} alt="" className="rounded-lg aspect-square object-cover" />
|
|
123
|
+
))}
|
|
124
|
+
</div>
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Step 5: Test It
|
|
128
|
+
|
|
129
|
+
> "Try uploading an image. It should appear on the page immediately. You can also check your Supabase dashboard → Storage → images to see the file."
|
|
130
|
+
|
|
131
|
+
## Variations
|
|
132
|
+
|
|
133
|
+
### Profile Picture
|
|
134
|
+
Upload + save URL to user record:
|
|
135
|
+
```typescript
|
|
136
|
+
// After upload, save to user profile
|
|
137
|
+
await supabase.from('profiles').update({
|
|
138
|
+
avatar_url: publicUrl
|
|
139
|
+
}).eq('id', userId)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Multiple Images
|
|
143
|
+
Allow selecting multiple files:
|
|
144
|
+
```tsx
|
|
145
|
+
<input type="file" accept="image/*" multiple onChange={handleMultiUpload} />
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Drag and Drop
|
|
149
|
+
Add drag-and-drop to the upload zone:
|
|
150
|
+
```tsx
|
|
151
|
+
<div
|
|
152
|
+
onDragOver={(e) => { e.preventDefault(); setDragging(true) }}
|
|
153
|
+
onDragLeave={() => setDragging(false)}
|
|
154
|
+
onDrop={(e) => {
|
|
155
|
+
e.preventDefault()
|
|
156
|
+
setDragging(false)
|
|
157
|
+
const file = e.dataTransfer.files[0]
|
|
158
|
+
if (file) handleUploadFile(file)
|
|
159
|
+
}}
|
|
160
|
+
className={`border-2 border-dashed rounded-xl p-8 text-center transition-colors ${
|
|
161
|
+
dragging ? 'border-blue-400 bg-blue-50' : 'border-gray-300'
|
|
162
|
+
}`}
|
|
163
|
+
>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## File Size & Type Limits
|
|
167
|
+
|
|
168
|
+
Set reasonable defaults:
|
|
169
|
+
- Max file size: 5MB for images, 10MB for documents
|
|
170
|
+
- Accepted types: `image/*` for images, or specific extensions
|
|
171
|
+
- Validate before upload, show friendly error if too large
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
if (file.size > 5 * 1024 * 1024) {
|
|
175
|
+
alert('This image is too large. Please use one under 5MB.')
|
|
176
|
+
return
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Common Issues
|
|
181
|
+
|
|
182
|
+
| Problem | Fix |
|
|
183
|
+
|---|---|
|
|
184
|
+
| "Bucket not found" | Create the bucket in Supabase dashboard |
|
|
185
|
+
| Upload succeeds but image doesn't display | Check bucket is set to Public |
|
|
186
|
+
| "Permission denied" on upload | Check storage policies — insert policy needed |
|
|
187
|
+
| Image loads slowly | Supabase serves from CDN, but very large images will be slow. Consider resizing before upload |
|
|
188
|
+
| Wrong file type uploaded | Add `accept` attribute to input and validate in JS |
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: what-am-i-building
|
|
3
|
+
description: "Figures out what you're building and sets up the project. Triggers: 'I want to build', 'I want to make', 'new project', 'start something', 'I have an idea', 'help me build', 'create a', 'build me a', 'I need a website', 'I need an app', 'make me a'."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# What Am I Building
|
|
7
|
+
|
|
8
|
+
The entry point for every new project. Translates the designer's idea into a working scaffold as fast as possible.
|
|
9
|
+
|
|
10
|
+
## Fast Mode (Default)
|
|
11
|
+
|
|
12
|
+
Ask ONE question, then scaffold. Do not front-load planning.
|
|
13
|
+
|
|
14
|
+
### The Question
|
|
15
|
+
|
|
16
|
+
> "What are you making? Give me the quick version — what does someone DO when they visit?"
|
|
17
|
+
|
|
18
|
+
### From Their Answer, Extract
|
|
19
|
+
|
|
20
|
+
1. **Platform** — web, mobile, desktop, or other (see routing table below)
|
|
21
|
+
2. **Core action** — the ONE thing a visitor does (book, buy, read, contact, upload)
|
|
22
|
+
3. **First visible thing** — the landing page / first screen
|
|
23
|
+
|
|
24
|
+
### Then Immediately
|
|
25
|
+
|
|
26
|
+
1. Show a simple flow map:
|
|
27
|
+
```
|
|
28
|
+
📍 Here's how it'll work:
|
|
29
|
+
[Visitor arrives] → [Sees X] → [Does Y] → [Result Z]
|
|
30
|
+
```
|
|
31
|
+
2. Scaffold the project
|
|
32
|
+
3. Start the dev server
|
|
33
|
+
4. Show the first page in the browser
|
|
34
|
+
|
|
35
|
+
Total time from idea to first page in browser: **under 5 minutes.**
|
|
36
|
+
|
|
37
|
+
## Platform Routing
|
|
38
|
+
|
|
39
|
+
| They say | Platform | Stack |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| "website", "landing page", "portfolio", "blog" | Web | Next.js + Tailwind + Supabase + Vercel |
|
|
42
|
+
| "web app", "dashboard", "tool", "SaaS" | Web | Next.js + Tailwind + Supabase + Vercel |
|
|
43
|
+
| "app", "iPhone", "Android", "mobile" | Mobile | React Native + Expo + Supabase + EAS |
|
|
44
|
+
| "store", "sell", "shop", "products", "e-commerce" | Shopify | Liquid + Shopify CLI |
|
|
45
|
+
| "blog", "CMS", "content site", "magazine" | WordPress | Block themes + WP REST API |
|
|
46
|
+
| "Figma plugin", "extend Figma" | Figma Plugin | TypeScript + Figma Plugin API |
|
|
47
|
+
| "email template", "newsletter" | Email | MJML + Resend |
|
|
48
|
+
| "desktop app", "Mac app", "Windows app" | Desktop | Electron + Vite |
|
|
49
|
+
| "art", "generative", "creative", "animation" | Creative | p5.js or Three.js |
|
|
50
|
+
|
|
51
|
+
If ambiguous: "Is this something people use on their phone, in a browser, or on their desktop?"
|
|
52
|
+
|
|
53
|
+
Only ask if truly unclear — most of the time, the context makes it obvious.
|
|
54
|
+
|
|
55
|
+
## Scaffolding (Web — Default)
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npx create-next-app@latest . --typescript --tailwind --eslint --app --src-dir --import-alias "@/*" --use-npm
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
After scaffold, immediately:
|
|
62
|
+
1. Clean up starter boilerplate (remove default Next.js hero content)
|
|
63
|
+
2. Add a minimal but attractive landing page with good typography
|
|
64
|
+
3. `npm run dev` and tell the designer to check their browser
|
|
65
|
+
|
|
66
|
+
## System Design (Invisible)
|
|
67
|
+
|
|
68
|
+
When the designer describes their idea, internally decompose into building blocks. NEVER show this as a technical diagram. Show it as the flow map above.
|
|
69
|
+
|
|
70
|
+
Internal mapping (you think this, they never see it):
|
|
71
|
+
```
|
|
72
|
+
Story element → Building block → Skill to invoke later
|
|
73
|
+
"people can book" → Form + database → save-data
|
|
74
|
+
"only I can see bookings" → Auth + protected page → add-login
|
|
75
|
+
"get notified" → Email integration → send-email
|
|
76
|
+
"upload photos" → File storage → upload-file
|
|
77
|
+
"show all bookings" → Data display → show-data
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Scope Control
|
|
81
|
+
|
|
82
|
+
If they describe many features, prioritize without blocking:
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
📍 You've described a few features. Here's what I'd suggest:
|
|
86
|
+
|
|
87
|
+
🟢 BUILD FIRST (gets you live fastest):
|
|
88
|
+
1. [Core visible page]
|
|
89
|
+
2. [Primary action / form]
|
|
90
|
+
|
|
91
|
+
🟡 ADD NEXT (once the core works):
|
|
92
|
+
3. [Secondary feature]
|
|
93
|
+
4. [Another feature]
|
|
94
|
+
|
|
95
|
+
🔵 LATER (nice to have):
|
|
96
|
+
5. [Extra feature]
|
|
97
|
+
|
|
98
|
+
The first 2 could be working in 15 minutes. Start there?
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Never enforce this order. If they say "actually start with #4," start with #4.
|
|
102
|
+
|
|
103
|
+
## Re-Activation
|
|
104
|
+
|
|
105
|
+
This skill re-activates when the designer says:
|
|
106
|
+
- "I also want to add..."
|
|
107
|
+
- "Can we add a new feature?"
|
|
108
|
+
- "What about adding..."
|
|
109
|
+
|
|
110
|
+
Re-evaluate the flow map, identify new building blocks, integrate without restructuring what exists.
|
|
111
|
+
|
|
112
|
+
## Project Context File
|
|
113
|
+
|
|
114
|
+
After the first scaffold, auto-generate `.github/project-context.md` so the assistant remembers across sessions:
|
|
115
|
+
|
|
116
|
+
```markdown
|
|
117
|
+
# Project Context
|
|
118
|
+
- **What:** [Description from designer]
|
|
119
|
+
- **Platform:** web
|
|
120
|
+
- **Stack:** Next.js + Tailwind + Supabase + Vercel
|
|
121
|
+
- **Core flow:** [Visitor arrives] → [Does X] → [Result Y]
|
|
122
|
+
- **Features present:** landing page
|
|
123
|
+
- **Features planned:** [from scope control]
|
|
124
|
+
- **Database tables:** (none yet)
|
|
125
|
+
- **Auth:** not set up
|
|
126
|
+
- **Deploy:** not deployed yet
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Update this file whenever a major feature is added.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Platform Routing Reference
|
|
2
|
+
|
|
3
|
+
## Decision Table
|
|
4
|
+
|
|
5
|
+
| Signal words | Platform | Default stack |
|
|
6
|
+
|---|---|---|
|
|
7
|
+
| website, landing page, portfolio, personal site | Web | Next.js + Tailwind + Supabase + Vercel |
|
|
8
|
+
| web app, tool, SaaS, dashboard, admin | Web | Next.js + Tailwind + Supabase + Vercel |
|
|
9
|
+
| booking, scheduling, appointments | Web | Next.js + Tailwind + Supabase + Vercel |
|
|
10
|
+
| app, iPhone, Android, mobile, phone | Mobile | Expo (React Native) + Supabase + EAS |
|
|
11
|
+
| store, shop, sell, products, e-commerce, marketplace | Shopify | Shopify CLI + Liquid |
|
|
12
|
+
| blog, CMS, content, magazine, news, articles | WordPress | Block themes + WP REST API |
|
|
13
|
+
| Figma plugin, extend Figma, Figma tool | Figma Plugin | TypeScript + Figma Plugin API |
|
|
14
|
+
| email, newsletter, email template | Email | MJML + Resend |
|
|
15
|
+
| desktop app, Mac app, Windows app | Desktop | Electron + Vite + React |
|
|
16
|
+
| art, generative, creative, animation, 3D | Creative | p5.js / Three.js / Canvas |
|
|
17
|
+
| game, interactive, playful | Creative | p5.js / Pixi.js |
|
|
18
|
+
| physical, hardware, Arduino, sensor | Hardware | Python / Arduino C++ |
|
|
19
|
+
| AI tool, automation, script | Utility | Python + API calls |
|
|
20
|
+
|
|
21
|
+
## Disambiguation
|
|
22
|
+
|
|
23
|
+
If multiple signals conflict, ask ONE question:
|
|
24
|
+
|
|
25
|
+
> "Sounds cool! Quick question — will people use this on their phone, in a browser, or on their desktop?"
|
|
26
|
+
|
|
27
|
+
Do NOT ask about technology choices (React vs Vue, PostgreSQL vs MongoDB). The platform decision determines the stack automatically.
|
|
28
|
+
|
|
29
|
+
## Per-Platform Scaffold Commands
|
|
30
|
+
|
|
31
|
+
### Web (Next.js)
|
|
32
|
+
```bash
|
|
33
|
+
npx create-next-app@latest . --typescript --tailwind --eslint --app --src-dir --import-alias "@/*" --use-npm
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Mobile (Expo)
|
|
37
|
+
```bash
|
|
38
|
+
npx create-expo-app@latest . --template tabs
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Shopify
|
|
42
|
+
```bash
|
|
43
|
+
npm init @shopify/theme@latest
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### WordPress
|
|
47
|
+
```bash
|
|
48
|
+
npx @wordpress/create-block@latest theme-starter
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Email (MJML)
|
|
52
|
+
```bash
|
|
53
|
+
npm init -y && npm install mjml
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Desktop (Electron)
|
|
57
|
+
```bash
|
|
58
|
+
npm create electron-vite@latest . -- --template react-ts
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Creative (p5.js)
|
|
62
|
+
```bash
|
|
63
|
+
npm init -y && npm install p5
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
For v1, only **Web (Next.js)** has full skill support. Other platforms can be scaffolded but won't have outcome skills (save-data, add-login, etc.) customized for them yet.
|