diff --git a/src/atlasComponents/sapi/core/sapiRegion.ts b/src/atlasComponents/sapi/core/sapiRegion.ts
index d8b1d2306ae27b9215fd7ca9ffa0f43c9208e0a3..585077f4b074579a9d96acf4ed725b4aa025f8a9 100644
--- a/src/atlasComponents/sapi/core/sapiRegion.ts
+++ b/src/atlasComponents/sapi/core/sapiRegion.ts
@@ -4,7 +4,7 @@ import { strToRgb, hexToRgb } from 'common/util'
 
 export class SAPIRegion{
 
-  static GetDisplayColor(region: SapiRegionModel){
+  static GetDisplayColor(region: SapiRegionModel): [number, number, number]{
     if (!region) {
       throw new Error(`region must be provided!`)
     }
diff --git a/src/atlasComponents/sapiViews/core/parcellation/tile/parcellation.tile.template.html b/src/atlasComponents/sapiViews/core/parcellation/tile/parcellation.tile.template.html
index 10a48ac5e779d57c46fba2d37a84b64ba6a67571..324120aa0f23b0d2f7504da98e2668c776c4fed0 100644
--- a/src/atlasComponents/sapiViews/core/parcellation/tile/parcellation.tile.template.html
+++ b/src/atlasComponents/sapiViews/core/parcellation/tile/parcellation.tile.template.html
@@ -11,7 +11,7 @@
           *ngFor="let parc of subParcellations">
 
           <tile-cmp *ngIf="parc"
-            class="iv-custom-comp text"
+            class="sxplr-custom-cmp text"
             [tile-text]="parc.name"
             [tile-image-src]="parc | previewParcellationUrl"
             [tile-selected]="selected"
diff --git a/src/atlasComponents/sapiViews/core/region/region/chip/region.chip.template.html b/src/atlasComponents/sapiViews/core/region/region/chip/region.chip.template.html
index 03db372cf5c31f39529a87d07bf1efa8c4bdb9c6..9b8262ee0441e16ce49b34d4a6d43ea9b60833b8 100644
--- a/src/atlasComponents/sapiViews/core/region/region/chip/region.chip.template.html
+++ b/src/atlasComponents/sapiViews/core/region/region/chip/region.chip.template.html
@@ -23,7 +23,7 @@
   }">
   <mat-chip
     (click)="onClick($event)"
-    class="iv-custom-comp text"
+    class="sxplr-custom-cmp text"
     [style.backgroundColor]="regionRgbString"
     >
     <ng-template [ngTemplateOutlet]="prefixTmpl"></ng-template>
diff --git a/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.template.html b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.template.html
index 23d94a6cf393179dd4c553d54d23dc90a4facf24..65d3667d2c67e7a5c2d3379fbc775a0bb038bd1d 100644
--- a/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.template.html
+++ b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.template.html
@@ -23,7 +23,7 @@
 
     <ng-template [ngTemplateOutlet]="headerTmpl"></ng-template>
 
-    <mat-card-title class="iv-custom-comp text">
+    <mat-card-title class="sxplr-custom-cmp text">
       {{ region.name }}
     </mat-card-title>
 
diff --git a/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.component.ts b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.component.ts
index b0daa753edfc2c14148e87249d01842be4e6d2d4..fa7fc0cd69178b883ed732dee3240b7e82738f85 100644
--- a/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.component.ts
+++ b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.component.ts
@@ -36,6 +36,9 @@ export class SapiViewsCoreRichRegionsHierarchy {
     )
   }
 
+  @Input('sxplr-sapiviews-core-rich-regionshierarchy-accent-regions')
+  accentedRegions: SapiRegionModel[] = []
+
   @Input('sxplr-sapiviews-core-rich-regionshierarchy-placeholder')
   placeholderText: string = 'Search all regions'
 
diff --git a/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.template.html b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.template.html
index 226c78741fcbe23ed773c903d88f0ed425816759..9c085e07cef508f4de0506fb740705c341616790 100644
--- a/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.template.html
+++ b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.template.html
@@ -1,4 +1,4 @@
-<form class="iv-custom-comp text sxplr-w-100">
+<form class="sxplr-custom-cmp text sxplr-w-100">
   <mat-form-field class="sxplr-w-100">
     <input
       [placeholder]="placeholderText"
@@ -18,6 +18,9 @@
 
 <ng-template #tmplRef let-region>
   <div class="mat-body sxplr-d-flex sxplr-align-items-center sxplr-h-100 region-tmpl"
+    [ngClass]="{
+      'sxplr-custom-cmp accent': accentedRegions | includes : region
+    }"
     [innerHTML]="region.name | hightlightPipe : searchTerm">
   </div>
 </ng-template>
@@ -27,5 +30,6 @@
   [sxplr-flat-hierarchy-is-parent]="isParent"
   [sxplr-flat-hierarchy-render-node-tmpl]="tmplRef"
   [sxplr-flat-hierarchy-tree-view-expand-on-init]="true"
+  sxplr-flat-hierarchy-tree-view-lineheight="24"
   (sxplr-flat-hierarchy-tree-view-node-clicked)="nodeClicked.emit($event)">
 </sxplr-flat-hierarchy-tree-view>
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.component.ts b/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.component.ts
index f2bb0d8b800b8177483f99eb291083a294e8d2de..58be89382e45d671cca1997a32e214c1bf7bb21c 100644
--- a/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.component.ts
+++ b/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.component.ts
@@ -70,10 +70,6 @@ export class SapiViewsCoreRichRegionListSearch {
     return region?.name || ''
   }
 
-  onInputFocus(){
-    this.regions[0].name.includes('left')
-  }
-
   optionSelected(opt: MatAutocompleteSelectedEvent) {
     const selectedRegion = opt.option.value as SapiRegionModel
     this.onOptionSelected.emit(selectedRegion)
diff --git a/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.template.html b/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.template.html
index 31aa64f230ce4bf3dbbb00201472a86006b6426e..9cdee5d47c0e7a92a5b3165054b2ad1cc4184678 100644
--- a/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.template.html
+++ b/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.template.html
@@ -1,4 +1,4 @@
-<form class="iv-custom-comp text sxplr-w-100">
+<form class="sxplr-custom-cmp text sxplr-w-100">
   <mat-form-field
     class="sxplr-w-100"
     floatLabel="never">
@@ -7,7 +7,6 @@
       [value]="currentSearch"
       #trigger="matAutocompleteTrigger"
       type="text"
-      (focus)="onInputFocus()"
       matInput
       name="searchTerm"
       [attr.aria-label]="ARIA_LABELS.TEXT_INPUT_SEARCH_REGION"
diff --git a/src/atlasComponents/userAnnotations/tools/line/line.template.html b/src/atlasComponents/userAnnotations/tools/line/line.template.html
index e0b3b06a78c4ebb58e26e4d9254f6c59e74a2ca3..3f72bd1d7a590f34b3f20636a44a7ff46f9ce2e2 100644
--- a/src/atlasComponents/userAnnotations/tools/line/line.template.html
+++ b/src/atlasComponents/userAnnotations/tools/line/line.template.html
@@ -36,10 +36,10 @@
 </div>
 
 <mat-menu #exportMenu="matMenu" xPosition="before">
-  <div class="iv-custom-comp card text"
+  <div class="sxplr-custom-cmp card text"
     iav-stop="click">
 
-    <div class="iv-custom-comp text">
+    <div class="sxplr-custom-cmp text">
       <textarea-copy-export
         [textarea-copy-export-label]="useFormat"
         [textarea-copy-export-text]="updateAnnotation.updateSignal$ | async | toFormattedStringPipe : updateAnnotation : useFormat"
diff --git a/src/atlasComponents/userAnnotations/tools/point/point.template.html b/src/atlasComponents/userAnnotations/tools/point/point.template.html
index b1b5ab285e11bc2926f3a00d4b8b0b39b4ab0579..805ab08767a59c9f202ae053b814150678bef881 100644
--- a/src/atlasComponents/userAnnotations/tools/point/point.template.html
+++ b/src/atlasComponents/userAnnotations/tools/point/point.template.html
@@ -35,10 +35,10 @@
 </div>
 
 <mat-menu #exportMenu="matMenu" xPosition="before">
-  <div class="iv-custom-comp card text"
+  <div class="sxplr-custom-cmp card text"
     iav-stop="click">
 
-    <div class="iv-custom-comp text">
+    <div class="sxplr-custom-cmp text">
 
       <textarea-copy-export
         [textarea-copy-export-label]="useFormat"
diff --git a/src/atlasComponents/userAnnotations/tools/poly/poly.template.html b/src/atlasComponents/userAnnotations/tools/poly/poly.template.html
index ea8665437974b0c1d29b31de294b72ed426f7095..6827915b80ecfb04504184c6519b9670cfa62cf0 100644
--- a/src/atlasComponents/userAnnotations/tools/poly/poly.template.html
+++ b/src/atlasComponents/userAnnotations/tools/poly/poly.template.html
@@ -36,10 +36,10 @@
 </div>
 
 <mat-menu #exportMenu="matMenu" xPosition="before">
-  <div class="iv-custom-comp card text"
+  <div class="sxplr-custom-cmp card text"
     iav-stop="click">
 
-    <div class="iv-custom-comp text">
+    <div class="sxplr-custom-cmp text">
       <textarea-copy-export
         [textarea-copy-export-label]="useFormat"
         [textarea-copy-export-text]="updateAnnotation.updateSignal$ | async | toFormattedStringPipe : updateAnnotation : useFormat"
diff --git a/src/components/flatHierarchy/treeView/treeView.component.ts b/src/components/flatHierarchy/treeView/treeView.component.ts
index 18520093110b0f00809fc7e8ddd0c9282a8f9a5d..cab03376d07031cf42a1f2bc137edf8ef5c9a912 100644
--- a/src/components/flatHierarchy/treeView/treeView.component.ts
+++ b/src/components/flatHierarchy/treeView/treeView.component.ts
@@ -15,7 +15,7 @@ import { TreeNode } from "../const"
 
 export class SxplrFlatHierarchyTreeView<T extends object> implements OnChanges{
   @HostBinding('class')
-  class = 'iv-custom-comp'
+  class = 'sxplr-custom-cmp'
 
   @Input('sxplr-flat-hierarchy-nodes')
   nodes: T[] = []
diff --git a/src/components/tile/tile.template.html b/src/components/tile/tile.template.html
index d56ad788ba2b1bc0043f19df11f44e51c111f23d..1187193efd379373fabdb461f9f4883251fb5e41 100644
--- a/src/components/tile/tile.template.html
+++ b/src/components/tile/tile.template.html
@@ -9,7 +9,7 @@
         backgroundColor: darktheme ? 'white' : 'black',
         color: darktheme ? 'black': 'white'
       }"
-      class="mat-elevation-z2 iv-custom-comp info-button">
+      class="mat-elevation-z2 sxplr-custom-cmp info-button">
       <small>
         <i class="fas fa-info"></i>
       </small>
@@ -23,7 +23,7 @@
       'darktheme': darktheme,
       'lighttheme': !darktheme
     }">
-    <i class="fas fa-folder folder-container fa-2x iv-custom-comp text"></i>
+    <i class="fas fa-folder folder-container fa-2x sxplr-custom-cmp text"></i>
   </div>
 
   <img [src]="tileImgSrc"
diff --git a/src/components/vButton/vButton.component.ts b/src/components/vButton/vButton.component.ts
index f2720905b3ff3bb9ff75a5b01d9add65643b552e..3dbd98aa23dc24da26dfc564e52859cdd99ea5b7 100644
--- a/src/components/vButton/vButton.component.ts
+++ b/src/components/vButton/vButton.component.ts
@@ -19,7 +19,7 @@ export class IAVVerticalButton{
   private color$ = new Subject<TIVBColor>()
   public class$ = this.color$.pipe(
     startWith('default'),
-    map(colorCls => `d-flex flex-column align-items-center iv-custom-comp ${colorCls} h-100`)
+    map(colorCls => `d-flex flex-column align-items-center sxplr-custom-cmp ${colorCls} h-100`)
   )
 
   @Input()
diff --git a/src/overwrite.scss b/src/overwrite.scss
index 82efbd6c0103fcdb55177e984cc353f3b8b2a4fe..2a2e2468ea041d89ff6f1c2ef81d8de29c55adb6 100644
--- a/src/overwrite.scss
+++ b/src/overwrite.scss
@@ -244,3 +244,16 @@ $flex-wrap-vars: nowrap, wrap, wrap-reverse;
     flex-wrap: $flex-wrap-var;
   }
 }
+$flex-directions: row,column;
+@each $flex-direction in $flex-directions {
+  .#{$nsp}-flex-#{$flex-direction} {
+    flex-direction: $flex-direction;
+  }
+}
+.#{$nsp}-flex-var {
+  flex: 1 1 0px;
+}
+
+.#{$nsp}-flex-static {
+  flex: 0 0 auto;
+}
\ No newline at end of file
diff --git a/src/screenshot/screenshotCmp/screenshot.template.html b/src/screenshot/screenshotCmp/screenshot.template.html
index ad7143b67c6c49b75f8341450b70633e997149eb..ee570082170d036ee4429f425ce4f0d112255d79 100644
--- a/src/screenshot/screenshotCmp/screenshot.template.html
+++ b/src/screenshot/screenshotCmp/screenshot.template.html
@@ -2,7 +2,7 @@
 <ng-template #placeholderTmpl>
 
   <div class="d-flex align-items-center justify-content-center w-100 h-100 cover">
-    <span class="iv-custom-comp text">
+    <span class="sxplr-custom-cmp text">
       <h2 class="mat-h2 text-center">
         <span>
           Drag a box to take a screenshot or
diff --git a/src/sharedModules/angularMaterial.module.ts b/src/sharedModules/angularMaterial.module.ts
index b2d27d67a09a1ff277802b4318acec9e54281784..3c0a913f69beda1a06f24535f4326272e48eb519 100644
--- a/src/sharedModules/angularMaterial.module.ts
+++ b/src/sharedModules/angularMaterial.module.ts
@@ -97,7 +97,6 @@ const defaultDialogOption: MatDialogConfig = new MatDialogConfig()
     provide: MAT_DIALOG_DEFAULT_OPTIONS,
     useValue: {
       ...defaultDialogOption,
-      panelClass: 'iav-dialog-class',
     },
   }],
 })
diff --git a/src/state/atlasAppearance/const.ts b/src/state/atlasAppearance/const.ts
index 17ebe814184485cbdc24dd05553f68c8397c1cf1..c6c24f31b6ffc7e8bebf9647f9b0b4f538c0f710 100644
--- a/src/state/atlasAppearance/const.ts
+++ b/src/state/atlasAppearance/const.ts
@@ -1,4 +1,4 @@
-import { SAPIRegion } from "src/atlasComponents/sapi/core"
+import { SapiRegionModel } from "src/atlasComponents/sapi"
 export const nameSpace = `[state.atlasAppearance]`
 
 type CustomLayerBase = {
@@ -7,7 +7,20 @@ type CustomLayerBase = {
 
 export type ColorMapCustomLayer = {
   clType: 'customlayer/colormap' | 'baselayer/colormap'
-  colormap: WeakMap<SAPIRegion, number[]>
+  colormap: WeakMap<SapiRegionModel, number[]>
+} & CustomLayerBase
+
+export type ThreeSurferCustomLayer = {
+  clType: 'baselayer/threesurfer'
+  source: string
+  laterality: 'left' | 'right'
+  name: string
+} & CustomLayerBase
+
+export type ThreeSurferCustomLabelLayer = {
+  clType: 'baselayer/threesurfer-label'
+  source: string
+  laterality: 'left' | 'right'
 } & CustomLayerBase
 
 export type NgLayerCustomLayer = {
@@ -36,4 +49,4 @@ export type NgLayerCustomLayer = {
  * - clType facilitates viewer on how to interprete the custom layer
  * - id allows custom layer to be removed, if necessary
  */
-export type CustomLayer = ColorMapCustomLayer | NgLayerCustomLayer
+export type CustomLayer = ColorMapCustomLayer | NgLayerCustomLayer | ThreeSurferCustomLayer | ThreeSurferCustomLabelLayer
diff --git a/src/state/atlasSelection/actions.ts b/src/state/atlasSelection/actions.ts
index 14504de34a7a5afbf92da526fb356c79d706a779..8e8ecf2e80f0d6724a4a13a3fe2c2ff9045cbcc0 100644
--- a/src/state/atlasSelection/actions.ts
+++ b/src/state/atlasSelection/actions.ts
@@ -30,8 +30,15 @@ export const setSelectedParcellationAllRegions = createAction(
   }>()
 )
 
-export const selectRegions = createAction(
-  `${nameSpace} selectRegions`,
+export const selectRegion = createAction(
+  `${nameSpace} selectRegion`,
+  props<{
+    region: SapiRegionModel
+  }>()
+)
+
+export const setSelectedRegions = createAction(
+  `${nameSpace} setSelectedRegions`,
   props<{
     regions: SapiRegionModel[]
   }>()
diff --git a/src/state/atlasSelection/effects.ts b/src/state/atlasSelection/effects.ts
index a4b4800eb8561038cba0ddfdcb61e7a5115e0fc4..7620569b11e05d28463d7ee80f4bac482492cdb5 100644
--- a/src/state/atlasSelection/effects.ts
+++ b/src/state/atlasSelection/effects.ts
@@ -1,14 +1,15 @@
 import { Injectable } from "@angular/core";
 import { Actions, createEffect, ofType } from "@ngrx/effects";
-import { forkJoin, from, merge, of } from "rxjs";
+import { forkJoin, merge, of } from "rxjs";
 import { filter, map, mapTo, switchMap, switchMapTo, withLatestFrom } from "rxjs/operators";
-import { SAPI } from "src/atlasComponents/sapi";
+import { SAPI, SAPIRegion, SapiRegionModel } from "src/atlasComponents/sapi";
 import * as mainActions from "../actions"
 import { select, Store } from "@ngrx/store";
 import { selectors, actions } from '.'
 import { fromRootStore } from "./util";
 import { ParcellationIsBaseLayer } from "src/atlasComponents/sapiViews/core/parcellation/parcellationIsBaseLayer.pipe";
 import { OrderParcellationByVersionPipe } from "src/atlasComponents/sapiViews/core/parcellation/parcellationVersion.pipe";
+import { atlasAppearance } from "..";
 
 @Injectable()
 export class Effect {
@@ -62,6 +63,38 @@ export class Effect {
     )
   ))
 
+  onATPSelectionClearBaseLayerColorMap = createEffect(() => this.store.pipe(
+    select(selectors.selectedParcAllRegions),
+    withLatestFrom(
+      this.store.pipe(
+        select(atlasAppearance.selectors.customLayers),
+        map(layers => layers.filter(l => l.clType === "baselayer/colormap"))
+      )
+    ),
+    switchMap(([regions, layers]) => {
+      const map = new WeakMap<SapiRegionModel, number[]>()
+      for (const region of regions) {
+        map.set(region, SAPIRegion.GetDisplayColor(region))
+      }
+      const actions = [
+        ...layers.map(({ id }) =>
+          atlasAppearance.actions.removeCustomLayer({
+            id
+          })
+        ),
+        atlasAppearance.actions.addCustomLayer({
+          customLayer: {
+            clType: "baselayer/colormap",
+            id: 'base-colormap-id',
+            colormap: map
+          }
+        })
+      ]
+      return of(...actions)
+    })
+  ))
+
+
   onAtlasSelClearStandAloneVolumes = createEffect(() => this.action.pipe(
     ofType(actions.selectAtlas),
     mapTo(actions.setStandAloneVolumes({
@@ -71,7 +104,7 @@ export class Effect {
 
   onClearRegion = createEffect(() => this.action.pipe(
     ofType(actions.clearSelectedRegions),
-    mapTo(actions.selectRegions({
+    mapTo(actions.setSelectedRegions({
       regions: []
     }))
   ))
@@ -146,7 +179,7 @@ export class Effect {
   ).pipe(
     switchMapTo(
       of(
-        actions.selectRegions({
+        actions.setSelectedRegions({
           regions: []
         }),
         actions.setSelectedParcellationAllRegions({
@@ -166,7 +199,7 @@ export class Effect {
     map(([ { region }, regions ]) => {
       const selectedRegionsIndicies = regions.map(r => r["@id"])
       const roiIndex = selectedRegionsIndicies.indexOf(region["@id"])
-      return actions.selectRegions({
+      return actions.setSelectedRegions({
         regions: roiIndex >= 0
           ? [...regions.slice(0, roiIndex), ...regions.slice(roiIndex + 1)]
           : [...regions, region]
diff --git a/src/state/atlasSelection/store.ts b/src/state/atlasSelection/store.ts
index 7332711b69be6094ca9708e0945f958627921b6f..d051a5475cbd806ae2a7a8535ce7e0878926f33f 100644
--- a/src/state/atlasSelection/store.ts
+++ b/src/state/atlasSelection/store.ts
@@ -79,7 +79,31 @@ const reducer = createReducer(
     }
   ),
   on(
-    actions.selectRegions,
+    actions.selectRegion,
+    (state, { region }) => {
+      /**
+       * if roi does not have visualizedIn defined
+       * or internal identifier
+       * 
+       * ignore
+       */
+      if (
+        !region.hasAnnotation?.visualizedIn
+        && region.hasAnnotation?.internalIdentifier === 'unknown'
+      ) {
+        return { ...state }
+      }
+      const selected = state.selectedRegions.includes(region)
+      return {
+        ...state,
+        selectedRegions: selected
+          ? [ ]
+          : [ region ]
+      }
+    }
+  ),
+  on(
+    actions.setSelectedRegions,
     (state, { regions }) => {
       return {
         ...state,
diff --git a/src/theme.scss b/src/theme.scss
index c7998b3816da0494de2e33fad025ad564e9b7cf9..6f0dcf85a110b9d810a7fd9f681f0c791f059783 100644
--- a/src/theme.scss
+++ b/src/theme.scss
@@ -13,8 +13,8 @@
   $accent: map-get($color-config, accent);
   $warn: map-get($color-config, warn);
 
-  [iv-custom-comp],
-  .iv-custom-comp
+  [sxplr-custom-cmp],
+  .sxplr-custom-cmp
   {
     color: mat.get-color-from-palette($foreground, text);
     
diff --git a/src/ui/config/configCmp/config.template.html b/src/ui/config/configCmp/config.template.html
index 1972951cd1030eda31471e9b19aab33d95f0ff2a..f00d4f15fdb9883202a489521931b8d87bfd9457 100644
--- a/src/ui/config/configCmp/config.template.html
+++ b/src/ui/config/configCmp/config.template.html
@@ -59,7 +59,7 @@
   <!-- viewer preference -->
   <mat-tab *ngIf="experimentalFlag" label="Viewer Preference">
 
-    <div class="iv-custom-comp text sxplr-m-2">
+    <div class="sxplr-custom-cmp text sxplr-m-2">
       <div class="mat-h2">
         Rearrange Viewports
       </div>
@@ -133,14 +133,14 @@
         </div>
       </current-layout>
 
-      <div class="iv-custom-comp text text-muted font-italic">
+      <div class="sxplr-custom-cmp text text-muted font-italic">
         Plane designation refers to default orientation (without oblique rotation).
       </div>
     </div>
 
     <!-- scroll window -->
 
-    <div class="sxplr-m-2 iv-custom-comp text">
+    <div class="sxplr-m-2 sxplr-custom-cmp text">
       <div class="mat-h2">
         Select a viewports configuration
       </div>
diff --git a/src/util/array.ts b/src/util/array.ts
index b8484db0a12933020fba0fbfb3519f26e0896dc7..2c9cfeea640639773c0249db3f64406a6a67dac4 100644
--- a/src/util/array.ts
+++ b/src/util/array.ts
@@ -1,11 +1,13 @@
 const defaultCmFn = <T>(o: T, n: T) => o === n
-export function arrayEqual<T>(array1: T[], array2: T[], compareFn: (o1: T, o2: T) => boolean = defaultCmFn, order = false) {
-  if (order) {
-    for (const idx in array1) {
-      if (!compareFn(array1[idx], array2[idx])) return false
+export function arrayEqual<T>(compareFn: (o1: T, o2: T) => boolean = defaultCmFn, order = false) {
+  return function(array1: T[], array2: T[]){
+    if (order) {
+      for (const idx in array1) {
+        if (!compareFn(array1[idx], array2[idx])) return false
+      }
+      return true
     }
-    return true
+    return !!array1.every(it1 => array2.find(it2 => compareFn(it1, it2)))
+      && !!array2.every(it2 => array1.find(it1 => compareFn(it1, it2)))
   }
-  return !!array1.every(it1 => array2.find(it2 => compareFn(it1, it2)))
-    && !!array2.every(it2 => array1.find(it1 => compareFn(it1, it2)))
 }
diff --git a/src/viewerModule/nehuba/config.service/util.ts b/src/viewerModule/nehuba/config.service/util.ts
index a82cce3aa19f61b13e3ae2a4a8402c2ccab6cd7a..2e6533f6b3d2c9e881817ee3a3b032450b41453c 100644
--- a/src/viewerModule/nehuba/config.service/util.ts
+++ b/src/viewerModule/nehuba/config.service/util.ts
@@ -167,6 +167,7 @@ const BACKCOMAP_KEY_DICT = {
 export function getTmplNgLayer(atlas: SapiAtlasModel, tmpl: SapiSpaceModel, spaceVolumes: SapiVolumeModel[]): Record<string, NgLayerSpec>{
   const ngId = `_${MultiDimMap.GetKey(atlas["@id"], tmpl["@id"], "tmplImage")}`
   const tmplImage = spaceVolumes.find(v => "neuroglancer/precomputed" in v.data.detail)
+  if (!tmplImage) return {}
   return {
     [ngId]: {
       source: `precomputed://${tmplImage.data.url.replace(/^precomputed:\/\//, '')}`,
@@ -238,24 +239,26 @@ export const getNgLayersFromVolumesATP = (volumes: CongregatedVolume, ATP: { atl
 }
 
 export const fromRootStore = {
-  getNgLayers: (store: Store, sapiSvc: SAPI) => pipe(
-    select(atlasSelection.selectors.selectedATP),
-    switchMap(ATP => 
-      forkJoin({
-        tmplVolumes: store.pipe(
-          nehubaStoreFromRootStore.getTmplVolumes(sapiSvc),
-        ),
-        tmplAuxMeshVolumes: store.pipe(
-          nehubaStoreFromRootStore.getAuxMeshVolumes(sapiSvc),
-        ),
-        parcVolumes: store.pipe(
-          nehubaStoreFromRootStore.getParcVolumes(sapiSvc),
+  getNgLayers: (store: Store, sapiSvc: SAPI) => {
+    return pipe(
+      select(atlasSelection.selectors.selectedATP),
+      switchMap(ATP =>
+        forkJoin({
+          tmplVolumes: store.pipe(
+            nehubaStoreFromRootStore.getTmplVolumes(sapiSvc),
+          ),
+          tmplAuxMeshVolumes: store.pipe(
+            nehubaStoreFromRootStore.getAuxMeshVolumes(sapiSvc),
+          ),
+          parcVolumes: store.pipe(
+            nehubaStoreFromRootStore.getParcVolumes(sapiSvc),
+          )
+        }).pipe(
+          map(volumes => getNgLayersFromVolumesATP(volumes, ATP))
         )
-      }).pipe(
-        map(volumes => getNgLayersFromVolumesATP(volumes, ATP))
       )
     )
-  )
+  }
 }
 
 export function getRegionLabelIndex(atlas: SapiAtlasModel, tmpl: SapiSpaceModel, parc: SapiParcellationModel, region: SapiRegionModel) {
diff --git a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts
index d223772c926d95266ad5f985c8fd0ed6b07f891e..dfcb5ab6eb576f41d99931208ad9a5cabd7a82dd 100644
--- a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts
+++ b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts
@@ -356,7 +356,7 @@ export class NehubaGlueCmp implements IViewer<'nehuba'>, OnDestroy, AfterViewIni
         select(atlasAppearance.selectors.customLayers),
         debounceTime(16),
         map(cl => cl.filter(l => l.clType === "baselayer/nglayer") as NgLayerCustomLayer[]),
-        distinctUntilChanged((o, n) => arrayEqual(o, n, (oi, ni) => oi.id === ni.id)),
+        distinctUntilChanged(arrayEqual((oi, ni) => oi.id === ni.id)),
         filter(layers => layers.length > 0),
         map(ngBaseLayers => {
           return {
@@ -633,17 +633,17 @@ export class NehubaGlueCmp implements IViewer<'nehuba'>, OnDestroy, AfterViewIni
           return newMainMap
         },
         applyLayersColourMap: (map) => {
-          if (!this.layerCtrlService) {
-            throw new Error(`layerCtrlService not injected. Cannot call getLayersSegmentColourMap`)
-          }
-          const obj: IColorMap = {}
-          for (const [ key, value ] of map.entries()) {
-            const cmap = obj[key] = {}
-            for (const [ labelIdx, rgb ] of value.entries()) {
-              cmap[Number(labelIdx)] = rgb
-            }
-          }
-          this.layerCtrlService.overwriteColorMap$.next(obj)
+          // if (!this.layerCtrlService) {
+          //   throw new Error(`layerCtrlService not injected. Cannot call getLayersSegmentColourMap`)
+          // }
+          // const obj: IColorMap = {}
+          // for (const [ key, value ] of map.entries()) {
+          //   const cmap = obj[key] = {}
+          //   for (const [ labelIdx, rgb ] of value.entries()) {
+          //     cmap[Number(labelIdx)] = rgb
+          //   }
+          // }
+          // this.layerCtrlService.overwriteColorMap$.next(obj)
         },
         /**
          * TODO go via layerCtrl.service
@@ -716,8 +716,8 @@ export class NehubaGlueCmp implements IViewer<'nehuba'>, OnDestroy, AfterViewIni
     const trueOnhoverSegments = this.onhoverSegments && this.onhoverSegments.filter(v => typeof v === 'object')
     if (!trueOnhoverSegments || (trueOnhoverSegments.length === 0)) return true
     this.store$.dispatch(
-      atlasSelection.actions.selectRegions({
-        regions: trueOnhoverSegments.slice(0, 1)
+      atlasSelection.actions.selectRegion({
+        region: trueOnhoverSegments[0]
       })
     )
     return true
diff --git a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.template.html b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.template.html
index d2ec2c4e46889fe840f41e3764a976a48a132a04..575f8d188b05db891b340e321e83ef89341c93cf 100644
--- a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.template.html
+++ b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.template.html
@@ -219,7 +219,7 @@
     </button>
 
     <div *ngIf="data.moreInfoFlag"
-      class="iv-custom-comp darker-bg overflow-hidden grid-wide-3">
+      class="sxplr-custom-cmp darker-bg overflow-hidden grid-wide-3">
       <ng-layer-tune
         advanced-control="true"
         [ngLayerName]="data.layerName"
diff --git a/src/viewerModule/nehuba/statusCard/statusCard.template.html b/src/viewerModule/nehuba/statusCard/statusCard.template.html
index 8f0c517ee63e00f7509403509cfa187489e82cbe..026a49446b2d44ba2d24addbeb80419c4fd34e2a 100644
--- a/src/viewerModule/nehuba/statusCard/statusCard.template.html
+++ b/src/viewerModule/nehuba/statusCard/statusCard.template.html
@@ -106,7 +106,7 @@
 
 <!-- minimised status bar -->
 <ng-template #showMin>
-  <div class="iv-custom-comp text of-visible text-nowrap d-inline-flex align-items-center m-1 mt-3"
+  <div class="sxplr-custom-cmp text of-visible text-nowrap d-inline-flex align-items-center m-1 mt-3"
     iav-media-query
     #media="iavMediaQuery">
 
diff --git a/src/viewerModule/nehuba/viewerCtrl/change-perspective-orientation/changePerspectiveOrientation.component.html b/src/viewerModule/nehuba/viewerCtrl/change-perspective-orientation/changePerspectiveOrientation.component.html
index c194ea9659faa473dbf30de56ace6b5e6e83901d..4aa94fa5d1bce357ba7caba1c6a1b3f901cdc30c 100644
--- a/src/viewerModule/nehuba/viewerCtrl/change-perspective-orientation/changePerspectiveOrientation.component.html
+++ b/src/viewerModule/nehuba/viewerCtrl/change-perspective-orientation/changePerspectiveOrientation.component.html
@@ -4,7 +4,7 @@
 
 <mat-menu #perspectiveOrientationMenu="matMenu">
 
-    <div class="d-flex align-items-center iv-custom-comp text">
+    <div class="d-flex align-items-center sxplr-custom-cmp text">
         <button mat-button color="basic" class="flex-grow-1 text-left font-weight-normal"
                 (click)="set3DViewPoint('coronal', 'first')">
             Coronal view
@@ -15,7 +15,7 @@
         </button>
     </div>
 
-    <div class="d-flex align-items-center iv-custom-comp text"> <!--mat-menu-item-->
+    <div class="d-flex align-items-center sxplr-custom-cmp text"> <!--mat-menu-item-->
         <button mat-button color="basic" class="flex-grow-1 text-left font-weight-normal"
                 (click)="set3DViewPoint('sagittal', 'first')">
             Sagittal view
@@ -26,7 +26,7 @@
         </button>
     </div>
 
-    <div class="d-flex align-items-center iv-custom-comp text"> <!--mat-menu-item-->
+    <div class="d-flex align-items-center sxplr-custom-cmp text"> <!--mat-menu-item-->
         <button mat-button color="basic" class="flex-grow-1 text-left font-weight-normal"
                 (click)="set3DViewPoint('axial', 'first')">
             Axial view
diff --git a/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.template.html b/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.template.html
index f029408a905a925885518c0c834ec34ad77362c7..149666c1c40bdcab574133178809f45fb885897e 100644
--- a/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.template.html
+++ b/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.template.html
@@ -1,13 +1,13 @@
 <mat-divider class="mt-2 mb-2"></mat-divider>
 
-<h3 class="iv-custom-comp text mat-h3">
+<h3 class="sxplr-custom-cmp text mat-h3">
   Perspective View
 </h3>
 
 <mat-slide-toggle [(ngModel)]="removeOctantFlag"
   [aria-label]="ARIA_LABELS.TOGGLE_FRONTAL_OCTANT"
   name="remove-frontal-octant">
-  <span class="iv-custom-comp text">
+  <span class="sxplr-custom-cmp text">
     Remove frontal octant
   </span>
 </mat-slide-toggle>
@@ -19,7 +19,7 @@
       [formControlName]="auxMesh['@id']"
       class="d-block"
       [name]="'toggle-aux-mesh-' + auxMesh['@id']">
-      <span class="iv-custom-comp text">
+      <span class="sxplr-custom-cmp text">
         {{ auxMesh.displayName || auxMesh.name }}
       </span>
     </mat-slide-toggle>
diff --git a/src/viewerModule/threeSurfer/module.ts b/src/viewerModule/threeSurfer/module.ts
index 21f11580befb07553f6f613b3b0f86fa2366e8d6..7a1f1c3e3c908f64451cebc7db7e513d7a1f9e40 100644
--- a/src/viewerModule/threeSurfer/module.ts
+++ b/src/viewerModule/threeSurfer/module.ts
@@ -1,11 +1,14 @@
 import { CommonModule } from "@angular/common";
 import { NgModule } from "@angular/core";
 import { FormsModule } from "@angular/forms";
+import { StoreModule } from "@ngrx/store";
 import { ComponentsModule } from "src/components";
 import { AngularMaterialModule } from "src/sharedModules";
 import { UtilModule } from "src/util";
 import { ThreeSurferGlueCmp } from "./threeSurferGlue/threeSurfer.component";
 import { ThreeSurferViewerConfig } from "./tsViewerConfig/tsViewerConfig.component";
+import { nameSpace, reducer, ThreeSurferEffects } from "./store"
+import { EffectsModule } from "@ngrx/effects";
 
 @NgModule({
   imports: [
@@ -14,6 +17,13 @@ import { ThreeSurferViewerConfig } from "./tsViewerConfig/tsViewerConfig.compone
     UtilModule,
     FormsModule,
     ComponentsModule,
+    StoreModule.forFeature(
+      nameSpace,
+      reducer
+    ),
+    EffectsModule.forFeature([
+      ThreeSurferEffects,
+    ])
   ],
   declarations: [
     ThreeSurferGlueCmp,
diff --git a/src/viewerModule/threeSurfer/store/actions.ts b/src/viewerModule/threeSurfer/store/actions.ts
new file mode 100644
index 0000000000000000000000000000000000000000..613fcbbfc7b943dfc412717987314a2b34fb5fb6
--- /dev/null
+++ b/src/viewerModule/threeSurfer/store/actions.ts
@@ -0,0 +1,9 @@
+import { createAction, props } from "@ngrx/store";
+import { nameSpace } from "./const"
+
+export const selectVolumeById = createAction(
+  `${nameSpace} selectVolumeById`,
+  props<{
+    id: string
+  }>()
+)
diff --git a/src/viewerModule/threeSurfer/store/const.ts b/src/viewerModule/threeSurfer/store/const.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5643484e1b0628d986eb70dec222903a1fbfe6cd
--- /dev/null
+++ b/src/viewerModule/threeSurfer/store/const.ts
@@ -0,0 +1 @@
+export const nameSpace = `[threeSurfer]`
\ No newline at end of file
diff --git a/src/viewerModule/threeSurfer/store/effects.ts b/src/viewerModule/threeSurfer/store/effects.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9357427c2054109e8978e1d99d79f18faf489120
--- /dev/null
+++ b/src/viewerModule/threeSurfer/store/effects.ts
@@ -0,0 +1,154 @@
+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 { debounceTime, map, switchMap, withLatestFrom, filter, take, shareReplay, tap, distinctUntilChanged } from "rxjs/operators";
+import { SAPI, SapiAtlasModel, SapiParcellationModel, SapiSpaceModel } from "src/atlasComponents/sapi";
+import { atlasAppearance, atlasSelection } from "src/state";
+import { ThreeSurferCustomLabelLayer, ThreeSurferCustomLayer } from "src/state/atlasAppearance/const";
+import * as selectors from "./selectors"
+import * as actions from "./actions"
+
+export const fromATP = {
+  getThreeSurfaces: (sapi: SAPI) => {
+    return pipe(
+      filter(
+        ({ atlas, template, parcellation }: {atlas: SapiAtlasModel, template: SapiSpaceModel, parcellation: SapiParcellationModel}) => !!atlas && !!template && !!parcellation
+      ),
+      switchMap(({ atlas, template, parcellation }: {atlas: SapiAtlasModel, template: SapiSpaceModel, parcellation: SapiParcellationModel}) => 
+        forkJoin({
+          surfaces: sapi.getSpace(atlas["@id"], template["@id"])
+            .getVolumes()
+            .then(volumes => volumes.filter(vol => vol.data.type === "gii")),
+          labels: sapi.getParcellation(atlas["@id"], parcellation["@id"])
+            .getVolumes()
+            .then(volumes => 
+              volumes.filter(vol =>
+                vol.data.type === "gii-label" &&
+                vol.data.space["@id"] === template["@id"]
+              )
+            )
+        })
+      )
+    )
+  }
+}
+
+@Injectable()
+export class ThreeSurferEffects {
+
+  private onATP$ = this.store.pipe(
+    select(atlasSelection.selectors.selectedATP)
+  )
+
+  private selectedSurfaceId$ = this.store.pipe(
+    select(selectors.getSelectedVolumeId),
+    distinctUntilChanged()
+  )
+
+  private threeSurferBaseCustomLayers$: Observable<ThreeSurferCustomLayer[]> = this.store.pipe(
+    select(atlasAppearance.selectors.customLayers),
+    map(
+      cl => cl.filter(layer => layer.clType === "baselayer/threesurfer") as ThreeSurferCustomLayer[]
+    )
+  )
+
+  onATPClearBaseLayers = createEffect(() => merge(
+    this.onATP$,
+    this.selectedSurfaceId$,
+  ).pipe(
+    withLatestFrom(
+      this.threeSurferBaseCustomLayers$
+    ),
+    switchMap(([_, layers]) => 
+      of(
+        ...layers.map(layer => 
+          atlasAppearance.actions.removeCustomLayer({
+            id: layer.id
+          })  
+        )
+      )
+    )
+  ))
+
+  public onATPDebounceThreeSurferLayers$ = this.onATP$.pipe(
+    debounceTime(16),
+    fromATP.getThreeSurfaces(this.sapi),
+    shareReplay(1),
+  )
+
+  onATPDebounceHasSurfaceVolumes = createEffect(() => this.onATPDebounceThreeSurferLayers$.pipe(
+    switchMap(({ surfaces }) => {
+      const defaultSurface = surfaces.find(s => s.metadata.shortName === "pial") || surfaces[0]
+      if (!defaultSurface) return EMPTY
+      return of(
+        actions.selectVolumeById({
+          id: defaultSurface["@id"]
+        })
+      )
+    })
+  ))
+
+  onSurfaceSelected = createEffect(() => this.selectedSurfaceId$.pipe(
+    switchMap(id => this.onATPDebounceThreeSurferLayers$.pipe(
+      switchMap(({ surfaces }) => {
+        if (surfaces.length === 0) return EMPTY
+  
+        const layers: ThreeSurferCustomLayer[] = []
+        /**
+         * select the pial or first one by default
+         */
+        const selectedSrc = surfaces.find(s => s["@id"] === id)
+  
+        if (!(selectedSrc.data?.url_map)) {
+          return throwError(`Expecting surfaces[0].data.url_map to be defined, but is not.`)
+        }
+  
+        for (const key in selectedSrc.data.url_map) {
+          layers.push({
+            clType: 'baselayer/threesurfer',
+            id: `${selectedSrc["@id"]}-${key}`,
+            name: `${selectedSrc["@id"]}-${key}`,
+            laterality: key as 'left' | 'right',
+            source: selectedSrc.data.url_map[key]
+          })
+        }
+        return of(...[
+          ...layers.map(customLayer => 
+            atlasAppearance.actions.addCustomLayer({
+              customLayer
+            })
+          )
+        ])
+      })
+    ))
+  ))
+
+  onATPDebounceAddBaseLayers$ = createEffect(() => this.onATPDebounceThreeSurferLayers$.pipe(
+    switchMap(({ labels }) => {
+      const labelMaps: ThreeSurferCustomLabelLayer[] = []
+      for (const label of labels) {
+        labelMaps.push({
+          clType: 'baselayer/threesurfer-label',
+          id: `${label["@id"]}-${label.metadata.shortName}`,
+          laterality: label.metadata.shortName as 'left' | 'right',
+          source: label.data.url
+        })
+      }
+      return of(
+        ...labelMaps.map(customLayer => 
+          atlasAppearance.actions.addCustomLayer({
+            customLayer
+          })  
+        )
+      )
+    })
+  ))
+
+  constructor(
+    private store: Store,
+    private sapi: SAPI,
+  ){
+
+  }
+}
\ No newline at end of file
diff --git a/src/viewerModule/threeSurfer/store/index.ts b/src/viewerModule/threeSurfer/store/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e84f687f54017d80038471ae103fa2f4131e3626
--- /dev/null
+++ b/src/viewerModule/threeSurfer/store/index.ts
@@ -0,0 +1,5 @@
+export { reducer, Store, defaultStore } from "./store"
+export * as actions from "./actions"
+export * as selectors from "./selectors"
+export { nameSpace } from "./const"
+export { ThreeSurferEffects } from "./effects"
\ No newline at end of file
diff --git a/src/viewerModule/threeSurfer/store/selectors.ts b/src/viewerModule/threeSurfer/store/selectors.ts
new file mode 100644
index 0000000000000000000000000000000000000000..82c32604a57a26efac7881f9345fdb6c38cf9c2c
--- /dev/null
+++ b/src/viewerModule/threeSurfer/store/selectors.ts
@@ -0,0 +1,10 @@
+import { createSelector } from "@ngrx/store"
+import { nameSpace } from "./const"
+import { Store } from "./store"
+
+const selectStore = state => state[nameSpace] as Store
+
+export const getSelectedVolumeId = createSelector(
+  selectStore,
+  ({ selectedVolumeId }) => selectedVolumeId
+)
diff --git a/src/viewerModule/threeSurfer/store/store.ts b/src/viewerModule/threeSurfer/store/store.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b830d4278e88bfea95ca9a5f0697fb68f7db2171
--- /dev/null
+++ b/src/viewerModule/threeSurfer/store/store.ts
@@ -0,0 +1,24 @@
+import { createReducer, on } from "@ngrx/store";
+import * as actions from "./actions"
+
+export type Store = {
+  selectedVolumeId: string
+}
+
+
+export const defaultStore: Store = {
+  selectedVolumeId: null
+}
+
+export const reducer = createReducer(
+  defaultStore,
+  on(
+    actions.selectVolumeById,
+    (state, { id }) => {
+      return {
+        ...state,
+        selectedVolumeId: id
+      }
+    }
+  )
+)
diff --git a/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts b/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts
index 0a62b9a08053b43328afd1300ae638d2c3c90e17..d433d11c79969e6f63bf3df71a23226f56749ff1 100644
--- a/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts
+++ b/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts
@@ -1,20 +1,22 @@
-import { Component, Input, Output, EventEmitter, ElementRef, OnChanges, OnDestroy, AfterViewInit, Inject, Optional } from "@angular/core";
+import { Component, Output, EventEmitter, ElementRef, OnChanges, OnDestroy, AfterViewInit, Inject, Optional, ChangeDetectionStrategy } from "@angular/core";
 import { EnumViewerEvt, IViewer, TViewerEvent } from "src/viewerModule/viewer.interface";
-import { TThreeSurferConfig, TThreeSurferMode } from "../types";
-import { parseContext } from "../util";
-import { retry, flattenRegions } from 'common/util'
-import { Observable, Subject } from "rxjs";
-import { debounceTime, filter, switchMap } from "rxjs/operators";
+import { combineLatest, Observable, Subject } from "rxjs";
+import { debounceTime, distinctUntilChanged, filter, map, shareReplay, tap } from "rxjs/operators";
 import { ComponentStore } from "src/viewerModule/componentStore";
 import { select, Store } from "@ngrx/store";
 import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR } from "src/util";
 import { MatSnackBar } from "@angular/material/snack-bar";
 import { CONST } from 'common/constants'
 import { API_SERVICE_SET_VIEWER_HANDLE_TOKEN, TSetViewerHandle } from "src/atlasViewer/atlasViewer.apiService.service";
-import { getUuid, switchMapWaitFor } from "src/util/fn";
+import { getUuid } from "src/util/fn";
 import { AUTO_ROTATE, TInteralStatePayload, ViewerInternalStateSvc } from "src/viewerModule/viewerInternalState.service";
-import { actions } from "src/state/atlasSelection";
-import { atlasSelection } from "src/state";
+import { atlasAppearance, atlasSelection } from "src/state";
+import { ThreeSurferCustomLabelLayer, ThreeSurferCustomLayer, ColorMapCustomLayer } from "src/state/atlasAppearance/const";
+import { SapiRegionModel, SapiVolumeModel } from "src/atlasComponents/sapi";
+import { getRegionLabelIndex } from "src/viewerModule/nehuba/config.service";
+import { arrayEqual } from "src/util/array";
+import { ThreeSurferEffects } from "../store/effects";
+import { selectors, actions  } from "../store"
 
 const viewerType = 'ThreeSurfer'
 type TInternalState = {
@@ -27,10 +29,10 @@ type TInternalState = {
   hemisphere: 'left' | 'right' | 'both'
 }
 const pZoomFactor = 5e3
-const preferredFsMode = 'pial'
 
 type THandlingCustomEv = {
-  regions: ({ name?: string, error?: string })[]
+  regions: SapiRegionModel[]
+  error?: string
   evMesh?: {
     faceIndex: number
     verticesIndicies: number[]
@@ -42,16 +44,27 @@ type TCameraOrientation = {
   perspectiveZoom: number
 }
 
-const threshold = 1e-3
-
-function getHemisphereKey(region: { name: string }){
-  return /left/.test(region.name)
-    ? 'left'
-    : /right/.test(region.name)
-      ? 'right'
-      : null
+type TThreeGeometry = {
+  visible: boolean
+}
+type GiiInstance = {}
+type TThreeSurfer = {
+  loadMesh: (url: string) => Promise<TThreeGeometry>
+  unloadMesh: (geom: TThreeGeometry) => void
+  redraw: (geom: TThreeGeometry) => void
+  applyColorMap: (geom: TThreeGeometry, idxMap?: number[], custom?: { usePreset?: any, custom?: Map<number, number[]> }) => void
+  loadColormap: (url: string) => Promise<GiiInstance>
+  setupAnimation: () => void
+  dispose: () => void
+  control: any
+  camera: any
+  customColormap: WeakMap<TThreeGeometry, any>
 }
 
+type LateralityRecord<T> = Record<string, T>
+
+const threshold = 1e-3
+
 function cameraNavsAreSimilar(c1: TCameraOrientation, c2: TCameraOrientation){
   if (c1 === c2) return true
   if (!!c1 && !!c2) return true
@@ -74,40 +87,116 @@ function cameraNavsAreSimilar(c1: TCameraOrientation, c2: TCameraOrientation){
   styleUrls: [
     './threeSurfer.style.css'
   ],
-  providers: [ ComponentStore ]
+  providers: [ ComponentStore ],
+  changeDetection: ChangeDetectionStrategy.OnPush
 })
 
-export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, AfterViewInit, OnDestroy {
-
-  private loanedColorMap = new WeakSet()
-
-  @Input()
-  selectedTemplate: any
+export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, AfterViewInit, OnDestroy {
 
-  @Input()
-  selectedParcellation: any
   
   @Output()
   viewerEvent = new EventEmitter<TViewerEvent<'threeSurfer'>>()
 
   private domEl: HTMLElement
-  private config: TThreeSurferConfig
-  public modes: TThreeSurferMode[] = []
-  public selectedMode: string
-
   private mainStoreCameraNav: TCameraOrientation = null
   private localCameraNav: TCameraOrientation = null
 
-  public allKeys: {name: string, checked: boolean}[] = []
+  public lateralityMeshRecord: LateralityRecord<{
+    visible: boolean
+    meshLayer: ThreeSurferCustomLayer
+    mesh: TThreeGeometry
+  }> = {}
+
+  public latLblIdxRecord: LateralityRecord<{
+    indexLayer: ThreeSurferCustomLabelLayer
+    labelIndices: number[]
+  }> = {}
   private internalStateNext: (arg: TInteralStatePayload<TInternalState>) => void
 
-  private regionMap: Map<string, Map<number, any>> = new Map()
-  private mouseoverRegions = []
+  private mouseoverRegions: SapiRegionModel[] = []
   
-  private raf: number
+  private selectedRegions$ = this.store$.pipe(
+    select(atlasSelection.selectors.selectedRegions)
+  )
+
+  private customLayers$ = this.store$.pipe(
+    select(atlasAppearance.selectors.customLayers),
+    distinctUntilChanged(arrayEqual((o, n) => o.id === n.id)),
+    shareReplay(1)
+  )
+  public meshLayers$: Observable<ThreeSurferCustomLayer[]> = this.customLayers$.pipe(
+    map(layers => layers.filter(l => l.clType === "baselayer/threesurfer") as ThreeSurferCustomLayer[]),
+    distinctUntilChanged(arrayEqual((o, n) => o.id === n.id)),
+  )
+
+  private vertexIndexLayers$: Observable<ThreeSurferCustomLabelLayer[]> = this.customLayers$.pipe(
+    map(layers => layers.filter(l => l.clType === "baselayer/threesurfer-label") as ThreeSurferCustomLabelLayer[]),
+    distinctUntilChanged(arrayEqual((o, n) => o.id === n.id)),
+  )
+
+  /**
+   * maps laterality to label index to sapi region
+   */
+  private latLblIdxToRegionRecord: LateralityRecord<Record<number, SapiRegionModel>> = {}
+  private latLblIdxToRegionRecord$: Observable<LateralityRecord<Record<number, SapiRegionModel>>> = combineLatest([
+    this.store$.pipe(
+      select(atlasSelection.selectors.selectedATP)
+    ),
+    this.store$.pipe(
+      select(atlasSelection.selectors.selectedParcAllRegions),
+    )
+  ]).pipe(
+    map(([ { atlas, parcellation, template },  regions]) => {
+      const returnObj = {
+        'left': {} as Record<number, SapiRegionModel>,
+        'right': {} as Record<number, SapiRegionModel>
+      }
+      
+      for (const region of regions) {
+        const idx = getRegionLabelIndex(atlas, template, parcellation, region)
+        if (idx) {
+          let key : 'left' | 'right'
+          if ( /left/i.test(region.name) ) key = 'left'
+          if ( /right/i.test(region.name) ) key = 'right'
+          if (!key) {
+            /**
+             * TODO
+             * there are ... more regions than expected, which has label index without laterality
+             */
+            continue
+          }
+          returnObj[key][idx] = region
+        }
+      }
+      return returnObj
+    })
+  )
+
+  /**
+   * colormap in use (both base & custom)
+   */
+
+  private colormapInUse: ColorMapCustomLayer
+  private colormaps$: Observable<ColorMapCustomLayer[]> = this.customLayers$.pipe(
+    map(layers => layers.filter(l => l.clType === "baselayer/colormap" || l.clType === "customlayer/colormap") as ColorMapCustomLayer[]),
+  )
+
+  /**
+   * show delination map
+   */
+  private showDelineation: boolean = true
+
+  public threeSurferSurfaceLayers$ = this.effect.onATPDebounceThreeSurferLayers$.pipe(
+    map(({ surfaces }) => surfaces)
+  )
+  public selectedSurfaceLayerId$ = this.store$.pipe(
+    select(selectors.getSelectedVolumeId)
+  )
+
   constructor(
+    private effect: ThreeSurferEffects,
     private el: ElementRef,
-    private store$: Store<any>,
+    private store$: Store,
     private navStateStoreRelay: ComponentStore<{ perspectiveOrientation: [number, number, number, number], perspectiveZoom: number }>,
     private snackbar: MatSnackBar,
     @Optional() intViewerStateSvc: ViewerInternalStateSvc,
@@ -151,40 +240,42 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, Af
       setViewerHandle({
         add3DLandmarks: nyi,
         loadLayer: nyi,
-        applyLayersColourMap: (map: Map<string, Map<number, { red: number, green: number, blue: number }>>) => {
-          if (this.loanedColorMap.has(map)) {
-            this.externalHemisphLblColorMap = null
-          } else {
-
-            const applyCm = new Map()
-            for (const [hem, m] of map.entries()) {
-              const nMap = new Map()
-              applyCm.set(hem, nMap)
-              for (const [lbl, vals] of m.entries()) {
-                const { red, green, blue } = vals
-                nMap.set(lbl, [red/255, green/255, blue/255])
-              }
-            }
-            this.externalHemisphLblColorMap = applyCm
-          }
-          this.applyColorMap()
+        applyLayersColourMap: function(map: Map<string, Map<number, { red: number, green: number, blue: number }>>){
+          throw new Error(`NYI`)
+          // if (this.loanedColorMap.has(map)) {
+          //   this.externalHemisphLblColorMap = null
+          // } else {
+
+          //   const applyCm = new Map()
+          //   for (const [hem, m] of map.entries()) {
+          //     const nMap = new Map()
+          //     applyCm.set(hem, nMap)
+          //     for (const [lbl, vals] of m.entries()) {
+          //       const { red, green, blue } = vals
+          //       nMap.set(lbl, [red/255, green/255, blue/255])
+          //     }
+          //   }
+          //   this.externalHemisphLblColorMap = applyCm
+          // }
+          // this.applyColorMap()
         },
         getLayersSegmentColourMap: () => {
-          const map = this.getColormapCopy()
-          const outmap = new Map<string, Map<number, { red: number, green: number, blue: number }>>()
-          for (const [ hem, m ] of map.entries()) {
-            const nMap = new Map<number, {red: number, green: number, blue: number}>()
-            outmap.set(hem, nMap)
-            for (const [ lbl, vals ] of m.entries()) {
-              nMap.set(lbl, {
-                red: vals[0] * 255,
-                green: vals[1] * 255,
-                blue: vals[2] * 255,
-              })
-            }
-          }
-          this.loanedColorMap.add(outmap)
-          return outmap
+          throw new Error(`NYI`)
+          // const map = this.getColormapCopy()
+          // const outmap = new Map<string, Map<number, { red: number, green: number, blue: number }>>()
+          // for (const [ hem, m ] of map.entries()) {
+          //   const nMap = new Map<number, {red: number, green: number, blue: number}>()
+          //   outmap.set(hem, nMap)
+          //   for (const [ lbl, vals ] of m.entries()) {
+          //     nMap.set(lbl, {
+          //       red: vals[0] * 255,
+          //       green: vals[1] * 255,
+          //       blue: vals[2] * 255,
+          //     })
+          //   }
+          // }
+          // this.loanedColorMap.add(outmap)
+          // return outmap
         },
         getNgHash: nyi,
         hideAllSegments: nyi,
@@ -208,53 +299,6 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, Af
       () => setViewerHandle(null)
     )
 
-    const sub = this.store$.pipe(
-      select(atlasSelection.selectors.selectedRegions)
-    ).subscribe(() => {
-
-      /**
-       * TODO
-       * fix ... this?
-       */
-
-      // if (this.roi$) {
-      //   const sub = this.roi$.pipe(
-      //     switchMap(switchMapWaitFor({
-      //       condition: () => this.colormapLoaded
-      //     }))
-      //   ).subscribe(r => {
-      //     try {
-      //       if (!r) throw new Error(`No region selected.`)
-      //       const cmap = this.getColormapCopy()
-      //       const hemisphere = getHemisphereKey(r)
-      //       if (!hemisphere) {
-      //         this.snackbar.open(CONST.CANNOT_DECIPHER_HEMISPHERE, 'Dismiss', {
-      //           duration: 3000
-      //         })
-      //         throw new Error(CONST.CANNOT_DECIPHER_HEMISPHERE)
-      //       }
-      //       for (const [ hem, m ] of cmap.entries()) {
-      //         for (const lbl of m.keys()) {
-      //           if (hem !== hemisphere || lbl !== r.labelIndex) {
-      //             m.set(lbl, [1, 1, 1])
-      //           }
-      //         }
-      //       }
-      //       this.internalHemisphLblColorMap = cmap
-      //     } catch (e) {
-      //       this.internalHemisphLblColorMap = null
-      //     }
-
-      //     this.applyColorMap()
-      //   })
-      //   this.onDestroyCb.push(
-      //     () => sub.unsubscribe()
-      //   )
-      // }
-
-    })
-    this.onDestroyCb.push(() => sub.unsubscribe())
-
     /**
      * intercept click and act
      */
@@ -274,10 +318,9 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, Af
           return true
         }
 
-        // TODO check why typing is all messed up here
         const regions = this.mouseoverRegions.slice(0, 1) as any[]
         this.store$.dispatch(
-          actions.selectRegions({ regions })
+          atlasSelection.actions.setSelectedRegions({ regions })
         )
         return true
       }
@@ -325,7 +368,7 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, Af
      */
     const navStateSub = this.navStateStoreRelay.select(s => s).subscribe(v => {
       this.store$.dispatch(
-        actions.navigateTo({
+        atlasSelection.actions.navigateTo({
           navigation: {
             position: [0, 0, 0],
             orientation: [0, 0, 0, 1],
@@ -372,23 +415,9 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, Af
     )
   }
 
-  tsRef: any
-  loadedMeshes: {
-    threeSurfer: any
-    mesh: string
-    colormap: string
-    hemisphere: string
-    vIdxArr: number[]
-  }[] = []
-  private hemisphLblColorMap: Map<string, Map<number, [number, number, number]>> = new Map()
-  private internalHemisphLblColorMap: Map<string, Map<number, [number, number, number]>>
-  private externalHemisphLblColorMap: Map<string, Map<number, [number, number, number]>>
-  
-  get activeColorMap() {
-    if (this.externalHemisphLblColorMap) return this.externalHemisphLblColorMap
-    if (this.internalHemisphLblColorMap) return this.internalHemisphLblColorMap
-    return this.hemisphLblColorMap
-  }
+  private tsRef: TThreeSurfer
+  private selectedRegions: SapiRegionModel[] = []
+
   private relayStoreLock: () => void = null
   private tsRefInitCb: ((tsRef: any) => void)[] = []
   private toTsRef(callback: (tsRef: any) => void) {
@@ -399,84 +428,47 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, Af
     this.tsRefInitCb.push(callback)
   }
 
-  private unloadAllMeshes() {
-    this.allKeys = []
-    while(this.loadedMeshes.length > 0) {
-      const m = this.loadedMeshes.pop()
-      this.tsRef.unloadMesh(m.threeSurfer)
-    }
-    this.hemisphLblColorMap.clear()
-    this.colormapLoaded = false
-  }
+  private async loadMeshes(layers: ThreeSurferCustomLayer[]) {
+    if (!this.tsRef) throw new Error(`loadMeshes error: this.tsRef is not defined!!`)
 
-  public async loadMode(mode: TThreeSurferMode) {
-    
-    this.unloadAllMeshes()
-
-    this.selectedMode = mode.name
-    const { meshes } = mode
-    await retry(async () => {
-      for (const singleMesh of meshes) {
-        const { hemisphere } = singleMesh
-        if (!this.regionMap.has(hemisphere)) throw new Error(`regionmap does not have hemisphere defined!`)
-      }
-    }, {
-      timeout: 32,
-      retries: 10
-    })
-    for (const singleMesh of meshes) {
-      const { mesh, colormap, hemisphere } = singleMesh
-      this.allKeys.push({name: hemisphere, checked: true})
-
-      const tsM = await this.tsRef.loadMesh(
-        parseContext(mesh, [this.config['@context']])
-      )
-
-      if (!this.regionMap.has(hemisphere)) continue
-      const rMap = this.regionMap.get(hemisphere)
-      const applyCM = new Map()
-      for (const [ lblIdx, region ] of rMap.entries()) {
-        applyCM.set(lblIdx, (region.rgb || [200, 200, 200]).map(v => v/255))
+    /**
+     * remove the layers... 
+     */
+    for (const layer of layers) {
+      if (!!this.lateralityMeshRecord[layer.laterality]) {
+        this.tsRef.unloadMesh(this.lateralityMeshRecord[layer.laterality].mesh)
       }
+    }
 
-      const tsC = await this.tsRef.loadColormap(
-        parseContext(colormap, [this.config['@context']])
-      )
-      
-      let colorIdx = tsC[0].getData()
-      if (tsC[0].attributes.DataType === 'NIFTI_TYPE_INT16') {
-        colorIdx = (window as any).ThreeSurfer.GiftiBase.castF32UInt16(colorIdx)
+    for (const layer of layers) {
+      const threeMesh = await this.tsRef.loadMesh(layer.source)
+      this.lateralityMeshRecord[layer.laterality] = {
+        visible: true,
+        meshLayer: layer,
+        mesh: threeMesh
       }
-
-      this.loadedMeshes.push({
-        threeSurfer: tsM,
-        colormap,
-        mesh,
-        hemisphere,
-        vIdxArr: colorIdx
-      })
-
-      this.hemisphLblColorMap.set(hemisphere, applyCM)
     }
-    this.colormapLoaded = true
-    this.applyColorMap()
+    this.applyColor()
   }
 
-  private colormapLoaded = false
+  private async loadVertexIndexMap(layers: ThreeSurferCustomLabelLayer[]) {
+    if (!this.tsRef) throw new Error(`loadVertexIndexMap error: this.tsRef is not defined!!`)
+    for (const layer of layers) {
+      const giiInstance = await this.tsRef.loadColormap(layer.source)
 
-  private getColormapCopy(): Map<string, Map<number, [number, number, number]>> {
-    const outmap = new Map()
-    for (const [key, value] of this.hemisphLblColorMap.entries()) {
-      outmap.set(key, new Map(value))
+      let labelIndices: number[] = giiInstance[0].getData()
+      if (giiInstance[0].attributes.DataType === 'NIFTI_TYPE_INT16') {
+        labelIndices = (window as any).ThreeSurfer.GiftiBase.castF32UInt16(labelIndices)
+      }
+      this.latLblIdxRecord[layer.laterality] = {
+        indexLayer: layer,
+        labelIndices
+      }
     }
-    return outmap
+    this.applyColor()
   }
 
-  /**
-   * TODO perhaps debounce calls to applycolormap
-   * so that the colormap doesn't "flick"
-   */
-  private applyColorMap(){
+  private applyColor() {
     /**
      * on apply color map, reset mesh visibility
      * this issue is more difficult to solve than first anticiplated.
@@ -486,72 +478,34 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, Af
      * 2/ hide hemisphere, select region, unhide hemisphere
      * 3/ select region, hide hemisphere, deselect region
      */
-    for (const key of this.allKeys) {
-      key.checked = true
-    }
-    for (const mesh of this.loadedMeshes) {
-      const { hemisphere, threeSurfer, vIdxArr } = mesh
-      const applyCM = this.activeColorMap.get(hemisphere)
-      this.tsRef.applyColorMap(threeSurfer, vIdxArr, 
-        {
-          custom: applyCM
-        }
-      )
-    }
-  }
-
-  async ngOnChanges(){
-    if (this.tsRef) {
-      this.ngOnDestroy()
-      this.ngAfterViewInit()
-    }
-    if (this.selectedTemplate) {
-
-      /**
-       * wait until threesurfer is defined in window
-       */
-      await retry(async () => {
-        if (typeof (window as any).ThreeSurfer === 'undefined') throw new Error('ThreeSurfer not yet defined')
-      }, {
-        timeout: 160,
-        retries: 10,
-      })
-      
-      this.config = this.selectedTemplate['three-surfer']
-      // somehow curv ... cannot be parsed properly by gifti parser... something about points missing
-      this.modes = this.config.modes.filter(m => !/curv/.test(m.name))
-      if (!this.tsRef) {
-        this.tsRef = new (window as any).ThreeSurfer(this.domEl, {highlightHovered: true})
-        this.onDestroyCb.push(
-          () => {
-            this.tsRef.dispose()
-            this.tsRef = null
-          }
-        )
-        this.tsRef.control.enablePan = false
-        while (this.tsRefInitCb.length > 0) this.tsRefInitCb.pop()(this.tsRef)
+    if (!this.colormapInUse) return
+    const isBaseCM = this.colormapInUse?.clType === "baselayer/colormap"
+
+    for (const laterality in this.lateralityMeshRecord) {
+      const { mesh } = this.lateralityMeshRecord[laterality]
+      if (!this.latLblIdxRecord[laterality]) continue
+      const { labelIndices } = this.latLblIdxRecord[laterality]
+
+      const lblIdxToRegionRecord = this.latLblIdxToRegionRecord[laterality]
+      if (!lblIdxToRegionRecord) {
+        this.tsRef.applyColorMap(mesh, labelIndices)
+        continue
       }
-
-      const flattenedRegions = flattenRegions(this.selectedParcellation.regions)
-      for (const region of flattenedRegions) {
-        if (region.labelIndex) {
-          const hemisphere = getHemisphereKey(region)
-          if (!hemisphere) throw new Error(`region ${region.name} does not have hemisphere defined`)
-          if (!this.regionMap.has(hemisphere)) {
-            this.regionMap.set(hemisphere, new Map())
-          }
-          const rMap = this.regionMap.get(hemisphere)
-          rMap.set(region.labelIndex, region)
+      const map = new Map<number, number[]>()
+      for (const lblIdx in lblIdxToRegionRecord) {
+        const region = lblIdxToRegionRecord[lblIdx]
+        let color: number[]
+        if (!this.showDelineation) {
+          color = [1,1,1]
+        } else if (isBaseCM && this.selectedRegions.length > 0 && !this.selectedRegions.includes(region)) {
+          color = [1,1,1]
+        } else {
+          color = (this.colormapInUse.colormap.get(region) || [255, 255, 255]).map(v => v/255)
         }
+        map.set(Number(lblIdx), color)
       }
-      
-      // load preferredFsMode or mode0 by default
-      const loadMode = this.config.modes.find(m => m.name === preferredFsMode) || this.config.modes[0]
-      this.loadMode(loadMode)
-
-      this.viewerEvent.emit({
-        type: EnumViewerEvt.VIEWERLOADED,
-        data: true
+      this.tsRef.applyColorMap(mesh, labelIndices, {
+        custom: map
       })
     }
   }
@@ -571,59 +525,66 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, Af
       return this.handleMouseoverEvent(custEv)
     }
 
-    const evGeom = detail.mesh.geometry
-    const evVertIdx = detail.mesh.verticesIdicies
-    const found = this.loadedMeshes.find(({ threeSurfer }) => threeSurfer === evGeom)
-    if (!found) return this.handleMouseoverEvent(custEv)
-
-    /**
-     * check if the mesh is toggled off
-     * if so, do not proceed
-     */
-    const checkKey = this.allKeys.find(key => key.name === found.hemisphere)
-    if (checkKey && !checkKey.checked) return
-
-    const { hemisphere: key, vIdxArr } = found
-
-    if (!key || !evVertIdx) {
-      return this.handleMouseoverEvent(custEv)
-    }
+    const {
+      geometry: evGeometry,
+      // typo in three-surfer
+      verticesIdicies: evVerticesIndicies,
+    } = detail.mesh as { geometry: TThreeGeometry, verticesIdicies: number[] }
 
-    const labelIdxSet = new Set<number>()
-    
-    for (const vIdx of evVertIdx) {
-      labelIdxSet.add(
-        vIdxArr[vIdx]
-      )
-    }
-    if (labelIdxSet.size === 0) {
-      return this.handleMouseoverEvent(custEv)
-    }
+    for (const laterality in this.lateralityMeshRecord) {
+      const meshRecord = this.lateralityMeshRecord[laterality]
+      if (meshRecord.mesh !== evGeometry) {
+        continue
+      }
+      /**
+       * if either labelindex record or colormap record is undefined for this laterality, emit empty event
+       */
+      if (!this.latLblIdxRecord[laterality] || !this.latLblIdxToRegionRecord[laterality]) {
+        return this.handleMouseoverEvent(custEv)
+      }
+      const labelIndexRecord = this.latLblIdxRecord[laterality]
+      const regionRecord = this.latLblIdxToRegionRecord[laterality]
 
-    const hemisphereMap = this.regionMap.get(key)
+      /**
+       * check if the mesh is toggled off
+       * if so, do not proceed
+       */
+      if (!meshRecord.visible) {
+        return
+      }
 
-    if (!hemisphereMap) {
-      custEv.regions = Array.from(labelIdxSet).map(v => {
-        return {
-          error: `unknown#${v}`
+      /**
+       * translate vertex indices to label indicies via set, to remove duplicates
+       */
+      const labelIndexSet = new Set<number>()
+      for (const idx of evVerticesIndicies){
+        const labelOfInterest = labelIndexRecord.labelIndices[idx]
+        if (!labelOfInterest) {
+          continue
         }
-      })
-      return this.handleMouseoverEvent(custEv)
-    }
+        labelIndexSet.add(labelOfInterest)
+      }
 
-    custEv.regions =  Array.from(labelIdxSet)
-      .map(lblIdx => {
-        const ontoR = hemisphereMap.get(lblIdx)
-        if (ontoR) {
-          return ontoR
-        } else {
-          return {
-            error: `unkonwn#${lblIdx}`
-          }
+      /**
+       * decode label index to region
+       */
+      if (labelIndexSet.size === 0) {
+        return this.handleMouseoverEvent(custEv)
+      }
+      for (const labelIndex of Array.from(labelIndexSet)) {
+        if (!regionRecord[labelIndex]) {
+          custEv.error = `${custEv.error || ''} Cannot decode label index ${labelIndex}`
+          continue
         }
-      })
-    return this.handleMouseoverEvent(custEv)
+        const region = regionRecord[labelIndex]
+        custEv.regions.push(region)
+      }
 
+      /**
+       * return handle event
+       */
+      return this.handleMouseoverEvent(custEv)
+    }
   }
 
   private cameraEv$ = new Subject<{ position: { x: number, y: number, z: number }, zoom: number }>()
@@ -634,7 +595,7 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, Af
         "@type": 'TViewerInternalStateEmitterEvent',
         viewerType,
         payload: {
-          mode: this.selectedMode,
+          mode: '',
           camera: detail.position,
           hemisphere: 'both'
         }
@@ -657,42 +618,109 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, Af
     this.onDestroyCb.push(
       () => this.domEl.removeEventListener((window as any).ThreeSurfer.CUSTOM_EVENTNAME_UPDATED, customEvHandler)
     )
+    this.tsRef = new (window as any).ThreeSurfer(this.domEl, {highlightHovered: true})
+
+    this.onDestroyCb.push(
+      () => {
+        this.tsRef.dispose()
+        this.tsRef = null
+      }
+    )
+    this.tsRef.control.enablePan = false
+    while (this.tsRefInitCb.length > 0) this.tsRefInitCb.pop()(this.tsRef)
+
+    const meshSub = this.meshLayers$.pipe(
+      distinctUntilChanged(),
+      debounceTime(16),
+    ).subscribe(layers => {
+      this.loadMeshes(layers)
+    })
+    const vertexIdxSub = this.vertexIndexLayers$.subscribe(layers => this.loadVertexIndexMap(layers))
+    const roiSelectedSub = this.selectedRegions$.subscribe(regions => {
+      this.selectedRegions = regions
+      this.applyColor()
+    })
+    const colormapSub = this.colormaps$.subscribe(cm => {
+      this.colormapInUse = cm[0] || null
+      this.applyColor()
+    })
+    const recordToRegionSub = this.latLblIdxToRegionRecord$.subscribe(val => this.latLblIdxToRegionRecord = val)
+    const hideDelineationSub = this.store$.pipe(
+      select(atlasAppearance.selectors.showDelineation)
+    ).subscribe(flag => {
+      this.showDelineation = flag
+      this.applyColor()
+      /**
+       * apply color resets mesh visibility
+       */
+      this.updateMeshVisibility()
+    })
+
+    this.onDestroyCb.push(() => {
+      meshSub.unsubscribe()
+      vertexIdxSub.unsubscribe()
+      roiSelectedSub.unsubscribe()
+      colormapSub.unsubscribe()
+      recordToRegionSub.unsubscribe()
+      hideDelineationSub.unsubscribe()
+    })
+
+    this.viewerEvent.emit({
+      type: EnumViewerEvt.VIEWERLOADED,
+      data: true
+    })
   }
 
   public mouseoverText: string
   private handleMouseoverEvent(ev: THandlingCustomEv){
-    const { regions: mouseover, evMesh } = ev
+    const { regions: mouseover, evMesh, error } = ev
     this.mouseoverRegions = mouseover
     this.viewerEvent.emit({
       type: EnumViewerEvt.VIEWER_CTX,
       data: {
         viewerType: 'threeSurfer',
         payload: {
-          fsversion: this.selectedMode,
+          fsversion: '',
           faceIndex: evMesh?.faceIndex,
           vertexIndices: evMesh?.verticesIndicies,
           position: [],
-          _mouseoverRegion: mouseover.filter(el => !el.error)
+          regions: mouseover,
+          error
         }
       }
     })
-    this.mouseoverText = mouseover.length === 0 ?
-      null :
-      mouseover.map(
-        el => el.name || el.error
-      ).join(' / ')
+    this.mouseoverText = ''
+    if (mouseover.length > 0) {
+      this.mouseoverText += mouseover.map(el => el.name).join(' / ')
+    }
+    if (error) {
+      this.mouseoverText += `::error: ${error}`
+    }
+    if (this.mouseoverText === '') this.mouseoverText = null
   }
 
-  public handleCheckBox(key: { name: string, checked: boolean }, flag: boolean){
-    const foundMesh = this.loadedMeshes.find(m => m.hemisphere === key.name)
-    if (!foundMesh) {
-      throw new Error(`Cannot find mesh with name: ${key.name}`)
-    }
-    const meshObj = this.tsRef.customColormap.get(foundMesh.threeSurfer)
-    if (!meshObj) {
-      throw new Error(`mesh obj not found!`)
+  public updateMeshVisibility(){
+
+    for (const key in this.lateralityMeshRecord) {
+
+      const latMeshRecord = this.lateralityMeshRecord[key]
+      if (!latMeshRecord) {
+        return
+      }
+      const meshObj = this.tsRef.customColormap.get(latMeshRecord.mesh)
+      if (!meshObj) {
+        throw new Error(`mesh obj not found!`)
+      }
+      meshObj.mesh.visible = latMeshRecord.visible
     }
-    meshObj.mesh.visible = flag
+  }
+
+  switchSurfaceLayer(layer: SapiVolumeModel){
+    this.store$.dispatch(
+      actions.selectVolumeById({
+        id: layer["@id"]
+      })
+    )
   }
 
   private onDestroyCb: (() => void) [] = []
@@ -700,10 +728,4 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, Af
   ngOnDestroy() {
     while (this.onDestroyCb.length > 0) this.onDestroyCb.pop()()
   }
-
-  toggleMode(){
-    const currIdx = this.modes.findIndex(m => m.name === this.selectedMode)
-    const newIdx = (currIdx + 1) % this.modes.length
-    this.loadMode(this.modes[newIdx])
-  }
 }
diff --git a/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.template.html b/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.template.html
index cd04b28dc5a327f5461719c200ce01ab3361ef51..2a9703618ce87100f39bbd6470f88d4cd5bd5a57 100644
--- a/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.template.html
+++ b/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.template.html
@@ -1,5 +1,5 @@
 <span *ngIf="mouseoverText"
-  class="mouseover iv-custom-comp text">
+  class="mouseover sxplr-custom-cmp text">
   {{ mouseoverText }}
 </span>
 
@@ -7,8 +7,6 @@
 
   <!-- selector & configurator -->
   <button mat-icon-button
-    [iav-key-listener]="[{ type: 'keydown', key: 'q', target: 'document' }]"
-    (iav-key-event)="toggleMode()"
     color="primary"
     class="pe-all"
     [matMenuTriggerFor]="fsModeSelMenu">
@@ -20,30 +18,27 @@
 <!-- selector/configurator menu -->
 <mat-menu #fsModeSelMenu="matMenu">
 
-  <div class="iv-custom-comp text sxplr-pl-2 m-2">
-    <mat-checkbox *ngFor="let key of allKeys"
+  <div class="sxplr-custom-cmp text sxplr-pl-2 m-2">
+    <mat-checkbox *ngFor="let item of lateralityMeshRecord | keyvalue"
       class="d-block"
       iav-stop="click"
-      (ngModelChange)="handleCheckBox(key, $event)"
-      [(ngModel)]="key.checked">
-      {{ key.name }}
+      (change)="updateMeshVisibility()"
+      [(ngModel)]="item.value.visible">
+      {{ item.key }}
     </mat-checkbox>
   </div>
   <mat-divider></mat-divider>
-  <button *ngFor="let mode of modes"
+
+  <button *ngFor="let surfaceLayer of threeSurferSurfaceLayers$ | async"
     mat-menu-item
-    (click)="loadMode(mode)"
+    (click)="switchSurfaceLayer(surfaceLayer)"
     color="primary">
     <mat-icon
       fontSet="fas"
-      [fontIcon]="mode.name === selectedMode ? 'fa-circle' : 'fa-none'">
+      [fontIcon]="surfaceLayer['@id'] === (selectedSurfaceLayerId$ | async) ? 'fa-circle' : 'fa-none'">
     </mat-icon>
     <span>
-      {{ mode.name }}
+      {{ surfaceLayer.metadata.shortName }}
     </span>
-    <markdown-dom *ngIf="mode.name === selectedMode"
-      class="d-inline-block"
-      markdown="`[q]`">
-    </markdown-dom>
   </button>
 </mat-menu>
diff --git a/src/viewerModule/threeSurfer/types.ts b/src/viewerModule/threeSurfer/types.ts
index 8ec78e98787d2e05faeb976e272fdc2981809fcd..9a15c5642d7c04e5c5057a412532eaabe51be8fe 100644
--- a/src/viewerModule/threeSurfer/types.ts
+++ b/src/viewerModule/threeSurfer/types.ts
@@ -1,4 +1,4 @@
-import { IContext } from './util'
+import { SapiRegionModel } from 'src/atlasComponents/sapi'
 
 export type TThreeSurferMesh = {
   colormap: string
@@ -6,20 +6,11 @@ export type TThreeSurferMesh = {
   hemisphere: 'left' | 'right'
 }
 
-export type TThreeSurferMode = {
-  name: string
-  meshes: TThreeSurferMesh[]
-}
-
-export type TThreeSurferConfig = {
-  ['@context']: IContext
-  modes: TThreeSurferMode[]
-}
-
 export type TThreeSurferContextInfo = {
   position: number[]
   faceIndex: number
   vertexIndices: number[]
   fsversion: string
-  _mouseoverRegion: { name: string, error?: string }[]
+  regions: SapiRegionModel[]
+  error?: string
 }
diff --git a/src/viewerModule/threeSurfer/util.ts b/src/viewerModule/threeSurfer/util.ts
index 34a0d85065cf7104d57faf08790dd943804f7b26..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644
--- a/src/viewerModule/threeSurfer/util.ts
+++ b/src/viewerModule/threeSurfer/util.ts
@@ -1,14 +0,0 @@
-export interface IContext {
-  [key: string]: string
-}
-
-export function parseContext(input: string, contexts: IContext[]){
-  let output = input
-  for (const context of contexts) {
-    for (const key in context) {
-      const re = new RegExp(`${key}:`, 'g')
-      output = output.replace(re, context[key])
-    }
-  }
-  return output
-}
diff --git a/src/viewerModule/viewer.interface.ts b/src/viewerModule/viewer.interface.ts
index 2f86f5eaa7229713bc6d16e7c8dad6111bfeecb0..cbc85aca0e3e2620a0903b4e332dcb92386e8db0 100644
--- a/src/viewerModule/viewer.interface.ts
+++ b/src/viewerModule/viewer.interface.ts
@@ -59,9 +59,6 @@ export type TViewerEvent<T extends keyof IViewerCtx> = TViewerEventViewerLoaded
 export type TSupportedViewers = keyof IViewerCtx
 
 export interface IViewer<K extends keyof IViewerCtx> {
-  
-  selectedTemplate: any
-  selectedParcellation: any
   viewerCtrlHandler?: IViewerCtrl
   viewerEvent: EventEmitter<TViewerEvent<K>>
 }
diff --git a/src/viewerModule/viewerCmp/viewerCmp.component.ts b/src/viewerModule/viewerCmp/viewerCmp.component.ts
index d0ef3818805ae74c1087478b06f04412889615ca..02e48a907ca71600df58d375310b8bad80607bc2 100644
--- a/src/viewerModule/viewerCmp/viewerCmp.component.ts
+++ b/src/viewerModule/viewerCmp/viewerCmp.component.ts
@@ -289,7 +289,7 @@ export class ViewerCmp implements OnDestroy {
       }
 
       if (context.viewerType === 'threeSurfer') {
-        hoveredRegions = (context as TContextArg<'threeSurfer'>).payload._mouseoverRegion
+        hoveredRegions = (context as TContextArg<'threeSurfer'>).payload.regions
       }
 
       if (hoveredRegions.length > 0) {
@@ -324,8 +324,8 @@ export class ViewerCmp implements OnDestroy {
 
   public selectRoi(roi: SapiRegionModel) {
     this.store$.dispatch(
-      actions.selectRegions({
-        regions: [ roi ]
+      actions.selectRegion({
+        region: roi
       })
     )
   }
diff --git a/src/viewerModule/viewerCmp/viewerCmp.style.css b/src/viewerModule/viewerCmp/viewerCmp.style.css
index bc4d6b48d991b1d7f74fd7e2d2d1545ee164537a..4d6407b910bb98bb8791bdebfcb2321bac7c835b 100644
--- a/src/viewerModule/viewerCmp/viewerCmp.style.css
+++ b/src/viewerModule/viewerCmp/viewerCmp.style.css
@@ -80,4 +80,12 @@ sxplr-sapiviews-core-region-region-chip [prefix]
 .auto-complete-container > button
 {
   flex: 0 0 auto;
-}
\ No newline at end of file
+}
+
+.min-tray-explr-btn
+{
+  width: 100%;
+  padding-left: 0.5rem;
+  padding-right: 0.5rem;
+  margin-top: -1rem;
+}
diff --git a/src/viewerModule/viewerCmp/viewerCmp.template.html b/src/viewerModule/viewerCmp/viewerCmp.template.html
index 4ac57dfe0614bb8e8c030c912a2d5d189b2a5006..4a0264bb7275d928ea6f1583ad029f207c29e049 100644
--- a/src/viewerModule/viewerCmp/viewerCmp.template.html
+++ b/src/viewerModule/viewerCmp/viewerCmp.template.html
@@ -23,7 +23,7 @@
     (@openClose.done)="$event.toState === 'closed' && drawer.close()"
     [autoFocus]="false"
     [disableClose]="true"
-    class="iv-custom-comp darker-bg sxplr-p-0 pe-all col-10 col-sm-10 col-md-5 col-lg-4 col-xl-3 col-xxl-2 z-index-10">
+    class="sxplr-custom-cmp darker-bg sxplr-p-0 pe-all col-10 col-sm-10 col-md-5 col-lg-4 col-xl-3 col-xxl-2 z-index-10">
 
     <!-- entry template -->
     <ng-template [ngIf]="viewerMode$ | async" let-mode [ngIfElse]="regularTmpl">
@@ -204,7 +204,7 @@
 
     <!-- such a gross implementation -->
     <!-- TODO fix this -->
-    <div class="sxplr-mt-1-n w-100 sxplr-pl-1 sxplr-pr-1"
+    <div class="min-tray-explr-btn"
       sxplr-sapiviews-core-region
       [sxplr-sapiviews-core-region-atlas]="selectedAtlas$ | async"
       [sxplr-sapiviews-core-region-template]="templateSelected$ | async"
@@ -224,7 +224,7 @@
           'lighttheme': !sapiRegion.regionDarkmode
         }"
         [style.backgroundColor]="sapiRegion.regionRgbString">
-        <span class="text iv-custom-comp">
+        <span class="text sxplr-custom-cmp">
           Explore
         </span>
       </button>
@@ -335,7 +335,7 @@
   </top-menu-cmp>
 
   <sxplr-sapiviews-core-atlas-dropdown-selector
-    class="v-align-top sxplr-pt-2 pe-all mt-2 iv-custom-comp bg card m-2 mat-elevation-z2 d-inline-block"
+    class="v-align-top sxplr-pt-2 pe-all mt-2 sxplr-custom-cmp bg card m-2 mat-elevation-z2 d-inline-block"
     quick-tour
     [quick-tour-description]="quickTourAtlasSelector.description"
     [quick-tour-order]="quickTourAtlasSelector.order">
@@ -434,9 +434,7 @@
       <!-- three surfer (free surfer viewer) -->
       <three-surfer-glue-cmp class="d-block w-100 h-100 position-absolute left-0 tosxplr-p-0"
         *ngSwitchCase="'threeSurfer'"
-        (viewerEvent)="handleViewerEvent($event)"
-        [selectedTemplate]="templateSelected$ | async"
-        [selectedParcellation]="parcellationSelected$ | async">
+        (viewerEvent)="handleViewerEvent($event)">
       </three-surfer-glue-cmp>
 
       <!-- if not supported, show not supported message -->
@@ -459,19 +457,24 @@
 <!-- region-hierarchy-tmpl -->
 
 <ng-template #regionHierarchyTmpl>
-  <div class="sxplr-w-100 sxplr-h-100">
+  <div class="sxplr-d-flex sxplr-flex-column sxplr-h-100">
     <sxplr-sapiviews-core-rich-regionshierarchy
-      class="sxplr-w-100 sxplr-h-100"
+      class="sxplr-w-100 sxplr-flex-var"
       [sxplr-sapiviews-core-rich-regionshierarchy-regions]="allAvailableRegions$ | async"
-      (sxplr-sapiviews-core-rich-regionshierarchy-region-select)="navigateTo($event)"
+      [sxplr-sapiviews-core-rich-regionshierarchy-accent-regions]="selectedRegions$ | async"
+      (sxplr-sapiviews-core-rich-regionshierarchy-region-select)="selectRoi($event)"
       >
     </sxplr-sapiviews-core-rich-regionshierarchy>
+  
+    <mat-dialog-actions align="center" class="sxplr-flex-static">
+      <button mat-button mat-dialog-close>Close</button>
+    </mat-dialog-actions>
   </div>
 </ng-template>
 
 <!-- auto complete search box -->
 <ng-template #autocompleteTmpl let-showTour="showTour">
-  <div class="iv-custom-comp bg card ml-2 mr-2 mat-elevation-z8 pe-all auto-complete-container">
+  <div class="sxplr-custom-cmp bg card ml-2 mr-2 mat-elevation-z8 pe-all auto-complete-container">
     
     <ng-template #selectedRegionCheckTmpl let-region>
       <ng-template #fallbackTmpl>
@@ -620,7 +623,7 @@
     [matBadge]="badge"
     [matBadgeColor]="badgeColor || 'warn'">
 
-    <span [ngClass]="{'iv-custom-comp  text': !!customColor}">
+    <span [ngClass]="{'sxplr-custom-cmp  text': !!customColor}">
       <i class="fas" [ngClass]="fontIcon || 'fa-question'"></i>
     </span>
   </button>
@@ -969,7 +972,7 @@
       <div *ngFor="let feature of spatialFeatureBbox.features$ | async"
         mat-ripple
         (click)="showDataset(feature)"
-        class="iv-custom-comp hoverable w-100 overflow-hidden text-overflow-ellipses">
+        class="sxplr-custom-cmp hoverable w-100 overflow-hidden text-overflow-ellipses">
         {{ feature.name }}
       </div>
     </mat-card>