diff --git a/src/components/GlossPanel.vue b/src/components/GlossPanel.vue index 8373dfa..39ecdb3 100644 --- a/src/components/GlossPanel.vue +++ b/src/components/GlossPanel.vue @@ -14,7 +14,9 @@ import { useVueFlow } from "@vue-flow/core" import { computed } from "vue" import { type GlossEntry, INFO_LINK_GLOSS, NODE_GLOSSARY } from "@/model/glossary" import type { EdgeData, NodeData } from "@/model/projection" +import { useModelStore } from "@/store/model" +const store = useModelStore() const { getSelectedNodes, getSelectedEdges } = useVueFlow("meadows") const gloss = computed(() => { @@ -32,6 +34,33 @@ const gloss = computed(() => { } return null }) + +/** + * The selected element's own "why it's here" note (G4) — distinct from the + * generic `gloss` above, which defines the *kind*. Read live from the store so + * it tracks edits, and resolves the same selection a pipe/info edge stands in for. + */ +const description = computed(() => { + const nodes = getSelectedNodes.value + const edges = getSelectedEdges.value + + if (nodes.length === 1 && edges.length === 0) { + const node = store.model.nodes.find((n) => n.id === nodes[0].id) + return node && node.kind !== "cloud" ? (node.description ?? null) : null + } + if (edges.length === 1 && nodes.length === 0) { + const edge = edges[0] + const kind = (edge.data as EdgeData | undefined)?.kind + if (kind === "pipe") { + const flow = store.model.nodes.find((n) => n.id === edge.id.split("::")[0]) + return flow && flow.kind !== "cloud" ? (flow.description ?? null) : null + } + if (kind === "info") { + return store.model.infoLinks.find((l) => l.id === edge.id)?.description ?? null + } + } + return null +}) diff --git a/src/components/Inspector.vue b/src/components/Inspector.vue index 615a025..b439625 100644 --- a/src/components/Inspector.vue +++ b/src/components/Inspector.vue @@ -14,20 +14,25 @@ import { useVueFlow } from "@vue-flow/core" import { computed } from "vue" import type { EdgeData } from "@/model/projection" -import type { ConverterNode, FlowNode, Rule, StockNode } from "@/model/types" +import type { ConverterNode, FlowNode, InformationLink, Rule, StockNode } from "@/model/types" import { useModelStore } from "@/store/model" const store = useModelStore() const { getSelectedNodes, getSelectedEdges } = useVueFlow("meadows") -/** The single selected element's id — a node directly, or a Flow via its pipe edge. */ +/** + * The single selected element's id — a node directly, a Flow via its pipe edge, + * or an Information Link via its info edge (the edge id *is* the link id). + */ const selectedId = computed(() => { const nodes = getSelectedNodes.value const edges = getSelectedEdges.value if (nodes.length === 1 && edges.length === 0) return nodes[0].id if (edges.length === 1 && nodes.length === 0) { const edge = edges[0] - if ((edge.data as EdgeData | undefined)?.kind === "pipe") return edge.id.split("::")[0] + const kind = (edge.data as EdgeData | undefined)?.kind + if (kind === "pipe") return edge.id.split("::")[0] + if (kind === "info") return edge.id } return null }) @@ -41,8 +46,21 @@ const element = computed(() => { return null }) +/** The live Information Link behind the selection, when a link (not a node) is selected. */ +const link = computed(() => { + const id = selectedId.value + if (!id) return null + return store.model.infoLinks.find((l) => l.id === id) ?? null +}) + const KIND_LABEL = { stock: "Stock", flow: "Flow", converter: "Converter" } as const +/** A link's endpoints are always named nodes — it never touches a Cloud (validation.ts). */ +function nodeName(id: string): string { + const node = store.model.nodes.find((n) => n.id === id) + return node && "name" in node ? node.name : "" +} + /** Every rule carries exactly one number (a value or a factor); read it uniformly. */ function ruleNumber(rule?: Rule): number { if (!rule) return 0 @@ -68,6 +86,13 @@ function onUnit(event: Event): void { store.setUnit(el.id, (event.target as HTMLInputElement).value) } +/** Write the description for whichever element (node or link) is selected. */ +function onDescription(event: Event): void { + const id = element.value?.id ?? link.value?.id + if (!id) return + store.setDescription(id, (event.target as HTMLTextAreaElement).value) +} + function onKind(event: Event): void { const el = element.value if (el?.kind !== "flow" && el?.kind !== "converter") return @@ -95,70 +120,96 @@ const RULE_HINT: Record = {