Quellcode durchsuchen

Issue #805: rotation is in progress

cycle20 vor 1 Jahr
Ursprung
Commit
9516c7a6d8

+ 3 - 2
src/RcsbFvStructure/StructureViewers/MolstarViewer/MolstarManagerFactory.ts

@@ -1,6 +1,7 @@
 import {ViewerManagerFactoryInterface} from "../../StructureViewerInterface";
 import {LoadMolstarInterface, MolstarActionManager} from "./MolstarActionManager";
-import {Viewer, ViewerProps} from "@rcsb/rcsb-molstar/build/src/viewer";
+import { ViewerProps} from "@rcsb/rcsb-molstar/build/src/viewer";
+import { TmViewer } from "../../../examples/assembly-tm/tmdet-viewer/TmViewer";
 import {MolstarModelMapManager} from "./MolstarModelMapManager";
 import {MolstarCallbackManager} from "./MolstarCallbackManager";
 import {DataContainer} from "../../../Utils/DataContainer";
@@ -18,7 +19,7 @@ export class MolstarManagerFactory<P,L> implements ViewerManagerFactoryInterface
         const loadingFlag: DataContainer<boolean> = new DataContainer(false);
         const innerSelectionFlag: DataContainer<boolean> = new DataContainer(false);
         const innerReprChangeFlag: DataContainer<boolean> = new DataContainer(false);
-        const viewer = new Viewer(viewerParams.viewerElement, {
+        const viewer = new TmViewer(viewerParams.viewerElement, {
             ...viewerParams.viewerProps,
             layoutShowControls:false,
             layoutShowSequence: true,

+ 9 - 9
src/examples/assembly-tm/preset.ts

@@ -166,24 +166,25 @@ export const TmDetRcsbPreset = TrajectoryHierarchyPresetProvider({
         } else if (p.kind === 'nakb') {
             console.warn('nakb case not implemented in TmDetRcsbPreset');
         } else {
+            // Transformation:
+            const pdbtmDescriptor = TmDetDescriptorCache.get('1afo'); // TODO
+            let matrix = undefined;
+            if (pdbtmDescriptor) {
+                matrix = DebugUtil.descriptorMxToMat4(pdbtmDescriptor!.additional_entry_annotations.membrane.transformation_matrix as any);
+                //applyTransformations(plugin, pdbtmDescriptor!);
+            }
 
             console.log('HERE WE GO:', { structure: structure, structureProps: structureProperties, presetParams: presetParams });
             //representation = await plugin.builders.structure.representation.applyPreset(structureProperties!, 'auto', presetParams);
             representation = await plugin.builders.structure.representation.applyPreset(
-                structureProperties!, 'auto', { theme: { globalName: 'tmdet-custom-color-theme' } });
+                structureProperties!, 'empty', { theme: { globalName: 'tmdet-custom-color-theme' } });
             const selector = await plugin.builders.structure.hierarchy.applyPreset(
                 trajectory, 'default', { representationPreset: TMDET_STRUCTURE_PRESET_ID as any });
 
-            // Transformation:
-            const pdbtmDescriptor = TmDetDescriptorCache.get('1afo'); // TODO
-            let matrix = undefined;
-            if (pdbtmDescriptor) {
-                matrix = DebugUtil.descriptorMxToMat4(pdbtmDescriptor!.additional_entry_annotations.membrane.transformation_matrix as any);
-            }
 
             console.log('PRESET apply result', {
                 matrix: matrix,
-                selector: selector,
+//                selector: selector,
                 model: model,
                 modelProperties: modelProperties,
                 unitcell: unitcell,
@@ -192,7 +193,6 @@ export const TmDetRcsbPreset = TrajectoryHierarchyPresetProvider({
                 representation: representation
             });
 
-            applyTransformations(plugin, pdbtmDescriptor!);
             // if (matrix && selector?.structureProperties) {
             //     const params = {
             //         transform: {

+ 5 - 1
src/examples/assembly-tm/tmdet-extension/behavior.ts

@@ -54,7 +54,7 @@ export const TMDETMembraneOrientation = PluginBehavior.create<{ autoAttach: bool
         private provider = MembraneOrientationProvider
 
         register(): void {
-            console.log('TMDET REGISER');
+            console.log('TMDET plugin behavior REGISTER');
             DefaultQueryRuntimeTable.addCustomProp(this.provider.descriptor);
 
             this.ctx.customStructureProperties.register(this.provider, this.params.autoAttach);
@@ -77,6 +77,7 @@ export const TMDETMembraneOrientation = PluginBehavior.create<{ autoAttach: bool
         }
 
         update(p: { autoAttach: boolean }) {
+            console.log('TMDET plugin behavior UPDATE');
             let updated = this.params.autoAttach !== p.autoAttach;
             this.params.autoAttach = p.autoAttach;
             this.ctx.customStructureProperties.setDefaultAutoAttach(this.provider.descriptor.name, this.params.autoAttach);
@@ -84,6 +85,7 @@ export const TMDETMembraneOrientation = PluginBehavior.create<{ autoAttach: bool
         }
 
         unregister() {
+            console.log('TMDET plugin behavior UNREGISTER');
             DefaultQueryRuntimeTable.removeCustomProp(this.provider.descriptor);
 
             this.ctx.customStructureProperties.unregister(this.provider.descriptor.name);
@@ -332,6 +334,8 @@ export const MembraneOrientationPreset = StructureRepresentationPresetProvider({
 
         const membraneOrientation = await tryCreateMembraneOrientation(plugin, structureCell);
         const colorTheme =  TmDetColorThemeProvider.name as any;
+        console.log('MembOriPreset apply params:', params);
+        console.log('MembOriPreset ref:', ref);
         const preset = await PresetStructureRepresentations.auto.apply(ref, { ...params, theme: { globalName: colorTheme, focus: { name: colorTheme } } }, plugin);
 
         return { components: preset.components, representations: { ...preset.representations, membraneOrientation } };

+ 2 - 1
src/examples/assembly-tm/tmdet-extension/symmetry.ts

@@ -112,7 +112,8 @@ function createPdbxStructOperList(pdbx_struct_oper_list: Table<mmCIF_Schema['pdb
             name: Column.ofStringArray([ '1_555' ]),
             symmetry_operation: Column.ofStringArray([ 'x,y,z' ]),
             matrix:  Column.ofArray({
-                array: [[ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]],
+                //array: [[ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]],
+                array: [[ 0.44060603, 0, 0.89770061, -0.88605607, 0.16054524, 0.43489069, -0.14412156, -0.98702848, 0.0707372, 0.0822401, -0.06780469, -1.02110946 ]],
                 schema: pdbx_struct_oper_list.matrix.schema
             }),
             vector: Column.ofArray({

+ 84 - 0
src/examples/assembly-tm/tmdet-viewer/TmViewer.ts

@@ -0,0 +1,84 @@
+/**
+ * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author Joan Segura <joan.segura@rcsb.org>
+ * @author Yana Rose <yana.rose@rcsb.org>
+ * @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
+ */
+
+import { Viewer, ViewerProps } from '@rcsb/rcsb-molstar/build/src/viewer';
+import { TmDetColorThemeProvider } from '../tmdet-extension/tmdet-color-theme';
+import { PluginBehavior } from 'molstar/lib/mol-plugin/behavior';
+import { PluginSpec } from 'molstar/lib/mol-plugin/spec';
+import { MembraneOrientationPreset, TMDETMembraneOrientation } from '../tmdet-extension/behavior';
+import { SyncBehaviors } from 'molstar/lib/mol-plugin/behavior/static/state';
+import { PluginStateObject } from 'molstar/lib/mol-plugin-state/objects';
+import { MembraneOrientationProvider, TmDetDescriptorCache } from '../tmdet-extension/prop';
+import { MembraneOrientationRepresentationProvider } from '../tmdet-extension/representation';
+import { TmDetLabelProvider } from '../tmdet-extension/labeling';
+import { registerTmDetSymmetry } from '../tmdet-extension/symmetry';
+
+
+/** package version, filled in at bundle build time */
+declare const __RCSB_MOLSTAR_VERSION__: string;
+export const RCSB_MOLSTAR_VERSION = typeof __RCSB_MOLSTAR_VERSION__ != 'undefined' ? __RCSB_MOLSTAR_VERSION__ : 'none';
+
+/** unix time stamp, to be filled in at bundle build time */
+declare const __BUILD_TIMESTAMP__: number;
+export const BUILD_TIMESTAMP = typeof __BUILD_TIMESTAMP__ != 'undefined' ? __BUILD_TIMESTAMP__ : 'none';
+export const BUILD_DATE = new Date(BUILD_TIMESTAMP);
+
+export class TmViewer extends Viewer {
+
+    constructor(elementOrId: string | HTMLElement, props: Partial<ViewerProps> = {}) {
+        console.log("TmViewer constructor");
+        super(elementOrId, props);
+        //this.plugin.representation.structure.themes.colorThemeRegistry.add(TmDetColorThemeProvider);
+        this.initBehavior();
+    }
+
+    public async initBehavior() {
+        registerTmDetSymmetry(TmDetDescriptorCache.get('1afo')!);
+
+        let tree = this.plugin.state.behaviors.build();
+
+
+        this.plugin.builders.structure.representation.registerPreset(MembraneOrientationPreset);
+        this.plugin.representation.structure.themes.colorThemeRegistry.add(TmDetColorThemeProvider);
+        this.plugin.managers.lociLabels.addProvider(TmDetLabelProvider);
+
+        // const behavior = PluginSpec.Behavior(TMDETMembraneOrientation);
+        // this.plugin.customStructureProperties.register(MembraneOrientationProvider, false);
+        // this.plugin.representation.structure.registry.add(MembraneOrientationRepresentationProvider);
+
+
+        // // // Init custom properties 1st
+        // let cat = PluginBehavior.getCategoryId(behavior.transformer);
+        // console.log('Behavior', behavior);
+        // console.log('Transformer', behavior.transformer);
+        // if (cat === 'custom-props') {
+        //     tree.to(PluginBehavior.getCategoryId(behavior.transformer)).apply(behavior.transformer, behavior.defaultParams, { ref: behavior.transformer.id });
+        // }
+        // // this.plugin.state.events.object.created.subscribe(o => {
+        // //     if (!PluginStateObject.isBehavior(o.obj)) return;
+        // //     console.log({ o });
+        // // });
+        // //o.obj.data.register(o.ref);
+        // //behavior.re
+        // console.log('behavior', behavior);
+        // console.log(PluginBehavior.Categories);
+    
+        // // await this.plugin.runTask(this.plugin.state.behaviors.updateTree(tree, { doNotUpdateCurrent: true, doNotLogTiming: true }));
+
+        // tree = this.plugin.state.behaviors.build();
+        // //const cat = PluginBehavior.getCategoryId(behavior.transformer);
+        // if (cat !== 'custom-props') {
+        //     tree.to(PluginBehavior.getCategoryId(behavior.transformer)).apply(behavior.transformer, behavior.defaultParams, { ref: behavior.transformer.id });
+        // }
+
+        await this.plugin.runTask(this.plugin.state.behaviors.updateTree(tree, { doNotUpdateCurrent: true, doNotLogTiming: true }));
+    }
+
+}
+

+ 0 - 568
src/examples/assembly-tm/tmdet-viewer/index.ts

@@ -1,568 +0,0 @@
-/**
- * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- * @author Joan Segura <joan.segura@rcsb.org>
- * @author Yana Rose <yana.rose@rcsb.org>
- * @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
- */
-
-import { BehaviorSubject } from 'rxjs';
-import { Plugin } from 'molstar/lib/mol-plugin-ui/plugin';
-import { PluginCommands } from 'molstar/lib/mol-plugin/commands';
-import { ViewerState, CollapsedState, ModelUrlProvider, LigandViewerState, LoadParams } from '@rcsb/rcsb-molstar/build/src/viewer/types';
-import { PluginSpec } from 'molstar/lib/mol-plugin/spec';
-
-import { ColorNames } from 'molstar/lib/mol-util/color/names';
-import * as React from 'react';
-
-import { ModelLoader } from '@rcsb/rcsb-molstar/build/src/viewer/helpers/model';
-import { PresetProps } from '@rcsb/rcsb-molstar/build/src/viewer/helpers/preset';
-import { ControlsWrapper } from '@rcsb/rcsb-molstar/build/src/viewer/ui/controls';
-import { PluginConfig } from 'molstar/lib/mol-plugin/config';
-import { RCSBAssemblySymmetry } from 'molstar/lib/extensions/rcsb/assembly-symmetry/behavior';
-import { RCSBValidationReport } from 'molstar/lib/extensions/rcsb/validation-report/behavior';
-import { Mat4 } from 'molstar/lib/mol-math/linear-algebra';
-import { PluginState } from 'molstar/lib/mol-plugin/state';
-import { BuiltInTrajectoryFormat } from 'molstar/lib/mol-plugin-state/formats/trajectory';
-import { ObjectKeys } from 'molstar/lib/mol-util/type-helpers';
-import { PluginLayoutControlsDisplay } from 'molstar/lib/mol-plugin/layout';
-import { SuperposeColorThemeProvider } from '@rcsb/rcsb-molstar/build/src/viewer/helpers/superpose/color';
-import { NakbColorThemeProvider } from '@rcsb/rcsb-molstar/build/src/viewer/helpers/nakb/color';
-import { setFocusFromRange, removeComponent, clearSelection, createComponent, select } from '@rcsb/rcsb-molstar/build/src/viewer/helpers/viewer';
-import { SelectBase, SelectRange, SelectTarget, Target } from '@rcsb/rcsb-molstar/build/src/viewer/helpers/selection';
-import { StructureRepresentationRegistry } from 'molstar/lib/mol-repr/structure/registry';
-import { DefaultPluginUISpec, PluginUISpec } from 'molstar/lib/mol-plugin-ui/spec';
-import { PluginUIContext } from 'molstar/lib/mol-plugin-ui/context';
-import { ANVILMembraneOrientation, MembraneOrientationPreset } from 'molstar/lib/extensions/anvil/behavior';
-import { MembraneOrientationRepresentationProvider } from 'molstar/lib/extensions/anvil/representation';
-import { PluginContext } from 'molstar/lib/mol-plugin/context';
-import { TrajectoryHierarchyPresetProvider } from 'molstar/lib/mol-plugin-state/builder/structure/hierarchy-preset';
-import { AnimateStateSnapshots } from 'molstar/lib/mol-plugin-state/animation/built-in/state-snapshots';
-import { PluginFeatureDetection } from 'molstar/lib/mol-plugin/features';
-import { PresetStructureRepresentations } from 'molstar/lib/mol-plugin-state/builder/structure/representation-preset';
-import { MAQualityAssessment } from 'molstar/lib/extensions/model-archive/quality-assessment/behavior';
-import { ModelExport } from 'molstar/lib/extensions/model-export';
-import { exportHierarchy } from 'molstar/lib/extensions/model-export/export';
-import { GeometryExport } from 'molstar/lib/extensions/geo-export';
-import { Mp4Export } from 'molstar/lib/extensions/mp4-export';
-import { PartialCanvas3DProps } from 'molstar/lib/mol-canvas3d/canvas3d';
-import { RSCCScore } from '@rcsb/rcsb-molstar/build/src/viewer/helpers/rscc/behavior';
-import { createRoot } from 'react-dom/client';
-import { AssemblySymmetry } from 'molstar/lib/extensions/rcsb/assembly-symmetry/prop';
-import { wwPDBChemicalComponentDictionary } from 'molstar/lib/extensions/wwpdb/ccd/behavior';
-import { ChemicalCompontentTrajectoryHierarchyPreset } from 'molstar/lib/extensions/wwpdb/ccd/representation';
-import { StateTransforms } from 'molstar/lib/mol-plugin-state/transforms';
-import { TMDETMembraneOrientation } from '../tmdet-extension/behavior';
-
-/** package version, filled in at bundle build time */
-declare const __RCSB_MOLSTAR_VERSION__: string;
-export const RCSB_MOLSTAR_VERSION = typeof __RCSB_MOLSTAR_VERSION__ != 'undefined' ? __RCSB_MOLSTAR_VERSION__ : 'none';
-
-/** unix time stamp, to be filled in at bundle build time */
-declare const __BUILD_TIMESTAMP__: number;
-export const BUILD_TIMESTAMP = typeof __BUILD_TIMESTAMP__ != 'undefined' ? __BUILD_TIMESTAMP__ : 'none';
-export const BUILD_DATE = new Date(BUILD_TIMESTAMP);
-
-const Extensions = {
-    'rcsb-assembly-symmetry': PluginSpec.Behavior(RCSBAssemblySymmetry),
-    'rcsb-validation-report': PluginSpec.Behavior(RCSBValidationReport),
-    'rscc': PluginSpec.Behavior(RSCCScore),
-    'anvil-membrane-orientation': PluginSpec.Behavior(ANVILMembraneOrientation),
-    'ma-quality-assessment': PluginSpec.Behavior(MAQualityAssessment),
-    'model-export': PluginSpec.Behavior(ModelExport),
-    'mp4-export': PluginSpec.Behavior(Mp4Export),
-    'geo-export': PluginSpec.Behavior(GeometryExport),
-    'tmdet-membrane-orientation': PluginSpec.Behavior(TMDETMembraneOrientation)
-};
-
-const DefaultViewerProps = {
-    showImportControls: false,
-    showSessionControls: false,
-    showStructureSourceControls: true,
-    showMeasurementsControls: true,
-    showStrucmotifSubmitControls: true,
-    showSuperpositionControls: true,
-    showQuickStylesControls: false,
-    showStructureComponentControls: true,
-    showVolumeStreamingControls: true,
-    showAssemblySymmetryControls: true,
-    showValidationReportControls: true,
-
-    showMembraneOrientationPreset: false,
-    showNakbColorTheme: false,
-    /**
-     * Needed when running outside of sierra. If set to true, the strucmotif UI will use an absolute URL to sierra-prod.
-     * Otherwise, the link will be relative on the current host.
-     */
-    detachedFromSierra: false,
-    modelUrlProviders: [
-        (pdbId: string) => ({
-            url: `https://models.rcsb.org/${pdbId.toLowerCase()}.bcif`,
-            format: 'mmcif',
-            isBinary: true
-        }),
-        (pdbId: string) => ({
-            url: `https://files.rcsb.org/download/${pdbId.toLowerCase()}.cif`,
-            format: 'mmcif',
-            isBinary: false
-        })
-    ] as ModelUrlProvider[],
-
-    extensions: ObjectKeys(Extensions),
-    layoutIsExpanded: false,
-    layoutShowControls: true,
-    layoutControlsDisplay: 'reactive' as PluginLayoutControlsDisplay,
-    layoutShowSequence: true,
-    layoutShowLog: false,
-
-    viewportShowExpand: true,
-    viewportShowSelectionMode: true,
-    volumeStreamingServer: 'https://maps.rcsb.org/',
-
-    backgroundColor: ColorNames.white,
-    manualReset: false, // switch to 'true' for 'motif' preset
-    pickingAlphaThreshold: 0.5, // lower to 0.2 to accommodate 'motif' preset
-    showWelcomeToast: true,
-};
-export type ViewerProps = typeof DefaultViewerProps & { canvas3d: PartialCanvas3DProps }
-
-const LigandExtensions = {
-    'wwpdb-chemical-component-dictionary': PluginSpec.Behavior(wwPDBChemicalComponentDictionary),
-};
-
-const DefaultLigandViewerProps = {
-    modelUrlProviders: [
-        (id: string) => ({
-            url: id.length <= 5 ? `https://files.rcsb.org/ligands/view/${id.toUpperCase()}.cif` : `https://files.rcsb.org/birds/view/${id.toUpperCase()}.cif`,
-            format: 'mmcif',
-            isBinary: false
-        })
-    ] as ModelUrlProvider[],
-
-    extensions: ObjectKeys(LigandExtensions),
-    layoutIsExpanded: false,
-    layoutShowControls: false,
-    layoutControlsDisplay: 'reactive' as PluginLayoutControlsDisplay,
-    layoutShowLog: false,
-
-    viewportShowExpand: true,
-    viewportShowSelectionMode: true,
-
-    backgroundColor: ColorNames.white,
-    showWelcomeToast: true,
-
-    ignoreHydrogens: true,
-    showLabels: false,
-    shownCoordinateType: 'ideal' as const,
-    aromaticBonds: false, // stylize aromatic rings
-};
-export type LigandViewerProps = typeof DefaultLigandViewerProps & { canvas3d: PartialCanvas3DProps }
-
-export class Viewer {
-    private readonly _plugin: PluginUIContext;
-    private readonly modelUrlProviders: ModelUrlProvider[];
-    private prevExpanded: boolean;
-
-    constructor(elementOrId: string | HTMLElement, props: Partial<ViewerProps> = {}) {
-        console.log("RCSB constructor");
-        const element = typeof elementOrId === 'string' ? document.getElementById(elementOrId)! : elementOrId;
-        if (!element) throw new Error(`Could not get element with id '${elementOrId}'`);
-
-        const o = { ...DefaultViewerProps, ...props };
-
-        const defaultSpec = DefaultPluginUISpec();
-        const spec: PluginUISpec = {
-            ...defaultSpec,
-            actions: defaultSpec.actions,
-            behaviors: [
-                ...defaultSpec.behaviors,
-                ...o.extensions.map(e => Extensions[e]),
-            ],
-            animations: [...defaultSpec.animations?.filter(a => a.name !== AnimateStateSnapshots.name) || []],
-            layout: {
-                initial: {
-                    isExpanded: o.layoutIsExpanded,
-                    showControls: o.layoutShowControls,
-                    controlsDisplay: o.layoutControlsDisplay,
-                },
-            },
-            canvas3d: {
-                ...defaultSpec.canvas3d,
-                ...o.canvas3d,
-                renderer: {
-                    ...defaultSpec.canvas3d?.renderer,
-                    ...o.canvas3d?.renderer,
-                    backgroundColor: o.backgroundColor,
-                    pickingAlphaThreshold: o.pickingAlphaThreshold
-                },
-                camera: {
-                    // desirable for alignment view so that the display doesn't "jump around" as more structures get loaded
-                    manualReset: o.manualReset
-                }
-            },
-            components: {
-                ...defaultSpec.components,
-                controls: {
-                    ...defaultSpec.components?.controls,
-                    top: o.layoutShowSequence ? undefined : 'none',
-                    bottom: o.layoutShowLog ? undefined : 'none',
-                    left: 'none',
-                    right: ControlsWrapper,
-                },
-                remoteState: 'none',
-            },
-            config: [
-                [PluginConfig.Viewport.ShowExpand, o.viewportShowExpand],
-                [PluginConfig.Viewport.ShowSelectionMode, o.viewportShowSelectionMode],
-                [PluginConfig.Viewport.ShowAnimation, false],
-                [PluginConfig.VolumeStreaming.DefaultServer, o.volumeStreamingServer],
-                [PluginConfig.Download.DefaultPdbProvider, 'rcsb'],
-                [PluginConfig.Download.DefaultEmdbProvider, 'rcsb'],
-                [PluginConfig.Structure.DefaultRepresentationPreset, PresetStructureRepresentations.auto.id],
-                // wboit & webgl1 checks are needed to work properly on recent Safari versions
-                [PluginConfig.General.EnableWboit, PluginFeatureDetection.preferWebGl1],
-                [PluginConfig.General.PreferWebGl1, PluginFeatureDetection.preferWebGl1]
-            ]
-        };
-
-        this._plugin = new PluginUIContext(spec);
-        this.modelUrlProviders = o.modelUrlProviders;
-
-        (this._plugin.customState as ViewerState) = {
-            showImportControls: o.showImportControls,
-            showSessionControls: o.showSessionControls,
-            showStructureSourceControls: o.showStructureSourceControls,
-            showMeasurementsControls: o.showMeasurementsControls,
-            showStrucmotifSubmitControls: o.showStrucmotifSubmitControls,
-            showSuperpositionControls: o.showSuperpositionControls,
-            showQuickStylesControls: o.showQuickStylesControls,
-            showStructureComponentControls: o.showStructureComponentControls,
-            showVolumeStreamingControls: o.showVolumeStreamingControls,
-            showAssemblySymmetryControls: o.showAssemblySymmetryControls,
-            showValidationReportControls: o.showValidationReportControls,
-            modelLoader: new ModelLoader(this._plugin),
-            collapsed: new BehaviorSubject<CollapsedState>({
-                selection: true,
-                measurements: true,
-                strucmotifSubmit: true,
-                superposition: true,
-                quickStyles: false,
-                component: false,
-                volume: true,
-                assemblySymmetry: true,
-                validationReport: true,
-                custom: true,
-            }),
-            detachedFromSierra: o.detachedFromSierra
-        };
-
-        this._plugin.init()
-            .then(async () => {
-                // hide 'Membrane Orientation' preset from UI - has to happen 'before' react render, apparently
-                // the corresponding behavior must be registered either way, because the 3d-view uses it (even without appearing in the UI)
-                if (!o.showMembraneOrientationPreset) {
-                    this._plugin.builders.structure.representation.unregisterPreset(MembraneOrientationPreset);
-                    this._plugin.representation.structure.registry.remove(MembraneOrientationRepresentationProvider);
-                }
-                // normally, this would be part of CustomStructureControls -- we want to manage its collapsed state individually though
-                this._plugin.customStructureControls.delete(AssemblySymmetry.Tag.Representation);
-
-                const root = createRoot(element);
-                root.render(React.createElement(Plugin, { plugin: this._plugin }));
-
-                this._plugin.representation.structure.themes.colorThemeRegistry.add(SuperposeColorThemeProvider);
-                if (o.showNakbColorTheme) this._plugin.representation.structure.themes.colorThemeRegistry.add(NakbColorThemeProvider);
-
-                if (o.showWelcomeToast) {
-                    await PluginCommands.Toast.Show(this._plugin, {
-                        title: 'Welcome',
-                        message: `RCSB PDB Mol* Viewer ${RCSB_MOLSTAR_VERSION} [${BUILD_DATE.toLocaleString()}]`,
-                        key: 'toast-welcome',
-                        timeoutMs: 5000
-                    });
-                }
-
-                this.prevExpanded = this._plugin.layout.state.isExpanded;
-                this._plugin.layout.events.updated.subscribe(() => this.toggleControls());
-            });
-    }
-
-    get plugin() {
-        return this._plugin;
-    }
-
-    pluginCall(f: (plugin: PluginContext) => void) {
-        f(this.plugin);
-    }
-
-    private get customState() {
-        return this._plugin.customState as ViewerState;
-    }
-
-    private toggleControls(): void {
-        const currExpanded = this._plugin.layout.state.isExpanded;
-        const expandedChanged = (this.prevExpanded !== currExpanded);
-        if (!expandedChanged) return;
-
-        if (currExpanded && !this._plugin.layout.state.showControls) {
-            this._plugin.layout.setProps({ showControls: true });
-        } else if (!currExpanded && this._plugin.layout.state.showControls) {
-            this._plugin.layout.setProps({ showControls: false });
-        }
-        this.prevExpanded = this._plugin.layout.state.isExpanded;
-    }
-
-    resetCamera(durationMs?: number) {
-        this._plugin.managers.camera.reset(undefined, durationMs);
-    }
-
-    clear() {
-        const state = this._plugin.state.data;
-        return PluginCommands.State.RemoveObject(this._plugin, { state, ref: state.tree.root.ref });
-    }
-
-    async loadPdbId<P, S>(pdbId: string, config?: { props?: PresetProps; matrix?: Mat4; reprProvider?: TrajectoryHierarchyPresetProvider<P, S>, params?: P }) {
-        for (const provider of this.modelUrlProviders) {
-            try {
-                const p = provider(pdbId);
-                return await this.customState.modelLoader.load<P, S>({ fileOrUrl: p.url, format: p.format, isBinary: p.isBinary }, config?.props, config?.matrix, config?.reprProvider, config?.params);
-            } catch (e) {
-                console.warn(`loading '${pdbId}' failed with '${e}', trying next model-loader-provider`);
-            }
-        }
-    }
-
-    async loadPdbIds<P, S>(args: { pdbId: string, config?: {props?: PresetProps; matrix?: Mat4; reprProvider?: TrajectoryHierarchyPresetProvider<P, S>, params?: P} }[]) {
-        const out = [];
-        for (const { pdbId, config } of args) {
-            out.push(await this.loadPdbId(pdbId, config));
-        }
-        if (!this.plugin.spec.canvas3d?.camera?.manualReset) this.resetCamera(0);
-        return out;
-    }
-
-    loadStructureFromUrl<P, S>(url: string, format: BuiltInTrajectoryFormat, isBinary: boolean, config?: {props?: PresetProps & { dataLabel?: string }; matrix?: Mat4; reprProvider?: TrajectoryHierarchyPresetProvider<P, S>, params?: P}) {
-        return this.customState.modelLoader.load({ fileOrUrl: url, format, isBinary }, config?.props, config?.matrix, config?.reprProvider, config?.params);
-    }
-
-    loadSnapshotFromUrl(url: string, type: PluginState.SnapshotType) {
-        return PluginCommands.State.Snapshots.OpenUrl(this._plugin, { url, type });
-    }
-
-    loadStructureFromData<P, S>(data: string | number[], format: BuiltInTrajectoryFormat, isBinary: boolean, config?: {props?: PresetProps & { dataLabel?: string }; matrix?: Mat4; reprProvider?: TrajectoryHierarchyPresetProvider<P, S>, params?: P}) {
-        return this.customState.modelLoader.parse({ data, format, isBinary }, config?.props, config?.matrix, config?.reprProvider, config?.params);
-    }
-
-    handleResize() {
-        this._plugin.layout.events.updated.next(void 0);
-    }
-
-    exportLoadedStructures(options?: { format?: 'cif' | 'bcif' }) {
-        return exportHierarchy(this.plugin, options);
-    }
-
-    setFocus(target: SelectRange) {
-        setFocusFromRange(this._plugin, target);
-    }
-
-    clearFocus(): void {
-        this._plugin.managers.structure.focus.clear();
-    }
-
-    select(targets: SelectTarget | SelectTarget[], mode: 'select' | 'hover', modifier: 'add' | 'set') {
-        select(this._plugin, targets, mode, modifier);
-    }
-
-    clearSelection(mode: 'select' | 'hover', target?: { modelId: string; } & Target) {
-        clearSelection(this._plugin, mode, target);
-    }
-
-    async createComponent(label: string, targets: SelectBase | SelectTarget | SelectTarget[], representationType: StructureRepresentationRegistry.BuiltIn) {
-        await createComponent(this._plugin, label, targets, representationType);
-    }
-
-    async removeComponent(componentLabel: string) {
-        await removeComponent(this._plugin, componentLabel);
-    }
-}
-
-export class LigandViewer {
-    private readonly _plugin: PluginUIContext;
-    private readonly modelUrlProviders: ModelUrlProvider[];
-
-    constructor(elementOrId: string | HTMLElement, props: Partial<LigandViewerProps> = {}) {
-        const element = typeof elementOrId === 'string' ? document.getElementById(elementOrId)! : elementOrId;
-        if (!element) throw new Error(`Could not get element with id '${elementOrId}'`);
-
-        const o = { ...DefaultLigandViewerProps, ...props };
-
-        const defaultSpec = DefaultPluginUISpec();
-        const spec: PluginUISpec = {
-            ...defaultSpec,
-            actions: defaultSpec.actions,
-            behaviors: [
-                ...defaultSpec.behaviors,
-                ...o.extensions.map(e => LigandExtensions[e]),
-            ],
-            animations: [...defaultSpec.animations?.filter(a => a.name !== AnimateStateSnapshots.name) || []],
-            layout: {
-                initial: {
-                    isExpanded: o.layoutIsExpanded,
-                    showControls: o.layoutShowControls,
-                    controlsDisplay: o.layoutControlsDisplay,
-                },
-            },
-            canvas3d: {
-                ...defaultSpec.canvas3d,
-                ...o.canvas3d,
-                renderer: {
-                    ...defaultSpec.canvas3d?.renderer,
-                    ...o.canvas3d?.renderer,
-                    backgroundColor: o.backgroundColor,
-                },
-                camera: {
-                    helper: {
-                        axes: {
-                            name: 'off', params: {}
-                        }
-                    }
-                }
-            },
-            components: {
-                ...defaultSpec.components,
-                controls: {
-                    ...defaultSpec.components?.controls,
-                    top: 'none',
-                    bottom: o.layoutShowLog ? undefined : 'none',
-                    left: 'none',
-                    right: ControlsWrapper,
-                },
-                remoteState: 'none',
-            },
-            config: [
-                [PluginConfig.VolumeStreaming.Enabled, false],
-                [PluginConfig.Viewport.ShowExpand, o.viewportShowExpand],
-                [PluginConfig.Viewport.ShowSelectionMode, o.viewportShowSelectionMode],
-                [PluginConfig.Viewport.ShowAnimation, false],
-                [PluginConfig.Download.DefaultPdbProvider, 'rcsb'],
-                [PluginConfig.Download.DefaultEmdbProvider, 'rcsb'],
-                [PluginConfig.Structure.DefaultRepresentationPreset, PresetStructureRepresentations.auto.id],
-                // wboit & webgl1 checks are needed to work properly on recent Safari versions
-                [PluginConfig.General.EnableWboit, PluginFeatureDetection.preferWebGl1],
-                [PluginConfig.General.PreferWebGl1, PluginFeatureDetection.preferWebGl1]
-            ]
-        };
-
-        this._plugin = new PluginUIContext(spec);
-        this.modelUrlProviders = o.modelUrlProviders;
-
-        (this._plugin.customState as LigandViewerState) = {
-            showMeasurementsControls: true,
-            showStructureComponentControls: true,
-            modelLoader: new ModelLoader(this._plugin),
-            collapsed: new BehaviorSubject<CollapsedState>({
-                selection: true,
-                measurements: true,
-                strucmotifSubmit: true,
-                superposition: true,
-                quickStyles: true,
-                component: false,
-                volume: true,
-                assemblySymmetry: true,
-                validationReport: true,
-                custom: true,
-            }),
-            ignoreHydrogens: o.ignoreHydrogens,
-            showLabels: o.showLabels,
-            shownCoordinateType: o.shownCoordinateType,
-            aromaticBonds: o.aromaticBonds,
-        };
-
-        this._plugin.init()
-            .then(async () => {
-                const root = createRoot(element);
-                root.render(React.createElement(Plugin, { plugin: this._plugin }));
-
-                if (o.showWelcomeToast) {
-                    await PluginCommands.Toast.Show(this._plugin, {
-                        title: 'Welcome',
-                        message: `RCSB PDB Mol* Ligand Viewer ${RCSB_MOLSTAR_VERSION} [${BUILD_DATE.toLocaleString()}]`,
-                        key: 'toast-welcome',
-                        timeoutMs: 5000
-                    });
-                }
-
-                // allow picking of individual atoms
-                this._plugin.managers.interactivity.setProps({ granularity: 'element' });
-            });
-    }
-
-    private get customState() {
-        return this._plugin.customState as LigandViewerState;
-    }
-
-    clear() {
-        const state = this._plugin.state.data;
-        return PluginCommands.State.RemoveObject(this._plugin, { state, ref: state.tree.root.ref });
-    }
-
-    async loadLigandId(id: string) {
-        for (const provider of this.modelUrlProviders) {
-            try {
-                const p = provider(id);
-                await this.load({ fileOrUrl: p.url, format: p.format, isBinary: p.isBinary });
-            } catch (e) {
-                console.warn(`loading '${id}' failed with '${e}', trying next ligand-loader-provider`);
-            }
-        }
-    }
-
-    private async load(p: LoadParams) {
-        await this.customState.modelLoader.load<any, any>(p, undefined, undefined, ChemicalCompontentTrajectoryHierarchyPreset, { shownCoordinateType: this.customState.shownCoordinateType, aromaticBonds: this.customState.aromaticBonds });
-        await this.syncHydrogenState();
-
-        for (const s of this._plugin.managers.structure.hierarchy.current.structures) {
-            for (const c of s.components) {
-                const isHidden = c.cell.state.isHidden === true || !this.customState.showLabels;
-                await this._plugin.builders.structure.representation.addRepresentation(c.cell, { type: 'label', color: 'uniform', colorParams: { value: ColorNames.black }, typeParams: { level: 'element', fontQuality: 4, borderWidth: 0.1, borderColor: ColorNames.lightgray, attachment: 'bottom-left', ignoreHydrogens: this.customState.ignoreHydrogens } }, { initialState: { isHidden } });
-            }
-        }
-    }
-
-    async toggleHydrogens() {
-        this.customState.ignoreHydrogens = !this.customState.ignoreHydrogens;
-        await this.syncHydrogenState();
-    }
-
-    private async syncHydrogenState() {
-        const update = this._plugin.build();
-        for (const s of this._plugin.managers.structure.hierarchy.current.structures) {
-            for (const c of s.components) {
-                for (const r of c.representations) {
-                    update.to(r.cell).update(StateTransforms.Representation.StructureRepresentation3D, old => {
-                        old.type.params.ignoreHydrogens = this.customState.ignoreHydrogens;
-                    });
-                }
-            }
-        }
-        await update.commit();
-    }
-
-    async toggleLabels() {
-        this.customState.showLabels = !this.customState.showLabels;
-        await this.syncLabelState();
-    }
-
-    private async syncLabelState() {
-        for (const s of this._plugin.managers.structure.hierarchy.current.structures) {
-            for (const c of s.components) {
-                if (c.cell.state.isHidden) continue;
-                for (const r of c.representations) {
-                    if (r.cell.obj?.label !== 'Label') continue;
-                    this._plugin.managers.structure.hierarchy.toggleVisibility([r], this.customState.showLabels ? 'show' : 'hide');
-                }
-            }
-        }
-    }
-}