feat: add app icons and update UI with logo branding
This commit is contained in:
parent
397f1b4b80
commit
58bd57543b
@ -29,7 +29,11 @@
|
|||||||
"src/styles.css"
|
"src/styles.css"
|
||||||
],
|
],
|
||||||
"assets": [
|
"assets": [
|
||||||
"src/assets"
|
{
|
||||||
|
"glob": "**/*",
|
||||||
|
"input": "src/assets",
|
||||||
|
"output": "assets"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
|
|||||||
@ -4,6 +4,11 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>ObsiViewer - Obsidian Vault Viewer</title>
|
<title>ObsiViewer - Obsidian Vault Viewer</title>
|
||||||
|
<base href="/" />
|
||||||
|
<link rel="icon" type="image/x-icon" href="/assets/favicon.ico">
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/assets/favicon.svg">
|
||||||
|
<link rel="apple-touch-icon" href="/assets/apple-touch-icon.png">
|
||||||
|
<link rel="manifest" href="/assets/site.webmanifest">
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -19,8 +19,12 @@ archive: false
|
|||||||
draft: false
|
draft: false
|
||||||
private: false
|
private: false
|
||||||
---
|
---
|
||||||
|

|
||||||
|
|
||||||
# 🧭 Guide d'utilisateur ObsiViewer
|
# 🧭 Guide d'utilisateur ObsiViewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Bienvenue dans **ObsiViewer** !
|
Bienvenue dans **ObsiViewer** !
|
||||||
|
|
||||||
Ce guide vous aidera à comprendre les principales fonctionnalités :
|
Ce guide vous aidera à comprendre les principales fonctionnalités :
|
||||||
|
|||||||
@ -56,8 +56,8 @@ import { trigger, transition, style, animate } from '@angular/animations';
|
|||||||
<div class="text-center space-y-6">
|
<div class="text-center space-y-6">
|
||||||
<!-- Logo/Icon -->
|
<!-- Logo/Icon -->
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<div class="w-20 h-20 rounded-2xl bg-gradient-to-br from-nimbus-400 to-nimbus-600 flex items-center justify-center text-4xl shadow-lg">
|
<div class="w-40 h-40 rounded-2xl bg-gradient-to-br from-nimbus-400 to-nimbus-600 flex items-center justify-center shadow-lg overflow-hidden">
|
||||||
📖
|
<img src="assets/favicon.svg" alt="ObsiViewer" class="w-40 h-40" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,10 @@ import { TrashExplorerComponent } from '../../layout/sidebar/trash/trash-explore
|
|||||||
[class.translate-x-0]="mobileNav.sidebarOpen()">
|
[class.translate-x-0]="mobileNav.sidebarOpen()">
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="flex items-center justify-between p-4 border-b border-border dark:border-gray-800 bg-gradient-to-r from-nimbus-50 to-transparent dark:from-nimbus-900/20">
|
<div class="flex items-center justify-between p-4 border-b border-border dark:border-gray-800 bg-gradient-to-r from-nimbus-50 to-transparent dark:from-nimbus-900/20">
|
||||||
|
<div class="flex items-center gap-2 min-w-0">
|
||||||
|
<img src="assets/favicon.svg" alt="ObsiViewer" class="h-6 w-6 flex-shrink-0" />
|
||||||
<h2 class="text-lg font-semibold truncate">{{ vaultName || 'ObsiViewer' }}</h2>
|
<h2 class="text-lg font-semibold truncate">{{ vaultName || 'ObsiViewer' }}</h2>
|
||||||
|
</div>
|
||||||
<button
|
<button
|
||||||
(click)="mobileNav.toggleSidebar()"
|
(click)="mobileNav.toggleSidebar()"
|
||||||
class="p-2 rounded-full hover:bg-surface1 dark:hover:bg-card transition-all active:scale-95 transform flex-shrink-0">
|
class="p-2 rounded-full hover:bg-surface1 dark:hover:bg-card transition-all active:scale-95 transform flex-shrink-0">
|
||||||
|
|||||||
@ -17,7 +17,10 @@ import { VaultService } from '../../../services/vault.service';
|
|||||||
<div class="h-full flex flex-col overflow-hidden select-none">
|
<div class="h-full flex flex-col overflow-hidden select-none">
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="h-12 flex items-center justify-between px-3 border-b border-border dark:border-gray-800">
|
<div class="h-12 flex items-center justify-between px-3 border-b border-border dark:border-gray-800">
|
||||||
|
<div class="flex items-center gap-2 min-w-0">
|
||||||
|
<img src="assets/favicon.svg" alt="ObsiViewer" class="h-6 w-6 flex-shrink-0" />
|
||||||
<div class="text-sm font-semibold truncate">{{ vaultName }} - ObsiViewer</div>
|
<div class="text-sm font-semibold truncate">{{ vaultName }} - ObsiViewer</div>
|
||||||
|
</div>
|
||||||
<button (click)="toggleSidebarRequest.emit()" class="p-2 rounded hover:bg-surface1 dark:hover:bg-card" title="Hide Sidebar">⟨⟨</button>
|
<button (click)="toggleSidebarRequest.emit()" class="p-2 rounded hover:bg-surface1 dark:hover:bg-card" title="Hide Sidebar">⟨⟨</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Component, EventEmitter, HostListener, Input, Output, inject } from '@angular/core';
|
import { Component, EventEmitter, HostListener, Input, Output, inject, effect } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { UiModeService } from '../../shared/services/ui-mode.service';
|
import { UiModeService } from '../../shared/services/ui-mode.service';
|
||||||
import { ResponsiveService } from '../../shared/services/responsive.service';
|
import { ResponsiveService } from '../../shared/services/responsive.service';
|
||||||
@ -70,17 +70,30 @@ import { AboutPanelComponent } from '../../features/about/about-panel.component'
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-template #collapsedRail>
|
<ng-template #collapsedRail>
|
||||||
<aside class="border-r border-border dark:border-gray-800 h-full w-14 flex flex-col items-center py-3 gap-3">
|
<aside class="border-r border-border dark:border-gray-800 h-full w-14 flex flex-col items-center py-3 gap-3">
|
||||||
|
<img src="assets/favicon.svg" alt="ObsiViewer" class="h-6 w-6 mb-2" />
|
||||||
<button class="p-2 rounded hover:bg-surface1 dark:hover:bg-card" (click)="toggleSidebarRequest.emit()" title="Show Sidebar">☰</button>
|
<button class="p-2 rounded hover:bg-surface1 dark:hover:bg-card" (click)="toggleSidebarRequest.emit()" title="Show Sidebar">☰</button>
|
||||||
<button class="p-2 rounded hover:bg-surface1 dark:hover:bg-card" (mouseenter)="openFlyout('quick')" (mouseleave)="scheduleCloseFlyout()" title="Quick Links">▦</button>
|
<button class="p-2 rounded hover:bg-surface1 dark:hover:bg-card" (mouseenter)="openFlyout('quick')" (mouseleave)="scheduleCloseFlyout()" title="Quick Links">▦</button>
|
||||||
<button class="p-2 rounded hover:bg-surface1 dark:hover:bg-card" (mouseenter)="openFlyout('folders')" (mouseleave)="scheduleCloseFlyout()" title="Folders">📁</button>
|
<button class="p-2 rounded hover:bg-surface1 dark:hover:bg-card" (mouseenter)="openFlyout('folders')" (mouseleave)="scheduleCloseFlyout()" title="Folders">📁</button>
|
||||||
<button class="p-2 rounded hover:bg-surface1 dark:hover:bg-card" (mouseenter)="openFlyout('tags')" (mouseleave)="scheduleCloseFlyout()" title="Tags">🏷️</button>
|
<button class="p-2 rounded hover:bg-surface1 dark:hover:bg-card" (mouseenter)="openFlyout('tags')" (mouseleave)="scheduleCloseFlyout()" title="Tags">🏷️</button>
|
||||||
<button class="p-2 rounded hover:bg-surface1 dark:hover:bg-card" (mouseenter)="openFlyout('trash')" (mouseleave)="scheduleCloseFlyout()" title="Trash">🗑️</button>
|
<button class="p-2 rounded hover:bg-surface1 dark:hover:bg-card" (mouseenter)="openFlyout('trash')" (mouseleave)="scheduleCloseFlyout()" title="Trash">🗑️</button>
|
||||||
|
<div class="h-px w-8 bg-border dark:bg-gray-800 my-1"></div>
|
||||||
|
<button class="p-2 rounded hover:bg-surface1 dark:hover:bg-card" (mouseenter)="openFlyout('playground')" (mouseleave)="scheduleCloseFlyout()" title="Markdown Playground">🧪</button>
|
||||||
|
<button class="p-2 rounded hover:bg-surface1 dark:hover:bg-card" (mouseenter)="openFlyout('help')" (mouseleave)="scheduleCloseFlyout()" title="Help">🆘</button>
|
||||||
|
<button class="p-2 rounded hover:bg-surface1 dark:hover:bg-card" (mouseenter)="openFlyout('about')" (mouseleave)="scheduleCloseFlyout()" title="About">ℹ️</button>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<!-- Flyouts -->
|
<!-- Flyouts -->
|
||||||
<div class="absolute left-14 top-0 bottom-0 w-80 max-w-[70vw] bg-card dark:bg-main border-r border-border dark:border-gray-800 shadow-xl z-50" *ngIf="hoveredFlyout as f" (mouseenter)="cancelCloseFlyout()" (mouseleave)="scheduleCloseFlyout()">
|
<div class="absolute left-14 top-0 bottom-0 w-80 max-w-[70vw] bg-card dark:bg-main border-r border-border dark:border-gray-800 shadow-xl z-50" *ngIf="hoveredFlyout as f" (mouseenter)="cancelCloseFlyout()" (mouseleave)="scheduleCloseFlyout()">
|
||||||
<div class="h-12 flex items-center justify-between px-3 border-b border-border dark:border-gray-800">
|
<div class="h-12 flex items-center justify-between px-3 border-b border-border dark:border-gray-800">
|
||||||
<div class="text-sm font-semibold">{{ f === 'quick' ? 'Quick Links' : (f === 'folders' ? 'Folders' : (f === 'tags' ? 'Tags' : 'Trash')) }}</div>
|
<div class="text-sm font-semibold">{{
|
||||||
|
f === 'quick' ? 'Quick Links' :
|
||||||
|
(f === 'folders' ? 'Folders' :
|
||||||
|
(f === 'tags' ? 'Tags' :
|
||||||
|
(f === 'trash' ? 'Trash' :
|
||||||
|
(f === 'help' ? 'Help' :
|
||||||
|
(f === 'about' ? 'About' :
|
||||||
|
(f === 'playground' ? 'Playground' : ''))))))
|
||||||
|
}}</div>
|
||||||
<button class="p-2 rounded hover:bg-surface1 dark:hover:bg-card" (click)="hoveredFlyout=null">✕</button>
|
<button class="p-2 rounded hover:bg-surface1 dark:hover:bg-card" (click)="hoveredFlyout=null">✕</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="h-[calc(100%-3rem)] overflow-y-auto" appScrollableOverlay>
|
<div class="h-[calc(100%-3rem)] overflow-y-auto" appScrollableOverlay>
|
||||||
@ -96,6 +109,30 @@ import { AboutPanelComponent } from '../../features/about/about-panel.component'
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
<div *ngSwitchCase="'trash'" class="p-2">
|
||||||
|
<ng-container *ngIf="(vault.trashTree() || []).length > 0; else emptyTrash">
|
||||||
|
<app-file-explorer
|
||||||
|
[nodes]="vault.trashTree()"
|
||||||
|
[selectedNoteId]="selectedNoteId"
|
||||||
|
[foldersOnly]="true"
|
||||||
|
[useTrashCounts]="true"
|
||||||
|
(folderSelected)="onFolderSelected($event)"
|
||||||
|
(fileSelected)="noteSelected.emit($event)">
|
||||||
|
</app-file-explorer>
|
||||||
|
</ng-container>
|
||||||
|
<ng-template #emptyTrash>
|
||||||
|
<div class="px-3 py-2 text-muted text-sm">La corbeille est vide</div>
|
||||||
|
</ng-template>
|
||||||
|
</div>
|
||||||
|
<div *ngSwitchCase="'help'" class="p-3">
|
||||||
|
<button type="button" class="w-full text-left flex items-center gap-2 px-3 py-2 text-sm rounded-lg hover:bg-surface1 dark:hover:bg-card" (click)="onHelpPageSelected()">🆘 <span>Help Page</span></button>
|
||||||
|
</div>
|
||||||
|
<div *ngSwitchCase="'about'" class="p-3">
|
||||||
|
<button type="button" class="w-full text-left flex items-center gap-2 px-3 py-2 text-sm rounded-lg hover:bg-surface1 dark:hover:bg-card" (click)="onAboutSelected()">ℹ️ <span>About</span></button>
|
||||||
|
</div>
|
||||||
|
<div *ngSwitchCase="'playground'" class="p-3">
|
||||||
|
<button type="button" class="w-full text-left flex items-center gap-2 px-3 py-2 text-sm rounded-lg hover:bg-surface1 dark:hover:bg-card" (click)="onMarkdownPlaygroundSelected()">🧪 <span>Markdown Playground</span></button>
|
||||||
|
</div>
|
||||||
<div *ngSwitchDefault class="p-3 text-sm text-muted">Empty</div>
|
<div *ngSwitchDefault class="p-3 text-sm text-muted">Empty</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
@ -115,7 +152,7 @@ import { AboutPanelComponent } from '../../features/about/about-panel.component'
|
|||||||
[quickLinkFilter]="quickLinkFilter"
|
[quickLinkFilter]="quickLinkFilter"
|
||||||
[query]="listQuery"
|
[query]="listQuery"
|
||||||
(openNote)="onOpenNote($event)"
|
(openNote)="onOpenNote($event)"
|
||||||
(queryChange)="listQuery = $event"
|
(queryChange)="onQueryChange($event)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@ -190,7 +227,7 @@ import { AboutPanelComponent } from '../../features/about/about-panel.component'
|
|||||||
<app-file-explorer [nodes]="effectiveFileTree" [selectedNoteId]="selectedNoteId" [foldersOnly]="true" (folderSelected)="onFolderSelected($event)" (fileSelected)="onOpenNote($event)"></app-file-explorer>
|
<app-file-explorer [nodes]="effectiveFileTree" [selectedNoteId]="selectedNoteId" [foldersOnly]="true" (folderSelected)="onFolderSelected($event)" (fileSelected)="onOpenNote($event)"></app-file-explorer>
|
||||||
</div>
|
</div>
|
||||||
<div [hidden]="mobileNav.activeTab() !== 'list'" class="h-full overflow-y-auto" appScrollableOverlay>
|
<div [hidden]="mobileNav.activeTab() !== 'list'" class="h-full overflow-y-auto" appScrollableOverlay>
|
||||||
<app-notes-list [notes]="vault.allNotes()" [folderFilter]="folderFilter" [tagFilter]="tagFilter" [quickLinkFilter]="quickLinkFilter" [query]="listQuery" (queryChange)="listQuery=$event" (openNote)="onOpenNote($event)"></app-notes-list>
|
<app-notes-list [notes]="vault.allNotes()" [folderFilter]="folderFilter" [tagFilter]="tagFilter" [quickLinkFilter]="quickLinkFilter" [query]="listQuery" (queryChange)="onQueryChange($event)" (openNote)="onOpenNote($event)"></app-notes-list>
|
||||||
</div>
|
</div>
|
||||||
<div [hidden]="mobileNav.activeTab() !== 'page'" class="note-content-area h-full overflow-y-auto px-3 py-4" appScrollableOverlay>
|
<div [hidden]="mobileNav.activeTab() !== 'page'" class="note-content-area h-full overflow-y-auto px-3 py-4" appScrollableOverlay>
|
||||||
<app-markdown-playground *ngIf="activeView === 'markdown-playground'"></app-markdown-playground>
|
<app-markdown-playground *ngIf="activeView === 'markdown-playground'"></app-markdown-playground>
|
||||||
@ -218,7 +255,7 @@ import { AboutPanelComponent } from '../../features/about/about-panel.component'
|
|||||||
|
|
||||||
@if (mobileNav.activeTab() === 'list') {
|
@if (mobileNav.activeTab() === 'list') {
|
||||||
<div class="h-full flex flex-col overflow-hidden animate-fadeIn">
|
<div class="h-full flex flex-col overflow-hidden animate-fadeIn">
|
||||||
<app-notes-list class="flex-1" [notes]="vault.allNotes()" [folderFilter]="folderFilter" [tagFilter]="tagFilter" [quickLinkFilter]="quickLinkFilter" [query]="listQuery" (queryChange)="listQuery=$event" (openNote)="onNoteSelectedMobile($event)"></app-notes-list>
|
<app-notes-list class="flex-1" [notes]="vault.allNotes()" [folderFilter]="folderFilter" [tagFilter]="tagFilter" [quickLinkFilter]="quickLinkFilter" [query]="listQuery" (queryChange)="onQueryChange($event)" (openNote)="onNoteSelectedMobile($event)"></app-notes-list>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,23 +333,78 @@ export class AppShellNimbusLayoutComponent {
|
|||||||
|
|
||||||
folderFilter: string | null = null;
|
folderFilter: string | null = null;
|
||||||
listQuery: string = '';
|
listQuery: string = '';
|
||||||
hoveredFlyout: 'quick' | 'folders' | 'tags' | 'trash' | null = null;
|
hoveredFlyout: 'quick' | 'folders' | 'tags' | 'trash' | 'help' | 'about' | 'playground' | null = null;
|
||||||
private flyoutCloseTimer: any = null;
|
private flyoutCloseTimer: any = null;
|
||||||
tagFilter: string | null = null;
|
tagFilter: string | null = null;
|
||||||
quickLinkFilter: 'favoris' | 'publish' | 'draft' | 'template' | 'task' | 'private' | 'archive' | null = null;
|
quickLinkFilter: 'favoris' | 'publish' | 'draft' | 'template' | 'task' | 'private' | 'archive' | null = null;
|
||||||
|
|
||||||
|
// Auto-select first note when filters change
|
||||||
|
private autoSelectFirstNote() {
|
||||||
|
const filteredNotes = this.getFilteredNotes();
|
||||||
|
if (filteredNotes.length > 0 && filteredNotes[0].id !== this.selectedNoteId) {
|
||||||
|
this.noteSelected.emit(filteredNotes[0].id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getFilteredNotes(): Note[] {
|
||||||
|
const q = (this.listQuery || '').toLowerCase().trim();
|
||||||
|
const folder = (this.folderFilter || '').toLowerCase().replace(/^\/+|\/+$/g, '');
|
||||||
|
const tag = (this.tagFilter || '').toLowerCase();
|
||||||
|
const quickLink = this.quickLinkFilter;
|
||||||
|
let list = this.vault.allNotes();
|
||||||
|
|
||||||
|
if (folder) {
|
||||||
|
if (folder === '.trash') {
|
||||||
|
// All files anywhere under .trash (including subfolders)
|
||||||
|
list = list.filter(n => {
|
||||||
|
const filePath = (n.filePath || n.originalPath || '').toLowerCase().replace(/\\/g, '/');
|
||||||
|
return filePath.startsWith('.trash/') || filePath.includes('/.trash/');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
list = list.filter(n => {
|
||||||
|
const originalPath = (n.originalPath || '').toLowerCase().replace(/^\/+|\/+$/g, '');
|
||||||
|
return originalPath === folder || originalPath.startsWith(folder + '/');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tag) {
|
||||||
|
list = list.filter(n => Array.isArray(n.tags) && n.tags.some(t => (t || '').toLowerCase() === tag));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply Quick Link filter
|
||||||
|
if (quickLink) {
|
||||||
|
list = list.filter(n => {
|
||||||
|
const frontmatter = n.frontmatter || {};
|
||||||
|
return frontmatter[quickLink] === true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply query if present
|
||||||
|
if (q) {
|
||||||
|
list = list.filter(n => {
|
||||||
|
const title = (n.title || '').toLowerCase();
|
||||||
|
const filePath = (n.filePath || '').toLowerCase();
|
||||||
|
return title.includes(q) || filePath.includes(q);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by most recent first (same as notes-list component)
|
||||||
|
const parseDate = (s?: string) => (s ? Date.parse(s) : 0) || 0;
|
||||||
|
const score = (n: Note) => n.mtime || parseDate(n.updatedAt) || parseDate(n.createdAt) || 0;
|
||||||
|
return [...list].sort((a, b) => (score(b) - score(a)));
|
||||||
|
}
|
||||||
|
|
||||||
|
onQueryChange(query: string) {
|
||||||
|
this.listQuery = query;
|
||||||
|
this.autoSelectFirstNote();
|
||||||
|
}
|
||||||
|
|
||||||
toggleNoteFullScreen(): void {
|
toggleNoteFullScreen(): void {
|
||||||
this.noteFullScreen = !this.noteFullScreen;
|
this.noteFullScreen = !this.noteFullScreen;
|
||||||
document.body.classList.toggle('note-fullscreen-active', this.noteFullScreen);
|
document.body.classList.toggle('note-fullscreen-active', this.noteFullScreen);
|
||||||
}
|
}
|
||||||
|
|
||||||
@HostListener('document:keydown.escape')
|
|
||||||
handleEscape(): void {
|
|
||||||
if (!this.noteFullScreen) return;
|
|
||||||
this.noteFullScreen = false;
|
|
||||||
document.body.classList.remove('note-fullscreen-active');
|
|
||||||
}
|
|
||||||
|
|
||||||
nextTab() {
|
nextTab() {
|
||||||
const order: Array<'sidebar' | 'list' | 'page' | 'toc'> = ['sidebar', 'list', 'page', 'toc'];
|
const order: Array<'sidebar' | 'list' | 'page' | 'toc'> = ['sidebar', 'list', 'page', 'toc'];
|
||||||
const idx = order.indexOf(this.mobileNav.activeTab());
|
const idx = order.indexOf(this.mobileNav.activeTab());
|
||||||
@ -340,6 +432,7 @@ export class AppShellNimbusLayoutComponent {
|
|||||||
onFolderSelected(path: string) {
|
onFolderSelected(path: string) {
|
||||||
this.folderFilter = path || null;
|
this.folderFilter = path || null;
|
||||||
this.tagFilter = null; // clear tag when focusing folder
|
this.tagFilter = null; // clear tag when focusing folder
|
||||||
|
this.autoSelectFirstNote();
|
||||||
if (this.responsive.isMobile() || this.responsive.isTablet()) {
|
if (this.responsive.isMobile() || this.responsive.isTablet()) {
|
||||||
this.mobileNav.setActiveTab('list');
|
this.mobileNav.setActiveTab('list');
|
||||||
}
|
}
|
||||||
@ -347,6 +440,8 @@ export class AppShellNimbusLayoutComponent {
|
|||||||
|
|
||||||
onFolderSelectedFromDrawer(path: string) {
|
onFolderSelectedFromDrawer(path: string) {
|
||||||
this.folderFilter = path || null;
|
this.folderFilter = path || null;
|
||||||
|
this.tagFilter = null; // clear tag when focusing folder
|
||||||
|
this.autoSelectFirstNote();
|
||||||
this.mobileNav.setActiveTab('list');
|
this.mobileNav.setActiveTab('list');
|
||||||
this.mobileNav.sidebarOpen.set(false);
|
this.mobileNav.sidebarOpen.set(false);
|
||||||
}
|
}
|
||||||
@ -433,6 +528,8 @@ export class AppShellNimbusLayoutComponent {
|
|||||||
}
|
}
|
||||||
this.scheduleCloseFlyout(150);
|
this.scheduleCloseFlyout(150);
|
||||||
}
|
}
|
||||||
|
// Auto-select first note after filter changes
|
||||||
|
this.autoSelectFirstNote();
|
||||||
}
|
}
|
||||||
|
|
||||||
onTagSelected(tagName: string) {
|
onTagSelected(tagName: string) {
|
||||||
@ -443,6 +540,8 @@ export class AppShellNimbusLayoutComponent {
|
|||||||
// Clear other filters and search to focus on tag results
|
// Clear other filters and search to focus on tag results
|
||||||
this.quickLinkFilter = null;
|
this.quickLinkFilter = null;
|
||||||
this.listQuery = '';
|
this.listQuery = '';
|
||||||
|
// Auto-select first note after filter changes
|
||||||
|
this.autoSelectFirstNote();
|
||||||
// Ensure the list is visible: exit fullscreen if active
|
// Ensure the list is visible: exit fullscreen if active
|
||||||
if (this.noteFullScreen) {
|
if (this.noteFullScreen) {
|
||||||
this.noteFullScreen = false;
|
this.noteFullScreen = false;
|
||||||
|
|||||||
BIN
src/assets/apple-touch-icon.png
Normal file
BIN
src/assets/apple-touch-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
BIN
src/assets/favicon-96x96.png
Normal file
BIN
src/assets/favicon-96x96.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
BIN
src/assets/favicon.ico
Normal file
BIN
src/assets/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
3
src/assets/favicon.svg
Normal file
3
src/assets/favicon.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 316 KiB |
21
src/assets/site.webmanifest
Normal file
21
src/assets/site.webmanifest
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "MyWebSite",
|
||||||
|
"short_name": "MySite",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/web-app-manifest-192x192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/web-app-manifest-512x512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"theme_color": "#ffffff",
|
||||||
|
"background_color": "#ffffff",
|
||||||
|
"display": "standalone"
|
||||||
|
}
|
||||||
BIN
src/assets/web-app-manifest-192x192.png
Normal file
BIN
src/assets/web-app-manifest-192x192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
BIN
src/assets/web-app-manifest-512x512.png
Normal file
BIN
src/assets/web-app-manifest-512x512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 191 KiB |
@ -340,6 +340,7 @@ export class VaultService implements OnDestroy {
|
|||||||
if (!item?.path) continue;
|
if (!item?.path) continue;
|
||||||
|
|
||||||
const path = this.normalizePath(item.path);
|
const path = this.normalizePath(item.path);
|
||||||
|
if (this.isBuiltinPath(path)) continue;
|
||||||
this.indexMetadata(item, path);
|
this.indexMetadata(item, path);
|
||||||
|
|
||||||
const parentFolder = this.buildFolderStructure(path, folders, root);
|
const parentFolder = this.buildFolderStructure(path, folders, root);
|
||||||
@ -426,6 +427,9 @@ export class VaultService implements OnDestroy {
|
|||||||
const sortedNotes = this.getSortedNotes();
|
const sortedNotes = this.getSortedNotes();
|
||||||
|
|
||||||
for (const note of sortedNotes) {
|
for (const note of sortedNotes) {
|
||||||
|
if (this.isBuiltinPath(note.originalPath) || this.isBuiltinPath(note.filePath)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const segments = note.originalPath.split('/').filter(Boolean);
|
const segments = note.originalPath.split('/').filter(Boolean);
|
||||||
const folderSegments = segments.slice(0, -1);
|
const folderSegments = segments.slice(0, -1);
|
||||||
|
|
||||||
@ -559,7 +563,7 @@ export class VaultService implements OnDestroy {
|
|||||||
|
|
||||||
for (const note of this.allNotes()) {
|
for (const note of this.allNotes()) {
|
||||||
const path = this.normalizePath(note.originalPath || note.filePath || '');
|
const path = this.normalizePath(note.originalPath || note.filePath || '');
|
||||||
if (!path || this.isInTrash(path)) continue;
|
if (!path || this.isInTrash(path) || this.isBuiltinPath(path)) continue;
|
||||||
|
|
||||||
const parts = path.split('/');
|
const parts = path.split('/');
|
||||||
parts.pop(); // Remove filename
|
parts.pop(); // Remove filename
|
||||||
@ -1072,6 +1076,13 @@ export class VaultService implements OnDestroy {
|
|||||||
return (path || '').replace(/\\/g, '/');
|
return (path || '').replace(/\\/g, '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private isBuiltinPath(path: string): boolean {
|
||||||
|
const normalized = this.normalizePath(path).replace(/^\/+/g, '');
|
||||||
|
if (!normalized) return false;
|
||||||
|
const [firstSegment] = normalized.split('/');
|
||||||
|
return firstSegment === '__builtin__';
|
||||||
|
}
|
||||||
|
|
||||||
private normalizeLineEndings(content: string): string {
|
private normalizeLineEndings(content: string): string {
|
||||||
return content.replace(/\r\n/g, '\n');
|
return content.replace(/\r\n/g, '\n');
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user