Browse Source

Merge branch 'master' of https://github.com/molstar/molstar

Alexander Rose 5 years ago
parent
commit
e6a538dbd7
44 changed files with 503 additions and 546 deletions
  1. 3 3
      src/apps/basic-wrapper/index.ts
  2. 1 1
      src/examples/proteopedia-wrapper/index.ts
  3. 1 1
      src/mol-model-props/common/custom-element-property.ts
  4. 26 24
      src/mol-plugin-state/actions/data-format.ts
  5. 3 3
      src/mol-plugin-state/actions/shape.ts
  6. 60 60
      src/mol-plugin-state/actions/structure.ts
  7. 12 11
      src/mol-plugin-state/actions/volume.ts
  8. 10 51
      src/mol-plugin-state/builder/structure.ts
  9. 1 1
      src/mol-plugin-state/builder/structure/preset.ts
  10. 116 0
      src/mol-plugin-state/formats/registry.ts
  11. 93 0
      src/mol-plugin-state/formats/trajectory.ts
  12. 0 0
      src/mol-plugin-state/helpers/model-unitcell.ts
  13. 1 1
      src/mol-plugin-state/helpers/structure-component.ts
  14. 3 3
      src/mol-plugin-state/helpers/structure-selection-query.ts
  15. 0 58
      src/mol-plugin-state/manager/data.ts
  16. 0 17
      src/mol-plugin-state/manager/data/actions.ts
  17. 0 50
      src/mol-plugin-state/manager/data/formats.ts
  18. 0 50
      src/mol-plugin-state/manager/data/provider.ts
  19. 0 27
      src/mol-plugin-state/manager/data/sources.ts
  20. 13 13
      src/mol-plugin-state/manager/interactivity.ts
  21. 1 1
      src/mol-plugin-state/manager/loci-label.ts
  22. 53 22
      src/mol-plugin-state/manager/structure/hierarchy-state.ts
  23. 4 4
      src/mol-plugin-state/manager/structure/selection.ts
  24. 1 1
      src/mol-plugin-state/transforms/model.ts
  25. 1 1
      src/mol-plugin-state/transforms/representation.ts
  26. 1 1
      src/mol-plugin-ui/controls.tsx
  27. 9 9
      src/mol-plugin-ui/sequence/sequence.tsx
  28. 4 4
      src/mol-plugin-ui/structure/measurements.tsx
  29. 4 4
      src/mol-plugin-ui/structure/selection.tsx
  30. 2 2
      src/mol-plugin-ui/toast.tsx
  31. 1 1
      src/mol-plugin/behavior/dynamic/camera.ts
  32. 2 2
      src/mol-plugin/behavior/dynamic/custom-props/computed/accessible-surface-area.ts
  33. 2 2
      src/mol-plugin/behavior/dynamic/custom-props/computed/interactions.ts
  34. 2 2
      src/mol-plugin/behavior/dynamic/custom-props/computed/valence-model.ts
  35. 2 2
      src/mol-plugin/behavior/dynamic/custom-props/pdbe/structure-quality-report.ts
  36. 2 2
      src/mol-plugin/behavior/dynamic/custom-props/rcsb/validation-report.ts
  37. 22 22
      src/mol-plugin/behavior/dynamic/representation.ts
  38. 23 23
      src/mol-plugin/behavior/dynamic/selection/structure-representation-interaction.ts
  39. 1 1
      src/mol-plugin/behavior/static/misc.ts
  40. 5 5
      src/mol-plugin/behavior/static/state.ts
  41. 2 2
      src/mol-plugin/commands.ts
  42. 13 13
      src/mol-plugin/context.ts
  43. 3 3
      src/mol-plugin/state.ts
  44. 0 43
      src/mol-plugin/util/structure-complex-helper.ts

+ 3 - 3
src/apps/basic-wrapper/index.ts

@@ -48,7 +48,7 @@ class BasicWrapper {
         });
 
         this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add(StripedResidues.propertyProvider.descriptor.name, StripedResidues.colorThemeProvider!);
-        this.plugin.lociLabels.addProvider(StripedResidues.labelProvider!);
+        this.plugin.managers.lociLabels.addProvider(StripedResidues.labelProvider!);
         this.plugin.customModelProperties.register(StripedResidues.propertyProvider, true);
     }
 
@@ -173,10 +173,10 @@ class BasicWrapper {
                 'group-by': Q.struct.atomProperty.macromolecular.residueKey()
             }), data);
             const loci = StructureSelection.toLociWithSourceUnits(sel);
-            this.plugin.interactivity.lociHighlights.highlightOnly({ loci });
+            this.plugin.managers.interactivity.lociHighlights.highlightOnly({ loci });
         },
         clearHighlight: () => {
-            this.plugin.interactivity.lociHighlights.highlightOnly({ loci: EmptyLoci });
+            this.plugin.managers.interactivity.lociHighlights.highlightOnly({ loci: EmptyLoci });
         }
     }
 

+ 1 - 1
src/examples/proteopedia-wrapper/index.ts

@@ -72,7 +72,7 @@ class MolStarProteopediaWrapper {
 
         this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add('proteopedia-custom', customColoring);
         this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add(EvolutionaryConservation.propertyProvider.descriptor.name, EvolutionaryConservation.colorThemeProvider!);
-        this.plugin.lociLabels.addProvider(EvolutionaryConservation.labelProvider!);
+        this.plugin.managers.lociLabels.addProvider(EvolutionaryConservation.labelProvider!);
         this.plugin.customModelProperties.register(EvolutionaryConservation.propertyProvider, true);
     }
 

+ 1 - 1
src/mol-model-props/common/custom-element-property.ts

@@ -15,7 +15,7 @@ import { Loci } from '../../mol-model/loci';
 import { OrderedSet } from '../../mol-data/int';
 import { CustomModelProperty } from './custom-model-property';
 import { CustomProperty } from './custom-property';
-import { LociLabelProvider } from '../../mol-plugin/util/loci-label-manager';
+import { LociLabelProvider } from '../../mol-plugin-state/manager/loci-label';
 
 export { CustomElementProperty };
 

+ 26 - 24
src/mol-plugin-state/actions/data-format.ts

@@ -4,17 +4,16 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
+import msgpackDecode from '../../mol-io/common/msgpack/decode';
 import { PluginContext } from '../../mol-plugin/context';
-import { State, StateBuilder, StateAction } from '../../mol-state';
+import { State, StateAction, StateObjectRef } from '../../mol-state';
 import { Task } from '../../mol-task';
 import { FileInfo, getFileInfo } from '../../mol-util/file-info';
-import { PluginStateObject } from '../objects';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
-import { Ccp4Provider, Dsn6Provider, DscifProvider } from './volume';
-import { StateTransforms } from '../transforms';
-import { MmcifProvider, PdbProvider, GroProvider, Provider3dg, DcdProvider, PsfProvider } from './structure';
-import msgpackDecode from '../../mol-io/common/msgpack/decode'
+import { PluginStateObject } from '../objects';
 import { PlyProvider } from './shape';
+import { DcdProvider, GroProvider, MmcifProvider, PdbProvider, Provider3dg, PsfProvider } from './structure';
+import { Ccp4Provider, DscifProvider, Dsn6Provider } from './volume';
 
 export class DataFormatRegistry<D extends PluginStateObject.Data.Binary | PluginStateObject.Data.String> {
     private _list: { name: string, provider: DataFormatProvider<D> }[] = []
@@ -114,7 +113,7 @@ export interface DataFormatProvider<D extends PluginStateObject.Data.Binary | Pl
     stringExtensions: string[]
     binaryExtensions: string[]
     isApplicable(info: FileInfo, data: string | Uint8Array): boolean
-    getDefaultBuilder(ctx: PluginContext, data: StateBuilder.To<D>, options: DataFormatBuilderOptions, state?: State): Task<void>
+    getDefaultBuilder(ctx: PluginContext, data: StateObjectRef<D>, options: DataFormatBuilderOptions, state: State): Task<void>
 }
 
 //
@@ -130,24 +129,27 @@ export const OpenFiles = StateAction.build({
             visuals: PD.Boolean(true, { description: 'Add default visuals' }),
         }
     }
-})(({ params, state }, ctx: PluginContext) => Task.create('Open Files', async taskCtx => {
-    for (let i = 0, il = params.files.length; i < il; ++i) {
-        try {
-            const file = params.files[i]
-            const info = getFileInfo(file)
-            const isBinary = ctx.dataFormat.registry.binaryExtensions.has(info.ext)
-            const data = state.build().toRoot().apply(StateTransforms.Data.ReadFile, { file, isBinary });
-            const dataStateObject = await state.updateTree(data).runInContext(taskCtx);
-            const provider = params.format === 'auto'
-                ? ctx.dataFormat.registry.auto(info, dataStateObject)
-                : ctx.dataFormat.registry.get(params.format)
-            const b = state.build().to(data.ref);
-            // need to await so that the enclosing Task finishes after the update is done.
-            await provider.getDefaultBuilder(ctx, b, { visuals: params.visuals }, state).runInContext(taskCtx)
-        } catch (e) {
-            ctx.log.error(e)
+})(({ params, state }, plugin: PluginContext) => Task.create('Open Files', async taskCtx => {
+    await state.transaction(async () => {
+        for (let i = 0, il = params.files.length; i < il; ++i) {
+            try {
+                const file = params.files[i]
+                const info = getFileInfo(file)
+                const isBinary = plugin.dataFormat.registry.binaryExtensions.has(info.ext)
+                const { data } = await plugin.builders.data.readFile({ file, isBinary });
+                //const data = state.build().toRoot().apply(StateTransforms.Data.ReadFile, { file, isBinary });
+                // const dataStateObject = await state.updateTree(data).runInContext(taskCtx);
+                const provider = params.format === 'auto'
+                    ? plugin.dataFormat.registry.auto(info, data.cell?.obj!)
+                    : plugin.dataFormat.registry.get(params.format)
+                
+                // need to await so that the enclosing Task finishes after the update is done.
+                await provider.getDefaultBuilder(plugin, data, { visuals: params.visuals }, state).runInContext(taskCtx)
+            } catch (e) {
+                plugin.log.error(e)
+            }
         }
-    }
+    }).runInContext(taskCtx);
 }));
 
 //

+ 3 - 3
src/mol-plugin-state/actions/shape.ts

@@ -8,7 +8,6 @@ import { PluginContext } from '../../mol-plugin/context';
 import { State, StateBuilder } from '../../mol-state';
 import { Task } from '../../mol-task';
 import { FileInfo } from '../../mol-util/file-info';
-import { PluginStateObject } from '../objects';
 import { StateTransforms } from '../transforms';
 import { DataFormatProvider, DataFormatBuilderOptions } from './data-format';
 
@@ -20,9 +19,10 @@ export const PlyProvider: DataFormatProvider<any> = {
     isApplicable: (info: FileInfo, data: string) => {
         return info.ext === 'ply'
     },
-    getDefaultBuilder: (ctx: PluginContext, data: StateBuilder.To<PluginStateObject.Data.String>, options: DataFormatBuilderOptions, state: State) => {
+    getDefaultBuilder: (ctx: PluginContext, data, options: DataFormatBuilderOptions, state: State) => {
         return Task.create('PLY default builder', async taskCtx => {
-            let tree: StateBuilder.To<any> = data.apply(StateTransforms.Data.ParsePly)
+            let tree: StateBuilder.To<any> = state.build().to(data)
+                .apply(StateTransforms.Data.ParsePly)
                 .apply(StateTransforms.Model.ShapeFromPly)
             if (options.visuals) {
                 tree = tree.apply(StateTransforms.Representation.ShapeRepresentation3D)

+ 60 - 60
src/mol-plugin-state/actions/structure.ts

@@ -5,23 +5,24 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
+import { Loci } from '../../mol-model/loci';
+import { StructureElement } from '../../mol-model/structure';
 import { PluginContext } from '../../mol-plugin/context';
-import { StateAction, StateBuilder, StateSelection, StateTransformer, State } from '../../mol-state';
+import { StateAction, StateBuilder, StateSelection, StateTransformer } from '../../mol-state';
+import { Task } from '../../mol-task';
+import { UUID } from '../../mol-util';
+import { FileInfo } from '../../mol-util/file-info';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
+import { RootStructureDefinition } from '../helpers/root-structure';
 import { PluginStateObject } from '../objects';
 import { StateTransforms } from '../transforms';
 import { Download, ParsePsf } from '../transforms/data';
-import { CustomModelProperties, CustomStructureProperties, CoordinatesFromDcd, TrajectoryFromModelAndCoordinates, TopologyFromPsf } from '../transforms/model';
-import { DataFormatProvider, guessCifVariant, DataFormatBuilderOptions } from './data-format';
-import { FileInfo } from '../../mol-util/file-info';
-import { Task } from '../../mol-task';
-import { StructureElement } from '../../mol-model/structure';
-import { createDefaultStructureComplex } from '../../mol-plugin/util/structure-complex-helper';
-import { RootStructureDefinition } from '../helpers/root-structure';
-import { UUID } from '../../mol-util';
-import { Loci } from '../../mol-model/loci';
+import { CoordinatesFromDcd, CustomModelProperties, CustomStructureProperties, TopologyFromPsf, TrajectoryFromModelAndCoordinates } from '../transforms/model';
+import { DataFormatProvider, guessCifVariant } from './data-format';
+import { TrajectoryFormat } from '../builder/structure';
+import { BuildInTrajectoryFormat } from '../formats/trajectory';
 
-export const MmcifProvider: DataFormatProvider<any> = {
+export const MmcifProvider: DataFormatProvider<PluginStateObject.Data.String | PluginStateObject.Data.Binary> = {
     label: 'mmCIF',
     description: 'mmCIF',
     stringExtensions: ['cif', 'mmcif', 'mcif'],
@@ -32,10 +33,12 @@ export const MmcifProvider: DataFormatProvider<any> = {
         if (info.ext === 'cif' || info.ext === 'bcif') return guessCifVariant(info, data) !== 'dscif'
         return false
     },
-    getDefaultBuilder: (ctx: PluginContext, data: StateBuilder.To<PluginStateObject.Data.Binary | PluginStateObject.Data.String>, options: DataFormatBuilderOptions, state: State) => {
+    getDefaultBuilder: (ctx: PluginContext, data, options) => {
         return Task.create('mmCIF default builder', async taskCtx => {
-            const traj = createModelTree(data, 'cif');
-            await state.updateTree(options.visuals ? createStructureAndVisuals(ctx, traj, false) : traj).runInContext(taskCtx)
+            const { structure } = await ctx.builders.structure.parseStructure({ data, dataFormat: 'mmcif' });
+            if (options.visuals) {
+                await ctx.builders.representation.structurePreset(structure, 'auto');
+            }
         })
     }
 }
@@ -48,10 +51,12 @@ export const PdbProvider: DataFormatProvider<any> = {
     isApplicable: (info: FileInfo, data: string) => {
         return info.ext === 'pdb' || info.ext === 'ent'
     },
-    getDefaultBuilder: (ctx: PluginContext, data: StateBuilder.To<PluginStateObject.Data.String>, options: DataFormatBuilderOptions, state: State) => {
-        return Task.create('PDB default builder', async taskCtx => {
-            const traj = createModelTree(data, 'pdb');
-            await state.updateTree(options.visuals ? createStructureAndVisuals(ctx, traj, false) : traj).runInContext(taskCtx)
+    getDefaultBuilder: (ctx: PluginContext, data, options) => {
+        return Task.create('PDB default builder', async () => {
+            const { structure } = await ctx.builders.structure.parseStructure({ data, dataFormat: 'pdb' });
+            if (options.visuals) {
+                await ctx.builders.representation.structurePreset(structure, 'auto');
+            }
         })
     }
 }
@@ -64,10 +69,12 @@ export const GroProvider: DataFormatProvider<any> = {
     isApplicable: (info: FileInfo, data: string) => {
         return info.ext === 'gro'
     },
-    getDefaultBuilder: (ctx: PluginContext, data: StateBuilder.To<PluginStateObject.Data.String>, options: DataFormatBuilderOptions, state: State) => {
-        return Task.create('GRO default builder', async taskCtx => {
-            const traj = createModelTree(data, 'gro');
-            await state.updateTree(options.visuals ? createStructureAndVisuals(ctx, traj, false) : traj).runInContext(taskCtx)
+    getDefaultBuilder: (ctx: PluginContext, data, options) => {
+        return Task.create('GRO default builder', async () => {
+            const { structure } = await ctx.builders.structure.parseStructure({ data, dataFormat: 'gro' });
+            if (options.visuals) {
+                await ctx.builders.representation.structurePreset(structure, 'auto');
+            }
         })
     }
 }
@@ -80,10 +87,12 @@ export const Provider3dg: DataFormatProvider<any> = {
     isApplicable: (info: FileInfo, data: string) => {
         return info.ext === '3dg'
     },
-    getDefaultBuilder: (ctx: PluginContext, data: StateBuilder.To<PluginStateObject.Data.String>, options: DataFormatBuilderOptions, state: State) => {
-        return Task.create('3DG default builder', async taskCtx => {
-            const traj = createModelTree(data, '3dg');
-            await state.updateTree(options.visuals ? createStructureAndVisuals(ctx, traj, false) : traj).runInContext(taskCtx)
+    getDefaultBuilder: (ctx: PluginContext, data, options) => {
+        return Task.create('3DG default builder', async () => {
+            const { structure } = await ctx.builders.structure.parseStructure({ data, dataFormat: '3dg' });
+            if (options.visuals) {
+                await ctx.builders.representation.structurePreset(structure, 'auto');
+            }
         })
     }
 }
@@ -96,9 +105,10 @@ export const PsfProvider: DataFormatProvider<any> = {
     isApplicable: (info: FileInfo, data: string) => {
         return info.ext === 'psf'
     },
-    getDefaultBuilder: (ctx: PluginContext, data: StateBuilder.To<PluginStateObject.Data.String>, options: DataFormatBuilderOptions, state: State) => {
+    getDefaultBuilder: (ctx: PluginContext, data, options, state) => {
         return Task.create('PSF default builder', async taskCtx => {
-            await state.updateTree(data.apply(ParsePsf, {}, { state: { isGhost: true } }).apply(TopologyFromPsf)).runInContext(taskCtx)
+            const build = state.build().to(data).apply(ParsePsf, {}, { state: { isGhost: true } }).apply(TopologyFromPsf)
+            await state.updateTree(build).runInContext(taskCtx)
         })
     }
 }
@@ -111,15 +121,14 @@ export const DcdProvider: DataFormatProvider<any> = {
     isApplicable: (info: FileInfo, data: string) => {
         return info.ext === 'dcd'
     },
-    getDefaultBuilder: (ctx: PluginContext, data: StateBuilder.To<PluginStateObject.Data.Binary>, options: DataFormatBuilderOptions, state: State) => {
+    getDefaultBuilder: (ctx: PluginContext, data, options, state) => {
         return Task.create('DCD default builder', async taskCtx => {
-            await state.updateTree(data.apply(CoordinatesFromDcd)).runInContext(taskCtx)
+            const build = state.build().to(data).apply(CoordinatesFromDcd);
+            await state.updateTree(build).runInContext(taskCtx)
         })
     }
 }
 
-type StructureFormat = 'pdb' | 'cif' | 'gro' | '3dg'
-
 //
 
 const DownloadModelRepresentationOptions = PD.Group({
@@ -166,7 +175,7 @@ const DownloadStructure = StateAction.build({
             }, { isFlat: true, label: 'SWISS-MODEL', description: 'Loads the best homology model or experimental structure' }),
             'url': PD.Group({
                 url: PD.Text(''),
-                format: PD.Select('cif', [['cif', 'CIF'], ['pdb', 'PDB']] as ['cif' | 'pdb', string][]),
+                format: PD.Select('mmcif', [['mmcif', 'CIF'], ['pdb', 'PDB']] as ['mmcif' | 'pdb', string][]),
                 isBinary: PD.Boolean(false),
                 structure: DownloadModelRepresentationOptions,
                 options: PD.Group({
@@ -177,11 +186,11 @@ const DownloadStructure = StateAction.build({
         })
     }
 })(({ params, state }, plugin: PluginContext) => Task.create('Download Structure', async ctx => {
-    // plugin.behaviors.layout.leftPanelTabName.next('data');
+    plugin.behaviors.layout.leftPanelTabName.next('data');
 
     const src = params.source;
     let downloadParams: StateTransformer.Params<Download>[];
-    let supportProps = false, asTrajectory = false, format: StructureFormat = 'cif';
+    let supportProps = false, asTrajectory = false, format: BuildInTrajectoryFormat = 'mmcif';
 
     switch (src.name) {
         case 'url':
@@ -268,7 +277,7 @@ function getDownloadParams(src: string, url: (id: string) => string, label: (id:
     return ret;
 }
 
-export function createModelTree(b: StateBuilder.To<PluginStateObject.Data.Binary | PluginStateObject.Data.String>, format: StructureFormat = 'cif') {
+export function createModelTree(b: StateBuilder.To<PluginStateObject.Data.Binary | PluginStateObject.Data.String>, format: TrajectoryFormat = 'cif') {
     let parsed: StateBuilder.To<PluginStateObject.Molecule.Trajectory>
     switch (format) {
         case 'cif':
@@ -291,20 +300,6 @@ export function createModelTree(b: StateBuilder.To<PluginStateObject.Data.Binary
     return parsed.apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 });
 }
 
-function createStructure(b: StateBuilder.To<PluginStateObject.Molecule.Model>, supportProps: boolean, params?: RootStructureDefinition.Params) {
-    let root = b;
-    if (supportProps) {
-        root = root.apply(StateTransforms.Model.CustomModelProperties);
-    }
-    return root.apply(StateTransforms.Model.StructureFromModel, { type: params || { name: 'assembly', params: { } } });
-}
-
-function createStructureAndVisuals(ctx: PluginContext, b: StateBuilder.To<PluginStateObject.Molecule.Model>, supportProps: boolean, params?: RootStructureDefinition.Params) {
-    const structure = createStructure(b, supportProps, params);
-    createDefaultStructureComplex(ctx, structure);
-    return b;
-}
-
 export const Create3DRepresentationPreset = StateAction.build({
     display: { name: '3D Representation Preset', description: 'Create one of preset 3D representations.' },
     from: PluginStateObject.Molecule.Structure,
@@ -433,13 +428,18 @@ export const AddTrajectory = StateAction.build({
             coordinates: PD.Select(coordOptions.length ? coordOptions[0][0] : '', coordOptions)
         }
     }
-})(({ ref, params, state }, ctx: PluginContext) => {
-    const dependsOn = [params.model, params.coordinates];
-    const root = state.build().toRoot()
-        .apply(TrajectoryFromModelAndCoordinates, {
-            modelRef: params.model,
-            coordinatesRef: params.coordinates
-        }, { dependsOn })
-        .apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 })
-    return state.updateTree(createStructureAndVisuals(ctx, root, false));
-});
+})(({ params, state }, ctx: PluginContext) => Task.create('Add Trajectory', taskCtx => {
+    return state.transaction(async () => {
+        const dependsOn = [params.model, params.coordinates];
+        const model = state.build().toRoot()
+            .apply(TrajectoryFromModelAndCoordinates, {
+                modelRef: params.model,
+                coordinatesRef: params.coordinates
+            }, { dependsOn })
+            .apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 });
+
+        await state.updateTree(model).runInContext(taskCtx);
+        const structure = await ctx.builders.structure.createStructure(model.selector);
+        await ctx.builders.representation.structurePreset(structure, 'auto');
+    }).runInContext(taskCtx)
+}));

+ 12 - 11
src/mol-plugin-state/actions/volume.ts

@@ -26,9 +26,10 @@ export const Ccp4Provider: DataFormatProvider<any> = {
     isApplicable: (info: FileInfo, data: Uint8Array) => {
         return info.ext === 'ccp4' || info.ext === 'mrc' || info.ext === 'map'
     },
-    getDefaultBuilder: (ctx: PluginContext, data: StateBuilder.To<PluginStateObject.Data.Binary>, options: DataFormatBuilderOptions, state: State) => {
+    getDefaultBuilder: (ctx: PluginContext, data, options: DataFormatBuilderOptions, state: State) => {
         return Task.create('CCP4/MRC/BRIX default builder', async taskCtx => {
-            let tree: StateBuilder.To<any> = data.apply(StateTransforms.Data.ParseCcp4, {}, { state: { isGhost: true } })
+            let tree: StateBuilder.To<any> = state.build().to(data)
+                .apply(StateTransforms.Data.ParseCcp4, {}, { state: { isGhost: true } })
                 .apply(StateTransforms.Volume.VolumeFromCcp4)
             if (options.visuals) {
                 tree = tree.apply(StateTransforms.Representation.VolumeRepresentation3D)
@@ -46,9 +47,10 @@ export const Dsn6Provider: DataFormatProvider<any> = {
     isApplicable: (info: FileInfo, data: Uint8Array) => {
         return info.ext === 'dsn6' || info.ext === 'brix'
     },
-    getDefaultBuilder: (ctx: PluginContext, data: StateBuilder.To<PluginStateObject.Data.Binary>, options: DataFormatBuilderOptions, state: State) => {
+    getDefaultBuilder: (ctx: PluginContext, data, options: DataFormatBuilderOptions, state: State) => {
         return Task.create('DSN6/BRIX default builder', async taskCtx => {
-            let tree: StateBuilder.To<any> = data.apply(StateTransforms.Data.ParseDsn6, {}, { state: { isGhost: true } })
+            let tree: StateBuilder.To<any> = state.build().to(data)
+                .apply(StateTransforms.Data.ParseDsn6, {}, { state: { isGhost: true } })
                 .apply(StateTransforms.Volume.VolumeFromDsn6)
             if (options.visuals) {
                 tree = tree.apply(StateTransforms.Representation.VolumeRepresentation3D)
@@ -66,9 +68,9 @@ export const DscifProvider: DataFormatProvider<any> = {
     isApplicable: (info: FileInfo, data: Uint8Array | string) => {
         return guessCifVariant(info, data) === 'dscif' ? true : false
     },
-    getDefaultBuilder: (ctx: PluginContext, data: StateBuilder.To<PluginStateObject.Data.Binary | PluginStateObject.Data.String>, options: DataFormatBuilderOptions, state: State) => {
+    getDefaultBuilder: (ctx: PluginContext, data, options: DataFormatBuilderOptions, state: State) => {
         return Task.create('DensityServer CIF default builder', async taskCtx => {
-            const cifBuilder = data.apply(StateTransforms.Data.ParseCif)
+            const cifBuilder = state.build().to(data).apply(StateTransforms.Data.ParseCif)
             const cifStateObject = await state.updateTree(cifBuilder).runInContext(taskCtx)
             const b = state.build().to(cifBuilder.ref);
             const blocks = cifStateObject.data.blocks.slice(1); // zero block contains query meta-data
@@ -185,13 +187,13 @@ const DownloadDensity = StateAction.build({
         default: throw new Error(`${(src as any).name} not supported.`);
     }
 
-    const data = state.build().toRoot().apply(StateTransforms.Data.Download, downloadParams);
-    const dataStateObject = await state.updateTree(data).runInContext(taskCtx);
+    const data = await ctx.builders.data.download(downloadParams);
+    //const dataStateObject = await state.updateTree(data).runInContext(taskCtx);
 
     switch (src.name) {
         case 'url':
             downloadParams = src.params;
-            provider = src.params.format === 'auto' ? ctx.dataFormat.registry.auto(getFileInfo(downloadParams.url), dataStateObject) : ctx.dataFormat.registry.get(src.params.format)
+            provider = src.params.format === 'auto' ? ctx.dataFormat.registry.auto(getFileInfo(downloadParams.url), data.cell?.obj!) : ctx.dataFormat.registry.get(src.params.format)
             break;
         case 'pdbe':
             provider = ctx.dataFormat.registry.get('ccp4')
@@ -206,6 +208,5 @@ const DownloadDensity = StateAction.build({
         default: throw new Error(`${(src as any).name} not supported.`);
     }
 
-    const b = state.build().to(data.ref);
-    await provider.getDefaultBuilder(ctx, b, { visuals: true }, state).runInContext(taskCtx)
+    await provider.getDefaultBuilder(ctx, data, { visuals: true }, state).runInContext(taskCtx)
 }));

+ 10 - 51
src/mol-plugin-state/builder/structure.ts

@@ -5,13 +5,14 @@
  */
 
 import { PluginContext } from '../../mol-plugin/context';
-import { StateBuilder, StateObjectRef, StateObjectSelector, StateTransformer } from '../../mol-state';
+import { StateObjectRef, StateObjectSelector, StateTransformer } from '../../mol-state';
 import { PluginStateObject as SO } from '../objects';
 import { StateTransforms } from '../transforms';
 import { RootStructureDefinition } from '../helpers/root-structure';
 import { StructureComponentParams } from '../helpers/structure-component';
+import { BuildInTrajectoryFormat, TrajectoryFormatProvider } from '../formats/trajectory';
 
-type TrajectoryFormat = 'pdb' | 'cif' | 'gro' | '3dg'
+export type TrajectoryFormat = 'pdb' | 'cif' | 'gro' | '3dg'
 
 export enum StructureBuilderTags {
     Trajectory = 'trajectory',
@@ -27,32 +28,11 @@ export class StructureBuilder {
         return this.plugin.state.dataState;
     }
 
-    private async parseTrajectoryData(data: StateObjectRef<SO.Data.Binary | SO.Data.String>, format: TrajectoryFormat) {
-        const state = this.dataState;
-        const root = state.build().to(data);
-        let parsed: StateBuilder.To<SO.Molecule.Trajectory>;
-        const tag = { tags: StructureBuilderTags.Trajectory };
-        switch (format) {
-            case 'cif':
-                parsed = root.apply(StateTransforms.Data.ParseCif, void 0, { state: { isGhost: true } })
-                    .apply(StateTransforms.Model.TrajectoryFromMmCif, void 0, tag)
-                break
-            case 'pdb':
-                parsed = root.apply(StateTransforms.Model.TrajectoryFromPDB, void 0, tag);
-                break
-            case 'gro':
-                parsed = root.apply(StateTransforms.Model.TrajectoryFromGRO, void 0, tag);
-                break
-            case '3dg':
-                parsed = root.apply(StateTransforms.Model.TrajectoryFrom3DG, void 0, tag);
-                break
-            default:
-                throw new Error('unsupported format')
-        }
-
-        await this.plugin.runTask(this.dataState.updateTree(parsed, { revertOnError: true }));
-
-        return parsed.selector;
+    private async parseTrajectoryData(data: StateObjectRef<SO.Data.Binary | SO.Data.String>, format: BuildInTrajectoryFormat | TrajectoryFormatProvider) {
+        const provider = typeof format === 'string' ? this.plugin.dataFormat.trajectory.get(format) : format;
+        if (!provider) throw new Error(`'${format}' is not a supported data format.`);
+        const { trajectory } = await provider.parse(this.plugin, data, { trajectoryTags: StructureBuilderTags.Trajectory });
+        return trajectory;
     }
 
     private async parseTrajectoryBlob(data: StateObjectRef<SO.Data.Blob>, params: StateTransformer.Params<StateTransforms['Data']['ParseBlob']>) {
@@ -66,7 +46,7 @@ export class StructureBuilder {
 
     async parseStructure(params: {
         data?: StateObjectRef<SO.Data.Binary | SO.Data.String>,
-        dataFormat?: TrajectoryFormat,
+        dataFormat?: BuildInTrajectoryFormat | TrajectoryFormatProvider,
         blob?: StateObjectRef<SO.Data.Blob>
         blobParams?: StateTransformer.Params<StateTransforms['Data']['ParseBlob']>,
         model?: StateTransformer.Params<StateTransforms['Model']['ModelFromTrajectory']>,
@@ -97,11 +77,9 @@ export class StructureBuilder {
         };
     }
 
-    async parseTrajectory(data: StateObjectRef<SO.Data.Binary | SO.Data.String>, format: TrajectoryFormat): Promise<StateObjectSelector<SO.Molecule.Trajectory>>
+    async parseTrajectory(data: StateObjectRef<SO.Data.Binary | SO.Data.String>, format: BuildInTrajectoryFormat | TrajectoryFormatProvider): Promise<StateObjectSelector<SO.Molecule.Trajectory>>
     async parseTrajectory(blob: StateObjectRef<SO.Data.Blob>, params: StateTransformer.Params<StateTransforms['Data']['ParseBlob']>): Promise<StateObjectSelector<SO.Molecule.Trajectory>>
     async parseTrajectory(data: StateObjectRef, params: any) {
-        // TODO: proper format support
-        // needs to integrated to transforms directly because of blobs
         const cell = StateObjectRef.resolveAndCheck(this.dataState, data as StateObjectRef);
         if (!cell) throw new Error('Invalid data cell.');
 
@@ -114,21 +92,11 @@ export class StructureBuilder {
 
     async createModel(trajectory: StateObjectRef<SO.Molecule.Trajectory>, params?: StateTransformer.Params<StateTransforms['Model']['ModelFromTrajectory']>) {
         const state = this.dataState;
-
         const model = state.build().to(trajectory)
             .apply(StateTransforms.Model.ModelFromTrajectory, params || { modelIndex: 0 }, { tags: StructureBuilderTags.Model });
 
-        // const props = !!params?.properties 
-        //     ? model.apply(StateTransforms.Model.CustomModelProperties, typeof params?.properties !== 'boolean' ? params?.properties : void 0, { tags: StructureBuilderTags.ModelProperties, isDecorator: true })
-        //     : void 0;
-
         await this.plugin.runTask(this.dataState.updateTree(model, { revertOnError: true }));
-
         return model.selector;
-
-        // const modelSelector = model.selector, propertiesSelector = props?.selector;
-
-        // return { model: propertiesSelector || modelSelector, index: modelSelector, properties: propertiesSelector };
     }
 
     async insertModelProperties(model: StateObjectRef<SO.Molecule.Model>, params?: StateTransformer.Params<StateTransforms['Model']['CustomModelProperties']>) {
@@ -144,17 +112,8 @@ export class StructureBuilder {
         const structure = state.build().to(model)
             .apply(StateTransforms.Model.StructureFromModel, { type: params || { name: 'assembly', params: { } } }, { tags: StructureBuilderTags.Structure });        
 
-        // const props = !!params?.properties 
-        //     ? structure.apply(StateTransforms.Model.CustomStructureProperties, typeof params?.properties !== 'boolean' ? params?.properties : void 0, { tags: StructureBuilderTags.StructureProperties, isDecorator: true })
-        //     : void 0;
-
         await this.plugin.runTask(this.dataState.updateTree(structure, { revertOnError: true }));
-
         return structure.selector;
-
-        // const structureSelector = structure.selector, propertiesSelector = props?.selector;
-
-        // return { structure: propertiesSelector || structureSelector, definition: structureSelector, properties: propertiesSelector };
     }
 
     async insertStructureProperties(structure: StateObjectRef<SO.Molecule.Structure>, params?: StateTransformer.Params<StateTransforms['Model']['CustomStructureProperties']>) {

+ 1 - 1
src/mol-plugin-state/builder/structure/preset.ts

@@ -7,7 +7,7 @@
 
 import { StateTransforms } from '../../transforms';
 import { StructureRepresentation3DHelpers } from '../../transforms/representation';
-import { StructureSelectionQueries as Q } from '../../../mol-plugin/util/structure-selection-query';
+import { StructureSelectionQueries as Q } from '../../helpers/structure-selection-query';
 import { BuiltInStructureRepresentations } from '../../../mol-repr/structure/registry';
 import { StructureRepresentationProvider, RepresentationProviderTags } from './provider';
 import { StateObjectRef } from '../../../mol-state';

+ 116 - 0
src/mol-plugin-state/formats/registry.ts

@@ -0,0 +1,116 @@
+/**
+ * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import msgpackDecode from '../../mol-io/common/msgpack/decode';
+import { PluginContext } from '../../mol-plugin/context';
+import { StateObjectRef } from '../../mol-state';
+import { FileInfo } from '../../mol-util/file-info';
+import { PluginStateObject } from '../objects';
+
+export class DataFormatRegistry<Provider extends DataFormatProvider> {
+    private _list: { name: string, provider: Provider }[] = []
+    private _map = new Map<string, Provider>()
+    private _extensions: Set<string> | undefined = undefined
+    private _binaryExtensions: Set<string> | undefined = undefined
+    private _options: [string, string][] | undefined = undefined
+
+    get types(): [string, string][] {
+        return this._list.map(e => [e.name, e.provider.label] as [string, string]);
+    }
+
+    get extensions() {
+        if (this._extensions) return this._extensions
+        const extensions = new Set<string>()
+        this._list.forEach(({ provider }) => {
+            provider.stringExtensions.forEach(ext => extensions.add(ext))
+            provider.binaryExtensions.forEach(ext => extensions.add(ext))
+        })
+        this._extensions = extensions
+        return extensions
+    }
+
+    get binaryExtensions() {
+        if (this._binaryExtensions) return this._binaryExtensions
+        const binaryExtensions = new Set<string>()
+        this._list.forEach(({ provider }) => provider.binaryExtensions.forEach(ext => binaryExtensions.add(ext)))
+        this._binaryExtensions = binaryExtensions
+        return binaryExtensions
+    }
+
+    get options() {
+        if (this._options) return this._options
+        const options: [string, string][] = [['auto', 'Automatic']]
+        this._list.forEach(({ name, provider }) => options.push([ name, provider.label ]))
+        this._options = options
+        return options
+    }
+
+    constructor(buildInFormats: ReadonlyArray<readonly [string, Provider]>) {
+        for (const [id, p] of buildInFormats) this.add(id, p);
+    };
+
+    private _clear() {
+        this._extensions = undefined
+        this._binaryExtensions = undefined
+        this._options = undefined
+    }
+
+    add(name: string, provider: Provider) {
+        this._clear()
+        this._list.push({ name, provider })
+        this._map.set(name, provider)
+    }
+
+    remove(name: string) {
+        this._clear()
+        this._list.splice(this._list.findIndex(e => e.name === name), 1)
+        this._map.delete(name)
+    }
+
+    auto(info: FileInfo, dataStateObject: PluginStateObject.Data.Binary | PluginStateObject.Data.String) {
+        for (let i = 0, il = this.list.length; i < il; ++i) {
+            const { provider } = this._list[i];
+            if (provider.isApplicable(info, dataStateObject.data)) return provider;
+        }
+        return;
+    }
+
+    get(name: string): Provider | undefined {
+        if (this._map.has(name)) {
+            return this._map.get(name)!
+        } else {
+            throw new Error(`unknown data format name '${name}'`)
+        }
+    }
+
+    get list() {
+        return this._list
+    }
+}
+
+export interface DataFormatProvider<P = any, R = any> {
+    label: string
+    description: string
+    stringExtensions: string[]
+    binaryExtensions: string[]
+    isApplicable(info: FileInfo, data: string | Uint8Array): boolean
+    parse(plugin: PluginContext, data: StateObjectRef<PluginStateObject.Data.Binary | PluginStateObject.Data.String>, params: P | undefined): Promise<R>
+}
+
+type cifVariants = 'dscif' | -1
+export function guessCifVariant(info: FileInfo, data: Uint8Array | string): cifVariants {
+    if (info.ext === 'bcif') {
+        try {
+            // TODO: find a way to run msgpackDecode only once
+            //      now it is run twice, here and during file parsing
+            if (msgpackDecode(data as Uint8Array).encoder.startsWith('VolumeServer')) return 'dscif'
+        } catch { }
+    } else if (info.ext === 'cif') {
+        if ((data as string).startsWith('data_SERVER\n#\n_density_server_result')) return 'dscif'
+    }
+    return -1
+}

+ 93 - 0
src/mol-plugin-state/formats/trajectory.ts

@@ -0,0 +1,93 @@
+/**
+ * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { FileInfo } from '../../mol-util/file-info';
+import { StateTransforms } from '../transforms';
+import { guessCifVariant, DataFormatProvider, DataFormatRegistry } from './registry';
+import { StateTransformer, StateObjectRef } from '../../mol-state';
+import { PluginStateObject } from '../objects';
+
+export interface TrajectoryFormatProvider<P extends { trajectoryTags?: string | string[] } = { trajectoryTags?: string | string[] }, R extends { trajectory: StateObjectRef<PluginStateObject.Molecule.Trajectory> } = { trajectory: StateObjectRef<PluginStateObject.Molecule.Trajectory> }> 
+    extends DataFormatProvider<P, R> {
+}
+
+export function TrajectoryFormatRegistry() {
+    return new DataFormatRegistry<TrajectoryFormatProvider>(BuildInTrajectoryFormats);
+}
+
+export const MmcifProvider: TrajectoryFormatProvider = {
+    label: 'mmCIF',
+    description: 'mmCIF',
+    stringExtensions: ['cif', 'mmcif', 'mcif'],
+    binaryExtensions: ['bcif'],
+    isApplicable: (info: FileInfo, data: Uint8Array | string) => {
+        if (info.ext === 'mmcif' || info.ext === 'mcif') return true
+        // assume cif/bcif files that are not DensityServer CIF are mmCIF
+        if (info.ext === 'cif' || info.ext === 'bcif') return guessCifVariant(info, data) !== 'dscif'
+        return false
+    },
+    parse: async (plugin, data, params) => {
+        const state = plugin.state.dataState;
+        const trajectory = state.build().to(data)
+            .apply(StateTransforms.Data.ParseCif, void 0, { state: { isGhost: true } })
+            .apply(StateTransforms.Model.TrajectoryFromMmCif, void 0, { tags: params?.trajectoryTags })
+        await plugin.runTask(state.updateTree(trajectory, { revertOnError: true }));
+        return { trajectory: trajectory.selector };
+    }
+}
+
+function directTrajectory(transformer: StateTransformer<PluginStateObject.Data.String | PluginStateObject.Data.Binary, PluginStateObject.Molecule.Trajectory>): TrajectoryFormatProvider['parse'] {
+    return async (plugin, data, params) => {
+        const state = plugin.state.dataState;
+        const trajectory = state.build().to(data)
+            .apply(transformer, void 0, { tags: params?.trajectoryTags })
+        await plugin.runTask(state.updateTree(trajectory, { revertOnError: true }));
+        return { trajectory: trajectory.selector };
+    }
+}
+
+export const PdbProvider: TrajectoryFormatProvider = {
+    label: 'PDB',
+    description: 'PDB',
+    stringExtensions: ['pdb', 'ent'],
+    binaryExtensions: [],
+    isApplicable: (info: FileInfo, data: string) => {
+        return info.ext === 'pdb' || info.ext === 'ent'
+    },
+    parse: directTrajectory(StateTransforms.Model.TrajectoryFromPDB)
+}
+
+export const GroProvider: TrajectoryFormatProvider = {
+    label: 'GRO',
+    description: 'GRO',
+    stringExtensions: ['gro'],
+    binaryExtensions: [],
+    isApplicable: (info: FileInfo, data: string) => {
+        return info.ext === 'gro'
+    },
+    parse: directTrajectory(StateTransforms.Model.TrajectoryFromGRO)
+}
+
+export const Provider3dg: TrajectoryFormatProvider = {
+    label: '3DG',
+    description: '3DG',
+    stringExtensions: ['3dg'],
+    binaryExtensions: [],
+    isApplicable: (info: FileInfo, data: string) => {
+        return info.ext === '3dg'
+    },
+    parse: directTrajectory(StateTransforms.Model.TrajectoryFrom3DG)
+}
+
+export const BuildInTrajectoryFormats = [
+    ['mmcif', MmcifProvider] as const,
+    ['pdb', PdbProvider] as const,
+    ['gro', GroProvider] as const,
+    ['3dg', Provider3dg] as const,
+] as const
+
+export type BuildInTrajectoryFormat = (typeof BuildInTrajectoryFormats)[number][0]

+ 0 - 0
src/mol-plugin/util/model-unitcell.ts → src/mol-plugin-state/helpers/model-unitcell.ts


+ 1 - 1
src/mol-plugin-state/helpers/structure-component.ts

@@ -10,7 +10,7 @@ import { MolScriptBuilder } from '../../mol-script/language/builder';
 import { StructureElement, Structure, StructureSelection as Sel, StructureQuery, Queries, QueryContext } from '../../mol-model/structure';
 import { StructureQueryHelper } from './structure-query';
 import { PluginStateObject as SO } from '../objects';
-import { StructureSelectionQueries } from '../../mol-plugin/util/structure-selection-query';
+import { StructureSelectionQueries } from './structure-selection-query';
 import { StateTransformer, StateObject } from '../../mol-state';
 import { Script } from '../../mol-script/script';
 

+ 3 - 3
src/mol-plugin/util/structure-selection-query.ts → src/mol-plugin-state/helpers/structure-selection-query.ts

@@ -10,8 +10,8 @@ import { AccessibleSurfaceAreaProvider, AccessibleSurfaceAreaSymbols } from '../
 import { ValidationReport, ValidationReportProvider } from '../../mol-model-props/rcsb/validation-report';
 import { QueryContext, Structure, StructureQuery, StructureSelection } from '../../mol-model/structure';
 import { BondType, NucleicBackboneAtoms, ProteinBackboneAtoms, SecondaryStructureType } from '../../mol-model/structure/model/types';
-import { PluginStateObject } from '../../mol-plugin-state/objects';
-import { StateTransforms } from '../../mol-plugin-state/transforms';
+import { PluginStateObject } from '../objects';
+import { StateTransforms } from '../transforms';
 import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
 import Expression from '../../mol-script/language/expression';
 import { compile } from '../../mol-script/runtime/query/compiler';
@@ -19,7 +19,7 @@ import { StateBuilder } from '../../mol-state';
 import { RuntimeContext } from '../../mol-task';
 import { SetUtils } from '../../mol-util/set';
 import { stringToWords } from '../../mol-util/string';
-import { PluginContext } from '../context';
+import { PluginContext } from '../../mol-plugin/context';
 
 export enum StructureSelectionCategory {
     Type = 'Type',

+ 0 - 58
src/mol-plugin-state/manager/data.ts

@@ -1,58 +0,0 @@
-/**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
-
-import { StateTransformer, StateTransform, StateObjectSelector, StateObjectCell } from '../../mol-state';
-import { PluginContext } from '../../mol-plugin/context';
-import { Download, ReadFile } from '../transforms/data';
-import { getFileInfo } from '../../mol-util/file-info';
-import { DataFormatProvider } from './data/provider';
-import { BuiltInDataFormats } from './data/formats';
-import { objectForEach } from '../../mol-util/object';
-
-export class DataManager {
-    readonly formats: DataFormatProvider[] = [];
-
-    addFormat(p: DataFormatProvider) {
-        this.formats.push(p);
-    }
-
-    get dataState() {
-        return this.plugin.state.dataState;
-    }
-
-    async download(params: StateTransformer.Params<Download>, options?: Partial<StateTransform.Options>) {
-        const data = this.dataState.build().toRoot().apply(Download, params, options);
-        await this.plugin.runTask(this.dataState.updateTree(data));
-        return { data: data.selector };
-    }
-
-    async readFile(params: StateTransformer.Params<ReadFile>, options?: Partial<StateTransform.Options>) {
-        const data = this.dataState.build().toRoot().apply(ReadFile, params, options);
-        const fileInfo = getFileInfo(params.file);
-        await this.plugin.runTask(this.dataState.updateTree(data));
-        return { data: data.selector, fileInfo };
-    }
-
-    async parse<K extends keyof BuiltInDataFormats, P extends BuiltInDataFormats[K]>(provider: K, data: StateObjectSelector<DataFormatProvider.Data<P>> | StateObjectCell | string, params?: DataFormatProvider.Params<P>): Promise<DataFormatProvider.Ret<P>>
-    async parse<P extends DataFormatProvider>(provider: P, data: StateObjectSelector<DataFormatProvider.Data<P>> | StateObjectCell | string, params?: DataFormatProvider.Params<P>): Promise<DataFormatProvider.Ret<P>>
-    async parse<P extends DataFormatProvider>(providerOrBuildIn: P | string, data: StateObjectSelector<DataFormatProvider.Data<P>> | StateObjectCell | StateTransform.Ref, params?: DataFormatProvider.Params<P>) {
-        const provider: P = typeof providerOrBuildIn === 'string' ? BuiltInDataFormats[providerOrBuildIn as keyof BuiltInDataFormats] as unknown as P : providerOrBuildIn as P;
-        const cell = StateObjectCell.resolve(this.dataState, data);
-        if (!cell) {
-            throw new Error('Could not resolve data cell.');
-        }
-        return provider.apply({ state: this.dataState, plugin: this.plugin }, cell, params);
-    }
-
-    // async test() {
-    //     const { data } = await this.download({ url: '' });
-    //     const cif = await this.parse('mmcif', data);
-    // }
-
-    constructor(public plugin: PluginContext) {
-        objectForEach(BuiltInDataFormats, f => this.formats.push(f));
-    }
-}

+ 0 - 17
src/mol-plugin-state/manager/data/actions.ts

@@ -1,17 +0,0 @@
-/**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
-
-import { BuilderAction } from '../base';
-import { StateTransformer } from '../../../mol-state';
-import { Download as DownloadData, ReadFile } from '../../transforms/data';
-
-export const Download = BuilderAction((builder, params: StateTransformer.Params<DownloadData>, { options }) => {
-    return builder.apply(DownloadData, params, options);
-});
-
-export const OpenFile = BuilderAction((builder, params: StateTransformer.Params<ReadFile>, { options }) => {
-    return builder.apply(ReadFile, params, options);
-});

+ 0 - 50
src/mol-plugin-state/manager/data/formats.ts

@@ -1,50 +0,0 @@
-/**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
-
-import { DataFormatProvider } from './provider';
-import { PluginContext } from '../../../mol-plugin/context';
-import { FileInfo } from '../../../mol-util/file-info';
-import { StateTransforms } from '../../transforms';
-
-export const MmcifFormatProvider = DataFormatProvider({
-    id: 'mmcif',
-    display: { name: 'mmCIF', group: 'Molecule' },
-    extensions: { text: ['cif', 'mmcif', 'mcif'], binary: ['bcif'] }
-})({
-    isApplicable(plugin: PluginContext, data: string | Uint8Array, info?: FileInfo): boolean {
-        // TODO: check CIF variants
-        return true;
-    },
-    async apply({ plugin, state }, data) {
-        const dictionary = state.build().to(data).apply(StateTransforms.Data.ParseCif, void 0, { state: { isGhost: true } });
-        const trajectory = dictionary.apply(StateTransforms.Model.TrajectoryFromMmCif);
-        await plugin.runTask(state.updateTree(trajectory));
-        return { dictionary: dictionary.selector, trajectory: trajectory.selector };
-    }
-});
-
-export const PdbFormatProvider = DataFormatProvider({
-    id: 'pdb',
-    display: { name: 'PDB', group: 'Molecule' },
-    extensions: { text: ['pdb', 'ent'] }
-})({
-    async apply({ plugin, state }, data) {
-        const trajectory = state.build().to(data).apply(StateTransforms.Model.TrajectoryFromPDB);
-        await plugin.runTask(state.updateTree(trajectory));
-        return { trajectory: trajectory.selector };
-    }
-});
-
-export const BuiltInDataFormats = {
-    'mmcif': MmcifFormatProvider,
-    'pdb': PdbFormatProvider
-}
-export type BuiltInDataFormats = typeof BuiltInDataFormats
-
-// export const TrajectoryFormatProviders = {
-//     'mmcif': MmcifFormatProvider,
-//     'pdb': PdbFormatProvider
-// }

+ 0 - 50
src/mol-plugin-state/manager/data/provider.ts

@@ -1,50 +0,0 @@
-/**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
-
-import { PluginContext } from '../../../mol-plugin/context';
-import { State, StateObjectCell } from '../../../mol-state';
-import { ParamDefinition as PD } from '../../../mol-util/param-definition';
-import { PluginStateObject } from '../../objects';
-import { FileInfo } from '../../../mol-util/file-info';
-
-export { DataFormatProvider }
-
-type DataFormatProvider<Id extends string = string, D extends PluginStateObject.Data.Binary | PluginStateObject.Data.String = PluginStateObject.Data.Binary | PluginStateObject.Data.String, P = any, S = {}> =
-    DataFormatProvider.Base<Id, P> & DataFormatProvider.Definition<D, P, S>
-
-function DataFormatProvider<Id extends string, P = {}>(provider: DataFormatProvider.Base<Id, P>) {
-    return function<D extends PluginStateObject.Data.Binary | PluginStateObject.Data.String, S>(p: DataFormatProvider.Definition<D, P, S>): DataFormatProvider<Id, D, P, S> {
-        return { ...provider, ...p };
-    };
-}
-
-namespace DataFormatProvider {
-    export type Ret<P extends DataFormatProvider> = P extends DataFormatProvider<any, any, any, infer T> ? T : never
-    export type Data<P extends DataFormatProvider> = P extends DataFormatProvider<any, infer T, any, any> ? T : never
-    export type Params<P extends DataFormatProvider> = P extends DataFormatProvider<any, any, infer T, any> ? T : never
-
-    export interface Base<Id extends string = string, P = any> {
-        id: Id,
-        display: { name: string, group?: string, description?: string },
-        extensions: { text?: string[], binary?: string[] },
-        params?(plugin: PluginContext, data: string | Uint8Array, info?: FileInfo): PD.Def<P>
-        // TODO: default representation
-        // defaultRepresentation?: RepresenatationProvider
-    }
-    export interface Definition<D extends PluginStateObject.Data.Binary | PluginStateObject.Data.String = PluginStateObject.Data.Binary | PluginStateObject.Data.String, P = any, S = any> {
-        isApplicable?(plugin: PluginContext, data: string | Uint8Array, info?: FileInfo): boolean,
-        apply(ctx: { state: State, plugin: PluginContext }, data: StateObjectCell<D>, params: P): Promise<S>
-    }
-}
-
-// interface DataSourceProvider<Id extends string = string, Format extends DataFormatProvider = DataFormatProvider, P = any, S = {}> {
-//     id: Id,
-//     display: { name: string, group?: string, description?: string },
-//     format: Format,
-//     apply(ctx: { ctx: RuntimeContext, state: State, plugin: PluginContext }, params: P): Promise<S>,
-//     params(plugin: PluginContext): PD.Def<P>,
-// }
-// function DataSourceProvider<Id extends string, Format extends DataFormatProvider, P, S>(provider: DataSourceProvider<Id, Format, P, S>) { return provider; }

+ 0 - 27
src/mol-plugin-state/manager/data/sources.ts

@@ -1,27 +0,0 @@
-/**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
-
-// import { DataSourceProvider } from './provider';
-// import { MmcifFormatProvider } from './formats';
-// import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
-// import { Download } from './actions';
-
-// TODO: basic types string, binary (both download and from file)
-// TODO: decompress functionality (string|binary --> string|binary)
-
-// export const PDBeUpdatedMmcifDataSource = DataSourceProvider({
-//     id: 'pdbe-updated-mmcif',
-//     display: { name: 'PDBe Updated mmCIF', group: 'Molecule' },
-//     format: MmcifFormatProvider,
-//     params() {
-//         return { id: PD.Text('1cbs', { label: 'PDB Id(s)', description: 'One or more comma separated PDB ids.' }) }
-//     },
-//     async apply(ctx, params) {
-//         const download = Download(ctx.state.build().toRoot(), { url: `https://www.ebi.ac.uk/pdbe/static/entry/${params.id.toLowerCase()}_updated.cif` }, ctx);
-//         await ctx.state.updateTree(download).runInContext(ctx.ctx);
-//         return 0;
-//     }
-// })

+ 13 - 13
src/mol-plugin/util/interactivity.ts → src/mol-plugin-state/manager/interactivity.ts

@@ -10,38 +10,38 @@ import { ModifiersKeys, ButtonsType } from '../../mol-util/input/input-observer'
 import { Representation } from '../../mol-repr/representation';
 import { StructureElement } from '../../mol-model/structure';
 import { MarkerAction } from '../../mol-util/marker-action';
-import { PluginContext } from '../context';
+import { PluginContext } from '../../mol-plugin/context';
 import { Structure } from '../../mol-model/structure';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
-import { PluginCommands } from '../commands';
-import { StructureSelectionManager } from '../../mol-plugin-state/manager/structure/selection';
+import { PluginCommands } from '../../mol-plugin/commands';
+import { StructureSelectionManager } from './structure/selection';
 
-export { Interactivity }
+export { InteractivityManager }
 
-class Interactivity {
-    readonly lociSelects: Interactivity.LociSelectManager;
-    readonly lociHighlights: Interactivity.LociHighlightManager;
+class InteractivityManager {
+    readonly lociSelects: InteractivityManager.LociSelectManager;
+    readonly lociHighlights: InteractivityManager.LociHighlightManager;
 
-    private _props = PD.getDefaultValues(Interactivity.Params)
+    private _props = PD.getDefaultValues(InteractivityManager.Params)
 
     get props() { return { ...this._props } }
-    setProps(props: Partial<Interactivity.Props>) {
+    setProps(props: Partial<InteractivityManager.Props>) {
         Object.assign(this._props, props)
         this.lociSelects.setProps(this._props)
         this.lociHighlights.setProps(this._props)
     }
 
-    constructor(readonly ctx: PluginContext, props: Partial<Interactivity.Props> = {}) {
+    constructor(readonly ctx: PluginContext, props: Partial<InteractivityManager.Props> = {}) {
         Object.assign(this._props, props)
 
-        this.lociSelects = new Interactivity.LociSelectManager(ctx, this._props);
-        this.lociHighlights = new Interactivity.LociHighlightManager(ctx, this._props);
+        this.lociSelects = new InteractivityManager.LociSelectManager(ctx, this._props);
+        this.lociHighlights = new InteractivityManager.LociHighlightManager(ctx, this._props);
 
         PluginCommands.Interactivity.SetProps.subscribe(ctx, e => this.setProps(e.props));
     }
 }
 
-namespace Interactivity {
+namespace InteractivityManager {
     export interface Loci<T extends ModelLoci = ModelLoci> { loci: T, repr?: Representation.Any }
 
     export namespace Loci {

+ 1 - 1
src/mol-plugin/util/loci-label-manager.ts → src/mol-plugin-state/manager/loci-label.ts

@@ -37,6 +37,6 @@ export class LociLabelManager {
     }
 
     constructor(public ctx: PluginContext) {
-        ctx.interactivity.lociHighlights.addProvider((loci, action) => ctx.behaviors.labels.highlight.next({ entries: this.getInfo(loci, action) }))
+        ctx.managers.interactivity.lociHighlights.addProvider((loci, action) => ctx.behaviors.labels.highlight.next({ entries: this.getInfo(loci, action) }))
     }
 }

+ 53 - 22
src/mol-plugin-state/manager/structure/hierarchy-state.ts

@@ -8,10 +8,12 @@ import { PluginStateObject as SO } from '../../objects';
 import { StateObject, StateTransform, State, StateObjectCell, StateTree } from '../../../mol-state';
 import { StructureBuilderTags } from '../../builder/structure';
 import { RepresentationProviderTags } from '../../builder/structure/provider';
+import { StructureRepresentationInteractionTags } from '../../../mol-plugin/behavior/dynamic/selection/structure-representation-interaction';
 
 export function buildStructureHierarchy(state: State, previous?: StructureHierarchy) {
     const build = BuildState(state, previous || StructureHierarchy());
-    StateTree.doPreOrder(state.tree, state.tree.root, build, visitCell);
+//    StateTree.doPreOrder(state.tree, state.tree.root, build, visitCell);
+    doPreOrder(state.tree, build);
     if (previous) previous.refs.forEach(isRemoved, build);
     return { hierarchy: build.hierarchy, added: build.added, updated: build.updated, removed: build.removed };
 }
@@ -147,34 +149,46 @@ function createOrUpdateRef<R extends HierarchyRef, C extends any[]>(state: Build
     return ref;
 }
 
-const tagMap: [string, (state: BuildState, cell: StateObjectCell) => boolean | void][] = [
+const tagMap: [string, (state: BuildState, cell: StateObjectCell) => boolean | void, (state: BuildState) => any][] = [
     [StructureBuilderTags.Trajectory, (state, cell) => {
         state.currentTrajectory = createOrUpdateRefList(state, cell, state.hierarchy.trajectories, TrajectoryRef, cell);
-    }],
+    }, state => state.currentTrajectory = void 0],
     [StructureBuilderTags.Model, (state, cell) => {
         if (!state.currentTrajectory) return false;
         state.currentModel = createOrUpdateRefList(state, cell, state.currentTrajectory.models, ModelRef, cell, state.currentTrajectory);
-    }],
+    }, state => state.currentModel = void 0],
     [StructureBuilderTags.ModelProperties, (state, cell) => {
         if (!state.currentModel) return false;
         state.currentModel.properties = createOrUpdateRef(state, cell, state.currentModel.properties, ModelPropertiesRef, cell, state.currentModel);
-    }],
+    }, state => { }],
     [StructureBuilderTags.Structure, (state, cell) => {
         if (!state.currentModel) return false;
         state.currentStructure = createOrUpdateRefList(state, cell, state.currentModel.structures, StructureRef, cell, state.currentModel);
-    }],
+    }, state => state.currentStructure = void 0],
     [StructureBuilderTags.StructureProperties, (state, cell) => {
         if (!state.currentStructure) return false;
         state.currentStructure.properties = createOrUpdateRef(state, cell, state.currentStructure.properties, StructurePropertiesRef, cell, state.currentStructure);
-    }],
+    }, state => { }],
     [StructureBuilderTags.Component, (state, cell) => {
         if (!state.currentStructure) return false;
         state.currentComponent = createOrUpdateRefList(state, cell, state.currentStructure.components, StructureComponentRef, cell, state.currentStructure);
-    }],
+    }, state => state.currentComponent = void 0],
     [RepresentationProviderTags.Representation, (state, cell) => {
         if (!state.currentComponent) return false;
         createOrUpdateRefList(state, cell, state.currentComponent.representations, StructureRepresentationRef, cell, state.currentComponent);
-    }]
+    }, state => { }],
+    [StructureRepresentationInteractionTags.ResidueSel, (state, cell) => {
+        if (!state.currentStructure) return false;
+        if (!state.currentStructure.currentFocus) state.currentStructure.currentFocus = { };
+        state.currentStructure.currentFocus.focus = StructureComponentRef(cell, state.currentStructure);
+        state.currentComponent = state.currentStructure.currentFocus.focus;
+    }, state => state.currentComponent = void 0],
+    [StructureRepresentationInteractionTags.SurrSel, (state, cell) => {
+        if (!state.currentStructure) return false;
+        if (!state.currentStructure.currentFocus) state.currentStructure.currentFocus = { };
+        state.currentStructure.currentFocus.surroundings = StructureComponentRef(cell, state.currentStructure);
+        state.currentComponent = state.currentStructure.currentFocus.surroundings;
+    }, state => state.currentComponent = void 0]
 ]
 
 function isValidCell(cell?: StateObjectCell): cell is StateObjectCell {
@@ -184,27 +198,44 @@ function isValidCell(cell?: StateObjectCell): cell is StateObjectCell {
     return true;
 }
 
-function visitCell(t: StateTransform, tree: StateTree, state: BuildState): boolean {
-    const cell = state.state.cells.get(t.ref);
-    if (!isValidCell(cell)) return false;
+function isRemoved(this: BuildState, ref: HierarchyRef) {
+    const { cell } = ref;
+    if (isValidCell(cell)) return;
+    this.removed.push(ref);
+}
+
+type VisitorCtx = { tree: StateTree, state: BuildState };
+
+function _preOrderFunc(this: VisitorCtx, c: StateTransform.Ref | undefined) { _doPreOrder(this, this.tree.transforms.get(c!)!); }
+function _doPreOrder(ctx: VisitorCtx, root: StateTransform) {
+    const { state } = ctx;
+    const cell = state.state.cells.get(root.ref);
+    if (!isValidCell(cell)) return;
 
-    for (const [t, f] of tagMap) {
+    let onLeave: undefined | ((state: BuildState) => any) = void 0;
+    for (const [t, f, l] of tagMap) {
         if (StateObject.hasTag(cell.obj!, t)) {
             const stop = f(state, cell);
-            if (stop === false) return false;
-            return true;
+            if (stop === false) return;
+            onLeave = l;
+            break;
         }
     }
 
-    if (state.currentComponent && SO.Molecule.Structure.Representation3D.is(cell.obj)) {
+    if (!onLeave && state.currentComponent && SO.Molecule.Structure.Representation3D.is(cell.obj)) {
         createOrUpdateRefList(state, cell, state.currentComponent.representations, StructureRepresentationRef, cell, state.currentComponent);
     }
 
-    return true;
-}
+    const children = ctx.tree.children.get(root.ref);
+    if (children && children.size) {
+        children.forEach(_preOrderFunc, ctx);
+    }
 
-function isRemoved(this: BuildState, ref: HierarchyRef) {
-    const { cell } = ref;
-    if (isValidCell(cell)) return;
-    this.removed.push(ref);
+    if (onLeave) onLeave(state);
 }
+
+function doPreOrder(tree: StateTree, state: BuildState): BuildState {
+    const ctx: VisitorCtx = { tree, state };
+    _doPreOrder(ctx, tree.root);
+    return ctx.state;
+}

+ 4 - 4
src/mol-plugin-state/manager/structure/selection.ts

@@ -18,7 +18,7 @@ import { arrayRemoveAtInPlace } from '../../../mol-util/array';
 import { EmptyLoci, Loci } from '../../../mol-model/loci';
 import { StateObject, StateSelection } from '../../../mol-state';
 import { PluginStateObject } from '../../objects';
-import { StructureSelectionQuery } from '../../../mol-plugin/util/structure-selection-query';
+import { StructureSelectionQuery } from '../../helpers/structure-selection-query';
 import { Task } from '../../../mol-task';
 
 interface StructureSelectionManagerState {
@@ -348,13 +348,13 @@ export class StructureSelectionManager extends PluginComponent<StructureSelectio
     private triggerInteraction(modifier: StructureSelectionModifier, loci: Loci, applyGranularity = true) {
         switch (modifier) {
             case 'add':
-                this.plugin.interactivity.lociSelects.select({ loci }, applyGranularity)
+                this.plugin.managers.interactivity.lociSelects.select({ loci }, applyGranularity)
                 break
             case 'remove':
-                this.plugin.interactivity.lociSelects.deselect({ loci }, applyGranularity)
+                this.plugin.managers.interactivity.lociSelects.deselect({ loci }, applyGranularity)
                 break
             case 'set':
-                this.plugin.interactivity.lociSelects.selectOnly({ loci }, applyGranularity)
+                this.plugin.managers.interactivity.lociSelects.selectOnly({ loci }, applyGranularity)
                 break
         }
     }

+ 1 - 1
src/mol-plugin-state/transforms/model.ts

@@ -24,7 +24,7 @@ import { SymmetryOperator } from '../../mol-math/geometry';
 import { Script } from '../../mol-script/script';
 import { parse3DG } from '../../mol-io/reader/3dg/parser';
 import { trajectoryFrom3DG } from '../../mol-model-formats/structure/3dg';
-import { StructureSelectionQueries } from '../../mol-plugin/util/structure-selection-query';
+import { StructureSelectionQueries } from '../helpers/structure-selection-query';
 import { StructureQueryHelper } from '../helpers/structure-query';
 import { RootStructureDefinition } from '../helpers/root-structure';
 import { parseDcd } from '../../mol-io/reader/dcd/parser';

+ 1 - 1
src/mol-plugin-state/transforms/representation.ts

@@ -29,7 +29,7 @@ import { Overpaint } from '../../mol-theme/overpaint';
 import { Transparency } from '../../mol-theme/transparency';
 import { BaseGeometry } from '../../mol-geo/geometry/base';
 import { Script } from '../../mol-script/script';
-import { getUnitcellRepresentation, UnitcellParams } from '../../mol-plugin/util/model-unitcell';
+import { getUnitcellRepresentation, UnitcellParams } from '../helpers/model-unitcell';
 import { DistanceParams, DistanceRepresentation } from '../../mol-repr/shape/loci/distance';
 import { getDistanceDataFromStructureSelections, getLabelDataFromStructureSelections, getOrientationDataFromStructureSelections, getAngleDataFromStructureSelections, getDihedralDataFromStructureSelections } from './helpers';
 import { LabelParams, LabelRepresentation } from '../../mol-repr/shape/loci/label';

+ 1 - 1
src/mol-plugin-ui/controls.tsx

@@ -9,7 +9,7 @@ import * as React from 'react';
 import { PluginCommands } from '../mol-plugin/commands';
 import { UpdateTrajectory } from '../mol-plugin-state/actions/structure';
 import { PluginUIComponent } from './base';
-import { LociLabelEntry } from '../mol-plugin/util/loci-label-manager';
+import { LociLabelEntry } from '../mol-plugin-state/manager/loci-label';
 import { IconButton } from './controls/common';
 import { PluginStateObject } from '../mol-plugin-state/objects';
 import { StateTransforms } from '../mol-plugin-state/transforms';

+ 9 - 9
src/mol-plugin-ui/sequence/sequence.tsx

@@ -7,7 +7,7 @@
 
 import * as React from 'react'
 import { PluginUIComponent } from '../base';
-import { Interactivity } from '../../mol-plugin/util/interactivity';
+import { InteractivityManager } from '../../mol-plugin-state/manager/interactivity';
 import { MarkerAction } from '../../mol-util/marker-action';
 import { ButtonsType, ModifiersKeys, getButtons, getModifiers, getButton } from '../../mol-util/input/input-observer';
 import { SequenceWrapper } from './wrapper';
@@ -32,12 +32,12 @@ export class Sequence<P extends SequenceProps> extends PluginUIComponent<P> {
     private lastMouseOverSeqIdx = -1;
     private highlightQueue = new Subject<{ seqIdx: number, buttons: number, button: number, modifiers: ModifiersKeys }>();
 
-    private lociHighlightProvider = (loci: Interactivity.Loci, action: MarkerAction) => {
+    private lociHighlightProvider = (loci: InteractivityManager.Loci, action: MarkerAction) => {
         const changed = this.props.sequenceWrapper.markResidue(loci.loci, action)
         if (changed) this.updateMarker();
     }
 
-    private lociSelectionProvider = (loci: Interactivity.Loci, action: MarkerAction) => {
+    private lociSelectionProvider = (loci: InteractivityManager.Loci, action: MarkerAction) => {
         const changed = this.props.sequenceWrapper.markResidue(loci.loci, action)
         if (changed) this.updateMarker();
     }
@@ -53,8 +53,8 @@ export class Sequence<P extends SequenceProps> extends PluginUIComponent<P> {
     }
 
     componentDidMount() {
-        this.plugin.interactivity.lociHighlights.addProvider(this.lociHighlightProvider)
-        this.plugin.interactivity.lociSelects.addProvider(this.lociSelectionProvider)
+        this.plugin.managers.interactivity.lociHighlights.addProvider(this.lociHighlightProvider)
+        this.plugin.managers.interactivity.lociSelects.addProvider(this.lociSelectionProvider)
 
         this.subscribe(debounceTime<{ seqIdx: number, buttons: number, button: number, modifiers: ModifiersKeys }>(15)(this.highlightQueue), (e) => {
             const loci = this.getLoci(e.seqIdx < 0 ? void 0 : e.seqIdx)
@@ -65,8 +65,8 @@ export class Sequence<P extends SequenceProps> extends PluginUIComponent<P> {
     }
 
     componentWillUnmount() {
-        this.plugin.interactivity.lociHighlights.removeProvider(this.lociHighlightProvider)
-        this.plugin.interactivity.lociSelects.removeProvider(this.lociSelectionProvider)
+        this.plugin.managers.interactivity.lociHighlights.removeProvider(this.lociHighlightProvider)
+        this.plugin.managers.interactivity.lociSelects.removeProvider(this.lociSelectionProvider)
     }
 
     getLoci(seqIdx: number | undefined) {
@@ -86,7 +86,7 @@ export class Sequence<P extends SequenceProps> extends PluginUIComponent<P> {
     }
 
     hover(loci: StructureElement.Loci | undefined, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys) {
-        const ev = { current: Interactivity.Loci.Empty, buttons, button, modifiers }
+        const ev = { current: InteractivityManager.Loci.Empty, buttons, button, modifiers }
         if (loci !== undefined && !StructureElement.Loci.isEmpty(loci)) {
             ev.current = { loci };
         }
@@ -94,7 +94,7 @@ export class Sequence<P extends SequenceProps> extends PluginUIComponent<P> {
     }
 
     click(loci: StructureElement.Loci | undefined, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys) {
-        const ev = { current: Interactivity.Loci.Empty, buttons, button, modifiers }
+        const ev = { current: InteractivityManager.Loci.Empty, buttons, button, modifiers }
         if (loci !== undefined && !StructureElement.Loci.isEmpty(loci)) {
             ev.current = { loci };
         }

+ 4 - 4
src/mol-plugin-ui/structure/measurements.tsx

@@ -208,15 +208,15 @@ class MeasurementEntry extends PurePluginUIComponent<{ cell: StructureMeasuremen
         const selections = this.selections;
         if (!selections) return;
 
-        this.plugin.interactivity.lociHighlights.clearHighlights();
+        this.plugin.managers.interactivity.lociHighlights.clearHighlights();
         for (const d of selections.data) {
-            this.plugin.interactivity.lociHighlights.highlight({ loci: d.loci }, false);
+            this.plugin.managers.interactivity.lociHighlights.highlight({ loci: d.loci }, false);
         }
-        this.plugin.interactivity.lociHighlights.highlight({ loci: this.props.cell.obj?.data.repr.getLoci()! }, false);
+        this.plugin.managers.interactivity.lociHighlights.highlight({ loci: this.props.cell.obj?.data.repr.getLoci()! }, false);
     }
 
     clearHighlight = () => {
-        this.plugin.interactivity.lociHighlights.clearHighlights();
+        this.plugin.managers.interactivity.lociHighlights.clearHighlights();
     }
 
     focus = () => {

+ 4 - 4
src/mol-plugin-ui/structure/selection.tsx

@@ -7,10 +7,10 @@
 
 import * as React from 'react';
 import { CollapsableControls, CollapsableState } from '../base';
-import { StructureSelectionQuery, StructureSelectionQueryList } from '../../mol-plugin/util/structure-selection-query';
+import { StructureSelectionQuery, StructureSelectionQueryList } from '../../mol-plugin-state/helpers/structure-selection-query';
 import { PluginCommands } from '../../mol-plugin/commands';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
-import { Interactivity } from '../../mol-plugin/util/interactivity';
+import { InteractivityManager } from '../../mol-plugin-state/manager/interactivity';
 import { ParameterControls } from '../controls/parameters';
 import { stripTags } from '../../mol-util/string';
 import { StructureElement } from '../../mol-model/structure';
@@ -25,7 +25,7 @@ export const DefaultQueries = ActionMenu.createItems(StructureSelectionQueryList
 });
 
 const StructureSelectionParams = {
-    granularity: Interactivity.Params.granularity,
+    granularity: InteractivityManager.Params.granularity,
 }
 
 interface StructureSelectionControlsState extends CollapsableState {
@@ -90,7 +90,7 @@ export class StructureSelectionControls<P, S extends StructureSelectionControlsS
 
     get values () {
         return {
-            granularity: this.plugin.interactivity.props.granularity,
+            granularity: this.plugin.managers.interactivity.props.granularity,
         }
     }
 

+ 2 - 2
src/mol-plugin-ui/toast.tsx

@@ -40,11 +40,11 @@ class ToastEntry extends PluginUIComponent<{ entry: PluginToastManager.Entry }>
 
 export class Toasts extends PluginUIComponent {
     componentDidMount() {
-        this.subscribe(this.plugin.toasts.events.changed, () => this.forceUpdate());
+        this.subscribe(this.plugin.managers.toast.events.changed, () => this.forceUpdate());
     }
 
     render() {
-        const state = this.plugin.toasts.state;
+        const state = this.plugin.managers.toast.state;
 
         if (!state.entries.count()) return null;
 

+ 1 - 1
src/mol-plugin/behavior/dynamic/camera.ts

@@ -40,7 +40,7 @@ export const FocusLoci = PluginBehavior.create<FocusLociProps>({
                 if (!this.ctx.canvas3d) return;
                 const p = this.params;
                 if (Binding.match(this.params.bindings.clickCenterFocus, button, modifiers)) {
-                    const loci = Loci.normalize(current.loci, this.ctx.interactivity.props.granularity)
+                    const loci = Loci.normalize(current.loci, this.ctx.managers.interactivity.props.granularity)
                     if (Loci.isEmpty(loci)) {
                         PluginCommands.Camera.Reset(this.ctx, { })
                     } else {

+ 2 - 2
src/mol-plugin/behavior/dynamic/custom-props/computed/accessible-surface-area.ts

@@ -41,7 +41,7 @@ export const AccessibleSurfaceArea = PluginBehavior.create<{ autoAttach: boolean
 
             this.ctx.customStructureProperties.register(this.provider, this.params.autoAttach);
             this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.add('accessible-surface-area', AccessibleSurfaceAreaColorThemeProvider)
-            this.ctx.lociLabels.addProvider(this.label);
+            this.ctx.managers.lociLabels.addProvider(this.label);
         }
 
         unregister() {
@@ -50,7 +50,7 @@ export const AccessibleSurfaceArea = PluginBehavior.create<{ autoAttach: boolean
 
             this.ctx.customStructureProperties.unregister(this.provider.descriptor.name);
             this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.remove('accessible-surface-area')
-            this.ctx.lociLabels.removeProvider(this.label);
+            this.ctx.managers.lociLabels.removeProvider(this.label);
         }
     },
     params: () => ({

+ 2 - 2
src/mol-plugin/behavior/dynamic/custom-props/computed/interactions.ts

@@ -100,14 +100,14 @@ export const Interactions = PluginBehavior.create<{ autoAttach: boolean, showToo
         register(): void {
             this.ctx.customStructureProperties.register(this.provider, this.params.autoAttach);
             this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.add('interaction-type', InteractionTypeColorThemeProvider)
-            this.ctx.lociLabels.addProvider(this.label);
+            this.ctx.managers.lociLabels.addProvider(this.label);
             this.ctx.structureRepresentation.registry.add('interactions', InteractionsRepresentationProvider)
         }
 
         unregister() {
             this.ctx.customStructureProperties.unregister(this.provider.descriptor.name);
             this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.remove('interaction-type')
-            this.ctx.lociLabels.removeProvider(this.label);
+            this.ctx.managers.lociLabels.removeProvider(this.label);
             this.ctx.structureRepresentation.registry.remove('interactions')
         }
     },

+ 2 - 2
src/mol-plugin/behavior/dynamic/custom-props/computed/valence-model.ts

@@ -86,12 +86,12 @@ export const ValenceModel = PluginBehavior.create<{ autoAttach: boolean, showToo
 
         register(): void {
             this.ctx.customStructureProperties.register(this.provider, this.params.autoAttach);
-            this.ctx.lociLabels.addProvider(this.label);
+            this.ctx.managers.lociLabels.addProvider(this.label);
         }
 
         unregister() {
             this.ctx.customStructureProperties.unregister(this.provider.descriptor.name);
-            this.ctx.lociLabels.removeProvider(this.label);
+            this.ctx.managers.lociLabels.removeProvider(this.label);
         }
     },
     params: () => ({

+ 2 - 2
src/mol-plugin/behavior/dynamic/custom-props/pdbe/structure-quality-report.ts

@@ -44,7 +44,7 @@ export const PDBeStructureQualityReport = PluginBehavior.create<{ autoAttach: bo
 
         register(): void {
             this.ctx.customModelProperties.register(this.provider, false);
-            this.ctx.lociLabels.addProvider(this.labelPDBeValidation);
+            this.ctx.managers.lociLabels.addProvider(this.labelPDBeValidation);
 
             this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.add('pdbe-structure-quality-report', StructureQualityReportColorThemeProvider)
         }
@@ -59,7 +59,7 @@ export const PDBeStructureQualityReport = PluginBehavior.create<{ autoAttach: bo
 
         unregister() {
             this.ctx.customModelProperties.unregister(StructureQualityReportProvider.descriptor.name);
-            this.ctx.lociLabels.removeProvider(this.labelPDBeValidation);
+            this.ctx.managers.lociLabels.removeProvider(this.labelPDBeValidation);
             this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.remove('pdbe-structure-quality-report')
         }
     },

+ 2 - 2
src/mol-plugin/behavior/dynamic/custom-props/rcsb/validation-report.ts

@@ -42,7 +42,7 @@ export const RCSBValidationReport = PluginBehavior.create<{ autoAttach: boolean,
 
             this.ctx.customModelProperties.register(this.provider, this.params.autoAttach);
 
-            this.ctx.lociLabels.addProvider(this.label);
+            this.ctx.managers.lociLabels.addProvider(this.label);
 
             this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.add(Tag.DensityFit, DensityFitColorThemeProvider)
             this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.add(Tag.GeometryQuality, GeometryQualityColorThemeProvider)
@@ -65,7 +65,7 @@ export const RCSBValidationReport = PluginBehavior.create<{ autoAttach: boolean,
 
             this.ctx.customStructureProperties.unregister(this.provider.descriptor.name);
 
-            this.ctx.lociLabels.removeProvider(this.label);
+            this.ctx.managers.lociLabels.removeProvider(this.label);
 
             this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.remove(Tag.DensityFit)
             this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.remove(Tag.GeometryQuality)

+ 22 - 22
src/mol-plugin/behavior/dynamic/representation.ts

@@ -10,7 +10,7 @@ import { PluginContext } from '../../../mol-plugin/context';
 import { PluginStateObject as SO } from '../../../mol-plugin-state/objects';
 import { lociLabel } from '../../../mol-theme/label';
 import { PluginBehavior } from '../behavior';
-import { Interactivity } from '../../util/interactivity';
+import { InteractivityManager } from '../../../mol-plugin-state/manager/interactivity';
 import { StateTreeSpine } from '../../../mol-state/tree/spine';
 import { StateSelection } from '../../../mol-state';
 import { ButtonsType, ModifiersKeys } from '../../../mol-util/input/input-observer';
@@ -39,7 +39,7 @@ export const HighlightLoci = PluginBehavior.create({
     name: 'representation-highlight-loci',
     category: 'interaction',
     ctor: class extends PluginBehavior.Handler<HighlightLociProps> {
-        private lociMarkProvider = (interactionLoci: Interactivity.Loci, action: MarkerAction) => {
+        private lociMarkProvider = (interactionLoci: InteractivityManager.Loci, action: MarkerAction) => {
             if (!this.ctx.canvas3d) return;
             this.ctx.canvas3d.mark({ loci: interactionLoci.loci }, action)
         }
@@ -49,23 +49,23 @@ export const HighlightLoci = PluginBehavior.create({
                 let matched = false
 
                 if (Binding.match(this.params.bindings.hoverHighlightOnly, buttons, modifiers)) {
-                    this.ctx.interactivity.lociHighlights.highlightOnly(current)
+                    this.ctx.managers.interactivity.lociHighlights.highlightOnly(current)
                     matched = true
                 }
 
                 if (Binding.match(this.params.bindings.hoverHighlightOnlyExtend, buttons, modifiers)) {
-                    this.ctx.interactivity.lociHighlights.highlightOnlyExtend(current)
+                    this.ctx.managers.interactivity.lociHighlights.highlightOnlyExtend(current)
                     matched = true
                 }
 
                 if (!matched) {
-                    this.ctx.interactivity.lociHighlights.highlightOnly({ repr: current.repr, loci: EmptyLoci })
+                    this.ctx.managers.interactivity.lociHighlights.highlightOnly({ repr: current.repr, loci: EmptyLoci })
                 }
             });
-            this.ctx.interactivity.lociHighlights.addProvider(this.lociMarkProvider)
+            this.ctx.managers.interactivity.lociHighlights.addProvider(this.lociMarkProvider)
         }
         unregister() {
-            this.ctx.interactivity.lociHighlights.removeProvider(this.lociMarkProvider)
+            this.ctx.managers.interactivity.lociHighlights.removeProvider(this.lociMarkProvider)
         }
     },
     params: () => HighlightLociParams,
@@ -92,7 +92,7 @@ export const SelectLoci = PluginBehavior.create({
     category: 'interaction',
     ctor: class extends PluginBehavior.Handler<SelectLociProps> {
         private spine: StateTreeSpine.Impl
-        private lociMarkProvider = (interactionLoci: Interactivity.Loci, action: MarkerAction) => {
+        private lociMarkProvider = (interactionLoci: InteractivityManager.Loci, action: MarkerAction) => {
             if (!this.ctx.canvas3d) return;
             this.ctx.canvas3d.mark({ loci: interactionLoci.loci }, action)
         }
@@ -111,16 +111,16 @@ export const SelectLoci = PluginBehavior.create({
             }
         }
         register() {
-            const lociIsEmpty = (current: Interactivity.Loci) => Loci.isEmpty(current.loci)
-            const lociIsNotEmpty = (current: Interactivity.Loci) => !Loci.isEmpty(current.loci)
-
-            const actions: [keyof typeof DefaultSelectLociBindings, (current: Interactivity.Loci) => void, ((current: Interactivity.Loci) => boolean) | undefined][] = [
-                ['clickSelect', current => this.ctx.interactivity.lociSelects.select(current), lociIsNotEmpty],
-                ['clickToggle', current => this.ctx.interactivity.lociSelects.toggle(current), lociIsNotEmpty],
-                ['clickToggleExtend', current => this.ctx.interactivity.lociSelects.toggleExtend(current), lociIsNotEmpty],
-                ['clickSelectOnly', current => this.ctx.interactivity.lociSelects.selectOnly(current), lociIsNotEmpty],
-                ['clickDeselect', current => this.ctx.interactivity.lociSelects.deselect(current), lociIsNotEmpty],
-                ['clickDeselectAllOnEmpty', () => this.ctx.interactivity.lociSelects.deselectAll(), lociIsEmpty],
+            const lociIsEmpty = (current: InteractivityManager.Loci) => Loci.isEmpty(current.loci)
+            const lociIsNotEmpty = (current: InteractivityManager.Loci) => !Loci.isEmpty(current.loci)
+
+            const actions: [keyof typeof DefaultSelectLociBindings, (current: InteractivityManager.Loci) => void, ((current: InteractivityManager.Loci) => boolean) | undefined][] = [
+                ['clickSelect', current => this.ctx.managers.interactivity.lociSelects.select(current), lociIsNotEmpty],
+                ['clickToggle', current => this.ctx.managers.interactivity.lociSelects.toggle(current), lociIsNotEmpty],
+                ['clickToggleExtend', current => this.ctx.managers.interactivity.lociSelects.toggleExtend(current), lociIsNotEmpty],
+                ['clickSelectOnly', current => this.ctx.managers.interactivity.lociSelects.selectOnly(current), lociIsNotEmpty],
+                ['clickDeselect', current => this.ctx.managers.interactivity.lociSelects.deselect(current), lociIsNotEmpty],
+                ['clickDeselectAllOnEmpty', () => this.ctx.managers.interactivity.lociSelects.deselectAll(), lociIsEmpty],
             ];
 
             // sort the action so that the ones with more modifiers trigger sooner.
@@ -142,7 +142,7 @@ export const SelectLoci = PluginBehavior.create({
                     }
                 }
             });
-            this.ctx.interactivity.lociSelects.addProvider(this.lociMarkProvider)
+            this.ctx.managers.interactivity.lociSelects.addProvider(this.lociMarkProvider)
 
             this.subscribeObservable(this.ctx.events.state.object.created, ({ ref }) => this.applySelectMark(ref));
 
@@ -156,7 +156,7 @@ export const SelectLoci = PluginBehavior.create({
             });
         }
         unregister() {
-            this.ctx.interactivity.lociSelects.removeProvider(this.lociMarkProvider)
+            this.ctx.managers.interactivity.lociSelects.removeProvider(this.lociMarkProvider)
         }
         constructor(ctx: PluginContext, params: SelectLociProps) {
             super(ctx, params)
@@ -172,8 +172,8 @@ export const DefaultLociLabelProvider = PluginBehavior.create({
     category: 'interaction',
     ctor: class implements PluginBehavior<undefined> {
         private f = (loci: Loci) => lociLabel(loci);
-        register() { this.ctx.lociLabels.addProvider(this.f); }
-        unregister() { this.ctx.lociLabels.removeProvider(this.f); }
+        register() { this.ctx.managers.lociLabels.addProvider(this.f); }
+        unregister() { this.ctx.managers.lociLabels.removeProvider(this.f); }
         constructor(protected ctx: PluginContext) { }
     },
     display: { name: 'Provide Default Loci Label' }

+ 23 - 23
src/mol-plugin/behavior/dynamic/selection/structure-representation-interaction.ts

@@ -35,7 +35,7 @@ const StructureRepresentationInteractionParams = {
 }
 type StructureRepresentationInteractionProps = PD.Values<typeof StructureRepresentationInteractionParams>
 
-enum Tags {
+export enum StructureRepresentationInteractionTags {
     Group = 'structure-interaction-group',
     ResidueSel = 'structure-interaction-residue-sel',
     ResidueRepr = 'structure-interaction-residue-repr',
@@ -44,7 +44,7 @@ enum Tags {
     SurrNciRepr = 'structure-interaction-surr-nci-repr'
 }
 
-const TagSet: Set<Tags> = new Set([Tags.Group, Tags.ResidueSel, Tags.ResidueRepr, Tags.SurrSel, Tags.SurrRepr, Tags.SurrNciRepr])
+const TagSet: Set<StructureRepresentationInteractionTags> = new Set([StructureRepresentationInteractionTags.Group, StructureRepresentationInteractionTags.ResidueSel, StructureRepresentationInteractionTags.ResidueRepr, StructureRepresentationInteractionTags.SurrSel, StructureRepresentationInteractionTags.SurrRepr, StructureRepresentationInteractionTags.SurrNciRepr])
 
 export class StructureRepresentationInteractionBehavior extends PluginBehavior.WithSubscribers<StructureRepresentationInteractionProps> {
 
@@ -78,35 +78,35 @@ export class StructureRepresentationInteractionBehavior extends PluginBehavior.W
 
         if (!refs['structure-interaction-group']) {
             refs['structure-interaction-group'] = builder.to(cell).group(StateTransforms.Misc.CreateGroup,
-                { label: 'Current Focus' }, { tags: Tags.Group }).ref;
+                { label: 'Current Focus' }, { tags: StructureRepresentationInteractionTags.Group }).ref;
         }
 
         // Selections
-        if (!refs[Tags.ResidueSel]) {
-            refs[Tags.ResidueSel] = builder.to(refs['structure-interaction-group']).apply(StateTransforms.Model.StructureSelectionFromBundle,
-                { bundle: { } as any, label: 'Residue' }, { tags: Tags.ResidueSel }).ref;
+        if (!refs[StructureRepresentationInteractionTags.ResidueSel]) {
+            refs[StructureRepresentationInteractionTags.ResidueSel] = builder.to(refs['structure-interaction-group']).apply(StateTransforms.Model.StructureSelectionFromBundle,
+                { bundle: { } as any, label: 'Residue' }, { tags: StructureRepresentationInteractionTags.ResidueSel }).ref;
         }
 
-        if (!refs[Tags.SurrSel]) {
-            refs[Tags.SurrSel] = builder.to(refs['structure-interaction-group']).apply(StateTransforms.Model.StructureSelectionFromExpression,
-                { expression: { } as any, label: 'Surroundings' }, { tags: Tags.SurrSel }).ref;
+        if (!refs[StructureRepresentationInteractionTags.SurrSel]) {
+            refs[StructureRepresentationInteractionTags.SurrSel] = builder.to(refs['structure-interaction-group']).apply(StateTransforms.Model.StructureSelectionFromExpression,
+                { expression: { } as any, label: 'Surroundings' }, { tags: StructureRepresentationInteractionTags.SurrSel }).ref;
         }
 
         // Representations
         // TODO: ability to customize how it looks in the behavior params
-        if (!refs[Tags.ResidueRepr]) {
-            refs[Tags.ResidueRepr] = builder.to(refs['structure-interaction-residue-sel']!).apply(StateTransforms.Representation.StructureRepresentation3D,
-                this.createResVisualParams(cell.obj!.data), { tags: Tags.ResidueRepr }).ref;
+        if (!refs[StructureRepresentationInteractionTags.ResidueRepr]) {
+            refs[StructureRepresentationInteractionTags.ResidueRepr] = builder.to(refs['structure-interaction-residue-sel']!).apply(StateTransforms.Representation.StructureRepresentation3D,
+                this.createResVisualParams(cell.obj!.data), { tags: StructureRepresentationInteractionTags.ResidueRepr }).ref;
         }
 
-        if (!refs[Tags.SurrRepr]) {
-            refs[Tags.SurrRepr] = builder.to(refs['structure-interaction-surr-sel']!).apply(StateTransforms.Representation.StructureRepresentation3D,
-                this.createSurVisualParams(cell.obj!.data), { tags: Tags.SurrRepr }).ref;
+        if (!refs[StructureRepresentationInteractionTags.SurrRepr]) {
+            refs[StructureRepresentationInteractionTags.SurrRepr] = builder.to(refs['structure-interaction-surr-sel']!).apply(StateTransforms.Representation.StructureRepresentation3D,
+                this.createSurVisualParams(cell.obj!.data), { tags: StructureRepresentationInteractionTags.SurrRepr }).ref;
         }
 
-        if (!refs[Tags.SurrNciRepr]) {
-            refs[Tags.SurrNciRepr] = builder.to(refs['structure-interaction-surr-sel']!).apply(StateTransforms.Representation.StructureRepresentation3D,
-                this.createSurNciVisualParams(cell.obj!.data), { tags: Tags.SurrNciRepr }).ref;
+        if (!refs[StructureRepresentationInteractionTags.SurrNciRepr]) {
+            refs[StructureRepresentationInteractionTags.SurrNciRepr] = builder.to(refs['structure-interaction-surr-sel']!).apply(StateTransforms.Representation.StructureRepresentation3D,
+                this.createSurNciVisualParams(cell.obj!.data), { tags: StructureRepresentationInteractionTags.SurrNciRepr }).ref;
         }
 
         return { state, builder, refs };
@@ -114,7 +114,7 @@ export class StructureRepresentationInteractionBehavior extends PluginBehavior.W
 
     private clear(root: StateTransform.Ref) {
         const state = this.plugin.state.dataState;
-        const groups = state.select(StateSelection.Generators.byRef(root).subtree().withTag(Tags.Group));
+        const groups = state.select(StateSelection.Generators.byRef(root).subtree().withTag(StructureRepresentationInteractionTags.Group));
         if (groups.length === 0) return;
 
         const update = state.build();
@@ -123,8 +123,8 @@ export class StructureRepresentationInteractionBehavior extends PluginBehavior.W
         for (const g of groups) {
             // TODO: update props of the group node to ghost
 
-            const res = StateSelection.findTagInSubtree(state.tree, g.transform.ref, Tags.ResidueSel);
-            const surr = StateSelection.findTagInSubtree(state.tree, g.transform.ref, Tags.SurrSel);
+            const res = StateSelection.findTagInSubtree(state.tree, g.transform.ref, StructureRepresentationInteractionTags.ResidueSel);
+            const surr = StateSelection.findTagInSubtree(state.tree, g.transform.ref, StructureRepresentationInteractionTags.SurrSel);
             if (res) update.to(res).update(StateTransforms.Model.StructureSelectionFromBundle, old => ({ ...old, bundle }));
             if (surr) update.to(surr).update(StateTransforms.Model.StructureSelectionFromExpression, old => ({ ...old, expression }));
         }
@@ -194,8 +194,8 @@ export class StructureRepresentationInteractionBehavior extends PluginBehavior.W
 
                 const { state, builder, refs } = this.ensureShape(parent);
 
-                builder.to(refs[Tags.ResidueSel]!).update(StateTransforms.Model.StructureSelectionFromBundle, old => ({ ...old, bundle: residueBundle }));
-                builder.to(refs[Tags.SurrSel]!).update(StateTransforms.Model.StructureSelectionFromExpression, old => ({ ...old, expression: surroundings }));
+                builder.to(refs[StructureRepresentationInteractionTags.ResidueSel]!).update(StateTransforms.Model.StructureSelectionFromBundle, old => ({ ...old, bundle: residueBundle }));
+                builder.to(refs[StructureRepresentationInteractionTags.SurrSel]!).update(StateTransforms.Model.StructureSelectionFromExpression, old => ({ ...old, expression: surroundings }));
 
                 PluginCommands.State.Update(this.plugin, { state, tree: builder, options: { doNotLogTiming: true, doNotUpdateCurrent: true } });
             }

+ 1 - 1
src/mol-plugin/behavior/static/misc.ts

@@ -22,7 +22,7 @@ export function Canvas3DSetSettings(ctx: PluginContext) {
 
 export function InteractivitySetProps(ctx: PluginContext) {
     PluginCommands.Interactivity.SetProps.subscribe(ctx, e => {
-        ctx.interactivity.setProps(e.props);
+        ctx.managers.interactivity.setProps(e.props);
         ctx.events.interactivity.propsUpdated.next();
     })
 }

+ 5 - 5
src/mol-plugin/behavior/static/state.ts

@@ -108,14 +108,14 @@ export function Highlight(ctx: PluginContext) {
         const cell = state.select(ref)[0];
         if (!cell) return;
         if (SO.Molecule.Structure.is(cell.obj)) {
-            ctx.interactivity.lociHighlights.highlightOnly({ loci: Structure.Loci(cell.obj.data) }, false);
+            ctx.managers.interactivity.lociHighlights.highlightOnly({ loci: Structure.Loci(cell.obj.data) }, false);
         } else if (cell && SO.isRepresentation3D(cell.obj)) {
             const { repr } = cell.obj.data
-            ctx.interactivity.lociHighlights.highlightOnly({ loci: repr.getLoci(), repr }, false);
+            ctx.managers.interactivity.lociHighlights.highlightOnly({ loci: repr.getLoci(), repr }, false);
         } else if (SO.Molecule.Structure.Selections.is(cell.obj)) {
-            ctx.interactivity.lociHighlights.clearHighlights();
+            ctx.managers.interactivity.lociHighlights.clearHighlights();
             for (const entry of cell.obj.data) {
-                ctx.interactivity.lociHighlights.highlight({ loci: entry.loci }, false);
+                ctx.managers.interactivity.lociHighlights.highlight({ loci: entry.loci }, false);
             }
         }
 
@@ -126,7 +126,7 @@ export function Highlight(ctx: PluginContext) {
 
 export function ClearHighlight(ctx: PluginContext) {
     PluginCommands.State.ClearHighlight.subscribe(ctx, ({ state, ref }) => {
-        ctx.interactivity.lociHighlights.clearHighlights();
+        ctx.managers.interactivity.lociHighlights.clearHighlights();
     });
 }
 

+ 2 - 2
src/mol-plugin/commands.ts

@@ -11,7 +11,7 @@ import { Canvas3DProps } from '../mol-canvas3d/canvas3d';
 import { PluginLayoutStateProps } from './layout';
 import { StructureElement } from '../mol-model/structure';
 import { PluginState } from './state';
-import { Interactivity } from './util/interactivity';
+import { InteractivityManager } from '../mol-plugin-state/manager/interactivity';
 import { PluginToast } from './util/toast';
 import { Vec3 } from '../mol-math/linear-algebra';
 
@@ -44,7 +44,7 @@ export const PluginCommands = {
         }
     },
     Interactivity: {
-        SetProps: PluginCommand<{ props: Partial<Interactivity.Props> }>(),
+        SetProps: PluginCommand<{ props: Partial<InteractivityManager.Props> }>(),
         Structure: {
             Highlight: PluginCommand<{ loci: StructureElement.Loci, isOff?: boolean }>(),
             Select: PluginCommand<{ loci: StructureElement.Loci, isOff?: boolean }>()

+ 13 - 13
src/mol-plugin/context.ts

@@ -28,14 +28,14 @@ import { PluginSpec } from './spec';
 import { PluginState } from './state';
 import { DataFormatRegistry } from '../mol-plugin-state/actions/data-format';
 import { StateTransformParameters } from '../mol-plugin-ui/state/common';
-import { LociLabelEntry, LociLabelManager } from './util/loci-label-manager';
+import { LociLabelEntry, LociLabelManager } from '../mol-plugin-state/manager/loci-label';
 import { TaskManager } from './util/task-manager';
 import { PLUGIN_VERSION, PLUGIN_VERSION_DATE } from './version';
 import { SubstructureParentHelper } from './util/substructure-parent-helper';
 import { ModifiersKeys } from '../mol-util/input/input-observer';
 import { isProductionMode, isDebugMode } from '../mol-util/debug';
 import { Model, Structure } from '../mol-model/structure';
-import { Interactivity } from './util/interactivity';
+import { InteractivityManager } from '../mol-plugin-state/manager/interactivity';
 import { StructureRepresentationHelper } from './util/structure-representation-helper';
 import { StructureOverpaintHelper } from './util/structure-overpaint-helper';
 import { PluginToastManager } from './util/toast';
@@ -48,6 +48,7 @@ import { DataBuilder } from '../mol-plugin-state/builder/data';
 import { StructureBuilder } from '../mol-plugin-state/builder/structure';
 import { StructureHierarchyManager } from '../mol-plugin-state/manager/structure/hierarchy';
 import { StructureSelectionManager } from '../mol-plugin-state/manager/structure/selection';
+import { TrajectoryFormatRegistry } from '../mol-plugin-state/formats/trajectory';
 
 export class PluginContext {
     private disposed = false;
@@ -92,8 +93,8 @@ export class PluginContext {
             isBusy: this.ev.behavior<boolean>(false)
         },
         interaction: {
-            hover: this.ev.behavior<Interactivity.HoverEvent>({ current: Interactivity.Loci.Empty, modifiers: ModifiersKeys.None, buttons: 0, button: 0 }),
-            click: this.ev.behavior<Interactivity.ClickEvent>({ current: Interactivity.Loci.Empty, modifiers: ModifiersKeys.None, buttons: 0, button: 0 })
+            hover: this.ev.behavior<InteractivityManager.HoverEvent>({ current: InteractivityManager.Loci.Empty, modifiers: ModifiersKeys.None, buttons: 0, button: 0 }),
+            click: this.ev.behavior<InteractivityManager.ClickEvent>({ current: InteractivityManager.Loci.Empty, modifiers: ModifiersKeys.None, buttons: 0, button: 0 })
         },
         labels: {
             highlight: this.ev.behavior<{ entries: ReadonlyArray<LociLabelEntry> }>({ entries: [] })
@@ -105,11 +106,6 @@ export class PluginContext {
 
     readonly canvas3d: Canvas3D | undefined;
     readonly layout = new PluginLayout(this);
-    readonly toasts = new PluginToastManager(this);
-    readonly interactivity: Interactivity;
-
-    readonly lociLabels: LociLabelManager;
-
 
     readonly structureRepresentation = {
         registry: new StructureRepresentationRegistry(),
@@ -122,6 +118,7 @@ export class PluginContext {
     } as const
 
     readonly dataFormat = {
+        trajectory: TrajectoryFormatRegistry(),
         registry: new DataFormatRegistry()
     } as const
 
@@ -136,8 +133,11 @@ export class PluginContext {
             hierarchy: new StructureHierarchyManager(this),
             measurement: new StructureMeasurementManager(this),
             selection: new StructureSelectionManager(this)
-        }
-    };
+        },
+        interactivity: void 0 as any as InteractivityManager,
+        lociLabels: void 0 as any as LociLabelManager,
+        toast: new PluginToastManager(this)
+    } as const
 
     readonly customModelProperties = new CustomProperty.Registry<Model>();
     readonly customStructureProperties = new CustomProperty.Registry<Structure>();
@@ -295,8 +295,8 @@ export class PluginContext {
         this.initAnimations();
         this.initCustomParamEditors();
 
-        this.interactivity = new Interactivity(this);
-        this.lociLabels = new LociLabelManager(this);
+        (this.managers.interactivity as InteractivityManager) = new InteractivityManager(this);
+        (this.managers.lociLabels as LociLabelManager) = new LociLabelManager(this);
 
         (this.builders.representation as RepresentationBuilder)= new RepresentationBuilder(this);
 

+ 3 - 3
src/mol-plugin/state.ts

@@ -16,7 +16,7 @@ import { PluginCommands } from './commands';
 import { PluginAnimationManager } from '../mol-plugin-state/animation/manager';
 import { ParamDefinition as PD } from '../mol-util/param-definition';
 import { UUID } from '../mol-util';
-import { Interactivity } from './util/interactivity';
+import { InteractivityManager } from '../mol-plugin-state/manager/interactivity';
 export { PluginState }
 
 class PluginState {
@@ -58,7 +58,7 @@ class PluginState {
             } : void 0,
             cameraSnapshots: p.cameraSnapshots ? this.cameraSnapshots.getStateSnapshot() : void 0,
             canvas3d: p.canvas3d ? { props: this.plugin.canvas3d?.props } : void 0,
-            interactivity: p.interactivity ? { props: this.plugin.interactivity.props } : void 0,
+            interactivity: p.interactivity ? { props: this.plugin.managers.interactivity.props } : void 0,
             durationInMs: params && params.durationInMs
         };
     }
@@ -158,7 +158,7 @@ namespace PluginState {
             props?: Canvas3DProps
         },
         interactivity?: {
-            props?: Interactivity.Props
+            props?: InteractivityManager.Props
         },
         durationInMs?: number
     }

+ 0 - 43
src/mol-plugin/util/structure-complex-helper.ts

@@ -1,43 +0,0 @@
-/**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
-
-import { PluginContext } from '../context';
-import { StateBuilder } from '../../mol-state';
-import { PluginStateObject } from '../../mol-plugin-state/objects';
-import { StateTransforms } from '../../mol-plugin-state/transforms';
-import { StructureRepresentation3DHelpers } from '../../mol-plugin-state/transforms/representation';
-import { StructureComplexElementTypes } from '../../mol-plugin-state/transforms/model';
-
-export function createDefaultStructureComplex(
-    ctx: PluginContext, root: StateBuilder.To<PluginStateObject.Molecule.Structure>
-) {
-    root.apply(StateTransforms.Model.StructureComplexElement, { type: 'polymer' }, { tags: StructureComplexElementTypes['polymer'] })
-        .apply(StateTransforms.Representation.StructureRepresentation3D,
-            StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'cartoon'));
-
-    root.apply(StateTransforms.Model.StructureComplexElement, { type: 'ligand' }, { tags: StructureComplexElementTypes.ligand })
-        .apply(StateTransforms.Representation.StructureRepresentation3D,
-            StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'ball-and-stick'));
-
-    root.apply(StateTransforms.Model.StructureComplexElement, { type: 'non-standard' }, { tags: StructureComplexElementTypes['non-standard'] })
-        .apply(StateTransforms.Representation.StructureRepresentation3D,
-            StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'ball-and-stick', void 0, 'polymer-id'));
-
-    const branched = root.apply(StateTransforms.Model.StructureComplexElement, { type: 'branched' }, { tags: StructureComplexElementTypes.branched })
-
-    branched.apply(StateTransforms.Representation.StructureRepresentation3D,
-        StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'ball-and-stick', { alpha: 0.15 }));
-    branched.apply(StateTransforms.Representation.StructureRepresentation3D,
-        StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'carbohydrate'));
-
-    root.apply(StateTransforms.Model.StructureComplexElement, { type: 'water' }, { tags: StructureComplexElementTypes.water })
-        .apply(StateTransforms.Representation.StructureRepresentation3D,
-            StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'ball-and-stick', { alpha: 0.51 }));
-
-    root.apply(StateTransforms.Model.StructureComplexElement, { type: 'coarse' }, { tags: StructureComplexElementTypes.coarse })
-        .apply(StateTransforms.Representation.StructureRepresentation3D,
-            StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'spacefill', {}, 'polymer-id'));
-}