关于 agent-panel
Agent Panel 是用于 GUI 合成编辑器中 `ef-edit` 事件流程的技能。它收集并去重有意义的编辑,按选择器分组,并生成可复制的编码 Agent 提示词。
主要功能
- 01捕获 `ef-edit` 事件
- 02按元素和属性去重编辑
- 03按选择器整理编辑
- 04生成可复制的编码 Agent 提示词
- 05说明事件路由和测试模式
适用场景
- 01为编码 Agent 汇总 GUI 合成编辑
- 02避免重复记录属性变更
- 03按编辑元素生成分组提示词
- 04测试 DOM 和 Shadow DOM 编辑事件
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ editframe/ skills --skill agent-panel安装后在 Claude Code 中输入/agent-panel确认已加载。
详细介绍
Agent 面板
EFAgentPanel 会捕获用户在 GUI 中进行有意义的组合编辑时分发的 ef-edit CustomEvents,通过带键注册表按元素和属性去重,按选择器分组,并渲染可复制的编码 Agent 提示词。
事件流水线
GUI interaction (canvas drag, inspector change, trim drag, loop toggle)
└─ component.dispatchEvent(createEditCustomEvent(editEvent))
└─ [bubbles: true, composed: true]
└─ EFWorkbench listener → agentPanel.addEdit(event)
└─ Map<editChangeKey, EditEvent> registry (deduplicates)
└─ groupEditsBySelector() → buildAgentPrompt() → copy button
EFWorkbench 会在自身注册 addEventListener('ef-edit', ...)——因为 ef-edit 会冒泡且是 composed 的,所以任何插槽中的组件(时间线、层级结构、画布)都可以分发它,无需特殊路由。
关键类型(editEvents.ts)
// EditEvent — what the panel accumulates
interface EditEvent {
operation: EditOperation;
description: string; // human sentence
selector: string; // CSS path from composition root
elementHtml: string; // cleaned outerHTML snippet
timestamp: number;
}
// Operation union — extend here when adding new interaction types
type EditOperation =
| { type: "element-property-changed"; elementId; tagName; label; property; oldValue; newValue }
| { type: "element-moved"; elementId; tagName; label; fromX; fromY; toX; toY }
| { type: "element-resized"; elementId; tagName; label; x; y; width; height }
| { type: "element-rotated"; elementId; tagName; label; rotation }
注册表键格式——决定去重方式:
- 属性变更:
selector::prop:propName - 移动/调整大小/旋转:
selector::move、selector::resize、selector::rotate
模式:添加新的 ef-edit 分发点
import {
createEditCustomEvent, buildSelectorPath, getElementHtml,
buildEditDescription, type ElementPropertyChangedOperation,
} from "../../editEvents.js";
const oldValue = element.someProperty;
element.someProperty = newValue;
const op: ElementPropertyChangedOperation = {
type: "element-property-changed",
elementId: el.id,
tagName: el.tagName.toLowerCase(),
label: el.textContent?.trim().slice(0, 30) || el.id || el.tagName.toLowerCase(),
property: "attr-name", // attribute name as used in HTML source
oldValue,
newValue,
};
this.dispatchEvent(createEditCustomEvent({
operation: op,
description: buildEditDescription(op),
selector: buildSelectorPath(el),
elementHtml: getElementHtml(el),
timestamp: Date.now(),
}));
EFInspector.ts 中的 buildPropertyEditEvent(element, property, oldValue, newValue) 是用于简单属性编辑的简写形式。
查找当前分发点
# Every call site that produces an ef-edit event
rg 'createEditCustomEvent' elements/packages/elements/src/
# Track subclasses that override render() — each must bind @trim-change-end
rg 'override render' elements/packages/elements/src/gui/timeline/tracks/
# Registered element schemas
rg 'SCHEMA_REGISTRY' elements/packages/elements/src/gui/elementPropertySchema.ts
尚未接入的部分(有意为之)
播放控件(播放/暂停/跳转)、平移缩放/缩放级别以及工具栏设置都属于仅用于显示的状态,不应产生 ef-edit 事件。层级结构重新排序(来自 EFHierarchy 的 hierarchy-reorder)需要新增 ElementReorderedOperation 类型;目前尚未实现。
Shadow DOM 事件路由注意事项
trim-change-end 会从 EFTrimHandles 冒泡,且 composed: true。TrackItem.render() 会在 ef-trim-handles 元素上绑定 @trim-change-end。**EFVideoTrack 会完全覆盖 render(),并提供自己的 ef-trim-handles 绑定。**今后任何覆盖 render() 且包含 ef-trim-handles 的轨道子类,都必须同时绑定 @trim-change 和 @trim-change-end——覆盖 render() 后不会继承基类 TrackItem.render() 的处理器。
元素属性架构(elementPropertySchema.ts)
静态描述符用于驱动检查器 UI。通过在底部使用 SCHEMA_REGISTRY 注册新的元素类型。使用工厂函数:
timeDescriptor(attr, label, opts)——时间值(以${ms}ms属性存储)enumDescriptor(attr, label, options, condition?)boolDescriptor(attr, label, condition?)numberDescriptor(attr, label, opts)stringDescriptor(attr, label)
未在注册表中登记的元素不会显示在检查器中。请参阅 elementPropertySchema.test.ts 了解测试模式。
测试 ef-edit 事件
单元测试(Node/vitest):用于测试 editEvents.ts 辅助函数——editChangeKey、rollUpEdits、groupEditsBySelector、buildAgentPrompt。
浏览器测试(Chromium/vitest):任何涉及 DOM 事件或 shadow DOM 的测试都必须使用。
对于 trim ef-edit,直接在 ef-trim-handles 宿主元素上分发 trim-change-end——不要尝试通过 shadow DOM 层模拟完整的指针事件,因为在测试环境中,指针捕获机制并不可靠:
const trimHandles = track.shadowRoot?.querySelector("ef-trim-handles");
trimHandles?.dispatchEvent(new CustomEvent("trim-change-end", {
detail: { elementId: video.id, type: "start" },
bubbles: true, composed: true,
}));
对于 loop ef-edit,通过 shadowRoot.querySelector("button[title='Loop']").click() 点击循环按钮。
在 document 层级监听——ef-edit 的属性为 bubbles: true, composed: true,因此它会从任何组件传播到 document。