mcpField
V
vvvv-fileformat

vvvv-fileformat

github.com/tebjan开发3477 次安装2 天前更新
代码仓库

关于 vvvv-fileformat

vvvv-fileformat 是用于理解和编辑 vvvv gamma 的 .vl 可视化数据流文档的技能,适合需要维护或以程序方式修改 Patch 的用户。它强调保留 XML 结构、ID、节点布局和链接语义,并提供可复用的 Python 编辑器及 MCP/CLI 适配器。

主要功能

  1. 01说明 .vl 文档结构
  2. 02指导节点、引脚与链接编辑
  3. 03维护 ID 和布局信息
  4. 04提供 Python 编辑器
  5. 05包含校验规则与编辑建议

适用场景

  1. 01修改现有 vvvv gamma Patch
  2. 02验证 .vl 文档及链接
  3. 03以程序方式构建数据流
  4. 04排查节点或执行结构问题

MCP 相关

该技能包含匹配的 MCP 适配器,可将其编辑能力提供给 MCP 客户端;同时包含 CLI 适配器。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/tebjan/vvvv-skills --skill vvvv-fileformat

安装后在 Claude Code 中输入/vvvv-fileformat确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

best-practices.md
format-reference.md
help-patch-authoring.md
local-access.md
programmatic-editing.md
scripts/vl-patch-manipulator/
.mcp.json
pyproject.toml
README.md
scripts/vl-patch-manipulator/.codex-plugin/
plugin.json
scripts/vl-patch-manipulator/src/vl_patch_manipulator/
__init__.py
benchmark.py
catalog.py
cli.py
diagnostics.py
document.py
ids.py
operations.py
server.py
session.py
spatial.py
scripts/vl-patch-manipulator/tests/
test_feedback.py
test_framework.py
test_generator_layout_rules.py
test_input_placements.py
test_peer_order_and_names.py
test_process_orphans.py
test_processes.py
test_server.py
test_slots.py
test_spatial_scopes.py

详细介绍

VL 文件格式(.vl)

概述

.vl 文件是一个 XML 文档,用于编码 vvvv gamma 的可视化数据流程序。关键元素:

  • 文档 — 根元素,包含依赖项和一个顶层 Patch
  • Patch — 可视化元素的容器(Node、Pad、Link、Canvas)
  • Node — 操作调用、类型定义或区域
  • Pin — Node 上的输入/输出(在定义处定义)
  • Pad — 可视化数据元素(IOBox),用于显示/编辑值
  • Link — 通过引用端点的 ID 来连接两个端点
  • Canvas — 可视化分组容器(无逻辑作用域)
  • ProcessDefinition — 通过 Fragments 定义生命周期(Create、Update)
  • Slot — 类型定义中的状态字段

编辑现有文档时,应保留未知 XML、声明顺序、ID、节点宽度和现有几何布局,除非所请求的更改要求移动它们。XML 有效本身并不能证明节点表面、Pin 顺序或布局正确。

此技能包含可复用的 Python 编辑器及相应的 MCP/CLI 适配器。 首次使用时,如果过去 7 天内没有成功完成针对整个集合的上游检查,请遵循共享维护政策。有选择地比较上游内容;切勿覆盖项目自有扩展。贡献已验证的通用发现时,应附上版本、复现步骤和发布授权;渲染器特定事实应保留在所属项目中。已安装的副本只能通过其获授权的安装/更新工作流更新。

XML 根节点和命名空间

<?xml version="1.0" encoding="utf-8"?>
<Document xmlns:p="property" xmlns:r="reflection" Id="C2vqbtoStWoOI1eKIKZBBM"
          LanguageVersion="2024.6.0" Version="0.128">
  
</Document>
前缀URI用途
pproperty必需。 复杂属性作为子元素(<p:NodeReference>)
rreflection可选。仅在使用 r:IsNull="true" 表示显式 null 值时需要

根属性:Id(base62 GUID)、LanguageVersion(例如 "2024.6.0")、Version(始终为 "0.128")。

ID 系统

每个元素都有唯一的 Id —— 一个使用 [0-9A-Za-z] 的22 字符 VL 编码 GUID。文档中的所有 ID 都必须唯一。可通过 GUIDEncoders.GuidTobase62(Guid.NewGuid()) 或操作器的 new_vl_id() 生成。VL 的编码并不是对一个 128 位整数进行普通的 base62 编码:它使用字母表 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789、Guid.ToByteArray() 字节顺序,以及两个各含 11 个字符、分别编码的 64 位半部。源代码位于匹配的 vvvv 源代码检出中的 VL.Lang/src/ImmutableModel/Internal/GUIDEncoders.cs;不要仅凭 22 字符的形状臆造 ID 编码方案。

Link 的 Ids 是由逗号分隔的两个或多个数据集线器 ID 序列。VL 的 Link.SourceId 是第一个,SinkId 是最后一个;中间的 ID 是有效的路由数据集线器,并非额外或格式错误的 Link。未完成的编辑器拖动可能会生成空的或只有一端的 <Link>;vlpatch validate 会拒绝这种 Link。只有在确认预期端点后,才使用操作器的显式命令 apply --remove-incomplete-links --connect SOURCE_ID TARGET_ID;切勿根据距离推断缺失端点。不要仅因为三 ID Link 不是一对 ID,就将其丢弃。在 vvvv 中重新打开 Patch,确认每个新 ProcessNode 的输出确实到达渲染器组:仅 XML 解析成功并不能证明它确实参与了渲染。

元素层级

Document
├── NugetDependency (0..n)
├── DocumentDependency (0..n)
├── PlatformDependency (0..n)
└── Patch (exactly 1, top-level)
    ├── Canvas (DefaultCategory, CanvasType="FullCategory")
    └── Node (Name="Application")
        └── Patch (inner)
            ├── Canvas (CanvasType="Group")
            │   ├── Node (operation calls)
            │   ├── Pad (IOBoxes)
            │   └── ...
            ├── Patch (Name="Create")
            ├── Patch (Name="Update")
            ├── ProcessDefinition
            │   ├── Fragment → Create patch
            │   └── Fragment → Update patch
            └── Link (0..n)

重要:Dependencies 是 Document 的直接子元素,而不是 Patch 的子元素。

依赖项

<NugetDependency Id="..." Location="VL.CoreLib" Version="2024.6.0" />
<DocumentDependency Id="..." Location="./MyOtherFile.vl" />
<PlatformDependency Id="..." Location="VL.Core.dll" />

几乎每个文档都需要 VL.CoreLib。使用 IsForward="true" 可将类型重新导出给使用方。

NodeReference 系统(Choices)

<p:NodeReference> 属性定义 Node 的类型。它包含用于标识目标符号的 <Choice> 元素。

操作调用
<p:NodeReference LastCategoryFullName="Primitive.Math" LastDependency="CoreLibBasics.vl">
  <Choice Kind="NodeFlag" Name="Node" Fixed="true" />
  <Choice Kind="OperationCallFlag" Name="+" />
</p:NodeReference>
  • 首选:使用 Kind="NodeFlag" 和 Fixed="true"(形状指示器)
  • 次选:ProcessAppFlag(有状态)或 OperationCallFlag(无状态)

自定义 Process/节点注册名称使用 PascalCase,不含空格。保留实际的内置/导入名称。对本地辅助 Process 使用 rename_local_process,以便其定义和匹配的调用选择器同步更改;仅更改显示标签不会重命名已注册的节点。请参阅语义编辑。

LastCategoryFullName/LastDependency 是最近使用的编辑器提示,并非完整的重载选择器。保留有效调用中实际保存的 Choices、CategoryReference 以及任何 PinReference 选择器。自适应调用和显式重载调用属于不同的契约;不要不加区分地添加/删除 Fixed。关于标量 Cons 与序列 Concat、Spread-to-memory 自适应调用和单独的 MutableArray 重载,请阅读集合维度和自适应转换调用。

类型定义

<Choice Kind="ContainerDefinition" Name="Process" />
<CategoryReference Kind="Category" Name="Primitive" />


<Choice Kind="ClassDefinition" Name="Class" />
<Choice Kind="RecordDefinition" Name="Record" />
<Choice Kind="InterfaceDefinition" Name="Interface" />
<Choice Kind="ForwardDefinition" Name="Forward" />
区域
<Choice Kind="StatefulRegion" Name="Region (Stateful)" Fixed="true" />
<CategoryReference Kind="Category" Name="Primitive" />
<Choice Kind="ApplicationStatefulRegion" Name="If" />  

区域将 StatefulRegion 用作第一个 Choice(而非 NodeFlag)。If/ForEach 使用 ApplicationStatefulRegion,Cache 使用 ProcessStatefulRegion。

节点元素

<Node Name="MyNode" Bounds="300,200,65,19" Id="...">
  <p:NodeReference>...</p:NodeReference>
  <Pin Id="..." Name="Input" Kind="InputPin" />
  <Pin Id="..." Name="Output" Kind="OutputPin" />
</Node>

关键属性:Id、Name、Bounds("X,Y" 或 "X,Y,W,H")、Summary、Tags。

Pin 元素

<Pin Id="..." Name="Value" Kind="InputPin" DefaultValue="42" />
<Pin Id="..." Name="Result" Kind="OutputPin" />

Kind 值:InputPin、OutputPin、StateInputPin、StateOutputPin、ApplyPin。

可见性:Visible(默认)、Optional、OnCreateDefault、Hidden。

Pad 元素(IOBox)

<Pad Id="..." Bounds="200,160,80,20" ShowValueBox="true" isIOBox="true" Value="3.14"
     Comment="My Value">
  <p:TypeAnnotation LastCategoryFullName="Primitive" LastDependency="CoreLibBasics.vl">
    <Choice Kind="TypeFlag" Name="Float32" />
  </p:TypeAnnotation>
</Pad>

注意 isIOBox 中的小写 i。常见类型:Boolean、Int32、Float32、Float64、String、Vector2、Vector3。

注释 Pad
<Pad Id="..." Bounds="100,100,400,25" ShowValueBox="true" isIOBox="true"
     Value="Title text here">
  <p:TypeAnnotation><Choice Kind="TypeFlag" Name="String" /></p:TypeAnnotation>
  <p:ValueBoxSettings>
    <p:fontsize p:Type="Int32">9</p:fontsize>
    <p:stringtype p:Assembly="VL.Core" p:Type="VL.Core.StringType">Comment</p:stringtype>
  </p:ValueBoxSettings>
</Pad>

可见标题使用相同结构,并将 fontsize 设为 15。可点击的 URL 使用相同的 String Pad,并将 stringtype 设为 Link。

Link 元素

<Link Id="..." Ids="outputPinId,inputPinId" />

Ids 格式:"sourceId,sinkId" 或 "sourceId,intermediateHubId,sinkId"(也可以是更长的路由)——源在前,汇在后。参考链接使用 IsHidden="true"。IsFeedback="true" 并非适用于任意节点链接的延迟:检查过的编译器会跳过该链接的赋值;它不会创建已存储状态。首先检查实际的摄像头/输入契约。如果渲染器已将窗口输入关联到其连接的摄像头,则不需要反向链接或 Slot。对于需要显式窗口输入反馈的摄像头,请复制经过验证的 Slot 模式:Process 的内部 Patch 上有一个 Slot,两个单独的读/写 pad 共用其 SlotId,并使用普通链接。读 pad 为摄像头提供输入;窗口则为写 pad 提供输入。不要闭合直接的摄像头/窗口循环,然后仅仅将其标记为反馈。

ProcessDefinition 和 Fragments

<Patch Id="innerPatchId">
  <Canvas Id="..." CanvasType="Group" />
  <Patch Id="createId" Name="Create" />
  <Patch Id="updateId" Name="Update" />
  <ProcessDefinition Id="...">
    <Fragment Id="..." Patch="createId" Enabled="true" />
    <Fragment Id="..." Patch="updateId" Enabled="true" />
  </ProcessDefinition>
</Patch>

片段的 Patch 属性引用同级 <Patch> 元素的 Id。

对于容器为 Process 的 Application,此生命周期块是必需的,即使 Create 和 Update 为空也不例外。单独的 Group 画布只是可显示的 XML:vvvv 可以打开它并绘制每个节点和链接,但它没有执行入口,因此整个数据流会一直呈灰色且不会运行。验证器和生成器必须要求两个已命名的同级 patch,以及分别以这两个 patch 为目标的已启用片段。

控制流区域

区域使用 4 值边界("X,Y,W,H"),并在边界处设有 ControlPoint:

<Node Bounds="100,200,400,300" Id="...">
  <p:NodeReference LastCategoryFullName="Primitive" LastDependency="Builtin">
    <Choice Kind="StatefulRegion" Name="Region (Stateful)" Fixed="true" />
    <CategoryReference Kind="Category" Name="Primitive" />
    <Choice Kind="ApplicationStatefulRegion" Name="If" />
  </p:NodeReference>
  <Patch Id="...">
    <Canvas Id="..." CanvasType="Group"></Canvas>
    <Patch Id="thenId" Name="Then" />
    <Fragment Id="..." Patch="thenId" Enabled="true" />
  </Patch>
  <ControlPoint Id="..." Bounds="150,200" Alignment="Top" />
  <ControlPoint Id="..." Bounds="150,500" Alignment="Bottom" />
</Node>

区域补丁名称:If 使用 Then/Else,ForEach 使用 Create/Update/Dispose,Cache 使用 Create/Update。

ControlPoint 是双向区域门户,并非传统的输入引脚。 链接可以从区域外部合法地指向它,并从区域内部以它为源(区域输出则相反)。对于 CPU 实例化器变换,请使用内置的 PatchSession.add_matrix_repeat_builder(...):它会创建标准的 Cache/Repeat/MutableArray 结构,使用同一个计数进行分配和迭代,并返回稳定的 ReadOnlyMemory<Matrix> 端点。 它的位置输入是一个转发到 Repeat 的 XyZ.Input 的 Vector2 spread,而不是直接连接到 TransformSRT 平移。它的标量 Y 和缩放端点彼此独立。请保留明确的 MutableArray<T> -> ReadOnlyMemory<T> 重载引用;仅凭结构性 XML 验证无法证明 vvvv 类型解析成功。

TypeAnnotation


<p:TypeAnnotation>
  <Choice Kind="TypeFlag" Name="Float32" />
</p:TypeAnnotation>


<p:TypeAnnotation LastCategoryFullName="Collections" LastDependency="VL.Collections.vl">
  <Choice Kind="TypeFlag" Name="Spread" />
  <p:TypeArguments>
    <TypeReference LastCategoryFullName="Color" LastDependency="CoreLibBasics.vl">
      <Choice Kind="TypeFlag" Name="RGBA" />
    </TypeReference>
  </p:TypeArguments>
</p:TypeAnnotation>

Slot 元素(状态字段)

对于共享资源,多个访问器 Pad 可以在本地读取同一个具名 Slot。 Slot 是真实状态:初始化、读写编译顺序和资源生命周期仍然重要。仅凭其放置位置,不能保证产生一帧延迟。请使用 PatchSession.add_slot_read(...),而不要重复创建资源生成器。

在本地 Process 中,若要重复放置同一个输入,请使用 PatchSession.add_input_placement(...)。它会创建一个新的 ControlPoint,并通过隐藏的引用链接连接到现有签名 Pin,而不是另一个参数或虚构的 DefinitionId。请将读取节点放在其使用者旁边,并将相关的创意控件放在一起。请参阅 programmatic-editing.md。

<Slot Id="..." Name="MyField">
  <p:TypeAnnotation><Choice Kind="TypeFlag" Name="Float32" /></p:TypeAnnotation>
  <p:Value>0.5</p:Value>
</Slot>

完整的最小示例

<?xml version="1.0" encoding="utf-8"?>
<Document xmlns:p="property" Id="A1b2C3d4E5f6G7h8I9j0Kl"
          LanguageVersion="2024.6.0" Version="0.128">
  <NugetDependency Id="B2c3D4e5F6g7H8i9J0k1Lm" Location="VL.CoreLib"
                   Version="2024.6.0" />
  <Patch Id="C3d4E5f6G7h8I9j0K1l2Mn">
    <Canvas Id="D4e5F6g7H8i9J0k1L2m3No" DefaultCategory="Main"
            BordersChecked="false" CanvasType="FullCategory" />
    <Node Name="Application" Bounds="100,100" Id="E5f6G7h8I9j0K1l2M3n4Op">
      <p:NodeReference>
        <Choice Kind="ContainerDefinition" Name="Process" />
        <CategoryReference Kind="Category" Name="Primitive" />
      </p:NodeReference>
      <Patch Id="F6g7H8i9J0k1L2m3N4o5Pq">
        <Canvas Id="G7h8I9j0K1l2M3n4O5p6Qr" CanvasType="Group" />
        <Patch Id="H8i9J0k1L2m3N4o5P6q7Rs" Name="Create" />
        <Patch Id="I9j0K1l2M3n4O5p6Q7r8St" Name="Update" />
        <ProcessDefinition Id="J0k1L2m3N4o5P6q7R8s9Tu">
          <Fragment Id="K1l2M3n4O5p6Q7r8S9t0Uv"
                   Patch="H8i9J0k1L2m3N4o5P6q7Rs" Enabled="true" />
          <Fragment Id="L2m3N4o5P6q7R8s9T0u1Vw"
                   Patch="I9j0K1l2M3n4O5p6Q7r8St" Enabled="true" />
        </ProcessDefinition>
      </Patch>
    </Node>
  </Patch>
</Document>

验证规则

  1. 所有 ID 都必须唯一(22 字符的 base62 GUID)
  2. xmlns:p="property" 必须位于 Document 上
  3. 始终需要 Version="0.128"
  4. 片段 Patch 必须引用现有的同级 Patch ID
  5. Link Ids:至少包含两个非空数据中心 ID——源在前,接收端在最后;保留中间的数据中心
  6. CanvasType="FullCategory" 仅用于根画布
  7. 每个文档都需要 VL.CoreLib 依赖项
  8. Application 节点是入口点(Name="Application", ContainerDefinition);Process Application 必须有同级 Create + Update 补丁,并为两者启用 ProcessDefinition 片段
  9. 元素名称区分大小写(Patch 而非 patch)
  10. isIOBox 中的 i 必须小写
  11. 依赖项是 Document 的子元素,而非 Patch 的子元素
  12. 仓库自有文本采用 UTF-8 编码,且不带字节顺序标记
  13. 公共引脚及其顺序必须来自实时/反射目录或生产签名,不能凭空创建
  14. 可见的帮助说明使用 String Pad 注解;XML 注释不是画布帮助内容
  15. 链接端点类型必须兼容。在经过验证的 gamma 版本中,Integer32 和 Integer32 (Unsigned) 不可隐式互换:被拒绝的链接不会传递任何值。可能需要更正带类型的 IOBox 的 TypeAnnotation,使其符合实际的使用者契约,而不是新建 Link 表示。Shader 工厂根据实际反射规则确定引脚类型;请比对 shader 签名与实时节点元数据。更改引脚类型时,请一并迁移受影响的 IOBox/使用者。编辑器的链接类型诊断只检查目录中已知的端点类型;当实际签名不可用时,它不会发出提示。

常见错误

  • 忘记声明 xmlns:p="property" 命名空间
  • 将依赖项放在 Patch 中,而不是 Document 中
  • Link 方向颠倒(第一个 ID 必须是源/输出)
  • 认为可见的 Application Group 画布可执行,却遗漏其 Create/Update patch 或启用的 ProcessDefinition 片段
  • Bounds 格式错误(使用逗号,不加空格:"100,200,65,19")
  • 使用 IsIOBox 而不是 isIOBox
  • 将节点中心视为引脚位置
  • 按字母顺序排列引脚,或将主输出放在诊断输出之后
  • 对已经手动排列的 patch 运行整篇文档布局
  • 需要视觉 Overlay 时却使用控制流区域,反之亦然
  • 将可见注释写成 XML ``
  • 将 GridSpread (2D) 的 Width 视为最外侧单元格中心之间的距离。采用居中对齐时,Center 是网格中点,单元格中心在每个轴上的间距为 Width / Count。若要对齐一个 3×2 网格,使位置为 x = -1.6、0、1.6,z = 0、-2.5,请使用 Center = (0, -1.25)、Width = (4.8, 5.0)、Count = (3, 2)。请根据渲染后的 patch 验证,而不只是检查 XML 链接。

如需查看包含所有属性、Choice 类型和序列化细节的完整元素参考,请参阅 format-reference.md。 如需了解布局、精确的引脚锚点、节点尺寸以及保留人工调整的编辑规则,请在更改坐标前阅读 best-practices.md。

对于包含本地 Process 定义的文档,请使用 layout_analysis(canvas=...) 和 render_svg(..., canvas=...) 检查每个关联 Canvas 的几何布局。 不得将独立辅助节点的坐标视为同一全局画布上的坐标。 移动重复的输入位置和 Slot pad 时,必须保留其双坐标 Bounds; 还必须在碰撞检查中纳入它们的标签。请参阅 programmatic-editing.md。 如需了解帮助 patch 的分类、可见注释、叠加层、Help Browser 清单、相邻资源和验证,请阅读 help-patch-authoring.md。 如需了解如何在自动编辑中保留信息,以及仓库的持久 patch 操作器,请阅读 programmatic-editing.md。Python edit_batch、MCP vl_edit 和 CLI vlpatch edit 共用一套可发现的语义契约。使用 vl_capabilities / vlpatch capabilities 获取当前参数,使用 vl_canvases 获取作用域 ID;新增创作 API 时,必须在同一次更改中将其暴露到全部三个接口,并完成奇偶性测试。已在运行的 MCP 进程需要重启或重新连接,才能加载新增工具。仅进行文件验证并不等于实时 vvvv 编译。

评论

支持 Markdown