Quellcode durchsuchen

annotation hover highlight && zoom on click && saguaro update

bioinsilico vor 4 Jahren
Ursprung
Commit
4d43550f19

+ 84 - 66
package-lock.json

@@ -1,6 +1,6 @@
 {
   "name": "@rcsb/rcsb-saguaro-3d",
-  "version": "0.0.9",
+  "version": "0.0.12",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
@@ -1104,9 +1104,9 @@
       }
     },
     "@graphql-codegen/cli": {
-      "version": "1.20.0",
-      "resolved": "https://registry.npmjs.org/@graphql-codegen/cli/-/cli-1.20.0.tgz",
-      "integrity": "sha512-5pLtZoaqEmEui6PR7IArmD23VLD3++UQby6iNe4NFG4eMcRai2raIM0E4a/MSn7SjyfSRguekYMMC5JKS1VgQw==",
+      "version": "1.20.1",
+      "resolved": "https://registry.npmjs.org/@graphql-codegen/cli/-/cli-1.20.1.tgz",
+      "integrity": "sha512-jT5aMxIniER/gg0/sfx+BPmvI2ZAncQ6ZT/xkiFvXcYMiL9tDiAcVYJTmXcRdMTEIZnlzw3rhE4VPYiw1KqruQ==",
       "requires": {
         "@graphql-codegen/core": "1.17.9",
         "@graphql-codegen/plugin-helpers": "^1.18.2",
@@ -1128,7 +1128,7 @@
         "constant-case": "^3.0.3",
         "cosmiconfig": "^7.0.0",
         "debounce": "^1.2.0",
-        "dependency-graph": "^0.9.0",
+        "dependency-graph": "^0.10.0",
         "detect-indent": "^6.0.0",
         "glob": "^7.1.6",
         "graphql-config": "^3.2.0",
@@ -1144,10 +1144,9 @@
         "minimatch": "^3.0.4",
         "mkdirp": "^1.0.4",
         "pascal-case": "^3.1.1",
-        "request": "^2.88.2",
         "string-env-interpolation": "^1.0.1",
         "ts-log": "^2.2.3",
-        "tslib": "~2.0.1",
+        "tslib": "~2.1.0",
         "upper-case": "^2.0.2",
         "valid-url": "^1.0.9",
         "wrap-ansi": "^7.0.0",
@@ -1198,11 +1197,6 @@
             "ansi-regex": "^5.0.0"
           }
         },
-        "tslib": {
-          "version": "2.0.3",
-          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
-          "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
-        },
         "wrap-ansi": {
           "version": "7.0.0",
           "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
@@ -1780,14 +1774,14 @@
       "integrity": "sha512-FM3y6kfJaj5MCoAjdv24EDCTDbuFz+4+pgAunbjYfugwIE4O/xx8mPNji1n/ouG8pHCntSnBr1xwTOensF23Gg=="
     },
     "@rcsb/rcsb-molstar": {
-      "version": "1.0.29",
-      "resolved": "https://registry.npmjs.org/@rcsb/rcsb-molstar/-/rcsb-molstar-1.0.29.tgz",
-      "integrity": "sha512-JEXn0o+RPyki4DCrRWj0JF86QVDFTbjB7q5eXyg5Jg3kyLzpBiqrJd1x4EuyD33XbPbt0o4gBXgc+t54JCiyAQ=="
+      "version": "1.0.32",
+      "resolved": "https://registry.npmjs.org/@rcsb/rcsb-molstar/-/rcsb-molstar-1.0.32.tgz",
+      "integrity": "sha512-ssV6iW62ZZTwqgs2KJLQUIuOHpSieNCStmoNGtyhOYBt6eOYgugA2VtvrJ32dcJdZxamQzLRKTv7y3dyHLaX8A=="
     },
     "@rcsb/rcsb-saguaro": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/@rcsb/rcsb-saguaro/-/rcsb-saguaro-1.0.5.tgz",
-      "integrity": "sha512-MoNlwkacq1X+XxESYqyUk0ASu6R4t3Ps30fK0AU72eFB7pcKCZp3UZWxx+x48Kzs+JoqYRhrPToHfTh6zTP/nQ==",
+      "version": "1.0.13",
+      "resolved": "https://registry.npmjs.org/@rcsb/rcsb-saguaro/-/rcsb-saguaro-1.0.13.tgz",
+      "integrity": "sha512-xlZlwefR/7gCypgeVojRveXm3xm8zraMyf2XDyX0lj3lYgn/OsvNdo9l94ooxMp5cbxXrzWF8TV8lOSFOYYvZA==",
       "requires": {
         "@d3fc/d3fc-sample": "^4.1.0",
         "@popperjs/core": "^2.4.4",
@@ -1801,31 +1795,13 @@
       }
     },
     "@rcsb/rcsb-saguaro-app": {
-      "version": "1.0.14",
-      "resolved": "https://registry.npmjs.org/@rcsb/rcsb-saguaro-app/-/rcsb-saguaro-app-1.0.14.tgz",
-      "integrity": "sha512-pGkz49dU6WZ6OkVYcci5IPQQvFynbH9iD2yWzUOc/qVSHGF7klgOTVey2xZK8C3/YFz5ZSGUZLS5Q50qt6fN6Q==",
+      "version": "1.0.22",
+      "resolved": "https://registry.npmjs.org/@rcsb/rcsb-saguaro-app/-/rcsb-saguaro-app-1.0.22.tgz",
+      "integrity": "sha512-AqAm48NsJ3OyTaBskaYc7br7mL6BHd5+b5VDRrr6nbonuuFC/OK+PFNHJOfFi2VeI8ng5xPDMIuPuUCBYqrSNA==",
       "requires": {
         "@graphql-codegen/cli": "^1.16.0",
-        "@rcsb/rcsb-saguaro": "^1.0.6",
+        "@rcsb/rcsb-saguaro": "^1.0.13",
         "ideogram": "^1.27.0"
-      },
-      "dependencies": {
-        "@rcsb/rcsb-saguaro": {
-          "version": "1.0.6",
-          "resolved": "https://registry.npmjs.org/@rcsb/rcsb-saguaro/-/rcsb-saguaro-1.0.6.tgz",
-          "integrity": "sha512-HdWtDsrRHr9n2Bl7pIZjTDsXWxyscXy+Hv7DipLnggDOgjX6BWYXLSHSQ/Z7xhApzdgpZdweIaXPUYBM3rn+qQ==",
-          "requires": {
-            "@d3fc/d3fc-sample": "^4.1.0",
-            "@popperjs/core": "^2.4.4",
-            "@types/d3": "^5.7.2",
-            "d3": "^5.16.0",
-            "d3-shape": "^1.3.7",
-            "react": "^16.13.1",
-            "react-dom": "^16.13.1",
-            "react-transition-group": "^4.4.1",
-            "rxjs": "^6.6.0"
-          }
-        }
       }
     },
     "@samverschueren/stream-to-observable": {
@@ -2810,6 +2786,7 @@
       "version": "0.2.4",
       "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
       "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
+      "dev": true,
       "requires": {
         "safer-buffer": "~2.1.0"
       }
@@ -2864,7 +2841,8 @@
     "assert-plus": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
-      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
+      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+      "dev": true
     },
     "assign-symbols": {
       "version": "1.0.0",
@@ -2899,12 +2877,14 @@
     "aws-sign2": {
       "version": "0.7.0",
       "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
-      "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
+      "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
+      "dev": true
     },
     "aws4": {
       "version": "1.11.0",
       "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
-      "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA=="
+      "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
+      "dev": true
     },
     "babel-loader": {
       "version": "8.2.2",
@@ -2996,6 +2976,7 @@
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
       "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+      "dev": true,
       "requires": {
         "tweetnacl": "^0.14.3"
       }
@@ -3386,7 +3367,8 @@
     "caseless": {
       "version": "0.12.0",
       "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
-      "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
+      "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
+      "dev": true
     },
     "chalk": {
       "version": "4.1.0",
@@ -3875,7 +3857,8 @@
     "core-util-is": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
-      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+      "dev": true
     },
     "cors": {
       "version": "2.8.5",
@@ -4336,6 +4319,7 @@
       "version": "1.14.1",
       "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
       "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+      "dev": true,
       "requires": {
         "assert-plus": "^1.0.0"
       }
@@ -4504,9 +4488,9 @@
       "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
     },
     "dependency-graph": {
-      "version": "0.9.0",
-      "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.9.0.tgz",
-      "integrity": "sha512-9YLIBURXj4DJMFALxXw9K3Y3rwb5Fk0X5/8ipCzaN84+gKxoHK43tVKRNakCQbiEx07E8Uwhuq21BpUagFhZ8w=="
+      "version": "0.10.0",
+      "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.10.0.tgz",
+      "integrity": "sha512-c9amUgpgxSi1bE5/sbLwcs5diLD0ygCQYmhfM5H1s5VH1mCsYkcmAL3CcNdv4kdSw6JuMoHeDGzLgj/gAXdWVg=="
     },
     "des.js": {
       "version": "1.0.1",
@@ -4629,6 +4613,7 @@
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
       "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
+      "dev": true,
       "requires": {
         "jsbn": "~0.1.0",
         "safer-buffer": "^2.1.0"
@@ -4940,7 +4925,8 @@
     "extend": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
-      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
+      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+      "dev": true
     },
     "extend-shallow": {
       "version": "3.0.2",
@@ -5046,7 +5032,8 @@
     "extsprintf": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
-      "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
+      "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
+      "dev": true
     },
     "fast-deep-equal": {
       "version": "3.1.3",
@@ -5321,7 +5308,8 @@
     "forever-agent": {
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
-      "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
+      "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+      "dev": true
     },
     "form-data": {
       "version": "3.0.0",
@@ -5545,6 +5533,7 @@
       "version": "0.1.7",
       "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
       "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+      "dev": true,
       "requires": {
         "assert-plus": "^1.0.0"
       }
@@ -5823,12 +5812,14 @@
     "har-schema": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
-      "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
+      "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
+      "dev": true
     },
     "har-validator": {
       "version": "5.1.5",
       "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
       "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
+      "dev": true,
       "requires": {
         "ajv": "^6.12.3",
         "har-schema": "^2.0.0"
@@ -6061,6 +6052,7 @@
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
       "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+      "dev": true,
       "requires": {
         "assert-plus": "^1.0.0",
         "jsprim": "^1.2.2",
@@ -6134,9 +6126,12 @@
       },
       "dependencies": {
         "d3-array": {
-          "version": "2.9.1",
-          "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.9.1.tgz",
-          "integrity": "sha512-Ob7RdOtkqsjx1NWyQHMFLtCSk6/aKTxDdC4ZIolX+O+mDD2RzrsYgAyc0WGAlfYFVELLSilS7w8BtE3PKM8bHg=="
+          "version": "2.11.0",
+          "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.11.0.tgz",
+          "integrity": "sha512-26clcwmHQEdsLv34oNKq5Ia9tQ26Y/4HqS3dQzF42QBUqymZJ+9PORcN1G52bt37NsL2ABoX4lvyYZc+A9Y0zw==",
+          "requires": {
+            "internmap": "^1.0.0"
+          }
         },
         "d3-brush": {
           "version": "2.1.0",
@@ -6417,6 +6412,11 @@
         }
       }
     },
+    "internmap": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.0.tgz",
+      "integrity": "sha512-SdoDWwNOTE2n4JWUsLn4KXZGuZPjPF9yyOGc8bnfWnBQh7BD/l80rzSznKc/r4Y0aQ7z3RTk9X+tV4tHBpu+dA=="
+    },
     "interpret": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
@@ -6629,7 +6629,8 @@
     "is-typedarray": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
-      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
+      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+      "dev": true
     },
     "is-utf8": {
       "version": "0.2.1",
@@ -6704,7 +6705,8 @@
     "isstream": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
-      "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
+      "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
+      "dev": true
     },
     "jest-diff": {
       "version": "25.5.0",
@@ -6756,7 +6758,8 @@
     "jsbn": {
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
-      "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
+      "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
+      "dev": true
     },
     "jsesc": {
       "version": "2.5.2",
@@ -6782,7 +6785,8 @@
     "json-schema": {
       "version": "0.2.3",
       "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
-      "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
+      "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
+      "dev": true
     },
     "json-schema-traverse": {
       "version": "0.4.1",
@@ -6800,7 +6804,8 @@
     "json-stringify-safe": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
-      "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
+      "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
+      "dev": true
     },
     "json-to-pretty-yaml": {
       "version": "1.2.2",
@@ -6862,6 +6867,7 @@
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
       "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
+      "dev": true,
       "requires": {
         "assert-plus": "1.0.0",
         "extsprintf": "1.3.0",
@@ -8156,7 +8162,8 @@
     "oauth-sign": {
       "version": "0.9.0",
       "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
-      "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
+      "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
+      "dev": true
     },
     "object-assign": {
       "version": "4.1.1",
@@ -8519,7 +8526,8 @@
     "performance-now": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
-      "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
+      "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
+      "dev": true
     },
     "picomatch": {
       "version": "2.2.2",
@@ -8785,7 +8793,8 @@
     "psl": {
       "version": "1.8.0",
       "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
-      "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
+      "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==",
+      "dev": true
     },
     "public-encrypt": {
       "version": "4.0.3",
@@ -9228,6 +9237,7 @@
       "version": "2.88.2",
       "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
       "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
+      "dev": true,
       "requires": {
         "aws-sign2": "~0.7.0",
         "aws4": "^1.8.0",
@@ -9255,6 +9265,7 @@
           "version": "2.3.3",
           "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
           "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+          "dev": true,
           "requires": {
             "asynckit": "^0.4.0",
             "combined-stream": "^1.0.6",
@@ -9264,7 +9275,8 @@
         "qs": {
           "version": "6.5.2",
           "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
-          "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
+          "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
+          "dev": true
         }
       }
     },
@@ -9900,6 +9912,7 @@
       "version": "1.16.1",
       "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
       "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
+      "dev": true,
       "requires": {
         "asn1": "~0.2.3",
         "assert-plus": "^1.0.0",
@@ -10395,6 +10408,7 @@
       "version": "2.5.0",
       "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
       "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+      "dev": true,
       "requires": {
         "psl": "^1.1.28",
         "punycode": "^2.1.1"
@@ -10525,6 +10539,7 @@
       "version": "0.6.0",
       "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
       "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+      "dev": true,
       "requires": {
         "safe-buffer": "^5.0.1"
       }
@@ -10532,7 +10547,8 @@
     "tweetnacl": {
       "version": "0.14.5",
       "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
-      "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
+      "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+      "dev": true
     },
     "type-fest": {
       "version": "0.13.1",
@@ -10838,7 +10854,8 @@
     "uuid": {
       "version": "3.4.0",
       "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
-      "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
+      "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+      "dev": true
     },
     "v8-compile-cache": {
       "version": "2.2.0",
@@ -10870,6 +10887,7 @@
       "version": "1.10.0",
       "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
       "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+      "dev": true,
       "requires": {
         "assert-plus": "^1.0.0",
         "core-util-is": "1.0.2",

+ 4 - 4
package.json

@@ -1,6 +1,6 @@
 {
   "name": "@rcsb/rcsb-saguaro-3d",
-  "version": "0.0.12",
+  "version": "0.0.13",
   "description": "RCSB Molstar/Saguaro Web App",
   "main": "build/dist/RcsbFv3DBuilder.js",
   "files": [
@@ -74,9 +74,9 @@
     "webpack-cli": "^3.3.12"
   },
   "dependencies": {
-    "@rcsb/rcsb-molstar": "^1.0.29",
-    "@rcsb/rcsb-saguaro": "^1.0.5",
-    "@rcsb/rcsb-saguaro-app": "^1.0.14",
+    "@rcsb/rcsb-molstar": "^1.0.32",
+    "@rcsb/rcsb-saguaro": "^1.0.13",
+    "@rcsb/rcsb-saguaro-app": "^1.0.22",
     "@types/react": "^16.9.49",
     "molstar": "^1.2.3"
   },

+ 42 - 23
src/RcsbFvSelection/RcsbFvSelection.ts

@@ -4,11 +4,17 @@ export interface ResidueSelectionInterface {
     labelAsymId: string;
     seqIds: Set<number>;
 }
+export interface RegionSelectionInterface{
+    begin:number;
+    end:number;
+    isEmpty?: boolean;
+    source:'structure'|'sequence';
+}
 
 export interface ChainSelectionInterface {
     modelId: string;
     labelAsymId: string;
-    regions: Array<{begin:number;end:number}>;
+    regions: Array<RegionSelectionInterface>;
 }
 
 export class RcsbFvSelection {
@@ -16,7 +22,7 @@ export class RcsbFvSelection {
     private selection: Array<ChainSelectionInterface> = new Array<ChainSelectionInterface>();
     private hover: Array<ChainSelectionInterface> = new Array<ChainSelectionInterface>();
 
-    public setSelectionFromRegion(modelId: string, labelAsymId: string, region: {begin:number, end:number}, mode:'select'|'hover'): void {
+    public setSelectionFromRegion(modelId: string, labelAsymId: string, region: RegionSelectionInterface, mode:'select'|'hover'): void {
         if(mode === 'select'){
             this.selection = new Array<ChainSelectionInterface>();
             this.selection.push({modelId:modelId, labelAsymId:labelAsymId, regions:[region]});
@@ -27,7 +33,7 @@ export class RcsbFvSelection {
 
     }
 
-    public addSelectionFromRegion(modelId: string, labelAsymId: string, region: {begin:number, end:number}, mode:'select'|'hover'): void {
+    public addSelectionFromRegion(modelId: string, labelAsymId: string, region: RegionSelectionInterface, mode:'select'|'hover'): void {
         if(mode === 'select'){
             this.selection.push({modelId:modelId, labelAsymId:labelAsymId, regions:[region]});
         }else{
@@ -35,13 +41,13 @@ export class RcsbFvSelection {
         }
     }
 
-    public setSelectionFromMultipleRegions(regions: {modelId: string, labelAsymId: string, region: {begin:number, end:number}}[], mode:'select'|'hover'): void {
+    public setSelectionFromMultipleRegions(regions: {modelId: string, labelAsymId: string, region: RegionSelectionInterface}[], mode:'select'|'hover'): void {
         regions.forEach(r=>{
             this.addSelectionFromRegion(r.modelId, r.labelAsymId, r.region, mode);
         });
     }
 
-    public setSelectionFromResidueSelection(res: Array<ResidueSelectionInterface>, mode:'select'|'hover'): void {
+    public setSelectionFromResidueSelection(res: Array<ResidueSelectionInterface>, mode:'select'|'hover', source: 'structure'|'sequence'): void {
         const selMap: Map<string,Map<string,Set<number>>> = new Map<string, Map<string, Set<number>>>();
         res.forEach(r=>{
             if(!selMap.has(r.modelId))
@@ -56,14 +62,14 @@ export class RcsbFvSelection {
             this.selection = new Array<ChainSelectionInterface>();
             selMap.forEach((labelMap, modelId)=>{
                 labelMap.forEach((seqSet,labelId)=>{
-                    this.selection.push({modelId:modelId, labelAsymId: labelId, regions:RcsbFvSelection.buildIntervals(seqSet)});
+                    this.selection.push({modelId:modelId, labelAsymId: labelId, regions:RcsbFvSelection.buildIntervals(seqSet, source)});
                 });
             });
         }else{
             this.hover = new Array<ChainSelectionInterface>();
             selMap.forEach((labelMap, modelId)=>{
                 labelMap.forEach((seqSet,labelId)=>{
-                    this.hover.push({modelId:modelId, labelAsymId: labelId, regions:RcsbFvSelection.buildIntervals(seqSet)});
+                    this.hover.push({modelId:modelId, labelAsymId: labelId, regions:RcsbFvSelection.buildIntervals(seqSet, source)});
                 });
             });
         }
@@ -78,26 +84,39 @@ export class RcsbFvSelection {
     }
 
     public getSelectionWithCondition(modelId: string, labelAsymId: string, mode:'select'|'hover'): ChainSelectionInterface | undefined{
-        if(mode === 'select'){
-            const sel: Array<ChainSelectionInterface> = this.selection.filter(d=>(d.modelId===modelId && d.labelAsymId === labelAsymId));
-            if(sel.length > 0)
-                return sel[0]
-        }else{
-            const sel: Array<ChainSelectionInterface> = this.hover.filter(d=>(d.modelId===modelId && d.labelAsymId === labelAsymId));
-            if(sel.length > 0)
-                return sel[0]
-        }
+        const sel: Array<ChainSelectionInterface> = mode === 'select' ?
+            this.selection.filter(d=>(d.modelId===modelId && d.labelAsymId === labelAsymId)) :
+            this.hover.filter(d=>(d.modelId===modelId && d.labelAsymId === labelAsymId));
+        if(sel.length > 0)
+            return {modelId: sel[0].modelId, labelAsymId: sel[0].labelAsymId, regions:[].concat.apply([],sel.map(s=>s.regions))};
     }
 
-    public clearSelection(mode:'select'|'hover'): void {
+    public clearSelection(mode:'select'|'hover', labelAsymId?: string): void {
+        if(labelAsymId == null)
+            if(mode === 'select')
+                this.selection = new Array<ChainSelectionInterface>();
+            else
+                this.hover = new Array<ChainSelectionInterface>();
+        else
+            if(mode === 'select')
+                this.selection = this.selection.filter(r=>r.labelAsymId!=labelAsymId)
+            else
+                this.hover = this.hover.filter(r=>r.labelAsymId!=labelAsymId)
+    }
+
+    public selectionSource(mode:'select'|'hover', region:{modelId:string;labelAsymId:string;begin:number;end:number;}): 'structure'|'sequence'|undefined{
         if(mode === 'select')
-            this.selection = new Array<ChainSelectionInterface>();
+            return this.selection
+                .filter(r=>(r.modelId === region.modelId && r.labelAsymId === region.labelAsymId))[0]?.regions
+                .filter(r=>(r.begin === region. begin && r.end === region.end))[0]?.source;
         else
-            this.hover = new Array<ChainSelectionInterface>();
+            return this.hover
+                .filter(r=>(r.modelId === region.modelId && r.labelAsymId === region.labelAsymId))[0]?.regions
+                .filter(r=>(r.begin === region. begin && r.end === region.end))[0]?.source;
     }
 
-    private static buildIntervals(ids: Set<number>): Array<{begin:number,end:number}>{
-        const out: Array<{begin:number,end:number}> = new Array<{begin: number; end: number}>();
+    private static buildIntervals(ids: Set<number>, source: 'structure'|'sequence'): Array<RegionSelectionInterface>{
+        const out: Array<RegionSelectionInterface> = new Array<RegionSelectionInterface>();
         const sorted: Array<number> = Array.from(ids).sort((a,b)=>(a-b));
         let begin: number = sorted.shift()!;
         let end: number = begin;
@@ -105,12 +124,12 @@ export class RcsbFvSelection {
             if(n==(end+1)){
                 end = n;
             }else{
-                out.push({begin:begin,end:end});
+                out.push({begin:begin,end:end,source:source});
                 begin = n;
                 end = n;
             }
         }
-        out.push({begin:begin,end:end});
+        out.push({begin:begin,end:end,source:source});
         return out;
     }
 

+ 123 - 37
src/RcsbFvSequence/SequenceViews/AssemblyView.tsx

@@ -12,6 +12,7 @@ import {InstanceSequenceOnchangeInterface} from "@rcsb/rcsb-saguaro-app/build/di
 import {RcsbFvTrackDataElementInterface} from "@rcsb/rcsb-saguaro";
 import {ChainSelectionInterface} from "../../RcsbFvSelection/RcsbFvSelection";
 import {SaguaroPluginModelMapType} from "../../RcsbFvStructure/StructurePlugins/SaguaroPluginInterface";
+import {SelectionInterface} from "@rcsb/rcsb-saguaro/build/RcsbBoard/RcsbSelection";
 
 export interface AssemblyViewInterface {
     entryId: string;
@@ -19,11 +20,13 @@ export interface AssemblyViewInterface {
 
 export class AssemblyView extends AbstractView<AssemblyViewInterface & AbstractViewInterface, AssemblyViewInterface & AbstractViewInterface>{
 
-    private currentLabelId: string;
+    private currentLabelAsymId: string;
     private currentEntryId: string;
     private currentModelId: string;
     private createComponentThresholdBatch = 3;
     private createComponentThreshold: number = 9;
+    private innerSelectionFlag: boolean = false;
+    //private readonly componentSet = new Map<string, {current: Set<string>, previous: Set<string>}>();
 
     constructor(props: AssemblyViewInterface & AbstractViewInterface) {
         super({
@@ -48,38 +51,60 @@ export class AssemblyView extends AbstractView<AssemblyViewInterface & AbstractV
         const trackWidth: number = width - 190 - 55;
         setBoardConfig({
             trackWidth: trackWidth,
-            elementClickCallBack:(e: RcsbFvTrackDataElementInterface)=>{
-                this.props.plugin.clearSelection('select');
+            elementClickCallBack:(e:RcsbFvTrackDataElementInterface)=>{
                 this.props.plugin.removeComponent();
                 if(e == null)
                     return;
                 const x = e.begin;
                 const y = e.end ?? e.begin;
                 if(e.isEmpty){
-                    this.props.plugin.selectSet(
-                        [{modelId: this.currentModelId, asymId: this.currentLabelId, position: x},{modelId: this.currentModelId, asymId: this.currentLabelId, position: y}], 'select'
-                    );
-                    this.props.selection.setSelectionFromMultipleRegions(
-                        [
-                            {modelId: this.currentModelId, labelAsymId: this.currentLabelId, region: {begin:x, end: x}},
-                            {modelId: this.currentModelId, labelAsymId: this.currentLabelId, region: {begin: y, end: y}}
-                            ], 'select'
-                    );
-                    this.props.plugin.createComponentFromSet(this.currentModelId,[{asymId:this.currentLabelId, position:x}, {asymId:this.currentLabelId, position:y}], 'spacefill');
+                    const componentId: string = this.currentLabelAsymId +":"+ ((x === y) ? x.toString() : x.toString()+","+y.toString());
+                    this.props.plugin.createComponentFromSet(
+                        componentId,
+                        this.currentModelId,
+                        [{asymId: this.currentLabelAsymId, position: x}, {asymId: this.currentLabelAsymId, position: y}],
+                        'ball-and-stick'
+                    )
+                    this.props.plugin.focusPositions(this.currentModelId, this.currentLabelAsymId, [x,y]);
                 }else{
-                    this.props.plugin.selectRange(this.currentModelId, this.currentLabelId,x,y, 'select');
-                    this.props.selection.setSelectionFromRegion(this.currentModelId, this.currentLabelId, {begin:x, end:y}, 'select');
+                    const componentId: string = this.currentLabelAsymId +":"+ (x === y ? x.toString() : x.toString()+"-"+y.toString());
                     if((y-x)<this.createComponentThreshold){
-                        this.props.plugin.createComponentFromRange(this.currentModelId, this.currentLabelId, x, y, 'spacefill');
+                        this.props.plugin.createComponentFromRange(componentId, this.currentModelId, this.currentLabelAsymId, x, y, 'ball-and-stick')
                     }
+                    this.props.plugin.focusRange(this.currentModelId, this.currentLabelAsymId, x, y);
                 }
             },
+            selectionChangeCallBack:(selection: Array<SelectionInterface>)=>{
+                if(this.innerSelectionFlag)
+                    return;
+                this.props.plugin.clearSelection('select', {modelId: this.currentModelId, labelAsymId: this.currentLabelAsymId});
+                this.props.selection.clearSelection('select', this.currentLabelAsymId);
+                if(selection == null || selection.length === 0) {
+                    this.props.plugin.pluginCall(plugin => {
+                        plugin.managers.camera.reset();
+                    });
+                    return;
+                }
+                this.select(selection);
+            },
             highlightHoverPosition:true,
+            highlightHoverElement:true,
             highlightHoverCallback:(selection: RcsbFvTrackDataElementInterface[])=>{
                 this.props.plugin.clearSelection('hover');
-                if(selection != null && selection.length > 0)
-                    this.props.plugin.selectRange(this.currentModelId, this.currentLabelId,selection[0].begin,selection[0].end ?? selection[0].begin,'hover');
-            }
+                if(selection != null && selection.length > 0) {
+                    if(selection[0].isEmpty){
+                        const selectionList = [{modelId: this.currentModelId, asymId: this.currentLabelAsymId, position: selection[0].begin}];
+                        if(selection[0].end != null) selectionList.push({modelId: this.currentModelId, asymId: this.currentLabelAsymId, position: selection[0].end})
+                        this.props.plugin.selectSet(
+                            selectionList,
+                            'hover',
+                            'add'
+                        );
+                    }else {
+                        this.props.plugin.selectRange(this.currentModelId, this.currentLabelAsymId, selection[0].begin, selection[0].end ?? selection[0].begin, 'hover', 'set');
+                    }
+                }
+            },
         });
     }
 
@@ -97,17 +122,27 @@ export class AssemblyView extends AbstractView<AssemblyViewInterface & AbstractV
     }
 
     private pluginSelectCallback(mode:'select'|'hover'): void{
-        const sel: ChainSelectionInterface | undefined = this.props.selection.getSelectionWithCondition(this.currentModelId, this.currentLabelId, mode)
-
         if(getRcsbFv(this.pfvDivId) == null)
             return;
-        if(sel == null) {
+        this.innerSelectionFlag = true;
+        if(mode === 'select')
+            this.props.plugin.removeComponent();
+        const allSel: Array<ChainSelectionInterface> | undefined = this.props.selection.getSelection(mode);
+        if(allSel == null || allSel.length ===0) {
             getRcsbFv(this.pfvDivId).clearSelection(mode);
             if(mode === 'select')
-                this.props.plugin.removeComponent();
-        } else {
-            getRcsbFv(this.pfvDivId).setSelection({elements: sel.regions, mode: mode});
+                this.props.plugin.pluginCall(plugin => {
+                    plugin.managers.camera.reset();
+                });
+        }else{
+            const sel: ChainSelectionInterface | undefined = this.props.selection.getSelectionWithCondition(this.currentModelId, this.currentLabelAsymId, mode);
+            if (sel == null) {
+                getRcsbFv(this.pfvDivId).clearSelection(mode);
+            } else {
+                getRcsbFv(this.pfvDivId).setSelection({elements: sel.regions, mode: mode});
+            }
         }
+        this.innerSelectionFlag = false;
     }
 
     protected modelChangeCallback(modelMap:SaguaroPluginModelMapType): void {
@@ -116,7 +151,7 @@ export class AssemblyView extends AbstractView<AssemblyViewInterface & AbstractV
         modelMap.forEach((v,k)=>{
             onChangeCallback.set(v.entryId,(x)=>{
                 this.currentEntryId = v.entryId;
-                this.currentLabelId = x.asymId;
+                this.currentLabelAsymId = x.asymId;
                 this.currentModelId = k;
                 setTimeout(()=>{
                     this.structureSelectionCallback();
@@ -126,17 +161,18 @@ export class AssemblyView extends AbstractView<AssemblyViewInterface & AbstractV
         });
         unmount(this.pfvDivId);
         const entryId: string = Array.from(modelMap.values()).map(d=>d.entryId)[0];
-        buildInstanceSequenceFv(
-            this.pfvDivId,
-            RcsbFvDOMConstants.SELECT_INSTANCE_PFV_ID,
-            entryId,
-            undefined,
-            onChangeCallback.get(entryId),
-            filterInstances.get(entryId)
-        ).then(()=>{
-            const length: number = getRcsbFv(this.pfvDivId).getBoardConfig().length ?? 0;
-            this.createComponentThreshold = (((Math.floor(length/100))+1)*this.createComponentThresholdBatch)-1;
-        });
+        if(entryId != null)
+            buildInstanceSequenceFv(
+                this.pfvDivId,
+                RcsbFvDOMConstants.SELECT_INSTANCE_PFV_ID,
+                entryId,
+                undefined,
+                onChangeCallback.get(entryId),
+                filterInstances.get(entryId)
+            ).then(()=>{
+                const length: number = getRcsbFv(this.pfvDivId).getBoardConfig().length ?? 0;
+                this.createComponentThreshold = (((Math.floor(length/100))+1)*this.createComponentThresholdBatch)-1;
+            });
     }
 
     protected updateDimensions(): void{
@@ -145,4 +181,54 @@ export class AssemblyView extends AbstractView<AssemblyViewInterface & AbstractV
         getRcsbFv(this.pfvDivId).updateBoardConfig({boardConfigData:{trackWidth:trackWidth}});
     }
 
+    private select(selection: Array<SelectionInterface>): void{
+        selection.forEach(d=>{
+            const e: RcsbFvTrackDataElementInterface = d.rcsbFvTrackDataElement;
+            const x = e.begin;
+            const y = e.end ?? e.begin;
+            if(e.isEmpty){
+                this.props.plugin.selectSet(
+                    [{modelId: this.currentModelId, asymId: this.currentLabelAsymId, position: x},{modelId: this.currentModelId, asymId: this.currentLabelAsymId, position: y}], 'select',
+                    'add'
+                );
+                this.props.selection.addSelectionFromRegion(this.currentModelId, this.currentLabelAsymId, {begin:x, end:y, isEmpty: true, source: 'sequence'}, 'select');
+            }else{
+                this.props.plugin.selectRange(this.currentModelId, this.currentLabelAsymId,x,y, 'select', 'add');
+                this.props.selection.addSelectionFromRegion(this.currentModelId, this.currentLabelAsymId, {begin:x, end:y, source: 'sequence'}, 'select');
+            }
+        });
+    }
+
+    /*private removeComponents(labelAsymId?:string){
+        if(labelAsymId != null){
+            this.componentSet.get(labelAsymId)?.current.forEach(componentId=>{
+                this.props.plugin.removeComponent(componentId);
+            });
+        }else{
+            Array.from(this.componentSet.keys()).forEach(labelAsymId=>{
+                this.componentSet.get(labelAsymId)?.current.forEach(componentId=>{
+                    this.props.plugin.removeComponent(componentId);
+                });
+            });
+        }
+    }
+
+    private removeObsoleteComponents(): void{
+        this.componentSet.get(this.currentLabelAsymId)?.previous.forEach(componentId=>{
+            if(!this.componentSet.get(this.currentLabelAsymId)?.current.has(componentId)) {
+                this.props.plugin.removeComponent(componentId);
+            }
+        });
+    }
+
+    private resetComponentKeys(): void {
+        if(!this.componentSet.has(this.currentLabelAsymId))
+            this.componentSet.set(this.currentLabelAsymId, {current: new Set<string>(), previous: new Set<string>()});
+        this.componentSet.get(this.currentLabelAsymId)?.previous.clear();
+        this.componentSet.get(this.currentLabelAsymId)?.current.forEach(e=>{
+            this.componentSet.get(this.currentLabelAsymId)?.previous.add(e);
+        });
+        this.componentSet.get(this.currentLabelAsymId)?.current.clear();
+    }*/
+
 }

+ 97 - 25
src/RcsbFvStructure/StructurePlugins/MolstarPlugin.ts

@@ -11,7 +11,12 @@ import {Loci} from "molstar/lib/mol-model/loci";
 import {Mat4} from "molstar/lib/mol-math/linear-algebra";
 import {BuiltInTrajectoryFormat} from "molstar/lib/mol-plugin-state/formats/trajectory";
 import {PluginState} from "molstar/lib/mol-plugin/state";
-import {Structure, StructureElement, StructureProperties as SP } from "molstar/lib/mol-model/structure";
+import {
+    Structure,
+    StructureElement,
+    StructureProperties as SP,
+    StructureSelection
+} from "molstar/lib/mol-model/structure";
 import {OrderedSet} from "molstar/lib/mol-data/int";
 import { PluginStateObject as PSO } from 'molstar/lib/mol-plugin-state/objects';
 import {State, StateSelection} from "molstar/lib/mol-state";
@@ -20,7 +25,9 @@ import {RcsbFvSelection, ResidueSelectionInterface} from "../../RcsbFvSelection/
 import {AbstractPlugin} from "./AbstractPlugin";
 import {Subscription} from "rxjs";
 import {InteractivityManager} from "molstar/lib/mol-plugin-state/manager/interactivity";
-import {StateBuilder} from "molstar/lib/mol-state/state/builder";
+import {Script} from "molstar/lib/mol-script/script";
+import {MolScriptBuilder} from "molstar/lib/mol-script/language/builder";
+import {SetUtils} from "molstar/lib/mol-util/set";
 
 export enum LoadMethod {
     loadPdbId = "loadPdbId",
@@ -49,12 +56,13 @@ interface LoadParams {
 
 export class MolstarPlugin extends AbstractPlugin implements SaguaroPluginInterface, SaguaroPluginPublicInterface {
     private plugin: Viewer;
-    private innerSelectionFlag: number = 0;
+    private innerSelectionFlag: boolean = false;
     private loadingFlag: boolean = false;
     private modelChangeCallback: (chainMap:SaguaroPluginModelMapType)=>void;
     private modelMap: Map<string,string|undefined> = new Map<string, string>();
     private selectCallbackSubs: Subscription;
     private modelChangeCallbackSubs: Subscription;
+    private readonly componentSet: Set<string> = new Set<string>();
 
     constructor(props: RcsbFvSelection) {
         super(props);
@@ -124,29 +132,91 @@ export class MolstarPlugin extends AbstractPlugin implements SaguaroPluginInterf
     public setBackground(color: number) {
     }
 
-    public selectRange(modelId:string, asymId: string, begin: number, end: number, mode: 'select'|'hover'): void {
+    public selectRange(modelId:string, asymId: string, begin: number, end: number, mode: 'select'|'hover', operation:'add'|'set'): void {
         if(mode == null || mode === 'select') {
-            this.innerSelectionFlag += 1;
+            this.innerSelectionFlag = true;
         }
-        this.plugin.select(this.getModelId(modelId), asymId, begin, end, mode);
+        this.plugin.select(this.getModelId(modelId), asymId, begin, end, mode, operation);
+        this.innerSelectionFlag = false;
     }
-    public selectSet(selection: Array<{modelId:string; asymId: string; position: number;}>, mode: 'select'|'hover'): void {
+    public selectSet(selection: Array<{modelId:string; asymId: string; position: number;}>, mode: 'select'|'hover', operation:'add'|'set'): void {
         if(mode == null || mode === 'select') {
-            this.innerSelectionFlag += 1;
+            this.innerSelectionFlag = true;
+        }
+        this.plugin.select(selection.map(r=>{return{modelId: this.getModelId(r.modelId), position:r.position, asymId: r.asymId}}), mode, operation);
+        this.innerSelectionFlag = false;
+    }
+
+    public focusPositions(modelId: string, asymId: string, positions:Array<number>): void{
+        const data: Structure | undefined = getStructureWithModelId(this.plugin.getPlugin().managers.structure.hierarchy.current.structures, this.getModelId(modelId));
+        if (data == null) return;
+        const sel: StructureSelection = Script.getStructureSelection(Q => Q.struct.generator.atomGroups({
+            'chain-test': Q.core.rel.eq([asymId, MolScriptBuilder.ammp('label_asym_id')]),
+            'residue-test': Q.core.set.has([MolScriptBuilder.set(...SetUtils.toArray(new Set(positions))), MolScriptBuilder.ammp('label_seq_id')])
+        }), data);
+        const loci: Loci = StructureSelection.toLociWithSourceUnits(sel);
+        if(!StructureElement.Loci.isEmpty(loci))
+            this.plugin.getPlugin().managers.camera.focusLoci(loci);
+        else
+            this.plugin.getPlugin().managers.camera.reset();
+    }
+
+    public focusRange(modelId: string, asymId: string, begin: number, end: number): void{
+        const seqIds: Array<number> = new Array<number>();
+        for(let n = begin; n <= end; n++){
+            seqIds.push(n);
         }
-        this.plugin.select(selection.map(r=>{return{modelId: this.getModelId(r.modelId), position:r.position, asymId: r.asymId}}), mode);
+        this.focusPositions(modelId, asymId, seqIds);
+    }
+
+    public createComponentFromRange(componentId: string, modelId:string, asymId: string, begin: number, end : number, representationType: 'ball-and-stick' | 'spacefill' | 'gaussian-surface' | 'cartoon'): Promise<void> {
+        return this.plugin.createComponentFromRange(componentId, this.getModelId(modelId), asymId, begin, end, representationType).then(()=>{
+            this.componentSet.add(componentId);
+        });
+    }
+
+
+    public createComponentFromSet(componentId: string, modelId:string, residues: Array<{asymId: string; position: number;}>, representationType: 'ball-and-stick' | 'spacefill' | 'gaussian-surface' | 'cartoon'): Promise<void> {
+        return this.plugin.createComponentFromSet(componentId, this.getModelId(modelId), residues, representationType).then(()=>{
+            this.componentSet.add(componentId);
+        });
     }
 
-    public createComponentFromRange(modelId:string, asymId: string, begin: number, end : number, representationType: 'ball-and-stick' | 'spacefill' | 'gaussian-surface' | 'cartoon'): void {
-        this.plugin.createComponentFromRange("1D annotation", this.getModelId(modelId), asymId, begin, end, representationType);
+    public isComponent(componentId: string): boolean{
+        for(const c of this.plugin.getPlugin().managers.structure.hierarchy.currentComponentGroups){
+            for(const comp of c){
+                if(comp.cell.obj?.label === componentId) {
+                    return true;
+                }
+            }
+        }
+        return false;
     }
 
-    public createComponentFromSet(modelId:string, residues: Array<{asymId: string; position: number;}>, representationType: 'ball-and-stick' | 'spacefill' | 'gaussian-surface' | 'cartoon'): void {
-        this.plugin.createComponentFromSet("1D annotation", this.getModelId(modelId), residues, representationType);
+    public getComponentSet(): Set<string>{
+        const out: Set<string> = new Set<string>();
+        this.plugin.getPlugin().managers.structure.hierarchy.currentComponentGroups.forEach(c=>{
+            for(const comp of c){
+                if(comp.cell.obj?.label != null && out.has(comp.cell.obj?.label)) {
+                    break;
+                }else if(comp.cell.obj?.label != null){
+                    out.add(comp.cell.obj?.label);
+                }
+            }
+        });
+        return out;
     }
 
-    public removeComponent(): void{
-        this.plugin.removeComponent("1D annotation");
+    public removeComponent(componentId?: string): void{
+        if(componentId == null){
+            this.componentSet.forEach(id=>{
+                this.plugin.removeComponent(id);
+            })
+            this.componentSet.clear();
+        }else{
+            this.plugin.removeComponent(componentId);
+            this.componentSet.delete(componentId);
+        }
     }
 
     public setHoverCallback(g:()=>void){
@@ -170,15 +240,14 @@ export class MolstarPlugin extends AbstractPlugin implements SaguaroPluginInterf
                     });
                 }
             }
-            this.selection.setSelectionFromResidueSelection(sequenceData, 'hover');
+            this.selection.setSelectionFromResidueSelection(sequenceData, 'hover', 'structure');
             g();
         });
     }
 
-    public setSelectCallback(g:()=>void){
+    public setSelectCallback(g:(flag?:boolean)=>void){
         this.selectCallbackSubs = this.plugin.getPlugin().managers.structure.selection.events.changed.subscribe(()=>{
-            if(this.innerSelectionFlag > 0) {
-                this.innerSelectionFlag -= 1;
+            if(this.innerSelectionFlag) {
                 return;
             }
             const sequenceData: Array<ResidueSelectionInterface> = new Array<ResidueSelectionInterface>();
@@ -204,17 +273,20 @@ export class MolstarPlugin extends AbstractPlugin implements SaguaroPluginInterf
 
                 }
             }
-            this.selection.setSelectionFromResidueSelection(sequenceData, 'select');
+            this.selection.setSelectionFromResidueSelection(sequenceData, 'select', 'structure');
             g();
         });
     }
 
-    public clearSelection(mode:'select'|'hover'): void {
+    public clearSelection(mode:'select'|'hover', option?:{modelId:string; labelAsymId:string;}): void {
         if(mode === 'select') {
-            this.innerSelectionFlag += 1;
+            this.innerSelectionFlag = true;
         }
-        this.plugin.clearSelection(mode);
-
+        if(option != null)
+            this.plugin.clearSelection(mode, {modelId: this.getModelId(option.modelId), labelAsymId: option.labelAsymId});
+        else
+            this.plugin.clearSelection(mode);
+        this.innerSelectionFlag = false;
     }
 
     public pluginCall(f: (plugin: PluginContext) => void){
@@ -223,7 +295,7 @@ export class MolstarPlugin extends AbstractPlugin implements SaguaroPluginInterf
 
     public setModelChangeCallback(f:(modelMap:SaguaroPluginModelMapType)=>void){
         this.modelChangeCallback = f;
-        this. modelChangeCallbackSubs = this.plugin.getPlugin().state.events.object.updated.subscribe((o)=>{
+        this.modelChangeCallbackSubs = this.plugin.getPlugin().state.events.object.updated.subscribe((o)=>{
             if(this.loadingFlag)
                 return;
             if(o.action === "in-place" && o.ref === "ms-plugin.create-structure-focus-representation") {

+ 13 - 15
src/RcsbFvStructure/StructurePlugins/SaguaroPluginInterface.ts

@@ -3,28 +3,26 @@ import {PluginContext} from "molstar/lib/mol-plugin/context";
 
 export type SaguaroPluginModelMapType = Map<string,{entryId: string; chains:Array<{label:string, auth:string}>;}>;
 
-export interface SaguaroPluginInterface {
+export interface SaguaroPluginInterface extends SaguaroPluginPublicInterface{
     init: (elementId: string, props?: any) => void;
     load: (args: LoadMolstarInterface) => void;
     pluginCall: (f:(plugin: PluginContext)=>void) => void;
     clear: () => void;
-    setSelectCallback: (g:()=>void)=>void;
+    setSelectCallback: (g:(flag?:boolean)=>void)=>void;
     setModelChangeCallback: (f:(modelMap:SaguaroPluginModelMapType)=>void)=>void;
-    setHoverCallback:(g:()=>void)=>void;
+    setHoverCallback:(g:(flag?:boolean)=>void)=>void;
     unsetCallbacks:()=>void;
-    selectRange: (modelId:string, asymId: string, x: number, y: number, mode: 'select'|'hover') => void;
-    selectSet: (selection: Array<{modelId:string; asymId: string; position: number;}>, mode: 'select'|'hover') => void;
-    clearSelection: (mode:'select'|'hover') => void;
-    createComponentFromRange: (modelId:string, asymId: string, x: number, y: number, representationType: 'ball-and-stick' | 'spacefill' | 'gaussian-surface' | 'cartoon') => void;
-    createComponentFromSet: (modelId:string, residues: Array<{asymId: string; position: number;}>, representationType: 'ball-and-stick' | 'spacefill' | 'gaussian-surface' | 'cartoon') => void;
-    removeComponent: () => void;
 }
 
 export interface SaguaroPluginPublicInterface {
-    selectRange: (modelId:string, asymId: string, x: number, y: number, mode: 'select'|'hover') => void;
-    selectSet: (selection: Array<{modelId:string; asymId: string; position: number;}>, mode: 'select'|'hover') => void;
-    clearSelection: (mode:'select'|'hover') => void;
-    createComponentFromRange: (modelId:string, asymId: string, x: number, y: number, representationType: 'ball-and-stick' | 'spacefill' | 'gaussian-surface' | 'cartoon') => void;
-    createComponentFromSet: (modelId:string, residues: Array<{asymId: string; position: number;}>, representationType: 'ball-and-stick' | 'spacefill' | 'gaussian-surface' | 'cartoon') => void;
-    removeComponent: () => void;
+    selectRange: (modelId:string, asymId: string, x: number, y: number, mode: 'select'|'hover', operation:'set'|'add') => void;
+    selectSet: (selection: Array<{modelId:string; asymId: string; position: number;}>, mode: 'select'|'hover', operation:'add'|'set') => void;
+    clearSelection: (mode:'select'|'hover', option?:{modelId:string; labelAsymId:string;}) => void;
+    createComponentFromRange: (componentId: string, modelId:string, asymId: string, x: number, y: number, representationType: 'ball-and-stick' | 'spacefill' | 'gaussian-surface' | 'cartoon') => Promise<void>;
+    createComponentFromSet: (componentId: string, modelId:string, residues: Array<{asymId: string; position: number;}>, representationType: 'ball-and-stick' | 'spacefill' | 'gaussian-surface' | 'cartoon') => Promise<void>;
+    removeComponent: (componentId?: string) => void;
+    isComponent: (componentId: string) => boolean;
+    getComponentSet: () => Set<string>;
+    focusPositions: (modelId: string, asymId: string, positions:Array<number>)=> void;
+    focusRange: (modelId: string, asymId: string, begin: number, end: number) => void;
 }