@x12i/diagrams 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 x12i
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,42 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
1
+ # @x12i/diagrams
2
+
3
+ Diagram document schema for the Unified UI Kit, plus a text outline for screen
4
+ readers.
5
+
6
+ > **Status: foundation.** This package ships the versioned JSON schema and the
7
+ > outline generator. The node canvas, Mermaid and Excalidraw wrappers are not
8
+ > built yet.
9
+
10
+ ```ts
11
+ import { defineDiagram, diagramToOutline } from "@x12i/diagrams";
12
+
13
+ const diagram = defineDiagram({
14
+ version: 1,
15
+ title: "Order approval",
16
+ nodes: [
17
+ { id: "a", kind: "start", label: "Order placed", position: { x: 0, y: 0 } },
18
+ { id: "b", kind: "task", label: "Review", position: { x: 200, y: 0 } },
19
+ { id: "c", kind: "end", label: "Shipped", position: { x: 400, y: 0 } },
20
+ ],
21
+ edges: [
22
+ { id: "ab", source: "a", target: "b" },
23
+ { id: "bc", source: "b", target: "c" },
24
+ ],
25
+ });
26
+
27
+ diagramToOutline(diagram); // ["Order placed to Review to Shipped"]
28
+ ```
29
+
30
+ Every canvas will generate this outline, so a diagram has a form screen readers
31
+ can read (spec section 10).
32
+
33
+ ## Planned engines
34
+
35
+ | Tier | Engine |
36
+ | --- | --- |
37
+ | Text to diagram | Mermaid (MIT) |
38
+ | Node canvas | `@xyflow/react` (MIT) |
39
+ | Freeform whiteboard | Excalidraw (MIT) |
40
+ | Specialized layouts | D3 hierarchy (ISC) |
41
+
42
+ tldraw is excluded on license grounds.
@@ -0,0 +1,72 @@
1
+ /**
2
+ * The kit's own diagram document (spec section 10). It is versioned JSON, so
3
+ * the node canvas engine can change without breaking saved diagrams.
4
+ */
5
+ declare const DIAGRAM_VERSION = 1;
6
+ /** The node library. Every node reads kit tokens. */
7
+ type NodeKind = "start" | "end" | "task" | "decision" | "group" | "note" | "image" | "entity" | "code" | "chart" | "agent" | "subflow";
8
+ /** Status colors match the rest of the kit: a status means the same in a badge, a chart and a node. */
9
+ type NodeStatus = "neutral" | "success" | "warning" | "danger" | "info";
10
+ type EdgeKind = "straight" | "bezier" | "step" | "smooth-step";
11
+ interface Position {
12
+ x: number;
13
+ y: number;
14
+ }
15
+ interface DiagramNode {
16
+ id: string;
17
+ kind: NodeKind;
18
+ /** The text read aloud in the outline. Pass a translated string. */
19
+ label: string;
20
+ position: Position;
21
+ status?: NodeStatus;
22
+ /** Set on children of a group or subflow. */
23
+ parentId?: string;
24
+ width?: number;
25
+ height?: number;
26
+ /** Typed handles, for connection validation. */
27
+ ports?: Array<{
28
+ id: string;
29
+ direction: "in" | "out";
30
+ type?: string;
31
+ }>;
32
+ /** Free-form payload for custom node bodies, such as a ChartSpec for a chart node. */
33
+ data?: Record<string, unknown>;
34
+ }
35
+ interface DiagramEdge {
36
+ id: string;
37
+ source: string;
38
+ target: string;
39
+ sourcePort?: string;
40
+ targetPort?: string;
41
+ kind?: EdgeKind;
42
+ label?: string;
43
+ /** Draw arrowheads at both ends. */
44
+ bidirectional?: boolean;
45
+ animated?: boolean;
46
+ }
47
+ interface DiagramDocument {
48
+ version: typeof DIAGRAM_VERSION;
49
+ /** Every diagram has a title that states what it shows. */
50
+ title: string;
51
+ nodes: DiagramNode[];
52
+ edges: DiagramEdge[];
53
+ }
54
+ /** Identity helper for authoring-time type checking. */
55
+ declare function defineDiagram(document: DiagramDocument): DiagramDocument;
56
+
57
+ interface OutlineOptions {
58
+ /** Joins labels along a path. Pass a translated word. Defaults to "to". */
59
+ connector?: string;
60
+ /** Stop after this many paths so a dense graph cannot flood the outline. */
61
+ maxPaths?: number;
62
+ }
63
+ /**
64
+ * Generates the text outline that makes a diagram readable by screen readers:
65
+ * one line per path through the graph, such as "Start to Review to Approve".
66
+ * Nodes with no connections get a line of their own, and a path that loops back
67
+ * ends with the node it returns to.
68
+ */
69
+ declare function diagramToOutline(diagram: DiagramDocument, { connector, maxPaths }?: OutlineOptions): string[];
70
+
71
+ export { DIAGRAM_VERSION, defineDiagram, diagramToOutline };
72
+ export type { DiagramDocument, DiagramEdge, DiagramNode, EdgeKind, NodeKind, NodeStatus, OutlineOptions, Position };
package/dist/index.js ADDED
@@ -0,0 +1,46 @@
1
+ // src/schema.ts
2
+ var DIAGRAM_VERSION = 1;
3
+ function defineDiagram(document) {
4
+ return document;
5
+ }
6
+
7
+ // src/outline.ts
8
+ function diagramToOutline(diagram, { connector = "to", maxPaths = 100 } = {}) {
9
+ const labels = new Map(diagram.nodes.map((node) => [node.id, node.label]));
10
+ const outgoing = /* @__PURE__ */ new Map();
11
+ const hasIncoming = /* @__PURE__ */ new Set();
12
+ for (const edge of diagram.edges) {
13
+ if (!labels.has(edge.source) || !labels.has(edge.target)) continue;
14
+ outgoing.set(edge.source, [...outgoing.get(edge.source) ?? [], edge.target]);
15
+ hasIncoming.add(edge.target);
16
+ }
17
+ const lines = [];
18
+ const visited = /* @__PURE__ */ new Set();
19
+ const label = (id) => labels.get(id) ?? id;
20
+ const sentence = (ids) => ids.map(label).join(` ${connector} `);
21
+ const walk = (id, trail) => {
22
+ if (lines.length >= maxPaths) return;
23
+ visited.add(id);
24
+ const path = [...trail, id];
25
+ const next = outgoing.get(id) ?? [];
26
+ if (next.length === 0) {
27
+ lines.push(sentence(path));
28
+ return;
29
+ }
30
+ for (const target of next) {
31
+ if (path.includes(target)) lines.push(sentence([...path, target]));
32
+ else walk(target, path);
33
+ }
34
+ };
35
+ for (const node of diagram.nodes) {
36
+ if (!hasIncoming.has(node.id)) walk(node.id, []);
37
+ }
38
+ for (const node of diagram.nodes) {
39
+ if (!visited.has(node.id)) walk(node.id, []);
40
+ }
41
+ return lines;
42
+ }
43
+
44
+ export { DIAGRAM_VERSION, defineDiagram, diagramToOutline };
45
+ //# sourceMappingURL=index.js.map
46
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/schema.ts","../src/outline.ts"],"names":[],"mappings":";AAKO,IAAM,eAAA,GAAkB;AAkExB,SAAS,cAAc,QAAA,EAA4C;AACxE,EAAA,OAAO,QAAA;AACT;;;AC1DO,SAAS,gBAAA,CACd,SACA,EAAE,SAAA,GAAY,MAAM,QAAA,GAAW,GAAA,EAAI,GAAoB,EAAC,EAC9C;AACV,EAAA,MAAM,MAAA,GAAS,IAAI,GAAA,CAAI,OAAA,CAAQ,MAAM,GAAA,CAAI,CAAC,IAAA,KAAS,CAAC,IAAA,CAAK,EAAA,EAAI,IAAA,CAAK,KAAK,CAAC,CAAC,CAAA;AACzE,EAAA,MAAM,QAAA,uBAAe,GAAA,EAAsB;AAC3C,EAAA,MAAM,WAAA,uBAAkB,GAAA,EAAY;AAEpC,EAAA,KAAA,MAAW,IAAA,IAAQ,QAAQ,KAAA,EAAO;AAChC,IAAA,IAAI,CAAC,MAAA,CAAO,GAAA,CAAI,IAAA,CAAK,MAAM,CAAA,IAAK,CAAC,MAAA,CAAO,GAAA,CAAI,IAAA,CAAK,MAAM,CAAA,EAAG;AAC1D,IAAA,QAAA,CAAS,GAAA,CAAI,IAAA,CAAK,MAAA,EAAQ,CAAC,GAAI,QAAA,CAAS,GAAA,CAAI,IAAA,CAAK,MAAM,CAAA,IAAK,EAAC,EAAI,IAAA,CAAK,MAAM,CAAC,CAAA;AAC7E,IAAA,WAAA,CAAY,GAAA,CAAI,KAAK,MAAM,CAAA;AAAA,EAC7B;AAEA,EAAA,MAAM,QAAkB,EAAC;AACzB,EAAA,MAAM,OAAA,uBAAc,GAAA,EAAY;AAChC,EAAA,MAAM,QAAQ,CAAC,EAAA,KAAe,MAAA,CAAO,GAAA,CAAI,EAAE,CAAA,IAAK,EAAA;AAChD,EAAA,MAAM,QAAA,GAAW,CAAC,GAAA,KAAkB,GAAA,CAAI,GAAA,CAAI,KAAK,CAAA,CAAE,IAAA,CAAK,CAAA,CAAA,EAAI,SAAS,CAAA,CAAA,CAAG,CAAA;AAGxE,EAAA,MAAM,IAAA,GAAO,CAAC,EAAA,EAAY,KAAA,KAAoB;AAC5C,IAAA,IAAI,KAAA,CAAM,UAAU,QAAA,EAAU;AAC9B,IAAA,OAAA,CAAQ,IAAI,EAAE,CAAA;AACd,IAAA,MAAM,IAAA,GAAO,CAAC,GAAG,KAAA,EAAO,EAAE,CAAA;AAC1B,IAAA,MAAM,IAAA,GAAO,QAAA,CAAS,GAAA,CAAI,EAAE,KAAK,EAAC;AAElC,IAAA,IAAI,IAAA,CAAK,WAAW,CAAA,EAAG;AACrB,MAAA,KAAA,CAAM,IAAA,CAAK,QAAA,CAAS,IAAI,CAAC,CAAA;AACzB,MAAA;AAAA,IACF;AAEA,IAAA,KAAA,MAAW,UAAU,IAAA,EAAM;AACzB,MAAA,IAAI,IAAA,CAAK,QAAA,CAAS,MAAM,CAAA,EAAG,KAAA,CAAM,IAAA,CAAK,QAAA,CAAS,CAAC,GAAG,IAAA,EAAM,MAAM,CAAC,CAAC,CAAA;AAAA,WAC5D,IAAA,CAAK,QAAQ,IAAI,CAAA;AAAA,IACxB;AAAA,EACF,CAAA;AAEA,EAAA,KAAA,MAAW,IAAA,IAAQ,QAAQ,KAAA,EAAO;AAChC,IAAA,IAAI,CAAC,WAAA,CAAY,GAAA,CAAI,IAAA,CAAK,EAAE,GAAG,IAAA,CAAK,IAAA,CAAK,EAAA,EAAI,EAAE,CAAA;AAAA,EACjD;AAGA,EAAA,KAAA,MAAW,IAAA,IAAQ,QAAQ,KAAA,EAAO;AAChC,IAAA,IAAI,CAAC,OAAA,CAAQ,GAAA,CAAI,IAAA,CAAK,EAAE,GAAG,IAAA,CAAK,IAAA,CAAK,EAAA,EAAI,EAAE,CAAA;AAAA,EAC7C;AAEA,EAAA,OAAO,KAAA;AACT","file":"index.js","sourcesContent":["/**\r\n * The kit's own diagram document (spec section 10). It is versioned JSON, so\r\n * the node canvas engine can change without breaking saved diagrams.\r\n */\r\n\r\nexport const DIAGRAM_VERSION = 1;\r\n\r\n/** The node library. Every node reads kit tokens. */\r\nexport type NodeKind =\r\n | \"start\"\r\n | \"end\"\r\n | \"task\"\r\n | \"decision\"\r\n | \"group\"\r\n | \"note\"\r\n | \"image\"\r\n | \"entity\"\r\n | \"code\"\r\n | \"chart\"\r\n | \"agent\"\r\n | \"subflow\";\r\n\r\n/** Status colors match the rest of the kit: a status means the same in a badge, a chart and a node. */\r\nexport type NodeStatus = \"neutral\" | \"success\" | \"warning\" | \"danger\" | \"info\";\r\n\r\nexport type EdgeKind = \"straight\" | \"bezier\" | \"step\" | \"smooth-step\";\r\n\r\nexport interface Position {\r\n x: number;\r\n y: number;\r\n}\r\n\r\nexport interface DiagramNode {\r\n id: string;\r\n kind: NodeKind;\r\n /** The text read aloud in the outline. Pass a translated string. */\r\n label: string;\r\n position: Position;\r\n status?: NodeStatus;\r\n /** Set on children of a group or subflow. */\r\n parentId?: string;\r\n width?: number;\r\n height?: number;\r\n /** Typed handles, for connection validation. */\r\n ports?: Array<{ id: string; direction: \"in\" | \"out\"; type?: string }>;\r\n /** Free-form payload for custom node bodies, such as a ChartSpec for a chart node. */\r\n data?: Record<string, unknown>;\r\n}\r\n\r\nexport interface DiagramEdge {\r\n id: string;\r\n source: string;\r\n target: string;\r\n sourcePort?: string;\r\n targetPort?: string;\r\n kind?: EdgeKind;\r\n label?: string;\r\n /** Draw arrowheads at both ends. */\r\n bidirectional?: boolean;\r\n animated?: boolean;\r\n}\r\n\r\nexport interface DiagramDocument {\r\n version: typeof DIAGRAM_VERSION;\r\n /** Every diagram has a title that states what it shows. */\r\n title: string;\r\n nodes: DiagramNode[];\r\n edges: DiagramEdge[];\r\n}\r\n\r\n/** Identity helper for authoring-time type checking. */\r\nexport function defineDiagram(document: DiagramDocument): DiagramDocument {\r\n return document;\r\n}\r\n","import type { DiagramDocument } from \"./schema\";\r\n\r\nexport interface OutlineOptions {\r\n /** Joins labels along a path. Pass a translated word. Defaults to \"to\". */\r\n connector?: string;\r\n /** Stop after this many paths so a dense graph cannot flood the outline. */\r\n maxPaths?: number;\r\n}\r\n\r\n/**\r\n * Generates the text outline that makes a diagram readable by screen readers:\r\n * one line per path through the graph, such as \"Start to Review to Approve\".\r\n * Nodes with no connections get a line of their own, and a path that loops back\r\n * ends with the node it returns to.\r\n */\r\nexport function diagramToOutline(\r\n diagram: DiagramDocument,\r\n { connector = \"to\", maxPaths = 100 }: OutlineOptions = {},\r\n): string[] {\r\n const labels = new Map(diagram.nodes.map((node) => [node.id, node.label]));\r\n const outgoing = new Map<string, string[]>();\r\n const hasIncoming = new Set<string>();\r\n\r\n for (const edge of diagram.edges) {\r\n if (!labels.has(edge.source) || !labels.has(edge.target)) continue;\r\n outgoing.set(edge.source, [...(outgoing.get(edge.source) ?? []), edge.target]);\r\n hasIncoming.add(edge.target);\r\n }\r\n\r\n const lines: string[] = [];\r\n const visited = new Set<string>();\r\n const label = (id: string) => labels.get(id) ?? id;\r\n const sentence = (ids: string[]) => ids.map(label).join(` ${connector} `);\r\n\r\n /** `trail` holds the ids on the path so far, not including `id`. */\r\n const walk = (id: string, trail: string[]) => {\r\n if (lines.length >= maxPaths) return;\r\n visited.add(id);\r\n const path = [...trail, id];\r\n const next = outgoing.get(id) ?? [];\r\n\r\n if (next.length === 0) {\r\n lines.push(sentence(path));\r\n return;\r\n }\r\n\r\n for (const target of next) {\r\n if (path.includes(target)) lines.push(sentence([...path, target]));\r\n else walk(target, path);\r\n }\r\n };\r\n\r\n for (const node of diagram.nodes) {\r\n if (!hasIncoming.has(node.id)) walk(node.id, []);\r\n }\r\n\r\n // Anything left over sits on a cycle with no entry point.\r\n for (const node of diagram.nodes) {\r\n if (!visited.has(node.id)) walk(node.id, []);\r\n }\r\n\r\n return lines;\r\n}\r\n"]}
package/package.json CHANGED
@@ -1,6 +1,37 @@
1
1
  {
2
2
  "name": "@x12i/diagrams",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "1.0.0",
4
+ "description": "Diagram document schema for the Unified UI Kit, plus a text outline for screen readers. The node canvas (on @xyflow/react) is planned.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/x12i/unified-ui-kit.git",
9
+ "directory": "packages/diagrams"
10
+ },
11
+ "homepage": "https://github.com/x12i/unified-ui-kit/tree/main/packages/diagrams#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/x12i/unified-ui-kit/issues"
14
+ },
15
+ "type": "module",
16
+ "sideEffects": false,
17
+ "files": [
18
+ "dist",
19
+ "README.md"
20
+ ],
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/index.d.ts",
24
+ "default": "./dist/index.js"
25
+ },
26
+ "./package.json": "./package.json"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "scripts": {
32
+ "build": "tsup",
33
+ "typecheck": "tsc --noEmit",
34
+ "lint": "eslint .",
35
+ "test": "vitest run --passWithNoTests"
36
+ }
6
37
  }