Jupyter-scoped Visualizer singleton — under Jupyter, Visualizer()
now returns the same instance across re-runs (one server + one scene host),
so re-running a cell that re-creates the viewer reuses it instead of binding
the port again; re-running a construction cell also clears the default scene
and re-adds axes/grid per add_default_axes / add_default_grid.
Named-scene re-run reset — viz.scene(name) is created on first call,
cleared when the same cell is re-run, and get-or-create across different
cells, so scene-building cells are safe to re-run.
PortConflictMode port-conflict resolution — when the viewer port is
already in use, VizServer resolves it by policy (CANCEL/AUTO/KILL/
ASK, via psutil), and Visualizer(port_conflict_mode=...) picks the
behaviour — defaulting to AUTO under Jupyter and ASK at the terminal.
Port conflict on cell re-run — re-running a notebook cell that
re-constructs Visualizer() no longer tries to bind port 8765 a second time
(previously PortInUseError → SystemExit).
interactive.ipynb used an unsupported wireframe=True keyword — the
Jupyter example now passes style=SphereStyle(wireframe=True) instead,
matching the documented API (the bare keyword raised TypeError).
SIGTERM no longer swallowed — the server's signal handler only set a
shutdown event and ignored SIGTERM, so kill (and a VSCode kernel
"Restart") could not terminate the process and the port stayed held; SIGTERM
now tears the server down and re-raises, so the OS releases the port.
Visualizer.__call__ explicit signature — viz(...) now declares the
same keyword-only parameters as new() instead of forwarding through
**kwargs, so unsupported keywords fail at the call site with full
type-checking and editor support.