npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

headless-vpl

v0.3.0

Published

A headless library for building visual programming languages

Readme


特徴

🧩 Headless — UI を持たない。React、Vue、Svelte、バニラ DOM、何でも使える。

🎯 VPL 特化 — 汎用キャンバスライブラリではない。VPL のために設計された API。

📦 Pure TypeScript — ゼロ依存。完全な型安全。バニラ TS で書かれている。

🔬 Simple > Easy — ブラックボックスを作らない。全てを理解し制御できる。


Blockly・ReactFlow との比較

| | Headless VPL | Blockly | ReactFlow | | -------------- | -------------------------------- | ---------------------- | ------------------------ | | VPL タイプ | ブロック + フロー + ハイブリッド | ブロックのみ | フローのみ | | フレームワーク | 何でも(バニラ TS) | なし(独自レンダラー) | React のみ | | レンダリング | 自由(DOM オーバーレイ) | Blockly レンダラー固定 | React コンポーネント固定 | | 型安全 | TypeScript ジェネリクス完全対応 | JSON/文字列ベース | 部分的 | | デザイン自由度 | 完全 | 制限あり | コンポーネントレベル | | API スタイル | 薄くコンポーザブル | 大きく独自的 | イベント駆動・暗黙的 |

コード削減実績

| 機能 | 従来 | Headless VPL | 削減率 | | ----------------- | ----- | ------------ | -------- | | レイアウト構築 | 71 行 | 22 行 | -69% | | ドラッグ&ドロップ | 38 行 | 8 行 | -78% | | スタイリング | 7 行 | 1 行 | -85% |


Quick Start

npm install headless-vpl

最小構成 — ドラッグ可能な 2 ノードをエッジで接続:

import {
  Workspace,
  Container,
  Connector,
  Edge,
  Position,
  SvgRenderer,
  InteractionManager,
  DomSyncHelper,
  bindWheelZoom,
  bindDefaultShortcuts,
} from 'headless-vpl'
import { getMouseState } from 'headless-vpl/util/mouse'
import { animate } from 'headless-vpl/util/animate'

// 1. ワークスペース + レンダラー
const workspace = new Workspace()
const svg = document.querySelector('#workspace') as SVGSVGElement
new SvgRenderer(svg, workspace)

// 2. ノード作成
const nodeA = new Container({
  workspace,
  position: new Position(100, 50),
  name: 'nodeA',
  width: 160,
  height: 60,
  children: {
    output: new Connector({ position: new Position(160, -30), name: 'out', type: 'output' }),
  },
})

const nodeB = new Container({
  workspace,
  position: new Position(400, 50),
  name: 'nodeB',
  width: 160,
  height: 60,
  children: {
    input: new Connector({ position: new Position(0, -30), name: 'in', type: 'input' }),
  },
})

// 3. エッジで接続
new Edge({ start: nodeA.children.output, end: nodeB.children.input, edgeType: 'bezier' })

// 4. インタラクション(DnD + パン + マーキー選択 + リサイズ)
const canvasEl = svg.parentElement as HTMLElement
const containers = [nodeA, nodeB]

const interaction = new InteractionManager({
  workspace,
  canvasElement: canvasEl,
  containers: () => containers,
})

const mouse = getMouseState(canvasEl, {
  mousedown: (_bs, mp, ev) => interaction.handlePointerDown(mp, ev),
  mouseup: (_bs, mp) => interaction.handlePointerUp(mp),
})

// 5. ホイールズーム + キーボードショートカット
bindWheelZoom(canvasEl, { workspace })
bindDefaultShortcuts({ workspace, element: document.body, containers: () => containers })

// 6. アニメーションループ
animate(() => {
  interaction.tick(mouse.mousePosition, mouse.buttonState)
})

コアコンセプト: 4 つの型

全ての VPL は 4 つの普遍的パターンに分解できる:

| パターン | 内容 | 対応 API | | -------- | ----------------------------------------- | ----------------------------------------------------- | | 描画 | レスポンシブサイジング、オートレイアウト | Container, AutoLayout, SvgRenderer | | 接続 | コネクター、エッジ、親子関係 | Connector, Edge, SnapConnection | | 移動 | ドラッグ&ドロップ、スナップ、グループ移動 | DragAndDrop, SnapConnection, InteractionManager | | 入力 | テキスト、数字、トグル、スライダー | 開発者の DOM(フレームワーク非依存) |

単方向データフロー

フロントエンド(React / Vue / vanilla)
  → マウス/キーボードイベント
    → Headless VPL コア(Workspace, Container, Edge)
      → EventBus 通知
        → SvgRenderer(デバッグ用ワイヤーフレーム)
        → 開発者の DOM コンポーネント(本番 UI)

Headless アーキテクチャ

SVG ワイヤーフレームでヒット検知とデバッグを行い、実際の UI は DOM を上に被せるだけ:

┌─ Canvas ─────────────────────────┐
│  SVG layer (wireframe, invisible)│  ← ヒット検知、デバッグ
│  DOM overlay (your components)   │  ← ユーザーに見える UI
└──────────────────────────────────┘

DomSyncHelper で Headless 座標系と DOM 位置を自動同期。


主要 API

全 API の詳細は docs/api-reference.md を参照。

コア

| API | 説明 | | ---------------------------------------------------- | -------------------------------------------------------- | | Workspace | ルートコンテナ。ビューポート・選択・履歴・イベントを管理 | | Container<T> | 主要構成要素。コネクターや AutoLayout を型付きで保持 | | Connector | 入出力接続ポイント | | Edge | コネクター間の接続線。4 種のパスアルゴリズム | | AutoLayout | CSS Flexbox ライクな自動レイアウト |

// ノードの定義
const node = new Container({
  workspace,
  position: new Position(100, 50),
  name: 'myNode',
  width: 200,
  height: 80,
  widthMode: 'hug', // コンテンツにフィット
  children: {
    input: new Connector({ position: new Position(0, -40), name: 'in', type: 'input' }),
    output: new Connector({ position: new Position(200, -40), name: 'out', type: 'output' }),
  },
})
node.children.input // 型安全にアクセス

イベント

| API | 説明 | | -------------------------------------------------------------- | ------------------------------------------- | | EventBus | パブリッシュ/サブスクライブイベントシステム | | SelectionManager | 選択状態管理 |

const unsub = workspace.on('move', (event) => console.log(event.target))
workspace.selection.select(node)
workspace.selection.deselectAll()

履歴

| API | 説明 | | ---------------------------------------------------------------------------------------------------------------------- | ---------------------- | | History | Undo/Redo スタック | | MoveCommand | 移動記録 | | AddCommand / RemoveCommand | 追加/削除記録 | | ConnectCommand | 接続記録 | | NestCommand | ネスト記録 | | BatchCommand | 複数コマンドをまとめる |

workspace.history.execute(new MoveCommand(element, 0, 0, 100, 100))
workspace.history.undo()
workspace.history.redo()

レンダリング

| API | 説明 | | ---------------------------------------------------- | ----------------------------------------- | | SvgRenderer | デバッグ用 SVG ワイヤーフレームレンダラー |

new SvgRenderer(svgElement, workspace)

ユーティリティ

| API | 説明 | | -------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | | InteractionManager | DnD・パン・マーキー・リサイズ・Edge 作成を統合管理 | | DragAndDrop | ドラッグ&ドロップ | | SnapConnection | スナップ接続管理 | | EdgeBuilder | ドラッグで Edge を作成 | | NestingZone | AutoLayout へのネスト管理 | | DomSyncHelper | Container → DOM 位置同期 | | bindWheelZoom | ホイールズーム | | bindDefaultShortcuts | 標準キーボードショートカット | | observeContentSize | DOM サイズ → Container 自動同期 | | KeyboardManager | カスタムキーバインド | | computeAutoPan | 端ドラッグ時の自動パン | | detectResizeHandle | リサイズハンドル検出・適用 | | createMarqueeRect | マーキー範囲選択 | | snapToGrid | グリッドスナップ | | copyElements / pasteElements | コピー/ペースト | | screenToWorld / worldToScreen | 座標変換 |

// InteractionManager ですべてを統合
const interaction = new InteractionManager({
  workspace,
  canvasElement: canvasEl,
  containers: () => containers,
  connectors: () => connectors,
  snapConnections: [snapConn],
  nestingZones: zones,
  edgeBuilder,
})

型一覧

全量は docs/api-reference.md を参照。

| 型 | 説明 | | --------------------- | -------------------------------------------------------------------------------- | | IWorkspaceElement | ワークスペース要素のインターフェース | | IEdge | エッジのインターフェース | | IPosition | { x: number, y: number } | | Viewport | { x: number, y: number, scale: number } | | VplEvent | { type, target, data? } | | VplEventType | 'move' \| 'connect' \| 'disconnect' \| ... | | SizingMode | 'fixed' \| 'hug' \| 'fill' | | EdgeType | 'straight' \| 'bezier' \| 'step' \| 'smoothstep' | | Command | { execute(): void, undo(): void } | | InteractionMode | 'idle' \| 'panning' \| 'dragging' \| 'marquee' \| 'resizing' \| 'edgeBuilding' | | ConnectionValidator | () => boolean | | SnapStrategy | (source, target, dragContainers) => boolean |


レシピ

各種 VPL のコード例は docs/recipes.md を参照。


Claude Code スキル

Claude Code を使った開発を支援するスキル(スラッシュコマンド)を同梱しています。

| コマンド | 説明 | 使用例 | | --------------------- | ----------------------------------------------------- | ------------------------------------------ | | /create-vpl | 要件に基づいて headless-vpl を使った VPL コードを生成 | /create-vpl Scratchのようなブロック型VPL | | /headless-vpl-guide | ライブラリの API・アーキテクチャに関する質問に回答 | /headless-vpl-guide DnDの設定方法 | | /debug-vpl | 典型的な問題の診断と解決策を提示 | /debug-vpl ドラッグが動かない |


ドキュメント

詳細なドキュメントは docs-site を参照してください。


開発

npm install && npm run dev     # 開発サーバー起動
npm run build                  # プロダクションビルド
npm run build:lib              # ライブラリビルド
npm run test                   # テスト実行
npm run lint                   # リント
npm run typecheck              # 型チェック
npm run docs:dev               # ドキュメントサイト起動

コントリビューション

コントリビューションを歓迎しています!詳細は CONTRIBUTING.md を参照してください。


ライセンス

MIT