diff --git a/SKILL.md b/SKILL.md index acb8db5..12779db 100644 --- a/SKILL.md +++ b/SKILL.md @@ -175,6 +175,7 @@ Diagrams next to `main.archify` are listed first, ungrouped; deeper ones by fold - **Links are relative to the model root**, never to the file, and must end in `.archify` and exist. `..` is refused. - **A second `main.archify` deeper down** is reported, not nested. One model per tree. - **Diagram kinds other than architecture** take `ref` too (workflow and dataflow `nodes`, sequence `participants`, lifecycle `states`), but the registry `type` must fit that kind: lifecycle states use their own type enum, and sequence participants have no `tag`. +- **Cards come from the registry.** A model diagram without `cards:` gets one card per drawn shared element that has a description. To show something else below the diagram, write `cards:` yourself; to show nothing, write `cards: []`. - **No em dashes or smart quotes in ids**; ids match `^[a-zA-Z][a-zA-Z0-9_-]*$`. ## References diff --git a/references/formats.md b/references/formats.md index bb0d8a4..db68c23 100644 --- a/references/formats.md +++ b/references/formats.md @@ -100,3 +100,7 @@ For each diagram file under the root (and a single file outside a model): 5. Hand the result to `archify validate --json` (or `render`). `task model:resolve -- --file=… --out=x.json` writes step 4's result. + +## Cards under a model diagram + +Archify shows `cards` (a coloured dot, a title, a few lines) below a diagram. An architecture diagram inside a model that draws shared elements and defines no `cards` of its own gets one card per drawn element that has a `description` in the registry, in drawing order, with the dot colour of the element's type (frontend cyan, backend emerald, database violet, cloud amber, security rose, messagebus orange, external slate). `model:resolve` and the extension both add them; write your own `cards:` in the diagram to replace them. diff --git a/scripts/src/services/model.mjs b/scripts/src/services/model.mjs index fbe2fe5..64f5020 100644 --- a/scripts/src/services/model.mjs +++ b/scripts/src/services/model.mjs @@ -28,6 +28,18 @@ const COPY = { }; // What a diagram may override on a shared element. Everything else in COPY is fixed. export const OVERRIDE = ['label', 'sublabel', 'tag']; +// Archify card dot colour per component type (the colour Archify gives the node). +const DOT_BY_TYPE = { frontend: 'cyan', backend: 'emerald', database: 'violet', cloud: 'amber', security: 'rose', messagebus: 'orange', external: 'slate' }; +export const dotForType = (type) => DOT_BY_TYPE[type ?? ''] ?? 'slate'; + +// Archify info cards for shared elements: one per element with a description, +// titled by its label. An architecture diagram without cards of its own gets +// these for the elements it draws, as the extension does. +export function cardsFromElements(elements) { + return elements + .filter((element) => typeof element.description === 'string' && element.description.trim()) + .map((element) => ({ dot: dotForType(element.type), title: element.label ?? element.id, items: [element.description.trim()] })); +} // Registry-only fields, never sent to Archify. const REGISTRY_ONLY = ['description', 'diagram']; const SKIP_DIRS = new Set(['node_modules', '.git']); @@ -241,6 +253,11 @@ export function resolveDiagram(file, { root, elements } = {}) { } return merged; }); + if (type === 'architecture' && diagram.cards === undefined && refs.length) { + const drawn = [...new Set(refs)].map((ref) => elements?.[ref]).filter(Boolean); + const cards = cardsFromElements(drawn); + if (cards.length) diagram.cards = cards; + } result.resolved = diagram; return result; } diff --git a/scripts/src/test/run.mjs b/scripts/src/test/run.mjs index eb529cf..8f3442c 100644 --- a/scripts/src/test/run.mjs +++ b/scripts/src/test/run.mjs @@ -80,7 +80,10 @@ try { assert(json.model === undefined, 'model block leaked into the resolved JSON'); const api = json.components.find((c) => c.id === 'api'); assert(api && api.type === 'backend' && api.brand === 'node-js' && api.description === undefined && api.diagram === undefined, 'api was not resolved from the registry'); - notes.push(`${json.components.length} components, refs expanded`); + const cards = json.cards ?? []; + assert(cards.length === 7 && cards.every((c) => c.dot && c.title && c.items.length === 1), `expected 7 generated cards, got ${JSON.stringify(cards).slice(0, 200)}`); + assert(cards.find((c) => c.title === 'API')?.dot === 'emerald', 'API card has the backend colour'); + notes.push(`${json.components.length} components, refs expanded, ${cards.length} cards from the registry`); }); check('model-resolve prints the JSON inline without --out', ['--action', 'model-resolve', '--file', path.join(example, 'main.archify')], (r) => {