原生视图与组件
原生渲染器、组件目录、快照/补丁协议与视频组件。
插件界面由 IDE 的原生渲染器(native.*)与原生组件节点组成,均通过快照/补丁协议同步。插件端使用 SDK 的类型化 facade(self.views.*)或组件构建器(pyrite_sdk.api.components),IDE 端用 Flutter 渲染。
渲染器目录
[[contributes.views]] 的 renderer 决定 IDE 如何渲染该视图:
| 渲染器 | SDK facade | 数据节点 | 用途 |
|---|---|---|---|
native.form | self.views.form() | FormField | 表单、配置面板 |
native.tree | self.views.tree() | TreeItem | 文件树、结构树 |
native.virtualList | self.views.virtual_list() | VirtualListItem | 大规模虚拟列表 |
native.table | self.views.table() | TableRowItem / TableColumn | 数据表格 |
native.markdown | self.views.markdown() | MarkdownContent | 文档视图 |
native.log | self.views.log() | LogEntry | 日志、串口输出 |
native.outline | self.views.outline() | OutlineItem | 大纲、符号列表 |
native.variableInspector | self.views.variable_inspector() | VariableEntry / VariableScope | 设备变量检查器 |
Manifest 的 renderer 只接受注册表中的 native.* 值。其他值会在清单校验阶段以稳定错误码拒绝。
组件目录
pyrite_sdk.api.components 提供声明式组件构建器(c.Row、c.TextField、c.Button 等),组件是普通字典,序列化为快照/补丁节点,由 IDE 的组件注册表校验(COMPONENT_SCHEMA_VERSION = 1)。
| 分类 | 组件 |
|---|---|
| 布局 | Row、Column、Flex、Grid、Wrap、SplitView、Tabs / Tab、Section、Toolbar |
| 文本与展示 | Text、Icon、Image、Video、Markdown、CodeBlock、Badge |
| 输入 | TextField、NumberField、Select、Checkbox、Switch、Slider |
| 菜单与对话框 | Menu、MenuBar、ContextMenu、Dropdown、Dialog、Tooltip |
| 操作 | Button、IconButton |
| 数据 | VirtualList、TreeView、DataTable、PropertyGrid |
组件事件通过 ide.view.event 帧投递,绑定方式为构造参数(on_press=、on_change=)或 .on(event, handler)。
媒体与资源
Image、Video 等媒体组件的 src 必须使用 self.resources.asset("assets/...") 生成的 PluginResource(plugin-resource:///...)。路径必须是 assets/ 下的相对路径,禁止 ..、前导 / 或反斜杠。
IDE 端视频组件由 features/plugin_view/plugin_video_player.dart 的 PluginVideoPlayer 实现,并基于 video_player 提供 play、pause、seek、volume、speed、loop 和 fullscreen 控制:
video = c.Video(
self.resources.asset("assets/demo.mp4"),
id="demo",
width=480,
height=270,
autoplay=True,
show_controls=True,
)
video.controller.play() # VideoController.play()
video.controller.seek_to(3000) # 跳转到 3000ms快照/补丁协议
每个视图实例维护一个 ViewModel,采用 revision 跟踪:
- 首次发送完整快照(
sdk.view.snapshot)。 - 之后发送增量补丁(
sdk.view.patch),每个补丁携带baseRevision与目标revision。 - 同一时刻最多一个补丁在途;等待确认期间产生的操作合并进下一个补丁。
- IDE 确认(
ide.view.ack)后 revision 前移;拒绝(ide.view.nack)时插件自动resync()重发快照。
with view.model.batch():
view.model.insert("a", {"label": "A"})
view.model.update("b", {"label": "B"})
view.model.move("c", 0)协议限制
| 限制 | 值 |
|---|---|
MAX_SNAPSHOT_NODES | 20 000 节点 / 快照 |
MAX_PATCH_OPS | 2 000 操作 / 补丁 |
MAX_VIEW_PAYLOAD_BYTES | 2 MB / 快照或补丁负载 |
超过限制会在插件侧抛出 ViewProtocolError,不会发送到 IDE。补丁负载过大时 SDK 会按 1/2 递归收缩操作数。
Tree / List / Table 在 IDE 端使用虚拟化与稳定 ID,因此增量补丁基于稳定的节点 ID 定位,而不是行号。
组件树页面结构
AppBar、Scaffold 和 Canvas 在 lib/core/sdk/component_schema.dart 中注册,由 lib/features/plugin_view/component_builder.dart 构建。
AppBar使用 40 像素工具栏高度,只接受IconButton、Menu和Dropdown子组件。组件树构建器没有 Manifest 标题菜单上下文,因此这里只渲染组件树自身的操作。Scaffold将第一个AppBar子节点作为顶部栏,其他子节点作为正文。正文放在Expanded中,保证滚动组件得到有限主轴约束。Canvas由lib/features/plugin_view/plugin_canvas.dart实现。持久基础层来自props.ops,命令式调用写入仅存在于PluginCanvasState的覆盖层。
Canvas 在宿主侧维护图片缓存、几何命中索引和视口变换。平移、缩放、悬停高亮、拖动预览及框选不需要插件参与;只有订阅的 tap、drag、hover、pointer 事件会发送到 SDK。高频事件使用合并节流,基础层和覆盖层分别隔离保存栈,无法配对的 restore 不会影响组件外部的 Flutter Canvas。
sdk.view.component.invoke 的 Canvas 分支提供 push_ops、set_ops、clear、clear_layer 和 hit_test。SDK 在 src/pyrite_sdk/api/components.py 中对绘制调用执行 2 MiB 参数检查,src/pyrite_sdk/api/canvas.py 生成冻结 schema 对应的操作字典。跨仓协议样例位于 test/fixtures/protocol/protocol_v1_components.json,IDE 与 SDK 必须使用内容一致的副本。
渲染路径与同步边界
plugin_view_surface.dart 根据快照首节点选择渲染路径。带 type 的组件树交给 ComponentBuilder;普通数据节点交给 Manifest 指定的 native.* 渲染器。两类节点不能放进同一个模型。
渲染器 facade 会插入协议角色节点。appBarAction、appBarMenu、placeholder、viewConfig 和 contextMenuProvider 由宿主消费,不属于插件业务数据。修改渲染器时,要保留角色分流及稳定 ID 语义。
ViewModelStore 以插件 ID、会话 ID、视图 ID 和实例 ID 隔离模型。侧边栏实例采用 container:<containerId>,标签页采用宿主分配的 tab:<n>。修订断层或缺少快照会触发 nack 和 resync;非法操作与已关闭视图只返回 nack。恢复投递与隐藏视图重新可见时,SDK 发送完整快照,宿主不能依赖命令式覆盖层继续存在。
导航栏滚动
Desktop 与 Tablet 的侧栏导航由 lib/features/function_page.dart 构建。NavigationRail 外层依次使用 LayoutBuilder、SingleChildScrollView、ConstrainedBox 和 IntrinsicHeight:
LayoutBuilder读取可用高度。ConstrainedBox将最小高度限制为视口高度,使短内容仍填满侧栏。IntrinsicHeight允许NavigationRail在目的项过多时按内容增长。SingleChildScrollView在内容超过视口时提供滚动。
尾部操作仍作为 NavigationRail.trailing 布局在内容末端。调整侧栏结构时,要同时验证短列表的底部位置与长列表的滚动可达性,并覆盖 Desktop 和 Tablet 两处分支。
更多信息
- SDK 侧完整 API 见 SDK API — view、native_views、components。
- Manifest 中视图贡献与渲染器声明见 Manifest v2。