Implemented confi snapshots
This commit is contained in:
@@ -4,6 +4,7 @@ import SignalPicker, { SignalOption } from './SignalPicker';
|
||||
import LuaEditor from './LuaEditor';
|
||||
import type { SignalDef, SynthGraph, SynthGraphNode } from './lib/types';
|
||||
import { inferNodeTypes, SynthType } from './lib/synthTypes';
|
||||
import { VersionTree, DiffViewer } from './VersionHistory';
|
||||
|
||||
interface DataSource { name: string; }
|
||||
interface SignalInfo { name: string; type?: string; }
|
||||
@@ -313,6 +314,11 @@ export default function SyntheticGraphEditor({ name, create, panelId, onClose, o
|
||||
// Quick-add HUD (S = signal, N = node); null when closed.
|
||||
const [hud, setHud] = useState<null | 'signal' | 'node'>(null);
|
||||
const [hudFilter, setHudFilter] = useState('');
|
||||
// Version history pane (existing signals only).
|
||||
const [showVersions, setShowVersions] = useState(false);
|
||||
const [viewingVersion, setViewingVersion] = useState<number | null>(null);
|
||||
const [diff, setDiff] = useState<{ av: number; bv: number } | null>(null);
|
||||
const [verReload, setVerReload] = useState(0);
|
||||
|
||||
// Identity fields — editable only when creating a new signal.
|
||||
const [newName, setNewName] = useState('');
|
||||
@@ -710,6 +716,44 @@ export default function SyntheticGraphEditor({ name, create, panelId, onClose, o
|
||||
}
|
||||
}
|
||||
|
||||
// Apply a fetched SignalDef to the editor (meta fields + rebuilt node graph).
|
||||
function applyDef(d: SignalDef, asEdit: boolean) {
|
||||
setDef(d);
|
||||
setUnit(d.meta?.unit ?? '');
|
||||
setDesc(d.meta?.description ?? '');
|
||||
setDispLow(String(d.meta?.displayLow ?? '0'));
|
||||
setDispHigh(String(d.meta?.displayHigh ?? '100'));
|
||||
const g = buildInitial(d);
|
||||
if (asEdit) commit(g); else { undoStack.current = []; redoStack.current = []; setGraph(g); bump(); }
|
||||
g.nodes.forEach(n => { if (n.kind === 'source' && n.ds) loadSignals(n.ds); });
|
||||
}
|
||||
|
||||
// Load a past revision into the editor as an undoable edit; saving promotes it
|
||||
// to a new current version (non-destructive).
|
||||
async function viewVersion(version: number) {
|
||||
if (canUndo && !confirm('Discard unsaved changes to load this version?')) return;
|
||||
try {
|
||||
const res = await fetch(`/api/v1/synthetic/${encodeURIComponent(sigName)}/versions/${version}`);
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
const d: SignalDef = await res.json();
|
||||
applyDef(d, false);
|
||||
setViewingVersion(version);
|
||||
} catch (err) {
|
||||
setError(`Load version failed: ${err instanceof Error ? err.message : err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function reloadCurrent() {
|
||||
try {
|
||||
const res = await fetch(`/api/v1/synthetic/${encodeURIComponent(sigName)}`);
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
applyDef(await res.json(), false);
|
||||
setViewingVersion(null);
|
||||
} catch (err) {
|
||||
setError(`Reload failed: ${err instanceof Error ? err.message : err}`);
|
||||
}
|
||||
}
|
||||
|
||||
function nodeClass(n: GNode): string {
|
||||
const cat = n.kind === 'source' ? 'trigger' : n.kind === 'output' ? 'action' : 'flow';
|
||||
const err = nodeErrors.has(n.id) ? ' flow-node-error' : '';
|
||||
@@ -721,6 +765,10 @@ export default function SyntheticGraphEditor({ name, create, panelId, onClose, o
|
||||
<div class="synth-graph" onClick={(e) => e.stopPropagation()}>
|
||||
<div class="wizard-header">
|
||||
<span>Synthetic Signal{create ? ' — new' : ` — ${name}`}</span>
|
||||
{!create && (
|
||||
<button class={`toolbar-btn${showVersions ? ' toolbar-btn-primary' : ''}`}
|
||||
style="margin-left:auto;" onClick={() => setShowVersions(v => !v)}>History</button>
|
||||
)}
|
||||
<button class="icon-btn" onClick={onClose}>✕</button>
|
||||
</div>
|
||||
|
||||
@@ -982,6 +1030,30 @@ export default function SyntheticGraphEditor({ name, create, panelId, onClose, o
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!create && showVersions && (
|
||||
<div class="ver-pane synth-ver-pane">
|
||||
<div class="ver-pane-head">Version history
|
||||
<button class="icon-btn" onClick={() => setShowVersions(false)}>✕</button>
|
||||
</div>
|
||||
<VersionTree
|
||||
base={`/api/v1/synthetic/${encodeURIComponent(sigName)}`}
|
||||
viewing={viewingVersion}
|
||||
dirty={canUndo}
|
||||
reloadKey={verReload}
|
||||
onView={viewVersion}
|
||||
onForked={() => { onSaved(); setVerReload(k => k + 1); }}
|
||||
onPromoted={async () => { await reloadCurrent(); onSaved(); setVerReload(k => k + 1); }}
|
||||
onCompare={(av, bv) => setDiff({ av, bv })}
|
||||
onError={setError} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{diff && (
|
||||
<DiffViewer base={`/api/v1/synthetic/${encodeURIComponent(sigName)}`}
|
||||
av={diff.av} bv={diff.bv} title={`${sigName} — v${diff.av} → v${diff.bv}`}
|
||||
onClose={() => setDiff(null)} />
|
||||
)}
|
||||
|
||||
<div class="wizard-footer">
|
||||
{error && <span class="wizard-error" style="flex:1;">{error}</span>}
|
||||
{!error && firstError && <span class="hint" style="flex:1;">{firstError}</span>}
|
||||
|
||||
Reference in New Issue
Block a user