diff --git a/angular.json b/angular.json
index 46d2ba12824d187f38aab94428da75566e9fccee..e755712e210013183c9dd1f5eca16231c0bc9c4b 100644
--- a/angular.json
+++ b/angular.json
@@ -42,65 +42,80 @@
                 "bundleName": "vanillaMain"
               }
             ],
-            "scripts": [{
-              "input": "worker/worker.js",
-              "inject": false,
-              "bundleName": "worker"
-            },{
-              "input": "worker/worker-plotly.js",
-              "inject": false,
-              "bundleName": "worker-plotly"
-            },{
-              "input": "worker/worker-nifti.js",
-              "inject": false,
-              "bundleName": "worker-nifti"
-            },{
-              "input": "worker/worker-typedarray.js",
-              "inject": false,
-              "bundleName": "worker-typedarray"
-            },{
-              "input": "third_party/catchSyntaxError.js",
-              "inject": false,
-              "bundleName": "catchSyntaxError"
-            },{
-              "input": "third_party/extra_js.js",
-              "inject": false,
-              "bundleName": "extra_js"
-            },
-            {
-              "input": "third_party/vanilla_nehuba.js",
-              "inject": false,
-              "bundleName": "vanilla_nehuba"
-            },
-            
-            {
-              "input": "export-nehuba/dist/min/main.bundle.js",
-              "inject": false,
-              "bundleName": "main.bundle"
-            },{
-              "input": "export-nehuba/dist/min/chunk_worker.bundle.js",
-              "inject": false,
-              "bundleName": "chunk_worker.bundle"
-            },
-            {
-              "input": "export-nehuba/dist/min/draco.bundle.js",
-              "inject": false,
-              "bundleName": "draco.bundle"
-            },{
-              "input": "export-nehuba/dist/min/async_computation.bundle.js",
-              "inject": false,
-              "bundleName": "async_computation.bundle"
-            },{
-              "input": "export-nehuba/dist/min/blosc.bundle.js",
-              "inject": false,
-              "bundleName": "blosc.bundle"
-            },
-            
-            {
-              "inject": false,
-              "input": "third_party/leap-0.6.4.js",
-              "bundleName": "leap-0.6.4"
-            }]
+            "scripts": [
+              {
+                "input": "worker/worker.js",
+                "inject": false,
+                "bundleName": "worker"
+              },
+              {
+                "input": "worker/worker-plotly.js",
+                "inject": false,
+                "bundleName": "worker-plotly"
+              },
+              {
+                "input": "worker/worker-nifti.js",
+                "inject": false,
+                "bundleName": "worker-nifti"
+              },
+              {
+                "input": "worker/worker-typedarray.js",
+                "inject": false,
+                "bundleName": "worker-typedarray"
+              },
+              {
+                "input": "worker/worker-regionFilter.js",
+                "inject": false,
+                "bundleName": "worker-regionFilter"
+              },
+              {
+                "input": "third_party/catchSyntaxError.js",
+                "inject": false,
+                "bundleName": "catchSyntaxError"
+              },
+              {
+                "input": "third_party/extra_js.js",
+                "inject": false,
+                "bundleName": "extra_js"
+              },
+              {
+                "input": "third_party/vanilla_nehuba.js",
+                "inject": false,
+                "bundleName": "vanilla_nehuba"
+              },
+              
+              {
+                "input": "export-nehuba/dist/min/main.bundle.js",
+                "inject": false,
+                "bundleName": "main.bundle"
+              },
+              {
+                "input": "export-nehuba/dist/min/chunk_worker.bundle.js",
+                "inject": false,
+                "bundleName": "chunk_worker.bundle"
+              },
+              {
+                "input": "export-nehuba/dist/min/draco.bundle.js",
+                "inject": false,
+                "bundleName": "draco.bundle"
+              },
+              {
+                "input": "export-nehuba/dist/min/async_computation.bundle.js",
+                "inject": false,
+                "bundleName": "async_computation.bundle"
+              },
+              {
+                "input": "export-nehuba/dist/min/blosc.bundle.js",
+                "inject": false,
+                "bundleName": "blosc.bundle"
+              },
+              
+              {
+                "inject": false,
+                "input": "third_party/leap-0.6.4.js",
+                "bundleName": "leap-0.6.4"
+              }
+            ]
           },
           "configurations": {
             "production": {
diff --git a/docs/releases/v2.14.0.md b/docs/releases/v2.14.0.md
index fd75241e279355715372abcb3858f84288d6d804..e683b6817dc225c3d38e0bb1038c777a79d797c6 100644
--- a/docs/releases/v2.14.0.md
+++ b/docs/releases/v2.14.0.md
@@ -8,6 +8,8 @@
 - experimental support for other versions of regions
 - experimental support for drag/drop pointcloud `.json` files
 - experimental support for `deepzoom://` source format
+- quick search now show branches in addition to leaves
+- added context in region hierarchy view
 
 ## Bugfix
 
@@ -18,3 +20,5 @@
 - minor refactoring
 - update to Angular 15
 - migrated to Angular material 15
+- filter region now runs on worker thread. This should speed up region filter responsiveness
+- quick search now show all results, rather than the first four
diff --git a/src/atlasComponents/sapiViews/core/region/region/listItem/region.listItem.style.css b/src/atlasComponents/sapiViews/core/region/region/listItem/region.listItem.style.css
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0f153b70e5bfa05bdb65c2b3ca86d77df8efb94c 100644
--- a/src/atlasComponents/sapiViews/core/region/region/listItem/region.listItem.style.css
+++ b/src/atlasComponents/sapiViews/core/region/region/listItem/region.listItem.style.css
@@ -0,0 +1,11 @@
+.container
+{
+    display: inline-flex;
+    width: 100%;
+    align-items: center;
+}
+
+.container .mat-body
+{
+    text-wrap: nowrap;
+}
diff --git a/src/atlasComponents/sapiViews/core/region/region/listItem/region.listItem.template.html b/src/atlasComponents/sapiViews/core/region/region/listItem/region.listItem.template.html
index 4c833e9218409ff1ed338d4cc781dc852742fd52..5c6ff521806ea8d0ea9501b52054f878ff16968d 100644
--- a/src/atlasComponents/sapiViews/core/region/region/listItem/region.listItem.template.html
+++ b/src/atlasComponents/sapiViews/core/region/region/listItem/region.listItem.template.html
@@ -1,7 +1,7 @@
-<div *ngIf="region" matRipple [matRippleDisabled]="!ripple" class="sxplr-d-inline-flex">
+<div *ngIf="region" matRipple [matRippleDisabled]="!ripple" class="container">
   <ng-content select="[prefix]"></ng-content>
   <span class="mat-body">
     {{ region.name }}
   </span>
   <ng-content select="[suffix]"></ng-content>
-</div>
\ No newline at end of file
+</div>
diff --git a/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.components.ts b/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.components.ts
index 2a169d93a49517e37c1bd59c6d069ed3aafce31e..da005aece81a89384a6e3b09d56d90aeb28f96c1 100644
--- a/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.components.ts
+++ b/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.components.ts
@@ -1,9 +1,10 @@
-import { AfterViewInit, ChangeDetectionStrategy, Component, EventEmitter, HostBinding, Input, OnDestroy, Output, QueryList, ViewChildren } from "@angular/core";
-import { BehaviorSubject, Subscription, combineLatest, concat, merge, of } from "rxjs";
-import { map, switchMap } from "rxjs/operators";
+import { AfterViewInit, ChangeDetectionStrategy, Component, EventEmitter, HostBinding, Input, Output, QueryList, ViewChildren, inject } from "@angular/core";
+import { BehaviorSubject, combineLatest, concat, merge, of } from "rxjs";
+import { debounceTime, map, switchMap, takeUntil } from "rxjs/operators";
 import { SxplrAtlas, SxplrParcellation, SxplrTemplate } from "src/atlasComponents/sapi/sxplrTypes";
 import { FilterGroupedParcellationPipe, GroupedParcellation } from "src/atlasComponents/sapiViews/core/parcellation";
 import { SmartChip } from "src/components/smartChip";
+import { DestroyDirective } from "src/util/directives/destroy.directive";
 
 export const darkThemePalette = [
   "#141414",
@@ -41,15 +42,21 @@ const pipe = new FilterGroupedParcellationPipe()
     `./pureATPSelector.style.scss`
   ],
   changeDetection: ChangeDetectionStrategy.OnPush,
+  hostDirectives: [
+    DestroyDirective
+  ]
 })
 
-export class PureATPSelector implements AfterViewInit, OnDestroy{
+export class PureATPSelector implements AfterViewInit{
 
-  #subscriptions: Subscription[] = []
+  #onDestroy$ = inject(DestroyDirective).destroyed$
 
   @Input('sxplr-pure-atp-selector-color-palette')
   colorPalette: string[] = darkThemePalette
 
+  @Input("sxplr-pure-atp-selector-minimized")
+  minimized = true
+
   #selectedATP$ = new BehaviorSubject<ATP>(null)
   @Input(`sxplr-pure-atp-selector-selected-atp`)
   set selectedATP(val: ATP){
@@ -112,8 +119,8 @@ export class PureATPSelector implements AfterViewInit, OnDestroy{
       ]
       const selectedIds = [atlas?.id, parcellation?.id, template?.id].filter(v => !!v)
 
-      const hideParcChip = parcAndGroup.length <= 1
-      const hideTmplChip = availableTemplates?.length <= 1
+      const hideParcChip = this.minimized && parcAndGroup.length <= 1
+      const hideTmplChip = this.minimized && availableTemplates?.length <= 1
       
       return {
         atlas,
@@ -130,53 +137,48 @@ export class PureATPSelector implements AfterViewInit, OnDestroy{
   )
 
   ngAfterViewInit(): void {
-    this.#subscriptions.push(
-      concat(
-        of(null),
-        this.smartChips.changes,
-      ).pipe(
-        switchMap(() =>
-          combineLatest(
-            Array.from(this.smartChips).map(chip =>
-              concat(
-                of(false),
-                merge(
-                  chip.menuOpened.pipe(
-                    map(() => true)
-                  ),
-                  chip.menuClosed.pipe(
-                    map(() => false)
-                  )
+    concat(
+      of(null),
+      this.smartChips.changes,
+    ).pipe(
+      switchMap(() =>
+        combineLatest(
+          Array.from(this.smartChips).map(chip =>
+            concat(
+              of(false),
+              merge(
+                chip.menuOpened.pipe(
+                  map(() => true)
+                ),
+                chip.menuClosed.pipe(
+                  map(() => false)
                 )
               )
             )
           )
-        ),
-      ).subscribe(arr => {
-        const newVal = {
-          some: arr.some(val => val),
-          all: arr.every(val => val),
-          none: arr.every(val => !val),
-        }
-        this.#menuOpen$.next(newVal)
-
-        this.menuOpen = null
-        if (newVal.none) {
-          this.menuOpen = 'none'
-        }
-        if (newVal.all) {
-          this.menuOpen = 'all'
-        }
-        if (newVal.some) {
-          this.menuOpen = 'some'
-        }
-      })
-    )
-  }
+        )
+      ),
+      debounceTime(0),
+      takeUntil(this.#onDestroy$)
+    ).subscribe(arr => {
+      const newVal = {
+        some: arr.some(val => val),
+        all: arr.every(val => val),
+        none: arr.every(val => !val),
+      }
+      this.#menuOpen$.next(newVal)
 
-  ngOnDestroy(): void {
-    while (this.#subscriptions.length > 0) {
-      this.#subscriptions.pop().unsubscribe()
-    }
+      this.menuOpen = null
+      if (newVal.none) {
+        this.menuOpen = 'none'
+      }
+      if (newVal.all) {
+        this.menuOpen = 'all'
+      }
+      if (newVal.some) {
+        this.menuOpen = 'some'
+      }
+    })
   }
+
 }
diff --git a/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.template.html b/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.template.html
index 936214c582184df9395980f04bf938963c588a78..7ad281dc612baf43381c3308bc4729426196bf8c 100644
--- a/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.template.html
+++ b/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.template.html
@@ -1,31 +1,25 @@
 <ng-template [ngIf]="view$ | async" let-view>
   
   <!-- fallback for parcellation info icon -->
-  <ng-template [ngIf]="view.hideParcChip">
-    <button mat-icon-button
-      sxplr-dialog
-      [sxplr-dialog-size]="null"
-      [sxplr-dialog-data]="{
-        title: view.parcellation.name,
-        descMd: view.parcellation.desc,
-        actions: view.parcellation | parcTmplDoiPipe
-      }">
-      <i class="fas fa-info"></i>
-    </button>
-  </ng-template>
-
-  <!-- fallback for space info icon -->
-  <ng-template  [ngIf]="view.hideTmplChip">
-    <button mat-icon-button
-      sxplr-dialog
-      [sxplr-dialog-size]="null"
-      [sxplr-dialog-data]="{
-        title: view.template.name,
-        descMd: view.template.desc,
-        actions: view.template | parcTmplDoiPipe
-      }">
-      <i class="fas fa-info"></i>
-    </button>
+  <ng-template  [ngIf]="view.hideParcChip">
+    <sxplr-smart-chip
+      [items]="[]"
+      [noMenu]="true"
+      [color]="colorPalette[1]">
+      <ng-template sxplrSmartChipContent></ng-template>
+      <ng-template sxplrSmartChipAction>
+        <button mat-icon-button
+          sxplr-dialog
+          [sxplr-dialog-size]="null"
+          [sxplr-dialog-data]="{
+            title: view.parcellation.name,
+            descMd: view.parcellation.desc,
+            actions: view.parcellation | parcTmplDoiPipe
+          }">
+          <i class="fas fa-info"></i>
+        </button>
+      </ng-template>
+    </sxplr-smart-chip>
   </ng-template>
 
   <!-- parcellation smart chip -->
@@ -43,7 +37,7 @@
       </span>
 
       <span class="sxplr-ml-1 text-muted">
-        ({{ view.parcellations.length }})
+        ({{ view.parcellations?.length }})
       </span>
     </ng-template>
 
@@ -87,6 +81,29 @@
     </ng-template>
   </sxplr-smart-chip>
 
+  
+  <!-- fallback for space info icon -->
+  <ng-template  [ngIf]="view.hideTmplChip">
+    <sxplr-smart-chip
+      [items]="[]"
+      [noMenu]="true"
+      [color]="colorPalette[1]">
+      <ng-template sxplrSmartChipContent></ng-template>
+      <ng-template sxplrSmartChipAction>
+        <button mat-icon-button
+          sxplr-dialog
+          [sxplr-dialog-size]="null"
+          [sxplr-dialog-data]="{
+            title: view.template.name,
+            descMd: view.template.desc,
+            actions: view.template | parcTmplDoiPipe
+          }">
+          <i class="fas fa-info"></i>
+        </button>
+      </ng-template>
+    </sxplr-smart-chip>
+  </ng-template>
+
   <!-- space smart chip -->
   <sxplr-smart-chip *ngIf="!view.hideTmplChip"
     [items]="view.availableTemplates || []"
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 f7655e5aff437e1b6eebcb702469e8dbc9769548..c946baa2bdc922dfa6a11ebb339edf69d12f552f 100644
--- a/src/atlasComponents/sapiViews/core/rich/ATPSelector/wrapper/wrapper.component.ts
+++ b/src/atlasComponents/sapiViews/core/rich/ATPSelector/wrapper/wrapper.component.ts
@@ -1,4 +1,4 @@
-import { Component, EventEmitter, Inject, OnDestroy, Output } from "@angular/core";
+import { Component, EventEmitter, Inject, Input, OnDestroy, Output } from "@angular/core";
 import { MatDialog } from "src/sharedModules/angularMaterial.exports";
 import { select, Store } from "@ngrx/store";
 import { Observable, Subject, Subscription } from "rxjs";
@@ -6,15 +6,10 @@ import { switchMap, withLatestFrom } from "rxjs/operators";
 import { SAPI } from "src/atlasComponents/sapi/sapi.service";
 import { atlasAppearance, atlasSelection } from "src/state";
 import { fromRootStore } from "src/state/atlasSelection";
-import { DialogFallbackCmp } from "src/ui/dialogInfo";
 import { DARKTHEME } from "src/util/injectionTokens";
 import { ParcellationVisibilityService } from "../../../parcellation/parcellationVis.service";
 import { darkThemePalette, lightThemePalette, ATP } from "../pureDumb/pureATPSelector.components"
 
-type AskUserConfig = {
-  actionsAsList: boolean
-}
-
 @Component({
   selector: 'sxplr-wrapper-atp-selector',
   templateUrl: './wrapper.template.html',
@@ -25,6 +20,9 @@ type AskUserConfig = {
 
 export class WrapperATPSelector implements OnDestroy{
 
+  @Input('sxplr-wrapper-atp-selector-minimized')
+  minimized = true
+
   @Output('sxplr-wrapper-atp-selector-menu-open')
   menuOpen = new EventEmitter<{some: boolean, all: boolean, none: boolean}>()
 
@@ -33,18 +31,6 @@ export class WrapperATPSelector implements OnDestroy{
 
   #subscription: Subscription[] = []
 
-  #askUser(title: string, titleMd: string, descMd: string, actions: string[], config?: Partial<AskUserConfig>): Observable<string> {
-    return this.dialog.open(DialogFallbackCmp, {
-      data: {
-        title,
-        titleMd,
-        descMd,
-        actions: actions,
-        actionsAsList: config?.actionsAsList
-      }
-    }).afterClosed()
-  }
-
   selectedATP$ = this.store$.pipe(
     fromRootStore.distinctATP(),
   )
diff --git a/src/atlasComponents/sapiViews/core/rich/ATPSelector/wrapper/wrapper.template.html b/src/atlasComponents/sapiViews/core/rich/ATPSelector/wrapper/wrapper.template.html
index 72f3b51cc9e54f3f5e0dfaffcc40bb58b955b395..f18f0077fc2b2a0f5bfd9a42f0396fd913717bd6 100644
--- a/src/atlasComponents/sapiViews/core/rich/ATPSelector/wrapper/wrapper.template.html
+++ b/src/atlasComponents/sapiViews/core/rich/ATPSelector/wrapper/wrapper.template.html
@@ -1,4 +1,5 @@
 <sxplr-pure-atp-selector
+  [sxplr-pure-atp-selector-minimized]="minimized"
   [sxplr-pure-atp-selector-color-palette]="(darktheme$ | async) ? darkThemePalette : lightThemePalette"
   [sxplr-pure-atp-selector-selected-atp]="selectedATP$ | async"
   [sxplr-pure-atp-selector-atlases]="allAtlases$ | async"
diff --git a/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/filterByRegex.pipe.ts b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/filterByRegex.pipe.ts
deleted file mode 100644
index 5b1aaa4d71d1c4791cb09d83dbc5b00100523502..0000000000000000000000000000000000000000
--- a/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/filterByRegex.pipe.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-
-@Pipe({
-  name : 'filterByRegex',
-  pure: true,
-})
-
-export class FilterByRegexPipe implements PipeTransform {
-  public transform(searchFields: string[], searchTerm: string) {
-    try {
-      return searchFields.some(searchField => new RegExp(searchTerm, 'i').test(searchField))
-    } catch (e) {
-      // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
-      // CC0 or MIT
-      return searchFields.some(searchField => new RegExp(searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).test(searchField))
-    }
-  }
-}
diff --git a/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/highlight.pipe.ts b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/highlight.pipe.ts
index 73f3e3ddced7b1ab08f3c53e31e25b11f30d93d1..eca834c77d680113430d8eb35fb9928c187cc2c1 100644
--- a/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/highlight.pipe.ts
+++ b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/highlight.pipe.ts
@@ -10,19 +10,26 @@ export class HighlightPipe implements PipeTransform {
   
   constructor(private sanitizer: DomSanitizer){}
 
-  transform(input: string, highlight: string = ''): SafeHtml {
-    let regex: RegExp
-    if (highlight === '') return input
-    try {
-      regex = new RegExp(highlight, 'i')
-    } catch (e) {
-      // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
-      // CC0 or MIT
-      regex = new RegExp(highlight.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
+  transform(input: string, highlight: string = '', regexFlag=false): SafeHtml {
+    if (!highlight) return input
+    if (regexFlag) {
+      let regex: RegExp
+      try {
+        regex = new RegExp(highlight, 'i')
+      } catch (e) {
+        // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
+        // CC0 or MIT
+        regex = new RegExp(highlight.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
+      }
+      return this.sanitizer.sanitize(
+        SecurityContext.HTML,
+        input.replace(regex, s => `<mark>${s}</mark>`)
+      )
     }
+    const regex = new RegExp(highlight.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i')
     return this.sanitizer.sanitize(
       SecurityContext.HTML,
       input.replace(regex, s => `<mark>${s}</mark>`)
     )
   }
-}
\ No newline at end of file
+}
diff --git a/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionTreeFilter.pipe.ts b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionTreeFilter.pipe.ts
deleted file mode 100644
index b125458a82355e482fd9340ca3631d9aa198fded..0000000000000000000000000000000000000000
--- a/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionTreeFilter.pipe.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-
-@Pipe({
-  name : 'regionTreeFilter',
-  pure: true
-})
-
-export class RegionTreeFilterPipe implements PipeTransform {
-  public transform<T>(array: T[], filterFn: (item: T) => boolean, getChildren: (item: T) => T[]): T[] {
-    const transformSingle = (item: T): boolean =>
-      filterFn(item) || (getChildren(item) || []).some(transformSingle)
-      
-    return array
-      ? array.filter(transformSingle)
-      : []
-  }
-}
diff --git a/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.component.ts b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.component.ts
index 0c85164a964950a7dec142e1fb047ac1e1aa9106..690706285adf8c629569dc750b818cc16fe53e48 100644
--- a/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.component.ts
+++ b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.component.ts
@@ -1,14 +1,10 @@
-import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output, ViewChild } from "@angular/core";
-import { UntypedFormControl } from "@angular/forms";
-import { Subscription } from "rxjs";
-import { debounceTime, distinctUntilChanged, filter, startWith } from "rxjs/operators";
+import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, ViewChild } from "@angular/core";
+import { FormControl } from "@angular/forms";
+import { BehaviorSubject, combineLatest, concat, from, of, timer } from "rxjs";
+import { debounceTime, filter, map, shareReplay, switchMap, take, takeUntil, tap } from "rxjs/operators";
 import { SxplrRegion } from "src/atlasComponents/sapi/sxplrTypes";
 import { SxplrFlatHierarchyTreeView } from "src/components/flatHierarchy/treeView/treeView.component";
-import { FilterByRegexPipe } from "./filterByRegex.pipe";
-import { RegionTreeFilterPipe } from "./regionTreeFilter.pipe";
-
-const regionTreeFilterPipe = new RegionTreeFilterPipe()
-const filterByRegexPipe = new FilterByRegexPipe()
+import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service";
 
 @Component({
   selector: `sxplr-sapiviews-core-rich-regionshierarchy`,
@@ -20,23 +16,13 @@ const filterByRegexPipe = new FilterByRegexPipe()
 })
 
 export class SapiViewsCoreRichRegionsHierarchy {
+
   TXT_CANNOT_BE_SELECTED = "Not mapped in this template space."
 
   static IsParent(region: SxplrRegion, parentRegion: SxplrRegion): boolean {
     return region.parentIds.some(id => parentRegion.id === id)
   }
 
-  static FilterRegions(regions: SxplrRegion[], searchTerm: string): SxplrRegion[]{
-    if (searchTerm === '' || !searchTerm) {
-      return regions
-    }
-    return regionTreeFilterPipe.transform(
-      regions,
-      region => filterByRegexPipe.transform([ region.name ], searchTerm),
-      region => regions.filter(child => SapiViewsCoreRichRegionsHierarchy.IsParent(child, region))
-    )
-  }
-
   @Input('sxplr-sapiviews-core-rich-regionshierarchy-label-mapped-region-names')
   labelMappedRegionNames: string[] = []
 
@@ -46,19 +32,16 @@ export class SapiViewsCoreRichRegionsHierarchy {
   @Input('sxplr-sapiviews-core-rich-regionshierarchy-placeholder')
   placeholderText: string = 'Search all regions'
 
-  passedRegions: SxplrRegion[] = []
-
-  private _regions: SxplrRegion[] = []
-  get regions(){
-    return this._regions
+  @Input('sxplr-sapiviews-core-rich-regionshierarchy-searchstring')
+  set initialSearchTerm(val: string) {
+    this.#initialSearchTerm.next(val)
   }
+  #initialSearchTerm = new BehaviorSubject<string>(null)
+
+  #allAvailableRegions$ = new BehaviorSubject<SxplrRegion[]>([])
   @Input('sxplr-sapiviews-core-rich-regionshierarchy-regions')
   set regions(val: SxplrRegion[]){
-    this._regions = val
-    this.passedRegions = SapiViewsCoreRichRegionsHierarchy.FilterRegions(
-      this._regions,
-      this.searchTerm
-    )
+    this.#allAvailableRegions$.next(val)
   }
 
   @Output('sxplr-sapiviews-core-rich-regionshierarchy-region-select')
@@ -72,36 +55,53 @@ export class SapiViewsCoreRichRegionsHierarchy {
 
   isParent = SapiViewsCoreRichRegionsHierarchy.IsParent
 
-  searchFormControl = new UntypedFormControl()
+  searchFormControl = new FormControl<string>('')
+
+  searchTerm$ = concat(
+    of(null as string),
+    this.#initialSearchTerm.pipe(
+      filter(v => !!v),
+      take(1),
+      tap(val => {
+        if (val) {
+          this.searchFormControl.setValue(val)
+        }
+      }),
+      takeUntil(timer(160)),
+    ),
+    this.searchFormControl.valueChanges,
+  ).pipe(
+    shareReplay(1),
+  )
+
+  #filteredRegions$ = combineLatest([
+    this.searchTerm$,
+    this.#allAvailableRegions$,
+  ]).pipe(
+    debounceTime(320),
+    switchMap(([ searchTerm, regions ]) => {
+      return from(
+        this.worker.sendMessage({
+          method: "FILTER_REGIONS",
+          param: { regions, searchTerm }
+        })
+      ).pipe(
+        map(({ result }) => {
+          const { filteredRegions } = result
+          const { regions, dups } = filteredRegions as Record<string, SxplrRegion[]>
+          return { regions, dups }
+        })
+      )
+    }),
+    shareReplay(1)
+  )
   
-  searchTerm: string
-
-  constructor(
-    private cdr: ChangeDetectorRef
-  ){
-    this.subs.push(
-      this.searchFormControl.valueChanges.pipe(
-        startWith(''),
-        distinctUntilChanged(),
-        debounceTime(320),
-        /**
-         * empty string should trigger search
-         * showing all regions
-         */
-        filter(val => val === '' || !!val)
-      ).subscribe(val => {
-        this.searchTerm = val
-        this.passedRegions = SapiViewsCoreRichRegionsHierarchy.FilterRegions(
-          this._regions,
-          this.searchTerm
-        )
-        this.cdr.markForCheck()
-      })
-    )
-  }
+  passedRegions$ = this.#filteredRegions$.pipe(
+    map(({ regions }) => regions)
+  )
+
+  constructor(private worker: AtlasWorkerService){}
 
-  private subs: Subscription[] = []
-  
   onNodeClick({node: roi, event }: {node: SxplrRegion, event: MouseEvent}){
     /**
      * Only allow the regions that are labelled mapped to be selected.
diff --git a/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.template.html b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.template.html
index fe58b7efcf598036e2fbae302f54720a9d0369d0..6591f3a198f35fd55ca738ef329936e5f3df8c43 100644
--- a/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.template.html
+++ b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.template.html
@@ -21,12 +21,12 @@
       'sxplr-custom-cmp accent': accentedRegions | includes : region,
       'muted-7': !labelMappedRegionNames.includes(region.name)
     }"
-    [innerHTML]="region.name | hightlightPipe : searchTerm">
+    [innerHTML]="region.name | hightlightPipe : (searchTerm$ | async)">
   </div>
 </ng-template>
 
 <sxplr-flat-hierarchy-tree-view
-  [sxplr-flat-hierarchy-nodes]="passedRegions"
+  [sxplr-flat-hierarchy-nodes]="passedRegions$ | async"
   [sxplr-flat-hierarchy-is-parent]="isParent"
   [sxplr-flat-hierarchy-render-node-tmpl]="tmplRef"
   [sxplr-flat-hierarchy-tree-view-expand-on-init]="true"
diff --git a/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.component.ts b/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.component.ts
index 0fa6f0384a42726f0f24725ba362037a965308ce..23dd87062e1f5e9f127488f0377b5875c662de0c 100644
--- a/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.component.ts
+++ b/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.component.ts
@@ -1,30 +1,53 @@
-import { ChangeDetectionStrategy, Component, ContentChild, EventEmitter, HostListener, Input, Output, TemplateRef } from "@angular/core";
+import { ChangeDetectionStrategy, Component, ContentChild, EventEmitter, HostListener, Input, Output, TemplateRef, ViewChild, inject } from "@angular/core";
 import { SxplrRegion } from "src/atlasComponents/sapi/sxplrTypes";
 import { ARIA_LABELS } from "common/constants"
-import { UntypedFormControl } from "@angular/forms";
-import { debounceTime, distinctUntilChanged, map, startWith } from "rxjs/operators";
+import { FormControl } from "@angular/forms";
+import { debounceTime, distinctUntilChanged, filter, map, shareReplay, takeUntil } from "rxjs/operators";
 import { MatAutocompleteSelectedEvent } from 'src/sharedModules/angularMaterial.exports'
 import { SapiViewsCoreRichRegionListTemplateDirective } from "./regionListSearchTmpl.directive";
-import { BehaviorSubject, combineLatest } from "rxjs";
+import { BehaviorSubject, combineLatest, concat, of } from "rxjs";
+import { DestroyDirective } from "src/util/directives/destroy.directive";
+import { MatAutocompleteTrigger } from "@angular/material/autocomplete";
 
 const filterRegionViaSearch = (searchTerm: string) => (region:SxplrRegion) => {
   return region.name.toLocaleLowerCase().includes(searchTerm.toLocaleLowerCase())
 }
 
+type RegionExtra = {
+  extra: {
+    showMore?: true
+    noneFound?: true
+  }
+}
+
+function isExtra(input: unknown): input is RegionExtra{
+  return !!(input['extra'])
+}
+
+function filterGetIsNotExtra(input: SxplrRegion|string|RegionExtra): input is SxplrRegion|string{
+  return !isExtra(input)
+}
+
 @Component({
   selector: `sxplr-sapiviews-core-rich-regionlistsearch`,
   templateUrl: './regionListSearch.template.html',
   styleUrls: [
     `./regionListSearch.style.css`
   ],
-  changeDetection: ChangeDetectionStrategy.OnPush
+  changeDetection: ChangeDetectionStrategy.OnPush,
+  exportAs: "sapiRegionListSearch",
+  hostDirectives: [
+    DestroyDirective,
+  ]
 })
 
 export class SapiViewsCoreRichRegionListSearch {
 
+  readonly #ondestroy$ = inject(DestroyDirective).destroyed$
+
   ARIA_LABELS = ARIA_LABELS
 
-  showNOptions = 4
+  showNOptions = Number.POSITIVE_INFINITY
 
   #regions = new BehaviorSubject<SxplrRegion[]>([])
   @Input('sxplr-sapiviews-core-rich-regionlistsearch-regions')
@@ -32,11 +55,8 @@ export class SapiViewsCoreRichRegionListSearch {
     this.#regions.next(reg)
   }
 
-  #mappedRegionNames = new BehaviorSubject<string[]>([])
-  @Input('sxplr-sapiviews-core-rich-regionlistsearch-mapped-region-names')
-  set mappedRegions(regNames: string[]) {
-    this.#mappedRegionNames.next(regNames)
-  }
+  @ViewChild(MatAutocompleteTrigger)
+  autoComplete: MatAutocompleteTrigger
 
   @ContentChild(SapiViewsCoreRichRegionListTemplateDirective)
   regionTmplDirective: SapiViewsCoreRichRegionListTemplateDirective
@@ -53,27 +73,63 @@ export class SapiViewsCoreRichRegionListSearch {
   @Output('sxplr-sapiviews-core-rich-regionlistsearch-region-toggle')
   onRegionToggle = new EventEmitter<SxplrRegion>()
 
-  public searchFormControl = new UntypedFormControl()
+  @Output('sxplr-sapiviews-core-rich-regionlistsearch-region-select-extra')
+  onRegionShowCustomSearch = new EventEmitter<string>()
+
+  #searchTerm: string = ""
+
+  constructor(){
+    this.searchTerm$.pipe(
+      takeUntil(this.#ondestroy$)
+    ).subscribe(searchTerm => {
+      if (typeof searchTerm === "string") {
+        this.#searchTerm = searchTerm
+        return
+      }
+    })
+  }
+
+  public searchFormControl = new FormControl<string|SxplrRegion|RegionExtra>(null)
+
+  searchTerm$ = this.searchFormControl.valueChanges.pipe(
+    filter(filterGetIsNotExtra),
+    distinctUntilChanged(),
+    debounceTime(160),
+  )
+
+  searchTermString$ = this.searchTerm$.pipe(
+    map(val => {
+      if (typeof val === "string") {
+        return val
+      }
+      if (isExtra(val)) {
+        return null
+      }
+      return val.name
+    }),
+    filter(val => val !== null),
+    shareReplay(1),
+  )
 
   public searchedList$ = combineLatest([
-    this.searchFormControl.valueChanges.pipe(
-      startWith(''),
-      distinctUntilChanged(),
-      debounceTime(160),
+    concat(
+      of(''),
+      this.searchTerm$,
     ),
     this.#regions,
-    this.#mappedRegionNames
   ]).pipe(
-    map(([searchTerm, regions, mappedRegionNames]) => {
+    map(([searchTerm, regions]) => {
+      let searchString = ""
       if (typeof searchTerm === "string") {
-        return regions.filter(r => mappedRegionNames.includes(r.name)).filter(filterRegionViaSearch(searchTerm))
+        searchString = searchTerm
+      } else {
+        searchString = searchTerm.name
       }
-      return []
+      return regions.filter(filterRegionViaSearch(searchString))
     })
   )
 
   public autocompleteList$ = this.searchedList$.pipe(
-    map(list => list.slice(0, this.showNOptions))
   )
 
   displayFn(region: SxplrRegion){
@@ -81,7 +137,20 @@ export class SapiViewsCoreRichRegionListSearch {
   }
 
   optionSelected(opt: MatAutocompleteSelectedEvent) {
-    const selectedRegion = opt.option.value as SxplrRegion
+    const selectedRegion = opt.option.value as (SxplrRegion | RegionExtra)
+    if (isExtra(selectedRegion)) {
+      if (selectedRegion.extra.noneFound) {
+        this.onRegionShowCustomSearch.emit("")
+        return
+      }
+      if (selectedRegion.extra.showMore) {
+        this.onRegionShowCustomSearch.emit(this.#searchTerm || "")
+        return
+      }
+      
+      return
+    }
+    
     if (this.ctrlFlag) {
       this.onRegionToggle.emit(selectedRegion)
     } else {
@@ -100,4 +169,8 @@ export class SapiViewsCoreRichRegionListSearch {
   keyup(event: KeyboardEvent) {
     this.ctrlFlag = event.ctrlKey
   }
+
+  dismissAutoComplete(){
+    this.autoComplete?.closePanel()
+  }
 }
diff --git a/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.template.html b/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.template.html
index fd9edae6072eb4e5ae89cfeb590cf19e3865a111..92e413e062af501ab83ee305c4c9817ea7f134e6 100644
--- a/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.template.html
+++ b/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.template.html
@@ -24,8 +24,8 @@
 </mat-form-field>
 
 <mat-autocomplete
-  panelWidth="auto"
   (optionSelected)="optionSelected($event)"
+  [hideSingleSelectionIndicator]="true"
   autoActiveFirstOption
   #auto="matAutocomplete"
   [displayWith]="displayFn">
@@ -47,7 +47,9 @@
   <ng-template [ngIf]="searchedList$ | async" let-searchedList>
 
     <mat-option *ngIf="searchedList.length > showNOptions"
-      [disabled]="true">
+      [value]="{
+        extra: { showMore: true }
+      }">
 
       <ng-template [ngIf]="regionTmplDirective">
 
@@ -61,5 +63,23 @@
         </ng-template>
       </ng-template>
     </mat-option>
+
+    <mat-option *ngIf="searchedList.length === 0"
+      [value]="{
+        extra: { noneFound: true }
+      }">
+
+      <ng-template [ngIf]="regionTmplDirective">
+
+        <ng-template
+          [ngTemplateOutlet]="regionTmplDirective.tmplRef"
+          [ngTemplateOutletContext]="{
+            $implicit: {
+              name: 'None found.'
+            }
+          }">
+        </ng-template>
+      </ng-template>
+    </mat-option>
   </ng-template>
 </mat-autocomplete>
\ No newline at end of file
diff --git a/src/components/flatHierarchy/treeView/treeView.component.ts b/src/components/flatHierarchy/treeView/treeView.component.ts
index e38281e448d3efb63c66db03dea0e109cf3ce951..994a8d6188cc5388c0a0590df5523a37cc1fd043 100644
--- a/src/components/flatHierarchy/treeView/treeView.component.ts
+++ b/src/components/flatHierarchy/treeView/treeView.component.ts
@@ -44,7 +44,7 @@ export class SxplrFlatHierarchyTreeView<T extends Record<string, unknown>> exten
 
   ngOnChanges(changes: SimpleChanges): void {
     if (changes.sxplrNodes || changes.sxplrIsParent) {
-      this.nodes = this.sxplrNodes
+      this.nodes = this.sxplrNodes || []
       this.isParent = this.sxplrIsParent
       this.dataSource.data = this.rootNodes
       if (this.expandOnInit) {
diff --git a/src/index.html b/src/index.html
index 4be80ba95d35bd074ee4421c2436e089fd04d5d8..38aa50647e838391744f198662c64f1addece15a 100644
--- a/src/index.html
+++ b/src/index.html
@@ -9,7 +9,6 @@
   <link rel="stylesheet" href="assets/fontawesome/css/all.min.css">
   <link rel="stylesheet" href="assets/academicons/css/academicons.min.css">
   <link rel="stylesheet" href="icons/iav-icons.css">
-  <link rel="stylesheet" href="main.css">
   <link rel="stylesheet" href="version.css">
   <link rel="icon" type="image/png" href="assets/favicons/favicon-128-light.png"/>
   <script src="extra_js.js"></script>
diff --git a/src/screenshot/screenshotCmp/screenshot.template.html b/src/screenshot/screenshotCmp/screenshot.template.html
index ee570082170d036ee4429f425ce4f0d112255d79..760053de49ffc1af6835d9e2d21d02730d33389c 100644
--- a/src/screenshot/screenshotCmp/screenshot.template.html
+++ b/src/screenshot/screenshotCmp/screenshot.template.html
@@ -58,16 +58,12 @@
       <span class="ml-1">Save</span>
     </a>
 
-    <button mat-stroked-button
-      color="default"
-      mat-dialog-close="try again">
+    <button mat-stroked-button mat-dialog-close="try again">
       <i class="fas fa-camera"></i>
       <span class="ml-1">Try again</span>
     </button>
 
-    <button mat-button
-      color="default"
-      mat-dialog-close="cancel">
+    <button mat-button mat-dialog-close="cancel">
       Cancel
     </button>
   </mat-dialog-actions>
diff --git a/src/ui/dialogInfo/dialog.directive.ts b/src/ui/dialogInfo/dialog.directive.ts
index 48d8daf656d8958bc2e9a494a4724cdd7a583b90..9eb8a06d26a277151675a7652d11c14f300e591f 100644
--- a/src/ui/dialogInfo/dialog.directive.ts
+++ b/src/ui/dialogInfo/dialog.directive.ts
@@ -38,18 +38,19 @@ export class DialogDirective{
   size: DialogSize = 'm'
 
   @Input('sxplr-dialog-data')
-  data: unknown
+  data: any = {}
 
   constructor(private matDialog: MatDialog){}
 
   @HostListener('click')
-  onClick(){
+  onClick(data: any={}){
     const tmpl = this.templateRef instanceof TemplateRef
       ? this.templateRef
       : DialogFallbackCmp
 
     this.matDialog.open(tmpl, {
-      data: this.data,
+      autoFocus: null,
+      data: {...this.data, ...data},
       ...(sizeDict[this.size] || {})
     })
   }
diff --git a/src/util/includes.pipe.ts b/src/util/includes.pipe.ts
index 2625cd6d3c2d0794ef5ca5d0e54b53a056617357..8b67ad8737288742ae5d3ced9159248c1e80d433 100644
--- a/src/util/includes.pipe.ts
+++ b/src/util/includes.pipe.ts
@@ -1,13 +1,13 @@
 import { Pipe, PipeTransform } from "@angular/core";
 
-const defaultCompareFn = (item: any, comparator: any): boolean => item === comparator
+const defaultCompareFn = <T>(item: T, comparator: T): boolean => item === comparator
 
 @Pipe({
   name: 'includes',
 })
 
-export class IncludesPipe implements PipeTransform {
-  public transform(array: any[], item: any, compareFn = defaultCompareFn): boolean {
+export class IncludesPipe<T> implements PipeTransform {
+  public transform(array: T[], item: T, compareFn: (a: T, b:T) => boolean = defaultCompareFn): boolean {
     if (!array) { return false }
     if (!(array instanceof Array)) { return false }
     return array.some(it => compareFn(it, item))
diff --git a/src/viewerModule/viewerCmp/viewerCmp.component.ts b/src/viewerModule/viewerCmp/viewerCmp.component.ts
index 6ddd87d449d29782eca4b0efd75b4faf9c0242e2..eb91a6b8b31ce641966d34374d6dbc28282cce31 100644
--- a/src/viewerModule/viewerCmp/viewerCmp.component.ts
+++ b/src/viewerModule/viewerCmp/viewerCmp.component.ts
@@ -16,6 +16,10 @@ import { EntryComponent } from "src/features/entry/entry.component";
 import { TFace, TSandsPoint, getCoord } from "src/util/types";
 import { wait } from "src/util/fn";
 
+interface HasName {
+  name: string
+}
+
 @Component({
   selector: 'iav-cmp-viewer-container',
   templateUrl: './viewerCmp.template.html',
@@ -162,9 +166,10 @@ export class ViewerCmp implements OnDestroy {
 
   #view1$ = combineLatest([
     this.#currentMap$,
+    this.allAvailableRegions$,
   ]).pipe(
-    map(( [ currentMap ] ) => ({
-      currentMap
+    map(( [ currentMap, allAvailableRegions ] ) => ({
+      currentMap, allAvailableRegions
     }))
   )
 
@@ -173,7 +178,7 @@ export class ViewerCmp implements OnDestroy {
     this.#view1$,
   ]).pipe(
     map(([v0, v1]) => ({ ...v0, ...v1 })),
-    map(({ selectedRegions, viewerMode, selectedFeature, selectedPoint, selectedTemplate, selectedParcellation, currentMap }) => {
+    map(({ selectedRegions, viewerMode, selectedFeature, selectedPoint, selectedTemplate, selectedParcellation, currentMap, allAvailableRegions }) => {
       let spatialObjectTitle: string
       let spatialObjectSubtitle: string
       if (selectedPoint) {
@@ -189,6 +194,8 @@ export class ViewerCmp implements OnDestroy {
         spatialObjectSubtitle = selectedTemplate.name
       }
 
+      const parentIds = new Set(allAvailableRegions.flatMap(v => v.parentIds))
+
       const labelMappedRegionNames = currentMap && Object.keys(currentMap.indices) || []
       return {
         viewerMode,
@@ -198,6 +205,9 @@ export class ViewerCmp implements OnDestroy {
         selectedTemplate,
         selectedParcellation,
         labelMappedRegionNames,
+        allAvailableRegions,
+        leafRegions: allAvailableRegions.filter(r => !parentIds.has(r.id)),
+        branchRegions: allAvailableRegions.filter(r => parentIds.has(r.id)),
 
         /**
          * Selected Spatial Object
@@ -358,7 +368,7 @@ export class ViewerCmp implements OnDestroy {
     )
   }
 
-  public selectRoi(roi: SxplrRegion): void {
+  public selectRoi(roi: SxplrRegion) {
     this.store$.dispatch(
       atlasSelection.actions.selectRegion({
         region: roi
@@ -519,4 +529,8 @@ export class ViewerCmp implements OnDestroy {
       })
     )
   }
+
+  nameEql(a: HasName, b: HasName){
+    return a.name === b.name
+  }
 }
diff --git a/src/viewerModule/viewerCmp/viewerCmp.style.css b/src/viewerModule/viewerCmp/viewerCmp.style.css
index f1ba346a219f1b1b460a0c44c23004d0dae57bf9..13f9f9906cce4a7d44d78ea843912dd5083fb4ab 100644
--- a/src/viewerModule/viewerCmp/viewerCmp.style.css
+++ b/src/viewerModule/viewerCmp/viewerCmp.style.css
@@ -175,3 +175,9 @@ sxplr-sapiviews-core-region-region-list-item
   margin-top: 0.5rem;
   margin-bottom: 0.5rem;
 }
+
+.region-list-search-row
+{
+  display: flex;
+  align-items: center;
+}
diff --git a/src/viewerModule/viewerCmp/viewerCmp.template.html b/src/viewerModule/viewerCmp/viewerCmp.template.html
index e31ad27551e54d3d5d4d7ff1bfdc599d9edc869a..118e5c2892d7498ddb47bb3d5d740666aee18160 100644
--- a/src/viewerModule/viewerCmp/viewerCmp.template.html
+++ b/src/viewerModule/viewerCmp/viewerCmp.template.html
@@ -23,7 +23,6 @@
       </mat-list>
     </div>
 
-
   </div>
 </div>
 
@@ -509,28 +508,6 @@
   </div>
 </ng-template>
 
-<!-- region-hierarchy-tmpl -->
-
-<ng-template #regionHierarchyTmpl>
-  <ng-template [ngIf]="view$ | async" let-view>
-
-    <div class="sxplr-d-flex sxplr-flex-column sxplr-h-100">
-      <sxplr-sapiviews-core-rich-regionshierarchy
-        class="sxplr-w-100 sxplr-flex-var"
-        [sxplr-sapiviews-core-rich-regionshierarchy-regions]="allAvailableRegions$ | async"
-        [sxplr-sapiviews-core-rich-regionshierarchy-label-mapped-region-names]="view.labelMappedRegionNames"
-        [sxplr-sapiviews-core-rich-regionshierarchy-accent-regions]="view.selectedRegions"
-        (sxplr-sapiviews-core-rich-regionshierarchy-region-select)="selectRoi($event)"
-        (sxplr-sapiviews-core-rich-regionshierarchy-region-toggle)="toggleRoi($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>
-</ng-template>
 
 <!-- auto complete search box -->
 <ng-template #autocompleteTmpl let-showTour="showTour">
@@ -538,28 +515,35 @@
     class="pe-all auto-complete-container">
     <sxplr-sapiviews-core-rich-regionlistsearch
       class="mat-elevation-z4"
-      [sxplr-sapiviews-core-rich-regionlistsearch-regions]="allAvailableRegions$ | async"
-      [sxplr-sapiviews-core-rich-regionlistsearch-mapped-region-names]="view.labelMappedRegionNames"
+      [sxplr-sapiviews-core-rich-regionlistsearch-regions]="view.allAvailableRegions"
       [sxplr-sapiviews-core-rich-regionlistsearch-current-search]="view.selectedRegions.length === 1 ? view.selectedRegions[0].name : null"
-      (sxplr-sapiviews-core-rich-regionlistsearch-region-select)="selectRoi($event)"
-      (sxplr-sapiviews-core-rich-regionlistsearch-region-toggle)="toggleRoi($event)">
+      (sxplr-sapiviews-core-rich-regionlistsearch-region-select)="view.leafRegions.includes($event) ? selectRoi($event) : showHierarchyBtn.onClick({ 'searchTerm': $event.name })"
+      (sxplr-sapiviews-core-rich-regionlistsearch-region-toggle)="view.leafRegions.includes($event) ? toggleRoi($event) : showHierarchyBtn.onClick({ 'searchTerm': $event.name })"
+      (sxplr-sapiviews-core-rich-regionlistsearch-region-select-extra)="showHierarchyBtn.onClick({ 'searchTerm': $event })"
+      #regionListSearch="sapiRegionListSearch">
       <ng-template regionTemplate let-region>
-        <div class="sxplr-d-flex">
-          <button
-            mat-icon-button
-            class="sxplr-mt-a sxplr-mb-a">
-            <i [ngClass]="(view.selectedRegions | includes : region) ? 'fa-circle' : 'fa-none'" class="fas"></i>
-          </button>
+        <div class="region-list-search-row">
+          <span>
+            <i [ngClass]="(view.selectedRegions | includes : region: nameEql) ? 'fa-circle' : 'fa-none'" class="fas"></i>
+          </span>
 
           <sxplr-sapiviews-core-region-region-list-item
-            [sxplr-sapiviews-core-region-region]="region">
+            [sxplr-sapiviews-core-region-region]="region"
+            [ngClass]="{
+              'text-muted': !view.labelMappedRegionNames.includes(region.name)
+            }">
+            <span prefix class="sxplr-m-2">
+              <i *ngIf="view.leafRegions.includes(region)" class="fas fa-brain"></i>
+              <i *ngIf="view.branchRegions.includes(region)" class="fas fa-code-branch"></i>
+            </span>
+            
           </sxplr-sapiviews-core-region-region-list-item>
         </div>
       </ng-template>
       <button mat-icon-button
         search-input-suffix
         *ngIf="view.selectedRegions.length > 0"
-        (click)="clearRoi()">
+        (click)="clearRoi(); regionListSearch.dismissAutoComplete()">
         <i class="fas fa-times"></i>
       </button>
       <button mat-icon-button
@@ -567,9 +551,38 @@
         search-input-prefix
         iav-stop="click"
         [sxplr-dialog]="regionHierarchyTmpl"
-        sxplr-dialog-size="xl">
+        sxplr-dialog-size="xl"
+        #showHierarchyBtn="sxplrDialog">
         <i class="fas fa-sitemap"></i>
       </button>
+
+      <!-- region-hierarchy-tmpl -->
+      
+      <ng-template #regionHierarchyTmpl let-data>
+        <ng-template [ngIf]="view$ | async" let-view>
+      
+          <div class="sxplr-d-flex sxplr-flex-column sxplr-h-100">
+            <div class="sxplr-m-2">
+              <sxplr-wrapper-atp-selector [sxplr-wrapper-atp-selector-minimized]="false" >
+              </sxplr-wrapper-atp-selector>
+            </div>
+            <sxplr-sapiviews-core-rich-regionshierarchy
+              class="sxplr-w-100 sxplr-flex-var"
+              [sxplr-sapiviews-core-rich-regionshierarchy-searchstring]="data?.searchTerm || (regionListSearch.searchTermString$ | async)"
+              [sxplr-sapiviews-core-rich-regionshierarchy-regions]="view.allAvailableRegions"
+              [sxplr-sapiviews-core-rich-regionshierarchy-label-mapped-region-names]="view.labelMappedRegionNames"
+              [sxplr-sapiviews-core-rich-regionshierarchy-accent-regions]="view.selectedRegions"
+              (sxplr-sapiviews-core-rich-regionshierarchy-region-select)="selectRoi($event)"
+              (sxplr-sapiviews-core-rich-regionshierarchy-region-toggle)="toggleRoi($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>
+      </ng-template>
       
     </sxplr-sapiviews-core-rich-regionlistsearch>
 
diff --git a/third_party/vanilla.html b/third_party/vanilla.html
index 5513962d41c1a6cb07e54a164e06a6bae15784f5..c86a671744ed470edbc621f572821e39d248eb78 100644
--- a/third_party/vanilla.html
+++ b/third_party/vanilla.html
@@ -8,7 +8,6 @@
   
   <script src="main.bundle.js"></script>
   <link rel="stylesheet" href="vanilla_styles.css">
-  <link rel="stylesheet" href="main.css">
   <link rel="stylesheet" href="vanillaMain.css">
 </head>
 <body>
diff --git a/worker/worker-regionFilter.js b/worker/worker-regionFilter.js
new file mode 100644
index 0000000000000000000000000000000000000000..1f35a0853b71814440f376a3b3f9857f3df72d47
--- /dev/null
+++ b/worker/worker-regionFilter.js
@@ -0,0 +1,111 @@
+/**
+ * @typedef SxplrRegionPartial
+ * @type {object}
+ * @property {Array.<string>} parentIds
+ * @property {string} name
+ * @property {string} id
+ */
+
+(function(exports){
+  const FUSE = 100
+  /**
+   * 
+   * @param {Array.<SxplrRegionPartial>} regions 
+   * @returns {Array.<SxplrRegionPartial>}
+   */
+  function findDup(regions){
+    return regions.filter(region => regions.filter(r => region.name === r.name).length > 1)
+  }
+
+  exports.filterRegion = {
+    /**
+     * 
+     * @param {Array.<SxplrRegionPartial>} regions 
+     * @param {string} searchTerm 
+     * @returns 
+     */
+    filterRegion(regions, searchTerm){
+      const dups = findDup(regions)
+      if (!searchTerm) return {regions, dups}
+
+      const regex = new RegExp(searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i')
+
+      /**
+       * 
+       * @param {SxplrRegionPartial} region 
+       * @param {string} searchTerm 
+       * @returns {boolean}
+       */
+      function filterRegionName(region){
+        return regex.test(region.name)
+      }
+
+      /**
+       * 
+       * @param {SxplrRegionPartial} parent 
+       * @returns {Array.<SxplrRegionPartial>}
+       */
+      function getChildren(parent){
+        return regions.filter(r => r.parentIds.includes(parent.id))
+      }
+      /**
+       * 
+       * @param {SxplrRegionPartial} parent 
+       * @returns {Array.<SxplrRegionPartial>}
+       */
+      function getParent(child){
+        return regions.filter(r => child.parentIds.includes(r.id))
+      }
+      /**
+       * 
+       * @param {SxplrRegionPartial} item 
+       * @returns {boolean}
+       */
+      const transformSingle = (item) => {
+        const allParents = []
+        const allChildren = []
+        let currItemParents = [item]
+        let currItemChildren = [item]
+        let breakParent = false
+        let breakChildren = false
+        let iter = 0
+        // eslint-disable-next-line no-constant-condition
+        while (true) {
+          iter ++
+          if (iter > FUSE || (breakParent && breakChildren)) {
+            break
+          }
+          if (!breakParent) {
+            const parents = currItemParents.map(getParent).flatMap(v => v)
+            if (parents.length === 0) {
+              breakParent = true
+            }
+            currItemParents = parents
+            allParents.push(...currItemParents)
+          }
+          if (!breakChildren) {
+            const children = currItemChildren.map(getChildren).flatMap(v => v)
+            if (children.length === 0) {
+              breakChildren = true
+            }
+            currItemChildren = children
+            allChildren.push(...currItemChildren)
+          }
+        }
+        return (
+          // if self is filtered true
+          filterRegionName(item)
+          // if any children is filtered true
+          || allChildren.some(filterRegionName)
+          // if any parent is filtered true
+          || allParents.some(filterRegionName)
+        )
+      }
+        
+      const filteredRegions = regions
+        ? regions.filter(transformSingle)
+        : []
+      return { regions: filteredRegions, dups }
+    }
+  }
+})(typeof exports === 'undefined' ? self : exports)
diff --git a/worker/worker.js b/worker/worker.js
index 314e0838032142545197a572ce968eb0320d7ed1..1e825881efdd18e5e3b789c34d376f9903953e4e 100644
--- a/worker/worker.js
+++ b/worker/worker.js
@@ -10,6 +10,7 @@ globalThis.constants = {
 if (typeof self.importScripts === 'function')  self.importScripts('./worker-plotly.js')
 if (typeof self.importScripts === 'function')  self.importScripts('./worker-nifti.js')
 if (typeof self.importScripts === 'function')  self.importScripts('./worker-typedarray.js')
+if (typeof self.importScripts === 'function')  self.importScripts('./worker-regionFilter.js')
 
 
 const VALID_METHOD = {
@@ -19,6 +20,7 @@ const VALID_METHOD = {
   PROCESS_TYPED_ARRAY_F2RGBA: `PROCESS_TYPED_ARRAY_F2RGBA`,
   PROCESS_TYPED_ARRAY_CM2RGBA: "PROCESS_TYPED_ARRAY_CM2RGBA",
   PROCESS_TYPED_ARRAY_RAW: "PROCESS_TYPED_ARRAY_RAW",
+  FILTER_REGIONS: "FILTER_REGIONS"
 }
 
 const VALID_METHODS = [
@@ -28,6 +30,7 @@ const VALID_METHODS = [
   VALID_METHOD.PROCESS_TYPED_ARRAY_F2RGBA,
   VALID_METHOD.PROCESS_TYPED_ARRAY_CM2RGBA,
   VALID_METHOD.PROCESS_TYPED_ARRAY_RAW,
+  VALID_METHOD.FILTER_REGIONS,
 ]
 
 const encoder = new TextEncoder()
@@ -41,8 +44,8 @@ onmessage = (message) => {
   if (message.data.type === 'webpackOk') return
 
   if (message.data.method && VALID_METHODS.indexOf(message.data.method) >= 0) {
-    const { id } = message.data
-    if (message.data.method === VALID_METHOD.PROCESS_PLOTLY) {
+    const { id, method, param } = message.data || {}
+    if (method === VALID_METHOD.PROCESS_PLOTLY) {
       try {
         if (plotyVtkUrl) URL.revokeObjectURL(plotyVtkUrl)
         const { data: plotlyData } = message.data.param
@@ -71,9 +74,9 @@ onmessage = (message) => {
       }
     }
 
-    if (message.data.method === VALID_METHOD.PROCESS_NIFTI) {
+    if (method === VALID_METHOD.PROCESS_NIFTI) {
       try {
-        const { nifti } = message.data.param
+        const { nifti } = param
         const {
           meta,
           buffer
@@ -96,9 +99,9 @@ onmessage = (message) => {
         })
       }
     }
-    if (message.data.method === VALID_METHOD.PROCESS_TYPED_ARRAY) {
+    if (method === VALID_METHOD.PROCESS_TYPED_ARRAY) {
       try {
-        const { inputArray, dtype, width, height, channel } = message.data.param
+        const { inputArray, dtype, width, height, channel } = param
         const array = self.typedArray.packNpArray(inputArray, dtype, width, height, channel)
 
         postMessage({
@@ -117,9 +120,9 @@ onmessage = (message) => {
         })
       }
     }
-    if (message.data.method === VALID_METHOD.PROCESS_TYPED_ARRAY_F2RGBA) {
+    if (method === VALID_METHOD.PROCESS_TYPED_ARRAY_F2RGBA) {
       try {
-        const { inputArray, width, height, channel } = message.data.param
+        const { inputArray, width, height, channel } = param
         const buffer = self.typedArray.fortranToRGBA(inputArray, width, height, channel)
 
         postMessage({
@@ -138,9 +141,9 @@ onmessage = (message) => {
         })
       }
     }
-    if (message.data.method === VALID_METHOD.PROCESS_TYPED_ARRAY_CM2RGBA) {
+    if (method === VALID_METHOD.PROCESS_TYPED_ARRAY_CM2RGBA) {
       try {
-        const { inputArray, width, height, channel, dtype, processParams } = message.data.param
+        const { inputArray, width, height, channel, dtype, processParams } = param
         const { buffer, min, max } = self.typedArray.cm2rgba(inputArray, width, height, channel, dtype, processParams)
 
         postMessage({
@@ -161,9 +164,9 @@ onmessage = (message) => {
         })
       }
     }
-    if (message.data.method === VALID_METHOD.PROCESS_TYPED_ARRAY_RAW) {
+    if (method === VALID_METHOD.PROCESS_TYPED_ARRAY_RAW) {
       try {
-        const { inputArray, width, height, channel, dtype, processParams } = message.data.param
+        const { inputArray, width, height, channel, dtype, processParams } = param
         const { outputArray, min, max } = self.typedArray.rawArray(inputArray, width, height, channel, dtype, processParams)
 
         postMessage({
@@ -184,6 +187,27 @@ onmessage = (message) => {
         })
       }
     }
+    if (method === VALID_METHOD.FILTER_REGIONS) {
+      const { regions, searchTerm } = param
+      try {
+        const filteredRegions = self.filterRegion.filterRegion(regions, searchTerm)
+        
+        postMessage({
+          id,
+          result: { filteredRegions }
+        })
+      } catch (e) {
+        postMessage({
+          id,
+          error: {
+            code: 401,
+            message: `filter region error: ${e.toString()}`
+          }
+        })
+      }
+      
+      return
+    }
     postMessage({
       id,
       error: {