feat: improve grouping perf in edgeless (#14442)

fix #14433 

#### PR Dependency Tree


* **PR #14442** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
  * Level-of-detail thumbnails for large images.
  * Adaptive pacing for snapping, distribution and other alignment work.
  * RAF coalescer utility to batch high-frequency updates.
  * Operation timing utility to measure synchronous work.

* **Improvements**
* Batch group/ungroup reparenting that preserves element order and
selection.
  * Coalesced panning and drag updates to reduce jitter.
* Connector/group indexing for more reliable updates, deletions and
sync.
  * Throttled viewport refresh behavior.

* **Documentation**
  * Docs added for RAF coalescer and measureOperation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
DarkSky
2026-02-15 03:17:22 +08:00
committed by GitHub
parent c0694c589b
commit 25227a09f7
33 changed files with 2169 additions and 159 deletions
@@ -235,6 +235,69 @@ describe('connector', () => {
expect(model.getConnectors(id2)).toEqual([]);
});
test('should update endpoint index when connector retargets', () => {
const id = model.addElement({
type: 'shape',
});
const id2 = model.addElement({
type: 'shape',
});
const id3 = model.addElement({
type: 'shape',
});
const connectorId = model.addElement({
type: 'connector',
source: {
id,
},
target: {
id: id2,
},
});
const connector = model.getElementById(connectorId)!;
expect(model.getConnectors(id).map(c => c.id)).toEqual([connector.id]);
expect(model.getConnectors(id2).map(c => c.id)).toEqual([connector.id]);
model.updateElement(connectorId, {
source: {
id: id3,
},
target: {
id: id2,
},
});
expect(model.getConnectors(id)).toEqual([]);
expect(model.getConnectors(id3).map(c => c.id)).toEqual([connector.id]);
expect(model.getConnectors(id2).map(c => c.id)).toEqual([connector.id]);
});
test('getConnectors should purge stale connector ids from endpoint cache', () => {
const shapeId = model.addElement({
type: 'shape',
});
const surfaceModel = model as any;
surfaceModel._connectorIdsByEndpoint.set(
shapeId,
new Set(['missing-connector-id'])
);
surfaceModel._connectorEndpoints.set('missing-connector-id', {
sourceId: shapeId,
targetId: null,
});
expect(model.getConnectors(shapeId)).toEqual([]);
expect(
surfaceModel._connectorIdsByEndpoint
.get(shapeId)
?.has('missing-connector-id') ?? false
).toBe(false);
expect(surfaceModel._connectorEndpoints.has('missing-connector-id')).toBe(
false
);
});
test('should return null if connector are deleted', async () => {
const id = model.addElement({
type: 'shape',