diff --git a/docs/content/docs/2.components/accordion.md b/docs/content/docs/2.components/accordion.md
index bd3e4d42b7..6342fbc6c7 100644
--- a/docs/content/docs/2.components/accordion.md
+++ b/docs/content/docs/2.components/accordion.md
@@ -65,6 +65,7 @@ externalTypes:
- AccordionItem[]
hide:
- class
+ai: true
props:
class: 'px-4'
items:
diff --git a/docs/content/docs/2.components/alert.md b/docs/content/docs/2.components/alert.md
index f039cf6f7a..d8911139ab 100644
--- a/docs/content/docs/2.components/alert.md
+++ b/docs/content/docs/2.components/alert.md
@@ -15,6 +15,7 @@ Use the `title` prop to set the title of the Alert.
::component-code
---
+ai: true
props:
title: 'Heads up!'
---
diff --git a/docs/content/docs/2.components/select-menu.md b/docs/content/docs/2.components/select-menu.md
index 4032373253..02e3db82c8 100644
--- a/docs/content/docs/2.components/select-menu.md
+++ b/docs/content/docs/2.components/select-menu.md
@@ -60,6 +60,7 @@ ignore:
external:
- items
- modelValue
+ai: true
props:
modelValue: 'Backlog'
items:
diff --git a/docs/package.json b/docs/package.json
index 5d3184c251..685d6fe015 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -29,6 +29,7 @@
"better-sqlite3": "^12.2.0",
"capture-website": "^5.0.0",
"joi": "^18.0.1",
+ "json-schema-to-zod": "^2.6.1",
"maska": "^3.2.0",
"motion-v": "^1.7.1",
"nuxt": "^4.0.3",
diff --git a/docs/server/api/content-generation.ts b/docs/server/api/content-generation.ts
new file mode 100644
index 0000000000..0006755a1f
--- /dev/null
+++ b/docs/server/api/content-generation.ts
@@ -0,0 +1,92 @@
+import { getComponentMeta } from 'nuxt-component-meta/parser'
+import { propsToJsonSchema } from 'nuxt-component-meta/utils'
+import { jsonSchemaToZod } from 'json-schema-to-zod'
+import { gateway } from '@ai-sdk/gateway'
+import { generateObject } from 'ai'
+import { z } from 'zod'
+
+export default defineEventHandler(async (event) => {
+ try {
+ const { prompt, componentName } = await readValidatedBody(event, z.object({
+ prompt: z.string(),
+ componentName: z.string()
+ }).parse)
+
+ const schema = getComponentSchema(componentName)
+
+ if (!schema) {
+ throw createError({
+ statusCode: 400,
+ statusMessage: `Component ${componentName} not supported`
+ })
+ }
+
+ const systemPrompt = `You are an expert content generator specialized in populating component properties with realistic, meaningful data.
+
+Rules:
+- NEVER use HTML tags in text content (no , , , , etc.)
+- ONLY use plain text for all content fields
+- ONLY generate properties that exist in the provided schema
+- DO NOT invent new properties or keys
+- Generate ONLY the exact structure required by the schema
+
+LUCIDE ICONS - USE ONLY EXISTING ICONS:
+- Use format: i-lucide-[icon-name]
+- Common icons that EXIST: i-lucide-home, i-lucide-user, i-lucide-settings, i-lucide-mail, i-lucide-phone, i-lucide-briefcase, i-lucide-code, i-lucide-palette, i-lucide-camera, i-lucide-map-pin, i-lucide-clock, i-lucide-star, i-lucide-heart, i-lucide-shield, i-lucide-globe, i-lucide-folder, i-lucide-file, i-lucide-image, i-lucide-music, i-lucide-video, i-lucide-download, i-lucide-upload, i-lucide-search, i-lucide-plus, i-lucide-minus, i-lucide-check, i-lucide-x, i-lucide-arrow-right, i-lucide-arrow-left, i-lucide-chevron-right, i-lucide-chevron-down
+- DO NOT invent icons like "i-lucide-tools", "i-lucide-design", etc.
+- When in doubt, use common icons like i-lucide-folder, i-lucide-file, or i-lucide-star
+
+CONTENT GUIDELINES:
+- Generate content that matches the user's prompt and use case
+- Use clear, descriptive labels and meaningful text
+- Keep content concise but informative
+- Ensure all generated data is realistic and would be genuinely useful in a real application
+- If generating arrays/lists, create 3-5 items unless otherwise specified
+- If generating links, use '#' as the source
+- Use professional, modern language appropriate for web applications
+
+Component: ${componentName}
+Generate ONLY the exact structure required by the schema. Do not add extra properties.`
+
+ const result = await generateObject({
+ model: gateway('openai/gpt-4.1-nano'),
+ system: systemPrompt,
+ prompt: `${prompt}. Generate content for a ${componentName} component.`,
+ temperature: 0.3,
+ schema
+ })
+
+ return result.object
+ } catch (error) {
+ console.error('AI generation error:', error)
+ throw createError({
+ statusCode: 500,
+ statusMessage: 'Internal server error during AI generation'
+ })
+ }
+})
+
+const AI_PROPS = [
+ 'items',
+ 'description',
+ 'title',
+ 'label',
+ 'text'
+]
+
+export function getComponentSchema(componentName: string): z.ZodType | null {
+ try {
+ const meta = getComponentMeta(`src/runtime/components/${componentName}.vue`)
+
+ if (!meta?.props) return null
+
+ const filteredProps = Object.values(meta.props).filter((prop: any) => AI_PROPS.includes(prop.name))
+ if (filteredProps.length === 0) return null
+
+ const jsonSchema = propsToJsonSchema(filteredProps)
+ const zodString = jsonSchemaToZod(jsonSchema)
+ return eval(zodString)
+ } catch {
+ return null
+ }
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d7abea3fcb..c645fb8ef1 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -297,6 +297,9 @@ importers:
joi:
specifier: ^18.0.1
version: 18.0.1
+ json-schema-to-zod:
+ specifier: ^2.6.1
+ version: 2.6.1
maska:
specifier: ^3.2.0
version: 3.2.0