emberflow-skills 1.4.0 → 1.7.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/.claude/skills/ember-publish/SKILL.md +93 -0
- package/.claude/skills/ember-publish-doc/SKILL.md +197 -0
- package/.claude/skills/ember-publish-explainer/SKILL.md +402 -0
- package/.claude/skills/ember-publish-explainer/templates/architecture-explainer.json +40 -0
- package/.claude/skills/ember-publish-explainer/templates/dashboard-explainer.json +33 -0
- package/.claude/skills/ember-publish-explainer/templates/project-overview-explainer.json +40 -0
- package/.claude/skills/ember-publish-json/SKILL.md +118 -0
- package/.claude/skills/ember-publish-space/SKILL.md +211 -0
- package/bin/install.js +25 -5
- package/package.json +1 -1
- package/skills/ember-publish/SKILL.md +6 -3
- package/skills/ember-publish-explainer/SKILL.md +402 -0
- package/skills/ember-publish-explainer/templates/architecture-explainer.json +40 -0
- package/skills/ember-publish-explainer/templates/dashboard-explainer.json +33 -0
- package/skills/ember-publish-explainer/templates/project-overview-explainer.json +40 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ember-publish
|
|
3
|
+
description: Publish content to Emberflow — automatically picks the right format (document, JSON explorer, or Space) based on your content
|
|
4
|
+
argument-hint: [topic, file path, directory, or description of what to publish]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Emberflow Publisher
|
|
8
|
+
|
|
9
|
+
Publish content to Emberflow at **https://emberflow.ai**. This skill automatically determines the best format based on what you're publishing:
|
|
10
|
+
|
|
11
|
+
| Content | Published as | Equivalent skill |
|
|
12
|
+
|---------|-------------|-----------------|
|
|
13
|
+
| A topic or markdown description | Markdown document with Mermaid diagrams | `/ember-publish-doc` |
|
|
14
|
+
| JSON data or a `.json` file | Interactive JSON explorer with tree + graph | `/ember-publish-json` |
|
|
15
|
+
| A directory of `.md` files | Multi-page docs site (Space) with sidebar nav | `/ember-publish-space` |
|
|
16
|
+
| An interactive visual explanation of a concept | Interactive slide-based HTML explainer | `/ember-publish-explainer` |
|
|
17
|
+
|
|
18
|
+
## How to Decide
|
|
19
|
+
|
|
20
|
+
1. **If the user asks for an interactive explainer**, visual walkthrough, animated diagram, or slide-based explanation → use the `/ember-publish-explainer` workflow
|
|
21
|
+
2. **If the user provides a directory path** or mentions "docs site", "space", or "multi-page" → use the `/ember-publish-space` workflow
|
|
22
|
+
3. **If the user provides a `.json` file**, JSON data, or asks to publish JSON/API responses → use the `/ember-publish-json` workflow
|
|
23
|
+
4. **Otherwise** (topic description, markdown file, or general documentation request) → use the `/ember-publish-doc` workflow
|
|
24
|
+
|
|
25
|
+
## Delegation
|
|
26
|
+
|
|
27
|
+
Once you've determined the content type, follow the full instructions from the appropriate specific skill:
|
|
28
|
+
|
|
29
|
+
- **Interactive visual explainer** → Follow `/ember-publish-explainer` instructions exactly
|
|
30
|
+
- **Markdown documents** → Follow `/ember-publish-doc` instructions exactly
|
|
31
|
+
- **JSON data** → Follow `/ember-publish-json` instructions exactly
|
|
32
|
+
- **Directory / Space** → Follow `/ember-publish-space` instructions exactly
|
|
33
|
+
|
|
34
|
+
> **Tip:** If you're unsure, default to `/ember-publish-doc` — it's the most common use case.
|
|
35
|
+
|
|
36
|
+
## Quick Reference
|
|
37
|
+
|
|
38
|
+
### Authentication
|
|
39
|
+
|
|
40
|
+
All three workflows use the same auth. Session tokens are stored at `~/.emberflow/token.json`. Check if a valid session exists:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
cat ~/.emberflow/token.json 2>/dev/null
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
If the file exists, verify the token still works:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
curl -s -H "Authorization: Bearer $(jq -r .token ~/.emberflow/token.json)" \
|
|
50
|
+
https://emberflow.ai/api/docs
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
If no session exists, it's expired, or the verify call returns 401, authenticate using the device flow:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
EMBERFLOW_URL="https://emberflow.ai"
|
|
57
|
+
|
|
58
|
+
# Step 1: Request a device code
|
|
59
|
+
RESP=$(curl -s -X POST "$EMBERFLOW_URL/api/device-code")
|
|
60
|
+
CODE=$(echo "$RESP" | jq -r .code)
|
|
61
|
+
URL=$(echo "$RESP" | jq -r .verification_url)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Tell the user to open the URL in their browser to sign in and approve the device. Then poll until approved:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# Step 2: Poll until approved (every 3s)
|
|
68
|
+
while true; do
|
|
69
|
+
STATUS=$(curl -s "$EMBERFLOW_URL/api/device-code/$CODE")
|
|
70
|
+
S=$(echo "$STATUS" | jq -r .status)
|
|
71
|
+
if [ "$S" = "approved" ]; then
|
|
72
|
+
TOKEN=$(echo "$STATUS" | jq -r .session_token)
|
|
73
|
+
mkdir -p ~/.emberflow
|
|
74
|
+
echo "{\"token\":\"$TOKEN\"}" > ~/.emberflow/token.json
|
|
75
|
+
break
|
|
76
|
+
fi
|
|
77
|
+
if [ "$S" = "expired" ]; then
|
|
78
|
+
echo "Code expired. Please try again."
|
|
79
|
+
break
|
|
80
|
+
fi
|
|
81
|
+
sleep 3
|
|
82
|
+
done
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Publish Endpoints
|
|
86
|
+
|
|
87
|
+
- **Markdown / JSON**: `POST /api/docs` with `{ slug, title, content, visibility, content_type? }`
|
|
88
|
+
- **Spaces**: `POST /api/spaces` then `POST /api/spaces/{id}/publish` with `{ documents, nav }`
|
|
89
|
+
|
|
90
|
+
### Document URLs
|
|
91
|
+
|
|
92
|
+
- Documents: `https://emberflow.ai/d/<shortId>/<slug>`
|
|
93
|
+
- Spaces: `https://emberflow.ai/s/<author_short_id>/<space_slug>`
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ember-publish-doc
|
|
3
|
+
description: Publish a markdown document with Mermaid diagrams to Emberflow for hosted viewing with comments
|
|
4
|
+
argument-hint: [topic or description of what to document]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Emberflow Document Publisher
|
|
8
|
+
|
|
9
|
+
Create a polished markdown document and publish it to Emberflow — a hosted viewer at **https://emberflow.ai** with Mermaid diagram rendering (zoom/pan/fullscreen), dark mode, font selection, and per-block commenting.
|
|
10
|
+
|
|
11
|
+
## Step 1: Create the Markdown File
|
|
12
|
+
|
|
13
|
+
Write a `.md` file in the current project. The document should follow these conventions:
|
|
14
|
+
|
|
15
|
+
### Structure
|
|
16
|
+
- Start with a single `# Title` as the first line (this becomes the document title and slug)
|
|
17
|
+
- Use `##` and `###` for sections — these become commentable blocks in the viewer
|
|
18
|
+
- Keep paragraphs concise — each paragraph, list, table, blockquote, and heading is independently commentable by readers
|
|
19
|
+
|
|
20
|
+
### Mermaid Diagrams
|
|
21
|
+
|
|
22
|
+
Use fenced code blocks with the `mermaid` language tag. The viewer renders them with zoom, pan, and fullscreen controls.
|
|
23
|
+
|
|
24
|
+
````markdown
|
|
25
|
+
```mermaid
|
|
26
|
+
graph LR
|
|
27
|
+
A[Start] --> B{Decision}
|
|
28
|
+
B -->|Yes| C[Action]
|
|
29
|
+
B -->|No| D[Other Action]
|
|
30
|
+
```
|
|
31
|
+
````
|
|
32
|
+
|
|
33
|
+
Supported diagram types:
|
|
34
|
+
- `graph` / `flowchart` — flow diagrams (LR, TD, etc.)
|
|
35
|
+
- `sequenceDiagram` — interaction sequences
|
|
36
|
+
- `classDiagram` — class relationships
|
|
37
|
+
- `stateDiagram-v2` — state machines
|
|
38
|
+
- `erDiagram` — entity relationships
|
|
39
|
+
- `gantt` — project timelines
|
|
40
|
+
- `pie` — pie charts
|
|
41
|
+
- `gitgraph` — git branch visualizations
|
|
42
|
+
- `mindmap` — mind maps
|
|
43
|
+
- `timeline` — chronological events
|
|
44
|
+
|
|
45
|
+
#### Dark Mode Color Palette
|
|
46
|
+
|
|
47
|
+
The viewer auto-remaps these light colors to dark equivalents. Use them for best cross-theme rendering:
|
|
48
|
+
|
|
49
|
+
| Color | Use For |
|
|
50
|
+
|---|---|
|
|
51
|
+
| `#e1f5fe` | Blue backgrounds |
|
|
52
|
+
| `#e8f5e9` | Green backgrounds |
|
|
53
|
+
| `#fff3e0` | Orange backgrounds |
|
|
54
|
+
| `#fce4ec` | Red backgrounds |
|
|
55
|
+
| `#f3e5f5` | Purple backgrounds |
|
|
56
|
+
| `#fff9c4` | Yellow backgrounds |
|
|
57
|
+
|
|
58
|
+
### Tables, Code, Blockquotes
|
|
59
|
+
|
|
60
|
+
Standard GFM (GitHub Flavored Markdown) is fully supported:
|
|
61
|
+
|
|
62
|
+
```markdown
|
|
63
|
+
| Column A | Column B |
|
|
64
|
+
|----------|----------|
|
|
65
|
+
| value | value |
|
|
66
|
+
|
|
67
|
+
> Blockquotes render with a blue left border
|
|
68
|
+
|
|
69
|
+
`inline code` and fenced code blocks with syntax highlighting
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Example Document
|
|
73
|
+
|
|
74
|
+
````markdown
|
|
75
|
+
# API Architecture Overview
|
|
76
|
+
|
|
77
|
+
Brief introduction to the system.
|
|
78
|
+
|
|
79
|
+
## Components
|
|
80
|
+
|
|
81
|
+
```mermaid
|
|
82
|
+
graph TD
|
|
83
|
+
Client[Web Client] --> API[API Gateway]
|
|
84
|
+
API --> Auth[Auth Service]
|
|
85
|
+
API --> Docs[Doc Service]
|
|
86
|
+
Docs --> DB[(PostgreSQL)]
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Request Flow
|
|
90
|
+
|
|
91
|
+
```mermaid
|
|
92
|
+
sequenceDiagram
|
|
93
|
+
participant C as Client
|
|
94
|
+
participant G as Gateway
|
|
95
|
+
participant S as Service
|
|
96
|
+
C->>G: Request
|
|
97
|
+
G->>S: Forward
|
|
98
|
+
S->>G: Response
|
|
99
|
+
G->>C: Response
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Data Model
|
|
103
|
+
|
|
104
|
+
| Entity | Purpose |
|
|
105
|
+
|--------|---------|
|
|
106
|
+
| Users | Account management |
|
|
107
|
+
| Documents | Published content |
|
|
108
|
+
| Comments | Discussion threads |
|
|
109
|
+
|
|
110
|
+
## Design Notes
|
|
111
|
+
|
|
112
|
+
> We chose event sourcing to maintain a complete audit trail of all state changes.
|
|
113
|
+
````
|
|
114
|
+
|
|
115
|
+
## Step 2: Authenticate (if needed)
|
|
116
|
+
|
|
117
|
+
Session tokens are stored at `~/.emberflow/token.json`. Check if a valid session exists:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
cat ~/.emberflow/token.json 2>/dev/null
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
If the file exists, verify the token still works:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
curl -s -H "Authorization: Bearer $(jq -r .token ~/.emberflow/token.json)" \
|
|
127
|
+
https://emberflow.ai/api/docs
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
If no session exists, it's expired, or the verify call returns 401, authenticate using the device flow:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
EMBERFLOW_URL="https://emberflow.ai"
|
|
134
|
+
|
|
135
|
+
# Step 1: Request a device code
|
|
136
|
+
RESP=$(curl -s -X POST "$EMBERFLOW_URL/api/device-code")
|
|
137
|
+
CODE=$(echo "$RESP" | jq -r .code)
|
|
138
|
+
URL=$(echo "$RESP" | jq -r .verification_url)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Tell the user to open the URL in their browser to sign in and approve the device. Then poll until approved:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# Step 2: Poll until approved (every 3s)
|
|
145
|
+
while true; do
|
|
146
|
+
STATUS=$(curl -s "$EMBERFLOW_URL/api/device-code/$CODE")
|
|
147
|
+
S=$(echo "$STATUS" | jq -r .status)
|
|
148
|
+
if [ "$S" = "approved" ]; then
|
|
149
|
+
TOKEN=$(echo "$STATUS" | jq -r .session_token)
|
|
150
|
+
mkdir -p ~/.emberflow
|
|
151
|
+
echo "{\"token\":\"$TOKEN\"}" > ~/.emberflow/token.json
|
|
152
|
+
break
|
|
153
|
+
fi
|
|
154
|
+
if [ "$S" = "expired" ]; then
|
|
155
|
+
echo "Code expired. Please try again."
|
|
156
|
+
break
|
|
157
|
+
fi
|
|
158
|
+
sleep 3
|
|
159
|
+
done
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Step 3: Publish
|
|
163
|
+
|
|
164
|
+
Generate a slug from the document title and publish using the API:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# Read the file, extract title, generate slug, and publish
|
|
168
|
+
EMBERFLOW_URL="https://emberflow.ai"
|
|
169
|
+
FILE_PATH="/absolute/path/to/document.md"
|
|
170
|
+
TITLE=$(head -1 "$FILE_PATH" | sed 's/^#\s*//')
|
|
171
|
+
SLUG=$(echo "$TITLE" | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9' '-' | sed 's/^-//;s/-$//')
|
|
172
|
+
TOKEN=$(jq -r .token ~/.emberflow/token.json)
|
|
173
|
+
|
|
174
|
+
curl -s -X POST "$EMBERFLOW_URL/api/docs" \
|
|
175
|
+
-H 'Content-Type: application/json' \
|
|
176
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
177
|
+
-d "$(jq -n --arg slug "$SLUG" --arg title "$TITLE" --rawfile content "$FILE_PATH" \
|
|
178
|
+
'{slug: $slug, title: $title, content: $content, visibility: "public"}')"
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
The response JSON includes the URL. Documents are viewable at:
|
|
182
|
+
- Public: `https://emberflow.ai/d/<slug>`
|
|
183
|
+
- Private: `https://emberflow.ai/d/<slug>?key=<private-key>`
|
|
184
|
+
|
|
185
|
+
To **update** an existing document, publish again with the same slug — the API upserts for the same author.
|
|
186
|
+
|
|
187
|
+
> **JSON documents**: You can also publish JSON data by passing `content_type: "json"` in the API payload. The content should be valid JSON (either raw data or the multi-payload format `{"payloads": [{"label": "...", "data": ...}]}`). Use the `/ember-publish-json` skill for a dedicated JSON publishing workflow.
|
|
188
|
+
|
|
189
|
+
### Other Operations
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
# List all your documents
|
|
193
|
+
curl -s -H "Authorization: Bearer $TOKEN" "$EMBERFLOW_URL/api/docs"
|
|
194
|
+
|
|
195
|
+
# Delete a document
|
|
196
|
+
curl -s -X DELETE -H "Authorization: Bearer $TOKEN" "$EMBERFLOW_URL/api/docs/SLUG_HERE"
|
|
197
|
+
```
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ember-publish-explainer
|
|
3
|
+
description: Generate an interactive visual explainer — AI chooses the best visualization (flowchart, chart, timeline, grid, etc.) for the topic
|
|
4
|
+
argument-hint: [topic or concept to explain visually]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Interactive Visual Explainer
|
|
8
|
+
|
|
9
|
+
Generate a **structured JSON** explainer that the Emberflow platform renders as an interactive, slide-based visual. The layout is always **left prose + right visualization**, with platform-provided navigation (prev/next, progress pips, keyboard arrows).
|
|
10
|
+
|
|
11
|
+
You choose the best visualization type for each slide. A single explainer can mix different viz types across slides — a timeline on slide 2, a data table on slide 3, a chart on slide 4.
|
|
12
|
+
|
|
13
|
+
## Output
|
|
14
|
+
|
|
15
|
+
Write a single JSON file to `{topic-slug}-explainer.json` in the current working directory. Then publish it to Emberflow.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## A. JSON Schema
|
|
20
|
+
|
|
21
|
+
The output JSON has this structure:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"css": "/* shared styles for all viz panels */",
|
|
26
|
+
"slides": [
|
|
27
|
+
{
|
|
28
|
+
"label": "Overview",
|
|
29
|
+
"title": "Authentication Flow",
|
|
30
|
+
"prose": "<p>When a user signs in...</p>",
|
|
31
|
+
"viz": "<svg>...nodes, edges, chart markup...</svg>",
|
|
32
|
+
"script": "var nodes = container.querySelectorAll('.d-node');\nnodes.forEach(function(n, i) { setTimeout(function() { n.classList.add('visible'); }, 100 + i * 60); });"
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Fields
|
|
39
|
+
|
|
40
|
+
- **`css`** (string): Shared CSS for all viz panels. Injected once into a `<style>` block. Can reference platform design tokens (`var(--accent)`, `var(--border)`, etc. — see Section C). Keep this concise — only styles needed by your viz HTML.
|
|
41
|
+
- **`slides[]`** (array, 4-7 items): Slide objects, each with:
|
|
42
|
+
- `label` (string): Short uppercase tag (e.g., "Overview", "Step 1", "Budget")
|
|
43
|
+
- `title` (string): Heading text. First slide renders as `<h1>`, rest as `<h2>`
|
|
44
|
+
- `prose` (string): HTML for the left panel — paragraphs, lists, inline code. No block-level scripts or styles.
|
|
45
|
+
- `viz` (string): HTML markup for the right panel — SVG elements, DOM elements, tables, etc. This is inserted as `innerHTML` each time the slide activates.
|
|
46
|
+
- `script` (string, optional): JavaScript that runs when the slide activates. Receives `container` as a parameter — the viz panel DOM element. Use `var` (not `const`/`let`) for broad compatibility. The script runs via `new Function('container', script)(vizEl)`.
|
|
47
|
+
|
|
48
|
+
### What the platform provides
|
|
49
|
+
|
|
50
|
+
The platform handles everything outside the slide data:
|
|
51
|
+
- Topbar, sidebar, document list
|
|
52
|
+
- Slide layout (left prose / right viz grid)
|
|
53
|
+
- Navigation (prev/next buttons, progress pips, arrow keys, space bar)
|
|
54
|
+
- Slide transitions (opacity fade)
|
|
55
|
+
- Responsive behavior (stacks on mobile)
|
|
56
|
+
- Theme integration (light/dark mode via CSS custom properties)
|
|
57
|
+
|
|
58
|
+
### What you provide
|
|
59
|
+
|
|
60
|
+
Only the content:
|
|
61
|
+
- Shared CSS for your viz elements (`css` field)
|
|
62
|
+
- Per-slide: label, title, prose HTML, viz HTML, optional activation script
|
|
63
|
+
- No `<html>`, `<body>`, `<head>`, navigation, or layout wrappers
|
|
64
|
+
- No external dependencies (no CDN links, no imports, no web fonts)
|
|
65
|
+
|
|
66
|
+
### Platform-provided CSS primitives
|
|
67
|
+
|
|
68
|
+
The platform ships CSS for common patterns so you don't need to define them in your `css` field:
|
|
69
|
+
|
|
70
|
+
#### Code blocks (`.ex-code`)
|
|
71
|
+
|
|
72
|
+
Use in the **viz** panel to show syntax-highlighted code with line numbers and per-slide line highlighting:
|
|
73
|
+
|
|
74
|
+
```html
|
|
75
|
+
<pre class="ex-code"><code>
|
|
76
|
+
<span class="ex-line">function authenticate(req) {</span>
|
|
77
|
+
<span class="ex-line highlight"> const token = req.headers.authorization;</span>
|
|
78
|
+
<span class="ex-line highlight"> if (!token) return null;</span>
|
|
79
|
+
<span class="ex-line"> return verifyJWT(token);</span>
|
|
80
|
+
<span class="ex-line">}</span>
|
|
81
|
+
</code></pre>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
- Each `<span class="ex-line">` gets automatic line numbers via CSS counters
|
|
85
|
+
- Add `.highlight` to emphasize specific lines (orange accent background)
|
|
86
|
+
- Add `<span class="ex-lang">js</span>` inside the `<pre>` for a language badge
|
|
87
|
+
|
|
88
|
+
For **prose** panel code, just use standard `<pre><code>...</code></pre>` — the platform styles those too.
|
|
89
|
+
|
|
90
|
+
#### Click hints (`.ex-click-hint`)
|
|
91
|
+
|
|
92
|
+
When a viz has interactive/clickable elements, add a hint so the user knows:
|
|
93
|
+
|
|
94
|
+
```html
|
|
95
|
+
<div class="ex-click-hint">Click to explore</div>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The platform renders this as a small muted label with a pointer icon, positioned at the top of the viz panel. It auto-fades after 3 seconds. Use it on any slide where clicking items reveals detail content.
|
|
99
|
+
|
|
100
|
+
#### Architecture diagrams (declarative, auto-laid-out)
|
|
101
|
+
|
|
102
|
+
For architecture diagrams, flowcharts, and any node-and-edge visualization, use a **declarative JSON object** as the `viz` field instead of an HTML string. The platform auto-computes layout using dagre and renders with animated SVG.
|
|
103
|
+
|
|
104
|
+
**Set `viz` to a JSON object** (not a string) with this structure:
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"direction": "LR",
|
|
109
|
+
"nodesep": 50,
|
|
110
|
+
"ranksep": 80,
|
|
111
|
+
"nodes": [
|
|
112
|
+
{"id": "api", "label": "API Server", "sublabel": "validates slides[]", "icon": "server", "color": "blue"},
|
|
113
|
+
{"id": "db", "label": "PostgreSQL", "sublabel": "content column", "icon": "database", "color": "blue"},
|
|
114
|
+
{"id": "client", "label": "Browser", "sublabel": "JSON.parse(atob())", "icon": "browser", "color": "green"}
|
|
115
|
+
],
|
|
116
|
+
"edges": [
|
|
117
|
+
{"from": "api", "to": "db", "label": "INSERT", "color": "blue"},
|
|
118
|
+
{"from": "db", "to": "client", "label": "SELECT", "color": "green"}
|
|
119
|
+
],
|
|
120
|
+
"groups": [
|
|
121
|
+
{"id": "backend", "label": "Server-Side", "nodes": ["api", "db"]},
|
|
122
|
+
{"id": "frontend", "label": "Client-Side", "nodes": ["client"]}
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**How it works:** The platform runs dagre (a directed graph layout engine) to automatically compute node positions. Each group is laid out as an independent horizontal row, then groups are stacked vertically. Cross-group edges are drawn as smooth bezier curves. Nodes animate in with staggered entrance, edges draw in with stroke animation. You never write SVG coordinates.
|
|
128
|
+
|
|
129
|
+
**Node properties:**
|
|
130
|
+
- `id` (required): Unique identifier
|
|
131
|
+
- `label` (required): Main text
|
|
132
|
+
- `sublabel` (optional): Smaller muted subtitle below label
|
|
133
|
+
- `icon` (optional): One of: `server`, `database`, `browser`, `code`, `play`, `sun`, `grid`, `cloud`, `lock`, `user`, `file`, `api`, `cpu`, `network`, `mail`, `zap`, `box`
|
|
134
|
+
- `color` (optional): `orange`, `green`, `blue`, `red`, `purple` — tints the node border and fill
|
|
135
|
+
|
|
136
|
+
**Edge properties:**
|
|
137
|
+
- `from`, `to` (required): Node IDs
|
|
138
|
+
- `label` (optional): Text displayed at the edge midpoint
|
|
139
|
+
- `color` (optional): Same color options as nodes
|
|
140
|
+
|
|
141
|
+
**Group properties:**
|
|
142
|
+
- `id` (required): Unique identifier
|
|
143
|
+
- `label` (required): Region label (displayed above the group)
|
|
144
|
+
- `nodes` (required): Array of node IDs in this group
|
|
145
|
+
|
|
146
|
+
**Layout options:**
|
|
147
|
+
- `direction`: `"LR"` (left-to-right, default) or `"TB"` (top-to-bottom) — controls flow within each group
|
|
148
|
+
- `nodesep`: Pixel spacing between nodes in the same rank (default 50)
|
|
149
|
+
- `ranksep`: Pixel spacing between ranks (default 80)
|
|
150
|
+
|
|
151
|
+
**Color coding best practices:**
|
|
152
|
+
- Blue for data stores and infrastructure
|
|
153
|
+
- Green for client-side / success paths
|
|
154
|
+
- Orange for the highlighted/active component or entry points
|
|
155
|
+
- Purple for middleware / transformation layers
|
|
156
|
+
- Red for errors or warnings
|
|
157
|
+
|
|
158
|
+
**When to use diagrams vs raw SVG:** Use declarative diagrams for any node-and-edge visualization (architecture, flowcharts, data flows, org charts). Use raw SVG/HTML for other viz types (charts, tables, timelines, KPIs) where spatial positioning matters.
|
|
159
|
+
|
|
160
|
+
No `script` field is needed for diagram slides — the platform handles all animation automatically.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## B. Visualization Primitive Catalog
|
|
165
|
+
|
|
166
|
+
Choose from these primitives. Compose them freely — combine, nest, or invent new ones as the topic demands.
|
|
167
|
+
|
|
168
|
+
### Data Display
|
|
169
|
+
|
|
170
|
+
- **KPI / stat cards** — Grid of boxes with label, large value, subtitle. Use for overview slides. `display: grid; grid-template-columns: 1fr 1fr; gap: 12px`
|
|
171
|
+
- **Data table** — `<table>` with muted uppercase headers, monospace numbers, color-coded values (green positive, red negative). Category dots on row labels.
|
|
172
|
+
- **Comparison matrix** — Table with checkmark/cross SVG icons per cell. Column headers are options, rows are features. Active column highlighted with accent border.
|
|
173
|
+
|
|
174
|
+
### Charts
|
|
175
|
+
|
|
176
|
+
- **Vertical bar chart** — Flex row of bars, height as percentage of max. Color-code by threshold (green/orange/red). Hover reveals value label. `transition: height 0.6s cubic-bezier(0.4, 0, 0.2, 1)`
|
|
177
|
+
- **Horizontal bar chart** — Rows with label left, bar extending right. Good for ranked lists. Bar width as percentage via `flex` layout.
|
|
178
|
+
- **Donut / ring chart** — SVG circle with `stroke-dasharray`/`stroke-dashoffset`. Percentage label centered absolutely.
|
|
179
|
+
- **Funnel diagram** — Stacked horizontal bars decreasing in width, centered. Labels and conversion percentages on each stage.
|
|
180
|
+
|
|
181
|
+
### Timelines & Sequences
|
|
182
|
+
|
|
183
|
+
- **Vertical timeline** — Left border line with dot markers. Each event has date, title, description, optional progress bar and status badge.
|
|
184
|
+
- **Horizontal timeline** — Flex row of connected nodes along a horizontal line. Good for fewer items (3-6).
|
|
185
|
+
- **Progress stepper** — Numbered circles connected by lines. Active step highlighted, completed steps filled.
|
|
186
|
+
|
|
187
|
+
### Relationships & Structure (Auto-Layout Diagrams)
|
|
188
|
+
|
|
189
|
+
For any node-and-edge visualization, use a **declarative diagram object** as the `viz` field. The platform auto-positions nodes and routes edges — no manual coordinate math needed.
|
|
190
|
+
|
|
191
|
+
- **Architecture diagram** — Use `viz: { nodes, edges, groups }` with color-coded nodes, meaningful icons, and labeled groups. The platform lays out each group as a horizontal row, stacks groups vertically, and draws cross-group edges as smooth beziers. See Section A → "Architecture diagrams".
|
|
192
|
+
- **Flowchart** — Same declarative format with a single group or no groups. Set `direction: "TB"` for top-to-bottom flow.
|
|
193
|
+
- **Org chart / hierarchy** — Use `direction: "TB"` and groups for departments. Color-code by role.
|
|
194
|
+
- **Network / data flow** — Multiple groups connected by cross-group edges. Color edges by data type.
|
|
195
|
+
|
|
196
|
+
### Grids & Categories
|
|
197
|
+
|
|
198
|
+
- **Periodic table / grid** — CSS grid of cards with colored top bar per category. Hover shows detail.
|
|
199
|
+
- **Kanban board** — Columns with card items. Cards can highlight or shift between columns per slide.
|
|
200
|
+
|
|
201
|
+
### Status & Indicators
|
|
202
|
+
|
|
203
|
+
- **Risk cards** — Stacked cards with severity SVG icon, title, description, colored severity badge.
|
|
204
|
+
- **Stat delta** — Large number with up/down arrow SVG and percentage change.
|
|
205
|
+
- **Utilization bars** — Rows with label, horizontal progress bar, percentage.
|
|
206
|
+
- **Checklist** — Items with check/cross SVG icons. Grouped by category.
|
|
207
|
+
|
|
208
|
+
### Inventing New Primitives
|
|
209
|
+
|
|
210
|
+
The catalog above is a starting point. If the topic calls for a visualization not listed, invent one. Combine primitives freely. The only constraints are the design tokens and the slide-based interaction model.
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## C. Design Tokens (Platform-Provided)
|
|
215
|
+
|
|
216
|
+
These CSS custom properties are available in both the `css` field and `viz` HTML. They adapt to the user's light/dark theme:
|
|
217
|
+
|
|
218
|
+
| Token | Light | Dark | Use |
|
|
219
|
+
|---|---|---|---|
|
|
220
|
+
| `--bg` | `#ffffff` | `#09090b` | Page background |
|
|
221
|
+
| `--bg-secondary` | `#f9fafb` | `#18181b` | Card/panel backgrounds |
|
|
222
|
+
| `--text` | `#0f172a` | `#fafafa` | Body text |
|
|
223
|
+
| `--text-muted` | `#64748b` | `#a1a1aa` | Secondary text, labels |
|
|
224
|
+
| `--border` | `#e2e8f0` | `#27272a` | Borders, dividers |
|
|
225
|
+
| `--link` | `#ea580c` | `#fb923c` | Accent color (orange) |
|
|
226
|
+
| `--heading` | `#0f172a` | `#fafafa` | Heading text |
|
|
227
|
+
| `--code-bg` | `#f1f5f9` | `#18181b` | Code block backgrounds |
|
|
228
|
+
|
|
229
|
+
Additional tokens you can define in your `css` field for internal use (common choices):
|
|
230
|
+
|
|
231
|
+
```css
|
|
232
|
+
--accent: var(--link);
|
|
233
|
+
--accent-lt: var(--link);
|
|
234
|
+
--ring: rgba(234, 88, 12, 0.15);
|
|
235
|
+
--glow: rgba(234, 88, 12, 0.25);
|
|
236
|
+
--green: #22c55e;
|
|
237
|
+
--green-dim: rgba(34, 197, 94, 0.12);
|
|
238
|
+
--yellow: #eab308;
|
|
239
|
+
--yellow-dim: rgba(234, 179, 8, 0.12);
|
|
240
|
+
--red: #ef4444;
|
|
241
|
+
--red-dim: rgba(239, 68, 68, 0.12);
|
|
242
|
+
--blue: #3b82f6;
|
|
243
|
+
--blue-dim: rgba(59, 130, 246, 0.12);
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
**Font stack:** Inherited from the platform. Don't set font-family.
|
|
247
|
+
**Monospace:** `'JetBrains Mono', 'SF Mono', 'Fira Code', monospace`
|
|
248
|
+
|
|
249
|
+
**Transitions:**
|
|
250
|
+
- State changes: `0.3-0.5s, cubic-bezier(0.4, 0, 0.2, 1)`
|
|
251
|
+
- Hover effects: `0.15s`
|
|
252
|
+
- Bar/chart animations: `0.6s cubic-bezier(0.4, 0, 0.2, 1)`
|
|
253
|
+
|
|
254
|
+
**Active state:** `border-color: var(--link)` or `stroke: var(--link)` + glow `filter: drop-shadow(0 0 12px rgba(251, 146, 60, 0.15))`
|
|
255
|
+
**Dimmed state:** `opacity: 0.2`
|
|
256
|
+
**Staggered entrance:** `setTimeout(function() { el.classList.add('visible'); }, baseDelay + i * 60)`
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## D. Slide Planning
|
|
261
|
+
|
|
262
|
+
Before writing any code, plan 4-7 slides:
|
|
263
|
+
|
|
264
|
+
1. **Slide 1** = Overview. Show the full visualization at a glance — all elements visible, none dimmed. Give the reader context.
|
|
265
|
+
2. **Slides 2-6** = Each focuses on one concept or subset. Zoom in, highlight, or switch viz type.
|
|
266
|
+
3. **Last slide** (optional) = Summary or call-to-action.
|
|
267
|
+
|
|
268
|
+
For each slide, define:
|
|
269
|
+
- **label**: Short uppercase label
|
|
270
|
+
- **title**: Heading text
|
|
271
|
+
- **prose**: 1-3 paragraphs of explanation (HTML)
|
|
272
|
+
- **viz**: The HTML markup for the right panel
|
|
273
|
+
- **script** (optional): JS to animate/activate the viz
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## E. Script Conventions
|
|
278
|
+
|
|
279
|
+
The `script` field receives `container` (the viz panel DOM element). Common patterns:
|
|
280
|
+
|
|
281
|
+
```javascript
|
|
282
|
+
// Staggered entrance
|
|
283
|
+
var items = container.querySelectorAll('.my-item');
|
|
284
|
+
items.forEach(function(el, i) {
|
|
285
|
+
setTimeout(function() { el.classList.add('visible'); }, 100 + i * 60);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// Animate bar heights
|
|
289
|
+
var bars = container.querySelectorAll('.bar');
|
|
290
|
+
bars.forEach(function(bar, i) {
|
|
291
|
+
setTimeout(function() {
|
|
292
|
+
bar.style.height = bar.dataset.height;
|
|
293
|
+
}, 80 + i * 60);
|
|
294
|
+
});
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Interactive patterns
|
|
298
|
+
|
|
299
|
+
Viz elements can be **clickable**. Use click handlers to show/hide detail panels, switch between views, or highlight related content within a single slide. This makes dense slides explorable without needing more slides.
|
|
300
|
+
|
|
301
|
+
**When to use interactivity:** Use it when a slide has a list of items (sections, features, steps, categories) where each has detail content that would clutter the viz if all shown at once. The user clicks an item to drill in, clicks another to switch.
|
|
302
|
+
|
|
303
|
+
**Visual affordances:** Always signal that elements are clickable:
|
|
304
|
+
- Add `cursor: pointer` and a hover border/background change
|
|
305
|
+
- Include a `<div class="ex-click-hint">` element — the platform renders this as a small "click to explore" label with a pointer icon (see Platform-provided CSS primitives)
|
|
306
|
+
- Give the initially-selected item an `.active` class with accent styling
|
|
307
|
+
|
|
308
|
+
**Common interactive patterns:**
|
|
309
|
+
|
|
310
|
+
```javascript
|
|
311
|
+
// Clickable list with detail panel
|
|
312
|
+
// viz HTML: items with data-detail="..." attribute + a .detail-panel div
|
|
313
|
+
var items = container.querySelectorAll('.clickable-item');
|
|
314
|
+
var detail = container.querySelector('.detail-panel');
|
|
315
|
+
items.forEach(function(item) {
|
|
316
|
+
item.addEventListener('click', function() {
|
|
317
|
+
items.forEach(function(el) { el.classList.remove('active'); });
|
|
318
|
+
item.classList.add('active');
|
|
319
|
+
detail.innerHTML = item.getAttribute('data-detail');
|
|
320
|
+
detail.style.opacity = '0';
|
|
321
|
+
setTimeout(function() { detail.style.opacity = '1'; }, 30);
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
// Activate the first item by default
|
|
325
|
+
if (items[0]) items[0].click();
|
|
326
|
+
|
|
327
|
+
// Tabbed content switcher
|
|
328
|
+
// viz HTML: tab buttons with data-tab="id" + panels with data-panel="id"
|
|
329
|
+
var tabs = container.querySelectorAll('.tab-btn');
|
|
330
|
+
var panels = container.querySelectorAll('.tab-panel');
|
|
331
|
+
tabs.forEach(function(tab) {
|
|
332
|
+
tab.addEventListener('click', function() {
|
|
333
|
+
tabs.forEach(function(t) { t.classList.remove('active'); });
|
|
334
|
+
panels.forEach(function(p) { p.style.display = 'none'; });
|
|
335
|
+
tab.classList.add('active');
|
|
336
|
+
var panel = container.querySelector('[data-panel="' + tab.getAttribute('data-tab') + '"]');
|
|
337
|
+
if (panel) panel.style.display = 'block';
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
if (tabs[0]) tabs[0].click();
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
**Key rules for interactivity:**
|
|
344
|
+
- Always have a default selection — never show an empty state on load
|
|
345
|
+
- Transition content changes (opacity fade or translateY) so they don't feel jarring
|
|
346
|
+
- Keep the clickable area generous (full card/row, not just the text)
|
|
347
|
+
- All state lives in the DOM within `container` — no external variables needed
|
|
348
|
+
|
|
349
|
+
Rules:
|
|
350
|
+
- Use `var`, not `const`/`let`
|
|
351
|
+
- Use `function(){}`, not arrow functions
|
|
352
|
+
- Always scope queries to `container` (`container.querySelectorAll(...)`)
|
|
353
|
+
- Scripts run each time the slide activates (viz is rebuilt fresh each time)
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## F. Quality Rules
|
|
358
|
+
|
|
359
|
+
1. **Zero external dependencies** — no CDN links, no imports, no fonts
|
|
360
|
+
2. **No `<canvas>`** — SVG or DOM only
|
|
361
|
+
3. **Total JSON < 50KB** — be concise with CSS, HTML, and scripts
|
|
362
|
+
4. **4-7 slides** — no more, no fewer
|
|
363
|
+
5. **No scrolling in visualization pane** — everything must fit
|
|
364
|
+
6. **No emojis** — use inline SVGs with `stroke="currentColor"` for all icons
|
|
365
|
+
7. **All state changes animated** — opacity, transform, stroke-dashoffset, height, width
|
|
366
|
+
8. **Icons** — inline SVG, 16-24px viewBox, 1.5px stroke, `stroke-linecap="round" stroke-linejoin="round"`
|
|
367
|
+
9. **Use `var`** — not `const`/`let` in scripts
|
|
368
|
+
10. **Diagrams are declarative** — for any node-and-edge visualization, set `viz` to a JSON object with `nodes`, `edges`, and `groups`. The platform auto-computes layout and renders animated SVG. Never hand-code SVG coordinates for diagrams.
|
|
369
|
+
11. **SVG sizing** — for non-diagram SVG viz (custom charts, etc.), always use `viewBox`, never set `width`/`height` attributes. The platform stretches SVGs to fill the viz panel.
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
## G. Publishing
|
|
374
|
+
|
|
375
|
+
After generating the JSON file, publish it to Emberflow:
|
|
376
|
+
|
|
377
|
+
```bash
|
|
378
|
+
# Read the JSON and publish
|
|
379
|
+
CONTENT=$(cat {topic-slug}-explainer.json)
|
|
380
|
+
curl -X POST "${EMBERFLOW_URL}/api/docs" \
|
|
381
|
+
-H "Content-Type: application/json" \
|
|
382
|
+
-H "Authorization: Bearer ${EMBERFLOW_TOKEN}" \
|
|
383
|
+
-d "{\"slug\": \"{topic-slug}-explainer\", \"title\": \"{Title}\", \"content\": $(echo "$CONTENT" | jq -Rs .), \"content_type\": \"explainer\"}"
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
Or use the Emberflow MCP/CLI to publish with `content_type: 'explainer'`.
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## H. Reference Templates
|
|
391
|
+
|
|
392
|
+
Before generating, read the template that best matches your planned visualization:
|
|
393
|
+
|
|
394
|
+
```
|
|
395
|
+
Read templates/architecture-explainer.json for a flowchart example (SVG nodes + edges, show/active/dimmed states, staggered entrance).
|
|
396
|
+
|
|
397
|
+
Read templates/project-overview-explainer.json for a mixed-viz example (KPIs, timeline, budget table, donut ring, risk cards — different viz type per slide).
|
|
398
|
+
|
|
399
|
+
Read templates/dashboard-explainer.json for a data viz example (animated bar chart, color-coded values, dataset switching per slide, insight callouts).
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
Follow the patterns in the template closely. The templates are the ground truth for JSON structure, CSS conventions, and script patterns.
|