diff --git a/frontend/src/pages/CausalLab.tsx b/frontend/src/pages/CausalLab.tsx index d70a8fa..aaa76e5 100644 --- a/frontend/src/pages/CausalLab.tsx +++ b/frontend/src/pages/CausalLab.tsx @@ -538,6 +538,65 @@ function applyGrammarOps(state: EditorState, ops: GrammarOp[]): { state: EditorS return { state: s, errors } } +function graphToGrammar(state: EditorState): string { + const lines: string[] = [] + + if (state.nodes.length) { + lines.push(`# Nœuds (${state.nodes.length})`) + for (const n of state.nodes) { + let l = `+node id=${n.id} label="${n.label}" type=${n.type} x=${n.x} y=${n.y}` + if (n.instrument) l += ` instrument=${n.instrument}` + if (n.unit) l += ` unit=${n.unit}` + if (n.formula) l += ` formula="${n.formula}"` + lines.push(l) + } + lines.push('') + } + + if (state.edges.length) { + lines.push(`# Arêtes (${state.edges.length})`) + for (const e of state.edges) { + const sg = e.sign === 'positive' ? '+' : e.sign === 'negative' ? '-' : '=' + let l = `+edge from=${e.from} to=${e.to} sign=${sg}` + if (e.strength != null && e.strength !== 2) l += ` s=${e.strength}` + if (e.style === 'dashed') l += ` style=dashed` + if (e.label) l += ` label="${e.label}"` + if (e.lag_days != null) l += ` lag=${e.lag_days}` + if (e.decay_days === null) l += ` decay=null` + else if (e.decay_days != null) l += ` decay=${e.decay_days}` + lines.push(l) + } + lines.push('') + } + + const coefs = Object.entries(state.coefficients) + if (coefs.length) { + lines.push(`# Coefficients (${coefs.length})`) + for (const [key, c] of coefs) { + let l = `+coef key=${key} value=${c.value}` + if (c.description) l += ` desc="${c.description}"` + lines.push(l) + } + lines.push('') + } + + const inputs = Object.entries(state.inputMapping) + if (inputs.length) { + lines.push(`# Inputs (${inputs.length})`) + for (const [node, m] of inputs) { + let l = `~input node=${node} source=${m.source}` + if (m.key) l += ` key=${m.key}` + lines.push(l) + } + lines.push('') + } + + if (state.instruments.length) + lines.push(`~instruments ${state.instruments.join(',')}`) + + return lines.join('\n').trim() +} + const GRAMMAR_REFERENCE = `# Nœuds +node id= label="" type= [x=] [y=] [instrument=] ~node id= [label="..."] [type=...] [x=] [y=] [instrument=] @@ -1103,7 +1162,9 @@ function TabEditor({ initialId }: { initialId?: number | null }) { const [grammarApplied, setGrammarApplied] = useState(0) // count of applied ops const [aiLoading, setAiLoading] = useState(false) const [aiError, setAiError] = useState(null) - const [showGrammarRef, setShowGrammarRef] = useState(false) + const [showGrammarRef, setShowGrammarRef] = useState(false) + const [showGrammarExport, setShowGrammarExport] = useState(false) + const [copied, setCopied] = useState(false) const svgRef = useRef(null) useEffect(() => { @@ -1837,6 +1898,41 @@ function TabEditor({ initialId }: { initialId?: number | null }) { )} + {/* Export — grammaire courante du graphe */} +
+ + {showGrammarExport && (() => { + const grammar = graphToGrammar(state) + return ( +
+
+                    {grammar}
+                  
+ +
+ ) + })()} +
+