Token导航 LogoToken导航TokenDH.com
开发需要联网clawhub未标认证来源可访问clear审计通过

vvvv-channelsvvvv 频道

Agent Skill

vvvv-channels 用于补充开发相关能力,适合在 OpenClaw 中需要让 Agent 承接开发相关任务时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

8,911

周安装

364

GitHub Stars

公开资料未说明

下载量

2,883
OpenClaw

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

MIT-0

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:vvvv-channels(vvvv 频道)
来源仓库:https://github.com/tebjan/vvvv-channels
安装命令:
openclaw skills install vvvv-channels
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 OpenClaw 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

ClawHubOpenClaw
openclaw skills install vvvv-channels

简介

帮助理解 C# 中 vvvv gamma 通道系统,包括 IChannelHub 与分层数据传播机制。

  • 适合图形编程开发场景,可优化节点间通信与实时数据流处理。
  • 使用时需结合具体项目结构配置公共通道与订阅关系。
  • 通过 clawhub 安装,专为 OpenClaw 宿主设计,需熟悉 vvvv gamma 基础概念。
  • 注意通道生命周期管理不当可能导致内存泄漏,建议遵循无状态操作原则。

SKILL.md

name
vvvv-channels
description
Helps work with vvvv gamma's Channel system from C# — IChannelHub, public channels, [CanBePublished] attributes, hierarchical data propagation, channel subscriptions, bang channels, and spread sub-channels. Use when reading or writing public channels from C# nodes, publishing .NET types as channels, working with IChannelHub, subscribing to channel changes, managing hierarchical channel state, or implementing reactive/observable data flow. Trigger for any mention of IChannel, IChannelHub, reactive binding, observable state, two-way data binding, or TryGetChannel in a vvvv context.
license
CC-BY-SA-4.0
compatibility
Designed for coding AI agents assisting with vvvv gamma development
metadata
author
Tebjan Halm
version
1.1

vvvv gamma Channels — C# Integration

What Are Channels

Channels are named, typed, observable value containers — the central reactive data flow mechanism in vvvv gamma. Any code (patches, C# nodes, external bindings) can read and write channels by their string path.

Key properties:

  • Each channel has a path (string), a type, and a current value
  • Setting a value fires all subscribers (reactive push)
  • vvvv provides built-in channel bindings for MIDI, OSC, Redis, and UI
  • Channels persist state across sessions

Public Channels and IChannelHub

Public channels are channels registered in the app-wide channel hub — accessible by any code via string path lookup.

Core API (VL.Core.Reactive)

using VL.Core.Reactive;

// Get the app-wide channel hub (singleton)
var hub = IChannelHub.HubForApp;

// Safe lookup — returns null if channel doesn't exist yet
IChannel<object>? ch = hub.TryGetChannel("MyApp.Settings.Volume");

// Read the current value
object? value = ch.Object;

// Write a new value (fires all subscribers)
ch.Object = newValue;

CRITICAL: NEVER use hub.TryAddChannel() — it creates channels with null values, which causes NullReferenceException in vvvv's SubChannelsBinding.EnsureMutatingPropertiesAreReflectedInChannels. The SubChannel system tries to walk properties of the null value and crashes. Always use TryGetChannel (lookup only).

[CanBePublished] Attribute

For vvvv to expose .NET type properties as public channels, the type must be decorated with [CanBePublished(true)] from VL.Core.EditorAttributes.

using VL.Core.EditorAttributes;

// All public properties become channels when this type is published
[CanBePublished(true)]
public class MyModel
{
    // Standard .NET types work directly — float, bool, string, Vector3, etc.
    public float Volume { get; set; } = 0.5f;
    public bool Muted { get; set; } = false;
    public string Label { get; set; } = "Default";

    // Hidden from the channel system entirely
    [CanBePublished(false)]
    public string InternalId { get; } = Guid.NewGuid().ToString();
}

Rules:

  • [CanBePublished(true)] on a class/struct → all properties are published as channels
  • [CanBePublished(false)] on an individual property → hides it from the channel system
  • .NET types are NOT published by default — the attribute is required
  • Available from VL.Core version 2025.7.1-0163+

Channel Path Conventions

Channels use dot-separated hierarchical paths. Spread elements use bracket notation:

Root.Page.Zone.Group.Parameter          — leaf parameter
Root.Page.Zone                          — hierarchy node (model object)
Root.Page.Items[0].PropertyName         — spread element sub-channel
Root.Page.Items[2].DeleteInstance        — indexed bang channel

Sub-channels are created automatically by vvvv's SubChannel system when a type with [CanBePublished(true)] is published. You don't create them manually.

Use const string path constants to avoid typos:

public static class ChannelPaths
{
    public const string Volume = "Settings.Audio.Volume";
    public const string Brightness = "App.Scene.Display.Brightness";
}

Retry-Bind Pattern

Channels may not exist when your node starts — vvvv publishes them after model initialization. You must retry each frame until the channel appears:

[ProcessNode]
public class MyChannelReader : IDisposable
{
    private IChannel<object>? _channel;

    public void Update(out float value)
    {
        // Retry until channel exists
        if (_channel == null)
        {
            var hub = IChannelHub.HubForApp;
            if (hub != null)
                _channel = hub.TryGetChannel("Settings.Audio.Volume");
        }

        // Read value (with safe cast)
        value = _channel?.Object is float f ? f : 0f;
    }

    public void Dispose() { _channel = null; }
}

Once bound, the cached reference is valid for the node's lifetime — no need to re-lookup.

PublicChannelHelper — Reusable Utility

A helper class that encapsulates the retry-bind + optional subscription pattern. Reusable in any project:

public class PublicChannelHelper : IDisposable
{
    private IChannel<object>? _channel;
    private IDisposable? _subscription;
    private readonly Action<object?>? _onNext;

    public PublicChannelHelper(Action<object?>? onNext = null)
    {
        _onNext = onNext;
    }

    public IChannel<object>? Channel => _channel;
    public bool IsBound => _channel != null;

    public bool TryBind(IChannelHub hub, string path)
    {
        if (_channel != null) return true;

        var ch = hub.TryGetChannel(path);
        if (ch == null) return false;

        _channel = ch;
        if (_onNext != null)
            _subscription = ch.Subscribe(new CallbackObserver(_onNext));
        return true;
    }

    public void Dispose()
    {
        _subscription?.Dispose();
        _subscription = null;
        _channel = null;
    }

    private sealed class CallbackObserver : IObserver<object?>
    {
        private readonly Action<object?> _onNext;
        public CallbackObserver(Action<object?> onNext) => _onNext = onNext;
        public void OnNext(object? value) => _onNext(value);
        public void OnError(Exception error) { }
        public void OnCompleted() { }
    }
}

Usage in a ProcessNode:

[ProcessNode]
public class VolumeReader : IDisposable
{
    private readonly PublicChannelHelper _ch = new();

    public void Update(out float value, out IChannel<object>? channel)
    {
        if (!_ch.IsBound)
        {
            var hub = IChannelHub.HubForApp;
            if (hub != null) _ch.TryBind(hub, "Settings.Audio.Volume");
        }
        value = _ch.Channel?.Object is float f ? f : 0f;
        channel = _ch.Channel;
    }

    public void Dispose() => _ch.Dispose();
}

Channel Accessor Node Patterns

Three patterns for ProcessNodes that wrap channel access:

Hierarchy Node (reads a model object)

[ProcessNode]
public class Camera : IDisposable
{
    private readonly PublicChannelHelper _ch = new();

    public void Update(out CameraSettings? value, out IChannel<object>? channel)
    {
        if (!_ch.IsBound)
        {
            var hub = IChannelHub.HubForApp;
            if (hub != null) _ch.TryBind(hub, "App.Scene.Camera");
        }
        value = _ch.Channel?.Object as CameraSettings;
        channel = _ch.Channel;
    }

    public void Dispose() => _ch.Dispose();
}

Leaf Parameter Node (reads a typed value)

Same pattern as hierarchy node, but the output type is a standard leaf value like float, bool, or string.

Indexed Spread Element Node (takes int index)

[ProcessNode]
public class SceneItemAccessor : IDisposable
{
    private PublicChannelHelper _ch = new();
    private int _boundIndex = -1;

    public void Update(out SceneItem? value, out IChannel<object>? channel, int index = 0)
    {
        // Rebind when index changes
        if (index != _boundIndex)
        {
            _ch.Dispose();
            _ch = new();
            _boundIndex = index;
        }
        if (!_ch.IsBound)
        {
            var hub = IChannelHub.HubForApp;
            if (hub != null) _ch.TryBind(hub, $"App.Effects[{index}]");
        }
        value = _ch.Channel?.Object as SceneItem;
        channel = _ch.Channel;
    }

    public void Dispose() => _ch.Dispose();
}

Reactive Subscriptions

Subscribe to channel value changes using the standard IObservable pattern:

IChannel<object>? ch = hub.TryGetChannel("Settings.Audio.Volume");
if (ch != null)
{
    IDisposable subscription = ch.Subscribe(new CallbackObserver(value =>
    {
        // Called whenever the channel value changes
        if (value is float f)
            ApplyVolume(f);
    }));

    // ALWAYS dispose when done (in node's Dispose method)
}

Bang Channels

For trigger/event properties (delete, insert, move operations), use System.Reactive.Unit — NOT float:

using System.Reactive;

[CanBePublished(true)]
public class MyInstance
{
    public Unit DeleteInstance { get; set; }     // Bang channel
    public Unit InsertAfterInstance { get; set; } // Bang channel
}

The event IS the bang — the value is irrelevant. Subscribe and act on the callback:

var ch = hub.TryGetChannel("Project.Items[0].DeleteInstance");
ch?.Subscribe(new BangObserver(() =>
{
    // Triggered when the bang fires — queue the action
    _pendingDeletions.Add(0);
}));

Unit channels show as "Unit" type in vvvv's node browser, not "Float".

Hierarchical Data Propagation

vvvv's channel system automatically propagates changes through the hierarchy:

  • Write a root record to its channel → all child channels auto-update
  • Write a leaf channel → parent channels auto-update
  • This is built into vvvv's SubChannel system — no manual propagation needed

This enables efficient bulk operations:

// Save: read the root channel → serialize the entire hierarchy
var model = rootChannel.Object as AppModel;
string json = JsonSerializer.Serialize(model);

// Load: deserialize → write to root channel → ALL children update
var loaded = JsonSerializer.Deserialize<AppModel>(json);
rootChannel.Object = loaded;  // Every sub-channel updates automatically

Spread Channels and Sub-Channels

When a Spread<T> property is published (where T has [CanBePublished(true)]), vvvv automatically creates sub-channels for each element:

Project.Items           → Spread<ItemModel>
Project.Items[0]        → ItemModel (auto-created)
Project.Items[0].Name   → string (auto-created)
Project.Items[1].Name   → string (auto-created)

Setting the spread channel propagates to all sub-channels automatically. No need to update individual sub-channels.

When modifying a spread (add/remove/reorder elements):

// Build new spread
var newSpread = modifiedList.ToArray().AsSpreadUnsafe();

// Set on spread channel — sub-channels update automatically
spreadChannel.Object = newSpread;

Warning: Spread.AsSpreadUnsafe(array) wraps without copying — the array must NOT be mutated after.

Feedback loop prevention: When writing to a channel you're also subscribed to, use a suppression flag:

_suppressCallback = true;
_channel.Object = newValue;
_suppressCallback = false;

// In the subscription callback:
void OnChanged(object? value)
{
    if (_suppressCallback) return;
    // Process the change...
}

Critical Rules

  1. NEVER TryAddChannel — only use TryGetChannel (lookup-only)
  2. Always retry-bind — channels appear after model initialization, not immediately
  3. [CanBePublished(true)] required on .NET types for channel publication
  4. Always dispose subscriptions — in the node's Dispose() method
  5. System.Reactive.Unit for bangs — not float or bool
  6. Spread.AsSpreadUnsafe — array must not be mutated after wrapping
  7. Suppression flags — prevent feedback loops when writing to subscribed channels

For code examples, see examples.md.

适合场景

01

OpenClaw 用户查找和安装 Skill 时

02

用户想查找某类 Agent Skill 时

03

需要根据任务场景推荐可安装能力包时

04

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

补充不同宿主或平台的使用分布数据

能力 5

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

OpenClaw

80.96%
按下载量换算2,334

安全审计

VirusTotal

通过

ClawScan

通过

Static analysis

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills