chore: update Angular cache and TypeScript build info files

This commit is contained in:
Bruno Charest 2025-10-02 13:46:33 -04:00
parent 20cc6e9215
commit cf4a3b60eb
11 changed files with 210 additions and 157 deletions

File diff suppressed because one or more lines are too long

View File

@ -453,14 +453,15 @@
</button> </button>
</div> </div>
<div class="relative w-full lg:flex-1 lg:max-w-none lg:min-w-0"> <div class="relative w-full lg:flex-1 lg:max-w-none lg:min-w-0">
<svg class="pointer-events-none absolute left-3 top-1/2 h-5 w-5 -translate-y-1/2 text-obs-l-text-muted dark:text-obs-d-text-muted" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" /></svg> <app-search-input-with-assistant
<input [value]="sidebarSearchTerm()"
type="text" (valueChange)="updateSearchTerm($event, true)"
placeholder="Rechercher dans la voûte..." (submit)="onSearchSubmit($event)"
[(ngModel)]="sidebarSearchTerm" [placeholder]="'Rechercher dans la voûte...'"
(ngModelChange)="activeView.set('search')" [context]="'vault-sidebar'"
class="w-full rounded-full border border-obs-l-border bg-obs-l-bg-secondary/60 pl-11 pr-4 py-2.5 text-sm text-obs-l-text-main placeholder:text-obs-l-text-muted shadow-sm focus:outline-none focus:ring-2 focus:ring-obs-l-accent dark:border-obs-d-border dark:bg-obs-d-bg-secondary/60 dark:text-obs-d-text-main dark:placeholder:text-obs-d-text-muted dark:focus:ring-obs-d-accent" [showSearchIcon]="true"
aria-label="Rechercher dans la voûte" [showExamples]="false"
[inputClass]="'w-full rounded-full border border-border bg-bg-muted/70 py-2.5 pr-4 text-sm text-text-main placeholder:text-text-muted shadow-subtle focus:outline-none focus:ring-2 focus:ring-ring transition-all dark:border-gray-600 dark:bg-gray-800/80 dark:text-gray-100 dark:placeholder-gray-400 dark:focus:ring-blue-500'"
/> />
</div> </div>
</div> </div>

View File

@ -195,8 +195,9 @@
(submit)="onSearchSubmit($event)" (submit)="onSearchSubmit($event)"
[placeholder]="'Rechercher dans la voûte...'" [placeholder]="'Rechercher dans la voûte...'"
[context]="'vault-sidebar'" [context]="'vault-sidebar'"
[showSearchIcon]="false" [showSearchIcon]="true"
[inputClass]="'input'" [showExamples]="false"
[inputClass]="'w-full rounded-full border border-border bg-bg-muted/70 py-2.5 pr-4 text-sm text-text-main placeholder:text-text-muted shadow-subtle focus:outline-none focus:ring-2 focus:ring-ring transition-all dark:border-gray-600 dark:bg-gray-800/80 dark:text-gray-100 dark:placeholder-gray-400 dark:focus:ring-blue-500'"
/> />
</div> </div>
@if (activeTagDisplay(); as tagDisplay) { @if (activeTagDisplay(); as tagDisplay) {

View File

@ -559,8 +559,7 @@ export class AppComponent implements OnDestroy {
onSearchSubmit(query: string): void { onSearchSubmit(query: string): void {
if (query && query.trim()) { if (query && query.trim()) {
// Add to history // History is already handled by SearchInputWithAssistant using its [context]
this.searchHistoryService.add('vault-search', query);
// Update search term and switch to search view // Update search term and switch to search view
this.sidebarSearchTerm.set(query); this.sidebarSearchTerm.set(query);
this.activeView.set('search'); this.activeView.set('search');

View File

@ -1,23 +1,27 @@
import { Component, ChangeDetectionStrategy, input, output } from '@angular/core'; import { Component, ChangeDetectionStrategy, input, output } from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { SearchInputWithAssistantComponent } from '../../../../components/search-input-with-assistant/search-input-with-assistant.component';
import { GraphConfig } from '../../graph-settings.types'; import { GraphConfig } from '../../graph-settings.types';
@Component({ @Component({
selector: 'app-graph-filters-section', selector: 'app-graph-filters-section',
standalone: true, standalone: true,
imports: [CommonModule, FormsModule], imports: [CommonModule, FormsModule, SearchInputWithAssistantComponent],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
template: ` template: `
<div class="space-y-3"> <div class="space-y-3">
<!-- Search input --> <!-- Search input -->
<div> <div>
<input <app-search-input-with-assistant
type="text" [placeholder]="'Search files...'"
[ngModel]="config().search" [value]="config().search || ''"
(ngModelChange)="onSearchChange($event)" [context]="'graph-filters'"
placeholder="Search files..." [showSearchIcon]="true"
class="w-full px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-colors"> [showExamples]="false"
(valueChange)="onSearchChange($event)"
(submit)="onSearchChange($event)"
/>
</div> </div>
<!-- Toggle options --> <!-- Toggle options -->

View File

@ -1,4 +1,4 @@
import { Component, ChangeDetectionStrategy, input, output, inject, effect } from '@angular/core'; import { Component, ChangeDetectionStrategy, input, output, inject, effect, signal } from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { GraphConfig } from '../graph-settings.types'; import { GraphConfig } from '../graph-settings.types';
import { GraphSettingsService } from '../graph-settings.service'; import { GraphSettingsService } from '../graph-settings.service';
@ -11,7 +11,7 @@ import { GraphForcesSectionComponent } from './sections/forces-section.component
selector: 'app-graph-settings-panel', selector: 'app-graph-settings-panel',
standalone: true, standalone: true,
imports: [ imports: [
CommonModule, CommonModule,
GraphFiltersSectionComponent, GraphFiltersSectionComponent,
GraphGroupsSectionComponent, GraphGroupsSectionComponent,
GraphDisplaySectionComponent, GraphDisplaySectionComponent,
@ -52,99 +52,82 @@ import { GraphForcesSectionComponent } from './sections/forces-section.component
</div> </div>
<div class="panel-body"> <div class="panel-body">
<section class="section"> <p-accordion
<button type="button" (click)="toggleSection('filter')" class="section-header"> class="graph-settings-accordion"
<div class="flex items-center gap-2"> [multiple]="true"
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> [value]="accordionValue()"
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" /> (valueChange)="onAccordionValueChange($event)">
</svg>
<span>Filters</span>
</div>
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 transition-transform" [class.rotate-180]="!config()['collapse-filter']" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
</button>
@if (!config()['collapse-filter']) { <p-accordion-panel value="filters">
<div class="section-content"> <p-accordion-header>
<span class="header-content">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
</svg>
<span>Filters</span>
</span>
</p-accordion-header>
<p-accordion-content>
<app-graph-filters-section <app-graph-filters-section
[config]="config()" [config]="config()"
(configChange)="onConfigChange($event)"> (configChange)="onConfigChange($event)">
</app-graph-filters-section> </app-graph-filters-section>
</div> </p-accordion-content>
} </p-accordion-panel>
</section>
<section class="section"> <p-accordion-panel value="groups">
<button type="button" (click)="toggleSection('color-groups')" class="section-header"> <p-accordion-header>
<div class="flex items-center gap-2"> <span class="header-content">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01" />
</svg> </svg>
<span>Groups</span> <span>Groups</span>
</div> </span>
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 transition-transform" [class.rotate-180]="!config()['collapse-color-groups']" fill="none" viewBox="0 0 24 24" stroke="currentColor"> </p-accordion-header>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" /> <p-accordion-content>
</svg>
</button>
@if (!config()['collapse-color-groups']) {
<div class="section-content">
<app-graph-groups-section <app-graph-groups-section
[config]="config()" [config]="config()"
(configChange)="onConfigChange($event)"> (configChange)="onConfigChange($event)">
</app-graph-groups-section> </app-graph-groups-section>
</div> </p-accordion-content>
} </p-accordion-panel>
</section>
<section class="section"> <p-accordion-panel value="display">
<button type="button" (click)="toggleSection('display')" class="section-header"> <p-accordion-header>
<div class="flex items-center gap-2"> <span class="header-content">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg> </svg>
<span>Display</span> <span>Display</span>
</div> </span>
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 transition-transform" [class.rotate-180]="!config()['collapse-display']" fill="none" viewBox="0 0 24 24" stroke="currentColor"> </p-accordion-header>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" /> <p-accordion-content>
</svg>
</button>
@if (!config()['collapse-display']) {
<div class="section-content">
<app-graph-display-section <app-graph-display-section
[config]="config()" [config]="config()"
(configChange)="onConfigChange($event)" (configChange)="onConfigChange($event)"
(animateRequested)="animateRequested.emit()"> (animateRequested)="animateRequested.emit()">
</app-graph-display-section> </app-graph-display-section>
</div> </p-accordion-content>
} </p-accordion-panel>
</section>
<section class="section"> <p-accordion-panel value="forces">
<button type="button" (click)="toggleSection('forces')" class="section-header"> <p-accordion-header>
<div class="flex items-center gap-2"> <span class="header-content">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg> </svg>
<span>Forces</span> <span>Forces</span>
</div> </span>
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 transition-transform" [class.rotate-180]="!config()['collapse-forces']" fill="none" viewBox="0 0 24 24" stroke="currentColor"> </p-accordion-header>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" /> <p-accordion-content>
</svg>
</button>
@if (!config()['collapse-forces']) {
<div class="section-content">
<app-graph-forces-section <app-graph-forces-section
[config]="config()" [config]="config()"
(configChange)="onConfigChange($event)"> (configChange)="onConfigChange($event)">
</app-graph-forces-section> </app-graph-forces-section>
</div> </p-accordion-content>
} </p-accordion-panel>
</section> </p-accordion>
</div> </div>
</div> </div>
} }
@ -204,53 +187,76 @@ import { GraphForcesSectionComponent } from './sections/forces-section.component
padding: 1rem; padding: 1rem;
} }
.section { :host ::ng-deep .graph-settings-accordion .p-accordion-panel {
margin-bottom: 1rem; border: 1px solid rgba(148, 163, 184, 0.35);
border: 1px solid #E5E7EB; border-radius: 0.85rem;
border-radius: 0.5rem; background-color: #ffffff;
overflow: hidden; overflow: hidden;
margin-bottom: 0.875rem;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
} }
:host-context(.dark) .section { :host ::ng-deep .graph-settings-accordion .p-accordion-panel:last-of-type {
border-color: #374151; margin-bottom: 0;
} }
.section-header { :host ::ng-deep .graph-settings-accordion .p-accordion-header {
width: 100%; font-size: 0.9rem;
font-weight: 600;
color: #1e293b;
padding: 0.95rem 1.2rem;
background: linear-gradient(180deg, rgba(248, 250, 252, 0.9) 0%, rgba(248, 250, 252, 0.6) 100%);
}
:host ::ng-deep .graph-settings-accordion .p-accordion-header .header-content {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; gap: 0.6rem;
padding: 0.75rem 1rem;
background-color: #F9FAFB;
font-size: 0.875rem;
font-weight: 600;
color: #111827;
cursor: pointer;
transition: background-color 0.2s;
border: none;
text-align: left;
} }
.section-header:hover { :host ::ng-deep .graph-settings-accordion .p-accordion-header:hover {
background-color: #F3F4F6; background: rgba(241, 245, 249, 0.9);
} }
:host-context(.dark) .section-header { :host ::ng-deep .graph-settings-accordion .p-accordion-toggle-icon {
background-color: #374151; color: #0f172a;
color: #F3F4F6;
} }
:host-context(.dark) .section-header:hover { :host ::ng-deep .graph-settings-accordion .p-accordion-panel-active {
background-color: #4B5563; border-color: rgba(59, 130, 246, 0.45);
box-shadow: 0 12px 24px -16px rgba(15, 23, 42, 0.35);
} }
.section-content { :host ::ng-deep .graph-settings-accordion .p-accordion-content {
padding: 1rem; background-color: #ffffff;
background-color: white; padding: 1.1rem 1.25rem 1.25rem;
} }
:host-context(.dark) .section-content { :host-context(.dark) ::ng-deep .graph-settings-accordion .p-accordion-panel {
background-color: #1F2937; border-color: rgba(71, 85, 105, 0.5);
background-color: rgba(30, 41, 59, 0.9);
}
:host-context(.dark) ::ng-deep .graph-settings-accordion .p-accordion-panel-active {
border-color: rgba(96, 165, 250, 0.6);
box-shadow: 0 12px 28px -12px rgba(2, 132, 199, 0.35);
}
:host-context(.dark) ::ng-deep .graph-settings-accordion .p-accordion-header {
color: #f8fafc;
background: linear-gradient(180deg, rgba(30, 41, 59, 0.95) 0%, rgba(30, 41, 59, 0.75) 100%);
}
:host-context(.dark) ::ng-deep .graph-settings-accordion .p-accordion-header:hover {
background: rgba(30, 41, 59, 0.9);
}
:host-context(.dark) ::ng-deep .graph-settings-accordion .p-accordion-toggle-icon {
color: #e2e8f0;
}
:host-context(.dark) ::ng-deep .graph-settings-accordion .p-accordion-content {
background-color: rgba(15, 23, 42, 0.9);
} }
@media (max-width: 768px) { @media (max-width: 768px) {
@ -258,10 +264,6 @@ import { GraphForcesSectionComponent } from './sections/forces-section.component
max-width: 100%; max-width: 100%;
} }
} }
.rotate-180 {
transform: rotate(180deg);
}
`] `]
}) })
export class GraphSettingsPanelComponent { export class GraphSettingsPanelComponent {
@ -272,35 +274,49 @@ export class GraphSettingsPanelComponent {
private settingsService = inject(GraphSettingsService); private settingsService = inject(GraphSettingsService);
config = this.settingsService.config; config = this.settingsService.config;
accordionValue = signal<string[]>([]);
constructor() { constructor() {
// Listen to Escape key globally when panel is open // Listen to Escape key globally when panel is open
effect(() => { effect(() => {
if (this.isOpen()) { if (!this.isOpen()) {
const handler = (e: KeyboardEvent) => { return;
if (e.key === 'Escape') {
this.close.emit();
}
};
document.addEventListener('keydown', handler);
return () => document.removeEventListener('keydown', handler);
} }
return;
const handler = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
this.close.emit();
}
};
document.addEventListener('keydown', handler);
return () => document.removeEventListener('keydown', handler);
}); });
}
onConfigChange(patch: Partial<GraphConfig>): void { // Keep accordion state in sync with persisted collapse flags
this.settingsService.save(patch); effect(() => {
} const currentConfig = this.config();
const active: string[] = [];
toggleSection(section: 'filter' | 'color-groups' | 'display' | 'forces'): void { if (!currentConfig['collapse-filter']) {
this.settingsService.toggleCollapse(section); active.push('filters');
} }
if (!currentConfig['collapse-color-groups']) {
active.push('groups');
}
if (!currentConfig['collapse-display']) {
active.push('display');
}
if (!currentConfig['collapse-forces']) {
active.push('forces');
}
this.accordionValue.set(active);
});
onResetAll(): void {
if (confirm('Reset all graph settings to defaults?')) {
this.settingsService.resetToDefaults();
}
} }
} }

View File

@ -52,7 +52,7 @@ import { SearchOptions } from '../../core/search/search-parser.types';
(keydown.enter)="onEnter()" (keydown.enter)="onEnter()"
(keydown.escape)="onEscape()" (keydown.escape)="onEscape()"
[class]="inputClass" [class]="inputClass"
[style.padding-left]="showSearchIcon ? '2.5rem' : '0.75rem'" [style.padding-left]="showSearchIcon ? '2.75rem' : '1rem'"
[style.padding-right]="'7rem'" [style.padding-right]="'7rem'"
[attr.aria-label]="placeholder" [attr.aria-label]="placeholder"
autocomplete="off" autocomplete="off"
@ -112,7 +112,9 @@ import { SearchOptions } from '../../core/search/search-parser.types';
[context]="context" [context]="context"
[currentQuery]="query" [currentQuery]="query"
[anchorElement]="anchorElement" [anchorElement]="anchorElement"
[showExamples]="showExamples"
(queryChange)="onQueryChange($event)" (queryChange)="onQueryChange($event)"
(previewChange)="onPreviewChange($event)"
(querySubmit)="onQuerySubmit($event)" (querySubmit)="onQuerySubmit($event)"
/> />
</div> </div>
@ -131,8 +133,9 @@ import { SearchOptions } from '../../core/search/search-parser.types';
export class SearchBarComponent implements AfterViewInit, OnInit { export class SearchBarComponent implements AfterViewInit, OnInit {
@Input() placeholder: string = 'Search in vault...'; @Input() placeholder: string = 'Search in vault...';
@Input() context: string = 'vault'; @Input() context: string = 'vault';
@Input() showExamples: boolean = true;
@Input() showSearchIcon: boolean = true; @Input() showSearchIcon: boolean = true;
@Input() inputClass: string = 'w-full px-3 py-2 text-sm border border-border dark:border-gray-600 rounded-lg bg-bg-primary dark:bg-gray-800 text-text-main dark:text-gray-100 placeholder-text-muted dark:placeholder-gray-500 focus:ring-2 focus:ring-accent dark:focus:ring-blue-500 transition-all'; @Input() inputClass: string = 'w-full rounded-full border border-border bg-bg-muted/70 py-2.5 pr-4 text-sm text-text-main placeholder:text-text-muted shadow-subtle focus:outline-none focus:ring-2 focus:ring-ring transition-all dark:border-gray-600 dark:bg-gray-800/80 dark:text-gray-100 dark:placeholder-gray-400 dark:focus:ring-blue-500';
@Input() initialQuery: string = ''; @Input() initialQuery: string = '';
@Output() search = new EventEmitter<{ query: string; options: SearchOptions }>(); @Output() search = new EventEmitter<{ query: string; options: SearchOptions }>();
@ -157,6 +160,16 @@ export class SearchBarComponent implements AfterViewInit, OnInit {
} }
} }
/**
* Handle preview changes from assistant: update visible input only.
*/
onPreviewChange(preview: string): void {
this.query = preview;
if (this.searchInputRef) {
this.searchInputRef.nativeElement.value = preview;
}
}
ngAfterViewInit(): void { ngAfterViewInit(): void {
this.anchorElement = this.hostElement.nativeElement; this.anchorElement = this.hostElement.nativeElement;
} }

View File

@ -29,7 +29,7 @@ import { SearchHistoryService } from '../../core/search/search-history.service';
<div class="relative"> <div class="relative">
<svg <svg
*ngIf="showSearchIcon" *ngIf="showSearchIcon"
class="pointer-events-none absolute left-3 top-1/2 h-5 w-5 -translate-y-1/2 text-text-muted" class="pointer-events-none absolute left-3 top-1/2 h-5 w-5 -translate-y-1/2 text-text-muted dark:text-gray-400"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
fill="none" fill="none"
viewBox="0 0 24 24" viewBox="0 0 24 24"
@ -47,6 +47,7 @@ import { SearchHistoryService } from '../../core/search/search-history.service';
(blur)="onBlur()" (blur)="onBlur()"
(keydown.enter)="onEnter()" (keydown.enter)="onEnter()"
[class]="inputClass" [class]="inputClass"
[style.padding-left]="showSearchIcon ? '2.75rem' : '1rem'"
[attr.aria-label]="placeholder" [attr.aria-label]="placeholder"
/> />
@ -67,7 +68,9 @@ import { SearchHistoryService } from '../../core/search/search-history.service';
[context]="context" [context]="context"
[currentQuery]="value" [currentQuery]="value"
[anchorElement]="anchorElement" [anchorElement]="anchorElement"
[showExamples]="showExamples"
(queryChange)="onQueryChange($event)" (queryChange)="onQueryChange($event)"
(previewChange)="onPreviewChange($event)"
(querySubmit)="onQuerySubmit($event)" (querySubmit)="onQuerySubmit($event)"
/> />
</div> </div>
@ -83,7 +86,8 @@ export class SearchInputWithAssistantComponent implements AfterViewInit {
@Input() value: string = ''; @Input() value: string = '';
@Input() context: string = 'default'; @Input() context: string = 'default';
@Input() showSearchIcon: boolean = true; @Input() showSearchIcon: boolean = true;
@Input() inputClass: string = 'w-full px-3 py-2 text-sm border border-border rounded-md bg-bg-primary text-text-main placeholder-text-muted focus:outline-none focus:ring-2 focus:ring-accent'; @Input() inputClass: string = 'w-full rounded-full border border-border bg-bg-muted/70 py-2.5 pr-4 text-sm text-text-main placeholder:text-text-muted shadow-subtle focus:outline-none focus:ring-2 focus:ring-ring transition-all dark:border-gray-600 dark:bg-gray-800/80 dark:text-gray-100 dark:placeholder-gray-400 dark:focus:ring-blue-500';
@Input() showExamples: boolean = true;
@Output() valueChange = new EventEmitter<string>(); @Output() valueChange = new EventEmitter<string>();
@Output() submit = new EventEmitter<string>(); @Output() submit = new EventEmitter<string>();
@ -170,6 +174,17 @@ export class SearchInputWithAssistantComponent implements AfterViewInit {
} }
} }
/**
* Handle preview change from assistant (keyboard navigation):
* only update the visible input value without emitting or recomputing.
*/
onPreviewChange(query: string): void {
this.value = query;
if (this.searchInputRef) {
this.searchInputRef.nativeElement.value = query;
}
}
/** /**
* Handle query submit from assistant * Handle query submit from assistant
*/ */

View File

@ -33,6 +33,7 @@ import { VaultService } from '../../services/vault.service';
[placeholder]="placeholder" [placeholder]="placeholder"
[context]="context" [context]="context"
[showSearchIcon]="true" [showSearchIcon]="true"
[showExamples]="false"
(search)="onSearch($event)" (search)="onSearch($event)"
(queryChange)="onQueryChange($event)" (queryChange)="onQueryChange($event)"
/> />

View File

@ -82,7 +82,7 @@ type NavigationItem =
{{ option.description }} {{ option.description }}
</div> </div>
<span <span
*ngIf="option.example" *ngIf="showExamples && option.example"
class="text-[11px] text-text-muted dark:text-gray-500 font-mono ml-2 truncate" class="text-[11px] text-text-muted dark:text-gray-500 font-mono ml-2 truncate"
> >
{{ option.example }} {{ option.example }}
@ -180,8 +180,10 @@ export class SearchQueryAssistantComponent {
@Input() context: string = 'default'; // Search context (vault, graph, etc.) @Input() context: string = 'default'; // Search context (vault, graph, etc.)
@Input() currentQuery: string = ''; @Input() currentQuery: string = '';
@Input() anchorElement: HTMLElement | null = null; @Input() anchorElement: HTMLElement | null = null;
@Input() showExamples: boolean = true;
@Output() queryChange = new EventEmitter<string>(); @Output() queryChange = new EventEmitter<string>();
@Output() querySubmit = new EventEmitter<string>(); @Output() querySubmit = new EventEmitter<string>();
@Output() previewChange = new EventEmitter<string>();
@ViewChild('popover') popoverRef?: ElementRef; @ViewChild('popover') popoverRef?: ElementRef;
@ -482,7 +484,8 @@ export class SearchQueryAssistantComponent {
} }
this.currentQuery = previewQuery ?? this.currentQuery; this.currentQuery = previewQuery ?? this.currentQuery;
this.queryChange.emit(this.currentQuery); // Emit preview change instead of confirmed change to avoid recomputing options in parent
this.previewChange.emit(this.currentQuery);
} }
isSelected(type: NavigationItem['type'], index: number): boolean { isSelected(type: NavigationItem['type'], index: number): boolean {

View File

@ -1,7 +1,7 @@
{ {
"collapse-filter": false, "collapse-filter": false,
"search": "", "search": "file:",
"showTags": true, "showTags": false,
"showAttachments": false, "showAttachments": false,
"hideUnresolved": false, "hideUnresolved": false,
"showOrphans": false, "showOrphans": false,