@limeade-labs/sparkui 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/.env.example +9 -0
- package/CONTRIBUTING.md +63 -0
- package/LICENSE +21 -0
- package/README.md +232 -0
- package/SKILL.md +242 -0
- package/bin/deploy +23 -0
- package/bin/sparkui.js +390 -0
- package/docs/README.md +51 -0
- package/docs/api-reference.md +428 -0
- package/docs/chatgpt-setup.md +206 -0
- package/docs/components.md +432 -0
- package/docs/getting-started.md +179 -0
- package/docs/mcp-setup.md +195 -0
- package/docs/openclaw-setup.md +177 -0
- package/docs/templates.md +289 -0
- package/lib/components.js +474 -0
- package/lib/store.js +193 -0
- package/lib/templates.js +48 -0
- package/lib/ws-client.js +197 -0
- package/mcp-server/README.md +189 -0
- package/mcp-server/index.js +174 -0
- package/mcp-server/package.json +15 -0
- package/package.json +52 -0
- package/server.js +620 -0
- package/templates/base.js +82 -0
- package/templates/checkout.js +271 -0
- package/templates/feedback-form.js +140 -0
- package/templates/macro-tracker.js +205 -0
- package/templates/workout-timer.js +510 -0
- package/templates/ws-test.js +136 -0
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
# Composable Components
|
|
2
|
+
|
|
3
|
+
SparkUI provides 8 composable components that you can mix and match to build custom pages without writing HTML. Use the `POST /api/compose` endpoint to assemble a page from a `sections` array.
|
|
4
|
+
|
|
5
|
+
This is the recommended approach for most use cases — faster than raw HTML, more flexible than templates.
|
|
6
|
+
|
|
7
|
+
## How It Works
|
|
8
|
+
|
|
9
|
+
Send a layout object to the compose API:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
curl -X POST http://localhost:3457/api/compose \
|
|
13
|
+
-H "Authorization: Bearer $PUSH_TOKEN" \
|
|
14
|
+
-H "Content-Type: application/json" \
|
|
15
|
+
-d '{
|
|
16
|
+
"title": "My Dashboard",
|
|
17
|
+
"sections": [
|
|
18
|
+
{ "type": "header", "config": { ... } },
|
|
19
|
+
{ "type": "stats", "config": { ... } },
|
|
20
|
+
{ "type": "progress", "config": { ... } }
|
|
21
|
+
],
|
|
22
|
+
"ttl": 3600
|
|
23
|
+
}'
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Each section has a `type` (component name) and a `config` object with component-specific props.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 1. header
|
|
31
|
+
|
|
32
|
+
Page header with title, subtitle, optional icon, and optional badge.
|
|
33
|
+
|
|
34
|
+
### Config
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
interface HeaderConfig {
|
|
38
|
+
title: string; // Main heading text
|
|
39
|
+
subtitle?: string; // Secondary text below the title
|
|
40
|
+
icon?: string; // Emoji or icon character
|
|
41
|
+
badge?: string; // Small highlighted label (e.g. "New", "Beta")
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Example
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"type": "header",
|
|
50
|
+
"config": {
|
|
51
|
+
"title": "Daily Dashboard",
|
|
52
|
+
"subtitle": "March 14, 2026",
|
|
53
|
+
"icon": "📊",
|
|
54
|
+
"badge": "Live"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 2. button
|
|
62
|
+
|
|
63
|
+
Action button that emits an event when clicked. Supports primary, secondary, and danger styles.
|
|
64
|
+
|
|
65
|
+
### Config
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
interface ButtonConfig {
|
|
69
|
+
label: string; // Button text
|
|
70
|
+
action: string; // Event action name sent via WebSocket
|
|
71
|
+
style?: "primary" | "secondary" | "danger"; // Default: "primary"
|
|
72
|
+
icon?: string; // Emoji prepended to label
|
|
73
|
+
disabled?: boolean; // Greyed out, non-clickable
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Example
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"type": "button",
|
|
82
|
+
"config": {
|
|
83
|
+
"label": "Approve Request",
|
|
84
|
+
"action": "approve",
|
|
85
|
+
"style": "primary",
|
|
86
|
+
"icon": "✅"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Event emitted:** `{ type: "event", data: { action: "approve" } }`
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## 3. timer
|
|
96
|
+
|
|
97
|
+
Countdown timer, stopwatch, or interval timer with start/pause/reset controls.
|
|
98
|
+
|
|
99
|
+
### Config
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
interface TimerConfig {
|
|
103
|
+
mode: "countdown" | "stopwatch" | "interval";
|
|
104
|
+
duration?: number; // Seconds (for countdown mode)
|
|
105
|
+
intervals?: Array<{
|
|
106
|
+
label: string; // e.g. "Work", "Rest"
|
|
107
|
+
seconds: number;
|
|
108
|
+
}>;
|
|
109
|
+
autoStart?: boolean; // Start immediately on page load
|
|
110
|
+
onComplete?: string; // (reserved for future use)
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Examples
|
|
115
|
+
|
|
116
|
+
**Countdown timer:**
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"type": "timer",
|
|
121
|
+
"config": {
|
|
122
|
+
"mode": "countdown",
|
|
123
|
+
"duration": 300,
|
|
124
|
+
"autoStart": false
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Interval timer (HIIT):**
|
|
130
|
+
|
|
131
|
+
```json
|
|
132
|
+
{
|
|
133
|
+
"type": "timer",
|
|
134
|
+
"config": {
|
|
135
|
+
"mode": "interval",
|
|
136
|
+
"intervals": [
|
|
137
|
+
{ "label": "Work", "seconds": 30 },
|
|
138
|
+
{ "label": "Rest", "seconds": 10 },
|
|
139
|
+
{ "label": "Work", "seconds": 30 },
|
|
140
|
+
{ "label": "Rest", "seconds": 10 }
|
|
141
|
+
]
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Event emitted on completion:** `{ type: "timer", data: { action: "complete", mode: "countdown", elapsed: 300 } }`
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## 4. checklist
|
|
151
|
+
|
|
152
|
+
Interactive checklist with tappable items, optional progress bar, and optional "add item" input.
|
|
153
|
+
|
|
154
|
+
### Config
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
interface ChecklistConfig {
|
|
158
|
+
items: Array<{
|
|
159
|
+
text: string;
|
|
160
|
+
checked?: boolean; // Default: false
|
|
161
|
+
}>;
|
|
162
|
+
allowAdd?: boolean; // Show an input to add new items
|
|
163
|
+
showProgress?: boolean; // Show progress bar and percentage
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Example
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"type": "checklist",
|
|
172
|
+
"config": {
|
|
173
|
+
"items": [
|
|
174
|
+
{ "text": "Review PR #42", "checked": false },
|
|
175
|
+
{ "text": "Update docs", "checked": true },
|
|
176
|
+
{ "text": "Deploy to staging", "checked": false }
|
|
177
|
+
],
|
|
178
|
+
"showProgress": true,
|
|
179
|
+
"allowAdd": true
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**Events emitted:**
|
|
185
|
+
|
|
186
|
+
- Toggle: `{ type: "event", data: { action: "checklist_toggle", index: 0, checked: true, text: "Review PR #42" } }`
|
|
187
|
+
- All complete: `{ type: "completion", data: { action: "checklist_complete", items: [...] } }`
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## 5. progress
|
|
192
|
+
|
|
193
|
+
Single or multi-segment progress bar with animated fill.
|
|
194
|
+
|
|
195
|
+
### Config
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
interface ProgressConfig {
|
|
199
|
+
// Single bar mode
|
|
200
|
+
value?: number; // Current value
|
|
201
|
+
max?: number; // Maximum value (default: 100)
|
|
202
|
+
label?: string; // Label text
|
|
203
|
+
color?: string; // Bar color (default: "#00ff88")
|
|
204
|
+
showPercent?: boolean; // Show percentage (default: true)
|
|
205
|
+
|
|
206
|
+
// Multi-segment mode
|
|
207
|
+
segments?: Array<{
|
|
208
|
+
label: string;
|
|
209
|
+
value: number;
|
|
210
|
+
max: number;
|
|
211
|
+
color?: string;
|
|
212
|
+
}>;
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Examples
|
|
217
|
+
|
|
218
|
+
**Single bar:**
|
|
219
|
+
|
|
220
|
+
```json
|
|
221
|
+
{
|
|
222
|
+
"type": "progress",
|
|
223
|
+
"config": {
|
|
224
|
+
"value": 65,
|
|
225
|
+
"max": 100,
|
|
226
|
+
"label": "Project completion",
|
|
227
|
+
"color": "#6c63ff"
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
**Multi-segment (macros):**
|
|
233
|
+
|
|
234
|
+
```json
|
|
235
|
+
{
|
|
236
|
+
"type": "progress",
|
|
237
|
+
"config": {
|
|
238
|
+
"segments": [
|
|
239
|
+
{ "label": "Protein", "value": 62, "max": 86, "color": "#ff6b6b" },
|
|
240
|
+
{ "label": "Fat", "value": 45, "max": 95, "color": "#ffd93d" },
|
|
241
|
+
{ "label": "Carbs", "value": 15, "max": 25, "color": "#6bcb77" }
|
|
242
|
+
]
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## 6. stats
|
|
250
|
+
|
|
251
|
+
Grid of stat cards (2 columns) with values, labels, optional icons, units, and trend indicators.
|
|
252
|
+
|
|
253
|
+
### Config
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
interface StatsConfig {
|
|
257
|
+
items: Array<{
|
|
258
|
+
label: string; // Stat label (e.g. "Weight")
|
|
259
|
+
value: string | number;
|
|
260
|
+
unit?: string; // e.g. "lbs", "days", "%"
|
|
261
|
+
icon?: string; // Emoji icon
|
|
262
|
+
trend?: "up" | "down" | "flat"; // Trend arrow
|
|
263
|
+
}>;
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Example
|
|
268
|
+
|
|
269
|
+
```json
|
|
270
|
+
{
|
|
271
|
+
"type": "stats",
|
|
272
|
+
"config": {
|
|
273
|
+
"items": [
|
|
274
|
+
{ "label": "Weight", "value": "222", "unit": "lbs", "icon": "⚖️", "trend": "down" },
|
|
275
|
+
{ "label": "Streak", "value": "5", "unit": "days", "icon": "🔥", "trend": "up" },
|
|
276
|
+
{ "label": "Calories", "value": "1,250", "unit": "cal", "icon": "🍽️", "trend": "flat" },
|
|
277
|
+
{ "label": "Steps", "value": "8,432", "icon": "👟", "trend": "up" }
|
|
278
|
+
]
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## 7. form
|
|
286
|
+
|
|
287
|
+
Form with multiple field types. Sends a `completion` event with all field data on submit.
|
|
288
|
+
|
|
289
|
+
### Config
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
interface FormConfig {
|
|
293
|
+
fields: Array<{
|
|
294
|
+
type: "text" | "number" | "email" | "textarea" | "select" | "rating";
|
|
295
|
+
name: string; // Field name in submitted data
|
|
296
|
+
label?: string; // Display label
|
|
297
|
+
placeholder?: string;
|
|
298
|
+
required?: boolean;
|
|
299
|
+
options?: Array<string | { value: string; label: string }>; // For select type
|
|
300
|
+
}>;
|
|
301
|
+
submitLabel?: string; // Submit button text (default: "Submit")
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Example
|
|
306
|
+
|
|
307
|
+
```json
|
|
308
|
+
{
|
|
309
|
+
"type": "form",
|
|
310
|
+
"config": {
|
|
311
|
+
"fields": [
|
|
312
|
+
{ "type": "rating", "name": "satisfaction", "label": "How satisfied are you?" },
|
|
313
|
+
{ "type": "select", "name": "category", "label": "Category", "options": ["Bug", "Feature", "Question"], "required": true },
|
|
314
|
+
{ "type": "text", "name": "title", "label": "Title", "placeholder": "Brief summary", "required": true },
|
|
315
|
+
{ "type": "textarea", "name": "details", "label": "Details", "placeholder": "Tell us more..." }
|
|
316
|
+
],
|
|
317
|
+
"submitLabel": "Send Feedback"
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
**Event emitted:** `{ type: "completion", data: { formData: { satisfaction: 4, category: "Feature", title: "...", details: "..." } } }`
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
## 8. tabs
|
|
327
|
+
|
|
328
|
+
Tab switcher with multiple content panels. Content can include HTML strings (including output from other components rendered server-side).
|
|
329
|
+
|
|
330
|
+
### Config
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
interface TabsConfig {
|
|
334
|
+
tabs: Array<{
|
|
335
|
+
label: string; // Tab button label
|
|
336
|
+
content: string; // HTML content for the panel
|
|
337
|
+
}>;
|
|
338
|
+
activeIndex?: number; // Initially active tab (default: 0)
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Example
|
|
343
|
+
|
|
344
|
+
```json
|
|
345
|
+
{
|
|
346
|
+
"type": "tabs",
|
|
347
|
+
"config": {
|
|
348
|
+
"tabs": [
|
|
349
|
+
{ "label": "Overview", "content": "<p style='color:#e0e0e0'>Project overview content here...</p>" },
|
|
350
|
+
{ "label": "Details", "content": "<p style='color:#e0e0e0'>Detailed breakdown...</p>" },
|
|
351
|
+
{ "label": "History", "content": "<p style='color:#e0e0e0'>Change log...</p>" }
|
|
352
|
+
],
|
|
353
|
+
"activeIndex": 0
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## Full Composed Page Example
|
|
361
|
+
|
|
362
|
+
Here's a complete example composing a fitness dashboard from multiple components:
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
curl -X POST http://localhost:3457/api/compose \
|
|
366
|
+
-H "Authorization: Bearer $PUSH_TOKEN" \
|
|
367
|
+
-H "Content-Type: application/json" \
|
|
368
|
+
-d '{
|
|
369
|
+
"title": "Fitness Dashboard",
|
|
370
|
+
"sections": [
|
|
371
|
+
{
|
|
372
|
+
"type": "header",
|
|
373
|
+
"config": {
|
|
374
|
+
"title": "Fitness Dashboard",
|
|
375
|
+
"subtitle": "March 14, 2026",
|
|
376
|
+
"icon": "💪",
|
|
377
|
+
"badge": "Day 5"
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
"type": "stats",
|
|
382
|
+
"config": {
|
|
383
|
+
"items": [
|
|
384
|
+
{ "label": "Weight", "value": "222", "unit": "lbs", "icon": "⚖️", "trend": "down" },
|
|
385
|
+
{ "label": "Streak", "value": "5", "unit": "days", "icon": "🔥", "trend": "up" },
|
|
386
|
+
{ "label": "Calories", "value": "1,250", "unit": "cal", "icon": "🍽️" },
|
|
387
|
+
{ "label": "Protein", "value": "62", "unit": "g", "icon": "💪" }
|
|
388
|
+
]
|
|
389
|
+
}
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
"type": "progress",
|
|
393
|
+
"config": {
|
|
394
|
+
"segments": [
|
|
395
|
+
{ "label": "Calories", "value": 1250, "max": 1900, "color": "#00d4aa" },
|
|
396
|
+
{ "label": "Protein", "value": 62, "max": 86, "color": "#6c63ff" },
|
|
397
|
+
{ "label": "Fat", "value": 45, "max": 95, "color": "#ff6b6b" },
|
|
398
|
+
{ "label": "Carbs", "value": 15, "max": 25, "color": "#ffd93d" }
|
|
399
|
+
]
|
|
400
|
+
}
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
"type": "checklist",
|
|
404
|
+
"config": {
|
|
405
|
+
"items": [
|
|
406
|
+
{ "text": "Morning workout", "checked": true },
|
|
407
|
+
{ "text": "Take vitamins", "checked": true },
|
|
408
|
+
{ "text": "10k steps", "checked": false },
|
|
409
|
+
{ "text": "Drink 8 glasses water", "checked": false }
|
|
410
|
+
],
|
|
411
|
+
"showProgress": true
|
|
412
|
+
}
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
"type": "timer",
|
|
416
|
+
"config": {
|
|
417
|
+
"mode": "countdown",
|
|
418
|
+
"duration": 1800
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
],
|
|
422
|
+
"ttl": 7200,
|
|
423
|
+
"openclaw": {
|
|
424
|
+
"enabled": true,
|
|
425
|
+
"channel": "slack",
|
|
426
|
+
"to": "C0AKMF5E0KD",
|
|
427
|
+
"eventTypes": ["completion"]
|
|
428
|
+
}
|
|
429
|
+
}'
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
> **Tip:** When choosing between compose and templates, prefer **compose** for custom layouts and **templates** only when you need their specialized rendering (e.g., macro-tracker's animated charts).
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# Getting Started
|
|
2
|
+
|
|
3
|
+
Get SparkUI running and push your first page in under 5 minutes.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- **Node.js 18+** — [Download](https://nodejs.org/)
|
|
8
|
+
- **npm** (comes with Node.js)
|
|
9
|
+
|
|
10
|
+
Verify your setup:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
node --version # v18.0.0 or higher
|
|
14
|
+
npm --version # 9.0.0 or higher
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
### Option A: From npm
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Install globally
|
|
23
|
+
npm install -g sparkui
|
|
24
|
+
|
|
25
|
+
# Initialize in your project directory
|
|
26
|
+
sparkui init
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Or use npx without installing:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npx sparkui init
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The `init` command creates a `.env` file with a randomly generated push token.
|
|
36
|
+
|
|
37
|
+
### Option B: From source
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
git clone https://github.com/Limeade-Labs/sparkui.git
|
|
41
|
+
cd sparkui
|
|
42
|
+
npm install
|
|
43
|
+
cp .env.example .env
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Configure
|
|
47
|
+
|
|
48
|
+
Edit the `.env` file in your project directory:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Authentication token for the push API (generated by `sparkui init`)
|
|
52
|
+
PUSH_TOKEN=spk_your_generated_token_here
|
|
53
|
+
|
|
54
|
+
# Server port (default: 3457)
|
|
55
|
+
SPARKUI_PORT=3457
|
|
56
|
+
|
|
57
|
+
# Public URL for generated page links
|
|
58
|
+
# Set this if behind a reverse proxy, tunnel, or deployed to a server
|
|
59
|
+
SPARKUI_BASE_URL=http://localhost:3457
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
| Variable | Required | Default | Description |
|
|
63
|
+
|----------|----------|---------|-------------|
|
|
64
|
+
| `PUSH_TOKEN` | Yes | Auto-generated | Bearer token for API authentication |
|
|
65
|
+
| `SPARKUI_PORT` | No | `3457` | Port the server listens on |
|
|
66
|
+
| `SPARKUI_BASE_URL` | No | `http://localhost:{port}` | Public URL for generated links |
|
|
67
|
+
| `OPENCLAW_HOOKS_URL` | No | — | OpenClaw webhook URL (for event forwarding) |
|
|
68
|
+
| `OPENCLAW_HOOKS_TOKEN` | No | — | OpenClaw webhook auth token |
|
|
69
|
+
|
|
70
|
+
> **Tip:** If you don't set `PUSH_TOKEN`, SparkUI auto-generates one and appends it to `.env` on first start.
|
|
71
|
+
|
|
72
|
+
## Start the Server
|
|
73
|
+
|
|
74
|
+
### Using the CLI
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
sparkui start
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Or with npx:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npx sparkui start
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Using npm (from source)
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
npm start
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Using Node directly
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
node server.js
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
You should see:
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
⚡ SparkUI server running on port 3457
|
|
102
|
+
Health: http://localhost:3457/
|
|
103
|
+
Push token: spk_abc12345...6789
|
|
104
|
+
Templates: macro-tracker, ws-test, feedback-form, checkout, workout-timer
|
|
105
|
+
WebSocket: ws://localhost:3457/ws
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Verify it's running
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
curl http://localhost:3457/
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
|
|
116
|
+
```json
|
|
117
|
+
{
|
|
118
|
+
"status": "ok",
|
|
119
|
+
"service": "sparkui",
|
|
120
|
+
"version": "1.1.0",
|
|
121
|
+
"pages": 0,
|
|
122
|
+
"wsClients": 0,
|
|
123
|
+
"templates": ["macro-tracker", "ws-test", "feedback-form", "checkout", "workout-timer"],
|
|
124
|
+
"uptime": 5
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Push Your First Page
|
|
129
|
+
|
|
130
|
+
Replace `YOUR_TOKEN` with the token from your `.env` file:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
curl -X POST http://localhost:3457/api/push \
|
|
134
|
+
-H "Authorization: Bearer YOUR_TOKEN" \
|
|
135
|
+
-H "Content-Type: application/json" \
|
|
136
|
+
-d '{
|
|
137
|
+
"template": "feedback-form",
|
|
138
|
+
"data": {
|
|
139
|
+
"title": "Hello SparkUI!",
|
|
140
|
+
"subtitle": "Your first ephemeral page"
|
|
141
|
+
},
|
|
142
|
+
"ttl": 3600
|
|
143
|
+
}'
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Response:
|
|
147
|
+
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
|
151
|
+
"url": "/s/a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
|
152
|
+
"fullUrl": "http://localhost:3457/s/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Open the `fullUrl` in your browser — you'll see a polished dark-themed feedback form.
|
|
157
|
+
|
|
158
|
+
## Push with the CLI
|
|
159
|
+
|
|
160
|
+
You can also use the SparkUI CLI to push pages:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
# Push from a template
|
|
164
|
+
sparkui push --template feedback-form --data '{"title": "Quick Survey"}'
|
|
165
|
+
|
|
166
|
+
# Push raw HTML
|
|
167
|
+
sparkui push --html my-page.html --ttl 7200
|
|
168
|
+
|
|
169
|
+
# Check server status
|
|
170
|
+
sparkui status
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## What's Next?
|
|
174
|
+
|
|
175
|
+
- **[API Reference](./api-reference.md)** — Full endpoint documentation
|
|
176
|
+
- **[Templates](./templates.md)** — Explore all 5 built-in templates
|
|
177
|
+
- **[Components](./components.md)** — Build custom pages from composable components
|
|
178
|
+
- **[MCP Setup](./mcp-setup.md)** — Use SparkUI from Claude Desktop or Cursor
|
|
179
|
+
- **[OpenClaw Setup](./openclaw-setup.md)** — Integrate with an OpenClaw agent
|