feat: improve note path handling and add delete confirmation modal

- Updated API endpoints to handle note paths with slashes using regex routes
- Added warning modal component for note deletion confirmation
- Fixed URL encoding of note paths to preserve spaces and special characters
- Improved fullscreen note handling to use URL state and custom events
- Enhanced internal link copying to use proper URL format
- Removed test/sample note files from vault directory
This commit is contained in:
2025-10-25 16:27:42 -04:00
parent 96745e9997
commit b873577e93
11 changed files with 420 additions and 54 deletions
@@ -0,0 +1,89 @@
import { Component, ChangeDetectionStrategy, EventEmitter, Input, Output, OnInit, OnDestroy, ElementRef, Renderer2, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-warning-panel',
standalone: true,
imports: [CommonModule],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div *ngIf="visible" class="fixed inset-0 z-[99999] flex items-center justify-center bg-black/40 backdrop-blur-[1px]" (click)="onBackdrop($event)" role="dialog" aria-modal="true">
<div class="w-full max-w-md mx-3 rounded-2xl shadow-xl transition-all duration-200 p-6 border"
[class.bg-white]="!isDarkTheme()" [class.border-slate-200]="!isDarkTheme()"
[class.bg-slate-800/95]="isDarkTheme()" [class.border-slate-600]="isDarkTheme()"
(click)="$event.stopPropagation()">
<div class="flex items-center gap-3 mb-4">
<svg class="w-6 h-6 text-yellow-500" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path>
<line x1="12" y1="9" x2="12" y2="13"></line>
<line x1="12" y1="17" x2="12.01" y2="17"></line>
</svg>
<h2 class="text-lg font-semibold">{{ title }}</h2>
</div>
<p class="text-sm mb-6" [class.text-slate-500]="!isDarkTheme()" [class.text-slate-300]="isDarkTheme()">{{ message }}</p>
<div class="flex justify-end gap-3">
<button type="button" (click)="onCancel()"
class="px-4 py-2 rounded-lg transition-colors cursor-pointer"
[ngClass]="isDarkTheme() ? 'text-slate-300 hover:bg-slate-700' : 'text-slate-700 hover:bg-slate-100'">{{ cancelText || 'Cancel' }}</button>
<button type="button" (click)="onDelete()"
class="px-4 py-2 rounded-lg text-white transition-colors cursor-pointer"
[ngClass]="confirmColor === 'danger' ? 'bg-red-600 hover:bg-red-700' : 'bg-blue-600 hover:bg-blue-700'">
{{ confirmText || 'Delete' }}
</button>
</div>
</div>
</div>
`,
})
export class WarningPanelComponent implements OnInit, OnDestroy {
@Input() visible = false;
@Input() title = 'Delete this note?';
@Input() message = 'The note will be moved to the trash folder and can be restored later.';
@Input() confirmText = 'Delete';
@Input() cancelText = 'Cancel';
@Input() confirmColor: 'danger' | 'primary' = 'danger';
@Output() cancel = new EventEmitter<void>();
@Output() delete = new EventEmitter<void>();
private readonly host = inject(ElementRef<HTMLElement>);
private readonly renderer = inject(Renderer2);
// Use documentElement dark class to decide theme quickly without injecting ThemeService
isDarkTheme(): boolean {
try { return document.documentElement.classList.contains('dark'); } catch { return false; }
}
ngOnInit(): void {
if (typeof document === 'undefined') return;
const element = this.host.nativeElement;
// Ensure host doesn't create an extra layer interfering with positioning
this.renderer.setStyle(element, 'display', 'contents');
// Move host to body so it sits above any stacking-context limitations
this.renderer.appendChild(document.body, element);
}
ngOnDestroy(): void {
if (typeof document === 'undefined') return;
const element = this.host.nativeElement;
if (element.parentNode) {
this.renderer.removeChild(element.parentNode, element);
}
}
onCancel() {
console.log('[WarningPanel] Cancel button clicked');
this.cancel.emit();
}
onDelete() {
console.log('[WarningPanel] Confirm button clicked');
this.delete.emit();
}
onBackdrop(event: MouseEvent) {
if (event.target === event.currentTarget) {
this.cancel.emit();
}
}
}