open-prompt / src /app /frameworks /page.tsx
GitHub Action
Automated sync to Hugging Face
bcce530
"use client"
import { useState } from "react"
import { useUser } from "@stackframe/stack"
import { useRouter } from "next/navigation"
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Textarea } from "@/components/ui/textarea"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { ModelSelector } from "@/components/model-selector"
import { DEFAULT_MODEL } from "@/types/prompt"
import ReactMarkdown from "react-markdown"
import {
BookOpen,
Sparkles,
Copy,
Check,
ChevronRight,
Lightbulb,
Play,
Loader2,
Save
} from "lucide-react"
interface Framework {
id: string
name: string
acronym: string
description: string
color: string
sections: {
letter: string
name: string
description: string
placeholder: string
}[]
example: string
}
const FRAMEWORKS: Framework[] = [
{
id: "race",
name: "RACE",
acronym: "Role, Action, Context, Expectation",
description: "Define who the AI is, what to do, the situation, and desired outcome",
color: "from-blue-500 to-cyan-500",
sections: [
{ letter: "R", name: "Role", description: "Who should the AI act as?", placeholder: "e.g., Act as an experienced marketing consultant" },
{ letter: "A", name: "Action", description: "What should the AI do?", placeholder: "e.g., Create a marketing strategy for..." },
{ letter: "C", name: "Context", description: "What's the situation?", placeholder: "e.g., For a startup launching a new mobile app" },
{ letter: "E", name: "Expectation", description: "What's the desired outcome?", placeholder: "e.g., Provide 5 actionable strategies with timelines" },
],
example: "Act as an experienced marketing consultant. Create a comprehensive marketing strategy for a B2B SaaS startup launching a project management tool. The target audience is small to medium businesses. Provide 5 actionable strategies with detailed implementation timelines and expected ROI."
},
{
id: "care",
name: "CARE",
acronym: "Context, Action, Result, Example",
description: "Provide background, specify task, define output, and show examples",
color: "from-green-500 to-emerald-500",
sections: [
{ letter: "C", name: "Context", description: "What's the background?", placeholder: "e.g., I'm building an e-commerce website" },
{ letter: "A", name: "Action", description: "What task needs to be done?", placeholder: "e.g., Write product descriptions" },
{ letter: "R", name: "Result", description: "What output do you want?", placeholder: "e.g., 100-word descriptions with features and benefits" },
{ letter: "E", name: "Example", description: "Show an example if possible", placeholder: "e.g., Here's a sample description format..." },
],
example: "Context: I'm launching a premium coffee subscription service targeting busy professionals. Action: Write compelling email subject lines for our welcome sequence. Result: Give me 10 subject lines under 50 characters that drive high open rates. Example: 'Your morning ritual just upgraded' is the tone I'm looking for."
},
{
id: "ape",
name: "APE",
acronym: "Action, Purpose, Expectation",
description: "Simple framework focusing on task, reason, and outcome",
color: "from-orange-500 to-red-500",
sections: [
{ letter: "A", name: "Action", description: "What action should be taken?", placeholder: "e.g., Analyze this sales data" },
{ letter: "P", name: "Purpose", description: "Why is this being done?", placeholder: "e.g., To identify top-performing products" },
{ letter: "E", name: "Expectation", description: "What's the expected output?", placeholder: "e.g., A ranked list with insights" },
],
example: "Analyze the quarterly sales report I'll provide. The purpose is to identify our top 3 performing products and understand why they're successful. I expect a summary with percentage growth, key success factors, and recommendations for other products."
},
{
id: "create",
name: "CREATE",
acronym: "Character, Request, Examples, Adjustments, Type, Extras",
description: "Comprehensive framework for detailed, nuanced prompts",
color: "from-purple-500 to-pink-500",
sections: [
{ letter: "C", name: "Character", description: "Who is the AI?", placeholder: "e.g., You are a senior UX designer" },
{ letter: "R", name: "Request", description: "What's the main request?", placeholder: "e.g., Review this app's user flow" },
{ letter: "E", name: "Examples", description: "Provide examples", placeholder: "e.g., Good flows: Spotify, Bad flows: ..." },
{ letter: "A", name: "Adjustments", description: "Any constraints?", placeholder: "e.g., Focus on mobile, ignore web" },
{ letter: "T", name: "Type", description: "Output format?", placeholder: "e.g., Bullet points with priority levels" },
{ letter: "E", name: "Extras", description: "Additional notes?", placeholder: "e.g., Be critical, I need honest feedback" },
],
example: "Character: You are a senior UX designer with 10+ years experience in mobile apps. Request: Review my fitness app's onboarding flow. Examples: Reference the smooth onboarding in Headspace and Duolingo. Adjustments: Focus only on the first-time user experience. Type: Provide feedback as numbered points with severity ratings (1-5). Extras: Be brutally honest - I need to ship next week."
},
{
id: "risen",
name: "RISEN",
acronym: "Role, Instructions, Steps, End goal, Narrowing",
description: "Step-by-step framework with clear constraints",
color: "from-indigo-500 to-violet-500",
sections: [
{ letter: "R", name: "Role", description: "Define the AI's role", placeholder: "e.g., Technical writer" },
{ letter: "I", name: "Instructions", description: "Core instructions", placeholder: "e.g., Document this API" },
{ letter: "S", name: "Steps", description: "Step-by-step process", placeholder: "e.g., 1. Overview 2. Endpoints 3. Examples" },
{ letter: "E", name: "End goal", description: "Final objective", placeholder: "e.g., Complete API documentation" },
{ letter: "N", name: "Narrowing", description: "Constraints & limits", placeholder: "e.g., Max 2 pages, no jargon" },
],
example: "Role: You are a technical writer specializing in developer documentation. Instructions: Create documentation for our REST API. Steps: Start with a quick overview, then list each endpoint with method/URL/params, add code examples, and end with error codes. End goal: A developer should be able to integrate within 30 minutes. Narrowing: Keep it under 3 pages, use simple language, and include curl examples only."
},
]
export default function FrameworksClient() {
const user = useUser()
const router = useRouter()
const [selectedFramework, setSelectedFramework] = useState<Framework | null>(null)
const [sectionValues, setSectionValues] = useState<Record<string, string>>({})
const [generatedPrompt, setGeneratedPrompt] = useState("")
const [copied, setCopied] = useState(false)
// AI Testing state
const [selectedModel, setSelectedModel] = useState<string>(DEFAULT_MODEL)
const [aiOutput, setAiOutput] = useState("")
const [isRunning, setIsRunning] = useState(false)
// Save state
const [isSaving, setIsSaving] = useState(false)
const [promptTitle, setPromptTitle] = useState("")
// Select a framework
const selectFramework = (framework: Framework) => {
setSelectedFramework(framework)
setSectionValues({})
setGeneratedPrompt("")
}
// Update section value
const updateSection = (letter: string, value: string) => {
setSectionValues(prev => ({ ...prev, [letter]: value }))
}
// Generate prompt from sections
const generatePrompt = () => {
if (!selectedFramework) return
const parts = selectedFramework.sections
.map(section => {
const value = sectionValues[section.letter]
if (value?.trim()) {
return `**${section.name}:** ${value}`
}
return null
})
.filter(Boolean)
setGeneratedPrompt(parts.join("\n\n"))
}
// Copy prompt
const copyPrompt = async () => {
await navigator.clipboard.writeText(generatedPrompt)
setCopied(true)
setTimeout(() => setCopied(false), 2000)
}
// Use example
const useExample = () => {
if (!selectedFramework) return
setGeneratedPrompt(selectedFramework.example)
}
// Run with AI
const runWithAI = async () => {
if (!generatedPrompt.trim()) return
setIsRunning(true)
setAiOutput("")
try {
const response = await fetch("/api/run", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
prompt: generatedPrompt,
model: selectedModel,
}),
})
const reader = response.body?.getReader()
const decoder = new TextDecoder()
if (reader) {
while (true) {
const { done, value } = await reader.read()
if (done) break
const chunk = decoder.decode(value)
setAiOutput(prev => prev + chunk)
}
}
} catch (error) {
console.error("Failed to run:", error)
setAiOutput("Error: Failed to get AI response")
} finally {
setIsRunning(false)
}
}
// Save to Library
const saveToLibrary = async () => {
if (!generatedPrompt.trim() || !selectedFramework) return
setIsSaving(true)
try {
const response = await fetch("/api/prompts", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
title: promptTitle || `${selectedFramework.name} Framework Prompt`,
prompt: generatedPrompt,
description: `Created using ${selectedFramework.name} framework`,
category: "general",
visibility: "private",
creatorId: undefined, // handled server-side
}),
})
if (response.ok) {
const data = await response.json()
router.push(`/p/${data.slug}`)
}
} catch (error) {
console.error("Failed to save:", error)
} finally {
setIsSaving(false)
}
}
return (
<div className="container mx-auto px-4 py-8 max-w-6xl">
{/* Hero Section */}
<div className="text-center mb-12">
<div className="inline-flex items-center justify-center h-16 w-16 rounded-2xl bg-linear-to-br from-primary to-accent mb-4">
<BookOpen className="h-8 w-8 text-white" />
</div>
<h1 className="text-4xl md:text-5xl font-serif font-medium mb-4">
Prompt <span className="text-gradient italic">Frameworks</span>
</h1>
<p className="text-xl text-muted-foreground max-w-2xl mx-auto">
Use proven frameworks to create better prompts. Guided templates for consistent results.
</p>
</div>
<div className="grid lg:grid-cols-3 gap-6">
{/* Framework Selector */}
<div className="space-y-4">
<h2 className="text-lg font-semibold mb-4">Choose a Framework</h2>
{FRAMEWORKS.map(framework => (
<Card
key={framework.id}
className={`cursor-pointer transition-all ${selectedFramework?.id === framework.id
? "ring-2 ring-primary"
: "hover:border-primary/50"
}`}
onClick={() => selectFramework(framework)}
>
<CardContent className="p-4">
<div className="flex items-center justify-between mb-2">
<Badge className={`bg-linear-to-r ${framework.color} text-white border-0`}>
{framework.name}
</Badge>
<ChevronRight className={`h-4 w-4 transition-transform ${selectedFramework?.id === framework.id ? "rotate-90" : ""
}`} />
</div>
<p className="text-sm font-medium mb-1">{framework.acronym}</p>
<p className="text-sm text-muted-foreground">{framework.description}</p>
</CardContent>
</Card>
))}
</div>
{/* Framework Builder */}
<div className="lg:col-span-2">
{selectedFramework ? (
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<div>
<CardTitle className="flex items-center gap-2">
<Badge className={`bg-linear-to-r ${selectedFramework.color} text-white border-0`}>
{selectedFramework.name}
</Badge>
Framework Builder
</CardTitle>
<CardDescription>{selectedFramework.acronym}</CardDescription>
</div>
<Button variant="outline" size="sm" onClick={useExample} className="gap-2">
<Lightbulb className="h-4 w-4" />
See Example
</Button>
</div>
</CardHeader>
<CardContent className="space-y-6">
{/* Section Inputs */}
<div className="space-y-4">
{selectedFramework.sections.map(section => (
<div key={section.letter} className="space-y-2">
<Label className="flex items-center gap-2">
<span className={`inline-flex items-center justify-center h-6 w-6 rounded-full bg-linear-to-r ${selectedFramework.color} text-white text-xs font-bold`}>
{section.letter}
</span>
{section.name}
<span className="text-muted-foreground font-normal">
- {section.description}
</span>
</Label>
<Textarea
value={sectionValues[section.letter] || ""}
onChange={(e) => updateSection(section.letter, e.target.value)}
placeholder={section.placeholder}
rows={2}
/>
</div>
))}
</div>
{/* Generate Button */}
<Button onClick={generatePrompt} className="w-full gap-2">
<Sparkles className="h-4 w-4" />
Generate Prompt
</Button>
{/* Generated Prompt */}
{generatedPrompt && (
<div className="space-y-4">
<div className="flex items-center justify-between">
<Label>Generated Prompt</Label>
<Button variant="ghost" size="sm" onClick={copyPrompt} className="gap-2">
{copied ? (
<>
<Check className="h-4 w-4" />
Copied!
</>
) : (
<>
<Copy className="h-4 w-4" />
Copy
</>
)}
</Button>
</div>
<div className="p-4 rounded-lg border bg-muted/30 whitespace-pre-wrap">
{generatedPrompt}
</div>
{/* Run with AI Section */}
<div className="border-t pt-4 space-y-3">
<Label>Test with AI</Label>
<div className="flex gap-3">
<div className="flex-1">
<ModelSelector
value={selectedModel}
onChange={setSelectedModel}
/>
</div>
<Button
onClick={runWithAI}
disabled={isRunning}
className="gap-2"
>
{isRunning ? (
<>
<Loader2 className="h-4 w-4 animate-spin" />
Running...
</>
) : (
<>
<Play className="h-4 w-4" />
Run with AI
</>
)}
</Button>
</div>
{/* AI Output */}
{aiOutput && (
<div className="max-h-60 overflow-auto rounded-lg border bg-background p-4">
<div className="prose prose-sm dark:prose-invert max-w-none">
<ReactMarkdown>{aiOutput}</ReactMarkdown>
</div>
</div>
)}
</div>
{/* Save to Library Section */}
<div className="border-t pt-4 space-y-3">
<Label>Save to Library</Label>
<div className="flex gap-3">
<Input
value={promptTitle}
onChange={(e) => setPromptTitle(e.target.value)}
placeholder="Prompt title (optional)"
className="flex-1"
/>
<Button
variant="outline"
onClick={saveToLibrary}
disabled={isSaving}
className="gap-2"
>
{isSaving ? (
<>
<Loader2 className="h-4 w-4 animate-spin" />
Saving...
</>
) : (
<>
<Save className="h-4 w-4" />
Save
</>
)}
</Button>
</div>
</div>
</div>
)}
</CardContent>
</Card>
) : (
<Card className="h-full flex items-center justify-center border-dashed">
<CardContent className="p-12 text-center text-muted-foreground">
<BookOpen className="h-16 w-16 mx-auto mb-4 opacity-20" />
<p className="text-lg font-medium mb-2">Select a Framework</p>
<p>Choose a framework from the left to start building your prompt</p>
</CardContent>
</Card>
)}
</div>
</div>
</div>
)
}