Skip to content

feat(broadcast): add getSelection/setSelection and selectionUpdate - #330

Open
jtj70 wants to merge 1 commit into
gephi:mainfrom
jtj70:feat/broadcast-selection-api
Open

feat(broadcast): add getSelection/setSelection and selectionUpdate#330
jtj70 wants to merge 1 commit into
gephi:mainfrom
jtj70:feat/broadcast-selection-api

Conversation

@jtj70

@jtj70 jtj70 commented Jul 26, 2026

Copy link
Copy Markdown

This adds the two remaining broadcast methods listed in the TODO checklist in packages/broadcast/src/types.ts: getSelection / setSelection, plus a selectionUpdate event so external callers can react to selection changes made inside the app (clicks, marquee, etc.), not just ones they initiated themselves.

What's included

  • New SerializedSelectionState { nodeIds: string[]; edgeIds: string[] } wire type, independent of the internal SelectionState (which can only be nodes or edges at a time). deserializeSelection documents the precedence rule for the case where both arrays are non-empty: nodeIds wins, since Gephi Lite cannot represent a simultaneous nodes+edges selection today.
  • BroadcastClient binds to the existing selectionAtom and only emits selectionUpdate when the effective selection actually changes. Atoms compare by reference, so a content-level check (selectionStatesAreEqual) was needed to avoid firing on every no-op select()/setSelection() call, and to avoid echo-loops when a caller sets back a selection it just received.
  • setGraphDataset now prunes the current selection against the new dataset (pruneSelectionToGraph), keeping ids that still exist and clearing the rest. Previously a dataset swap over broadcast could leave a stale selection referencing nodes/edges that no longer exist.
  • setSelection prunes unknown ids the same way, so a caller can't pollute the internal state with ids that don't exist in the current graph.
  • Small, necessary side fix: ITypedEventEmitter's listener typing assumed every event was argument-less (true for the only existing event, newInstance). Generalized it via a new EventListener<F> helper so a listener can correctly receive an event's data, typed from its declared return type — needed because selectionUpdate is the first event to actually carry a payload.

Tests

packages/gephi-lite/src/core/broadcast/client.spec.ts, 4 cases:

  • getSelection returns the current selection
  • setSelection changes the selection and emits selectionUpdate
  • setGraphDataset keeps still-valid selected ids and drops stale ones
  • no selectionUpdate when the effective selection doesn't change

Separately noticed, not part of this PR

While testing setGraphDataset, I found a pre-existing, unrelated bug: filteredGraphAtom (core/graph/index.ts) is a derivedAtom without checkOutput: false, and lodash.isEqual treats any two different graphology Graph instances of equal node/edge count as equal — so a same-size dataset swap can leave it silently stale, which can later make resetCamera() throw. Reproduces without any of this PR's changes. Not fixing it here to keep this PR focused; happy to open a separate issue/PR if useful.

Open questions for maintainers

Very open to a different wire shape for SerializedSelectionState or different dataset-replacement semantics if you'd prefer something else here — this is meant as a starting point matching the existing getFilters/setFilters pattern, not a fixed proposal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants