From bca53679f1ced3bf741da2f62fef38190e0d241e Mon Sep 17 00:00:00 2001 From: Virgil Date: Thu, 2 Apr 2026 20:16:54 +0000 Subject: [PATCH] ui: add global search to shell --- ui/src/app/dashboard.component.ts | 52 ++++++++- ui/src/frame/application-frame.component.html | 48 ++++---- ui/src/frame/application-frame.component.ts | 104 +++++++++++++++++- ui/src/index.ts | 1 + ui/src/services/ui-state.service.ts | 24 ++++ 5 files changed, 204 insertions(+), 25 deletions(-) create mode 100644 ui/src/services/ui-state.service.ts diff --git a/ui/src/app/dashboard.component.ts b/ui/src/app/dashboard.component.ts index f514fdd..b322874 100644 --- a/ui/src/app/dashboard.component.ts +++ b/ui/src/app/dashboard.component.ts @@ -5,6 +5,7 @@ import { ProviderDiscoveryService, type ProviderInfo } from '../services/provide import { TranslationService } from '../services/translation.service'; import { WebSocketService } from '../services/websocket.service'; import { ProviderHostComponent } from '../components/provider-host.component'; +import { UiStateService } from '../services/ui-state.service'; @Component({ selector: 'dashboard-view', @@ -48,6 +49,10 @@ import { ProviderHostComponent } from '../components/provider-host.component'; API base {{ apiBase() }} +
+ Search + {{ searchQuery() || 'All providers' }} +
@@ -58,7 +63,9 @@ import { ProviderHostComponent } from '../components/provider-host.component';

Discovered providers

Renderable capabilities

- {{ providerCount() }} total + + {{ filteredProviders().length }} shown / {{ providerCount() }} total +
@@ -84,8 +91,16 @@ import { ProviderHostComponent } from '../components/provider-host.component';
- No providers discovered yet. - The shell will populate this view once the backend exposes provider metadata. + + {{ searchQuery() ? 'No providers match the current search.' : 'No providers discovered yet.' }} + + + {{ + searchQuery() + ? 'Clear the search box to restore the full provider list.' + : 'The shell will populate this view once the backend exposes provider metadata.' + }} +
@@ -103,6 +118,10 @@ import { ProviderHostComponent } from '../components/provider-host.component'; Provider discovery Loads provider metadata and registers custom element scripts automatically. +
  • + Global search + Filters navigation and provider cards from a single shell-level search box. +
  • Realtime status Tracks the websocket connection used for backend events. @@ -156,6 +175,7 @@ export class DashboardComponent { private readonly translations = inject(TranslationService); private readonly websocket = inject(WebSocketService); private readonly destroyRef = inject(DestroyRef); + private readonly uiState = inject(UiStateService); protected readonly title = signal('Core GUI'); protected readonly subtitle = signal('Desktop orchestration console'); @@ -166,9 +186,31 @@ export class DashboardComponent { protected readonly providerCount = computed(() => this.providers().length); protected readonly connected = this.websocket.connected; protected readonly apiBase = computed(() => this.apiConfig.effectiveBaseUrl); + protected readonly searchQuery = this.uiState.searchQuery; + + protected readonly filteredProviders = computed(() => { + const query = this.searchQuery().trim().toLowerCase(); + const providers = this.providers(); + if (!query) { + return providers; + } + + return providers.filter((provider) => { + const haystack = [ + provider.name, + provider.basePath, + provider.status ?? '', + provider.element?.tag ?? '', + provider.element?.source ?? '', + ] + .join(' ') + .toLowerCase(); + return haystack.includes(query); + }); + }); protected readonly featuredProviders = computed(() => - this.providers().filter((provider) => provider.element?.tag).slice(0, 6), + this.filteredProviders().filter((provider) => provider.element?.tag).slice(0, 6), ); protected readonly selectedRenderableProvider = computed(() => { @@ -178,7 +220,7 @@ export class DashboardComponent { } return ( - this.providers().find((provider) => provider.name === selection && provider.element?.tag) ?? + this.filteredProviders().find((provider) => provider.name === selection && provider.element?.tag) ?? this.featuredProviders()[0] ?? null ); diff --git a/ui/src/frame/application-frame.component.html b/ui/src/frame/application-frame.component.html index 8f5202f..8b65f87 100644 --- a/ui/src/frame/application-frame.component.html +++ b/ui/src/frame/application-frame.component.html @@ -17,23 +17,33 @@ -
    -
    - - -
    -
    - + } +
    + +
    + {{ visibleNavigation().length }} visible + @@ -101,7 +111,7 @@