theme and search fixes

This commit is contained in:
master
2026-03-08 16:20:52 +02:00
parent eb4ade0335
commit 9f6fd0b4aa
21 changed files with 455 additions and 319 deletions

View File

@@ -0,0 +1,129 @@
import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
import { TestBed } from '@angular/core/testing';
import { firstValueFrom } from 'rxjs';
import { AppConfigService } from '../config/app-config.service';
import { AuthHttpInterceptor } from '../auth/auth-http.interceptor';
import { AuthorityAuthService } from '../auth/authority-auth.service';
import { DpopService } from '../auth/dpop/dpop.service';
import { TenantActivationService } from '../auth/tenant-activation.service';
import { AUDIT_BUNDLES_API_BASE_URL, AuditBundlesHttpClient } from './audit-bundles.client';
describe('AuditBundlesHttpClient', () => {
let client: AuditBundlesHttpClient;
let httpMock: HttpTestingController;
let auth: Pick<AuthorityAuthService, 'getAuthHeadersForRequest'> & {
getAuthHeadersForRequest: jasmine.Spy;
};
beforeEach(() => {
auth = {
getAuthHeadersForRequest: jasmine
.createSpy('getAuthHeadersForRequest')
.and.returnValue(
Promise.resolve({
authorization: 'DPoP access-token',
dpop: 'proof-token',
})
),
};
TestBed.configureTestingModule({
providers: [
provideHttpClient(withInterceptorsFromDi()),
provideHttpClientTesting(),
AuditBundlesHttpClient,
{
provide: HTTP_INTERCEPTORS,
useClass: AuthHttpInterceptor,
multi: true,
},
{
provide: AuthorityAuthService,
useValue: auth,
},
{
provide: AppConfigService,
useValue: {
authority: {
issuer: 'https://stella-ops.local',
tokenEndpoint: '/connect/token',
authorizeEndpoint: '/connect/authorize',
},
},
},
{
provide: DpopService,
useValue: {
setNonce: jasmine.createSpy('setNonce').and.returnValue(Promise.resolve()),
},
},
{
provide: TenantActivationService,
useValue: {
activeTenantId: () => 'demo-prod',
},
},
{
provide: AUDIT_BUNDLES_API_BASE_URL,
useValue: '',
},
],
});
client = TestBed.inject(AuditBundlesHttpClient);
httpMock = TestBed.inject(HttpTestingController);
});
afterEach(() => {
httpMock.verify();
});
it('leaves auth to the shared interceptor so audit bundle creation uses DPoP headers', async () => {
const responsePromise = firstValueFrom(client.createBundle({
subject: {
type: 'IMAGE',
name: 'asset-web-prod',
digest: { sha256: 'sha256:test-bundle-probe' },
},
contents: {
vulnReports: true,
sbom: true,
vex: true,
policyEvals: true,
attestations: true,
},
}));
await Promise.resolve();
const request = httpMock.expectOne((pending) => pending.url === '/v1/audit-bundles');
expect(auth.getAuthHeadersForRequest).toHaveBeenCalledOnceWith(
`${window.location.origin}/v1/audit-bundles`,
'POST'
);
expect(request.request.headers.get('Authorization')).toBe('DPoP access-token');
expect(request.request.headers.get('DPoP')).toBe('proof-token');
expect(request.request.headers.get('X-Stella-Tenant')).toBe('demo-prod');
request.flush({
bundleId: 'bndl-0001',
status: 'queued',
statusUrl: '/v1/audit-bundles/bndl-0001',
});
await responsePromise;
});
it('uses the interceptor for audit bundle downloads too', async () => {
const responsePromise = firstValueFrom(client.downloadBundle('bndl-0001'));
await Promise.resolve();
const request = httpMock.expectOne((pending) => pending.url === '/v1/audit-bundles/bndl-0001/download');
expect(request.request.headers.get('Authorization')).toBe('DPoP access-token');
expect(request.request.headers.get('DPoP')).toBe('proof-token');
expect(request.request.headers.get('Accept')).toBe('application/zip');
request.flush(new Blob(['bundle'], { type: 'application/zip' }));
await responsePromise;
});
});

View File

@@ -2,7 +2,6 @@ import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Inject, Injectable, InjectionToken, inject } from '@angular/core'; import { Inject, Injectable, InjectionToken, inject } from '@angular/core';
import { Observable, of, delay, map, catchError, throwError } from 'rxjs'; import { Observable, of, delay, map, catchError, throwError } from 'rxjs';
import { AuthSessionStore } from '../auth/auth-session.store';
import { TenantActivationService } from '../auth/tenant-activation.service'; import { TenantActivationService } from '../auth/tenant-activation.service';
import { generateTraceId } from './trace.util'; import { generateTraceId } from './trace.util';
import type { import type {
@@ -82,7 +81,6 @@ export class AuditBundlesHttpClient implements AuditBundlesApi {
constructor( constructor(
private readonly http: HttpClient, private readonly http: HttpClient,
private readonly authSession: AuthSessionStore,
@Inject(AUDIT_BUNDLES_API_BASE_URL) private readonly baseUrl: string @Inject(AUDIT_BUNDLES_API_BASE_URL) private readonly baseUrl: string
) {} ) {}
@@ -210,11 +208,6 @@ export class AuditBundlesHttpClient implements AuditBundlesApi {
Accept: 'application/json', Accept: 'application/json',
}); });
const accessToken = this.authSession.session()?.tokens.accessToken;
if (accessToken) {
headers = headers.set('Authorization', `Bearer ${accessToken}`);
}
if (projectId) headers = headers.set('X-Stella-Project', projectId); if (projectId) headers = headers.set('X-Stella-Project', projectId);
return headers; return headers;

View File

@@ -138,7 +138,7 @@ interface LoadedBundle {
.management-header h2 { .management-header h2 {
font-size: 1.25rem; font-size: 1.25rem;
font-weight: var(--font-weight-semibold); font-weight: var(--font-weight-semibold);
color: var(--color-border-primary); color: var(--color-text-heading);
margin: 0 0 0.25rem; margin: 0 0 0.25rem;
} }
@@ -161,8 +161,8 @@ interface LoadedBundle {
} }
.section-card { .section-card {
background: rgba(30, 41, 59, 0.4); background: var(--color-surface-secondary);
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
padding: 1.5rem; padding: 1.5rem;
} }
@@ -170,7 +170,7 @@ interface LoadedBundle {
.section-card h3 { .section-card h3 {
font-size: 1rem; font-size: 1rem;
font-weight: var(--font-weight-semibold); font-weight: var(--font-weight-semibold);
color: var(--color-border-primary); color: var(--color-text-heading);
margin: 0 0 1rem; margin: 0 0 1rem;
} }
@@ -187,8 +187,8 @@ interface LoadedBundle {
} }
.bundle-card { .bundle-card {
background: rgba(15, 23, 42, 0.5); background: var(--color-surface-elevated);
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
padding: 1rem; padding: 1rem;
} }
@@ -258,7 +258,7 @@ interface LoadedBundle {
.meta-value { .meta-value {
font-size: 0.875rem; font-size: 0.875rem;
color: var(--color-border-primary); color: var(--color-text-primary);
} }
.bundle-actions { .bundle-actions {
@@ -273,8 +273,8 @@ interface LoadedBundle {
} }
.category-card { .category-card {
background: rgba(15, 23, 42, 0.5); background: var(--color-surface-elevated);
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
padding: 1rem; padding: 1rem;
} }
@@ -297,7 +297,7 @@ interface LoadedBundle {
.category-name { .category-name {
font-weight: var(--font-weight-medium); font-weight: var(--font-weight-medium);
color: var(--color-border-primary); color: var(--color-text-heading);
} }
.category-count { .category-count {
@@ -316,7 +316,7 @@ interface LoadedBundle {
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 0.375rem 0.5rem; padding: 0.375rem 0.5rem;
background: rgba(30, 41, 59, 0.5); background: var(--color-surface-tertiary);
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
font-size: 0.75rem; font-size: 0.75rem;
} }
@@ -353,8 +353,8 @@ interface LoadedBundle {
} }
.btn--secondary { .btn--secondary {
background: var(--color-text-primary); background: var(--color-surface-tertiary);
color: var(--color-border-primary); color: var(--color-text-primary);
} }
.btn--danger { .btn--danger {

View File

@@ -73,7 +73,7 @@ import { OfflineModeService } from '../../core/services/offline-mode.service';
styles: [` styles: [`
.offline-kit-layout { .offline-kit-layout {
min-height: 100%; min-height: 100%;
background: var(--color-text-heading); background: var(--color-surface-primary);
} }
.page-header { .page-header {
@@ -81,13 +81,13 @@ import { OfflineModeService } from '../../core/services/offline-mode.service';
justify-content: space-between; justify-content: space-between;
align-items: flex-start; align-items: flex-start;
padding: 1.5rem 2rem; padding: 1.5rem 2rem;
border-bottom: 1px solid var(--color-text-primary); border-bottom: 1px solid var(--color-border-primary);
} }
.header-content h1 { .header-content h1 {
font-size: 1.5rem; font-size: 1.5rem;
font-weight: var(--font-weight-semibold); font-weight: var(--font-weight-semibold);
color: var(--color-surface-tertiary); color: var(--color-text-heading);
margin: 0 0 0.25rem; margin: 0 0 0.25rem;
} }
@@ -105,7 +105,7 @@ import { OfflineModeService } from '../../core/services/offline-mode.service';
} }
.page-shortcuts a { .page-shortcuts a {
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
border-radius: var(--radius-full); border-radius: var(--radius-full);
color: var(--color-text-muted); color: var(--color-text-muted);
font-size: 0.72rem; font-size: 0.72rem;
@@ -114,8 +114,8 @@ import { OfflineModeService } from '../../core/services/offline-mode.service';
} }
.page-shortcuts a:hover { .page-shortcuts a:hover {
color: var(--color-border-primary); color: var(--color-text-link);
border-color: var(--color-border-primary); border-color: var(--color-text-link);
} }
.connection-status { .connection-status {
@@ -159,8 +159,8 @@ import { OfflineModeService } from '../../core/services/offline-mode.service';
display: flex; display: flex;
gap: 0.25rem; gap: 0.25rem;
padding: 0 2rem; padding: 0 2rem;
background: var(--color-text-heading); background: var(--color-surface-primary);
border-bottom: 1px solid var(--color-text-primary); border-bottom: 1px solid var(--color-border-primary);
} }
.tab-link { .tab-link {

View File

@@ -103,29 +103,29 @@ import { PolicyPackStore } from '../services/policy-pack.store';
`, `,
styles: [ styles: [
` `
:host { display: block; background: var(--color-surface-inverse); color: var(--color-border-primary); min-height: 100vh; } :host { display: block; background: var(--color-surface-primary); color: var(--color-text-primary); min-height: 100vh; }
.workspace { max-width: 1200px; margin: 0 auto; padding: 1.5rem; } .workspace { max-width: 1200px; margin: 0 auto; padding: 1.5rem; }
.workspace__header { margin-bottom: 1rem; } .workspace__header { margin-bottom: 1rem; }
.workspace__eyebrow { margin: 0; color: var(--color-status-info); text-transform: uppercase; letter-spacing: 0.05em; font-size: 0.8rem; } .workspace__eyebrow { margin: 0; color: var(--color-status-info); text-transform: uppercase; letter-spacing: 0.05em; font-size: 0.8rem; }
.workspace__lede { margin: 0.2rem 0 0; color: var(--color-text-muted); } .workspace__lede { margin: 0.2rem 0 0; color: var(--color-text-muted); }
.workspace__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 1rem; } .workspace__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 1rem; }
.pack-card { background: var(--color-text-heading); border: 1px solid var(--color-text-heading); border-radius: var(--radius-xl); padding: 1rem; box-shadow: 0 12px 30px rgba(0,0,0,0.28); display: grid; gap: 0.6rem; } .pack-card { background: var(--color-surface-elevated); border: 1px solid var(--color-border-primary); border-radius: var(--radius-xl); padding: 1rem; box-shadow: var(--shadow-md); display: grid; gap: 0.6rem; }
.pack-card__head { display: flex; justify-content: space-between; gap: 0.75rem; align-items: flex-start; } .pack-card__head { display: flex; justify-content: space-between; gap: 0.75rem; align-items: flex-start; }
.pack-card__eyebrow { margin: 0; color: var(--color-brand-muted); font-size: 0.75rem; letter-spacing: 0.05em; text-transform: uppercase; } .pack-card__eyebrow { margin: 0; color: var(--color-brand-secondary); font-size: 0.75rem; letter-spacing: 0.05em; text-transform: uppercase; }
.pack-card__desc { margin: 0.2rem 0 0; color: rgba(212, 201, 168, 0.5); } .pack-card__desc { margin: 0.2rem 0 0; color: var(--color-text-muted); }
.pack-card__meta { display: grid; justify-items: end; gap: 0.2rem; color: var(--color-text-muted); font-size: 0.9rem; } .pack-card__meta { display: grid; justify-items: end; gap: 0.2rem; color: var(--color-text-muted); font-size: 0.9rem; }
.pack-card__tags { list-style: none; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: 0.35rem; } .pack-card__tags { list-style: none; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: 0.35rem; }
.pack-card__tags li { padding: 0.2rem 0.45rem; border: 1px solid var(--color-text-heading); border-radius: var(--radius-full); background: var(--color-surface-inverse); } .pack-card__tags li { padding: 0.2rem 0.45rem; border: 1px solid var(--color-border-primary); border-radius: var(--radius-full); background: var(--color-surface-tertiary); }
.pack-card__actions { display: flex; gap: 0.5rem; flex-wrap: wrap; } .pack-card__actions { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.pack-card__actions a { color: var(--color-border-primary); border: 1px solid var(--color-text-primary); border-radius: var(--radius-lg); padding: 0.35rem 0.6rem; text-decoration: none; } .pack-card__actions a { color: var(--color-text-primary); border: 1px solid var(--color-border-primary); border-radius: var(--radius-lg); padding: 0.35rem 0.6rem; text-decoration: none; }
.pack-card__actions a:hover { border-color: var(--color-status-info); } .pack-card__actions a:hover { border-color: var(--color-status-info); }
.pack-card__actions a.action-disabled { opacity: 0.5; pointer-events: none; border-style: dashed; } .pack-card__actions a.action-disabled { opacity: 0.5; pointer-events: none; border-style: dashed; }
.pack-card__detail { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 0.35rem 1rem; margin: 0; } .pack-card__detail { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 0.35rem 1rem; margin: 0; }
dt { color: var(--color-text-muted); font-size: 0.85rem; margin: 0; } dt { color: var(--color-text-muted); font-size: 0.85rem; margin: 0; }
dd { margin: 0; color: var(--color-border-primary); } dd { margin: 0; color: var(--color-text-primary); }
.workspace__banner { background: var(--color-text-heading); border: 1px solid var(--color-text-primary); color: var(--color-status-warning-border); padding: 0.75rem 1rem; border-radius: var(--radius-xl); margin: 0.5rem 0 1rem; } .workspace__banner { background: var(--color-status-warning-bg); border: 1px solid var(--color-status-warning-border); color: var(--color-status-warning-text); padding: 0.75rem 1rem; border-radius: var(--radius-xl); margin: 0.5rem 0 1rem; }
.workspace__footer { margin-top: 0.8rem; } .workspace__footer { margin-top: 0.8rem; }
.workspace__footer button { background: var(--color-status-info-text); border: 1px solid var(--color-status-info-text); color: var(--color-border-primary); border-radius: var(--radius-lg); padding: 0.45rem 0.8rem; } .workspace__footer button { background: var(--color-brand-primary); border: 1px solid var(--color-brand-primary); color: var(--color-text-inverse); border-radius: var(--radius-lg); padding: 0.45rem 0.8rem; cursor: pointer; }
`, `,
] ]
}) })

View File

@@ -313,7 +313,7 @@ export type AirgapEventType =
} }
.status-info strong { .status-info strong {
color: var(--color-border-primary); color: var(--color-text-primary);
font-size: 1.1rem; font-size: 1.1rem;
} }
@@ -344,15 +344,15 @@ export type AirgapEventType =
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
padding: 1rem; padding: 1rem;
background: var(--color-surface-inverse); background: var(--color-surface-primary);
border: 1px solid var(--color-text-heading); border: 1px solid var(--color-border-primary);
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
} }
.stat-value { .stat-value {
font-size: 1.5rem; font-size: 1.5rem;
font-weight: var(--font-weight-bold); font-weight: var(--font-weight-bold);
color: var(--color-border-primary); color: var(--color-text-primary);
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
} }
@@ -384,10 +384,10 @@ export type AirgapEventType =
.filter-group input, .filter-group input,
.filter-group select { .filter-group select {
background: var(--color-surface-inverse); background: var(--color-surface-primary);
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
border-radius: var(--radius-md); border-radius: var(--radius-md);
color: var(--color-border-primary); color: var(--color-text-primary);
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
min-width: 160px; min-width: 160px;
} }
@@ -430,8 +430,8 @@ export type AirgapEventType =
} }
.event-card { .event-card {
background: var(--color-surface-inverse); background: var(--color-surface-primary);
border: 1px solid var(--color-text-heading); border: 1px solid var(--color-border-primary);
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
padding: 1rem; padding: 1rem;
cursor: pointer; cursor: pointer;
@@ -439,7 +439,7 @@ export type AirgapEventType =
} }
.event-card:hover { .event-card:hover {
border-color: var(--color-text-primary); border-color: var(--color-border-primary);
} }
.event-card--warning { .event-card--warning {
@@ -555,7 +555,7 @@ export type AirgapEventType =
.event-card__details { .event-card__details {
margin-top: 1rem; margin-top: 1rem;
padding-top: 1rem; padding-top: 1rem;
border-top: 1px solid var(--color-text-heading); border-top: 1px solid var(--color-border-primary);
} }
.event-card__details h4 { .event-card__details h4 {
@@ -567,10 +567,10 @@ export type AirgapEventType =
.details-json { .details-json {
margin: 0; margin: 0;
padding: 0.75rem; padding: 0.75rem;
background: var(--color-text-heading); background: var(--color-surface-tertiary);
border-radius: var(--radius-md); border-radius: var(--radius-md);
font-size: 0.8rem; font-size: 0.8rem;
color: var(--color-border-primary); color: var(--color-text-primary);
overflow-x: auto; overflow-x: auto;
} }
@@ -581,13 +581,13 @@ export type AirgapEventType =
gap: 1rem; gap: 1rem;
padding: 1rem; padding: 1rem;
margin-top: 1rem; margin-top: 1rem;
border-top: 1px solid var(--color-text-heading); border-top: 1px solid var(--color-border-primary);
} }
.airgap-audit__pagination button { .airgap-audit__pagination button {
background: transparent; background: transparent;
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
color: var(--color-border-primary); color: var(--color-text-primary);
border-radius: var(--radius-md); border-radius: var(--radius-md);
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
cursor: pointer; cursor: pointer;

View File

@@ -506,7 +506,7 @@ import {
align-items: center; align-items: center;
gap: 1rem; gap: 1rem;
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
background: var(--color-text-heading); background: var(--color-surface-elevated);
border-radius: var(--radius-md); border-radius: var(--radius-md);
} }
@@ -522,7 +522,7 @@ import {
.alert-name { .alert-name {
font-weight: var(--font-weight-medium); font-weight: var(--font-weight-medium);
color: var(--color-border-primary); color: var(--color-text-primary);
} }
.alert-type { .alert-type {
@@ -578,10 +578,10 @@ import {
.filter-group input, .filter-group input,
.filter-group select { .filter-group select {
background: var(--color-surface-inverse); background: var(--color-surface-primary);
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
border-radius: var(--radius-md); border-radius: var(--radius-md);
color: var(--color-border-primary); color: var(--color-text-primary);
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
min-width: 160px; min-width: 160px;
} }
@@ -630,11 +630,11 @@ import {
.cert-table td { .cert-table td {
padding: 0.75rem 1rem; padding: 0.75rem 1rem;
text-align: left; text-align: left;
border-bottom: 1px solid var(--color-text-heading); border-bottom: 1px solid var(--color-border-primary);
} }
.cert-table th { .cert-table th {
background: var(--color-surface-inverse); background: var(--color-surface-primary);
color: var(--color-text-muted); color: var(--color-text-muted);
font-weight: var(--font-weight-medium); font-weight: var(--font-weight-medium);
font-size: 0.85rem; font-size: 0.85rem;
@@ -679,7 +679,7 @@ import {
} }
.cert-name strong { .cert-name strong {
color: var(--color-border-primary); color: var(--color-text-primary);
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.5rem; gap: 0.5rem;
@@ -720,7 +720,7 @@ import {
} }
.subject-cn { .subject-cn {
color: var(--color-border-primary); color: var(--color-text-primary);
font-family: monospace; font-family: monospace;
font-size: 0.85rem; font-size: 0.85rem;
} }
@@ -767,7 +767,7 @@ import {
.btn-chain { .btn-chain {
background: transparent; background: transparent;
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
color: var(--color-status-info); color: var(--color-status-info);
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
padding: 0.15rem 0.4rem; padding: 0.15rem 0.4rem;
@@ -785,8 +785,8 @@ import {
.btn-action { .btn-action {
background: transparent; background: transparent;
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
color: var(--color-border-primary); color: var(--color-text-primary);
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
padding: 0.25rem 0.5rem; padding: 0.25rem 0.5rem;
font-size: 0.8rem; font-size: 0.8rem;
@@ -806,13 +806,13 @@ import {
align-items: center; align-items: center;
gap: 1rem; gap: 1rem;
padding: 1rem; padding: 1rem;
border-top: 1px solid var(--color-text-heading); border-top: 1px solid var(--color-border-primary);
} }
.cert-inventory__pagination button { .cert-inventory__pagination button {
background: transparent; background: transparent;
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
color: var(--color-border-primary); color: var(--color-text-primary);
border-radius: var(--radius-md); border-radius: var(--radius-md);
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
cursor: pointer; cursor: pointer;
@@ -845,8 +845,8 @@ import {
.detail-modal, .detail-modal,
.chain-modal { .chain-modal {
background: var(--color-text-heading); background: var(--color-surface-elevated);
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
border-radius: var(--radius-xl); border-radius: var(--radius-xl);
width: 90%; width: 90%;
max-width: 600px; max-width: 600px;
@@ -861,7 +861,7 @@ import {
align-items: center; align-items: center;
gap: 1rem; gap: 1rem;
padding: 1rem 1.5rem; padding: 1rem 1.5rem;
border-bottom: 1px solid var(--color-text-heading); border-bottom: 1px solid var(--color-border-primary);
} }
.detail-modal__header h3, .detail-modal__header h3,
@@ -872,7 +872,7 @@ import {
.btn-close { .btn-close {
background: transparent; background: transparent;
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
color: var(--color-text-muted); color: var(--color-text-muted);
width: 28px; width: 28px;
height: 28px; height: 28px;
@@ -927,7 +927,7 @@ import {
.detail-item dd { .detail-item dd {
margin: 0; margin: 0;
color: var(--color-border-primary); color: var(--color-text-primary);
} }
.mono { .mono {
@@ -965,7 +965,7 @@ import {
.san-list li { .san-list li {
font-family: monospace; font-family: monospace;
font-size: 0.85rem; font-size: 0.85rem;
color: var(--color-border-primary); color: var(--color-text-primary);
} }
.detail-modal__footer, .detail-modal__footer,
@@ -974,13 +974,13 @@ import {
justify-content: flex-end; justify-content: flex-end;
gap: 0.5rem; gap: 0.5rem;
padding: 1rem 1.5rem; padding: 1rem 1.5rem;
border-top: 1px solid var(--color-text-heading); border-top: 1px solid var(--color-border-primary);
} }
.btn-secondary { .btn-secondary {
background: transparent; background: transparent;
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
color: var(--color-border-primary); color: var(--color-text-primary);
border-radius: var(--radius-md); border-radius: var(--radius-md);
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
cursor: pointer; cursor: pointer;
@@ -1016,8 +1016,8 @@ import {
gap: 1rem; gap: 1rem;
width: 100%; width: 100%;
padding: 0.75rem 1rem; padding: 0.75rem 1rem;
background: var(--color-surface-inverse); background: var(--color-surface-primary);
border: 1px solid var(--color-text-heading); border: 1px solid var(--color-border-primary);
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
} }
@@ -1035,10 +1035,10 @@ import {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
background: var(--color-text-heading); background: var(--color-surface-tertiary);
border-radius: var(--radius-md); border-radius: var(--radius-md);
font-weight: var(--font-weight-bold); font-weight: var(--font-weight-bold);
color: var(--color-border-primary); color: var(--color-text-primary);
} }
.chain-node--root .chain-node__icon { .chain-node--root .chain-node__icon {
@@ -1060,7 +1060,7 @@ import {
.chain-node__name { .chain-node__name {
font-weight: var(--font-weight-medium); font-weight: var(--font-weight-medium);
color: var(--color-border-primary); color: var(--color-text-primary);
} }
.chain-node__cn { .chain-node__cn {

View File

@@ -329,15 +329,15 @@ export type IncidentType =
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
padding: 1rem; padding: 1rem;
background: var(--color-surface-inverse); background: var(--color-surface-primary);
border: 1px solid var(--color-text-heading); border: 1px solid var(--color-border-primary);
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
cursor: pointer; cursor: pointer;
transition: all 0.15s; transition: all 0.15s;
} }
.summary-card:hover { .summary-card:hover {
border-color: var(--color-text-primary); border-color: var(--color-border-primary);
} }
.summary-card--open { border-left: 3px solid var(--color-status-error); } .summary-card--open { border-left: 3px solid var(--color-status-error); }
@@ -349,7 +349,7 @@ export type IncidentType =
.summary-value { .summary-value {
font-size: 1.75rem; font-size: 1.75rem;
font-weight: var(--font-weight-bold); font-weight: var(--font-weight-bold);
color: var(--color-border-primary); color: var(--color-text-primary);
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
} }
@@ -381,10 +381,10 @@ export type IncidentType =
.filter-group input, .filter-group input,
.filter-group select { .filter-group select {
background: var(--color-surface-inverse); background: var(--color-surface-primary);
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
border-radius: var(--radius-md); border-radius: var(--radius-md);
color: var(--color-border-primary); color: var(--color-text-primary);
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
min-width: 160px; min-width: 160px;
} }
@@ -427,15 +427,15 @@ export type IncidentType =
} }
.incident-card { .incident-card {
background: var(--color-surface-inverse); background: var(--color-surface-primary);
border: 1px solid var(--color-text-heading); border: 1px solid var(--color-border-primary);
border-radius: var(--radius-xl); border-radius: var(--radius-xl);
overflow: hidden; overflow: hidden;
transition: border-color 0.15s; transition: border-color 0.15s;
} }
.incident-card:hover { .incident-card:hover {
border-color: var(--color-text-primary); border-color: var(--color-border-primary);
} }
.incident-card--critical { .incident-card--critical {
@@ -489,7 +489,7 @@ export type IncidentType =
margin: 0; margin: 0;
font-size: 1rem; font-size: 1rem;
font-weight: var(--font-weight-semibold); font-weight: var(--font-weight-semibold);
color: var(--color-border-primary); color: var(--color-text-primary);
} }
.incident-id { .incident-id {
@@ -549,9 +549,9 @@ export type IncidentType =
} }
.incident-card__details { .incident-card__details {
border-top: 1px solid var(--color-text-heading); border-top: 1px solid var(--color-border-primary);
padding: 1.25rem; padding: 1.25rem;
background: rgba(15, 23, 42, 0.5); background: var(--color-surface-secondary);
} }
.details-section { .details-section {
@@ -581,7 +581,7 @@ export type IncidentType =
align-items: center; align-items: center;
gap: 0.75rem; gap: 0.75rem;
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
background: var(--color-surface-inverse); background: var(--color-surface-primary);
border-radius: var(--radius-md); border-radius: var(--radius-md);
} }
@@ -597,7 +597,7 @@ export type IncidentType =
.resource-name { .resource-name {
flex: 1; flex: 1;
color: var(--color-border-primary); color: var(--color-text-primary);
font-size: 0.9rem; font-size: 0.9rem;
} }
@@ -638,14 +638,14 @@ export type IncidentType =
top: 16px; top: 16px;
bottom: 0; bottom: 0;
width: 2px; width: 2px;
background: var(--color-text-heading); background: var(--color-border-primary);
} }
.timeline-marker { .timeline-marker {
width: 12px; width: 12px;
height: 12px; height: 12px;
border-radius: var(--radius-full); border-radius: var(--radius-full);
background: var(--color-text-primary); background: var(--color-border-primary);
flex-shrink: 0; flex-shrink: 0;
margin-top: 4px; margin-top: 4px;
} }
@@ -669,7 +669,7 @@ export type IncidentType =
.timeline-type { .timeline-type {
font-size: 0.75rem; font-size: 0.75rem;
font-weight: var(--font-weight-medium); font-weight: var(--font-weight-medium);
color: var(--color-border-primary); color: var(--color-text-primary);
} }
.timeline-time { .timeline-time {
@@ -691,10 +691,10 @@ export type IncidentType =
.details-json { .details-json {
margin: 0; margin: 0;
padding: 0.75rem; padding: 0.75rem;
background: var(--color-surface-inverse); background: var(--color-surface-primary);
border-radius: var(--radius-md); border-radius: var(--radius-md);
font-size: 0.8rem; font-size: 0.8rem;
color: var(--color-border-primary); color: var(--color-text-primary);
overflow-x: auto; overflow-x: auto;
} }
@@ -703,13 +703,13 @@ export type IncidentType =
gap: 0.5rem; gap: 0.5rem;
margin-top: 1rem; margin-top: 1rem;
padding-top: 1rem; padding-top: 1rem;
border-top: 1px solid var(--color-text-heading); border-top: 1px solid var(--color-border-primary);
} }
.btn-action { .btn-action {
background: transparent; background: transparent;
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
color: var(--color-border-primary); color: var(--color-text-primary);
border-radius: var(--radius-md); border-radius: var(--radius-md);
padding: 0.4rem 0.75rem; padding: 0.4rem 0.75rem;
font-size: 0.85rem; font-size: 0.85rem;
@@ -729,13 +729,13 @@ export type IncidentType =
gap: 1rem; gap: 1rem;
padding: 1rem; padding: 1rem;
margin-top: 1rem; margin-top: 1rem;
border-top: 1px solid var(--color-text-heading); border-top: 1px solid var(--color-border-primary);
} }
.incident-audit__pagination button { .incident-audit__pagination button {
background: transparent; background: transparent;
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
color: var(--color-border-primary); color: var(--color-text-primary);
border-radius: var(--radius-md); border-radius: var(--radius-md);
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
cursor: pointer; cursor: pointer;

View File

@@ -289,10 +289,10 @@ import { TrustScoreConfigComponent } from './trust-score-config.component';
.filter-group input, .filter-group input,
.filter-group select { .filter-group select {
background: var(--color-surface-inverse); background: var(--color-surface-primary);
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
border-radius: var(--radius-md); border-radius: var(--radius-md);
color: var(--color-border-primary); color: var(--color-text-primary);
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
min-width: 160px; min-width: 160px;
} }
@@ -335,7 +335,7 @@ import { TrustScoreConfigComponent } from './trust-score-config.component';
gap: 1.5rem; gap: 1.5rem;
margin-bottom: 1.5rem; margin-bottom: 1.5rem;
padding: 1rem; padding: 1rem;
background: var(--color-surface-inverse); background: var(--color-surface-primary);
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
} }
@@ -386,11 +386,11 @@ import { TrustScoreConfigComponent } from './trust-score-config.component';
.issuer-table td { .issuer-table td {
padding: 0.75rem 1rem; padding: 0.75rem 1rem;
text-align: left; text-align: left;
border-bottom: 1px solid var(--color-text-heading); border-bottom: 1px solid var(--color-border-primary);
} }
.issuer-table th { .issuer-table th {
background: var(--color-surface-inverse); background: var(--color-surface-primary);
color: var(--color-text-muted); color: var(--color-text-muted);
font-weight: var(--font-weight-medium); font-weight: var(--font-weight-medium);
font-size: 0.85rem; font-size: 0.85rem;
@@ -429,7 +429,7 @@ import { TrustScoreConfigComponent } from './trust-score-config.component';
} }
.issuer-info strong { .issuer-info strong {
color: var(--color-border-primary); color: var(--color-text-primary);
} }
.issuer-name { .issuer-name {
@@ -466,7 +466,7 @@ import { TrustScoreConfigComponent } from './trust-score-config.component';
.score-bar { .score-bar {
width: 60px; width: 60px;
height: 6px; height: 6px;
background: var(--color-text-heading); background: var(--color-surface-tertiary);
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
overflow: hidden; overflow: hidden;
} }
@@ -517,8 +517,8 @@ import { TrustScoreConfigComponent } from './trust-score-config.component';
.btn-action { .btn-action {
background: transparent; background: transparent;
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
color: var(--color-border-primary); color: var(--color-text-primary);
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
padding: 0.25rem 0.5rem; padding: 0.25rem 0.5rem;
font-size: 0.8rem; font-size: 0.8rem;
@@ -548,13 +548,13 @@ import { TrustScoreConfigComponent } from './trust-score-config.component';
align-items: center; align-items: center;
gap: 1rem; gap: 1rem;
padding: 1rem; padding: 1rem;
border-top: 1px solid var(--color-text-heading); border-top: 1px solid var(--color-border-primary);
} }
.issuer-list__pagination button { .issuer-list__pagination button {
background: transparent; background: transparent;
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
color: var(--color-border-primary); color: var(--color-text-primary);
border-radius: var(--radius-md); border-radius: var(--radius-md);
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
cursor: pointer; cursor: pointer;

View File

@@ -273,8 +273,8 @@ import {
bottom: 0; bottom: 0;
width: 480px; width: 480px;
max-width: 100vw; max-width: 100vw;
background: var(--color-text-heading); background: var(--color-surface-elevated);
border-left: 1px solid var(--color-text-heading); border-left: 1px solid var(--color-border-primary);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
z-index: 1000; z-index: 1000;
@@ -286,7 +286,7 @@ import {
justify-content: space-between; justify-content: space-between;
align-items: flex-start; align-items: flex-start;
padding: 1.5rem; padding: 1.5rem;
border-bottom: 1px solid var(--color-text-heading); border-bottom: 1px solid var(--color-border-primary);
} }
.detail-panel__header h2 { .detail-panel__header h2 {
@@ -303,7 +303,7 @@ import {
.btn-close { .btn-close {
background: transparent; background: transparent;
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
color: var(--color-text-muted); color: var(--color-text-muted);
width: 32px; width: 32px;
height: 32px; height: 32px;
@@ -356,7 +356,7 @@ import {
.detail-item dd { .detail-item dd {
margin: 0; margin: 0;
color: var(--color-border-primary); color: var(--color-text-primary);
} }
.fingerprint { .fingerprint {
@@ -428,7 +428,7 @@ import {
.breakdown-bar { .breakdown-bar {
display: flex; display: flex;
height: 8px; height: 8px;
background: var(--color-text-heading); background: var(--color-surface-tertiary);
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
overflow: hidden; overflow: hidden;
} }
@@ -479,8 +479,8 @@ import {
.history-item { .history-item {
padding: 0.75rem; padding: 0.75rem;
background: var(--color-surface-inverse); background: var(--color-surface-primary);
border: 1px solid var(--color-text-heading); border: 1px solid var(--color-border-primary);
border-radius: var(--radius-md); border-radius: var(--radius-md);
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
} }
@@ -520,8 +520,8 @@ import {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
background: var(--color-surface-inverse); background: var(--color-surface-primary);
border: 1px solid var(--color-text-heading); border: 1px solid var(--color-border-primary);
border-radius: var(--radius-md); border-radius: var(--radius-md);
} }
@@ -558,7 +558,7 @@ import {
display: flex; display: flex;
gap: 0.5rem; gap: 0.5rem;
padding: 0.35rem 0; padding: 0.35rem 0;
border-bottom: 1px solid var(--color-text-heading); border-bottom: 1px solid var(--color-border-primary);
} }
.metadata-item dt { .metadata-item dt {
@@ -568,7 +568,7 @@ import {
.metadata-item dd { .metadata-item dd {
margin: 0; margin: 0;
color: var(--color-border-primary); color: var(--color-text-primary);
font-family: monospace; font-family: monospace;
font-size: 0.85rem; font-size: 0.85rem;
} }
@@ -577,7 +577,7 @@ import {
display: flex; display: flex;
gap: 0.5rem; gap: 0.5rem;
padding: 1rem 1.5rem; padding: 1rem 1.5rem;
border-top: 1px solid var(--color-text-heading); border-top: 1px solid var(--color-border-primary);
} }
.btn-primary, .btn-primary,
@@ -602,8 +602,8 @@ import {
.btn-secondary { .btn-secondary {
background: transparent; background: transparent;
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
color: var(--color-border-primary); color: var(--color-text-primary);
} }
.btn-secondary:hover { .btn-secondary:hover {
@@ -612,7 +612,7 @@ import {
.btn-danger { .btn-danger {
background: transparent; background: transparent;
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
color: var(--color-status-error); color: var(--color-status-error);
} }

View File

@@ -134,7 +134,7 @@ import { KeyExpiryAlert } from '../../core/api/trust.models';
margin: 0; margin: 0;
font-size: 1rem; font-size: 1rem;
font-weight: var(--font-weight-semibold); font-weight: var(--font-weight-semibold);
color: var(--color-border-primary); color: var(--color-text-primary);
} }
.expiry-warnings__title p { .expiry-warnings__title p {
@@ -145,8 +145,8 @@ import { KeyExpiryAlert } from '../../core/api/trust.models';
.btn-toggle { .btn-toggle {
background: transparent; background: transparent;
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
color: var(--color-border-primary); color: var(--color-text-primary);
border-radius: var(--radius-md); border-radius: var(--radius-md);
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
cursor: pointer; cursor: pointer;
@@ -172,8 +172,8 @@ import { KeyExpiryAlert } from '../../core/api/trust.models';
} }
.alert-card { .alert-card {
background: var(--color-text-heading); background: var(--color-surface-elevated);
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
padding: 1rem; padding: 1rem;
} }
@@ -199,7 +199,7 @@ import { KeyExpiryAlert } from '../../core/api/trust.models';
.alert-card__name { .alert-card__name {
font-weight: var(--font-weight-semibold); font-weight: var(--font-weight-semibold);
color: var(--color-border-primary); color: var(--color-text-primary);
} }
.purpose-badge { .purpose-badge {
@@ -245,7 +245,7 @@ import { KeyExpiryAlert } from '../../core/api/trust.models';
align-items: center; align-items: center;
gap: 1rem; gap: 1rem;
padding-top: 0.75rem; padding-top: 0.75rem;
border-top: 1px solid var(--color-text-heading); border-top: 1px solid var(--color-border-primary);
} }
.suggested-action { .suggested-action {

View File

@@ -334,8 +334,8 @@ export type WizardStep = 'review' | 'configure' | 'confirm' | 'rotating' | 'comp
} }
.wizard-modal { .wizard-modal {
background: var(--color-text-heading); background: var(--color-surface-elevated);
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
border-radius: var(--radius-2xl); border-radius: var(--radius-2xl);
width: 90%; width: 90%;
max-width: 640px; max-width: 640px;
@@ -349,7 +349,7 @@ export type WizardStep = 'review' | 'configure' | 'confirm' | 'rotating' | 'comp
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 1.25rem 1.5rem; padding: 1.25rem 1.5rem;
border-bottom: 1px solid var(--color-text-heading); border-bottom: 1px solid var(--color-border-primary);
} }
.wizard-header h2 { .wizard-header h2 {
@@ -360,7 +360,7 @@ export type WizardStep = 'review' | 'configure' | 'confirm' | 'rotating' | 'comp
.btn-close { .btn-close {
background: transparent; background: transparent;
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
color: var(--color-text-muted); color: var(--color-text-muted);
width: 32px; width: 32px;
height: 32px; height: 32px;
@@ -400,7 +400,7 @@ export type WizardStep = 'review' | 'configure' | 'confirm' | 'rotating' | 'comp
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
background: var(--color-text-heading); background: var(--color-surface-tertiary);
color: var(--color-text-secondary); color: var(--color-text-secondary);
font-weight: var(--font-weight-semibold); font-weight: var(--font-weight-semibold);
transition: all 0.2s; transition: all 0.2s;
@@ -432,7 +432,7 @@ export type WizardStep = 'review' | 'configure' | 'confirm' | 'rotating' | 'comp
.progress-connector { .progress-connector {
width: 40px; width: 40px;
height: 2px; height: 2px;
background: var(--color-text-heading); background: var(--color-surface-tertiary);
transition: background-color 0.2s; transition: background-color 0.2s;
} }
@@ -467,8 +467,8 @@ export type WizardStep = 'review' | 'configure' | 'confirm' | 'rotating' | 'comp
} }
.key-summary { .key-summary {
background: var(--color-surface-inverse); background: var(--color-surface-primary);
border: 1px solid var(--color-text-heading); border: 1px solid var(--color-border-primary);
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
padding: 1rem; padding: 1rem;
} }
@@ -491,7 +491,7 @@ export type WizardStep = 'review' | 'configure' | 'confirm' | 'rotating' | 'comp
} }
.summary-value { .summary-value {
color: var(--color-border-primary); color: var(--color-text-primary);
} }
.summary-value.mono { .summary-value.mono {
@@ -573,16 +573,16 @@ export type WizardStep = 'review' | 'configure' | 'confirm' | 'rotating' | 'comp
.form-group label { .form-group label {
font-size: 0.85rem; font-size: 0.85rem;
color: var(--color-border-primary); color: var(--color-text-primary);
} }
.form-group input, .form-group input,
.form-group select, .form-group select,
.form-group textarea { .form-group textarea {
background: var(--color-surface-inverse); background: var(--color-surface-primary);
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
border-radius: var(--radius-md); border-radius: var(--radius-md);
color: var(--color-border-primary); color: var(--color-text-primary);
padding: 0.6rem 0.75rem; padding: 0.6rem 0.75rem;
font-size: 0.9rem; font-size: 0.9rem;
} }
@@ -638,7 +638,7 @@ export type WizardStep = 'review' | 'configure' | 'confirm' | 'rotating' | 'comp
} }
.confirm-value { .confirm-value {
color: var(--color-border-primary); color: var(--color-text-primary);
} }
.confirm-value.mono { .confirm-value.mono {
@@ -668,7 +668,7 @@ export type WizardStep = 'review' | 'configure' | 'confirm' | 'rotating' | 'comp
.rotating-spinner { .rotating-spinner {
width: 60px; width: 60px;
height: 60px; height: 60px;
border: 4px solid var(--color-text-heading); border: 4px solid var(--color-surface-tertiary);
border-top-color: var(--color-status-info); border-top-color: var(--color-status-info);
border-radius: var(--radius-full); border-radius: var(--radius-full);
animation: spin 1s linear infinite; animation: spin 1s linear infinite;
@@ -722,8 +722,8 @@ export type WizardStep = 'review' | 'configure' | 'confirm' | 'rotating' | 'comp
.new-key-info { .new-key-info {
margin-top: 1.5rem; margin-top: 1.5rem;
padding: 1rem; padding: 1rem;
background: var(--color-surface-inverse); background: var(--color-surface-primary);
border: 1px solid var(--color-text-heading); border: 1px solid var(--color-border-primary);
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
text-align: left; text-align: left;
width: 100%; width: 100%;
@@ -741,7 +741,7 @@ export type WizardStep = 'review' | 'configure' | 'confirm' | 'rotating' | 'comp
} }
.new-key-value { .new-key-value {
color: var(--color-border-primary); color: var(--color-text-primary);
} }
.new-key-value.mono { .new-key-value.mono {
@@ -764,7 +764,7 @@ export type WizardStep = 'review' | 'configure' | 'confirm' | 'rotating' | 'comp
justify-content: flex-end; justify-content: flex-end;
gap: 0.75rem; gap: 0.75rem;
padding: 1rem 1.5rem; padding: 1rem 1.5rem;
border-top: 1px solid var(--color-text-heading); border-top: 1px solid var(--color-border-primary);
} }
.btn-primary, .btn-primary,
@@ -794,8 +794,8 @@ export type WizardStep = 'review' | 'configure' | 'confirm' | 'rotating' | 'comp
.btn-secondary { .btn-secondary {
background: transparent; background: transparent;
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
color: var(--color-border-primary); color: var(--color-text-primary);
} }
.btn-secondary:hover { .btn-secondary:hover {

View File

@@ -288,10 +288,10 @@ import { KeyRotationWizardComponent } from './key-rotation-wizard.component';
.filter-group input, .filter-group input,
.filter-group select { .filter-group select {
background: var(--color-surface-inverse); background: var(--color-surface-primary);
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
border-radius: var(--radius-md); border-radius: var(--radius-md);
color: var(--color-border-primary); color: var(--color-text-primary);
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
min-width: 180px; min-width: 180px;
} }
@@ -340,11 +340,11 @@ import { KeyRotationWizardComponent } from './key-rotation-wizard.component';
.key-table td { .key-table td {
padding: 0.75rem 1rem; padding: 0.75rem 1rem;
text-align: left; text-align: left;
border-bottom: 1px solid var(--color-text-heading); border-bottom: 1px solid var(--color-border-primary);
} }
.key-table th { .key-table th {
background: var(--color-surface-inverse); background: var(--color-surface-primary);
color: var(--color-text-muted); color: var(--color-text-muted);
font-weight: var(--font-weight-medium); font-weight: var(--font-weight-medium);
font-size: 0.85rem; font-size: 0.85rem;
@@ -384,7 +384,7 @@ import { KeyRotationWizardComponent } from './key-rotation-wizard.component';
} }
.key-name strong { .key-name strong {
color: var(--color-border-primary); color: var(--color-text-primary);
} }
.key-desc { .key-desc {
@@ -493,8 +493,8 @@ import { KeyRotationWizardComponent } from './key-rotation-wizard.component';
.btn-action { .btn-action {
background: transparent; background: transparent;
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
color: var(--color-border-primary); color: var(--color-text-primary);
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
padding: 0.25rem 0.5rem; padding: 0.25rem 0.5rem;
font-size: 0.8rem; font-size: 0.8rem;
@@ -528,13 +528,13 @@ import { KeyRotationWizardComponent } from './key-rotation-wizard.component';
align-items: center; align-items: center;
gap: 1rem; gap: 1rem;
padding: 1rem; padding: 1rem;
border-top: 1px solid var(--color-text-heading); border-top: 1px solid var(--color-border-primary);
} }
.key-dashboard__pagination button { .key-dashboard__pagination button {
background: transparent; background: transparent;
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
color: var(--color-border-primary); color: var(--color-text-primary);
border-radius: var(--radius-md); border-radius: var(--radius-md);
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
cursor: pointer; cursor: pointer;

View File

@@ -207,8 +207,8 @@ const TRUST_ADMIN_TABS: readonly TrustAdminTab[] = [
styles: [` styles: [`
:host { :host {
display: block; display: block;
background: var(--color-surface-inverse); background: var(--color-surface-primary);
color: var(--color-border-primary); color: var(--color-text-primary);
min-height: 100vh; min-height: 100vh;
} }
@@ -257,8 +257,8 @@ const TRUST_ADMIN_TABS: readonly TrustAdminTab[] = [
.btn-secondary { .btn-secondary {
background: transparent; background: transparent;
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
color: var(--color-border-primary); color: var(--color-text-primary);
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
cursor: pointer; cursor: pointer;
@@ -297,8 +297,8 @@ const TRUST_ADMIN_TABS: readonly TrustAdminTab[] = [
display: flex; display: flex;
align-items: center; align-items: center;
gap: 1rem; gap: 1rem;
background: var(--color-text-heading); background: var(--color-surface-elevated);
border: 1px solid var(--color-text-heading); border: 1px solid var(--color-border-primary);
border-radius: var(--radius-xl); border-radius: var(--radius-xl);
padding: 1rem; padding: 1rem;
} }
@@ -358,7 +358,7 @@ const TRUST_ADMIN_TABS: readonly TrustAdminTab[] = [
.trust-admin__tabs { .trust-admin__tabs {
display: flex; display: flex;
gap: 0.25rem; gap: 0.25rem;
border-bottom: 1px solid var(--color-text-heading); border-bottom: 1px solid var(--color-border-primary);
margin-bottom: 1.5rem; margin-bottom: 1.5rem;
} }
@@ -374,7 +374,7 @@ const TRUST_ADMIN_TABS: readonly TrustAdminTab[] = [
} }
.trust-admin__tab:hover { .trust-admin__tab:hover {
color: var(--color-border-primary); color: var(--color-text-primary);
} }
.trust-admin__tab--active { .trust-admin__tab--active {
@@ -405,8 +405,8 @@ const TRUST_ADMIN_TABS: readonly TrustAdminTab[] = [
} }
.trust-admin__content { .trust-admin__content {
background: var(--color-text-heading); background: var(--color-surface-elevated);
border: 1px solid var(--color-text-heading); border: 1px solid var(--color-border-primary);
border-radius: var(--radius-xl); border-radius: var(--radius-xl);
min-height: 400px; min-height: 400px;
} }

View File

@@ -250,10 +250,10 @@ import {
.filter-group input, .filter-group input,
.filter-group select { .filter-group select {
background: var(--color-surface-inverse); background: var(--color-surface-primary);
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
border-radius: var(--radius-md); border-radius: var(--radius-md);
color: var(--color-border-primary); color: var(--color-text-primary);
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
min-width: 140px; min-width: 140px;
} }
@@ -315,8 +315,8 @@ import {
} }
.event-card { .event-card {
background: var(--color-surface-inverse); background: var(--color-surface-primary);
border: 1px solid var(--color-text-heading); border: 1px solid var(--color-border-primary);
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
padding: 1rem; padding: 1rem;
cursor: pointer; cursor: pointer;
@@ -324,7 +324,7 @@ import {
} }
.event-card:hover { .event-card:hover {
border-color: var(--color-text-primary); border-color: var(--color-border-primary);
} }
.event-card--warning { .event-card--warning {
@@ -368,7 +368,7 @@ import {
.event-type { .event-type {
font-weight: var(--font-weight-medium); font-weight: var(--font-weight-medium);
color: var(--color-border-primary); color: var(--color-text-primary);
} }
.event-time { .event-time {
@@ -412,7 +412,7 @@ import {
.event-card__details { .event-card__details {
margin-top: 1rem; margin-top: 1rem;
padding-top: 1rem; padding-top: 1rem;
border-top: 1px solid var(--color-text-heading); border-top: 1px solid var(--color-border-primary);
} }
.event-details-grid { .event-details-grid {
@@ -435,7 +435,7 @@ import {
.detail-item dd { .detail-item dd {
margin: 0; margin: 0;
color: var(--color-border-primary); color: var(--color-text-primary);
} }
.mono { .mono {
@@ -464,10 +464,10 @@ import {
.change-value { .change-value {
margin: 0; margin: 0;
padding: 0.5rem; padding: 0.5rem;
background: var(--color-text-heading); background: var(--color-surface-tertiary);
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
font-size: 0.8rem; font-size: 0.8rem;
color: var(--color-border-primary); color: var(--color-text-primary);
overflow-x: auto; overflow-x: auto;
} }
@@ -485,10 +485,10 @@ import {
.details-value { .details-value {
margin: 0; margin: 0;
padding: 0.5rem; padding: 0.5rem;
background: var(--color-text-heading); background: var(--color-surface-tertiary);
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
font-size: 0.8rem; font-size: 0.8rem;
color: var(--color-border-primary); color: var(--color-text-primary);
overflow-x: auto; overflow-x: auto;
} }
@@ -499,13 +499,13 @@ import {
gap: 1rem; gap: 1rem;
padding: 1rem; padding: 1rem;
margin-top: 1rem; margin-top: 1rem;
border-top: 1px solid var(--color-text-heading); border-top: 1px solid var(--color-border-primary);
} }
.audit-log__pagination button { .audit-log__pagination button {
background: transparent; background: transparent;
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
color: var(--color-border-primary); color: var(--color-text-primary);
border-radius: var(--radius-md); border-radius: var(--radius-md);
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
cursor: pointer; cursor: pointer;

View File

@@ -278,8 +278,8 @@ import {
`, `,
styles: [` styles: [`
.config-panel { .config-panel {
background: var(--color-surface-inverse); background: var(--color-surface-primary);
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
border-radius: var(--radius-xl); border-radius: var(--radius-xl);
margin-bottom: 1.5rem; margin-bottom: 1.5rem;
overflow: hidden; overflow: hidden;
@@ -290,8 +290,8 @@ import {
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 1rem 1.5rem; padding: 1rem 1.5rem;
background: var(--color-text-heading); background: var(--color-surface-elevated);
border-bottom: 1px solid var(--color-text-heading); border-bottom: 1px solid var(--color-border-primary);
} }
.config-panel__header h3 { .config-panel__header h3 {
@@ -302,7 +302,7 @@ import {
.btn-close { .btn-close {
background: transparent; background: transparent;
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
color: var(--color-text-muted); color: var(--color-text-muted);
width: 28px; width: 28px;
height: 28px; height: 28px;
@@ -362,7 +362,7 @@ import {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
font-size: 0.85rem; font-size: 0.85rem;
color: var(--color-border-primary); color: var(--color-text-primary);
} }
.weight-value { .weight-value {
@@ -374,7 +374,7 @@ import {
.weight-control input[type="range"] { .weight-control input[type="range"] {
width: 100%; width: 100%;
height: 6px; height: 6px;
background: var(--color-text-heading); background: var(--color-surface-tertiary);
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
appearance: none; appearance: none;
cursor: pointer; cursor: pointer;
@@ -395,7 +395,7 @@ import {
} }
.preview-section { .preview-section {
background: var(--color-text-heading); background: var(--color-surface-tertiary);
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
padding: 1rem; padding: 1rem;
} }
@@ -432,7 +432,7 @@ import {
} }
.preview-score--new { .preview-score--new {
color: var(--color-border-primary); color: var(--color-text-primary);
} }
.preview-score--new.score-up { .preview-score--new.score-up {
@@ -486,7 +486,7 @@ import {
.impact-value { .impact-value {
font-size: 1.25rem; font-size: 1.25rem;
font-weight: var(--font-weight-semibold); font-weight: var(--font-weight-semibold);
color: var(--color-border-primary); color: var(--color-text-primary);
} }
.impact-label { .impact-label {
@@ -513,10 +513,10 @@ import {
} }
.threshold-control input[type="number"] { .threshold-control input[type="number"] {
background: var(--color-text-heading); background: var(--color-surface-tertiary);
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
color: var(--color-border-primary); color: var(--color-text-primary);
padding: 0.4rem 0.6rem; padding: 0.4rem 0.6rem;
width: 100%; width: 100%;
} }
@@ -555,8 +555,8 @@ import {
justify-content: flex-end; justify-content: flex-end;
gap: 0.5rem; gap: 0.5rem;
padding: 1rem 1.5rem; padding: 1rem 1.5rem;
background: var(--color-text-heading); background: var(--color-surface-elevated);
border-top: 1px solid var(--color-text-heading); border-top: 1px solid var(--color-border-primary);
} }
.btn-save, .btn-save,
@@ -586,8 +586,8 @@ import {
.btn-cancel { .btn-cancel {
background: transparent; background: transparent;
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
color: var(--color-border-primary); color: var(--color-text-primary);
} }
.btn-cancel:hover { .btn-cancel:hover {
@@ -596,7 +596,7 @@ import {
.btn-reset { .btn-reset {
background: transparent; background: transparent;
border: 1px solid var(--color-text-primary); border: 1px solid var(--color-border-primary);
color: var(--color-status-warning-border); color: var(--color-status-warning-border);
margin-right: auto; margin-right: auto;
} }

View File

@@ -122,29 +122,15 @@ interface NavSectionGroup {
<div class="sb-divider"></div> <div class="sb-divider"></div>
} }
@if (!effectiveCollapsed && section.displayChildren.length > 0) { @if (!effectiveCollapsed && section.displayChildren.length > 0) {
<!-- Section with foldable children: link + chevron toggle --> <!-- Section with children: link + always-visible sub-items -->
<div class="sb-section" [class.sb-section--folded]="sidebarPrefs.collapsedSections().has(section.id)"> <div class="sb-section">
<div class="sb-section__head"> <app-sidebar-nav-item
<app-sidebar-nav-item [label]="section.label"
[label]="section.label" [icon]="section.icon"
[icon]="section.icon" [route]="section.route"
[route]="section.route" [badge]="section.sectionBadge"
[badge]="section.sectionBadge" [collapsed]="effectiveCollapsed"
[collapsed]="effectiveCollapsed" ></app-sidebar-nav-item>
></app-sidebar-nav-item>
<button
type="button"
class="sb-section__chevron"
(click)="sidebarPrefs.toggleSection(section.id)"
[attr.aria-expanded]="!sidebarPrefs.collapsedSections().has(section.id)"
[attr.aria-controls]="'nav-sec-' + section.id"
[attr.aria-label]="(sidebarPrefs.collapsedSections().has(section.id) ? 'Expand ' : 'Collapse ') + section.label"
>
<svg class="sb-section__chevron-icon" viewBox="0 0 16 16" width="10" height="10" aria-hidden="true">
<path d="M4 6l4 4.5 4-4.5z" fill="currentColor"/>
</svg>
</button>
</div>
<div class="sb-section__body" [id]="'nav-sec-' + section.id"> <div class="sb-section__body" [id]="'nav-sec-' + section.id">
<div class="sb-section__body-inner"> <div class="sb-section__body-inner">
@for (child of section.displayChildren; track child.id) { @for (child of section.displayChildren; track child.id) {
@@ -247,7 +233,6 @@ interface NavSectionGroup {
.sidebar--collapsed.sidebar--flyout .sb-group__header, .sidebar--collapsed.sidebar--flyout .sb-group__header,
.sidebar--collapsed.sidebar--flyout .sb-divider, .sidebar--collapsed.sidebar--flyout .sb-divider,
.sidebar--collapsed.sidebar--flyout .sb-section__head,
.sidebar--collapsed.sidebar--flyout .sb-section__body { .sidebar--collapsed.sidebar--flyout .sb-section__body {
animation: flyoutFadeIn 0.2s ease-out both; animation: flyoutFadeIn 0.2s ease-out both;
} }
@@ -477,83 +462,15 @@ interface NavSectionGroup {
margin: 0.25rem 0; margin: 0.25rem 0;
} }
/* Section head: nav-item + chevron */ /* Section children */
.sb-section__head {
position: relative;
display: flex;
align-items: center;
margin: 0;
padding: 0;
}
.sb-section__head > app-sidebar-nav-item {
flex: 1;
min-width: 0;
}
.sb-section__chevron {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
border: none;
border-radius: 5px;
background: transparent;
color: var(--color-sidebar-text-muted);
cursor: pointer;
opacity: 0.25;
transition: opacity 0.2s, background 0.2s, color 0.2s, transform 0.15s;
margin-right: 0.25rem;
&:hover {
opacity: 1 !important;
background: rgba(245, 166, 35, 0.12);
color: var(--color-sidebar-active-text);
transform: scale(1.1);
}
&:focus-visible {
opacity: 1 !important;
outline: 1.5px solid var(--color-sidebar-active-border);
outline-offset: -1.5px;
}
}
.sb-section__head:hover .sb-section__chevron {
opacity: 0.7;
}
.sb-section--folded .sb-section__chevron {
opacity: 0.45;
}
.sb-section__chevron-icon {
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.sb-section--folded .sb-section__chevron-icon {
transform: rotate(-90deg);
}
/* Animated section body */
.sb-section__body { .sb-section__body {
display: grid; display: block;
grid-template-rows: 1fr;
transition: grid-template-rows 0.28s cubic-bezier(0.4, 0, 0.2, 1);
}
.sb-section--folded .sb-section__body {
grid-template-rows: 0fr;
} }
.sb-section__body-inner { .sb-section__body-inner {
overflow: hidden;
margin-left: 1.375rem; margin-left: 1.375rem;
padding-left: 0.375rem; padding-left: 0.375rem;
position: relative; position: relative;
transition: opacity 0.24s ease;
&::before { &::before {
content: ''; content: '';
@@ -571,10 +488,6 @@ interface NavSectionGroup {
} }
} }
.sb-section--folded .sb-section__body-inner {
opacity: 0;
}
/* ================================================================ /* ================================================================
Footer Footer
================================================================ */ ================================================================ */
@@ -780,7 +693,7 @@ export class AppSidebarComponent implements AfterViewInit {
StellaOpsScopes.RELEASE_WRITE, StellaOpsScopes.RELEASE_WRITE,
StellaOpsScopes.RELEASE_PUBLISH, StellaOpsScopes.RELEASE_PUBLISH,
], ],
}, },
{ id: 'rel-hotfix-list', label: 'Hotfixes', route: '/releases/hotfixes', icon: 'zap' }, { id: 'rel-hotfix-list', label: 'Hotfixes', route: '/releases/hotfixes', icon: 'zap' },
{ id: 'rel-envs', label: 'Environments', route: '/releases/environments', icon: 'globe' }, { id: 'rel-envs', label: 'Environments', route: '/releases/environments', icon: 'globe' },
{ {

View File

@@ -997,6 +997,7 @@ export class GlobalSearchComponent implements OnInit, OnDestroy {
readonly placeholderIndex = signal(0); readonly placeholderIndex = signal(0);
readonly searchResponse = signal<UnifiedSearchResponse | null>(null); readonly searchResponse = signal<UnifiedSearchResponse | null>(null);
readonly suggestionViability = signal<SearchSuggestionViabilityResponse | null>(null); readonly suggestionViability = signal<SearchSuggestionViabilityResponse | null>(null);
readonly suggestionViabilityStatus = signal<'idle' | 'ready' | 'unavailable'>('idle');
readonly recentSearches = signal<string[]>([]); readonly recentSearches = signal<string[]>([]);
readonly suppressedStarterQueries = signal<string[]>([]); readonly suppressedStarterQueries = signal<string[]>([]);
readonly expandedCardKey = signal<string | null>(null); readonly expandedCardKey = signal<string | null>(null);
@@ -1243,6 +1244,29 @@ export class GlobalSearchComponent implements OnInit, OnDestroy {
}; };
} }
if (this.isSearchEnvironmentLikelyUnready(response)) {
return {
status: 'insufficient',
eyebrow,
title: this.t('ui.search.answer.title.unready', 'Search needs live data'),
summary: this.t(
'ui.search.answer.summary.unready',
'The live search service accepted "{query}" but returned zero indexed matches. This usually means search ingestion or index rebuild is not ready for this environment.',
{ query },
),
evidence: this.t(
'ui.search.answer.evidence.unready',
'Search suggestion verification is also unavailable on this environment, so starter queries cannot be trusted until the search APIs are fully configured.',
),
citations: [],
questionLabel: '',
questions: [],
guidanceLabel: null,
guidance: [],
nextSearches: [],
};
}
const clarifyingQuestions = this.clarifyingQuestions(); const clarifyingQuestions = this.clarifyingQuestions();
if (clarifyingQuestions.length > 0) { if (clarifyingQuestions.length > 0) {
return { return {
@@ -2106,6 +2130,7 @@ export class GlobalSearchComponent implements OnInit, OnDestroy {
if (queries.length === 0) { if (queries.length === 0) {
this.suggestionViability.set(null); this.suggestionViability.set(null);
this.suggestionViabilityStatus.set('idle');
return; return;
} }
@@ -2113,6 +2138,7 @@ export class GlobalSearchComponent implements OnInit, OnDestroy {
.pipe(takeUntil(this.destroy$)) .pipe(takeUntil(this.destroy$))
.subscribe((response) => { .subscribe((response) => {
this.suggestionViability.set(response); this.suggestionViability.set(response);
this.suggestionViabilityStatus.set(response ? 'ready' : 'unavailable');
}); });
} }
@@ -2399,6 +2425,18 @@ export class GlobalSearchComponent implements OnInit, OnDestroy {
|| (response.synthesis?.sourceCount ?? 0) > 0; || (response.synthesis?.sourceCount ?? 0) > 0;
} }
private hasZeroIndexedMatches(response: UnifiedSearchResponse): boolean {
return !this.hasSearchEvidence(response)
&& (response.diagnostics?.ftsMatches ?? 0) === 0
&& (response.diagnostics?.vectorMatches ?? 0) === 0
&& (response.diagnostics?.entityCardCount ?? 0) === 0;
}
private isSearchEnvironmentLikelyUnready(response: UnifiedSearchResponse): boolean {
return this.suggestionViabilityStatus() === 'unavailable'
&& this.hasZeroIndexedMatches(response);
}
private reconcileSuggestedExecution(response: UnifiedSearchResponse): void { private reconcileSuggestedExecution(response: UnifiedSearchResponse): void {
const pending = this.pendingSuggestedExecution; const pending = this.pendingSuggestedExecution;
if (!pending) { if (!pending) {

View File

@@ -818,6 +818,25 @@ describe('GlobalSearchComponent', () => {
expect(guidanceItems).toContain('Add the part of the finding that matters most right now.'); expect(guidanceItems).toContain('Add the part of the finding that matters most right now.');
}); });
it('renders a search-readiness answer instead of clarify guidance when viability preflight is unavailable', async () => {
searchClient.evaluateSuggestions.and.returnValue(of(null));
component.onFocus();
fixture.detectChanges();
component.onQueryChange('database connectivity');
await waitForDebounce();
fixture.detectChanges();
const answerPanel = fixture.nativeElement.querySelector('[data-answer-status="insufficient"]') as HTMLElement | null;
const guidanceItems = fixture.nativeElement.querySelectorAll('[data-answer-guidance="clarify"]');
expect(answerPanel).not.toBeNull();
expect(answerPanel?.textContent).toContain('Search needs live data');
expect(answerPanel?.textContent).toContain('returned zero indexed matches');
expect(answerPanel?.textContent).toContain('suggestion verification is also unavailable');
expect(guidanceItems.length).toBe(0);
});
it('does not hard-filter search requests to the current route scope', async () => { it('does not hard-filter search requests to the current route scope', async () => {
ambientContext.buildContextFilter.and.returnValue({ domains: ['findings'] } as any); ambientContext.buildContextFilter.and.returnValue({ domains: ['findings'] } as any);

View File

@@ -362,6 +362,34 @@ test.describe('Unified Search - Experience Quality UX', () => {
await expect(page.locator('app-global-search input[type="text"]')).toHaveValue('mystery release blocker'); await expect(page.locator('app-global-search input[type="text"]')).toHaveValue('mystery release blocker');
}); });
test('shows search-readiness guidance instead of blaming the query when the live search corpus is unavailable', async ({ page }) => {
await page.unroute('**/api/v1/search/suggestions/evaluate');
await page.route('**/api/v1/search/suggestions/evaluate', async (route) =>
route.fulfill({
status: 404,
body: '',
}),
);
await mockSearchResponses(page, () => emptyResponse('database connectivity'));
await page.goto('/releases/versions');
await expect(page.locator('aside.sidebar')).toBeVisible({ timeout: 15_000 });
const searchInput = page.locator('app-global-search input[type="text"]');
await searchInput.focus();
await waitForResults(page);
await searchInput.fill('database connectivity');
await searchInput.press('Enter');
await waitForResults(page);
const answerPanel = page.locator('[data-answer-status="insufficient"]');
await expect(answerPanel).toBeVisible();
await expect(answerPanel).toContainText(/search needs live data/i);
await expect(answerPanel).toContainText(/returned zero indexed matches/i);
await expect(answerPanel).toContainText(/suggestion verification is also unavailable/i);
await expect(page.locator('[data-answer-guidance="clarify"]')).toHaveCount(0);
});
test('uses backend answer framing and shows overflow as secondary results without manual filters', async ({ page }) => { test('uses backend answer framing and shows overflow as secondary results without manual filters', async ({ page }) => {
await mockSearchResponses(page, (query) => await mockSearchResponses(page, (query) =>
query.includes('critical findings') query.includes('critical findings')

View File

@@ -0,0 +1,16 @@
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
pool: 'threads',
poolOptions: {
threads: {
singleThread: true,
},
},
fileParallelism: false,
isolate: false,
maxWorkers: 1,
minWorkers: 1,
},
});