Import it from @grafloria/engine.
tsabstract class DiagramEntity
Properties
| Name | Type | Default | Description |
|---|---|---|---|
id | string | ||
uuid | string | ||
version | number | 1 | |
metadata | Map<string, any> |
Methods
constructor(id?: string, uuid?: string)get isDirty(): boolean— Check if entity has been modified since last rendermarkDirty(reason?: string): void— Mark entity as dirty (needs re-render)markClean(): void— Mark entity as clean (rendered)getDirtyReasons(): string[]— Get reasons why entity is dirtygetDirtyTimestamp(): number | null— Get timestamp when entity was marked dirtybeginBatch(): void— Begin batch update Delays dirty marking until batch endsendBatch(): void— End batch update Marks dirty once if any changes occurredisBatching(): boolean— Check if currently in batch modereportRegisterWrite(property: string, oldValue: unknown, newValue: unknown): void— REPORT A REGISTER WRITE THE COLLAB REDUCER JUST PERFORMED.
trackChange() is protected, and applyOp lives outside the class — so every
register the reducer wrote by plain field assignment (anything with no dedicated
mutator: a link's style, flexConfig, a group's isCollapsed, layoutConfig,
bounds, parentId, …) mutated the model WITHOUT passing the funnel.
That is not a cosmetic gap. UndoStack.undo() applies the inverse THROUGH THE MODEL
WITH CAPTURE LIVE and then reads back whatever capture minted. No trackChange means
no op, which means THE UNDO REACHES NOBODY. Measured before this existed:
link style execute → 1 op undo → 0 ops flexConfig execute → 1 op undo → 0 ops isCollapsed execute → 1 op undo → 0 ops position execute → 1 op undo → 1 op ← a real mutator: always worked
The author's own document was correct every time — which is exactly why this
survived. It is the UpdateLinkStyleCommand defect (see style-undo.spec.ts) living
one layer down, in the reducer, and hitting every mutator-less register at once.
So the reducer gets a seam onto the funnel rather than a copy of it. There is still ONE funnel; this is a door onto it, not a second source of truth.
on(event: string, handler: Function): () => void— Subscribe to changesonPropertyChange(property: string, handler: (data: any) => void): () => void— Subscribe to property changesgetChangeLog(): ReadonlyArray<ChangeEntry>— Get change historyclearChangeLog(): void— Clear change historygetLabel(): string | undefined— CANONICAL label read.metadata.labelwins; legacydata['label'](on subclasses that carry a data bag) is honoured as a fallback so pre-canon documents and third-party writers still surface their labels everywhere — rendering, a11y names, and Mermaid export alike.setLabel(label: string): void— CANONICAL label write. Writesmetadata.label(tracked → dirty → repaint) and mirrors into the legacydata['label']slot when the entity has a data bag, so any reader still on the old field sees the same value. The mirror is a shadow: metadata is authoritative on every read path.setMetadata(key: string, value: any): void— Set metadata valuegetMetadata(key: string): any— Get metadata valuedeleteMetadata(key: string): void— Remove metadataabstract serialize(): SerializedEntity— Serialize entity to JSONisDisposed(): boolean— Check if entity has been disposeddispose(): void— Dispose entity and clean up resources Prevents memory leaks by:- Removing all event listeners
- Clearing change log
- Clearing metadata
- Marking as disposed
Was this page helpful?