2626vtkObject .GlobalWarningDisplayOff ()
2727
2828
29- class ScenePartStateApi (Protocol ):
29+ class SceneMutationApi (Protocol ):
3030 """Structural type of the coordinator surface LocalApp calls.
3131
3232 Typing only: there is no ``runtime_checkable`` decoration and no
3333 ``isinstance`` check anywhere against it. Declaring it here rather than
3434 importing the scene keeps this module free of any scene type, so the
3535 injected object remains LocalApp's only route to the scene.
3636
37- Not all of it is per-part, and the name is historical. ``sync_camera``,
38- the three widget-state toggles and ``set_projection`` are scene-wide, and
39- they are declared here anyway: the one production injection site passes
40- the whole scene coordinator, so a second protocol would be the same object
41- under a second name. Read this as the whole coordinator surface LocalApp calls, not only
42- the per-part part of it.
37+ Covers both per-part mutations (visibility, opacity, colour, selection)
38+ and scene-wide ones (camera sync, the widget-state toggles, projection,
39+ cross-section plane). The one production injection site passes the
40+ whole scene coordinator, so this protocol describes that whole surface.
4341 """
4442
4543 def set_part_visibility (self , node_id : int , visible : bool ) -> None : ...
@@ -217,7 +215,7 @@ def __init__(
217215 standalone : bool = True ,
218216 trame_logger : Logger | None = None ,
219217 pick_geometry = None ,
220- scene_part_state_api : ScenePartStateApi | None = None ,
218+ scene_mutation_api : SceneMutationApi | None = None ,
221219 ):
222220 self .server = server
223221 # Callable to get the scene details in JSON format
@@ -226,10 +224,10 @@ def __init__(
226224 self ._handle_save_state_response = handle_save_state_response
227225 # Callable for sub-geometry picking (optional)
228226 self ._pick_geometry = pick_geometry
229- # Per-part visual state coordinator (see ScenePartStateApi ). The one
227+ # Per-part visual state coordinator (see SceneMutationApi ). The one
230228 # production construction site always supplies it; it is optional so
231229 # that the class stays constructible without a scene.
232- self ._scene_part_state_api = scene_part_state_api
230+ self ._scene_mutation_api = scene_mutation_api
233231 # logger for logging trame server lifecycle info
234232 self .__trame_logger = trame_logger
235233
@@ -354,13 +352,13 @@ def perf_report_server_update(self, payload: dict):
354352 # body.
355353 # ------------------------------------------------------------------
356354
357- def _part_state_api (self , trigger_name : str , payload : BaseModel ) -> ScenePartStateApi | None :
355+ def _part_state_api (self , trigger_name : str , payload : BaseModel ) -> SceneMutationApi | None :
358356 """Return the injected coordinator, or ``None`` after logging."""
359357 logger .debug ("[trigger] %s arrived: %s." , trigger_name , payload )
360- if self ._scene_part_state_api is None :
358+ if self ._scene_mutation_api is None :
361359 logger .debug ("%s: no scene part-state API injected; ignoring." , trigger_name )
362360 return None
363- return self ._scene_part_state_api
361+ return self ._scene_mutation_api
364362
365363 @trigger ("set_part_visibility" )
366364 @parse_payload (SetPartVisibilityPayload )
@@ -492,31 +490,14 @@ def sync_camera(self, payload) -> None:
492490 # ------------------------------------------------------------------
493491 # Widget-state triggers
494492 #
495- # Frontend -> Backend. One trigger per server-tracked toggle. Each
496- # carries the absolute target value the client's widget settled on,
497- # never a toggle and never a delta: the toolbar buttons are toggles, so
498- # the client reads its widget back after the local write and sends the
499- # result. A message the client suppresses as redundant is therefore
500- # indistinguishable from one that set a value the server already held,
501- # and both are correct.
493+ # Frontend -> Backend. One trigger per server-tracked toggle. Each
494+ # carries the absolute target value, not a delta, so a redundant
495+ # message is indistinguishable from a no-op one, and both are fine.
496+ # No ``origin`` field: unlike the camera, a toggle echo is idempotent.
502497 #
503- # No ``origin`` field (RS-1). The camera needed one because a
504- # programmatic echo re-applied a *stale* camera over a newer one; a
505- # toggle echo carries the same boolean the server already holds, so the
506- # write is idempotent and the client's own widgets damp it with their
507- # value guards. It is the same echo the per-part deliveries already
508- # produce on load. Separating delivery from mutation is a later story's
509- # work, not this one's.
510- #
511- # Payloads are validated at this boundary by ``@parse_payload`` exactly
512- # as the per-part ones are. The wire key is ``visible`` on the three
513- # visibility triggers and ``parallel`` on ``set_projection``; none carries
514- # a pydantic alias, snake_case and camelCase coinciding on all four.
515- #
516- # ``set_projection`` sits in this block because it arrives from the same
517- # toolbar and under the same absolute-value rule, but it is not a fourth
518- # toggle: the server keeps no projection field, the camera record holds
519- # it, and the coordinator re-serialises the camera as part of the write.
498+ # ``set_projection`` lives here too: it is delivered the same way, but
499+ # it writes the camera record's projection field rather than a toggle
500+ # of its own.
520501 # ------------------------------------------------------------------
521502
522503 @trigger ("set_cross_section_visibility" )
0 commit comments