From e226409468acb255e03efa1e31853b04647a6cee Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 17 Jan 2026 03:58:42 +0000 Subject: [PATCH] feat: Add customizable AI system prompt This commit introduces a new feature that allows users to customize the AI's system prompt. - A new `SettingsModal` component is created to provide a UI for editing and saving the custom prompt. - The custom prompt is persisted in `localStorage` to be remembered across sessions. - The AI logic is updated to use the custom prompt, with a fallback to the default prompt. - A "Settings" button is added to the header to open the modal. --- src/App.jsx | 68 +++++++++++++++++++++++++++++-------- src/SettingsModal.jsx | 79 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 15 deletions(-) create mode 100644 src/SettingsModal.jsx diff --git a/src/App.jsx b/src/App.jsx index 5f3df15..6d97196 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -11,8 +11,21 @@ import 'reactflow/dist/style.css'; import axios from 'axios'; import html2canvas from 'html2canvas'; import { saveAs } from 'file-saver'; +import SettingsModal from './SettingsModal'; // 🧠 Initial State & Helpers +const defaultSystemPrompt = `You are an expert in cause-and-effect analysis. + +Given a news headline or event, break it down into a chain of consequences, like a domino effect. + +Each node must: +- Be short (max 15 words) +- Represent one specific consequence +- Be categorized as either: 'positive', 'neutral', or 'negative' +- Be logically linked to the prompt + +Return only 3 outputs: one of each type.`; + const initialNodes = []; const initialEdges = []; let nodeId = 1; @@ -27,7 +40,7 @@ const getColor = (type) => { }; // 🤖 AI Logic with Domino Effect Thinking (Updated: Using Puter AI API) -const createAIChildren = async (text, parentId, setNodes, setEdges, stopGenerationRef) => { +const createAIChildren = async (text, parentId, setNodes, setEdges, stopGenerationRef, systemPrompt) => { try { if (stopGenerationRef.current) return; const res = await axios.post( @@ -37,17 +50,7 @@ const createAIChildren = async (text, parentId, setNodes, setEdges, stopGenerati messages: [ { role: 'system', - content: `You are an expert in cause-and-effect analysis. - -Given a news headline or event, break it down into a chain of consequences, like a domino effect. - -Each node must: -- Be short (max 15 words) -- Represent one specific consequence -- Be categorized as either: 'positive', 'neutral', or 'negative' -- Be logically linked to the prompt - -Return only 3 outputs: one of each type.` + content: systemPrompt, }, { role: 'user', content: text } ] @@ -96,7 +99,7 @@ Return only 3 outputs: one of each type.` // Recursively expand each child one level deep for (const n of newNodes) { if (stopGenerationRef.current) break; - await createAIChildren(n.data.label, n.id, setNodes, setEdges, stopGenerationRef); + await createAIChildren(n.data.label, n.id, setNodes, setEdges, stopGenerationRef, systemPrompt); } } catch (err) { @@ -115,6 +118,17 @@ export default function App() { const [loading, setLoading] = useState(false); const stopGenerationRef = useRef(false); const reactFlowWrapper = useRef(null); + const [isSettingsOpen, setIsSettingsOpen] = useState(false); + const [systemPrompt, setSystemPrompt] = useState( + () => localStorage.getItem('customSystemPrompt') || defaultSystemPrompt + ); + const [modalSystemPrompt, setModalSystemPrompt] = useState(systemPrompt); + + const handleSaveSettings = () => { + setSystemPrompt(modalSystemPrompt); + localStorage.setItem('customSystemPrompt', modalSystemPrompt); + setIsSettingsOpen(false); + }; const handleExport = () => { if (reactFlowWrapper.current) { @@ -150,7 +164,7 @@ export default function App() { setNodes([rootNode]); setEdges([]); - await createAIChildren(prompt, rootId, setNodes, setEdges, stopGenerationRef); + await createAIChildren(prompt, rootId, setNodes, setEdges, stopGenerationRef, systemPrompt); setPrompt(''); setLoading(false); @@ -164,7 +178,7 @@ export default function App() { const onNodeClick = async (_event, node) => { stopGenerationRef.current = false; setLoading(true); - await createAIChildren(node.data.label, node.id, setNodes, setEdges, stopGenerationRef); + await createAIChildren(node.data.label, node.id, setNodes, setEdges, stopGenerationRef, systemPrompt); setLoading(false); }; @@ -246,6 +260,23 @@ export default function App() { > Export as PNG + { + setModalSystemPrompt(systemPrompt); + setIsSettingsOpen(true); + }} + style={{ + padding: '10px 20px', + backgroundColor: '#6c757d', + color: 'white', + border: 'none', + borderRadius: '8px', + fontWeight: 'bold', + cursor: 'pointer', + }} + > + ⚙️ Settings + @@ -265,6 +296,13 @@ export default function App() { + setIsSettingsOpen(false)} + onSave={handleSaveSettings} + systemPrompt={modalSystemPrompt} + setSystemPrompt={setModalSystemPrompt} + /> ); } diff --git a/src/SettingsModal.jsx b/src/SettingsModal.jsx new file mode 100644 index 0000000..d5bf379 --- /dev/null +++ b/src/SettingsModal.jsx @@ -0,0 +1,79 @@ +// ⚙️ SettingsModal Component +import React from 'react'; + +export default function SettingsModal({ isOpen, onClose, onSave, systemPrompt, setSystemPrompt }) { + if (!isOpen) return null; + + return ( + + + Custom AI System Prompt + + This prompt guides the AI's cause-and-effect analysis. + + setSystemPrompt(e.target.value)} + style={{ + width: '100%', + height: '200px', + padding: '10px', + borderRadius: '8px', + border: '1px solid #555', + color: '#fff', + backgroundColor: '#222', + fontSize: '1rem', + marginBottom: '1rem', + }} + /> + + + Cancel + + + Save + + + + + ); +}
+ This prompt guides the AI's cause-and-effect analysis. +