import path from 'node:path'; import * as vscode from 'vscode'; import { DiagramSource, readDiagramSource } from '../sources/detect'; import { DecisionAttachment, ModelProblem, Registry, resolveRefs } from './format'; import { effectiveStatus } from './decisions'; import { ModelService } from './service'; /** * A diagram ready for Archify: the source as written, plus the resolved JSON * with registry references expanded, the model problems found on the way, * and the diagram links the viewer should offer. */ export interface PreparedDiagram { source: DiagramSource; /** Model root when the file is inside a model. */ root?: vscode.Uri; registry?: Registry; problems: ModelProblem[]; /** Node id → diagram path relative to the model root. */ links: Map; /** Node id → registry description. */ descriptions: Map; /** Decisions of this diagram for the Decisions rail panel: pointed at by the diagram, or naming it. */ decisions: DiagramDecision[]; } export interface DiagramDecision { id: string; title?: string; status: string; file: string; attachedTo: DecisionAttachment[]; } /** * Reads a diagram and resolves it against its model, if any. Every place * that hands a diagram to Archify (preview, Problems, render, browser) goes * through here, so they all see the same resolved diagram. */ export async function prepareDiagram(models: ModelService, uri: vscode.Uri, text: string): Promise { const source = readDiagramSource(text, uri.path); if (!source) return undefined; const index = await models.indexFor(uri); const found = index ? { root: index.root, registry: index.registry } : undefined; const resolved = resolveRefs(source.diagram, { registry: found?.registry, decisions: index ? new Set(index.decisions.decisions.keys()) : undefined, fileName: uri.path }); // Only diagrams that use refs (or carry a model block) need the resolved form. const changed = resolved.refs.size > 0 || resolved.problems.length > 0 || 'model' in source.diagram || resolved.links.size > 0 || resolved.decisionRefs.length > 0; const prepared: DiagramSource = changed ? { ...source, diagram: resolved.diagram, json: JSON.stringify(resolved.diagram, null, 2) } : source; // Decisions for the rail: the ones this diagram points at, plus the ones that name this diagram. const decisions: DiagramDecision[] = []; if (index) { const rel = path.relative(index.root.fsPath, uri.fsPath).split(path.sep).join('/'); const byId = new Map(); const add = (id: string, attachedTo?: DecisionAttachment) => { const d = index.decisions.decisions.get(id); if (!d) return; let row = byId.get(id); if (!row) { row = { id, title: d.title, status: effectiveStatus(d, index.decisions.decisions).status, file: d.file, attachedTo: [] }; byId.set(id, row); decisions.push(row); } if (attachedTo && !row.attachedTo.some((a) => JSON.stringify(a) === JSON.stringify(attachedTo))) row.attachedTo.push(attachedTo); }; for (const ref of resolved.decisionRefs) add(ref.id, ref.attachedTo); for (const d of index.decisions.decisions.values()) if (d.diagrams.includes(rel)) add(d.id, { kind: 'diagram' }); } return { source: prepared, root: found?.root, registry: found?.registry, problems: resolved.problems, links: resolved.links, descriptions: resolved.descriptions, decisions }; }