@@ -32,7 +32,7 @@ import SidePanel from "./sidepanel/index";
3232import { useStore , useViewStateDebounced } from "./state" ;
3333import Toolbar from "./toolbar.js" ;
3434import { getTooltip } from "./tooltip/index.js" ;
35- import type { Message } from "./types.js" ;
35+ import type { FlyToMessage } from "./types.js" ;
3636import { isDefined , isGlobeView , sanitizeViewState } from "./util.js" ;
3737
3838import "maplibre-gl/dist/maplibre-gl.css" ;
@@ -163,20 +163,18 @@ function App() {
163163
164164 const rendererRef = useRef < RendererRef | null > ( null ) ;
165165
166- // Handle custom messages
166+ // Handle imperative fly-to commands. `_fly_to_command` is a one-way synced
167+ // trait (control -> map): writing it from Python (via `fly_to()`) or from an
168+ // external widget repositions the rendered map. Using a trait rather than a
169+ // Comm message means this also works in statically exported notebooks that
170+ // have no kernel.
167171 useEffect ( ( ) => {
168- const handler = ( msg : Message ) => {
169- switch ( msg . type ) {
170- case "fly-to" :
171- rendererRef . current ?. flyTo ( msg ) ;
172- break ;
173-
174- default :
175- break ;
176- }
172+ const apply = ( ) => {
173+ const cmd = model . get ( "_fly_to_command" ) as FlyToMessage | null ;
174+ if ( cmd ) rendererRef . current ?. flyTo ( cmd ) ;
177175 } ;
178- model . on ( "msg:custom " , handler ) ;
179- return ( ) => model . off ( "msg:custom " , handler ) ;
176+ model . on ( "change:_fly_to_command " , apply ) ;
177+ return ( ) => model . off ( "change:_fly_to_command " , apply ) ;
180178 } , [ model ] ) ;
181179
182180 // Fake state just to get react to re-render when a model callback is called
0 commit comments