You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
An audit of the deck.gl API surface against what pydeck exposes found four gaps that the extension-binding pattern from #10447 closes cheaply:
Effects are accidentally half-supported today — raw @@type dicts already hydrate into LightingEffect/lights, but there is no typed wrapper, no docs, no examples, and sun/camera lights are unreachable under canonical names.
Interactive split views already work today via SplitterWidget — undocumented.
deck.gl 9.4's declarative ViewLayout maps naturally onto pydeck — layout containers are already plain JSON and leaves serialize exactly like pydeck's existing View binding.
Motivation
pydeck serializes Python objects to a JSON payload consumed by @deck.gl/json's JSONConverter inside modules/jupyter-widget. Two mechanisms make new bindings nearly free:
Every binding subclasses JSONMixin (bindings/pydeck/pydeck/bindings/json_tools.py); json.dumps(..., default=default_serialize) applies recursively, so wrapper objects nest inside other wrappers' kwargs with no extra machinery. Class discriminator is the "@@type" attribute; kwargs are camelCased.
The JS catalog (modules/jupyter-widget/src/playground/create-deck.js) auto-registers every uppercase export of the deck bundle, and jsonConverter.convert(jsonInput) hydrates the entire payload recursively — any nested {"@@type": X} becomes a class instance if X is registered.
The catch: experimental underscore exports (_GlobeView, _SunLight, ...) are only registered under their underscore names and need manual canonical aliases — the precedent being the widget alias block and TerrainExtension in #10447.
Proposal 1: Effects — a pdk.Effect wrapper
pdk.Deck has carried an effects passthrough parameter for years, and LightingEffect, AmbientLight, DirectionalLight, PointLight are already auto-registered (uppercase @deck.gl/core exports). LightingEffect's constructor takes a Record mapping arbitrary string keys to light instances, discriminated at runtime — which matches the converter's new Class(props) convention exactly. This renders a lit scene today, with zero code changes:
Rationale: identical to the established one-generic-wrapper-per-converter-concept precedent, zero per-class wrappers to keep in sync with JS. The "a light is not an effect" naming awkwardness is cosmetic; raw dicts remain a documented equivalent, and a pdk.Light = Effect alias can be added later without breakage. The shadow-enabling _shadow key survives camelCasing (leading underscore preserved).
JS side
Add canonical aliases in create-deck.js: SunLight: deckExports._SunLight, CameraLight: deckExports._CameraLight (same block as the widget aliases). Until a frontend publish carries them, "_SunLight" works as a type string.
No other JS change: props.effects arrives hydrated at the deck via the whole-payload conversion.
Exclusion
PostProcessEffect is not JSON-constructible — its constructor takes a luma.gl shader-module object as the first positional argument and would crash under new Class(props). Document as unsupported (reachable only via settings.custom_libraries).
Cleanup
pydeck/bindings/light_settings.py is a deck.gl 8.x-era leftover not wired into Deck at all; deprecate its docstring in favor of Effect.
The 9.4 View Layout system (#10269) is a JSON-friendly declarative description of multi-view arrangements: plain container objects ({type: 'row' | 'column' | 'overlay' | 'spacer', children: [...]} plus a split form) with View instances as leaves, compiled by buildViewsFromViewLayout({layout, width, height, ...}) into the View[] that core's views prop requires.
It maps onto pydeck unusually cleanly: containers are plain JSON (the converter recurses through them untouched) and leaves are exactly what pdk.View already serializes — hydrated in place. No converter changes needed, only a compile step.
Serialization difference from every other pydeck binding: containers are discriminated by literal "type" (not "@@type"), so ViewLayout stores type as a normal attribute. Leaf pdk.Views keep @@type; camelCasing (split_id → splitId) and None-dropping come free from JSONMixin.
Validate in __init__: type in the allowed set XOR the orientation+views split form; row/column/overlay require children, spacer forbids them; every leaf View must have an id (the JS compiler throws otherwise — fail early in Python).
Payload key: ride the existing views key.Deck(views=pdk.ViewLayout(...)) serializes to an object where arrays serialize today; JS discriminates with Array.isArray vs isViewLayout. No new Deck parameter; the default views array is unchanged.
JS side (modules/jupyter-widget)
Pre-req: export isViewLayout/assertViewLayout from @deck.gl/widgets (currently internal — only the compiler and the type are exported).
New playground/view-layout-support.js: after conversion, if props.views is a hydrated layout object, compile with buildViewsFromViewLayout using the container's client size; compose an onResize that recompiles with previous (structural view reuse keeps controllers/view state alive) and setProps({views}). Note the standalone HTML path currently wires noonResize — the composed handler must be injected in both HTML and widget modes. Keep per-deck compile state outside the converter (it short-circuits on shallow-equal input) and reuse it in updateDeck.
Works today: SplitterWidget recipe (document it)
_SplitterWidget is already aliased to SplitterWidget in the catalog and manages deck.props.views itself, deferring when views are managed externally. So this works in pydeck 0.9.x now:
Document it (widget docs + gallery example), including that combining a SplitterWidget with externally-set views is unsupported by design.
Version gating
ViewLayout exists only on the 9.4 line. Bump modules/jupyter-widget/package.json deck deps to 9.4.0-alpha.1 (already true on master; re-verify after #10447 merges) and bindings/pydeck/pydeck/frontend_semver.pyDECKGL_SEMVER from "~9.3.*" to the 9.4 line — noting jsdelivr may not resolve ~9.4.* to prerelease tags, in which case pin the exact alpha (PUBLISH.md documents the manual-edit escape hatch). Local dev doesn't wait for a publish: make fast-build && make copy-bundle + to_html(offline=True) or PYDECK_DEV_PORT.
Non-Goals
Post-processing effects via JSON (shader-module constructor arg — not expressible)
Bundling @deck.gl/carto / mapbox / arcgis into the jupyter-widget (bundle-size discussion, separate effort)
Each item branches from master independently of #10447 and lands after it merges (shared files: create-deck.js, deck-bundle.js, both __init__.pys, examples/docs gallery structure, create-deck.spec.ts).
Test Plan
Python (cd bindings/pydeck && make test): test_effect.py — nested @@type serialization, camelCasing, Deck(effects=...) round-trip; test_view_layout.py — plain "type" vs leaf @@type, validation errors, Deck.to_json placement under views.
JS (yarn vitest run test/modules/jupyter-widget): effects hydrate to LightingEffect instances with hydrated lights; views-layout payload compiles to View[] with correct rects/splitters; array payloads pass through unchanged.
End-to-end: run the new gallery examples against a local bundle and confirm lighting/shadows render and layouts recompile on window resize.
Open questions
Initial container size before the first onResize in the iframe/HTML template (needs visual verification).
jsdelivr prerelease semver resolution for the 9.4 alpha frontend.
Basemap sync uses the first viewport — recommend map_provider=None for multi-view layouts, or document first-view behavior.
Widget-mode updateDeck relies on size captured from onResize; confirm ordering in the ipywidget flow.
RFC: Expand pydeck API surface
RFC details
bindings/pydeck,modules/jupyter-widget,@deck.gl/json,@deck.gl/widgetsSummary
An audit of the deck.gl API surface against what pydeck exposes found four gaps that the extension-binding pattern from #10447 closes cheaply:
@@typedicts already hydrate intoLightingEffect/lights, but there is no typed wrapper, no docs, no examples, and sun/camera lights are unreachable under canonical names.GlobeViewneeds only a one-line alias to shed its_GlobeViewexperimental name — implemented in feat(pydeck): register GlobeView canonical alias in jupyter-widget #10451.SplitterWidget— undocumented.ViewLayoutmaps naturally onto pydeck — layout containers are already plain JSON and leaves serialize exactly like pydeck's existingViewbinding.Motivation
pydeck serializes Python objects to a JSON payload consumed by
@deck.gl/json'sJSONConverterinsidemodules/jupyter-widget. Two mechanisms make new bindings nearly free:JSONMixin(bindings/pydeck/pydeck/bindings/json_tools.py);json.dumps(..., default=default_serialize)applies recursively, so wrapper objects nest inside other wrappers' kwargs with no extra machinery. Class discriminator is the"@@type"attribute; kwargs are camelCased.modules/jupyter-widget/src/playground/create-deck.js) auto-registers every uppercase export of the deck bundle, andjsonConverter.convert(jsonInput)hydrates the entire payload recursively — any nested{"@@type": X}becomes a class instance ifXis registered.The catch: experimental underscore exports (
_GlobeView,_SunLight, ...) are only registered under their underscore names and need manual canonical aliases — the precedent being the widget alias block andTerrainExtensionin #10447.Proposal 1: Effects — a
pdk.Effectwrapperpdk.Deckhas carried aneffectspassthrough parameter for years, andLightingEffect,AmbientLight,DirectionalLight,PointLightare already auto-registered (uppercase@deck.gl/coreexports).LightingEffect's constructor takes aRecordmapping arbitrary string keys to light instances, discriminated at runtime — which matches the converter'snew Class(props)convention exactly. This renders a lit scene today, with zero code changes:It is, however, undocumented, untested, and undiscoverable.
Public API
One generic
pdk.Effect(type, **kwargs)mirroringpdk.Extension/pdk.Widget, used for both effects and lights:Rationale: identical to the established one-generic-wrapper-per-converter-concept precedent, zero per-class wrappers to keep in sync with JS. The "a light is not an effect" naming awkwardness is cosmetic; raw dicts remain a documented equivalent, and a
pdk.Light = Effectalias can be added later without breakage. The shadow-enabling_shadowkey survives camelCasing (leading underscore preserved).JS side
create-deck.js:SunLight: deckExports._SunLight,CameraLight: deckExports._CameraLight(same block as the widget aliases). Until a frontend publish carries them,"_SunLight"works as a type string.props.effectsarrives hydrated at the deck via the whole-payload conversion.Exclusion
PostProcessEffectis not JSON-constructible — its constructor takes a luma.gl shader-module object as the first positional argument and would crash undernew Class(props). Document as unsupported (reachable only viasettings.custom_libraries).Cleanup
pydeck/bindings/light_settings.pyis a deck.gl 8.x-era leftover not wired intoDeckat all; deprecate its docstring in favor ofEffect.Proposal 2: ViewLayout (deck.gl 9.4 declarative views), phase 1
The 9.4 View Layout system (#10269) is a JSON-friendly declarative description of multi-view arrangements: plain container objects (
{type: 'row' | 'column' | 'overlay' | 'spacer', children: [...]}plus a split form) withViewinstances as leaves, compiled bybuildViewsFromViewLayout({layout, width, height, ...})into theView[]that core'sviewsprop requires.It maps onto pydeck unusually cleanly: containers are plain JSON (the converter recurses through them untouched) and leaves are exactly what
pdk.Viewalready serializes — hydrated in place. No converter changes needed, only a compile step.Public API
"type"(not"@@type"), soViewLayoutstorestypeas a normal attribute. Leafpdk.Views keep@@type; camelCasing (split_id→splitId) andNone-dropping come free fromJSONMixin.__init__:typein the allowed set XOR theorientation+viewssplit form;row/column/overlayrequirechildren,spacerforbids them; every leafViewmust have anid(the JS compiler throws otherwise — fail early in Python).viewskey.Deck(views=pdk.ViewLayout(...))serializes to an object where arrays serialize today; JS discriminates withArray.isArrayvsisViewLayout. No newDeckparameter; the default views array is unchanged.JS side (
modules/jupyter-widget)isViewLayout/assertViewLayoutfrom@deck.gl/widgets(currently internal — only the compiler and the type are exported).playground/view-layout-support.js: after conversion, ifprops.viewsis a hydrated layout object, compile withbuildViewsFromViewLayoutusing the container's client size; compose anonResizethat recompiles withprevious(structural view reuse keeps controllers/view state alive) andsetProps({views}). Note the standalone HTML path currently wires noonResize— the composed handler must be injected in both HTML and widget modes. Keep per-deck compile state outside the converter (it short-circuits on shallow-equal input) and reuse it inupdateDeck.Works today: SplitterWidget recipe (document it)
_SplitterWidgetis already aliased toSplitterWidgetin the catalog and managesdeck.props.viewsitself, deferring when views are managed externally. So this works in pydeck 0.9.x now:Document it (widget docs + gallery example), including that combining a
SplitterWidgetwith externally-set views is unsupported by design.Version gating
ViewLayout exists only on the 9.4 line. Bump
modules/jupyter-widget/package.jsondeck deps to9.4.0-alpha.1(already true on master; re-verify after #10447 merges) andbindings/pydeck/pydeck/frontend_semver.pyDECKGL_SEMVERfrom"~9.3.*"to the 9.4 line — noting jsdelivr may not resolve~9.4.*to prerelease tags, in which case pin the exact alpha (PUBLISH.mddocuments the manual-edit escape hatch). Local dev doesn't wait for a publish:make fast-build && make copy-bundle+to_html(offline=True)orPYDECK_DEV_PORT.Non-Goals
@deck.gl/carto/mapbox/arcgisinto the jupyter-widget (bundle-size discussion, separate effort)Deck.update()ipywidget transport (see RFC: Modernize the pydeck Jupyter frontend and interactive multi-view support #10453)splitIds (phase 2, see RFC: Modernize the pydeck Jupyter frontend and interactive multi-view support #10453)Implementation Plan
pdk.Effect+ tests + gallery example (examples/effects/lighting_effect.pyin feat(jupyter-widget, pydeck): register @deck.gl/extensions for JSON @@type resolution #10447's grouped layout) +effect.rst; document the SplitterWidget recipe; deprecateLightSettings.SunLight/CameraLightaliases (andGlobeView, already in feat(pydeck): register GlobeView canonical alias in jupyter-widget #10451).pdk.ViewLayout(pydeck/bindings/view_layout.py) +isViewLayoutexport from@deck.gl/widgets+ jupyter-widget compile step + frontend semver bump.Each item branches from master independently of #10447 and lands after it merges (shared files:
create-deck.js,deck-bundle.js, both__init__.pys, examples/docs gallery structure,create-deck.spec.ts).Test Plan
cd bindings/pydeck && make test):test_effect.py— nested@@typeserialization, camelCasing,Deck(effects=...)round-trip;test_view_layout.py— plain"type"vs leaf@@type, validation errors,Deck.to_jsonplacement underviews.yarn vitest run test/modules/jupyter-widget): effects hydrate toLightingEffectinstances with hydrated lights; views-layout payload compiles toView[]with correct rects/splitters; array payloads pass through unchanged.Open questions
onResizein the iframe/HTML template (needs visual verification).map_provider=Nonefor multi-view layouts, or document first-view behavior.updateDeckrelies on size captured fromonResize; confirm ordering in the ipywidget flow.