ai-site-pilot 0.5.0 → 0.5.2
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/CLAUDE.md +346 -375
- package/package.json +1 -1
package/CLAUDE.md
CHANGED
|
@@ -1,341 +1,196 @@
|
|
|
1
1
|
# AI Instructions for ai-site-pilot
|
|
2
2
|
|
|
3
|
-
This file
|
|
3
|
+
This file helps AI coding assistants (Claude, GPT, Copilot) integrate ai-site-pilot into projects.
|
|
4
4
|
|
|
5
5
|
## What is ai-site-pilot?
|
|
6
6
|
|
|
7
|
-
A drop-in AI chat widget that can **control and navigate websites**.
|
|
7
|
+
A drop-in AI chat widget that can **control and navigate websites**. The AI can execute tools to filter content, open modals, navigate sections, and more.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
**Key features:**
|
|
10
|
+
- Works with any AI model via OpenRouter (Gemini, GPT-4, Claude, Llama)
|
|
11
|
+
- Free tier available (Gemini 2.0 Flash)
|
|
12
|
+
- Auto-generates system prompts from site content
|
|
13
|
+
- Streaming responses with tool execution
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
## IMPORTANT: Keeping Chatbot in Sync with Site Content
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
2. **Define tools** - Create tools that match your site's DOM and state
|
|
15
|
-
3. **Write handlers** - Client-side code that executes the actual actions
|
|
17
|
+
**The chatbot must always reflect the current site content.** To achieve this:
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
1. **Find where site data lives** - Look for constants files, data files, CMS exports, or API endpoints that contain the site's content (products, services, team, etc.)
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
// In your API route - this is the MOST IMPORTANT part
|
|
21
|
-
const SYSTEM_PROMPT = `You are an AI assistant for MyStore.
|
|
22
|
-
|
|
23
|
-
## SITE STRUCTURE
|
|
24
|
-
The page has these sections (use navigate_to_section tool):
|
|
25
|
-
- hero: Top of page with tagline
|
|
26
|
-
- products: Product grid (id="products")
|
|
27
|
-
- about: About us section (id="about")
|
|
28
|
-
- contact: Contact form (id="contact")
|
|
29
|
-
|
|
30
|
-
## AVAILABLE DATA
|
|
31
|
-
Products on this site:
|
|
32
|
-
- Electronics: laptops, phones, tablets
|
|
33
|
-
- Clothing: shirts, pants, shoes
|
|
34
|
-
- Home: furniture, decor, kitchen
|
|
35
|
-
|
|
36
|
-
Categories for filtering: electronics, clothing, home, all
|
|
37
|
-
|
|
38
|
-
## YOUR TOOLS
|
|
39
|
-
- navigate_to_section: Scroll to a section by ID
|
|
40
|
-
- filter_products: Filter the product grid by category
|
|
41
|
-
- open_product_modal: Show details for a specific product (use product slug)
|
|
42
|
-
- open_contact: Open the contact modal
|
|
43
|
-
|
|
44
|
-
## BEHAVIOR
|
|
45
|
-
- When users ask about products, use filter_products to show relevant category
|
|
46
|
-
- When users want details, use open_product_modal
|
|
47
|
-
- When users want to buy/contact, use open_contact
|
|
48
|
-
- Always be helpful and proactively use tools to show relevant content`;
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
The system prompt teaches the AI:
|
|
52
|
-
- What sections exist and their IDs
|
|
53
|
-
- What data/content is available
|
|
54
|
-
- What tools it can use and when
|
|
55
|
-
- How to behave (proactively use tools)
|
|
21
|
+
2. **Import from the same source** - The API route should import from these existing data sources, NOT duplicate the data
|
|
56
22
|
|
|
57
|
-
|
|
23
|
+
3. **Maintain this pattern** - When updating site content, the chatbot automatically stays in sync
|
|
58
24
|
|
|
59
|
-
###
|
|
25
|
+
### Example Pattern
|
|
60
26
|
|
|
61
|
-
```
|
|
62
|
-
|
|
27
|
+
```typescript
|
|
28
|
+
// lib/data.ts - The site's single source of truth
|
|
29
|
+
export const CLASSES = [
|
|
30
|
+
{ id: 'ballet', name: 'Ballet', description: 'Classical ballet', price: '$80/mo' },
|
|
31
|
+
{ id: 'jazz', name: 'Jazz', description: 'High-energy jazz', price: '$75/mo' },
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
export const TEACHERS = [
|
|
35
|
+
{ id: 'sarah', name: 'Sarah Johnson', role: 'Owner', bio: '15 years experience' },
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
export const SITE_INFO = {
|
|
39
|
+
name: 'Dance Studio',
|
|
40
|
+
email: 'info@studio.com',
|
|
41
|
+
phone: '555-1234',
|
|
42
|
+
};
|
|
63
43
|
```
|
|
64
44
|
|
|
65
|
-
### 2. Create the API route
|
|
66
|
-
|
|
67
|
-
Create `app/api/chat/route.ts` (Next.js App Router):
|
|
68
|
-
|
|
69
45
|
```typescript
|
|
70
|
-
|
|
71
|
-
import {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
Use these tools proactively when relevant to the user's request.`;
|
|
86
|
-
|
|
87
|
-
export async function POST(req: Request) {
|
|
88
|
-
const { messages } = await req.json();
|
|
89
|
-
|
|
90
|
-
const result = streamText({
|
|
91
|
-
model: google('gemini-2.0-flash'),
|
|
92
|
-
system: SYSTEM_PROMPT,
|
|
93
|
-
messages,
|
|
94
|
-
tools: {
|
|
95
|
-
// Define your site-specific tools here
|
|
96
|
-
navigate_to_section: {
|
|
97
|
-
description: 'Scroll to a section of the page',
|
|
98
|
-
parameters: {
|
|
99
|
-
type: 'object',
|
|
100
|
-
properties: {
|
|
101
|
-
section: { type: 'string', enum: ['hero', 'features', 'pricing', 'contact'] }
|
|
102
|
-
},
|
|
103
|
-
required: ['section']
|
|
104
|
-
}
|
|
105
|
-
},
|
|
106
|
-
// Add more tools as needed
|
|
46
|
+
// app/api/chat/route.ts - Imports from the SAME source
|
|
47
|
+
import { CLASSES, TEACHERS, SITE_INFO } from '@/lib/data';
|
|
48
|
+
import { createHandler } from 'ai-site-pilot/api';
|
|
49
|
+
|
|
50
|
+
export const POST = createHandler({
|
|
51
|
+
siteContent: {
|
|
52
|
+
name: SITE_INFO.name,
|
|
53
|
+
items: [
|
|
54
|
+
...CLASSES.map(c => ({ ...c, category: 'class' })),
|
|
55
|
+
...TEACHERS.map(t => ({ ...t, category: 'teacher' })),
|
|
56
|
+
],
|
|
57
|
+
contact: {
|
|
58
|
+
email: SITE_INFO.email,
|
|
59
|
+
phone: SITE_INFO.phone,
|
|
107
60
|
},
|
|
108
|
-
}
|
|
61
|
+
},
|
|
62
|
+
tools,
|
|
63
|
+
});
|
|
64
|
+
```
|
|
109
65
|
|
|
110
|
-
|
|
66
|
+
```tsx
|
|
67
|
+
// components/ClassList.tsx - Also uses the same source
|
|
68
|
+
import { CLASSES } from '@/lib/data';
|
|
69
|
+
|
|
70
|
+
export function ClassList() {
|
|
71
|
+
return CLASSES.map(c => <ClassCard key={c.id} {...c} />);
|
|
111
72
|
}
|
|
112
73
|
```
|
|
113
74
|
|
|
114
|
-
|
|
75
|
+
**Update `lib/data.ts` → Both site AND chatbot update automatically.**
|
|
115
76
|
|
|
116
|
-
|
|
77
|
+
### When Setting Up a New Project
|
|
117
78
|
|
|
118
|
-
|
|
119
|
-
'use client';
|
|
79
|
+
As an AI assistant, you should:
|
|
120
80
|
|
|
121
|
-
|
|
122
|
-
|
|
81
|
+
1. **Scan the codebase** for existing data files:
|
|
82
|
+
- `constants.ts`, `data.ts`, `config.ts`
|
|
83
|
+
- `lib/`, `data/`, `content/` directories
|
|
84
|
+
- JSON files with site content
|
|
123
85
|
|
|
124
|
-
|
|
125
|
-
const handleToolCall = (name: string, args: Record<string, unknown>) => {
|
|
126
|
-
// Handle tool execution - this runs on the client
|
|
127
|
-
switch (name) {
|
|
128
|
-
case 'navigate_to_section':
|
|
129
|
-
document.getElementById(args.section as string)?.scrollIntoView({ behavior: 'smooth' });
|
|
130
|
-
break;
|
|
131
|
-
case 'open_modal':
|
|
132
|
-
// Your modal logic
|
|
133
|
-
break;
|
|
134
|
-
// Handle other tools
|
|
135
|
-
}
|
|
136
|
-
};
|
|
86
|
+
2. **Identify the data structure** - What does the site display? Products? Services? Team members?
|
|
137
87
|
|
|
138
|
-
|
|
139
|
-
<SitePilot
|
|
140
|
-
apiEndpoint="/api/chat"
|
|
141
|
-
onToolCall={handleToolCall}
|
|
142
|
-
suggestions={[
|
|
143
|
-
{ text: 'Show me features', icon: '✨' },
|
|
144
|
-
{ text: 'How does pricing work?', icon: '💰' },
|
|
145
|
-
{ text: 'Contact sales', icon: '📞' },
|
|
146
|
-
]}
|
|
147
|
-
welcomeMessage="Hi! I can help you explore this site. What would you like to know?"
|
|
148
|
-
theme={{
|
|
149
|
-
accentColor: '#3b82f6', // Your brand color
|
|
150
|
-
backgroundColor: '#0f172a', // Dark background
|
|
151
|
-
// See full theme options below
|
|
152
|
-
}}
|
|
153
|
-
/>
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
```
|
|
88
|
+
3. **Create the API route** that imports from these existing sources
|
|
157
89
|
|
|
158
|
-
|
|
90
|
+
4. **Never duplicate data** - If you can't find existing data files, create ONE shared file that both the site and chatbot use
|
|
159
91
|
|
|
160
|
-
|
|
92
|
+
## Quick Setup (Recommended)
|
|
161
93
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
position: 'bottom-right', // 'bottom-left', 'top-right', 'top-left'
|
|
167
|
-
borderRadius: 24, // pixels
|
|
168
|
-
|
|
169
|
-
// Colors
|
|
170
|
-
accentColor: '#f59e0b', // Primary accent (buttons, highlights)
|
|
171
|
-
accentColorDark: '#d97706', // Gradient end color
|
|
172
|
-
backgroundColor: '#0F0720', // Panel background
|
|
173
|
-
textColor: '#ffffff', // Primary text
|
|
174
|
-
textMutedColor: '#a1a1aa', // Secondary text
|
|
175
|
-
borderColor: 'rgba(255,255,255,0.1)',
|
|
176
|
-
|
|
177
|
-
// Message bubbles
|
|
178
|
-
userMessageBg: 'linear-gradient(135deg, #f59e0b, #d97706)',
|
|
179
|
-
assistantMessageBg: 'rgba(255,255,255,0.05)',
|
|
180
|
-
}}
|
|
181
|
-
/>
|
|
94
|
+
### 1. Install
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
npm install ai-site-pilot
|
|
182
98
|
```
|
|
183
99
|
|
|
184
|
-
|
|
100
|
+
### 2. Configure Tailwind (Required)
|
|
185
101
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
102
|
+
Add to `tailwind.config.js`:
|
|
103
|
+
|
|
104
|
+
```js
|
|
105
|
+
module.exports = {
|
|
106
|
+
content: [
|
|
107
|
+
'./app/**/*.{js,ts,jsx,tsx}',
|
|
108
|
+
'./components/**/*.{js,ts,jsx,tsx}',
|
|
109
|
+
'./node_modules/ai-site-pilot/dist/**/*.{js,mjs}', // Required!
|
|
110
|
+
],
|
|
111
|
+
}
|
|
195
112
|
```
|
|
196
113
|
|
|
197
|
-
|
|
114
|
+
### 3. Create API Route with `siteContent`
|
|
198
115
|
|
|
199
|
-
|
|
116
|
+
The easiest way - just pass your site's data and the prompt is auto-generated:
|
|
200
117
|
|
|
201
118
|
```typescript
|
|
202
|
-
//
|
|
119
|
+
// app/api/chat/route.ts
|
|
120
|
+
import { createHandler } from 'ai-site-pilot/api';
|
|
203
121
|
import { defineTool } from 'ai-site-pilot/tools';
|
|
204
122
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
123
|
+
// Define tools the AI can use
|
|
124
|
+
const navigateTool = defineTool({
|
|
125
|
+
name: 'navigate_to_section',
|
|
126
|
+
description: 'Scroll to a section of the page',
|
|
208
127
|
parameters: {
|
|
209
128
|
type: 'object',
|
|
210
129
|
properties: {
|
|
211
|
-
|
|
130
|
+
section: {
|
|
212
131
|
type: 'string',
|
|
213
|
-
description: '
|
|
214
|
-
enum: ['
|
|
215
|
-
}
|
|
132
|
+
description: 'Section ID to navigate to',
|
|
133
|
+
enum: ['hero', 'services', 'about', 'contact'],
|
|
134
|
+
},
|
|
216
135
|
},
|
|
217
|
-
required: ['
|
|
218
|
-
}
|
|
136
|
+
required: ['section'],
|
|
137
|
+
},
|
|
219
138
|
});
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
## Environment Variables
|
|
223
|
-
|
|
224
|
-
For the API route, you'll need your AI provider's API key:
|
|
225
|
-
|
|
226
|
-
```env
|
|
227
|
-
# For Google Gemini
|
|
228
|
-
GOOGLE_GENERATIVE_AI_API_KEY=your-key-here
|
|
229
|
-
|
|
230
|
-
# Or for OpenAI
|
|
231
|
-
OPENAI_API_KEY=your-key-here
|
|
232
|
-
|
|
233
|
-
# Or for Anthropic
|
|
234
|
-
ANTHROPIC_API_KEY=your-key-here
|
|
235
|
-
```
|
|
236
139
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
Available projects (use open_project_modal with these IDs):
|
|
247
|
-
- acme-dashboard: Acme Dashboard (SaaS, Live) - Analytics platform
|
|
248
|
-
- mobile-app: FitTrack (Mobile, Live) - Fitness tracking app
|
|
249
|
-
- ecommerce: ShopFlow (E-commerce, In Progress) - Online store template
|
|
250
|
-
- api-service: DataSync API (Backend, Live) - Data synchronization service
|
|
251
|
-
|
|
252
|
-
Categories: All, SaaS, Mobile, E-commerce, Backend
|
|
253
|
-
Statuses: Live, In Progress, Concept
|
|
254
|
-
`;
|
|
255
|
-
|
|
256
|
-
const SYSTEM_PROMPT = `You are a portfolio assistant for Jane Developer.
|
|
257
|
-
|
|
258
|
-
## SITE SECTIONS
|
|
259
|
-
- hero: Landing section with intro (id="hero")
|
|
260
|
-
- projects: Project showcase grid (id="projects")
|
|
261
|
-
- about: About section (id="about")
|
|
262
|
-
- contact: Contact form (id="contact")
|
|
263
|
-
|
|
264
|
-
## PROJECT DATA
|
|
265
|
-
${PROJECTS}
|
|
266
|
-
|
|
267
|
-
## YOUR TOOLS
|
|
268
|
-
- navigate_to_section: Scroll to hero, projects, about, or contact
|
|
269
|
-
- filter_by_category: Filter projects (SaaS, Mobile, E-commerce, Backend, All)
|
|
270
|
-
- filter_by_status: Filter by status (Live, In Progress, Concept)
|
|
271
|
-
- open_project_modal: Open project details (use project ID like "acme-dashboard")
|
|
272
|
-
- highlight_project: Pulse animation on a project card
|
|
273
|
-
- open_contact: Open contact/hire modal
|
|
274
|
-
|
|
275
|
-
## WHEN TO USE TOOLS
|
|
276
|
-
- "Show me your work" → navigate_to_section("projects")
|
|
277
|
-
- "Any mobile apps?" → filter_by_category("Mobile")
|
|
278
|
-
- "What's live?" → filter_by_status("Live")
|
|
279
|
-
- "Tell me about Acme" → open_project_modal("acme-dashboard")
|
|
280
|
-
- "I want to hire you" → open_contact()
|
|
281
|
-
|
|
282
|
-
Be conversational but proactively use tools to make the site interactive.`;
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
### Part 2: Tool Definitions (what the AI can call)
|
|
286
|
-
|
|
287
|
-
```typescript
|
|
288
|
-
// app/api/chat/route.ts (continued)
|
|
289
|
-
export async function POST(req: Request) {
|
|
290
|
-
const { messages } = await req.json();
|
|
291
|
-
|
|
292
|
-
const result = streamText({
|
|
293
|
-
model: google('gemini-2.0-flash'),
|
|
294
|
-
system: SYSTEM_PROMPT,
|
|
295
|
-
messages,
|
|
296
|
-
tools: {
|
|
297
|
-
navigate_to_section: {
|
|
298
|
-
description: 'Scroll to a page section',
|
|
299
|
-
parameters: {
|
|
300
|
-
type: 'object',
|
|
301
|
-
properties: {
|
|
302
|
-
section: { type: 'string', enum: ['hero', 'projects', 'about', 'contact'] }
|
|
303
|
-
},
|
|
304
|
-
required: ['section']
|
|
305
|
-
}
|
|
306
|
-
},
|
|
307
|
-
filter_by_category: {
|
|
308
|
-
description: 'Filter projects by category',
|
|
309
|
-
parameters: {
|
|
310
|
-
type: 'object',
|
|
311
|
-
properties: {
|
|
312
|
-
category: { type: 'string', enum: ['All', 'SaaS', 'Mobile', 'E-commerce', 'Backend'] }
|
|
313
|
-
},
|
|
314
|
-
required: ['category']
|
|
315
|
-
}
|
|
316
|
-
},
|
|
317
|
-
open_project_modal: {
|
|
318
|
-
description: 'Open project detail modal',
|
|
319
|
-
parameters: {
|
|
320
|
-
type: 'object',
|
|
321
|
-
properties: {
|
|
322
|
-
projectId: { type: 'string', description: 'Project ID like acme-dashboard' }
|
|
323
|
-
},
|
|
324
|
-
required: ['projectId']
|
|
325
|
-
}
|
|
140
|
+
const showServiceTool = defineTool({
|
|
141
|
+
name: 'show_service',
|
|
142
|
+
description: 'Show details about a specific service',
|
|
143
|
+
parameters: {
|
|
144
|
+
type: 'object',
|
|
145
|
+
properties: {
|
|
146
|
+
serviceId: {
|
|
147
|
+
type: 'string',
|
|
148
|
+
description: 'The service ID',
|
|
326
149
|
},
|
|
327
|
-
open_contact: {
|
|
328
|
-
description: 'Open the contact/hire modal',
|
|
329
|
-
parameters: { type: 'object', properties: {}, required: [] }
|
|
330
|
-
}
|
|
331
150
|
},
|
|
332
|
-
|
|
151
|
+
required: ['serviceId'],
|
|
152
|
+
},
|
|
153
|
+
});
|
|
333
154
|
|
|
334
|
-
|
|
335
|
-
|
|
155
|
+
// Create handler with site content - prompt is auto-generated!
|
|
156
|
+
export const POST = createHandler({
|
|
157
|
+
model: 'google/gemini-2.0-flash-exp:free', // Free!
|
|
158
|
+
siteContent: {
|
|
159
|
+
name: 'Acme Dance Studio',
|
|
160
|
+
type: 'dance studio',
|
|
161
|
+
description: 'Premier dance education for all ages',
|
|
162
|
+
personality: 'warm, encouraging, and helpful',
|
|
163
|
+
pages: ['home', 'classes', 'teachers', 'schedule', 'contact'],
|
|
164
|
+
items: [
|
|
165
|
+
// Classes
|
|
166
|
+
{ id: 'ballet', name: 'Ballet', category: 'class', description: 'Classical ballet for ages 3-adult', price: '$80/month' },
|
|
167
|
+
{ id: 'jazz', name: 'Jazz', category: 'class', description: 'High-energy jazz for ages 6+', price: '$75/month' },
|
|
168
|
+
{ id: 'hip-hop', name: 'Hip Hop', category: 'class', description: 'Street dance styles for ages 8+', price: '$70/month' },
|
|
169
|
+
// Teachers
|
|
170
|
+
{ id: 'sarah', name: 'Sarah Johnson', category: 'teacher', description: 'Owner, 15 years experience, specializes in ballet' },
|
|
171
|
+
{ id: 'mike', name: 'Mike Chen', category: 'teacher', description: 'Hip hop and jazz instructor, 8 years experience' },
|
|
172
|
+
],
|
|
173
|
+
faqs: [
|
|
174
|
+
{ question: 'What should I wear?', answer: 'Leotard and ballet slippers for ballet, athletic wear for jazz and hip hop.' },
|
|
175
|
+
{ question: 'Do you offer trial classes?', answer: 'Yes! First class is free for new students.' },
|
|
176
|
+
],
|
|
177
|
+
contact: {
|
|
178
|
+
email: 'info@acmedance.com',
|
|
179
|
+
phone: '555-123-4567',
|
|
180
|
+
address: '123 Dance Street, NYC',
|
|
181
|
+
hours: 'Mon-Sat 9am-8pm',
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
tools: [navigateTool, showServiceTool],
|
|
185
|
+
});
|
|
336
186
|
```
|
|
337
187
|
|
|
338
|
-
|
|
188
|
+
**The AI now automatically knows:**
|
|
189
|
+
- All your classes, teachers, pricing
|
|
190
|
+
- FAQs and contact info
|
|
191
|
+
- What tools to use and when
|
|
192
|
+
|
|
193
|
+
### 4. Add the Chat Widget
|
|
339
194
|
|
|
340
195
|
```tsx
|
|
341
196
|
// components/ChatWidget.tsx
|
|
@@ -349,26 +204,15 @@ export function ChatWidget() {
|
|
|
349
204
|
switch (name) {
|
|
350
205
|
case 'navigate_to_section':
|
|
351
206
|
document.getElementById(args.section as string)?.scrollIntoView({
|
|
352
|
-
behavior: 'smooth'
|
|
207
|
+
behavior: 'smooth',
|
|
353
208
|
});
|
|
354
209
|
break;
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
detail: { category: args.category }
|
|
360
|
-
}));
|
|
361
|
-
break;
|
|
362
|
-
|
|
363
|
-
case 'open_project_modal':
|
|
364
|
-
window.dispatchEvent(new CustomEvent('open-project', {
|
|
365
|
-
detail: { projectId: args.projectId }
|
|
210
|
+
case 'show_service':
|
|
211
|
+
// Dispatch event for your page to handle
|
|
212
|
+
window.dispatchEvent(new CustomEvent('show-service', {
|
|
213
|
+
detail: { serviceId: args.serviceId },
|
|
366
214
|
}));
|
|
367
215
|
break;
|
|
368
|
-
|
|
369
|
-
case 'open_contact':
|
|
370
|
-
window.dispatchEvent(new CustomEvent('open-contact'));
|
|
371
|
-
break;
|
|
372
216
|
}
|
|
373
217
|
};
|
|
374
218
|
|
|
@@ -377,110 +221,237 @@ export function ChatWidget() {
|
|
|
377
221
|
apiEndpoint="/api/chat"
|
|
378
222
|
onToolCall={handleToolCall}
|
|
379
223
|
suggestions={[
|
|
380
|
-
{ text:
|
|
381
|
-
{ text:
|
|
382
|
-
{ text:
|
|
224
|
+
{ text: 'What classes do you offer?', icon: '💃' },
|
|
225
|
+
{ text: 'Meet the teachers', icon: '👩🏫' },
|
|
226
|
+
{ text: 'Contact us', icon: '📞' },
|
|
383
227
|
]}
|
|
384
|
-
theme={{
|
|
385
|
-
|
|
386
|
-
backgroundColor: '#0f0f23',
|
|
387
|
-
}}
|
|
228
|
+
theme={{ accent: 'pink' }} // or accentColor: '#ec4899'
|
|
229
|
+
welcomeMessage="Hi! I'm your dance studio assistant. How can I help you today?"
|
|
388
230
|
/>
|
|
389
231
|
);
|
|
390
232
|
}
|
|
391
233
|
```
|
|
392
234
|
|
|
393
|
-
###
|
|
235
|
+
### 5. Environment Variable
|
|
394
236
|
|
|
395
|
-
```
|
|
396
|
-
|
|
397
|
-
|
|
237
|
+
```bash
|
|
238
|
+
# .env.local
|
|
239
|
+
OPENROUTER_API_KEY=sk-or-...
|
|
240
|
+
```
|
|
398
241
|
|
|
399
|
-
|
|
242
|
+
Get a free key at [openrouter.ai](https://openrouter.ai) - Gemini 2.0 Flash is free!
|
|
400
243
|
|
|
401
|
-
|
|
402
|
-
const [filter, setFilter] = useState('All');
|
|
403
|
-
const [selectedProject, setSelectedProject] = useState(null);
|
|
404
|
-
const [showContact, setShowContact] = useState(false);
|
|
244
|
+
## Alternative: Manual System Prompt
|
|
405
245
|
|
|
406
|
-
|
|
407
|
-
const handleFilter = (e: CustomEvent) => setFilter(e.detail.category);
|
|
408
|
-
const handleProject = (e: CustomEvent) => setSelectedProject(e.detail.projectId);
|
|
409
|
-
const handleContact = () => setShowContact(true);
|
|
246
|
+
If you need full control, use `systemPrompt` instead of `siteContent`:
|
|
410
247
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
248
|
+
```typescript
|
|
249
|
+
export const POST = createHandler({
|
|
250
|
+
model: 'google/gemini-2.0-flash-exp:free',
|
|
251
|
+
systemPrompt: `You are an assistant for Acme Dance Studio.
|
|
252
|
+
|
|
253
|
+
## Classes
|
|
254
|
+
- Ballet: Ages 3-adult, $80/month
|
|
255
|
+
- Jazz: Ages 6+, $75/month
|
|
256
|
+
- Hip Hop: Ages 8+, $70/month
|
|
257
|
+
|
|
258
|
+
## Tools
|
|
259
|
+
- navigate_to_section: Scroll to a section (hero, classes, teachers, contact)
|
|
260
|
+
- show_service: Show details about a class
|
|
261
|
+
|
|
262
|
+
When users ask about classes, provide specific details.
|
|
263
|
+
Use tools proactively to show relevant content.`,
|
|
264
|
+
tools: [navigateTool, showServiceTool],
|
|
265
|
+
});
|
|
266
|
+
```
|
|
414
267
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
268
|
+
## SiteContent Schema
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
interface SiteContent {
|
|
272
|
+
name: string; // Business name
|
|
273
|
+
type?: string; // Business type (e.g., 'dance studio')
|
|
274
|
+
description?: string; // Brief description
|
|
275
|
+
personality?: string; // AI personality (e.g., 'warm and helpful')
|
|
276
|
+
pages?: string[]; // Available navigation pages
|
|
277
|
+
items?: SiteContentItem[]; // Products, services, team, etc.
|
|
278
|
+
faqs?: Array<{ question: string; answer: string }>;
|
|
279
|
+
contact?: {
|
|
280
|
+
email?: string;
|
|
281
|
+
phone?: string;
|
|
282
|
+
address?: string;
|
|
283
|
+
hours?: string;
|
|
284
|
+
};
|
|
285
|
+
additionalContext?: string; // Extra info for the prompt
|
|
286
|
+
}
|
|
421
287
|
|
|
422
|
-
|
|
288
|
+
interface SiteContentItem {
|
|
289
|
+
id: string; // Unique identifier
|
|
290
|
+
name: string; // Display name
|
|
291
|
+
category?: string; // Category (e.g., 'class', 'teacher', 'product')
|
|
292
|
+
description?: string; // Description
|
|
293
|
+
[key: string]: unknown; // Any extra fields (price, duration, etc.)
|
|
423
294
|
}
|
|
424
295
|
```
|
|
425
296
|
|
|
426
|
-
##
|
|
297
|
+
## Theme Presets
|
|
427
298
|
|
|
428
|
-
|
|
429
|
-
```tsx
|
|
430
|
-
const tools = {
|
|
431
|
-
search_products: { /* search inventory */ },
|
|
432
|
-
filter_by_category: { /* filter product grid */ },
|
|
433
|
-
add_to_cart: { /* add item to cart */ },
|
|
434
|
-
open_product: { /* show product details */ },
|
|
435
|
-
};
|
|
436
|
-
```
|
|
299
|
+
Use preset names for easy theming:
|
|
437
300
|
|
|
438
|
-
### Documentation Site
|
|
439
301
|
```tsx
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
}
|
|
302
|
+
<SitePilot theme={{ accent: 'pink' }} /> // Hot pink
|
|
303
|
+
<SitePilot theme={{ accent: 'amber' }} /> // Default orange/amber
|
|
304
|
+
<SitePilot theme={{ accent: 'blue' }} /> // Primary blue
|
|
305
|
+
<SitePilot theme={{ accent: 'green' }} /> // Emerald green
|
|
306
|
+
<SitePilot theme={{ accent: 'purple' }} /> // Violet purple
|
|
307
|
+
<SitePilot theme={{ accent: 'cyan' }} /> // Teal cyan
|
|
445
308
|
```
|
|
446
309
|
|
|
447
|
-
|
|
310
|
+
Or use a custom hex color:
|
|
311
|
+
|
|
448
312
|
```tsx
|
|
449
|
-
|
|
450
|
-
filter_projects: { /* filter by category/status */ },
|
|
451
|
-
open_project_modal: { /* show project details */ },
|
|
452
|
-
navigate_to_section: { /* scroll to section */ },
|
|
453
|
-
open_contact: { /* open contact form */ },
|
|
454
|
-
};
|
|
313
|
+
<SitePilot theme={{ accentColor: '#8b5cf6' }} />
|
|
455
314
|
```
|
|
456
315
|
|
|
457
|
-
##
|
|
316
|
+
## Tool-Only Response Handling
|
|
458
317
|
|
|
459
|
-
|
|
460
|
-
- Make sure you imported the CSS: `import 'ai-site-pilot/styles.css'`
|
|
461
|
-
- Check that the component is rendered (use React DevTools)
|
|
318
|
+
When the AI uses tools without text, customize the fallback message:
|
|
462
319
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
- Check browser console for the tool calls
|
|
466
|
-
- Make sure tool names match between API and client
|
|
320
|
+
```tsx
|
|
321
|
+
import { SitePilot, createFallbackMessageGenerator } from 'ai-site-pilot';
|
|
467
322
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
323
|
+
const generateFallback = createFallbackMessageGenerator({
|
|
324
|
+
navigate_to_section: (args) => `Scrolled to **${args.section}**.`,
|
|
325
|
+
show_service: (args) => `Showing details for **${args.serviceId}**.`,
|
|
326
|
+
filter_products: (args) => `Filtered to **${args.category}** products.`,
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
<SitePilot
|
|
330
|
+
apiEndpoint="/api/chat"
|
|
331
|
+
generateFallbackMessage={generateFallback}
|
|
332
|
+
/>
|
|
333
|
+
```
|
|
471
334
|
|
|
472
335
|
## Full Props Reference
|
|
473
336
|
|
|
474
337
|
```typescript
|
|
475
338
|
interface SitePilotProps {
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
339
|
+
// Required
|
|
340
|
+
apiEndpoint: string;
|
|
341
|
+
|
|
342
|
+
// Tool handling
|
|
480
343
|
onToolCall?: (name: string, args: Record<string, unknown>) => void;
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
344
|
+
generateFallbackMessage?: (toolName: string, args: Record<string, unknown>) => string;
|
|
345
|
+
|
|
346
|
+
// UI customization
|
|
347
|
+
suggestions?: Array<{ text: string; icon?: string }>;
|
|
348
|
+
welcomeMessage?: string;
|
|
349
|
+
placeholder?: string;
|
|
350
|
+
defaultOpen?: boolean;
|
|
351
|
+
position?: 'bottom-right' | 'bottom-left';
|
|
352
|
+
|
|
353
|
+
// Theme
|
|
354
|
+
theme?: {
|
|
355
|
+
accent?: 'amber' | 'pink' | 'blue' | 'green' | 'purple' | 'red' | 'cyan' | 'orange';
|
|
356
|
+
accentColor?: string; // Hex color (overrides accent)
|
|
357
|
+
backgroundColor?: string;
|
|
358
|
+
radius?: number;
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
// Features
|
|
362
|
+
features?: {
|
|
363
|
+
speech?: boolean; // Voice input
|
|
364
|
+
tts?: boolean; // Text-to-speech
|
|
365
|
+
fullscreen?: boolean; // Fullscreen toggle
|
|
366
|
+
};
|
|
485
367
|
}
|
|
486
368
|
```
|
|
369
|
+
|
|
370
|
+
## Common Patterns
|
|
371
|
+
|
|
372
|
+
### E-commerce Site
|
|
373
|
+
|
|
374
|
+
```typescript
|
|
375
|
+
siteContent: {
|
|
376
|
+
name: 'MyStore',
|
|
377
|
+
type: 'online store',
|
|
378
|
+
items: [
|
|
379
|
+
{ id: 'laptop-1', name: 'MacBook Pro', category: 'product', price: '$1999', inStock: true },
|
|
380
|
+
{ id: 'phone-1', name: 'iPhone 15', category: 'product', price: '$999', inStock: true },
|
|
381
|
+
],
|
|
382
|
+
pages: ['home', 'products', 'cart', 'checkout'],
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Tools: search_products, filter_by_category, add_to_cart, open_product
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Portfolio Site
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
siteContent: {
|
|
392
|
+
name: 'Jane Developer',
|
|
393
|
+
type: 'portfolio',
|
|
394
|
+
items: [
|
|
395
|
+
{ id: 'project-1', name: 'Acme Dashboard', category: 'project', status: 'Live', tech: 'React, Node' },
|
|
396
|
+
{ id: 'project-2', name: 'FitTrack App', category: 'project', status: 'Live', tech: 'React Native' },
|
|
397
|
+
],
|
|
398
|
+
pages: ['hero', 'projects', 'about', 'contact'],
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Tools: filter_projects, open_project_modal, navigate_to_section, open_contact
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### SaaS Landing Page
|
|
405
|
+
|
|
406
|
+
```typescript
|
|
407
|
+
siteContent: {
|
|
408
|
+
name: 'CloudSync',
|
|
409
|
+
type: 'SaaS platform',
|
|
410
|
+
description: 'Real-time data synchronization for teams',
|
|
411
|
+
items: [
|
|
412
|
+
{ id: 'starter', name: 'Starter Plan', category: 'pricing', price: '$9/mo', features: '5 users, 10GB' },
|
|
413
|
+
{ id: 'pro', name: 'Pro Plan', category: 'pricing', price: '$29/mo', features: '25 users, 100GB' },
|
|
414
|
+
],
|
|
415
|
+
faqs: [
|
|
416
|
+
{ question: 'Is there a free trial?', answer: 'Yes, 14-day free trial on all plans.' },
|
|
417
|
+
],
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Tools: navigate_to_section, show_pricing, open_signup, open_demo
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
## Troubleshooting
|
|
424
|
+
|
|
425
|
+
### Button only shows icon, no "Ask AI" text
|
|
426
|
+
Your Tailwind config isn't scanning the package:
|
|
427
|
+
```js
|
|
428
|
+
content: ['./node_modules/ai-site-pilot/dist/**/*.{js,mjs}']
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
### Theme colors not working
|
|
432
|
+
Use component props, not CSS variables:
|
|
433
|
+
```tsx
|
|
434
|
+
// Good
|
|
435
|
+
<SitePilot theme={{ accent: 'pink' }} />
|
|
436
|
+
|
|
437
|
+
// Bad - don't set CSS vars directly
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### AI gives generic responses
|
|
441
|
+
Use `siteContent` to provide actual data about your site. The AI only knows what you tell it.
|
|
442
|
+
|
|
443
|
+
### Tools not executing
|
|
444
|
+
1. Check `onToolCall` is passed to SitePilot
|
|
445
|
+
2. Verify tool names match between API and client handler
|
|
446
|
+
3. Check browser console for errors
|
|
447
|
+
|
|
448
|
+
## Available Models
|
|
449
|
+
|
|
450
|
+
| Model | ID | Notes |
|
|
451
|
+
|-------|-----|-------|
|
|
452
|
+
| Gemini 2.0 Flash | `google/gemini-2.0-flash-exp:free` | **Free!** Default |
|
|
453
|
+
| GPT-4o | `openai/gpt-4o` | Best overall |
|
|
454
|
+
| Claude 3.5 Sonnet | `anthropic/claude-3.5-sonnet` | Best for coding |
|
|
455
|
+
| Llama 3.1 70B | `meta-llama/llama-3.1-70b-instruct` | Open source |
|
|
456
|
+
|
|
457
|
+
See all models at [openrouter.ai/models](https://openrouter.ai/models)
|
package/package.json
CHANGED