ObsiViewer/docs/MENU_CONTEXTUEL/CONTEXT_MENU_VAULT_SERVICE_INTEGRATION.md
Bruno Charest b1da9b111d feat: reorganize context menu documentation into docs folder
- Moved CONTEXT_MENU_INDEX.md and CONTEXT_MENU_VERIFICATION.md into docs/ directory for better organization
- Consolidated all context menu documentation files in one location for easier maintenance
- Documentation remains complete with 1000+ lines covering implementation, integration, and verification

The change improves documentation structure by moving context menu related files into a dedicated docs folder, making it easier for developers to find an
2025-10-25 20:17:10 -04:00

11 KiB

Context Menu - VaultService Integration Guide

Overview

This guide shows how to implement the context menu actions by integrating them with the VaultService. Each action corresponds to a VaultService method that needs to be called.

Action Implementation Examples

1. Create Subfolder

Current Implementation:

private createSubfolder() {
  if (!this.ctxTarget) return;
  const name = prompt('Enter subfolder name:');
  if (!name) return;
  const newPath = `${this.ctxTarget.path}/${name}`;
  this.showNotification(`Creating subfolder: ${newPath}`, 'info');
}

Complete Implementation:

private createSubfolder() {
  if (!this.ctxTarget) return;
  const name = prompt('Enter subfolder name:');
  if (!name) return;
  
  try {
    const newPath = `${this.ctxTarget.path}/${name}`;
    this.vaultService.createFolder(newPath);
    this.showNotification(`Subfolder "${name}" created successfully`, 'success');
  } catch (error) {
    this.showNotification(`Failed to create subfolder: ${error}`, 'error');
  }
}

Required VaultService Method:

createFolder(path: string): void {
  // Create folder at the specified path
  // Update file tree
  // Emit change notification
}

2. Rename Folder

Current Implementation:

private renameFolder() {
  if (!this.ctxTarget) return;
  const newName = prompt('Enter new folder name:', this.ctxTarget.name);
  if (!newName || newName === this.ctxTarget.name) return;
  this.showNotification(`Renaming folder to: ${newName}`, 'info');
}

Complete Implementation:

private renameFolder() {
  if (!this.ctxTarget) return;
  const newName = prompt('Enter new folder name:', this.ctxTarget.name);
  if (!newName || newName === this.ctxTarget.name) return;
  
  try {
    this.vaultService.renameFolder(this.ctxTarget.path, newName);
    this.showNotification(`Folder renamed to "${newName}"`, 'success');
  } catch (error) {
    this.showNotification(`Failed to rename folder: ${error}`, 'error');
  }
}

Required VaultService Method:

renameFolder(oldPath: string, newName: string): void {
  // Rename folder
  // Update all notes with new path
  // Update file tree
  // Emit change notification
}

3. Duplicate Folder

Current Implementation:

private duplicateFolder() {
  if (!this.ctxTarget) return;
  const newName = prompt('Enter duplicate folder name:', `${this.ctxTarget.name} (copy)`);
  if (!newName) return;
  this.showNotification(`Duplicating folder to: ${newName}`, 'info');
}

Complete Implementation:

private duplicateFolder() {
  if (!this.ctxTarget) return;
  const newName = prompt('Enter duplicate folder name:', `${this.ctxTarget.name} (copy)`);
  if (!newName) return;
  
  try {
    const parentPath = this.ctxTarget.path.substring(0, this.ctxTarget.path.lastIndexOf('/'));
    const newPath = parentPath ? `${parentPath}/${newName}` : newName;
    this.vaultService.duplicateFolder(this.ctxTarget.path, newPath);
    this.showNotification(`Folder duplicated to "${newName}"`, 'success');
  } catch (error) {
    this.showNotification(`Failed to duplicate folder: ${error}`, 'error');
  }
}

Required VaultService Method:

duplicateFolder(sourcePath: string, destinationPath: string): void {
  // Copy folder structure
  // Copy all notes
  // Update file tree
  // Emit change notification
}

4. Create New Page

Current Implementation:

private createPageInFolder() {
  if (!this.ctxTarget) return;
  const pageName = prompt('Enter page name:');
  if (!pageName) return;
  this.showNotification(`Creating page in folder: ${pageName}`, 'info');
}

Complete Implementation:

private createPageInFolder() {
  if (!this.ctxTarget) return;
  const pageName = prompt('Enter page name:');
  if (!pageName) return;
  
  try {
    const noteId = this.vaultService.createNote(
      this.ctxTarget.path,
      pageName,
      {
        title: pageName,
        created: new Date().toISOString(),
        updated: new Date().toISOString(),
      }
    );
    this.showNotification(`Page "${pageName}" created`, 'success');
    // Optionally open the new note
    // this.noteSelected.emit(noteId);
  } catch (error) {
    this.showNotification(`Failed to create page: ${error}`, 'error');
  }
}

Required VaultService Method:

createNote(folderPath: string, fileName: string, frontmatter?: Record<string, any>): string {
  // Create new markdown file
  // Add frontmatter
  // Update file tree
  // Return note ID
  // Emit change notification
}

Current Implementation (Already Complete):

private copyInternalLink() {
  if (!this.ctxTarget) return;
  const link = `[[${this.ctxTarget.path}]]`;
  navigator.clipboard.writeText(link).then(() => {
    this.showNotification('Internal link copied to clipboard!', 'success');
  }).catch(() => {
    this.showNotification('Failed to copy link', 'error');
  });
}

Status: This action is fully implemented and working!


6. Delete Folder

Current Implementation:

private deleteFolder() {
  if (!this.ctxTarget) return;
  const confirmed = confirm(`Are you sure you want to delete the folder "${this.ctxTarget.name}"?`);
  if (!confirmed) return;
  this.showNotification(`Deleting folder: ${this.ctxTarget.name}`, 'warning');
}

Complete Implementation:

private deleteFolder() {
  if (!this.ctxTarget) return;
  const confirmed = confirm(`Are you sure you want to delete the folder "${this.ctxTarget.name}"?`);
  if (!confirmed) return;
  
  try {
    this.vaultService.deleteFolder(this.ctxTarget.path);
    this.showNotification(`Folder "${this.ctxTarget.name}" deleted`, 'success');
  } catch (error) {
    this.showNotification(`Failed to delete folder: ${error}`, 'error');
  }
}

Required VaultService Method:

deleteFolder(path: string): void {
  // Move folder to trash or permanently delete
  // Update file tree
  // Emit change notification
}

7. Delete All Pages in Folder

Current Implementation:

private deleteAllPagesInFolder() {
  if (!this.ctxTarget) return;
  const confirmed = confirm(`Are you sure you want to delete ALL pages in "${this.ctxTarget.name}"? This cannot be undone.`);
  if (!confirmed) return;
  this.showNotification(`Deleting all pages in folder: ${this.ctxTarget.name}`, 'warning');
}

Complete Implementation:

private deleteAllPagesInFolder() {
  if (!this.ctxTarget) return;
  const confirmed = confirm(`Are you sure you want to delete ALL pages in "${this.ctxTarget.name}"? This cannot be undone.`);
  if (!confirmed) return;
  
  try {
    this.vaultService.deleteAllNotesInFolder(this.ctxTarget.path);
    this.showNotification(`All pages in "${this.ctxTarget.name}" deleted`, 'success');
  } catch (error) {
    this.showNotification(`Failed to delete pages: ${error}`, 'error');
  }
}

Required VaultService Method:

deleteAllNotesInFolder(folderPath: string): void {
  // Get all notes in folder
  // Delete each note
  // Update file tree
  // Emit change notification
}

VaultService Methods Checklist

Below is a checklist of all VaultService methods needed for full context menu functionality:

// Folder Operations
createFolder(path: string): void
renameFolder(oldPath: string, newName: string): void
duplicateFolder(sourcePath: string, destinationPath: string): void
deleteFolder(path: string): void

// Note Operations
createNote(folderPath: string, fileName: string, frontmatter?: Record<string, any>): string
deleteAllNotesInFolder(folderPath: string): void

// Existing Methods (already available)
toggleFolder(path: string): void
allNotes(): Note[]
folderCounts(): Record<string, number>

Integration Steps

Step 1: Update file-explorer.component.ts

Replace the action methods with complete implementations:

// In file-explorer.component.ts

private createSubfolder() {
  if (!this.ctxTarget) return;
  const name = prompt('Enter subfolder name:');
  if (!name) return;
  
  try {
    const newPath = `${this.ctxTarget.path}/${name}`;
    this.vaultService.createFolder(newPath);
    this.showNotification(`Subfolder "${name}" created successfully`, 'success');
  } catch (error) {
    this.showNotification(`Failed to create subfolder: ${error}`, 'error');
  }
}

// ... repeat for other actions

Step 2: Implement VaultService Methods

Add the required methods to vault.service.ts:

// In vault.service.ts

createFolder(path: string): void {
  // Implementation
}

renameFolder(oldPath: string, newName: string): void {
  // Implementation
}

// ... etc

Step 3: Test Each Action

Test each action individually:

npm run dev
# Right-click on folder
# Test each action
# Check console for errors

Step 4: Add Error Handling

Ensure proper error handling in each action:

try {
  // Action logic
  this.vaultService.someMethod();
  this.showNotification('Success message', 'success');
} catch (error) {
  console.error('Action failed:', error);
  this.showNotification(`Failed: ${error.message}`, 'error');
}

Notification System

The showNotification method currently logs to console. You can enhance it:

private showNotification(message: string, type: 'success' | 'info' | 'warning' | 'error') {
  // Current implementation
  console.log(`[${type.toUpperCase()}] ${message}`);
  
  // TODO: Replace with proper toast notification service
  // this.toastr.show(message, type);
}

Testing Checklist

  • Create subfolder works
  • Rename folder works
  • Duplicate folder works
  • Create new page works
  • Copy internal link works
  • Delete folder works (with confirmation)
  • Delete all pages works (with confirmation)
  • Error messages display correctly
  • File tree updates after each action
  • Colors persist after actions

Common Issues & Solutions

Issue: "vaultService method not found"

Solution: Ensure the method exists in VaultService before calling it.

Issue: "File tree doesn't update"

Solution: Make sure VaultService emits change notifications after modifications.

Issue: "Confirmation dialog doesn't appear"

Solution: Check if confirm() is being called before the action.

Issue: "Notification doesn't show"

Solution: Implement a proper toast notification service instead of console.log.


Performance Considerations

  • Use ChangeDetectionStrategy.OnPush (already implemented)
  • Batch file tree updates when possible
  • Debounce rapid folder operations
  • Cache folder counts to avoid recalculation

Security Considerations

  • Confirmation dialogs for destructive operations
  • Input validation for folder/page names
  • Path sanitization to prevent directory traversal
  • Rate limiting for bulk operations (future)

  • CONTEXT_MENU_IMPLEMENTATION.md - Full technical documentation
  • CONTEXT_MENU_QUICK_START.md - Quick start guide
  • src/services/vault.service.ts - VaultService source code
  • src/components/file-explorer/file-explorer.component.ts - Integration point

Status: Ready for VaultService Integration
Effort: ~2-3 hours
Difficulty: Easy to Medium