{"id":45916782,"url":"https://github.com/fidesit/graph-editor","last_synced_at":"2026-03-07T09:14:39.799Z","repository":{"id":341116358,"uuid":"1168973305","full_name":"fidesit/graph-editor","owner":"fidesit","description":"Configuration-driven visual graph editor for Angular 19+","archived":false,"fork":false,"pushed_at":"2026-02-28T07:24:41.000Z","size":302,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-28T08:32:00.495Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fidesit.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-28T02:26:58.000Z","updated_at":"2026-02-28T08:10:19.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/fidesit/graph-editor","commit_stats":null,"previous_names":["fidesit/graph-editor"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/fidesit/graph-editor","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fidesit%2Fgraph-editor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fidesit%2Fgraph-editor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fidesit%2Fgraph-editor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fidesit%2Fgraph-editor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fidesit","download_url":"https://codeload.github.com/fidesit/graph-editor/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fidesit%2Fgraph-editor/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30117471,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-05T08:19:04.902Z","status":"ssl_error","status_checked_at":"2026-03-05T08:17:37.148Z","response_time":93,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-02-28T07:49:31.804Z","updated_at":"2026-03-05T09:01:16.375Z","avatar_url":"https://github.com/fidesit.png","language":"TypeScript","readme":"# @utisha/graph-editor\n\n[![npm version](https://badge.fury.io/js/@utisha%2Fgraph-editor.svg)](https://www.npmjs.com/package/@utisha/graph-editor)\n[![CI](https://github.com/fidesit/graph-editor/actions/workflows/ci.yml/badge.svg)](https://github.com/fidesit/graph-editor/actions/workflows/ci.yml)\n[![Deploy](https://github.com/fidesit/graph-editor/actions/workflows/pages.yml/badge.svg)](https://fidesit.github.io/graph-editor)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz_small.svg)](https://stackblitz.com/github/fidesit/graph-editor)\n\nConfiguration-driven visual graph editor for Angular 19+.\n\n**[Live Demo](https://fidesit.github.io/graph-editor)** | **[Try on StackBlitz](https://stackblitz.com/github/fidesit/graph-editor)**\n\n![Graph Editor Demo](docs/demo.gif)\n\n![Theme Presets](files/themes.png)\n\n## Features\n\n- ⚙️ **Configuration-driven** — No hardcoded domain logic\n- 🎯 **Type-safe** — Full TypeScript support with strict mode\n- 🎭 **Themeable** — Full theme config (canvas, nodes, edges, ports, selection, fonts, toolbar) + CSS custom properties\n- ⌨️ **Keyboard shortcuts** — Delete, arrow keys, escape, undo/redo\n- 📦 **Lightweight** — Only Angular + dagre dependencies\n- 🔌 **Framework-agnostic data** — Works with any backend/state management\n- 🖼️ **Custom node images** — Use images instead of emoji icons\n- 🎨 **Custom SVG icons** — Define your own icon sets with `iconSvg` property\n- ⬜ **Multi-selection** — Box select (Shift+drag) or Ctrl+Click to select multiple items\n- ↩️ **Undo/Redo** — Full history with Ctrl+Z / Ctrl+Y\n- 🔲 **Node resize** — Drag corner handle to resize nodes (Hand tool)\n- 📝 **Text wrapping** — Labels wrap and auto-size to fit within node bounds\n- 🛠️ **Built-in toolbar** — Tools, zoom controls, layout actions in top bar; node palette on left\n- 🧩 **Custom rendering** — ng-template injection for nodes (HTML or SVG) and edges, plus `ngComponentOutlet` support\n- 🔀 **Edge path strategies** — Straight, bezier, and step routing algorithms\n- 📋 **Copy/Paste/Cut** — Ctrl+C/V/X to duplicate or cut selected nodes with their internal edges\n- 📐 **Snap alignment guides** — Visual guide lines when dragging near other nodes' edges or center\n- 🔗 **Drag-to-connect** — Select node to reveal ports, drag from port to create edges\n- 🔵 **Multiple anchor points** — Dynamic port density per side with configurable spacing\n- 🛡️ **Lifecycle hooks** — `beforeNodeAdd`, `beforeNodeRemove`, `beforeEdgeAdd`, `beforeEdgeRemove`, `canConnect` guards that can cancel operations\n\n## Installation\n\n```bash\nnpm install @utisha/graph-editor\n```\n\n## Quick Start\n\n### 1. Import the component\n\n```typescript\nimport { Component, signal } from '@angular/core';\nimport { GraphEditorComponent, Graph, GraphEditorConfig } from '@utisha/graph-editor';\n\n@Component({\n  selector: 'app-my-editor',\n  standalone: true,\n  imports: [GraphEditorComponent],\n  template: `\n    \u003cgraph-editor\n      [config]=\"editorConfig\"\n      [graph]=\"currentGraph()\"\n      (graphChange)=\"onGraphChange($event)\"\n    /\u003e\n  `\n})\nexport class MyEditorComponent {\n  // See configuration below\n}\n```\n\n### 2. Configure the editor\n\n```typescript\neditorConfig: GraphEditorConfig = {\n  nodes: {\n    types: [\n      {\n        type: 'process',\n        label: 'Process',\n        icon: '⚙️',\n        component: null, // Uses default rendering, or provide your own component\n        defaultData: { name: 'New Process' },\n        size: { width: 180, height: 80 }\n      },\n      {\n        type: 'decision',\n        label: 'Decision',\n        icon: '🔀',\n        component: null,\n        defaultData: { name: 'Decision' },\n        size: { width: 180, height: 80 }\n      }\n    ]\n  },\n  edges: {\n    component: null, // Uses default rendering\n    style: {\n      stroke: '#94a3b8',\n      strokeWidth: 2,\n      markerEnd: 'arrow'\n    }\n  },\n  canvas: {\n    grid: {\n      enabled: true,\n      size: 20,\n      snap: true\n    },\n    zoom: {\n      enabled: true,\n      min: 0.25,\n      max: 2.0,\n      wheelEnabled: true\n    },\n    pan: {\n      enabled: true\n    }\n  },\n  palette: {\n    enabled: true,\n    position: 'left'\n  }\n};\n```\n\n### 3. Initialize your graph\n\n```typescript\ncurrentGraph = signal\u003cGraph\u003e({\n  nodes: [\n    { id: '1', type: 'process', data: { name: 'Start' }, position: { x: 100, y: 100 } },\n    { id: '2', type: 'decision', data: { name: 'Check' }, position: { x: 300, y: 100 } }\n  ],\n  edges: [\n    { id: 'e1', source: '1', target: '2' }\n  ]\n});\n\nonGraphChange(graph: Graph): void {\n  this.currentGraph.set(graph);\n  // Save to backend, update state, etc.\n}\n```\n\n## Configuration\n\n### GraphEditorConfig\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `nodes` | `NodesConfig` | Node type definitions + icon position |\n| `edges` | `EdgesConfig` | Edge configuration |\n| `canvas` | `CanvasConfig` | Canvas behavior (grid, zoom, pan) |\n| `validation` | `ValidationConfig` | Validation rules |\n| `palette` | `PaletteConfig` | Node palette configuration |\n| `layout` | `LayoutConfig` | Layout algorithm (dagre, force, tree) |\n| `theme` | `ThemeConfig` | Visual theme (shadows, CSS variables) |\n| `toolbar` | `ToolbarConfig` | Top toolbar visibility and button selection |\n| `hooks` | `LifecycleHooks` | Lifecycle guards to intercept/cancel user actions |\n\n### Node Type Definition\n\n```typescript\ninterface NodeTypeDefinition {\n  type: string;           // Unique identifier\n  label?: string;         // Display name in palette\n  icon?: string;          // Fallback icon (emoji or text)\n  iconSvg?: SvgIconDefinition;  // Professional SVG icon (preferred)\n  component: Type\u003cany\u003e;   // Angular component to render\n  defaultData: Record\u003cstring, any\u003e;\n  size?: { width: number; height: number };\n  ports?: PortConfig;     // Connection ports\n  constraints?: NodeConstraints;\n}\n```\n\n### Custom Node Images\n\nNodes can display custom images instead of emoji icons. Set `imageUrl` in `defaultData` or per-instance in `node.data['imageUrl']`:\n\n```typescript\n// In node type definition (applies to all nodes of this type)\n{\n  type: 'agent',\n  label: 'AI Agent',\n  icon: '🤖',  // Fallback if imageUrl fails to load\n  component: null,\n  defaultData: {\n    name: 'Agent',\n    imageUrl: '/assets/icons/agent.svg'  // Custom image URL\n  }\n}\n\n// Or per-instance (overrides type default)\nconst node: GraphNode = {\n  id: '1',\n  type: 'agent',\n  data: {\n    name: 'Custom Agent',\n    imageUrl: 'https://example.com/custom-icon.png'  // Instance-specific\n  },\n  position: { x: 100, y: 100 }\n};\n```\n\nSupported formats: SVG, PNG, JPG, data URLs, or any valid image URL.\n\n### Custom SVG Icons\n\nDefine your own SVG icons using the `SvgIconDefinition` interface. This allows you to use professional vector icons that match your design system:\n\n```typescript\nimport { SvgIconDefinition, NodeTypeDefinition } from '@utisha/graph-editor';\n\n// Define your icon set\nconst MY_ICONS: Record\u003cstring, SvgIconDefinition\u003e = {\n  process: {\n    viewBox: '0 0 24 24',\n    fill: 'none',\n    stroke: '#6366f1',  // Your brand color\n    strokeWidth: 1.75,\n    path: `M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z\n           M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06...`\n  },\n  decision: {\n    viewBox: '0 0 24 24',\n    fill: 'none',\n    stroke: '#8b5cf6',\n    strokeWidth: 1.75,\n    path: `M12 3L21 12L12 21L3 12L12 3Z\n           M12 8v4\n           M12 16h.01`\n  },\n  start: {\n    viewBox: '0 0 24 24',\n    fill: 'none',\n    stroke: '#22c55e',  // Semantic: green for start\n    strokeWidth: 1.75,\n    path: `M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2...\n           M10 8l6 4-6 4V8Z`\n  }\n};\n\n// Use in node types\nconst nodeTypes: NodeTypeDefinition[] = [\n  { type: 'process', label: 'Process', iconSvg: MY_ICONS.process, component: null, defaultData: { name: 'Process' } },\n  { type: 'decision', label: 'Decision', iconSvg: MY_ICONS.decision, component: null, defaultData: { name: 'Decision' } },\n  { type: 'start', label: 'Start', iconSvg: MY_ICONS.start, component: null, defaultData: { name: 'Start' } },\n];\n```\n\n**Icon priority:** `node.data['imageUrl']` → `nodeType.iconSvg` → `nodeType.defaultData['imageUrl']` → `nodeType.icon` (emoji fallback)\n\n### Canvas Configuration\n\n```typescript\ninterface CanvasConfig {\n  grid?: {\n    enabled: boolean;\n    size: number;      // Grid cell size in pixels\n    snap: boolean;     // Snap nodes to grid\n    color?: string;\n  };\n  zoom?: {\n    enabled: boolean;\n    min: number;       // Minimum zoom level\n    max: number;       // Maximum zoom level\n    step: number;      // Zoom increment\n    wheelEnabled: boolean;\n  };\n  pan?: {\n    enabled: boolean;\n  };\n}\n```\n\n## API\n\n### Inputs\n\n| Input | Type | Description |\n|-------|------|-------------|\n| `config` | `GraphEditorConfig` | Editor configuration (required) |\n| `graph` | `Graph` | Current graph data |\n| `readonly` | `boolean` | Disable editing |\n| `visualizationMode` | `boolean` | Display only mode |\n\n### Outputs\n\n| Output | Type | Description |\n|--------|------|-------------|\n| `graphChange` | `EventEmitter\u003cGraph\u003e` | Emitted on any graph mutation |\n| `nodeClick` | `EventEmitter\u003cGraphNode\u003e` | Node clicked |\n| `nodeDoubleClick` | `EventEmitter\u003cGraphNode\u003e` | Node double-clicked |\n| `edgeClick` | `EventEmitter\u003cGraphEdge\u003e` | Edge clicked |\n| `edgeDoubleClick` | `EventEmitter\u003cGraphEdge\u003e` | Edge double-clicked |\n| `selectionChange` | `EventEmitter\u003cSelectionState\u003e` | Selection changed |\n| `validationChange` | `EventEmitter\u003cValidationResult\u003e` | Validation state changed |\n| `contextMenu` | `EventEmitter\u003cContextMenuEvent\u003e` | Right-click on canvas/node/edge |\n\n### Methods\n\n```typescript\n// Node operations\naddNode(type: string, position?: Position): GraphNode;\nremoveNode(nodeId: string): void;\nupdateNode(nodeId: string, updates: Partial\u003cGraphNode\u003e): void;\n\n// Selection\nselectNode(nodeId: string | null): void;\nselectEdge(edgeId: string | null): void;\nclearSelection(): void;\ngetSelection(): SelectionState;\n\n// Layout\napplyLayout(algorithm?: 'dagre-tb' | 'dagre-lr' | 'force' | 'tree'): Promise\u003cvoid\u003e;\nfitToScreen(padding?: number): void;\nzoomTo(level: number): void;\n\n// Validation\nvalidate(): ValidationResult;\n```\n\n## Theming\n\nCustomize the editor appearance using CSS custom properties:\n\n```css\n:root {\n  --graph-editor-canvas-bg: #f8f9fa;\n  --graph-editor-grid-color: #e0e0e0;\n  --graph-editor-node-bg: #ffffff;\n  --graph-editor-node-border: #cbd5e0;\n  --graph-editor-node-selected: #3b82f6;\n  --graph-editor-edge-stroke: #94a3b8;\n  --graph-editor-edge-selected: #3b82f6;\n}\n```\n\n## Validation\n\nAdd custom validation rules:\n\n```typescript\nconst config: GraphEditorConfig = {\n  // ...\n  validation: {\n    validateOnChange: true,\n    validators: [\n      {\n        id: 'no-orphans',\n        message: 'All nodes must be connected',\n        validator: (graph) =\u003e {\n          const orphans = findOrphanNodes(graph);\n          return orphans.map(node =\u003e ({\n            rule: 'no-orphans',\n            message: `Node \"${node.data.name}\" is not connected`,\n            nodeId: node.id,\n            severity: 'warning'\n          }));\n        }\n      }\n    ]\n  }\n};\n```\n\n## Lifecycle Hooks\n\nLifecycle hooks let you intercept and cancel user-initiated graph mutations.\nConfigure them via the `hooks` property on `GraphEditorConfig`.\n\n\u003e **Note:** Hooks only apply to user-initiated actions (palette add, keyboard delete/cut, drag-to-connect).\n\u003e Programmatic API calls (`addNode()`, `removeNode()`, `removeEdge()`) do **not** trigger hooks.\n\n### Hook Types\n\n| Hook | Sync/Async | When |\n|------|-----------|------|\n| `canConnect` | **Sync** | Every mousemove during drag-to-connect and edge reconnection |\n| `beforeNodeAdd` | Async | Before a node is added via the palette |\n| `beforeNodeRemove` | Async | Before nodes are deleted (Delete/Backspace/Cut) |\n| `beforeEdgeAdd` | Async | Before an edge is created via drag-to-connect |\n| `beforeEdgeRemove` | Async | Before edges are deleted (Delete/Backspace/Cut) |\n\nAll async hooks accept a return type of `boolean | Promise\u003cboolean\u003e`. Returning (or resolving) `false` cancels the operation. If a hook throws an error, the operation is also cancelled.\n\n### Example\n\n```typescript\nimport { GraphEditorConfig, LifecycleHooks } from '@utisha/graph-editor';\n\nconst hooks: LifecycleHooks = {\n  // Sync — called on every mousemove, must return immediately\n  canConnect: (source, target, graph) =\u003e {\n    // No self-loops\n    if (source.nodeId === target.nodeId) return false;\n    // No duplicate edges\n    return !graph.edges.some(\n      e =\u003e e.source === source.nodeId \u0026\u0026 e.target === target.nodeId\n    );\n  },\n\n  // Async — can show confirmation dialogs or call a server\n  beforeNodeRemove: async (nodes, graph) =\u003e {\n    const critical = nodes.filter(n =\u003e n.type === 'start');\n    if (critical.length \u003e 0) {\n      return confirm('Delete the Start node?');\n    }\n    return true;\n  },\n\n  beforeNodeAdd: (type, graph) =\u003e {\n    // Only one Start node allowed\n    if (type === 'start' \u0026\u0026 graph.nodes.some(n =\u003e n.type === 'start')) {\n      return false;\n    }\n    return true;\n  },\n\n  beforeEdgeAdd: (edge, graph) =\u003e true,\n  beforeEdgeRemove: (edges, graph) =\u003e true,\n};\n\nconst config: GraphEditorConfig = {\n  // ...nodes, edges, canvas, etc.\n  hooks\n};\n```\n\n### Demo\n\nThe [live demo](https://fidesit.github.io/graph-editor) includes a **Guards** toggle\nthat enables workflow-level lifecycle hooks with toast notifications:\n\n- `canConnect` — prevents self-loops, duplicate edges, incoming to Start, outgoing from End\n- `beforeNodeAdd` — enforces max 1 Start and 1 End node (with toast notification)\n- `beforeNodeRemove` — shows `confirm()` dialog when deleting Start/End nodes\n- `beforeEdgeAdd` — limits non-Decision nodes to 2 outgoing edges (with toast)\n\nToggle it off to compare unrestricted editing.\n\n## Development\n\n```bash\n# Clone the repository\ngit clone https://github.com/fidesit/graph-editor.git\ncd graph-editor\n\n# Install dependencies\nnpm install\n\n# Build the library\nnpm run build\n\n# Run the demo app\nnpm run start\n\n# Run tests\nnpm test\n```\n\n## Roadmap\n\n### Completed\n\n- [x] ~~Custom node components via `foreignObject`~~ — Template injection + `ngComponentOutlet`\n- [x] ~~Context menus~~ — Event emits on right-click (see demo for example UI)\n- [x] ~~Multi-select~~ — Box selection (Shift+drag) and Ctrl+Click toggle\n- [x] ~~Keyboard shortcuts~~ — Delete, arrows, Escape, Undo/Redo\n- [x] ~~Undo/Redo~~ — Ctrl+Z / Ctrl+Y with full history\n- [x] ~~Custom node images~~ — Use `imageUrl` in node data\n- [x] ~~Custom SVG icons~~ — Define icons with `iconSvg` property\n- [x] ~~Node resize~~ — Drag corner handle with Hand tool\n- [x] ~~Text wrapping~~ — Labels wrap and auto-size within node bounds\n- [x] ~~Comprehensive theming~~ — Full ThemeConfig with 7 sub-interfaces + CSS custom properties bridge\n\n### Interaction \u0026 Editing\n\n- [x] ~~Copy/paste~~ — Ctrl+C/V/X to duplicate or cut nodes with their internal edges\n- [x] ~~Snap guides~~ — Alignment lines when dragging near another node's edge/center (also during resize)\n- [x] ~~Drag-to-connect~~ — Select node to reveal ports, drag from port to connect (replaces line tool)\n- [ ] Edge labels — Clickable, editable text on edges (conditions, weights, transition names)\n- [x] ~~Edge waypoints~~ — Ctrl+click to add draggable waypoints on edges for manual routing bends\n- [ ] Group/collapse — Select multiple nodes and group into a collapsible container node\n\n### Navigation \u0026 Visualization\n\n- [ ] Minimap — Small overview panel with viewport indicator\n- [ ] Search/filter — Ctrl+F to find nodes by label/type, dim non-matching ones\n- [ ] Heatmap overlay — Color nodes by a numeric metric using `overlayData`\n- [ ] Edge animation — Animated dashes flowing along edges to show data direction/activity\n- [ ] Zoom to selection — Double-click a node to zoom and center on it\n\n### Data \u0026 Export\n\n- [ ] Export as image — SVG/PNG export of the current canvas\n- [ ] Import/export JSON — Toolbar button or API to serialize/deserialize the graph\n- [ ] Clipboard integration — Paste graph JSON from clipboard to import subgraphs\n\n### Validation \u0026 Feedback\n\n- [ ] Port-based connections with type checking — Color-coded ports that only accept compatible connections\n- [ ] Inline validation badges — Show error/warning icons directly on offending nodes\n- [ ] Max connections indicator — Show remaining connection slots on ports\n\n### Developer Experience\n\n- [x] ~~Event hooks~~ — `beforeNodeRemove`, `beforeEdgeAdd`, `canConnect` lifecycle guards with async support\n- [ ] Custom toolbar items — Inject custom buttons/components into the toolbar via template projection\n- [ ] Readonly per-node — Lock individual nodes from editing while others remain editable\n- [ ] Touch/mobile support — Pinch-to-zoom, touch drag, long-press for context menu\n- [ ] Accessibility improvements\n\n### Layout\n\n- [x] ~~Multiple layout algorithms~~ — Hierarchical (dagre TB/LR), force-directed, and tree layouts with dropdown switcher\n- [ ] Incremental layout — Re-layout only the neighborhood of a changed node\n- [ ] Swim lanes — Horizontal/vertical partitions that nodes snap into\n\n## Contributing\n\nContributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details.\n\n## License\n\n[MIT](LICENSE) © Utisha / Fides IT\n","funding_links":[],"categories":["Third Party Components"],"sub_categories":["Charts"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffidesit%2Fgraph-editor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffidesit%2Fgraph-editor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffidesit%2Fgraph-editor/lists"}