feat(sdk): stage 7 step 2 — setSelection API#1442
Conversation
james-russo-rames-d-jusso
left a comment
There was a problem hiding this comment.
Verified at HEAD 88f067ef.
Stage 7 step 2 — setSelection API (+96/-0). Thin state-setter plus selectionchange dispatch, well-tested. Verdict: approve-with-concerns.
What's good
- Defensive copy on both input (
[...ids]) and output (h([...this.currentSelection])) — listeners can't mutate internal state. Good. - Explicit tests that
setSelectiondoes NOT push to undo stack (line 269) and does NOT emit apatchevent (line 281). Exactly the determinism / replay-safety properties the stack needs for #1450's shadow-parity to work. selection()proxy snapshots ids at call time (line 261) — no stale-ref hazard.
Concerns
-
No equality check before firing
selectionchange(packages/sdk/src/session.ts:224-227).setSelection(["hf-title"])followed bysetSelection(["hf-title"])firesselectionchangetwice. Wired into Studio via #1443'suseSdkSelectionSync, this becomes a real perf risk: any React render that recreates thedomEditGroupSelectionsarray (even with identical contents) re-fires the effect → re-firessetSelection→ re-firesselectionchange→ potentially re-renders subscribers. Cheap fix: shallow-compare before assigning and skip the dispatch if unchanged. Worth doing now before #1450 starts measuring shadow-parity dispatch counts. -
No de-dup of input ids.
setSelection(["hf-title", "hf-title"])stores duplicates verbatim. Probably fine, but #1426'sCanResultdispatch-boundary checks iterate selection — duplicate ids = duplicate work. Worth aArray.from(new Set(ids))on the way in, or an explicit "callers are responsible for de-dup" note in the jsdoc. -
No validation that ids exist in the composition.
setSelection(["nonexistent-id"])succeeds silently. Tolerant API choice (matchesgetSelectionreturning whatever was set), but means a typo from an agent caller silently selects nothing. Not blocking — tolerant-by-default is consistent with the rest of the SDK surface.
Nits
- jsdoc on
setSelection(types.ts:256) says "Pass [] to clear" — true, butsetSelection(["only-hf-id"])also implicitly clears any prior selection. The "replace" semantic is in the first half of the sentence; consider tightening to "Replaces the current selection (use [] to clear)."
What I didn't verify
- Whether #1450's shadow-parity comparison includes
selectionchangeevent counts — if it does, the no-equality-check concern is a likely false-positive source for shadow-parity drift.
Review by Rames D Jusso
miguel-heygen
left a comment
There was a problem hiding this comment.
CI blocked by the root oxfmt failure in #1423. setSelection implementation is clean.
Strengths:
packages/sdk/src/session.tssetSelection— correctly fires a fresh copy of the array to each listener ([...this.currentSelection]), so a listener that mutates its argument doesn't corrupt the stored selection. The snapshot test verifies this.- Tests cover: empty-array clear, multi-id set, event payload copy, unsubscribe stops firing, undo-stack isolation, patch-event isolation. Comprehensive for a 5-line implementation.
Important:
packages/sdk/src/session.tssetSelectiondoes not validate that the ids exist in the document. Passing ids for elements that don't exist silently sets the selection. For a debug/dev experience, aconsole.warnorCanResult-style validation when an id resolves to null would help authors catch typos. Not a blocker — callers may intentionally set selection before the document is populated.
Nit:
packages/sdk/src/types.ts—setSelectionis documented "Replace the current selection; fires selectionchange." Missing: "Pass[]to clear." (already on the impl comment but not the type doc — copy it there for IDE hover).
Verdict: APPROVE (pending #1423 oxfmt fix)
Reasoning: Correct, minimal, and well-tested. Selection does not affect undo stack or patch events — correct isolation.
— magi
88f067e to
5f335b3
Compare
db5032f to
757ec4c
Compare
a32b07b to
02bd36c
Compare
|
Thanks @james-russo-rames-d-jusso and @miguel-heygen. Fixed:
Not changed:
|
02bd36c to
c13f332
Compare
0f6f62b to
ae58fa2
Compare
|
@james-russo-rames-d-jusso — all concerns addressed in post-review commits
|
ae58fa2 to
9e58fd1
Compare
Adds setSelection(ids: string[]) to Composition interface and CompositionImpl. Fires selectionchange; does not touch undo stack or patch stream. 11 contract tests: get/set/clear, event firing, copy semantics, no undo/patch side-effects. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Skip event dispatch when ids are identical (same length, same order) to prevent double-firing selectionchange from callers that call setSelection with the same list. Two new tests (RED→GREEN verified). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Miguel Ángel <miguel07alm@protonmail.com>
Reviewer noted it is dead surface in this stack — no caller uses it. Add comment explaining it is wired up in stage 8 when the preview host pushes selection events up to the SDK session. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Miguel Ángel <miguel07alm@protonmail.com>
c13f332 to
377007a
Compare
The base branch was changed.

What
Adds
session.setSelection(ids: string[])to the SDKCompositionAPI. Fires aselectionchangeevent on the session; does not create an undo entry or emit a patch.Why
Studio needs to mirror canvas selection state into the SDK session so downstream consumers (agent context, inspector panels, future collaborative cursors) can read who is selected without polling the DOM. Selection is metadata, not content — it must not appear in the undo stack or trigger a persist write.
How
setSelectionstores the id array in session state and emitsselectionchangesession.getSelection()returns the current idsTest plan
session.subcomp.test.ts/session.test.ts: all 11 selection tests passuseSdkSelectionSync(PR feat(studio): stage 7 step 1 — wire SDK session into Studio #1443) callssetSelectionon every canvas selection change and the session reflects it correctly