スキル一覧に戻る
Nymfarious

creating-widgets

by Nymfarious

StickerNest V3 - A sticker canvas application

0🍴 0📅 2025年12月28日
GitHubで見るManusで実行

SKILL.md


name: creating-widgets description: Creating new StickerNest widgets from scratch. Use when the user asks to create a widget, build a widget, add a new widget, make a widget component, or implement widget functionality. Covers manifest creation, HTML templates, TypeScript module structure, Protocol v3.0 communication, and widget registration.

Creating StickerNest Widgets

This skill guides you through creating new widgets for StickerNest v2. Widgets are self-contained HTML/JS applications that run in sandboxed iframes and communicate via the postMessage API (Protocol v3.0).

Widget Types

StickerNest supports two widget implementation approaches:

1. Inline HTML Widgets (Most Common)

TypeScript module that exports manifest + html string. Located in src/widgets/builtin/.

2. React Component Widgets (Advanced)

TypeScript/React component with separate manifest JSON. Located in src/widgets/ subdirectories.


Quick Start: Creating an Inline Widget

Step 1: Create the Widget File

Create src/widgets/builtin/MyWidget.ts:

import type { WidgetManifest } from '../../types/manifest';
import type { BuiltinWidget } from './index';

export const MyWidgetManifest: WidgetManifest = {
  id: 'stickernest.my-widget',        // MUST be lowercase with hyphens
  name: 'My Widget',
  version: '1.0.0',
  kind: 'display',                     // See widget kinds below
  entry: 'index.html',
  description: 'Brief description for AI context',
  author: 'StickerNest',
  tags: ['category', 'feature'],
  inputs: {
    // Input ports - see connecting-widget-pipelines skill
  },
  outputs: {
    // Output ports - see connecting-widget-pipelines skill
  },
  capabilities: {
    draggable: true,
    resizable: true,
    rotatable: true,
  },
  io: {
    inputs: ['trigger.activate'],      // AI wiring hints
    outputs: ['data.result'],
  },
  size: {
    width: 200,
    height: 150,
    minWidth: 100,
    minHeight: 80,
    scaleMode: 'scale',
  },
};

export const MyWidgetHTML = `
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    html, body {
      width: 100%;
      height: 100%;
      overflow: hidden;
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    }
    .container {
      width: 100%;
      height: 100%;
      display: flex;
      align-items: center;
      justify-content: center;
      background: var(--sn-bg-primary, #0f0f19);
      color: var(--sn-text-primary, #e2e8f0);
      padding: 12px;
    }
  </style>
</head>
<body>
  <div class="container" id="container">
    <!-- Widget content here -->
  </div>
  <script>
    (function() {
      const API = window.WidgetAPI;

      // State
      let state = {};

      // Initialize from saved state
      API.onMount(function(context) {
        state = context.state || {};
        // Initialize UI
        API.log('MyWidget mounted');
      });

      // Handle inputs
      API.onInput('trigger.activate', function(value) {
        // Handle trigger
      });

      // Handle state changes
      API.onStateChange(function(newState) {
        state = { ...state, ...newState };
        // Update UI
      });

      // Cleanup
      API.onDestroy(function() {
        API.log('MyWidget destroyed');
      });
    })();
  </script>
</body>
</html>
`;

export const MyWidget: BuiltinWidget = {
  manifest: MyWidgetManifest,
  html: MyWidgetHTML,
};

Step 2: Register the Widget

Add to src/widgets/builtin/index.ts:

import { MyWidget } from './MyWidget';

export const BUILTIN_WIDGETS: Record<string, BuiltinWidget> = {
  // ... existing widgets
  'stickernest.my-widget': MyWidget,
};

// Also add to re-exports at bottom
export { MyWidget };

Widget Manifest Reference

Required Fields

FieldTypeDescription
idstringUnique ID: lowercase, alphanumeric, hyphens. Format: stickernest.widget-name
namestringDisplay name for UI
versionstringSemantic version (X.Y.Z)
kindWidgetKindWidget category (see below)
entrystringEntry file, usually index.html
inputsRecordInput port definitions
outputsRecordOutput port definitions
capabilitiesobject{ draggable, resizable, rotatable }

Widget Kinds

KindUse Case
displayText, images, static content
interactiveButtons, forms, user input
containerGroups other widgets
2d2D graphics, canvas
3dThree.js, 3D content
audioAudio players, visualizers
videoVideo players
hybridMultiple content types

Optional Fields

{
  description?: string;           // For AI context
  tags?: string[];                // Search/categorization
  author?: string;                // Creator info
  io?: WidgetCapabilityDeclaration; // AI wiring hints
  size?: WidgetSizeConfig;        // Default dimensions
  events?: { emits?: string[]; listens?: string[] }; // Broadcast events
  skin?: WidgetSkinManifest;      // Theming support
}

Size Configuration

size: {
  width: 200,           // Default width
  height: 150,          // Default height
  minWidth: 100,        // Minimum width
  minHeight: 80,        // Minimum height
  maxWidth?: 800,       // Maximum width
  maxHeight?: 600,      // Maximum height
  aspectRatio?: 16/9,   // Lock aspect ratio
  lockAspectRatio?: true,
  scaleMode: 'scale' | 'crop' | 'stretch' | 'contain',
}

WidgetAPI Reference

The window.WidgetAPI object provides the interface for widget communication:

Lifecycle

API.onMount(callback)      // Called when widget loads, receives context with state
API.onDestroy(callback)    // Called before widget unloads

State Management

API.setState({ key: value })           // Save persistent state
API.onStateChange(callback)            // Listen for external state changes

Input/Output (Pipelines)

API.onInput('portId', callback)        // Listen for pipeline inputs
API.emitOutput('portId', data)         // Emit to pipeline outputs

Events (Broadcast)

API.emit('eventName', payload)         // Emit broadcast event
API.on('eventName', callback)          // Listen for broadcast events

Utilities

API.log(message)                       // Debug logging
API.getAssetUrl(path)                  // Resolve asset URLs

Theme Tokens

Use StickerNest CSS variables for consistent theming:

/* Backgrounds */
--sn-bg-primary: #0f0f19;
--sn-bg-secondary: #1a1a2e;
--sn-bg-tertiary: #252538;

/* Text */
--sn-text-primary: #e2e8f0;
--sn-text-secondary: #94a3b8;

/* Accents */
--sn-accent-primary: #8b5cf6;
--sn-success: #22c55e;
--sn-error: #ef4444;
--sn-warning: #f59e0b;

/* Borders */
--sn-border-primary: rgba(139, 92, 246, 0.2);
--sn-radius-sm: 4px;
--sn-radius-md: 6px;
--sn-radius-lg: 8px;

Common Patterns

Editable Content

API.onInput('content.set', function(value) {
  state.content = value;
  updateDisplay();
  API.setState({ content: value });
  API.emitOutput('content.changed', value);
});

Click Handling

element.addEventListener('click', function() {
  API.emit('clicked', { timestamp: Date.now() });
  API.emitOutput('ui.clicked', {});
});

Loading External Data

API.onMount(async function(context) {
  try {
    const response = await fetch(API.getAssetUrl('data.json'));
    const data = await response.json();
    // Use data
  } catch (err) {
    API.log('Failed to load data: ' + err.message);
  }
});

Validation Checklist

Before registering a new widget:

  • id is lowercase with hyphens only (e.g., stickernest.my-widget)
  • version follows semver (X.Y.Z)
  • All required manifest fields present
  • Input/output port IDs match between manifest and code
  • Uses window.WidgetAPI for communication
  • Has onMount and onDestroy lifecycle handlers
  • Uses CSS variables for theming
  • Added to BUILTIN_WIDGETS registry
  • Added to re-exports

Reference Files

  • Manifest types: src/types/manifest.ts
  • Widget registry: src/widgets/builtin/index.ts
  • Example widget: src/widgets/builtin/BasicTextWidget.ts
  • Protocol docs: Docs/WIDGET-DEVELOPMENT.md
  • Full spec: Docs/WIDGET-PROTOCOL-SPEC.md

スコア

総合スコア

50/100

リポジトリの品質指標に基づく評価

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

レビュー

💬

レビュー機能は近日公開予定です