@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.
@@ -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