import { Component, EventEmitter, Input, Output, ViewChild, inject, OnChanges, SimpleChanges } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { FileExplorerComponent } from '../../../components/file-explorer/file-explorer.component';
import { QuickLinksComponent } from '../quick-links/quick-links.component';
import { ScrollableOverlayDirective } from '../../shared/overlay-scrollbar/scrollable-overlay.directive';
import type { VaultNode, TagInfo } from '../../../types';
import { environment } from '../../../environments/environment';
import { VaultService } from '../../../services/vault.service';
import { UrlStateService } from '../../services/url-state.service';
import { SidebarStateService } from '../../services/sidebar-state.service';
import { FilterService } from '../../services/filter.service';
@Component({
selector: 'app-nimbus-sidebar',
standalone: true,
imports: [CommonModule, RouterModule, FileExplorerComponent, QuickLinksComponent, ScrollableOverlayDirective],
host: { class: 'block h-full' },
template: `
-
{{ t.count }}
ObsiViewer
`
})
export class NimbusSidebarComponent implements OnChanges {
@Input() vaultName = '';
@Input() effectiveFileTree: VaultNode[] = [];
@Input() selectedNoteId: string | null = null;
@Input() tags: TagInfo[] = [];
@Input() quickLinkFilter: 'favoris' | 'publish' | 'draft' | 'template' | 'task' | 'private' | 'archive' | null = null;
@Input() forceOpenSection: 'folders' | 'tags' | 'quick' | null = null;
@Output() toggleSidebarRequest = new EventEmitter();
@Output() folderSelected = new EventEmitter();
@Output() fileSelected = new EventEmitter();
@Output() tagSelected = new EventEmitter();
@Output() quickLinkSelected = new EventEmitter();
@Output() markdownPlaygroundSelected = new EventEmitter();
@Output() testsPanelSelected = new EventEmitter();
@Output() testsExcalidrawSelected = new EventEmitter();
@Output() helpPageSelected = new EventEmitter();
@Output() aboutSelected = new EventEmitter();
env = environment;
open = { quick: true, folders: false, tags: false, trash: false, tests: false };
private vault = inject(VaultService);
urlState = inject(UrlStateService);
private sidebar = inject(SidebarStateService);
filters = inject(FilterService);
@ViewChild('foldersExplorer') private foldersExplorer?: FileExplorerComponent;
ngOnChanges(changes: SimpleChanges): void {
if (changes['forceOpenSection']) {
const which = this.forceOpenSection;
if (which === 'folders') {
this.open = { quick: false, folders: true, tags: false, trash: false, tests: false };
} else if (which === 'tags') {
this.open = { quick: false, folders: false, tags: true, trash: false, tests: false };
} else if (which === 'quick') {
this.open = { quick: true, folders: false, tags: false, trash: false, tests: false };
}
}
}
onQuickLink(id: string) { this.quickLinkSelected.emit(id); }
onHomeClick(event: MouseEvent): void {
event.preventDefault();
this.open = { quick: true, folders: false, tags: false, trash: false, tests: false };
this.sidebar.open('quick');
void this.urlState.setQuickWithMarkdown('all');
}
onQuickLinksHeaderClick(event: MouseEvent): void {
event.preventDefault();
this.open = { quick: true, folders: false, tags: false, trash: false, tests: false };
this.sidebar.open('quick');
void this.urlState.setQuickWithMarkdown('all');
}
onMarkdownPlaygroundClick(): void {
this.markdownPlaygroundSelected.emit();
}
onApiTestsPanelClick(): void {
this.testsPanelSelected.emit();
}
trashNotes = () => this.vault.trashNotes();
trashCount = () => this.vault.counts().trash;
trashHasContent = () => (this.vault.trashTree() || []).length > 0;
trackNoteId = (_: number, n: { id: string }) => n.id;
toggleSection(which: 'quick' | 'folders' | 'tags'): void {
// Open requested section, close others, and reset filters/search via SidebarStateService
this.open = { quick: false, folders: false, tags: false, trash: false, tests: false };
(this.open as any)[which] = true;
this.sidebar.open(which);
}
onCreateFolderAtRoot(): void {
// If not yet rendered, open the section first, then defer action
if (!this.open.folders) {
this.open.folders = true;
setTimeout(() => this.foldersExplorer?.openCreateAtRoot(), 0);
} else {
this.foldersExplorer?.openCreateAtRoot();
}
}
toggleTrashSection(): void {
const next = !this.open.trash;
this.open.trash = next;
if (next) {
this.folderSelected.emit('.trash');
}
}
setKind(kind: 'image'|'video'|'pdf'|'markdown'|'excalidraw'|'code'|'all') {
this.filters.toggleKind(kind === 'all' ? 'all' : kind as any);
}
chipClass(active: boolean): string {
return active
? 'bg-primary/15 text-primary ring-1 ring-primary/40'
: 'bg-surface1/50 text-muted hover:bg-surface1 dark:hover:bg-card';
}
}