@zaflun/lumio-agent-skill 0.4.0 → 0.5.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/dist/bin/lumio-skills.js
CHANGED
|
@@ -77,7 +77,8 @@ All three surfaces share storage via \`useExtensionStorage()\` \u2014 changes in
|
|
|
77
77
|
"visibility": "public",
|
|
78
78
|
"pricing": { "type": "free" },
|
|
79
79
|
"config_schema": [
|
|
80
|
-
{ "key": "title", "type": "string", "label": "Widget Title", "default_value": "My Widget", "required": true },
|
|
80
|
+
{ "key": "title", "type": "string", "label": "Widget Title", "default_value": "My Widget", "required": true, "placeholder": "Enter title..." },
|
|
81
|
+
{ "key": "fontSize", "type": "number", "label": "Font Size", "default_value": 16, "required": false, "min": 8, "max": 200, "unit": "px" },
|
|
81
82
|
{ "key": "showBorder", "type": "boolean", "label": "Show Border", "default_value": true, "required": false }
|
|
82
83
|
]
|
|
83
84
|
}
|
|
@@ -136,11 +137,11 @@ lumio deploy
|
|
|
136
137
|
|
|
137
138
|
## Components
|
|
138
139
|
|
|
139
|
-
|
|
140
|
+
17 SDK components (all accept \`style: VisionStyle\`):
|
|
140
141
|
|
|
141
142
|
**Layout:** \`Box\`, \`VStack\`, \`HStack\`, \`CompactView\`, \`List\`
|
|
142
|
-
**Display:** \`Text\` (variant: heading/muted/default), \`Image\` (objectFit), \`Animated\`
|
|
143
|
-
**Input:** \`Button\` (variant: default/destructive), \`Toggle\`, \`TextField\`, \`TextArea\`, \`NumberField\`, \`Dropdown\`, \`Slider\`
|
|
143
|
+
**Display:** \`Text\` (variant: heading/muted/default), \`Image\` (objectFit), \`Animated\`, \`IconDisplay\`
|
|
144
|
+
**Input:** \`Button\` (variant: default/destructive), \`Toggle\`, \`TextField\`, \`TextArea\`, \`NumberField\`, \`Dropdown\`, \`Slider\`, \`IconPicker\`
|
|
144
145
|
|
|
145
146
|
**VisionStyle** is a typed CSS subset (allowed: flexbox, grid, \`position: relative\` / \`absolute\`, sizing, spacing, colors, border, typography, transitions; blocked: \`fixed\`, \`sticky\`, CSS expressions).
|
|
146
147
|
|
|
@@ -150,7 +151,7 @@ See \`components.md\` for full props and examples.
|
|
|
150
151
|
|
|
151
152
|
## Hooks
|
|
152
153
|
|
|
153
|
-
|
|
154
|
+
12 SDK hooks:
|
|
154
155
|
|
|
155
156
|
| Hook | Returns | Purpose |
|
|
156
157
|
|------|---------|---------|
|
|
@@ -165,6 +166,7 @@ See \`components.md\` for full props and examples.
|
|
|
165
166
|
| \`useQuery(fn, params?)\` | \`{ data, isLoading, error, refetch }\` | Call server query |
|
|
166
167
|
| \`useMutation(fn)\` | \`{ mutate, mutateAsync, data, isLoading, error }\` | Call server mutation/action |
|
|
167
168
|
| \`useAnimation(preset, options?)\` | \`animationProps\` | SDK animation presets for \`Animated\` component |
|
|
169
|
+
| \`useIconPicker(options?)\` | \`{ isOpen, selected, open, close, select, clear, sources }\` | Headless state for custom icon picker UIs |
|
|
168
170
|
|
|
169
171
|
**Important:** \`setStorage\` must always spread: \`setStorage({ ...storage, key: value })\`
|
|
170
172
|
|
|
@@ -429,7 +431,59 @@ See \`troubleshooting.md\` for the full error reference.
|
|
|
429
431
|
// src/content/components.ts
|
|
430
432
|
var componentsContent = `# Lumio SDK Components
|
|
431
433
|
|
|
432
|
-
All
|
|
434
|
+
All 31 components available in \`@zaflun/lumio-sdk\`. Every component accepts an optional \`style: VisionStyle\` prop.
|
|
435
|
+
|
|
436
|
+
## Editor Components
|
|
437
|
+
|
|
438
|
+
14 components for building settings UIs in \`editor.tsx\` and \`designer.tsx\`. They serialize via the Reconciler and render as \`@lumio/ui\` components on the host \u2014 matching the dashboard design system.
|
|
439
|
+
|
|
440
|
+
\`\`\`tsx
|
|
441
|
+
import {
|
|
442
|
+
EditorInput, EditorTextarea, EditorSelect, EditorCheckbox,
|
|
443
|
+
EditorSlider, EditorColorPicker, EditorGroup, EditorLabel,
|
|
444
|
+
EditorDivider, EditorInfo, EditorFontRow, EditorButton,
|
|
445
|
+
EditorFontPicker, EditorChannelRow, useEditorAction,
|
|
446
|
+
} from "@zaflun/lumio-sdk";
|
|
447
|
+
|
|
448
|
+
// Text/number input with label, placeholder, min/max, unit
|
|
449
|
+
<EditorInput label="Font Size" type="number" value="16" onChange={setSize} min={8} max={200} unit="px" />
|
|
450
|
+
|
|
451
|
+
// Select dropdown
|
|
452
|
+
<EditorSelect label="Theme" value={theme} onChange={setTheme} options={[{ value: "dark", label: "Dark" }]} />
|
|
453
|
+
|
|
454
|
+
// Checkbox toggle
|
|
455
|
+
<EditorCheckbox label="Show Badges" checked={show} onChange={setShow} />
|
|
456
|
+
|
|
457
|
+
// Slider with range
|
|
458
|
+
<EditorSlider label="Opacity" value={100} onChange={setOpacity} min={0} max={100} unit="%" />
|
|
459
|
+
|
|
460
|
+
// Color picker (HSV canvas + hex input)
|
|
461
|
+
<EditorColorPicker label="Background" value="#000000" onChange={setColor} />
|
|
462
|
+
|
|
463
|
+
// Compact font row: [B] [size px] [color]
|
|
464
|
+
<EditorFontRow bold={isBold} onBoldChange={setBold} fontSize={16} onFontSizeChange={setSize} fontColor="#fff" onFontColorChange={setColor} />
|
|
465
|
+
|
|
466
|
+
// Full-width button (primary/default/destructive)
|
|
467
|
+
<EditorButton label="Open Designer" variant="primary" onClick={openDesigner} />
|
|
468
|
+
|
|
469
|
+
// Collapsible section
|
|
470
|
+
<EditorGroup label="Advanced"><EditorCheckbox ... /></EditorGroup>
|
|
471
|
+
|
|
472
|
+
// Font picker with weight dropdown (opens NavigationStack page)
|
|
473
|
+
<EditorFontPicker label="Font Family" value="Lexend" onChange={setFont} weight="400" onWeightChange={setWeight} />
|
|
474
|
+
|
|
475
|
+
// Channel row: platform + name + color + delete in one line
|
|
476
|
+
<EditorChannelRow platform="twitch" onPlatformChange={setPlatform} channel="username" onChannelChange={setChannel} color="#9146FF" onColorChange={setColor} onRemove={remove} />
|
|
477
|
+
|
|
478
|
+
// Dispatch UI actions (e.g. open designer)
|
|
479
|
+
const { dispatch } = useEditorAction();
|
|
480
|
+
<EditorButton label="Open Designer" variant="primary" onClick={() => dispatch("open-designer")} />
|
|
481
|
+
|
|
482
|
+
// Section label, divider, info text
|
|
483
|
+
<EditorLabel text="Settings" />
|
|
484
|
+
<EditorDivider />
|
|
485
|
+
<EditorInfo content="Configure your widget here." />
|
|
486
|
+
\`\`\`
|
|
433
487
|
|
|
434
488
|
> **Note:** There is no direct DOM access from extension code. The custom React reconciler serializes component trees to \`LumioComponentNode[]\` via postMessage \u2014 you must use SDK components exclusively.
|
|
435
489
|
|
|
@@ -750,6 +804,69 @@ const exitAnim = useAnimation("fade-out", { duration: 200 });
|
|
|
750
804
|
- Keep animation durations short on the layer surface (< 500ms) to avoid frame drops
|
|
751
805
|
- Avoid animating layout-affecting properties (\`width\`, \`height\`) on the layer; prefer \`opacity\` and \`transform\`
|
|
752
806
|
|
|
807
|
+
## Picker Components
|
|
808
|
+
|
|
809
|
+
### IconPicker
|
|
810
|
+
|
|
811
|
+
Universal icon/emoji/emote picker with 4 tabs: Lucide icons (1960), Unicode emojis (1584), platform emotes (via API), and file upload. **Editor-only** \u2014 use \`IconDisplay\` to render the selected icon on the layer surface.
|
|
812
|
+
|
|
813
|
+
\`\`\`tsx
|
|
814
|
+
import { IconPicker, IconDisplay } from "@zaflun/lumio-sdk";
|
|
815
|
+
import type { IconValue } from "@zaflun/lumio-sdk";
|
|
816
|
+
|
|
817
|
+
// In editor.tsx \u2014 let the user pick an icon
|
|
818
|
+
const [storage, setStorage] = useExtensionStorage();
|
|
819
|
+
|
|
820
|
+
<IconPicker
|
|
821
|
+
value={storage.icon ?? null}
|
|
822
|
+
onChange={(icon) => setStorage({ ...storage, icon })}
|
|
823
|
+
sources={["lucide", "emoji", "emote", "upload"]}
|
|
824
|
+
size="md"
|
|
825
|
+
placeholder="Choose an icon..."
|
|
826
|
+
/>
|
|
827
|
+
|
|
828
|
+
// In layer.tsx \u2014 render the selected icon
|
|
829
|
+
<IconDisplay icon={storage.icon ?? null} size={32} />
|
|
830
|
+
\`\`\`
|
|
831
|
+
|
|
832
|
+
**IconPickerProps:**
|
|
833
|
+
- \`value: IconValue | null\` \u2014 currently selected icon
|
|
834
|
+
- \`onChange: (icon: IconValue | null) => void\` \u2014 selection callback
|
|
835
|
+
- \`sources?: IconSource[]\` \u2014 tabs to show (default: all four: \`["lucide", "emoji", "emote", "upload"]\`)
|
|
836
|
+
- \`size?: "sm" | "md" | "lg"\` \u2014 picker trigger button size
|
|
837
|
+
- \`placeholder?: string\` \u2014 placeholder text when no icon selected
|
|
838
|
+
|
|
839
|
+
**IconDisplayProps:**
|
|
840
|
+
- \`icon: IconValue | null\` \u2014 icon to render (null renders nothing)
|
|
841
|
+
- \`size?: number\` \u2014 pixel size
|
|
842
|
+
- \`className?: string\`
|
|
843
|
+
|
|
844
|
+
**IconValue type:**
|
|
845
|
+
\`\`\`typescript
|
|
846
|
+
type IconSource = "lucide" | "emoji" | "emote" | "upload";
|
|
847
|
+
|
|
848
|
+
interface IconValue {
|
|
849
|
+
type: IconSource; // which tab/source the icon came from
|
|
850
|
+
value: string; // icon name (lucide), Unicode char (emoji), CDN URL (emote), upload URL (upload)
|
|
851
|
+
label?: string; // human-readable label
|
|
852
|
+
platform?: string; // platform identifier for emotes (e.g. "twitch", "7tv")
|
|
853
|
+
}
|
|
854
|
+
\`\`\`
|
|
855
|
+
|
|
856
|
+
**\`useIconPicker()\` hook** \u2014 headless state management for custom picker UIs:
|
|
857
|
+
\`\`\`tsx
|
|
858
|
+
import { useIconPicker } from "@zaflun/lumio-sdk";
|
|
859
|
+
|
|
860
|
+
const { isOpen, selected, open, close, select, clear, sources } = useIconPicker({
|
|
861
|
+
sources: ["lucide", "emoji"],
|
|
862
|
+
onSelect: (icon) => console.log("Selected:", icon),
|
|
863
|
+
});
|
|
864
|
+
\`\`\`
|
|
865
|
+
|
|
866
|
+
Returns: \`{ isOpen, selected, open, close, select, clear, sources }\`
|
|
867
|
+
|
|
868
|
+
**Upload tab** requires the extension to declare \`egress.allowHosts\` including the Lumio API. Uploaded files are stored per-extension with quota tracking via the \`extension_uploads\` table.
|
|
869
|
+
|
|
753
870
|
## VisionStyle
|
|
754
871
|
|
|
755
872
|
Typed CSS subset accepted by all components. Sanitized by the host \u2014 only safe properties are allowed.
|
|
@@ -989,6 +1106,88 @@ updateLayer({ layerId: "uuid", visible: true });
|
|
|
989
1106
|
|
|
990
1107
|
Actions are proxied through the Lumio API \u2014 the extension never accesses OAuth tokens or platform credentials directly.
|
|
991
1108
|
|
|
1109
|
+
## Automation Node Handlers
|
|
1110
|
+
|
|
1111
|
+
Extensions with category \`automation_node\` export a single handler in \`server/handler.ts\`. The handler type depends on the \`node_type\` declared in \`lumio.config.json\`.
|
|
1112
|
+
|
|
1113
|
+
### \`defineAutomationAction(handler)\`
|
|
1114
|
+
|
|
1115
|
+
Action nodes execute when triggered by an upstream node in the automation workflow. They receive input data from the previous node and can perform side effects (HTTP calls, DB writes).
|
|
1116
|
+
|
|
1117
|
+
\`\`\`typescript
|
|
1118
|
+
import { defineAutomationAction } from "@zaflun/lumio-sdk/server";
|
|
1119
|
+
|
|
1120
|
+
export default defineAutomationAction({
|
|
1121
|
+
handler: async (ctx) => {
|
|
1122
|
+
const webhookUrl = ctx.nodeConfig.webhook_url;
|
|
1123
|
+
const res = await ctx.fetch(webhookUrl, {
|
|
1124
|
+
method: "POST",
|
|
1125
|
+
headers: { "Content-Type": "application/json" },
|
|
1126
|
+
body: JSON.stringify(ctx.inputData),
|
|
1127
|
+
});
|
|
1128
|
+
return { status: res.status, body: await res.json() };
|
|
1129
|
+
},
|
|
1130
|
+
});
|
|
1131
|
+
\`\`\`
|
|
1132
|
+
|
|
1133
|
+
### \`defineAutomationTrigger({ onWebhook?, onPoll? })\`
|
|
1134
|
+
|
|
1135
|
+
Trigger nodes start automation workflows. They support two modes: webhook (receives external HTTP calls) and poll (periodic checks for new data).
|
|
1136
|
+
|
|
1137
|
+
\`\`\`typescript
|
|
1138
|
+
import { defineAutomationTrigger } from "@zaflun/lumio-sdk/server";
|
|
1139
|
+
|
|
1140
|
+
export default defineAutomationTrigger({
|
|
1141
|
+
onWebhook: async (ctx, request) => {
|
|
1142
|
+
const body = await request.json();
|
|
1143
|
+
if (body.event === "order.created") {
|
|
1144
|
+
return { trigger: true, outputData: { orderId: body.id, total: body.total } };
|
|
1145
|
+
}
|
|
1146
|
+
return { trigger: false };
|
|
1147
|
+
},
|
|
1148
|
+
onPoll: async (ctx) => {
|
|
1149
|
+
const lastCheck = await ctx.cache.get("last_check") ?? "1970-01-01T00:00:00Z";
|
|
1150
|
+
const res = await ctx.fetch(
|
|
1151
|
+
\`https://api.example.com/events?since=\${lastCheck}\`,
|
|
1152
|
+
{ headers: { "Authorization": \`Bearer \${ctx.secrets.get("API_KEY")}\` } }
|
|
1153
|
+
);
|
|
1154
|
+
const events = await res.json();
|
|
1155
|
+
await ctx.cache.set("last_check", new Date().toISOString());
|
|
1156
|
+
return events.map((e: any) => ({ trigger: true, outputData: e }));
|
|
1157
|
+
},
|
|
1158
|
+
});
|
|
1159
|
+
\`\`\`
|
|
1160
|
+
|
|
1161
|
+
### \`defineAutomationLogic(handler)\`
|
|
1162
|
+
|
|
1163
|
+
Logic nodes evaluate conditions and route the workflow to different branches. They return a branch key that determines which downstream path executes.
|
|
1164
|
+
|
|
1165
|
+
\`\`\`typescript
|
|
1166
|
+
import { defineAutomationLogic } from "@zaflun/lumio-sdk/server";
|
|
1167
|
+
|
|
1168
|
+
export default defineAutomationLogic({
|
|
1169
|
+
handler: async (ctx) => {
|
|
1170
|
+
const amount = ctx.inputData.total;
|
|
1171
|
+
if (amount >= 100) return { branch: "high_value" };
|
|
1172
|
+
if (amount >= 25) return { branch: "medium_value" };
|
|
1173
|
+
return { branch: "low_value" };
|
|
1174
|
+
},
|
|
1175
|
+
});
|
|
1176
|
+
\`\`\`
|
|
1177
|
+
|
|
1178
|
+
### Automation Handler Context (\`ctx\`)
|
|
1179
|
+
|
|
1180
|
+
| Property | Available in | Description |
|
|
1181
|
+
|----------|-------------|-------------|
|
|
1182
|
+
| \`ctx.nodeConfig\` | All | Node configuration from the user's config panel |
|
|
1183
|
+
| \`ctx.inputData\` | Action, Logic | Data from the upstream node |
|
|
1184
|
+
| \`ctx.fetch(url, options?)\` | All | HTTP request (egress allowlist enforced) |
|
|
1185
|
+
| \`ctx.cache.get(key)\` / \`ctx.cache.set(key, value, ttl?)\` | All | Per-installation KV cache (Redis, 1h default TTL) |
|
|
1186
|
+
| \`ctx.secrets.get(key)\` | All | Server-side secret |
|
|
1187
|
+
| \`ctx.db\` | All | Extension DB schema access |
|
|
1188
|
+
| \`ctx.auth\` | All | \`{ accountId }\` \u2014 account identity |
|
|
1189
|
+
| \`ctx.defer()\` | Action | Mark execution as async (returns 202, completes in background) |
|
|
1190
|
+
|
|
992
1191
|
## Bot Module Handlers
|
|
993
1192
|
|
|
994
1193
|
Extensions with category \`bot_module\` export handler wrappers that register triggers. Each wrapper defines when the handler fires and what context it receives.
|
|
@@ -1087,6 +1286,37 @@ export const toxicFilter = moderate({
|
|
|
1087
1286
|
});
|
|
1088
1287
|
\`\`\`
|
|
1089
1288
|
|
|
1289
|
+
### \`defineModerate(handler)\`
|
|
1290
|
+
|
|
1291
|
+
Convenience wrapper for system-style moderation handlers. The handler receives a unified context with the chat message and returns a moderation action.
|
|
1292
|
+
|
|
1293
|
+
\`\`\`typescript
|
|
1294
|
+
import { defineModerate } from "@zaflun/lumio-sdk/server";
|
|
1295
|
+
|
|
1296
|
+
export default defineModerate(async (ctx) => {
|
|
1297
|
+
if (ctx.message.text.includes("spam")) {
|
|
1298
|
+
return { action: "delete" };
|
|
1299
|
+
}
|
|
1300
|
+
return { action: "allow" };
|
|
1301
|
+
});
|
|
1302
|
+
\`\`\`
|
|
1303
|
+
|
|
1304
|
+
### \`defineTimer(handler)\`
|
|
1305
|
+
|
|
1306
|
+
Convenience wrapper for timer-based handlers (e.g., timed messages). Fires on a recurring interval configured in the extension's install config.
|
|
1307
|
+
|
|
1308
|
+
\`\`\`typescript
|
|
1309
|
+
import { defineTimer } from "@zaflun/lumio-sdk/server";
|
|
1310
|
+
|
|
1311
|
+
export default defineTimer(async (ctx) => {
|
|
1312
|
+
const messages = (ctx.installConfig.messages as string || "").split("\\n").filter(Boolean);
|
|
1313
|
+
if (messages.length === 0) return null;
|
|
1314
|
+
const idx = parseInt(await ctx.cache.get("idx") || "0", 10);
|
|
1315
|
+
await ctx.cache.set("idx", String((idx + 1) % messages.length));
|
|
1316
|
+
return { reply: messages[idx] };
|
|
1317
|
+
});
|
|
1318
|
+
\`\`\`
|
|
1319
|
+
|
|
1090
1320
|
### Handler Return Actions
|
|
1091
1321
|
|
|
1092
1322
|
All bot module handlers return an action object:
|
|
@@ -1170,15 +1400,14 @@ lumio init my-widget
|
|
|
1170
1400
|
# ? Category: widget
|
|
1171
1401
|
# ? Targets: editor, layer
|
|
1172
1402
|
# ? Server functions: yes
|
|
1173
|
-
# ?
|
|
1403
|
+
# ? How should the editor UI be built? config_schema only / editor.tsx only / Both
|
|
1174
1404
|
# ? Owner: My Stream (personal) / Awesome Team <- team selection if applicable
|
|
1175
1405
|
#
|
|
1176
1406
|
# Creating extension on Lumio...
|
|
1177
1407
|
# Extension-ID: 88b2478a-39c7-468e-bebd-14e52fd97ac0
|
|
1178
1408
|
# Slug: k8f2x-my-widget
|
|
1179
1409
|
#
|
|
1180
|
-
# Writing lumio.config.json...
|
|
1181
|
-
# Writing src/editor.tsx...
|
|
1410
|
+
# Writing lumio.config.json (with config_schema)...
|
|
1182
1411
|
# Writing src/layer.tsx...
|
|
1183
1412
|
# Writing server/schema.ts...
|
|
1184
1413
|
# Writing server/functions.ts...
|
|
@@ -1186,6 +1415,32 @@ lumio init my-widget
|
|
|
1186
1415
|
# Done! Run \`lumio dev\` to start developing.
|
|
1187
1416
|
\`\`\`
|
|
1188
1417
|
|
|
1418
|
+
### Automation Node Example
|
|
1419
|
+
|
|
1420
|
+
\`\`\`bash
|
|
1421
|
+
lumio init my-webhook-action
|
|
1422
|
+
|
|
1423
|
+
# Interactive prompts:
|
|
1424
|
+
# ? Extension name: My Webhook Action
|
|
1425
|
+
# ? Description: Posts data to a webhook
|
|
1426
|
+
# ? Category: automation_node
|
|
1427
|
+
# ? Node type: action
|
|
1428
|
+
# ? Server functions: yes (required for automation nodes)
|
|
1429
|
+
# ? Owner: My Stream (personal) / Awesome Team
|
|
1430
|
+
#
|
|
1431
|
+
# Creating extension on Lumio...
|
|
1432
|
+
# Extension-ID: a1b2c3d4-...
|
|
1433
|
+
# Slug: x9k1z-my-webhook-action
|
|
1434
|
+
#
|
|
1435
|
+
# Writing lumio.config.json...
|
|
1436
|
+
# Writing src/editor.tsx...
|
|
1437
|
+
# Writing server/handler.ts...
|
|
1438
|
+
#
|
|
1439
|
+
# Done! Run \`lumio dev\` to start developing.
|
|
1440
|
+
\`\`\`
|
|
1441
|
+
|
|
1442
|
+
Automation nodes always generate \`server/handler.ts\` (not \`server/functions.ts\`) and \`src/editor.tsx\` for the config panel. The \`node_type\` can be \`action\`, \`trigger\`, or \`logic\`.
|
|
1443
|
+
|
|
1189
1444
|
### Templates
|
|
1190
1445
|
|
|
1191
1446
|
| Template | Targets | Server | Description |
|
|
@@ -1195,6 +1450,9 @@ lumio init my-widget
|
|
|
1195
1450
|
| Full-Stack | editor + layer | Yes | Complete extension with server functions |
|
|
1196
1451
|
| Interactive Game | editor + layer + interactive | Yes | Viewer interaction game |
|
|
1197
1452
|
| Bot Module | server-only | Yes | Chat command module |
|
|
1453
|
+
| Automation Action | server + editor | Yes | Automation workflow action node |
|
|
1454
|
+
| Automation Trigger | server + editor | Yes | Automation workflow trigger node |
|
|
1455
|
+
| Automation Logic | server + editor | Yes | Automation workflow logic/branching node |
|
|
1198
1456
|
| Integration | server | Yes | External API integration |
|
|
1199
1457
|
| Theme | editor | No | Style variables only |
|
|
1200
1458
|
|
|
@@ -1215,11 +1473,33 @@ lumio dev
|
|
|
1215
1473
|
# Press 'q' to quit.
|
|
1216
1474
|
\`\`\`
|
|
1217
1475
|
|
|
1476
|
+
### Automation Node Development
|
|
1477
|
+
|
|
1478
|
+
For automation node extensions, the dev server auto-detects \`category: "automation_node"\` and launches the automation simulator:
|
|
1479
|
+
|
|
1480
|
+
\`\`\`bash
|
|
1481
|
+
lumio dev
|
|
1482
|
+
# or explicitly:
|
|
1483
|
+
lumio dev --automation-node
|
|
1484
|
+
|
|
1485
|
+
# Output:
|
|
1486
|
+
# Lumio Dev Server running at http://localhost:5173
|
|
1487
|
+
# Config Panel: http://localhost:5173/editor
|
|
1488
|
+
#
|
|
1489
|
+
# Automation Simulator ready. Commands:
|
|
1490
|
+
# /execute [JSON] \u2014 Run the action handler with input data
|
|
1491
|
+
# /webhook [JSON] \u2014 Simulate an incoming webhook (trigger nodes)
|
|
1492
|
+
# /poll \u2014 Trigger a poll cycle (trigger nodes)
|
|
1493
|
+
# /config [JSON] \u2014 Update node config
|
|
1494
|
+
# /quit \u2014 Stop the dev server
|
|
1495
|
+
\`\`\`
|
|
1496
|
+
|
|
1218
1497
|
Features:
|
|
1219
1498
|
- Hot reload on file changes
|
|
1220
1499
|
- Mock event system (simulated chat, follows, subs)
|
|
1221
1500
|
- Sandbox environment matching production constraints
|
|
1222
1501
|
- Server function execution with local DB
|
|
1502
|
+
- Automation simulator with /execute, /webhook, /poll, /config commands
|
|
1223
1503
|
|
|
1224
1504
|
## lumio build
|
|
1225
1505
|
|
|
@@ -1697,22 +1977,46 @@ Lumio.render(<Overlay />, { target: "layer" });
|
|
|
1697
1977
|
|
|
1698
1978
|
## Config Schema
|
|
1699
1979
|
|
|
1700
|
-
Use \`config_schema\` in \`lumio.config.json\` for user-customizable settings. The
|
|
1980
|
+
Use \`config_schema\` in \`lumio.config.json\` for user-customizable settings. The widget editor auto-generates form fields from the schema \u2014 no custom \`editor.tsx\` needed for simple settings. For advanced UIs, use \`editor.tsx\` alongside (or instead of) config_schema.
|
|
1981
|
+
|
|
1982
|
+
**Three valid combos:**
|
|
1983
|
+
- **config_schema only** \u2014 auto-generated editor form, \`targets: ["layer"]\`
|
|
1984
|
+
- **editor.tsx only** \u2014 fully custom React editor UI, \`targets: ["layer", "editor"]\`
|
|
1985
|
+
- **Both** \u2014 schema fields render first, editor.tsx renders below for advanced controls
|
|
1986
|
+
|
|
1987
|
+
**Supported field types:** \`string\`, \`number\`, \`boolean\`, \`color\`, \`select\`, \`textarea\`, \`slider\`, \`font\`, \`icon\`, \`image\`, \`sound\`, \`multiselect\`, \`group\`, \`divider\`, \`info\`, \`channel\`, \`designer\`, \`presets\`
|
|
1701
1988
|
|
|
1702
1989
|
\`\`\`json
|
|
1703
1990
|
{
|
|
1704
1991
|
"config_schema": [
|
|
1705
|
-
{ "key": "title", "type": "string", "label": "Widget Title", "default_value": "My Widget", "required": true },
|
|
1706
|
-
{ "key": "
|
|
1992
|
+
{ "key": "title", "type": "string", "label": "Widget Title", "default_value": "My Widget", "required": true, "placeholder": "Enter title..." },
|
|
1993
|
+
{ "key": "fontSize", "type": "number", "label": "Font Size", "default_value": 16, "required": false, "min": 8, "max": 200, "unit": "px" },
|
|
1994
|
+
{ "key": "volume", "type": "slider", "label": "Volume", "default_value": 50, "required": false, "min": 0, "max": 100, "unit": "%" },
|
|
1995
|
+
{ "key": "theme", "type": "select", "label": "Color Theme", "options": ["dark", "light"], "default_value": "dark", "required": false },
|
|
1996
|
+
{ "key": "primaryColor", "type": "color", "label": "Primary Color", "default_value": "#43F7CD", "required": false },
|
|
1707
1997
|
{ "key": "showBorder", "type": "boolean", "label": "Show Border", "default_value": true, "required": false },
|
|
1998
|
+
{ "key": "targetDate", "type": "string", "label": "Target Date", "default_value": "", "required": false, "visible_when": { "key": "mode", "equals": "countdown" } },
|
|
1708
1999
|
{
|
|
1709
|
-
"key": "
|
|
1710
|
-
"
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
}
|
|
2000
|
+
"key": "twitch_events", "type": "group", "label": "Twitch Events", "required": false, "collapsed": true,
|
|
2001
|
+
"fields": [
|
|
2002
|
+
{ "key": "includeFollowers", "type": "boolean", "label": "Followers", "default_value": true, "required": false },
|
|
2003
|
+
{ "key": "includeSubs", "type": "boolean", "label": "Subscriptions", "default_value": true, "required": false }
|
|
2004
|
+
]
|
|
2005
|
+
},
|
|
2006
|
+
{ "key": "separator", "type": "divider", "label": "", "required": false },
|
|
2007
|
+
{ "key": "help_text", "type": "info", "label": "", "required": false, "content": "Configure the events you want to display." }
|
|
2008
|
+
]
|
|
2009
|
+
}
|
|
2010
|
+
\`\`\`
|
|
2011
|
+
|
|
2012
|
+
**Designer Schema:** Extensions can define a \`designer_schema\` alongside \`config_schema\` for a fullpage visual design editor. The designer opens as a fullpage overlay with the schema fields on the left and a live preview on the right. Designer values are stored under \`config.design\`.
|
|
2013
|
+
|
|
2014
|
+
\`\`\`json
|
|
2015
|
+
{
|
|
2016
|
+
"designer_schema": [
|
|
2017
|
+
{ "key": "bubbleColor", "type": "color", "label": "Chat Bubble Color", "default_value": "#9146FF", "required": false },
|
|
2018
|
+
{ "key": "nameFont", "type": "font", "label": "Username Font", "default_value": "Inter", "required": false },
|
|
2019
|
+
{ "key": "badgeSize", "type": "slider", "label": "Badge Size", "default_value": 18, "required": false, "min": 12, "max": 32, "unit": "px" }
|
|
1716
2020
|
]
|
|
1717
2021
|
}
|
|
1718
2022
|
\`\`\`
|
|
@@ -1722,7 +2026,7 @@ Read config values via \`useLumioConfig()\`:
|
|
|
1722
2026
|
\`\`\`tsx
|
|
1723
2027
|
const [config] = useLumioConfig();
|
|
1724
2028
|
const title = config.title ?? "My Widget";
|
|
1725
|
-
const
|
|
2029
|
+
const fontSize = config.fontSize ?? 16;
|
|
1726
2030
|
\`\`\`
|
|
1727
2031
|
|
|
1728
2032
|
**When to use config_schema vs storage:**
|
package/dist/index.js
CHANGED