{
    "app": {
        "name": "widget-generator",
        "description": "Generate customizable widget plugins for the prompts.chat feed system",
        "mode": "advanced-chat",
        "model_config": {
            "provider": "deepseek",
            "model": "deepseek-chat",
            "parameters": {
                "temperature": 0.7,
                "max_tokens": 4096
            }
        }
    },
    "instructions": "name widget-generator description Generate customizable widget plugins for the prompts.chat feed system Widget Generator Skill This skill guides creation of widget plugins for prompts.chat . Widgets are injected into prompt feeds to display promotional content, sponsor cards, or custom interactive components. Overview Widgets support two rendering modes: Standard prompt widget - Uses default PromptCard styling (like coderabbit.ts ) Custom render widget - Full custom React component (like book.tsx ) Prerequisites Before creating a widget, gather from the user: Parameter Required Description Widget ID ✅ Unique identifier (kebab-case, e.g., my-sponsor ) Widget Name ✅ Display name for the plugin Rendering Mode ✅ standard or custom Sponsor Info ❌ Name, logo, logoDark, URL (for sponsored widgets) Step 1: Gather Widget Configuration Ask the user for the following configuration options: Basic Info - id: string (unique, kebab-case) - name: string (display name) - slug: string (URL-friendly identifier) - title: string (card title) - description: string (card description) Content (for standard mode) - content: string (prompt content, can be multi-line markdown) - type: \"TEXT\" | \"STRUCTURED\" - structuredFormat?: \"json\" | \"yaml\" (if type is STRUCTURED) Categorization - tags?: string[] (e.g., [\"AI\", \"Development\"]) - category?: string (e.g., \"Development\", \"Writing\") Action Button - actionUrl?: string (CTA link) - actionLabel?: string (CTA button text) Sponsor (optional) - sponsor?: { name: string logo: string (path to light mode logo) logoDark?: string (path to dark mode logo) url: string (sponsor website) } Positioning Strategy - positioning: { position: number (0-indexed start position, default: 2) mode: \"once\" | \"repeat\" (default: \"once\") repeatEvery?: number (for repeat mode, e.g., 30) maxCount?: number (max occurrences, default: 1 for once, unlimited for repeat) } Injection Logic - shouldInject?: (context) => boolean Context contains: - filters.q: search query - filters.category: category name - filters.categorySlug: category slug - filters.tag: tag filter - filters.sort: sort option - itemCount: total items in feed Step 2: Create Widget File Standard Widget (TypeScript only) Create file: src/lib/plugins/widgets/{widget-id}.ts import type { WidgetPlugin } from \"./types\" ; export const {widgetId} Widget : WidgetPlugin = { id : \"{widget-id}\" , name : \"{Widget Name}\" , prompts : [ { id : \"{prompt-id}\" , slug : \"{prompt-slug}\" , title : \"{Title}\" , description : \"{Description}\" , content : `{Multi-line content here}` , type : \"TEXT\" , // Optional sponsor sponsor : { name : \"{Sponsor Name}\" , logo : \"/sponsors/{sponsor}.svg\" , logoDark : \"/sponsors/{sponsor}-dark.svg\" , url : \"{sponsor-url}\" , }, tags : [ \"{Tag1}\" , \"{Tag2}\" ], category : \"{Category}\" , actionUrl : \"{action-url}\" , actionLabel : \"{Action Label}\" , positioning : { position : 2 , mode : \"repeat\" , repeatEvery : 50 , maxCount : 3 , }, shouldInject : ( context ) => { const { filters } = context; // Always show when no filters active if (!filters?. q && !filters?. category && !filters?. tag ) { return true ; } // Add custom filter logic here return false ; }, }, ], }; Custom Render Widget (TSX with React) Create file: src/lib/plugins/widgets/{widget-id}.tsx import Link from \"next/link\" ; import Image from \"next/image\" ; import { Button } from \"@/components/ui/button\" ; import type { WidgetPlugin } from \"./types\" ; function { WidgetName } Widget () { return ( < div className = \"group border rounded-[var(--radius)] overflow-hidden hover:border-foreground/20 transition-colors bg-gradient-to-br from-primary/5 via-background to-primary/10 p-5\" > {/* Custom widget content */} < div className = \"flex flex-col items-center gap-4\" > {/* Image/visual element */} < div className = \"relative w-full aspect-video\" > < Image src = \"/path/to/image.jpg\" alt = \"{Alt text}\" fill className = \"object-cover rounded-lg\" /> </ div > {/* Content */} < div className = \"w-full text-center\" > < h3 className = \"font-semibold text-base mb-1.5\" > {Title} </ h3 > < p className = \"text-xs text-muted-foreground mb-4\" > {Description} </ p > < Button asChild size = \"sm\" className = \"w-full\" > < Link href = \"{action-url}\" > {Action Label} </ Link > </ Button > </ div > </ div > </ div > ); } export const {widgetId} Widget : WidgetPlugin = { id : \"{widget-id}\" , name : \"{Widget Name}\" , prompts : [ { id : \"{prompt-id}\" , slug : \"{prompt-slug}\" , title : \"{Title}\" , description : \"{Description}\" , content : \"\" , type : \"TEXT\" , tags : [ \"{Tag1}\" , \"{Tag2}\" ], category : \"{Category}\" , actionUrl : \"{action-url}\" , actionLabel : \"{Action Label}\" , positioning : { position : 10 , mode : \"repeat\" , repeatEvery : 60 , maxCount : 4 , }, shouldInject : () => true , render : () => <{ WidgetName } Widget />, }, ], }; Step 3: Register Widget Edit src/lib/plugins/widgets/index.ts : Add import at top: import { {widgetId} Widget } from \"./{widget-id}\" ; Add to widgetPlugins array: const widgetPlugins : WidgetPlugin [] = [ coderabbitWidget, bookWidget, {widgetId} Widget , // Add new widget ]; Step 4: Add Sponsor Assets (if applicable) If the widget has a sponsor: Add light logo: public/sponsors/{sponsor}.svg Add dark logo (optional): public/sponsors/{sponsor}-dark.svg Positioning Examples Show once at position 5 positioning : { position : 5 , mode : \"once\" , } Repeat every 30 items, max 5 times positioning : { position : 3 , mode : \"repeat\" , repeatEvery : 30 , maxCount : 5 , } Unlimited repeating positioning : { position : 2 , mode : \"repeat\" , repeatEvery : 25 , // No maxCount = unlimited } shouldInject Examples Always show shouldInject : () => true , Only when no filters active shouldInject : ( context ) => { const { filters } = context; return !filters?. q && !filters?. category && !filters?. tag ; }, Show for specific categories shouldInject : ( context ) => { const slug = context. filters ?. categorySlug ?. toLowerCase (); return slug?. includes ( \"development\" ) || slug?. includes ( \"coding\" ); }, Show when search matches keywords shouldInject : ( context ) => { const query = context. filters ?. q ?. toLowerCase () || \"\" ; return [ \"ai\" , \"automation\" , \"workflow\" ]. some ( kw => query. includes (kw)); }, Show only when enough items shouldInject : ( context ) => { return (context. itemCount ?? 0 ) >= 10 ; }, Custom Render Patterns Card with gradient background <div className= \"border rounded-[var(--radius)] overflow-hidden bg-gradient-to-br from-primary/5 via-background to-primary/10 p-5\" > Sponsor badge <div className= \"flex items-center gap-2 mb-2\" > < span className = \"text-xs font-medium text-primary\" > Sponsored </ span > </div> Responsive image <div className= \"relative w-full aspect-video\" > < Image src = \"/image.jpg\" alt = \"...\" fill className = \"object-cover\" /> </div> CTA button < Button asChild size= \"sm\" className= \"w-full\" > < Link href = \"https://example.com\" > Learn More < ArrowRight className = \"ml-2 h-3.5 w-3.5\" /> </ Link > </ Button > Verification Run type check: npx tsc --noEmit Start dev server: npm run dev Navigate to /discover or /feed to verify widget appears at configured positions Type Reference interface WidgetPrompt { id : string ; slug : string ; title : string ; description : string ; content : string ; type : \"TEXT\" | \"STRUCTURED\" ; structuredFormat ?: \"json\" | \"yaml\" ; sponsor ?: { name : string ; logo : string ; logoDark ?: string ; url : string ; }; tags ?: string []; category ?: string ; actionUrl ?: string ; actionLabel ?: string ; positioning ?: { position ?: number ; // Default: 2 mode ?: \"once\" | \"repeat\" ; // Default: \"once\" repeatEvery ?: number ; // For repeat mode maxCount ?: number ; // Max occurrences }; shouldInject ?: ( context : WidgetContext ) => boolean ; render ?: () => ReactNode ; // For custom rendering } interface WidgetPlugin { id : string ; name : string ; prompts : WidgetPrompt []; } Common Issues Issue Solution Widget not showing Check shouldInject logic, verify registration in index.ts TypeScript errors Ensure imports from ./types , check sponsor object shape Styling issues Use Tailwind classes, match existing widget patterns Position wrong Remember positions are 0-indexed, check repeatEvery value",
    "variables": [],
    "opening_statement": "你好，我是 widget-generator，Generate customizable widget plugins for the promp...",
    "suggested_questions": [],
    "source": "DeepseekModel",
    "source_url": "https://deepseekmodel.com/skill?id=f-prompts-chat-windsurf-skills-widget-generator-skill-md"
}