feat(editor): add a simulation panel and an element inspector

- ResultsPanel: a Simulate panel that charts every Stock over time (hand-built
  SVG), with start/stop/dt run controls and a divergence warning
- Inspector: edit a selected Stock's initial value or a Flow/Converter's rule
- store: undoable setInitialValue / setRule / setSimSpec actions
This commit is contained in:
Julien Calixte
2026-06-20 13:56:19 +02:00
parent 40dc9fba43
commit 34df540e4a
4 changed files with 390 additions and 1 deletions

View File

@@ -33,8 +33,10 @@ import { canConnect } from "@/model/validation"
import { useModelStore } from "@/store/model"
import { NODE_DND_MIME, type PlaceableKind } from "./palette-dnd"
import GlossPanel from "./GlossPanel.vue"
import Inspector from "./Inspector.vue"
import LoopOverlay from "./LoopOverlay.vue"
import Palette from "./Palette.vue"
import ResultsPanel from "./ResultsPanel.vue"
import InfoLinkEdge from "./edges/InfoLinkEdge.vue"
import PipeEdge from "./edges/PipeEdge.vue"
import CloudNode from "./nodes/CloudNode.vue"
@@ -48,6 +50,10 @@ const graph = computed(() => project(store.model))
const nodes = computed(() => graph.value.nodes)
const edges = computed(() => graph.value.edges)
// The simulation results panel (phase 2). Toggled from the header; the panel runs
// the Model and recomputes reactively while open.
const showResults = ref(false)
// Explicit shared id: useVueFlow() runs here in the parent setup, before
// <VueFlow> mounts. Pinning both to the same id guarantees they resolve to one
// store instance, so the event hooks below actually fire.
@@ -358,6 +364,13 @@ onBeforeUnmount(() => {
</li>
</ul>
</div>
<button
class="btn btn-primary btn-sm"
:class="{ 'btn-active': showResults }"
@click="showResults = !showResults"
>
Simulate
</button>
<button class="btn btn-ghost btn-sm" @click="exportModel">Export</button>
<button class="btn btn-ghost btn-sm" @click="fileInput?.click()">Import</button>
<input
@@ -420,6 +433,8 @@ onBeforeUnmount(() => {
<Palette class="absolute top-3 left-3 z-20" @add="addNode" />
<LoopOverlay />
<GlossPanel />
<Inspector />
<ResultsPanel v-if="showResults" @close="showResults = false" />
<!-- Self-dismissing teaching hint, e.g. when a Flow is dragged back onto its
own Stock to "close the loop". Click to dismiss early; z-30 keeps it above