feat: introduce archify skill for generating architecture diagrams
- Added a new Archify skill, enabling users to create polished architecture, workflow, sequence, data-flow, and lifecycle diagrams. - Implemented comprehensive functionality including rendering, validation, and delivery of diagrams in various formats. - Integrated a user-friendly command-line interface for generating and previewing diagrams. - Developed supporting files including package.json, LICENSE, and SKILL.md for documentation and licensing. - Added unit tests to ensure reliability and functionality of the new skill. These changes enhance the application by providing a structured approach to visualizing system architecture and workflows, improving user experience and data representation.
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
/** Grid placement for architecture IR (#8). Not auto-layout — fixed cell math only. */
|
||||
|
||||
export const DEFAULT_GRID = {
|
||||
mode: 'grid',
|
||||
origin: [40, 80],
|
||||
cols: 4,
|
||||
gapX: 30,
|
||||
gapY: 40,
|
||||
cellW: 130,
|
||||
cellH: 64,
|
||||
};
|
||||
|
||||
export function gridLayout(arch) {
|
||||
const raw = arch.layout;
|
||||
if (!raw || raw.mode !== 'grid') return null;
|
||||
return { ...DEFAULT_GRID, ...raw };
|
||||
}
|
||||
|
||||
export function resolveComponentPos(component, grid) {
|
||||
if (Array.isArray(component.pos) && component.pos.length === 2) {
|
||||
return component.pos;
|
||||
}
|
||||
if (!grid) return [NaN, NaN];
|
||||
if (!Number.isInteger(component.row) || !Number.isInteger(component.col)) {
|
||||
return [NaN, NaN];
|
||||
}
|
||||
const [ox, oy] = grid.origin;
|
||||
const stepX = grid.cellW + grid.gapX;
|
||||
const stepY = grid.cellH + grid.gapY;
|
||||
return [ox + component.col * stepX, oy + component.row * stepY];
|
||||
}
|
||||
|
||||
export function validateGridPlacement(arch, grid, problems) {
|
||||
if (!grid) return;
|
||||
if (arch.layout !== undefined && arch.layout.mode !== 'grid') {
|
||||
problems.push('layout.mode must be "grid" when layout is set (free placement omits layout entirely).');
|
||||
return;
|
||||
}
|
||||
const seen = new Map();
|
||||
for (const c of arch.components ?? []) {
|
||||
const hasPos = Array.isArray(c.pos) && c.pos.length === 2;
|
||||
const hasCell = Number.isInteger(c.row) && Number.isInteger(c.col);
|
||||
if (hasPos) continue; // pos wins; row/col are optional hints only
|
||||
if (!hasPos && !hasCell) {
|
||||
problems.push(`Component "${c.id}" needs pos [x,y] or grid row/col when layout.mode is "grid".`);
|
||||
continue;
|
||||
}
|
||||
if (c.row < 0 || c.col < 0) {
|
||||
problems.push(`Component "${c.id}" row/col must be non-negative integers.`);
|
||||
continue;
|
||||
}
|
||||
if (c.col >= grid.cols) {
|
||||
problems.push(`Component "${c.id}" col ${c.col} exceeds layout.cols ${grid.cols} (valid: 0..${grid.cols - 1}).`);
|
||||
}
|
||||
const key = `${c.row},${c.col}`;
|
||||
if (seen.has(key)) {
|
||||
problems.push(`Components "${seen.get(key)}" and "${c.id}" share grid cell row ${c.row} col ${c.col}.`);
|
||||
} else {
|
||||
seen.set(key, c.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user