agent-panel
Original:🇺🇸 English
Translated
Workbench agent panel system — ef-edit CustomEvent pipeline, registry roll-up, selector grouping, and element property schema. Use when adding new GUI edit capture points, expanding the inspector schema, or continuing development of the EFAgentPanel feature.
4installs
Sourceeditframe/skills
Added on
NPX Install
npx skill4agent add editframe/skills agent-panelTags
Translated version includes tags in frontmatterSKILL.md Content
View Translation Comparison →Agent Panel
The captures CustomEvents dispatched whenever a user makes a meaningful composition edit in the GUI, deduplicates them by element+property via a keyed registry, groups them by selector, and renders a copy-able coding-agent prompt.
EFAgentPanelef-editEvent Pipeline
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 buttonEFWorkbenchaddEventListener('ef-edit', ...)ef-editKey Types (editEvents.ts
)
editEvents.tsts
// 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 }Registry key format — determines deduplication:
- Property change:
selector::prop:propName - Move/resize/rotate: ,
selector::move,selector::resizeselector::rotate
Pattern: Adding a New ef-edit Dispatch Point
ts
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(),
}));buildPropertyEditEvent(element, property, oldValue, newValue)EFInspector.tsFinding Current Dispatch Points
bash
# 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.tsWhat is NOT Wired (intentional)
Playback controls (play/pause/seek), pan-zoom/zoom level, and toolbar settings are display-only state — they should not produce events. Hierarchy reorder ( from ) would require a new type; not yet implemented.
ef-edithierarchy-reorderEFHierarchyElementReorderedOperationShadow DOM Event Routing Gotcha
trim-change-endcomposed: trueEFTrimHandlesTrackItem.render()@trim-change-endef-trim-handlesEFVideoTrackrender()ef-trim-handlesrender()ef-trim-handles@trim-change@trim-change-endTrackItem.render()render()Element Property Schema (elementPropertySchema.ts
)
elementPropertySchema.tsStatic descriptors drive the inspector UI. Register new element types at the bottom via . Use the factory functions:
SCHEMA_REGISTRY- — time values (stored as
timeDescriptor(attr, label, opts)attributes)${ms}ms enumDescriptor(attr, label, options, condition?)boolDescriptor(attr, label, condition?)numberDescriptor(attr, label, opts)stringDescriptor(attr, label)
Elements with no registry entry are invisible to the inspector. Check for the test pattern.
elementPropertySchema.test.tsTesting ef-edit Events
Unit tests (Node/vitest): use for helpers — , , , .
editEvents.tseditChangeKeyrollUpEditsgroupEditsBySelectorbuildAgentPromptBrowser tests (Chromium/vitest): required for any test involving DOM events or shadow DOM.
For trim ef-edit, dispatch directly on the host element — do not try to simulate full pointer events through shadow DOM layers, as pointer capture mechanics are unreliable in test environments:
trim-change-endef-trim-handlests
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,
}));For loop ef-edit, click the loop button via .
shadowRoot.querySelector("button[title='Loop']").click()Listen at level — is , so it propagates from any component to .
documentef-editbubbles: true, composed: truedocument