Registry as Archify cards on the model page; cards under model diagrams from registry descriptions
- model page: registry elements are rendered as Archify info cards (type colour dot, label, id · type, description, used-in chips that open the diagram), replacing the table - resolveRefs: a diagram without cards of its own gets one card per drawn shared element that has a description, so the registry text shows under every model diagram as it does on hand-written diagrams - unit tests for generated cards, own cards winning and no cards without refs; docs and changelog
This commit is contained in:
@@ -55,6 +55,42 @@ export interface Registry {
|
||||
problems: ModelProblem[];
|
||||
}
|
||||
|
||||
/** Archify card dot colour for a component type (the colours Archify gives the nodes). */
|
||||
const DOT_BY_TYPE: Record<string, string> = {
|
||||
frontend: 'cyan',
|
||||
backend: 'emerald',
|
||||
database: 'violet',
|
||||
cloud: 'amber',
|
||||
security: 'rose',
|
||||
messagebus: 'orange',
|
||||
external: 'slate',
|
||||
};
|
||||
|
||||
export function dotForType(type?: string): string {
|
||||
return DOT_BY_TYPE[type ?? ''] ?? 'slate';
|
||||
}
|
||||
|
||||
export interface Card {
|
||||
dot: string;
|
||||
title: string;
|
||||
items: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Archify info cards for registry elements: one per element that has a
|
||||
* description, titled by its label. What a diagram gets below itself when
|
||||
* it draws shared elements and defines no cards of its own.
|
||||
*/
|
||||
export function cardsFromElements(elements: Iterable<RegistryElement>): Card[] {
|
||||
const cards: Card[] = [];
|
||||
for (const element of elements) {
|
||||
if (!element.description) continue;
|
||||
const items = [element.description.trim()];
|
||||
cards.push({ dot: dotForType(element.type), title: element.label ?? element.id, items });
|
||||
}
|
||||
return cards;
|
||||
}
|
||||
|
||||
export function isMainFile(fileName: string): boolean {
|
||||
return (fileName.split(/[\\/]/).pop() ?? '') === MAIN_FILE;
|
||||
}
|
||||
@@ -172,6 +208,8 @@ export interface ResolveOptions {
|
||||
* Expands `ref` components from the registry and strips what Archify does
|
||||
* not know. Component order and indexes are kept, so Archify's diagnostics
|
||||
* (JSON pointers) still point at the right component in the source file.
|
||||
* A diagram without `cards` of its own gets cards from the descriptions of
|
||||
* the shared elements it draws, so the registry text shows under the diagram.
|
||||
*/
|
||||
export function resolveRefs(doc: unknown, options: ResolveOptions): ResolvedDiagram {
|
||||
const problems: ModelProblem[] = [];
|
||||
@@ -229,6 +267,13 @@ export function resolveRefs(doc: unknown, options: ResolveOptions): ResolvedDiag
|
||||
if (link) links.set(id, link);
|
||||
return merged;
|
||||
});
|
||||
if (diagram.cards === undefined && refs.size) {
|
||||
const seen = new Set<string>();
|
||||
const elements: RegistryElement[] = [];
|
||||
for (const element of refs.values()) if (!seen.has(element.id)) { seen.add(element.id); elements.push(element); }
|
||||
const cards = cardsFromElements(elements);
|
||||
if (cards.length) diagram.cards = cards;
|
||||
}
|
||||
return { diagram, problems, refs, links, used };
|
||||
}
|
||||
|
||||
|
||||
+33
-17
@@ -183,6 +183,9 @@ function bridgeScript(options: PageOptions): string {
|
||||
}
|
||||
}, true);
|
||||
|
||||
var DOT_BY_TYPE = { frontend: 'cyan', backend: 'emerald', database: 'violet', cloud: 'amber', security: 'rose', messagebus: 'orange', external: 'slate' };
|
||||
function dotForType(type) { return DOT_BY_TYPE[type || ''] || 'slate'; }
|
||||
|
||||
function el(tag, className, text) {
|
||||
var node = document.createElement(tag);
|
||||
if (className) node.className = className;
|
||||
@@ -223,30 +226,34 @@ function bridgeScript(options: PageOptions): string {
|
||||
});
|
||||
body.appendChild(list);
|
||||
});
|
||||
// Registry elements as Archify info cards: the same cards a diagram shows below itself.
|
||||
if (model.registry.length) {
|
||||
body.appendChild(el('h2', 'archify-model-title', 'Registry · ' + model.registry.length));
|
||||
var table = el('table', 'archify-model-registry');
|
||||
var cards = el('div', 'cards archify-model-cards');
|
||||
model.registry.forEach(function (element) {
|
||||
var row = el('tr');
|
||||
var name = el('td', 'archify-model-element');
|
||||
name.appendChild(el('strong', '', element.label || element.id));
|
||||
name.appendChild(el('span', 'archify-model-item-meta', element.id + (element.type ? ' · ' + element.type : '')));
|
||||
row.appendChild(name);
|
||||
row.appendChild(el('td', 'archify-model-element-description', element.description || ''));
|
||||
var used = el('td', 'archify-model-used');
|
||||
var card = el('div', 'card archify-model-card');
|
||||
var header = el('div', 'card-header');
|
||||
header.appendChild(el('div', 'card-dot ' + dotForType(element.type)));
|
||||
header.appendChild(el('h3', '', element.label || element.id));
|
||||
card.appendChild(header);
|
||||
var list = el('ul');
|
||||
list.appendChild(el('li', 'archify-model-card-id', element.id + (element.type ? ' · ' + element.type : '')));
|
||||
if (element.description) list.appendChild(el('li', '', element.description.trim()));
|
||||
var used = el('li', 'archify-model-card-used');
|
||||
if (element.usedIn.length) {
|
||||
element.usedIn.forEach(function (path) {
|
||||
var link = el('button', 'archify-model-used-link', path.replace(/\.archify$/, ''));
|
||||
link.type = 'button';
|
||||
link.title = path;
|
||||
link.title = 'Open ' + path;
|
||||
link.addEventListener('click', function () { post({ type: 'openDiagram', path: path }); });
|
||||
used.appendChild(link);
|
||||
});
|
||||
} else used.appendChild(el('span', 'archify-model-item-meta', 'not used yet'));
|
||||
row.appendChild(used);
|
||||
table.appendChild(row);
|
||||
list.appendChild(used);
|
||||
card.appendChild(list);
|
||||
cards.appendChild(card);
|
||||
});
|
||||
body.appendChild(table);
|
||||
body.appendChild(cards);
|
||||
}
|
||||
if (anchor && anchor.parentNode) anchor.parentNode.insertBefore(body, anchor.nextSibling); else document.body.appendChild(body);
|
||||
}
|
||||
@@ -290,11 +297,20 @@ const MODEL_STYLE = `
|
||||
.archify-model-item-title { font-weight: 600; font-size: 0.9375rem; }
|
||||
.archify-model-item-meta { font-size: 0.75rem; color: var(--m-muted); font-family: ui-monospace, Menlo, monospace; }
|
||||
.archify-model-empty { color: var(--m-muted); }
|
||||
.archify-model-registry { width: 100%; border-collapse: collapse; font-size: 0.875rem; }
|
||||
.archify-model-registry td { padding: 0.55rem 0.6rem; border-top: 1px solid var(--m-border); vertical-align: top; }
|
||||
.archify-model-element { width: 22%; } .archify-model-element strong { display: block; }
|
||||
.archify-model-element-description { color: var(--m-muted); line-height: 1.45; }
|
||||
.archify-model-used { width: 28%; display: flex; flex-wrap: wrap; gap: 0.3rem; }
|
||||
/* Registry cards: Archify's own .card look, with fallbacks for the page without a diagram. */
|
||||
.archify-model { --m-frontend: var(--frontend-stroke, #22d3ee); --m-backend: var(--backend-stroke, #34d399); --m-database: var(--database-stroke, #a78bfa); --m-cloud: var(--cloud-stroke, #fbbf24); --m-security: var(--security-stroke, #fb7185); --m-messagebus: var(--messagebus-stroke, #fb923c); --m-external: var(--external-stroke, #94a3b8); }
|
||||
.archify-model-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr)); gap: 1rem; margin-top: 0.75rem; }
|
||||
.archify-model-card { background: var(--m-panel); border: 1px solid var(--m-border); border-radius: 0.75rem; padding: 1.125rem 1.25rem; }
|
||||
.archify-model-card .card-header { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.6rem; }
|
||||
.archify-model-card .card-dot { flex: none; width: 10px; height: 10px; border-radius: 50%; }
|
||||
.archify-model-card .card-dot.cyan { background: var(--m-frontend); } .archify-model-card .card-dot.emerald { background: var(--m-backend); } .archify-model-card .card-dot.violet { background: var(--m-database); } .archify-model-card .card-dot.amber { background: var(--m-cloud); } .archify-model-card .card-dot.rose { background: var(--m-security); } .archify-model-card .card-dot.orange { background: var(--m-messagebus); } .archify-model-card .card-dot.slate { background: var(--m-external); }
|
||||
.archify-model-card h3 { margin: 0; font-size: 0.9375rem; font-weight: 600; line-height: 1.4; color: var(--m-text); }
|
||||
.archify-model-card ul { list-style: none; margin: 0; padding: 0; color: var(--m-muted); font-size: 0.8125rem; line-height: 1.6; }
|
||||
.archify-model-card li { margin-bottom: 0.3rem; }
|
||||
.archify-model-card li.archify-model-card-id, .archify-model-card li.archify-model-card-used { padding-left: 0; }
|
||||
.archify-model-card li.archify-model-card-id::before, .archify-model-card li.archify-model-card-used::before { display: none; }
|
||||
.archify-model-card-id { font-family: ui-monospace, Menlo, monospace; font-size: 0.75rem; }
|
||||
.archify-model-card-used { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-top: 0.5rem; }
|
||||
.archify-model-used-link { font: inherit; font-size: 0.75rem; padding: 0.15rem 0.5rem; border-radius: 999px; border: 1px solid var(--m-border); background: transparent; color: var(--m-text); cursor: pointer; font-family: ui-monospace, Menlo, monospace; }
|
||||
.archify-model-used-link:hover { border-color: var(--m-accent); }
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user