Restructure to skill-manager conventions

- 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>
This commit is contained in:
2026-07-27 15:20:26 +03:00
co-authored by Claude Fable 5
parent 99f1fd07e3
commit 77655d1d97
72 changed files with 1089 additions and 1828 deletions
@@ -0,0 +1,72 @@
/**
* dom-polyfill.ts
*
* Sets up (or extends) the global DOM environment required by @maxgraph/core
* when running in Node.js. Safe to import multiple times — idempotent.
*
* The parser (drawio-parser/parser.ts) already sets up a partial DOM polyfill
* for the basic maxGraph model/serializer classes. This module extends it with
* the additional globals needed by Graph+GraphView (rendering pipeline):
* - location
* - SVGElement, MouseEvent, KeyboardEvent, TouchEvent
* - requestAnimationFrame / cancelAnimationFrame
*
* Must be imported before any maxGraph Graph/GraphView usage.
*/
import { JSDOM } from "jsdom";
// Use the same DOM that may already be set up by parser.ts, or create a new one.
// We detect by checking whether global.document already exists.
let win: Record<string, unknown>;
if ((globalThis as Record<string, unknown>)["document"]) {
// Parser already set up a DOM — re-use the existing window
win = (globalThis as Record<string, unknown>)["window"] as Record<string, unknown>;
} else {
const dom = new JSDOM(
'<!DOCTYPE html><html><body></body></html>',
{ pretendToBeVisual: true }
);
win = dom.window as unknown as Record<string, unknown>;
(globalThis as Record<string, unknown>)["document"] = win["document"];
(globalThis as Record<string, unknown>)["window"] = win;
(globalThis as Record<string, unknown>)["DOMParser"] = win["DOMParser"];
(globalThis as Record<string, unknown>)["XMLSerializer"] = win["XMLSerializer"];
(globalThis as Record<string, unknown>)["HTMLElement"] = win["HTMLElement"];
(globalThis as Record<string, unknown>)["Element"] = win["Element"];
(globalThis as Record<string, unknown>)["Node"] = win["Node"];
(globalThis as Record<string, unknown>)["Event"] = win["Event"];
try {
Object.defineProperty(globalThis, "navigator", {
value: win["navigator"],
writable: true,
configurable: true,
});
} catch { /* already defined */ }
}
// Add/overwrite globals needed specifically for Graph+GraphView rendering
const g = globalThis as Record<string, unknown>;
if (!g["SVGElement"]) g["SVGElement"] = win["SVGElement"] ?? class SVGElement {};
if (!g["MouseEvent"]) g["MouseEvent"] = win["MouseEvent"] ?? class MouseEvent {};
if (!g["KeyboardEvent"]) g["KeyboardEvent"] = win["KeyboardEvent"] ?? class KeyboardEvent {};
if (!g["TouchEvent"]) g["TouchEvent"] = win["TouchEvent"] ?? class TouchEvent {};
if (!g["requestAnimationFrame"]) {
g["requestAnimationFrame"] = (fn: () => void) => setTimeout(fn, 0);
}
if (!g["cancelAnimationFrame"]) {
g["cancelAnimationFrame"] = clearTimeout;
}
// location is needed by UrlConverter (used when rendering image shapes)
if (!g["location"]) {
g["location"] = {
protocol: "http:",
host: "localhost",
href: "http://localhost/",
pathname: "/",
};
}
export {};