- Move the TypeScript project under scripts/ (src, tsconfig, package.json, pnpm lockfile/workspace, canonical .gitignore); drop the npm package-lock - Add scripts/Taskfile.yml aggregator plus .scripts modules (loggers, base, cli) with build, run, and validate tasks - Move the six SKILL-*.md docs into references/ with kebab names and extract the connector-routing sections from SKILL.md into references/routing-best-practices.md (SKILL.md 666 -> ~310 lines) - Add license/metadata/compatibility frontmatter, an Available scripts section, and update all CLI paths in README and references skill-manager validate: 13/13 passed, 0 warnings. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
101 lines
2.7 KiB
TypeScript
101 lines
2.7 KiB
TypeScript
/**
|
|
* Action: page-hierarchy-full
|
|
*
|
|
* Extends page-hierarchy with full geometry for every shape at each nesting level.
|
|
*
|
|
* Reuses the shared hierarchy-builder service (same logic as page-hierarchy).
|
|
*
|
|
* "Levels" = containment depth levels in the shape hierarchy (diagramming term):
|
|
* level 1 (depth 0) = root-level shapes (parentId === "1")
|
|
* level 2 (depth 1) = shapes whose parent is at level 1
|
|
* level N (depth N-1) = shapes nested N-1 levels deep
|
|
*
|
|
* Output structure:
|
|
* summary:
|
|
* levelsTotal — number of distinct nesting levels
|
|
* shapesTotal — total shape count
|
|
* shapesPerLevel — { levelNumber: count }
|
|
* levels:
|
|
* - number — 1-based nesting level (1 = root, 2 = children, …)
|
|
* shapeCount
|
|
* shapes:
|
|
* - id, label, x, y, width, height, parentId
|
|
*
|
|
* IMPORTANT: Parser returns absolute canvas coordinates for all shapes.
|
|
*/
|
|
|
|
import { parseAllPages } from "../../services/drawio-parser/parser.js";
|
|
import { buildHierarchy } from "../../services/hierarchy-builder/hierarchy-builder.js";
|
|
import type { Shape } from "../../services/drawio-parser/parser.js";
|
|
|
|
interface ShapeEntry {
|
|
id: string;
|
|
label: string;
|
|
x: number;
|
|
y: number;
|
|
width: number;
|
|
height: number;
|
|
parentId: string;
|
|
}
|
|
|
|
interface NestingLevel {
|
|
number: number;
|
|
shapeCount: number;
|
|
shapes: ShapeEntry[];
|
|
}
|
|
|
|
export function run(
|
|
filePath: string,
|
|
pageIndex: number = 0
|
|
): Record<string, unknown> {
|
|
const pages = parseAllPages(filePath);
|
|
const page = pages[pageIndex];
|
|
if (!page) {
|
|
return { error: true, message: `Page index ${pageIndex} not found` };
|
|
}
|
|
|
|
// Reuse shared hierarchy builder
|
|
const { depthMap, maxDepth, totalLevels } = buildHierarchy(page);
|
|
|
|
const allShapes = Array.from(page.shapes.values());
|
|
|
|
// Group shapes by depth
|
|
const levelMap = new Map<number, Shape[]>();
|
|
for (const s of allShapes) {
|
|
const d = depthMap.get(s.id);
|
|
if (d === undefined) continue;
|
|
if (!levelMap.has(d)) levelMap.set(d, []);
|
|
levelMap.get(d)!.push(s);
|
|
}
|
|
|
|
const levels: NestingLevel[] = [];
|
|
for (let d = 0; d <= maxDepth; d++) {
|
|
const shapes = (levelMap.get(d) ?? []).sort((a, b) => a.y - b.y || a.x - b.x);
|
|
levels.push({
|
|
number: d + 1,
|
|
shapeCount: shapes.length,
|
|
shapes: shapes.map((s) => ({
|
|
id: s.id,
|
|
label: s.label.replace(/\n/g, " / "),
|
|
x: s.x,
|
|
y: s.y,
|
|
width: s.width,
|
|
height: s.height,
|
|
parentId: s.parentId,
|
|
})),
|
|
});
|
|
}
|
|
|
|
return {
|
|
action: "page-hierarchy-full",
|
|
file: filePath,
|
|
pageIndex,
|
|
summary: {
|
|
levelsTotal: totalLevels,
|
|
shapesTotal: allShapes.length,
|
|
shapesPerLevel: Object.fromEntries(levels.map((l) => [l.number, l.shapeCount])),
|
|
},
|
|
levels,
|
|
};
|
|
}
|