chore: update Angular TypeScript build info cache
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
import { Injectable, signal } from '@angular/core';
|
||||
|
||||
/**
|
||||
* Index of vault content for graph view
|
||||
* Maintains lists of files, tags, paths, attachments for fast filtering
|
||||
*/
|
||||
export interface GraphIndexData {
|
||||
/** All files (markdown notes) */
|
||||
files: FileIndexEntry[];
|
||||
/** All unique tags found in the vault */
|
||||
tags: string[];
|
||||
/** All unique paths (folders) */
|
||||
paths: string[];
|
||||
/** All attachments (non-markdown files) */
|
||||
attachments: AttachmentIndexEntry[];
|
||||
/** Map of file ID to tags */
|
||||
fileToTags: Map<string, string[]>;
|
||||
/** Map of file ID to path */
|
||||
fileToPath: Map<string, string>;
|
||||
}
|
||||
|
||||
export interface FileIndexEntry {
|
||||
id: string;
|
||||
name: string;
|
||||
path: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
export interface AttachmentIndexEntry {
|
||||
id: string;
|
||||
name: string;
|
||||
path: string;
|
||||
extension: string;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class GraphIndexService {
|
||||
private indexData = signal<GraphIndexData>({
|
||||
files: [],
|
||||
tags: [],
|
||||
paths: [],
|
||||
attachments: [],
|
||||
fileToTags: new Map(),
|
||||
fileToPath: new Map()
|
||||
});
|
||||
|
||||
/**
|
||||
* Current index data as signal
|
||||
*/
|
||||
get index() {
|
||||
return this.indexData.asReadonly();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild index from vault data
|
||||
*/
|
||||
rebuildIndex(notes: any[]): void {
|
||||
const files: FileIndexEntry[] = [];
|
||||
const tagsSet = new Set<string>();
|
||||
const pathsSet = new Set<string>();
|
||||
const attachments: AttachmentIndexEntry[] = [];
|
||||
const fileToTags = new Map<string, string[]>();
|
||||
const fileToPath = new Map<string, string>();
|
||||
|
||||
for (const note of notes) {
|
||||
// Extract path components
|
||||
const filePath = note.filePath || note.path || '';
|
||||
const pathParts = filePath.split('/');
|
||||
const fileName = pathParts[pathParts.length - 1] || '';
|
||||
const folderPath = pathParts.slice(0, -1).join('/');
|
||||
|
||||
// Add to paths
|
||||
if (folderPath) {
|
||||
pathsSet.add(folderPath);
|
||||
// Also add parent paths
|
||||
const parentParts = folderPath.split('/');
|
||||
for (let i = 1; i <= parentParts.length; i++) {
|
||||
pathsSet.add(parentParts.slice(0, i).join('/'));
|
||||
}
|
||||
}
|
||||
|
||||
// Check if it's an attachment (non-markdown file)
|
||||
const isAttachment = this.isAttachmentFile(fileName);
|
||||
|
||||
if (isAttachment) {
|
||||
const extension = this.getFileExtension(fileName);
|
||||
attachments.push({
|
||||
id: note.id,
|
||||
name: fileName,
|
||||
path: filePath,
|
||||
extension
|
||||
});
|
||||
} else {
|
||||
// Regular markdown file
|
||||
const tags = note.tags || [];
|
||||
|
||||
// Add tags to set (normalize to include #)
|
||||
tags.forEach((tag: string) => {
|
||||
const normalized = tag.startsWith('#') ? tag : `#${tag}`;
|
||||
tagsSet.add(normalized);
|
||||
});
|
||||
|
||||
files.push({
|
||||
id: note.id,
|
||||
name: fileName,
|
||||
path: filePath,
|
||||
tags
|
||||
});
|
||||
|
||||
fileToTags.set(note.id, tags);
|
||||
}
|
||||
|
||||
fileToPath.set(note.id, filePath);
|
||||
}
|
||||
|
||||
// Update index
|
||||
this.indexData.set({
|
||||
files,
|
||||
tags: Array.from(tagsSet).sort(),
|
||||
paths: Array.from(pathsSet).sort(),
|
||||
attachments,
|
||||
fileToTags,
|
||||
fileToPath
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get suggestions based on query type
|
||||
*/
|
||||
getSuggestions(type: 'path' | 'tag' | 'file' | 'property', filter?: string): string[] {
|
||||
const index = this.indexData();
|
||||
const filterLower = filter?.toLowerCase() || '';
|
||||
|
||||
switch (type) {
|
||||
case 'path':
|
||||
return index.paths.filter(p => !filter || p.toLowerCase().includes(filterLower));
|
||||
|
||||
case 'tag':
|
||||
return index.tags.filter(t => !filter || t.toLowerCase().includes(filterLower));
|
||||
|
||||
case 'file':
|
||||
return index.files
|
||||
.filter(f => !filter || f.name.toLowerCase().includes(filterLower))
|
||||
.map(f => f.name)
|
||||
.slice(0, 50); // Limit to 50 suggestions
|
||||
|
||||
case 'property':
|
||||
// Common Obsidian properties
|
||||
const commonProps = ['title', 'tags', 'aliases', 'description', 'author', 'date', 'created', 'modified'];
|
||||
return commonProps.filter(p => !filter || p.toLowerCase().includes(filterLower));
|
||||
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if file is an attachment (non-markdown)
|
||||
*/
|
||||
private isAttachmentFile(fileName: string): boolean {
|
||||
const attachmentExtensions = [
|
||||
'pdf', 'png', 'jpg', 'jpeg', 'gif', 'svg', 'webp', 'bmp',
|
||||
'mp4', 'webm', 'ogv', 'mov', 'mkv',
|
||||
'mp3', 'wav', 'ogg', 'm4a', 'flac',
|
||||
'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx',
|
||||
'zip', 'rar', '7z', 'tar', 'gz'
|
||||
];
|
||||
|
||||
const ext = this.getFileExtension(fileName);
|
||||
return attachmentExtensions.includes(ext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file extension (lowercase, no dot)
|
||||
*/
|
||||
private getFileExtension(fileName: string): string {
|
||||
const lastDot = fileName.lastIndexOf('.');
|
||||
if (lastDot === -1) return '';
|
||||
return fileName.substring(lastDot + 1).toLowerCase();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user