@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.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  installSkillFiles
4
- } from "../chunk-SSYJXSJX.js";
4
+ } from "../chunk-QXQNAYHN.js";
5
5
 
6
6
  // src/bin/lumio-skills.ts
7
7
  import { resolve } from "path";
@@ -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
- 15 SDK components (all accept \`style: VisionStyle\`):
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
- 11 SDK hooks:
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 15 components available in \`@zaflun/lumio-sdk\`. Every component accepts an optional \`style: VisionStyle\` prop.
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
- # ? Template: Widget (editor + layer + storage)
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 Lumio dashboard renders these as a form in the Widget Gallery install flow \u2014 the user fills them in without needing to open the editor surface.
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": "maxItems", "type": "number", "label": "Max Items", "default_value": 10, "required": false },
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": "theme",
1710
- "type": "select",
1711
- "label": "Color Theme",
1712
- "options": ["dark", "light"],
1713
- "default_value": "dark",
1714
- "required": false
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 maxItems = config.maxItems ?? 10;
2029
+ const fontSize = config.fontSize ?? 16;
1726
2030
  \`\`\`
1727
2031
 
1728
2032
  **When to use config_schema vs storage:**
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  ROOT_FILES,
3
3
  SKILL_FILES,
4
4
  installSkillFiles
5
- } from "./chunk-SSYJXSJX.js";
5
+ } from "./chunk-QXQNAYHN.js";
6
6
  export {
7
7
  ROOT_FILES,
8
8
  SKILL_FILES,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zaflun/lumio-agent-skill",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "AI agent skill files for building Lumio Extensions",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",