ソースを参照

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

Alexander Rose 5 年 前
コミット
615d9758bb
94 ファイル変更630 行追加594 行削除
  1. 4 4
      package.json
  2. 5 8
      src/apps/basic-wrapper/helpers.ts
  3. 6 6
      src/apps/basic-wrapper/index.ts
  4. 3 3
      src/apps/demos/lighting/index.ts
  5. 3 3
      src/apps/viewer/extensions/cellpack/model.ts
  6. 3 3
      src/apps/viewer/extensions/jolecule.ts
  7. 2 3
      src/examples/proteopedia-wrapper/coloring.ts
  8. 23 24
      src/examples/proteopedia-wrapper/index.ts
  9. 1 0
      src/mol-model-props/common/custom-element-property.ts
  10. 3 2
      src/mol-model-props/computed/representations/interactions.ts
  11. 2 1
      src/mol-model-props/computed/themes/accessible-surface-area.ts
  12. 2 1
      src/mol-model-props/computed/themes/interaction-type.ts
  13. 2 1
      src/mol-model-props/integrative/cross-link-restraint/color.ts
  14. 3 2
      src/mol-model-props/integrative/cross-link-restraint/representation.ts
  15. 2 1
      src/mol-model-props/pdbe/themes/structure-quality-report.ts
  16. 4 3
      src/mol-model-props/rcsb/representations/validation-report-clashes.ts
  17. 2 1
      src/mol-model-props/rcsb/themes/assembly-symmetry-cluster.ts
  18. 2 1
      src/mol-model-props/rcsb/themes/density-fit.ts
  19. 2 1
      src/mol-model-props/rcsb/themes/geometry-quality.ts
  20. 2 1
      src/mol-model-props/rcsb/themes/random-coil-index.ts
  21. 4 0
      src/mol-model/structure/structure/element/bundle.ts
  22. 11 50
      src/mol-plugin-state/actions/structure.ts
  23. 9 6
      src/mol-plugin-state/builder/structure.ts
  24. 12 12
      src/mol-plugin-state/builder/structure/preset.ts
  25. 35 82
      src/mol-plugin-state/builder/structure/representation.ts
  26. 97 0
      src/mol-plugin-state/helpers/structure-representation-params.ts
  27. 38 29
      src/mol-plugin-state/helpers/structure-selection-query.ts
  28. 1 3
      src/mol-plugin-state/manager/structure/component.ts
  29. 38 12
      src/mol-plugin-state/manager/structure/hierarchy-state.ts
  30. 16 40
      src/mol-plugin-state/manager/structure/hierarchy.ts
  31. 4 4
      src/mol-plugin-state/manager/structure/selection.ts
  32. 4 3
      src/mol-plugin-state/transforms/model.ts
  33. 2 100
      src/mol-plugin-state/transforms/representation.ts
  34. 2 2
      src/mol-plugin-ui/controls/common.tsx
  35. 29 7
      src/mol-plugin-ui/structure/components.tsx
  36. 3 2
      src/mol-plugin-ui/structure/source.tsx
  37. 2 2
      src/mol-plugin/behavior/dynamic/custom-props/computed/accessible-surface-area.ts
  38. 4 4
      src/mol-plugin/behavior/dynamic/custom-props/computed/interactions.ts
  39. 4 6
      src/mol-plugin/behavior/dynamic/custom-props/integrative/cross-link-restraint.ts
  40. 2 2
      src/mol-plugin/behavior/dynamic/custom-props/pdbe/structure-quality-report.ts
  41. 2 2
      src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts
  42. 8 10
      src/mol-plugin/behavior/dynamic/custom-props/rcsb/validation-report.ts
  43. 79 51
      src/mol-plugin/behavior/dynamic/selection/structure-representation-interaction.ts
  44. 0 3
      src/mol-plugin/index.ts
  45. 1 5
      src/mol-plugin/spec.ts
  46. 14 7
      src/mol-repr/representation.ts
  47. 6 4
      src/mol-repr/structure/registry.ts
  48. 2 2
      src/mol-repr/structure/representation.ts
  49. 3 2
      src/mol-repr/structure/representation/ball-and-stick.ts
  50. 3 2
      src/mol-repr/structure/representation/carbohydrate.ts
  51. 3 2
      src/mol-repr/structure/representation/cartoon.ts
  52. 3 2
      src/mol-repr/structure/representation/ellipsoid.ts
  53. 3 2
      src/mol-repr/structure/representation/gaussian-surface.ts
  54. 3 2
      src/mol-repr/structure/representation/gaussian-volume.ts
  55. 3 2
      src/mol-repr/structure/representation/label.ts
  56. 3 2
      src/mol-repr/structure/representation/molecular-surface.ts
  57. 3 2
      src/mol-repr/structure/representation/orientation.ts
  58. 3 2
      src/mol-repr/structure/representation/point.ts
  59. 3 2
      src/mol-repr/structure/representation/putty.ts
  60. 3 2
      src/mol-repr/structure/representation/spacefill.ts
  61. 3 2
      src/mol-repr/volume/direct-volume.ts
  62. 3 2
      src/mol-repr/volume/isosurface.ts
  63. 6 3
      src/mol-repr/volume/registry.ts
  64. 2 1
      src/mol-repr/volume/representation.ts
  65. 4 2
      src/mol-theme/color.ts
  66. 2 1
      src/mol-theme/color/carbohydrate-symbol.ts
  67. 2 1
      src/mol-theme/color/chain-id.ts
  68. 2 1
      src/mol-theme/color/element-index.ts
  69. 2 1
      src/mol-theme/color/element-symbol.ts
  70. 2 1
      src/mol-theme/color/entity-source.ts
  71. 2 1
      src/mol-theme/color/hydrophobicity.ts
  72. 2 1
      src/mol-theme/color/illustrative.ts
  73. 2 1
      src/mol-theme/color/model-index.ts
  74. 2 1
      src/mol-theme/color/molecule-type.ts
  75. 2 1
      src/mol-theme/color/occupancy.ts
  76. 2 1
      src/mol-theme/color/operator-hkl.ts
  77. 2 1
      src/mol-theme/color/operator-name.ts
  78. 2 1
      src/mol-theme/color/polymer-id.ts
  79. 2 1
      src/mol-theme/color/polymer-index.ts
  80. 2 1
      src/mol-theme/color/residue-name.ts
  81. 2 1
      src/mol-theme/color/secondary-structure.ts
  82. 2 1
      src/mol-theme/color/sequence-id.ts
  83. 2 1
      src/mol-theme/color/shape-group.ts
  84. 2 1
      src/mol-theme/color/uncertainty.ts
  85. 2 1
      src/mol-theme/color/uniform.ts
  86. 2 1
      src/mol-theme/color/unit-index.ts
  87. 2 2
      src/mol-theme/size.ts
  88. 2 1
      src/mol-theme/size/physical.ts
  89. 2 1
      src/mol-theme/size/shape-group.ts
  90. 2 1
      src/mol-theme/size/uncertainty.ts
  91. 2 1
      src/mol-theme/size/uniform.ts
  92. 14 4
      src/mol-theme/theme.ts
  93. 2 2
      src/mol-util/param-definition.ts
  94. 3 8
      webpack.config.common.js

+ 4 - 4
package.json

@@ -17,12 +17,12 @@
     "build-tsc": "tsc --incremental",
     "build-extra": "cpx \"src/**/*.{scss,woff,woff2,ttf,otf,eot,svg,html,ico}\" lib/",
     "build-webpack": "webpack --mode production",
-    "watch": "concurrently --kill-others \"npm:watch-tsc\" \"npm:watch-extra\" \"npm:watch-webpack\"",
-    "watch-viewer": "concurrently --kill-others \"npm:watch-tsc\" \"npm:watch-extra\" \"npm:watch-webpack-viewer\"",
+    "watch": "concurrently -c \"green,gray,gray\" --names \"tsc,ext,wpc\" --kill-others \"npm:watch-tsc\" \"npm:watch-extra\" \"npm:watch-webpack\"",
+    "watch-viewer": "concurrently -c \"green,gray,gray\" --names \"tsc,ext,wpc\" --kill-others \"npm:watch-tsc\" \"npm:watch-extra\" \"npm:watch-webpack-viewer\"",
     "watch-tsc": "tsc --watch --incremental",
     "watch-extra": "cpx \"src/**/*.{scss,woff,woff2,ttf,otf,eot,svg,html,ico}\" lib/ --watch",
-    "watch-webpack": "webpack -w --mode development --display minimal",
-    "watch-webpack-viewer": "webpack -w --mode development --display minimal --config ./webpack.config.viewer.js",
+    "watch-webpack": "webpack -w --mode development --display errors-only --info-verbosity verbose",
+    "watch-webpack-viewer": "webpack -w --mode development --display errors-only --info-verbosity verbose --config ./webpack.config.viewer.js",
     "serve": "http-server -p 1338",
     "model-server": "node lib/servers/model/server.js",
     "model-server-watch": "nodemon --watch lib lib/servers/model/server.js",

+ 5 - 8
src/apps/basic-wrapper/helpers.ts

@@ -8,11 +8,11 @@ import { Mat4, Vec3 } from '../../mol-math/linear-algebra';
 import { PluginContext } from '../../mol-plugin/context';
 import { PluginStateObject as PSO } from '../../mol-plugin-state/objects';
 import { StateTransforms } from '../../mol-plugin-state/transforms';
-import { StructureRepresentation3DHelpers } from '../../mol-plugin-state/transforms/representation';
 import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
 import { StateBuilder } from '../../mol-state';
 import Expression from '../../mol-script/language/expression';
 import { BuiltInColorThemeName } from '../../mol-theme/color';
+import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
 type SupportedFormats = 'cif' | 'pdb'
 
 export namespace StateHelper {
@@ -79,21 +79,18 @@ export namespace StateHelper {
     export function visual(ctx: PluginContext, visualRoot: StateBuilder.To<PSO.Molecule.Structure>) {
         visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' })
             .apply(StateTransforms.Representation.StructureRepresentation3D,
-                StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'cartoon'), { tags: 'seq-visual' });
+                createStructureRepresentationParams(ctx, void 0, { type: 'cartoon' }), { tags: 'seq-visual' });
         visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' })
             .apply(StateTransforms.Representation.StructureRepresentation3D,
-                StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'ball-and-stick'), { tags: 'het-visual' });
-        // visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'water' })
-        //     .apply(StateTransforms.Representation.StructureRepresentation3D,
-        //         StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'ball-and-stick', { alpha: 0.51 }), { tags: 'water-visual' });
+                createStructureRepresentationParams(ctx, void 0, { type: 'ball-and-stick' }), { tags: 'het-visual' });
         return visualRoot;
     }
 
-    export function ballsAndSticks(ctx: PluginContext, visualRoot: StateBuilder.To<PSO.Molecule.Structure>, expression: Expression, coloring?: BuiltInColorThemeName) {
+    export function ballsAndSticks(ctx: PluginContext, visualRoot: StateBuilder.To<PSO.Molecule.Structure>, expression: Expression, color?: BuiltInColorThemeName) {
         visualRoot
             .apply(StateTransforms.Model.StructureSelectionFromExpression, { expression })
             .apply(StateTransforms.Representation.StructureRepresentation3D,
-                StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'ball-and-stick', void 0, coloring), { tags: 'het-visual' });
+                createStructureRepresentationParams(ctx, void 0, { type: 'ball-and-stick', color }), { tags: 'het-visual' });
         return visualRoot;
     }
 

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

@@ -9,7 +9,6 @@ import './index.html'
 import { PluginContext } from '../../mol-plugin/context';
 import { PluginCommands } from '../../mol-plugin/commands';
 import { StateTransforms } from '../../mol-plugin-state/transforms';
-import { StructureRepresentation3DHelpers } from '../../mol-plugin-state/transforms/representation';
 import { Color } from '../../mol-util/color';
 import { PluginStateObject as PSO, PluginStateObject } from '../../mol-plugin-state/objects';
 import { AnimateModelIndex } from '../../mol-plugin-state/animation/built-in';
@@ -21,6 +20,7 @@ import { CustomToastMessage } from './controls';
 import { EmptyLoci } from '../../mol-model/loci';
 import { StructureSelection } from '../../mol-model/structure';
 import { Script } from '../../mol-script/script';
+import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
 require('mol-plugin-ui/skin/light.scss')
 
 type SupportedFormats = 'cif' | 'pdb'
@@ -47,7 +47,7 @@ class BasicWrapper {
             }
         });
 
-        this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add(StripedResidues.propertyProvider.descriptor.name, StripedResidues.colorThemeProvider!);
+        this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add(StripedResidues.colorThemeProvider!);
         this.plugin.managers.lociLabels.addProvider(StripedResidues.labelProvider!);
         this.plugin.customModelProperties.register(StripedResidues.propertyProvider, true);
     }
@@ -76,16 +76,16 @@ class BasicWrapper {
     private visual(visualRoot: StateBuilder.To<PSO.Molecule.Structure>) {
         visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' }, { ref: 'seq' })
             .apply(StateTransforms.Representation.StructureRepresentation3D,
-                StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'cartoon'), { ref: 'seq-visual' });
+                createStructureRepresentationParams(this.plugin, void 0, { type: 'cartoon' }), { ref: 'seq-visual' });
         visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' })
             .apply(StateTransforms.Representation.StructureRepresentation3D,
-                StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'ball-and-stick'), { ref: 'het-visual' });
+                createStructureRepresentationParams(this.plugin, void 0, { type: 'ball-and-stick' }), { ref: 'het-visual' });
         visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'water' })
             .apply(StateTransforms.Representation.StructureRepresentation3D,
-                StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'ball-and-stick', { alpha: 0.51 }), { ref: 'water-visual' });
+                createStructureRepresentationParams(this.plugin, void 0, { type: 'ball-and-stick', typeParams: { alpha: 0.51 } }), { ref: 'water-visual' });
         visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'spheres' })
             .apply(StateTransforms.Representation.StructureRepresentation3D,
-                StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'spacefill'), { ref: 'ihm-visual' });
+                createStructureRepresentationParams(this.plugin, void 0, { type: 'spacefill' }), { ref: 'ihm-visual' });
         return visualRoot;
     }
 

+ 3 - 3
src/apps/demos/lighting/index.ts

@@ -9,10 +9,10 @@ import './index.html'
 import { PluginContext } from '../../../mol-plugin/context';
 import { PluginCommands } from '../../../mol-plugin/commands';
 import { StateTransforms } from '../../../mol-plugin-state/transforms';
-import { StructureRepresentation3DHelpers } from '../../../mol-plugin-state/transforms/representation';
 import { PluginStateObject as PSO } from '../../../mol-plugin-state/objects';
 import { StateBuilder } from '../../../mol-state';
 import { Canvas3DProps } from '../../../mol-canvas3d/canvas3d';
+import { createStructureRepresentationParams } from '../../../mol-plugin-state/helpers/structure-representation-params';
 require('mol-plugin-ui/skin/light.scss')
 
 type SupportedFormats = 'cif' | 'pdb'
@@ -134,10 +134,10 @@ class LightingDemo {
     private visual(visualRoot: StateBuilder.To<PSO.Molecule.Structure>) {
         visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' })
             .apply(StateTransforms.Representation.StructureRepresentation3D,
-                StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'spacefill', {}, 'illustrative'), { ref: 'seq-visual' });
+                createStructureRepresentationParams(this.plugin, void 0, { type: 'spacefill', color: 'illustrative' }), { ref: 'seq-visual' });
         visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' })
             .apply(StateTransforms.Representation.StructureRepresentation3D,
-                StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'ball-and-stick'), { ref: 'het-visual' });
+                createStructureRepresentationParams(this.plugin, void 0, { type: 'ball-and-stick' }), { ref: 'het-visual' });
         return visualRoot;
     }
 

+ 3 - 3
src/apps/viewer/extensions/cellpack/model.ts

@@ -16,7 +16,6 @@ import { trajectoryFromPDB } from '../../../../mol-model-formats/structure/pdb';
 import { Mat4, Vec3, Quat } from '../../../../mol-math/linear-algebra';
 import { SymmetryOperator } from '../../../../mol-math/geometry';
 import { Task } from '../../../../mol-task';
-import { StructureRepresentation3DHelpers } from '../../../../mol-plugin-state/transforms/representation';
 import { StateTransforms } from '../../../../mol-plugin-state/transforms';
 import { distinctColors } from '../../../../mol-util/color/distinct';
 import { ModelIndexColorThemeProvider } from '../../../../mol-theme/color/model-index';
@@ -30,6 +29,7 @@ import { CifCategory, CifField } from '../../../../mol-io/reader/cif';
 import { mmCIF_Schema } from '../../../../mol-io/reader/cif/schema/mmcif';
 import { Column } from '../../../../mol-data/db';
 import { createModels } from '../../../../mol-model-formats/structure/basic/parser';
+import { createStructureRepresentationParams } from '../../../../mol-plugin-state/helpers/structure-representation-params';
 
 function getCellPackModelUrl(fileName: string, baseUrl: string) {
     return `${baseUrl}/results/${fileName}`
@@ -396,7 +396,7 @@ export const LoadCellPackModel = StateAction.build({
         }
         cellpackTree
             .apply(StateTransforms.Representation.StructureRepresentation3D,
-                StructureRepresentation3DHelpers.createParams(ctx, Structure.Empty, {
+                createStructureRepresentationParams(ctx, Structure.Empty, {
                     ...getReprParams(ctx, params.preset),
                     ...getColorParams(hue)
                 })
@@ -412,7 +412,7 @@ export const LoadCellPackModel = StateAction.build({
             .apply(StateTransforms.Model.StructureFromModel, undefined, { state: { isGhost: true } })
             .apply(StateTransforms.Misc.CreateGroup, { label: 'HIV1_envelope_Membrane' })
             .apply(StateTransforms.Representation.StructureRepresentation3D,
-                StructureRepresentation3DHelpers.createParams(ctx, Structure.Empty, {
+                createStructureRepresentationParams(ctx, Structure.Empty, {
                     ...getReprParams(ctx, params.preset),
                     color: UniformColorThemeProvider
                 })

+ 3 - 3
src/apps/viewer/extensions/jolecule.ts

@@ -18,7 +18,7 @@
 // import { UUID } from '../../../mol-util';
 // import { ColorNames } from '../../../mol-util/color/names';
 // import { Camera } from '../../../mol-canvas3d/camera';
-// import { StructureRepresentation3DHelpers } from '../../../mol-plugin/state/transforms/representation';
+// import { createStructureRepresentation3dParamss } from '../../../mol-plugin/state/transforms/representation';
 // import { createDefaultStructureComplex } from '../../../mol-plugin/util/structure-complex-helper';
 
 // export const CreateJoleculeState = StateAction.build({
@@ -108,13 +108,13 @@
 //         group
 //             .apply(StateTransforms.Model.StructureSelectionFromExpression, { expression: MS.struct.modifier.wholeResidues([ expression ]), label: 'Residue' })
 //             .apply(StateTransforms.Representation.StructureRepresentation3D,
-//                 StructureRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'ball-and-stick', {  }));
+//                 createStructureRepresentation3dParamss.getDefaultParamsStatic(plugin, 'ball-and-stick', {  }));
 //     }
 //     if (params.e.selected && params.e.selected.length > 0) {
 //         b.to(template.structure)
 //             .apply(StateTransforms.Model.StructureSelectionFromExpression, { expression: createExpression(params.e.selected), label: `Selected` })
 //             .apply(StateTransforms.Representation.StructureRepresentation3D,
-//                 StructureRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'ball-and-stick'));
+//                 createStructureRepresentation3dParamss.getDefaultParamsStatic(plugin, 'ball-and-stick'));
 //     }
 //     // TODO
 //     // for (const l of params.e.distances) {

+ 2 - 3
src/examples/proteopedia-wrapper/coloring.ts

@@ -94,7 +94,8 @@ export function createProteopediaCustomTheme(colors: number[]) {
         }
     }
 
-    const ProteopediaCustomColorThemeProvider: ColorTheme.Provider<ProteopediaCustomColorThemeParams> = {
+    return {
+        name: 'proteopedia-custom',
         label: 'Proteopedia Custom',
         category: 'Custom',
         factory: ProteopediaCustomColorTheme,
@@ -102,6 +103,4 @@ export function createProteopediaCustomTheme(colors: number[]) {
         defaultValues: PD.getDefaultValues(ProteopediaCustomColorThemeParams),
         isApplicable: (ctx: ThemeDataContext) => !!ctx.structure
     }
-
-    return ProteopediaCustomColorThemeProvider;
 }

+ 23 - 24
src/examples/proteopedia-wrapper/index.ts

@@ -10,7 +10,6 @@ import './index.html'
 import { PluginContext } from '../../mol-plugin/context';
 import { PluginCommands } from '../../mol-plugin/commands';
 import { StateTransforms } from '../../mol-plugin-state/transforms';
-import { StructureRepresentation3DHelpers } from '../../mol-plugin-state/transforms/representation';
 import { Color } from '../../mol-util/color';
 import { PluginStateObject as PSO, PluginStateObject } from '../../mol-plugin-state/objects';
 import { AnimateModelIndex } from '../../mol-plugin-state/animation/built-in';
@@ -23,12 +22,10 @@ import { PluginState } from '../../mol-plugin/state';
 import { Scheduler } from '../../mol-task';
 import { createProteopediaCustomTheme } from './coloring';
 import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
-import { BuiltInStructureRepresentations } from '../../mol-repr/structure/registry';
-import { BuiltInColorThemes } from '../../mol-theme/color';
-import { BuiltInSizeThemes } from '../../mol-theme/size';
 import { ColorNames } from '../../mol-util/color/names';
 import { InitVolumeStreaming, CreateVolumeStreamingInfo } from '../../mol-plugin/behavior/dynamic/volume-streaming/transformers';
 import { DefaultCanvas3DParams, Canvas3DProps } from '../../mol-canvas3d/canvas3d';
+import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
 // import { Vec3 } from 'mol-math/linear-algebra';
 // import { ParamDefinition } from 'mol-util/param-definition';
 // import { Text } from 'mol-geo/geometry/text/text';
@@ -70,8 +67,8 @@ class MolStarProteopediaWrapper {
 
         const customColoring = createProteopediaCustomTheme((options && options.customColorList) || []);
 
-        this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add('proteopedia-custom', customColoring);
-        this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add(EvolutionaryConservation.propertyProvider.descriptor.name, EvolutionaryConservation.colorThemeProvider!);
+        this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add(customColoring);
+        this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add(EvolutionaryConservation.colorThemeProvider!);
         this.plugin.managers.lociLabels.addProvider(EvolutionaryConservation.labelProvider!);
         this.plugin.customModelProperties.register(EvolutionaryConservation.propertyProvider, true);
     }
@@ -127,9 +124,10 @@ class MolStarProteopediaWrapper {
                 root.delete(StateElements.SequenceVisual);
             } else {
                 root.applyOrUpdate(StateElements.SequenceVisual, StateTransforms.Representation.StructureRepresentation3D,
-                    StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin,
-                        (style.sequence && style.sequence.kind) || 'cartoon',
-                        (style.sequence && style.sequence.coloring) || 'unit-index', structure));
+                    createStructureRepresentationParams(this.plugin, structure, {
+                        type: (style.sequence && style.sequence.kind) || 'cartoon',
+                        color: (style.sequence && style.sequence.coloring) || 'unit-index'
+                    }));
             }
         }
 
@@ -142,9 +140,10 @@ class MolStarProteopediaWrapper {
                     root.delete(StateElements.HetVisual);
                 } else {
                     root.applyOrUpdate(StateElements.HetVisual, StateTransforms.Representation.StructureRepresentation3D,
-                        StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin,
-                            (style.hetGroups && style.hetGroups.kind) || 'ball-and-stick',
-                            (style.hetGroups && style.hetGroups.coloring), structure));
+                        createStructureRepresentationParams(this.plugin, structure, {
+                            type: (style.hetGroups && style.hetGroups.kind) || 'ball-and-stick',
+                            color: style.hetGroups && style.hetGroups.coloring
+                        }));
                 }
             }
         }
@@ -158,7 +157,7 @@ class MolStarProteopediaWrapper {
                     root.delete(StateElements.Het3DSNFG);
                 } else {
                     root.applyOrUpdate(StateElements.Het3DSNFG, StateTransforms.Representation.StructureRepresentation3D,
-                        StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin, 'carbohydrate', void 0, structure));
+                        createStructureRepresentationParams(this.plugin, structure, { type: 'carbohydrate' }));
                 }
             }
         }
@@ -169,9 +168,11 @@ class MolStarProteopediaWrapper {
                 root.delete(StateElements.WaterVisual);
             } else {
                 root.applyOrUpdate(StateElements.WaterVisual, StateTransforms.Representation.StructureRepresentation3D,
-                    StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin,
-                        (style.water && style.water.kind) || 'ball-and-stick',
-                        (style.water && style.water.coloring), structure, { alpha: 0.51 }));
+                    createStructureRepresentationParams(this.plugin, structure, {
+                        type: (style.water && style.water.kind) || 'ball-and-stick',
+                        typeParams: { alpha: 0.51 },
+                        color: style.water && style.water.coloring
+                    }));
             }
         }
 
@@ -408,19 +409,17 @@ class MolStarProteopediaWrapper {
 
     private createSurVisualParams() {
         const asm = this.state.select(StateElements.Assembly)[0].obj as PluginStateObject.Molecule.Structure;
-        return StructureRepresentation3DHelpers.createParams(this.plugin, asm.data, {
-            type: BuiltInStructureRepresentations['ball-and-stick'],
-            color: BuiltInColorThemes.uniform, colorParams: { value: ColorNames.gray },
-            size: BuiltInSizeThemes.uniform, sizeParams: { value: 0.33 }
+        return createStructureRepresentationParams(this.plugin, asm.data, {
+            type: 'ball-and-stick',
+            color: 'uniform', colorParams: { value: ColorNames.gray },
+            size: 'uniform', sizeParams: { value: 0.33 }
         });
     }
 
     private createCoreVisualParams() {
         const asm = this.state.select(StateElements.Assembly)[0].obj as PluginStateObject.Molecule.Structure;
-        return StructureRepresentation3DHelpers.createParams(this.plugin, asm.data, {
-            type: BuiltInStructureRepresentations['ball-and-stick'],
-            // color: [BuiltInColorThemes.uniform, () => ({ value: ColorNames.gray })],
-            // size: [BuiltInSizeThemes.uniform, () => ({ value: 0.33 } )]
+        return createStructureRepresentationParams(this.plugin, asm.data, {
+            type: 'ball-and-stick'
         });
     }
 

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

@@ -98,6 +98,7 @@ namespace CustomElementProperty {
         }
 
         return {
+            name: modelProperty.descriptor.name,
             label: modelProperty.label,
             category: 'Custom',
             factory: Coloring,

+ 3 - 2
src/mol-model-props/computed/representations/interactions.ts

@@ -37,7 +37,8 @@ export function InteractionRepresentation(ctx: RepresentationContext, getParams:
     return Representation.createMulti('Interactions', ctx, getParams, StructureRepresentationStateBuilder, InteractionsVisuals as unknown as Representation.Def<Structure, InteractionsParams>)
 }
 
-export const InteractionsRepresentationProvider: StructureRepresentationProvider<InteractionsParams> = {
+export const InteractionsRepresentationProvider = StructureRepresentationProvider({
+    name: 'interactions',
     label: 'Non-covalent Interactions',
     description: 'Displays non-covalent interactions as dashed cylinders.',
     factory: InteractionRepresentation,
@@ -50,4 +51,4 @@ export const InteractionsRepresentationProvider: StructureRepresentationProvider
         attach: (ctx: CustomProperty.Context, structure: Structure) => InteractionsProvider.attach(ctx, structure, void 0, true),
         detach: (_, data) => InteractionsProvider.ref(data, false)
     }
-}
+})

+ 2 - 1
src/mol-model-props/computed/themes/accessible-surface-area.ts

@@ -64,7 +64,8 @@ export function AccessibleSurfaceAreaColorTheme(ctx: ThemeDataContext, props: PD
     }
 }
 
-export const AccessibleSurfaceAreaColorThemeProvider: ColorTheme.Provider<AccessibleSurfaceAreaColorThemeParams> = {
+export const AccessibleSurfaceAreaColorThemeProvider: ColorTheme.Provider<AccessibleSurfaceAreaColorThemeParams, 'accessible-surface-area'> = {
+    name: 'accessible-surface-area',
     label: 'Accessible Surface Area',
     category: ColorTheme.Category.Residue,
     factory: AccessibleSurfaceAreaColorTheme,

+ 2 - 1
src/mol-model-props/computed/themes/interaction-type.ts

@@ -106,7 +106,8 @@ export function InteractionTypeColorTheme(ctx: ThemeDataContext, props: PD.Value
     }
 }
 
-export const InteractionTypeColorThemeProvider: ColorTheme.Provider<InteractionTypeColorThemeParams> = {
+export const InteractionTypeColorThemeProvider: ColorTheme.Provider<InteractionTypeColorThemeParams, 'interaction-type'> = {
+    name: 'interaction-type',
     label: 'Interaction Type',
     category: ColorTheme.Category.Misc,
     factory: InteractionTypeColorTheme,

+ 2 - 1
src/mol-model-props/integrative/cross-link-restraint/color.ts

@@ -61,7 +61,8 @@ export function CrossLinkColorTheme(ctx: ThemeDataContext, props: PD.Values<Cros
     }
 }
 
-export const CrossLinkColorThemeProvider: ColorTheme.Provider<CrossLinkColorThemeParams> = {
+export const CrossLinkColorThemeProvider: ColorTheme.Provider<CrossLinkColorThemeParams, 'cross-link'> = {
+    name: 'cross-link',
     label: 'Cross Link',
     category: ColorTheme.Category.Misc,
     factory: CrossLinkColorTheme,

+ 3 - 2
src/mol-model-props/integrative/cross-link-restraint/representation.ts

@@ -134,7 +134,8 @@ export function CrossLinkRestraintRepresentation(ctx: RepresentationContext, get
     return Representation.createMulti('CrossLinkRestraint', ctx, getParams, StructureRepresentationStateBuilder, CrossLinkRestraintVisuals as unknown as Representation.Def<Structure, CrossLinkRestraintParams>)
 }
 
-export const CrossLinkRestraintRepresentationProvider: StructureRepresentationProvider<CrossLinkRestraintParams> = {
+export const CrossLinkRestraintRepresentationProvider = StructureRepresentationProvider({
+    name: CrossLinkRestraint.Tag.CrossLinkRestraint,
     label: 'Cross Link Restraint',
     description: 'Displays cross-link restraints.',
     factory: CrossLinkRestraintRepresentation,
@@ -147,4 +148,4 @@ export const CrossLinkRestraintRepresentationProvider: StructureRepresentationPr
         attach: (ctx: CustomProperty.Context, structure: Structure) => CrossLinkRestraintProvider.attach(ctx, structure, void 0, true),
         detach: (_, data) => CrossLinkRestraintProvider.ref(data, false)
     }
-}
+})

+ 2 - 1
src/mol-model-props/pdbe/themes/structure-quality-report.ts

@@ -77,7 +77,8 @@ export function StructureQualityReportColorTheme(ctx: ThemeDataContext, props: P
     }
 }
 
-export const StructureQualityReportColorThemeProvider: ColorTheme.Provider<Params> =  {
+export const StructureQualityReportColorThemeProvider: ColorTheme.Provider<Params, 'pdbe-structure-quality-report'> =  {
+    name: 'pdbe-structure-quality-report',
     label: 'Structure Quality Report',
     category: ColorTheme.Category.Validation,
     factory: StructureQualityReportColorTheme,

+ 4 - 3
src/mol-model-props/rcsb/representations/validation-report-clashes.ts

@@ -20,7 +20,7 @@ import { createLinkCylinderMesh, LinkCylinderParams, LinkCylinderStyle } from '.
 import { UnitsMeshParams, UnitsVisual, UnitsMeshVisual, StructureGroup } from '../../../mol-repr/structure/units-visual';
 import { VisualUpdateState } from '../../../mol-repr/util';
 import { LocationIterator } from '../../../mol-geo/util/location-iterator';
-import { ClashesProvider, IntraUnitClashes, InterUnitClashes } from '../validation-report';
+import { ClashesProvider, IntraUnitClashes, InterUnitClashes, ValidationReport } from '../validation-report';
 import { CustomProperty } from '../../common/custom-property';
 import { ComplexMeshParams, ComplexVisual, ComplexMeshVisual } from '../../../mol-repr/structure/complex-visual';
 import { Color } from '../../../mol-util/color';
@@ -278,7 +278,8 @@ export function ClashesRepresentation(ctx: RepresentationContext, getParams: Rep
     return repr
 }
 
-export const ClashesRepresentationProvider: StructureRepresentationProvider<ClashesParams> = {
+export const ClashesRepresentationProvider = StructureRepresentationProvider({
+    name: ValidationReport.Tag.Clashes,
     label: 'Validation Clashes',
     description: 'Displays clashes between atoms as disks. Data from wwPDB Validation Report, obtained via RCSB PDB.',
     factory: ClashesRepresentation,
@@ -291,4 +292,4 @@ export const ClashesRepresentationProvider: StructureRepresentationProvider<Clas
         attach: (ctx: CustomProperty.Context, structure: Structure) => ClashesProvider.attach(ctx, structure, void 0, true),
         detach: (_, data) => ClashesProvider.ref(data, false)
     }
-}
+})

+ 2 - 1
src/mol-model-props/rcsb/themes/assembly-symmetry-cluster.ts

@@ -91,7 +91,8 @@ export function AssemblySymmetryClusterColorTheme(ctx: ThemeDataContext, props:
     }
 }
 
-export const AssemblySymmetryClusterColorThemeProvider: ColorTheme.Provider<AssemblySymmetryClusterColorThemeParams> = {
+export const AssemblySymmetryClusterColorThemeProvider: ColorTheme.Provider<AssemblySymmetryClusterColorThemeParams, AssemblySymmetry.Tag.Cluster> = {
+    name: AssemblySymmetry.Tag.Cluster,
     label: 'Assembly Symmetry Cluster',
     category: ColorTheme.Category.Symmetry,
     factory: AssemblySymmetryClusterColorTheme,

+ 2 - 1
src/mol-model-props/rcsb/themes/density-fit.ts

@@ -60,7 +60,8 @@ export function DensityFitColorTheme(ctx: ThemeDataContext, props: {}): ColorThe
     }
 }
 
-export const DensityFitColorThemeProvider: ColorTheme.Provider<{}> = {
+export const DensityFitColorThemeProvider: ColorTheme.Provider<{}, ValidationReport.Tag.DensityFit> = {
+    name: ValidationReport.Tag.DensityFit,
     label: 'Density Fit',
     category: ColorTheme.Category.Validation,
     factory: DensityFitColorTheme,

+ 2 - 1
src/mol-model-props/rcsb/themes/geometry-quality.ts

@@ -100,7 +100,8 @@ export function GeometryQualityColorTheme(ctx: ThemeDataContext, props: PD.Value
     }
 }
 
-export const GeometryQualityColorThemeProvider: ColorTheme.Provider<GeometricQualityColorThemeParams> = {
+export const GeometryQualityColorThemeProvider: ColorTheme.Provider<GeometricQualityColorThemeParams, ValidationReport.Tag.GeometryQuality> = {
+    name: ValidationReport.Tag.GeometryQuality,
     label: 'Geometry Quality',
     category: ColorTheme.Category.Validation,
     factory: GeometryQualityColorTheme,

+ 2 - 1
src/mol-model-props/rcsb/themes/random-coil-index.ts

@@ -51,7 +51,8 @@ export function RandomCoilIndexColorTheme(ctx: ThemeDataContext, props: {}): Col
     }
 }
 
-export const RandomCoilIndexColorThemeProvider: ColorTheme.Provider<{}> = {
+export const RandomCoilIndexColorThemeProvider: ColorTheme.Provider<{}, ValidationReport.Tag.RandomCoilIndex> = {
+    name: ValidationReport.Tag.RandomCoilIndex,
     label: 'Random Coil Index',
     category: ColorTheme.Category.Validation,
     factory: RandomCoilIndexColorTheme,

+ 4 - 0
src/mol-model/structure/structure/element/bundle.ts

@@ -40,6 +40,10 @@ export namespace Bundle {
         return fromLoci(StructureSelection.toLociWithSourceUnits(StructureSelection.Singletons(parent, structure)))
     }
 
+    export function fromSelection(selection: StructureSelection) {
+        return fromLoci(StructureSelection.toLociWithSourceUnits(selection));
+    }
+
     export function fromLoci(loci: Loci): Bundle {
         const _elements: {
             unit: Unit

+ 11 - 50
src/mol-plugin-state/actions/structure.ts

@@ -5,22 +5,19 @@
  * @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 } 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 { TrajectoryFormat } from '../builder/structure';
+import { BuildInTrajectoryFormat } from '../formats/trajectory';
 import { RootStructureDefinition } from '../helpers/root-structure';
 import { PluginStateObject } from '../objects';
 import { StateTransforms } from '../transforms';
 import { Download, ParsePsf } from '../transforms/data';
 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<PluginStateObject.Data.String | PluginStateObject.Data.Binary> = {
     label: 'mmCIF',
@@ -37,7 +34,7 @@ export const MmcifProvider: DataFormatProvider<PluginStateObject.Data.String | P
         return Task.create('mmCIF default builder', async taskCtx => {
             const { structure } = await ctx.builders.structure.parseStructure({ data, dataFormat: 'mmcif' });
             if (options.visuals) {
-                await ctx.builders.structure.representation.structurePreset(structure, 'auto');
+                await ctx.builders.structure.representation.applyPreset(structure, 'auto');
             }
         })
     }
@@ -55,7 +52,7 @@ export const PdbProvider: DataFormatProvider<any> = {
         return Task.create('PDB default builder', async () => {
             const { structure } = await ctx.builders.structure.parseStructure({ data, dataFormat: 'pdb' });
             if (options.visuals) {
-                await ctx.builders.structure.representation.structurePreset(structure, 'auto');
+                await ctx.builders.structure.representation.applyPreset(structure, 'auto');
             }
         })
     }
@@ -73,7 +70,7 @@ export const GroProvider: DataFormatProvider<any> = {
         return Task.create('GRO default builder', async () => {
             const { structure } = await ctx.builders.structure.parseStructure({ data, dataFormat: 'gro' });
             if (options.visuals) {
-                await ctx.builders.structure.representation.structurePreset(structure, 'auto');
+                await ctx.builders.structure.representation.applyPreset(structure, 'auto');
             }
         })
     }
@@ -91,7 +88,7 @@ export const Provider3dg: DataFormatProvider<any> = {
         return Task.create('3DG default builder', async () => {
             const { structure } = await ctx.builders.structure.parseStructure({ data, dataFormat: '3dg' });
             if (options.visuals) {
-                await ctx.builders.structure.representation.structurePreset(structure, 'auto');
+                await ctx.builders.structure.representation.applyPreset(structure, 'auto');
             }
         })
     }
@@ -249,7 +246,7 @@ const DownloadStructure = StateAction.build({
                 structureProperties: supportProps
             });
             if (createRepr) {
-                await plugin.builders.structure.representation.structurePreset(structure, 'auto');
+                await plugin.builders.structure.representation.applyPreset(structure, 'auto');
             }
         } else {
             for (const download of downloadParams) {
@@ -261,7 +258,7 @@ const DownloadStructure = StateAction.build({
                     structureProperties: supportProps
                 });
                 if (createRepr) {
-                    await plugin.builders.structure.representation.structurePreset(structure, 'auto');
+                    await plugin.builders.structure.representation.applyPreset(structure, 'auto');
                 }
             }
         }
@@ -306,25 +303,11 @@ export const Create3DRepresentationPreset = StateAction.build({
     isApplicable(a, _, plugin: PluginContext) { return plugin.builders.structure.representation.hasPreset(a.data); },
     params(a, plugin: PluginContext) {
         return {
-            type: plugin.builders.structure.representation.getPresets(a.data)
+            type: plugin.builders.structure.representation.getPresetsWithOptions(a.data)
         };
     }
 })(({ ref, params }, plugin: PluginContext) => {
-    plugin.builders.structure.representation.structurePreset(ref, params.type.name, params.type.params);
-});
-
-export const Remove3DRepresentationPreset = StateAction.build({
-    display: { name: 'Remove 3D Representation Preset', description: 'Remove 3D representations.' },
-    from: PluginStateObject.Molecule.Structure,
-    isApplicable(_, t, plugin: PluginContext) { return plugin.builders.structure.representation.hasPresetRepresentation(t.ref); },
-    params(a, plugin: PluginContext) {
-        return {
-            type: plugin.builders.structure.representation.getPresets(a.data).select
-        };
-    }
-})(({ ref, params }, plugin: PluginContext) => {
-    // TODO: this will be completely handled by the managed and is just for testing purposes
-    plugin.builders.structure.representation.removePreset(params.type, ref);
+    return plugin.builders.structure.representation.applyPreset(ref, params.type.name, params.type.params);
 });
 
 export const UpdateTrajectory = StateAction.build({
@@ -389,28 +372,6 @@ export const TransformStructureConformation = StateAction.build({
     return state.updateTree(root);
 });
 
-export const StructureFromSelection = StateAction.build({
-    display: { name: 'Structure from Current Selection', description: 'Create a new Structure from the current selection.' },
-    from: PluginStateObject.Molecule.Structure,
-    params: {
-        label: PD.Text()
-    }
-    // isApplicable(a, t, ctx: PluginContext) {
-    //     return t.transformer !== CustomModelProperties;
-    // }
-})(async ({ a, ref, params, state }, plugin: PluginContext) => {
-    const sel = plugin.managers.structure.selection.getLoci(a.data);
-    if (Loci.isEmpty(sel)) return;
-
-    const bundle = StructureElement.Bundle.fromLoci(sel);
-
-    await plugin.builders.structure.tryCreateComponent(ref, {
-        type: { name: 'bundle', params: bundle },
-        nullIfEmpty: true,
-        label: params.label
-    }, UUID.create22());
-});
-
 export const AddTrajectory = StateAction.build({
     display: { name: 'Add Trajectory', description: 'Add trajectory from existing model/topology and coordinates.' },
     from: PluginStateObject.Root,
@@ -440,6 +401,6 @@ export const AddTrajectory = StateAction.build({
 
         await state.updateTree(model).runInContext(taskCtx);
         const structure = await ctx.builders.structure.createStructure(model.selector);
-        await ctx.builders.structure.representation.structurePreset(structure, 'auto');
+        await ctx.builders.structure.representation.applyPreset(structure, 'auto');
     }).runInContext(taskCtx)
 }));

+ 9 - 6
src/mol-plugin-state/builder/structure.ts

@@ -5,7 +5,7 @@
  */
 
 import { PluginContext } from '../../mol-plugin/context';
-import { StateObjectRef, StateObjectSelector, StateTransformer } from '../../mol-state';
+import { StateObjectRef, StateObjectSelector, StateTransformer, StateTransform } from '../../mol-state';
 import { PluginStateObject as SO } from '../objects';
 import { StateTransforms } from '../transforms';
 import { RootStructureDefinition } from '../helpers/root-structure';
@@ -14,6 +14,7 @@ import { BuildInTrajectoryFormat, TrajectoryFormatProvider } from '../formats/tr
 import { StructureRepresentationBuilder } from './structure/representation';
 import { StructureSelectionQuery } from '../helpers/structure-selection-query';
 import { Task } from '../../mol-task';
+import { StructureElement } from '../../mol-model/structure';
 
 export type TrajectoryFormat = 'pdb' | 'cif' | 'gro' | '3dg'
 
@@ -95,10 +96,10 @@ export class StructureBuilder {
         }
     }
 
-    async createModel(trajectory: StateObjectRef<SO.Molecule.Trajectory>, params?: StateTransformer.Params<StateTransforms['Model']['ModelFromTrajectory']>) {
+    async createModel(trajectory: StateObjectRef<SO.Molecule.Trajectory>, params?: StateTransformer.Params<StateTransforms['Model']['ModelFromTrajectory']>, initialState?: Partial<StateTransform.State>) {
         const state = this.dataState;
         const model = state.build().to(trajectory)
-            .apply(StateTransforms.Model.ModelFromTrajectory, params || { modelIndex: 0 }, { tags: StructureBuilderTags.Model });
+            .apply(StateTransforms.Model.ModelFromTrajectory, params || { modelIndex: 0 }, { tags: StructureBuilderTags.Model, state: initialState });
 
         await this.plugin.runTask(this.dataState.updateTree(model, { revertOnError: true }));
         return model.selector;
@@ -112,10 +113,10 @@ export class StructureBuilder {
         return props.selector;
     }
 
-    async createStructure(model: StateObjectRef<SO.Molecule.Model>, params?: RootStructureDefinition.Params) {
+    async createStructure(model: StateObjectRef<SO.Molecule.Model>, params?: RootStructureDefinition.Params, initialState?: Partial<StateTransform.State>) {
         const state = this.dataState;
         const structure = state.build().to(model)
-            .apply(StateTransforms.Model.StructureFromModel, { type: params || { name: 'assembly', params: { } } }, { tags: StructureBuilderTags.Structure });        
+            .apply(StateTransforms.Model.StructureFromModel, { type: params || { name: 'assembly', params: { } } }, { tags: StructureBuilderTags.Structure, state: initialState });        
 
         await this.plugin.runTask(this.dataState.updateTree(structure, { revertOnError: true }));
         return structure.selector;
@@ -172,7 +173,9 @@ export class StructureBuilder {
     
             const transformParams: StructureComponentParams = query.referencesCurrent
                 ? {
-                    type: { name: 'bundle', params: await StructureSelectionQuery.getBundle(this.plugin, taskCtx, query, structureData) },
+                    type: {
+                        name: 'bundle',
+                        params: StructureElement.Bundle.fromSelection(await query.getSelection(this.plugin, taskCtx, structureData)) },
                     nullIfEmpty: true,
                     label: label || query.label
                 } : {

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

@@ -75,15 +75,15 @@ const polymerAndLigand = StructureRepresentationProvider({
 
         const { update, builder, typeParams, color } = reprBuilder(plugin, params);
         const representations = {
-            polymer: builder.builtInRepresentation(update, components.polymer, { type: 'cartoon', typeParams, color }),
-            ligand: builder.builtInRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams, color }),
-            nonStandard: builder.builtInRepresentation(update, components.nonStandard, { type: 'ball-and-stick', typeParams, color: color || 'polymer-id' }),
+            polymer: builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams, color }),
+            ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams, color }),
+            nonStandard: builder.buildRepresentation(update, components.nonStandard, { type: 'ball-and-stick', typeParams, color: color || 'polymer-id' }),
             branched: components.branched && {
-                ballAndStick: builder.builtInRepresentation(update, components.branched, { type: 'ball-and-stick', typeParams: { ...typeParams, alpha: 0.15 }, color }),
-                snfg3d: builder.builtInRepresentation(update, components.branched, { type: 'carbohydrate', typeParams, color }),
+                ballAndStick: builder.buildRepresentation(update, components.branched, { type: 'ball-and-stick', typeParams: { ...typeParams, alpha: 0.15 }, color }),
+                snfg3d: builder.buildRepresentation(update, components.branched, { type: 'carbohydrate', typeParams, color }),
             },
-            water: builder.builtInRepresentation(update, components.water, { type: 'ball-and-stick', typeParams: { ...typeParams, alpha: 0.51 }, color }),
-            coarse: builder.builtInRepresentation(update, components.coarse, { type: 'spacefill', typeParams, color: color || 'polymer-id' })
+            water: builder.buildRepresentation(update, components.water, { type: 'ball-and-stick', typeParams: { ...typeParams, alpha: 0.51 }, color }),
+            coarse: builder.buildRepresentation(update, components.coarse, { type: 'spacefill', typeParams, color: color || 'polymer-id' })
         };
 
         await state.updateTree(update, { revertOnError: false }).runInContext(ctx);
@@ -103,8 +103,8 @@ const proteinAndNucleic = StructureRepresentationProvider({
 
         const { update, builder, typeParams, color } = reprBuilder(plugin, params);
         const representations = {
-            protein: builder.builtInRepresentation(update, components.protein, { type: 'cartoon', typeParams, color }),
-            nucleic: builder.builtInRepresentation(update, components.nucleic, { type: 'gaussian-surface', typeParams, color })
+            protein: builder.buildRepresentation(update, components.protein, { type: 'cartoon', typeParams, color }),
+            nucleic: builder.buildRepresentation(update, components.nucleic, { type: 'gaussian-surface', typeParams, color })
         };
 
         await state.updateTree(update, { revertOnError: true }).runInContext(ctx);
@@ -142,7 +142,7 @@ const coarseSurface = StructureRepresentationProvider({
         
         const { update, builder, typeParams, color } = reprBuilder(plugin, params);
         const representations = {
-            trace: builder.builtInRepresentation(update, components.trace, { type: 'gaussian-surface', typeParams: { ...typeParams, ...gaussianProps }, color })
+            trace: builder.buildRepresentation(update, components.trace, { type: 'gaussian-surface', typeParams: { ...typeParams, ...gaussianProps }, color })
         };
 
         await state.updateTree(update, { revertOnError: true }).runInContext(ctx);
@@ -161,7 +161,7 @@ const polymerCartoon = StructureRepresentationProvider({
 
         const { update, builder, typeParams, color } = reprBuilder(plugin, params);
         const representations = {
-            polymer: builder.builtInRepresentation(update, components.polymer, { type: 'cartoon', typeParams, color })
+            polymer: builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams, color })
         };
 
         await state.updateTree(update, { revertOnError: true }).runInContext(ctx);
@@ -181,7 +181,7 @@ const atomicDetail = StructureRepresentationProvider({
 
         const { update, builder, typeParams, color } = reprBuilder(plugin, params);
         const representations = {
-            all: builder.builtInRepresentation(update, components.all, { type: 'ball-and-stick', typeParams, color })
+            all: builder.buildRepresentation(update, components.all, { type: 'ball-and-stick', typeParams, color })
         };
 
         await state.updateTree(update, { revertOnError: true }).runInContext(ctx);

+ 35 - 82
src/mol-plugin-state/builder/structure/representation.ts

@@ -6,26 +6,22 @@
 
 import { arrayFind } from '../../../mol-data/util';
 import { Structure } from '../../../mol-model/structure';
-import { StateTransform, StateTree, StateSelection, StateObjectRef, StateBuilder } from '../../../mol-state';
+import { PluginContext } from '../../../mol-plugin/context';
+import { StateBuilder, StateObjectRef, StateObjectSelector } from '../../../mol-state';
 import { Task } from '../../../mol-task';
 import { isProductionMode } from '../../../mol-util/debug';
 import { objectForEach } from '../../../mol-util/object';
 import { ParamDefinition as PD } from '../../../mol-util/param-definition';
-import { PluginContext } from '../../../mol-plugin/context';
-import { PresetStructureReprentations } from './preset';
-import { StructureRepresentationProvider, RepresentationProviderTags } from './provider';
-import { UniqueArray } from '../../../mol-data/generic';
+import { createStructureRepresentationParams, StructureRepresentationBuiltInProps, StructureRepresentationProps } from '../../helpers/structure-representation-params';
 import { PluginStateObject } from '../../objects';
-import { StructureRepresentation3D, StructureRepresentation3DHelpers } from '../../transforms/representation';
-import { RepresentationProvider } from '../../../mol-repr/representation';
-import { SizeTheme, BuiltInSizeThemeName } from '../../../mol-theme/size';
-import { ColorTheme, BuiltInColorThemeName } from '../../../mol-theme/color';
-import { BuiltInStructureRepresentationsName } from '../../../mol-repr/structure/registry';
+import { StructureRepresentation3D } from '../../transforms/representation';
+import { PresetStructureReprentations } from './preset';
+import { RepresentationProviderTags, StructureRepresentationProvider } from './provider';
 
 export type StructureRepresentationProviderRef = keyof PresetStructureReprentations | StructureRepresentationProvider | string
 
 export class StructureRepresentationBuilder {
-    private providers: StructureRepresentationProvider[] = [];
+    private _providers: StructureRepresentationProvider[] = [];
     private providerMap: Map<string, StructureRepresentationProvider> = new Map();
     private get dataState() { return this.plugin.state.dataState; }
 
@@ -33,23 +29,33 @@ export class StructureRepresentationBuilder {
 
     private resolveProvider(ref: StructureRepresentationProviderRef) {
         return typeof ref === 'string'
-            ? PresetStructureReprentations[ref as keyof PresetStructureReprentations] ?? arrayFind(this.providers, p => p.id === ref)
+            ? PresetStructureReprentations[ref as keyof PresetStructureReprentations] ?? arrayFind(this._providers, p => p.id === ref)
             : ref;
     }
 
     hasPreset(s: Structure) {
-        for (const p of this.providers) {
+        for (const p of this._providers) {
             if (!p.isApplicable || p.isApplicable(s, this.plugin)) return true;
         }
         return false;
     }
 
-    get providerList(): ReadonlyArray<StructureRepresentationProvider> { return this.providers; }
+    get providers(): ReadonlyArray<StructureRepresentationProvider> { return this._providers; }
+
+    getPresets(s?: Structure) {
+        if (!s) return this.providers;
+        const ret = [];
+        for (const p of this._providers) {
+            if (p.isApplicable && !p.isApplicable(s, this.plugin)) continue;
+            ret.push(p);
+        }
+        return ret;
+    }
 
-    getPresets(s: Structure) {
+    getPresetsWithOptions(s: Structure) {
         const options: [string, string][] = [];
         const map: { [K in string]: PD.Any } = Object.create(null);
-        for (const p of this.providers) {
+        for (const p of this._providers) {
             if (p.isApplicable && !p.isApplicable(s, this.plugin)) continue;
 
             options.push([p.id, p.display.name]);
@@ -59,71 +65,18 @@ export class StructureRepresentationBuilder {
         return PD.MappedStatic(options[0][0], map, { options });
     }
 
-    hasPresetRepresentation(ref: StateObjectRef) {
-        // TODO: make this state selection function?
-        const tree = this.plugin.state.dataState.tree;
-        const root = StateObjectRef.resolve(this.plugin.state.dataState, ref);
-        if (!root) return false;
-        return StateTree.doPreOrder(tree, root.transform,  { found: false, map: this.providerMap }, (n, _, s) => {
-            if (!n.tags) return;
-            for (const t of n.tags) {
-                if (s.map.has(t)) {
-                    s.found = true;
-                    return false;
-                }
-            }
-        }).found;
-    }
-
-    getPresetRepresentations(ref: StateObjectRef) {
-        const tree = this.plugin.state.dataState.tree;
-        const root = StateObjectRef.resolve(this.plugin.state.dataState, ref);
-        if (!root) return [];
-        return StateTree.doPreOrder(tree, root.transform, { found: UniqueArray.create<string, StructureRepresentationProvider>(), map: this.providerMap }, (n, _, s) => {
-            if (!n.tags) return;
-            for (const t of n.tags) {
-                if (s.map.has(t)) UniqueArray.add(s.found, t, s.map.get(t)!);
-            }
-        }).found.array;
-    }
-
     registerPreset(provider: StructureRepresentationProvider) {
         if (this.providerMap.has(provider.id)) {
             throw new Error(`Repr. provider with id '${provider.id}' already registered.`);
         }
-        // TODO: sort by group
-        this.providers.push(provider);
+        this._providers.push(provider);
         this.providerMap.set(provider.id, provider);
     }
 
-    removePreset(providerRef: StructureRepresentationProviderRef, structureRoot?: StateObjectRef) {
-        const id = this.resolveProvider(providerRef)?.id;
-        if (!id) return;
-
-        const state = this.plugin.state.dataState;
-        const root = StateObjectRef.resolveRef(structureRoot) || StateTransform.RootRef;
-        const reprs = StateSelection.findWithAllTags(state.tree, root, new Set([id, RepresentationProviderTags.Representation]));
-
-        const builder = state.build();
-        for (const r of reprs) {
-            builder.delete(r.ref);
-        }
-
-        const tree = builder.currentTree;
-        const selections = StateSelection.findWithAllTags(tree, root, new Set([RepresentationProviderTags.Component]));
-
-        for (const s of selections) {
-            if (!tree.children.has(s.ref) || tree.children.get(s.ref).size === 0) builder.delete(s.ref);
-        }
-
-        if (builder.editInfo.count === 0) return;
-        return this.plugin.runTask(state.updateTree(builder));
-    }
-
-    structurePreset<K extends keyof PresetStructureReprentations>(parent: StateObjectRef, preset: K, params?: StructureRepresentationProvider.Params<PresetStructureReprentations[K]>): Promise<StructureRepresentationProvider.State<PresetStructureReprentations[K]>> | undefined
-    structurePreset<P = any, S = {}>(parent: StateObjectRef, providers: StructureRepresentationProvider<P, S>, params?: P): Promise<S> | undefined
-    structurePreset(parent: StateObjectRef, providerId: string, params?: any): Promise<any> | undefined
-    structurePreset(parent: StateObjectRef, providerRef: string | StructureRepresentationProvider, params?: any): Promise<any> | undefined {
+    applyPreset<K extends keyof PresetStructureReprentations>(parent: StateObjectRef, preset: K, params?: StructureRepresentationProvider.Params<PresetStructureReprentations[K]>): Promise<StructureRepresentationProvider.State<PresetStructureReprentations[K]>> | undefined
+    applyPreset<P = any, S = {}>(parent: StateObjectRef, providers: StructureRepresentationProvider<P, S>, params?: P): Promise<S> | undefined
+    applyPreset(parent: StateObjectRef, providerId: string, params?: any): Promise<any> | undefined
+    applyPreset(parent: StateObjectRef, providerRef: string | StructureRepresentationProvider, params?: any): Promise<any> | undefined {
         const provider = this.resolveProvider(providerRef);
         if (!provider) return;
 
@@ -143,13 +96,13 @@ export class StructureRepresentationBuilder {
         return this.plugin.runTask(task);
     }
 
-    async addRepresentation<R extends RepresentationProvider<Structure, any, any>, C extends ColorTheme.Provider<any>, S extends SizeTheme.Provider<any>>
-        (structure: StateObjectRef<PluginStateObject.Molecule.Structure>, props?: StructureRepresentation3DHelpers.Props<R, C, S>) {
-
+    async addRepresentation<P extends StructureRepresentationBuiltInProps>(structure: StateObjectRef<PluginStateObject.Molecule.Structure>, props?: P): Promise<StateObjectSelector<PluginStateObject.Molecule.Structure.Representation3D>>
+    async addRepresentation<P extends StructureRepresentationProps>(structure: StateObjectRef<PluginStateObject.Molecule.Structure>, props?: P): Promise<StateObjectSelector<PluginStateObject.Molecule.Structure.Representation3D>>
+    async addRepresentation(structure: StateObjectRef<PluginStateObject.Molecule.Structure>, props?: any) {
         const data = StateObjectRef.resolveAndCheck(this.dataState, structure)?.obj?.data;
         if (!data) return;
 
-        const params = StructureRepresentation3DHelpers.createParams(this.plugin, data, props);
+        const params = createStructureRepresentationParams(this.plugin, data, props);
         const repr = this.dataState.build()
             .to(structure)
             .apply(StructureRepresentation3D, params, { tags: RepresentationProviderTags.Representation });
@@ -158,14 +111,14 @@ export class StructureRepresentationBuilder {
         return  repr.selector;
     }
 
-    builtInRepresentation<R extends BuiltInStructureRepresentationsName, C extends BuiltInColorThemeName, S extends BuiltInSizeThemeName>
-        (builder: StateBuilder.Root, structure: StateObjectRef<PluginStateObject.Molecule.Structure> | undefined, props?: StructureRepresentation3DHelpers.BuildInProps<R, C, S>) {
-
+    async buildRepresentation<P extends StructureRepresentationBuiltInProps>(builder: StateBuilder.Root, structure: StateObjectRef<PluginStateObject.Molecule.Structure> | undefined, props?: P): Promise<StateObjectSelector<PluginStateObject.Molecule.Structure.Representation3D>>
+    async buildRepresentation<P extends StructureRepresentationProps>(builder: StateBuilder.Root, structure: StateObjectRef<PluginStateObject.Molecule.Structure> | undefined, props?: P): Promise<StateObjectSelector<PluginStateObject.Molecule.Structure.Representation3D>>
+    async buildRepresentation(builder: StateBuilder.Root, structure: StateObjectRef<PluginStateObject.Molecule.Structure> | undefined, props?: any) {
         if (!structure) return;
         const data = StateObjectRef.resolveAndCheck(this.dataState, structure)?.obj?.data;
         if (!data) return;
 
-        const params = StructureRepresentation3DHelpers.createBuiltInParams(this.plugin, data, props);
+        const params = createStructureRepresentationParams(this.plugin, data, props);
         return builder
             .to(structure)
             .apply(StructureRepresentation3D, params, { tags: RepresentationProviderTags.Representation })

+ 97 - 0
src/mol-plugin-state/helpers/structure-representation-params.ts

@@ -0,0 +1,97 @@
+/**
+ * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { Structure } from '../../mol-model/structure';
+import { PluginContext } from '../../mol-plugin/context';
+import { RepresentationProvider } from '../../mol-repr/representation';
+import { BuiltInStructureRepresentations, BuiltInStructureRepresentationsName } from '../../mol-repr/structure/registry'
+import { StateTransformer } from '../../mol-state';
+import { BuiltInColorThemeName, BuiltInColorThemes, ColorTheme } from '../../mol-theme/color';
+import { BuiltInSizeThemeName, BuiltInSizeThemes, SizeTheme } from '../../mol-theme/size';
+import { ParamDefinition as PD } from '../../mol-util/param-definition';
+import { StructureRepresentation3D } from '../transforms/representation';
+
+export interface StructureRepresentationBuiltInProps<
+    R extends BuiltInStructureRepresentationsName = BuiltInStructureRepresentationsName,
+    C extends BuiltInColorThemeName = BuiltInColorThemeName,
+    S extends BuiltInSizeThemeName = BuiltInSizeThemeName> {
+    /** Using any registered name will work, but code completion will break */
+    type?: R,
+    typeParams?: Partial<RepresentationProvider.ParamValues<BuiltInStructureRepresentations[R]>>,
+    /** Using any registered name will work, but code completion will break */
+    color?: C,
+    colorParams?: Partial<ColorTheme.ParamValues<BuiltInColorThemes[C]>>,
+    /** Using any registered name will work, but code completion will break */
+    size?: S,
+    sizeParams?: Partial<SizeTheme.ParamValues<BuiltInSizeThemes[S]>>
+}
+
+export interface StructureRepresentationProps<
+    R extends RepresentationProvider<Structure> = RepresentationProvider<Structure>,
+    C extends ColorTheme.Provider = ColorTheme.Provider,
+    S extends SizeTheme.Provider = SizeTheme.Provider> {
+    type?: R,
+    typeParams?: Partial<RepresentationProvider.ParamValues<R>>,
+    color?: C,
+    colorParams?: Partial<ColorTheme.ParamValues<C>>,
+    size?: S,
+    sizeParams?: Partial<SizeTheme.ParamValues<S>>
+}
+
+export function createStructureRepresentationParams
+    <R extends BuiltInStructureRepresentationsName, C extends BuiltInColorThemeName, S extends BuiltInSizeThemeName>
+    (ctx: PluginContext, structure?: Structure, props?: StructureRepresentationBuiltInProps<R, C, S>): StateTransformer.Params<StructureRepresentation3D>
+export function createStructureRepresentationParams
+    <R extends RepresentationProvider<Structure>, C extends ColorTheme.Provider, S extends SizeTheme.Provider>
+    (ctx: PluginContext, structure?: Structure, props?: StructureRepresentationProps<R, C, S>): StateTransformer.Params<StructureRepresentation3D> 
+export function createStructureRepresentationParams(ctx: PluginContext, structure?: Structure, props: any = {}): StateTransformer.Params<StructureRepresentation3D>  {
+    const p = props as StructureRepresentationBuiltInProps;
+    if (typeof p.type === 'string' || typeof p.color === 'string' || typeof p.size === 'string') return createParamsByName(ctx, structure || Structure.Empty, props);
+    return createParamsProvider(ctx, structure || Structure.Empty, props);
+}
+
+function createParamsByName(ctx: PluginContext, structure: Structure, props: StructureRepresentationBuiltInProps): StateTransformer.Params<StructureRepresentation3D> {
+    const typeProvider = (props.type && ctx.structureRepresentation.registry.get(props.type))
+        || ctx.structureRepresentation.registry.default.provider;
+    const colorProvider = (props.color && ctx.structureRepresentation.themeCtx.colorThemeRegistry.get(props.color)) 
+        || ctx.structureRepresentation.themeCtx.colorThemeRegistry.get(typeProvider.defaultColorTheme.name);
+    const sizeProvider = (props.size && ctx.structureRepresentation.themeCtx.sizeThemeRegistry.get(props.size))
+        || ctx.structureRepresentation.themeCtx.sizeThemeRegistry.get(typeProvider.defaultSizeTheme.name);
+
+    return createParamsProvider(ctx, structure, {
+        type: typeProvider,
+        typeParams: props.typeParams,
+        color: colorProvider,
+        colorParams: props.colorParams,
+        size: sizeProvider,
+        sizeParams: props.sizeParams
+    });
+}
+
+function createParamsProvider(ctx: PluginContext, structure: Structure, props: StructureRepresentationProps = {}): StateTransformer.Params<StructureRepresentation3D> {
+    const { themeCtx } = ctx.structureRepresentation
+    const themeDataCtx = { structure };
+    
+    const repr = props.type || ctx.structureRepresentation.registry.default.provider;
+    const reprDefaultParams = PD.getDefaultValues(repr.getParams(themeCtx, structure));
+    const reprParams = Object.assign(reprDefaultParams, props.typeParams);
+    
+    const color = props.color || themeCtx.colorThemeRegistry.get(repr.defaultColorTheme.name);
+    const colorDefaultParams = PD.getDefaultValues(color.getParams(themeDataCtx));
+    if (color.name === repr.defaultColorTheme.name) Object.assign(colorDefaultParams, repr.defaultColorTheme.props);
+    const colorParams = Object.assign(colorDefaultParams, props.colorParams);
+
+    const size = props.size || themeCtx.sizeThemeRegistry.get(repr.defaultSizeTheme.name);
+    const sizeDefaultParams = PD.getDefaultValues(size.getParams(themeDataCtx));
+    if (size.name === repr.defaultSizeTheme.name) Object.assign(sizeDefaultParams, repr.defaultSizeTheme.props);
+    const sizeParams = Object.assign(sizeDefaultParams, props.sizeParams);
+
+    return ({
+        type: { name: repr.name, params: reprParams },
+        colorTheme: { name: color.name, params: colorParams },
+        sizeTheme: { name: size.name, params: sizeParams }
+    });
+}

+ 38 - 29
src/mol-plugin-state/helpers/structure-selection-query.ts

@@ -8,10 +8,9 @@
 import { CustomProperty } from '../../mol-model-props/common/custom-property';
 import { AccessibleSurfaceAreaProvider, AccessibleSurfaceAreaSymbols } from '../../mol-model-props/computed/accessible-surface-area';
 import { ValidationReport, ValidationReportProvider } from '../../mol-model-props/rcsb/validation-report';
-import { QueryContext, Structure, StructureQuery, StructureSelection, StructureElement } from '../../mol-model/structure';
+import { QueryContext, Structure, StructureQuery, StructureSelection } from '../../mol-model/structure';
 import { BondType, NucleicBackboneAtoms, ProteinBackboneAtoms, SecondaryStructureType } from '../../mol-model/structure/model/types';
-import { PluginStateObject } from '../objects';
-import { StateTransforms } from '../transforms';
+import { PluginContext } from '../../mol-plugin/context';
 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 +18,8 @@ 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 '../../mol-plugin/context';
+import { PluginStateObject } from '../objects';
+import { StateTransforms } from '../transforms';
 
 export enum StructureSelectionCategory {
     Type = 'Type',
@@ -46,6 +46,7 @@ interface StructureSelectionQuery {
     readonly referencesCurrent: boolean
     readonly query: StructureQuery
     readonly ensureCustomProperties?: (ctx: CustomProperty.Context, structure: Structure) => Promise<void>
+    getSelection(plugin: PluginContext, runtime: RuntimeContext, structure: Structure): Promise<StructureSelection>
 }
 
 interface StructureSelectionQueryProps {
@@ -69,7 +70,16 @@ function StructureSelectionQuery(label: string, expression: Expression, props: S
             if (!_query) _query = compile<StructureSelection>(expression)
             return _query
         },
-        ensureCustomProperties: props.ensureCustomProperties
+        ensureCustomProperties: props.ensureCustomProperties,
+        async getSelection(plugin, runtime, structure) {
+            const current = plugin.managers.structure.selection.getStructure(structure)
+            const currentSelection = current ? StructureSelection.Singletons(structure, current) : StructureSelection.Empty(structure);
+            if (props.ensureCustomProperties) {
+                await props.ensureCustomProperties({ fetch: plugin.fetch, runtime }, structure)
+            }
+            if (!_query) _query = compile<StructureSelection>(expression)    
+            return _query(new QueryContext(structure, { currentSelection }));
+        }
     }
 }
 
@@ -510,33 +520,32 @@ export function applyBuiltInSelection(to: StateBuilder.To<PluginStateObject.Mole
         { tags: customTag ? [query, customTag] : [query] });
 }
 
-namespace StructureSelectionQuery {
-    export async function getStructure(plugin: PluginContext, runtime: RuntimeContext, selectionQuery: StructureSelectionQuery, structure: Structure) {
-        const current = plugin.managers.structure.selection.getStructure(structure)
-        const currentSelection = current ? StructureSelection.Singletons(structure, current) : StructureSelection.Empty(structure);
+// namespace StructureSelectionQuery {
+//     // export async function getStructure(plugin: PluginContext, runtime: RuntimeContext, selectionQuery: StructureSelectionQuery, structure: Structure) {
+//     //     const current = plugin.managers.structure.selection.getStructure(structure)
+//     //     const currentSelection = current ? StructureSelection.Singletons(structure, current) : StructureSelection.Empty(structure);
 
-        if (selectionQuery.ensureCustomProperties) {
-            await selectionQuery.ensureCustomProperties({ fetch: plugin.fetch, runtime }, structure)
-        }
+//     //     if (selectionQuery.ensureCustomProperties) {
+//     //         await selectionQuery.ensureCustomProperties({ fetch: plugin.fetch, runtime }, structure)
+//     //     }
 
-        const result = selectionQuery.query(new QueryContext(structure, { currentSelection }))
-        return StructureSelection.unionStructure(result)
-    }
+//     //     const result = selectionQuery.query(new QueryContext(structure, { currentSelection }))
+//     //     return StructureSelection.unionStructure(result)
+//     // }
 
-    export async function getLoci(plugin: PluginContext, runtime: RuntimeContext, selectionQuery: StructureSelectionQuery, structure: Structure) {
-        const current = plugin.managers.structure.selection.getStructure(structure)
-        const currentSelection = current ? StructureSelection.Singletons(structure, current) : StructureSelection.Empty(structure);
+//     // export async function getSelection(plugin: PluginContext, runtime: RuntimeContext, selectionQuery: StructureSelectionQuery, structure: Structure) {
+//     //     const current = plugin.managers.structure.selection.getStructure(structure)
+//     //     const currentSelection = current ? StructureSelection.Singletons(structure, current) : StructureSelection.Empty(structure);
 
-        if (selectionQuery.ensureCustomProperties) {
-            await selectionQuery.ensureCustomProperties({ fetch: plugin.fetch, runtime }, structure)
-        }
+//     //     if (selectionQuery.ensureCustomProperties) {
+//     //         await selectionQuery.ensureCustomProperties({ fetch: plugin.fetch, runtime }, structure)
+//     //     }
 
-        const result = selectionQuery.query(new QueryContext(structure, { currentSelection }))
-        return StructureSelection.toLociWithSourceUnits(result)
-    }
+//     //     return selectionQuery.query(new QueryContext(structure, { currentSelection }));
+//     // }
 
-    export async function getBundle(plugin: PluginContext, runtime: RuntimeContext, selectionQuery: StructureSelectionQuery, structure: Structure) {
-        const loci = await getLoci(plugin, runtime, selectionQuery, structure);
-        return StructureElement.Bundle.fromLoci(loci);
-    }
-}
+//     // export async function getBundle(plugin: PluginContext, runtime: RuntimeContext, selectionQuery: StructureSelectionQuery, structure: Structure) {
+//     //     const loci = await getLoci(plugin, runtime, selectionQuery, structure);
+//     //     return StructureElement.Bundle.fromLoci(loci);
+//     // }
+// }

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

@@ -31,8 +31,6 @@ interface StructureComponentManagerState {
     options: StructureComponentManager.Options
 }
 
-// TODO: add/update representation in all selected components
-
 class StructureComponentManager extends PluginComponent<StructureComponentManagerState> {
     readonly events = {
         optionsUpdated: this.ev<undefined>()
@@ -113,7 +111,7 @@ class StructureComponentManager extends PluginComponent<StructureComponentManage
         return this.plugin.dataTransaction(async () => {
             await this.clearComponents(structures);
             for (const s of structures) {
-                await this.plugin.builders.structure.representation.structurePreset(s.cell, provider, params);
+                await this.plugin.builders.structure.representation.applyPreset(s.cell, provider, params);
             }
         });
     }

+ 38 - 12
src/mol-plugin-state/manager/structure/hierarchy-state.ts

@@ -20,11 +20,13 @@ export function buildStructureHierarchy(state: State, previous?: StructureHierar
 
 export interface StructureHierarchy {
     trajectories: TrajectoryRef[],
+    models: ModelRef[],
+    structures: StructureRef[],
     refs: Map<StateTransform.Ref, HierarchyRef>
 }
 
 export function StructureHierarchy(): StructureHierarchy {
-    return { trajectories: [], refs: new Map() }
+    return { trajectories: [], models: [], structures: [], refs: new Map() }
 }
 
 interface RefBase<K extends string = string, T extends StateObject = StateObject> {
@@ -47,12 +49,13 @@ function TrajectoryRef(cell: StateObjectCell<SO.Molecule.Trajectory>): Trajector
 }
 
 export interface ModelRef extends RefBase<'model', SO.Molecule.Model> {
-    trajectory: TrajectoryRef,
+    trajectory?: TrajectoryRef,
     properties?: ModelPropertiesRef,
-    structures: StructureRef[]
+    structures: StructureRef[],
+    genericRepresentations?: GenericRepresentationRef[],
 }
 
-function ModelRef(cell: StateObjectCell<SO.Molecule.Model>, trajectory: TrajectoryRef): ModelRef {
+function ModelRef(cell: StateObjectCell<SO.Molecule.Model>, trajectory?: TrajectoryRef): ModelRef {
     return { kind: 'model', cell, version: cell.transform.version, trajectory, structures: [] };
 }
 
@@ -65,18 +68,18 @@ function ModelPropertiesRef(cell: StateObjectCell<SO.Molecule.Model>, model: Mod
 }
 
 export interface StructureRef extends RefBase<'structure', SO.Molecule.Structure> {
-    model: ModelRef,
+    model?: ModelRef,
     properties?: StructurePropertiesRef,
     components: StructureComponentRef[],
-    // representations: StructureRepresentationRef[],
     currentFocus?: {
         focus?: StructureComponentRef,
         surroundings?: StructureComponentRef,
     },
+    genericRepresentations?: GenericRepresentationRef[],
     // volumeStreaming?: ....
 }
 
-function StructureRef(cell: StateObjectCell<SO.Molecule.Structure>, model: ModelRef): StructureRef {
+function StructureRef(cell: StateObjectCell<SO.Molecule.Structure>, model?: ModelRef): StructureRef {
     return { kind: 'structure', cell, version: cell.transform.version, model, components: [] };
 }
 
@@ -92,6 +95,7 @@ export interface StructureComponentRef extends RefBase<'structure-component', SO
     structure: StructureRef,
     key?: string,
     representations: StructureRepresentationRef[],
+    genericRepresentations?: GenericRepresentationRef[]
 }
 
 function componentKey(cell: StateObjectCell<SO.Molecule.Structure>) {
@@ -111,6 +115,14 @@ function StructureRepresentationRef(cell: StateObjectCell<SO.Molecule.Structure.
     return { kind: 'structure-representation', cell, version: cell.transform.version, component };
 }
 
+export interface GenericRepresentationRef extends RefBase<'generic-representation', SO.Any> {
+    parent: HierarchyRef
+}
+
+function GenericRepresentationRef(cell: StateObjectCell<SO.Molecule.Structure.Representation3D>, parent: HierarchyRef): GenericRepresentationRef {
+    return { kind: 'generic-representation', cell, version: cell.transform.version, parent };
+}
+
 interface BuildState {
     state: State,
     oldHierarchy: StructureHierarchy,
@@ -160,16 +172,24 @@ const tagMap: [string, (state: BuildState, cell: StateObjectCell) => boolean | v
         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);
+        if (state.currentTrajectory) {
+            state.currentModel = createOrUpdateRefList(state, cell, state.currentTrajectory.models, ModelRef, cell, state.currentTrajectory);
+        } else {
+            state.currentModel = ModelRef(cell)
+        }
+        state.hierarchy.models.push(state.currentModel);
     }, 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);
+        if (state.currentModel) {
+            state.currentStructure = createOrUpdateRefList(state, cell, state.currentModel.structures, StructureRef, cell, state.currentModel);
+        } else {
+            state.currentStructure = StructureRef(cell);
+        }
+        state.hierarchy.structures.push(state.currentStructure);
     }, state => state.currentStructure = void 0],
     [StructureBuilderTags.StructureProperties, (state, cell) => {
         if (!state.currentStructure) return false;
@@ -230,8 +250,14 @@ function _doPreOrder(ctx: VisitorCtx, root: StateTransform) {
         }
     }
 
-    if (!onLeave && state.currentComponent && SO.Molecule.Structure.Representation3D.is(cell.obj)) {
+    if (!onLeave && !cell.state.isGhost && state.currentComponent && SO.Molecule.Structure.Representation3D.is(cell.obj)) {
         createOrUpdateRefList(state, cell, state.currentComponent.representations, StructureRepresentationRef, cell, state.currentComponent);
+    } else if (!cell.state.isGhost && SO.isRepresentation3D(cell.obj)) {
+        const genericTarget = state.currentComponent || state.currentModel || state.currentStructure;
+        if (genericTarget) {
+            if (!genericTarget.genericRepresentations) genericTarget.genericRepresentations = [];
+            genericTarget.genericRepresentations.push(GenericRepresentationRef(cell, genericTarget));
+        }
     }
 
     const children = ctx.tree.children.get(root.ref);

+ 16 - 40
src/mol-plugin-state/manager/structure/hierarchy.ts

@@ -7,7 +7,6 @@
 import { PluginContext } from '../../../mol-plugin/context';
 import { StructureHierarchy, buildStructureHierarchy, ModelRef, StructureComponentRef, StructureRef, HierarchyRef, TrajectoryRef } from './hierarchy-state';
 import { PluginComponent } from '../../component';
-import { StateTransform } from '../../../mol-state';
 import { SetUtils } from '../../../mol-util/set';
 
 interface StructureHierarchyManagerState {
@@ -51,45 +50,20 @@ export class StructureHierarchyManager extends PluginComponent<StructureHierarch
         return this._currentSelectionSet;
     }
 
-    private syncCurrentTrajectories(hierarchy: StructureHierarchy, map: Map<StateTransform.Ref, HierarchyRef>): TrajectoryRef[] {
-        const current = this.state.current.trajectories;
-        if (current.length === 0) return hierarchy.trajectories.length > 0 ? [hierarchy.trajectories[0]] : [];
-
-        const newCurrent: TrajectoryRef[] = [];
-        for (const c of current) {
-            const ref = map.get(c.cell.transform.ref) as TrajectoryRef;
-            if (ref) newCurrent.push(ref);
-        }
-
-        if (newCurrent.length === 0) return hierarchy.trajectories.length > 0 ? [hierarchy.trajectories[0]] : [];
-        return newCurrent;
-    }
-
-    private syncCurrentModels(hierarchy: StructureHierarchy, map: Map<StateTransform.Ref, HierarchyRef>, currentTrajectories: TrajectoryRef[]): ModelRef[] {
-        const current = this.state.current.models;
-        if (current.length === 0) return currentTrajectories[0]?.models || [];
-
-        const newCurrent: ModelRef[] = [];
-        for (const c of current) {
-            const ref = map.get(c.cell.transform.ref) as ModelRef;
-            if (ref) newCurrent.push(ref);
-        }
-
-        if (newCurrent.length === 0) return currentTrajectories[0]?.models || [];
-        return newCurrent;
+    get current() {
+        return this.state.current;
     }
 
-    private syncCurrentStructures(map: Map<StateTransform.Ref, HierarchyRef>, currentModels: ModelRef[]): StructureRef[] {
-        const current = this.state.current.structures;
-        if (current.length === 0) return Array.prototype.concat.apply([], currentModels.map(m => m.structures));
+    private syncCurrent<T extends HierarchyRef>(hierarchy: StructureHierarchy, current: ReadonlyArray<T>, all: ReadonlyArray<T>): T[] {
+        if (current.length === 0) return all.length > 0 ? [all[0]] : [];
 
-        const newCurrent: StructureRef[] = [];
+        const newCurrent: T[] = [];
         for (const c of current) {
-            const ref = map.get(c.cell.transform.ref) as StructureRef;
+            const ref = hierarchy.refs.get(c.cell.transform.ref) as T;
             if (ref) newCurrent.push(ref);
         }
 
-        if (newCurrent.length === 0 && currentModels.length > 0) return Array.prototype.concat.apply([], currentModels.map(m => m.structures));
+        if (newCurrent.length === 0) return all.length > 0 ? [all[0]] : [];
         return newCurrent;
     }
 
@@ -98,15 +72,17 @@ export class StructureHierarchyManager extends PluginComponent<StructureHierarch
         if (update.added.length === 0 && update.updated.length === 0 && update.removed.length === 0) {
             return;
         }
+
         this._currentComponentGroups = void 0;
         this._currentSelectionSet = void 0;
 
-        const trajectories = this.syncCurrentTrajectories(update.hierarchy, update.hierarchy.refs);
-        const models = this.syncCurrentModels(update.hierarchy, update.hierarchy.refs, trajectories);
-        const structures = this.syncCurrentStructures(update.hierarchy.refs, models);
+        const { hierarchy } = update;
+        const trajectories = this.syncCurrent(hierarchy, this.state.current.trajectories, hierarchy.trajectories);
+        const models = this.syncCurrent(hierarchy, this.state.current.models, hierarchy.models);
+        const structures = this.syncCurrent(hierarchy, this.state.current.structures, hierarchy.structures);
 
-        this.updateState({ hierarchy: update.hierarchy, current: { trajectories, models, structures }});
-        this.behaviors.current.next({ hierarchy: update.hierarchy, trajectories, models, structures });
+        this.updateState({ hierarchy, current: { trajectories, models, structures }});
+        this.behaviors.current.next({ hierarchy, trajectories, models, structures });
     }
 
     updateCurrent(refs: HierarchyRef[], action: 'add' | 'remove') {
@@ -151,9 +127,9 @@ export class StructureHierarchyManager extends PluginComponent<StructureHierarch
 
             const tr = trajectory.cell.obj?.data!;
             for (let i = 0; i < tr.length; i++) {
-                const model = await this.plugin.builders.structure.createModel(trajectory.cell, { modelIndex: i });
+                const model = await this.plugin.builders.structure.createModel(trajectory.cell, { modelIndex: i }, { isCollapsed: true });
                 const structure = await this.plugin.builders.structure.createStructure(model, { name: 'deposited', params: { } });
-                await this.plugin.builders.structure.representation.structurePreset(structure, 'auto', { globalThemeName: 'model-index' });
+                await this.plugin.builders.structure.representation.applyPreset(structure, 'auto', { globalThemeName: 'model-index' });
             }
         })
     }

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

@@ -10,7 +10,7 @@ import { BoundaryHelper } from '../../../mol-math/geometry/boundary-helper';
 import { Vec3 } from '../../../mol-math/linear-algebra';
 import { PrincipalAxes } from '../../../mol-math/linear-algebra/matrix/principal-axes';
 import { EmptyLoci, Loci } from '../../../mol-model/loci';
-import { Structure, StructureElement } from '../../../mol-model/structure';
+import { Structure, StructureElement, StructureSelection } from '../../../mol-model/structure';
 import { Boundary } from '../../../mol-model/structure/structure/util/boundary';
 import { PluginContext } from '../../../mol-plugin/context';
 import { StateObject } from '../../../mol-state';
@@ -380,11 +380,11 @@ export class StructureSelectionManager extends PluginComponent<StructureSelectio
         this.triggerInteraction(modifier, loci, applyGranularity);
     }
 
-    fromSelectionQuery(modifier: StructureSelectionModifier, selectionQuery: StructureSelectionQuery, applyGranularity = true) {
+    fromSelectionQuery(modifier: StructureSelectionModifier, query: StructureSelectionQuery, applyGranularity = true) {
         this.plugin.runTask(Task.create('Structure Selection', async runtime => {
             for (const s of this.applicableStructures) {
-                const loci = await StructureSelectionQuery.getLoci(this.plugin, runtime, selectionQuery, s);
-                this.triggerInteraction(modifier, loci, applyGranularity);
+                const loci = await query.getSelection(this.plugin, runtime, s);
+                this.triggerInteraction(modifier, StructureSelection.toLociWithSourceUnits(loci), applyGranularity);
             }
         }))
     }

+ 4 - 3
src/mol-plugin-state/transforms/model.ts

@@ -245,10 +245,11 @@ const ModelFromTrajectory = PluginStateTransform.BuiltIn({
 })({
     isApplicable: a => a.data.length > 0,
     apply({ a, params }) {
-        if (params.modelIndex < 0 || params.modelIndex >= a.data.length) throw new Error(`Invalid modelIndex ${params.modelIndex}`);
+        let modelIndex = params.modelIndex % a.data.length;
+        if (modelIndex < 0) modelIndex += a.data.length;
         const model = a.data[params.modelIndex];
-        const label = `Model ${model.modelNum}`
-        const description = a.data.length === 1 ? undefined : `Model ${params.modelIndex + 1} of ${a.data.length}`
+        const label = `Model ${model.modelNum}`;
+        const description = a.data.length === 1 ? undefined : `of ${a.data.length}`;
         return new SO.Molecule.Model(model, { label, description });
     }
 });

+ 2 - 100
src/mol-plugin-state/transforms/representation.ts

@@ -8,15 +8,12 @@
 import { Structure, StructureElement } from '../../mol-model/structure';
 import { VolumeData, VolumeIsoValue } from '../../mol-model/volume';
 import { PluginContext } from '../../mol-plugin/context';
-import { RepresentationProvider } from '../../mol-repr/representation';
-import { BuiltInStructureRepresentationsName, BuiltInStructureRepresentations } from '../../mol-repr/structure/registry';
-import { StructureParams } from '../../mol-repr/structure/representation';
 import { BuiltInVolumeRepresentationsName } from '../../mol-repr/volume/registry';
 import { VolumeParams } from '../../mol-repr/volume/representation';
 import { StateTransformer, StateObject } from '../../mol-state';
 import { Task } from '../../mol-task';
-import { BuiltInColorThemeName, BuiltInColorThemes, ColorTheme } from '../../mol-theme/color';
-import { BuiltInSizeThemeName, BuiltInSizeThemes, SizeTheme } from '../../mol-theme/size';
+import { BuiltInColorThemeName, ColorTheme } from '../../mol-theme/color';
+import { BuiltInSizeThemeName, SizeTheme } from '../../mol-theme/size';
 import { Theme } from '../../mol-theme/theme';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { PluginStateObject as SO, PluginStateTransform } from '../objects';
@@ -39,7 +36,6 @@ import { DihedralParams, DihedralRepresentation } from '../../mol-repr/shape/loc
 import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry';
 
 export { StructureRepresentation3D }
-export { StructureRepresentation3DHelpers }
 export { ExplodeStructureRepresentation3D }
 export { UnwindStructureAssemblyRepresentation3D }
 export { OverpaintStructureRepresentation3DFromScript }
@@ -48,100 +44,6 @@ export { TransparencyStructureRepresentation3DFromScript }
 export { TransparencyStructureRepresentation3DFromBundle }
 export { VolumeRepresentation3D }
 
-namespace StructureRepresentation3DHelpers {
-    export type BuildInProps<R extends BuiltInStructureRepresentationsName, C extends BuiltInColorThemeName, S extends BuiltInSizeThemeName> = {
-        type?: R,
-        typeParams?: Partial<RepresentationProvider.ParamValues<BuiltInStructureRepresentations[R]>>,
-        color?: C,
-        colorParams?: Partial<ColorTheme.ParamValues<BuiltInColorThemes[C]>>,
-        size?: S,
-        sizeParams?: Partial<SizeTheme.ParamValues<BuiltInSizeThemes[S]>>
-    }
-
-    export type Props<R extends RepresentationProvider<Structure, any, any> = any, C extends ColorTheme.Provider<any> = any, S extends SizeTheme.Provider<any> = any> = {
-        type?: R,
-        typeParams?: Partial<RepresentationProvider.ParamValues<R>>,
-        color?: C,
-        colorParams?: Partial<ColorTheme.ParamValues<C>>,
-        size?: S,
-        sizeParams?: Partial<SizeTheme.ParamValues<S>>
-    }
-
-    export function createBuiltInParams<R extends BuiltInStructureRepresentationsName, C extends BuiltInColorThemeName, S extends BuiltInSizeThemeName>(
-        ctx: PluginContext, structure: Structure, props: BuildInProps<R, C, S> = {}
-    ) {
-        const type = (props.type && ctx.structureRepresentation.registry.get(props.type))
-            || ctx.structureRepresentation.registry.default.provider;
-        const color = (props.color && ctx.structureRepresentation.themeCtx.colorThemeRegistry.get(props.color)) 
-            || ctx.structureRepresentation.themeCtx.colorThemeRegistry.get(type.defaultColorTheme.name);
-        const size = (props.size && ctx.structureRepresentation.themeCtx.sizeThemeRegistry.get(props.size))
-            || ctx.structureRepresentation.themeCtx.sizeThemeRegistry.get(type.defaultSizeTheme.name);
-
-        const ps: Props = {
-            type: type,
-            typeParams: props.typeParams,
-            color,
-            colorParams: props.colorParams,
-            size,
-            sizeParams: props.sizeParams
-        };
-
-        return createParams(ctx, structure, ps);
-    }
-
-    export function createParams<R extends RepresentationProvider<Structure, any, any>, C extends ColorTheme.Provider<any>, S extends SizeTheme.Provider<any>>(
-        ctx: PluginContext, structure: Structure, props: Props<R, C, S> = {}): StateTransformer.Params<StructureRepresentation3D> {
-
-        const { themeCtx } = ctx.structureRepresentation
-        const themeDataCtx = { structure }
-
-        const repr = props.type || ctx.structureRepresentation.registry.default.provider;
-        const reprDefaultParams = PD.getDefaultValues(repr.getParams(themeCtx, structure));
-        const reprParams = { ...reprDefaultParams, ...props.typeParams };
-
-        const color = props.color || themeCtx.colorThemeRegistry.get(repr.defaultColorTheme.name);
-        const colorDefaultParams = { ...PD.getDefaultValues(color.getParams(themeDataCtx)), ...repr.defaultColorTheme.props }
-        const colorParams = { ...colorDefaultParams, ...props.colorParams };
-
-        const size = props.size || themeCtx.sizeThemeRegistry.get(repr.defaultSizeTheme.name);
-        const sizeDefaultParams = { ...PD.getDefaultValues(size.getParams(themeDataCtx)), ...repr.defaultSizeTheme.props }
-        const sizeParams = { ...sizeDefaultParams, ...props.sizeParams };
-
-        return ({
-            type: { name: ctx.structureRepresentation.registry.getName(repr), params: reprParams },
-            colorTheme: { name: themeCtx.colorThemeRegistry.getName(color), params: colorParams },
-            sizeTheme: { name: themeCtx.sizeThemeRegistry.getName(size), params: sizeParams }
-        })
-    }
-
-    export function getDefaultParamsWithTheme(ctx: PluginContext, reprName: BuiltInStructureRepresentationsName, colorName: BuiltInColorThemeName | undefined, structure: Structure, structureParams?: Partial<PD.Values<StructureParams>>): StateTransformer.Params<StructureRepresentation3D> {
-        const type = ctx.structureRepresentation.registry.get(reprName);
-
-        const themeDataCtx = { structure };
-        const color = colorName || type.defaultColorTheme.name;
-        const colorParams = ctx.structureRepresentation.themeCtx.colorThemeRegistry.get(color).getParams(themeDataCtx);
-        const sizeParams = ctx.structureRepresentation.themeCtx.sizeThemeRegistry.get(type.defaultSizeTheme.name).getParams(themeDataCtx)
-        const structureDefaultParams = PD.getDefaultValues(type.getParams(ctx.structureRepresentation.themeCtx, structure))
-        return ({
-            type: { name: reprName, params: structureParams ? { ...structureDefaultParams, ...structureParams } : structureDefaultParams },
-            colorTheme: { name: color, params: PD.getDefaultValues(colorParams) },
-            sizeTheme: { name: type.defaultSizeTheme.name, params: PD.getDefaultValues(sizeParams) }
-        })
-    }
-
-    export function getDefaultParamsStatic(ctx: PluginContext, name: BuiltInStructureRepresentationsName, structureParams?: Partial<PD.Values<StructureParams>>, colorName?: BuiltInColorThemeName): StateTransformer.Params<StructureRepresentation3D> {
-        const type = ctx.structureRepresentation.registry.get(name);
-        const color = colorName || type.defaultColorTheme.name;
-        const colorParams = ctx.structureRepresentation.themeCtx.colorThemeRegistry.get(color).defaultValues;
-        const sizeParams = ctx.structureRepresentation.themeCtx.sizeThemeRegistry.get(type.defaultSizeTheme.name).defaultValues
-        return ({
-            type: { name, params: structureParams ? { ...type.defaultValues, ...structureParams } : type.defaultValues },
-            colorTheme: { name: color, params: colorParams },
-            sizeTheme: { name: type.defaultSizeTheme.name, params: sizeParams }
-        })
-    }
-}
-
 type StructureRepresentation3D = typeof StructureRepresentation3D
 const StructureRepresentation3D = PluginStateTransform.BuiltIn({
     name: 'structure-representation-3d',

+ 2 - 2
src/mol-plugin-ui/controls/common.tsx

@@ -332,14 +332,14 @@ export class ToggleButton extends React.PureComponent<ToggleButtonProps> {
     }
 }
 
-export class ExpandGroup extends React.PureComponent<{ header: string, initiallyExpanded?: boolean, noOffset?: boolean }, { isExpanded: boolean }> {
+export class ExpandGroup extends React.PureComponent<{ header: string, initiallyExpanded?: boolean, noOffset?: boolean, marginTop?: 0 | string }, { isExpanded: boolean }> {
     state = { isExpanded: !!this.props.initiallyExpanded };
 
     toggleExpanded = () => this.setState({ isExpanded: !this.state.isExpanded });
 
     render() {
         return <>
-            <div className='msp-control-group-header' style={{ marginTop: '1px' }}>
+            <div className='msp-control-group-header' style={{ marginTop: this.props.marginTop !== void 0 ? this.props.marginTop : '1px' }}>
                 <button className='msp-btn msp-btn-block' onClick={this.toggleExpanded}>
                     <Icon name={this.state.isExpanded ? 'collapse' : 'expand'} />
                     {this.props.header}

+ 29 - 7
src/mol-plugin-ui/structure/components.tsx

@@ -10,7 +10,7 @@ import { StructureComponentRef, StructureRepresentationRef, StructureRef } from
 import { PluginCommands } from '../../mol-plugin/commands';
 import { State } from '../../mol-state';
 import { ParamDefinition } from '../../mol-util/param-definition';
-import { CollapsableControls, CollapsableState, PurePluginUIComponent } from '../base';
+import { CollapsableControls, CollapsableState, PurePluginUIComponent, PluginUIComponent } from '../base';
 import { ActionMenu } from '../controls/action-menu';
 import { ExpandGroup, IconButton, ToggleButton } from '../controls/common';
 import { Icon } from '../controls/icons';
@@ -84,8 +84,9 @@ class ComponentEditorControls extends PurePluginUIComponent<{}, ComponentEditorC
         const actions = [
             ActionMenu.Item('Clear', null),
         ];
-        // TODO: filter by applicable??
-        for (const p of this.plugin.builders.structure.representation.providerList) {
+        const pivot = this.plugin.managers.structure.component.pivotStructure;
+        const providers = this.plugin.builders.structure.representation.getPresets(pivot?.cell.obj?.data)
+        for (const p of providers) {
             actions.push(ActionMenu.Item(p.display.name, p));
         }
         return actions;
@@ -183,12 +184,33 @@ class ComponentListControls extends PurePluginUIComponent {
 
     render() {
         const componentGroups = this.plugin.managers.structure.hierarchy.currentComponentGroups;
-        return <div>
+        if (componentGroups.length === 0) return null;
+
+        return <div style={{ marginTop: '6px' }}>
             {componentGroups.map(g => <StructureComponentGroup key={g[0].cell.transform.ref} group={g} />)}
+            <CurrentFocus />
         </div>;
     }
 }
 
+class CurrentFocus extends PluginUIComponent {
+    findInteraction() {
+        const xs = this.plugin.managers.structure.hierarchy.current.structures;
+        for (const s of xs) {
+            if (s.currentFocus?.focus || s.currentFocus?.surroundings) return s.currentFocus;
+        }
+    }
+
+    render() {
+        const interaction = this.findInteraction();
+        if (!interaction) return null;
+        return <ExpandGroup header='Current Focus' marginTop={0} noOffset>
+            {interaction.focus && <StructureComponentGroup group={[interaction.focus]} />}
+            {interaction.surroundings && <StructureComponentGroup group={[interaction.surroundings]} />}
+        </ExpandGroup>;
+    }
+}
+
 type StructureComponentEntryActions = 'action' | 'remove'
 
 class StructureComponentGroup extends PurePluginUIComponent<{ group: StructureComponentRef[] }, { action?: StructureComponentEntryActions }> {
@@ -283,8 +305,8 @@ class StructureComponentGroup extends PurePluginUIComponent<{ group: StructureCo
         const component = this.pivot;
         const cell = component.cell;
         const label = cell.obj?.label;
-        return <>
-            <div className='msp-control-row' style={{ marginTop: '6px' }}>
+        return <div style={{ marginBottom: '6px' }}>
+            <div className='msp-control-row'>
                 <button className='msp-control-button-label' title={`${label}. Click to focus.`} onClick={this.focus} onMouseEnter={this.highlight} onMouseLeave={this.clearHighlight} style={{ textAlign: 'left' }}>
                     {label}
                 </button>
@@ -299,7 +321,7 @@ class StructureComponentGroup extends PurePluginUIComponent<{ group: StructureCo
             <div className='msp-control-offset'>
                 {component.representations.map(r => <StructureRepresentationEntry group={this.props.group} key={r.cell.transform.ref} representation={r} />)}
             </div>
-        </>;
+        </div>;
     }
 }
 

+ 3 - 2
src/mol-plugin-ui/structure/source.tsx

@@ -62,8 +62,9 @@ export class StructureSourceControls extends CollapsableControls<{}, StructureSo
         
         if (structures.length === 1) {
             const s = structures[0];
-            if (s.model.trajectory.models.length === 1) return s.cell.obj?.data.label;
-            return `${s.model.cell.obj?.label} | ${s.cell.obj?.data.label}`;
+            if (s.model?.trajectory?.models && s.model.trajectory.models.length === 1) return s.cell.obj?.data.label;
+            if (s.model) return `${s.model.cell.obj?.label} | ${s.cell.obj?.data.label}`;
+            return s.cell.obj?.data.label;
         }
 
         if (structures.length > 1) {

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

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

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

@@ -99,16 +99,16 @@ 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.structureRepresentation.themeCtx.colorThemeRegistry.add(InteractionTypeColorThemeProvider)
             this.ctx.managers.lociLabels.addProvider(this.label);
-            this.ctx.structureRepresentation.registry.add('interactions', InteractionsRepresentationProvider)
+            this.ctx.structureRepresentation.registry.add(InteractionsRepresentationProvider)
         }
 
         unregister() {
             this.ctx.customStructureProperties.unregister(this.provider.descriptor.name);
-            this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.remove('interaction-type')
+            this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.remove(InteractionTypeColorThemeProvider)
             this.ctx.managers.lociLabels.removeProvider(this.label);
-            this.ctx.structureRepresentation.registry.remove('interactions')
+            this.ctx.structureRepresentation.registry.remove(InteractionsRepresentationProvider)
         }
     },
     params: () => ({

+ 4 - 6
src/mol-plugin/behavior/dynamic/custom-props/integrative/cross-link-restraint.ts

@@ -12,8 +12,6 @@ import { CrossLinkRestraintRepresentationProvider } from '../../../../../mol-mod
 import { CrossLinkColorThemeProvider } from '../../../../../mol-model-props/integrative/cross-link-restraint/color';
 import { CrossLinkRestraint as _CrossLinkRestraint } from '../../../../../mol-model-props/integrative/cross-link-restraint/property';
 
-const Tag = _CrossLinkRestraint.Tag
-
 export const CrossLinkRestraint = PluginBehavior.create<{ }>({
     name: 'integrative-cross-link-restraint',
     category: 'custom-props',
@@ -24,15 +22,15 @@ export const CrossLinkRestraint = PluginBehavior.create<{ }>({
         register(): void {
             this.provider.formatRegistry.add('mmCIF', crossLinkRestraintFromMmcif)
 
-            this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.add(Tag.CrossLinkRestraint, CrossLinkColorThemeProvider)
-            this.ctx.structureRepresentation.registry.add(Tag.CrossLinkRestraint, CrossLinkRestraintRepresentationProvider)
+            this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.add(CrossLinkColorThemeProvider)
+            this.ctx.structureRepresentation.registry.add(CrossLinkRestraintRepresentationProvider)
         }
 
         unregister() {
             this.provider.formatRegistry.remove('mmCIF')
 
-            this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.remove(Tag.CrossLinkRestraint)
-            this.ctx.structureRepresentation.registry.remove(Tag.CrossLinkRestraint)
+            this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.remove(CrossLinkColorThemeProvider)
+            this.ctx.structureRepresentation.registry.remove(CrossLinkRestraintRepresentationProvider)
         }
     }
 });

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

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

+ 2 - 2
src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts

@@ -29,7 +29,7 @@ export const RCSBAssemblySymmetry = PluginBehavior.create<{ autoAttach: boolean
         register(): void {
             this.ctx.state.dataState.actions.add(InitAssemblySymmetry3D)
             this.ctx.customStructureProperties.register(this.provider, this.params.autoAttach);
-            this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.add(Tag.Cluster, AssemblySymmetryClusterColorThemeProvider)
+            this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.add(AssemblySymmetryClusterColorThemeProvider)
         }
 
         update(p: { autoAttach: boolean }) {
@@ -42,7 +42,7 @@ export const RCSBAssemblySymmetry = PluginBehavior.create<{ autoAttach: boolean
         unregister() {
             this.ctx.state.dataState.actions.remove(InitAssemblySymmetry3D)
             this.ctx.customStructureProperties.unregister(this.provider.descriptor.name);
-            this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.remove(Tag.Cluster)
+            this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.remove(AssemblySymmetryClusterColorThemeProvider)
         }
     },
     params: () => ({

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

@@ -16,8 +16,6 @@ import { DensityFitColorThemeProvider } from '../../../../../mol-model-props/rcs
 import { cantorPairing } from '../../../../../mol-data/util';
 import { DefaultQueryRuntimeTable } from '../../../../../mol-script/runtime/query/compiler';
 
-const Tag = ValidationReport.Tag
-
 export const RCSBValidationReport = PluginBehavior.create<{ autoAttach: boolean, showTooltip: boolean }>({
     name: 'rcsb-validation-report-prop',
     category: 'custom-props',
@@ -44,11 +42,11 @@ export const RCSBValidationReport = PluginBehavior.create<{ autoAttach: boolean,
 
             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)
-            this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.add(Tag.RandomCoilIndex, RandomCoilIndexColorThemeProvider)
+            this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.add(DensityFitColorThemeProvider)
+            this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.add(GeometryQualityColorThemeProvider)
+            this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.add(RandomCoilIndexColorThemeProvider)
 
-            this.ctx.structureRepresentation.registry.add(Tag.Clashes, ClashesRepresentationProvider)
+            this.ctx.structureRepresentation.registry.add(ClashesRepresentationProvider)
         }
 
         update(p: { autoAttach: boolean, showTooltip: boolean }) {
@@ -67,11 +65,11 @@ export const RCSBValidationReport = PluginBehavior.create<{ autoAttach: boolean,
 
             this.ctx.managers.lociLabels.removeProvider(this.label);
 
-            this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.remove(Tag.DensityFit)
-            this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.remove(Tag.GeometryQuality)
-            this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.remove(Tag.RandomCoilIndex)
+            this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.remove(DensityFitColorThemeProvider)
+            this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.remove(GeometryQualityColorThemeProvider)
+            this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.remove(RandomCoilIndexColorThemeProvider)
 
-            this.ctx.structureRepresentation.registry.remove(Tag.Clashes)
+            this.ctx.structureRepresentation.registry.remove(ClashesRepresentationProvider)
         }
     },
     params: () => ({

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

@@ -5,23 +5,22 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Structure, StructureElement, Bond } from '../../../../mol-model/structure';
-import { PluginBehavior } from '../../../../mol-plugin/behavior';
-import { PluginCommands } from '../../../commands';
+import { InteractionsRepresentationProvider } from '../../../../mol-model-props/computed/representations/interactions';
+import { InteractionTypeColorThemeProvider } from '../../../../mol-model-props/computed/themes/interaction-type';
+import { EmptyLoci, isEmptyLoci, Loci } from '../../../../mol-model/loci';
+import { Bond, Structure, StructureElement } from '../../../../mol-model/structure';
+import { createStructureRepresentationParams } from '../../../../mol-plugin-state/helpers/structure-representation-params';
 import { PluginStateObject } from '../../../../mol-plugin-state/objects';
 import { StateTransforms } from '../../../../mol-plugin-state/transforms';
-import { StructureRepresentation3DHelpers } from '../../../../mol-plugin-state/transforms/representation';
-import { BuiltInStructureRepresentations } from '../../../../mol-repr/structure/registry';
+import { PluginBehavior } from '../../../../mol-plugin/behavior';
 import { MolScriptBuilder as MS } from '../../../../mol-script/language/builder';
 import { StateObjectCell, StateSelection, StateTransform } from '../../../../mol-state';
-import { BuiltInColorThemes } from '../../../../mol-theme/color';
 import { BuiltInSizeThemes } from '../../../../mol-theme/size';
-import { ButtonsType, ModifiersKeys } from '../../../../mol-util/input/input-observer';
 import { Binding } from '../../../../mol-util/binding';
+import { ButtonsType, ModifiersKeys } from '../../../../mol-util/input/input-observer';
 import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
-import { isEmptyLoci, Loci, EmptyLoci } from '../../../../mol-model/loci';
-import { InteractionsRepresentationProvider } from '../../../../mol-model-props/computed/representations/interactions';
-import { InteractionTypeColorThemeProvider } from '../../../../mol-model-props/computed/themes/interaction-type';
+import { PluginCommands } from '../../../commands';
+import { PluginContext } from '../../../context';
 
 const B = ButtonsType
 const M = ModifiersKeys
@@ -30,10 +29,38 @@ const Trigger = Binding.Trigger
 const DefaultStructureRepresentationInteractionBindings = {
     clickInteractionAroundOnly: Binding([Trigger(B.Flag.Secondary, M.create()), Trigger(B.Flag.Primary, M.create({ control: true }))], 'Show the structure interaction around only the clicked element using ${triggers}.'),
 }
-const StructureRepresentationInteractionParams = {
-    bindings: PD.Value(DefaultStructureRepresentationInteractionBindings, { isHidden: true }),
+// const StructureRepresentationInteractionParams = {
+//     bindings: PD.Value(DefaultStructureRepresentationInteractionBindings, { isHidden: true }),
+// }
+
+const StructureRepresentationInteractionParams = (plugin: PluginContext) => {
+    
+    const reprParams = StateTransforms.Representation.StructureRepresentation3D.definition.params!(void 0, plugin) as PD.Params;
+    return {
+        bindings: PD.Value(DefaultStructureRepresentationInteractionBindings, { isHidden: true }),
+        focusParams: PD.Group(reprParams, {
+            label: 'Focus',
+            customDefault: createStructureRepresentationParams(plugin, void 0, { type: 'ball-and-stick', size: 'uniform' })
+        }),
+        surroundingsParams: PD.Group(reprParams, {
+            label: 'Surroundings',
+            customDefault: createStructureRepresentationParams(plugin, void 0, { type: 'ball-and-stick', color: 'element-symbol', size: 'uniform' })
+        }),
+        nciParams: PD.Group(reprParams, {
+            label: 'Non-covalent Int.',
+            // customDefault: createStructureRepresentationParams(plugin, void 0, { type: 'ball-and-stick', color: 'element-symbol', size: 'uniform' })
+            customDefault: createStructureRepresentationParams(plugin, void 0, {
+                type: InteractionsRepresentationProvider,
+                color: InteractionTypeColorThemeProvider,
+                size: BuiltInSizeThemes.uniform
+            })
+        })
+    };
 }
-type StructureRepresentationInteractionProps = PD.Values<typeof StructureRepresentationInteractionParams>
+
+type StructureRepresentationInteractionProps = PD.ValuesFor<ReturnType<typeof StructureRepresentationInteractionParams>>
+
+//PD.Values<typeof StructureRepresentationInteractionParams>
 
 export enum StructureRepresentationInteractionTags {
     Group = 'structure-interaction-group',
@@ -47,65 +74,49 @@ export enum StructureRepresentationInteractionTags {
 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> {
-    private createResVisualParams(s: Structure) {
-        return StructureRepresentation3DHelpers.createParams(this.plugin, s, {
-            type: BuiltInStructureRepresentations['ball-and-stick'],
-            size: BuiltInSizeThemes.uniform
-        });
-    }
-
-    private createSurVisualParams(s: Structure) {
-        return StructureRepresentation3DHelpers.createParams(this.plugin, s, {
-            type: BuiltInStructureRepresentations['ball-and-stick'],
-            color: BuiltInColorThemes['element-symbol'],
-            size: BuiltInSizeThemes.uniform
-        });
-    }
-
-    private createSurNciVisualParams(s: Structure) {
-        return StructureRepresentation3DHelpers.createParams(this.plugin, s, {
-            type: InteractionsRepresentationProvider,
-            color: InteractionTypeColorThemeProvider,
-            size: BuiltInSizeThemes.uniform
-        });
-    }
-
     private ensureShape(cell: StateObjectCell<PluginStateObject.Molecule.Structure>) {
         const state = this.plugin.state.dataState, tree = state.tree;
         const builder = state.build();
         const refs = StateSelection.findUniqueTagsInSubtree(tree, cell.transform.ref, TagSet);
 
         if (!refs['structure-interaction-group']) {
-            refs['structure-interaction-group'] = builder.to(cell).group(StateTransforms.Misc.CreateGroup,
-                { label: 'Current Focus' }, { tags: StructureRepresentationInteractionTags.Group }).ref;
+            refs['structure-interaction-group'] = builder
+                .to(cell)
+                .group(StateTransforms.Misc.CreateGroup, { label: 'Current Focus' }, { tags: StructureRepresentationInteractionTags.Group }).ref;
         }
 
         // Selections
         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;
+            refs[StructureRepresentationInteractionTags.ResidueSel] = builder
+                .to(refs['structure-interaction-group'])
+                .apply(StateTransforms.Model.StructureSelectionFromBundle,
+                    { bundle: { } as any, label: 'Focus' }, { tags: StructureRepresentationInteractionTags.ResidueSel }).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;
+            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[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;
+            refs[StructureRepresentationInteractionTags.ResidueRepr] = builder
+                .to(refs['structure-interaction-residue-sel']!)
+                .apply(StateTransforms.Representation.StructureRepresentation3D, this.params.focusParams, { tags: StructureRepresentationInteractionTags.ResidueRepr }).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;
+            refs[StructureRepresentationInteractionTags.SurrRepr] = builder
+                .to(refs['structure-interaction-surr-sel']!)
+                .apply(StateTransforms.Representation.StructureRepresentation3D,this.params.nciParams, { tags: StructureRepresentationInteractionTags.SurrRepr }).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;
+            refs[StructureRepresentationInteractionTags.SurrNciRepr] = builder
+                .to(refs['structure-interaction-surr-sel']!)
+                .apply(StateTransforms.Representation.StructureRepresentation3D, this.params.surroundingsParams, { tags: StructureRepresentationInteractionTags.SurrNciRepr }).ref;
         }
 
         return { state, builder, refs };
@@ -202,7 +213,24 @@ export class StructureRepresentationInteractionBehavior extends PluginBehavior.W
     }
 
     async update(params: StructureRepresentationInteractionProps) {
-        return false;
+        this.params = params;
+
+        const state = this.plugin.state.dataState;
+        const builder = state.build();
+
+        const all = StateSelection.Generators.root.subtree();        
+        for (const repr of state.select(all.withTag(StructureRepresentationInteractionTags.ResidueRepr))) {
+            builder.to(repr).update(this.params.focusParams);
+        }
+        for (const repr of state.select(all.withTag(StructureRepresentationInteractionTags.SurrRepr))) {
+            builder.to(repr).update(this.params.surroundingsParams);
+        }
+        for (const repr of state.select(all.withTag(StructureRepresentationInteractionTags.SurrNciRepr))) {
+            builder.to(repr).update(this.params.nciParams);
+        }
+
+        await PluginCommands.State.Update(this.plugin, { state, tree: builder, options: { doNotLogTiming: true, doNotUpdateCurrent: true } });
+        return true;
     }
 }
 
@@ -211,5 +239,5 @@ export const StructureRepresentationInteraction = PluginBehavior.create({
     display: { name: 'Structure Representation Interaction' },
     category: 'interaction',
     ctor: StructureRepresentationInteractionBehavior,
-    params: () => StructureRepresentationInteractionParams
+    params: (_, plugin) => StructureRepresentationInteractionParams(plugin)
 });

+ 0 - 3
src/mol-plugin/index.ts

@@ -27,7 +27,6 @@ export const DefaultPluginSpec: PluginSpec = {
         PluginSpec.Action(StateActions.Volume.DownloadDensity),
         PluginSpec.Action(StateActions.DataFormat.OpenFiles),
         PluginSpec.Action(StateActions.Structure.Create3DRepresentationPreset),
-        PluginSpec.Action(StateActions.Structure.Remove3DRepresentationPreset),
         PluginSpec.Action(StateActions.Structure.EnableModelCustomProps),
         PluginSpec.Action(StateActions.Structure.EnableStructureCustomProps),
 
@@ -62,8 +61,6 @@ export const DefaultPluginSpec: PluginSpec = {
 
         PluginSpec.Action(StateTransforms.Volume.VolumeFromCcp4),
         PluginSpec.Action(StateTransforms.Representation.VolumeRepresentation3D),
-
-        PluginSpec.Action(StateActions.Structure.StructureFromSelection),
     ],
     behaviors: [
         PluginSpec.Behavior(PluginBehaviors.Representation.HighlightLoci),

+ 1 - 5
src/mol-plugin/spec.ts

@@ -9,7 +9,6 @@ import { StateTransformer, StateAction } from '../mol-state';
 import { StateTransformParameters } from '../mol-plugin-ui/state/common';
 import { PluginLayoutStateProps } from './layout';
 import { PluginStateAnimation } from '../mol-plugin-state/animation/model';
-import { ParamDefinition as PD } from '../mol-util/param-definition';
 import { PluginConfigItem } from './config';
 
 export { PluginSpec }
@@ -49,10 +48,7 @@ namespace PluginSpec {
     }
 
     export function Behavior<T extends StateTransformer>(transformer: T, defaultParams: Partial<StateTransformer.Params<T>> = {}): Behavior {
-        const params = transformer.definition.params
-            ? PD.getDefaultValues(transformer.definition.params(undefined, undefined))
-            : {}
-        return { transformer, defaultParams: { ...params, ...defaultParams } };
+        return { transformer, defaultParams };
     }
 
     export interface LayoutControls {

+ 14 - 7
src/mol-repr/representation.ts

@@ -39,7 +39,8 @@ export type RepresentationFactory<D, P extends PD.Params, S extends Representati
 
 //
 
-export interface RepresentationProvider<D, P extends PD.Params, S extends Representation.State> {
+export interface RepresentationProvider<D = any, P extends PD.Params = any, S extends Representation.State = any, Id extends string = string> {
+    readonly name: Id,
     readonly label: string
     readonly description: string
     readonly factory: RepresentationFactory<D, P, S>
@@ -86,18 +87,24 @@ export class RepresentationRegistry<D, S extends Representation.State> {
 
     constructor() {};
 
-    add<P extends PD.Params>(name: string, provider: RepresentationProvider<D, P, S>) {
-        this._list.push({ name, provider })
-        this._map.set(name, provider)
-        this._name.set(provider, name)
+    add<P extends PD.Params>(provider: RepresentationProvider<D, P, S>) {
+        if (this._map.has(provider.name)) {
+            throw new Error(`${provider.name} already registered.`);
+        }
+
+        this._list.push({ name: provider.name, provider })
+        this._map.set(provider.name, provider)
+        this._name.set(provider, provider.name)
     }
 
-    getName(provider: RepresentationProvider<D, any, S>): string {
+    getName(provider: RepresentationProvider<D, any, any>): string {
         if (!this._name.has(provider)) throw new Error(`'${provider.label}' is not a registered represenatation provider.`);
         return this._name.get(provider)!;
     }
 
-    remove(name: string) {
+    remove(provider: RepresentationProvider<D, any, any>) {
+        const name = provider.name;
+
         this._list.splice(this._list.findIndex(e => e.name === name), 1)
         const p = this._map.get(name);
         if (p) {

+ 6 - 4
src/mol-repr/structure/registry.ts

@@ -5,7 +5,7 @@
  */
 
 import { Structure } from '../../mol-model/structure';
-import { RepresentationProvider, RepresentationRegistry } from '../representation';
+import { RepresentationRegistry } from '../representation';
 import { CartoonRepresentationProvider } from './representation/cartoon';
 import { BallAndStickRepresentationProvider } from './representation/ball-and-stick';
 import { GaussianSurfaceRepresentationProvider } from './representation/gaussian-surface';
@@ -18,13 +18,14 @@ import { MolecularSurfaceRepresentationProvider } from './representation/molecul
 import { EllipsoidRepresentationProvider } from './representation/ellipsoid';
 import { OrientationRepresentationProvider } from './representation/orientation';
 import { LabelRepresentationProvider } from './representation/label';
+import { objectForEach } from '../../mol-util/object';
 
 export class StructureRepresentationRegistry extends RepresentationRegistry<Structure, StructureRepresentationState> {
     constructor() {
         super()
-        Object.keys(BuiltInStructureRepresentations).forEach(name => {
-            const p = (BuiltInStructureRepresentations as { [k: string]: RepresentationProvider<Structure, any, StructureRepresentationState> })[name]
-            this.add(name, p)
+        objectForEach(BuiltInStructureRepresentations, (p, k) => {
+            if (p.name !== k) throw new Error(`Fix BuiltInStructureRepresentations to have matching names. ${p.name} ${k}`);
+            this.add(p as any)
         })
     }
 }
@@ -43,6 +44,7 @@ export const BuiltInStructureRepresentations = {
     'putty': PuttyRepresentationProvider,
     'spacefill': SpacefillRepresentationProvider,
 }
+
 export type BuiltInStructureRepresentations = typeof BuiltInStructureRepresentations
 export type BuiltInStructureRepresentationsName = keyof typeof BuiltInStructureRepresentations
 export const BuiltInStructureRepresentationsNames = Object.keys(BuiltInStructureRepresentations)

+ 2 - 2
src/mol-repr/structure/representation.ts

@@ -38,8 +38,8 @@ export const StructureRepresentationStateBuilder: Representation.StateBuilder<St
 
 export interface StructureRepresentation<P extends RepresentationProps = {}> extends Representation<Structure, P, StructureRepresentationState> { }
 
-export type StructureRepresentationProvider<P extends PD.Params> = RepresentationProvider<Structure, P, StructureRepresentationState>
-
+export type StructureRepresentationProvider<P extends PD.Params, Id extends string = string> = RepresentationProvider<Structure, P, StructureRepresentationState, Id>
+export function StructureRepresentationProvider<P extends PD.Params, Id extends string>(p: StructureRepresentationProvider<P, Id>): StructureRepresentationProvider<P, Id> { return p; }
 //
 
 export const StructureParams = { ...BaseGeometry.Params }

+ 3 - 2
src/mol-repr/structure/representation/ball-and-stick.ts

@@ -41,7 +41,8 @@ export function BallAndStickRepresentation(ctx: RepresentationContext, getParams
     return Representation.createMulti('Ball & Stick', ctx, getParams, StructureRepresentationStateBuilder, BallAndStickVisuals as unknown as Representation.Def<Structure, BallAndStickParams>)
 }
 
-export const BallAndStickRepresentationProvider: StructureRepresentationProvider<BallAndStickParams> = {
+export const BallAndStickRepresentationProvider = StructureRepresentationProvider({
+    name: 'ball-and-stick',
     label: 'Ball & Stick',
     description: 'Displays atoms as spheres and bonds as cylinders.',
     factory: BallAndStickRepresentation,
@@ -50,4 +51,4 @@ export const BallAndStickRepresentationProvider: StructureRepresentationProvider
     defaultColorTheme: { name: 'element-symbol' },
     defaultSizeTheme: { name: 'physical' },
     isApplicable: (structure: Structure) => structure.elementCount > 0
-}
+})

+ 3 - 2
src/mol-repr/structure/representation/carbohydrate.ts

@@ -36,7 +36,8 @@ export function CarbohydrateRepresentation(ctx: RepresentationContext, getParams
     return Representation.createMulti('Carbohydrate', ctx, getParams, StructureRepresentationStateBuilder, CarbohydrateVisuals as unknown as Representation.Def<Structure, CarbohydrateParams>)
 }
 
-export const CarbohydrateRepresentationProvider: StructureRepresentationProvider<CarbohydrateParams> = {
+export const CarbohydrateRepresentationProvider = StructureRepresentationProvider({
+    name: 'carbohydrate',
     label: 'Carbohydrate',
     description: 'Displays carbohydrate symbols (3D SNFG).',
     factory: CarbohydrateRepresentation,
@@ -45,4 +46,4 @@ export const CarbohydrateRepresentationProvider: StructureRepresentationProvider
     defaultColorTheme: { name: 'carbohydrate-symbol' },
     defaultSizeTheme: { name: 'uniform' },
     isApplicable: (structure: Structure) => structure.carbohydrates.elements.length > 0
-}
+})

+ 3 - 2
src/mol-repr/structure/representation/cartoon.ts

@@ -55,7 +55,8 @@ export function CartoonRepresentation(ctx: RepresentationContext, getParams: Rep
     return Representation.createMulti('Cartoon', ctx, getParams, StructureRepresentationStateBuilder, CartoonVisuals as unknown as Representation.Def<Structure, CartoonParams>)
 }
 
-export const CartoonRepresentationProvider: StructureRepresentationProvider<CartoonParams> = {
+export const CartoonRepresentationProvider = StructureRepresentationProvider({
+    name: 'cartoon',
     label: 'Cartoon',
     description: 'Displays ribbons, planks, tubes smoothly following the trace atoms of polymers.',
     factory: CartoonRepresentation,
@@ -68,4 +69,4 @@ export const CartoonRepresentationProvider: StructureRepresentationProvider<Cart
         attach: (ctx: CustomProperty.Context, structure: Structure) => SecondaryStructureProvider.attach(ctx, structure, void 0, true),
         detach: (_, data) => SecondaryStructureProvider.ref(data, false)
     }
-}
+})

+ 3 - 2
src/mol-repr/structure/representation/ellipsoid.ts

@@ -41,7 +41,8 @@ export function EllipsoidRepresentation(ctx: RepresentationContext, getParams: R
     return Representation.createMulti('Ellipsoid', ctx, getParams, StructureRepresentationStateBuilder, EllipsoidVisuals as unknown as Representation.Def<Structure, EllipsoidParams>)
 }
 
-export const EllipsoidRepresentationProvider: StructureRepresentationProvider<EllipsoidParams> = {
+export const EllipsoidRepresentationProvider = StructureRepresentationProvider({
+    name: 'ellipsoid',
     label: 'Ellipsoid',
     description: 'Displays anisotropic displacement ellipsoids of atomic elements plus bonds as cylinders.',
     factory: EllipsoidRepresentation,
@@ -50,4 +51,4 @@ export const EllipsoidRepresentationProvider: StructureRepresentationProvider<El
     defaultColorTheme: { name: 'element-symbol' },
     defaultSizeTheme: { name: 'uniform' },
     isApplicable: (structure: Structure) => structure.elementCount > 0 && structure.models.some(m => m.customProperties.has(AtomSiteAnisotrop.Descriptor))
-}
+})

+ 3 - 2
src/mol-repr/structure/representation/gaussian-surface.ts

@@ -35,7 +35,8 @@ export function GaussianSurfaceRepresentation(ctx: RepresentationContext, getPar
     return Representation.createMulti('Gaussian Surface', ctx, getParams, StructureRepresentationStateBuilder, GaussianSurfaceVisuals as unknown as Representation.Def<Structure, GaussianSurfaceParams>)
 }
 
-export const GaussianSurfaceRepresentationProvider: StructureRepresentationProvider<GaussianSurfaceParams> = {
+export const GaussianSurfaceRepresentationProvider = StructureRepresentationProvider({
+    name: 'gaussian-surface',
     label: 'Gaussian Surface',
     description: 'Displays a gaussian molecular surface.',
     factory: GaussianSurfaceRepresentation,
@@ -44,4 +45,4 @@ export const GaussianSurfaceRepresentationProvider: StructureRepresentationProvi
     defaultColorTheme: { name: 'polymer-id' },
     defaultSizeTheme: { name: 'uniform' },
     isApplicable: (structure: Structure) => structure.elementCount > 0
-}
+})

+ 3 - 2
src/mol-repr/structure/representation/gaussian-volume.ts

@@ -28,7 +28,8 @@ export function GaussianVolumeRepresentation(ctx: RepresentationContext, getPara
     return Representation.createMulti('Gaussian Volume', ctx, getParams, StructureRepresentationStateBuilder, GaussianVolumeVisuals as unknown as Representation.Def<Structure, GaussianVolumeParams>)
 }
 
-export const GaussianVolumeRepresentationProvider: StructureRepresentationProvider<GaussianVolumeParams> = {
+export const GaussianVolumeRepresentationProvider = StructureRepresentationProvider({
+    name: 'gaussian-volume',
     label: 'Gaussian Volume',
     description: 'Displays a gaussian molecular density using direct volume rendering.',
     factory: GaussianVolumeRepresentation,
@@ -37,4 +38,4 @@ export const GaussianVolumeRepresentationProvider: StructureRepresentationProvid
     defaultColorTheme: { name: 'polymer-id' },
     defaultSizeTheme: { name: 'uniform' },
     isApplicable: (structure: Structure) => structure.elementCount > 0
-}
+})

+ 3 - 2
src/mol-repr/structure/representation/label.ts

@@ -32,7 +32,8 @@ export function LabelRepresentation(ctx: RepresentationContext, getParams: Repre
     return repr
 }
 
-export const LabelRepresentationProvider: StructureRepresentationProvider<LabelParams> = {
+export const LabelRepresentationProvider = StructureRepresentationProvider({
+    name: 'label',
     label: 'Label',
     description: 'Displays labels.',
     factory: LabelRepresentation,
@@ -41,4 +42,4 @@ export const LabelRepresentationProvider: StructureRepresentationProvider<LabelP
     defaultColorTheme: { name: 'uniform' },
     defaultSizeTheme: { name: 'uniform' },
     isApplicable: (structure: Structure) => structure.elementCount > 0
-}
+})

+ 3 - 2
src/mol-repr/structure/representation/molecular-surface.ts

@@ -33,7 +33,8 @@ export function MolecularSurfaceRepresentation(ctx: RepresentationContext, getPa
     return Representation.createMulti('Molecular Surface', ctx, getParams, StructureRepresentationStateBuilder, MolecularSurfaceVisuals as unknown as Representation.Def<Structure, MolecularSurfaceParams>)
 }
 
-export const MolecularSurfaceRepresentationProvider: StructureRepresentationProvider<MolecularSurfaceParams> = {
+export const MolecularSurfaceRepresentationProvider = StructureRepresentationProvider({
+    name: 'molecular-surface',
     label: 'Molecular Surface',
     description: 'Displays a molecular surface.',
     factory: MolecularSurfaceRepresentation,
@@ -42,4 +43,4 @@ export const MolecularSurfaceRepresentationProvider: StructureRepresentationProv
     defaultColorTheme: { name: 'polymer-id' },
     defaultSizeTheme: { name: 'uniform' },
     isApplicable: (structure: Structure) => structure.elementCount > 0
-}
+})

+ 3 - 2
src/mol-repr/structure/representation/orientation.ts

@@ -30,7 +30,8 @@ export function OrientationRepresentation(ctx: RepresentationContext, getParams:
     return Representation.createMulti('Orientation', ctx, getParams, StructureRepresentationStateBuilder, OrientationVisuals as unknown as Representation.Def<Structure, OrientationParams>)
 }
 
-export const OrientationRepresentationProvider: StructureRepresentationProvider<OrientationParams> = {
+export const OrientationRepresentationProvider = StructureRepresentationProvider({
+    name: 'orientation',
     label: 'Orientation',
     description: 'Displays orientation ellipsoids for polymer chains.',
     factory: OrientationRepresentation,
@@ -39,4 +40,4 @@ export const OrientationRepresentationProvider: StructureRepresentationProvider<
     defaultColorTheme: { name: 'polymer-id' },
     defaultSizeTheme: { name: 'uniform' },
     isApplicable: (structure: Structure) => structure.elementCount > 0
-}
+})

+ 3 - 2
src/mol-repr/structure/representation/point.ts

@@ -31,7 +31,8 @@ export function PointRepresentation(ctx: RepresentationContext, getParams: Repre
     return Representation.createMulti('Point', ctx, getParams, StructureRepresentationStateBuilder, PointVisuals as unknown as Representation.Def<Structure, PointParams>)
 }
 
-export const PointRepresentationProvider: StructureRepresentationProvider<PointParams> = {
+export const PointRepresentationProvider = StructureRepresentationProvider({
+    name: 'point',
     label: 'Point',
     description: 'Displays elements (atoms, coarse spheres) as spheres.',
     factory: PointRepresentation,
@@ -40,4 +41,4 @@ export const PointRepresentationProvider: StructureRepresentationProvider<PointP
     defaultColorTheme: { name: 'element-symbol' },
     defaultSizeTheme: { name: 'physical' },
     isApplicable: (structure: Structure) => structure.elementCount > 0
-}
+})

+ 3 - 2
src/mol-repr/structure/representation/putty.ts

@@ -43,7 +43,8 @@ export function PuttyRepresentation(ctx: RepresentationContext, getParams: Repre
     return Representation.createMulti('Putty', ctx, getParams, StructureRepresentationStateBuilder, PuttyVisuals as unknown as Representation.Def<Structure, PuttyParams>)
 }
 
-export const PuttyRepresentationProvider: StructureRepresentationProvider<PuttyParams> = {
+export const PuttyRepresentationProvider = StructureRepresentationProvider({
+    name: 'putty',
     label: 'Putty',
     description: 'Displays a tube smoothly following the trace atoms of polymers.',
     factory: PuttyRepresentation,
@@ -52,4 +53,4 @@ export const PuttyRepresentationProvider: StructureRepresentationProvider<PuttyP
     defaultColorTheme: { name: 'polymer-id' },
     defaultSizeTheme: { name: 'uncertainty' },
     isApplicable: (structure: Structure) => structure.polymerResidueCount > 0
-}
+})

+ 3 - 2
src/mol-repr/structure/representation/spacefill.ts

@@ -31,7 +31,8 @@ export function SpacefillRepresentation(ctx: RepresentationContext, getParams: R
     return Representation.createMulti('Spacefill', ctx, getParams, StructureRepresentationStateBuilder, SpacefillVisuals as unknown as Representation.Def<Structure, SpacefillParams>)
 }
 
-export const SpacefillRepresentationProvider: StructureRepresentationProvider<SpacefillParams> = {
+export const SpacefillRepresentationProvider = StructureRepresentationProvider({
+    name: 'spacefill',
     label: 'Spacefill',
     description: 'Displays atomic/coarse elements as spheres.',
     factory: SpacefillRepresentation,
@@ -40,4 +41,4 @@ export const SpacefillRepresentationProvider: StructureRepresentationProvider<Sp
     defaultColorTheme: { name: 'element-symbol' },
     defaultSizeTheme: { name: 'physical' },
     isApplicable: (structure: Structure) => structure.elementCount > 0
-}
+})

+ 3 - 2
src/mol-repr/volume/direct-volume.ts

@@ -179,7 +179,8 @@ export function DirectVolumeRepresentation(ctx: RepresentationContext, getParams
     return VolumeRepresentation('Direct Volume', ctx, getParams, DirectVolumeVisual)
 }
 
-export const DirectVolumeRepresentationProvider: VolumeRepresentationProvider<DirectVolumeParams> = {
+export const DirectVolumeRepresentationProvider = VolumeRepresentationProvider({
+    name: 'direct-volume',
     label: 'Direct Volume',
     description: 'Direct volume rendering of volumetric data.',
     factory: DirectVolumeRepresentation,
@@ -188,4 +189,4 @@ export const DirectVolumeRepresentationProvider: VolumeRepresentationProvider<Di
     defaultColorTheme: { name: 'uniform' },
     defaultSizeTheme: { name: 'uniform' },
     isApplicable: (volume: VolumeData) => volume.data.data.length > 0
-}
+})

+ 3 - 2
src/mol-repr/volume/isosurface.ts

@@ -164,7 +164,8 @@ export function IsosurfaceRepresentation(ctx: RepresentationContext, getParams:
     return Representation.createMulti('Isosurface', ctx, getParams, Representation.StateBuilder, IsosurfaceVisuals as unknown as Representation.Def<VolumeData, IsosurfaceParams>)
 }
 
-export const IsosurfaceRepresentationProvider: VolumeRepresentationProvider<IsosurfaceParams> = {
+export const IsosurfaceRepresentationProvider = VolumeRepresentationProvider({
+    name: 'isosurface',
     label: 'Isosurface',
     description: 'Displays an isosurface of volumetric data.',
     factory: IsosurfaceRepresentation,
@@ -173,4 +174,4 @@ export const IsosurfaceRepresentationProvider: VolumeRepresentationProvider<Isos
     defaultColorTheme: { name: 'uniform' },
     defaultSizeTheme: { name: 'uniform' },
     isApplicable: (volume: VolumeData) => volume.data.data.length > 0
-}
+})

+ 6 - 3
src/mol-repr/volume/registry.ts

@@ -4,16 +4,19 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { RepresentationProvider, RepresentationRegistry, Representation } from '../representation';
+import { RepresentationRegistry, Representation } from '../representation';
 import { VolumeData } from '../../mol-model/volume';
 import { IsosurfaceRepresentationProvider } from './isosurface';
+import { objectForEach } from '../../mol-util/object';
 
 export class VolumeRepresentationRegistry extends RepresentationRegistry<VolumeData, Representation.State> {
     constructor() {
         super()
         Object.keys(BuiltInVolumeRepresentations).forEach(name => {
-            const p = (BuiltInVolumeRepresentations as { [k: string]: RepresentationProvider<VolumeData, any, Representation.State> })[name]
-            this.add(name, p)
+            objectForEach(BuiltInVolumeRepresentations, (p, k) => {
+                if (p.name !== k) throw new Error(`Fix BuiltInVolumeRepresentations to have matching names. ${p.name} ${k}`);
+                this.add(p as any)
+            })
         })
     }
 }

+ 2 - 1
src/mol-repr/volume/representation.ts

@@ -200,7 +200,8 @@ export function VolumeVisual<G extends Geometry, P extends VolumeParams & Geomet
 
 export interface VolumeRepresentation<P extends VolumeParams> extends Representation<VolumeData, P> { }
 
-export type VolumeRepresentationProvider<P extends VolumeParams> = RepresentationProvider<VolumeData, P, Representation.State>
+export type VolumeRepresentationProvider<P extends VolumeParams, Id extends string = string> = RepresentationProvider<VolumeData, P, Representation.State, Id>
+export function VolumeRepresentationProvider<P extends VolumeParams, Id extends string>(p: VolumeRepresentationProvider<P, Id>): VolumeRepresentationProvider<P, Id> { return p; }
 
 //
 

+ 4 - 2
src/mol-theme/color.ts

@@ -70,8 +70,8 @@ namespace ColorTheme {
         return themeA.contextHash === themeB.contextHash && themeA.factory === themeB.factory && deepEqual(themeA.props, themeB.props)
     }
 
-    export interface Provider<P extends PD.Params> extends ThemeProvider<ColorTheme<P>, P> { }
-    export const EmptyProvider: Provider<{}> = { label: '', category: '', factory: EmptyFactory, getParams: () => ({}), defaultValues: {}, isApplicable: () => true }
+    export interface Provider<P extends PD.Params = any, Id extends string = string> extends ThemeProvider<ColorTheme<P>, P, Id> { }
+    export const EmptyProvider: Provider<{}> = { name: '', label: '', category: '', factory: EmptyFactory, getParams: () => ({}), defaultValues: {}, isApplicable: () => true }
 
     export type Registry = ThemeRegistry<ColorTheme<any>>
     export function createRegistry() {
@@ -81,6 +81,8 @@ namespace ColorTheme {
     export type ParamValues<C extends ColorTheme.Provider<any>> = C extends ColorTheme.Provider<infer P> ? PD.Values<P> : never
 }
 
+export function ColorThemeProvider<P extends PD.Params, Id extends string>(p: ColorTheme.Provider<P, Id>): ColorTheme.Provider<P, Id> { return p; }
+
 export const BuiltInColorThemes = {
     'carbohydrate-symbol': CarbohydrateSymbolColorThemeProvider,
     'chain-id': ChainIdColorThemeProvider,

+ 2 - 1
src/mol-theme/color/carbohydrate-symbol.ts

@@ -60,7 +60,8 @@ export function CarbohydrateSymbolColorTheme(ctx: ThemeDataContext, props: PD.Va
     }
 }
 
-export const CarbohydrateSymbolColorThemeProvider: ColorTheme.Provider<CarbohydrateSymbolColorThemeParams> = {
+export const CarbohydrateSymbolColorThemeProvider: ColorTheme.Provider<CarbohydrateSymbolColorThemeParams, 'carbohydrate-symbol'> = {
+    name: 'carbohydrate-symbol',
     label: 'Carbohydrate Symbol',
     category: ColorTheme.Category.Residue,
     factory: CarbohydrateSymbolColorTheme,

+ 2 - 1
src/mol-theme/color/chain-id.ts

@@ -117,7 +117,8 @@ export function ChainIdColorTheme(ctx: ThemeDataContext, props: PD.Values<ChainI
     }
 }
 
-export const ChainIdColorThemeProvider: ColorTheme.Provider<ChainIdColorThemeParams> = {
+export const ChainIdColorThemeProvider: ColorTheme.Provider<ChainIdColorThemeParams, 'chain-id'> = {
+    name: 'chain-id',
     label: 'Chain Id',
     category: ColorTheme.Category.Chain,
     factory: ChainIdColorTheme,

+ 2 - 1
src/mol-theme/color/element-index.ts

@@ -71,7 +71,8 @@ export function ElementIndexColorTheme(ctx: ThemeDataContext, props: PD.Values<E
     }
 }
 
-export const ElementIndexColorThemeProvider: ColorTheme.Provider<ElementIndexColorThemeParams> = {
+export const ElementIndexColorThemeProvider: ColorTheme.Provider<ElementIndexColorThemeParams, 'element-index'> = {
+    name: 'element-index',
     label: 'Element Index',
     category: ColorTheme.Category.Atom,
     factory: ElementIndexColorTheme,

+ 2 - 1
src/mol-theme/color/element-symbol.ts

@@ -67,7 +67,8 @@ export function ElementSymbolColorTheme(ctx: ThemeDataContext, props: PD.Values<
     }
 }
 
-export const ElementSymbolColorThemeProvider: ColorTheme.Provider<ElementSymbolColorThemeParams> = {
+export const ElementSymbolColorThemeProvider: ColorTheme.Provider<ElementSymbolColorThemeParams, 'element-symbol'> = {
+    name: 'element-symbol',
     label: 'Element Symbol',
     category: ColorTheme.Category.Atom,
     factory: ElementSymbolColorTheme,

+ 2 - 1
src/mol-theme/color/entity-source.ts

@@ -173,7 +173,8 @@ export function EntitySourceColorTheme(ctx: ThemeDataContext, props: PD.Values<E
     }
 }
 
-export const EntitySourceColorThemeProvider: ColorTheme.Provider<EntitySourceColorThemeParams> = {
+export const EntitySourceColorThemeProvider: ColorTheme.Provider<EntitySourceColorThemeParams, 'entity-source'> = {
+    name: 'entity-source',
     label: 'Entity Source',
     category: ColorTheme.Category.Chain,
     factory: EntitySourceColorTheme,

+ 2 - 1
src/mol-theme/color/hydrophobicity.ts

@@ -92,7 +92,8 @@ export function HydrophobicityColorTheme(ctx: ThemeDataContext, props: PD.Values
     }
 }
 
-export const HydrophobicityColorThemeProvider: ColorTheme.Provider<HydrophobicityColorThemeParams> = {
+export const HydrophobicityColorThemeProvider: ColorTheme.Provider<HydrophobicityColorThemeParams, 'hydrophobicity'> = {
+    name: 'hydrophobicity',
     label: 'Hydrophobicity',
     category: ColorTheme.Category.Residue,
     factory: HydrophobicityColorTheme,

+ 2 - 1
src/mol-theme/color/illustrative.ts

@@ -74,7 +74,8 @@ export function IllustrativeColorTheme(ctx: ThemeDataContext, props: PD.Values<I
     }
 }
 
-export const IllustrativeColorThemeProvider: ColorTheme.Provider<IllustrativeColorThemeParams> = {
+export const IllustrativeColorThemeProvider: ColorTheme.Provider<IllustrativeColorThemeParams, 'illustrative'> = {
+    name: 'illustrative',
     label: 'Illustrative',
     category: ColorTheme.Category.Misc,
     factory: IllustrativeColorTheme,

+ 2 - 1
src/mol-theme/color/model-index.ts

@@ -64,7 +64,8 @@ export function ModelIndexColorTheme(ctx: ThemeDataContext, props: PD.Values<Mod
     }
 }
 
-export const ModelIndexColorThemeProvider: ColorTheme.Provider<ModelIndexColorThemeParams> = {
+export const ModelIndexColorThemeProvider: ColorTheme.Provider<ModelIndexColorThemeParams, 'model-index'> = {
+    name: 'model-index',
     label: 'Model Index',
     category: ColorTheme.Category.Chain,
     factory: ModelIndexColorTheme,

+ 2 - 1
src/mol-theme/color/molecule-type.ts

@@ -76,7 +76,8 @@ export function MoleculeTypeColorTheme(ctx: ThemeDataContext, props: PD.Values<M
     }
 }
 
-export const MoleculeTypeColorThemeProvider: ColorTheme.Provider<MoleculeTypeColorThemeParams> = {
+export const MoleculeTypeColorThemeProvider: ColorTheme.Provider<MoleculeTypeColorThemeParams, 'molecule-type'> = {
+    name: 'molecule-type',
     label: 'Molecule Type',
     category: ColorTheme.Category.Residue,
     factory: MoleculeTypeColorTheme,

+ 2 - 1
src/mol-theme/color/occupancy.ts

@@ -58,7 +58,8 @@ export function OccupancyColorTheme(ctx: ThemeDataContext, props: PD.Values<Occu
     }
 }
 
-export const OccupancyColorThemeProvider: ColorTheme.Provider<OccupancyColorThemeParams> = {
+export const OccupancyColorThemeProvider: ColorTheme.Provider<OccupancyColorThemeParams, 'occupancy'> = {
+    name: 'occupancy',
     label: 'Occupancy',
     category: ColorTheme.Category.Atom,
     factory: OccupancyColorTheme,

+ 2 - 1
src/mol-theme/color/operator-hkl.ts

@@ -117,7 +117,8 @@ export function OperatorHklColorTheme(ctx: ThemeDataContext, props: PD.Values<Op
     }
 }
 
-export const OperatorHklColorThemeProvider: ColorTheme.Provider<OperatorHklColorThemeParams> = {
+export const OperatorHklColorThemeProvider: ColorTheme.Provider<OperatorHklColorThemeParams, 'operator-hkl'> = {
+    name: 'operator-hkl',
     label: 'Operator HKL',
     category: ColorTheme.Category.Symmetry,
     factory: OperatorHklColorTheme,

+ 2 - 1
src/mol-theme/color/operator-name.ts

@@ -83,7 +83,8 @@ export function OperatorNameColorTheme(ctx: ThemeDataContext, props: PD.Values<O
     }
 }
 
-export const OperatorNameColorThemeProvider: ColorTheme.Provider<OperatorNameColorThemeParams> = {
+export const OperatorNameColorThemeProvider: ColorTheme.Provider<OperatorNameColorThemeParams, 'operator-name'> = {
+    name: 'operator-name',
     label: 'Operator Name',
     category: ColorTheme.Category.Symmetry,
     factory: OperatorNameColorTheme,

+ 2 - 1
src/mol-theme/color/polymer-id.ts

@@ -126,7 +126,8 @@ export function PolymerIdColorTheme(ctx: ThemeDataContext, props: PD.Values<Poly
     }
 }
 
-export const PolymerIdColorThemeProvider: ColorTheme.Provider<PolymerIdColorThemeParams> = {
+export const PolymerIdColorThemeProvider: ColorTheme.Provider<PolymerIdColorThemeParams, 'polymer-id'> = {
+    name: 'polymer-id',
     label: 'Polymer Chain Id',
     category: ColorTheme.Category.Chain,
     factory: PolymerIdColorTheme,

+ 2 - 1
src/mol-theme/color/polymer-index.ts

@@ -86,7 +86,8 @@ export function PolymerIndexColorTheme(ctx: ThemeDataContext, props: PD.Values<P
     }
 }
 
-export const PolymerIndexColorThemeProvider: ColorTheme.Provider<PolymerIndexColorThemeParams> = {
+export const PolymerIndexColorThemeProvider: ColorTheme.Provider<PolymerIndexColorThemeParams, 'polymer-index'> = {
+    name: 'polymer-index',
     label: 'Polymer Chain Instance',
     category: ColorTheme.Category.Chain,
     factory: PolymerIndexColorTheme,

+ 2 - 1
src/mol-theme/color/residue-name.ts

@@ -128,7 +128,8 @@ export function ResidueNameColorTheme(ctx: ThemeDataContext, props: PD.Values<Re
     }
 }
 
-export const ResidueNameColorThemeProvider: ColorTheme.Provider<ResidueNameColorThemeParams> = {
+export const ResidueNameColorThemeProvider: ColorTheme.Provider<ResidueNameColorThemeParams, 'residue-name'> = {
+    name: 'residue-name',
     label: 'Residue Name',
     category: ColorTheme.Category.Residue,
     factory: ResidueNameColorTheme,

+ 2 - 1
src/mol-theme/color/secondary-structure.ts

@@ -111,7 +111,8 @@ export function SecondaryStructureColorTheme(ctx: ThemeDataContext, props: PD.Va
     }
 }
 
-export const SecondaryStructureColorThemeProvider: ColorTheme.Provider<SecondaryStructureColorThemeParams> = {
+export const SecondaryStructureColorThemeProvider: ColorTheme.Provider<SecondaryStructureColorThemeParams, 'secondary-structure'> = {
+    name: 'secondary-structure',
     label: 'Secondary Structure',
     category: ColorTheme.Category.Residue,
     factory: SecondaryStructureColorTheme,

+ 2 - 1
src/mol-theme/color/sequence-id.ts

@@ -99,7 +99,8 @@ export function SequenceIdColorTheme(ctx: ThemeDataContext, props: PD.Values<Seq
     }
 }
 
-export const SequenceIdColorThemeProvider: ColorTheme.Provider<SequenceIdColorThemeParams> = {
+export const SequenceIdColorThemeProvider: ColorTheme.Provider<SequenceIdColorThemeParams, 'sequence-id'> = {
+    name: 'sequence-id',
     label: 'Sequence Id',
     category: ColorTheme.Category.Residue,
     factory: SequenceIdColorTheme,

+ 2 - 1
src/mol-theme/color/shape-group.ts

@@ -35,7 +35,8 @@ export function ShapeGroupColorTheme(ctx: ThemeDataContext, props: PD.Values<Sha
     }
 }
 
-export const ShapeGroupColorThemeProvider: ColorTheme.Provider<ShapeGroupColorThemeParams> = {
+export const ShapeGroupColorThemeProvider: ColorTheme.Provider<ShapeGroupColorThemeParams, 'shape-group'> = {
+    name: 'shape-group',
     label: 'Shape Group',
     category: ColorTheme.Category.Misc,
     factory: ShapeGroupColorTheme,

+ 2 - 1
src/mol-theme/color/uncertainty.ts

@@ -62,7 +62,8 @@ export function UncertaintyColorTheme(ctx: ThemeDataContext, props: PD.Values<Un
     }
 }
 
-export const UncertaintyColorThemeProvider: ColorTheme.Provider<UncertaintyColorThemeParams> = {
+export const UncertaintyColorThemeProvider: ColorTheme.Provider<UncertaintyColorThemeParams, 'uncertainty'> = {
+    name: 'uncertainty',
     label: 'Uncertainty/Disorder',
     category: ColorTheme.Category.Atom,
     factory: UncertaintyColorTheme,

+ 2 - 1
src/mol-theme/color/uniform.ts

@@ -35,7 +35,8 @@ export function UniformColorTheme(ctx: ThemeDataContext, props: PD.Values<Unifor
     }
 }
 
-export const UniformColorThemeProvider: ColorTheme.Provider<UniformColorThemeParams> = {
+export const UniformColorThemeProvider: ColorTheme.Provider<UniformColorThemeParams, 'uniform'> = {
+    name: 'uniform',
     label: 'Uniform',
     category: ColorTheme.Category.Misc,
     factory: UniformColorTheme,

+ 2 - 1
src/mol-theme/color/unit-index.ts

@@ -71,7 +71,8 @@ export function UnitIndexColorTheme(ctx: ThemeDataContext, props: PD.Values<Unit
     }
 }
 
-export const UnitIndexColorThemeProvider: ColorTheme.Provider<UnitIndexColorThemeParams> = {
+export const UnitIndexColorThemeProvider: ColorTheme.Provider<UnitIndexColorThemeParams, 'unit-index'> = {
+    name: 'unit-index',
     label: 'Chain Instance',
     category: ColorTheme.Category.Chain,
     factory: UnitIndexColorTheme,

+ 2 - 2
src/mol-theme/size.ts

@@ -31,8 +31,8 @@ namespace SizeTheme {
         return themeA.factory === themeB.factory && deepEqual(themeA.props, themeB.props)
     }
 
-    export interface Provider<P extends PD.Params> extends ThemeProvider<SizeTheme<P>, P> { }
-    export const EmptyProvider: Provider<{}> = { label: '', category: '', factory: EmptyFactory, getParams: () => ({}), defaultValues: {}, isApplicable: () => true }
+    export interface Provider<P extends PD.Params = any, Id extends string = string> extends ThemeProvider<SizeTheme<P>, P, Id> { }
+    export const EmptyProvider: Provider<{}> = { name: '', label: '', category: '', factory: EmptyFactory, getParams: () => ({}), defaultValues: {}, isApplicable: () => true }
 
     export type Registry = ThemeRegistry<SizeTheme<any>>
     export function createRegistry() {

+ 2 - 1
src/mol-theme/size/physical.ts

@@ -56,7 +56,8 @@ export function PhysicalSizeTheme(ctx: ThemeDataContext, props: PD.Values<Physic
     }
 }
 
-export const PhysicalSizeThemeProvider: SizeTheme.Provider<PhysicalSizeThemeParams> = {
+export const PhysicalSizeThemeProvider: SizeTheme.Provider<PhysicalSizeThemeParams, 'physical'> = {
+    name: 'physical',
     label: 'Physical',
     category: '',
     factory: PhysicalSizeTheme,

+ 2 - 1
src/mol-theme/size/shape-group.ts

@@ -34,7 +34,8 @@ export function ShapeGroupSizeTheme(ctx: ThemeDataContext, props: PD.Values<Shap
     }
 }
 
-export const ShapeGroupSizeThemeProvider: SizeTheme.Provider<ShapeGroupSizeThemeParams> = {
+export const ShapeGroupSizeThemeProvider: SizeTheme.Provider<ShapeGroupSizeThemeParams, 'shape-group'> = {
+    name: 'shape-group',
     label: 'Shape Group',
     category: '',    
     factory: ShapeGroupSizeTheme,

+ 2 - 1
src/mol-theme/size/uncertainty.ts

@@ -52,7 +52,8 @@ export function UncertaintySizeTheme(ctx: ThemeDataContext, props: PD.Values<Unc
     }
 }
 
-export const UncertaintySizeThemeProvider: SizeTheme.Provider<UncertaintySizeThemeParams> = {
+export const UncertaintySizeThemeProvider: SizeTheme.Provider<UncertaintySizeThemeParams, 'uncertainty'> = {
+    name: 'uncertainty',
     label: 'Uncertainty/Disorder',
     category: '',
     factory: UncertaintySizeTheme,

+ 2 - 1
src/mol-theme/size/uniform.ts

@@ -30,7 +30,8 @@ export function UniformSizeTheme(ctx: ThemeDataContext, props: PD.Values<Uniform
     }
 }
 
-export const UniformSizeThemeProvider: SizeTheme.Provider<UniformSizeThemeParams> = {
+export const UniformSizeThemeProvider: SizeTheme.Provider<UniformSizeThemeParams, 'uniform'> = {
+    name: 'uniform',
     label: 'Uniform',
     category: '',
     factory: UniformSizeTheme,

+ 14 - 4
src/mol-theme/theme.ts

@@ -11,6 +11,7 @@ import { VolumeData } from '../mol-model/volume';
 import { ParamDefinition as PD } from '../mol-util/param-definition';
 import { Shape } from '../mol-model/shape';
 import { CustomProperty } from '../mol-model-props/common/custom-property';
+import { objectForEach } from '../mol-util/object';
 
 export interface ThemeRegistryContext {
     colorThemeRegistry: ColorTheme.Registry
@@ -64,7 +65,8 @@ namespace Theme {
 
 //
 
-export interface ThemeProvider<T extends ColorTheme<P> | SizeTheme<P>, P extends PD.Params> {
+export interface ThemeProvider<T extends ColorTheme<P> | SizeTheme<P>, P extends PD.Params, Id extends string = string> {
+    readonly name: Id
     readonly label: string
     readonly category: string
     readonly factory: (ctx: ThemeDataContext, props: PD.Values<P>) => T
@@ -91,7 +93,10 @@ export class ThemeRegistry<T extends ColorTheme<any> | SizeTheme<any>> {
     get types(): [string, string, string][] { return getTypes(this._list) }
 
     constructor(builtInThemes: { [k: string]: ThemeProvider<T, any> }, private emptyProvider: ThemeProvider<T, any>) {
-        Object.keys(builtInThemes).forEach(name => this.add(name, builtInThemes[name]))
+        objectForEach(builtInThemes, (p, k) => {
+            if (p.name !== k) throw new Error(`Fix build in themes to have matching names. ${p.name} ${k}`);
+            this.add(p as any)
+        })
     }
 
     private sort() {
@@ -103,14 +108,19 @@ export class ThemeRegistry<T extends ColorTheme<any> | SizeTheme<any>> {
         });
     }
 
-    add<P extends PD.Params>(name: string, provider: ThemeProvider<T, P>) {
+    add<P extends PD.Params>(provider: ThemeProvider<T, P>) {
+        if (this._map.has(provider.name)) {
+            throw new Error(`${provider.name} already registered.`);
+        }
+
+        const name = provider.name;
         this._list.push({ name, provider })
         this._map.set(name, provider)
         this._name.set(provider, name)
         this.sort();
     }
 
-    remove(name: string) {
+    remove(provider: ThemeProvider<T, any>) {
         this._list.splice(this._list.findIndex(e => e.name === name), 1)
         const p = this._map.get(name);
         if (p) {

+ 2 - 2
src/mol-util/param-definition.ts

@@ -199,8 +199,8 @@ export namespace ParamDefinition {
         isExpanded?: boolean,
         isFlat?: boolean
     }
-    export function Group<T>(params: For<T>, info?: Info & { isExpanded?: boolean, isFlat?: boolean }): Group<Normalize<T>> {
-        const ret = setInfo<Group<Normalize<T>>>({ type: 'group', defaultValue: getDefaultValues(params as any as Params) as any, params: params as any as Params }, info);
+    export function Group<T>(params: For<T>, info?: Info & { isExpanded?: boolean, isFlat?: boolean, customDefault?: any }): Group<Normalize<T>> {
+        const ret = setInfo<Group<Normalize<T>>>({ type: 'group', defaultValue: info?.customDefault || getDefaultValues(params as any as Params) as any, params: params as any as Params }, info);
         if (info && info.isExpanded) ret.isExpanded = info.isExpanded;
         if (info && info.isFlat) ret.isFlat = info.isFlat;
         return ret;

+ 3 - 8
webpack.config.common.js

@@ -29,14 +29,6 @@ const sharedConfig = {
         //     failOnError: false,
         //     cwd: process.cwd(),
         // }),
-        new webpack.ProvidePlugin({
-            '__assign': ['tslib', '__assign'],
-            '__extends': ['tslib', '__extends'],
-            '__awaiter': ['tslib', '__awaiter'],
-            '__generator': ['tslib', '__generator'],
-            '__spread': ['tslib', '__spread'],
-            '__spreadArrays': ['tslib', '__spreadArrays'],
-        }),
         new ExtraWatchWebpackPlugin({
             files: [
                 './lib/**/*.scss',
@@ -55,6 +47,9 @@ const sharedConfig = {
             path.resolve(__dirname, 'lib/')
         ],
     },
+    watchOptions: {
+        aggregateTimeout: 750
+    },
     devtool: ''
 }