import { useState, useEffect, useCallback } from 'react'; import { getPrompt, getPrompts, PromptContentResponse, Template, resetPrompt, savePrompt, } from '../../api'; import { Card, CardContent, CardHeader, CardTitle } from '../ui/card'; import { Button } from '../ui/button'; import { AlertTriangle, RotateCcw, ArrowLeft } from 'lucide-react'; import { toast } from 'react-toastify'; import { defineMessages, useIntl } from '../../i18n'; const i18n = defineMessages({ failedToLoadPrompts: { id: 'promptsSettings.failedToLoadPrompts', defaultMessage: 'Failed to load prompts', }, failedToLoadPrompt: { id: 'promptsSettings.failedToLoadPrompt', defaultMessage: 'Failed to load prompt', }, confirmResetAll: { id: 'promptsSettings.confirmResetAll', defaultMessage: 'Are you sure you want to reset all prompts to their defaults? This cannot be undone.', }, allPromptsReset: { id: 'promptsSettings.allPromptsReset', defaultMessage: 'All prompts reset to defaults', }, failedToResetPrompts: { id: 'promptsSettings.failedToResetPrompts', defaultMessage: 'Failed to reset prompts', }, promptSaved: { id: 'promptsSettings.promptSaved', defaultMessage: 'Prompt saved', }, failedToSavePrompt: { id: 'promptsSettings.failedToSavePrompt', defaultMessage: 'Failed to save prompt', }, confirmResetOne: { id: 'promptsSettings.confirmResetOne', defaultMessage: 'Are you sure you want to reset this prompt to its default? This cannot be undone.', }, promptResetToDefault: { id: 'promptsSettings.promptResetToDefault', defaultMessage: 'Prompt reset to default', }, failedToResetPrompt: { id: 'promptsSettings.failedToResetPrompt', defaultMessage: 'Failed to reset prompt', }, confirmReplaceWithDefault: { id: 'promptsSettings.confirmReplaceWithDefault', defaultMessage: 'Replace current content with default? Your changes will be lost.', }, confirmUnsavedBack: { id: 'promptsSettings.confirmUnsavedBack', defaultMessage: 'You have unsaved changes. Are you sure you want to go back?', }, backToList: { id: 'promptsSettings.backToList', defaultMessage: 'Back to List', }, resetToDefault: { id: 'promptsSettings.resetToDefault', defaultMessage: 'Reset to Default', }, save: { id: 'promptsSettings.save', defaultMessage: 'Save', }, editPromptTitle: { id: 'promptsSettings.editPromptTitle', defaultMessage: 'Edit: {name}', }, customized: { id: 'promptsSettings.customized', defaultMessage: 'Customized', }, templateTip: { id: 'promptsSettings.templateTip', defaultMessage: 'Template variables like {extensionsExample} or {forExample} are replaced with actual values at runtime. Be careful not to remove required variables.', }, editingLabel: { id: 'promptsSettings.editingLabel', defaultMessage: 'Editing: {name}', }, restoreDefault: { id: 'promptsSettings.restoreDefault', defaultMessage: 'Restore Default', }, enterPromptContent: { id: 'promptsSettings.enterPromptContent', defaultMessage: 'Enter prompt content...', }, unsavedChanges: { id: 'promptsSettings.unsavedChanges', defaultMessage: 'You have unsaved changes', }, promptEditingTitle: { id: 'promptsSettings.promptEditingTitle', defaultMessage: 'Prompt Editing', }, promptEditingDescription: { id: 'promptsSettings.promptEditingDescription', defaultMessage: "Customize the prompts that define goose's behavior in different contexts. These prompts use Jinja2 templating syntax. Be careful when modifying template variables, as incorrect changes can break functionality. Please share any improvements with the community.", }, resetAll: { id: 'promptsSettings.resetAll', defaultMessage: 'Reset All', }, edit: { id: 'promptsSettings.edit', defaultMessage: 'Edit', }, }); export default function PromptsSettingsSection() { const intl = useIntl(); const [prompts, setPrompts] = useState([]); const [selectedPrompt, setSelectedPrompt] = useState(null); const [promptData, setPromptData] = useState(null); const [content, setContent] = useState(''); const [hasChanges, setHasChanges] = useState(false); const fetchPrompts = useCallback(async () => { try { const response = await getPrompts(); if (response.data) { setPrompts(response.data.prompts); } } catch (error) { console.error('Failed to fetch prompts:', error); toast.error(intl.formatMessage(i18n.failedToLoadPrompts)); } }, [intl]); useEffect(() => { fetchPrompts(); }, [fetchPrompts]); useEffect(() => { if (selectedPrompt) { const fetchPrompt = async () => { try { const response = await getPrompt({ path: { name: selectedPrompt } }); if (response.data) { setPromptData(response.data); setContent(response.data.content); } } catch (error) { console.error('Failed to fetch prompt:', error); toast.error(intl.formatMessage(i18n.failedToLoadPrompt)); } }; fetchPrompt(); } }, [selectedPrompt, intl]); useEffect(() => { if (promptData) { setHasChanges(content !== promptData.content); } }, [content, promptData]); const handleResetAll = async () => { if ( !window.confirm( intl.formatMessage(i18n.confirmResetAll) ) ) { return; } try { const customizedPrompts = prompts.filter((p) => p.is_customized); for (const prompt of customizedPrompts) { await resetPrompt({ path: { name: prompt.name } }); } toast.success(intl.formatMessage(i18n.allPromptsReset)); fetchPrompts(); } catch (error) { console.error('Failed to reset all prompts:', error); toast.error(intl.formatMessage(i18n.failedToResetPrompts)); } }; const handleSave = async () => { if (!selectedPrompt) return; try { await savePrompt({ path: { name: selectedPrompt }, body: { content }, }); toast.success(intl.formatMessage(i18n.promptSaved)); setPromptData((prev) => (prev ? { ...prev, content, is_customized: true } : null)); fetchPrompts(); } catch (error) { console.error('Failed to save prompt:', error); toast.error(intl.formatMessage(i18n.failedToSavePrompt)); } }; const handleReset = async () => { if (!selectedPrompt) return; if ( !window.confirm( intl.formatMessage(i18n.confirmResetOne) ) ) { return; } try { await resetPrompt({ path: { name: selectedPrompt } }); if (promptData) { setContent(promptData.default_content); setPromptData({ ...promptData, content: promptData.default_content, is_customized: false }); } fetchPrompts(); toast.success(intl.formatMessage(i18n.promptResetToDefault)); } catch (error) { console.error('Failed to reset prompt:', error); toast.error(intl.formatMessage(i18n.failedToResetPrompt)); } }; const handleRestoreDefault = () => { if (promptData) { if (hasChanges) { if (!window.confirm(intl.formatMessage(i18n.confirmReplaceWithDefault))) { return; } } setContent(promptData.default_content); } }; const handleBack = () => { if (hasChanges) { if (!window.confirm(intl.formatMessage(i18n.confirmUnsavedBack))) { return; } } setSelectedPrompt(null); setPromptData(null); setContent(''); }; const hasCustomizedPrompts = prompts.some((p) => p.is_customized); if (selectedPrompt) { return (
{promptData?.is_customized && ( )}
{intl.formatMessage(i18n.editPromptTitle, { name: selectedPrompt })} {promptData?.is_customized && ( {intl.formatMessage(i18n.customized)} )}

{intl.formatMessage(i18n.templateTip, { extensionsExample: '{{ extensions }}', forExample: '{% for item in list %}', })}

{promptData?.is_customized && content !== promptData.default_content && ( )}