fix(i18n): Fix syntax errors and add missing translation keys - Restored broken useStore destructuring in PromptEnhancer, UXDesignerPrompt, ActionPlanGenerator, SlidesGenerator - Fixed GoogleAdsGenerator scope issue with renderMagicWandSectionContent - Added missing inputLabel keys to promptEnhancer for all languages - Fixed t.resultTitle to t.enhancedTitle in PromptEnhancer

This commit is contained in:
Gemini AI
2025-12-28 02:00:42 +04:00
Unverified
parent b859d77307
commit 238a576cb8
6 changed files with 1836 additions and 1794 deletions

View File

@@ -11,7 +11,16 @@ import { cn } from "@/lib/utils";
import { translations } from "@/lib/i18n/translations";
export default function ActionPlanGenerator() {
const {
language,
currentPrompt,
actionPlan,
selectedProvider,
selectedModels,
availableModels,
apiKeys,
isProcessing,
error,
setCurrentPrompt,
setSelectedProvider,
setActionPlan,
@@ -21,15 +30,15 @@ export default function ActionPlanGenerator() {
setSelectedModel,
} = useStore();
const t = translations[language].actionPlan;
const common = translations[language].common;
const t = translations[language].actionPlan;
const common = translations[language].common;
const [copied, setCopied] = useState(false);
const [copied, setCopied] = useState(false);
const selectedModel = selectedModels[selectedProvider];
const models = availableModels[selectedProvider] || modelAdapter.getAvailableModels(selectedProvider);
const selectedModel = selectedModels[selectedProvider];
const models = availableModels[selectedProvider] || modelAdapter.getAvailableModels(selectedProvider);
useEffect(() => {
useEffect(() => {
if (typeof window !== "undefined") {
loadAvailableModels();
const saved = localStorage.getItem("promptarch-api-keys");
@@ -44,9 +53,9 @@ useEffect(() => {
}
}
}
}, [selectedProvider]);
}, [selectedProvider]);
const loadAvailableModels = async () => {
const loadAvailableModels = async () => {
const fallbackModels = modelAdapter.getAvailableModels(selectedProvider);
setAvailableModels(selectedProvider, fallbackModels);
@@ -58,9 +67,9 @@ const loadAvailableModels = async () => {
} catch (error) {
console.error("Failed to load models:", error);
}
};
};
const handleGenerate = async () => {
const handleGenerate = async () => {
if (!currentPrompt.trim()) {
setError("Please enter PRD or project requirements");
return;
@@ -111,17 +120,17 @@ const handleGenerate = async () => {
} finally {
setProcessing(false);
}
};
};
const handleCopy = async () => {
const handleCopy = async () => {
if (actionPlan?.rawContent) {
await navigator.clipboard.writeText(actionPlan.rawContent);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}
};
};
return (
return (
<div className="mx-auto grid max-w-7xl gap-4 lg:gap-6 grid-cols-1 lg:grid-cols-2 text-start">
<Card className="h-fit">
<CardHeader className="p-4 lg:p-6 text-start">
@@ -257,5 +266,5 @@ return (
</CardContent>
</Card>
</div>
);
);
}

View File

@@ -394,10 +394,9 @@ export default function GoogleAdsGenerator() {
default:
return <pre className="whitespace-pre-wrap text-xs">{googleAdsResult.rawContent}</pre>;
}
}
};
};
const renderMagicWandSectionContent = (sectionId: string) => {
const renderMagicWandSectionContent = (sectionId: string) => {
if (!magicWandResult) return null;
switch (sectionId) {
@@ -546,10 +545,10 @@ const renderMagicWandSectionContent = (sectionId: string) => {
default:
return <pre className="whitespace-pre-wrap text-xs">{magicWandResult.rawContent}</pre>;
}
};
};
return (
return (
<div className="mx-auto grid max-w-7xl gap-4 lg:gap-6 grid-cols-1 lg:grid-cols-2">
<Card className="h-fit">
<CardHeader className="p-4 lg:p-6 text-start">
@@ -797,5 +796,5 @@ return (
</CardContent>
</Card>
</div>
);
);
}

View File

@@ -11,7 +11,16 @@ import { cn } from "@/lib/utils";
import { translations } from "@/lib/i18n/translations";
export default function PromptEnhancer() {
const {
language,
currentPrompt,
enhancedPrompt,
selectedProvider,
selectedModels,
availableModels,
apiKeys,
isProcessing,
error,
setSelectedProvider,
setCurrentPrompt,
setEnhancedPrompt,
@@ -21,15 +30,15 @@ export default function PromptEnhancer() {
setSelectedModel,
} = useStore();
const t = translations[language].promptEnhancer;
const common = translations[language].common;
const t = translations[language].promptEnhancer;
const common = translations[language].common;
const [copied, setCopied] = useState(false);
const [copied, setCopied] = useState(false);
const selectedModel = selectedModels[selectedProvider];
const models = availableModels[selectedProvider] || modelAdapter.getAvailableModels(selectedProvider);
const selectedModel = selectedModels[selectedProvider];
const models = availableModels[selectedProvider] || modelAdapter.getAvailableModels(selectedProvider);
useEffect(() => {
useEffect(() => {
if (typeof window !== "undefined") {
loadAvailableModels();
const saved = localStorage.getItem("promptarch-api-keys");
@@ -44,9 +53,9 @@ useEffect(() => {
}
}
}
}, [selectedProvider]);
}, [selectedProvider]);
const loadAvailableModels = async () => {
const loadAvailableModels = async () => {
const fallbackModels = modelAdapter.getAvailableModels(selectedProvider);
setAvailableModels(selectedProvider, fallbackModels);
@@ -58,9 +67,9 @@ const loadAvailableModels = async () => {
} catch (error) {
console.error("Failed to load models:", error);
}
};
};
const handleEnhance = async () => {
const handleEnhance = async () => {
if (!currentPrompt.trim()) {
setError("Please enter a prompt to enhance");
return;
@@ -96,23 +105,23 @@ const handleEnhance = async () => {
} finally {
setProcessing(false);
}
};
};
const handleCopy = async () => {
const handleCopy = async () => {
if (enhancedPrompt) {
await navigator.clipboard.writeText(enhancedPrompt);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}
};
};
const handleClear = () => {
const handleClear = () => {
setCurrentPrompt("");
setEnhancedPrompt(null);
setError(null);
};
};
return (
return (
<div className="mx-auto grid max-w-7xl gap-4 lg:gap-6 grid-cols-1 lg:grid-cols-2 text-start">
<Card className="h-fit">
<CardHeader className="p-4 lg:p-6 text-start">
@@ -209,7 +218,7 @@ return (
<CardTitle className="flex items-center justify-between text-base lg:text-lg">
<span className="flex items-center gap-2">
<CheckCircle2 className="h-4 w-4 lg:h-5 lg:w-5 text-green-500" />
{t.resultTitle}
{t.enhancedTitle}
</span>
{enhancedPrompt && (
<Button variant="ghost" size="icon" onClick={handleCopy} className="h-8 w-8 lg:h-9 lg:w-9">
@@ -238,5 +247,5 @@ return (
</CardContent>
</Card>
</div>
);
);
}

View File

@@ -108,36 +108,49 @@ const ACCEPTED_FILE_TYPES = {
const ALL_ACCEPTED = Object.values(ACCEPTED_FILE_TYPES).flat().join(",");
export default function SlidesGenerator() {
const {
selectedProvider,
selectedModels,
availableModels,
apiKeys,
isProcessing,
error,
slidesPresentation,
setSelectedProvider,
setSlidesPresentation,
setProcessing,
setError,
setAvailableModels,
setSelectedModel,
language: uiLanguage,
} = useStore();
const t = translations[uiLanguage].slidesGen;
const common = translations[uiLanguage].common;
const t = translations[uiLanguage].slidesGen;
const common = translations[uiLanguage].common;
const [topic, setTopic] = useState("");
const [language, setLanguage] = useState("en");
const [theme, setTheme] = useState("executive-dark");
const [audience, setAudience] = useState("executives");
const [organization, setOrganization] = useState("");
const [slideCount, setSlideCount] = useState(10);
const [animationStyle, setAnimationStyle] = useState("professional");
const [copied, setCopied] = useState(false);
const [currentSlide, setCurrentSlide] = useState(0);
const [isFullscreen, setIsFullscreen] = useState(false);
const [isAutoPlaying, setIsAutoPlaying] = useState(false);
const [showAdvanced, setShowAdvanced] = useState(false);
const [attachedFiles, setAttachedFiles] = useState<AttachedFile[]>([]);
const [isDragOver, setIsDragOver] = useState(false);
const [uploadProgress, setUploadProgress] = useState<string | null>(null);
const slideContainerRef = useRef<HTMLDivElement>(null);
const autoPlayRef = useRef<NodeJS.Timeout | null>(null);
const fileInputRef = useRef<HTMLInputElement>(null);
const [topic, setTopic] = useState("");
const [language, setLanguage] = useState("en");
const [theme, setTheme] = useState("executive-dark");
const [audience, setAudience] = useState("executives");
const [organization, setOrganization] = useState("");
const [slideCount, setSlideCount] = useState(10);
const [animationStyle, setAnimationStyle] = useState("professional");
const [copied, setCopied] = useState(false);
const [currentSlide, setCurrentSlide] = useState(0);
const [isFullscreen, setIsFullscreen] = useState(false);
const [isAutoPlaying, setIsAutoPlaying] = useState(false);
const [showAdvanced, setShowAdvanced] = useState(false);
const [attachedFiles, setAttachedFiles] = useState<AttachedFile[]>([]);
const [isDragOver, setIsDragOver] = useState(false);
const [uploadProgress, setUploadProgress] = useState<string | null>(null);
const slideContainerRef = useRef<HTMLDivElement>(null);
const autoPlayRef = useRef<NodeJS.Timeout | null>(null);
const fileInputRef = useRef<HTMLInputElement>(null);
const selectedModel = selectedModels[selectedProvider];
const models = availableModels[selectedProvider] || modelAdapter.getAvailableModels(selectedProvider);
const selectedModel = selectedModels[selectedProvider];
const models = availableModels[selectedProvider] || modelAdapter.getAvailableModels(selectedProvider);
useEffect(() => {
useEffect(() => {
if (typeof window !== "undefined") {
loadAvailableModels();
const saved = localStorage.getItem("promptarch-api-keys");
@@ -152,9 +165,9 @@ useEffect(() => {
}
}
}
}, [selectedProvider]);
}, [selectedProvider]);
useEffect(() => {
useEffect(() => {
if (isAutoPlaying && slidesPresentation?.slides) {
autoPlayRef.current = setInterval(() => {
setCurrentSlide((prev) =>
@@ -167,9 +180,9 @@ useEffect(() => {
clearInterval(autoPlayRef.current);
}
};
}, [isAutoPlaying, slidesPresentation?.slides?.length]);
}, [isAutoPlaying, slidesPresentation?.slides?.length]);
const loadAvailableModels = async () => {
const loadAvailableModels = async () => {
const fallbackModels = modelAdapter.getAvailableModels(selectedProvider);
setAvailableModels(selectedProvider, fallbackModels);
@@ -181,10 +194,10 @@ const loadAvailableModels = async () => {
} catch (error) {
console.error("Failed to load models:", error);
}
};
};
// Extract colors from image
const extractColorsFromImage = (file: File): Promise<string[]> => {
// Extract colors from image
const extractColorsFromImage = (file: File): Promise<string[]> => {
return new Promise((resolve) => {
const img = document.createElement("img");
const canvas = document.createElement("canvas");
@@ -221,10 +234,10 @@ const extractColorsFromImage = (file: File): Promise<string[]> => {
img.src = URL.createObjectURL(file);
});
};
};
// Process uploaded file
const processFile = async (file: File): Promise<AttachedFile | null> => {
// Process uploaded file
const processFile = async (file: File): Promise<AttachedFile | null> => {
const id = Math.random().toString(36).substr(2, 9);
const ext = file.name.split(".").pop()?.toLowerCase() || "";
@@ -276,17 +289,17 @@ const processFile = async (file: File): Promise<AttachedFile | null> => {
console.error("Error processing file:", err);
return attachedFile;
}
};
};
const handleFileDrop = useCallback(async (e: React.DragEvent) => {
const handleFileDrop = useCallback(async (e: React.DragEvent) => {
e.preventDefault();
setIsDragOver(false);
const files = Array.from(e.dataTransfer.files);
await handleFileUpload(files);
}, []);
}, []);
const handleFileUpload = async (files: File[]) => {
const handleFileUpload = async (files: File[]) => {
setUploadProgress("Processing files...");
const newFiles: AttachedFile[] = [];
@@ -300,9 +313,9 @@ const handleFileUpload = async (files: File[]) => {
setAttachedFiles(prev => [...prev, ...newFiles]);
setUploadProgress(null);
};
};
const removeFile = (id: string) => {
const removeFile = (id: string) => {
setAttachedFiles(prev => {
const file = prev.find(f => f.id === id);
if (file?.preview) {
@@ -310,16 +323,16 @@ const removeFile = (id: string) => {
}
return prev.filter(f => f.id !== id);
});
};
};
const getFileIcon = (type: string, name: string) => {
const getFileIcon = (type: string, name: string) => {
if (name.match(/\.(png|jpg|jpeg|svg|webp|gif)$/i)) return <ImageIcon className="h-4 w-4" />;
if (name.match(/\.(pdf|doc|docx|txt|md)$/i)) return <FileText className="h-4 w-4" />;
if (name.match(/\.(pptx|ppt|key)$/i)) return <Presentation className="h-4 w-4" />;
return <File className="h-4 w-4" />;
};
};
const buildFileContext = (): string => {
const buildFileContext = (): string => {
if (attachedFiles.length === 0) return "";
let context = "\n\n## ATTACHED FILES CONTEXT:\n";
@@ -342,9 +355,9 @@ const buildFileContext = (): string => {
}
return context;
};
};
const parseSlides = (content: string): SlidesPresentation | null => {
const parseSlides = (content: string): SlidesPresentation | null => {
try {
const jsonMatch = content.match(/```(?:json)?\s*([\s\S]*?)```/);
const jsonStr = jsonMatch ? jsonMatch[1].trim() : content.trim();
@@ -377,9 +390,9 @@ const parseSlides = (content: string): SlidesPresentation | null => {
console.error("Failed to parse slides:", e);
}
return null;
};
};
const generateAnimatedHtml = (slide: any, index: number): string => {
const generateAnimatedHtml = (slide: any, index: number): string => {
const themeConfig = THEMES.find(t => t.id === theme) || THEMES[1];
const [bg, accent, secondary, text] = themeConfig.colors;
const gradient = themeConfig.gradient;
@@ -476,9 +489,9 @@ const generateAnimatedHtml = (slide: any, index: number): string => {
</style>
</div>
`;
};
};
const handleGenerate = async () => {
const handleGenerate = async () => {
if (!topic.trim()) {
setError("Please enter a topic for your presentation");
return;
@@ -574,17 +587,17 @@ const handleGenerate = async () => {
} finally {
setProcessing(false);
}
};
};
const handleCopy = async () => {
const handleCopy = async () => {
if (slidesPresentation?.rawContent) {
await navigator.clipboard.writeText(slidesPresentation.rawContent);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}
};
};
const handleDownloadHtml = () => {
const handleDownloadHtml = () => {
if (!slidesPresentation) return;
const themeConfig = THEMES.find(t => t.id === slidesPresentation.theme) || THEMES[1];
@@ -786,9 +799,9 @@ const handleDownloadHtml = () => {
a.download = `${slidesPresentation.title.replace(/[^a-z0-9]/gi, '_')}_animated_presentation.html`;
a.click();
URL.revokeObjectURL(url);
};
};
const toggleFullscreen = () => {
const toggleFullscreen = () => {
if (!slideContainerRef.current) return;
if (!document.fullscreenElement) {
@@ -798,21 +811,21 @@ const toggleFullscreen = () => {
document.exitFullscreen();
setIsFullscreen(false);
}
};
};
const goToSlide = (index: number) => {
const goToSlide = (index: number) => {
if (slidesPresentation?.slides) {
setCurrentSlide(Math.max(0, Math.min(index, slidesPresentation.slides.length - 1)));
}
};
};
const formatFileSize = (bytes: number) => {
const formatFileSize = (bytes: number) => {
if (bytes < 1024) return bytes + " B";
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + " KB";
return (bytes / (1024 * 1024)).toFixed(1) + " MB";
};
};
return (
return (
<div className="mx-auto grid max-w-7xl gap-4 lg:gap-6 grid-cols-1 xl:grid-cols-2 text-start">
{/* Input Panel */}
<Card className="h-fit">
@@ -1279,5 +1292,5 @@ return (
</CardContent>
</Card>
</div>
);
);
}

View File

@@ -11,7 +11,16 @@ import { cn } from "@/lib/utils";
import { translations } from "@/lib/i18n/translations";
export default function UXDesignerPrompt() {
const {
language,
currentPrompt,
enhancedPrompt,
selectedProvider,
selectedModels,
availableModels,
apiKeys,
isProcessing,
error,
setSelectedProvider,
setCurrentPrompt,
setEnhancedPrompt,
@@ -21,16 +30,16 @@ export default function UXDesignerPrompt() {
setSelectedModel,
} = useStore();
const t = translations[language].uxDesigner;
const common = translations[language].common;
const t = translations[language].uxDesigner;
const common = translations[language].common;
const [copied, setCopied] = useState(false);
const [generatedPrompt, setGeneratedPrompt] = useState<string | null>(null);
const [copied, setCopied] = useState(false);
const [generatedPrompt, setGeneratedPrompt] = useState<string | null>(null);
const selectedModel = selectedModels[selectedProvider];
const models = availableModels[selectedProvider] || modelAdapter.getAvailableModels(selectedProvider);
const selectedModel = selectedModels[selectedProvider];
const models = availableModels[selectedProvider] || modelAdapter.getAvailableModels(selectedProvider);
useEffect(() => {
useEffect(() => {
if (typeof window !== "undefined") {
loadAvailableModels();
const saved = localStorage.getItem("promptarch-api-keys");
@@ -44,9 +53,9 @@ useEffect(() => {
}
}
}
}, [selectedProvider]);
}, [selectedProvider]);
const loadAvailableModels = async () => {
const loadAvailableModels = async () => {
const fallbackModels = modelAdapter.getAvailableModels(selectedProvider);
setAvailableModels(selectedProvider, fallbackModels);
@@ -58,9 +67,9 @@ const loadAvailableModels = async () => {
} catch (error) {
console.error("Failed to load models:", error);
}
};
};
const handleGenerate = async () => {
const handleGenerate = async () => {
if (!currentPrompt.trim()) {
setError("Please enter an app description");
return;
@@ -98,24 +107,24 @@ const handleGenerate = async () => {
} finally {
setProcessing(false);
}
};
};
const handleCopy = async () => {
const handleCopy = async () => {
if (generatedPrompt) {
await navigator.clipboard.writeText(generatedPrompt);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}
};
};
const handleClear = () => {
const handleClear = () => {
setCurrentPrompt("");
setGeneratedPrompt(null);
setEnhancedPrompt(null);
setError(null);
};
};
return (
return (
<div className="mx-auto grid max-w-7xl gap-4 lg:gap-6 grid-cols-1 lg:grid-cols-2 text-start">
<Card className="h-fit">
<CardHeader className="p-4 lg:p-6 text-start">
@@ -245,5 +254,5 @@ return (
</CardContent>
</Card>
</div>
);
);
}

View File

@@ -29,6 +29,7 @@ export const translations = {
title: "Prompt Enhancer",
description: "Transform your simple ideas into professional, high-quality prompts",
placeholder: "Enter your prompt here...",
inputLabel: "Your Prompt",
enhancedTitle: "Enhanced Prompt",
enhancedDesc: "Your prompt has been optimized for better AI performance",
},
@@ -150,6 +151,7 @@ export const translations = {
title: "Улучшение промптов",
description: "Превратите ваши простые идеи в профессиональные, качественные промпты",
placeholder: "Введите ваш промпт здесь...",
inputLabel: "Ваш промпт",
enhancedTitle: "Улучшенный промпт",
enhancedDesc: "Ваш промпт оптимизирован для лучшей работы ИИ",
},
@@ -271,6 +273,7 @@ export const translations = {
title: "משפר פרומפטים",
description: "הפוך רעיונות פשוטים לפרומפטים מקצועיים באיכות גבוהה",
placeholder: "הזן את הפרומפט שלך כאן...",
inputLabel: "הפרומפט שלך",
enhancedTitle: "פרומפט משופר",
enhancedDesc: "הפרומפט שלך הותאם לביצועי בינה מלאכותית טובים יותר",
},