feat(messages): add skill field to prompt messages for various languages

This commit is contained in:
Fatih Kadir Akın
2025-12-20 20:54:08 +03:00
parent 6884eeb5a9
commit be102a0d09
27 changed files with 541 additions and 138 deletions
+6 -2
View File
@@ -160,7 +160,8 @@
"video": "فيديو",
"audio": "صوت",
"structured": "منظم",
"document": "مستند"
"document": "مستند",
"skill": "مهارة"
},
"inputType": "أمر المستخدم",
"outputType": "استجابة الذكاء الاصطناعي",
@@ -168,9 +169,11 @@
"inputTypeDescription": "كيف يتم تنسيق الأمر الخاص بك",
"outputTypeDescription": "أظهر للمجتمع كيف يعمل هذا الأمر عند تشغيله على أدوات الذكاء الاصطناعي. يمكنك إنشاء المخرجات بنفسك باستخدام تطبيق الذكاء الاصطناعي المفضل لديك.",
"outputTypeStructuredNote": "الأوامر المنظمة يمكن أن تنتج أنواع مختلفة من المخرجات",
"outputTypeSkillNote": "المهارات توجه الوكلاء لإنشاء الكود. سينتج الذكاء الاصطناعي مخرجات كود بناءً على تعليمات مهارتك.",
"inputTypes": {
"text": "أمر نصي",
"structured": "منظم (JSON/YAML)"
"structured": "منظم (JSON/YAML)",
"skill": "مهارة (مهارات الوكيل)"
},
"outputTypes": {
"text": "نص",
@@ -231,6 +234,7 @@
"run": "تشغيل",
"downloadMarkdown": "تحميل MD",
"downloadYaml": "تحميل YAML",
"downloadSkillMd": "تحميل SKILL.md",
"copyMarkdownUrl": "نسخ رابط MD",
"copyYamlUrl": "نسخ رابط YAML",
"downloadStarted": "بدأ التحميل",
+6 -2
View File
@@ -160,7 +160,8 @@
"video": "Video",
"audio": "Audio",
"structured": "Strukturiert",
"document": "Dokument"
"document": "Dokument",
"skill": "Skill"
},
"inputType": "Benutzer-Prompt",
"outputType": "KI-Antwort",
@@ -168,9 +169,11 @@
"inputTypeDescription": "Wie Ihr Prompt formatiert ist",
"outputTypeDescription": "Zeigen Sie der Community, wie dieser Prompt funktioniert, wenn er mit KI-Tools ausgeführt wird. Sie können die Ausgaben selbst mit Ihrer bevorzugten KI-App generieren.",
"outputTypeStructuredNote": "Strukturierte Prompts können verschiedene Ausgabetypen erzeugen",
"outputTypeSkillNote": "Skills weisen Agenten an, Code zu generieren. Die KI erzeugt Code-Ausgabe basierend auf Ihren Skill-Anweisungen.",
"inputTypes": {
"text": "Text-Prompt",
"structured": "Strukturiert (JSON/YAML)"
"structured": "Strukturiert (JSON/YAML)",
"skill": "Skill (Agent Skills)"
},
"outputTypes": {
"text": "Text",
@@ -217,6 +220,7 @@
"run": "Ausführen",
"downloadMarkdown": "MD herunterladen",
"downloadYaml": "YAML herunterladen",
"downloadSkillMd": "SKILL.md herunterladen",
"copyMarkdownUrl": "MD-Link kopieren",
"copyYamlUrl": "YAML-Link kopieren",
"downloadStarted": "Download gestartet",
+6 -2
View File
@@ -160,7 +160,8 @@
"video": "Βίντεο",
"audio": "Ήχος",
"structured": "Δομημένο",
"document": "Έγγραφο"
"document": "Έγγραφο",
"skill": "Δεξιότητα"
},
"inputType": "Prompt χρήστη",
"outputType": "Απάντηση AI",
@@ -168,9 +169,11 @@
"inputTypeDescription": "Πώς είναι μορφοποιημένο το prompt σας",
"outputTypeDescription": "Δείξτε στην κοινότητα πώς αποδίδει αυτό το prompt όταν εκτελείται σε εργαλεία AI. Μπορείτε να δημιουργήσετε τα αποτελέσματα μόνοι σας χρησιμοποιώντας την αγαπημένη σας εφαρμογή AI.",
"outputTypeStructuredNote": "Τα δομημένα prompts μπορούν να παράγουν διάφορους τύπους εξόδου",
"outputTypeSkillNote": "Οι δεξιότητες καθοδηγούν τους agents να παράγουν κώδικα. Η AI θα παράγει κώδικα με βάση τις οδηγίες δεξιοτήτων σας.",
"inputTypes": {
"text": "Prompt κειμένου",
"structured": "Δομημένο (JSON/YAML)"
"structured": "Δομημένο (JSON/YAML)",
"skill": "Δεξιότητα (Agent Skills)"
},
"outputTypes": {
"text": "Κείμενο",
@@ -217,6 +220,7 @@
"run": "Εκτέλεση",
"downloadMarkdown": "Λήψη MD",
"downloadYaml": "Λήψη YAML",
"downloadSkillMd": "Λήψη SKILL.md",
"copyMarkdownUrl": "Αντιγραφή συνδέσμου MD",
"copyYamlUrl": "Αντιγραφή συνδέσμου YAML",
"downloadStarted": "Η λήψη ξεκίνησε",
+6 -2
View File
@@ -160,7 +160,8 @@
"video": "Video",
"audio": "Audio",
"structured": "Structured",
"document": "Document"
"document": "Document",
"skill": "Skill"
},
"inputType": "User Prompt",
"outputType": "AI Response",
@@ -168,9 +169,11 @@
"inputTypeDescription": "How your prompt is formatted",
"outputTypeDescription": "Show the community how this prompt performs when run on AI tools. You can generate outputs yourself using your favorite AI app.",
"outputTypeStructuredNote": "Structured prompts can produce various output types",
"outputTypeSkillNote": "Skills instruct agents to generate code. The AI will produce code output based on your skill instructions.",
"inputTypes": {
"text": "Text Prompt",
"structured": "Structured (JSON/YAML)"
"structured": "Structured (JSON/YAML)",
"skill": "Skill (Agent Skills)"
},
"outputTypes": {
"text": "Text",
@@ -217,6 +220,7 @@
"run": "Run",
"downloadMarkdown": "Download MD",
"downloadYaml": "Download YAML",
"downloadSkillMd": "Download SKILL.md",
"copyMarkdownUrl": "Copy MD Link",
"copyYamlUrl": "Copy YAML Link",
"downloadStarted": "Download started",
+6 -2
View File
@@ -160,7 +160,8 @@
"video": "Video",
"audio": "Audio",
"structured": "Estructurado",
"document": "Documento"
"document": "Documento",
"skill": "Habilidad"
},
"inputType": "Prompt del Usuario",
"outputType": "Respuesta de IA",
@@ -168,9 +169,11 @@
"inputTypeDescription": "Cómo está formateado tu prompt",
"outputTypeDescription": "Muestra a la comunidad cómo funciona este prompt cuando se ejecuta en herramientas de IA. Puedes generar las salidas tú mismo usando tu aplicación de IA favorita.",
"outputTypeStructuredNote": "Los prompts estructurados pueden producir varios tipos de salida",
"outputTypeSkillNote": "Las habilidades instruyen a los agentes para generar código. La IA producirá código basado en las instrucciones de tu habilidad.",
"inputTypes": {
"text": "Prompt de Texto",
"structured": "Estructurado (JSON/YAML)"
"structured": "Estructurado (JSON/YAML)",
"skill": "Habilidad (Agent Skills)"
},
"outputTypes": {
"text": "Texto",
@@ -231,6 +234,7 @@
"run": "Ejecutar",
"downloadMarkdown": "Descargar MD",
"downloadYaml": "Descargar YAML",
"downloadSkillMd": "Descargar SKILL.md",
"copyMarkdownUrl": "Copiar enlace MD",
"copyYamlUrl": "Copiar enlace YAML",
"downloadStarted": "Descarga iniciada",
+6 -2
View File
@@ -160,7 +160,8 @@
"video": "Vidéo",
"audio": "Audio",
"structured": "Structuré",
"document": "Document"
"document": "Document",
"skill": "Compétence"
},
"inputType": "Prompt Utilisateur",
"outputType": "Réponse IA",
@@ -168,9 +169,11 @@
"inputTypeDescription": "Comment votre prompt est formaté",
"outputTypeDescription": "Montrez à la communauté comment ce prompt fonctionne lorsqu'il est exécuté sur des outils IA. Vous pouvez générer les résultats vous-même en utilisant votre application IA préférée.",
"outputTypeStructuredNote": "Les prompts structurés peuvent produire divers types de sortie",
"outputTypeSkillNote": "Les compétences instruisent les agents à générer du code. L'IA produira du code basé sur vos instructions de compétence.",
"inputTypes": {
"text": "Prompt Texte",
"structured": "Structuré (JSON/YAML)"
"structured": "Structuré (JSON/YAML)",
"skill": "Compétence (Agent Skills)"
},
"outputTypes": {
"text": "Texte",
@@ -217,6 +220,7 @@
"run": "Exécuter",
"downloadMarkdown": "Télécharger MD",
"downloadYaml": "Télécharger YAML",
"downloadSkillMd": "Télécharger SKILL.md",
"copyMarkdownUrl": "Copier le lien MD",
"copyYamlUrl": "Copier le lien YAML",
"downloadStarted": "Téléchargement démarré",
+6 -2
View File
@@ -160,7 +160,8 @@
"video": "וידאו",
"audio": "אודיו",
"structured": "מובנה",
"document": "מסמך"
"document": "מסמך",
"skill": "מיומנות"
},
"inputType": "פרומפט משתמש",
"outputType": "תגובת AI",
@@ -168,9 +169,11 @@
"inputTypeDescription": "איך הפרומפט שלך מעוצב",
"outputTypeDescription": "הראה לקהילה איך הפרומפט הזה מתפקד כשהוא רץ בכלי AI. אתה יכול ליצור את התוצרים בעצמך באמצעות אפליקציית ה-AI המועדפת עליך.",
"outputTypeStructuredNote": "פרומפטים מובנים יכולים לייצר סוגי פלט שונים",
"outputTypeSkillNote": "מיומנויות מנחות סוכנים ליצור קוד. הAI ייצר פלט קוד בהתאם להוראות המיומנות שלך.",
"inputTypes": {
"text": "פרומפט טקסט",
"structured": "מובנה (JSON/YAML)"
"structured": "מובנה (JSON/YAML)",
"skill": "מיומנות (Agent Skills)"
},
"outputTypes": {
"text": "טקסט",
@@ -217,6 +220,7 @@
"run": "הפעל",
"downloadMarkdown": "הורד MD",
"downloadYaml": "הורד YAML",
"downloadSkillMd": "הורד SKILL.md",
"copyMarkdownUrl": "העתק קישור MD",
"copyYamlUrl": "העתק קישור YAML",
"downloadStarted": "ההורדה החלה",
+6 -2
View File
@@ -160,7 +160,8 @@
"video": "Video",
"audio": "Audio",
"structured": "Strutturato",
"document": "Documento"
"document": "Documento",
"skill": "Abilità"
},
"inputType": "Prompt Utente",
"outputType": "Risposta AI",
@@ -168,9 +169,11 @@
"inputTypeDescription": "Come è formattato il tuo prompt",
"outputTypeDescription": "Mostra alla community come funziona questo prompt quando viene eseguito su strumenti AI. Puoi generare gli output tu stesso usando la tua app AI preferita.",
"outputTypeStructuredNote": "I prompt strutturati possono produrre vari tipi di output",
"outputTypeSkillNote": "Le abilità istruiscono gli agenti a generare codice. L'IA produrrà codice basato sulle istruzioni della tua abilità.",
"inputTypes": {
"text": "Prompt Testuale",
"structured": "Strutturato (JSON/YAML)"
"structured": "Strutturato (JSON/YAML)",
"skill": "Abilità (Agent Skills)"
},
"outputTypes": {
"text": "Testo",
@@ -217,6 +220,7 @@
"run": "Esegui",
"downloadMarkdown": "Scarica MD",
"downloadYaml": "Scarica YAML",
"downloadSkillMd": "Scarica SKILL.md",
"copyMarkdownUrl": "Copia link MD",
"copyYamlUrl": "Copia link YAML",
"downloadStarted": "Download avviato",
+6 -2
View File
@@ -160,7 +160,8 @@
"video": "動画",
"audio": "音声",
"structured": "構造化",
"document": "ドキュメント"
"document": "ドキュメント",
"skill": "スキル"
},
"inputType": "ユーザープロンプト",
"outputType": "AI応答",
@@ -168,9 +169,11 @@
"inputTypeDescription": "プロンプトの形式",
"outputTypeDescription": "AIツールでこのプロンプトを実行したときの結果をコミュニティに示しましょう。お気に入りのAIアプリを使って自分で出力を生成できます。",
"outputTypeStructuredNote": "構造化プロンプトは様々な出力タイプを生成できます",
"outputTypeSkillNote": "スキルはエージェントにコードを生成するよう指示します。AIはスキルの指示に基づいてコードを出力します。",
"inputTypes": {
"text": "テキストプロンプト",
"structured": "構造化 (JSON/YAML)"
"structured": "構造化 (JSON/YAML)",
"skill": "スキル (Agent Skills)"
},
"outputTypes": {
"text": "テキスト",
@@ -231,6 +234,7 @@
"run": "実行",
"downloadMarkdown": "MDをダウンロード",
"downloadYaml": "YAMLをダウンロード",
"downloadSkillMd": "SKILL.mdをダウンロード",
"copyMarkdownUrl": "MDリンクをコピー",
"copyYamlUrl": "YAMLリンクをコピー",
"downloadStarted": "ダウンロード開始",
+6 -2
View File
@@ -160,7 +160,8 @@
"video": "비디오",
"audio": "오디오",
"structured": "구조화",
"document": "문서"
"document": "문서",
"skill": "스킬"
},
"inputType": "사용자 프롬프트",
"outputType": "AI 응답",
@@ -168,9 +169,11 @@
"inputTypeDescription": "프롬프트 형식",
"outputTypeDescription": "이 프롬프트가 AI 도구에서 실행될 때 어떻게 작동하는지 커뮤니티에 보여주세요. 좋아하는 AI 앱을 사용하여 직접 출력을 생성할 수 있습니다.",
"outputTypeStructuredNote": "구조화된 프롬프트는 다양한 출력 유형을 생성할 수 있습니다",
"outputTypeSkillNote": "스킬은 에이전트에게 코드를 생성하도록 지시합니다. AI는 스킬 지침에 따라 코드 출력을 생성합니다.",
"inputTypes": {
"text": "텍스트 프롬프트",
"structured": "구조화 (JSON/YAML)"
"structured": "구조화 (JSON/YAML)",
"skill": "스킬 (Agent Skills)"
},
"outputTypes": {
"text": "텍스트",
@@ -217,6 +220,7 @@
"run": "실행",
"downloadMarkdown": "MD 다운로드",
"downloadYaml": "YAML 다운로드",
"downloadSkillMd": "SKILL.md 다운로드",
"copyMarkdownUrl": "MD 링크 복사",
"copyYamlUrl": "YAML 링크 복사",
"downloadStarted": "다운로드 시작",
+6 -2
View File
@@ -160,7 +160,8 @@
"video": "Vídeo",
"audio": "Áudio",
"structured": "Estruturado",
"document": "Documento"
"document": "Documento",
"skill": "Habilidade"
},
"inputType": "Prompt do Usuário",
"outputType": "Resposta da IA",
@@ -168,9 +169,11 @@
"inputTypeDescription": "Como seu prompt está formatado",
"outputTypeDescription": "Mostre à comunidade como este prompt funciona quando executado em ferramentas de IA. Você pode gerar as saídas você mesmo usando seu aplicativo de IA favorito.",
"outputTypeStructuredNote": "Prompts estruturados podem produzir vários tipos de saída",
"outputTypeSkillNote": "Habilidades instruem agentes a gerar código. A IA produzirá código baseado nas instruções da sua habilidade.",
"inputTypes": {
"text": "Prompt de Texto",
"structured": "Estruturado (JSON/YAML)"
"structured": "Estruturado (JSON/YAML)",
"skill": "Habilidade (Agent Skills)"
},
"outputTypes": {
"text": "Texto",
@@ -217,6 +220,7 @@
"run": "Executar",
"downloadMarkdown": "Baixar MD",
"downloadYaml": "Baixar YAML",
"downloadSkillMd": "Baixar SKILL.md",
"copyMarkdownUrl": "Copiar link MD",
"copyYamlUrl": "Copiar link YAML",
"downloadStarted": "Download iniciado",
+6 -2
View File
@@ -160,7 +160,8 @@
"video": "Видео",
"audio": "Аудио",
"structured": "Структурированный",
"document": "Документ"
"document": "Документ",
"skill": "Навык"
},
"inputType": "Промпт пользователя",
"outputType": "Ответ ИИ",
@@ -168,9 +169,11 @@
"inputTypeDescription": "Как отформатирован ваш промпт",
"outputTypeDescription": "Покажите сообществу, как работает этот промпт при запуске в инструментах ИИ. Вы можете сами сгенерировать результаты, используя ваше любимое ИИ-приложение.",
"outputTypeStructuredNote": "Структурированные промпты могут создавать различные типы вывода",
"outputTypeSkillNote": "Навыки инструктируют агентов генерировать код. ИИ будет создавать код на основе ваших инструкций навыка.",
"inputTypes": {
"text": "Текстовый промпт",
"structured": "Структурированный (JSON/YAML)"
"structured": "Структурированный (JSON/YAML)",
"skill": "Навык (Agent Skills)"
},
"outputTypes": {
"text": "Текст",
@@ -217,6 +220,7 @@
"run": "Запустить",
"downloadMarkdown": "Скачать MD",
"downloadYaml": "Скачать YAML",
"downloadSkillMd": "Скачать SKILL.md",
"copyMarkdownUrl": "Копировать ссылку MD",
"copyYamlUrl": "Копировать ссылку YAML",
"downloadStarted": "Загрузка началась",
+6 -2
View File
@@ -160,7 +160,8 @@
"video": "Video",
"audio": "Ses",
"structured": "Yapılandırılmış",
"document": "Doküman"
"document": "Doküman",
"skill": "Yetenek"
},
"inputType": "Kullanıcı Promptu",
"outputType": "AI Yanıtı",
@@ -168,9 +169,11 @@
"inputTypeDescription": "Promptunuzun formatı",
"outputTypeDescription": "Bu prompt AI araçlarında çalıştırıldığında nasıl performans gösterdiğini topluluğa gösterin. Çıktıları kendiniz favori AI uygulamanızı kullanarak oluşturabilirsiniz.",
"outputTypeStructuredNote": "Yapılandırılmış promptlar çeşitli çıktı türleri üretebilir",
"outputTypeSkillNote": "Yetenekler, ajanları kod üretmeye yönlendirir. AI, yetenek talimatlarınıza göre kod çıktısı üretecektir.",
"inputTypes": {
"text": "Metin Promptu",
"structured": "Yapılandırılmış (JSON/YAML)"
"structured": "Yapılandırılmış (JSON/YAML)",
"skill": "Yetenek (Agent Skills)"
},
"outputTypes": {
"text": "Metin",
@@ -217,6 +220,7 @@
"run": "Çalıştır",
"downloadMarkdown": "MD İndir",
"downloadYaml": "YAML İndir",
"downloadSkillMd": "SKILL.md İndir",
"copyMarkdownUrl": "MD Linkini Kopyala",
"copyYamlUrl": "YAML Linkini Kopyala",
"downloadStarted": "İndirme başladı",
+6 -2
View File
@@ -160,7 +160,8 @@
"video": "视频",
"audio": "音频",
"structured": "结构化",
"document": "文档"
"document": "文档",
"skill": "技能"
},
"inputType": "用户提示词",
"outputType": "AI 响应",
@@ -168,9 +169,11 @@
"inputTypeDescription": "您的提示词格式",
"outputTypeDescription": "向社区展示此提示词在AI工具上运行时的表现。您可以使用您喜欢的AI应用自行生成输出。",
"outputTypeStructuredNote": "结构化提示词可以产生多种输出类型",
"outputTypeSkillNote": "技能指导代理生成代码。AI将根据您的技能指令产生代码输出。",
"inputTypes": {
"text": "文本提示词",
"structured": "结构化 (JSON/YAML)"
"structured": "结构化 (JSON/YAML)",
"skill": "技能 (Agent Skills)"
},
"outputTypes": {
"text": "文本",
@@ -217,6 +220,7 @@
"run": "运行",
"downloadMarkdown": "下载 MD",
"downloadYaml": "下载 YAML",
"downloadSkillMd": "下载 SKILL.md",
"copyMarkdownUrl": "复制 MD 链接",
"copyYamlUrl": "复制 YAML 链接",
"downloadStarted": "开始下载",
@@ -0,0 +1,2 @@
-- AlterEnum
ALTER TYPE "PromptType" ADD VALUE 'SKILL';
+1
View File
@@ -343,6 +343,7 @@ enum PromptType {
VIDEO
AUDIO
STRUCTURED
SKILL
}
enum StructuredFormat {
+212 -1
View File
@@ -255,6 +255,16 @@ async function main() {
update: {},
create: { name: "Agent", slug: "agent", color: "#14B8A6" },
}),
prisma.tag.upsert({
where: { slug: "skill" },
update: {},
create: { name: "Skill", slug: "skill", color: "#8B5CF6" },
}),
prisma.tag.upsert({
where: { slug: "copilot" },
update: {},
create: { name: "Copilot", slug: "copilot", color: "#000000" },
}),
]);
console.log("✅ Created", tags.length, "tags");
@@ -510,6 +520,207 @@ variables:
tagSlugs: ["workflow", "claude", "advanced"],
authorId: bob.id
},
// SKILL type prompts (Agent Skills)
{
title: "Code Refactoring Skill",
description: "An agent skill for refactoring code to improve quality and maintainability",
content: `# Code Refactoring Skill
This skill helps refactor code to improve readability, maintainability, and performance.
## Instructions
When asked to refactor code:
1. **Analyze the code structure** - Identify code smells, duplications, and areas for improvement
2. **Apply refactoring patterns** - Use appropriate design patterns and best practices
3. **Preserve functionality** - Ensure all existing behavior is maintained
4. **Add documentation** - Include clear comments explaining complex logic
5. **Optimize performance** - Look for opportunities to improve efficiency
## Guidelines
- Follow the language's style guide and conventions
- Break large functions into smaller, focused ones
- Use meaningful variable and function names
- Remove dead code and unused imports
- Add type annotations where applicable
## Output Format
Provide the refactored code with explanations of changes made.`,
type: "SKILL",
categorySlug: "coding",
tagSlugs: ["skill", "copilot", "advanced"],
authorId: admin.id
},
{
title: "Test Generator Skill",
description: "An agent skill for generating comprehensive unit tests",
content: `# Test Generator Skill
This skill generates comprehensive unit tests for code.
## Instructions
When generating tests:
1. **Identify testable units** - Find functions, methods, and classes to test
2. **Cover edge cases** - Include tests for boundary conditions and error states
3. **Use appropriate assertions** - Match assertions to expected outcomes
4. **Mock dependencies** - Isolate units from external dependencies
5. **Follow naming conventions** - Use descriptive test names
## Test Categories
- **Happy path tests** - Normal expected behavior
- **Edge case tests** - Boundary conditions
- **Error handling tests** - Exception and error scenarios
- **Integration tests** - Component interactions
## Framework Guidelines
- Use the project's existing test framework
- Follow AAA pattern (Arrange, Act, Assert)
- Keep tests independent and isolated
- Aim for high code coverage`,
type: "SKILL",
categorySlug: "coding",
tagSlugs: ["skill", "copilot", "beginner"],
authorId: demo.id
},
{
title: "Documentation Writer Skill",
description: "An agent skill for generating comprehensive documentation",
content: `# Documentation Writer Skill
This skill creates clear, comprehensive documentation for code and APIs.
## Instructions
When writing documentation:
1. **Understand the code** - Analyze functionality and purpose
2. **Write clear descriptions** - Explain what the code does in plain language
3. **Document parameters** - List all inputs with types and descriptions
4. **Include examples** - Provide usage examples for common scenarios
5. **Note edge cases** - Document any limitations or special behaviors
## Documentation Types
### Function/Method Documentation
- Purpose and description
- Parameters with types
- Return values
- Exceptions thrown
- Usage examples
### API Documentation
- Endpoint descriptions
- Request/response formats
- Authentication requirements
- Rate limits and errors
### README Documentation
- Project overview
- Installation instructions
- Quick start guide
- Configuration options`,
type: "SKILL",
categorySlug: "technical-writing",
tagSlugs: ["skill", "copilot", "beginner"],
authorId: alice.id
},
{
title: "Git Commit Message Skill",
description: "An agent skill for writing clear, conventional commit messages",
content: `# Git Commit Message Skill
This skill writes clear, conventional commit messages following best practices.
## Instructions
When writing commit messages:
1. **Use conventional commit format** - type(scope): description
2. **Be concise but descriptive** - Summarize changes clearly
3. **Use imperative mood** - "Add feature" not "Added feature"
4. **Reference issues** - Link to relevant tickets or issues
5. **Separate concerns** - One logical change per commit
## Commit Types
- **feat**: New feature
- **fix**: Bug fix
- **docs**: Documentation changes
- **style**: Code style changes (formatting, etc.)
- **refactor**: Code refactoring
- **test**: Adding or updating tests
- **chore**: Maintenance tasks
## Format
\`\`\`
type(scope): short description
Longer description if needed. Explain what and why,
not how.
Refs: #issue-number
\`\`\``,
type: "SKILL",
categorySlug: "devops",
tagSlugs: ["skill", "copilot", "beginner"],
authorId: bob.id
},
{
title: "Code Review Skill",
description: "An agent skill for performing thorough code reviews",
content: `# Code Review Skill
This skill performs comprehensive code reviews with actionable feedback.
## Instructions
When reviewing code:
1. **Check correctness** - Verify logic and functionality
2. **Review code style** - Ensure consistency with project standards
3. **Identify security issues** - Look for vulnerabilities
4. **Assess performance** - Find potential bottlenecks
5. **Evaluate maintainability** - Consider future maintenance
## Review Checklist
### Functionality
- [ ] Code works as intended
- [ ] Edge cases are handled
- [ ] Error handling is appropriate
### Code Quality
- [ ] Code is readable and well-organized
- [ ] Functions are focused and not too long
- [ ] No code duplication
- [ ] Variable names are descriptive
### Security
- [ ] Input validation is present
- [ ] No hardcoded secrets
- [ ] SQL injection prevention
- [ ] XSS prevention
## Feedback Format
Provide specific, actionable feedback with:
- Line references
- Suggested improvements
- Code examples when helpful`,
type: "SKILL",
categorySlug: "coding",
tagSlugs: ["skill", "copilot", "advanced"],
authorId: charlie.id
},
];
for (const promptData of prompts) {
@@ -526,7 +737,7 @@ variables:
title: promptData.title,
description: promptData.description,
content: promptData.content,
type: promptData.type as "TEXT" | "IMAGE" | "VIDEO" | "AUDIO" | "STRUCTURED",
type: promptData.type as "TEXT" | "IMAGE" | "VIDEO" | "AUDIO" | "STRUCTURED" | "SKILL",
structuredFormat: (promptData as { structuredFormat?: "JSON" | "YAML" }).structuredFormat,
authorId: promptData.authorId,
categoryId: category?.id,
+36 -15
View File
@@ -2,16 +2,23 @@ import { NextRequest, NextResponse } from "next/server";
import { db } from "@/lib/db";
type OutputFormat = "md" | "yml";
type FileType = "prompt" | "skill";
/**
* Extracts the prompt ID and format from a URL parameter
* Extracts the prompt ID, format, and file type from a URL parameter
* Supports formats: "abc123.prompt.md", "abc123_some-slug.prompt.md", "abc123.prompt.yml"
* Also supports: "abc123.SKILL.md", "abc123_some-slug.SKILL.md" (SKILL only has .md format)
*/
function parseIdParam(idParam: string): { id: string; format: OutputFormat } {
function parseIdParam(idParam: string): { id: string; format: OutputFormat; fileType: FileType } {
let param = idParam;
let format: OutputFormat = "md";
let fileType: FileType = "prompt";
if (param.endsWith(".prompt.yml")) {
if (param.endsWith(".SKILL.md")) {
format = "md";
fileType = "skill";
param = param.slice(0, -".SKILL.md".length);
} else if (param.endsWith(".prompt.yml")) {
format = "yml";
param = param.slice(0, -".prompt.yml".length);
} else if (param.endsWith(".prompt.md")) {
@@ -25,7 +32,7 @@ function parseIdParam(idParam: string): { id: string; format: OutputFormat } {
param = param.substring(0, underscoreIndex);
}
return { id: param, format };
return { id: param, format, fileType };
}
/**
@@ -50,11 +57,11 @@ export async function GET(
{ params }: { params: Promise<{ id: string }> }
) {
const { id: idParam } = await params;
const { id, format } = parseIdParam(idParam);
const { id, format, fileType } = parseIdParam(idParam);
const prompt = await db.prompt.findFirst({
where: { id, deletedAt: null, isPrivate: false, isUnlisted: false },
select: { title: true, description: true, content: true },
select: { title: true, description: true, content: true, type: true },
});
if (!prompt) {
@@ -64,8 +71,11 @@ export async function GET(
let output: string;
let contentType: string;
// Use skill format if requested via .SKILL.md URL or if prompt type is SKILL
const isSkill = fileType === "skill" || prompt.type === "SKILL";
if (format === "yml") {
// Format as structured YAML with messages array
// YAML format (only for regular prompts, not skills)
const lines = [
`name: ${prompt.title}`,
];
@@ -83,14 +93,25 @@ export async function GET(
output = lines.join("\n");
contentType = "text/yaml; charset=utf-8";
} else {
// Format as markdown with YAML frontmatter
const frontmatter = [
"---",
`name: ${prompt.title}`,
prompt.description ? `description: ${prompt.description}` : null,
"---",
].filter(Boolean).join("\n");
output = `${frontmatter}\n${prompt.content}`;
if (isSkill) {
// Format as SKILL.md (Agent Skills specification)
const frontmatter = [
"---",
`name: ${prompt.title}`,
prompt.description ? `description: ${prompt.description}` : null,
"---",
].filter(Boolean).join("\n");
output = `${frontmatter}\n\n${prompt.content}`;
} else {
// Format as markdown with YAML frontmatter
const frontmatter = [
"---",
`name: ${prompt.title}`,
prompt.description ? `description: ${prompt.description}` : null,
"---",
].filter(Boolean).join("\n");
output = `${frontmatter}\n${prompt.content}`;
}
contentType = "text/plain; charset=utf-8";
}
+1 -1
View File
@@ -10,7 +10,7 @@ const updatePromptSchema = z.object({
title: z.string().min(1).max(200).optional(),
description: z.string().max(500).optional(),
content: z.string().min(1).optional(),
type: z.enum(["TEXT", "IMAGE", "VIDEO", "AUDIO"]).optional(), // Output type only
type: z.enum(["TEXT", "IMAGE", "VIDEO", "AUDIO", "SKILL"]).optional(), // Output type or SKILL
structuredFormat: z.enum(["JSON", "YAML"]).optional().nullable(),
categoryId: z.string().optional().nullable(),
tagIds: z.array(z.string()).optional(),
+1 -1
View File
@@ -12,7 +12,7 @@ const promptSchema = z.object({
title: z.string().min(1).max(200),
description: z.string().max(500).optional(),
content: z.string().min(1),
type: z.enum(["TEXT", "IMAGE", "VIDEO", "AUDIO"]), // Output type only
type: z.enum(["TEXT", "IMAGE", "VIDEO", "AUDIO", "SKILL"]), // Output type or SKILL
structuredFormat: z.enum(["JSON", "YAML"]).nullish(), // Input type indicator
categoryId: z.string().optional(),
tagIds: z.array(z.string()),
+1 -1
View File
@@ -87,7 +87,7 @@ export default async function EditPromptPage({ params }: EditPromptPageProps) {
title: prompt.title,
description: prompt.description || "",
content: prompt.content,
type: ((prompt.type === "IMAGE" || prompt.type === "VIDEO" || prompt.type === "AUDIO") ? prompt.type : "TEXT") as "TEXT" | "IMAGE" | "VIDEO" | "AUDIO",
type: ((prompt.type === "IMAGE" || prompt.type === "VIDEO" || prompt.type === "AUDIO" || prompt.type === "SKILL") ? prompt.type : "TEXT") as "TEXT" | "IMAGE" | "VIDEO" | "AUDIO" | "SKILL",
structuredFormat: prompt.structuredFormat ? (prompt.structuredFormat as "JSON" | "YAML") : undefined,
categoryId: prompt.categoryId || undefined,
tagIds: prompt.tags.map((t) => t.tagId),
+2
View File
@@ -429,6 +429,7 @@ export default async function PromptPage({ params }: PromptPageProps) {
parentCategoryName={prompt.category?.parent?.name}
promptId={prompt.id}
promptSlug={prompt.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")}
promptType={prompt.type}
/>
) : (
<InteractivePromptContent
@@ -439,6 +440,7 @@ export default async function PromptPage({ params }: PromptPageProps) {
parentCategoryName={prompt.category?.parent?.name}
promptId={prompt.id}
promptSlug={prompt.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")}
promptType={prompt.type}
/>
)}
</div>
@@ -16,21 +16,26 @@ import { toast } from "sonner";
interface DownloadPromptDropdownProps {
promptId: string;
promptSlug?: string;
promptType?: string;
}
export function DownloadPromptDropdown({ promptId, promptSlug }: DownloadPromptDropdownProps) {
export function DownloadPromptDropdown({ promptId, promptSlug, promptType }: DownloadPromptDropdownProps) {
const t = useTranslations("prompts");
const [copiedFormat, setCopiedFormat] = useState<"md" | "yml" | null>(null);
const isSkill = promptType === "SKILL";
const getFileName = (format: "md" | "yml") => {
const base = promptSlug ? `${promptId}_${promptSlug}` : promptId;
return `${base}.prompt.${format}`;
const filePrefix = isSkill ? "SKILL" : "prompt";
return `${base}.${filePrefix}.${format}`;
};
const getFileUrl = (format: "md" | "yml") => {
if (typeof window === "undefined") return "";
const base = promptSlug ? `${promptId}_${promptSlug}` : promptId;
return `${window.location.origin}/prompts/${base}.prompt.${format}`;
const filePrefix = isSkill ? "SKILL" : "prompt";
return `${window.location.origin}/prompts/${base}.${filePrefix}.${format}`;
};
const handleDownload = async (format: "md" | "yml") => {
@@ -68,6 +73,16 @@ export function DownloadPromptDropdown({ promptId, promptSlug }: DownloadPromptD
}
};
// For SKILL type, show a simple button instead of dropdown
if (isSkill) {
return (
<Button variant="ghost" size="sm" onClick={() => handleDownload("md")}>
<Download className="h-4 w-4 mr-1" />
{t("downloadSkillMd")}
</Button>
);
}
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
@@ -11,6 +11,7 @@ import { RunPromptButton } from "./run-prompt-button";
import { TranslateButton } from "./translate-button";
import { DownloadPromptDropdown } from "./download-prompt-dropdown";
import { CodeView } from "@/components/ui/code-view";
import { CodeEditor } from "@/components/ui/code-editor";
import { prettifyJson } from "@/lib/format";
interface Variable {
@@ -30,6 +31,7 @@ interface InteractivePromptContentProps {
parentCategoryName?: string;
promptId?: string;
promptSlug?: string;
promptType?: string;
}
// Parse ${variablename:defaultvalue} or ${variablename} patterns
@@ -149,7 +151,8 @@ export function InteractivePromptContent({
categoryName,
parentCategoryName,
promptId,
promptSlug
promptSlug,
promptType
}: InteractivePromptContentProps) {
const t = useTranslations("common");
const [copied, setCopied] = useState(false);
@@ -255,8 +258,46 @@ export function InteractivePromptContent({
setTranslatedContent(translated);
}, []);
// Check if this is a SKILL type prompt
const isSkill = promptType === "SKILL";
// If no variables, render simple content
if (variables.length === 0) {
if (isSkill) {
// SKILL type: render with Monaco editor (read-only markdown)
return (
<div className={className}>
<div className="flex items-center justify-between mb-3">
<div className="flex items-center gap-1">
{title && <h3 className="text-base font-semibold">{title}</h3>}
<TranslateButton
content={content}
onTranslate={handleTranslate}
isLoggedIn={isLoggedIn}
/>
</div>
<div className="flex items-center gap-2">
{promptId && <DownloadPromptDropdown promptId={promptId} promptSlug={promptSlug} promptType={promptType} />}
<Button variant="ghost" size="sm" onClick={copyToClipboard}>
{copied ? (
<Check className="h-4 w-4 text-green-500" />
) : (
<Copy className="h-4 w-4" />
)}
</Button>
</div>
</div>
<CodeEditor
value={displayedContent}
onChange={() => {}}
language="markdown"
minHeight="400px"
className="text-sm"
readOnly={true}
/>
</div>
);
}
if (isStructured) {
return (
<div className={className}>
@@ -270,7 +311,7 @@ export function InteractivePromptContent({
/>
</div>
<div className="flex items-center gap-2">
{promptId && <DownloadPromptDropdown promptId={promptId} promptSlug={promptSlug} />}
{promptId && <DownloadPromptDropdown promptId={promptId} promptSlug={promptSlug} promptType={promptType} />}
<RunPromptButton
content={displayContent}
unfilledVariables={unfilledVariables}
@@ -308,7 +349,7 @@ export function InteractivePromptContent({
/>
</div>
<div className="flex items-center gap-2">
{promptId && <DownloadPromptDropdown promptId={promptId} promptSlug={promptSlug} />}
{promptId && <DownloadPromptDropdown promptId={promptId} promptSlug={promptSlug} promptType={promptType} />}
<RunPromptButton
content={displayedContent}
unfilledVariables={unfilledVariables}
@@ -354,7 +395,7 @@ export function InteractivePromptContent({
{t("reset")}
</Button>
)}
{promptId && <DownloadPromptDropdown promptId={promptId} promptSlug={promptSlug} />}
{promptId && <DownloadPromptDropdown promptId={promptId} promptSlug={promptSlug} promptType={promptType} />}
<RunPromptButton
content={getFinalContent()}
unfilledVariables={unfilledVariables}
@@ -458,7 +499,7 @@ export function InteractivePromptContent({
{t("reset")}
</Button>
)}
{promptId && <DownloadPromptDropdown promptId={promptId} promptSlug={promptSlug} />}
{promptId && <DownloadPromptDropdown promptId={promptId} promptSlug={promptSlug} promptType={promptType} />}
<RunPromptButton
content={getFinalContent()}
unfilledVariables={unfilledVariables}
+1 -1
View File
@@ -14,7 +14,7 @@ interface PromptBuilderState {
title: string;
description: string;
content: string;
type: "TEXT" | "IMAGE" | "VIDEO" | "AUDIO"; // Output type only
type: "TEXT" | "IMAGE" | "VIDEO" | "AUDIO" | "SKILL"; // Output type or SKILL
structuredFormat?: "JSON" | "YAML"; // Input type indicator
categoryId?: string;
tagIds: string[];
+128 -79
View File
@@ -198,7 +198,7 @@ const createPromptSchema = (t: (key: string) => string) => z.object({
title: z.string().min(1, t("titleRequired")).max(200),
description: z.string().max(500).optional(),
content: z.string().min(1, t("contentRequired")),
type: z.enum(["TEXT", "IMAGE", "VIDEO", "AUDIO"]), // Output type only
type: z.enum(["TEXT", "IMAGE", "VIDEO", "AUDIO", "SKILL"]), // Output type or SKILL
structuredFormat: z.enum(["JSON", "YAML"]).optional(),
categoryId: z.string().optional(),
tagIds: z.array(z.string()),
@@ -301,7 +301,7 @@ export function PromptForm({ categories, tags, initialData, initialContributors
title: string;
description: string;
content: string;
type: "TEXT" | "IMAGE" | "VIDEO" | "AUDIO";
type: "TEXT" | "IMAGE" | "VIDEO" | "AUDIO" | "SKILL";
structuredFormat?: "JSON" | "YAML";
categoryId?: string;
tagIds: string[];
@@ -721,12 +721,17 @@ export function PromptForm({ categories, tags, initialData, initialContributors
<div className="flex flex-col sm:flex-row sm:items-center gap-3">
<div className="flex items-center gap-3">
<Select
value={isStructuredInput ? "STRUCTURED" : "TEXT"}
value={promptType === "SKILL" ? "SKILL" : (isStructuredInput ? "STRUCTURED" : "TEXT")}
onValueChange={(v) => {
if (v === "STRUCTURED") {
form.setValue("structuredFormat", "JSON");
form.setValue("type", "TEXT");
} else if (v === "SKILL") {
form.setValue("structuredFormat", undefined);
form.setValue("type", "SKILL");
} else {
form.setValue("structuredFormat", undefined);
form.setValue("type", "TEXT");
}
}}
>
@@ -736,6 +741,7 @@ export function PromptForm({ categories, tags, initialData, initialContributors
<SelectContent>
<SelectItem value="TEXT">{t("inputTypes.text")}</SelectItem>
<SelectItem value="STRUCTURED">{t("inputTypes.structured")}</SelectItem>
<SelectItem value="SKILL">{t("inputTypes.skill")}</SelectItem>
</SelectContent>
</Select>
{isStructuredInput && (
@@ -810,7 +816,20 @@ export function PromptForm({ categories, tags, initialData, initialContributors
render={({ field }) => (
<FormItem>
<FormControl>
{isStructuredInput ? (
{promptType === "SKILL" ? (
<div className="rounded-md border overflow-hidden">
<VariableToolbar onInsert={insertVariable} getSelectedText={getSelectedText} />
<CodeEditor
ref={codeEditorRef}
value={field.value}
onChange={field.onChange}
language="markdown"
placeholder={`# My Skill\n\nDescribe what this skill does and how the agent should use it.\n\n## Instructions\n\n- Step 1: ...\n- Step 2: ...`}
minHeight="400px"
className="border-0 rounded-none"
/>
</div>
) : isStructuredInput ? (
<div className="rounded-md border overflow-hidden">
<VariableToolbar onInsert={insertVariable} getSelectedText={getSelectedText} />
<CodeEditor
@@ -871,87 +890,117 @@ export function PromptForm({ categories, tags, initialData, initialContributors
</div>
{/* ===== OUTPUT SECTION ===== */}
<div className="space-y-4 py-6 border-t">
<div className="space-y-1">
<h2 className="text-base font-semibold">{t("outputType")}</h2>
<p className="text-sm text-muted-foreground">{t("outputTypeDescription")}</p>
</div>
{/* Output Type selector as grouped buttons */}
<div className="grid grid-cols-2 sm:inline-flex rounded-md border sm:divide-x">
{(["TEXT", "IMAGE", "VIDEO", "AUDIO"] as const).map((type, index) => (
<button
key={type}
type="button"
onClick={() => form.setValue("type", type)}
className={`px-4 py-2 text-sm font-medium transition-colors flex items-center justify-center gap-2 ${
index === 0 ? "rounded-tl-md sm:rounded-l-md sm:rounded-tr-none" : ""
} ${
index === 1 ? "rounded-tr-md sm:rounded-none border-l sm:border-l-0" : ""
} ${
index === 2 ? "rounded-bl-md sm:rounded-none border-t sm:border-t-0" : ""
} ${
index === 3 ? "rounded-br-md sm:rounded-r-md sm:rounded-bl-none border-t border-l sm:border-t-0 sm:border-l-0" : ""
} ${
promptType === type
? "bg-primary text-primary-foreground"
: "bg-background hover:bg-muted"
}`}
>
{type === "TEXT" && <span className="text-xs">Aa</span>}
{type === "IMAGE" && <ImageIcon className="h-4 w-4" />}
{type === "VIDEO" && <Video className="h-4 w-4" />}
{type === "AUDIO" && <Volume2 className="h-4 w-4" />}
{t(`outputTypes.${type.toLowerCase()}`)}
</button>
))}
{promptType === "SKILL" ? (
/* SKILL type shows a code output preview - code generated BY the skill */
<div className="space-y-4 py-6 border-t">
<div className="space-y-1">
<h2 className="text-base font-semibold">{t("outputType")}</h2>
<p className="text-sm text-muted-foreground">{t("outputTypeSkillNote")}</p>
</div>
{/* Code output preview - what the agent generates */}
<div className="rounded-lg border bg-[#1e1e1e] overflow-hidden">
{/* Editor title bar */}
<div className="flex items-center gap-2 px-3 py-2 bg-[#2d2d2d] border-b border-[#3d3d3d]">
<div className="flex gap-1.5">
<div className="w-3 h-3 rounded-full bg-[#ff5f57]" />
<div className="w-3 h-3 rounded-full bg-[#febc2e]" />
<div className="w-3 h-3 rounded-full bg-[#28c840]" />
</div>
<span className="text-xs text-[#808080] ml-2 font-mono">generated-code.ts</span>
</div>
{/* Code output content */}
<div className="p-4 text-xs space-y-1" style={{ fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace' }}>
<div><span className="text-[#6a9955]">// Code generated by skill...</span></div>
<div><span className="text-[#c586c0]">export</span> <span className="text-[#569cd6]">function</span> <span className="text-[#dcdcaa]">handler</span><span className="text-[#d4d4d4]">()</span> <span className="text-[#d4d4d4]">{'{'}</span></div>
<div><span className="text-[#d4d4d4]"> </span><span className="text-[#c586c0]">return</span> <span className="text-[#ce9178]">&quot;...&quot;</span><span className="text-[#d4d4d4]">;</span></div>
<div><span className="text-[#d4d4d4]">{'}'}</span></div>
</div>
</div>
</div>
) : (
<div className="space-y-4 py-6 border-t">
<div className="space-y-1">
<h2 className="text-base font-semibold">{t("outputType")}</h2>
<p className="text-sm text-muted-foreground">{t("outputTypeDescription")}</p>
</div>
{/* Output Type selector as grouped buttons */}
<div className="grid grid-cols-2 sm:inline-flex rounded-md border sm:divide-x">
{(["TEXT", "IMAGE", "VIDEO", "AUDIO"] as const).map((type, index) => (
<button
key={type}
type="button"
onClick={() => form.setValue("type", type)}
className={`px-4 py-2 text-sm font-medium transition-colors flex items-center justify-center gap-2 ${
index === 0 ? "rounded-tl-md sm:rounded-l-md sm:rounded-tr-none" : ""
} ${
index === 1 ? "rounded-tr-md sm:rounded-none border-l sm:border-l-0" : ""
} ${
index === 2 ? "rounded-bl-md sm:rounded-none border-t sm:border-t-0" : ""
} ${
index === 3 ? "rounded-br-md sm:rounded-r-md sm:rounded-bl-none border-t border-l sm:border-t-0 sm:border-l-0" : ""
} ${
promptType === type
? "bg-primary text-primary-foreground"
: "bg-background hover:bg-muted"
}`}
>
{type === "TEXT" && <span className="text-xs">Aa</span>}
{type === "IMAGE" && <ImageIcon className="h-4 w-4" />}
{type === "VIDEO" && <Video className="h-4 w-4" />}
{type === "AUDIO" && <Volume2 className="h-4 w-4" />}
{t(`outputTypes.${type.toLowerCase()}`)}
</button>
))}
</div>
{/* Output Preview based on type */}
<div className="rounded-lg border bg-muted/20 p-4">
{promptType === "TEXT" && (
<div className="text-muted-foreground/50 italic text-sm">
{t("outputPreview.text")}
</div>
)}
{promptType === "IMAGE" && (
<div className="space-y-3">
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<ImageIcon className="h-4 w-4" />
<span>{t("outputPreview.imageUpload")}</span>
{/* Output Preview based on type */}
<div className="rounded-lg border bg-muted/20 p-4">
{promptType === "TEXT" && (
<div className="text-muted-foreground/50 italic text-sm">
{t("outputPreview.text")}
</div>
<MediaField form={form} t={t} />
</div>
)}
{promptType === "VIDEO" && (
<div className="space-y-3">
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<Video className="h-4 w-4" />
<span>{t("outputPreview.videoUpload")}</span>
</div>
<MediaField form={form} t={t} />
</div>
)}
{promptType === "AUDIO" && (
<div className="space-y-2">
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<Volume2 className="h-4 w-4" />
<span>{t("outputPreview.audio")}</span>
</div>
{/* Fake audio player bar */}
<div className="flex items-center gap-3 p-3 rounded-md bg-muted/50">
<button type="button" className="h-8 w-8 rounded-full bg-muted flex items-center justify-center cursor-not-allowed" disabled>
<Play className="h-4 w-4 text-muted-foreground/50" />
</button>
<div className="flex-1 h-1.5 bg-muted-foreground/20 rounded-full">
<div className="h-full w-0 bg-muted-foreground/30 rounded-full" />
)}
{promptType === "IMAGE" && (
<div className="space-y-3">
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<ImageIcon className="h-4 w-4" />
<span>{t("outputPreview.imageUpload")}</span>
</div>
<span className="text-xs text-muted-foreground/50">0:00</span>
<MediaField form={form} t={t} />
</div>
</div>
)}
)}
{promptType === "VIDEO" && (
<div className="space-y-3">
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<Video className="h-4 w-4" />
<span>{t("outputPreview.videoUpload")}</span>
</div>
<MediaField form={form} t={t} />
</div>
)}
{promptType === "AUDIO" && (
<div className="space-y-2">
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<Volume2 className="h-4 w-4" />
<span>{t("outputPreview.audio")}</span>
</div>
{/* Fake audio player bar */}
<div className="flex items-center gap-3 p-3 rounded-md bg-muted/50">
<button type="button" className="h-8 w-8 rounded-full bg-muted flex items-center justify-center cursor-not-allowed" disabled>
<Play className="h-4 w-4 text-muted-foreground/50" />
</button>
<div className="flex-1 h-1.5 bg-muted-foreground/20 rounded-full">
<div className="h-full w-0 bg-muted-foreground/30 rounded-full" />
</div>
<span className="text-xs text-muted-foreground/50">0:00</span>
</div>
</div>
)}
</div>
</div>
</div>
)}
<div className="flex justify-end gap-4 pt-2">
<Button type="button" variant="outline" onClick={() => router.back()}>
+8 -3
View File
@@ -12,11 +12,12 @@ export interface CodeEditorHandle {
interface CodeEditorProps {
value: string;
onChange: (value: string) => void;
language: "json" | "yaml";
language: "json" | "yaml" | "markdown";
placeholder?: string;
className?: string;
minHeight?: string;
debounceMs?: number;
readOnly?: boolean;
}
const CodeEditorInner = forwardRef<CodeEditorHandle, CodeEditorProps>(function CodeEditorInner({
@@ -27,6 +28,7 @@ const CodeEditorInner = forwardRef<CodeEditorHandle, CodeEditorProps>(function C
className,
minHeight = "300px",
debounceMs = 0,
readOnly = false,
}, ref) {
const { resolvedTheme } = useTheme();
const editorRef = useRef<Parameters<OnMount>[0] | null>(null);
@@ -120,9 +122,11 @@ const CodeEditorInner = forwardRef<CodeEditorHandle, CodeEditorProps>(function C
automaticLayout: true,
tabSize: 2,
padding: { top: 8, bottom: 8 },
renderLineHighlight: "none",
renderLineHighlight: readOnly ? "none" : "line",
overviewRulerBorder: false,
hideCursorInOverviewRuler: true,
readOnly: readOnly,
domReadOnly: readOnly,
scrollbar: {
vertical: "auto",
horizontal: "auto",
@@ -144,6 +148,7 @@ export const CodeEditor = memo(CodeEditorInner, (prevProps, nextProps) => {
prevProps.placeholder === nextProps.placeholder &&
prevProps.className === nextProps.className &&
prevProps.minHeight === nextProps.minHeight &&
prevProps.debounceMs === nextProps.debounceMs
prevProps.debounceMs === nextProps.debounceMs &&
prevProps.readOnly === nextProps.readOnly
);
});