diff --git a/.eslintrc.js b/.eslintrc.js
index fe0ec8f038dea3da56ed33293a2a6d6c14e5cd79..54af39fd8e639934ed7c4c2658f668a4facc8536 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -20,8 +20,9 @@ module.exports = {
         "requireLast": false
       }
     }],
-    "@typescript-eslint/no-unused-vars": ["warn", {
-      "argsIgnorePattern": "^_"
+    "@typescript-eslint/no-unused-vars": ["error", {
+      "argsIgnorePattern": "^_",
+      "ignoreRestSiblings": true
     }],
     "@typescript-eslint/explicit-function-return-type": "off",
     "@typescript-eslint/no-explicit-any": "off",
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index e092ca43f6bf407a44eda7316b426e95b71ceaa2..25a2cd761b2c38a48147a54f79640b34ad0fa7ae 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -14,7 +14,7 @@ jobs:
   #   runs-on: ubuntu-latest
 
   #   steps:
-  #   - uses: actions/checkout@v2
+  #   - uses: actions/checkout@v3
   #   - uses: actions/setup-node@v1
   #     with:
   #       node-version: '16.x'
@@ -25,7 +25,7 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v3
     - name: Use Node.js 16.x for lint
       uses: actions/setup-node@v1
       with:
@@ -41,7 +41,7 @@ jobs:
       NODE_ENV: test
       
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v3
     - name: Use Node.js 16.x
       uses: actions/setup-node@v1
       with:
@@ -63,7 +63,7 @@ jobs:
       NODE_ENV: test
 
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v3
     - name: Use Node.js 16.x
       uses: actions/setup-node@v1
       with:
diff --git a/.github/workflows/docker_img.yml b/.github/workflows/docker_img.yml
index 5dd8db7891e24bf018b1f8e90164c038bf4b7b93..eb5368f03107d568f2157cbee31359bae1f03f6b 100644
--- a/.github/workflows/docker_img.yml
+++ b/.github/workflows/docker_img.yml
@@ -30,7 +30,7 @@ jobs:
         build: [ 'local', 'prod' ]
 
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v3
     - name: 'Set matomo env var'
       # if matrix.build is local, only run if master or dev
       if: ${{ !(matrix.build == 'local' && github.ref != 'refs/heads/master' && github.ref != 'refs/heads/dev') }} 
@@ -120,7 +120,7 @@ jobs:
 
     needs: build-docker-img
     steps:
-      - uses: actions/checkout@v2
+      - uses: actions/checkout@v3
       - name: Set env var
         run: |
           echo "Using github.ref: $GITHUB_REF"
diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml
index 9203ddec11d5972bd515d88cabe518281765a226..ec46c942aebec12804baf7c73da2bd092023d55c 100644
--- a/.github/workflows/e2e.yml
+++ b/.github/workflows/e2e.yml
@@ -59,7 +59,7 @@ jobs:
       failure-state: ${{ steps.failure-state-step.outputs.failure-state }}
 
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v3
       with:
         ref: ${{ github.event.ref }}
 
diff --git a/.github/workflows/manual_e2e.yml b/.github/workflows/manual_e2e.yml
index 9829d7053bd047b73209192c0effbb5c87715599..a55bd1e16b0aee7d9dc6c91af8aba780b2bc40ec 100644
--- a/.github/workflows/manual_e2e.yml
+++ b/.github/workflows/manual_e2e.yml
@@ -9,7 +9,7 @@ jobs:
   hide_previous_if_exists:
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v3
       with:
         ref: 'master'
     - uses: actions/github-script@v5
@@ -23,7 +23,7 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v3
     - name: 'Add checklist comment'
       uses: actions/github-script@v5
       with:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 9f582522c71f68ce8ce171c4b02c4b7f3ae51aea..211fd6d9ed6b726d95894c16366837d1c743392a 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -24,6 +24,7 @@ jobs:
     if: success()
     runs-on: ubuntu-latest
     steps:
+    - uses: actions/checkout@v3
     - name: Create Release
       id: create_release
       uses: actions/create-release@v1
@@ -32,11 +33,10 @@ jobs:
       with:
         tag_name: ${{ needs.check-version.outputs.package-version }}
         release_name: Release ${{ needs.check-version.outputs.package-version }}
-        body_path: docs/releases/v${{ needs.check-version.outputs.package-version }}.md
+        body_path: docs/releases/${{ needs.check-version.outputs.package-version }}.md
         draft: false
         prerelease: false
         
-    - uses: actions/checkout@v2
     - name: Use Node.js 16.x 
       uses: actions/setup-node@v1
       with:
diff --git a/.github/workflows/repo_sync_ebrains.yml b/.github/workflows/repo_sync_ebrains.yml
index 548b768b0cd23b694a323919a7e64c71ee512d92..e29122fa0ac82239f8772e61b0d0b8581135fbd1 100644
--- a/.github/workflows/repo_sync_ebrains.yml
+++ b/.github/workflows/repo_sync_ebrains.yml
@@ -9,7 +9,7 @@ jobs:
   sync:
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v3
     - uses: wei/git-sync@v3
       with:
         source_repo: ${GITHUB_REPOSITORY}
diff --git a/common/constants.js b/common/constants.js
index 0b92fb901782e568b9c8f2db52f9f7c9a79156b3..d3fe2b2b308520de3e5e9142d58b681c9dbc2a5a 100644
--- a/common/constants.js
+++ b/common/constants.js
@@ -163,5 +163,6 @@ If you do not accept the Terms & Conditions you are not permitted to access or u
   }
 
   exports.QUICKTOUR_DESC_MD = {
+    SLICE_VIEW: `The planar views allow you to zoom \`[mouse wheel]\`, pan the view \`[drag]\`, and select oblique sections \`<shift>\` + \`[drag]\`. You can \`[click]\` any brain regions to select them.`
   }
 })(typeof exports === 'undefined' ? module.exports : exports)
diff --git a/deploy/csp/index.js b/deploy/csp/index.js
index 1d67da419baa8f3c60bb7e93893ce5939f9d5ee2..e48f7f155e90e027e0274590f854d7281ca83bad 100644
--- a/deploy/csp/index.js
+++ b/deploy/csp/index.js
@@ -115,7 +115,7 @@ module.exports = {
           'https://unpkg.com/d3@6.2.0/', // required for preview component
           'https://unpkg.com/mathjax@3.1.2/', // math jax
           'https://unpkg.com/three-surfer@0.0.13/dist/bundle.js', // for threeSurfer (freesurfer support in browser)
-          'https://unpkg.com/ng-layer-tune@0.0.6/dist/ng-layer-tune/', // needed for ng layer control
+          'https://unpkg.com/ng-layer-tune@0.0.13/dist/ng-layer-tune/', // needed for ng layer control
           'https://unpkg.com/hbp-connectivity-component@0.6.6/', // needed for connectivity component
           (req, res) => res.locals.nonce ? `'nonce-${res.locals.nonce}'` : null,
           ...SCRIPT_SRC,
diff --git a/docs/releases/v2.11.1.md b/docs/releases/v2.11.1.md
new file mode 100644
index 0000000000000000000000000000000000000000..3c4b52dceda310dbf1790cef6ff3b245e5875ffa
--- /dev/null
+++ b/docs/releases/v2.11.1.md
@@ -0,0 +1,20 @@
+# v2.11.1
+
+## Feature
+
+- Allow point assignment result to be sorted
+- Allow point assignment result to be downloaded as csv
+- Informs user when atlas download should be occurring, and check popup blocker
+- Restores Julich Brain 2.9 in Colin 27 space full mesh view
+
+## Bugfixes
+
+- Fixed point assignment full table not showing
+- On template/parcellation/atlas change, clear currently selected feature
+
+## Behind the scenes
+
+- Bump siibra-api version dependency. Remove guard for feature type query restrictions
+- Removed unused components
+- Tweaked context menu, showing on hover effects
+- now also attempts to fetch `transform.json` when external layer is populated
diff --git a/docs/releases/v2.11.2.md b/docs/releases/v2.11.2.md
new file mode 100644
index 0000000000000000000000000000000000000000..f48103d0127710d9c33753d9982c3c004e8488ce
--- /dev/null
+++ b/docs/releases/v2.11.2.md
@@ -0,0 +1,18 @@
+# v2.11.2
+
+## Features
+
+- Allow external layer control to persist state (#1338)
+
+## Bugfixes
+
+- Fixed point assignment for maps that do not have statistical maps. Also fixed error messages.
+- Fixed neuron display
+- Fixed typos in plugin API messages
+
+## Behind the scenes
+
+- Using hashed region name to encode selected region, rather than the archaic ngId & label index
+- Pass messages to plugin API when not handled
+- Fix lint
+- Update CI
diff --git a/docs/releases/v2.12.0.md b/docs/releases/v2.12.0.md
new file mode 100644
index 0000000000000000000000000000000000000000..9af41c221a2b364cb69e74b65365fb5cdbbc1c6c
--- /dev/null
+++ b/docs/releases/v2.12.0.md
@@ -0,0 +1,5 @@
+# v2.12.0
+
+## Feature
+
+- enable rat connectivity
diff --git a/e2e/checklist.md b/e2e/checklist.md
index 51e3e9e1fa9db1bd058fad045a71503d1acac4f7..50e2c7125d560b2ae8b8491a764a4e18f806f478 100644
--- a/e2e/checklist.md
+++ b/e2e/checklist.md
@@ -31,16 +31,6 @@
         - [ ] `Preview` tab exists and works
         - [ ] fingerprint is shown, interactable
         - [ ] profiles can be loaded, interactable
-        - [ ] GDPR warning triangle
-        - [ ] `Open in KG` button exists and works
-        - [ ] perspective view works
-          - [ ] mesh becomes transparent
-          - [ ] mesh transparency returns when exit the panel
-          - [ ] electrodes appear in perspective view
-          - [ ] some contact points should apepar red (intersect with region)
-        - [ ] electrode tab
-          - [ ] show should a number of contact points
-          - [ ] clicking on electrode should navigate to the contact point location
     - [ ] `Connectivity` tab exists and works
       - [ ] on opening tab, PMap disappear, colour mapped segmentation appears
       - [ ] on closing tab, PMap reappear, segmentation hides
@@ -62,7 +52,7 @@
 - [ ] saneurl generation functions properly
   - [ ] try existing key (human), and get unavailable error
   - [ ] try non existing key, and get available
-  - [ ] create use key `x-tmp-foo` and new url works
+  - [ ] create use key `x_tmp_foo` and new url works
 - [ ] [saneUrl](https://atlases.ebrains.eu/viewer-staging/saneUrl/bigbrainGreyWhite) redirects to big brain
 - [ ] [saneUrl](https://atlases.ebrains.eu/viewer-staging/saneUrl/julichbrain) redirects to julich brain (colin 27)
 - [ ] [saneUrl](https://atlases.ebrains.eu/viewer-staging/saneUrl/whs4) redirects to waxholm v4
diff --git a/mkdocs.yml b/mkdocs.yml
index 0f21368640d98f4d765b121fe151f77a45ad86c5..fc88fe80be9e51f1591d52834ece4a8698b79ef5 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -33,6 +33,8 @@ nav:
     - Fetching datasets: 'advanced/datasets.md'
     - Display non-atlas volumes: 'advanced/otherVolumes.md'
   - Release notes:
+    - v2.11.2: 'releases/v2.11.2.md'
+    - v2.11.1: 'releases/v2.11.1.md'
     - v2.11.0: 'releases/v2.11.0.md'
     - v2.10.3: 'releases/v2.10.3.md'
     - v2.10.2: 'releases/v2.10.2.md'
diff --git a/package-lock.json b/package-lock.json
index eaa6b864e66c3e6d3556f6482a0816e0eb9e5d0c..792fff775330a11e4a0c83b26158e1694e7836f7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "siibra-explorer",
-  "version": "2.10.0",
+  "version": "2.11.2",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "siibra-explorer",
-      "version": "2.10.0",
+      "version": "2.11.2",
       "license": "apache-2.0",
       "dependencies": {
         "@angular/animations": "^14.2.12",
diff --git a/package.json b/package.json
index 593c06d72221ece38464f61b65e29828639ff594..9414f748771618ee48197f0ef0e3e58c898c0f0c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "siibra-explorer",
-  "version": "2.11.0",
+  "version": "2.12.0",
   "description": "siibra-explorer - explore brain atlases. Based on humanbrainproject/nehuba & google/neuroglancer. Built with angular",
   "scripts": {
     "lint": "eslint src --ext .ts",
diff --git a/src/api/service.ts b/src/api/service.ts
index 6adceb099682923f5306647b857346dca23b1aa3..44520f13f9a2958437463fb45793a925ee5b5aec 100644
--- a/src/api/service.ts
+++ b/src/api/service.ts
@@ -172,7 +172,7 @@ const broadCastDefault: BroadCastingApiEvents = {
 
 export class ApiService implements BoothResponder<ApiBoothEvents>{
 
-  public broadcastCh = createBroadcastingJsonRpcChannel<`${NAMESPACE_TYPE}.on`, BroadCastingApiEvents>(`${namespace}.on`, broadCastDefault)
+  public broadcastCh = createBroadcastingJsonRpcChannel<`${NAMESPACE_TYPE}.on.`, BroadCastingApiEvents>(`${namespace}.on.`, broadCastDefault)
   public booth = new Booth<ApiBoothEvents>(this)
 
   private requestUserQueue: RequestUser<keyof RequestUserTypes>[] = []
@@ -263,27 +263,27 @@ export class ApiService implements BoothResponder<ApiBoothEvents>{
     this.store.pipe(
       select(atlasSelection.selectors.selectedAtlas)
     ).subscribe(atlas => {
-      this.broadcastCh.emit('atlasSelected', translateV3Entities.retrieveAtlas(atlas))
+      this.broadcastCh.emit('atlasSelected', atlas && translateV3Entities.retrieveAtlas(atlas))
     })
     this.store.pipe(
       select(atlasSelection.selectors.selectedParcellation)
     ).subscribe(parcellation => {
-      this.broadcastCh.emit('parcellationSelected', translateV3Entities.retrieveParcellation(parcellation))
+      this.broadcastCh.emit('parcellationSelected', parcellation && translateV3Entities.retrieveParcellation(parcellation))
     })
     this.store.pipe(
       select(atlasSelection.selectors.selectedTemplate)
     ).subscribe(template => {
-      this.broadcastCh.emit('templateSelected', translateV3Entities.retrieveTemplate(template))
+      this.broadcastCh.emit('templateSelected', template && translateV3Entities.retrieveTemplate(template))
     })
     this.store.pipe(
       select(atlasSelection.selectors.selectedRegions)
     ).subscribe(regions => {
-      this.broadcastCh.emit('regionsSelected', regions.map(reg => translateV3Entities.retrieveRegion(reg)))
+      this.broadcastCh.emit('regionsSelected', regions && regions.map(reg => translateV3Entities.retrieveRegion(reg)))
     })
     this.store.pipe(
       select(atlasSelection.selectors.selectedParcAllRegions)
     ).subscribe(regions => {
-      this.broadcastCh.emit('allRegions', regions.map(reg => translateV3Entities.retrieveRegion(reg)))
+      this.broadcastCh.emit('allRegions', regions && regions.map(reg => translateV3Entities.retrieveRegion(reg)))
     })
     this.store.pipe(
       select(atlasSelection.selectors.navigation)
diff --git a/src/atlas-download/atlas-download.directive.spec.ts b/src/atlas-download/atlas-download.directive.spec.ts
index e81fe2966b376e21eded2b6ec96a79a85faaf616..61c17884a4151cbf5d22cd056e8ca42fc9e242d6 100644
--- a/src/atlas-download/atlas-download.directive.spec.ts
+++ b/src/atlas-download/atlas-download.directive.spec.ts
@@ -3,7 +3,7 @@ import { AtlasDownloadDirective } from './atlas-download.directive';
 
 describe('AtlasDownloadDirective', () => {
   it('should create an instance', () => {
-    const directive = new AtlasDownloadDirective(NEVER as any);
+    const directive = new AtlasDownloadDirective(NEVER as any, null);
     expect(directive).toBeTruthy();
   });
 });
diff --git a/src/atlas-download/atlas-download.directive.ts b/src/atlas-download/atlas-download.directive.ts
index ce873301181fa39c1975848bd4a76de9b09adbf4..9b2ba98268b8f52cebec05803ca637a0d75e2353 100644
--- a/src/atlas-download/atlas-download.directive.ts
+++ b/src/atlas-download/atlas-download.directive.ts
@@ -1,4 +1,5 @@
 import { Directive, HostListener } from '@angular/core';
+import { MatSnackBar } from '@angular/material/snack-bar';
 import { Store, select } from '@ngrx/store';
 import { Subject, concat, of } from 'rxjs';
 import { distinctUntilChanged, shareReplay, take } from 'rxjs/operators';
@@ -70,6 +71,7 @@ export class AtlasDownloadDirective {
        */
       window.open(`${endpoint}/atlas_download/${task_id}/download`, "_blank")
       this.#busy$.next(false)
+      this.snackbar.open(`Download starting. If it has not, please check your browser's popup blocker.`, 'Dismiss')
     } catch (e) {
       this.#busy$.next(false)
       this.#error$.next(e.toString())
@@ -89,6 +91,6 @@ export class AtlasDownloadDirective {
   #error$ = new Subject<string>()
   error$ = this.#error$.pipe()
 
-  constructor(private store: Store<MainState>) { }
+  constructor(private store: Store<MainState>, private snackbar: MatSnackBar) { }
 
 }
diff --git a/src/atlas-download/atlas-download.module.ts b/src/atlas-download/atlas-download.module.ts
index b4181a9cc0526254f2cabd6fbe1aa7b9894acfc8..910154cb4bce732b351da2a27738d85cab6a275a 100644
--- a/src/atlas-download/atlas-download.module.ts
+++ b/src/atlas-download/atlas-download.module.ts
@@ -1,6 +1,7 @@
 import { NgModule } from '@angular/core';
 import { CommonModule } from '@angular/common';
 import { AtlasDownloadDirective } from './atlas-download.directive';
+import { MatSnackBarModule } from '@angular/material/snack-bar';
 
 
 @NgModule({
@@ -9,6 +10,7 @@ import { AtlasDownloadDirective } from './atlas-download.directive';
   ],
   imports: [
     CommonModule,
+    MatSnackBarModule,
   ],
   exports: [
     AtlasDownloadDirective
diff --git a/src/atlasComponents/sapi/core/index.ts b/src/atlasComponents/sapi/core/index.ts
index a7fc4cbb696db25e9fa2159b5862e23c0bdb260e..ed68003d55ce542a35f5e173aaa173345202d597 100644
--- a/src/atlasComponents/sapi/core/index.ts
+++ b/src/atlasComponents/sapi/core/index.ts
@@ -1,4 +1,3 @@
 export { SAPIAtlas } from "./sapiAtlas"
-export { SAPISpace } from "./sapiSpace"
 export { SAPIParcellation } from "./sapiParcellation"
 export { SAPIRegion } from "./sapiRegion"
diff --git a/src/atlasComponents/sapi/core/sapiRegion.ts b/src/atlasComponents/sapi/core/sapiRegion.ts
index 0c5a2d0baf73fa0ba4d6e499abdbe4e3930eeb13..5c9db2f9fa82eed38c7c85afaebb57653bab0cee 100644
--- a/src/atlasComponents/sapi/core/sapiRegion.ts
+++ b/src/atlasComponents/sapi/core/sapiRegion.ts
@@ -1,7 +1,6 @@
 import { SAPI } from "..";
 import { SapiRegionModel, RouteParam } from "../typeV3";
-import { strToRgb, hexToRgb } from 'common/util'
-import { NEVER, Observable, of } from "rxjs";
+import { Observable, of } from "rxjs";
 import { map } from "rxjs/operators";
 import { SAPIBase } from "./base";
 import { SxplrRegion } from "../sxplrTypes";
@@ -92,16 +91,6 @@ export class SAPIRegion extends SAPIBase<RF>{
     )
   }
 
-  /**
-   * 
-   * @deprecated
-   * @param volumeId 
-   * @returns 
-   */
-  getVolumeInstance(volumeId: string): Observable<never> {
-    return NEVER
-  }
-
   getDetail(spaceId: string): Observable<SapiRegionModel> {
     return this.sapi.v3Get("/regions/{region_id}", {
       path: {
diff --git a/src/atlasComponents/sapi/core/sapiSpace.ts b/src/atlasComponents/sapi/core/sapiSpace.ts
deleted file mode 100644
index b19359e3cbc373699aa332abb3b06d424d7f0742..0000000000000000000000000000000000000000
--- a/src/atlasComponents/sapi/core/sapiSpace.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-import { Observable, of, throwError } from "rxjs"
-import { SAPI } from '../sapi.service'
-import { SxplrTemplate } from "../sxplrTypes"
-import { map, switchMap } from "rxjs/operators"
-import { SAPIBase } from "./base"
-
-/**
- * All valid parcellation features
- */
-const SpaceFeatures = {
-  Image: "Image",
-} as const
-
-export type SF = keyof typeof SpaceFeatures
-
-type FeatureResponse = {
-  features: {
-    [key: string]: string
-  }
-}
-
-type RegionalSpatialFeatureOpts = {
-  parcellationId: string
-  region: string
-}
-
-type BBoxSpatialFEatureOpts = {
-  bbox: string
-}
-
-type SpatialFeatureOpts = RegionalSpatialFeatureOpts | BBoxSpatialFEatureOpts
-
-export class SAPISpace extends SAPIBase<SF>{
-
-  static Features$ = of(Object.keys(SpaceFeatures) as SF[])
-  public features$ = SAPISpace.Features$
-
-  constructor(private sapi: SAPI, public atlasId: string, public id: string){
-    super(sapi)
-    this.prefix$ = SAPI.BsEndpoint$.pipe(
-      map(endpt => `${endpt}/atlases/${encodeURIComponent(this.atlasId)}/spaces/${encodeURIComponent(this.id)}`)
-    )
-  }
-
-  private prefix$: Observable<string>
-
-  getModalities(): Observable<FeatureResponse> {
-    return this.prefix$.pipe(
-      switchMap(prefix => this.sapi.httpGet<FeatureResponse>(
-        `${prefix}/features`,
-        null,
-      ))
-    )
-  }
-
-  getDetail(): Observable<SxplrTemplate>{
-    return this.prefix$.pipe(
-      switchMap(prefix => this.sapi.httpGet<SxplrTemplate>(
-        `${prefix}`,
-        null,
-      ))
-    )
-  }
-
-}
diff --git a/src/atlasComponents/sapi/index.ts b/src/atlasComponents/sapi/index.ts
index 356276003c98cafb620eaf5aa67df35acffcff4b..2ec890861ec737a5a1aec4b1f6d4d899fac532c7 100644
--- a/src/atlasComponents/sapi/index.ts
+++ b/src/atlasComponents/sapi/index.ts
@@ -3,7 +3,6 @@ export { SAPIModule } from './module'
 export { SAPI } from "./sapi.service"
 export {
   SAPIAtlas,
-  SAPISpace,
   SAPIParcellation,
   SAPIRegion
 } from "./core"
diff --git a/src/atlasComponents/sapi/sapi.service.ts b/src/atlasComponents/sapi/sapi.service.ts
index faf55163bb5607a3b9214cee6e4b6089e75cfc40..7afa3871728516ca4ebd63c10e08b6cf88526ab7 100644
--- a/src/atlasComponents/sapi/sapi.service.ts
+++ b/src/atlasComponents/sapi/sapi.service.ts
@@ -1,7 +1,7 @@
 import { Injectable } from "@angular/core";
 import { HttpClient } from '@angular/common/http';
 import { catchError, map, shareReplay, switchMap, take, tap } from "rxjs/operators";
-import { getExportNehuba } from "src/util/fn";
+import { getExportNehuba, noop } from "src/util/fn";
 import { MatSnackBar } from "@angular/material/snack-bar";
 import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service";
 import { EnumColorMapName } from "src/util/colorMaps";
@@ -13,7 +13,6 @@ import {
 import { FeatureType, PathReturn, RouteParam, SapiRoute } from "./typeV3";
 import { BoundingBox, SxplrAtlas, SxplrParcellation, SxplrRegion, SxplrTemplate, VoiFeature, Feature } from "./sxplrTypes";
 import { parcBanList, speciesOrder } from "src/util/constants";
-import { IDS } from "./constants";
 
 export const useViewer = {
   THREESURFER: "THREESURFER",
@@ -22,7 +21,7 @@ export const useViewer = {
 } as const
 
 export const SIIBRA_API_VERSION_HEADER_KEY='x-siibra-api-version'
-export const EXPECTED_SIIBRA_API_VERSION = '0.3.3'
+export const EXPECTED_SIIBRA_API_VERSION = '0.3.5'
 
 let BS_ENDPOINT_CACHED_VALUE: Observable<string> = null
 
@@ -111,8 +110,7 @@ export class SAPI{
               .then(flag => {
                 if (flag) rs(endpt)
               })
-              // eslint-disable-next-line  @typescript-eslint/no-empty-function
-              .catch(e => {})
+              .catch(noop)
           }
         })
         try {
@@ -298,7 +296,7 @@ export class SAPI{
     switchMap(atlases => forkJoin(
       atlases.items.map(atlas => translateV3Entities.translateAtlas(atlas))
     )),
-    map(atlases => atlases.sort((a, b) => speciesOrder.indexOf(a.species) - speciesOrder.indexOf(b.species))),
+    map(atlases => atlases.sort((a, b) => (speciesOrder as string[]).indexOf(a.species) - (speciesOrder as string[]).indexOf(b.species))),
     tap(() => {
       const respVersion = SAPI.API_VERSION
       if (respVersion !== EXPECTED_SIIBRA_API_VERSION) {
@@ -351,7 +349,7 @@ export class SAPI{
             template.id,
             "LABELLED"
           ).pipe(
-            catchError((err, obs) => of(null as SxplrParcellation)),
+            catchError(() => of(null as SxplrParcellation)),
             map(_map => _map && parc)
           )
         )
@@ -429,7 +427,7 @@ export class SAPI{
             space.id,
             "LABELLED"
           ).pipe(
-            catchError((err, obs) => of(null as SxplrTemplate)),
+            catchError(() => of(null as SxplrTemplate)),
             map(_map => _map && space)
           )
         )
@@ -515,12 +513,12 @@ export class SAPI{
     const map = await this.getLabelledMap(parcellation, template)
 
     for (const regionname in map.indices) {
-      if (parcellation.id === IDS.PARCELLATION.CORTICAL_LAYERS) {
-        if (regionname.includes("left") || regionname.includes("right")) {
-          continue
-        }
-      }
-      for (const { volume: volumeIdx, fragment, label } of map.indices[regionname]) {
+      // if (parcellation.id === IDS.PARCELLATION.CORTICAL_LAYERS) {
+      //   if (regionname.includes("left") || regionname.includes("right")) {
+      //     continue
+      //   }
+      // }
+      for (const { volume: volumeIdx, fragment } of map.indices[regionname]) {
         const { providedVolumes } = map.volumes[volumeIdx]
         if (!("neuroglancer/precomputed" in providedVolumes)) {
           continue
diff --git a/src/atlasComponents/sapi/schemaV3.ts b/src/atlasComponents/sapi/schemaV3.ts
index de27935f67b5a381ef1d04069f41fad1733a6b6e..3c10d79b9c849d9aa745879549de94cfe82b45fa 100644
--- a/src/atlasComponents/sapi/schemaV3.ts
+++ b/src/atlasComponents/sapi/schemaV3.ts
@@ -81,6 +81,10 @@ export interface paths {
     /** Get Task Id */
     get: operations["get_task_id_atlas_download__task_id__get"]
   }
+  "/atlas_download/{task_id}/download": {
+    /** Get Task Id */
+    get: operations["get_task_id_atlas_download__task_id__download_get"]
+  }
   "/feature/_types": {
     /** Get All Feature Types */
     get: operations["get_all_feature_types_feature__types_get"]
@@ -441,12 +445,6 @@ export interface components {
        */
       versionIdentifier: string
     }
-    /**
-     * ConnectivityTypes 
-     * @description An enumeration. 
-     * @enum {unknown}
-     */
-    ConnectivityTypes: "FunctionalConnectivity" | "StreamlineCounts" | "StreamlineLengths"
     /** CoordinatePointModel */
     CoordinatePointModel: {
       /** @Type */
@@ -494,20 +492,14 @@ export interface components {
        */
       year: string
     }
-    /**
-     * CorticalProfileTypes 
-     * @description An enumeration. 
-     * @enum {unknown}
-     */
-    CorticalProfileTypes: "ReceptorDensityProfile" | "CellDensityProfile" | "BigBrainIntensityProfile"
     /** DataFrameModel */
     DataFrameModel: {
       /** @Type */
       "@type": string
       /** Index */
-      index: unknown[]
+      index: any[]
       /** Columns */
-      columns: unknown[]
+      columns: any[]
       /** Ndim */
       ndim: number
       /** Data */
@@ -650,12 +642,6 @@ export interface components {
        */
       ontologyIdentifier?: (string)[]
     }
-    /**
-     * ImageTypes 
-     * @description An enumeration. 
-     * @enum {unknown}
-     */
-    ImageTypes: "BlockfaceVolumeOfInterest" | "CellBodyStainedVolumeOfInterest" | "CellbodyStainedSection" | "MRIVolumeOfInterest" | "PLIVolumeOfInterest" | "SegmentedVolumeOfInterest" | "XPCTVolumeOfInterest"
     /** LocationModel */
     LocationModel: {
       /** @Type */
@@ -1245,12 +1231,6 @@ export interface components {
       /** Max */
       max: number
     }
-    /**
-     * TabularTypes 
-     * @description An enumeration. 
-     * @enum {unknown}
-     */
-    TabularTypes: "ReceptorDensityFingerprint" | "LayerwiseBigBrainIntensities" | "LayerwiseCellDensity" | "RegionalBOLD"
     /** ValidationError */
     ValidationError: {
       /** Location */
@@ -1685,6 +1665,7 @@ export interface operations {
         parcellation_id: string
         space_id: string
         point: string
+        assignment_type?: string
         sigma_mm?: number
       }
     }
@@ -1749,6 +1730,28 @@ export interface operations {
       }
     }
   }
+  get_task_id_atlas_download__task_id__download_get: {
+    /** Get Task Id */
+    parameters: {
+      path: {
+        task_id: string
+      }
+    }
+    responses: {
+      /** @description Successful Response */
+      200: {
+        content: {
+          "application/json": Record<string, never>
+        }
+      }
+      /** @description Validation Error */
+      422: {
+        content: {
+          "application/json": components["schemas"]["HTTPValidationError"]
+        }
+      }
+    }
+  }
   get_all_feature_types_feature__types_get: {
     /** Get All Feature Types */
     parameters?: {
@@ -1777,7 +1780,7 @@ export interface operations {
     parameters: {
       query: {
         parcellation_id: string
-        type?: components["schemas"]["ConnectivityTypes"]
+        type?: string
         page?: number
         size?: number
       }
@@ -1808,7 +1811,7 @@ export interface operations {
       query: {
         parcellation_id: string
         subject?: string
-        type?: components["schemas"]["ConnectivityTypes"]
+        type?: string
       }
       path: {
         feature_id: string
@@ -1835,7 +1838,7 @@ export interface operations {
       query: {
         parcellation_id: string
         region_id: string
-        type?: components["schemas"]["CorticalProfileTypes"]
+        type?: string
         page?: number
         size?: number
       }
@@ -1861,7 +1864,7 @@ export interface operations {
       query: {
         parcellation_id: string
         region_id: string
-        type?: components["schemas"]["CorticalProfileTypes"]
+        type?: string
       }
       path: {
         feature_id: string
@@ -1888,7 +1891,7 @@ export interface operations {
       query: {
         parcellation_id: string
         region_id: string
-        type?: components["schemas"]["TabularTypes"]
+        type?: string
         page?: number
         size?: number
       }
@@ -1914,7 +1917,7 @@ export interface operations {
       query: {
         parcellation_id: string
         region_id: string
-        type?: components["schemas"]["TabularTypes"]
+        type?: string
       }
       path: {
         feature_id: string
@@ -1941,7 +1944,7 @@ export interface operations {
       query: {
         space_id: string
         bbox?: string
-        type?: components["schemas"]["ImageTypes"]
+        type?: string
         page?: number
         size?: number
       }
@@ -1966,7 +1969,7 @@ export interface operations {
     parameters: {
       query: {
         space_id: string
-        type?: components["schemas"]["ImageTypes"]
+        type?: string
       }
       path: {
         feature_id: string
diff --git a/src/atlasComponents/sapi/sxplrTypes.ts b/src/atlasComponents/sapi/sxplrTypes.ts
index ef556f25eb9c77344aa826d7b6d88e1e478c1227..95860602d584abe68740c2bbe9a5a4018e5e8641 100644
--- a/src/atlasComponents/sapi/sxplrTypes.ts
+++ b/src/atlasComponents/sapi/sxplrTypes.ts
@@ -102,10 +102,6 @@ export type Feature = {
   category?: string
 } & Partial<AdditionalInfo>
 
-type DataFrame = {
-  index: Record<string, string>
-}
-
 export type VoiFeature = {
   bbox: BoundingBox
   ngVolume: {
diff --git a/src/atlasComponents/sapi/translateV3.ts b/src/atlasComponents/sapi/translateV3.ts
index 79775ba84f4ca64aa8a03169373ed1049a487c92..da87a784b79e7d07e4b6848bbba43de822dc2138 100644
--- a/src/atlasComponents/sapi/translateV3.ts
+++ b/src/atlasComponents/sapi/translateV3.ts
@@ -40,7 +40,7 @@ class TranslateV3 {
   }
   async translateParcellation(parcellation:PathReturn<"/parcellations/{parcellation_id}">): Promise<SxplrParcellation> {
     const ds = await Promise.all((parcellation.datasets || []).map(ds => this.translateDs(ds)))
-    const { name, ...rest } = ds[0] || {}
+    const { ...rest } = ds[0] || {}
     const { ['@id']: prevId } = parcellation.version?.prev || {}
     return {
       id: parcellation["@id"],
@@ -147,7 +147,7 @@ class TranslateV3 {
     for (const defaultImage of validImages) {
       
       const { providedVolumes } = defaultImage
-      const { "neuroglancer/precomputed": precomputedVol, ...rest } = await this.#extractNgPrecompUnfrag(providedVolumes)
+      const { "neuroglancer/precomputed": precomputedVol } = await this.#extractNgPrecompUnfrag(providedVolumes)
       
       if (!precomputedVol) {
         console.error(`neuroglancer/precomputed data source has not been found!`)
@@ -287,17 +287,7 @@ class TranslateV3 {
       segLayerSpec.layer.labelIndicies.push(label)
       segLayerSpec.region.push(region)
     }
-    const { ['@id']: mapId } = map
     for (const regionname in map.indices) {
-      /**
-       * temporary fix
-       * see https://github.com/FZJ-INM1-BDA/siibra-python/issues/317
-       */
-      if (mapId === "siibra-map-v0.0.1_bigbrain-cortical-labelled") {
-        if (regionname.includes("left") || regionname.includes("right")) {
-          continue
-        }
-      }
       for (const index of map.indices[regionname]) {
         const { volume:volumeIdx=0, fragment, label } = index
         if (!label) {
diff --git a/src/atlasComponents/sapi/typeV3.ts b/src/atlasComponents/sapi/typeV3.ts
index d65db66debe2bff385851d785213cec40749b3c5..22fdbcfe328e20fec2328f648be5baae4e1aa615 100644
--- a/src/atlasComponents/sapi/typeV3.ts
+++ b/src/atlasComponents/sapi/typeV3.ts
@@ -1,4 +1,4 @@
-import { components, paths, operations } from "./schemaV3"
+import { components, paths } from "./schemaV3"
 
 export type SapiAtlasModel = PathReturn<"/atlases/{atlas_id}">
 export type SapiSpaceModel = PathReturn<"/spaces/{space_id}">
@@ -22,6 +22,7 @@ type _FeatureType<FeatureRoute extends SapiRoute> = FeatureRoute extends `/featu
     ? never
     : FT extends "{feature_id}"
       ? never
+      // eslint-disable-next-line @typescript-eslint/no-unused-vars
       : FT extends `${infer _FT}/{${infer _FID}}`
         ? never
         : FT
diff --git a/src/atlasComponents/sapiViews/core/region/module.ts b/src/atlasComponents/sapiViews/core/region/module.ts
index 9482528706f278dc726d6a17f8096ac82fed2328..6e9140d0b5968e384f4f08907246dd6b9a621a30 100644
--- a/src/atlasComponents/sapiViews/core/region/module.ts
+++ b/src/atlasComponents/sapiViews/core/region/module.ts
@@ -9,7 +9,6 @@ import { StrictLocalModule } from "src/strictLocal";
 import { SapiViewsUtilModule } from "../../util/module";
 import { SapiViewsCoreRegionRegionListItem } from "./region/listItem/region.listItem.component";
 import { SapiViewsCoreRegionRegionBase } from "./region/region.base.directive";
-import { SapiViewsCoreRegionRegionalFeatureDirective } from "./region/region.features.directive";
 import { SapiViewsCoreRegionRegionRich } from "./region/rich/region.rich.component";
 
 @NgModule({
@@ -27,13 +26,11 @@ import { SapiViewsCoreRegionRegionRich } from "./region/rich/region.rich.compone
     SapiViewsCoreRegionRegionListItem,
     SapiViewsCoreRegionRegionRich,
     SapiViewsCoreRegionRegionBase,
-    SapiViewsCoreRegionRegionalFeatureDirective,
   ],
   exports: [
     SapiViewsCoreRegionRegionListItem,
     SapiViewsCoreRegionRegionRich,
     SapiViewsCoreRegionRegionBase,
-    SapiViewsCoreRegionRegionalFeatureDirective,
   ]
 })
 
diff --git a/src/atlasComponents/sapiViews/core/region/region/region.base.directive.ts b/src/atlasComponents/sapiViews/core/region/region/region.base.directive.ts
index a2204a080335eb69d7644bf8c383eb4fb3d643f4..59b09ba91bb2e0eddbac28add6ed03fdd4c0cade 100644
--- a/src/atlasComponents/sapiViews/core/region/region/region.base.directive.ts
+++ b/src/atlasComponents/sapiViews/core/region/region/region.base.directive.ts
@@ -1,4 +1,4 @@
-import { Directive, EventEmitter, Input, Output, SimpleChanges } from "@angular/core";
+import { Directive, EventEmitter, Input, Output } from "@angular/core";
 import { SxplrAtlas, SxplrParcellation, SxplrRegion, SxplrTemplate } from "src/atlasComponents/sapi/sxplrTypes";
 import { translateV3Entities } from "src/atlasComponents/sapi/translateV3"
 import { rgbToHsl } from 'common/util'
@@ -72,7 +72,7 @@ export class SapiViewsCoreRegionRegionBase {
     map(([ atp, region ]) => ({ ...atp, region }))
   )
 
-  ngOnChanges(sc: SimpleChanges): void {
+  ngOnChanges(): void {
     const { atlas, template, parcellation } = this
     this.ATP$.next({ atlas, template, parcellation })
   }
@@ -97,7 +97,7 @@ export class SapiViewsCoreRegionRegionBase {
        */
       const rgb = SAPIRegion.GetDisplayColor(this.region) || [200, 200, 200]
       this.regionRgbString = `rgb(${rgb.join(',')})`
-      const [_h, _s, l] = rgbToHsl(...rgb)
+      const [ /* _h */, /* _s */, l] = rgbToHsl(...rgb)
       this.regionDarkmode = l < 0.4
       
       /**
diff --git a/src/atlasComponents/sapiViews/core/region/region/region.features.directive.ts b/src/atlasComponents/sapiViews/core/region/region/region.features.directive.ts
deleted file mode 100644
index b7019d477953329a64fb471ad620b4f7486b059f..0000000000000000000000000000000000000000
--- a/src/atlasComponents/sapiViews/core/region/region/region.features.directive.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { Directive, OnChanges, SimpleChanges } from "@angular/core";
-import { BehaviorSubject, merge, NEVER } from "rxjs";
-import { switchMap,  filter, startWith, shareReplay, scan } from "rxjs/operators";
-import { SAPI, SAPIRegion } from "src/atlasComponents/sapi";
-import { SxplrAtlas, SxplrParcellation, SxplrTemplate, SxplrRegion } from "src/atlasComponents/sapi/sxplrTypes"
-import { SapiViewsCoreRegionRegionBase } from "./region.base.directive";
-
-@Directive({
-  selector: '[sxplr-sapiviews-core-region-regional-feature]',
-  exportAs: 'sapiViewsRegionalFeature'
-})
-
-export class SapiViewsCoreRegionRegionalFeatureDirective extends SapiViewsCoreRegionRegionBase implements OnChanges{
-
-  constructor(sapi: SAPI){
-    super(sapi)
-  }
-
-  private features$ = NEVER
-
-  public listOfFeatures$ = this.features$.pipe(
-    startWith([]),
-    shareReplay(1),
-  )
-
-  public busy$ = new BehaviorSubject<boolean>(false)
-}
diff --git a/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.component.ts b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.component.ts
index 9db77ddb32c2077ff94f932785ffc5469a50b6a3..c61d33b9d2c25cd406383ed85cc437f6364748ca 100644
--- a/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.component.ts
+++ b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.component.ts
@@ -28,8 +28,6 @@ export class SapiViewsCoreRegionRegionRich extends SapiViewsCoreRegionRegionBase
   @Output('sxplr-sapiviews-core-region-region-rich-feature-clicked')
   featureClicked = new EventEmitter<Feature>()
 
-  public expandedPanel: string
-
   constructor(
     sapi: SAPI,
     @Inject(DARKTHEME) public darktheme$: Observable<boolean>,
@@ -41,16 +39,6 @@ export class SapiViewsCoreRegionRegionRich extends SapiViewsCoreRegionRegionBase
     this.featureClicked.emit(feat)
   }
 
-  // eslint-disable-next-line  @typescript-eslint/no-empty-function
-  handleExpansionPanelClosedEv(title: string){
-    this.expandedPanel = null
-  }
-
-  // eslint-disable-next-line  @typescript-eslint/no-empty-function
-  handleExpansionPanelAfterExpandEv(title: string) {
-    this.expandedPanel = title
-  }
-
   activePanelTitles$: Observable<string[]> = new Subject()
 
   private regionalStatisticalMaps$ = this.ATPR$.pipe(
diff --git a/src/atlasComponents/sapiViews/core/rich/ATPSelector/wrapper/wrapper.component.ts b/src/atlasComponents/sapiViews/core/rich/ATPSelector/wrapper/wrapper.component.ts
index 6a8a8b546078f57907c0d7bf03bb8443f8d1bcb0..160902ec19a877eb868a8dab7e636f854b84cb2b 100644
--- a/src/atlasComponents/sapiViews/core/rich/ATPSelector/wrapper/wrapper.component.ts
+++ b/src/atlasComponents/sapiViews/core/rich/ATPSelector/wrapper/wrapper.component.ts
@@ -16,11 +16,6 @@ function isATPGuard(obj: any): obj is Partial<ATP&{ requested: Partial<ATP> }> {
   return (obj.atlas || obj.template || obj.parcellation) && (!obj.requested || isATPGuard(obj.requested))
 }
 
-const banListParcName = new Set([
-  "VEP Atlas",
-  "Desikan-Killiany 2006"
-])
-
 @Component({
   selector: 'sxplr-wrapper-atp-selector',
   templateUrl: './wrapper.template.html',
diff --git a/src/atlasComponents/sapiViews/core/space/boundingBox.directive.ts b/src/atlasComponents/sapiViews/core/space/boundingBox.directive.ts
index 72d0e2e66fc7ff0b8da0d520a6ec4438432a88df..ed1aa7731dfeda9b771a64cdadfc82236bf9eec1 100644
--- a/src/atlasComponents/sapiViews/core/space/boundingBox.directive.ts
+++ b/src/atlasComponents/sapiViews/core/space/boundingBox.directive.ts
@@ -1,4 +1,4 @@
-import { Directive, Input, OnChanges } from "@angular/core";
+import { Directive, Input, OnChanges, Output } from "@angular/core";
 import { BehaviorSubject, Observable } from "rxjs";
 import { distinctUntilChanged } from "rxjs/operators";
 import { BoundingBox, SxplrTemplate, SxplrAtlas } from "src/atlasComponents/sapi/sxplrTypes"
@@ -60,6 +60,7 @@ export class SapiViewsCoreSpaceBoundingBox implements OnChanges{
     bbox: null
   })
 
+  @Output('sxplr-sapiviews-core-space-boundingbox-changed')
   public bbox$: Observable<{
     atlas: SxplrAtlas
     space: SxplrTemplate
diff --git a/src/atlasComponents/sapiViews/volumes/point-assignment/point-assignment.component.html b/src/atlasComponents/sapiViews/volumes/point-assignment/point-assignment.component.html
index 8fc0aa7b4411e39a46e1d30f200f45907eb8d1ee..59694f74a9f84f067a93a56eb0f0d9988895d8e1 100644
--- a/src/atlasComponents/sapiViews/volumes/point-assignment/point-assignment.component.html
+++ b/src/atlasComponents/sapiViews/volumes/point-assignment/point-assignment.component.html
@@ -1,7 +1,13 @@
-<div class="sxplr-m-2" *ngIf="busy$ | async">
+<div class="sxplr-m-2" *ngIf="busy$ | async as busyMessage">
     <spinner-cmp class="sxplr-d-inline-block"></spinner-cmp>
     <span>
-        Loading assignment ...
+        {{ busyMessage }}
+    </span>
+</div>
+
+<div class="sxplr-m-2" *ngIf="error$ | async">
+    <span>
+        An error occurred when performing map assignment.
     </span>
 </div>
 
@@ -13,9 +19,14 @@
     </button>
 
     <!-- simple table -->
-    <table mat-table [dataSource]="df | dfToDs" class="sxplr-w-100">
+    <table mat-table [dataSource]="df | dfToDs : simpleTblSort"
+        matSort
+        class="sxplr-w-100"
+        #simpleTblSort="matSort"
+        matSortActive="map value"
+        matSortDirection="desc">
         <ng-container matColumnDef="region">
-            <th mat-header-cell *matHeaderCellDef>
+            <th mat-header-cell *matHeaderCellDef mat-sort-header>
                 region
             </th>
             <td mat-cell *matCellDef="let element">
@@ -25,12 +36,12 @@
                 </button>
             </td>
         </ng-container>
-        <ng-container matColumnDef="intersection over union">
-            <th mat-header-cell *matHeaderCellDef>
-                intersection over union
+        <ng-container matColumnDef="map value">
+            <th mat-header-cell *matHeaderCellDef mat-sort-header>
+                map value
             </th>
             <td mat-cell *matCellDef="let element">
-                {{ element['intersection over union'] | prettyPresent }}
+                {{ element['map value'] | prettyPresent }}
             </td>
         </ng-container>
         
@@ -42,10 +53,14 @@
 <ng-template #datatableTmpl>
     <h2 mat-dialog-title>Assignment</h2>
     <mat-dialog-content>
-        <table mat-table [dataSource]="df$ | async | dfToDs">
+        <table mat-table [dataSource]="df$ | async | dfToDs : comphTableSort"
+            matSort
+            #comphTableSort="matSort"
+            matSortActive="map value"
+            matSortDirection="desc">
             <ng-container *ngFor="let column of columns$ | async"
                 [matColumnDef]="column">
-                <th mat-header-cell *matHeaderCellDef>
+                <th mat-header-cell *matHeaderCellDef mat-sort-header>
                     {{ column }}
                 </th>
                 <td mat-cell *matCellDef="let element">
@@ -58,6 +73,14 @@
         </table>
     </mat-dialog-content>
     <mat-dialog-actions align="center">
+        <button mat-raised-button color="primary"
+            [zip-files-output]="zipfileConfig$ | async"
+            zip-files-output-zip-filename="pointassignment.zip">
+            <i class="fas fa-download"></i>
+            <span>
+                Download CSV
+            </span>
+        </button>
         <button mat-button mat-dialog-close>Close</button>
-      </mat-dialog-actions>
+    </mat-dialog-actions>
 </ng-template>
diff --git a/src/atlasComponents/sapiViews/volumes/point-assignment/point-assignment.component.ts b/src/atlasComponents/sapiViews/volumes/point-assignment/point-assignment.component.ts
index 2bf281e7816015f0607b4f77122373d7097705c5..a28f5691c45974889ded72e3c28e57015d1051b1 100644
--- a/src/atlasComponents/sapiViews/volumes/point-assignment/point-assignment.component.ts
+++ b/src/atlasComponents/sapiViews/volumes/point-assignment/point-assignment.component.ts
@@ -1,12 +1,17 @@
 import { Component, Input, OnDestroy, Output, TemplateRef, EventEmitter } from '@angular/core';
 import { MatDialog } from '@angular/material/dialog';
-import { BehaviorSubject, EMPTY, Subscription, combineLatest, concat, of } from 'rxjs';
-import { map, shareReplay, switchMap, tap } from 'rxjs/operators';
-import { SAPI } from 'src/atlasComponents/sapi/sapi.service';
+import { BehaviorSubject, EMPTY, Observable, Subscription, combineLatest, concat, of } from 'rxjs';
+import { catchError, map, shareReplay, switchMap, tap } from 'rxjs/operators';
+import { SAPI, EXPECTED_SIIBRA_API_VERSION } from 'src/atlasComponents/sapi/sapi.service';
 import { SxplrParcellation, SxplrRegion, SxplrTemplate } from 'src/atlasComponents/sapi/sxplrTypes';
 import { translateV3Entities } from 'src/atlasComponents/sapi/translateV3';
 import { PathReturn } from 'src/atlasComponents/sapi/typeV3';
 import { TSandsPoint } from 'src/util/types';
+import { TZipFileConfig } from "src/zipFilesOutput/type"
+import { environment } from "src/environments/environment"
+
+const DOING_PROB_ASGMT = "Performing probabilistic assignment ..."
+const DOING_LABEL_ASGMT = "Probabilistic assignment failed. Performing labelled assignment ..."
 
 @Component({
   selector: 'sxplr-point-assignment',
@@ -17,12 +22,15 @@ export class PointAssignmentComponent implements OnDestroy {
 
   SIMPLE_COLUMNS = [
     "region",
-    "intersection over union",
+    "map value",
   ]
   
-  #busy$ = new BehaviorSubject(false)
+  #busy$ = new BehaviorSubject<string>(null)
   busy$ = this.#busy$.asObservable()
 
+  #error$ = new BehaviorSubject<string>(null)
+  error$ = this.#error$.asObservable()
+
   #point = new BehaviorSubject<TSandsPoint>(null)
   @Input()
   set point(val: TSandsPoint) {
@@ -44,12 +52,15 @@ export class PointAssignmentComponent implements OnDestroy {
   @Output()
   clickOnRegion = new EventEmitter<{ target: SxplrRegion, event: MouseEvent }>()
 
-  df$ = combineLatest([
+  df$: Observable<PathReturn<"/map/assign">> = combineLatest([
     this.#point,
     this.#parcellation,
     this.#template,
   ]).pipe(
     switchMap(([ point, parcellation, template ]) => {
+
+      this.#error$.next(null)
+
       if (!point || !parcellation || !template) {
         return EMPTY
       }
@@ -58,7 +69,7 @@ export class PointAssignmentComponent implements OnDestroy {
         console.warn(`point coordination space id ${ptSpaceId} is not the same as template id ${template.id}.`)
         return EMPTY
       }
-      this.#busy$.next(true)
+      this.#busy$.next(DOING_PROB_ASGMT)
       return concat(
         of(null),
         this.sapi.v3Get("/map/assign", {
@@ -66,15 +77,31 @@ export class PointAssignmentComponent implements OnDestroy {
             parcellation_id: parcellation.id,
             point: point.coordinates.map(v => `${v.value/1e6}mm`).join(','),
             space_id: template.id,
-            sigma_mm: 3.0
+            sigma_mm: 0
           }
         }).pipe(
-          tap(() => this.#busy$.next(false)),
+          catchError(() => {
+            this.#busy$.next(DOING_LABEL_ASGMT)
+            return this.sapi.v3Get("/map/assign", {
+              query: {
+                parcellation_id: parcellation.id,
+                point: point.coordinates.map(v => `${v.value/1e6}mm`).join(','),
+                space_id: template.id,
+                sigma_mm: 0,
+                assignment_type: "labelled"
+              }
+            })
+          }),
+          catchError((err) => {
+            this.#busy$.next(null)
+            this.#error$.next(err.toString())
+            return of(null)
+          }),
+          tap(() => this.#busy$.next(null)),
         )
-      ).pipe(
-        shareReplay(1),
       )
-    })
+    }),
+    shareReplay(1),
   )
 
   columns$ = this.df$.pipe(
@@ -95,4 +122,76 @@ export class PointAssignmentComponent implements OnDestroy {
     const sxplrReg = await translateV3Entities.translateRegion(region)
     this.clickOnRegion.emit({ target: sxplrReg, event })
   }
+
+  zipfileConfig$: Observable<TZipFileConfig[]> = combineLatest([
+    this.#point,
+    this.#parcellation,
+    this.#template,
+    this.df$
+  ]).pipe(
+    map(([ pt, parc, tmpl, df ]) => {
+      return [{
+        filename: 'README.md',
+        filecontent: generateReadMe(pt, parc, tmpl)
+      }, {
+        filename: 'pointassignment.csv',
+        filecontent: generateCsv(df)
+      }] as TZipFileConfig[]
+    })
+  )
+}
+
+function generateReadMe(pt: TSandsPoint, parc: SxplrParcellation, tmpl: SxplrTemplate){
+  return `# Point assignment exporter
+
+Exported by siibra-explorer verison \`${environment.VERSION}\` hash: \`${environment.GIT_HASH.trim()}\`.
+
+On: ${new Date().toString()}
+
+Data retrieved through siibra-api version \`${EXPECTED_SIIBRA_API_VERSION}\`
+
+Retrieval parameters:
+
+Point
+- coord: ${pt.coordinates.map(v => v.value/1e6).join(',')} mm
+
+Parcellation
+- name: ${parc.name || parc.shortName}
+- id: ${parc.id}
+
+Space
+- name: ${tmpl.name || tmpl.shortName}
+- id: ${tmpl.id}
+`
+}
+
+function escapeFactory(chars: string[] = []){
+  const search = new RegExp(`[${chars.join('').replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}]`, 'g')
+  return function escape(s: string) {
+    return s.replace(search, s => `\\${s}`)
+  }
+}
+
+const escapeDoubleQuotes = escapeFactory(['"'])
+
+function processRow(v: unknown[]): string{
+  const returnValue: string[] = []
+  for (const item of v) {
+
+    // region
+    if (typeof item === "object" && item?.['@type'] === "siibra-0.4/region") {
+      returnValue.push(item['name'])
+      continue
+    }
+
+    returnValue.push(JSON.stringify(item))
+  }
+  return returnValue.map(escapeDoubleQuotes).map(v => `"${v}"`).join(",")
+}
+
+function generateCsv(df: PathReturn<"/map/assign">) {
+  return [
+    df.columns.map(escapeDoubleQuotes).map(v => `"${v}"`).join(","),
+    ...df.data.map(processRow)
+  ].join("\n")
 }
diff --git a/src/atlasComponents/sapiViews/volumes/volumes.module.ts b/src/atlasComponents/sapiViews/volumes/volumes.module.ts
index eee2503b61afec1b5533c4d265ce178669ee492c..1a5a32d0f5cf07a536f3e559c29ad835ca1abb6f 100644
--- a/src/atlasComponents/sapiViews/volumes/volumes.module.ts
+++ b/src/atlasComponents/sapiViews/volumes/volumes.module.ts
@@ -6,6 +6,8 @@ import { UtilModule } from 'src/util';
 import { SpinnerModule } from 'src/components/spinner';
 import { MatDialogModule } from '@angular/material/dialog';
 import { MatButtonModule } from '@angular/material/button';
+import { MatSortModule } from '@angular/material/sort';
+import { ZipFilesOutputModule } from 'src/zipFilesOutput/module';
 
 
 
@@ -20,6 +22,8 @@ import { MatButtonModule } from '@angular/material/button';
     SpinnerModule,
     MatDialogModule,
     MatButtonModule,
+    MatSortModule,
+    ZipFilesOutputModule,
   ],
   exports: [
     PointAssignmentComponent
diff --git a/src/atlasComponents/userAnnotations/module.ts b/src/atlasComponents/userAnnotations/module.ts
index ec2586e5741ddc2d25ff4c45657154d882386946..f6c3d1682f6979a7fa2446a5f2a67c658f66d722 100644
--- a/src/atlasComponents/userAnnotations/module.ts
+++ b/src/atlasComponents/userAnnotations/module.ts
@@ -54,7 +54,7 @@ import { RoutedAnnotationService } from "./routedAnnotation.service";
     // ... in url correctly
     {
       provide: APP_INITIALIZER,
-      useFactory:(svc: RoutedAnnotationService) => {
+      useFactory:(_svc: RoutedAnnotationService) => {
         return () => Promise.resolve()
       },
       deps: [ RoutedAnnotationService ],
diff --git a/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.component.ts b/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.component.ts
index 15bfc561e3f86608e4b18f0de67970d1391d8af5..09895109507b4eb9baf2283557cabd45ca7a59bc 100644
--- a/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.component.ts
+++ b/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.component.ts
@@ -44,7 +44,7 @@ export class SingleAnnotationUnit implements OnDestroy, AfterViewInit{
 
     this.chSubs.push(
       this.formGrp.valueChanges.subscribe(value => {
-        const { name, desc, spaceId } = value
+        const { name, desc } = value
         this.managedAnnotation.name = name
         this.managedAnnotation.desc = desc
       })
diff --git a/src/atlasComponents/userAnnotations/tools/delete.ts b/src/atlasComponents/userAnnotations/tools/delete.ts
index 2e118bcd3dddb46ab2aa8d6ce702b8b14ccdd054..be375acbc193ad1a6ec52e11c33ea75ee934fa4a 100644
--- a/src/atlasComponents/userAnnotations/tools/delete.ts
+++ b/src/atlasComponents/userAnnotations/tools/delete.ts
@@ -2,7 +2,7 @@ import { Directive, OnDestroy } from "@angular/core";
 import { Observable, Subject, Subscription } from "rxjs";
 import { filter, switchMapTo, takeUntil, withLatestFrom } from "rxjs/operators";
 import { Point } from "./point";
-import { AbsToolClass, IAnnotationEvents, IAnnotationGeometry, IAnnotationTools, TAnnotationEvent, TCallbackFunction, TNgAnnotationPoint, TToolType } from "./type";
+import { AbsToolClass, IAnnotationEvents, IAnnotationGeometry, IAnnotationTools, TAnnotationEvent, TCallbackFunction, TToolType } from "./type";
 
 @Directive()
 export class ToolDelete extends AbsToolClass<Point> implements IAnnotationTools, OnDestroy {
diff --git a/src/atlasComponents/userAnnotations/tools/point.ts b/src/atlasComponents/userAnnotations/tools/point.ts
index a2e10804108682e14e844481094063cfac3fad5c..58d8cdde8acd05863e682814e37f8d667124e36e 100644
--- a/src/atlasComponents/userAnnotations/tools/point.ts
+++ b/src/atlasComponents/userAnnotations/tools/point.ts
@@ -1,4 +1,4 @@
-import { AbsToolClass, getCoord, IAnnotationEvents, IAnnotationGeometry, IAnnotationTools, INgAnnotationTypes, TAnnotationEvent, TBaseAnnotationGeomtrySpec, TCallbackFunction, TNgAnnotationEv, TSandsPoint, TToolType } from "./type";
+import { AbsToolClass, getCoord, IAnnotationEvents, IAnnotationGeometry, IAnnotationTools, INgAnnotationTypes, TAnnotationEvent, TBaseAnnotationGeomtrySpec, TCallbackFunction, TSandsPoint, TToolType } from "./type";
 import { Observable, Subject, Subscription } from "rxjs";
 import { Directive, OnDestroy } from "@angular/core";
 import { filter, switchMapTo, takeUntil } from "rxjs/operators";
diff --git a/src/atlasComponents/userAnnotations/tools/poly.ts b/src/atlasComponents/userAnnotations/tools/poly.ts
index 37a0fdbd397fe43fa136409af1503a2cb2443bbe..244c71a6a2d11163ba70af11727adcefd1ac399f 100644
--- a/src/atlasComponents/userAnnotations/tools/poly.ts
+++ b/src/atlasComponents/userAnnotations/tools/poly.ts
@@ -1,7 +1,7 @@
-import { IAnnotationTools, IAnnotationGeometry, TAnnotationEvent, IAnnotationEvents, AbsToolClass, INgAnnotationTypes, TNgAnnotationEv, TToolType, TBaseAnnotationGeomtrySpec, TSandsPolyLine, getCoord, TCallbackFunction } from "./type";
+import { IAnnotationTools, IAnnotationGeometry, TAnnotationEvent, IAnnotationEvents, AbsToolClass, INgAnnotationTypes, TToolType, TBaseAnnotationGeomtrySpec, TSandsPolyLine, getCoord, TCallbackFunction } from "./type";
 import { Point, TPointJsonSpec } from './point'
 import { Directive, OnDestroy } from "@angular/core";
-import { merge, Observable, Subject, Subscription } from "rxjs";
+import { Observable, Subject, Subscription } from "rxjs";
 import { filter, switchMapTo, takeUntil, withLatestFrom } from "rxjs/operators";
 import { getUuid } from "src/util/fn";
 
@@ -129,7 +129,7 @@ export class Polygon extends IAnnotationGeometry{
   }
 
   parseNgAnnotationObj(pickedAnnotationId: string, pickedOffset: number): { edge: [number, number], edgeIdx: number, point: Point, pointIdx: number } {
-    const [ id, edgeIdx, _shouldBeZero ] = pickedAnnotationId.split('_')
+    const [ id, edgeIdx, /* _shouldBeZero */ ] = pickedAnnotationId.split('_')
     if (id !== this.id) return null
 
     if (pickedOffset === 0) {
diff --git a/src/atlasComponents/userAnnotations/tools/select.ts b/src/atlasComponents/userAnnotations/tools/select.ts
index 10befd2dfe4b65ade055f2fcc81d57af5e2d4d3b..3653d86efb003f7676018b6adf53a5d7b40c80ef 100644
--- a/src/atlasComponents/userAnnotations/tools/select.ts
+++ b/src/atlasComponents/userAnnotations/tools/select.ts
@@ -2,7 +2,7 @@ import { Directive, OnDestroy } from "@angular/core";
 import { Observable, Subject, Subscription } from "rxjs";
 import { filter } from 'rxjs/operators'
 import { Point } from "./point";
-import { AbsToolClass, IAnnotationEvents, IAnnotationGeometry, IAnnotationTools, TAnnotationEvent, TCallbackFunction, TNgAnnotationPoint, TToolType } from "./type";
+import { AbsToolClass, IAnnotationEvents, IAnnotationGeometry, IAnnotationTools, TAnnotationEvent, TCallbackFunction, TToolType } from "./type";
 
 @Directive()
 export class ToolSelect extends AbsToolClass<Point> implements IAnnotationTools, OnDestroy {
diff --git a/src/atlasComponents/userAnnotations/tools/service.ts b/src/atlasComponents/userAnnotations/tools/service.ts
index 4a18bd4f3dc806ea1bef95f1f6339a17b63f6ead..03f0f060e620f05897e380044ef381bea50c5c71 100644
--- a/src/atlasComponents/userAnnotations/tools/service.ts
+++ b/src/atlasComponents/userAnnotations/tools/service.ts
@@ -6,7 +6,7 @@ import { BehaviorSubject, combineLatest, fromEvent, merge, Observable, of, Subje
 import {map, switchMap, filter, shareReplay, pairwise, withLatestFrom } from "rxjs/operators";
 import { NehubaViewerUnit } from "src/viewerModule/nehuba";
 import { NEHUBA_INSTANCE_INJTKN } from "src/viewerModule/nehuba/util";
-import { AbsToolClass, ANNOTATION_EVENT_INJ_TOKEN, IAnnotationEvents, IAnnotationGeometry, INgAnnotationTypes, INJ_ANNOT_TARGET, TAnnotationEvent, ClassInterface, TCallbackFunction, TSands, TGeometryJson, TNgAnnotationLine, TCallback } from "./type";
+import { AbsToolClass, ANNOTATION_EVENT_INJ_TOKEN, IAnnotationEvents, IAnnotationGeometry, INgAnnotationTypes, INJ_ANNOT_TARGET, TAnnotationEvent, ClassInterface, TCallbackFunction, TSands, TGeometryJson, TCallback } from "./type";
 import { getExportNehuba, switchMapWaitFor } from "src/util/fn";
 import { Polygon } from "./poly";
 import { Line } from "./line";
@@ -194,7 +194,7 @@ export class ModularUserAnnotationToolService implements OnDestroy{
   }): AbsToolClass<any>{
     const { toolCls: Cls, target, editCmp } = arg
     const newTool = new Cls(this.annotnEvSubj, arg => this.handleToolCallback(arg)) as T & { ngOnDestroy?: () => void }
-    const { name, iconClass, onMouseMoveRenderPreview } = newTool
+    const { name, iconClass } = newTool
     
     this.moduleAnnotationTypes.push({
       instance: newTool,
diff --git a/src/atlasViewer/atlasViewer.component.ts b/src/atlasViewer/atlasViewer.component.ts
index 9255c9065120627b7998d0c9c297972a69f1c0ff..c28d15f9a80133259294b4de407a543014adcf78 100644
--- a/src/atlasViewer/atlasViewer.component.ts
+++ b/src/atlasViewer/atlasViewer.component.ts
@@ -18,7 +18,7 @@ import { colorAnimation } from "./atlasViewer.animation"
 import { MouseHoverDirective } from "src/mouseoverModule";
 import {MatSnackBar, MatSnackBarRef} from "@angular/material/snack-bar";
 import {MatDialog, MatDialogRef} from "@angular/material/dialog";
-import { ARIA_LABELS, CONST } from 'common/constants'
+import { CONST } from 'common/constants'
 
 import { SlServiceService } from "src/spotlight/sl-service.service";
 import { ClickInterceptorService } from "src/glue";
@@ -28,12 +28,6 @@ import { userPreference } from "src/state"
 import { DARKTHEME } from "src/util/injectionTokens";
 import { EnumQuickTourSeverity } from "src/ui/quickTour/constrants";
 
-/**
- * TODO
- * check against auxlillary mesh indicies, to only filter out aux indicies
- */
-const filterFn = (segment) => typeof segment.segment !== 'string'
-const compareFn = (it, item) => it.name === item.name
 
 @Component({
   selector: 'atlas-viewer',
@@ -49,7 +43,6 @@ const compareFn = (it, item) => it.name === item.name
 export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit {
 
   public CONST = CONST
-  public compareFn = compareFn
 
   @ViewChild('cookieAgreementComponent', {read: TemplateRef}) public cookieAgreementComponent: TemplateRef<any>
 
diff --git a/src/atlasViewer/atlasViewer.workerService.service.ts b/src/atlasViewer/atlasViewer.workerService.service.ts
index 67a422714822d25e47fdb55b345116cda9db8eba..32d6c3b05e767da8b43d6c019f3c7e440cdae729 100644
--- a/src/atlasViewer/atlasViewer.workerService.service.ts
+++ b/src/atlasViewer/atlasViewer.workerService.service.ts
@@ -34,7 +34,7 @@ export class AtlasWorkerService {
     ).toPromise()
 
     const { data: returnData } = message as MessageEvent
-    const { id, error, ...rest } = returnData
+    const { error, ...rest } = returnData
     if (error) {
       throw new Error(error.message || error)
     }
diff --git a/src/components/fabSpeedDial/fabSpeedDialContainer.directive.ts b/src/components/fabSpeedDial/fabSpeedDialContainer.directive.ts
index 2c5967a634ff77bd08cf6b46a920afdd6fc51c74..321e5f9bc6f52dbb1289af7ea04db2084d5a341d 100644
--- a/src/components/fabSpeedDial/fabSpeedDialContainer.directive.ts
+++ b/src/components/fabSpeedDial/fabSpeedDialContainer.directive.ts
@@ -1,4 +1,4 @@
-import { Directive, OnDestroy, Output, EventEmitter, Input, OnChanges, SimpleChanges, HostListener, ElementRef, HostBinding } from "@angular/core";
+import { Directive, OnDestroy, Output, EventEmitter, Input, OnChanges, SimpleChanges, HostBinding } from "@angular/core";
 import { FabSpeedDialService } from "./fabSpeedDial.service";
 import { Subscription } from "rxjs";
 import { SCALE_ORIGIN } from './fabSpeedDial.service'
diff --git a/src/components/flatHierarchy/spacer.pipe.ts b/src/components/flatHierarchy/spacer.pipe.ts
index a7533b68c759ed72c1a56b87a762409a4d8a5f2b..73498906a0e6aa52ac93a16027c59e20f30b7c0d 100644
--- a/src/components/flatHierarchy/spacer.pipe.ts
+++ b/src/components/flatHierarchy/spacer.pipe.ts
@@ -7,7 +7,7 @@ import { TreeNode } from "./const"
 })
 
 export class FlatHierarchySpacer implements PipeTransform{
-  public transform<T>(inputNode: TreeNode<T>, ...args: any[]) {
+  public transform<T>(inputNode: TreeNode<T>, ..._args: any[]) {
     return Array(inputNode.level).fill(null)
   }
 }
diff --git a/src/components/flatHierarchy/treeView/treeControl.ts b/src/components/flatHierarchy/treeView/treeControl.ts
index 7471dfbb839cb36c23413d026c2f9c8f607af6dd..d8676d4ff1b7840994f9ac12bebed0ab5c8d8e31 100644
--- a/src/components/flatHierarchy/treeView/treeControl.ts
+++ b/src/components/flatHierarchy/treeView/treeControl.ts
@@ -13,7 +13,7 @@ export class Tree<T extends Record<string, unknown>>{
     return this._nodes
   }
 
-  private _isParent: IsParent<T> = (c, p) => false
+  private _isParent: IsParent<T> = (_c, _p) => false
   protected set isParent(fn: IsParent<T>){
     if (fn === this._isParent) return
     this._isParent = fn
@@ -81,7 +81,7 @@ export class Tree<T extends Record<string, unknown>>{
 
   constructor(
     _nodes: T[] = [],
-    _isParent: IsParent<T> = (c, p) => false
+    _isParent: IsParent<T> = (_c, _p) => false
   ){
     this._nodes = _nodes
     this._isParent = _isParent
diff --git a/src/components/markdown/markdownCmp/markdown.component.ts b/src/components/markdown/markdownCmp/markdown.component.ts
index 7084626a5ed37fb4162d610edfe232fcf3852381..fd6776dec0c312e74c659bff8fb4668e2fbf130a 100644
--- a/src/components/markdown/markdownCmp/markdown.component.ts
+++ b/src/components/markdown/markdownCmp/markdown.component.ts
@@ -1,4 +1,4 @@
-import { ChangeDetectionStrategy, Component, ElementRef, Input, ViewChild, ChangeDetectorRef, AfterViewChecked, OnChanges, SecurityContext } from '@angular/core'
+import { ChangeDetectionStrategy, Component, ElementRef, Input, ViewChild, ChangeDetectorRef, OnChanges, SecurityContext } from '@angular/core'
 import { DomSanitizer } from '@angular/platform-browser'
 import * as showdown from 'showdown'
 
diff --git a/src/components/parseAttribute.directive.ts b/src/components/parseAttribute.directive.ts
deleted file mode 100644
index 34a9b124841c32106a8f590f00d80d69f85d337b..0000000000000000000000000000000000000000
--- a/src/components/parseAttribute.directive.ts
+++ /dev/null
@@ -1,75 +0,0 @@
-import { Directive, OnChanges, SimpleChanges } from "@angular/core";
-
-// TODO deprecate this directive
-function parseAttribute(arg: any, expectedType: string) {
-
-  // if(
-  //   typeof arg === expectedType ||
-  //   arg === undefined ||
-  //   arg === null
-  // ){
-  //   return arg
-  // }
-  // switch (expectedType){
-  //   case 'object':
-  //     try{
-  //       const json =  JSON.parse(arg)
-  //       return json
-  //     }catch(e){
-  //       this.log.warn('parseAttribute error, cannot JSON.parse object')
-  //       return arg
-  //     }
-  //   case 'boolean' :
-  //     return arg === 'true'
-  //   case 'number':
-  //     return isNaN(arg) ? 0 : Number(arg)
-
-  //   case 'string':
-  //   default :
-  //     return arg
-  // }
-
-  /* return if empty string */
-  if (
-    arg === '' ||
-    arg === undefined ||
-    arg === null
-  ) {
-    return arg
-  }
-
-  if (!isNaN(arg)) {
-    return Number(arg)
-  }
-
-  if (arg === 'true') {
-    return true
-  }
-
-  if (arg === 'false') {
-    return false
-  }
-
-  try {
-    const json = JSON.parse(arg)
-    return json
-  } catch (e) {
-    // this.log.warn('parseAttribute, parse JSON, not a json')
-    /* not a json, continue */
-    /* probably print in debug mode */
-  }
-
-  return arg
-}
-
-@Directive({
-  selector : '[ivparseattribute]',
-})
-
-export class ParseAttributeDirective implements OnChanges {
-  public ngOnChanges(simpleChanges: SimpleChanges) {
-    Object.keys(simpleChanges).forEach(key => {
-      this[key] = parseAttribute(simpleChanges[key].currentValue, typeof simpleChanges[key].previousValue)
-    })
-  }
-}
diff --git a/src/components/smartChip/component/smartChip.component.ts b/src/components/smartChip/component/smartChip.component.ts
index b68cfb42769e31067b42614351e8db04717fdcf7..99eed11983dd317c32c5f4875e93f640caf8bf95 100644
--- a/src/components/smartChip/component/smartChip.component.ts
+++ b/src/components/smartChip/component/smartChip.component.ts
@@ -13,12 +13,12 @@ const cssColorToHsl = (input: string) => {
       parseInt(match[2]),
       parseInt(match[3]),
     ]
-    const [_h, _s, l] = rgbToHsl(...rgb)
+    const [ /* _h */, /* _s */, l] = rgbToHsl(...rgb)
     return l
   }
   if (/hsl/i.test(input)) {
     const match = /\((.*)\)/.exec(input)
-    const [h, s, l] = match[1].split(",")
+    const [ /* _h */, /* _s */, l] = match[1].split(",")
     const trimmedL = l.trim()
     if (/%$/.test(trimmedL)) {
       const match = /^([0-9]+)%/.exec(trimmedL)
@@ -27,7 +27,7 @@ const cssColorToHsl = (input: string) => {
   }
   if (/^#/i.test(input) && input.length === 7) {
     const [r, g, b] = hexToRgb(input)
-    const [_h, _s, l] = rgbToHsl(r, g, b)
+    const [ /*_h */, /*_s */, l] = rgbToHsl(r, g, b)
     return l
   }
   throw new Error(`Cannot parse css color: ${input}`)
diff --git a/src/features/category-acc.directive.ts b/src/features/category-acc.directive.ts
index cddba9e69912494aa8a156eb802fe2b25f76ad1e..73156e7af17e6cf4e39e97fca101655eb02b07bb 100644
--- a/src/features/category-acc.directive.ts
+++ b/src/features/category-acc.directive.ts
@@ -118,7 +118,8 @@ export class CategoryAccDirective implements AfterContentInit, OnDestroy {
           return this.datasource
         }),
       )
-    })
+    }),
+    shareReplay(1),
   )
 
   constructor(){
diff --git a/src/features/connectivity/connectivityBrowser/connectivityBrowser.component.ts b/src/features/connectivity/connectivityBrowser/connectivityBrowser.component.ts
index dbc546a7ead3d44685043b5654767b8499debe28..77b69d99dccae4d04870b68d7ac6a223b59548df 100644
--- a/src/features/connectivity/connectivityBrowser/connectivityBrowser.component.ts
+++ b/src/features/connectivity/connectivityBrowser/connectivityBrowser.component.ts
@@ -1,7 +1,7 @@
 import {  Component, ElementRef, OnDestroy, ViewChild, Input, SimpleChanges, HostListener, OnChanges } from "@angular/core";
 import { Store, select} from "@ngrx/store";
 import { Subscription, BehaviorSubject, combineLatest, merge, concat, NEVER} from "rxjs";
-import { switchMap, map, tap, shareReplay, distinctUntilChanged, withLatestFrom, filter, finalize } from "rxjs/operators";
+import { switchMap, map, shareReplay, distinctUntilChanged, withLatestFrom, filter, finalize } from "rxjs/operators";
 
 import { atlasAppearance, atlasSelection } from "src/state";
 import { SAPI } from "src/atlasComponents/sapi/sapi.service";
@@ -156,13 +156,15 @@ export class ConnectivityBrowserComponent implements OnChanges, OnDestroy {
        * on
        * - accordion update
        * - colormap change
-       * - fetching matrix
+       * - fetching matrix flag is true
        * remove custom layer
        */
       merge(
         this.#accordionExpanded$,
         this.colormap$,
-        this.#fetchingMatrix$,
+        this.#fetchingMatrix$.pipe(
+          filter(flag => !!flag)
+        ),
       ).subscribe(() => {
         this.removeCustomLayer()
       }),
@@ -207,6 +209,7 @@ export class ConnectivityBrowserComponent implements OnChanges, OnDestroy {
        * on pure connection update, update logchecked box
        */
       this.#pureConnections$.subscribe(v => {
+        if (!v) return
         for (const val of Object.values(v)) {
           if (val > 1) {
             this.displayForm.get("logChecked").enable()
@@ -256,30 +259,32 @@ export class ConnectivityBrowserComponent implements OnChanges, OnDestroy {
         }
 
         const typedName = getType(selectedType.name)
-        if (!guardType(typedName)) {
-          throw new Error(`type ${typedName} is not in ${validTypes.join(',')}`)
-        }
         const query = {
           parcellation_id: parc.id,
           type: typedName
         }
         this.busy$.next(true)
-        return this.sapi.v3Get(
-          "/feature/RegionalConnectivity",
-          { query }
-        ).pipe(
-          switchMap(resp =>
-            this.sapi.iteratePages(
-              resp,
-              page => this.sapi.v3Get(
-                "/feature/RegionalConnectivity",
-                { query: { ...query, page } }
-              )
-            )
+        return concat(
+          of(
+            [] as PathReturn<"/feature/RegionalConnectivity/{feature_id}">[],
           ),
-          finalize(() => {
-            this.busy$.next(false)
-          })
+          this.sapi.v3Get(
+            "/feature/RegionalConnectivity",
+            { query }
+          ).pipe(
+            switchMap(resp =>
+              this.sapi.iteratePages(
+                resp,
+                page => this.sapi.v3Get(
+                  "/feature/RegionalConnectivity",
+                  { query: { ...query, page } }
+                )
+              )
+            ),
+            finalize(() => {
+              this.busy$.next(false)
+            })
+          )
         )
       })
     )),
@@ -328,7 +333,7 @@ export class ConnectivityBrowserComponent implements OnChanges, OnDestroy {
   displaySubject$ = this.selectedDataset$.pipe(
     distinctUntilChanged((o, n) => o?.id === n?.id),
     map(ds => {
-      return (idx: number) => ds.subjects[idx]
+      return (idx: number) => ds?.subjects?.[idx]
     })  
   )
 
@@ -336,7 +341,7 @@ export class ConnectivityBrowserComponent implements OnChanges, OnDestroy {
     map(ds => ds ? ds.datasets : [])
   )
 
-  #fetchingMatrix$ = new BehaviorSubject<boolean>(true)
+  #fetchingMatrix$ = new BehaviorSubject<boolean>(false)
 
   #matrixInput$ = combineLatest([
     this.parcellation$,
@@ -346,7 +351,8 @@ export class ConnectivityBrowserComponent implements OnChanges, OnDestroy {
     map(([ parcellation, form, dss ]) => {
       const {
         selectedDatasetIndex: dsIdx,
-        selectedSubjectIndex: subIdx
+        selectedSubjectIndex: subIdx,
+        selectedView
       } = form
       const ds = dss[dsIdx]
       if (!ds) {
@@ -360,7 +366,8 @@ export class ConnectivityBrowserComponent implements OnChanges, OnDestroy {
       return {
         parcellation,
         feature_id: ds.id,
-        subject
+        subject,
+        selectedView
       }
     }),
     shareReplay(1),
@@ -379,26 +386,33 @@ export class ConnectivityBrowserComponent implements OnChanges, OnDestroy {
         {
           query: {
             parcellation_id: parcellation.id,
-            subject,
+            ...(input.selectedView === "average"
+            ? {}
+            : { subject })
           },
           path: {
             feature_id
           }
         }
+      ).pipe(
+        finalize(() => {
+          this.#fetchingMatrix$.next(false)
+        })
       )
     }),
-    tap(() => this.#fetchingMatrix$.next(false)),
     shareReplay(1),
   )
 
   #pureConnections$ = this.#matrixInput$.pipe(
-    filter(v => !!v),
-    switchMap(({ subject }) =>
+    switchMap(matrixInput =>
       this.#selectedMatrix$.pipe(
-        filter(v => !!v.matrices[subject]),
         withLatestFrom(this.region$),
         map(([ v, region ]) => {
-          const b = v.matrices[subject]
+          const matrixKey = matrixInput?.selectedView === "average" ? "_average" : matrixInput?.subject
+          if (!v || !matrixInput || !v.matrices?.[matrixKey]) {
+            return null
+          }
+          const b = v.matrices[matrixKey]
           const foundIdx = b.columns.findIndex(v => v['name'] === region.name)
           if (typeof foundIdx !== 'number') {
             return null
@@ -429,6 +443,7 @@ export class ConnectivityBrowserComponent implements OnChanges, OnDestroy {
           distinctUntilChanged()
         )
       ]).pipe(
+        filter(conn => !!conn),
         map(([ conn, flag ]) => processProfile(conn, flag))
       )
     ))
@@ -545,12 +560,6 @@ function getType(name: string) {
   return name.split(".").slice(-1)[0]
 }
 
-const validTypes = ["FunctionalConnectivity", "StreamlineCounts", "StreamlineLengths"]
-
-function guardType(name: unknown): name is "FunctionalConnectivity" | "StreamlineCounts" | "StreamlineLengths" {
-  return typeof name === "string" && validTypes.includes(name)
-}
-
 type ConnectedArea = {
     color: {r: number, g: number, b: number}
     name: string
diff --git a/src/features/connectivity/connectivityBrowser/connectivityBrowser.template.html b/src/features/connectivity/connectivityBrowser/connectivityBrowser.template.html
index a7061fe7a83fedfb63c776e39deb613d1c5008dd..416763f02dab74f3dd141ab7d954d7155739a5b7 100644
--- a/src/features/connectivity/connectivityBrowser/connectivityBrowser.template.html
+++ b/src/features/connectivity/connectivityBrowser/connectivityBrowser.template.html
@@ -16,29 +16,37 @@
                     </mat-select>
                 </mat-form-field>
 
-                <mat-form-field *ngIf="!(busy$ | async) && (formValue$ | async | getProperty : 'selectedType')" class="flex-grow-1 flex-shrink-1 w-100">
-                    <mat-label>
-                        Cohort
-                    </mat-label>
-                    <mat-select formControlName="selectedCohort">
-                        <mat-option *ngFor="let cohort of cohorts$ | async"
-                            [value]="cohort">
-                            {{ cohort }}
-                        </mat-option>
-                    </mat-select>
-                </mat-form-field>
-            </div>
-        </ng-template>
+                <ng-template [ngIf]="cohorts$ | async" let-cohorts>
+                    <ng-template [ngIf]="cohorts.length > 0">
 
-        <ng-template [ngIf]="formValue$ | async | getProperty : 'selectedCohort'">
-            <mat-radio-group formControlName="selectedView">
-                <mat-radio-button value="average" class="m-2" color="primary">
-                    Average
-                </mat-radio-button>
-                <mat-radio-button value="subject" class="m-2" color="primary">
-                    Subject
-                </mat-radio-button>
-            </mat-radio-group>
+                        <mat-form-field class="flex-grow-1 flex-shrink-1 w-100">
+                            <mat-label>
+                                Cohort
+                            </mat-label>
+                            <mat-select formControlName="selectedCohort">
+                                <mat-option *ngFor="let cohort of cohorts$ | async"
+                                    [value]="cohort">
+                                    {{ cohort }}
+                                </mat-option>
+                            </mat-select>
+                        </mat-form-field>
+
+                        
+                        <ng-template [ngIf]="formValue$ | async | getProperty : 'selectedCohort'">
+                            <mat-radio-group formControlName="selectedView">
+                                <mat-radio-button value="average" class="m-2" color="primary">
+                                    Average
+                                </mat-radio-button>
+                                <mat-radio-button value="subject" class="m-2" color="primary">
+                                    Subject
+                                </mat-radio-button>
+                            </mat-radio-group>
+                        </ng-template>
+                
+                    </ng-template>
+                </ng-template>
+
+            </div>
         </ng-template>
 
         <ng-template [ngIf]="cohortDatasets$ | async" let-cohortDatasets>
@@ -84,7 +92,7 @@
         <!-- loading spinner -->
         <ng-template [ngIf]="view$ | async | getProperty : 'fetchingMatrix'"
             [ngIfElse]="profileTmpl">
-            <div class="d-flex justify-content-center" id = 'blabla'>
+            <div class="d-flex justify-content-center">
                 <mat-spinner></mat-spinner>
             </div>
         </ng-template>
@@ -92,7 +100,13 @@
         <!-- profile -->
         <!-- <pre>{{ view$ | async | json }}</pre> -->
         <ng-template #profileTmpl>
-            <ng-template [ngIf]="view$ | async | getProperty : 'connections'" let-conn>
+            
+            <ng-template #noConnTmpl>
+                No connectivity Found
+            </ng-template>
+        
+            <ng-template [ngIf]="view$ | async | getProperty : 'connections'" let-conn
+                [ngIfElse]="noConnTmpl">
                 <div class="d-flex align-items-center">
                     <form [formGroup]="displayForm">
                         <mat-checkbox
diff --git a/src/features/entry/entry.component.ts b/src/features/entry/entry.component.ts
index 979fea404ef798b8f0bfc510477bf423782938b0..e4cfbe75e126083681838c1fe815bca654737345 100644
--- a/src/features/entry/entry.component.ts
+++ b/src/features/entry/entry.component.ts
@@ -1,20 +1,21 @@
 import { AfterViewInit, Component, OnDestroy, QueryList, ViewChildren } from '@angular/core';
 import { select, Store } from '@ngrx/store';
-import { distinctUntilChanged, map, scan, shareReplay, switchMap, tap } from 'rxjs/operators';
+import { debounceTime, distinctUntilChanged, map, scan, shareReplay, switchMap, withLatestFrom } from 'rxjs/operators';
 import { IDS, SAPI } from 'src/atlasComponents/sapi';
 import { Feature } from 'src/atlasComponents/sapi/sxplrTypes';
 import { FeatureBase } from '../base';
 import * as userInteraction from "src/state/userInteraction"
 import { atlasSelection } from 'src/state';
 import { CategoryAccDirective } from "../category-acc.directive"
-import { BehaviorSubject, combineLatest, concat, merge, of, Subscription } from 'rxjs';
+import { combineLatest, concat, forkJoin, merge, of, Subject, Subscription } from 'rxjs';
 import { DsExhausted, IsAlreadyPulling, PulledDataSource } from 'src/util/pullable';
 import { TranslatedFeature } from '../list/list.directive';
+import { SPECIES_ENUM } from 'src/util/constants';
 
 const categoryAcc = <T extends Record<string, unknown>>(categories: T[]) => {
   const returnVal: Record<string, T[]> = {}
   for (const item of categories) {
-    const { category, ...rest } = item
+    const { category } = item
     if (!category) continue
     if (typeof category !== "string") continue
     if (!returnVal[category]) {
@@ -33,56 +34,63 @@ const categoryAcc = <T extends Record<string, unknown>>(categories: T[]) => {
 })
 export class EntryComponent extends FeatureBase implements AfterViewInit, OnDestroy {
 
-  private _features$ = new BehaviorSubject<TranslatedFeature[]>([])
-  features$ = this._features$.pipe(
-    shareReplay(1)
-  )
-
   @ViewChildren(CategoryAccDirective)
   catAccDirs: QueryList<CategoryAccDirective>
 
-  public busyTallying$ = new BehaviorSubject<boolean>(false)
-  public totals$ = new BehaviorSubject<number>(null)
-
   constructor(private sapi: SAPI, private store: Store) {
     super()
   }
 
   #subscriptions: Subscription[] = []
+  #catAccDirs = new Subject<CategoryAccDirective[]>()
+  features$ = this.#catAccDirs.pipe(
+    switchMap(dirs => concat(
+      of([] as TranslatedFeature[]),
+      merge(...dirs.map((dir, idx) =>
+        dir.datasource$.pipe(
+          switchMap(ds =>  ds.data$),
+          map(val => ({ val, idx }))
+        ))
+      ).pipe(
+        map(({ idx, val }) => ({ [idx.toString()]: val })),
+        scan((acc, curr) => ({ ...acc, ...curr })),
+        map(record => Object.values(record).flatMap(v => v))
+      )
+    )),
+    shareReplay(1),
+  )
 
-  private _busy$ = new BehaviorSubject<boolean>(false)
-  busy$ = this._busy$.pipe(
+  busy$ = this.#catAccDirs.pipe(
+    switchMap(dirs => combineLatest(
+      dirs.map(dir => dir.isBusy$)
+    )),
+    map(flags => flags.some(flag => flag)),
+    distinctUntilChanged(),
     shareReplay(1)
   )
 
-  ngOnDestroy(): void {
-    while (this.#subscriptions.length > 0) this.#subscriptions.pop().unsubscribe()
-  }
-  ngAfterViewInit(): void {
-    const catAccDirs$ = merge(
-      of(null),
-      this.catAccDirs.changes
-    ).pipe(
-      map(() => Array.from(this.catAccDirs))
-    )
-    this.#subscriptions.push(
-      catAccDirs$.pipe(
-        switchMap(dirs => combineLatest(
-          dirs.map(dir => dir.isBusy$)
-        )),
-        map(flags => flags.some(flag => flag)),
-        distinctUntilChanged(),
-      ).subscribe(value => {
-        this._busy$.next(value)
-      }),
-      catAccDirs$.pipe(
-        tap(() => this.busyTallying$.next(true)),
-        switchMap(catArrDirs => merge(
-          ...catArrDirs.map((dir, idx) => dir.total$.pipe(
-            map(val => ({ idx, val }))
-          ))
-        )),
-        
+  public busyTallying$ = this.#catAccDirs.pipe(
+    switchMap(arr => concat(
+      of(true),
+      forkJoin(
+        arr.map(dir => dir.total$)
+      ).pipe(
+        map(() => false)
+      )
+    )),
+    shareReplay(1)
+  )
+
+  public totals$ = this.#catAccDirs.pipe(
+    switchMap(arr => concat(
+      of(0),
+      merge(
+        ...arr.map((dir, idx) =>
+          dir.total$.pipe(
+            map(val => ({ val, idx }))
+          )
+        )
+      ).pipe(
         map(({ idx, val }) => ({ [idx.toString()]: val })),
         scan((acc, curr) => ({ ...acc, ...curr })),
         map(record => {
@@ -92,11 +100,46 @@ export class EntryComponent extends FeatureBase implements AfterViewInit, OnDest
           }
           return tally
         }),
-        tap(num => {
-          this.busyTallying$.next(false)
-          this.totals$.next(num)
-        }),
-      ).subscribe(),
+      )
+    ))
+  )
+
+  ngOnDestroy(): void {
+    while (this.#subscriptions.length > 0) this.#subscriptions.pop().unsubscribe()
+  }
+  ngAfterViewInit(): void {
+    this.#subscriptions.push(
+      merge(
+        of(null),
+        this.catAccDirs.changes
+      ).pipe(
+        map(() => Array.from(this.catAccDirs))
+      ).subscribe(dirs => this.#catAccDirs.next(dirs)),
+
+      this.#pullAll.pipe(
+        debounceTime(320),
+        withLatestFrom(this.#catAccDirs),
+        switchMap(([_, dirs]) => combineLatest(dirs.map(dir => dir.datasource$))),
+      ).subscribe(async dss => {
+        await Promise.all(
+          dss.map(async ds => {
+            // eslint-disable-next-line no-constant-condition
+            while (true) {
+              try {
+                await ds.pull()
+              } catch (e) {
+                if (e instanceof DsExhausted) {
+                  break
+                }
+                if (e instanceof IsAlreadyPulling ) {
+                  continue
+                }
+                throw e
+              }
+            }
+          })
+        )
+      })
     )
   }
 
@@ -106,10 +149,10 @@ export class EntryComponent extends FeatureBase implements AfterViewInit, OnDest
 
   public showConnectivity$ = combineLatest([
     this.selectedAtlas$.pipe(
-      map(atlas => atlas?.species === "Homo sapiens")
+      map(atlas => atlas?.species === SPECIES_ENUM.HOMO_SAPIENS || atlas?.species === SPECIES_ENUM.RATTUS_NORVEGICUS)
     ),
     this.TPRBbox$.pipe(
-      map(({ parcellation }) => parcellation?.id === IDS.PARCELLATION.JBA29)
+      map(({ parcellation }) => parcellation?.id === IDS.PARCELLATION.JBA29 || parcellation?.id === IDS.PARCELLATION.WAXHOLMV4)
     )
   ]).pipe(
     map(flags => flags.every(f => f))
@@ -167,28 +210,8 @@ export class EntryComponent extends FeatureBase implements AfterViewInit, OnDest
     }
   }
 
-  async pullAll(){
-    const dss = Array.from(this.catAccDirs).map(catAcc => catAcc.datasource)
-
-    this._features$.next([])
-    await Promise.all(
-      dss.map(async ds => {
-        // eslint-disable-next-line no-constant-condition
-        while (true) {
-          try {
-            await ds.pull()
-          } catch (e) {
-            if (e instanceof DsExhausted) {
-              break
-            }
-            if (e instanceof IsAlreadyPulling ) {
-              continue
-            }
-            throw e
-          }
-        }
-      })
-    )
-    this._features$.next(dss.flatMap(ds => ds.finalValue))
+  #pullAll = new Subject()
+  pullAll(){
+    this.#pullAll.next(null)
   }
 }
diff --git a/src/features/voi-bbox.directive.ts b/src/features/voi-bbox.directive.ts
index d6ed2e0aac4ca11816b5f214699ed9c07cc8a47d..f150225b9cf66f5ce2f68f26c7a936f1c30f6576 100644
--- a/src/features/voi-bbox.directive.ts
+++ b/src/features/voi-bbox.directive.ts
@@ -47,7 +47,7 @@ export class VoiBboxDirective implements OnDestroy {
   
   @Input()
   set features(feats: Feature[]){
-    this.#voiFeatures = feats.filter(isVoiData)
+    this.#voiFeatures = (feats || []).filter(isVoiData)
     this.#features$.next(this.#voiFeatures)
   }
   get features(): VoiFeature[]{
diff --git a/src/index.html b/src/index.html
index 0f009f4f2d5ab33219754015cfc136a5c8758899..1ed2d8187899e51442bc39c8ab9305ac15c7ea0f 100644
--- a/src/index.html
+++ b/src/index.html
@@ -14,7 +14,7 @@
   <script src="extra_js.js"></script>
   <script src="https://unpkg.com/kg-dataset-previewer@1.2.0/dist/kg-dataset-previewer/kg-dataset-previewer.js" defer></script>
   <script src="https://unpkg.com/three-surfer@0.0.13/dist/bundle.js" defer></script>
-  <script type="module" src="https://unpkg.com/ng-layer-tune@0.0.6/dist/ng-layer-tune/ng-layer-tune.esm.js"></script>
+  <script type="module" src="https://unpkg.com/ng-layer-tune@0.0.13/dist/ng-layer-tune/ng-layer-tune.esm.js"></script>
   <script type="module" src="https://unpkg.com/hbp-connectivity-component@0.6.6/dist/connectivity-component/connectivity-component.js" ></script>
   <script defer src="https://unpkg.com/mathjax@3.1.2/es5/tex-svg.js"></script>
   <script defer src="https://unpkg.com/d3@6.2.0/dist/d3.min.js"></script>
diff --git a/src/keyframesModule/keyframeCtrl/keyframeCtrl.component.ts b/src/keyframesModule/keyframeCtrl/keyframeCtrl.component.ts
index 6f909d5280166c3544c8f3ec81ed0610db49fa88..1b39b366a26ad75ffd55b1a2af12fc78679dd20e 100644
--- a/src/keyframesModule/keyframeCtrl/keyframeCtrl.component.ts
+++ b/src/keyframesModule/keyframeCtrl/keyframeCtrl.component.ts
@@ -171,13 +171,11 @@ export class KeyFrameCtrlCmp implements OnDestroy {
         )
 
         const startVec = vec1.clone()
-        const vec1Length = vec1.length()
         const vec2 = new THREE.Vector3(
           toPayloadCamera.x,
           toPayloadCamera.y,
           toPayloadCamera.z,
         )
-        const vec2Length = vec2.length()
         vec1.normalize()
         vec2.normalize()
 
diff --git a/src/messaging/nmvSwc/index.ts b/src/messaging/nmvSwc/index.ts
index d7bd4d6a767d013e2e357cb98d79202a7ecfed08..87e4b10ef6a82fee3b44926a4e7e02684274433c 100644
--- a/src/messaging/nmvSwc/index.ts
+++ b/src/messaging/nmvSwc/index.ts
@@ -86,12 +86,6 @@ const getAtlas = (spaceId: SPACE_ID) => {
   return DEFAULT_ATLAS[spaceId]
 }
 
-const getVoxelFromSpace = (spaceId: string) => {
-  for (const key in NM_IDS){
-    if (NM_IDS[key] === spaceId) return IAV_VOXEL_SIZES_NM[key]
-  }
-  return null
-}
 
 export const processJsonLd = (json: { [key: string]: any }): Observable<IMessagingActions<keyof IMessagingActionTmpl>> => {
   const subject = new Subject<IMessagingActions<keyof IMessagingActionTmpl>>()
@@ -156,27 +150,16 @@ export const processJsonLd = (json: { [key: string]: any }): Observable<IMessagi
     )
     const uuid = getUuid()
 
-    // NG internal treats skeleton as mm
-    const voxelSize = getVoxelFromSpace(toSpace)
-    /**
-     * swc seem to scale with voxelSize... strangely enough
-     * voxelSize nm / voxel -> goal is 1 voxel/um
-     * 1e3 / voxelSize
-     */
-    const scaleUmToVoxelFixed = [
-      voxelSize[0],
-      voxelSize[1],
-      voxelSize[2],
-    ]
     // NG translation works on nm scale
     const scaleUmToNm = 1e3
     const modA = mat3.fromValues(
-      scaleUmToVoxelFixed[0], 0, 0,
-      0, scaleUmToVoxelFixed[1], 0,
-      0, 0, scaleUmToVoxelFixed[2]
+      scaleUmToNm, 0, 0,
+      0, scaleUmToNm, 0,
+      0, 0, scaleUmToNm
     )
     mat3.mul(modA, modA, [...A[0], ...A[1], ...A[2]])
-    const modb = vec3.scale(vec3.create(), b, scaleUmToNm)
+    const modb = vec3.mul(vec3.create(), b, [ scaleUmToNm, scaleUmToNm, scaleUmToNm])
+    vec3.scale(vec3.create(), b, scaleUmToNm)
     const transform = [
       [...modA.slice(0, 3), modb[0]] as TVec4,
       [...modA.slice(3, 6), modb[1]] as TVec4,
diff --git a/src/messaging/service.spec.ts b/src/messaging/service.spec.ts
index 8ba8cad4e3cdaf0d776fd5ff2f1e1cc0958c8b08..05da2a3999f7c350a304b3fca3e997029bec624e 100644
--- a/src/messaging/service.spec.ts
+++ b/src/messaging/service.spec.ts
@@ -1,5 +1,5 @@
 import { TestBed } from "@angular/core/testing"
-import { MockStore, provideMockStore } from "@ngrx/store/testing"
+import { provideMockStore } from "@ngrx/store/testing"
 import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service"
 import { AngularMaterialModule } from "src/sharedModules"
 import { getUuid } from "src/util/fn"
@@ -9,6 +9,7 @@ import { MANAGED_METHODS } from './service'
 import { of, Subject } from "rxjs"
 import { ConfirmDialogComponent } from "src/components/confirmDialog/confirmDialog.component"
 import { TYPE as NATIVE_TYPE } from './native'
+import { ApiService } from "src/api"
 
 describe('> service.ts', () => {
   describe('> MessagingService', () => {
@@ -34,6 +35,17 @@ describe('> service.ts', () => {
           {
             provide: WINDOW_MESSAGING_HANDLER_TOKEN,
             useValue: windowMessagehandler
+          },
+          {
+            provide: ApiService,
+            useValue: {
+              booth: {
+                handshake(){}
+              },
+              broadcastCh: {
+                addListener(){}
+              }
+            }
           }
         ]
       })
@@ -118,7 +130,7 @@ describe('> service.ts', () => {
         it('> pong', async () => {
           const result = await mService.handleMessage({
             data: {
-              method: `${IAV_POSTMESSAGE_NAMESPACE}ping`
+              method: `ping`
             },
             origin: 'foobar'
           })
diff --git a/src/messaging/service.ts b/src/messaging/service.ts
index 365a2d1880bd4513bb088b49ff0b761faa30b36c..80a97ca603746ffd3543761946d211ce11a48ff4 100644
--- a/src/messaging/service.ts
+++ b/src/messaging/service.ts
@@ -1,29 +1,44 @@
 import { Inject, Injectable, Optional } from "@angular/core";
 import { Observable } from "rxjs";
 import { MatDialog } from "@angular/material/dialog";
-import { MatSnackBar } from "@angular/material/snack-bar";
-
-import { getUuid } from "src/util/fn";
-import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service";
+import { getUuid, noop } from "src/util/fn";
 import { ConfirmDialogComponent } from "src/components/confirmDialog/confirmDialog.component";
 
 import { IMessagingActions, IMessagingActionTmpl, WINDOW_MESSAGING_HANDLER_TOKEN, IWindowMessaging } from './types'
 import { TYPE as NMV_TYPE, processJsonLd as nmvProcess } from './nmvSwc/index'
 import { TYPE as NATIVE_TYPE, processJsonLd as nativeProcess } from './native'
+import { BoothVisitor, JRPCRequest, ListenerChannel } from "src/api/jsonrpc"
+import { ApiService } from "src/api";
+import { ApiBoothEvents, namespace as apiNameSpace } from "src/api/service";
 
 export const IAV_POSTMESSAGE_NAMESPACE = `ebrains:iav:`
 
+const RECOGNISED_NAMESPACES = [
+  IAV_POSTMESSAGE_NAMESPACE,
+  apiNameSpace
+]
+
 export const MANAGED_METHODS = [
   'openminds:nmv:loadSwc',
   'openminds:nmv:unloadSwc'
 ]
 
+class WindowOpenerListener implements ListenerChannel {
+  constructor(
+    public registerLeaveCb: () => void,
+    public notify: (payload: JRPCRequest<unknown, unknown>) => void
+  ){}
+  
+}
+
 @Injectable({
   providedIn: 'root'
 })
 
 export class MessagingService {
 
+  private originListenerMap = new Map<string, {listener: WindowOpenerListener, visitor: BoothVisitor<ApiBoothEvents>}>()
+
   private whiteListedOrigins = new Set()
   private pendingRequests: Map<string, Promise<boolean>> = new Map()
   private windowName: string
@@ -32,8 +47,7 @@ export class MessagingService {
   
   constructor(
     private dialog: MatDialog,
-    private snackbar: MatSnackBar,
-    private worker: AtlasWorkerService,
+    private apiService: ApiService,
     @Optional() @Inject(WINDOW_MESSAGING_HANDLER_TOKEN) private messagingHandler: IWindowMessaging,
   ){
     
@@ -60,11 +74,36 @@ export class MessagingService {
 
     window.addEventListener('message', async ({ data, origin, source }) => {
       if (/^webpack/.test(data.type)) return
-      if (data === '') return
+      if (!data) return
+      const { method } = data
+      if (RECOGNISED_NAMESPACES.every(namespace => (method || '').indexOf(namespace) !== 0)) {
+        return
+      }
       const src = source as Window
       const { id } = data
       try {
         const result = await this.handleMessage({ data, origin })
+        if (!this.originListenerMap.has(origin)) {
+          const listener = new WindowOpenerListener(
+            noop,
+            val => src.postMessage(val, origin)
+          )
+          
+          const visitor = this.apiService.booth.handshake()
+          this.originListenerMap.set(origin, {listener, visitor})
+
+          this.apiService.broadcastCh.addListener(listener)
+          
+
+          /**
+           * if result was not yet populated, try populating it with 
+           * siibra-explorer api
+           */
+        }
+        if (!result) {
+          const { visitor } = this.originListenerMap.get(origin)
+          return await visitor.request(data)
+        }
         src.postMessage({
           id,
           jsonrpc: '2.0',
@@ -94,27 +133,25 @@ export class MessagingService {
 
   public async handleMessage({ data, origin }) {
     const { method, param } = data
-    
-    if (!method) return
-    if (method.indexOf(IAV_POSTMESSAGE_NAMESPACE) !== 0) return
-    const strippedMethod = method.replace(IAV_POSTMESSAGE_NAMESPACE, '')
-
     /**
      * if ping method, respond pong method
      */
-    if (strippedMethod === 'ping') {
+    if (method === 'ping') {
       return 'pong'
     }
 
     /**
      * otherwise, check permission
      */
-
     const allow = await this.checkOrigin({ origin })
     if (!allow) throw ({
       code: 403,
       message: 'User declined'
     })
+    
+    if (!method) return
+    if (method.indexOf(IAV_POSTMESSAGE_NAMESPACE) !== 0) return
+    const strippedMethod = method.replace(IAV_POSTMESSAGE_NAMESPACE, '')
 
     // TODO 
     // in future, check if in managed_methods
@@ -169,50 +206,11 @@ export class MessagingService {
     // TODO combine api service and messaging service into one
     // and implement it properly
 
-    // if (method === 'viewerHandle:add3DLandmarks') {
-    //   this.apiService.interactiveViewer.viewerHandle.add3DLandmarks(param)
-    //   return 'OK'
-    // }
-
-    // if (method === 'viewerHandle:remove3DLandmarks') {
-    //   this.apiService.interactiveViewer.viewerHandle.remove3DLandmarks(param)
-    //   return 'OK'
-    // }
-
-    /**
-     * TODO use loadResource in the future
-     */
-    if (method === '_tmp:plotly') {
-      const isLoadingSnack = this.snackbar.open(`Loading plotly mesh ...`)
-      const resp = await this.worker.sendMessage({
-        method: `PROCESS_PLOTLY`,
-        param
-      })
-      isLoadingSnack?.dismiss()
-      const meshId = 'bobby'
-      /**
-       * TODO re-enable plotly VTK mesh
-       */
-      
-      // if (false) {
-      //   const { objectUrl, customFragmentColor } = resp.result || {}
-      //   this.loadMesh({
-      //     type: 'VTK',
-      //     id: meshId,
-      //     url: objectUrl,
-      //     customFragmentColor
-      //   })
-      // } else {
-      //   this.snackbar.open(`Error: loadMesh method not injected.`)
-      // }
-      return 'OK'
-    }
-
     if (MANAGED_METHODS.indexOf(method) >= 0) {
       return await this.processJsonld(param)
     }
 
-    throw ({ code: 404, message: 'Method not found' })
+    return
   }
 
   async checkOrigin({ origin }): Promise<boolean> {
diff --git a/src/mouseoverModule/type.ts b/src/mouseoverModule/type.ts
deleted file mode 100644
index 38033f903e9047b4164c90137564a19d0f75ae86..0000000000000000000000000000000000000000
--- a/src/mouseoverModule/type.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import { TRegionSummary } from "src/util/siibraApiConstants/types";
-
-export type TMouseOverVtkLandmark = {
-  landmarkName: string
-}
\ No newline at end of file
diff --git a/src/overwrite.scss b/src/overwrite.scss
index 9fd0bb0b88e2fc3039a03040dfa659a2ec341cf8..5b17076e66aca6d569e24b3e6f5d0b6ab4c65f32 100644
--- a/src/overwrite.scss
+++ b/src/overwrite.scss
@@ -289,3 +289,38 @@ a[mat-raised-button]
 {
   pointer-events: none!important;
 }
+
+// list like button
+$llb: "#{$nsp}-list-like-button";
+button.#{$llb}
+{
+  width: 100%;
+
+  > .mat-button-wrapper
+  {
+    display: flex;
+    
+    > .#{$llb}-icon
+    {
+      width: 2rem;
+      flex: 0 0 auto;
+      margin: auto;
+      font-size: 150%;
+    }
+
+    > .#{$llb}-body
+    {
+      flex: 1 1 0px;
+      display: flex;
+      flex-direction: column;
+      margin:1rem;
+  
+      .#{$llb}-body-line
+      {
+        flex: 0 0 0px;
+        text-align: start;
+        line-height: 1.5rem;
+      }
+    }
+  }
+}
diff --git a/src/plugin/plugin.module.ts b/src/plugin/plugin.module.ts
index 998ca4056ecbb7a069133b021bc3e0aab8605fdc..563a7d9dbb010c2e5654ff7631e1168098c52919 100644
--- a/src/plugin/plugin.module.ts
+++ b/src/plugin/plugin.module.ts
@@ -1,10 +1,9 @@
-import { CommonModule, DOCUMENT } from "@angular/common";
+import { CommonModule } from "@angular/common";
 import { HttpClientModule } from "@angular/common/http";
 import { NgModule } from "@angular/core";
 import { LoggingModule } from "src/logging";
 import { AngularMaterialModule } from "src/sharedModules";
 import { UtilModule } from "src/util";
-import { appendScriptFactory, APPEND_SCRIPT_TOKEN, removeScriptFactory, REMOVE_SCRIPT_TOKEN } from "src/util/constants";
 import { IFrameSrcPipe } from "./iframeSrc.pipe";
 import { PluginBannerUI } from "./pluginBanner/pluginBanner.component";
 import { PluginPortal } from "./pluginPortal/pluginPortal.component";
diff --git a/src/routerModule/routeStateTransform.service.spec.ts b/src/routerModule/routeStateTransform.service.spec.ts
index a341cdd6d405e275beedfd82a29c8f42ef3f36a5..2dee59c937f86923ca06f41c073aca2a22dcc0ba 100644
--- a/src/routerModule/routeStateTransform.service.spec.ts
+++ b/src/routerModule/routeStateTransform.service.spec.ts
@@ -6,6 +6,7 @@ import { DefaultUrlSerializer } from "@angular/router"
 import * as nehubaConfigService from "src/viewerModule/nehuba/config.service"
 import { atlasSelection, userInteraction } from "src/state"
 import { encodeNumber } from "./cipher"
+import { QuickHash } from "src/util/fn"
 
 const serializer = new DefaultUrlSerializer()
 
@@ -123,7 +124,7 @@ describe("> routeStateTransform.service.ts", () => {
         const altasObj = {"@id": 'foo-bar-a'}
         const templObj = {"@id": 'foo-bar-t'}
         const parcObj = {"@id": 'foo-bar-p'}
-        const regions = [{}]
+        const regions = [{'name': 'selected-region-1'}]
         const standAloneVolumes = []
         const navigation = null
 
@@ -181,9 +182,12 @@ describe("> routeStateTransform.service.ts", () => {
           
           const getRegionLabelIndicesSpy = sapi.getRegionLabelIndices as jasmine.Spy
           getRegionLabelIndicesSpy.and.resolveTo(labelIndex)
-
           const s = await svc.cvtStateToRoute(state as any)
-          expect(s).toContain(`r:${ngId}::${encodeNumber(labelIndex, { float: false })}`)
+          
+          expect(getParcNgId).not.toHaveBeenCalled()
+          expect(getRegionLabelIndicesSpy).not.toHaveBeenCalled()
+
+          expect(s).toContain(`rn:${QuickHash.GetHash(regions[0].name)}`)
         })
   
         it('> ngId containing expected value', async () => {
diff --git a/src/routerModule/routeStateTransform.service.ts b/src/routerModule/routeStateTransform.service.ts
index 1032448ec67ea1c9bf80d3f18f82f901bf7c6057..b01ac54beb7085b01ea548ac2de69b4d0a103fff 100644
--- a/src/routerModule/routeStateTransform.service.ts
+++ b/src/routerModule/routeStateTransform.service.ts
@@ -9,6 +9,7 @@ import { getParcNgId } from "src/viewerModule/nehuba/config.service";
 import { decodeToNumber, encodeNumber, encodeURIFull, separator } from "./cipher";
 import { TUrlAtlas, TUrlPathObj, TUrlStandaloneVolume } from "./type";
 import { decodePath, encodeId, decodeId, encodePath } from "./util";
+import { QuickHash } from "src/util/fn";
 
 @Injectable()
 export class RouteStateTransformSvc {
@@ -26,6 +27,7 @@ export class RouteStateTransformSvc {
     const selectedTemplateId = decodeId( RouteStateTransformSvc.GetOneAndOnlyOne(obj.t) )
     const selectedParcellationId = decodeId( RouteStateTransformSvc.GetOneAndOnlyOne(obj.p) )
     const selectedRegionIds = obj.r
+    const selectedRegionNames = obj.rn
     
     if (!selectedAtlasId || !selectedTemplateId || !selectedParcellationId) {
       return {}
@@ -60,7 +62,11 @@ export class RouteStateTransformSvc {
     const userViewer = await this.sapi.useViewer(selectedTemplate).toPromise()
     
     const selectedRegions = await (async () => {
-      if (!selectedRegionIds) return []
+      if (!selectedRegionIds && !selectedRegionNames) return []
+
+      if (selectedRegionNames && selectedRegionNames.length > 0) {
+        return allParcellationRegions.filter(region => selectedRegionNames.includes(QuickHash.GetHash(region.name)))
+      }
 
       /**
        * should account for 
@@ -268,16 +274,7 @@ export class RouteStateTransformSvc {
       }
     }
   
-    // encoding selected regions
-    let selectedRegionsString: string
-    if (selectedRegions.length === 1) {
-      const region = selectedRegions[0]
-      const labelIndex = await this.sapi.getRegionLabelIndices(selectedTemplate, selectedParcellation, region)
-      
-      const ngId = getParcNgId(selectedAtlas, selectedTemplate, selectedParcellation, region)
-      selectedRegionsString = `${ngId}::${encodeNumber(labelIndex, { float: false })}`
-    }
-    let routes: TUrlPathObj<string, TUrlAtlas<string>> | TUrlPathObj<string, TUrlStandaloneVolume<string>>
+    let routes: TUrlPathObj<string|string[], TUrlAtlas<string|string[]>> | TUrlPathObj<string, TUrlStandaloneVolume<string>>
     
     routes = {
       // for atlas
@@ -287,7 +284,8 @@ export class RouteStateTransformSvc {
       // for parcellation
       p: selectedParcellation && encodeId(selectedParcellation.id),
       // for regions
-      r: selectedRegionsString && encodeURIFull(selectedRegionsString),
+      // r: selectedRegionsString && encodeURIFull(selectedRegionsString),
+      rn: selectedRegions[0] && selectedRegions.map(r => QuickHash.GetHash(r.name)),
       // nav
       ['@']: cNavString,
       // showing dataset
diff --git a/src/routerModule/type.ts b/src/routerModule/type.ts
index 13205ad3e09e0969a8c419a5d9803b2883bc2de3..284b5e7a9441fd52be9281ad3dec5c749e390993 100644
--- a/src/routerModule/type.ts
+++ b/src/routerModule/type.ts
@@ -7,6 +7,7 @@ export type TUrlAtlas<T> = {
   t: T   // template
   p: T   // parcellation
   r?: T  // region selected
+  rn?: T
 }
 
 export type TUrlPlugin<T> = {
diff --git a/src/screenshot/screenshotCmp/screenshot.component.ts b/src/screenshot/screenshotCmp/screenshot.component.ts
index 9af9d6b90a7d60af3438b2784906918b6f74aab0..2ccb1da45087f34c917a7626827ae14759cd48db 100644
--- a/src/screenshot/screenshotCmp/screenshot.component.ts
+++ b/src/screenshot/screenshotCmp/screenshot.component.ts
@@ -2,7 +2,7 @@ import { Component, HostListener, Inject, OnDestroy, Output, EventEmitter, Optio
 import { MatDialog } from "@angular/material/dialog";
 import { MatSnackBar } from "@angular/material/snack-bar";
 import { combineLatest, Observable, Subject, Subscription } from "rxjs";
-import { distinctUntilChanged, map, mapTo, shareReplay, startWith, switchMap, switchMapTo, tap } from "rxjs/operators";
+import { distinctUntilChanged, map, mapTo, shareReplay, startWith, switchMap, switchMapTo } from "rxjs/operators";
 import { HANDLE_SCREENSHOT_PROMISE, TypeHandleScrnShotPromise } from "../util";
 import { getDateString } from 'common/util'
 import { getUuid } from "src/util/fn";
diff --git a/src/services/uiService.service.ts b/src/services/uiService.service.ts
index cc8774728fb1742e36722135960341c35f7c0599..2e6a580418690c4d3e1182aa352132d853189410 100644
--- a/src/services/uiService.service.ts
+++ b/src/services/uiService.service.ts
@@ -1,6 +1,6 @@
 import { Injectable } from "@angular/core";
 import {MatSnackBar, MatSnackBarConfig} from "@angular/material/snack-bar";
-import { MatDialog } from "@angular/material/dialog";
+import { MatDialog, MatDialogConfig } from "@angular/material/dialog";
 import { ActionDialog } from "src/ui/actionDialog/actionDialog.component";
 
 @Injectable({
@@ -18,7 +18,7 @@ export class UIService {
     return this.snackbar.open(message, actionBtnTxt, config)
   }
 
-  public showDialog(data, options){
+  public showDialog(data: unknown, options: MatDialogConfig){
     return this.dialog.open(ActionDialog, {
       ...options,
       data
diff --git a/src/share/saneUrl/saneUrl.component.ts b/src/share/saneUrl/saneUrl.component.ts
index f7fbed0ea2bb72dcb5500bcf3d2cec5623b51872..0ce99cce3b2ac0d5167380d9f4182455f2de36e5 100644
--- a/src/share/saneUrl/saneUrl.component.ts
+++ b/src/share/saneUrl/saneUrl.component.ts
@@ -1,6 +1,6 @@
 import { Component, OnDestroy, Input } from "@angular/core";
 import { Observable, merge, of, Subscription, BehaviorSubject, combineLatest } from "rxjs";
-import { startWith, mapTo, map, debounceTime, switchMap, catchError, shareReplay, filter, tap, takeUntil, distinctUntilChanged } from "rxjs/operators";
+import { startWith, mapTo, map, debounceTime, switchMap, catchError, shareReplay, filter, tap, distinctUntilChanged } from "rxjs/operators";
 import { UntypedFormControl } from "@angular/forms";
 import { ErrorStateMatcher } from "@angular/material/core";
 import { Clipboard } from "@angular/cdk/clipboard";
@@ -88,7 +88,7 @@ export class SaneUrl implements OnDestroy{
         ? of(false)
         : this.svc.getKeyVal(val).pipe(
           mapTo(false),
-          catchError((err, obs) => {
+          catchError((err) => {
             if (err instanceof NotFoundError) return of(true)
             return of(false)
           })
@@ -164,9 +164,7 @@ export class SaneUrl implements OnDestroy{
       this.customUrl.value,
       this.stateTobeSaved
     ).subscribe(
-      resp => {
-        this.savingProgress$.next(ESavingProgress.DONE)
-      },
+      () => this.savingProgress$.next(ESavingProgress.DONE),
       err => {
         this.customUrl.enable()
 
diff --git a/src/share/saneUrl/saneUrl.service.ts b/src/share/saneUrl/saneUrl.service.ts
index d78a8ac903089062ba3cc86ca009e627947f70c6..d838c72b83c63d084102e9327b93462821caa703 100644
--- a/src/share/saneUrl/saneUrl.service.ts
+++ b/src/share/saneUrl/saneUrl.service.ts
@@ -25,7 +25,7 @@ export class SaneUrlSvc implements IKeyValStore{
       `${this.saneUrlRoot}${key}`,
       { responseType: 'json' }
     ).pipe(
-      catchError((err, obs) => {
+      catchError((err) => {
         const { status } = err
         if (status === 404) {
           return throwError(new NotFoundError('Not found'))
diff --git a/src/state/atlasSelection/util.ts b/src/state/atlasSelection/util.ts
index a76fd1f9f2e8fc45335a5b3695c65bb68322dfef..525df7f1fdc00f7528bdfbbc210a547db3b738ac 100644
--- a/src/state/atlasSelection/util.ts
+++ b/src/state/atlasSelection/util.ts
@@ -1,8 +1,6 @@
 import { createSelector, select } from "@ngrx/store";
-import { forkJoin, of, pipe } from "rxjs";
-import { distinctUntilChanged, map, switchMap } from "rxjs/operators";
-import { SAPI } from "src/atlasComponents/sapi";
-import { translateV3Entities } from "src/atlasComponents/sapi/translateV3"
+import { pipe } from "rxjs";
+import { distinctUntilChanged, map } from "rxjs/operators";
 import { SxplrAtlas, SxplrParcellation, SxplrTemplate } from "src/atlasComponents/sapi/sxplrTypes";
 import { jsonEqual } from "src/util/json";
 import * as selectors from "./selectors"
diff --git a/src/state/index.ts b/src/state/index.ts
index 40e84c234c4bca8d7eb7fece2c2c999ab249629f..aa9964ec9c0a69f063874b6a8b65382a72e2025c 100644
--- a/src/state/index.ts
+++ b/src/state/index.ts
@@ -22,6 +22,7 @@ export {
 
 export * as generalActions from "./actions"
 
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
 function debug(reducer: ActionReducer<MainState>): ActionReducer<MainState> {
   return function(state, action) {
     console.log('state', state);
@@ -71,6 +72,7 @@ export function getStoreEffects() {
     plugins.Effects,
     atlasSelection.Effect,
     userInterface.Effects,
+    userInteraction.Effect,
   ]
 }
 
diff --git a/src/state/userInteraction/effects.ts b/src/state/userInteraction/effects.ts
index 5d4986ad5965117705e6d1aa869407fabdbd8a65..7fd81d8ae1252522deba3c4f589eda32d61513e7 100644
--- a/src/state/userInteraction/effects.ts
+++ b/src/state/userInteraction/effects.ts
@@ -1,17 +1,33 @@
 import { Injectable } from "@angular/core";
 import { Actions, createEffect, ofType } from "@ngrx/effects";
-import * as atlasSelectionActions from "../atlasSelection/actions"
 import * as userInterface from "../userInterface"
-import { mapTo } from "rxjs/operators";
+import * as atlasSelection from "../atlasSelection"
+import * as actions from "./actions"
+import { filter, map, mapTo, skip } from "rxjs/operators";
+import { Store } from "@ngrx/store";
 
 @Injectable()
 export class Effect {
   onStandAloneVolumesExistCloseMatDrawer = createEffect(() => this.action.pipe(
-    ofType(atlasSelectionActions.clearStandAloneVolumes),
+    ofType(atlasSelection.actions.clearStandAloneVolumes),
     mapTo(userInterface.actions.closeSidePanel())
   ))
 
-  constructor(private action: Actions){
+  #distinctATP$ = this.store.pipe(
+    atlasSelection.fromRootStore.distinctATP(),
+  )
+
+  onATPUpdateUnselectFeature = createEffect(() => this.#distinctATP$.pipe(
+    filter(v => !!v.atlas && !!v.parcellation && !!v.template),
+    /**
+     * First non empty emit would be from selecting atlas.
+     * So ignore it.
+     */
+    skip(1),
+    map(() => actions.clearShownFeature())
+  ))
+
+  constructor(private action: Actions, private store: Store){
 
   }
-}
\ No newline at end of file
+}
diff --git a/src/ui/actionDialog/actionDialog.component.ts b/src/ui/actionDialog/actionDialog.component.ts
index 0275f8cf576cf236cfe6878446347144412711c4..ced5d32d45c3de46755b1ff7af1f14b8331965b7 100644
--- a/src/ui/actionDialog/actionDialog.component.ts
+++ b/src/ui/actionDialog/actionDialog.component.ts
@@ -22,7 +22,7 @@ export class ActionDialog{
   constructor(
     @Optional() @Inject(MAT_DIALOG_DATA) data: any
   ){
-    const { config, content, template, actions = [] } = data || {}
+    const { config, content, actions = [] } = data || {}
     const { sameLine = false } = config || {}
 
     this.content = content
@@ -30,7 +30,4 @@ export class ActionDialog{
     this.actions = actions
   }
 
-  actionHandler(event: MouseEvent, action: IDialogAction){
-    // TODO fill in the actionHandler
-  }
-}
\ No newline at end of file
+}
diff --git a/src/ui/actionDialog/actionDialog.template.html b/src/ui/actionDialog/actionDialog.template.html
index 095b27ee6c147c69f53ac164bb7c3f865fd2a021..5388f69f0d10a0d887a2dce39164a273f1e200d5 100644
--- a/src/ui/actionDialog/actionDialog.template.html
+++ b/src/ui/actionDialog/actionDialog.template.html
@@ -28,8 +28,7 @@
     <button *ngSwitchCase="'mat-flat-button'"
       mat-flat-button
       [color]="action.color || 'default'"
-      [mat-dialog-close]="action.dismiss && action"
-      (click)="actionHandler($event, action)">
+      [mat-dialog-close]="action.dismiss && action">
       <ng-container *ngTemplateOutlet="textNodeTmpl; context: action"></ng-container>        
     </button>
 
@@ -37,8 +36,7 @@
     <button *ngSwitchCase="'mat-raised-button'"
       mat-raised-button
       [color]="action.color || 'default'"
-      [mat-dialog-close]="action.dismiss && action"
-      (click)="actionHandler($event, action)">
+      [mat-dialog-close]="action.dismiss && action">
       <ng-container *ngTemplateOutlet="textNodeTmpl; context: action"></ng-container>        
     </button>
 
@@ -46,8 +44,7 @@
     <button *ngSwitchCase="'mat-stroked-button'"
       mat-stroked-button
       [color]="action.color || 'default'"
-      [mat-dialog-close]="action.dismiss && action"
-      (click)="actionHandler($event, action)">
+      [mat-dialog-close]="action.dismiss && action">
       <ng-container *ngTemplateOutlet="textNodeTmpl; context: action"></ng-container>        
     </button>
 
@@ -55,8 +52,7 @@
     <button *ngSwitchDefault
       mat-button
       [color]="action.color || 'default'"
-      [mat-dialog-close]="action.dismiss && action"
-      (click)="actionHandler($event, action)">
+      [mat-dialog-close]="action.dismiss && action">
       <ng-container *ngTemplateOutlet="textNodeTmpl; context: action"></ng-container>    
     </button>
   </ng-container>
diff --git a/src/ui/config/module.ts b/src/ui/config/module.ts
index 55fcded2c2c01aa17ce1bdb3420dc2e6c9634e5e..751aa6f16aca850c8dc9fd3e951d5e1484ba87bb 100644
--- a/src/ui/config/module.ts
+++ b/src/ui/config/module.ts
@@ -1,7 +1,6 @@
 import { CommonModule } from "@angular/common";
 import { NgModule } from "@angular/core";
 import { LayoutModule } from "src/layouts/layout.module";
-import { PluginModule } from "src/plugin";
 import { AngularMaterialModule } from "src/sharedModules";
 import { ConfigComponent } from "./configCmp/config.component";
 
@@ -9,7 +8,6 @@ import { ConfigComponent } from "./configCmp/config.component";
   imports: [
     CommonModule,
     AngularMaterialModule,
-    // PluginModule,
     LayoutModule,
   ],
   declarations: [
diff --git a/src/ui/dialogInfo/const.ts b/src/ui/dialogInfo/const.ts
deleted file mode 100644
index 09aa80f6f979e24b8cd407ec54fcbdfa4c8d3154..0000000000000000000000000000000000000000
--- a/src/ui/dialogInfo/const.ts
+++ /dev/null
@@ -1 +0,0 @@
-import { InjectionToken } from "@angular/core";
diff --git a/src/ui/help/helpOnePager/helpOnePager.component.ts b/src/ui/help/helpOnePager/helpOnePager.component.ts
index 570072137ea77c542c3efb56a60a717063cbef64..d1298937d73f2a5483265cc92d043eafb96cc090 100644
--- a/src/ui/help/helpOnePager/helpOnePager.component.ts
+++ b/src/ui/help/helpOnePager/helpOnePager.component.ts
@@ -1,4 +1,3 @@
-import { MatDialog } from '@angular/material/dialog';
 import { Component } from "@angular/core";
 import { ARIA_LABELS } from 'common/constants'
 
diff --git a/src/ui/nehubaContainer/mobileOverlay/mobileOverlay.component.ts b/src/ui/nehubaContainer/mobileOverlay/mobileOverlay.component.ts
deleted file mode 100644
index 4948b3a38be2f43149491447ec4c79a33237ae36..0000000000000000000000000000000000000000
--- a/src/ui/nehubaContainer/mobileOverlay/mobileOverlay.component.ts
+++ /dev/null
@@ -1,332 +0,0 @@
-import { Component, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild, Inject, TemplateRef, ViewChildren, QueryList } from "@angular/core";
-import { combineLatest, concat, fromEvent, merge, Observable, of, Subject, BehaviorSubject } from "rxjs";
-import { filter, map, scan, switchMap, takeUntil, startWith, pairwise, tap, shareReplay, distinctUntilChanged, switchMapTo, reduce } from "rxjs/operators";
-import { clamp } from "src/util/generator";
-import { DOCUMENT } from "@angular/common";
-
-const TOUCHMOVE_THRESHOLD = 50
-const SUBMENU_IXOBS_CONFIG = {
-
-}
-
-export interface ITunableProp{
-  name: string
-  displayName?: string
-  values: string[]
-  selected?: any
-}
-
-@Component({
-  selector : 'mobile-overlay',
-  templateUrl : './mobileOverlay.template.html',
-  styleUrls : [
-    './mobileOverlay.style.css',
-  ],
-  styles : [
-    `
-div.active > span:before
-{
-  content: '\u2022';
-  width: 1em;
-  display: inline-block;
-  background:none;
-}
-div:not(.active) > span:before
-{
-  content : ' ';
-  width : 1em;
-  display: inline-block;
-}
-    `,
-  ],
-})
-
-export class MobileOverlay implements OnInit, OnDestroy {
-
-  @Input('iav-mobile-overlay-guide-tmpl')
-  public guideTmpl: TemplateRef<any>
-
-  @Input('iav-mobile-overlay-hide-ctrl-btn')
-  public hideCtrlBtn: boolean = false
-
-  @Input('iav-mobile-overlay-ctrl-btn-pos')
-  public ctrlBtnPosition: { left: string, top: string } = { left: '50%', top: '50%' }
-  
-  @Input() public tunableProperties: ITunableProp[] = []
-  
-  @Output() public tunablePropertySelected: EventEmitter<ITunableProp> = new EventEmitter()
-  @Output() public deltaValue: EventEmitter<{delta: number, selectedProp: ITunableProp}> = new EventEmitter()
-  @Output() public valueSelected: EventEmitter<{ value: string, selectedProp: ITunableProp }> = new EventEmitter()
-
-  @ViewChild('initiator', {read: ElementRef, static: true}) public initiator: ElementRef
-  @ViewChild('mobileMenuContainer', {read: ElementRef, static: true}) public menuContainer: ElementRef
-  @ViewChild('intersector', {read: ElementRef, static: true}) public intersector: ElementRef
-  @ViewChild('subMenuObserver', {read: ElementRef, static: false}) public subMenuIx: ElementRef
-  @ViewChild('setValueContainer', { read: ElementRef, static: false }) public setValueContainer?: ElementRef
-
-  private _onDestroySubject: Subject<boolean> = new Subject()
-
-  private _focusedProperties: ITunableProp
-  get focusedProperty() {
-    return this._focusedProperties
-      ? this._focusedProperties
-      : this.tunableProperties[0]
-  }
-  get focusedIndex() {
-    return this._focusedProperties
-      ? this.tunableProperties.findIndex(p => p === this._focusedProperties)
-      : 0
-  }
-
-  private initiatorSingleTouchStart$: Observable<any>
-
-  public showScreen$: Observable<boolean>
-  public showProperties$: Observable<boolean>
-  public showDelta$: Observable<boolean>
-  public showInitiator$: Observable<boolean>
-  private _drag$: Observable<any>
-  private _thresholdDrag$: Observable<any>
-  private intersectionObserver: IntersectionObserver
-  private subMenuIxObs: IntersectionObserver
-
-  constructor(
-    @Inject(DOCUMENT) private document: Document
-  ){
-
-  }
-
-  public ngOnDestroy() {
-    this._onDestroySubject.next(true)
-    this._onDestroySubject.complete()
-    this.intersectionObserver && this.intersectionObserver.disconnect()
-    this.subMenuIxObs && this.subMenuIxObs.disconnect()
-  }
-
-  public ngOnInit() {
-
-    this.initiatorSingleTouchStart$ = fromEvent(this.initiator.nativeElement, 'touchstart').pipe(
-      filter((ev: TouchEvent) => ev.touches.length === 1),
-      shareReplay(1)
-    )
-
-    const itemCount = this.tunableProperties.length
-
-    const config = {
-      root: this.intersector.nativeElement,
-      threshold: [...[...Array(itemCount)].map((_, k) => k / itemCount), 1],
-    }
-
-    this.intersectionObserver = new IntersectionObserver((arg) => {
-      if (arg[0].isIntersecting) {
-        const ratio = arg[0].intersectionRatio - (1 / this.tunableProperties.length / 2)
-        this.focusItemIndex = this.tunableProperties.length - Math.round(ratio * this.tunableProperties.length) - 1
-      }
-    }, config)
-
-    this.intersectionObserver.observe(this.menuContainer.nativeElement)
-
-    this._drag$ = fromEvent(this.document, 'touchmove').pipe(
-      filter((ev: TouchEvent) => ev.touches.length === 1),
-      takeUntil(fromEvent(this.document, 'touchend').pipe(
-        filter((ev: TouchEvent) => ev.touches.length === 0),
-      )),
-    )
-
-    this._thresholdDrag$ = this._drag$.pipe(
-      distinctUntilChanged((o, n) => {
-        const deltaX = o.touches[0].screenX - n.touches[0].screenX
-        const deltaY = o.touches[0].screenY - n.touches[0].screenY
-        return (deltaX ** 2 + deltaY ** 2) < TOUCHMOVE_THRESHOLD
-      }),
-    )
-
-    this.showProperties$ = this.initiatorSingleTouchStart$.pipe(
-      switchMap(() => concat(
-        this._thresholdDrag$.pipe(
-          pairwise(),
-          map(double => ({
-            deltaX : double[1].touches[0].screenX - double[0].touches[0].screenX,
-            deltaY : double[1].touches[0].screenY - double[0].touches[0].screenY,
-          })),
-          map(v => v.deltaY ** 2 > v.deltaX ** 2),
-          scan((acc, _curr) => acc),
-        ),
-        of(false)
-      )),
-      startWith(false),
-      distinctUntilChanged(),
-      shareReplay(1)
-    )
-
-    this.showDelta$ = this.initiatorSingleTouchStart$.pipe(
-      switchMap(() => concat(
-        this._thresholdDrag$.pipe(
-          pairwise(),
-          map(double => ({
-            deltaX : double[1].touches[0].screenX - double[0].touches[0].screenX,
-            deltaY : double[1].touches[0].screenY - double[0].touches[0].screenY,
-          })),
-          scan((acc, _curr) => acc),
-          map(v => v.deltaX ** 2 > v.deltaY ** 2),
-        ),
-        of(false),
-      )),
-      startWith(false),
-      distinctUntilChanged(),
-      shareReplay(1)
-    )
-
-    this.showInitiator$ = combineLatest(
-      this.showProperties$,
-      this.showDelta$,
-    ).pipe(
-      map(([flag1, flag2]) => !flag1 && !flag2),
-    )
-
-    this.showScreen$ = combineLatest(
-      merge(
-        fromEvent(this.initiator.nativeElement, 'touchstart'),
-        fromEvent(this.initiator.nativeElement, 'touchend'),
-      ),
-      this.showInitiator$,
-    ).pipe(
-      map(([ev, showInitiator]: [TouchEvent, boolean]) => showInitiator && ev.touches.length === 1),
-    )
-
-    this.showDelta$.pipe(
-      filter(flag => flag),
-      switchMapTo(this._thresholdDrag$.pipe(
-        pairwise(),
-        map(double => double[1].touches[0].screenX - double[0].touches[0].screenX)
-      )),
-      takeUntil(this._onDestroySubject)
-    ).subscribe(ev => {
-      this.deltaValue.emit({
-        delta: ev,
-        selectedProp: this.focusedProperty
-      })
-    })
-
-    const offsetObs$ = this.initiatorSingleTouchStart$.pipe(
-      switchMap(() => this._drag$)
-    )
-
-    combineLatest(
-      this.showProperties$,
-      offsetObs$,
-    ).pipe(
-      filter(v => v[0]),
-      map(v => v[1]),
-      scan((acc, curr) => {
-        const { startY } = acc
-        const { screenY } = curr.touches[0]
-        return {
-          startY: startY || screenY,
-          totalDeltaY: screenY - (startY || 0)
-        }
-      }, {
-        startY: null,
-        totalDeltaY: 0
-      }),
-      takeUntil(this._onDestroySubject),
-    ).subscribe(v => {
-      const deltaY = v.totalDeltaY
-      const cellHeight = this.menuContainer && this.tunableProperties && this.tunableProperties.length > 0 && this.menuContainer.nativeElement.offsetHeight / this.tunableProperties.length
-      const adjHeight = - this.focusedIndex * cellHeight - cellHeight * 0.5
-
-      const min = - cellHeight * 0.5
-      const max = - this.tunableProperties.length * cellHeight + cellHeight * 0.5
-      const finalYTranslate = clamp(adjHeight + deltaY, min, max )
-      this.menuTransform = `translate(0px, ${finalYTranslate}px)`
-    })
-
-    this.showProperties$.pipe(
-      takeUntil(this._onDestroySubject),
-      filter(v => !v),
-    ).subscribe(() => {
-      if (this.focusItemIndex >= 0) {
-        this._focusedProperties = this.tunableProperties[this.focusItemIndex]
-      }
-    })
-
-    this.showDelta$.pipe(
-      tap(flag => {
-        this.highlightedSubmenu$.next(this.focusedProperty.values[0])
-        if (!flag && !!this.subMenuIxObs) {
-          this.subMenuIxObs.disconnect()
-          this.subMenuIxObs = null
-        }
-      }),
-      filter(v => !!v),
-      // when options show again, options may have changed, so need to recalculate
-      tap(() => {
-        this.setValueContainerClampStart = null
-        this.setValueContainerWidth = null
-        this.setValueContainerClampEnd = null
-        this.setValueContainerOffset = null
-      }), 
-      switchMapTo(this._drag$.pipe(
-        scan((acc, curr) => {
-          const { startX } = acc
-          const { screenX } = curr.touches[0]
-          return {
-            startX: startX || screenX,
-            totalDeltaX: screenX - (startX  || 0)
-          }
-        }, {
-          startX: null,
-          totalDeltaX: 0
-        })
-      )),
-      takeUntil(this._onDestroySubject)
-    ).subscribe(({ totalDeltaX }) => {
-      if (!this.subMenuIxObs && this.subMenuIx) {
-        this.subMenuIxObs = new IntersectionObserver(ixs => {
-          const ix = ixs.find(({ intersectionRatio }) => intersectionRatio < 0.7)
-          if (!ix) return console.log(ixs)
-          const value = ix.target.getAttribute('data-submenu-value')
-          this.highlightedSubmenu$.next(value)
-        }, {
-          root: this.subMenuIx.nativeElement,
-          threshold: [ 0.1, 0.3, 0.5, 0.7, 0.9 ]
-        })
-
-        for (const btn of this.setValueContainer.nativeElement.children) {
-          this.subMenuIxObs.observe(btn)
-        }
-      }
-      if (!this.setValueContainerWidth) {
-        if (!this.setValueContainer) return
-        if (this.setValueContainer.nativeElement.children.length === 0) return
-        const { children, clientWidth } = this.setValueContainer.nativeElement
-
-        this.setValueContainerWidth = clientWidth
-        const firstChildWidth = children[0].clientWidth
-        const lastChildWidth = children[children.length - 1].clientWidth
-
-        this.setValueContainerOffset = firstChildWidth / -2
-        this.setValueContainerClampStart = firstChildWidth / -2
-        this.setValueContainerClampEnd = lastChildWidth / 2 - clientWidth
-      }
-      const actualDeltaX = clamp(totalDeltaX + this.setValueContainerOffset, this.setValueContainerClampStart, this.setValueContainerClampEnd)
-      this.subMenuTransform = `translate(${actualDeltaX}px , 0px)`
-    })
-
-    this.showDelta$.pipe(
-      takeUntil(this._onDestroySubject),
-      filter(v => !v)
-    ).subscribe(() => this.valueSelected.emit({ selectedProp: this.focusedProperty, value: this.highlightedSubmenu$.value }))
-  }
-
-  public highlightedSubmenu$: BehaviorSubject<string> = new BehaviorSubject(null)
-
-  private setValueContainerOffset = null
-  private setValueContainerClampEnd = null
-  private setValueContainerClampStart = null
-  private setValueContainerWidth = null
-  public subMenuTransform = `translate(0px, 0px)`
-  public menuTransform = `translate(0px, 0px)`
-
-  public focusItemIndex: number = 0
-
-}
diff --git a/src/ui/nehubaContainer/mobileOverlay/mobileOverlay.style.css b/src/ui/nehubaContainer/mobileOverlay/mobileOverlay.style.css
deleted file mode 100644
index 8d548523eaefd725bb5baa490dfc38e3e7abdc2e..0000000000000000000000000000000000000000
--- a/src/ui/nehubaContainer/mobileOverlay/mobileOverlay.style.css
+++ /dev/null
@@ -1,110 +0,0 @@
-:host
-{
-  width: 100%;
-  height: 100%;
-  top: 0;
-  left: 0;
-  position: absolute;
-  z-index: 9;
-
-  pointer-events: none;
-}
-
-:host [screen]
-{
-  width: 100%;
-  height: 100%;
-  top: 0;
-  left: 0;
-  position: absolute;
-  z-index: 99999;
-
-  color : black;
-  background-color: rgba(255, 255, 255, 0.5);
-
-  transition: all 200ms linear;
-}
-
-:host-context([darktheme="true"]) [screen]
-{
-  color : white;
-  background-color: rgba(0, 0, 0, 0.5);
-}
-
-[intersector]
-{
-  position: absolute;
-  top: 50%;
-  left: 0;
-  width: 100%;
-  height: 50%;
-
-  display: flex;
-  flex-direction: column;
-  justify-content: flex-start;
-  align-items: center;
-}
-
-[mobileMenuContainer]
-{
-  z-index: 1000;
-}
-
-.scrollFocus:after
-{
-  content: ' ';
-  position:absolute;
-  width:100%;
-  height:100%;
-  top: 0;
-  left: 0;
-  
-  background-color: rgba(0, 0, 128, 0.2);
-}
-
-:host-context([darktheme="true"]) .scrollFocus:after
-{
-  background-color: rgba(128, 128, 200, 0.2);
-}
-
-.base-container
-{
-  position: relative;
-  width: 100%;
-  left: 0;
-  top: 50%;
-  height: 0;
-  z-index: 9999;
-}
-
-div[delta]
-{
-  white-space: nowrap
-}
-
-.popup
-{
-  transition: all 120ms linear;
-  transform-origin: 50% 100%;
-}
-
-.scale-y-0
-{
-  transform: scale(0.5, 0);
-  opacity: 0;
-}
-
-.subMenu
-{
-  bottom: 15px;
-}
-
-.w-50
-{
-  width: 50%;
-}
-
-.sliver
-{
-  width: 1px;
-}
\ No newline at end of file
diff --git a/src/ui/nehubaContainer/mobileOverlay/mobileOverlay.template.html b/src/ui/nehubaContainer/mobileOverlay/mobileOverlay.template.html
deleted file mode 100644
index 791edddf1b9dda7ae39043f7746afa8703f87bef..0000000000000000000000000000000000000000
--- a/src/ui/nehubaContainer/mobileOverlay/mobileOverlay.template.html
+++ /dev/null
@@ -1,82 +0,0 @@
-<div screen [hidden]="!(showProperties$ | async)">
-  <div intersector #intersector>
-    <div [style.transform] = "menuTransform" class = "btn-group-vertical" role = "group" mobileMenuContainer #mobileMenuContainer>
-      <div 
-        *ngFor = "let p of tunableProperties; let i = index"
-        [ngClass] = "{'active' : p === focusedProperty, 'scrollFocus': i === focusItemIndex}" 
-        class = "btn btn-default theme-controlled property scrollFocus">
-        <!-- scrollFocus class -->
-        <span>
-          {{ p.displayName || p.name }}
-        </span>
-      </div>
-    </div>
-  </div>
-</div>
-
-<!-- container class -->
-<div class="d-flex flex-column-reverse flex-nowrap align-items-center base-container position-relative">
-
-  <!-- ctrl nub -->
-  <div class="h-0 d-inline-flex align-items-center" [hidden]="!(showInitiator$ | async)" #initiator>
-    <div (contextmenu)="$event.stopPropagation(); $event.preventDefault();"
-      [ngStyle]="ctrlBtnPosition"
-      class="pe-all"
-      initiator>
-      <button mat-mini-fab color="primary">
-        <i class="fas fa-globe"></i>
-      </button>
-    </div>
-  </div>
-
-  <!-- guide text -->
-  <mat-card [ngClass]="{ 'scale-y-0': !(showScreen$ | async)  }"
-    class="mb-4 popup muted position-absolute subMenu">
-    <mat-card-content>
-      <ng-container *ngTemplateOutlet="guideTmpl">
-      </ng-container>
-    </mat-card-content>
-  </mat-card>
-
-  <!-- mobile set value -->
-  <div *ngIf="showDelta$ | async" class="position-absolute h-0 w-100 d-flex flex-row justify-content-end align-items-end">
-
-    <!-- intersection observer -->
-    <div class="w-50 d-flex flex-row flex-nowrap" #subMenuObserver>
-    
-      <!-- value selection container -->
-      <div class="position-relative mb-4" [style.transform]="subMenuTransform" #setValueContainer>
-        <!-- value selections -->
-        <ng-container *ngFor="let value of focusedProperty.values">
-          <!-- selected button -->
-          <ng-template
-            [ngIf]="focusedProperty.selected === value"
-            [ngIfElse]="notSelectedTmpl">
-            <button
-              [attr.data-submenu-value]="value"
-              mat-flat-button
-              [ngClass]="{ 'muted': (highlightedSubmenu$ | async) !== value }"
-              color="primary"
-              class="mr-2">
-              {{ value }}
-            </button>
-          </ng-template>
-
-          <!-- not selected button -->
-          <ng-template #notSelectedTmpl>
-            <button
-              [attr.data-submenu-value]="value"
-              mat-flat-button
-              [ngClass]="{ 'muted': (highlightedSubmenu$ | async) !== value }"
-              color="default"
-              class="mr-2">
-              {{ value }}
-            </button>
-          </ng-template>
-
-        </ng-container>
-      </div>
-    </div>
-  </div>
-
-</div>
diff --git a/src/ui/nehubaContainer/reorderPanelIndex.pipe.ts b/src/ui/nehubaContainer/reorderPanelIndex.pipe.ts
deleted file mode 100644
index 3e76f4d0234a6942743f9eeff983eb065b938298..0000000000000000000000000000000000000000
--- a/src/ui/nehubaContainer/reorderPanelIndex.pipe.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-
-@Pipe({
-  name: 'reorderPanelIndexPipe',
-})
-
-export class ReorderPanelIndexPipe implements PipeTransform {
-  public transform(panelOrder: string, uncorrectedIndex: number) {
-    return uncorrectedIndex === null
-      ? null
-      : panelOrder.indexOf(uncorrectedIndex.toString())
-  }
-}
diff --git a/src/ui/topMenu/topMenuCmp/topMenu.components.ts b/src/ui/topMenu/topMenuCmp/topMenu.components.ts
index cf6922a353a041bd18b136ac7c1876dbc6e74c05..688da21160db6fef1fd93c1e95e17423cb627cbf 100644
--- a/src/ui/topMenu/topMenuCmp/topMenu.components.ts
+++ b/src/ui/topMenu/topMenuCmp/topMenu.components.ts
@@ -3,7 +3,6 @@ import {
   Component,
   Input,
   TemplateRef,
-  ViewChild,
 } from "@angular/core";
 import { Observable } from "rxjs";
 import { map } from "rxjs/operators";
diff --git a/src/ui/topMenu/topMenuCmp/topMenu.template.html b/src/ui/topMenu/topMenuCmp/topMenu.template.html
index a33377a72f41a7126bd272754e3862651fd8b86a..c64c0dcee48ba00781195e882d4ffe17cd84e72d 100644
--- a/src/ui/topMenu/topMenuCmp/topMenu.template.html
+++ b/src/ui/topMenu/topMenuCmp/topMenu.template.html
@@ -22,7 +22,7 @@
 
     <!-- pinned dataset -->
     <div iav-fab-speed-dial-child>
-      <ng-container *ngTemplateOutlet="pinnedDatasetBtnTmpl">
+      <ng-container *ngTemplateOutlet="downloadAllTmpl">
       </ng-container>
     </div>
 
@@ -53,7 +53,7 @@
     </ng-container>
 
     <!-- pinned dataset -->
-    <ng-container *ngTemplateOutlet="pinnedDatasetBtnTmpl">
+    <ng-container *ngTemplateOutlet="downloadAllTmpl">
     </ng-container>
 
     <!-- help one pager -->
@@ -100,7 +100,7 @@
 </ng-template>
 
 <!-- pinned dataset btn -->
-<ng-template #pinnedDatasetBtnTmpl>
+<ng-template #downloadAllTmpl>
   <div class="btnWrapper"
     [matTooltip]="(atlasDlDct.busy$| async) ? busyTxt : idleTxt"
     quick-tour
diff --git a/src/ui/ui.module.ts b/src/ui/ui.module.ts
index a657e32cc68916a0b63a485f226dbdd7a05e948e..2e083c87bbc17a268a43c2aba3cf1dd90acc5018 100644
--- a/src/ui/ui.module.ts
+++ b/src/ui/ui.module.ts
@@ -6,11 +6,6 @@ import { ScrollingModule } from "@angular/cdk/scrolling"
 import { HttpClientModule } from "@angular/common/http";
 import { AngularMaterialModule } from 'src/sharedModules'
 import { UtilModule } from "src/util";
-import { DownloadDirective } from "../util/directives/download.directive";
-import { MobileOverlay } from "./nehubaContainer/mobileOverlay/mobileOverlay.component";
-import { HumanReadableFileSizePipe } from "src/util/pipes/humanReadableFileSize.pipe";
-import { ReorderPanelIndexPipe } from "./nehubaContainer/reorderPanelIndex.pipe";
-import { FixedMouseContextualContainerDirective } from "src/util/directives/FixedMouseContextualContainerDirective.directive";
 import { ShareModule } from "src/share";
 import { AuthModule } from "src/auth";
 import { ActionDialog } from "./actionDialog/actionDialog.component";
@@ -34,14 +29,7 @@ import { HANDLE_SCREENSHOT_PROMISE, TypeHandleScrnShotPromise } from "../screens
     AuthModule,
   ],
   declarations: [
-    MobileOverlay,
     ActionDialog,
-    /* pipes */
-    HumanReadableFileSizePipe,
-    ReorderPanelIndexPipe,
-    /* directive */
-    DownloadDirective,
-    FixedMouseContextualContainerDirective,
   ],
   providers: [
     {
@@ -53,8 +41,10 @@ import { HANDLE_SCREENSHOT_PROMISE, TypeHandleScrnShotPromise } from "../screens
       provide: HANDLE_SCREENSHOT_PROMISE,
       useValue: ((param) => {
         const canvas: HTMLCanvasElement = document.querySelector('#neuroglancer-container canvas')
-        if (!canvas) return Promise.reject(`element '#neuroglancer-container canvas' not found`)
-        const _ = (window as any).viewer.display.draw()
+        if (!canvas) {
+          return Promise.reject(`element '#neuroglancer-container canvas' not found`)
+        }
+        (window as any).viewer.display.draw()
         if (!param) {
           return new Promise(rs => {
             canvas.toBlob(blob => {
@@ -101,10 +91,6 @@ import { HANDLE_SCREENSHOT_PROMISE, TypeHandleScrnShotPromise } from "../screens
     }
   ],
   exports: [
-    // NehubaContainer,
-    MobileOverlay,
-    // StatusCardComponent,
-    FixedMouseContextualContainerDirective,
   ]
 })
 
diff --git a/src/util/constants.ts b/src/util/constants.ts
index 001b1283945568cc84378cc55b4f06a41f02f866..763bc0695829eec89caa1c15c41d86be61d720ad 100644
--- a/src/util/constants.ts
+++ b/src/util/constants.ts
@@ -126,14 +126,21 @@ export const UNSUPPORTED_PREVIEW = [{
 
 export const UNSUPPORTED_INTERVAL = 7000
 
+export const SPECIES_ENUM = {
+  HOMO_SAPIENS: "Homo sapiens",
+  MACACA_FASCICULARIS: "Macaca fascicularis",
+  RATTUS_NORVEGICUS: "Rattus norvegicus",
+  MUS_MUSCULUS: "Mus musculus",
+} as const
+
 /**
  * atlas should follow the following order
  */
 export const speciesOrder = [
-  "Homo sapiens",
-  "Macaca fascicularis",
-  "Rattus norvegicus",
-  "Mus musculus"
+  SPECIES_ENUM.HOMO_SAPIENS,
+  SPECIES_ENUM.MACACA_FASCICULARIS,
+  SPECIES_ENUM.RATTUS_NORVEGICUS,
+  SPECIES_ENUM.MUS_MUSCULUS,
 ]
 
 export const parcBanList: string[] = [
diff --git a/src/util/df-to-ds.pipe.ts b/src/util/df-to-ds.pipe.ts
index 612b8b48220cb6463458d21bd63f5f5c2eb60e7b..f50be028d82a3522df9277be89ef75b59accd33d 100644
--- a/src/util/df-to-ds.pipe.ts
+++ b/src/util/df-to-ds.pipe.ts
@@ -1,5 +1,7 @@
 import { CdkTableDataSourceInput } from '@angular/cdk/table';
 import { Pipe, PipeTransform } from '@angular/core';
+import { MatSort } from '@angular/material/sort';
+import { MatTableDataSource } from '@angular/material/table';
 import { components } from "src/atlasComponents/sapi/schemaV3"
 type DF = components["schemas"]["DataFrameModel"]
 
@@ -19,11 +21,11 @@ function isDf(val: object): val is DF {
 })
 export class DfToDsPipe implements PipeTransform {
 
-  transform(df: object): CdkTableDataSourceInput<unknown> {
+  transform(df: object, sort: MatSort): CdkTableDataSourceInput<unknown> {
     if (!isDf(df)) {
       return null
     }
-    return df.data.map((arr, idx) => {
+    const v = df.data.map((arr, idx) => {
       const val = df.index[idx] as any
       const returnVal: Record<string, string|number|number[]> = {
         index: val,
@@ -37,6 +39,9 @@ export class DfToDsPipe implements PipeTransform {
       })
       return returnVal
     })
+    const ds = new MatTableDataSource(v)
+    ds.sort = sort
+    return ds
   }
 
 }
diff --git a/src/util/directives/FixedMouseContextualContainerDirective.directive.spec.ts b/src/util/directives/FixedMouseContextualContainerDirective.directive.spec.ts
deleted file mode 100644
index 0cf11aeb387d08a01a89f616e7095cc716fb63c5..0000000000000000000000000000000000000000
--- a/src/util/directives/FixedMouseContextualContainerDirective.directive.spec.ts
+++ /dev/null
@@ -1,131 +0,0 @@
-import { Component, ViewChild } from "@angular/core";
-import { TestBed } from "@angular/core/testing";
-import { FixedMouseContextualContainerDirective } from "./FixedMouseContextualContainerDirective.directive";
-import { By } from "@angular/platform-browser";
-
-@Component({
-  template: ''
-})
-
-class TestCmp{
-  @ViewChild(FixedMouseContextualContainerDirective) directive: FixedMouseContextualContainerDirective
-}
-
-describe('FixedMouseContextualContainerDirective', () => {
-  beforeEach(() => {
-    TestBed.configureTestingModule({
-      imports: [
-        
-      ],
-      declarations: [
-        TestCmp,
-        FixedMouseContextualContainerDirective
-      ]
-    })
-  })
-
-  it('> can instantiate directive properly', () => {
-    TestBed.overrideComponent(TestCmp, {
-      set: {
-        template: `
-        <div fixedMouseContextualContainerDirective>
-        </div>
-        `
-      }
-    }).compileComponents()
-
-    const fixture = TestBed.createComponent(TestCmp)
-    fixture.detectChanges()
-    const directive = fixture.debugElement.query( By.directive(FixedMouseContextualContainerDirective) )
-    expect(directive).toBeTruthy()
-
-    expect(fixture.componentInstance.directive).toBeTruthy()
-  })
-
-  describe('> hides if no content', () => {
-    it('> on #show, if content exists, isShown will be true', () => {
-
-      TestBed.overrideComponent(TestCmp, {
-        set: {
-          template: `
-          <div fixedMouseContextualContainerDirective>
-            <span>Hello World</span>
-          </div>
-          `
-        }
-      }).compileComponents()
-
-      const fixture = TestBed.createComponent(TestCmp)
-      fixture.detectChanges()
-
-      const cmp = fixture.componentInstance
-      cmp.directive.show()
-      fixture.detectChanges()
-      expect(cmp.directive.isShown).toBeTrue()
-    })
-
-    it('> on #show, if only comment exists, isShown will be false', () => {
-
-      TestBed.overrideComponent(TestCmp, {
-        set: {
-          template: `
-          <div fixedMouseContextualContainerDirective>
-            <!-- hello world -->
-          </div>
-          `
-        }
-      }).compileComponents()
-
-      const fixture = TestBed.createComponent(TestCmp)
-      fixture.detectChanges()
-
-      const cmp = fixture.componentInstance
-      cmp.directive.show()
-      fixture.detectChanges()
-      expect(cmp.directive.isShown).toBeFalse()
-    })
-
-    it('> on #show, if only text exists, isShown will be false', () => {
-
-      TestBed.overrideComponent(TestCmp, {
-        set: {
-          template: `
-          <div fixedMouseContextualContainerDirective>
-            hello world
-          </div>
-          `
-        }
-      }).compileComponents()
-
-      const fixture = TestBed.createComponent(TestCmp)
-      fixture.detectChanges()
-
-      const cmp = fixture.componentInstance
-      cmp.directive.show()
-      fixture.detectChanges()
-      expect(cmp.directive.isShown).toBeFalse()
-    })
-
-    it('> on #show, if nothing exists, isShown will be false', () => {
-
-      TestBed.overrideComponent(TestCmp, {
-        set: {
-          template: `
-          <div fixedMouseContextualContainerDirective>
-          </div>
-          `
-        }
-      }).compileComponents()
-
-      const fixture = TestBed.createComponent(TestCmp)
-      fixture.detectChanges()
-
-      const cmp = fixture.componentInstance
-      cmp.directive.show()
-      fixture.detectChanges()
-      expect(cmp.directive.isShown).toBeFalse()
-    })
-  })
-
-  // TODO complete tests for FixedMouseContextualContainerDirective
-})
\ No newline at end of file
diff --git a/src/util/directives/FixedMouseContextualContainerDirective.directive.ts b/src/util/directives/FixedMouseContextualContainerDirective.directive.ts
deleted file mode 100644
index d7fbf0905efa08166e8f864f45cf375a97950102..0000000000000000000000000000000000000000
--- a/src/util/directives/FixedMouseContextualContainerDirective.directive.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-import { Directive, ElementRef, EventEmitter, HostBinding, Input, Output, AfterContentChecked, ChangeDetectorRef, AfterViewInit } from "@angular/core";
-
-@Directive({
-  selector: '[fixedMouseContextualContainerDirective]',
-  exportAs: 'iavFixedMouseCtxContainer'
-})
-
-export class FixedMouseContextualContainerDirective implements AfterContentChecked {
-
-  private defaultPos: [number, number] = [-1e3, -1e3]
-  public isShown: boolean = false
-
-  @Input()
-  public mousePos: [number, number] = this.defaultPos
-
-  @Output()
-  public onShow: EventEmitter<null> = new EventEmitter()
-
-  @Output()
-  public onHide: EventEmitter<null> = new EventEmitter()
-
-  constructor(
-    public el: ElementRef,
-    private cdr: ChangeDetectorRef,
-  ) {
-  }
-
-  public recalculatePosition(){
-    const clientWidth = this.el.nativeElement.clientWidth
-    const clientHeight = this.el.nativeElement.clientHeight
-
-    const windowInnerWidth = window.innerWidth
-    const windowInnerHeight = window.innerHeight
-    if (windowInnerHeight - this.mousePos[1] < clientHeight) {
-      this.mousePos[1] = windowInnerHeight - clientHeight
-    }
-
-    if ((windowInnerWidth - this.mousePos[0]) < clientWidth) {
-      this.mousePos[0] = windowInnerWidth - clientWidth
-    }
-
-    this.transform = `translate(${this.mousePos.map(v => v.toString() + 'px').join(', ')})`
-  }
-
-  ngAfterContentChecked(){
-    if (this.el.nativeElement.childElementCount === 0) {
-      this.hide()
-    }
-    this.recalculatePosition()
-    this.cdr.markForCheck()
-  }
-
-  public show() {
-    this.styleDisplay = 'inline-block'
-    this.isShown = true
-    this.onShow.emit()
-  }
-
-  public hide() {
-    this.transform = `translate(${this.defaultPos.map(v => v.toString() + 'px').join(', ')})`
-    this.styleDisplay = 'none'
-    this.isShown = false
-    this.onHide.emit()
-  }
-
-  @HostBinding('style.display')
-  public styleDisplay = `none`
-
-  @HostBinding('style.transform')
-  public transform = `translate(${this.mousePos.map(v => v.toString() + 'px').join(', ')})`
-
-}
diff --git a/src/util/directives/download.directive.ts b/src/util/directives/download.directive.ts
deleted file mode 100644
index c0851002d0977cd7e534bc0bbf0b99cd446a7921..0000000000000000000000000000000000000000
--- a/src/util/directives/download.directive.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { Directive, ElementRef, Renderer2 } from "@angular/core";
-
-@Directive({
-  selector : 'a[download]',
-})
-
-export class DownloadDirective {
-
-  public downloadIcon: HTMLElement
-
-  constructor(public el: ElementRef, public rd2: Renderer2) {
-    this.downloadIcon = rd2.createElement('i')
-    rd2.addClass(this.downloadIcon, 'fas')
-    rd2.addClass(this.downloadIcon, 'fa-download-alt')
-  }
-
-  public ngAfterViewInit() {
-    this.rd2.appendChild(this.el.nativeElement, this.downloadIcon)
-  }
-}
diff --git a/src/util/fn.ts b/src/util/fn.ts
index ff9b1c07fc1d93938e4f6e62a990aa34ce84422a..6457ccf78a80061da34734ae0c3b2e20b0ffe238 100644
--- a/src/util/fn.ts
+++ b/src/util/fn.ts
@@ -17,6 +17,9 @@ export function getDebug() {
   return (window as any).__DEBUG__
 }
 
+// eslint-disable-next-line  @typescript-eslint/no-empty-function
+export function noop(){}
+
 export async function getExportNehuba() {
   // eslint-disable-next-line no-constant-condition
   while (true) {
@@ -26,15 +29,6 @@ export async function getExportNehuba() {
   }
 }
 
-const recursiveFlatten = (region, {ngId}) => {
-  return [{
-    ngId,
-    ...region,
-  }].concat(
-    ...((region.children && region.children.map && region.children.map(c => recursiveFlatten(c, { ngId : region.ngId || ngId })) ) || []),
-  )
-}
-
 export function getUuid(){
   return crypto.getRandomValues(new Uint32Array(1))[0].toString(16)
 }
@@ -140,7 +134,7 @@ export const CachedFunction = (config?: TCacheFunctionArg) => {
   }
 }
 
-// A quick, non security hash function
+// A quick, non secure hash function
 export class QuickHash {
   private length = 6
   constructor(opts?: any){
@@ -423,7 +417,7 @@ export function bufferUntil<T>(opts: ISwitchMapWaitFor) {
 export function defaultdict<T>(fn: () => T): Record<string, T> {
   const obj = {}
   return new Proxy(obj, {
-    get(target, prop, rec) {
+    get(target, prop, _rec) {
       if (!(prop in target)){
         target[prop] = fn()
       }
diff --git a/src/util/interfaces.ts b/src/util/interfaces.ts
index 6c057cb1ebe6acb7381f58864a0511cb3c1dc061..b9ba0df799dcd95099fdc94e718d20342bbfe62e 100644
--- a/src/util/interfaces.ts
+++ b/src/util/interfaces.ts
@@ -3,7 +3,6 @@
  */
 
 import { InjectionToken } from "@angular/core"
-import { Observable } from "rxjs"
 
 export interface IHasId{
   ['@id']: string
diff --git a/src/util/pipes/humanReadableFileSize.pipe.spec.ts b/src/util/pipes/humanReadableFileSize.pipe.spec.ts
deleted file mode 100644
index 74ae1113992802d8994023e8724acb4ee30a97ca..0000000000000000000000000000000000000000
--- a/src/util/pipes/humanReadableFileSize.pipe.spec.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import {} from 'jasmine'
-import { HumanReadableFileSizePipe } from './humanReadableFileSize.pipe'
-
-describe('humanReadableFileSize.pipe.ts', () => {
-  describe('HumanReadableFileSizePipe', () => {
-    it('steps properly when nubmers ets large', () => {
-      const pipe = new HumanReadableFileSizePipe()
-      const num = 12
-
-      expect(pipe.transform(num, 0)).toBe(`12 byte(s)`)
-      expect(pipe.transform(num * 1e3, 0)).toBe(`12 KB`)
-      expect(pipe.transform(num * 1e6, 0)).toBe(`12 MB`)
-      expect(pipe.transform(num * 1e9, 0)).toBe(`12 GB`)
-      expect(pipe.transform(num * 1e12, 0)).toBe(`12 TB`)
-
-      expect(pipe.transform(num * 1e15, 0)).toBe(`12000 TB`)
-    })
-
-    it('pads the correct zeros', () => {
-      const pipe = new HumanReadableFileSizePipe()
-      const num = 3.14159
-
-      expect(pipe.transform(num, 0)).toBe(`3 byte(s)`)
-      expect(pipe.transform(num, 1)).toBe(`3.1 byte(s)`)
-      expect(pipe.transform(num, 2)).toBe(`3.14 byte(s)`)
-      expect(pipe.transform(num, 3)).toBe(`3.142 byte(s)`)
-      expect(pipe.transform(num, 4)).toBe(`3.1416 byte(s)`)
-      expect(pipe.transform(num, 5)).toBe(`3.14159 byte(s)`)
-      expect(pipe.transform(num, 6)).toBe(`3.141590 byte(s)`)
-      expect(pipe.transform(num, 7)).toBe(`3.1415900 byte(s)`)
-    })
-
-    it('parses string as well as number', () => {
-      // TODO finish tests
-    })
-
-    it('throws when a non number is passed to either argument', () => {
-      // TODO finish tests
-    })
-
-  })
-})
diff --git a/src/util/pipes/humanReadableFileSize.pipe.ts b/src/util/pipes/humanReadableFileSize.pipe.ts
deleted file mode 100644
index 1abb96e6bcb826dd64a4ebda3183a7e3b910df85..0000000000000000000000000000000000000000
--- a/src/util/pipes/humanReadableFileSize.pipe.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-
-export const steps = [
-  'byte(s)',
-  'KB',
-  'MB',
-  'GB',
-  'TB',
-]
-
-@Pipe({
-  name: 'humanReadableFileSizePipe',
-})
-
-export class HumanReadableFileSizePipe implements PipeTransform {
-  public transform(input: string | number, precision: number = 2) {
-    let _input = Number(input)
-    if (!_input) { throw new Error(`HumanReadableFileSizePipe needs a string or a number that can be parsed to number`) }
-    const _precision = Number(precision)
-    if (isNaN(_precision)) { throw new Error(`precision must be a number`) }
-    let counter = 0
-    while (_input > 1000 && counter < 4) {
-      _input = _input / 1000
-      counter += 1
-    }
-    return `${_input.toFixed(precision)} ${steps[counter]}`
-  }
-}
diff --git a/src/util/pullable.ts b/src/util/pullable.ts
index 24ae06f3b70e51ac84cef085807866dfea5a2ade..6a5d87e96701b3c9c41553ce66c0f611054c0afa 100644
--- a/src/util/pullable.ts
+++ b/src/util/pullable.ts
@@ -1,5 +1,5 @@
 import { DataSource } from "@angular/cdk/collections"
-import { BehaviorSubject, Observable, ReplaySubject, Subscription, combineLatest, concat, of, timer } from "rxjs"
+import { BehaviorSubject, Observable, ReplaySubject, Subscription, concat, of, timer } from "rxjs"
 import { finalize, map, scan, shareReplay, startWith, tap } from "rxjs/operators"
 import { cachedPromise } from "./fn"
 
diff --git a/src/viewerModule/nehuba/base.service/base.service.ts b/src/viewerModule/nehuba/base.service/base.service.ts
index bb42ed31f608691ce4b91084927b9a44d0a8a558..a6bfe046ab51e81f2d84db4930e9747d22f0388d 100644
--- a/src/viewerModule/nehuba/base.service/base.service.ts
+++ b/src/viewerModule/nehuba/base.service/base.service.ts
@@ -44,7 +44,7 @@ export class BaseService {
             regionmap.set(r.name, r)
           }
           const returnVal: Record<string, Record<number, NgMapReturnType>> = {}
-          for (const [url, { layer, region }] of Object.entries(record)) {
+          for (const [ /* url */ , { layer, region }] of Object.entries(record)) {
             
             
             for (const { name, label } of region) {
diff --git a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.effects.ts b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.effects.ts
index 68f181a8654696b4c3c080bc8f079f694dce4591..b8e5e4b26a8f59e6f0dce71eaf8ee227d6e46e80 100644
--- a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.effects.ts
+++ b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.effects.ts
@@ -172,7 +172,7 @@ export class LayerCtrlEffects {
         parcNgLayers: from(this.sapi.getTranslatedLabelledNgMap(parcellation, template)).pipe(
           map(val => {
             const returnVal: Record<string, NgSegLayerSpec> = {}
-            for (const [ url, { layer, region } ] of Object.entries(val)) {
+            for (const [ /** url */, { layer, region } ] of Object.entries(val)) {
               const { name } = region[0]
               const ngId = getParcNgId(atlas, template, parcellation, {
                 id: '',
diff --git a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts
index f68170be4a59e2610000d997b852084512529dc4..be87e8eeec2a8fad91596feadac14a1dde29da3f 100644
--- a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts
+++ b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts
@@ -46,13 +46,16 @@ export class NehubaLayerControlService implements OnDestroy{
     combineLatest([
       this.completeNgIdLabelRegionMap$,
       this.customLayers$,
+      this.selectedRegion$,
     ]).pipe(
-      map(([record, layers]) => {
+      map(([record, layers, selectedRegions]) => {
         const returnVal: IColorMap = {}
 
         const cmCustomLayers = layers.filter(l => l.clType === "customlayer/colormap") as atlasAppearance.const.ColorMapCustomLayer[]
         const cmBaseLayers = layers.filter(l => l.clType === "baselayer/colormap") as atlasAppearance.const.ColorMapCustomLayer[]
         
+        const usingCustomCM = cmCustomLayers.length > 0
+
         const useCm = (() => {
           /**
            * if custom layer exist, use the last custom layer
@@ -72,11 +75,20 @@ export class NehubaLayerControlService implements OnDestroy{
             get: (r: SxplrRegion) => r.color
           }
         })()
+
+        const selectedRegionNameSet = new Set(selectedRegions.map(v => v.name))
         
         for (const [ngId, labelRecord] of Object.entries(record)) {
           for (const [label, region] of Object.entries(labelRecord)) {
             if (!region.color) continue
-            const [ red, green, blue ] = useCm.get(region) || [200, 200, 200]
+            /**
+             * if custom color map is used, do *not* selectively paint selected region
+             * custom color map can choose to subscribe to selected regions, and update the color map accordingly, 
+             * if they wish to respect the selected regions
+             */
+            const [ red, green, blue ] = usingCustomCM || selectedRegionNameSet.size === 0 || selectedRegionNameSet.has(region.name)
+              ? useCm.get(region) || [200, 200, 200]
+              : [255, 255, 255]
             if (!returnVal[ngId]) {
               returnVal[ngId] = {}
             }
diff --git a/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.component.ts b/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.component.ts
index a6e58b8462cb398d87f2f6b26b61607110950f2d..118031cc0806bb692dead3bff0f7811adae32759 100644
--- a/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.component.ts
+++ b/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.component.ts
@@ -4,7 +4,7 @@ import { combineLatest, fromEvent, interval, merge, Observable, of, Subject, Sub
 import { userInterface } from "src/state";
 import { NehubaViewerUnit } from "../../nehubaViewer/nehubaViewer.component";
 import { NEHUBA_INSTANCE_INJTKN, takeOnePipe, getFourPanel, getHorizontalOneThree, getSinglePanel, getPipPanel, getVerticalOneThree } from "../../util";
-import { QUICKTOUR_DESC, ARIA_LABELS, IDS } from 'common/constants'
+import { QUICKTOUR_DESC, QUICKTOUR_DESC_MD, ARIA_LABELS, IDS } from 'common/constants'
 import { IQuickTourData } from "src/ui/quickTour/constrants";
 import { debounce, debounceTime, distinctUntilChanged, filter, map, mapTo, switchMap, take } from "rxjs/operators";
 import {panelOrder} from "src/state/userInterface/selectors";
@@ -28,6 +28,7 @@ export class NehubaLayoutOverlay implements OnDestroy{
   public quickTourSliceViewSlide: IQuickTourData = {
     order: 1,
     description: QUICKTOUR_DESC.SLICE_VIEW,
+    descriptionMd: QUICKTOUR_DESC_MD.SLICE_VIEW
   }
 
   public quickTour3dViewSlide: IQuickTourData = {
diff --git a/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.template.html b/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.template.html
index 5ae334a2db080f80a983734c1004c3e3e46e231b..18968039f924b3625f13254ff2b2d6e0bf346f61 100644
--- a/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.template.html
+++ b/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.template.html
@@ -7,6 +7,7 @@
     [iav-window-resize-time]="64"
     (iav-window-resize-event)="setQuickTourPos()"
     quick-tour
+    [quick-tour-description-md]="quickTourSliceViewSlide.descriptionMd"
     [quick-tour-description]="quickTourSliceViewSlide.description"
     [quick-tour-order]="quickTourSliceViewSlide.order"
     [quick-tour-overwrite-arrow]="sliceViewArrow"
diff --git a/src/viewerModule/nehuba/mesh.service/mesh.service.spec.ts b/src/viewerModule/nehuba/mesh.service/mesh.service.spec.ts
index 051d366a882a44f75f782e10fe58ccd25314869e..7db5392a46b74554df8c9e2db2ab8d5e221ab44b 100644
--- a/src/viewerModule/nehuba/mesh.service/mesh.service.spec.ts
+++ b/src/viewerModule/nehuba/mesh.service/mesh.service.spec.ts
@@ -196,6 +196,8 @@ describe('> mesh.service.ts', () => {
           beforeEach(() => {
             const mockStore = TestBed.inject(MockStore)
             mockStore.overrideSelector(atlasSelection.selectors.selectedRegions, [])
+            mockStore.overrideSelector(atlasSelection.selectors.selectedTemplate, {} as any)
+            mockStore.overrideSelector(atlasSelection.selectors.selectedParcellation, {} as any)
           })
 
           it("> load all meshes", () => {
@@ -226,6 +228,8 @@ describe('> mesh.service.ts', () => {
           beforeEach(() => {
             const mockStore = TestBed.inject(MockStore)
             mockStore.overrideSelector(atlasSelection.selectors.selectedRegions, [fits1])
+            mockStore.overrideSelector(atlasSelection.selectors.selectedTemplate, {} as any)
+            mockStore.overrideSelector(atlasSelection.selectors.selectedParcellation, {} as any)
           })
           it("> load only selected mesh", () => {
 
diff --git a/src/viewerModule/nehuba/mesh.service/mesh.service.ts b/src/viewerModule/nehuba/mesh.service/mesh.service.ts
index 29a40e94c6882b6022033b2d55103627d3d9b8cf..2af102701593d4918f8b4c45e3eef92f2382952b 100644
--- a/src/viewerModule/nehuba/mesh.service/mesh.service.ts
+++ b/src/viewerModule/nehuba/mesh.service/mesh.service.ts
@@ -7,6 +7,7 @@ import { selectorAuxMeshes } from "../store";
 import { LayerCtrlEffects } from "../layerCtrl.service/layerCtrl.effects";
 import { atlasSelection } from "src/state";
 import { BaseService } from "../base.service/base.service";
+import { IDS } from "src/atlasComponents/sapi"
 
 /**
  * control mesh loading etc
@@ -38,7 +39,7 @@ export class NehubaMeshService implements OnDestroy {
       const ngIdRecord: Record<string, number[]> = {}
 
       for (const [ngId, labelToRegion] of Object.entries(record)) {
-        for (const [label, _region] of Object.entries(labelToRegion)) {
+        for (const [label, ] of Object.entries(labelToRegion)) {
           if (!ngIdRecord[ngId]) {
             ngIdRecord[ngId] = []
           }
@@ -126,8 +127,21 @@ export class NehubaMeshService implements OnDestroy {
     this.#allSegmentMeshes$,
     this.#selectedSegmentMeshes$,
     this.#auxMesh$,
+    this.store$.pipe(
+      select(atlasSelection.selectors.selectedTemplate)
+    ),
+    this.store$.pipe(
+      select(atlasSelection.selectors.selectedParcellation)
+    )
   ]).pipe(
-    switchMap(([ allSegMesh, selectedSegMesh, auxmesh ]) => {
+    switchMap(([ allSegMesh, selectedSegMesh, auxmesh, selectedTemplate, selectedParcellation ]) => {
+      /**
+       * TODO monkey patching jba29 in colin to show all meshes
+       * 
+       */
+      if (selectedParcellation.id === IDS.PARCELLATION.JBA29 && selectedTemplate.id === IDS.TEMPLATES.COLIN27) {
+        return of(...allSegMesh)
+      }
       const hasSegSelected = selectedSegMesh.some(v => v.labelIndicies.length !== 0)
       const hasAuxMesh = auxmesh.length > 0
       const meshesToLoad: IMeshesToLoad[] = []
diff --git a/src/viewerModule/nehuba/module.ts b/src/viewerModule/nehuba/module.ts
index 8243707dae27438a851e0ccd38cf39fa428519ff..682fae0bb9cf878ebd3ff18007a1ad26eb8cbfb0 100644
--- a/src/viewerModule/nehuba/module.ts
+++ b/src/viewerModule/nehuba/module.ts
@@ -1,4 +1,4 @@
-import { APP_INITIALIZER, CUSTOM_ELEMENTS_SCHEMA, NgModule } from "@angular/core";
+import { APP_INITIALIZER, NgModule } from "@angular/core";
 import { NehubaViewerContainerDirective } from './nehubaViewerInterface/nehubaViewerInterface.directive'
 import { IMPORT_NEHUBA_INJECT_TOKEN, NehubaViewerUnit } from "./nehubaViewer/nehubaViewer.component";
 import { CommonModule } from "@angular/common";
diff --git a/src/viewerModule/nehuba/navigation.service/navigation.base.service.ts b/src/viewerModule/nehuba/navigation.service/navigation.base.service.ts
index dd82454761ad41d4ccb18f8a37ea3abbabe3c6f5..66413f1ffe994dab36f2ecc0aa3eab5c6225b374 100644
--- a/src/viewerModule/nehuba/navigation.service/navigation.base.service.ts
+++ b/src/viewerModule/nehuba/navigation.service/navigation.base.service.ts
@@ -1,6 +1,6 @@
 import { Inject, Injectable, Optional } from "@angular/core";
 import { concat, EMPTY, NEVER, Observable, of } from "rxjs";
-import { delay, exhaustMap, shareReplay, switchMap, take, tap } from "rxjs/operators";
+import { delay, exhaustMap, shareReplay, switchMap, take } from "rxjs/operators";
 import { TNehubaViewerUnit } from "../constants";
 import { NEHUBA_INSTANCE_INJTKN } from "../util";
 
diff --git a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts
index e1806ae87856ee7dd6756f15e122ce54e3bc1409..e0e25cd158aedca533bbe1b9a336832ff2f82298 100644
--- a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts
+++ b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts
@@ -13,17 +13,6 @@ import { IColorMap, SET_COLORMAP_OBS, SET_LAYER_VISIBILITY } from "../layerCtrl.
  */
 import { INgLayerCtrl, NG_LAYER_CONTROL, SET_SEGMENT_VISIBILITY, TNgLayerCtrl } from "../layerCtrl.service/layerCtrl.util";
 
-const NG_LANDMARK_LAYER_NAME = 'spatial landmark layer'
-const NG_USER_LANDMARK_LAYER_NAME = 'user landmark layer'
-
-/**
- * optimized for nehubaConfig.layout.useNehubaPerspective.fixedZoomPerspectiveSlices
- *  sliceZoom
- *  sliceViewportWidth
- *  sliceViewportHeight
- */
-const NG_LANDMARK_CONSTANT = 1e-8
-
 export const IMPORT_NEHUBA_INJECT_TOKEN = `IMPORT_NEHUBA_INJECT_TOKEN`
 
 interface LayerLabelIndex {
@@ -85,8 +74,7 @@ export class NehubaViewerUnit implements OnDestroy {
         url?: string
       }
     }> = new EventEmitter()
-  @Output() public mouseoverLandmarkEmitter: EventEmitter<string> = new EventEmitter()
-  @Output() public mouseoverUserlandmarkEmitter: EventEmitter<string> = new EventEmitter()
+
   @Output() public regionSelectionEmitter: EventEmitter<{
     segment: number
     layer: {
@@ -95,6 +83,7 @@ export class NehubaViewerUnit implements OnDestroy {
   }}> = new EventEmitter()
   @Output() public errorEmitter: EventEmitter<any> = new EventEmitter()
 
+  @Output() public totalMeshesToLoad = new EventEmitter<number>()
 
   /* only used to set initial navigation state */
   public initNav: any
@@ -319,7 +308,7 @@ export class NehubaViewerUnit implements OnDestroy {
             totalMeshes += labelIndicies.length
             this.nehubaViewer.setMeshesToLoad(labelIndicies, layer)
           }
-          // TODO implement total mesh to be loaded and mesh loading UI
+          this.totalMeshesToLoad.emit(totalMeshes)
         }),
       )
     } else {
@@ -336,25 +325,6 @@ export class NehubaViewerUnit implements OnDestroy {
     }
   }
 
-  public spatialLandmarkSelectionChanged(labels: number[]) {
-    const getCondition = (label: number) => `if(label > ${label - 0.1} && label < ${label + 0.1} ){${FRAGMENT_EMIT_RED}}`
-    const newShader = `void main(){ ${labels.map(getCondition).join('else ')}else {${FRAGMENT_EMIT_WHITE}} }`
-    if (!this.nehubaViewer) {
-      this.log.warn('setting special landmark selection changed failed ... nehubaViewer is not yet defined')
-      return
-    }
-    const landmarkLayer = this.nehubaViewer.ngviewer.layerManager.getLayerByName(NG_LANDMARK_LAYER_NAME)
-    if (!landmarkLayer) {
-      this.log.warn('landmark layer could not be found ... will not update colour map')
-      return
-    }
-    if (labels.length === 0) {
-      landmarkLayer.layer.displayState.fragmentMain.restoreState(FRAGMENT_MAIN_WHITE)
-    } else {
-      landmarkLayer.layer.displayState.fragmentMain.restoreState(newShader)
-    }
-  }
-
   public navPosReal: [number, number, number] = [0, 0, 0]
   public navPosVoxel: [number, number, number] = [0, 0, 0]
 
@@ -483,20 +453,6 @@ export class NehubaViewerUnit implements OnDestroy {
     )
   }
 
-  private userLandmarkShader: string = FRAGMENT_MAIN_WHITE
-
-  public removeSpatialSearch3DLandmarks() {
-    this.removeLayer({
-      name : NG_LANDMARK_LAYER_NAME,
-    })
-  }
-
-  public removeuserLandmarks() {
-    this.removeLayer({
-      name : NG_USER_LANDMARK_LAYER_NAME,
-    })
-  }
-
   public setLayerVisibility(condition: {name: string|RegExp}, visible: boolean) {
     if (!this.nehubaViewer) {
       return false
@@ -798,19 +754,6 @@ export class NehubaViewerUnit implements OnDestroy {
         })
       })
 
-    // TODO bug: mouseoverlandmarkemitter does not emit empty for VTK layer when user mouse click
-    this.ondestroySubscriptions.push(
-      this.nehubaViewer.mouseOver.layer
-        .filter(obj => obj.layer.name === NG_LANDMARK_LAYER_NAME)
-        .subscribe(obj => this.mouseoverLandmarkEmitter.emit(obj.value)),
-    )
-
-    this.ondestroySubscriptions.push(
-      this.nehubaViewer.mouseOver.layer
-        .filter(obj => obj.layer.name === NG_USER_LANDMARK_LAYER_NAME)
-        .subscribe(obj => this.mouseoverUserlandmarkEmitter.emit(obj.value)),
-    )
-
     this._s4$ = this.nehubaViewer.navigationState.position.inRealSpace
       .filter(v => typeof v !== 'undefined' && v !== null)
       .subscribe(v => {
diff --git a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts
index 1ff596295312d22d8d87e775c3c12821f0f1cbcb..c3bc899bd568f5242f37f4a90f2cf1f57f78579b 100644
--- a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts
+++ b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts
@@ -1,4 +1,4 @@
-import { ChangeDetectionStrategy, Component, EventEmitter, Inject, OnDestroy, Optional, Output, TemplateRef, ViewChild } from "@angular/core";
+import { ChangeDetectionStrategy, Component, EventEmitter, Inject, OnDestroy, Optional, Output } from "@angular/core";
 import { select, Store } from "@ngrx/store";
 import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR } from "src/util";
 import { distinctUntilChanged } from "rxjs/operators";
diff --git a/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.spec.ts b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.spec.ts
index 19314d6737ebfd0c3229b41f3fb62055d9b0bcb6..db6f3a030a26adaaf5748a2a0e1b4816f0080c85 100644
--- a/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.spec.ts
+++ b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.spec.ts
@@ -94,8 +94,6 @@ describe('> nehubaViewerInterface.directive.ts', () => {
         layersChanged: new Subject(),
         nehubaReady: new Subject(),
         mouseoverSegmentEmitter: new Subject(),
-        mouseoverLandmarkEmitter: new Subject(),
-        mouseoverUserlandmarkEmitter: new Subject(),
         elementRef: {
           nativeElement: {}
         },
@@ -171,76 +169,7 @@ describe('> nehubaViewerInterface.directive.ts', () => {
     })
   
     describe('> subscription of nehuba instance', () => {
-      describe('> mouseoverUserlandmarkEmitter', () => {
-        let spyNehubaViewerInstance: any
-        let dispatchSpy: jasmine.Spy
-        let directiveInstance: NehubaViewerContainerDirective
-        const template = {}
-        const lifecycle = {}
-        beforeEach(() => {
-
-          spyNehubaViewerInstance = {
-            config: null,
-            lifecycle: null,
-            templateId: null,
-            errorEmitter: new Subject(),
-            viewerPositionChange: new Subject(),
-            layersChanged: new Subject(),
-            nehubaReady: new Subject(),
-            mouseoverSegmentEmitter: new Subject(),
-            mouseoverLandmarkEmitter: new Subject(),
-            mouseoverUserlandmarkEmitter: new Subject(),
-            elementRef: {
-              nativeElement: {}
-            }
-          }
-          const mockStore = TestBed.inject(MockStore)
-          dispatchSpy = spyOn(mockStore, 'dispatch')
-
-          const fixture = TestBed.createComponent(DummyCmp)
-          const directive = fixture.debugElement.query(
-            By.directive(NehubaViewerContainerDirective)
-          )
-          
-          const spyComRef = {
-            destroy: jasmine.createSpy('destroy')
-          }
-          directiveInstance = directive.injector.get(NehubaViewerContainerDirective)
-          spyOnProperty(directiveInstance, 'nehubaViewerInstance').and.returnValue(spyNehubaViewerInstance)
-          spyOn(directiveInstance['el'], 'clear').and.callFake(() => {})
-          spyOn(directiveInstance, 'clear').and.callFake(() => {})
-          // casting return value to any is not perfect, but since only 2 methods and 1 property is used, it's a quick way 
-          // rather than allow component to be created
-          spyOn(directiveInstance['el'], 'createComponent').and.returnValue(spyComRef as any)
-
-        })
-
-        afterEach(() => {
-          dispatchSpy.calls.reset()
-        })
-        it('> single null emits null', fakeAsync(() => {
-
-        }))
 
-        it('> single value emits value', fakeAsync(() => {
-
-        }))
-
-        describe('> double value in 140ms emits last value', () => {
-
-          it('> null - 24 emits 24', fakeAsync(() => {
-
-          }))
-          it('> 24 - null emits null', fakeAsync(() => {
-
-
-          }))
-        })
-      
-        it('> single value outside 140 ms emits separately', fakeAsync(() => {
-
-        }))
-      })
     })
   })
 })
diff --git a/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.ts b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.ts
index aee39e6f48641ebdb9b5b8051f23c6c023a91545..45d9228cb40e036d94b3d78c644c722cf043e600 100644
--- a/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.ts
+++ b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.ts
@@ -1,8 +1,8 @@
-import { Directive, ViewContainerRef, ComponentRef, OnDestroy, Output, EventEmitter, Optional, ChangeDetectorRef, ComponentFactoryResolver, ComponentFactory } from "@angular/core";
+import { Directive, ViewContainerRef, ComponentRef, OnDestroy, Output, EventEmitter, Optional, ChangeDetectorRef } from "@angular/core";
 import { NehubaViewerUnit } from "../nehubaViewer/nehubaViewer.component";
 import { Store, select } from "@ngrx/store";
-import { Subscription, Observable, asyncScheduler, combineLatest } from "rxjs";
-import { distinctUntilChanged, filter, debounceTime, scan, map, throttleTime, switchMap, take, tap } from "rxjs/operators";
+import { Subscription, Observable, combineLatest } from "rxjs";
+import { distinctUntilChanged, filter, debounceTime, scan, map, switchMap, take } from "rxjs/operators";
 import { serializeSegment } from "../util";
 import { LoggingService } from "src/logging";
 import { arrayOfPrimitiveEqual } from 'src/util/fn'
@@ -313,19 +313,6 @@ export class NehubaViewerContainerDirective implements OnDestroy{
         map((map: Map<string, any>) => Array.from(map.entries()).filter(([_ngId, { segmentId }]) => segmentId)),
       ).subscribe(val => this.handleMouseoverSegments(val)),
 
-      this.nehubaViewerInstance.mouseoverLandmarkEmitter.pipe(
-        distinctUntilChanged()
-      ).subscribe(label => {
-        console.warn(`mouseover landmark`, label)
-      }),
-
-      this.nehubaViewerInstance.mouseoverUserlandmarkEmitter.pipe(
-        throttleTime(160, asyncScheduler, {trailing: true}),
-      ).subscribe(label => {
-        const idx = Number(label.replace('label=', ''))
-        // TODO 
-        // this is exclusive for vtk layer
-      }),
 
       combineLatest([
         this.nehubaViewerInstance.mousePosInVoxel$,
diff --git a/src/viewerModule/nehuba/userLayers/service.ts b/src/viewerModule/nehuba/userLayers/service.ts
index 042cc54851be1e31cf457926d81768dfcbc2a5a8..3fe9d0a7db656354ed6217ebd13a0fd0354deae4 100644
--- a/src/viewerModule/nehuba/userLayers/service.ts
+++ b/src/viewerModule/nehuba/userLayers/service.ts
@@ -1,8 +1,8 @@
 import { Injectable, OnDestroy } from "@angular/core"
 import { MatDialog } from "@angular/material/dialog"
 import { select, Store } from "@ngrx/store"
-import { concat, of, Subscription } from "rxjs"
-import { pairwise } from "rxjs/operators"
+import { concat, from, of, Subscription } from "rxjs"
+import { catchError, map, pairwise, switchMap } from "rxjs/operators"
 import {
   linearTransform,
   TVALID_LINEAR_XFORM_DST,
@@ -198,29 +198,48 @@ export class UserLayerService implements OnDestroy {
   ) {
     this.#subscription.push(
       concat(
-        of(null),
-        this.routerSvc.customRoute$.pipe(select((v) => v[OVERLAY_LAYER_KEY]))
-      )
-        .pipe(pairwise())
-        .subscribe(([prev, curr]) => {
-          if (prev) {
-            this.removeUserLayer(prev)
+        of(null as string),
+        this.routerSvc.customRoute$.pipe(
+          select(v => v[OVERLAY_LAYER_KEY])
+        )
+      ).pipe(
+        pairwise(),
+        switchMap(([prev, curr]) => {
+          /**
+           * for precomputed sources, check if transform.json exists.
+           * if so, try to fetch it, and set it as transform
+           */
+          if (!curr) {
+            return of([prev, curr, null])
           }
-          if (curr) {
-            this.addUserLayer(
-              curr,
-              {
-                filename: curr,
-                message: `Overlay layer populated in URL`,
-              },
-              {
-                shader: getShader({
-                  colormap: EnumColorMapName.MAGMA,
-                }),
-              }
-            )
+          if (!curr.startsWith("precomputed://")) {
+            return of([prev, curr, null])
           }
+          return from(fetch(`${curr.replace('precomputed://', '')}/transform.json`).then(res => res.json())).pipe(
+            catchError(() => of([prev, curr, null])),
+            map(transform => [prev, curr, transform])
+          )
         })
+      ).subscribe(([prev, curr, transform]) => {
+        if (prev) {
+          this.removeUserLayer(prev)
+        }
+        if (curr) {
+          this.addUserLayer(
+            curr,
+            {
+              filename: curr,
+              message: `Overlay layer populated in URL`,
+            },
+            {
+              shader: getShader({
+                colormap: EnumColorMapName.MAGMA,
+              }),
+              transform
+            }
+          )
+        }
+      })
     )
   }
 
diff --git a/src/viewerModule/nehuba/util.ts b/src/viewerModule/nehuba/util.ts
index 81aed3560abe6fe1f41b9faf13211d1002702591..c8d56c7e5ba037ab8366e4ca40d0066481146789 100644
--- a/src/viewerModule/nehuba/util.ts
+++ b/src/viewerModule/nehuba/util.ts
@@ -1,7 +1,7 @@
 import { InjectionToken } from '@angular/core'
 import { Observable, pipe } from 'rxjs'
 import { filter, scan, take } from 'rxjs/operators'
-import { getExportNehuba, getViewer } from 'src/util/fn'
+import { getViewer } from 'src/util/fn'
 import { NehubaViewerUnit } from './nehubaViewer/nehubaViewer.component'
 import { userInterface } from 'src/state'
 
diff --git a/src/viewerModule/nehuba/viewerCtrl/effects.ts b/src/viewerModule/nehuba/viewerCtrl/effects.ts
index fd10b9db4dabeadb61bd8f75683b3492081d7a74..5eeaafed8eaf61c7d03a5ddee0cb55759b39a8e6 100644
--- a/src/viewerModule/nehuba/viewerCtrl/effects.ts
+++ b/src/viewerModule/nehuba/viewerCtrl/effects.ts
@@ -2,7 +2,7 @@ import { Injectable } from "@angular/core";
 import { createEffect } from "@ngrx/effects";
 import { Store } from "@ngrx/store";
 import { of } from "rxjs";
-import { mapTo, switchMap } from "rxjs/operators";
+import { switchMap } from "rxjs/operators";
 import { atlasSelection, userInterface } from "src/state";
 
 @Injectable()
diff --git a/src/viewerModule/nehuba/viewerCtrl/perspectiveViewSlider/perspectiveViewSlider.component.ts b/src/viewerModule/nehuba/viewerCtrl/perspectiveViewSlider/perspectiveViewSlider.component.ts
index 67706f44a39f0b2600e75574dcfe8e25f41a18ac..6342411ebf03fabb2d1c816eac98cf15695a8451 100644
--- a/src/viewerModule/nehuba/viewerCtrl/perspectiveViewSlider/perspectiveViewSlider.component.ts
+++ b/src/viewerModule/nehuba/viewerCtrl/perspectiveViewSlider/perspectiveViewSlider.component.ts
@@ -1,7 +1,7 @@
 import { Component, OnDestroy, Inject, ViewChild, ChangeDetectionStrategy } from "@angular/core";
 import { FormControl } from "@angular/forms";
 import { select, Store } from "@ngrx/store";
-import { combineLatest, concat, forkJoin, NEVER, Observable, of, Subject, Subscription, throwError } from "rxjs";
+import { combineLatest, concat, NEVER, Observable, of, Subject, Subscription } from "rxjs";
 import { switchMap, distinctUntilChanged, map, debounceTime, shareReplay, take, withLatestFrom } from "rxjs/operators";
 import { SAPI } from "src/atlasComponents/sapi";
 import { SxplrTemplate } from "src/atlasComponents/sapi/sxplrTypes"
@@ -292,7 +292,7 @@ export class PerspectiveViewSlider implements OnDestroy {
         this.currentTemplateSize$,
         this.rangeControlIsVertical$,
       ]).pipe(
-        map(([ navigation, viewportSize, ctrl, templateSize, isVertical ]) => {
+        map(([ navigation, viewportSize, ctrl, templateSize, ..._rest ]) => {
           if (!ctrl || !(templateSize?.real) || !navigation) return null
 
           const { zoom, position } = navigation
diff --git a/src/viewerModule/threeSurfer/store/effects.ts b/src/viewerModule/threeSurfer/store/effects.ts
index 501107552338fc19658b5757c13610b3fae15782..86992a1c3330ed1b3150467d04aa4f984cde169e 100644
--- a/src/viewerModule/threeSurfer/store/effects.ts
+++ b/src/viewerModule/threeSurfer/store/effects.ts
@@ -1,7 +1,7 @@
 import { Injectable } from "@angular/core";
 import { createEffect } from "@ngrx/effects";
 import { select, Store } from "@ngrx/store";
-import { EMPTY, forkJoin, merge, Observable, of, pipe, throwError } from "rxjs";
+import { EMPTY, forkJoin, merge, Observable, of, pipe } from "rxjs";
 import { debounceTime, map, switchMap, withLatestFrom, filter, shareReplay, distinctUntilChanged } from "rxjs/operators";
 import { SAPI } from "src/atlasComponents/sapi";
 import { SxplrAtlas, SxplrParcellation, SxplrTemplate } from "src/atlasComponents/sapi/sxplrTypes"
@@ -118,7 +118,7 @@ export class ThreeSurferEffects {
   ))
 
   onATPDebounceAddBaseLayers$ = createEffect(() => this.onATPDebounceThreeSurferLayers$.pipe(
-    switchMap(({ labels, surfaces }) => {
+    switchMap(({ labels }) => {
       const labelMaps: ThreeSurferCustomLabelLayer[] = []
       for (const key in labels) {
         const { laterality, url } = labels[key]
diff --git a/src/viewerModule/viewerCmp/viewerCmp.component.ts b/src/viewerModule/viewerCmp/viewerCmp.component.ts
index 136f0baf82166534a9e0d7480159e0dce58edc32..27d4c91ba0ef4cf7975cc89a45ac821b24b3a580 100644
--- a/src/viewerModule/viewerCmp/viewerCmp.component.ts
+++ b/src/viewerModule/viewerCmp/viewerCmp.component.ts
@@ -1,4 +1,4 @@
-import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, TemplateRef, ViewChild, ViewContainerRef } from "@angular/core";
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, TemplateRef, ViewChild } from "@angular/core";
 import { select, Store } from "@ngrx/store";
 import { combineLatest, Observable, Subscription } from "rxjs";
 import { debounceTime, map, shareReplay } from "rxjs/operators";
@@ -14,6 +14,7 @@ import { atlasAppearance, atlasSelection, userInteraction } from "src/state";
 import { SxplrTemplate } from "src/atlasComponents/sapi/sxplrTypes";
 import { EntryComponent } from "src/features/entry/entry.component";
 import { TFace, TSandsPoint, getCoord } from "src/util/types";
+import { wait } from "src/util/fn";
 
 @Component({
   selector: 'iav-cmp-viewer-container',
@@ -438,4 +439,14 @@ export class ViewerCmp implements OnDestroy {
       })
     )
   }
+
+  @ViewChild('voiFeatureEntryCmp')
+  voiFeatureEntryCmp: EntryComponent
+
+  async pullAllVoi(){
+    if (this.voiFeatureEntryCmp){
+      await wait(320)
+      this.voiFeatureEntryCmp.pullAll()
+    }
+  }
 }
diff --git a/src/viewerModule/viewerCmp/viewerCmp.template.html b/src/viewerModule/viewerCmp/viewerCmp.template.html
index 0c80076b1593496a3f94cfb7892f312d192cad22..459fb19e841d22944af9cf5a8bca36512bcb6d0c 100644
--- a/src/viewerModule/viewerCmp/viewerCmp.template.html
+++ b/src/viewerModule/viewerCmp/viewerCmp.template.html
@@ -905,62 +905,69 @@
 <ng-template #viewerStatusCtxMenu let-data>
   <ng-template [ngIf]="data.context" let-context>
 
-    <mat-list>
+    <!-- ref space & position -->
+    <ng-container [ngSwitch]="context.viewerType">
 
-      <!-- ref space & position -->
-      <ng-container [ngSwitch]="context.viewerType">
-  
-        <!-- volumetric i.e. nehuba -->
-        <ng-container *ngSwitchCase="'nehuba'">
-          <mat-list-item mat-ripple
-            (click)="selectPoint({ point: context.payload.mouse.real }, data.metadata.template)">
-            <div mat-list-icon>
-              <i class="fas fa-map"></i>
-            </div>
-            
-            <div mat-line>
+      <!-- volumetric i.e. nehuba -->
+      <ng-container *ngSwitchCase="'nehuba'">
+        <button mat-button class="sxplr-list-like-button"
+          (click)="selectPoint({ point: context.payload.mouse.real }, data.metadata.template)">
+
+          <div class="sxplr-list-like-button-icon">
+            <i class="fas fa-map"></i>
+          </div>
+    
+          <div class="sxplr-list-like-button-body">
+    
+            <span class="sxplr-list-like-button-body-line">
               {{ context.payload.mouse.real | nmToMm | numbers | addUnitAndJoin : '' }} (mm)
-            </div>
-            <div mat-line class="text-muted">
+            </span>
+            <span class="sxplr-list-like-button-body-line text-muted">
               Point
-            </div>
-            <div mat-line class="text-muted">
+            </span>
+            <span class="sxplr-list-like-button-body-line text-muted">
               {{ data.metadata.template.name }}
-            </div>
-          </mat-list-item>
-        </ng-container>
-  
-        <ng-container *ngSwitchCase="'threeSurfer'">
-
-          <ng-template [ngIf]="context.payload?.faceIndex" let-faceIndex>
-            <ng-template [ngIf]="context.payload?.vertexIndices" let-vertexIndices>
-              <mat-list-item mat-ripple
-                (click)="selectPoint({ face: faceIndex, vertices: vertexIndices }, data.metadata.template)">
-
-                <div mat-list-icon>
-                  <i class="fas fa-map"></i>
-                </div>
-                
-                <div mat-line>
+            </span>
+          </div>    
+
+        </button>
+      </ng-container>
+
+      <ng-container *ngSwitchCase="'threeSurfer'">
+
+        <ng-template [ngIf]="context.payload?.faceIndex" let-faceIndex>
+          <ng-template [ngIf]="context.payload?.vertexIndices" let-vertexIndices>
+            <button mat-button class="sxplr-list-like-button"
+            (click)="selectPoint({ face: faceIndex, vertices: vertexIndices }, data.metadata.template)">
+    
+              <div class="sxplr-list-like-button-icon">
+                <i class="fas fa-map"></i>
+              </div>
+        
+              <div class="sxplr-list-like-button-body">
+        
+                <span class="sxplr-list-like-button-body-line">
                   Face Index: {{ faceIndex }}, Vertices Index: {{ vertexIndices | addUnitAndJoin : '' }}
-                </div>
-                <div mat-line class="text-muted">
+                </span>
+                <span class="sxplr-list-like-button-body-line text-muted">
                   Mesh Face
-                </div>
-                <div mat-line class="text-muted">
+                </span>
+                <span class="sxplr-list-like-button-body-line text-muted">
                   {{ data.metadata.template.name }}
-                </div>
-              </mat-list-item>
-            </ng-template>
+                </span>
+              </div>    
+    
+            </button>
+
           </ng-template>
-          
-        </ng-container>
-  
-        <ng-container *ngSwitchDefault>
-          DEFAULT
-        </ng-container>
+        </ng-template>
+        
+      </ng-container>
+
+      <ng-container *ngSwitchDefault>
+        DEFAULT
       </ng-container>
-    </mat-list>
+    </ng-container>
   </ng-template>
 </ng-template>
 
@@ -968,41 +975,32 @@
 <!-- viewer state hover ctx menu -->
 <ng-template #viewerStatusRegionCtxMenu let-data>
   <!-- hovered ROIs -->
-  <mat-list>
-    <ng-template ngFor [ngForOf]="data.metadata.hoveredRegions"
-      let-region
-      let-first="first">
+  <ng-template ngFor [ngForOf]="data.metadata.hoveredRegions"
+    let-region
+    let-first="first">
 
-      <mat-divider class="top-0" *ngIf="!first"></mat-divider>
+    <mat-divider class="top-0" *ngIf="!first"></mat-divider>
 
-      <mat-list-item mat-ripple
-        class="cursor-default"
-        (click)="$event.ctrlKey ? toggleRoi(region) : selectRoi(region)">
+    <button mat-button
+      (click)="$event.ctrlKey ? toggleRoi(region) : selectRoi(region)"
+      class="sxplr-list-like-button">
+      
+      <div class="sxplr-list-like-button-icon">
+        <i class="fas fa-brain"></i>
+      </div>
 
-        <div mat-list-icon>
-          <i class="fas fa-brain"></i>
-        </div>
+      <div class="sxplr-list-like-button-body">
 
-        <span mat-line>
+        <span class="sxplr-list-like-button-body-line">
           {{ region.name }}
         </span>
-        <span mat-line class="text-muted">
-          <span>
-            Brain region
-          </span>
+        <span class="sxplr-list-like-button-body-line text-muted">
+          Brain region
         </span>
-      
-        <!-- lookup region -->
-        <!-- <button mat-icon-button
-          (click)="selectRoi(region)"
-          ctx-menu-dismiss>
-          <i class="fas fa-search"></i>
-        </button> -->
-      </mat-list-item>
-      
+      </div>    
+    </button>
 
-    </ng-template>
-  </mat-list>
+  </ng-template>
 </ng-template>
 
 <!-- feature tmpls -->
@@ -1126,6 +1124,7 @@
     class="sxplr-pe-all mat-elevation-z8"
     [template]="view.selectedTemplate"
     [bbox]="bbox.bbox$ | async | getProperty : 'bbox'"
+    [attr.data-feature-length]="((voiFeatureEntryCmp.features$ | async) || []).length"
     #voiFeatureEntryCmp="featureEntryCmp">
   </sxplr-feature-entry>
 
@@ -1143,7 +1142,7 @@
     iav-switch
     [iav-switch-state]="false"
     #voiSwitch="iavSwitch"
-    (iav-switch-event)="$event && voiFeatureEntryCmp.pullAll()"
+    (iav-switch-event)="$event && pullAllVoi()"
     (click)="voiSwitch.toggle()">
 
     <ng-template [ngIf]="voiSwitch.switchState$ | async" [ngIfElse]="chevronCollapseTmpl">
@@ -1175,6 +1174,7 @@
 
 <div
   sxplr-sapiviews-core-space-boundingbox
+  (sxplr-sapiviews-core-space-boundingbox-changed)="pullAllVoi()"
   [sxplr-sapiviews-core-space-boundingbox-atlas]="selectedAtlas$ | async"
   [sxplr-sapiviews-core-space-boundingbox-space]="templateSelected$ | async"
   [sxplr-sapiviews-core-space-boundingbox-spec]="viewerCtx$ | async | nehubaVCtxToBbox"
diff --git a/src/widget/service.ts b/src/widget/service.ts
index 9430269418731ceb3b57810b54b26ef155ef7ef0..67856fb1f1c79ed1de0145394e3148f99c7bb633 100644
--- a/src/widget/service.ts
+++ b/src/widget/service.ts
@@ -1,5 +1,5 @@
 import { ComponentPortal } from "@angular/cdk/portal";
-import { ComponentFactory, ComponentFactoryResolver, ComponentRef, Injectable, Injector, ViewContainerRef } from "@angular/core";
+import { ComponentRef, Injectable, Injector, ViewContainerRef } from "@angular/core";
 import { RM_WIDGET } from "./constants";
 import { WidgetPortal } from "./widgetPortal/widgetPortal.component";
 
@@ -12,11 +12,6 @@ export class WidgetService {
   public vcr: ViewContainerRef
 
   private viewRefMap = new Map<WidgetPortal<unknown>, ComponentRef<WidgetPortal<unknown>>>()
-  private cf: ComponentFactory<WidgetPortal<unknown>>
-  
-  constructor(cfr: ComponentFactoryResolver){
-    this.cf = cfr.resolveComponentFactory(WidgetPortal)
-  }
 
   public addNewWidget<T>(Component: new (...arg: any) => T, injector: Injector): WidgetPortal<T> {
     const inj = Injector.create({
@@ -26,7 +21,8 @@ export class WidgetService {
       }],
       parent: injector
     })
-    const widgetPortal = this.vcr.createComponent(this.cf, 0, inj) as ComponentRef<WidgetPortal<T>>
+    
+    const widgetPortal = this.vcr.createComponent(WidgetPortal, {index: 0, injector: inj}) as ComponentRef<WidgetPortal<T>>
     const cmpPortal = new ComponentPortal<T>(Component, this.vcr, inj)
     
     this.viewRefMap.set(widgetPortal.instance, widgetPortal)