diff --git a/backend/nodes/text_note.py b/backend/nodes/text_note.py index 306e297..fa8501d 100644 --- a/backend/nodes/text_note.py +++ b/backend/nodes/text_note.py @@ -3,7 +3,7 @@ from __future__ import annotations from backend.node_registry import register_node -@register_node(display_name="Text Note") +@register_node(display_name="Journal") class TextNote: """A floating text card for annotating workflows. Supports Markdown.""" diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 89f6580..0a96220 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -873,6 +873,7 @@ function Flow() { const canvasRightZoomRef = useRef(null); const suppressPaneContextMenuUntilRef = useRef(0); const loadNodeOutputRequestVersionsRef = useRef(new Map()); + const journalContentRef = useRef(''); const reactFlow = useReactFlow(); // ── WebSocket ─────────────────────────────────────────────────────── @@ -1959,12 +1960,13 @@ function Flow() { const openJournalTab = useCallback(() => { setHelpTabs((prev) => { if (prev.find((t) => t.label === 'Journal')) return prev; - return [...prev, { label: 'Journal', type: 'journal', content: '' }]; + return [...prev, { label: 'Journal', type: 'journal', content: journalContentRef.current }]; }); setActiveHelpTab('Journal'); }, []); const updateTabContent = useCallback((label, content) => { + if (label === 'Journal') journalContentRef.current = content; setHelpTabs((prev) => prev.map((t) => t.label === label ? { ...t, content } : t)); }, []); @@ -1993,6 +1995,10 @@ function Flow() { setNodes(sortNodesForParentOrder(hydrated.nodes)); setEdges(hydrated.edges); nextIdRef.current = hydrated.nextNodeId; + journalContentRef.current = data.journalContent || ''; + setHelpTabs((prev) => prev.map((t) => + t.label === 'Journal' ? { ...t, content: journalContentRef.current } : t, + )); initializeDynamicNodes(hydrated.nodes); }, [initializeDynamicNodes, setNodes, setEdges]); @@ -2095,6 +2101,7 @@ function Flow() { const stampedBlob = await stampLogoOnBlob(blob); const workflow = serializeWorkflowState(allNodes, reactFlow.getEdges()); + if (journalContentRef.current) workflow.journalContent = journalContentRef.current; return embedWorkflow(stampedBlob, workflow); }, [reactFlow]); @@ -3003,6 +3010,7 @@ function Flow() { onTabSelect={setActiveHelpTab} onTabClose={closeHelpTab} onTabContentChange={updateTabContent} + onOpenJournal={openJournalTab} /> ); diff --git a/frontend/src/HelpPanelManager.jsx b/frontend/src/HelpPanelManager.jsx index 6f71930..07380cc 100644 --- a/frontend/src/HelpPanelManager.jsx +++ b/frontend/src/HelpPanelManager.jsx @@ -4,7 +4,11 @@ import { marked } from 'marked'; function JournalTab({ content, onChange }) { const [isEditing, setIsEditing] = useState(false); - const renderedHtml = content?.trim() ? marked.parse(content) : ''; + + let renderedHtml = ''; + if (!isEditing && content?.trim()) { + try { renderedHtml = marked.parse(content); } catch { /* fallback to raw */ renderedHtml = content; } + } return (