Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
"repository": "https://github.com/MakingChatbots/genesys-cloud-architect",
"license": "MIT",
"keywords": ["genesys cloud", "architect", "flows", "ivr", "contact-center", "ccaas"],
"mcpServers": "./.mcp.json"
"mcpServers": "./.mcp.json",
"skills": "./skills/"
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ dist/
.env*
skills-lock.json
.agents/
.pnpm-store
189 changes: 145 additions & 44 deletions servers/genesys-cloud-architect-mcp.js

Large diffs are not rendered by default.

83 changes: 83 additions & 0 deletions skills/flow-diagram/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
---
name: flow-diagram
description: This skill should be used when the user asks to create an interactive diagram, flowchart, flow visualization, dependency tree, architecture map, state machine, or pipeline view. Common trigger phrases include "visualize", "diagram", "flowchart", "graph", "map out", "show the flow", "draw the dependencies", "create a diagram", or any request where a visual representation of nodes and connections would communicate better than text.
---

# Flow Diagram Skill

Create interactive flow diagrams as standalone HTML files using React Flow (@xyflow/react). The diagrams are self-contained (no build step, no npm install), use a dark theme, and open directly in a browser.

## When to Use

Generate a React Flow diagram when:
- Displaying hierarchical relationships (dependency trees, org charts, call chains)
- Showing data/control flow between components (pipelines, workflows, request paths)
- Visualizing state machines or decision trees
- Mapping architecture (service dependencies, module relationships)
- Any scenario where a graph of connected nodes communicates better than a table or list

Prefer plain text (ASCII, markdown table) when the graph has fewer than 3 nodes or no edges. For large diagrams (30+ nodes), consider grouping related nodes into composite cards or splitting into multiple diagrams — browser rendering remains smooth up to ~100 nodes, but readability degrades well before that.

## How to Create a Diagram

### 1. Start from the template

Copy `assets/template.html` to the target location. The template contains the tested import map, dark-theme CSS, and React Flow scaffolding. Do not modify the import map URLs or `?external` parameters — they are calibrated to avoid duplicate-React issues.

### 2. Define custom node types

Create node components using `createElement` (aliased as `h`). Every node needs `Handle` components for connections. Consult `references/patterns.md` for ready-made node designs:
- **Card with sub-items** — for nodes with child lists (e.g., a flow listing its data actions)
- **Simple labeled node** — for minimal states or pipeline steps
- **Status node** — for nodes with a health/status indicator

Register node types in a `nodeTypes` object **outside the App component**.

### 3. Define data, nodes, and edges

Build the `initialNodes` and `initialEdges` arrays from the data being visualized.

Each node needs: `id`, `type` (matching a key in `nodeTypes`), `position: { x, y }`, and `data` (props passed to the node component).

Each edge needs: `id`, `source` (node id), `target` (node id). Optional: `animated`, `label`, `type` (`'smoothstep'` for flowcharts, default bezier for trees).

### 4. Layout the nodes

For manual layout strategies (trees, grids, multi-level), see `references/patterns.md` under "Layout Strategies". Use `fitView` on the ReactFlow component to auto-zoom.

### 5. Add title, legend, and type-specific CSS

Use the `.diagram-title` and `.legend` overlay classes from the template. Add type badge CSS using the accent color pattern from `references/patterns.md`.

### 6. Replace template placeholders

The template has `%%PLACEHOLDER%%` comments marking where to insert content:
- `%%TITLE%%` — page title
- `%%CUSTOM_STYLES%%` — type badge classes and additional CSS
- `%%NODE_TYPES%%` — custom node components and `nodeTypes` object
- `%%DATA%%` — the `initialNodes` and `initialEdges` arrays
- `%%LAYOUT%%` — (already part of data if positions are inline)
- `%%TITLE_OVERLAY%%` — title createElement call
- `%%LEGEND%%` — legend createElement call

### 7. Open in browser

The resulting HTML file opens directly in any modern browser. No server needed.

## Key Constraints

- **No JSX** — use `createElement` (aliased `h`). No Babel, no build step.
- **No additional CDN dependencies** — the import map in the template is sufficient.
- **`nodeTypes` must be defined outside components** — React Flow remounts nodes if the object identity changes per render.
- **Dark theme only** — the CSS is designed for the `#0f172a` background. Changing to light theme requires restyling all components.

## Additional Resources

### Reference Files
- **`references/patterns.md`** — Import map details, createElement syntax, layout strategies (tree/grid/multi-level), edge types, color palette, and ready-made node component patterns

### Asset Files
- **`assets/template.html`** — Base HTML template with the working import map, dark-theme CSS, and React Flow scaffolding. Copy this as the starting point for every diagram.

### Example Files
- **`examples/dependency-tree.html`** — Complete working diagram showing a Genesys Cloud Architect flow dependency tree with 3 nodes, custom card nodes with sub-item lists, animated edges, title overlay, and legend. Use as a reference for the expected end result.
166 changes: 166 additions & 0 deletions skills/flow-diagram/assets/template.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>%%TITLE%%</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@xyflow/react@12/dist/style.css">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body, #root { width: 100%; height: 100%; }
body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #0f172a; }

/* --- Base node card --- */
.node-card {
background: #1e293b;
border-radius: 12px;
padding: 16px 18px;
min-width: 180px;
max-width: 280px;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4);
border: 1px solid #334155;
transition: box-shadow 0.2s, border-color 0.2s;
}
.node-card:hover {
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.6);
border-color: #475569;
}

/* --- Type badge --- */
.node-card .type-badge {
display: inline-block;
font-size: 9px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
padding: 3px 8px;
border-radius: 6px;
margin-bottom: 10px;
}

/* --- Node title --- */
.node-card .node-title {
font-size: 13px;
font-weight: 600;
color: #f1f5f9;
line-height: 1.4;
margin-bottom: 8px;
}

/* --- Section label --- */
.node-card .section-label {
font-size: 9px;
font-weight: 700;
text-transform: uppercase;
color: #64748b;
letter-spacing: 0.08em;
margin-bottom: 6px;
}

/* --- List item with left accent --- */
.node-card .list-item {
font-size: 11px;
color: #cbd5e1;
padding: 4px 8px;
margin-bottom: 3px;
background: rgba(255, 255, 255, 0.03);
border-radius: 4px;
border-left: 2px solid #f59e0b;
}

/* --- Muted text --- */
.node-card .muted { font-size: 11px; color: #475569; font-style: italic; }

/* --- React Flow overrides (dark theme) --- */
.react-flow__edge-path { stroke: #475569 !important; stroke-width: 2px; }
.react-flow__edge.animated path { stroke: #60a5fa !important; }
.react-flow__attribution { display: none; }
.react-flow__controls { background: #1e293b !important; border: 1px solid #334155 !important; border-radius: 8px !important; overflow: hidden; }
.react-flow__controls button { background: #1e293b !important; color: #94a3b8 !important; border-color: #334155 !important; }
.react-flow__controls button:hover { background: #334155 !important; }
.react-flow__controls button svg { fill: #94a3b8 !important; }
.react-flow__minimap { background: #0f172a !important; border: 1px solid #334155 !important; border-radius: 8px !important; }

/* --- Title overlay --- */
.diagram-title {
position: absolute; top: 16px; left: 16px; z-index: 10;
color: #f1f5f9; font-size: 16px; font-weight: 700;
background: rgba(15, 23, 42, 0.8); backdrop-filter: blur(8px);
padding: 10px 16px; border-radius: 10px; border: 1px solid #334155;
}
.diagram-title .subtitle { font-size: 11px; font-weight: 400; color: #64748b; margin-top: 2px; }

/* --- Legend overlay --- */
.legend {
position: absolute; bottom: 16px; left: 16px; z-index: 10;
background: rgba(15, 23, 42, 0.8); backdrop-filter: blur(8px);
padding: 12px 16px; border-radius: 10px; border: 1px solid #334155;
display: flex; gap: 16px;
}
.legend-item { display: flex; align-items: center; gap: 6px; font-size: 11px; color: #94a3b8; }
.legend-dot { width: 10px; height: 10px; border-radius: 50%; }

/* %%CUSTOM_STYLES%% */
</style>

<script type="importmap">
{
"imports": {
"react": "https://esm.sh/react@18.3.1",
"react/": "https://esm.sh/react@18.3.1/",
"react-dom": "https://esm.sh/react-dom@18.3.1?external=react",
"react-dom/client": "https://esm.sh/react-dom@18.3.1/client?external=react",
"@xyflow/react": "https://esm.sh/@xyflow/react@12?external=react,react-dom"
}
}
</script>
</head>
<body>
<div id="root"></div>

<script type="module">
import React, { createElement as h } from 'react';
import { createRoot } from 'react-dom/client';
import {
ReactFlow,
Background,
Controls,
MiniMap,
Handle,
Position,
useNodesState,
useEdgesState,
} from '@xyflow/react';

// %%NODE_TYPES%%
// %%DATA%%
// %%LAYOUT%%

function App() {
const [nodes, , onNodesChange] = useNodesState(initialNodes);
const [edges, , onEdgesChange] = useEdgesState(initialEdges);

return h('div', { style: { width: '100%', height: '100%', position: 'relative' } },
// %%TITLE_OVERLAY%%
h(ReactFlow, {
nodes, edges, onNodesChange, onEdgesChange, nodeTypes,
fitView: true,
fitViewOptions: { padding: 0.3 },
minZoom: 0.3, maxZoom: 2,
proOptions: { hideAttribution: true },
},
h(Background, { color: '#1e293b', gap: 20, size: 1 }),
h(Controls, null),
h(MiniMap, {
nodeColor: () => '#64748b',
maskColor: 'rgba(15, 23, 42, 0.8)',
}),
),
// %%LEGEND%%
);
}

createRoot(document.getElementById('root')).render(h(App));
</script>
</body>
</html>
Loading
Loading