mirror of
https://github.com/f/awesome-chatgpt-prompts.git
synced 2026-03-03 02:57:01 +00:00
feat(messages): add hero prompt input translations for multiple languages
This commit is contained in:
@@ -650,6 +650,20 @@
|
||||
"tokens": "رموز",
|
||||
"noChanges": "لا توجد تغييرات"
|
||||
},
|
||||
"heroPromptInput": {
|
||||
"placeholder": "صف البرومبت الذي تريد إنشاءه...",
|
||||
"ariaLabel": "صف البرومبت الذي تريد إنشاءه",
|
||||
"submit": "إنشاء برومبت",
|
||||
"hint": "انقر للبدء في الإنشاء بالذكاء الاصطناعي",
|
||||
"modelName": "وكيل البرومبت",
|
||||
"examples": {
|
||||
"codeReview": "أنشئ مساعد مراجعة كود يكتشف الأخطاء",
|
||||
"emailWriter": "أنشئ كاتب بريد إلكتروني احترافي لأي مناسبة",
|
||||
"studyPlanner": "صمم منشئ خطط دراسية مخصصة",
|
||||
"recipeGenerator": "أنشئ منشئ وصفات بناءً على المكونات المتوفرة",
|
||||
"interviewCoach": "أنشئ مدرب تحضير للمقابلات"
|
||||
}
|
||||
},
|
||||
"notFound": {
|
||||
"title": "الصفحة غير موجودة",
|
||||
"description": "الصفحة التي تبحث عنها غير موجودة أو تم نقلها.",
|
||||
@@ -676,7 +690,11 @@
|
||||
"currentPrompt": "قيد الإنشاء:",
|
||||
"stateTitle": "العنوان",
|
||||
"stateContent": "المحتوى",
|
||||
"stateTags": "وسوم"
|
||||
"stateTags": "وسوم",
|
||||
"editAction1": "املأ الحقول الناقصة، حدّث الوسوم.",
|
||||
"editAction2": "حسّن المتغيرات",
|
||||
"editAction3": "استخدم المتغيرات",
|
||||
"editAction4": "حوّل إلى أمر JSON"
|
||||
},
|
||||
"report": {
|
||||
"report": "إبلاغ",
|
||||
|
||||
@@ -650,6 +650,20 @@
|
||||
"tokens": "Token",
|
||||
"noChanges": "Keine Änderungen"
|
||||
},
|
||||
"heroPromptInput": {
|
||||
"placeholder": "Beschreiben Sie den Prompt, den Sie erstellen möchten...",
|
||||
"ariaLabel": "Beschreiben Sie den Prompt, den Sie erstellen möchten",
|
||||
"submit": "Prompt erstellen",
|
||||
"hint": "Klicken Sie, um mit KI zu erstellen",
|
||||
"modelName": "Prompt-Agent",
|
||||
"examples": {
|
||||
"codeReview": "Erstellen Sie einen Code-Review-Assistenten, der Bugs findet",
|
||||
"emailWriter": "Bauen Sie einen professionellen E-Mail-Verfasser für jeden Anlass",
|
||||
"studyPlanner": "Entwerfen Sie einen personalisierten Lernplan-Generator",
|
||||
"recipeGenerator": "Erstellen Sie einen Rezeptgenerator basierend auf verfügbaren Zutaten",
|
||||
"interviewCoach": "Erstellen Sie einen Vorstellungsgespräch-Coach"
|
||||
}
|
||||
},
|
||||
"notFound": {
|
||||
"title": "Seite nicht gefunden",
|
||||
"description": "Die gesuchte Seite existiert nicht oder wurde verschoben.",
|
||||
@@ -676,7 +690,11 @@
|
||||
"currentPrompt": "Erstellen:",
|
||||
"stateTitle": "Titel",
|
||||
"stateContent": "Inhalt",
|
||||
"stateTags": "Tags"
|
||||
"stateTags": "Tags",
|
||||
"editAction1": "Fehlende Felder ausfüllen, Tags aktualisieren.",
|
||||
"editAction2": "Variablen verbessern",
|
||||
"editAction3": "Variablen verwenden",
|
||||
"editAction4": "In JSON-Prompt konvertieren"
|
||||
},
|
||||
"report": {
|
||||
"report": "Melden",
|
||||
|
||||
@@ -660,6 +660,20 @@
|
||||
"categories": "Categories",
|
||||
"createPrompt": "Create Prompt"
|
||||
},
|
||||
"heroPromptInput": {
|
||||
"placeholder": "Describe the prompt you want to build...",
|
||||
"ariaLabel": "Describe the prompt you want to build",
|
||||
"submit": "Create prompt",
|
||||
"hint": "Click to start building with AI",
|
||||
"modelName": "Prompt Agent",
|
||||
"examples": {
|
||||
"codeReview": "Create a code review assistant that catches bugs",
|
||||
"emailWriter": "Build a professional email writer for any occasion",
|
||||
"studyPlanner": "Design a personalized study plan generator",
|
||||
"recipeGenerator": "Make a recipe creator based on available ingredients",
|
||||
"interviewCoach": "Create an interview preparation coach"
|
||||
}
|
||||
},
|
||||
"promptBuilder": {
|
||||
"title": "Prompt Building Agent",
|
||||
"openBuilder": "Prompt Agent",
|
||||
@@ -676,7 +690,11 @@
|
||||
"currentPrompt": "Building:",
|
||||
"stateTitle": "Title",
|
||||
"stateContent": "Content",
|
||||
"stateTags": "tags"
|
||||
"stateTags": "tags",
|
||||
"editAction1": "Fill missing fields, update tags.",
|
||||
"editAction2": "Make variables better",
|
||||
"editAction3": "Use variables",
|
||||
"editAction4": "Convert to JSON prompt"
|
||||
},
|
||||
"report": {
|
||||
"report": "Report",
|
||||
|
||||
@@ -650,6 +650,20 @@
|
||||
"tokens": "tokens",
|
||||
"noChanges": "Sin cambios"
|
||||
},
|
||||
"heroPromptInput": {
|
||||
"placeholder": "Describe el prompt que quieres crear...",
|
||||
"ariaLabel": "Describe el prompt que quieres crear",
|
||||
"submit": "Crear prompt",
|
||||
"hint": "Haz clic para empezar a crear con IA",
|
||||
"modelName": "Agente de Prompts",
|
||||
"examples": {
|
||||
"codeReview": "Crea un asistente de revisión de código que detecte errores",
|
||||
"emailWriter": "Construye un escritor de emails profesionales para cualquier ocasión",
|
||||
"studyPlanner": "Diseña un generador de planes de estudio personalizados",
|
||||
"recipeGenerator": "Crea un generador de recetas basado en ingredientes disponibles",
|
||||
"interviewCoach": "Crea un coach de preparación para entrevistas"
|
||||
}
|
||||
},
|
||||
"notFound": {
|
||||
"title": "Página No Encontrada",
|
||||
"description": "La página que buscas no existe o ha sido movida.",
|
||||
@@ -676,7 +690,11 @@
|
||||
"currentPrompt": "Construyendo:",
|
||||
"stateTitle": "Título",
|
||||
"stateContent": "Contenido",
|
||||
"stateTags": "etiquetas"
|
||||
"stateTags": "etiquetas",
|
||||
"editAction1": "Completar campos faltantes, actualizar etiquetas.",
|
||||
"editAction2": "Mejorar variables",
|
||||
"editAction3": "Usar variables",
|
||||
"editAction4": "Convertir a prompt JSON"
|
||||
},
|
||||
"report": {
|
||||
"report": "Reportar",
|
||||
|
||||
@@ -650,6 +650,20 @@
|
||||
"tokens": "tokens",
|
||||
"noChanges": "Aucune modification"
|
||||
},
|
||||
"heroPromptInput": {
|
||||
"placeholder": "Décrivez le prompt que vous voulez créer...",
|
||||
"ariaLabel": "Décrivez le prompt que vous voulez créer",
|
||||
"submit": "Créer un prompt",
|
||||
"hint": "Cliquez pour commencer à créer avec l'IA",
|
||||
"modelName": "Agent de Prompts",
|
||||
"examples": {
|
||||
"codeReview": "Créez un assistant de revue de code qui détecte les bugs",
|
||||
"emailWriter": "Construisez un rédacteur d'emails professionnels pour toute occasion",
|
||||
"studyPlanner": "Concevez un générateur de plans d'études personnalisés",
|
||||
"recipeGenerator": "Créez un générateur de recettes basé sur les ingrédients disponibles",
|
||||
"interviewCoach": "Créez un coach de préparation aux entretiens"
|
||||
}
|
||||
},
|
||||
"notFound": {
|
||||
"title": "Page Non Trouvée",
|
||||
"description": "La page que vous recherchez n'existe pas ou a été déplacée.",
|
||||
@@ -676,7 +690,11 @@
|
||||
"currentPrompt": "En construction :",
|
||||
"stateTitle": "Titre",
|
||||
"stateContent": "Contenu",
|
||||
"stateTags": "tags"
|
||||
"stateTags": "tags",
|
||||
"editAction1": "Remplir les champs manquants, mettre à jour les tags.",
|
||||
"editAction2": "Améliorer les variables",
|
||||
"editAction3": "Utiliser des variables",
|
||||
"editAction4": "Convertir en prompt JSON"
|
||||
},
|
||||
"report": {
|
||||
"report": "Signaler",
|
||||
|
||||
@@ -650,6 +650,20 @@
|
||||
"tokens": "token",
|
||||
"noChanges": "Nessuna modifica"
|
||||
},
|
||||
"heroPromptInput": {
|
||||
"placeholder": "Descrivi il prompt che vuoi creare...",
|
||||
"ariaLabel": "Descrivi il prompt che vuoi creare",
|
||||
"submit": "Crea prompt",
|
||||
"hint": "Clicca per iniziare a creare con l'IA",
|
||||
"modelName": "Agente Prompt",
|
||||
"examples": {
|
||||
"codeReview": "Crea un assistente di revisione codice che rileva bug",
|
||||
"emailWriter": "Costruisci uno scrittore di email professionali per ogni occasione",
|
||||
"studyPlanner": "Progetta un generatore di piani di studio personalizzati",
|
||||
"recipeGenerator": "Crea un generatore di ricette basato sugli ingredienti disponibili",
|
||||
"interviewCoach": "Crea un coach di preparazione ai colloqui"
|
||||
}
|
||||
},
|
||||
"notFound": {
|
||||
"title": "Pagina Non Trovata",
|
||||
"description": "La pagina che stai cercando non esiste o è stata spostata.",
|
||||
@@ -676,7 +690,11 @@
|
||||
"currentPrompt": "In costruzione:",
|
||||
"stateTitle": "Titolo",
|
||||
"stateContent": "Contenuto",
|
||||
"stateTags": "tag"
|
||||
"stateTags": "tag",
|
||||
"editAction1": "Compila i campi mancanti, aggiorna i tag.",
|
||||
"editAction2": "Migliora le variabili",
|
||||
"editAction3": "Usa le variabili",
|
||||
"editAction4": "Converti in prompt JSON"
|
||||
},
|
||||
"report": {
|
||||
"report": "Segnala",
|
||||
|
||||
@@ -650,6 +650,20 @@
|
||||
"tokens": "トークン",
|
||||
"noChanges": "変更なし"
|
||||
},
|
||||
"heroPromptInput": {
|
||||
"placeholder": "作成したいプロンプトを説明してください...",
|
||||
"ariaLabel": "作成したいプロンプトを説明してください",
|
||||
"submit": "プロンプトを作成",
|
||||
"hint": "クリックしてAIで作成を開始",
|
||||
"modelName": "プロンプトエージェント",
|
||||
"examples": {
|
||||
"codeReview": "バグを検出するコードレビューアシスタントを作成",
|
||||
"emailWriter": "あらゆる場面に対応するプロフェッショナルなメール作成ツールを構築",
|
||||
"studyPlanner": "パーソナライズされた学習プランジェネレーターを設計",
|
||||
"recipeGenerator": "手持ちの食材でレシピを作成するツールを作成",
|
||||
"interviewCoach": "面接対策コーチを作成"
|
||||
}
|
||||
},
|
||||
"notFound": {
|
||||
"title": "ページが見つかりません",
|
||||
"description": "お探しのページは存在しないか、移動された可能性があります。",
|
||||
@@ -676,7 +690,11 @@
|
||||
"currentPrompt": "構築中:",
|
||||
"stateTitle": "タイトル",
|
||||
"stateContent": "内容",
|
||||
"stateTags": "タグ"
|
||||
"stateTags": "タグ",
|
||||
"editAction1": "不足フィールドを埋め、タグを更新。",
|
||||
"editAction2": "変数を改善",
|
||||
"editAction3": "変数を使用",
|
||||
"editAction4": "JSONプロンプトに変換"
|
||||
},
|
||||
"report": {
|
||||
"report": "報告",
|
||||
|
||||
@@ -650,6 +650,20 @@
|
||||
"tokens": "토큰",
|
||||
"noChanges": "변경 없음"
|
||||
},
|
||||
"heroPromptInput": {
|
||||
"placeholder": "만들고 싶은 프롬프트를 설명하세요...",
|
||||
"ariaLabel": "만들고 싶은 프롬프트를 설명하세요",
|
||||
"submit": "프롬프트 만들기",
|
||||
"hint": "클릭하여 AI로 만들기 시작",
|
||||
"modelName": "프롬프트 에이전트",
|
||||
"examples": {
|
||||
"codeReview": "버그를 잡아내는 코드 리뷰 어시스턴트 만들기",
|
||||
"emailWriter": "모든 상황에 맞는 전문 이메일 작성기 만들기",
|
||||
"studyPlanner": "맞춤형 학습 계획 생성기 설계",
|
||||
"recipeGenerator": "가용 재료로 레시피 생성기 만들기",
|
||||
"interviewCoach": "면접 준비 코치 만들기"
|
||||
}
|
||||
},
|
||||
"notFound": {
|
||||
"title": "페이지를 찾을 수 없습니다",
|
||||
"description": "찾고 계신 페이지가 존재하지 않거나 이동되었습니다.",
|
||||
@@ -676,7 +690,11 @@
|
||||
"currentPrompt": "구축 중:",
|
||||
"stateTitle": "제목",
|
||||
"stateContent": "내용",
|
||||
"stateTags": "태그"
|
||||
"stateTags": "태그",
|
||||
"editAction1": "누락된 필드 채우기, 태그 업데이트.",
|
||||
"editAction2": "변수 개선",
|
||||
"editAction3": "변수 사용",
|
||||
"editAction4": "JSON 프롬프트로 변환"
|
||||
},
|
||||
"report": {
|
||||
"report": "신고",
|
||||
|
||||
@@ -650,6 +650,20 @@
|
||||
"tokens": "tokens",
|
||||
"noChanges": "Sem alterações"
|
||||
},
|
||||
"heroPromptInput": {
|
||||
"placeholder": "Descreva o prompt que você quer criar...",
|
||||
"ariaLabel": "Descreva o prompt que você quer criar",
|
||||
"submit": "Criar prompt",
|
||||
"hint": "Clique para começar a criar com IA",
|
||||
"modelName": "Agente de Prompts",
|
||||
"examples": {
|
||||
"codeReview": "Crie um assistente de revisão de código que detecta bugs",
|
||||
"emailWriter": "Construa um escritor de emails profissionais para qualquer ocasião",
|
||||
"studyPlanner": "Projete um gerador de planos de estudo personalizados",
|
||||
"recipeGenerator": "Crie um gerador de receitas baseado em ingredientes disponíveis",
|
||||
"interviewCoach": "Crie um coach de preparação para entrevistas"
|
||||
}
|
||||
},
|
||||
"notFound": {
|
||||
"title": "Página Não Encontrada",
|
||||
"description": "A página que você está procurando não existe ou foi movida.",
|
||||
@@ -676,7 +690,11 @@
|
||||
"currentPrompt": "Construindo:",
|
||||
"stateTitle": "Título",
|
||||
"stateContent": "Conteúdo",
|
||||
"stateTags": "tags"
|
||||
"stateTags": "tags",
|
||||
"editAction1": "Preencher campos faltantes, atualizar tags.",
|
||||
"editAction2": "Melhorar variáveis",
|
||||
"editAction3": "Usar variáveis",
|
||||
"editAction4": "Converter para prompt JSON"
|
||||
},
|
||||
"report": {
|
||||
"report": "Denunciar",
|
||||
|
||||
@@ -650,6 +650,20 @@
|
||||
"tokens": "token",
|
||||
"noChanges": "Değişiklik yok"
|
||||
},
|
||||
"heroPromptInput": {
|
||||
"placeholder": "Oluşturmak istediğiniz promptu tanımlayın...",
|
||||
"ariaLabel": "Oluşturmak istediğiniz promptu tanımlayın",
|
||||
"submit": "Prompt oluştur",
|
||||
"hint": "AI ile oluşturmaya başlamak için tıklayın",
|
||||
"modelName": "Prompt Ajanı",
|
||||
"examples": {
|
||||
"codeReview": "Hataları yakalayan bir kod inceleme asistanı oluştur",
|
||||
"emailWriter": "Her durum için profesyonel e-posta yazıcısı oluştur",
|
||||
"studyPlanner": "Kişiselleştirilmiş çalışma planı oluşturucu tasarla",
|
||||
"recipeGenerator": "Mevcut malzemelere göre tarif oluşturucu yap",
|
||||
"interviewCoach": "Mülakat hazırlık koçu oluştur"
|
||||
}
|
||||
},
|
||||
"notFound": {
|
||||
"title": "Sayfa Bulunamadı",
|
||||
"description": "Aradığınız sayfa mevcut değil veya taşınmış olabilir.",
|
||||
@@ -676,7 +690,11 @@
|
||||
"currentPrompt": "Oluşturuluyor:",
|
||||
"stateTitle": "Başlık",
|
||||
"stateContent": "İçerik",
|
||||
"stateTags": "etiketler"
|
||||
"stateTags": "etiketler",
|
||||
"editAction1": "Eksik alanları doldur, etiketleri güncelle.",
|
||||
"editAction2": "Değişkenleri iyileştir",
|
||||
"editAction3": "Değişkenleri kullan",
|
||||
"editAction4": "JSON promptuna dönüştür"
|
||||
},
|
||||
"report": {
|
||||
"report": "Şikayet Et",
|
||||
|
||||
@@ -650,6 +650,20 @@
|
||||
"tokens": "token",
|
||||
"noChanges": "无变更"
|
||||
},
|
||||
"heroPromptInput": {
|
||||
"placeholder": "描述你想要创建的提示词...",
|
||||
"ariaLabel": "描述你想要创建的提示词",
|
||||
"submit": "创建提示词",
|
||||
"hint": "点击开始用AI创建",
|
||||
"modelName": "提示词助手",
|
||||
"examples": {
|
||||
"codeReview": "创建一个能发现bug的代码审查助手",
|
||||
"emailWriter": "打造一个适用于各种场合的专业邮件撰写器",
|
||||
"studyPlanner": "设计一个个性化学习计划生成器",
|
||||
"recipeGenerator": "制作一个根据现有食材生成食谱的工具",
|
||||
"interviewCoach": "创建一个面试准备教练"
|
||||
}
|
||||
},
|
||||
"notFound": {
|
||||
"title": "页面未找到",
|
||||
"description": "您要查找的页面不存在或已被移动。",
|
||||
@@ -676,7 +690,11 @@
|
||||
"currentPrompt": "构建中:",
|
||||
"stateTitle": "标题",
|
||||
"stateContent": "内容",
|
||||
"stateTags": "标签"
|
||||
"stateTags": "标签",
|
||||
"editAction1": "填写缺失字段,更新标签。",
|
||||
"editAction2": "改进变量",
|
||||
"editAction3": "使用变量",
|
||||
"editAction4": "转换为JSON提示词"
|
||||
},
|
||||
"report": {
|
||||
"report": "举报",
|
||||
|
||||
1180
package-lock.json
generated
1180
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -51,6 +51,7 @@
|
||||
"react": "19.2.0",
|
||||
"react-dom": "19.2.0",
|
||||
"react-hook-form": "^7.68.0",
|
||||
"react-markdown": "^10.1.0",
|
||||
"sharp": "^0.33.5",
|
||||
"sonner": "^2.0.7",
|
||||
"tailwind-merge": "^3.4.0",
|
||||
|
||||
@@ -10,17 +10,23 @@ import {
|
||||
|
||||
const GENERATIVE_MODEL = process.env.OPENAI_GENERATIVE_MODEL || "gpt-4o-mini";
|
||||
|
||||
const SYSTEM_PROMPT = `You are an expert prompt engineer agent. Your job is to quickly build high-quality prompts.
|
||||
const SYSTEM_PROMPT = `You are an expert prompt engineer agent. Your job is to quickly build high-quality prompts that match the style and quality of existing prompts in the database.
|
||||
|
||||
MANDATORY FIRST STEP - LEARN FROM EXAMPLES:
|
||||
Before creating ANY prompt, you MUST first call search_prompts to study existing prompts in the database.
|
||||
This is NON-NEGOTIABLE. You need to understand how prompts are written in this system before creating new ones.
|
||||
Study the structure, tone, format, and quality of existing prompts and match that style.
|
||||
|
||||
IMPORTANT - REASONING FORMAT:
|
||||
Before EVERY tool call, write a brief reasoning line starting with "→" to explain what you're about to do.
|
||||
Example: "→ Searching for similar prompts to get inspiration..."
|
||||
Example: "→ Searching for similar prompts to learn the style..."
|
||||
This makes your actions transparent and agentic.
|
||||
|
||||
WORKFLOW FOR NEW PROMPTS:
|
||||
1. ALWAYS call search_prompts first to find similar examples for inspiration
|
||||
2. Immediately set the prompt fields using tools (set_title, set_description, set_content, set_tags)
|
||||
3. Only respond with a brief summary of what you created
|
||||
1. MANDATORY: Call search_prompts FIRST to study 3-5 similar examples - learn their structure, tone, and format
|
||||
2. Analyze the examples to understand how prompts are written in this database
|
||||
3. Create your prompt matching that same quality and style using tools (set_title, set_description, set_content, set_tags)
|
||||
4. Only respond with a brief summary of what you created
|
||||
|
||||
WORKFLOW FOR CHANGES/EDITS:
|
||||
1. The current prompt state is provided below - review it first
|
||||
@@ -52,23 +58,53 @@ If the user's request mentions any of these, automatically call set_media_requir
|
||||
Set appropriate mediaType (IMAGE, VIDEO, DOCUMENT) based on context.
|
||||
|
||||
RULES:
|
||||
- NEVER skip searching for examples - this is your first action for ANY new prompt
|
||||
- Be ACTION-ORIENTED: Use tools immediately, don't ask many questions
|
||||
- ALWAYS write reasoning ("→ ...") before each tool call
|
||||
- For NEW prompts: search for examples first, then build
|
||||
- For NEW prompts: MUST search for examples first to learn the style, then build matching that style
|
||||
- For EDITS: modify only what the user asked, don't rewrite everything
|
||||
- For JSON: search structured prompts first, then convert preserving all content
|
||||
- Auto-detect and set media requirements when user mentions files/uploads
|
||||
- Use variables: \${variableName} or \${variableName:defaultValue} for customizable parts
|
||||
- Keep responses SHORT - just confirm what you did
|
||||
- If the request is clear, act immediately
|
||||
- If the request is clear, act immediately (but always search examples first for new prompts)
|
||||
- Only ask ONE clarifying question if absolutely necessary
|
||||
|
||||
PROMPT STYLE (MANDATORY FOR TEXT PROMPTS):
|
||||
- ALWAYS use "Act as" role-playing format: "Act as a [role]. You are [description]..."
|
||||
- Be INSTRUCTIVE and IMPERATIVE: Use "do this", "act as", "you will", "your task is"
|
||||
- Define a clear ROLE/PERSONA the AI should adopt
|
||||
- Include specific RESPONSIBILITIES and BEHAVIORS
|
||||
- Add CONSTRAINTS and RULES for the role
|
||||
- Example format:
|
||||
"Act as a [Role]. You are an expert in [domain] with [experience/skills].
|
||||
Your task is to [main objective].
|
||||
You will:
|
||||
- [Responsibility 1]
|
||||
- [Responsibility 2]
|
||||
Rules:
|
||||
- [Constraint 1]
|
||||
- [Constraint 2]"
|
||||
|
||||
VARIABLES (HIGHLY ENCOURAGED):
|
||||
- ALWAYS look for opportunities to add variables to make prompts reusable
|
||||
- Use syntax: \${variableName} or \${variableName:defaultValue}
|
||||
- Common variable patterns:
|
||||
- \${topic} - main subject/topic
|
||||
- \${language:English} - target language with default
|
||||
- \${tone:professional} - writing tone
|
||||
- \${length:medium} - output length
|
||||
- \${context} - additional context from user
|
||||
- \${input} - user's input text to process
|
||||
- Variables make prompts flexible and powerful - include at least 1-2 in every prompt
|
||||
- Example: "Translate the following text to \${language:Spanish}"
|
||||
|
||||
PROMPT QUALITY:
|
||||
- Write clear, specific instructions
|
||||
- Include context and constraints
|
||||
- Add examples when helpful
|
||||
- Use structured sections for complex prompts
|
||||
- Make prompts reusable with variables
|
||||
- Make prompts reusable with variables (see above)
|
||||
|
||||
You have tools to: search_prompts (with promptType and structuredFormat filters), set_title, set_description, set_content, set_type, set_tags, set_category, set_privacy, set_media_requirements, get_current_state, get_available_tags, get_available_categories.
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { auth } from "@/lib/auth";
|
||||
import { getConfig } from "@/lib/config";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { DiscoveryPrompts } from "@/components/prompts/discovery-prompts";
|
||||
import { HeroPromptInput } from "@/components/prompts/hero-prompt-input";
|
||||
|
||||
function getOrdinalSuffix(n: number): string {
|
||||
const s = ["th", "st", "nd", "rd"];
|
||||
@@ -24,6 +25,7 @@ export default async function HomePage() {
|
||||
const showRegisterButton = !session && (isOAuth || (config.auth.provider === "credentials" && config.auth.allowRegistration));
|
||||
|
||||
const useCloneBranding = config.homepage?.useCloneBranding ?? false;
|
||||
const aiGenerationEnabled = config.features?.aiGeneration ?? false;
|
||||
|
||||
// Fetch GitHub stars dynamically (with caching) - only if not using clone branding
|
||||
let githubStars = 139000; // fallback
|
||||
@@ -67,16 +69,25 @@ export default async function HomePage() {
|
||||
</div>
|
||||
) : (
|
||||
<div className="absolute top-0 right-0 bottom-0 w-1/2 hidden md:block pointer-events-none">
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-background via-background/80 to-transparent z-10" />
|
||||
<video
|
||||
autoPlay
|
||||
loop
|
||||
muted
|
||||
playsInline
|
||||
className="absolute top-1/2 -translate-y-1/2 right-0 w-full h-auto opacity-30 dark:opacity-15 dark:invert"
|
||||
>
|
||||
<source src="/animation.mp4" type="video/mp4" />
|
||||
</video>
|
||||
{/* Video background */}
|
||||
<div className="absolute inset-0">
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-background via-background/80 to-transparent z-10" />
|
||||
<video
|
||||
autoPlay
|
||||
loop
|
||||
muted
|
||||
playsInline
|
||||
className="absolute top-1/2 -translate-y-1/2 right-0 w-full h-auto opacity-30 dark:opacity-15 dark:invert"
|
||||
>
|
||||
<source src="/animation.mp4" type="video/mp4" />
|
||||
</video>
|
||||
</div>
|
||||
{/* Animated input overlay - only show if AI generation is enabled */}
|
||||
{aiGenerationEnabled && (
|
||||
<div className="absolute inset-0 hidden lg:flex items-center justify-center z-30 pr-8 pointer-events-auto">
|
||||
<HeroPromptInput />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -11,9 +11,14 @@ export const metadata: Metadata = {
|
||||
description: "Create a new prompt",
|
||||
};
|
||||
|
||||
export default async function NewPromptPage() {
|
||||
interface PageProps {
|
||||
searchParams: Promise<{ prompt?: string }>;
|
||||
}
|
||||
|
||||
export default async function NewPromptPage({ searchParams }: PageProps) {
|
||||
const session = await auth();
|
||||
const t = await getTranslations("prompts");
|
||||
const { prompt: initialPromptRequest } = await searchParams;
|
||||
|
||||
if (!session?.user) {
|
||||
redirect("/login");
|
||||
@@ -46,6 +51,7 @@ export default async function NewPromptPage() {
|
||||
tags={tags}
|
||||
aiGenerationEnabled={aiGenerationEnabled}
|
||||
aiModelName={aiModelName}
|
||||
initialPromptRequest={initialPromptRequest}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
185
src/components/prompts/hero-prompt-input.tsx
Normal file
185
src/components/prompts/hero-prompt-input.tsx
Normal file
@@ -0,0 +1,185 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useRef, useEffect, useCallback, useMemo } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Bot, ArrowUp } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const TYPING_SPEED = 50; // ms per character
|
||||
const PAUSE_BETWEEN_PROMPTS = 2000; // ms to pause after completing a prompt
|
||||
const DELETE_SPEED = 30; // ms per character when deleting
|
||||
|
||||
export function HeroPromptInput() {
|
||||
const t = useTranslations("heroPromptInput");
|
||||
const router = useRouter();
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
const examplePrompts = useMemo(() => [
|
||||
t("examples.codeReview"),
|
||||
t("examples.emailWriter"),
|
||||
t("examples.studyPlanner"),
|
||||
t("examples.recipeGenerator"),
|
||||
t("examples.interviewCoach"),
|
||||
], [t]);
|
||||
|
||||
const [displayText, setDisplayText] = useState("");
|
||||
const [inputValue, setInputValue] = useState("");
|
||||
const [isFocused, setIsFocused] = useState(false);
|
||||
const [isAnimating, setIsAnimating] = useState(true);
|
||||
const [currentPromptIndex, setCurrentPromptIndex] = useState(0);
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
|
||||
const animationRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
const clearAnimation = useCallback(() => {
|
||||
if (animationRef.current) {
|
||||
clearTimeout(animationRef.current);
|
||||
animationRef.current = null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Typing animation effect
|
||||
useEffect(() => {
|
||||
if (!isAnimating || isFocused) {
|
||||
clearAnimation();
|
||||
return;
|
||||
}
|
||||
|
||||
const currentPrompt = examplePrompts[currentPromptIndex];
|
||||
|
||||
if (isDeleting) {
|
||||
if (displayText.length > 0) {
|
||||
animationRef.current = setTimeout(() => {
|
||||
setDisplayText((prev) => prev.slice(0, -1));
|
||||
}, DELETE_SPEED);
|
||||
} else {
|
||||
setIsDeleting(false);
|
||||
setCurrentPromptIndex((prev) => (prev + 1) % examplePrompts.length);
|
||||
}
|
||||
} else {
|
||||
if (displayText.length < currentPrompt.length) {
|
||||
animationRef.current = setTimeout(() => {
|
||||
setDisplayText(currentPrompt.slice(0, displayText.length + 1));
|
||||
}, TYPING_SPEED);
|
||||
} else {
|
||||
// Finished typing, wait then start deleting
|
||||
animationRef.current = setTimeout(() => {
|
||||
setIsDeleting(true);
|
||||
}, PAUSE_BETWEEN_PROMPTS);
|
||||
}
|
||||
}
|
||||
|
||||
return clearAnimation;
|
||||
}, [displayText, isAnimating, isFocused, currentPromptIndex, isDeleting, clearAnimation, examplePrompts]);
|
||||
|
||||
const handleFocus = () => {
|
||||
setIsFocused(true);
|
||||
setIsAnimating(false);
|
||||
clearAnimation();
|
||||
// Transfer the animated text to the actual input value
|
||||
setInputValue(displayText);
|
||||
setDisplayText("");
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
setIsFocused(false);
|
||||
// Only restart animation if input is empty
|
||||
if (!inputValue.trim()) {
|
||||
setIsAnimating(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = (e?: React.FormEvent) => {
|
||||
e?.preventDefault();
|
||||
const value = inputValue.trim();
|
||||
if (value) {
|
||||
router.push(`/prompts/new?prompt=${encodeURIComponent(value)}`);
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
if (e.key === "Enter" && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
handleSubmit();
|
||||
}
|
||||
};
|
||||
|
||||
const handleAnimatedTextClick = () => {
|
||||
// Stop animation, clear input, and focus for user to type
|
||||
setIsFocused(true);
|
||||
setIsAnimating(false);
|
||||
clearAnimation();
|
||||
setInputValue("");
|
||||
setDisplayText("");
|
||||
// Focus the textarea
|
||||
setTimeout(() => {
|
||||
textareaRef.current?.focus();
|
||||
}, 0);
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className="w-full max-w-lg">
|
||||
<div
|
||||
className={cn(
|
||||
"rounded-xl bg-muted/50 border px-4 py-3 backdrop-blur-sm transition-all duration-200 shadow-sm",
|
||||
isFocused && "border-foreground/30 ring-1 ring-ring"
|
||||
)}
|
||||
>
|
||||
{/* Textarea area with animated text overlay */}
|
||||
<div className="relative min-h-[44px]">
|
||||
{/* Animated placeholder text - clickable to redirect */}
|
||||
{!isFocused && isAnimating && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleAnimatedTextClick}
|
||||
className="absolute inset-0 flex items-start text-left cursor-pointer hover:opacity-80 transition-opacity"
|
||||
>
|
||||
<span className="text-base text-muted-foreground">
|
||||
{displayText}
|
||||
<span className="inline-block w-0.5 h-5 bg-primary ml-0.5 animate-pulse align-middle" />
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* Actual textarea */}
|
||||
<textarea
|
||||
ref={textareaRef}
|
||||
value={inputValue}
|
||||
onChange={(e) => setInputValue(e.target.value)}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder={isFocused ? t("placeholder") : ""}
|
||||
className={cn(
|
||||
"min-h-[44px] max-h-[100px] w-full resize-none text-base bg-transparent border-0 p-0 focus-visible:ring-0 focus-visible:ring-offset-0 outline-none placeholder:text-muted-foreground",
|
||||
!isFocused && isAnimating && "text-transparent caret-transparent pointer-events-none"
|
||||
)}
|
||||
aria-label={t("ariaLabel")}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Bottom row: Bot icon + model name + submit button */}
|
||||
<div className="flex items-center justify-between mt-2">
|
||||
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<Bot className="h-3 w-3" />
|
||||
<span>{t("modelName")}</span>
|
||||
</div>
|
||||
<Button
|
||||
type="submit"
|
||||
size="icon"
|
||||
disabled={!inputValue.trim()}
|
||||
className="h-7 w-7 rounded-full"
|
||||
>
|
||||
<ArrowUp className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-muted-foreground mt-3 text-center">
|
||||
{t("hint")}
|
||||
</p>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { ArrowUp, Loader2, Sparkles, X, ChevronRight, Bot } from "lucide-react";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
@@ -55,6 +56,7 @@ interface PromptBuilderProps {
|
||||
currentState: PromptBuilderState;
|
||||
onStateChange: (state: PromptBuilderState) => void;
|
||||
modelName?: string;
|
||||
initialPromptRequest?: string;
|
||||
}
|
||||
|
||||
export function PromptBuilder({
|
||||
@@ -63,6 +65,7 @@ export function PromptBuilder({
|
||||
currentState,
|
||||
onStateChange,
|
||||
modelName = "gpt-4o-mini",
|
||||
initialPromptRequest,
|
||||
}: PromptBuilderProps) {
|
||||
const t = useTranslations("promptBuilder");
|
||||
const [isOpen, setIsOpen] = useState(true);
|
||||
@@ -82,13 +85,26 @@ export function PromptBuilder({
|
||||
scrollToBottom();
|
||||
}, [messages, streamingContent]);
|
||||
|
||||
const sendMessage = async () => {
|
||||
if (!input.trim() || isLoading) return;
|
||||
// Auto-send initial prompt request if provided
|
||||
const initialRequestSentRef = useRef(false);
|
||||
useEffect(() => {
|
||||
if (initialPromptRequest && !initialRequestSentRef.current && !isLoading) {
|
||||
initialRequestSentRef.current = true;
|
||||
setInput(initialPromptRequest);
|
||||
// Use setTimeout to ensure the input is set before sending
|
||||
setTimeout(() => {
|
||||
sendMessageWithContent(initialPromptRequest);
|
||||
}, 100);
|
||||
}
|
||||
}, [initialPromptRequest]);
|
||||
|
||||
const sendMessageWithContent = async (content: string) => {
|
||||
if (!content.trim() || isLoading) return;
|
||||
|
||||
const userMessage: Message = {
|
||||
id: crypto.randomUUID(),
|
||||
role: "user",
|
||||
content: input.trim(),
|
||||
content: content.trim(),
|
||||
};
|
||||
|
||||
setMessages((prev) => [...prev, userMessage]);
|
||||
@@ -198,6 +214,11 @@ export function PromptBuilder({
|
||||
}
|
||||
};
|
||||
|
||||
const sendMessage = async () => {
|
||||
if (!input.trim() || isLoading) return;
|
||||
await sendMessageWithContent(input);
|
||||
};
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||||
if (e.key === "Enter" && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
@@ -320,12 +341,7 @@ export function PromptBuilder({
|
||||
{/* Show edit mode actions if there's existing content */}
|
||||
{currentState.content ? (
|
||||
<>
|
||||
{[
|
||||
"Fill missing fields, update tags.",
|
||||
"Make variables better",
|
||||
"Use variables",
|
||||
"Convert to JSON prompt"
|
||||
].map((action, i) => (
|
||||
{[t("editAction1"), t("editAction2"), t("editAction3"), t("editAction4")].map((action, i) => (
|
||||
<button
|
||||
type="button"
|
||||
key={i}
|
||||
@@ -378,7 +394,40 @@ export function PromptBuilder({
|
||||
: "bg-muted"
|
||||
)}
|
||||
>
|
||||
<p className="whitespace-pre-wrap">{message.content}</p>
|
||||
{message.role === "user" ? (
|
||||
<p className="whitespace-pre-wrap">{message.content}</p>
|
||||
) : (
|
||||
<div className="text-sm">
|
||||
<ReactMarkdown
|
||||
components={{
|
||||
p: ({ children }) => <p className="mb-2 last:mb-0">{children}</p>,
|
||||
ul: ({ children }) => <ul className="mb-2 ml-4 list-disc list-inside">{children}</ul>,
|
||||
ol: ({ children }) => <ol className="mb-2 ml-4 list-decimal list-inside">{children}</ol>,
|
||||
li: ({ children }) => <li className="mb-1">{children}</li>,
|
||||
code: ({ className, children, ...props }) => {
|
||||
const isBlock = className?.includes("language-") || String(children).includes("\n");
|
||||
if (isBlock) {
|
||||
return (
|
||||
<pre className="bg-background/80 border rounded-md p-3 my-2 overflow-x-auto">
|
||||
<code className="text-xs">{children}</code>
|
||||
</pre>
|
||||
);
|
||||
}
|
||||
return <code className="bg-background/80 px-1.5 py-0.5 rounded text-xs font-mono" {...props}>{children}</code>;
|
||||
},
|
||||
pre: ({ children }) => <>{children}</>,
|
||||
strong: ({ children }) => <strong className="font-bold">{children}</strong>,
|
||||
em: ({ children }) => <em className="italic">{children}</em>,
|
||||
h1: ({ children }) => <h1 className="text-lg font-bold mb-2 mt-3">{children}</h1>,
|
||||
h2: ({ children }) => <h2 className="text-base font-bold mb-2 mt-3">{children}</h2>,
|
||||
h3: ({ children }) => <h3 className="text-sm font-bold mb-1 mt-2">{children}</h3>,
|
||||
blockquote: ({ children }) => <blockquote className="border-l-2 border-muted-foreground/50 pl-3 my-2 italic">{children}</blockquote>,
|
||||
}}
|
||||
>
|
||||
{message.content}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Tool calls indicator */}
|
||||
|
||||
@@ -234,9 +234,10 @@ interface PromptFormProps {
|
||||
mode?: "create" | "edit";
|
||||
aiGenerationEnabled?: boolean;
|
||||
aiModelName?: string;
|
||||
initialPromptRequest?: string;
|
||||
}
|
||||
|
||||
export function PromptForm({ categories, tags, initialData, initialContributors = [], promptId, mode = "create", aiGenerationEnabled = false, aiModelName }: PromptFormProps) {
|
||||
export function PromptForm({ categories, tags, initialData, initialContributors = [], promptId, mode = "create", aiGenerationEnabled = false, aiModelName, initialPromptRequest }: PromptFormProps) {
|
||||
const router = useRouter();
|
||||
const t = useTranslations("prompts");
|
||||
const tCommon = useTranslations("common");
|
||||
@@ -422,6 +423,7 @@ export function PromptForm({ categories, tags, initialData, initialContributors
|
||||
currentState={currentBuilderState}
|
||||
onStateChange={handleBuilderStateChange}
|
||||
modelName={aiModelName}
|
||||
initialPromptRequest={initialPromptRequest}
|
||||
/>
|
||||
)}
|
||||
<FormField
|
||||
|
||||
@@ -312,7 +312,7 @@ export async function executeToolCall(
|
||||
type: string;
|
||||
structuredFormat: string | null;
|
||||
tags: string[];
|
||||
source: "text" | "semantic";
|
||||
source: "text" | "semantic" | "random";
|
||||
similarity?: string;
|
||||
}> = [];
|
||||
|
||||
@@ -354,7 +354,45 @@ export async function executeToolCall(
|
||||
}
|
||||
|
||||
// Limit final results
|
||||
const finalResults = combinedResults.slice(0, limit);
|
||||
let finalResults = combinedResults.slice(0, limit);
|
||||
|
||||
// If no results found, get random prompts to learn the style
|
||||
if (finalResults.length === 0) {
|
||||
const randomPrompts = await db.prompt.findMany({
|
||||
where: {
|
||||
isPrivate: false,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
description: true,
|
||||
content: true,
|
||||
type: true,
|
||||
structuredFormat: true,
|
||||
tags: {
|
||||
select: {
|
||||
tag: {
|
||||
select: { name: true, color: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
take: limit,
|
||||
orderBy: { createdAt: "desc" }
|
||||
});
|
||||
|
||||
finalResults = randomPrompts.map(p => ({
|
||||
id: p.id,
|
||||
title: p.title,
|
||||
description: p.description,
|
||||
contentPreview: p.content.substring(0, 200) + (p.content.length > 200 ? "..." : ""),
|
||||
type: p.type,
|
||||
structuredFormat: p.structuredFormat,
|
||||
tags: p.tags.map((t: { tag: { name: string } }) => t.tag.name),
|
||||
source: "random" as const
|
||||
}));
|
||||
}
|
||||
|
||||
return {
|
||||
result: {
|
||||
@@ -362,8 +400,13 @@ export async function executeToolCall(
|
||||
data: {
|
||||
prompts: finalResults,
|
||||
count: finalResults.length,
|
||||
searchType: useSemanticSearch ? "hybrid" : "text",
|
||||
filters: { promptType, structuredFormat }
|
||||
searchType: finalResults.length > 0 && finalResults[0].source === "random"
|
||||
? "random_examples"
|
||||
: (useSemanticSearch ? "hybrid" : "text"),
|
||||
filters: { promptType, structuredFormat },
|
||||
note: finalResults.length > 0 && finalResults[0].source === "random"
|
||||
? "No matching prompts found. Showing random examples to understand the prompt style."
|
||||
: undefined
|
||||
}
|
||||
},
|
||||
newState
|
||||
|
||||
@@ -4,14 +4,15 @@ import { LOCALE_COOKIE } from "./config";
|
||||
|
||||
/**
|
||||
* Set the user's locale preference (client-side)
|
||||
* This updates the cookie and reloads the page
|
||||
* This updates the cookie and forces a hard navigation to apply the new locale
|
||||
*/
|
||||
export function setLocale(locale: string): void {
|
||||
// Set cookie with 1 year expiry
|
||||
document.cookie = `${LOCALE_COOKIE}=${locale}; path=/; max-age=${60 * 60 * 24 * 365}; SameSite=Lax`;
|
||||
|
||||
// Reload to apply the new locale
|
||||
window.location.reload();
|
||||
// Force a hard navigation to the same URL to ensure server re-renders with new locale
|
||||
// Using href assignment instead of reload() for more reliable behavior in Next.js
|
||||
window.location.href = window.location.href;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user