Skip to content

Commit 1c40055

Browse files
committed
feat: convert WebPreview component to Vue 3 and update backend route handling
1 parent 8e5bcf9 commit 1c40055

File tree

1 file changed

+78
-89
lines changed

1 file changed

+78
-89
lines changed

apps/www/content/3.components/1.chatbot/web-preview.md

Lines changed: 78 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,9 @@ npm i v0-sdk
4040

4141
Add the following component to your frontend:
4242
<!-- TOOD: Using Vue 3 example -->
43-
```tsx title="app/page.tsx"
44-
'use client'
45-
46-
import { useState } from 'react'
43+
```vue title="app.vue"
44+
<script setup lang="ts">
45+
import { Loader } from '@/components/ai-elements/loader'
4746
import {
4847
Input,
4948
PromptInputSubmit,
@@ -55,102 +54,92 @@ import {
5554
WebPreviewNavigation,
5655
WebPreviewUrl,
5756
} from '@/components/ai-elements/web-preview'
58-
import { Loader } from '../ai-elements/loader'
59-
60-
function WebPreviewDemo() {
61-
const [previewUrl, setPreviewUrl] = useState('')
62-
const [prompt, setPrompt] = useState('')
63-
const [isGenerating, setIsGenerating] = useState(false)
64-
65-
const handleSubmit = async (e: React.FormEvent) => {
66-
e.preventDefault()
67-
if (!prompt.trim())
68-
return
69-
setPrompt('')
70-
71-
setIsGenerating(true)
72-
try {
73-
const response = await fetch('/api/v0', {
74-
method: 'POST',
75-
headers: { 'Content-Type': 'application/json' },
76-
body: JSON.stringify({ prompt }),
77-
})
78-
79-
const data = await response.json()
80-
setPreviewUrl(data.demo || '/')
81-
console.log('Generation finished:', data)
82-
}
83-
catch (error) {
84-
console.error('Generation failed:', error)
85-
}
86-
finally {
87-
setIsGenerating(false)
88-
}
89-
}
9057
91-
return (
92-
<div className="max-w-4xl mx-auto p-6 relative size-full rounded-lg border h-[600px]">
93-
<div className="flex flex-col h-full">
94-
<div className="flex-1 mb-4">
95-
{isGenerating
96-
? (
97-
<div className="flex flex-col items-center justify-center h-full">
98-
<Loader />
99-
<p className="mt-4 text-muted-foreground">
100-
Generating app, this may take a few seconds...
101-
</p>
102-
</div>
103-
)
104-
: previewUrl
105-
? (
106-
<WebPreview defaultUrl={previewUrl}>
107-
<WebPreviewNavigation>
108-
<WebPreviewUrl />
109-
</WebPreviewNavigation>
110-
<WebPreviewBody src={previewUrl} />
111-
</WebPreview>
112-
)
113-
: (
114-
<div className="flex items-center justify-center h-full text-muted-foreground">
115-
Your generated app will appear here
116-
</div>
117-
)}
58+
const previewUrl = ref('')
59+
const prompt = ref('')
60+
const isGenerating = ref(false)
61+
62+
async function handleSubmit(e: Event) {
63+
e.preventDefault()
64+
if (!prompt.value.trim())
65+
return
66+
prompt.value = ''
67+
68+
isGenerating.value = true
69+
try {
70+
const response = await fetch('/api/v0', {
71+
method: 'POST',
72+
headers: { 'Content-Type': 'application/json' },
73+
body: JSON.stringify({ prompt: prompt.value }),
74+
})
75+
76+
const data = await response.json()
77+
previewUrl.value = data.demo || '/'
78+
console.log('Generation finished:', data)
79+
}
80+
catch (error) {
81+
console.error('Generation failed:', error)
82+
}
83+
finally {
84+
isGenerating.value = false
85+
}
86+
}
87+
</script>
88+
89+
<template>
90+
<div class="max-w-4xl mx-auto p-6 relative size-full rounded-lg border h-[600px]">
91+
<div class="flex flex-col h-full">
92+
<div class="flex-1 mb-4">
93+
<div v-if="isGenerating" class="flex flex-col items-center justify-center h-full">
94+
<Loader />
95+
<p v-if="isGenerating" class="mt-4 text-muted-foreground">
96+
Generating app, this may take a few seconds...
97+
</p>
11898
</div>
99+
<WebPreview v-else-if="previewUrl" :default-url="previewUrl">
100+
<WebPreviewNavigation>
101+
<WebPreviewUrl />
102+
</WebPreviewNavigation>
103+
<WebPreviewBody :src="previewUrl" />
104+
</WebPreview>
105+
<div v-else class="flex items-center justify-center h-full text-muted-foreground">
106+
Your generated app will appear here
107+
</div>
108+
</div>
119109
120-
<Input
121-
onSubmit={handleSubmit}
122-
className="w-full max-w-2xl mx-auto relative"
110+
<Input
111+
class="w-full max-w-2xl mx-auto relative"
112+
@submit="handleSubmit"
113+
>
114+
<PromptInputTextarea
115+
:value="prompt"
116+
placeholder="Describe the app you want to build..."
117+
class="pr-12 min-h-[60px]"
118+
@change="(e: any) => (prompt = e?.target?.value ?? '')"
123119
>
124-
<PromptInputTextarea
125-
value={prompt}
126-
placeholder="Describe the app you want to build..."
127-
onChange={e => setPrompt(e.currentTarget.value)}
128-
className="pr-12 min-h-[60px]"
129-
/>
130120
<PromptInputSubmit
131-
status={isGenerating ? 'streaming' : 'ready'}
132-
disabled={!prompt.trim()}
133-
className="absolute bottom-1 right-1"
121+
:status="isGenerating ? 'streaming' : 'ready'"
122+
:disabled="!prompt.trim()"
123+
class="absolute bottom-1 right-1"
134124
/>
135-
</Input>
136-
</div>
125+
</PromptInputTextarea>
126+
</Input>
137127
</div>
138-
)
139-
}
140-
141-
export default WebPreviewDemo
128+
</div>
129+
</template>
142130
```
143131

144132
Add the following route to your backend:
145133

146134
<!-- TOOD: Using Nuxt example -->
147135

148-
```ts title="app/api/v0/route.ts"
136+
```ts title="server/api/v0.post.ts"
137+
import type { ChatsCreateResponse } from 'v0-sdk'
138+
import { defineEventHandler, readBody } from 'h3'
149139
import { v0 } from 'v0-sdk'
150140

151-
export async function POST(req: Request) {
152-
const { prompt }: { prompt: string } = await req.json()
153-
141+
export default defineEventHandler(async (event) => {
142+
const { prompt }: { prompt: string } = await readBody(event)
154143
const result = await v0.chats.create({
155144
system: 'You are an expert coder',
156145
message: prompt,
@@ -159,13 +148,13 @@ export async function POST(req: Request) {
159148
imageGenerations: false,
160149
thinking: false,
161150
},
162-
})
151+
}) as ChatsCreateResponse
163152

164-
return Response.json({
153+
return {
165154
demo: result.demo,
166155
webUrl: result.webUrl,
167-
})
168-
}
156+
}
157+
})
169158
```
170159

171160
## Features

0 commit comments

Comments
 (0)