infrahub-schema-visualizer 0.0.1

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.
@@ -0,0 +1,12 @@
1
+ export { BottomToolbar, type BottomToolbarProps } from "./BottomToolbar";
2
+ export { FilterPanel, type FilterPanelProps } from "./FilterPanel";
3
+ export { LegendPanel } from "./LegendPanel";
4
+ export {
5
+ NodeDetailsPanel,
6
+ type NodeDetailsPanelProps,
7
+ } from "./NodeDetailsPanel";
8
+ export { SchemaNode } from "./SchemaNode";
9
+ export {
10
+ SchemaVisualizer,
11
+ type SchemaVisualizerProps,
12
+ } from "./SchemaVisualizer";
@@ -0,0 +1 @@
1
+ export * from "./schema";
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Schema types for the schema visualizer component.
3
+ * These types are designed to be framework-agnostic so they can be used
4
+ * by external tools that integrate with Infrahub.
5
+ */
6
+
7
+ export interface AttributeSchema {
8
+ id?: string | null;
9
+ name: string;
10
+ kind: string;
11
+ label?: string | null;
12
+ description?: string | null;
13
+ optional?: boolean;
14
+ unique?: boolean;
15
+ read_only?: boolean;
16
+ inherited?: boolean;
17
+ }
18
+
19
+ export interface RelationshipSchema {
20
+ id?: string | null;
21
+ name: string;
22
+ peer: string;
23
+ kind: string;
24
+ label?: string | null;
25
+ description?: string | null;
26
+ identifier?: string | null;
27
+ cardinality: "one" | "many";
28
+ optional?: boolean;
29
+ inherited?: boolean;
30
+ min_count?: number;
31
+ max_count?: number;
32
+ }
33
+
34
+ export interface BaseSchema {
35
+ id?: string | null;
36
+ name: string;
37
+ namespace: string;
38
+ kind?: string | null;
39
+ label?: string | null;
40
+ description?: string | null;
41
+ icon?: string | null;
42
+ attributes?: AttributeSchema[];
43
+ relationships?: RelationshipSchema[];
44
+ }
45
+
46
+ export interface NodeSchema extends BaseSchema {
47
+ inherit_from?: string[];
48
+ hierarchy?: string | null;
49
+ parent?: string | null;
50
+ children?: string | null;
51
+ }
52
+
53
+ export interface GenericSchema extends BaseSchema {
54
+ used_by?: string[];
55
+ hierarchical?: boolean;
56
+ }
57
+
58
+ export interface ProfileSchema extends BaseSchema {
59
+ profile_priority?: number;
60
+ }
61
+
62
+ export interface TemplateSchema extends BaseSchema {
63
+ inherit_from?: string[];
64
+ }
65
+
66
+ export type SchemaType = "node" | "generic" | "profile" | "template";
67
+
68
+ export interface SchemaVisualizerData {
69
+ nodes: NodeSchema[];
70
+ generics: GenericSchema[];
71
+ profiles?: ProfileSchema[];
72
+ templates?: TemplateSchema[];
73
+ }
@@ -0,0 +1,6 @@
1
+ import { type ClassValue, clsx } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }
@@ -0,0 +1,20 @@
1
+ export { cn } from "./cn";
2
+ export { getLayoutedElements, type LayoutOptions } from "./layout";
3
+ export {
4
+ clearPersistedState,
5
+ hasPersistedState,
6
+ loadPersistedState,
7
+ type NodePosition,
8
+ type PersistedState,
9
+ savePersistedState,
10
+ } from "./persistence";
11
+ export {
12
+ applyNamespaceLayout,
13
+ getSchemaKind,
14
+ groupByNamespace,
15
+ type SchemaFlowData,
16
+ type SchemaFlowNode,
17
+ type SchemaNodeData,
18
+ schemaToFlow,
19
+ schemaToFlowFiltered,
20
+ } from "./schema-to-flow";
@@ -0,0 +1,60 @@
1
+ import Dagre from "@dagrejs/dagre";
2
+ import type { Edge, Node } from "@xyflow/react";
3
+
4
+ export interface LayoutOptions {
5
+ direction?: "TB" | "BT" | "LR" | "RL";
6
+ nodeWidth?: number;
7
+ nodeHeight?: number;
8
+ rankSep?: number;
9
+ nodeSep?: number;
10
+ }
11
+
12
+ export function getLayoutedElements(
13
+ nodes: Node[],
14
+ edges: Edge[],
15
+ options: LayoutOptions = {},
16
+ ): { nodes: Node[]; edges: Edge[] } {
17
+ const {
18
+ direction = "LR",
19
+ nodeWidth = 300,
20
+ nodeHeight = 200,
21
+ rankSep = 100,
22
+ nodeSep = 50,
23
+ } = options;
24
+
25
+ const g = new Dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({}));
26
+
27
+ g.setGraph({
28
+ rankdir: direction,
29
+ ranksep: rankSep,
30
+ nodesep: nodeSep,
31
+ });
32
+
33
+ for (const node of nodes) {
34
+ const width = node.measured?.width ?? nodeWidth;
35
+ const height = node.measured?.height ?? nodeHeight;
36
+ g.setNode(node.id, { width, height });
37
+ }
38
+
39
+ for (const edge of edges) {
40
+ g.setEdge(edge.source, edge.target);
41
+ }
42
+
43
+ Dagre.layout(g);
44
+
45
+ const layoutedNodes = nodes.map((node) => {
46
+ const nodeWithPosition = g.node(node.id);
47
+ const width = node.measured?.width ?? nodeWidth;
48
+ const height = node.measured?.height ?? nodeHeight;
49
+
50
+ return {
51
+ ...node,
52
+ position: {
53
+ x: nodeWithPosition.x - width / 2,
54
+ y: nodeWithPosition.y - height / 2,
55
+ },
56
+ };
57
+ });
58
+
59
+ return { nodes: layoutedNodes, edges };
60
+ }
@@ -0,0 +1,69 @@
1
+ import type { EdgeStyle } from "../components/BottomToolbar";
2
+
3
+ const STORAGE_KEY = "schema-visualizer-state";
4
+
5
+ export interface NodePosition {
6
+ id: string;
7
+ x: number;
8
+ y: number;
9
+ }
10
+
11
+ export interface PersistedState {
12
+ hiddenNodes: string[];
13
+ edgeStyle: EdgeStyle;
14
+ nodePositions: NodePosition[];
15
+ version: number;
16
+ }
17
+
18
+ const CURRENT_VERSION = 1;
19
+
20
+ export function loadPersistedState(): PersistedState | null {
21
+ try {
22
+ const stored = localStorage.getItem(STORAGE_KEY);
23
+ if (!stored) return null;
24
+
25
+ const parsed = JSON.parse(stored) as PersistedState;
26
+
27
+ // Reset on version mismatch - no migrations needed
28
+ if (parsed.version !== CURRENT_VERSION) {
29
+ localStorage.removeItem(STORAGE_KEY);
30
+ return null;
31
+ }
32
+
33
+ return parsed;
34
+ } catch {
35
+ // On any error, clear storage and return null
36
+ localStorage.removeItem(STORAGE_KEY);
37
+ return null;
38
+ }
39
+ }
40
+
41
+ export function savePersistedState(
42
+ state: Omit<PersistedState, "version">,
43
+ ): void {
44
+ try {
45
+ const toStore: PersistedState = {
46
+ ...state,
47
+ version: CURRENT_VERSION,
48
+ };
49
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(toStore));
50
+ } catch {
51
+ // Silently fail if localStorage is not available
52
+ }
53
+ }
54
+
55
+ export function clearPersistedState(): void {
56
+ try {
57
+ localStorage.removeItem(STORAGE_KEY);
58
+ } catch {
59
+ // Silently fail if localStorage is not available
60
+ }
61
+ }
62
+
63
+ export function hasPersistedState(): boolean {
64
+ try {
65
+ return localStorage.getItem(STORAGE_KEY) !== null;
66
+ } catch {
67
+ return false;
68
+ }
69
+ }