Browse Source

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

giagitom 1 year ago
parent
commit
c6210ae1a0

+ 5 - 0
CHANGELOG.md

@@ -6,11 +6,16 @@ Note that since we don't clearly distinguish between a public and private interf
 
 ## [Unreleased]
 
+## [v3.42.0] - 2023-11-05
+
 - Fix handling of PDB files with insertion codes (#945)
 - Fix de-/saturate of colors with no hue
 - Improve `distinctColors` function
     - Add `sort` and `sampleCountFactor` parameters
     - Fix clustering issues
+- Add `clipPrimitive` option to spheres geometry, clipping whole spheres instead of cutting them
+- Add `DragAndDropManager`
+- Add `options` support for default bond labels
 
 ## [v3.41.0] - 2023-10-15
 

File diff suppressed because it is too large
+ 402 - 193
package-lock.json


+ 23 - 23
package.json

@@ -1,6 +1,6 @@
 {
   "name": "molstar",
-  "version": "3.41.0",
+  "version": "3.42.0",
   "description": "A comprehensive macromolecular library.",
   "homepage": "https://github.com/molstar/molstar#readme",
   "repository": {
@@ -108,23 +108,23 @@
     "@graphql-codegen/time": "^5.0.0",
     "@graphql-codegen/typescript": "^4.0.1",
     "@graphql-codegen/typescript-graphql-files-modules": "^3.0.0",
-    "@graphql-codegen/typescript-graphql-request": "^6.0.0",
+    "@graphql-codegen/typescript-graphql-request": "^6.0.1",
     "@graphql-codegen/typescript-operations": "^4.0.1",
-    "@types/cors": "^2.8.14",
-    "@types/gl": "^6.0.3",
+    "@types/cors": "^2.8.15",
+    "@types/gl": "^6.0.4",
     "@types/jpeg-js": "^0.3.7",
-    "@types/pngjs": "^6.0.2",
-    "@types/jest": "^29.5.5",
-    "@types/react": "^18.2.23",
-    "@types/react-dom": "^18.2.8",
-    "@typescript-eslint/eslint-plugin": "^6.7.3",
-    "@typescript-eslint/parser": "^6.7.3",
+    "@types/pngjs": "^6.0.3",
+    "@types/jest": "^29.5.7",
+    "@types/react": "^18.2.35",
+    "@types/react-dom": "^18.2.14",
+    "@typescript-eslint/eslint-plugin": "^6.9.1",
+    "@typescript-eslint/parser": "^6.9.1",
     "benchmark": "^2.1.4",
-    "concurrently": "^8.2.1",
-    "cpx2": "^5.0.0",
+    "concurrently": "^8.2.2",
+    "cpx2": "^6.0.1",
     "crypto-browserify": "^3.12.0",
     "css-loader": "^6.8.1",
-    "eslint": "^8.50.0",
+    "eslint": "^8.53.0",
     "extra-watch-webpack-plugin": "^1.0.3",
     "file-loader": "^6.2.0",
     "fs-extra": "^11.1.1",
@@ -136,24 +136,24 @@
     "raw-loader": "^4.0.2",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
-    "sass": "^1.68.0",
+    "sass": "^1.69.5",
     "sass-loader": "^13.3.2",
     "simple-git": "^3.20.0",
     "stream-browserify": "^3.0.0",
     "style-loader": "^3.3.3",
     "ts-jest": "^29.1.1",
     "typescript": "^5.2.2",
-    "webpack": "^5.88.2",
+    "webpack": "^5.89.0",
     "webpack-cli": "^5.1.4"
   },
   "dependencies": {
-    "@types/argparse": "^2.0.11",
-    "@types/benchmark": "^2.1.3",
-    "@types/compression": "1.7.3",
-    "@types/express": "^4.17.18",
-    "@types/node": "^16.18.55",
-    "@types/node-fetch": "^2.6.6",
-    "@types/swagger-ui-dist": "3.30.2",
+    "@types/argparse": "^2.0.12",
+    "@types/benchmark": "^2.1.4",
+    "@types/compression": "1.7.4",
+    "@types/express": "^4.17.20",
+    "@types/node": "^16.18.60",
+    "@types/node-fetch": "^2.6.8",
+    "@types/swagger-ui-dist": "3.30.3",
     "argparse": "^2.0.1",
     "body-parser": "^1.20.2",
     "compression": "^1.7.4",
@@ -164,7 +164,7 @@
     "immutable": "^4.3.4",
     "node-fetch": "^2.7.0",
     "rxjs": "^7.8.1",
-    "swagger-ui-dist": "^5.9.0",
+    "swagger-ui-dist": "^5.9.1",
     "tslib": "^2.6.2",
     "util.promisify": "^1.1.2",
     "xhr2": "^0.2.1"

+ 8 - 0
src/examples/basic-wrapper/index.ts

@@ -46,6 +46,14 @@ class BasicWrapper {
         this.plugin.representation.structure.themes.colorThemeRegistry.add(CustomColorThemeProvider);
         this.plugin.managers.lociLabels.addProvider(StripedResidues.labelProvider!);
         this.plugin.customModelProperties.register(StripedResidues.propertyProvider, true);
+
+        this.plugin.managers.dragAndDrop.addHandler('custom-wrapper', (files) => {
+            if (files.some(f => f.name.toLowerCase().endsWith('.testext'))) {
+                console.log('.testext File dropped');
+                return true;
+            }
+            return false;
+        });
     }
 
     async load({ url, format = 'mmcif', isBinary = false, assemblyId = '' }: LoadParams) {

+ 8 - 8
src/extensions/rcsb/graphql/types.ts

@@ -6,7 +6,7 @@ export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?:
 export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
 export type MakeEmpty<T extends { [key: string]: unknown }, K extends keyof T> = { [_ in K]?: never };
 export type Incremental<T> = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never };
-// Generated on 2023-09-30T10:32:40-07:00
+// Generated on 2023-11-05T13:21:21-08:00
 
 /** All built-in and custom scalars, mapped to their actual values */
 export type Scalars = {
@@ -2030,7 +2030,7 @@ export type EmImaging = {
    * The name of the model of microscope.
    *
    * Allowable values:
-   * FEI MORGAGNI, FEI POLARA 300, FEI TALOS ARCTICA, FEI TECNAI 10, FEI TECNAI 12, FEI TECNAI 20, FEI TECNAI ARCTICA, FEI TECNAI F20, FEI TECNAI F30, FEI TECNAI SPHERA, FEI TECNAI SPIRIT, FEI TITAN, FEI TITAN KRIOS, FEI/PHILIPS CM10, FEI/PHILIPS CM12, FEI/PHILIPS CM120T, FEI/PHILIPS CM200FEG, FEI/PHILIPS CM200FEG/SOPHIE, FEI/PHILIPS CM200FEG/ST, FEI/PHILIPS CM200FEG/UT, FEI/PHILIPS CM200T, FEI/PHILIPS CM300FEG/HE, FEI/PHILIPS CM300FEG/ST, FEI/PHILIPS CM300FEG/T, FEI/PHILIPS EM400, FEI/PHILIPS EM420, HITACHI EF2000, HITACHI EF3000, HITACHI H-9500SD, HITACHI H3000 UHVEM, HITACHI H7600, HITACHI HF2000, HITACHI HF3000, JEOL 1000EES, JEOL 100B, JEOL 100CX, JEOL 1010, JEOL 1200, JEOL 1200EX, JEOL 1200EXII, JEOL 1230, JEOL 1400, JEOL 2000EX, JEOL 2000EXII, JEOL 2010, JEOL 2010F, JEOL 2010HC, JEOL 2010HT, JEOL 2010UHR, JEOL 2011, JEOL 2100, JEOL 2100F, JEOL 2200FS, JEOL 2200FSC, JEOL 3000SFF, JEOL 3100FEF, JEOL 3100FFC, JEOL 3200FS, JEOL 3200FSC, JEOL 4000, JEOL 4000EX, JEOL CRYO ARM 200, JEOL CRYO ARM 300, JEOL KYOTO-3000SFF, SIEMENS SULEIKA, TFS GLACIOS, TFS KRIOS, TFS TALOS, TFS TALOS F200C, TFS TALOS L120C, TFS TUNDRA, ZEISS LEO912, ZEISS LIBRA120PLUS
+   * FEI MORGAGNI, FEI POLARA 300, FEI TALOS ARCTICA, FEI TECNAI 10, FEI TECNAI 12, FEI TECNAI 20, FEI TECNAI ARCTICA, FEI TECNAI F20, FEI TECNAI F30, FEI TECNAI SPHERA, FEI TECNAI SPIRIT, FEI TITAN, FEI TITAN KRIOS, FEI/PHILIPS CM10, FEI/PHILIPS CM12, FEI/PHILIPS CM120T, FEI/PHILIPS CM200FEG, FEI/PHILIPS CM200FEG/SOPHIE, FEI/PHILIPS CM200FEG/ST, FEI/PHILIPS CM200FEG/UT, FEI/PHILIPS CM200T, FEI/PHILIPS CM300FEG/HE, FEI/PHILIPS CM300FEG/ST, FEI/PHILIPS CM300FEG/T, FEI/PHILIPS EM400, FEI/PHILIPS EM420, HITACHI EF2000, HITACHI EF3000, HITACHI H-9500SD, HITACHI H3000 UHVEM, HITACHI H7600, HITACHI HF2000, HITACHI HF3000, JEOL 1000EES, JEOL 100B, JEOL 100CX, JEOL 1010, JEOL 1200, JEOL 1200EX, JEOL 1200EXII, JEOL 1230, JEOL 1400, JEOL 1400/HR + YPS FEG, JEOL 2000EX, JEOL 2000EXII, JEOL 2010, JEOL 2010F, JEOL 2010HC, JEOL 2010HT, JEOL 2010UHR, JEOL 2011, JEOL 2100, JEOL 2100F, JEOL 2200FS, JEOL 2200FSC, JEOL 3000SFF, JEOL 3100FEF, JEOL 3100FFC, JEOL 3200FS, JEOL 3200FSC, JEOL 4000, JEOL 4000EX, JEOL CRYO ARM 200, JEOL CRYO ARM 300, JEOL KYOTO-3000SFF, SIEMENS SULEIKA, TFS GLACIOS, TFS KRIOS, TFS TALOS, TFS TALOS F200C, TFS TALOS L120C, TFS TUNDRA, ZEISS LEO912, ZEISS LIBRA120PLUS
    *
    */
   readonly microscope_model?: Maybe<Scalars['String']['output']>;
@@ -2151,7 +2151,7 @@ export type EmSoftware = {
    * The purpose of the software.
    *
    * Allowable values:
-   * CLASSIFICATION, CRYSTALLOGRAPHY MERGING, CTF CORRECTION, DIFFRACTION INDEXING, FINAL EULER ASSIGNMENT, IMAGE ACQUISITION, INITIAL EULER ASSIGNMENT, LATTICE DISTORTION CORRECTION, LAYERLINE INDEXING, MASKING, MODEL FITTING, MODEL REFINEMENT, MOLECULAR REPLACEMENT, OTHER, PARTICLE SELECTION, RECONSTRUCTION, SERIES ALIGNMENT, SYMMETRY DETERMINATION, VOLUME SELECTION
+   * CLASSIFICATION, CRYSTALLOGRAPHY MERGING, CTF CORRECTION, DIFFRACTION INDEXING, EWALD SPHERE CORRECTION, FINAL EULER ASSIGNMENT, IMAGE ACQUISITION, INITIAL EULER ASSIGNMENT, LATTICE DISTORTION CORRECTION, LAYERLINE INDEXING, MASKING, MODEL FITTING, MODEL REFINEMENT, MOLECULAR REPLACEMENT, OTHER, PARTICLE SELECTION, RECONSTRUCTION, SERIES ALIGNMENT, SYMMETRY DETERMINATION, VOLUME SELECTION
    *
    */
   readonly category?: Maybe<Scalars['String']['output']>;
@@ -2308,7 +2308,7 @@ export type EmVitrification = {
    * The type of instrument used in the vitrification process.
    *
    * Allowable values:
-   * EMS-002 RAPID IMMERSION FREEZER, FEI VITROBOT MARK I, FEI VITROBOT MARK II, FEI VITROBOT MARK III, FEI VITROBOT MARK IV, GATAN CRYOPLUNGE 3, HOMEMADE PLUNGER, LEICA EM CPC, LEICA EM GP, LEICA KF80, LEICA PLUNGER, REICHERT-JUNG PLUNGER, SPOTITON, ZEISS PLUNGE FREEZER CRYOBOX
+   * CRYOSOL VITROJET, EMS-002 RAPID IMMERSION FREEZER, FEI VITROBOT MARK I, FEI VITROBOT MARK II, FEI VITROBOT MARK III, FEI VITROBOT MARK IV, GATAN CRYOPLUNGE 3, HOMEMADE PLUNGER, LEICA EM CPC, LEICA EM GP, LEICA KF80, LEICA PLUNGER, REICHERT-JUNG PLUNGER, SPOTITON, ZEISS PLUNGE FREEZER CRYOBOX
    *
    */
   readonly instrument?: Maybe<Scalars['String']['output']>;
@@ -3541,7 +3541,7 @@ export type PdbxChemCompAudit = {
    * The action associated with this audit record.
    *
    * Allowable values:
-   * Create component, Initial release, Modify aromatic_flag, Modify atom id, Modify charge, Modify component atom id, Modify component comp_id, Modify coordinates, Modify descriptor, Modify formal charge, Modify formula, Modify identifier, Modify internal type, Modify leaving atom flag, Modify linking type, Modify model coordinates code, Modify name, Modify one letter code, Modify parent residue, Modify processing site, Modify subcomponent list, Modify synonyms, Modify value order, Obsolete component, Other modification
+   * Create component, Initial release, Modify aromatic_flag, Modify atom id, Modify backbone, Modify charge, Modify component atom id, Modify component comp_id, Modify coordinates, Modify descriptor, Modify formal charge, Modify formula, Modify identifier, Modify internal type, Modify leaving atom flag, Modify linking type, Modify model coordinates code, Modify name, Modify one letter code, Modify parent residue, Modify processing site, Modify subcomponent list, Modify synonyms, Modify value order, Obsolete component, Other modification
    *
    */
   readonly action_type?: Maybe<Scalars['String']['output']>;
@@ -3708,7 +3708,7 @@ export type PdbxDatabaseRelated = {
    * The identifying content type of the related entry.
    *
    * Allowable values:
-   * associated EM volume, associated NMR restraints, associated SAS data, associated structure factors, complete structure, derivative structure, ensemble, minimized average structure, native structure, other, other EM volume, protein target sequence and/or protocol data, re-refinement, representative structure, split, unspecified
+   * associated EM volume, associated NMR restraints, associated SAS data, associated structure factors, complete structure, consensus EM volume, derivative structure, ensemble, focused EM volume, minimized average structure, native structure, other, other EM volume, protein target sequence and/or protocol data, re-refinement, representative structure, split, unspecified
    *
    */
   readonly content_type: Scalars['String']['output'];
@@ -4038,7 +4038,7 @@ export type PdbxInitialRefinementModel = {
    * This item identifies the resource of initial model used for refinement
    *
    * Allowable values:
-   * AlphaFold, ITasser, ModelArchive, Modeller, Other, PDB, RoseTTAFold, SwissModel
+   * AlphaFold, ITasser, ModelArchive, Modeller, Other, PDB, PDB-Dev, RoseTTAFold, SwissModel
    *
    */
   readonly source_name?: Maybe<Scalars['String']['output']>;
@@ -7822,7 +7822,7 @@ export type RcsbChemCompRelated = {
    * The resource name for the related chemical reference.
    *
    * Allowable values:
-   * CAS, CCDC/CSD, ChEBI, ChEMBL, DrugBank, Pharos, PubChem, RESID
+   * CAS, CCDC/CSD, COD, ChEBI, ChEMBL, DrugBank, Pharos, PubChem, RESID
    *
    */
   readonly resource_name?: Maybe<Scalars['String']['output']>;

+ 3 - 0
src/mol-geo/geometry/spheres/spheres.ts

@@ -150,6 +150,7 @@ export namespace Spheres {
         xrayShaded: PD.Select<boolean | 'inverted'>(false, [[false, 'Off'], [true, 'On'], ['inverted', 'Inverted']], BaseGeometry.ShadingCategory),
         transparentBackfaces: PD.Select('off', PD.arrayToOptions(['off', 'on', 'opaque']), BaseGeometry.ShadingCategory),
         solidInterior: PD.Boolean(true, BaseGeometry.ShadingCategory),
+        clipPrimitive: PD.Boolean(false, { ...BaseGeometry.ShadingCategory, description: 'Clip whole sphere instead of cutting it.' }),
         approximate: PD.Boolean(false, { ...BaseGeometry.ShadingCategory, description: 'Faster rendering, but has artifacts.' }),
         alphaThickness: PD.Numeric(0, { min: 0, max: 20, step: 1 }, { ...BaseGeometry.ShadingCategory, description: 'If not zero, adjusts alpha for radius.' }),
         bumpFrequency: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
@@ -236,6 +237,7 @@ export namespace Spheres {
             dXrayShaded: ValueCell.create(props.xrayShaded === 'inverted' ? 'inverted' : props.xrayShaded === true ? 'on' : 'off'),
             dTransparentBackfaces: ValueCell.create(props.transparentBackfaces),
             dSolidInterior: ValueCell.create(props.solidInterior),
+            dClipPrimitive: ValueCell.create(props.clipPrimitive),
             dApproximate: ValueCell.create(props.approximate),
             uAlphaThickness: ValueCell.create(props.alphaThickness),
             uBumpFrequency: ValueCell.create(props.bumpFrequency),
@@ -260,6 +262,7 @@ export namespace Spheres {
         ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded === 'inverted' ? 'inverted' : props.xrayShaded === true ? 'on' : 'off');
         ValueCell.updateIfChanged(values.dTransparentBackfaces, props.transparentBackfaces);
         ValueCell.updateIfChanged(values.dSolidInterior, props.solidInterior);
+        ValueCell.updateIfChanged(values.dClipPrimitive, props.clipPrimitive);
         ValueCell.updateIfChanged(values.dApproximate, props.approximate);
         ValueCell.updateIfChanged(values.uAlphaThickness, props.alphaThickness);
         ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);

+ 1 - 0
src/mol-gl/renderable/spheres.ts

@@ -24,6 +24,7 @@ export const SpheresSchema = {
     dXrayShaded: DefineSpec('string', ['off', 'on', 'inverted']),
     dTransparentBackfaces: DefineSpec('string', ['off', 'on', 'opaque']),
     dSolidInterior: DefineSpec('boolean'),
+    dClipPrimitive: DefineSpec('boolean'),
     dApproximate: DefineSpec('boolean'),
     uAlphaThickness: UniformSpec('f'),
     uBumpFrequency: UniformSpec('f', 'material'),

+ 4 - 3
src/mol-gl/shader/spheres.frag.ts

@@ -51,7 +51,7 @@ bool SphereImpostor(out vec3 modelPos, out vec3 cameraPos, out vec3 cameraNormal
 
     bool objectClipped = false;
 
-    #if defined(dClipVariant_pixel) && dClipObjectCount != 0
+    #if !defined(dClipPrimitive) && defined(dClipVariant_pixel) && dClipObjectCount != 0
         if (clipTest(vec4(modelPos, 0.0))) {
             objectClipped = true;
             fragmentDepth = -1.0;
@@ -85,7 +85,6 @@ bool SphereImpostor(out vec3 modelPos, out vec3 cameraPos, out vec3 cameraNormal
 void main(void){
     vec3 cameraNormal;
     float fragmentDepth;
-    bool clipped = false;
 
     #ifdef dApproximate
         vec3 pointDir = -vPointViewPosition - vPoint;
@@ -112,7 +111,9 @@ void main(void){
         vec3 vViewPosition = cameraPos;
     #endif
 
-    #include clip_pixel
+    #if !defined(dClipPrimitive) && defined(dClipVariant_pixel) && dClipObjectCount != 0
+        #include clip_pixel
+    #endif
     #include assign_material_color
 
     #if defined(dRenderVariant_pick)

+ 8 - 1
src/mol-gl/shader/spheres.vert.ts

@@ -110,6 +110,13 @@ void main(void){
         gl_Position.z = (uProjection * vec4(mvPosition.xyz, 1.0)).z;
     }
 
-    #include clip_instance
+    #if defined(dClipPrimitive) && !defined(dClipVariant_instance) && dClipObjectCount != 0
+        if (clipTest(vec4(vModelPosition.xyz, 0.0))) {
+            // move out of [ -w, +w ] to 'discard' in vert shader
+            gl_Position.z = 2.0 * gl_Position.w;
+        }
+    #else
+        #include clip_instance
+    #endif
 }
 `;

+ 1 - 1
src/mol-gl/webgl/extensions.ts

@@ -144,7 +144,7 @@ export function createExtensions(gl: GLRenderingContext): WebGLExtensions {
     }
     const clipControl = getClipControl(gl);
     if (isDebugMode && clipControl === null) {
-        console.log('Could not find support for "clipControl"');
+        console.log('Could not find support for "clip_control"');
     }
 
     const noNonInstancedActiveAttribs = getNoNonInstancedActiveAttribs(gl);

+ 1 - 1
src/mol-io/reader/cif/schema/bird.ts

@@ -1,7 +1,7 @@
 /**
  * Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
- * Code-generated 'BIRD' schema file. Dictionary versions: mmCIF 5.379, IHM 1.23, MA 1.4.5.
+ * Code-generated 'BIRD' schema file. Dictionary versions: mmCIF 5.381, IHM 1.23, MA 1.4.5.
  *
  * @author molstar/ciftools package
  */

+ 1 - 1
src/mol-io/reader/cif/schema/ccd.ts

@@ -1,7 +1,7 @@
 /**
  * Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
- * Code-generated 'CCD' schema file. Dictionary versions: mmCIF 5.379, IHM 1.23, MA 1.4.5.
+ * Code-generated 'CCD' schema file. Dictionary versions: mmCIF 5.381, IHM 1.23, MA 1.4.5.
  *
  * @author molstar/ciftools package
  */

+ 1 - 1
src/mol-io/reader/cif/schema/mmcif.ts

@@ -1,7 +1,7 @@
 /**
  * Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
- * Code-generated 'mmCIF' schema file. Dictionary versions: mmCIF 5.379, IHM 1.23, MA 1.4.5.
+ * Code-generated 'mmCIF' schema file. Dictionary versions: mmCIF 5.381, IHM 1.23, MA 1.4.5.
  *
  * @author molstar/ciftools package
  */

File diff suppressed because it is too large
+ 0 - 0
src/mol-model/structure/model/types/saccharides.ts


+ 58 - 0
src/mol-plugin-state/manager/drag-and-drop.ts

@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+
+import { OpenFiles } from '../actions/file';
+import { Asset } from '../../mol-util/assets';
+import { PluginCommands } from '../../mol-plugin/commands';
+import { PluginContext } from '../../mol-plugin/context';
+
+export type PluginDragAndDropHandler = (files: File[], plugin: PluginContext) => Promise<boolean> | boolean
+
+export class DragAndDropManager {
+    private handlers: [name: string, handler: PluginDragAndDropHandler][] = [];
+
+    addHandler(name: string, handler: PluginDragAndDropHandler) {
+        const index = this.handlers.findIndex(h => h[0] === name);
+        if (index < 0) this.handlers.push([name, handler]);
+        else this.handlers[index][1] = handler;
+    }
+
+    removeHandler(name: string) {
+        const index = this.handlers.findIndex(h => h[0] === name);
+        if (index >= 0) this.handlers.splice(index, 1);
+    }
+
+    async handle(files: File[]) {
+        for (let i = this.handlers.length - 1; i >= 0; i--) {
+            const handler = this.handlers[i][1];
+            const handled = await handler(files, this.plugin);
+            if (handled) return;
+        }
+
+        defaultDragAndDropHandler(this.plugin, files);
+    }
+
+    constructor(public plugin: PluginContext) {
+    }
+}
+
+function defaultDragAndDropHandler(plugin: PluginContext, files: File[]) {
+    const sessions = files.filter(f => {
+        const fn = f.name.toLowerCase();
+        return fn.endsWith('.molx') || fn.endsWith('.molj');
+    });
+
+    if (sessions.length > 0) {
+        PluginCommands.State.Snapshots.OpenFile(plugin, { file: sessions[0] });
+    } else {
+        plugin.runTask(plugin.state.data.applyAction(OpenFiles, {
+            files: files.map(f => Asset.File(f)),
+            format: { name: 'auto', params: {} },
+            visuals: true
+        }));
+    }
+}

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

@@ -211,20 +211,7 @@ function dropFiles(ev: React.DragEvent<HTMLDivElement>, plugin: PluginUIContext,
         }
     }
 
-    const sessions = files.filter(f => {
-        const fn = f.name.toLowerCase();
-        return fn.endsWith('.molx') || fn.endsWith('.molj');
-    });
-
-    if (sessions.length > 0) {
-        PluginCommands.State.Snapshots.OpenFile(plugin, { file: sessions[0] });
-    } else {
-        plugin.runTask(plugin.state.data.applyAction(OpenFiles, {
-            files: files.map(f => Asset.File(f)),
-            format: { name: 'auto', params: {} },
-            visuals: true
-        }));
-    }
+    plugin.managers.dragAndDrop.handle(files);
 }
 
 function DragOverlay({ plugin, showDragOverlay }: { plugin: PluginUIContext, showDragOverlay: BehaviorSubject<boolean> }) {

+ 3 - 1
src/mol-plugin/context.ts

@@ -61,6 +61,7 @@ import { PluginToastManager } from './util/toast';
 import { ViewportScreenshotHelper } from './util/viewport-screenshot';
 import { PLUGIN_VERSION, PLUGIN_VERSION_DATE } from './version';
 import { setSaccharideCompIdMapType } from '../mol-model/structure/structure/carbohydrates/constants';
+import { DragAndDropManager } from '../mol-plugin-state/manager/drag-and-drop';
 
 export type PluginInitializedState =
     | { kind: 'no' }
@@ -186,7 +187,8 @@ export class PluginContext {
         lociLabels: void 0 as any as LociLabelManager,
         toast: new PluginToastManager(this),
         asset: new AssetManager(),
-        task: new TaskManager()
+        task: new TaskManager(),
+        dragAndDrop: new DragAndDropManager(this),
     } as const;
 
     readonly events = {

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

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  * @author David Sehnal <david.sehnal@gmail.com>
@@ -34,7 +34,7 @@ export function lociLabel(loci: Loci, options: Partial<LabelOptions> = {}): stri
             return structureElementStatsLabel(StructureElement.Stats.ofLoci(loci), options);
         case 'bond-loci':
             const bond = loci.bonds[0];
-            return bond ? bondLabel(bond) : '';
+            return bond ? bondLabel(bond, options) : '';
         case 'shape-loci':
             return loci.shape.name;
         case 'group-loci':

Some files were not shown because too many files changed in this diff