Skip to content

Commit 3a48f61

Browse files
authored
add v0 (#70)
* add v0 * Delete pnpm-workspace.yaml
1 parent e1e4c69 commit 3a48f61

27 files changed

+637
-5
lines changed

app/api/ai/generate/route.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ Action types:
187187
- Scrape: {"actionType": "Scrape", "url": "https://example.com"}
188188
- Search: {"actionType": "Search", "query": "search query", "limit": 10}
189189
- Condition: {"actionType": "Condition", "condition": "{{@nodeId:Label.field}} === 'value'"}
190+
- Create Chat (v0): {"actionType": "Create Chat", "message": "Create a line graph showing DAU over time", "system": "You are an expert coder"} - Use v0 for generating UI components, visualizations (charts, graphs, dashboards), landing pages, or any React/Next.js code. PREFER v0 over Generate Text/Image for any visual output like charts, graphs, or UI.
191+
- Send Message (v0): {"actionType": "Send Message", "chatId": "{{@nodeId:Label.chatId}}", "message": "Add dark mode"} - Use this to continue a v0 chat conversation
190192
191193
CRITICAL ABOUT CONDITION NODES:
192194
- Condition nodes evaluate a boolean expression

components/settings/integration-form-dialog.tsx

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,11 @@ type IntegrationFormData = {
4242
const INTEGRATION_TYPES: IntegrationType[] = [
4343
"ai-gateway",
4444
"database",
45+
"firecrawl",
4546
"linear",
4647
"resend",
4748
"slack",
48-
"firecrawl",
49+
"v0",
4950
];
5051

5152
const INTEGRATION_LABELS: Record<IntegrationType, string> = {
@@ -55,6 +56,7 @@ const INTEGRATION_LABELS: Record<IntegrationType, string> = {
5556
database: "Database",
5657
"ai-gateway": "AI Gateway",
5758
firecrawl: "Firecrawl",
59+
v0: "v0",
5860
};
5961

6062
export function IntegrationFormDialog({
@@ -290,6 +292,30 @@ export function IntegrationFormDialog({
290292
</p>
291293
</div>
292294
);
295+
case "v0":
296+
return (
297+
<div className="space-y-2">
298+
<Label htmlFor="apiKey">API Key</Label>
299+
<Input
300+
id="apiKey"
301+
onChange={(e) => updateConfig("apiKey", e.target.value)}
302+
placeholder="v0_..."
303+
type="password"
304+
value={formData.config.apiKey || ""}
305+
/>
306+
<p className="text-muted-foreground text-xs">
307+
Get your API key from{" "}
308+
<a
309+
className="underline hover:text-foreground"
310+
href="https://v0.dev/chat/settings/keys"
311+
rel="noopener noreferrer"
312+
target="_blank"
313+
>
314+
v0.dev/chat/settings/keys
315+
</a>
316+
</p>
317+
</div>
318+
);
293319
default:
294320
return null;
295321
}

components/settings/integrations-manager.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const INTEGRATION_TYPE_LABELS: Record<IntegrationType, string> = {
2626
database: "Database",
2727
"ai-gateway": "AI Gateway",
2828
firecrawl: "Firecrawl",
29+
v0: "v0",
2930
};
3031

3132
type IntegrationsManagerProps = {

components/ui/template-autocomplete.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,19 @@ const getCommonFields = (node: WorkflowNode) => {
177177
if (actionType === "Search") {
178178
return [{ field: "web", description: "Array of search results" }];
179179
}
180+
if (actionType === "Create Chat") {
181+
return [
182+
{ field: "chatId", description: "v0 chat ID" },
183+
{ field: "url", description: "v0 chat URL" },
184+
{ field: "demoUrl", description: "Demo preview URL" },
185+
];
186+
}
187+
if (actionType === "Send Message") {
188+
return [
189+
{ field: "chatId", description: "v0 chat ID" },
190+
{ field: "demoUrl", description: "Demo preview URL" },
191+
];
192+
}
180193
if (node.data.type === "trigger") {
181194
const triggerType = node.data.config?.triggerType as string | undefined;
182195
const webhookSchema = node.data.config?.webhookSchema as string | undefined;

components/workflow/nodes/action-node.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ const getIntegrationFromActionType = (actionType: string): string => {
7979
Scrape: "Firecrawl",
8080
Search: "Firecrawl",
8181
Condition: "Condition",
82+
"Create Chat": "v0",
83+
"Send Message": "v0",
8284
};
8385
return integrationMap[actionType] || "System";
8486
};
@@ -106,6 +108,8 @@ const requiresIntegration = (actionType: string): boolean => {
106108
"Database Query",
107109
"Scrape",
108110
"Search",
111+
"Create Chat",
112+
"Send Message",
109113
];
110114
return requiresIntegrationActions.includes(actionType);
111115
};
@@ -139,6 +143,9 @@ const getProviderLogo = (actionType: string) => {
139143
return <Code className="size-12 text-green-300" strokeWidth={1.5} />;
140144
case "Condition":
141145
return <GitBranch className="size-12 text-pink-300" strokeWidth={1.5} />;
146+
case "Create Chat":
147+
case "Send Message":
148+
return <IntegrationIcon className="size-12" integration="v0" />;
142149
default:
143150
return <Zap className="size-12 text-amber-300" strokeWidth={1.5} />;
144151
}

components/workflow/utils/code-generators.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import { searchCodegenTemplate } from "@/plugins/firecrawl/codegen/search";
1212
import { createTicketCodegenTemplate } from "@/plugins/linear/codegen/create-ticket";
1313
import { sendEmailCodegenTemplate } from "@/plugins/resend/codegen/send-email";
1414
import { sendSlackMessageCodegenTemplate } from "@/plugins/slack/codegen/send-slack-message";
15+
import { createChatCodegenTemplate } from "@/plugins/v0/codegen/create-chat";
16+
import { sendMessageCodegenTemplate } from "@/plugins/v0/codegen/send-message";
1517

1618
// Generate code snippet for a single node
1719
export const generateNodeCode = (node: {
@@ -84,6 +86,10 @@ export async function POST(request: NextRequest) {
8486
return scrapeCodegenTemplate;
8587
case "Search":
8688
return searchCodegenTemplate;
89+
case "Create Chat":
90+
return createChatCodegenTemplate;
91+
case "Send Message":
92+
return sendMessageCodegenTemplate;
8793
default:
8894
return `async function actionStep(input: Record<string, unknown>) {
8995
"use step";

lib/api-client.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,8 @@ export type IntegrationType =
317317
| "slack"
318318
| "database"
319319
| "ai-gateway"
320-
| "firecrawl";
320+
| "firecrawl"
321+
| "v0";
321322

322323
export type IntegrationConfig = {
323324
apiKey?: string;

lib/credential-fetcher.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export type WorkflowCredentials = {
2626
AI_GATEWAY_API_KEY?: string;
2727
DATABASE_URL?: string;
2828
FIRECRAWL_API_KEY?: string;
29+
V0_API_KEY?: string;
2930
};
3031

3132
function mapResendConfig(config: IntegrationConfig): WorkflowCredentials {
@@ -82,6 +83,14 @@ function mapFirecrawlConfig(config: IntegrationConfig): WorkflowCredentials {
8283
return creds;
8384
}
8485

86+
function mapV0Config(config: IntegrationConfig): WorkflowCredentials {
87+
const creds: WorkflowCredentials = {};
88+
if (config.apiKey) {
89+
creds.V0_API_KEY = config.apiKey;
90+
}
91+
return creds;
92+
}
93+
8594
/**
8695
* Map integration config to WorkflowCredentials format
8796
*/
@@ -107,6 +116,9 @@ function mapIntegrationConfig(
107116
if (integrationType === "firecrawl") {
108117
return mapFirecrawlConfig(config);
109118
}
119+
if (integrationType === "v0") {
120+
return mapV0Config(config);
121+
}
110122
return {};
111123
}
112124

lib/db/integrations.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ export type IntegrationType =
100100
| "slack"
101101
| "database"
102102
| "ai-gateway"
103-
| "firecrawl";
103+
| "firecrawl"
104+
| "v0";
104105

105106
export type IntegrationConfig = {
106107
// Resend
@@ -115,6 +116,7 @@ export type IntegrationConfig = {
115116
openaiApiKey?: string;
116117
// Firecrawl
117118
firecrawlApiKey?: string;
119+
// v0 (uses apiKey)
118120
};
119121

120122
export type DecryptedIntegration = {

lib/db/schema.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,13 @@ export const integrations = pgTable("integrations", {
8585
type: text("type")
8686
.notNull()
8787
.$type<
88-
"resend" | "linear" | "slack" | "database" | "ai-gateway" | "firecrawl"
88+
| "resend"
89+
| "linear"
90+
| "slack"
91+
| "database"
92+
| "ai-gateway"
93+
| "firecrawl"
94+
| "v0"
8995
>(),
9096
// biome-ignore lint/suspicious/noExplicitAny: JSONB type - encrypted credentials stored as JSON
9197
config: jsonb("config").notNull().$type<any>(),

0 commit comments

Comments
 (0)