关于 vvvv-fileformat
vvvv-fileformat 是用于理解和编辑 vvvv gamma 的 .vl 可视化数据流文档的技能,适合需要维护或以程序方式修改 Patch 的用户。它强调保留 XML 结构、ID、节点布局和链接语义,并提供可复用的 Python 编辑器及 MCP/CLI 适配器。
主要功能
- 01说明 .vl 文档结构
- 02指导节点、引脚与链接编辑
- 03维护 ID 和布局信息
- 04提供 Python 编辑器
- 05包含校验规则与编辑建议
适用场景
- 01修改现有 vvvv gamma Patch
- 02验证 .vl 文档及链接
- 03以程序方式构建数据流
- 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.mdformat-reference.mdhelp-patch-authoring.mdlocal-access.mdprogrammatic-editing.mdscripts/vl-patch-manipulator/.mcp.jsonpyproject.tomlREADME.mdscripts/vl-patch-manipulator/.codex-plugin/plugin.jsonscripts/vl-patch-manipulator/src/vl_patch_manipulator/__init__.pybenchmark.pycatalog.pycli.pydiagnostics.pydocument.pyids.pyoperations.pyserver.pysession.pyspatial.pyscripts/vl-patch-manipulator/tests/test_feedback.pytest_framework.pytest_generator_layout_rules.pytest_input_placements.pytest_peer_order_and_names.pytest_process_orphans.pytest_processes.pytest_server.pytest_slots.pytest_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 | 用途 |
|---|---|---|
p | property | 必需。 复杂属性作为子元素(<p:NodeReference>) |
r | reflection | 可选。仅在使用 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>
验证规则
- 所有 ID 都必须唯一(22 字符的 base62 GUID)
xmlns:p="property"必须位于Document上- 始终需要
Version="0.128" - 片段
Patch必须引用现有的同级 Patch ID - Link
Ids:至少包含两个非空数据中心 ID——源在前,接收端在最后;保留中间的数据中心 CanvasType="FullCategory"仅用于根画布- 每个文档都需要
VL.CoreLib依赖项 - Application 节点是入口点(Name="Application", ContainerDefinition);Process Application 必须有同级
Create+Update补丁,并为两者启用 ProcessDefinition 片段 - 元素名称区分大小写(
Patch而非patch) isIOBox中的i必须小写- 依赖项是
Document的子元素,而非Patch的子元素 - 仓库自有文本采用 UTF-8 编码,且不带字节顺序标记
- 公共引脚及其顺序必须来自实时/反射目录或生产签名,不能凭空创建
- 可见的帮助说明使用 String Pad 注解;XML 注释不是画布帮助内容
- 链接端点类型必须兼容。在经过验证的 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 编译。