Spaces:
Paused
Paused
| import re | |
| with open("App.tsx", "r") as f: | |
| content = f.read() | |
| # 1. Imports | |
| imports_old = """import { TemplateFillerModal } from './components/TemplateFillerModal'; | |
| import { HFCueWizardModal } from './components/HFCueWizardModal';""" | |
| imports_new = """import { TemplateFillerModal } from './components/TemplateFillerModal'; | |
| import { WorkflowPanel } from './components/WorkflowPanel'; | |
| import { Workflow } from './types';""" | |
| content = content.replace(imports_old, imports_new) | |
| # 2. Tabs | |
| tab_old = "const [activeTab, setActiveTab] = useState<'chat' | 'merge' | 'import'>('chat');" | |
| tab_new = "const [activeTab, setActiveTab] = useState<'chat' | 'merge' | 'import' | 'workflows'>('chat');" | |
| content = content.replace(tab_old, tab_new) | |
| # 3. Workflow State | |
| state_old = "const [hfWizardOpen, setHfWizardOpen] = useState(false);" | |
| state_new = """const [activeWorkflow, setActiveWorkflow] = useState<{ workflow: Workflow, stepIndex: number, sessionId: string | null } | null>(null);""" | |
| content = content.replace(state_old, state_new) | |
| # 4. Timer Logic | |
| # Look for the timer interval we wrote earlier | |
| timer_regex = re.compile(r" // Timer interval to process HF Deployment Cues every 10 seconds.*?// --- Message Sending & Event Streaming ---", re.DOTALL) | |
| new_timer = """ // Timer interval to process Workflow/HF Cues every 10 seconds | |
| useEffect(() => { | |
| const interval = setInterval(() => { | |
| const stored = localStorage.getItem('jules_hf_timer_queue'); | |
| if (!stored) return; | |
| let currentQueue = JSON.parse(stored); | |
| const now = Date.now(); | |
| let hasChanges = false; | |
| for (const sessionId of Object.keys(currentQueue)) { | |
| const queueData = currentQueue[sessionId]; | |
| // Support both the old format {templates: string[]} and the new format {messages: QueuedMessage[]} | |
| // Let's assume we use the new format where each message has its own fireAt | |
| if (queueData.messages && queueData.messages.length > 0) { | |
| const pending = []; | |
| for (let i = 0; i < queueData.messages.length; i++) { | |
| const msg = queueData.messages[i]; | |
| if (now >= msg.fireAt) { | |
| console.log(`[TimerQueue] Firing queued message for session ${sessionId}`); | |
| setTimeout(() => { | |
| _sendText(sessionId, msg.text).catch(e => console.error("Timer send failed", e)); | |
| }, 0); | |
| hasChanges = true; | |
| } else { | |
| pending.push(msg); | |
| } | |
| } | |
| if (pending.length !== queueData.messages.length) { | |
| if (pending.length > 0) { | |
| currentQueue[sessionId].messages = pending; | |
| } else { | |
| delete currentQueue[sessionId]; | |
| } | |
| hasChanges = true; | |
| } | |
| } | |
| } | |
| if (hasChanges) { | |
| setHfTimerQueue(currentQueue); | |
| localStorage.setItem('jules_hf_timer_queue', JSON.stringify(currentQueue)); | |
| } | |
| }, 10000); | |
| return () => clearInterval(interval); | |
| }, []); | |
| // --- Message Sending & Event Streaming ---""" | |
| content = timer_regex.sub(new_timer, content) | |
| # 5. handleStartWorkflow Logic | |
| handler_old = """ const handleHuggingFaceCue = () => { | |
| setHfWizardOpen(true); | |
| };""" | |
| handler_new = """ const handleHuggingFaceCue = () => { | |
| setActiveTab('workflows'); | |
| }; | |
| const handleStartWorkflow = (workflow: Workflow) => { | |
| setActiveWorkflow({ workflow, stepIndex: 0, sessionId: null }); | |
| if (workflow.steps.length > 0) { | |
| const template = notes.find(n => n.id === workflow.steps[0].templateId); | |
| if (template) { | |
| handleUseTemplate(template); | |
| } else { | |
| alert(`Template ${workflow.steps[0].templateId} not found.`); | |
| setActiveWorkflow(null); | |
| } | |
| } | |
| }; | |
| // Called when a template is submitted to chat | |
| const handleTemplateSubmitted = async (text: string, isNewSession: boolean, sessionIdOverride?: string) => { | |
| if (activeWorkflow) { | |
| const { workflow, stepIndex, sessionId } = activeWorkflow; | |
| const targetSessionId = sessionId || sessionIdOverride || currentSessionId; | |
| if (isNewSession && !targetSessionId) { | |
| // Let the session creation logic handle setting the sessionId and continuing the workflow! | |
| return; | |
| } | |
| if (!isNewSession && targetSessionId) { | |
| // Send the current step text to chat | |
| await _sendText(targetSessionId, text); | |
| } | |
| // Proceed to next step | |
| const nextIndex = stepIndex + 1; | |
| if (nextIndex < workflow.steps.length) { | |
| setActiveWorkflow({ workflow, stepIndex: nextIndex, sessionId: targetSessionId }); | |
| const nextTemplate = notes.find(n => n.id === workflow.steps[nextIndex].templateId); | |
| if (nextTemplate) { | |
| handleUseTemplate(nextTemplate); | |
| } | |
| } else { | |
| setActiveWorkflow(null); | |
| } | |
| } else { | |
| // Standard template use | |
| if (!isNewSession && currentSessionId) { | |
| await _sendText(currentSessionId, text); | |
| } | |
| } | |
| }; | |
| const queueWorkflowStep = (text: string, targetSessionId: string, delayMinutes: number) => { | |
| const fireAt = Date.now() + (delayMinutes * 60 * 1000); | |
| setHfTimerQueue(prev => { | |
| const newQueue = { ...prev }; | |
| if (!newQueue[targetSessionId]) { | |
| newQueue[targetSessionId] = { messages: [] }; | |
| } else if (!newQueue[targetSessionId].messages) { | |
| // Migration from old array | |
| newQueue[targetSessionId].messages = []; | |
| } | |
| newQueue[targetSessionId].messages.push({ | |
| id: `queue-${Date.now()}`, | |
| text, | |
| fireAt, | |
| sessionId: targetSessionId | |
| }); | |
| return newQueue; | |
| }); | |
| }; | |
| """ | |
| content = content.replace(handler_old, handler_new) | |
| # Update the HFCue handler | |
| hf_old = """ const handleStartHfTimer = async (config: any) => {""" | |
| content = content.replace(hf_old, " // Obsolete HF Wizard removed\n /*") | |
| hf_end = """ } | |
| } | |
| } | |
| };""" | |
| content = content.replace(hf_end, " */\n") | |
| # 6. Session Creation handling of Workflow | |
| session_old = """ setCurrentSessionId(newSession.id); | |
| // Queue HF Deployment Cue if checked | |
| if (config.runHfDeploymentCue) { | |
| const templates = [ | |
| "Based on the investigation, create a detailed project vision and implementation plan. Outline the necessary changes and new components. READY", | |
| "Implement the changes according to the plan and prepare for deployment. Ensure all tests pass and documentation is updated. READY" | |
| ]; | |
| // The first template is config.prompt which is already sent | |
| setHfTimerQueue(prev => ({ | |
| ...prev, | |
| [newSession.id]: { | |
| templates: templates, | |
| nextExecutionTime: Date.now() + 30 * 60 * 1000 | |
| } | |
| })); | |
| } | |
| setNewSessionModalOpen(false);""" | |
| session_new = """ setCurrentSessionId(newSession.id); | |
| if (activeWorkflow && activeWorkflow.stepIndex === 0) { | |
| // First step created the session. Move to next step! | |
| const nextIndex = 1; | |
| if (nextIndex < activeWorkflow.workflow.steps.length) { | |
| setActiveWorkflow({ ...activeWorkflow, stepIndex: nextIndex, sessionId: newSession.id }); | |
| const nextTemplate = notes.find(n => n.id === activeWorkflow.workflow.steps[nextIndex].templateId); | |
| if (nextTemplate) { | |
| setTimeout(() => handleUseTemplate(nextTemplate), 500); // Small delay to let modal close | |
| } | |
| } else { | |
| setActiveWorkflow(null); | |
| } | |
| } | |
| setNewSessionModalOpen(false);""" | |
| content = content.replace(session_old, session_new) | |
| # 7. Update TemplateFillerModal integration | |
| modal_old = """ <TemplateFillerModal | |
| note={selectedNoteForTemplate} | |
| isOpen={templateModalOpen} | |
| onClose={() => setTemplateModalOpen(false)} | |
| onSubmit={async (text) => { | |
| if (!currentSessionId) return; | |
| await _sendText(currentSessionId, text); | |
| }} | |
| onStartNewChat={handleStartNewChatFromTemplate} | |
| hfProfileData={hfProfiles[currentAgent.id]} | |
| />""" | |
| modal_new = """ <TemplateFillerModal | |
| note={selectedNoteForTemplate} | |
| isOpen={templateModalOpen} | |
| onClose={() => { | |
| setTemplateModalOpen(false); | |
| if (activeWorkflow && activeWorkflow.stepIndex > 0) { | |
| // If they close during a workflow, cancel the rest | |
| if (confirm('Cancel the rest of the workflow?')) { | |
| setActiveWorkflow(null); | |
| } | |
| } | |
| }} | |
| onSubmit={async (text) => { | |
| if (activeWorkflow && activeWorkflow.stepIndex > 0 && activeWorkflow.sessionId) { | |
| const delay = activeWorkflow.workflow.steps[activeWorkflow.stepIndex].delayMinutes; | |
| queueWorkflowStep(text, activeWorkflow.sessionId, delay); | |
| // Move to next | |
| const nextIndex = activeWorkflow.stepIndex + 1; | |
| if (nextIndex < activeWorkflow.workflow.steps.length) { | |
| setActiveWorkflow({ ...activeWorkflow, stepIndex: nextIndex }); | |
| const nextTemplate = notes.find(n => n.id === activeWorkflow.workflow.steps[nextIndex].templateId); | |
| if (nextTemplate) handleUseTemplate(nextTemplate); | |
| } else { | |
| setActiveWorkflow(null); | |
| setTemplateModalOpen(false); | |
| } | |
| } else { | |
| await handleTemplateSubmitted(text, false); | |
| setTemplateModalOpen(false); | |
| } | |
| }} | |
| onStartNewChat={(text) => { | |
| if (activeWorkflow) { | |
| handleStartNewChatFromTemplate(text); | |
| } else { | |
| handleStartNewChatFromTemplate(text); | |
| } | |
| }} | |
| hfProfileData={hfProfiles[currentAgent.id]} | |
| workflowContext={activeWorkflow ? { | |
| stepIndex: activeWorkflow.stepIndex, | |
| totalSteps: activeWorkflow.workflow.steps.length, | |
| delayMinutes: activeWorkflow.workflow.steps[activeWorkflow.stepIndex].delayMinutes | |
| } : undefined} | |
| />""" | |
| content = content.replace(modal_old, modal_new) | |
| # 8. Update UI Tab rendering | |
| tab_ui_old = """ <button | |
| onClick={() => setActiveTab('import')} | |
| className={`flex items-center gap-2 px-4 py-1.5 rounded-lg text-sm font-bold transition-all ${activeTab === 'import' ? 'bg-white text-indigo-600 shadow-sm' : 'text-gray-500 hover:text-gray-700'}`} | |
| > | |
| <DownloadCloud className="w-4 h-4" /> | |
| Import | |
| </button> | |
| </div>""" | |
| tab_ui_new = """ <button | |
| onClick={() => setActiveTab('import')} | |
| className={`flex items-center gap-2 px-4 py-1.5 rounded-lg text-sm font-bold transition-all ${activeTab === 'import' ? 'bg-white text-indigo-600 shadow-sm' : 'text-gray-500 hover:text-gray-700'}`} | |
| > | |
| <DownloadCloud className="w-4 h-4" /> | |
| Import | |
| </button> | |
| <button | |
| onClick={() => setActiveTab('workflows')} | |
| className={`flex items-center gap-2 px-4 py-1.5 rounded-lg text-sm font-bold transition-all ${activeTab === 'workflows' ? 'bg-white text-indigo-600 shadow-sm' : 'text-gray-500 hover:text-gray-700'}`} | |
| > | |
| <Rocket className="w-4 h-4" /> | |
| Workflows | |
| </button> | |
| </div>""" | |
| content = content.replace(tab_ui_old, tab_ui_new) | |
| render_ui_old = """ ) : ( | |
| <ImportPanel | |
| githubToken={settings.githubTokens[currentAgent.id] || ''} | |
| profile={settings.githubProfiles[currentAgent.id]} | |
| /> | |
| )}""" | |
| render_ui_new = """ ) : activeTab === 'import' ? ( | |
| <ImportPanel | |
| githubToken={settings.githubTokens[currentAgent.id] || ''} | |
| profile={settings.githubProfiles[currentAgent.id]} | |
| /> | |
| ) : ( | |
| <WorkflowPanel | |
| templates={notes} | |
| onStartWorkflow={handleStartWorkflow} | |
| /> | |
| )}""" | |
| content = content.replace(render_ui_old, render_ui_new) | |
| # 9. Remove obsolete HFCueWizardModal | |
| content = re.sub(r'<HFCueWizardModal\s+isOpen=\{hfWizardOpen\}(.*?)\/>\s+', '', content, flags=re.DOTALL) | |
| with open("App.tsx", "w") as f: | |
| f.write(content) | |